diff --git a/7092821-java.security.Provider.getService-is-synchro.patch b/7092821-java.security.Provider.getService-is-synchro.patch new file mode 100644 index 0000000000000000000000000000000000000000..a20593fbc510c7cb8dab00f7d108500458b18b73 --- /dev/null +++ b/7092821-java.security.Provider.getService-is-synchro.patch @@ -0,0 +1,4815 @@ +From 834e8792532d89505e5cabfdbca0de3481b5c8ed Mon Sep 17 00:00:00 2001 +From: z00558301 +Date: Wed, 8 Jun 2022 09:38:47 +0800 +Subject: [PATCH 06/10] 7092821: java.security.Provider.getService() is + synchronized and became scalability bottleneck + +Bug url: https://bugs.openjdk.java.net/browse/JDK-7092821 +--- + .../com/sun/crypto/provider/SunJCE.java | 1300 ++++++++--------- + .../security/AlgorithmParameterGenerator.java | 5 +- + .../share/classes/java/security/Provider.java | 129 +- + .../classes/java/security/SecureRandom.java | 70 +- + .../share/classes/javax/crypto/Cipher.java | 8 +- + .../classes/javax/crypto/JceSecurity.java | 2 - + .../classes/javax/crypto/KeyAgreement.java | 4 +- + .../classes/javax/crypto/KeyGenerator.java | 4 +- + .../classes/sun/security/provider/Sun.java | 25 +- + .../sun/security/provider/SunEntries.java | 333 ++--- + .../provider/VerificationProvider.java | 28 +- + .../classes/sun/security/rsa/SunRsaSign.java | 25 +- + .../sun/security/rsa/SunRsaSignEntries.java | 171 +-- + .../classes/sun/security/ssl/SunJSSE.java | 136 +- + .../Provider/BaseProviderValidator.java | 76 + + .../security/Provider/GetServiceRace.java | 98 ++ + .../security/Provider/LegacyPutAlias.java | 86 ++ + .../Provider/ProviderValidationUtil.java | 270 ++++ + .../security/Provider/SunJCEValidator.java | 574 ++++++++ + .../security/Provider/SunJSSEValidator.java | 137 ++ + .../Provider/SunRsaSignValidator.java | 154 ++ + .../java/security/Provider/SunValidator.java | 263 ++++ + .../security/SecureRandom/DefaultAlgo.java | 117 ++ + .../provider/GetServiceBenchmark.java | 83 ++ + 24 files changed, 2965 insertions(+), 1133 deletions(-) + create mode 100644 jdk/test/java/security/Provider/BaseProviderValidator.java + create mode 100644 jdk/test/java/security/Provider/GetServiceRace.java + create mode 100644 jdk/test/java/security/Provider/LegacyPutAlias.java + create mode 100644 jdk/test/java/security/Provider/ProviderValidationUtil.java + create mode 100644 jdk/test/java/security/Provider/SunJCEValidator.java + create mode 100644 jdk/test/java/security/Provider/SunJSSEValidator.java + create mode 100644 jdk/test/java/security/Provider/SunRsaSignValidator.java + create mode 100644 jdk/test/java/security/Provider/SunValidator.java + create mode 100644 jdk/test/java/security/SecureRandom/DefaultAlgo.java + create mode 100644 jdk/test/micro/org/openeuler/bench/security/provider/GetServiceBenchmark.java + +diff --git a/jdk/src/share/classes/com/sun/crypto/provider/SunJCE.java b/jdk/src/share/classes/com/sun/crypto/provider/SunJCE.java +index 1e5b5dd0..66a26db2 100644 +--- a/jdk/src/share/classes/com/sun/crypto/provider/SunJCE.java ++++ b/jdk/src/share/classes/com/sun/crypto/provider/SunJCE.java +@@ -28,7 +28,10 @@ package com.sun.crypto.provider; + import java.security.AccessController; + import java.security.Provider; + import java.security.SecureRandom; +- ++import java.security.PrivilegedAction; ++import java.util.Arrays; ++import java.util.HashMap; ++import java.util.List; + + /** + * The "SunJCE" Cryptographic Service Provider. +@@ -78,16 +81,6 @@ public final class SunJCE extends Provider { + "(implements RSA, DES, Triple DES, AES, Blowfish, ARCFOUR, RC2, PBE, " + + "Diffie-Hellman, HMAC)"; + +- private static final String OID_PKCS12_RC4_128 = "1.2.840.113549.1.12.1.1"; +- private static final String OID_PKCS12_RC4_40 = "1.2.840.113549.1.12.1.2"; +- private static final String OID_PKCS12_DESede = "1.2.840.113549.1.12.1.3"; +- private static final String OID_PKCS12_RC2_128 = "1.2.840.113549.1.12.1.5"; +- private static final String OID_PKCS12_RC2_40 = "1.2.840.113549.1.12.1.6"; +- private static final String OID_PKCS5_MD5_DES = "1.2.840.113549.1.5.3"; +- private static final String OID_PKCS5_PBKDF2 = "1.2.840.113549.1.5.12"; +- private static final String OID_PKCS5_PBES2 = "1.2.840.113549.1.5.13"; +- private static final String OID_PKCS3 = "1.2.840.113549.1.3.1"; +- + /* Are we debugging? -- for developers */ + static final boolean debug = false; + +@@ -102,10 +95,115 @@ public final class SunJCE extends Provider { + } + static SecureRandom getRandom() { return SecureRandomHolder.RANDOM; } + ++ // create an aliases List from the specified aliases ++ public static List createAliases(String ... aliases) { ++ return Arrays.asList(aliases); ++ } ++ ++ // create an aliases List from the specified oid followed by other aliases ++ public static List createAliasesWithOid(String ... oids) { ++ String[] result = Arrays.copyOf(oids, oids.length + 1); ++ result[result.length - 1] = "OID." + oids[0]; ++ return Arrays.asList(result); ++ } ++ ++ private void ps(String type, String algo, String cn, ++ List aliases, HashMap attrs) { ++ putService(new Provider.Service(this, type, algo, cn, aliases, attrs)); ++ } ++ + public SunJCE() { + /* We are the "SunJCE" provider */ + super("SunJCE", 1.8d, info); + ++ // if there is no security manager installed, put directly into ++ // the provider ++ if (System.getSecurityManager() == null) { ++ putEntries(); ++ } else { ++ AccessController.doPrivileged(new PrivilegedAction() { ++ @Override ++ public Void run() { ++ putEntries(); ++ return null; ++ } ++ }); ++ } ++ if (instance == null) { ++ instance = this; ++ } ++ } ++ ++ void putEntries() { ++ // common aliases and oids ++ List aesAliases = createAliases("Rijndael"); ++ List desEdeAliases = createAliases("TripleDES"); ++ List arcFourAliases = createAliases("RC4"); ++ List sunTlsMSAliases = createAliases( ++ "SunTls12MasterSecret", "SunTlsExtendedMasterSecret" ++ ); ++ List sunTlsKMAliases = createAliases("SunTls12KeyMaterial"); ++ List sunTlsRsaPMSAliases = createAliases("SunTls12RsaPremasterSecret"); ++ ++ String aes128Oid = "2.16.840.1.101.3.4.1."; ++ String aes192Oid = "2.16.840.1.101.3.4.1.2"; ++ String aes256Oid = "2.16.840.1.101.3.4.1.4"; ++ ++ List pkcs12RC4_128Aliases = ++ createAliasesWithOid("1.2.840.113549.1.12.1.1"); ++ ++ List pkcs12RC4_40Aliases = ++ createAliasesWithOid("1.2.840.113549.1.12.1.2"); ++ ++ List pkcs12DESedeAliases = ++ createAliasesWithOid("1.2.840.113549.1.12.1.3"); ++ ++ List pkcs12RC2_128Aliases = ++ createAliasesWithOid("1.2.840.113549.1.12.1.5"); ++ ++ List pkcs12RC2_40Aliases = ++ createAliasesWithOid("1.2.840.113549.1.12.1.6"); ++ ++ List pkcs5MD5_DESAliases = ++ createAliasesWithOid("1.2.840.113549.1.5.3", "PBE"); ++ ++ List pkcs5PBKDF2Aliases = ++ createAliasesWithOid("1.2.840.113549.1.5.12"); ++ ++ List pkcs5PBES2Aliases = ++ createAliasesWithOid("1.2.840.113549.1.5.13"); ++ ++ List diffieHellmanAliases = ++ createAliasesWithOid("1.2.840.113549.1.3.1", "DH"); ++ ++ String macOidBase = "1.2.840.113549.2."; ++ List macSHA1Aliases = createAliasesWithOid(macOidBase + "7"); ++ List macSHA224Aliases = createAliasesWithOid(macOidBase + "8"); ++ List macSHA256Aliases = createAliasesWithOid(macOidBase + "9"); ++ List macSHA384Aliases = createAliasesWithOid(macOidBase + "10"); ++ List macSHA512Aliases = createAliasesWithOid(macOidBase + "11"); ++ ++ // reuse attribute map and reset before each reuse ++ HashMap attrs = new HashMap<>(3); ++ attrs.put("SupportedModes", "ECB"); ++ attrs.put("SupportedPaddings", "NOPADDING|PKCS1PADDING|OAEPPADDING" ++ + "|OAEPWITHMD5ANDMGF1PADDING" ++ + "|OAEPWITHSHA1ANDMGF1PADDING" ++ + "|OAEPWITHSHA-1ANDMGF1PADDING" ++ + "|OAEPWITHSHA-224ANDMGF1PADDING" ++ + "|OAEPWITHSHA-256ANDMGF1PADDING" ++ + "|OAEPWITHSHA-384ANDMGF1PADDING" ++ + "|OAEPWITHSHA-512ANDMGF1PADDING" ++ + "|OAEPWITHSHA-512/224ANDMGF1PADDING" ++ + "|OAEPWITHSHA-512/256ANDMGF1PADDING"); ++ attrs.put("SupportedKeyClasses", ++ "java.security.interfaces.RSAPublicKey" + ++ "|java.security.interfaces.RSAPrivateKey"); ++ ps("Cipher", "RSA", ++ "com.sun.crypto.provider.RSACipher", null, attrs); ++ ++ // common block cipher modes, pads ++ + final String BLOCK_MODES = "ECB|CBC|PCBC|CTR|CTS|CFB|OFB" + + "|CFB8|CFB16|CFB24|CFB32|CFB40|CFB48|CFB56|CFB64" + + "|OFB8|OFB16|OFB24|OFB32|OFB40|OFB48|OFB56|OFB64"; +@@ -114,694 +212,529 @@ public final class SunJCE extends Provider { + "|OFB72|OFB80|OFB88|OFB96|OFB104|OFB112|OFB120|OFB128"; + final String BLOCK_PADS = "NOPADDING|PKCS5PADDING|ISO10126PADDING"; + +- AccessController.doPrivileged( +- new java.security.PrivilegedAction() { +- public Object run() { +- +- /* +- * Cipher engines +- */ +- put("Cipher.RSA", "com.sun.crypto.provider.RSACipher"); +- put("Cipher.RSA SupportedModes", "ECB"); +- put("Cipher.RSA SupportedPaddings", +- "NOPADDING|PKCS1PADDING|OAEPPADDING" +- + "|OAEPWITHMD5ANDMGF1PADDING" +- + "|OAEPWITHSHA1ANDMGF1PADDING" +- + "|OAEPWITHSHA-1ANDMGF1PADDING" +- + "|OAEPWITHSHA-224ANDMGF1PADDING" +- + "|OAEPWITHSHA-256ANDMGF1PADDING" +- + "|OAEPWITHSHA-384ANDMGF1PADDING" +- + "|OAEPWITHSHA-512ANDMGF1PADDING" +- + "|OAEPWITHSHA-512/224ANDMGF1PADDING" +- + "|OAEPWITHSHA-512/256ANDMGF1PADDING"); +- put("Cipher.RSA SupportedKeyClasses", +- "java.security.interfaces.RSAPublicKey" + +- "|java.security.interfaces.RSAPrivateKey"); +- +- put("Cipher.DES", "com.sun.crypto.provider.DESCipher"); +- put("Cipher.DES SupportedModes", BLOCK_MODES); +- put("Cipher.DES SupportedPaddings", BLOCK_PADS); +- put("Cipher.DES SupportedKeyFormats", "RAW"); +- +- put("Cipher.DESede", "com.sun.crypto.provider.DESedeCipher"); +- put("Alg.Alias.Cipher.TripleDES", "DESede"); +- put("Cipher.DESede SupportedModes", BLOCK_MODES); +- put("Cipher.DESede SupportedPaddings", BLOCK_PADS); +- put("Cipher.DESede SupportedKeyFormats", "RAW"); +- +- put("Cipher.DESedeWrap", +- "com.sun.crypto.provider.DESedeWrapCipher"); +- put("Cipher.DESedeWrap SupportedModes", "CBC"); +- put("Cipher.DESedeWrap SupportedPaddings", "NOPADDING"); +- put("Cipher.DESedeWrap SupportedKeyFormats", "RAW"); +- +- // PBES1 +- +- put("Cipher.PBEWithMD5AndDES", +- "com.sun.crypto.provider.PBEWithMD5AndDESCipher"); +- put("Alg.Alias.Cipher.OID."+OID_PKCS5_MD5_DES, +- "PBEWithMD5AndDES"); +- put("Alg.Alias.Cipher."+OID_PKCS5_MD5_DES, +- "PBEWithMD5AndDES"); +- +- put("Cipher.PBEWithMD5AndTripleDES", +- "com.sun.crypto.provider.PBEWithMD5AndTripleDESCipher"); +- +- put("Cipher.PBEWithSHA1AndDESede", +- "com.sun.crypto.provider.PKCS12PBECipherCore$" + +- "PBEWithSHA1AndDESede"); +- put("Alg.Alias.Cipher.OID." + OID_PKCS12_DESede, +- "PBEWithSHA1AndDESede"); +- put("Alg.Alias.Cipher." + OID_PKCS12_DESede, +- "PBEWithSHA1AndDESede"); +- +- put("Cipher.PBEWithSHA1AndRC2_40", +- "com.sun.crypto.provider.PKCS12PBECipherCore$" + +- "PBEWithSHA1AndRC2_40"); +- put("Alg.Alias.Cipher.OID." + OID_PKCS12_RC2_40, +- "PBEWithSHA1AndRC2_40"); +- put("Alg.Alias.Cipher." + OID_PKCS12_RC2_40, +- "PBEWithSHA1AndRC2_40"); +- +- put("Cipher.PBEWithSHA1AndRC2_128", +- "com.sun.crypto.provider.PKCS12PBECipherCore$" + +- "PBEWithSHA1AndRC2_128"); +- put("Alg.Alias.Cipher.OID." + OID_PKCS12_RC2_128, +- "PBEWithSHA1AndRC2_128"); +- put("Alg.Alias.Cipher." + OID_PKCS12_RC2_128, +- "PBEWithSHA1AndRC2_128"); +- +- put("Cipher.PBEWithSHA1AndRC4_40", +- "com.sun.crypto.provider.PKCS12PBECipherCore$" + +- "PBEWithSHA1AndRC4_40"); +- put("Alg.Alias.Cipher.OID." + OID_PKCS12_RC4_40, +- "PBEWithSHA1AndRC4_40"); +- put("Alg.Alias.Cipher." + OID_PKCS12_RC4_40, +- "PBEWithSHA1AndRC4_40"); +- +- put("Cipher.PBEWithSHA1AndRC4_128", +- "com.sun.crypto.provider.PKCS12PBECipherCore$" + +- "PBEWithSHA1AndRC4_128"); +- put("Alg.Alias.Cipher.OID." + OID_PKCS12_RC4_128, +- "PBEWithSHA1AndRC4_128"); +- put("Alg.Alias.Cipher." + OID_PKCS12_RC4_128, +- "PBEWithSHA1AndRC4_128"); +- +- //PBES2 +- +- put("Cipher.PBEWithHmacSHA1AndAES_128", +- "com.sun.crypto.provider.PBES2Core$HmacSHA1AndAES_128"); +- +- put("Cipher.PBEWithHmacSHA224AndAES_128", +- "com.sun.crypto.provider.PBES2Core$" + +- "HmacSHA224AndAES_128"); +- +- put("Cipher.PBEWithHmacSHA256AndAES_128", +- "com.sun.crypto.provider.PBES2Core$" + +- "HmacSHA256AndAES_128"); +- +- put("Cipher.PBEWithHmacSHA384AndAES_128", +- "com.sun.crypto.provider.PBES2Core$" + +- "HmacSHA384AndAES_128"); +- +- put("Cipher.PBEWithHmacSHA512AndAES_128", +- "com.sun.crypto.provider.PBES2Core$" + +- "HmacSHA512AndAES_128"); +- +- put("Cipher.PBEWithHmacSHA1AndAES_256", +- "com.sun.crypto.provider.PBES2Core$HmacSHA1AndAES_256"); +- +- put("Cipher.PBEWithHmacSHA224AndAES_256", +- "com.sun.crypto.provider.PBES2Core$" + +- "HmacSHA224AndAES_256"); +- +- put("Cipher.PBEWithHmacSHA256AndAES_256", +- "com.sun.crypto.provider.PBES2Core$" + +- "HmacSHA256AndAES_256"); +- +- put("Cipher.PBEWithHmacSHA384AndAES_256", +- "com.sun.crypto.provider.PBES2Core$" + +- "HmacSHA384AndAES_256"); +- +- put("Cipher.PBEWithHmacSHA512AndAES_256", +- "com.sun.crypto.provider.PBES2Core$" + +- "HmacSHA512AndAES_256"); +- +- put("Cipher.Blowfish", +- "com.sun.crypto.provider.BlowfishCipher"); +- put("Cipher.Blowfish SupportedModes", BLOCK_MODES); +- put("Cipher.Blowfish SupportedPaddings", BLOCK_PADS); +- put("Cipher.Blowfish SupportedKeyFormats", "RAW"); +- +- put("Cipher.AES", "com.sun.crypto.provider.AESCipher$General"); +- put("Alg.Alias.Cipher.Rijndael", "AES"); +- put("Cipher.AES SupportedModes", BLOCK_MODES128); +- put("Cipher.AES SupportedPaddings", BLOCK_PADS); +- put("Cipher.AES SupportedKeyFormats", "RAW"); +- +- put("Cipher.AES_128/ECB/NoPadding", "com.sun.crypto.provider.AESCipher$AES128_ECB_NoPadding"); +- put("Alg.Alias.Cipher.2.16.840.1.101.3.4.1.1", "AES_128/ECB/NoPadding"); +- put("Alg.Alias.Cipher.OID.2.16.840.1.101.3.4.1.1", "AES_128/ECB/NoPadding"); +- put("Cipher.AES_128/CBC/NoPadding", "com.sun.crypto.provider.AESCipher$AES128_CBC_NoPadding"); +- put("Alg.Alias.Cipher.2.16.840.1.101.3.4.1.2", "AES_128/CBC/NoPadding"); +- put("Alg.Alias.Cipher.OID.2.16.840.1.101.3.4.1.2", "AES_128/CBC/NoPadding"); +- put("Cipher.AES_128/OFB/NoPadding", "com.sun.crypto.provider.AESCipher$AES128_OFB_NoPadding"); +- put("Alg.Alias.Cipher.2.16.840.1.101.3.4.1.3", "AES_128/OFB/NoPadding"); +- put("Alg.Alias.Cipher.OID.2.16.840.1.101.3.4.1.3", "AES_128/OFB/NoPadding"); +- put("Cipher.AES_128/CFB/NoPadding", "com.sun.crypto.provider.AESCipher$AES128_CFB_NoPadding"); +- put("Alg.Alias.Cipher.2.16.840.1.101.3.4.1.4", "AES_128/CFB/NoPadding"); +- put("Alg.Alias.Cipher.OID.2.16.840.1.101.3.4.1.4", "AES_128/CFB/NoPadding"); +- put("Cipher.AES_128/GCM/NoPadding", "com.sun.crypto.provider.AESCipher$AES128_GCM_NoPadding"); +- put("Alg.Alias.Cipher.2.16.840.1.101.3.4.1.6", "AES_128/GCM/NoPadding"); +- put("Alg.Alias.Cipher.OID.2.16.840.1.101.3.4.1.6", "AES_128/GCM/NoPadding"); +- +- put("Cipher.AES_192/ECB/NoPadding", "com.sun.crypto.provider.AESCipher$AES192_ECB_NoPadding"); +- put("Alg.Alias.Cipher.2.16.840.1.101.3.4.1.21", "AES_192/ECB/NoPadding"); +- put("Alg.Alias.Cipher.OID.2.16.840.1.101.3.4.1.21", "AES_192/ECB/NoPadding"); +- put("Cipher.AES_192/CBC/NoPadding", "com.sun.crypto.provider.AESCipher$AES192_CBC_NoPadding"); +- put("Alg.Alias.Cipher.2.16.840.1.101.3.4.1.22", "AES_192/CBC/NoPadding"); +- put("Alg.Alias.Cipher.OID.2.16.840.1.101.3.4.1.22", "AES_192/CBC/NoPadding"); +- put("Cipher.AES_192/OFB/NoPadding", "com.sun.crypto.provider.AESCipher$AES192_OFB_NoPadding"); +- put("Alg.Alias.Cipher.2.16.840.1.101.3.4.1.23", "AES_192/OFB/NoPadding"); +- put("Alg.Alias.Cipher.OID.2.16.840.1.101.3.4.1.23", "AES_192/OFB/NoPadding"); +- put("Cipher.AES_192/CFB/NoPadding", "com.sun.crypto.provider.AESCipher$AES192_CFB_NoPadding"); +- put("Alg.Alias.Cipher.2.16.840.1.101.3.4.1.24", "AES_192/CFB/NoPadding"); +- put("Alg.Alias.Cipher.OID.2.16.840.1.101.3.4.1.24", "AES_192/CFB/NoPadding"); +- put("Cipher.AES_192/GCM/NoPadding", "com.sun.crypto.provider.AESCipher$AES192_GCM_NoPadding"); +- put("Alg.Alias.Cipher.2.16.840.1.101.3.4.1.26", "AES_192/GCM/NoPadding"); +- put("Alg.Alias.Cipher.OID.2.16.840.1.101.3.4.1.26", "AES_192/GCM/NoPadding"); +- +- put("Cipher.AES_256/ECB/NoPadding", "com.sun.crypto.provider.AESCipher$AES256_ECB_NoPadding"); +- put("Alg.Alias.Cipher.2.16.840.1.101.3.4.1.41", "AES_256/ECB/NoPadding"); +- put("Alg.Alias.Cipher.OID.2.16.840.1.101.3.4.1.41", "AES_256/ECB/NoPadding"); +- put("Cipher.AES_256/CBC/NoPadding", "com.sun.crypto.provider.AESCipher$AES256_CBC_NoPadding"); +- put("Alg.Alias.Cipher.2.16.840.1.101.3.4.1.42", "AES_256/CBC/NoPadding"); +- put("Alg.Alias.Cipher.OID.2.16.840.1.101.3.4.1.42", "AES_256/CBC/NoPadding"); +- put("Cipher.AES_256/OFB/NoPadding", "com.sun.crypto.provider.AESCipher$AES256_OFB_NoPadding"); +- put("Alg.Alias.Cipher.2.16.840.1.101.3.4.1.43", "AES_256/OFB/NoPadding"); +- put("Alg.Alias.Cipher.OID.2.16.840.1.101.3.4.1.43", "AES_256/OFB/NoPadding"); +- put("Cipher.AES_256/CFB/NoPadding", "com.sun.crypto.provider.AESCipher$AES256_CFB_NoPadding"); +- put("Alg.Alias.Cipher.2.16.840.1.101.3.4.1.44", "AES_256/CFB/NoPadding"); +- put("Alg.Alias.Cipher.OID.2.16.840.1.101.3.4.1.44", "AES_256/CFB/NoPadding"); +- put("Cipher.AES_256/GCM/NoPadding", "com.sun.crypto.provider.AESCipher$AES256_GCM_NoPadding"); +- put("Alg.Alias.Cipher.2.16.840.1.101.3.4.1.46", "AES_256/GCM/NoPadding"); +- put("Alg.Alias.Cipher.OID.2.16.840.1.101.3.4.1.46", "AES_256/GCM/NoPadding"); +- +- put("Cipher.AESWrap", "com.sun.crypto.provider.AESWrapCipher$General"); +- put("Cipher.AESWrap SupportedModes", "ECB"); +- put("Cipher.AESWrap SupportedPaddings", "NOPADDING"); +- put("Cipher.AESWrap SupportedKeyFormats", "RAW"); +- +- put("Cipher.AESWrap_128", "com.sun.crypto.provider.AESWrapCipher$AES128"); +- put("Alg.Alias.Cipher.2.16.840.1.101.3.4.1.5", "AESWrap_128"); +- put("Alg.Alias.Cipher.OID.2.16.840.1.101.3.4.1.5", "AESWrap_128"); +- put("Cipher.AESWrap_192", "com.sun.crypto.provider.AESWrapCipher$AES192"); +- put("Alg.Alias.Cipher.2.16.840.1.101.3.4.1.25", "AESWrap_192"); +- put("Alg.Alias.Cipher.OID.2.16.840.1.101.3.4.1.25", "AESWrap_192"); +- put("Cipher.AESWrap_256", "com.sun.crypto.provider.AESWrapCipher$AES256"); +- put("Alg.Alias.Cipher.2.16.840.1.101.3.4.1.45", "AESWrap_256"); +- put("Alg.Alias.Cipher.OID.2.16.840.1.101.3.4.1.45", "AESWrap_256"); +- +- put("Cipher.RC2", +- "com.sun.crypto.provider.RC2Cipher"); +- put("Cipher.RC2 SupportedModes", BLOCK_MODES); +- put("Cipher.RC2 SupportedPaddings", BLOCK_PADS); +- put("Cipher.RC2 SupportedKeyFormats", "RAW"); +- +- put("Cipher.ARCFOUR", +- "com.sun.crypto.provider.ARCFOURCipher"); +- put("Alg.Alias.Cipher.RC4", "ARCFOUR"); +- put("Cipher.ARCFOUR SupportedModes", "ECB"); +- put("Cipher.ARCFOUR SupportedPaddings", "NOPADDING"); +- put("Cipher.ARCFOUR SupportedKeyFormats", "RAW"); +- +- /* +- * Key(pair) Generator engines +- */ +- put("KeyGenerator.DES", +- "com.sun.crypto.provider.DESKeyGenerator"); +- +- put("KeyGenerator.DESede", +- "com.sun.crypto.provider.DESedeKeyGenerator"); +- put("Alg.Alias.KeyGenerator.TripleDES", "DESede"); +- +- put("KeyGenerator.Blowfish", +- "com.sun.crypto.provider.BlowfishKeyGenerator"); +- +- put("KeyGenerator.AES", +- "com.sun.crypto.provider.AESKeyGenerator"); +- put("Alg.Alias.KeyGenerator.Rijndael", "AES"); +- +- put("KeyGenerator.RC2", +- "com.sun.crypto.provider.KeyGeneratorCore$" + +- "RC2KeyGenerator"); +- put("KeyGenerator.ARCFOUR", +- "com.sun.crypto.provider.KeyGeneratorCore$" + +- "ARCFOURKeyGenerator"); +- put("Alg.Alias.KeyGenerator.RC4", "ARCFOUR"); +- +- put("KeyGenerator.HmacMD5", +- "com.sun.crypto.provider.HmacMD5KeyGenerator"); +- +- put("KeyGenerator.HmacSHA1", +- "com.sun.crypto.provider.HmacSHA1KeyGenerator"); +- put("Alg.Alias.KeyGenerator.OID.1.2.840.113549.2.7", "HmacSHA1"); +- put("Alg.Alias.KeyGenerator.1.2.840.113549.2.7", "HmacSHA1"); +- +- put("KeyGenerator.HmacSHA224", +- "com.sun.crypto.provider.KeyGeneratorCore$HmacSHA2KG$SHA224"); +- put("Alg.Alias.KeyGenerator.OID.1.2.840.113549.2.8", "HmacSHA224"); +- put("Alg.Alias.KeyGenerator.1.2.840.113549.2.8", "HmacSHA224"); +- +- put("KeyGenerator.HmacSHA256", +- "com.sun.crypto.provider.KeyGeneratorCore$HmacSHA2KG$SHA256"); +- put("Alg.Alias.KeyGenerator.OID.1.2.840.113549.2.9", "HmacSHA256"); +- put("Alg.Alias.KeyGenerator.1.2.840.113549.2.9", "HmacSHA256"); +- +- put("KeyGenerator.HmacSHA384", +- "com.sun.crypto.provider.KeyGeneratorCore$HmacSHA2KG$SHA384"); +- put("Alg.Alias.KeyGenerator.OID.1.2.840.113549.2.10", "HmacSHA384"); +- put("Alg.Alias.KeyGenerator.1.2.840.113549.2.10", "HmacSHA384"); +- +- put("KeyGenerator.HmacSHA512", +- "com.sun.crypto.provider.KeyGeneratorCore$HmacSHA2KG$SHA512"); +- put("Alg.Alias.KeyGenerator.OID.1.2.840.113549.2.11", "HmacSHA512"); +- put("Alg.Alias.KeyGenerator.1.2.840.113549.2.11", "HmacSHA512"); +- +- put("KeyPairGenerator.DiffieHellman", +- "com.sun.crypto.provider.DHKeyPairGenerator"); +- put("Alg.Alias.KeyPairGenerator.DH", "DiffieHellman"); +- put("Alg.Alias.KeyPairGenerator.OID."+OID_PKCS3, +- "DiffieHellman"); +- put("Alg.Alias.KeyPairGenerator."+OID_PKCS3, +- "DiffieHellman"); +- +- /* +- * Algorithm parameter generation engines +- */ +- put("AlgorithmParameterGenerator.DiffieHellman", +- "com.sun.crypto.provider.DHParameterGenerator"); +- put("Alg.Alias.AlgorithmParameterGenerator.DH", +- "DiffieHellman"); +- put("Alg.Alias.AlgorithmParameterGenerator.OID."+OID_PKCS3, +- "DiffieHellman"); +- put("Alg.Alias.AlgorithmParameterGenerator."+OID_PKCS3, +- "DiffieHellman"); +- +- /* +- * Key Agreement engines +- */ +- put("KeyAgreement.DiffieHellman", +- "com.sun.crypto.provider.DHKeyAgreement"); +- put("Alg.Alias.KeyAgreement.DH", "DiffieHellman"); +- put("Alg.Alias.KeyAgreement.OID."+OID_PKCS3, "DiffieHellman"); +- put("Alg.Alias.KeyAgreement."+OID_PKCS3, "DiffieHellman"); +- +- put("KeyAgreement.DiffieHellman SupportedKeyClasses", +- "javax.crypto.interfaces.DHPublicKey" + +- "|javax.crypto.interfaces.DHPrivateKey"); +- +- /* +- * Algorithm Parameter engines +- */ +- put("AlgorithmParameters.DiffieHellman", +- "com.sun.crypto.provider.DHParameters"); +- put("Alg.Alias.AlgorithmParameters.DH", "DiffieHellman"); +- put("Alg.Alias.AlgorithmParameters.OID."+OID_PKCS3, +- "DiffieHellman"); +- put("Alg.Alias.AlgorithmParameters."+OID_PKCS3, +- "DiffieHellman"); +- +- put("AlgorithmParameters.DES", +- "com.sun.crypto.provider.DESParameters"); +- +- put("AlgorithmParameters.DESede", +- "com.sun.crypto.provider.DESedeParameters"); +- put("Alg.Alias.AlgorithmParameters.TripleDES", "DESede"); +- +- put("AlgorithmParameters.PBE", +- "com.sun.crypto.provider.PBEParameters"); +- +- put("AlgorithmParameters.PBEWithMD5AndDES", +- "com.sun.crypto.provider.PBEParameters"); +- put("Alg.Alias.AlgorithmParameters.OID."+OID_PKCS5_MD5_DES, +- "PBEWithMD5AndDES"); +- put("Alg.Alias.AlgorithmParameters."+OID_PKCS5_MD5_DES, +- "PBEWithMD5AndDES"); +- +- put("AlgorithmParameters.PBEWithMD5AndTripleDES", +- "com.sun.crypto.provider.PBEParameters"); +- +- put("AlgorithmParameters.PBEWithSHA1AndDESede", +- "com.sun.crypto.provider.PBEParameters"); +- put("Alg.Alias.AlgorithmParameters.OID."+OID_PKCS12_DESede, +- "PBEWithSHA1AndDESede"); +- put("Alg.Alias.AlgorithmParameters."+OID_PKCS12_DESede, +- "PBEWithSHA1AndDESede"); +- +- put("AlgorithmParameters.PBEWithSHA1AndRC2_40", +- "com.sun.crypto.provider.PBEParameters"); +- put("Alg.Alias.AlgorithmParameters.OID."+OID_PKCS12_RC2_40, +- "PBEWithSHA1AndRC2_40"); +- put("Alg.Alias.AlgorithmParameters." + OID_PKCS12_RC2_40, +- "PBEWithSHA1AndRC2_40"); +- +- put("AlgorithmParameters.PBEWithSHA1AndRC2_128", +- "com.sun.crypto.provider.PBEParameters"); +- put("Alg.Alias.AlgorithmParameters.OID."+OID_PKCS12_RC2_128, +- "PBEWithSHA1AndRC2_128"); +- put("Alg.Alias.AlgorithmParameters." + OID_PKCS12_RC2_128, +- "PBEWithSHA1AndRC2_128"); +- +- put("AlgorithmParameters.PBEWithSHA1AndRC4_40", +- "com.sun.crypto.provider.PBEParameters"); +- put("Alg.Alias.AlgorithmParameters.OID."+OID_PKCS12_RC4_40, +- "PBEWithSHA1AndRC4_40"); +- put("Alg.Alias.AlgorithmParameters." + OID_PKCS12_RC4_40, +- "PBEWithSHA1AndRC4_40"); +- +- put("AlgorithmParameters.PBEWithSHA1AndRC4_128", +- "com.sun.crypto.provider.PBEParameters"); +- put("Alg.Alias.AlgorithmParameters.OID."+OID_PKCS12_RC4_128, +- "PBEWithSHA1AndRC4_128"); +- put("Alg.Alias.AlgorithmParameters." + OID_PKCS12_RC4_128, +- "PBEWithSHA1AndRC4_128"); +- +- put("AlgorithmParameters.PBES2", +- "com.sun.crypto.provider.PBES2Parameters$General"); +- put("Alg.Alias.AlgorithmParameters.OID."+OID_PKCS5_PBES2, +- "PBES2"); +- put("Alg.Alias.AlgorithmParameters." + OID_PKCS5_PBES2, +- "PBES2"); +- +- put("AlgorithmParameters.PBEWithHmacSHA1AndAES_128", +- "com.sun.crypto.provider.PBES2Parameters$HmacSHA1AndAES_128"); +- +- put("AlgorithmParameters.PBEWithHmacSHA224AndAES_128", +- "com.sun.crypto.provider.PBES2Parameters$HmacSHA224AndAES_128"); +- +- put("AlgorithmParameters.PBEWithHmacSHA256AndAES_128", +- "com.sun.crypto.provider.PBES2Parameters$HmacSHA256AndAES_128"); +- +- put("AlgorithmParameters.PBEWithHmacSHA384AndAES_128", +- "com.sun.crypto.provider.PBES2Parameters$HmacSHA384AndAES_128"); +- +- put("AlgorithmParameters.PBEWithHmacSHA512AndAES_128", +- "com.sun.crypto.provider.PBES2Parameters$HmacSHA512AndAES_128"); +- +- put("AlgorithmParameters.PBEWithHmacSHA1AndAES_256", +- "com.sun.crypto.provider.PBES2Parameters$HmacSHA1AndAES_256"); +- +- put("AlgorithmParameters.PBEWithHmacSHA224AndAES_256", +- "com.sun.crypto.provider.PBES2Parameters$HmacSHA224AndAES_256"); +- +- put("AlgorithmParameters.PBEWithHmacSHA256AndAES_256", +- "com.sun.crypto.provider.PBES2Parameters$HmacSHA256AndAES_256"); +- +- put("AlgorithmParameters.PBEWithHmacSHA384AndAES_256", +- "com.sun.crypto.provider.PBES2Parameters$HmacSHA384AndAES_256"); +- +- put("AlgorithmParameters.PBEWithHmacSHA512AndAES_256", +- "com.sun.crypto.provider.PBES2Parameters$HmacSHA512AndAES_256"); +- +- put("AlgorithmParameters.Blowfish", +- "com.sun.crypto.provider.BlowfishParameters"); +- +- put("AlgorithmParameters.AES", +- "com.sun.crypto.provider.AESParameters"); +- put("Alg.Alias.AlgorithmParameters.Rijndael", "AES"); +- put("AlgorithmParameters.GCM", +- "com.sun.crypto.provider.GCMParameters"); +- +- +- put("AlgorithmParameters.RC2", +- "com.sun.crypto.provider.RC2Parameters"); +- +- put("AlgorithmParameters.OAEP", +- "com.sun.crypto.provider.OAEPParameters"); +- +- /* +- * Key factories +- */ +- put("KeyFactory.DiffieHellman", +- "com.sun.crypto.provider.DHKeyFactory"); +- put("Alg.Alias.KeyFactory.DH", "DiffieHellman"); +- put("Alg.Alias.KeyFactory.OID."+OID_PKCS3, +- "DiffieHellman"); +- put("Alg.Alias.KeyFactory."+OID_PKCS3, "DiffieHellman"); +- +- /* +- * Secret-key factories +- */ +- put("SecretKeyFactory.DES", +- "com.sun.crypto.provider.DESKeyFactory"); +- +- put("SecretKeyFactory.DESede", +- "com.sun.crypto.provider.DESedeKeyFactory"); +- put("Alg.Alias.SecretKeyFactory.TripleDES", "DESede"); +- +- put("SecretKeyFactory.PBEWithMD5AndDES", +- "com.sun.crypto.provider.PBEKeyFactory$PBEWithMD5AndDES" +- ); +- put("Alg.Alias.SecretKeyFactory.OID."+OID_PKCS5_MD5_DES, +- "PBEWithMD5AndDES"); +- put("Alg.Alias.SecretKeyFactory."+OID_PKCS5_MD5_DES, +- "PBEWithMD5AndDES"); +- +- put("Alg.Alias.SecretKeyFactory.PBE", +- "PBEWithMD5AndDES"); +- +- /* +- * Internal in-house crypto algorithm used for +- * the JCEKS keystore type. Since this was developed +- * internally, there isn't an OID corresponding to this +- * algorithm. +- */ +- put("SecretKeyFactory.PBEWithMD5AndTripleDES", +- "com.sun.crypto.provider.PBEKeyFactory$" + +- "PBEWithMD5AndTripleDES" +- ); +- +- put("SecretKeyFactory.PBEWithSHA1AndDESede", +- "com.sun.crypto.provider.PBEKeyFactory$PBEWithSHA1AndDESede" +- ); +- put("Alg.Alias.SecretKeyFactory.OID."+OID_PKCS12_DESede, +- "PBEWithSHA1AndDESede"); +- put("Alg.Alias.SecretKeyFactory." + OID_PKCS12_DESede, +- "PBEWithSHA1AndDESede"); +- +- put("SecretKeyFactory.PBEWithSHA1AndRC2_40", +- "com.sun.crypto.provider.PBEKeyFactory$PBEWithSHA1AndRC2_40" +- ); +- put("Alg.Alias.SecretKeyFactory.OID." + OID_PKCS12_RC2_40, +- "PBEWithSHA1AndRC2_40"); +- put("Alg.Alias.SecretKeyFactory." + OID_PKCS12_RC2_40, +- "PBEWithSHA1AndRC2_40"); +- +- put("SecretKeyFactory.PBEWithSHA1AndRC2_128", +- "com.sun.crypto.provider.PBEKeyFactory$PBEWithSHA1AndRC2_128" +- ); +- put("Alg.Alias.SecretKeyFactory.OID." + OID_PKCS12_RC2_128, +- "PBEWithSHA1AndRC2_128"); +- put("Alg.Alias.SecretKeyFactory." + OID_PKCS12_RC2_128, +- "PBEWithSHA1AndRC2_128"); +- +- put("SecretKeyFactory.PBEWithSHA1AndRC4_40", +- "com.sun.crypto.provider.PBEKeyFactory$PBEWithSHA1AndRC4_40" +- ); +- +- put("Alg.Alias.SecretKeyFactory.OID." + OID_PKCS12_RC4_40, +- "PBEWithSHA1AndRC4_40"); +- put("Alg.Alias.SecretKeyFactory." + OID_PKCS12_RC4_40, +- "PBEWithSHA1AndRC4_40"); +- +- put("SecretKeyFactory.PBEWithSHA1AndRC4_128", +- "com.sun.crypto.provider.PBEKeyFactory$PBEWithSHA1AndRC4_128" +- ); +- +- put("Alg.Alias.SecretKeyFactory.OID." + OID_PKCS12_RC4_128, +- "PBEWithSHA1AndRC4_128"); +- put("Alg.Alias.SecretKeyFactory." + OID_PKCS12_RC4_128, +- "PBEWithSHA1AndRC4_128"); +- +- put("SecretKeyFactory.PBEWithHmacSHA1AndAES_128", +- "com.sun.crypto.provider.PBEKeyFactory$" + +- "PBEWithHmacSHA1AndAES_128"); +- +- put("SecretKeyFactory.PBEWithHmacSHA224AndAES_128", +- "com.sun.crypto.provider.PBEKeyFactory$" + +- "PBEWithHmacSHA224AndAES_128"); +- +- put("SecretKeyFactory.PBEWithHmacSHA256AndAES_128", +- "com.sun.crypto.provider.PBEKeyFactory$" + +- "PBEWithHmacSHA256AndAES_128"); +- +- put("SecretKeyFactory.PBEWithHmacSHA384AndAES_128", +- "com.sun.crypto.provider.PBEKeyFactory$" + +- "PBEWithHmacSHA384AndAES_128"); +- +- put("SecretKeyFactory.PBEWithHmacSHA512AndAES_128", +- "com.sun.crypto.provider.PBEKeyFactory$" + +- "PBEWithHmacSHA512AndAES_128"); +- +- put("SecretKeyFactory.PBEWithHmacSHA1AndAES_256", +- "com.sun.crypto.provider.PBEKeyFactory$" + +- "PBEWithHmacSHA1AndAES_256"); +- +- put("SecretKeyFactory.PBEWithHmacSHA224AndAES_256", +- "com.sun.crypto.provider.PBEKeyFactory$" + +- "PBEWithHmacSHA224AndAES_256"); +- +- put("SecretKeyFactory.PBEWithHmacSHA256AndAES_256", +- "com.sun.crypto.provider.PBEKeyFactory$" + +- "PBEWithHmacSHA256AndAES_256"); +- +- put("SecretKeyFactory.PBEWithHmacSHA384AndAES_256", +- "com.sun.crypto.provider.PBEKeyFactory$" + +- "PBEWithHmacSHA384AndAES_256"); +- +- put("SecretKeyFactory.PBEWithHmacSHA512AndAES_256", +- "com.sun.crypto.provider.PBEKeyFactory$" + +- "PBEWithHmacSHA512AndAES_256"); +- +- // PBKDF2 +- +- put("SecretKeyFactory.PBKDF2WithHmacSHA1", +- "com.sun.crypto.provider.PBKDF2Core$HmacSHA1"); +- put("Alg.Alias.SecretKeyFactory.OID." + OID_PKCS5_PBKDF2, +- "PBKDF2WithHmacSHA1"); +- put("Alg.Alias.SecretKeyFactory." + OID_PKCS5_PBKDF2, +- "PBKDF2WithHmacSHA1"); +- +- put("SecretKeyFactory.PBKDF2WithHmacSHA224", +- "com.sun.crypto.provider.PBKDF2Core$HmacSHA224"); +- put("SecretKeyFactory.PBKDF2WithHmacSHA256", +- "com.sun.crypto.provider.PBKDF2Core$HmacSHA256"); +- put("SecretKeyFactory.PBKDF2WithHmacSHA384", +- "com.sun.crypto.provider.PBKDF2Core$HmacSHA384"); +- put("SecretKeyFactory.PBKDF2WithHmacSHA512", +- "com.sun.crypto.provider.PBKDF2Core$HmacSHA512"); +- +- /* +- * MAC +- */ +- put("Mac.HmacMD5", "com.sun.crypto.provider.HmacMD5"); +- put("Mac.HmacSHA1", "com.sun.crypto.provider.HmacSHA1"); +- put("Alg.Alias.Mac.OID.1.2.840.113549.2.7", "HmacSHA1"); +- put("Alg.Alias.Mac.1.2.840.113549.2.7", "HmacSHA1"); +- put("Mac.HmacSHA224", +- "com.sun.crypto.provider.HmacCore$HmacSHA224"); +- put("Alg.Alias.Mac.OID.1.2.840.113549.2.8", "HmacSHA224"); +- put("Alg.Alias.Mac.1.2.840.113549.2.8", "HmacSHA224"); +- put("Mac.HmacSHA256", +- "com.sun.crypto.provider.HmacCore$HmacSHA256"); +- put("Alg.Alias.Mac.OID.1.2.840.113549.2.9", "HmacSHA256"); +- put("Alg.Alias.Mac.1.2.840.113549.2.9", "HmacSHA256"); +- put("Mac.HmacSHA384", +- "com.sun.crypto.provider.HmacCore$HmacSHA384"); +- put("Alg.Alias.Mac.OID.1.2.840.113549.2.10", "HmacSHA384"); +- put("Alg.Alias.Mac.1.2.840.113549.2.10", "HmacSHA384"); +- put("Mac.HmacSHA512", +- "com.sun.crypto.provider.HmacCore$HmacSHA512"); +- put("Alg.Alias.Mac.OID.1.2.840.113549.2.11", "HmacSHA512"); +- put("Alg.Alias.Mac.1.2.840.113549.2.11", "HmacSHA512"); +- +- put("Mac.HmacPBESHA1", +- "com.sun.crypto.provider.HmacPKCS12PBECore$HmacPKCS12PBE_SHA1"); +- put("Mac.HmacPBESHA224", +- "com.sun.crypto.provider.HmacPKCS12PBECore$HmacPKCS12PBE_SHA224"); +- put("Mac.HmacPBESHA256", +- "com.sun.crypto.provider.HmacPKCS12PBECore$HmacPKCS12PBE_SHA256"); +- put("Mac.HmacPBESHA384", +- "com.sun.crypto.provider.HmacPKCS12PBECore$HmacPKCS12PBE_SHA384"); +- put("Mac.HmacPBESHA512", +- "com.sun.crypto.provider.HmacPKCS12PBECore$HmacPKCS12PBE_SHA512"); +- put("Mac.HmacPBESHA512/224", +- "com.sun.crypto.provider.HmacPKCS12PBECore$HmacPKCS12PBE_SHA512_224"); +- put("Mac.HmacPBESHA512/256", +- "com.sun.crypto.provider.HmacPKCS12PBECore$HmacPKCS12PBE_SHA512_256"); +- +- // PBMAC1 +- +- put("Mac.PBEWithHmacSHA1", +- "com.sun.crypto.provider.PBMAC1Core$HmacSHA1"); +- put("Mac.PBEWithHmacSHA224", +- "com.sun.crypto.provider.PBMAC1Core$HmacSHA224"); +- put("Mac.PBEWithHmacSHA256", +- "com.sun.crypto.provider.PBMAC1Core$HmacSHA256"); +- put("Mac.PBEWithHmacSHA384", +- "com.sun.crypto.provider.PBMAC1Core$HmacSHA384"); +- put("Mac.PBEWithHmacSHA512", +- "com.sun.crypto.provider.PBMAC1Core$HmacSHA512"); +- +- put("Mac.SslMacMD5", +- "com.sun.crypto.provider.SslMacCore$SslMacMD5"); +- put("Mac.SslMacSHA1", +- "com.sun.crypto.provider.SslMacCore$SslMacSHA1"); +- +- put("Mac.HmacMD5 SupportedKeyFormats", "RAW"); +- put("Mac.HmacSHA1 SupportedKeyFormats", "RAW"); +- put("Mac.HmacSHA224 SupportedKeyFormats", "RAW"); +- put("Mac.HmacSHA256 SupportedKeyFormats", "RAW"); +- put("Mac.HmacSHA384 SupportedKeyFormats", "RAW"); +- put("Mac.HmacSHA512 SupportedKeyFormats", "RAW"); +- put("Mac.HmacPBESHA1 SupportedKeyFormats", "RAW"); +- put("Mac.HmacPBESHA224 SupportedKeyFormats", "RAW"); +- put("Mac.HmacPBESHA256 SupportedKeyFormats", "RAW"); +- put("Mac.HmacPBESHA384 SupportedKeyFormats", "RAW"); +- put("Mac.HmacPBESHA512 SupportedKeyFormats", "RAW"); +- put("Mac.HmacPBESHA512/224 SupportedKeyFormats", "RAW"); +- put("Mac.HmacPBESHA512/256 SupportedKeyFormats", "RAW"); +- put("Mac.PBEWithHmacSHA1 SupportedKeyFormatS", "RAW"); +- put("Mac.PBEWithHmacSHA224 SupportedKeyFormats", "RAW"); +- put("Mac.PBEWithHmacSHA256 SupportedKeyFormats", "RAW"); +- put("Mac.PBEWithHmacSHA384 SupportedKeyFormats", "RAW"); +- put("Mac.PBEWithHmacSHA512 SupportedKeyFormats", "RAW"); +- put("Mac.SslMacMD5 SupportedKeyFormats", "RAW"); +- put("Mac.SslMacSHA1 SupportedKeyFormats", "RAW"); +- +- /* +- * KeyStore +- */ +- put("KeyStore.JCEKS", "com.sun.crypto.provider.JceKeyStore"); +- +- /* +- * SSL/TLS mechanisms +- * +- * These are strictly internal implementations and may +- * be changed at any time. These names were chosen +- * because PKCS11/SunPKCS11 does not yet have TLS1.2 +- * mechanisms, and it will cause calls to come here. +- */ +- put("KeyGenerator.SunTlsPrf", +- "com.sun.crypto.provider.TlsPrfGenerator$V10"); +- put("KeyGenerator.SunTls12Prf", +- "com.sun.crypto.provider.TlsPrfGenerator$V12"); +- +- put("KeyGenerator.SunTlsMasterSecret", +- "com.sun.crypto.provider.TlsMasterSecretGenerator"); +- put("Alg.Alias.KeyGenerator.SunTls12MasterSecret", +- "SunTlsMasterSecret"); +- put("Alg.Alias.KeyGenerator.SunTlsExtendedMasterSecret", +- "SunTlsMasterSecret"); +- +- put("KeyGenerator.SunTlsKeyMaterial", +- "com.sun.crypto.provider.TlsKeyMaterialGenerator"); +- put("Alg.Alias.KeyGenerator.SunTls12KeyMaterial", +- "SunTlsKeyMaterial"); +- +- put("KeyGenerator.SunTlsRsaPremasterSecret", +- "com.sun.crypto.provider.TlsRsaPremasterSecretGenerator"); +- put("Alg.Alias.KeyGenerator.SunTls12RsaPremasterSecret", +- "SunTlsRsaPremasterSecret"); +- +- return null; +- } +- }); +- +- if (instance == null) { +- instance = this; +- } ++ attrs.clear(); ++ attrs.put("SupportedModes", BLOCK_MODES); ++ attrs.put("SupportedPaddings", BLOCK_PADS); ++ attrs.put("SupportedKeyFormats", "RAW"); ++ ps("Cipher", "DES", ++ "com.sun.crypto.provider.DESCipher", null, attrs); ++ ps("Cipher", "DESede", "com.sun.crypto.provider.DESedeCipher", ++ desEdeAliases, attrs); ++ ps("Cipher", "Blowfish", ++ "com.sun.crypto.provider.BlowfishCipher", null, attrs); ++ ++ ps("Cipher", "RC2", ++ "com.sun.crypto.provider.RC2Cipher", null, attrs); ++ ++ attrs.clear(); ++ attrs.put("SupportedModes", BLOCK_MODES128); ++ attrs.put("SupportedPaddings", BLOCK_PADS); ++ attrs.put("SupportedKeyFormats", "RAW"); ++ ps("Cipher", "AES", "com.sun.crypto.provider.AESCipher$General", ++ aesAliases, attrs); ++ ++ attrs.clear(); ++ attrs.put("SupportedKeyFormats", "RAW"); ++ ps("Cipher", "AES_128/ECB/NoPadding", ++ "com.sun.crypto.provider.AESCipher$AES128_ECB_NoPadding", ++ createAliasesWithOid(aes128Oid+"1"), attrs); ++ ps("Cipher", "AES_128/CBC/NoPadding", ++ "com.sun.crypto.provider.AESCipher$AES128_CBC_NoPadding", ++ createAliasesWithOid(aes128Oid+"2"), attrs); ++ ps("Cipher", "AES_128/OFB/NoPadding", ++ "com.sun.crypto.provider.AESCipher$AES128_OFB_NoPadding", ++ createAliasesWithOid(aes128Oid+"3"), attrs); ++ ps("Cipher", "AES_128/CFB/NoPadding", ++ "com.sun.crypto.provider.AESCipher$AES128_CFB_NoPadding", ++ createAliasesWithOid(aes128Oid+"4"), attrs); ++ ps("Cipher", "AES_128/GCM/NoPadding", ++ "com.sun.crypto.provider.AESCipher$AES128_GCM_NoPadding", ++ createAliasesWithOid(aes128Oid+"6"), attrs); ++ ++ ps("Cipher", "AES_192/ECB/NoPadding", ++ "com.sun.crypto.provider.AESCipher$AES192_ECB_NoPadding", ++ createAliasesWithOid(aes192Oid+"1"), attrs); ++ ps("Cipher", "AES_192/CBC/NoPadding", ++ "com.sun.crypto.provider.AESCipher$AES192_CBC_NoPadding", ++ createAliasesWithOid(aes192Oid+"2"), attrs); ++ ps("Cipher", "AES_192/OFB/NoPadding", ++ "com.sun.crypto.provider.AESCipher$AES192_OFB_NoPadding", ++ createAliasesWithOid(aes192Oid+"3"), attrs); ++ ps("Cipher", "AES_192/CFB/NoPadding", ++ "com.sun.crypto.provider.AESCipher$AES192_CFB_NoPadding", ++ createAliasesWithOid(aes192Oid+"4"), attrs); ++ ps("Cipher", "AES_192/GCM/NoPadding", ++ "com.sun.crypto.provider.AESCipher$AES192_GCM_NoPadding", ++ createAliasesWithOid(aes192Oid+"6"), attrs); ++ ++ ps("Cipher", "AES_256/ECB/NoPadding", ++ "com.sun.crypto.provider.AESCipher$AES256_ECB_NoPadding", ++ createAliasesWithOid(aes256Oid+"1"), attrs); ++ ps("Cipher", "AES_256/CBC/NoPadding", ++ "com.sun.crypto.provider.AESCipher$AES256_CBC_NoPadding", ++ createAliasesWithOid(aes256Oid+"2"), attrs); ++ ps("Cipher", "AES_256/OFB/NoPadding", ++ "com.sun.crypto.provider.AESCipher$AES256_OFB_NoPadding", ++ createAliasesWithOid(aes256Oid+"3"), attrs); ++ ps("Cipher", "AES_256/CFB/NoPadding", ++ "com.sun.crypto.provider.AESCipher$AES256_CFB_NoPadding", ++ createAliasesWithOid(aes256Oid+"4"), attrs); ++ ps("Cipher", "AES_256/GCM/NoPadding", ++ "com.sun.crypto.provider.AESCipher$AES256_GCM_NoPadding", ++ createAliasesWithOid(aes256Oid+"6"), attrs); ++ ++ attrs.clear(); ++ attrs.put("SupportedModes", "CBC"); ++ attrs.put("SupportedPaddings", "NOPADDING"); ++ attrs.put("SupportedKeyFormats", "RAW"); ++ ps("Cipher", "DESedeWrap", ++ "com.sun.crypto.provider.DESedeWrapCipher", null, attrs); ++ ++ attrs.clear(); ++ attrs.put("SupportedModes", "ECB"); ++ attrs.put("SupportedPaddings", "NOPADDING"); ++ attrs.put("SupportedKeyFormats", "RAW"); ++ ps("Cipher", "ARCFOUR", "com.sun.crypto.provider.ARCFOURCipher", ++ arcFourAliases, attrs); ++ ps("Cipher", "AESWrap", "com.sun.crypto.provider.AESWrapCipher$General", ++ null, attrs); ++ ps("Cipher", "AESWrap_128", ++ "com.sun.crypto.provider.AESWrapCipher$AES128", ++ createAliasesWithOid(aes128Oid+"5"), attrs); ++ ps("Cipher", "AESWrap_192", ++ "com.sun.crypto.provider.AESWrapCipher$AES192", ++ createAliasesWithOid(aes192Oid+"5"), attrs); ++ ps("Cipher", "AESWrap_256", ++ "com.sun.crypto.provider.AESWrapCipher$AES256", ++ createAliasesWithOid(aes256Oid+"5"), attrs); ++ ++ attrs.clear(); ++ attrs.put("SupportedKeyFormats", "RAW"); ++ ++ // PBES1 ++ ps("Cipher", "PBEWithMD5AndDES", ++ "com.sun.crypto.provider.PBEWithMD5AndDESCipher", ++ pkcs5MD5_DESAliases, null); ++ ps("Cipher", "PBEWithMD5AndTripleDES", ++ "com.sun.crypto.provider.PBEWithMD5AndTripleDESCipher", ++ null, null); ++ ps("Cipher", "PBEWithSHA1AndDESede", ++ "com.sun.crypto.provider.PKCS12PBECipherCore$PBEWithSHA1AndDESede", ++ pkcs12DESedeAliases, null); ++ ps("Cipher", "PBEWithSHA1AndRC2_40", ++ "com.sun.crypto.provider.PKCS12PBECipherCore$PBEWithSHA1AndRC2_40", ++ pkcs12RC2_40Aliases, null); ++ ps("Cipher", "PBEWithSHA1AndRC2_128", ++ "com.sun.crypto.provider.PKCS12PBECipherCore$PBEWithSHA1AndRC2_128", ++ pkcs12RC2_128Aliases, null); ++ ps("Cipher", "PBEWithSHA1AndRC4_40", ++ "com.sun.crypto.provider.PKCS12PBECipherCore$PBEWithSHA1AndRC4_40", ++ pkcs12RC4_40Aliases, null); ++ ++ ps("Cipher", "PBEWithSHA1AndRC4_128", ++ "com.sun.crypto.provider.PKCS12PBECipherCore$PBEWithSHA1AndRC4_128", ++ pkcs12RC4_128Aliases, null); ++ ++ // PBES2 ++ ps("Cipher", "PBEWithHmacSHA1AndAES_128", ++ "com.sun.crypto.provider.PBES2Core$HmacSHA1AndAES_128", ++ null, null); ++ ++ ps("Cipher", "PBEWithHmacSHA224AndAES_128", ++ "com.sun.crypto.provider.PBES2Core$HmacSHA224AndAES_128", ++ null, null); ++ ++ ps("Cipher", "PBEWithHmacSHA256AndAES_128", ++ "com.sun.crypto.provider.PBES2Core$HmacSHA256AndAES_128", ++ null, null); ++ ++ ps("Cipher", "PBEWithHmacSHA384AndAES_128", ++ "com.sun.crypto.provider.PBES2Core$HmacSHA384AndAES_128", ++ null, null); ++ ++ ps("Cipher", "PBEWithHmacSHA512AndAES_128", ++ "com.sun.crypto.provider.PBES2Core$HmacSHA512AndAES_128", ++ null, null); ++ ++ ps("Cipher", "PBEWithHmacSHA1AndAES_256", ++ "com.sun.crypto.provider.PBES2Core$HmacSHA1AndAES_256", ++ null, null); ++ ++ ps("Cipher", "PBEWithHmacSHA224AndAES_256", ++ "com.sun.crypto.provider.PBES2Core$HmacSHA224AndAES_256", ++ null, null); ++ ++ ps("Cipher", "PBEWithHmacSHA256AndAES_256", ++ "com.sun.crypto.provider.PBES2Core$HmacSHA256AndAES_256", ++ null, null); ++ ++ ps("Cipher", "PBEWithHmacSHA384AndAES_256", ++ "com.sun.crypto.provider.PBES2Core$HmacSHA384AndAES_256", ++ null, null); ++ ++ ps("Cipher", "PBEWithHmacSHA512AndAES_256", ++ "com.sun.crypto.provider.PBES2Core$HmacSHA512AndAES_256", ++ null, null); ++ ++ /* ++ * Key(pair) Generator engines ++ */ ++ ps("KeyGenerator", "DES", ++ "com.sun.crypto.provider.DESKeyGenerator", ++ null, null); ++ ps("KeyGenerator", "DESede", ++ "com.sun.crypto.provider.DESedeKeyGenerator", ++ desEdeAliases, null); ++ ps("KeyGenerator", "Blowfish", ++ "com.sun.crypto.provider.BlowfishKeyGenerator", ++ null, null); ++ ps("KeyGenerator", "AES", ++ "com.sun.crypto.provider.AESKeyGenerator", ++ aesAliases, null); ++ ps("KeyGenerator", "RC2", ++ "com.sun.crypto.provider.KeyGeneratorCore$RC2KeyGenerator", ++ null, null); ++ ps("KeyGenerator", "ARCFOUR", ++ "com.sun.crypto.provider.KeyGeneratorCore$ARCFOURKeyGenerator", ++ arcFourAliases, null); ++ ps("KeyGenerator", "HmacMD5", ++ "com.sun.crypto.provider.HmacMD5KeyGenerator", ++ null, null); ++ ++ ps("KeyGenerator", "HmacSHA1", ++ "com.sun.crypto.provider.HmacSHA1KeyGenerator", ++ macSHA1Aliases, null); ++ ps("KeyGenerator", "HmacSHA224", ++ "com.sun.crypto.provider.KeyGeneratorCore$HmacSHA2KG$SHA224", ++ macSHA224Aliases, null); ++ ps("KeyGenerator", "HmacSHA256", ++ "com.sun.crypto.provider.KeyGeneratorCore$HmacSHA2KG$SHA256", ++ macSHA256Aliases, null); ++ ps("KeyGenerator", "HmacSHA384", ++ "com.sun.crypto.provider.KeyGeneratorCore$HmacSHA2KG$SHA384", ++ macSHA384Aliases, null); ++ ps("KeyGenerator", "HmacSHA512", ++ "com.sun.crypto.provider.KeyGeneratorCore$HmacSHA2KG$SHA512", ++ macSHA512Aliases, null); ++ ++ ps("KeyPairGenerator", "DiffieHellman", ++ "com.sun.crypto.provider.DHKeyPairGenerator", ++ diffieHellmanAliases, null); ++ ++ /* ++ * Algorithm parameter generation engines ++ */ ++ ps("AlgorithmParameterGenerator", ++ "DiffieHellman", "com.sun.crypto.provider.DHParameterGenerator", ++ diffieHellmanAliases, null); ++ ++ /* ++ * Key Agreement engines ++ */ ++ attrs.clear(); ++ attrs.put("SupportedKeyClasses", "javax.crypto.interfaces.DHPublicKey" + ++ "|javax.crypto.interfaces.DHPrivateKey"); ++ ps("KeyAgreement", "DiffieHellman", ++ "com.sun.crypto.provider.DHKeyAgreement", ++ diffieHellmanAliases, attrs); ++ ++ /* ++ * Algorithm Parameter engines ++ */ ++ ps("AlgorithmParameters", "DiffieHellman", ++ "com.sun.crypto.provider.DHParameters", ++ diffieHellmanAliases, null); ++ ++ ps("AlgorithmParameters", "DES", ++ "com.sun.crypto.provider.DESParameters", ++ null, null); ++ ++ ps("AlgorithmParameters", "DESede", ++ "com.sun.crypto.provider.DESedeParameters", ++ desEdeAliases, null); ++ ++ ps("AlgorithmParameters", "PBEWithMD5AndDES", ++ "com.sun.crypto.provider.PBEParameters", ++ pkcs5MD5_DESAliases, null); ++ ++ ps("AlgorithmParameters", "PBEWithMD5AndTripleDES", ++ "com.sun.crypto.provider.PBEParameters", ++ null, null); ++ ++ ps("AlgorithmParameters", "PBEWithSHA1AndDESede", ++ "com.sun.crypto.provider.PBEParameters", ++ pkcs12DESedeAliases, null); ++ ++ ps("AlgorithmParameters", "PBEWithSHA1AndRC2_40", ++ "com.sun.crypto.provider.PBEParameters", ++ pkcs12RC2_40Aliases, null); ++ ++ ps("AlgorithmParameters", "PBEWithSHA1AndRC2_128", ++ "com.sun.crypto.provider.PBEParameters", ++ pkcs12RC2_128Aliases, null); ++ ++ ps("AlgorithmParameters", "PBEWithSHA1AndRC4_40", ++ "com.sun.crypto.provider.PBEParameters", ++ pkcs12RC4_40Aliases, null); ++ ++ ps("AlgorithmParameters", "PBEWithSHA1AndRC4_128", ++ "com.sun.crypto.provider.PBEParameters", ++ pkcs12RC4_128Aliases, null); ++ ++ ps("AlgorithmParameters", "PBES2", ++ "com.sun.crypto.provider.PBES2Parameters$General", ++ pkcs5PBES2Aliases, null); ++ ++ ps("AlgorithmParameters", "PBEWithHmacSHA1AndAES_128", ++ "com.sun.crypto.provider.PBES2Parameters$HmacSHA1AndAES_128", ++ null, null); ++ ++ ps("AlgorithmParameters", "PBEWithHmacSHA224AndAES_128", ++ "com.sun.crypto.provider.PBES2Parameters$HmacSHA224AndAES_128", ++ null, null); ++ ++ ps("AlgorithmParameters", "PBEWithHmacSHA256AndAES_128", ++ "com.sun.crypto.provider.PBES2Parameters$HmacSHA256AndAES_128", ++ null, null); ++ ++ ps("AlgorithmParameters", "PBEWithHmacSHA384AndAES_128", ++ "com.sun.crypto.provider.PBES2Parameters$HmacSHA384AndAES_128", ++ null, null); ++ ++ ps("AlgorithmParameters", "PBEWithHmacSHA512AndAES_128", ++ "com.sun.crypto.provider.PBES2Parameters$HmacSHA512AndAES_128", ++ null, null); ++ ++ ps("AlgorithmParameters", "PBEWithHmacSHA1AndAES_256", ++ "com.sun.crypto.provider.PBES2Parameters$HmacSHA1AndAES_256", ++ null, null); ++ ++ ps("AlgorithmParameters", "PBEWithHmacSHA224AndAES_256", ++ "com.sun.crypto.provider.PBES2Parameters$HmacSHA224AndAES_256", ++ null, null); ++ ++ ps("AlgorithmParameters", "PBEWithHmacSHA256AndAES_256", ++ "com.sun.crypto.provider.PBES2Parameters$HmacSHA256AndAES_256", ++ null, null); ++ ++ ps("AlgorithmParameters", "PBEWithHmacSHA384AndAES_256", ++ "com.sun.crypto.provider.PBES2Parameters$HmacSHA384AndAES_256", ++ null, null); ++ ++ ps("AlgorithmParameters", "PBEWithHmacSHA512AndAES_256", ++ "com.sun.crypto.provider.PBES2Parameters$HmacSHA512AndAES_256", ++ null, null); ++ ++ ps("AlgorithmParameters", "Blowfish", ++ "com.sun.crypto.provider.BlowfishParameters", ++ null, null); ++ ++ ps("AlgorithmParameters", "AES", ++ "com.sun.crypto.provider.AESParameters", ++ aesAliases, null); ++ ++ ps("AlgorithmParameters", "GCM", ++ "com.sun.crypto.provider.GCMParameters", ++ null, null); ++ ++ ps("AlgorithmParameters", "RC2", ++ "com.sun.crypto.provider.RC2Parameters", ++ null, null); ++ ++ ps("AlgorithmParameters", "OAEP", ++ "com.sun.crypto.provider.OAEPParameters", ++ null, null); ++ ++ /* ++ * Key factories ++ */ ++ ps("KeyFactory", "DiffieHellman", ++ "com.sun.crypto.provider.DHKeyFactory", ++ diffieHellmanAliases, null); ++ ++ /* ++ * Secret-key factories ++ */ ++ ps("SecretKeyFactory", "DES", ++ "com.sun.crypto.provider.DESKeyFactory", ++ null, null); ++ ++ ps("SecretKeyFactory", "DESede", ++ "com.sun.crypto.provider.DESedeKeyFactory", ++ desEdeAliases, null); ++ ++ ps("SecretKeyFactory", "PBEWithMD5AndDES", ++ "com.sun.crypto.provider.PBEKeyFactory$PBEWithMD5AndDES", ++ pkcs5MD5_DESAliases, null); ++ ++ /* ++ * Internal in-house crypto algorithm used for ++ * the JCEKS keystore type. Since this was developed ++ * internally, there isn't an OID corresponding to this ++ * algorithm. ++ */ ++ ps("SecretKeyFactory", "PBEWithMD5AndTripleDES", ++ "com.sun.crypto.provider.PBEKeyFactory$PBEWithMD5AndTripleDES", ++ null, null); ++ ++ ps("SecretKeyFactory", "PBEWithSHA1AndDESede", ++ "com.sun.crypto.provider.PBEKeyFactory$PBEWithSHA1AndDESede", ++ pkcs12DESedeAliases, null); ++ ++ ps("SecretKeyFactory", "PBEWithSHA1AndRC2_40", ++ "com.sun.crypto.provider.PBEKeyFactory$PBEWithSHA1AndRC2_40", ++ pkcs12RC2_40Aliases, null); ++ ++ ps("SecretKeyFactory", "PBEWithSHA1AndRC2_128", ++ "com.sun.crypto.provider.PBEKeyFactory$PBEWithSHA1AndRC2_128", ++ pkcs12RC2_128Aliases, null); ++ ++ ps("SecretKeyFactory", "PBEWithSHA1AndRC4_40", ++ "com.sun.crypto.provider.PBEKeyFactory$PBEWithSHA1AndRC4_40", ++ pkcs12RC4_40Aliases,null); ++ ++ ps("SecretKeyFactory", "PBEWithSHA1AndRC4_128", ++ "com.sun.crypto.provider.PBEKeyFactory$PBEWithSHA1AndRC4_128", ++ pkcs12RC4_128Aliases, null); ++ ++ ps("SecretKeyFactory", "PBEWithHmacSHA1AndAES_128", ++ "com.sun.crypto.provider.PBEKeyFactory$PBEWithHmacSHA1AndAES_128", ++ null, null); ++ ++ ps("SecretKeyFactory", "PBEWithHmacSHA224AndAES_128", ++ "com.sun.crypto.provider.PBEKeyFactory$PBEWithHmacSHA224AndAES_128", ++ null, null); ++ ++ ps("SecretKeyFactory", "PBEWithHmacSHA256AndAES_128", ++ "com.sun.crypto.provider.PBEKeyFactory$PBEWithHmacSHA256AndAES_128", ++ null, null); ++ ++ ps("SecretKeyFactory", "PBEWithHmacSHA384AndAES_128", ++ "com.sun.crypto.provider.PBEKeyFactory$PBEWithHmacSHA384AndAES_128", ++ null, null); ++ ++ ps("SecretKeyFactory", "PBEWithHmacSHA512AndAES_128", ++ "com.sun.crypto.provider.PBEKeyFactory$PBEWithHmacSHA512AndAES_128", ++ null, null); ++ ++ ps("SecretKeyFactory", "PBEWithHmacSHA1AndAES_256", ++ "com.sun.crypto.provider.PBEKeyFactory$PBEWithHmacSHA1AndAES_256", ++ null, null); ++ ++ ps("SecretKeyFactory", "PBEWithHmacSHA224AndAES_256", ++ "com.sun.crypto.provider.PBEKeyFactory$PBEWithHmacSHA224AndAES_256", ++ null, null); ++ ++ ps("SecretKeyFactory", "PBEWithHmacSHA256AndAES_256", ++ "com.sun.crypto.provider.PBEKeyFactory$PBEWithHmacSHA256AndAES_256", ++ null, null); ++ ++ ps("SecretKeyFactory", "PBEWithHmacSHA384AndAES_256", ++ "com.sun.crypto.provider.PBEKeyFactory$PBEWithHmacSHA384AndAES_256", ++ null, null); ++ ++ ps("SecretKeyFactory", "PBEWithHmacSHA512AndAES_256", ++ "com.sun.crypto.provider.PBEKeyFactory$PBEWithHmacSHA512AndAES_256", ++ null, null); ++ ++ // PBKDF2 ++ ps("SecretKeyFactory", "PBKDF2WithHmacSHA1", ++ "com.sun.crypto.provider.PBKDF2Core$HmacSHA1", ++ pkcs5PBKDF2Aliases, null); ++ ps("SecretKeyFactory", "PBKDF2WithHmacSHA224", ++ "com.sun.crypto.provider.PBKDF2Core$HmacSHA224", ++ null, null); ++ ps("SecretKeyFactory", "PBKDF2WithHmacSHA256", ++ "com.sun.crypto.provider.PBKDF2Core$HmacSHA256", ++ null, null); ++ ps("SecretKeyFactory", "PBKDF2WithHmacSHA384", ++ "com.sun.crypto.provider.PBKDF2Core$HmacSHA384", ++ null, null); ++ ps("SecretKeyFactory", "PBKDF2WithHmacSHA512", ++ "com.sun.crypto.provider.PBKDF2Core$HmacSHA512", ++ null, null); ++ ++ /* ++ * MAC ++ */ ++ attrs.clear(); ++ attrs.put("SupportedKeyFormats", "RAW"); ++ ps("Mac", "HmacMD5", "com.sun.crypto.provider.HmacMD5", null, attrs); ++ ps("Mac", "HmacSHA1", "com.sun.crypto.provider.HmacSHA1", ++ macSHA1Aliases, attrs); ++ ps("Mac", "HmacSHA224", "com.sun.crypto.provider.HmacCore$HmacSHA224", ++ macSHA224Aliases, attrs); ++ ps("Mac", "HmacSHA256", "com.sun.crypto.provider.HmacCore$HmacSHA256", ++ macSHA256Aliases, attrs); ++ ps("Mac", "HmacSHA384", "com.sun.crypto.provider.HmacCore$HmacSHA384", ++ macSHA384Aliases, attrs); ++ ps("Mac", "HmacSHA512", "com.sun.crypto.provider.HmacCore$HmacSHA512", ++ macSHA512Aliases, attrs); ++ // TODO: aliases with OIDs ++ ps("Mac", "HmacPBESHA1", "com.sun.crypto.provider.HmacPKCS12PBECore$HmacPKCS12PBE_SHA1", ++ null, attrs); ++ ps("Mac", "HmacPBESHA224", "com.sun.crypto.provider.HmacPKCS12PBECore$HmacPKCS12PBE_SHA224", ++ null, attrs); ++ ps("Mac", "HmacPBESHA256", "com.sun.crypto.provider.HmacPKCS12PBECore$HmacPKCS12PBE_SHA256", ++ null, attrs); ++ ps("Mac", "HmacPBESHA384", "com.sun.crypto.provider.HmacPKCS12PBECore$HmacPKCS12PBE_SHA384", ++ null, attrs); ++ ps("Mac", "HmacPBESHA512", "com.sun.crypto.provider.HmacPKCS12PBECore$HmacPKCS12PBE_SHA512", ++ null, attrs); ++ ps("Mac", "HmacPBESHA512/224", "com.sun.crypto.provider.HmacPKCS12PBECore$HmacPKCS12PBE_SHA512_224", ++ null, attrs); ++ ps("Mac", "HmacPBESHA512/256", "com.sun.crypto.provider.HmacPKCS12PBECore$HmacPKCS12PBE_SHA512_256", ++ null, attrs); ++ ++ // PBMAC1 ++ ps("Mac", "PBEWithHmacSHA1", ++ "com.sun.crypto.provider.PBMAC1Core$HmacSHA1", null, attrs); ++ ps("Mac", "PBEWithHmacSHA224", ++ "com.sun.crypto.provider.PBMAC1Core$HmacSHA224", null, attrs); ++ ps("Mac", "PBEWithHmacSHA256", ++ "com.sun.crypto.provider.PBMAC1Core$HmacSHA256", null, attrs); ++ ps("Mac", "PBEWithHmacSHA384", ++ "com.sun.crypto.provider.PBMAC1Core$HmacSHA384", null, attrs); ++ ps("Mac", "PBEWithHmacSHA512", ++ "com.sun.crypto.provider.PBMAC1Core$HmacSHA512", null, attrs); ++ ps("Mac", "SslMacMD5", ++ "com.sun.crypto.provider.SslMacCore$SslMacMD5", null, attrs); ++ ps("Mac", "SslMacSHA1", ++ "com.sun.crypto.provider.SslMacCore$SslMacSHA1", null, attrs); ++ ++ /* ++ * KeyStore ++ */ ++ ps("KeyStore", "JCEKS", ++ "com.sun.crypto.provider.JceKeyStore", ++ null, null); ++ ++ /* ++ * SSL/TLS mechanisms ++ * ++ * These are strictly internal implementations and may ++ * be changed at any time. These names were chosen ++ * because PKCS11/SunPKCS11 does not yet have TLS1.2 ++ * mechanisms, and it will cause calls to come here. ++ */ ++ ps("KeyGenerator", "SunTlsPrf", ++ "com.sun.crypto.provider.TlsPrfGenerator$V10", ++ null, null); ++ ps("KeyGenerator", "SunTls12Prf", ++ "com.sun.crypto.provider.TlsPrfGenerator$V12", ++ null, null); ++ ++ ps("KeyGenerator", "SunTlsMasterSecret", ++ "com.sun.crypto.provider.TlsMasterSecretGenerator", ++ createAliases("SunTls12MasterSecret", ++ "SunTlsExtendedMasterSecret"), null); ++ ps("KeyGenerator", "SunTlsKeyMaterial", ++ "com.sun.crypto.provider.TlsKeyMaterialGenerator", ++ createAliases("SunTls12KeyMaterial"), null); ++ ++ ps("KeyGenerator", "SunTlsRsaPremasterSecret", ++ "com.sun.crypto.provider.TlsRsaPremasterSecretGenerator", ++ createAliases("SunTls12RsaPremasterSecret"), null); + } + + // Return the instance of this class or create one if needed. +diff --git a/jdk/src/share/classes/java/security/AlgorithmParameterGenerator.java b/jdk/src/share/classes/java/security/AlgorithmParameterGenerator.java +index 7f9c7cbf4..b8cb61a56 100644 +--- a/jdk/src/share/classes/java/security/AlgorithmParameterGenerator.java ++++ b/jdk/src/share/classes/java/security/AlgorithmParameterGenerator.java +@@ -26,6 +26,7 @@ + package java.security; + + import java.security.spec.AlgorithmParameterSpec; ++import sun.security.jca.JCAUtil; + + /** + * The {@code AlgorithmParameterGenerator} class is used to generate a +@@ -282,7 +283,7 @@ public class AlgorithmParameterGenerator { + * @param size the size (number of bits). + */ + public final void init(int size) { +- paramGenSpi.engineInit(size, new SecureRandom()); ++ paramGenSpi.engineInit(size, JCAUtil.getSecureRandom()); + } + + /** +@@ -313,7 +314,7 @@ public class AlgorithmParameterGenerator { + */ + public final void init(AlgorithmParameterSpec genParamSpec) + throws InvalidAlgorithmParameterException { +- paramGenSpi.engineInit(genParamSpec, new SecureRandom()); ++ paramGenSpi.engineInit(genParamSpec, JCAUtil.getSecureRandom()); + } + + /** +diff --git a/jdk/src/share/classes/java/security/Provider.java b/jdk/src/share/classes/java/security/Provider.java +index 1eadb0e62..34f5ab22b 100644 +--- a/jdk/src/share/classes/java/security/Provider.java ++++ b/jdk/src/share/classes/java/security/Provider.java +@@ -30,6 +30,7 @@ import java.util.*; + import static java.util.Locale.ENGLISH; + import java.lang.ref.*; + import java.lang.reflect.*; ++import java.util.concurrent.ConcurrentHashMap; + import java.util.function.BiConsumer; + import java.util.function.BiFunction; + import java.util.function.Function; +@@ -135,6 +136,7 @@ public abstract class Provider extends Properties { + this.name = name; + this.version = version; + this.info = info; ++ this.serviceMap = new ConcurrentHashMap<>(); + putId(); + initialized = true; + } +@@ -662,15 +664,20 @@ public abstract class Provider extends Properties { + // legacy properties changed since last call to any services method? + private transient boolean legacyChanged; + // serviceMap changed since last call to getServices() +- private transient boolean servicesChanged; ++ private volatile transient boolean servicesChanged; + +- // Map ++ // Map used to keep track of legacy registration + private transient Map legacyStrings; + + // Map + // used for services added via putService(), initialized on demand + private transient Map serviceMap; + ++ // For backward compatibility, the registration ordering of ++ // SecureRandom (RNG) algorithms needs to be preserved for ++ // "new SecureRandom()" calls when this provider is used ++ private transient Set prngAlgos; ++ + // Map + // used for services added via legacy methods, init on demand + private transient Map legacyMap; +@@ -698,11 +705,13 @@ public abstract class Provider extends Properties { + } + defaults = null; + in.defaultReadObject(); ++ this.serviceMap = new ConcurrentHashMap<>(); + implClear(); + initialized = true; + putAll(copy); + } + ++ // check whether to update 'legacyString' with the specified key + private boolean checkLegacy(Object key) { + String keyString = (String)key; + if (keyString.startsWith("Provider.")) { +@@ -711,7 +720,7 @@ public abstract class Provider extends Properties { + + legacyChanged = true; + if (legacyStrings == null) { +- legacyStrings = new LinkedHashMap(); ++ legacyStrings = new LinkedHashMap<>(); + } + return true; + } +@@ -742,7 +751,7 @@ public abstract class Provider extends Properties { + if (!checkLegacy(key)) { + return false; + } +- legacyStrings.remove((String)key, value); ++ legacyStrings.remove((String)key, (String)value); + } + return super.remove(key, value); + } +@@ -772,7 +781,7 @@ public abstract class Provider extends Properties { + private void implReplaceAll(BiFunction function) { + legacyChanged = true; + if (legacyStrings == null) { +- legacyStrings = new LinkedHashMap(); ++ legacyStrings = new LinkedHashMap<>(); + } else { + legacyStrings.replaceAll((BiFunction) function); + } +@@ -796,8 +805,8 @@ public abstract class Provider extends Properties { + if (!checkLegacy(key)) { + return null; + } +- legacyStrings.computeIfAbsent((String) key, +- (Function) remappingFunction); ++ legacyStrings.compute((String) key, ++ (BiFunction) remappingFunction); + } + return super.compute(key, remappingFunction); + } +@@ -851,12 +860,11 @@ public abstract class Provider extends Properties { + if (legacyMap != null) { + legacyMap.clear(); + } +- if (serviceMap != null) { +- serviceMap.clear(); +- } ++ serviceMap.clear(); + legacyChanged = false; + servicesChanged = false; + serviceSet = null; ++ prngAlgos = null; + super.clear(); + putId(); + } +@@ -873,13 +881,13 @@ public abstract class Provider extends Properties { + this.algorithm = intern ? algorithm.intern() : algorithm; + } + public int hashCode() { +- return type.hashCode() + algorithm.hashCode(); ++ return Objects.hash(type, algorithm); + } + public boolean equals(Object obj) { + if (this == obj) { + return true; + } +- if (obj instanceof ServiceKey == false) { ++ if (!(obj instanceof ServiceKey)) { + return false; + } + ServiceKey other = (ServiceKey)obj; +@@ -901,7 +909,7 @@ public abstract class Provider extends Properties { + } + serviceSet = null; + if (legacyMap == null) { +- legacyMap = new LinkedHashMap(); ++ legacyMap = new ConcurrentHashMap<>(); + } else { + legacyMap.clear(); + } +@@ -957,7 +965,10 @@ public abstract class Provider extends Properties { + String type = getEngineName(typeAndAlg[0]); + String aliasAlg = typeAndAlg[1].intern(); + ServiceKey key = new ServiceKey(type, stdAlg, true); +- Service s = legacyMap.get(key); ++ Service s = serviceMap.get(key); ++ if (s == null) { ++ s = legacyMap.get(key); ++ } + if (s == null) { + s = new Service(this); + s.type = type; +@@ -986,6 +997,10 @@ public abstract class Provider extends Properties { + legacyMap.put(key, s); + } + s.className = className; ++ ++ if (type.equals("SecureRandom")) { ++ updateSecureRandomEntries(true, s.algorithm); ++ } + } else { // attribute + // e.g. put("MessageDigest.SHA-1 ImplementedIn", "Software"); + String attributeValue = value; +@@ -1031,7 +1046,7 @@ public abstract class Provider extends Properties { + * + * @since 1.5 + */ +- public synchronized Service getService(String type, String algorithm) { ++ public Service getService(String type, String algorithm) { + checkInitialized(); + // avoid allocating a new key object if possible + ServiceKey key = previousKey; +@@ -1039,14 +1054,19 @@ public abstract class Provider extends Properties { + key = new ServiceKey(type, algorithm, false); + previousKey = key; + } +- if (serviceMap != null) { +- Service service = serviceMap.get(key); +- if (service != null) { +- return service; ++ if (!serviceMap.isEmpty()) { ++ Service s = serviceMap.get(key); ++ if (s != null) { ++ return s; ++ } ++ } ++ synchronized (this){ ++ ensureLegacyParsed(); ++ if (legacyMap != null && !legacyMap.isEmpty()) { ++ return legacyMap.get(key); + } + } +- ensureLegacyParsed(); +- return (legacyMap != null) ? legacyMap.get(key) : null; ++ return null; + } + + // ServiceKey from previous getService() call +@@ -1075,10 +1095,10 @@ public abstract class Provider extends Properties { + if (serviceSet == null) { + ensureLegacyParsed(); + Set set = new LinkedHashSet<>(); +- if (serviceMap != null) { ++ if (!serviceMap.isEmpty()) { + set.addAll(serviceMap.values()); + } +- if (legacyMap != null) { ++ if (legacyMap != null && !legacyMap.isEmpty()) { + set.addAll(legacyMap.values()); + } + serviceSet = Collections.unmodifiableSet(set); +@@ -1116,7 +1136,7 @@ public abstract class Provider extends Properties { + * + * @since 1.5 + */ +- protected synchronized void putService(Service s) { ++ protected void putService(Service s) { + check("putProviderProperty." + name); + if (debug != null) { + debug.println(name + ".putService(): " + s); +@@ -1128,20 +1148,58 @@ public abstract class Provider extends Properties { + throw new IllegalArgumentException + ("service.getProvider() must match this Provider object"); + } +- if (serviceMap == null) { +- serviceMap = new LinkedHashMap(); +- } +- servicesChanged = true; + String type = s.getType(); + String algorithm = s.getAlgorithm(); + ServiceKey key = new ServiceKey(type, algorithm, true); +- // remove existing service + implRemoveService(serviceMap.get(key)); + serviceMap.put(key, s); + for (String alias : s.getAliases()) { + serviceMap.put(new ServiceKey(type, alias, true), s); + } +- putPropertyStrings(s); ++ servicesChanged = true; ++ synchronized (this) { ++ putPropertyStrings(s); ++ if (type.equals("SecureRandom")) { ++ updateSecureRandomEntries(true, s.algorithm); ++ } ++ } ++ } ++ ++ // keep tracks of the registered secure random algos and store them in order ++ private void updateSecureRandomEntries(boolean doAdd, String s) { ++ Objects.requireNonNull(s); ++ if (doAdd) { ++ if (prngAlgos == null) { ++ prngAlgos = new LinkedHashSet(); ++ } ++ prngAlgos.add(s); ++ } else { ++ prngAlgos.remove(s); ++ } ++ ++ if (debug != null) { ++ debug.println((doAdd? "Add":"Remove") + " SecureRandom algo " + s); ++ } ++ } ++ ++ // used by new SecureRandom() to find out the default SecureRandom ++ // service for this provider ++ synchronized Service getDefaultSecureRandomService() { ++ checkInitialized(); ++ ++ if (legacyChanged) { ++ prngAlgos = null; ++ ensureLegacyParsed(); ++ } ++ ++ if (prngAlgos != null && !prngAlgos.isEmpty()) { ++ // IMPORTANT: use the Service obj returned by getService(...) call ++ // as providers may override putService(...)/getService(...) and ++ // return their own Service objects ++ return getService("SecureRandom", prngAlgos.iterator().next()); ++ } ++ ++ return null; + } + + /** +@@ -1208,7 +1266,7 @@ public abstract class Provider extends Properties { + * + * @since 1.5 + */ +- protected synchronized void removeService(Service s) { ++ protected void removeService(Service s) { + check("removeProviderProperty." + name); + if (debug != null) { + debug.println(name + ".removeService(): " + s); +@@ -1220,7 +1278,7 @@ public abstract class Provider extends Properties { + } + + private void implRemoveService(Service s) { +- if ((s == null) || (serviceMap == null)) { ++ if ((s == null) || serviceMap.isEmpty()) { + return; + } + String type = s.getType(); +@@ -1235,7 +1293,12 @@ public abstract class Provider extends Properties { + for (String alias : s.getAliases()) { + serviceMap.remove(new ServiceKey(type, alias, false)); + } +- removePropertyStrings(s); ++ synchronized (this) { ++ removePropertyStrings(s); ++ if (type.equals("SecureRandom")) { ++ updateSecureRandomEntries(false, s.algorithm); ++ } ++ } + } + + // Wrapped String that behaves in a case insensitive way for equals/hashCode +diff --git a/jdk/src/share/classes/java/security/SecureRandom.java b/jdk/src/share/classes/java/security/SecureRandom.java +index 6848be5a2..05ff79191 100644 +--- a/jdk/src/share/classes/java/security/SecureRandom.java ++++ b/jdk/src/share/classes/java/security/SecureRandom.java +@@ -32,6 +32,7 @@ import java.security.Provider.Service; + + import sun.security.jca.*; + import sun.security.jca.GetInstance.Instance; ++import sun.security.provider.SunEntries; + import sun.security.util.Debug; + + /** +@@ -191,35 +192,50 @@ public class SecureRandom extends java.util.Random { + } + + private void getDefaultPRNG(boolean setSeed, byte[] seed) { +- String prng = getPrngAlgorithm(); +- if (prng == null) { +- // bummer, get the SUN implementation +- prng = "SHA1PRNG"; ++ Service prngService = null; ++ String prngAlgorithm = null; ++ for (Provider p : Providers.getProviderList().providers()) { ++ // SUN provider uses the SunEntries.DEF_SECURE_RANDOM_ALGO ++ // as the default SecureRandom algorithm; for other providers, ++ // Provider.getDefaultSecureRandom() will use the 1st ++ // registered SecureRandom algorithm ++ if (p.getName().equals("SUN")) { ++ prngAlgorithm = SunEntries.DEF_SECURE_RANDOM_ALGO; ++ prngService = p.getService("SecureRandom", prngAlgorithm); ++ break; ++ } else { ++ prngService = p.getDefaultSecureRandomService(); ++ if (prngService != null) { ++ prngAlgorithm = prngService.getAlgorithm(); ++ break; ++ } ++ } ++ } ++ // per javadoc, if none of the Providers support a RNG algorithm, ++ // then an implementation-specific default is returned. ++ if (prngService == null) { ++ prngAlgorithm = "SHA1PRNG"; + this.secureRandomSpi = new sun.security.provider.SecureRandom(); + this.provider = Providers.getSunProvider(); +- if (setSeed) { +- this.secureRandomSpi.engineSetSeed(seed); +- } + } else { + try { +- SecureRandom random = SecureRandom.getInstance(prng); +- this.secureRandomSpi = random.getSecureRandomSpi(); +- this.provider = random.getProvider(); +- if (setSeed) { +- this.secureRandomSpi.engineSetSeed(seed); +- } ++ this.secureRandomSpi = (SecureRandomSpi) prngService.newInstance(null); ++ this.provider = prngService.getProvider(); + } catch (NoSuchAlgorithmException nsae) { +- // never happens, because we made sure the algorithm exists ++ // should not happen + throw new RuntimeException(nsae); + } + } ++ if (setSeed) { ++ this.secureRandomSpi.engineSetSeed(seed); ++ } + // JDK 1.1 based implementations subclass SecureRandom instead of + // SecureRandomSpi. They will also go through this code path because + // they must call a SecureRandom constructor as it is their superclass. + // If we are dealing with such an implementation, do not set the + // algorithm value as it would be inaccurate. + if (getClass() == SecureRandom.class) { +- this.algorithm = prng; ++ this.algorithm = prngAlgorithm; + } + } + +@@ -386,13 +402,6 @@ public class SecureRandom extends java.util.Random { + instance.provider, algorithm); + } + +- /** +- * Returns the SecureRandomSpi of this SecureRandom object. +- */ +- SecureRandomSpi getSecureRandomSpi() { +- return secureRandomSpi; +- } +- + /** + * Returns the provider of this SecureRandom object. + * +@@ -548,23 +557,6 @@ public class SecureRandom extends java.util.Random { + return retVal; + } + +- /** +- * Gets a default PRNG algorithm by looking through all registered +- * providers. Returns the first PRNG algorithm of the first provider that +- * has registered a SecureRandom implementation, or null if none of the +- * registered providers supplies a SecureRandom implementation. +- */ +- private static String getPrngAlgorithm() { +- for (Provider p : Providers.getProviderList().providers()) { +- for (Service s : p.getServices()) { +- if (s.getType().equals("SecureRandom")) { +- return s.getAlgorithm(); +- } +- } +- } +- return null; +- } +- + /* + * Lazily initialize since Pattern.compile() is heavy. + * Effective Java (2nd Edition), Item 71. +diff --git a/jdk/src/share/classes/javax/crypto/Cipher.java b/jdk/src/share/classes/javax/crypto/Cipher.java +index d3d09d7e2..93c177e77 100644 +--- a/jdk/src/share/classes/javax/crypto/Cipher.java ++++ b/jdk/src/share/classes/javax/crypto/Cipher.java +@@ -1186,7 +1186,7 @@ public class Cipher { + * by the underlying {@code CipherSpi}. + */ + public final void init(int opmode, Key key) throws InvalidKeyException { +- init(opmode, key, JceSecurity.RANDOM); ++ init(opmode, key, JCAUtil.getSecureRandom()); + } + + /** +@@ -1327,7 +1327,7 @@ public class Cipher { + public final void init(int opmode, Key key, AlgorithmParameterSpec params) + throws InvalidKeyException, InvalidAlgorithmParameterException + { +- init(opmode, key, params, JceSecurity.RANDOM); ++ init(opmode, key, params, JCAUtil.getSecureRandom()); + } + + /** +@@ -1470,7 +1470,7 @@ public class Cipher { + public final void init(int opmode, Key key, AlgorithmParameters params) + throws InvalidKeyException, InvalidAlgorithmParameterException + { +- init(opmode, key, params, JceSecurity.RANDOM); ++ init(opmode, key, params, JCAUtil.getSecureRandom()); + } + + /** +@@ -1618,7 +1618,7 @@ public class Cipher { + public final void init(int opmode, Certificate certificate) + throws InvalidKeyException + { +- init(opmode, certificate, JceSecurity.RANDOM); ++ init(opmode, certificate, JCAUtil.getSecureRandom()); + } + + /** +diff --git a/jdk/src/share/classes/javax/crypto/JceSecurity.java b/jdk/src/share/classes/javax/crypto/JceSecurity.java +index e7e3a99f5..1186dc351 100644 +--- a/jdk/src/share/classes/javax/crypto/JceSecurity.java ++++ b/jdk/src/share/classes/javax/crypto/JceSecurity.java +@@ -49,8 +49,6 @@ import sun.security.util.Debug; + + final class JceSecurity { + +- static final SecureRandom RANDOM = new SecureRandom(); +- + // The defaultPolicy and exemptPolicy will be set up + // in the static initializer. + private static CryptoPermissions defaultPolicy = null; +diff --git a/jdk/src/share/classes/javax/crypto/KeyAgreement.java b/jdk/src/share/classes/javax/crypto/KeyAgreement.java +index 513fc501e..4e16bcacb 100644 +--- a/jdk/src/share/classes/javax/crypto/KeyAgreement.java ++++ b/jdk/src/share/classes/javax/crypto/KeyAgreement.java +@@ -438,7 +438,7 @@ public class KeyAgreement { + * has an incompatible algorithm type. + */ + public final void init(Key key) throws InvalidKeyException { +- init(key, JceSecurity.RANDOM); ++ init(key, JCAUtil.getSecureRandom()); + } + + /** +@@ -506,7 +506,7 @@ public class KeyAgreement { + public final void init(Key key, AlgorithmParameterSpec params) + throws InvalidKeyException, InvalidAlgorithmParameterException + { +- init(key, params, JceSecurity.RANDOM); ++ init(key, params, JCAUtil.getSecureRandom()); + } + + /** +diff --git a/jdk/src/share/classes/javax/crypto/KeyGenerator.java b/jdk/src/share/classes/javax/crypto/KeyGenerator.java +index 2a26da5e5..71fa64715 100644 +--- a/jdk/src/share/classes/javax/crypto/KeyGenerator.java ++++ b/jdk/src/share/classes/javax/crypto/KeyGenerator.java +@@ -427,7 +427,7 @@ public class KeyGenerator { + public final void init(AlgorithmParameterSpec params) + throws InvalidAlgorithmParameterException + { +- init(params, JceSecurity.RANDOM); ++ init(params, JCAUtil.getSecureRandom()); + } + + /** +@@ -491,7 +491,7 @@ public class KeyGenerator { + * supported. + */ + public final void init(int keysize) { +- init(keysize, JceSecurity.RANDOM); ++ init(keysize, JCAUtil.getSecureRandom()); + } + + /** +diff --git a/jdk/src/share/classes/sun/security/provider/Sun.java b/jdk/src/share/classes/sun/security/provider/Sun.java +index 07ef2ff4a..75b411605 100644 +--- a/jdk/src/share/classes/sun/security/provider/Sun.java ++++ b/jdk/src/share/classes/sun/security/provider/Sun.java +@@ -28,7 +28,6 @@ package sun.security.provider; + import java.util.*; + import java.security.*; + +-import sun.security.action.PutAllAction; + + /** + * The SUN Security Provider. +@@ -49,17 +48,27 @@ public final class Sun extends Provider { + /* We are the SUN provider */ + super("SUN", 1.8d, INFO); + ++ Provider p = this; ++ Iterator serviceIter = new SunEntries(p).iterator(); ++ + // if there is no security manager installed, put directly into +- // the provider. Otherwise, create a temporary map and use a +- // doPrivileged() call at the end to transfer the contents ++ // the provider. + if (System.getSecurityManager() == null) { +- SunEntries.putEntries(this); ++ putEntries(serviceIter); + } else { +- // use LinkedHashMap to preserve the order of the PRNGs +- Map map = new LinkedHashMap<>(); +- SunEntries.putEntries(map); +- AccessController.doPrivileged(new PutAllAction(this, map)); ++ AccessController.doPrivileged(new PrivilegedAction() { ++ @Override ++ public Void run() { ++ putEntries(serviceIter); ++ return null; ++ } ++ }); + } + } + ++ void putEntries(Iterator i) { ++ while (i.hasNext()) { ++ putService(i.next()); ++ } ++ } + } +diff --git a/jdk/src/share/classes/sun/security/provider/SunEntries.java b/jdk/src/share/classes/sun/security/provider/SunEntries.java +index d85697841..fb61d40b0 100644 +--- a/jdk/src/share/classes/sun/security/provider/SunEntries.java ++++ b/jdk/src/share/classes/sun/security/provider/SunEntries.java +@@ -27,7 +27,7 @@ package sun.security.provider; + + import java.io.*; + import java.net.*; +-import java.util.Map; ++import java.util.*; + import java.security.*; + import sun.security.action.GetPropertyAction; + +@@ -77,255 +77,222 @@ import sun.security.action.GetPropertyAction; + * - JavaLoginConfig is the default file-based LoginModule Configuration type. + */ + +-final class SunEntries { ++public final class SunEntries { + +- private static final boolean useLegacyDSA = +- Boolean.parseBoolean(GetPropertyAction.privilegedGetProperty +- ("jdk.security.legacyDSAKeyPairGenerator")); ++ // the default algo used by SecureRandom class for new SecureRandom() calls ++ public static final String DEF_SECURE_RANDOM_ALGO; ++ ++ // create an aliases List from the specified aliases ++ public static List createAliases(String ... aliases) { ++ return Arrays.asList(aliases); ++ } + +- private SunEntries() { +- // empty ++ // create an aliases List from the specified oid followed by other aliases ++ public static List createAliasesWithOid(String ... oids) { ++ String[] result = Arrays.copyOf(oids, oids.length + 1); ++ result[result.length - 1] = "OID." + oids[0]; ++ return Arrays.asList(result); + } + +- static void putEntries(Map map) { ++ SunEntries(Provider p) { ++ services = new LinkedHashSet<>(50, 0.9f); ++ ++ // start populating content using the specified provider ++ ++ // common attribute map ++ HashMap attrs = new HashMap<>(3); + + /* +- * SecureRandom +- * +- * Register these first to speed up "new SecureRandom()", +- * which iterates through the list of algorithms ++ * SecureRandom engines + */ +- // register the native PRNG, if available +- // if user selected /dev/urandom, we put it before SHA1PRNG, +- // otherwise after it +- boolean nativeAvailable = NativePRNG.isAvailable(); +- boolean useNativePRNG = seedSource.equals(URL_DEV_URANDOM) || +- seedSource.equals(URL_DEV_RANDOM); +- +- if (nativeAvailable && useNativePRNG) { +- map.put("SecureRandom.NativePRNG", +- "sun.security.provider.NativePRNG"); +- } + +- map.put("SecureRandom.SHA1PRNG", +- "sun.security.provider.SecureRandom"); +- if (nativeAvailable && !useNativePRNG) { +- map.put("SecureRandom.NativePRNG", +- "sun.security.provider.NativePRNG"); ++ if (NativePRNG.isAvailable()) { ++ add(p, "SecureRandom", "NativePRNG", ++ "sun.security.provider.NativePRNG", ++ null, attrs); + } + + if (NativePRNG.Blocking.isAvailable()) { +- map.put("SecureRandom.NativePRNGBlocking", +- "sun.security.provider.NativePRNG$Blocking"); ++ add(p, "SecureRandom", "NativePRNGBlocking", ++ "sun.security.provider.NativePRNG$Blocking", null, attrs); + } + + if (NativePRNG.NonBlocking.isAvailable()) { +- map.put("SecureRandom.NativePRNGNonBlocking", +- "sun.security.provider.NativePRNG$NonBlocking"); ++ add(p, "SecureRandom", "NativePRNGNonBlocking", ++ "sun.security.provider.NativePRNG$NonBlocking", null, attrs); + } + ++ attrs.put("ImplementedIn", "Software"); ++ add(p, "SecureRandom", "SHA1PRNG", ++ "sun.security.provider.SecureRandom", null, attrs); ++ + /* + * Signature engines + */ +- map.put("Signature.SHA1withDSA", +- "sun.security.provider.DSA$SHA1withDSA"); +- map.put("Signature.NONEwithDSA", "sun.security.provider.DSA$RawDSA"); +- map.put("Alg.Alias.Signature.RawDSA", "NONEwithDSA"); +- map.put("Signature.SHA224withDSA", +- "sun.security.provider.DSA$SHA224withDSA"); +- map.put("Signature.SHA256withDSA", +- "sun.security.provider.DSA$SHA256withDSA"); +- ++ attrs.clear(); + String dsaKeyClasses = "java.security.interfaces.DSAPublicKey" + + "|java.security.interfaces.DSAPrivateKey"; +- map.put("Signature.SHA1withDSA SupportedKeyClasses", dsaKeyClasses); +- map.put("Signature.NONEwithDSA SupportedKeyClasses", dsaKeyClasses); +- map.put("Signature.SHA224withDSA SupportedKeyClasses", dsaKeyClasses); +- map.put("Signature.SHA256withDSA SupportedKeyClasses", dsaKeyClasses); +- +- map.put("Alg.Alias.Signature.DSA", "SHA1withDSA"); +- map.put("Alg.Alias.Signature.DSS", "SHA1withDSA"); +- map.put("Alg.Alias.Signature.SHA/DSA", "SHA1withDSA"); +- map.put("Alg.Alias.Signature.SHA-1/DSA", "SHA1withDSA"); +- map.put("Alg.Alias.Signature.SHA1/DSA", "SHA1withDSA"); +- map.put("Alg.Alias.Signature.SHAwithDSA", "SHA1withDSA"); +- map.put("Alg.Alias.Signature.DSAWithSHA1", "SHA1withDSA"); +- map.put("Alg.Alias.Signature.OID.1.2.840.10040.4.3", +- "SHA1withDSA"); +- map.put("Alg.Alias.Signature.1.2.840.10040.4.3", "SHA1withDSA"); +- map.put("Alg.Alias.Signature.1.3.14.3.2.13", "SHA1withDSA"); +- map.put("Alg.Alias.Signature.1.3.14.3.2.27", "SHA1withDSA"); +- map.put("Alg.Alias.Signature.OID.2.16.840.1.101.3.4.3.1", +- "SHA224withDSA"); +- map.put("Alg.Alias.Signature.2.16.840.1.101.3.4.3.1", "SHA224withDSA"); +- map.put("Alg.Alias.Signature.OID.2.16.840.1.101.3.4.3.2", +- "SHA256withDSA"); +- map.put("Alg.Alias.Signature.2.16.840.1.101.3.4.3.2", "SHA256withDSA"); ++ attrs.put("SupportedKeyClasses", dsaKeyClasses); ++ attrs.put("ImplementedIn", "Software"); ++ ++ attrs.put("KeySize", "1024"); // for NONE and SHA1 DSA signatures ++ ++ add(p, "Signature", "SHA1withDSA", ++ "sun.security.provider.DSA$SHA1withDSA", ++ createAliasesWithOid("1.2.840.10040.4.3", "DSA", "DSS", ++ "SHA/DSA", "SHA-1/DSA", "SHA1/DSA", "SHAwithDSA", ++ "DSAWithSHA1", "1.3.14.3.2.13", "1.3.14.3.2.27"), attrs); ++ add(p, "Signature", "NONEwithDSA", "sun.security.provider.DSA$RawDSA", ++ createAliases("RawDSA"), attrs); ++ ++ attrs.put("KeySize", "2048"); // for SHA224 and SHA256 DSA signatures ++ ++ add(p, "Signature", "SHA224withDSA", ++ "sun.security.provider.DSA$SHA224withDSA", ++ createAliasesWithOid("2.16.840.1.101.3.4.3.1"), attrs); ++ add(p, "Signature", "SHA256withDSA", ++ "sun.security.provider.DSA$SHA256withDSA", ++ createAliasesWithOid("2.16.840.1.101.3.4.3.2"), attrs); ++ ++ attrs.remove("KeySize"); + + /* + * Key Pair Generator engines + */ ++ attrs.clear(); ++ attrs.put("ImplementedIn", "Software"); ++ attrs.put("KeySize", "2048"); // for DSA KPG and APG only ++ ++ String dsaOid = "1.2.840.10040.4.1"; ++ List dsaAliases = createAliasesWithOid(dsaOid, "1.3.14.3.2.12"); + String dsaKPGImplClass = "sun.security.provider.DSAKeyPairGenerator$"; + dsaKPGImplClass += (useLegacyDSA? "Legacy" : "Current"); +- map.put("KeyPairGenerator.DSA", dsaKPGImplClass); +- map.put("Alg.Alias.KeyPairGenerator.OID.1.2.840.10040.4.1", "DSA"); +- map.put("Alg.Alias.KeyPairGenerator.1.2.840.10040.4.1", "DSA"); +- map.put("Alg.Alias.KeyPairGenerator.1.3.14.3.2.12", "DSA"); +- +- /* +- * Digest engines +- */ +- map.put("MessageDigest.MD2", "sun.security.provider.MD2"); +- map.put("MessageDigest.MD5", "sun.security.provider.MD5"); +- map.put("MessageDigest.SHA", "sun.security.provider.SHA"); +- +- map.put("Alg.Alias.MessageDigest.SHA-1", "SHA"); +- map.put("Alg.Alias.MessageDigest.SHA1", "SHA"); +- map.put("Alg.Alias.MessageDigest.1.3.14.3.2.26", "SHA"); +- map.put("Alg.Alias.MessageDigest.OID.1.3.14.3.2.26", "SHA"); +- +- map.put("MessageDigest.SHA-224", "sun.security.provider.SHA2$SHA224"); +- map.put("Alg.Alias.MessageDigest.2.16.840.1.101.3.4.2.4", "SHA-224"); +- map.put("Alg.Alias.MessageDigest.OID.2.16.840.1.101.3.4.2.4", +- "SHA-224"); +- +- map.put("MessageDigest.SHA-256", "sun.security.provider.SHA2$SHA256"); +- map.put("Alg.Alias.MessageDigest.2.16.840.1.101.3.4.2.1", "SHA-256"); +- map.put("Alg.Alias.MessageDigest.OID.2.16.840.1.101.3.4.2.1", +- "SHA-256"); +- map.put("MessageDigest.SHA-384", "sun.security.provider.SHA5$SHA384"); +- map.put("Alg.Alias.MessageDigest.2.16.840.1.101.3.4.2.2", "SHA-384"); +- map.put("Alg.Alias.MessageDigest.OID.2.16.840.1.101.3.4.2.2", +- "SHA-384"); +- map.put("MessageDigest.SHA-512", "sun.security.provider.SHA5$SHA512"); +- map.put("Alg.Alias.MessageDigest.2.16.840.1.101.3.4.2.3", "SHA-512"); +- map.put("Alg.Alias.MessageDigest.OID.2.16.840.1.101.3.4.2.3", +- "SHA-512"); +- map.put("MessageDigest.SHA-512/224", "sun.security.provider.SHA5$SHA512_224"); +- map.put("Alg.Alias.MessageDigest.2.16.840.1.101.3.4.2.5", "SHA-512/224"); +- map.put("Alg.Alias.MessageDigest.OID.2.16.840.1.101.3.4.2.5", +- "SHA-512/224"); +- map.put("MessageDigest.SHA-512/256", "sun.security.provider.SHA5$SHA512_256"); +- map.put("Alg.Alias.MessageDigest.2.16.840.1.101.3.4.2.6", "SHA-512/256"); +- map.put("Alg.Alias.MessageDigest.OID.2.16.840.1.101.3.4.2.6", +- "SHA-512/256"); ++ add(p, "KeyPairGenerator", "DSA", dsaKPGImplClass, dsaAliases, attrs); + + /* + * Algorithm Parameter Generator engines + */ +- map.put("AlgorithmParameterGenerator.DSA", +- "sun.security.provider.DSAParameterGenerator"); ++ add(p, "AlgorithmParameterGenerator", "DSA", ++ "sun.security.provider.DSAParameterGenerator", dsaAliases, ++ attrs); ++ attrs.remove("KeySize"); + + /* + * Algorithm Parameter engines + */ +- map.put("AlgorithmParameters.DSA", +- "sun.security.provider.DSAParameters"); +- map.put("Alg.Alias.AlgorithmParameters.OID.1.2.840.10040.4.1", "DSA"); +- map.put("Alg.Alias.AlgorithmParameters.1.2.840.10040.4.1", "DSA"); +- map.put("Alg.Alias.AlgorithmParameters.1.3.14.3.2.12", "DSA"); ++ add(p, "AlgorithmParameters", "DSA", ++ "sun.security.provider.DSAParameters", dsaAliases, attrs); + + /* + * Key factories + */ +- map.put("KeyFactory.DSA", "sun.security.provider.DSAKeyFactory"); +- map.put("Alg.Alias.KeyFactory.OID.1.2.840.10040.4.1", "DSA"); +- map.put("Alg.Alias.KeyFactory.1.2.840.10040.4.1", "DSA"); +- map.put("Alg.Alias.KeyFactory.1.3.14.3.2.12", "DSA"); ++ add(p, "KeyFactory", "DSA", "sun.security.provider.DSAKeyFactory", ++ dsaAliases, attrs); + + /* +- * Certificates ++ * Digest engines + */ +- map.put("CertificateFactory.X.509", +- "sun.security.provider.X509Factory"); +- map.put("Alg.Alias.CertificateFactory.X509", "X.509"); ++ add(p, "MessageDigest", "MD2", "sun.security.provider.MD2", null, attrs); ++ add(p, "MessageDigest", "MD5", "sun.security.provider.MD5", null, attrs); ++ add(p, "MessageDigest", "SHA", "sun.security.provider.SHA", ++ createAliasesWithOid("1.3.14.3.2.26", "SHA-1", "SHA1"), attrs); ++ ++ String sha2BaseOid = "2.16.840.1.101.3.4.2"; ++ add(p, "MessageDigest", "SHA-224", "sun.security.provider.SHA2$SHA224", ++ createAliasesWithOid(sha2BaseOid + ".4"), attrs); ++ add(p, "MessageDigest", "SHA-256", "sun.security.provider.SHA2$SHA256", ++ createAliasesWithOid(sha2BaseOid + ".1"), attrs); ++ add(p, "MessageDigest", "SHA-384", "sun.security.provider.SHA5$SHA384", ++ createAliasesWithOid(sha2BaseOid + ".2"), attrs); ++ add(p, "MessageDigest", "SHA-512", "sun.security.provider.SHA5$SHA512", ++ createAliasesWithOid(sha2BaseOid + ".3"), attrs); ++ add(p, "MessageDigest", "SHA-512/224", ++ "sun.security.provider.SHA5$SHA512_224", ++ createAliasesWithOid(sha2BaseOid + ".5"), attrs); ++ add(p, "MessageDigest", "SHA-512/256", ++ "sun.security.provider.SHA5$SHA512_256", ++ createAliasesWithOid(sha2BaseOid + ".6"), attrs); + +- /* +- * KeyStore +- */ +- map.put("KeyStore.JKS", +- "sun.security.provider.JavaKeyStore$DualFormatJKS"); +- map.put("KeyStore.CaseExactJKS", +- "sun.security.provider.JavaKeyStore$CaseExactJKS"); +- map.put("KeyStore.DKS", "sun.security.provider.DomainKeyStore$DKS"); + + /* +- * Policy ++ * Certificates + */ +- map.put("Policy.JavaPolicy", "sun.security.provider.PolicySpiFile"); ++ add(p, "CertificateFactory", "X.509", ++ "sun.security.provider.X509Factory", ++ createAliases("X509"), attrs); + + /* +- * Configuration ++ * KeyStore + */ +- map.put("Configuration.JavaLoginConfig", +- "sun.security.provider.ConfigFile$Spi"); ++ add(p, "KeyStore", "JKS", ++ "sun.security.provider.JavaKeyStore$DualFormatJKS", ++ null, attrs); ++ add(p, "KeyStore", "CaseExactJKS", ++ "sun.security.provider.JavaKeyStore$CaseExactJKS", ++ null, attrs); ++ add(p, "KeyStore", "DKS", "sun.security.provider.DomainKeyStore$DKS", ++ null, attrs); + + /* +- * CertPathBuilder ++ * CertStores + */ +- map.put("CertPathBuilder.PKIX", +- "sun.security.provider.certpath.SunCertPathBuilder"); +- map.put("CertPathBuilder.PKIX ValidationAlgorithm", +- "RFC5280"); ++ attrs.put("LDAPSchema", "RFC2587"); ++ add(p, "CertStore", "LDAP", ++ "sun.security.provider.certpath.ldap.LDAPCertStore", null, attrs); ++ attrs.remove("LDAPSchema"); ++ add(p, "CertStore", "Collection", ++ "sun.security.provider.certpath.CollectionCertStore", ++ null, attrs); ++ add(p, "CertStore", "com.sun.security.IndexedCollection", ++ "sun.security.provider.certpath.IndexedCollectionCertStore", ++ null, attrs); + + /* +- * CertPathValidator ++ * Policy + */ +- map.put("CertPathValidator.PKIX", +- "sun.security.provider.certpath.PKIXCertPathValidator"); +- map.put("CertPathValidator.PKIX ValidationAlgorithm", +- "RFC5280"); ++ add(p, "Policy", "JavaPolicy", "sun.security.provider.PolicySpiFile", ++ null, null); + + /* +- * CertStores ++ * Configuration + */ +- map.put("CertStore.LDAP", +- "sun.security.provider.certpath.ldap.LDAPCertStore"); +- map.put("CertStore.LDAP LDAPSchema", "RFC2587"); +- map.put("CertStore.Collection", +- "sun.security.provider.certpath.CollectionCertStore"); +- map.put("CertStore.com.sun.security.IndexedCollection", +- "sun.security.provider.certpath.IndexedCollectionCertStore"); ++ add(p, "Configuration", "JavaLoginConfig", ++ "sun.security.provider.ConfigFile$Spi", null, null); + + /* +- * KeySize ++ * CertPathBuilder and CertPathValidator + */ +- map.put("Signature.NONEwithDSA KeySize", "1024"); +- map.put("Signature.SHA1withDSA KeySize", "1024"); +- map.put("Signature.SHA224withDSA KeySize", "2048"); +- map.put("Signature.SHA256withDSA KeySize", "2048"); +- +- map.put("KeyPairGenerator.DSA KeySize", "2048"); +- map.put("AlgorithmParameterGenerator.DSA KeySize", "2048"); ++ attrs.clear(); ++ attrs.put("ValidationAlgorithm", "RFC5280"); ++ attrs.put("ImplementedIn", "Software"); ++ add(p, "CertPathBuilder", "PKIX", ++ "sun.security.provider.certpath.SunCertPathBuilder", ++ null, attrs); ++ add(p, "CertPathValidator", "PKIX", ++ "sun.security.provider.certpath.PKIXCertPathValidator", ++ null, attrs); ++ } + +- /* +- * Implementation type: software or hardware +- */ +- map.put("Signature.SHA1withDSA ImplementedIn", "Software"); +- map.put("KeyPairGenerator.DSA ImplementedIn", "Software"); +- map.put("MessageDigest.MD5 ImplementedIn", "Software"); +- map.put("MessageDigest.SHA ImplementedIn", "Software"); +- map.put("AlgorithmParameterGenerator.DSA ImplementedIn", +- "Software"); +- map.put("AlgorithmParameters.DSA ImplementedIn", "Software"); +- map.put("KeyFactory.DSA ImplementedIn", "Software"); +- map.put("SecureRandom.SHA1PRNG ImplementedIn", "Software"); +- map.put("CertificateFactory.X.509 ImplementedIn", "Software"); +- map.put("KeyStore.JKS ImplementedIn", "Software"); +- map.put("CertPathValidator.PKIX ImplementedIn", "Software"); +- map.put("CertPathBuilder.PKIX ImplementedIn", "Software"); +- map.put("CertStore.LDAP ImplementedIn", "Software"); +- map.put("CertStore.Collection ImplementedIn", "Software"); +- map.put("CertStore.com.sun.security.IndexedCollection ImplementedIn", +- "Software"); ++ Iterator iterator() { ++ return services.iterator(); ++ } + ++ private void add(Provider p, String type, String algo, String cn, ++ List aliases, HashMap attrs) { ++ services.add(new Provider.Service(p, type, algo, cn, aliases, attrs)); + } + ++ private LinkedHashSet services; ++ + // name of the *System* property, takes precedence over PROP_RNDSOURCE + private final static String PROP_EGD = "java.security.egd"; + // name of the *Security* property + private final static String PROP_RNDSOURCE = "securerandom.source"; + ++ private static final boolean useLegacyDSA = ++ Boolean.parseBoolean(GetPropertyAction.privilegedGetProperty ++ ("jdk.security.legacyDSAKeyPairGenerator")); ++ + final static String URL_DEV_RANDOM = "file:/dev/random"; + final static String URL_DEV_URANDOM = "file:/dev/urandom"; + +@@ -348,6 +315,12 @@ final class SunEntries { + return egdSource; + } + }); ++ ++ DEF_SECURE_RANDOM_ALGO = (NativePRNG.isAvailable() && ++ (seedSource.equals(URL_DEV_URANDOM) || ++ seedSource.equals(URL_DEV_RANDOM)) ? ++ "NativePRNG" : "SHA1PRNG"); ++ + } + + static String getSeedSource() { +diff --git a/jdk/src/share/classes/sun/security/provider/VerificationProvider.java b/jdk/src/share/classes/sun/security/provider/VerificationProvider.java +index 296b03437..d76d81999 100644 +--- a/jdk/src/share/classes/sun/security/provider/VerificationProvider.java ++++ b/jdk/src/share/classes/sun/security/provider/VerificationProvider.java +@@ -28,8 +28,6 @@ package sun.security.provider; + import java.util.*; + import java.security.*; + +-import sun.security.action.PutAllAction; +- + import sun.security.rsa.SunRsaSignEntries; + + /** +@@ -68,19 +66,29 @@ public final class VerificationProvider extends Provider { + return; + } + ++ Provider p = this; ++ Iterator sunIter = new SunEntries(p).iterator(); ++ Iterator rsaIter = new SunRsaSignEntries(p).iterator(); + // if there is no security manager installed, put directly into +- // the provider. Otherwise, create a temporary map and use a +- // doPrivileged() call at the end to transfer the contents ++ // the provider. + if (System.getSecurityManager() == null) { +- SunEntries.putEntries(this); +- SunRsaSignEntries.putEntries(this); ++ putEntries(sunIter); ++ putEntries(rsaIter); + } else { + // use LinkedHashMap to preserve the order of the PRNGs +- Map map = new LinkedHashMap<>(); +- SunEntries.putEntries(map); +- SunRsaSignEntries.putEntries(map); +- AccessController.doPrivileged(new PutAllAction(this, map)); ++ AccessController.doPrivileged(new PrivilegedAction() { ++ public Void run() { ++ putEntries(sunIter); ++ putEntries(rsaIter); ++ return null; ++ } ++ }); + } + } + ++ void putEntries(Iterator i) { ++ while (i.hasNext()) { ++ putService(i.next()); ++ } ++ } + } +diff --git a/jdk/src/share/classes/sun/security/rsa/SunRsaSign.java b/jdk/src/share/classes/sun/security/rsa/SunRsaSign.java +index 65ae02a08..3c3d0f693 100644 +--- a/jdk/src/share/classes/sun/security/rsa/SunRsaSign.java ++++ b/jdk/src/share/classes/sun/security/rsa/SunRsaSign.java +@@ -29,7 +29,6 @@ import java.util.*; + + import java.security.*; + +-import sun.security.action.PutAllAction; + + /** + * Provider class for the RSA signature provider. Supports RSA keyfactory, +@@ -45,17 +44,25 @@ public final class SunRsaSign extends Provider { + public SunRsaSign() { + super("SunRsaSign", 1.8d, "Sun RSA signature provider"); + +- // if there is no security manager installed, put directly into +- // the provider. Otherwise, create a temporary map and use a +- // doPrivileged() call at the end to transfer the contents ++ Provider p = this; ++ Iterator serviceIter = new SunRsaSignEntries(p).iterator(); ++ + if (System.getSecurityManager() == null) { +- SunRsaSignEntries.putEntries(this); ++ putEntries(serviceIter); + } else { +- // use LinkedHashMap to preserve the order of the PRNGs +- Map map = new HashMap<>(); +- SunRsaSignEntries.putEntries(map); +- AccessController.doPrivileged(new PutAllAction(this, map)); ++ AccessController.doPrivileged(new PrivilegedAction() { ++ @Override ++ public Void run() { ++ putEntries(serviceIter); ++ return null; ++ } ++ }); + } + } + ++ void putEntries(Iterator i) { ++ while (i.hasNext()) { ++ putService(i.next()); ++ } ++ } + } +diff --git a/jdk/src/share/classes/sun/security/rsa/SunRsaSignEntries.java b/jdk/src/share/classes/sun/security/rsa/SunRsaSignEntries.java +index 6af5fdf85..f8de9eccc 100644 +--- a/jdk/src/share/classes/sun/security/rsa/SunRsaSignEntries.java ++++ b/jdk/src/share/classes/sun/security/rsa/SunRsaSignEntries.java +@@ -25,7 +25,9 @@ + + package sun.security.rsa; + +-import java.util.Map; ++import java.util.*; ++import java.security.Provider; ++import static sun.security.provider.SunEntries.createAliasesWithOid; + + /** + * Defines the entries of the SunRsaSign provider. +@@ -34,102 +36,81 @@ import java.util.Map; + */ + public final class SunRsaSignEntries { + +- private SunRsaSignEntries() { +- // empty ++ private void add(Provider p, String type, String algo, String cn, ++ List aliases, HashMap attrs) { ++ services.add(new Provider.Service(p, type, algo, cn, aliases, attrs)); + } + +- public static void putEntries(Map map) { +- +- // main algorithms +- map.put("KeyFactory.RSA", +- "sun.security.rsa.RSAKeyFactory$Legacy"); +- map.put("KeyPairGenerator.RSA", +- "sun.security.rsa.RSAKeyPairGenerator$Legacy"); +- map.put("Signature.MD2withRSA", +- "sun.security.rsa.RSASignature$MD2withRSA"); +- map.put("Signature.MD5withRSA", +- "sun.security.rsa.RSASignature$MD5withRSA"); +- map.put("Signature.SHA1withRSA", +- "sun.security.rsa.RSASignature$SHA1withRSA"); +- map.put("Signature.SHA224withRSA", +- "sun.security.rsa.RSASignature$SHA224withRSA"); +- map.put("Signature.SHA256withRSA", +- "sun.security.rsa.RSASignature$SHA256withRSA"); +- map.put("Signature.SHA384withRSA", +- "sun.security.rsa.RSASignature$SHA384withRSA"); +- map.put("Signature.SHA512withRSA", +- "sun.security.rsa.RSASignature$SHA512withRSA"); +- map.put("Signature.SHA512/224withRSA", +- "sun.security.rsa.RSASignature$SHA512_224withRSA"); +- map.put("Signature.SHA512/256withRSA", +- "sun.security.rsa.RSASignature$SHA512_256withRSA"); +- +- map.put("KeyFactory.RSASSA-PSS", +- "sun.security.rsa.RSAKeyFactory$PSS"); +- map.put("KeyPairGenerator.RSASSA-PSS", +- "sun.security.rsa.RSAKeyPairGenerator$PSS"); +- map.put("Signature.RSASSA-PSS", +- "sun.security.rsa.RSAPSSSignature"); +- map.put("AlgorithmParameters.RSASSA-PSS", +- "sun.security.rsa.PSSParameters"); +- +- // attributes for supported key classes +- String rsaKeyClasses = "java.security.interfaces.RSAPublicKey" + +- "|java.security.interfaces.RSAPrivateKey"; +- map.put("Signature.MD2withRSA SupportedKeyClasses", rsaKeyClasses); +- map.put("Signature.MD5withRSA SupportedKeyClasses", rsaKeyClasses); +- map.put("Signature.SHA1withRSA SupportedKeyClasses", rsaKeyClasses); +- map.put("Signature.SHA224withRSA SupportedKeyClasses", rsaKeyClasses); +- map.put("Signature.SHA256withRSA SupportedKeyClasses", rsaKeyClasses); +- map.put("Signature.SHA384withRSA SupportedKeyClasses", rsaKeyClasses); +- map.put("Signature.SHA512withRSA SupportedKeyClasses", rsaKeyClasses); +- map.put("Signature.SHA512/224withRSA SupportedKeyClasses", rsaKeyClasses); +- map.put("Signature.SHA512/256withRSA SupportedKeyClasses", rsaKeyClasses); +- map.put("Signature.RSASSA-PSS SupportedKeyClasses", rsaKeyClasses); +- +- // aliases +- map.put("Alg.Alias.KeyFactory.1.2.840.113549.1.1", "RSA"); +- map.put("Alg.Alias.KeyFactory.OID.1.2.840.113549.1.1", "RSA"); +- +- map.put("Alg.Alias.KeyPairGenerator.1.2.840.113549.1.1", "RSA"); +- map.put("Alg.Alias.KeyPairGenerator.OID.1.2.840.113549.1.1", "RSA"); +- +- map.put("Alg.Alias.Signature.1.2.840.113549.1.1.2", "MD2withRSA"); +- map.put("Alg.Alias.Signature.OID.1.2.840.113549.1.1.2", "MD2withRSA"); +- +- map.put("Alg.Alias.Signature.1.2.840.113549.1.1.4", "MD5withRSA"); +- map.put("Alg.Alias.Signature.OID.1.2.840.113549.1.1.4", "MD5withRSA"); +- +- map.put("Alg.Alias.Signature.1.2.840.113549.1.1.5", "SHA1withRSA"); +- map.put("Alg.Alias.Signature.OID.1.2.840.113549.1.1.5", "SHA1withRSA"); +- map.put("Alg.Alias.Signature.1.3.14.3.2.29", "SHA1withRSA"); +- +- map.put("Alg.Alias.Signature.1.2.840.113549.1.1.14", "SHA224withRSA"); +- map.put("Alg.Alias.Signature.OID.1.2.840.113549.1.1.14", "SHA224withRSA"); +- +- map.put("Alg.Alias.Signature.1.2.840.113549.1.1.11", "SHA256withRSA"); +- map.put("Alg.Alias.Signature.OID.1.2.840.113549.1.1.11", "SHA256withRSA"); +- +- map.put("Alg.Alias.Signature.1.2.840.113549.1.1.12", "SHA384withRSA"); +- map.put("Alg.Alias.Signature.OID.1.2.840.113549.1.1.12", "SHA384withRSA"); +- +- map.put("Alg.Alias.Signature.1.2.840.113549.1.1.13", "SHA512withRSA"); +- map.put("Alg.Alias.Signature.OID.1.2.840.113549.1.1.13", "SHA512withRSA"); +- map.put("Alg.Alias.Signature.1.2.840.113549.1.1.15", "SHA512/224withRSA"); +- map.put("Alg.Alias.Signature.OID.1.2.840.113549.1.1.15", "SHA512/224withRSA"); +- map.put("Alg.Alias.Signature.1.2.840.113549.1.1.16", "SHA512/256withRSA"); +- map.put("Alg.Alias.Signature.OID.1.2.840.113549.1.1.16", "SHA512/256withRSA"); +- +- map.put("Alg.Alias.KeyFactory.1.2.840.113549.1.1.10", "RSASSA-PSS"); +- map.put("Alg.Alias.KeyFactory.OID.1.2.840.113549.1.1.10", "RSASSA-PSS"); +- +- map.put("Alg.Alias.KeyPairGenerator.1.2.840.113549.1.1.10", "RSASSA-PSS"); +- map.put("Alg.Alias.KeyPairGenerator.OID.1.2.840.113549.1.1.10", "RSASSA-PSS"); +- +- map.put("Alg.Alias.Signature.1.2.840.113549.1.1.10", "RSASSA-PSS"); +- map.put("Alg.Alias.Signature.OID.1.2.840.113549.1.1.10", "RSASSA-PSS"); ++ // extend LinkedHashSet for consistency with SunEntries ++ // used by sun.security.provider.VerificationProvider ++ public SunRsaSignEntries(Provider p) { ++ services = new LinkedHashSet<>(20, 0.9f); ++ ++ // start populating content using the specified provider ++ ++ // common oids ++ String rsaOid = "1.2.840.113549.1.1"; ++ List rsaAliases = createAliasesWithOid(rsaOid); ++ List rsapssAliases = createAliasesWithOid(rsaOid + ".10"); ++ String sha1withRSAOid2 = "1.3.14.3.2.29"; ++ ++ // common attribute map ++ HashMap attrs = new HashMap<>(3); ++ attrs.put("SupportedKeyClasses", ++ "java.security.interfaces.RSAPublicKey" + ++ "|java.security.interfaces.RSAPrivateKey"); ++ ++ add(p, "KeyFactory", "RSA", ++ "sun.security.rsa.RSAKeyFactory$Legacy", ++ rsaAliases, null); ++ add(p, "KeyPairGenerator", "RSA", ++ "sun.security.rsa.RSAKeyPairGenerator$Legacy", ++ rsaAliases, null); ++ add(p, "Signature", "MD2withRSA", ++ "sun.security.rsa.RSASignature$MD2withRSA", ++ createAliasesWithOid(rsaOid + ".2"), attrs); ++ add(p, "Signature", "MD5withRSA", ++ "sun.security.rsa.RSASignature$MD5withRSA", ++ createAliasesWithOid(rsaOid + ".4"), attrs); ++ add(p, "Signature", "SHA1withRSA", ++ "sun.security.rsa.RSASignature$SHA1withRSA", ++ createAliasesWithOid(rsaOid + ".5", sha1withRSAOid2), attrs); ++ add(p, "Signature", "SHA224withRSA", ++ "sun.security.rsa.RSASignature$SHA224withRSA", ++ createAliasesWithOid(rsaOid + ".14"), attrs); ++ add(p, "Signature", "SHA256withRSA", ++ "sun.security.rsa.RSASignature$SHA256withRSA", ++ createAliasesWithOid(rsaOid + ".11"), attrs); ++ add(p, "Signature", "SHA384withRSA", ++ "sun.security.rsa.RSASignature$SHA384withRSA", ++ createAliasesWithOid(rsaOid + ".12"), attrs); ++ add(p, "Signature", "SHA512withRSA", ++ "sun.security.rsa.RSASignature$SHA512withRSA", ++ createAliasesWithOid(rsaOid + ".13"), attrs); ++ add(p, "Signature", "SHA512/224withRSA", ++ "sun.security.rsa.RSASignature$SHA512_224withRSA", ++ createAliasesWithOid(rsaOid + ".15"), attrs); ++ add(p, "Signature", "SHA512/256withRSA", ++ "sun.security.rsa.RSASignature$SHA512_256withRSA", ++ createAliasesWithOid(rsaOid + ".16"), attrs); ++ ++ add(p, "KeyFactory", "RSASSA-PSS", ++ "sun.security.rsa.RSAKeyFactory$PSS", ++ rsapssAliases, null); ++ add(p, "KeyPairGenerator", "RSASSA-PSS", ++ "sun.security.rsa.RSAKeyPairGenerator$PSS", ++ rsapssAliases, null); ++ add(p, "Signature", "RSASSA-PSS", ++ "sun.security.rsa.RSAPSSSignature", ++ rsapssAliases, attrs); ++ add(p, "AlgorithmParameters", "RSASSA-PSS", ++ "sun.security.rsa.PSSParameters", ++ rsapssAliases, null); ++ } + +- map.put("Alg.Alias.AlgorithmParameters.1.2.840.113549.1.1.10", "RSASSA-PSS"); +- map.put("Alg.Alias.AlgorithmParameters.OID.1.2.840.113549.1.1.10", "RSASSA-PSS"); ++ public Iterator iterator() { ++ return services.iterator(); + } ++ ++ private LinkedHashSet services; + } +diff --git a/jdk/src/share/classes/sun/security/ssl/SunJSSE.java b/jdk/src/share/classes/sun/security/ssl/SunJSSE.java +index 2845dc379..58b791c99 100644 +--- a/jdk/src/share/classes/sun/security/ssl/SunJSSE.java ++++ b/jdk/src/share/classes/sun/security/ssl/SunJSSE.java +@@ -26,9 +26,12 @@ + + package sun.security.ssl; + +-import static sun.security.util.SecurityConstants.PROVIDER_VER; +- + import java.security.*; ++import java.util.*; ++ ++import static sun.security.provider.SunEntries.createAliasesWithOid; ++import static sun.security.util.SecurityConstants.PROVIDER_VER; ++import static sun.security.provider.SunEntries.createAliases; + + /** + * The JSSE provider. +@@ -159,79 +162,78 @@ public abstract class SunJSSE extends java.security.Provider { + }); + } + ++ private void ps(String type, String algo, String cn, ++ List aliases, HashMap attrs) { ++ putService(new Provider.Service(this, type, algo, cn, aliases, attrs)); ++ } ++ ++ + private void doRegister(boolean isfips) { + if (isfips == false) { +- put("KeyFactory.RSA", +- "sun.security.rsa.RSAKeyFactory$Legacy"); +- put("Alg.Alias.KeyFactory.1.2.840.113549.1.1", "RSA"); +- put("Alg.Alias.KeyFactory.OID.1.2.840.113549.1.1", "RSA"); +- +- put("KeyPairGenerator.RSA", +- "sun.security.rsa.RSAKeyPairGenerator$Legacy"); +- put("Alg.Alias.KeyPairGenerator.1.2.840.113549.1.1", "RSA"); +- put("Alg.Alias.KeyPairGenerator.OID.1.2.840.113549.1.1", "RSA"); +- +- put("Signature.MD2withRSA", +- "sun.security.rsa.RSASignature$MD2withRSA"); +- put("Alg.Alias.Signature.1.2.840.113549.1.1.2", "MD2withRSA"); +- put("Alg.Alias.Signature.OID.1.2.840.113549.1.1.2", +- "MD2withRSA"); +- +- put("Signature.MD5withRSA", +- "sun.security.rsa.RSASignature$MD5withRSA"); +- put("Alg.Alias.Signature.1.2.840.113549.1.1.4", "MD5withRSA"); +- put("Alg.Alias.Signature.OID.1.2.840.113549.1.1.4", +- "MD5withRSA"); +- +- put("Signature.SHA1withRSA", +- "sun.security.rsa.RSASignature$SHA1withRSA"); +- put("Alg.Alias.Signature.1.2.840.113549.1.1.5", "SHA1withRSA"); +- put("Alg.Alias.Signature.OID.1.2.840.113549.1.1.5", +- "SHA1withRSA"); +- put("Alg.Alias.Signature.1.3.14.3.2.29", "SHA1withRSA"); +- put("Alg.Alias.Signature.OID.1.3.14.3.2.29", "SHA1withRSA"); ++ // common oids ++ String rsaOid = "1.2.840.113549.1.1"; ++ List rsaAliases = createAliasesWithOid(rsaOid); ++ String sha1withRSAOid2 = "1.3.14.3.2.29"; ++ ++ // common attribute map ++ HashMap attrs = new HashMap<>(3); ++ attrs.put("SupportedKeyClasses", ++ "java.security.interfaces.RSAPublicKey" + ++ "|java.security.interfaces.RSAPrivateKey"); ++ ++ ps("KeyFactory", "RSA", ++ "sun.security.rsa.RSAKeyFactory$Legacy", ++ rsaAliases, null); ++ ps("KeyPairGenerator", "RSA", ++ "sun.security.rsa.RSAKeyPairGenerator$Legacy", ++ rsaAliases, null); ++ ps("Signature", "MD2withRSA", ++ "sun.security.rsa.RSASignature$MD2withRSA", ++ createAliasesWithOid(rsaOid + ".2"), attrs); ++ ps("Signature", "MD5withRSA", ++ "sun.security.rsa.RSASignature$MD5withRSA", ++ createAliasesWithOid(rsaOid + ".4"), attrs); ++ ps("Signature", "SHA1withRSA", ++ "sun.security.rsa.RSASignature$SHA1withRSA", ++ createAliasesWithOid(rsaOid + ".5", sha1withRSAOid2, "OID." + sha1withRSAOid2), attrs); + + } +- put("Signature.MD5andSHA1withRSA", +- "sun.security.ssl.RSASignature"); +- +- put("KeyManagerFactory.SunX509", +- "sun.security.ssl.KeyManagerFactoryImpl$SunX509"); +- put("KeyManagerFactory.NewSunX509", +- "sun.security.ssl.KeyManagerFactoryImpl$X509"); +- put("Alg.Alias.KeyManagerFactory.PKIX", "NewSunX509"); +- +- put("TrustManagerFactory.SunX509", +- "sun.security.ssl.TrustManagerFactoryImpl$SimpleFactory"); +- put("TrustManagerFactory.PKIX", +- "sun.security.ssl.TrustManagerFactoryImpl$PKIXFactory"); +- put("Alg.Alias.TrustManagerFactory.SunPKIX", "PKIX"); +- put("Alg.Alias.TrustManagerFactory.X509", "PKIX"); +- put("Alg.Alias.TrustManagerFactory.X.509", "PKIX"); +- +- put("SSLContext.TLSv1", +- "sun.security.ssl.SSLContextImpl$TLS10Context"); +- put("SSLContext.TLSv1.1", +- "sun.security.ssl.SSLContextImpl$TLS11Context"); +- put("SSLContext.TLSv1.2", +- "sun.security.ssl.SSLContextImpl$TLS12Context"); +- put("SSLContext.TLSv1.3", +- "sun.security.ssl.SSLContextImpl$TLS13Context"); +- put("SSLContext.TLS", +- "sun.security.ssl.SSLContextImpl$TLSContext"); +- if (isfips == false) { +- put("Alg.Alias.SSLContext.SSL", "TLS"); +- put("Alg.Alias.SSLContext.SSLv3", "TLSv1"); +- } +- +- put("SSLContext.Default", +- "sun.security.ssl.SSLContextImpl$DefaultSSLContext"); ++ ps("Signature", "MD5andSHA1withRSA", ++ "sun.security.ssl.RSASignature", null, null); ++ ++ ps("KeyManagerFactory", "SunX509", ++ "sun.security.ssl.KeyManagerFactoryImpl$SunX509", null, null); ++ ps("KeyManagerFactory", "NewSunX509", ++ "sun.security.ssl.KeyManagerFactoryImpl$X509", ++ createAliases("PKIX"), null); ++ ++ ps("TrustManagerFactory", "SunX509", ++ "sun.security.ssl.TrustManagerFactoryImpl$SimpleFactory", null, null); ++ ps("TrustManagerFactory", "PKIX", ++ "sun.security.ssl.TrustManagerFactoryImpl$PKIXFactory", ++ createAliases("SunPKIX", "X509", "X.509"), null); ++ ++ ps("SSLContext", "TLSv1", ++ "sun.security.ssl.SSLContextImpl$TLS10Context", ++ (isfips? null : createAliases("SSLv3")), null); ++ ps("SSLContext", "TLSv1.1", ++ "sun.security.ssl.SSLContextImpl$TLS11Context", null, null); ++ ps("SSLContext", "TLSv1.2", ++ "sun.security.ssl.SSLContextImpl$TLS12Context", null, null); ++ ps("SSLContext", "TLSv1.3", ++ "sun.security.ssl.SSLContextImpl$TLS13Context", null, null); ++ ps("SSLContext", "TLS", ++ "sun.security.ssl.SSLContextImpl$TLSContext", ++ (isfips? null : createAliases("SSL")), null); ++ ++ ps("SSLContext", "Default", ++ "sun.security.ssl.SSLContextImpl$DefaultSSLContext", null, null); + + /* + * KeyStore + */ +- put("KeyStore.PKCS12", +- "sun.security.pkcs12.PKCS12KeyStore"); ++ ps("KeyStore", "PKCS12", ++ "sun.security.pkcs12.PKCS12KeyStore", null, null); + } + + private void subclassCheck() { +diff --git a/jdk/test/java/security/Provider/BaseProviderValidator.java b/jdk/test/java/security/Provider/BaseProviderValidator.java +new file mode 100644 +index 000000000..510529baa +--- /dev/null ++++ b/jdk/test/java/security/Provider/BaseProviderValidator.java +@@ -0,0 +1,76 @@ ++/* ++ * Copyright (c) 2022, Huawei Technologies Co., Ltd. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. Huawei designates this ++ * particular file as subject to the "Classpath" exception as provided ++ * by Huawei in the LICENSE file that accompanied this code. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please visit https://gitee.com/openeuler/bishengjdk-8 if you need additional ++ * information or have any questions. ++ */ ++ ++/* ++ * @test ++ * @bug 7092821 ++ * @library ../testlibrary ++ * @summary make sure that Sun providers do not miss any algorithms after ++ * modifying the frameworks underneath ++ * @author Henry Yang ++ */ ++ ++import java.security.Provider; ++import java.security.Provider.Service; ++ ++/** ++ * Base class for a provider validator ++ * ++ * @author Henry Yang ++ * @since 2022-05-05 ++ */ ++public abstract class BaseProviderValidator { ++ String providerName; ++ Provider provider; ++ ++ public BaseProviderValidator() { ++ provider = getDefaultProvider(); ++ providerName = provider.getName(); ++ } ++ ++ abstract Provider getDefaultProvider(); ++ ++ abstract boolean validate() throws Exception; ++ ++ Service getService(String type, String algo) { ++ return ProviderValidationUtil.getService(provider, type, algo); ++ } ++ ++ boolean checkService(String serviceName) { ++ String[] typeAndAlg = ProviderValidationUtil.getTypeAndAlgorithm(serviceName); ++ if(typeAndAlg == null || typeAndAlg.length < 2){ ++ throw new RuntimeException("service name is not in a right formation"); ++ } ++ return ProviderValidationUtil.checkService(provider, typeAndAlg[0], typeAndAlg[1]); ++ } ++ ++ boolean checkAlias(String aliasFullName, String serviceShortName) { ++ return ProviderValidationUtil.checkAlias(provider, aliasFullName, serviceShortName); ++ } ++ ++ boolean checkAttribute(String attrName, String attrValue) { ++ String[] nameAndAttr = attrName.split("\\s+"); ++ return ProviderValidationUtil.checkAttribute(provider, nameAndAttr[0], nameAndAttr[1], attrValue); ++ } ++} +diff --git a/jdk/test/java/security/Provider/GetServiceRace.java b/jdk/test/java/security/Provider/GetServiceRace.java +new file mode 100644 +index 000000000..b5b47b5d9 +--- /dev/null ++++ b/jdk/test/java/security/Provider/GetServiceRace.java +@@ -0,0 +1,98 @@ ++/* ++ * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ */ ++ ++/* ++ * @test ++ * @bug 8231387 ++ * @library ../testlibrary ++ * @summary make sure getService() avoids a race ++ * @author Tianmin Shi ++ */ ++ ++import java.security.Provider; ++ ++public class GetServiceRace { ++ ++ private static final Provider testProvider; ++ static { ++ testProvider = new Provider("MyProvider", 1.0, "test") { ++ }; ++ testProvider.put("CertificateFactory.Fixed", "MyCertificateFactory"); ++ } ++ ++ private static final int NUMBER_OF_RETRIEVERS = 3; ++ private static final int TEST_TIME_MS = 1000; ++ ++ public static boolean testFailed = false; ++ ++ public static void main(String[] args) throws Exception { ++ Updater updater = new Updater(); ++ updater.start(); ++ Retriever [] retrievers = new Retriever[NUMBER_OF_RETRIEVERS]; ++ for (int i=0; i Test Passed"); ++ } ++ ++ private static void validate(Provider p, String algo, String alias) { ++ Provider.Service s = p.getService("SecureRandom", alias); ++ if (s == null) { ++ throw new RuntimeException("Failed alias " + alias + " check, " + ++ "exp: " + algo + ", got null"); ++ } ++ if (!algo.equals(s.getAlgorithm())) { ++ throw new RuntimeException("Failed alias " + alias + " check, " + ++ "exp: " + algo + ", got " + s.getAlgorithm()); ++ } ++ } ++ ++ ++ private static final String SR_IMPLCLASS = ++ "sun.security.provider.SecureRandom"; ++ private static class CustomProvider extends Provider { ++ private static class CustomService extends Provider.Service { ++ CustomService(Provider p, String type, String algo, String cName) { ++ super(p, type, algo, cName, null, null); ++ } ++ } ++ ++ CustomProvider() { ++ super("CP", 1.0, "test provider that registers two services, " + ++ "one with put and one with putService"); ++ ++ putService(new CustomService(this, "SecureRandom", ++ MODERN_ALGO, SR_IMPLCLASS)); ++ put("SecureRandom." + LEGACY_ALGO, SR_IMPLCLASS); ++ } ++ } ++} +diff --git a/jdk/test/java/security/Provider/ProviderValidationUtil.java b/jdk/test/java/security/Provider/ProviderValidationUtil.java +new file mode 100644 +index 000000000..8c4ef89c7 +--- /dev/null ++++ b/jdk/test/java/security/Provider/ProviderValidationUtil.java +@@ -0,0 +1,270 @@ ++/* ++ * Copyright (c) 2022, Huawei Technologies Co., Ltd. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. Huawei designates this ++ * particular file as subject to the "Classpath" exception as provided ++ * by Huawei in the LICENSE file that accompanied this code. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please visit https://gitee.com/openeuler/bishengjdk-8 if you need additional ++ * information or have any questions. ++ */ ++ ++/* ++ * @test ++ * @bug 7092821 ++ * @library ../testlibrary ++ * @summary make sure that Sun providers do not miss any algorithms after ++ * modifying the frameworks underneath ++ * @author Henry Yang ++ */ ++ ++import static java.util.Locale.ENGLISH; ++ ++import java.lang.reflect.InvocationTargetException; ++import java.lang.reflect.Method; ++import java.security.Provider; ++import java.security.Provider.Service; ++import java.util.Collections; ++import java.util.HashSet; ++import java.util.List; ++import java.util.Set; ++ ++/** ++ * utils for provider validator ++ * ++ * @author Henry Yang ++ * @since 2022-05-05 ++ */ ++public class ProviderValidationUtil { ++ private static final String ALIAS_PREFIX_LOWER = "alg.alias."; ++ private static final int ALIAS_LENGTH = ALIAS_PREFIX_LOWER.length(); ++ ++ /** ++ * get a service from a provider for a specific algorithm ++ * ++ * @param provider the provider to get a service ++ * @param type algorithm type ++ * @param algo algorithm name ++ * @return the service of the specific algorithm ++ */ ++ public static Service getService(Provider provider, String type, String algo) { ++ Service service = provider.getService(type, algo); ++ if (service == null) { ++ throw new ServiceNotFoundException(provider.getName(), getServiceName(type, algo)); ++ } ++ return service; ++ } ++ ++ /** ++ * checks if the provider offers services for a specific algorithm ++ * ++ * @param provider the provider to check ++ * @param type algorithm type ++ * @param algo algorithm name ++ * @return true if passed this check ++ */ ++ public static boolean checkService(Provider provider, String type, String algo) { ++ Service service = getService(provider, type, algo); ++ String className = service.getClassName(); ++ if (className == null) { ++ throw new ServiceNotFoundException(provider.getName(), getServiceName(type, algo)); ++ } ++ try { ++ Class.forName(className); ++ } catch (ClassNotFoundException e) { ++ throw new ServiceNotFoundException(provider.getName(), getServiceName(type, algo)); ++ } ++ return true; ++ } ++ ++ private static List getAlias(Service service) { ++ try { ++ Method method = Service.class.getDeclaredMethod("getAliases"); ++ method.setAccessible(true); ++ List aliases = (List) method.invoke(service, null); ++ return aliases; ++ } catch (NoSuchMethodException | InvocationTargetException | IllegalAccessException e) { ++ e.printStackTrace(); ++ } ++ return Collections.emptyList(); ++ } ++ ++ /** ++ * check if the provider associates the alias name to the service ++ * ++ * @param provider the provider to check ++ * @param aliasFullName alias ++ * @param serviceShortName service name for short ++ * @return true if passed this check ++ */ ++ public static boolean checkAlias(Provider provider, String aliasFullName, String serviceShortName) { ++ if (aliasFullName.toLowerCase(ENGLISH).startsWith(ALIAS_PREFIX_LOWER)) { ++ // for example, in provider defination put("Alg.Alias.MessageDigest.SHA", "SHA-1"); ++ // Alg.Alias.MessageDigest.SHA for the aliasFullNanme and SHA-1 for serviceShortName ++ // the aliasKey is MessageDigest.SHA ++ String aliasKey = aliasFullName.substring(ALIAS_LENGTH); ++ String[] typeAndAlg = getTypeAndAlgorithm(aliasKey); ++ if (typeAndAlg == null || typeAndAlg.length < 2) { ++ throw new NameMalFormatException("alias name and type cannot be null"); ++ } ++ String type = typeAndAlg[0]; ++ String aliasAlg = typeAndAlg[1].intern(); ++ Service aliasService = provider.getService(type, aliasAlg); ++ if (aliasService == null) { ++ throw new ServiceNotFoundException(provider.getName(), getServiceName(type, aliasAlg)); ++ } ++ Service service = provider.getService(type, serviceShortName); ++ if (service == null) { ++ throw new ServiceNotFoundException(provider.getName(), getServiceName(type, serviceShortName)); ++ } ++ if (service != aliasService || !checkAliasInService(service, aliasAlg)) { ++ throw new AliasNotMatchedException( ++ getServiceName(type, aliasAlg), getServiceName(type, serviceShortName)); ++ } ++ } else { ++ throw new NameMalFormatException("Alias name is not in a proper format"); ++ } ++ return true; ++ } ++ ++ private static boolean checkAliasInService(Service service, String... aliasArray) { ++ List aliases = getAlias(service); ++ Set aliasesSet = new HashSet<>(); ++ aliasesSet.addAll(aliases); ++ for (String aliasName : aliasArray) { ++ if (!aliasesSet.contains(aliasName)) { ++ return false; ++ } ++ } ++ return true; ++ } ++ ++ /** ++ * check if the service has a specific attribute with the correct value in the provider ++ * ++ * @param provider the provider to check ++ * @param serviceName service name ++ * @param attrName attribute name ++ * @param attrValue attribute value ++ * @return true if passed this check ++ */ ++ public static boolean checkAttribute(Provider provider, String serviceName, String attrName, String attrValue) { ++ String[] typeAndAlg = getTypeAndAlgorithm(serviceName); ++ if (typeAndAlg == null || typeAndAlg.length < 2) { ++ throw new NameMalFormatException("service name is not in a right formation"); ++ } ++ Service service = getService(provider, typeAndAlg[0], typeAndAlg[1]); ++ return checkAttribute(service, attrName, attrValue); ++ } ++ ++ private static boolean checkAttribute(Service service, String attrName, String attrValue) { ++ if (!attrValue.equals(service.getAttribute(attrName))) { ++ throw new AttributeNotFoundException(service.getType(), service.getAlgorithm(), attrName, attrValue); ++ } ++ return true; ++ } ++ ++ private static String getServiceName(String type, String algo) { ++ return type + "." + algo; ++ } ++ ++ /** ++ * seperate algorithm key with type and name ++ * ++ * @param key algorithm full name ++ * @return string array with algorithm type and name ++ */ ++ public static String[] getTypeAndAlgorithm(String key) { ++ int index = key.indexOf('.'); ++ if (index < 1) { ++ return new String[0]; ++ } ++ String type = key.substring(0, index); ++ String alg = key.substring(index + 1); ++ return new String[] {type, alg}; ++ } ++ ++ /** ++ * throws this exception if we cannot find the service in the provider ++ * ++ * @author Henry Yang ++ * @since 2022-05-05 ++ */ ++ public static class ServiceNotFoundException extends RuntimeException { ++ public ServiceNotFoundException(String provider, String serviceName) { ++ this("faild to find " + serviceName + " in " + provider + " provider"); ++ } ++ ++ public ServiceNotFoundException(String message) { ++ super(message); ++ } ++ } ++ ++ /** ++ * throws this exception if we cannot find the attribute in the service ++ * or the attribute value is not correct ++ * ++ * @author Henry Yang ++ * @since 2022-05-05 ++ */ ++ public static class AttributeNotFoundException extends RuntimeException { ++ public AttributeNotFoundException(String type, String algo, String attrName, String attrValue) { ++ this( ++ "faild " ++ + type ++ + "." ++ + algo ++ + " '" ++ + attrName ++ + "' attribute check, " ++ + "the correct value should be '" ++ + attrValue ++ + "'"); ++ } ++ ++ public AttributeNotFoundException(String message) { ++ super(message); ++ } ++ } ++ ++ /** ++ * throws this exception if we cannot find the alias name in the provider ++ * ++ * @author Henry Yang ++ * @since 2022-05-05 ++ */ ++ public static class AliasNotMatchedException extends RuntimeException { ++ public AliasNotMatchedException(String aliasName, String serviceName) { ++ this("faild to find alias name " + aliasName + " in " + serviceName); ++ } ++ ++ public AliasNotMatchedException(String message) { ++ super(message); ++ } ++ } ++ ++ /** ++ * throws this exception if the name is in a malformation ++ * ++ * @author Henry Yang ++ * @since 2022-05-05 ++ */ ++ public static class NameMalFormatException extends RuntimeException { ++ public NameMalFormatException(String message) { ++ super(message); ++ } ++ } ++} +diff --git a/jdk/test/java/security/Provider/SunJCEValidator.java b/jdk/test/java/security/Provider/SunJCEValidator.java +new file mode 100644 +index 000000000..314abb380 +--- /dev/null ++++ b/jdk/test/java/security/Provider/SunJCEValidator.java +@@ -0,0 +1,574 @@ ++/* ++ * Copyright (c) 2022, Huawei Technologies Co., Ltd. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. Huawei designates this ++ * particular file as subject to the "Classpath" exception as provided ++ * by Huawei in the LICENSE file that accompanied this code. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please visit https://gitee.com/openeuler/bishengjdk-8 if you need additional ++ * information or have any questions. ++ */ ++ ++/* ++ * @test ++ * @bug 7092821 ++ * @library ../testlibrary ++ * @summary make sure that Sun providers do not miss any algorithms after ++ * modifying the frameworks underneath ++ * @author Henry Yang ++ */ ++ ++/* ++ *- @TestCaseID:Provider/SunJCEValidator.java ++ *- @TestCaseName:Provider/SunJCEValidator.java ++ *- @TestCaseType:Function test ++ *- @RequirementID:AR.SR.IREQ02758058.001.001 ++ *- @RequirementName: java.security.Provider.getService() is synchronized and became scalability bottleneck ++ *- @Condition:JDK8u302及以后 ++ *- @Brief:测试相应provider更改底层架构以后所提供的service是否与原先有差异(以openJDK8u302为准) ++ * -#step:比较openJDK8u302 SunJceProvider与此特性修改后的SunJceProvider所提供的service是否一致 ++ *- @Expect:正常运行 ++ *- @Priority:Level 1 ++ */ ++ ++import com.sun.crypto.provider.SunJCE; ++ ++import java.security.Provider; ++ ++/** ++ * validator for SunJCE provider, make sure we do not miss any algorithm ++ * after the modification. ++ * ++ * @author Henry Yang ++ * @since 2022-05-05 ++ */ ++public class SunJCEValidator extends BaseProviderValidator { ++ private static final String OID_PKCS12_RC4_128 = "1.2.840.113549.1.12.1.1"; ++ private static final String OID_PKCS12_RC4_40 = "1.2.840.113549.1.12.1.2"; ++ private static final String OID_PKCS12_DESede = "1.2.840.113549.1.12.1.3"; ++ private static final String OID_PKCS12_RC2_128 = "1.2.840.113549.1.12.1.5"; ++ private static final String OID_PKCS12_RC2_40 = "1.2.840.113549.1.12.1.6"; ++ private static final String OID_PKCS5_MD5_DES = "1.2.840.113549.1.5.3"; ++ private static final String OID_PKCS5_PBKDF2 = "1.2.840.113549.1.5.12"; ++ private static final String OID_PKCS5_PBES2 = "1.2.840.113549.1.5.13"; ++ private static final String OID_PKCS3 = "1.2.840.113549.1.3.1"; ++ ++ public static void main(String[] args) throws Exception { ++ SunJCEValidator validator = new SunJCEValidator(); ++ validator.validate(); ++ } ++ ++ @Override ++ Provider getDefaultProvider() { ++ return new SunJCE(); ++ } ++ ++ @Override ++ boolean validate() throws Exception { ++ final String BLOCK_MODES = ++ "ECB|CBC|PCBC|CTR|CTS|CFB|OFB" ++ + "|CFB8|CFB16|CFB24|CFB32|CFB40|CFB48|CFB56|CFB64" ++ + "|OFB8|OFB16|OFB24|OFB32|OFB40|OFB48|OFB56|OFB64"; ++ final String BLOCK_MODES128 = ++ BLOCK_MODES ++ + "|GCM|CFB72|CFB80|CFB88|CFB96|CFB104|CFB112|CFB120|CFB128" ++ + "|OFB72|OFB80|OFB88|OFB96|OFB104|OFB112|OFB120|OFB128"; ++ final String BLOCK_PADS = "NOPADDING|PKCS5PADDING|ISO10126PADDING"; ++ ++ /* ++ * Cipher engines ++ */ ++ checkService("Cipher.RSA"); ++ checkAttribute("Cipher.RSA SupportedModes", "ECB"); ++ checkAttribute( ++ "Cipher.RSA SupportedPaddings", ++ "NOPADDING|PKCS1PADDING|OAEPPADDING" ++ + "|OAEPWITHMD5ANDMGF1PADDING" ++ + "|OAEPWITHSHA1ANDMGF1PADDING" ++ + "|OAEPWITHSHA-1ANDMGF1PADDING" ++ + "|OAEPWITHSHA-224ANDMGF1PADDING" ++ + "|OAEPWITHSHA-256ANDMGF1PADDING" ++ + "|OAEPWITHSHA-384ANDMGF1PADDING" ++ + "|OAEPWITHSHA-512ANDMGF1PADDING" ++ + "|OAEPWITHSHA-512/224ANDMGF1PADDING" ++ + "|OAEPWITHSHA-512/256ANDMGF1PADDING"); ++ checkAttribute( ++ "Cipher.RSA SupportedKeyClasses", ++ "java.security.interfaces.RSAPublicKey" + "|java.security.interfaces.RSAPrivateKey"); ++ ++ checkService("Cipher.DES"); ++ checkAttribute("Cipher.DES SupportedModes", BLOCK_MODES); ++ checkAttribute("Cipher.DES SupportedPaddings", BLOCK_PADS); ++ checkAttribute("Cipher.DES SupportedKeyFormats", "RAW"); ++ ++ checkService("Cipher.DESede"); ++ checkAlias("Alg.Alias.Cipher.TripleDES", "DESede"); ++ checkAttribute("Cipher.DESede SupportedModes", BLOCK_MODES); ++ checkAttribute("Cipher.DESede SupportedPaddings", BLOCK_PADS); ++ checkAttribute("Cipher.DESede SupportedKeyFormats", "RAW"); ++ ++ checkService("Cipher.DESedeWrap"); ++ checkAttribute("Cipher.DESedeWrap SupportedModes", "CBC"); ++ checkAttribute("Cipher.DESedeWrap SupportedPaddings", "NOPADDING"); ++ checkAttribute("Cipher.DESedeWrap SupportedKeyFormats", "RAW"); ++ System.out.println("Cipher engines check passed"); ++ ++ // PBES1 ++ checkService("Cipher.PBEWithMD5AndDES"); ++ checkAlias("Alg.Alias.Cipher.OID." + OID_PKCS5_MD5_DES, "PBEWithMD5AndDES"); ++ checkAlias("Alg.Alias.Cipher." + OID_PKCS5_MD5_DES, "PBEWithMD5AndDES"); ++ ++ checkService("Cipher.PBEWithMD5AndTripleDES"); ++ ++ checkService("Cipher.PBEWithSHA1AndDESede"); ++ checkAlias("Alg.Alias.Cipher.OID." + OID_PKCS12_DESede, "PBEWithSHA1AndDESede"); ++ checkAlias("Alg.Alias.Cipher." + OID_PKCS12_DESede, "PBEWithSHA1AndDESede"); ++ ++ checkService("Cipher.PBEWithSHA1AndRC2_40"); ++ checkAlias("Alg.Alias.Cipher.OID." + OID_PKCS12_RC2_40, "PBEWithSHA1AndRC2_40"); ++ checkAlias("Alg.Alias.Cipher." + OID_PKCS12_RC2_40, "PBEWithSHA1AndRC2_40"); ++ ++ checkService("Cipher.PBEWithSHA1AndRC2_128"); ++ checkAlias("Alg.Alias.Cipher.OID." + OID_PKCS12_RC2_128, "PBEWithSHA1AndRC2_128"); ++ checkAlias("Alg.Alias.Cipher." + OID_PKCS12_RC2_128, "PBEWithSHA1AndRC2_128"); ++ ++ checkService("Cipher.PBEWithSHA1AndRC4_40"); ++ checkAlias("Alg.Alias.Cipher.OID." + OID_PKCS12_RC4_40, "PBEWithSHA1AndRC4_40"); ++ checkAlias("Alg.Alias.Cipher." + OID_PKCS12_RC4_40, "PBEWithSHA1AndRC4_40"); ++ ++ checkService("Cipher.PBEWithSHA1AndRC4_128"); ++ checkAlias("Alg.Alias.Cipher.OID." + OID_PKCS12_RC4_128, "PBEWithSHA1AndRC4_128"); ++ checkAlias("Alg.Alias.Cipher." + OID_PKCS12_RC4_128, "PBEWithSHA1AndRC4_128"); ++ System.out.println("PBES1 check passed"); ++ ++ // PBES2 ++ ++ checkService("Cipher.PBEWithHmacSHA1AndAES_128"); ++ ++ checkService("Cipher.PBEWithHmacSHA224AndAES_128"); ++ ++ checkService("Cipher.PBEWithHmacSHA256AndAES_128"); ++ ++ checkService("Cipher.PBEWithHmacSHA384AndAES_128"); ++ ++ checkService("Cipher.PBEWithHmacSHA512AndAES_128"); ++ ++ checkService("Cipher.PBEWithHmacSHA1AndAES_256"); ++ ++ checkService("Cipher.PBEWithHmacSHA224AndAES_256"); ++ ++ checkService("Cipher.PBEWithHmacSHA256AndAES_256"); ++ ++ checkService("Cipher.PBEWithHmacSHA384AndAES_256"); ++ ++ checkService("Cipher.PBEWithHmacSHA512AndAES_256"); ++ ++ checkService("Cipher.Blowfish"); ++ checkAttribute("Cipher.Blowfish SupportedModes", BLOCK_MODES); ++ checkAttribute("Cipher.Blowfish SupportedPaddings", BLOCK_PADS); ++ checkAttribute("Cipher.Blowfish SupportedKeyFormats", "RAW"); ++ ++ checkService("Cipher.AES"); ++ checkAlias("Alg.Alias.Cipher.Rijndael", "AES"); ++ checkAttribute("Cipher.AES SupportedModes", BLOCK_MODES128); ++ checkAttribute("Cipher.AES SupportedPaddings", BLOCK_PADS); ++ checkAttribute("Cipher.AES SupportedKeyFormats", "RAW"); ++ ++ checkService("Cipher.AES_128/ECB/NoPadding"); ++ checkAlias("Alg.Alias.Cipher.2.16.840.1.101.3.4.1.1", "AES_128/ECB/NoPadding"); ++ checkAlias("Alg.Alias.Cipher.OID.2.16.840.1.101.3.4.1.1", "AES_128/ECB/NoPadding"); ++ checkService("Cipher.AES_128/CBC/NoPadding"); ++ checkAlias("Alg.Alias.Cipher.2.16.840.1.101.3.4.1.2", "AES_128/CBC/NoPadding"); ++ checkAlias("Alg.Alias.Cipher.OID.2.16.840.1.101.3.4.1.2", "AES_128/CBC/NoPadding"); ++ checkService("Cipher.AES_128/OFB/NoPadding"); ++ checkAlias("Alg.Alias.Cipher.2.16.840.1.101.3.4.1.3", "AES_128/OFB/NoPadding"); ++ checkAlias("Alg.Alias.Cipher.OID.2.16.840.1.101.3.4.1.3", "AES_128/OFB/NoPadding"); ++ checkService("Cipher.AES_128/CFB/NoPadding"); ++ checkAlias("Alg.Alias.Cipher.2.16.840.1.101.3.4.1.4", "AES_128/CFB/NoPadding"); ++ checkAlias("Alg.Alias.Cipher.OID.2.16.840.1.101.3.4.1.4", "AES_128/CFB/NoPadding"); ++ checkService("Cipher.AES_128/GCM/NoPadding"); ++ checkAlias("Alg.Alias.Cipher.2.16.840.1.101.3.4.1.6", "AES_128/GCM/NoPadding"); ++ checkAlias("Alg.Alias.Cipher.OID.2.16.840.1.101.3.4.1.6", "AES_128/GCM/NoPadding"); ++ ++ checkService("Cipher.AES_192/ECB/NoPadding"); ++ checkAlias("Alg.Alias.Cipher.2.16.840.1.101.3.4.1.21", "AES_192/ECB/NoPadding"); ++ checkAlias("Alg.Alias.Cipher.OID.2.16.840.1.101.3.4.1.21", "AES_192/ECB/NoPadding"); ++ checkService("Cipher.AES_192/CBC/NoPadding"); ++ checkAlias("Alg.Alias.Cipher.2.16.840.1.101.3.4.1.22", "AES_192/CBC/NoPadding"); ++ checkAlias("Alg.Alias.Cipher.OID.2.16.840.1.101.3.4.1.22", "AES_192/CBC/NoPadding"); ++ checkService("Cipher.AES_192/OFB/NoPadding"); ++ checkAlias("Alg.Alias.Cipher.2.16.840.1.101.3.4.1.23", "AES_192/OFB/NoPadding"); ++ checkAlias("Alg.Alias.Cipher.OID.2.16.840.1.101.3.4.1.23", "AES_192/OFB/NoPadding"); ++ checkService("Cipher.AES_192/CFB/NoPadding"); ++ checkAlias("Alg.Alias.Cipher.2.16.840.1.101.3.4.1.24", "AES_192/CFB/NoPadding"); ++ checkAlias("Alg.Alias.Cipher.OID.2.16.840.1.101.3.4.1.24", "AES_192/CFB/NoPadding"); ++ checkService("Cipher.AES_192/GCM/NoPadding"); ++ checkAlias("Alg.Alias.Cipher.2.16.840.1.101.3.4.1.26", "AES_192/GCM/NoPadding"); ++ checkAlias("Alg.Alias.Cipher.OID.2.16.840.1.101.3.4.1.26", "AES_192/GCM/NoPadding"); ++ ++ checkService("Cipher.AES_256/ECB/NoPadding"); ++ checkAlias("Alg.Alias.Cipher.2.16.840.1.101.3.4.1.41", "AES_256/ECB/NoPadding"); ++ checkAlias("Alg.Alias.Cipher.OID.2.16.840.1.101.3.4.1.41", "AES_256/ECB/NoPadding"); ++ checkService("Cipher.AES_256/CBC/NoPadding"); ++ checkAlias("Alg.Alias.Cipher.2.16.840.1.101.3.4.1.42", "AES_256/CBC/NoPadding"); ++ checkAlias("Alg.Alias.Cipher.OID.2.16.840.1.101.3.4.1.42", "AES_256/CBC/NoPadding"); ++ checkService("Cipher.AES_256/OFB/NoPadding"); ++ checkAlias("Alg.Alias.Cipher.2.16.840.1.101.3.4.1.43", "AES_256/OFB/NoPadding"); ++ checkAlias("Alg.Alias.Cipher.OID.2.16.840.1.101.3.4.1.43", "AES_256/OFB/NoPadding"); ++ checkService("Cipher.AES_256/CFB/NoPadding"); ++ checkAlias("Alg.Alias.Cipher.2.16.840.1.101.3.4.1.44", "AES_256/CFB/NoPadding"); ++ checkAlias("Alg.Alias.Cipher.OID.2.16.840.1.101.3.4.1.44", "AES_256/CFB/NoPadding"); ++ checkService("Cipher.AES_256/GCM/NoPadding"); ++ checkAlias("Alg.Alias.Cipher.2.16.840.1.101.3.4.1.46", "AES_256/GCM/NoPadding"); ++ checkAlias("Alg.Alias.Cipher.OID.2.16.840.1.101.3.4.1.46", "AES_256/GCM/NoPadding"); ++ ++ checkService("Cipher.AESWrap"); ++ checkAttribute("Cipher.AESWrap SupportedModes", "ECB"); ++ checkAttribute("Cipher.AESWrap SupportedPaddings", "NOPADDING"); ++ checkAttribute("Cipher.AESWrap SupportedKeyFormats", "RAW"); ++ ++ checkService("Cipher.AESWrap_128"); ++ checkAlias("Alg.Alias.Cipher.2.16.840.1.101.3.4.1.5", "AESWrap_128"); ++ checkAlias("Alg.Alias.Cipher.OID.2.16.840.1.101.3.4.1.5", "AESWrap_128"); ++ checkService("Cipher.AESWrap_192"); ++ checkAlias("Alg.Alias.Cipher.2.16.840.1.101.3.4.1.25", "AESWrap_192"); ++ checkAlias("Alg.Alias.Cipher.OID.2.16.840.1.101.3.4.1.25", "AESWrap_192"); ++ checkService("Cipher.AESWrap_256"); ++ checkAlias("Alg.Alias.Cipher.2.16.840.1.101.3.4.1.45", "AESWrap_256"); ++ checkAlias("Alg.Alias.Cipher.OID.2.16.840.1.101.3.4.1.45", "AESWrap_256"); ++ ++ checkService("Cipher.RC2"); ++ checkAttribute("Cipher.RC2 SupportedModes", BLOCK_MODES); ++ checkAttribute("Cipher.RC2 SupportedPaddings", BLOCK_PADS); ++ checkAttribute("Cipher.RC2 SupportedKeyFormats", "RAW"); ++ ++ checkService("Cipher.ARCFOUR"); ++ checkAlias("Alg.Alias.Cipher.RC4", "ARCFOUR"); ++ checkAttribute("Cipher.ARCFOUR SupportedModes", "ECB"); ++ checkAttribute("Cipher.ARCFOUR SupportedPaddings", "NOPADDING"); ++ checkAttribute("Cipher.ARCFOUR SupportedKeyFormats", "RAW"); ++ System.out.println("PBES2 check passed"); ++ ++ /* ++ * Key(pair) Generator engines ++ */ ++ checkService("KeyGenerator.DES"); ++ ++ checkService("KeyGenerator.DESede"); ++ checkAlias("Alg.Alias.KeyGenerator.TripleDES", "DESede"); ++ ++ checkService("KeyGenerator.Blowfish"); ++ ++ checkService("KeyGenerator.AES"); ++ checkAlias("Alg.Alias.KeyGenerator.Rijndael", "AES"); ++ ++ checkService("KeyGenerator.RC2"); ++ checkService("KeyGenerator.ARCFOUR"); ++ checkAlias("Alg.Alias.KeyGenerator.RC4", "ARCFOUR"); ++ ++ checkService("KeyGenerator.HmacMD5"); ++ ++ checkService("KeyGenerator.HmacSHA1"); ++ checkAlias("Alg.Alias.KeyGenerator.OID.1.2.840.113549.2.7", "HmacSHA1"); ++ checkAlias("Alg.Alias.KeyGenerator.1.2.840.113549.2.7", "HmacSHA1"); ++ ++ checkService("KeyGenerator.HmacSHA224"); ++ checkAlias("Alg.Alias.KeyGenerator.OID.1.2.840.113549.2.8", "HmacSHA224"); ++ checkAlias("Alg.Alias.KeyGenerator.1.2.840.113549.2.8", "HmacSHA224"); ++ ++ checkService("KeyGenerator.HmacSHA256"); ++ checkAlias("Alg.Alias.KeyGenerator.OID.1.2.840.113549.2.9", "HmacSHA256"); ++ checkAlias("Alg.Alias.KeyGenerator.1.2.840.113549.2.9", "HmacSHA256"); ++ ++ checkService("KeyGenerator.HmacSHA384"); ++ checkAlias("Alg.Alias.KeyGenerator.OID.1.2.840.113549.2.10", "HmacSHA384"); ++ checkAlias("Alg.Alias.KeyGenerator.1.2.840.113549.2.10", "HmacSHA384"); ++ ++ checkService("KeyGenerator.HmacSHA512"); ++ checkAlias("Alg.Alias.KeyGenerator.OID.1.2.840.113549.2.11", "HmacSHA512"); ++ checkAlias("Alg.Alias.KeyGenerator.1.2.840.113549.2.11", "HmacSHA512"); ++ ++ checkService("KeyPairGenerator.DiffieHellman"); ++ checkAlias("Alg.Alias.KeyPairGenerator.DH", "DiffieHellman"); ++ checkAlias("Alg.Alias.KeyPairGenerator.OID." + OID_PKCS3, "DiffieHellman"); ++ checkAlias("Alg.Alias.KeyPairGenerator." + OID_PKCS3, "DiffieHellman"); ++ System.out.println("Key(pair) Generator engines check passed"); ++ ++ /* ++ * Algorithm parameter generation engines ++ */ ++ checkService("AlgorithmParameterGenerator.DiffieHellman"); ++ checkAlias("Alg.Alias.AlgorithmParameterGenerator.DH", "DiffieHellman"); ++ checkAlias("Alg.Alias.AlgorithmParameterGenerator.OID." + OID_PKCS3, "DiffieHellman"); ++ checkAlias("Alg.Alias.AlgorithmParameterGenerator." + OID_PKCS3, "DiffieHellman"); ++ System.out.println("Algorithm parameter generation engines check passed"); ++ ++ /* ++ * Key Agreement engines ++ */ ++ checkService("KeyAgreement.DiffieHellman"); ++ checkAlias("Alg.Alias.KeyAgreement.DH", "DiffieHellman"); ++ checkAlias("Alg.Alias.KeyAgreement.OID." + OID_PKCS3, "DiffieHellman"); ++ checkAlias("Alg.Alias.KeyAgreement." + OID_PKCS3, "DiffieHellman"); ++ ++ checkAttribute( ++ "KeyAgreement.DiffieHellman SupportedKeyClasses", ++ "javax.crypto.interfaces.DHPublicKey" + "|javax.crypto.interfaces.DHPrivateKey"); ++ System.out.println("Key Agreement engines check passed"); ++ ++ /* ++ * Algorithm Parameter engines ++ */ ++ checkService("AlgorithmParameters.DiffieHellman"); ++ checkAlias("Alg.Alias.AlgorithmParameters.DH", "DiffieHellman"); ++ checkAlias("Alg.Alias.AlgorithmParameters.OID." + OID_PKCS3, "DiffieHellman"); ++ checkAlias("Alg.Alias.AlgorithmParameters." + OID_PKCS3, "DiffieHellman"); ++ ++ checkService("AlgorithmParameters.DES"); ++ ++ checkService("AlgorithmParameters.DESede"); ++ checkAlias("Alg.Alias.AlgorithmParameters.TripleDES", "DESede"); ++ ++ checkService("AlgorithmParameters.PBE"); ++ ++ checkService("AlgorithmParameters.PBEWithMD5AndDES"); ++ checkAlias("Alg.Alias.AlgorithmParameters.OID." + OID_PKCS5_MD5_DES, "PBEWithMD5AndDES"); ++ checkAlias("Alg.Alias.AlgorithmParameters." + OID_PKCS5_MD5_DES, "PBEWithMD5AndDES"); ++ ++ checkService("AlgorithmParameters.PBEWithMD5AndTripleDES"); ++ ++ checkService("AlgorithmParameters.PBEWithSHA1AndDESede"); ++ checkAlias("Alg.Alias.AlgorithmParameters.OID." + OID_PKCS12_DESede, "PBEWithSHA1AndDESede"); ++ checkAlias("Alg.Alias.AlgorithmParameters." + OID_PKCS12_DESede, "PBEWithSHA1AndDESede"); ++ ++ checkService("AlgorithmParameters.PBEWithSHA1AndRC2_40"); ++ checkAlias("Alg.Alias.AlgorithmParameters.OID." + OID_PKCS12_RC2_40, "PBEWithSHA1AndRC2_40"); ++ checkAlias("Alg.Alias.AlgorithmParameters." + OID_PKCS12_RC2_40, "PBEWithSHA1AndRC2_40"); ++ ++ checkService("AlgorithmParameters.PBEWithSHA1AndRC2_128"); ++ checkAlias("Alg.Alias.AlgorithmParameters.OID." + OID_PKCS12_RC2_128, "PBEWithSHA1AndRC2_128"); ++ checkAlias("Alg.Alias.AlgorithmParameters." + OID_PKCS12_RC2_128, "PBEWithSHA1AndRC2_128"); ++ ++ checkService("AlgorithmParameters.PBEWithSHA1AndRC4_40"); ++ checkAlias("Alg.Alias.AlgorithmParameters.OID." + OID_PKCS12_RC4_40, "PBEWithSHA1AndRC4_40"); ++ checkAlias("Alg.Alias.AlgorithmParameters." + OID_PKCS12_RC4_40, "PBEWithSHA1AndRC4_40"); ++ ++ checkService("AlgorithmParameters.PBEWithSHA1AndRC4_128"); ++ checkAlias("Alg.Alias.AlgorithmParameters.OID." + OID_PKCS12_RC4_128, "PBEWithSHA1AndRC4_128"); ++ checkAlias("Alg.Alias.AlgorithmParameters." + OID_PKCS12_RC4_128, "PBEWithSHA1AndRC4_128"); ++ ++ checkService("AlgorithmParameters.PBES2"); ++ checkAlias("Alg.Alias.AlgorithmParameters.OID." + OID_PKCS5_PBES2, "PBES2"); ++ checkAlias("Alg.Alias.AlgorithmParameters." + OID_PKCS5_PBES2, "PBES2"); ++ ++ checkService("AlgorithmParameters.PBEWithHmacSHA1AndAES_128"); ++ ++ checkService("AlgorithmParameters.PBEWithHmacSHA224AndAES_128"); ++ ++ checkService("AlgorithmParameters.PBEWithHmacSHA256AndAES_128"); ++ ++ checkService("AlgorithmParameters.PBEWithHmacSHA384AndAES_128"); ++ ++ checkService("AlgorithmParameters.PBEWithHmacSHA512AndAES_128"); ++ ++ checkService("AlgorithmParameters.PBEWithHmacSHA1AndAES_256"); ++ ++ checkService("AlgorithmParameters.PBEWithHmacSHA224AndAES_256"); ++ ++ checkService("AlgorithmParameters.PBEWithHmacSHA256AndAES_256"); ++ ++ checkService("AlgorithmParameters.PBEWithHmacSHA384AndAES_256"); ++ ++ checkService("AlgorithmParameters.PBEWithHmacSHA512AndAES_256"); ++ ++ checkService("AlgorithmParameters.Blowfish"); ++ ++ checkService("AlgorithmParameters.AES"); ++ checkAlias("Alg.Alias.AlgorithmParameters.Rijndael", "AES"); ++ checkService("AlgorithmParameters.GCM"); ++ ++ checkService("AlgorithmParameters.RC2"); ++ ++ checkService("AlgorithmParameters.OAEP"); ++ System.out.println("Algorithm Parameter engines check passed"); ++ ++ /* ++ * Key factories ++ */ ++ checkService("KeyFactory.DiffieHellman"); ++ checkAlias("Alg.Alias.KeyFactory.DH", "DiffieHellman"); ++ checkAlias("Alg.Alias.KeyFactory.OID." + OID_PKCS3, "DiffieHellman"); ++ checkAlias("Alg.Alias.KeyFactory." + OID_PKCS3, "DiffieHellman"); ++ System.out.println("Key factories check passed"); ++ ++ /* ++ * Secret-key factories ++ */ ++ checkService("SecretKeyFactory.DES"); ++ ++ checkService("SecretKeyFactory.DESede"); ++ checkAlias("Alg.Alias.SecretKeyFactory.TripleDES", "DESede"); ++ ++ checkService("SecretKeyFactory.PBEWithMD5AndDES"); ++ checkAlias("Alg.Alias.SecretKeyFactory.OID." + OID_PKCS5_MD5_DES, "PBEWithMD5AndDES"); ++ checkAlias("Alg.Alias.SecretKeyFactory." + OID_PKCS5_MD5_DES, "PBEWithMD5AndDES"); ++ ++ checkAlias("Alg.Alias.SecretKeyFactory.PBE", "PBEWithMD5AndDES"); ++ ++ /* ++ * Internal in-house crypto algorithm used for ++ * the JCEKS keystore type. Since this was developed ++ * internally, there isn't an OID corresponding to this ++ * algorithm. ++ */ ++ checkService("SecretKeyFactory.PBEWithMD5AndTripleDES"); ++ ++ checkService("SecretKeyFactory.PBEWithSHA1AndDESede"); ++ checkAlias("Alg.Alias.SecretKeyFactory.OID." + OID_PKCS12_DESede, "PBEWithSHA1AndDESede"); ++ checkAlias("Alg.Alias.SecretKeyFactory." + OID_PKCS12_DESede, "PBEWithSHA1AndDESede"); ++ ++ checkService("SecretKeyFactory.PBEWithSHA1AndRC2_40"); ++ checkAlias("Alg.Alias.SecretKeyFactory.OID." + OID_PKCS12_RC2_40, "PBEWithSHA1AndRC2_40"); ++ checkAlias("Alg.Alias.SecretKeyFactory." + OID_PKCS12_RC2_40, "PBEWithSHA1AndRC2_40"); ++ ++ checkService("SecretKeyFactory.PBEWithSHA1AndRC2_128"); ++ checkAlias("Alg.Alias.SecretKeyFactory.OID." + OID_PKCS12_RC2_128, "PBEWithSHA1AndRC2_128"); ++ checkAlias("Alg.Alias.SecretKeyFactory." + OID_PKCS12_RC2_128, "PBEWithSHA1AndRC2_128"); ++ ++ checkService("SecretKeyFactory.PBEWithSHA1AndRC4_40"); ++ ++ checkAlias("Alg.Alias.SecretKeyFactory.OID." + OID_PKCS12_RC4_40, "PBEWithSHA1AndRC4_40"); ++ checkAlias("Alg.Alias.SecretKeyFactory." + OID_PKCS12_RC4_40, "PBEWithSHA1AndRC4_40"); ++ ++ checkService("SecretKeyFactory.PBEWithSHA1AndRC4_128"); ++ ++ checkAlias("Alg.Alias.SecretKeyFactory.OID." + OID_PKCS12_RC4_128, "PBEWithSHA1AndRC4_128"); ++ checkAlias("Alg.Alias.SecretKeyFactory." + OID_PKCS12_RC4_128, "PBEWithSHA1AndRC4_128"); ++ ++ checkService("SecretKeyFactory.PBEWithHmacSHA1AndAES_128"); ++ ++ checkService("SecretKeyFactory.PBEWithHmacSHA224AndAES_128"); ++ ++ checkService("SecretKeyFactory.PBEWithHmacSHA256AndAES_128"); ++ ++ checkService("SecretKeyFactory.PBEWithHmacSHA384AndAES_128"); ++ ++ checkService("SecretKeyFactory.PBEWithHmacSHA512AndAES_128"); ++ ++ checkService("SecretKeyFactory.PBEWithHmacSHA1AndAES_256"); ++ ++ checkService("SecretKeyFactory.PBEWithHmacSHA224AndAES_256"); ++ ++ checkService("SecretKeyFactory.PBEWithHmacSHA256AndAES_256"); ++ ++ checkService("SecretKeyFactory.PBEWithHmacSHA384AndAES_256"); ++ ++ checkService("SecretKeyFactory.PBEWithHmacSHA512AndAES_256"); ++ System.out.println("crypto algorithm for JCEKS keystore check passed "); ++ ++ // PBKDF2 ++ ++ checkService("SecretKeyFactory.PBKDF2WithHmacSHA1"); ++ checkAlias("Alg.Alias.SecretKeyFactory.OID." + OID_PKCS5_PBKDF2, "PBKDF2WithHmacSHA1"); ++ checkAlias("Alg.Alias.SecretKeyFactory." + OID_PKCS5_PBKDF2, "PBKDF2WithHmacSHA1"); ++ ++ checkService("SecretKeyFactory.PBKDF2WithHmacSHA224"); ++ checkService("SecretKeyFactory.PBKDF2WithHmacSHA256"); ++ checkService("SecretKeyFactory.PBKDF2WithHmacSHA384"); ++ checkService("SecretKeyFactory.PBKDF2WithHmacSHA512"); ++ ++ System.out.println("PBKDF2 check passed"); ++ ++ /* ++ * MAC ++ */ ++ checkService("Mac.HmacMD5"); ++ checkService("Mac.HmacSHA1"); ++ checkAlias("Alg.Alias.Mac.OID.1.2.840.113549.2.7", "HmacSHA1"); ++ checkAlias("Alg.Alias.Mac.1.2.840.113549.2.7", "HmacSHA1"); ++ checkService("Mac.HmacSHA224"); ++ checkAlias("Alg.Alias.Mac.OID.1.2.840.113549.2.8", "HmacSHA224"); ++ checkAlias("Alg.Alias.Mac.1.2.840.113549.2.8", "HmacSHA224"); ++ checkService("Mac.HmacSHA256"); ++ checkAlias("Alg.Alias.Mac.OID.1.2.840.113549.2.9", "HmacSHA256"); ++ checkAlias("Alg.Alias.Mac.1.2.840.113549.2.9", "HmacSHA256"); ++ checkService("Mac.HmacSHA384"); ++ checkAlias("Alg.Alias.Mac.OID.1.2.840.113549.2.10", "HmacSHA384"); ++ checkAlias("Alg.Alias.Mac.1.2.840.113549.2.10", "HmacSHA384"); ++ checkService("Mac.HmacSHA512"); ++ checkAlias("Alg.Alias.Mac.OID.1.2.840.113549.2.11", "HmacSHA512"); ++ checkAlias("Alg.Alias.Mac.1.2.840.113549.2.11", "HmacSHA512"); ++ checkService("Mac.HmacPBESHA1"); ++ ++ System.out.println("MAC check passed"); ++ ++ // PBMAC1 ++ ++ checkService("Mac.PBEWithHmacSHA1"); ++ checkService("Mac.PBEWithHmacSHA224"); ++ checkService("Mac.PBEWithHmacSHA256"); ++ checkService("Mac.PBEWithHmacSHA384"); ++ checkService("Mac.PBEWithHmacSHA512"); ++ ++ checkService("Mac.SslMacMD5"); ++ checkService("Mac.SslMacSHA1"); ++ ++ checkAttribute("Mac.HmacMD5 SupportedKeyFormats", "RAW"); ++ checkAttribute("Mac.HmacSHA1 SupportedKeyFormats", "RAW"); ++ checkAttribute("Mac.HmacSHA224 SupportedKeyFormats", "RAW"); ++ checkAttribute("Mac.HmacSHA256 SupportedKeyFormats", "RAW"); ++ checkAttribute("Mac.HmacSHA384 SupportedKeyFormats", "RAW"); ++ checkAttribute("Mac.HmacSHA512 SupportedKeyFormats", "RAW"); ++ checkAttribute("Mac.HmacPBESHA1 SupportedKeyFormats", "RAW"); ++ checkAttribute("Mac.PBEWithHmacSHA1 SupportedKeyFormatS", "RAW"); ++ checkAttribute("Mac.PBEWithHmacSHA224 SupportedKeyFormats", "RAW"); ++ checkAttribute("Mac.PBEWithHmacSHA256 SupportedKeyFormats", "RAW"); ++ checkAttribute("Mac.PBEWithHmacSHA384 SupportedKeyFormats", "RAW"); ++ checkAttribute("Mac.PBEWithHmacSHA512 SupportedKeyFormats", "RAW"); ++ checkAttribute("Mac.SslMacMD5 SupportedKeyFormats", "RAW"); ++ checkAttribute("Mac.SslMacSHA1 SupportedKeyFormats", "RAW"); ++ System.out.println("PBMAC1 check passed"); ++ ++ /* ++ * KeyStore ++ */ ++ checkService("KeyStore.JCEKS"); ++ System.out.println("KeyStore check passed"); ++ ++ /* ++ * SSL/TLS mechanisms ++ * ++ * These are strictly internal implementations and may ++ * be changed at any time. These names were chosen ++ * because PKCS11/SunPKCS11 does not yet have TLS1.2 ++ * mechanisms, and it will cause calls to come here. ++ */ ++ checkService("KeyGenerator.SunTlsPrf"); ++ checkService("KeyGenerator.SunTls12Prf"); ++ ++ checkService("KeyGenerator.SunTlsMasterSecret"); ++ checkAlias("Alg.Alias.KeyGenerator.SunTls12MasterSecret", "SunTlsMasterSecret"); ++ checkAlias("Alg.Alias.KeyGenerator.SunTlsExtendedMasterSecret", "SunTlsMasterSecret"); ++ ++ checkService("KeyGenerator.SunTlsKeyMaterial"); ++ checkAlias("Alg.Alias.KeyGenerator.SunTls12KeyMaterial", "SunTlsKeyMaterial"); ++ ++ checkService("KeyGenerator.SunTlsRsaPremasterSecret"); ++ checkAlias("Alg.Alias.KeyGenerator.SunTls12RsaPremasterSecret", "SunTlsRsaPremasterSecret"); ++ System.out.println("SSL/TLS mechanisms check passed"); ++ return true; ++ } ++} +diff --git a/jdk/test/java/security/Provider/SunJSSEValidator.java b/jdk/test/java/security/Provider/SunJSSEValidator.java +new file mode 100644 +index 000000000..5817c3b7f +--- /dev/null ++++ b/jdk/test/java/security/Provider/SunJSSEValidator.java +@@ -0,0 +1,137 @@ ++/* ++ * Copyright (c) 2022, Huawei Technologies Co., Ltd. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. Huawei designates this ++ * particular file as subject to the "Classpath" exception as provided ++ * by Huawei in the LICENSE file that accompanied this code. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please visit https://gitee.com/openeuler/bishengjdk-8 if you need additional ++ * information or have any questions. ++ */ ++ ++/* ++ * @test ++ * @bug 7092821 ++ * @library ../testlibrary ++ * @summary make sure that Sun providers do not miss any algorithms after ++ * modifying the frameworks underneath ++ * @author Henry Yang ++ */ ++ ++/* ++ *- @TestCaseID:Provider/SunJSSEValidator.java ++ *- @TestCaseName:Provider/SunJSSEValidator.java ++ *- @TestCaseType:Function test ++ *- @RequirementID:AR.SR.IREQ02758058.001.001 ++ *- @RequirementName: java.security.Provider.getService() is synchronized and became scalability bottleneck ++ *- @Condition:JDK8u302及以后 ++ *- @Brief:测试相应provider更改底层架构以后所提供的service是否与原先有差异(以openJDK8u302为准) ++ * -#step:比较openJDK8u302 SunJSSEProvider与此特性修改后的SunJSSEProvider所提供的service是否一致 ++ *- @Expect:正常运行 ++ *- @Priority:Level 1 ++ */ ++ ++import java.security.Provider; ++import java.util.Locale; ++ ++/** ++ * validator for SunJSSE provider, make sure we do not miss any algorithm ++ * after the modification. ++ * ++ * @author Henry Yang ++ * @since 2022-05-05 ++ */ ++public class SunJSSEValidator extends BaseProviderValidator { ++ private boolean fips = false; ++ ++ public static void main(String[] args) throws Exception { ++ SunJSSEValidator validator = new SunJSSEValidator(); ++ if (args != null && args.length > 0) { ++ String fipsStr = args[0].toLowerCase(Locale.ENGLISH); ++ if (!"true".equals(fipsStr) && !"false".equals(fipsStr)) { ++ throw new RuntimeException("Fips mode argument should be a boolean value"); ++ } ++ validator.setFips(Boolean.parseBoolean(fipsStr)); ++ } ++ validator.validate(); ++ } ++ ++ public void setFips(boolean isFips) { ++ this.fips = isFips; ++ } ++ ++ @Override ++ Provider getDefaultProvider() { ++ return new com.sun.net.ssl.internal.ssl.Provider(); ++ } ++ ++ @Override ++ boolean validate() throws Exception { ++ if (fips == false) { ++ checkService("KeyFactory.RSA"); ++ checkAlias("Alg.Alias.KeyFactory.1.2.840.113549.1.1", "RSA"); ++ checkAlias("Alg.Alias.KeyFactory.OID.1.2.840.113549.1.1", "RSA"); ++ ++ checkService("KeyPairGenerator.RSA"); ++ checkAlias("Alg.Alias.KeyPairGenerator.1.2.840.113549.1.1", "RSA"); ++ checkAlias("Alg.Alias.KeyPairGenerator.OID.1.2.840.113549.1.1", "RSA"); ++ ++ checkService("Signature.MD2withRSA"); ++ checkAlias("Alg.Alias.Signature.1.2.840.113549.1.1.2", "MD2withRSA"); ++ checkAlias("Alg.Alias.Signature.OID.1.2.840.113549.1.1.2", "MD2withRSA"); ++ ++ checkService("Signature.MD5withRSA"); ++ checkAlias("Alg.Alias.Signature.1.2.840.113549.1.1.4", "MD5withRSA"); ++ checkAlias("Alg.Alias.Signature.OID.1.2.840.113549.1.1.4", "MD5withRSA"); ++ ++ checkService("Signature.SHA1withRSA"); ++ checkAlias("Alg.Alias.Signature.1.2.840.113549.1.1.5", "SHA1withRSA"); ++ checkAlias("Alg.Alias.Signature.OID.1.2.840.113549.1.1.5", "SHA1withRSA"); ++ checkAlias("Alg.Alias.Signature.1.3.14.3.2.29", "SHA1withRSA"); ++ checkAlias("Alg.Alias.Signature.OID.1.3.14.3.2.29", "SHA1withRSA"); ++ } ++ checkService("Signature.MD5andSHA1withRSA"); ++ ++ checkService("KeyManagerFactory.SunX509"); ++ checkService("KeyManagerFactory.NewSunX509"); ++ checkAlias("Alg.Alias.KeyManagerFactory.PKIX", "NewSunX509"); ++ ++ checkService("TrustManagerFactory.SunX509"); ++ checkService("TrustManagerFactory.PKIX"); ++ checkAlias("Alg.Alias.TrustManagerFactory.SunPKIX", "PKIX"); ++ checkAlias("Alg.Alias.TrustManagerFactory.X509", "PKIX"); ++ checkAlias("Alg.Alias.TrustManagerFactory.X.509", "PKIX"); ++ ++ checkService("SSLContext.TLSv1"); ++ checkService("SSLContext.TLSv1.1"); ++ checkService("SSLContext.TLSv1.2"); ++ checkService("SSLContext.TLSv1.3"); ++ checkService("SSLContext.TLS"); ++ if (fips == false) { ++ checkAlias("Alg.Alias.SSLContext.SSL", "TLS"); ++ checkAlias("Alg.Alias.SSLContext.SSLv3", "TLSv1"); ++ } ++ ++ checkService("SSLContext.Default"); ++ ++ /* ++ * KeyStore ++ */ ++ checkService("KeyStore.PKCS12"); ++ System.out.println("SunJSSE check passed"); ++ return true; ++ } ++} +diff --git a/jdk/test/java/security/Provider/SunRsaSignValidator.java b/jdk/test/java/security/Provider/SunRsaSignValidator.java +new file mode 100644 +index 000000000..66fb33a44 +--- /dev/null ++++ b/jdk/test/java/security/Provider/SunRsaSignValidator.java +@@ -0,0 +1,154 @@ ++/* ++ * Copyright (c) 2022, Huawei Technologies Co., Ltd. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. Huawei designates this ++ * particular file as subject to the "Classpath" exception as provided ++ * by Huawei in the LICENSE file that accompanied this code. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please visit https://gitee.com/openeuler/bishengjdk-8 if you need additional ++ * information or have any questions. ++ */ ++ ++/* ++ * @test ++ * @bug 7092821 ++ * @library ../testlibrary ++ * @summary make sure that Sun providers do not miss any algorithms after ++ * modifying the frameworks underneath ++ * @author Henry Yang ++ */ ++ ++/* ++ *- @TestCaseID:Provider/SunRsaSignValidator.java ++ *- @TestCaseName:Provider/SunRsaSignValidator.java ++ *- @TestCaseType:Function test ++ *- @RequirementID:AR.SR.IREQ02758058.001.001 ++ *- @RequirementName: java.security.Provider.getService() is synchronized and became scalability bottleneck ++ *- @Condition:JDK8u302及以后 ++ *- @Brief:测试相应provider更改底层架构以后所提供的service是否与原先有差异(以openJDK8u302为准) ++ * -#step:比较openJDK8u302 SunRsaSignProvider与此特性修改后的SunRsaSignProvider所提供的service是否一致 ++ *- @Expect:正常运行 ++ *- @Priority:Level 1 ++ */ ++ ++import sun.security.rsa.SunRsaSign; ++ ++import java.security.Provider; ++ ++/** ++ * validator for SunRsaSign provider, make sure we do not miss any algorithm ++ * after the modification. ++ * ++ * @author Henry Yang ++ * @since 2022-05-05 ++ */ ++public class SunRsaSignValidator extends BaseProviderValidator { ++ public static void main(String[] args) throws Exception { ++ SunRsaSignValidator validator = new SunRsaSignValidator(); ++ validator.validate(); ++ } ++ ++ @Override ++ Provider getDefaultProvider() { ++ return new SunRsaSign(); ++ } ++ ++ @Override ++ boolean validate() throws Exception { ++ // main algorithms ++ checkService("KeyFactory.RSA"); ++ checkService("KeyPairGenerator.RSA"); ++ checkService("Signature.MD2withRSA"); ++ checkService("Signature.MD5withRSA"); ++ checkService("Signature.SHA1withRSA"); ++ checkService("Signature.SHA224withRSA"); ++ checkService("Signature.SHA256withRSA"); ++ checkService("Signature.SHA384withRSA"); ++ checkService("Signature.SHA512withRSA"); ++ checkService("Signature.SHA512/224withRSA"); ++ checkService("Signature.SHA512/256withRSA"); ++ ++ checkService("KeyFactory.RSASSA-PSS"); ++ checkService("KeyPairGenerator.RSASSA-PSS"); ++ checkService("Signature.RSASSA-PSS"); ++ checkService("AlgorithmParameters.RSASSA-PSS"); ++ ++ System.out.println("service check passed"); ++ ++ // attributes for supported key classes ++ String rsaKeyClasses = "java.security.interfaces.RSAPublicKey" + "|java.security.interfaces.RSAPrivateKey"; ++ checkAttribute("Signature.MD2withRSA SupportedKeyClasses", rsaKeyClasses); ++ checkAttribute("Signature.MD5withRSA SupportedKeyClasses", rsaKeyClasses); ++ checkAttribute("Signature.SHA1withRSA SupportedKeyClasses", rsaKeyClasses); ++ checkAttribute("Signature.SHA224withRSA SupportedKeyClasses", rsaKeyClasses); ++ checkAttribute("Signature.SHA256withRSA SupportedKeyClasses", rsaKeyClasses); ++ checkAttribute("Signature.SHA384withRSA SupportedKeyClasses", rsaKeyClasses); ++ checkAttribute("Signature.SHA512withRSA SupportedKeyClasses", rsaKeyClasses); ++ checkAttribute("Signature.SHA512/224withRSA SupportedKeyClasses", rsaKeyClasses); ++ checkAttribute("Signature.SHA512/256withRSA SupportedKeyClasses", rsaKeyClasses); ++ checkAttribute("Signature.RSASSA-PSS SupportedKeyClasses", rsaKeyClasses); ++ ++ System.out.println("attribute check passed"); ++ ++ // aliases ++ checkAlias("Alg.Alias.KeyFactory.1.2.840.113549.1.1", "RSA"); ++ checkAlias("Alg.Alias.KeyFactory.OID.1.2.840.113549.1.1", "RSA"); ++ ++ checkAlias("Alg.Alias.KeyPairGenerator.1.2.840.113549.1.1", "RSA"); ++ checkAlias("Alg.Alias.KeyPairGenerator.OID.1.2.840.113549.1.1", "RSA"); ++ ++ checkAlias("Alg.Alias.Signature.1.2.840.113549.1.1.2", "MD2withRSA"); ++ checkAlias("Alg.Alias.Signature.OID.1.2.840.113549.1.1.2", "MD2withRSA"); ++ ++ checkAlias("Alg.Alias.Signature.1.2.840.113549.1.1.4", "MD5withRSA"); ++ checkAlias("Alg.Alias.Signature.OID.1.2.840.113549.1.1.4", "MD5withRSA"); ++ ++ checkAlias("Alg.Alias.Signature.1.2.840.113549.1.1.5", "SHA1withRSA"); ++ checkAlias("Alg.Alias.Signature.OID.1.2.840.113549.1.1.5", "SHA1withRSA"); ++ checkAlias("Alg.Alias.Signature.1.3.14.3.2.29", "SHA1withRSA"); ++ ++ checkAlias("Alg.Alias.Signature.1.2.840.113549.1.1.14", "SHA224withRSA"); ++ checkAlias("Alg.Alias.Signature.OID.1.2.840.113549.1.1.14", "SHA224withRSA"); ++ ++ checkAlias("Alg.Alias.Signature.1.2.840.113549.1.1.11", "SHA256withRSA"); ++ checkAlias("Alg.Alias.Signature.OID.1.2.840.113549.1.1.11", "SHA256withRSA"); ++ ++ checkAlias("Alg.Alias.Signature.1.2.840.113549.1.1.12", "SHA384withRSA"); ++ checkAlias("Alg.Alias.Signature.OID.1.2.840.113549.1.1.12", "SHA384withRSA"); ++ ++ checkAlias("Alg.Alias.Signature.1.2.840.113549.1.1.13", "SHA512withRSA"); ++ checkAlias("Alg.Alias.Signature.OID.1.2.840.113549.1.1.13", "SHA512withRSA"); ++ checkAlias("Alg.Alias.Signature.1.2.840.113549.1.1.15", "SHA512/224withRSA"); ++ checkAlias("Alg.Alias.Signature.OID.1.2.840.113549.1.1.15", "SHA512/224withRSA"); ++ checkAlias("Alg.Alias.Signature.1.2.840.113549.1.1.16", "SHA512/256withRSA"); ++ checkAlias("Alg.Alias.Signature.OID.1.2.840.113549.1.1.16", "SHA512/256withRSA"); ++ ++ checkAlias("Alg.Alias.KeyFactory.1.2.840.113549.1.1.10", "RSASSA-PSS"); ++ checkAlias("Alg.Alias.KeyFactory.OID.1.2.840.113549.1.1.10", "RSASSA-PSS"); ++ ++ checkAlias("Alg.Alias.KeyPairGenerator.1.2.840.113549.1.1.10", "RSASSA-PSS"); ++ checkAlias("Alg.Alias.KeyPairGenerator.OID.1.2.840.113549.1.1.10", "RSASSA-PSS"); ++ ++ checkAlias("Alg.Alias.Signature.1.2.840.113549.1.1.10", "RSASSA-PSS"); ++ checkAlias("Alg.Alias.Signature.OID.1.2.840.113549.1.1.10", "RSASSA-PSS"); ++ ++ checkAlias("Alg.Alias.AlgorithmParameters.1.2.840.113549.1.1.10", "RSASSA-PSS"); ++ checkAlias("Alg.Alias.AlgorithmParameters.OID.1.2.840.113549.1.1.10", "RSASSA-PSS"); ++ ++ System.out.println("check alias passed"); ++ return true; ++ } ++} +diff --git a/jdk/test/java/security/Provider/SunValidator.java b/jdk/test/java/security/Provider/SunValidator.java +new file mode 100644 +index 000000000..3f4b81222 +--- /dev/null ++++ b/jdk/test/java/security/Provider/SunValidator.java +@@ -0,0 +1,263 @@ ++/* ++ * Copyright (c) 2022, Huawei Technologies Co., Ltd. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. Huawei designates this ++ * particular file as subject to the "Classpath" exception as provided ++ * by Huawei in the LICENSE file that accompanied this code. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please visit https://gitee.com/openeuler/bishengjdk-8 if you need additional ++ * information or have any questions. ++ */ ++ ++/* ++ * @test ++ * @bug 7092821 ++ * @library ../testlibrary ++ * @summary make sure that Sun providers do not miss any algorithms after ++ * modifying the frameworks underneath ++ * @author Henry Yang ++ */ ++ ++/* ++ *- @TestCaseID:Provider/SunValidator.java ++ *- @TestCaseName:Provider/SunValidator.java ++ *- @TestCaseType:Function test ++ *- @RequirementID:AR.SR.IREQ02758058.001.001 ++ *- @RequirementName: java.security.Provider.getService() is synchronized and became scalability bottleneck ++ *- @Condition:JDK8u302及以后 ++ *- @Brief:测试相应provider更改底层架构以后所提供的service是否与原先有差异(以openJDK8u302为准) ++ * -#step:比较openJDK8u302 SunProvider与此特性修改后的SunProvider所提供的service是否一致 ++ *- @Expect:正常运行 ++ *- @Priority:Level 1 ++ */ ++ ++import sun.security.provider.NativePRNG; ++import sun.security.provider.Sun; ++ ++import java.lang.reflect.Method; ++import java.security.Provider; ++ ++/** ++ * validator for Sun provider, make sure we do not miss any algorithm ++ * after the modification. ++ * ++ * @author Henry Yang ++ * @since 2022-05-05 ++ */ ++public class SunValidator extends BaseProviderValidator { ++ public static void main(String[] args) throws Exception { ++ SunValidator validator = new SunValidator(); ++ validator.validate(); ++ } ++ ++ @Override ++ Provider getDefaultProvider() { ++ return new Sun(); ++ } ++ ++ @Override ++ public boolean validate() throws Exception { ++ Method nativeAvailableMethod = NativePRNG.class.getDeclaredMethod("isAvailable"); ++ nativeAvailableMethod.setAccessible(true); ++ boolean nativeAvailable = (Boolean) nativeAvailableMethod.invoke(null); ++ if (nativeAvailable) { ++ checkService("SecureRandom.NativePRNG"); ++ } ++ ++ checkService("SecureRandom.SHA1PRNG"); ++ ++ /* ++ * Signature engines ++ */ ++ checkService("Signature.SHA1withDSA"); ++ checkService("Signature.NONEwithDSA"); ++ checkAlias("Alg.Alias.Signature.RawDSA", "NONEwithDSA"); ++ checkService("Signature.SHA224withDSA"); ++ checkService("Signature.SHA256withDSA"); ++ ++ String dsaKeyClasses = "java.security.interfaces.DSAPublicKey" + "|java.security.interfaces.DSAPrivateKey"; ++ checkAttribute("Signature.SHA1withDSA SupportedKeyClasses", dsaKeyClasses); ++ checkAttribute("Signature.NONEwithDSA SupportedKeyClasses", dsaKeyClasses); ++ checkAttribute("Signature.SHA224withDSA SupportedKeyClasses", dsaKeyClasses); ++ checkAttribute("Signature.SHA256withDSA SupportedKeyClasses", dsaKeyClasses); ++ ++ checkAlias("Alg.Alias.Signature.DSA", "SHA1withDSA"); ++ checkAlias("Alg.Alias.Signature.DSS", "SHA1withDSA"); ++ checkAlias("Alg.Alias.Signature.SHA/DSA", "SHA1withDSA"); ++ checkAlias("Alg.Alias.Signature.SHA-1/DSA", "SHA1withDSA"); ++ checkAlias("Alg.Alias.Signature.SHA1/DSA", "SHA1withDSA"); ++ checkAlias("Alg.Alias.Signature.SHAwithDSA", "SHA1withDSA"); ++ checkAlias("Alg.Alias.Signature.DSAWithSHA1", "SHA1withDSA"); ++ checkAlias("Alg.Alias.Signature.OID.1.2.840.10040.4.3", "SHA1withDSA"); ++ checkAlias("Alg.Alias.Signature.1.2.840.10040.4.3", "SHA1withDSA"); ++ checkAlias("Alg.Alias.Signature.1.3.14.3.2.13", "SHA1withDSA"); ++ checkAlias("Alg.Alias.Signature.1.3.14.3.2.27", "SHA1withDSA"); ++ checkAlias("Alg.Alias.Signature.OID.2.16.840.1.101.3.4.3.1", "SHA224withDSA"); ++ checkAlias("Alg.Alias.Signature.2.16.840.1.101.3.4.3.1", "SHA224withDSA"); ++ checkAlias("Alg.Alias.Signature.OID.2.16.840.1.101.3.4.3.2", "SHA256withDSA"); ++ checkAlias("Alg.Alias.Signature.2.16.840.1.101.3.4.3.2", "SHA256withDSA"); ++ System.out.println("Signature engines check passed"); ++ ++ /* ++ * Key Pair Generator engines ++ */ ++ checkService("KeyPairGenerator.DSA"); ++ checkAlias("Alg.Alias.KeyPairGenerator.OID.1.2.840.10040.4.1", "DSA"); ++ checkAlias("Alg.Alias.KeyPairGenerator.1.2.840.10040.4.1", "DSA"); ++ checkAlias("Alg.Alias.KeyPairGenerator.1.3.14.3.2.12", "DSA"); ++ System.out.println("Key Pair Generator engines check passed"); ++ ++ /* ++ * Digest engines ++ */ ++ checkService("MessageDigest.MD2"); ++ checkService("MessageDigest.MD5"); ++ checkService("MessageDigest.SHA"); ++ ++ checkAlias("Alg.Alias.MessageDigest.SHA-1", "SHA"); ++ checkAlias("Alg.Alias.MessageDigest.SHA1", "SHA"); ++ checkAlias("Alg.Alias.MessageDigest.1.3.14.3.2.26", "SHA"); ++ checkAlias("Alg.Alias.MessageDigest.OID.1.3.14.3.2.26", "SHA"); ++ ++ checkService("MessageDigest.SHA-224"); ++ checkAlias("Alg.Alias.MessageDigest.2.16.840.1.101.3.4.2.4", "SHA-224"); ++ checkAlias("Alg.Alias.MessageDigest.OID.2.16.840.1.101.3.4.2.4", "SHA-224"); ++ ++ checkService("MessageDigest.SHA-256"); ++ checkAlias("Alg.Alias.MessageDigest.2.16.840.1.101.3.4.2.1", "SHA-256"); ++ checkAlias("Alg.Alias.MessageDigest.OID.2.16.840.1.101.3.4.2.1", "SHA-256"); ++ checkService("MessageDigest.SHA-384"); ++ checkAlias("Alg.Alias.MessageDigest.2.16.840.1.101.3.4.2.2", "SHA-384"); ++ checkAlias("Alg.Alias.MessageDigest.OID.2.16.840.1.101.3.4.2.2", "SHA-384"); ++ checkService("MessageDigest.SHA-512"); ++ checkAlias("Alg.Alias.MessageDigest.2.16.840.1.101.3.4.2.3", "SHA-512"); ++ checkAlias("Alg.Alias.MessageDigest.OID.2.16.840.1.101.3.4.2.3", "SHA-512"); ++ checkService("MessageDigest.SHA-512/224"); ++ checkAlias("Alg.Alias.MessageDigest.2.16.840.1.101.3.4.2.5", "SHA-512/224"); ++ checkAlias("Alg.Alias.MessageDigest.OID.2.16.840.1.101.3.4.2.5", "SHA-512/224"); ++ checkService("MessageDigest.SHA-512/256"); ++ checkAlias("Alg.Alias.MessageDigest.2.16.840.1.101.3.4.2.6", "SHA-512/256"); ++ checkAlias("Alg.Alias.MessageDigest.OID.2.16.840.1.101.3.4.2.6", "SHA-512/256"); ++ System.out.println("Digest engines check passed"); ++ ++ /* ++ * Algorithm Parameter Generator engines ++ */ ++ checkService("AlgorithmParameterGenerator.DSA"); ++ System.out.println("Algorithm Parameter Generator engines check passed"); ++ ++ /* ++ * Algorithm Parameter engines ++ */ ++ checkService("AlgorithmParameters.DSA"); ++ checkAlias("Alg.Alias.AlgorithmParameters.OID.1.2.840.10040.4.1", "DSA"); ++ checkAlias("Alg.Alias.AlgorithmParameters.1.2.840.10040.4.1", "DSA"); ++ checkAlias("Alg.Alias.AlgorithmParameters.1.3.14.3.2.12", "DSA"); ++ System.out.println("Algorithm Parameter engines check passed"); ++ ++ /* ++ * Key factories ++ */ ++ checkService("KeyFactory.DSA"); ++ checkAlias("Alg.Alias.KeyFactory.OID.1.2.840.10040.4.1", "DSA"); ++ checkAlias("Alg.Alias.KeyFactory.1.2.840.10040.4.1", "DSA"); ++ checkAlias("Alg.Alias.KeyFactory.1.3.14.3.2.12", "DSA"); ++ System.out.println("Key factories check passed"); ++ ++ /* ++ * Certificates ++ */ ++ checkService("CertificateFactory.X.509"); ++ checkAlias("Alg.Alias.CertificateFactory.X509", "X.509"); ++ System.out.println("Certificates check passed"); ++ ++ /* ++ * KeyStore ++ */ ++ checkService("KeyStore.JKS"); ++ checkService("KeyStore.CaseExactJKS"); ++ checkService("KeyStore.DKS"); ++ System.out.println("KeyStore check passed"); ++ ++ /* ++ * Policy ++ */ ++ checkService("Policy.JavaPolicy"); ++ System.out.println("Policy check passed"); ++ ++ /* ++ * Configuration ++ */ ++ checkService("Configuration.JavaLoginConfig"); ++ System.out.println("Configuration check passed"); ++ ++ /* ++ * CertPathBuilder ++ */ ++ checkService("CertPathBuilder.PKIX"); ++ checkAttribute("CertPathBuilder.PKIX ValidationAlgorithm", "RFC5280"); ++ System.out.println("CertPathBuilder check passed"); ++ ++ /* ++ * CertPathValidator ++ */ ++ checkService("CertPathValidator.PKIX"); ++ checkAttribute("CertPathValidator.PKIX ValidationAlgorithm", "RFC5280"); ++ System.out.println("CertPathValidator check passed"); ++ ++ /* ++ * CertStores ++ */ ++ checkService("CertStore.LDAP"); ++ checkAttribute("CertStore.LDAP LDAPSchema", "RFC2587"); ++ checkService("CertStore.Collection"); ++ checkService("CertStore.com.sun.security.IndexedCollection"); ++ System.out.println("CertStores check passed"); ++ ++ /* ++ * KeySize ++ */ ++ checkAttribute("Signature.NONEwithDSA KeySize", "1024"); ++ checkAttribute("Signature.SHA1withDSA KeySize", "1024"); ++ checkAttribute("Signature.SHA224withDSA KeySize", "2048"); ++ checkAttribute("Signature.SHA256withDSA KeySize", "2048"); ++ ++ checkAttribute("KeyPairGenerator.DSA KeySize", "2048"); ++ checkAttribute("AlgorithmParameterGenerator.DSA KeySize", "2048"); ++ System.out.println("KeySize attribute check passed"); ++ ++ /* ++ * Implementation type: software or hardware ++ */ ++ checkAttribute("Signature.SHA1withDSA ImplementedIn", "Software"); ++ checkAttribute("KeyPairGenerator.DSA ImplementedIn", "Software"); ++ checkAttribute("MessageDigest.MD5 ImplementedIn", "Software"); ++ checkAttribute("MessageDigest.SHA ImplementedIn", "Software"); ++ checkAttribute("AlgorithmParameterGenerator.DSA ImplementedIn", "Software"); ++ checkAttribute("AlgorithmParameters.DSA ImplementedIn", "Software"); ++ checkAttribute("KeyFactory.DSA ImplementedIn", "Software"); ++ checkAttribute("SecureRandom.SHA1PRNG ImplementedIn", "Software"); ++ checkAttribute("CertificateFactory.X.509 ImplementedIn", "Software"); ++ checkAttribute("KeyStore.JKS ImplementedIn", "Software"); ++ checkAttribute("CertPathValidator.PKIX ImplementedIn", "Software"); ++ checkAttribute("CertPathBuilder.PKIX ImplementedIn", "Software"); ++ checkAttribute("CertStore.LDAP ImplementedIn", "Software"); ++ checkAttribute("CertStore.Collection ImplementedIn", "Software"); ++ checkAttribute("CertStore.com.sun.security.IndexedCollection ImplementedIn", "Software"); ++ System.out.println("Implementation type attribute check passed"); ++ return true; ++ } ++} +diff --git a/jdk/test/java/security/SecureRandom/DefaultAlgo.java b/jdk/test/java/security/SecureRandom/DefaultAlgo.java +new file mode 100644 +index 000000000..ce786f7a2 +--- /dev/null ++++ b/jdk/test/java/security/SecureRandom/DefaultAlgo.java +@@ -0,0 +1,117 @@ ++/* ++ * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ */ ++ ++import static java.lang.System.out; ++import java.security.Provider; ++import java.security.Security; ++import java.security.SecureRandom; ++import java.security.Provider.Service; ++import java.util.Objects; ++import java.util.Arrays; ++import sun.security.provider.SunEntries; ++ ++/** ++ * @test ++ * @bug 8228613 ++ * @summary Ensure that the default SecureRandom algo used is based ++ * on the registration ordering, and falls to next provider ++ * if none are found ++ * @modules java.base/sun.security.provider ++ */ ++public class DefaultAlgo { ++ ++ public static void main(String[] args) throws Exception { ++ String[] algos = { "A", "B", "C" }; ++ test3rdParty(algos); ++ // reverse the order and re-check ++ String[] algosReversed = { "C", "B", "A" }; ++ test3rdParty(algosReversed); ++ } ++ ++ private static void test3rdParty(String[] algos) { ++ Provider[] provs = { ++ new SampleLegacyProvider(algos), ++ new SampleServiceProvider(algos) ++ }; ++ for (Provider p : provs) { ++ checkDefault(p, algos); ++ } ++ } ++ ++ // validate the specified SecureRandom obj to be from the specified ++ // provider and matches the specified algorithm ++ private static void validate(SecureRandom sr, String pName, String algo) { ++ if (!sr.getProvider().getName().equals(pName)) { ++ throw new RuntimeException("Failed provider check, exp: " + ++ pName + ", got " + sr.getProvider().getName()); ++ } ++ if (!sr.getAlgorithm().equals(algo)) { ++ throw new RuntimeException("Failed algo check, exp: " + ++ algo + ", got " + sr.getAlgorithm()); ++ } ++ } ++ ++ private static void checkDefault(Provider p, String ... algos) { ++ out.println(p.getName() + " with " + Arrays.toString(algos)); ++ int pos = Security.insertProviderAt(p, 1); ++ String pName = p.getName(); ++ boolean isLegacy = pName.equals("SampleLegacy"); ++ try { ++ if (isLegacy) { ++ for (String s : algos) { ++ validate(new SecureRandom(), pName, s); ++ p.remove("SecureRandom." + s); ++ out.println("removed " + s); ++ } ++ validate(new SecureRandom(), "SUN", ++ SunEntries.DEF_SECURE_RANDOM_ALGO); ++ } else { ++ validate(new SecureRandom(), pName, algos[0]); ++ } ++ out.println("=> Test Passed"); ++ } finally { ++ if (pos != -1) { ++ Security.removeProvider(p.getName()); ++ } ++ } ++ } ++ ++ private static class SampleLegacyProvider extends Provider { ++ SampleLegacyProvider(String[] listOfSupportedRNGs) { ++ super("SampleLegacy", 1.0, "test provider using legacy put"); ++ for (String s : listOfSupportedRNGs) { ++ put("SecureRandom." + s, "sun.security.provider.SecureRandom"); ++ } ++ } ++ } ++ ++ private static class SampleServiceProvider extends Provider { ++ SampleServiceProvider(String[] listOfSupportedRNGs) { ++ super("SampleService", 1.0, "test provider using putService"); ++ for (String s : listOfSupportedRNGs) { ++ putService(new Provider.Service(this, "SecureRandom", s, ++ "sun.security.provider.SecureRandom", null, null)); ++ } ++ } ++ } ++} +\ No newline at end of file +diff --git a/jdk/test/micro/org/openeuler/bench/security/provider/GetServiceBenchmark.java b/jdk/test/micro/org/openeuler/bench/security/provider/GetServiceBenchmark.java +new file mode 100644 +index 000000000..93cd887d6 +--- /dev/null ++++ b/jdk/test/micro/org/openeuler/bench/security/provider/GetServiceBenchmark.java +@@ -0,0 +1,83 @@ ++/* ++ * Copyright (c) 2022, Huawei Technologies Co., Ltd. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. Huawei designates this ++ * particular file as subject to the "Classpath" exception as provided ++ * by Huawei in the LICENSE file that accompanied this code. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please visit https://gitee.com/openeuler/bishengjdk-8 if you need additional ++ * information or have any questions. ++ */ ++ ++/* ++ * - @TestCaseID:provider/GetServiceBenchmark.java ++ * - @TestCaseName:provider/GetServiceBenchmark.java ++ * - @TestCaseType:Performance test ++ * - @RequirementID:AR.SR.IREQ02758058.001.001 ++ * - @RequirementName:java.security.Provider.getService() is synchronized and became scalability bottleneck ++ * - @Condition:JDK8u302及以后 ++ * - @Brief:测试provider.getService的性能 ++ * -#step:创建jmh的maven项目mvn archetype:generate -DinteractiveMode=false -DarchetypeGroupId=org.openjdk.jmh -DarchetypeArtifactId=jmh-java-benchmark-archetype -DgroupId=org.openeuler.bench.security.provider -DartifactId=provider-benchmark -Dversion=1.0 ++ * -#step2:删除项目中的多余文件rm -rf provider-benchmark/src/main/java/org/openeuler/bench/security/provider/MyBenchmark.java ++ * -#step3:将本文件拷贝进项目目录cp GetServiceBenchmark.java provider-benchmark/src/main/java/org/openeuler/bench/security/provider/ ++ * -#step4:构建项目mvn install ++ * -#step5:运行测试java -jar target/benchmarks.jar GetServiceBenchmark ++ * - @Expect:正常运行 ++ * - @Priority:Level 1 ++ */ ++ ++package org.openeuler.bench.security.provider; ++ ++import com.sun.crypto.provider.SunJCE; ++ ++import org.openjdk.jmh.annotations.Benchmark; ++import org.openjdk.jmh.annotations.BenchmarkMode; ++import org.openjdk.jmh.annotations.Fork; ++import org.openjdk.jmh.annotations.Measurement; ++import org.openjdk.jmh.annotations.Mode; ++import org.openjdk.jmh.annotations.Scope; ++import org.openjdk.jmh.annotations.State; ++import org.openjdk.jmh.annotations.Threads; ++import org.openjdk.jmh.annotations.Warmup; ++ ++import java.security.Provider; ++import java.util.concurrent.TimeUnit; ++ ++/** ++ * Benchmark to test the performance of provider.getService in ++ * high concurrency scenarios. ++ * ++ * @author Henry Yang ++ * @since 2022-05-05 ++ */ ++@BenchmarkMode(Mode.Throughput) ++@Fork(1) ++@Threads(2000) ++@Warmup(iterations = 3, time = 3, timeUnit = TimeUnit.SECONDS) ++@Measurement(iterations = 5, time = 3, timeUnit = TimeUnit.SECONDS) ++@State(Scope.Benchmark) ++public class GetServiceBenchmark { ++ private Provider provider = new SunJCE(); ++ ++ @Benchmark ++ public void getService() { ++ try { ++ provider.getService("Cipher", "RSA"); ++ } catch (Exception e) { ++ e.printStackTrace(); ++ } ++ } ++} +-- +2.22.0 + diff --git a/8065402-G1-does-not-expand-marking-stack-when-mark-s.patch b/8065402-G1-does-not-expand-marking-stack-when-mark-s.patch new file mode 100644 index 0000000000000000000000000000000000000000..7bc0fdd7731b9220af6a96b4bec30292101b6077 --- /dev/null +++ b/8065402-G1-does-not-expand-marking-stack-when-mark-s.patch @@ -0,0 +1,96 @@ +From 21a76a7829958e0064051956d1d1f6ddb8b48650 Mon Sep 17 00:00:00 2001 +From: eapen +Date: Mon, 24 Oct 2022 14:54:04 +0800 +Subject: [PATCH 28/33] I68TO2: 8065402: G1 does not expand marking stack when mark + stack overflow happens during concurrent marking +--- + .../vm/gc_implementation/g1/concurrentMark.cpp | 22 ++++++---------------- + .../vm/gc_implementation/g1/concurrentMark.hpp | 4 ---- + 2 files changed, 6 insertions(+), 20 deletions(-) + +diff --git a/hotspot/src/share/vm/gc_implementation/g1/concurrentMark.cpp b/hotspot/src/share/vm/gc_implementation/g1/concurrentMark.cpp +index 831ec94..df901a5 100644 +--- a/hotspot/src/share/vm/gc_implementation/g1/concurrentMark.cpp ++++ b/hotspot/src/share/vm/gc_implementation/g1/concurrentMark.cpp +@@ -247,7 +247,6 @@ bool CMMarkStack::allocate(size_t capacity) { + setEmpty(); + _capacity = (jint) capacity; + _saved_index = -1; +- _should_expand = false; + NOT_PRODUCT(_max_depth = 0); + return true; + } +@@ -256,8 +255,6 @@ void CMMarkStack::expand() { + // Called, during remark, if we've overflown the marking stack during marking. + assert(isEmpty(), "stack should been emptied while handling overflow"); + assert(_capacity <= (jint) MarkStackSizeMax, "stack bigger than permitted"); +- // Clear expansion flag +- _should_expand = false; + if (_capacity == (jint) MarkStackSizeMax) { + if (PrintGCDetails && Verbose) { + gclog_or_tty->print_cr(" (benign) Can't expand marking stack capacity, at max size limit"); +@@ -290,13 +287,6 @@ void CMMarkStack::expand() { + } + } + +-void CMMarkStack::set_should_expand() { +- // If we're resetting the marking state because of an +- // marking stack overflow, record that we should, if +- // possible, expand the stack. +- _should_expand = _cm->has_overflown(); +-} +- + CMMarkStack::~CMMarkStack() { + if (_base != NULL) { + _base = NULL; +@@ -795,8 +785,13 @@ void ConcurrentMark::reset() { + + + void ConcurrentMark::reset_marking_state(bool clear_overflow) { +- _markStack.set_should_expand(); + _markStack.setEmpty(); // Also clears the _markStack overflow flag ++ ++ // Expand the marking stack, if we have to and if we can. ++ if (has_overflown()) { ++ _markStack.expand(); ++ } ++ + if (clear_overflow) { + clear_has_overflown(); + } else { +@@ -1367,11 +1362,6 @@ void ConcurrentMark::checkpointRootsFinal(bool clear_all_soft_refs) { + set_non_marking_state(); + } + +- // Expand the marking stack, if we have to and if we can. +- if (_markStack.should_expand()) { +- _markStack.expand(); +- } +- + // Statistics + double now = os::elapsedTime(); + _remark_mark_times.add((mark_work_end - start) * 1000.0); +diff --git a/hotspot/src/share/vm/gc_implementation/g1/concurrentMark.hpp b/hotspot/src/share/vm/gc_implementation/g1/concurrentMark.hpp +index f78b1cb..bbd5d59 100644 +--- a/hotspot/src/share/vm/gc_implementation/g1/concurrentMark.hpp ++++ b/hotspot/src/share/vm/gc_implementation/g1/concurrentMark.hpp +@@ -178,7 +178,6 @@ class CMMarkStack VALUE_OBJ_CLASS_SPEC { + NOT_PRODUCT(jint _max_depth;) // max depth plumbed during run + + bool _overflow; +- bool _should_expand; + DEBUG_ONLY(bool _drain_in_progress;) + DEBUG_ONLY(bool _drain_in_progress_yields;) + +@@ -255,9 +254,6 @@ class CMMarkStack VALUE_OBJ_CLASS_SPEC { + bool overflow() { return _overflow; } + void clear_overflow() { _overflow = false; } + +- bool should_expand() const { return _should_expand; } +- void set_should_expand(); +- + // Expand the stack, typically in response to an overflow condition + void expand(); + +-- +1.8.3.1 diff --git a/8065895-Synchronous-signals-during-error-reporting-may-terminate-or-hang-vm-process.patch b/8065895-Synchronous-signals-during-error-reporting-may-terminate-or-hang-vm-process.patch new file mode 100644 index 0000000000000000000000000000000000000000..2e5bc2595a55796498e848c8f53be00871148ae3 --- /dev/null +++ b/8065895-Synchronous-signals-during-error-reporting-may-terminate-or-hang-vm-process.patch @@ -0,0 +1,485 @@ +From 69371858fda3c1793faf8bbf116ec4fe554605ac Mon Sep 17 00:00:00 2001 +From: hedongbo +Date: Fri, 21 Oct 2022 15:16:04 +0800 +Subject: 8065895: Synchronous signals during error reporting may + terminate or hang VM process + +Bug url: https://bugs.openjdk.java.net/browse/JDK-8065895 +--- + hotspot/src/os/aix/vm/vmError_aix.cpp | 11 +++-- + hotspot/src/os/bsd/vm/vmError_bsd.cpp | 47 +++++++++++------- + hotspot/src/os/linux/vm/vmError_linux.cpp | 48 ++++++++++++------- + hotspot/src/os/solaris/vm/vmError_solaris.cpp | 48 ++++++++++++------- + hotspot/src/share/vm/runtime/globals.hpp | 4 ++ + hotspot/src/share/vm/utilities/debug.cpp | 48 ++++++++++++++++--- + hotspot/src/share/vm/utilities/debug.hpp | 18 +++++++ + hotspot/src/share/vm/utilities/vmError.cpp | 29 +++++++++++ + 8 files changed, 196 insertions(+), 57 deletions(-) + +diff --git a/hotspot/src/os/aix/vm/vmError_aix.cpp b/hotspot/src/os/aix/vm/vmError_aix.cpp +index d99436ebc..34709134a 100644 +--- a/hotspot/src/os/aix/vm/vmError_aix.cpp ++++ b/hotspot/src/os/aix/vm/vmError_aix.cpp +@@ -80,7 +80,6 @@ static void save_signal(int idx, int sig) { + } + + int VMError::get_resetted_sigflags(int sig) { +- // Handle all program errors. + for (int i = 0; i < NUM_SIGNALS; i++) { + if (SIGNALS[i] == sig) { + return resettedSigflags[i]; +@@ -90,7 +89,6 @@ int VMError::get_resetted_sigflags(int sig) { + } + + address VMError::get_resetted_sighandler(int sig) { +- // Handle all program errors. + for (int i = 0; i < NUM_SIGNALS; i++) { + if (SIGNALS[i] == sig) { + return resettedSighandler[i]; +@@ -100,12 +98,19 @@ address VMError::get_resetted_sighandler(int sig) { + } + + static void crash_handler(int sig, siginfo_t* info, void* ucVoid) { ++ + // Unmask current signal. + sigset_t newset; + sigemptyset(&newset); + sigaddset(&newset, sig); ++ // and all other synchronous signals too. ++ for (int i = 0; i < NUM_SIGNALS; i++) { ++ sigaddset(&newset, SIGNALS[i]); ++ } ++ sigthreadmask(SIG_UNBLOCK, &newset, NULL); + +- Unimplemented(); ++ VMError err(NULL, sig, NULL, info, ucVoid); ++ err.report_and_die(); + } + + void VMError::reset_signal_handlers() { +diff --git a/hotspot/src/os/bsd/vm/vmError_bsd.cpp b/hotspot/src/os/bsd/vm/vmError_bsd.cpp +index 8ec6ca04c..f09e1163f 100644 +--- a/hotspot/src/os/bsd/vm/vmError_bsd.cpp ++++ b/hotspot/src/os/bsd/vm/vmError_bsd.cpp +@@ -63,9 +63,15 @@ void VMError::show_message_box(char *buf, int buflen) { + } while (yes); + } + ++// handle all synchronous program error signals which may happen during error ++// reporting. They must be unblocked, caught, handled. ++ ++static const int SIGNALS[] = { SIGSEGV, SIGBUS, SIGILL, SIGFPE, SIGTRAP }; // add more if needed ++static const int NUM_SIGNALS = sizeof(SIGNALS) / sizeof(int); ++ + // Space for our "saved" signal flags and handlers +-static int resettedSigflags[2]; +-static address resettedSighandler[2]; ++static int resettedSigflags[NUM_SIGNALS]; ++static address resettedSighandler[NUM_SIGNALS]; + + static void save_signal(int idx, int sig) + { +@@ -78,19 +84,19 @@ static void save_signal(int idx, int sig) + } + + int VMError::get_resetted_sigflags(int sig) { +- if(SIGSEGV == sig) { +- return resettedSigflags[0]; +- } else if(SIGBUS == sig) { +- return resettedSigflags[1]; ++ for (int i = 0; i < NUM_SIGNALS; i++) { ++ if (SIGNALS[i] == sig) { ++ return resettedSigflags[i]; ++ } + } + return -1; + } + + address VMError::get_resetted_sighandler(int sig) { +- if(SIGSEGV == sig) { +- return resettedSighandler[0]; +- } else if(SIGBUS == sig) { +- return resettedSighandler[1]; ++ for (int i = 0; i < NUM_SIGNALS; i++) { ++ if (SIGNALS[i] == sig) { ++ return resettedSighandler[i]; ++ } + } + return NULL; + } +@@ -100,16 +106,25 @@ static void crash_handler(int sig, siginfo_t* info, void* ucVoid) { + sigset_t newset; + sigemptyset(&newset); + sigaddset(&newset, sig); +- sigprocmask(SIG_UNBLOCK, &newset, NULL); ++ // also unmask other synchronous signals ++ for (int i = 0; i < NUM_SIGNALS; i++) { ++ sigaddset(&newset, SIGNALS[i]); ++ } ++ pthread_sigmask(SIG_UNBLOCK, &newset, NULL); + + VMError err(NULL, sig, NULL, info, ucVoid); + err.report_and_die(); + } + + void VMError::reset_signal_handlers() { +- // Save sigflags for resetted signals +- save_signal(0, SIGSEGV); +- save_signal(1, SIGBUS); +- os::signal(SIGSEGV, CAST_FROM_FN_PTR(void *, crash_handler)); +- os::signal(SIGBUS, CAST_FROM_FN_PTR(void *, crash_handler)); ++ // install signal handlers for all synchronous program error signals ++ sigset_t newset; ++ sigemptyset(&newset); ++ ++ for (int i = 0; i < NUM_SIGNALS; i++) { ++ save_signal(i, SIGNALS[i]); ++ os::signal(SIGNALS[i], CAST_FROM_FN_PTR(void *, crash_handler)); ++ sigaddset(&newset, SIGNALS[i]); ++ } ++ pthread_sigmask(SIG_UNBLOCK, &newset, NULL); + } +diff --git a/hotspot/src/os/linux/vm/vmError_linux.cpp b/hotspot/src/os/linux/vm/vmError_linux.cpp +index 378c9a6ab..fca239c7e 100644 +--- a/hotspot/src/os/linux/vm/vmError_linux.cpp ++++ b/hotspot/src/os/linux/vm/vmError_linux.cpp +@@ -63,9 +63,15 @@ void VMError::show_message_box(char *buf, int buflen) { + } while (yes); + } + ++// handle all synchronous program error signals which may happen during error ++// reporting. They must be unblocked, caught, handled. ++ ++static const int SIGNALS[] = { SIGSEGV, SIGBUS, SIGILL, SIGFPE, SIGTRAP }; // add more if needed ++static const int NUM_SIGNALS = sizeof(SIGNALS) / sizeof(int); ++ + // Space for our "saved" signal flags and handlers +-static int resettedSigflags[2]; +-static address resettedSighandler[2]; ++static int resettedSigflags[NUM_SIGNALS]; ++static address resettedSighandler[NUM_SIGNALS]; + + static void save_signal(int idx, int sig) + { +@@ -78,19 +84,19 @@ static void save_signal(int idx, int sig) + } + + int VMError::get_resetted_sigflags(int sig) { +- if(SIGSEGV == sig) { +- return resettedSigflags[0]; +- } else if(SIGBUS == sig) { +- return resettedSigflags[1]; ++ for (int i = 0; i < NUM_SIGNALS; i++) { ++ if (SIGNALS[i] == sig) { ++ return resettedSigflags[i]; ++ } + } + return -1; + } + + address VMError::get_resetted_sighandler(int sig) { +- if(SIGSEGV == sig) { +- return resettedSighandler[0]; +- } else if(SIGBUS == sig) { +- return resettedSighandler[1]; ++ for (int i = 0; i < NUM_SIGNALS; i++) { ++ if (SIGNALS[i] == sig) { ++ return resettedSighandler[i]; ++ } + } + return NULL; + } +@@ -100,16 +106,26 @@ static void crash_handler(int sig, siginfo_t* info, void* ucVoid) { + sigset_t newset; + sigemptyset(&newset); + sigaddset(&newset, sig); +- sigprocmask(SIG_UNBLOCK, &newset, NULL); ++ // also unmask other synchronous signals ++ for (int i = 0; i < NUM_SIGNALS; i++) { ++ sigaddset(&newset, SIGNALS[i]); ++ } ++ pthread_sigmask(SIG_UNBLOCK, &newset, NULL); + + VMError err(NULL, sig, NULL, info, ucVoid); + err.report_and_die(); + } + + void VMError::reset_signal_handlers() { +- // Save sigflags for resetted signals +- save_signal(0, SIGSEGV); +- save_signal(1, SIGBUS); +- os::signal(SIGSEGV, CAST_FROM_FN_PTR(void *, crash_handler)); +- os::signal(SIGBUS, CAST_FROM_FN_PTR(void *, crash_handler)); ++ // install signal handlers for all synchronous program error signals ++ sigset_t newset; ++ sigemptyset(&newset); ++ ++ for (int i = 0; i < NUM_SIGNALS; i++) { ++ save_signal(i, SIGNALS[i]); ++ os::signal(SIGNALS[i], CAST_FROM_FN_PTR(void *, crash_handler)); ++ sigaddset(&newset, SIGNALS[i]); ++ } ++ pthread_sigmask(SIG_UNBLOCK, &newset, NULL); ++ + } +diff --git a/hotspot/src/os/solaris/vm/vmError_solaris.cpp b/hotspot/src/os/solaris/vm/vmError_solaris.cpp +index 6f3f5b06f..e24e5f6bf 100644 +--- a/hotspot/src/os/solaris/vm/vmError_solaris.cpp ++++ b/hotspot/src/os/solaris/vm/vmError_solaris.cpp +@@ -30,6 +30,7 @@ + + #include + #include ++#include + #include + + void VMError::show_message_box(char *buf, int buflen) { +@@ -59,9 +60,15 @@ void VMError::show_message_box(char *buf, int buflen) { + } while (yes); + } + ++// handle all synchronous program error signals which may happen during error ++// reporting. They must be unblocked, caught, handled. ++ ++static const int SIGNALS[] = { SIGSEGV, SIGBUS, SIGILL, SIGFPE, SIGTRAP }; // add more if needed ++static const int NUM_SIGNALS = sizeof(SIGNALS) / sizeof(int); ++ + // Space for our "saved" signal flags and handlers +-static int resettedSigflags[2]; +-static address resettedSighandler[2]; ++static int resettedSigflags[NUM_SIGNALS]; ++static address resettedSighandler[NUM_SIGNALS]; + + static void save_signal(int idx, int sig) + { +@@ -74,19 +81,19 @@ static void save_signal(int idx, int sig) + } + + int VMError::get_resetted_sigflags(int sig) { +- if(SIGSEGV == sig) { +- return resettedSigflags[0]; +- } else if(SIGBUS == sig) { +- return resettedSigflags[1]; ++ for (int i = 0; i < NUM_SIGNALS; i++) { ++ if (SIGNALS[i] == sig) { ++ return resettedSigflags[i]; ++ } + } + return -1; + } + + address VMError::get_resetted_sighandler(int sig) { +- if(SIGSEGV == sig) { +- return resettedSighandler[0]; +- } else if(SIGBUS == sig) { +- return resettedSighandler[1]; ++ for (int i = 0; i < NUM_SIGNALS; i++) { ++ if (SIGNALS[i] == sig) { ++ return resettedSighandler[i]; ++ } + } + return NULL; + } +@@ -96,16 +103,25 @@ static void crash_handler(int sig, siginfo_t* info, void* ucVoid) { + sigset_t newset; + sigemptyset(&newset); + sigaddset(&newset, sig); +- sigprocmask(SIG_UNBLOCK, &newset, NULL); ++ // also unmask other synchronous signals ++ for (int i = 0; i < NUM_SIGNALS; i++) { ++ sigaddset(&newset, SIGNALS[i]); ++ } ++ thr_sigsetmask(SIG_UNBLOCK, &newset, NULL); + + VMError err(NULL, sig, NULL, info, ucVoid); + err.report_and_die(); + } + + void VMError::reset_signal_handlers() { +- // Save sigflags for resetted signals +- save_signal(0, SIGSEGV); +- save_signal(1, SIGBUS); +- os::signal(SIGSEGV, CAST_FROM_FN_PTR(void *, crash_handler)); +- os::signal(SIGBUS, CAST_FROM_FN_PTR(void *, crash_handler)); ++ // install signal handlers for all synchronous program error signals ++ sigset_t newset; ++ sigemptyset(&newset); ++ ++ for (int i = 0; i < NUM_SIGNALS; i++) { ++ save_signal(i, SIGNALS[i]); ++ os::signal(SIGNALS[i], CAST_FROM_FN_PTR(void *, crash_handler)); ++ sigaddset(&newset, SIGNALS[i]); ++ } ++ thr_sigsetmask(SIG_UNBLOCK, &newset, NULL); + } +diff --git a/hotspot/src/share/vm/runtime/globals.hpp b/hotspot/src/share/vm/runtime/globals.hpp +index eb13ee0d7..0dab18e1a 100644 +--- a/hotspot/src/share/vm/runtime/globals.hpp ++++ b/hotspot/src/share/vm/runtime/globals.hpp +@@ -916,6 +916,10 @@ class CommandLineFlags { + "determines which error to provoke. See test_error_handler() " \ + "in debug.cpp.") \ + \ ++ notproduct(uintx, TestCrashInErrorHandler, 0, \ ++ "If > 0, provokes an error inside VM error handler (a secondary " \ ++ "crash). see test_error_handler() in debug.cpp.") \ ++ \ + develop(bool, Verbose, false, \ + "Print additional debugging information from other modes") \ + \ +diff --git a/hotspot/src/share/vm/utilities/debug.cpp b/hotspot/src/share/vm/utilities/debug.cpp +index 58a32a2b8..8cea16d32 100644 +--- a/hotspot/src/share/vm/utilities/debug.cpp ++++ b/hotspot/src/share/vm/utilities/debug.cpp +@@ -337,13 +337,47 @@ bool is_error_reported() { + #ifndef PRODUCT + #include + ++typedef void (*voidfun_t)(); ++// Crash with an authentic sigfpe ++static void crash_with_sigfpe() { ++ // generate a native synchronous SIGFPE where possible; ++ // if that did not cause a signal (e.g. on ppc), just ++ // raise the signal. ++ volatile int x = 0; ++ volatile int y = 1/x; ++#ifndef _WIN32 ++ raise(SIGFPE); ++#endif ++} // end: crash_with_sigfpe ++ ++// crash with sigsegv at non-null address. ++static void crash_with_segfault() { ++ ++ char* const crash_addr = (char*) get_segfault_address(); ++ *crash_addr = 'X'; ++ ++} // end: crash_with_segfault ++ ++// returns an address which is guaranteed to generate a SIGSEGV on read, ++// for test purposes, which is not NULL and contains bits in every word ++void* get_segfault_address() { ++ return (void*) ++#ifdef _LP64 ++ 0xABC0000000000ABCULL; ++#else ++ 0x00000ABC; ++#endif ++} ++ + void test_error_handler() { +- uintx test_num = ErrorHandlerTest; +- if (test_num == 0) return; ++ controlled_crash(ErrorHandlerTest); ++} ++ ++void controlled_crash(int how) { ++ if (how == 0) return; + + // If asserts are disabled, use the corresponding guarantee instead. +- size_t n = test_num; +- NOT_DEBUG(if (n <= 2) n += 2); ++ NOT_DEBUG(if (how <= 2) how += 2); + + const char* const str = "hello"; + const size_t num = (size_t)os::vm_page_size(); +@@ -354,7 +388,7 @@ void test_error_handler() { + const void (*funcPtr)(void) = (const void(*)()) 0xF; // bad function pointer + + // Keep this in sync with test/runtime/6888954/vmerrors.sh. +- switch (n) { ++ switch (how) { + case 1: assert(str == NULL, "expected null"); + case 2: assert(num == 1023 && *str == 'X', + err_msg("num=" SIZE_FORMAT " str=\"%s\"", num, str)); +@@ -379,8 +413,10 @@ void test_error_handler() { + // There's no guarantee the bad function pointer will crash us + // so "break" out to the ShouldNotReachHere(). + case 13: (*funcPtr)(); break; ++ case 14: crash_with_segfault(); break; ++ case 15: crash_with_sigfpe(); break; + +- default: tty->print_cr("ERROR: %d: unexpected test_num value.", n); ++ default: tty->print_cr("ERROR: %d: unexpected test_num value.", how); + } + ShouldNotReachHere(); + } +diff --git a/hotspot/src/share/vm/utilities/debug.hpp b/hotspot/src/share/vm/utilities/debug.hpp +index 3c3f8afe2..7a5e1523b 100644 +--- a/hotspot/src/share/vm/utilities/debug.hpp ++++ b/hotspot/src/share/vm/utilities/debug.hpp +@@ -266,6 +266,24 @@ void set_error_reported(); + /* Test assert(), fatal(), guarantee(), etc. */ + NOT_PRODUCT(void test_error_handler();) + ++// crash in a controlled way: ++// how can be one of: ++// 1,2 - asserts ++// 3,4 - guarantee ++// 5-7 - fatal ++// 8 - vm_exit_out_of_memory ++// 9 - ShouldNotCallThis ++// 10 - ShouldNotReachHere ++// 11 - Unimplemented ++// 12,13 - (not guaranteed) crashes ++// 14 - SIGSEGV ++// 15 - SIGFPE ++NOT_PRODUCT(void controlled_crash(int how);) ++ ++// returns an address which is guaranteed to generate a SIGSEGV on read, ++// for test purposes, which is not NULL and contains bits in every word ++NOT_PRODUCT(void* get_segfault_address();) ++ + void pd_ps(frame f); + void pd_obfuscate_location(char *buf, size_t buflen); + +diff --git a/hotspot/src/share/vm/utilities/vmError.cpp b/hotspot/src/share/vm/utilities/vmError.cpp +index aa0b63a80..9b40a3468 100644 +--- a/hotspot/src/share/vm/utilities/vmError.cpp ++++ b/hotspot/src/share/vm/utilities/vmError.cpp +@@ -397,6 +397,26 @@ void VMError::report(outputStream* st) { + "Runtime Environment to continue."); + } + ++#ifndef PRODUCT ++ // Error handler self tests ++ ++ // test secondary error handling. Test it twice, to test that resetting ++ // error handler after a secondary crash works. ++ STEP(13, "(test secondary crash 1)") ++ if (_verbose && TestCrashInErrorHandler != 0) { ++ st->print_cr("Will crash now (TestCrashInErrorHandler=%d)...", ++ TestCrashInErrorHandler); ++ controlled_crash(TestCrashInErrorHandler); ++ } ++ ++ STEP(14, "(test secondary crash 2)") ++ if (_verbose && TestCrashInErrorHandler != 0) { ++ st->print_cr("Will crash now (TestCrashInErrorHandler=%d)...", ++ TestCrashInErrorHandler); ++ controlled_crash(TestCrashInErrorHandler); ++ } ++#endif // PRODUCT ++ + STEP(15, "(printing type of error)") + + switch(static_cast(_id)) { +@@ -829,6 +849,15 @@ void VMError::report(outputStream* st) { + st->cr(); + } + ++#ifndef PRODUCT ++ // print a defined marker to show that error handling finished correctly. ++ STEP(290, "(printing end marker)" ) ++ ++ if (_verbose) { ++ st->print_cr("END."); ++ } ++#endif ++ + END + + # undef BEGIN +-- +2.22.0 + diff --git a/8067941-TESTBUG-Fix-tests-for-OS-with-64K-page-size.patch b/8067941-TESTBUG-Fix-tests-for-OS-with-64K-page-size.patch new file mode 100644 index 0000000000000000000000000000000000000000..3f428476463513b57725151b82347dda7ed036e3 --- /dev/null +++ b/8067941-TESTBUG-Fix-tests-for-OS-with-64K-page-size.patch @@ -0,0 +1,170 @@ +From c97998519552b7d8287125e46a3db2f29293784f Mon Sep 17 00:00:00 2001 +From: xiezhaokun +Date: Wed, 8 Jun 2022 10:32:52 +0800 +Subject: [PATCH 08/10] 8067941: [TESTBUG] Fix tests for OS with 64K page size + +--- + hotspot/src/share/vm/memory/metaspace.cpp | 8 +++++--- + hotspot/test/compiler/6865265/StackOverflowBug.java | 2 +- + hotspot/test/compiler/8009761/Test8009761.java | 2 +- + .../exceptions/TestRecursiveReplacedException.java | 2 +- + .../compiler/uncommontrap/StackOverflowGuardPagesOff.java | 2 +- + .../compiler/uncommontrap/TestStackBangMonitorOwned.java | 2 +- + hotspot/test/compiler/uncommontrap/TestStackBangRbp.java | 2 +- + hotspot/test/gc/arguments/TestMaxHeapSizeTools.java | 2 +- + hotspot/test/gc/g1/TestHumongousAllocInitialMark.java | 4 +++- + 9 files changed, 15 insertions(+), 11 deletions(-) + +diff --git a/hotspot/src/share/vm/memory/metaspace.cpp b/hotspot/src/share/vm/memory/metaspace.cpp +index 600bcfd1..2912f41b 100644 +--- a/hotspot/src/share/vm/memory/metaspace.cpp ++++ b/hotspot/src/share/vm/memory/metaspace.cpp +@@ -3937,11 +3937,13 @@ class TestVirtualSpaceNodeTest { + assert(cm.sum_free_chunks() == 2*MediumChunk, "sizes should add up"); + } + +- { // 4 pages of VSN is committed, some is used by chunks ++ const size_t page_chunks = 4 * (size_t)os::vm_page_size() / BytesPerWord; ++ // This doesn't work for systems with vm_page_size >= 16K. ++ if (page_chunks < MediumChunk) { ++ // 4 pages of VSN is committed, some is used by chunks + ChunkManager cm(SpecializedChunk, SmallChunk, MediumChunk); + VirtualSpaceNode vsn(vsn_test_size_bytes); +- const size_t page_chunks = 4 * (size_t)os::vm_page_size() / BytesPerWord; +- assert(page_chunks < MediumChunk, "Test expects medium chunks to be at least 4*page_size"); ++ + vsn.initialize(); + vsn.expand_by(page_chunks, page_chunks); + vsn.get_chunk_vs(SmallChunk); +diff --git a/hotspot/test/compiler/6865265/StackOverflowBug.java b/hotspot/test/compiler/6865265/StackOverflowBug.java +index 295a6b41..c5d0f3b6 100644 +--- a/hotspot/test/compiler/6865265/StackOverflowBug.java ++++ b/hotspot/test/compiler/6865265/StackOverflowBug.java +@@ -28,7 +28,7 @@ + * @summary JVM crashes with "missing exception handler" error + * @author volker.simonis@sap.com + * +- * @run main/othervm -XX:CompileThreshold=100 -Xbatch -Xss248k StackOverflowBug ++ * @run main/othervm -XX:CompileThreshold=100 -Xbatch -Xss512k StackOverflowBug + */ + + +diff --git a/hotspot/test/compiler/8009761/Test8009761.java b/hotspot/test/compiler/8009761/Test8009761.java +index 401458b6..b41f49fd 100644 +--- a/hotspot/test/compiler/8009761/Test8009761.java ++++ b/hotspot/test/compiler/8009761/Test8009761.java +@@ -25,7 +25,7 @@ + * @test + * @bug 8009761 + * @summary Deoptimization on sparc doesn't set Llast_SP correctly in the interpreter frames it creates +- * @run main/othervm -XX:CompileCommand=exclude,Test8009761::m2 -XX:-UseOnStackReplacement -XX:-BackgroundCompilation -Xss256K Test8009761 ++ * @run main/othervm -XX:CompileCommand=exclude,Test8009761::m2 -XX:-UseOnStackReplacement -XX:-BackgroundCompilation -Xss512K Test8009761 + * + */ + +diff --git a/hotspot/test/compiler/exceptions/TestRecursiveReplacedException.java b/hotspot/test/compiler/exceptions/TestRecursiveReplacedException.java +index 996d82a0..950ed18c 100644 +--- a/hotspot/test/compiler/exceptions/TestRecursiveReplacedException.java ++++ b/hotspot/test/compiler/exceptions/TestRecursiveReplacedException.java +@@ -25,7 +25,7 @@ + * @test + * @bug 8054224 + * @summary Recursive method compiled by C1 is unable to catch StackOverflowError +- * @run main/othervm -Xcomp -XX:CompileOnly=Test.run -XX:+TieredCompilation -XX:TieredStopAtLevel=2 -Xss256K TestRecursiveReplacedException ++ * @run main/othervm -Xcomp -XX:CompileOnly=Test.run -XX:+TieredCompilation -XX:TieredStopAtLevel=2 -Xss512K TestRecursiveReplacedException + * + */ + +diff --git a/hotspot/test/compiler/uncommontrap/StackOverflowGuardPagesOff.java b/hotspot/test/compiler/uncommontrap/StackOverflowGuardPagesOff.java +index 4ad409bb..835283c0 100644 +--- a/hotspot/test/compiler/uncommontrap/StackOverflowGuardPagesOff.java ++++ b/hotspot/test/compiler/uncommontrap/StackOverflowGuardPagesOff.java +@@ -25,7 +25,7 @@ + * @test + * @bug 8029383 + * @summary stack overflow if callee is marked for deoptimization causes crash +- * @run main/othervm -XX:TieredStopAtLevel=1 -XX:-BackgroundCompilation -XX:CompileCommand=dontinline,StackOverflowGuardPagesOff::m1 -XX:CompileCommand=exclude,StackOverflowGuardPagesOff::m2 -Xss256K -XX:-UseOnStackReplacement StackOverflowGuardPagesOff ++ * @run main/othervm -XX:TieredStopAtLevel=1 -XX:-BackgroundCompilation -XX:CompileCommand=dontinline,StackOverflowGuardPagesOff::m1 -XX:CompileCommand=exclude,StackOverflowGuardPagesOff::m2 -Xss512K -XX:-UseOnStackReplacement StackOverflowGuardPagesOff + * + */ + +diff --git a/hotspot/test/compiler/uncommontrap/TestStackBangMonitorOwned.java b/hotspot/test/compiler/uncommontrap/TestStackBangMonitorOwned.java +index 3d93d7d5..c07a995d 100644 +--- a/hotspot/test/compiler/uncommontrap/TestStackBangMonitorOwned.java ++++ b/hotspot/test/compiler/uncommontrap/TestStackBangMonitorOwned.java +@@ -25,7 +25,7 @@ + * @test + * @bug 8032410 + * @summary Stack overflow at deoptimization doesn't release owned monitors +- * @run main/othervm -XX:-BackgroundCompilation -XX:CompileCommand=dontinline,TestStackBangMonitorOwned::m1 -XX:CompileCommand=exclude,TestStackBangMonitorOwned::m2 -Xss256K -XX:-UseOnStackReplacement TestStackBangMonitorOwned ++ * @run main/othervm -XX:-BackgroundCompilation -XX:CompileCommand=dontinline,TestStackBangMonitorOwned::m1 -XX:CompileCommand=exclude,TestStackBangMonitorOwned::m2 -Xss512K -XX:-UseOnStackReplacement TestStackBangMonitorOwned + * + */ + public class TestStackBangMonitorOwned { +diff --git a/hotspot/test/compiler/uncommontrap/TestStackBangRbp.java b/hotspot/test/compiler/uncommontrap/TestStackBangRbp.java +index 38d4e206..9b96951a 100644 +--- a/hotspot/test/compiler/uncommontrap/TestStackBangRbp.java ++++ b/hotspot/test/compiler/uncommontrap/TestStackBangRbp.java +@@ -25,7 +25,7 @@ + * @test + * @bug 8028308 + * @summary rbp not restored when stack overflow is thrown from deopt/uncommon trap blobs +- * @run main/othervm -XX:-BackgroundCompilation -XX:CompileCommand=dontinline,TestStackBangRbp::m1 -XX:CompileCommand=exclude,TestStackBangRbp::m2 -Xss256K -XX:-UseOnStackReplacement TestStackBangRbp ++ * @run main/othervm -XX:-BackgroundCompilation -XX:CompileCommand=dontinline,TestStackBangRbp::m1 -XX:CompileCommand=exclude,TestStackBangRbp::m2 -Xss512K -XX:-UseOnStackReplacement TestStackBangRbp + * + */ + public class TestStackBangRbp { +diff --git a/hotspot/test/gc/arguments/TestMaxHeapSizeTools.java b/hotspot/test/gc/arguments/TestMaxHeapSizeTools.java +index b5859b5c..99ed508d 100644 +--- a/hotspot/test/gc/arguments/TestMaxHeapSizeTools.java ++++ b/hotspot/test/gc/arguments/TestMaxHeapSizeTools.java +@@ -112,7 +112,7 @@ class TestMaxHeapSizeTools { + } + + private static void checkInvalidMinInitialHeapCombinations(String gcflag) throws Exception { +- expectError(new String[] { gcflag, "-Xms8M", "-XX:InitialHeapSize=4M", "-version" }); ++ expectError(new String[] { gcflag, "-Xms64M", "-XX:InitialHeapSize=32M", "-version" }); + } + + private static void checkValidMinInitialHeapCombinations(String gcflag) throws Exception { +diff --git a/hotspot/test/gc/g1/TestHumongousAllocInitialMark.java b/hotspot/test/gc/g1/TestHumongousAllocInitialMark.java +index 473ce666..b6e5c3d6 100644 +--- a/hotspot/test/gc/g1/TestHumongousAllocInitialMark.java ++++ b/hotspot/test/gc/g1/TestHumongousAllocInitialMark.java +@@ -31,7 +31,9 @@ + import com.oracle.java.testlibrary.*; + + public class TestHumongousAllocInitialMark { +- private static final int heapSize = 200; // MB ++ // Heap sizes < 224 MB are increased to 224 MB if vm_page_size == 64K to ++ // fulfill alignment constraints. ++ private static final int heapSize = 224; // MB + private static final int heapRegionSize = 1; // MB + private static final int initiatingHeapOccupancyPercent = 50; // % + +diff --git a/hotspot/test/runtime/6929067/invoke.c b/hotspot/test/runtime/6929067/invoke.c +index 8dde2cd6..cf8014be 100644 +--- a/hotspot/test/runtime/6929067/invoke.c ++++ b/hotspot/test/runtime/6929067/invoke.c +@@ -68,7 +68,7 @@ floobydust (void *p) + int + main (int argc, const char** argv) + { +- options[0].optionString = "-Xss320k"; ++ options[0].optionString = "-Xss512k"; + + vm_args.version = JNI_VERSION_1_2; + vm_args.ignoreUnrecognized = JNI_TRUE; +diff --git a/hotspot/test/runtime/InitialThreadOverflow/invoke.cxx b/hotspot/test/runtime/InitialThreadOverflow/invoke.cxx +index 55213c0f..2bca88f1 100644 +--- a/hotspot/test/runtime/InitialThreadOverflow/invoke.cxx ++++ b/hotspot/test/runtime/InitialThreadOverflow/invoke.cxx +@@ -48,7 +48,7 @@ floobydust (void *p) { + int + main (int argc, const char** argv) { + JavaVMOption options[1]; +- options[0].optionString = (char*) "-Xss320k"; ++ options[0].optionString = (char*) "-Xss512k"; + + JavaVMInitArgs vm_args; + vm_args.version = JNI_VERSION_1_2; diff --git a/8140594-Various-minor-code-improvements-compiler.patch b/8140594-Various-minor-code-improvements-compiler.patch new file mode 100644 index 0000000000000000000000000000000000000000..9c9881bef30ed30a21a1671a375b125eba951f42 --- /dev/null +++ b/8140594-Various-minor-code-improvements-compiler.patch @@ -0,0 +1,498 @@ +From 39f5104db20a569e361c0300756e35ab7e6ac296 Mon Sep 17 00:00:00 2001 +From: eapen +Date: Fri, 16 Dec 2022 16:06:02 +0000 +Subject: [PATCH 30/33] I68TO2: 8140594: Various minor code improvements (compiler) +--- + hotspot/src/os/linux/vm/os_linux.cpp | 5 +- + hotspot/src/os/linux/vm/vmError_linux.cpp | 16 ++--- + hotspot/src/os_cpu/linux_x86/vm/os_linux_x86.cpp | 2 +- + hotspot/src/share/vm/adlc/formssel.cpp | 13 ++-- + hotspot/src/share/vm/c1/c1_LinearScan.cpp | 7 +- + hotspot/src/share/vm/classfile/classFileParser.cpp | 11 +-- + .../src/share/vm/classfile/systemDictionary.cpp | 6 +- + hotspot/src/share/vm/compiler/compileBroker.cpp | 84 +++++++--------------- + hotspot/src/share/vm/compiler/compileBroker.hpp | 11 +-- + hotspot/src/share/vm/compiler/compileLog.cpp | 3 +- + hotspot/src/share/vm/compiler/disassembler.cpp | 6 +- + hotspot/src/share/vm/oops/generateOopMap.cpp | 8 ++- + hotspot/src/share/vm/opto/block.cpp | 2 +- + hotspot/src/share/vm/opto/graphKit.cpp | 2 +- + hotspot/src/share/vm/opto/matcher.cpp | 9 ++- + hotspot/src/share/vm/runtime/relocator.cpp | 4 +- + 16 files changed, 84 insertions(+), 105 deletions(-) + +diff --git a/hotspot/src/os/linux/vm/os_linux.cpp b/hotspot/src/os/linux/vm/os_linux.cpp +index ab28ee3..b82352c 100644 +--- a/hotspot/src/os/linux/vm/os_linux.cpp ++++ b/hotspot/src/os/linux/vm/os_linux.cpp +@@ -2159,9 +2159,10 @@ static bool _print_ascii_file(const char* filename, outputStream* st) { + return false; + } + +- char buf[32]; ++ char buf[33]; + int bytes; +- while ((bytes = ::read(fd, buf, sizeof(buf))) > 0) { ++ buf[32] = '\0'; ++ while ((bytes = ::read(fd, buf, sizeof(buf) - 1)) > 0) { + st->print_raw(buf, bytes); + } + +diff --git a/hotspot/src/os/linux/vm/vmError_linux.cpp b/hotspot/src/os/linux/vm/vmError_linux.cpp +index fca239c..52ca40b 100644 +--- a/hotspot/src/os/linux/vm/vmError_linux.cpp ++++ b/hotspot/src/os/linux/vm/vmError_linux.cpp +@@ -42,19 +42,19 @@ void VMError::show_message_box(char *buf, int buflen) { + char *p = &buf[len]; + + jio_snprintf(p, buflen - len, +- "\n\n" +- "Do you want to debug the problem?\n\n" +- "To debug, run 'gdb /proc/%d/exe %d'; then switch to thread " UINTX_FORMAT " (" INTPTR_FORMAT ")\n" +- "Enter 'yes' to launch gdb automatically (PATH must include gdb)\n" +- "Otherwise, press RETURN to abort...", +- os::current_process_id(), os::current_process_id(), +- os::current_thread_id(), os::current_thread_id()); ++ "\n\n" ++ "Do you want to debug the problem?\n\n" ++ "To debug, run 'gdb /proc/%d/exe %d'; then switch to thread " UINTX_FORMAT " (" INTPTR_FORMAT ")\n" ++ "Enter 'yes' to launch gdb automatically (PATH must include gdb)\n" ++ "Otherwise, press RETURN to abort...", ++ os::current_process_id(), os::current_process_id(), ++ os::current_thread_id(), os::current_thread_id()); + + yes = os::message_box("Unexpected Error", buf); + + if (yes) { + // yes, user asked VM to launch debugger +- jio_snprintf(buf, buflen, "gdb /proc/%d/exe %d", ++ jio_snprintf(buf, sizeof(char)*buflen, "gdb /proc/%d/exe %d", + os::current_process_id(), os::current_process_id()); + + os::fork_and_exec(buf); +diff --git a/hotspot/src/os_cpu/linux_x86/vm/os_linux_x86.cpp b/hotspot/src/os_cpu/linux_x86/vm/os_linux_x86.cpp +index 4775dc8..fba3d28 100644 +--- a/hotspot/src/os_cpu/linux_x86/vm/os_linux_x86.cpp ++++ b/hotspot/src/os_cpu/linux_x86/vm/os_linux_x86.cpp +@@ -813,7 +813,7 @@ void os::print_context(outputStream *st, void *context) { + + intptr_t *sp = (intptr_t *)os::Linux::ucontext_get_sp(uc); + st->print_cr("Top of Stack: (sp=" PTR_FORMAT ")", sp); +- print_hex_dump(st, (address)sp, (address)(sp + 8*sizeof(intptr_t)), sizeof(intptr_t)); ++ print_hex_dump(st, (address)sp, (address)(sp + 8), sizeof(intptr_t)); + st->cr(); + + // Note: it may be unsafe to inspect memory near pc. For example, pc may +diff --git a/hotspot/src/share/vm/adlc/formssel.cpp b/hotspot/src/share/vm/adlc/formssel.cpp +index 23fa1bb..6d57ff2 100644 +--- a/hotspot/src/share/vm/adlc/formssel.cpp ++++ b/hotspot/src/share/vm/adlc/formssel.cpp +@@ -1496,7 +1496,8 @@ void MachNodeForm::output(FILE *fp) { + // twice, we need to check that the operands are pointer-eequivalent in + // the DFA during the labeling process. + Predicate *InstructForm::build_predicate() { +- char buf[1024], *s=buf; ++ const int buflen = 1024; ++ char buf[buflen], *s=buf; + Dict names(cmpstr,hashstr,Form::arena); // Map Names to counts + + MatchNode *mnode = +@@ -1505,12 +1506,12 @@ Predicate *InstructForm::build_predicate() { + + uint first = 1; + // Start with the predicate supplied in the .ad file. +- if( _predicate ) { +- if( first ) first=0; +- strcpy(s,"("); s += strlen(s); +- strcpy(s,_predicate->_pred); ++ if(_predicate) { ++ if(first) first = 0; ++ strcpy(s, "("); s += strlen(s); ++ strncpy(s, _predicate->_pred, buflen - strlen(s) - 1); + s += strlen(s); +- strcpy(s,")"); s += strlen(s); ++ strcpy(s, ")"); s += strlen(s); + } + for( DictI i(&names); i.test(); ++i ) { + uintptr_t cnt = (uintptr_t)i._value; +diff --git a/hotspot/src/share/vm/c1/c1_LinearScan.cpp b/hotspot/src/share/vm/c1/c1_LinearScan.cpp +index ec4a67e..d754aa9 100644 +--- a/hotspot/src/share/vm/c1/c1_LinearScan.cpp ++++ b/hotspot/src/share/vm/c1/c1_LinearScan.cpp +@@ -5484,7 +5484,7 @@ int LinearScanWalker::find_locked_double_reg(int reg_needed_until, int interval_ + } + } + +- if (_block_pos[max_reg] <= interval_to || _block_pos[max_reg + 1] <= interval_to) { ++ if (max_reg != any_reg && (_block_pos[max_reg] <= interval_to || _block_pos[max_reg + 1] <= interval_to)) { + *need_split = true; + } + +@@ -6443,8 +6443,9 @@ void LinearScanStatistic::print(const char* title) { + if (_counters_sum[i] > 0 || _counters_max[i] >= 0) { + tty->print("%25s: %8d", counter_name(i), _counters_sum[i]); + +- if (base_counter(i) != invalid_counter) { +- tty->print(" (%5.1f%%) ", _counters_sum[i] * 100.0 / _counters_sum[base_counter(i)]); ++ LinearScanStatistic::Counter cntr = base_counter(i); ++ if (cntr != invalid_counter) { ++ tty->print(" (%5.1f%%) ", _counters_sum[i] * 100.0 / _counters_sum[cntr]); + } else { + tty->print(" "); + } +diff --git a/hotspot/src/share/vm/classfile/classFileParser.cpp b/hotspot/src/share/vm/classfile/classFileParser.cpp +index 51ab4f5..d8e99e6 100644 +--- a/hotspot/src/share/vm/classfile/classFileParser.cpp ++++ b/hotspot/src/share/vm/classfile/classFileParser.cpp +@@ -1085,9 +1085,11 @@ class FieldAllocationCount: public ResourceObj { + + FieldAllocationType update(bool is_static, BasicType type) { + FieldAllocationType atype = basic_type_to_atype(is_static, type); +- // Make sure there is no overflow with injected fields. +- assert(count[atype] < 0xFFFF, "More than 65535 fields"); +- count[atype]++; ++ if (atype != BAD_ALLOCATION_TYPE) { ++ // Make sure there is no overflow with injected fields. ++ assert(count[atype] < 0xFFFF, "More than 65535 fields"); ++ count[atype]++; ++ } + return atype; + } + }; +@@ -3087,8 +3089,9 @@ void ClassFileParser::parse_classfile_attributes(ClassFileParser::ClassAnnotatio + } + } else if (tag == vmSymbols::tag_bootstrap_methods() && + _major_version >= Verifier::INVOKEDYNAMIC_MAJOR_VERSION) { +- if (parsed_bootstrap_methods_attribute) ++ if (parsed_bootstrap_methods_attribute) { + classfile_parse_error("Multiple BootstrapMethods attributes in class file %s", CHECK); ++ } + parsed_bootstrap_methods_attribute = true; + parse_classfile_bootstrap_methods_attribute(attribute_length, CHECK); + } else if (tag == vmSymbols::tag_runtime_visible_type_annotations()) { +diff --git a/hotspot/src/share/vm/classfile/systemDictionary.cpp b/hotspot/src/share/vm/classfile/systemDictionary.cpp +index d02ed31..9089a76 100644 +--- a/hotspot/src/share/vm/classfile/systemDictionary.cpp ++++ b/hotspot/src/share/vm/classfile/systemDictionary.cpp +@@ -1195,10 +1195,10 @@ Klass* SystemDictionary::resolve_from_stream(Symbol* class_name, + while ((index = strchr(name, '/')) != NULL) { + *index = '.'; // replace '/' with '.' in package name + } +- const char* fmt = "Prohibited package name: %s"; +- size_t len = strlen(fmt) + strlen(name); ++ const char* msg_text = "Prohibited package name: "; ++ size_t len = strlen(msg_text) + strlen(name) + 1; + char* message = NEW_RESOURCE_ARRAY(char, len); +- jio_snprintf(message, len, fmt, name); ++ jio_snprintf(message, len, "%s%s", msg_text, name); + Exceptions::_throw_msg(THREAD_AND_LOCATION, + vmSymbols::java_lang_SecurityException(), message); + } +diff --git a/hotspot/src/share/vm/compiler/compileBroker.cpp b/hotspot/src/share/vm/compiler/compileBroker.cpp +index 0e9af0d..7963625 100644 +--- a/hotspot/src/share/vm/compiler/compileBroker.cpp ++++ b/hotspot/src/share/vm/compiler/compileBroker.cpp +@@ -136,11 +136,6 @@ AbstractCompiler* CompileBroker::_compilers[2]; + volatile jint CompileBroker::_compilation_id = 0; + volatile jint CompileBroker::_osr_compilation_id = 0; + +-// Debugging information +-int CompileBroker::_last_compile_type = no_compile; +-int CompileBroker::_last_compile_level = CompLevel_none; +-char CompileBroker::_last_method_compiled[CompileBroker::name_buffer_length]; +- + // Performance counters + PerfCounter* CompileBroker::_perf_total_compilation = NULL; + PerfCounter* CompileBroker::_perf_osr_compilation = NULL; +@@ -882,8 +877,6 @@ CompilerCounters::CompilerCounters(const char* thread_name, int instance, TRAPS) + // + // Initialize the Compilation object + void CompileBroker::compilation_init() { +- _last_method_compiled[0] = '\0'; +- + // No need to initialize compilation system if we do not use it. + if (!UseCompiler) { + return; +@@ -1964,8 +1957,10 @@ void CompileBroker::invoke_compiler_on_method(CompileTask* task) { + } + assert(!method->is_native(), "no longer compile natives"); + +- // Save information about this method in case of failure. +- set_last_compile(thread, method, is_osr, task_level); ++ // Update compile information when using perfdata. ++ if (UsePerfData) { ++ update_compile_perf_data(thread, method, is_osr); ++ } + + DTRACE_METHOD_COMPILE_BEGIN_PROBE(method, compiler_name(task_level)); + } +@@ -2180,52 +2175,44 @@ void CompileBroker::handle_full_code_cache() { + // CompileBroker::set_last_compile + // + // Record this compilation for debugging purposes. +-void CompileBroker::set_last_compile(CompilerThread* thread, methodHandle method, bool is_osr, int comp_level) { ++void CompileBroker::update_compile_perf_data(CompilerThread* thread, const methodHandle& method, bool is_osr) { + ResourceMark rm; + char* method_name = method->name()->as_C_string(); +- strncpy(_last_method_compiled, method_name, CompileBroker::name_buffer_length); +- _last_method_compiled[CompileBroker::name_buffer_length - 1] = '\0'; // ensure null terminated + char current_method[CompilerCounters::cmname_buffer_length]; + size_t maxLen = CompilerCounters::cmname_buffer_length; + +- if (UsePerfData) { +- const char* class_name = method->method_holder()->name()->as_C_string(); ++ const char* class_name = method->method_holder()->name()->as_C_string(); + +- size_t s1len = strlen(class_name); +- size_t s2len = strlen(method_name); ++ size_t s1len = strlen(class_name); ++ size_t s2len = strlen(method_name); + +- // check if we need to truncate the string +- if (s1len + s2len + 2 > maxLen) { ++ // check if we need to truncate the string ++ if (s1len + s2len + 2 > maxLen) { + +- // the strategy is to lop off the leading characters of the +- // class name and the trailing characters of the method name. ++ // the strategy is to lop off the leading characters of the ++ // class name and the trailing characters of the method name. + +- if (s2len + 2 > maxLen) { +- // lop of the entire class name string, let snprintf handle +- // truncation of the method name. +- class_name += s1len; // null string +- } +- else { +- // lop off the extra characters from the front of the class name +- class_name += ((s1len + s2len + 2) - maxLen); +- } ++ if (s2len + 2 > maxLen) { ++ // lop of the entire class name string, let snprintf handle ++ // truncation of the method name. ++ class_name += s1len; // null string ++ } ++ else { ++ // lop off the extra characters from the front of the class name ++ class_name += ((s1len + s2len + 2) - maxLen); + } +- +- jio_snprintf(current_method, maxLen, "%s %s", class_name, method_name); + } + ++ jio_snprintf(current_method, maxLen, "%s %s", class_name, method_name); ++ ++ int last_compile_type = normal_compile; + if (CICountOSR && is_osr) { +- _last_compile_type = osr_compile; +- } else { +- _last_compile_type = normal_compile; ++ last_compile_type = normal_compile; + } +- _last_compile_level = comp_level; + +- if (UsePerfData) { +- CompilerCounters* counters = thread->counters(); +- counters->set_current_method(current_method); +- counters->set_compile_type((jlong)_last_compile_type); +- } ++ CompilerCounters* counters = thread->counters(); ++ counters->set_current_method(current_method); ++ counters->set_compile_type((jlong) last_compile_type); + } + + +@@ -2417,23 +2404,6 @@ void CompileBroker::print_times() { + tty->print_cr(" nmethod total size : %6d bytes", CompileBroker::_sum_nmethod_size); + } + +-// Debugging output for failure +-void CompileBroker::print_last_compile() { +- if ( _last_compile_level != CompLevel_none && +- compiler(_last_compile_level) != NULL && +- _last_method_compiled != NULL && +- _last_compile_type != no_compile) { +- if (_last_compile_type == osr_compile) { +- tty->print_cr("Last parse: [osr]%d+++(%d) %s", +- _osr_compilation_id, _last_compile_level, _last_method_compiled); +- } else { +- tty->print_cr("Last parse: %d+++(%d) %s", +- _compilation_id, _last_compile_level, _last_method_compiled); +- } +- } +-} +- +- + void CompileBroker::print_compiler_threads_on(outputStream* st) { + #ifndef PRODUCT + st->print_cr("Compiler thread printing unimplemented."); +diff --git a/hotspot/src/share/vm/compiler/compileBroker.hpp b/hotspot/src/share/vm/compiler/compileBroker.hpp +index 16e0ba3..96d5e81 100644 +--- a/hotspot/src/share/vm/compiler/compileBroker.hpp ++++ b/hotspot/src/share/vm/compiler/compileBroker.hpp +@@ -265,7 +265,7 @@ class CompileBroker: AllStatic { + name_buffer_length = 100 + }; + +- // Compile type Information for print_last_compile() and CompilerCounters ++ // Compile type Information for CompilerCounters + enum { no_compile, normal_compile, osr_compile, native_compile }; + static int assign_compile_id (methodHandle method, int osr_bci); + +@@ -284,10 +284,6 @@ class CompileBroker: AllStatic { + static volatile jint _compilation_id; + static volatile jint _osr_compilation_id; + +- static int _last_compile_type; +- static int _last_compile_level; +- static char _last_method_compiled[name_buffer_length]; +- + static CompileQueue* _c2_compile_queue; + static CompileQueue* _c1_compile_queue; + +@@ -356,7 +352,7 @@ class CompileBroker: AllStatic { + static void wait_for_completion(CompileTask* task); + + static void invoke_compiler_on_method(CompileTask* task); +- static void set_last_compile(CompilerThread *thread, methodHandle method, bool is_osr, int comp_level); ++ static void update_compile_perf_data(CompilerThread *thread, const methodHandle& method, bool is_osr); + static void push_jni_handle_block(); + static void pop_jni_handle_block(); + static bool check_break_at(methodHandle method, int compile_id, bool is_osr); +@@ -454,9 +450,6 @@ class CompileBroker: AllStatic { + // Print a detailed accounting of compilation time + static void print_times(); + +- // Debugging output for failure +- static void print_last_compile(); +- + static void print_compiler_threads_on(outputStream* st); + + // compiler name for debugging +diff --git a/hotspot/src/share/vm/compiler/compileLog.cpp b/hotspot/src/share/vm/compiler/compileLog.cpp +index 0637fd0..7dd9b46 100644 +--- a/hotspot/src/share/vm/compiler/compileLog.cpp ++++ b/hotspot/src/share/vm/compiler/compileLog.cpp +@@ -221,7 +221,8 @@ void CompileLog::finish_log_on_error(outputStream* file, char* buf, int buflen) + // Copy any remaining data inside a quote: + bool saw_slop = false; + int end_cdata = 0; // state machine [0..2] watching for too many "]]" +- while ((nr = read(partial_fd, buf, buflen)) > 0) { ++ while ((nr = read(partial_fd, buf, buflen-1)) > 0) { ++ buf[buflen-1] = '\0'; + if (!saw_slop) { + file->print_raw_cr(""); + file->print_raw_cr("= 0) { + // 2. /jre/lib///hsdis-.so + strcpy(&buf[lib_offset], hsdis_library_name); + strcat(&buf[lib_offset], os::dll_file_extension()); + _library = os::dll_load(buf, ebuf, sizeof ebuf); + } +- if (_library == NULL) { ++ if (_library == NULL && lib_offset > 0) { + // 3. /jre/lib//hsdis-.so + buf[lib_offset - 1] = '\0'; + const char* p = strrchr(buf, *os::file_separator()); +diff --git a/hotspot/src/share/vm/oops/generateOopMap.cpp b/hotspot/src/share/vm/oops/generateOopMap.cpp +index bb951f8..8452f96 100644 +--- a/hotspot/src/share/vm/oops/generateOopMap.cpp ++++ b/hotspot/src/share/vm/oops/generateOopMap.cpp +@@ -1684,7 +1684,13 @@ void GenerateOopMap::ppdupswap(int poplen, const char *out) { + assert(poplen < 5, "this must be less than length of actual vector"); + + // pop all arguments +- for(int i = 0; i < poplen; i++) actual[i] = pop(); ++ for (int i = 0; i < poplen; i++) { ++ actual[i] = pop(); ++ } ++ // Field _state is uninitialized when calling push. ++ for (int i = poplen; i < 5; i++) { ++ actual[i] = CellTypeState::uninit; ++ } + + // put them back + char push_ch = *out++; +diff --git a/hotspot/src/share/vm/opto/block.cpp b/hotspot/src/share/vm/opto/block.cpp +index 245ce42..1789d49 100644 +--- a/hotspot/src/share/vm/opto/block.cpp ++++ b/hotspot/src/share/vm/opto/block.cpp +@@ -1430,7 +1430,7 @@ void PhaseBlockLayout::find_edges() { + if (n->num_preds() != 1) break; + + i++; +- assert(n = _cfg.get_block(i), "expecting next block"); ++ assert(n == _cfg.get_block(i), "expecting next block"); + tr->append(n); + uf->map(n->_pre_order, tr->id()); + traces[n->_pre_order] = NULL; +diff --git a/hotspot/src/share/vm/opto/graphKit.cpp b/hotspot/src/share/vm/opto/graphKit.cpp +index f7c1009..dfadd3e 100644 +--- a/hotspot/src/share/vm/opto/graphKit.cpp ++++ b/hotspot/src/share/vm/opto/graphKit.cpp +@@ -1074,7 +1074,7 @@ bool GraphKit::compute_stack_effects(int& inputs, int& depth) { + case Bytecodes::_freturn: + case Bytecodes::_dreturn: + case Bytecodes::_areturn: +- assert(rsize = -depth, ""); ++ assert(rsize == -depth, ""); + inputs = rsize; + break; + +diff --git a/hotspot/src/share/vm/opto/matcher.cpp b/hotspot/src/share/vm/opto/matcher.cpp +index b26015c..11e11e0 100644 +--- a/hotspot/src/share/vm/opto/matcher.cpp ++++ b/hotspot/src/share/vm/opto/matcher.cpp +@@ -659,11 +659,14 @@ void Matcher::Fixup_Save_On_Entry( ) { + uint reth_edge_cnt = TypeFunc::Parms+1; + RegMask *reth_rms = init_input_masks( reth_edge_cnt + soe_cnt, _return_addr_mask, c_frame_ptr_mask ); + // Rethrow takes exception oop only, but in the argument 0 slot. +- reth_rms[TypeFunc::Parms] = mreg2regmask[find_receiver(false)]; ++ OptoReg::Name reg = find_receiver(false); ++ if (reg >= 0) { ++ reth_rms[TypeFunc::Parms] = mreg2regmask[reg]; + #ifdef _LP64 +- // Need two slots for ptrs in 64-bit land +- reth_rms[TypeFunc::Parms].Insert(OptoReg::add(OptoReg::Name(find_receiver(false)),1)); ++ // Need two slots for ptrs in 64-bit land ++ reth_rms[TypeFunc::Parms].Insert(OptoReg::add(OptoReg::Name(reg), 1)); + #endif ++ } + + // Input RegMask array shared by all TailCalls + uint tail_call_edge_cnt = TypeFunc::Parms+2; +diff --git a/hotspot/src/share/vm/runtime/relocator.cpp b/hotspot/src/share/vm/runtime/relocator.cpp +index 450bcf2..2bbb8db 100644 +--- a/hotspot/src/share/vm/runtime/relocator.cpp ++++ b/hotspot/src/share/vm/runtime/relocator.cpp +@@ -612,8 +612,8 @@ bool Relocator::relocate_code(int bci, int ilen, int delta) { + // In case we have shrunken a tableswitch/lookupswitch statement, we store the last + // bytes that get overwritten. We have to copy the bytes after the change_jumps method + // has been called, since it is likly to update last offset in a tableswitch/lookupswitch +- if (delta < 0) { +- assert(delta>=-3, "we cannot overwrite more than 3 bytes"); ++ assert(delta >= -3, "We cannot overwrite more than 3 bytes."); ++ if (delta < 0 && delta >= -3) { + memcpy(_overwrite, addr_at(bci + ilen + delta), -delta); + } + +-- +1.8.3.1 diff --git a/8143925-enhancing-CounterMode.crypt-for-AESCrypt.patch b/8143925-enhancing-CounterMode.crypt-for-AESCrypt.patch new file mode 100644 index 0000000000000000000000000000000000000000..81acb5c4ec2e49c9990e1cf2ed85d1ecad7211ac --- /dev/null +++ b/8143925-enhancing-CounterMode.crypt-for-AESCrypt.patch @@ -0,0 +1,3938 @@ +From 02b097417275acaad294d71a852c2def2222be25 Mon Sep 17 00:00:00 2001 +From: kuenking111 +Date: Sat, 3 Sep 2022 14:17:50 +0000 +Subject: [PATCH 1/6] 8143925-enhancing-CounterMode.crypt-for-AESCrypt + +--- + .../src/cpu/aarch64/vm/assembler_aarch64.hpp | 35 +- + .../cpu/aarch64/vm/macroAssembler_aarch64.hpp | 17 + + .../aarch64/vm/macroAssembler_aarch64_aes.cpp | 685 ++++++++++++++++++ + .../cpu/aarch64/vm/stubGenerator_aarch64.cpp | 324 ++++++++- + .../cpu/aarch64/vm/stubRoutines_aarch64.hpp | 2 +- + .../src/cpu/aarch64/vm/vm_version_aarch64.cpp | 13 +- + hotspot/src/cpu/ppc/vm/vm_version_ppc.cpp | 5 + + hotspot/src/cpu/sparc/vm/vm_version_sparc.cpp | 5 + + hotspot/src/cpu/x86/vm/assembler_x86.cpp | 74 +- + hotspot/src/cpu/x86/vm/assembler_x86.hpp | 12 + + .../src/cpu/x86/vm/stubGenerator_x86_32.cpp | 344 +++++++++ + .../src/cpu/x86/vm/stubGenerator_x86_64.cpp | 340 ++++++++- + hotspot/src/cpu/x86/vm/stubRoutines_x86.cpp | 1 + + hotspot/src/cpu/x86/vm/stubRoutines_x86.hpp | 5 + + .../src/cpu/x86/vm/stubRoutines_x86_32.hpp | 2 +- + .../src/cpu/x86/vm/stubRoutines_x86_64.hpp | 2 +- + hotspot/src/cpu/x86/vm/vm_version_x86.cpp | 36 + + hotspot/src/share/vm/classfile/vmSymbols.hpp | 4 + + hotspot/src/share/vm/opto/escape.cpp | 1 + + hotspot/src/share/vm/opto/library_call.cpp | 174 +++++ + hotspot/src/share/vm/opto/runtime.cpp | 29 + + hotspot/src/share/vm/opto/runtime.hpp | 1 + + hotspot/src/share/vm/runtime/globals.hpp | 3 + + hotspot/src/share/vm/runtime/stubRoutines.cpp | 1 + + hotspot/src/share/vm/runtime/stubRoutines.hpp | 2 + + hotspot/src/share/vm/runtime/vmStructs.cpp | 1 + + .../test/compiler/7184394/TestAESBase.java | 4 +- + .../test/compiler/7184394/TestAESMain.java | 7 + + .../com/sun/crypto/provider/CounterMode.java | 11 +- + .../classes/com/sun/crypto/provider/GCTR.java | 89 +-- + .../com/sun/crypto/provider/GHASH.java | 20 +- + .../sun/security/ssl/SSLSocketImpl.java | 14 +- + .../security/ssl/SSLSocketInputRecord.java | 215 +++--- + .../sun/security/ssl/SSLTransport.java | 4 + + .../bench/javax/crypto/full/AESGCMBench.java | 128 ++++ + .../javax/crypto/full/AESGCMByteBuffer.java | 163 +++++ + .../bench/javax/crypto/full/CryptoBase.java | 102 +++ + .../bench/javax/crypto/small/AESGCMBench.java | 36 + + .../javax/crypto/small/AESGCMByteBuffer.java | 36 + + .../ssl/SSLSocketImpl/ClientTimeout.java | 3 +- + .../SSLSocketImpl/SSLExceptionForIOIssue.java | 4 +- + 41 files changed, 2738 insertions(+), 216 deletions(-) + create mode 100644 hotspot/src/cpu/aarch64/vm/macroAssembler_aarch64_aes.cpp + create mode 100644 jdk/test/micro/org/openjdk/bench/javax/crypto/full/AESGCMBench.java + create mode 100644 jdk/test/micro/org/openjdk/bench/javax/crypto/full/AESGCMByteBuffer.java + create mode 100644 jdk/test/micro/org/openjdk/bench/javax/crypto/full/CryptoBase.java + create mode 100644 jdk/test/micro/org/openjdk/bench/javax/crypto/small/AESGCMBench.java + create mode 100644 jdk/test/micro/org/openjdk/bench/javax/crypto/small/AESGCMByteBuffer.java + +diff --git a/hotspot/src/cpu/aarch64/vm/assembler_aarch64.hpp b/hotspot/src/cpu/aarch64/vm/assembler_aarch64.hpp +index b0fa9b5fc..9202e61f8 100644 +--- a/hotspot/src/cpu/aarch64/vm/assembler_aarch64.hpp ++++ b/hotspot/src/cpu/aarch64/vm/assembler_aarch64.hpp +@@ -146,6 +146,21 @@ REGISTER_DECLARATION(Register, esp, r20); + + #define assert_cond(ARG1) assert(ARG1, #ARG1) + ++// In many places we've added C-style casts to silence compiler ++// warnings, for example when truncating a size_t to an int when we ++// know the size_t is a small struct. Such casts are risky because ++// they effectively disable useful compiler warnings. We can make our ++// lives safer with this function, which ensures that any cast is ++// reversible without loss of information. It doesn't check ++// everything: it isn't intended to make sure that pointer types are ++// compatible, for example. ++template ++T2 checked_cast(T1 thing) { ++ T2 result = static_cast(thing); ++ assert(static_cast(result) == thing, "must be"); ++ return result; ++} ++ + namespace asm_util { + uint32_t encode_logical_immediate(bool is32, uint64_t imm); + }; +@@ -193,7 +208,7 @@ public: + static inline uint32_t extract(uint32_t val, int msb, int lsb) { + int nbits = msb - lsb + 1; + assert_cond(msb >= lsb); +- uint32_t mask = (1U << nbits) - 1; ++ uint32_t mask = checked_cast(right_n_bits(nbits)); + uint32_t result = val >> lsb; + result &= mask; + return result; +@@ -208,7 +223,7 @@ public: + int nbits = msb - lsb + 1; + guarantee(val < (1U << nbits), "Field too big for insn"); + assert_cond(msb >= lsb); +- unsigned mask = (1U << nbits) - 1; ++ unsigned mask = checked_cast(right_n_bits(nbits)); + val <<= lsb; + mask <<= lsb; + unsigned target = *(unsigned *)a; +@@ -222,7 +237,7 @@ public: + long chk = val >> (nbits - 1); + guarantee (chk == -1 || chk == 0, "Field too big for insn"); + unsigned uval = val; +- unsigned mask = (1U << nbits) - 1; ++ unsigned mask = checked_cast(right_n_bits(nbits)); + uval &= mask; + uval <<= lsb; + mask <<= lsb; +@@ -234,9 +249,9 @@ public: + + void f(unsigned val, int msb, int lsb) { + int nbits = msb - lsb + 1; +- guarantee(val < (1U << nbits), "Field too big for insn"); ++ guarantee(val < (1ULL << nbits), "Field too big for insn"); + assert_cond(msb >= lsb); +- unsigned mask = (1U << nbits) - 1; ++ unsigned mask = checked_cast(right_n_bits(nbits)); + val <<= lsb; + mask <<= lsb; + insn |= val; +@@ -255,7 +270,7 @@ public: + long chk = val >> (nbits - 1); + guarantee (chk == -1 || chk == 0, "Field too big for insn"); + unsigned uval = val; +- unsigned mask = (1U << nbits) - 1; ++ unsigned mask = checked_cast(right_n_bits(nbits)); + uval &= mask; + f(uval, lsb + nbits - 1, lsb); + } +@@ -280,7 +295,7 @@ public: + + unsigned get(int msb = 31, int lsb = 0) { + int nbits = msb - lsb + 1; +- unsigned mask = ((1U << nbits) - 1) << lsb; ++ unsigned mask = checked_cast(right_n_bits(nbits)) << lsb; + assert_cond((bits & mask) == mask); + return (insn & mask) >> lsb; + } +@@ -1991,21 +2006,21 @@ public: + starti; + f(0,31), f((int)T & 1, 30); + f(op1, 29, 21), f(0, 20, 16), f(op2, 15, 12); +- f((int)T >> 1, 11, 10), rf(Xn, 5), rf(Vt, 0); ++ f((int)T >> 1, 11, 10), srf(Xn, 5), rf(Vt, 0); + } + void ld_st(FloatRegister Vt, SIMD_Arrangement T, Register Xn, + int imm, int op1, int op2) { + starti; + f(0,31), f((int)T & 1, 30); + f(op1 | 0b100, 29, 21), f(0b11111, 20, 16), f(op2, 15, 12); +- f((int)T >> 1, 11, 10), rf(Xn, 5), rf(Vt, 0); ++ f((int)T >> 1, 11, 10), srf(Xn, 5), rf(Vt, 0); + } + void ld_st(FloatRegister Vt, SIMD_Arrangement T, Register Xn, + Register Xm, int op1, int op2) { + starti; + f(0,31), f((int)T & 1, 30); + f(op1 | 0b100, 29, 21), rf(Xm, 16), f(op2, 15, 12); +- f((int)T >> 1, 11, 10), rf(Xn, 5), rf(Vt, 0); ++ f((int)T >> 1, 11, 10), srf(Xn, 5), rf(Vt, 0); + } + + void ld_st(FloatRegister Vt, SIMD_Arrangement T, Address a, int op1, int op2) { +diff --git a/hotspot/src/cpu/aarch64/vm/macroAssembler_aarch64.hpp b/hotspot/src/cpu/aarch64/vm/macroAssembler_aarch64.hpp +index 0ca694038..d334f1b69 100644 +--- a/hotspot/src/cpu/aarch64/vm/macroAssembler_aarch64.hpp ++++ b/hotspot/src/cpu/aarch64/vm/macroAssembler_aarch64.hpp +@@ -1240,6 +1240,23 @@ public: + void multiply_to_len(Register x, Register xlen, Register y, Register ylen, Register z, + Register zlen, Register tmp1, Register tmp2, Register tmp3, + Register tmp4, Register tmp5, Register tmp6, Register tmp7); ++ void ghash_multiply(FloatRegister result_lo, FloatRegister result_hi, ++ FloatRegister a, FloatRegister b, FloatRegister a1_xor_a0, ++ FloatRegister tmp1, FloatRegister tmp2, FloatRegister tmp3); ++ void ghash_reduce(FloatRegister result, FloatRegister lo, FloatRegister hi, ++ FloatRegister p, FloatRegister z, FloatRegister t1); ++ void ghash_processBlocks_wide(address p, Register state, Register subkeyH, ++ Register data, Register blocks, int unrolls); ++ void ghash_modmul (FloatRegister result, ++ FloatRegister result_lo, FloatRegister result_hi, FloatRegister b, ++ FloatRegister a, FloatRegister vzr, FloatRegister a1_xor_a0, FloatRegister p, ++ FloatRegister t1, FloatRegister t2, FloatRegister t3); ++ ++ void aesenc_loadkeys(Register key, Register keylen); ++ void aesecb_encrypt(Register from, Register to, Register keylen, ++ FloatRegister data = v0, int unrolls = 1); ++ void aesecb_decrypt(Register from, Register to, Register key, Register keylen); ++ void aes_round(FloatRegister input, FloatRegister subkey); + // ISB may be needed because of a safepoint + void maybe_isb() { isb(); } + +diff --git a/hotspot/src/cpu/aarch64/vm/macroAssembler_aarch64_aes.cpp b/hotspot/src/cpu/aarch64/vm/macroAssembler_aarch64_aes.cpp +new file mode 100644 +index 000000000..1db79c97a +--- /dev/null ++++ b/hotspot/src/cpu/aarch64/vm/macroAssembler_aarch64_aes.cpp +@@ -0,0 +1,685 @@ ++/* ++ * Copyright (c) 2003, 2021, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2014, 2021, Red Hat Inc. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ * ++ */ ++ ++#include "precompiled.hpp" ++ ++#include "asm/assembler.hpp" ++#include "asm/assembler.inline.hpp" ++#include "macroAssembler_aarch64.hpp" ++#include "memory/resourceArea.hpp" ++#include "runtime/stubRoutines.hpp" ++ ++void MacroAssembler::aesecb_decrypt(Register from, Register to, Register key, Register keylen) { ++ Label L_doLast; ++ ++ ld1(v0, T16B, from); // get 16 bytes of input ++ ++ ld1(v5, T16B, post(key, 16)); ++ rev32(v5, T16B, v5); ++ ++ ld1(v1, v2, v3, v4, T16B, post(key, 64)); ++ rev32(v1, T16B, v1); ++ rev32(v2, T16B, v2); ++ rev32(v3, T16B, v3); ++ rev32(v4, T16B, v4); ++ aesd(v0, v1); ++ aesimc(v0, v0); ++ aesd(v0, v2); ++ aesimc(v0, v0); ++ aesd(v0, v3); ++ aesimc(v0, v0); ++ aesd(v0, v4); ++ aesimc(v0, v0); ++ ++ ld1(v1, v2, v3, v4, T16B, post(key, 64)); ++ rev32(v1, T16B, v1); ++ rev32(v2, T16B, v2); ++ rev32(v3, T16B, v3); ++ rev32(v4, T16B, v4); ++ aesd(v0, v1); ++ aesimc(v0, v0); ++ aesd(v0, v2); ++ aesimc(v0, v0); ++ aesd(v0, v3); ++ aesimc(v0, v0); ++ aesd(v0, v4); ++ aesimc(v0, v0); ++ ++ ld1(v1, v2, T16B, post(key, 32)); ++ rev32(v1, T16B, v1); ++ rev32(v2, T16B, v2); ++ ++ cmpw(keylen, 44); ++ br(Assembler::EQ, L_doLast); ++ ++ aesd(v0, v1); ++ aesimc(v0, v0); ++ aesd(v0, v2); ++ aesimc(v0, v0); ++ ++ ld1(v1, v2, T16B, post(key, 32)); ++ rev32(v1, T16B, v1); ++ rev32(v2, T16B, v2); ++ ++ cmpw(keylen, 52); ++ br(Assembler::EQ, L_doLast); ++ ++ aesd(v0, v1); ++ aesimc(v0, v0); ++ aesd(v0, v2); ++ aesimc(v0, v0); ++ ++ ld1(v1, v2, T16B, post(key, 32)); ++ rev32(v1, T16B, v1); ++ rev32(v2, T16B, v2); ++ ++ bind(L_doLast); ++ ++ aesd(v0, v1); ++ aesimc(v0, v0); ++ aesd(v0, v2); ++ ++ eor(v0, T16B, v0, v5); ++ ++ st1(v0, T16B, to); ++ ++ // Preserve the address of the start of the key ++ sub(key, key, keylen, LSL, exact_log2(sizeof (jint))); ++} ++ ++// Load expanded key into v17..v31 ++void MacroAssembler::aesenc_loadkeys(Register key, Register keylen) { ++ Label L_loadkeys_44, L_loadkeys_52; ++ cmpw(keylen, 52); ++ br(Assembler::LO, L_loadkeys_44); ++ br(Assembler::EQ, L_loadkeys_52); ++ ++ ld1(v17, v18, T16B, post(key, 32)); ++ rev32(v17, T16B, v17); ++ rev32(v18, T16B, v18); ++ bind(L_loadkeys_52); ++ ld1(v19, v20, T16B, post(key, 32)); ++ rev32(v19, T16B, v19); ++ rev32(v20, T16B, v20); ++ bind(L_loadkeys_44); ++ ld1(v21, v22, v23, v24, T16B, post(key, 64)); ++ rev32(v21, T16B, v21); ++ rev32(v22, T16B, v22); ++ rev32(v23, T16B, v23); ++ rev32(v24, T16B, v24); ++ ld1(v25, v26, v27, v28, T16B, post(key, 64)); ++ rev32(v25, T16B, v25); ++ rev32(v26, T16B, v26); ++ rev32(v27, T16B, v27); ++ rev32(v28, T16B, v28); ++ ld1(v29, v30, v31, T16B, post(key, 48)); ++ rev32(v29, T16B, v29); ++ rev32(v30, T16B, v30); ++ rev32(v31, T16B, v31); ++ ++ // Preserve the address of the start of the key ++ sub(key, key, keylen, LSL, exact_log2(sizeof (jint))); ++} ++ ++// NeoverseTM N1Software Optimization Guide: ++// Adjacent AESE/AESMC instruction pairs and adjacent AESD/AESIMC ++// instruction pairs will exhibit the performance characteristics ++// described in Section 4.6. ++void MacroAssembler::aes_round(FloatRegister input, FloatRegister subkey) { ++ aese(input, subkey); aesmc(input, input); ++} ++ ++// KernelGenerator ++// ++// The abstract base class of an unrolled function generator. ++// Subclasses override generate(), length(), and next() to generate ++// unrolled and interleaved functions. ++// ++// The core idea is that a subclass defines a method which generates ++// the base case of a function and a method to generate a clone of it, ++// shifted to a different set of registers. KernelGenerator will then ++// generate several interleaved copies of the function, with each one ++// using a different set of registers. ++ ++// The subclass must implement three methods: length(), which is the ++// number of instruction bundles in the intrinsic, generate(int n) ++// which emits the nth instruction bundle in the intrinsic, and next() ++// which takes an instance of the generator and returns a version of it, ++// shifted to a new set of registers. ++ ++class KernelGenerator: public MacroAssembler { ++protected: ++ const int _unrolls; ++public: ++ KernelGenerator(Assembler *as, int unrolls) ++ : MacroAssembler(as->code()), _unrolls(unrolls) { } ++ virtual void generate(int index) = 0; ++ virtual int length() = 0; ++ virtual KernelGenerator *next() = 0; ++ int unrolls() { return _unrolls; } ++ void unroll(); ++}; ++ ++void KernelGenerator::unroll() { ++ ResourceMark rm; ++ KernelGenerator **generators ++ = NEW_RESOURCE_ARRAY(KernelGenerator *, unrolls()); ++ ++ generators[0] = this; ++ for (int i = 1; i < unrolls(); i++) { ++ generators[i] = generators[i-1]->next(); ++ } ++ ++ for (int j = 0; j < length(); j++) { ++ for (int i = 0; i < unrolls(); i++) { ++ generators[i]->generate(j); ++ } ++ } ++} ++ ++// An unrolled and interleaved generator for AES encryption. ++class AESKernelGenerator: public KernelGenerator { ++ Register _from, _to; ++ const Register _keylen; ++ FloatRegister _data; ++ const FloatRegister _subkeys; ++ bool _once; ++ Label _rounds_44, _rounds_52; ++ ++public: ++ AESKernelGenerator(Assembler *as, int unrolls, ++ Register from, Register to, Register keylen, FloatRegister data, ++ FloatRegister subkeys, bool once = true) ++ : KernelGenerator(as, unrolls), ++ _from(from), _to(to), _keylen(keylen), _data(data), ++ _subkeys(subkeys), _once(once) { ++ } ++ ++ virtual void generate(int index) { ++ switch (index) { ++ case 0: ++ if (_from != noreg) { ++ ld1(_data, T16B, _from); // get 16 bytes of input ++ } ++ break; ++ case 1: ++ if (_once) { ++ cmpw(_keylen, 52); ++ br(Assembler::LO, _rounds_44); ++ br(Assembler::EQ, _rounds_52); ++ } ++ break; ++ case 2: aes_round(_data, _subkeys + 0); break; ++ case 3: aes_round(_data, _subkeys + 1); break; ++ case 4: ++ if (_once) bind(_rounds_52); ++ break; ++ case 5: aes_round(_data, _subkeys + 2); break; ++ case 6: aes_round(_data, _subkeys + 3); break; ++ case 7: ++ if (_once) bind(_rounds_44); ++ break; ++ case 8: aes_round(_data, _subkeys + 4); break; ++ case 9: aes_round(_data, _subkeys + 5); break; ++ case 10: aes_round(_data, _subkeys + 6); break; ++ case 11: aes_round(_data, _subkeys + 7); break; ++ case 12: aes_round(_data, _subkeys + 8); break; ++ case 13: aes_round(_data, _subkeys + 9); break; ++ case 14: aes_round(_data, _subkeys + 10); break; ++ case 15: aes_round(_data, _subkeys + 11); break; ++ case 16: aes_round(_data, _subkeys + 12); break; ++ case 17: aese(_data, _subkeys + 13); break; ++ case 18: eor(_data, T16B, _data, _subkeys + 14); break; ++ case 19: ++ if (_to != noreg) { ++ st1(_data, T16B, _to); ++ } ++ break; ++ default: ShouldNotReachHere(); ++ } ++ } ++ ++ virtual KernelGenerator *next() { ++ return new AESKernelGenerator(this, _unrolls, ++ _from, _to, _keylen, ++ _data + 1, _subkeys, /*once*/false); ++ } ++ ++ virtual int length() { return 20; } ++}; ++ ++// Uses expanded key in v17..v31 ++// Returns encrypted values in inputs. ++// If to != noreg, store value at to; likewise from ++// Preserves key, keylen ++// Increments from, to ++// Input data in v0, v1, ... ++// unrolls controls the number of times to unroll the generated function ++void MacroAssembler::aesecb_encrypt(Register from, Register to, Register keylen, ++ FloatRegister data, int unrolls) { ++ AESKernelGenerator(this, unrolls, from, to, keylen, data, v17) .unroll(); ++} ++ ++// ghash_multiply and ghash_reduce are the non-unrolled versions of ++// the GHASH function generators. ++void MacroAssembler::ghash_multiply(FloatRegister result_lo, FloatRegister result_hi, ++ FloatRegister a, FloatRegister b, FloatRegister a1_xor_a0, ++ FloatRegister tmp1, FloatRegister tmp2, FloatRegister tmp3) { ++ // Karatsuba multiplication performs a 128*128 -> 256-bit ++ // multiplication in three 128-bit multiplications and a few ++ // additions. ++ // ++ // (C1:C0) = A1*B1, (D1:D0) = A0*B0, (E1:E0) = (A0+A1)(B0+B1) ++ // (A1:A0)(B1:B0) = C1:(C0+C1+D1+E1):(D1+C0+D0+E0):D0 ++ // ++ // Inputs: ++ // ++ // A0 in a.d[0] (subkey) ++ // A1 in a.d[1] ++ // (A1+A0) in a1_xor_a0.d[0] ++ // ++ // B0 in b.d[0] (state) ++ // B1 in b.d[1] ++ ++ ext(tmp1, T16B, b, b, 0x08); ++ pmull2(result_hi, T1Q, b, a, T2D); // A1*B1 ++ eor(tmp1, T16B, tmp1, b); // (B1+B0) ++ pmull(result_lo, T1Q, b, a, T1D); // A0*B0 ++ pmull(tmp2, T1Q, tmp1, a1_xor_a0, T1D); // (A1+A0)(B1+B0) ++ ++ ext(tmp1, T16B, result_lo, result_hi, 0x08); ++ eor(tmp3, T16B, result_hi, result_lo); // A1*B1+A0*B0 ++ eor(tmp2, T16B, tmp2, tmp1); ++ eor(tmp2, T16B, tmp2, tmp3); ++ ++ // Register pair holds the result of carry-less multiplication ++ ins(result_hi, D, tmp2, 0, 1); ++ ins(result_lo, D, tmp2, 1, 0); ++} ++ ++void MacroAssembler::ghash_reduce(FloatRegister result, FloatRegister lo, FloatRegister hi, ++ FloatRegister p, FloatRegister vzr, FloatRegister t1) { ++ const FloatRegister t0 = result; ++ ++ // The GCM field polynomial f is z^128 + p(z), where p = ++ // z^7+z^2+z+1. ++ // ++ // z^128 === -p(z) (mod (z^128 + p(z))) ++ // ++ // so, given that the product we're reducing is ++ // a == lo + hi * z^128 ++ // substituting, ++ // === lo - hi * p(z) (mod (z^128 + p(z))) ++ // ++ // we reduce by multiplying hi by p(z) and subtracting the result ++ // from (i.e. XORing it with) lo. Because p has no nonzero high ++ // bits we can do this with two 64-bit multiplications, lo*p and ++ // hi*p. ++ ++ pmull2(t0, T1Q, hi, p, T2D); ++ ext(t1, T16B, t0, vzr, 8); ++ eor(hi, T16B, hi, t1); ++ ext(t1, T16B, vzr, t0, 8); ++ eor(lo, T16B, lo, t1); ++ pmull(t0, T1Q, hi, p, T1D); ++ eor(result, T16B, lo, t0); ++} ++ ++class GHASHMultiplyGenerator: public KernelGenerator { ++ FloatRegister _result_lo, _result_hi, _b, ++ _a, _vzr, _a1_xor_a0, _p, ++ _tmp1, _tmp2, _tmp3; ++ ++public: ++ GHASHMultiplyGenerator(Assembler *as, int unrolls, ++ FloatRegister result_lo, FloatRegister result_hi, ++ /* offsetted registers */ ++ FloatRegister b, ++ /* non-offsetted (shared) registers */ ++ FloatRegister a, FloatRegister a1_xor_a0, FloatRegister p, FloatRegister vzr, ++ /* offseted (temp) registers */ ++ FloatRegister tmp1, FloatRegister tmp2, FloatRegister tmp3) ++ : KernelGenerator(as, unrolls), ++ _result_lo(result_lo), _result_hi(result_hi), _b(b), ++ _a(a), _vzr(vzr), _a1_xor_a0(a1_xor_a0), _p(p), ++ _tmp1(tmp1), _tmp2(tmp2), _tmp3(tmp3) { } ++ ++ static const int register_stride = 7; ++ ++ virtual void generate(int index) { ++ // Karatsuba multiplication performs a 128*128 -> 256-bit ++ // multiplication in three 128-bit multiplications and a few ++ // additions. ++ // ++ // (C1:C0) = A1*B1, (D1:D0) = A0*B0, (E1:E0) = (A0+A1)(B0+B1) ++ // (A1:A0)(B1:B0) = C1:(C0+C1+D1+E1):(D1+C0+D0+E0):D0 ++ // ++ // Inputs: ++ // ++ // A0 in a.d[0] (subkey) ++ // A1 in a.d[1] ++ // (A1+A0) in a1_xor_a0.d[0] ++ // ++ // B0 in b.d[0] (state) ++ // B1 in b.d[1] ++ ++ switch (index) { ++ case 0: ext(_tmp1, T16B, _b, _b, 0x08); break; ++ case 1: pmull2(_result_hi, T1Q, _b, _a, T2D); // A1*B1 ++ break; ++ case 2: eor(_tmp1, T16B, _tmp1, _b); // (B1+B0) ++ break; ++ case 3: pmull(_result_lo, T1Q, _b, _a, T1D); // A0*B0 ++ break; ++ case 4: pmull(_tmp2, T1Q, _tmp1, _a1_xor_a0, T1D); // (A1+A0)(B1+B0) ++ break; ++ ++ case 5: ext(_tmp1, T16B, _result_lo, _result_hi, 0x08); break; ++ case 6: eor(_tmp3, T16B, _result_hi, _result_lo); // A1*B1+A0*B0 ++ break; ++ case 7: eor(_tmp2, T16B, _tmp2, _tmp1); break; ++ case 8: eor(_tmp2, T16B, _tmp2, _tmp3); break; ++ ++ // Register pair <_result_hi:_result_lo> holds the _result of carry-less multiplication ++ case 9: ins(_result_hi, D, _tmp2, 0, 1); break; ++ case 10: ins(_result_lo, D, _tmp2, 1, 0); break; ++ default: ShouldNotReachHere(); ++ } ++ } ++ ++ virtual KernelGenerator *next() { ++ GHASHMultiplyGenerator *result ++ = new GHASHMultiplyGenerator(this, _unrolls, _result_lo, _result_hi, ++ _b, _a, _a1_xor_a0, _p, _vzr, ++ _tmp1, _tmp2, _tmp3); ++ result->_result_lo += register_stride; ++ result->_result_hi += register_stride; ++ result->_b += register_stride; ++ result->_tmp1 += register_stride; ++ result->_tmp2 += register_stride; ++ result->_tmp3 += register_stride; ++ return result; ++ } ++ ++ virtual int length() { return 11; } ++}; ++ ++// Reduce the 128-bit product in hi:lo by the GCM field polynomial. ++// The FloatRegister argument called data is optional: if it is a ++// valid register, we interleave LD1 instructions with the ++// reduction. This is to reduce latency next time around the loop. ++class GHASHReduceGenerator: public KernelGenerator { ++ FloatRegister _result, _lo, _hi, _p, _vzr, _data, _t1; ++ int _once; ++public: ++ GHASHReduceGenerator(Assembler *as, int unrolls, ++ /* offsetted registers */ ++ FloatRegister result, FloatRegister lo, FloatRegister hi, ++ /* non-offsetted (shared) registers */ ++ FloatRegister p, FloatRegister vzr, FloatRegister data, ++ /* offseted (temp) registers */ ++ FloatRegister t1) ++ : KernelGenerator(as, unrolls), ++ _result(result), _lo(lo), _hi(hi), ++ _p(p), _vzr(vzr), _data(data), _t1(t1), _once(true) { } ++ ++ static const int register_stride = 7; ++ ++ virtual void generate(int index) { ++ const FloatRegister t0 = _result; ++ ++ switch (index) { ++ // The GCM field polynomial f is z^128 + p(z), where p = ++ // z^7+z^2+z+1. ++ // ++ // z^128 === -p(z) (mod (z^128 + p(z))) ++ // ++ // so, given that the product we're reducing is ++ // a == lo + hi * z^128 ++ // substituting, ++ // === lo - hi * p(z) (mod (z^128 + p(z))) ++ // ++ // we reduce by multiplying hi by p(z) and subtracting the _result ++ // from (i.e. XORing it with) lo. Because p has no nonzero high ++ // bits we can do this with two 64-bit multiplications, lo*p and ++ // hi*p. ++ ++ case 0: pmull2(t0, T1Q, _hi, _p, T2D); break; ++ case 1: ext(_t1, T16B, t0, _vzr, 8); break; ++ case 2: eor(_hi, T16B, _hi, _t1); break; ++ case 3: ext(_t1, T16B, _vzr, t0, 8); break; ++ case 4: eor(_lo, T16B, _lo, _t1); break; ++ case 5: pmull(t0, T1Q, _hi, _p, T1D); break; ++ case 6: eor(_result, T16B, _lo, t0); break; ++ default: ShouldNotReachHere(); ++ } ++ ++ // Sprinkle load instructions into the generated instructions ++ if (_data->is_valid() && _once) { ++ assert(length() >= unrolls(), "not enough room for inteleaved loads"); ++ if (index < unrolls()) { ++ ld1((_data + index*register_stride), T16B, post(r2, 0x10)); ++ } ++ } ++ } ++ ++ virtual KernelGenerator *next() { ++ GHASHReduceGenerator *result ++ = new GHASHReduceGenerator(this, _unrolls, ++ _result, _lo, _hi, _p, _vzr, _data, _t1); ++ result->_result += register_stride; ++ result->_hi += register_stride; ++ result->_lo += register_stride; ++ result->_t1 += register_stride; ++ result->_once = false; ++ return result; ++ } ++ ++ int length() { return 7; } ++}; ++ ++// Perform a GHASH multiply/reduce on a single FloatRegister. ++void MacroAssembler::ghash_modmul(FloatRegister result, ++ FloatRegister result_lo, FloatRegister result_hi, FloatRegister b, ++ FloatRegister a, FloatRegister vzr, FloatRegister a1_xor_a0, FloatRegister p, ++ FloatRegister t1, FloatRegister t2, FloatRegister t3) { ++ ghash_multiply(result_lo, result_hi, a, b, a1_xor_a0, t1, t2, t3); ++ ghash_reduce(result, result_lo, result_hi, p, vzr, t1); ++} ++ ++// Interleaved GHASH processing. ++// ++// Clobbers all vector registers. ++// ++void MacroAssembler::ghash_processBlocks_wide(address field_polynomial, Register state, ++ Register subkeyH, ++ Register data, Register blocks, int unrolls) { ++ int register_stride = 7; ++ ++ // Bafflingly, GCM uses little-endian for the byte order, but ++ // big-endian for the bit order. For example, the polynomial 1 is ++ // represented as the 16-byte string 80 00 00 00 | 12 bytes of 00. ++ // ++ // So, we must either reverse the bytes in each word and do ++ // everything big-endian or reverse the bits in each byte and do ++ // it little-endian. On AArch64 it's more idiomatic to reverse ++ // the bits in each byte (we have an instruction, RBIT, to do ++ // that) and keep the data in little-endian bit order throught the ++ // calculation, bit-reversing the inputs and outputs. ++ ++ assert(unrolls * register_stride < 32, "out of registers"); ++ ++ FloatRegister a1_xor_a0 = v28; ++ FloatRegister Hprime = v29; ++ FloatRegister vzr = v30; ++ FloatRegister p = v31; ++ eor(vzr, T16B, vzr, vzr); // zero register ++ ++ ldrq(p, field_polynomial); // The field polynomial ++ ++ ldrq(v0, Address(state)); ++ ldrq(Hprime, Address(subkeyH)); ++ ++ rev64(v0, T16B, v0); // Bit-reverse words in state and subkeyH ++ rbit(v0, T16B, v0); ++ rev64(Hprime, T16B, Hprime); ++ rbit(Hprime, T16B, Hprime); ++ ++ // Powers of H -> Hprime ++ ++ Label already_calculated, done; ++ { ++ // The first time around we'll have to calculate H**2, H**3, etc. ++ // Look at the largest power of H in the subkeyH array to see if ++ // it's already been calculated. ++ ldp(rscratch1, rscratch2, Address(subkeyH, 16 * (unrolls - 1))); ++ orr(rscratch1, rscratch1, rscratch2); ++ cbnz(rscratch1, already_calculated); ++ ++ orr(v6, T16B, Hprime, Hprime); // Start with H in v6 and Hprime ++ for (int i = 1; i < unrolls; i++) { ++ ext(a1_xor_a0, T16B, Hprime, Hprime, 0x08); // long-swap subkeyH into a1_xor_a0 ++ eor(a1_xor_a0, T16B, a1_xor_a0, Hprime); // xor subkeyH into subkeyL (Karatsuba: (A1+A0)) ++ ghash_modmul(/*result*/v6, /*result_lo*/v5, /*result_hi*/v4, /*b*/v6, ++ Hprime, vzr, a1_xor_a0, p, ++ /*temps*/v1, v3, v2); ++ rev64(v1, T16B, v6); ++ rbit(v1, T16B, v1); ++ strq(v1, Address(subkeyH, 16 * i)); ++ } ++ b(done); ++ } ++ { ++ bind(already_calculated); ++ ++ // Load the largest power of H we need into v6. ++ ldrq(v6, Address(subkeyH, 16 * (unrolls - 1))); ++ rev64(v6, T16B, v6); ++ rbit(v6, T16B, v6); ++ } ++ bind(done); ++ ++ orr(Hprime, T16B, v6, v6); // Move H ** unrolls into Hprime ++ ++ // Hprime contains (H ** 1, H ** 2, ... H ** unrolls) ++ // v0 contains the initial state. Clear the others. ++ for (int i = 1; i < unrolls; i++) { ++ int ofs = register_stride * i; ++ eor(ofs+v0, T16B, ofs+v0, ofs+v0); // zero each state register ++ } ++ ++ ext(a1_xor_a0, T16B, Hprime, Hprime, 0x08); // long-swap subkeyH into a1_xor_a0 ++ eor(a1_xor_a0, T16B, a1_xor_a0, Hprime); // xor subkeyH into subkeyL (Karatsuba: (A1+A0)) ++ ++ // Load #unrolls blocks of data ++ for (int ofs = 0; ofs < unrolls * register_stride; ofs += register_stride) { ++ ld1(v2+ofs, T16B, post(data, 0x10)); ++ } ++ ++ // Register assignments, replicated across 4 clones, v0 ... v23 ++ // ++ // v0: input / output: current state, result of multiply/reduce ++ // v1: temp ++ // v2: input: one block of data (the ciphertext) ++ // also used as a temp once the data has been consumed ++ // v3: temp ++ // v4: output: high part of product ++ // v5: output: low part ... ++ // v6: unused ++ // ++ // Not replicated: ++ // ++ // v28: High part of H xor low part of H' ++ // v29: H' (hash subkey) ++ // v30: zero ++ // v31: Reduction polynomial of the Galois field ++ ++ // Inner loop. ++ // Do the whole load/add/multiply/reduce over all our data except ++ // the last few rows. ++ { ++ Label L_ghash_loop; ++ bind(L_ghash_loop); ++ ++ // Prefetching doesn't help here. In fact, on Neoverse N1 it's worse. ++ // prfm(Address(data, 128), PLDL1KEEP); ++ ++ // Xor data into current state ++ for (int ofs = 0; ofs < unrolls * register_stride; ofs += register_stride) { ++ rbit((v2+ofs), T16B, (v2+ofs)); ++ eor((v2+ofs), T16B, v0+ofs, (v2+ofs)); // bit-swapped data ^ bit-swapped state ++ } ++ ++ // Generate fully-unrolled multiply-reduce in two stages. ++ ++ (new GHASHMultiplyGenerator(this, unrolls, ++ /*result_lo*/v5, /*result_hi*/v4, /*data*/v2, ++ Hprime, a1_xor_a0, p, vzr, ++ /*temps*/v1, v3, /* reuse b*/v2))->unroll(); ++ ++ // NB: GHASHReduceGenerator also loads the next #unrolls blocks of ++ // data into v0, v0+ofs, the current state. ++ (new GHASHReduceGenerator (this, unrolls, ++ /*result*/v0, /*lo*/v5, /*hi*/v4, p, vzr, ++ /*data*/v2, /*temp*/v3))->unroll(); ++ ++ sub(blocks, blocks, unrolls); ++ cmp(blocks, (unsigned char)(unrolls * 2)); ++ br(GE, L_ghash_loop); ++ } ++ ++ // Merge the #unrolls states. Note that the data for the next ++ // iteration has already been loaded into v4, v4+ofs, etc... ++ ++ // First, we multiply/reduce each clone by the appropriate power of H. ++ for (int i = 0; i < unrolls; i++) { ++ int ofs = register_stride * i; ++ ldrq(Hprime, Address(subkeyH, 16 * (unrolls - i - 1))); ++ ++ rbit(v2+ofs, T16B, v2+ofs); ++ eor(v2+ofs, T16B, ofs+v0, v2+ofs); // bit-swapped data ^ bit-swapped state ++ ++ rev64(Hprime, T16B, Hprime); ++ rbit(Hprime, T16B, Hprime); ++ ext(a1_xor_a0, T16B, Hprime, Hprime, 0x08); // long-swap subkeyH into a1_xor_a0 ++ eor(a1_xor_a0, T16B, a1_xor_a0, Hprime); // xor subkeyH into subkeyL (Karatsuba: (A1+A0)) ++ ghash_modmul(/*result*/v0+ofs, /*result_lo*/v5+ofs, /*result_hi*/v4+ofs, /*b*/v2+ofs, ++ Hprime, vzr, a1_xor_a0, p, ++ /*temps*/v1+ofs, v3+ofs, /* reuse b*/v2+ofs); ++ } ++ ++ // Then we sum the results. ++ for (int i = 0; i < unrolls - 1; i++) { ++ int ofs = register_stride * i; ++ eor(v0, T16B, v0, v0 + register_stride + ofs); ++ } ++ ++ sub(blocks, blocks, (unsigned char)unrolls); ++ ++ // And finally bit-reverse the state back to big endian. ++ rev64(v0, T16B, v0); ++ rbit(v0, T16B, v0); ++ st1(v0, T16B, state); ++} +\ No newline at end of file +diff --git a/hotspot/src/cpu/aarch64/vm/stubGenerator_aarch64.cpp b/hotspot/src/cpu/aarch64/vm/stubGenerator_aarch64.cpp +index 2e2e8ae78..c024dec55 100644 +--- a/hotspot/src/cpu/aarch64/vm/stubGenerator_aarch64.cpp ++++ b/hotspot/src/cpu/aarch64/vm/stubGenerator_aarch64.cpp +@@ -2804,6 +2804,266 @@ class StubGenerator: public StubCodeGenerator { + return start; + } + ++ // CTR AES crypt. ++ // Arguments: ++ // ++ // Inputs: ++ // c_rarg0 - source byte array address ++ // c_rarg1 - destination byte array address ++ // c_rarg2 - K (key) in little endian int array ++ // c_rarg3 - counter vector byte array address ++ // c_rarg4 - input length ++ // c_rarg5 - saved encryptedCounter start ++ // c_rarg6 - saved used length ++ // ++ // Output: ++ // r0 - input length ++ // ++ address generate_counterMode_AESCrypt() { ++ const Register in = c_rarg0; ++ const Register out = c_rarg1; ++ const Register key = c_rarg2; ++ const Register counter = c_rarg3; ++ const Register saved_len = c_rarg4, len = r10; ++ const Register saved_encrypted_ctr = c_rarg5; ++ const Register used_ptr = c_rarg6, used = r12; ++ ++ const Register offset = r7; ++ const Register keylen = r11; ++ ++ const unsigned char block_size = 16; ++ const int bulk_width = 4; ++ // NB: bulk_width can be 4 or 8. 8 gives slightly faster ++ // performance with larger data sizes, but it also means that the ++ // fast path isn't used until you have at least 8 blocks, and up ++ // to 127 bytes of data will be executed on the slow path. For ++ // that reason, and also so as not to blow away too much icache, 4 ++ // blocks seems like a sensible compromise. ++ ++ // Algorithm: ++ // ++ // if (len == 0) { ++ // goto DONE; ++ // } ++ // int result = len; ++ // do { ++ // if (used >= blockSize) { ++ // if (len >= bulk_width * blockSize) { ++ // CTR_large_block(); ++ // if (len == 0) ++ // goto DONE; ++ // } ++ // for (;;) { ++ // 16ByteVector v0 = counter; ++ // embeddedCipher.encryptBlock(v0, 0, encryptedCounter, 0); ++ // used = 0; ++ // if (len < blockSize) ++ // break; /* goto NEXT */ ++ // 16ByteVector v1 = load16Bytes(in, offset); ++ // v1 = v1 ^ encryptedCounter; ++ // store16Bytes(out, offset); ++ // used = blockSize; ++ // offset += blockSize; ++ // len -= blockSize; ++ // if (len == 0) ++ // goto DONE; ++ // } ++ // } ++ // NEXT: ++ // out[outOff++] = (byte)(in[inOff++] ^ encryptedCounter[used++]); ++ // len--; ++ // } while (len != 0); ++ // DONE: ++ // return result; ++ // ++ // CTR_large_block() ++ // Wide bulk encryption of whole blocks. ++ ++ __ align(CodeEntryAlignment); ++ StubCodeMark mark(this, "StubRoutines", "counterMode_AESCrypt"); ++ const address start = __ pc(); ++ __ enter(); ++ ++ Label DONE, CTR_large_block, large_block_return; ++ __ ldrw(used, Address(used_ptr)); ++ __ cbzw(saved_len, DONE); ++ ++ __ mov(len, saved_len); ++ __ mov(offset, 0); ++ ++ // Compute #rounds for AES based on the length of the key array ++ __ ldrw(keylen, Address(key, arrayOopDesc::length_offset_in_bytes() - arrayOopDesc::base_offset_in_bytes(T_INT))); ++ ++ __ aesenc_loadkeys(key, keylen); ++ ++ { ++ Label L_CTR_loop, NEXT; ++ ++ __ bind(L_CTR_loop); ++ ++ __ cmp(used, block_size); ++ __ br(__ LO, NEXT); ++ ++ // Maybe we have a lot of data ++ __ subsw(rscratch1, len, bulk_width * block_size); ++ __ br(__ HS, CTR_large_block); ++ __ BIND(large_block_return); ++ __ cbzw(len, DONE); ++ ++ // Setup the counter ++ __ movi(v4, __ T4S, 0); ++ __ movi(v5, __ T4S, 1); ++ __ ins(v4, __ S, v5, 3, 3); // v4 contains { 0, 0, 0, 1 } ++ ++ __ ld1(v0, __ T16B, counter); // Load the counter into v0 ++ __ rev32(v16, __ T16B, v0); ++ __ addv(v16, __ T4S, v16, v4); ++ __ rev32(v16, __ T16B, v16); ++ __ st1(v16, __ T16B, counter); // Save the incremented counter back ++ ++ { ++ // We have fewer than bulk_width blocks of data left. Encrypt ++ // them one by one until there is less than a full block ++ // remaining, being careful to save both the encrypted counter ++ // and the counter. ++ ++ Label inner_loop; ++ __ bind(inner_loop); ++ // Counter to encrypt is in v0 ++ __ aesecb_encrypt(noreg, noreg, keylen); ++ __ st1(v0, __ T16B, saved_encrypted_ctr); ++ ++ // Do we have a remaining full block? ++ ++ __ mov(used, 0); ++ __ cmp(len, block_size); ++ __ br(__ LO, NEXT); ++ ++ // Yes, we have a full block ++ __ ldrq(v1, Address(in, offset)); ++ __ eor(v1, __ T16B, v1, v0); ++ __ strq(v1, Address(out, offset)); ++ __ mov(used, block_size); ++ __ add(offset, offset, block_size); ++ ++ __ subw(len, len, block_size); ++ __ cbzw(len, DONE); ++ ++ // Increment the counter, store it back ++ __ orr(v0, __ T16B, v16, v16); ++ __ rev32(v16, __ T16B, v16); ++ __ addv(v16, __ T4S, v16, v4); ++ __ rev32(v16, __ T16B, v16); ++ __ st1(v16, __ T16B, counter); // Save the incremented counter back ++ ++ __ b(inner_loop); ++ } ++ ++ __ BIND(NEXT); ++ ++ // Encrypt a single byte, and loop. ++ // We expect this to be a rare event. ++ __ ldrb(rscratch1, Address(in, offset)); ++ __ ldrb(rscratch2, Address(saved_encrypted_ctr, used)); ++ __ eor(rscratch1, rscratch1, rscratch2); ++ __ strb(rscratch1, Address(out, offset)); ++ __ add(offset, offset, 1); ++ __ add(used, used, 1); ++ __ subw(len, len,1); ++ __ cbnzw(len, L_CTR_loop); ++ } ++ ++ __ bind(DONE); ++ __ strw(used, Address(used_ptr)); ++ __ mov(r0, saved_len); ++ ++ __ leave(); // required for proper stackwalking of RuntimeStub frame ++ __ ret(lr); ++ ++ // Bulk encryption ++ ++ __ BIND (CTR_large_block); ++ assert(bulk_width == 4 || bulk_width == 8, "must be"); ++ ++ if (bulk_width == 8) { ++ __ sub(sp, sp, 4 * 16); ++ __ st1(v12, v13, v14, v15, __ T16B, Address(sp)); ++ } ++ __ sub(sp, sp, 4 * 16); ++ __ st1(v8, v9, v10, v11, __ T16B, Address(sp)); ++ RegSet saved_regs = (RegSet::of(in, out, offset) ++ + RegSet::of(saved_encrypted_ctr, used_ptr, len)); ++ __ push(saved_regs, sp); ++ __ andr(len, len, -16 * bulk_width); // 8/4 encryptions, 16 bytes per encryption ++ __ add(in, in, offset); ++ __ add(out, out, offset); ++ ++ // Keys should already be loaded into the correct registers ++ ++ __ ld1(v0, __ T16B, counter); // v0 contains the first counter ++ __ rev32(v16, __ T16B, v0); // v16 contains byte-reversed counter ++ ++ // AES/CTR loop ++ { ++ Label L_CTR_loop; ++ __ BIND(L_CTR_loop); ++ ++ // Setup the counters ++ __ movi(v8, __ T4S, 0); ++ __ movi(v9, __ T4S, 1); ++ __ ins(v8, __ S, v9, 3, 3); // v8 contains { 0, 0, 0, 1 } ++ ++ for (FloatRegister f = v0; f < v0 + bulk_width; f++) { ++ __ rev32(f, __ T16B, v16); ++ __ addv(v16, __ T4S, v16, v8); ++ } ++ ++ __ ld1(v8, v9, v10, v11, __ T16B, __ post(in, 4 * 16)); ++ ++ // Encrypt the counters ++ __ aesecb_encrypt(noreg, noreg, keylen, v0, bulk_width); ++ ++ if (bulk_width == 8) { ++ __ ld1(v12, v13, v14, v15, __ T16B, __ post(in, 4 * 16)); ++ } ++ ++ // XOR the encrypted counters with the inputs ++ for (int i = 0; i < bulk_width; i++) { ++ __ eor(v0 + i, __ T16B, v0 + i, v8 + i); ++ } ++ ++ // Write the encrypted data ++ __ st1(v0, v1, v2, v3, __ T16B, __ post(out, 4 * 16)); ++ if (bulk_width == 8) { ++ __ st1(v4, v5, v6, v7, __ T16B, __ post(out, 4 * 16)); ++ } ++ ++ __ subw(len, len, 16 * bulk_width); ++ __ cbnzw(len, L_CTR_loop); ++ } ++ ++ // Save the counter back where it goes ++ __ rev32(v16, __ T16B, v16); ++ __ st1(v16, __ T16B, counter); ++ ++ __ pop(saved_regs, sp); ++ ++ __ ld1(v8, v9, v10, v11, __ T16B, __ post(sp, 4 * 16)); ++ if (bulk_width == 8) { ++ __ ld1(v12, v13, v14, v15, __ T16B, __ post(sp, 4 * 16)); ++ } ++ ++ __ andr(rscratch1, len, -16 * bulk_width); ++ __ sub(len, len, rscratch1); ++ __ add(offset, offset, rscratch1); ++ __ mov(used, 16); ++ __ strw(used, Address(used_ptr)); ++ __ b(large_block_return); ++ ++ return start; ++ } ++ ++ + // Arguments: + // + // Inputs: +@@ -3677,6 +3937,56 @@ class StubGenerator: public StubCodeGenerator { + return start; + } + ++ address generate_ghash_processBlocks_wide() { ++ address small = generate_ghash_processBlocks(); ++ ++ StubCodeMark mark(this, "StubRoutines", "ghash_processBlocks_wide"); ++ __ align(wordSize * 2); ++ address p = __ pc(); ++ __ emit_int64(0x87); // The low-order bits of the field ++ // polynomial (i.e. p = z^7+z^2+z+1) ++ // repeated in the low and high parts of a ++ // 128-bit vector ++ __ emit_int64(0x87); ++ ++ __ align(CodeEntryAlignment); ++ address start = __ pc(); ++ ++ Register state = c_rarg0; ++ Register subkeyH = c_rarg1; ++ Register data = c_rarg2; ++ Register blocks = c_rarg3; ++ ++ const int unroll = 4; ++ ++ __ cmp(blocks, (unsigned char)(unroll * 2)); ++ __ br(__ LT, small); ++ ++ if (unroll > 1) { ++ // Save state before entering routine ++ __ sub(sp, sp, 4 * 16); ++ __ st1(v12, v13, v14, v15, __ T16B, Address(sp)); ++ __ sub(sp, sp, 4 * 16); ++ __ st1(v8, v9, v10, v11, __ T16B, Address(sp)); ++ } ++ ++ __ ghash_processBlocks_wide(p, state, subkeyH, data, blocks, unroll); ++ ++ if (unroll > 1) { ++ // And restore state ++ __ ld1(v8, v9, v10, v11, __ T16B, __ post(sp, 4 * 16)); ++ __ ld1(v12, v13, v14, v15, __ T16B, __ post(sp, 4 * 16)); ++ } ++ ++ __ cmp(blocks, 0u); ++ __ br(__ GT, small); ++ ++ __ ret(lr); ++ ++ return start; ++ } ++ ++ + // Continuation point for throwing of implicit exceptions that are + // not handled in the current activation. Fabricates an exception + // oop and initiates normal exception dispatching in this +@@ -4687,6 +4997,15 @@ class StubGenerator: public StubCodeGenerator { + StubRoutines::_montgomerySquare = g.generate_multiply(); + } + ++ // generate GHASH intrinsics code ++ if (UseGHASHIntrinsics) { ++ if (UseAESCTRIntrinsics) { ++ StubRoutines::_ghash_processBlocks = generate_ghash_processBlocks_wide(); ++ } else { ++ StubRoutines::_ghash_processBlocks = generate_ghash_processBlocks(); ++ } ++ } ++ + if (UseAESIntrinsics) { + StubRoutines::_aescrypt_encryptBlock = generate_aescrypt_encryptBlock(); + StubRoutines::_aescrypt_decryptBlock = generate_aescrypt_decryptBlock(); +@@ -4694,9 +5013,8 @@ class StubGenerator: public StubCodeGenerator { + StubRoutines::_cipherBlockChaining_decryptAESCrypt = generate_cipherBlockChaining_decryptAESCrypt(); + } + +- // generate GHASH intrinsics code +- if (UseGHASHIntrinsics) { +- StubRoutines::_ghash_processBlocks = generate_ghash_processBlocks(); ++ if (UseAESCTRIntrinsics) { ++ StubRoutines::_counterMode_AESCrypt = generate_counterMode_AESCrypt(); + } + + if (UseSHA1Intrinsics) { +diff --git a/hotspot/src/cpu/aarch64/vm/stubRoutines_aarch64.hpp b/hotspot/src/cpu/aarch64/vm/stubRoutines_aarch64.hpp +index d1c312ab3..05619ce7f 100644 +--- a/hotspot/src/cpu/aarch64/vm/stubRoutines_aarch64.hpp ++++ b/hotspot/src/cpu/aarch64/vm/stubRoutines_aarch64.hpp +@@ -37,7 +37,7 @@ static bool returns_to_call_stub(address return_pc) { + + enum platform_dependent_constants { + code_size1 = 19000, // simply increase if too small (assembler will crash if too small) +- code_size2 = 22000 // simply increase if too small (assembler will crash if too small) ++ code_size2 = 32000 // simply increase if too small (assembler will crash if too small) + }; + + class aarch64 { +diff --git a/hotspot/src/cpu/aarch64/vm/vm_version_aarch64.cpp b/hotspot/src/cpu/aarch64/vm/vm_version_aarch64.cpp +index 9808337a0..de636fb83 100644 +--- a/hotspot/src/cpu/aarch64/vm/vm_version_aarch64.cpp ++++ b/hotspot/src/cpu/aarch64/vm/vm_version_aarch64.cpp +@@ -233,12 +233,21 @@ void VM_Version::get_processor_features() { + warning("UseAESIntrinsics enabled, but UseAES not, enabling"); + UseAES = true; + } ++ if (FLAG_IS_DEFAULT(UseAESCTRIntrinsics)) { ++ FLAG_SET_DEFAULT(UseAESCTRIntrinsics, false); ++ } + } else { + if (UseAES) { +- warning("UseAES specified, but not supported on this CPU"); ++ warning("AES instructions are not available on this CPU"); ++ FLAG_SET_DEFAULT(UseAES, false); + } + if (UseAESIntrinsics) { +- warning("UseAESIntrinsics specified, but not supported on this CPU"); ++ warning("AES intrinsics are not available on this CPU"); ++ FLAG_SET_DEFAULT(UseAESIntrinsics, false); ++ } ++ if (UseAESCTRIntrinsics) { ++ warning("AES/CTR intrinsics are not available on this CPU"); ++ FLAG_SET_DEFAULT(UseAESCTRIntrinsics, false); + } + } + +diff --git a/hotspot/src/cpu/ppc/vm/vm_version_ppc.cpp b/hotspot/src/cpu/ppc/vm/vm_version_ppc.cpp +index b5ce1cfa9..fea8b1f87 100644 +--- a/hotspot/src/cpu/ppc/vm/vm_version_ppc.cpp ++++ b/hotspot/src/cpu/ppc/vm/vm_version_ppc.cpp +@@ -194,6 +194,11 @@ void VM_Version::initialize() { + FLAG_SET_DEFAULT(UseAESIntrinsics, false); + } + ++ if (UseAESCTRIntrinsics) { ++ warning("AES/CTR intrinsics are not available on this CPU"); ++ FLAG_SET_DEFAULT(UseAESCTRIntrinsics, false); ++ } ++ + if (UseGHASHIntrinsics) { + warning("GHASH intrinsics are not available on this CPU"); + FLAG_SET_DEFAULT(UseGHASHIntrinsics, false); +diff --git a/hotspot/src/cpu/sparc/vm/vm_version_sparc.cpp b/hotspot/src/cpu/sparc/vm/vm_version_sparc.cpp +index bd893e138..08d7a7311 100644 +--- a/hotspot/src/cpu/sparc/vm/vm_version_sparc.cpp ++++ b/hotspot/src/cpu/sparc/vm/vm_version_sparc.cpp +@@ -319,6 +319,11 @@ void VM_Version::initialize() { + } + } + ++ if (UseAESCTRIntrinsics) { ++ warning("AES/CTR intrinsics are not available on this CPU"); ++ FLAG_SET_DEFAULT(UseAESCTRIntrinsics, false); ++ } ++ + // GHASH/GCM intrinsics + if (has_vis3() && (UseVIS > 2)) { + if (FLAG_IS_DEFAULT(UseGHASHIntrinsics)) { +diff --git a/hotspot/src/cpu/x86/vm/assembler_x86.cpp b/hotspot/src/cpu/x86/vm/assembler_x86.cpp +index 1759ecdfd..ddc1acfd8 100644 +--- a/hotspot/src/cpu/x86/vm/assembler_x86.cpp ++++ b/hotspot/src/cpu/x86/vm/assembler_x86.cpp +@@ -2373,20 +2373,52 @@ void Assembler::pcmpestri(XMMRegister dst, XMMRegister src, int imm8) { + + void Assembler::pextrd(Register dst, XMMRegister src, int imm8) { + assert(VM_Version::supports_sse4_1(), ""); +- int encode = simd_prefix_and_encode(as_XMMRegister(dst->encoding()), xnoreg, src, VEX_SIMD_66, VEX_OPCODE_0F_3A, false); ++ int encode = simd_prefix_and_encode(src, xnoreg, as_XMMRegister(dst->encoding()), VEX_SIMD_66, VEX_OPCODE_0F_3A, false); + emit_int8(0x16); + emit_int8((unsigned char)(0xC0 | encode)); + emit_int8(imm8); + } + ++void Assembler::pextrd(Address dst, XMMRegister src, int imm8) { ++ assert(VM_Version::supports_sse4_1(), ""); ++ simd_prefix(src, xnoreg, dst, VEX_SIMD_66, VEX_OPCODE_0F_3A, false); ++ emit_int8(0x16); ++ emit_operand(src, dst); ++ emit_int8(imm8); ++} ++ + void Assembler::pextrq(Register dst, XMMRegister src, int imm8) { + assert(VM_Version::supports_sse4_1(), ""); +- int encode = simd_prefix_and_encode(as_XMMRegister(dst->encoding()), xnoreg, src, VEX_SIMD_66, VEX_OPCODE_0F_3A, true); ++ int encode = simd_prefix_and_encode(src, xnoreg, as_XMMRegister(dst->encoding()), VEX_SIMD_66, VEX_OPCODE_0F_3A, true); + emit_int8(0x16); + emit_int8((unsigned char)(0xC0 | encode)); + emit_int8(imm8); + } + ++void Assembler::pextrq(Address dst, XMMRegister src, int imm8) { ++ assert(VM_Version::supports_sse4_1(), ""); ++ simd_prefix(src, xnoreg, dst, VEX_SIMD_66, VEX_OPCODE_0F_3A, true); ++ emit_int8(0x16); ++ emit_operand(src, dst); ++ emit_int8(imm8); ++} ++ ++void Assembler::pextrw(Address dst, XMMRegister src, int imm8) { ++ assert(VM_Version::supports_sse4_1(), ""); ++ simd_prefix(src, xnoreg, dst, VEX_SIMD_66, VEX_OPCODE_0F_3A); ++ emit_int8((unsigned char)0x15); ++ emit_operand(src, dst); ++ emit_int8(imm8); ++} ++ ++void Assembler::pextrb(Address dst, XMMRegister src, int imm8) { ++ assert(VM_Version::supports_sse4_1(), ""); ++ simd_prefix(src, xnoreg, dst, VEX_SIMD_66, VEX_OPCODE_0F_3A); ++ emit_int8(0x14); ++ emit_operand(src, dst); ++ emit_int8(imm8); ++} ++ + void Assembler::pinsrd(XMMRegister dst, Register src, int imm8) { + assert(VM_Version::supports_sse4_1(), ""); + int encode = simd_prefix_and_encode(dst, dst, as_XMMRegister(src->encoding()), VEX_SIMD_66, VEX_OPCODE_0F_3A, false); +@@ -2395,6 +2427,14 @@ void Assembler::pinsrd(XMMRegister dst, Register src, int imm8) { + emit_int8(imm8); + } + ++void Assembler::pinsrd(XMMRegister dst, Address src, int imm8) { ++ assert(VM_Version::supports_sse4_1(), ""); ++ simd_prefix(dst, dst, src, VEX_SIMD_66, VEX_OPCODE_0F_3A, false); ++ emit_int8(0x22); ++ emit_operand(dst,src); ++ emit_int8(imm8); ++} ++ + void Assembler::pinsrq(XMMRegister dst, Register src, int imm8) { + assert(VM_Version::supports_sse4_1(), ""); + int encode = simd_prefix_and_encode(dst, dst, as_XMMRegister(src->encoding()), VEX_SIMD_66, VEX_OPCODE_0F_3A, true); +@@ -2403,6 +2443,30 @@ void Assembler::pinsrq(XMMRegister dst, Register src, int imm8) { + emit_int8(imm8); + } + ++void Assembler::pinsrq(XMMRegister dst, Address src, int imm8) { ++ assert(VM_Version::supports_sse4_1(), ""); ++ simd_prefix(dst, dst, src, VEX_SIMD_66, VEX_OPCODE_0F_3A, true); ++ emit_int8(0x22); ++ emit_operand(dst, src); ++ emit_int8(imm8); ++} ++ ++void Assembler::pinsrw(XMMRegister dst, Address src, int imm8) { ++ assert(VM_Version::supports_sse2(), ""); ++ simd_prefix(dst, dst, src, VEX_SIMD_66, VEX_OPCODE_0F); ++ emit_int8((unsigned char)0xC4); ++ emit_operand(dst, src); ++ emit_int8(imm8); ++} ++ ++void Assembler::pinsrb(XMMRegister dst, Address src, int imm8) { ++ assert(VM_Version::supports_sse4_1(), ""); ++ simd_prefix(dst, dst, src, VEX_SIMD_66, VEX_OPCODE_0F_3A); ++ emit_int8(0x20); ++ emit_operand(dst, src); ++ emit_int8(imm8); ++} ++ + void Assembler::pmovzxbw(XMMRegister dst, Address src) { + assert(VM_Version::supports_sse4_1(), ""); + InstructionMark im(this); +@@ -3075,6 +3139,12 @@ void Assembler::xorl(Register dst, Register src) { + emit_arith(0x33, 0xC0, dst, src); + } + ++void Assembler::xorb(Register dst, Address src) { ++ InstructionMark im(this); ++ prefix(src, dst); ++ emit_int8(0x32); ++ emit_operand(dst, src); ++} + + // AVX 3-operands scalar float-point arithmetic instructions + +diff --git a/hotspot/src/cpu/x86/vm/assembler_x86.hpp b/hotspot/src/cpu/x86/vm/assembler_x86.hpp +index 5ea01311e..c2e70bc2a 100644 +--- a/hotspot/src/cpu/x86/vm/assembler_x86.hpp ++++ b/hotspot/src/cpu/x86/vm/assembler_x86.hpp +@@ -1479,10 +1479,20 @@ private: + // SSE 4.1 extract + void pextrd(Register dst, XMMRegister src, int imm8); + void pextrq(Register dst, XMMRegister src, int imm8); ++ void pextrd(Address dst, XMMRegister src, int imm8); ++ void pextrq(Address dst, XMMRegister src, int imm8); ++ void pextrb(Address dst, XMMRegister src, int imm8); ++ // SSE 2 extract ++ void pextrw(Address dst, XMMRegister src, int imm8); + + // SSE 4.1 insert + void pinsrd(XMMRegister dst, Register src, int imm8); + void pinsrq(XMMRegister dst, Register src, int imm8); ++ void pinsrd(XMMRegister dst, Address src, int imm8); ++ void pinsrq(XMMRegister dst, Address src, int imm8); ++ void pinsrb(XMMRegister dst, Address src, int imm8); ++ // SSE 2 insert ++ void pinsrw(XMMRegister dst, Address src, int imm8); + + // SSE4.1 packed move + void pmovzxbw(XMMRegister dst, XMMRegister src); +@@ -1687,6 +1697,8 @@ private: + void xorl(Register dst, Address src); + void xorl(Register dst, Register src); + ++ void xorb(Register dst, Address src); ++ + void xorq(Register dst, Address src); + void xorq(Register dst, Register src); + +diff --git a/hotspot/src/cpu/x86/vm/stubGenerator_x86_32.cpp b/hotspot/src/cpu/x86/vm/stubGenerator_x86_32.cpp +index 2e5599807..f555f3326 100644 +--- a/hotspot/src/cpu/x86/vm/stubGenerator_x86_32.cpp ++++ b/hotspot/src/cpu/x86/vm/stubGenerator_x86_32.cpp +@@ -2153,6 +2153,17 @@ class StubGenerator: public StubCodeGenerator { + return start; + } + ++ address generate_counter_shuffle_mask() { ++ __ align(16); ++ StubCodeMark mark(this, "StubRoutines", "counter_shuffle_mask"); ++ address start = __ pc(); ++ __ emit_data(0x0c0d0e0f, relocInfo::none, 0); ++ __ emit_data(0x08090a0b, relocInfo::none, 0); ++ __ emit_data(0x04050607, relocInfo::none, 0); ++ __ emit_data(0x00010203, relocInfo::none, 0); ++ return start; ++ } ++ + // Utility routine for loading a 128-bit key word in little endian format + // can optionally specify that the shuffle mask is already in an xmmregister + void load_key(XMMRegister xmmdst, Register key, int offset, XMMRegister xmm_shuf_mask=NULL) { +@@ -2178,6 +2189,31 @@ class StubGenerator: public StubCodeGenerator { + __ aesdec(xmmdst, xmmtmp); + } + ++ // Utility routine for increase 128bit counter (iv in CTR mode) ++ // XMM_128bit, D3, D2, D1, D0 ++ void inc_counter(Register reg, XMMRegister xmmdst, int inc_delta, Label& next_block) { ++ __ pextrd(reg, xmmdst, 0x0); ++ __ addl(reg, inc_delta); ++ __ pinsrd(xmmdst, reg, 0x0); ++ __ jcc(Assembler::carryClear, next_block); // jump if no carry ++ ++ __ pextrd(reg, xmmdst, 0x01); // Carry-> D1 ++ __ addl(reg, 0x01); ++ __ pinsrd(xmmdst, reg, 0x01); ++ __ jcc(Assembler::carryClear, next_block); // jump if no carry ++ ++ __ pextrd(reg, xmmdst, 0x02); // Carry-> D2 ++ __ addl(reg, 0x01); ++ __ pinsrd(xmmdst, reg, 0x02); ++ __ jcc(Assembler::carryClear, next_block); // jump if no carry ++ ++ __ pextrd(reg, xmmdst, 0x03); // Carry -> D3 ++ __ addl(reg, 0x01); ++ __ pinsrd(xmmdst, reg, 0x03); ++ ++ __ BIND(next_block); // next instruction ++ } ++ + + // Arguments: + // +@@ -2719,6 +2755,309 @@ class StubGenerator: public StubCodeGenerator { + return start; + } + ++ ++ // CTR AES crypt. ++ // In 32-bit stub, parallelize 4 blocks at a time ++ // Arguments: ++ // ++ // Inputs: ++ // c_rarg0 - source byte array address ++ // c_rarg1 - destination byte array address ++ // c_rarg2 - K (key) in little endian int array ++ // c_rarg3 - counter vector byte array address ++ // c_rarg4 - input length ++ // ++ // Output: ++ // rax - input length ++ // ++ address generate_counterMode_AESCrypt_Parallel() { ++ assert(UseAES, "need AES instructions and misaligned SSE support"); ++ __ align(CodeEntryAlignment); ++ StubCodeMark mark(this, "StubRoutines", "counterMode_AESCrypt"); ++ address start = __ pc(); ++ const Register from = rsi; // source array address ++ const Register to = rdx; // destination array address ++ const Register key = rcx; // key array address ++ const Register counter = rdi; // counter byte array initialized from initvector array address ++ ++ // and left with the results of the last encryption block ++ const Register len_reg = rbx; ++ const Register pos = rax; ++ ++ __ enter(); // required for proper stackwalking of RuntimeStub frame ++ handleSOERegisters(true /*saving*/); // save rbx, rsi, rdi ++ ++ // load registers from incoming parameters ++ const Address from_param(rbp, 8+0); ++ const Address to_param (rbp, 8+4); ++ const Address key_param (rbp, 8+8); ++ const Address rvec_param (rbp, 8+12); ++ const Address len_param (rbp, 8+16); ++ const Address saved_counter_param(rbp, 8 + 20); ++ const Address used_addr_param(rbp, 8 + 24); ++ ++ __ movptr(from , from_param); ++ __ movptr(to , to_param); ++ //__ movptr(key, key_param); ++ //__ movptr(counter, rvec_param); ++ __ movptr(len_reg , len_param); ++ //__ movptr(pos, 0); ++ ++ // Use the partially used encrpyted counter from last invocation ++ Label L_exit_preLoop, L_preLoop_start; ++ ++ // Use the registers 'counter' and 'key' here in this preloop ++ // to hold of last 2 params 'used' and 'saved_encCounter_start' ++ Register used = counter; ++ Register saved_encCounter_start = key; ++ Register used_addr = saved_encCounter_start; ++ ++ __ movptr(used_addr, used_addr_param); ++ __ movptr(used, Address(used_addr, 0)); ++ __ movptr(saved_encCounter_start, saved_counter_param); ++ ++ __ BIND(L_preLoop_start); ++ __ cmpptr(used, 16); ++ __ jcc(Assembler::aboveEqual, L_exit_preLoop); ++ __ cmpptr(len_reg, 0); ++ __ jcc(Assembler::lessEqual, L_exit_preLoop); ++ __ movb(rax, Address(saved_encCounter_start, used)); ++ __ xorb(rax, Address(from, 0)); ++ __ movb(Address(to, 0), rax); ++ __ addptr(from, 1); ++ __ addptr(to, 1); ++ __ addptr(used, 1); ++ __ subptr(len_reg, 1); ++ ++ __ jmp(L_preLoop_start); ++ ++ __ BIND(L_exit_preLoop); ++ __ movptr(used_addr, used_addr_param); ++ __ movptr(used_addr, used_addr_param); ++ __ movl(Address(used_addr, 0), used); ++ ++ // load the parameters 'key' and 'counter' ++ __ movptr(key, key_param); ++ __ movptr(counter, rvec_param); ++ ++ // xmm register assignments for the loops below ++ const XMMRegister xmm_curr_counter = xmm0; ++ const XMMRegister xmm_counter_shuf_mask = xmm1; // need to be reloaded ++ const XMMRegister xmm_key_shuf_mask = xmm2; // need to be reloaded ++ const XMMRegister xmm_key = xmm3; ++ const XMMRegister xmm_result0 = xmm4; ++ const XMMRegister xmm_result1 = xmm5; ++ const XMMRegister xmm_result2 = xmm6; ++ const XMMRegister xmm_result3 = xmm7; ++ const XMMRegister xmm_from0 = xmm1; //reuse XMM register ++ const XMMRegister xmm_from1 = xmm2; ++ const XMMRegister xmm_from2 = xmm3; ++ const XMMRegister xmm_from3 = xmm4; ++ ++ //for key_128, key_192, key_256 ++ const int rounds[3] = {10, 12, 14}; ++ Label L_singleBlockLoopTop[3]; ++ Label L_multiBlock_loopTop[3]; ++ Label L_key192_top, L_key256_top; ++ Label L_incCounter[3][4]; // 3: different key length, 4: 4 blocks at a time ++ Label L_incCounter_single[3]; //for single block, key128, key192, key256 ++ Label L_processTail_insr[3], L_processTail_4_insr[3], L_processTail_2_insr[3], L_processTail_1_insr[3], L_processTail_exit_insr[3]; ++ Label L_processTail_extr[3], L_processTail_4_extr[3], L_processTail_2_extr[3], L_processTail_1_extr[3], L_processTail_exit_extr[3]; ++ ++ Label L_exit; ++ const int PARALLEL_FACTOR = 4; //because of the limited register number ++ ++ // initialize counter with initial counter ++ __ movdqu(xmm_curr_counter, Address(counter, 0x00)); ++ __ movdqu(xmm_counter_shuf_mask, ExternalAddress(StubRoutines::x86::counter_shuffle_mask_addr())); ++ __ pshufb(xmm_curr_counter, xmm_counter_shuf_mask); //counter is shuffled for increase ++ ++ // key length could be only {11, 13, 15} * 4 = {44, 52, 60} ++ __ movdqu(xmm_key_shuf_mask, ExternalAddress(StubRoutines::x86::key_shuffle_mask_addr())); ++ __ movl(rax, Address(key, arrayOopDesc::length_offset_in_bytes() - arrayOopDesc::base_offset_in_bytes(T_INT))); ++ __ cmpl(rax, 52); ++ __ jcc(Assembler::equal, L_key192_top); ++ __ cmpl(rax, 60); ++ __ jcc(Assembler::equal, L_key256_top); ++ ++ //key128 begins here ++ __ movptr(pos, 0); // init pos before L_multiBlock_loopTop ++ ++#define CTR_DoFour(opc, src_reg) \ ++ __ opc(xmm_result0, src_reg); \ ++ __ opc(xmm_result1, src_reg); \ ++ __ opc(xmm_result2, src_reg); \ ++ __ opc(xmm_result3, src_reg); ++ ++ // k == 0 : generate code for key_128 ++ // k == 1 : generate code for key_192 ++ // k == 2 : generate code for key_256 ++ for (int k = 0; k < 3; ++k) { ++ //multi blocks starts here ++ __ align(OptoLoopAlignment); ++ __ BIND(L_multiBlock_loopTop[k]); ++ __ cmpptr(len_reg, PARALLEL_FACTOR * AESBlockSize); // see if at least PARALLEL_FACTOR blocks left ++ __ jcc(Assembler::less, L_singleBlockLoopTop[k]); ++ ++ __ movdqu(xmm_key_shuf_mask, ExternalAddress(StubRoutines::x86::key_shuffle_mask_addr())); ++ __ movdqu(xmm_counter_shuf_mask, ExternalAddress(StubRoutines::x86::counter_shuffle_mask_addr())); ++ ++ //load, then increase counters ++ CTR_DoFour(movdqa, xmm_curr_counter); ++ __ push(rbx); ++ inc_counter(rbx, xmm_result1, 0x01, L_incCounter[k][0]); ++ inc_counter(rbx, xmm_result2, 0x02, L_incCounter[k][1]); ++ inc_counter(rbx, xmm_result3, 0x03, L_incCounter[k][2]); ++ inc_counter(rbx, xmm_curr_counter, 0x04, L_incCounter[k][3]); ++ __ pop (rbx); ++ ++ load_key(xmm_key, key, 0x00, xmm_key_shuf_mask); // load Round 0 key. interleaving for better performance ++ ++ CTR_DoFour(pshufb, xmm_counter_shuf_mask); // after increased, shuffled counters back for PXOR ++ CTR_DoFour(pxor, xmm_key); //PXOR with Round 0 key ++ ++ for (int i = 1; i < rounds[k]; ++i) { ++ load_key(xmm_key, key, (0x10 * i), xmm_key_shuf_mask); ++ CTR_DoFour(aesenc, xmm_key); ++ } ++ load_key(xmm_key, key, (0x10 * rounds[k]), xmm_key_shuf_mask); ++ CTR_DoFour(aesenclast, xmm_key); ++ ++ // get next PARALLEL_FACTOR blocks into xmm_from registers ++ __ movdqu(xmm_from0, Address(from, pos, Address::times_1, 0 * AESBlockSize)); ++ __ movdqu(xmm_from1, Address(from, pos, Address::times_1, 1 * AESBlockSize)); ++ __ movdqu(xmm_from2, Address(from, pos, Address::times_1, 2 * AESBlockSize)); ++ ++ // PXOR with input text ++ __ pxor(xmm_result0, xmm_from0); //result0 is xmm4 ++ __ pxor(xmm_result1, xmm_from1); ++ __ pxor(xmm_result2, xmm_from2); ++ ++ // store PARALLEL_FACTOR results into the next 64 bytes of output ++ __ movdqu(Address(to, pos, Address::times_1, 0 * AESBlockSize), xmm_result0); ++ __ movdqu(Address(to, pos, Address::times_1, 1 * AESBlockSize), xmm_result1); ++ __ movdqu(Address(to, pos, Address::times_1, 2 * AESBlockSize), xmm_result2); ++ ++ // do it here after xmm_result0 is saved, because xmm_from3 reuse the same register of xmm_result0. ++ __ movdqu(xmm_from3, Address(from, pos, Address::times_1, 3 * AESBlockSize)); ++ __ pxor(xmm_result3, xmm_from3); ++ __ movdqu(Address(to, pos, Address::times_1, 3 * AESBlockSize), xmm_result3); ++ ++ __ addptr(pos, PARALLEL_FACTOR * AESBlockSize); // increase the length of crypt text ++ __ subptr(len_reg, PARALLEL_FACTOR * AESBlockSize); // decrease the remaining length ++ __ jmp(L_multiBlock_loopTop[k]); ++ ++ // singleBlock starts here ++ __ align(OptoLoopAlignment); ++ __ BIND(L_singleBlockLoopTop[k]); ++ __ cmpptr(len_reg, 0); ++ __ jcc(Assembler::equal, L_exit); ++ __ movdqu(xmm_key_shuf_mask, ExternalAddress(StubRoutines::x86::key_shuffle_mask_addr())); ++ __ movdqu(xmm_counter_shuf_mask, ExternalAddress(StubRoutines::x86::counter_shuffle_mask_addr())); ++ __ movdqa(xmm_result0, xmm_curr_counter); ++ load_key(xmm_key, key, 0x00, xmm_key_shuf_mask); ++ __ push(rbx);//rbx is used for increasing counter ++ inc_counter(rbx, xmm_curr_counter, 0x01, L_incCounter_single[k]); ++ __ pop (rbx); ++ __ pshufb(xmm_result0, xmm_counter_shuf_mask); ++ __ pxor(xmm_result0, xmm_key); ++ for (int i = 1; i < rounds[k]; i++) { ++ load_key(xmm_key, key, (0x10 * i), xmm_key_shuf_mask); ++ __ aesenc(xmm_result0, xmm_key); ++ } ++ load_key(xmm_key, key, (0x10 * rounds[k]), xmm_key_shuf_mask); ++ __ aesenclast(xmm_result0, xmm_key); ++ __ cmpptr(len_reg, AESBlockSize); ++ __ jcc(Assembler::less, L_processTail_insr[k]); ++ __ movdqu(xmm_from0, Address(from, pos, Address::times_1, 0 * AESBlockSize)); ++ __ pxor(xmm_result0, xmm_from0); ++ __ movdqu(Address(to, pos, Address::times_1, 0 * AESBlockSize), xmm_result0); ++ __ addptr(pos, AESBlockSize); ++ __ subptr(len_reg, AESBlockSize); ++ __ jmp(L_singleBlockLoopTop[k]); ++ ++ __ BIND(L_processTail_insr[k]); ++ __ addptr(pos, len_reg); ++ __ testptr(len_reg, 8); ++ __ jcc(Assembler::zero, L_processTail_4_insr[k]); ++ __ subptr(pos,8); ++ __ pinsrd(xmm_from0, Address(from, pos), 0); ++ __ pinsrd(xmm_from0, Address(from, pos, Address::times_1, 4), 1); ++ __ BIND(L_processTail_4_insr[k]); ++ __ testptr(len_reg, 4); ++ __ jcc(Assembler::zero, L_processTail_2_insr[k]); ++ __ subptr(pos,4); ++ __ pslldq(xmm_from0, 4); ++ __ pinsrd(xmm_from0, Address(from, pos), 0); ++ __ BIND(L_processTail_2_insr[k]); ++ __ testptr(len_reg, 2); ++ __ jcc(Assembler::zero, L_processTail_1_insr[k]); ++ __ subptr(pos, 2); ++ __ pslldq(xmm_from0, 2); ++ __ pinsrw(xmm_from0, Address(from, pos), 0); ++ __ BIND(L_processTail_1_insr[k]); ++ __ testptr(len_reg, 1); ++ __ jcc(Assembler::zero, L_processTail_exit_insr[k]); ++ __ subptr(pos, 1); ++ __ pslldq(xmm_from0, 1); ++ __ pinsrb(xmm_from0, Address(from, pos), 0); ++ __ BIND(L_processTail_exit_insr[k]); ++ ++ __ movptr(saved_encCounter_start, saved_counter_param); ++ __ movdqu(Address(saved_encCounter_start, 0), xmm_result0); ++ __ pxor(xmm_result0, xmm_from0); ++ ++ __ testptr(len_reg, 8); ++ __ jcc(Assembler::zero, L_processTail_4_extr[k]); ++ __ pextrd(Address(to, pos), xmm_result0, 0); ++ __ pextrd(Address(to, pos, Address::times_1, 4), xmm_result0, 1); ++ __ psrldq(xmm_result0, 8); ++ __ addptr(pos, 8); ++ __ BIND(L_processTail_4_extr[k]); ++ __ testptr(len_reg, 4); ++ __ jcc(Assembler::zero, L_processTail_2_extr[k]); ++ __ pextrd(Address(to, pos), xmm_result0, 0); ++ __ psrldq(xmm_result0, 4); ++ __ addptr(pos, 4); ++ __ BIND(L_processTail_2_extr[k]); ++ __ testptr(len_reg, 2); ++ __ jcc(Assembler::zero, L_processTail_1_extr[k]); ++ __ pextrb(Address(to, pos), xmm_result0, 0); ++ __ pextrb(Address(to, pos, Address::times_1, 1), xmm_result0, 1); ++ __ psrldq(xmm_result0, 2); ++ __ addptr(pos, 2); ++ __ BIND(L_processTail_1_extr[k]); ++ __ testptr(len_reg, 1); ++ __ jcc(Assembler::zero, L_processTail_exit_extr[k]); ++ __ pextrb(Address(to, pos), xmm_result0, 0); ++ ++ __ BIND(L_processTail_exit_extr[k]); ++ __ movptr(used_addr, used_addr_param); ++ __ movl(Address(used_addr, 0), len_reg); ++ __ jmp(L_exit); ++ } ++ ++ __ BIND(L_exit); ++ __ movdqu(xmm_counter_shuf_mask, ExternalAddress(StubRoutines::x86::counter_shuffle_mask_addr())); ++ __ pshufb(xmm_curr_counter, xmm_counter_shuf_mask); //counter is shuffled back. ++ __ movdqu(Address(counter, 0), xmm_curr_counter); //save counter back ++ handleSOERegisters(false /*restoring*/); ++ __ movptr(rax, len_param); // return length ++ __ leave(); // required for proper stackwalking of RuntimeStub frame ++ __ ret(0); ++ ++ __ BIND (L_key192_top); ++ __ movptr(pos, 0); // init pos before L_multiBlock_loopTop ++ __ jmp(L_multiBlock_loopTop[1]); //key192 ++ ++ __ BIND (L_key256_top); ++ __ movptr(pos, 0); // init pos before L_multiBlock_loopTop ++ __ jmp(L_multiBlock_loopTop[2]); //key192 ++ ++ return start; ++ } ++ ++ + // byte swap x86 long + address generate_ghash_long_swap_mask() { + __ align(CodeEntryAlignment); +@@ -3181,6 +3520,11 @@ class StubGenerator: public StubCodeGenerator { + StubRoutines::_cipherBlockChaining_decryptAESCrypt = generate_cipherBlockChaining_decryptAESCrypt(); + } + ++ if (UseAESCTRIntrinsics) { ++ StubRoutines::x86::_counter_shuffle_mask_addr = generate_counter_shuffle_mask(); ++ StubRoutines::_counterMode_AESCrypt = generate_counterMode_AESCrypt_Parallel(); ++ } ++ + // Generate GHASH intrinsics code + if (UseGHASHIntrinsics) { + StubRoutines::x86::_ghash_long_swap_mask_addr = generate_ghash_long_swap_mask(); +diff --git a/hotspot/src/cpu/x86/vm/stubGenerator_x86_64.cpp b/hotspot/src/cpu/x86/vm/stubGenerator_x86_64.cpp +index c5811b28b..254f63392 100644 +--- a/hotspot/src/cpu/x86/vm/stubGenerator_x86_64.cpp ++++ b/hotspot/src/cpu/x86/vm/stubGenerator_x86_64.cpp +@@ -3010,6 +3010,15 @@ class StubGenerator: public StubCodeGenerator { + return start; + } + ++ address generate_counter_shuffle_mask() { ++ __ align(16); ++ StubCodeMark mark(this, "StubRoutines", "counter_shuffle_mask"); ++ address start = __ pc(); ++ __ emit_data64(0x08090a0b0c0d0e0f, relocInfo::none); ++ __ emit_data64(0x0001020304050607, relocInfo::none); ++ return start; ++ } ++ + // Utility routine for loading a 128-bit key word in little endian format + // can optionally specify that the shuffle mask is already in an xmmregister + void load_key(XMMRegister xmmdst, Register key, int offset, XMMRegister xmm_shuf_mask=NULL) { +@@ -3021,6 +3030,18 @@ class StubGenerator: public StubCodeGenerator { + } + } + ++ // Utility routine for increase 128bit counter (iv in CTR mode) ++ void inc_counter(Register reg, XMMRegister xmmdst, int inc_delta, Label& next_block) { ++ __ pextrq(reg, xmmdst, 0x0); ++ __ addq(reg, inc_delta); ++ __ pinsrq(xmmdst, reg, 0x0); ++ __ jcc(Assembler::carryClear, next_block); // jump if no carry ++ __ pextrq(reg, xmmdst, 0x01); // Carry ++ __ addq(reg, 0x01); ++ __ pinsrq(xmmdst, reg, 0x01); //Carry end ++ __ BIND(next_block); // next instruction ++ } ++ + // Arguments: + // + // Inputs: +@@ -3639,6 +3660,320 @@ class StubGenerator: public StubCodeGenerator { + return start; + } + ++ // This is a version of CTR/AES crypt which does 6 blocks in a loop at a time ++ // to hide instruction latency ++ // ++ // Arguments: ++ // ++ // Inputs: ++ // c_rarg0 - source byte array address ++ // c_rarg1 - destination byte array address ++ // c_rarg2 - K (key) in little endian int array ++ // c_rarg3 - counter vector byte array address ++ // Linux ++ // c_rarg4 - input length ++ // c_rarg5 - saved encryptedCounter start ++ // rbp + 6 * wordSize - saved used length ++ // Windows ++ // rbp + 6 * wordSize - input length ++ // rbp + 7 * wordSize - saved encryptedCounter start ++ // rbp + 8 * wordSize - saved used length ++ // ++ // Output: ++ // rax - input length ++ // ++ address generate_counterMode_AESCrypt_Parallel() { ++ assert(UseAES, "need AES instructions and misaligned SSE support"); ++ __ align(CodeEntryAlignment); ++ StubCodeMark mark(this, "StubRoutines", "counterMode_AESCrypt"); ++ address start = __ pc(); ++ const Register from = c_rarg0; // source array address ++ const Register to = c_rarg1; // destination array address ++ const Register key = c_rarg2; // key array address ++ const Register counter = c_rarg3; // counter byte array initialized from counter array address ++ // and left with the results of the last encryption block ++#ifndef _WIN64 ++ const Register len_reg = c_rarg4; ++ const Register saved_encCounter_start = c_rarg5; ++ const Register used_addr = r10; ++ const Address used_mem(rbp, 2 * wordSize); ++ const Register used = r11; ++#else ++ const Address len_mem(rbp, 6 * wordSize); // length is on stack on Win64 ++ const Address saved_encCounter_mem(rbp, 7 * wordSize); // length is on stack on Win64 ++ const Address used_mem(rbp, 8 * wordSize); // length is on stack on Win64 ++ const Register len_reg = r10; // pick the first volatile windows register ++ const Register saved_encCounter_start = r11; ++ const Register used_addr = r13; ++ const Register used = r14; ++#endif ++ const Register pos = rax; ++ ++ const int PARALLEL_FACTOR = 6; ++ const XMMRegister xmm_counter_shuf_mask = xmm0; ++ const XMMRegister xmm_key_shuf_mask = xmm1; // used temporarily to swap key bytes up front ++ const XMMRegister xmm_curr_counter = xmm2; ++ ++ const XMMRegister xmm_key_tmp0 = xmm3; ++ const XMMRegister xmm_key_tmp1 = xmm4; ++ ++ // registers holding the four results in the parallelized loop ++ const XMMRegister xmm_result0 = xmm5; ++ const XMMRegister xmm_result1 = xmm6; ++ const XMMRegister xmm_result2 = xmm7; ++ const XMMRegister xmm_result3 = xmm8; ++ const XMMRegister xmm_result4 = xmm9; ++ const XMMRegister xmm_result5 = xmm10; ++ ++ const XMMRegister xmm_from0 = xmm11; ++ const XMMRegister xmm_from1 = xmm12; ++ const XMMRegister xmm_from2 = xmm13; ++ const XMMRegister xmm_from3 = xmm14; //the last one is xmm14. we have to preserve it on WIN64. ++ const XMMRegister xmm_from4 = xmm3; //reuse xmm3~4. Because xmm_key_tmp0~1 are useless when loading input text ++ const XMMRegister xmm_from5 = xmm4; ++ ++ //for key_128, key_192, key_256 ++ const int rounds[3] = {10, 12, 14}; ++ Label L_exit_preLoop, L_preLoop_start; ++ Label L_multiBlock_loopTop[3]; ++ Label L_singleBlockLoopTop[3]; ++ Label L__incCounter[3][6]; //for 6 blocks ++ Label L__incCounter_single[3]; //for single block, key128, key192, key256 ++ Label L_processTail_insr[3], L_processTail_4_insr[3], L_processTail_2_insr[3], L_processTail_1_insr[3], L_processTail_exit_insr[3]; ++ Label L_processTail_extr[3], L_processTail_4_extr[3], L_processTail_2_extr[3], L_processTail_1_extr[3], L_processTail_exit_extr[3]; ++ ++ Label L_exit; ++ ++ __ enter(); // required for proper stackwalking of RuntimeStub frame ++ ++#ifdef _WIN64 ++ // save the xmm registers which must be preserved 6-14 ++ const int XMM_REG_NUM_KEY_LAST = 14; ++ __ subptr(rsp, -rsp_after_call_off * wordSize); ++ for (int i = 6; i <= XMM_REG_NUM_KEY_LAST; i++) { ++ __ movdqu(xmm_save(i), as_XMMRegister(i)); ++ } ++ ++ const Address r13_save(rbp, rdi_off * wordSize); ++ const Address r14_save(rbp, rsi_off * wordSize); ++ ++ __ movptr(r13_save, r13); ++ __ movptr(r14_save, r14); ++ ++ // on win64, fill len_reg from stack position ++ __ movl(len_reg, len_mem); ++ __ movptr(saved_encCounter_start, saved_encCounter_mem); ++ __ movptr(used_addr, used_mem); ++ __ movl(used, Address(used_addr, 0)); ++#else ++ __ push(len_reg); // Save ++ __ movptr(used_addr, used_mem); ++ __ movl(used, Address(used_addr, 0)); ++#endif ++ ++ __ push(rbx); // Save RBX ++ __ movdqu(xmm_curr_counter, Address(counter, 0x00)); // initialize counter with initial counter ++ __ movdqu(xmm_counter_shuf_mask, ExternalAddress(StubRoutines::x86::counter_shuffle_mask_addr())); ++ __ pshufb(xmm_curr_counter, xmm_counter_shuf_mask); //counter is shuffled ++ __ movptr(pos, 0); ++ ++ // Use the partially used encrpyted counter from last invocation ++ __ BIND(L_preLoop_start); ++ __ cmpptr(used, 16); ++ __ jcc(Assembler::aboveEqual, L_exit_preLoop); ++ __ cmpptr(len_reg, 0); ++ __ jcc(Assembler::lessEqual, L_exit_preLoop); ++ __ movb(rbx, Address(saved_encCounter_start, used)); ++ __ xorb(rbx, Address(from, pos)); ++ __ movb(Address(to, pos), rbx); ++ __ addptr(pos, 1); ++ __ addptr(used, 1); ++ __ subptr(len_reg, 1); ++ ++ __ jmp(L_preLoop_start); ++ ++ __ BIND(L_exit_preLoop); ++ __ movl(Address(used_addr, 0), used); ++ ++ // key length could be only {11, 13, 15} * 4 = {44, 52, 60} ++ __ movdqu(xmm_key_shuf_mask, ExternalAddress(StubRoutines::x86::key_shuffle_mask_addr())); ++ __ movl(rbx, Address(key, arrayOopDesc::length_offset_in_bytes() - arrayOopDesc::base_offset_in_bytes(T_INT))); ++ __ cmpl(rbx, 52); ++ __ jcc(Assembler::equal, L_multiBlock_loopTop[1]); ++ __ cmpl(rbx, 60); ++ __ jcc(Assembler::equal, L_multiBlock_loopTop[2]); ++ ++#define CTR_DoSix(opc, src_reg) \ ++ __ opc(xmm_result0, src_reg); \ ++ __ opc(xmm_result1, src_reg); \ ++ __ opc(xmm_result2, src_reg); \ ++ __ opc(xmm_result3, src_reg); \ ++ __ opc(xmm_result4, src_reg); \ ++ __ opc(xmm_result5, src_reg); ++ ++ // k == 0 : generate code for key_128 ++ // k == 1 : generate code for key_192 ++ // k == 2 : generate code for key_256 ++ for (int k = 0; k < 3; ++k) { ++ //multi blocks starts here ++ __ align(OptoLoopAlignment); ++ __ BIND(L_multiBlock_loopTop[k]); ++ __ cmpptr(len_reg, PARALLEL_FACTOR * AESBlockSize); // see if at least PARALLEL_FACTOR blocks left ++ __ jcc(Assembler::less, L_singleBlockLoopTop[k]); ++ load_key(xmm_key_tmp0, key, 0x00, xmm_key_shuf_mask); ++ ++ //load, then increase counters ++ CTR_DoSix(movdqa, xmm_curr_counter); ++ inc_counter(rbx, xmm_result1, 0x01, L__incCounter[k][0]); ++ inc_counter(rbx, xmm_result2, 0x02, L__incCounter[k][1]); ++ inc_counter(rbx, xmm_result3, 0x03, L__incCounter[k][2]); ++ inc_counter(rbx, xmm_result4, 0x04, L__incCounter[k][3]); ++ inc_counter(rbx, xmm_result5, 0x05, L__incCounter[k][4]); ++ inc_counter(rbx, xmm_curr_counter, 0x06, L__incCounter[k][5]); ++ CTR_DoSix(pshufb, xmm_counter_shuf_mask); // after increased, shuffled counters back for PXOR ++ CTR_DoSix(pxor, xmm_key_tmp0); //PXOR with Round 0 key ++ ++ //load two ROUND_KEYs at a time ++ for (int i = 1; i < rounds[k]; ) { ++ load_key(xmm_key_tmp1, key, (0x10 * i), xmm_key_shuf_mask); ++ load_key(xmm_key_tmp0, key, (0x10 * (i+1)), xmm_key_shuf_mask); ++ CTR_DoSix(aesenc, xmm_key_tmp1); ++ i++; ++ if (i != rounds[k]) { ++ CTR_DoSix(aesenc, xmm_key_tmp0); ++ } else { ++ CTR_DoSix(aesenclast, xmm_key_tmp0); ++ } ++ i++; ++ } ++ ++ // get next PARALLEL_FACTOR blocks into xmm_result registers ++ __ movdqu(xmm_from0, Address(from, pos, Address::times_1, 0 * AESBlockSize)); ++ __ movdqu(xmm_from1, Address(from, pos, Address::times_1, 1 * AESBlockSize)); ++ __ movdqu(xmm_from2, Address(from, pos, Address::times_1, 2 * AESBlockSize)); ++ __ movdqu(xmm_from3, Address(from, pos, Address::times_1, 3 * AESBlockSize)); ++ __ movdqu(xmm_from4, Address(from, pos, Address::times_1, 4 * AESBlockSize)); ++ __ movdqu(xmm_from5, Address(from, pos, Address::times_1, 5 * AESBlockSize)); ++ ++ __ pxor(xmm_result0, xmm_from0); ++ __ pxor(xmm_result1, xmm_from1); ++ __ pxor(xmm_result2, xmm_from2); ++ __ pxor(xmm_result3, xmm_from3); ++ __ pxor(xmm_result4, xmm_from4); ++ __ pxor(xmm_result5, xmm_from5); ++ ++ // store 6 results into the next 64 bytes of output ++ __ movdqu(Address(to, pos, Address::times_1, 0 * AESBlockSize), xmm_result0); ++ __ movdqu(Address(to, pos, Address::times_1, 1 * AESBlockSize), xmm_result1); ++ __ movdqu(Address(to, pos, Address::times_1, 2 * AESBlockSize), xmm_result2); ++ __ movdqu(Address(to, pos, Address::times_1, 3 * AESBlockSize), xmm_result3); ++ __ movdqu(Address(to, pos, Address::times_1, 4 * AESBlockSize), xmm_result4); ++ __ movdqu(Address(to, pos, Address::times_1, 5 * AESBlockSize), xmm_result5); ++ ++ __ addptr(pos, PARALLEL_FACTOR * AESBlockSize); // increase the length of crypt text ++ __ subptr(len_reg, PARALLEL_FACTOR * AESBlockSize); // decrease the remaining length ++ __ jmp(L_multiBlock_loopTop[k]); ++ ++ // singleBlock starts here ++ __ align(OptoLoopAlignment); ++ __ BIND(L_singleBlockLoopTop[k]); ++ __ cmpptr(len_reg, 0); ++ __ jcc(Assembler::lessEqual, L_exit); ++ load_key(xmm_key_tmp0, key, 0x00, xmm_key_shuf_mask); ++ __ movdqa(xmm_result0, xmm_curr_counter); ++ inc_counter(rbx, xmm_curr_counter, 0x01, L__incCounter_single[k]); ++ __ pshufb(xmm_result0, xmm_counter_shuf_mask); ++ __ pxor(xmm_result0, xmm_key_tmp0); ++ for (int i = 1; i < rounds[k]; i++) { ++ load_key(xmm_key_tmp0, key, (0x10 * i), xmm_key_shuf_mask); ++ __ aesenc(xmm_result0, xmm_key_tmp0); ++ } ++ load_key(xmm_key_tmp0, key, (rounds[k] * 0x10), xmm_key_shuf_mask); ++ __ aesenclast(xmm_result0, xmm_key_tmp0); ++ __ cmpptr(len_reg, AESBlockSize); ++ __ jcc(Assembler::less, L_processTail_insr[k]); ++ __ movdqu(xmm_from0, Address(from, pos, Address::times_1, 0 * AESBlockSize)); ++ __ pxor(xmm_result0, xmm_from0); ++ __ movdqu(Address(to, pos, Address::times_1, 0 * AESBlockSize), xmm_result0); ++ __ addptr(pos, AESBlockSize); ++ __ subptr(len_reg, AESBlockSize); ++ __ jmp(L_singleBlockLoopTop[k]); ++ __ BIND(L_processTail_insr[k]); ++ __ addptr(pos, len_reg); ++ __ testptr(len_reg, 8); ++ __ jcc(Assembler::zero, L_processTail_4_insr[k]); ++ __ subptr(pos,8); ++ __ pinsrq(xmm_from0, Address(from, pos), 0); ++ __ BIND(L_processTail_4_insr[k]); ++ __ testptr(len_reg, 4); ++ __ jcc(Assembler::zero, L_processTail_2_insr[k]); ++ __ subptr(pos,4); ++ __ pslldq(xmm_from0, 4); ++ __ pinsrd(xmm_from0, Address(from, pos), 0); ++ __ BIND(L_processTail_2_insr[k]); ++ __ testptr(len_reg, 2); ++ __ jcc(Assembler::zero, L_processTail_1_insr[k]); ++ __ subptr(pos, 2); ++ __ pslldq(xmm_from0, 2); ++ __ pinsrw(xmm_from0, Address(from, pos), 0); ++ __ BIND(L_processTail_1_insr[k]); ++ __ testptr(len_reg, 1); ++ __ jcc(Assembler::zero, L_processTail_exit_insr[k]); ++ __ subptr(pos, 1); ++ __ pslldq(xmm_from0, 1); ++ __ pinsrb(xmm_from0, Address(from, pos), 0); ++ __ BIND(L_processTail_exit_insr[k]); ++ ++ __ movdqu(Address(saved_encCounter_start, 0), xmm_result0); ++ __ pxor(xmm_result0, xmm_from0); ++ ++ __ testptr(len_reg, 8); ++ __ jcc(Assembler::zero, L_processTail_4_extr[k]); ++ __ pextrq(Address(to, pos), xmm_result0, 0); ++ __ psrldq(xmm_result0, 8); ++ __ addptr(pos, 8); ++ __ BIND(L_processTail_4_extr[k]); ++ __ testptr(len_reg, 4); ++ __ jcc(Assembler::zero, L_processTail_2_extr[k]); ++ __ pextrd(Address(to, pos), xmm_result0, 0); ++ __ psrldq(xmm_result0, 4); ++ __ addptr(pos, 4); ++ __ BIND(L_processTail_2_extr[k]); ++ __ testptr(len_reg, 2); ++ __ jcc(Assembler::zero, L_processTail_1_extr[k]); ++ __ pextrw(Address(to, pos), xmm_result0, 0); ++ __ psrldq(xmm_result0, 2); ++ __ addptr(pos, 2); ++ __ BIND(L_processTail_1_extr[k]); ++ __ testptr(len_reg, 1); ++ __ jcc(Assembler::zero, L_processTail_exit_extr[k]); ++ __ pextrb(Address(to, pos), xmm_result0, 0); ++ ++ __ BIND(L_processTail_exit_extr[k]); ++ __ movl(Address(used_addr, 0), len_reg); ++ __ jmp(L_exit); ++ ++ } ++ ++ __ BIND(L_exit); ++ __ pshufb(xmm_curr_counter, xmm_counter_shuf_mask); //counter is shuffled back. ++ __ movdqu(Address(counter, 0), xmm_curr_counter); //save counter back ++ __ pop(rbx); // pop the saved RBX. ++#ifdef _WIN64 ++ // restore regs belonging to calling function ++ for (int i = 6; i <= XMM_REG_NUM_KEY_LAST; i++) { ++ __ movdqu(as_XMMRegister(i), xmm_save(i)); ++ } ++ __ movl(rax, len_mem); ++ __ movptr(r13, r13_save); ++ __ movptr(r14, r14_save); ++#else ++ __ pop(rax); // return 'len' ++#endif ++ __ leave(); // required for proper stackwalking of RuntimeStub frame ++ __ ret(0); ++ return start; ++ } + + // byte swap x86 long + address generate_ghash_long_swap_mask() { +@@ -4239,12 +4574,15 @@ class StubGenerator: public StubCodeGenerator { + // don't bother generating these AES intrinsic stubs unless global flag is set + if (UseAESIntrinsics) { + StubRoutines::x86::_key_shuffle_mask_addr = generate_key_shuffle_mask(); // needed by the others +- + StubRoutines::_aescrypt_encryptBlock = generate_aescrypt_encryptBlock(); + StubRoutines::_aescrypt_decryptBlock = generate_aescrypt_decryptBlock(); + StubRoutines::_cipherBlockChaining_encryptAESCrypt = generate_cipherBlockChaining_encryptAESCrypt(); + StubRoutines::_cipherBlockChaining_decryptAESCrypt = generate_cipherBlockChaining_decryptAESCrypt_Parallel(); + } ++ if (UseAESCTRIntrinsics){ ++ StubRoutines::x86::_counter_shuffle_mask_addr = generate_counter_shuffle_mask(); ++ StubRoutines::_counterMode_AESCrypt = generate_counterMode_AESCrypt_Parallel(); ++ } + + // Generate GHASH intrinsics code + if (UseGHASHIntrinsics) { +diff --git a/hotspot/src/cpu/x86/vm/stubRoutines_x86.cpp b/hotspot/src/cpu/x86/vm/stubRoutines_x86.cpp +index 9b0d8fc75..617879377 100644 +--- a/hotspot/src/cpu/x86/vm/stubRoutines_x86.cpp ++++ b/hotspot/src/cpu/x86/vm/stubRoutines_x86.cpp +@@ -33,6 +33,7 @@ + + address StubRoutines::x86::_verify_mxcsr_entry = NULL; + address StubRoutines::x86::_key_shuffle_mask_addr = NULL; ++address StubRoutines::x86::_counter_shuffle_mask_addr = NULL; + address StubRoutines::x86::_ghash_long_swap_mask_addr = NULL; + address StubRoutines::x86::_ghash_byte_swap_mask_addr = NULL; + +diff --git a/hotspot/src/cpu/x86/vm/stubRoutines_x86.hpp b/hotspot/src/cpu/x86/vm/stubRoutines_x86.hpp +index bb160486c..70b5a34ac 100644 +--- a/hotspot/src/cpu/x86/vm/stubRoutines_x86.hpp ++++ b/hotspot/src/cpu/x86/vm/stubRoutines_x86.hpp +@@ -33,6 +33,10 @@ + static address _verify_mxcsr_entry; + // shuffle mask for fixing up 128-bit words consisting of big-endian 32-bit integers + static address _key_shuffle_mask_addr; ++ ++ //shuffle mask for big-endian 128-bit integers ++ static address _counter_shuffle_mask_addr; ++ + // masks and table for CRC32 + static uint64_t _crc_by128_masks[]; + static juint _crc_table[]; +@@ -43,6 +47,7 @@ + public: + static address verify_mxcsr_entry() { return _verify_mxcsr_entry; } + static address key_shuffle_mask_addr() { return _key_shuffle_mask_addr; } ++ static address counter_shuffle_mask_addr() { return _counter_shuffle_mask_addr; } + static address crc_by128_masks_addr() { return (address)_crc_by128_masks; } + static address ghash_long_swap_mask_addr() { return _ghash_long_swap_mask_addr; } + static address ghash_byte_swap_mask_addr() { return _ghash_byte_swap_mask_addr; } +diff --git a/hotspot/src/cpu/x86/vm/stubRoutines_x86_32.hpp b/hotspot/src/cpu/x86/vm/stubRoutines_x86_32.hpp +index bca5d493c..538f83e69 100644 +--- a/hotspot/src/cpu/x86/vm/stubRoutines_x86_32.hpp ++++ b/hotspot/src/cpu/x86/vm/stubRoutines_x86_32.hpp +@@ -31,7 +31,7 @@ + + enum platform_dependent_constants { + code_size1 = 9000, // simply increase if too small (assembler will crash if too small) +- code_size2 = 22000 // simply increase if too small (assembler will crash if too small) ++ code_size2 = 25800 // simply increase if too small (assembler will crash if too small) + }; + + class x86 { +diff --git a/hotspot/src/cpu/x86/vm/stubRoutines_x86_64.hpp b/hotspot/src/cpu/x86/vm/stubRoutines_x86_64.hpp +index b048fd74e..f963cd2f8 100644 +--- a/hotspot/src/cpu/x86/vm/stubRoutines_x86_64.hpp ++++ b/hotspot/src/cpu/x86/vm/stubRoutines_x86_64.hpp +@@ -33,7 +33,7 @@ static bool returns_to_call_stub(address return_pc) { return return_pc == _ + + enum platform_dependent_constants { + code_size1 = 19000, // simply increase if too small (assembler will crash if too small) +- code_size2 = 24000 // simply increase if too small (assembler will crash if too small) ++ code_size2 = 27000 // simply increase if too small (assembler will crash if too small) + }; + + class x86 { +diff --git a/hotspot/src/cpu/x86/vm/vm_version_x86.cpp b/hotspot/src/cpu/x86/vm/vm_version_x86.cpp +index 46b3e32ea..ce3037d76 100644 +--- a/hotspot/src/cpu/x86/vm/vm_version_x86.cpp ++++ b/hotspot/src/cpu/x86/vm/vm_version_x86.cpp +@@ -573,6 +573,28 @@ void VM_Version::get_processor_features() { + } + FLAG_SET_DEFAULT(UseAESIntrinsics, false); + } ++ ++ // --AES-CTR begins-- ++ if (!UseAESIntrinsics) { ++ if (UseAESCTRIntrinsics && !FLAG_IS_DEFAULT(UseAESCTRIntrinsics)) { ++ warning("AES-CTR intrinsics require UseAESIntrinsics flag to be enabled. Intrinsics will be disabled."); ++ FLAG_SET_DEFAULT(UseAESCTRIntrinsics, false); ++ } ++ } else { ++ if(supports_sse4_1() && UseSSE >= 4) { ++ if (FLAG_IS_DEFAULT(UseAESCTRIntrinsics)) { ++ FLAG_SET_DEFAULT(UseAESCTRIntrinsics, true); ++ } ++ } else { ++ // The AES-CTR intrinsic stubs require AES instruction support (of course) ++ // but also require sse4.1 mode or higher for instructions it use. ++ if (UseAESCTRIntrinsics && !FLAG_IS_DEFAULT(UseAESCTRIntrinsics)) { ++ warning("X86 AES-CTR intrinsics require SSE4.1 instructions or higher. Intrinsics will be disabled."); ++ } ++ FLAG_SET_DEFAULT(UseAESCTRIntrinsics, false); ++ } ++ } ++ // --AES-CTR ends-- + } + } else if (UseAES || UseAESIntrinsics) { + if (UseAES && !FLAG_IS_DEFAULT(UseAES)) { +@@ -583,6 +605,10 @@ void VM_Version::get_processor_features() { + warning("AES intrinsics are not available on this CPU"); + FLAG_SET_DEFAULT(UseAESIntrinsics, false); + } ++ if (UseAESCTRIntrinsics && !FLAG_IS_DEFAULT(UseAESCTRIntrinsics)) { ++ warning("AES-CTR intrinsics are not available on this CPU"); ++ FLAG_SET_DEFAULT(UseAESCTRIntrinsics, false); ++ } + } + + // Use CLMUL instructions if available. +@@ -606,6 +632,16 @@ void VM_Version::get_processor_features() { + FLAG_SET_DEFAULT(UseCRC32Intrinsics, false); + } + ++ if (UseAESIntrinsics) { ++ if (FLAG_IS_DEFAULT(UseAESCTRIntrinsics)) { ++ UseAESCTRIntrinsics = true; ++ } ++ } else if (UseAESCTRIntrinsics) { ++ if (!FLAG_IS_DEFAULT(UseAESCTRIntrinsics)) ++ warning("AES/CTR intrinsics are not available on this CPU"); ++ FLAG_SET_DEFAULT(UseAESCTRIntrinsics, false); ++ } ++ + // GHASH/GCM intrinsics + if (UseCLMUL && (UseSSE > 2)) { + if (FLAG_IS_DEFAULT(UseGHASHIntrinsics)) { +diff --git a/hotspot/src/share/vm/classfile/vmSymbols.hpp b/hotspot/src/share/vm/classfile/vmSymbols.hpp +index 942d172a1..4ca2a3ad4 100644 +--- a/hotspot/src/share/vm/classfile/vmSymbols.hpp ++++ b/hotspot/src/share/vm/classfile/vmSymbols.hpp +@@ -846,6 +846,10 @@ + do_name( decrypt_name, "implDecrypt") \ + do_signature(byteArray_int_int_byteArray_int_signature, "([BII[BI)I") \ + \ ++ do_class(com_sun_crypto_provider_counterMode, "com/sun/crypto/provider/CounterMode") \ ++ do_intrinsic(_counterMode_AESCrypt, com_sun_crypto_provider_counterMode, crypt_name, byteArray_int_int_byteArray_int_signature, F_R) \ ++ do_name( crypt_name, "implCrypt") \ ++ \ + /* support for sun.security.provider.SHA */ \ + do_class(sun_security_provider_sha, "sun/security/provider/SHA") \ + do_intrinsic(_sha_implCompress, sun_security_provider_sha, implCompress_name, implCompress_signature, F_R) \ +diff --git a/hotspot/src/share/vm/opto/escape.cpp b/hotspot/src/share/vm/opto/escape.cpp +index 6f8ffe608..a0e497f08 100644 +--- a/hotspot/src/share/vm/opto/escape.cpp ++++ b/hotspot/src/share/vm/opto/escape.cpp +@@ -952,6 +952,7 @@ void ConnectionGraph::process_call_arguments(CallNode *call) { + strcmp(call->as_CallLeaf()->_name, "aescrypt_decryptBlock") == 0 || + strcmp(call->as_CallLeaf()->_name, "cipherBlockChaining_encryptAESCrypt") == 0 || + strcmp(call->as_CallLeaf()->_name, "cipherBlockChaining_decryptAESCrypt") == 0 || ++ strcmp(call->as_CallLeaf()->_name, "counterMode_AESCrypt") == 0 || + strcmp(call->as_CallLeaf()->_name, "ghash_processBlocks") == 0 || + strcmp(call->as_CallLeaf()->_name, "sha1_implCompress") == 0 || + strcmp(call->as_CallLeaf()->_name, "sha1_implCompressMB") == 0 || +diff --git a/hotspot/src/share/vm/opto/library_call.cpp b/hotspot/src/share/vm/opto/library_call.cpp +index bb721f6f1..2add82dd1 100644 +--- a/hotspot/src/share/vm/opto/library_call.cpp ++++ b/hotspot/src/share/vm/opto/library_call.cpp +@@ -196,6 +196,7 @@ class LibraryCallKit : public GraphKit { + return generate_method_call(method_id, true, false); + } + Node * load_field_from_object(Node * fromObj, const char * fieldName, const char * fieldTypeString, bool is_exact, bool is_static); ++ Node * field_address_from_object(Node * fromObj, const char * fieldName, const char * fieldTypeString, bool is_exact, bool is_static, ciInstanceKlass * fromKls); + + Node* make_string_method_node(int opcode, Node* str1_start, Node* cnt1, Node* str2_start, Node* cnt2); + Node* make_string_method_node(int opcode, Node* str1, Node* str2); +@@ -309,7 +310,9 @@ class LibraryCallKit : public GraphKit { + bool inline_reference_get(); + bool inline_aescrypt_Block(vmIntrinsics::ID id); + bool inline_cipherBlockChaining_AESCrypt(vmIntrinsics::ID id); ++ bool inline_counterMode_AESCrypt(vmIntrinsics::ID id); + Node* inline_cipherBlockChaining_AESCrypt_predicate(bool decrypting); ++ Node* inline_counterMode_AESCrypt_predicate(); + Node* get_key_start_from_aescrypt_object(Node* aescrypt_object); + Node* get_original_key_start_from_aescrypt_object(Node* aescrypt_object); + bool inline_ghash_processBlocks(); +@@ -558,6 +561,13 @@ CallGenerator* Compile::make_vm_intrinsic(ciMethod* m, bool is_virtual) { + predicates = 1; + break; + ++ case vmIntrinsics::_counterMode_AESCrypt: ++ if (!UseAESCTRIntrinsics) { ++ return NULL; ++ } ++ predicates = 1; ++ break; ++ + case vmIntrinsics::_sha_implCompress: + if (!UseSHA1Intrinsics) return NULL; + break; +@@ -950,6 +960,9 @@ bool LibraryCallKit::try_to_inline(int predicate) { + case vmIntrinsics::_cipherBlockChaining_decryptAESCrypt: + return inline_cipherBlockChaining_AESCrypt(intrinsic_id()); + ++ case vmIntrinsics::_counterMode_AESCrypt: ++ return inline_counterMode_AESCrypt(intrinsic_id()); ++ + case vmIntrinsics::_sha_implCompress: + case vmIntrinsics::_sha2_implCompress: + case vmIntrinsics::_sha5_implCompress: +@@ -1021,6 +1034,8 @@ Node* LibraryCallKit::try_to_predicate(int predicate) { + return inline_cipherBlockChaining_AESCrypt_predicate(false); + case vmIntrinsics::_cipherBlockChaining_decryptAESCrypt: + return inline_cipherBlockChaining_AESCrypt_predicate(true); ++ case vmIntrinsics::_counterMode_AESCrypt: ++ return inline_counterMode_AESCrypt_predicate(); + case vmIntrinsics::_digestBase_implCompressMB: + return inline_digestBase_implCompressMB_predicate(predicate); + +@@ -6581,6 +6596,39 @@ Node * LibraryCallKit::load_field_from_object(Node * fromObj, const char * field + return loadedField; + } + ++Node * LibraryCallKit::field_address_from_object(Node * fromObj, const char * fieldName, const char * fieldTypeString, ++ bool is_exact = true, bool is_static = false, ++ ciInstanceKlass * fromKls = NULL) { ++ if (fromKls == NULL) { ++ const TypeInstPtr* tinst = _gvn.type(fromObj)->isa_instptr(); ++ assert(tinst != NULL, "obj is null"); ++ assert(tinst->klass()->is_loaded(), "obj is not loaded"); ++ assert(!is_exact || tinst->klass_is_exact(), "klass not exact"); ++ fromKls = tinst->klass()->as_instance_klass(); ++ } ++ else { ++ assert(is_static, "only for static field access"); ++ } ++ ciField* field = fromKls->get_field_by_name(ciSymbol::make(fieldName), ++ ciSymbol::make(fieldTypeString), ++ is_static); ++ ++ assert(field != NULL, "undefined field"); ++ assert(!field->is_volatile(), "not defined for volatile fields"); ++ ++ if (is_static) { ++ const TypeInstPtr* tip = TypeInstPtr::make(fromKls->java_mirror()); ++ fromObj = makecon(tip); ++ } ++ ++ // Next code copied from Parse::do_get_xxx(): ++ ++ // Compute address and memory type. ++ int offset = field->offset_in_bytes(); ++ Node *adr = basic_plus_adr(fromObj, fromObj, offset); ++ ++ return adr; ++} + + //------------------------------inline_aescrypt_Block----------------------- + bool LibraryCallKit::inline_aescrypt_Block(vmIntrinsics::ID id) { +@@ -6747,6 +6795,90 @@ bool LibraryCallKit::inline_cipherBlockChaining_AESCrypt(vmIntrinsics::ID id) { + return true; + } + ++//------------------------------inline_counterMode_AESCrypt----------------------- ++bool LibraryCallKit::inline_counterMode_AESCrypt(vmIntrinsics::ID id) { ++ assert(UseAES, "need AES instruction support"); ++ if (!UseAESCTRIntrinsics) return false; ++ ++ address stubAddr = NULL; ++ const char *stubName = NULL; ++ if (id == vmIntrinsics::_counterMode_AESCrypt) { ++ stubAddr = StubRoutines::counterMode_AESCrypt(); ++ stubName = "counterMode_AESCrypt"; ++ } ++ if (stubAddr == NULL) return false; ++ ++ Node* counterMode_object = argument(0); ++ Node* src = argument(1); ++ Node* src_offset = argument(2); ++ Node* len = argument(3); ++ Node* dest = argument(4); ++ Node* dest_offset = argument(5); ++ ++ // (1) src and dest are arrays. ++ const Type* src_type = src->Value(&_gvn); ++ const Type* dest_type = dest->Value(&_gvn); ++ const TypeAryPtr* top_src = src_type->isa_aryptr(); ++ const TypeAryPtr* top_dest = dest_type->isa_aryptr(); ++ assert(top_src != NULL && top_src->klass() != NULL && ++ top_dest != NULL && top_dest->klass() != NULL, "args are strange"); ++ ++ // checks are the responsibility of the caller ++ Node* src_start = src; ++ Node* dest_start = dest; ++ if (src_offset != NULL || dest_offset != NULL) { ++ assert(src_offset != NULL && dest_offset != NULL, ""); ++ src_start = array_element_address(src, src_offset, T_BYTE); ++ dest_start = array_element_address(dest, dest_offset, T_BYTE); ++ } ++ ++ // if we are in this set of code, we "know" the embeddedCipher is an AESCrypt object ++ // (because of the predicated logic executed earlier). ++ // so we cast it here safely. ++ // this requires a newer class file that has this array as littleEndian ints, otherwise we revert to java ++ Node* embeddedCipherObj = load_field_from_object(counterMode_object, "embeddedCipher", "Lcom/sun/crypto/provider/SymmetricCipher;", /*is_exact*/ false); ++ if (embeddedCipherObj == NULL) return false; ++ // cast it to what we know it will be at runtime ++ const TypeInstPtr* tinst = _gvn.type(counterMode_object)->isa_instptr(); ++ assert(tinst != NULL, "CTR obj is null"); ++ assert(tinst->klass()->is_loaded(), "CTR obj is not loaded"); ++ ciKlass* klass_AESCrypt = tinst->klass()->as_instance_klass()->find_klass(ciSymbol::make("com/sun/crypto/provider/AESCrypt")); ++ assert(klass_AESCrypt->is_loaded(), "predicate checks that this class is loaded"); ++ ciInstanceKlass* instklass_AESCrypt = klass_AESCrypt->as_instance_klass(); ++ const TypeKlassPtr* aklass = TypeKlassPtr::make(instklass_AESCrypt); ++ const TypeOopPtr* xtype = aklass->as_instance_type(); ++ Node* aescrypt_object = new (C) CheckCastPPNode(control(), embeddedCipherObj, xtype); ++ aescrypt_object = _gvn.transform(aescrypt_object); ++ // we need to get the start of the aescrypt_object's expanded key array ++ Node* k_start = get_key_start_from_aescrypt_object(aescrypt_object); ++ if (k_start == NULL) return false; ++ // similarly, get the start address of the r vector ++ Node* obj_counter = load_field_from_object(counterMode_object, "counter", "[B", /*is_exact*/ false); ++ if (obj_counter == NULL) return false; ++ Node* cnt_start = array_element_address(obj_counter, intcon(0), T_BYTE); ++ ++ Node* saved_encCounter = load_field_from_object(counterMode_object, "encryptedCounter", "[B", /*is_exact*/ false); ++ if (saved_encCounter == NULL) return false; ++ Node* saved_encCounter_start = array_element_address(saved_encCounter, intcon(0), T_BYTE); ++ Node* used = field_address_from_object(counterMode_object, "used", "I", /*is_exact*/ false); ++ ++ Node* ctrCrypt; ++ if (Matcher::pass_original_key_for_aes()) { ++ // no SPARC version for AES/CTR intrinsics now. ++ return false; ++ } ++ // Call the stub, passing src_start, dest_start, k_start, r_start and src_len ++ ctrCrypt = make_runtime_call(RC_LEAF|RC_NO_FP, ++ OptoRuntime::counterMode_aescrypt_Type(), ++ stubAddr, stubName, TypePtr::BOTTOM, ++ src_start, dest_start, k_start, cnt_start, len, saved_encCounter_start, used); ++ ++ // return cipher length (int) ++ Node* retvalue = _gvn.transform(new (C) ProjNode(ctrCrypt, TypeFunc::Parms)); ++ set_result(retvalue); ++ return true; ++} ++ + //------------------------------get_key_start_from_aescrypt_object----------------------- + Node * LibraryCallKit::get_key_start_from_aescrypt_object(Node *aescrypt_object) { + #ifdef PPC64 +@@ -6841,6 +6973,48 @@ Node* LibraryCallKit::inline_cipherBlockChaining_AESCrypt_predicate(bool decrypt + return _gvn.transform(region); + } + ++//----------------------------inline_counterMode_AESCrypt_predicate---------------------------- ++// Return node representing slow path of predicate check. ++// the pseudo code we want to emulate with this predicate is: ++// for encryption: ++// if (embeddedCipherObj instanceof AESCrypt) do_intrinsic, else do_javapath ++// for decryption: ++// if ((embeddedCipherObj instanceof AESCrypt) && (cipher!=plain)) do_intrinsic, else do_javapath ++// note cipher==plain is more conservative than the original java code but that's OK ++// ++ ++Node* LibraryCallKit::inline_counterMode_AESCrypt_predicate() { ++ // The receiver was checked for NULL already. ++ Node* objCTR = argument(0); ++ ++ // Load embeddedCipher field of CipherBlockChaining object. ++ Node* embeddedCipherObj = load_field_from_object(objCTR, "embeddedCipher", "Lcom/sun/crypto/provider/SymmetricCipher;", /*is_exact*/ false); ++ ++ // get AESCrypt klass for instanceOf check ++ // AESCrypt might not be loaded yet if some other SymmetricCipher got us to this compile point ++ // will have same classloader as CipherBlockChaining object ++ const TypeInstPtr* tinst = _gvn.type(objCTR)->isa_instptr(); ++ assert(tinst != NULL, "CTRobj is null"); ++ assert(tinst->klass()->is_loaded(), "CTRobj is not loaded"); ++ ++ // we want to do an instanceof comparison against the AESCrypt class ++ ciKlass* klass_AESCrypt = tinst->klass()->as_instance_klass()->find_klass(ciSymbol::make("com/sun/crypto/provider/AESCrypt")); ++ if (!klass_AESCrypt->is_loaded()) { ++ // if AESCrypt is not even loaded, we never take the intrinsic fast path ++ Node* ctrl = control(); ++ set_control(top()); // no regular fast path ++ return ctrl; ++ } ++ ++ ciInstanceKlass* instklass_AESCrypt = klass_AESCrypt->as_instance_klass(); ++ Node* instof = gen_instanceof(embeddedCipherObj, makecon(TypeKlassPtr::make(instklass_AESCrypt))); ++ Node* cmp_instof = _gvn.transform(new (C) CmpINode(instof, intcon(1))); ++ Node* bool_instof = _gvn.transform(new (C) BoolNode(cmp_instof, BoolTest::ne)); ++ Node* instof_false = generate_guard(bool_instof, NULL, PROB_MIN); ++ ++ return instof_false; // even if it is NULL ++} ++ + //------------------------------inline_ghash_processBlocks + bool LibraryCallKit::inline_ghash_processBlocks() { + address stubAddr; +diff --git a/hotspot/src/share/vm/opto/runtime.cpp b/hotspot/src/share/vm/opto/runtime.cpp +index 0a86211ba..1c51be19b 100644 +--- a/hotspot/src/share/vm/opto/runtime.cpp ++++ b/hotspot/src/share/vm/opto/runtime.cpp +@@ -1021,6 +1021,35 @@ const TypeFunc* OptoRuntime::cipherBlockChaining_aescrypt_Type() { + return TypeFunc::make(domain, range); + } + ++//for counterMode calls of aescrypt encrypt/decrypt, four pointers and a length, returning int ++const TypeFunc* OptoRuntime::counterMode_aescrypt_Type() { ++ // create input type (domain) ++ int num_args = 7; ++ if (Matcher::pass_original_key_for_aes()) { ++ num_args = 8; ++ } ++ int argcnt = num_args; ++ const Type** fields = TypeTuple::fields(argcnt); ++ int argp = TypeFunc::Parms; ++ fields[argp++] = TypePtr::NOTNULL; // src ++ fields[argp++] = TypePtr::NOTNULL; // dest ++ fields[argp++] = TypePtr::NOTNULL; // k array ++ fields[argp++] = TypePtr::NOTNULL; // counter array ++ fields[argp++] = TypeInt::INT; // src len ++ fields[argp++] = TypePtr::NOTNULL; // saved_encCounter ++ fields[argp++] = TypePtr::NOTNULL; // saved used addr ++ if (Matcher::pass_original_key_for_aes()) { ++ fields[argp++] = TypePtr::NOTNULL; // original k array ++ } ++ assert(argp == TypeFunc::Parms + argcnt, "correct decoding"); ++ const TypeTuple* domain = TypeTuple::make(TypeFunc::Parms + argcnt, fields); ++ // returning cipher len (int) ++ fields = TypeTuple::fields(1); ++ fields[TypeFunc::Parms + 0] = TypeInt::INT; ++ const TypeTuple* range = TypeTuple::make(TypeFunc::Parms + 1, fields); ++ return TypeFunc::make(domain, range); ++} ++ + /* + * void implCompress(byte[] buf, int ofs) + */ +diff --git a/hotspot/src/share/vm/opto/runtime.hpp b/hotspot/src/share/vm/opto/runtime.hpp +index 47133d58c..f27e7d507 100644 +--- a/hotspot/src/share/vm/opto/runtime.hpp ++++ b/hotspot/src/share/vm/opto/runtime.hpp +@@ -299,6 +299,7 @@ private: + + static const TypeFunc* aescrypt_block_Type(); + static const TypeFunc* cipherBlockChaining_aescrypt_Type(); ++ static const TypeFunc* counterMode_aescrypt_Type(); + + static const TypeFunc* sha_implCompress_Type(); + static const TypeFunc* digestBase_implCompressMB_Type(); +diff --git a/hotspot/src/share/vm/runtime/globals.hpp b/hotspot/src/share/vm/runtime/globals.hpp +index 65dfcf69b..91e52f033 100644 +--- a/hotspot/src/share/vm/runtime/globals.hpp ++++ b/hotspot/src/share/vm/runtime/globals.hpp +@@ -734,6 +734,9 @@ class CommandLineFlags { + product(bool, UseAESIntrinsics, false, \ + "Use intrinsics for AES versions of crypto") \ + \ ++ product(bool, UseAESCTRIntrinsics, false, \ ++ "Use intrinsics for the paralleled version of AES/CTR crypto") \ ++ \ + product(bool, UseSHA1Intrinsics, false, \ + "Use intrinsics for SHA-1 crypto hash function") \ + \ +diff --git a/hotspot/src/share/vm/runtime/stubRoutines.cpp b/hotspot/src/share/vm/runtime/stubRoutines.cpp +index f2106d13a..d66237137 100644 +--- a/hotspot/src/share/vm/runtime/stubRoutines.cpp ++++ b/hotspot/src/share/vm/runtime/stubRoutines.cpp +@@ -124,6 +124,7 @@ address StubRoutines::_aescrypt_encryptBlock = NULL; + address StubRoutines::_aescrypt_decryptBlock = NULL; + address StubRoutines::_cipherBlockChaining_encryptAESCrypt = NULL; + address StubRoutines::_cipherBlockChaining_decryptAESCrypt = NULL; ++address StubRoutines::_counterMode_AESCrypt = NULL; + address StubRoutines::_ghash_processBlocks = NULL; + + address StubRoutines::_sha1_implCompress = NULL; +diff --git a/hotspot/src/share/vm/runtime/stubRoutines.hpp b/hotspot/src/share/vm/runtime/stubRoutines.hpp +index 16075d9f4..9fb589540 100644 +--- a/hotspot/src/share/vm/runtime/stubRoutines.hpp ++++ b/hotspot/src/share/vm/runtime/stubRoutines.hpp +@@ -202,6 +202,7 @@ class StubRoutines: AllStatic { + static address _aescrypt_decryptBlock; + static address _cipherBlockChaining_encryptAESCrypt; + static address _cipherBlockChaining_decryptAESCrypt; ++ static address _counterMode_AESCrypt; + static address _ghash_processBlocks; + + static address _sha1_implCompress; +@@ -370,6 +371,7 @@ class StubRoutines: AllStatic { + static address aescrypt_decryptBlock() { return _aescrypt_decryptBlock; } + static address cipherBlockChaining_encryptAESCrypt() { return _cipherBlockChaining_encryptAESCrypt; } + static address cipherBlockChaining_decryptAESCrypt() { return _cipherBlockChaining_decryptAESCrypt; } ++ static address counterMode_AESCrypt() { return _counterMode_AESCrypt; } + static address ghash_processBlocks() { return _ghash_processBlocks; } + + static address sha1_implCompress() { return _sha1_implCompress; } +diff --git a/hotspot/src/share/vm/runtime/vmStructs.cpp b/hotspot/src/share/vm/runtime/vmStructs.cpp +index 3f2bfeb74..842b5840d 100644 +--- a/hotspot/src/share/vm/runtime/vmStructs.cpp ++++ b/hotspot/src/share/vm/runtime/vmStructs.cpp +@@ -815,6 +815,7 @@ typedef TwoOopHashtable SymbolTwoOopHashtable; + static_field(StubRoutines, _aescrypt_decryptBlock, address) \ + static_field(StubRoutines, _cipherBlockChaining_encryptAESCrypt, address) \ + static_field(StubRoutines, _cipherBlockChaining_decryptAESCrypt, address) \ ++ static_field(StubRoutines, _counterMode_AESCrypt, address) \ + static_field(StubRoutines, _ghash_processBlocks, address) \ + static_field(StubRoutines, _updateBytesCRC32, address) \ + static_field(StubRoutines, _crc_table_adr, address) \ +diff --git a/hotspot/test/compiler/7184394/TestAESBase.java b/hotspot/test/compiler/7184394/TestAESBase.java +index 5c3e6881e..afda2a1f7 100644 +--- a/hotspot/test/compiler/7184394/TestAESBase.java ++++ b/hotspot/test/compiler/7184394/TestAESBase.java +@@ -106,8 +106,8 @@ abstract public class TestAESBase { + cipher = Cipher.getInstance(algorithm + "/" + mode + "/" + paddingStr, "SunJCE"); + dCipher = Cipher.getInstance(algorithm + "/" + mode + "/" + paddingStr, "SunJCE"); + +- // CBC init +- if (mode.equals("CBC")) { ++ // CBC or CTR init ++ if (mode.equals("CBC") || mode.equals("CTR")) { + IvParameterSpec initVector = new IvParameterSpec(iv); + cipher.init(Cipher.ENCRYPT_MODE, key, initVector); + algParams = cipher.getParameters(); +diff --git a/hotspot/test/compiler/7184394/TestAESMain.java b/hotspot/test/compiler/7184394/TestAESMain.java +index ddd8eeaef..65949420a 100644 +--- a/hotspot/test/compiler/7184394/TestAESMain.java ++++ b/hotspot/test/compiler/7184394/TestAESMain.java +@@ -48,6 +48,13 @@ + * @run main/othervm/timeout=600 -Xbatch -DcheckOutput=true -Dmode=GCM -DencInputOffset=1 -DencOutputOffset=1 TestAESMain + * @run main/othervm/timeout=600 -Xbatch -DcheckOutput=true -Dmode=GCM -DencInputOffset=1 -DencOutputOffset=1 -DdecOutputOffset=1 TestAESMain + * @run main/othervm/timeout=600 -Xbatch -DcheckOutput=true -Dmode=GCM -DencInputOffset=1 -DencOutputOffset=1 -DdecOutputOffset=1 -DpaddingStr=NoPadding -DmsgSize=640 TestAESMain ++ * @run main/othervm/timeout=600 -Xbatch -DcheckOutput=true -Dmode=CTR TestAESMain ++ * @run main/othervm/timeout=600 -Xbatch -DcheckOutput=true -Dmode=CTR -DencInputOffset=1 TestAESMain ++ * @run main/othervm/timeout=600 -Xbatch -DcheckOutput=true -Dmode=CTR -DencOutputOffset=1 TestAESMain ++ * @run main/othervm/timeout=600 -Xbatch -DcheckOutput=true -Dmode=CTR -DdecOutputOffset=1 TestAESMain ++ * @run main/othervm/timeout=600 -Xbatch -DcheckOutput=true -Dmode=CTR -DencInputOffset=1 -DencOutputOffset=1 TestAESMain ++ * @run main/othervm/timeout=600 -Xbatch -DcheckOutput=true -Dmode=CTR -DencInputOffset=1 -DencOutputOffset=1 -DdecOutputOffset=1 TestAESMain ++ * @run main/othervm/timeout=600 -Xbatch -DcheckOutput=true -Dmode=CTR -DencInputOffset=1 -DencOutputOffset=1 -DdecOutputOffset=1 -DpaddingStr=NoPadding -DmsgSize=640 TestAESMain + * + * @author Tom Deneau + */ +diff --git a/jdk/src/share/classes/com/sun/crypto/provider/CounterMode.java b/jdk/src/share/classes/com/sun/crypto/provider/CounterMode.java +index aea9336c9..c2bd38a71 100644 +--- a/jdk/src/share/classes/com/sun/crypto/provider/CounterMode.java ++++ b/jdk/src/share/classes/com/sun/crypto/provider/CounterMode.java +@@ -39,10 +39,10 @@ import java.security.InvalidKeyException; + * @author Andreas Sterbenz + * @since 1.4.2 + */ +-final class CounterMode extends FeedbackCipher { ++class CounterMode extends FeedbackCipher { + + // current counter value +- private final byte[] counter; ++ final byte[] counter; + + // encrypted bytes of the previous counter value + private final byte[] encryptedCounter; +@@ -137,7 +137,7 @@ final class CounterMode extends FeedbackCipher { + * cipherOffset. + * + * @param in the buffer with the input data to be encrypted +- * @param inOffset the offset in plain ++ * @param inOff the offset in plain + * @param len the length of the input data + * @param out the buffer for the result + * @param outOff the offset in cipher +@@ -176,6 +176,11 @@ final class CounterMode extends FeedbackCipher { + RangeUtil.nullAndBoundsCheck(in, inOff, len); + RangeUtil.nullAndBoundsCheck(out, outOff, len); + ++ return implCrypt(in, inOff, len, out, outOff); ++ } ++ ++ // Implementation of crpyt() method. Possibly replaced with a compiler intrinsic. ++ private int implCrypt(byte[] in, int inOff, int len, byte[] out, int outOff) { + int result = len; + while (len-- > 0) { + if (used >= blockSize) { +diff --git a/jdk/src/share/classes/com/sun/crypto/provider/GCTR.java b/jdk/src/share/classes/com/sun/crypto/provider/GCTR.java +index f8a3eaa0a..6a394e448 100644 +--- a/jdk/src/share/classes/com/sun/crypto/provider/GCTR.java ++++ b/jdk/src/share/classes/com/sun/crypto/provider/GCTR.java +@@ -1,5 +1,5 @@ + /* +- * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2013, 2017 Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it +@@ -29,52 +29,43 @@ + + package com.sun.crypto.provider; + +-import java.security.*; +-import javax.crypto.*; ++import javax.crypto.IllegalBlockSizeException; + import static com.sun.crypto.provider.AESConstants.AES_BLOCK_SIZE; + + /** + * This class represents the GCTR function defined in NIST 800-38D +- * under section 6.5. It needs to be constructed w/ an initialized +- * cipher object, and initial counter block(ICB). Given an input X +- * of arbitrary length, it processes and returns an output which has +- * the same length as X. The invariants of this class are: +- * +- * (1) The length of intialCounterBlk (and also of its clones, e.g., +- * fields counter and counterSave) is equal to AES_BLOCK_SIZE. +- * +- * (2) After construction, the field counter never becomes null, it +- * always contains a byte array of length AES_BLOCK_SIZE. ++ * under section 6.5. With a given cipher object and initial counter ++ * block, a counter mode operation is performed. Blocksize is limited ++ * to 16 bytes. + * + * If any invariant is broken, failures can occur because the + * AESCrypt.encryptBlock method can be intrinsified on the HotSpot VM + * (see JDK-8067648 for details). + * ++ * The counter mode operations can be intrinsified and parallelized ++ * by using CounterMode.implCrypt() if HotSpot VM supports it on the ++ * architecture. ++ * + *

This function is used in the implementation of GCM mode. + * + * @since 1.8 + */ +-final class GCTR { +- +- // these fields should not change after the object has been constructed +- private final SymmetricCipher aes; +- private final byte[] icb; +- +- // the current counter value +- private byte[] counter; ++final class GCTR extends CounterMode { + +- // needed for save/restore calls +- private byte[] counterSave = null; +- +- // NOTE: cipher should already be initialized + GCTR(SymmetricCipher cipher, byte[] initialCounterBlk) { +- this.aes = cipher; ++ super(cipher); + if (initialCounterBlk.length != AES_BLOCK_SIZE) { + throw new RuntimeException("length of initial counter block (" + initialCounterBlk.length + + ") not equal to AES_BLOCK_SIZE (" + AES_BLOCK_SIZE + ")"); + } +- this.icb = initialCounterBlk; +- this.counter = icb.clone(); ++ ++ iv = initialCounterBlk; ++ reset(); ++ } ++ ++ @Override ++ String getFeedback() { ++ return "GCTR"; + } + + // input must be multiples of 128-bit blocks when calling update +@@ -89,23 +80,11 @@ final class GCTR { + throw new RuntimeException("output buffer too small"); + } + +- byte[] encryptedCntr = new byte[AES_BLOCK_SIZE]; +- +- int numOfCompleteBlocks = inLen / AES_BLOCK_SIZE; +- for (int i = 0; i < numOfCompleteBlocks; i++) { +- aes.encryptBlock(counter, 0, encryptedCntr, 0); +- for (int n = 0; n < AES_BLOCK_SIZE; n++) { +- int index = (i * AES_BLOCK_SIZE + n); +- out[outOfs + index] = +- (byte) ((in[inOfs + index] ^ encryptedCntr[n])); +- } +- GaloisCounterMode.increment32(counter); +- } +- return inLen; ++ return encrypt(in, inOfs, inLen, out, outOfs); + } + + // input can be arbitrary size when calling doFinal +- protected int doFinal(byte[] in, int inOfs, int inLen, byte[] out, ++ int doFinal(byte[] in, int inOfs, int inLen, byte[] out, + int outOfs) throws IllegalBlockSizeException { + try { + if (inLen < 0) { +@@ -118,7 +97,7 @@ final class GCTR { + if (lastBlockSize != 0) { + // do the last partial block + byte[] encryptedCntr = new byte[AES_BLOCK_SIZE]; +- aes.encryptBlock(counter, 0, encryptedCntr, 0); ++ embeddedCipher.encryptBlock(counter, 0, encryptedCntr, 0); + for (int n = 0; n < lastBlockSize; n++) { + out[outOfs + completeBlkLen + n] = + (byte) ((in[inOfs + completeBlkLen + n] ^ +@@ -131,28 +110,4 @@ final class GCTR { + } + return inLen; + } +- +- /** +- * Resets the content of this object to when it's first constructed. +- */ +- void reset() { +- System.arraycopy(icb, 0, counter, 0, icb.length); +- counterSave = null; +- } +- +- /** +- * Save the current content of this object. +- */ +- void save() { +- this.counterSave = this.counter.clone(); +- } +- +- /** +- * Restores the content of this object to the previous saved one. +- */ +- void restore() { +- if (this.counterSave != null) { +- this.counter = this.counterSave; +- } +- } + } +diff --git a/jdk/src/share/classes/com/sun/crypto/provider/GHASH.java b/jdk/src/share/classes/com/sun/crypto/provider/GHASH.java +index dc42e6bbf..78f0723d7 100644 +--- a/jdk/src/share/classes/com/sun/crypto/provider/GHASH.java ++++ b/jdk/src/share/classes/com/sun/crypto/provider/GHASH.java +@@ -122,10 +122,10 @@ final class GHASH { + + } + +- /* subkeyH and state are stored in long[] for GHASH intrinsic use */ ++ /* subkeyHtbl and state are stored in long[] for GHASH intrinsic use */ + +- // hash subkey H; should not change after the object has been constructed +- private final long[] subkeyH; ++ // hashtable subkeyHtbl; holds 2*9 powers of subkeyH computed using carry-less multiplication ++ private long[] subkeyHtbl; + + // buffer for storing hash + private final long[] state; +@@ -147,9 +147,9 @@ final class GHASH { + throw new ProviderException("Internal error"); + } + state = new long[2]; +- this.subkeyH = new long[2]; +- this.subkeyH[0] = getLong(subkeyH, 0); +- this.subkeyH[1] = getLong(subkeyH, 8); ++ subkeyHtbl = new long[2*9]; ++ subkeyHtbl[0] = getLong(subkeyH, 0); ++ subkeyHtbl[1] = getLong(subkeyH, 8); + } + + /** +@@ -192,8 +192,8 @@ final class GHASH { + if (inLen == 0) { + return; + } +- ghashRangeCheck(in, inOfs, inLen, state, subkeyH); +- processBlocks(in, inOfs, inLen/AES_BLOCK_SIZE, state, subkeyH); ++ ghashRangeCheck(in, inOfs, inLen, state, subkeyHtbl); ++ processBlocks(in, inOfs, inLen/AES_BLOCK_SIZE, state, subkeyHtbl); + } + + private static void ghashRangeCheck(byte[] in, int inOfs, int inLen, long[] st, long[] subH) { +@@ -217,8 +217,8 @@ final class GHASH { + throw new RuntimeException("internal state has invalid length: " + + st.length); + } +- if (subH.length != 2) { +- throw new RuntimeException("internal subkeyH has invalid length: " + ++ if (subH.length != 18) { ++ throw new RuntimeException("internal subkeyHtbl has invalid length: " + + subH.length); + } + } +diff --git a/jdk/src/share/classes/sun/security/ssl/SSLSocketImpl.java b/jdk/src/share/classes/sun/security/ssl/SSLSocketImpl.java +index ab93e3097..dd2618455 100644 +--- a/jdk/src/share/classes/sun/security/ssl/SSLSocketImpl.java ++++ b/jdk/src/share/classes/sun/security/ssl/SSLSocketImpl.java +@@ -439,6 +439,8 @@ public final class SSLSocketImpl + if (!conContext.isNegotiated) { + readHandshakeRecord(); + } ++ } catch (InterruptedIOException iioe) { ++ handleException(iioe); + } catch (IOException ioe) { + throw conContext.fatal(Alert.HANDSHAKE_FAILURE, + "Couldn't kickstart handshaking", ioe); +@@ -1309,12 +1311,11 @@ public final class SSLSocketImpl + } + } catch (SSLException ssle) { + throw ssle; ++ } catch (InterruptedIOException iioe) { ++ // don't change exception in case of timeouts or interrupts ++ throw iioe; + } catch (IOException ioe) { +- if (!(ioe instanceof SSLException)) { +- throw new SSLException("readHandshakeRecord", ioe); +- } else { +- throw ioe; +- } ++ throw new SSLException("readHandshakeRecord", ioe); + } + } + +@@ -1375,6 +1376,9 @@ public final class SSLSocketImpl + } + } catch (SSLException ssle) { + throw ssle; ++ } catch (InterruptedIOException iioe) { ++ // don't change exception in case of timeouts or interrupts ++ throw iioe; + } catch (IOException ioe) { + if (!(ioe instanceof SSLException)) { + throw new SSLException("readApplicationRecord", ioe); +diff --git a/jdk/src/share/classes/sun/security/ssl/SSLSocketInputRecord.java b/jdk/src/share/classes/sun/security/ssl/SSLSocketInputRecord.java +index 401822759..ab5712acc 100644 +--- a/jdk/src/share/classes/sun/security/ssl/SSLSocketInputRecord.java ++++ b/jdk/src/share/classes/sun/security/ssl/SSLSocketInputRecord.java +@@ -26,6 +26,7 @@ + package sun.security.ssl; + + import java.io.EOFException; ++import java.io.InterruptedIOException; + import java.io.IOException; + import java.io.InputStream; + import java.io.OutputStream; +@@ -47,37 +48,31 @@ import sun.security.ssl.SSLCipher.SSLReadCipher; + final class SSLSocketInputRecord extends InputRecord implements SSLRecord { + private InputStream is = null; + private OutputStream os = null; +- private final byte[] temporary = new byte[1024]; ++ private final byte[] header = new byte[headerSize]; ++ private int headerOff = 0; ++ // Cache for incomplete record body. ++ private ByteBuffer recordBody = ByteBuffer.allocate(1024); + + private boolean formatVerified = false; // SSLv2 ruled out? + + // Cache for incomplete handshake messages. + private ByteBuffer handshakeBuffer = null; + +- private boolean hasHeader = false; // Had read the record header +- + SSLSocketInputRecord(HandshakeHash handshakeHash) { + super(handshakeHash, SSLReadCipher.nullTlsReadCipher()); + } + + @Override + int bytesInCompletePacket() throws IOException { +- if (!hasHeader) { +- // read exactly one record +- try { +- int really = read(is, temporary, 0, headerSize); +- if (really < 0) { +- // EOF: peer shut down incorrectly +- return -1; +- } +- } catch (EOFException eofe) { +- // The caller will handle EOF. +- return -1; +- } +- hasHeader = true; ++ // read header ++ try { ++ readHeader(); ++ } catch (EOFException eofe) { ++ // The caller will handle EOF. ++ return -1; + } + +- byte byteZero = temporary[0]; ++ byte byteZero = header[0]; + int len = 0; + + /* +@@ -93,9 +88,9 @@ final class SSLSocketInputRecord extends InputRecord implements SSLRecord { + * Last sanity check that it's not a wild record + */ + if (!ProtocolVersion.isNegotiable( +- temporary[1], temporary[2], false)) { ++ header[1], header[2], false)) { + throw new SSLException("Unrecognized record version " + +- ProtocolVersion.nameOf(temporary[1], temporary[2]) + ++ ProtocolVersion.nameOf(header[1], header[2]) + + " , plaintext connection?"); + } + +@@ -109,8 +104,8 @@ final class SSLSocketInputRecord extends InputRecord implements SSLRecord { + /* + * One of the SSLv3/TLS message types. + */ +- len = ((temporary[3] & 0xFF) << 8) + +- (temporary[4] & 0xFF) + headerSize; ++ len = ((header[3] & 0xFF) << 8) + ++ (header[4] & 0xFF) + headerSize; + } else { + /* + * Must be SSLv2 or something unknown. +@@ -121,11 +116,11 @@ final class SSLSocketInputRecord extends InputRecord implements SSLRecord { + */ + boolean isShort = ((byteZero & 0x80) != 0); + +- if (isShort && ((temporary[2] == 1) || (temporary[2] == 4))) { ++ if (isShort && ((header[2] == 1) || (header[2] == 4))) { + if (!ProtocolVersion.isNegotiable( +- temporary[3], temporary[4], false)) { ++ header[3], header[4], false)) { + throw new SSLException("Unrecognized record version " + +- ProtocolVersion.nameOf(temporary[3], temporary[4]) + ++ ProtocolVersion.nameOf(header[3], header[4]) + + " , plaintext connection?"); + } + +@@ -138,9 +133,9 @@ final class SSLSocketInputRecord extends InputRecord implements SSLRecord { + // + // int mask = (isShort ? 0x7F : 0x3F); + // len = ((byteZero & mask) << 8) + +- // (temporary[1] & 0xFF) + (isShort ? 2 : 3); ++ // (header[1] & 0xFF) + (isShort ? 2 : 3); + // +- len = ((byteZero & 0x7F) << 8) + (temporary[1] & 0xFF) + 2; ++ len = ((byteZero & 0x7F) << 8) + (header[1] & 0xFF) + 2; + } else { + // Gobblygook! + throw new SSLException( +@@ -160,34 +155,41 @@ final class SSLSocketInputRecord extends InputRecord implements SSLRecord { + return null; + } + +- if (!hasHeader) { +- // read exactly one record +- int really = read(is, temporary, 0, headerSize); +- if (really < 0) { +- throw new EOFException("SSL peer shut down incorrectly"); +- } +- hasHeader = true; +- } ++ // read header ++ readHeader(); + +- Plaintext plaintext = null; +- if (!formatVerified) { +- formatVerified = true; ++ Plaintext[] plaintext = null; ++ boolean cleanInBuffer = true; ++ try { ++ if (!formatVerified) { ++ formatVerified = true; + +- /* +- * The first record must either be a handshake record or an +- * alert message. If it's not, it is either invalid or an +- * SSLv2 message. +- */ +- if ((temporary[0] != ContentType.HANDSHAKE.id) && +- (temporary[0] != ContentType.ALERT.id)) { +- hasHeader = false; +- return handleUnknownRecord(temporary); ++ /* ++ * The first record must either be a handshake record or an ++ * alert message. If it's not, it is either invalid or an ++ * SSLv2 message. ++ */ ++ if ((header[0] != ContentType.HANDSHAKE.id) && ++ (header[0] != ContentType.ALERT.id)) { ++ plaintext = handleUnknownRecord(); ++ } + } +- } + +- // The record header should has consumed. +- hasHeader = false; +- return decodeInputRecord(temporary); ++ // The record header should has consumed. ++ if (plaintext == null) { ++ plaintext = decodeInputRecord(); ++ } ++ } catch(InterruptedIOException e) { ++ // do not clean header and recordBody in case of Socket Timeout ++ cleanInBuffer = false; ++ throw e; ++ } finally { ++ if (cleanInBuffer) { ++ headerOff = 0; ++ recordBody.clear(); ++ } ++ } ++ return plaintext; + } + + @Override +@@ -200,9 +202,7 @@ final class SSLSocketInputRecord extends InputRecord implements SSLRecord { + this.os = outputStream; + } + +- // Note that destination may be null +- private Plaintext[] decodeInputRecord( +- byte[] header) throws IOException, BadPaddingException { ++ private Plaintext[] decodeInputRecord() throws IOException, BadPaddingException { + byte contentType = header[0]; // pos: 0 + byte majorVersion = header[1]; // pos: 1 + byte minorVersion = header[2]; // pos: 2 +@@ -227,30 +227,27 @@ final class SSLSocketInputRecord extends InputRecord implements SSLRecord { + } + + // +- // Read a complete record. ++ // Read a complete record and store in the recordBody ++ // recordBody is used to cache incoming record and restore in case of ++ // read operation timedout + // +- ByteBuffer destination = ByteBuffer.allocate(headerSize + contentLen); +- int dstPos = destination.position(); +- destination.put(temporary, 0, headerSize); +- while (contentLen > 0) { +- int howmuch = Math.min(temporary.length, contentLen); +- int really = read(is, temporary, 0, howmuch); +- if (really < 0) { +- throw new EOFException("SSL peer shut down incorrectly"); ++ if (recordBody.position() == 0) { ++ if (recordBody.capacity() < contentLen) { ++ recordBody = ByteBuffer.allocate(contentLen); + } +- +- destination.put(temporary, 0, howmuch); +- contentLen -= howmuch; ++ recordBody.limit(contentLen); ++ } else { ++ contentLen = recordBody.remaining(); + } +- destination.flip(); +- destination.position(dstPos + headerSize); ++ readFully(contentLen); ++ recordBody.flip(); + + if (SSLLogger.isOn && SSLLogger.isOn("record")) { + SSLLogger.fine( + "READ: " + + ProtocolVersion.nameOf(majorVersion, minorVersion) + + " " + ContentType.nameOf(contentType) + ", length = " + +- destination.remaining()); ++ recordBody.remaining()); + } + + // +@@ -259,7 +256,7 @@ final class SSLSocketInputRecord extends InputRecord implements SSLRecord { + ByteBuffer fragment; + try { + Plaintext plaintext = +- readCipher.decrypt(contentType, destination, null); ++ readCipher.decrypt(contentType, recordBody, null); + fragment = plaintext.fragment; + contentType = plaintext.contentType; + } catch (BadPaddingException bpe) { +@@ -368,8 +365,7 @@ final class SSLSocketInputRecord extends InputRecord implements SSLRecord { + }; + } + +- private Plaintext[] handleUnknownRecord( +- byte[] header) throws IOException, BadPaddingException { ++ private Plaintext[] handleUnknownRecord() throws IOException, BadPaddingException { + byte firstByte = header[0]; + byte thirdByte = header[2]; + +@@ -411,32 +407,29 @@ final class SSLSocketInputRecord extends InputRecord implements SSLRecord { + } + + int msgLen = ((header[0] & 0x7F) << 8) | (header[1] & 0xFF); +- +- ByteBuffer destination = ByteBuffer.allocate(headerSize + msgLen); +- destination.put(temporary, 0, headerSize); +- msgLen -= 3; // had read 3 bytes of content as header +- while (msgLen > 0) { +- int howmuch = Math.min(temporary.length, msgLen); +- int really = read(is, temporary, 0, howmuch); +- if (really < 0) { +- throw new EOFException("SSL peer shut down incorrectly"); ++ if (recordBody.position() == 0) { ++ if (recordBody.capacity() < (headerSize + msgLen)) { ++ recordBody = ByteBuffer.allocate(headerSize + msgLen); + } +- +- destination.put(temporary, 0, howmuch); +- msgLen -= howmuch; ++ recordBody.limit(headerSize + msgLen); ++ recordBody.put(header, 0, headerSize); ++ } else { ++ msgLen = recordBody.remaining(); + } +- destination.flip(); ++ msgLen -= 3; // had read 3 bytes of content as header ++ readFully(msgLen); ++ recordBody.flip(); + + /* + * If we can map this into a V3 ClientHello, read and + * hash the rest of the V2 handshake, turn it into a + * V3 ClientHello message, and pass it up. + */ +- destination.position(2); // exclude the header +- handshakeHash.receive(destination); +- destination.position(0); ++ recordBody.position(2); // exclude the header ++ handshakeHash.receive(recordBody); ++ recordBody.position(0); + +- ByteBuffer converted = convertToClientHello(destination); ++ ByteBuffer converted = convertToClientHello(recordBody); + + if (SSLLogger.isOn && SSLLogger.isOn("packet")) { + SSLLogger.fine( +@@ -456,28 +449,42 @@ final class SSLSocketInputRecord extends InputRecord implements SSLRecord { + } + } + +- // Read the exact bytes of data, otherwise, return -1. +- private static int read(InputStream is, +- byte[] buffer, int offset, int len) throws IOException { +- int n = 0; +- while (n < len) { +- int readLen = is.read(buffer, offset + n, len - n); +- if (readLen < 0) { +- if (SSLLogger.isOn && SSLLogger.isOn("packet")) { +- SSLLogger.fine("Raw read: EOF"); +- } +- return -1; ++ // Read the exact bytes of data, otherwise, throw IOException. ++ private int readFully(int len) throws IOException { ++ int end = len + recordBody.position(); ++ int off = recordBody.position(); ++ try { ++ while (off < end) { ++ off += read(is, recordBody.array(), off, end - off); + } ++ } finally { ++ recordBody.position(off); ++ } ++ return len; ++ } ++ ++ // Read SSE record header, otherwise, throw IOException. ++ private int readHeader() throws IOException { ++ while (headerOff < headerSize) { ++ headerOff += read(is, header, headerOff, headerSize - headerOff); ++ } ++ return headerSize; ++ } + ++ private static int read(InputStream is, byte[] buf, int off, int len) throws IOException { ++ int readLen = is.read(buf, off, len); ++ if (readLen < 0) { + if (SSLLogger.isOn && SSLLogger.isOn("packet")) { +- ByteBuffer bb = ByteBuffer.wrap(buffer, offset + n, readLen); +- SSLLogger.fine("Raw read", bb); ++ SSLLogger.fine("Raw read: EOF"); + } +- +- n += readLen; ++ throw new EOFException("SSL peer shut down incorrectly"); + } + +- return n; ++ if (SSLLogger.isOn && SSLLogger.isOn("packet")) { ++ ByteBuffer bb = ByteBuffer.wrap(buf, off, readLen); ++ SSLLogger.fine("Raw read", bb); ++ } ++ return readLen; + } + + // Try to use up the input stream without impact the performance too much. +diff --git a/jdk/src/share/classes/sun/security/ssl/SSLTransport.java b/jdk/src/share/classes/sun/security/ssl/SSLTransport.java +index b3d03b370..78e13ea2c 100644 +--- a/jdk/src/share/classes/sun/security/ssl/SSLTransport.java ++++ b/jdk/src/share/classes/sun/security/ssl/SSLTransport.java +@@ -27,6 +27,7 @@ package sun.security.ssl; + + import java.io.EOFException; + import java.io.IOException; ++import java.io.InterruptedIOException; + import java.nio.ByteBuffer; + import javax.crypto.AEADBadTagException; + import javax.crypto.BadPaddingException; +@@ -134,6 +135,9 @@ interface SSLTransport { + } catch (EOFException eofe) { + // rethrow EOFException, the call will handle it if neede. + throw eofe; ++ } catch (InterruptedIOException iioe) { ++ // don't close the Socket in case of timeouts or interrupts. ++ throw iioe; + } catch (IOException ioe) { + throw context.fatal(Alert.UNEXPECTED_MESSAGE, ioe); + } +diff --git a/jdk/test/micro/org/openjdk/bench/javax/crypto/full/AESGCMBench.java b/jdk/test/micro/org/openjdk/bench/javax/crypto/full/AESGCMBench.java +new file mode 100644 +index 000000000..258672f59 +--- /dev/null ++++ b/jdk/test/micro/org/openjdk/bench/javax/crypto/full/AESGCMBench.java +@@ -0,0 +1,128 @@ ++/* ++ * Copyright (c) 2015, 2019, Oracle and/or its affiliates. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ */ ++package org.openjdk.bench.javax.crypto.full; ++ ++import org.openjdk.jmh.annotations.Benchmark; ++import org.openjdk.jmh.annotations.Param; ++import org.openjdk.jmh.annotations.Setup; ++ ++import javax.crypto.Cipher; ++import javax.crypto.spec.GCMParameterSpec; ++import javax.crypto.spec.SecretKeySpec; ++ ++/** ++ * This performance tests runs AES/GCM encryption and decryption using byte[] ++ * as input and output buffers for single and multi-part testing. ++ * ++ * This test rotates the IV and creates a new GCMParameterSpec for each encrypt ++ * benchmark operation ++ */ ++ ++public class AESGCMBench extends CryptoBase { ++ ++ @Param({"128"}) ++ private int keyLength; ++ ++ @Param({"1024", "1500", "4096", "16384"}) ++ private int dataSize; ++ ++ byte[] encryptedData; ++ byte[] in, out; ++ private Cipher encryptCipher; ++ private Cipher decryptCipher; ++ SecretKeySpec ks; ++ GCMParameterSpec gcm_spec; ++ byte[] iv; ++ ++ private static final int IV_BUFFER_SIZE = 32; ++ private static final int IV_MODULO = IV_BUFFER_SIZE - 16; ++ int iv_index = 0; ++ int updateLen = 0; ++ ++ private int next_iv_index() { ++ int r = iv_index; ++ iv_index = (iv_index + 1) % IV_MODULO; ++ return r; ++ } ++ ++ @Setup ++ public void setup() throws Exception { ++ setupProvider(); ++ ++ // Setup key material ++ byte[] keystring = fillSecureRandom(new byte[keyLength / 8]); ++ ks = new SecretKeySpec(keystring, "AES"); ++ iv = fillSecureRandom(new byte[IV_BUFFER_SIZE]); ++ gcm_spec = new GCMParameterSpec(96, iv, next_iv_index(), 16); ++ ++ // Setup Cipher classes ++ encryptCipher = makeCipher(prov, "AES/GCM/NoPadding"); ++ encryptCipher.init(Cipher.ENCRYPT_MODE, ks, gcm_spec); ++ decryptCipher = makeCipher(prov, "AES/GCM/NoPadding"); ++ decryptCipher.init(Cipher.DECRYPT_MODE, ks, ++ encryptCipher.getParameters(). ++ getParameterSpec(GCMParameterSpec.class)); ++ ++ // Setup input/output buffers ++ in = fillRandom(new byte[dataSize]); ++ encryptedData = new byte[encryptCipher.getOutputSize(in.length)]; ++ out = new byte[encryptedData.length]; ++ encryptCipher.doFinal(in, 0, in.length, encryptedData, 0); ++ updateLen = in.length / 2; ++ ++ } ++ ++ @Benchmark ++ public void encrypt() throws Exception { ++ gcm_spec = new GCMParameterSpec(96, iv, next_iv_index(), 16); ++ encryptCipher.init(Cipher.ENCRYPT_MODE, ks, gcm_spec); ++ encryptCipher.doFinal(in, 0, in.length, out, 0); ++ } ++ ++ @Benchmark ++ public void encryptMultiPart() throws Exception { ++ gcm_spec = new GCMParameterSpec(96, iv, next_iv_index(), 16); ++ encryptCipher.init(Cipher.ENCRYPT_MODE, ks, gcm_spec); ++ int outOfs = encryptCipher.update(in, 0, updateLen, out, 0); ++ encryptCipher.doFinal(in, updateLen, in.length - updateLen, ++ out, outOfs); ++ } ++ ++ @Benchmark ++ public void decrypt() throws Exception { ++ decryptCipher.init(Cipher.DECRYPT_MODE, ks, ++ encryptCipher.getParameters(). ++ getParameterSpec(GCMParameterSpec.class)); ++ decryptCipher.doFinal(encryptedData, 0, encryptedData.length, out, 0); ++ } ++ ++ @Benchmark ++ public void decryptMultiPart() throws Exception { ++ decryptCipher.init(Cipher.DECRYPT_MODE, ks, ++ encryptCipher.getParameters(). ++ getParameterSpec(GCMParameterSpec.class)); ++ decryptCipher.update(encryptedData, 0, updateLen, out, 0); ++ decryptCipher.doFinal(encryptedData, updateLen, ++ encryptedData.length - updateLen, out, 0); ++ } ++} +\ No newline at end of file +diff --git a/jdk/test/micro/org/openjdk/bench/javax/crypto/full/AESGCMByteBuffer.java b/jdk/test/micro/org/openjdk/bench/javax/crypto/full/AESGCMByteBuffer.java +new file mode 100644 +index 000000000..cb6d20c51 +--- /dev/null ++++ b/jdk/test/micro/org/openjdk/bench/javax/crypto/full/AESGCMByteBuffer.java +@@ -0,0 +1,163 @@ ++/* ++ * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. Oracle designates this ++ * particular file as subject to the "Classpath" exception as provided ++ * by Oracle in the LICENSE file that accompanied this code. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ */ ++package org.openjdk.bench.javax.crypto.full; ++ ++import org.openjdk.jmh.annotations.Benchmark; ++import org.openjdk.jmh.annotations.Param; ++import org.openjdk.jmh.annotations.Setup; ++ ++import javax.crypto.Cipher; ++import javax.crypto.spec.GCMParameterSpec; ++import javax.crypto.spec.SecretKeySpec; ++import java.nio.ByteBuffer; ++ ++/** ++ * This performance tests runs AES/GCM encryption and decryption using heap and ++ * direct ByteBuffers as input and output buffers for single and multi-part ++ * operations. ++ * ++ * This test rotates the IV and creates a new GCMParameterSpec for each encrypt ++ * benchmark operation ++ */ ++ ++public class AESGCMByteBuffer extends CryptoBase { ++ ++ @Param({"128"}) ++ private int keyLength; ++ ++ @Param({"1024", "1500", "4096", "16384"}) ++ private int dataSize; ++ ++ @Param({"direct", "heap"}) ++ private String dataMethod; ++ ++ byte[] data; ++ ByteBuffer encryptedData; ++ ByteBuffer in, out; ++ private Cipher encryptCipher; ++ private Cipher decryptCipher; ++ SecretKeySpec ks; ++ GCMParameterSpec gcm_spec; ++ byte[] iv; ++ ++ private static final int IV_BUFFER_SIZE = 32; ++ private static final int IV_MODULO = IV_BUFFER_SIZE - 16; ++ int iv_index = 0; ++ int updateLen = 0; ++ ++ private int next_iv_index() { ++ int r = iv_index; ++ iv_index = (iv_index + 1) % IV_MODULO; ++ return r; ++ } ++ ++ @Setup ++ public void setup() throws Exception { ++ setupProvider(); ++ ++ // Setup key material ++ byte[] keystring = fillSecureRandom(new byte[keyLength / 8]); ++ ks = new SecretKeySpec(keystring, "AES"); ++ iv = fillSecureRandom(new byte[IV_BUFFER_SIZE]); ++ gcm_spec = new GCMParameterSpec(96, iv, next_iv_index(), 16); ++ ++ // Setup Cipher classes ++ encryptCipher = makeCipher(prov, "AES/GCM/NoPadding"); ++ encryptCipher.init(Cipher.ENCRYPT_MODE, ks, gcm_spec); ++ decryptCipher = makeCipher(prov, "AES/GCM/NoPadding"); ++ decryptCipher.init(Cipher.DECRYPT_MODE, ks, ++ encryptCipher.getParameters(). ++ getParameterSpec(GCMParameterSpec.class)); ++ ++ // Setup input/output buffers ++ data = fillRandom(new byte[dataSize]); ++ if (dataMethod.equalsIgnoreCase("direct")) { ++ in = ByteBuffer.allocateDirect(data.length); ++ in.put(data); ++ in.flip(); ++ encryptedData = ByteBuffer.allocateDirect( ++ encryptCipher.getOutputSize(data.length)); ++ out = ByteBuffer.allocateDirect(encryptedData.capacity()); ++ } else if (dataMethod.equalsIgnoreCase("heap")) { ++ in = ByteBuffer.wrap(data); ++ encryptedData = ByteBuffer.allocate( ++ encryptCipher.getOutputSize(data.length)); ++ out = ByteBuffer.allocate(encryptedData.capacity()); ++ } ++ ++ encryptCipher.doFinal(in, encryptedData); ++ encryptedData.flip(); ++ in.flip(); ++ updateLen = in.remaining() / 2; ++ } ++ ++ @Benchmark ++ public void encrypt() throws Exception { ++ gcm_spec = new GCMParameterSpec(96, iv, next_iv_index(), 16); ++ encryptCipher.init(Cipher.ENCRYPT_MODE, ks, gcm_spec); ++ encryptCipher.doFinal(in, out); ++ out.flip(); ++ in.flip(); ++ } ++ ++ @Benchmark ++ public void encryptMultiPart() throws Exception { ++ gcm_spec = new GCMParameterSpec(96, iv, next_iv_index(), 16); ++ encryptCipher.init(Cipher.ENCRYPT_MODE, ks, gcm_spec); ++ in.limit(updateLen); ++ encryptCipher.update(in, out); ++ in.limit(in.capacity()); ++ encryptCipher.doFinal(in, out); ++ out.flip(); ++ in.flip(); ++ } ++ ++ @Benchmark ++ public void decrypt() throws Exception { ++ decryptCipher.init(Cipher.DECRYPT_MODE, ks, ++ encryptCipher.getParameters(). ++ getParameterSpec(GCMParameterSpec.class)); ++ decryptCipher.doFinal(encryptedData, out); ++ encryptedData.flip(); ++ out.flip(); ++ } ++ ++ @Benchmark ++ public void decryptMultiPart() throws Exception { ++ decryptCipher.init(Cipher.DECRYPT_MODE, ks, ++ encryptCipher.getParameters(). ++ getParameterSpec(GCMParameterSpec.class)); ++ ++ int len = encryptedData.remaining(); ++ encryptedData.limit(updateLen); ++ decryptCipher.update(encryptedData, out); ++ encryptedData.limit(len); ++ ++ decryptCipher.doFinal(encryptedData, out); ++ encryptedData.flip(); ++ out.flip(); ++ } ++ ++} +\ No newline at end of file +diff --git a/jdk/test/micro/org/openjdk/bench/javax/crypto/full/CryptoBase.java b/jdk/test/micro/org/openjdk/bench/javax/crypto/full/CryptoBase.java +new file mode 100644 +index 000000000..4af12703b +--- /dev/null ++++ b/jdk/test/micro/org/openjdk/bench/javax/crypto/full/CryptoBase.java +@@ -0,0 +1,102 @@ ++/* ++ * Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ */ ++package org.openjdk.bench.javax.crypto.full; ++ ++import org.openjdk.jmh.annotations.BenchmarkMode; ++import org.openjdk.jmh.annotations.Fork; ++import org.openjdk.jmh.annotations.Measurement; ++import org.openjdk.jmh.annotations.Mode; ++import org.openjdk.jmh.annotations.OutputTimeUnit; ++import org.openjdk.jmh.annotations.Param; ++import org.openjdk.jmh.annotations.Scope; ++import org.openjdk.jmh.annotations.Setup; ++import org.openjdk.jmh.annotations.State; ++import org.openjdk.jmh.annotations.Warmup; ++ ++import javax.crypto.BadPaddingException; ++import javax.crypto.Cipher; ++import javax.crypto.IllegalBlockSizeException; ++import javax.crypto.NoSuchPaddingException; ++import java.security.NoSuchAlgorithmException; ++import java.security.Provider; ++import java.security.SecureRandom; ++import java.security.Security; ++import java.util.Random; ++import java.util.concurrent.TimeUnit; ++ ++ ++@Fork(jvmArgsAppend = {"-XX:+AlwaysPreTouch"}, value = 5) ++@Warmup(iterations = 3, time = 3) ++@Measurement(iterations = 8, time = 2) ++@OutputTimeUnit(TimeUnit.SECONDS) ++@State(Scope.Thread) ++@BenchmarkMode(Mode.Throughput) ++public class CryptoBase { ++ ++ @Param({""}) ++ private String provider; ++ ++ public Provider prov = null; ++ ++ @Setup ++ public void setupProvider() { ++ if (provider != null && !provider.isEmpty()) { ++ prov = Security.getProvider(provider); ++ if (prov == null) { ++ throw new RuntimeException("Can't find prodiver \"" + provider + "\""); ++ } ++ } ++ } ++ ++ public static Cipher makeCipher(Provider prov, String algorithm) throws NoSuchPaddingException, NoSuchAlgorithmException { ++ return (prov == null) ? Cipher.getInstance(algorithm) : Cipher.getInstance(algorithm, prov); ++ } ++ ++ public static byte[][] fillRandom(byte[][] data) { ++ Random rnd = new Random(); ++ for (byte[] d : data) { ++ rnd.nextBytes(d); ++ } ++ return data; ++ } ++ ++ public static byte[] fillRandom(byte[] data) { ++ Random rnd = new Random(); ++ rnd.nextBytes(data); ++ return data; ++ } ++ ++ public static byte[] fillSecureRandom(byte[] data) { ++ SecureRandom rnd = new SecureRandom(); ++ rnd.nextBytes(data); ++ return data; ++ } ++ ++ public static byte[][] fillEncrypted(byte[][] data, Cipher encryptCipher) throws BadPaddingException, IllegalBlockSizeException { ++ byte[][] encryptedData = new byte[data.length][]; ++ for (int i = 0; i < encryptedData.length; i++) { ++ encryptedData[i] = encryptCipher.doFinal(data[i]); ++ } ++ return encryptedData; ++ } ++} +\ No newline at end of file +diff --git a/jdk/test/micro/org/openjdk/bench/javax/crypto/small/AESGCMBench.java b/jdk/test/micro/org/openjdk/bench/javax/crypto/small/AESGCMBench.java +new file mode 100644 +index 000000000..a21b0c87f +--- /dev/null ++++ b/jdk/test/micro/org/openjdk/bench/javax/crypto/small/AESGCMBench.java +@@ -0,0 +1,36 @@ ++/* ++ * Copyright (c) 2015, 2021, Oracle and/or its affiliates. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ */ ++package org.openjdk.bench.javax.crypto.small; ++ ++import org.openjdk.jmh.annotations.Param; ++ ++public class AESGCMBench extends ++ org.openjdk.bench.javax.crypto.full.AESGCMBench { ++ ++ @Param({"128"}) ++ private int keyLength; ++ ++ @Param({"1024"}) ++ private int dataSize; ++ ++} +\ No newline at end of file +diff --git a/jdk/test/micro/org/openjdk/bench/javax/crypto/small/AESGCMByteBuffer.java b/jdk/test/micro/org/openjdk/bench/javax/crypto/small/AESGCMByteBuffer.java +new file mode 100644 +index 000000000..2e389d300 +--- /dev/null ++++ b/jdk/test/micro/org/openjdk/bench/javax/crypto/small/AESGCMByteBuffer.java +@@ -0,0 +1,36 @@ ++/* ++ * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ */ ++package org.openjdk.bench.javax.crypto.small; ++ ++import org.openjdk.jmh.annotations.Param; ++ ++public class AESGCMByteBuffer extends ++ org.openjdk.bench.javax.crypto.full.AESGCMByteBuffer { ++ ++ @Param({"128"}) ++ private int keyLength; ++ ++ @Param({"1024"}) ++ private int dataSize; ++ ++} +\ No newline at end of file +diff --git a/jdk/test/sun/security/ssl/SSLSocketImpl/ClientTimeout.java b/jdk/test/sun/security/ssl/SSLSocketImpl/ClientTimeout.java +index 3eb1d7b89..7678cc71f 100644 +--- a/jdk/test/sun/security/ssl/SSLSocketImpl/ClientTimeout.java ++++ b/jdk/test/sun/security/ssl/SSLSocketImpl/ClientTimeout.java +@@ -26,8 +26,7 @@ + + /* + * @test +- * @bug 4836493 +- * @ignore need further evaluation ++ * @bug 4836493 8239798 + * @summary Socket timeouts for SSLSockets causes data corruption. + * @run main/othervm ClientTimeout + */ +diff --git a/jdk/test/sun/security/ssl/SSLSocketImpl/SSLExceptionForIOIssue.java b/jdk/test/sun/security/ssl/SSLSocketImpl/SSLExceptionForIOIssue.java +index 3e626a257..5578ea725 100644 +--- a/jdk/test/sun/security/ssl/SSLSocketImpl/SSLExceptionForIOIssue.java ++++ b/jdk/test/sun/security/ssl/SSLSocketImpl/SSLExceptionForIOIssue.java +@@ -36,7 +36,7 @@ + + import javax.net.ssl.*; + import java.io.*; +-import java.net.InetAddress; ++import java.net.*; + + public class SSLExceptionForIOIssue implements SSLContextTemplate { + +@@ -139,7 +139,7 @@ public class SSLExceptionForIOIssue implements SSLContextTemplate { + } catch (SSLProtocolException | SSLHandshakeException sslhe) { + clientException = sslhe; + System.err.println("unexpected client exception: " + sslhe); +- } catch (SSLException ssle) { ++ } catch (SSLException | SocketTimeoutException ssle) { + // the expected exception, ignore it + System.err.println("expected client exception: " + ssle); + } catch (Exception e) { +-- +2.17.1 + diff --git a/8159720-Failure-of-C2-compilation-with-tiered-preven.patch b/8159720-Failure-of-C2-compilation-with-tiered-preven.patch new file mode 100644 index 0000000000000000000000000000000000000000..a52d0c90a81ef183ecc162b86487ce0d0e75d5b2 --- /dev/null +++ b/8159720-Failure-of-C2-compilation-with-tiered-preven.patch @@ -0,0 +1,114 @@ +From 717ae5f43045b1e2d6f95c52fbd81c54ebf50977 Mon Sep 17 00:00:00 2001 +Date: Fri, 16 Sep 2022 01:12:20 +0000 +Subject: 8159720: Failure of C2 compilation with tiered prevents some + C1 compilations. + +--- + hotspot/src/share/vm/opto/compile.cpp | 2 +- + hotspot/src/share/vm/opto/compile.hpp | 10 +++------- + hotspot/src/share/vm/opto/matcher.cpp | 8 ++++---- + hotspot/src/share/vm/opto/parse1.cpp | 4 ++-- + 4 files changed, 10 insertions(+), 14 deletions(-) + +diff --git a/hotspot/src/share/vm/opto/compile.cpp b/hotspot/src/share/vm/opto/compile.cpp +index 5a42422e1..4a32e8a9f 100644 +--- a/hotspot/src/share/vm/opto/compile.cpp ++++ b/hotspot/src/share/vm/opto/compile.cpp +@@ -791,7 +791,7 @@ Compile::Compile( ciEnv* ci_env, C2Compiler* compiler, ciMethod* target, int osr + } + if (failing()) return; + if (cg == NULL) { +- record_method_not_compilable_all_tiers("cannot parse method"); ++ record_method_not_compilable("cannot parse method"); + return; + } + JVMState* jvms = build_start_state(start(), tf()); +diff --git a/hotspot/src/share/vm/opto/compile.hpp b/hotspot/src/share/vm/opto/compile.hpp +index 1150fd549..fb12b6874 100644 +--- a/hotspot/src/share/vm/opto/compile.hpp ++++ b/hotspot/src/share/vm/opto/compile.hpp +@@ -742,16 +742,12 @@ class Compile : public Phase { + bool failure_reason_is(const char* r) { return (r==_failure_reason) || (r!=NULL && _failure_reason!=NULL && strcmp(r, _failure_reason)==0); } + + void record_failure(const char* reason); +- void record_method_not_compilable(const char* reason, bool all_tiers = false) { +- // All bailouts cover "all_tiers" when TieredCompilation is off. +- if (!TieredCompilation) all_tiers = true; +- env()->record_method_not_compilable(reason, all_tiers); ++ void record_method_not_compilable(const char* reason) { ++ // Bailouts cover "all_tiers" when TieredCompilation is off. ++ env()->record_method_not_compilable(reason, !TieredCompilation); + // Record failure reason. + record_failure(reason); + } +- void record_method_not_compilable_all_tiers(const char* reason) { +- record_method_not_compilable(reason, true); +- } + bool check_node_count(uint margin, const char* reason) { + if (live_nodes() + margin > max_node_limit()) { + record_method_not_compilable(reason); +diff --git a/hotspot/src/share/vm/opto/matcher.cpp b/hotspot/src/share/vm/opto/matcher.cpp +index 07b8ee4c6..b26015ce6 100644 +--- a/hotspot/src/share/vm/opto/matcher.cpp ++++ b/hotspot/src/share/vm/opto/matcher.cpp +@@ -137,7 +137,7 @@ OptoReg::Name Matcher::warp_incoming_stk_arg( VMReg reg ) { + _in_arg_limit = OptoReg::add(warped, 1); // Bump max stack slot seen + if (!RegMask::can_represent_arg(warped)) { + // the compiler cannot represent this method's calling sequence +- C->record_method_not_compilable_all_tiers("unsupported incoming calling sequence"); ++ C->record_method_not_compilable("unsupported incoming calling sequence"); + return OptoReg::Bad; + } + return warped; +@@ -1148,7 +1148,7 @@ OptoReg::Name Matcher::warp_outgoing_stk_arg( VMReg reg, OptoReg::Name begin_out + if( warped >= out_arg_limit_per_call ) + out_arg_limit_per_call = OptoReg::add(warped,1); + if (!RegMask::can_represent_arg(warped)) { +- C->record_method_not_compilable_all_tiers("unsupported calling sequence"); ++ C->record_method_not_compilable("unsupported calling sequence"); + return OptoReg::Bad; + } + return warped; +@@ -1327,7 +1327,7 @@ MachNode *Matcher::match_sfpt( SafePointNode *sfpt ) { + uint r_cnt = mcall->tf()->range()->cnt(); + MachProjNode *proj = new (C) MachProjNode( mcall, r_cnt+10000, RegMask::Empty, MachProjNode::fat_proj ); + if (!RegMask::can_represent_arg(OptoReg::Name(out_arg_limit_per_call-1))) { +- C->record_method_not_compilable_all_tiers("unsupported outgoing calling sequence"); ++ C->record_method_not_compilable("unsupported outgoing calling sequence"); + } else { + for (int i = begin_out_arg_area; i < out_arg_limit_per_call; i++) + proj->_rout.Insert(OptoReg::Name(i)); +@@ -1515,7 +1515,7 @@ Node *Matcher::Label_Root( const Node *n, State *svec, Node *control, const Node + // out of stack space. See bugs 6272980 & 6227033 for more info. + LabelRootDepth++; + if (LabelRootDepth > MaxLabelRootDepth) { +- C->record_method_not_compilable_all_tiers("Out of stack space, increase MaxLabelRootDepth"); ++ C->record_method_not_compilable("Out of stack space, increase MaxLabelRootDepth"); + return NULL; + } + uint care = 0; // Edges matcher cares about +diff --git a/hotspot/src/share/vm/opto/parse1.cpp b/hotspot/src/share/vm/opto/parse1.cpp +index a9ef4f910..4fcd58cb4 100644 +--- a/hotspot/src/share/vm/opto/parse1.cpp ++++ b/hotspot/src/share/vm/opto/parse1.cpp +@@ -415,7 +415,7 @@ Parse::Parse(JVMState* caller, ciMethod* parse_method, float expected_uses) + _iter.reset_to_method(method()); + _flow = method()->get_flow_analysis(); + if (_flow->failing()) { +- C->record_method_not_compilable_all_tiers(_flow->failure_reason()); ++ C->record_method_not_compilable(_flow->failure_reason()); + } + + #ifndef PRODUCT +@@ -1088,7 +1088,7 @@ SafePointNode* Parse::create_entry_map() { + // Check for really stupid bail-out cases. + uint len = TypeFunc::Parms + method()->max_locals() + method()->max_stack(); + if (len >= 32760) { +- C->record_method_not_compilable_all_tiers("too many local variables"); ++ C->record_method_not_compilable("too many local variables"); + return NULL; + } + +-- +2.18.0.huawei.25 + diff --git a/8168926.patch b/8168926.patch deleted file mode 100644 index 2eef8859afc511bf16dda001ba30f870fd750b38..0000000000000000000000000000000000000000 --- a/8168926.patch +++ /dev/null @@ -1,85 +0,0 @@ -From 72853c670c97aae4eab64a5e9edb3c7176beaf6a Mon Sep 17 00:00:00 2001 -Date: Fri, 22 Jan 2021 16:36:41 +0800 -Subject: 8168926: C2: Bytecode escape analyzer crashes due to - stack overflow - -Summary: :8168926: C2: Bytecode escape analyzer crashes due to stack overflow -LLT: N/A -Bug url: https://bugs.openjdk.java.net/browse/JDK-8168926 ---- - hotspot/src/share/vm/ci/bcEscapeAnalyzer.cpp | 30 ++++++++++++++++++-- - hotspot/src/share/vm/ci/ciMethod.hpp | 12 +++++--- - 2 files changed, 35 insertions(+), 7 deletions(-) - -diff --git a/hotspot/src/share/vm/ci/bcEscapeAnalyzer.cpp b/hotspot/src/share/vm/ci/bcEscapeAnalyzer.cpp -index 2b9e0e514..34bdbe94d 100644 ---- a/hotspot/src/share/vm/ci/bcEscapeAnalyzer.cpp -+++ b/hotspot/src/share/vm/ci/bcEscapeAnalyzer.cpp -@@ -894,9 +894,33 @@ void BCEscapeAnalyzer::iterate_one_block(ciBlock *blk, StateInfo &state, Growabl - ciMethod* target = s.get_method(ignored_will_link, &declared_signature); - ciKlass* holder = s.get_declared_method_holder(); - assert(declared_signature != NULL, "cannot be null"); -- // Push appendix argument, if one. -- if (s.has_appendix()) { -- state.apush(unknown_obj); -+ // If the current bytecode has an attached appendix argument, -+ // push an unknown object to represent that argument. (Analysis -+ // of dynamic call sites, especially invokehandle calls, needs -+ // the appendix argument on the stack, in addition to "regular" arguments -+ // pushed onto the stack by bytecode instructions preceding the call.) -+ // -+ // The escape analyzer does _not_ use the ciBytecodeStream::has_appendix(s) -+ // method to determine whether the current bytecode has an appendix argument. -+ // The has_appendix() method obtains the appendix from the -+ // ConstantPoolCacheEntry::_f1 field, which can happen concurrently with -+ // resolution of dynamic call sites. Callees in the -+ // ciBytecodeStream::get_method() call above also access the _f1 field; -+ // interleaving the get_method() and has_appendix() calls in the current -+ // method with call site resolution can lead to an inconsistent view of -+ // the current method's argument count. In particular, some interleaving(s) -+ // can cause the method's argument count to not include the appendix, which -+ // then leads to stack over-/underflow in the escape analyzer. -+ // -+ // Instead of pushing the argument if has_appendix() is true, the escape analyzer -+ // pushes an appendix for all call sites targeted by invokedynamic and invokehandle -+ // instructions, except if the call site is the _invokeBasic intrinsic -+ // (that intrinsic is always targeted by an invokehandle instruction but does -+ // not have an appendix argument). -+ if (target->is_loaded() && -+ Bytecodes::has_optional_appendix(s.cur_bc_raw()) && -+ target->intrinsic_id() != vmIntrinsics::_invokeBasic) { -+ state.apush(unknown_obj); - } - // Pass in raw bytecode because we need to see invokehandle instructions. - invoke(state, s.cur_bc_raw(), target, holder); -diff --git a/hotspot/src/share/vm/ci/ciMethod.hpp b/hotspot/src/share/vm/ci/ciMethod.hpp -index 307452422..99d8dbe67 100644 ---- a/hotspot/src/share/vm/ci/ciMethod.hpp -+++ b/hotspot/src/share/vm/ci/ciMethod.hpp -@@ -133,15 +133,19 @@ class ciMethod : public ciMetadata { - check_is_loaded(); - return _signature->size() + (_flags.is_static() ? 0 : 1); - } -- // Report the number of elements on stack when invoking this method. -- // This is different than the regular arg_size because invokedynamic -- // has an implicit receiver. -+ // Report the number of elements on stack when invoking the current method. -+ // If the method is loaded, arg_size() gives precise information about the -+ // number of stack elements (using the method's signature and its flags). -+ // However, if the method is not loaded, the number of stack elements must -+ // be determined differently, as the method's flags are not yet available. -+ // The invoke_arg_size() method assumes in that case that all bytecodes except -+ // invokestatic and invokedynamic have a receiver that is also pushed onto the -+ // stack by the caller of the current method. - int invoke_arg_size(Bytecodes::Code code) const { - if (is_loaded()) { - return arg_size(); - } else { - int arg_size = _signature->size(); -- // Add a receiver argument, maybe: - if (code != Bytecodes::_invokestatic && - code != Bytecodes::_invokedynamic) { - arg_size++; --- -2.19.0 - diff --git a/8173361-various-crashes-in-JvmtiExport-post_compiled.patch b/8173361-various-crashes-in-JvmtiExport-post_compiled.patch deleted file mode 100755 index 2b14cc7a333bce48c10049f12810d85d683adb50..0000000000000000000000000000000000000000 --- a/8173361-various-crashes-in-JvmtiExport-post_compiled.patch +++ /dev/null @@ -1,290 +0,0 @@ -diff --git a/hotspot/src/share/vm/code/nmethod.cpp b/hotspot/src/share/vm/code/nmethod.cpp -index 175c195c6..01e878022 100644 ---- a/hotspot/src/share/vm/code/nmethod.cpp -+++ b/hotspot/src/share/vm/code/nmethod.cpp -@@ -1656,24 +1656,28 @@ bool nmethod::can_unload(BoolObjectClosure* is_alive, oop* root, bool unloading_ - // Transfer information from compilation to jvmti - void nmethod::post_compiled_method_load_event() { - -- Method* moop = method(); -+ // This is a bad time for a safepoint. We don't want -+ // this nmethod to get unloaded while we're queueing the event. -+ No_Safepoint_Verifier nsv; -+ -+ Method* m = method(); - #ifndef USDT2 - HS_DTRACE_PROBE8(hotspot, compiled__method__load, -- moop->klass_name()->bytes(), -- moop->klass_name()->utf8_length(), -- moop->name()->bytes(), -- moop->name()->utf8_length(), -- moop->signature()->bytes(), -- moop->signature()->utf8_length(), -+ m->klass_name()->bytes(), -+ m->klass_name()->utf8_length(), -+ m->name()->bytes(), -+ m->name()->utf8_length(), -+ m->signature()->bytes(), -+ m->signature()->utf8_length(), - insts_begin(), insts_size()); - #else /* USDT2 */ - HOTSPOT_COMPILED_METHOD_LOAD( -- (char *) moop->klass_name()->bytes(), -- moop->klass_name()->utf8_length(), -- (char *) moop->name()->bytes(), -- moop->name()->utf8_length(), -- (char *) moop->signature()->bytes(), -- moop->signature()->utf8_length(), -+ (char *) m->klass_name()->bytes(), -+ m->klass_name()->utf8_length(), -+ (char *) m->name()->bytes(), -+ m->name()->utf8_length(), -+ (char *) m->signature()->bytes(), -+ m->signature()->utf8_length(), - insts_begin(), insts_size()); - #endif /* USDT2 */ - -diff --git a/hotspot/src/share/vm/oops/instanceKlass.cpp b/hotspot/src/share/vm/oops/instanceKlass.cpp -index 895fbbf07..367c9a09d 100644 ---- a/hotspot/src/share/vm/oops/instanceKlass.cpp -+++ b/hotspot/src/share/vm/oops/instanceKlass.cpp -@@ -1786,7 +1786,7 @@ jmethodID InstanceKlass::get_jmethod_id(instanceKlassHandle ik_h, methodHandle m - // we're single threaded or at a safepoint - no locking needed - get_jmethod_id_length_value(jmeths, idnum, &length, &id); - } else { -- MutexLocker ml(JmethodIdCreation_lock); -+ MutexLockerEx ml(JmethodIdCreation_lock, Mutex::_no_safepoint_check_flag); - get_jmethod_id_length_value(jmeths, idnum, &length, &id); - } - } -@@ -1836,7 +1836,7 @@ jmethodID InstanceKlass::get_jmethod_id(instanceKlassHandle ik_h, methodHandle m - id = get_jmethod_id_fetch_or_update(ik_h, idnum, new_id, new_jmeths, - &to_dealloc_id, &to_dealloc_jmeths); - } else { -- MutexLocker ml(JmethodIdCreation_lock); -+ MutexLockerEx ml(JmethodIdCreation_lock, Mutex::_no_safepoint_check_flag); - id = get_jmethod_id_fetch_or_update(ik_h, idnum, new_id, new_jmeths, - &to_dealloc_id, &to_dealloc_jmeths); - } -diff --git a/hotspot/src/share/vm/prims/jvmtiExport.cpp b/hotspot/src/share/vm/prims/jvmtiExport.cpp -index 9b612598f..967ed200d 100644 ---- a/hotspot/src/share/vm/prims/jvmtiExport.cpp -+++ b/hotspot/src/share/vm/prims/jvmtiExport.cpp -@@ -1754,7 +1754,7 @@ jvmtiCompiledMethodLoadInlineRecord* create_inline_record(nmethod* nm) { - int stackframe = 0; - for(ScopeDesc* sd = nm->scope_desc_at(p->real_pc(nm));sd != NULL;sd = sd->sender()) { - // sd->method() can be NULL for stubs but not for nmethods. To be completely robust, include an assert that we should never see a null sd->method() -- assert(sd->method() != NULL, "sd->method() cannot be null."); -+ guarantee(sd->method() != NULL, "sd->method() cannot be null."); - record->pcinfo[scope].methods[stackframe] = sd->method()->jmethod_id(); - record->pcinfo[scope].bcis[stackframe] = sd->bci(); - stackframe++; -diff --git a/hotspot/src/share/vm/prims/jvmtiImpl.cpp b/hotspot/src/share/vm/prims/jvmtiImpl.cpp -index 3c66b1671..3bcd15ed6 100644 ---- a/hotspot/src/share/vm/prims/jvmtiImpl.cpp -+++ b/hotspot/src/share/vm/prims/jvmtiImpl.cpp -@@ -897,9 +897,6 @@ JvmtiDeferredEvent JvmtiDeferredEvent::compiled_method_load_event( - nmethod* nm) { - JvmtiDeferredEvent event = JvmtiDeferredEvent(TYPE_COMPILED_METHOD_LOAD); - event._event_data.compiled_method_load = nm; -- // Keep the nmethod alive until the ServiceThread can process -- // this deferred event. -- nmethodLocker::lock_nmethod(nm); - return event; - } - -@@ -932,14 +929,12 @@ JvmtiDeferredEvent JvmtiDeferredEvent::dynamic_code_generated_event( - } - - void JvmtiDeferredEvent::post() { -- assert(ServiceThread::is_service_thread(Thread::current()), -+ assert(Thread::current()->is_service_thread(), - "Service thread must post enqueued events"); - switch(_type) { - case TYPE_COMPILED_METHOD_LOAD: { - nmethod* nm = _event_data.compiled_method_load; - JvmtiExport::post_compiled_method_load(nm); -- // done with the deferred event so unlock the nmethod -- nmethodLocker::unlock_nmethod(nm); - break; - } - case TYPE_COMPILED_METHOD_UNLOAD: { -@@ -969,6 +964,21 @@ void JvmtiDeferredEvent::post() { - } - } - -+// Keep the nmethod for compiled_method_load from being unloaded. -+void JvmtiDeferredEvent::oops_do(OopClosure* f, CodeBlobClosure* cf) { -+ if (cf != NULL && _type == TYPE_COMPILED_METHOD_LOAD) { -+ cf->do_code_blob(_event_data.compiled_method_load); -+ } -+} -+ -+// The sweeper calls this and marks the nmethods here on the stack so that -+// they cannot be turned into zombies while in the queue. -+void JvmtiDeferredEvent::nmethods_do(CodeBlobClosure* cf) { -+ if (cf != NULL && _type == TYPE_COMPILED_METHOD_LOAD) { -+ cf->do_code_blob(_event_data.compiled_method_load); -+ } // May add UNLOAD event but it doesn't work yet. -+} -+ - JvmtiDeferredEventQueue::QueueNode* JvmtiDeferredEventQueue::_queue_tail = NULL; - JvmtiDeferredEventQueue::QueueNode* JvmtiDeferredEventQueue::_queue_head = NULL; - -@@ -1084,3 +1094,15 @@ void JvmtiDeferredEventQueue::process_pending_events() { - } - } - } -+ -+void JvmtiDeferredEventQueue::oops_do(OopClosure* f, CodeBlobClosure* cf) { -+ for(QueueNode* node = _queue_head; node != NULL; node = node->next()) { -+ node->event().oops_do(f, cf); -+ } -+} -+ -+void JvmtiDeferredEventQueue::nmethods_do(CodeBlobClosure* cf) { -+ for(QueueNode* node = _queue_head; node != NULL; node = node->next()) { -+ node->event().nmethods_do(cf); -+ } -+} -diff --git a/hotspot/src/share/vm/prims/jvmtiImpl.hpp b/hotspot/src/share/vm/prims/jvmtiImpl.hpp -index 9f36f28fb..d74789451 100644 ---- a/hotspot/src/share/vm/prims/jvmtiImpl.hpp -+++ b/hotspot/src/share/vm/prims/jvmtiImpl.hpp -@@ -492,6 +492,10 @@ class JvmtiDeferredEvent VALUE_OBJ_CLASS_SPEC { - - // Actually posts the event. - void post() NOT_JVMTI_RETURN; -+ // Sweeper support to keep nmethods from being zombied while in the queue. -+ void nmethods_do(CodeBlobClosure* cf); -+ // GC support to keep nmethod from being unloaded while in the queue. -+ void oops_do(OopClosure* f, CodeBlobClosure* cf); - }; - - /** -@@ -511,7 +515,7 @@ class JvmtiDeferredEventQueue : AllStatic { - QueueNode(const JvmtiDeferredEvent& event) - : _event(event), _next(NULL) {} - -- const JvmtiDeferredEvent& event() const { return _event; } -+ JvmtiDeferredEvent& event() { return _event; } - QueueNode* next() const { return _next; } - - void set_next(QueueNode* next) { _next = next; } -@@ -529,6 +533,10 @@ class JvmtiDeferredEventQueue : AllStatic { - static bool has_events() NOT_JVMTI_RETURN_(false); - static void enqueue(const JvmtiDeferredEvent& event) NOT_JVMTI_RETURN; - static JvmtiDeferredEvent dequeue() NOT_JVMTI_RETURN_(JvmtiDeferredEvent()); -+ // Sweeper support to keep nmethods from being zombied while in the queue. -+ static void nmethods_do(CodeBlobClosure* cf); -+ // GC support to keep nmethod from being unloaded while in the queue. -+ static void oops_do(OopClosure* f, CodeBlobClosure* cf); - - // Used to enqueue events without using a lock, for times (such as during - // safepoint) when we can't or don't want to lock the Service_lock. -diff --git a/hotspot/src/share/vm/runtime/serviceThread.cpp b/hotspot/src/share/vm/runtime/serviceThread.cpp -index c3a2b88a5..a2a32ad2b 100644 ---- a/hotspot/src/share/vm/runtime/serviceThread.cpp -+++ b/hotspot/src/share/vm/runtime/serviceThread.cpp -@@ -34,6 +34,7 @@ - #include "services/diagnosticFramework.hpp" - - ServiceThread* ServiceThread::_instance = NULL; -+JvmtiDeferredEvent* ServiceThread::_jvmti_event = NULL; - - void ServiceThread::initialize() { - EXCEPTION_MARK; -@@ -112,12 +113,15 @@ void ServiceThread::service_thread_entry(JavaThread* jt, TRAPS) { - } - - if (has_jvmti_events) { -+ // Get the event under the Service_lock - jvmti_event = JvmtiDeferredEventQueue::dequeue(); -+ _jvmti_event = &jvmti_event; - } - } - - if (has_jvmti_events) { -- jvmti_event.post(); -+ _jvmti_event->post(); -+ _jvmti_event = NULL; // reset - } - - if (sensors_changed) { -@@ -138,6 +142,26 @@ void ServiceThread::service_thread_entry(JavaThread* jt, TRAPS) { - } - } - --bool ServiceThread::is_service_thread(Thread* thread) { -- return thread == _instance; -+void ServiceThread::oops_do(OopClosure* f, CLDClosure* cld_f, CodeBlobClosure* cf) { -+ JavaThread::oops_do(f, cld_f, cf); -+ // The ServiceThread "owns" the JVMTI Deferred events, scan them here -+ // to keep them alive until they are processed. -+ if (cf != NULL) { -+ if (_jvmti_event != NULL) { -+ _jvmti_event->oops_do(f, cf); -+ } -+ MutexLockerEx ml(Service_lock, Mutex::_no_safepoint_check_flag); -+ JvmtiDeferredEventQueue::oops_do(f, cf); -+ } -+} -+ -+void ServiceThread::nmethods_do(CodeBlobClosure* cf) { -+ JavaThread::nmethods_do(cf); -+ if (cf != NULL) { -+ if (_jvmti_event != NULL) { -+ _jvmti_event->nmethods_do(cf); -+ } -+ MutexLockerEx ml(Service_lock, Mutex::_no_safepoint_check_flag); -+ JvmtiDeferredEventQueue::nmethods_do(cf); -+ } - } -diff --git a/hotspot/src/share/vm/runtime/serviceThread.hpp b/hotspot/src/share/vm/runtime/serviceThread.hpp -index 42373e6f7..a9c219580 100644 ---- a/hotspot/src/share/vm/runtime/serviceThread.hpp -+++ b/hotspot/src/share/vm/runtime/serviceThread.hpp -@@ -29,11 +29,13 @@ - - // A JavaThread for low memory detection support and JVMTI - // compiled-method-load events. -+class JvmtiDeferredEvent; -+ - class ServiceThread : public JavaThread { - friend class VMStructs; - private: -- - static ServiceThread* _instance; -+ static JvmtiDeferredEvent* _jvmti_event; - - static void service_thread_entry(JavaThread* thread, TRAPS); - ServiceThread(ThreadFunction entry_point) : JavaThread(entry_point) {}; -@@ -43,9 +45,11 @@ class ServiceThread : public JavaThread { - - // Hide this thread from external view. - bool is_hidden_from_external_view() const { return true; } -+ bool is_service_thread() const { return true; } - -- // Returns true if the passed thread is the service thread. -- static bool is_service_thread(Thread* thread); -+ // GC support -+ void oops_do(OopClosure* f, CLDClosure* cld_f, CodeBlobClosure* cf); -+ void nmethods_do(CodeBlobClosure* cf); - }; - - #endif // SHARE_VM_RUNTIME_SERVICETHREAD_HPP -diff --git a/hotspot/src/share/vm/runtime/thread.hpp b/hotspot/src/share/vm/runtime/thread.hpp -index cc976182d..950c1b4fa 100644 ---- a/hotspot/src/share/vm/runtime/thread.hpp -+++ b/hotspot/src/share/vm/runtime/thread.hpp -@@ -313,6 +313,7 @@ class Thread: public ThreadShadow { - virtual bool is_VM_thread() const { return false; } - virtual bool is_Java_thread() const { return false; } - virtual bool is_Compiler_thread() const { return false; } -+ virtual bool is_service_thread() const { return false; } - virtual bool is_hidden_from_external_view() const { return false; } - virtual bool is_jvmti_agent_thread() const { return false; } - // True iff the thread can perform GC operations at a safepoint. --- -2.22.0 - diff --git a/8189688-NMT-Report-per-class-load-metadata-informati.patch b/8189688-NMT-Report-per-class-load-metadata-informati.patch new file mode 100644 index 0000000000000000000000000000000000000000..63dbfa53503985307b0e8a46151af0272b94a882 --- /dev/null +++ b/8189688-NMT-Report-per-class-load-metadata-informati.patch @@ -0,0 +1,561 @@ +From 528a3b6459e34532120f468ba9afb8833d516f5a Mon Sep 17 00:00:00 2001 +From: eapen +Date: Thu, 15 Dec 2022 11:38:55 +0800 +Subject: [PATCH 19/33] I68TO2: 8189688: NMT: Report per-class load metadata + information +--- + hotspot/src/share/vm/memory/metaspace.cpp | 322 ++++++++++++++++++++++++- + hotspot/src/share/vm/memory/metaspace.hpp | 4 + + hotspot/src/share/vm/runtime/vm_operations.cpp | 4 + + hotspot/src/share/vm/runtime/vm_operations.hpp | 12 + + hotspot/src/share/vm/services/nmtDCmd.cpp | 16 +- + hotspot/src/share/vm/services/nmtDCmd.hpp | 1 + + 6 files changed, 354 insertions(+), 5 deletions(-) + +diff --git a/hotspot/src/share/vm/memory/metaspace.cpp b/hotspot/src/share/vm/memory/metaspace.cpp +index 6c4654b..cf4a112 100644 +--- a/hotspot/src/share/vm/memory/metaspace.cpp ++++ b/hotspot/src/share/vm/memory/metaspace.cpp +@@ -75,6 +75,22 @@ enum ChunkIndex { + NumberOfInUseLists = 4 + }; + ++// Helper, returns a descriptive name for the given index. ++static const char* chunk_size_name(ChunkIndex index) { ++ switch (index) { ++ case SpecializedIndex: ++ return "specialized"; ++ case SmallIndex: ++ return "small"; ++ case MediumIndex: ++ return "medium"; ++ case HumongousIndex: ++ return "humongous"; ++ default: ++ return "Invalid index"; ++ } ++} ++ + enum ChunkSizes { // in words. + ClassSpecializedChunk = 128, + SpecializedChunk = 128, +@@ -89,6 +105,18 @@ static ChunkIndex next_chunk_index(ChunkIndex i) { + return (ChunkIndex) (i+1); + } + ++static const char* scale_unit(size_t scale) { ++ switch(scale) { ++ case 1: return "BYTES"; ++ case K: return "KB"; ++ case M: return "MB"; ++ case G: return "GB"; ++ default: ++ ShouldNotReachHere(); ++ return NULL; ++ } ++} ++ + volatile intptr_t MetaspaceGC::_capacity_until_GC = 0; + uint MetaspaceGC::_shrink_factor = 0; + bool MetaspaceGC::_should_concurrent_collect = false; +@@ -141,6 +169,18 @@ class ChunkManager : public CHeapObj { + } + void verify_free_chunks_count(); + ++ struct ChunkManagerStatistics { ++ size_t num_by_type[NumberOfFreeLists]; ++ size_t single_size_by_type[NumberOfFreeLists]; ++ size_t total_size_by_type[NumberOfFreeLists]; ++ size_t num_humongous_chunks; ++ size_t total_size_humongous_chunks; ++ }; ++ ++ void locked_get_statistics(ChunkManagerStatistics* stat) const; ++ void get_statistics(ChunkManagerStatistics* stat) const; ++ static void print_statistics(const ChunkManagerStatistics* stat, outputStream* out, size_t scale); ++ + public: + + ChunkManager(size_t specialized_size, size_t small_size, size_t medium_size) +@@ -157,6 +197,9 @@ class ChunkManager : public CHeapObj { + // for special, small, medium, and humongous chunks. + ChunkIndex list_index(size_t size); + ++ // Map a given index to the chunk size. ++ size_t size_by_index(ChunkIndex index) const; ++ + // Remove the chunk from its freelist. It is + // expected to be on one of the _free_chunks[] lists. + void remove_chunk(Metachunk* chunk); +@@ -249,6 +292,10 @@ class ChunkManager : public CHeapObj { + void locked_print_sum_free_chunks(outputStream* st); + + void print_on(outputStream* st) const; ++ ++ // Prints composition for both non-class and (if available) ++ // class chunk manager. ++ static void print_all_chunkmanagers(outputStream* out, size_t scale = 1); + }; + + // Used to manage the free list of Metablocks (a block corresponds +@@ -1707,7 +1754,6 @@ bool Metadebug::test_metadata_failure() { + #endif + + // ChunkManager methods +- + size_t ChunkManager::free_chunks_total_words() { + return _free_chunks_total; + } +@@ -1729,6 +1775,12 @@ size_t ChunkManager::free_chunks_count() { + return _free_chunks_count; + } + ++size_t ChunkManager::size_by_index(ChunkIndex index) const { ++ index_bounds_check(index); ++ assert(index != HumongousIndex, "Do not call for humongous chunks."); ++ return _free_chunks[index].size(); ++} ++ + void ChunkManager::locked_verify_free_chunks_total() { + assert_lock_strong(SpaceManager::expand_lock()); + assert(sum_free_chunks() == _free_chunks_total, +@@ -1918,7 +1970,83 @@ Metachunk* ChunkManager::chunk_freelist_allocate(size_t word_size) { + + void ChunkManager::print_on(outputStream* out) const { + if (PrintFLSStatistics != 0) { +- const_cast(this)->humongous_dictionary()->report_statistics(); ++ _humongous_dictionary.report_statistics(); ++ } ++} ++ ++void ChunkManager::locked_get_statistics(ChunkManagerStatistics* stat) const { ++ assert_lock_strong(SpaceManager::expand_lock()); ++ for (ChunkIndex i = ZeroIndex; i < NumberOfFreeLists; i = next_chunk_index(i)) { ++ stat->num_by_type[i] = num_free_chunks(i); ++ stat->single_size_by_type[i] = size_by_index(i); ++ stat->total_size_by_type[i] = size_free_chunks_in_bytes(i); ++ } ++ stat->num_humongous_chunks = num_free_chunks(HumongousIndex); ++ stat->total_size_humongous_chunks = size_free_chunks_in_bytes(HumongousIndex); ++} ++ ++void ChunkManager::get_statistics(ChunkManagerStatistics* stat) const { ++ MutexLockerEx cl(SpaceManager::expand_lock(), ++ Mutex::_no_safepoint_check_flag); ++ locked_get_statistics(stat); ++} ++ ++void ChunkManager::print_statistics(const ChunkManagerStatistics* stat, outputStream* out, size_t scale) { ++ size_t total = 0; ++ assert(scale == 1 || scale == K || scale == M || scale == G, "Invalid scale"); ++ ++ const char* unit = scale_unit(scale); ++ for (ChunkIndex i = ZeroIndex; i < NumberOfFreeLists; i = next_chunk_index(i)) { ++ out->print(" " SIZE_FORMAT " %s (" SIZE_FORMAT " bytes) chunks, total ", ++ stat->num_by_type[i], chunk_size_name(i), ++ stat->single_size_by_type[i]); ++ if (scale == 1) { ++ out->print_cr(SIZE_FORMAT " bytes", stat->total_size_by_type[i]); ++ } else { ++ out->print_cr("%.2f%s", (float)stat->total_size_by_type[i] / scale, unit); ++ } ++ ++ total += stat->total_size_by_type[i]; ++ } ++ ++ total += stat->total_size_humongous_chunks; ++ ++ if (scale == 1) { ++ out->print_cr(" " SIZE_FORMAT " humongous chunks, total " SIZE_FORMAT " bytes", ++ stat->num_humongous_chunks, stat->total_size_humongous_chunks); ++ ++ out->print_cr(" total size: " SIZE_FORMAT " bytes.", total); ++ } else { ++ out->print_cr(" " SIZE_FORMAT " humongous chunks, total %.2f%s", ++ stat->num_humongous_chunks, ++ (float)stat->total_size_humongous_chunks / scale, unit); ++ ++ out->print_cr(" total size: %.2f%s.", (float)total / scale, unit); ++ } ++ ++} ++ ++void ChunkManager::print_all_chunkmanagers(outputStream* out, size_t scale) { ++ assert(scale == 1 || scale == K || scale == M || scale == G, "Invalid scale"); ++ ++ // Note: keep lock protection only to retrieving statistics; keep printing ++ // out of lock protection ++ ChunkManagerStatistics stat; ++ out->print_cr("Chunkmanager (non-class):"); ++ const ChunkManager* const non_class_cm = Metaspace::chunk_manager_metadata(); ++ if (non_class_cm != NULL) { ++ non_class_cm->get_statistics(&stat); ++ ChunkManager::print_statistics(&stat, out, scale); ++ } else { ++ out->print_cr("unavailable."); ++ } ++ out->print_cr("Chunkmanager (class):"); ++ const ChunkManager* const class_cm = Metaspace::chunk_manager_class(); ++ if (class_cm != NULL) { ++ class_cm->get_statistics(&stat); ++ ChunkManager::print_statistics(&stat, out, scale); ++ } else { ++ out->print_cr("unavailable."); + } + } + +@@ -2930,6 +3058,195 @@ void MetaspaceAux::print_waste(outputStream* out) { + } + } + ++class MetadataStats VALUE_OBJ_CLASS_SPEC { ++private: ++ size_t _capacity; ++ size_t _used; ++ size_t _free; ++ size_t _waste; ++ ++public: ++ MetadataStats() : _capacity(0), _used(0), _free(0), _waste(0) { } ++ MetadataStats(size_t capacity, size_t used, size_t free, size_t waste) ++ : _capacity(capacity), _used(used), _free(free), _waste(waste) { } ++ ++ void add(const MetadataStats& stats) { ++ _capacity += stats.capacity(); ++ _used += stats.used(); ++ _free += stats.free(); ++ _waste += stats.waste(); ++ } ++ ++ size_t capacity() const { return _capacity; } ++ size_t used() const { return _used; } ++ size_t free() const { return _free; } ++ size_t waste() const { return _waste; } ++ ++ void print_on(outputStream* out, size_t scale) const; ++}; ++ ++ ++void MetadataStats::print_on(outputStream* out, size_t scale) const { ++ const char* unit = scale_unit(scale); ++ out->print_cr("capacity=%10.2f%s used=%10.2f%s free=%10.2f%s waste=%10.2f%s", ++ (float)capacity() / scale, unit, ++ (float)used() / scale, unit, ++ (float)free() / scale, unit, ++ (float)waste() / scale, unit); ++} ++ ++class PrintCLDMetaspaceInfoClosure : public CLDClosure { ++private: ++ outputStream* _out; ++ size_t _scale; ++ ++ size_t _total_count; ++ MetadataStats _total_metadata; ++ MetadataStats _total_class; ++ ++ size_t _total_anon_count; ++ MetadataStats _total_anon_metadata; ++ MetadataStats _total_anon_class; ++ ++public: ++ PrintCLDMetaspaceInfoClosure(outputStream* out, size_t scale = K) ++ : _out(out), _scale(scale), _total_count(0), _total_anon_count(0) { } ++ ++ ~PrintCLDMetaspaceInfoClosure() { ++ print_summary(); ++ } ++ ++ void do_cld(ClassLoaderData* cld) { ++ assert(SafepointSynchronize::is_at_safepoint(), "Must be at a safepoint"); ++ ++ if (cld->is_unloading()) return; ++ Metaspace* msp = cld->metaspace_or_null(); ++ if (msp == NULL) { ++ return; ++ } ++ ++ bool anonymous = false; ++ if (cld->is_anonymous()) { ++ _out->print_cr("ClassLoader: for anonymous class"); ++ anonymous = true; ++ } else { ++ ResourceMark rm; ++ _out->print_cr("ClassLoader: %s", cld->loader_name()); ++ } ++ ++ print_metaspace(msp, anonymous); ++ _out->cr(); ++ } ++ ++private: ++ void print_metaspace(Metaspace* msp, bool anonymous); ++ void print_summary() const; ++}; ++ ++void PrintCLDMetaspaceInfoClosure::print_metaspace(Metaspace* msp, bool anonymous){ ++ assert(msp != NULL, "Sanity"); ++ SpaceManager* vsm = msp->vsm(); ++ const char* unit = scale_unit(_scale); ++ ++ size_t capacity = vsm->sum_capacity_in_chunks_in_use() * BytesPerWord; ++ size_t used = vsm->sum_used_in_chunks_in_use() * BytesPerWord; ++ size_t free = vsm->sum_free_in_chunks_in_use() * BytesPerWord; ++ size_t waste = vsm->sum_waste_in_chunks_in_use() * BytesPerWord; ++ ++ _total_count ++; ++ MetadataStats metadata_stats(capacity, used, free, waste); ++ _total_metadata.add(metadata_stats); ++ ++ if (anonymous) { ++ _total_anon_count ++; ++ _total_anon_metadata.add(metadata_stats); ++ } ++ ++ _out->print(" Metadata "); ++ metadata_stats.print_on(_out, _scale); ++ ++ if (Metaspace::using_class_space()) { ++ vsm = msp->class_vsm(); ++ ++ capacity = vsm->sum_capacity_in_chunks_in_use() * BytesPerWord; ++ used = vsm->sum_used_in_chunks_in_use() * BytesPerWord; ++ free = vsm->sum_free_in_chunks_in_use() * BytesPerWord; ++ waste = vsm->sum_waste_in_chunks_in_use() * BytesPerWord; ++ ++ MetadataStats class_stats(capacity, used, free, waste); ++ _total_class.add(class_stats); ++ ++ if (anonymous) { ++ _total_anon_class.add(class_stats); ++ } ++ ++ _out->print(" Class data "); ++ class_stats.print_on(_out, _scale); ++ } ++} ++ ++void PrintCLDMetaspaceInfoClosure::print_summary() const { ++ const char* unit = scale_unit(_scale); ++ _out->cr(); ++ _out->print_cr("Summary:"); ++ ++ MetadataStats total; ++ total.add(_total_metadata); ++ total.add(_total_class); ++ ++ _out->print(" Total class loaders=" SIZE_FORMAT_W(6) " ", _total_count); ++ total.print_on(_out, _scale); ++ ++ _out->print(" Metadata "); ++ _total_metadata.print_on(_out, _scale); ++ ++ if (Metaspace::using_class_space()) { ++ _out->print(" Class data "); ++ _total_class.print_on(_out, _scale); ++ } ++ _out->cr(); ++ ++ MetadataStats total_anon; ++ total_anon.add(_total_anon_metadata); ++ total_anon.add(_total_anon_class); ++ ++ _out->print("For anonymous classes=" SIZE_FORMAT_W(6) " ", _total_anon_count); ++ total_anon.print_on(_out, _scale); ++ ++ _out->print(" Metadata "); ++ _total_anon_metadata.print_on(_out, _scale); ++ ++ if (Metaspace::using_class_space()) { ++ _out->print(" Class data "); ++ _total_anon_class.print_on(_out, _scale); ++ } ++} ++ ++void MetaspaceAux::print_metadata_for_nmt(outputStream* out, size_t scale) { ++ const char* unit = scale_unit(scale); ++ out->print_cr("Metaspaces:"); ++ out->print_cr(" Metadata space: reserved=" SIZE_FORMAT_W(10) "%s committed=" SIZE_FORMAT_W(10) "%s", ++ reserved_bytes(Metaspace::NonClassType) / scale, unit, ++ committed_bytes(Metaspace::NonClassType) / scale, unit); ++ if (Metaspace::using_class_space()) { ++ out->print_cr(" Class space: reserved=" SIZE_FORMAT_W(10) "%s committed=" SIZE_FORMAT_W(10) "%s", ++ reserved_bytes(Metaspace::ClassType) / scale, unit, ++ committed_bytes(Metaspace::ClassType) / scale, unit); ++ } ++ ++ out->cr(); ++ ChunkManager::print_all_chunkmanagers(out, scale); ++ ++ out->cr(); ++ out->print_cr("Per-classloader metadata:"); ++ out->cr(); ++ ++ PrintCLDMetaspaceInfoClosure cl(out, scale); ++ ClassLoaderDataGraph::cld_do(&cl); ++} ++ ++ ++ + // Dump global metaspace things from the end of ClassLoaderDataGraph + void MetaspaceAux::dump(outputStream* out) { + out->print_cr("All Metaspace:"); +@@ -3743,6 +4060,7 @@ void Metaspace::report_metadata_oome(ClassLoaderData* loader_data, size_t word_s + loader_data->dump(gclog_or_tty); + } + MetaspaceAux::dump(gclog_or_tty); ++ ChunkManager::print_all_chunkmanagers(gclog_or_tty); + } + + bool out_of_compressed_class_space = false; +diff --git a/hotspot/src/share/vm/memory/metaspace.hpp b/hotspot/src/share/vm/memory/metaspace.hpp +index 122dd4b..ff1b232 100644 +--- a/hotspot/src/share/vm/memory/metaspace.hpp ++++ b/hotspot/src/share/vm/memory/metaspace.hpp +@@ -65,6 +65,7 @@ class MetaspaceTracer; + class MetaWord; + class Mutex; + class outputStream; ++class PrintCLDMetaspaceInfoClosure; + class SpaceManager; + class VirtualSpaceList; + +@@ -88,6 +89,7 @@ class Metaspace : public CHeapObj { + friend class VM_CollectForMetadataAllocation; + friend class MetaspaceGC; + friend class MetaspaceAux; ++ friend class PrintCLDMetaspaceInfoClosure; + + public: + enum MetadataType { +@@ -372,6 +374,8 @@ class MetaspaceAux : AllStatic { + return min_chunk_size_words() * BytesPerWord; + } + ++ static void print_metadata_for_nmt(outputStream* out, size_t scale = K); ++ + static bool has_chunk_free_list(Metaspace::MetadataType mdtype); + static MetaspaceChunkFreeListSummary chunk_free_list_summary(Metaspace::MetadataType mdtype); + +diff --git a/hotspot/src/share/vm/runtime/vm_operations.cpp b/hotspot/src/share/vm/runtime/vm_operations.cpp +index d401ea6..b42d18f 100644 +--- a/hotspot/src/share/vm/runtime/vm_operations.cpp ++++ b/hotspot/src/share/vm/runtime/vm_operations.cpp +@@ -209,6 +209,10 @@ void VM_PrintJNI::doit() { + JNIHandles::print_on(_out); + } + ++void VM_PrintMetadata::doit() { ++ MetaspaceAux::print_metadata_for_nmt(_out, _scale); ++} ++ + VM_FindDeadlocks::~VM_FindDeadlocks() { + if (_deadlocks != NULL) { + DeadlockCycle* cycle = _deadlocks; +diff --git a/hotspot/src/share/vm/runtime/vm_operations.hpp b/hotspot/src/share/vm/runtime/vm_operations.hpp +index 3744040..19c33f8 100644 +--- a/hotspot/src/share/vm/runtime/vm_operations.hpp ++++ b/hotspot/src/share/vm/runtime/vm_operations.hpp +@@ -101,6 +101,7 @@ + template(ClassLoaderHierarchyOperation) \ + template(JFROldObject) \ + template(PrintClasses) \ ++ template(PrintMetadata) \ + + class VM_Operation: public CHeapObj { + public: +@@ -329,6 +330,17 @@ class VM_PrintJNI: public VM_Operation { + void doit(); + }; + ++class VM_PrintMetadata : public VM_Operation { ++ private: ++ outputStream* _out; ++ size_t _scale; ++ public: ++ VM_PrintMetadata(outputStream* out, size_t scale) : _out(out), _scale(scale) {}; ++ ++ VMOp_Type type() const { return VMOp_PrintMetadata; } ++ void doit(); ++}; ++ + class DeadlockCycle; + class VM_FindDeadlocks: public VM_Operation { + private: +diff --git a/hotspot/src/share/vm/services/nmtDCmd.cpp b/hotspot/src/share/vm/services/nmtDCmd.cpp +index fcad784..659ca33 100644 +--- a/hotspot/src/share/vm/services/nmtDCmd.cpp ++++ b/hotspot/src/share/vm/services/nmtDCmd.cpp +@@ -24,6 +24,8 @@ + #include "precompiled.hpp" + + #include "runtime/mutexLocker.hpp" ++#include "runtime/vmThread.hpp" ++#include "runtime/vm_operations.hpp" + #include "services/nmtDCmd.hpp" + #include "services/memReporter.hpp" + #include "services/memTracker.hpp" +@@ -38,6 +40,8 @@ NMTDCmd::NMTDCmd(outputStream* output, + _detail("detail", "request runtime to report memory allocation >= " + "1K by each callsite.", + "BOOLEAN", false, "false"), ++ _metadata("metadata", "request runtime to report metadata information", ++ "BOOLEAN", false, "false"), + _baseline("baseline", "request runtime to baseline current memory usage, " \ + "so it can be compared against in later time.", + "BOOLEAN", false, "false"), +@@ -57,6 +61,7 @@ NMTDCmd::NMTDCmd(outputStream* output, + "STRING", false, "KB") { + _dcmdparser.add_dcmd_option(&_summary); + _dcmdparser.add_dcmd_option(&_detail); ++ _dcmdparser.add_dcmd_option(&_metadata); + _dcmdparser.add_dcmd_option(&_baseline); + _dcmdparser.add_dcmd_option(&_summary_diff); + _dcmdparser.add_dcmd_option(&_detail_diff); +@@ -92,6 +97,7 @@ void NMTDCmd::execute(DCmdSource source, TRAPS) { + int nopt = 0; + if (_summary.is_set() && _summary.value()) { ++nopt; } + if (_detail.is_set() && _detail.value()) { ++nopt; } ++ if (_metadata.is_set() && _metadata.value()) { ++nopt; } + if (_baseline.is_set() && _baseline.value()) { ++nopt; } + if (_summary_diff.is_set() && _summary_diff.value()) { ++nopt; } + if (_detail_diff.is_set() && _detail_diff.value()) { ++nopt; } +@@ -100,7 +106,7 @@ void NMTDCmd::execute(DCmdSource source, TRAPS) { + + if (nopt > 1) { + output()->print_cr("At most one of the following option can be specified: " \ +- "summary, detail, baseline, summary.diff, detail.diff, shutdown"); ++ "summary, detail, metadata, baseline, summary.diff, detail.diff, shutdown"); + return; + } else if (nopt == 0) { + if (_summary.is_set()) { +@@ -118,9 +124,13 @@ void NMTDCmd::execute(DCmdSource source, TRAPS) { + report(true, scale_unit); + } else if (_detail.value()) { + if (!check_detail_tracking_level(output())) { +- return; +- } ++ return; ++ } + report(false, scale_unit); ++ } else if (_metadata.value()) { ++ size_t scale = get_scale(_scale.value()); ++ VM_PrintMetadata op(output(), scale); ++ VMThread::execute(&op); + } else if (_baseline.value()) { + MemBaseline& baseline = MemTracker::get_baseline(); + if (!baseline.baseline(MemTracker::tracking_level() != NMT_detail)) { +diff --git a/hotspot/src/share/vm/services/nmtDCmd.hpp b/hotspot/src/share/vm/services/nmtDCmd.hpp +index df1ab36..bbd1391 100644 +--- a/hotspot/src/share/vm/services/nmtDCmd.hpp ++++ b/hotspot/src/share/vm/services/nmtDCmd.hpp +@@ -39,6 +39,7 @@ class NMTDCmd: public DCmdWithParser { + protected: + DCmdArgument _summary; + DCmdArgument _detail; ++ DCmdArgument _metadata; + DCmdArgument _baseline; + DCmdArgument _summary_diff; + DCmdArgument _detail_diff; +-- +1.8.3.1 diff --git a/8194154.patch b/8194154.patch deleted file mode 100644 index 0adbad3e0f5aa4cf01e72b87e0f1957539147602..0000000000000000000000000000000000000000 --- a/8194154.patch +++ /dev/null @@ -1,156 +0,0 @@ -From 5547d1f77577ad8514136255eed16921e4d02845 Mon Sep 17 00:00:00 2001 -Date: Fri, 22 Jan 2021 15:23:47 +0800 -Subject: 8194154: System property user.dir should not be changed - -Summary: : System property user.dir should not be changed -LLT: jdk/test/java/io/File/UserDirChangedTest.java -Bug url: https://bugs.openjdk.java.net/browse/JDK-8194154 ---- - .../classes/java/io/UnixFileSystem.java | 11 +++- - .../classes/java/io/WinNTFileSystem.java | 11 +++- - jdk/test/java/io/File/UserDirChangedTest.java | 51 +++++++++++++++++++ - 3 files changed, 69 insertions(+), 4 deletions(-) - create mode 100644 jdk/test/java/io/File/UserDirChangedTest.java - -diff --git a/jdk/src/solaris/classes/java/io/UnixFileSystem.java b/jdk/src/solaris/classes/java/io/UnixFileSystem.java -index fb0fef636..a6ef2d3a6 100644 ---- a/jdk/src/solaris/classes/java/io/UnixFileSystem.java -+++ b/jdk/src/solaris/classes/java/io/UnixFileSystem.java -@@ -1,5 +1,5 @@ - /* -- * Copyright (c) 1998, 2010, Oracle and/or its affiliates. All rights reserved. -+ * Copyright (c) 1998, 2018, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it -@@ -34,6 +34,7 @@ class UnixFileSystem extends FileSystem { - private final char slash; - private final char colon; - private final String javaHome; -+ private final String userDir; - - public UnixFileSystem() { - slash = AccessController.doPrivileged( -@@ -42,6 +43,8 @@ class UnixFileSystem extends FileSystem { - new GetPropertyAction("path.separator")).charAt(0); - javaHome = AccessController.doPrivileged( - new GetPropertyAction("java.home")); -+ userDir = AccessController.doPrivileged( -+ new GetPropertyAction("user.dir")); - } - - -@@ -130,7 +133,11 @@ class UnixFileSystem extends FileSystem { - - public String resolve(File f) { - if (isAbsolute(f)) return f.getPath(); -- return resolve(System.getProperty("user.dir"), f.getPath()); -+ SecurityManager sm = System.getSecurityManager(); -+ if (sm != null) { -+ sm.checkPropertyAccess("user.dir"); -+ } -+ return resolve(userDir, f.getPath()); - } - - // Caches for canonicalization results to improve startup performance. -diff --git a/jdk/src/windows/classes/java/io/WinNTFileSystem.java b/jdk/src/windows/classes/java/io/WinNTFileSystem.java -index caa47f80c..1844a662a 100644 ---- a/jdk/src/windows/classes/java/io/WinNTFileSystem.java -+++ b/jdk/src/windows/classes/java/io/WinNTFileSystem.java -@@ -1,5 +1,5 @@ - /* -- * Copyright (c) 2001, 2012, Oracle and/or its affiliates. All rights reserved. -+ * Copyright (c) 2001, 2018, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it -@@ -40,6 +40,7 @@ class WinNTFileSystem extends FileSystem { - private final char slash; - private final char altSlash; - private final char semicolon; -+ private final String userDir; - - public WinNTFileSystem() { - slash = AccessController.doPrivileged( -@@ -47,6 +48,8 @@ class WinNTFileSystem extends FileSystem { - semicolon = AccessController.doPrivileged( - new GetPropertyAction("path.separator")).charAt(0); - altSlash = (this.slash == '\\') ? '/' : '\\'; -+ userDir = AccessController.doPrivileged( -+ new GetPropertyAction("user.dir")); - } - - private boolean isSlash(char c) { -@@ -343,7 +346,11 @@ class WinNTFileSystem extends FileSystem { - private String getUserPath() { - /* For both compatibility and security, - we must look this up every time */ -- return normalize(System.getProperty("user.dir")); -+ SecurityManager sm = System.getSecurityManager(); -+ if (sm != null) { -+ sm.checkPropertyAccess("user.dir"); -+ } -+ return normalize(userDir); - } - - private String getDrive(String path) { -diff --git a/jdk/test/java/io/File/UserDirChangedTest.java b/jdk/test/java/io/File/UserDirChangedTest.java -new file mode 100644 -index 000000000..9eccb768e ---- /dev/null -+++ b/jdk/test/java/io/File/UserDirChangedTest.java -@@ -0,0 +1,51 @@ -+/* -+ * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. -+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. -+ * -+ * This code is free software; you can redistribute it and/or modify it -+ * under the terms of the GNU General Public License version 2 only, as -+ * published by the Free Software Foundation. -+ * -+ * This code is distributed in the hope that it will be useful, but WITHOUT -+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License -+ * version 2 for more details (a copy is included in the LICENSE file that -+ * accompanied this code). -+ * -+ * You should have received a copy of the GNU General Public License version -+ * 2 along with this work; if not, write to the Free Software Foundation, -+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. -+ * -+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA -+ * or visit www.oracle.com if you need additional information or have any -+ * questions. -+ */ -+ -+/* @test -+ @bug 8194154 -+ @summary Test changing property user.dir on impacting getCanonicalPath -+ @run main/othervm UserDirChangedTest -+ */ -+ -+import java.io.File; -+ -+public class UserDirChangedTest { -+ public static void main(String[] args) throws Exception { -+ String keyUserDir = "user.dir"; -+ String userDirNew = "/home/a/b/c/"; -+ String fileName = "./a"; -+ -+ String userDir = System.getProperty(keyUserDir); -+ File file = new File(fileName); -+ String canFilePath = file.getCanonicalPath(); -+ -+ // now reset user.dir, this will cause crash on linux without bug 8194154 fixed. -+ System.setProperty(keyUserDir, userDirNew); -+ String newCanFilePath = file.getCanonicalPath(); -+ System.out.format("%24s %48s%n", "Canonical Path = ", canFilePath); -+ System.out.format("%24s %48s%n", "new Canonical Path = ", newCanFilePath); -+ if (!canFilePath.equals(newCanFilePath)) { -+ throw new RuntimeException("Changing property user.dir should have no effect on getCanonicalPath"); -+ } -+ } -+} --- -2.19.0 - diff --git a/8198553-jcmd-separate-Metaspace-statistics-from-NMT.patch b/8198553-jcmd-separate-Metaspace-statistics-from-NMT.patch new file mode 100644 index 0000000000000000000000000000000000000000..d2b5208b6aa9b35cfdb7cf8be6616fa1194975a0 --- /dev/null +++ b/8198553-jcmd-separate-Metaspace-statistics-from-NMT.patch @@ -0,0 +1,165 @@ +From d19efeaa550f4a2069273d9ab23c27a53bf4ec91 Mon Sep 17 00:00:00 2001 +From: eapen +Date: Thu, 15 Dec 2022 19:00:53 +0800 +Subject: [PATCH 22/33] I68TO2: 8198553: jcmd: separate Metaspace statistics from NMT +--- + .../src/share/vm/services/diagnosticCommand.cpp | 1 + + .../src/share/vm/services/diagnosticCommand.hpp | 23 +++++++++++++- + hotspot/src/share/vm/services/metaspaceDCmd.cpp | 36 ++++++++++++++++++++++ + hotspot/src/share/vm/services/nmtDCmd.cpp | 10 +----- + hotspot/src/share/vm/services/nmtDCmd.hpp | 1 - + 5 files changed, 60 insertions(+), 11 deletions(-) + create mode 100644 hotspot/src/share/vm/services/metaspaceDCmd.cpp + +diff --git a/hotspot/src/share/vm/services/diagnosticCommand.cpp b/hotspot/src/share/vm/services/diagnosticCommand.cpp +index d3b91d9..c9bc7d2 100644 +--- a/hotspot/src/share/vm/services/diagnosticCommand.cpp ++++ b/hotspot/src/share/vm/services/diagnosticCommand.cpp +@@ -67,6 +67,7 @@ void DCmdRegistrant::register_dcmds(){ + DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl(full_export, true, false)); + DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl(full_export, true, false)); + DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl(full_export, true, false)); ++ DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl(full_export, true, false)); + #endif // INCLUDE_SERVICES + DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl(full_export, true, false)); + DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl(full_export, true, false)); +diff --git a/hotspot/src/share/vm/services/diagnosticCommand.hpp b/hotspot/src/share/vm/services/diagnosticCommand.hpp +index f86ab5f..275e053 100644 +--- a/hotspot/src/share/vm/services/diagnosticCommand.hpp ++++ b/hotspot/src/share/vm/services/diagnosticCommand.hpp +@@ -1,5 +1,5 @@ + /* +- * Copyright (c) 2011, 2017, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2011, 2018, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it +@@ -487,4 +487,25 @@ public: + } + }; + ++class MetaspaceDCmd : public DCmd { ++public: ++ MetaspaceDCmd(outputStream* output, bool heap); ++ static const char* name() { ++ return "VM.metaspace"; ++ } ++ static const char* description() { ++ return "Prints the statistics for the metaspace"; ++ } ++ static const char* impact() { ++ return "Medium: Depends on number of classes loaded."; ++ } ++ static const JavaPermission permission() { ++ JavaPermission p = {"java.lang.management.ManagementPermission", ++ "monitor", NULL}; ++ return p; ++ } ++ static int num_arguments() { return 0; } ++ virtual void execute(DCmdSource source, TRAPS); ++}; ++ + #endif // SHARE_VM_SERVICES_DIAGNOSTICCOMMAND_HPP +diff --git a/hotspot/src/share/vm/services/metaspaceDCmd.cpp b/hotspot/src/share/vm/services/metaspaceDCmd.cpp +new file mode 100644 +index 0000000..9d4262e +--- /dev/null ++++ b/hotspot/src/share/vm/services/metaspaceDCmd.cpp +@@ -0,0 +1,36 @@ ++/* ++ * Copyright (c) 2022, Huawei Technologies Co., Ltd. All rights reserved. ++ * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ * ++ */ ++#include "precompiled.hpp" ++#include "memory/metaspace.hpp" ++#include "services/diagnosticCommand.hpp" ++ ++MetaspaceDCmd::MetaspaceDCmd(outputStream* output, bool heap): DCmd(output, heap) { ++} ++ ++void MetaspaceDCmd::execute(DCmdSource source, TRAPS) { ++ const size_t scale = 1 * K; ++ VM_PrintMetadata op(output(), scale); ++ VMThread::execute(&op); ++} +diff --git a/hotspot/src/share/vm/services/nmtDCmd.cpp b/hotspot/src/share/vm/services/nmtDCmd.cpp +index 659ca33..2635bbb 100644 +--- a/hotspot/src/share/vm/services/nmtDCmd.cpp ++++ b/hotspot/src/share/vm/services/nmtDCmd.cpp +@@ -1,5 +1,5 @@ + /* +- * Copyright (c) 2012, 2014, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2012, 2018, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it +@@ -40,8 +40,6 @@ NMTDCmd::NMTDCmd(outputStream* output, + _detail("detail", "request runtime to report memory allocation >= " + "1K by each callsite.", + "BOOLEAN", false, "false"), +- _metadata("metadata", "request runtime to report metadata information", +- "BOOLEAN", false, "false"), + _baseline("baseline", "request runtime to baseline current memory usage, " \ + "so it can be compared against in later time.", + "BOOLEAN", false, "false"), +@@ -61,7 +59,6 @@ NMTDCmd::NMTDCmd(outputStream* output, + "STRING", false, "KB") { + _dcmdparser.add_dcmd_option(&_summary); + _dcmdparser.add_dcmd_option(&_detail); +- _dcmdparser.add_dcmd_option(&_metadata); + _dcmdparser.add_dcmd_option(&_baseline); + _dcmdparser.add_dcmd_option(&_summary_diff); + _dcmdparser.add_dcmd_option(&_detail_diff); +@@ -97,7 +94,6 @@ void NMTDCmd::execute(DCmdSource source, TRAPS) { + int nopt = 0; + if (_summary.is_set() && _summary.value()) { ++nopt; } + if (_detail.is_set() && _detail.value()) { ++nopt; } +- if (_metadata.is_set() && _metadata.value()) { ++nopt; } + if (_baseline.is_set() && _baseline.value()) { ++nopt; } + if (_summary_diff.is_set() && _summary_diff.value()) { ++nopt; } + if (_detail_diff.is_set() && _detail_diff.value()) { ++nopt; } +@@ -127,10 +123,6 @@ void NMTDCmd::execute(DCmdSource source, TRAPS) { + return; + } + report(false, scale_unit); +- } else if (_metadata.value()) { +- size_t scale = get_scale(_scale.value()); +- VM_PrintMetadata op(output(), scale); +- VMThread::execute(&op); + } else if (_baseline.value()) { + MemBaseline& baseline = MemTracker::get_baseline(); + if (!baseline.baseline(MemTracker::tracking_level() != NMT_detail)) { +diff --git a/hotspot/src/share/vm/services/nmtDCmd.hpp b/hotspot/src/share/vm/services/nmtDCmd.hpp +index bbd1391..df1ab36 100644 +--- a/hotspot/src/share/vm/services/nmtDCmd.hpp ++++ b/hotspot/src/share/vm/services/nmtDCmd.hpp +@@ -39,7 +39,6 @@ class NMTDCmd: public DCmdWithParser { + protected: + DCmdArgument _summary; + DCmdArgument _detail; +- DCmdArgument _metadata; + DCmdArgument _baseline; + DCmdArgument _summary_diff; + DCmdArgument _detail_diff; +-- +1.8.3.1 diff --git a/8200332-Improve-GCM-counting.patch b/8200332-Improve-GCM-counting.patch new file mode 100644 index 0000000000000000000000000000000000000000..5990053ab1c82aab9e0c5f2a4136cb5450bf1a52 --- /dev/null +++ b/8200332-Improve-GCM-counting.patch @@ -0,0 +1,68 @@ +From 30883daeac796c877a765cedee52f27f51444203 Mon Sep 17 00:00:00 2001 +Date: Thu, 8 Sep 2022 10:22:32 +0800 +Subject: 8200332: Improve GCM counting + +Bug url: https://bugs.openjdk.org/browse/JDK-8200332 +--- + .../classes/com/sun/crypto/provider/GCTR.java | 31 ++++++++++++++++++- + 1 file changed, 30 insertions(+), 1 deletion(-) + +diff --git a/jdk/src/share/classes/com/sun/crypto/provider/GCTR.java b/jdk/src/share/classes/com/sun/crypto/provider/GCTR.java +index 6a394e448..1ab0f63db 100644 +--- a/jdk/src/share/classes/com/sun/crypto/provider/GCTR.java ++++ b/jdk/src/share/classes/com/sun/crypto/provider/GCTR.java +@@ -29,6 +29,8 @@ + + package com.sun.crypto.provider; + ++import java.nio.ByteBuffer; ++import java.nio.ByteOrder; + import javax.crypto.IllegalBlockSizeException; + import static com.sun.crypto.provider.AESConstants.AES_BLOCK_SIZE; + +@@ -68,6 +70,15 @@ final class GCTR extends CounterMode { + return "GCTR"; + } + ++ // return the number of blocks until the lower 32 bits roll over ++ private long blocksUntilRollover() { ++ ByteBuffer buf = ByteBuffer.wrap(counter, counter.length - 4, 4); ++ buf.order(ByteOrder.BIG_ENDIAN); ++ long ctr32 = 0xFFFFFFFFL & buf.getInt(); ++ long blocksLeft = (1L << 32) - ctr32; ++ return blocksLeft; ++ } ++ + // input must be multiples of 128-bit blocks when calling update + int update(byte[] in, int inOfs, int inLen, byte[] out, int outOfs) { + if (inLen - inOfs > in.length) { +@@ -80,7 +91,25 @@ final class GCTR extends CounterMode { + throw new RuntimeException("output buffer too small"); + } + +- return encrypt(in, inOfs, inLen, out, outOfs); ++ long blocksLeft = blocksUntilRollover(); ++ int numOfCompleteBlocks = inLen / AES_BLOCK_SIZE; ++ if (numOfCompleteBlocks >= blocksLeft) { ++ // Counter Mode encryption cannot be used because counter will ++ // roll over incorrectly. Use GCM-specific code instead. ++ byte[] encryptedCntr = new byte[AES_BLOCK_SIZE]; ++ for (int i = 0; i < numOfCompleteBlocks; i++) { ++ embeddedCipher.encryptBlock(counter, 0, encryptedCntr, 0); ++ for (int n = 0; n < AES_BLOCK_SIZE; n++) { ++ int index = (i * AES_BLOCK_SIZE + n); ++ out[outOfs + index] = ++ (byte) ((in[inOfs + index] ^ encryptedCntr[n])); ++ } ++ GaloisCounterMode.increment32(counter); ++ } ++ return inLen; ++ } else { ++ return encrypt(in, inOfs, inLen, out, outOfs); ++ } + } + + // input can be arbitrary size when calling doFinal +-- +2.22.0 + diff --git a/8200720-Print-additional-information-in-thread-dump-.patch b/8200720-Print-additional-information-in-thread-dump-.patch new file mode 100644 index 0000000000000000000000000000000000000000..12c1b59cac2fbfa08db35f96db2a7bed4fe748de --- /dev/null +++ b/8200720-Print-additional-information-in-thread-dump-.patch @@ -0,0 +1,409 @@ +From f68539b01f6345809266cf57fe4bdc0f45c8ab37 Mon Sep 17 00:00:00 2001 +From: eapen +Date: Fri, 16 Dec 2022 10:02:37 +0800 +Subject: [PATCH 25/33] I68TO2: 8200720: Print additional information in thread dump + (times, allocated bytes etc.) +--- + hotspot/src/share/vm/classfile/classFileParser.cpp | 4 ++ + hotspot/src/share/vm/runtime/globals.hpp | 3 ++ + hotspot/src/share/vm/runtime/thread.cpp | 29 ++++++++++--- + hotspot/src/share/vm/runtime/thread.hpp | 15 +++++-- + .../src/share/vm/runtime/threadStatisticalInfo.hpp | 49 ++++++++++++++++++++++ + hotspot/src/share/vm/runtime/vm_operations.cpp | 2 +- + hotspot/src/share/vm/runtime/vm_operations.hpp | 13 ++++-- + hotspot/src/share/vm/services/attachListener.cpp | 14 +++++-- + .../src/share/vm/services/diagnosticCommand.cpp | 6 ++- + .../src/share/vm/services/diagnosticCommand.hpp | 1 + + jdk/src/share/classes/sun/tools/jstack/JStack.java | 19 ++++++--- + 11 files changed, 131 insertions(+), 24 deletions(-) + create mode 100644 hotspot/src/share/vm/runtime/threadStatisticalInfo.hpp + +diff --git a/hotspot/src/share/vm/classfile/classFileParser.cpp b/hotspot/src/share/vm/classfile/classFileParser.cpp +index 3ec6aec..51ab4f5 100644 +--- a/hotspot/src/share/vm/classfile/classFileParser.cpp ++++ b/hotspot/src/share/vm/classfile/classFileParser.cpp +@@ -3843,6 +3843,10 @@ instanceKlassHandle ClassFileParser::parseClassFile(Symbol* name, + ClassFileStream* cfs = stream(); + // Timing + assert(THREAD->is_Java_thread(), "must be a JavaThread"); ++ ++ // increment counter ++ THREAD->statistical_info().incr_define_class_count(); ++ + JavaThread* jt = (JavaThread*) THREAD; + + PerfClassTraceTime ctimer(ClassLoader::perf_class_parse_time(), +diff --git a/hotspot/src/share/vm/runtime/globals.hpp b/hotspot/src/share/vm/runtime/globals.hpp +index 14c3c89..2631971 100644 +--- a/hotspot/src/share/vm/runtime/globals.hpp ++++ b/hotspot/src/share/vm/runtime/globals.hpp +@@ -1001,6 +1001,9 @@ class CommandLineFlags { + product(bool, PrintCompilation, false, \ + "Print compilations") \ + \ ++ product(bool, PrintExtendedThreadInfo, false, \ ++ "Print more information in thread dump") \ ++ \ + diagnostic(bool, TraceNMethodInstalls, false, \ + "Trace nmethod installation") \ + \ +diff --git a/hotspot/src/share/vm/runtime/thread.cpp b/hotspot/src/share/vm/runtime/thread.cpp +index 61627e4..bd35e95 100644 +--- a/hotspot/src/share/vm/runtime/thread.cpp ++++ b/hotspot/src/share/vm/runtime/thread.cpp +@@ -71,6 +71,7 @@ + #include "runtime/thread.inline.hpp" + #include "runtime/threadCritical.hpp" + #include "runtime/threadLocalStorage.hpp" ++#include "runtime/threadStatisticalInfo.hpp" + #include "runtime/vframe.hpp" + #include "runtime/vframeArray.hpp" + #include "runtime/vframe_hp.hpp" +@@ -859,13 +860,29 @@ void Thread::metadata_do(void f(Metadata*)) { + } + } + +-void Thread::print_on(outputStream* st) const { ++void Thread::print_on(outputStream* st, bool print_extended_info) const { + // get_priority assumes osthread initialized + if (osthread() != NULL) { + int os_prio; + if (os::get_native_priority(this, &os_prio) == OS_OK) { + st->print("os_prio=%d ", os_prio); + } ++ ++ st->print("cpu=%.2fms ", ++ os::thread_cpu_time(const_cast(this), true) / 1000000.0 ++ ); ++ st->print("elapsed=%.2fs ", ++ _statistical_info.getElapsedTime() / 1000.0 ++ ); ++ if (is_Java_thread() && (PrintExtendedThreadInfo || print_extended_info)) { ++ size_t allocated_bytes = (size_t) const_cast(this)->cooked_allocated_bytes(); ++ st->print("allocated=" SIZE_FORMAT "%s ", ++ byte_size_in_proper_unit(allocated_bytes), ++ proper_unit_for_byte_size(allocated_bytes) ++ ); ++ st->print("defined_classes=" INT64_FORMAT " ", _statistical_info.getDefineClassCount()); ++ } ++ + st->print("tid=" INTPTR_FORMAT " ", this); + ext().print_on(st); + osthread()->print_on(st); +@@ -2856,7 +2873,7 @@ void JavaThread::print_thread_state() const { + #endif // PRODUCT + + // Called by Threads::print() for VM_PrintThreads operation +-void JavaThread::print_on(outputStream *st) const { ++void JavaThread::print_on(outputStream *st, bool print_extended_info) const { + st->print("\"%s\" ", get_thread_name()); + oop thread_oop = threadObj(); + if (thread_oop != NULL) { +@@ -2864,7 +2881,7 @@ void JavaThread::print_on(outputStream *st) const { + if (java_lang_Thread::is_daemon(thread_oop)) st->print("daemon "); + st->print("prio=%d ", java_lang_Thread::priority(thread_oop)); + } +- Thread::print_on(st); ++ Thread::print_on(st, print_extended_info); + // print guess for valid stack memory region (assume 4K pages); helps lock debugging + st->print_cr("[" INTPTR_FORMAT "]", (intptr_t)last_Java_sp() & ~right_n_bits(12)); + if (thread_oop != NULL && JDK_Version::is_gte_jdk15x_version()) { +@@ -4344,7 +4361,9 @@ JavaThread *Threads::owning_thread_from_monitor_owner(address owner, bool doLock + } + + // Threads::print_on() is called at safepoint by VM_PrintThreads operation. +-void Threads::print_on(outputStream* st, bool print_stacks, bool internal_format, bool print_concurrent_locks) { ++void Threads::print_on(outputStream* st, bool print_stacks, ++ bool internal_format, bool print_concurrent_locks, ++ bool print_extended_info) { + char buf[32]; + st->print_cr("%s", os::local_time_string(buf, sizeof(buf))); + +@@ -4365,7 +4384,7 @@ void Threads::print_on(outputStream* st, bool print_stacks, bool internal_format + + ALL_JAVA_THREADS(p) { + ResourceMark rm; +- p->print_on(st); ++ p->print_on(st, print_extended_info); + if (print_stacks) { + if (internal_format) { + p->trace_stack(); +diff --git a/hotspot/src/share/vm/runtime/thread.hpp b/hotspot/src/share/vm/runtime/thread.hpp +index fcd4814..be53498 100644 +--- a/hotspot/src/share/vm/runtime/thread.hpp ++++ b/hotspot/src/share/vm/runtime/thread.hpp +@@ -41,6 +41,7 @@ + #include "runtime/stubRoutines.hpp" + #include "runtime/threadLocalStorage.hpp" + #include "runtime/thread_ext.hpp" ++#include "runtime/threadStatisticalInfo.hpp" + #include "runtime/unhandledOops.hpp" + #include "utilities/exceptions.hpp" + #include "utilities/macros.hpp" +@@ -263,6 +264,8 @@ class Thread: public ThreadShadow { + // Thread-local buffer used by MetadataOnStackMark. + MetadataOnStackBuffer* _metadata_on_stack_buffer; + ++ ThreadStatisticalInfo _statistical_info; // Statistics about the thread ++ + JFR_ONLY(DEFINE_THREAD_LOCAL_FIELD_JFR;) // Thread-local data for jfr + + ThreadExt _ext; +@@ -446,6 +449,8 @@ class Thread: public ThreadShadow { + void incr_allocated_bytes(jlong size) { _allocated_bytes += size; } + inline jlong cooked_allocated_bytes(); + ++ ThreadStatisticalInfo& statistical_info() { return _statistical_info; } ++ + JFR_ONLY(DEFINE_THREAD_LOCAL_ACCESSOR_JFR;) + JFR_ONLY(DEFINE_TRACE_SUSPEND_FLAG_METHODS) + +@@ -570,7 +575,8 @@ protected: + void set_lgrp_id(int value) { _lgrp_id = value; } + + // Printing +- void print_on(outputStream* st) const; ++ void print_on(outputStream* st, bool print_extended_info) const; ++ void print_on(outputStream* st) const { print_on(st, false); } + void print() const { print_on(tty); } + virtual void print_on_error(outputStream* st, char* buf, int buflen) const; + +@@ -1463,7 +1469,8 @@ class JavaThread: public Thread { + + // Misc. operations + char* name() const { return (char*)get_thread_name(); } +- void print_on(outputStream* st) const; ++ void print_on(outputStream* st, bool print_extended_info) const; ++ void print_on(outputStream* st) const { print_on(st, false); } + void print() const { print_on(tty); } + void print_value(); + void print_thread_state_on(outputStream* ) const PRODUCT_RETURN; +@@ -1975,10 +1982,10 @@ class Threads: AllStatic { + + // Verification + static void verify(); +- static void print_on(outputStream* st, bool print_stacks, bool internal_format, bool print_concurrent_locks); ++ static void print_on(outputStream* st, bool print_stacks, bool internal_format, bool print_concurrent_locks, bool print_extended_info); + static void print(bool print_stacks, bool internal_format) { + // this function is only used by debug.cpp +- print_on(tty, print_stacks, internal_format, false /* no concurrent lock printed */); ++ print_on(tty, print_stacks, internal_format, false /* no concurrent lock printed */, false /* simple format */); + } + static void print_on_error(outputStream* st, Thread* current, char* buf, int buflen); + +diff --git a/hotspot/src/share/vm/runtime/threadStatisticalInfo.hpp b/hotspot/src/share/vm/runtime/threadStatisticalInfo.hpp +new file mode 100644 +index 0000000..9dbe62d +--- /dev/null ++++ b/hotspot/src/share/vm/runtime/threadStatisticalInfo.hpp +@@ -0,0 +1,49 @@ ++/* ++ * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2018 SAP SE. All rights reserved. ++ * Copyright (c) 2022, Huawei Technologies Co., Ltd. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ * ++ */ ++ ++#ifndef SHARE_VM_RUNTIME_THREADSTATISTICS_HPP ++#define SHARE_VM_RUNTIME_THREADSTATISTICS_HPP ++ ++#include "prims/jni.h" ++#include "runtime/os.hpp" ++#include "utilities/globalDefinitions.hpp" ++ ++ ++class ThreadStatisticalInfo { ++ // The time stamp the thread was started. ++ const uint64_t _start_time_stamp; ++ uint64_t _define_class_count; ++ ++public: ++ ThreadStatisticalInfo() : _start_time_stamp(os::javaTimeMillis()), _define_class_count(0) {} ++ uint64_t getStartTime() const { return _start_time_stamp; } ++ uint64_t getDefineClassCount() const { return _define_class_count; } ++ void setDefineClassCount(uint64_t defineClassCount) { _define_class_count = defineClassCount; } ++ void incr_define_class_count() { _define_class_count += 1; } ++ uint64_t getElapsedTime() const { return os::javaTimeMillis() - getStartTime(); } ++}; ++ ++#endif // SHARE_VM_RUNTIME_THREADSTATISTICS_HPP +diff --git a/hotspot/src/share/vm/runtime/vm_operations.cpp b/hotspot/src/share/vm/runtime/vm_operations.cpp +index b42d18f..03c4249 100644 +--- a/hotspot/src/share/vm/runtime/vm_operations.cpp ++++ b/hotspot/src/share/vm/runtime/vm_operations.cpp +@@ -195,7 +195,7 @@ bool VM_PrintThreads::doit_prologue() { + } + + void VM_PrintThreads::doit() { +- Threads::print_on(_out, true, false, _print_concurrent_locks); ++ Threads::print_on(_out, true, false, _print_concurrent_locks, _print_extended_info); + } + + void VM_PrintThreads::doit_epilogue() { +diff --git a/hotspot/src/share/vm/runtime/vm_operations.hpp b/hotspot/src/share/vm/runtime/vm_operations.hpp +index 19c33f8..baf6042 100644 +--- a/hotspot/src/share/vm/runtime/vm_operations.hpp ++++ b/hotspot/src/share/vm/runtime/vm_operations.hpp +@@ -311,10 +311,17 @@ class VM_PrintThreads: public VM_Operation { + private: + outputStream* _out; + bool _print_concurrent_locks; ++ bool _print_extended_info; + public: +- VM_PrintThreads() { _out = tty; _print_concurrent_locks = PrintConcurrentLocks; } +- VM_PrintThreads(outputStream* out, bool print_concurrent_locks) { _out = out; _print_concurrent_locks = print_concurrent_locks; } +- VMOp_Type type() const { return VMOp_PrintThreads; } ++ VM_PrintThreads() ++ : _out(tty), _print_concurrent_locks(PrintConcurrentLocks), _print_extended_info(false) ++ {} ++ VM_PrintThreads(outputStream* out, bool print_concurrent_locks, bool print_extended_info) ++ : _out(out), _print_concurrent_locks(print_concurrent_locks), _print_extended_info(print_extended_info) ++ {} ++ VMOp_Type type() const { ++ return VMOp_PrintThreads; ++ } + void doit(); + bool doit_prologue(); + void doit_epilogue(); +diff --git a/hotspot/src/share/vm/services/attachListener.cpp b/hotspot/src/share/vm/services/attachListener.cpp +index d4dea71..7c57637 100644 +--- a/hotspot/src/share/vm/services/attachListener.cpp ++++ b/hotspot/src/share/vm/services/attachListener.cpp +@@ -132,12 +132,20 @@ static jint data_dump(AttachOperation* op, outputStream* out) { + // + static jint thread_dump(AttachOperation* op, outputStream* out) { + bool print_concurrent_locks = false; +- if (op->arg(0) != NULL && strcmp(op->arg(0), "-l") == 0) { +- print_concurrent_locks = true; ++ bool print_extended_info = false; ++ if (op->arg(0) != NULL) { ++ for (int i = 0; op->arg(0)[i] != 0; ++i) { ++ if (op->arg(0)[i] == 'l') { ++ print_concurrent_locks = true; ++ } ++ if (op->arg(0)[i] == 'e') { ++ print_extended_info = true; ++ } ++ } + } + + // thread stacks +- VM_PrintThreads op1(out, print_concurrent_locks); ++ VM_PrintThreads op1(out, print_concurrent_locks, print_extended_info); + VMThread::execute(&op1); + + // JNI global handles +diff --git a/hotspot/src/share/vm/services/diagnosticCommand.cpp b/hotspot/src/share/vm/services/diagnosticCommand.cpp +index c9bc7d2..fb8e293 100644 +--- a/hotspot/src/share/vm/services/diagnosticCommand.cpp ++++ b/hotspot/src/share/vm/services/diagnosticCommand.cpp +@@ -535,13 +535,15 @@ int ClassStatsDCmd::num_arguments() { + + ThreadDumpDCmd::ThreadDumpDCmd(outputStream* output, bool heap) : + DCmdWithParser(output, heap), +- _locks("-l", "print java.util.concurrent locks", "BOOLEAN", false, "false") { ++ _locks("-l", "print java.util.concurrent locks", "BOOLEAN", false, "false"), ++ _extended("-e", "print extended thread information", "BOOLEAN", false, "false") { + _dcmdparser.add_dcmd_option(&_locks); ++ _dcmdparser.add_dcmd_option(&_extended); + } + + void ThreadDumpDCmd::execute(DCmdSource source, TRAPS) { + // thread stacks +- VM_PrintThreads op1(output(), _locks.value()); ++ VM_PrintThreads op1(output(), _locks.value(), _extended.value()); + VMThread::execute(&op1); + + // JNI global handles +diff --git a/hotspot/src/share/vm/services/diagnosticCommand.hpp b/hotspot/src/share/vm/services/diagnosticCommand.hpp +index 275e053..87bff52 100644 +--- a/hotspot/src/share/vm/services/diagnosticCommand.hpp ++++ b/hotspot/src/share/vm/services/diagnosticCommand.hpp +@@ -362,6 +362,7 @@ public: + class ThreadDumpDCmd : public DCmdWithParser { + protected: + DCmdArgument _locks; ++ DCmdArgument _extended; + public: + ThreadDumpDCmd(outputStream* output, bool heap); + static const char* name() { return "Thread.print"; } +diff --git a/jdk/src/share/classes/sun/tools/jstack/JStack.java b/jdk/src/share/classes/sun/tools/jstack/JStack.java +index 6c96af2..3eea094 100644 +--- a/jdk/src/share/classes/sun/tools/jstack/JStack.java ++++ b/jdk/src/share/classes/sun/tools/jstack/JStack.java +@@ -48,6 +48,7 @@ public class JStack { + boolean useSA = false; + boolean mixed = false; + boolean locks = false; ++ boolean extended = false; + + // Parse the options (arguments starting with "-" ) + int optionCount = 0; +@@ -69,7 +70,11 @@ public class JStack { + if (arg.equals("-l")) { + locks = true; + } else { +- usage(1); ++ if (arg.equals("-e")) { ++ extended = true; ++ } else { ++ usage(1); ++ } + } + } + } +@@ -107,11 +112,12 @@ public class JStack { + } else { + // pass -l to thread dump operation to get extra lock info + String pid = args[optionCount]; +- String params[]; ++ String params[]= new String[] { "" }; ++ if (extended) { ++ params[0] += "-e "; ++ } + if (locks) { +- params = new String[] { "-l" }; +- } else { +- params = new String[0]; ++ params[0] += "-l"; + } + runThreadDump(pid, params); + } +@@ -205,7 +211,7 @@ public class JStack { + // print usage message + private static void usage(int exit) { + System.err.println("Usage:"); +- System.err.println(" jstack [-l] "); ++ System.err.println(" jstack [-l][-e] "); + System.err.println(" (to connect to running process)"); + + if (loadSAClass() != null) { +@@ -227,6 +233,7 @@ public class JStack { + } + + System.err.println(" -l long listing. Prints additional information about locks"); ++ System.err.println(" -e extended listing. Prints additional information about threads"); + System.err.println(" -h or -help to print this help message"); + System.exit(exit); + } +-- +1.8.3.1 diff --git a/8202142-jfr-event-io-TestInstrumentation-is-unstable.patch b/8202142-jfr-event-io-TestInstrumentation-is-unstable.patch deleted file mode 100755 index 6c10c0a559d0a8d58e98c6330089d1f9e5b8a4f8..0000000000000000000000000000000000000000 --- a/8202142-jfr-event-io-TestInstrumentation-is-unstable.patch +++ /dev/null @@ -1,1185 +0,0 @@ -From baadf220d261a6c610920d749a2b9c19f864ba96 Mon Sep 17 00:00:00 2001 -From: wuyan -Date: Sat, 11 Sep 2021 10:07:53 +0800 -Subject: [PATCH 20/23] 8202142: jfr/event/io/TestInstrumentation is unstable - -Summary: : JDK-8202142: jfr/event/io/TestInstrumentation is unstable -LLT: jdk/test/jdk/jfr/event/io/TestInstrumentation.java -Patch Type: backport -Bug url: https://bugs.openjdk.java.net/browse/JDK-8202142 ---- - jdk/test/jdk/jfr/event/io/IOEvent.java | 9 +- - jdk/test/jdk/jfr/event/io/IOHelper.java | 8 +- - .../jdk/jfr/event/io/TestDisabledEvents.java | 33 +-- - .../jfr/event/io/TestFileChannelEvents.java | 138 +++++------ - .../jdk/jfr/event/io/TestFileReadOnly.java | 77 +++--- - .../jfr/event/io/TestFileStreamEvents.java | 69 +++--- - .../jdk/jfr/event/io/TestInstrumentation.java | 4 +- - .../event/io/TestRandomAccessFileEvents.java | 115 ++++----- - .../event/io/TestRandomAccessFileThread.java | 222 +++++++++--------- - .../jfr/event/io/TestSocketChannelEvents.java | 122 +++++----- - .../jdk/jfr/event/io/TestSocketEvents.java | 104 ++++---- - 11 files changed, 455 insertions(+), 446 deletions(-) - -diff --git a/jdk/test/jdk/jfr/event/io/IOEvent.java b/jdk/test/jdk/jfr/event/io/IOEvent.java -index e3939fbf8..dcf70ccc3 100644 ---- a/jdk/test/jdk/jfr/event/io/IOEvent.java -+++ b/jdk/test/jdk/jfr/event/io/IOEvent.java -@@ -1,5 +1,5 @@ - /* -- * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. -+ * Copyright (c) 2018, 2020, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it -@@ -197,14 +197,11 @@ public class IOEvent { - } - return canonical_path; - } else { -- return String.format("%s/%s:%d", -- event.getValue("host"), -- event.getValue("address"), -- event.getValue("port")); -+ return event.getValue("address") + ":" + event.getValue("port"); - } - } - - private static String getAddress(Socket s) { -- return s.getInetAddress().toString() + ":" + s.getPort(); -+ return s.getInetAddress().getHostAddress() + ":" + s.getPort(); - } - } -diff --git a/jdk/test/jdk/jfr/event/io/IOHelper.java b/jdk/test/jdk/jfr/event/io/IOHelper.java -index f1f205529..23e61f59a 100644 ---- a/jdk/test/jdk/jfr/event/io/IOHelper.java -+++ b/jdk/test/jdk/jfr/event/io/IOHelper.java -@@ -1,5 +1,5 @@ - /* -- * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. -+ * Copyright (c) 2018, 2020, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it -@@ -28,6 +28,8 @@ package jdk.jfr.event.io; - import static jdk.test.lib.Asserts.assertEquals; - import static jdk.test.lib.Asserts.assertTrue; - -+import java.util.Collections; -+import java.util.Comparator; - import java.util.List; - import java.util.stream.Collectors; - -@@ -41,6 +43,7 @@ import jdk.test.lib.jfr.Events; - public class IOHelper { - - public static void verifyEqualsInOrder(List events, List expectedEvents) throws Throwable { -+ Collections.sort(events, Comparator.comparing(RecordedEvent::getStartTime)); - List actualEvents = getTestEvents(events, expectedEvents); - try { - assertEquals(actualEvents.size(), expectedEvents.size(), "Wrong number of events."); -@@ -48,6 +51,9 @@ public class IOHelper { - assertEquals(actualEvents.get(i), expectedEvents.get(i), "Wrong event at pos " + i); - } - } catch (Throwable t) { -+ for (RecordedEvent e: events) { -+ System.out.println(e); -+ } - logEvents(actualEvents, expectedEvents); - throw t; - } -diff --git a/jdk/test/jdk/jfr/event/io/TestDisabledEvents.java b/jdk/test/jdk/jfr/event/io/TestDisabledEvents.java -index aad1b217f..d80304cf0 100644 ---- a/jdk/test/jdk/jfr/event/io/TestDisabledEvents.java -+++ b/jdk/test/jdk/jfr/event/io/TestDisabledEvents.java -@@ -1,5 +1,5 @@ - /* -- * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. -+ * Copyright (c) 2018, 2020, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it -@@ -57,21 +57,22 @@ public class TestDisabledEvents { - public static void main(String[] args) throws Throwable { - File tmp = File.createTempFile("TestDisabledEvents", ".tmp", new File(".")); - tmp.deleteOnExit(); -- Recording recording = new Recording(); -- recording.disable(IOEvent.EVENT_FILE_READ); -- recording.disable(IOEvent.EVENT_FILE_WRITE); -- recording.start(); -- -- useRandomAccessFile(tmp); -- useFileStreams(tmp); -- useFileChannel(tmp); -- -- recording.stop(); -- for (RecordedEvent event : Events.fromRecording(recording)) { -- final String eventName = event.getEventType().getName(); -- System.out.println("Got eventName:" + eventName); -- assertNotEquals(eventName, IOEvent.EVENT_FILE_READ, "Got disabled read event"); -- assertNotEquals(eventName, IOEvent.EVENT_FILE_WRITE, "Got disabled write event"); -+ try (Recording recording = new Recording()) { -+ recording.disable(IOEvent.EVENT_FILE_READ); -+ recording.disable(IOEvent.EVENT_FILE_WRITE); -+ recording.start(); -+ -+ useRandomAccessFile(tmp); -+ useFileStreams(tmp); -+ useFileChannel(tmp); -+ -+ recording.stop(); -+ for (RecordedEvent event : Events.fromRecording(recording)) { -+ final String eventName = event.getEventType().getName(); -+ System.out.println("Got eventName:" + eventName); -+ assertNotEquals(eventName, IOEvent.EVENT_FILE_READ, "Got disabled read event"); -+ assertNotEquals(eventName, IOEvent.EVENT_FILE_WRITE, "Got disabled write event"); -+ } - } - } - -diff --git a/jdk/test/jdk/jfr/event/io/TestFileChannelEvents.java b/jdk/test/jdk/jfr/event/io/TestFileChannelEvents.java -index cb90bc54f..632fcaba3 100644 ---- a/jdk/test/jdk/jfr/event/io/TestFileChannelEvents.java -+++ b/jdk/test/jdk/jfr/event/io/TestFileChannelEvents.java -@@ -1,5 +1,5 @@ - /* -- * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. -+ * Copyright (c) 2018, 2020, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it -@@ -50,74 +50,74 @@ public class TestFileChannelEvents { - public static void main(String[] args) throws Throwable { - File tmp = File.createTempFile("TestFileChannelEvents", ".tmp", new File(".")); - tmp.deleteOnExit(); -- Recording recording = new Recording(); -- List expectedEvents = new ArrayList<>(); -- -- try (RandomAccessFile rf = new RandomAccessFile(tmp, "rw"); FileChannel ch = rf.getChannel();) { -- recording.enable(IOEvent.EVENT_FILE_FORCE).withThreshold(Duration.ofMillis(0)); -- recording.enable(IOEvent.EVENT_FILE_READ).withThreshold(Duration.ofMillis(0)); -- recording.enable(IOEvent.EVENT_FILE_WRITE).withThreshold(Duration.ofMillis(0)); -- recording.start(); -- -- ByteBuffer bufA = ByteBuffer.allocateDirect(10); -- ByteBuffer bufB = ByteBuffer.allocateDirect(20); -- bufA.put("1234567890".getBytes()); -- bufB.put("1234567890".getBytes()); -- -- // test write(ByteBuffer) -- bufA.flip(); -- long size = ch.write(bufA); -- expectedEvents.add(IOEvent.createFileWriteEvent(size, tmp)); -- -- // test write(ByteBuffer, long) -- bufA.flip(); -- size = ch.write(bufA, bufA.capacity() / 2); -- expectedEvents.add(IOEvent.createFileWriteEvent(size, tmp)); -- -- // test write(ByteBuffer[]) -- bufA.flip(); -- bufA.limit(5); -- bufB.flip(); -- bufB.limit(5); -- size = ch.write(new ByteBuffer[] { bufA, bufB }); -- expectedEvents.add(IOEvent.createFileWriteEvent(size, tmp)); -- -- // test force(boolean) -- ch.force(true); -- expectedEvents.add(IOEvent.createFileForceEvent(tmp)); -- -- // reset file -- ch.position(0); -- -- // test read(ByteBuffer) -- bufA.clear(); -- size = ch.read(bufA); -- expectedEvents.add(IOEvent.createFileReadEvent(size, tmp)); -- -- // test read(ByteBuffer, long) -- bufA.clear(); -- size = ch.read(bufA, bufA.capacity() / 2); -- expectedEvents.add(IOEvent.createFileReadEvent(size, tmp)); -- -- // test read(ByteBuffer[]) -- bufA.clear(); -- bufA.limit(5); -- bufB.clear(); -- bufB.limit(5); -- size = ch.read(new ByteBuffer[] { bufA, bufB }); -- expectedEvents.add(IOEvent.createFileReadEvent(size, tmp)); -- -- // Read at EOF. Should get size -1 in event. -- ch.position(ch.size()); -- bufA.clear(); -- size = ch.read(bufA); -- assertEquals(size, -1L, "Expected size -1 when read at EOF"); -- expectedEvents.add(IOEvent.createFileReadEvent(size, tmp)); -- -- ch.close(); -- recording.stop(); -- List events = Events.fromRecording(recording); -- IOHelper.verifyEqualsInOrder(events, expectedEvents); -+ try (Recording recording = new Recording()) { -+ List expectedEvents = new ArrayList<>(); -+ try (RandomAccessFile rf = new RandomAccessFile(tmp, "rw"); FileChannel ch = rf.getChannel();) { -+ recording.enable(IOEvent.EVENT_FILE_FORCE).withThreshold(Duration.ofMillis(0)); -+ recording.enable(IOEvent.EVENT_FILE_READ).withThreshold(Duration.ofMillis(0)); -+ recording.enable(IOEvent.EVENT_FILE_WRITE).withThreshold(Duration.ofMillis(0)); -+ recording.start(); -+ -+ ByteBuffer bufA = ByteBuffer.allocateDirect(10); -+ ByteBuffer bufB = ByteBuffer.allocateDirect(20); -+ bufA.put("1234567890".getBytes()); -+ bufB.put("1234567890".getBytes()); -+ -+ // test write(ByteBuffer) -+ bufA.flip(); -+ long size = ch.write(bufA); -+ expectedEvents.add(IOEvent.createFileWriteEvent(size, tmp)); -+ -+ // test write(ByteBuffer, long) -+ bufA.flip(); -+ size = ch.write(bufA, bufA.capacity() / 2); -+ expectedEvents.add(IOEvent.createFileWriteEvent(size, tmp)); -+ -+ // test write(ByteBuffer[]) -+ bufA.flip(); -+ bufA.limit(5); -+ bufB.flip(); -+ bufB.limit(5); -+ size = ch.write(new ByteBuffer[] { bufA, bufB }); -+ expectedEvents.add(IOEvent.createFileWriteEvent(size, tmp)); -+ -+ // test force(boolean) -+ ch.force(true); -+ expectedEvents.add(IOEvent.createFileForceEvent(tmp)); -+ -+ // reset file -+ ch.position(0); -+ -+ // test read(ByteBuffer) -+ bufA.clear(); -+ size = ch.read(bufA); -+ expectedEvents.add(IOEvent.createFileReadEvent(size, tmp)); -+ -+ // test read(ByteBuffer, long) -+ bufA.clear(); -+ size = ch.read(bufA, bufA.capacity() / 2); -+ expectedEvents.add(IOEvent.createFileReadEvent(size, tmp)); -+ -+ // test read(ByteBuffer[]) -+ bufA.clear(); -+ bufA.limit(5); -+ bufB.clear(); -+ bufB.limit(5); -+ size = ch.read(new ByteBuffer[] { bufA, bufB }); -+ expectedEvents.add(IOEvent.createFileReadEvent(size, tmp)); -+ -+ // Read at EOF. Should get size -1 in event. -+ ch.position(ch.size()); -+ bufA.clear(); -+ size = ch.read(bufA); -+ assertEquals(size, -1L, "Expected size -1 when read at EOF"); -+ expectedEvents.add(IOEvent.createFileReadEvent(size, tmp)); -+ -+ ch.close(); -+ recording.stop(); -+ List events = Events.fromRecording(recording); -+ IOHelper.verifyEqualsInOrder(events, expectedEvents); -+ } - } - } - } -diff --git a/jdk/test/jdk/jfr/event/io/TestFileReadOnly.java b/jdk/test/jdk/jfr/event/io/TestFileReadOnly.java -index 065ebadc3..b7e20d0ef 100644 ---- a/jdk/test/jdk/jfr/event/io/TestFileReadOnly.java -+++ b/jdk/test/jdk/jfr/event/io/TestFileReadOnly.java -@@ -1,5 +1,5 @@ - /* -- * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. -+ * Copyright (c) 2018, 2020, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it -@@ -52,50 +52,51 @@ public class TestFileReadOnly { - public static void main(String[] args) throws Throwable { - File tmp = File.createTempFile("TestFileReadOnly", ".tmp", new File(".")); - tmp.deleteOnExit(); -- Recording recording = new Recording(); -- List expectedEvents = new ArrayList<>(); -+ try(Recording recording = new Recording()) { -+ List expectedEvents = new ArrayList<>(); - -- recording.enable(IOEvent.EVENT_FILE_READ).withThreshold(Duration.ofMillis(0)); -- recording.enable(IOEvent.EVENT_FILE_WRITE).withThreshold(Duration.ofMillis(0)); -- recording.start(); -+ recording.enable(IOEvent.EVENT_FILE_READ).withThreshold(Duration.ofMillis(0)); -+ recording.enable(IOEvent.EVENT_FILE_WRITE).withThreshold(Duration.ofMillis(0)); -+ recording.start(); - -- final byte[] buf = { 1, 2, 3 }; -+ final byte[] buf = { 1, 2, 3 }; - -- // Create the file. -- try (RandomAccessFile f = new RandomAccessFile(tmp, "rw")) { -- f.write(buf); -- expectedEvents.add(IOEvent.createFileWriteEvent(buf.length, tmp)); -- } -- -- // Reopen the file as ReadOnly and try to write to it. -- // Should generate an event with bytesWritten = -1. -- try (RandomAccessFile f = new RandomAccessFile(tmp, "r")) { -- try { -+ // Create the file. -+ try (RandomAccessFile f = new RandomAccessFile(tmp, "rw")) { - f.write(buf); -- fail("No exception for ReadOnly File"); -- } catch (IOException e) { -- // Expected exception -- expectedEvents.add(IOEvent.createFileWriteEvent(-1, tmp)); -+ expectedEvents.add(IOEvent.createFileWriteEvent(buf.length, tmp)); - } -- } - -- // Try to write to read-only FileChannel. -- try (RandomAccessFile f = new RandomAccessFile(tmp, "r"); FileChannel ch = f.getChannel()) { -- ByteBuffer writeBuf = ByteBuffer.allocateDirect(buf.length); -- writeBuf.put(buf); -- writeBuf.flip(); -- ch.position(0); -- try { -- ch.write(writeBuf); -- fail("No exception for ReadOnly FileChannel"); -- } catch (java.nio.channels.NonWritableChannelException e) { -- // Expected exception -- expectedEvents.add(IOEvent.createFileWriteEvent(-1, tmp)); -+ // Reopen the file as ReadOnly and try to write to it. -+ // Should generate an event with bytesWritten = -1. -+ try (RandomAccessFile f = new RandomAccessFile(tmp, "r")) { -+ try { -+ f.write(buf); -+ fail("No exception for ReadOnly File"); -+ } catch (IOException e) { -+ // Expected exception -+ expectedEvents.add(IOEvent.createFileWriteEvent(-1, tmp)); -+ } - } -- } - -- recording.stop(); -- List events = Events.fromRecording(recording); -- IOHelper.verifyEqualsInOrder(events, expectedEvents); -+ // Try to write to read-only FileChannel. -+ try (RandomAccessFile f = new RandomAccessFile(tmp, "r"); FileChannel ch = f.getChannel()) { -+ ByteBuffer writeBuf = ByteBuffer.allocateDirect(buf.length); -+ writeBuf.put(buf); -+ writeBuf.flip(); -+ ch.position(0); -+ try { -+ ch.write(writeBuf); -+ fail("No exception for ReadOnly FileChannel"); -+ } catch (java.nio.channels.NonWritableChannelException e) { -+ // Expected exception -+ expectedEvents.add(IOEvent.createFileWriteEvent(-1, tmp)); -+ } -+ } -+ -+ recording.stop(); -+ List events = Events.fromRecording(recording); -+ IOHelper.verifyEqualsInOrder(events, expectedEvents); -+ } - } - } -diff --git a/jdk/test/jdk/jfr/event/io/TestFileStreamEvents.java b/jdk/test/jdk/jfr/event/io/TestFileStreamEvents.java -index 46c7b80f3..0bddf5a6c 100644 ---- a/jdk/test/jdk/jfr/event/io/TestFileStreamEvents.java -+++ b/jdk/test/jdk/jfr/event/io/TestFileStreamEvents.java -@@ -1,5 +1,5 @@ - /* -- * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. -+ * Copyright (c) 2018, 2020, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it -@@ -50,47 +50,48 @@ public class TestFileStreamEvents { - public static void main(String[] args) throws Throwable { - File tmp = File.createTempFile("TestFileStreamEvents", ".tmp", new File(".")); - tmp.deleteOnExit(); -- Recording recording = new Recording(); -- List expectedEvents = new ArrayList<>(); -+ try (Recording recording = new Recording()) { -+ List expectedEvents = new ArrayList<>(); - -- try(FileOutputStream fos = new FileOutputStream(tmp); FileInputStream fis = new FileInputStream(tmp);) { -- recording.enable(IOEvent.EVENT_FILE_READ).withThreshold(Duration.ofMillis(0)); -- recording.enable(IOEvent.EVENT_FILE_WRITE).withThreshold(Duration.ofMillis(0)); -- recording.start(); -+ try(FileOutputStream fos = new FileOutputStream(tmp); FileInputStream fis = new FileInputStream(tmp);) { -+ recording.enable(IOEvent.EVENT_FILE_READ).withThreshold(Duration.ofMillis(0)); -+ recording.enable(IOEvent.EVENT_FILE_WRITE).withThreshold(Duration.ofMillis(0)); -+ recording.start(); - -- int writeByte = 47; -- byte[] writeBuf = {11, 12, 13, 14}; -+ int writeByte = 47; -+ byte[] writeBuf = {11, 12, 13, 14}; - -- // Write -- fos.write(writeByte); -- expectedEvents.add(IOEvent.createFileWriteEvent(1, tmp)); -- fos.write(writeBuf); -- expectedEvents.add(IOEvent.createFileWriteEvent(writeBuf.length, tmp)); -- fos.write(writeBuf, 0, 2); -- expectedEvents.add(IOEvent.createFileWriteEvent(2, tmp)); -+ // Write -+ fos.write(writeByte); -+ expectedEvents.add(IOEvent.createFileWriteEvent(1, tmp)); -+ fos.write(writeBuf); -+ expectedEvents.add(IOEvent.createFileWriteEvent(writeBuf.length, tmp)); -+ fos.write(writeBuf, 0, 2); -+ expectedEvents.add(IOEvent.createFileWriteEvent(2, tmp)); - -- // Read -- int readByte = fis.read(); -- assertEquals(readByte, writeByte, "Wrong byte read"); -- expectedEvents.add(IOEvent.createFileReadEvent(1, tmp)); -+ // Read -+ int readByte = fis.read(); -+ assertEquals(readByte, writeByte, "Wrong byte read"); -+ expectedEvents.add(IOEvent.createFileReadEvent(1, tmp)); - -- byte[] readBuf = new byte[writeBuf.length]; -- long size = fis.read(readBuf); -- assertEquals(size, (long)writeBuf.length, "Wrong size when reading byte[]"); -- expectedEvents.add(IOEvent.createFileReadEvent(size, tmp)); -+ byte[] readBuf = new byte[writeBuf.length]; -+ long size = fis.read(readBuf); -+ assertEquals(size, (long)writeBuf.length, "Wrong size when reading byte[]"); -+ expectedEvents.add(IOEvent.createFileReadEvent(size, tmp)); - -- size = fis.read(readBuf, 0, 2); -- assertEquals(size, 2L, "Wrong size when reading 2 bytes"); -- expectedEvents.add(IOEvent.createFileReadEvent(size, tmp)); -+ size = fis.read(readBuf, 0, 2); -+ assertEquals(size, 2L, "Wrong size when reading 2 bytes"); -+ expectedEvents.add(IOEvent.createFileReadEvent(size, tmp)); - -- // We are at EOF. Read more and verify we get size -1. -- size = fis.read(readBuf); -- assertEquals(size, -1L, "Size should be -1 at EOF"); -- expectedEvents.add(IOEvent.createFileReadEvent(size, tmp)); -+ // We are at EOF. Read more and verify we get size -1. -+ size = fis.read(readBuf); -+ assertEquals(size, -1L, "Size should be -1 at EOF"); -+ expectedEvents.add(IOEvent.createFileReadEvent(size, tmp)); - -- recording.stop(); -- List events = Events.fromRecording(recording); -- IOHelper.verifyEqualsInOrder(events, expectedEvents); -+ recording.stop(); -+ List events = Events.fromRecording(recording); -+ IOHelper.verifyEqualsInOrder(events, expectedEvents); -+ } - } - } - } -diff --git a/jdk/test/jdk/jfr/event/io/TestInstrumentation.java b/jdk/test/jdk/jfr/event/io/TestInstrumentation.java -index d5430e6c6..19fe5a6da 100644 ---- a/jdk/test/jdk/jfr/event/io/TestInstrumentation.java -+++ b/jdk/test/jdk/jfr/event/io/TestInstrumentation.java -@@ -1,5 +1,5 @@ - /* -- * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. -+ * Copyright (c) 2018, 2020, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it -@@ -104,11 +104,9 @@ public class TestInstrumentation implements ClassFileTransformer { - "java/io/FileOutputStream::write::([B)V", - "java/io/FileOutputStream::write::([BII)V", - "java/net/SocketInputStream::read::()I", -- "java/net/SocketInputStream::read::([B)I", - "java/net/SocketInputStream::read::([BII)I", - "java/net/SocketInputStream::close::()V", - "java/net/SocketOutputStream::write::(I)V", -- "java/net/SocketOutputStream::write::([B)V", - "java/net/SocketOutputStream::write::([BII)V", - "java/net/SocketOutputStream::close::()V", - "java/nio/channels/FileChannel::read::([Ljava/nio/ByteBuffer;)J", -diff --git a/jdk/test/jdk/jfr/event/io/TestRandomAccessFileEvents.java b/jdk/test/jdk/jfr/event/io/TestRandomAccessFileEvents.java -index 959ed4d22..9c28231c5 100644 ---- a/jdk/test/jdk/jfr/event/io/TestRandomAccessFileEvents.java -+++ b/jdk/test/jdk/jfr/event/io/TestRandomAccessFileEvents.java -@@ -1,5 +1,5 @@ - /* -- * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. -+ * Copyright (c) 2018, 2020, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it -@@ -49,62 +49,63 @@ public class TestRandomAccessFileEvents { - public static void main(String[] args) throws Throwable { - File tmp = File.createTempFile("TestRandomAccessFileEvents", ".tmp", new File(".")); - tmp.deleteOnExit(); -- Recording recording = new Recording(); -- List expectedEvents = new ArrayList<>(); -- -- recording.enable(IOEvent.EVENT_FILE_WRITE).withThreshold(Duration.ofMillis(0)); -- recording.enable(IOEvent.EVENT_FILE_READ).withThreshold(Duration.ofMillis(0)); -- recording.start(); -- -- RandomAccessFile ras = new RandomAccessFile(tmp, "rw"); -- int writeInt = 47; -- byte[] writeBuffer = {10,11,12,13}; -- -- // Write an int and a buffer. -- ras.write(writeInt); -- expectedEvents.add(IOEvent.createFileWriteEvent(1, tmp)); -- ras.write(writeBuffer); -- expectedEvents.add(IOEvent.createFileWriteEvent(writeBuffer.length, tmp)); -- -- ras.seek(0); -- -- // Read int and buffer -- int readInt = ras.read(); -- assertEquals(readInt, writeInt, "wrong int read"); -- expectedEvents.add(IOEvent.createFileReadEvent(1, tmp)); -- byte[] readBuffer = new byte [writeBuffer.length]; -- int size = ras.read(readBuffer); -- verifyBufferEquals(readBuffer, writeBuffer); -- expectedEvents.add(IOEvent.createFileReadEvent(readBuffer.length, tmp)); -- -- // Read beyond EOF -- readInt = ras.read(); -- assertEquals(-1, readInt, "wrong read after EOF"); -- expectedEvents.add(IOEvent.createFileReadEvent(-1, tmp)); -- -- // Seek to beginning and verify we can read after EOF. -- ras.seek(0); -- readInt = ras.read(); -- assertEquals(readInt, writeInt, "wrong int read after seek(0)"); -- expectedEvents.add(IOEvent.createFileReadEvent(1, tmp)); -- -- // seek beyond EOF and verify we get EOF when reading. -- ras.seek(10); -- readInt = ras.read(); -- assertEquals(-1, readInt, "wrong read after seek beyond EOF"); -- expectedEvents.add(IOEvent.createFileReadEvent(-1, tmp)); -- -- // Read partial buffer. -- int partialSize = writeBuffer.length - 2; -- ras.seek(ras.length()-partialSize); -- size = ras.read(readBuffer); -- assertEquals(size, partialSize, "Wrong size partial buffer read"); -- expectedEvents.add(IOEvent.createFileReadEvent(size, tmp)); -- -- ras.close(); -- recording.stop(); -- List events = Events.fromRecording(recording); -- IOHelper.verifyEqualsInOrder(events, expectedEvents); -+ try (Recording recording = new Recording()) { -+ List expectedEvents = new ArrayList<>(); -+ -+ recording.enable(IOEvent.EVENT_FILE_WRITE).withThreshold(Duration.ofMillis(0)); -+ recording.enable(IOEvent.EVENT_FILE_READ).withThreshold(Duration.ofMillis(0)); -+ recording.start(); -+ -+ RandomAccessFile ras = new RandomAccessFile(tmp, "rw"); -+ int writeInt = 47; -+ byte[] writeBuffer = {10,11,12,13}; -+ -+ // Write an int and a buffer. -+ ras.write(writeInt); -+ expectedEvents.add(IOEvent.createFileWriteEvent(1, tmp)); -+ ras.write(writeBuffer); -+ expectedEvents.add(IOEvent.createFileWriteEvent(writeBuffer.length, tmp)); -+ -+ ras.seek(0); -+ -+ // Read int and buffer -+ int readInt = ras.read(); -+ assertEquals(readInt, writeInt, "wrong int read"); -+ expectedEvents.add(IOEvent.createFileReadEvent(1, tmp)); -+ byte[] readBuffer = new byte [writeBuffer.length]; -+ int size = ras.read(readBuffer); -+ verifyBufferEquals(readBuffer, writeBuffer); -+ expectedEvents.add(IOEvent.createFileReadEvent(readBuffer.length, tmp)); -+ -+ // Read beyond EOF -+ readInt = ras.read(); -+ assertEquals(-1, readInt, "wrong read after EOF"); -+ expectedEvents.add(IOEvent.createFileReadEvent(-1, tmp)); -+ -+ // Seek to beginning and verify we can read after EOF. -+ ras.seek(0); -+ readInt = ras.read(); -+ assertEquals(readInt, writeInt, "wrong int read after seek(0)"); -+ expectedEvents.add(IOEvent.createFileReadEvent(1, tmp)); -+ -+ // seek beyond EOF and verify we get EOF when reading. -+ ras.seek(10); -+ readInt = ras.read(); -+ assertEquals(-1, readInt, "wrong read after seek beyond EOF"); -+ expectedEvents.add(IOEvent.createFileReadEvent(-1, tmp)); -+ -+ // Read partial buffer. -+ int partialSize = writeBuffer.length - 2; -+ ras.seek(ras.length()-partialSize); -+ size = ras.read(readBuffer); -+ assertEquals(size, partialSize, "Wrong size partial buffer read"); -+ expectedEvents.add(IOEvent.createFileReadEvent(size, tmp)); -+ -+ ras.close(); -+ recording.stop(); -+ List events = Events.fromRecording(recording); -+ IOHelper.verifyEqualsInOrder(events, expectedEvents); -+ } - } - - private static void verifyBufferEquals(byte[] a, byte[] b) { -diff --git a/jdk/test/jdk/jfr/event/io/TestRandomAccessFileThread.java b/jdk/test/jdk/jfr/event/io/TestRandomAccessFileThread.java -index b6200fd66..539759c6f 100644 ---- a/jdk/test/jdk/jfr/event/io/TestRandomAccessFileThread.java -+++ b/jdk/test/jdk/jfr/event/io/TestRandomAccessFileThread.java -@@ -1,5 +1,5 @@ - /* -- * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. -+ * Copyright (c) 2018, 2020, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it -@@ -64,43 +64,42 @@ public class TestRandomAccessFileThread { - public static void main(String[] args) throws Throwable { - File tmp = File.createTempFile("TestRandomAccessFileThread", ".tmp", new File(".")); - tmp.deleteOnExit(); -- -- Recording recording = new Recording(); -- recording.enable(IOEvent.EVENT_FILE_READ).withThreshold(Duration.ofMillis(0)); -- recording.enable(IOEvent.EVENT_FILE_WRITE).withThreshold(Duration.ofMillis(0)); -- recording.start(); -- -- TestThread writerThread = new TestThread(new XRun() { -- @Override -- public void xrun() throws IOException { -- final byte[] buf = new byte[OP_COUNT]; -- for (int i = 0; i < buf.length; ++i) { -- buf[i] = (byte)((i + 'a') % 255); -- } -- try (RandomAccessFile raf = new RandomAccessFile(tmp, "rwd")) { -- for(int i = 0; i < OP_COUNT; ++i) { -- raf.write(buf, 0, i + 1); -- writeCount++; -+ try (Recording recording = new Recording()) { -+ recording.enable(IOEvent.EVENT_FILE_READ).withThreshold(Duration.ofMillis(0)); -+ recording.enable(IOEvent.EVENT_FILE_WRITE).withThreshold(Duration.ofMillis(0)); -+ recording.start(); -+ -+ TestThread writerThread = new TestThread(new XRun() { -+ @Override -+ public void xrun() throws IOException { -+ final byte[] buf = new byte[OP_COUNT]; -+ for (int i = 0; i < buf.length; ++i) { -+ buf[i] = (byte)((i + 'a') % 255); - } -- } -- }}, "TestWriterThread"); -+ try (RandomAccessFile raf = new RandomAccessFile(tmp, "rwd")) { -+ for(int i = 0; i < OP_COUNT; ++i) { -+ raf.write(buf, 0, i + 1); -+ writeCount++; -+ } -+ } -+ }}, "TestWriterThread"); - - TestThread readerThread = new TestThread(new XRun() { -- @Override -- public void xrun() throws IOException { -- try (RandomAccessFile raf = new RandomAccessFile(tmp, "r")) { -- byte[] buf = new byte[OP_COUNT]; -- for(int i = 0; i < OP_COUNT; ++i) { -- while (writeCount <= i) { -- // No more data to read. Wait for writer thread. -- Thread.yield(); -+ @Override -+ public void xrun() throws IOException { -+ try (RandomAccessFile raf = new RandomAccessFile(tmp, "r")) { -+ byte[] buf = new byte[OP_COUNT]; -+ for(int i = 0; i < OP_COUNT; ++i) { -+ while (writeCount <= i) { -+ // No more data to read. Wait for writer thread. -+ Thread.yield(); -+ } -+ int expectedSize = i + 1; -+ int actualSize = raf.read(buf, 0, expectedSize); -+ Asserts.assertEquals(actualSize, expectedSize, "Wrong read size. Probably test error."); - } -- int expectedSize = i + 1; -- int actualSize = raf.read(buf, 0, expectedSize); -- Asserts.assertEquals(actualSize, expectedSize, "Wrong read size. Probably test error."); - } -- } -- }}, "TestReaderThread"); -+ }}, "TestReaderThread"); - - readerThread.start(); - writerThread.start(); -@@ -118,7 +117,7 @@ public class TestRandomAccessFileThread { - continue; - } - logEventSummary(event); -- if (Events.isEventType(event,IOEvent.EVENT_FILE_READ)) { -+ if (Events.isEventType(event, IOEvent.EVENT_FILE_READ)) { - readEvents.add(event); - } else { - writeEvents.add(event); -@@ -136,91 +135,92 @@ public class TestRandomAccessFileThread { - Asserts.assertEquals(readEvents.size(), OP_COUNT, "Wrong number of read events"); - Asserts.assertEquals(writeEvents.size(), OP_COUNT, "Wrong number of write events"); - } -- -- private static void logEventSummary(RecordedEvent event) { -- boolean isRead = Events.isEventType(event, IOEvent.EVENT_FILE_READ); -- String name = isRead ? "read " : "write"; -- String bytesField = isRead ? "bytesRead" : "bytesWritten"; -- long bytes = Events.assertField(event, bytesField).getValue(); -- long commit = Events.assertField(event, "startTime").getValue(); -- Instant start = event.getStartTime(); -- Instant end = event.getEndTime(); -- System.out.printf("%s: bytes=%d, commit=%d, start=%s, end=%s%n", name, bytes, commit, start, end); -- } -- -- private static void verifyThread(List events, Thread thread) { -- events.stream().forEach(e -> Events.assertEventThread(e, thread)); -- } -- -- private static void verifyBytes(List events, String fieldName) { -- long expectedBytes = 0; -- for (RecordedEvent event : events) { -- Events.assertField(event, fieldName).equal(++expectedBytes); -- } -+ } -+ -+ private static void logEventSummary(RecordedEvent event) { -+ boolean isRead = Events.isEventType(event, IOEvent.EVENT_FILE_READ); -+ String name = isRead ? "read " : "write"; -+ String bytesField = isRead ? "bytesRead" : "bytesWritten"; -+ long bytes = Events.assertField(event, bytesField).getValue(); -+ long commit = Events.assertField(event, "startTime").getValue(); -+ Instant start = event.getStartTime(); -+ Instant end = event.getEndTime(); -+ System.out.printf("%s: bytes=%d, commit=%d, start=%s, end=%s%n", name, bytes, commit, start, end); -+ } -+ -+ private static void verifyThread(List events, Thread thread) { -+ events.stream().forEach(e -> Events.assertEventThread(e, thread)); -+ } -+ -+ private static void verifyBytes(List events, String fieldName) { -+ long expectedBytes = 0; -+ for (RecordedEvent event : events) { -+ Events.assertField(event, fieldName).equal(++expectedBytes); - } -- -- // Verify that all times are increasing -- private static void verifyTimes(List events) { -- RecordedEvent prev = null; -- for (RecordedEvent curr : events) { -- if (prev != null) { -- try { -- Asserts.assertGreaterThanOrEqual(curr.getStartTime(), prev.getStartTime(), "Wrong startTime"); -- Asserts.assertGreaterThanOrEqual(curr.getEndTime(), prev.getEndTime(), "Wrong endTime"); -- long commitPrev = Events.assertField(prev, "startTime").getValue(); -- long commitCurr = Events.assertField(curr, "startTime").getValue(); -- Asserts.assertGreaterThanOrEqual(commitCurr, commitPrev, "Wrong commitTime"); -- } catch (Exception e) { -- System.out.println("Error: " + e.getMessage()); -- System.out.println("Prev Event: " + prev); -- System.out.println("Curr Event: " + curr); -- throw e; -- } -+ } -+ -+ // Verify that all times are increasing -+ private static void verifyTimes(List events) { -+ RecordedEvent prev = null; -+ for (RecordedEvent curr : events) { -+ if (prev != null) { -+ try { -+ Asserts.assertGreaterThanOrEqual(curr.getStartTime(), prev.getStartTime(), "Wrong startTime"); -+ Asserts.assertGreaterThanOrEqual(curr.getEndTime(), prev.getEndTime(), "Wrong endTime"); -+ long commitPrev = Events.assertField(prev, "startTime").getValue(); -+ long commitCurr = Events.assertField(curr, "startTime").getValue(); -+ Asserts.assertGreaterThanOrEqual(commitCurr, commitPrev, "Wrong commitTime"); -+ } catch (Exception e) { -+ System.out.println("Error: " + e.getMessage()); -+ System.out.println("Prev Event: " + prev); -+ System.out.println("Curr Event: " + curr); -+ throw e; - } -- prev = curr; - } -+ prev = curr; - } -- -- // Verify that all times are increasing -- private static void verifyReadWriteTimes(List readEvents, List writeEvents) { -- List events = new ArrayList<>(); -- events.addAll(readEvents); -- events.addAll(writeEvents); -- events.sort(new EventComparator()); -- -- int countRead = 0; -- int countWrite = 0; -- for (RecordedEvent event : events) { -- if (Events.isEventType(event, IOEvent.EVENT_FILE_READ)) { -- ++countRead; -- } else { -- ++countWrite; -- } -- // We can not read from the file before it has been written. -- // This check verifies that times of different threads are correct. -- // Since the read and write are from different threads, it is possible that the read -- // is committed before the same write. -- // But read operation may only be 1 step ahead of the write operation. -- Asserts.assertLessThanOrEqual(countRead, countWrite + 1, "read must be after write"); -+ } -+ -+ // Verify that all times are increasing -+ private static void verifyReadWriteTimes(List readEvents, List writeEvents) { -+ List events = new ArrayList<>(); -+ events.addAll(readEvents); -+ events.addAll(writeEvents); -+ events.sort(new EventComparator()); -+ -+ int countRead = 0; -+ int countWrite = 0; -+ for (RecordedEvent event : events) { -+ if (Events.isEventType(event, IOEvent.EVENT_FILE_READ)) { -+ ++countRead; -+ } else { -+ ++countWrite; - } -+ // We can not read from the file before it has been written. -+ // This check verifies that times of different threads are correct. -+ // Since the read and write are from different threads, it is possible that the read -+ // is committed before the same write. -+ // But read operation may only be 1 step ahead of the write operation. -+ Asserts.assertLessThanOrEqual(countRead, countWrite + 1, "read must be after write"); - } -+ } - -- private static boolean isOurEvent(RecordedEvent event, File file) { -- if (!Events.isEventType(event, IOEvent.EVENT_FILE_READ) && -- !Events.isEventType(event, IOEvent.EVENT_FILE_WRITE)) { -- return false; -- } -- String path = Events.assertField(event, "path").getValue(); -- return file.getPath().equals(path); -+ private static boolean isOurEvent(RecordedEvent event, File file) { -+ if (!Events.isEventType(event, IOEvent.EVENT_FILE_READ) && -+ !Events.isEventType(event, IOEvent.EVENT_FILE_WRITE)) { -+ return false; - } -- -- private static class EventComparator implements Comparator { -- @Override -- public int compare(RecordedEvent a, RecordedEvent b) { -- long commitA = Events.assertField(a, "startTime").getValue(); -- long commitB = Events.assertField(b, "startTime").getValue(); -- return Long.compare(commitA, commitB); -- } -+ String path = Events.assertField(event, "path").getValue(); -+ return file.getPath().equals(path); -+ } -+ -+ private static class EventComparator implements Comparator { -+ @Override -+ public int compare(RecordedEvent a, RecordedEvent b) { -+ long commitA = Events.assertField(a, "startTime").getValue(); -+ long commitB = Events.assertField(b, "startTime").getValue(); -+ return Long.compare(commitA, commitB); - } -+ } - - } -diff --git a/jdk/test/jdk/jfr/event/io/TestSocketChannelEvents.java b/jdk/test/jdk/jfr/event/io/TestSocketChannelEvents.java -index dbd43adbf..23b692a31 100644 ---- a/jdk/test/jdk/jfr/event/io/TestSocketChannelEvents.java -+++ b/jdk/test/jdk/jfr/event/io/TestSocketChannelEvents.java -@@ -1,5 +1,5 @@ - /* -- * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. -+ * Copyright (c) 2018, 2020, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it -@@ -53,6 +53,7 @@ public class TestSocketChannelEvents { - private static final int bufSizeB = 20; - - private List expectedEvents = new ArrayList<>(); -+ - private synchronized void addExpectedEvent(IOEvent event) { - expectedEvents.add(event); - } -@@ -62,69 +63,70 @@ public class TestSocketChannelEvents { - } - - public void test() throws Throwable { -- Recording recording = new Recording(); -- -- try (ServerSocketChannel ss = ServerSocketChannel.open()) { -- recording.enable(IOEvent.EVENT_SOCKET_READ).withThreshold(Duration.ofMillis(0)); -- recording.enable(IOEvent.EVENT_SOCKET_WRITE).withThreshold(Duration.ofMillis(0)); -- recording.start(); -- -- ss.socket().setReuseAddress(true); -- ss.socket().bind(null); -- -- TestThread readerThread = new TestThread(new XRun() { -- @Override -- public void xrun() throws IOException { -- ByteBuffer bufA = ByteBuffer.allocate(bufSizeA); -- ByteBuffer bufB = ByteBuffer.allocate(bufSizeB); -- try (SocketChannel sc = ss.accept()) { -- int readSize = sc.read(bufA); -- assertEquals(readSize, bufSizeA, "Wrong readSize bufA"); -- addExpectedEvent(IOEvent.createSocketReadEvent(bufSizeA, sc.socket())); -- -- bufA.clear(); -- bufA.limit(1); -- readSize = (int)sc.read(new ByteBuffer[] { bufA, bufB }); -- assertEquals(readSize, 1 + bufSizeB, "Wrong readSize 1+bufB"); -- addExpectedEvent(IOEvent.createSocketReadEvent(readSize, sc.socket())); -- -- // We try to read, but client have closed. Should get EOF. -- bufA.clear(); -- bufA.limit(1); -- readSize = sc.read(bufA); -- assertEquals(readSize, -1, "Wrong readSize at EOF"); -- addExpectedEvent(IOEvent.createSocketReadEvent(-1, sc.socket())); -+ try (Recording recording = new Recording()) { -+ try (ServerSocketChannel ss = ServerSocketChannel.open()) { -+ recording.enable(IOEvent.EVENT_SOCKET_READ).withThreshold(Duration.ofMillis(0)); -+ recording.enable(IOEvent.EVENT_SOCKET_WRITE).withThreshold(Duration.ofMillis(0)); -+ recording.start(); -+ -+ ss.socket().setReuseAddress(true); -+ ss.socket().bind(null); -+ -+ TestThread readerThread = new TestThread(new XRun() { -+ @Override -+ public void xrun() throws IOException { -+ ByteBuffer bufA = ByteBuffer.allocate(bufSizeA); -+ ByteBuffer bufB = ByteBuffer.allocate(bufSizeB); -+ try (SocketChannel sc = ss.accept()) { -+ int readSize = sc.read(bufA); -+ assertEquals(readSize, bufSizeA, "Wrong readSize bufA"); -+ addExpectedEvent(IOEvent.createSocketReadEvent(bufSizeA, sc.socket())); -+ -+ bufA.clear(); -+ bufA.limit(1); -+ readSize = (int) sc.read(new ByteBuffer[] { bufA, bufB }); -+ assertEquals(readSize, 1 + bufSizeB, "Wrong readSize 1+bufB"); -+ addExpectedEvent(IOEvent.createSocketReadEvent(readSize, sc.socket())); -+ -+ // We try to read, but client have closed. Should -+ // get EOF. -+ bufA.clear(); -+ bufA.limit(1); -+ readSize = sc.read(bufA); -+ assertEquals(readSize, -1, "Wrong readSize at EOF"); -+ addExpectedEvent(IOEvent.createSocketReadEvent(-1, sc.socket())); -+ } - } -- } -- }); -- readerThread.start(); -- -- try (SocketChannel sc = SocketChannel.open(ss.socket().getLocalSocketAddress())) { -- ByteBuffer bufA = ByteBuffer.allocateDirect(bufSizeA); -- ByteBuffer bufB = ByteBuffer.allocateDirect(bufSizeB); -- for (int i = 0; i < bufSizeA; ++i) { -- bufA.put((byte)('a' + (i % 20))); -- } -- for (int i = 0; i < bufSizeB; ++i) { -- bufB.put((byte)('A' + (i % 20))); -- } -- bufA.flip(); -- bufB.flip(); -+ }); -+ readerThread.start(); -+ -+ try (SocketChannel sc = SocketChannel.open(ss.socket().getLocalSocketAddress())) { -+ ByteBuffer bufA = ByteBuffer.allocateDirect(bufSizeA); -+ ByteBuffer bufB = ByteBuffer.allocateDirect(bufSizeB); -+ for (int i = 0; i < bufSizeA; ++i) { -+ bufA.put((byte) ('a' + (i % 20))); -+ } -+ for (int i = 0; i < bufSizeB; ++i) { -+ bufB.put((byte) ('A' + (i % 20))); -+ } -+ bufA.flip(); -+ bufB.flip(); - -- sc.write(bufA); -- addExpectedEvent(IOEvent.createSocketWriteEvent(bufSizeA, sc.socket())); -+ sc.write(bufA); -+ addExpectedEvent(IOEvent.createSocketWriteEvent(bufSizeA, sc.socket())); - -- bufA.clear(); -- bufA.limit(1); -- int bytesWritten = (int)sc.write(new ByteBuffer[] { bufA, bufB }); -- assertEquals(bytesWritten, 1 + bufSizeB, "Wrong bytesWritten 1+bufB"); -- addExpectedEvent(IOEvent.createSocketWriteEvent(bytesWritten, sc.socket())); -- } -+ bufA.clear(); -+ bufA.limit(1); -+ int bytesWritten = (int) sc.write(new ByteBuffer[] { bufA, bufB }); -+ assertEquals(bytesWritten, 1 + bufSizeB, "Wrong bytesWritten 1+bufB"); -+ addExpectedEvent(IOEvent.createSocketWriteEvent(bytesWritten, sc.socket())); -+ } - -- readerThread.joinAndThrow(); -- recording.stop(); -- List events= Events.fromRecording(recording); -- IOHelper.verifyEquals(events, expectedEvents); -+ readerThread.joinAndThrow(); -+ recording.stop(); -+ List events = Events.fromRecording(recording); -+ IOHelper.verifyEquals(events, expectedEvents); -+ } - } - } - } -diff --git a/jdk/test/jdk/jfr/event/io/TestSocketEvents.java b/jdk/test/jdk/jfr/event/io/TestSocketEvents.java -index c0b64aa7d..5b544cc7e 100644 ---- a/jdk/test/jdk/jfr/event/io/TestSocketEvents.java -+++ b/jdk/test/jdk/jfr/event/io/TestSocketEvents.java -@@ -1,5 +1,5 @@ - /* -- * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. -+ * Copyright (c) 2018, 2020, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it -@@ -55,6 +55,7 @@ public class TestSocketEvents { - private static final byte[] writeBuf = { 'B', 'C', 'D', 'E' }; - - private List expectedEvents = new ArrayList<>(); -+ - private synchronized void addExpectedEvent(IOEvent event) { - expectedEvents.add(event); - } -@@ -64,58 +65,59 @@ public class TestSocketEvents { - } - - private void test() throws Throwable { -- Recording recording = new Recording(); -- -- try (ServerSocket ss = new ServerSocket()) { -- recording.enable(IOEvent.EVENT_SOCKET_READ).withThreshold(Duration.ofMillis(0)); -- recording.enable(IOEvent.EVENT_SOCKET_WRITE).withThreshold(Duration.ofMillis(0)); -- recording.start(); -- -- ss.setReuseAddress(true); -- ss.bind(null); -- -- TestThread readerThread = new TestThread(new XRun() { -- @Override -- public void xrun() throws IOException { -- byte[] bs = new byte[4]; -- try (Socket s = ss.accept(); InputStream is = s.getInputStream()) { -- int readInt = is.read(); -- assertEquals(readInt, writeInt, "Wrong readInt"); -- addExpectedEvent(IOEvent.createSocketReadEvent(1, s)); -- -- int bytesRead = is.read(bs, 0, 3); -- assertEquals(bytesRead, 3, "Wrong bytesRead partial buffer"); -- addExpectedEvent(IOEvent.createSocketReadEvent(bytesRead, s)); -- -- bytesRead = is.read(bs); -- assertEquals(bytesRead, writeBuf.length, "Wrong bytesRead full buffer"); -- addExpectedEvent(IOEvent.createSocketReadEvent(bytesRead, s)); -- -- // Try to read more, but writer have closed. Should get EOF. -- readInt = is.read(); -- assertEquals(readInt, -1, "Wrong readInt at EOF"); -- addExpectedEvent(IOEvent.createSocketReadEvent(-1, s)); -- } -- } -- }); -- readerThread.start(); -- -- try (Socket s = new Socket()) { -- s.connect(ss.getLocalSocketAddress()); -- try (OutputStream os = s.getOutputStream()) { -- os.write(writeInt); -- addExpectedEvent(IOEvent.createSocketWriteEvent(1, s)); -- os.write(writeBuf, 0, 3); -- addExpectedEvent(IOEvent.createSocketWriteEvent(3, s)); -- os.write(writeBuf); -- addExpectedEvent(IOEvent.createSocketWriteEvent(writeBuf.length, s)); -+ try (Recording recording = new Recording()) { -+ try (ServerSocket ss = new ServerSocket()) { -+ recording.enable(IOEvent.EVENT_SOCKET_READ).withThreshold(Duration.ofMillis(0)); -+ recording.enable(IOEvent.EVENT_SOCKET_WRITE).withThreshold(Duration.ofMillis(0)); -+ recording.start(); -+ -+ ss.setReuseAddress(true); -+ ss.bind(null); -+ -+ TestThread readerThread = new TestThread(new XRun() { -+ @Override -+ public void xrun() throws IOException { -+ byte[] bs = new byte[4]; -+ try (Socket s = ss.accept(); InputStream is = s.getInputStream()) { -+ int readInt = is.read(); -+ assertEquals(readInt, writeInt, "Wrong readInt"); -+ addExpectedEvent(IOEvent.createSocketReadEvent(1, s)); -+ -+ int bytesRead = is.read(bs, 0, 3); -+ assertEquals(bytesRead, 3, "Wrong bytesRead partial buffer"); -+ addExpectedEvent(IOEvent.createSocketReadEvent(bytesRead, s)); -+ -+ bytesRead = is.read(bs); -+ assertEquals(bytesRead, writeBuf.length, "Wrong bytesRead full buffer"); -+ addExpectedEvent(IOEvent.createSocketReadEvent(bytesRead, s)); -+ -+ // Try to read more, but writer have closed. Should -+ // get EOF. -+ readInt = is.read(); -+ assertEquals(readInt, -1, "Wrong readInt at EOF"); -+ addExpectedEvent(IOEvent.createSocketReadEvent(-1, s)); -+ } -+ } -+ }); -+ readerThread.start(); -+ -+ try (Socket s = new Socket()) { -+ s.connect(ss.getLocalSocketAddress()); -+ try (OutputStream os = s.getOutputStream()) { -+ os.write(writeInt); -+ addExpectedEvent(IOEvent.createSocketWriteEvent(1, s)); -+ os.write(writeBuf, 0, 3); -+ addExpectedEvent(IOEvent.createSocketWriteEvent(3, s)); -+ os.write(writeBuf); -+ addExpectedEvent(IOEvent.createSocketWriteEvent(writeBuf.length, s)); -+ } - } -- } - -- readerThread.joinAndThrow(); -- recording.stop(); -- List events = Events.fromRecording(recording); -- IOHelper.verifyEquals(events, expectedEvents); -+ readerThread.joinAndThrow(); -+ recording.stop(); -+ List events = Events.fromRecording(recording); -+ IOHelper.verifyEquals(events, expectedEvents); -+ } - } - } - } --- -2.22.0 - diff --git a/8202951-Support-default-jsa.patch b/8202951-Support-default-jsa.patch new file mode 100644 index 0000000000000000000000000000000000000000..0724ff1c97e131598afaf47223b5bafdff75b8fe --- /dev/null +++ b/8202951-Support-default-jsa.patch @@ -0,0 +1,1394 @@ +From 49f7ef8df4cade226de5754172e208975343967c Mon Sep 17 00:00:00 2001 +Date: Sat, 3 Sep 2022 14:25:50 +0000 +Subject: 8202951-Support-default-jsa + +--- + common/autoconf/configure.ac | 3 + + common/autoconf/generated-configure.sh | 40 +++++ + common/autoconf/jdk-options.m4 | 32 ++++ + common/autoconf/spec.gmk.in | 4 + + common/bin/compare.sh | 1 + + hotspot/src/share/vm/cds/archiveBuilder.cpp | 34 +++- + hotspot/src/share/vm/cds/archiveBuilder.hpp | 4 + + hotspot/src/share/vm/cds/dynamicArchive.cpp | 1 + + .../src/share/vm/classfile/classLoader.cpp | 152 +++++++++++++++++- + .../src/share/vm/classfile/classLoader.hpp | 15 +- + .../share/vm/classfile/classLoaderData.hpp | 1 + + hotspot/src/share/vm/classfile/dictionary.cpp | 2 +- + .../vm/classfile/sharedPathsMiscInfo.hpp | 13 +- + .../vm/classfile/systemDictionaryShared.cpp | 2 +- + hotspot/src/share/vm/memory/filemap.cpp | 34 +++- + hotspot/src/share/vm/memory/filemap.hpp | 7 + + hotspot/src/share/vm/memory/metachunk.hpp | 2 + + hotspot/src/share/vm/memory/metaspace.cpp | 14 ++ + hotspot/src/share/vm/memory/metaspace.hpp | 2 + + .../src/share/vm/memory/metaspaceShared.cpp | 22 +++ + .../src/share/vm/memory/metaspaceShared.hpp | 5 +- + hotspot/src/share/vm/oops/instanceKlass.cpp | 5 +- + hotspot/src/share/vm/runtime/arguments.cpp | 34 ++-- + hotspot/src/share/vm/runtime/arguments.hpp | 6 + + hotspot/src/share/vm/utilities/hashtable.cpp | 1 + + hotspot/test/runtime/appcds/TestCommon.java | 74 ++++++++- + .../appcds/dynamicArchive/DynamicFlag.java | 39 +++++ + .../dynamicArchive/DynamicHelloTest.java | 42 +++++ + .../VerifyWithDynamicArchive.java | 42 +++++ + jdk/make/BuildJdk.gmk | 7 + + 30 files changed, 603 insertions(+), 37 deletions(-) + create mode 100644 hotspot/test/runtime/appcds/dynamicArchive/DynamicFlag.java + create mode 100644 hotspot/test/runtime/appcds/dynamicArchive/DynamicHelloTest.java + create mode 100644 hotspot/test/runtime/appcds/dynamicArchive/VerifyWithDynamicArchive.java + +diff --git a/common/autoconf/configure.ac b/common/autoconf/configure.ac +index 151e5a10..dbcdd59e 100644 +--- a/common/autoconf/configure.ac ++++ b/common/autoconf/configure.ac +@@ -98,6 +98,9 @@ JDKOPT_SETUP_JVM_INTERPRETER + JDKOPT_SETUP_JVM_VARIANTS + JDKOPT_SETUP_DEBUG_LEVEL + ++# Enable default CDS ARCHIVE ++JDKOPT_ENABLE_DISABLE_CDS_ARCHIVE ++ + # With basic setup done, call the custom early hook. + CUSTOM_EARLY_HOOK + +diff --git a/common/autoconf/generated-configure.sh b/common/autoconf/generated-configure.sh +index c41c4336..f0e49f50 100644 +--- a/common/autoconf/generated-configure.sh ++++ b/common/autoconf/generated-configure.sh +@@ -883,6 +883,7 @@ OUTPUT_ROOT + CONF_NAME + SPEC + DEVKIT_LIB_DIR ++BUILD_CDS_ARCHIVE + BUILD_VARIANT_RELEASE + DEBUG_CLASSFILES + FASTDEBUG +@@ -1047,6 +1048,7 @@ with_jvm_interpreter + with_jvm_variants + enable_debug + with_debug_level ++enable_cds_archive + with_devkit + with_sys_root + with_sysroot +@@ -1857,6 +1859,8 @@ Optional Features: + [disabled] + --enable-debug set the debug level to fastdebug (shorthand for + --with-debug-level=fastdebug) [disabled] ++ --disable-cds-archive Set to disable generation of a default CDS archive ++ in the product image [enabled] + --disable-headful disable building headful support (graphical UI + support) [enabled] + --enable-hotspot-test-in-build +@@ -14704,6 +14708,42 @@ fi + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $DEBUG_LEVEL" >&5 + $as_echo "$DEBUG_LEVEL" >&6; } + ++ ++ ++# Enable default CDS ARCHIVE ++ ++ # Check whether --enable-cds-archive was given. ++if test "${enable_cds_archive+set}" = set; then : ++ enableval=$enable_cds_archive; ++fi ++ ++ ++{ $as_echo "$as_me:${as_lineno-$LINENO}: checking if a default CDS archive should be generated" >&5 ++$as_echo_n "checking if a default CDS archive should be generated... " >&6; } ++ if test "x$COMPILE_TYPE" = "xcross"; then ++ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no, not possible with cross compilation" >&5 ++$as_echo "no, not possible with cross compilation" >&6; } ++ BUILD_CDS_ARCHIVE="false" ++ elif test "x$enable_cds_archive" = "xyes"; then ++ { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes, forced" >&5 ++$as_echo "yes, forced" >&6; } ++ BUILD_CDS_ARCHIVE="true" ++ elif test "x$enable_cds_archive" = "x"; then ++ { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 ++$as_echo "yes" >&6; } ++ BUILD_CDS_ARCHIVE="true" ++ elif test "x$enable_cds_archive" = "xno"; then ++ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no, forced" >&5 ++$as_echo "no, forced" >&6; } ++ BUILD_CDS_ARCHIVE="false" ++ else ++ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 ++$as_echo "no" >&6; } ++ as_fn_error $? "--enable-cds_archive can only be yes/no or empty" "$LINENO" 5 ++ fi ++ ++ ++ + if test "x$DEBUG_LEVEL" != xrelease && \ + test "x$DEBUG_LEVEL" != xfastdebug && \ + test "x$DEBUG_LEVEL" != xslowdebug; then +diff --git a/common/autoconf/jdk-options.m4 b/common/autoconf/jdk-options.m4 +index bca78afe..c506086d 100644 +--- a/common/autoconf/jdk-options.m4 ++++ b/common/autoconf/jdk-options.m4 +@@ -789,6 +789,38 @@ AC_DEFUN_ONCE([JDKOPT_SETUP_DEBUG_SYMBOLS], + AC_SUBST(ZIP_DEBUGINFO_FILES) + ]) + ++################################################################################ ++# ++# Disable the default CDS archive generation ++# cross compilation - disabled ++# ++AC_DEFUN_ONCE([JDKOPT_ENABLE_DISABLE_CDS_ARCHIVE], ++[ ++ AC_ARG_ENABLE([cds-archive], [AS_HELP_STRING([--disable-cds-archive], ++ [Set to disable generation of a default CDS archive in the product image @<:@enabled@:>@])]) ++ ++ AC_MSG_CHECKING([if a default CDS archive should be generated]) ++ if test "x$COMPILE_TYPE" = "xcross"; then ++ AC_MSG_RESULT([no, not possible with cross compilation]) ++ BUILD_CDS_ARCHIVE="false" ++ elif test "x$enable_cds_archive" = "xyes"; then ++ AC_MSG_RESULT([yes, forced]) ++ BUILD_CDS_ARCHIVE="true" ++ elif test "x$enable_cds_archive" = "x"; then ++ AC_MSG_RESULT([yes]) ++ BUILD_CDS_ARCHIVE="true" ++ elif test "x$enable_cds_archive" = "xno"; then ++ AC_MSG_RESULT([no, forced]) ++ BUILD_CDS_ARCHIVE="false" ++ else ++ AC_MSG_RESULT([no]) ++ AC_MSG_ERROR([--enable-cds_archive can only be yes/no or empty]) ++ fi ++ ++ AC_SUBST(BUILD_CDS_ARCHIVE) ++]) ++ ++ + # Support for customization of the build process. Some build files + # will include counterparts from this location, if they exist. This allows + # for a degree of customization of the build targets and the rules/recipes +diff --git a/common/autoconf/spec.gmk.in b/common/autoconf/spec.gmk.in +index 4c3a9f61..79248cbf 100644 +--- a/common/autoconf/spec.gmk.in ++++ b/common/autoconf/spec.gmk.in +@@ -611,6 +611,10 @@ LIBZIP_CAN_USE_MMAP:=@LIBZIP_CAN_USE_MMAP@ + MSVCR_DLL:=@MSVCR_DLL@ + MSVCP_DLL:=@MSVCP_DLL@ + UCRT_DLL_DIR:=@UCRT_DLL_DIR@ ++# CDS_ARCHIVE ++BUILD_CDS_ARCHIVE:=@BUILD_CDS_ARCHIVE@ ++ ++ + + + # ADD_SRCS takes a single argument with source roots +diff --git a/common/bin/compare.sh b/common/bin/compare.sh +index ff88bb1f..a36464a9 100644 +--- a/common/bin/compare.sh ++++ b/common/bin/compare.sh +@@ -290,6 +290,7 @@ compare_general_files() { + ! -name "ct.sym" ! -name "*.diz" ! -name "*.dll" \ + ! -name "*.pdb" ! -name "*.exp" ! -name "*.ilk" \ + ! -name "*.lib" ! -name "*.war" ! -name "JavaControlPanel" \ ++ ! -name "classes.jsa" \ + | $GREP -v "./bin/" | $SORT | $FILTER) + + echo General files... +diff --git a/hotspot/src/share/vm/cds/archiveBuilder.cpp b/hotspot/src/share/vm/cds/archiveBuilder.cpp +index 144dedfa..13a62002 100644 +--- a/hotspot/src/share/vm/cds/archiveBuilder.cpp ++++ b/hotspot/src/share/vm/cds/archiveBuilder.cpp +@@ -59,6 +59,18 @@ ArchiveBuilder::SourceObjList::~SourceObjList() { + delete _objs; + } + ++static void caculate_fingerprint(Klass * klass) { ++ if (klass->oop_is_instance()) { ++ InstanceKlass* ik = InstanceKlass::cast(klass); ++ for (int i = 0; i < ik->methods()->length(); i++) { ++ Method* m = ik->methods()->at(i); ++ Fingerprinter fp(m); ++ // The side effect of this call sets method's fingerprint field. ++ fp.fingerprint(); ++ } ++ } ++} ++ + void ArchiveBuilder::SourceObjList::append(MetaspaceClosure::Ref* enclosing_ref, SourceObjInfo* src_info) { + // Save this source object for copying + _objs->append(src_info); +@@ -166,6 +178,7 @@ ArchiveBuilder::ArchiveBuilder() : + _buffer_to_requested_delta(0), + _rw_region("rw", MAX_SHARED_DELTA), + _ro_region("ro", MAX_SHARED_DELTA), ++ _md_region("md", MAX_SHARED_DELTA), + _rw_src_objs(), + _ro_src_objs(), + _src_obj_table(INITIAL_TABLE_SIZE), +@@ -384,6 +397,7 @@ bool ArchiveBuilder::gather_klass_and_symbol(MetaspaceClosure::Ref* ref, bool re + Klass* klass = (Klass*)ref->obj(); + assert(klass->is_klass(), "must be"); + if (!is_excluded(klass)) { ++ caculate_fingerprint(klass); + _klasses->append(klass); + if (klass->oop_is_instance()) { + _num_instance_klasses ++; +@@ -434,7 +448,8 @@ size_t ArchiveBuilder::estimate_archive_size() { + + address ArchiveBuilder::reserve_buffer() { + size_t buffer_size = estimate_archive_size(); +- ReservedSpace rs(buffer_size, os::vm_allocation_granularity(), false); ++ size_t package_hash_table_est = align_up(ClassLoader::estimate_size_for_archive(), (size_t)os::vm_allocation_granularity()); ++ ReservedSpace rs(buffer_size + package_hash_table_est, os::vm_allocation_granularity(), false); + if (!rs.is_reserved()) { + tty->print_cr("Failed to reserve " SIZE_FORMAT " bytes of output buffer.", buffer_size); + vm_direct_exit(0); +@@ -443,7 +458,8 @@ address ArchiveBuilder::reserve_buffer() { + // buffer_bottom is the lowest address of the 2 core regions (rw, ro) when + // we are copying the class metadata into the buffer. + address buffer_bottom = (address)rs.base(); +- _shared_rs = rs; ++ _shared_rs = rs.first_part(buffer_size); ++ _md_rs = rs.last_part(buffer_size); + + _buffer_bottom = buffer_bottom; + _last_verified_top = buffer_bottom; +@@ -508,6 +524,19 @@ void ArchiveBuilder::dump_ro_metadata() { + make_shallow_copies(&_ro_region, &_ro_src_objs); + } + ++void ArchiveBuilder::dump_md_metadata() { ++ ResourceMark rm; ++ if (InfoDynamicCDS) { ++ dynamic_cds_log->print_cr("Allocating MD objects ... "); ++ } ++ _current_dump_space = &_md_region; ++ _md_region.init(&_md_rs, &_md_vs); ++ char* md_top = _md_vs.low(); ++ char* md_end = _md_vs.high_boundary(); ++ _md_region.allocate(md_end - md_top); ++ ClassLoader::serialize_package_hash_table(&md_top, md_end); ++} ++ + void ArchiveBuilder::start_dump_space(DumpRegion* next) { + address bottom = _last_verified_top; + address top = (address)(_current_dump_space->top()); +@@ -749,6 +778,7 @@ void ArchiveBuilder::write_archive(FileMapInfo* mapinfo) { + + write_region(mapinfo, MetaspaceShared::d_rw, &_rw_region, /*read_only=*/false,/*allow_exec=*/false); + write_region(mapinfo, MetaspaceShared::d_ro, &_ro_region, /*read_only=*/true, /*allow_exec=*/false); ++ write_region(mapinfo, MetaspaceShared::d_md, &_md_region, /*read_only=*/true, /*allow_exec=*/false); + + char* bitmap = mapinfo->write_bitmap_region(ArchivePtrMarker::ptrmap()); + +diff --git a/hotspot/src/share/vm/cds/archiveBuilder.hpp b/hotspot/src/share/vm/cds/archiveBuilder.hpp +index 18cd3c62..f7a5c107 100644 +--- a/hotspot/src/share/vm/cds/archiveBuilder.hpp ++++ b/hotspot/src/share/vm/cds/archiveBuilder.hpp +@@ -163,10 +163,13 @@ private: + static const int MAX_TABLE_SIZE = 1000000; + + ReservedSpace _shared_rs; ++ ReservedSpace _md_rs; + VirtualSpace _shared_vs; ++ VirtualSpace _md_vs; + + DumpRegion _rw_region; + DumpRegion _ro_region; ++ DumpRegion _md_region; + BitMap _ptrmap; + + SourceObjList _rw_src_objs; // objs to put in rw region +@@ -327,6 +330,7 @@ public: + + void dump_rw_metadata(); + void dump_ro_metadata(); ++ void dump_md_metadata(); + void relocate_metaspaceobj_embedded_pointers(); + void relocate_roots(); + void make_klasses_shareable(); +diff --git a/hotspot/src/share/vm/cds/dynamicArchive.cpp b/hotspot/src/share/vm/cds/dynamicArchive.cpp +index efed275c..a623c5b0 100644 +--- a/hotspot/src/share/vm/cds/dynamicArchive.cpp ++++ b/hotspot/src/share/vm/cds/dynamicArchive.cpp +@@ -149,6 +149,7 @@ public: + + relocate_to_requested(); + ++ dump_md_metadata(); + write_archive(serialized_data); + release_header(); + +diff --git a/hotspot/src/share/vm/classfile/classLoader.cpp b/hotspot/src/share/vm/classfile/classLoader.cpp +index e3470ca8..04fa84d4 100644 +--- a/hotspot/src/share/vm/classfile/classLoader.cpp ++++ b/hotspot/src/share/vm/classfile/classLoader.cpp +@@ -219,6 +219,30 @@ const char* ClassLoader::package_from_name(const char* const class_name, bool* b + return (const char *)pkg_name; + } + ++const char* ClassLoader::get_file_name_from_path(const char* path) { ++ const char* pos = strrchr(path, '/'); ++ if (pos == NULL) { ++ return path; ++ } else { ++ return pos + 1; ++ } ++} ++ ++const char* ClassLoader::get_boot_class_path(const char* shared_path) { ++ const char* shared_name = get_file_name_from_path(shared_path); ++ ClassPathEntry* e = _first_entry; ++ while (e != NULL) { ++ if (e->sys_class()) { ++ const char* name = get_file_name_from_path(e->name()); ++ if (strcmp(name, shared_name) == 0) { ++ return e->name(); ++ } ++ } ++ e = e->next(); ++ } ++ return NULL; ++} ++ + MetaIndex::MetaIndex(char** meta_package_names, int num_meta_package_names) { + if (num_meta_package_names == 0) { + _meta_package_names = NULL; +@@ -512,6 +536,8 @@ void ClassLoader::setup_meta_index(const char* meta_index_path, const char* meta + int line_no = 0; + #if INCLUDE_CDS + if (DumpSharedSpaces) { ++ meta_index_path = Arguments::get_is_default_jsa() ? ++ get_file_name_from_path(meta_index_path) : meta_index_path; + if (file != NULL) { + _shared_paths_misc_info->add_required_file(meta_index_path); + } else { +@@ -644,7 +670,9 @@ void ClassLoader::setup_bootstrap_search_path() { + } + #if INCLUDE_CDS + if (DumpSharedSpaces) { +- _shared_paths_misc_info->add_boot_classpath(sys_class_path); ++ const char* new_sys_class_path = Arguments::get_is_default_jsa() ? ++ get_file_name_from_path(sys_class_path) : sys_class_path; ++ _shared_paths_misc_info->add_boot_classpath(new_sys_class_path); + } + #endif + setup_search_path(sys_class_path); +@@ -688,7 +716,7 @@ void ClassLoader::setup_search_path(const char *class_path, bool canonicalize) { + path = canonical_path; + } + } +- update_class_path_entry_list(path, /*check_for_duplicates=*/canonicalize); ++ update_class_path_entry_list(path, /*check_for_duplicates=*/canonicalize, true, true); + #if INCLUDE_CDS + if (DumpSharedSpaces) { + check_shared_classpath(path); +@@ -816,7 +844,9 @@ void ClassLoader::add_to_list(ClassPathEntry *new_entry) { + // Returns true IFF the file/dir exists and the entry was successfully created. + bool ClassLoader::update_class_path_entry_list(const char *path, + bool check_for_duplicates, +- bool throw_exception) { ++ bool throw_exception, ++ bool sys_class_type) { ++ // sys_class_type indicates whether *path is a system path. The default value is false. + struct stat st; + if (os::stat(path, &st) == 0) { + // File or directory found +@@ -826,6 +856,11 @@ bool ClassLoader::update_class_path_entry_list(const char *path, + if (new_entry == NULL) { + return false; + } ++ // If the path is a system path, set sys_class of the newly created ++ // linked list node to true. The default value is false. ++ if (sys_class_type) { ++ new_entry->set_sys_class(true); ++ } + // The kernel VM adds dynamically to the end of the classloader path and + // doesn't reorder the bootclasspath which would break java.lang.Package + // (see PackageInfo). +@@ -837,6 +872,8 @@ bool ClassLoader::update_class_path_entry_list(const char *path, + } else { + #if INCLUDE_CDS + if (DumpSharedSpaces) { ++ path = Arguments::get_is_default_jsa() ? ++ get_file_name_from_path(path) : path; + _shared_paths_misc_info->add_nonexist_path(path); + } + #endif +@@ -918,6 +955,7 @@ int ClassLoader::crc32(int crc, const char* buf, int len) { + class PackageInfo: public BasicHashtableEntry { + public: + const char* _pkgname; // Package name ++ const char* _filename; // File name + int _classpath_index; // Index of directory or JAR file loaded from + + PackageInfo* next() { +@@ -926,9 +964,10 @@ public: + + const char* pkgname() { return _pkgname; } + void set_pkgname(char* pkgname) { _pkgname = pkgname; } ++ void set_filename(char* filename) { _filename = filename; } + + const char* filename() { +- return ClassLoader::classpath_entry(_classpath_index)->name(); ++ return _filename == NULL ? ClassLoader::classpath_entry(_classpath_index)->name() : _filename; + } + + void set_index(int index) { +@@ -975,11 +1014,12 @@ public: + return get_entry(hash_to_index(hash), hash, pkgname, n); + } + +- PackageInfo* new_entry(char* pkgname, int n) { ++ PackageInfo* new_entry(char* pkgname, int n, char* filename = NULL) { + unsigned int hash = compute_hash(pkgname, n); + PackageInfo* pp; + pp = (PackageInfo*)BasicHashtable::new_entry(hash); + pp->set_pkgname(pkgname); ++ pp->set_filename(filename); + return pp; + } + +@@ -999,6 +1039,9 @@ public: + } + + CDS_ONLY(void copy_table(char** top, char* end, PackageHashtable* table);) ++ CDS_ONLY(void serialize(char** top, char* end);) ++ CDS_ONLY(void deserialize(char* start);) ++ CDS_ONLY(size_t estimate_size();) + }; + + #if INCLUDE_CDS +@@ -1035,6 +1078,93 @@ void PackageHashtable::copy_table(char** top, char* end, + *tableSize = len; + } + ++size_t PackageHashtable::estimate_size() { ++ int size = sizeof(int); ++ ClassPathEntry* e = ClassLoader::_first_entry; ++ while (e != NULL) { ++ int length = (int)(strlen(e->name()) + 1); ++ size += length; ++ e = e->next(); ++ } ++ size = align_size_up(size, sizeof(int)); ++ ++ size += sizeof(int); ++ for (int i = 0; i < table_size(); ++i) { ++ for (PackageInfo* pp = bucket(i); ++ pp != NULL; ++ pp = pp->next()) { ++ size += sizeof(int); ++ int n1 = (int)(strlen(pp->pkgname()) + 1); ++ n1 = align_size_up(n1, sizeof(int)); ++ size += n1; ++ } ++ } ++ return align_size_up(size, sizeof(int)); ++} ++ ++void PackageHashtable::serialize(char** top, char* end) { ++ *(int*)(*top) = ClassLoader::_num_entries; ++ *top += sizeof(int); ++ ++ ClassPathEntry* e = ClassLoader::_first_entry; ++ while (e != NULL) { ++ int length = (int)(strlen(e->name()) + 1); ++ memcpy(*top, e->name(), length); ++ *top += length; ++ e = e->next(); ++ } ++ *top = (char*)align_size_up((intptr_t)*top, sizeof(int)); ++ *(int*)(*top) = number_of_entries(); ++ *top += sizeof(int); ++ ++ for (int i = 0; i < table_size(); ++i) { ++ for (PackageInfo* pp = bucket(i); ++ pp != NULL; ++ pp = pp->next()) { ++ *(int*)(*top) = pp->_classpath_index; ++ *top += sizeof(int); ++ int n1 = (int)(strlen(pp->pkgname()) + 1); ++ memcpy(*top, pp->pkgname(), n1); ++ n1 = align_size_up(n1, sizeof(int)); ++ *top += n1; ++ } ++ } ++} ++ ++void PackageHashtable::deserialize(char* start) { ++ int num_entries = *(int*)start; ++ char** class_loader_entries = NEW_C_HEAP_ARRAY(char*, num_entries, mtClass); ++ start += sizeof(int); ++ int entries_len = 0; ++ for (int i = 0, index = 0; i < num_entries; i++) { ++ class_loader_entries[index++] = start + entries_len; ++ entries_len += (int)(strlen(start + entries_len) + 1); ++ } ++ start += align_size_up(entries_len, sizeof(int)); ++ int number_of_entries = *(int*)start; ++ start += sizeof(int); ++ for (int i = 0; i < number_of_entries; i++) { ++ int classpath_index = *(int*)start; ++ start += sizeof(int); ++ char* pkgname = start; ++ const char *cp = strrchr(pkgname, '/'); ++ if (cp != NULL) { ++ int n = cp - pkgname + 1; ++ if (get_entry(pkgname, n) == NULL) { ++ PackageInfo* info = new_entry(pkgname, n, class_loader_entries[classpath_index]); ++ add_entry(info); ++ } ++ } ++ int n1 = (int)(strlen(start) + 1); ++ start += align_size_up(n1, sizeof(int)); ++ } ++ FREE_C_HEAP_ARRAY(char*, class_loader_entries, mtClass); ++} ++ ++void ClassLoader::deserialize_package_hash_table(char* start) { ++ assert(_package_hash_table != NULL, "should have one yet"); ++ _package_hash_table->deserialize(start); ++} + + void ClassLoader::copy_package_info_buckets(char** top, char* end) { + _package_hash_table->copy_buckets(top, end); +@@ -1043,6 +1173,14 @@ void ClassLoader::copy_package_info_buckets(char** top, char* end) { + void ClassLoader::copy_package_info_table(char** top, char* end) { + _package_hash_table->copy_table(top, end, _package_hash_table); + } ++ ++size_t ClassLoader::estimate_size_for_archive() { ++ return _package_hash_table->estimate_size(); ++} ++ ++void ClassLoader::serialize_package_hash_table(char** top, char* end) { ++ return _package_hash_table->serialize(top, end); ++} + #endif + + PackageInfo* ClassLoader::lookup_package(const char *pkgname) { +@@ -1226,8 +1364,8 @@ void ClassLoader::create_package_info_table(HashtableBucket *t, int len + + + void ClassLoader::create_package_info_table() { +- assert(_package_hash_table == NULL, "shouldn't have one yet"); +- _package_hash_table = new PackageHashtable(package_hash_table_size); ++ assert(_package_hash_table == NULL, "shouldn't have one yet"); ++ _package_hash_table = new PackageHashtable(package_hash_table_size); + } + + +diff --git a/hotspot/src/share/vm/classfile/classLoader.hpp b/hotspot/src/share/vm/classfile/classLoader.hpp +index 9514d3bb..cf39ce99 100644 +--- a/hotspot/src/share/vm/classfile/classLoader.hpp ++++ b/hotspot/src/share/vm/classfile/classLoader.hpp +@@ -49,13 +49,18 @@ class MetaIndex: public CHeapObj { + class ClassPathEntry: public CHeapObj { + private: + ClassPathEntry* _next; ++ bool _sys_class; + public: + // Next entry in class path + ClassPathEntry* next() { return _next; } ++ bool sys_class() const { return _sys_class; } + void set_next(ClassPathEntry* next) { + // may have unlocked readers, so write atomically. + OrderAccess::release_store_ptr(&_next, next); + } ++ void set_sys_class(bool isSysClass) { ++ _sys_class = isSysClass; ++ } + virtual bool is_jar_file() = 0; + virtual const char* name() = 0; + virtual bool is_lazy(); +@@ -158,6 +163,7 @@ class ClassLoader: AllStatic { + }; + protected: + friend class LazyClassPathEntry; ++ friend class PackageHashtable; + + // Performance counters + static PerfCounter* _perf_accumulated_time; +@@ -234,7 +240,8 @@ class ClassLoader: AllStatic { + static int crc32(int crc, const char* buf, int len); + static bool update_class_path_entry_list(const char *path, + bool check_for_duplicates, +- bool throw_exception=true); ++ bool throw_exception=true, ++ bool sys_class=false); + static void print_bootclasspath(); + + // Timing +@@ -318,6 +325,9 @@ class ClassLoader: AllStatic { + // Initialization + static void initialize(); + CDS_ONLY(static void initialize_shared_path();) ++ static const char* get_file_name_from_path(const char* path); ++ static const char* get_boot_class_path(const char* shared_path); ++ + static void create_package_info_table(); + static void create_package_info_table(HashtableBucket *t, int length, + int number_of_entries); +@@ -340,6 +350,9 @@ class ClassLoader: AllStatic { + // Sharing dump and restore + static void copy_package_info_buckets(char** top, char* end); + static void copy_package_info_table(char** top, char* end); ++ static size_t estimate_size_for_archive(); ++ static void serialize_package_hash_table(char** top, char* end); ++ static void deserialize_package_hash_table(char* start); + + static void check_shared_classpath(const char *path); + static void finalize_shared_paths_misc_info(); +diff --git a/hotspot/src/share/vm/classfile/classLoaderData.hpp b/hotspot/src/share/vm/classfile/classLoaderData.hpp +index 9b901303..7155257e 100644 +--- a/hotspot/src/share/vm/classfile/classLoaderData.hpp ++++ b/hotspot/src/share/vm/classfile/classLoaderData.hpp +@@ -168,6 +168,7 @@ class ClassLoaderData : public CHeapObj { + friend class ClassLoaderDataGraphMetaspaceIterator; + friend class MetaDataFactory; + friend class Method; ++ friend class VM_PopulateDumpSharedSpace; + + static ClassLoaderData * _the_null_class_loader_data; + +diff --git a/hotspot/src/share/vm/classfile/dictionary.cpp b/hotspot/src/share/vm/classfile/dictionary.cpp +index b9d473b0..d41372ec 100644 +--- a/hotspot/src/share/vm/classfile/dictionary.cpp ++++ b/hotspot/src/share/vm/classfile/dictionary.cpp +@@ -197,7 +197,7 @@ void Dictionary::roots_oops_do(OopClosure* strong, OopClosure* weak) { + } + + void Dictionary::remove_classes_in_error_state() { +- assert(DumpSharedSpaces, "supported only when dumping"); ++ assert(DynamicDumpSharedSpaces || DumpSharedSpaces, "supported only when dumping"); + DictionaryEntry* probe = NULL; + for (int index = 0; index < table_size(); index++) { + for (DictionaryEntry** p = bucket_addr(index); *p != NULL; ) { +diff --git a/hotspot/src/share/vm/classfile/sharedPathsMiscInfo.hpp b/hotspot/src/share/vm/classfile/sharedPathsMiscInfo.hpp +index 882fed01..b1609e46 100644 +--- a/hotspot/src/share/vm/classfile/sharedPathsMiscInfo.hpp ++++ b/hotspot/src/share/vm/classfile/sharedPathsMiscInfo.hpp +@@ -26,6 +26,7 @@ + #define SHARE_VM_CLASSFILE_SHAREDPATHSMISCINFO_HPP + + #include "runtime/os.hpp" ++#include "runtime/arguments.hpp" + + // During dumping time, when processing class paths, we build up the dump-time + // classpath. The JAR files that exist are stored in the list ClassLoader::_first_entry. +@@ -111,12 +112,18 @@ public: + add_path(path, REQUIRED); + + struct stat st; +- if (os::stat(path, &st) != 0) { ++ if (!Arguments::get_is_default_jsa() && os::stat(path, &st) != 0) { + assert(0, "sanity"); + ClassLoader::exit_with_path_failure("failed to os::stat(%s)", path); // should not happen + } +- write_time(st.st_mtime); +- write_long(st.st_size); ++ ++ if (Arguments::get_is_default_jsa()) { ++ write_time(0); ++ write_long(0); ++ } else { ++ write_time(st.st_mtime); ++ write_long(st.st_size); ++ } + } + + // The path must exist, and must contain exactly files/dirs +diff --git a/hotspot/src/share/vm/classfile/systemDictionaryShared.cpp b/hotspot/src/share/vm/classfile/systemDictionaryShared.cpp +index 99354cd4..3a601ee3 100644 +--- a/hotspot/src/share/vm/classfile/systemDictionaryShared.cpp ++++ b/hotspot/src/share/vm/classfile/systemDictionaryShared.cpp +@@ -659,7 +659,7 @@ bool SystemDictionaryShared::warn_excluded(InstanceKlass* k, const char* reason) + + bool SystemDictionaryShared::is_jfr_event_class(InstanceKlass *k) { + while (k) { +- if (k->name()->equals("jdk/jfr/Event")) { ++ if (k->name()->equals("jdk/jfr/Event") || k->name()->starts_with("jdk/jfr/event")) { + return true; + } + k = k->java_super(); +diff --git a/hotspot/src/share/vm/memory/filemap.cpp b/hotspot/src/share/vm/memory/filemap.cpp +index 3f410647..5fd62a74 100644 +--- a/hotspot/src/share/vm/memory/filemap.cpp ++++ b/hotspot/src/share/vm/memory/filemap.cpp +@@ -263,7 +263,12 @@ void FileMapInfo::allocate_classpath_entry_table() { + + for (int cur_entry = 0 ; cpe != NULL; cpe = cpe->next(), cur_entry++) { + const char *name = cpe->name(); +- int name_bytes = (int)(strlen(name) + 1); ++ int name_bytes; ++ if (cpe->sys_class()) { ++ name_bytes = (int)(strlen(ClassLoader::get_file_name_from_path(name)) + 1); ++ } else { ++ name_bytes = (int)(strlen(name) + 1); ++ } + + if (pass == 0) { + count ++; +@@ -286,7 +291,13 @@ void FileMapInfo::allocate_classpath_entry_table() { + } + + EXCEPTION_MARK; // The following call should never throw, but would exit VM on error. +- SharedClassUtil::update_shared_classpath(cpe, ent, st.st_mtime, st.st_size, THREAD); ++ if (cpe->sys_class()) { ++ // Jdk boot jar not need validate timestamp for we may copy whole jdk. ++ SharedClassUtil::update_shared_classpath(cpe, ent, 0, st.st_size, THREAD); ++ ent->set_sys_class(true); ++ } else { ++ SharedClassUtil::update_shared_classpath(cpe, ent, st.st_mtime, st.st_size, THREAD); ++ } + } else { + ent->_filesize = -1; + if (!os::dir_is_empty(name)) { +@@ -295,7 +306,11 @@ void FileMapInfo::allocate_classpath_entry_table() { + } + ent->_name = strptr; + if (strptr + name_bytes <= strptr_max) { +- strncpy(strptr, name, (size_t)name_bytes); // name_bytes includes trailing 0. ++ if (cpe->sys_class()) { ++ strncpy(strptr, ClassLoader::get_file_name_from_path(name), (size_t)name_bytes); ++ } else { ++ strncpy(strptr, name, (size_t)name_bytes); // name_bytes includes trailing 0. ++ } + strptr += name_bytes; + } else { + assert(0, "miscalculated buffer size"); +@@ -334,6 +349,14 @@ bool FileMapInfo::validate_classpath_entry_table() { + if (TraceClassPaths || (TraceClassLoading && Verbose)) { + tty->print_cr("[Checking shared classpath entry: %s]", name); + } ++ if (ent->_sys_class) { ++ name = ClassLoader::get_boot_class_path(name); ++ if (name == NULL) { ++ fail_continue("Required classpath entry of system class does not exist"); ++ continue; ++ } ++ } ++ + if (os::stat(name, &st) != 0) { + fail_continue("Required classpath entry does not exist: %s", name); + ok = false; +@@ -343,7 +366,7 @@ bool FileMapInfo::validate_classpath_entry_table() { + ok = false; + } + } else { +- if (ent->_timestamp != st.st_mtime || ++ if ((ent->_timestamp != 0 && ent->_timestamp != st.st_mtime) || + ent->_filesize != st.st_size) { + ok = false; + if (PrintSharedArchiveAndExit) { +@@ -640,6 +663,7 @@ void FileMapInfo::write_space(int i, Metaspace* space, bool read_only) { + size_t used = space->used_bytes_slow(Metaspace::NonClassType); + size_t capacity = space->capacity_bytes_slow(Metaspace::NonClassType); + struct FileMapInfo::FileMapHeader::space_info* si = &_header->_space[i]; ++ space->reset_metachunks(); + write_region(i, (char*)space->bottom(), used, capacity, read_only, false); + } + +@@ -967,7 +991,7 @@ bool FileMapInfo::validate_header() { + return DynamicArchive::validate(this); + } + +- if (status) { ++ if (status && !_header->_is_default_jsa) { + if (!ClassLoader::check_shared_paths_misc_info(_paths_misc_info, _header->_paths_misc_info_size)) { + if (!PrintSharedArchiveAndExit) { + fail_continue("shared class paths mismatch (hint: enable -XX:+TraceClassPaths to diagnose the failure)"); +diff --git a/hotspot/src/share/vm/memory/filemap.hpp b/hotspot/src/share/vm/memory/filemap.hpp +index eab9ebcf..36b27f13 100644 +--- a/hotspot/src/share/vm/memory/filemap.hpp ++++ b/hotspot/src/share/vm/memory/filemap.hpp +@@ -52,9 +52,13 @@ public: + const char *_name; + time_t _timestamp; // jar timestamp, 0 if is directory + long _filesize; // jar file size, -1 if is directory ++ bool _sys_class; + bool is_dir() { + return _filesize == -1; + } ++ void set_sys_class(bool isSysClass) { ++ _sys_class = isSysClass; ++ } + }; + + class FileMapInfo : public CHeapObj { +@@ -100,6 +104,7 @@ public: + int _version; // (from enum, above.) + size_t _alignment; // how shared archive should be aligned + int _obj_alignment; // value of ObjectAlignmentInBytes ++ bool _is_default_jsa; // indicates whether is the default jsa file + + struct space_info { + int _crc; // crc checksum of the current space +@@ -264,6 +269,8 @@ public: + bool is_open() { return _file_open; } + bool is_static() const { return _is_static; } + bool is_mapped() const { return _is_mapped; } ++ bool is_default_jsa() const { return _header->_is_default_jsa; } ++ void set_is_default_jsa(bool v) { _header->_is_default_jsa = v; } + void set_is_mapped(bool v) { _is_mapped = v; } + ReservedSpace reserve_shared_memory(); + void set_requested_base(char* b) { dynamic_header()->set_requested_base(b); } +diff --git a/hotspot/src/share/vm/memory/metachunk.hpp b/hotspot/src/share/vm/memory/metachunk.hpp +index e873dc6a..7889b622 100644 +--- a/hotspot/src/share/vm/memory/metachunk.hpp ++++ b/hotspot/src/share/vm/memory/metachunk.hpp +@@ -126,6 +126,8 @@ class Metachunk : public Metabase { + + VirtualSpaceNode* container() const { return _container; } + ++ void reset_container() { _container = NULL; } ++ + MetaWord* bottom() const { return (MetaWord*) this; } + + // Reset top to bottom so chunk can be reused. +diff --git a/hotspot/src/share/vm/memory/metaspace.cpp b/hotspot/src/share/vm/memory/metaspace.cpp +index 7e95b5c0..6c4654b2 100644 +--- a/hotspot/src/share/vm/memory/metaspace.cpp ++++ b/hotspot/src/share/vm/memory/metaspace.cpp +@@ -775,6 +775,7 @@ class SpaceManager : public CHeapObj { + // Notify memory usage to MemoryService. + void track_metaspace_memory_usage(); + ++ void reset_metachunks(); + // debugging support. + + void dump(outputStream* const out) const; +@@ -1923,6 +1924,15 @@ void ChunkManager::print_on(outputStream* out) const { + + // SpaceManager methods + ++void SpaceManager::reset_metachunks() { ++ for (ChunkIndex i = ZeroIndex; i <= HumongousIndex; i = next_chunk_index(i)) { ++ Metachunk* chunks = chunks_in_use(i); ++ if (chunks != NULL) { ++ chunks->reset_container(); ++ } ++ } ++} ++ + size_t SpaceManager::adjust_initial_chunk_size(size_t requested, bool is_class_space) { + size_t chunk_sizes[] = { + specialized_chunk_size(is_class_space), +@@ -3002,6 +3012,10 @@ Metaspace::~Metaspace() { + } + } + ++void Metaspace::reset_metachunks() { ++ vsm()->reset_metachunks(); ++} ++ + VirtualSpaceList* Metaspace::_space_list = NULL; + VirtualSpaceList* Metaspace::_class_space_list = NULL; + +diff --git a/hotspot/src/share/vm/memory/metaspace.hpp b/hotspot/src/share/vm/memory/metaspace.hpp +index 2b06cb62..122dd4bf 100644 +--- a/hotspot/src/share/vm/memory/metaspace.hpp ++++ b/hotspot/src/share/vm/memory/metaspace.hpp +@@ -243,6 +243,8 @@ class Metaspace : public CHeapObj { + MetaWord* expand_and_allocate(size_t size, + MetadataType mdtype); + ++ void reset_metachunks(); ++ + static bool contains(const void* ptr); + + void dump(outputStream* const out) const; +diff --git a/hotspot/src/share/vm/memory/metaspaceShared.cpp b/hotspot/src/share/vm/memory/metaspaceShared.cpp +index 00fb9fe9..b31d0a3f 100644 +--- a/hotspot/src/share/vm/memory/metaspaceShared.cpp ++++ b/hotspot/src/share/vm/memory/metaspaceShared.cpp +@@ -205,6 +205,21 @@ static void patch_klass_vtables(void** vtbl_list, void* new_vtable_start) { + } + } + ++static void patch_deallocate_meta_vtables(void** vtbl_list, void* new_vtable_start, GrowableArray* deallocate_list) { ++ if (deallocate_list == NULL) { ++ return; ++ } ++ for (int i = deallocate_list->length() - 1; i >= 0; i--) { ++ Metadata* m = deallocate_list->at(i); ++ if (!m->on_stack()) { ++ if (m->is_constantPool()) { ++ ((ConstantPool*)m)->remove_unshareable_info(); ++ *(void**)m = find_matching_vtbl_ptr(vtbl_list, new_vtable_start, m); ++ } ++ } ++ } ++} ++ + // Closure for serializing initialization data out to a data area to be + // written to the shared file. + +@@ -591,6 +606,7 @@ void VM_PopulateDumpSharedSpace::doit() { + // Update the vtable pointers in all of the Klass objects in the + // heap. They should point to newly generated vtable. + patch_klass_vtables(vtbl_list, vtable); ++ patch_deallocate_meta_vtables(vtbl_list, vtable, _loader_data->_deallocate_list); + + // dunno what this is for. + char* saved_vtbl = (char*)os::malloc(vtbl_list_size * sizeof(void*), mtClass); +@@ -602,6 +618,9 @@ void VM_PopulateDumpSharedSpace::doit() { + FileMapInfo* mapinfo = new FileMapInfo(); + mapinfo->populate_header(MetaspaceShared::max_alignment()); + ++ if (Arguments::get_is_default_jsa()) { ++ mapinfo->set_is_default_jsa(true); ++ } + // Pass 1 - update file offsets in header. + mapinfo->write_header(); + mapinfo->write_space(MetaspaceShared::ro, _loader_data->ro_metaspace(), true); +@@ -997,6 +1016,8 @@ bool MetaspaceShared::map_shared_spaces(FileMapInfo* mapinfo) { + mapinfo->verify_region_checksum(d_rw) && + (_ro_base = mapinfo->map_region(d_ro)) != NULL && + mapinfo->verify_region_checksum(d_ro) && ++ (_ro_base = mapinfo->map_region(d_md)) != NULL && ++ mapinfo->verify_region_checksum(d_md) && + (image_alignment == (size_t)max_alignment())) { + mapinfo->set_is_mapped(true); + return true; +@@ -1153,6 +1174,7 @@ void MetaspaceShared::initialize_shared_spaces() { + ReadClosure rc(&buffer); + SymbolTable::serialize_shared_table_header(&rc); + SystemDictionaryShared::serialize_dictionary_headers(&rc); ++ ClassLoader::deserialize_package_hash_table(dynamic_mapinfo->region_base(d_md)); + dynamic_mapinfo->close(); + } + +diff --git a/hotspot/src/share/vm/memory/metaspaceShared.hpp b/hotspot/src/share/vm/memory/metaspaceShared.hpp +index a9dadfbb..3eb8b12c 100644 +--- a/hotspot/src/share/vm/memory/metaspaceShared.hpp ++++ b/hotspot/src/share/vm/memory/metaspaceShared.hpp +@@ -90,8 +90,9 @@ class MetaspaceShared : AllStatic { + // core dynamic archive spaces + d_rw = 0, // read-write shared space in the heap + d_ro = 1, // read-only shared space in the heap +- d_bm = 2, // relocation bitmaps (freed after file mapping is finished) +- d_n_regions = 2 // d_rw and d_ro ++ d_md = 2, // miscellaneous data ++ d_bm = 3, // relocation bitmaps (freed after file mapping is finished) ++ d_n_regions = 3 // d_rw, d_ro, d_md + }; + + // Accessor functions to save shared space created for metadata, which has +diff --git a/hotspot/src/share/vm/oops/instanceKlass.cpp b/hotspot/src/share/vm/oops/instanceKlass.cpp +index 0d1b1a8d..9276b895 100644 +--- a/hotspot/src/share/vm/oops/instanceKlass.cpp ++++ b/hotspot/src/share/vm/oops/instanceKlass.cpp +@@ -2633,7 +2633,7 @@ void InstanceKlass::restore_unshareable_info(ClassLoaderData* loader_data, Handl + + // returns true IFF is_in_error_state() has been changed as a result of this call. + bool InstanceKlass::check_sharing_error_state() { +- assert(DumpSharedSpaces, "should only be called during dumping"); ++ assert(DynamicDumpSharedSpaces || DumpSharedSpaces, "should only be called during dumping"); + bool old_state = is_in_error_state(); + + if (!is_in_error_state()) { +@@ -3573,6 +3573,9 @@ void InstanceKlass::verify_on(outputStream* st) { + // Avoid redundant verifies, this really should be in product. + if (_verify_count == Universe::verify_count()) return; + _verify_count = Universe::verify_count(); ++ if (is_in_error_state()) { ++ return; ++ } + #endif + + // Verify Klass +diff --git a/hotspot/src/share/vm/runtime/arguments.cpp b/hotspot/src/share/vm/runtime/arguments.cpp +index 1f603021..5a79ab7e 100644 +--- a/hotspot/src/share/vm/runtime/arguments.cpp ++++ b/hotspot/src/share/vm/runtime/arguments.cpp +@@ -100,6 +100,7 @@ do { \ + } \ + } while(0) + ++bool Arguments::_is_default_jsa = false; + char** Arguments::_jvm_flags_array = NULL; + int Arguments::_num_jvm_flags = 0; + char** Arguments::_jvm_args_array = NULL; +@@ -4041,23 +4042,32 @@ static void force_serial_gc() { + } + #endif // INCLUDE_ALL_GCS + ++char* Arguments::get_default_shared_archive_path() { ++ char *default_archive_path; ++ char jvm_path[JVM_MAXPATHLEN]; ++ os::jvm_path(jvm_path, sizeof(jvm_path)); ++ char *end = strrchr(jvm_path, *os::file_separator()); ++ if (end != NULL) { ++ *end = '\0'; ++ } ++ size_t jvm_path_len = strlen(jvm_path); ++ size_t file_sep_len = strlen(os::file_separator()); ++ const size_t len = jvm_path_len + file_sep_len + 20; ++ default_archive_path = NEW_C_HEAP_ARRAY(char, len, mtInternal); ++ if (default_archive_path != NULL) { ++ jio_snprintf(default_archive_path, len, "%s%sclasses.jsa", ++ jvm_path, os::file_separator()); ++ } ++ Arguments::set_is_default_jsa(true); ++ return default_archive_path; ++} ++ + // Sharing support + // Construct the path to the archive + static char* get_shared_archive_path() { + char *shared_archive_path; + if (SharedArchiveFile == NULL) { +- char jvm_path[JVM_MAXPATHLEN]; +- os::jvm_path(jvm_path, sizeof(jvm_path)); +- char *end = strrchr(jvm_path, *os::file_separator()); +- if (end != NULL) *end = '\0'; +- size_t jvm_path_len = strlen(jvm_path); +- size_t file_sep_len = strlen(os::file_separator()); +- const size_t len = jvm_path_len + file_sep_len + 20; +- shared_archive_path = NEW_C_HEAP_ARRAY(char, len, mtInternal); +- if (shared_archive_path != NULL) { +- jio_snprintf(shared_archive_path, len, "%s%sclasses.jsa", +- jvm_path, os::file_separator()); +- } ++ shared_archive_path = Arguments::get_default_shared_archive_path(); + } else { + shared_archive_path = os::strdup(SharedArchiveFile, mtInternal); + } +diff --git a/hotspot/src/share/vm/runtime/arguments.hpp b/hotspot/src/share/vm/runtime/arguments.hpp +index 19f5cb60..65907eb4 100644 +--- a/hotspot/src/share/vm/runtime/arguments.hpp ++++ b/hotspot/src/share/vm/runtime/arguments.hpp +@@ -240,6 +240,8 @@ class Arguments : AllStatic { + + private: + ++ // Indicates whether the JSA file is the default jsa file. ++ static bool _is_default_jsa; + // an array containing all flags specified in the .hotspotrc file + static char** _jvm_flags_array; + static int _num_jvm_flags; +@@ -487,6 +489,9 @@ class Arguments : AllStatic { + // Return the maximum size a heap with compressed oops can take + static size_t max_heap_for_compressed_oops(); + ++ static void set_is_default_jsa(bool is_default) { _is_default_jsa = is_default; } ++ static bool get_is_default_jsa() { return _is_default_jsa; } ++ + // return a char* array containing all options + static char** jvm_flags_array() { return _jvm_flags_array; } + static char** jvm_args_array() { return _jvm_args_array; } +@@ -622,6 +627,7 @@ class Arguments : AllStatic { + static char* get_ext_dirs() { return _java_ext_dirs->value(); } + static char* get_appclasspath() { return _java_class_path->value(); } + static void fix_appclasspath(); ++ static char* get_default_shared_archive_path(); + + // Operation modi + static Mode mode() { return _mode; } +diff --git a/hotspot/src/share/vm/utilities/hashtable.cpp b/hotspot/src/share/vm/utilities/hashtable.cpp +index 66df8f1f..df290d99 100644 +--- a/hotspot/src/share/vm/utilities/hashtable.cpp ++++ b/hotspot/src/share/vm/utilities/hashtable.cpp +@@ -58,6 +58,7 @@ template BasicHashtableEntry* BasicHashtable::new_entry(unsig + len = 1 << log2_int(len); // round down to power of 2 + assert(len >= _entry_size, ""); + _first_free_entry = NEW_C_HEAP_ARRAY2(char, len, F, CURRENT_PC); ++ memset(_first_free_entry, 0, len); + _end_block = _first_free_entry + len; + } + entry = (BasicHashtableEntry*)_first_free_entry; +diff --git a/hotspot/test/runtime/appcds/TestCommon.java b/hotspot/test/runtime/appcds/TestCommon.java +index 22eef4ed..6a61dc31 100644 +--- a/hotspot/test/runtime/appcds/TestCommon.java ++++ b/hotspot/test/runtime/appcds/TestCommon.java +@@ -54,6 +54,7 @@ public class TestCommon extends CDSTestUtils { + System.getProperty("test.timeout.factor", "1.0"); + + private static String currentArchiveName; ++ private static String topArchiveName; + + // Call this method to start new archive with new unique name + public static void startNewArchiveName() { +@@ -62,6 +63,13 @@ public class TestCommon extends CDSTestUtils { + timeStampFormat.format(new Date()) + ".jsa"; + } + ++ public static String getTopArchiveName() { ++ topArchiveName = System.getProperty("user.dir") + ++ File.separator + "d-appcds-" + timeStampFormat.format(new Date()) + ".jsa"; ++ currentArchiveName = topArchiveName; ++ return topArchiveName; ++ } ++ + // Call this method to get current archive name + public static String getCurrentArchiveName() { + return currentArchiveName; +@@ -90,6 +98,16 @@ public class TestCommon extends CDSTestUtils { + } + } + ++ public static void deletePriorTopArchives() { ++ File dir = new File(System.getProperty("user.dir")); ++ String files[] = dir.list(); ++ for (String name : files) { ++ if (name.startsWith("d-appcds-") && name.endsWith(".jsa")) { ++ if (!(new File(dir, name)).delete()) ++ System.out.println("deletePriorArchives(): delete failed for file " + name); ++ } ++ } ++ } + + // Create AppCDS archive using most common args - convenience method + // Legacy name preserved for compatibility +@@ -132,7 +150,6 @@ public class TestCommon extends CDSTestUtils { + + cmd.add("-Xshare:dump"); + cmd.add("-XX:+UseAppCDS"); +-// cmd.add("-Xlog:cds,cds+hashtables"); comment out because it will be run by jdk1.8 + cmd.add("-XX:ExtraSharedClassListFile=" + classList.getPath()); + + if (opts.archiveName == null) +@@ -147,6 +164,36 @@ public class TestCommon extends CDSTestUtils { + return executeAndLog(pb, "dump"); + } + ++ public static OutputAnalyzer createBaseArchive(String appJar, String appClasses[], String... suffix) ++ throws Exception { ++ return createArchive(appJar, appClasses, suffix); ++ } ++ ++ public static OutputAnalyzer createTopArchive(String appJar, String...suffix) ++ throws Exception { ++ AppCDSOptions opts = new AppCDSOptions(); ++ opts.setAppJar(appJar); ++ opts.addSuffix(suffix); ++ ++ ArrayList cmd = new ArrayList(); ++ cmd.add("-cp"); ++ cmd.add(opts.appJar); ++ ++ String baseArchiveName = getCurrentArchiveName(); ++ deletePriorTopArchives(); ++ String topArchiveNmae = getTopArchiveName(); ++ cmd.add("-XX:+UnlockExperimentalVMOptions"); ++ cmd.add("-Xshare:on"); ++ cmd.add("-XX:SharedArchiveFile=" + baseArchiveName); ++ cmd.add("-XX:ArchiveClassesAtExit=" + topArchiveNmae); ++ cmd.add("-XX:+InfoDynamicCDS"); ++ ++ for (String s : opts.suffix) cmd.add(s); ++ ++ String[] cmdLine = cmd.toArray(new String[cmd.size()]); ++ ProcessBuilder pb = ProcessTools.createJavaProcessBuilder(true, makeCommandLineForAppCDS(cmdLine)); ++ return executeAndLog(pb, "dump"); ++ } + + // Execute JVM using AppCDS archive with specified AppCDSOptions + public static OutputAnalyzer runWithArchive(AppCDSOptions opts) +@@ -156,6 +203,9 @@ public class TestCommon extends CDSTestUtils { + + for (String p : opts.prefix) cmd.add(p); + ++ if (topArchiveName != null) { ++ cmd.add("-XX:+InfoDynamicCDS"); ++ } + cmd.add("-Xshare:" + opts.xShareMode); + cmd.add("-XX:+UseAppCDS"); + cmd.add("-showversion"); +@@ -174,7 +224,6 @@ public class TestCommon extends CDSTestUtils { + return executeAndLog(pb, "exec"); + } + +- + public static OutputAnalyzer execCommon(String... suffix) throws Exception { + AppCDSOptions opts = (new AppCDSOptions()); + opts.addSuffix(suffix); +@@ -261,6 +310,27 @@ public class TestCommon extends CDSTestUtils { + } + + ++ public static OutputAnalyzer testDynamicCDS(String appJar, String appClasses[], String... args) ++ throws Exception { ++ // Create base archive ++ OutputAnalyzer output = createBaseArchive(appJar, appClasses, args); ++ output.shouldContain("Loading classes to share"); ++ output.shouldHaveExitValue(0); ++ ++ // Create top archive ++ output = createTopArchive(appJar, args); ++ output.shouldContain("Written dynamic archive"); ++ output.shouldHaveExitValue(0); ++ ++ // Exec with top archive ++ output = exec(appJar, args); ++ ++ // Check exec result ++ checkMatches(output, "SharedArchivePath", "SharedDynamicArchivePath"); ++ output.shouldHaveExitValue(0); ++ return output; ++ } ++ + public static OutputAnalyzer checkExecReturn(OutputAnalyzer output, int ret, + boolean checkContain, String... matches) throws Exception { + try { +diff --git a/hotspot/test/runtime/appcds/dynamicArchive/DynamicFlag.java b/hotspot/test/runtime/appcds/dynamicArchive/DynamicFlag.java +new file mode 100644 +index 00000000..79f30759 +--- /dev/null ++++ b/hotspot/test/runtime/appcds/dynamicArchive/DynamicFlag.java +@@ -0,0 +1,39 @@ ++/* ++ * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2022, Huawei Technologies Co., Ltd. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ * ++ */ ++ ++/* ++ * @test ++ * @summary The DynamicDumpShareSpaces flag is internal, setting it at the command line should have no effect. ++ * @library /testlibrary /runtime/appcds /runtime/appcds/test-classes ++ * @compile ../test-classes/Hello.java ++ * @run driver DynamicFlag ++ */ ++ ++public class DynamicFlag { ++ public static void main(String[] args) throws Exception { ++ TestCommon.testDynamicCDS(JarBuilder.getOrCreateHelloJar(), ++ TestCommon.list("Hello"), "-XX:+DynamicDumpSharedSpaces", "Hello"); ++ } ++} +diff --git a/hotspot/test/runtime/appcds/dynamicArchive/DynamicHelloTest.java b/hotspot/test/runtime/appcds/dynamicArchive/DynamicHelloTest.java +new file mode 100644 +index 00000000..48e97cb2 +--- /dev/null ++++ b/hotspot/test/runtime/appcds/dynamicArchive/DynamicHelloTest.java +@@ -0,0 +1,42 @@ ++/* ++ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2022, Huawei Technologies Co., Ltd. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ * ++ */ ++ ++/* ++ * @test ++ * @summary Hello World test for dynamic cds ++ * @library /testlibrary /runtime/appcds /runtime/appcds/test-classes ++ * @compile ../test-classes/Hello.java ++ * @run main DynamicHelloTest ++ */ ++ ++public class DynamicHelloTest { ++ public static void main(String[] args) throws Exception { ++ TestCommon.testDynamicCDS(JarBuilder.getOrCreateHelloJar(), ++ null, "Hello"); ++ ++ TestCommon.testDynamicCDS(JarBuilder.getOrCreateHelloJar(), ++ TestCommon.list("Hello"), "Hello"); ++ } ++} +diff --git a/hotspot/test/runtime/appcds/dynamicArchive/VerifyWithDynamicArchive.java b/hotspot/test/runtime/appcds/dynamicArchive/VerifyWithDynamicArchive.java +new file mode 100644 +index 00000000..eacc1aff +--- /dev/null ++++ b/hotspot/test/runtime/appcds/dynamicArchive/VerifyWithDynamicArchive.java +@@ -0,0 +1,42 @@ ++/* ++ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2022, Huawei Technologies Co., Ltd. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ * ++ */ ++ ++/* ++ * @test ++ * @summary Hello World test for dynamic cds ++ * @library /testlibrary /runtime/appcds /runtime/appcds/test-classes ++ * @compile ../test-classes/Hello.java ++ * @run main VerifyWithDynamicArchive ++ */ ++ ++public class VerifyWithDynamicArchive { ++ public static void main(String[] args) throws Exception { ++ TestCommon.testDynamicCDS(JarBuilder.getOrCreateHelloJar(), ++ null, "-XX:+VerifySharedSpaces", "Hello"); ++ ++ TestCommon.testDynamicCDS(JarBuilder.getOrCreateHelloJar(), ++ TestCommon.list("Hello"), "-XX:+VerifySharedSpaces", "Hello"); ++ } ++} +diff --git a/jdk/make/BuildJdk.gmk b/jdk/make/BuildJdk.gmk +index 467792fa..bb8ea8a9 100644 +--- a/jdk/make/BuildJdk.gmk ++++ b/jdk/make/BuildJdk.gmk +@@ -103,6 +103,13 @@ images: + ifeq ($(OPENJDK_TARGET_OS), macosx) + +$(MAKE) -f Bundles.gmk + endif ++ ifeq ($(BUILD_CDS_ARCHIVE), true) ++ echo Creating CDS archive for jdk image ++ $(JDK_IMAGE_DIR)/bin/java -Xshare:dump -Xmx128M -Xms128M -XX:ParallelGCThreads=1 -Xint $(LOG_INFO) ++ echo Creating CDS archive for jre image ++ $(JRE_IMAGE_DIR)/bin/java -Xshare:dump -Xmx128M -Xms128M -XX:ParallelGCThreads=1 -Xint $(LOG_INFO) ++ endif ++ + + overlay-images: + +$(MAKE) -f CompileLaunchers.gmk OVERLAY_IMAGES=true diff --git a/8203682-Add-jcmd-VM.classloaders-command-to-print-ou.patch b/8203682-Add-jcmd-VM.classloaders-command-to-print-ou.patch new file mode 100644 index 0000000000000000000000000000000000000000..d8778fab9ffc068853b70b211d2e9ef4af12b500 --- /dev/null +++ b/8203682-Add-jcmd-VM.classloaders-command-to-print-ou.patch @@ -0,0 +1,811 @@ +From 953fdbbfbc6512c3f04f3663fa5ad216d7547984 Mon Sep 17 00:00:00 2001 +From: eapen +Date: Thu, 15 Dec 2022 09:48:37 +0800 +Subject: [PATCH 17/33] I68TO2: 8203682: Add jcmd "VM.classloaders" command to print + out class loader hierarchy, details +--- + .../vm/classfile/classLoaderHierarchyDCmd.cpp | 468 +++++++++++++++++++++ + .../vm/classfile/classLoaderHierarchyDCmd.hpp | 59 +++ + hotspot/src/share/vm/runtime/vm_operations.hpp | 1 + + .../src/share/vm/services/diagnosticCommand.cpp | 2 + + .../dcmd/ClassLoaderHierarchyTest.java | 213 ++++++++++ + 5 files changed, 743 insertions(+) + create mode 100644 hotspot/src/share/vm/classfile/classLoaderHierarchyDCmd.cpp + create mode 100644 hotspot/src/share/vm/classfile/classLoaderHierarchyDCmd.hpp + create mode 100644 hotspot/test/serviceability/dcmd/ClassLoaderHierarchyTest.java + +diff --git a/hotspot/src/share/vm/classfile/classLoaderHierarchyDCmd.cpp b/hotspot/src/share/vm/classfile/classLoaderHierarchyDCmd.cpp +new file mode 100644 +index 0000000..4c25091 +--- /dev/null ++++ b/hotspot/src/share/vm/classfile/classLoaderHierarchyDCmd.cpp +@@ -0,0 +1,468 @@ ++/* ++ * Copyright (c) 2021, Huawei Technologies Co., Ltd. All rights reserved. ++ * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2018 SAP SE. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ * ++ */ ++ ++#include "precompiled.hpp" ++ ++#include "classfile/classLoaderData.inline.hpp" ++#include "classfile/classLoaderHierarchyDCmd.hpp" ++#include "memory/allocation.hpp" ++#include "memory/resourceArea.hpp" ++#include "runtime/safepoint.hpp" ++#include "utilities/globalDefinitions.hpp" ++#include "utilities/ostream.hpp" ++ ++ ++ClassLoaderHierarchyDCmd::ClassLoaderHierarchyDCmd(outputStream* output, bool heap) ++ : DCmdWithParser(output, heap) ++ , _show_classes("show-classes", "Print loaded classes.", "BOOLEAN", false, "false") ++ , _verbose("verbose", "Print detailed information.", "BOOLEAN", false, "false") { ++ _dcmdparser.add_dcmd_option(&_show_classes); ++ _dcmdparser.add_dcmd_option(&_verbose); ++} ++ ++ ++int ClassLoaderHierarchyDCmd::num_arguments() { ++ ResourceMark rm; ++ ClassLoaderHierarchyDCmd* dcmd = new ClassLoaderHierarchyDCmd(NULL, false); ++ if (dcmd != NULL) { ++ DCmdMark mark(dcmd); ++ return dcmd->_dcmdparser.num_arguments(); ++ } else { ++ return 0; ++ } ++} ++ ++// Helper class for drawing the branches to the left of a node. ++class BranchTracker : public StackObj { ++ // "" ++ // " |---" ++ // " | | ++ // " | " ++ // " | |--- ++ // " | |--- ++ // ^^^^^^^ ^^^ ++ // A B ++ ++ // Some terms for the graphics: ++ // - branch: vertical connection between a node's ancestor to a later sibling. ++ // - branchwork: (A) the string to print as a prefix at the start of each line, contains all branches. ++ // - twig (B): Length of the dashed line connecting a node to its branch. ++ // - branch spacing: how many spaces between branches are printed. ++ ++public: ++ ++ enum { max_depth = 64, twig_len = 2, branch_spacing = 5 }; ++ ++private: ++ ++ char _branches[max_depth]; ++ int _pos; ++ ++public: ++ BranchTracker() ++ : _pos(0) {} ++ ++ void push(bool has_branch) { ++ if (_pos < max_depth) { ++ _branches[_pos] = has_branch ? '|' : ' '; ++ } ++ _pos ++; // beyond max depth, omit branch drawing but do count on. ++ } ++ ++ void pop() { ++ assert(_pos > 0, "must be"); ++ _pos --; ++ } ++ ++ void print(outputStream* st) { ++ for (int i = 0; i < _pos; i ++) { ++ st->print("%c%.*s", _branches[i], branch_spacing, " "); ++ } ++ } ++ ++ class Mark { ++ BranchTracker& _tr; ++ public: ++ Mark(BranchTracker& tr, bool has_branch_here) ++ : _tr(tr) { _tr.push(has_branch_here); } ++ ~Mark() { _tr.pop(); } ++ }; ++ ++}; // end: BranchTracker ++ ++struct LoadedClassInfo : public ResourceObj { ++public: ++ LoadedClassInfo* _next; ++ Klass* const _klass; ++ const ClassLoaderData* const _cld; ++ ++ LoadedClassInfo(Klass* klass, const ClassLoaderData* cld) ++ : _next(NULL), _klass(klass), _cld(cld) {} ++ ++}; ++ ++class LoaderTreeNode : public ResourceObj { ++ ++ // We walk the CLDG and, for each CLD which is non-anonymous, add ++ // a tree node. To add a node we need its parent node; if it itself ++ // does not exist yet, we add a preliminary node for it. This preliminary ++ // node just contains its loader oop; later, when encountering its CLD in ++ // our CLDG walk, we complete the missing information in this node. ++ ++ const oop _loader_oop; ++ const ClassLoaderData* _cld; // May be NULL if loader never loaded anything ++ ++ LoaderTreeNode* _child; ++ LoaderTreeNode* _next; ++ ++ LoadedClassInfo* _classes; ++ int _num_classes; ++ ++ LoadedClassInfo* _anon_classes; ++ int _num_anon_classes; ++ ++ // Returns Klass of loader; NULL for bootstrap loader ++ const Klass* loader_klass() const { ++ return (_loader_oop != NULL) ? _loader_oop->klass() : NULL; ++ } ++ ++ // Returns ResourceArea-allocated class name of loader class; "" if there is no klass (bootstrap loader) ++ const char* loader_class_name() const { ++ const Klass* klass = loader_klass(); ++ return klass != NULL ? klass->external_name() : ""; ++ } ++ ++ bool is_bootstrap() const { ++ if (_loader_oop == NULL) { ++ assert(_cld != NULL && _cld->is_the_null_class_loader_data(), "bootstrap loader must have CLD"); ++ return true; ++ } ++ return false; ++ } ++ ++ void print_with_child_nodes(outputStream* st, BranchTracker& branchtracker, ++ bool print_classes, bool verbose) const { ++ ++ assert(SafepointSynchronize::is_at_safepoint(), "invariant"); ++ ++ ResourceMark rm; ++ ++ // Retrieve information. ++ const Klass* const the_loader_klass = loader_klass(); ++ const char* const the_loader_class_name = loader_class_name(); ++ // ClassLoader.java does not contain 'name' field. Replace it with loader_class_name(). ++ const char* const the_loader_name = the_loader_class_name; ++ ++ branchtracker.print(st); ++ ++ // e.g. "+--- jdk.internal.reflect.DelegatingClassLoader" ++ st->print("+%.*s", BranchTracker::twig_len, "----------"); ++ if (is_bootstrap()) { ++ st->print(" "); ++ } else { ++ if (the_loader_name[0] != '\0') { ++ st->print(" \"%s\",", the_loader_name); ++ } ++ st->print(" %s", the_loader_class_name); ++ st->print(" {" PTR_FORMAT "}", p2i(_loader_oop)); ++ } ++ st->cr(); ++ ++ // Output following this node (node details and child nodes) - up to the next sibling node ++ // needs to be prefixed with "|" if there is a follow up sibling. ++ const bool have_sibling = _next != NULL; ++ BranchTracker::Mark trm(branchtracker, have_sibling); ++ ++ { ++ // optional node details following this node needs to be prefixed with "|" ++ // if there are follow up child nodes. ++ const bool have_child = _child != NULL; ++ BranchTracker::Mark trm(branchtracker, have_child); ++ ++ // Empty line ++ branchtracker.print(st); ++ st->cr(); ++ ++ const int indentation = 18; ++ ++ if (verbose) { ++ branchtracker.print(st); ++ st->print_cr("%*s " PTR_FORMAT, indentation, "Loader Data:", p2i(_cld)); ++ branchtracker.print(st); ++ st->print_cr("%*s " PTR_FORMAT, indentation, "Loader Klass:", p2i(the_loader_klass)); ++ ++ // Empty line ++ branchtracker.print(st); ++ st->cr(); ++ } ++ ++ if (print_classes) { ++ ++ if (_classes != NULL) { ++ assert(_cld != NULL, "we have classes, we should have a CLD"); ++ for (LoadedClassInfo* lci = _classes; lci; lci = lci->_next) { ++ branchtracker.print(st); ++ if (lci == _classes) { // first iteration ++ st->print("%*s ", indentation, "Classes:"); ++ } else { ++ st->print("%*s ", indentation, ""); ++ } ++ st->print("%s", lci->_klass->external_name()); ++ st->cr(); ++ // Non-anonymous classes should live in the primary CLD of its loader ++ assert(lci->_cld == _cld, "must be"); ++ } ++ branchtracker.print(st); ++ st->print("%*s ", indentation, ""); ++ st->print_cr("(%u class%s)", _num_classes, (_num_classes == 1) ? "" : "es"); ++ ++ // Empty line ++ branchtracker.print(st); ++ st->cr(); ++ } ++ ++ if (_anon_classes != NULL) { ++ assert(_cld != NULL, "we have classes, we should have a CLD"); ++ for (LoadedClassInfo* lci = _anon_classes; lci; lci = lci->_next) { ++ branchtracker.print(st); ++ if (lci == _anon_classes) { // first iteration ++ st->print("%*s ", indentation, "Anonymous Classes:"); ++ } else { ++ st->print("%*s ", indentation, ""); ++ } ++ st->print("%s", lci->_klass->external_name()); ++ // For anonymous classes, also print CLD if verbose. Should be a different one than the primary CLD. ++ assert(lci->_cld != _cld, "must be"); ++ if (verbose) { ++ st->print(" (CLD: " PTR_FORMAT ")", p2i(lci->_cld)); ++ } ++ st->cr(); ++ } ++ branchtracker.print(st); ++ st->print("%*s ", indentation, ""); ++ st->print_cr("(%u anonymous class%s)", _num_anon_classes, (_num_anon_classes == 1) ? "" : "es"); ++ ++ // Empty line ++ branchtracker.print(st); ++ st->cr(); ++ } ++ ++ } // end: print_classes ++ ++ } // Pop branchtracker mark ++ ++ // Print children, recursively ++ LoaderTreeNode* c = _child; ++ while (c != NULL) { ++ c->print_with_child_nodes(st, branchtracker, print_classes, verbose); ++ c = c->_next; ++ } ++ ++ } ++ ++public: ++ ++ LoaderTreeNode(const oop loader_oop) ++ : _loader_oop(loader_oop), _cld(NULL) ++ , _child(NULL), _next(NULL) ++ , _classes(NULL), _anon_classes(NULL) ++ , _num_classes(0), _num_anon_classes(0) {} ++ ++ void set_cld(const ClassLoaderData* cld) { ++ assert(_cld == NULL, "there should be only one primary CLD per loader"); ++ _cld = cld; ++ } ++ ++ void add_child(LoaderTreeNode* info) { ++ info->_next = _child; ++ _child = info; ++ } ++ ++ void add_sibling(LoaderTreeNode* info) { ++ assert(info->_next == NULL, "must be"); ++ info->_next = _next; ++ _next = info; ++ } ++ ++ void add_classes(LoadedClassInfo* first_class, int num_classes, bool anonymous) { ++ LoadedClassInfo** p_list_to_add_to = anonymous ? &_anon_classes : &_classes; ++ // Search tail. ++ while ((*p_list_to_add_to) != NULL) { ++ p_list_to_add_to = &(*p_list_to_add_to)->_next; ++ } ++ *p_list_to_add_to = first_class; ++ if (anonymous) { ++ _num_anon_classes += num_classes; ++ } else { ++ _num_classes += num_classes; ++ } ++ } ++ ++ LoaderTreeNode* find(const oop loader_oop) { ++ LoaderTreeNode* result = NULL; ++ if (_loader_oop == loader_oop) { ++ result = this; ++ } else { ++ LoaderTreeNode* c = _child; ++ while (c != NULL && result == NULL) { ++ result = c->find(loader_oop); ++ c = c->_next; ++ } ++ } ++ return result; ++ } ++ ++ void print_with_child_nodes(outputStream* st, bool print_classes, bool print_add_info) const { ++ BranchTracker bwt; ++ print_with_child_nodes(st, bwt, print_classes, print_add_info); ++ } ++ ++}; ++ ++class LoadedClassCollectClosure : public KlassClosure { ++public: ++ LoadedClassInfo* _list; ++ const ClassLoaderData* _cld; ++ int _num_classes; ++ LoadedClassCollectClosure(const ClassLoaderData* cld) ++ : _list(NULL), _cld(cld), _num_classes(0) {} ++ void do_klass(Klass* k) { ++ LoadedClassInfo* lki = new LoadedClassInfo(k, _cld); ++ lki->_next = _list; ++ _list = lki; ++ _num_classes ++; ++ } ++}; ++ ++class LoaderInfoScanClosure : public CLDClosure { ++ ++ const bool _print_classes; ++ const bool _verbose; ++ LoaderTreeNode* _root; ++ ++ static void fill_in_classes(LoaderTreeNode* info, const ClassLoaderData* cld) { ++ assert(info != NULL && cld != NULL, "must be"); ++ LoadedClassCollectClosure lccc(cld); ++ const_cast(cld)->classes_do(&lccc); ++ if (lccc._num_classes > 0) { ++ info->add_classes(lccc._list, lccc._num_classes, cld->is_anonymous()); ++ } ++ } ++ ++ LoaderTreeNode* find_node_or_add_empty_node(oop loader_oop) { ++ ++ assert(_root != NULL, "root node must exist"); ++ ++ if (loader_oop == NULL) { ++ return _root; ++ } ++ ++ // Check if a node for this oop already exists. ++ LoaderTreeNode* info = _root->find(loader_oop); ++ ++ if (info == NULL) { ++ // It does not. Create a node. ++ info = new LoaderTreeNode(loader_oop); ++ ++ // Add it to tree. ++ LoaderTreeNode* parent_info = NULL; ++ ++ // Recursively add parent nodes if needed. ++ const oop parent_oop = java_lang_ClassLoader::parent(loader_oop); ++ if (parent_oop == NULL) { ++ parent_info = _root; ++ } else { ++ parent_info = find_node_or_add_empty_node(parent_oop); ++ } ++ assert(parent_info != NULL, "must be"); ++ ++ parent_info->add_child(info); ++ } ++ return info; ++ } ++ ++ ++public: ++ LoaderInfoScanClosure(bool print_classes, bool verbose) ++ : _print_classes(print_classes), _verbose(verbose), _root(NULL) { ++ _root = new LoaderTreeNode(NULL); ++ } ++ ++ void print_results(outputStream* st) const { ++ _root->print_with_child_nodes(st, _print_classes, _verbose); ++ } ++ ++ void do_cld (ClassLoaderData* cld) { ++ ++ // We do not display unloading loaders, for now. ++ if (cld->is_unloading()) { ++ return; ++ } ++ ++ const oop loader_oop = cld->class_loader(); ++ ++ LoaderTreeNode* info = find_node_or_add_empty_node(loader_oop); ++ assert(info != NULL, "must be"); ++ ++ // Update CLD in node, but only if this is the primary CLD for this loader. ++ if (cld->is_anonymous() == false) { ++ info->set_cld(cld); ++ } ++ ++ // Add classes. ++ fill_in_classes(info, cld); ++ } ++ ++}; ++ ++ ++class ClassLoaderHierarchyVMOperation : public VM_Operation { ++ outputStream* const _out; ++ const bool _show_classes; ++ const bool _verbose; ++public: ++ ClassLoaderHierarchyVMOperation(outputStream* out, bool show_classes, bool verbose) : ++ _out(out), _show_classes(show_classes), _verbose(verbose) ++ {} ++ ++ VMOp_Type type() const { ++ return VMOp_ClassLoaderHierarchyOperation; ++ } ++ ++ void doit() { ++ assert(SafepointSynchronize::is_at_safepoint(), "must be a safepoint"); ++ ResourceMark rm; ++ LoaderInfoScanClosure cl (_show_classes, _verbose); ++ ClassLoaderDataGraph::cld_do(&cl); ++ cl.print_results(_out); ++ } ++}; ++ ++// This command needs to be executed at a safepoint. ++void ClassLoaderHierarchyDCmd::execute(DCmdSource source, TRAPS) { ++ ClassLoaderHierarchyVMOperation op(output(), _show_classes.value(), _verbose.value()); ++ VMThread::execute(&op); ++} +\ No newline at end of file +diff --git a/hotspot/src/share/vm/classfile/classLoaderHierarchyDCmd.hpp b/hotspot/src/share/vm/classfile/classLoaderHierarchyDCmd.hpp +new file mode 100644 +index 0000000..49027e6 +--- /dev/null ++++ b/hotspot/src/share/vm/classfile/classLoaderHierarchyDCmd.hpp +@@ -0,0 +1,59 @@ ++/* ++ * Copyright (c) 2021, Huawei Technologies Co., Ltd. All rights reserved. ++ * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2018 SAP SE. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ * ++ */ ++ ++#ifndef HOTSPOT_SHARE_CLASSFILE_CLASSLOADERHIERARCHYDCMD_HPP_ ++#define HOTSPOT_SHARE_CLASSFILE_CLASSLOADERHIERARCHYDCMD_HPP_ ++ ++#include "services/diagnosticCommand.hpp" ++ ++class ClassLoaderHierarchyDCmd: public DCmdWithParser { ++ DCmdArgument _show_classes; ++ DCmdArgument _verbose; ++public: ++ ++ ClassLoaderHierarchyDCmd(outputStream* output, bool heap); ++ ++ static const char* name() { ++ return "VM.classloaders"; ++ } ++ ++ static const char* description() { ++ return "Prints classloader hierarchy."; ++ } ++ static const char* impact() { ++ return "Medium: Depends on number of class loaders and classes loaded."; ++ } ++ static const JavaPermission permission() { ++ JavaPermission p = {"java.lang.management.ManagementPermission", ++ "monitor", NULL}; ++ return p; ++ } ++ static int num_arguments(); ++ virtual void execute(DCmdSource source, TRAPS); ++ ++}; ++ ++#endif /* HOTSPOT_SHARE_CLASSFILE_CLASSLOADERHIERARCHYDCMD_HPP_ */ +\ No newline at end of file +diff --git a/hotspot/src/share/vm/runtime/vm_operations.hpp b/hotspot/src/share/vm/runtime/vm_operations.hpp +index a8ba78b..3744040 100644 +--- a/hotspot/src/share/vm/runtime/vm_operations.hpp ++++ b/hotspot/src/share/vm/runtime/vm_operations.hpp +@@ -98,6 +98,7 @@ + template(RotateGCLog) \ + template(WhiteBoxOperation) \ + template(ClassLoaderStatsOperation) \ ++ template(ClassLoaderHierarchyOperation) \ + template(JFROldObject) \ + template(PrintClasses) \ + +diff --git a/hotspot/src/share/vm/services/diagnosticCommand.cpp b/hotspot/src/share/vm/services/diagnosticCommand.cpp +index e4e6185..d3b91d9 100644 +--- a/hotspot/src/share/vm/services/diagnosticCommand.cpp ++++ b/hotspot/src/share/vm/services/diagnosticCommand.cpp +@@ -24,6 +24,7 @@ + + #include "precompiled.hpp" + #include "cds/dynamicArchive.hpp" ++#include "classfile/classLoaderHierarchyDCmd.hpp" + #include "classfile/classLoaderStats.hpp" + #include "gc_implementation/shared/vmGCOperations.hpp" + #include "runtime/javaCalls.hpp" +@@ -70,6 +71,7 @@ void DCmdRegistrant::register_dcmds(){ + DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl(full_export, true, false)); + DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl(full_export, true, false)); + DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl(full_export, true, false)); ++ DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl(full_export, true, false)); + #ifdef LINUX + DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl(full_export, true, false)); + #endif // LINUX +diff --git a/hotspot/test/serviceability/dcmd/ClassLoaderHierarchyTest.java b/hotspot/test/serviceability/dcmd/ClassLoaderHierarchyTest.java +new file mode 100644 +index 0000000..378997d +--- /dev/null ++++ b/hotspot/test/serviceability/dcmd/ClassLoaderHierarchyTest.java +@@ -0,0 +1,213 @@ ++/* ++ * Copyright (c) 2021, Huawei Technologies Co., Ltd. All rights reserved. ++ * Copyright (c) 2014, 2018, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2018, SAP SE. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ */ ++ ++/* ++ * @test ++ * @summary Test of diagnostic command VM.classloaders ++ * @library /testlibrary ++ * @modules java.base/jdk.internal.misc ++ * java.compiler ++ * java.management ++ * jdk.internal.jvmstat/sun.jvmstat.monitor ++ * @run testng ClassLoaderHierarchyTest ++ */ ++ ++import org.testng.Assert; ++import org.testng.annotations.Test; ++ ++import com.oracle.java.testlibrary.OutputAnalyzer; ++import com.oracle.java.testlibrary.CommandExecutor; ++import com.oracle.java.testlibrary.JMXExecutor; ++ ++import java.io.File; ++import java.io.FileInputStream; ++import java.io.IOException; ++import java.nio.ByteBuffer; ++import java.nio.channels.FileChannel; ++ ++public class ClassLoaderHierarchyTest { ++ ++ class EmptyDelegatingLoader extends ClassLoader { ++ EmptyDelegatingLoader(ClassLoader parent) { ++ super(parent); ++ } ++ } ++ ++ static void loadTestClassInLoaderAndCheck(String classname, ClassLoader loader) throws ClassNotFoundException { ++ Class c = Class.forName(classname, true, loader); ++ if (c.getClassLoader() != loader) { ++ Assert.fail(classname + " defined by wrong classloader: " + c.getClassLoader()); ++ } ++ } ++ ++//+-- ++// | ++// +-- "sun.misc.Launcher$ExtClassLoader", sun.misc.Launcher$ExtClassLoader ++// | | ++// | +-- "sun.misc.Launcher$AppClassLoader", sun.misc.Launcher$AppClassLoader ++// | ++// +-- "sun.reflect.DelegatingClassLoader", sun.reflect.DelegatingClassLoader ++// | ++// +-- "ClassLoaderHierarchyTest$TestClassLoader", ClassLoaderHierarchyTest$TestClassLoader ++// | | ++// | +-- "ClassLoaderHierarchyTest$TestClassLoader", ClassLoaderHierarchyTest$TestClassLoader ++// | ++// +-- "ClassLoaderHierarchyTest$EmptyDelegatingLoader", ClassLoaderHierarchyTest$EmptyDelegatingLoader ++// | | ++// | +-- "ClassLoaderHierarchyTest$EmptyDelegatingLoader", ClassLoaderHierarchyTest$EmptyDelegatingLoader ++// | | ++// | +-- "ClassLoaderHierarchyTest$TestClassLoader", ClassLoaderHierarchyTest$TestClassLoader ++// | ++// +-- "ClassLoaderHierarchyTest$EmptyDelegatingLoader", ClassLoaderHierarchyTest$EmptyDelegatingLoader ++// | ++// +-- "ClassLoaderHierarchyTest$EmptyDelegatingLoader", ClassLoaderHierarchyTest$EmptyDelegatingLoader ++// | ++// +-- "ClassLoaderHierarchyTest$TestClassLoader", ClassLoaderHierarchyTest$TestClassLoader ++// | ++// +-- "ClassLoaderHierarchyTest$TestClassLoader", ClassLoaderHierarchyTest$TestClassLoader ++// | ++// +-- "ClassLoaderHierarchyTest$TestClassLoader", ClassLoaderHierarchyTest$TestClassLoader ++ ++ ++ public void run(CommandExecutor executor) throws ClassNotFoundException { ++ ++ // A) one unnamed, two named loaders ++ ClassLoader unnamed_cl = new TestClassLoader(null); ++ ClassLoader named_child_cl = new TestClassLoader(unnamed_cl); ++ loadTestClassInLoaderAndCheck("TestClass2", unnamed_cl); ++ loadTestClassInLoaderAndCheck("TestClass2", named_child_cl); ++ ++ // B) A named CL with empty loaders as parents (JDK-8293156) ++ EmptyDelegatingLoader emptyLoader1 = new EmptyDelegatingLoader( null); ++ EmptyDelegatingLoader emptyLoader2 = new EmptyDelegatingLoader(emptyLoader1); ++ ClassLoader named_child_2_cl = new TestClassLoader(emptyLoader2); ++ loadTestClassInLoaderAndCheck("TestClass2", named_child_2_cl); ++ ++ // C) Test output for several *unnamed* class loaders, same class, same parents, ++ // and all these should be folded by default. ++ EmptyDelegatingLoader emptyLoader3 = new EmptyDelegatingLoader(null); ++ EmptyDelegatingLoader emptyLoader4 = new EmptyDelegatingLoader(emptyLoader3); ++ ClassLoader named_child_3_cl = new TestClassLoader(emptyLoader4); // Same names ++ ClassLoader named_child_4_cl = new TestClassLoader(emptyLoader4); ++ ClassLoader named_child_5_cl = new TestClassLoader(emptyLoader4); ++ loadTestClassInLoaderAndCheck("TestClass2", named_child_3_cl); ++ loadTestClassInLoaderAndCheck("TestClass2", named_child_4_cl); ++ loadTestClassInLoaderAndCheck("TestClass2", named_child_5_cl); ++ ++ // First test: simple output, no classes displayed ++ OutputAnalyzer output = executor.execute("VM.classloaders"); ++ // (A) ++ output.shouldContain("+-- "); ++ output.shouldContain(" +-- \"sun.misc.Launcher$ExtClassLoader\", sun.misc.Launcher$ExtClassLoader"); ++ output.shouldContain(" | +-- \"sun.misc.Launcher$AppClassLoader\", sun.misc.Launcher$AppClassLoader"); ++ output.shouldContain(" +-- \"sun.reflect.DelegatingClassLoader\", sun.reflect.DelegatingClassLoader"); ++ output.shouldContain(" +-- \"ClassLoaderHierarchyTest$TestClassLoader\", ClassLoaderHierarchyTest$TestClassLoader"); ++ output.shouldContain(" | +-- \"ClassLoaderHierarchyTest$TestClassLoader\", ClassLoaderHierarchyTest$TestClassLoader"); ++ // (B) ++ output.shouldContain(" +-- \"ClassLoaderHierarchyTest$EmptyDelegatingLoader\", ClassLoaderHierarchyTest$EmptyDelegatingLoader"); ++ output.shouldContain(" | +-- \"ClassLoaderHierarchyTest$EmptyDelegatingLoader\", ClassLoaderHierarchyTest$EmptyDelegatingLoader"); ++ output.shouldContain(" | +-- \"ClassLoaderHierarchyTest$TestClassLoader\", ClassLoaderHierarchyTest$TestClassLoader"); ++ // (C) ++ output.shouldContain(" +-- \"ClassLoaderHierarchyTest$EmptyDelegatingLoader\", ClassLoaderHierarchyTest$EmptyDelegatingLoader"); ++ output.shouldContain(" +-- \"ClassLoaderHierarchyTest$EmptyDelegatingLoader\", ClassLoaderHierarchyTest$EmptyDelegatingLoader"); ++ output.shouldContain(" +-- \"ClassLoaderHierarchyTest$TestClassLoader\", ClassLoaderHierarchyTest$TestClassLoader"); ++ ++ // Second test: print with classes. ++ output = executor.execute("VM.classloaders show-classes"); ++ output.shouldContain(""); ++ output.shouldContain("java.lang.Object"); ++ output.shouldContain("java.lang.Enum"); ++ output.shouldContain("java.lang.NullPointerException"); ++ output.shouldContain("TestClass2"); ++ } ++ ++ static class TestClassLoader extends ClassLoader { ++ ++ public TestClassLoader() { ++ super(); ++ } ++ ++ public TestClassLoader(ClassLoader parent) { ++ super(parent); ++ } ++ ++ public static final String CLASS_NAME = "TestClass2"; ++ ++ static ByteBuffer readClassFile(String name) ++ { ++ File f = new File(System.getProperty("test.classes", "."), ++ name); ++ try (FileInputStream fin = new FileInputStream(f); ++ FileChannel fc = fin.getChannel()) ++ { ++ return fc.map(FileChannel.MapMode.READ_ONLY, 0, fc.size()); ++ } catch (IOException e) { ++ Assert.fail("Can't open file: " + name, e); ++ } ++ ++ /* Will not reach here as Assert.fail() throws exception */ ++ return null; ++ } ++ ++ protected Class loadClass(String name, boolean resolve) ++ throws ClassNotFoundException ++ { ++ Class c; ++ if (!CLASS_NAME.equals(name)) { ++ c = super.loadClass(name, resolve); ++ } else { ++ // should not delegate to the system class loader ++ c = findClass(name); ++ if (resolve) { ++ resolveClass(c); ++ } ++ } ++ return c; ++ } ++ ++ protected Class findClass(String name) ++ throws ClassNotFoundException ++ { ++ if (!CLASS_NAME.equals(name)) { ++ throw new ClassNotFoundException("Unexpected class: " + name); ++ } ++ return defineClass(name, readClassFile(name + ".class"), null); ++ } ++ ++ } ++ ++ @Test ++ public void jmx() throws ClassNotFoundException { ++ run(new JMXExecutor()); ++ } ++ ++} ++ ++class TestClass2 { ++ static { ++ Runnable r = () -> System.out.println("Hello"); ++ r.run(); ++ } ++} +\ No newline at end of file +-- +1.8.3.1 diff --git a/8204595-add-more-thread-related-system-settings-info.patch b/8204595-add-more-thread-related-system-settings-info.patch new file mode 100644 index 0000000000000000000000000000000000000000..47dc73d4692ad032002e9d96d7bcbed6a4e558df --- /dev/null +++ b/8204595-add-more-thread-related-system-settings-info.patch @@ -0,0 +1,69 @@ +From 16caa051cb7299312cdaf9d79eaef01d294474f6 Mon Sep 17 00:00:00 2001 +From: eapen +Date: Thu, 15 Dec 2022 17:06:41 +0800 +Subject: [PATCH 21/33] I68TO2: 8204595: add more thread-related system settings info + to hs_error file on Linux +--- + hotspot/src/os/linux/vm/os_linux.cpp | 22 +++++++++++++++++++++- + hotspot/src/os/linux/vm/os_linux.hpp | 1 + + 2 files changed, 22 insertions(+), 1 deletion(-) + +diff --git a/hotspot/src/os/linux/vm/os_linux.cpp b/hotspot/src/os/linux/vm/os_linux.cpp +index abf2031..1ec68ab 100644 +--- a/hotspot/src/os/linux/vm/os_linux.cpp ++++ b/hotspot/src/os/linux/vm/os_linux.cpp +@@ -2249,6 +2249,8 @@ void os::print_os_info(outputStream* st) { + + os::Linux::print_process_memory_info(st); + ++ os::Linux::print_proc_sys_info(st); ++ + os::Linux::print_container_info(st); + } + +@@ -2390,6 +2392,24 @@ void os::Linux::print_process_memory_info(outputStream* st) { + + } + ++void os::Linux::print_proc_sys_info(outputStream* st) { ++ st->cr(); ++ st->print_cr("/proc/sys/kernel/threads-max (system-wide limit on the number of threads):"); ++ _print_ascii_file("/proc/sys/kernel/threads-max", st); ++ st->cr(); ++ st->cr(); ++ ++ st->print_cr("/proc/sys/vm/max_map_count (maximum number of memory map areas a process may have):"); ++ _print_ascii_file("/proc/sys/vm/max_map_count", st); ++ st->cr(); ++ st->cr(); ++ ++ st->print_cr("/proc/sys/kernel/pid_max (system-wide limit on number of process identifiers):"); ++ _print_ascii_file("/proc/sys/kernel/pid_max", st); ++ st->cr(); ++ st->cr(); ++} ++ + void os::Linux::print_container_info(outputStream* st) { + if (!OSContainer::is_containerized()) { + return; +@@ -6928,4 +6948,4 @@ bool os::trim_native_heap(os::size_change_t* rss_change) { + #else + return false; // musl + #endif +-} +\ No newline at end of file ++} +diff --git a/hotspot/src/os/linux/vm/os_linux.hpp b/hotspot/src/os/linux/vm/os_linux.hpp +index 6c27bcb..4ee2c9b 100644 +--- a/hotspot/src/os/linux/vm/os_linux.hpp ++++ b/hotspot/src/os/linux/vm/os_linux.hpp +@@ -125,6 +125,7 @@ class Linux { + static void print_container_info(outputStream* st); + static void print_distro_info(outputStream* st); + static void print_libversion_info(outputStream* st); ++ static void print_proc_sys_info(outputStream* st); + + public: + static bool _stack_is_executable; +-- +1.8.3.1 diff --git a/8219584-Try-to-dump-error-file-by-thread-which-cause.patch b/8219584-Try-to-dump-error-file-by-thread-which-cause.patch new file mode 100644 index 0000000000000000000000000000000000000000..fcf71861b70454708e02dbd60a031340f8773010 --- /dev/null +++ b/8219584-Try-to-dump-error-file-by-thread-which-cause.patch @@ -0,0 +1,407 @@ +From b61cd484f501a1fe7d49c336878a4b8398e727d9 Mon Sep 17 00:00:00 2001 +From: eapen +Date: Thu, 15 Dec 2022 14:28:05 +0800 +Subject: [PATCH 20/33] I68TO2: 8219584: Try to dump error file by thread which causes + safepoint timeout +--- + hotspot/src/os/posix/vm/os_posix.cpp | 31 ++++++- + hotspot/src/os/windows/vm/os_windows.cpp | 9 ++ + hotspot/src/share/vm/runtime/globals.hpp | 2 +- + hotspot/src/share/vm/runtime/os.hpp | 4 + + hotspot/src/share/vm/runtime/safepoint.cpp | 21 +++-- + hotspot/src/share/vm/runtime/vmThread.cpp | 36 +++++--- + hotspot/src/share/vm/runtime/vmThread.hpp | 10 ++- + hotspot/src/share/vm/utilities/vmError.cpp | 3 + + .../Safepoint/TestAbortVMOnSafepointTimeout.java | 97 ++++++++++++++++++++++ + 9 files changed, 195 insertions(+), 18 deletions(-) + create mode 100644 hotspot/test/runtime/Safepoint/TestAbortVMOnSafepointTimeout.java + +diff --git a/hotspot/src/os/posix/vm/os_posix.cpp b/hotspot/src/os/posix/vm/os_posix.cpp +index e7f1fdd..d2663bd 100644 +--- a/hotspot/src/os/posix/vm/os_posix.cpp ++++ b/hotspot/src/os/posix/vm/os_posix.cpp +@@ -26,6 +26,7 @@ + #include "prims/jvm.h" + #include "runtime/frame.inline.hpp" + #include "runtime/os.hpp" ++#include "utilities/events.hpp" + #include "utilities/vmError.hpp" + + #include +@@ -814,6 +815,15 @@ static bool get_signal_code_description(const siginfo_t* si, enum_sigcode_desc_t + return true; + } + ++bool os::signal_sent_by_kill(const void* siginfo) { ++ const siginfo_t* const si = (const siginfo_t*)siginfo; ++ return si->si_code == SI_USER || si->si_code == SI_QUEUE ++#ifdef SI_TKILL ++ || si->si_code == SI_TKILL ++#endif ++ ; ++} ++ + // A POSIX conform, platform-independend siginfo print routine. + // Short print out on one line. + void os::Posix::print_siginfo_brief(outputStream* os, const siginfo_t* si) { +@@ -844,7 +854,7 @@ void os::Posix::print_siginfo_brief(outputStream* os, const siginfo_t* si) { + const int me = (int) ::getpid(); + const int pid = (int) si->si_pid; + +- if (si->si_code == SI_USER || si->si_code == SI_QUEUE) { ++ if (signal_sent_by_kill(si)) { + if (IS_VALID_PID(pid) && pid != me) { + os->print(", sent from pid: %d (uid: %d)", pid, (int) si->si_uid); + } +@@ -860,6 +870,25 @@ void os::Posix::print_siginfo_brief(outputStream* os, const siginfo_t* si) { + } + } + ++bool os::signal_thread(Thread* thread, int sig, const char* reason) { ++ OSThread* osthread = thread->osthread(); ++ if (osthread) { ++#if defined (SOLARIS) ++ // Note: we cannot use pthread_kill on Solaris - not because ++ // its missing, but because we do not have the pthread_t id. ++ int status = thr_kill(osthread->thread_id(), sig); ++#else ++ int status = pthread_kill(osthread->pthread_id(), sig); ++#endif ++ if (status == 0) { ++ Events::log(Thread::current(), "sent signal %d to Thread " INTPTR_FORMAT " because %s.", ++ sig, p2i(thread), reason); ++ return true; ++ } ++ } ++ return false; ++} ++ + bool os::Posix::is_root(uid_t uid){ + return ROOT_UID == uid; + } +diff --git a/hotspot/src/os/windows/vm/os_windows.cpp b/hotspot/src/os/windows/vm/os_windows.cpp +index cc31126..cf1036c 100644 +--- a/hotspot/src/os/windows/vm/os_windows.cpp ++++ b/hotspot/src/os/windows/vm/os_windows.cpp +@@ -1877,6 +1877,11 @@ void os::print_memory_info(outputStream* st) { + st->cr(); + } + ++bool os::signal_sent_by_kill(const void* siginfo) { ++ // TODO: Is this possible? ++ return false; ++} ++ + void os::print_siginfo(outputStream *st, void *siginfo) { + EXCEPTION_RECORD* er = (EXCEPTION_RECORD*)siginfo; + st->print("siginfo:"); +@@ -1911,6 +1916,10 @@ void os::print_siginfo(outputStream *st, void *siginfo) { + st->cr(); + } + ++bool os::signal_thread(Thread* thread, int sig, const char* reason) { ++ // TODO: Can we kill thread? ++ return false; ++} + + int os::vsnprintf(char* buf, size_t len, const char* fmt, va_list args) { + #if _MSC_VER >= 1900 +diff --git a/hotspot/src/share/vm/runtime/globals.hpp b/hotspot/src/share/vm/runtime/globals.hpp +index 10e4e7f..64d40e0 100644 +--- a/hotspot/src/share/vm/runtime/globals.hpp ++++ b/hotspot/src/share/vm/runtime/globals.hpp +@@ -650,7 +650,7 @@ class CommandLineFlags { + "Print out every time compilation is longer than " \ + "a given threshold") \ + \ +- develop(bool, SafepointALot, false, \ ++ diagnostic(bool, SafepointALot, false, \ + "Generate a lot of safepoints. This works with " \ + "GuaranteedSafepointInterval") \ + \ +diff --git a/hotspot/src/share/vm/runtime/os.hpp b/hotspot/src/share/vm/runtime/os.hpp +index 5f41e96..092459c 100644 +--- a/hotspot/src/share/vm/runtime/os.hpp ++++ b/hotspot/src/share/vm/runtime/os.hpp +@@ -492,6 +492,9 @@ class os: AllStatic { + static void pd_start_thread(Thread* thread); + static void start_thread(Thread* thread); + ++ // Returns true if successful. ++ static bool signal_thread(Thread* thread, int sig, const char* reason); ++ + static void initialize_thread(Thread* thr); + static void free_thread(OSThread* osthread); + +@@ -653,6 +656,7 @@ class os: AllStatic { + static void print_environment_variables(outputStream* st, const char** env_list, char* buffer, int len); + static void print_context(outputStream* st, void* context); + static void print_register_info(outputStream* st, void* context); ++ static bool signal_sent_by_kill(const void* siginfo); + static void print_siginfo(outputStream* st, void* siginfo); + static void print_signal_handlers(outputStream* st, char* buf, size_t buflen); + static void print_date_and_time(outputStream* st, char* buf, size_t buflen); +diff --git a/hotspot/src/share/vm/runtime/safepoint.cpp b/hotspot/src/share/vm/runtime/safepoint.cpp +index 440617c..8408bed 100644 +--- a/hotspot/src/share/vm/runtime/safepoint.cpp ++++ b/hotspot/src/share/vm/runtime/safepoint.cpp +@@ -476,8 +476,7 @@ void SafepointSynchronize::begin() { + GC_locker::set_jni_lock_count(_current_jni_active_count); + + if (TraceSafepoint) { +- VM_Operation *op = VMThread::vm_operation(); +- tty->print_cr("Entering safepoint region: %s", (op != NULL) ? op->name() : "no vm operation"); ++ tty->print_cr("Entering safepoint region: %s", VMThread::vm_safepoint_description()); + } + + RuntimeService::record_safepoint_synchronized(); +@@ -929,11 +928,23 @@ void SafepointSynchronize::print_safepoint_timeout(SafepointTimeoutReason reason + // To debug the long safepoint, specify both AbortVMOnSafepointTimeout & + // ShowMessageBoxOnError. + if (AbortVMOnSafepointTimeout) { ++ // Send the blocking thread a signal to terminate and write an error file. ++ for (JavaThread *cur_thread = Threads::first(); cur_thread; ++ cur_thread = cur_thread->next()) { ++ ThreadSafepointState *cur_state = cur_thread->safepoint_state(); ++ if (cur_thread->thread_state() != _thread_blocked && ++ ((reason == _spinning_timeout && cur_state->is_running()) || ++ (reason == _blocking_timeout && !cur_state->has_called_back()))) { ++ if (!os::signal_thread(cur_thread, SIGILL, "blocking a safepoint")) { ++ break; // Could not send signal. Report fatal error. ++ } ++ // Give cur_thread a chance to report the error and terminate the VM. ++ os::sleep(Thread::current(), 3000, false); ++ } ++ } + char msg[1024]; +- VM_Operation *op = VMThread::vm_operation(); + sprintf(msg, "Safepoint sync time longer than " INTX_FORMAT "ms detected when executing %s.", +- SafepointTimeoutDelay, +- op != NULL ? op->name() : "no vm operation"); ++ SafepointTimeoutDelay, VMThread::vm_safepoint_description()); + fatal(msg); + } + } +diff --git a/hotspot/src/share/vm/runtime/vmThread.cpp b/hotspot/src/share/vm/runtime/vmThread.cpp +index b27c287..4f1695e 100644 +--- a/hotspot/src/share/vm/runtime/vmThread.cpp ++++ b/hotspot/src/share/vm/runtime/vmThread.cpp +@@ -217,6 +217,7 @@ VMThread* VMThread::_vm_thread = NULL; + VM_Operation* VMThread::_cur_vm_operation = NULL; + VMOperationQueue* VMThread::_vm_queue = NULL; + PerfCounter* VMThread::_perf_accumulated_vm_operation_time = NULL; ++const char* VMThread::_no_op_reason = NULL; + + + void VMThread::create() { +@@ -290,6 +291,7 @@ void VMThread::run() { + } + + // 4526887 let VM thread exit at Safepoint ++ _no_op_reason = "Halt"; + SafepointSynchronize::begin(); + + if (VerifyBeforeExit) { +@@ -422,6 +424,25 @@ void VMThread::evaluate_operation(VM_Operation* op) { + } + } + ++bool VMThread::no_op_safepoint_needed(bool check_time) { ++ if (SafepointALot) { ++ _no_op_reason = "SafepointALot"; ++ return true; ++ } ++ if (!SafepointSynchronize::is_cleanup_needed()) { ++ return false; ++ } ++ if (check_time) { ++ long interval = SafepointSynchronize::last_non_safepoint_interval(); ++ bool max_time_exceeded = GuaranteedSafepointInterval != 0 && ++ (interval > GuaranteedSafepointInterval); ++ if (!max_time_exceeded) { ++ return false; ++ } ++ } ++ _no_op_reason = "Cleanup"; ++ return true; ++} + + void VMThread::loop() { + assert(_cur_vm_operation == NULL, "no current one should be executing"); +@@ -460,8 +481,7 @@ void VMThread::loop() { + exit(-1); + } + +- if (timedout && (SafepointALot || +- SafepointSynchronize::is_cleanup_needed())) { ++ if (timedout && VMThread::no_op_safepoint_needed(false)) { + MutexUnlockerEx mul(VMOperationQueue_lock, + Mutex::_no_safepoint_check_flag); + // Force a safepoint since we have not had one for at least +@@ -585,14 +605,10 @@ void VMThread::loop() { + // + // We want to make sure that we get to a safepoint regularly. + // +- if (SafepointALot || SafepointSynchronize::is_cleanup_needed()) { +- long interval = SafepointSynchronize::last_non_safepoint_interval(); +- bool max_time_exceeded = GuaranteedSafepointInterval != 0 && (interval > GuaranteedSafepointInterval); +- if (SafepointALot || max_time_exceeded) { +- HandleMark hm(VMThread::vm_thread()); +- SafepointSynchronize::begin(); +- SafepointSynchronize::end(); +- } ++ if (VMThread::no_op_safepoint_needed(true)) { ++ HandleMark hm(VMThread::vm_thread()); ++ SafepointSynchronize::begin(); ++ SafepointSynchronize::end(); + } + } + } +diff --git a/hotspot/src/share/vm/runtime/vmThread.hpp b/hotspot/src/share/vm/runtime/vmThread.hpp +index a6d1ad3..d8af0d9 100644 +--- a/hotspot/src/share/vm/runtime/vmThread.hpp ++++ b/hotspot/src/share/vm/runtime/vmThread.hpp +@@ -100,7 +100,12 @@ class VMThread: public NamedThread { + static Monitor * _terminate_lock; + static PerfCounter* _perf_accumulated_vm_operation_time; + ++ static const char* _no_op_reason; ++ ++ static bool no_op_safepoint_needed(bool check_time); ++ + void evaluate_operation(VM_Operation* op); ++ + public: + // Constructor + VMThread(); +@@ -123,7 +128,10 @@ class VMThread: public NamedThread { + static void execute(VM_Operation* op); + + // Returns the current vm operation if any. +- static VM_Operation* vm_operation() { return _cur_vm_operation; } ++ static VM_Operation* vm_operation() { return _cur_vm_operation; } ++ ++ // Returns the current vm operation name or set reason ++ static const char* vm_safepoint_description() { return _cur_vm_operation != NULL ? _cur_vm_operation->name() : _no_op_reason; }; + + // Returns the single instance of VMThread. + static VMThread* vm_thread() { return _vm_thread; } +diff --git a/hotspot/src/share/vm/utilities/vmError.cpp b/hotspot/src/share/vm/utilities/vmError.cpp +index 9b40a34..261591d 100644 +--- a/hotspot/src/share/vm/utilities/vmError.cpp ++++ b/hotspot/src/share/vm/utilities/vmError.cpp +@@ -460,6 +460,9 @@ void VMError::report(outputStream* st) { + st->print("%s", buf); + st->print(" (0x%x)", _id); // signal number + st->print(" at pc=" PTR_FORMAT, _pc); ++ if (_siginfo != NULL && os::signal_sent_by_kill(_siginfo)) { ++ st->print(" (sent by kill)"); ++ } + } else { + if (should_report_bug(_id)) { + st->print("Internal Error"); +diff --git a/hotspot/test/runtime/Safepoint/TestAbortVMOnSafepointTimeout.java b/hotspot/test/runtime/Safepoint/TestAbortVMOnSafepointTimeout.java +new file mode 100644 +index 0000000..a097bdc +--- /dev/null ++++ b/hotspot/test/runtime/Safepoint/TestAbortVMOnSafepointTimeout.java +@@ -0,0 +1,97 @@ ++/* ++ * Copyright (c) 2019, SAP SE. All rights reserved. ++ * Copyright (c) 2021, Huawei Technologies Co., Ltd. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ */ ++ ++import com.oracle.java.testlibrary.*; ++ ++/* ++ * @test TestAbortVMOnSafepointTimeout ++ * @summary Check if VM can kill thread which doesn't reach safepoint. ++ * @bug 8219584 8227528 ++ * @library /testlibrary ++ * ++ */ ++ ++public class TestAbortVMOnSafepointTimeout { ++ ++ public static void main(String[] args) throws Exception { ++ if (args.length > 0) { ++ int result = test_loop(3); ++ System.out.println("This message would occur after some time with result " + result); ++ return; ++ } ++ ++ testWith(500, 500); ++ } ++ ++ static int test_loop(int x) { ++ int sum = 0; ++ if (x != 0) { ++ // Long running loop without safepoint. ++ for (int y = 1; y < Integer.MAX_VALUE; ++y) { ++ if (y % x == 0) ++sum; ++ } ++ } ++ return sum; ++ } ++ ++ public static void testWith(int sfpt_interval, int timeout_delay) throws Exception { ++ // -XX:-UseCountedLoopSafepoints - is used to prevent the loop ++ // in test_loop() to poll for safepoints. ++ // -XX:LoopStripMiningIter=0 and -XX:LoopUnrollLimit=0 - are ++ // used to prevent optimizations over the loop in test_loop() ++ // since we actually want it to provoke a safepoint timeout. ++ // -XX:-UseBiasedLocking - is used to prevent biased locking ++ // handshakes from changing the timing of this test. ++ ProcessBuilder pb = ProcessTools.createJavaProcessBuilder( ++ "-XX:+UnlockDiagnosticVMOptions", ++ "-XX:-UseBiasedLocking", ++ "-XX:+SafepointTimeout", ++ "-XX:+SafepointALot", ++ "-XX:+AbortVMOnSafepointTimeout", ++ "-XX:SafepointTimeoutDelay=" + timeout_delay, ++ "-XX:GuaranteedSafepointInterval=" + sfpt_interval, ++ "-XX:-TieredCompilation", ++ "-XX:-UseCountedLoopSafepoints", ++ "-XX:LoopUnrollLimit=0", ++ "-XX:CompileCommand=compileonly,TestAbortVMOnSafepointTimeout::test_loop", ++ "-Xcomp", ++ "-XX:-CreateMinidumpOnCrash", ++ "-Xms64m", ++ "TestAbortVMOnSafepointTimeout", ++ "runTestLoop" ++ ); ++ ++ OutputAnalyzer output = new OutputAnalyzer(pb.start()); ++ if (Platform.isWindows()) { ++ output.shouldMatch("Safepoint sync time longer than"); ++ } else { ++ output.shouldMatch("SIGILL"); ++ if (Platform.isLinux()) { ++ output.shouldMatch("(sent by kill)"); ++ } ++ output.shouldMatch("TestAbortVMOnSafepointTimeout.test_loop"); ++ } ++ output.shouldNotHaveExitValue(0); ++ } ++} +-- +1.8.3.1 diff --git a/8229517-Support-for-optional-asynchronous-buffered-l.patch b/8229517-Support-for-optional-asynchronous-buffered-l.patch new file mode 100644 index 0000000000000000000000000000000000000000..5a43c8f1aa2f28a50b8beca051012480641ad073 --- /dev/null +++ b/8229517-Support-for-optional-asynchronous-buffered-l.patch @@ -0,0 +1,697 @@ +From 577f318d824d91e5deb8b6b82dd211583cb93cac Mon Sep 17 00:00:00 2001 +From: eapen +Date: Thu, 15 Dec 2022 10:37:31 +0800 +Subject: [PATCH 18/33] I68TO2: 8229517: Support for optional asynchronous/buffered + logging +--- + hotspot/src/os/windows/vm/os_windows.cpp | 1 + + hotspot/src/share/vm/runtime/arguments.cpp | 10 ++ + hotspot/src/share/vm/runtime/globals.hpp | 9 ++ + hotspot/src/share/vm/runtime/init.cpp | 2 + + hotspot/src/share/vm/runtime/logAsyncWriter.cpp | 164 ++++++++++++++++++++++++ + hotspot/src/share/vm/runtime/logAsyncWriter.hpp | 159 +++++++++++++++++++++++ + hotspot/src/share/vm/runtime/os.hpp | 1 + + hotspot/src/share/vm/runtime/thread.cpp | 26 +++- + hotspot/src/share/vm/runtime/vmStructs.cpp | 2 + + hotspot/src/share/vm/utilities/linkedlist.hpp | 47 +++++-- + hotspot/src/share/vm/utilities/ostream.cpp | 26 ++++ + hotspot/src/share/vm/utilities/ostream.hpp | 3 + + 12 files changed, 440 insertions(+), 10 deletions(-) + create mode 100644 hotspot/src/share/vm/runtime/logAsyncWriter.cpp + create mode 100644 hotspot/src/share/vm/runtime/logAsyncWriter.hpp + +diff --git a/hotspot/src/os/windows/vm/os_windows.cpp b/hotspot/src/os/windows/vm/os_windows.cpp +index 25122de..cc31126 100644 +--- a/hotspot/src/os/windows/vm/os_windows.cpp ++++ b/hotspot/src/os/windows/vm/os_windows.cpp +@@ -562,6 +562,7 @@ bool os::create_thread(Thread* thread, ThreadType thr_type, size_t stack_size) { + case os::pgc_thread: + case os::cgc_thread: + case os::watcher_thread: ++ case os::asynclog_thread: + if (VMThreadStackSize > 0) stack_size = (size_t)(VMThreadStackSize * K); + break; + } +diff --git a/hotspot/src/share/vm/runtime/arguments.cpp b/hotspot/src/share/vm/runtime/arguments.cpp +index 91e2ce0..fba3d4b 100644 +--- a/hotspot/src/share/vm/runtime/arguments.cpp ++++ b/hotspot/src/share/vm/runtime/arguments.cpp +@@ -2269,6 +2269,16 @@ bool Arguments::verify_percentage(uintx value, const char* name) { + // no gc log rotation when log file not supplied or + // NumberOfGCLogFiles is 0 + void check_gclog_consistency() { ++ if (UseAsyncGCLog) { ++ if (Arguments::gc_log_filename() == NULL) { ++ jio_fprintf(defaultStream::output_stream(), ++ "To enable Async GC log, use -Xloggc: -XX:UseAsyncGCLog\n" ++ "Async GC log is turned off\n"); ++ UseAsyncGCLog = false; ++ ++ } ++ } ++ + if (UseGCLogFileRotation) { + if ((Arguments::gc_log_filename() == NULL) || (NumberOfGCLogFiles == 0)) { + jio_fprintf(defaultStream::output_stream(), +diff --git a/hotspot/src/share/vm/runtime/globals.hpp b/hotspot/src/share/vm/runtime/globals.hpp +index 41b1392..10e4e7f 100644 +--- a/hotspot/src/share/vm/runtime/globals.hpp ++++ b/hotspot/src/share/vm/runtime/globals.hpp +@@ -4104,6 +4104,15 @@ class CommandLineFlags { + \ + JFR_ONLY(product(bool, LogJFR, false, \ + "Enable JFR logging (consider +Verbose)")) \ ++ \ ++ product(bool, UseAsyncGCLog, false, \ ++ "Enable asynchronous GC logging") \ ++ \ ++ product(uintx, AsyncLogBufferSize, 2*M, \ ++ "Memory budget (in bytes) for the buffer of Asynchronous") \ ++ \ ++ diagnostic(bool, PrintAsyncGCLog, false, \ ++ "Print some information of Async GC Log") \ + + /* + * Macros for factoring of globals +diff --git a/hotspot/src/share/vm/runtime/init.cpp b/hotspot/src/share/vm/runtime/init.cpp +index d2e0f22..b185409 100644 +--- a/hotspot/src/share/vm/runtime/init.cpp ++++ b/hotspot/src/share/vm/runtime/init.cpp +@@ -32,6 +32,7 @@ + #include "runtime/handles.inline.hpp" + #include "runtime/icache.hpp" + #include "runtime/init.hpp" ++#include "runtime/logAsyncWriter.hpp" + #include "runtime/safepoint.hpp" + #include "runtime/sharedRuntime.hpp" + #include "services/memTracker.hpp" +@@ -106,6 +107,7 @@ jint init_globals() { + if (status != JNI_OK) + return status; + ++ AsyncLogWriter::initialize(); + interpreter_init(); // before any methods loaded + invocationCounter_init(); // before any methods loaded + marksweep_init(); +diff --git a/hotspot/src/share/vm/runtime/logAsyncWriter.cpp b/hotspot/src/share/vm/runtime/logAsyncWriter.cpp +new file mode 100644 +index 0000000..750a23f +--- /dev/null ++++ b/hotspot/src/share/vm/runtime/logAsyncWriter.cpp +@@ -0,0 +1,164 @@ ++/* ++ * Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. ++ * Copyright (c) 2022, Huawei Technologies Co., Ltd. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ * ++ */ ++#include "precompiled.hpp" ++#include "runtime/atomic.hpp" ++#include "runtime/logAsyncWriter.hpp" ++#include "utilities/ostream.hpp" ++ ++class AsyncLogWriter::AsyncLogLocker : public StackObj { ++ public: ++ AsyncLogLocker() { ++ assert(_instance != NULL, "AsyncLogWriter::_lock is unavailable"); ++ _instance->_lock.wait(); ++ } ++ ++ ~AsyncLogLocker() { ++ _instance->_lock.signal(); ++ } ++}; ++ ++void AsyncLogWriter::enqueue_locked(const AsyncLogMessage& msg) { ++ if (_buffer.size() >= _buffer_max_size) { ++ // drop the enqueueing message. ++ os::free(msg.message()); ++ return; ++ } ++ ++ assert(_buffer.size() < _buffer_max_size, "_buffer is over-sized."); ++ _buffer.push_back(msg); ++ _sem.signal(); ++} ++ ++void AsyncLogWriter::enqueue(const char* msg) { ++ AsyncLogMessage m(os::strdup(msg)); ++ ++ { // critical area ++ AsyncLogLocker locker; ++ enqueue_locked(m); ++ } ++} ++ ++AsyncLogWriter::AsyncLogWriter() ++ : NamedThread(), ++ _lock(1), _sem(0), _io_sem(1), ++ _initialized(false), ++ _buffer_max_size(AsyncLogBufferSize / sizeof(AsyncLogMessage)) { ++ if (os::create_thread(this, os::asynclog_thread)) { ++ _initialized = true; ++ set_name("AsyncLog Thread"); ++ } else { ++ if (PrintAsyncGCLog) { ++ tty->print_cr("AsyncLogging failed to create thread. Falling back to synchronous logging."); ++ } ++ } ++ ++ if (PrintAsyncGCLog) { ++ tty->print_cr("The maximum entries of AsyncLogBuffer: " SIZE_FORMAT ", estimated memory use: " SIZE_FORMAT " bytes", ++ _buffer_max_size, AsyncLogBufferSize); ++ } ++} ++ ++void AsyncLogWriter::write() { ++ // Use kind of copy-and-swap idiom here. ++ // Empty 'logs' swaps the content with _buffer. ++ // Along with logs destruction, all processed messages are deleted. ++ // ++ // The operation 'pop_all()' is done in O(1). All I/O jobs are then performed without ++ // lock protection. This guarantees I/O jobs don't block logsites. ++ AsyncLogBuffer logs; ++ bool own_io = false; ++ ++ { // critical region ++ AsyncLogLocker locker; ++ ++ _buffer.pop_all(&logs); ++ own_io = _io_sem.trywait(); ++ } ++ ++ LinkedListIterator it(logs.head()); ++ if (!own_io) { ++ _io_sem.wait(); ++ } ++ ++ bool flush = false; ++ while (!it.is_empty()) { ++ AsyncLogMessage* e = it.next(); ++ char* msg = e->message(); ++ ++ if (msg != NULL) { ++ flush = true; ++ ((gcLogFileStream*)gclog_or_tty)->write_blocking(msg, strlen(msg)); ++ os::free(msg); ++ } ++ } ++ if (flush) { ++ ((gcLogFileStream*)gclog_or_tty)->fileStream::flush(); ++ } ++ _io_sem.signal(); ++} ++ ++void AsyncLogWriter::run() { ++ while (true) { ++ // The value of a semphore cannot be negative. Therefore, the current thread falls asleep ++ // when its value is zero. It will be waken up when new messages are enqueued. ++ _sem.wait(); ++ write(); ++ } ++} ++ ++AsyncLogWriter* AsyncLogWriter::_instance = NULL; ++ ++void AsyncLogWriter::initialize() { ++ if (!UseAsyncGCLog) return; ++ ++ assert(_instance == NULL, "initialize() should only be invoked once."); ++ ++ AsyncLogWriter* self = new AsyncLogWriter(); ++ if (self->_initialized) { ++ OrderAccess::release_store_ptr(&AsyncLogWriter::_instance, self); ++ os::start_thread(self); ++ if (PrintAsyncGCLog) { ++ tty->print_cr("Async logging thread started."); ++ } ++ } ++} ++ ++AsyncLogWriter* AsyncLogWriter::instance() { ++ return _instance; ++} ++ ++// write() acquires and releases _io_sem even _buffer is empty. ++// This guarantees all logging I/O of dequeued messages are done when it returns. ++void AsyncLogWriter::flush() { ++ if (_instance != NULL) { ++ _instance->write(); ++ } ++} ++ ++void AsyncLogWriter::print_on(outputStream* st) const{ ++ st->print("\"%s\" ", name()); ++ Thread::print_on(st); ++ st->cr(); ++} +diff --git a/hotspot/src/share/vm/runtime/logAsyncWriter.hpp b/hotspot/src/share/vm/runtime/logAsyncWriter.hpp +new file mode 100644 +index 0000000..5242426 +--- /dev/null ++++ b/hotspot/src/share/vm/runtime/logAsyncWriter.hpp +@@ -0,0 +1,159 @@ ++/* ++ * Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. ++ * Copyright (c) 2022, Huawei Technologies Co., Ltd. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ * ++ */ ++#ifndef SHARE_VM_RUNTIME_LOGASYNCWRITER_HPP ++#define SHARE_VM_RUNTIME_LOGASYNCWRITER_HPP ++#include "memory/resourceArea.hpp" ++#include "runtime/semaphore.hpp" ++#include "utilities/linkedlist.hpp" ++ ++template ++class LinkedListDeque : private LinkedListImpl { ++ private: ++ LinkedListNode* _tail; ++ size_t _size; ++ ++ public: ++ LinkedListDeque() : _tail(NULL), _size(0) {} ++ void push_back(const E& e) { ++ if (!_tail) { ++ _tail = this->add(e); ++ } else { ++ _tail = this->insert_after(e, _tail); ++ } ++ ++ ++_size; ++ } ++ ++ // pop all elements to logs. ++ void pop_all(LinkedList* logs) { ++ logs->move(static_cast* >(this)); ++ _tail = NULL; ++ _size = 0; ++ } ++ ++ void pop_all(LinkedListDeque* logs) { ++ logs->_size = _size; ++ logs->_tail = _tail; ++ pop_all(static_cast* >(logs)); ++ } ++ ++ void pop_front() { ++ LinkedListNode* h = this->unlink_head(); ++ if (h == _tail) { ++ _tail = NULL; ++ } ++ ++ if (h != NULL) { ++ --_size; ++ this->delete_node(h); ++ } ++ } ++ ++ size_t size() const { return _size; } ++ ++ const E* front() const { ++ return this->_head == NULL ? NULL : this->_head->peek(); ++ } ++ ++ const E* back() const { ++ return _tail == NULL ? NULL : _tail->peek(); ++ } ++ ++ LinkedListNode* head() const { ++ return this->_head; ++ } ++}; ++ ++class AsyncLogMessage { ++ char* _message; ++ ++public: ++ AsyncLogMessage(char* msg) ++ : _message(msg) {} ++ ++ // placeholder for LinkedListImpl. ++ bool equals(const AsyncLogMessage& o) const { return false; } ++ ++ char* message() const { return _message; } ++}; ++ ++typedef LinkedListDeque AsyncLogBuffer; ++ ++// ++// ASYNC LOGGING SUPPORT ++// ++// Summary: ++// Async Logging is working on the basis of singleton AsyncLogWriter, which manages an intermediate buffer and a flushing thread. ++// ++// Interface: ++// ++// initialize() is called once when JVM is initialized. It creates and initializes the singleton instance of AsyncLogWriter. ++// Once async logging is established, there's no way to turn it off. ++// ++// instance() is MT-safe and returns the pointer of the singleton instance if and only if async logging is enabled and has well ++// initialized. Clients can use its return value to determine async logging is established or not. ++// ++// The basic operation of AsyncLogWriter is enqueue(). 2 overloading versions of it are provided to match LogOutput::write(). ++// They are both MT-safe and non-blocking. Derived classes of LogOutput can invoke the corresponding enqueue() in write() and ++// return 0. AsyncLogWriter is responsible of copying neccessary data. ++// ++// The static member function flush() is designated to flush out all pending messages when JVM is terminating. ++// In normal JVM termination, flush() is invoked in LogConfiguration::finalize(). flush() is MT-safe and can be invoked arbitrary ++// times. It is no-op if async logging is not established. ++// ++class AsyncLogWriter : public NamedThread { ++ class AsyncLogLocker; ++ ++ static AsyncLogWriter* _instance; ++ // _lock(1) denotes a critional region. ++ Semaphore _lock; ++ // _sem is a semaphore whose value denotes how many messages have been enqueued. ++ // It decreases in AsyncLogWriter::run() ++ Semaphore _sem; ++ // A lock of IO ++ Semaphore _io_sem; ++ ++ volatile bool _initialized; ++ AsyncLogBuffer _buffer; ++ ++ const size_t _buffer_max_size; ++ ++ AsyncLogWriter(); ++ void enqueue_locked(const AsyncLogMessage& msg); ++ void write(); ++ void run(); ++ ++ public: ++ void enqueue(const char* msg); ++ ++ static AsyncLogWriter* instance(); ++ static void initialize(); ++ static void flush(); ++ // Printing ++ void print_on(outputStream* st) const; ++ ++}; ++ ++#endif // SHARE_LOGGING_LOGASYNCWRITER_HPP +diff --git a/hotspot/src/share/vm/runtime/os.hpp b/hotspot/src/share/vm/runtime/os.hpp +index acc57f4..5f41e96 100644 +--- a/hotspot/src/share/vm/runtime/os.hpp ++++ b/hotspot/src/share/vm/runtime/os.hpp +@@ -463,6 +463,7 @@ class os: AllStatic { + java_thread, + compiler_thread, + watcher_thread, ++ asynclog_thread, // dedicated to flushing logs + os_thread + }; + +diff --git a/hotspot/src/share/vm/runtime/thread.cpp b/hotspot/src/share/vm/runtime/thread.cpp +index cacab59..61627e4 100644 +--- a/hotspot/src/share/vm/runtime/thread.cpp ++++ b/hotspot/src/share/vm/runtime/thread.cpp +@@ -57,6 +57,7 @@ + #include "runtime/java.hpp" + #include "runtime/javaCalls.hpp" + #include "runtime/jniPeriodicChecker.hpp" ++#include "runtime/logAsyncWriter.hpp" + #include "runtime/memprofiler.hpp" + #include "runtime/mutexLocker.hpp" + #include "runtime/objectMonitor.hpp" +@@ -881,7 +882,9 @@ void Thread::print_on_error(outputStream* st, char* buf, int buflen) const { + else if (is_GC_task_thread()) st->print("GCTaskThread"); + else if (is_Watcher_thread()) st->print("WatcherThread"); + else if (is_ConcurrentGC_thread()) st->print("ConcurrentGCThread"); +- else st->print("Thread"); ++ else if (this == AsyncLogWriter::instance()) { ++ st->print("%s", this->name()); ++ } else st->print("Thread"); + + st->print(" [stack: " PTR_FORMAT "," PTR_FORMAT "]", + _stack_base - _stack_size, _stack_base); +@@ -4387,6 +4390,12 @@ void Threads::print_on(outputStream* st, bool print_stacks, bool internal_format + st->cr(); + } + CompileBroker::print_compiler_threads_on(st); ++ if (UseAsyncGCLog) { ++ AsyncLogWriter* aio_writer = AsyncLogWriter::instance(); ++ if (aio_writer != NULL) { ++ aio_writer->print_on(st); ++ } ++ } + st->flush(); + } + +@@ -4432,6 +4441,21 @@ void Threads::print_on_error(outputStream* st, Thread* current, char* buf, int b + wt->print_on_error(st, buf, buflen); + st->cr(); + } ++ ++ if (UseAsyncGCLog) { ++ AsyncLogWriter* aio_writer = AsyncLogWriter::instance(); ++ if (aio_writer != NULL) { ++ bool is_current = (current == aio_writer); ++ found_current = found_current || is_current; ++ st->print("%s", is_current ? "=>" : " "); ++ ++ st->print(PTR_FORMAT, aio_writer); ++ st->print(" "); ++ aio_writer->print_on_error(st, buf, buflen); ++ st->cr(); ++ } ++ } ++ + if (!found_current) { + st->cr(); + st->print("=>" PTR_FORMAT " (exited) ", current); +diff --git a/hotspot/src/share/vm/runtime/vmStructs.cpp b/hotspot/src/share/vm/runtime/vmStructs.cpp +index ab20f5c..5d1cf2b 100644 +--- a/hotspot/src/share/vm/runtime/vmStructs.cpp ++++ b/hotspot/src/share/vm/runtime/vmStructs.cpp +@@ -97,6 +97,7 @@ + #include "runtime/sharedRuntime.hpp" + #include "runtime/stubRoutines.hpp" + #include "runtime/thread.inline.hpp" ++#include "runtime/logAsyncWriter.hpp" + #include "runtime/virtualspace.hpp" + #include "runtime/vmStructs.hpp" + #include "utilities/array.hpp" +@@ -1599,6 +1600,7 @@ typedef TwoOopHashtable SymbolTwoOopHashtable; + declare_type(Thread, ThreadShadow) \ + declare_type(NamedThread, Thread) \ + declare_type(WatcherThread, Thread) \ ++ declare_type(AsyncLogWriter, Thread) \ + declare_type(JavaThread, Thread) \ + declare_type(JvmtiAgentThread, JavaThread) \ + declare_type(ServiceThread, JavaThread) \ +diff --git a/hotspot/src/share/vm/utilities/linkedlist.hpp b/hotspot/src/share/vm/utilities/linkedlist.hpp +index a76c15c..f4f2a9b 100644 +--- a/hotspot/src/share/vm/utilities/linkedlist.hpp ++++ b/hotspot/src/share/vm/utilities/linkedlist.hpp +@@ -40,6 +40,25 @@ template class LinkedListNode : public ResourceObj { + E _data; // embedded content + LinkedListNode* _next; // next entry + ++ // Select member function 'bool U::equals(const U&) const' if 'U' is of class ++ // type. This works because of the "Substitution Failure Is Not An Error" ++ // (SFINAE) rule. Notice that this version of 'equal' will also be chosen for ++ // class types which don't define a corresponding 'equals()' method (and will ++ // result in a compilation error for them). It is not easily possible to ++ // specialize this 'equal()' function exclusively for class types which define ++ // the correct 'equals()' function because that function can be in a base ++ // class, a dependent base class or have a compatible but slightly different ++ // signature. ++ template ++ static bool equal(const U& a, const U& b, bool (U::*t)(const U&) const) { ++ return a.equals(b); ++ } ++ ++ template ++ static bool equal(const U& a, const U& b, ...) { ++ return a == b; ++ } ++ + protected: + LinkedListNode() : _next(NULL) { } + +@@ -51,6 +70,10 @@ template class LinkedListNode : public ResourceObj { + + E* data() { return &_data; } + const E* peek() const { return &_data; } ++ ++ bool equals(const E& t) const { ++ return equal(_data, t, NULL); ++ } + }; + + // A linked list interface. It does not specify +@@ -62,6 +85,7 @@ template class LinkedList : public ResourceObj { + + public: + LinkedList() : _head(NULL) { } ++ virtual ~LinkedList() {} + + inline void set_head(LinkedListNode* h) { _head = h; } + inline LinkedListNode* head() const { return _head; } +@@ -182,7 +206,7 @@ template * find_node(const E& e) { + LinkedListNode* p = this->head(); +- while (p != NULL && !p->peek()->equals(e)) { ++ while (p != NULL && !p->equals(e)) { + p = p->next(); + } + return p; +@@ -229,7 +253,7 @@ template * prev = NULL; + + while (tmp != NULL) { +- if (tmp->peek()->equals(e)) { ++ if (tmp->equals(e)) { + return remove_after(prev); + } + prev = tmp; +@@ -396,16 +420,21 @@ template class LinkedListIterator : public StackObj { + private: +- LinkedListNode* _p; +- bool _is_empty; ++ mutable LinkedListNode* _p; ++ + public: +- LinkedListIterator(LinkedListNode* head) : _p(head) { +- _is_empty = (head == NULL); +- } ++ LinkedListIterator(LinkedListNode* head) : _p(head) { } ++ ++ bool is_empty() const { return _p == NULL; } + +- bool is_empty() const { return _is_empty; } ++ E* next() { ++ if (_p == NULL) return NULL; ++ E* e = _p->data(); ++ _p = _p->next(); ++ return e; ++ } + +- const E* next() { ++ const E* next() const { + if (_p == NULL) return NULL; + const E* e = _p->peek(); + _p = _p->next(); +diff --git a/hotspot/src/share/vm/utilities/ostream.cpp b/hotspot/src/share/vm/utilities/ostream.cpp +index 14d82ad..5d40559 100644 +--- a/hotspot/src/share/vm/utilities/ostream.cpp ++++ b/hotspot/src/share/vm/utilities/ostream.cpp +@@ -30,6 +30,7 @@ + #include "runtime/mutexLocker.hpp" + #include "runtime/os.hpp" + #include "runtime/vmThread.hpp" ++#include "runtime/logAsyncWriter.hpp" + #include "utilities/defaultStream.hpp" + #include "utilities/ostream.hpp" + #include "utilities/top.hpp" +@@ -876,6 +877,17 @@ gcLogFileStream::gcLogFileStream(const char* file_name) : _file_lock(NULL) { + } + + void gcLogFileStream::write(const char* s, size_t len) { ++ if (UseAsyncGCLog) { ++ AsyncLogWriter* aio_writer = AsyncLogWriter::instance(); ++ if (aio_writer != NULL) { ++ aio_writer->enqueue(s); ++ return; ++ } ++ } ++ write_blocking(s, len); ++} ++ ++void gcLogFileStream::write_blocking(const char* s, size_t len) { + if (_file != NULL) { + // we can't use Thread::current() here because thread may be NULL + // in early stage(ostream_init_log) +@@ -1047,6 +1059,17 @@ void gcLogFileStream::rotate_log_impl(bool force, outputStream* out) { + } + } + ++void gcLogFileStream::flush() { ++ if (UseAsyncGCLog) { ++ AsyncLogWriter* aio_writer = AsyncLogWriter::instance(); ++ if (aio_writer != NULL) { ++ // do nothing ++ return; ++ } ++ } ++ fileStream::flush(); ++} ++ + defaultStream* defaultStream::instance = NULL; + int defaultStream::_output_fd = 1; + int defaultStream::_error_fd = 2; +@@ -1456,6 +1479,9 @@ void ostream_exit() { + + // ostream_abort() is called by os::abort() when VM is about to die. + void ostream_abort() { ++ if (UseAsyncGCLog) { ++ AsyncLogWriter::flush(); ++ } + // Here we can't delete gclog_or_tty and tty, just flush their output + if (gclog_or_tty) gclog_or_tty->flush(); + if (tty) tty->flush(); +diff --git a/hotspot/src/share/vm/utilities/ostream.hpp b/hotspot/src/share/vm/utilities/ostream.hpp +index d0f9aac..85ff599 100644 +--- a/hotspot/src/share/vm/utilities/ostream.hpp ++++ b/hotspot/src/share/vm/utilities/ostream.hpp +@@ -254,6 +254,7 @@ class gcLogFileStream : public fileStream { + gcLogFileStream(const char* file_name); + ~gcLogFileStream(); + virtual void write(const char* c, size_t len); ++ void write_blocking(const char* c, size_t len); + virtual void rotate_log(bool force, outputStream* out = NULL); + void dump_loggc_header(); + +@@ -263,6 +264,8 @@ class gcLogFileStream : public fileStream { + ((GCLogFileSize != 0) && ((uintx)_bytes_written >= GCLogFileSize)); + } + ++ virtual void flush(); ++ + }; + + #ifndef PRODUCT +-- +1.8.3.1 diff --git a/8232069-enable-shutdown-UseCompressedClassPointers-U.patch b/8232069-enable-shutdown-UseCompressedClassPointers-U.patch new file mode 100644 index 0000000000000000000000000000000000000000..243fa732cf21405e4f0775f1965d3886ee77aed3 --- /dev/null +++ b/8232069-enable-shutdown-UseCompressedClassPointers-U.patch @@ -0,0 +1,594 @@ +From bf7e5b40eab65acf8988a30c1530654db1f8cf07 Mon Sep 17 00:00:00 2001 +From: eapen +Date: Fri, 30 Sep 2022 17:18:50 +0800 +Subject: [PATCH 27/33] I68TO2: 8232069: enable shutdown UseCompressedClassPointers && + UseCompressedOops when CDS +--- + common/bin/compare.sh | 2 +- + hotspot/src/share/vm/memory/filemap.cpp | 12 ++ + hotspot/src/share/vm/memory/filemap.hpp | 4 + + hotspot/src/share/vm/memory/metaspace.cpp | 42 +++-- + hotspot/src/share/vm/runtime/arguments.cpp | 47 ++--- + hotspot/src/share/vm/runtime/arguments.hpp | 2 +- + .../CDSCompressedKPtrsError.java | 93 ---------- + .../appcds/CommandLineFlagComboNegative.java | 5 +- + .../appcds/TestCombinedCompressedFlags.java | 192 +++++++++++++++++++++ + jdk/make/BuildJdk.gmk | 2 + + 10 files changed, 253 insertions(+), 148 deletions(-) + delete mode 100644 hotspot/test/runtime/CDSCompressedKPtrs/CDSCompressedKPtrsError.java + create mode 100644 hotspot/test/runtime/appcds/TestCombinedCompressedFlags.java + +diff --git a/common/bin/compare.sh b/common/bin/compare.sh +index a36464a..e6a3f67 100644 +--- a/common/bin/compare.sh ++++ b/common/bin/compare.sh +@@ -290,7 +290,7 @@ compare_general_files() { + ! -name "ct.sym" ! -name "*.diz" ! -name "*.dll" \ + ! -name "*.pdb" ! -name "*.exp" ! -name "*.ilk" \ + ! -name "*.lib" ! -name "*.war" ! -name "JavaControlPanel" \ +- ! -name "classes.jsa" \ ++ ! -name "classes.jsa" | -name "classes_nocoops.jsa" \ + | $GREP -v "./bin/" | $SORT | $FILTER) + + echo General files... +diff --git a/hotspot/src/share/vm/memory/filemap.cpp b/hotspot/src/share/vm/memory/filemap.cpp +index 0682cd6..0d21707 100644 +--- a/hotspot/src/share/vm/memory/filemap.cpp ++++ b/hotspot/src/share/vm/memory/filemap.cpp +@@ -241,6 +241,8 @@ void FileMapInfo::FileMapHeader::populate(FileMapInfo* mapinfo, size_t alignment + _alignment = alignment; + _obj_alignment = ObjectAlignmentInBytes; + ++ _compressed_oops = UseCompressedOops; ++ _compressed_class_ptrs = UseCompressedClassPointers; + if (!DynamicDumpSharedSpaces) { + _classpath_entry_table_size = mapinfo->_classpath_entry_table_size; + _classpath_entry_table = mapinfo->_classpath_entry_table; +@@ -987,6 +989,16 @@ bool FileMapInfo::FileMapHeader::validate() { + _obj_alignment, ObjectAlignmentInBytes); + return false; + } ++ if (PrintSharedSpaces) { ++ tty->print_cr("Archive was created with UseCompressedOops = %d, UseCompressedClassPointers = %d", ++ compressed_oops(), compressed_class_pointers()); ++ } ++ ++ if (compressed_oops() != UseCompressedOops || compressed_class_pointers() != UseCompressedClassPointers) { ++ FileMapInfo::fail_continue("Unable to use shared archive.\nThe saved state of UseCompressedOops and UseCompressedClassPointers is " ++ "different from runtime, CDS will be disabled."); ++ return false; ++ } + + return true; + } +diff --git a/hotspot/src/share/vm/memory/filemap.hpp b/hotspot/src/share/vm/memory/filemap.hpp +index 27fff35..debfb50 100644 +--- a/hotspot/src/share/vm/memory/filemap.hpp ++++ b/hotspot/src/share/vm/memory/filemap.hpp +@@ -105,6 +105,8 @@ public: + size_t _alignment; // how shared archive should be aligned + int _obj_alignment; // value of ObjectAlignmentInBytes + bool _is_default_jsa; // indicates whether is the default jsa file ++ bool _compressed_oops; // save the flag UseCompressedOops ++ bool _compressed_class_ptrs; // save the flag UseCompressedClassPointers + + struct space_info { + int _crc; // crc checksum of the current space +@@ -156,6 +158,8 @@ public: + int compute_crc(); + unsigned int magic() const { return _magic; } + const char* jvm_ident() const { return _jvm_ident; } ++ bool compressed_oops() const { return _compressed_oops; } ++ bool compressed_class_pointers() const { return _compressed_class_ptrs; } + }; + + // Fixme +diff --git a/hotspot/src/share/vm/memory/metaspace.cpp b/hotspot/src/share/vm/memory/metaspace.cpp +index cf4a112..07bc47a 100644 +--- a/hotspot/src/share/vm/memory/metaspace.cpp ++++ b/hotspot/src/share/vm/memory/metaspace.cpp +@@ -3634,25 +3634,33 @@ void Metaspace::global_initialize() { + } + + #ifdef _LP64 +- if (cds_total + compressed_class_space_size() > UnscaledClassSpaceMax) { +- vm_exit_during_initialization("Unable to dump shared archive.", +- err_msg("Size of archive (" SIZE_FORMAT ") + compressed class space (" +- SIZE_FORMAT ") == total (" SIZE_FORMAT ") is larger than compressed " +- "klass limit: " SIZE_FORMAT, cds_total, compressed_class_space_size(), +- cds_total + compressed_class_space_size(), UnscaledClassSpaceMax)); +- } ++ if (UseCompressedClassPointers) { ++ if (cds_total + compressed_class_space_size() > UnscaledClassSpaceMax) { ++ vm_exit_during_initialization("Unable to dump shared archive.", ++ err_msg("Size of archive (" SIZE_FORMAT ") + compressed class space (" ++ SIZE_FORMAT ") == total (" SIZE_FORMAT ") is larger than compressed " ++ "klass limit: " SIZE_FORMAT, cds_total, compressed_class_space_size(), ++ cds_total + compressed_class_space_size(), UnscaledClassSpaceMax)); ++ } + +- // Set the compressed klass pointer base so that decoding of these pointers works +- // properly when creating the shared archive. +- assert(UseCompressedOops && UseCompressedClassPointers, +- "UseCompressedOops and UseCompressedClassPointers must be set"); +- Universe::set_narrow_klass_base((address)_space_list->current_virtual_space()->bottom()); +- if (TraceMetavirtualspaceAllocation && Verbose) { +- gclog_or_tty->print_cr("Setting_narrow_klass_base to Address: " PTR_FORMAT, +- _space_list->current_virtual_space()->bottom()); +- } ++ // Set the compressed klass pointer base so that decoding of these pointers works ++ // properly when creating the shared archive. ++ assert(UseCompressedOops && UseCompressedClassPointers, ++ "UseCompressedOops and UseCompressedClassPointers must be set"); ++ Universe::set_narrow_klass_base((address)_space_list->current_virtual_space()->bottom()); ++ if (TraceMetavirtualspaceAllocation && Verbose) { ++ gclog_or_tty->print_cr("Setting_narrow_klass_base to Address: " PTR_FORMAT, ++ _space_list->current_virtual_space()->bottom()); ++ } + +- Universe::set_narrow_klass_shift(0); ++ Universe::set_narrow_klass_shift(0); ++ } else { ++ if (cds_total > UnscaledClassSpaceMax) { ++ vm_exit_during_initialization("Unable to dump shared archive.", ++ err_msg("Size of archive (" SIZE_FORMAT ") is larger than compressed " ++ "klass limit: " SIZE_FORMAT, cds_total, UnscaledClassSpaceMax)); ++ } ++ } + #endif // _LP64 + #endif // INCLUDE_CDS + } else { +diff --git a/hotspot/src/share/vm/runtime/arguments.cpp b/hotspot/src/share/vm/runtime/arguments.cpp +index fba3d4b..b0b5414 100644 +--- a/hotspot/src/share/vm/runtime/arguments.cpp ++++ b/hotspot/src/share/vm/runtime/arguments.cpp +@@ -242,7 +242,9 @@ bool Arguments::init_shared_archive_paths() { + } + } + +- if (SharedArchiveFile != NULL) { ++ if (SharedArchiveFile == NULL) { ++ SharedArchivePath = get_default_shared_archive_path(); ++ } else { + int archives = num_archives(SharedArchiveFile); + if (is_dumping_archive()) { + if (archives > 1) { +@@ -4008,7 +4010,7 @@ jint Arguments::parse_options_environment_variable(const char* name, SysClassPat + return JNI_OK; + } + +-void Arguments::set_shared_spaces_flags() { ++jint Arguments::set_shared_spaces_flags() { + if (DumpSharedSpaces) { + if (FailOverToOldVerifier) { + // Don't fall back to the old verifier on verification failure. If a +@@ -4022,22 +4024,16 @@ void Arguments::set_shared_spaces_flags() { + warning("cannot dump shared archive while using shared archive"); + } + UseSharedSpaces = false; +-#ifdef _LP64 +- if (!UseCompressedOops || !UseCompressedClassPointers) { +- vm_exit_during_initialization( +- "Cannot dump shared archive when UseCompressedOops or UseCompressedClassPointers is off.", NULL); +- } +- } else { +- if (!UseCompressedOops || !UseCompressedClassPointers) { +- no_shared_spaces("UseCompressedOops and UseCompressedClassPointers must be on for UseSharedSpaces."); +- } +-#endif + } + + #if INCLUDE_CDS + // Initialize shared archive paths which could include both base and dynamic archive paths +- init_shared_archive_paths(); ++ // This must be after set_ergonomics_flags() called so flag UseCompressedOops is set properly. ++ if(!init_shared_archive_paths()) { ++ return JNI_ENOMEM; ++ } + #endif // INCLUDE_CDS ++ return JNI_OK; + } + + #if !INCLUDE_ALL_GCS +@@ -4065,25 +4061,14 @@ char* Arguments::get_default_shared_archive_path() { + const size_t len = jvm_path_len + file_sep_len + 20; + default_archive_path = NEW_C_HEAP_ARRAY(char, len, mtInternal); + if (default_archive_path != NULL) { +- jio_snprintf(default_archive_path, len, "%s%sclasses.jsa", ++ jio_snprintf(default_archive_path, len, ++ UseCompressedClassPointers ? "%s%sclasses.jsa" : "%s%sclasses_nocoops.jsa", + jvm_path, os::file_separator()); + } + Arguments::set_is_default_jsa(true); + return default_archive_path; + } + +-// Sharing support +-// Construct the path to the archive +-static char* get_shared_archive_path() { +- char *shared_archive_path; +- if (SharedArchiveFile == NULL) { +- shared_archive_path = Arguments::get_default_shared_archive_path(); +- } else { +- shared_archive_path = os::strdup(SharedArchiveFile, mtInternal); +- } +- return shared_archive_path; +-} +- + + #ifndef PRODUCT + // Determine whether LogVMOutput should be implicitly turned on. +@@ -4221,13 +4206,6 @@ jint Arguments::parse(const JavaVMInitArgs* args) { + return result; + } + +- // Call get_shared_archive_path() here, after possible SharedArchiveFile option got parsed. +- SharedArchivePath = get_shared_archive_path(); +- if (SharedArchivePath == NULL) { +- return JNI_ENOMEM; +- } +- +- + // Set up VerifySharedSpaces + if (FLAG_IS_DEFAULT(VerifySharedSpaces) && SharedArchiveFile != NULL) { + VerifySharedSpaces = true; +@@ -4321,7 +4299,8 @@ jint Arguments::apply_ergo() { + // Set flags based on ergonomics. + set_ergonomics_flags(); + +- set_shared_spaces_flags(); ++ jint result = set_shared_spaces_flags(); ++ if (result != JNI_OK) return result; + + #if defined(SPARC) + // BIS instructions require 'membar' instruction regardless of the number +diff --git a/hotspot/src/share/vm/runtime/arguments.hpp b/hotspot/src/share/vm/runtime/arguments.hpp +index 65907eb..88741e8 100644 +--- a/hotspot/src/share/vm/runtime/arguments.hpp ++++ b/hotspot/src/share/vm/runtime/arguments.hpp +@@ -343,7 +343,7 @@ class Arguments : AllStatic { + static void set_use_compressed_klass_ptrs(); + static void select_gc(); + static void set_ergonomics_flags(); +- static void set_shared_spaces_flags(); ++ static jint set_shared_spaces_flags(); + // limits the given memory size by the maximum amount of memory this process is + // currently allowed to allocate or reserve. + static julong limit_by_allocatable_memory(julong size); +diff --git a/hotspot/test/runtime/CDSCompressedKPtrs/CDSCompressedKPtrsError.java b/hotspot/test/runtime/CDSCompressedKPtrs/CDSCompressedKPtrsError.java +deleted file mode 100644 +index 05b4ac9..0000000 +--- a/hotspot/test/runtime/CDSCompressedKPtrs/CDSCompressedKPtrsError.java ++++ /dev/null +@@ -1,93 +0,0 @@ +-/* +- * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. +- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +- * +- * This code is free software; you can redistribute it and/or modify it +- * under the terms of the GNU General Public License version 2 only, as +- * published by the Free Software Foundation. +- * +- * This code is distributed in the hope that it will be useful, but WITHOUT +- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +- * version 2 for more details (a copy is included in the LICENSE file that +- * accompanied this code). +- * +- * You should have received a copy of the GNU General Public License version +- * 2 along with this work; if not, write to the Free Software Foundation, +- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +- * +- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA +- * or visit www.oracle.com if you need additional information or have any +- * questions. +- */ +- +-/* +- * @test +- * @bug 8003424 +- * @summary Test that cannot use CDS if UseCompressedClassPointers is turned off. +- * @library /testlibrary +- * @run main CDSCompressedKPtrsError +- */ +- +-import com.oracle.java.testlibrary.*; +- +-public class CDSCompressedKPtrsError { +- public static void main(String[] args) throws Exception { +- ProcessBuilder pb; +- if (Platform.is64bit()) { +- pb = ProcessTools.createJavaProcessBuilder( +- "-XX:+UseCompressedOops", "-XX:+UseCompressedClassPointers", "-XX:+UnlockDiagnosticVMOptions", +- "-XX:SharedArchiveFile=./sample.jsa", "-Xshare:dump"); +- OutputAnalyzer output = new OutputAnalyzer(pb.start()); +- try { +- output.shouldContain("Loading classes to share"); +- output.shouldHaveExitValue(0); +- +- pb = ProcessTools.createJavaProcessBuilder( +- "-XX:-UseCompressedClassPointers", "-XX:-UseCompressedOops", +- "-XX:+UnlockDiagnosticVMOptions", "-XX:SharedArchiveFile=./sample.jsa", "-Xshare:on", "-version"); +- output = new OutputAnalyzer(pb.start()); +- output.shouldContain("Unable to use shared archive"); +- output.shouldHaveExitValue(0); +- +- pb = ProcessTools.createJavaProcessBuilder( +- "-XX:-UseCompressedClassPointers", "-XX:+UseCompressedOops", +- "-XX:+UnlockDiagnosticVMOptions", "-XX:SharedArchiveFile=./sample.jsa", "-Xshare:on", "-version"); +- output = new OutputAnalyzer(pb.start()); +- output.shouldContain("Unable to use shared archive"); +- output.shouldHaveExitValue(0); +- +- pb = ProcessTools.createJavaProcessBuilder( +- "-XX:+UseCompressedClassPointers", "-XX:-UseCompressedOops", +- "-XX:+UnlockDiagnosticVMOptions", "-XX:SharedArchiveFile=./sample.jsa", "-Xshare:on", "-version"); +- output = new OutputAnalyzer(pb.start()); +- output.shouldContain("Unable to use shared archive"); +- output.shouldHaveExitValue(0); +- +- } catch (RuntimeException e) { +- output.shouldContain("Unable to use shared archive"); +- output.shouldHaveExitValue(1); +- } +- +- // Test bad options with -Xshare:dump. +- pb = ProcessTools.createJavaProcessBuilder( +- "-XX:-UseCompressedOops", "-XX:+UseCompressedClassPointers", "-XX:+UnlockDiagnosticVMOptions", +- "-XX:SharedArchiveFile=./sample.jsa", "-Xshare:dump"); +- output = new OutputAnalyzer(pb.start()); +- output.shouldContain("Cannot dump shared archive"); +- +- pb = ProcessTools.createJavaProcessBuilder( +- "-XX:+UseCompressedOops", "-XX:-UseCompressedClassPointers", "-XX:+UnlockDiagnosticVMOptions", +- "-XX:SharedArchiveFile=./sample.jsa", "-Xshare:dump"); +- output = new OutputAnalyzer(pb.start()); +- output.shouldContain("Cannot dump shared archive"); +- +- pb = ProcessTools.createJavaProcessBuilder( +- "-XX:-UseCompressedOops", "-XX:-UseCompressedClassPointers", "-XX:+UnlockDiagnosticVMOptions", +- "-XX:SharedArchiveFile=./sample.jsa", "-Xshare:dump"); +- output = new OutputAnalyzer(pb.start()); +- output.shouldContain("Cannot dump shared archive"); +- +- } +- } +-} +diff --git a/hotspot/test/runtime/appcds/CommandLineFlagComboNegative.java b/hotspot/test/runtime/appcds/CommandLineFlagComboNegative.java +index 4fb965a..286893e 100644 +--- a/hotspot/test/runtime/appcds/CommandLineFlagComboNegative.java ++++ b/hotspot/test/runtime/appcds/CommandLineFlagComboNegative.java +@@ -1,5 +1,6 @@ + /* + * Copyright (c) 2014, 2018, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2022, Huawei Technologies Co., Ltd. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it +@@ -64,9 +65,9 @@ public class CommandLineFlagComboNegative { + testTable.add( new TestVector("-XX:ObjectAlignmentInBytes=64", "-XX:ObjectAlignmentInBytes=32", + "An error has occurred while processing the shared archive file", 1) ); + testTable.add( new TestVector("-XX:+UseCompressedOops", "-XX:-UseCompressedOops", +- "Class data sharing is inconsistent with other specified options", 1) ); ++ "The saved state of UseCompressedOops and UseCompressedClassPointers is different from runtime, CDS will be disabled", 1) ); + testTable.add( new TestVector("-XX:+UseCompressedClassPointers", "-XX:-UseCompressedClassPointers", +- "Class data sharing is inconsistent with other specified options", 1) ); ++ "The saved state of UseCompressedOops and UseCompressedClassPointers is different from runtime, CDS will be disabled", 1) ); + } + } + +diff --git a/hotspot/test/runtime/appcds/TestCombinedCompressedFlags.java b/hotspot/test/runtime/appcds/TestCombinedCompressedFlags.java +new file mode 100644 +index 0000000..6f0a3be +--- /dev/null ++++ b/hotspot/test/runtime/appcds/TestCombinedCompressedFlags.java +@@ -0,0 +1,192 @@ ++/* ++ * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2022, Huawei Technologies Co., Ltd. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ */ ++ ++/** ++ * @test ++ * @bug 8232069 ++ * @summary Testing different combination of CompressedOops and CompressedClassPointers ++ * @requires (vm.gc=="null") ++ * @library /testlibrary ++ * @compile test-classes/Hello.java ++ * @run main/othervm TestCombinedCompressedFlags ++ */ ++ ++import com.oracle.java.testlibrary.Platform; ++import com.oracle.java.testlibrary.OutputAnalyzer; ++import java.util.List; ++import java.util.ArrayList; ++ ++public class TestCombinedCompressedFlags { ++ public static String HELLO_STRING = "Hello World"; ++ public static String EXEC_ABNORMAL_MSG = "Unable to use shared archive."; ++ public static final int PASS = 0; ++ public static final int FAIL = 1; ++ ++ static class ConfArg { ++ public boolean useCompressedOops; // UseCompressedOops ++ public boolean useCompressedClassPointers; // UseCompressedClassPointers ++ public String msg; ++ public int code; ++ public ConfArg(boolean useCompressedOops, boolean useCompressedClassPointers, String msg, int code) { ++ this.useCompressedOops = useCompressedOops; ++ this.useCompressedClassPointers = useCompressedClassPointers; ++ this.msg = msg; ++ this.code = code; ++ } ++ } ++ ++ static class RunArg { ++ public ConfArg dumpArg; ++ public List execArgs; ++ public RunArg(ConfArg arg) { ++ dumpArg = arg; ++ initExecArgs(); ++ } ++ private void initExecArgs() { ++ /* The combinations have four cases. Note COOP off, CCPTR must be off ++ * UseCompressedOops UseCompressedClassPointers Result ++ * 1. ++ * dump: on on ++ * test: on on Pass ++ * on off Fail ++ * off on Fail ++ * off off Fail ++ * 2. ++ * dump: on off ++ * test: on off Pass ++ * on on Fail ++ * off on Pass ++ * off off Fail ++ * 3. ++ * dump: off on ++ * test: off on Pass ++ * off off Pass ++ * on on Fail ++ * on off Fail ++ * 4. ++ * dump: off off ++ * test: off off Pass ++ * off on Pass ++ * on on Fail ++ * on off Fail ++ **/ ++ execArgs = new ArrayList(); ++ if (dumpArg.useCompressedOops && dumpArg.useCompressedClassPointers) { ++ execArgs ++ .add(new ConfArg(true, true, HELLO_STRING, PASS)); ++ execArgs ++ .add(new ConfArg(true, false, EXEC_ABNORMAL_MSG, FAIL)); ++ execArgs ++ .add(new ConfArg(false, true, EXEC_ABNORMAL_MSG, FAIL)); ++ execArgs ++ .add(new ConfArg(false, false, EXEC_ABNORMAL_MSG, FAIL)); ++ ++ } else if(dumpArg.useCompressedOops && !dumpArg.useCompressedClassPointers) { ++ execArgs ++ .add(new ConfArg(true, false, HELLO_STRING, PASS)); ++ execArgs ++ .add(new ConfArg(true, true, EXEC_ABNORMAL_MSG, FAIL)); ++ execArgs ++ .add(new ConfArg(false, true, EXEC_ABNORMAL_MSG, FAIL)); ++ execArgs ++ .add(new ConfArg(false, false, EXEC_ABNORMAL_MSG, FAIL)); ++ ++ } else if (!dumpArg.useCompressedOops && dumpArg.useCompressedClassPointers) { ++ execArgs ++ .add(new ConfArg(false, false, HELLO_STRING, PASS)); ++ execArgs ++ .add(new ConfArg(false, true, HELLO_STRING, PASS)); ++ execArgs ++ .add(new ConfArg(true, true, EXEC_ABNORMAL_MSG, FAIL)); ++ execArgs ++ .add(new ConfArg(true, false, EXEC_ABNORMAL_MSG, FAIL)); ++ } else if (!dumpArg.useCompressedOops && !dumpArg.useCompressedClassPointers) { ++ execArgs ++ .add(new ConfArg(false, false, HELLO_STRING, PASS)); ++ execArgs ++ .add(new ConfArg(false, true, HELLO_STRING, PASS)); ++ execArgs ++ .add(new ConfArg(true, true, EXEC_ABNORMAL_MSG, FAIL)); ++ execArgs ++ .add(new ConfArg(true, false, EXEC_ABNORMAL_MSG, FAIL)); ++ } ++ } ++ } ++ ++ public static String getCompressedOopsArg(boolean on) { ++ if (on) return "-XX:+UseCompressedOops"; ++ else return "-XX:-UseCompressedOops"; ++ } ++ ++ public static String getCompressedClassPointersArg(boolean on) { ++ if (on) return "-XX:+UseCompressedClassPointers"; ++ else return "-XX:-UseCompressedClassPointers"; ++ } ++ ++ public static List runList; ++ ++ public static void configureRunArgs() { ++ runList = new ArrayList(); ++ runList ++ .add(new RunArg(new ConfArg(true, true, null, PASS))); ++ runList ++ .add(new RunArg(new ConfArg(true, false, null, PASS))); ++ runList ++ .add(new RunArg(new ConfArg(false, true, null, PASS))); ++ runList ++ .add(new RunArg(new ConfArg(false, false, null, PASS))); ++ } ++ ++ public static void main(String[] args) throws Exception { ++ if (!Platform.is64bit()) { ++ System.out.println("Test case not applicable on 32-bit platforms"); ++ return; ++ ++ } ++ ++ String helloJar = JarBuilder.build("hello", "Hello"); ++ configureRunArgs(); ++ OutputAnalyzer out; ++ for (RunArg t: runList) { ++ out = TestCommon ++ .dump(helloJar, ++ new String[] {"Hello"}, ++ getCompressedOopsArg(t.dumpArg.useCompressedOops), ++ getCompressedClassPointersArg(t.dumpArg.useCompressedClassPointers)); ++ out.shouldContain("total : "); ++ out.shouldHaveExitValue(0); ++ ++ for (ConfArg c : t.execArgs) { ++ out = TestCommon.exec(helloJar, ++ "-cp", ++ helloJar, ++ getCompressedOopsArg(c.useCompressedOops), ++ getCompressedClassPointersArg(c.useCompressedClassPointers), ++ "Hello"); ++ out.shouldContain(c.msg); ++ out.shouldHaveExitValue(c.code); ++ } ++ } ++ } ++} +diff --git a/jdk/make/BuildJdk.gmk b/jdk/make/BuildJdk.gmk +index bb8ea8a..6707456 100644 +--- a/jdk/make/BuildJdk.gmk ++++ b/jdk/make/BuildJdk.gmk +@@ -106,8 +106,10 @@ images: + ifeq ($(BUILD_CDS_ARCHIVE), true) + echo Creating CDS archive for jdk image + $(JDK_IMAGE_DIR)/bin/java -Xshare:dump -Xmx128M -Xms128M -XX:ParallelGCThreads=1 -Xint $(LOG_INFO) ++ $(JDK_IMAGE_DIR)/bin/java -Xshare:dump -Xmx128M -Xms128M -XX:ParallelGCThreads=1 -Xint -XX:-UseCompressedOops $(LOG_INFO) + echo Creating CDS archive for jre image + $(JRE_IMAGE_DIR)/bin/java -Xshare:dump -Xmx128M -Xms128M -XX:ParallelGCThreads=1 -Xint $(LOG_INFO) ++ $(JDK_IMAGE_DIR)/bin/java -Xshare:dump -Xmx128M -Xms128M -XX:ParallelGCThreads=1 -Xint -XX:-UseCompressedOops $(LOG_INFO) + endif + + +-- +1.8.3.1 diff --git a/8242181-Show-source-information-when-printing-native.patch b/8242181-Show-source-information-when-printing-native.patch new file mode 100644 index 0000000000000000000000000000000000000000..ca02b21294b61fb42c605bcde91914e464d16713 --- /dev/null +++ b/8242181-Show-source-information-when-printing-native.patch @@ -0,0 +1,2830 @@ +From 24dedc988ac599b3191f6a69c1bce35fcc6bf748 Mon Sep 17 00:00:00 2001 +From: eapen +Date: Thu, 15 Dec 2022 20:05:20 +0800 +Subject: [PATCH 23/33] I68TO2: 8242181: Show source information when printing native + stack traces in hs_err files +--- + hotspot/src/share/vm/runtime/globals.hpp | 3 + + hotspot/src/share/vm/utilities/debug.cpp | 9 + + hotspot/src/share/vm/utilities/decoder.cpp | 9 + + hotspot/src/share/vm/utilities/decoder.hpp | 17 + + hotspot/src/share/vm/utilities/decoder_elf.cpp | 39 + + hotspot/src/share/vm/utilities/decoder_elf.hpp | 2 + + hotspot/src/share/vm/utilities/elfFile.cpp | 1510 +++++++++++++++++++- + hotspot/src/share/vm/utilities/elfFile.hpp | 732 +++++++++- + hotspot/src/share/vm/utilities/nativeCallStack.cpp | 11 +- + hotspot/src/share/vm/utilities/vmError.cpp | 6 + + hotspot/src/share/vm/utilities/vmError.hpp | 3 + + jdk/test/jdk/java/dwarf/TestDwarf.java | 240 ++++ + 12 files changed, 2563 insertions(+), 18 deletions(-) + create mode 100644 jdk/test/jdk/java/dwarf/TestDwarf.java + +diff --git a/hotspot/src/share/vm/runtime/globals.hpp b/hotspot/src/share/vm/runtime/globals.hpp +index 64d40e0..d1e3cda 100644 +--- a/hotspot/src/share/vm/runtime/globals.hpp ++++ b/hotspot/src/share/vm/runtime/globals.hpp +@@ -546,6 +546,9 @@ class CommandLineFlags { + develop(bool, CleanChunkPoolAsync, falseInEmbedded, \ + "Clean the chunk pool asynchronously") \ + \ ++ develop(intx, TraceDwarfLevel, 0, \ ++ "Debug levels for the dwarf parser") \ ++ \ + /* Temporary: See 6948537 */ \ + experimental(bool, UseMemSetInBOT, true, \ + "(Unstable) uses memset in BOT updates in GC code") \ +diff --git a/hotspot/src/share/vm/utilities/debug.cpp b/hotspot/src/share/vm/utilities/debug.cpp +index 8cea16d..6a9310e 100644 +--- a/hotspot/src/share/vm/utilities/debug.cpp ++++ b/hotspot/src/share/vm/utilities/debug.cpp +@@ -51,6 +51,7 @@ + #include "utilities/events.hpp" + #include "utilities/top.hpp" + #include "utilities/vmError.hpp" ++#include "utilities/decoder.hpp" + #ifdef TARGET_OS_FAMILY_linux + # include "os_linux.inline.hpp" + #endif +@@ -751,7 +752,15 @@ void print_native_stack(outputStream* st, frame fr, Thread* t, char* buf, int bu + int count = 0; + while (count++ < StackPrintLimit) { + fr.print_on_error(st, buf, buf_size); ++ ++ char filename[128]; ++ int line_no; ++ if (Decoder::get_source_info(fr.pc(), filename, sizeof(filename), &line_no, count != 1)) { ++ st->print(" (%s:%d)", filename, line_no); ++ } ++ + st->cr(); ++ + // Compiled code may use EBP register on x86 so it looks like + // non-walkable C frame. Use frame.sender() for java frames. + if (t && t->is_Java_thread()) { +diff --git a/hotspot/src/share/vm/utilities/decoder.cpp b/hotspot/src/share/vm/utilities/decoder.cpp +index 7ed913a..ae58bf5 100644 +--- a/hotspot/src/share/vm/utilities/decoder.cpp ++++ b/hotspot/src/share/vm/utilities/decoder.cpp +@@ -143,6 +143,15 @@ bool Decoder::can_decode_C_frame_in_vm() { + return decoder->can_decode_C_frame_in_vm(); + } + ++bool Decoder::get_source_info(address pc, char* filename, size_t filename_len, int* line, bool is_pc_after_call) { ++ if (VMError::is_error_reported_in_current_thread()) { ++ return get_error_handler_instance()->get_source_info(pc, filename, filename_len, line, is_pc_after_call); ++ } else { ++ MutexLockerEx locker(shared_decoder_lock(), Mutex::_no_safepoint_check_flag); ++ return get_shared_instance()->get_source_info(pc, filename, filename_len, line, is_pc_after_call); ++ } ++} ++ + /* + * Shutdown shared decoder and replace it with + * _do_nothing_decoder. Do nothing with error handler +diff --git a/hotspot/src/share/vm/utilities/decoder.hpp b/hotspot/src/share/vm/utilities/decoder.hpp +index c6c09e3..e83b87f 100644 +--- a/hotspot/src/share/vm/utilities/decoder.hpp ++++ b/hotspot/src/share/vm/utilities/decoder.hpp +@@ -68,6 +68,11 @@ public: + return (status > 0); + } + ++ // Get filename and line number information. ++ virtual bool get_source_info(address pc, char* filename, size_t filename_len, int* line, bool is_pc_after_call) { ++ return false; ++ } ++ + protected: + decoder_status _decoder_status; + }; +@@ -97,6 +102,11 @@ public: + virtual bool can_decode_C_frame_in_vm() const { + return false; + } ++ ++ // Get filename and line number information. ++ virtual bool get_source_info(address pc, char* filename, size_t filename_len, int* line, bool is_pc_after_call) { ++ return false; ++ } + }; + + +@@ -107,6 +117,13 @@ public: + static bool demangle(const char* symbol, char* buf, int buflen); + static bool can_decode_C_frame_in_vm(); + ++ // Attempts to retrieve source file name and line number associated with a pc. ++ // If filename != NULL, points to a buffer of size filename_len which will receive the ++ // file name. File name will be silently truncated if output buffer is too small. ++ // If is_pc_after_call is true, then pc is treated as pointing to the next instruction ++ // after a call. The source information for the call instruction is fetched in that case. ++ static bool get_source_info(address pc, char* filename, size_t filename_len, int* line, bool is_pc_after_call = false); ++ + // shutdown shared instance + static void shutdown(); + protected: +diff --git a/hotspot/src/share/vm/utilities/decoder_elf.cpp b/hotspot/src/share/vm/utilities/decoder_elf.cpp +index 9730883..bb72ce1 100644 +--- a/hotspot/src/share/vm/utilities/decoder_elf.cpp ++++ b/hotspot/src/share/vm/utilities/decoder_elf.cpp +@@ -73,4 +73,43 @@ ElfFile* ElfDecoder::get_elf_file(const char* filepath) { + + return file; + } ++ ++bool ElfDecoder::get_source_info(address pc, char* filename, size_t filename_len, int* line, bool is_pc_after_call) { ++ assert(filename != NULL && filename_len > 0 && line != NULL, "Argument error"); ++ filename[0] = '\0'; ++ *line = -1; ++ ++ char filepath[JVM_MAXPATHLEN]; ++ filepath[JVM_MAXPATHLEN - 1] = '\0'; ++ int offset_in_library = -1; ++ if (!os::dll_address_to_library_name(pc, filepath, sizeof(filepath), &offset_in_library)) { ++ // Method not found. offset_in_library should not overflow. ++ DWARF_LOG_ERROR("Did not find library for address " INTPTR_FORMAT, p2i(pc)) ++ return false; ++ } ++ ++ if (filepath[JVM_MAXPATHLEN - 1] != '\0') { ++ DWARF_LOG_ERROR("File path is too large to fit into buffer of size %d", JVM_MAXPATHLEN); ++ return false; ++ } ++ ++ const uint32_t unsigned_offset_in_library = (uint32_t)offset_in_library; ++ ++ ElfFile* file = get_elf_file(filepath); ++ if (file == NULL) { ++ return false; ++ } ++ DWARF_LOG_INFO("##### Find filename and line number for offset " PTR32_FORMAT " in library %s #####", ++ unsigned_offset_in_library, filepath); ++ ++ if (!file->get_source_info(unsigned_offset_in_library, filename, filename_len, line, is_pc_after_call)) { ++ return false; ++ } ++ ++ DWARF_LOG_SUMMARY("pc: " INTPTR_FORMAT ", offset: " PTR32_FORMAT ", filename: %s, line: %u", ++ p2i(pc), offset_in_library, filename, *line); ++ DWARF_LOG_INFO("\n") // To structure the debug output better. ++ return true; ++} ++ + #endif // !_WINDOWS && !__APPLE__ +diff --git a/hotspot/src/share/vm/utilities/decoder_elf.hpp b/hotspot/src/share/vm/utilities/decoder_elf.hpp +index e92c958..5551f42 100644 +--- a/hotspot/src/share/vm/utilities/decoder_elf.hpp ++++ b/hotspot/src/share/vm/utilities/decoder_elf.hpp +@@ -48,6 +48,8 @@ public: + return false; + } + ++ bool get_source_info(address pc, char* buf, size_t buflen, int* line, bool is_pc_after_call); ++ + private: + ElfFile* get_elf_file(const char* filepath); + +diff --git a/hotspot/src/share/vm/utilities/elfFile.cpp b/hotspot/src/share/vm/utilities/elfFile.cpp +index ac943bd..81bd441 100644 +--- a/hotspot/src/share/vm/utilities/elfFile.cpp ++++ b/hotspot/src/share/vm/utilities/elfFile.cpp +@@ -32,12 +32,41 @@ + #include + + #include "memory/allocation.inline.hpp" ++#include "memory/resourceArea.hpp" + #include "utilities/decoder.hpp" + #include "utilities/elfFile.hpp" + #include "utilities/elfFuncDescTable.hpp" + #include "utilities/elfStringTable.hpp" + #include "utilities/elfSymbolTable.hpp" + ++const char* ElfFile::USR_LIB_DEBUG_DIRECTORY = "/usr/lib/debug"; ++ ++bool FileReader::read(void* buf, size_t size) { ++ assert(buf != NULL, "no buffer"); ++ assert(size > 0, "no space"); ++ return fread(buf, size, 1, _fd) == 1; ++} ++ ++size_t FileReader::read_buffer(void* buf, size_t size) { ++ assert(buf != NULL, "no buffer"); ++ assert(size > 0, "no space"); ++ return fread(buf, 1, size, _fd); ++} ++ ++bool FileReader::set_position(long offset) { ++ return fseek(_fd, offset, SEEK_SET) == 0; ++} ++ ++MarkedFileReader::MarkedFileReader(FILE* fd) : FileReader(fd) { ++ _marked_pos = ftell(fd); ++} ++ ++MarkedFileReader::~MarkedFileReader() { ++ if (_marked_pos != -1) { ++ set_position(_marked_pos); ++ } ++} ++ + + ElfFile::ElfFile(const char* filepath) { + assert(filepath, "null file path"); +@@ -47,6 +76,8 @@ ElfFile::ElfFile(const char* filepath) { + m_funcDesc_table = NULL; + m_next = NULL; + m_status = NullDecoder::no_error; ++ m_shdr_string_table = NULL; ++ m_dwarf_file = NULL; + + int len = strlen(filepath) + 1; + m_filepath = (const char*)os::malloc(len * sizeof(char), mtInternal); +@@ -83,6 +114,16 @@ ElfFile::~ElfFile() { + if (m_next != NULL) { + delete m_next; + } ++ ++ if (m_shdr_string_table != NULL) { ++ delete m_shdr_string_table; ++ } ++ ++ if (m_dwarf_file != NULL) { ++ delete m_dwarf_file; ++ m_dwarf_file = NULL; ++ } ++ + }; + + +@@ -128,7 +169,12 @@ bool ElfFile::load_tables() { + m_status = NullDecoder::out_of_memory; + return false; + } +- add_string_table(table); ++ if (index == m_elfHdr.e_shstrndx) { ++ assert(m_shdr_string_table == NULL, "Only set once"); ++ m_shdr_string_table = table; ++ } else { ++ add_string_table(table); ++ } + } else if (shdr.sh_type == SHT_SYMTAB || shdr.sh_type == SHT_DYNSYM) { + // symbol tables + ElfSymbolTable* table = new (std::nothrow) ElfSymbolTable(m_file, shdr); +@@ -270,4 +316,1466 @@ bool ElfFile::specifies_noexecstack() { + } + #endif + ++bool ElfFile::get_source_info(const uint32_t offset_in_library, char* filename, const size_t filename_len, ++ int* line, bool is_pc_after_call) { ++ ResourceMark rm; ++ if (!load_dwarf_file()) { ++ // Some ELF libraries do not provide separate .debuginfo files. Check if the current ELF file has the required ++ // DWARF sections. If so, treat the current ELF file as DWARF file. ++ if (!is_valid_dwarf_file()) { ++ DWARF_LOG_ERROR("Failed to load DWARF file for library %s or find DWARF sections directly inside it.", m_filepath); ++ return false; ++ } ++ DWARF_LOG_INFO("No separate .debuginfo file for library %s. It already contains the required DWARF sections.", ++ m_filepath); ++ if (!create_new_dwarf_file(m_filepath)) { ++ return false; ++ } ++ } ++ ++ // Store result in filename and line pointer. ++ if (!m_dwarf_file->get_filename_and_line_number(offset_in_library, filename, filename_len, line, is_pc_after_call)) { ++ DWARF_LOG_ERROR("Failed to retrieve file and line number information for %s at offset: " PTR32_FORMAT, m_filepath, ++ offset_in_library); ++ return false; ++ } ++ return true; ++} ++ ++bool ElfFile::is_valid_dwarf_file() const { ++ Elf_Shdr shdr; ++ return read_section_header(".debug_abbrev", shdr) && read_section_header(".debug_aranges", shdr) ++ && read_section_header(".debug_info", shdr) && read_section_header(".debug_line", shdr); ++} ++ ++// (1) Load the debuginfo file from the path specified in this ELF file in the .gnu_debuglink section. ++// Adapted from Serviceability Agent. ++bool ElfFile::load_dwarf_file() { ++ if (m_dwarf_file != NULL) { ++ return true; // Already opened. ++ } ++ ++ DebugInfo debug_info; ++ if (!read_debug_info(&debug_info)) { ++ DWARF_LOG_DEBUG("Could not read debug info from .gnu_debuglink section"); ++ return false; ++ } ++ ++ DwarfFilePath dwarf_file_path(debug_info); ++ return load_dwarf_file_from_same_directory(dwarf_file_path) ++ || load_dwarf_file_from_env_var_path(dwarf_file_path) ++ || load_dwarf_file_from_debug_sub_directory(dwarf_file_path) ++ || load_dwarf_file_from_usr_lib_debug(dwarf_file_path); ++} ++ ++// Read .gnu_debuglink section which contains: ++// Filename (null terminated) + 0-3 padding bytes (to 4 byte align) + CRC (4 bytes) ++bool ElfFile::read_debug_info(DebugInfo* debug_info) const { ++ Elf_Shdr shdr; ++ if (!read_section_header(".gnu_debuglink", shdr)) { ++ DWARF_LOG_DEBUG("Failed to read the .gnu_debuglink header."); ++ return false; ++ } ++ ++ if (shdr.sh_size % 4 != 0) { ++ DWARF_LOG_ERROR(".gnu_debuglink section is not 4 byte aligned (i.e. file is corrupted)"); ++ return false; ++ } ++ ++ MarkedFileReader mfd(fd()); ++ if (!mfd.has_mark() || !mfd.set_position(m_elfHdr.e_shoff)) { ++ return false; ++ } ++ ++ uint64_t filename_max_len = shdr.sh_size - DebugInfo::CRC_LEN; ++ mfd.set_position(shdr.sh_offset); ++ if (!mfd.read(&debug_info->_dwarf_filename, filename_max_len)) { ++ return false; ++ } ++ ++ if (debug_info->_dwarf_filename[filename_max_len - 1] != '\0') { ++ // Filename not null-terminated (i.e. overflowed). ++ DWARF_LOG_ERROR("Dwarf filename is not null-terminated"); ++ return false; ++ } ++ ++ return mfd.read(&debug_info->_crc, DebugInfo::CRC_LEN); ++} ++ ++bool ElfFile::DwarfFilePath::set(const char* src) { ++ int bytes_written = jio_snprintf(_path, MAX_DWARF_PATH_LENGTH, "%s", src); ++ if (bytes_written < 0 || bytes_written >= MAX_DWARF_PATH_LENGTH) { ++ DWARF_LOG_ERROR("Dwarf file path buffer is too small"); ++ return false; ++ } ++ update_null_terminator_index(); ++ return check_valid_path(); // Sanity check ++} ++ ++bool ElfFile::DwarfFilePath::set_after_last_slash(const char* src) { ++ char* last_slash = strrchr(_path, '/'); ++ if (last_slash == NULL) { ++ // Should always find a slash. ++ return false; ++ } ++ ++ uint16_t index_after_slash = (uint16_t)(last_slash + 1 - _path); ++ return copy_to_path_index(index_after_slash, src); ++} ++ ++bool ElfFile::DwarfFilePath::append(const char* src) { ++ return copy_to_path_index(_null_terminator_index, src); ++} ++ ++bool ElfFile::DwarfFilePath::copy_to_path_index(uint16_t index_in_path, const char* src) { ++ if (index_in_path >= MAX_DWARF_PATH_LENGTH - 1) { ++ // Should not override '\0' at _path[MAX_DWARF_PATH_LENGTH - 1] ++ DWARF_LOG_ERROR("Dwarf file path buffer is too small"); ++ return false; ++ } ++ ++ uint16_t max_len = MAX_DWARF_PATH_LENGTH - index_in_path; ++ int bytes_written = jio_snprintf(_path + index_in_path, max_len, "%s", src); ++ if (bytes_written < 0 || bytes_written >= max_len) { ++ DWARF_LOG_ERROR("Dwarf file path buffer is too small"); ++ return false; ++ } ++ update_null_terminator_index(); ++ return check_valid_path(); // Sanity check ++} ++ ++// Try to load the dwarf file from the same directory as the library file. ++bool ElfFile::load_dwarf_file_from_same_directory(DwarfFilePath& dwarf_file_path) { ++ if (!dwarf_file_path.set(m_filepath) ++ || !dwarf_file_path.set_filename_after_last_slash()) { ++ return false; ++ } ++ return open_valid_debuginfo_file(dwarf_file_path); ++} ++ ++// Try to load the dwarf file from a user specified path in environmental variable _JVM_DWARF_PATH. ++bool ElfFile::load_dwarf_file_from_env_var_path(DwarfFilePath& dwarf_file_path) { ++ const char* dwarf_path_from_env = ::getenv("_JVM_DWARF_PATH"); ++ if (dwarf_path_from_env != NULL) { ++ DWARF_LOG_DEBUG("_JVM_DWARF_PATH: %s", dwarf_path_from_env); ++ return (load_dwarf_file_from_env_path_folder(dwarf_file_path, dwarf_path_from_env, "/lib/server/") ++ || load_dwarf_file_from_env_path_folder(dwarf_file_path, dwarf_path_from_env, "/lib/") ++ || load_dwarf_file_from_env_path_folder(dwarf_file_path, dwarf_path_from_env, "/bin/") ++ || load_dwarf_file_from_env_path_folder(dwarf_file_path, dwarf_path_from_env, "/")); ++ } ++ return false; ++} ++ ++bool ElfFile::load_dwarf_file_from_env_path_folder(DwarfFilePath& dwarf_file_path, const char* dwarf_path_from_env, ++ const char* folder) { ++ if (!dwarf_file_path.set(dwarf_path_from_env) ++ || !dwarf_file_path.append(folder) ++ || !dwarf_file_path.append(dwarf_file_path.filename())) { ++ DWARF_LOG_ERROR("Dwarf file path buffer is too small"); ++ return false; ++ } ++ return open_valid_debuginfo_file(dwarf_file_path); ++} ++ ++// Try to load the dwarf file from a subdirectory named .debug within the directory of the library file. ++bool ElfFile::load_dwarf_file_from_debug_sub_directory(DwarfFilePath& dwarf_file_path) { ++ if (!dwarf_file_path.set(m_filepath) ++ || !dwarf_file_path.set_after_last_slash(".debug/") ++ || !dwarf_file_path.append(dwarf_file_path.filename())) { ++ DWARF_LOG_ERROR("Dwarf file path buffer is too small"); ++ return false; ++ } ++ return open_valid_debuginfo_file(dwarf_file_path); ++} ++ ++// Try to load the dwarf file from /usr/lib/debug + the full pathname. ++bool ElfFile::load_dwarf_file_from_usr_lib_debug(DwarfFilePath& dwarf_file_path) { ++ if (!dwarf_file_path.set(USR_LIB_DEBUG_DIRECTORY) ++ || !dwarf_file_path.append(m_filepath) ++ || !dwarf_file_path.set_filename_after_last_slash()) { ++ DWARF_LOG_ERROR("Dwarf file path buffer is too small"); ++ return false; ++ } ++ return open_valid_debuginfo_file(dwarf_file_path); ++} ++ ++bool ElfFile::read_section_header(const char* name, Elf_Shdr& hdr) const { ++ if (m_shdr_string_table == NULL) { ++ assert(false, "section header string table should be loaded"); ++ return false; ++ } ++ const uint8_t buf_len = 24; ++ char buf[buf_len]; ++ size_t len = strlen(name) + 1; ++ if (len > buf_len) { ++ DWARF_LOG_ERROR("Section header name buffer is too small: Required: %zu, Found: %d", len, buf_len); ++ return false; ++ } ++ ++ MarkedFileReader mfd(fd()); ++ if (!mfd.has_mark() || !mfd.set_position(m_elfHdr.e_shoff)) { ++ return false; ++ } ++ ++ for (int index = 0; index < m_elfHdr.e_shnum; index++) { ++ if (!mfd.read((void*)&hdr, sizeof(hdr))) { ++ return false; ++ } ++ if (m_shdr_string_table->string_at(hdr.sh_name, buf, buf_len)) { ++ if (strncmp(buf, name, buf_len) == 0) { ++ return true; ++ } ++ } ++ } ++ return false; ++} ++ ++// Taken from https://sourceware.org/gdb/current/onlinedocs/gdb/Separate-Debug-Files.html#Separate-Debug-Files ++static const uint32_t crc32_table[256] = { ++ 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, ++ 0x706af48f, 0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, ++ 0xe0d5e91e, 0x97d2d988, 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, ++ 0x90bf1d91, 0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de, ++ 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, 0x136c9856, ++ 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9, ++ 0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, ++ 0xa2677172, 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, ++ 0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, ++ 0x45df5c75, 0xdcd60dcf, 0xabd13d59, 0x26d930ac, 0x51de003a, ++ 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, 0xcfba9599, ++ 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, ++ 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, ++ 0x01db7106, 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, ++ 0x9fbfe4a5, 0xe8b8d433, 0x7807c9a2, 0x0f00f934, 0x9609a88e, ++ 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01, ++ 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, 0x6c0695ed, ++ 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950, ++ 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, ++ 0xfbd44c65, 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, ++ 0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, ++ 0x346ed9fc, 0xad678846, 0xda60b8d0, 0x44042d73, 0x33031de5, ++ 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa, 0xbe0b1010, ++ 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f, ++ 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, ++ 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, ++ 0x03b6e20c, 0x74b1d29a, 0xead54739, 0x9dd277af, 0x04db2615, ++ 0x73dc1683, 0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8, ++ 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, 0xf00f9344, ++ 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb, ++ 0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, ++ 0x67dd4acc, 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, ++ 0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, ++ 0xa6bc5767, 0x3fb506dd, 0x48b2364b, 0xd80d2bda, 0xaf0a1b4c, ++ 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, 0x316e8eef, ++ 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, ++ 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, ++ 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, ++ 0x2cd99e8b, 0x5bdeae1d, 0x9b64c2b0, 0xec63f226, 0x756aa39c, ++ 0x026d930a, 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713, ++ 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, 0x92d28e9b, ++ 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242, ++ 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, ++ 0x18b74777, 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, ++ 0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45, 0xa00ae278, ++ 0xd70dd2ee, 0x4e048354, 0x3903b3c2, 0xa7672661, 0xd06016f7, ++ 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc, 0x40df0b66, ++ 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9, ++ 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, ++ 0xcdd70693, 0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, ++ 0x5d681b02, 0x2a6f2b94, 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, ++ 0x2d02ef8d ++ }; ++ ++bool ElfFile::open_valid_debuginfo_file(const DwarfFilePath& dwarf_file_path) { ++ if (m_dwarf_file != NULL) { ++ // Already opened. ++ return true; ++ } ++ ++ const char* filepath = dwarf_file_path.path(); ++ FILE* file = fopen(filepath, "r"); ++ if (file == NULL) { ++ DWARF_LOG_DEBUG("Could not open dwarf file %s ", filepath); ++ return false; ++ } ++ ++ uint32_t file_crc = get_file_crc(file); ++ fclose(file); // Close it here to reopen it again when the DwarfFile object is created below. ++ ++ if (dwarf_file_path.crc() != file_crc) { ++ // Must be equal, otherwise the file is corrupted. ++ DWARF_LOG_ERROR("CRC did not match. Expected: " PTR32_FORMAT ", found: " PTR32_FORMAT, dwarf_file_path.crc(), ++ file_crc); ++ return false; ++ } ++ return create_new_dwarf_file(filepath); ++} ++ ++uint32_t ElfFile::get_file_crc(FILE* const file) { ++ uint32_t file_crc = 0; ++ uint8_t buffer[8 * 1024]; ++ MarkedFileReader reader(file); ++ while (true) { ++ size_t len = reader.read_buffer(buffer, sizeof(buffer)); ++ if (len == 0) { ++ break; ++ } ++ file_crc = gnu_debuglink_crc32(file_crc, buffer, len); ++ } ++ return file_crc; ++} ++ ++// The CRC used in gnu_debuglink, retrieved from ++// http://sourceware.org/gdb/current/onlinedocs/gdb/Separate-Debug-Files.html#Separate-Debug-Files. ++uint32_t ElfFile::gnu_debuglink_crc32(uint32_t crc, uint8_t* buf, const size_t len) { ++ crc = ~crc; ++ for (uint8_t* end = buf + len; buf < end; buf++) { ++ crc = crc32_table[(crc ^ *buf) & 0xffu] ^ (crc >> 8u); ++ } ++ return ~crc; ++} ++ ++bool ElfFile::create_new_dwarf_file(const char* filepath) { ++ DWARF_LOG_SUMMARY("Open DWARF file: %s", filepath); ++ m_dwarf_file = new (std::nothrow) DwarfFile(filepath); ++ if (m_dwarf_file == NULL) { ++ DWARF_LOG_ERROR("Failed to create new DwarfFile object for %s.", m_filepath); ++ return false; ++ } ++ if (!m_dwarf_file->is_valid_dwarf_file()) { ++ DWARF_LOG_ERROR("Did not find required DWARF sections in %s", filepath); ++ return false; ++ } ++ return true; ++} ++ ++// Starting point of reading line number and filename information from the DWARF file. ++bool DwarfFile::get_filename_and_line_number(const uint32_t offset_in_library, char* filename, const size_t filename_len, ++ int* line, const bool is_pc_after_call) { ++ DebugAranges debug_aranges(this); ++ uint32_t compilation_unit_offset = 0; // 4-bytes for 32-bit DWARF ++ if (!debug_aranges.find_compilation_unit_offset(offset_in_library, &compilation_unit_offset)) { ++ DWARF_LOG_ERROR("Failed to find .debug_info offset for the compilation unit."); ++ return false; ++ } ++ DWARF_LOG_INFO(".debug_info offset: " PTR32_FORMAT, compilation_unit_offset); ++ ++ CompilationUnit compilation_unit(this, compilation_unit_offset); ++ uint32_t debug_line_offset = 0; // 4-bytes for 32-bit DWARF ++ if (!compilation_unit.find_debug_line_offset(&debug_line_offset)) { ++ DWARF_LOG_ERROR("Failed to find .debug_line offset for the line number program."); ++ return false; ++ } ++ DWARF_LOG_INFO(".debug_line offset: " PTR32_FORMAT, debug_line_offset); ++ ++ LineNumberProgram line_number_program(this, offset_in_library, debug_line_offset, is_pc_after_call); ++ if (!line_number_program.find_filename_and_line_number(filename, filename_len, line)) { ++ DWARF_LOG_ERROR("Failed to process the line number program correctly."); ++ return false; ++ } ++ return true; ++} ++ ++// (2) The .debug_aranges section contains a number of entries/sets. Each set contains one or multiple address range descriptors of the ++// form [beginning_address, beginning_address+length). Start reading these sets and their descriptors until we find one that contains ++// 'offset_in_library'. Read the debug_info_offset field from the header of this set which defines the offset for the compilation unit. ++// This process is described in section 6.1.2 of the DWARF 4 spec. ++bool DwarfFile::DebugAranges::find_compilation_unit_offset(const uint32_t offset_in_library, uint32_t* compilation_unit_offset) { ++ if (!read_section_header()) { ++ DWARF_LOG_ERROR("Failed to read a .debug_aranges header."); ++ return false; ++ } ++ ++ DebugArangesSetHeader set_header; ++ bool found_matching_set = false; ++ while (_reader.has_bytes_left()) { ++ // Read multiple sets and therefore multiple headers. ++ if (!read_set_header(set_header)) { ++ DWARF_LOG_ERROR("Failed to read a .debug_aranges header."); ++ return false; ++ } ++ ++ if (!read_address_descriptors(offset_in_library, found_matching_set)) { ++ return false; ++ } ++ ++ if (found_matching_set) { ++ // Found the correct set, read the debug_info_offset from the header of this set. ++ DWARF_LOG_INFO(".debug_aranges offset: " PTR32_FORMAT, (uint32_t)_reader.get_position()); ++ *compilation_unit_offset = set_header._debug_info_offset; ++ return true; ++ } ++ } ++ ++ assert(false, "No address descriptor found containing offset_in_library."); ++ return false; ++} ++ ++bool DwarfFile::DebugAranges::read_section_header() { ++ Elf_Shdr shdr; ++ if (!_dwarf_file->read_section_header(".debug_aranges", shdr)) { ++ return false; ++ } ++ ++ _section_start_address = shdr.sh_offset; ++ _reader.set_max_pos(shdr.sh_offset + shdr.sh_size); ++ return _reader.set_position(shdr.sh_offset); ++} ++ ++// Parse set header as specified in section 6.1.2 of the DWARF 4 spec. ++bool DwarfFile::DebugAranges::read_set_header(DebugArangesSetHeader& header) { ++ if (!_reader.read_dword(&header._unit_length) || header._unit_length == 0xFFFFFFFF) { ++ // For 64-bit DWARF, the first 32-bit value is 0xFFFFFFFF. The current implementation only supports 32-bit DWARF ++ // format since GCC only emits 32-bit DWARF. ++ DWARF_LOG_ERROR("64-bit DWARF is not supported for .debug_aranges") ++ return false; ++ } ++ ++ if (!_reader.read_word(&header._version) || header._version != 2) { ++ // DWARF 4 uses version 2 as specified in Appendix F of the DWARF 4 spec. ++ DWARF_LOG_ERROR(".debug_aranges in unsupported DWARF version %" PRIu16, header._version) ++ return false; ++ } ++ ++ if (!_reader.read_dword(&header._debug_info_offset)) { ++ return false; ++ } ++ ++ if (!_reader.read_byte(&header._address_size) || header._address_size != DwarfFile::ADDRESS_SIZE) { ++ // Addresses must be either 4 bytes for 32-bit architectures or 8 bytes for 64-bit architectures. ++ DWARF_LOG_ERROR(".debug_aranges specifies wrong address size %" PRIu8, header._address_size); ++ return false; ++ } ++ ++ if (!_reader.read_byte(&header._segment_size) || header._segment_size != 0) { ++ // Segment size should be 0. ++ DWARF_LOG_ERROR(".debug_aranges segment size is non-zero: %" PRIu8, header._segment_size); ++ return false; ++ } ++ ++ // We must align to twice the address size. ++ uint8_t alignment = DwarfFile::ADDRESS_SIZE * 2; ++ uint8_t padding = alignment - (_reader.get_position() - _section_start_address) % alignment; ++ return _reader.move_position(padding); ++} ++ ++bool DwarfFile::DebugAranges::read_address_descriptors(const uint32_t offset_in_library, bool& found_matching_set) { ++ AddressDescriptor descriptor; ++ do { ++ if (!read_address_descriptor(descriptor)) { ++ return false; ++ } ++ ++ if (does_match_offset(offset_in_library, descriptor)) { ++ found_matching_set = true; ++ return true; ++ } ++ } while (!is_terminating_entry(descriptor) && _reader.has_bytes_left()); ++ ++ // Set does not match offset_in_library. Continue with next. ++ return true; ++} ++ ++bool DwarfFile::DebugAranges::read_address_descriptor(AddressDescriptor& descriptor) { ++ return _reader.read_address_sized(&descriptor.beginning_address) ++ && _reader.read_address_sized(&descriptor.range_length); ++} ++ ++bool DwarfFile::DebugAranges::does_match_offset(const uint32_t offset_in_library, const AddressDescriptor& descriptor) { ++ return descriptor.beginning_address <= offset_in_library ++ && offset_in_library < descriptor.beginning_address + descriptor.range_length; ++} ++ ++bool DwarfFile::DebugAranges::is_terminating_entry(const AddressDescriptor& descriptor) { ++ return descriptor.beginning_address == 0 && descriptor.range_length == 0; ++} ++ ++// Find the .debug_line offset for the line number program by reading from the .debug_abbrev and .debug_info section. ++bool DwarfFile::CompilationUnit::find_debug_line_offset(uint32_t* debug_line_offset) { ++ // (3a,b) ++ if (!read_header()) { ++ DWARF_LOG_ERROR("Failed to read the compilation unit header."); ++ return false; ++ } ++ ++ // (3c) Read the abbreviation code immediately following the compilation unit header which is an offset to the ++ // correct abbreviation table in .debug_abbrev for this compilation unit. ++ uint64_t abbrev_code; ++ if (!_reader.read_uleb128(&abbrev_code)) { ++ return false; ++ } ++ ++ DebugAbbrev debug_abbrev(_dwarf_file, this); ++ if (!debug_abbrev.read_section_header(_header._debug_abbrev_offset)) { ++ DWARF_LOG_ERROR("Failed to read the .debug_abbrev header at " PTR32_FORMAT, _header._debug_abbrev_offset); ++ return false; ++ } ++ if (!debug_abbrev.find_debug_line_offset(abbrev_code)) { ++ return false; ++ } ++ *debug_line_offset = _debug_line_offset; // Result was stored in _debug_line_offset. ++ return true; ++} ++ ++// (3a) Parse header as specified in section 7.5.1.1 of the DWARF 4 spec. ++bool DwarfFile::CompilationUnit::read_header() { ++ Elf_Shdr shdr; ++ if (!_dwarf_file->read_section_header(".debug_info", shdr)) { ++ DWARF_LOG_ERROR("Failed to read the .debug_info section header."); ++ return false; ++ } ++ ++ if (!_reader.set_position(shdr.sh_offset + _compilation_unit_offset)) { ++ return false; ++ } ++ ++ if (!_reader.read_dword(&_header._unit_length) || _header._unit_length == 0xFFFFFFFF) { ++ // For 64-bit DWARF, the first 32-bit value is 0xFFFFFFFF. The current implementation only supports 32-bit DWARF ++ // format since GCC only emits 32-bit DWARF. ++ DWARF_LOG_ERROR("64-bit DWARF is not supported for .debug_info") ++ return false; ++ } ++ ++ if (!_reader.read_word(&_header._version) || _header._version != 4) { ++ // DWARF 4 uses version 4 as specified in Appendix F of the DWARF 4 spec. ++ DWARF_LOG_ERROR(".debug_info in unsupported DWARF version %" PRIu16, _header._version) ++ return false; ++ } ++ ++ // (3b) Offset into .debug_abbrev section. ++ if (!_reader.read_dword(&_header._debug_abbrev_offset)) { ++ return false; ++ } ++ ++ if (!_reader.read_byte(&_header._address_size) || _header._address_size != DwarfFile::ADDRESS_SIZE) { ++ // Addresses must be either 4 bytes for 32-bit architectures or 8 bytes for 64-bit architectures. ++ DWARF_LOG_ERROR(".debug_info specifies wrong address size %" PRIu8, _header._address_size); ++ return false; ++ } ++ ++ // Add because _unit_length is not included. ++ _reader.set_max_pos(_reader.get_position() + _header._unit_length + 4); ++ return true; ++} ++ ++bool DwarfFile::DebugAbbrev::read_section_header(uint32_t debug_abbrev_offset) { ++ Elf_Shdr shdr; ++ if (!_dwarf_file->read_section_header(".debug_abbrev", shdr)) { ++ return false; ++ } ++ ++ _reader.set_max_pos(shdr.sh_offset + shdr.sh_size); ++ if (!_reader.set_position(shdr.sh_offset + debug_abbrev_offset)) { ++ return false; ++ } ++ return true; ++} ++ ++// (3d) The abbreviations table for a compilation unit consists of a series of abbreviation declarations. Each declaration ++// specifies an abbrev code and a tag. Parse all declarations until we find the declaration which matches 'abbrev_code'. ++// Read the attribute values from the compilation unit in .debug_info by using the format described in the declaration. ++// This process is described in section 7.5 and 7.5.3 of the DWARF 4 spec. ++bool DwarfFile::DebugAbbrev::find_debug_line_offset(const uint64_t abbrev_code) { ++ DWARF_LOG_TRACE("Series of declarations [code, tag]:"); ++ AbbreviationDeclaration declaration; ++ while (_reader.has_bytes_left()) { ++ if (!read_declaration(declaration)) { ++ return false; ++ } ++ ++ DWARF_LOG_TRACE(" Series of attributes [name, form]:"); ++ if (declaration._abbrev_code == abbrev_code) { ++ // Found the correct declaration. ++ if (is_wrong_or_unsupported_format(declaration)) { ++ return false; ++ } ++ DWARF_LOG_INFO(".debug_abbrev offset: " PTR32_FORMAT, (uint32_t)_reader.get_position()); ++ DWARF_LOG_TRACE(" Read the following attribute values from compilation unit:"); ++ return read_attribute_specifications(true); ++ } else { ++ // Not the correct declaration. Read its attributes and continue with the next declaration. ++ if (!read_attribute_specifications(false)) { ++ return false; ++ } ++ } ++ } ++ ++ assert(false, ".debug_line offset not found"); ++ return false; ++} ++ ++bool DwarfFile::DebugAbbrev::read_declaration(DwarfFile::DebugAbbrev::AbbreviationDeclaration& declaration) { ++ if (!_reader.read_uleb128(&declaration._abbrev_code)) { ++ return false; ++ } ++ ++ if (declaration._abbrev_code == 0) { ++ // Reached the end of the abbreviation declarations for this compilation unit. ++ DWARF_LOG_ERROR("abbrev_code not found in any declaration"); ++ return false; ++ } ++ ++ if (!_reader.read_uleb128(&declaration._tag) || !_reader.read_byte(&declaration._has_children)) { ++ return false; ++ } ++ ++ DWARF_LOG_TRACE("Code: 0x" UINT64_FORMAT_X ", Tag: 0x" UINT64_FORMAT_X, declaration._abbrev_code, declaration._tag); ++ return true; ++} ++ ++bool DwarfFile::DebugAbbrev::is_wrong_or_unsupported_format(const DwarfFile::DebugAbbrev::AbbreviationDeclaration& declaration) { ++ if (declaration._tag != DW_TAG_compile_unit) { ++ // Is not DW_TAG_compile_unit as specified in Figure 18 in section 7.5 of the DWARF 4 spec. It could also ++ // be DW_TAG_partial_unit (0x3c) which is currently not supported by this parser. ++ DWARF_LOG_ERROR("Found unsupported tag in compilation unit: " UINT64_FORMAT_X, declaration._tag); ++ return true; ++ } ++ if (declaration._has_children != DW_CHILDREN_yes) { ++ DWARF_LOG_ERROR("Must have children but none specified"); ++ return true; ++ } ++ return false; ++} ++ ++// Read the attribute names and forms which define the actual attribute values that follow the abbrev code in the compilation unit. All ++// attributes need to be read from the compilation unit until we reach the DW_AT_stmt_list attribute which specifies the offset for the ++// line number program into the .debug_line section. The offset is stored in the _debug_line_offset field of the compilation unit. ++bool DwarfFile::DebugAbbrev::read_attribute_specifications(const bool is_DW_TAG_compile_unit) { ++ AttributeSpecification attribute_specification; ++ while (_reader.has_bytes_left()) { ++ if (!read_attribute_specification(attribute_specification)) { ++ return false; ++ } ++ ++ if (is_terminating_specification(attribute_specification)) { ++ // Parsed all attributes of this declaration. ++ if (is_DW_TAG_compile_unit) { ++ DWARF_LOG_ERROR("Did not find DW_AT_stmt_list in .debug_abbrev"); ++ return false; ++ } else { ++ // Continue with next declaration if this was not DW_TAG_compile_unit. ++ return true; ++ } ++ } ++ ++ if (is_DW_TAG_compile_unit) { ++ // Read attribute from compilation unit ++ if (attribute_specification._name == DW_AT_stmt_list) { ++ // This attribute represents the .debug_line offset. Read it and then stop parsing. ++ return _compilation_unit->read_attribute_value(attribute_specification._form, true); ++ } else { ++ // Not DW_AT_stmt_list, read it and continue with the next attribute. ++ if (!_compilation_unit->read_attribute_value(attribute_specification._form, false)) { ++ return false; ++ } ++ } ++ } ++ } ++ ++ assert(false, ".debug_abbrev section appears to be corrupted"); ++ return false; ++} ++ ++bool DwarfFile::DebugAbbrev::read_attribute_specification(DwarfFile::DebugAbbrev::AttributeSpecification& specification) { ++ bool result = _reader.read_uleb128(&specification._name) && _reader.read_uleb128(&specification._form); ++ DWARF_LOG_TRACE(" Name: 0x" UINT64_FORMAT_X ", Form: 0x" UINT64_FORMAT_X, ++ specification._name, specification._form); ++ return result; ++} ++ ++bool DwarfFile::DebugAbbrev::is_terminating_specification(const DwarfFile::DebugAbbrev::AttributeSpecification& specification) { ++ return specification._name == 0 && specification._form == 0; ++} ++ ++ ++// (3e) Read the actual attribute values from the compilation unit in the .debug_info section. Each attribute has an encoding ++// that specifies which values need to be read for it. This is specified in section 7.5.4 of the DWARF 4 spec. ++// If is_DW_AT_stmt_list_attribute is: ++// - False: Ignore the read attribute value. ++// - True: We are going to read the attribute value of the DW_AT_stmt_list attribute which specifies the offset into the ++// .debug_line section for the line number program. Store this offset in the _debug_line_offset field. ++bool DwarfFile::CompilationUnit::read_attribute_value(const uint64_t attribute_form, const bool is_DW_AT_stmt_list_attribute) { ++ // Reset to the stored _cur_pos of the reader since the DebugAbbrev reader changed the index into the file with its reader. ++ _reader.update_to_stored_position(); ++ uint8_t next_byte = 0; ++ uint16_t next_word = 0; ++ uint32_t next_dword = 0; ++ uint64_t next_qword = 0; ++ ++ switch (attribute_form) { ++ case DW_FORM_addr: ++ // Move position by the size of an address. ++ _reader.move_position(DwarfFile::ADDRESS_SIZE); ++ break; ++ case DW_FORM_block2: ++ // New position: length + data length (next_word) ++ if (!_reader.read_word(&next_word) || !_reader.move_position(next_word)) { ++ return false; ++ } ++ break; ++ case DW_FORM_block4: ++ // New position: length + data length (next_dword) ++ if (!_reader.read_dword(&next_dword) || !_reader.move_position(next_dword)) { ++ return false; ++ } ++ break; ++ case DW_FORM_data2: ++ case DW_FORM_ref2: ++ if (!_reader.move_position(2)) { ++ return false; ++ } ++ break; ++ case DW_FORM_data4: ++ case DW_FORM_strp: // 4 bytes in 32-bit DWARF ++ case DW_FORM_ref_addr: // second type of reference: 4 bytes in 32-bit DWARF ++ case DW_FORM_ref4: ++ if (!_reader.move_position(4)) { ++ return false; ++ } ++ break; ++ case DW_FORM_data8: ++ case DW_FORM_ref8: ++ case DW_FORM_ref_sig8: // 64-bit type signature ++ if (!_reader.move_position(8)) { ++ return false; ++ } ++ break; ++ case DW_FORM_string: ++ if (!_reader.read_string()) { ++ return false; ++ } ++ break; ++ case DW_FORM_block: ++ case DW_FORM_exprloc: ++ // New position: length + data length (next_qword). ++ if (!_reader.read_uleb128(&next_qword) || !_reader.move_position(next_qword)) { ++ return false; ++ } ++ break; ++ case DW_FORM_block1: ++ // New position: length + data length (next_byte). ++ if (!_reader.read_byte(&next_byte) || !_reader.move_position(next_byte)) { ++ return false; ++ } ++ break; ++ case DW_FORM_data1: ++ case DW_FORM_ref1: ++ case DW_FORM_flag: ++ case DW_FORM_flag_present: ++ if (!_reader.move_position(1)) { ++ return false; ++ } ++ break; ++ case DW_FORM_sdata: ++ case DW_FORM_udata: ++ case DW_FORM_ref_udata: ++ if (!_reader.read_uleb128(&next_qword)) { ++ return false; ++ } ++ break; ++ case DW_FORM_indirect: ++ // Should not be used and therefore is not supported by this parser. ++ DWARF_LOG_ERROR("DW_FORM_indirect is not supported."); ++ return false; ++ case DW_FORM_sec_offset: ++ if (is_DW_AT_stmt_list_attribute) { ++ // DW_AT_stmt_list has the DW_FORM_sec_offset attribute encoding. Store the result in _debug_line_offset. ++ // 4 bytes for 32-bit DWARF. ++ DWARF_LOG_TRACE(" Name: DW_AT_stmt_list, Form: DW_FORM_sec_offset"); ++ DWARF_LOG_TRACE(" Reading .debug_line offset from compilation unit at " PTR32_FORMAT, ++ (uint32_t)_reader.get_position()); ++ if (!_reader.read_dword(&_debug_line_offset)) { ++ return false; ++ } ++ break; ++ } else { ++ if (!_reader.move_position(DwarfFile::DWARF_SECTION_OFFSET_SIZE)) { ++ return false; ++ } ++ break; ++ } ++ default: ++ assert(false, "Unknown DW_FORM_* attribute encoding."); ++ return false; ++ } ++ // Reset the index into the file to the original position where the DebugAbbrev reader stopped reading before calling this method. ++ _reader.reset_to_previous_position(); ++ return true; ++} ++ ++bool DwarfFile::LineNumberProgram::find_filename_and_line_number(char* filename, const size_t filename_len, int* line) { ++ if (!read_header()) { ++ DWARF_LOG_ERROR("Failed to parse the line number program header correctly."); ++ return false; ++ } ++ return run_line_number_program(filename, filename_len, line); ++} ++ ++// Parsing header as specified in section 6.2.4 of DWARF 4 spec. We do not read the file_names field, yet. ++bool DwarfFile::LineNumberProgram::read_header() { ++ Elf_Shdr shdr; ++ if (!_dwarf_file->read_section_header(".debug_line", shdr)) { ++ DWARF_LOG_ERROR("Failed to read the .debug_line section header."); ++ return false; ++ } ++ ++ if (!_reader.set_position(shdr.sh_offset + _debug_line_offset)) { ++ return false; ++ } ++ ++ if (!_reader.read_dword(&_header._unit_length) || _header._unit_length == 0xFFFFFFFF) { ++ // For 64-bit DWARF, the first 32-bit value is 0xFFFFFFFF. The current implementation only supports 32-bit DWARF ++ // format since GCC only emits 32-bit DWARF. ++ DWARF_LOG_ERROR("64-bit DWARF is not supported for .debug_line") ++ return false; ++ } ++ ++ if (!_reader.read_word(&_header._version) || _header._version < 2 || _header._version > 4) { ++ // DWARF 3 uses version 3 and DWARF 4 uses version 4 as specified in Appendix F of the DWARF 3 and 4 spec, respectively. ++ // For some reason, GCC is not following the standard here. While GCC emits DWARF 4 for the other parsed sections, ++ // it chooses a different DWARF standard for .debug_line based on the GCC version: ++ // - GCC 8 and earlier: .debug_line is in DWARF 2 format (= version 2). ++ // - GCC 9 and 10: .debug_line is in DWARF 3 format (= version 3). ++ // - GCC 11: .debug_line is in DWARF 4 format (= version 4). ++ DWARF_LOG_ERROR(".debug_line in unsupported DWARF version %" PRIu16, _header._version) ++ return false; ++ } ++ ++ if (!_reader.read_dword(&_header._header_length)) { ++ return false; ++ } ++ ++ // To ensure not to read too many bytes in case of file corruption when reading the path_names field. ++ _reader.set_max_pos(_reader.get_position() + _header._header_length); ++ ++ if (!_reader.read_byte(&_header._minimum_instruction_length)) { ++ return false; ++ } ++ ++ if (_header._version == 4) { ++ if (!_reader.read_byte(&_header._maximum_operations_per_instruction)) { ++ return false; ++ } ++ } ++ ++ if (!_reader.read_byte(&_header._default_is_stmt)) { ++ return false; ++ } ++ ++ if (!_reader.read_sbyte(&_header._line_base)) { ++ return false; ++ } ++ ++ if (!_reader.read_byte(&_header._line_range)) { ++ return false; ++ } ++ ++ if (!_reader.read_byte(&_header._opcode_base) || _header._opcode_base - 1 != 12) { ++ // There are 12 standard opcodes for DWARF 3 and 4. ++ DWARF_LOG_ERROR("Wrong number of opcodes: %" PRIu8, _header._opcode_base) ++ return false; ++ } ++ ++ for (uint8_t i = 0; i < _header._opcode_base - 1; i++) { ++ if (!_reader.read_byte(&_header._standard_opcode_lengths[i])) { ++ return false; ++ } ++ } ++ ++ // Read field include_directories which is a sequence of path names. These are terminated by a single null byte. ++ // We do not care about them, just read the strings and move on. ++ while (_reader.read_string()) { } ++ ++ // Delay reading file_names until we found the correct file index in the line number program. Store the position where ++ // the file names start to parse them later. We directly jump to the line number program which starts at offset ++ // header_size (=HEADER_DESCRIPTION_BYTES + _header_length) + _debug_line_offset ++ _header._file_names_offset = _reader.get_position(); ++ uint32_t header_size = LineNumberProgramHeader::HEADER_DESCRIPTION_BYTES + _header._header_length; ++ if (!_reader.set_position(shdr.sh_offset + header_size + _debug_line_offset)) { ++ return false; ++ } ++ ++ // Now reset the max position to where the line number information for this compilation unit ends (i.e. where the state ++ // machine gets terminated). Add 4 bytes to the offset because the size of the _unit_length field is not included in this ++ // value. ++ _reader.set_max_pos(shdr.sh_offset + _debug_line_offset + _header._unit_length + 4); ++ return true; ++} ++ ++// Create the line number information matrix as described in section 6.2 of the DWARF 4 spec. Try to find the correct entry ++// by comparing the address register belonging to each matrix row with _offset_in_library. Once it is found, we can read ++// the line number from the line register and the filename by parsing the file_names list from the header until we reach ++// the correct filename as specified by the file register. ++// ++// If space was not a problem, the .debug_line section could provide a large matrix that contains an entry for each ++// compiler instruction that contains the line number, the column number, the filename etc. But that's impractical. ++// Two techniques optimize such a matrix: ++// (1) If two offsets share the same file, line and column (and discriminator) information, the row is dropped. ++// (2) We store a stream of bytes that represent opcodes to be executed in a well-defined state machine language ++// instead of actually storing the entire matrix row by row. ++// ++// Let's consider a simple example: ++// 25: int iFld = 42; ++// 26: ++// 27: void bar(int i) { ++// 28: } ++// 29: ++// 30: void foo() { ++// 31: bar(*iFld); ++// 32: } ++// ++// Disassembly of foo() with source code: ++// 30: void foo() { ++// 0x55d132: 55 push rbp ++// 0x55d133: 48 89 e5 mov rbp,rsp ++// 31: bar(*iFld); ++// 0x55d136: 48 8b 05 b3 ee e8 01 mov rax,QWORD PTR [rip+0x1e8eeb3] # 23ebff0 ++// 0x55d13d: 8b 00 mov eax,DWORD PTR [rax] ++// 0x55d13f: 89 c7 mov edi,eax ++// 0x55d141: e8 e2 ff ff ff call 55d128 <_Z3bari> ++// 32: } ++// 0x55d146: 90 nop ++// 0x55d147: 5d pop rbp ++// 0x55d148: c3 ret ++// ++// This would produce the following matrix for foo() where duplicated lines (0x55d133, 0x55d13d, 0x55d13f) were removed ++// according to (1): ++// Address: Line: Column: File: ++// 0x55d132 30 12 1 ++// 0x55d136 31 6 1 ++// 0x55d146 32 1 1 ++// ++// When trying to get the line number for a PC, which is translated into an offset address x into the library file, we can either: ++// - Directly find the last entry in the matrix for which address == x (there could be multiple entries with the same address). ++// - If there is no matching address for x: ++// 1. Find two consecutive entries in the matrix for which: address_entry_1 < x < address_entry_2. ++// 2. Then take the entry of address_entry_1. ++// E.g. x = 0x55d13f -> 0x55d136 < 0x55d13f < 0x55d146 -> Take entry 0x55d136. ++// ++// Enable logging with debug level to print the generated line number information matrix. ++bool DwarfFile::LineNumberProgram::run_line_number_program(char* filename, const size_t filename_len, int* line) { ++ DWARF_LOG_DEBUG(" "); ++ DWARF_LOG_DEBUG("Line Number Information Matrix"); ++ DWARF_LOG_DEBUG("------------------------------"); ++#ifndef _LP64 ++ DWARF_LOG_DEBUG("Address: Line: Column: File:"); ++#else ++ DWARF_LOG_DEBUG("Address: Line: Column: File:"); ++#endif ++ _state = new (std::nothrow) LineNumberProgramState(_header); ++ if (_state == NULL) { ++ DWARF_LOG_ERROR("Failed to create new LineNumberProgramState object"); ++ return false; ++ } ++ uintptr_t previous_address = 0; ++ uint32_t previous_file = 0; ++ uint32_t previous_line = 0; ++ while (_reader.has_bytes_left()) { ++ if (!apply_opcode()) { ++ assert(false, "Could not apply opcode"); ++ return false; ++ } ++ ++ if (_state->_append_row) { ++ // Append a new line to the line number information matrix. ++ if (_state->_first_entry_in_sequence) { ++ // First entry in sequence: Check if _offset_in_library >= _state->address. If not, then all following entries ++ // belonging to this sequence cannot match our _offset_in_library because the addresses are always increasing ++ // in a sequence. ++ _state->_can_sequence_match_offset = _offset_in_library >= _state->_address; ++ _state->_first_entry_in_sequence = false; ++ } ++ if (does_offset_match_entry(previous_address, previous_file, previous_line)) { ++ // We are using an int for the line number which should never be larger than INT_MAX for any files. ++ *line = (int)_state->_line; ++ return get_filename_from_header(_state->_file, filename, filename_len); ++ } ++ ++ // We do not actually store the matrix while searching the correct entry. Enable logging to print/debug it. ++ DWARF_LOG_DEBUG(INTPTR_FORMAT " %-5u %-3u %-4u", ++ _state->_address, _state->_line, _state->_column, _state->_file); ++ previous_file = _state->_file; ++ previous_line = _state->_line; ++ previous_address = _state->_address; ++ _state->_append_row = false; ++ if (_state->_do_reset) { ++ // Current sequence terminated. ++ _state->reset_fields(); ++ } ++ } ++ } ++ ++ return false; ++} ++ ++// Apply next opcode to update the state machine. ++bool DwarfFile::LineNumberProgram::apply_opcode() { ++ uint8_t opcode; ++ if (!_reader.read_byte(&opcode)) { ++ return false; ++ } ++ ++ DWARF_LOG_TRACE(" Opcode: 0x%02x ", opcode); ++ if (opcode == 0) { ++ // Extended opcodes start with a zero byte. ++ if (!apply_extended_opcode()) { ++ assert(false, "Could not apply extended opcode"); ++ return false; ++ } ++ } else if (opcode <= 12) { ++ // 12 standard opcodes in DWARF 3 and 4. ++ if (!apply_standard_opcode(opcode)) { ++ assert(false, "Could not apply standard opcode"); ++ return false; ++ } ++ } else { ++ // Special opcodes range from 13 until 255. ++ apply_special_opcode(opcode); ++ } ++ return true; ++} ++ ++// Specified in section 6.2.5.3 of the DWARF 4 spec. ++bool DwarfFile::LineNumberProgram::apply_extended_opcode() { ++ uint64_t extended_opcode_length; // Does not include the already written zero byte and the length leb128. ++ uint8_t extended_opcode; ++ if (!_reader.read_uleb128(&extended_opcode_length) || !_reader.read_byte(&extended_opcode)) { ++ return false; ++ } ++ ++ switch (extended_opcode) { ++ case DW_LNE_end_sequence: // No operands ++ DWARF_LOG_TRACE(" DW_LNE_end_sequence"); ++ _state->_end_sequence = true; ++ _state->_append_row = true; ++ _state->_do_reset = true; ++ break; ++ case DW_LNE_set_address: // 1 operand ++ if (!_reader.read_address_sized(&_state->_address)) { ++ return false; ++ } ++ DWARF_LOG_TRACE(" DW_LNE_set_address " INTPTR_FORMAT, _state->_address); ++ if (_state->_dwarf_version == 4) { ++ _state->_op_index = 0; ++ } ++ break; ++ case DW_LNE_define_file: // 4 operands ++ DWARF_LOG_TRACE(" DW_LNE_define_file"); ++ if (!_reader.read_string()) { ++ return false; ++ } ++ // Operand 2-4: uleb128 numbers we do not care about. ++ if (!_reader.read_uleb128_ignore() ++ || !_reader.read_uleb128_ignore() ++ || !_reader.read_uleb128_ignore()) { ++ return false; ++ } ++ break; ++ case DW_LNE_set_discriminator: // 1 operand ++ DWARF_LOG_TRACE(" DW_LNE_set_discriminator"); ++ uint64_t discriminator; ++ // For some reason, GCC emits this opcode even for earlier versions than DWARF 4 which introduced this opcode. ++ // We need to consume it. ++ if (!_reader.read_uleb128(&discriminator, 4)) { ++ // Must be an unsigned integer as specified in section 6.2.2 of the DWARF 4 spec for the discriminator register. ++ return false; ++ } ++ _state->_discriminator = discriminator; ++ break; ++ default: ++ assert(false, "Unknown extended opcode"); ++ return false; ++ } ++ return true; ++} ++ ++// Specified in section 6.2.5.2 of the DWARF 4 spec. ++bool DwarfFile::LineNumberProgram::apply_standard_opcode(const uint8_t opcode) { ++ switch (opcode) { ++ case DW_LNS_copy: // No operands ++ DWARF_LOG_TRACE(" DW_LNS_copy"); ++ _state->_append_row = true; ++ _state->_basic_block = false; ++ _state->_prologue_end = false; ++ _state->_epilogue_begin = false; ++ if (_state->_dwarf_version == 4) { ++ _state->_discriminator = 0; ++ } ++ break; ++ case DW_LNS_advance_pc: { // 1 operand ++ uint64_t operation_advance; ++ if (!_reader.read_uleb128(&operation_advance, 4)) { ++ // Must be at most 4 bytes because the index register is only 4 bytes wide. ++ return false; ++ } ++ _state->add_to_address_register(operation_advance, _header); ++ if (_state->_dwarf_version == 4) { ++ _state->set_index_register(operation_advance, _header); ++ } ++ DWARF_LOG_TRACE(" DW_LNS_advance_pc (" INTPTR_FORMAT ")", _state->_address); ++ break; ++ } ++ case DW_LNS_advance_line: // 1 operand ++ int64_t line; ++ if (!_reader.read_sleb128(&line, 4)) { ++ // line register is 4 bytes wide. ++ return false; ++ } ++ _state->_line += line; ++ DWARF_LOG_TRACE(" DW_LNS_advance_line (%d)", _state->_line); ++ break; ++ case DW_LNS_set_file: // 1 operand ++ uint64_t file; ++ if (!_reader.read_uleb128(&file, 4)) { ++ // file register is 4 bytes wide. ++ return false; ++ } ++ _state->_file = file; ++ DWARF_LOG_TRACE(" DW_LNS_set_file (%u)", _state->_file); ++ break; ++ case DW_LNS_set_column: // 1 operand ++ uint64_t column; ++ if (!_reader.read_uleb128(&column, 4)) { ++ // column register is 4 bytes wide. ++ return false; ++ } ++ _state->_column = column; ++ DWARF_LOG_TRACE(" DW_LNS_set_column (%u)", _state->_column); ++ break; ++ case DW_LNS_negate_stmt: // No operands ++ DWARF_LOG_TRACE(" DW_LNS_negate_stmt"); ++ _state->_is_stmt = !_state->_is_stmt; ++ break; ++ case DW_LNS_set_basic_block: // No operands ++ DWARF_LOG_TRACE(" DW_LNS_set_basic_block"); ++ _state->_basic_block = true; ++ break; ++ case DW_LNS_const_add_pc: { // No operands ++ // Update address and op_index registers by the increments of special opcode 255. ++ uint8_t adjusted_opcode_255 = 255 - _header._opcode_base; ++ uint8_t operation_advance = adjusted_opcode_255 / _header._line_range; ++ uintptr_t old_address = _state->_address; ++ _state->add_to_address_register(operation_advance, _header); ++ if (_state->_dwarf_version == 4) { ++ _state->set_index_register(operation_advance, _header); ++ } ++ DWARF_LOG_TRACE(" DW_LNS_const_add_pc (" INTPTR_FORMAT ")", _state->_address - old_address); ++ break; ++ } ++ case DW_LNS_fixed_advance_pc: // 1 operand ++ uint16_t operand; ++ if (!_reader.read_word(&operand)) { ++ return false; ++ } ++ _state->_address += operand; ++ _state->_op_index = 0; ++ DWARF_LOG_TRACE(" DW_LNS_fixed_advance_pc (" INTPTR_FORMAT ")", _state->_address); ++ break; ++ case DW_LNS_set_prologue_end: // No operands ++ DWARF_LOG_TRACE(" DW_LNS_set_basic_block"); ++ _state->_prologue_end = true; ++ break; ++ case DW_LNS_set_epilogue_begin: // No operands ++ DWARF_LOG_TRACE(" DW_LNS_set_epilogue_begin"); ++ _state->_epilogue_begin = true; ++ break; ++ case DW_LNS_set_isa: // 1 operand ++ uint64_t isa; ++ if (!_reader.read_uleb128(&isa, 4)) { ++ // isa register is 4 bytes wide. ++ return false; ++ } ++ _state->_isa = isa; ++ DWARF_LOG_TRACE(" DW_LNS_set_isa (%u)", _state->_isa); ++ break; ++ default: ++ assert(false, "Unknown standard opcode"); ++ return false; ++ } ++ return true; ++} ++ ++// Specified in section 6.2.5.1 of the DWARF 4 spec. ++void DwarfFile::LineNumberProgram::apply_special_opcode(const uint8_t opcode) { ++ uintptr_t old_address = _state->_address; ++ uint32_t old_line = _state->_line; ++ uint8_t adjusted_opcode = opcode - _header._opcode_base; ++ uint8_t operation_advance = adjusted_opcode / _header._line_range; ++ _state->add_to_address_register(operation_advance, _header); ++ if (_state->_dwarf_version == 4) { ++ _state->set_index_register(operation_advance, _header); ++ _state->_discriminator = 0; ++ } ++ _state->_line += _header._line_base + (adjusted_opcode % _header._line_range); ++ DWARF_LOG_TRACE(" address += " INTPTR_FORMAT ", line += %d", _state->_address - old_address, ++ _state->_line - old_line); ++ _state->_append_row = true; ++ _state->_basic_block = false; ++ _state->_prologue_end = false; ++ _state->_epilogue_begin = false; ++} ++ ++bool DwarfFile::LineNumberProgram::does_offset_match_entry(const uintptr_t previous_address, const uint32_t previous_file, ++ const uint32_t previous_line) { ++ if (_state->_can_sequence_match_offset) { ++ bool matches_entry_directly = _offset_in_library == _state->_address; ++ if (matches_entry_directly ++ || (_offset_in_library > previous_address && _offset_in_library < _state->_address)) { // in between two entries ++ _state->_found_match = true; ++ if (!matches_entry_directly || _is_pc_after_call) { ++ // We take the previous row in the matrix either when: ++ // - We try to match an offset that is between two entries. ++ // - We have an offset from a PC that is at a call-site in which case we need to get the line information for ++ // the call instruction in the previous entry. ++ print_and_store_prev_entry(previous_file, previous_line); ++ return true; ++ } else if (!_reader.has_bytes_left()) { ++ // We take the current entry when this is the very last entry in the matrix (i.e. must be the right one). ++ DWARF_LOG_DEBUG("^^^ Found line for requested offset " PTR32_FORMAT " ^^^", _offset_in_library); ++ return true; ++ } ++ // Else: Exact match. We cannot take this entry because we do not know if there are more entries following this ++ // one with the same offset (we could have multiple entries for the same address in the matrix). Continue ++ // to parse entries. When we have the first non-exact match, then we know that the previous entry is the ++ // correct one to take (handled in the else-if-case below). If this is the very last entry in a matrix, ++ // we will take the current entry (handled in else-if-case above). ++ } else if (_state->_found_match) { ++ // We found an entry before with an exact match. This is now the first entry with a new offset. Pick the previous ++ // entry which matches our offset and is guaranteed to be the last entry which matches our offset (if there are ++ // multiple entries with the same offset). ++ print_and_store_prev_entry(previous_file, previous_line); ++ return true; ++ } ++ } ++ return false; ++} ++ ++void DwarfFile::LineNumberProgram::print_and_store_prev_entry(const uint32_t previous_file, const uint32_t previous_line) { ++ _state->_file = previous_file; ++ _state->_line = previous_line; ++ DWARF_LOG_DEBUG("^^^ Found line for requested offset " PTR32_FORMAT " ^^^", _offset_in_library); ++ // Also print the currently parsed entry. ++ DWARF_LOG_DEBUG(INTPTR_FORMAT " %-5u %-3u %-4u", ++ _state->_address, _state->_line, _state->_column, _state->_file); ++} ++ ++// Read field file_names from the header as specified in section 6.2.4 of the DWARF 4 spec. ++bool DwarfFile::LineNumberProgram::get_filename_from_header(const uint32_t file_index, char* filename, const size_t filename_len) { ++ // We do not need to restore the position afterwards as this is the last step of parsing from the file for this compilation unit. ++ _reader.set_position(_header._file_names_offset); ++ uint32_t current_index = 1; // file_names start at index 1 ++ while (_reader.has_bytes_left()) { ++ if (!_reader.read_string(filename, filename_len)) { ++ // Either an error while reading or we have reached the end of the file_names. Both should not happen. ++ return false; ++ } ++ ++ if (current_index == file_index) { ++ // Found correct file. ++ return true; ++ } ++ ++ // We don't care about these values. ++ if (!_reader.read_uleb128_ignore() // Read directory index ++ || !_reader.read_uleb128_ignore() // Read last modification of file ++ || !_reader.read_uleb128_ignore()) { // Read file length ++ return false; ++ } ++ current_index++; ++ } ++ DWARF_LOG_DEBUG("Did not find filename entry at index " UINT32_FORMAT " in .debug_line header", file_index); ++ return false; ++} ++ ++void DwarfFile::LineNumberProgram::LineNumberProgramState::reset_fields() { ++ _address = 0; ++ _op_index = 0; ++ _file = 1; ++ _line = 1; ++ _column = 0; ++ _is_stmt = _initial_is_stmt; ++ _basic_block = false; ++ _end_sequence = false; ++ _prologue_end = false; ++ _epilogue_begin = false; ++ _isa = 0; ++ _discriminator = 0; ++ _append_row = false; ++ _do_reset = false; ++ _first_entry_in_sequence = true; ++ _can_sequence_match_offset = false; ++} ++ ++// Defined in section 6.2.5.1 of the DWARF 4 spec. ++void DwarfFile::LineNumberProgram::LineNumberProgramState::add_to_address_register(const uint32_t operation_advance, ++ const LineNumberProgramHeader& header) { ++ if (_dwarf_version == 2 || _dwarf_version == 3) { ++ _address += (uintptr_t)(operation_advance * header._minimum_instruction_length); ++ } else if (_dwarf_version == 4) { ++ _address += (uintptr_t)(header._minimum_instruction_length * ++ ((_op_index + operation_advance) / header._maximum_operations_per_instruction)); ++ } ++} ++ ++// Defined in section 6.2.5.1 of the DWARF 4 spec. ++void DwarfFile::LineNumberProgram::LineNumberProgramState::set_index_register(const uint32_t operation_advance, ++ const LineNumberProgramHeader& header) { ++ _op_index = (_op_index + operation_advance) % header._maximum_operations_per_instruction; ++} ++ ++bool DwarfFile::MarkedDwarfFileReader::set_position(const long new_pos) { ++ if (new_pos < 0) { ++ return false; ++ } ++ _current_pos = new_pos; ++ return FileReader::set_position(new_pos); ++} ++ ++bool DwarfFile::MarkedDwarfFileReader::has_bytes_left() const { ++ if (_max_pos == -1) { ++ return false; ++ } ++ return _current_pos < _max_pos; ++} ++ ++bool DwarfFile::MarkedDwarfFileReader::update_to_stored_position() { ++ _marked_pos = ftell(_fd); ++ if (_marked_pos < 0) { ++ return false; ++ } ++ return FileReader::set_position(_current_pos); ++} ++ ++bool DwarfFile::MarkedDwarfFileReader::reset_to_previous_position() { ++ return FileReader::set_position(_marked_pos); ++} ++ ++bool DwarfFile::MarkedDwarfFileReader::move_position(const long offset) { ++ if (offset == 0) { ++ return true; ++ } ++ return set_position(_current_pos + offset); ++} ++ ++bool DwarfFile::MarkedDwarfFileReader::read_sbyte(int8_t* result) { ++ _current_pos++; ++ return read(result, 1); ++} ++ ++bool DwarfFile::MarkedDwarfFileReader::read_byte(uint8_t* result) { ++ _current_pos++; ++ return read(result, 1); ++} ++ ++bool DwarfFile::MarkedDwarfFileReader::read_word(uint16_t* result) { ++ _current_pos += 2; ++ return read(result, 2); ++} ++ ++bool DwarfFile::MarkedDwarfFileReader::read_dword(uint32_t* result) { ++ _current_pos += 4; ++ return read(result, 4); ++} ++ ++bool DwarfFile::MarkedDwarfFileReader::read_qword(uint64_t* result) { ++ _current_pos += 8; ++ return read(result, 8); ++} ++ ++bool DwarfFile::MarkedDwarfFileReader::read_address_sized(uintptr_t* result) { ++ _current_pos += DwarfFile::ADDRESS_SIZE; ++ return read(result, DwarfFile::ADDRESS_SIZE); ++} ++ ++// See Figure 46/47 in Appendix C of the DWARF 4 spec. ++bool DwarfFile::MarkedDwarfFileReader::read_leb128(uint64_t* result, const int8_t check_size, bool is_signed) { ++ *result = 0; // Ensure a proper result by zeroing it first. ++ uint8_t buf; ++ uint8_t shift = 0; ++ uint8_t bytes_read = 0; ++ // leb128 is not larger than 8 bytes. ++ while (bytes_read < 8) { ++ if (!read_byte(&buf)) { ++ return false; ++ } ++ bytes_read++; ++ *result |= (buf & 0x7fu) << shift; ++ shift += 7; ++ if ((buf & 0x80u) == 0) { ++ break; ++ } ++ } ++ if (bytes_read > 8 || (check_size != -1 && bytes_read > check_size)) { ++ // Invalid leb128 encoding or the read leb128 was larger than expected. ++ return false; ++ } ++ ++ if (is_signed && (shift < 64) && (buf & 0x40u)) { ++ *result |= static_cast(-1L) << shift; ++ } ++ return true; ++} ++ ++bool DwarfFile::MarkedDwarfFileReader::read_uleb128_ignore(const int8_t check_size) { ++ uint64_t dont_care; ++ return read_leb128(&dont_care, check_size, false); ++} ++ ++bool DwarfFile::MarkedDwarfFileReader::read_uleb128(uint64_t* result, const int8_t check_size) { ++ return read_leb128(result, check_size, false); ++} ++ ++bool DwarfFile::MarkedDwarfFileReader::read_sleb128(int64_t* result, const int8_t check_size) { ++ return read_leb128((uint64_t*)result, check_size, true); ++} ++ ++// If result is a nullptr, we do not care about the content of the string being read. ++bool DwarfFile::MarkedDwarfFileReader::read_string(char* result, const size_t result_len) { ++ uint8_t next_byte; ++ if (!read_byte(&next_byte)) { ++ return false; ++ } ++ ++ if (next_byte == 0) { ++ // Strings must contain at least one non-null byte. ++ return false; ++ } ++ ++ if (result != NULL) { ++ if (result_len < 2) { ++ // Strings must contain at least one non-null byte and a null byte terminator. ++ return false; ++ } ++ result[0] = (char)next_byte; ++ } ++ ++ size_t char_index = 1; ++ bool exceeded_buffer = false; ++ while (has_bytes_left()) { ++ // Read until we find a null byte which terminates the string. ++ if (!read_byte(&next_byte)) { ++ return false; ++ } ++ ++ if (result != NULL) { ++ if (char_index >= result_len) { ++ // Exceeded buffer size of 'result'. ++ exceeded_buffer = true; ++ } else { ++ result[char_index] = (char)next_byte; ++ } ++ char_index++; ++ } ++ if (next_byte == 0) { ++ if (exceeded_buffer) { ++ result[result_len - 1] = '\0'; // Mark end of string. ++ DWARF_LOG_ERROR("Tried to read " SIZE_FORMAT " bytes but exceeded buffer size of " SIZE_FORMAT ". Truncating string.", ++ char_index, result_len); ++ } ++ return true; ++ } ++ } ++ return false; ++} ++ + #endif // !_WINDOWS && !__APPLE__ +diff --git a/hotspot/src/share/vm/utilities/elfFile.hpp b/hotspot/src/share/vm/utilities/elfFile.hpp +index 3ce8e92..3277a40 100644 +--- a/hotspot/src/share/vm/utilities/elfFile.hpp ++++ b/hotspot/src/share/vm/utilities/elfFile.hpp +@@ -36,6 +36,27 @@ + + #ifdef _LP64 + ++#ifdef ASSERT ++// Helper macros to print different log levels during DWARF parsing ++#define DWARF_LOG_SUMMARY(format, ...) DWARF_LOG_WITH_LEVEL(1, format, ##__VA_ARGS__) // Same level as error logging ++#define DWARF_LOG_ERROR(format, ...) DWARF_LOG_WITH_LEVEL(1, format, ##__VA_ARGS__) ++#define DWARF_LOG_INFO(format, ...) DWARF_LOG_WITH_LEVEL(2, format, ##__VA_ARGS__) ++#define DWARF_LOG_DEBUG(format, ...) DWARF_LOG_WITH_LEVEL(3, format, ##__VA_ARGS__) ++#define DWARF_LOG_TRACE(format, ...) DWARF_LOG_WITH_LEVEL(4, format, ##__VA_ARGS__) ++ ++#define DWARF_LOG_WITH_LEVEL(level, format, ...) \ ++ if (TraceDwarfLevel >= level) { \ ++ tty->print("[dwarf] "); \ ++ tty->print_cr(format, ##__VA_ARGS__); \ ++ } ++#else ++#define DWARF_LOG_SUMMARY(format, ...) ++#define DWARF_LOG_ERROR(format, ...) ++#define DWARF_LOG_INFO(format, ...) ++#define DWARF_LOG_DEBUG(format, ...) ++#define DWARF_LOG_TRACE(format, ...) ++#endif ++ + typedef Elf64_Half Elf_Half; + typedef Elf64_Word Elf_Word; + typedef Elf64_Off Elf_Off; +@@ -76,6 +97,29 @@ typedef Elf32_Sym Elf_Sym; + class ElfStringTable; + class ElfSymbolTable; + class ElfFuncDescTable; ++class DwarfFile; ++ ++class FileReader : public StackObj { ++ protected: ++ FILE* const _fd; ++ public: ++ FileReader(FILE* const fd) : _fd(fd) {}; ++ bool read(void* buf, size_t size); ++ size_t read_buffer(void* buf, size_t size); ++ virtual bool set_position(long offset); ++}; ++ ++// Mark current position, so we can get back to it after ++// reads. ++class MarkedFileReader : public FileReader { ++ protected: ++ long _marked_pos; ++ public: ++ MarkedFileReader(FILE* const fd); ++ ~MarkedFileReader(); ++ ++ bool has_mark() const { return _marked_pos >= 0; } ++}; + + + // On Solaris/Linux platforms, libjvm.so does contain all private symbols. +@@ -87,6 +131,34 @@ class ElfFuncDescTable; + + class ElfFile: public CHeapObj { + friend class ElfDecoder; ++ ++ protected: ++ ElfFile* m_next; ++ ++ private: ++ // file ++ const char* m_filepath; ++ FILE* m_file; ++ DwarfFile* m_dwarf_file; ++ ++ static const char* USR_LIB_DEBUG_DIRECTORY; ++ ++ // Elf header ++ Elf_Ehdr m_elfHdr; ++ ++ // symbol tables ++ ElfSymbolTable* m_symbol_tables; ++ ++ // string tables ++ ElfStringTable* m_string_tables; ++ ++ // function descriptors table ++ ElfFuncDescTable* m_funcDesc_table; ++ ++ NullDecoder::decoder_status m_status; ++ ++ ElfStringTable* m_shdr_string_table; ++ + public: + ElfFile(const char* filepath); + ~ElfFile(); +@@ -122,6 +194,9 @@ class ElfFile: public CHeapObj { + // return a string table at specified section index + ElfStringTable* get_string_table(int index); + ++ // Get filename and line number information ++ bool get_source_info(uint32_t offset_in_library, char* filename, size_t filename_len, int* line, bool is_pc_after_call); ++ + protected: + ElfFile* next() const { return m_next; } + void set_next(ElfFile* file) { m_next = file; } +@@ -134,27 +209,654 @@ protected: + // On systems other than linux it always returns false. + bool specifies_noexecstack() NOT_LINUX({ return false; }); + +- protected: +- ElfFile* m_next; + +- private: +- // file +- const char* m_filepath; +- FILE* m_file; ++ bool create_new_dwarf_file(const char* filepath); + +- // Elf header +- Elf_Ehdr m_elfHdr; ++ // Struct to store the debug info read from the .gnu_debuglink section. ++ struct DebugInfo { ++ static const uint8_t CRC_LEN = 4; + +- // symbol tables +- ElfSymbolTable* m_symbol_tables; ++ char _dwarf_filename[JVM_MAXPATHLEN]; ++ uint32_t _crc; ++ }; + +- // string tables +- ElfStringTable* m_string_tables; ++ // Helper class to create DWARF paths when loading a DWARF file. ++ class DwarfFilePath { ++ private: ++ static const uint16_t MAX_DWARF_PATH_LENGTH = JVM_MAXPATHLEN; ++ const char* _filename; ++ char _path[MAX_DWARF_PATH_LENGTH]; ++ const uint32_t _crc; ++ uint16_t _null_terminator_index; // Index for the current null terminator of the string stored in _path + +- // function descriptors table +- ElfFuncDescTable* m_funcDesc_table; ++ bool check_valid_path() const { ++ return _path[MAX_DWARF_PATH_LENGTH - 1] == '\0'; ++ } + +- NullDecoder::decoder_status m_status; ++ void update_null_terminator_index() { ++ _null_terminator_index = strlen(_path); ++ } ++ ++ bool copy_to_path_index(uint16_t index_in_path, const char* src); ++ ++ public: ++ DwarfFilePath(DebugInfo& debug_info) ++ : _filename(debug_info._dwarf_filename), _crc(debug_info._crc), _null_terminator_index(0) { ++ _path[MAX_DWARF_PATH_LENGTH - 1] = '\0'; // Ensures to have a null terminated string and not read beyond the buffer limit. ++ } ++ ++ const char* path() const { ++ return _path; ++ } ++ ++ const char* filename() const { ++ return _filename; ++ } ++ ++ uint32_t crc() const { ++ return _crc; ++ } ++ ++ bool set(const char* src); ++ ++ bool set_filename_after_last_slash() { ++ return set_after_last_slash(_filename); ++ } ++ ++ bool set_after_last_slash(const char* src); ++ bool append(const char* src); ++ }; ++ ++ // Load the DWARF file (.debuginfo) that belongs to this file either from (checked in listed order): ++ // - Same directory as the library file. ++ // - User defined path in environmental variable _JVM_DWARF_PATH. ++ // - Subdirectory .debug in same directory as the library file. ++ // - /usr/lib/debug directory ++ bool load_dwarf_file(); ++ ++ ++ bool read_debug_info(DebugInfo* debug_info) const; ++ ++ bool load_dwarf_file_from_same_directory(DwarfFilePath& dwarf_file_path); ++ bool load_dwarf_file_from_env_var_path(DwarfFilePath& dwarf_file_path); ++ bool load_dwarf_file_from_env_path_folder(DwarfFilePath& dwarf_file_path, const char* dwarf_path_from_env, const char* folder); ++ bool load_dwarf_file_from_debug_sub_directory(DwarfFilePath& dwarf_file_path); ++ bool load_dwarf_file_from_usr_lib_debug(DwarfFilePath& dwarf_file_path); ++ bool open_valid_debuginfo_file(const DwarfFilePath& dwarf_file_path); ++ static uint32_t get_file_crc(FILE* const file); ++ static uint gnu_debuglink_crc32(uint32_t crc, uint8_t* buf, size_t len); ++ ++ protected: ++ FILE* const fd() const { return m_file; } ++ ++ // Read the section header of section 'name'. ++ bool read_section_header(const char* name, Elf_Shdr& hdr) const; ++ bool is_valid_dwarf_file() const; ++}; ++ ++/* ++ * This class parses and reads filename and line number information from an associated .debuginfo file that belongs to ++ * this ELF file or directly from this ELF file if there is no separate .debuginfo file. The debug info is written by GCC ++ * in DWARF - a standardized debugging data format. There are special sections where the DWARF info is written to. These ++ * sections can either be put into the same ELF file or a separate .debuginfo file. For simplicity, when referring to the ++ * "DWARF file" or the ".debuginfo file" we just mean the file that contains the required DWARF sections. The current version ++ * of GCC uses DWARF version 4 as default which is defined in the official standard: http://www.dwarfstd.org/doc/DWARF4.pdf. ++ * This class is able to parse 32-bit DWARF version 4 for 32 and 64-bit Linux builds. GCC does not emit 64-bit DWARF and ++ * therefore is not supported by this parser. For some reason, GCC emits DWARF version 3 for the .debug_line section as a ++ * default. This parser was therefore adapted to support DWARF version 3 and 4 for the .debug_line section. Apart from that, ++ * other DWARF versions, especially the newest version 5, are not (yet) supported. ++ * ++ * Description of used DWARF file sections: ++ * - .debug_aranges: A table that consists of sets of variable length entries, each set describing the portion of the ++ * program's address space that is covered by a single compilation unit. In other words, the entries ++ * describe a mapping between addresses and compilation units. ++ * - .debug_info: The core DWARF data containing DWARF Information Entries (DIEs). Each DIE consists of a tag and a ++ * series of attributes. Each (normal) compilation unit is represented by a DIE with the tag ++ * DW_TAG_compile_unit and contains children. For our purposes, we are only interested in this DIE to ++ * get to the .debug_line section. We do not care about the children. This parser currently only ++ * supports normal compilation units and no partial compilation or type units. ++ * - .debug_abbrev: Represents abbreviation tables for all compilation units. A table for a specific compilation unit ++ * consists of a series of abbreviation declarations. Each declaration specifies a tag and attributes ++ * for a DIE. The DIEs from the compilation units in the .debug_info section need the abbreviation table ++ * to decode their attributes (their meaning and size). ++ * - .debug_line: Contains filename and line number information for each compilation unit. To get the information, a ++ * state machine needs to be executed which generates a matrix. Each row of this matrix describes the ++ * filename and line number (among other information) for a specific offset in the associated ELF library ++ * file. The state machine is executed until the row for the requested offset is found. The filename and ++ * line number information can then be fetched with the current register values of the state machine. ++ * ++ * Algorithm ++ * --------- ++ * Given: Offset into the ELF file library. ++ * Return: Filename and line number for this offset. ++ * (1) First, the path to the .debuginfo DWARF file is found by inspecting the .gnu_debuglink section of the library file. ++ * The DWARF file is then opened by calling the constructor of this class. Once this is done, the processing of the ++ * DWARF file is initiated by calling find_filename_and_line_number(). ++ * (2) Find the compilation unit offset by reading entries from the section .debug_aranges, which contain address range ++ * descriptors, until we find the correct descriptor that includes the library offset. ++ * (3) Find the .debug_line offset for the line number information program from the .debug_info section: ++ * (a) Parse the compilation unit header from the .debug_info section at the offset obtained by (2). ++ * (b) Read the debug_abbrev_offset into the .debug_abbrev section that belongs to this compilation unit from the ++ * header obtained in (3a). ++ * (c) Read the abbreviation code that immediately follows the compilation unit header from (3a) which is needed to ++ * find the correct entry in the .debug_abbrev section. ++ * (d) Find the correct entry in the abbreviation table in the .debug_abbrev section by starting to parse entries at ++ * the debug_abbrev_offset from (3b) until we find the correct one matching the abbreviation code from (3c). ++ * (e) Read the specified attributes of the abbreviation entry from (3d) from the compilation unit (in the .debug_info ++ * section) until we find the attribute DW_AT_stmt_list. This attributes represents an offset into the .debug_line ++ * section which contains the line number program information to get the filename and the line number. ++ * (4) Find the filename and line number belonging to the given library offset by running the line number program state ++ * machine with its registers. This creates a matrix where each row stores information for specific addresses (library ++ * offsets). The state machine executes different opcodes which modify the state machine registers. Certain opcodes ++ * will add a new row to the matrix by taking the current values of state machine registers. As soon as the correct ++ * matrix row matching the library offset is found, we can read the line number from the line register of the state ++ * machine and parse the filename from the line number program header with the given file index from the file register ++ * of the state machine. ++ * ++ * More details about the different phases can be found at the associated classes and methods. A visualization of the ++ * algorithm inside the different sections can be found in the class comments for DebugAranges, DebugAbbrev and ++ * LineNumberProgram further down in this file. ++ * ++ * Available (develop) log levels (-XX:TraceDwarfLevel=[1,4]) which are only present in debug builds. Each level prints ++ * all the logs of the previous levels and adds some more fine-grained logging: ++ * - Level 1 (summary + errors): ++ * - Prints the path of parsed DWARF file together with the resulting source information. ++ * - Prints all errors. ++ * - Level 2 (info): ++ * - Prints the found offsets of all DWARF sections ++ * - Level 3 (debug): ++ * - Prints the results of the steps (1) - (4) together with the generated line information matrix. ++ * - Level 4 (trace): ++ * - Complete information about intermediate states/results when parsing the DWARF file. ++ */ ++class DwarfFile : public ElfFile { ++ ++ static const uint8_t ADDRESS_SIZE = NOT_LP64(4) LP64_ONLY(8); ++ // We only support 32-bit DWARF (emitted by GCC) which uses 32-bit values for DWARF section lengths and offsets ++ // relative to the beginning of a section. ++ static const uint8_t DWARF_SECTION_OFFSET_SIZE = 4; ++ ++ class MarkedDwarfFileReader : public MarkedFileReader { ++ private: ++ long _current_pos; ++ long _max_pos; // Used to guarantee that we stop reading in case we reached the end of a section. ++ ++ bool read_leb128(uint64_t* result, int8_t check_size, bool is_signed); ++ public: ++ MarkedDwarfFileReader(FILE* const fd) : MarkedFileReader(fd), _current_pos(-1), _max_pos(-1) {} ++ ++ virtual bool set_position(long new_pos); ++ long get_position() const { return _current_pos; } ++ void set_max_pos(long max_pos) { _max_pos = max_pos; } ++ // Have we reached the limit of maximally allowable bytes to read? Used to ensure to stop reading when a section ends. ++ bool has_bytes_left() const; ++ // Call this if another file reader has changed the position of the same file handle. ++ bool update_to_stored_position(); ++ // Must be called to restore the old position before this file reader changed it with update_to_stored_position(). ++ bool reset_to_previous_position(); ++ bool move_position(long offset); ++ bool read_sbyte(int8_t* result); ++ bool read_byte(uint8_t* result); ++ bool read_word(uint16_t* result); ++ bool read_dword(uint32_t* result); ++ bool read_qword(uint64_t* result); ++ bool read_uleb128_ignore(int8_t check_size = -1); ++ bool read_uleb128(uint64_t* result, int8_t check_size = -1); ++ bool read_sleb128(int64_t* result, int8_t check_size = -1); ++ // Reads 4 bytes for 32-bit and 8 bytes for 64-bit builds. ++ bool read_address_sized(uintptr_t* result); ++ bool read_string(char* result = NULL, size_t result_len = 0); ++ }; ++ ++ // (2) Processing the .debug_aranges section to find the compilation unit which covers offset_in_library. ++ // This is specified in section 6.1.2 of the DWARF 4 spec. ++ // ++ // Structure of .debug_aranges: ++ // Section Header ++ // % Table of variable length sets describing the address space covered by a compilation unit ++ // % Set 1 ++ // ... ++ // % Set i: ++ // % Set header ++ // ... ++ // debug_info_offset -> offset to compilation unit ++ // % Series of address range descriptors [beginning_address, range_length]: ++ // % Descriptor 1 ++ // ... ++ // % Descriptor j: ++ // beginning_address <= offset_in_library < beginning_address + range_length? ++ // => Found the correct set covering offset_in_library. Take debug_info_offset from the set header to get ++ // to the correct compilation unit in .debug_info. ++ class DebugAranges { ++ ++ // The header is defined in section 6.1.2 of the DWARF 4 spec. ++ struct DebugArangesSetHeader { ++ // The total length of all of the entries for that set, not including the length field itself. ++ uint32_t _unit_length; ++ ++ // This number is specific to the address lookup table and is independent of the DWARF version number. ++ uint16_t _version; ++ ++ // The offset from the beginning of the .debug_info or .debug_types section of the compilation unit header referenced ++ // by the set. In this parser we only use it as offset into .debug_info. This must be 4 bytes for 32-bit DWARF. ++ uint32_t _debug_info_offset; ++ ++ // The size of an address in bytes on the target architecture, 4 bytes for 32-bit and 8 bytes for 64-bit Linux builds. ++ uint8_t _address_size; ++ ++ // The size of a segment selector in bytes on the target architecture. This should be 0. ++ uint8_t _segment_size; ++ }; ++ ++ // Address descriptor defining a range that is covered by a compilation unit. It is defined in section 6.1.2 after ++ // the set header in the DWARF 4 spec. ++ struct AddressDescriptor { ++ uintptr_t beginning_address; ++ uintptr_t range_length; ++ }; ++ ++ DwarfFile* _dwarf_file; ++ MarkedDwarfFileReader _reader; ++ uint32_t _section_start_address; ++ ++ bool read_section_header(); ++ bool read_set_header(DebugArangesSetHeader& header); ++ bool read_address_descriptors(uint32_t offset_in_library, bool& found_matching_set); ++ bool read_address_descriptor(AddressDescriptor& descriptor); ++ static bool does_match_offset(uint32_t offset_in_library, const AddressDescriptor& descriptor) ; ++ static bool is_terminating_entry(const AddressDescriptor& descriptor); ++ public: ++ DebugAranges(DwarfFile* dwarf_file) : _dwarf_file(dwarf_file), _reader(dwarf_file->fd()), _section_start_address(0) {} ++ bool find_compilation_unit_offset(uint32_t offset_in_library, uint32_t* compilation_unit_offset); ++ ++ }; ++ ++ // (3a-c,e) The compilation unit is read from the .debug_info section. The structure of .debug_info is shown in the ++ // comments of class DebugAbbrev. ++ class CompilationUnit { ++ ++ // Attribute form encodings from Figure 21 in section 7.5 of the DWARF 4 spec. ++ static const uint8_t DW_FORM_addr = 0x01; // address ++ static const uint8_t DW_FORM_block2 = 0x03; // block ++ static const uint8_t DW_FORM_block4 = 0x04; // block ++ static const uint8_t DW_FORM_data2 = 0x05; // constant ++ static const uint8_t DW_FORM_data4 = 0x06; // constant ++ static const uint8_t DW_FORM_data8 = 0x07; // constant ++ static const uint8_t DW_FORM_string = 0x08; // string ++ static const uint8_t DW_FORM_block = 0x09; // block ++ static const uint8_t DW_FORM_block1 = 0x0a; // block ++ static const uint8_t DW_FORM_data1 = 0x0b; // constant ++ static const uint8_t DW_FORM_flag = 0x0c; // flag ++ static const uint8_t DW_FORM_sdata = 0x0d; // constant ++ static const uint8_t DW_FORM_strp = 0x0e; // string ++ static const uint8_t DW_FORM_udata = 0x0f; // constant ++ static const uint8_t DW_FORM_ref_addr = 0x10; // reference0; ++ static const uint8_t DW_FORM_ref1 = 0x11; // reference ++ static const uint8_t DW_FORM_ref2 = 0x12; // reference ++ static const uint8_t DW_FORM_ref4 = 0x13; // reference ++ static const uint8_t DW_FORM_ref8 = 0x14; // reference ++ static const uint8_t DW_FORM_ref_udata = 0x15; // reference ++ static const uint8_t DW_FORM_indirect = 0x16; // see Section 7.5.3 ++ static const uint8_t DW_FORM_sec_offset = 0x17; // lineptr, loclistptr, macptr, rangelistptr ++ static const uint8_t DW_FORM_exprloc = 0x18; // exprloc ++ static const uint8_t DW_FORM_flag_present = 0x19; // flag ++ static const uint8_t DW_FORM_ref_sig8 = 0x20; // reference ++ ++ // The header is defined in section 7.5.1.1 of the DWARF 4 spec. ++ struct CompilationUnitHeader { ++ // The length of the .debug_info contribution for that compilation unit, not including the length field itself. ++ uint32_t _unit_length; ++ ++ // The version of the DWARF information for the compilation unit. The value in this field is 4 for DWARF 4. ++ uint16_t _version; ++ ++ // The offset into the .debug_abbrev section. This offset associates the compilation unit with a particular set of ++ // debugging information entry abbreviations. ++ uint32_t _debug_abbrev_offset; ++ ++ // The size in bytes of an address on the target architecture, 4 bytes for 32-bit and 8 bytes for 64-bit Linux builds. ++ uint8_t _address_size; ++ }; ++ ++ DwarfFile* _dwarf_file; ++ MarkedDwarfFileReader _reader; ++ CompilationUnitHeader _header; ++ const uint32_t _compilation_unit_offset; ++ ++ // Result of a request initiated by find_debug_line_offset(). ++ uint32_t _debug_line_offset; ++ ++ bool read_header(); ++ public: ++ CompilationUnit(DwarfFile* dwarf_file, uint32_t compilation_unit_offset) ++ : _dwarf_file(dwarf_file), _reader(dwarf_file->fd()), _compilation_unit_offset(compilation_unit_offset), _debug_line_offset(0) {} ++ ++ bool find_debug_line_offset(uint32_t* debug_line_offset); ++ bool read_attribute_value(uint64_t attribute_form, bool is_DW_AT_stmt_list_attribute); ++ }; ++ ++ // (3d) Read from the .debug_abbrev section at the debug_abbrev_offset specified by the compilation unit header. ++ // ++ // The interplay between the .debug_info and .debug_abbrev sections is more complex. The following visualization of the structure ++ // of both sections support the comments found in the parsing steps of the CompilationUnit and DebugAbbrev class. ++ // ++ // Structure of .debug_abbrev: ++ // Section Header ++ // % Series of abbreviation tables ++ // % Abbreviation table 1 ++ // ... ++ // % Abbreviation table for compilation unit at debug_abbrev_offset: ++ // % Series of declarations: ++ // % Declaration 1: ++ // abbreviation code ++ // tag ++ // DW_CHILDREN_yes/no ++ // % Series of attribute specifications ++ // % Attribute specification 1: ++ // attribute name ++ // attribute form ++ // ... ++ // % Last attribute specification: ++ // 0 ++ // 0 ++ // ... ++ // % Declaration i: ++ // Abbrev code read from compilation unit [AC] ++ // DW_TAG_compile_unit ++ // DW_CHILDREN_yes ++ // % Series of attribute specifications ++ // % Attribute specification 1 [AS1] ++ // ... ++ // % Attribute specification j [ASj]: ++ // DW_AT_stmt_list ++ // DW_FORM_sec_offset ++ // ++ // ++ // Structure of .debug_info: ++ // Section Header ++ // % Series of compilation units ++ // % Compilation unit 1 ++ // ... ++ // % Compilation unit i for library offset fetched from .debug_aranges: ++ // % Compilation unit header: ++ // ... ++ // debug_abbrev_offset -> offset for abbreviation table in .debug_abbrev for this compilation unit ++ // ... ++ // Abbrev code -> used in .debug_abbrev to find the correct declaration [AC] ++ // % Series of attribute values ++ // Attribute value 1 (in the format defined by attribute specification 1 [AS1]) ++ // ... ++ // Attribute value j (in the format defined by attribute specification j [ASj]): ++ // => Specifies Offset to line number program for this compilation unit in .debug_line ++ class DebugAbbrev { ++ ++ struct AbbreviationDeclaration { ++ uint64_t _abbrev_code; ++ uint64_t _tag; ++ uint8_t _has_children; ++ }; ++ ++ struct AttributeSpecification { ++ uint64_t _name; ++ uint64_t _form; ++ }; ++ ++ // Tag encoding from Figure 18 in section 7.5 of the DWARF 4 spec. ++ static const uint8_t DW_TAG_compile_unit = 0x11; ++ ++ // Child determination encoding from Figure 19 in section 7.5 of the DWARF 4 spec. ++ static const uint8_t DW_CHILDREN_yes = 0x01; ++ ++ // Attribute encoding from Figure 20 in section 7.5 of the DWARF 4 spec. ++ static const uint8_t DW_AT_stmt_list = 0x10; ++ ++ /* There is no specific header for this section */ ++ ++ DwarfFile* _dwarf_file; ++ MarkedDwarfFileReader _reader; ++ CompilationUnit* _compilation_unit; // Need to read from compilation unit while parsing the entries in .debug_abbrev. ++ ++ // Result field of a request ++ uint32_t* _debug_line_offset; ++ ++ bool read_declaration(AbbreviationDeclaration& declaration); ++ static bool is_wrong_or_unsupported_format(const AbbreviationDeclaration& declaration); ++ bool read_attribute_specifications(bool is_DW_TAG_compile_unit); ++ bool read_attribute_specification(AttributeSpecification& specification); ++ static bool is_terminating_specification(const AttributeSpecification& attribute_specification) ; ++ ++ public: ++ DebugAbbrev(DwarfFile* dwarf_file, CompilationUnit* compilation_unit) : ++ _dwarf_file(dwarf_file), _reader(_dwarf_file->fd()), _compilation_unit(compilation_unit), ++ _debug_line_offset(NULL) {} ++ ++ bool read_section_header(uint32_t debug_abbrev_offset); ++ bool find_debug_line_offset(uint64_t abbrev_code); ++ }; ++ ++ // (4) The line number program for the compilation unit at the offset of the .debug_line obtained by (3). ++ // For some reason, earlier GCC versions emit the line number program in DWARF 2 or 3 format even though the ++ // default is DWARF 4. It also mixes the standards (see comments in the parsing code). ++ // ++ // Therefore, this class supports DWARF 2, 3 and 4 parsing as specified in section 6.2 of the DWARF specs. ++ // The parsing of DWARF 2 is already covered by the parsing of DWARF 3 as they use the shared opcodes in the same way. ++ // The parsing of DWARF 4, however, needs some adaptation as it consumes more data for some shared opcodes. ++ // ++ // DWARF 2 standard: https://dwarfstd.org/doc/dwarf-2.0.0.pdf ++ // DWARF 3 standard: https://dwarfstd.org/doc/Dwarf3.pdf ++ // ++ // ++ // Structure of .debug_ling: ++ // Section Header ++ // % Series of line number program entries for each compilation unit ++ // % Line number program 1 ++ // ... ++ // % Line number program i for our compilation unit: ++ // % Line program header unit header: ++ // ... ++ // version -> currently emits version 3 by default ++ // ... ++ // file_name -> sequence of file names ++ // % Sequence of opcodes as part of the line number program to build the line number information matrix: ++ // % Format of matrix: [offset, line, directory_index, file_index] ++ // % Line 1 ++ // ... ++ // % Line j: ++ // [offset matching offset_in_library, line, directory_index, file_index] ++ // => Get line number + look up file_index in file_name list (pick file_index'th string) ++ class LineNumberProgram { ++ ++ // Standard opcodes for the line number program defined in section 6.2.5.2 of the DWARF 4 spec. ++ static const uint8_t DW_LNS_copy = 1; ++ static const uint8_t DW_LNS_advance_pc = 2; ++ static const uint8_t DW_LNS_advance_line = 3; ++ static const uint8_t DW_LNS_set_file = 4; ++ static const uint8_t DW_LNS_set_column = 5; ++ static const uint8_t DW_LNS_negate_stmt = 6; ++ static const uint8_t DW_LNS_set_basic_block = 7; ++ static const uint8_t DW_LNS_const_add_pc = 8; ++ static const uint8_t DW_LNS_fixed_advance_pc = 9; ++ static const uint8_t DW_LNS_set_prologue_end = 10; // Introduced with DWARF 3 ++ static const uint8_t DW_LNS_set_epilogue_begin = 11; // Introduced with DWARF 3 ++ static const uint8_t DW_LNS_set_isa = 12; // Introduced with DWARF 3 ++ ++ // Extended opcodes for the line number program defined in section 6.2.5.2 of the DWARF 4 spec. ++ static const uint8_t DW_LNE_end_sequence = 1; ++ static const uint8_t DW_LNE_set_address = 2; ++ static const uint8_t DW_LNE_define_file = 3; ++ static const uint8_t DW_LNE_set_discriminator = 4; // Introduced with DWARF 4 ++ ++ // The header is defined in section 6.2.4 of the DWARF 4 spec. ++ struct LineNumberProgramHeader { ++ // The size in bytes of the line number information for this compilation unit, not including the unit_length ++ // field itself. 32-bit DWARF uses 4 bytes. ++ uint32_t _unit_length; ++ ++ // The version of the DWARF information for the line number program unit. The value in this field should be 4 for ++ // DWARF 4 and version 3 as used for DWARF 3. ++ uint16_t _version; ++ ++ // The number of bytes following the header_length field to the beginning of the first byte of the line number ++ // program itself. 32-bit DWARF uses 4 bytes. ++ uint32_t _header_length; ++ ++ // The size in bytes of the smallest target machine instruction. Line number program opcodes that alter the address ++ // and op_index registers use this and maximum_operations_per_instruction in their calculations. ++ uint8_t _minimum_instruction_length; ++ ++ // The maximum number of individual operations that may be encoded in an instruction. Line number program opcodes ++ // that alter the address and op_index registers use this and minimum_instruction_length in their calculations. ++ // For non-VLIW architectures, this field is 1, the op_index register is always 0, and the operation pointer is ++ // simply the address register. This is only used with DWARF 4. ++ uint8_t _maximum_operations_per_instruction; ++ ++ // The initial value of the is_stmt register. ++ uint8_t _default_is_stmt; ++ ++ // This parameter affects the meaning of the special opcodes. ++ int8_t _line_base; ++ ++ // This parameter affects the meaning of the special opcodes. ++ uint8_t _line_range; ++ ++ // The number assigned to the first special opcode. ++ uint8_t _opcode_base; ++ ++ // This array specifies the number of LEB128 operands for each of the standard opcodes. The first element of the ++ // array corresponds to the opcode whose value is 1, and the last element corresponds to the opcode whose value is ++ // opcode_base-1. DWARF 2 uses 9 standard opcodes while DWARF 3 and 4 use 12. ++ uint8_t _standard_opcode_lengths[12]; ++ ++ /* ++ * The following fields are not part of the real header and are only used for the implementation. ++ */ ++ // Offset where the filename strings are starting in header. ++ long _file_names_offset; ++ ++ // _header_length only specifies the number of bytes following the _header_length field. It does not include ++ // the size of _unit_length, _version and _header_length itself. This constant represents the number of missing ++ // bytes to get the real size of the header: ++ // sizeof(_unit_length) + sizeof(_version) + sizeof(_header_length) = 4 + 2 + 4 = 10 ++ static const uint8_t HEADER_DESCRIPTION_BYTES = 10; ++ }; ++ ++ // The line number program state consists of several registers that hold the current state of the line number program ++ // state machine. The state/different state registers are defined in section 6.2.2 of the DWARF 4 spec. Most of these ++ // fields (state registers) are not used to get the filename and the line number information. ++ struct LineNumberProgramState : public CHeapObj { ++ // The program-counter value corresponding to a machine instruction generated by the compiler. ++ // 4 bytes on 32-bit and 8 bytes on 64-bit. ++ uintptr_t _address; ++ ++ // The index of an operation within a VLIW instruction. The index of the first operation is 0. For non-VLIW ++ // architectures, this register will always be 0. ++ // The address and op_index registers, taken together, form an operation pointer that can reference any ++ // individual operation with the instruction stream. This field was introduced with DWARF 4. ++ uint32_t _op_index; ++ ++ // The identity of the source file corresponding to a machine instruction. ++ uint32_t _file; ++ ++ // A source line number. Lines are numbered beginning at 1. The compiler may emit the value 0 in cases where an ++ // instruction cannot be attributed to any source line. ++ uint32_t _line; ++ ++ // A column number within a source line. Columns are numbered beginning at 1. The value 0 is reserved to indicate ++ // that a statement begins at the “left edge” of the line. ++ uint32_t _column; ++ ++ // Indicates that the current instruction is a recommended breakpoint location. ++ bool _is_stmt; ++ ++ // Indicates that the current instruction is the beginning of a basic block. ++ bool _basic_block; ++ ++ // Indicates that the current address is that of the first byte after the end of a sequence of target machine ++ // instructions. end_sequence terminates a sequence of lines. ++ bool _end_sequence; ++ ++ // Indicates that the current address is one (of possibly many) where execution should be suspended for an entry ++ // breakpoint of a function. This field was introduced with DWARF 3. ++ bool _prologue_end; ++ ++ // Indicates that the current address is one (of possibly many) where execution should be suspended for an exit ++ // breakpoint of a function. This field was introduced with DWARF 3. ++ bool _epilogue_begin; ++ ++ // Encodes the applicable instruction set architecture for the current instruction. ++ // This field was introduced with DWARF 3. ++ uint32_t _isa; ++ ++ // Identifies the block to which the current instruction belongs. This field was introduced with DWARF 4. ++ uint32_t _discriminator; ++ ++ /* ++ * Additional fields which are not part of the actual state as described in DWARF spec. ++ */ ++ // Header fields ++ // Specifies which DWARF version is used in the .debug_line section. Supported version: DWARF 2, 3, and 4. ++ const uint16_t _dwarf_version; ++ const bool _initial_is_stmt; ++ ++ // Implementation specific fields ++ bool _append_row; ++ bool _do_reset; ++ bool _first_entry_in_sequence; ++ bool _can_sequence_match_offset; ++ bool _found_match; ++ ++ LineNumberProgramState(const LineNumberProgramHeader& header) ++ : _is_stmt(header._default_is_stmt != 0), _dwarf_version(header._version), ++ _initial_is_stmt(header._default_is_stmt != 0), _found_match(false) { ++ reset_fields(); ++ } ++ ++ void reset_fields(); ++ // Defined in section 6.2.5.1 of the DWARF spec 4. add_to_address_register() must always be executed before set_index_register. ++ void add_to_address_register(uint32_t operation_advance, const LineNumberProgramHeader& header); ++ void set_index_register(uint32_t operation_advance, const LineNumberProgramHeader& header); ++ }; ++ ++ DwarfFile* _dwarf_file; ++ MarkedDwarfFileReader _reader; ++ LineNumberProgramHeader _header; ++ LineNumberProgramState* _state; ++ const uint32_t _offset_in_library; ++ const uint64_t _debug_line_offset; ++ bool _is_pc_after_call; ++ ++ bool read_header(); ++ bool run_line_number_program(char* filename, size_t filename_len, int* line); ++ bool apply_opcode(); ++ bool apply_extended_opcode(); ++ bool apply_standard_opcode(uint8_t opcode); ++ void apply_special_opcode(const uint8_t opcode); ++ bool does_offset_match_entry(uintptr_t previous_address, uint32_t previous_file, uint32_t previous_line); ++ void print_and_store_prev_entry(uint32_t previous_file, uint32_t previous_line); ++ bool get_filename_from_header(uint32_t file_index, char* filename, size_t filename_len); ++ ++ public: ++ LineNumberProgram(DwarfFile* dwarf_file, uint32_t offset_in_library, uint64_t debug_line_offset, bool is_pc_after_call) ++ : _dwarf_file(dwarf_file), _reader(dwarf_file->fd()), _offset_in_library(offset_in_library), ++ _debug_line_offset(debug_line_offset), _is_pc_after_call(is_pc_after_call) {} ++ ++ bool find_filename_and_line_number(char* filename, size_t filename_len, int* line); ++ }; ++ ++ public: ++ DwarfFile(const char* filepath) : ElfFile(filepath) {} ++ ++ /* ++ * Starting point of reading line number and filename information from the DWARF file. ++ * ++ * Given: Offset into the ELF library file, a filename buffer of size filename_size, a line number pointer. ++ * Return: True: The filename is set in the 'filename' buffer and the line number at the address pointed to by 'line'. ++ * False: Something went wrong either while reading from the file or during parsing due to an unexpected format. ++ * This could happen if the DWARF file is in an unsupported or wrong format. ++ * ++ * More details about the different phases can be found at the associated methods. ++ */ ++ bool get_filename_and_line_number(uint32_t offset_in_library, char* filename, size_t filename_len, int* line, bool is_pc_after_call); + }; + + #endif // !_WINDOWS && !__APPLE__ +diff --git a/hotspot/src/share/vm/utilities/nativeCallStack.cpp b/hotspot/src/share/vm/utilities/nativeCallStack.cpp +index ee6eb31..6b18b49 100644 +--- a/hotspot/src/share/vm/utilities/nativeCallStack.cpp ++++ b/hotspot/src/share/vm/utilities/nativeCallStack.cpp +@@ -24,6 +24,7 @@ + + #include "precompiled.hpp" + #include "runtime/os.hpp" ++#include "utilities/decoder.hpp" + #include "utilities/globalDefinitions.hpp" + #include "utilities/nativeCallStack.hpp" + +@@ -93,6 +94,8 @@ void NativeCallStack::print_on(outputStream* out, int indent) const { + address pc; + char buf[1024]; + int offset; ++ int line_no; ++ + if (is_empty()) { + for (int index = 0; index < indent; index ++) out->print(" "); + #if PLATFORM_NATIVE_STACK_WALKING_SUPPORTED +@@ -107,10 +110,14 @@ void NativeCallStack::print_on(outputStream* out, int indent) const { + // Print indent + for (int index = 0; index < indent; index ++) out->print(" "); + if (os::dll_address_to_function_name(pc, buf, sizeof(buf), &offset)) { +- out->print_cr("[" PTR_FORMAT "] %s+0x%x", p2i(pc), buf, offset); ++ out->print("[" PTR_FORMAT "] %s+0x%x", p2i(pc), buf, offset); + } else { +- out->print_cr("[" PTR_FORMAT "]", p2i(pc)); ++ out->print("[" PTR_FORMAT "]", p2i(pc)); ++ } ++ if (Decoder::get_source_info(pc, buf, sizeof(buf), &line_no, frame != 0)) { ++ out->print(" (%s:%d)", buf, line_no); + } ++ out->cr(); + } + } + } +diff --git a/hotspot/src/share/vm/utilities/vmError.cpp b/hotspot/src/share/vm/utilities/vmError.cpp +index 261591d..26408fa 100644 +--- a/hotspot/src/share/vm/utilities/vmError.cpp ++++ b/hotspot/src/share/vm/utilities/vmError.cpp +@@ -1209,3 +1209,9 @@ void VMError::report_java_out_of_memory() { + VMThread::execute(&op); + } + } ++ ++// Returns true if the current thread reported a fatal error. ++bool VMError::is_error_reported_in_current_thread() { ++ return first_error_tid == os::current_thread_id(); ++} ++ +diff --git a/hotspot/src/share/vm/utilities/vmError.hpp b/hotspot/src/share/vm/utilities/vmError.hpp +index 299cfaa..21db84d 100644 +--- a/hotspot/src/share/vm/utilities/vmError.hpp ++++ b/hotspot/src/share/vm/utilities/vmError.hpp +@@ -140,6 +140,9 @@ public: + static jlong get_first_error_tid() { + return first_error_tid; + } ++ ++ // Returns true if the current thread reported a fatal error. ++ static bool is_error_reported_in_current_thread(); + }; + + #endif // SHARE_VM_UTILITIES_VMERROR_HPP +diff --git a/jdk/test/jdk/java/dwarf/TestDwarf.java b/jdk/test/jdk/java/dwarf/TestDwarf.java +new file mode 100644 +index 0000000..8e41a28 +--- /dev/null ++++ b/jdk/test/jdk/java/dwarf/TestDwarf.java +@@ -0,0 +1,240 @@ ++/* ++ * Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2022, Huawei Technologies Co., Ltd. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ */ ++ ++/* ++ * @test ++ * @bug 8242181 ++ * @library ../../../lib/ ../../../lib/testlibrary ++ * @summary Test DWARF parser with various crashes if debug symbols are available. If the libjvm debug symbols are not ++ * in the same directory as the libjvm.so file, in a subdirectory called .debug, or in the path specified ++ * by the environment variable _JVM_DWARF_PATH, then no verification of the hs_err_file is done for libjvm.so. ++ * @requires vm.compMode != "Xint" & os.family == "linux" ++ * @run main/othervm TestDwarf ++ */ ++ ++import jdk.test.lib.Asserts; ++import jdk.test.lib.Platform; ++import jdk.testlibrary.OutputAnalyzer; ++import jdk.testlibrary.ProcessTools; ++ ++import sun.misc.Unsafe; ++ ++import java.io.BufferedReader; ++import java.io.File; ++import java.io.FileReader; ++import java.lang.reflect.Field; ++import java.util.ArrayList; ++import java.util.Arrays; ++import java.util.List; ++import java.util.regex.Matcher; ++import java.util.regex.Pattern; ++ ++public class TestDwarf { ++ public static void main(String[] args) throws Throwable { ++ if (args.length != 0) { ++ switch (args[0]) { ++ case "outOfMemory" : ++ crashOutOfMemory(); ++ Asserts.fail("Should crash in crashOutOfMemory()"); ++ case "abortVMOnException" : ++ crashAbortVmOnException(); ++ Asserts.fail("Should crash in crashAbortVmOnException()"); ++ } ++ } else { ++ try { ++ test(); ++ } catch (UnsupportedDwarfVersionException e) { ++ System.out.println("Skip test due to a DWARF section that is in an unsupported version by the parser."); ++ } ++ } ++ } ++ ++ // Crash the VM in different ways in order to verify that DWARF parsing is able to print the source information ++ // in the hs_err_files for each VM and C stack frame. ++ private static void test() throws Throwable { ++ runAndCheck(new Flags("-Xcomp", "-XX:CICrashAt=1", "-version")); ++ runAndCheck(new Flags("-Xmx100M", "-XX:ErrorHandlerTest=15", "-version")); ++ runAndCheck(new Flags("-XX:+CrashGCForDumpingJavaThread", "-version")); ++ runAndCheck(new Flags("-Xmx10m", "-XX:+CrashOnOutOfMemoryError", TestDwarf.class.getCanonicalName(), "outOfMemory")); ++ // Use -XX:-TieredCompilation as C1 is currently not aborting the VM (JDK-8264899). ++ runAndCheck(new Flags("-XX:-TieredCompilation", "-XX:+UnlockDiagnosticVMOptions", "-XX:AbortVMOnException=MyException", ++ TestDwarf.class.getCanonicalName(), "abortVMOnException")); ++ if (Platform.isX64() || Platform.isX86()) { ++ // Not all platforms raise SIGFPE but x86_32 and x86_64 do. ++ } ++ } ++ ++ private static void runAndCheck(Flags flags, DwarfConstraint... constraints) throws Throwable { ++ OutputAnalyzer crashOut; ++ ProcessBuilder pb; ++ int flag_size = flags.getFlags().size(); ++ pb = ProcessTools.createJavaProcessBuilder(flags.getFlags().toArray(new String[flag_size])); ++ crashOut = ProcessTools.executeProcess(pb); ++ String crashOutputString = crashOut.getOutput(); ++ Asserts.assertNotEquals(crashOut.getExitValue(), 0, "Crash JVM should not exit gracefully"); ++ Pattern pattern = Pattern.compile("hs_err_pid[0-9]*.log"); ++ Matcher matcher = pattern.matcher(crashOutputString); ++ System.out.println(crashOutputString); ++ if (matcher.find()) { ++ String hsErrFileName = matcher.group(); ++ System.out.println("hs_err_file: " + hsErrFileName); ++ File hs_err_file = new File(hsErrFileName); ++ BufferedReader reader = new BufferedReader(new FileReader(hs_err_file)); ++ String line; ++ boolean foundNativeFrames = false; ++ int matches = 0; ++ int frameIdx = 0; ++ // Check all stack entries after the line starting with "Native frames" in the hs_err_file until an empty line ++ // is found which denotes the end of the stack frames. ++ while ((line = reader.readLine()) != null) { ++ if (foundNativeFrames) { ++ if (line.isEmpty()) { ++ // Done with the entire stack. ++ break; ++ } else if ((line.startsWith("C") || line.startsWith("V"))) { ++ // Could be VM or native C frame. There are usually no symbols available for libpthread.so. ++ matches++; ++ // File and library names are non-empty and may contain English letters, underscores, dots or numbers ([a-zA-Z0-9_.]+). ++ // Line numbers have at least one digit and start with non-zero ([1-9][0-9]*). ++ pattern = Pattern.compile("[CV][\\s\\t]+\\[([a-zA-Z0-9_.]+)\\+0x.+][\\s\\t]+.*\\+0x.+[\\s\\t]+\\([a-zA-Z0-9_.]+\\.[a-z]+:[1-9][0-9]*\\)"); ++ matcher = pattern.matcher(line); ++ if (!matcher.find()) { ++ checkNoSourceLine(crashOutputString, line); ++ } ++ ++ // Check additional DWARF constraints ++ if (constraints != null) { ++ int finalFrameIdx = frameIdx; ++ String finalLine = line; ++ Arrays.stream(constraints).forEach(c -> c.checkConstraint(finalFrameIdx, finalLine)); ++ } ++ } ++ frameIdx++; ++ } else if (line.startsWith("Native frames")) { ++ // Stack starts after this line. ++ foundNativeFrames = true; ++ } ++ } ++ Asserts.assertGreaterThan(matches, 0, "Could not find any stack frames"); ++ } else { ++ throw new RuntimeException("Could not find an hs_err_file"); ++ } ++ } ++ ++ /** ++ * There are some valid cases where we cannot find source information. Check these. ++ */ ++ private static void checkNoSourceLine(String crashOutputString, String line) { ++ Pattern pattern = Pattern.compile("[CV][\\s\\t]+\\[([a-zA-Z0-9_.]+)\\+0x.+][\\s\\t]+.*\\+0x"); ++ Matcher matcher = pattern.matcher(line); ++ Asserts.assertTrue(matcher.find(), "Must find library in \"" + line + "\""); ++ // Check if there are symbols available for library. If not, then we cannot find any source information for this library. ++ // This can happen if this test is run without any JDK debug symbols at all but also for some libraries like libpthread.so ++ // which usually has no symbols available. ++ String library = matcher.group(1); ++ pattern = Pattern.compile("Failed to load DWARF file for library.*" + library + ".*or find DWARF sections directly inside it"); ++ matcher = pattern.matcher(crashOutputString); ++ if (!matcher.find()) { ++ bailoutIfUnsupportedDwarfVersion(crashOutputString); ++ throw new RuntimeException("Could not find filename or line number in \"" + line + "\""); ++ } ++ // We should always find symbols for libTestDwarf.so. ++ Asserts.assertFalse(library.equals("libTestDwarf.so"), "Could not find filename or line number in \"" + line + "\" for libTestDwarf.so"); ++ System.out.println("Did not find symbols for " + library + ". If they are not in the same directory as " + library + " consider setting " + ++ "the environmental variable _JVM_DWARF_PATH to point to the debug symbols directory."); ++ } ++ ++ /** ++ * Some older GCC versions might emit DWARF sections in an old format that is not supported by the DWARF parser. ++ * If this is the case, skip this entire test by throwing UnsupportedDwarfVersionException. ++ */ ++ private static void bailoutIfUnsupportedDwarfVersion(String crashOutputString) { ++ Pattern pattern = Pattern.compile(".debug_\\S+ in unsupported DWARF version \\d+"); ++ Matcher matcher = pattern.matcher(crashOutputString); ++ if (matcher.find()) { ++ throw new UnsupportedDwarfVersionException(); ++ } ++ } ++ ++ // Crash with SIGSEGV. ++ private static void crashUnsafeAccess() throws Exception { ++ Field f = Unsafe.class.getDeclaredField("theUnsafe"); ++ f.setAccessible(true); ++ Unsafe unsafe = (Unsafe)f.get(null); ++ unsafe.putAddress(0, 0); // Crash ++ } ++ ++ // Crash with Internal Error: Java heap space. ++ private static void crashOutOfMemory() { ++ Object[] o = null; ++ ++ // Loop endlessly and consume memory until we run out. Will crash due to -XX:+CrashOnOutOfMemoryError. ++ while (true) { ++ o = new Object[] {o}; ++ } ++ } ++ ++ // Crash with Internal Error: Saw java.lang.RuntimeException, aborting. ++ // Crash happens due to an exception raised in combination with -XX:AbortVMOnException. ++ private static void crashAbortVmOnException() { ++ throw new MyException(); ++ } ++} ++ ++class UnsupportedDwarfVersionException extends RuntimeException { } ++ ++class MyException extends RuntimeException { } ++ ++class Flags { ++ private final List listOfOptions = new ArrayList<>(); ++ ++ Flags(String... flags) { ++ listOfOptions.add("-XX:TraceDwarfLevel=2"); // Always add debug flag ++ listOfOptions.addAll(Arrays.asList(flags)); ++ } ++ ++ public List getFlags() { ++ return listOfOptions; ++ } ++ ++} ++class DwarfConstraint { ++ private final int frameIdx; ++ private final String methodName; ++ private final String dwarfInfo; ++ ++ DwarfConstraint(int frameIdx, String methodName, String fileName, int lineNo) { ++ this.frameIdx = frameIdx; ++ this.methodName = methodName; ++ this.dwarfInfo = "(" + fileName + ":" + lineNo + ")"; ++ } ++ ++ public void checkConstraint(int currentFrameIdx, String line) { ++ if (frameIdx == currentFrameIdx) { ++ Asserts.assertTrue(line.contains(methodName), "Could not find method name " + methodName + " in \"" + line + "\""); ++ Asserts.assertTrue(line.contains(dwarfInfo) , "Could not find DWARF info " + dwarfInfo + " in \"" + line + "\""); ++ } ++ } ++} ++ +-- +1.8.3.1 diff --git a/8257695-linux-Add-process-memory-information-to-hs-e.patch b/8257695-linux-Add-process-memory-information-to-hs-e.patch new file mode 100644 index 0000000000000000000000000000000000000000..7cd83f422d0fa48247ce6de1b4365ed5c91e1a75 --- /dev/null +++ b/8257695-linux-Add-process-memory-information-to-hs-e.patch @@ -0,0 +1,123 @@ +From d68c637a36b65d0bce893991e9c910efbc06239a Mon Sep 17 00:00:00 2001 +From: eapen +Date: Mon, 12 Dec 2022 16:10:41 +0800 +Subject: [PATCH 10/33] I68TO2: 8257695: [linux] Add process-memory information to + hs-err and VM.info +--- + hotspot/src/os/linux/vm/os_linux.cpp | 67 ++++++++++++++++++++++++++++++++++-- + hotspot/src/os/linux/vm/os_linux.hpp | 3 +- + 2 files changed, 67 insertions(+), 3 deletions(-) + +diff --git a/hotspot/src/os/linux/vm/os_linux.cpp b/hotspot/src/os/linux/vm/os_linux.cpp +index 6dbedf5..4c265d5 100644 +--- a/hotspot/src/os/linux/vm/os_linux.cpp ++++ b/hotspot/src/os/linux/vm/os_linux.cpp +@@ -103,6 +103,9 @@ + # include + # include + # include ++#ifdef __GLIBC__ ++# include ++#endif + + PRAGMA_FORMAT_MUTE_WARNINGS_FOR_GCC + +@@ -2216,7 +2219,10 @@ void os::print_os_info(outputStream* st) { + + os::Posix::print_load_average(st); + +- os::Linux::print_full_memory_info(st); ++ os::Linux::print_system_memory_info(st); ++ st->cr(); ++ ++ os::Linux::print_process_memory_info(st); + + os::Linux::print_container_info(st); + } +@@ -2278,12 +2284,69 @@ void os::Linux::print_libversion_info(outputStream* st) { + st->cr(); + } + +-void os::Linux::print_full_memory_info(outputStream* st) { ++void os::Linux::print_system_memory_info(outputStream* st) { + st->print("\n/proc/meminfo:\n"); + _print_ascii_file("/proc/meminfo", st); + st->cr(); + } + ++void os::Linux::print_process_memory_info(outputStream* st) { ++ ++ st->print_cr("Process Memory:"); ++ ++ // Print virtual and resident set size; peak values; swap; and for ++ // rss its components if the kernel is recent enough. ++ ssize_t vmsize = -1, vmpeak = -1, vmswap = -1, ++ vmrss = -1, vmhwm = -1, rssanon = -1, rssfile = -1, rssshmem = -1; ++ const int num_values = 8; ++ int num_found = 0; ++ FILE* f = ::fopen("/proc/self/status", "r"); ++ char buf[256]; ++ while (::fgets(buf, sizeof(buf), f) != NULL && num_found < num_values) { ++ if ( (vmsize == -1 && sscanf(buf, "VmSize: " SSIZE_FORMAT " kB", &vmsize) == 1) || ++ (vmpeak == -1 && sscanf(buf, "VmPeak: " SSIZE_FORMAT " kB", &vmpeak) == 1) || ++ (vmswap == -1 && sscanf(buf, "VmSwap: " SSIZE_FORMAT " kB", &vmswap) == 1) || ++ (vmhwm == -1 && sscanf(buf, "VmHWM: " SSIZE_FORMAT " kB", &vmhwm) == 1) || ++ (vmrss == -1 && sscanf(buf, "VmRSS: " SSIZE_FORMAT " kB", &vmrss) == 1) || ++ (rssanon == -1 && sscanf(buf, "RssAnon: " SSIZE_FORMAT " kB", &rssanon) == 1) || ++ (rssfile == -1 && sscanf(buf, "RssFile: " SSIZE_FORMAT " kB", &rssfile) == 1) || ++ (rssshmem == -1 && sscanf(buf, "RssShmem: " SSIZE_FORMAT " kB", &rssshmem) == 1) ++ ) ++ { ++ num_found ++; ++ } ++ } ++ st->print_cr("Virtual Size: " SSIZE_FORMAT "K (peak: " SSIZE_FORMAT "K)", vmsize, vmpeak); ++ st->print("Resident Set Size: " SSIZE_FORMAT "K (peak: " SSIZE_FORMAT "K)", vmrss, vmhwm); ++ if (rssanon != -1) { // requires kernel >= 4.5 ++ st->print(" (anon: " SSIZE_FORMAT "K, file: " SSIZE_FORMAT "K, shmem: " SSIZE_FORMAT "K)", ++ rssanon, rssfile, rssshmem); ++ } ++ st->cr(); ++ if (vmswap != -1) { // requires kernel >= 2.6.34 ++ st->print_cr("Swapped out: " SSIZE_FORMAT "K", vmswap); ++ } ++ ++ // Print glibc outstanding allocations. ++ // (note: there is no implementation of mallinfo for muslc) ++#ifdef __GLIBC__ ++ struct mallinfo mi = ::mallinfo(); ++ ++ // mallinfo is an old API. Member names mean next to nothing and, beyond that, are int. ++ // So values may have wrapped around. Still useful enough to see how much glibc thinks ++ // we allocated. ++ const size_t total_allocated = (size_t)(unsigned)mi.uordblks; ++ st->print("C-Heap outstanding allocations: " SIZE_FORMAT "K", total_allocated / K); ++ // Since mallinfo members are int, glibc values may have wrapped. Warn about this. ++ if ((vmrss * K) > UINT_MAX && (vmrss * K) > (total_allocated + UINT_MAX)) { ++ st->print(" (may have wrapped)"); ++ } ++ st->cr(); ++ ++#endif // __GLIBC__ ++ ++} ++ + void os::Linux::print_container_info(outputStream* st) { + if (!OSContainer::is_containerized()) { + return; +diff --git a/hotspot/src/os/linux/vm/os_linux.hpp b/hotspot/src/os/linux/vm/os_linux.hpp +index c674882..066b03a 100644 +--- a/hotspot/src/os/linux/vm/os_linux.hpp ++++ b/hotspot/src/os/linux/vm/os_linux.hpp +@@ -120,7 +120,8 @@ class Linux { + static bool release_memory_special_shm(char* base, size_t bytes); + static bool release_memory_special_huge_tlbfs(char* base, size_t bytes); + +- static void print_full_memory_info(outputStream* st); ++ static void print_process_memory_info(outputStream* st); ++ static void print_system_memory_info(outputStream* st); + static void print_container_info(outputStream* st); + static void print_distro_info(outputStream* st); + static void print_libversion_info(outputStream* st); +-- +1.8.3.1 diff --git a/8261167-print_process_memory_info-add-a-close-call-a.patch b/8261167-print_process_memory_info-add-a-close-call-a.patch new file mode 100644 index 0000000000000000000000000000000000000000..80142d9752711d6c4f11b6bbc0c01f0f053a56ec --- /dev/null +++ b/8261167-print_process_memory_info-add-a-close-call-a.patch @@ -0,0 +1,73 @@ +From 959f2dfd0868274f202c313a24784b0be8da3d32 Mon Sep 17 00:00:00 2001 +From: eapen +Date: Mon, 12 Dec 2022 17:00:02 +0800 +Subject: [PATCH 11/33] I68TO2: 8261167: print_process_memory_info add a close call + after fopen +--- + hotspot/src/os/linux/vm/os_linux.cpp | 50 ++++++++++++++++++++---------------- + 1 file changed, 28 insertions(+), 22 deletions(-) + +diff --git a/hotspot/src/os/linux/vm/os_linux.cpp b/hotspot/src/os/linux/vm/os_linux.cpp +index 4c265d5..1a3504f 100644 +--- a/hotspot/src/os/linux/vm/os_linux.cpp ++++ b/hotspot/src/os/linux/vm/os_linux.cpp +@@ -2302,29 +2302,35 @@ void os::Linux::print_process_memory_info(outputStream* st) { + int num_found = 0; + FILE* f = ::fopen("/proc/self/status", "r"); + char buf[256]; +- while (::fgets(buf, sizeof(buf), f) != NULL && num_found < num_values) { +- if ( (vmsize == -1 && sscanf(buf, "VmSize: " SSIZE_FORMAT " kB", &vmsize) == 1) || +- (vmpeak == -1 && sscanf(buf, "VmPeak: " SSIZE_FORMAT " kB", &vmpeak) == 1) || +- (vmswap == -1 && sscanf(buf, "VmSwap: " SSIZE_FORMAT " kB", &vmswap) == 1) || +- (vmhwm == -1 && sscanf(buf, "VmHWM: " SSIZE_FORMAT " kB", &vmhwm) == 1) || +- (vmrss == -1 && sscanf(buf, "VmRSS: " SSIZE_FORMAT " kB", &vmrss) == 1) || +- (rssanon == -1 && sscanf(buf, "RssAnon: " SSIZE_FORMAT " kB", &rssanon) == 1) || +- (rssfile == -1 && sscanf(buf, "RssFile: " SSIZE_FORMAT " kB", &rssfile) == 1) || +- (rssshmem == -1 && sscanf(buf, "RssShmem: " SSIZE_FORMAT " kB", &rssshmem) == 1) +- ) +- { +- num_found ++; ++ if (f != NULL) { ++ while (::fgets(buf, sizeof(buf), f) != NULL && num_found < num_values) { ++ if ( (vmsize == -1 && sscanf(buf, "VmSize: " SSIZE_FORMAT " kB", &vmsize) == 1) || ++ (vmpeak == -1 && sscanf(buf, "VmPeak: " SSIZE_FORMAT " kB", &vmpeak) == 1) || ++ (vmswap == -1 && sscanf(buf, "VmSwap: " SSIZE_FORMAT " kB", &vmswap) == 1) || ++ (vmhwm == -1 && sscanf(buf, "VmHWM: " SSIZE_FORMAT " kB", &vmhwm) == 1) || ++ (vmrss == -1 && sscanf(buf, "VmRSS: " SSIZE_FORMAT " kB", &vmrss) == 1) || ++ (rssanon == -1 && sscanf(buf, "RssAnon: " SSIZE_FORMAT " kB", &rssanon) == 1) || ++ (rssfile == -1 && sscanf(buf, "RssFile: " SSIZE_FORMAT " kB", &rssfile) == 1) || ++ (rssshmem == -1 && sscanf(buf, "RssShmem: " SSIZE_FORMAT " kB", &rssshmem) == 1) ++ ) ++ { ++ num_found ++; ++ } + } +- } +- st->print_cr("Virtual Size: " SSIZE_FORMAT "K (peak: " SSIZE_FORMAT "K)", vmsize, vmpeak); +- st->print("Resident Set Size: " SSIZE_FORMAT "K (peak: " SSIZE_FORMAT "K)", vmrss, vmhwm); +- if (rssanon != -1) { // requires kernel >= 4.5 +- st->print(" (anon: " SSIZE_FORMAT "K, file: " SSIZE_FORMAT "K, shmem: " SSIZE_FORMAT "K)", +- rssanon, rssfile, rssshmem); +- } +- st->cr(); +- if (vmswap != -1) { // requires kernel >= 2.6.34 +- st->print_cr("Swapped out: " SSIZE_FORMAT "K", vmswap); ++ fclose(f); ++ ++ st->print_cr("Virtual Size: " SSIZE_FORMAT "K (peak: " SSIZE_FORMAT "K)", vmsize, vmpeak); ++ st->print("Resident Set Size: " SSIZE_FORMAT "K (peak: " SSIZE_FORMAT "K)", vmrss, vmhwm); ++ if (rssanon != -1) { // requires kernel >= 4.5 ++ st->print(" (anon: " SSIZE_FORMAT "K, file: " SSIZE_FORMAT "K, shmem: " SSIZE_FORMAT "K)", ++ rssanon, rssfile, rssshmem); ++ } ++ st->cr(); ++ if (vmswap != -1) { // requires kernel >= 2.6.34 ++ st->print_cr("Swapped out: " SSIZE_FORMAT "K", vmswap); ++ } ++ } else { ++ st->print_cr("Could not open /proc/self/status to get process memory related information"); + } + + // Print glibc outstanding allocations. +-- +1.8.3.1 diff --git a/8263185-Mallinfo-deprecated-in-glibc-2.33.patch b/8263185-Mallinfo-deprecated-in-glibc-2.33.patch new file mode 100644 index 0000000000000000000000000000000000000000..f80335fddcbf7ae9718879579b98e7c51673bb21 --- /dev/null +++ b/8263185-Mallinfo-deprecated-in-glibc-2.33.patch @@ -0,0 +1,121 @@ +From ccd4293dbec4b1048bf7eb342b8de8241a3667d4 Mon Sep 17 00:00:00 2001 +From: eapen +Date: Mon, 12 Dec 2022 18:44:43 +0800 +Subject: [PATCH 13/33] I68TO2: 8263185: Mallinfo deprecated in glibc 2.33 +--- + hotspot/src/os/linux/vm/os_linux.cpp | 39 ++++++++++++++++++++++++++---------- + hotspot/src/os/linux/vm/os_linux.hpp | 34 +++++++++++++++++++++++++++++++ + 2 files changed, 62 insertions(+), 11 deletions(-) + +diff --git a/hotspot/src/os/linux/vm/os_linux.cpp b/hotspot/src/os/linux/vm/os_linux.cpp +index c687b1c..099dafa 100644 +--- a/hotspot/src/os/linux/vm/os_linux.cpp ++++ b/hotspot/src/os/linux/vm/os_linux.cpp +@@ -152,6 +152,11 @@ const char * os::Linux::_glibc_version = NULL; + const char * os::Linux::_libpthread_version = NULL; + pthread_condattr_t os::Linux::_condattr[1]; + ++#ifdef __GLIBC__ ++os::Linux::mallinfo_func_t os::Linux::_mallinfo = NULL; ++os::Linux::mallinfo2_func_t os::Linux::_mallinfo2 = NULL; ++#endif // __GLIBC__ ++ + static jlong initial_time_count=0; + + static int clock_tics_per_sec = 100; +@@ -2343,18 +2348,25 @@ void os::Linux::print_process_memory_info(outputStream* st) { + // Print glibc outstanding allocations. + // (note: there is no implementation of mallinfo for muslc) + #ifdef __GLIBC__ +- struct mallinfo mi = ::mallinfo(); +- +- // mallinfo is an old API. Member names mean next to nothing and, beyond that, are int. +- // So values may have wrapped around. Still useful enough to see how much glibc thinks +- // we allocated. +- const size_t total_allocated = (size_t)(unsigned)mi.uordblks; +- st->print("C-Heap outstanding allocations: " SIZE_FORMAT "K", total_allocated / K); +- // Since mallinfo members are int, glibc values may have wrapped. Warn about this. +- if ((info.vmrss * K) > UINT_MAX && (info.vmrss * K) > (total_allocated + UINT_MAX)) { +- st->print(" (may have wrapped)"); ++ size_t total_allocated = 0; ++ bool might_have_wrapped = false; ++ if (_mallinfo2 != NULL) { ++ struct glibc_mallinfo2 mi = _mallinfo2(); ++ total_allocated = mi.uordblks; ++ } else if (_mallinfo != NULL) { ++ // mallinfo is an old API. Member names mean next to nothing and, beyond that, are int. ++ // So values may have wrapped around. Still useful enough to see how much glibc thinks ++ // we allocated. ++ struct glibc_mallinfo mi = _mallinfo(); ++ total_allocated = (size_t)(unsigned)mi.uordblks; ++ // Since mallinfo members are int, glibc values may have wrapped. Warn about this. ++ might_have_wrapped = (info.vmrss * K) > UINT_MAX && (info.vmrss * K) > (total_allocated + UINT_MAX); ++ } ++ if (_mallinfo2 != NULL || _mallinfo != NULL) { ++ st->print_cr("C-Heap outstanding allocations: " SIZE_FORMAT "K%s", ++ total_allocated / K, ++ might_have_wrapped ? " (may have wrapped)" : ""); + } +- st->cr(); + + #endif // __GLIBC__ + +@@ -5174,6 +5186,11 @@ void os::init(void) { + + Linux::initialize_system_info(); + ++#ifdef __GLIBC__ ++ Linux::_mallinfo = CAST_TO_FN_PTR(Linux::mallinfo_func_t, dlsym(RTLD_DEFAULT, "mallinfo")); ++ Linux::_mallinfo2 = CAST_TO_FN_PTR(Linux::mallinfo2_func_t, dlsym(RTLD_DEFAULT, "mallinfo2")); ++#endif // __GLIBC__ ++ + // _main_thread points to the thread that created/loaded the JVM. + Linux::_main_thread = pthread_self(); + +diff --git a/hotspot/src/os/linux/vm/os_linux.hpp b/hotspot/src/os/linux/vm/os_linux.hpp +index 2c4efff..2bb3fd2 100644 +--- a/hotspot/src/os/linux/vm/os_linux.hpp ++++ b/hotspot/src/os/linux/vm/os_linux.hpp +@@ -338,6 +338,40 @@ private: + }; + static NumaAllocationPolicy _current_numa_policy; + ++#ifdef __GLIBC__ ++ struct glibc_mallinfo { ++ int arena; ++ int ordblks; ++ int smblks; ++ int hblks; ++ int hblkhd; ++ int usmblks; ++ int fsmblks; ++ int uordblks; ++ int fordblks; ++ int keepcost; ++ }; ++ ++ struct glibc_mallinfo2 { ++ size_t arena; ++ size_t ordblks; ++ size_t smblks; ++ size_t hblks; ++ size_t hblkhd; ++ size_t usmblks; ++ size_t fsmblks; ++ size_t uordblks; ++ size_t fordblks; ++ size_t keepcost; ++ }; ++ ++ typedef struct glibc_mallinfo (*mallinfo_func_t)(void); ++ typedef struct glibc_mallinfo2 (*mallinfo2_func_t)(void); ++ ++ static mallinfo_func_t _mallinfo; ++ static mallinfo2_func_t _mallinfo2; ++#endif ++ + public: + static int sched_getcpu() { return _sched_getcpu != NULL ? _sched_getcpu() : -1; } + static int numa_node_to_cpus(int node, unsigned long *buffer, int bufferlen) { +-- +1.8.3.1 diff --git a/8268819-SA-Remove-libthread_db-dependency-on-Linux.patch b/8268819-SA-Remove-libthread_db-dependency-on-Linux.patch index 77849a111712c40620012c1e4fca2ec31eafca85..d2d7530d28640879516f4cf7dfdc1ccc288f1855 100644 --- a/8268819-SA-Remove-libthread_db-dependency-on-Linux.patch +++ b/8268819-SA-Remove-libthread_db-dependency-on-Linux.patch @@ -3,11 +3,6 @@ From: d30023828 Date: Wed, 9 Feb 2022 18:32:05 +0800 Subject: [PATCH 3/8] 8268819: SA: Remove libthread_db dependency on Linux -DTS/AR: DTS2022020914784 -Summary:hotspot:SA: Remove libthread_db dependency on Linux -LLT:NA -Patch Type:backport -Bug url:https://bugs.openjdk.java.net/browse/JDK-8268819 --- .../agent/src/os/linux/LinuxDebuggerLocal.c | 3 +- hotspot/agent/src/os/linux/Makefile | 6 +- @@ -319,23 +314,16 @@ index 802e5b0bb..a8e0c2a5c 100644 - #endif /* _PROC_SERVICE_H_ */ diff --git a/hotspot/agent/src/os/linux/ps_core.c b/hotspot/agent/src/os/linux/ps_core.c -index b7fe4c095..6da43f195 100644 +index 6fb8c940..5728bcc4 100644 --- a/hotspot/agent/src/os/linux/ps_core.c +++ b/hotspot/agent/src/os/linux/ps_core.c -@@ -1,5 +1,5 @@ - /* -- * Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved. -+ * Copyright (c) 2003, 2019, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it @@ -31,6 +31,7 @@ #include #include #include "libproc_impl.h" +#include "proc_service.h" #include "salibelf.h" - + // This file has the libproc implementation to read core files. @@ -546,8 +547,7 @@ static bool core_handle_prstatus(struct ps_prochandle* ph, const char* buf, size prstatus_t* prstat = (prstatus_t*) buf; @@ -343,9 +331,9 @@ index b7fe4c095..6da43f195 100644 print_debug("got integer regset for lwp %d\n", prstat->pr_pid); - // we set pthread_t to -1 for core dump - if((newthr = add_thread_info(ph, (pthread_t) -1, prstat->pr_pid)) == NULL) -+ if((newthr = add_thread_info(ph, prstat->pr_pid)) == NULL) ++ if((newthr = add_thread_info(ph, prstat->pr_pid)) == NULL) return false; - + // copy regs diff --git a/hotspot/agent/src/os/linux/ps_proc.c b/hotspot/agent/src/os/linux/ps_proc.c index c4d6a9ecc..748cc1397 100644 diff --git a/8268893-jcmd-to-trim-the-glibc-heap.patch b/8268893-jcmd-to-trim-the-glibc-heap.patch new file mode 100644 index 0000000000000000000000000000000000000000..8c9f14be45cf3a95ff477adca431a6f990480b4c --- /dev/null +++ b/8268893-jcmd-to-trim-the-glibc-heap.patch @@ -0,0 +1,678 @@ +From 1b97a08d822b7e2388ded07c696fffe70b39697a Mon Sep 17 00:00:00 2001 +From: eapen +Date: Mon, 12 Dec 2022 17:40:09 +0800 +Subject: [PATCH 12/33] I68TO2: 8268893: jcmd to trim the glibc heap +--- + hotspot/src/os/linux/vm/os_linux.cpp | 57 ++++--- + hotspot/src/os/linux/vm/os_linux.hpp | 17 ++ + hotspot/src/os/linux/vm/trimCHeapDCmd.cpp | 77 +++++++++ + hotspot/src/os/linux/vm/trimCHeapDCmd.hpp | 52 ++++++ + .../src/share/vm/services/diagnosticCommand.cpp | 7 + + .../test/serviceability/dcmd/TrimLibcHeapTest.java | 53 ++++++ + .../oracle/java/testlibrary/CommandExecutor.java | 73 ++++++++ + .../java/testlibrary/CommandExecutorException.java | 36 ++++ + .../com/oracle/java/testlibrary/JMXExecutor.java | 185 +++++++++++++++++++++ + 9 files changed, 532 insertions(+), 25 deletions(-) + create mode 100644 hotspot/src/os/linux/vm/trimCHeapDCmd.cpp + create mode 100644 hotspot/src/os/linux/vm/trimCHeapDCmd.hpp + create mode 100644 hotspot/test/serviceability/dcmd/TrimLibcHeapTest.java + create mode 100644 hotspot/test/testlibrary/com/oracle/java/testlibrary/CommandExecutor.java + create mode 100644 hotspot/test/testlibrary/com/oracle/java/testlibrary/CommandExecutorException.java + create mode 100644 hotspot/test/testlibrary/com/oracle/java/testlibrary/JMXExecutor.java + +diff --git a/hotspot/src/os/linux/vm/os_linux.cpp b/hotspot/src/os/linux/vm/os_linux.cpp +index 1a3504f..c687b1c 100644 +--- a/hotspot/src/os/linux/vm/os_linux.cpp ++++ b/hotspot/src/os/linux/vm/os_linux.cpp +@@ -2290,44 +2290,51 @@ void os::Linux::print_system_memory_info(outputStream* st) { + st->cr(); + } + +-void os::Linux::print_process_memory_info(outputStream* st) { +- +- st->print_cr("Process Memory:"); +- +- // Print virtual and resident set size; peak values; swap; and for +- // rss its components if the kernel is recent enough. +- ssize_t vmsize = -1, vmpeak = -1, vmswap = -1, +- vmrss = -1, vmhwm = -1, rssanon = -1, rssfile = -1, rssshmem = -1; +- const int num_values = 8; +- int num_found = 0; ++bool os::Linux::query_process_memory_info(os::Linux::meminfo_t* info) { + FILE* f = ::fopen("/proc/self/status", "r"); ++ const int num_values = sizeof(os::Linux::meminfo_t) / sizeof(size_t); ++ int num_found = 0; + char buf[256]; ++ info->vmsize = info->vmpeak = info->vmrss = info->vmhwm = info->vmswap = ++ info->rssanon = info->rssfile = info->rssshmem = -1; + if (f != NULL) { + while (::fgets(buf, sizeof(buf), f) != NULL && num_found < num_values) { +- if ( (vmsize == -1 && sscanf(buf, "VmSize: " SSIZE_FORMAT " kB", &vmsize) == 1) || +- (vmpeak == -1 && sscanf(buf, "VmPeak: " SSIZE_FORMAT " kB", &vmpeak) == 1) || +- (vmswap == -1 && sscanf(buf, "VmSwap: " SSIZE_FORMAT " kB", &vmswap) == 1) || +- (vmhwm == -1 && sscanf(buf, "VmHWM: " SSIZE_FORMAT " kB", &vmhwm) == 1) || +- (vmrss == -1 && sscanf(buf, "VmRSS: " SSIZE_FORMAT " kB", &vmrss) == 1) || +- (rssanon == -1 && sscanf(buf, "RssAnon: " SSIZE_FORMAT " kB", &rssanon) == 1) || +- (rssfile == -1 && sscanf(buf, "RssFile: " SSIZE_FORMAT " kB", &rssfile) == 1) || +- (rssshmem == -1 && sscanf(buf, "RssShmem: " SSIZE_FORMAT " kB", &rssshmem) == 1) ++ if ( (info->vmsize == -1 && sscanf(buf, "VmSize: " SSIZE_FORMAT " kB", &info->vmsize) == 1) || ++ (info->vmpeak == -1 && sscanf(buf, "VmPeak: " SSIZE_FORMAT " kB", &info->vmpeak) == 1) || ++ (info->vmswap == -1 && sscanf(buf, "VmSwap: " SSIZE_FORMAT " kB", &info->vmswap) == 1) || ++ (info->vmhwm == -1 && sscanf(buf, "VmHWM: " SSIZE_FORMAT " kB", &info->vmhwm) == 1) || ++ (info->vmrss == -1 && sscanf(buf, "VmRSS: " SSIZE_FORMAT " kB", &info->vmrss) == 1) || ++ (info->rssanon == -1 && sscanf(buf, "RssAnon: " SSIZE_FORMAT " kB", &info->rssanon) == 1) || // Needs Linux 4.5 ++ (info->rssfile == -1 && sscanf(buf, "RssFile: " SSIZE_FORMAT " kB", &info->rssfile) == 1) || // Needs Linux 4.5 ++ (info->rssshmem == -1 && sscanf(buf, "RssShmem: " SSIZE_FORMAT " kB", &info->rssshmem) == 1) // Needs Linux 4.5 + ) + { + num_found ++; + } + } + fclose(f); ++ return true; ++ } ++ return false; ++} ++ ++void os::Linux::print_process_memory_info(outputStream* st) { + +- st->print_cr("Virtual Size: " SSIZE_FORMAT "K (peak: " SSIZE_FORMAT "K)", vmsize, vmpeak); +- st->print("Resident Set Size: " SSIZE_FORMAT "K (peak: " SSIZE_FORMAT "K)", vmrss, vmhwm); +- if (rssanon != -1) { // requires kernel >= 4.5 ++ st->print_cr("Process Memory:"); ++ ++ // Print virtual and resident set size; peak values; swap; and for ++ // rss its components if the kernel is recent enough. ++ meminfo_t info; ++ if (query_process_memory_info(&info)) { ++ st->print_cr("Virtual Size: " SSIZE_FORMAT "K (peak: " SSIZE_FORMAT "K)", info.vmsize, info.vmpeak); ++ st->print("Resident Set Size: " SSIZE_FORMAT "K (peak: " SSIZE_FORMAT "K)", info.vmrss, info.vmhwm); ++ if (info.rssanon != -1) { // requires kernel >= 4.5 + st->print(" (anon: " SSIZE_FORMAT "K, file: " SSIZE_FORMAT "K, shmem: " SSIZE_FORMAT "K)", +- rssanon, rssfile, rssshmem); ++ info.rssanon, info.rssfile, info.rssshmem); + } + st->cr(); +- if (vmswap != -1) { // requires kernel >= 2.6.34 +- st->print_cr("Swapped out: " SSIZE_FORMAT "K", vmswap); ++ if (info.vmswap != -1) { // requires kernel >= 2.6.34 ++ st->print_cr("Swapped out: " SSIZE_FORMAT "K", info.vmswap); + } + } else { + st->print_cr("Could not open /proc/self/status to get process memory related information"); +@@ -2344,7 +2351,7 @@ void os::Linux::print_process_memory_info(outputStream* st) { + const size_t total_allocated = (size_t)(unsigned)mi.uordblks; + st->print("C-Heap outstanding allocations: " SIZE_FORMAT "K", total_allocated / K); + // Since mallinfo members are int, glibc values may have wrapped. Warn about this. +- if ((vmrss * K) > UINT_MAX && (vmrss * K) > (total_allocated + UINT_MAX)) { ++ if ((info.vmrss * K) > UINT_MAX && (info.vmrss * K) > (total_allocated + UINT_MAX)) { + st->print(" (may have wrapped)"); + } + st->cr(); +diff --git a/hotspot/src/os/linux/vm/os_linux.hpp b/hotspot/src/os/linux/vm/os_linux.hpp +index 066b03a..2c4efff 100644 +--- a/hotspot/src/os/linux/vm/os_linux.hpp ++++ b/hotspot/src/os/linux/vm/os_linux.hpp +@@ -243,6 +243,23 @@ class Linux { + public: + static pthread_condattr_t* condAttr() { return _condattr; } + ++ // Output structure for query_process_memory_info() ++ struct meminfo_t { ++ ssize_t vmsize; // current virtual size ++ ssize_t vmpeak; // peak virtual size ++ ssize_t vmrss; // current resident set size ++ ssize_t vmhwm; // peak resident set size ++ ssize_t vmswap; // swapped out ++ ssize_t rssanon; // resident set size (anonymous mappings, needs 4.5) ++ ssize_t rssfile; // resident set size (file mappings, needs 4.5) ++ ssize_t rssshmem; // resident set size (shared mappings, needs 4.5) ++ }; ++ ++ // Attempts to query memory information about the current process and return it in the output structure. ++ // May fail (returns false) or succeed (returns true) but not all output fields are available; unavailable ++ // fields will contain -1. ++ static bool query_process_memory_info(meminfo_t* info); ++ + // Stack repair handling + + // none present +diff --git a/hotspot/src/os/linux/vm/trimCHeapDCmd.cpp b/hotspot/src/os/linux/vm/trimCHeapDCmd.cpp +new file mode 100644 +index 0000000..95d03d9 +--- /dev/null ++++ b/hotspot/src/os/linux/vm/trimCHeapDCmd.cpp +@@ -0,0 +1,77 @@ ++/* ++ * Copyright (c) 2021 SAP SE. All rights reserved. ++ * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ * ++ */ ++ ++#include "precompiled.hpp" ++#include "runtime/os.hpp" ++#include "utilities/debug.hpp" ++#include "utilities/ostream.hpp" ++#include "trimCHeapDCmd.hpp" ++ ++#include ++ ++void TrimCLibcHeapDCmd::execute(DCmdSource source, TRAPS) { ++#ifdef __GLIBC__ ++ stringStream ss_report(1024); // Note: before calling trim ++ ++ os::Linux::meminfo_t info1; ++ os::Linux::meminfo_t info2; ++ // Query memory before... ++ bool have_info1 = os::Linux::query_process_memory_info(&info1); ++ ++ _output->print_cr("Attempting trim..."); ++ ::malloc_trim(0); ++ _output->print_cr("Done."); ++ ++ // ...and after trim. ++ bool have_info2 = os::Linux::query_process_memory_info(&info2); ++ ++ // Print report both to output stream as well to UL ++ bool wrote_something = false; ++ if (have_info1 && have_info2) { ++ if (info1.vmsize != -1 && info2.vmsize != -1) { ++ ss_report.print_cr("Virtual size before: " SSIZE_FORMAT "k, after: " SSIZE_FORMAT "k, (" SSIZE_FORMAT "k)", ++ info1.vmsize, info2.vmsize, (info2.vmsize - info1.vmsize)); ++ wrote_something = true; ++ } ++ if (info1.vmrss != -1 && info2.vmrss != -1) { ++ ss_report.print_cr("RSS before: " SSIZE_FORMAT "k, after: " SSIZE_FORMAT "k, (" SSIZE_FORMAT "k)", ++ info1.vmrss, info2.vmrss, (info2.vmrss - info1.vmrss)); ++ wrote_something = true; ++ } ++ if (info1.vmswap != -1 && info2.vmswap != -1) { ++ ss_report.print_cr("Swap before: " SSIZE_FORMAT "k, after: " SSIZE_FORMAT "k, (" SSIZE_FORMAT "k)", ++ info1.vmswap, info2.vmswap, (info2.vmswap - info1.vmswap)); ++ wrote_something = true; ++ } ++ } ++ if (!wrote_something) { ++ ss_report.print_raw("No details available."); ++ } ++ ++ _output->print_raw(ss_report.base()); ++#else ++ _output->print_cr("Not available."); ++#endif ++} +diff --git a/hotspot/src/os/linux/vm/trimCHeapDCmd.hpp b/hotspot/src/os/linux/vm/trimCHeapDCmd.hpp +new file mode 100644 +index 0000000..4c5b5cc +--- /dev/null ++++ b/hotspot/src/os/linux/vm/trimCHeapDCmd.hpp +@@ -0,0 +1,52 @@ ++/* ++ * Copyright (c) 2021 SAP SE. All rights reserved. ++ * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ * ++ */ ++ ++#ifndef OS_LINUX_TRIMCHEAPDCMD_HPP ++#define OS_LINUX_TRIMCHEAPDCMD_HPP ++ ++#include "services/diagnosticCommand.hpp" ++ ++class outputStream; ++ ++class TrimCLibcHeapDCmd : public DCmd { ++public: ++ TrimCLibcHeapDCmd(outputStream* output, bool heap) : DCmd(output, heap) {} ++ static const char* name() { ++ return "System.trim_native_heap"; ++ } ++ static const char* description() { ++ return "Attempts to free up memory by trimming the C-heap."; ++ } ++ static const char* impact() { ++ return "Low"; ++ } ++ static const JavaPermission permission() { ++ JavaPermission p = { "java.lang.management.ManagementPermission", "control", NULL }; ++ return p; ++ } ++ virtual void execute(DCmdSource source, TRAPS); ++}; ++ ++#endif // OS_LINUX_TRIMCHEAPDCMD_HPP +diff --git a/hotspot/src/share/vm/services/diagnosticCommand.cpp b/hotspot/src/share/vm/services/diagnosticCommand.cpp +index 358ec6e..60417b5 100644 +--- a/hotspot/src/share/vm/services/diagnosticCommand.cpp ++++ b/hotspot/src/share/vm/services/diagnosticCommand.cpp +@@ -36,6 +36,10 @@ + #include "utilities/macros.hpp" + #include "oops/objArrayOop.hpp" + ++#ifdef LINUX ++#include "trimCHeapDCmd.hpp" ++#endif ++ + PRAGMA_FORMAT_MUTE_WARNINGS_FOR_GCC + + void DCmdRegistrant::register_dcmds(){ +@@ -65,6 +69,9 @@ void DCmdRegistrant::register_dcmds(){ + DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl(full_export, true, false)); + DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl(full_export, true, false)); + DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl(full_export, true, false)); ++#ifdef LINUX ++ DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl(full_export, true, false)); ++#endif // LINUX + + // Enhanced JMX Agent Support + // These commands won't be exported via the DiagnosticCommandMBean until an +diff --git a/hotspot/test/serviceability/dcmd/TrimLibcHeapTest.java b/hotspot/test/serviceability/dcmd/TrimLibcHeapTest.java +new file mode 100644 +index 0000000..0fe8e35 +--- /dev/null ++++ b/hotspot/test/serviceability/dcmd/TrimLibcHeapTest.java +@@ -0,0 +1,53 @@ ++/* ++ * Copyright (c) 2021 SAP SE. All rights reserved. ++ * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ */ ++ ++import org.testng.annotations.Test; ++import com.oracle.java.testlibrary.*; ++ ++/* ++ * @test ++ * @summary Test of diagnostic command VM.trim_libc_heap ++ * @library /testlibrary ++ * @requires os.family == "linux" ++ * @modules java.base/jdk.internal.misc ++ * java.compiler ++ * java.management ++ * jdk.internal.jvmstat/sun.jvmstat.monitor ++ * @run testng TrimLibcHeapTest ++ */ ++public class TrimLibcHeapTest { ++ public void run(CommandExecutor executor) { ++ OutputAnalyzer output = executor.execute("System.trim_native_heap"); ++ output.reportDiagnosticSummary(); ++ output.shouldMatch("(Done|Not available)"); // Not available could happen on Linux + non-glibc (eg. muslc) ++ if (output.firstMatch("Done") != null) { ++ output.shouldMatch("(Virtual size before|RSS before|Swap before|No details available)"); ++ } ++ } ++ ++ @Test ++ public void jmx() { ++ run(new JMXExecutor()); ++ } ++} +diff --git a/hotspot/test/testlibrary/com/oracle/java/testlibrary/CommandExecutor.java b/hotspot/test/testlibrary/com/oracle/java/testlibrary/CommandExecutor.java +new file mode 100644 +index 0000000..e95a437 +--- /dev/null ++++ b/hotspot/test/testlibrary/com/oracle/java/testlibrary/CommandExecutor.java +@@ -0,0 +1,73 @@ ++/* ++ * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ */ ++ ++package com.oracle.java.testlibrary; ++ ++/** ++ * Abstract base class for Diagnostic Command executors ++ */ ++public abstract class CommandExecutor { ++ ++ /** ++ * Execute a diagnostic command ++ * ++ * @param cmd The diagnostic command to execute ++ * @return an {@link jdk.testlibrary.OutputAnalyzer} encapsulating the output of the command ++ * @throws CommandExecutorException if there is an exception on the "calling side" while trying to execute the ++ * Diagnostic Command. Exceptions thrown on the remote side are available as textual representations in ++ * stderr, regardless of the specific executor used. ++ */ ++ public final OutputAnalyzer execute(String cmd) throws CommandExecutorException { ++ return execute(cmd, false); ++ } ++ ++ /** ++ * Execute a diagnostic command ++ * ++ * @param cmd The diagnostic command to execute ++ * @param silent Do not print the command output ++ * @return an {@link jdk.testlibrary.OutputAnalyzer} encapsulating the output of the command ++ * @throws CommandExecutorException if there is an exception on the "calling side" while trying to execute the ++ * Diagnostic Command. Exceptions thrown on the remote side are available as textual representations in ++ * stderr, regardless of the specific executor used. ++ */ ++ public final OutputAnalyzer execute(String cmd, boolean silent) throws CommandExecutorException { ++ if (!silent) { ++ System.out.printf("Running DCMD '%s' through '%s'%n", cmd, this.getClass().getSimpleName()); ++ } ++ ++ OutputAnalyzer oa = executeImpl(cmd); ++ ++ if (!silent) { ++ System.out.println("---------------- stdout ----------------"); ++ System.out.println(oa.getStdout()); ++ System.out.println("---------------- stderr ----------------"); ++ System.out.println(oa.getStderr()); ++ System.out.println("----------------------------------------"); ++ System.out.println(); ++ } ++ return oa; ++ } ++ ++ protected abstract OutputAnalyzer executeImpl(String cmd) throws CommandExecutorException; ++} +diff --git a/hotspot/test/testlibrary/com/oracle/java/testlibrary/CommandExecutorException.java b/hotspot/test/testlibrary/com/oracle/java/testlibrary/CommandExecutorException.java +new file mode 100644 +index 0000000..1857a23 +--- /dev/null ++++ b/hotspot/test/testlibrary/com/oracle/java/testlibrary/CommandExecutorException.java +@@ -0,0 +1,36 @@ ++/* ++ * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ */ ++ ++package com.oracle.java.testlibrary; ++ ++/** ++ * CommandExecutorException encapsulates exceptions thrown (on the "calling side") from the execution of Diagnostic ++ * Commands ++ */ ++public class CommandExecutorException extends RuntimeException { ++ private static final long serialVersionUID = -7039597746579144280L; ++ ++ public CommandExecutorException(String message, Throwable e) { ++ super(message, e); ++ } ++} +diff --git a/hotspot/test/testlibrary/com/oracle/java/testlibrary/JMXExecutor.java b/hotspot/test/testlibrary/com/oracle/java/testlibrary/JMXExecutor.java +new file mode 100644 +index 0000000..317fc5c +--- /dev/null ++++ b/hotspot/test/testlibrary/com/oracle/java/testlibrary/JMXExecutor.java +@@ -0,0 +1,185 @@ ++/* ++ * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ */ ++ ++package com.oracle.java.testlibrary; ++ ++import javax.management.*; ++import javax.management.remote.JMXConnector; ++import javax.management.remote.JMXConnectorFactory; ++import javax.management.remote.JMXServiceURL; ++ ++import java.io.IOException; ++import java.io.PrintWriter; ++import java.io.StringWriter; ++ ++import java.lang.management.ManagementFactory; ++ ++import java.util.HashMap; ++ ++/** ++ * Executes Diagnostic Commands on the target VM (specified by a host/port combination or a full JMX Service URL) using ++ * the JMX interface. If the target is not the current VM, the JMX Remote interface must be enabled beforehand. ++ */ ++public class JMXExecutor extends CommandExecutor { ++ ++ private final MBeanServerConnection mbs; ++ ++ /** ++ * Instantiates a new JMXExecutor targeting the current VM ++ */ ++ public JMXExecutor() { ++ super(); ++ mbs = ManagementFactory.getPlatformMBeanServer(); ++ } ++ ++ /** ++ * Instantiates a new JMXExecutor targeting the VM indicated by the given host/port combination or a full JMX ++ * Service URL ++ * ++ * @param target a host/port combination on the format "host:port" or a full JMX Service URL of the target VM ++ */ ++ public JMXExecutor(String target) { ++ String urlStr; ++ ++ if (target.matches("^\\w[\\w\\-]*(\\.[\\w\\-]+)*:\\d+$")) { ++ /* Matches "hostname:port" */ ++ urlStr = String.format("service:jmx:rmi:///jndi/rmi://%s/jmxrmi", target); ++ } else if (target.startsWith("service:")) { ++ urlStr = target; ++ } else { ++ throw new IllegalArgumentException("Could not recognize target string: " + target); ++ } ++ ++ try { ++ JMXServiceURL url = new JMXServiceURL(urlStr); ++ JMXConnector c = JMXConnectorFactory.connect(url, new HashMap<>()); ++ mbs = c.getMBeanServerConnection(); ++ } catch (IOException e) { ++ throw new CommandExecutorException("Could not initiate connection to target: " + target, e); ++ } ++ } ++ ++ protected OutputAnalyzer executeImpl(String cmd) throws CommandExecutorException { ++ String stdout = ""; ++ String stderr = ""; ++ ++ String[] cmdParts = cmd.split(" ", 2); ++ String operation = commandToMethodName(cmdParts[0]); ++ Object[] dcmdArgs = produceArguments(cmdParts); ++ String[] signature = {String[].class.getName()}; ++ ++ ObjectName beanName = getMBeanName(); ++ ++ try { ++ stdout = (String) mbs.invoke(beanName, operation, dcmdArgs, signature); ++ } ++ ++ /* Failures on the "local" side, the one invoking the command. */ ++ catch (ReflectionException e) { ++ Throwable cause = e.getCause(); ++ if (cause instanceof NoSuchMethodException) { ++ /* We want JMXExecutor to match the behavior of the other CommandExecutors */ ++ String message = "Unknown diagnostic command: " + operation; ++ stderr = exceptionTraceAsString(new IllegalArgumentException(message, e)); ++ } else { ++ rethrowExecutorException(operation, dcmdArgs, e); ++ } ++ } ++ ++ /* Failures on the "local" side, the one invoking the command. */ ++ catch (InstanceNotFoundException | IOException e) { ++ rethrowExecutorException(operation, dcmdArgs, e); ++ } ++ ++ /* Failures on the remote side, the one executing the invoked command. */ ++ catch (MBeanException e) { ++ stdout = exceptionTraceAsString(e); ++ } ++ ++ return new OutputAnalyzer(stdout, stderr); ++ } ++ ++ private void rethrowExecutorException(String operation, Object[] dcmdArgs, ++ Exception e) throws CommandExecutorException { ++ String message = String.format("Could not invoke: %s %s", operation, ++ String.join(" ", (String[]) dcmdArgs[0])); ++ throw new CommandExecutorException(message, e); ++ } ++ ++ private ObjectName getMBeanName() throws CommandExecutorException { ++ String MBeanName = "com.sun.management:type=DiagnosticCommand"; ++ ++ try { ++ return new ObjectName(MBeanName); ++ } catch (MalformedObjectNameException e) { ++ String message = "MBean not found: " + MBeanName; ++ throw new CommandExecutorException(message, e); ++ } ++ } ++ ++ private Object[] produceArguments(String[] cmdParts) { ++ Object[] dcmdArgs = {new String[0]}; /* Default: No arguments */ ++ ++ if (cmdParts.length == 2) { ++ dcmdArgs[0] = cmdParts[1].split(" "); ++ } ++ return dcmdArgs; ++ } ++ ++ /** ++ * Convert from diagnostic command to MBean method name ++ * ++ * Examples: ++ * help --> help ++ * VM.version --> vmVersion ++ * VM.command_line --> vmCommandLine ++ */ ++ private static String commandToMethodName(String cmd) { ++ String operation = ""; ++ boolean up = false; /* First letter is to be lower case */ ++ ++ /* ++ * If a '.' or '_' is encountered it is not copied, ++ * instead the next character will be converted to upper case ++ */ ++ for (char c : cmd.toCharArray()) { ++ if (('.' == c) || ('_' == c)) { ++ up = true; ++ } else if (up) { ++ operation = operation.concat(Character.toString(c).toUpperCase()); ++ up = false; ++ } else { ++ operation = operation.concat(Character.toString(c).toLowerCase()); ++ } ++ } ++ ++ return operation; ++ } ++ ++ private static String exceptionTraceAsString(Throwable cause) { ++ StringWriter sw = new StringWriter(); ++ cause.printStackTrace(new PrintWriter(sw)); ++ return sw.toString(); ++ } ++ ++} +-- +1.8.3.1 diff --git a/8275775-Add-jcmd-VM.classes-to-print-details-of-all-.patch b/8275775-Add-jcmd-VM.classes-to-print-details-of-all-.patch new file mode 100644 index 0000000000000000000000000000000000000000..3d089b3d6cd2310eabd1fcb0405fe18d0f87f24c --- /dev/null +++ b/8275775-Add-jcmd-VM.classes-to-print-details-of-all-.patch @@ -0,0 +1,430 @@ +From c427ef7ceeea1fb8f8ebd035e59b6f06b5ec34c1 Mon Sep 17 00:00:00 2001 +From: eapen +Date: Tue, 13 Dec 2022 21:06:41 +0800 +Subject: [PATCH 15/33] I68TO2: 8275775: Add jcmd VM.classes to print details of all + classes +--- + hotspot/src/share/vm/oops/instanceKlass.cpp | 56 ++++++++++++++++++++-- + hotspot/src/share/vm/oops/instanceKlass.hpp | 17 ++++--- + hotspot/src/share/vm/runtime/fieldDescriptor.cpp | 4 +- + hotspot/src/share/vm/runtime/fieldDescriptor.hpp | 4 +- + hotspot/src/share/vm/runtime/globals.hpp | 2 +- + hotspot/src/share/vm/runtime/vm_operations.hpp | 1 + + .../src/share/vm/services/diagnosticCommand.cpp | 53 ++++++++++++++++++++ + .../src/share/vm/services/diagnosticCommand.hpp | 23 +++++++++ + hotspot/test/runtime/CommandLine/PrintClasses.java | 51 ++++++++++++++++++++ + 9 files changed, 195 insertions(+), 16 deletions(-) + create mode 100644 hotspot/test/runtime/CommandLine/PrintClasses.java + +diff --git a/hotspot/src/share/vm/oops/instanceKlass.cpp b/hotspot/src/share/vm/oops/instanceKlass.cpp +index 2a9cd92..538645b 100644 +--- a/hotspot/src/share/vm/oops/instanceKlass.cpp ++++ b/hotspot/src/share/vm/oops/instanceKlass.cpp +@@ -1799,6 +1799,52 @@ Method* InstanceKlass::lookup_method_in_all_interfaces(Symbol* name, + return NULL; + } + ++PrintClassClosure::PrintClassClosure(outputStream* st, bool verbose) ++ :_st(st), _verbose(verbose) { ++ ResourceMark rm; ++ _st->print("%-18s ", "KlassAddr"); ++ _st->print("%-4s ", "Size"); ++ _st->print("%-20s ", "State"); ++ _st->print("%-7s ", "Flags"); ++ _st->print("%-5s ", "ClassName"); ++ _st->cr(); ++} ++ ++void PrintClassClosure::do_klass(Klass* k) { ++ ResourceMark rm; ++ // klass pointer ++ _st->print(INTPTR_FORMAT " ", p2i(k)); ++ // klass size ++ _st->print("%4d ", k->size()); ++ // initialization state ++ if (k->oop_is_instance()) { ++ _st->print("%-20s ",InstanceKlass::cast(k)->init_state_name()); ++ } else { ++ _st->print("%-20s ",""); ++ } ++ // misc flags(Changes should synced with ClassesDCmd::ClassesDCmd help doc) ++ char buf[10]; ++ int i = 0; ++ if (k->has_finalizer()) buf[i++] = 'F'; ++ if (k->has_final_method()) buf[i++] = 'f'; ++ if (k->oop_is_instance()) { ++ InstanceKlass* ik = InstanceKlass::cast(k); ++ if (ik->is_rewritten()) buf[i++] = 'W'; ++ if (ik->is_contended()) buf[i++] = 'C'; ++ if (ik->has_been_redefined()) buf[i++] = 'R'; ++ if (ik->is_shared()) buf[i++] = 'S'; ++ } ++ buf[i++] = '\0'; ++ _st->print("%-7s ", buf); ++ // klass name ++ _st->print("%-5s ", k->external_name()); ++ // end ++ _st->cr(); ++ if (_verbose) { ++ k->print_on(_st); ++ } ++} ++ + /* jni_id_for_impl for jfieldIds only */ + JNIid* InstanceKlass::jni_id_for_impl(instanceKlassHandle this_oop, int offset) { + MutexLocker ml(JfieldIdCreation_lock); +@@ -3244,7 +3290,6 @@ oop InstanceKlass::add_member_name(Handle mem_name, bool intern) { + // ----------------------------------------------------------------------------------------------------- + // Printing + +-#ifndef PRODUCT + + #define BULLET " - " + +@@ -3264,6 +3309,10 @@ static void print_vtable(intptr_t* start, int len, outputStream* st) { + } + } + ++const char* InstanceKlass::init_state_name() const { ++ return state_names[_init_state]; ++} ++ + void InstanceKlass::print_on(outputStream* st) const { + assert(is_klass(), "must be klass"); + Klass::print_on(st); +@@ -3271,7 +3320,7 @@ void InstanceKlass::print_on(outputStream* st) const { + st->print(BULLET"instance size: %d", size_helper()); st->cr(); + st->print(BULLET"klass size: %d", size()); st->cr(); + st->print(BULLET"access: "); access_flags().print_on(st); st->cr(); +- st->print(BULLET"state: "); st->print_cr("%s", state_names[_init_state]); ++ st->print(BULLET"state: "); st->print_cr("%s", init_state_name()); + st->print(BULLET"name: "); name()->print_value_on(st); st->cr(); + st->print(BULLET"super: "); super()->print_value_on_maybe_null(st); st->cr(); + st->print(BULLET"sub: "); +@@ -3380,7 +3429,6 @@ void InstanceKlass::print_on(outputStream* st) const { + st->cr(); + } + +-#endif //PRODUCT + + void InstanceKlass::print_value_on(outputStream* st) const { + assert(is_klass(), "must be klass"); +@@ -3388,7 +3436,6 @@ void InstanceKlass::print_value_on(outputStream* st) const { + name()->print_value_on(st); + } + +-#ifndef PRODUCT + + void FieldPrinter::do_field(fieldDescriptor* fd) { + _st->print(BULLET); +@@ -3449,7 +3496,6 @@ void InstanceKlass::oop_print_on(oop obj, outputStream* st) { + } + } + +-#endif //PRODUCT + + void InstanceKlass::oop_print_value_on(oop obj, outputStream* st) { + st->print("a "); +diff --git a/hotspot/src/share/vm/oops/instanceKlass.hpp b/hotspot/src/share/vm/oops/instanceKlass.hpp +index 43919e8..6e36fa4 100644 +--- a/hotspot/src/share/vm/oops/instanceKlass.hpp ++++ b/hotspot/src/share/vm/oops/instanceKlass.hpp +@@ -99,7 +99,6 @@ public: + virtual void do_field(fieldDescriptor* fd) = 0; + }; + +-#ifndef PRODUCT + // Print fields. + // If "obj" argument to constructor is NULL, prints static fields, otherwise prints non-static fields. + class FieldPrinter: public FieldClosure { +@@ -109,7 +108,6 @@ class FieldPrinter: public FieldClosure { + FieldPrinter(outputStream* st, oop obj = NULL) : _obj(obj), _st(st) {} + void do_field(fieldDescriptor* fd); + }; +-#endif // !PRODUCT + + // ValueObjs embedded in klass. Describes where oops are located in instances of + // this klass. +@@ -462,6 +460,7 @@ class InstanceKlass: public Klass { + bool is_in_error_state() const { return _init_state == initialization_error; } + bool is_reentrant_initialization(Thread *thread) { return thread == _init_thread; } + ClassState init_state() { return (ClassState)_init_state; } ++ const char* init_state_name() const; + bool is_rewritten() const { return (_misc_flags & _misc_rewritten) != 0; } + + // defineClass specified verification +@@ -1174,16 +1173,13 @@ public: + + public: + // Printing +-#ifndef PRODUCT + void print_on(outputStream* st) const; +-#endif + void print_value_on(outputStream* st) const; + + void oop_print_value_on(oop obj, outputStream* st); + +-#ifndef PRODUCT + void oop_print_on (oop obj, outputStream* st); +- ++#ifndef PRODUCT + void print_dependent_nmethods(bool verbose = false); + bool is_dependent_nmethod(nmethod* nm); + #endif +@@ -1217,6 +1213,15 @@ inline u2 InstanceKlass::next_method_idnum() { + } + } + ++class PrintClassClosure : public KlassClosure { ++private: ++ outputStream* _st; ++ bool _verbose; ++public: ++ PrintClassClosure(outputStream* st, bool verbose); ++ ++ void do_klass(Klass* k); ++}; + + /* JNIid class for jfieldIDs only */ + class JNIid: public CHeapObj { +diff --git a/hotspot/src/share/vm/runtime/fieldDescriptor.cpp b/hotspot/src/share/vm/runtime/fieldDescriptor.cpp +index 610402d..288e82d 100644 +--- a/hotspot/src/share/vm/runtime/fieldDescriptor.cpp ++++ b/hotspot/src/share/vm/runtime/fieldDescriptor.cpp +@@ -123,6 +123,8 @@ void fieldDescriptor::verify() const { + } + } + ++#endif /* PRODUCT */ ++ + void fieldDescriptor::print_on(outputStream* st) const { + access_flags().print_on(st); + name()->print_value_on(st); +@@ -206,5 +208,3 @@ void fieldDescriptor::print_on_for(outputStream* st, oop obj) { + st->print(" (%x)", as_int); + } + } +- +-#endif /* PRODUCT */ +diff --git a/hotspot/src/share/vm/runtime/fieldDescriptor.hpp b/hotspot/src/share/vm/runtime/fieldDescriptor.hpp +index 1810a16..f7e9a26 100644 +--- a/hotspot/src/share/vm/runtime/fieldDescriptor.hpp ++++ b/hotspot/src/share/vm/runtime/fieldDescriptor.hpp +@@ -129,8 +129,8 @@ class fieldDescriptor VALUE_OBJ_CLASS_SPEC { + + // Print + void print() { print_on(tty); } +- void print_on(outputStream* st) const PRODUCT_RETURN; +- void print_on_for(outputStream* st, oop obj) PRODUCT_RETURN; ++ void print_on(outputStream* st) const; ++ void print_on_for(outputStream* st, oop obj); + void verify() const PRODUCT_RETURN; + }; + +diff --git a/hotspot/src/share/vm/runtime/globals.hpp b/hotspot/src/share/vm/runtime/globals.hpp +index ec48c48..41b1392 100644 +--- a/hotspot/src/share/vm/runtime/globals.hpp ++++ b/hotspot/src/share/vm/runtime/globals.hpp +@@ -3097,7 +3097,7 @@ class CommandLineFlags { + notproduct(intx, MaxElementPrintSize, 256, \ + "maximum number of elements to print") \ + \ +- notproduct(intx, MaxSubklassPrintSize, 4, \ ++ product(intx, MaxSubklassPrintSize, 4, \ + "maximum number of subklasses to print when printing klass") \ + \ + product(intx, MaxInlineLevel, 9, \ +diff --git a/hotspot/src/share/vm/runtime/vm_operations.hpp b/hotspot/src/share/vm/runtime/vm_operations.hpp +index 8c6795a..a8ba78b 100644 +--- a/hotspot/src/share/vm/runtime/vm_operations.hpp ++++ b/hotspot/src/share/vm/runtime/vm_operations.hpp +@@ -99,6 +99,7 @@ + template(WhiteBoxOperation) \ + template(ClassLoaderStatsOperation) \ + template(JFROldObject) \ ++ template(PrintClasses) \ + + class VM_Operation: public CHeapObj { + public: +diff --git a/hotspot/src/share/vm/services/diagnosticCommand.cpp b/hotspot/src/share/vm/services/diagnosticCommand.cpp +index 60417b5..e4e6185 100644 +--- a/hotspot/src/share/vm/services/diagnosticCommand.cpp ++++ b/hotspot/src/share/vm/services/diagnosticCommand.cpp +@@ -64,6 +64,7 @@ void DCmdRegistrant::register_dcmds(){ + DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl(DCmd_Source_Internal | DCmd_Source_AttachAPI, true, false)); + DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl(DCmd_Source_Internal | DCmd_Source_AttachAPI, true, false)); + DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl(full_export, true, false)); ++ DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl(full_export, true, false)); + DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl(full_export, true, false)); + #endif // INCLUDE_SERVICES + DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl(full_export, true, false)); +@@ -98,9 +99,14 @@ HelpDCmd::HelpDCmd(outputStream* output, bool heap) : DCmdWithParser(output, hea + _dcmdparser.add_dcmd_argument(&_cmd); + }; + ++static int compare_strings(const char** s1, const char** s2) { ++ return ::strcmp(*s1, *s2); ++} ++ + void HelpDCmd::execute(DCmdSource source, TRAPS) { + if (_all.value()) { + GrowableArray* cmd_list = DCmdFactory::DCmd_list(source); ++ cmd_list->sort(compare_strings); + for (int i = 0; i < cmd_list->length(); i++) { + DCmdFactory* factory = DCmdFactory::factory(source, cmd_list->at(i), + strlen(cmd_list->at(i))); +@@ -141,6 +147,7 @@ void HelpDCmd::execute(DCmdSource source, TRAPS) { + } else { + output()->print_cr("The following commands are available:"); + GrowableArray* cmd_list = DCmdFactory::DCmd_list(source); ++ cmd_list->sort(compare_strings); + for (int i = 0; i < cmd_list->length(); i++) { + DCmdFactory* factory = DCmdFactory::factory(source, cmd_list->at(i), + strlen(cmd_list->at(i))); +@@ -419,6 +426,52 @@ int ClassHistogramDCmd::num_arguments() { + } + } + ++ClassesDCmd::ClassesDCmd(outputStream* output, bool heap) : ++ DCmdWithParser(output, heap), ++ _verbose("-verbose", ++ "Dump the detailed content of a Java class. " ++ "Some classes are annotated with flags: " ++ "F = has, or inherits, a non-empty finalize method, " ++ "f = has final method, " ++ "W = methods rewritten, " ++ "C = marked with @Contended annotation, " ++ "R = has been redefined, " ++ "S = is shared class", ++ "BOOLEAN", false, "false") { ++ _dcmdparser.add_dcmd_option(&_verbose); ++} ++ ++class VM_PrintClasses : public VM_Operation { ++private: ++ outputStream* _out; ++ bool _verbose; ++public: ++ VM_PrintClasses(outputStream* out, bool verbose) : _out(out), _verbose(verbose) {} ++ ++ virtual VMOp_Type type() const { return VMOp_PrintClasses; } ++ ++ virtual void doit() { ++ PrintClassClosure closure(_out, _verbose); ++ ClassLoaderDataGraph::classes_do(&closure); ++ } ++}; ++ ++void ClassesDCmd::execute(DCmdSource source, TRAPS) { ++ VM_PrintClasses vmop(output(), _verbose.is_set()); ++ VMThread::execute(&vmop); ++} ++ ++int ClassesDCmd::num_arguments() { ++ ResourceMark rm; ++ ClassesDCmd* dcmd = new ClassesDCmd(NULL, false); ++ if (dcmd != NULL) { ++ DCmdMark mark(dcmd); ++ return dcmd->_dcmdparser.num_arguments(); ++ } else { ++ return 0; ++ } ++} ++ + #define DEFAULT_COLUMNS "InstBytes,KlassBytes,CpAll,annotations,MethodCount,Bytecodes,MethodAll,ROAll,RWAll,Total" + ClassStatsDCmd::ClassStatsDCmd(outputStream* output, bool heap) : + DCmdWithParser(output, heap), +diff --git a/hotspot/src/share/vm/services/diagnosticCommand.hpp b/hotspot/src/share/vm/services/diagnosticCommand.hpp +index e28011f..f86ab5f 100644 +--- a/hotspot/src/share/vm/services/diagnosticCommand.hpp ++++ b/hotspot/src/share/vm/services/diagnosticCommand.hpp +@@ -314,6 +314,29 @@ public: + virtual void execute(DCmdSource source, TRAPS); + }; + ++class ClassesDCmd : public DCmdWithParser { ++protected: ++ DCmdArgument _verbose; ++public: ++ ClassesDCmd(outputStream* output, bool heap); ++ static const char* name() { ++ return "VM.classes"; ++ } ++ static const char* description() { ++ return "Print all loaded classes"; ++ } ++ static const char* impact() { ++ return "Medium: Depends on number of loaded classes."; ++ } ++ static const JavaPermission permission() { ++ JavaPermission p = {"java.lang.management.ManagementPermission", ++ "monitor", NULL}; ++ return p; ++ } ++ static int num_arguments(); ++ virtual void execute(DCmdSource source, TRAPS); ++}; ++ + class ClassStatsDCmd : public DCmdWithParser { + protected: + DCmdArgument _all; +diff --git a/hotspot/test/runtime/CommandLine/PrintClasses.java b/hotspot/test/runtime/CommandLine/PrintClasses.java +new file mode 100644 +index 0000000..7c1d4db +--- /dev/null ++++ b/hotspot/test/runtime/CommandLine/PrintClasses.java +@@ -0,0 +1,51 @@ ++/* ++ * Copyright (c) 2022, Alibaba Group Holding Limited. All rights reserved. ++ * Copyright (c) 2022, Huawei Technologies Co., Ltd. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ */ ++ ++ ++/* ++ * @test ++ * @bug 8275775 ++ * @summary Test jcmd VM.classes ++ * @library /testlibrary ++ * @run main/othervm PrintClasses ++ */ ++ ++import com.oracle.java.testlibrary.*; ++ ++public class PrintClasses { ++ public static void main(String args[]) throws Exception { ++ String pid = Integer.toString(ProcessTools.getProcessId()); ++ ProcessBuilder pb = new ProcessBuilder(); ++ ++ pb.command(new String[] { JDKToolFinder.getJDKTool("jcmd"), pid, "VM.classes"}); ++ OutputAnalyzer output = new OutputAnalyzer(pb.start()); ++ output.shouldNotContain("instance size"); ++ output.shouldContain(PrintClasses.class.getSimpleName()); ++ ++ pb.command(new String[] { JDKToolFinder.getJDKTool("jcmd"), pid, "VM.classes", "-verbose"}); ++ output = new OutputAnalyzer(pb.start()); ++ output.shouldContain("instance size"); ++ output.shouldContain(PrintClasses.class.getSimpleName()); ++ } ++} +\ No newline at end of file +-- +1.8.3.1 diff --git a/8287109-Distrust-failed-with-CertificateExpired.patch b/8287109-Distrust-failed-with-CertificateExpired.patch new file mode 100644 index 0000000000000000000000000000000000000000..11a8755508584eac643789f7ab155d4fba5a59b2 --- /dev/null +++ b/8287109-Distrust-failed-with-CertificateExpired.patch @@ -0,0 +1,237 @@ +From d2d3408154beb52370ee8784767375a7cc8d325d Mon Sep 17 00:00:00 2001 +Date: Wed, 21 Sep 2022 10:31:17 +0800 +Subject: 8287109: Distrust.java failed with CertificateExpiredException + +--- + .../Symantec/Distrust.java | 26 +++++- + .../Symantec/appleistca2g1-chain.pem | 80 ------------------- + .../Symantec/geotrustglobalca-chain.pem | 66 --------------- + 3 files changed, 23 insertions(+), 149 deletions(-) + delete mode 100644 jdk/test/sun/security/ssl/X509TrustManagerImpl/Symantec/appleistca2g1-chain.pem + delete mode 100644 jdk/test/sun/security/ssl/X509TrustManagerImpl/Symantec/geotrustglobalca-chain.pem + +diff --git a/jdk/test/sun/security/ssl/X509TrustManagerImpl/Symantec/Distrust.java b/jdk/test/sun/security/ssl/X509TrustManagerImpl/Symantec/Distrust.java +index d394f417..22266255 100644 +--- a/jdk/test/sun/security/ssl/X509TrustManagerImpl/Symantec/Distrust.java ++++ b/jdk/test/sun/security/ssl/X509TrustManagerImpl/Symantec/Distrust.java +@@ -51,15 +51,14 @@ public class Distrust { + // Each of the roots have a test certificate chain stored in a file + // named "-chain.pem". + private static String[] rootsToTest = new String[] { +- "geotrustglobalca", "geotrustprimarycag2", "geotrustprimarycag3", ++ "geotrustprimarycag2", "geotrustprimarycag3", + "geotrustuniversalca", "thawteprimaryrootca", "thawteprimaryrootcag2", + "thawteprimaryrootcag3", "verisignclass3g3ca", "verisignclass3g4ca", + "verisignclass3g5ca", "verisignuniversalrootca" }; + + // Each of the subCAs with a delayed distrust date have a test certificate + // chain stored in a file named "-chain.pem". +- private static String[] subCAsToTest = new String[] { +- "appleistca2g1", "appleistca8g1" }; ++ private static String[] subCAsToTest = new String[] {"appleistca8g1"}; + + // A date that is after the restrictions take affect + private static final Date APRIL_17_2019 = +@@ -177,6 +176,11 @@ public class Distrust { + throw new Exception("chain should be invalid"); + } + } catch (CertificateException ce) { ++ // expired TLS certificates should not be treated as failure ++ if (expired(ce)) { ++ System.err.println("Test is N/A, chain is expired"); ++ return; ++ } + if (valid) { + throw new Exception("Unexpected exception, chain " + + "should be valid", ce); +@@ -184,6 +188,7 @@ public class Distrust { + if (ce instanceof ValidatorException) { + ValidatorException ve = (ValidatorException)ce; + if (ve.getErrorType() != ValidatorException.T_UNTRUSTED_CERT) { ++ ce.printStackTrace(System.err); + throw new Exception("Unexpected exception: " + ce); + } + } else { +@@ -192,6 +197,21 @@ public class Distrust { + } + } + ++ // check if a cause of exception is an expired cert ++ private static boolean expired(CertificateException ce) { ++ if (ce instanceof CertificateExpiredException) { ++ return true; ++ } ++ Throwable t = ce.getCause(); ++ while (t != null) { ++ if (t instanceof CertificateExpiredException) { ++ return true; ++ } ++ t = t.getCause(); ++ } ++ return false; ++ } ++ + private static X509Certificate[] loadCertificateChain(String name) + throws Exception { + try (InputStream in = new FileInputStream(TEST_SRC + File.separator + +diff --git a/jdk/test/sun/security/ssl/X509TrustManagerImpl/Symantec/appleistca2g1-chain.pem b/jdk/test/sun/security/ssl/X509TrustManagerImpl/Symantec/appleistca2g1-chain.pem +deleted file mode 100644 +index 0235631d..00000000 +--- a/jdk/test/sun/security/ssl/X509TrustManagerImpl/Symantec/appleistca2g1-chain.pem ++++ /dev/null +@@ -1,80 +0,0 @@ +------BEGIN CERTIFICATE----- +-MIIGGzCCBQOgAwIBAgIITJltLCqcD0gwDQYJKoZIhvcNAQELBQAwYjEcMBoGA1UE +-AxMTQXBwbGUgSVNUIENBIDIgLSBHMTEgMB4GA1UECxMXQ2VydGlmaWNhdGlvbiBB +-dXRob3JpdHkxEzARBgNVBAoTCkFwcGxlIEluYy4xCzAJBgNVBAYTAlVTMB4XDTE5 +-MDEwODIxMTcxNFoXDTIwMDgwODIxMjcwMFowgaoxSjBIBgNVBAMMQWFjdGl2ZS5n +-ZW90cnVzdC1nbG9iYWwtY2EudGVzdC1wYWdlcy5jZXJ0aWZpY2F0ZW1hbmFnZXIu +-YXBwbGUuY29tMSUwIwYDVQQLDBxtYW5hZ2VtZW50OmlkbXMuZ3JvdXAuODY0ODU5 +-MRMwEQYDVQQKDApBcHBsZSBJbmMuMRMwEQYDVQQIDApDYWxpZm9ybmlhMQswCQYD +-VQQGEwJVUzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMCjFUrVHTEX +-0aVU6x9LiGa6oVr9blaCsMFrLicPQguc43Vs/pN+g4jzRXsTSMe9XefezBQb6tzZ +-SMRXVB4kWMr4K1BVgQDkXeyoh4KrXRkdEF9ZIJPNxwTmmYUOc5M6NOYwkLelYz+t +-7n1iNIGylbjwU4qwauElk2alFVqYTEPDLzwvqVDb9jMAJ8MPSDjfUlXW0XD9oXZM +-hC+8LU9JBgJ3YBdzRHa4WnrudUbWjspqaNfAYpVIX0cfCJKnMsKqaSKjS4pIRtWm +-L6NlCTCoIMyOh+wmbWPPX24H2D3+ump5FA35fRYbVznmosl5n1AK34S9tD4XZ7lO +-WZKfaFi1liMCAwEAAaOCAoowggKGMAwGA1UdEwEB/wQCMAAwHwYDVR0jBBgwFoAU +-2HqURHyQcJAWnt0XnAFEA4bWKikwfgYIKwYBBQUHAQEEcjBwMDQGCCsGAQUFBzAC +-hihodHRwOi8vY2VydHMuYXBwbGUuY29tL2FwcGxlaXN0Y2EyZzEuZGVyMDgGCCsG +-AQUFBzABhixodHRwOi8vb2NzcC5hcHBsZS5jb20vb2NzcDAzLWFwcGxlaXN0Y2Ey +-ZzEwMTBMBgNVHREERTBDgkFhY3RpdmUuZ2VvdHJ1c3QtZ2xvYmFsLWNhLnRlc3Qt +-cGFnZXMuY2VydGlmaWNhdGVtYW5hZ2VyLmFwcGxlLmNvbTCB/wYDVR0gBIH3MIH0 +-MIHxBgoqhkiG92NkBQsEMIHiMIGkBggrBgEFBQcCAjCBlwyBlFJlbGlhbmNlIG9u +-IHRoaXMgY2VydGlmaWNhdGUgYnkgYW55IHBhcnR5IGFzc3VtZXMgYWNjZXB0YW5j +-ZSBvZiBhbnkgYXBwbGljYWJsZSB0ZXJtcyBhbmQgY29uZGl0aW9ucyBvZiB1c2Ug +-YW5kL29yIGNlcnRpZmljYXRpb24gcHJhY3RpY2Ugc3RhdGVtZW50cy4wOQYIKwYB +-BQUHAgEWLWh0dHA6Ly93d3cuYXBwbGUuY29tL2NlcnRpZmljYXRlYXV0aG9yaXR5 +-L3JwYTAdBgNVHSUEFjAUBggrBgEFBQcDAgYIKwYBBQUHAwEwNwYDVR0fBDAwLjAs +-oCqgKIYmaHR0cDovL2NybC5hcHBsZS5jb20vYXBwbGVpc3RjYTJnMS5jcmwwHQYD +-VR0OBBYEFP0qkmFJhArI0MsfW0V+/wY9x4GSMA4GA1UdDwEB/wQEAwIFoDANBgkq +-hkiG9w0BAQsFAAOCAQEATjT8M0bIq+mFc8k5cd4KDjCMBjYl/l3/8zKlWYGP+nl1 +-KRogXcGRa3LcfpdJcqgMrx8e9Xohduvl8MBzwv671rYkppzZdsmZdLVorAdbL5GL +-suhTjAS5yL3NBWNMRpeOgFsVr7YtPDEvo3CFsnzjg7THe0S6Y35oYukJtUzGUvSY +-kC3ApBTdjj0vAeow+dbt+AHKnQiEnon4ToSFmtnkru08Uxe7uyHCQ2sLUg0EPYc9 +-t9I8lviaHfK/mQoCzlme2O/H5Rher8dXCv8hVT1NKbsi28EpgpqcTLS+hn/Edc/q +-4dPDoO1Ozs+ixRzFeMpA+JrnAyARb6qbSrAPBgtIbQ== +------END CERTIFICATE----- +------BEGIN CERTIFICATE----- +-MIIEQDCCAyigAwIBAgIDAjp0MA0GCSqGSIb3DQEBCwUAMEIxCzAJBgNVBAYTAlVT +-MRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMRswGQYDVQQDExJHZW9UcnVzdCBHbG9i +-YWwgQ0EwHhcNMTQwNjE2MTU0MjAyWhcNMjIwNTIwMTU0MjAyWjBiMRwwGgYDVQQD +-ExNBcHBsZSBJU1QgQ0EgMiAtIEcxMSAwHgYDVQQLExdDZXJ0aWZpY2F0aW9uIEF1 +-dGhvcml0eTETMBEGA1UEChMKQXBwbGUgSW5jLjELMAkGA1UEBhMCVVMwggEiMA0G +-CSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDQk6EdR0MgFrILa+vD1bTox5jN896/ +-6E3p4zaAB/xFG2p8RYauVtOkCX9hDWtdflJrfbTIOcT0Zzr3g84Zb4YvfkV+Rxxn +-UsqVBV3iNlGFwNRngDVvFd0+/R3S/Y80UNjsdiq+49Pa5P3I6ygClhGXF2Ec6cRZ +-O0LcMtEJHdqm0UOG/16yvIzPZtsBiwKulEjzOI/96jKoCOyGl1GUJD5JSZZT6Hmh +-QIHpBbuTlVH84/18EUv3ngizFUkVB/nRN6CbSzL2tcTcatH8Cu324MUpoKiLcf4N +-krz+VHAYCm3H7Qz7yS0Gw4yF/MuGXNY2jhKLCX/7GRo41fCUMHoPpozzAgMBAAGj +-ggEdMIIBGTAfBgNVHSMEGDAWgBTAephojYn7qwVkDBF9qn1luMrMTjAdBgNVHQ4E +-FgQU2HqURHyQcJAWnt0XnAFEA4bWKikwEgYDVR0TAQH/BAgwBgEB/wIBADAOBgNV +-HQ8BAf8EBAMCAQYwNQYDVR0fBC4wLDAqoCigJoYkaHR0cDovL2cuc3ltY2IuY29t +-L2NybHMvZ3RnbG9iYWwuY3JsMC4GCCsGAQUFBwEBBCIwIDAeBggrBgEFBQcwAYYS +-aHR0cDovL2cuc3ltY2QuY29tMEwGA1UdIARFMEMwQQYKYIZIAYb4RQEHNjAzMDEG +-CCsGAQUFBwIBFiVodHRwOi8vd3d3Lmdlb3RydXN0LmNvbS9yZXNvdXJjZXMvY3Bz +-MA0GCSqGSIb3DQEBCwUAA4IBAQAWR3NvhaJi4ecqdruJlUIml7xKrKxwUzo/MYM9 +-PByrmuKxXRx2GqA8DHJXvtOeUODImdZY1wLqzg0pVHzN9cLGkClVo28UqAtCDTqY +-bQZ4nvBqox0CCqIopI3CgUY+bWfa3j/+hQ5CKhLetbf7uBunlux3n+zUU5V6/wf0 +-8goUwFFSsdaOUAsamVy8C8m97e34XsFW201+I6QRoSzUGwWa5BtS9nw4mQVLunKN +-QolgBGYq9P1o12v3mUEo1mwkq+YlUy7Igpnioo8jvjCDsSeL+mh/AUnoxphrEC6Y +-XorXykuxx8lYmtA225aV7LaB5PLNbxt5h0wQPInkTfpU3Kqm +------END CERTIFICATE----- +------BEGIN CERTIFICATE----- +-MIIDVDCCAjygAwIBAgIDAjRWMA0GCSqGSIb3DQEBBQUAMEIxCzAJBgNVBAYTAlVT +-MRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMRswGQYDVQQDExJHZW9UcnVzdCBHbG9i +-YWwgQ0EwHhcNMDIwNTIxMDQwMDAwWhcNMjIwNTIxMDQwMDAwWjBCMQswCQYDVQQG +-EwJVUzEWMBQGA1UEChMNR2VvVHJ1c3QgSW5jLjEbMBkGA1UEAxMSR2VvVHJ1c3Qg +-R2xvYmFsIENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA2swYYzD9 +-9BcjGlZ+W988bDjkcbd4kdS8odhM+KhDtgPpTSEHCIjaWC9mOSm9BXiLnTjoBbdq +-fnGk5sRgprDvgOSJKA+eJdbtg/OtppHHmMlCGDUUna2YRpIuT8rxh0PBFpVXLVDv +-iS2Aelet8u5fa9IAjbkU+BQVNdnARqN7csiRv8lVK83Qlz6cJmTM386DGXHKTubU +-1XupGc1V3sjs0l44U+VcT4wt/lAjNvxm5suOpDkZALeVAjmRCw7+OC7RHQWa9k0+ +-bw8HHa8sHo9gOeL6NlMTOdReJivbPagUvTLrGAMoUgRx5aszPeE4uwc2hGKceeoW +-MPRfwCvocWvk+QIDAQABo1MwUTAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBTA +-ephojYn7qwVkDBF9qn1luMrMTjAfBgNVHSMEGDAWgBTAephojYn7qwVkDBF9qn1l +-uMrMTjANBgkqhkiG9w0BAQUFAAOCAQEANeMpauUvXVSOKVCUn5kaFOSPeCpilKIn +-Z57QzxpeR+nBsqTP3UEaBU6bS+5Kb1VSsyShNwrrZHYqLizz/Tt1kL/6cdjHPTfS +-tQWVYrmm3ok9Nns4d0iXrKYgjy6myQzCsplFAMfOEVEiIuCl6rYVSAlk6l5PdPcF +-PseKUgzbFbS9bZvlxrFUaKnjaZC2mqUPuLk/IH2uSrW4nOQdtqvmlKXBx4Ot2/Un +-hw4EbNX/3aBd7YdStysVAq45pmp06drE57xNNB6pXE0zX5IJL4hmXXeXxx12E6nV +-5fEWCRE11azbJHFwLJhWC9kXtNHjUStedejV0NxPNO3CBWaAocvmMw== +------END CERTIFICATE----- +diff --git a/jdk/test/sun/security/ssl/X509TrustManagerImpl/Symantec/geotrustglobalca-chain.pem b/jdk/test/sun/security/ssl/X509TrustManagerImpl/Symantec/geotrustglobalca-chain.pem +deleted file mode 100644 +index 3249716b..00000000 +--- a/jdk/test/sun/security/ssl/X509TrustManagerImpl/Symantec/geotrustglobalca-chain.pem ++++ /dev/null +@@ -1,66 +0,0 @@ +------BEGIN CERTIFICATE----- +-MIIHBjCCBe6gAwIBAgIQanINWwJAuap0V7lFjnfUwTANBgkqhkiG9w0BAQsFADBE +-MQswCQYDVQQGEwJVUzEWMBQGA1UEChMNR2VvVHJ1c3QgSW5jLjEdMBsGA1UEAxMU +-R2VvVHJ1c3QgU1NMIENBIC0gRzMwHhcNMTcwNTAzMDAwMDAwWhcNMjAwNTAyMjM1 +-OTU5WjCBkTELMAkGA1UEBhMCVVMxEzARBgNVBAgMCkNhbGlmb3JuaWExFjAUBgNV +-BAcMDU1vdW50YWluIFZpZXcxFzAVBgNVBAoMDkdlb1RydXN0LCBJbmMuMRgwFgYD +-VQQLDA9Sb290IDEwIC0gVkFMSUQxIjAgBgNVBAMMGXZhbGlkLXJvb3QxMC5nZW90 +-cnVzdC5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDTegUYhAh0 +-P7aF6jzk8dit4Vzddo3hM+J7Eak/+N1sqVUS2HpNd7VO50FrbEWKIRusv7QNtlpY +-1Cgrla8M4RAhCB0wkkHXZ1Evz6E1AEFQqNSjyuRQxeEXl+xCL+MF+yAMhDRnHh+E +-eSJ3ie0T66saOyaLM9fPpr3xomAQ/IRlP1atJ/Z8XbPo25HuxwzxiWFW+RjwVIfI +-gxHz4Okwc1uImDUIDlEu9Uaqqb4jHhxU1EkKMmgEncpqwCROcZMujUkogfB49Z7+ +-K17r6ARIrUuxqfNPrPwe+O88WgIeDSWffPM67UlvtomZOwuTNdv9OoCX1wUCLS7m +-/gZ3rqqqeJvfAgMBAAGjggOkMIIDoDAkBgNVHREEHTAbghl2YWxpZC1yb290MTAu +-Z2VvdHJ1c3QuY29tMAkGA1UdEwQCMAAwDgYDVR0PAQH/BAQDAgWgMCsGA1UdHwQk +-MCIwIKAeoByGGmh0dHA6Ly9nbi5zeW1jYi5jb20vZ24uY3JsMIGdBgNVHSAEgZUw +-gZIwgY8GBmeBDAECAjCBhDA/BggrBgEFBQcCARYzaHR0cHM6Ly93d3cuZ2VvdHJ1 +-c3QuY29tL3Jlc291cmNlcy9yZXBvc2l0b3J5L2xlZ2FsMEEGCCsGAQUFBwICMDUM +-M2h0dHBzOi8vd3d3Lmdlb3RydXN0LmNvbS9yZXNvdXJjZXMvcmVwb3NpdG9yeS9s +-ZWdhbDAdBgNVHSUEFjAUBggrBgEFBQcDAQYIKwYBBQUHAwIwHwYDVR0jBBgwFoAU +-0m/3lvSFP3I8MH0j2oV4m6N8WnwwVwYIKwYBBQUHAQEESzBJMB8GCCsGAQUFBzAB +-hhNodHRwOi8vZ24uc3ltY2QuY29tMCYGCCsGAQUFBzAChhpodHRwOi8vZ24uc3lt +-Y2IuY29tL2duLmNydDCCAfUGCisGAQQB1nkCBAIEggHlBIIB4QHfAHUA3esdK3oN +-T6Ygi4GtgWhwfi6OnQHVXIiNPRHEzbbsvswAAAFbz9h5vQAABAMARjBEAiAx/C0U +-5NdHxK4v2oHnstYksb1Vny8PcQkSvgpx9PsZEwIgNTOU70Zc5szG23xdbvtoH5lN +-SAoVswiF5gFQS5MGu1sAdgCkuQmQtBhYFIe7E6LMZ3AKPDWYBPkb37jjd80OyA3c +-EAAAAVvP2HnZAAAEAwBHMEUCIFGjB8r2H0VDwTUE/aY/Mv+M97sqAvEP1doOcHpg +-0qyfAiEArw/S2F7OEcmKGUY1WRBuApfAx5d7hzrTSV/jZv95qJwAdgDuS723dc5g +-uuFCaR+r4Z5mow9+X7By2IMAxHuJeqj9ywAAAVvP2HoDAAAEAwBHMEUCIQCH6MFZ +-tZF3Cqukt3/69fkU0Y5ePXXx8+xkOXRsIG3EGgIgSmCBWrnmPiiGA3x5QP8I8m4r +-Uee0y7s4NQNwjMgHrjwAdgC8eOHfxfY8aEZJM02hD6FfCXlpIAnAgbTz9pF/Ptm4 +-pQAAAVvP2HqcAAAEAwBHMEUCIA8e2kAVYYuQCtn4PqK98BuHnLm9rC40DboFLCle +-SmQsAiEApbCJR05hr9VkNWmjaaUUGGZdVyUu9XX504LHVWyXZDUwDQYJKoZIhvcN +-AQELBQADggEBAEtfBfZ2y5uTohvW3h00Kcuop6Nq7Y59GU3MeizPKtx48DB8qHyd +-y5bLFwXzsGA1WkwpKzPbROsTGcAAXJHh03bj24AemUr/J/eQcjkfSoNBdHDpiSsk +-VZkQK2fGJDiYJ/r9mxKZcgd2pyN3l2OtVtNMv2dnFGF35UkkeqO3jqImwbypAmRX +-HdQV9dvW2YDRjzkebNNey6UwY9+YTSzr4da2hcaMHrj588Eqa4DDgNcY9QnE2RzN +-giArA+4RlM4AZ3jC2A756I67hrlvH+lhumHLp06hGfMiQJF1aaauFVSa36HKc3C/ +-ty+sLdJbemEJLAr8uNXggFD+U8TKw1S4LSw= +------END CERTIFICATE----- +------BEGIN CERTIFICATE----- +-MIIETzCCAzegAwIBAgIDAjpvMA0GCSqGSIb3DQEBCwUAMEIxCzAJBgNVBAYTAlVT +-MRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMRswGQYDVQQDExJHZW9UcnVzdCBHbG9i +-YWwgQ0EwHhcNMTMxMTA1MjEzNjUwWhcNMjIwNTIwMjEzNjUwWjBEMQswCQYDVQQG +-EwJVUzEWMBQGA1UEChMNR2VvVHJ1c3QgSW5jLjEdMBsGA1UEAxMUR2VvVHJ1c3Qg +-U1NMIENBIC0gRzMwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDjvn4K +-hqPPa209K6GXrUkkTdd3uTR5CKWeop7eRxKSPX7qGYax6E89X/fQp3eaWx8KA7UZ +-U9ulIZRpY51qTJEMEEe+EfpshiW3qwRoQjgJZfAU2hme+msLq2LvjafvY3AjqK+B +-89FuiGdT7BKkKXWKp/JXPaKDmJfyCn3U50NuMHhiIllZuHEnRaoPZsZVP/oyFysx +-j0ag+mkUfJ2fWuLrM04QprPtd2PYw5703d95mnrU7t7dmszDt6ldzBE6B7tvl6QB +-I0eVH6N3+liSxsfQvc+TGEK3fveeZerVO8rtrMVwof7UEJrwEgRErBpbeFBFV0xv +-vYDLgVwts7x2oR5lAgMBAAGjggFKMIIBRjAfBgNVHSMEGDAWgBTAephojYn7qwVk +-DBF9qn1luMrMTjAdBgNVHQ4EFgQU0m/3lvSFP3I8MH0j2oV4m6N8WnwwEgYDVR0T +-AQH/BAgwBgEB/wIBADAOBgNVHQ8BAf8EBAMCAQYwNgYDVR0fBC8wLTAroCmgJ4Yl +-aHR0cDovL2cxLnN5bWNiLmNvbS9jcmxzL2d0Z2xvYmFsLmNybDAvBggrBgEFBQcB +-AQQjMCEwHwYIKwYBBQUHMAGGE2h0dHA6Ly9nMi5zeW1jYi5jb20wTAYDVR0gBEUw +-QzBBBgpghkgBhvhFAQc2MDMwMQYIKwYBBQUHAgEWJWh0dHA6Ly93d3cuZ2VvdHJ1 +-c3QuY29tL3Jlc291cmNlcy9jcHMwKQYDVR0RBCIwIKQeMBwxGjAYBgNVBAMTEVN5 +-bWFudGVjUEtJLTEtNTM5MA0GCSqGSIb3DQEBCwUAA4IBAQCg1Pcs+3QLf2TxzUNq +-n2JTHAJ8mJCi7k9o1CAacxI+d7NQ63K87oi+fxfqd4+DYZVPhKHLMk9sIb7SaZZ9 +-Y73cK6gf0BOEcP72NZWJ+aZ3sEbIu7cT9clgadZM/tKO79NgwYCA4ef7i28heUrg +-3Kkbwbf7w0lZXLV3B0TUl/xJAIlvBk4BcBmsLxHA4uYPL4ZLjXvDuacu9PGsFj45 +-SVGeF0tPEDpbpaiSb/361gsDTUdWVxnzy2v189bPsPX1oxHSIFMTNDcFLENaY9+N +-QNaFHlHpURceA1bJ8TCt55sRornQMYGbaLHZ6PPmlH7HrhMvh+3QJbBo+d4IWvMp +-zNSS +------END CERTIFICATE----- +-- +2.22.0 + diff --git a/8290705_fix_StringConcat_validate_mem_flow_asserts_with_unexpected_userStoreI.patch b/8290705_fix_StringConcat_validate_mem_flow_asserts_with_unexpected_userStoreI.patch new file mode 100644 index 0000000000000000000000000000000000000000..ae487e7ae7f18fe43d60b64919ba7516ba8ba78f --- /dev/null +++ b/8290705_fix_StringConcat_validate_mem_flow_asserts_with_unexpected_userStoreI.patch @@ -0,0 +1,145 @@ +diff --git a/hotspot/src/share/vm/opto/stringopts.cpp b/hotspot/src/share/vm/opto/stringopts.cpp +index d92a3d7a3..2d11b2257 100644 +--- a/hotspot/src/share/vm/opto/stringopts.cpp ++++ b/hotspot/src/share/vm/opto/stringopts.cpp +@@ -968,6 +968,21 @@ bool StringConcat::validate_control_flow() { + fail = true; + break; + } else if (ptr->is_Proj() && ptr->in(0)->is_Initialize()) { ++ // Check for side effect between Initialize and the constructor ++ for (SimpleDUIterator iter(ptr); iter.has_next(); iter.next()) { ++ Node* use = iter.get(); ++ if (!use->is_CFG() && !use->is_CheckCastPP() && !use->is_Load()) { ++#ifndef PRODUCT ++ if (PrintOptimizeStringConcat) { ++ tty->print_cr("unexpected control use of Initialize"); ++ ptr->in(0)->dump(); // Initialize node ++ use->dump(1); ++ } ++#endif ++ fail = true; ++ break; ++ } ++ } + ptr = ptr->in(0)->in(0); + } else if (ptr->is_Region()) { + Node* copy = ptr->as_Region()->is_copy(); +diff --git a/hotspot/test/compiler/stringopts/SideEffectBeforeConstructor.jasm b/hotspot/test/compiler/stringopts/SideEffectBeforeConstructor.jasm +new file mode 100644 +index 000000000..cbc6d754b +--- /dev/null ++++ b/hotspot/test/compiler/stringopts/SideEffectBeforeConstructor.jasm +@@ -0,0 +1,58 @@ ++/* ++ * Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ */ ++ ++super public class compiler/stringopts/SideEffectBeforeConstructor ++ version 51:0 ++{ ++ public static Field result:I; ++ ++ static Method "":"()V" ++ stack 2 locals 0 ++ { ++ iconst_0; ++ putstatic Field result:"I"; ++ return; ++ } ++ public Method "":"()V" ++ stack 1 locals 1 ++ { ++ aload_0; ++ invokespecial Method java/lang/Object."":"()V"; ++ return; ++ } ++ ++ public static Method test:"(Ljava/lang/String;)V" ++ stack 4 locals 1 ++ { ++ new class java/lang/StringBuffer; ++ dup; ++ getstatic Field result:"I"; ++ iconst_1; ++ iadd; ++ putstatic Field result:"I"; ++ aload_0; ++ invokespecial Method java/lang/StringBuffer."":"(Ljava/lang/String;)V"; ++ invokevirtual Method java/lang/StringBuffer.toString:"()Ljava/lang/String;"; ++ return; ++ } ++} +diff --git a/hotspot/test/compiler/stringopts/TestSideEffectBeforeConstructor.java b/hotspot/test/compiler/stringopts/TestSideEffectBeforeConstructor.java +new file mode 100644 +index 000000000..86c5eca1d +--- /dev/null ++++ b/hotspot/test/compiler/stringopts/TestSideEffectBeforeConstructor.java +@@ -0,0 +1,49 @@ ++/* ++ * Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ */ ++ ++/* ++ * @test ++ * @bug 8290705 ++ * @summary Test correctness of the string concatenation optimization with ++ * a store between StringBuffer allocation and constructor invocation. ++ * @compile SideEffectBeforeConstructor.jasm ++ * @run main/othervm -Xbatch compiler.stringopts.TestSideEffectBeforeConstructor ++ */ ++ ++package compiler.stringopts; ++ ++public class TestSideEffectBeforeConstructor { ++ ++ public static void main(String[] args) { ++ for (int i = 0; i < 100_000; ++i) { ++ try { ++ SideEffectBeforeConstructor.test(null); ++ } catch (NullPointerException npe) { ++ // Expected ++ } ++ } ++ if (SideEffectBeforeConstructor.result != 100_000) { ++ throw new RuntimeException("Unexpected result: " + SideEffectBeforeConstructor.result); ++ } ++ } ++} diff --git a/8293114-GC-should-trim-the-native-heap-and-bug-fix.patch b/8293114-GC-should-trim-the-native-heap-and-bug-fix.patch new file mode 100644 index 0000000000000000000000000000000000000000..1cc4fed773da81eaaa1e0408ca82f59be53c225b --- /dev/null +++ b/8293114-GC-should-trim-the-native-heap-and-bug-fix.patch @@ -0,0 +1,1503 @@ +From a5edc79220300bce7952feaacf28a832306884d8 Mon Sep 17 00:00:00 2001 +From: eapen +Date: Mon, 12 Dec 2022 19:28:28 +0800 +Subject: [PATCH 14/33] I68TO2: 8293114: GC should trim the native heap,8136854:G1 ConcurrentG1RefineThread::stop delays JVM shutdown for >150ms +--- + hotspot/src/os/aix/vm/os_aix.cpp | 5 + + hotspot/src/os/bsd/vm/os_bsd.cpp | 5 + + hotspot/src/os/linux/vm/os_linux.cpp | 149 ++++++- + hotspot/src/os/linux/vm/os_linux.hpp | 57 ++- + hotspot/src/os/linux/vm/trimCHeapDCmd.cpp | 59 +-- + hotspot/src/os/windows/vm/os_windows.cpp | 4 + + .../vm/gc_implementation/g1/g1CollectedHeap.cpp | 6 + + .../parallelScavenge/parallelScavengeHeap.cpp | 3 + + .../parallelScavenge/psParallelCompact.cpp | 6 + + .../shared/concurrentGCThread.cpp | 8 +- + .../shared/concurrentGCThread.hpp | 6 +- + .../gc_implementation/shared/gcTrimNativeHeap.cpp | 246 ++++++++++++ + .../gc_implementation/shared/gcTrimNativeHeap.hpp | 66 ++++ + hotspot/src/share/vm/memory/genCollectedHeap.cpp | 6 + + hotspot/src/share/vm/memory/sharedHeap.cpp | 6 + + hotspot/src/share/vm/runtime/globals.hpp | 10 + + hotspot/src/share/vm/runtime/init.cpp | 5 +- + hotspot/src/share/vm/runtime/java.cpp | 3 + + hotspot/src/share/vm/runtime/os.hpp | 11 + + .../src/share/vm/utilities/globalDefinitions.hpp | 3 + + hotspot/test/gc/TestTrimNative.java | 435 +++++++++++++++++++++ + .../test/serviceability/dcmd/TrimLibcHeapTest.java | 7 +- + 22 files changed, 994 insertions(+), 112 deletions(-) + create mode 100644 hotspot/src/share/vm/gc_implementation/shared/gcTrimNativeHeap.cpp + create mode 100644 hotspot/src/share/vm/gc_implementation/shared/gcTrimNativeHeap.hpp + create mode 100644 hotspot/test/gc/TestTrimNative.java + +diff --git a/hotspot/src/os/aix/vm/os_aix.cpp b/hotspot/src/os/aix/vm/os_aix.cpp +index b078bee..519b085 100644 +--- a/hotspot/src/os/aix/vm/os_aix.cpp ++++ b/hotspot/src/os/aix/vm/os_aix.cpp +@@ -5266,3 +5266,8 @@ void TestReserveMemorySpecial_test() { + // No tests available for this platform + } + #endif ++ ++// stubbed-out trim-native support ++bool os::can_trim_native_heap() { return false; } ++bool os::should_trim_native_heap() { return false; } ++bool os::trim_native_heap(os::size_change_t* rss_change) { return false; } +\ No newline at end of file +diff --git a/hotspot/src/os/bsd/vm/os_bsd.cpp b/hotspot/src/os/bsd/vm/os_bsd.cpp +index 340334c..85e2861 100644 +--- a/hotspot/src/os/bsd/vm/os_bsd.cpp ++++ b/hotspot/src/os/bsd/vm/os_bsd.cpp +@@ -4899,3 +4899,8 @@ void TestReserveMemorySpecial_test() { + // No tests available for this platform + } + #endif ++ ++// stubbed-out trim-native support ++bool os::can_trim_native_heap() { return false; } ++bool os::should_trim_native_heap() { return false; } ++bool os::trim_native_heap(os::size_change_t* rss_change) { return false; } +\ No newline at end of file +diff --git a/hotspot/src/os/linux/vm/os_linux.cpp b/hotspot/src/os/linux/vm/os_linux.cpp +index 099dafa..abf2031 100644 +--- a/hotspot/src/os/linux/vm/os_linux.cpp ++++ b/hotspot/src/os/linux/vm/os_linux.cpp +@@ -153,8 +153,28 @@ const char * os::Linux::_libpthread_version = NULL; + pthread_condattr_t os::Linux::_condattr[1]; + + #ifdef __GLIBC__ +-os::Linux::mallinfo_func_t os::Linux::_mallinfo = NULL; +-os::Linux::mallinfo2_func_t os::Linux::_mallinfo2 = NULL; ++// We want to be runnable with both old and new glibcs. ++// Old glibcs offer mallinfo(). New glibcs deprecate mallinfo() and offer mallinfo2() ++// as replacement. Future glibc's may remove the deprecated mallinfo(). ++// Therefore we may have one, both, or possibly neither (?). Code should tolerate all ++// cases, which is why we resolve the functions dynamically. Outside code should use ++// the Linux::get_mallinfo() utility function which exists to hide this mess. ++struct glibc_mallinfo { ++ int arena; ++ int ordblks; ++ int smblks; ++ int hblks; ++ int hblkhd; ++ int usmblks; ++ int fsmblks; ++ int uordblks; ++ int fordblks; ++ int keepcost; ++}; ++typedef struct glibc_mallinfo (*mallinfo_func_t)(void); ++typedef struct os::Linux::glibc_mallinfo2 (*mallinfo2_func_t)(void); ++static mallinfo_func_t g_mallinfo = NULL; ++static mallinfo2_func_t g_mallinfo2 = NULL; + #endif // __GLIBC__ + + static jlong initial_time_count=0; +@@ -2348,23 +2368,21 @@ void os::Linux::print_process_memory_info(outputStream* st) { + // Print glibc outstanding allocations. + // (note: there is no implementation of mallinfo for muslc) + #ifdef __GLIBC__ +- size_t total_allocated = 0; + bool might_have_wrapped = false; +- if (_mallinfo2 != NULL) { +- struct glibc_mallinfo2 mi = _mallinfo2(); +- total_allocated = mi.uordblks; +- } else if (_mallinfo != NULL) { +- // mallinfo is an old API. Member names mean next to nothing and, beyond that, are int. +- // So values may have wrapped around. Still useful enough to see how much glibc thinks +- // we allocated. +- struct glibc_mallinfo mi = _mallinfo(); +- total_allocated = (size_t)(unsigned)mi.uordblks; +- // Since mallinfo members are int, glibc values may have wrapped. Warn about this. +- might_have_wrapped = (info.vmrss * K) > UINT_MAX && (info.vmrss * K) > (total_allocated + UINT_MAX); +- } +- if (_mallinfo2 != NULL || _mallinfo != NULL) { +- st->print_cr("C-Heap outstanding allocations: " SIZE_FORMAT "K%s", +- total_allocated / K, ++ glibc_mallinfo2 mi; ++ mallinfo_retval_t mirc = os::Linux::get_mallinfo(&mi); ++ if (mirc != os::Linux::error) { ++ size_t total_allocated = mi.uordblks + mi.hblkhd; ++ size_t free_retained = mi.fordblks; ++#ifdef _LP64 ++ // If all we had is old mallinf(3), the values may have wrapped. Since that can confuse readers ++ // of this output, print a hint. ++ // We do this by checking virtual size of the process: if that is <4g, we could not have wrapped. ++ might_have_wrapped = (mirc == os::Linux::ok_but_possibly_wrapped) && ++ ((info.vmsize * K) > UINT_MAX); ++#endif ++ st->print_cr("C-Heap outstanding allocations: " SIZE_FORMAT "K, retained: " SIZE_FORMAT "K%s", ++ total_allocated / K, free_retained / K, + might_have_wrapped ? " (may have wrapped)" : ""); + } + +@@ -5187,8 +5205,8 @@ void os::init(void) { + Linux::initialize_system_info(); + + #ifdef __GLIBC__ +- Linux::_mallinfo = CAST_TO_FN_PTR(Linux::mallinfo_func_t, dlsym(RTLD_DEFAULT, "mallinfo")); +- Linux::_mallinfo2 = CAST_TO_FN_PTR(Linux::mallinfo2_func_t, dlsym(RTLD_DEFAULT, "mallinfo2")); ++ g_mallinfo = CAST_TO_FN_PTR(mallinfo_func_t, dlsym(RTLD_DEFAULT, "mallinfo")); ++ g_mallinfo2 = CAST_TO_FN_PTR(mallinfo2_func_t, dlsym(RTLD_DEFAULT, "mallinfo2")); + #endif // __GLIBC__ + + // _main_thread points to the thread that created/loaded the JVM. +@@ -6820,3 +6838,94 @@ void TestReserveMemorySpecial_test() { + } + + #endif ++ ++#ifdef __GLIBC__ ++os::Linux::mallinfo_retval_t os::Linux::get_mallinfo(glibc_mallinfo2* out) { ++ if (g_mallinfo2) { ++ glibc_mallinfo2 mi = g_mallinfo2(); ++ *out = mi; ++ return os::Linux::ok; ++ } else if (g_mallinfo) { ++ // mallinfo() returns 32-bit values. Not perfect but still useful if ++ // process virt size < 4g ++ glibc_mallinfo mi = g_mallinfo(); ++ out->arena = (int) mi.arena; ++ out->ordblks = (int) mi.ordblks; ++ out->smblks = (int) mi.smblks; ++ out->hblks = (int) mi.hblks; ++ out->hblkhd = (int) mi.hblkhd; ++ out->usmblks = (int) mi.usmblks; ++ out->fsmblks = (int) mi.fsmblks; ++ out->uordblks = (int) mi.uordblks; ++ out->fordblks = (int) mi.fordblks; ++ out->keepcost = (int) mi.keepcost; ++ return os::Linux::ok_but_possibly_wrapped; ++ } ++ return os::Linux::ok; ++} ++#endif // __GLIBC__ ++ ++// Trim-native support ++bool os::can_trim_native_heap() { ++#ifdef __GLIBC__ ++ return true; ++#else ++ return false; // musl ++#endif ++} ++ ++static const size_t retain_size = 2 * M; ++ ++bool os::should_trim_native_heap() { ++#ifdef __GLIBC__ ++ bool rc = true; ++ // We try, using mallinfo, to predict whether a malloc_trim(3) will be beneficial. ++ // ++ // "mallinfo::keepcost" is no help even if manpage claims this to be the projected ++ // trim size. In practice it is just a very small value with no relation to the actual ++ // effect trimming will have. ++ // ++ // Our best bet is "mallinfo::fordblks", the total chunk size of free blocks. Since ++ // only free blocks can be trimmed, a very low bar is to require their combined size ++ // to be higher than our retain size. Note, however, that "mallinfo::fordblks" includes ++ // already-trimmed blocks, since glibc trims by calling madvice(MADV_DONT_NEED) on free ++ // chunks but does not update its bookkeeping. ++ // ++ // In the end we want to prevent obvious bogus attempts to trim, and for that fordblks ++ // is good enough. ++ os::Linux::glibc_mallinfo2 mi; ++ os::Linux::mallinfo_retval_t mirc = os::Linux::get_mallinfo(&mi); ++ const size_t total_free = mi.fordblks; ++ if (mirc == os::Linux::ok) { ++ rc = retain_size < total_free; ++ } ++ return rc; ++#else ++ return false; // musl ++#endif ++} ++ ++bool os::trim_native_heap(os::size_change_t* rss_change) { ++#ifdef __GLIBC__ ++ os::Linux::meminfo_t info1; ++ os::Linux::meminfo_t info2; ++ ++ bool have_info1 = os::Linux::query_process_memory_info(&info1); ++ ::malloc_trim(retain_size); ++ bool have_info2 = have_info1 && os::Linux::query_process_memory_info(&info2); ++ ++ if (have_info1 && have_info2 && ++ info1.vmrss != -1 && info2.vmrss != -1 && ++ info1.vmswap != -1 && info2.vmswap != -1) { ++ // Note: query_process_memory_info returns values in K ++ rss_change->before = (info1.vmrss + info1.vmswap) * K; ++ rss_change->after = (info2.vmrss + info2.vmswap) * K; ++ } else { ++ rss_change->after = rss_change->before = SIZE_MAX; ++ } ++ ++ return true; ++#else ++ return false; // musl ++#endif ++} +\ No newline at end of file +diff --git a/hotspot/src/os/linux/vm/os_linux.hpp b/hotspot/src/os/linux/vm/os_linux.hpp +index 2bb3fd2..6c27bcb 100644 +--- a/hotspot/src/os/linux/vm/os_linux.hpp ++++ b/hotspot/src/os/linux/vm/os_linux.hpp +@@ -243,7 +243,7 @@ class Linux { + public: + static pthread_condattr_t* condAttr() { return _condattr; } + +- // Output structure for query_process_memory_info() ++ // Output structure for query_process_memory_info() (all values in KB) + struct meminfo_t { + ssize_t vmsize; // current virtual size + ssize_t vmpeak; // peak virtual size +@@ -338,40 +338,6 @@ private: + }; + static NumaAllocationPolicy _current_numa_policy; + +-#ifdef __GLIBC__ +- struct glibc_mallinfo { +- int arena; +- int ordblks; +- int smblks; +- int hblks; +- int hblkhd; +- int usmblks; +- int fsmblks; +- int uordblks; +- int fordblks; +- int keepcost; +- }; +- +- struct glibc_mallinfo2 { +- size_t arena; +- size_t ordblks; +- size_t smblks; +- size_t hblks; +- size_t hblkhd; +- size_t usmblks; +- size_t fsmblks; +- size_t uordblks; +- size_t fordblks; +- size_t keepcost; +- }; +- +- typedef struct glibc_mallinfo (*mallinfo_func_t)(void); +- typedef struct glibc_mallinfo2 (*mallinfo2_func_t)(void); +- +- static mallinfo_func_t _mallinfo; +- static mallinfo2_func_t _mallinfo2; +-#endif +- + public: + static int sched_getcpu() { return _sched_getcpu != NULL ? _sched_getcpu() : -1; } + static int numa_node_to_cpus(int node, unsigned long *buffer, int bufferlen) { +@@ -484,6 +450,27 @@ public: + return false; + } + } ++ ++#ifdef __GLIBC__ ++ struct glibc_mallinfo2 { ++ size_t arena; ++ size_t ordblks; ++ size_t smblks; ++ size_t hblks; ++ size_t hblkhd; ++ size_t usmblks; ++ size_t fsmblks; ++ size_t uordblks; ++ size_t fordblks; ++ size_t keepcost; ++ }; ++ enum mallinfo_retval_t { ok, error, ok_but_possibly_wrapped }; ++ // get_mallinfo() is a wrapper for mallinfo/mallinfo2. It will prefer mallinfo2() if found. ++ // If we only have mallinfo(), values may be 32-bit truncated, which is signaled via ++ // "ok_but_possibly_wrapped". ++ static mallinfo_retval_t get_mallinfo(glibc_mallinfo2* out); ++#endif ++ + }; + + +diff --git a/hotspot/src/os/linux/vm/trimCHeapDCmd.cpp b/hotspot/src/os/linux/vm/trimCHeapDCmd.cpp +index 95d03d9..39d47a3 100644 +--- a/hotspot/src/os/linux/vm/trimCHeapDCmd.cpp ++++ b/hotspot/src/os/linux/vm/trimCHeapDCmd.cpp +@@ -1,5 +1,5 @@ + /* +- * Copyright (c) 2021 SAP SE. All rights reserved. ++ * Copyright (c) 2022 SAP SE. All rights reserved. + * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * +@@ -25,53 +25,28 @@ + + #include "precompiled.hpp" + #include "runtime/os.hpp" ++#include "trimCHeapDCmd.hpp" + #include "utilities/debug.hpp" + #include "utilities/ostream.hpp" +-#include "trimCHeapDCmd.hpp" ++#include "utilities/globalDefinitions.hpp" + + #include + + void TrimCLibcHeapDCmd::execute(DCmdSource source, TRAPS) { +-#ifdef __GLIBC__ +- stringStream ss_report(1024); // Note: before calling trim +- +- os::Linux::meminfo_t info1; +- os::Linux::meminfo_t info2; +- // Query memory before... +- bool have_info1 = os::Linux::query_process_memory_info(&info1); +- +- _output->print_cr("Attempting trim..."); +- ::malloc_trim(0); +- _output->print_cr("Done."); +- +- // ...and after trim. +- bool have_info2 = os::Linux::query_process_memory_info(&info2); +- +- // Print report both to output stream as well to UL +- bool wrote_something = false; +- if (have_info1 && have_info2) { +- if (info1.vmsize != -1 && info2.vmsize != -1) { +- ss_report.print_cr("Virtual size before: " SSIZE_FORMAT "k, after: " SSIZE_FORMAT "k, (" SSIZE_FORMAT "k)", +- info1.vmsize, info2.vmsize, (info2.vmsize - info1.vmsize)); +- wrote_something = true; +- } +- if (info1.vmrss != -1 && info2.vmrss != -1) { +- ss_report.print_cr("RSS before: " SSIZE_FORMAT "k, after: " SSIZE_FORMAT "k, (" SSIZE_FORMAT "k)", +- info1.vmrss, info2.vmrss, (info2.vmrss - info1.vmrss)); +- wrote_something = true; +- } +- if (info1.vmswap != -1 && info2.vmswap != -1) { +- ss_report.print_cr("Swap before: " SSIZE_FORMAT "k, after: " SSIZE_FORMAT "k, (" SSIZE_FORMAT "k)", +- info1.vmswap, info2.vmswap, (info2.vmswap - info1.vmswap)); +- wrote_something = true; ++ if (os::can_trim_native_heap()) { ++ os::size_change_t sc; ++ if (os::trim_native_heap(&sc)) { ++ _output->print("Trim native heap: "); ++ if (sc.after != SIZE_MAX) { ++ const size_t delta = sc.after < sc.before ? (sc.before - sc.after) : (sc.after - sc.before); ++ const char sign = sc.after < sc.before ? '-' : '+'; ++ _output->print_cr("RSS+Swap: " PROPERFMT "->" PROPERFMT " (%c" PROPERFMT ")", ++ PROPERFMTARGS(sc.before), PROPERFMTARGS(sc.after), sign, PROPERFMTARGS(delta)); ++ } else { ++ _output->print_cr("(no details available)."); ++ } + } ++ } else { ++ _output->print_cr("Not available."); + } +- if (!wrote_something) { +- ss_report.print_raw("No details available."); +- } +- +- _output->print_raw(ss_report.base()); +-#else +- _output->print_cr("Not available."); +-#endif + } +diff --git a/hotspot/src/os/windows/vm/os_windows.cpp b/hotspot/src/os/windows/vm/os_windows.cpp +index 500a412..25122de 100644 +--- a/hotspot/src/os/windows/vm/os_windows.cpp ++++ b/hotspot/src/os/windows/vm/os_windows.cpp +@@ -5957,3 +5957,7 @@ void TestReserveMemorySpecial_test() { + } + #endif // PRODUCT + ++// stubbed-out trim-native support ++bool os::can_trim_native_heap() { return false; } ++bool os::should_trim_native_heap() { return false; } ++bool os::trim_native_heap(os::size_change_t* rss_change) { return false; } +\ No newline at end of file +diff --git a/hotspot/src/share/vm/gc_implementation/g1/g1CollectedHeap.cpp b/hotspot/src/share/vm/gc_implementation/g1/g1CollectedHeap.cpp +index ba156a2..7188925 100644 +--- a/hotspot/src/share/vm/gc_implementation/g1/g1CollectedHeap.cpp ++++ b/hotspot/src/share/vm/gc_implementation/g1/g1CollectedHeap.cpp +@@ -53,6 +53,7 @@ + #include "gc_implementation/shared/gcTimer.hpp" + #include "gc_implementation/shared/gcTrace.hpp" + #include "gc_implementation/shared/gcTraceTime.hpp" ++#include "gc_implementation/shared/gcTrimNativeHeap.hpp" + #include "gc_implementation/shared/isGCActiveMark.hpp" + #include "memory/allocation.hpp" + #include "memory/heapInspection.hpp" +@@ -1304,6 +1305,9 @@ bool G1CollectedHeap::do_collection(bool explicit_gc, + TraceCollectorStats tcs(g1mm()->full_collection_counters()); + TraceMemoryManagerStats tms(true /* fullGC */, gc_cause()); + ++ // Pause native trimming for the duration of the GC ++ GCTrimNative::pause_periodic_trim(); ++ + double start = os::elapsedTime(); + g1_policy()->record_full_collection_start(); + +@@ -1546,6 +1550,8 @@ bool G1CollectedHeap::do_collection(bool explicit_gc, + + gc_timer->register_gc_end(); + gc_tracer->report_gc_end(gc_timer->gc_end(), gc_timer->time_partitions()); ++ ++ GCTrimNative::schedule_trim(); + } + return true; + } +diff --git a/hotspot/src/share/vm/gc_implementation/parallelScavenge/parallelScavengeHeap.cpp b/hotspot/src/share/vm/gc_implementation/parallelScavenge/parallelScavengeHeap.cpp +index 74c1584..1c47125 100644 +--- a/hotspot/src/share/vm/gc_implementation/parallelScavenge/parallelScavengeHeap.cpp ++++ b/hotspot/src/share/vm/gc_implementation/parallelScavenge/parallelScavengeHeap.cpp +@@ -36,6 +36,7 @@ + #include "gc_implementation/parallelScavenge/psScavenge.hpp" + #include "gc_implementation/parallelScavenge/vmPSOperations.hpp" + #include "gc_implementation/shared/gcHeapSummary.hpp" ++#include "gc_implementation/shared/gcTrimNativeHeap.hpp" + #include "gc_implementation/shared/gcWhen.hpp" + #include "memory/gcLocker.inline.hpp" + #include "oops/oop.inline.hpp" +@@ -147,6 +148,8 @@ void ParallelScavengeHeap::post_initialize() { + PSMarkSweep::initialize(); + } + PSPromotionManager::initialize(); ++ ++ GCTrimNative::initialize(true); + } + + void ParallelScavengeHeap::update_counters() { +diff --git a/hotspot/src/share/vm/gc_implementation/parallelScavenge/psParallelCompact.cpp b/hotspot/src/share/vm/gc_implementation/parallelScavenge/psParallelCompact.cpp +index 3f103ee..26d64a1 100644 +--- a/hotspot/src/share/vm/gc_implementation/parallelScavenge/psParallelCompact.cpp ++++ b/hotspot/src/share/vm/gc_implementation/parallelScavenge/psParallelCompact.cpp +@@ -42,6 +42,7 @@ + #include "gc_implementation/shared/gcTimer.hpp" + #include "gc_implementation/shared/gcTrace.hpp" + #include "gc_implementation/shared/gcTraceTime.hpp" ++#include "gc_implementation/shared/gcTrimNativeHeap.hpp" + #include "gc_implementation/shared/isGCActiveMark.hpp" + #include "gc_interface/gcCause.hpp" + #include "memory/gcLocker.inline.hpp" +@@ -2008,6 +2009,9 @@ bool PSParallelCompact::invoke_no_policy(bool maximum_heap_compaction) { + return false; + } + ++ // Pause native trimming for the duration of the GC ++ GCTrimNative::pause_periodic_trim(); ++ + ParallelScavengeHeap* heap = gc_heap(); + + _gc_timer.register_gc_start(); +@@ -2182,6 +2186,8 @@ bool PSParallelCompact::invoke_no_policy(bool maximum_heap_compaction) { + // Resize the metaspace capactiy after a collection + MetaspaceGC::compute_new_size(); + ++ GCTrimNative::schedule_trim(); ++ + if (TraceGen1Time) accumulated_time()->stop(); + + if (PrintGC) { +diff --git a/hotspot/src/share/vm/gc_implementation/shared/concurrentGCThread.cpp b/hotspot/src/share/vm/gc_implementation/shared/concurrentGCThread.cpp +index e39fd7a..024499a 100644 +--- a/hotspot/src/share/vm/gc_implementation/shared/concurrentGCThread.cpp ++++ b/hotspot/src/share/vm/gc_implementation/shared/concurrentGCThread.cpp +@@ -1,5 +1,5 @@ + /* +- * Copyright (c) 2001, 2012, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2001, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it +@@ -40,12 +40,12 @@ ConcurrentGCThread::ConcurrentGCThread() : + _should_terminate(false), _has_terminated(false) { + }; + +-void ConcurrentGCThread::create_and_start() { ++void ConcurrentGCThread::create_and_start(ThreadPriority prio) { + if (os::create_thread(this, os::cgc_thread)) { + // XXX: need to set this to low priority + // unless "agressive mode" set; priority + // should be just less than that of VMThread. +- os::set_priority(this, NearMaxPriority); ++ os::set_priority(this, prio); + if (!_should_terminate && !DisableStartThread) { + os::start_thread(this); + } +@@ -63,7 +63,7 @@ void ConcurrentGCThread::initialize_in_thread() { + void ConcurrentGCThread::wait_for_universe_init() { + MutexLockerEx x(CGC_lock, Mutex::_no_safepoint_check_flag); + while (!is_init_completed() && !_should_terminate) { +- CGC_lock->wait(Mutex::_no_safepoint_check_flag, 200); ++ CGC_lock->wait(Mutex::_no_safepoint_check_flag, 1); + } + } + +diff --git a/hotspot/src/share/vm/gc_implementation/shared/concurrentGCThread.hpp b/hotspot/src/share/vm/gc_implementation/shared/concurrentGCThread.hpp +index e87228b..1e16bf7 100644 +--- a/hotspot/src/share/vm/gc_implementation/shared/concurrentGCThread.hpp ++++ b/hotspot/src/share/vm/gc_implementation/shared/concurrentGCThread.hpp +@@ -1,5 +1,5 @@ + /* +- * Copyright (c) 2001, 2013, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2001, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it +@@ -33,7 +33,7 @@ class ConcurrentGCThread: public NamedThread { + friend class VMStructs; + + protected: +- bool _should_terminate; ++ bool volatile _should_terminate; + bool _has_terminated; + + enum CGC_flag_type { +@@ -50,7 +50,7 @@ protected: + static int reset_CGC_flag(int b) { return _CGC_flag &= ~b; } + + // Create and start the thread (setting it's priority high.) +- void create_and_start(); ++ void create_and_start(ThreadPriority prio = NearMaxPriority); + + // Do initialization steps in the thread: record stack base and size, + // init thread local storage, set JNI handle block. +diff --git a/hotspot/src/share/vm/gc_implementation/shared/gcTrimNativeHeap.cpp b/hotspot/src/share/vm/gc_implementation/shared/gcTrimNativeHeap.cpp +new file mode 100644 +index 0000000..b9bac56 +--- /dev/null ++++ b/hotspot/src/share/vm/gc_implementation/shared/gcTrimNativeHeap.cpp +@@ -0,0 +1,246 @@ ++/* ++ * Copyright (c) 2022 SAP SE. All rights reserved. ++ * Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2022, Huawei Technologies Co., Ltd. All rights reserved. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ * ++ */ ++ ++#include "precompiled.hpp" ++#include "gc_implementation/shared/concurrentGCThread.hpp" ++#include "gc_implementation/shared/gcTrimNativeHeap.hpp" ++#include "gc_implementation/g1/g1_globals.hpp" ++#include "runtime/globals.hpp" ++#include "runtime/globals_extension.hpp" ++#include "runtime/mutex.hpp" ++#include "runtime/mutexLocker.hpp" ++#include "runtime/os.hpp" ++#include "utilities/debug.hpp" ++#include "utilities/globalDefinitions.hpp" ++#include "utilities/ostream.hpp" ++#include "utilities/ticks.hpp" ++ ++bool GCTrimNative::_async_mode = false; ++double GCTrimNative::_next_trim_not_before = 0; ++ ++// GCTrimNative works in two modes: ++// ++// - async mode, where GCTrimNative runs a trimmer thread on behalf of the GC. ++// The trimmer thread will be doing all the trims, both periodically and ++// triggered from outside via GCTrimNative::schedule_trim(). ++// ++// - synchronous mode, where the GC does the trimming itself in its own thread, ++// via GCTrimNative::should_trim() and GCTrimNative::execute_trim(). ++// ++// The mode is set as argument to GCTrimNative::initialize(). ++ ++class NativeTrimmer : public ConcurrentGCThread { ++ ++ Monitor* _lock; ++ volatile jlong _paused; ++ static NativeTrimmer* _the_trimmer; ++ ++public: ++ ++ virtual void run() { ++ initialize_in_thread(); ++ wait_for_universe_init(); ++ ++ assert(GCTrimNativeHeap, "Sanity"); ++ assert(os::can_trim_native_heap(), "Sanity"); ++ ++ gclog_or_tty->print_cr("NativeTrimmer started."); ++ ++ // Note: GCTrimNativeHeapInterval=0 -> zero wait time -> indefinite waits, disabling periodic trim ++ const int64_t delay_ms = GCTrimNativeHeapInterval * 1000; ++ for (;;) { ++ MonitorLockerEx ml(_lock, Mutex::_no_safepoint_check_flag); ++ ml.wait(Mutex::_no_safepoint_check_flag, delay_ms); ++ if (_should_terminate) { ++ gclog_or_tty->print_cr("NativeTrimmer stopped."); ++ break; ++ } ++ jlong paused = Atomic::load(&_paused); ++ if (!paused && os::should_trim_native_heap()) { ++ GCTrimNative::do_trim(); ++ } ++ } ++ ++ terminate(); ++ } ++ ++ void stop() { ++ { ++ MutexLockerEx ml(Terminator_lock); ++ _should_terminate = true; ++ } ++ ++ wakeup(); ++ ++ { ++ MutexLockerEx ml(Terminator_lock); ++ while (!_has_terminated) { ++ Terminator_lock->wait(); ++ } ++ } ++ } ++ ++protected: ++ ++ void wakeup() { ++ MonitorLockerEx ml(_lock, Mutex::_no_safepoint_check_flag); ++ ml.notify_all(); ++ } ++ ++ void pause() { ++ Atomic::store(1, &_paused); ++ debug_only(gclog_or_tty->print_cr("NativeTrimmer paused")); ++ } ++ ++ void unpause() { ++ Atomic::store(0, &_paused); ++ debug_only(gclog_or_tty->print_cr("NativeTrimmer unpaused")); ++ } ++ ++public: ++ ++ NativeTrimmer() : ++ _paused(0) ++ { ++ //Mutex::leaf+8 just for NativeTrimmer_lock ++ _lock = new (std::nothrow) Monitor(Mutex::leaf+8, "NativeTrimmer_lock", true); ++ set_name("NativeTrimmer Thread"); ++ } ++ ++ static bool is_enabled() { ++ return _the_trimmer != NULL; ++ } ++ ++ static void start_trimmer() { ++ _the_trimmer = new NativeTrimmer(); ++ _the_trimmer->create_and_start(NormPriority); ++ } ++ ++ static void stop_trimmer() { ++ _the_trimmer->stop(); ++ } ++ ++ static void pause_periodic_trim() { ++ _the_trimmer->pause(); ++ } ++ ++ static void unpause_periodic_trim() { ++ _the_trimmer->unpause(); ++ } ++ ++ static void schedule_trim_now() { ++ _the_trimmer->unpause(); ++ _the_trimmer->wakeup(); ++ } ++ ++}; // NativeTrimmer ++ ++NativeTrimmer* NativeTrimmer::_the_trimmer = NULL; ++ ++void GCTrimNative::do_trim() { ++ Ticks start = Ticks::now(); ++ os::size_change_t sc; ++ if (os::trim_native_heap(&sc)) { ++ Tickspan trim_time = (Ticks::now() - start); ++ if (sc.after != SIZE_MAX) { ++ const size_t delta = sc.after < sc.before ? (sc.before - sc.after) : (sc.after - sc.before); ++ const char sign = sc.after < sc.before ? '-' : '+'; ++ gclog_or_tty->print_cr("Trim native heap: RSS+Swap: " PROPERFMT "->" PROPERFMT " (%c" PROPERFMT "), %1.3fms", ++ PROPERFMTARGS(sc.before), PROPERFMTARGS(sc.after), sign, PROPERFMTARGS(delta), ++ trim_time.seconds() * 1000); ++ } else { ++ gclog_or_tty->print_cr("Trim native heap (no details)"); ++ } ++ } ++} ++ ++/// GCTrimNative outside facing methods ++ ++void GCTrimNative::initialize(bool async_mode) { ++ ++ if (GCTrimNativeHeap) { ++ ++ if (!os::can_trim_native_heap()) { ++ FLAG_SET_ERGO(bool, GCTrimNativeHeap, false); ++ gclog_or_tty->print_cr("GCTrimNativeHeap disabled - trim-native not supported on this platform."); ++ return; ++ } ++ ++ debug_only(gclog_or_tty->print_cr("GCTrimNativeHeap enabled.")); ++ ++ _async_mode = async_mode; ++ ++ // If we are to run the trimmer on behalf of the GC: ++ if (_async_mode) { ++ NativeTrimmer::start_trimmer(); ++ } ++ ++ _next_trim_not_before = GCTrimNativeHeapInterval; ++ } ++} ++ ++void GCTrimNative::cleanup() { ++ if (GCTrimNativeHeap) { ++ if (_async_mode) { ++ NativeTrimmer::stop_trimmer(); ++ } ++ } ++} ++ ++bool GCTrimNative::should_trim(bool ignore_delay) { ++ return ++ GCTrimNativeHeap && os::can_trim_native_heap() && ++ (ignore_delay || (GCTrimNativeHeapInterval > 0 && os::elapsedTime() > _next_trim_not_before)) && ++ os::should_trim_native_heap(); ++} ++ ++void GCTrimNative::execute_trim() { ++ if (GCTrimNativeHeap) { ++ assert(!_async_mode, "Only call for non-async mode"); ++ do_trim(); ++ _next_trim_not_before = os::elapsedTime() + GCTrimNativeHeapInterval; ++ } ++} ++ ++void GCTrimNative::pause_periodic_trim() { ++ if (GCTrimNativeHeap) { ++ assert(_async_mode, "Only call for async mode"); ++ NativeTrimmer::pause_periodic_trim(); ++ } ++} ++ ++void GCTrimNative::unpause_periodic_trim() { ++ if (GCTrimNativeHeap) { ++ assert(_async_mode, "Only call for async mode"); ++ NativeTrimmer::unpause_periodic_trim(); ++ } ++} ++ ++void GCTrimNative::schedule_trim() { ++ if (GCTrimNativeHeap) { ++ assert(_async_mode, "Only call for async mode"); ++ NativeTrimmer::schedule_trim_now(); ++ } ++} +diff --git a/hotspot/src/share/vm/gc_implementation/shared/gcTrimNativeHeap.hpp b/hotspot/src/share/vm/gc_implementation/shared/gcTrimNativeHeap.hpp +new file mode 100644 +index 0000000..f586093 +--- /dev/null ++++ b/hotspot/src/share/vm/gc_implementation/shared/gcTrimNativeHeap.hpp +@@ -0,0 +1,66 @@ ++/* ++ * Copyright (c) 2022 SAP SE. All rights reserved. ++ * Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2022, Huawei Technologies Co., Ltd. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ * ++ */ ++ ++#ifndef SHARE_GC_SHARED_GCTRIMNATIVEHEAP_HPP ++#define SHARE_GC_SHARED_GCTRIMNATIVEHEAP_HPP ++ ++#include "memory/allocation.hpp" ++ ++class NativeTrimmer; ++ ++class GCTrimNative : public AllStatic { ++ friend class NativeTrimmer; ++ ++ static bool _async_mode; ++ static double _next_trim_not_before; ++ ++ static void do_trim(); ++ ++public: ++ ++ static void initialize(bool async_mode); ++ static void cleanup(); ++ ++ // Returns true if: ++ // - trimming is enabled and possible ++ // - trimming may have an actual effect (guess) ++ // - delay timer has expired (unless ignore_delay is true) ++ static bool should_trim(bool ignore_delay); ++ ++ // Execute trim-native in this thread ++ static void execute_trim(); ++ ++ // Pause/unpause periodic trim ++ static void pause_periodic_trim(); ++ static void unpause_periodic_trim(); ++ ++ // Schedule an explicit trim now; if periodic trims had been ++ // paused, they are unpaused. ++ static void schedule_trim(); ++ ++}; ++ ++#endif // SHARE_GC_SHARED_GCTRIMNATIVEHEAP_HPP +diff --git a/hotspot/src/share/vm/memory/genCollectedHeap.cpp b/hotspot/src/share/vm/memory/genCollectedHeap.cpp +index 20fbbfd..7df3d68 100644 +--- a/hotspot/src/share/vm/memory/genCollectedHeap.cpp ++++ b/hotspot/src/share/vm/memory/genCollectedHeap.cpp +@@ -58,6 +58,7 @@ + #if INCLUDE_ALL_GCS + #include "gc_implementation/concurrentMarkSweep/concurrentMarkSweepThread.hpp" + #include "gc_implementation/concurrentMarkSweep/vmCMSOperations.hpp" ++#include "gc_implementation/shared/gcTrimNativeHeap.hpp" + #endif // INCLUDE_ALL_GCS + #if INCLUDE_JFR + #include "jfr/jfr.hpp" +@@ -572,6 +573,11 @@ void GenCollectedHeap::do_collection(bool full, + update_full_collections_completed(); + } + ++ // Trim the native heap, without a delay since this is a full gc ++ if (full && GCTrimNative::should_trim(true)) { ++ GCTrimNative::execute_trim(); ++ } ++ + // Track memory usage and detect low memory after GC finishes + MemoryService::track_memory_usage(); + +diff --git a/hotspot/src/share/vm/memory/sharedHeap.cpp b/hotspot/src/share/vm/memory/sharedHeap.cpp +index ef22f01..8c02320 100644 +--- a/hotspot/src/share/vm/memory/sharedHeap.cpp ++++ b/hotspot/src/share/vm/memory/sharedHeap.cpp +@@ -26,6 +26,7 @@ + #include "classfile/symbolTable.hpp" + #include "classfile/systemDictionary.hpp" + #include "code/codeCache.hpp" ++#include "gc_implementation/shared/gcTrimNativeHeap.hpp" + #include "gc_interface/collectedHeap.inline.hpp" + #include "memory/sharedHeap.hpp" + #include "oops/oop.inline.hpp" +@@ -104,6 +105,11 @@ void SharedHeap::set_barrier_set(BarrierSet* bs) { + void SharedHeap::post_initialize() { + CollectedHeap::post_initialize(); + ref_processing_init(); ++ if (!UseSerialGC) { ++ GCTrimNative::initialize(true); ++ } else { ++ GCTrimNative::initialize(false); // false since we will call trim inside the collecting thread ++ } + } + + void SharedHeap::ref_processing_init() {} +diff --git a/hotspot/src/share/vm/runtime/globals.hpp b/hotspot/src/share/vm/runtime/globals.hpp +index 0dab18e..ec48c48 100644 +--- a/hotspot/src/share/vm/runtime/globals.hpp ++++ b/hotspot/src/share/vm/runtime/globals.hpp +@@ -3387,6 +3387,16 @@ class CommandLineFlags { + "Number of entries we will try to leave on the stack " \ + "during parallel gc") \ + \ ++ experimental(bool, GCTrimNativeHeap, false, \ ++ "GC will attempt to trim the native heap periodically and at " \ ++ "full GCs.") \ ++ \ ++ experimental(uintx, GCTrimNativeHeapInterval, 60, \ ++ "If GCTrimNativeHeap is enabled: interval time, in seconds, in " \ ++ "which the VM will attempt to trim the native heap. A value of " \ ++ "0 disables periodic trimming while leaving trimming at full gc " \ ++ "enabled.") \ ++ \ + /* stack parameters */ \ + product_pd(intx, StackYellowPages, \ + "Number of yellow zone (recoverable overflows) pages") \ +diff --git a/hotspot/src/share/vm/runtime/init.cpp b/hotspot/src/share/vm/runtime/init.cpp +index d15e40d..d2e0f22 100644 +--- a/hotspot/src/share/vm/runtime/init.cpp ++++ b/hotspot/src/share/vm/runtime/init.cpp +@@ -1,5 +1,5 @@ + /* +- * Copyright (c) 1997, 2014, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it +@@ -166,8 +166,7 @@ void exit_globals() { + } + } + +- +-static bool _init_completed = false; ++static volatile bool _init_completed = false; + + bool is_init_completed() { + return _init_completed; +diff --git a/hotspot/src/share/vm/runtime/java.cpp b/hotspot/src/share/vm/runtime/java.cpp +index 4f290c8..54b980d 100644 +--- a/hotspot/src/share/vm/runtime/java.cpp ++++ b/hotspot/src/share/vm/runtime/java.cpp +@@ -30,6 +30,7 @@ + #include "code/codeCache.hpp" + #include "compiler/compileBroker.hpp" + #include "compiler/compilerOracle.hpp" ++#include "gc_implementation/shared/gcTrimNativeHeap.hpp" + #include "interpreter/bytecodeHistogram.hpp" + #include "jfr/jfrEvents.hpp" + #include "jfr/support/jfrThreadId.hpp" +@@ -509,6 +510,8 @@ void before_exit(JavaThread * thread) { + StatSampler::disengage(); + StatSampler::destroy(); + ++ GCTrimNative::cleanup(); ++ + // Stop concurrent GC threads + Universe::heap()->stop(); + +diff --git a/hotspot/src/share/vm/runtime/os.hpp b/hotspot/src/share/vm/runtime/os.hpp +index 7ae49fd..acc57f4 100644 +--- a/hotspot/src/share/vm/runtime/os.hpp ++++ b/hotspot/src/share/vm/runtime/os.hpp +@@ -333,6 +333,17 @@ class os: AllStatic { + static bool uncommit_memory(char* addr, size_t bytes); + static bool release_memory(char* addr, size_t bytes); + ++ // Does the platform support trimming the native heap? ++ static bool can_trim_native_heap(); ++ ++ // Does the platform recommend trimming? ++ static bool should_trim_native_heap(); ++ ++ // Trim the C-heap. Returns RSS size change and optionally return the rss size change. ++ // If trim was done but size change could not be obtained, SIZE_MAX is returned for after size. ++ struct size_change_t { size_t before; size_t after; }; ++ static bool trim_native_heap(size_change_t* rss_change); ++ + // Touch memory pages that cover the memory range from start to end (exclusive) + // to make the OS back the memory range with actual memory. + // Current implementation may not touch the last page if unaligned addresses +diff --git a/hotspot/src/share/vm/utilities/globalDefinitions.hpp b/hotspot/src/share/vm/utilities/globalDefinitions.hpp +index 25f6f02..12eea20 100644 +--- a/hotspot/src/share/vm/utilities/globalDefinitions.hpp ++++ b/hotspot/src/share/vm/utilities/globalDefinitions.hpp +@@ -260,6 +260,9 @@ inline T byte_size_in_proper_unit(T s) { + } + } + ++#define PROPERFMT SIZE_FORMAT "%s" ++#define PROPERFMTARGS(S) byte_size_in_proper_unit(S), proper_unit_for_byte_size(S) ++ + //---------------------------------------------------------------------------------------------------- + // VM type definitions + +diff --git a/hotspot/test/gc/TestTrimNative.java b/hotspot/test/gc/TestTrimNative.java +new file mode 100644 +index 0000000..58d5405 +--- /dev/null ++++ b/hotspot/test/gc/TestTrimNative.java +@@ -0,0 +1,435 @@ ++/* ++ * Copyright (c) 2022 SAP SE. All rights reserved. ++ * Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2022, Huawei Technologies Co., Ltd. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ * ++ */ ++ ++package gc; ++ ++/* ++ * All these tests test the trim-native feature for all GCs. ++ * Trim-native is the ability to trim the C-heap as part of the GC cycle. ++ * This feature is controlled by -XX:+GCTrimNativeHeap (by default off). ++ * Trimming happens on full gc for all gcs. Shenandoah and G1 also support ++ * concurrent trimming (Shenandoah supports this without any ties to java ++ * heap occupancy). ++ * ++ */ ++ ++//// full gc tests ///// ++ ++/* ++ * @test id=fullgc-serial ++ * @summary Test that GCTrimNativeHeap works with Serial ++ * @requires vm.gc=="Serial" ++ * @requires os.family=="linux" ++ * @modules java.base/jdk.internal.misc ++ * @library /testlibrary ++ * @run driver gc.TestTrimNative test-fullgc serial ++ */ ++ ++/* ++ * @test id=fullgc-parallel ++ * @summary Test that GCTrimNativeHeap works with Parallel ++ * @requires vm.gc=="Parallel" ++ * @requires os.family=="linux" ++ * @modules java.base/jdk.internal.misc ++ * @library /testlibrary ++ * @run driver gc.TestTrimNative test-fullgc parallel ++ */ ++ ++/* ++ * @test id=fullgc-g1 ++ * @summary Test that GCTrimNativeHeap works with G1 ++ * @requires vm.gc=="G1" ++ * @requires os.family=="linux" ++ * @modules java.base/jdk.internal.misc ++ * @library /testlibrary ++ * @run driver gc.TestTrimNative test-fullgc g1 ++ */ ++ ++//// auto mode tests ///// ++ ++// Note: not serial, since it does not do periodic trimming, only trimming on full gc ++ ++/* ++ * @test id=auto-parallel ++ * @summary Test that GCTrimNativeHeap works with Parallel ++ * @requires vm.gc=="Parallel" ++ * @requires os.family=="linux" ++ * @modules java.base/jdk.internal.misc ++ * @library /testlibrary ++ * @run driver gc.TestTrimNative test-auto parallel ++ */ ++ ++/* ++ * @test id=auto-g1 ++ * @summary Test that GCTrimNativeHeap works with G1 ++ * @requires vm.gc=="G1" ++ * @requires os.family=="linux" ++ * @modules java.base/jdk.internal.misc ++ * @library /testlibrary ++ * @run driver gc.TestTrimNative test-auto g1 ++ */ ++ ++ ++//// test-auto-high-interval interval test ///// ++ ++// Note: not serial, since it does not do periodic trimming, only trimming on full gc ++ ++/* ++ * @test id=auto-high-interval-parallel ++ * @summary Test that a high GCTrimNativeHeapInterval effectively disables automatic trimming ++ * @requires vm.gc=="Parallel" ++ * @requires os.family=="linux" ++ * @modules java.base/jdk.internal.misc ++ * @library /testlibrary ++ * @run driver gc.TestTrimNative test-auto-high-interval parallel ++ */ ++ ++/* ++ * @test id=auto-high-interval-g1 ++ * @summary Test that a high GCTrimNativeHeapInterval effectively disables automatic trimming ++ * @requires vm.gc=="G1" ++ * @requires os.family=="linux" ++ * @modules java.base/jdk.internal.misc ++ * @library /testlibrary ++ * @run driver gc.TestTrimNative test-auto-high-interval g1 ++ */ ++ ++//// test-auto-interval-0 test ///// ++ ++// Note: not serial, since it does not do periodic trimming, only trimming on full gc ++ ++/* ++ * @test id=auto-zero-interval-parallel ++ * @summary Test that a GCTrimNativeHeapInterval=0 disables periodic trimming ++ * @requires vm.gc=="Parallel" ++ * @requires os.family=="linux" ++ * @modules java.base/jdk.internal.misc ++ * @library /testlibrary ++ * @run driver gc.TestTrimNative test-auto-zero-interval parallel ++ */ ++ ++/* ++ * @test id=auto-zero-interval-g1 ++ * @summary Test that a GCTrimNativeHeapInterval=0 disables periodic trimming ++ * @requires vm.gc=="G1" ++ * @requires os.family=="linux" ++ * @modules java.base/jdk.internal.misc ++ * @library /testlibrary ++ * @run driver gc.TestTrimNative test-auto-zero-interval g1 ++ */ ++ ++// Other tests ++ ++/* ++ * @test id=off-explicit ++ * @summary Test that -GCTrimNative disables the feature ++ * @requires os.family=="linux" ++ * @modules java.base/jdk.internal.misc ++ * @library /testlibrary ++ * @run driver gc.TestTrimNative test-off-explicit ++ */ ++ ++/* ++ * @test id=off-by-default ++ * @summary Test that GCTrimNative is off by default ++ * @requires os.family=="linux" ++ * @modules java.base/jdk.internal.misc ++ * @library /testlibrary ++ * @run driver gc.TestTrimNative test-off-by-default ++ */ ++ ++/* ++ * @test id=off-on-other-platforms ++ * @summary Test that GCTrimNative is off on unsupportive platforms ++ * @requires os.family!="linux" ++ * @modules java.base/jdk.internal.misc ++ * @library /testlibrary ++ * @run driver gc.TestTrimNative test-off-on-other-platforms ++ */ ++ ++import sun.misc.Unsafe; ++import com.oracle.java.testlibrary.*; ++ ++import java.lang.reflect.Field; ++import java.util.*; ++import java.util.regex.Matcher; ++import java.util.regex.Pattern; ++ ++public class TestTrimNative { ++ ++ // Actual RSS increase is a lot larger than 4 MB. Depends on glibc overhead, and NMT malloc headers in debug VMs. ++ // We need small-grained allocations to make sure they actually increase RSS (all touched) and to see the ++ // glibc-retaining-memory effect. ++ static final int szAllocations = 16; ++ static final int totalAllocationsSize = 16 * 1024 * 1024; // 16 MB total ++ static final int numAllocations = totalAllocationsSize / szAllocations; ++ ++ static long[] ptrs = new long[numAllocations]; ++ ++ enum Unit { ++ B(1), K(1024), M(1024*1024), G(1024*1024*1024); ++ public final long size; ++ Unit(long size) { this.size = size; } ++ } ++ ++ enum GC { ++ serial, parallel, g1, shenandoah, z; ++ String getSwitchName() { ++ String s = name(); ++ return "-XX:+Use" + s.substring(0, 1).toUpperCase() + s.substring(1) + "GC"; ++ } ++ boolean isZ() { return this == GC.z; } ++ boolean isSerial() { return this == GC.serial; } ++ boolean isParallel() { return this == GC.parallel; } ++ boolean isG1() { return this == GC.g1; } ++ boolean isShenandoah() { return this == GC.shenandoah; } ++ } ++ ++ static private boolean usesNativeTrimmer(GC gc) { ++ return gc.isG1() || gc.isParallel() || gc.isZ(); ++ } ++ ++ static private final OutputAnalyzer runTestWithOptions(String[] extraOptions, String[] testArgs) throws Exception { ++ ++ List allOptions = new ArrayList(); ++ allOptions.add("-XX:+UnlockExperimentalVMOptions"); ++ allOptions.addAll(Arrays.asList(extraOptions)); ++ allOptions.add("-Xmx128m"); ++ allOptions.add("-Xms128m"); // Stabilize RSS ++ allOptions.add("-XX:+AlwaysPreTouch"); // Stabilize RSS ++ ++ allOptions.add(TestTrimNative.class.getName()); ++ allOptions.add("RUN"); ++ allOptions.addAll(Arrays.asList(testArgs)); ++ ProcessBuilder pb = ProcessTools.createJavaProcessBuilder(allOptions.toArray(new String[0])); ++ OutputAnalyzer output = new OutputAnalyzer(pb.start()); ++ output.shouldHaveExitValue(0); ++ return output; ++ ++ } ++ ++ /** ++ * Given JVM output, look for a log line that describes a successful negative trim in the megabyte range ++ * like this: ++ * "[2.053s][debug][gc,trim] Trim native heap (retain size: 5120K): RSS+Swap: 271M->223M (-49112K), 2.834ms" ++ * (Note: we use the "properXXX" print routines, therefore units can differ) ++ * Check that the sum of all trim log lines comes to a total RSS reduction in the MB range ++ * @param output ++ * @param minExpected min number of trim lines expected in UL log ++ * @param maxExpected max number of trim lines expected in UL log ++ */ ++ private final static void parseOutputAndLookForNegativeTrim(OutputAnalyzer output, int minExpected, int maxExpected) { ++ output.reportDiagnosticSummary(); ++ List lines = output.asLines(); ++ Pattern pat = Pattern.compile(".*\\[gc,trim\\] Trim native heap.*RSS\\+Swap: (\\d+)([KMB])->(\\d+)([KMB]).*"); ++ int numTrimsFound = 0; ++ long rssReductionTotal = 0; ++ for (String line : lines) { ++ Matcher mat = pat.matcher(line); ++ if (mat.matches()) { ++ long rss1 = Long.parseLong(mat.group(1)) * Unit.valueOf(mat.group(2)).size; ++ long rss2 = Long.parseLong(mat.group(3)) * Unit.valueOf(mat.group(4)).size; ++ System.out.println("Parsed Trim Line. rss1: " + rss1 + " rss2: " + rss2); ++ if (rss1 > rss2) { ++ rssReductionTotal += (rss1 - rss2); ++ } ++ numTrimsFound ++; ++ } ++ if (numTrimsFound > maxExpected) { ++ throw new RuntimeException("Abnormal high number of trim attempts found (more than " + maxExpected + ++ "). Does the interval setting not work?"); ++ } ++ } ++ if (numTrimsFound < minExpected) { ++ throw new RuntimeException("We found fewer trim lines in UL log than expected (expected " + minExpected + ++ ", found " + numTrimsFound + "."); ++ } ++ // This is very fuzzy. We malloced X, free'd X, trimmed, measured the combined effect of all reductions. ++ // This does not take into effect mallocs or frees that may happen concurrently. But we expect to see *some* ++ // reduction somewhere. Test with a fudge factor. ++ float fudge = 0.8f; ++ long expectedMinimalReduction = (long) (totalAllocationsSize * fudge); ++ if (rssReductionTotal < expectedMinimalReduction) { ++ throw new RuntimeException("We did not see the expected RSS reduction in the UL log. Expected (with fudge)" + ++ " to see at least a combined reduction of " + expectedMinimalReduction + "."); ++ } ++ } ++ ++ // Test that GCTrimNativeHeap=1 causes a trim-native on full gc ++ static private final void testWithFullGC(GC gc) throws Exception { ++ System.out.println("testWithFullGC"); ++ int sleeptime_secs = 2; ++ OutputAnalyzer output = runTestWithOptions ( ++ new String[] { gc.getSwitchName(), "-XX:+GCTrimNativeHeap" }, ++ new String[] { "true" /* full gc */, String.valueOf(sleeptime_secs * 1000) /* ms after peak */ } ++ ); ++ // With default interval time of 30 seconds, auto trimming should never kick in, so the only ++ // log line we expect to see is the one from the full-gc induced trim. ++ parseOutputAndLookForNegativeTrim(output, 1, 1); ++ // For GCs that use the NativeTrimmer, we want to see the NativeTrimmer paused during the GC, as well as ++ // started and shut down properly. ++ if (usesNativeTrimmer(gc)) { ++ output.shouldContain("NativeTrimmer started"); ++ output.shouldContain("NativeTrimmer paused"); ++ output.shouldContain("NativeTrimmer unpaused"); ++ output.shouldContain("NativeTrimmer stopped"); ++ } else { ++ output.shouldNotContain("NativeTrimmer"); ++ } ++ } ++ ++ // Test that GCTrimNativeHeap=1 causes a trim-native automatically, without GC (for now, shenandoah only) ++ static private final void testAuto(GC gc) throws Exception { ++ System.out.println("testAuto"); ++ long t1 = System.currentTimeMillis(); ++ int sleeptime_secs = 4; ++ OutputAnalyzer output = runTestWithOptions ( ++ new String[] { gc.getSwitchName(), "-XX:+GCTrimNativeHeap", "-XX:GCTrimNativeHeapInterval=1" }, ++ new String[] { "false" /* full gc */, String.valueOf(sleeptime_secs * 1000) /* ms after peak */ } ++ ); ++ long t2 = System.currentTimeMillis(); ++ int runtime_s = (int)((t2 - t1) / 1000); ++ // With an interval time of 1 second and a runtime of 6..x seconds we expect to see x log lines (+- fudge factor). ++ parseOutputAndLookForNegativeTrim(output, runtime_s - 4, runtime_s + 2); ++ } ++ ++ // Test that trim-native correctly honors interval ++ static private final void testAutoWithHighInterval(GC gc) throws Exception { ++ // We pass a very high interval. This should disable the feature for this short-lived test, we should see no trim ++ System.out.println("testAutoWithHighInterval"); ++ OutputAnalyzer output = runTestWithOptions ( ++ new String[] { gc.getSwitchName(), "-XX:+GCTrimNativeHeap", "-XX:GCTrimNativeHeapInterval=30" }, ++ new String[] { "false" /* full gc */, "6000" /* ms after peak */ } ++ ); ++ output.shouldNotContain("Trim native heap"); ++ } ++ ++ // Test that trim-native correctly honors interval ++ static private final void testAutoWithZeroInterval(GC gc) throws Exception { ++ // We pass a very high interval. This should disable the feature for this short-lived test, we should see no trim ++ System.out.println("testAutoWithHighInterval"); ++ OutputAnalyzer output = runTestWithOptions ( ++ new String[] { gc.getSwitchName(), "-XX:+GCTrimNativeHeap", "-XX:GCTrimNativeHeapInterval=0" }, ++ new String[] { "false" /* full gc */, "6000" /* ms after peak */ } ++ ); ++ output.shouldNotContain("Trim native heap"); ++ } ++ ++ // Test that trim-native gets disabled on platforms that don't support it. ++ static private final void testOffOnNonCompliantPlatforms() throws Exception { ++ // Logic is shared, so no need to test with every GC. Just use the default GC. ++ System.out.println("testOffOnNonCompliantPlatforms"); ++ OutputAnalyzer output = runTestWithOptions ( ++ new String[] { "-XX:+GCTrimNativeHeap" }, ++ new String[] { "true" /* full gc */, "2000" /* ms after peak */ } ++ ); ++ output.shouldContain("GCTrimNativeHeap disabled"); ++ output.shouldNotContain("Trim native heap"); ++ } ++ ++ // Test that GCTrimNativeHeap=0 switches trim-native off ++ static private final void testOffExplicit() throws Exception { ++ // Logic is shared, so no need to test with every GC. Just use the default GC. ++ System.out.println("testOffExplicit"); ++ OutputAnalyzer output = runTestWithOptions ( ++ new String[] { "-XX:-GCTrimNativeHeap" }, ++ new String[] { "true" /* full gc */, "2000" /* ms after peak */ } ++ ); ++ output.shouldNotContain("Trim native heap"); ++ } ++ ++ // Test that trim-native is disabled by default ++ static private final void testOffByDefault() throws Exception { ++ // Logic is shared, so no need to test with every GC. Just use the default GC. ++ System.out.println("testOffByDefault"); ++ OutputAnalyzer output = runTestWithOptions ( ++ new String[] { }, ++ new String[] { "true" /* full gc */, "2000" /* ms after peak */ } ++ ); ++ output.shouldNotContain("Trim native heap"); ++ } ++ ++ public static void main(String[] args) throws Exception { ++ ++ if (args.length == 0) { ++ throw new RuntimeException("Argument error"); ++ } ++ ++ if (args[0].equals("RUN")) { ++ boolean doFullGC = Boolean.parseBoolean(args[1]); ++ ++ System.out.println("Will spike now..."); ++ Field field = Unsafe.class.getDeclaredField("theUnsafe"); ++ field.setAccessible(true); ++ Unsafe unsafe = (Unsafe) field.get(null); ++ for (int i = 0; i < numAllocations; i++) { ++ ptrs[i] = unsafe.allocateMemory(szAllocations); ++ unsafe.putByte(ptrs[i], (byte)0); ++ unsafe.putByte(ptrs[i] + szAllocations / 2, (byte)0); ++ } ++ for (int i = 0; i < numAllocations; i++) { ++ unsafe.freeMemory(ptrs[i]); ++ } ++ System.out.println("Done spiking."); ++ ++ if (doFullGC) { ++ System.out.println("GC..."); ++ System.gc(); ++ } ++ ++ // give GC time to react ++ int time = Integer.parseInt(args[2]); ++ System.out.println("Sleeping..."); ++ Thread.sleep(time); ++ System.out.println("Done."); ++ ++ return; ++ ++ } else if (args[0].equals("test-fullgc")) { ++ final GC gc = GC.valueOf(args[1]); ++ testWithFullGC(gc); ++ } else if (args[0].equals("test-auto")) { ++ final GC gc = GC.valueOf(args[1]); ++ testAuto(gc); ++ } else if (args[0].equals("test-auto-high-interval")) { ++ final GC gc = GC.valueOf(args[1]); ++ testAutoWithHighInterval(gc); ++ } else if (args[0].equals("test-auto-zero-interval")) { ++ final GC gc = GC.valueOf(args[1]); ++ testAutoWithZeroInterval(gc); ++ } else if (args[0].equals("test-off-explicit")) { ++ testOffExplicit(); ++ } else if (args[0].equals("test-off-by-default")) { ++ testOffByDefault(); ++ } else if (args[0].equals("test-off-on-other-platforms")) { ++ testOffOnNonCompliantPlatforms(); ++ } else { ++ throw new RuntimeException("Invalid test " + args[0]); ++ } ++ ++ } ++ ++} +\ No newline at end of file +diff --git a/hotspot/test/serviceability/dcmd/TrimLibcHeapTest.java b/hotspot/test/serviceability/dcmd/TrimLibcHeapTest.java +index 0fe8e35..131fa4c 100644 +--- a/hotspot/test/serviceability/dcmd/TrimLibcHeapTest.java ++++ b/hotspot/test/serviceability/dcmd/TrimLibcHeapTest.java +@@ -29,7 +29,7 @@ import com.oracle.java.testlibrary.*; + * @test + * @summary Test of diagnostic command VM.trim_libc_heap + * @library /testlibrary +- * @requires os.family == "linux" ++ * @requires os.family=="linux" + * @modules java.base/jdk.internal.misc + * java.compiler + * java.management +@@ -40,10 +40,7 @@ public class TrimLibcHeapTest { + public void run(CommandExecutor executor) { + OutputAnalyzer output = executor.execute("System.trim_native_heap"); + output.reportDiagnosticSummary(); +- output.shouldMatch("(Done|Not available)"); // Not available could happen on Linux + non-glibc (eg. muslc) +- if (output.firstMatch("Done") != null) { +- output.shouldMatch("(Virtual size before|RSS before|Swap before|No details available)"); +- } ++ output.shouldMatch(".*Trim native heap: RSS\\+Swap: \\d+[BKM]->\\d+[BKM].*"); + } + + @Test +-- +1.8.3.1 diff --git a/8294357-tz-Update-Timezone-Data-to-2022d.patch b/8294357-tz-Update-Timezone-Data-to-2022d.patch new file mode 100644 index 0000000000000000000000000000000000000000..2e10ad41cd93d36f5e14464c36bbc4cbcb6cce88 --- /dev/null +++ b/8294357-tz-Update-Timezone-Data-to-2022d.patch @@ -0,0 +1,526 @@ +From 78c19b03f00f61f673311cf3c70a21ce25933eec Mon Sep 17 00:00:00 2001 +From: eapen +Date: Wed, 30 Nov 2022 11:39:58 +0000 +Subject: [PATCH 07/33] I68TO2: 8294357: (tz) Update Timezone Data to 2022d +--- + jdk/make/data/tzdata/VERSION | 2 +- + jdk/make/data/tzdata/asia | 30 +++++++---- + jdk/make/data/tzdata/backward | 2 + + jdk/make/data/tzdata/europe | 58 ++++------------------ + jdk/make/data/tzdata/southamerica | 10 +++- + jdk/make/data/tzdata/zone.tab | 2 - + .../classes/sun/util/calendar/ZoneInfoFile.java | 9 +--- + jdk/test/java/util/TimeZone/TimeZoneData/VERSION | 2 +- + .../java/util/TimeZone/TimeZoneData/aliases.txt | 2 + + .../util/TimeZone/TimeZoneData/displaynames.txt | 2 - + jdk/test/sun/util/calendar/zi/TestZoneInfo310.java | 15 ++++-- + jdk/test/sun/util/calendar/zi/tzdata/VERSION | 2 +- + jdk/test/sun/util/calendar/zi/tzdata/asia | 30 +++++++---- + jdk/test/sun/util/calendar/zi/tzdata/backward | 2 + + jdk/test/sun/util/calendar/zi/tzdata/europe | 58 ++++------------------ + jdk/test/sun/util/calendar/zi/tzdata/southamerica | 10 +++- + jdk/test/sun/util/calendar/zi/tzdata/zone.tab | 2 - + 17 files changed, 99 insertions(+), 139 deletions(-) + +diff --git a/jdk/make/data/tzdata/VERSION b/jdk/make/data/tzdata/VERSION +index decb871..889d0e6 100644 +--- a/jdk/make/data/tzdata/VERSION ++++ b/jdk/make/data/tzdata/VERSION +@@ -21,4 +21,4 @@ + # or visit www.oracle.com if you need additional information or have any + # questions. + # +-tzdata2022c ++tzdata2022d +diff --git a/jdk/make/data/tzdata/asia b/jdk/make/data/tzdata/asia +index 6cb6d2c..1dc7d34 100644 +--- a/jdk/make/data/tzdata/asia ++++ b/jdk/make/data/tzdata/asia +@@ -3398,10 +3398,6 @@ Zone Asia/Karachi 4:28:12 - LMT 1907 + # The winter time in 2015 started on October 23 at 01:00. + # https://wafa.ps/ar_page.aspx?id=CgpCdYa670694628582aCgpCdY + # http://www.palestinecabinet.gov.ps/portal/meeting/details/27583 +-# +-# From Paul Eggert (2019-04-10): +-# For now, guess spring-ahead transitions are at 00:00 on the Saturday +-# preceding March's last Sunday (i.e., Sat>=24). + + # From P Chan (2021-10-18): + # http://wafa.ps/Pages/Details/34701 +@@ -3418,6 +3414,18 @@ Zone Asia/Karachi 4:28:12 - LMT 1907 + # From Heba Hamad (2022-03-10): + # summer time will begin in Palestine from Sunday 03-27-2022, 00:00 AM. + ++# From Heba Hamad (2022-08-30): ++# winter time will begin in Palestine from Saturday 10-29, 02:00 AM by ++# 60 minutes backwards. Also the state of Palestine adopted the summer ++# and winter time for the years: 2023,2024,2025,2026 ... ++# https://mm.icann.org/pipermail/tz/attachments/20220830/9f024566/Time-0001.pdf ++# (2022-08-31): ... the Saturday before the last Sunday in March and October ++# at 2:00 AM ,for the years from 2023 to 2026. ++# (2022-09-05): https://mtit.pna.ps/Site/New/1453 ++# ++# From Paul Eggert (2022-08-31): ++# For now, assume that this rule will also be used after 2026. ++ + # Rule NAME FROM TO - IN ON AT SAVE LETTER/S + Rule EgyptAsia 1957 only - May 10 0:00 1:00 S + Rule EgyptAsia 1957 1958 - Oct 1 0:00 0 - +@@ -3448,14 +3456,16 @@ Rule Palestine 2013 only - Sep 27 0:00 0 - + Rule Palestine 2014 only - Oct 24 0:00 0 - + Rule Palestine 2015 only - Mar 28 0:00 1:00 S + Rule Palestine 2015 only - Oct 23 1:00 0 - +-Rule Palestine 2016 2018 - Mar Sat>=24 1:00 1:00 S +-Rule Palestine 2016 2018 - Oct Sat>=24 1:00 0 - ++Rule Palestine 2016 2018 - Mar Sat<=30 1:00 1:00 S ++Rule Palestine 2016 2018 - Oct Sat<=30 1:00 0 - + Rule Palestine 2019 only - Mar 29 0:00 1:00 S +-Rule Palestine 2019 only - Oct Sat>=24 0:00 0 - +-Rule Palestine 2020 2021 - Mar Sat>=24 0:00 1:00 S ++Rule Palestine 2019 only - Oct Sat<=30 0:00 0 - ++Rule Palestine 2020 2021 - Mar Sat<=30 0:00 1:00 S + Rule Palestine 2020 only - Oct 24 1:00 0 - +-Rule Palestine 2021 max - Oct Fri>=23 1:00 0 - +-Rule Palestine 2022 max - Mar Sun>=25 0:00 1:00 S ++Rule Palestine 2021 only - Oct 29 1:00 0 - ++Rule Palestine 2022 only - Mar 27 0:00 1:00 S ++Rule Palestine 2022 max - Oct Sat<=30 2:00 0 - ++Rule Palestine 2023 max - Mar Sat<=30 2:00 1:00 S + + # Zone NAME STDOFF RULES FORMAT [UNTIL] + Zone Asia/Gaza 2:17:52 - LMT 1900 Oct +diff --git a/jdk/make/data/tzdata/backward b/jdk/make/data/tzdata/backward +index d4a29e8..7765d99 100644 +--- a/jdk/make/data/tzdata/backward ++++ b/jdk/make/data/tzdata/backward +@@ -113,6 +113,8 @@ Link Etc/UTC Etc/UCT + Link Europe/London Europe/Belfast + Link Europe/Kyiv Europe/Kiev + Link Europe/Chisinau Europe/Tiraspol ++Link Europe/Kyiv Europe/Uzhgorod ++Link Europe/Kyiv Europe/Zaporozhye + Link Europe/London GB + Link Europe/London GB-Eire + Link Etc/GMT GMT+0 +diff --git a/jdk/make/data/tzdata/europe b/jdk/make/data/tzdata/europe +index f7eb7a3..9e0a538 100644 +--- a/jdk/make/data/tzdata/europe ++++ b/jdk/make/data/tzdata/europe +@@ -2638,10 +2638,14 @@ Zone Europe/Simferopol 2:16:24 - LMT 1880 + # From Alexander Krivenyshev (2014-03-17): + # time change at 2:00 (2am) on March 30, 2014 + # https://vz.ru/news/2014/3/17/677464.html +-# From Paul Eggert (2014-03-30): +-# Simferopol and Sevastopol reportedly changed their central town clocks +-# late the previous day, but this appears to have been ceremonial +-# and the discrepancies are small enough to not worry about. ++# From Tim Parenti (2022-07-01), per Paul Eggert (2014-03-30): ++# The clocks at the railway station in Simferopol were put forward from 22:00 ++# to 24:00 the previous day in a "symbolic ceremony"; however, per ++# contemporaneous news reports, "ordinary Crimeans [made] the daylight savings ++# time switch at 2am" on Sunday. ++# https://www.business-standard.com/article/pti-stories/crimea-to-set-clocks-to-russia-time-114033000014_1.html ++# https://www.reuters.com/article/us-ukraine-crisis-crimea-time/crimea-switches-to-moscow-time-amid-incorporation-frenzy-idUKBREA2S0LT20140329 ++# https://www.bbc.com/news/av/world-europe-26806583 + 2:00 EU EE%sT 2014 Mar 30 2:00 + 4:00 - MSK 2014 Oct 26 2:00s + 3:00 - MSK +@@ -3774,8 +3778,8 @@ Link Europe/Istanbul Asia/Istanbul # Istanbul is in both continents. + # US colleague David Cochrane) are still trying to get more + # information upon these local deviations from Kiev rules. + # +-# From Paul Eggert (2022-02-08): +-# For now, assume that Ukraine's other three zones followed the same rules, ++# From Paul Eggert (2022-08-27): ++# For now, assume that Ukraine's zones all followed the same rules, + # except that Crimea switched to Moscow time in 1994 as described elsewhere. + + # From Igor Karpov, who works for the Ukrainian Ministry of Justice, +@@ -3845,21 +3849,7 @@ Link Europe/Istanbul Asia/Istanbul # Istanbul is in both continents. + # * Ukrainian Government's Resolution of 20.03.1992, No. 139. + # http://www.uazakon.com/documents/date_8u/pg_grcasa.htm + +-# From Paul Eggert (2022-04-12): +-# As is usual in tzdb, Ukrainian zones use the most common English spellings. +-# In particular, tzdb's name Europe/Kyiv uses the most common spelling in +-# English for Ukraine's capital. Although tzdb's former name was Europe/Kiev, +-# "Kyiv" is now more common due to widespread reporting of the current conflict. +-# Conversely, tzdb continues to use the names Europe/Uzhgorod and +-# Europe/Zaporozhye; this is similar to tzdb's use of Europe/Prague, which is +-# certainly wrong as a transliteration of the Czech "Praha". +-# English-language spelling of Ukrainian names is in flux, and +-# some day "Uzhhorod" or "Zaporizhzhia" may become substantially more +-# common in English; in the meantime, do not change these +-# English spellings as that means less disruption for our users. +- + # Zone NAME STDOFF RULES FORMAT [UNTIL] +-# This represents most of Ukraine. See above for the spelling of "Kyiv". + Zone Europe/Kyiv 2:02:04 - LMT 1880 + 2:02:04 - KMT 1924 May 2 # Kyiv Mean Time + 2:00 - EET 1930 Jun 21 +@@ -3869,34 +3859,6 @@ Zone Europe/Kyiv 2:02:04 - LMT 1880 + 2:00 1:00 EEST 1991 Sep 29 3:00 + 2:00 C-Eur EE%sT 1996 May 13 + 2:00 EU EE%sT +-# Transcarpathia used CET 1990/1991. +-# "Uzhhorod" is the transliteration of the Rusyn/Ukrainian pronunciation, but +-# "Uzhgorod" is more common in English. +-Zone Europe/Uzhgorod 1:29:12 - LMT 1890 Oct +- 1:00 - CET 1940 +- 1:00 C-Eur CE%sT 1944 Oct +- 1:00 1:00 CEST 1944 Oct 26 +- 1:00 - CET 1945 Jun 29 +- 3:00 Russia MSK/MSD 1990 +- 3:00 - MSK 1990 Jul 1 2:00 +- 1:00 - CET 1991 Mar 31 3:00 +- 2:00 - EET 1992 Mar 20 +- 2:00 C-Eur EE%sT 1996 May 13 +- 2:00 EU EE%sT +-# Zaporozh'ye and eastern Lugansk oblasts observed DST 1990/1991. +-# "Zaporizhzhia" is the transliteration of the Ukrainian name, but +-# "Zaporozh'ye" is more common in English. Use the common English +-# spelling, except omit the apostrophe as it is not allowed in +-# portable Posix file names. +-Zone Europe/Zaporozhye 2:20:40 - LMT 1880 +- 2:20 - +0220 1924 May 2 +- 2:00 - EET 1930 Jun 21 +- 3:00 - MSK 1941 Aug 25 +- 1:00 C-Eur CE%sT 1943 Oct 25 +- 3:00 Russia MSK/MSD 1991 Mar 31 2:00 +- 2:00 E-Eur EE%sT 1992 Mar 20 +- 2:00 C-Eur EE%sT 1996 May 13 +- 2:00 EU EE%sT + + # Vatican City + # See Europe/Rome. +diff --git a/jdk/make/data/tzdata/southamerica b/jdk/make/data/tzdata/southamerica +index 13ec081..3c0e0e2 100644 +--- a/jdk/make/data/tzdata/southamerica ++++ b/jdk/make/data/tzdata/southamerica +@@ -1332,8 +1332,14 @@ Zone America/Rio_Branco -4:31:12 - LMT 1914 + # for America/Santiago will start on midnight of September 11th; + # and will end on April 1st, 2023. Magallanes region (America/Punta_Arenas) + # will keep UTC -3 "indefinitely"... This is because on September 4th +-# we will have a voting whether to approve a new Constitution.... +-# https://www.interior.gob.cl/noticias/2022/08/09/comunicado-el-proximo-sabado-10-de-septiembre-los-relojes-se-deben-adelantar-una-hora/ ++# we will have a voting whether to approve a new Constitution. ++# ++# From Eduardo Romero Urra (2022-08-17): ++# https://www.diariooficial.interior.gob.cl/publicaciones/2022/08/13/43327/01/2172567.pdf ++# ++# From Paul Eggert (2022-08-17): ++# Although the presidential decree stops at fall 2026, assume that ++# similar DST rules will continue thereafter. + + # Rule NAME FROM TO - IN ON AT SAVE LETTER/S + Rule Chile 1927 1931 - Sep 1 0:00 1:00 - +diff --git a/jdk/make/data/tzdata/zone.tab b/jdk/make/data/tzdata/zone.tab +index 51b65fa..ee02519 100644 +--- a/jdk/make/data/tzdata/zone.tab ++++ b/jdk/make/data/tzdata/zone.tab +@@ -424,8 +424,6 @@ TV -0831+17913 Pacific/Funafuti + TW +2503+12130 Asia/Taipei + TZ -0648+03917 Africa/Dar_es_Salaam + UA +5026+03031 Europe/Kyiv Ukraine (most areas) +-UA +4837+02218 Europe/Uzhgorod Transcarpathia +-UA +4750+03510 Europe/Zaporozhye Zaporozhye and east Lugansk + UG +0019+03225 Africa/Kampala + UM +2813-17722 Pacific/Midway Midway Islands + UM +1917+16637 Pacific/Wake Wake Island +diff --git a/jdk/src/share/classes/sun/util/calendar/ZoneInfoFile.java b/jdk/src/share/classes/sun/util/calendar/ZoneInfoFile.java +index 43bddd5..4b84cda 100644 +--- a/jdk/src/share/classes/sun/util/calendar/ZoneInfoFile.java ++++ b/jdk/src/share/classes/sun/util/calendar/ZoneInfoFile.java +@@ -573,12 +573,8 @@ public final class ZoneInfoFile { + // we can then pass in the dom = -1, dow > 0 into ZoneInfo + // + // hacking, assume the >=24 is the result of ZRB optimization for +- // "last", it works for now. From tzdata2020d this hacking +- // will not work for Asia/Gaza and Asia/Hebron which follow +- // Palestine DST rules. +- if (dom < 0 || dom >= 24 && +- !(zoneId.equals("Asia/Gaza") || +- zoneId.equals("Asia/Hebron"))) { ++ // "last", it works for now. ++ if (dom < 0 || dom >= 24) { + params[1] = -1; + params[2] = toCalendarDOW[dow]; + } else { +@@ -600,7 +596,6 @@ public final class ZoneInfoFile { + params[7] = 0; + } else { + // hacking: see comment above +- // No need of hacking for Asia/Gaza and Asia/Hebron from tz2021e + if (dom < 0 || dom >= 24) { + params[6] = -1; + params[7] = toCalendarDOW[dow]; +diff --git a/jdk/test/java/util/TimeZone/TimeZoneData/VERSION b/jdk/test/java/util/TimeZone/TimeZoneData/VERSION +index c32bee3..7147016 100644 +--- a/jdk/test/java/util/TimeZone/TimeZoneData/VERSION ++++ b/jdk/test/java/util/TimeZone/TimeZoneData/VERSION +@@ -1 +1 @@ +-tzdata2022c ++tzdata2022d +diff --git a/jdk/test/java/util/TimeZone/TimeZoneData/aliases.txt b/jdk/test/java/util/TimeZone/TimeZoneData/aliases.txt +index a5e6428..e3ce742 100644 +--- a/jdk/test/java/util/TimeZone/TimeZoneData/aliases.txt ++++ b/jdk/test/java/util/TimeZone/TimeZoneData/aliases.txt +@@ -183,6 +183,8 @@ Link Etc/UTC Etc/UCT + Link Europe/London Europe/Belfast + Link Europe/Kyiv Europe/Kiev + Link Europe/Chisinau Europe/Tiraspol ++Link Europe/Kyiv Europe/Uzhgorod ++Link Europe/Kyiv Europe/Zaporozhye + Link Europe/London GB + Link Europe/London GB-Eire + Link Etc/GMT GMT+0 +diff --git a/jdk/test/java/util/TimeZone/TimeZoneData/displaynames.txt b/jdk/test/java/util/TimeZone/TimeZoneData/displaynames.txt +index fc14853..b382395 100644 +--- a/jdk/test/java/util/TimeZone/TimeZoneData/displaynames.txt ++++ b/jdk/test/java/util/TimeZone/TimeZoneData/displaynames.txt +@@ -163,11 +163,9 @@ Europe/Simferopol MSK + Europe/Sofia EET EEST + Europe/Tallinn EET EEST + Europe/Tirane CET CEST +-Europe/Uzhgorod EET EEST + Europe/Vienna CET CEST + Europe/Vilnius EET EEST + Europe/Warsaw CET CEST +-Europe/Zaporozhye EET EEST + Europe/Zurich CET CEST + HST HST + MET MET MEST +diff --git a/jdk/test/sun/util/calendar/zi/TestZoneInfo310.java b/jdk/test/sun/util/calendar/zi/TestZoneInfo310.java +index 3aad69f..c682531 100644 +--- a/jdk/test/sun/util/calendar/zi/TestZoneInfo310.java ++++ b/jdk/test/sun/util/calendar/zi/TestZoneInfo310.java +@@ -173,10 +173,19 @@ public class TestZoneInfo310 { + * Temporary ignoring the failing TimeZones which are having zone + * rules defined till year 2037 and/or above and have negative DST + * save time in IANA tzdata. This bug is tracked via JDK-8223388. ++ * ++ * Tehran/Iran rule has rules beyond 2037, in which javazic assumes ++ * to be the last year. Thus javazic's rule is based on year 2037 ++ * (Mar 20th/Sep 20th are the cutover dates), while the real rule ++ * has year 2087 where Mar 21st/Sep 21st are the cutover dates. + */ +- if (zid.equals("Africa/Casablanca") || zid.equals("Africa/El_Aaiun") +- || zid.equals("Asia/Tehran") || zid.equals("Iran")) { +- continue; ++ if (zid.equals("Africa/Casablanca") || // uses "Morocco" rule ++ zid.equals("Africa/El_Aaiun") || // uses "Morocco" rule ++ zid.equals("Asia/Tehran") || // last rule mismatch ++ zid.equals("Asia/Gaza") || // uses "Palestine" rule ++ zid.equals("Asia/Hebron") || // uses "Palestine" rule ++ zid.equals("Iran")) { // last rule mismatch ++ continue; + } + if (! zi.equalsTo(ziOLD)) { + System.out.println(zi.diffsTo(ziOLD)); +diff --git a/jdk/test/sun/util/calendar/zi/tzdata/VERSION b/jdk/test/sun/util/calendar/zi/tzdata/VERSION +index decb871..889d0e6 100644 +--- a/jdk/test/sun/util/calendar/zi/tzdata/VERSION ++++ b/jdk/test/sun/util/calendar/zi/tzdata/VERSION +@@ -21,4 +21,4 @@ + # or visit www.oracle.com if you need additional information or have any + # questions. + # +-tzdata2022c ++tzdata2022d +diff --git a/jdk/test/sun/util/calendar/zi/tzdata/asia b/jdk/test/sun/util/calendar/zi/tzdata/asia +index 6cb6d2c..1dc7d34 100644 +--- a/jdk/test/sun/util/calendar/zi/tzdata/asia ++++ b/jdk/test/sun/util/calendar/zi/tzdata/asia +@@ -3398,10 +3398,6 @@ Zone Asia/Karachi 4:28:12 - LMT 1907 + # The winter time in 2015 started on October 23 at 01:00. + # https://wafa.ps/ar_page.aspx?id=CgpCdYa670694628582aCgpCdY + # http://www.palestinecabinet.gov.ps/portal/meeting/details/27583 +-# +-# From Paul Eggert (2019-04-10): +-# For now, guess spring-ahead transitions are at 00:00 on the Saturday +-# preceding March's last Sunday (i.e., Sat>=24). + + # From P Chan (2021-10-18): + # http://wafa.ps/Pages/Details/34701 +@@ -3418,6 +3414,18 @@ Zone Asia/Karachi 4:28:12 - LMT 1907 + # From Heba Hamad (2022-03-10): + # summer time will begin in Palestine from Sunday 03-27-2022, 00:00 AM. + ++# From Heba Hamad (2022-08-30): ++# winter time will begin in Palestine from Saturday 10-29, 02:00 AM by ++# 60 minutes backwards. Also the state of Palestine adopted the summer ++# and winter time for the years: 2023,2024,2025,2026 ... ++# https://mm.icann.org/pipermail/tz/attachments/20220830/9f024566/Time-0001.pdf ++# (2022-08-31): ... the Saturday before the last Sunday in March and October ++# at 2:00 AM ,for the years from 2023 to 2026. ++# (2022-09-05): https://mtit.pna.ps/Site/New/1453 ++# ++# From Paul Eggert (2022-08-31): ++# For now, assume that this rule will also be used after 2026. ++ + # Rule NAME FROM TO - IN ON AT SAVE LETTER/S + Rule EgyptAsia 1957 only - May 10 0:00 1:00 S + Rule EgyptAsia 1957 1958 - Oct 1 0:00 0 - +@@ -3448,14 +3456,16 @@ Rule Palestine 2013 only - Sep 27 0:00 0 - + Rule Palestine 2014 only - Oct 24 0:00 0 - + Rule Palestine 2015 only - Mar 28 0:00 1:00 S + Rule Palestine 2015 only - Oct 23 1:00 0 - +-Rule Palestine 2016 2018 - Mar Sat>=24 1:00 1:00 S +-Rule Palestine 2016 2018 - Oct Sat>=24 1:00 0 - ++Rule Palestine 2016 2018 - Mar Sat<=30 1:00 1:00 S ++Rule Palestine 2016 2018 - Oct Sat<=30 1:00 0 - + Rule Palestine 2019 only - Mar 29 0:00 1:00 S +-Rule Palestine 2019 only - Oct Sat>=24 0:00 0 - +-Rule Palestine 2020 2021 - Mar Sat>=24 0:00 1:00 S ++Rule Palestine 2019 only - Oct Sat<=30 0:00 0 - ++Rule Palestine 2020 2021 - Mar Sat<=30 0:00 1:00 S + Rule Palestine 2020 only - Oct 24 1:00 0 - +-Rule Palestine 2021 max - Oct Fri>=23 1:00 0 - +-Rule Palestine 2022 max - Mar Sun>=25 0:00 1:00 S ++Rule Palestine 2021 only - Oct 29 1:00 0 - ++Rule Palestine 2022 only - Mar 27 0:00 1:00 S ++Rule Palestine 2022 max - Oct Sat<=30 2:00 0 - ++Rule Palestine 2023 max - Mar Sat<=30 2:00 1:00 S + + # Zone NAME STDOFF RULES FORMAT [UNTIL] + Zone Asia/Gaza 2:17:52 - LMT 1900 Oct +diff --git a/jdk/test/sun/util/calendar/zi/tzdata/backward b/jdk/test/sun/util/calendar/zi/tzdata/backward +index d4a29e8..7765d99 100644 +--- a/jdk/test/sun/util/calendar/zi/tzdata/backward ++++ b/jdk/test/sun/util/calendar/zi/tzdata/backward +@@ -113,6 +113,8 @@ Link Etc/UTC Etc/UCT + Link Europe/London Europe/Belfast + Link Europe/Kyiv Europe/Kiev + Link Europe/Chisinau Europe/Tiraspol ++Link Europe/Kyiv Europe/Uzhgorod ++Link Europe/Kyiv Europe/Zaporozhye + Link Europe/London GB + Link Europe/London GB-Eire + Link Etc/GMT GMT+0 +diff --git a/jdk/test/sun/util/calendar/zi/tzdata/europe b/jdk/test/sun/util/calendar/zi/tzdata/europe +index f7eb7a3..9e0a538 100644 +--- a/jdk/test/sun/util/calendar/zi/tzdata/europe ++++ b/jdk/test/sun/util/calendar/zi/tzdata/europe +@@ -2638,10 +2638,14 @@ Zone Europe/Simferopol 2:16:24 - LMT 1880 + # From Alexander Krivenyshev (2014-03-17): + # time change at 2:00 (2am) on March 30, 2014 + # https://vz.ru/news/2014/3/17/677464.html +-# From Paul Eggert (2014-03-30): +-# Simferopol and Sevastopol reportedly changed their central town clocks +-# late the previous day, but this appears to have been ceremonial +-# and the discrepancies are small enough to not worry about. ++# From Tim Parenti (2022-07-01), per Paul Eggert (2014-03-30): ++# The clocks at the railway station in Simferopol were put forward from 22:00 ++# to 24:00 the previous day in a "symbolic ceremony"; however, per ++# contemporaneous news reports, "ordinary Crimeans [made] the daylight savings ++# time switch at 2am" on Sunday. ++# https://www.business-standard.com/article/pti-stories/crimea-to-set-clocks-to-russia-time-114033000014_1.html ++# https://www.reuters.com/article/us-ukraine-crisis-crimea-time/crimea-switches-to-moscow-time-amid-incorporation-frenzy-idUKBREA2S0LT20140329 ++# https://www.bbc.com/news/av/world-europe-26806583 + 2:00 EU EE%sT 2014 Mar 30 2:00 + 4:00 - MSK 2014 Oct 26 2:00s + 3:00 - MSK +@@ -3774,8 +3778,8 @@ Link Europe/Istanbul Asia/Istanbul # Istanbul is in both continents. + # US colleague David Cochrane) are still trying to get more + # information upon these local deviations from Kiev rules. + # +-# From Paul Eggert (2022-02-08): +-# For now, assume that Ukraine's other three zones followed the same rules, ++# From Paul Eggert (2022-08-27): ++# For now, assume that Ukraine's zones all followed the same rules, + # except that Crimea switched to Moscow time in 1994 as described elsewhere. + + # From Igor Karpov, who works for the Ukrainian Ministry of Justice, +@@ -3845,21 +3849,7 @@ Link Europe/Istanbul Asia/Istanbul # Istanbul is in both continents. + # * Ukrainian Government's Resolution of 20.03.1992, No. 139. + # http://www.uazakon.com/documents/date_8u/pg_grcasa.htm + +-# From Paul Eggert (2022-04-12): +-# As is usual in tzdb, Ukrainian zones use the most common English spellings. +-# In particular, tzdb's name Europe/Kyiv uses the most common spelling in +-# English for Ukraine's capital. Although tzdb's former name was Europe/Kiev, +-# "Kyiv" is now more common due to widespread reporting of the current conflict. +-# Conversely, tzdb continues to use the names Europe/Uzhgorod and +-# Europe/Zaporozhye; this is similar to tzdb's use of Europe/Prague, which is +-# certainly wrong as a transliteration of the Czech "Praha". +-# English-language spelling of Ukrainian names is in flux, and +-# some day "Uzhhorod" or "Zaporizhzhia" may become substantially more +-# common in English; in the meantime, do not change these +-# English spellings as that means less disruption for our users. +- + # Zone NAME STDOFF RULES FORMAT [UNTIL] +-# This represents most of Ukraine. See above for the spelling of "Kyiv". + Zone Europe/Kyiv 2:02:04 - LMT 1880 + 2:02:04 - KMT 1924 May 2 # Kyiv Mean Time + 2:00 - EET 1930 Jun 21 +@@ -3869,34 +3859,6 @@ Zone Europe/Kyiv 2:02:04 - LMT 1880 + 2:00 1:00 EEST 1991 Sep 29 3:00 + 2:00 C-Eur EE%sT 1996 May 13 + 2:00 EU EE%sT +-# Transcarpathia used CET 1990/1991. +-# "Uzhhorod" is the transliteration of the Rusyn/Ukrainian pronunciation, but +-# "Uzhgorod" is more common in English. +-Zone Europe/Uzhgorod 1:29:12 - LMT 1890 Oct +- 1:00 - CET 1940 +- 1:00 C-Eur CE%sT 1944 Oct +- 1:00 1:00 CEST 1944 Oct 26 +- 1:00 - CET 1945 Jun 29 +- 3:00 Russia MSK/MSD 1990 +- 3:00 - MSK 1990 Jul 1 2:00 +- 1:00 - CET 1991 Mar 31 3:00 +- 2:00 - EET 1992 Mar 20 +- 2:00 C-Eur EE%sT 1996 May 13 +- 2:00 EU EE%sT +-# Zaporozh'ye and eastern Lugansk oblasts observed DST 1990/1991. +-# "Zaporizhzhia" is the transliteration of the Ukrainian name, but +-# "Zaporozh'ye" is more common in English. Use the common English +-# spelling, except omit the apostrophe as it is not allowed in +-# portable Posix file names. +-Zone Europe/Zaporozhye 2:20:40 - LMT 1880 +- 2:20 - +0220 1924 May 2 +- 2:00 - EET 1930 Jun 21 +- 3:00 - MSK 1941 Aug 25 +- 1:00 C-Eur CE%sT 1943 Oct 25 +- 3:00 Russia MSK/MSD 1991 Mar 31 2:00 +- 2:00 E-Eur EE%sT 1992 Mar 20 +- 2:00 C-Eur EE%sT 1996 May 13 +- 2:00 EU EE%sT + + # Vatican City + # See Europe/Rome. +diff --git a/jdk/test/sun/util/calendar/zi/tzdata/southamerica b/jdk/test/sun/util/calendar/zi/tzdata/southamerica +index 13ec081..3c0e0e2 100644 +--- a/jdk/test/sun/util/calendar/zi/tzdata/southamerica ++++ b/jdk/test/sun/util/calendar/zi/tzdata/southamerica +@@ -1332,8 +1332,14 @@ Zone America/Rio_Branco -4:31:12 - LMT 1914 + # for America/Santiago will start on midnight of September 11th; + # and will end on April 1st, 2023. Magallanes region (America/Punta_Arenas) + # will keep UTC -3 "indefinitely"... This is because on September 4th +-# we will have a voting whether to approve a new Constitution.... +-# https://www.interior.gob.cl/noticias/2022/08/09/comunicado-el-proximo-sabado-10-de-septiembre-los-relojes-se-deben-adelantar-una-hora/ ++# we will have a voting whether to approve a new Constitution. ++# ++# From Eduardo Romero Urra (2022-08-17): ++# https://www.diariooficial.interior.gob.cl/publicaciones/2022/08/13/43327/01/2172567.pdf ++# ++# From Paul Eggert (2022-08-17): ++# Although the presidential decree stops at fall 2026, assume that ++# similar DST rules will continue thereafter. + + # Rule NAME FROM TO - IN ON AT SAVE LETTER/S + Rule Chile 1927 1931 - Sep 1 0:00 1:00 - +diff --git a/jdk/test/sun/util/calendar/zi/tzdata/zone.tab b/jdk/test/sun/util/calendar/zi/tzdata/zone.tab +index 51b65fa..ee02519 100644 +--- a/jdk/test/sun/util/calendar/zi/tzdata/zone.tab ++++ b/jdk/test/sun/util/calendar/zi/tzdata/zone.tab +@@ -424,8 +424,6 @@ TV -0831+17913 Pacific/Funafuti + TW +2503+12130 Asia/Taipei + TZ -0648+03917 Africa/Dar_es_Salaam + UA +5026+03031 Europe/Kyiv Ukraine (most areas) +-UA +4837+02218 Europe/Uzhgorod Transcarpathia +-UA +4750+03510 Europe/Zaporozhye Zaporozhye and east Lugansk + UG +0019+03225 Africa/Kampala + UM +2813-17722 Pacific/Midway Midway Islands + UM +1917+16637 Pacific/Wake Wake Island +-- +1.8.3.1 diff --git a/8296108-tz-Update-Timezone-Data-to-2022f.patch b/8296108-tz-Update-Timezone-Data-to-2022f.patch new file mode 100644 index 0000000000000000000000000000000000000000..19b7fbb716c9dc191d7af691536a3d6428858572 --- /dev/null +++ b/8296108-tz-Update-Timezone-Data-to-2022f.patch @@ -0,0 +1,4516 @@ +From b8733a1abad2eb73a9a1ae5e74be047cf7c5866a Mon Sep 17 00:00:00 2001 +From: eapen +Date: Wed, 30 Nov 2022 15:47:49 +0800 +Subject: [PATCH 09/33] I68TO2: 8296108: (tz) Update Timezone Data to 2022f +--- + jdk/make/data/tzdata/VERSION | 2 +- + jdk/make/data/tzdata/africa | 213 ++++--------- + jdk/make/data/tzdata/antarctica | 2 +- + jdk/make/data/tzdata/asia | 69 ++--- + jdk/make/data/tzdata/australasia | 58 ++-- + jdk/make/data/tzdata/backward | 291 ++++++++++++++---- + jdk/make/data/tzdata/etcetera | 17 +- + jdk/make/data/tzdata/europe | 202 +++++------- + jdk/make/data/tzdata/northamerica | 171 +++-------- + jdk/make/data/tzdata/southamerica | 15 - + jdk/make/data/tzdata/zone.tab | 3 - + .../classes/sun/util/resources/TimeZoneNames.java | 6 +- + .../sun/util/resources/de/TimeZoneNames_de.java | 6 +- + .../sun/util/resources/es/TimeZoneNames_es.java | 6 +- + .../sun/util/resources/fr/TimeZoneNames_fr.java | 6 +- + .../sun/util/resources/it/TimeZoneNames_it.java | 6 +- + .../sun/util/resources/ja/TimeZoneNames_ja.java | 6 +- + .../sun/util/resources/ko/TimeZoneNames_ko.java | 6 +- + .../sun/util/resources/pt/TimeZoneNames_pt_BR.java | 6 +- + .../sun/util/resources/sv/TimeZoneNames_sv.java | 6 +- + .../sun/util/resources/zh/TimeZoneNames_zh_CN.java | 6 +- + .../sun/util/resources/zh/TimeZoneNames_zh_TW.java | 6 +- + jdk/test/java/util/TimeZone/TimeZoneData/VERSION | 2 +- + .../java/util/TimeZone/TimeZoneData/aliases.txt | 341 +++++++++++---------- + .../util/TimeZone/TimeZoneData/displaynames.txt | 7 +- + jdk/test/sun/util/calendar/zi/tzdata/VERSION | 2 +- + jdk/test/sun/util/calendar/zi/tzdata/africa | 213 ++++--------- + jdk/test/sun/util/calendar/zi/tzdata/antarctica | 2 +- + jdk/test/sun/util/calendar/zi/tzdata/asia | 69 ++--- + jdk/test/sun/util/calendar/zi/tzdata/australasia | 58 ++-- + jdk/test/sun/util/calendar/zi/tzdata/backward | 291 ++++++++++++++---- + jdk/test/sun/util/calendar/zi/tzdata/etcetera | 17 +- + jdk/test/sun/util/calendar/zi/tzdata/europe | 202 +++++------- + jdk/test/sun/util/calendar/zi/tzdata/northamerica | 171 +++-------- + jdk/test/sun/util/calendar/zi/tzdata/southamerica | 15 - + jdk/test/sun/util/calendar/zi/tzdata/zone.tab | 3 - + 36 files changed, 1103 insertions(+), 1399 deletions(-) + +diff --git a/jdk/make/data/tzdata/VERSION b/jdk/make/data/tzdata/VERSION +index b8cb36e..b8d9ae7 100644 +--- a/jdk/make/data/tzdata/VERSION ++++ b/jdk/make/data/tzdata/VERSION +@@ -21,4 +21,4 @@ + # or visit www.oracle.com if you need additional information or have any + # questions. + # +-tzdata2022e ++tzdata2022f +diff --git a/jdk/make/data/tzdata/africa b/jdk/make/data/tzdata/africa +index e13899b..b4559cd 100644 +--- a/jdk/make/data/tzdata/africa ++++ b/jdk/make/data/tzdata/africa +@@ -120,22 +120,6 @@ Zone Africa/Algiers 0:12:12 - LMT 1891 Mar 16 + 0:00 Algeria WE%sT 1981 May + 1:00 - CET + +-# Angola +-# Benin +-# See Africa/Lagos. +- +-# Botswana +-# See Africa/Maputo. +- +-# Burkina Faso +-# See Africa/Abidjan. +- +-# Burundi +-# See Africa/Maputo. +- +-# Cameroon +-# See Africa/Lagos. +- + # Cape Verde / Cabo Verde + # + # From Paul Eggert (2018-02-16): +@@ -150,9 +134,6 @@ Zone Atlantic/Cape_Verde -1:34:04 - LMT 1912 Jan 01 2:00u # Praia + -2:00 - -02 1975 Nov 25 2:00 + -1:00 - -01 + +-# Central African Republic +-# See Africa/Lagos. +- + # Chad + # Zone NAME STDOFF RULES FORMAT [UNTIL] + Zone Africa/Ndjamena 1:00:12 - LMT 1912 # N'Djamena +@@ -160,33 +141,29 @@ Zone Africa/Ndjamena 1:00:12 - LMT 1912 # N'Djamena + 1:00 1:00 WAST 1980 Mar 8 + 1:00 - WAT + +-# Comoros +-# See Africa/Nairobi. +- +-# Democratic Republic of the Congo +-# See Africa/Lagos for the western part and Africa/Maputo for the eastern. ++# Burkina Faso ++# Côte d'Ivoire (Ivory Coast) ++# The Gambia ++# Ghana ++# Guinea ++# Iceland ++# Mali ++# Mauritania ++# St Helena ++# Senegal ++# Sierra Leone ++# Togo + +-# Republic of the Congo +-# See Africa/Lagos. ++# The other parts of the St Helena territory are similar: ++# Tristan da Cunha: on GMT, say Whitman and the CIA ++# Ascension: on GMT, say the USNO (1995-12-21) and the CIA ++# Gough (scientific station since 1955; sealers wintered previously): ++# on GMT, says the CIA ++# Inaccessible, Nightingale: uninhabited + +-# Côte d'Ivoire / Ivory Coast + # Zone NAME STDOFF RULES FORMAT [UNTIL] + Zone Africa/Abidjan -0:16:08 - LMT 1912 + 0:00 - GMT +-Link Africa/Abidjan Africa/Accra # Ghana +-Link Africa/Abidjan Africa/Bamako # Mali +-Link Africa/Abidjan Africa/Banjul # The Gambia +-Link Africa/Abidjan Africa/Conakry # Guinea +-Link Africa/Abidjan Africa/Dakar # Senegal +-Link Africa/Abidjan Africa/Freetown # Sierra Leone +-Link Africa/Abidjan Africa/Lome # Togo +-Link Africa/Abidjan Africa/Nouakchott # Mauritania +-Link Africa/Abidjan Africa/Ouagadougou # Burkina Faso +-Link Africa/Abidjan Atlantic/Reykjavik # Iceland +-Link Africa/Abidjan Atlantic/St_Helena # St Helena +- +-# Djibouti +-# See Africa/Nairobi. + + ############################################################################### + +@@ -382,33 +359,6 @@ Rule Egypt 2014 only - Sep lastThu 24:00 0 - + Zone Africa/Cairo 2:05:09 - LMT 1900 Oct + 2:00 Egypt EE%sT + +-# Equatorial Guinea +-# See Africa/Lagos. +- +-# Eritrea +-# See Africa/Nairobi. +- +-# Eswatini (formerly Swaziland) +-# See Africa/Johannesburg. +- +-# Ethiopia +-# See Africa/Nairobi. +-# +-# Unfortunately tzdb records only Western clock time in use in Ethiopia, +-# as the tzdb format is not up to properly recording a common Ethiopian +-# timekeeping practice that is based on solar time. See: +-# Mortada D. If you have a meeting in Ethiopia, you'd better double +-# check the time. PRI's The World. 2015-01-30 15:15 -05. +-# https://www.pri.org/stories/2015-01-30/if-you-have-meeting-ethiopia-you-better-double-check-time +- +-# Gabon +-# See Africa/Lagos. +- +-# The Gambia +-# Ghana +-# Guinea +-# See Africa/Abidjan. +- + # Guinea-Bissau + # + # From Paul Eggert (2018-02-16): +@@ -421,7 +371,16 @@ Zone Africa/Bissau -1:02:20 - LMT 1912 Jan 1 1:00u + -1:00 - -01 1975 + 0:00 - GMT + ++# Comoros ++# Djibouti ++# Eritrea ++# Ethiopia + # Kenya ++# Madagascar ++# Mayotte ++# Somalia ++# Tanzania ++# Uganda + + # From P Chan (2020-10-24): + # +@@ -464,6 +423,14 @@ Zone Africa/Bissau -1:02:20 - LMT 1912 Jan 1 1:00u + # The 1908-05-01 announcement does not give an effective date, + # so just say "1908 May". + ++# From Paul Eggert (2018-09-11): ++# Unfortunately tzdb records only Western clock time in use in Ethiopia, ++# as the tzdb format is not up to properly recording a common Ethiopian ++# timekeeping practice that is based on solar time. See: ++# Mortada D. If you have a meeting in Ethiopia, you'd better double ++# check the time. PRI's The World. 2015-01-30 15:15 -05. ++# https://www.pri.org/stories/2015-01-30/if-you-have-meeting-ethiopia-you-better-double-check-time ++ + # Zone NAME STDOFF RULES FORMAT [UNTIL] + Zone Africa/Nairobi 2:27:16 - LMT 1908 May + 2:30 - +0230 1928 Jun 30 24:00 +@@ -471,18 +438,6 @@ Zone Africa/Nairobi 2:27:16 - LMT 1908 May + 2:30 - +0230 1936 Dec 31 24:00 + 2:45 - +0245 1942 Jul 31 24:00 + 3:00 - EAT +-Link Africa/Nairobi Africa/Addis_Ababa # Ethiopia +-Link Africa/Nairobi Africa/Asmara # Eritrea +-Link Africa/Nairobi Africa/Dar_es_Salaam # Tanzania +-Link Africa/Nairobi Africa/Djibouti +-Link Africa/Nairobi Africa/Kampala # Uganda +-Link Africa/Nairobi Africa/Mogadishu # Somalia +-Link Africa/Nairobi Indian/Antananarivo # Madagascar +-Link Africa/Nairobi Indian/Comoro +-Link Africa/Nairobi Indian/Mayotte +- +-# Lesotho +-# See Africa/Johannesburg. + + # Liberia + # +@@ -563,16 +518,6 @@ Zone Africa/Tripoli 0:52:44 - LMT 1920 + 1:00 Libya CE%sT 2013 Oct 25 2:00 + 2:00 - EET + +-# Madagascar +-# See Africa/Nairobi. +- +-# Malawi +-# See Africa/Maputo. +- +-# Mali +-# Mauritania +-# See Africa/Abidjan. +- + # Mauritius + + # From Steffen Thorsen (2008-06-25): +@@ -666,12 +611,6 @@ Zone Indian/Mauritius 3:50:00 - LMT 1907 # Port Louis + # Agalega Is, Rodriguez + # no information; probably like Indian/Mauritius + +-# Mayotte +-# See Africa/Nairobi. +- +-# Morocco +-# See Africa/Ceuta for Spanish Morocco. +- + # From Alex Krivenyshev (2008-05-09): + # Here is an article that Morocco plan to introduce Daylight Saving Time between + # 1 June, 2008 and 27 September, 2008. +@@ -1160,7 +1099,14 @@ Zone Africa/El_Aaiun -0:52:48 - LMT 1934 Jan # El Aaiún + 0:00 Morocco +00/+01 2018 Oct 28 3:00 + 0:00 Morocco +00/+01 + ++# Botswana ++# Burundi ++# Democratic Republic of the Congo (eastern) ++# Malawi + # Mozambique ++# Rwanda ++# Zambia ++# Zimbabwe + # + # Shanks gives 1903-03-01 for the transition to CAT. + # Perhaps the 1911-05-26 Portuguese decree +@@ -1170,14 +1116,6 @@ Zone Africa/El_Aaiun -0:52:48 - LMT 1934 Jan # El Aaiún + # Zone NAME STDOFF RULES FORMAT [UNTIL] + Zone Africa/Maputo 2:10:20 - LMT 1903 Mar + 2:00 - CAT +-Link Africa/Maputo Africa/Blantyre # Malawi +-Link Africa/Maputo Africa/Bujumbura # Burundi +-Link Africa/Maputo Africa/Gaborone # Botswana +-Link Africa/Maputo Africa/Harare # Zimbabwe +-Link Africa/Maputo Africa/Kigali # Rwanda +-Link Africa/Maputo Africa/Lubumbashi # E Dem. Rep. of Congo +-Link Africa/Maputo Africa/Lusaka # Zambia +- + + # Namibia + +@@ -1256,9 +1194,16 @@ Zone Africa/Windhoek 1:08:24 - LMT 1892 Feb 8 + 2:00 - CAT + # End of rearguard section. + +-# Niger +-# See Africa/Lagos. + ++# Angola ++# Benin ++# Cameroon ++# Central African Republic ++# Democratic Republic of the Congo (western) ++# Republic of the Congo ++# Equatorial Guinea ++# Gabon ++# Niger + # Nigeria + + # From P Chan (2020-12-03): +@@ -1324,32 +1269,6 @@ Zone Africa/Lagos 0:13:35 - LMT 1905 Jul 1 + 0:13:35 - LMT 1914 Jan 1 + 0:30 - +0030 1919 Sep 1 + 1:00 - WAT +-Link Africa/Lagos Africa/Bangui # Central African Republic +-Link Africa/Lagos Africa/Brazzaville # Rep. of the Congo +-Link Africa/Lagos Africa/Douala # Cameroon +-Link Africa/Lagos Africa/Kinshasa # Dem. Rep. of the Congo (west) +-Link Africa/Lagos Africa/Libreville # Gabon +-Link Africa/Lagos Africa/Luanda # Angola +-Link Africa/Lagos Africa/Malabo # Equatorial Guinea +-Link Africa/Lagos Africa/Niamey # Niger +-Link Africa/Lagos Africa/Porto-Novo # Benin +- +-# Réunion +-# See Asia/Dubai. +-# +-# The Crozet Islands also observe Réunion time; see the 'antarctica' file. +- +-# Rwanda +-# See Africa/Maputo. +- +-# St Helena +-# See Africa/Abidjan. +-# The other parts of the St Helena territory are similar: +-# Tristan da Cunha: on GMT, say Whitman and the CIA +-# Ascension: on GMT, say the USNO (1995-12-21) and the CIA +-# Gough (scientific station since 1955; sealers wintered previously): +-# on GMT, says the CIA +-# Inaccessible, Nightingale: uninhabited + + # São Tomé and Príncipe + +@@ -1378,19 +1297,10 @@ Zone Africa/Sao_Tome 0:26:56 - LMT 1884 + 1:00 - WAT 2019 Jan 1 02:00 + 0:00 - GMT + +-# Senegal +-# See Africa/Abidjan. +- +-# Seychelles +-# See Asia/Dubai. +- +-# Sierra Leone +-# See Africa/Abidjan. +- +-# Somalia +-# See Africa/Nairobi. +- ++# Eswatini (Swaziland) ++# Lesotho + # South Africa ++ + # Rule NAME FROM TO - IN ON AT SAVE LETTER/S + Rule SA 1942 1943 - Sep Sun>=15 2:00 1:00 - + Rule SA 1943 1944 - Mar Sun>=15 2:00 0 - +@@ -1398,8 +1308,6 @@ Rule SA 1943 1944 - Mar Sun>=15 2:00 0 - + Zone Africa/Johannesburg 1:52:00 - LMT 1892 Feb 8 + 1:30 - SAST 1903 Mar + 2:00 SA SAST +-Link Africa/Johannesburg Africa/Maseru # Lesotho +-Link Africa/Johannesburg Africa/Mbabane # Eswatini + # + # Marion and Prince Edward Is + # scientific station since 1947 +@@ -1448,12 +1356,6 @@ Zone Africa/Juba 2:06:28 - LMT 1931 + 3:00 - EAT 2021 Feb 1 00:00 + 2:00 - CAT + +-# Tanzania +-# See Africa/Nairobi. +- +-# Togo +-# See Africa/Abidjan. +- + # Tunisia + + # From Gwillim Law (2005-04-30): +@@ -1551,10 +1453,3 @@ Rule Tunisia 2006 2008 - Oct lastSun 2:00s 0 - + Zone Africa/Tunis 0:40:44 - LMT 1881 May 12 + 0:09:21 - PMT 1911 Mar 11 # Paris Mean Time + 1:00 Tunisia CE%sT +- +-# Uganda +-# See Africa/Nairobi. +- +-# Zambia +-# Zimbabwe +-# See Africa/Maputo. +diff --git a/jdk/make/data/tzdata/antarctica b/jdk/make/data/tzdata/antarctica +index 34c302e..792542b 100644 +--- a/jdk/make/data/tzdata/antarctica ++++ b/jdk/make/data/tzdata/antarctica +@@ -329,4 +329,4 @@ Zone Antarctica/Rothera 0 - -00 1976 Dec 1 + # we have to go around and set them back 5 minutes or so. + # Maybe if we let them run fast all of the time, we'd get to leave here sooner!! + # +-# See 'australasia' for Antarctica/McMurdo. ++# See Pacific/Auckland. +diff --git a/jdk/make/data/tzdata/asia b/jdk/make/data/tzdata/asia +index f1771e4..8f1fcac 100644 +--- a/jdk/make/data/tzdata/asia ++++ b/jdk/make/data/tzdata/asia +@@ -172,9 +172,6 @@ Zone Asia/Baku 3:19:24 - LMT 1924 May 2 + 4:00 EUAsia +04/+05 1997 + 4:00 Azer +04/+05 + +-# Bahrain +-# See Asia/Qatar. +- + # Bangladesh + # From Alexander Krivenyshev (2009-05-13): + # According to newspaper Asian Tribune (May 6, 2009) Bangladesh may introduce +@@ -277,10 +274,8 @@ Zone Indian/Chagos 4:49:40 - LMT 1907 + 5:00 - +05 1996 + 6:00 - +06 + +-# Brunei +-# See Asia/Kuching. +- +-# Burma / Myanmar ++# Cocos (Keeling) Islands ++# Myanmar (Burma) + + # Milne says 6:24:40 was the meridian of the time ball observatory at Rangoon. + +@@ -296,11 +291,6 @@ Zone Asia/Yangon 6:24:47 - LMT 1880 # or Rangoon + 6:30 - +0630 1942 May + 9:00 - +09 1945 May 3 + 6:30 - +0630 +-Link Asia/Yangon Indian/Cocos +- +-# Cambodia +-# See Asia/Bangkok. +- + + # China + +@@ -688,10 +678,9 @@ Zone Asia/Shanghai 8:05:43 - LMT 1901 + 8:00 PRC C%sT + # Xinjiang time, used by many in western China; represented by Ürümqi / Ürümchi + # / Wulumuqi. (Please use Asia/Shanghai if you prefer Beijing time.) ++# Vostok base in Antarctica matches this since 1970. + Zone Asia/Urumqi 5:50:20 - LMT 1928 + 6:00 - +06 +-Link Asia/Urumqi Antarctica/Vostok +- + + # Hong Kong + +@@ -1195,10 +1184,6 @@ Zone Asia/Famagusta 2:15:48 - LMT 1921 Nov 14 + 3:00 - +03 2017 Oct 29 1:00u + 2:00 EUAsia EE%sT + +-# Classically, Cyprus belongs to Asia; e.g. see Herodotus, Histories, I.72. +-# However, for various reasons many users expect to find it under Europe. +-Link Asia/Nicosia Europe/Nicosia +- + # Georgia + # From Paul Eggert (1994-11-19): + # Today's _Economist_ (p 60) reports that Georgia moved its clocks forward +@@ -2727,14 +2712,6 @@ Zone Asia/Pyongyang 8:23:00 - LMT 1908 Apr 1 + 8:30 - KST 2018 May 4 23:30 + 9:00 - KST + +-############################################################################### +- +-# Kuwait +-# See Asia/Riyadh. +- +-# Laos +-# See Asia/Bangkok. +- + + # Lebanon + # Rule NAME FROM TO - IN ON AT SAVE LETTER/S +@@ -2766,7 +2743,9 @@ Rule Lebanon 1999 max - Oct lastSun 0:00 0 - + Zone Asia/Beirut 2:22:00 - LMT 1880 + 2:00 Lebanon EE%sT + +-# Malaysia ++# Brunei ++# Malaysia (eastern) ++# + # Rule NAME FROM TO - IN ON AT SAVE LETTER/S + Rule NBorneo 1935 1941 - Sep 14 0:00 0:20 - + Rule NBorneo 1935 1941 - Dec 14 0:00 0 - +@@ -2783,14 +2762,12 @@ Zone Asia/Kuching 7:21:20 - LMT 1926 Mar + 8:00 NBorneo +08/+0820 1942 Feb 16 + 9:00 - +09 1945 Sep 12 + 8:00 - +08 +-Link Asia/Kuching Asia/Brunei + + # Maldives + # Zone NAME STDOFF RULES FORMAT [UNTIL] + Zone Indian/Maldives 4:54:00 - LMT 1880 # Malé + 4:54:00 - MMT 1960 # Malé Mean Time + 5:00 - +05 +-Link Indian/Maldives Indian/Kerguelen + + # Mongolia + +@@ -2953,9 +2930,6 @@ Zone Asia/Kathmandu 5:41:16 - LMT 1920 + 5:30 - +0530 1986 + 5:45 - +0545 + +-# Oman +-# See Asia/Dubai. +- + # Pakistan + + # From Rives McDow (2002-03-13): +@@ -3566,14 +3540,18 @@ Zone Asia/Manila -15:56:00 - LMT 1844 Dec 31 + 9:00 - JST 1944 Nov + 8:00 Phil P%sT + ++# Bahrain + # Qatar + # Zone NAME STDOFF RULES FORMAT [UNTIL] + Zone Asia/Qatar 3:26:08 - LMT 1920 # Al Dawhah / Doha + 4:00 - +04 1972 Jun + 3:00 - +03 +-Link Asia/Qatar Asia/Bahrain + ++# Kuwait + # Saudi Arabia ++# Yemen ++# ++# Japan's year-round bases in Antarctica match this since 1970. + # + # From Paul Eggert (2018-08-29): + # Time in Saudi Arabia and other countries in the Arabian peninsula was not +@@ -3618,9 +3596,6 @@ Link Asia/Qatar Asia/Bahrain + # Zone NAME STDOFF RULES FORMAT [UNTIL] + Zone Asia/Riyadh 3:06:52 - LMT 1947 Mar 14 + 3:00 - +03 +-Link Asia/Riyadh Antarctica/Syowa +-Link Asia/Riyadh Asia/Aden # Yemen +-Link Asia/Riyadh Asia/Kuwait + + # Singapore + # taken from Mok Ly Yng (2003-10-30) +@@ -3635,7 +3610,6 @@ Zone Asia/Singapore 6:55:25 - LMT 1901 Jan 1 + 9:00 - +09 1945 Sep 12 + 7:30 - +0730 1982 Jan 1 + 8:00 - +08 +-Link Asia/Singapore Asia/Kuala_Lumpur + + # Spratly Is + # no information +@@ -3881,14 +3855,15 @@ Zone Asia/Dushanbe 4:35:12 - LMT 1924 May 2 + 5:00 1:00 +06 1991 Sep 9 2:00s + 5:00 - +05 + ++# Cambodia ++# Christmas I ++# Laos + # Thailand ++# Vietnam (northern) + # Zone NAME STDOFF RULES FORMAT [UNTIL] + Zone Asia/Bangkok 6:42:04 - LMT 1880 + 6:42:04 - BMT 1920 Apr # Bangkok Mean Time + 7:00 - +07 +-Link Asia/Bangkok Asia/Phnom_Penh # Cambodia +-Link Asia/Bangkok Asia/Vientiane # Laos +-Link Asia/Bangkok Indian/Christmas + + # Turkmenistan + # From Shanks & Pottenger. +@@ -3899,13 +3874,15 @@ Zone Asia/Ashgabat 3:53:32 - LMT 1924 May 2 # or Ashkhabad + 4:00 RussiaAsia +04/+05 1992 Jan 19 2:00 + 5:00 - +05 + ++# Oman ++# Réunion ++# Seychelles + # United Arab Emirates ++# ++# The Crozet Is also observe Réunion time; see the 'antarctica' file. + # Zone NAME STDOFF RULES FORMAT [UNTIL] + Zone Asia/Dubai 3:41:12 - LMT 1920 + 4:00 - +04 +-Link Asia/Dubai Asia/Muscat # Oman +-Link Asia/Dubai Indian/Mahe +-Link Asia/Dubai Indian/Reunion + + # Uzbekistan + # Byalokoz 1919 says Uzbekistan was 4:27:53. +@@ -3925,7 +3902,7 @@ Zone Asia/Tashkent 4:37:11 - LMT 1924 May 2 + 5:00 RussiaAsia +05/+06 1992 + 5:00 - +05 + +-# Vietnam ++# Vietnam (southern) + + # From Paul Eggert (2014-10-04): + # Milne gives 7:16:56 for the meridian of Saigon in 1899, as being +@@ -3999,7 +3976,3 @@ Zone Asia/Ho_Chi_Minh 7:06:30 - LMT 1906 Jul 1 + # For timestamps in north Vietnam back to 1970 (the tzdb cutoff), + # use Asia/Bangkok; see the VN entries in the file zone1970.tab. + # For timestamps before 1970, see Asia/Hanoi in the file 'backzone'. +- +- +-# Yemen +-# See Asia/Riyadh. +diff --git a/jdk/make/data/tzdata/australasia b/jdk/make/data/tzdata/australasia +index 019cd77..fbe3b8a 100644 +--- a/jdk/make/data/tzdata/australasia ++++ b/jdk/make/data/tzdata/australasia +@@ -274,13 +274,6 @@ Zone Antarctica/Macquarie 0 - -00 1899 Nov + 10:00 1:00 AEDT 2011 + 10:00 AT AE%sT + +-# Christmas +-# See Asia/Bangkok. +- +-# Cocos (Keeling) Is +-# See Asia/Yangon. +- +- + # Fiji + + # Milne gives 11:55:44 for Suva. +@@ -416,8 +409,14 @@ Zone Antarctica/Macquarie 0 - -00 1899 Nov + # concerned shifting arrival and departure times, which may look like a simple + # thing but requires some significant logistical adjustments domestically and + # internationally." +-# Assume for now that DST will resume with the recent pre-2020 rules for the +-# 2022/2023 season. ++ ++# From Shalvin Narayan (2022-10-27): ++# Please note that there will not be any daylight savings time change ++# in Fiji for 2022-2023.... ++# https://www.facebook.com/FijianGovernment/posts/pfbid0mmWVTYmTibn66ybpFda75pDcf34SSpoSaskJW5gXwaKo5Sgc7273Q4fXWc6kQV6Hl ++# ++# From Paul Eggert (2022-10-27): ++# For now, assume DST is suspended indefinitely. + + # Rule NAME FROM TO - IN ON AT SAVE LETTER/S + Rule Fiji 1998 1999 - Nov Sun>=1 2:00 1:00 - +@@ -432,8 +431,6 @@ Rule Fiji 2014 2018 - Nov Sun>=1 2:00 1:00 - + Rule Fiji 2015 2021 - Jan Sun>=12 3:00 0 - + Rule Fiji 2019 only - Nov Sun>=8 2:00 1:00 - + Rule Fiji 2020 only - Dec 20 2:00 1:00 - +-Rule Fiji 2022 max - Nov Sun>=8 2:00 1:00 - +-Rule Fiji 2023 max - Jan Sun>=12 3:00 0 - + # Zone NAME STDOFF RULES FORMAT [UNTIL] + Zone Pacific/Fiji 11:55:44 - LMT 1915 Oct 26 # Suva + 12:00 Fiji +12/+13 +@@ -449,7 +446,9 @@ Zone Pacific/Tahiti -9:58:16 - LMT 1912 Oct # Papeete + # Clipperton (near North America) is administered from French Polynesia; + # it is uninhabited. + ++ + # Guam ++# N Mariana Is + + # Rule NAME FROM TO - IN ON AT SAVE LETTER/S + # http://guamlegislature.com/Public_Laws_5th/PL05-025.pdf +@@ -489,17 +488,20 @@ Zone Pacific/Guam -14:21:00 - LMT 1844 Dec 31 + 9:00 - +09 1944 Jul 31 + 10:00 Guam G%sT 2000 Dec 23 + 10:00 - ChST # Chamorro Standard Time +-Link Pacific/Guam Pacific/Saipan # N Mariana Is + +-# Kiribati ++ ++# Kiribati (Gilbert Is) ++# Marshall Is ++# Tuvalu ++# Wake ++# Wallis & Futuna + # Zone NAME STDOFF RULES FORMAT [UNTIL] + Zone Pacific/Tarawa 11:32:04 - LMT 1901 # Bairiki + 12:00 - +12 +-Link Pacific/Tarawa Pacific/Funafuti +-Link Pacific/Tarawa Pacific/Majuro +-Link Pacific/Tarawa Pacific/Wake +-Link Pacific/Tarawa Pacific/Wallis + ++# Kiribati (except Gilbert Is) ++# See Pacific/Tarawa for the Gilbert Is. ++# Zone NAME STDOFF RULES FORMAT [UNTIL] + Zone Pacific/Kanton 0 - -00 1937 Aug 31 + -12:00 - -12 1979 Oct + -11:00 - -11 1994 Dec 31 +@@ -509,9 +511,6 @@ Zone Pacific/Kiritimati -10:29:20 - LMT 1901 + -10:00 - -10 1994 Dec 31 + 14:00 - +14 + +-# N Mariana Is +-# See Pacific/Guam. +- + # Marshall Is + # See Pacific/Tarawa for most locations. + # Zone NAME STDOFF RULES FORMAT [UNTIL] +@@ -561,6 +560,7 @@ Zone Pacific/Noumea 11:05:48 - LMT 1912 Jan 13 # Nouméa + ############################################################################### + + # New Zealand ++# McMurdo Station and Scott Base in Antarctica use Auckland time. + + # Rule NAME FROM TO - IN ON AT SAVE LETTER/S + Rule NZ 1927 only - Nov 6 2:00 1:00 S +@@ -596,7 +596,6 @@ Rule Chatham 2008 max - Apr Sun>=1 2:45s 0 - + Zone Pacific/Auckland 11:39:04 - LMT 1868 Nov 2 + 11:30 NZ NZ%sT 1946 Jan 1 + 12:00 NZ NZ%sT +-Link Pacific/Auckland Antarctica/McMurdo + + Zone Pacific/Chatham 12:13:48 - LMT 1868 Nov 2 + 12:15 - +1215 1946 Jan 1 +@@ -695,8 +694,6 @@ Zone Pacific/Palau -15:02:04 - LMT 1844 Dec 31 # Koror + Zone Pacific/Port_Moresby 9:48:40 - LMT 1880 + 9:48:32 - PMMT 1895 # Port Moresby Mean Time + 10:00 - +10 +-Link Pacific/Port_Moresby Antarctica/DumontDUrville +-Link Pacific/Port_Moresby Pacific/Chuuk + # + # From Paul Eggert (2014-10-13): + # Base the Bougainville entry on the Arawa-Kieta region, which appears to have +@@ -729,10 +726,10 @@ Zone Pacific/Pitcairn -8:40:20 - LMT 1901 # Adamstown + -8:00 - -08 + + # American Samoa ++# Midway + Zone Pacific/Pago_Pago 12:37:12 - LMT 1892 Jul 5 + -11:22:48 - LMT 1911 + -11:00 - SST # S=Samoa +-Link Pacific/Pago_Pago Pacific/Midway # in US minor outlying islands + + # Samoa (formerly and also known as Western Samoa) + +@@ -824,7 +821,6 @@ Zone Pacific/Apia 12:33:04 - LMT 1892 Jul 5 + # Zone NAME STDOFF RULES FORMAT [UNTIL] + Zone Pacific/Guadalcanal 10:39:48 - LMT 1912 Oct # Honiara + 11:00 - +11 +-Link Pacific/Guadalcanal Pacific/Pohnpei + + # Tokelau + # +@@ -864,9 +860,6 @@ Zone Pacific/Tongatapu 12:19:12 - LMT 1945 Sep 10 + 13:00 - +13 1999 + 13:00 Tonga +13/+14 + +-# Tuvalu +-# See Pacific/Tarawa. +- + + # US minor outlying islands + +@@ -917,15 +910,9 @@ Zone Pacific/Tongatapu 12:19:12 - LMT 1945 Sep 10 + # Kingman + # uninhabited + +-# Midway +-# See Pacific/Pago_Pago. +- + # Palmyra + # uninhabited since World War II; was probably like Pacific/Kiritimati + +-# Wake +-# See Pacific/Tarawa. +- + + # Vanuatu + +@@ -962,9 +949,6 @@ Rule Vanuatu 1992 only - Oct Sat>=22 24:00 1:00 - + Zone Pacific/Efate 11:13:16 - LMT 1912 Jan 13 # Vila + 11:00 Vanuatu +11/+12 + +-# Wallis and Futuna +-# See Pacific/Tarawa. +- + ############################################################################### + + # NOTES +diff --git a/jdk/make/data/tzdata/backward b/jdk/make/data/tzdata/backward +index 7765d99..1fb087a 100644 +--- a/jdk/make/data/tzdata/backward ++++ b/jdk/make/data/tzdata/backward +@@ -27,7 +27,7 @@ + # 2009-05-17 by Arthur David Olson. + + # This file provides links from old or merged timezone names to current ones. +-# Many names changed in late 1993, and many merged names moved here ++# Many names changed in 1993 and in 1995, and many merged names moved here + # in the period from 2013 through 2022. Several of these names are + # also present in the file 'backzone', which has data important only + # for pre-1970 timestamps and so is out of scope for tzdb proper. +@@ -36,50 +36,24 @@ + # building with 'make BACKWARD=', in practice downstream users + # typically use this file for backward compatibility. + +-# Link TARGET LINK-NAME +-Link Africa/Nairobi Africa/Asmera +-Link Africa/Abidjan Africa/Timbuktu +-Link America/Argentina/Catamarca America/Argentina/ComodRivadavia +-Link America/Adak America/Atka +-Link America/Argentina/Buenos_Aires America/Buenos_Aires +-Link America/Argentina/Catamarca America/Catamarca +-Link America/Panama America/Coral_Harbour +-Link America/Argentina/Cordoba America/Cordoba +-Link America/Tijuana America/Ensenada +-Link America/Indiana/Indianapolis America/Fort_Wayne +-Link America/Nuuk America/Godthab +-Link America/Indiana/Indianapolis America/Indianapolis +-Link America/Argentina/Jujuy America/Jujuy +-Link America/Indiana/Knox America/Knox_IN +-Link America/Kentucky/Louisville America/Louisville +-Link America/Argentina/Mendoza America/Mendoza +-Link America/Toronto America/Montreal +-Link America/Rio_Branco America/Porto_Acre +-Link America/Argentina/Cordoba America/Rosario +-Link America/Tijuana America/Santa_Isabel +-Link America/Denver America/Shiprock +-Link America/Puerto_Rico America/Virgin +-Link Pacific/Auckland Antarctica/South_Pole +-Link Asia/Ashgabat Asia/Ashkhabad +-Link Asia/Kolkata Asia/Calcutta +-Link Asia/Shanghai Asia/Chongqing +-Link Asia/Shanghai Asia/Chungking +-Link Asia/Dhaka Asia/Dacca +-Link Asia/Shanghai Asia/Harbin +-Link Asia/Urumqi Asia/Kashgar +-Link Asia/Kathmandu Asia/Katmandu +-Link Asia/Macau Asia/Macao +-Link Asia/Yangon Asia/Rangoon +-Link Asia/Ho_Chi_Minh Asia/Saigon +-Link Asia/Jerusalem Asia/Tel_Aviv +-Link Asia/Thimphu Asia/Thimbu +-Link Asia/Makassar Asia/Ujung_Pandang +-Link Asia/Ulaanbaatar Asia/Ulan_Bator +-Link Atlantic/Faroe Atlantic/Faeroe +-Link Europe/Berlin Atlantic/Jan_Mayen +-Link Australia/Sydney Australia/ACT +-Link Australia/Sydney Australia/Canberra +-Link Australia/Hobart Australia/Currie ++# This file is divided into sections, one for each major reason for a ++# backward compatibility link. Each section is sorted by link name. ++ ++# A "#= TARGET1" comment labels each link inserted only because some ++# .zi parsers (including tzcode through 2022e) mishandle links to links. ++# The comment says what the target would be if these parsers were fixed ++# so that data could contain links to links. For example, the line ++# "Link Australia/Sydney Australia/ACT #= Australia/Canberra" would be ++# "Link Australia/Canberra Australia/ACT" were it not that data lines ++# refrain from linking to links like Australia/Canberra, which means ++# the Australia/ACT line links instead to Australia/Sydney, ++# Australia/Canberra's target. ++ ++ ++# Pre-1993 naming conventions ++ ++# Link TARGET LINK-NAME #= TARGET1 ++Link Australia/Sydney Australia/ACT #= Australia/Canberra + Link Australia/Lord_Howe Australia/LHI + Link Australia/Sydney Australia/NSW + Link Australia/Darwin Australia/North +@@ -89,7 +63,7 @@ Link Australia/Hobart Australia/Tasmania + Link Australia/Melbourne Australia/Victoria + Link Australia/Perth Australia/West + Link Australia/Broken_Hill Australia/Yancowinna +-Link America/Rio_Branco Brazil/Acre ++Link America/Rio_Branco Brazil/Acre #= America/Porto_Acre + Link America/Noronha Brazil/DeNoronha + Link America/Sao_Paulo Brazil/East + Link America/Manaus Brazil/West +@@ -109,20 +83,36 @@ Link Pacific/Easter Chile/EasterIsland + Link America/Havana Cuba + Link Africa/Cairo Egypt + Link Europe/Dublin Eire ++# Vanguard section, for most .zi parsers. ++#Link GMT Etc/GMT ++#Link GMT Etc/GMT+0 ++#Link GMT Etc/GMT-0 ++#Link GMT Etc/GMT0 ++#Link GMT Etc/Greenwich ++# Rearguard section, for TZUpdater 2.3.2 and earlier. ++Link Etc/GMT Etc/GMT+0 ++Link Etc/GMT Etc/GMT-0 ++Link Etc/GMT Etc/GMT0 ++Link Etc/GMT Etc/Greenwich ++# End of rearguard section. + Link Etc/UTC Etc/UCT +-Link Europe/London Europe/Belfast +-Link Europe/Kyiv Europe/Kiev +-Link Europe/Chisinau Europe/Tiraspol +-Link Europe/Kyiv Europe/Uzhgorod +-Link Europe/Kyiv Europe/Zaporozhye ++Link Etc/UTC Etc/Universal ++Link Etc/UTC Etc/Zulu + Link Europe/London GB + Link Europe/London GB-Eire ++# Vanguard section, for most .zi parsers. ++#Link GMT GMT+0 ++#Link GMT GMT-0 ++#Link GMT GMT0 ++#Link GMT Greenwich ++# Rearguard section, for TZUpdater 2.3.2 and earlier. + Link Etc/GMT GMT+0 + Link Etc/GMT GMT-0 + Link Etc/GMT GMT0 + Link Etc/GMT Greenwich ++# End of rearguard section. + Link Asia/Hong_Kong Hongkong +-Link Africa/Abidjan Iceland ++Link Africa/Abidjan Iceland #= Atlantic/Reykjavik + Link Asia/Tehran Iran + Link Asia/Jerusalem Israel + Link America/Jamaica Jamaica +@@ -134,14 +124,8 @@ Link America/Mazatlan Mexico/BajaSur + Link America/Mexico_City Mexico/General + Link Pacific/Auckland NZ + Link Pacific/Chatham NZ-CHAT +-Link America/Denver Navajo ++Link America/Denver Navajo #= America/Shiprock + Link Asia/Shanghai PRC +-Link Pacific/Kanton Pacific/Enderbury +-Link Pacific/Honolulu Pacific/Johnston +-Link Pacific/Guadalcanal Pacific/Ponape +-Link Pacific/Pago_Pago Pacific/Samoa +-Link Pacific/Port_Moresby Pacific/Truk +-Link Pacific/Port_Moresby Pacific/Yap + Link Europe/Warsaw Poland + Link Europe/Lisbon Portugal + Link Asia/Taipei ROC +@@ -165,3 +149,192 @@ Link Etc/UTC UTC + Link Etc/UTC Universal + Link Europe/Moscow W-SU + Link Etc/UTC Zulu ++ ++ ++# Two-part names that were renamed mostly to three-part names in 1995 ++ ++# Link TARGET LINK-NAME #= TARGET1 ++Link America/Argentina/Buenos_Aires America/Buenos_Aires ++Link America/Argentina/Catamarca America/Catamarca ++Link America/Argentina/Cordoba America/Cordoba ++Link America/Indiana/Indianapolis America/Indianapolis ++Link America/Argentina/Jujuy America/Jujuy ++Link America/Indiana/Knox America/Knox_IN ++Link America/Kentucky/Louisville America/Louisville ++Link America/Argentina/Mendoza America/Mendoza ++Link America/Puerto_Rico America/Virgin #= America/St_Thomas ++Link Pacific/Pago_Pago Pacific/Samoa ++ ++ ++# Pre-2013 practice, which typically had a Zone per zone.tab line ++ ++# Link TARGET LINK-NAME ++Link Africa/Abidjan Africa/Accra ++Link Africa/Nairobi Africa/Addis_Ababa ++Link Africa/Nairobi Africa/Asmara ++Link Africa/Abidjan Africa/Bamako ++Link Africa/Lagos Africa/Bangui ++Link Africa/Abidjan Africa/Banjul ++Link Africa/Maputo Africa/Blantyre ++Link Africa/Lagos Africa/Brazzaville ++Link Africa/Maputo Africa/Bujumbura ++Link Africa/Abidjan Africa/Conakry ++Link Africa/Abidjan Africa/Dakar ++Link Africa/Nairobi Africa/Dar_es_Salaam ++Link Africa/Nairobi Africa/Djibouti ++Link Africa/Lagos Africa/Douala ++Link Africa/Abidjan Africa/Freetown ++Link Africa/Maputo Africa/Gaborone ++Link Africa/Maputo Africa/Harare ++Link Africa/Nairobi Africa/Kampala ++Link Africa/Maputo Africa/Kigali ++Link Africa/Lagos Africa/Kinshasa ++Link Africa/Lagos Africa/Libreville ++Link Africa/Abidjan Africa/Lome ++Link Africa/Lagos Africa/Luanda ++Link Africa/Maputo Africa/Lubumbashi ++Link Africa/Maputo Africa/Lusaka ++Link Africa/Lagos Africa/Malabo ++Link Africa/Johannesburg Africa/Maseru ++Link Africa/Johannesburg Africa/Mbabane ++Link Africa/Nairobi Africa/Mogadishu ++Link Africa/Lagos Africa/Niamey ++Link Africa/Abidjan Africa/Nouakchott ++Link Africa/Abidjan Africa/Ouagadougou ++Link Africa/Lagos Africa/Porto-Novo ++Link America/Puerto_Rico America/Anguilla ++Link America/Puerto_Rico America/Antigua ++Link America/Puerto_Rico America/Aruba ++Link America/Panama America/Atikokan ++Link America/Puerto_Rico America/Blanc-Sablon ++Link America/Panama America/Cayman ++Link America/Phoenix America/Creston ++Link America/Puerto_Rico America/Curacao ++Link America/Puerto_Rico America/Dominica ++Link America/Puerto_Rico America/Grenada ++Link America/Puerto_Rico America/Guadeloupe ++Link America/Puerto_Rico America/Kralendijk ++Link America/Puerto_Rico America/Lower_Princes ++Link America/Puerto_Rico America/Marigot ++Link America/Puerto_Rico America/Montserrat ++Link America/Toronto America/Nassau ++Link America/Puerto_Rico America/Port_of_Spain ++Link America/Puerto_Rico America/St_Barthelemy ++Link America/Puerto_Rico America/St_Kitts ++Link America/Puerto_Rico America/St_Lucia ++Link America/Puerto_Rico America/St_Thomas ++Link America/Puerto_Rico America/St_Vincent ++Link America/Puerto_Rico America/Tortola ++Link Pacific/Port_Moresby Antarctica/DumontDUrville ++Link Pacific/Auckland Antarctica/McMurdo ++Link Asia/Riyadh Antarctica/Syowa ++Link Asia/Urumqi Antarctica/Vostok ++Link Europe/Berlin Arctic/Longyearbyen ++Link Asia/Riyadh Asia/Aden ++Link Asia/Qatar Asia/Bahrain ++Link Asia/Kuching Asia/Brunei ++Link Asia/Singapore Asia/Kuala_Lumpur ++Link Asia/Riyadh Asia/Kuwait ++Link Asia/Dubai Asia/Muscat ++Link Asia/Bangkok Asia/Phnom_Penh ++Link Asia/Bangkok Asia/Vientiane ++Link Africa/Abidjan Atlantic/Reykjavik ++Link Africa/Abidjan Atlantic/St_Helena ++Link Europe/Brussels Europe/Amsterdam ++Link Europe/Prague Europe/Bratislava ++Link Europe/Zurich Europe/Busingen ++Link Europe/Berlin Europe/Copenhagen ++Link Europe/London Europe/Guernsey ++Link Europe/London Europe/Isle_of_Man ++Link Europe/London Europe/Jersey ++Link Europe/Belgrade Europe/Ljubljana ++Link Europe/Brussels Europe/Luxembourg ++Link Europe/Helsinki Europe/Mariehamn ++Link Europe/Paris Europe/Monaco ++Link Europe/Berlin Europe/Oslo ++Link Europe/Belgrade Europe/Podgorica ++Link Europe/Rome Europe/San_Marino ++Link Europe/Belgrade Europe/Sarajevo ++Link Europe/Belgrade Europe/Skopje ++Link Europe/Berlin Europe/Stockholm ++Link Europe/Zurich Europe/Vaduz ++Link Europe/Rome Europe/Vatican ++Link Europe/Belgrade Europe/Zagreb ++Link Africa/Nairobi Indian/Antananarivo ++Link Asia/Bangkok Indian/Christmas ++Link Asia/Yangon Indian/Cocos ++Link Africa/Nairobi Indian/Comoro ++Link Indian/Maldives Indian/Kerguelen ++Link Asia/Dubai Indian/Mahe ++Link Africa/Nairobi Indian/Mayotte ++Link Asia/Dubai Indian/Reunion ++Link Pacific/Port_Moresby Pacific/Chuuk ++Link Pacific/Tarawa Pacific/Funafuti ++Link Pacific/Tarawa Pacific/Majuro ++Link Pacific/Pago_Pago Pacific/Midway ++Link Pacific/Guadalcanal Pacific/Pohnpei ++Link Pacific/Guam Pacific/Saipan ++Link Pacific/Tarawa Pacific/Wake ++Link Pacific/Tarawa Pacific/Wallis ++ ++ ++# Non-zone.tab locations with timestamps since 1970 that duplicate ++# those of an existing location ++ ++# Link TARGET LINK-NAME ++Link Africa/Abidjan Africa/Timbuktu ++Link America/Argentina/Catamarca America/Argentina/ComodRivadavia ++Link America/Adak America/Atka ++Link America/Panama America/Coral_Harbour ++Link America/Tijuana America/Ensenada ++Link America/Indiana/Indianapolis America/Fort_Wayne ++Link America/Toronto America/Montreal ++Link America/Toronto America/Nipigon ++Link America/Rio_Branco America/Porto_Acre ++Link America/Winnipeg America/Rainy_River ++Link America/Argentina/Cordoba America/Rosario ++Link America/Tijuana America/Santa_Isabel ++Link America/Denver America/Shiprock ++Link America/Toronto America/Thunder_Bay ++Link Pacific/Auckland Antarctica/South_Pole ++Link Asia/Shanghai Asia/Chongqing ++Link Asia/Shanghai Asia/Harbin ++Link Asia/Urumqi Asia/Kashgar ++Link Asia/Jerusalem Asia/Tel_Aviv ++Link Europe/Berlin Atlantic/Jan_Mayen ++Link Australia/Sydney Australia/Canberra ++Link Australia/Hobart Australia/Currie ++Link Europe/London Europe/Belfast ++Link Europe/Chisinau Europe/Tiraspol ++Link Europe/Kyiv Europe/Uzhgorod ++Link Europe/Kyiv Europe/Zaporozhye ++Link Pacific/Kanton Pacific/Enderbury ++Link Pacific/Honolulu Pacific/Johnston ++Link Pacific/Port_Moresby Pacific/Yap ++ ++ ++# Alternate names for the same location ++ ++# Link TARGET LINK-NAME #= TARGET1 ++Link Africa/Nairobi Africa/Asmera #= Africa/Asmara ++Link America/Nuuk America/Godthab ++Link Asia/Ashgabat Asia/Ashkhabad ++Link Asia/Kolkata Asia/Calcutta ++Link Asia/Shanghai Asia/Chungking #= Asia/Chongqing ++Link Asia/Dhaka Asia/Dacca ++# Istanbul is in both continents. ++Link Europe/Istanbul Asia/Istanbul ++Link Asia/Kathmandu Asia/Katmandu ++Link Asia/Macau Asia/Macao ++Link Asia/Yangon Asia/Rangoon ++Link Asia/Ho_Chi_Minh Asia/Saigon ++Link Asia/Thimphu Asia/Thimbu ++Link Asia/Makassar Asia/Ujung_Pandang ++Link Asia/Ulaanbaatar Asia/Ulan_Bator ++Link Atlantic/Faroe Atlantic/Faeroe ++Link Europe/Kyiv Europe/Kiev ++# Classically, Cyprus is in Asia; e.g. see Herodotus, Histories, I.72. ++# However, for various reasons many users expect to find it under Europe. ++Link Asia/Nicosia Europe/Nicosia ++Link Pacific/Guadalcanal Pacific/Ponape #= Pacific/Pohnpei ++Link Pacific/Port_Moresby Pacific/Truk #= Pacific/Chuuk +diff --git a/jdk/make/data/tzdata/etcetera b/jdk/make/data/tzdata/etcetera +index 82ff6b4..8ae294f 100644 +--- a/jdk/make/data/tzdata/etcetera ++++ b/jdk/make/data/tzdata/etcetera +@@ -39,26 +39,23 @@ + # Do not use a POSIX TZ setting like TZ='GMT+4', which is four hours + # behind GMT but uses the completely misleading abbreviation "GMT". + +-Zone Etc/GMT 0 - GMT +- + # The following zone is used by tzcode functions like gmtime, + # which load the "UTC" file to handle seconds properly. + Zone Etc/UTC 0 - UTC + ++# Functions like gmtime load the "GMT" file to handle leap seconds properly. ++# Vanguard section, which works with most .zi parsers. ++#Zone GMT 0 - GMT ++# Rearguard section, for TZUpdater 2.3.2 and earlier. ++Zone Etc/GMT 0 - GMT ++ + # The following link uses older naming conventions, + # but it belongs here, not in the file 'backward', + # as it is needed for tzcode releases through 2022a, + # where functions like gmtime load "GMT" instead of the "Etc/UTC". + # We want this to work even on installations that omit 'backward'. + Link Etc/GMT GMT +- +-Link Etc/UTC Etc/Universal +-Link Etc/UTC Etc/Zulu +- +-Link Etc/GMT Etc/Greenwich +-Link Etc/GMT Etc/GMT-0 +-Link Etc/GMT Etc/GMT+0 +-Link Etc/GMT Etc/GMT0 ++# End of rearguard section. + + # Be consistent with POSIX TZ settings in the Zone names, + # even though this is the opposite of what many people expect. +diff --git a/jdk/make/data/tzdata/europe b/jdk/make/data/tzdata/europe +index 930cede..7b6aa13 100644 +--- a/jdk/make/data/tzdata/europe ++++ b/jdk/make/data/tzdata/europe +@@ -527,9 +527,6 @@ Zone Europe/London -0:01:15 - LMT 1847 Dec 1 + 1:00 - BST 1971 Oct 31 2:00u + 0:00 GB-Eire %s 1996 + 0:00 EU GMT/BST +-Link Europe/London Europe/Jersey +-Link Europe/London Europe/Guernsey +-Link Europe/London Europe/Isle_of_Man + + # From Paul Eggert (2018-02-15): + # In January 2018 we discovered that the negative SAVE values in the +@@ -902,6 +899,8 @@ Zone Europe/Minsk 1:50:16 - LMT 1880 + 3:00 - +03 + + # Belgium ++# Luxembourg ++# Netherlands + # + # From Michael Deckers (2019-08-25): + # The exposition in the web page +@@ -984,11 +983,6 @@ Zone Europe/Brussels 0:17:30 - LMT 1880 + 1:00 C-Eur CE%sT 1944 Sep 3 + 1:00 Belgium CE%sT 1977 + 1:00 EU CE%sT +-Link Europe/Brussels Europe/Amsterdam +-Link Europe/Brussels Europe/Luxembourg +- +-# Bosnia and Herzegovina +-# See Europe/Belgrade. + + # Bulgaria + # +@@ -1015,13 +1009,11 @@ Zone Europe/Sofia 1:33:16 - LMT 1880 + 2:00 E-Eur EE%sT 1997 + 2:00 EU EE%sT + +-# Croatia +-# See Europe/Belgrade. +- + # Cyprus + # Please see the 'asia' file for Asia/Nicosia. + +-# Czech Republic / Czechia ++# Czech Republic (Czechia) ++# Slovakia + # + # From Paul Eggert (2018-04-15): + # The source for Czech data is: Kdy začíná a končí letní čas. 2018-04-15. +@@ -1048,15 +1040,14 @@ Zone Europe/Prague 0:57:44 - LMT 1850 + # End of rearguard section. + 1:00 Czech CE%sT 1979 + 1:00 EU CE%sT +-Link Europe/Prague Europe/Bratislava +- +- +-# Denmark, Faroe Islands, and Greenland +-# For Denmark see Europe/Berlin. + ++# Faroe Is ++# Zone NAME STDOFF RULES FORMAT [UNTIL] + Zone Atlantic/Faroe -0:27:04 - LMT 1908 Jan 11 # Tórshavn + 0:00 - WET 1981 + 0:00 EU WE%sT ++ ++# Greenland + # + # From Paul Eggert (2004-10-31): + # During World War II, Germany maintained secret manned weather stations in +@@ -1282,11 +1273,8 @@ Zone Europe/Helsinki 1:39:49 - LMT 1878 May 31 + 2:00 Finland EE%sT 1983 + 2:00 EU EE%sT + +-# Åland Is +-Link Europe/Helsinki Europe/Mariehamn +- +- + # France ++# Monaco + + # From Ciro Discepolo (2000-12-20): + # +@@ -1423,9 +1411,11 @@ Zone Europe/Paris 0:09:21 - LMT 1891 Mar 16 + 0:00 France WE%sT 1945 Sep 16 3:00 + 1:00 France CE%sT 1977 + 1:00 EU CE%sT +-Link Europe/Paris Europe/Monaco + ++# Denmark + # Germany ++# Norway ++# Sweden + + # From Markus Kuhn (1998-09-29): + # The German time zone web site by the Physikalisch-Technische +@@ -1443,6 +1433,53 @@ Link Europe/Paris Europe/Monaco + # However, Moscow did not observe daylight saving in 1945, so + # this was equivalent to UT +03, not +04. + ++# Svalbard & Jan Mayen ++ ++# From Steffen Thorsen (2001-05-01): ++# Although I could not find it explicitly, it seems that Jan Mayen and ++# Svalbard have been using the same time as Norway at least since the ++# time they were declared as parts of Norway. Svalbard was declared ++# as a part of Norway by law of 1925-07-17 no 11, section 4 and Jan ++# Mayen by law of 1930-02-27 no 2, section 2. (From ++# and ++# ). The law/regulation ++# for normal/standard time in Norway is from 1894-06-29 no 1 (came ++# into operation on 1895-01-01) and Svalbard/Jan Mayen seem to be a ++# part of this law since 1925/1930. (From ++# ) I have not been ++# able to find if Jan Mayen used a different time zone (e.g. -0100) ++# before 1930. Jan Mayen has only been "inhabited" since 1921 by ++# Norwegian meteorologists and maybe used the same time as Norway ever ++# since 1921. Svalbard (Arctic/Longyearbyen) has been inhabited since ++# before 1895, and therefore probably changed the local time somewhere ++# between 1895 and 1925 (inclusive). ++ ++# From Paul Eggert (2013-09-04): ++# ++# Actually, Jan Mayen was never occupied by Germany during World War II, ++# so it must have diverged from Oslo time during the war, as Oslo was ++# keeping Berlin time. ++# ++# says that the meteorologists ++# burned down their station in 1940 and left the island, but returned in ++# 1941 with a small Norwegian garrison and continued operations despite ++# frequent air attacks from Germans. In 1943 the Americans established a ++# radiolocating station on the island, called "Atlantic City". Possibly ++# the UT offset changed during the war, but I think it unlikely that ++# Jan Mayen used German daylight-saving rules. ++# ++# Svalbard is more complicated, as it was raided in August 1941 by an ++# Allied party that evacuated the civilian population to England (says ++# ). The Svalbard FAQ ++# says that the Germans were ++# expelled on 1942-05-14. However, small parties of Germans did return, ++# and according to Wilhelm Dege's book "War North of 80" (1954) ++# http://www.ucalgary.ca/UofC/departments/UP/1-55238/1-55238-110-2.html ++# the German armed forces at the Svalbard weather station code-named ++# Haudegen did not surrender to the Allies until September 1945. ++# ++# All these events predate our cutoff date of 1970, so use Europe/Berlin ++# for these regions. + + # Rule NAME FROM TO - IN ON AT SAVE LETTER/S + Rule Germany 1946 only - Apr 14 2:00s 1:00 S +@@ -1467,11 +1504,6 @@ Zone Europe/Berlin 0:53:28 - LMT 1893 Apr + 1:00 SovietZone CE%sT 1946 + 1:00 Germany CE%sT 1980 + 1:00 EU CE%sT +-Link Europe/Berlin Arctic/Longyearbyen +-Link Europe/Berlin Europe/Copenhagen +-Link Europe/Berlin Europe/Oslo +-Link Europe/Berlin Europe/Stockholm +- + + # Georgia + # Please see the "asia" file for Asia/Tbilisi. +@@ -1590,10 +1622,9 @@ Zone Europe/Budapest 1:16:20 - LMT 1890 Nov 1 + 1:00 Hungary CE%sT 1984 + 1:00 EU CE%sT + +-# Iceland +-# See Africa/Abidjan. +- + # Italy ++# San Marino ++# Vatican City + # + # From Paul Eggert (2001-03-06): + # Sicily and Sardinia each had their own time zones from 1866 to 1893, +@@ -1712,13 +1743,6 @@ Zone Europe/Rome 0:49:56 - LMT 1866 Dec 12 + 1:00 C-Eur CE%sT 1944 Jun 4 + 1:00 Italy CE%sT 1980 + 1:00 EU CE%sT +-Link Europe/Rome Europe/Vatican +-Link Europe/Rome Europe/San_Marino +- +- +-# Kosovo +-# See Europe/Belgrade. +- + + # Latvia + +@@ -1802,10 +1826,6 @@ Zone Europe/Riga 1:36:34 - LMT 1880 + 2:00 - EET 2001 Jan 2 + 2:00 EU EE%sT + +-# Liechtenstein +-# See Europe/Zurich. +- +- + # Lithuania + + # From Paul Eggert (2016-03-18): +@@ -1858,12 +1878,6 @@ Zone Europe/Vilnius 1:41:16 - LMT 1880 + 2:00 - EET 2003 Jan 1 + 2:00 EU EE%sT + +-# Luxembourg +-# See Europe/Brussels. +- +-# North Macedonia +-# See Europe/Belgrade. +- + # Malta + # + # From Paul Eggert (2016-10-21): +@@ -1959,67 +1973,6 @@ Zone Europe/Chisinau 1:55:20 - LMT 1880 + # See Romania commentary for the guessed 1997 transition to EU rules. + 2:00 Moldova EE%sT + +-# Monaco +-# See Europe/Paris. +- +-# Montenegro +-# See Europe/Belgrade. +- +-# Netherlands +-# See Europe/Brussels. +- +-# Norway +-# See Europe/Berlin. +- +-# Svalbard & Jan Mayen +- +-# From Steffen Thorsen (2001-05-01): +-# Although I could not find it explicitly, it seems that Jan Mayen and +-# Svalbard have been using the same time as Norway at least since the +-# time they were declared as parts of Norway. Svalbard was declared +-# as a part of Norway by law of 1925-07-17 no 11, section 4 and Jan +-# Mayen by law of 1930-02-27 no 2, section 2. (From +-# and +-# ). The law/regulation +-# for normal/standard time in Norway is from 1894-06-29 no 1 (came +-# into operation on 1895-01-01) and Svalbard/Jan Mayen seem to be a +-# part of this law since 1925/1930. (From +-# ) I have not been +-# able to find if Jan Mayen used a different time zone (e.g. -0100) +-# before 1930. Jan Mayen has only been "inhabited" since 1921 by +-# Norwegian meteorologists and maybe used the same time as Norway ever +-# since 1921. Svalbard (Arctic/Longyearbyen) has been inhabited since +-# before 1895, and therefore probably changed the local time somewhere +-# between 1895 and 1925 (inclusive). +- +-# From Paul Eggert (2013-09-04): +-# +-# Actually, Jan Mayen was never occupied by Germany during World War II, +-# so it must have diverged from Oslo time during the war, as Oslo was +-# keeping Berlin time. +-# +-# says that the meteorologists +-# burned down their station in 1940 and left the island, but returned in +-# 1941 with a small Norwegian garrison and continued operations despite +-# frequent air attacks from Germans. In 1943 the Americans established a +-# radiolocating station on the island, called "Atlantic City". Possibly +-# the UT offset changed during the war, but I think it unlikely that +-# Jan Mayen used German daylight-saving rules. +-# +-# Svalbard is more complicated, as it was raided in August 1941 by an +-# Allied party that evacuated the civilian population to England (says +-# ). The Svalbard FAQ +-# says that the Germans were +-# expelled on 1942-05-14. However, small parties of Germans did return, +-# and according to Wilhelm Dege's book "War North of 80" (1954) +-# http://www.ucalgary.ca/UofC/departments/UP/1-55238/1-55238-110-2.html +-# the German armed forces at the Svalbard weather station code-named +-# Haudegen did not surrender to the Allies until September 1945. +-# +-# All these events predate our cutoff date of 1970, so use Europe/Berlin +-# for these regions. +- +- + # Poland + + # The 1919 dates and times can be found in Tygodnik Urzędowy nr 1 (1919-03-20), +@@ -3301,11 +3254,13 @@ Zone Asia/Anadyr 11:49:56 - LMT 1924 May 2 + 11:00 Russia +11/+12 2011 Mar 27 2:00s + 12:00 - +12 + +- +-# San Marino +-# See Europe/Rome. +- ++# Bosnia & Herzegovina ++# Croatia ++# Kosovo ++# Montenegro ++# North Macedonia + # Serbia ++# Slovenia + # Zone NAME STDOFF RULES FORMAT [UNTIL] + Zone Europe/Belgrade 1:22:00 - LMT 1884 + 1:00 - CET 1941 Apr 18 23:00 +@@ -3317,17 +3272,6 @@ Zone Europe/Belgrade 1:22:00 - LMT 1884 + # Shanks & Pottenger don't give as much detail, so go with Koželj. + 1:00 - CET 1982 Nov 27 + 1:00 EU CE%sT +-Link Europe/Belgrade Europe/Ljubljana # Slovenia +-Link Europe/Belgrade Europe/Podgorica # Montenegro +-Link Europe/Belgrade Europe/Sarajevo # Bosnia and Herzegovina +-Link Europe/Belgrade Europe/Skopje # North Macedonia +-Link Europe/Belgrade Europe/Zagreb # Croatia +- +-# Slovakia +-# See Europe/Prague. +- +-# Slovenia +-# See Europe/Belgrade. + + # Spain + # +@@ -3434,10 +3378,11 @@ Zone Atlantic/Canary -1:01:36 - LMT 1922 Mar # Las Palmas de Gran C. + # IATA SSIM (1996-09) says the Canaries switch at 2:00u, not 1:00u. + # Ignore this for now, as the Canaries are part of the EU. + +-# Sweden +-# See Europe/Berlin. + ++# Germany (Busingen enclave) ++# Liechtenstein + # Switzerland ++# + # From Howse: + # By the end of the 18th century clocks and watches became commonplace + # and their performance improved enormously. Communities began to keep +@@ -3550,9 +3495,6 @@ Zone Europe/Zurich 0:34:08 - LMT 1853 Jul 16 # See above comment. + 0:29:46 - BMT 1894 Jun # Bern Mean Time + 1:00 Swiss CE%sT 1981 + 1:00 EU CE%sT +-Link Europe/Zurich Europe/Busingen +-Link Europe/Zurich Europe/Vaduz +- + + # Turkey + +@@ -3757,7 +3699,6 @@ Zone Europe/Istanbul 1:55:52 - LMT 1880 + 2:00 1:00 EEST 2015 Nov 8 1:00u + 2:00 EU EE%sT 2016 Sep 7 + 3:00 - +03 +-Link Europe/Istanbul Asia/Istanbul # Istanbul is in both continents. + + # Ukraine + # +@@ -3860,9 +3801,6 @@ Zone Europe/Kyiv 2:02:04 - LMT 1880 + 2:00 C-Eur EE%sT 1996 May 13 + 2:00 EU EE%sT + +-# Vatican City +-# See Europe/Rome. +- + ############################################################################### + + # One source shows that Bulgaria, Cyprus, Finland, and Greece observe DST from +diff --git a/jdk/make/data/tzdata/northamerica b/jdk/make/data/tzdata/northamerica +index ce4ee74..465e8c2 100644 +--- a/jdk/make/data/tzdata/northamerica ++++ b/jdk/make/data/tzdata/northamerica +@@ -852,7 +852,6 @@ Zone America/Phoenix -7:28:18 - LMT 1883 Nov 18 19:00u + -7:00 - MST 1967 + -7:00 US M%sT 1968 Mar 21 + -7:00 - MST +-Link America/Phoenix America/Creston + + # From Arthur David Olson (1988-02-13): + # A writer from the Inter Tribal Council of Arizona, Inc., +@@ -1626,23 +1625,6 @@ Zone America/Moncton -4:19:08 - LMT 1883 Dec 9 + + # Ontario + +-# From Paul Eggert (2006-07-09): +-# Shanks & Pottenger write that since 1970 most of Ontario has been like +-# Toronto. +-# Thunder Bay skipped DST in 1973. +-# Many smaller locales did not observe peacetime DST until 1974; +-# Nipigon (EST) and Rainy River (CST) are the largest that we know of. +-# Far west Ontario is like Winnipeg; far east Quebec is like Halifax. +- +-# From Jeffery Nichols (2020-02-06): +-# According to the [Shanks] atlas, those western Ontario zones are huge, +-# covering most of Ontario northwest of Sault Ste Marie and Timmins. +-# The zones seem to include towns bigger than the ones they're named after, +-# like Dryden in America/Rainy_River and Wawa (and maybe Attawapiskat) in +-# America/Nipigon. I assume it's too much trouble to change the name of the +-# zone (like when you found out that America/Glace_Bay includes Sydney, Nova +-# Scotia).... +- + # From Mark Brader (2003-07-26): + # [According to the Toronto Star] Orillia, Ontario, adopted DST + # effective Saturday, 1912-06-22, 22:00; the article mentions that +@@ -1663,17 +1645,6 @@ Zone America/Moncton -4:19:08 - LMT 1883 Dec 9 + + # From Mark Brader (2010-03-06): + # +-# Currently the database has: +-# +-# # Ontario +-# +-# # From Paul Eggert (2006-07-09): +-# # Shanks & Pottenger write that since 1970 most of Ontario has been like +-# # Toronto. +-# # Thunder Bay skipped DST in 1973. +-# # Many smaller locales did not observe peacetime DST until 1974; +-# # Nipigon (EST) and Rainy River (CST) are the largest that we know of. +-# + # In the (Toronto) Globe and Mail for Saturday, 1955-09-24, in the bottom + # right corner of page 1, it says that Toronto will return to standard + # time at 2 am Sunday morning (which agrees with the database), and that: +@@ -1681,10 +1652,8 @@ Zone America/Moncton -4:19:08 - LMT 1883 Dec 9 + # The one-hour setback will go into effect throughout most of Ontario, + # except in areas like Windsor which remains on standard time all year. + # +-# Windsor is, of course, a lot larger than Nipigon. +-# +-# I only came across this incidentally. I don't know if Windsor began +-# observing DST when Detroit did, or in 1974, or on some other date. ++# ... I don't know if Windsor began observing DST when Detroit did, ++# or in 1974, or on some other date. + # + # By the way, the article continues by noting that: + # +@@ -1766,23 +1735,7 @@ Rule Toronto 1951 1956 - Sep lastSun 2:00 0 S + # Toronto Star, which said that DST was ending 1971-10-31 as usual. + Rule Toronto 1957 1973 - Oct lastSun 2:00 0 S + +-# From Paul Eggert (2003-07-27): +-# Willett (1914-03) writes (p. 17) "In the Cities of Fort William, and +-# Port Arthur, Ontario, the principle of the Bill has been in +-# operation for the past three years, and in the City of Moose Jaw, +-# Saskatchewan, for one year." +- +-# From David Bryan via Tory Tronrud, Director/Curator, +-# Thunder Bay Museum (2003-11-12): +-# There is some suggestion, however, that, by-law or not, daylight +-# savings time was being practiced in Fort William and Port Arthur +-# before 1909.... [I]n 1910, the line between the Eastern and Central +-# Time Zones was permanently moved about two hundred miles west to +-# include the Thunder Bay area.... When Canada adopted daylight +-# savings time in 1916, Fort William and Port Arthur, having done so +-# already, did not change their clocks.... During the Second World +-# War,... [t]he cities agreed to implement DST during the summer +-# months for the remainder of the war years. ++# The Bahamas match Toronto since 1970. + + # Zone NAME STDOFF RULES FORMAT [UNTIL] + Zone America/Toronto -5:17:32 - LMT 1895 +@@ -1791,22 +1744,6 @@ Zone America/Toronto -5:17:32 - LMT 1895 + -5:00 Canada E%sT 1946 + -5:00 Toronto E%sT 1974 + -5:00 Canada E%sT +-Link America/Toronto America/Nassau +-Zone America/Thunder_Bay -5:57:00 - LMT 1895 +- -6:00 - CST 1910 +- -5:00 - EST 1942 +- -5:00 Canada E%sT 1970 +- -5:00 Toronto E%sT 1973 +- -5:00 - EST 1974 +- -5:00 Canada E%sT +-Zone America/Nipigon -5:53:04 - LMT 1895 +- -5:00 Canada E%sT 1940 Sep 29 +- -5:00 1:00 EDT 1942 Feb 9 2:00s +- -5:00 Canada E%sT +-Zone America/Rainy_River -6:18:16 - LMT 1895 +- -6:00 Canada C%sT 1940 Sep 29 +- -6:00 1:00 CDT 1942 Feb 9 2:00s +- -6:00 Canada C%sT + # For Atikokan see America/Panama. + + +@@ -2639,6 +2576,12 @@ Zone America/Dawson -9:17:40 - LMT 1900 Aug 20 + # 5- The islands, reefs and keys shall take their timezone from the + # longitude they are located at. + ++# From Paul Eggert (2022-10-28): ++# The new Mexican law was published today: ++# https://www.dof.gob.mx/nota_detalle.php?codigo=5670045&fecha=28/10/2022 ++# This abolishes DST except where US DST rules are observed, ++# and in addition changes all of Chihuahua to -06 with no DST. ++ + # Rule NAME FROM TO - IN ON AT SAVE LETTER/S + Rule Mexico 1931 only - May 1 23:00 1:00 D + Rule Mexico 1931 only - Oct 1 0:00 0 S +@@ -2654,8 +2597,8 @@ Rule Mexico 1996 2000 - Apr Sun>=1 2:00 1:00 D + Rule Mexico 1996 2000 - Oct lastSun 2:00 0 S + Rule Mexico 2001 only - May Sun>=1 2:00 1:00 D + Rule Mexico 2001 only - Sep lastSun 2:00 0 S +-Rule Mexico 2002 max - Apr Sun>=1 2:00 1:00 D +-Rule Mexico 2002 max - Oct lastSun 2:00 0 S ++Rule Mexico 2002 2022 - Apr Sun>=1 2:00 1:00 D ++Rule Mexico 2002 2022 - Oct lastSun 2:00 0 S + # Zone NAME STDOFF RULES FORMAT [UNTIL] + # Quintana Roo; represented by Cancún + Zone America/Cancun -5:47:04 - LMT 1922 Jan 1 6:00u +@@ -2708,7 +2651,8 @@ Zone America/Ojinaga -6:57:40 - LMT 1922 Jan 1 7:00u + -6:00 Mexico C%sT 1998 + -6:00 - CST 1998 Apr Sun>=1 3:00 + -7:00 Mexico M%sT 2010 +- -7:00 US M%sT ++ -7:00 US M%sT 2022 Oct 30 2:00 ++ -6:00 - CST + # Chihuahua (away from US border) + Zone America/Chihuahua -7:04:20 - LMT 1922 Jan 1 7:00u + -7:00 - MST 1927 Jun 10 23:00 +@@ -2717,7 +2661,8 @@ Zone America/Chihuahua -7:04:20 - LMT 1922 Jan 1 7:00u + -6:00 - CST 1996 + -6:00 Mexico C%sT 1998 + -6:00 - CST 1998 Apr Sun>=1 3:00 +- -7:00 Mexico M%sT ++ -7:00 Mexico M%sT 2022 Oct 30 2:00 ++ -6:00 - CST + # Sonora + Zone America/Hermosillo -7:23:52 - LMT 1922 Jan 1 7:00u + -7:00 - MST 1927 Jun 10 23:00 +@@ -2815,20 +2760,16 @@ Zone America/Tijuana -7:48:04 - LMT 1922 Jan 1 7:00u + # http://dof.gob.mx/nota_detalle.php?codigo=5127480&fecha=06/01/2010 + # It has been moved to the 'backward' file. + # ++# From Paul Eggert (2022-10-28): ++# Today's new law states that the entire state of Baja California ++# follows US DST rules, which agrees with simplifications noted above. ++# + # + # Revillagigedo Is + # no information + + ############################################################################### + +-# Anguilla +-# Antigua and Barbuda +-# See America/Puerto_Rico. +- +-# The Bahamas +-# See America/Toronto. +- +- + # Barbados + + # For 1899 Milne gives -3:58:29.2. +@@ -3041,12 +2982,6 @@ Zone Atlantic/Bermuda -4:19:18 - LMT 1890 # Hamilton + -4:00 Canada A%sT 1976 + -4:00 US A%sT + +-# Caribbean Netherlands +-# See America/Puerto_Rico. +- +-# Cayman Is +-# See America/Panama. +- + # Costa Rica + + # Milne gives -5:36:13.3 as San José mean time. +@@ -3272,9 +3207,6 @@ Zone America/Havana -5:29:28 - LMT 1890 + -5:29:36 - HMT 1925 Jul 19 12:00 # Havana MT + -5:00 Cuba C%sT + +-# Dominica +-# See America/Puerto_Rico. +- + # Dominican Republic + + # From Steffen Thorsen (2000-10-30): +@@ -3321,12 +3253,6 @@ Rule Salv 1987 1988 - Sep lastSun 0:00 0 S + Zone America/El_Salvador -5:56:48 - LMT 1921 # San Salvador + -6:00 Salv C%sT + +-# Grenada +-# Guadeloupe +-# St Barthélemy +-# St Martin (French part) +-# See America/Puerto_Rico. +- + # Guatemala + # + # From Gwillim Law (2006-04-22), after a heads-up from Oscar van Vlijmen: +@@ -3512,9 +3438,6 @@ Zone America/Martinique -4:04:20 - LMT 1890 # Fort-de-France + -4:00 1:00 ADT 1980 Sep 28 + -4:00 - AST + +-# Montserrat +-# See America/Puerto_Rico. +- + # Nicaragua + # + # This uses Shanks & Pottenger for times before 2005. +@@ -3580,44 +3503,39 @@ Zone America/Managua -5:45:08 - LMT 1890 + -5:00 - EST 1997 + -6:00 Nic C%sT + ++# Cayman Is + # Panama ++# ++# Atikokan and Coral Harbour, Canada, match Panama since 1970. + # Zone NAME STDOFF RULES FORMAT [UNTIL] + Zone America/Panama -5:18:08 - LMT 1890 + -5:19:36 - CMT 1908 Apr 22 # Colón Mean Time + -5:00 - EST +-Link America/Panama America/Atikokan +-Link America/Panama America/Cayman + ++# Anguilla ++# Antigua & Barbuda ++# Aruba ++# Caribbean Netherlands ++# Curaçao ++# Dominica ++# Grenada ++# Guadeloupe ++# Montserrat + # Puerto Rico ++# St Barthélemy ++# St Kitts-Nevis ++# Sint Maarten / St Martin ++# St Lucia ++# St Vincent & the Grenadines ++# Trinidad & Tobago ++# Virgin Is (UK & US) ++# + # There are too many San Juans elsewhere, so we'll use 'Puerto_Rico'. + # Zone NAME STDOFF RULES FORMAT [UNTIL] + Zone America/Puerto_Rico -4:24:25 - LMT 1899 Mar 28 12:00 # San Juan + -4:00 - AST 1942 May 3 + -4:00 US A%sT 1946 + -4:00 - AST +-Link America/Puerto_Rico America/Anguilla +-Link America/Puerto_Rico America/Antigua +-Link America/Puerto_Rico America/Aruba +-Link America/Puerto_Rico America/Curacao +-Link America/Puerto_Rico America/Blanc-Sablon # Quebec (Lower North Shore) +-Link America/Puerto_Rico America/Dominica +-Link America/Puerto_Rico America/Grenada +-Link America/Puerto_Rico America/Guadeloupe +-Link America/Puerto_Rico America/Kralendijk # Caribbean Netherlands +-Link America/Puerto_Rico America/Lower_Princes # Sint Maarten +-Link America/Puerto_Rico America/Marigot # St Martin (French part) +-Link America/Puerto_Rico America/Montserrat +-Link America/Puerto_Rico America/Port_of_Spain # Trinidad & Tobago +-Link America/Puerto_Rico America/St_Barthelemy # St Barthélemy +-Link America/Puerto_Rico America/St_Kitts # St Kitts & Nevis +-Link America/Puerto_Rico America/St_Lucia +-Link America/Puerto_Rico America/St_Thomas # Virgin Islands (US) +-Link America/Puerto_Rico America/St_Vincent +-Link America/Puerto_Rico America/Tortola # Virgin Islands (UK) +- +-# St Kitts-Nevis +-# St Lucia +-# See America/Puerto_Rico. + + # St Pierre and Miquelon + # There are too many St Pierres elsewhere, so we'll use 'Miquelon'. +@@ -3627,12 +3545,6 @@ Zone America/Miquelon -3:44:40 - LMT 1911 May 15 # St Pierre + -3:00 - -03 1987 + -3:00 Canada -03/-02 + +-# St Vincent and the Grenadines +-# See America/Puerto_Rico. +- +-# Sint Maarten +-# See America/Puerto_Rico. +- + # Turks and Caicos + # + # From Chris Dunn in +@@ -3702,11 +3614,6 @@ Zone America/Grand_Turk -4:44:32 - LMT 1890 + -4:00 - AST 2018 Mar 11 3:00 + -5:00 US E%sT + +-# British Virgin Is +-# US Virgin Is +-# See America/Puerto_Rico. +- +- + # Local Variables: + # coding: utf-8 + # End: +diff --git a/jdk/make/data/tzdata/southamerica b/jdk/make/data/tzdata/southamerica +index 3c0e0e2..982ad09 100644 +--- a/jdk/make/data/tzdata/southamerica ++++ b/jdk/make/data/tzdata/southamerica +@@ -608,9 +608,6 @@ Zone America/Argentina/Ushuaia -4:33:12 - LMT 1894 Oct 31 + -3:00 Arg -03/-02 2008 Oct 18 + -3:00 - -03 + +-# Aruba +-# See America/Puerto_Rico. +- + # Bolivia + # Zone NAME STDOFF RULES FORMAT [UNTIL] + Zone America/La_Paz -4:32:36 - LMT 1890 +@@ -1455,15 +1452,6 @@ Zone America/Bogota -4:56:16 - LMT 1884 Mar 13 + # Malpelo, Providencia, San Andres + # no information; probably like America/Bogota + +-# Curaçao +-# See America/Puerto_Rico. +-# +-# From Arthur David Olson (2011-06-15): +-# use links for places with new iso3166 codes. +-# The name "Lower Prince's Quarter" is both longer than fourteen characters +-# and contains an apostrophe; use "Lower_Princes".... +-# From Paul Eggert (2021-09-29): +-# These backward-compatibility links now are in the 'northamerica' file. + + # Ecuador + # +@@ -1779,9 +1767,6 @@ Zone America/Paramaribo -3:40:40 - LMT 1911 + -3:30 - -0330 1984 Oct + -3:00 - -03 + +-# Trinidad and Tobago +-# See America/Puerto_Rico. +- + # Uruguay + # From Paul Eggert (1993-11-18): + # Uruguay wins the prize for the strangest peacetime manipulation of the rules. +diff --git a/jdk/make/data/tzdata/zone.tab b/jdk/make/data/tzdata/zone.tab +index ee02519..535d1c9 100644 +--- a/jdk/make/data/tzdata/zone.tab ++++ b/jdk/make/data/tzdata/zone.tab +@@ -137,13 +137,10 @@ CA +4606-06447 America/Moncton Atlantic - New Brunswick + CA +5320-06025 America/Goose_Bay Atlantic - Labrador (most areas) + CA +5125-05707 America/Blanc-Sablon AST - QC (Lower North Shore) + CA +4339-07923 America/Toronto Eastern - ON, QC (most areas) +-CA +4901-08816 America/Nipigon Eastern - ON, QC (no DST 1967-73) +-CA +4823-08915 America/Thunder_Bay Eastern - ON (Thunder Bay) + CA +6344-06828 America/Iqaluit Eastern - NU (most east areas) + CA +6608-06544 America/Pangnirtung Eastern - NU (Pangnirtung) + CA +484531-0913718 America/Atikokan EST - ON (Atikokan); NU (Coral H) + CA +4953-09709 America/Winnipeg Central - ON (west); Manitoba +-CA +4843-09434 America/Rainy_River Central - ON (Rainy R, Ft Frances) + CA +744144-0944945 America/Resolute Central - NU (Resolute) + CA +624900-0920459 America/Rankin_Inlet Central - NU (central) + CA +5024-10439 America/Regina CST - SK (most areas) +diff --git a/jdk/src/share/classes/sun/util/resources/TimeZoneNames.java b/jdk/src/share/classes/sun/util/resources/TimeZoneNames.java +index 215ad9f..d327baf 100644 +--- a/jdk/src/share/classes/sun/util/resources/TimeZoneNames.java ++++ b/jdk/src/share/classes/sun/util/resources/TimeZoneNames.java +@@ -1,5 +1,5 @@ + /* +- * Copyright (c) 1996, 2021, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 1996, 2022, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it +@@ -429,7 +429,7 @@ public final class TimeZoneNames extends TimeZoneNamesBundle { + "French Guiana Summer Time", "GFST", + "French Guiana Time", "GFT"}}, + {"America/Cayman", EST}, +- {"America/Chihuahua", MST}, ++ {"America/Chihuahua", CST}, + {"America/Creston", MST}, + {"America/Coral_Harbour", EST}, + {"America/Cordoba", AGT}, +@@ -518,7 +518,7 @@ public final class TimeZoneNames extends TimeZoneNamesBundle { + {"America/North_Dakota/Center", CST}, + {"America/North_Dakota/New_Salem", CST}, + {"America/Nuuk", WGT}, +- {"America/Ojinaga", MST}, ++ {"America/Ojinaga", CST}, + {"America/Panama", EST}, + {"America/Pangnirtung", EST}, + {"America/Paramaribo", new String[] {"Suriname Time", "SRT", +diff --git a/jdk/src/share/classes/sun/util/resources/de/TimeZoneNames_de.java b/jdk/src/share/classes/sun/util/resources/de/TimeZoneNames_de.java +index 00e490e..75d4213 100644 +--- a/jdk/src/share/classes/sun/util/resources/de/TimeZoneNames_de.java ++++ b/jdk/src/share/classes/sun/util/resources/de/TimeZoneNames_de.java +@@ -1,5 +1,5 @@ + /* +- * Copyright (c) 1997, 2021, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 1997, 2022, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it +@@ -427,7 +427,7 @@ public final class TimeZoneNames_de extends TimeZoneNamesBundle { + "Franz\u00f6sisch-Guiana Sommerzeit", "GFST", + "Franz\u00F6sisch-Guiana Zeit", "GFT"}}, + {"America/Cayman", EST}, +- {"America/Chihuahua", MST}, ++ {"America/Chihuahua", CST}, + {"America/Creston", MST}, + {"America/Coral_Harbour", EST}, + {"America/Cordoba", AGT}, +@@ -516,7 +516,7 @@ public final class TimeZoneNames_de extends TimeZoneNamesBundle { + {"America/North_Dakota/Center", CST}, + {"America/North_Dakota/New_Salem", CST}, + {"America/Nuuk", WGT}, +- {"America/Ojinaga", MST}, ++ {"America/Ojinaga", CST}, + {"America/Panama", EST}, + {"America/Pangnirtung", EST}, + {"America/Paramaribo", new String[] {"Suriname Zeit", "SRT", +diff --git a/jdk/src/share/classes/sun/util/resources/es/TimeZoneNames_es.java b/jdk/src/share/classes/sun/util/resources/es/TimeZoneNames_es.java +index aaaf4ec..97576bf 100644 +--- a/jdk/src/share/classes/sun/util/resources/es/TimeZoneNames_es.java ++++ b/jdk/src/share/classes/sun/util/resources/es/TimeZoneNames_es.java +@@ -1,5 +1,5 @@ + /* +- * Copyright (c) 1997, 2021, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 1997, 2022, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it +@@ -427,7 +427,7 @@ public final class TimeZoneNames_es extends TimeZoneNamesBundle { + "Hora de verano de la Guayana Francesa", "GFST", + "Hora de la Guayana Francesa", "GFT"}}, + {"America/Cayman", EST}, +- {"America/Chihuahua", MST}, ++ {"America/Chihuahua", CST}, + {"America/Creston", MST}, + {"America/Coral_Harbour", EST}, + {"America/Cordoba", AGT}, +@@ -516,7 +516,7 @@ public final class TimeZoneNames_es extends TimeZoneNamesBundle { + {"America/North_Dakota/Center", CST}, + {"America/North_Dakota/New_Salem", CST}, + {"America/Nuuk", WGT}, +- {"America/Ojinaga", MST}, ++ {"America/Ojinaga", CST}, + {"America/Panama", EST}, + {"America/Pangnirtung", EST}, + {"America/Paramaribo", new String[] {"Hora de Surinam", "SRT", +diff --git a/jdk/src/share/classes/sun/util/resources/fr/TimeZoneNames_fr.java b/jdk/src/share/classes/sun/util/resources/fr/TimeZoneNames_fr.java +index 6415066..bcf05e9 100644 +--- a/jdk/src/share/classes/sun/util/resources/fr/TimeZoneNames_fr.java ++++ b/jdk/src/share/classes/sun/util/resources/fr/TimeZoneNames_fr.java +@@ -1,5 +1,5 @@ + /* +- * Copyright (c) 1997, 2021, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 1997, 2022, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it +@@ -427,7 +427,7 @@ public final class TimeZoneNames_fr extends TimeZoneNamesBundle { + "Heure d'\u00e9t\u00e9 de Guyane fran\u00e7aise", "GFST", + "Heure de Guyane fran\u00E7aise", "GFT"}}, + {"America/Cayman", EST}, +- {"America/Chihuahua", MST}, ++ {"America/Chihuahua", CST}, + {"America/Creston", MST}, + {"America/Coral_Harbour", EST}, + {"America/Cordoba", AGT}, +@@ -516,7 +516,7 @@ public final class TimeZoneNames_fr extends TimeZoneNamesBundle { + {"America/North_Dakota/Center", CST}, + {"America/North_Dakota/New_Salem", CST}, + {"America/Nuuk", WGT}, +- {"America/Ojinaga", MST}, ++ {"America/Ojinaga", CST}, + {"America/Panama", EST}, + {"America/Pangnirtung", EST}, + {"America/Paramaribo", new String[] {"Heure du Surinam", "SRT", +diff --git a/jdk/src/share/classes/sun/util/resources/it/TimeZoneNames_it.java b/jdk/src/share/classes/sun/util/resources/it/TimeZoneNames_it.java +index dead599..951779d 100644 +--- a/jdk/src/share/classes/sun/util/resources/it/TimeZoneNames_it.java ++++ b/jdk/src/share/classes/sun/util/resources/it/TimeZoneNames_it.java +@@ -1,5 +1,5 @@ + /* +- * Copyright (c) 1997, 2021, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 1997, 2022, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it +@@ -427,7 +427,7 @@ public final class TimeZoneNames_it extends TimeZoneNamesBundle { + "Ora estiva della Guyana Francese", "GFST", + "Ora della Guyana Francese", "GFT"}}, + {"America/Cayman", EST}, +- {"America/Chihuahua", MST}, ++ {"America/Chihuahua", CST}, + {"America/Creston", MST}, + {"America/Coral_Harbour", EST}, + {"America/Cordoba", AGT}, +@@ -516,7 +516,7 @@ public final class TimeZoneNames_it extends TimeZoneNamesBundle { + {"America/North_Dakota/Center", CST}, + {"America/North_Dakota/New_Salem", CST}, + {"America/Nuuk", WGT}, +- {"America/Ojinaga", MST}, ++ {"America/Ojinaga", CST}, + {"America/Panama", EST}, + {"America/Pangnirtung", EST}, + {"America/Paramaribo", new String[] {"Ora di Suriname", "SRT", +diff --git a/jdk/src/share/classes/sun/util/resources/ja/TimeZoneNames_ja.java b/jdk/src/share/classes/sun/util/resources/ja/TimeZoneNames_ja.java +index 0c104b5..c1dce59 100644 +--- a/jdk/src/share/classes/sun/util/resources/ja/TimeZoneNames_ja.java ++++ b/jdk/src/share/classes/sun/util/resources/ja/TimeZoneNames_ja.java +@@ -1,5 +1,5 @@ + /* +- * Copyright (c) 1997, 2021, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 1997, 2022, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it +@@ -427,7 +427,7 @@ public final class TimeZoneNames_ja extends TimeZoneNamesBundle { + "\u4ecf\u9818\u30ae\u30a2\u30ca\u590f\u6642\u9593", "GFST", + "\u30D5\u30E9\u30F3\u30B9\u9818\u30AE\u30A2\u30CA\u6642\u9593", "GFT"}}, + {"America/Cayman", EST}, +- {"America/Chihuahua", MST}, ++ {"America/Chihuahua", CST}, + {"America/Creston", MST}, + {"America/Coral_Harbour", EST}, + {"America/Cordoba", AGT}, +@@ -516,7 +516,7 @@ public final class TimeZoneNames_ja extends TimeZoneNamesBundle { + {"America/North_Dakota/Center", CST}, + {"America/North_Dakota/New_Salem", CST}, + {"America/Nuuk", WGT}, +- {"America/Ojinaga", MST}, ++ {"America/Ojinaga", CST}, + {"America/Panama", EST}, + {"America/Pangnirtung", EST}, + {"America/Paramaribo", new String[] {"\u30b9\u30ea\u30ca\u30e0\u6642\u9593", "SRT", +diff --git a/jdk/src/share/classes/sun/util/resources/ko/TimeZoneNames_ko.java b/jdk/src/share/classes/sun/util/resources/ko/TimeZoneNames_ko.java +index 65efcf5..77cf2c4 100644 +--- a/jdk/src/share/classes/sun/util/resources/ko/TimeZoneNames_ko.java ++++ b/jdk/src/share/classes/sun/util/resources/ko/TimeZoneNames_ko.java +@@ -1,5 +1,5 @@ + /* +- * Copyright (c) 1997, 2021, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 1997, 2022, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it +@@ -427,7 +427,7 @@ public final class TimeZoneNames_ko extends TimeZoneNamesBundle { + "\ud504\ub791\uc2a4\ub839 \uae30\uc544\ub098 \uc77c\uad11\uc808\uc57d\uc2dc\uac04", "GFST", + "\uD504\uB791\uC2A4\uB839 \uAE30\uC544\uB098 \uD45C\uC900\uC2DC", "GFT"}}, + {"America/Cayman", EST}, +- {"America/Chihuahua", MST}, ++ {"America/Chihuahua", CST}, + {"America/Creston", MST}, + {"America/Coral_Harbour", EST}, + {"America/Cordoba", AGT}, +@@ -516,7 +516,7 @@ public final class TimeZoneNames_ko extends TimeZoneNamesBundle { + {"America/North_Dakota/Center", CST}, + {"America/North_Dakota/New_Salem", CST}, + {"America/Nuuk", WGT}, +- {"America/Ojinaga", MST}, ++ {"America/Ojinaga", CST}, + {"America/Panama", EST}, + {"America/Pangnirtung", EST}, + {"America/Paramaribo", new String[] {"\uc218\ub9ac\ub0a8 \uc2dc\uac04", "SRT", +diff --git a/jdk/src/share/classes/sun/util/resources/pt/TimeZoneNames_pt_BR.java b/jdk/src/share/classes/sun/util/resources/pt/TimeZoneNames_pt_BR.java +index b5f3acb..9b35f2b 100644 +--- a/jdk/src/share/classes/sun/util/resources/pt/TimeZoneNames_pt_BR.java ++++ b/jdk/src/share/classes/sun/util/resources/pt/TimeZoneNames_pt_BR.java +@@ -1,5 +1,5 @@ + /* +- * Copyright (c) 1997, 2021, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 1997, 2022, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it +@@ -427,7 +427,7 @@ public final class TimeZoneNames_pt_BR extends TimeZoneNamesBundle { + "Fuso hor\u00e1rio de ver\u00e3o da Guiana Francesa", "GFST", + "Hor\u00E1rio da Guiana Francesa", "GFT"}}, + {"America/Cayman", EST}, +- {"America/Chihuahua", MST}, ++ {"America/Chihuahua", CST}, + {"America/Creston", MST}, + {"America/Coral_Harbour", EST}, + {"America/Cordoba", AGT}, +@@ -516,7 +516,7 @@ public final class TimeZoneNames_pt_BR extends TimeZoneNamesBundle { + {"America/North_Dakota/Center", CST}, + {"America/North_Dakota/New_Salem", CST}, + {"America/Nuuk", WGT}, +- {"America/Ojinaga", MST}, ++ {"America/Ojinaga", CST}, + {"America/Panama", EST}, + {"America/Pangnirtung", EST}, + {"America/Paramaribo", new String[] {"Fuso hor\u00e1rio do Suriname", "SRT", +diff --git a/jdk/src/share/classes/sun/util/resources/sv/TimeZoneNames_sv.java b/jdk/src/share/classes/sun/util/resources/sv/TimeZoneNames_sv.java +index d51762c..0e5be03 100644 +--- a/jdk/src/share/classes/sun/util/resources/sv/TimeZoneNames_sv.java ++++ b/jdk/src/share/classes/sun/util/resources/sv/TimeZoneNames_sv.java +@@ -1,5 +1,5 @@ + /* +- * Copyright (c) 1997, 2021, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 1997, 2022, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it +@@ -427,7 +427,7 @@ public final class TimeZoneNames_sv extends TimeZoneNamesBundle { + "Franska Guyana, sommartid", "GFST", + "Franska Guyana-tid", "GFT"}}, + {"America/Cayman", EST}, +- {"America/Chihuahua", MST}, ++ {"America/Chihuahua", CST}, + {"America/Creston", MST}, + {"America/Coral_Harbour", EST}, + {"America/Cordoba", AGT}, +@@ -516,7 +516,7 @@ public final class TimeZoneNames_sv extends TimeZoneNamesBundle { + {"America/North_Dakota/Center", CST}, + {"America/North_Dakota/New_Salem", CST}, + {"America/Nuuk", WGT}, +- {"America/Ojinaga", MST}, ++ {"America/Ojinaga", CST}, + {"America/Panama", EST}, + {"America/Pangnirtung", EST}, + {"America/Paramaribo", new String[] {"Surinam, normaltid", "SRT", +diff --git a/jdk/src/share/classes/sun/util/resources/zh/TimeZoneNames_zh_CN.java b/jdk/src/share/classes/sun/util/resources/zh/TimeZoneNames_zh_CN.java +index 1c4eef0..c7946f3 100644 +--- a/jdk/src/share/classes/sun/util/resources/zh/TimeZoneNames_zh_CN.java ++++ b/jdk/src/share/classes/sun/util/resources/zh/TimeZoneNames_zh_CN.java +@@ -1,5 +1,5 @@ + /* +- * Copyright (c) 1997, 2021, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 1997, 2022, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it +@@ -427,7 +427,7 @@ public final class TimeZoneNames_zh_CN extends TimeZoneNamesBundle { + "\u6cd5\u5c5e\u572d\u4e9a\u90a3\u590f\u4ee4\u65f6", "GFST", + "\u6CD5\u5C5E\u572D\u4E9A\u90A3\u65F6\u95F4", "GFT"}}, + {"America/Cayman", EST}, +- {"America/Chihuahua", MST}, ++ {"America/Chihuahua", CST}, + {"America/Creston", MST}, + {"America/Coral_Harbour", EST}, + {"America/Cordoba", AGT}, +@@ -516,7 +516,7 @@ public final class TimeZoneNames_zh_CN extends TimeZoneNamesBundle { + {"America/North_Dakota/Center", CST}, + {"America/North_Dakota/New_Salem", CST}, + {"America/Nuuk", WGT}, +- {"America/Ojinaga", MST}, ++ {"America/Ojinaga", CST}, + {"America/Panama", EST}, + {"America/Pangnirtung", EST}, + {"America/Paramaribo", new String[] {"\u82cf\u5229\u5357\u65f6\u95f4", "SRT", +diff --git a/jdk/src/share/classes/sun/util/resources/zh/TimeZoneNames_zh_TW.java b/jdk/src/share/classes/sun/util/resources/zh/TimeZoneNames_zh_TW.java +index f83e2b8..cbc9e75 100644 +--- a/jdk/src/share/classes/sun/util/resources/zh/TimeZoneNames_zh_TW.java ++++ b/jdk/src/share/classes/sun/util/resources/zh/TimeZoneNames_zh_TW.java +@@ -1,5 +1,5 @@ + /* +- * Copyright (c) 1997, 2021, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 1997, 2022, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it +@@ -427,7 +427,7 @@ public final class TimeZoneNames_zh_TW extends TimeZoneNamesBundle { + "\u6cd5\u5c6c\u572d\u4e9e\u90a3\u590f\u4ee4\u6642\u9593", "GFST", + "\u6CD5\u5C6C\u572D\u4E9E\u90A3\u6642\u9593", "GFT"}}, + {"America/Cayman", EST}, +- {"America/Chihuahua", MST}, ++ {"America/Chihuahua", CST}, + {"America/Creston", MST}, + {"America/Coral_Harbour", EST}, + {"America/Cordoba", AGT}, +@@ -516,7 +516,7 @@ public final class TimeZoneNames_zh_TW extends TimeZoneNamesBundle { + {"America/North_Dakota/Center", CST}, + {"America/North_Dakota/New_Salem", CST}, + {"America/Nuuk", WGT}, +- {"America/Ojinaga", MST}, ++ {"America/Ojinaga", CST}, + {"America/Panama", EST}, + {"America/Pangnirtung", EST}, + {"America/Paramaribo", new String[] {"\u8607\u5229\u5357\u6642\u9593", "SRT", +diff --git a/jdk/test/java/util/TimeZone/TimeZoneData/VERSION b/jdk/test/java/util/TimeZone/TimeZoneData/VERSION +index 0cad939..f29d2d9 100644 +--- a/jdk/test/java/util/TimeZone/TimeZoneData/VERSION ++++ b/jdk/test/java/util/TimeZone/TimeZoneData/VERSION +@@ -1 +1 @@ +-tzdata2022e ++tzdata2022f +diff --git a/jdk/test/java/util/TimeZone/TimeZoneData/aliases.txt b/jdk/test/java/util/TimeZone/TimeZoneData/aliases.txt +index e3ce742..24cec5a 100644 +--- a/jdk/test/java/util/TimeZone/TimeZoneData/aliases.txt ++++ b/jdk/test/java/util/TimeZone/TimeZoneData/aliases.txt +@@ -1,158 +1,7 @@ +-Link Africa/Abidjan Africa/Accra # Ghana +-Link Africa/Abidjan Africa/Bamako # Mali +-Link Africa/Abidjan Africa/Banjul # The Gambia +-Link Africa/Abidjan Africa/Conakry # Guinea +-Link Africa/Abidjan Africa/Dakar # Senegal +-Link Africa/Abidjan Africa/Freetown # Sierra Leone +-Link Africa/Abidjan Africa/Lome # Togo +-Link Africa/Abidjan Africa/Nouakchott # Mauritania +-Link Africa/Abidjan Africa/Ouagadougou # Burkina Faso +-Link Africa/Abidjan Atlantic/Reykjavik # Iceland +-Link Africa/Abidjan Atlantic/St_Helena # St Helena +-Link Africa/Nairobi Africa/Addis_Ababa # Ethiopia +-Link Africa/Nairobi Africa/Asmara # Eritrea +-Link Africa/Nairobi Africa/Dar_es_Salaam # Tanzania +-Link Africa/Nairobi Africa/Djibouti +-Link Africa/Nairobi Africa/Kampala # Uganda +-Link Africa/Nairobi Africa/Mogadishu # Somalia +-Link Africa/Nairobi Indian/Antananarivo # Madagascar +-Link Africa/Nairobi Indian/Comoro +-Link Africa/Nairobi Indian/Mayotte +-Link Africa/Maputo Africa/Blantyre # Malawi +-Link Africa/Maputo Africa/Bujumbura # Burundi +-Link Africa/Maputo Africa/Gaborone # Botswana +-Link Africa/Maputo Africa/Harare # Zimbabwe +-Link Africa/Maputo Africa/Kigali # Rwanda +-Link Africa/Maputo Africa/Lubumbashi # E Dem. Rep. of Congo +-Link Africa/Maputo Africa/Lusaka # Zambia +-Link Africa/Lagos Africa/Bangui # Central African Republic +-Link Africa/Lagos Africa/Brazzaville # Rep. of the Congo +-Link Africa/Lagos Africa/Douala # Cameroon +-Link Africa/Lagos Africa/Kinshasa # Dem. Rep. of the Congo (west) +-Link Africa/Lagos Africa/Libreville # Gabon +-Link Africa/Lagos Africa/Luanda # Angola +-Link Africa/Lagos Africa/Malabo # Equatorial Guinea +-Link Africa/Lagos Africa/Niamey # Niger +-Link Africa/Lagos Africa/Porto-Novo # Benin +-Link Africa/Johannesburg Africa/Maseru # Lesotho +-Link Africa/Johannesburg Africa/Mbabane # Eswatini +-Link Asia/Yangon Indian/Cocos +-Link Asia/Urumqi Antarctica/Vostok +-Link Asia/Nicosia Europe/Nicosia +-Link Asia/Kuching Asia/Brunei +-Link Indian/Maldives Indian/Kerguelen +-Link Asia/Qatar Asia/Bahrain +-Link Asia/Riyadh Antarctica/Syowa +-Link Asia/Riyadh Asia/Aden # Yemen +-Link Asia/Riyadh Asia/Kuwait +-Link Asia/Singapore Asia/Kuala_Lumpur +-Link Asia/Bangkok Asia/Phnom_Penh # Cambodia +-Link Asia/Bangkok Asia/Vientiane # Laos +-Link Asia/Bangkok Indian/Christmas +-Link Asia/Dubai Asia/Muscat # Oman +-Link Asia/Dubai Indian/Mahe +-Link Asia/Dubai Indian/Reunion +-Link Pacific/Guam Pacific/Saipan # N Mariana Is +-Link Pacific/Tarawa Pacific/Funafuti +-Link Pacific/Tarawa Pacific/Majuro +-Link Pacific/Tarawa Pacific/Wake +-Link Pacific/Tarawa Pacific/Wallis +-Link Pacific/Auckland Antarctica/McMurdo +-Link Pacific/Port_Moresby Antarctica/DumontDUrville +-Link Pacific/Port_Moresby Pacific/Chuuk +-Link Pacific/Pago_Pago Pacific/Midway # in US minor outlying islands +-Link Pacific/Guadalcanal Pacific/Pohnpei +-Link Europe/London Europe/Jersey +-Link Europe/London Europe/Guernsey +-Link Europe/London Europe/Isle_of_Man +-Link Europe/Brussels Europe/Amsterdam +-Link Europe/Brussels Europe/Luxembourg +-Link Europe/Prague Europe/Bratislava +-Link Europe/Helsinki Europe/Mariehamn +-Link Europe/Paris Europe/Monaco +-Link Europe/Berlin Arctic/Longyearbyen +-Link Europe/Berlin Europe/Copenhagen +-Link Europe/Berlin Europe/Oslo +-Link Europe/Berlin Europe/Stockholm +-Link Europe/Rome Europe/Vatican +-Link Europe/Rome Europe/San_Marino +-Link Europe/Belgrade Europe/Ljubljana # Slovenia +-Link Europe/Belgrade Europe/Podgorica # Montenegro +-Link Europe/Belgrade Europe/Sarajevo # Bosnia and Herzegovina +-Link Europe/Belgrade Europe/Skopje # North Macedonia +-Link Europe/Belgrade Europe/Zagreb # Croatia +-Link Europe/Zurich Europe/Busingen +-Link Europe/Zurich Europe/Vaduz +-Link Europe/Istanbul Asia/Istanbul # Istanbul is in both continents. +-Link America/Phoenix America/Creston +-Link America/Toronto America/Nassau +-Link America/Panama America/Atikokan +-Link America/Panama America/Cayman +-Link America/Puerto_Rico America/Anguilla +-Link America/Puerto_Rico America/Antigua +-Link America/Puerto_Rico America/Aruba +-Link America/Puerto_Rico America/Curacao +-Link America/Puerto_Rico America/Blanc-Sablon # Quebec (Lower North Shore) +-Link America/Puerto_Rico America/Dominica +-Link America/Puerto_Rico America/Grenada +-Link America/Puerto_Rico America/Guadeloupe +-Link America/Puerto_Rico America/Kralendijk # Caribbean Netherlands +-Link America/Puerto_Rico America/Lower_Princes # Sint Maarten +-Link America/Puerto_Rico America/Marigot # St Martin (French part) +-Link America/Puerto_Rico America/Montserrat +-Link America/Puerto_Rico America/Port_of_Spain # Trinidad & Tobago +-Link America/Puerto_Rico America/St_Barthelemy # St Barthélemy +-Link America/Puerto_Rico America/St_Kitts # St Kitts & Nevis +-Link America/Puerto_Rico America/St_Lucia +-Link America/Puerto_Rico America/St_Thomas # Virgin Islands (US) +-Link America/Puerto_Rico America/St_Vincent +-Link America/Puerto_Rico America/Tortola # Virgin Islands (UK) + Link Asia/Riyadh87 Mideast/Riyadh87 + Link Asia/Riyadh88 Mideast/Riyadh88 + Link Asia/Riyadh89 Mideast/Riyadh89 +-Link Africa/Nairobi Africa/Asmera +-Link Africa/Abidjan Africa/Timbuktu +-Link America/Argentina/Catamarca America/Argentina/ComodRivadavia +-Link America/Adak America/Atka +-Link America/Argentina/Buenos_Aires America/Buenos_Aires +-Link America/Argentina/Catamarca America/Catamarca +-Link America/Panama America/Coral_Harbour +-Link America/Argentina/Cordoba America/Cordoba +-Link America/Tijuana America/Ensenada +-Link America/Indiana/Indianapolis America/Fort_Wayne +-Link America/Nuuk America/Godthab +-Link America/Indiana/Indianapolis America/Indianapolis +-Link America/Argentina/Jujuy America/Jujuy +-Link America/Indiana/Knox America/Knox_IN +-Link America/Kentucky/Louisville America/Louisville +-Link America/Argentina/Mendoza America/Mendoza +-Link America/Toronto America/Montreal +-Link America/Rio_Branco America/Porto_Acre +-Link America/Argentina/Cordoba America/Rosario +-Link America/Tijuana America/Santa_Isabel +-Link America/Denver America/Shiprock +-Link America/Puerto_Rico America/Virgin +-Link Pacific/Auckland Antarctica/South_Pole +-Link Asia/Ashgabat Asia/Ashkhabad +-Link Asia/Kolkata Asia/Calcutta +-Link Asia/Shanghai Asia/Chongqing +-Link Asia/Shanghai Asia/Chungking +-Link Asia/Dhaka Asia/Dacca +-Link Asia/Shanghai Asia/Harbin +-Link Asia/Urumqi Asia/Kashgar +-Link Asia/Kathmandu Asia/Katmandu +-Link Asia/Macau Asia/Macao +-Link Asia/Yangon Asia/Rangoon +-Link Asia/Ho_Chi_Minh Asia/Saigon +-Link Asia/Jerusalem Asia/Tel_Aviv +-Link Asia/Thimphu Asia/Thimbu +-Link Asia/Makassar Asia/Ujung_Pandang +-Link Asia/Ulaanbaatar Asia/Ulan_Bator +-Link Atlantic/Faroe Atlantic/Faeroe +-Link Europe/Berlin Atlantic/Jan_Mayen +-Link Australia/Sydney Australia/ACT +-Link Australia/Sydney Australia/Canberra +-Link Australia/Hobart Australia/Currie ++Link Australia/Sydney Australia/ACT #= Australia/Canberra + Link Australia/Lord_Howe Australia/LHI + Link Australia/Sydney Australia/NSW + Link Australia/Darwin Australia/North +@@ -162,7 +11,7 @@ Link Australia/Hobart Australia/Tasmania + Link Australia/Melbourne Australia/Victoria + Link Australia/Perth Australia/West + Link Australia/Broken_Hill Australia/Yancowinna +-Link America/Rio_Branco Brazil/Acre ++Link America/Rio_Branco Brazil/Acre #= America/Porto_Acre + Link America/Noronha Brazil/DeNoronha + Link America/Sao_Paulo Brazil/East + Link America/Manaus Brazil/West +@@ -179,12 +28,13 @@ Link Pacific/Easter Chile/EasterIsland + Link America/Havana Cuba + Link Africa/Cairo Egypt + Link Europe/Dublin Eire ++Link Etc/GMT Etc/GMT+0 ++Link Etc/GMT Etc/GMT-0 ++Link Etc/GMT Etc/GMT0 ++Link Etc/GMT Etc/Greenwich + Link Etc/UTC Etc/UCT +-Link Europe/London Europe/Belfast +-Link Europe/Kyiv Europe/Kiev +-Link Europe/Chisinau Europe/Tiraspol +-Link Europe/Kyiv Europe/Uzhgorod +-Link Europe/Kyiv Europe/Zaporozhye ++Link Etc/UTC Etc/Universal ++Link Etc/UTC Etc/Zulu + Link Europe/London GB + Link Europe/London GB-Eire + Link Etc/GMT GMT+0 +@@ -192,7 +42,7 @@ Link Etc/GMT GMT-0 + Link Etc/GMT GMT0 + Link Etc/GMT Greenwich + Link Asia/Hong_Kong Hongkong +-Link Africa/Abidjan Iceland ++Link Africa/Abidjan Iceland #= Atlantic/Reykjavik + Link Asia/Tehran Iran + Link Asia/Jerusalem Israel + Link America/Jamaica Jamaica +@@ -204,14 +54,8 @@ Link America/Mazatlan Mexico/BajaSur + Link America/Mexico_City Mexico/General + Link Pacific/Auckland NZ + Link Pacific/Chatham NZ-CHAT +-Link America/Denver Navajo ++Link America/Denver Navajo #= America/Shiprock + Link Asia/Shanghai PRC +-Link Pacific/Kanton Pacific/Enderbury +-Link Pacific/Honolulu Pacific/Johnston +-Link Pacific/Guadalcanal Pacific/Ponape +-Link Pacific/Pago_Pago Pacific/Samoa +-Link Pacific/Port_Moresby Pacific/Truk +-Link Pacific/Port_Moresby Pacific/Yap + Link Europe/Warsaw Poland + Link Europe/Lisbon Portugal + Link Asia/Taipei ROC +@@ -235,3 +79,168 @@ Link Etc/UTC UTC + Link Etc/UTC Universal + Link Europe/Moscow W-SU + Link Etc/UTC Zulu ++Link America/Argentina/Buenos_Aires America/Buenos_Aires ++Link America/Argentina/Catamarca America/Catamarca ++Link America/Argentina/Cordoba America/Cordoba ++Link America/Indiana/Indianapolis America/Indianapolis ++Link America/Argentina/Jujuy America/Jujuy ++Link America/Indiana/Knox America/Knox_IN ++Link America/Kentucky/Louisville America/Louisville ++Link America/Argentina/Mendoza America/Mendoza ++Link America/Puerto_Rico America/Virgin #= America/St_Thomas ++Link Pacific/Pago_Pago Pacific/Samoa ++Link Africa/Abidjan Africa/Accra ++Link Africa/Nairobi Africa/Addis_Ababa ++Link Africa/Nairobi Africa/Asmara ++Link Africa/Abidjan Africa/Bamako ++Link Africa/Lagos Africa/Bangui ++Link Africa/Abidjan Africa/Banjul ++Link Africa/Maputo Africa/Blantyre ++Link Africa/Lagos Africa/Brazzaville ++Link Africa/Maputo Africa/Bujumbura ++Link Africa/Abidjan Africa/Conakry ++Link Africa/Abidjan Africa/Dakar ++Link Africa/Nairobi Africa/Dar_es_Salaam ++Link Africa/Nairobi Africa/Djibouti ++Link Africa/Lagos Africa/Douala ++Link Africa/Abidjan Africa/Freetown ++Link Africa/Maputo Africa/Gaborone ++Link Africa/Maputo Africa/Harare ++Link Africa/Nairobi Africa/Kampala ++Link Africa/Maputo Africa/Kigali ++Link Africa/Lagos Africa/Kinshasa ++Link Africa/Lagos Africa/Libreville ++Link Africa/Abidjan Africa/Lome ++Link Africa/Lagos Africa/Luanda ++Link Africa/Maputo Africa/Lubumbashi ++Link Africa/Maputo Africa/Lusaka ++Link Africa/Lagos Africa/Malabo ++Link Africa/Johannesburg Africa/Maseru ++Link Africa/Johannesburg Africa/Mbabane ++Link Africa/Nairobi Africa/Mogadishu ++Link Africa/Lagos Africa/Niamey ++Link Africa/Abidjan Africa/Nouakchott ++Link Africa/Abidjan Africa/Ouagadougou ++Link Africa/Lagos Africa/Porto-Novo ++Link America/Puerto_Rico America/Anguilla ++Link America/Puerto_Rico America/Antigua ++Link America/Puerto_Rico America/Aruba ++Link America/Panama America/Atikokan ++Link America/Puerto_Rico America/Blanc-Sablon ++Link America/Panama America/Cayman ++Link America/Phoenix America/Creston ++Link America/Puerto_Rico America/Curacao ++Link America/Puerto_Rico America/Dominica ++Link America/Puerto_Rico America/Grenada ++Link America/Puerto_Rico America/Guadeloupe ++Link America/Puerto_Rico America/Kralendijk ++Link America/Puerto_Rico America/Lower_Princes ++Link America/Puerto_Rico America/Marigot ++Link America/Puerto_Rico America/Montserrat ++Link America/Toronto America/Nassau ++Link America/Puerto_Rico America/Port_of_Spain ++Link America/Puerto_Rico America/St_Barthelemy ++Link America/Puerto_Rico America/St_Kitts ++Link America/Puerto_Rico America/St_Lucia ++Link America/Puerto_Rico America/St_Thomas ++Link America/Puerto_Rico America/St_Vincent ++Link America/Puerto_Rico America/Tortola ++Link Pacific/Port_Moresby Antarctica/DumontDUrville ++Link Pacific/Auckland Antarctica/McMurdo ++Link Asia/Riyadh Antarctica/Syowa ++Link Asia/Urumqi Antarctica/Vostok ++Link Europe/Berlin Arctic/Longyearbyen ++Link Asia/Riyadh Asia/Aden ++Link Asia/Qatar Asia/Bahrain ++Link Asia/Kuching Asia/Brunei ++Link Asia/Singapore Asia/Kuala_Lumpur ++Link Asia/Riyadh Asia/Kuwait ++Link Asia/Dubai Asia/Muscat ++Link Asia/Bangkok Asia/Phnom_Penh ++Link Asia/Bangkok Asia/Vientiane ++Link Africa/Abidjan Atlantic/Reykjavik ++Link Africa/Abidjan Atlantic/St_Helena ++Link Europe/Brussels Europe/Amsterdam ++Link Europe/Prague Europe/Bratislava ++Link Europe/Zurich Europe/Busingen ++Link Europe/Berlin Europe/Copenhagen ++Link Europe/London Europe/Guernsey ++Link Europe/London Europe/Isle_of_Man ++Link Europe/London Europe/Jersey ++Link Europe/Belgrade Europe/Ljubljana ++Link Europe/Brussels Europe/Luxembourg ++Link Europe/Helsinki Europe/Mariehamn ++Link Europe/Paris Europe/Monaco ++Link Europe/Berlin Europe/Oslo ++Link Europe/Belgrade Europe/Podgorica ++Link Europe/Rome Europe/San_Marino ++Link Europe/Belgrade Europe/Sarajevo ++Link Europe/Belgrade Europe/Skopje ++Link Europe/Berlin Europe/Stockholm ++Link Europe/Zurich Europe/Vaduz ++Link Europe/Rome Europe/Vatican ++Link Europe/Belgrade Europe/Zagreb ++Link Africa/Nairobi Indian/Antananarivo ++Link Asia/Bangkok Indian/Christmas ++Link Asia/Yangon Indian/Cocos ++Link Africa/Nairobi Indian/Comoro ++Link Indian/Maldives Indian/Kerguelen ++Link Asia/Dubai Indian/Mahe ++Link Africa/Nairobi Indian/Mayotte ++Link Asia/Dubai Indian/Reunion ++Link Pacific/Port_Moresby Pacific/Chuuk ++Link Pacific/Tarawa Pacific/Funafuti ++Link Pacific/Tarawa Pacific/Majuro ++Link Pacific/Pago_Pago Pacific/Midway ++Link Pacific/Guadalcanal Pacific/Pohnpei ++Link Pacific/Guam Pacific/Saipan ++Link Pacific/Tarawa Pacific/Wake ++Link Pacific/Tarawa Pacific/Wallis ++Link Africa/Abidjan Africa/Timbuktu ++Link America/Argentina/Catamarca America/Argentina/ComodRivadavia ++Link America/Adak America/Atka ++Link America/Panama America/Coral_Harbour ++Link America/Tijuana America/Ensenada ++Link America/Indiana/Indianapolis America/Fort_Wayne ++Link America/Toronto America/Montreal ++Link America/Toronto America/Nipigon ++Link America/Rio_Branco America/Porto_Acre ++Link America/Winnipeg America/Rainy_River ++Link America/Argentina/Cordoba America/Rosario ++Link America/Tijuana America/Santa_Isabel ++Link America/Denver America/Shiprock ++Link America/Toronto America/Thunder_Bay ++Link Pacific/Auckland Antarctica/South_Pole ++Link Asia/Shanghai Asia/Chongqing ++Link Asia/Shanghai Asia/Harbin ++Link Asia/Urumqi Asia/Kashgar ++Link Asia/Jerusalem Asia/Tel_Aviv ++Link Europe/Berlin Atlantic/Jan_Mayen ++Link Australia/Sydney Australia/Canberra ++Link Australia/Hobart Australia/Currie ++Link Europe/London Europe/Belfast ++Link Europe/Chisinau Europe/Tiraspol ++Link Europe/Kyiv Europe/Uzhgorod ++Link Europe/Kyiv Europe/Zaporozhye ++Link Pacific/Kanton Pacific/Enderbury ++Link Pacific/Honolulu Pacific/Johnston ++Link Pacific/Port_Moresby Pacific/Yap ++Link Africa/Nairobi Africa/Asmera #= Africa/Asmara ++Link America/Nuuk America/Godthab ++Link Asia/Ashgabat Asia/Ashkhabad ++Link Asia/Kolkata Asia/Calcutta ++Link Asia/Shanghai Asia/Chungking #= Asia/Chongqing ++Link Asia/Dhaka Asia/Dacca ++Link Europe/Istanbul Asia/Istanbul ++Link Asia/Kathmandu Asia/Katmandu ++Link Asia/Macau Asia/Macao ++Link Asia/Yangon Asia/Rangoon ++Link Asia/Ho_Chi_Minh Asia/Saigon ++Link Asia/Thimphu Asia/Thimbu ++Link Asia/Makassar Asia/Ujung_Pandang ++Link Asia/Ulaanbaatar Asia/Ulan_Bator ++Link Atlantic/Faroe Atlantic/Faeroe ++Link Europe/Kyiv Europe/Kiev ++Link Asia/Nicosia Europe/Nicosia ++Link Pacific/Guadalcanal Pacific/Ponape #= Pacific/Pohnpei ++Link Pacific/Port_Moresby Pacific/Truk #= Pacific/Chuuk +diff --git a/jdk/test/java/util/TimeZone/TimeZoneData/displaynames.txt b/jdk/test/java/util/TimeZone/TimeZoneData/displaynames.txt +index 2f2786f..a1cd41d 100644 +--- a/jdk/test/java/util/TimeZone/TimeZoneData/displaynames.txt ++++ b/jdk/test/java/util/TimeZone/TimeZoneData/displaynames.txt +@@ -24,7 +24,7 @@ America/Boise MST MDT + America/Cambridge_Bay MST MDT + America/Cancun EST + America/Chicago CST CDT +-America/Chihuahua MST MDT ++America/Chihuahua CST + America/Costa_Rica CST CDT + America/Danmarkshavn GMT + America/Dawson MST +@@ -67,18 +67,16 @@ America/Mexico_City CST CDT + America/Moncton AST ADT + America/Monterrey CST CDT + America/New_York EST EDT +-America/Nipigon EST EDT + America/Nome AKST AKDT + America/North_Dakota/Beulah CST CDT + America/North_Dakota/Center CST CDT + America/North_Dakota/New_Salem CST CDT +-America/Ojinaga MST MDT ++America/Ojinaga CST + America/Panama EST + America/Pangnirtung EST EDT + America/Phoenix MST + America/Port-au-Prince EST EDT + America/Puerto_Rico AST +-America/Rainy_River CST CDT + America/Rankin_Inlet CST CDT + America/Regina CST + America/Resolute CST CDT +@@ -88,7 +86,6 @@ America/St_Johns NST NDT + America/Swift_Current CST + America/Tegucigalpa CST CDT + America/Thule AST ADT +-America/Thunder_Bay EST EDT + America/Tijuana PST PDT + America/Toronto EST EDT + America/Vancouver PST PDT +diff --git a/jdk/test/sun/util/calendar/zi/tzdata/VERSION b/jdk/test/sun/util/calendar/zi/tzdata/VERSION +index b8cb36e..b8d9ae7 100644 +--- a/jdk/test/sun/util/calendar/zi/tzdata/VERSION ++++ b/jdk/test/sun/util/calendar/zi/tzdata/VERSION +@@ -21,4 +21,4 @@ + # or visit www.oracle.com if you need additional information or have any + # questions. + # +-tzdata2022e ++tzdata2022f +diff --git a/jdk/test/sun/util/calendar/zi/tzdata/africa b/jdk/test/sun/util/calendar/zi/tzdata/africa +index e13899b..b4559cd 100644 +--- a/jdk/test/sun/util/calendar/zi/tzdata/africa ++++ b/jdk/test/sun/util/calendar/zi/tzdata/africa +@@ -120,22 +120,6 @@ Zone Africa/Algiers 0:12:12 - LMT 1891 Mar 16 + 0:00 Algeria WE%sT 1981 May + 1:00 - CET + +-# Angola +-# Benin +-# See Africa/Lagos. +- +-# Botswana +-# See Africa/Maputo. +- +-# Burkina Faso +-# See Africa/Abidjan. +- +-# Burundi +-# See Africa/Maputo. +- +-# Cameroon +-# See Africa/Lagos. +- + # Cape Verde / Cabo Verde + # + # From Paul Eggert (2018-02-16): +@@ -150,9 +134,6 @@ Zone Atlantic/Cape_Verde -1:34:04 - LMT 1912 Jan 01 2:00u # Praia + -2:00 - -02 1975 Nov 25 2:00 + -1:00 - -01 + +-# Central African Republic +-# See Africa/Lagos. +- + # Chad + # Zone NAME STDOFF RULES FORMAT [UNTIL] + Zone Africa/Ndjamena 1:00:12 - LMT 1912 # N'Djamena +@@ -160,33 +141,29 @@ Zone Africa/Ndjamena 1:00:12 - LMT 1912 # N'Djamena + 1:00 1:00 WAST 1980 Mar 8 + 1:00 - WAT + +-# Comoros +-# See Africa/Nairobi. +- +-# Democratic Republic of the Congo +-# See Africa/Lagos for the western part and Africa/Maputo for the eastern. ++# Burkina Faso ++# Côte d'Ivoire (Ivory Coast) ++# The Gambia ++# Ghana ++# Guinea ++# Iceland ++# Mali ++# Mauritania ++# St Helena ++# Senegal ++# Sierra Leone ++# Togo + +-# Republic of the Congo +-# See Africa/Lagos. ++# The other parts of the St Helena territory are similar: ++# Tristan da Cunha: on GMT, say Whitman and the CIA ++# Ascension: on GMT, say the USNO (1995-12-21) and the CIA ++# Gough (scientific station since 1955; sealers wintered previously): ++# on GMT, says the CIA ++# Inaccessible, Nightingale: uninhabited + +-# Côte d'Ivoire / Ivory Coast + # Zone NAME STDOFF RULES FORMAT [UNTIL] + Zone Africa/Abidjan -0:16:08 - LMT 1912 + 0:00 - GMT +-Link Africa/Abidjan Africa/Accra # Ghana +-Link Africa/Abidjan Africa/Bamako # Mali +-Link Africa/Abidjan Africa/Banjul # The Gambia +-Link Africa/Abidjan Africa/Conakry # Guinea +-Link Africa/Abidjan Africa/Dakar # Senegal +-Link Africa/Abidjan Africa/Freetown # Sierra Leone +-Link Africa/Abidjan Africa/Lome # Togo +-Link Africa/Abidjan Africa/Nouakchott # Mauritania +-Link Africa/Abidjan Africa/Ouagadougou # Burkina Faso +-Link Africa/Abidjan Atlantic/Reykjavik # Iceland +-Link Africa/Abidjan Atlantic/St_Helena # St Helena +- +-# Djibouti +-# See Africa/Nairobi. + + ############################################################################### + +@@ -382,33 +359,6 @@ Rule Egypt 2014 only - Sep lastThu 24:00 0 - + Zone Africa/Cairo 2:05:09 - LMT 1900 Oct + 2:00 Egypt EE%sT + +-# Equatorial Guinea +-# See Africa/Lagos. +- +-# Eritrea +-# See Africa/Nairobi. +- +-# Eswatini (formerly Swaziland) +-# See Africa/Johannesburg. +- +-# Ethiopia +-# See Africa/Nairobi. +-# +-# Unfortunately tzdb records only Western clock time in use in Ethiopia, +-# as the tzdb format is not up to properly recording a common Ethiopian +-# timekeeping practice that is based on solar time. See: +-# Mortada D. If you have a meeting in Ethiopia, you'd better double +-# check the time. PRI's The World. 2015-01-30 15:15 -05. +-# https://www.pri.org/stories/2015-01-30/if-you-have-meeting-ethiopia-you-better-double-check-time +- +-# Gabon +-# See Africa/Lagos. +- +-# The Gambia +-# Ghana +-# Guinea +-# See Africa/Abidjan. +- + # Guinea-Bissau + # + # From Paul Eggert (2018-02-16): +@@ -421,7 +371,16 @@ Zone Africa/Bissau -1:02:20 - LMT 1912 Jan 1 1:00u + -1:00 - -01 1975 + 0:00 - GMT + ++# Comoros ++# Djibouti ++# Eritrea ++# Ethiopia + # Kenya ++# Madagascar ++# Mayotte ++# Somalia ++# Tanzania ++# Uganda + + # From P Chan (2020-10-24): + # +@@ -464,6 +423,14 @@ Zone Africa/Bissau -1:02:20 - LMT 1912 Jan 1 1:00u + # The 1908-05-01 announcement does not give an effective date, + # so just say "1908 May". + ++# From Paul Eggert (2018-09-11): ++# Unfortunately tzdb records only Western clock time in use in Ethiopia, ++# as the tzdb format is not up to properly recording a common Ethiopian ++# timekeeping practice that is based on solar time. See: ++# Mortada D. If you have a meeting in Ethiopia, you'd better double ++# check the time. PRI's The World. 2015-01-30 15:15 -05. ++# https://www.pri.org/stories/2015-01-30/if-you-have-meeting-ethiopia-you-better-double-check-time ++ + # Zone NAME STDOFF RULES FORMAT [UNTIL] + Zone Africa/Nairobi 2:27:16 - LMT 1908 May + 2:30 - +0230 1928 Jun 30 24:00 +@@ -471,18 +438,6 @@ Zone Africa/Nairobi 2:27:16 - LMT 1908 May + 2:30 - +0230 1936 Dec 31 24:00 + 2:45 - +0245 1942 Jul 31 24:00 + 3:00 - EAT +-Link Africa/Nairobi Africa/Addis_Ababa # Ethiopia +-Link Africa/Nairobi Africa/Asmara # Eritrea +-Link Africa/Nairobi Africa/Dar_es_Salaam # Tanzania +-Link Africa/Nairobi Africa/Djibouti +-Link Africa/Nairobi Africa/Kampala # Uganda +-Link Africa/Nairobi Africa/Mogadishu # Somalia +-Link Africa/Nairobi Indian/Antananarivo # Madagascar +-Link Africa/Nairobi Indian/Comoro +-Link Africa/Nairobi Indian/Mayotte +- +-# Lesotho +-# See Africa/Johannesburg. + + # Liberia + # +@@ -563,16 +518,6 @@ Zone Africa/Tripoli 0:52:44 - LMT 1920 + 1:00 Libya CE%sT 2013 Oct 25 2:00 + 2:00 - EET + +-# Madagascar +-# See Africa/Nairobi. +- +-# Malawi +-# See Africa/Maputo. +- +-# Mali +-# Mauritania +-# See Africa/Abidjan. +- + # Mauritius + + # From Steffen Thorsen (2008-06-25): +@@ -666,12 +611,6 @@ Zone Indian/Mauritius 3:50:00 - LMT 1907 # Port Louis + # Agalega Is, Rodriguez + # no information; probably like Indian/Mauritius + +-# Mayotte +-# See Africa/Nairobi. +- +-# Morocco +-# See Africa/Ceuta for Spanish Morocco. +- + # From Alex Krivenyshev (2008-05-09): + # Here is an article that Morocco plan to introduce Daylight Saving Time between + # 1 June, 2008 and 27 September, 2008. +@@ -1160,7 +1099,14 @@ Zone Africa/El_Aaiun -0:52:48 - LMT 1934 Jan # El Aaiún + 0:00 Morocco +00/+01 2018 Oct 28 3:00 + 0:00 Morocco +00/+01 + ++# Botswana ++# Burundi ++# Democratic Republic of the Congo (eastern) ++# Malawi + # Mozambique ++# Rwanda ++# Zambia ++# Zimbabwe + # + # Shanks gives 1903-03-01 for the transition to CAT. + # Perhaps the 1911-05-26 Portuguese decree +@@ -1170,14 +1116,6 @@ Zone Africa/El_Aaiun -0:52:48 - LMT 1934 Jan # El Aaiún + # Zone NAME STDOFF RULES FORMAT [UNTIL] + Zone Africa/Maputo 2:10:20 - LMT 1903 Mar + 2:00 - CAT +-Link Africa/Maputo Africa/Blantyre # Malawi +-Link Africa/Maputo Africa/Bujumbura # Burundi +-Link Africa/Maputo Africa/Gaborone # Botswana +-Link Africa/Maputo Africa/Harare # Zimbabwe +-Link Africa/Maputo Africa/Kigali # Rwanda +-Link Africa/Maputo Africa/Lubumbashi # E Dem. Rep. of Congo +-Link Africa/Maputo Africa/Lusaka # Zambia +- + + # Namibia + +@@ -1256,9 +1194,16 @@ Zone Africa/Windhoek 1:08:24 - LMT 1892 Feb 8 + 2:00 - CAT + # End of rearguard section. + +-# Niger +-# See Africa/Lagos. + ++# Angola ++# Benin ++# Cameroon ++# Central African Republic ++# Democratic Republic of the Congo (western) ++# Republic of the Congo ++# Equatorial Guinea ++# Gabon ++# Niger + # Nigeria + + # From P Chan (2020-12-03): +@@ -1324,32 +1269,6 @@ Zone Africa/Lagos 0:13:35 - LMT 1905 Jul 1 + 0:13:35 - LMT 1914 Jan 1 + 0:30 - +0030 1919 Sep 1 + 1:00 - WAT +-Link Africa/Lagos Africa/Bangui # Central African Republic +-Link Africa/Lagos Africa/Brazzaville # Rep. of the Congo +-Link Africa/Lagos Africa/Douala # Cameroon +-Link Africa/Lagos Africa/Kinshasa # Dem. Rep. of the Congo (west) +-Link Africa/Lagos Africa/Libreville # Gabon +-Link Africa/Lagos Africa/Luanda # Angola +-Link Africa/Lagos Africa/Malabo # Equatorial Guinea +-Link Africa/Lagos Africa/Niamey # Niger +-Link Africa/Lagos Africa/Porto-Novo # Benin +- +-# Réunion +-# See Asia/Dubai. +-# +-# The Crozet Islands also observe Réunion time; see the 'antarctica' file. +- +-# Rwanda +-# See Africa/Maputo. +- +-# St Helena +-# See Africa/Abidjan. +-# The other parts of the St Helena territory are similar: +-# Tristan da Cunha: on GMT, say Whitman and the CIA +-# Ascension: on GMT, say the USNO (1995-12-21) and the CIA +-# Gough (scientific station since 1955; sealers wintered previously): +-# on GMT, says the CIA +-# Inaccessible, Nightingale: uninhabited + + # São Tomé and Príncipe + +@@ -1378,19 +1297,10 @@ Zone Africa/Sao_Tome 0:26:56 - LMT 1884 + 1:00 - WAT 2019 Jan 1 02:00 + 0:00 - GMT + +-# Senegal +-# See Africa/Abidjan. +- +-# Seychelles +-# See Asia/Dubai. +- +-# Sierra Leone +-# See Africa/Abidjan. +- +-# Somalia +-# See Africa/Nairobi. +- ++# Eswatini (Swaziland) ++# Lesotho + # South Africa ++ + # Rule NAME FROM TO - IN ON AT SAVE LETTER/S + Rule SA 1942 1943 - Sep Sun>=15 2:00 1:00 - + Rule SA 1943 1944 - Mar Sun>=15 2:00 0 - +@@ -1398,8 +1308,6 @@ Rule SA 1943 1944 - Mar Sun>=15 2:00 0 - + Zone Africa/Johannesburg 1:52:00 - LMT 1892 Feb 8 + 1:30 - SAST 1903 Mar + 2:00 SA SAST +-Link Africa/Johannesburg Africa/Maseru # Lesotho +-Link Africa/Johannesburg Africa/Mbabane # Eswatini + # + # Marion and Prince Edward Is + # scientific station since 1947 +@@ -1448,12 +1356,6 @@ Zone Africa/Juba 2:06:28 - LMT 1931 + 3:00 - EAT 2021 Feb 1 00:00 + 2:00 - CAT + +-# Tanzania +-# See Africa/Nairobi. +- +-# Togo +-# See Africa/Abidjan. +- + # Tunisia + + # From Gwillim Law (2005-04-30): +@@ -1551,10 +1453,3 @@ Rule Tunisia 2006 2008 - Oct lastSun 2:00s 0 - + Zone Africa/Tunis 0:40:44 - LMT 1881 May 12 + 0:09:21 - PMT 1911 Mar 11 # Paris Mean Time + 1:00 Tunisia CE%sT +- +-# Uganda +-# See Africa/Nairobi. +- +-# Zambia +-# Zimbabwe +-# See Africa/Maputo. +diff --git a/jdk/test/sun/util/calendar/zi/tzdata/antarctica b/jdk/test/sun/util/calendar/zi/tzdata/antarctica +index 34c302e..792542b 100644 +--- a/jdk/test/sun/util/calendar/zi/tzdata/antarctica ++++ b/jdk/test/sun/util/calendar/zi/tzdata/antarctica +@@ -329,4 +329,4 @@ Zone Antarctica/Rothera 0 - -00 1976 Dec 1 + # we have to go around and set them back 5 minutes or so. + # Maybe if we let them run fast all of the time, we'd get to leave here sooner!! + # +-# See 'australasia' for Antarctica/McMurdo. ++# See Pacific/Auckland. +diff --git a/jdk/test/sun/util/calendar/zi/tzdata/asia b/jdk/test/sun/util/calendar/zi/tzdata/asia +index f1771e4..8f1fcac 100644 +--- a/jdk/test/sun/util/calendar/zi/tzdata/asia ++++ b/jdk/test/sun/util/calendar/zi/tzdata/asia +@@ -172,9 +172,6 @@ Zone Asia/Baku 3:19:24 - LMT 1924 May 2 + 4:00 EUAsia +04/+05 1997 + 4:00 Azer +04/+05 + +-# Bahrain +-# See Asia/Qatar. +- + # Bangladesh + # From Alexander Krivenyshev (2009-05-13): + # According to newspaper Asian Tribune (May 6, 2009) Bangladesh may introduce +@@ -277,10 +274,8 @@ Zone Indian/Chagos 4:49:40 - LMT 1907 + 5:00 - +05 1996 + 6:00 - +06 + +-# Brunei +-# See Asia/Kuching. +- +-# Burma / Myanmar ++# Cocos (Keeling) Islands ++# Myanmar (Burma) + + # Milne says 6:24:40 was the meridian of the time ball observatory at Rangoon. + +@@ -296,11 +291,6 @@ Zone Asia/Yangon 6:24:47 - LMT 1880 # or Rangoon + 6:30 - +0630 1942 May + 9:00 - +09 1945 May 3 + 6:30 - +0630 +-Link Asia/Yangon Indian/Cocos +- +-# Cambodia +-# See Asia/Bangkok. +- + + # China + +@@ -688,10 +678,9 @@ Zone Asia/Shanghai 8:05:43 - LMT 1901 + 8:00 PRC C%sT + # Xinjiang time, used by many in western China; represented by Ürümqi / Ürümchi + # / Wulumuqi. (Please use Asia/Shanghai if you prefer Beijing time.) ++# Vostok base in Antarctica matches this since 1970. + Zone Asia/Urumqi 5:50:20 - LMT 1928 + 6:00 - +06 +-Link Asia/Urumqi Antarctica/Vostok +- + + # Hong Kong + +@@ -1195,10 +1184,6 @@ Zone Asia/Famagusta 2:15:48 - LMT 1921 Nov 14 + 3:00 - +03 2017 Oct 29 1:00u + 2:00 EUAsia EE%sT + +-# Classically, Cyprus belongs to Asia; e.g. see Herodotus, Histories, I.72. +-# However, for various reasons many users expect to find it under Europe. +-Link Asia/Nicosia Europe/Nicosia +- + # Georgia + # From Paul Eggert (1994-11-19): + # Today's _Economist_ (p 60) reports that Georgia moved its clocks forward +@@ -2727,14 +2712,6 @@ Zone Asia/Pyongyang 8:23:00 - LMT 1908 Apr 1 + 8:30 - KST 2018 May 4 23:30 + 9:00 - KST + +-############################################################################### +- +-# Kuwait +-# See Asia/Riyadh. +- +-# Laos +-# See Asia/Bangkok. +- + + # Lebanon + # Rule NAME FROM TO - IN ON AT SAVE LETTER/S +@@ -2766,7 +2743,9 @@ Rule Lebanon 1999 max - Oct lastSun 0:00 0 - + Zone Asia/Beirut 2:22:00 - LMT 1880 + 2:00 Lebanon EE%sT + +-# Malaysia ++# Brunei ++# Malaysia (eastern) ++# + # Rule NAME FROM TO - IN ON AT SAVE LETTER/S + Rule NBorneo 1935 1941 - Sep 14 0:00 0:20 - + Rule NBorneo 1935 1941 - Dec 14 0:00 0 - +@@ -2783,14 +2762,12 @@ Zone Asia/Kuching 7:21:20 - LMT 1926 Mar + 8:00 NBorneo +08/+0820 1942 Feb 16 + 9:00 - +09 1945 Sep 12 + 8:00 - +08 +-Link Asia/Kuching Asia/Brunei + + # Maldives + # Zone NAME STDOFF RULES FORMAT [UNTIL] + Zone Indian/Maldives 4:54:00 - LMT 1880 # Malé + 4:54:00 - MMT 1960 # Malé Mean Time + 5:00 - +05 +-Link Indian/Maldives Indian/Kerguelen + + # Mongolia + +@@ -2953,9 +2930,6 @@ Zone Asia/Kathmandu 5:41:16 - LMT 1920 + 5:30 - +0530 1986 + 5:45 - +0545 + +-# Oman +-# See Asia/Dubai. +- + # Pakistan + + # From Rives McDow (2002-03-13): +@@ -3566,14 +3540,18 @@ Zone Asia/Manila -15:56:00 - LMT 1844 Dec 31 + 9:00 - JST 1944 Nov + 8:00 Phil P%sT + ++# Bahrain + # Qatar + # Zone NAME STDOFF RULES FORMAT [UNTIL] + Zone Asia/Qatar 3:26:08 - LMT 1920 # Al Dawhah / Doha + 4:00 - +04 1972 Jun + 3:00 - +03 +-Link Asia/Qatar Asia/Bahrain + ++# Kuwait + # Saudi Arabia ++# Yemen ++# ++# Japan's year-round bases in Antarctica match this since 1970. + # + # From Paul Eggert (2018-08-29): + # Time in Saudi Arabia and other countries in the Arabian peninsula was not +@@ -3618,9 +3596,6 @@ Link Asia/Qatar Asia/Bahrain + # Zone NAME STDOFF RULES FORMAT [UNTIL] + Zone Asia/Riyadh 3:06:52 - LMT 1947 Mar 14 + 3:00 - +03 +-Link Asia/Riyadh Antarctica/Syowa +-Link Asia/Riyadh Asia/Aden # Yemen +-Link Asia/Riyadh Asia/Kuwait + + # Singapore + # taken from Mok Ly Yng (2003-10-30) +@@ -3635,7 +3610,6 @@ Zone Asia/Singapore 6:55:25 - LMT 1901 Jan 1 + 9:00 - +09 1945 Sep 12 + 7:30 - +0730 1982 Jan 1 + 8:00 - +08 +-Link Asia/Singapore Asia/Kuala_Lumpur + + # Spratly Is + # no information +@@ -3881,14 +3855,15 @@ Zone Asia/Dushanbe 4:35:12 - LMT 1924 May 2 + 5:00 1:00 +06 1991 Sep 9 2:00s + 5:00 - +05 + ++# Cambodia ++# Christmas I ++# Laos + # Thailand ++# Vietnam (northern) + # Zone NAME STDOFF RULES FORMAT [UNTIL] + Zone Asia/Bangkok 6:42:04 - LMT 1880 + 6:42:04 - BMT 1920 Apr # Bangkok Mean Time + 7:00 - +07 +-Link Asia/Bangkok Asia/Phnom_Penh # Cambodia +-Link Asia/Bangkok Asia/Vientiane # Laos +-Link Asia/Bangkok Indian/Christmas + + # Turkmenistan + # From Shanks & Pottenger. +@@ -3899,13 +3874,15 @@ Zone Asia/Ashgabat 3:53:32 - LMT 1924 May 2 # or Ashkhabad + 4:00 RussiaAsia +04/+05 1992 Jan 19 2:00 + 5:00 - +05 + ++# Oman ++# Réunion ++# Seychelles + # United Arab Emirates ++# ++# The Crozet Is also observe Réunion time; see the 'antarctica' file. + # Zone NAME STDOFF RULES FORMAT [UNTIL] + Zone Asia/Dubai 3:41:12 - LMT 1920 + 4:00 - +04 +-Link Asia/Dubai Asia/Muscat # Oman +-Link Asia/Dubai Indian/Mahe +-Link Asia/Dubai Indian/Reunion + + # Uzbekistan + # Byalokoz 1919 says Uzbekistan was 4:27:53. +@@ -3925,7 +3902,7 @@ Zone Asia/Tashkent 4:37:11 - LMT 1924 May 2 + 5:00 RussiaAsia +05/+06 1992 + 5:00 - +05 + +-# Vietnam ++# Vietnam (southern) + + # From Paul Eggert (2014-10-04): + # Milne gives 7:16:56 for the meridian of Saigon in 1899, as being +@@ -3999,7 +3976,3 @@ Zone Asia/Ho_Chi_Minh 7:06:30 - LMT 1906 Jul 1 + # For timestamps in north Vietnam back to 1970 (the tzdb cutoff), + # use Asia/Bangkok; see the VN entries in the file zone1970.tab. + # For timestamps before 1970, see Asia/Hanoi in the file 'backzone'. +- +- +-# Yemen +-# See Asia/Riyadh. +diff --git a/jdk/test/sun/util/calendar/zi/tzdata/australasia b/jdk/test/sun/util/calendar/zi/tzdata/australasia +index 019cd77..fbe3b8a 100644 +--- a/jdk/test/sun/util/calendar/zi/tzdata/australasia ++++ b/jdk/test/sun/util/calendar/zi/tzdata/australasia +@@ -274,13 +274,6 @@ Zone Antarctica/Macquarie 0 - -00 1899 Nov + 10:00 1:00 AEDT 2011 + 10:00 AT AE%sT + +-# Christmas +-# See Asia/Bangkok. +- +-# Cocos (Keeling) Is +-# See Asia/Yangon. +- +- + # Fiji + + # Milne gives 11:55:44 for Suva. +@@ -416,8 +409,14 @@ Zone Antarctica/Macquarie 0 - -00 1899 Nov + # concerned shifting arrival and departure times, which may look like a simple + # thing but requires some significant logistical adjustments domestically and + # internationally." +-# Assume for now that DST will resume with the recent pre-2020 rules for the +-# 2022/2023 season. ++ ++# From Shalvin Narayan (2022-10-27): ++# Please note that there will not be any daylight savings time change ++# in Fiji for 2022-2023.... ++# https://www.facebook.com/FijianGovernment/posts/pfbid0mmWVTYmTibn66ybpFda75pDcf34SSpoSaskJW5gXwaKo5Sgc7273Q4fXWc6kQV6Hl ++# ++# From Paul Eggert (2022-10-27): ++# For now, assume DST is suspended indefinitely. + + # Rule NAME FROM TO - IN ON AT SAVE LETTER/S + Rule Fiji 1998 1999 - Nov Sun>=1 2:00 1:00 - +@@ -432,8 +431,6 @@ Rule Fiji 2014 2018 - Nov Sun>=1 2:00 1:00 - + Rule Fiji 2015 2021 - Jan Sun>=12 3:00 0 - + Rule Fiji 2019 only - Nov Sun>=8 2:00 1:00 - + Rule Fiji 2020 only - Dec 20 2:00 1:00 - +-Rule Fiji 2022 max - Nov Sun>=8 2:00 1:00 - +-Rule Fiji 2023 max - Jan Sun>=12 3:00 0 - + # Zone NAME STDOFF RULES FORMAT [UNTIL] + Zone Pacific/Fiji 11:55:44 - LMT 1915 Oct 26 # Suva + 12:00 Fiji +12/+13 +@@ -449,7 +446,9 @@ Zone Pacific/Tahiti -9:58:16 - LMT 1912 Oct # Papeete + # Clipperton (near North America) is administered from French Polynesia; + # it is uninhabited. + ++ + # Guam ++# N Mariana Is + + # Rule NAME FROM TO - IN ON AT SAVE LETTER/S + # http://guamlegislature.com/Public_Laws_5th/PL05-025.pdf +@@ -489,17 +488,20 @@ Zone Pacific/Guam -14:21:00 - LMT 1844 Dec 31 + 9:00 - +09 1944 Jul 31 + 10:00 Guam G%sT 2000 Dec 23 + 10:00 - ChST # Chamorro Standard Time +-Link Pacific/Guam Pacific/Saipan # N Mariana Is + +-# Kiribati ++ ++# Kiribati (Gilbert Is) ++# Marshall Is ++# Tuvalu ++# Wake ++# Wallis & Futuna + # Zone NAME STDOFF RULES FORMAT [UNTIL] + Zone Pacific/Tarawa 11:32:04 - LMT 1901 # Bairiki + 12:00 - +12 +-Link Pacific/Tarawa Pacific/Funafuti +-Link Pacific/Tarawa Pacific/Majuro +-Link Pacific/Tarawa Pacific/Wake +-Link Pacific/Tarawa Pacific/Wallis + ++# Kiribati (except Gilbert Is) ++# See Pacific/Tarawa for the Gilbert Is. ++# Zone NAME STDOFF RULES FORMAT [UNTIL] + Zone Pacific/Kanton 0 - -00 1937 Aug 31 + -12:00 - -12 1979 Oct + -11:00 - -11 1994 Dec 31 +@@ -509,9 +511,6 @@ Zone Pacific/Kiritimati -10:29:20 - LMT 1901 + -10:00 - -10 1994 Dec 31 + 14:00 - +14 + +-# N Mariana Is +-# See Pacific/Guam. +- + # Marshall Is + # See Pacific/Tarawa for most locations. + # Zone NAME STDOFF RULES FORMAT [UNTIL] +@@ -561,6 +560,7 @@ Zone Pacific/Noumea 11:05:48 - LMT 1912 Jan 13 # Nouméa + ############################################################################### + + # New Zealand ++# McMurdo Station and Scott Base in Antarctica use Auckland time. + + # Rule NAME FROM TO - IN ON AT SAVE LETTER/S + Rule NZ 1927 only - Nov 6 2:00 1:00 S +@@ -596,7 +596,6 @@ Rule Chatham 2008 max - Apr Sun>=1 2:45s 0 - + Zone Pacific/Auckland 11:39:04 - LMT 1868 Nov 2 + 11:30 NZ NZ%sT 1946 Jan 1 + 12:00 NZ NZ%sT +-Link Pacific/Auckland Antarctica/McMurdo + + Zone Pacific/Chatham 12:13:48 - LMT 1868 Nov 2 + 12:15 - +1215 1946 Jan 1 +@@ -695,8 +694,6 @@ Zone Pacific/Palau -15:02:04 - LMT 1844 Dec 31 # Koror + Zone Pacific/Port_Moresby 9:48:40 - LMT 1880 + 9:48:32 - PMMT 1895 # Port Moresby Mean Time + 10:00 - +10 +-Link Pacific/Port_Moresby Antarctica/DumontDUrville +-Link Pacific/Port_Moresby Pacific/Chuuk + # + # From Paul Eggert (2014-10-13): + # Base the Bougainville entry on the Arawa-Kieta region, which appears to have +@@ -729,10 +726,10 @@ Zone Pacific/Pitcairn -8:40:20 - LMT 1901 # Adamstown + -8:00 - -08 + + # American Samoa ++# Midway + Zone Pacific/Pago_Pago 12:37:12 - LMT 1892 Jul 5 + -11:22:48 - LMT 1911 + -11:00 - SST # S=Samoa +-Link Pacific/Pago_Pago Pacific/Midway # in US minor outlying islands + + # Samoa (formerly and also known as Western Samoa) + +@@ -824,7 +821,6 @@ Zone Pacific/Apia 12:33:04 - LMT 1892 Jul 5 + # Zone NAME STDOFF RULES FORMAT [UNTIL] + Zone Pacific/Guadalcanal 10:39:48 - LMT 1912 Oct # Honiara + 11:00 - +11 +-Link Pacific/Guadalcanal Pacific/Pohnpei + + # Tokelau + # +@@ -864,9 +860,6 @@ Zone Pacific/Tongatapu 12:19:12 - LMT 1945 Sep 10 + 13:00 - +13 1999 + 13:00 Tonga +13/+14 + +-# Tuvalu +-# See Pacific/Tarawa. +- + + # US minor outlying islands + +@@ -917,15 +910,9 @@ Zone Pacific/Tongatapu 12:19:12 - LMT 1945 Sep 10 + # Kingman + # uninhabited + +-# Midway +-# See Pacific/Pago_Pago. +- + # Palmyra + # uninhabited since World War II; was probably like Pacific/Kiritimati + +-# Wake +-# See Pacific/Tarawa. +- + + # Vanuatu + +@@ -962,9 +949,6 @@ Rule Vanuatu 1992 only - Oct Sat>=22 24:00 1:00 - + Zone Pacific/Efate 11:13:16 - LMT 1912 Jan 13 # Vila + 11:00 Vanuatu +11/+12 + +-# Wallis and Futuna +-# See Pacific/Tarawa. +- + ############################################################################### + + # NOTES +diff --git a/jdk/test/sun/util/calendar/zi/tzdata/backward b/jdk/test/sun/util/calendar/zi/tzdata/backward +index 7765d99..1fb087a 100644 +--- a/jdk/test/sun/util/calendar/zi/tzdata/backward ++++ b/jdk/test/sun/util/calendar/zi/tzdata/backward +@@ -27,7 +27,7 @@ + # 2009-05-17 by Arthur David Olson. + + # This file provides links from old or merged timezone names to current ones. +-# Many names changed in late 1993, and many merged names moved here ++# Many names changed in 1993 and in 1995, and many merged names moved here + # in the period from 2013 through 2022. Several of these names are + # also present in the file 'backzone', which has data important only + # for pre-1970 timestamps and so is out of scope for tzdb proper. +@@ -36,50 +36,24 @@ + # building with 'make BACKWARD=', in practice downstream users + # typically use this file for backward compatibility. + +-# Link TARGET LINK-NAME +-Link Africa/Nairobi Africa/Asmera +-Link Africa/Abidjan Africa/Timbuktu +-Link America/Argentina/Catamarca America/Argentina/ComodRivadavia +-Link America/Adak America/Atka +-Link America/Argentina/Buenos_Aires America/Buenos_Aires +-Link America/Argentina/Catamarca America/Catamarca +-Link America/Panama America/Coral_Harbour +-Link America/Argentina/Cordoba America/Cordoba +-Link America/Tijuana America/Ensenada +-Link America/Indiana/Indianapolis America/Fort_Wayne +-Link America/Nuuk America/Godthab +-Link America/Indiana/Indianapolis America/Indianapolis +-Link America/Argentina/Jujuy America/Jujuy +-Link America/Indiana/Knox America/Knox_IN +-Link America/Kentucky/Louisville America/Louisville +-Link America/Argentina/Mendoza America/Mendoza +-Link America/Toronto America/Montreal +-Link America/Rio_Branco America/Porto_Acre +-Link America/Argentina/Cordoba America/Rosario +-Link America/Tijuana America/Santa_Isabel +-Link America/Denver America/Shiprock +-Link America/Puerto_Rico America/Virgin +-Link Pacific/Auckland Antarctica/South_Pole +-Link Asia/Ashgabat Asia/Ashkhabad +-Link Asia/Kolkata Asia/Calcutta +-Link Asia/Shanghai Asia/Chongqing +-Link Asia/Shanghai Asia/Chungking +-Link Asia/Dhaka Asia/Dacca +-Link Asia/Shanghai Asia/Harbin +-Link Asia/Urumqi Asia/Kashgar +-Link Asia/Kathmandu Asia/Katmandu +-Link Asia/Macau Asia/Macao +-Link Asia/Yangon Asia/Rangoon +-Link Asia/Ho_Chi_Minh Asia/Saigon +-Link Asia/Jerusalem Asia/Tel_Aviv +-Link Asia/Thimphu Asia/Thimbu +-Link Asia/Makassar Asia/Ujung_Pandang +-Link Asia/Ulaanbaatar Asia/Ulan_Bator +-Link Atlantic/Faroe Atlantic/Faeroe +-Link Europe/Berlin Atlantic/Jan_Mayen +-Link Australia/Sydney Australia/ACT +-Link Australia/Sydney Australia/Canberra +-Link Australia/Hobart Australia/Currie ++# This file is divided into sections, one for each major reason for a ++# backward compatibility link. Each section is sorted by link name. ++ ++# A "#= TARGET1" comment labels each link inserted only because some ++# .zi parsers (including tzcode through 2022e) mishandle links to links. ++# The comment says what the target would be if these parsers were fixed ++# so that data could contain links to links. For example, the line ++# "Link Australia/Sydney Australia/ACT #= Australia/Canberra" would be ++# "Link Australia/Canberra Australia/ACT" were it not that data lines ++# refrain from linking to links like Australia/Canberra, which means ++# the Australia/ACT line links instead to Australia/Sydney, ++# Australia/Canberra's target. ++ ++ ++# Pre-1993 naming conventions ++ ++# Link TARGET LINK-NAME #= TARGET1 ++Link Australia/Sydney Australia/ACT #= Australia/Canberra + Link Australia/Lord_Howe Australia/LHI + Link Australia/Sydney Australia/NSW + Link Australia/Darwin Australia/North +@@ -89,7 +63,7 @@ Link Australia/Hobart Australia/Tasmania + Link Australia/Melbourne Australia/Victoria + Link Australia/Perth Australia/West + Link Australia/Broken_Hill Australia/Yancowinna +-Link America/Rio_Branco Brazil/Acre ++Link America/Rio_Branco Brazil/Acre #= America/Porto_Acre + Link America/Noronha Brazil/DeNoronha + Link America/Sao_Paulo Brazil/East + Link America/Manaus Brazil/West +@@ -109,20 +83,36 @@ Link Pacific/Easter Chile/EasterIsland + Link America/Havana Cuba + Link Africa/Cairo Egypt + Link Europe/Dublin Eire ++# Vanguard section, for most .zi parsers. ++#Link GMT Etc/GMT ++#Link GMT Etc/GMT+0 ++#Link GMT Etc/GMT-0 ++#Link GMT Etc/GMT0 ++#Link GMT Etc/Greenwich ++# Rearguard section, for TZUpdater 2.3.2 and earlier. ++Link Etc/GMT Etc/GMT+0 ++Link Etc/GMT Etc/GMT-0 ++Link Etc/GMT Etc/GMT0 ++Link Etc/GMT Etc/Greenwich ++# End of rearguard section. + Link Etc/UTC Etc/UCT +-Link Europe/London Europe/Belfast +-Link Europe/Kyiv Europe/Kiev +-Link Europe/Chisinau Europe/Tiraspol +-Link Europe/Kyiv Europe/Uzhgorod +-Link Europe/Kyiv Europe/Zaporozhye ++Link Etc/UTC Etc/Universal ++Link Etc/UTC Etc/Zulu + Link Europe/London GB + Link Europe/London GB-Eire ++# Vanguard section, for most .zi parsers. ++#Link GMT GMT+0 ++#Link GMT GMT-0 ++#Link GMT GMT0 ++#Link GMT Greenwich ++# Rearguard section, for TZUpdater 2.3.2 and earlier. + Link Etc/GMT GMT+0 + Link Etc/GMT GMT-0 + Link Etc/GMT GMT0 + Link Etc/GMT Greenwich ++# End of rearguard section. + Link Asia/Hong_Kong Hongkong +-Link Africa/Abidjan Iceland ++Link Africa/Abidjan Iceland #= Atlantic/Reykjavik + Link Asia/Tehran Iran + Link Asia/Jerusalem Israel + Link America/Jamaica Jamaica +@@ -134,14 +124,8 @@ Link America/Mazatlan Mexico/BajaSur + Link America/Mexico_City Mexico/General + Link Pacific/Auckland NZ + Link Pacific/Chatham NZ-CHAT +-Link America/Denver Navajo ++Link America/Denver Navajo #= America/Shiprock + Link Asia/Shanghai PRC +-Link Pacific/Kanton Pacific/Enderbury +-Link Pacific/Honolulu Pacific/Johnston +-Link Pacific/Guadalcanal Pacific/Ponape +-Link Pacific/Pago_Pago Pacific/Samoa +-Link Pacific/Port_Moresby Pacific/Truk +-Link Pacific/Port_Moresby Pacific/Yap + Link Europe/Warsaw Poland + Link Europe/Lisbon Portugal + Link Asia/Taipei ROC +@@ -165,3 +149,192 @@ Link Etc/UTC UTC + Link Etc/UTC Universal + Link Europe/Moscow W-SU + Link Etc/UTC Zulu ++ ++ ++# Two-part names that were renamed mostly to three-part names in 1995 ++ ++# Link TARGET LINK-NAME #= TARGET1 ++Link America/Argentina/Buenos_Aires America/Buenos_Aires ++Link America/Argentina/Catamarca America/Catamarca ++Link America/Argentina/Cordoba America/Cordoba ++Link America/Indiana/Indianapolis America/Indianapolis ++Link America/Argentina/Jujuy America/Jujuy ++Link America/Indiana/Knox America/Knox_IN ++Link America/Kentucky/Louisville America/Louisville ++Link America/Argentina/Mendoza America/Mendoza ++Link America/Puerto_Rico America/Virgin #= America/St_Thomas ++Link Pacific/Pago_Pago Pacific/Samoa ++ ++ ++# Pre-2013 practice, which typically had a Zone per zone.tab line ++ ++# Link TARGET LINK-NAME ++Link Africa/Abidjan Africa/Accra ++Link Africa/Nairobi Africa/Addis_Ababa ++Link Africa/Nairobi Africa/Asmara ++Link Africa/Abidjan Africa/Bamako ++Link Africa/Lagos Africa/Bangui ++Link Africa/Abidjan Africa/Banjul ++Link Africa/Maputo Africa/Blantyre ++Link Africa/Lagos Africa/Brazzaville ++Link Africa/Maputo Africa/Bujumbura ++Link Africa/Abidjan Africa/Conakry ++Link Africa/Abidjan Africa/Dakar ++Link Africa/Nairobi Africa/Dar_es_Salaam ++Link Africa/Nairobi Africa/Djibouti ++Link Africa/Lagos Africa/Douala ++Link Africa/Abidjan Africa/Freetown ++Link Africa/Maputo Africa/Gaborone ++Link Africa/Maputo Africa/Harare ++Link Africa/Nairobi Africa/Kampala ++Link Africa/Maputo Africa/Kigali ++Link Africa/Lagos Africa/Kinshasa ++Link Africa/Lagos Africa/Libreville ++Link Africa/Abidjan Africa/Lome ++Link Africa/Lagos Africa/Luanda ++Link Africa/Maputo Africa/Lubumbashi ++Link Africa/Maputo Africa/Lusaka ++Link Africa/Lagos Africa/Malabo ++Link Africa/Johannesburg Africa/Maseru ++Link Africa/Johannesburg Africa/Mbabane ++Link Africa/Nairobi Africa/Mogadishu ++Link Africa/Lagos Africa/Niamey ++Link Africa/Abidjan Africa/Nouakchott ++Link Africa/Abidjan Africa/Ouagadougou ++Link Africa/Lagos Africa/Porto-Novo ++Link America/Puerto_Rico America/Anguilla ++Link America/Puerto_Rico America/Antigua ++Link America/Puerto_Rico America/Aruba ++Link America/Panama America/Atikokan ++Link America/Puerto_Rico America/Blanc-Sablon ++Link America/Panama America/Cayman ++Link America/Phoenix America/Creston ++Link America/Puerto_Rico America/Curacao ++Link America/Puerto_Rico America/Dominica ++Link America/Puerto_Rico America/Grenada ++Link America/Puerto_Rico America/Guadeloupe ++Link America/Puerto_Rico America/Kralendijk ++Link America/Puerto_Rico America/Lower_Princes ++Link America/Puerto_Rico America/Marigot ++Link America/Puerto_Rico America/Montserrat ++Link America/Toronto America/Nassau ++Link America/Puerto_Rico America/Port_of_Spain ++Link America/Puerto_Rico America/St_Barthelemy ++Link America/Puerto_Rico America/St_Kitts ++Link America/Puerto_Rico America/St_Lucia ++Link America/Puerto_Rico America/St_Thomas ++Link America/Puerto_Rico America/St_Vincent ++Link America/Puerto_Rico America/Tortola ++Link Pacific/Port_Moresby Antarctica/DumontDUrville ++Link Pacific/Auckland Antarctica/McMurdo ++Link Asia/Riyadh Antarctica/Syowa ++Link Asia/Urumqi Antarctica/Vostok ++Link Europe/Berlin Arctic/Longyearbyen ++Link Asia/Riyadh Asia/Aden ++Link Asia/Qatar Asia/Bahrain ++Link Asia/Kuching Asia/Brunei ++Link Asia/Singapore Asia/Kuala_Lumpur ++Link Asia/Riyadh Asia/Kuwait ++Link Asia/Dubai Asia/Muscat ++Link Asia/Bangkok Asia/Phnom_Penh ++Link Asia/Bangkok Asia/Vientiane ++Link Africa/Abidjan Atlantic/Reykjavik ++Link Africa/Abidjan Atlantic/St_Helena ++Link Europe/Brussels Europe/Amsterdam ++Link Europe/Prague Europe/Bratislava ++Link Europe/Zurich Europe/Busingen ++Link Europe/Berlin Europe/Copenhagen ++Link Europe/London Europe/Guernsey ++Link Europe/London Europe/Isle_of_Man ++Link Europe/London Europe/Jersey ++Link Europe/Belgrade Europe/Ljubljana ++Link Europe/Brussels Europe/Luxembourg ++Link Europe/Helsinki Europe/Mariehamn ++Link Europe/Paris Europe/Monaco ++Link Europe/Berlin Europe/Oslo ++Link Europe/Belgrade Europe/Podgorica ++Link Europe/Rome Europe/San_Marino ++Link Europe/Belgrade Europe/Sarajevo ++Link Europe/Belgrade Europe/Skopje ++Link Europe/Berlin Europe/Stockholm ++Link Europe/Zurich Europe/Vaduz ++Link Europe/Rome Europe/Vatican ++Link Europe/Belgrade Europe/Zagreb ++Link Africa/Nairobi Indian/Antananarivo ++Link Asia/Bangkok Indian/Christmas ++Link Asia/Yangon Indian/Cocos ++Link Africa/Nairobi Indian/Comoro ++Link Indian/Maldives Indian/Kerguelen ++Link Asia/Dubai Indian/Mahe ++Link Africa/Nairobi Indian/Mayotte ++Link Asia/Dubai Indian/Reunion ++Link Pacific/Port_Moresby Pacific/Chuuk ++Link Pacific/Tarawa Pacific/Funafuti ++Link Pacific/Tarawa Pacific/Majuro ++Link Pacific/Pago_Pago Pacific/Midway ++Link Pacific/Guadalcanal Pacific/Pohnpei ++Link Pacific/Guam Pacific/Saipan ++Link Pacific/Tarawa Pacific/Wake ++Link Pacific/Tarawa Pacific/Wallis ++ ++ ++# Non-zone.tab locations with timestamps since 1970 that duplicate ++# those of an existing location ++ ++# Link TARGET LINK-NAME ++Link Africa/Abidjan Africa/Timbuktu ++Link America/Argentina/Catamarca America/Argentina/ComodRivadavia ++Link America/Adak America/Atka ++Link America/Panama America/Coral_Harbour ++Link America/Tijuana America/Ensenada ++Link America/Indiana/Indianapolis America/Fort_Wayne ++Link America/Toronto America/Montreal ++Link America/Toronto America/Nipigon ++Link America/Rio_Branco America/Porto_Acre ++Link America/Winnipeg America/Rainy_River ++Link America/Argentina/Cordoba America/Rosario ++Link America/Tijuana America/Santa_Isabel ++Link America/Denver America/Shiprock ++Link America/Toronto America/Thunder_Bay ++Link Pacific/Auckland Antarctica/South_Pole ++Link Asia/Shanghai Asia/Chongqing ++Link Asia/Shanghai Asia/Harbin ++Link Asia/Urumqi Asia/Kashgar ++Link Asia/Jerusalem Asia/Tel_Aviv ++Link Europe/Berlin Atlantic/Jan_Mayen ++Link Australia/Sydney Australia/Canberra ++Link Australia/Hobart Australia/Currie ++Link Europe/London Europe/Belfast ++Link Europe/Chisinau Europe/Tiraspol ++Link Europe/Kyiv Europe/Uzhgorod ++Link Europe/Kyiv Europe/Zaporozhye ++Link Pacific/Kanton Pacific/Enderbury ++Link Pacific/Honolulu Pacific/Johnston ++Link Pacific/Port_Moresby Pacific/Yap ++ ++ ++# Alternate names for the same location ++ ++# Link TARGET LINK-NAME #= TARGET1 ++Link Africa/Nairobi Africa/Asmera #= Africa/Asmara ++Link America/Nuuk America/Godthab ++Link Asia/Ashgabat Asia/Ashkhabad ++Link Asia/Kolkata Asia/Calcutta ++Link Asia/Shanghai Asia/Chungking #= Asia/Chongqing ++Link Asia/Dhaka Asia/Dacca ++# Istanbul is in both continents. ++Link Europe/Istanbul Asia/Istanbul ++Link Asia/Kathmandu Asia/Katmandu ++Link Asia/Macau Asia/Macao ++Link Asia/Yangon Asia/Rangoon ++Link Asia/Ho_Chi_Minh Asia/Saigon ++Link Asia/Thimphu Asia/Thimbu ++Link Asia/Makassar Asia/Ujung_Pandang ++Link Asia/Ulaanbaatar Asia/Ulan_Bator ++Link Atlantic/Faroe Atlantic/Faeroe ++Link Europe/Kyiv Europe/Kiev ++# Classically, Cyprus is in Asia; e.g. see Herodotus, Histories, I.72. ++# However, for various reasons many users expect to find it under Europe. ++Link Asia/Nicosia Europe/Nicosia ++Link Pacific/Guadalcanal Pacific/Ponape #= Pacific/Pohnpei ++Link Pacific/Port_Moresby Pacific/Truk #= Pacific/Chuuk +diff --git a/jdk/test/sun/util/calendar/zi/tzdata/etcetera b/jdk/test/sun/util/calendar/zi/tzdata/etcetera +index 82ff6b4..8ae294f 100644 +--- a/jdk/test/sun/util/calendar/zi/tzdata/etcetera ++++ b/jdk/test/sun/util/calendar/zi/tzdata/etcetera +@@ -39,26 +39,23 @@ + # Do not use a POSIX TZ setting like TZ='GMT+4', which is four hours + # behind GMT but uses the completely misleading abbreviation "GMT". + +-Zone Etc/GMT 0 - GMT +- + # The following zone is used by tzcode functions like gmtime, + # which load the "UTC" file to handle seconds properly. + Zone Etc/UTC 0 - UTC + ++# Functions like gmtime load the "GMT" file to handle leap seconds properly. ++# Vanguard section, which works with most .zi parsers. ++#Zone GMT 0 - GMT ++# Rearguard section, for TZUpdater 2.3.2 and earlier. ++Zone Etc/GMT 0 - GMT ++ + # The following link uses older naming conventions, + # but it belongs here, not in the file 'backward', + # as it is needed for tzcode releases through 2022a, + # where functions like gmtime load "GMT" instead of the "Etc/UTC". + # We want this to work even on installations that omit 'backward'. + Link Etc/GMT GMT +- +-Link Etc/UTC Etc/Universal +-Link Etc/UTC Etc/Zulu +- +-Link Etc/GMT Etc/Greenwich +-Link Etc/GMT Etc/GMT-0 +-Link Etc/GMT Etc/GMT+0 +-Link Etc/GMT Etc/GMT0 ++# End of rearguard section. + + # Be consistent with POSIX TZ settings in the Zone names, + # even though this is the opposite of what many people expect. +diff --git a/jdk/test/sun/util/calendar/zi/tzdata/europe b/jdk/test/sun/util/calendar/zi/tzdata/europe +index 930cede..7b6aa13 100644 +--- a/jdk/test/sun/util/calendar/zi/tzdata/europe ++++ b/jdk/test/sun/util/calendar/zi/tzdata/europe +@@ -527,9 +527,6 @@ Zone Europe/London -0:01:15 - LMT 1847 Dec 1 + 1:00 - BST 1971 Oct 31 2:00u + 0:00 GB-Eire %s 1996 + 0:00 EU GMT/BST +-Link Europe/London Europe/Jersey +-Link Europe/London Europe/Guernsey +-Link Europe/London Europe/Isle_of_Man + + # From Paul Eggert (2018-02-15): + # In January 2018 we discovered that the negative SAVE values in the +@@ -902,6 +899,8 @@ Zone Europe/Minsk 1:50:16 - LMT 1880 + 3:00 - +03 + + # Belgium ++# Luxembourg ++# Netherlands + # + # From Michael Deckers (2019-08-25): + # The exposition in the web page +@@ -984,11 +983,6 @@ Zone Europe/Brussels 0:17:30 - LMT 1880 + 1:00 C-Eur CE%sT 1944 Sep 3 + 1:00 Belgium CE%sT 1977 + 1:00 EU CE%sT +-Link Europe/Brussels Europe/Amsterdam +-Link Europe/Brussels Europe/Luxembourg +- +-# Bosnia and Herzegovina +-# See Europe/Belgrade. + + # Bulgaria + # +@@ -1015,13 +1009,11 @@ Zone Europe/Sofia 1:33:16 - LMT 1880 + 2:00 E-Eur EE%sT 1997 + 2:00 EU EE%sT + +-# Croatia +-# See Europe/Belgrade. +- + # Cyprus + # Please see the 'asia' file for Asia/Nicosia. + +-# Czech Republic / Czechia ++# Czech Republic (Czechia) ++# Slovakia + # + # From Paul Eggert (2018-04-15): + # The source for Czech data is: Kdy začíná a končí letní čas. 2018-04-15. +@@ -1048,15 +1040,14 @@ Zone Europe/Prague 0:57:44 - LMT 1850 + # End of rearguard section. + 1:00 Czech CE%sT 1979 + 1:00 EU CE%sT +-Link Europe/Prague Europe/Bratislava +- +- +-# Denmark, Faroe Islands, and Greenland +-# For Denmark see Europe/Berlin. + ++# Faroe Is ++# Zone NAME STDOFF RULES FORMAT [UNTIL] + Zone Atlantic/Faroe -0:27:04 - LMT 1908 Jan 11 # Tórshavn + 0:00 - WET 1981 + 0:00 EU WE%sT ++ ++# Greenland + # + # From Paul Eggert (2004-10-31): + # During World War II, Germany maintained secret manned weather stations in +@@ -1282,11 +1273,8 @@ Zone Europe/Helsinki 1:39:49 - LMT 1878 May 31 + 2:00 Finland EE%sT 1983 + 2:00 EU EE%sT + +-# Åland Is +-Link Europe/Helsinki Europe/Mariehamn +- +- + # France ++# Monaco + + # From Ciro Discepolo (2000-12-20): + # +@@ -1423,9 +1411,11 @@ Zone Europe/Paris 0:09:21 - LMT 1891 Mar 16 + 0:00 France WE%sT 1945 Sep 16 3:00 + 1:00 France CE%sT 1977 + 1:00 EU CE%sT +-Link Europe/Paris Europe/Monaco + ++# Denmark + # Germany ++# Norway ++# Sweden + + # From Markus Kuhn (1998-09-29): + # The German time zone web site by the Physikalisch-Technische +@@ -1443,6 +1433,53 @@ Link Europe/Paris Europe/Monaco + # However, Moscow did not observe daylight saving in 1945, so + # this was equivalent to UT +03, not +04. + ++# Svalbard & Jan Mayen ++ ++# From Steffen Thorsen (2001-05-01): ++# Although I could not find it explicitly, it seems that Jan Mayen and ++# Svalbard have been using the same time as Norway at least since the ++# time they were declared as parts of Norway. Svalbard was declared ++# as a part of Norway by law of 1925-07-17 no 11, section 4 and Jan ++# Mayen by law of 1930-02-27 no 2, section 2. (From ++# and ++# ). The law/regulation ++# for normal/standard time in Norway is from 1894-06-29 no 1 (came ++# into operation on 1895-01-01) and Svalbard/Jan Mayen seem to be a ++# part of this law since 1925/1930. (From ++# ) I have not been ++# able to find if Jan Mayen used a different time zone (e.g. -0100) ++# before 1930. Jan Mayen has only been "inhabited" since 1921 by ++# Norwegian meteorologists and maybe used the same time as Norway ever ++# since 1921. Svalbard (Arctic/Longyearbyen) has been inhabited since ++# before 1895, and therefore probably changed the local time somewhere ++# between 1895 and 1925 (inclusive). ++ ++# From Paul Eggert (2013-09-04): ++# ++# Actually, Jan Mayen was never occupied by Germany during World War II, ++# so it must have diverged from Oslo time during the war, as Oslo was ++# keeping Berlin time. ++# ++# says that the meteorologists ++# burned down their station in 1940 and left the island, but returned in ++# 1941 with a small Norwegian garrison and continued operations despite ++# frequent air attacks from Germans. In 1943 the Americans established a ++# radiolocating station on the island, called "Atlantic City". Possibly ++# the UT offset changed during the war, but I think it unlikely that ++# Jan Mayen used German daylight-saving rules. ++# ++# Svalbard is more complicated, as it was raided in August 1941 by an ++# Allied party that evacuated the civilian population to England (says ++# ). The Svalbard FAQ ++# says that the Germans were ++# expelled on 1942-05-14. However, small parties of Germans did return, ++# and according to Wilhelm Dege's book "War North of 80" (1954) ++# http://www.ucalgary.ca/UofC/departments/UP/1-55238/1-55238-110-2.html ++# the German armed forces at the Svalbard weather station code-named ++# Haudegen did not surrender to the Allies until September 1945. ++# ++# All these events predate our cutoff date of 1970, so use Europe/Berlin ++# for these regions. + + # Rule NAME FROM TO - IN ON AT SAVE LETTER/S + Rule Germany 1946 only - Apr 14 2:00s 1:00 S +@@ -1467,11 +1504,6 @@ Zone Europe/Berlin 0:53:28 - LMT 1893 Apr + 1:00 SovietZone CE%sT 1946 + 1:00 Germany CE%sT 1980 + 1:00 EU CE%sT +-Link Europe/Berlin Arctic/Longyearbyen +-Link Europe/Berlin Europe/Copenhagen +-Link Europe/Berlin Europe/Oslo +-Link Europe/Berlin Europe/Stockholm +- + + # Georgia + # Please see the "asia" file for Asia/Tbilisi. +@@ -1590,10 +1622,9 @@ Zone Europe/Budapest 1:16:20 - LMT 1890 Nov 1 + 1:00 Hungary CE%sT 1984 + 1:00 EU CE%sT + +-# Iceland +-# See Africa/Abidjan. +- + # Italy ++# San Marino ++# Vatican City + # + # From Paul Eggert (2001-03-06): + # Sicily and Sardinia each had their own time zones from 1866 to 1893, +@@ -1712,13 +1743,6 @@ Zone Europe/Rome 0:49:56 - LMT 1866 Dec 12 + 1:00 C-Eur CE%sT 1944 Jun 4 + 1:00 Italy CE%sT 1980 + 1:00 EU CE%sT +-Link Europe/Rome Europe/Vatican +-Link Europe/Rome Europe/San_Marino +- +- +-# Kosovo +-# See Europe/Belgrade. +- + + # Latvia + +@@ -1802,10 +1826,6 @@ Zone Europe/Riga 1:36:34 - LMT 1880 + 2:00 - EET 2001 Jan 2 + 2:00 EU EE%sT + +-# Liechtenstein +-# See Europe/Zurich. +- +- + # Lithuania + + # From Paul Eggert (2016-03-18): +@@ -1858,12 +1878,6 @@ Zone Europe/Vilnius 1:41:16 - LMT 1880 + 2:00 - EET 2003 Jan 1 + 2:00 EU EE%sT + +-# Luxembourg +-# See Europe/Brussels. +- +-# North Macedonia +-# See Europe/Belgrade. +- + # Malta + # + # From Paul Eggert (2016-10-21): +@@ -1959,67 +1973,6 @@ Zone Europe/Chisinau 1:55:20 - LMT 1880 + # See Romania commentary for the guessed 1997 transition to EU rules. + 2:00 Moldova EE%sT + +-# Monaco +-# See Europe/Paris. +- +-# Montenegro +-# See Europe/Belgrade. +- +-# Netherlands +-# See Europe/Brussels. +- +-# Norway +-# See Europe/Berlin. +- +-# Svalbard & Jan Mayen +- +-# From Steffen Thorsen (2001-05-01): +-# Although I could not find it explicitly, it seems that Jan Mayen and +-# Svalbard have been using the same time as Norway at least since the +-# time they were declared as parts of Norway. Svalbard was declared +-# as a part of Norway by law of 1925-07-17 no 11, section 4 and Jan +-# Mayen by law of 1930-02-27 no 2, section 2. (From +-# and +-# ). The law/regulation +-# for normal/standard time in Norway is from 1894-06-29 no 1 (came +-# into operation on 1895-01-01) and Svalbard/Jan Mayen seem to be a +-# part of this law since 1925/1930. (From +-# ) I have not been +-# able to find if Jan Mayen used a different time zone (e.g. -0100) +-# before 1930. Jan Mayen has only been "inhabited" since 1921 by +-# Norwegian meteorologists and maybe used the same time as Norway ever +-# since 1921. Svalbard (Arctic/Longyearbyen) has been inhabited since +-# before 1895, and therefore probably changed the local time somewhere +-# between 1895 and 1925 (inclusive). +- +-# From Paul Eggert (2013-09-04): +-# +-# Actually, Jan Mayen was never occupied by Germany during World War II, +-# so it must have diverged from Oslo time during the war, as Oslo was +-# keeping Berlin time. +-# +-# says that the meteorologists +-# burned down their station in 1940 and left the island, but returned in +-# 1941 with a small Norwegian garrison and continued operations despite +-# frequent air attacks from Germans. In 1943 the Americans established a +-# radiolocating station on the island, called "Atlantic City". Possibly +-# the UT offset changed during the war, but I think it unlikely that +-# Jan Mayen used German daylight-saving rules. +-# +-# Svalbard is more complicated, as it was raided in August 1941 by an +-# Allied party that evacuated the civilian population to England (says +-# ). The Svalbard FAQ +-# says that the Germans were +-# expelled on 1942-05-14. However, small parties of Germans did return, +-# and according to Wilhelm Dege's book "War North of 80" (1954) +-# http://www.ucalgary.ca/UofC/departments/UP/1-55238/1-55238-110-2.html +-# the German armed forces at the Svalbard weather station code-named +-# Haudegen did not surrender to the Allies until September 1945. +-# +-# All these events predate our cutoff date of 1970, so use Europe/Berlin +-# for these regions. +- +- + # Poland + + # The 1919 dates and times can be found in Tygodnik Urzędowy nr 1 (1919-03-20), +@@ -3301,11 +3254,13 @@ Zone Asia/Anadyr 11:49:56 - LMT 1924 May 2 + 11:00 Russia +11/+12 2011 Mar 27 2:00s + 12:00 - +12 + +- +-# San Marino +-# See Europe/Rome. +- ++# Bosnia & Herzegovina ++# Croatia ++# Kosovo ++# Montenegro ++# North Macedonia + # Serbia ++# Slovenia + # Zone NAME STDOFF RULES FORMAT [UNTIL] + Zone Europe/Belgrade 1:22:00 - LMT 1884 + 1:00 - CET 1941 Apr 18 23:00 +@@ -3317,17 +3272,6 @@ Zone Europe/Belgrade 1:22:00 - LMT 1884 + # Shanks & Pottenger don't give as much detail, so go with Koželj. + 1:00 - CET 1982 Nov 27 + 1:00 EU CE%sT +-Link Europe/Belgrade Europe/Ljubljana # Slovenia +-Link Europe/Belgrade Europe/Podgorica # Montenegro +-Link Europe/Belgrade Europe/Sarajevo # Bosnia and Herzegovina +-Link Europe/Belgrade Europe/Skopje # North Macedonia +-Link Europe/Belgrade Europe/Zagreb # Croatia +- +-# Slovakia +-# See Europe/Prague. +- +-# Slovenia +-# See Europe/Belgrade. + + # Spain + # +@@ -3434,10 +3378,11 @@ Zone Atlantic/Canary -1:01:36 - LMT 1922 Mar # Las Palmas de Gran C. + # IATA SSIM (1996-09) says the Canaries switch at 2:00u, not 1:00u. + # Ignore this for now, as the Canaries are part of the EU. + +-# Sweden +-# See Europe/Berlin. + ++# Germany (Busingen enclave) ++# Liechtenstein + # Switzerland ++# + # From Howse: + # By the end of the 18th century clocks and watches became commonplace + # and their performance improved enormously. Communities began to keep +@@ -3550,9 +3495,6 @@ Zone Europe/Zurich 0:34:08 - LMT 1853 Jul 16 # See above comment. + 0:29:46 - BMT 1894 Jun # Bern Mean Time + 1:00 Swiss CE%sT 1981 + 1:00 EU CE%sT +-Link Europe/Zurich Europe/Busingen +-Link Europe/Zurich Europe/Vaduz +- + + # Turkey + +@@ -3757,7 +3699,6 @@ Zone Europe/Istanbul 1:55:52 - LMT 1880 + 2:00 1:00 EEST 2015 Nov 8 1:00u + 2:00 EU EE%sT 2016 Sep 7 + 3:00 - +03 +-Link Europe/Istanbul Asia/Istanbul # Istanbul is in both continents. + + # Ukraine + # +@@ -3860,9 +3801,6 @@ Zone Europe/Kyiv 2:02:04 - LMT 1880 + 2:00 C-Eur EE%sT 1996 May 13 + 2:00 EU EE%sT + +-# Vatican City +-# See Europe/Rome. +- + ############################################################################### + + # One source shows that Bulgaria, Cyprus, Finland, and Greece observe DST from +diff --git a/jdk/test/sun/util/calendar/zi/tzdata/northamerica b/jdk/test/sun/util/calendar/zi/tzdata/northamerica +index ce4ee74..465e8c2 100644 +--- a/jdk/test/sun/util/calendar/zi/tzdata/northamerica ++++ b/jdk/test/sun/util/calendar/zi/tzdata/northamerica +@@ -852,7 +852,6 @@ Zone America/Phoenix -7:28:18 - LMT 1883 Nov 18 19:00u + -7:00 - MST 1967 + -7:00 US M%sT 1968 Mar 21 + -7:00 - MST +-Link America/Phoenix America/Creston + + # From Arthur David Olson (1988-02-13): + # A writer from the Inter Tribal Council of Arizona, Inc., +@@ -1626,23 +1625,6 @@ Zone America/Moncton -4:19:08 - LMT 1883 Dec 9 + + # Ontario + +-# From Paul Eggert (2006-07-09): +-# Shanks & Pottenger write that since 1970 most of Ontario has been like +-# Toronto. +-# Thunder Bay skipped DST in 1973. +-# Many smaller locales did not observe peacetime DST until 1974; +-# Nipigon (EST) and Rainy River (CST) are the largest that we know of. +-# Far west Ontario is like Winnipeg; far east Quebec is like Halifax. +- +-# From Jeffery Nichols (2020-02-06): +-# According to the [Shanks] atlas, those western Ontario zones are huge, +-# covering most of Ontario northwest of Sault Ste Marie and Timmins. +-# The zones seem to include towns bigger than the ones they're named after, +-# like Dryden in America/Rainy_River and Wawa (and maybe Attawapiskat) in +-# America/Nipigon. I assume it's too much trouble to change the name of the +-# zone (like when you found out that America/Glace_Bay includes Sydney, Nova +-# Scotia).... +- + # From Mark Brader (2003-07-26): + # [According to the Toronto Star] Orillia, Ontario, adopted DST + # effective Saturday, 1912-06-22, 22:00; the article mentions that +@@ -1663,17 +1645,6 @@ Zone America/Moncton -4:19:08 - LMT 1883 Dec 9 + + # From Mark Brader (2010-03-06): + # +-# Currently the database has: +-# +-# # Ontario +-# +-# # From Paul Eggert (2006-07-09): +-# # Shanks & Pottenger write that since 1970 most of Ontario has been like +-# # Toronto. +-# # Thunder Bay skipped DST in 1973. +-# # Many smaller locales did not observe peacetime DST until 1974; +-# # Nipigon (EST) and Rainy River (CST) are the largest that we know of. +-# + # In the (Toronto) Globe and Mail for Saturday, 1955-09-24, in the bottom + # right corner of page 1, it says that Toronto will return to standard + # time at 2 am Sunday morning (which agrees with the database), and that: +@@ -1681,10 +1652,8 @@ Zone America/Moncton -4:19:08 - LMT 1883 Dec 9 + # The one-hour setback will go into effect throughout most of Ontario, + # except in areas like Windsor which remains on standard time all year. + # +-# Windsor is, of course, a lot larger than Nipigon. +-# +-# I only came across this incidentally. I don't know if Windsor began +-# observing DST when Detroit did, or in 1974, or on some other date. ++# ... I don't know if Windsor began observing DST when Detroit did, ++# or in 1974, or on some other date. + # + # By the way, the article continues by noting that: + # +@@ -1766,23 +1735,7 @@ Rule Toronto 1951 1956 - Sep lastSun 2:00 0 S + # Toronto Star, which said that DST was ending 1971-10-31 as usual. + Rule Toronto 1957 1973 - Oct lastSun 2:00 0 S + +-# From Paul Eggert (2003-07-27): +-# Willett (1914-03) writes (p. 17) "In the Cities of Fort William, and +-# Port Arthur, Ontario, the principle of the Bill has been in +-# operation for the past three years, and in the City of Moose Jaw, +-# Saskatchewan, for one year." +- +-# From David Bryan via Tory Tronrud, Director/Curator, +-# Thunder Bay Museum (2003-11-12): +-# There is some suggestion, however, that, by-law or not, daylight +-# savings time was being practiced in Fort William and Port Arthur +-# before 1909.... [I]n 1910, the line between the Eastern and Central +-# Time Zones was permanently moved about two hundred miles west to +-# include the Thunder Bay area.... When Canada adopted daylight +-# savings time in 1916, Fort William and Port Arthur, having done so +-# already, did not change their clocks.... During the Second World +-# War,... [t]he cities agreed to implement DST during the summer +-# months for the remainder of the war years. ++# The Bahamas match Toronto since 1970. + + # Zone NAME STDOFF RULES FORMAT [UNTIL] + Zone America/Toronto -5:17:32 - LMT 1895 +@@ -1791,22 +1744,6 @@ Zone America/Toronto -5:17:32 - LMT 1895 + -5:00 Canada E%sT 1946 + -5:00 Toronto E%sT 1974 + -5:00 Canada E%sT +-Link America/Toronto America/Nassau +-Zone America/Thunder_Bay -5:57:00 - LMT 1895 +- -6:00 - CST 1910 +- -5:00 - EST 1942 +- -5:00 Canada E%sT 1970 +- -5:00 Toronto E%sT 1973 +- -5:00 - EST 1974 +- -5:00 Canada E%sT +-Zone America/Nipigon -5:53:04 - LMT 1895 +- -5:00 Canada E%sT 1940 Sep 29 +- -5:00 1:00 EDT 1942 Feb 9 2:00s +- -5:00 Canada E%sT +-Zone America/Rainy_River -6:18:16 - LMT 1895 +- -6:00 Canada C%sT 1940 Sep 29 +- -6:00 1:00 CDT 1942 Feb 9 2:00s +- -6:00 Canada C%sT + # For Atikokan see America/Panama. + + +@@ -2639,6 +2576,12 @@ Zone America/Dawson -9:17:40 - LMT 1900 Aug 20 + # 5- The islands, reefs and keys shall take their timezone from the + # longitude they are located at. + ++# From Paul Eggert (2022-10-28): ++# The new Mexican law was published today: ++# https://www.dof.gob.mx/nota_detalle.php?codigo=5670045&fecha=28/10/2022 ++# This abolishes DST except where US DST rules are observed, ++# and in addition changes all of Chihuahua to -06 with no DST. ++ + # Rule NAME FROM TO - IN ON AT SAVE LETTER/S + Rule Mexico 1931 only - May 1 23:00 1:00 D + Rule Mexico 1931 only - Oct 1 0:00 0 S +@@ -2654,8 +2597,8 @@ Rule Mexico 1996 2000 - Apr Sun>=1 2:00 1:00 D + Rule Mexico 1996 2000 - Oct lastSun 2:00 0 S + Rule Mexico 2001 only - May Sun>=1 2:00 1:00 D + Rule Mexico 2001 only - Sep lastSun 2:00 0 S +-Rule Mexico 2002 max - Apr Sun>=1 2:00 1:00 D +-Rule Mexico 2002 max - Oct lastSun 2:00 0 S ++Rule Mexico 2002 2022 - Apr Sun>=1 2:00 1:00 D ++Rule Mexico 2002 2022 - Oct lastSun 2:00 0 S + # Zone NAME STDOFF RULES FORMAT [UNTIL] + # Quintana Roo; represented by Cancún + Zone America/Cancun -5:47:04 - LMT 1922 Jan 1 6:00u +@@ -2708,7 +2651,8 @@ Zone America/Ojinaga -6:57:40 - LMT 1922 Jan 1 7:00u + -6:00 Mexico C%sT 1998 + -6:00 - CST 1998 Apr Sun>=1 3:00 + -7:00 Mexico M%sT 2010 +- -7:00 US M%sT ++ -7:00 US M%sT 2022 Oct 30 2:00 ++ -6:00 - CST + # Chihuahua (away from US border) + Zone America/Chihuahua -7:04:20 - LMT 1922 Jan 1 7:00u + -7:00 - MST 1927 Jun 10 23:00 +@@ -2717,7 +2661,8 @@ Zone America/Chihuahua -7:04:20 - LMT 1922 Jan 1 7:00u + -6:00 - CST 1996 + -6:00 Mexico C%sT 1998 + -6:00 - CST 1998 Apr Sun>=1 3:00 +- -7:00 Mexico M%sT ++ -7:00 Mexico M%sT 2022 Oct 30 2:00 ++ -6:00 - CST + # Sonora + Zone America/Hermosillo -7:23:52 - LMT 1922 Jan 1 7:00u + -7:00 - MST 1927 Jun 10 23:00 +@@ -2815,20 +2760,16 @@ Zone America/Tijuana -7:48:04 - LMT 1922 Jan 1 7:00u + # http://dof.gob.mx/nota_detalle.php?codigo=5127480&fecha=06/01/2010 + # It has been moved to the 'backward' file. + # ++# From Paul Eggert (2022-10-28): ++# Today's new law states that the entire state of Baja California ++# follows US DST rules, which agrees with simplifications noted above. ++# + # + # Revillagigedo Is + # no information + + ############################################################################### + +-# Anguilla +-# Antigua and Barbuda +-# See America/Puerto_Rico. +- +-# The Bahamas +-# See America/Toronto. +- +- + # Barbados + + # For 1899 Milne gives -3:58:29.2. +@@ -3041,12 +2982,6 @@ Zone Atlantic/Bermuda -4:19:18 - LMT 1890 # Hamilton + -4:00 Canada A%sT 1976 + -4:00 US A%sT + +-# Caribbean Netherlands +-# See America/Puerto_Rico. +- +-# Cayman Is +-# See America/Panama. +- + # Costa Rica + + # Milne gives -5:36:13.3 as San José mean time. +@@ -3272,9 +3207,6 @@ Zone America/Havana -5:29:28 - LMT 1890 + -5:29:36 - HMT 1925 Jul 19 12:00 # Havana MT + -5:00 Cuba C%sT + +-# Dominica +-# See America/Puerto_Rico. +- + # Dominican Republic + + # From Steffen Thorsen (2000-10-30): +@@ -3321,12 +3253,6 @@ Rule Salv 1987 1988 - Sep lastSun 0:00 0 S + Zone America/El_Salvador -5:56:48 - LMT 1921 # San Salvador + -6:00 Salv C%sT + +-# Grenada +-# Guadeloupe +-# St Barthélemy +-# St Martin (French part) +-# See America/Puerto_Rico. +- + # Guatemala + # + # From Gwillim Law (2006-04-22), after a heads-up from Oscar van Vlijmen: +@@ -3512,9 +3438,6 @@ Zone America/Martinique -4:04:20 - LMT 1890 # Fort-de-France + -4:00 1:00 ADT 1980 Sep 28 + -4:00 - AST + +-# Montserrat +-# See America/Puerto_Rico. +- + # Nicaragua + # + # This uses Shanks & Pottenger for times before 2005. +@@ -3580,44 +3503,39 @@ Zone America/Managua -5:45:08 - LMT 1890 + -5:00 - EST 1997 + -6:00 Nic C%sT + ++# Cayman Is + # Panama ++# ++# Atikokan and Coral Harbour, Canada, match Panama since 1970. + # Zone NAME STDOFF RULES FORMAT [UNTIL] + Zone America/Panama -5:18:08 - LMT 1890 + -5:19:36 - CMT 1908 Apr 22 # Colón Mean Time + -5:00 - EST +-Link America/Panama America/Atikokan +-Link America/Panama America/Cayman + ++# Anguilla ++# Antigua & Barbuda ++# Aruba ++# Caribbean Netherlands ++# Curaçao ++# Dominica ++# Grenada ++# Guadeloupe ++# Montserrat + # Puerto Rico ++# St Barthélemy ++# St Kitts-Nevis ++# Sint Maarten / St Martin ++# St Lucia ++# St Vincent & the Grenadines ++# Trinidad & Tobago ++# Virgin Is (UK & US) ++# + # There are too many San Juans elsewhere, so we'll use 'Puerto_Rico'. + # Zone NAME STDOFF RULES FORMAT [UNTIL] + Zone America/Puerto_Rico -4:24:25 - LMT 1899 Mar 28 12:00 # San Juan + -4:00 - AST 1942 May 3 + -4:00 US A%sT 1946 + -4:00 - AST +-Link America/Puerto_Rico America/Anguilla +-Link America/Puerto_Rico America/Antigua +-Link America/Puerto_Rico America/Aruba +-Link America/Puerto_Rico America/Curacao +-Link America/Puerto_Rico America/Blanc-Sablon # Quebec (Lower North Shore) +-Link America/Puerto_Rico America/Dominica +-Link America/Puerto_Rico America/Grenada +-Link America/Puerto_Rico America/Guadeloupe +-Link America/Puerto_Rico America/Kralendijk # Caribbean Netherlands +-Link America/Puerto_Rico America/Lower_Princes # Sint Maarten +-Link America/Puerto_Rico America/Marigot # St Martin (French part) +-Link America/Puerto_Rico America/Montserrat +-Link America/Puerto_Rico America/Port_of_Spain # Trinidad & Tobago +-Link America/Puerto_Rico America/St_Barthelemy # St Barthélemy +-Link America/Puerto_Rico America/St_Kitts # St Kitts & Nevis +-Link America/Puerto_Rico America/St_Lucia +-Link America/Puerto_Rico America/St_Thomas # Virgin Islands (US) +-Link America/Puerto_Rico America/St_Vincent +-Link America/Puerto_Rico America/Tortola # Virgin Islands (UK) +- +-# St Kitts-Nevis +-# St Lucia +-# See America/Puerto_Rico. + + # St Pierre and Miquelon + # There are too many St Pierres elsewhere, so we'll use 'Miquelon'. +@@ -3627,12 +3545,6 @@ Zone America/Miquelon -3:44:40 - LMT 1911 May 15 # St Pierre + -3:00 - -03 1987 + -3:00 Canada -03/-02 + +-# St Vincent and the Grenadines +-# See America/Puerto_Rico. +- +-# Sint Maarten +-# See America/Puerto_Rico. +- + # Turks and Caicos + # + # From Chris Dunn in +@@ -3702,11 +3614,6 @@ Zone America/Grand_Turk -4:44:32 - LMT 1890 + -4:00 - AST 2018 Mar 11 3:00 + -5:00 US E%sT + +-# British Virgin Is +-# US Virgin Is +-# See America/Puerto_Rico. +- +- + # Local Variables: + # coding: utf-8 + # End: +diff --git a/jdk/test/sun/util/calendar/zi/tzdata/southamerica b/jdk/test/sun/util/calendar/zi/tzdata/southamerica +index 3c0e0e2..982ad09 100644 +--- a/jdk/test/sun/util/calendar/zi/tzdata/southamerica ++++ b/jdk/test/sun/util/calendar/zi/tzdata/southamerica +@@ -608,9 +608,6 @@ Zone America/Argentina/Ushuaia -4:33:12 - LMT 1894 Oct 31 + -3:00 Arg -03/-02 2008 Oct 18 + -3:00 - -03 + +-# Aruba +-# See America/Puerto_Rico. +- + # Bolivia + # Zone NAME STDOFF RULES FORMAT [UNTIL] + Zone America/La_Paz -4:32:36 - LMT 1890 +@@ -1455,15 +1452,6 @@ Zone America/Bogota -4:56:16 - LMT 1884 Mar 13 + # Malpelo, Providencia, San Andres + # no information; probably like America/Bogota + +-# Curaçao +-# See America/Puerto_Rico. +-# +-# From Arthur David Olson (2011-06-15): +-# use links for places with new iso3166 codes. +-# The name "Lower Prince's Quarter" is both longer than fourteen characters +-# and contains an apostrophe; use "Lower_Princes".... +-# From Paul Eggert (2021-09-29): +-# These backward-compatibility links now are in the 'northamerica' file. + + # Ecuador + # +@@ -1779,9 +1767,6 @@ Zone America/Paramaribo -3:40:40 - LMT 1911 + -3:30 - -0330 1984 Oct + -3:00 - -03 + +-# Trinidad and Tobago +-# See America/Puerto_Rico. +- + # Uruguay + # From Paul Eggert (1993-11-18): + # Uruguay wins the prize for the strangest peacetime manipulation of the rules. +diff --git a/jdk/test/sun/util/calendar/zi/tzdata/zone.tab b/jdk/test/sun/util/calendar/zi/tzdata/zone.tab +index ee02519..535d1c9 100644 +--- a/jdk/test/sun/util/calendar/zi/tzdata/zone.tab ++++ b/jdk/test/sun/util/calendar/zi/tzdata/zone.tab +@@ -137,13 +137,10 @@ CA +4606-06447 America/Moncton Atlantic - New Brunswick + CA +5320-06025 America/Goose_Bay Atlantic - Labrador (most areas) + CA +5125-05707 America/Blanc-Sablon AST - QC (Lower North Shore) + CA +4339-07923 America/Toronto Eastern - ON, QC (most areas) +-CA +4901-08816 America/Nipigon Eastern - ON, QC (no DST 1967-73) +-CA +4823-08915 America/Thunder_Bay Eastern - ON (Thunder Bay) + CA +6344-06828 America/Iqaluit Eastern - NU (most east areas) + CA +6608-06544 America/Pangnirtung Eastern - NU (Pangnirtung) + CA +484531-0913718 America/Atikokan EST - ON (Atikokan); NU (Coral H) + CA +4953-09709 America/Winnipeg Central - ON (west); Manitoba +-CA +4843-09434 America/Rainy_River Central - ON (Rainy R, Ft Frances) + CA +744144-0944945 America/Resolute Central - NU (Resolute) + CA +624900-0920459 America/Rankin_Inlet Central - NU (central) + CA +5024-10439 America/Regina CST - SK (most areas) +-- +1.8.3.1 diff --git a/8296241-tz-Update-Timezone-Data-to-2022e.patch b/8296241-tz-Update-Timezone-Data-to-2022e.patch new file mode 100644 index 0000000000000000000000000000000000000000..699894c6664b1b5fdf2bf165939a65f6e7499bed --- /dev/null +++ b/8296241-tz-Update-Timezone-Data-to-2022e.patch @@ -0,0 +1,826 @@ +From d31b6120315ecc095ddffa7a5fb92c53bb70bc3b Mon Sep 17 00:00:00 2001 +From: eapen +Date: Wed, 30 Nov 2022 14:57:07 +0000 +Subject: [PATCH 08/33] I68TO2: 8296241: (tz) Update Timezone Data to 2022e +--- + jdk/make/data/tzdata/VERSION | 2 +- + jdk/make/data/tzdata/asia | 36 +++++++--- + jdk/make/data/tzdata/europe | 2 +- + jdk/make/data/tzdata/northamerica | 84 ++++++++++------------ + jdk/test/java/util/TimeZone/TimeZoneData/VERSION | 2 +- + .../util/TimeZone/TimeZoneData/displaynames.txt | 2 - + jdk/test/sun/util/calendar/zi/tzdata/VERSION | 2 +- + jdk/test/sun/util/calendar/zi/tzdata/asia | 36 +++++++--- + jdk/test/sun/util/calendar/zi/tzdata/europe | 2 +- + jdk/test/sun/util/calendar/zi/tzdata/northamerica | 84 ++++++++++------------ + 10 files changed, 135 insertions(+), 117 deletions(-) + +diff --git a/jdk/make/data/tzdata/VERSION b/jdk/make/data/tzdata/VERSION +index 889d0e6..b8cb36e 100644 +--- a/jdk/make/data/tzdata/VERSION ++++ b/jdk/make/data/tzdata/VERSION +@@ -21,4 +21,4 @@ + # or visit www.oracle.com if you need additional information or have any + # questions. + # +-tzdata2022d ++tzdata2022e +diff --git a/jdk/make/data/tzdata/asia b/jdk/make/data/tzdata/asia +index 1dc7d34..f1771e4 100644 +--- a/jdk/make/data/tzdata/asia ++++ b/jdk/make/data/tzdata/asia +@@ -2254,6 +2254,17 @@ Zone Asia/Tokyo 9:18:59 - LMT 1887 Dec 31 15:00u + # From the Arabic version, it seems to say it would be at midnight + # (assume 24:00) on the last Thursday in February, starting from 2022. + ++# From Issam Al-Zuwairi (2022-10-05): ++# The Council of Ministers in Jordan decided Wednesday 5th October 2022, ++# that daylight saving time (DST) will be throughout the year.... ++# ++# From Brian Inglis (2022-10-06): ++# https://petra.gov.jo/Include/InnerPage.jsp?ID=45567&lang=en&name=en_news ++# ++# From Paul Eggert (2022-10-05): ++# Like Syria, model this as a transition from EEST +03 (DST) to plain +03 ++# (non-DST) at the point where DST would otherwise have ended. ++ + # Rule NAME FROM TO - IN ON AT SAVE LETTER/S + Rule Jordan 1973 only - Jun 6 0:00 1:00 S + Rule Jordan 1973 1975 - Oct 1 0:00 0 - +@@ -2285,11 +2296,12 @@ Rule Jordan 2005 only - Sep lastFri 0:00s 0 - + Rule Jordan 2006 2011 - Oct lastFri 0:00s 0 - + Rule Jordan 2013 only - Dec 20 0:00 0 - + Rule Jordan 2014 2021 - Mar lastThu 24:00 1:00 S +-Rule Jordan 2014 max - Oct lastFri 0:00s 0 - +-Rule Jordan 2022 max - Feb lastThu 24:00 1:00 S ++Rule Jordan 2014 2022 - Oct lastFri 0:00s 0 - ++Rule Jordan 2022 only - Feb lastThu 24:00 1:00 S + # Zone NAME STDOFF RULES FORMAT [UNTIL] + Zone Asia/Amman 2:23:44 - LMT 1931 +- 2:00 Jordan EE%sT ++ 2:00 Jordan EE%sT 2022 Oct 28 0:00s ++ 3:00 - +03 + + + # Kazakhstan +@@ -3838,19 +3850,27 @@ Rule Syria 2007 only - Nov Fri>=1 0:00 0 - + # Our brief summary: + # https://www.timeanddate.com/news/time/syria-dst-2012.html + +-# From Arthur David Olson (2012-03-27): +-# Assume last Friday in March going forward XXX. ++# From Steffen Thorsen (2022-10-05): ++# Syria is adopting year-round DST, starting this autumn.... ++# From https://www.enabbaladi.net/archives/607812 ++# "This [the decision] came after the weekly government meeting today, ++# Tuesday 4 October ..." ++# ++# From Paul Eggert (2022-10-05): ++# Like Jordan, model this as a transition from EEST +03 (DST) to plain +03 ++# (non-DST) at the point where DST would otherwise have ended. + + Rule Syria 2008 only - Apr Fri>=1 0:00 1:00 S + Rule Syria 2008 only - Nov 1 0:00 0 - + Rule Syria 2009 only - Mar lastFri 0:00 1:00 S + Rule Syria 2010 2011 - Apr Fri>=1 0:00 1:00 S +-Rule Syria 2012 max - Mar lastFri 0:00 1:00 S +-Rule Syria 2009 max - Oct lastFri 0:00 0 - ++Rule Syria 2012 2022 - Mar lastFri 0:00 1:00 S ++Rule Syria 2009 2022 - Oct lastFri 0:00 0 - + + # Zone NAME STDOFF RULES FORMAT [UNTIL] + Zone Asia/Damascus 2:25:12 - LMT 1920 # Dimashq +- 2:00 Syria EE%sT ++ 2:00 Syria EE%sT 2022 Oct 28 0:00 ++ 3:00 - +03 + + # Tajikistan + # From Shanks & Pottenger. +diff --git a/jdk/make/data/tzdata/europe b/jdk/make/data/tzdata/europe +index 9e0a538..930cede 100644 +--- a/jdk/make/data/tzdata/europe ++++ b/jdk/make/data/tzdata/europe +@@ -3417,7 +3417,7 @@ Zone Europe/Madrid -0:14:44 - LMT 1901 Jan 1 0:00u + 0:00 Spain WE%sT 1940 Mar 16 23:00 + 1:00 Spain CE%sT 1979 + 1:00 EU CE%sT +-Zone Africa/Ceuta -0:21:16 - LMT 1900 Dec 31 23:38:44 ++Zone Africa/Ceuta -0:21:16 - LMT 1901 Jan 1 0:00u + 0:00 - WET 1918 May 6 23:00 + 0:00 1:00 WEST 1918 Oct 7 23:00 + 0:00 - WET 1924 +diff --git a/jdk/make/data/tzdata/northamerica b/jdk/make/data/tzdata/northamerica +index 114cef1..ce4ee74 100644 +--- a/jdk/make/data/tzdata/northamerica ++++ b/jdk/make/data/tzdata/northamerica +@@ -462,7 +462,7 @@ Rule Chicago 1922 1966 - Apr lastSun 2:00 1:00 D + Rule Chicago 1922 1954 - Sep lastSun 2:00 0 S + Rule Chicago 1955 1966 - Oct lastSun 2:00 0 S + # Zone NAME STDOFF RULES FORMAT [UNTIL] +-Zone America/Chicago -5:50:36 - LMT 1883 Nov 18 12:09:24 ++Zone America/Chicago -5:50:36 - LMT 1883 Nov 18 18:00u + -6:00 US C%sT 1920 + -6:00 Chicago C%sT 1936 Mar 1 2:00 + -5:00 - EST 1936 Nov 15 2:00 +@@ -471,7 +471,7 @@ Zone America/Chicago -5:50:36 - LMT 1883 Nov 18 12:09:24 + -6:00 Chicago C%sT 1967 + -6:00 US C%sT + # Oliver County, ND switched from mountain to central time on 1992-10-25. +-Zone America/North_Dakota/Center -6:45:12 - LMT 1883 Nov 18 12:14:48 ++Zone America/North_Dakota/Center -6:45:12 - LMT 1883 Nov 18 19:00u + -7:00 US M%sT 1992 Oct 25 2:00 + -6:00 US C%sT + # Morton County, ND, switched from mountain to central time on +@@ -481,7 +481,7 @@ Zone America/North_Dakota/Center -6:45:12 - LMT 1883 Nov 18 12:14:48 + # Jones, Mellette, and Todd Counties in South Dakota; + # but in practice these other counties were already observing central time. + # See . +-Zone America/North_Dakota/New_Salem -6:45:39 - LMT 1883 Nov 18 12:14:21 ++Zone America/North_Dakota/New_Salem -6:45:39 - LMT 1883 Nov 18 19:00u + -7:00 US M%sT 2003 Oct 26 2:00 + -6:00 US C%sT + +@@ -498,7 +498,7 @@ Zone America/North_Dakota/New_Salem -6:45:39 - LMT 1883 Nov 18 12:14:21 + # largest city in Mercer County). Google Maps places Beulah's city hall + # at 47° 15' 51" N, 101° 46' 40" W, which yields an offset of 6h47'07". + +-Zone America/North_Dakota/Beulah -6:47:07 - LMT 1883 Nov 18 12:12:53 ++Zone America/North_Dakota/Beulah -6:47:07 - LMT 1883 Nov 18 19:00u + -7:00 US M%sT 2010 Nov 7 2:00 + -6:00 US C%sT + +@@ -530,7 +530,7 @@ Rule Denver 1921 only - May 22 2:00 0 S + Rule Denver 1965 1966 - Apr lastSun 2:00 1:00 D + Rule Denver 1965 1966 - Oct lastSun 2:00 0 S + # Zone NAME STDOFF RULES FORMAT [UNTIL] +-Zone America/Denver -6:59:56 - LMT 1883 Nov 18 12:00:04 ++Zone America/Denver -6:59:56 - LMT 1883 Nov 18 19:00u + -7:00 US M%sT 1920 + -7:00 Denver M%sT 1942 + -7:00 US M%sT 1946 +@@ -583,7 +583,7 @@ Rule CA 1950 1966 - Apr lastSun 1:00 1:00 D + Rule CA 1950 1961 - Sep lastSun 2:00 0 S + Rule CA 1962 1966 - Oct lastSun 2:00 0 S + # Zone NAME STDOFF RULES FORMAT [UNTIL] +-Zone America/Los_Angeles -7:52:58 - LMT 1883 Nov 18 12:07:02 ++Zone America/Los_Angeles -7:52:58 - LMT 1883 Nov 18 20:00u + -8:00 US P%sT 1946 + -8:00 CA P%sT 1967 + -8:00 US P%sT +@@ -845,7 +845,7 @@ Zone Pacific/Honolulu -10:31:26 - LMT 1896 Jan 13 12:00 + # Go with the Arizona State Library instead. + + # Zone NAME STDOFF RULES FORMAT [UNTIL] +-Zone America/Phoenix -7:28:18 - LMT 1883 Nov 18 11:31:42 ++Zone America/Phoenix -7:28:18 - LMT 1883 Nov 18 19:00u + -7:00 US M%sT 1944 Jan 1 0:01 + -7:00 - MST 1944 Apr 1 0:01 + -7:00 US M%sT 1944 Oct 1 0:01 +@@ -873,7 +873,7 @@ Link America/Phoenix America/Creston + # switched four weeks late in 1974. + # + # Zone NAME STDOFF RULES FORMAT [UNTIL] +-Zone America/Boise -7:44:49 - LMT 1883 Nov 18 12:15:11 ++Zone America/Boise -7:44:49 - LMT 1883 Nov 18 20:00u + -8:00 US P%sT 1923 May 13 2:00 + -7:00 US M%sT 1974 + -7:00 - MST 1974 Feb 3 2:00 +@@ -945,7 +945,7 @@ Rule Indianapolis 1941 only - Jun 22 2:00 1:00 D + Rule Indianapolis 1941 1954 - Sep lastSun 2:00 0 S + Rule Indianapolis 1946 1954 - Apr lastSun 2:00 1:00 D + # Zone NAME STDOFF RULES FORMAT [UNTIL] +-Zone America/Indiana/Indianapolis -5:44:38 - LMT 1883 Nov 18 12:15:22 ++Zone America/Indiana/Indianapolis -5:44:38 - LMT 1883 Nov 18 18:00u + -6:00 US C%sT 1920 + -6:00 Indianapolis C%sT 1942 + -6:00 US C%sT 1946 +@@ -965,7 +965,7 @@ Rule Marengo 1951 only - Sep lastSun 2:00 0 S + Rule Marengo 1954 1960 - Apr lastSun 2:00 1:00 D + Rule Marengo 1954 1960 - Sep lastSun 2:00 0 S + # Zone NAME STDOFF RULES FORMAT [UNTIL] +-Zone America/Indiana/Marengo -5:45:23 - LMT 1883 Nov 18 12:14:37 ++Zone America/Indiana/Marengo -5:45:23 - LMT 1883 Nov 18 18:00u + -6:00 US C%sT 1951 + -6:00 Marengo C%sT 1961 Apr 30 2:00 + -5:00 - EST 1969 +@@ -989,7 +989,7 @@ Rule Vincennes 1960 only - Oct lastSun 2:00 0 S + Rule Vincennes 1961 only - Sep lastSun 2:00 0 S + Rule Vincennes 1962 1963 - Oct lastSun 2:00 0 S + # Zone NAME STDOFF RULES FORMAT [UNTIL] +-Zone America/Indiana/Vincennes -5:50:07 - LMT 1883 Nov 18 12:09:53 ++Zone America/Indiana/Vincennes -5:50:07 - LMT 1883 Nov 18 18:00u + -6:00 US C%sT 1946 + -6:00 Vincennes C%sT 1964 Apr 26 2:00 + -5:00 - EST 1969 +@@ -1009,7 +1009,7 @@ Rule Perry 1955 1960 - Sep lastSun 2:00 0 S + Rule Perry 1956 1963 - Apr lastSun 2:00 1:00 D + Rule Perry 1961 1963 - Oct lastSun 2:00 0 S + # Zone NAME STDOFF RULES FORMAT [UNTIL] +-Zone America/Indiana/Tell_City -5:47:03 - LMT 1883 Nov 18 12:12:57 ++Zone America/Indiana/Tell_City -5:47:03 - LMT 1883 Nov 18 18:00u + -6:00 US C%sT 1946 + -6:00 Perry C%sT 1964 Apr 26 2:00 + -5:00 - EST 1967 Oct 29 2:00 +@@ -1026,7 +1026,7 @@ Rule Pike 1955 1960 - Sep lastSun 2:00 0 S + Rule Pike 1956 1964 - Apr lastSun 2:00 1:00 D + Rule Pike 1961 1964 - Oct lastSun 2:00 0 S + # Zone NAME STDOFF RULES FORMAT [UNTIL] +-Zone America/Indiana/Petersburg -5:49:07 - LMT 1883 Nov 18 12:10:53 ++Zone America/Indiana/Petersburg -5:49:07 - LMT 1883 Nov 18 18:00u + -6:00 US C%sT 1955 + -6:00 Pike C%sT 1965 Apr 25 2:00 + -5:00 - EST 1966 Oct 30 2:00 +@@ -1048,7 +1048,7 @@ Rule Starke 1955 1956 - Oct lastSun 2:00 0 S + Rule Starke 1957 1958 - Sep lastSun 2:00 0 S + Rule Starke 1959 1961 - Oct lastSun 2:00 0 S + # Zone NAME STDOFF RULES FORMAT [UNTIL] +-Zone America/Indiana/Knox -5:46:30 - LMT 1883 Nov 18 12:13:30 ++Zone America/Indiana/Knox -5:46:30 - LMT 1883 Nov 18 18:00u + -6:00 US C%sT 1947 + -6:00 Starke C%sT 1962 Apr 29 2:00 + -5:00 - EST 1963 Oct 27 2:00 +@@ -1064,7 +1064,7 @@ Rule Pulaski 1946 1954 - Sep lastSun 2:00 0 S + Rule Pulaski 1955 1956 - Oct lastSun 2:00 0 S + Rule Pulaski 1957 1960 - Sep lastSun 2:00 0 S + # Zone NAME STDOFF RULES FORMAT [UNTIL] +-Zone America/Indiana/Winamac -5:46:25 - LMT 1883 Nov 18 12:13:35 ++Zone America/Indiana/Winamac -5:46:25 - LMT 1883 Nov 18 18:00u + -6:00 US C%sT 1946 + -6:00 Pulaski C%sT 1961 Apr 30 2:00 + -5:00 - EST 1969 +@@ -1075,7 +1075,7 @@ Zone America/Indiana/Winamac -5:46:25 - LMT 1883 Nov 18 12:13:35 + # + # Switzerland County, Indiana, did not observe DST from 1973 through 2005. + # Zone NAME STDOFF RULES FORMAT [UNTIL] +-Zone America/Indiana/Vevay -5:40:16 - LMT 1883 Nov 18 12:19:44 ++Zone America/Indiana/Vevay -5:40:16 - LMT 1883 Nov 18 18:00u + -6:00 US C%sT 1954 Apr 25 2:00 + -5:00 - EST 1969 + -5:00 US E%sT 1973 +@@ -1111,7 +1111,7 @@ Rule Louisville 1950 1961 - Apr lastSun 2:00 1:00 D + Rule Louisville 1950 1955 - Sep lastSun 2:00 0 S + Rule Louisville 1956 1961 - Oct lastSun 2:00 0 S + # Zone NAME STDOFF RULES FORMAT [UNTIL] +-Zone America/Kentucky/Louisville -5:43:02 - LMT 1883 Nov 18 12:16:58 ++Zone America/Kentucky/Louisville -5:43:02 - LMT 1883 Nov 18 18:00u + -6:00 US C%sT 1921 + -6:00 Louisville C%sT 1942 + -6:00 US C%sT 1946 +@@ -1145,7 +1145,7 @@ Zone America/Kentucky/Louisville -5:43:02 - LMT 1883 Nov 18 12:16:58 + # Federal Register 65, 160 (2000-08-17), pp 50154-50158. + # https://www.gpo.gov/fdsys/pkg/FR-2000-08-17/html/00-20854.htm + # +-Zone America/Kentucky/Monticello -5:39:24 - LMT 1883 Nov 18 12:20:36 ++Zone America/Kentucky/Monticello -5:39:24 - LMT 1883 Nov 18 18:00u + -6:00 US C%sT 1946 + -6:00 - CST 1968 + -6:00 US C%sT 2000 Oct 29 2:00 +@@ -2640,6 +2640,8 @@ Zone America/Dawson -9:17:40 - LMT 1900 Aug 20 + # longitude they are located at. + + # Rule NAME FROM TO - IN ON AT SAVE LETTER/S ++Rule Mexico 1931 only - May 1 23:00 1:00 D ++Rule Mexico 1931 only - Oct 1 0:00 0 S + Rule Mexico 1939 only - Feb 5 0:00 1:00 D + Rule Mexico 1939 only - Jun 25 0:00 0 S + Rule Mexico 1940 only - Dec 9 0:00 1:00 D +@@ -2656,13 +2658,13 @@ Rule Mexico 2002 max - Apr Sun>=1 2:00 1:00 D + Rule Mexico 2002 max - Oct lastSun 2:00 0 S + # Zone NAME STDOFF RULES FORMAT [UNTIL] + # Quintana Roo; represented by Cancún +-Zone America/Cancun -5:47:04 - LMT 1922 Jan 1 0:12:56 ++Zone America/Cancun -5:47:04 - LMT 1922 Jan 1 6:00u + -6:00 - CST 1981 Dec 23 + -5:00 Mexico E%sT 1998 Aug 2 2:00 + -6:00 Mexico C%sT 2015 Feb 1 2:00 + -5:00 - EST + # Campeche, Yucatán; represented by Mérida +-Zone America/Merida -5:58:28 - LMT 1922 Jan 1 0:01:32 ++Zone America/Merida -5:58:28 - LMT 1922 Jan 1 6:00u + -6:00 - CST 1981 Dec 23 + -5:00 - EST 1982 Dec 2 + -6:00 Mexico C%sT +@@ -2676,23 +2678,21 @@ Zone America/Merida -5:58:28 - LMT 1922 Jan 1 0:01:32 + # See: Inicia mañana Horario de Verano en zona fronteriza, El Universal, + # 2016-03-12 + # http://www.eluniversal.com.mx/articulo/estados/2016/03/12/inicia-manana-horario-de-verano-en-zona-fronteriza +-Zone America/Matamoros -6:40:00 - LMT 1921 Dec 31 23:20:00 ++Zone America/Matamoros -6:30:00 - LMT 1922 Jan 1 6:00u + -6:00 - CST 1988 + -6:00 US C%sT 1989 + -6:00 Mexico C%sT 2010 + -6:00 US C%sT + # Durango; Coahuila, Nuevo León, Tamaulipas (away from US border) +-Zone America/Monterrey -6:41:16 - LMT 1921 Dec 31 23:18:44 ++Zone America/Monterrey -6:41:16 - LMT 1922 Jan 1 6:00u + -6:00 - CST 1988 + -6:00 US C%sT 1989 + -6:00 Mexico C%sT + # Central Mexico +-Zone America/Mexico_City -6:36:36 - LMT 1922 Jan 1 0:23:24 ++Zone America/Mexico_City -6:36:36 - LMT 1922 Jan 1 7:00u + -7:00 - MST 1927 Jun 10 23:00 + -6:00 - CST 1930 Nov 15 +- -7:00 - MST 1931 May 1 23:00 +- -6:00 - CST 1931 Oct +- -7:00 - MST 1932 Apr 1 ++ -7:00 Mexico M%sT 1932 Apr 1 + -6:00 Mexico C%sT 2001 Sep 30 2:00 + -6:00 - CST 2002 Feb 20 + -6:00 Mexico C%sT +@@ -2700,35 +2700,29 @@ Zone America/Mexico_City -6:36:36 - LMT 1922 Jan 1 0:23:24 + # This includes the municipalities of Janos, Ascensión, Juárez, Guadalupe, + # Práxedis G Guerrero, Coyame del Sotol, Ojinaga, and Manuel Benavides. + # (See the 2016-03-12 El Universal source mentioned above.) +-Zone America/Ojinaga -6:57:40 - LMT 1922 Jan 1 0:02:20 ++Zone America/Ojinaga -6:57:40 - LMT 1922 Jan 1 7:00u + -7:00 - MST 1927 Jun 10 23:00 + -6:00 - CST 1930 Nov 15 +- -7:00 - MST 1931 May 1 23:00 +- -6:00 - CST 1931 Oct +- -7:00 - MST 1932 Apr 1 ++ -7:00 Mexico M%sT 1932 Apr 1 + -6:00 - CST 1996 + -6:00 Mexico C%sT 1998 + -6:00 - CST 1998 Apr Sun>=1 3:00 + -7:00 Mexico M%sT 2010 + -7:00 US M%sT + # Chihuahua (away from US border) +-Zone America/Chihuahua -7:04:20 - LMT 1921 Dec 31 23:55:40 ++Zone America/Chihuahua -7:04:20 - LMT 1922 Jan 1 7:00u + -7:00 - MST 1927 Jun 10 23:00 + -6:00 - CST 1930 Nov 15 +- -7:00 - MST 1931 May 1 23:00 +- -6:00 - CST 1931 Oct +- -7:00 - MST 1932 Apr 1 ++ -7:00 Mexico M%sT 1932 Apr 1 + -6:00 - CST 1996 + -6:00 Mexico C%sT 1998 + -6:00 - CST 1998 Apr Sun>=1 3:00 + -7:00 Mexico M%sT + # Sonora +-Zone America/Hermosillo -7:23:52 - LMT 1921 Dec 31 23:36:08 ++Zone America/Hermosillo -7:23:52 - LMT 1922 Jan 1 7:00u + -7:00 - MST 1927 Jun 10 23:00 + -6:00 - CST 1930 Nov 15 +- -7:00 - MST 1931 May 1 23:00 +- -6:00 - CST 1931 Oct +- -7:00 - MST 1932 Apr 1 ++ -7:00 Mexico M%sT 1932 Apr 1 + -6:00 - CST 1942 Apr 24 + -7:00 - MST 1949 Jan 14 + -8:00 - PST 1970 +@@ -2763,24 +2757,20 @@ Zone America/Hermosillo -7:23:52 - LMT 1921 Dec 31 23:36:08 + # Use "Bahia_Banderas" to keep the name to fourteen characters. + + # Mazatlán +-Zone America/Mazatlan -7:05:40 - LMT 1921 Dec 31 23:54:20 ++Zone America/Mazatlan -7:05:40 - LMT 1922 Jan 1 7:00u + -7:00 - MST 1927 Jun 10 23:00 + -6:00 - CST 1930 Nov 15 +- -7:00 - MST 1931 May 1 23:00 +- -6:00 - CST 1931 Oct +- -7:00 - MST 1932 Apr 1 ++ -7:00 Mexico M%sT 1932 Apr 1 + -6:00 - CST 1942 Apr 24 + -7:00 - MST 1949 Jan 14 + -8:00 - PST 1970 + -7:00 Mexico M%sT + + # Bahía de Banderas +-Zone America/Bahia_Banderas -7:01:00 - LMT 1921 Dec 31 23:59:00 ++Zone America/Bahia_Banderas -7:01:00 - LMT 1922 Jan 1 7:00u + -7:00 - MST 1927 Jun 10 23:00 + -6:00 - CST 1930 Nov 15 +- -7:00 - MST 1931 May 1 23:00 +- -6:00 - CST 1931 Oct +- -7:00 - MST 1932 Apr 1 ++ -7:00 Mexico M%sT 1932 Apr 1 + -6:00 - CST 1942 Apr 24 + -7:00 - MST 1949 Jan 14 + -8:00 - PST 1970 +@@ -2788,7 +2778,7 @@ Zone America/Bahia_Banderas -7:01:00 - LMT 1921 Dec 31 23:59:00 + -6:00 Mexico C%sT + + # Baja California +-Zone America/Tijuana -7:48:04 - LMT 1922 Jan 1 0:11:56 ++Zone America/Tijuana -7:48:04 - LMT 1922 Jan 1 7:00u + -7:00 - MST 1924 + -8:00 - PST 1927 Jun 10 23:00 + -7:00 - MST 1930 Nov 15 +diff --git a/jdk/test/java/util/TimeZone/TimeZoneData/VERSION b/jdk/test/java/util/TimeZone/TimeZoneData/VERSION +index 7147016..0cad939 100644 +--- a/jdk/test/java/util/TimeZone/TimeZoneData/VERSION ++++ b/jdk/test/java/util/TimeZone/TimeZoneData/VERSION +@@ -1 +1 @@ +-tzdata2022d ++tzdata2022e +diff --git a/jdk/test/java/util/TimeZone/TimeZoneData/displaynames.txt b/jdk/test/java/util/TimeZone/TimeZoneData/displaynames.txt +index b382395..2f2786f 100644 +--- a/jdk/test/java/util/TimeZone/TimeZoneData/displaynames.txt ++++ b/jdk/test/java/util/TimeZone/TimeZoneData/displaynames.txt +@@ -97,9 +97,7 @@ America/Winnipeg CST CDT + America/Yakutat AKST AKDT + America/Yellowknife MST MDT + Antarctica/Macquarie AEST AEDT +-Asia/Amman EET EEST + Asia/Beirut EET EEST +-Asia/Damascus EET EEST + Asia/Famagusta EET EEST + Asia/Gaza EET EEST + Asia/Hebron EET EEST +diff --git a/jdk/test/sun/util/calendar/zi/tzdata/VERSION b/jdk/test/sun/util/calendar/zi/tzdata/VERSION +index 889d0e6..b8cb36e 100644 +--- a/jdk/test/sun/util/calendar/zi/tzdata/VERSION ++++ b/jdk/test/sun/util/calendar/zi/tzdata/VERSION +@@ -21,4 +21,4 @@ + # or visit www.oracle.com if you need additional information or have any + # questions. + # +-tzdata2022d ++tzdata2022e +diff --git a/jdk/test/sun/util/calendar/zi/tzdata/asia b/jdk/test/sun/util/calendar/zi/tzdata/asia +index 1dc7d34..f1771e4 100644 +--- a/jdk/test/sun/util/calendar/zi/tzdata/asia ++++ b/jdk/test/sun/util/calendar/zi/tzdata/asia +@@ -2254,6 +2254,17 @@ Zone Asia/Tokyo 9:18:59 - LMT 1887 Dec 31 15:00u + # From the Arabic version, it seems to say it would be at midnight + # (assume 24:00) on the last Thursday in February, starting from 2022. + ++# From Issam Al-Zuwairi (2022-10-05): ++# The Council of Ministers in Jordan decided Wednesday 5th October 2022, ++# that daylight saving time (DST) will be throughout the year.... ++# ++# From Brian Inglis (2022-10-06): ++# https://petra.gov.jo/Include/InnerPage.jsp?ID=45567&lang=en&name=en_news ++# ++# From Paul Eggert (2022-10-05): ++# Like Syria, model this as a transition from EEST +03 (DST) to plain +03 ++# (non-DST) at the point where DST would otherwise have ended. ++ + # Rule NAME FROM TO - IN ON AT SAVE LETTER/S + Rule Jordan 1973 only - Jun 6 0:00 1:00 S + Rule Jordan 1973 1975 - Oct 1 0:00 0 - +@@ -2285,11 +2296,12 @@ Rule Jordan 2005 only - Sep lastFri 0:00s 0 - + Rule Jordan 2006 2011 - Oct lastFri 0:00s 0 - + Rule Jordan 2013 only - Dec 20 0:00 0 - + Rule Jordan 2014 2021 - Mar lastThu 24:00 1:00 S +-Rule Jordan 2014 max - Oct lastFri 0:00s 0 - +-Rule Jordan 2022 max - Feb lastThu 24:00 1:00 S ++Rule Jordan 2014 2022 - Oct lastFri 0:00s 0 - ++Rule Jordan 2022 only - Feb lastThu 24:00 1:00 S + # Zone NAME STDOFF RULES FORMAT [UNTIL] + Zone Asia/Amman 2:23:44 - LMT 1931 +- 2:00 Jordan EE%sT ++ 2:00 Jordan EE%sT 2022 Oct 28 0:00s ++ 3:00 - +03 + + + # Kazakhstan +@@ -3838,19 +3850,27 @@ Rule Syria 2007 only - Nov Fri>=1 0:00 0 - + # Our brief summary: + # https://www.timeanddate.com/news/time/syria-dst-2012.html + +-# From Arthur David Olson (2012-03-27): +-# Assume last Friday in March going forward XXX. ++# From Steffen Thorsen (2022-10-05): ++# Syria is adopting year-round DST, starting this autumn.... ++# From https://www.enabbaladi.net/archives/607812 ++# "This [the decision] came after the weekly government meeting today, ++# Tuesday 4 October ..." ++# ++# From Paul Eggert (2022-10-05): ++# Like Jordan, model this as a transition from EEST +03 (DST) to plain +03 ++# (non-DST) at the point where DST would otherwise have ended. + + Rule Syria 2008 only - Apr Fri>=1 0:00 1:00 S + Rule Syria 2008 only - Nov 1 0:00 0 - + Rule Syria 2009 only - Mar lastFri 0:00 1:00 S + Rule Syria 2010 2011 - Apr Fri>=1 0:00 1:00 S +-Rule Syria 2012 max - Mar lastFri 0:00 1:00 S +-Rule Syria 2009 max - Oct lastFri 0:00 0 - ++Rule Syria 2012 2022 - Mar lastFri 0:00 1:00 S ++Rule Syria 2009 2022 - Oct lastFri 0:00 0 - + + # Zone NAME STDOFF RULES FORMAT [UNTIL] + Zone Asia/Damascus 2:25:12 - LMT 1920 # Dimashq +- 2:00 Syria EE%sT ++ 2:00 Syria EE%sT 2022 Oct 28 0:00 ++ 3:00 - +03 + + # Tajikistan + # From Shanks & Pottenger. +diff --git a/jdk/test/sun/util/calendar/zi/tzdata/europe b/jdk/test/sun/util/calendar/zi/tzdata/europe +index 9e0a538..930cede 100644 +--- a/jdk/test/sun/util/calendar/zi/tzdata/europe ++++ b/jdk/test/sun/util/calendar/zi/tzdata/europe +@@ -3417,7 +3417,7 @@ Zone Europe/Madrid -0:14:44 - LMT 1901 Jan 1 0:00u + 0:00 Spain WE%sT 1940 Mar 16 23:00 + 1:00 Spain CE%sT 1979 + 1:00 EU CE%sT +-Zone Africa/Ceuta -0:21:16 - LMT 1900 Dec 31 23:38:44 ++Zone Africa/Ceuta -0:21:16 - LMT 1901 Jan 1 0:00u + 0:00 - WET 1918 May 6 23:00 + 0:00 1:00 WEST 1918 Oct 7 23:00 + 0:00 - WET 1924 +diff --git a/jdk/test/sun/util/calendar/zi/tzdata/northamerica b/jdk/test/sun/util/calendar/zi/tzdata/northamerica +index 114cef1..ce4ee74 100644 +--- a/jdk/test/sun/util/calendar/zi/tzdata/northamerica ++++ b/jdk/test/sun/util/calendar/zi/tzdata/northamerica +@@ -462,7 +462,7 @@ Rule Chicago 1922 1966 - Apr lastSun 2:00 1:00 D + Rule Chicago 1922 1954 - Sep lastSun 2:00 0 S + Rule Chicago 1955 1966 - Oct lastSun 2:00 0 S + # Zone NAME STDOFF RULES FORMAT [UNTIL] +-Zone America/Chicago -5:50:36 - LMT 1883 Nov 18 12:09:24 ++Zone America/Chicago -5:50:36 - LMT 1883 Nov 18 18:00u + -6:00 US C%sT 1920 + -6:00 Chicago C%sT 1936 Mar 1 2:00 + -5:00 - EST 1936 Nov 15 2:00 +@@ -471,7 +471,7 @@ Zone America/Chicago -5:50:36 - LMT 1883 Nov 18 12:09:24 + -6:00 Chicago C%sT 1967 + -6:00 US C%sT + # Oliver County, ND switched from mountain to central time on 1992-10-25. +-Zone America/North_Dakota/Center -6:45:12 - LMT 1883 Nov 18 12:14:48 ++Zone America/North_Dakota/Center -6:45:12 - LMT 1883 Nov 18 19:00u + -7:00 US M%sT 1992 Oct 25 2:00 + -6:00 US C%sT + # Morton County, ND, switched from mountain to central time on +@@ -481,7 +481,7 @@ Zone America/North_Dakota/Center -6:45:12 - LMT 1883 Nov 18 12:14:48 + # Jones, Mellette, and Todd Counties in South Dakota; + # but in practice these other counties were already observing central time. + # See . +-Zone America/North_Dakota/New_Salem -6:45:39 - LMT 1883 Nov 18 12:14:21 ++Zone America/North_Dakota/New_Salem -6:45:39 - LMT 1883 Nov 18 19:00u + -7:00 US M%sT 2003 Oct 26 2:00 + -6:00 US C%sT + +@@ -498,7 +498,7 @@ Zone America/North_Dakota/New_Salem -6:45:39 - LMT 1883 Nov 18 12:14:21 + # largest city in Mercer County). Google Maps places Beulah's city hall + # at 47° 15' 51" N, 101° 46' 40" W, which yields an offset of 6h47'07". + +-Zone America/North_Dakota/Beulah -6:47:07 - LMT 1883 Nov 18 12:12:53 ++Zone America/North_Dakota/Beulah -6:47:07 - LMT 1883 Nov 18 19:00u + -7:00 US M%sT 2010 Nov 7 2:00 + -6:00 US C%sT + +@@ -530,7 +530,7 @@ Rule Denver 1921 only - May 22 2:00 0 S + Rule Denver 1965 1966 - Apr lastSun 2:00 1:00 D + Rule Denver 1965 1966 - Oct lastSun 2:00 0 S + # Zone NAME STDOFF RULES FORMAT [UNTIL] +-Zone America/Denver -6:59:56 - LMT 1883 Nov 18 12:00:04 ++Zone America/Denver -6:59:56 - LMT 1883 Nov 18 19:00u + -7:00 US M%sT 1920 + -7:00 Denver M%sT 1942 + -7:00 US M%sT 1946 +@@ -583,7 +583,7 @@ Rule CA 1950 1966 - Apr lastSun 1:00 1:00 D + Rule CA 1950 1961 - Sep lastSun 2:00 0 S + Rule CA 1962 1966 - Oct lastSun 2:00 0 S + # Zone NAME STDOFF RULES FORMAT [UNTIL] +-Zone America/Los_Angeles -7:52:58 - LMT 1883 Nov 18 12:07:02 ++Zone America/Los_Angeles -7:52:58 - LMT 1883 Nov 18 20:00u + -8:00 US P%sT 1946 + -8:00 CA P%sT 1967 + -8:00 US P%sT +@@ -845,7 +845,7 @@ Zone Pacific/Honolulu -10:31:26 - LMT 1896 Jan 13 12:00 + # Go with the Arizona State Library instead. + + # Zone NAME STDOFF RULES FORMAT [UNTIL] +-Zone America/Phoenix -7:28:18 - LMT 1883 Nov 18 11:31:42 ++Zone America/Phoenix -7:28:18 - LMT 1883 Nov 18 19:00u + -7:00 US M%sT 1944 Jan 1 0:01 + -7:00 - MST 1944 Apr 1 0:01 + -7:00 US M%sT 1944 Oct 1 0:01 +@@ -873,7 +873,7 @@ Link America/Phoenix America/Creston + # switched four weeks late in 1974. + # + # Zone NAME STDOFF RULES FORMAT [UNTIL] +-Zone America/Boise -7:44:49 - LMT 1883 Nov 18 12:15:11 ++Zone America/Boise -7:44:49 - LMT 1883 Nov 18 20:00u + -8:00 US P%sT 1923 May 13 2:00 + -7:00 US M%sT 1974 + -7:00 - MST 1974 Feb 3 2:00 +@@ -945,7 +945,7 @@ Rule Indianapolis 1941 only - Jun 22 2:00 1:00 D + Rule Indianapolis 1941 1954 - Sep lastSun 2:00 0 S + Rule Indianapolis 1946 1954 - Apr lastSun 2:00 1:00 D + # Zone NAME STDOFF RULES FORMAT [UNTIL] +-Zone America/Indiana/Indianapolis -5:44:38 - LMT 1883 Nov 18 12:15:22 ++Zone America/Indiana/Indianapolis -5:44:38 - LMT 1883 Nov 18 18:00u + -6:00 US C%sT 1920 + -6:00 Indianapolis C%sT 1942 + -6:00 US C%sT 1946 +@@ -965,7 +965,7 @@ Rule Marengo 1951 only - Sep lastSun 2:00 0 S + Rule Marengo 1954 1960 - Apr lastSun 2:00 1:00 D + Rule Marengo 1954 1960 - Sep lastSun 2:00 0 S + # Zone NAME STDOFF RULES FORMAT [UNTIL] +-Zone America/Indiana/Marengo -5:45:23 - LMT 1883 Nov 18 12:14:37 ++Zone America/Indiana/Marengo -5:45:23 - LMT 1883 Nov 18 18:00u + -6:00 US C%sT 1951 + -6:00 Marengo C%sT 1961 Apr 30 2:00 + -5:00 - EST 1969 +@@ -989,7 +989,7 @@ Rule Vincennes 1960 only - Oct lastSun 2:00 0 S + Rule Vincennes 1961 only - Sep lastSun 2:00 0 S + Rule Vincennes 1962 1963 - Oct lastSun 2:00 0 S + # Zone NAME STDOFF RULES FORMAT [UNTIL] +-Zone America/Indiana/Vincennes -5:50:07 - LMT 1883 Nov 18 12:09:53 ++Zone America/Indiana/Vincennes -5:50:07 - LMT 1883 Nov 18 18:00u + -6:00 US C%sT 1946 + -6:00 Vincennes C%sT 1964 Apr 26 2:00 + -5:00 - EST 1969 +@@ -1009,7 +1009,7 @@ Rule Perry 1955 1960 - Sep lastSun 2:00 0 S + Rule Perry 1956 1963 - Apr lastSun 2:00 1:00 D + Rule Perry 1961 1963 - Oct lastSun 2:00 0 S + # Zone NAME STDOFF RULES FORMAT [UNTIL] +-Zone America/Indiana/Tell_City -5:47:03 - LMT 1883 Nov 18 12:12:57 ++Zone America/Indiana/Tell_City -5:47:03 - LMT 1883 Nov 18 18:00u + -6:00 US C%sT 1946 + -6:00 Perry C%sT 1964 Apr 26 2:00 + -5:00 - EST 1967 Oct 29 2:00 +@@ -1026,7 +1026,7 @@ Rule Pike 1955 1960 - Sep lastSun 2:00 0 S + Rule Pike 1956 1964 - Apr lastSun 2:00 1:00 D + Rule Pike 1961 1964 - Oct lastSun 2:00 0 S + # Zone NAME STDOFF RULES FORMAT [UNTIL] +-Zone America/Indiana/Petersburg -5:49:07 - LMT 1883 Nov 18 12:10:53 ++Zone America/Indiana/Petersburg -5:49:07 - LMT 1883 Nov 18 18:00u + -6:00 US C%sT 1955 + -6:00 Pike C%sT 1965 Apr 25 2:00 + -5:00 - EST 1966 Oct 30 2:00 +@@ -1048,7 +1048,7 @@ Rule Starke 1955 1956 - Oct lastSun 2:00 0 S + Rule Starke 1957 1958 - Sep lastSun 2:00 0 S + Rule Starke 1959 1961 - Oct lastSun 2:00 0 S + # Zone NAME STDOFF RULES FORMAT [UNTIL] +-Zone America/Indiana/Knox -5:46:30 - LMT 1883 Nov 18 12:13:30 ++Zone America/Indiana/Knox -5:46:30 - LMT 1883 Nov 18 18:00u + -6:00 US C%sT 1947 + -6:00 Starke C%sT 1962 Apr 29 2:00 + -5:00 - EST 1963 Oct 27 2:00 +@@ -1064,7 +1064,7 @@ Rule Pulaski 1946 1954 - Sep lastSun 2:00 0 S + Rule Pulaski 1955 1956 - Oct lastSun 2:00 0 S + Rule Pulaski 1957 1960 - Sep lastSun 2:00 0 S + # Zone NAME STDOFF RULES FORMAT [UNTIL] +-Zone America/Indiana/Winamac -5:46:25 - LMT 1883 Nov 18 12:13:35 ++Zone America/Indiana/Winamac -5:46:25 - LMT 1883 Nov 18 18:00u + -6:00 US C%sT 1946 + -6:00 Pulaski C%sT 1961 Apr 30 2:00 + -5:00 - EST 1969 +@@ -1075,7 +1075,7 @@ Zone America/Indiana/Winamac -5:46:25 - LMT 1883 Nov 18 12:13:35 + # + # Switzerland County, Indiana, did not observe DST from 1973 through 2005. + # Zone NAME STDOFF RULES FORMAT [UNTIL] +-Zone America/Indiana/Vevay -5:40:16 - LMT 1883 Nov 18 12:19:44 ++Zone America/Indiana/Vevay -5:40:16 - LMT 1883 Nov 18 18:00u + -6:00 US C%sT 1954 Apr 25 2:00 + -5:00 - EST 1969 + -5:00 US E%sT 1973 +@@ -1111,7 +1111,7 @@ Rule Louisville 1950 1961 - Apr lastSun 2:00 1:00 D + Rule Louisville 1950 1955 - Sep lastSun 2:00 0 S + Rule Louisville 1956 1961 - Oct lastSun 2:00 0 S + # Zone NAME STDOFF RULES FORMAT [UNTIL] +-Zone America/Kentucky/Louisville -5:43:02 - LMT 1883 Nov 18 12:16:58 ++Zone America/Kentucky/Louisville -5:43:02 - LMT 1883 Nov 18 18:00u + -6:00 US C%sT 1921 + -6:00 Louisville C%sT 1942 + -6:00 US C%sT 1946 +@@ -1145,7 +1145,7 @@ Zone America/Kentucky/Louisville -5:43:02 - LMT 1883 Nov 18 12:16:58 + # Federal Register 65, 160 (2000-08-17), pp 50154-50158. + # https://www.gpo.gov/fdsys/pkg/FR-2000-08-17/html/00-20854.htm + # +-Zone America/Kentucky/Monticello -5:39:24 - LMT 1883 Nov 18 12:20:36 ++Zone America/Kentucky/Monticello -5:39:24 - LMT 1883 Nov 18 18:00u + -6:00 US C%sT 1946 + -6:00 - CST 1968 + -6:00 US C%sT 2000 Oct 29 2:00 +@@ -2640,6 +2640,8 @@ Zone America/Dawson -9:17:40 - LMT 1900 Aug 20 + # longitude they are located at. + + # Rule NAME FROM TO - IN ON AT SAVE LETTER/S ++Rule Mexico 1931 only - May 1 23:00 1:00 D ++Rule Mexico 1931 only - Oct 1 0:00 0 S + Rule Mexico 1939 only - Feb 5 0:00 1:00 D + Rule Mexico 1939 only - Jun 25 0:00 0 S + Rule Mexico 1940 only - Dec 9 0:00 1:00 D +@@ -2656,13 +2658,13 @@ Rule Mexico 2002 max - Apr Sun>=1 2:00 1:00 D + Rule Mexico 2002 max - Oct lastSun 2:00 0 S + # Zone NAME STDOFF RULES FORMAT [UNTIL] + # Quintana Roo; represented by Cancún +-Zone America/Cancun -5:47:04 - LMT 1922 Jan 1 0:12:56 ++Zone America/Cancun -5:47:04 - LMT 1922 Jan 1 6:00u + -6:00 - CST 1981 Dec 23 + -5:00 Mexico E%sT 1998 Aug 2 2:00 + -6:00 Mexico C%sT 2015 Feb 1 2:00 + -5:00 - EST + # Campeche, Yucatán; represented by Mérida +-Zone America/Merida -5:58:28 - LMT 1922 Jan 1 0:01:32 ++Zone America/Merida -5:58:28 - LMT 1922 Jan 1 6:00u + -6:00 - CST 1981 Dec 23 + -5:00 - EST 1982 Dec 2 + -6:00 Mexico C%sT +@@ -2676,23 +2678,21 @@ Zone America/Merida -5:58:28 - LMT 1922 Jan 1 0:01:32 + # See: Inicia mañana Horario de Verano en zona fronteriza, El Universal, + # 2016-03-12 + # http://www.eluniversal.com.mx/articulo/estados/2016/03/12/inicia-manana-horario-de-verano-en-zona-fronteriza +-Zone America/Matamoros -6:40:00 - LMT 1921 Dec 31 23:20:00 ++Zone America/Matamoros -6:30:00 - LMT 1922 Jan 1 6:00u + -6:00 - CST 1988 + -6:00 US C%sT 1989 + -6:00 Mexico C%sT 2010 + -6:00 US C%sT + # Durango; Coahuila, Nuevo León, Tamaulipas (away from US border) +-Zone America/Monterrey -6:41:16 - LMT 1921 Dec 31 23:18:44 ++Zone America/Monterrey -6:41:16 - LMT 1922 Jan 1 6:00u + -6:00 - CST 1988 + -6:00 US C%sT 1989 + -6:00 Mexico C%sT + # Central Mexico +-Zone America/Mexico_City -6:36:36 - LMT 1922 Jan 1 0:23:24 ++Zone America/Mexico_City -6:36:36 - LMT 1922 Jan 1 7:00u + -7:00 - MST 1927 Jun 10 23:00 + -6:00 - CST 1930 Nov 15 +- -7:00 - MST 1931 May 1 23:00 +- -6:00 - CST 1931 Oct +- -7:00 - MST 1932 Apr 1 ++ -7:00 Mexico M%sT 1932 Apr 1 + -6:00 Mexico C%sT 2001 Sep 30 2:00 + -6:00 - CST 2002 Feb 20 + -6:00 Mexico C%sT +@@ -2700,35 +2700,29 @@ Zone America/Mexico_City -6:36:36 - LMT 1922 Jan 1 0:23:24 + # This includes the municipalities of Janos, Ascensión, Juárez, Guadalupe, + # Práxedis G Guerrero, Coyame del Sotol, Ojinaga, and Manuel Benavides. + # (See the 2016-03-12 El Universal source mentioned above.) +-Zone America/Ojinaga -6:57:40 - LMT 1922 Jan 1 0:02:20 ++Zone America/Ojinaga -6:57:40 - LMT 1922 Jan 1 7:00u + -7:00 - MST 1927 Jun 10 23:00 + -6:00 - CST 1930 Nov 15 +- -7:00 - MST 1931 May 1 23:00 +- -6:00 - CST 1931 Oct +- -7:00 - MST 1932 Apr 1 ++ -7:00 Mexico M%sT 1932 Apr 1 + -6:00 - CST 1996 + -6:00 Mexico C%sT 1998 + -6:00 - CST 1998 Apr Sun>=1 3:00 + -7:00 Mexico M%sT 2010 + -7:00 US M%sT + # Chihuahua (away from US border) +-Zone America/Chihuahua -7:04:20 - LMT 1921 Dec 31 23:55:40 ++Zone America/Chihuahua -7:04:20 - LMT 1922 Jan 1 7:00u + -7:00 - MST 1927 Jun 10 23:00 + -6:00 - CST 1930 Nov 15 +- -7:00 - MST 1931 May 1 23:00 +- -6:00 - CST 1931 Oct +- -7:00 - MST 1932 Apr 1 ++ -7:00 Mexico M%sT 1932 Apr 1 + -6:00 - CST 1996 + -6:00 Mexico C%sT 1998 + -6:00 - CST 1998 Apr Sun>=1 3:00 + -7:00 Mexico M%sT + # Sonora +-Zone America/Hermosillo -7:23:52 - LMT 1921 Dec 31 23:36:08 ++Zone America/Hermosillo -7:23:52 - LMT 1922 Jan 1 7:00u + -7:00 - MST 1927 Jun 10 23:00 + -6:00 - CST 1930 Nov 15 +- -7:00 - MST 1931 May 1 23:00 +- -6:00 - CST 1931 Oct +- -7:00 - MST 1932 Apr 1 ++ -7:00 Mexico M%sT 1932 Apr 1 + -6:00 - CST 1942 Apr 24 + -7:00 - MST 1949 Jan 14 + -8:00 - PST 1970 +@@ -2763,24 +2757,20 @@ Zone America/Hermosillo -7:23:52 - LMT 1921 Dec 31 23:36:08 + # Use "Bahia_Banderas" to keep the name to fourteen characters. + + # Mazatlán +-Zone America/Mazatlan -7:05:40 - LMT 1921 Dec 31 23:54:20 ++Zone America/Mazatlan -7:05:40 - LMT 1922 Jan 1 7:00u + -7:00 - MST 1927 Jun 10 23:00 + -6:00 - CST 1930 Nov 15 +- -7:00 - MST 1931 May 1 23:00 +- -6:00 - CST 1931 Oct +- -7:00 - MST 1932 Apr 1 ++ -7:00 Mexico M%sT 1932 Apr 1 + -6:00 - CST 1942 Apr 24 + -7:00 - MST 1949 Jan 14 + -8:00 - PST 1970 + -7:00 Mexico M%sT + + # Bahía de Banderas +-Zone America/Bahia_Banderas -7:01:00 - LMT 1921 Dec 31 23:59:00 ++Zone America/Bahia_Banderas -7:01:00 - LMT 1922 Jan 1 7:00u + -7:00 - MST 1927 Jun 10 23:00 + -6:00 - CST 1930 Nov 15 +- -7:00 - MST 1931 May 1 23:00 +- -6:00 - CST 1931 Oct +- -7:00 - MST 1932 Apr 1 ++ -7:00 Mexico M%sT 1932 Apr 1 + -6:00 - CST 1942 Apr 24 + -7:00 - MST 1949 Jan 14 + -8:00 - PST 1970 +@@ -2788,7 +2778,7 @@ Zone America/Bahia_Banderas -7:01:00 - LMT 1921 Dec 31 23:59:00 + -6:00 Mexico C%sT + + # Baja California +-Zone America/Tijuana -7:48:04 - LMT 1922 Jan 1 0:11:56 ++Zone America/Tijuana -7:48:04 - LMT 1922 Jan 1 7:00u + -7:00 - MST 1924 + -8:00 - PST 1927 Jun 10 23:00 + -7:00 - MST 1930 Nov 15 +-- +1.8.3.1 diff --git a/8296480-Fix-the-problem-that-the-TestPolicy.java-cas.patch b/8296480-Fix-the-problem-that-the-TestPolicy.java-cas.patch new file mode 100644 index 0000000000000000000000000000000000000000..7d1be495df61f9860a6e4f748ee03abac84c2486 --- /dev/null +++ b/8296480-Fix-the-problem-that-the-TestPolicy.java-cas.patch @@ -0,0 +1,42 @@ +From 6d1c5b1ee82b2b2481a16f3510078fdc7ddc08f9 Mon Sep 17 00:00:00 2001 +From: eapen +Date: Tue, 15 Nov 2022 11:26:33 +0800 +Subject: [PATCH 04/33] 8296480: Fix the problem that the TestPolicy.java case + fails because the certificate expires. +--- + jdk/test/java/security/cert/pkix/policyChanges/TestPolicy.java | 6 +++++- + 1 file changed, 5 insertions(+), 1 deletion(-) + +diff --git a/jdk/test/java/security/cert/pkix/policyChanges/TestPolicy.java b/jdk/test/java/security/cert/pkix/policyChanges/TestPolicy.java +index a92eee2..b37debf 100644 +--- a/jdk/test/java/security/cert/pkix/policyChanges/TestPolicy.java ++++ b/jdk/test/java/security/cert/pkix/policyChanges/TestPolicy.java +@@ -1,5 +1,5 @@ + /* +- * Copyright (c) 2002, 2012, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2002, 2022, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it +@@ -34,6 +34,7 @@ + */ + + import java.io.*; ++import java.text.DateFormat; + import java.util.*; + + import java.security.Security; +@@ -97,6 +98,10 @@ public class TestPolicy { + params.setRevocationEnabled(false); + params.setInitialPolicies(testCase.initialPolicies); + ++ // Certs expired on 7th Nov 2022 ++ params.setDate(DateFormat.getDateInstance(DateFormat.MEDIUM, ++ Locale.US).parse("June 01, 2022")); ++ + CertPath path = factory.generateCertPath(Arrays.asList(new X509Certificate[] {ee, ca})); + + PKIXCertPathValidatorResult result = (PKIXCertPathValidatorResult)validator.validate(path, params); +-- +1.8.3.1 + diff --git a/8296485-BuildEEBasicConstraints.java-test-fails-with.patch b/8296485-BuildEEBasicConstraints.java-test-fails-with.patch new file mode 100644 index 0000000000000000000000000000000000000000..b641e884852cc93e1908b810bbd5ace2e76e60d6 --- /dev/null +++ b/8296485-BuildEEBasicConstraints.java-test-fails-with.patch @@ -0,0 +1,39 @@ +From b8aedd236ca707cfc15eb5daf91aab697a8014ed Mon Sep 17 00:00:00 2001 +From: eapen +Date: Wed, 23 Nov 2022 08:31:14 +0800 +Subject: [PATCH 06/33] I68TO2: 8296485: BuildEEBasicConstraints.java test fails with + SunCertPathBuilderException +--- + .../CertPathBuilder/targetConstraints/BuildEEBasicConstraints.java | 7 +++++++ + 1 file changed, 7 insertions(+) + +diff --git a/jdk/test/java/security/cert/CertPathBuilder/targetConstraints/BuildEEBasicConstraints.java b/jdk/test/java/security/cert/CertPathBuilder/targetConstraints/BuildEEBasicConstraints.java +index 6be5562..44926d2 100644 +--- a/jdk/test/java/security/cert/CertPathBuilder/targetConstraints/BuildEEBasicConstraints.java ++++ b/jdk/test/java/security/cert/CertPathBuilder/targetConstraints/BuildEEBasicConstraints.java +@@ -46,9 +46,11 @@ import java.security.cert.PKIXCertPathBuilderResult; + import java.security.cert.TrustAnchor; + import java.security.cert.X509Certificate; + import java.security.cert.X509CertSelector; ++import java.text.DateFormat; + import java.util.ArrayList; + import java.util.Collections; + import java.util.List; ++import java.util.Locale; + + public final class BuildEEBasicConstraints { + +@@ -65,6 +67,11 @@ public final class BuildEEBasicConstraints { + PKIXBuilderParameters params = new PKIXBuilderParameters + (Collections.singleton(anchor), sel); + params.setRevocationEnabled(false); ++ ++ // Certs expired on 7th Nov 2022 ++ params.setDate(DateFormat.getDateInstance(DateFormat.MEDIUM, ++ Locale.US).parse("June 01, 2022")); ++ + X509Certificate eeCert = CertUtils.getCertFromFile("ee.cer"); + X509Certificate caCert = CertUtils.getCertFromFile("ca.cer"); + ArrayList certs = new ArrayList(); +-- +1.8.3.1 diff --git a/Dynamic-CDS-Archive.patch b/Dynamic-CDS-Archive.patch new file mode 100644 index 0000000000000000000000000000000000000000..edccdea2d34992af8b341928e6630b677a2897e3 --- /dev/null +++ b/Dynamic-CDS-Archive.patch @@ -0,0 +1,8657 @@ +From f1cba2dd8fe526f4ad5ea4913154a174bd19a080 Mon Sep 17 00:00:00 2001 +Date: Sat, 3 Sep 2022 14:25:11 +0000 +Subject: Dynamic-CDS-Archive + +--- + hotspot/src/os/linux/vm/os_linux.cpp | 3 +- + hotspot/src/share/vm/cds/archiveBuilder.cpp | 807 ++++++++++++++++ + hotspot/src/share/vm/cds/archiveBuilder.hpp | 368 +++++++ + hotspot/src/share/vm/cds/archiveUtils.cpp | 247 +++++ + hotspot/src/share/vm/cds/archiveUtils.hpp | 141 +++ + hotspot/src/share/vm/cds/dumpAllocStats.cpp | 109 +++ + hotspot/src/share/vm/cds/dumpAllocStats.hpp | 88 ++ + hotspot/src/share/vm/cds/dynamicArchive.cpp | 412 ++++++++ + hotspot/src/share/vm/cds/dynamicArchive.hpp | 54 ++ + .../share/vm/classfile/classFileParser.cpp | 7 + + .../src/share/vm/classfile/classLoaderExt.hpp | 2 +- + .../share/vm/classfile/compactHashtable.cpp | 216 +++++ + .../share/vm/classfile/compactHashtable.hpp | 349 +++++++ + .../share/vm/classfile/sharedClassUtil.hpp | 4 + + .../src/share/vm/classfile/symbolTable.cpp | 102 +- + .../src/share/vm/classfile/symbolTable.hpp | 12 + + .../share/vm/classfile/systemDictionary.cpp | 159 +-- + .../share/vm/classfile/systemDictionary.hpp | 1 + + .../vm/classfile/systemDictionaryShared.cpp | 911 ++++++++++++++++++ + .../vm/classfile/systemDictionaryShared.hpp | 167 +++- + hotspot/src/share/vm/memory/allocation.hpp | 12 + + .../src/share/vm/memory/allocation.inline.hpp | 53 +- + hotspot/src/share/vm/memory/filemap.cpp | 352 +++++-- + hotspot/src/share/vm/memory/filemap.hpp | 104 +- + hotspot/src/share/vm/memory/iterator.hpp | 7 + + hotspot/src/share/vm/memory/metaspace.cpp | 80 +- + hotspot/src/share/vm/memory/metaspace.hpp | 1 + + .../src/share/vm/memory/metaspaceClosure.cpp | 87 ++ + .../src/share/vm/memory/metaspaceClosure.hpp | 381 ++++++++ + .../src/share/vm/memory/metaspaceShared.cpp | 148 ++- + .../src/share/vm/memory/metaspaceShared.hpp | 51 +- + hotspot/src/share/vm/oops/annotations.cpp | 12 + + hotspot/src/share/vm/oops/annotations.hpp | 9 + + hotspot/src/share/vm/oops/arrayKlass.cpp | 22 + + hotspot/src/share/vm/oops/arrayKlass.hpp | 3 +- + hotspot/src/share/vm/oops/constMethod.cpp | 26 + + hotspot/src/share/vm/oops/constMethod.hpp | 8 +- + hotspot/src/share/vm/oops/constantPool.cpp | 93 +- + hotspot/src/share/vm/oops/constantPool.hpp | 12 + + hotspot/src/share/vm/oops/cpCache.cpp | 69 ++ + hotspot/src/share/vm/oops/cpCache.hpp | 25 +- + hotspot/src/share/vm/oops/instanceKlass.cpp | 131 ++- + hotspot/src/share/vm/oops/instanceKlass.hpp | 12 +- + hotspot/src/share/vm/oops/klass.cpp | 83 +- + hotspot/src/share/vm/oops/klass.hpp | 10 +- + hotspot/src/share/vm/oops/klassVtable.hpp | 3 + + hotspot/src/share/vm/oops/metadata.hpp | 4 +- + hotspot/src/share/vm/oops/method.cpp | 22 +- + hotspot/src/share/vm/oops/method.hpp | 7 +- + hotspot/src/share/vm/oops/methodCounters.hpp | 7 + + hotspot/src/share/vm/oops/methodData.cpp | 9 + + hotspot/src/share/vm/oops/methodData.hpp | 5 +- + hotspot/src/share/vm/oops/objArrayKlass.cpp | 7 + + hotspot/src/share/vm/oops/objArrayKlass.hpp | 3 +- + hotspot/src/share/vm/oops/symbol.hpp | 22 +- + hotspot/src/share/vm/runtime/arguments.cpp | 142 +++ + hotspot/src/share/vm/runtime/arguments.hpp | 19 +- + hotspot/src/share/vm/runtime/globals.hpp | 21 + + hotspot/src/share/vm/runtime/java.cpp | 8 + + hotspot/src/share/vm/runtime/mutexLocker.cpp | 5 +- + hotspot/src/share/vm/runtime/mutexLocker.hpp | 3 + + hotspot/src/share/vm/runtime/os.cpp | 9 +- + hotspot/src/share/vm/runtime/os.hpp | 2 + + hotspot/src/share/vm/runtime/thread.cpp | 10 + + .../share/vm/services/diagnosticCommand.cpp | 13 + + .../share/vm/services/diagnosticCommand.hpp | 23 + + hotspot/src/share/vm/utilities/array.hpp | 1 + + hotspot/src/share/vm/utilities/bitMap.cpp | 17 +- + hotspot/src/share/vm/utilities/bitMap.hpp | 1 + + .../src/share/vm/utilities/constantTag.hpp | 5 +- + .../share/vm/utilities/globalDefinitions.hpp | 11 +- + hotspot/src/share/vm/utilities/hashtable.cpp | 60 +- + hotspot/src/share/vm/utilities/hashtable.hpp | 98 +- + .../share/vm/utilities/hashtable.inline.hpp | 2 +- + hotspot/src/share/vm/utilities/ostream.cpp | 11 + + hotspot/src/share/vm/utilities/ostream.hpp | 2 +- + .../src/share/vm/utilities/resourceHash.hpp | 27 +- + 77 files changed, 6234 insertions(+), 295 deletions(-) + create mode 100644 hotspot/src/share/vm/cds/archiveBuilder.cpp + create mode 100644 hotspot/src/share/vm/cds/archiveBuilder.hpp + create mode 100644 hotspot/src/share/vm/cds/archiveUtils.cpp + create mode 100644 hotspot/src/share/vm/cds/archiveUtils.hpp + create mode 100644 hotspot/src/share/vm/cds/dumpAllocStats.cpp + create mode 100644 hotspot/src/share/vm/cds/dumpAllocStats.hpp + create mode 100644 hotspot/src/share/vm/cds/dynamicArchive.cpp + create mode 100644 hotspot/src/share/vm/cds/dynamicArchive.hpp + create mode 100644 hotspot/src/share/vm/classfile/compactHashtable.cpp + create mode 100644 hotspot/src/share/vm/classfile/compactHashtable.hpp + create mode 100644 hotspot/src/share/vm/classfile/systemDictionaryShared.cpp + create mode 100644 hotspot/src/share/vm/memory/metaspaceClosure.cpp + create mode 100644 hotspot/src/share/vm/memory/metaspaceClosure.hpp + +diff --git a/hotspot/src/os/linux/vm/os_linux.cpp b/hotspot/src/os/linux/vm/os_linux.cpp +index f700335a3..6dbedf5c2 100644 +--- a/hotspot/src/os/linux/vm/os_linux.cpp ++++ b/hotspot/src/os/linux/vm/os_linux.cpp +@@ -2370,8 +2370,7 @@ void os::print_siginfo(outputStream* st, void* siginfo) { + #if INCLUDE_CDS + if (si && (si->si_signo == SIGBUS || si->si_signo == SIGSEGV) && + UseSharedSpaces) { +- FileMapInfo* mapinfo = FileMapInfo::current_info(); +- if (mapinfo->is_in_shared_space(si->si_addr)) { ++ if (MetaspaceShared::is_in_shared_space(si->si_addr)) { + st->print("\n\nError accessing class data sharing archive." \ + " Mapped file inaccessible during execution, " \ + " possible disk/network problem."); +diff --git a/hotspot/src/share/vm/cds/archiveBuilder.cpp b/hotspot/src/share/vm/cds/archiveBuilder.cpp +new file mode 100644 +index 000000000..144dedfa9 +--- /dev/null ++++ b/hotspot/src/share/vm/cds/archiveBuilder.cpp +@@ -0,0 +1,807 @@ ++/* ++ * Copyright (c) 2020, 2021, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2022, Huawei Technologies Co., Ltd. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ * ++ */ ++ ++#include "precompiled.hpp" ++#include "cds/archiveBuilder.hpp" ++#include "cds/archiveUtils.hpp" ++#include "classfile/symbolTable.hpp" ++#include "classfile/systemDictionaryShared.hpp" ++#include "interpreter/abstractInterpreter.hpp" ++#include "memory/filemap.hpp" ++#include "memory/memRegion.hpp" ++#include "memory/metaspaceShared.hpp" ++#include "memory/resourceArea.hpp" ++#include "oops/instanceKlass.hpp" ++#include "oops/objArrayKlass.hpp" ++#include "runtime/arguments.hpp" ++#include "runtime/globals_extension.hpp" ++#include "runtime/sharedRuntime.hpp" ++#include "runtime/thread.hpp" ++#include "utilities/align.hpp" ++#include "utilities/bitMap.inline.hpp" ++#include "utilities/hashtable.inline.hpp" ++ ++ArchiveBuilder* ArchiveBuilder::_current = NULL; ++ ++ArchiveBuilder::OtherROAllocMark::~OtherROAllocMark() { ++ char* newtop = ArchiveBuilder::current()->_ro_region.top(); ++ ArchiveBuilder::alloc_stats()->record_other_type(int(newtop - _oldtop), true); ++} ++ ++ArchiveBuilder::SourceObjList::SourceObjList() : _ptrmap(16 * K, false) { ++ _total_bytes = 0; ++ _objs = new (ResourceObj::C_HEAP, mtClassShared) GrowableArray(128 * K, mtClassShared); ++} ++ ++ArchiveBuilder::SourceObjList::~SourceObjList() { ++ delete _objs; ++} ++ ++void ArchiveBuilder::SourceObjList::append(MetaspaceClosure::Ref* enclosing_ref, SourceObjInfo* src_info) { ++ // Save this source object for copying ++ _objs->append(src_info); ++ ++ // Prepare for marking the pointers in this source object ++ assert(is_aligned(_total_bytes, sizeof(address)), "must be"); ++ src_info->set_ptrmap_start(_total_bytes / sizeof(address)); ++ _total_bytes = align_up(_total_bytes + (uintx)src_info->size_in_bytes(), sizeof(address)); ++ src_info->set_ptrmap_end(_total_bytes / sizeof(address)); ++ ++ BitMap::idx_t bitmap_size_needed = BitMap::idx_t(src_info->ptrmap_end()); ++ if (_ptrmap.size() <= bitmap_size_needed) { ++ _ptrmap.resize((bitmap_size_needed + 1) * 2, false); ++ } ++} ++ ++class PrintBitMap : public BitMapClosure { ++ public: ++ bool do_bit(BitMap::idx_t bit_offset) { ++ tty->print_cr("PrintBitMap : " SIZE_FORMAT, bit_offset); ++ return true; ++ } ++}; ++ ++void ArchiveBuilder::SourceObjList::remember_embedded_pointer(SourceObjInfo* src_info, MetaspaceClosure::Ref* ref) { ++ // src_obj contains a pointer. Remember the location of this pointer in _ptrmap, ++ // so that we can copy/relocate it later. E.g., if we have ++ // class Foo { intx scala; Bar* ptr; } ++ // Foo *f = 0x100; ++ // To mark the f->ptr pointer on 64-bit platform, this function is called with ++ // src_info()->obj() == 0x100 ++ // ref->addr() == 0x108 ++ address src_obj = src_info->obj(); ++ address* field_addr = ref->addr(); ++ assert(src_info->ptrmap_start() < _total_bytes, "sanity"); ++ assert(src_info->ptrmap_end() <= _total_bytes, "sanity"); ++ assert(*field_addr != NULL, "should have checked"); ++ ++ intx field_offset_in_bytes = ((address)field_addr) - src_obj; ++ DEBUG_ONLY(int src_obj_size = src_info->size_in_bytes();) ++ assert(field_offset_in_bytes >= 0, "must be"); ++ assert(field_offset_in_bytes + intx(sizeof(intptr_t)) <= intx(src_obj_size), "must be"); ++ assert(is_aligned(field_offset_in_bytes, sizeof(address)), "must be"); ++ ++ BitMap::idx_t idx = BitMap::idx_t(src_info->ptrmap_start() + (uintx)(field_offset_in_bytes / sizeof(address))); ++ if (TraceDynamicCDS) { ++ dynamic_cds_log->print_cr("remember_embedded_pointer: _ptrmap_start: " SIZE_FORMAT ++ "_ptrmap_end: " SIZE_FORMAT ++ " field: " PTR_FORMAT" -> " PTR_FORMAT ++ " bit_index: " SIZE_FORMAT " ", ++ src_info->ptrmap_start(), src_info->ptrmap_end(), p2i(src_obj), p2i(field_addr), idx); ++ } ++ _ptrmap.set_bit(BitMap::idx_t(idx)); ++} ++ ++class RelocateEmbeddedPointers : public BitMapClosure { ++ ArchiveBuilder* _builder; ++ address _dumped_obj; ++ BitMap::idx_t _start_idx; ++public: ++ RelocateEmbeddedPointers(ArchiveBuilder* builder, address dumped_obj, BitMap::idx_t start_idx) : ++ _builder(builder), _dumped_obj(dumped_obj), _start_idx(start_idx) {} ++ ++ bool do_bit(BitMap::idx_t bit_offset) { ++ uintx FLAG_MASK = 0x03; // See comments around MetaspaceClosure::FLAG_MASK ++ size_t field_offset = size_t(bit_offset - _start_idx) * sizeof(address); ++ address* ptr_loc = (address*)(_dumped_obj + field_offset); ++ uintx old_p_and_bits = (uintx)(*ptr_loc); ++ uintx flag_bits = (old_p_and_bits & FLAG_MASK); ++ address old_p = (address)(old_p_and_bits & (~FLAG_MASK)); ++ address new_p = _builder->get_dumped_addr(old_p); ++ uintx new_p_and_bits = ((uintx)new_p) | flag_bits; ++ ++ if (TraceDynamicCDS) { ++ dynamic_cds_log->print_cr("Ref: [" PTR_FORMAT "] -> " PTR_FORMAT " => " PTR_FORMAT, ++ p2i(ptr_loc), p2i(old_p), p2i(new_p)); ++ } ++ ArchivePtrMarker::set_and_mark_pointer(ptr_loc, (address)(new_p_and_bits)); ++ return true; // keep iterating the bitmap ++ } ++}; ++ ++void ArchiveBuilder::SourceObjList::relocate(int i, ArchiveBuilder* builder) { ++ SourceObjInfo* src_info = objs()->at(i); ++ assert(src_info->should_copy(), "must be"); ++ BitMap::idx_t start = BitMap::idx_t(src_info->ptrmap_start()); // inclusive ++ BitMap::idx_t end = BitMap::idx_t(src_info->ptrmap_end()); // exclusive ++ ++ RelocateEmbeddedPointers relocator(builder, src_info->dumped_addr(), start); ++ _ptrmap.iterate(&relocator, start, end); ++} ++ ++ArchiveBuilder::ArchiveBuilder() : ++ _current_dump_space(NULL), ++ _buffer_bottom(NULL), ++ _last_verified_top(NULL), ++ _num_dump_regions_used(0), ++ _other_region_used_bytes(0), ++ _requested_static_archive_bottom(NULL), ++ _requested_static_archive_top(NULL), ++ _requested_dynamic_archive_bottom(NULL), ++ _requested_dynamic_archive_top(NULL), ++ _mapped_static_archive_bottom(NULL), ++ _mapped_static_archive_top(NULL), ++ _buffer_to_requested_delta(0), ++ _rw_region("rw", MAX_SHARED_DELTA), ++ _ro_region("ro", MAX_SHARED_DELTA), ++ _rw_src_objs(), ++ _ro_src_objs(), ++ _src_obj_table(INITIAL_TABLE_SIZE), ++ _num_instance_klasses(0), ++ _num_obj_array_klasses(0), ++ _num_type_array_klasses(0), ++ _estimated_metaspaceobj_bytes(0), ++ _estimated_hashtable_bytes(0) { ++ _klasses = new (ResourceObj::C_HEAP, mtClassShared) GrowableArray(4 * K, mtClassShared); ++ _symbols = new (ResourceObj::C_HEAP, mtClassShared) GrowableArray(256 * K, mtClassShared); ++ ++ assert(_current == NULL, "must be"); ++ _current = this; ++} ++ ++ArchiveBuilder::~ArchiveBuilder() { ++ assert(_current == this, "must be"); ++ _current = NULL; ++ ++ clean_up_src_obj_table(); ++ ++ for (int i = 0; i < _symbols->length(); i++) { ++ _symbols->at(i)->decrement_refcount(); ++ } ++ ++ delete _klasses; ++ delete _symbols; ++ if (_shared_rs.is_reserved()) { ++ _shared_rs.release(); ++ } ++} ++ ++bool ArchiveBuilder::gather_one_source_obj(MetaspaceClosure::Ref* enclosing_ref, ++ MetaspaceClosure::Ref* ref, bool read_only) { ++ address src_obj = ref->obj(); ++ if (src_obj == NULL) { ++ return false; ++ } ++ ref->set_keep_after_pushing(); ++ remember_embedded_pointer_in_copied_obj(enclosing_ref, ref); ++ ++ FollowMode follow_mode = get_follow_mode(ref); ++ SourceObjInfo src_info(ref, read_only, follow_mode); ++ bool created; ++ SourceObjInfo* p = _src_obj_table.add_if_absent(src_obj, src_info, &created); ++ if (created) { ++ if (_src_obj_table.maybe_grow(MAX_TABLE_SIZE)) { ++ if (InfoDynamicCDS) { ++ dynamic_cds_log->print_cr("Expanded _src_obj_table table to %d", _src_obj_table.table_size()); ++ } ++ } ++ } ++ ++ assert(p->read_only() == src_info.read_only(), "must be"); ++ ++ if (created && src_info.should_copy()) { ++ ref->set_user_data((void*)p); ++ if (read_only) { ++ _ro_src_objs.append(enclosing_ref, p); ++ } else { ++ _rw_src_objs.append(enclosing_ref, p); ++ } ++ return true; // Need to recurse into this ref only if we are copying it ++ } else { ++ return false; ++ } ++} ++ ++void ArchiveBuilder::iterate_sorted_roots(MetaspaceClosure* it, bool is_relocating_pointers) { ++ int i; ++ ++ if (!is_relocating_pointers) { ++ // Don't relocate _symbol, so we can safely call decrement_refcount on the ++ // original symbols. ++ int num_symbols = _symbols->length(); ++ for (i = 0; i < num_symbols; i++) { ++ it->push(_symbols->adr_at(i)); ++ } ++ } ++ ++ int num_klasses = _klasses->length(); ++ for (i = 0; i < num_klasses; i++) { ++ it->push(_klasses->adr_at(i)); ++ } ++ ++ iterate_roots(it, is_relocating_pointers); ++} ++ ++class GatherSortedSourceObjs : public MetaspaceClosure { ++ ArchiveBuilder* _builder; ++ ++public: ++ GatherSortedSourceObjs(ArchiveBuilder* builder) : _builder(builder) {} ++ ++ virtual bool do_ref(Ref* ref, bool read_only) { ++ return _builder->gather_one_source_obj(enclosing_ref(), ref, read_only); ++ } ++ ++ virtual void do_pending_ref(Ref* ref) { ++ if (ref->obj() != NULL) { ++ _builder->remember_embedded_pointer_in_copied_obj(enclosing_ref(), ref); ++ } ++ } ++}; ++ ++void ArchiveBuilder::gather_source_objs() { ++ ResourceMark rm; ++ if (InfoDynamicCDS) { ++ dynamic_cds_log->print_cr("Gathering all archivable objects ... "); ++ } ++ gather_klasses_and_symbols(); ++ GatherSortedSourceObjs doit(this); ++ iterate_sorted_roots(&doit, /*is_relocating_pointers=*/false); ++ doit.finish(); ++} ++ ++bool ArchiveBuilder::is_excluded(Klass* klass) { ++ if (klass->oop_is_instance()) { ++ InstanceKlass* ik = InstanceKlass::cast(klass); ++ return SystemDictionaryShared::is_excluded_class(ik); ++ } else if (klass->oop_is_objArray()) { ++ if (DynamicDumpSharedSpaces) { ++ // Don't support archiving of array klasses for now (WHY???) ++ return true; ++ } ++ Klass* bottom = ObjArrayKlass::cast(klass)->bottom_klass(); ++ if (bottom->oop_is_instance()) { ++ return SystemDictionaryShared::is_excluded_class(InstanceKlass::cast(bottom)); ++ } ++ } ++ ++ return false; ++} ++ ++ArchiveBuilder::FollowMode ArchiveBuilder::get_follow_mode(MetaspaceClosure::Ref *ref) { ++ address obj = ref->obj(); ++ if (MetaspaceShared::is_in_shared_space(obj)) { ++ // Don't dump existing shared metadata again. ++ return point_to_it; ++ } else if (ref->msotype() == MetaspaceObj::MethodDataType) { ++ return set_to_null; ++ } else { ++ if (ref->msotype() == MetaspaceObj::ClassType) { ++ Klass* klass = (Klass*)ref->obj(); ++ assert(klass->is_klass(), "must be"); ++ if (is_excluded(klass)) { ++ if (TraceDynamicCDS) { ++ ResourceMark rm; ++ dynamic_cds_log->print_cr("Skipping class (excluded): %s", klass->external_name()); ++ } ++ return set_to_null; ++ } ++ } ++ ++ return make_a_copy; ++ } ++} ++ ++int ArchiveBuilder::compare_symbols_by_address(Symbol** a, Symbol** b) { ++ if (a[0] < b[0]) { ++ return -1; ++ } else { ++ assert(a[0] > b[0], "Duplicated symbol unexpected"); ++ return 1; ++ } ++} ++ ++int ArchiveBuilder::compare_klass_by_name(Klass** a, Klass** b) { ++ return a[0]->name()->fast_compare(b[0]->name()); ++} ++ ++void ArchiveBuilder::sort_klasses() { ++ if (InfoDynamicCDS) { ++ dynamic_cds_log->print_cr("Sorting classes ... "); ++ } ++ _klasses->sort(compare_klass_by_name); ++} ++ ++class GatherKlassesAndSymbols : public UniqueMetaspaceClosure { ++ ArchiveBuilder* _builder; ++ ++public: ++ GatherKlassesAndSymbols(ArchiveBuilder* builder) : _builder(builder) { } ++ ++ virtual bool do_unique_ref(Ref* ref, bool read_only) { ++ return _builder->gather_klass_and_symbol(ref, read_only); ++ } ++}; ++ ++void ArchiveBuilder::gather_klasses_and_symbols() { ++ ResourceMark rm; ++ if (InfoDynamicCDS) { ++ dynamic_cds_log->print_cr("Gathering classes and symbols ... "); ++ } ++ GatherKlassesAndSymbols doit(this); ++ iterate_roots(&doit, false); ++ doit.finish(); ++ ++ if (InfoDynamicCDS) { ++ dynamic_cds_log->print_cr("Number of classes %d", _num_instance_klasses + _num_obj_array_klasses + _num_type_array_klasses); ++ dynamic_cds_log->print_cr(" instance classes = %5d", _num_instance_klasses); ++ dynamic_cds_log->print_cr(" obj array classes = %5d", _num_obj_array_klasses); ++ dynamic_cds_log->print_cr(" type array classes = %5d", _num_type_array_klasses); ++ dynamic_cds_log->print_cr(" symbols = %5d", _symbols->length()); ++ } ++} ++ ++bool ArchiveBuilder::gather_klass_and_symbol(MetaspaceClosure::Ref* ref, bool read_only) { ++ if (ref->obj() == NULL) { ++ return false; ++ } ++ if (get_follow_mode(ref) != make_a_copy) { ++ return false; ++ } ++ if (ref->msotype() == MetaspaceObj::ClassType) { ++ Klass* klass = (Klass*)ref->obj(); ++ assert(klass->is_klass(), "must be"); ++ if (!is_excluded(klass)) { ++ _klasses->append(klass); ++ if (klass->oop_is_instance()) { ++ _num_instance_klasses ++; ++ } else if (klass->oop_is_objArray()) { ++ _num_obj_array_klasses ++; ++ } else { ++ assert(klass->oop_is_typeArray(), "sanity"); ++ _num_type_array_klasses ++; ++ } ++ } ++ // See RunTimeSharedClassInfo::get_for() ++ _estimated_metaspaceobj_bytes += align_up(BytesPerWord, KlassAlignmentInBytes); ++ } else if (ref->msotype() == MetaspaceObj::SymbolType) { ++ // Make sure the symbol won't be GC'ed while we are dumping the archive. ++ Symbol* sym = (Symbol*)ref->obj(); ++ sym->increment_refcount(); ++ _symbols->append(sym); ++ } ++ ++ int bytes = ref->size() * BytesPerWord; ++ _estimated_metaspaceobj_bytes += align_up(bytes, KlassAlignmentInBytes); ++ return true; // recurse ++} ++ ++size_t ArchiveBuilder::estimate_archive_size() { ++ // size of the symbol table and two dictionaries, plus the RunTimeSharedClassInfo's ++ size_t symbol_table_est = SymbolTable::estimate_size_for_archive(); ++ size_t dictionary_est = SystemDictionaryShared::estimate_size_for_archive(); ++ _estimated_hashtable_bytes = symbol_table_est + dictionary_est; ++ ++ size_t total = 0; ++ ++ total += _estimated_metaspaceobj_bytes; ++ total += _estimated_hashtable_bytes; ++ ++ // allow fragmentation at the end of each dump region ++ total += _total_dump_regions * ((size_t)os::vm_allocation_granularity()); ++ ++ if (InfoDynamicCDS) { ++ dynamic_cds_log->print_cr("_estimated_hashtable_bytes = " SIZE_FORMAT " + " SIZE_FORMAT " = " SIZE_FORMAT, ++ symbol_table_est, dictionary_est, _estimated_hashtable_bytes); ++ dynamic_cds_log->print_cr("_estimated_metaspaceobj_bytes = " SIZE_FORMAT, _estimated_metaspaceobj_bytes); ++ dynamic_cds_log->print_cr("total estimate bytes = " SIZE_FORMAT, total); ++ } ++ ++ return align_up(total, (size_t)os::vm_allocation_granularity()); ++} ++ ++address ArchiveBuilder::reserve_buffer() { ++ size_t buffer_size = estimate_archive_size(); ++ ReservedSpace rs(buffer_size, os::vm_allocation_granularity(), false); ++ if (!rs.is_reserved()) { ++ tty->print_cr("Failed to reserve " SIZE_FORMAT " bytes of output buffer.", buffer_size); ++ vm_direct_exit(0); ++ } ++ ++ // buffer_bottom is the lowest address of the 2 core regions (rw, ro) when ++ // we are copying the class metadata into the buffer. ++ address buffer_bottom = (address)rs.base(); ++ _shared_rs = rs; ++ ++ _buffer_bottom = buffer_bottom; ++ _last_verified_top = buffer_bottom; ++ _current_dump_space = &_rw_region; ++ _num_dump_regions_used = 1; ++ _other_region_used_bytes = 0; ++ _current_dump_space->init(&_shared_rs, &_shared_vs); ++ ++ ArchivePtrMarker::initialize(&_ptrmap, &_shared_vs); ++ ++ // The bottom of the static archive should be mapped at this address by default. ++ _requested_static_archive_bottom = (address)MetaspaceShared::requested_base_address(); ++ ++ size_t static_archive_size = FileMapInfo::shared_spaces_size(); ++ _requested_static_archive_top = _requested_static_archive_bottom + static_archive_size; ++ ++ _mapped_static_archive_bottom = (address)MetaspaceShared::shared_metaspace_static_bottom(); ++ _mapped_static_archive_top = _mapped_static_archive_bottom + static_archive_size; ++ ++ _requested_dynamic_archive_bottom = align_up(_requested_static_archive_top, (size_t)os::vm_allocation_granularity()); ++ ++ _buffer_to_requested_delta = _requested_dynamic_archive_bottom - _buffer_bottom; ++ ++ if (InfoDynamicCDS) { ++ dynamic_cds_log->print_cr("Reserved output buffer space at " PTR_FORMAT " [" SIZE_FORMAT " bytes]", ++ p2i(buffer_bottom), buffer_size); ++ dynamic_cds_log->print_cr("Dynamic archive mapped space at " PTR_FORMAT, p2i(_requested_dynamic_archive_bottom)); ++ } ++ ++ return buffer_bottom; ++} ++ ++void ArchiveBuilder::verify_estimate_size(size_t estimate, const char* which) { ++ address bottom = _last_verified_top; ++ address top = (address)(current_dump_space()->top()); ++ size_t used = size_t(top - bottom) + _other_region_used_bytes; ++ int diff = int(estimate) - int(used); ++ ++ if (InfoDynamicCDS) { ++ dynamic_cds_log->print_cr("%s estimate = " SIZE_FORMAT " used = " SIZE_FORMAT "; diff = %d bytes", which, estimate, used, diff); ++ } ++ assert(diff >= 0, "Estimate is too small"); ++ ++ _last_verified_top = top; ++ _other_region_used_bytes = 0; ++} ++ ++void ArchiveBuilder::dump_rw_metadata() { ++ ResourceMark rm; ++ if (InfoDynamicCDS) { ++ dynamic_cds_log->print_cr("Allocating RW objects ... "); ++ } ++ make_shallow_copies(&_rw_region, &_rw_src_objs); ++} ++ ++void ArchiveBuilder::dump_ro_metadata() { ++ ResourceMark rm; ++ if (InfoDynamicCDS) { ++ dynamic_cds_log->print_cr("Allocating RO objects ... "); ++ } ++ start_dump_space(&_ro_region); ++ make_shallow_copies(&_ro_region, &_ro_src_objs); ++} ++ ++void ArchiveBuilder::start_dump_space(DumpRegion* next) { ++ address bottom = _last_verified_top; ++ address top = (address)(_current_dump_space->top()); ++ _other_region_used_bytes += size_t(top - bottom); ++ _current_dump_space->pack(next); ++ _current_dump_space = next; ++ _num_dump_regions_used ++; ++ _last_verified_top = (address)(_current_dump_space->top()); ++} ++ ++void ArchiveBuilder::patch_shared_obj_vtable() { ++ SourceObjList* objs = &_rw_src_objs; ++ ++ for (int i = 0; i < objs->objs()->length(); i++) { ++ SourceObjInfo* src_info = objs->objs()->at(i); ++ address dest = src_info->dumped_addr(); ++ MetaspaceClosure::Ref* ref = src_info->ref(); ++ intptr_t* archived_vtable = MetaspaceShared::get_archived_vtable(ref->msotype(), dest); ++ if (archived_vtable != NULL) { ++ // When we copy archived vtable from base archive into dynamic archive's objs, we can't call ++ // virtual function before restore dynamic archive. ++ *(intptr_t**)dest = archived_vtable; ++ ArchivePtrMarker::mark_pointer((address*)dest); ++ } ++ } ++ if (InfoDynamicCDS) { ++ dynamic_cds_log->print_cr("patch vtable done (%d objects)", objs->objs()->length()); ++ } ++} ++ ++void ArchiveBuilder::remember_embedded_pointer_in_copied_obj(MetaspaceClosure::Ref* enclosing_ref, ++ MetaspaceClosure::Ref* ref) { ++ assert(ref->obj() != NULL, "should have checked"); ++ ++ if (enclosing_ref != NULL) { ++ SourceObjInfo* src_info = (SourceObjInfo*)enclosing_ref->user_data(); ++ if (src_info == NULL) { ++ // source objects of point_to_it/set_to_null types are not copied ++ // so we don't need to remember their pointers. ++ } else { ++ if (src_info->read_only()) { ++ _ro_src_objs.remember_embedded_pointer(src_info, ref); ++ } else { ++ _rw_src_objs.remember_embedded_pointer(src_info, ref); ++ } ++ } ++ } ++} ++ ++void ArchiveBuilder::make_shallow_copies(DumpRegion *dump_region, ++ const ArchiveBuilder::SourceObjList* src_objs) { ++ for (int i = 0; i < src_objs->objs()->length(); i++) { ++ make_shallow_copy(dump_region, src_objs->objs()->at(i)); ++ } ++ if (InfoDynamicCDS) { ++ dynamic_cds_log->print_cr("done (%d objects)", src_objs->objs()->length()); ++ } ++} ++ ++void ArchiveBuilder::make_shallow_copy(DumpRegion *dump_region, SourceObjInfo* src_info) { ++ MetaspaceClosure::Ref* ref = src_info->ref(); ++ address src = ref->obj(); ++ int bytes = src_info->size_in_bytes(); ++ char* dest; ++ char* oldtop; ++ char* newtop; ++ ++ oldtop = dump_region->top(); ++ if (ref->msotype() == MetaspaceObj::ClassType) { ++ // Save a pointer immediate in front of an InstanceKlass, so ++ // we can do a quick lookup from InstanceKlass* -> RunTimeSharedClassInfo* ++ // without building another hashtable. See RunTimeSharedClassInfo::get_for() ++ // in systemDictionaryShared.cpp. ++ Klass* klass = (Klass*)src; ++ if (klass->oop_is_instance()) { ++ dump_region->allocate(sizeof(address)); ++ } ++ } ++ dest = dump_region->allocate(bytes); ++ newtop = dump_region->top(); ++ ++ memcpy(dest, src, bytes); ++ ++ if (TraceDynamicCDS) { ++ dynamic_cds_log->print_cr("Copy: " PTR_FORMAT " ==> " PTR_FORMAT " %d", p2i(src), p2i(dest), bytes); ++ } ++ src_info->set_dumped_addr((address)dest); ++ ++ _alloc_stats.record(ref->msotype(), int(newtop - oldtop), src_info->read_only()); ++} ++ ++address ArchiveBuilder::get_dumped_addr(address src_obj) { ++ SourceObjInfo* p = _src_obj_table.lookup(src_obj); ++ assert(p != NULL, "must be"); ++ ++ return p->dumped_addr(); ++} ++ ++void ArchiveBuilder::relocate_embedded_pointers(ArchiveBuilder::SourceObjList* src_objs) { ++ for (int i = 0; i < src_objs->objs()->length(); i++) { ++ src_objs->relocate(i, this); ++ } ++} ++ ++void ArchiveBuilder::print_stats() { ++ _alloc_stats.print_stats(int(_ro_region.used()), int(_rw_region.used())); ++} ++ ++void ArchiveBuilder::make_klasses_shareable() { ++ for (int i = 0; i < klasses()->length(); i++) { ++ Klass* k = klasses()->at(i); ++ k->remove_java_mirror(); ++ if (k->oop_is_objArray()) { ++ // InstanceKlass and TypeArrayKlass will in turn call remove_unshareable_info ++ // on their array classes. ++ } else if (k->oop_is_typeArray()) { ++ k->remove_unshareable_info(); ++ } else { ++ assert(k->oop_is_instance(), " must be"); ++ InstanceKlass* ik = InstanceKlass::cast(k); ++ // High version introduce fast bytecode, jdk8 no need do it. ++ // MetaspaceShared::rewrite_nofast_bytecodes_and_calculate_fingerprints(Thread::current(), ik); ++ ik->remove_unshareable_info(); // assign_class_loader_type is in Klass::remove_unshareable_info ++ ++ if (DebugDynamicCDS) { ++ ResourceMark rm; ++ dynamic_cds_log->print_cr("klasses[%4d] = " PTR_FORMAT " => " PTR_FORMAT " %s", i, p2i(ik), p2i(to_requested(ik)), ik->external_name()); ++ } ++ } ++ } ++} ++ ++uintx ArchiveBuilder::buffer_to_offset(address p) const { ++ address requested_p = to_requested(p); ++ assert(requested_p >= _requested_static_archive_bottom, "must be"); ++ return requested_p - _requested_static_archive_bottom; ++} ++ ++uintx ArchiveBuilder::any_to_offset(address p) const { ++ if (is_in_mapped_static_archive(p)) { ++ assert(DynamicDumpSharedSpaces, "must be"); ++ return p - _mapped_static_archive_bottom; ++ } ++ return buffer_to_offset(p); ++} ++ ++// RelocateBufferToRequested --- Relocate all the pointers in rw/ro, ++// so that the archive can be mapped to the "requested" location without runtime relocation. ++// ++// - See ArchiveBuilder header for the definition of "buffer", "mapped" and "requested" ++// - ArchivePtrMarker::ptrmap() marks all the pointers in the rw/ro regions ++// - Every pointer must have one of the following values: ++// [a] NULL: ++// No relocation is needed. Remove this pointer from ptrmap so we don't need to ++// consider it at runtime. ++// [b] Points into an object X which is inside the buffer: ++// Adjust this pointer by _buffer_to_requested_delta, so it points to X ++// when the archive is mapped at the requested location. ++// [c] Points into an object Y which is inside mapped static archive: ++// - This happens only during dynamic dump ++// - Adjust this pointer by _mapped_to_requested_static_archive_delta, ++// so it points to Y when the static archive is mapped at the requested location. ++class RelocateBufferToRequested : public BitMapClosure { ++ ArchiveBuilder* _builder; ++ address _buffer_bottom; ++ intx _buffer_to_requested_delta; ++ intx _mapped_to_requested_static_archive_delta; ++ size_t _max_non_null_offset; ++ ++ public: ++ RelocateBufferToRequested(ArchiveBuilder* builder) { ++ _builder = builder; ++ _buffer_bottom = _builder->buffer_bottom(); ++ _buffer_to_requested_delta = builder->buffer_to_requested_delta(); ++ _mapped_to_requested_static_archive_delta = builder->requested_static_archive_bottom() - builder->mapped_static_archive_bottom(); ++ _max_non_null_offset = 0; ++ ++ address bottom = _builder->buffer_bottom(); ++ address top = _builder->buffer_top(); ++ address new_bottom = bottom + _buffer_to_requested_delta; ++ address new_top = top + _buffer_to_requested_delta; ++ if (TraceDynamicCDS) { ++ dynamic_cds_log->print_cr("Relocating archive from [" INTPTR_FORMAT " - " INTPTR_FORMAT "] to " ++ "[" INTPTR_FORMAT " - " INTPTR_FORMAT "]", ++ p2i(bottom), p2i(top), ++ p2i(new_bottom), p2i(new_top)); ++ } ++ } ++ ++ bool do_bit(size_t offset) { ++ address* p = (address*)_buffer_bottom + offset; ++ assert(_builder->is_in_buffer_space(p), "pointer must live in buffer space"); ++ ++ if (*p == NULL) { ++ // todo -- clear bit, etc ++ ArchivePtrMarker::ptrmap()->clear_bit(offset); ++ } else { ++ if (_builder->is_in_buffer_space(*p)) { ++ *p += _buffer_to_requested_delta; ++ // assert is in requested dynamic archive ++ } else { ++ assert(_builder->is_in_mapped_static_archive(*p), "old pointer must point inside buffer space or mapped static archive"); ++ *p += _mapped_to_requested_static_archive_delta; ++ assert(_builder->is_in_requested_static_archive(*p), "new pointer must point inside requested archive"); ++ } ++ ++ _max_non_null_offset = offset; ++ } ++ ++ return true; // keep iterating ++ } ++ ++ void doit() { ++ ArchivePtrMarker::ptrmap()->iterate(this); ++ ArchivePtrMarker::compact(_max_non_null_offset); ++ } ++}; ++ ++void ArchiveBuilder::relocate_to_requested() { ++ ro_region()->pack(); ++ ++ size_t my_archive_size = buffer_top() - buffer_bottom(); ++ ++ assert(DynamicDumpSharedSpaces, "must be"); ++ _requested_dynamic_archive_top = _requested_dynamic_archive_bottom + my_archive_size; ++ RelocateBufferToRequested patcher(this); ++ patcher.doit(); ++} ++ ++void ArchiveBuilder::clean_up_src_obj_table() { ++ SrcObjTableCleaner cleaner; ++ _src_obj_table.iterate(&cleaner); ++} ++ ++void ArchiveBuilder::write_archive(FileMapInfo* mapinfo) { ++ assert(mapinfo->header()->magic() == CDS_DYNAMIC_ARCHIVE_MAGIC, "Dynamic CDS calls only"); ++ ++ mapinfo->write_dynamic_header(); ++ ++ write_region(mapinfo, MetaspaceShared::d_rw, &_rw_region, /*read_only=*/false,/*allow_exec=*/false); ++ write_region(mapinfo, MetaspaceShared::d_ro, &_ro_region, /*read_only=*/true, /*allow_exec=*/false); ++ ++ char* bitmap = mapinfo->write_bitmap_region(ArchivePtrMarker::ptrmap()); ++ ++ if (InfoDynamicCDS && mapinfo->is_open()) { ++ print_stats(); ++ } ++ ++ mapinfo->close(); ++ FREE_C_HEAP_ARRAY(char, bitmap, mtClassShared); ++} ++ ++void ArchiveBuilder::write_region(FileMapInfo* mapinfo, int region_idx, DumpRegion* dump_region, bool read_only, bool allow_exec) { ++ mapinfo->write_region(region_idx, dump_region->base(), dump_region->used(), dump_region->used(), read_only, allow_exec); ++} ++ ++class RefRelocator: public MetaspaceClosure { ++ ArchiveBuilder* _builder; ++ ++public: ++ RefRelocator(ArchiveBuilder* builder) : _builder(builder) {} ++ ++ virtual bool do_ref(Ref* ref, bool read_only) { ++ if (ref->not_null()) { ++ ref->update(_builder->get_dumped_addr(ref->obj())); ++ ArchivePtrMarker::mark_pointer(ref->addr()); ++ } ++ return false; // Do not recurse. ++ } ++}; ++ ++void ArchiveBuilder::relocate_roots() { ++ if (InfoDynamicCDS) { ++ dynamic_cds_log->print_cr("Relocating external roots ... "); ++ } ++ ResourceMark rm; ++ RefRelocator doit(this); ++ iterate_sorted_roots(&doit, /*is_relocating_pointers=*/true); ++ doit.finish(); ++ if (InfoDynamicCDS) { ++ dynamic_cds_log->print_cr("done"); ++ } ++} ++ ++void ArchiveBuilder::relocate_metaspaceobj_embedded_pointers() { ++ if (InfoDynamicCDS) { ++ dynamic_cds_log->print_cr("Relocating embedded pointers in core regions ... "); ++ } ++ relocate_embedded_pointers(&_rw_src_objs); ++ relocate_embedded_pointers(&_ro_src_objs); ++} ++ ++#ifndef PRODUCT ++void ArchiveBuilder::assert_is_vm_thread() { ++ assert(Thread::current()->is_VM_thread(), "ArchiveBuilder should be used only inside the VMThread"); ++} ++#endif +diff --git a/hotspot/src/share/vm/cds/archiveBuilder.hpp b/hotspot/src/share/vm/cds/archiveBuilder.hpp +new file mode 100644 +index 000000000..18cd3c622 +--- /dev/null ++++ b/hotspot/src/share/vm/cds/archiveBuilder.hpp +@@ -0,0 +1,368 @@ ++/* ++ * Copyright (c) 2020, 2021, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2022, Huawei Technologies Co., Ltd. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ * ++ */ ++ ++#ifndef SHARE_VM_CDS_ARCHIVEBUILDER_HPP ++#define SHARE_VM_CDS_ARCHIVEBUILDER_HPP ++ ++#include "cds/archiveUtils.hpp" ++#include "cds/dumpAllocStats.hpp" ++#include "memory/metaspaceClosure.hpp" ++//#include "oops/array.hpp" ++#include "oops/klass.hpp" ++#include "runtime/os.hpp" ++#include "utilities/align.hpp" ++#include "utilities/bitMap.hpp" ++#include "utilities/growableArray.hpp" ++#include "utilities/hashtable.hpp" ++#include "utilities/resourceHash.hpp" ++ ++class FileMapInfo; ++// Overview of CDS archive creation (for both static??? and dynamic dump): ++// ++// [1] Load all classes (static dump: from the classlist, dynamic dump: as part of app execution) ++// [2] Allocate "output buffer" ++// [3] Copy contents of the 2 "core" regions (rw/ro) into the output buffer. ++// - allocate the cpp vtables in rw (static dump only) ++// - memcpy the MetaspaceObjs into rw/ro: ++// dump_rw_region(); ++// dump_ro_region(); ++// - fix all the pointers in the MetaspaceObjs to point to the copies ++// relocate_metaspaceobj_embedded_pointers() ++// [4] Copy symbol table, dictionary, etc, into the ro region ++// [5] Relocate all the pointers in rw/ro, so that the archive can be mapped to ++// the "requested" location without runtime relocation. See relocate_to_requested() ++class ArchiveBuilder : public StackObj { ++protected: ++ DumpRegion* _current_dump_space; ++ address _buffer_bottom; // for writing the contents of rw/ro regions ++ address _last_verified_top; ++ int _num_dump_regions_used; ++ size_t _other_region_used_bytes; ++ ++ // These are the addresses where we will request the static and dynamic archives to be ++ // mapped at run time. If the request fails (due to ASLR), we will map the archives at ++ // os-selected addresses. ++ address _requested_static_archive_bottom; // This is determined solely by the value of ++ // SharedBaseAddress during -Xshare:dump. ++ address _requested_static_archive_top; ++ address _requested_dynamic_archive_bottom; // Used only during dynamic dump. It's placed ++ // immediately above _requested_static_archive_top. ++ address _requested_dynamic_archive_top; ++ ++ // (Used only during dynamic dump) where the static archive is actually mapped. This ++ // may be different than _requested_static_archive_{bottom,top} due to ASLR ++ address _mapped_static_archive_bottom; ++ address _mapped_static_archive_top; ++ ++ intx _buffer_to_requested_delta; ++ ++ DumpRegion* current_dump_space() const { return _current_dump_space; } ++ ++public: ++ enum FollowMode { ++ make_a_copy, point_to_it, set_to_null ++ }; ++ ++private: ++ class SourceObjInfo { ++ MetaspaceClosure::Ref* _ref; ++ uintx _ptrmap_start; // The bit-offset of the start of this object (inclusive) ++ uintx _ptrmap_end; // The bit-offset of the end of this object (exclusive) ++ bool _read_only; ++ FollowMode _follow_mode; ++ int _size_in_bytes; ++ MetaspaceObj::Type _msotype; ++ address _dumped_addr; // Address this->obj(), as used by the dumped archive. ++ address _orig_obj; // The value of the original object (_ref->obj()) when this ++ // SourceObjInfo was created. Note that _ref->obj() may change ++ // later if _ref is relocated. ++ ++ public: ++ SourceObjInfo(MetaspaceClosure::Ref* ref, bool read_only, FollowMode follow_mode) : ++ _ref(ref), _ptrmap_start(0), _ptrmap_end(0), _read_only(read_only), _follow_mode(follow_mode), ++ _size_in_bytes(ref->size() * BytesPerWord), _msotype(ref->msotype()), ++ _orig_obj(ref->obj()) { ++ if (follow_mode == point_to_it) { ++ _dumped_addr = ref->obj(); ++ } else { ++ _dumped_addr = NULL; ++ } ++ } ++ ++ bool should_copy() const { return _follow_mode == make_a_copy; } ++ MetaspaceClosure::Ref* ref() const { return _ref; } ++ void set_dumped_addr(address dumped_addr) { ++ assert(should_copy(), "must be"); ++ assert(_dumped_addr == NULL, "cannot be copied twice"); ++ assert(dumped_addr != NULL, "must be a valid copy"); ++ _dumped_addr = dumped_addr; ++ } ++ void set_ptrmap_start(uintx v) { _ptrmap_start = v; } ++ void set_ptrmap_end(uintx v) { _ptrmap_end = v; } ++ uintx ptrmap_start() const { return _ptrmap_start; } // inclusive ++ uintx ptrmap_end() const { return _ptrmap_end; } // exclusive ++ bool read_only() const { return _read_only; } ++ int size_in_bytes() const { return _size_in_bytes; } ++ address orig_obj() const { return _orig_obj; } ++ address dumped_addr() const { return _dumped_addr; } ++ MetaspaceObj::Type msotype() const { return _msotype; } ++ ++ // convenience accessor ++ address obj() const { return ref()->obj(); } ++ }; ++ ++ class SourceObjList { ++ uintx _total_bytes; ++ GrowableArray* _objs; // Source objects to be archived ++ BitMap _ptrmap; // Marks the addresses of the pointer fields ++ // in the source objects ++ public: ++ SourceObjList(); ++ ~SourceObjList(); ++ GrowableArray* objs() const { return _objs; } ++ ++ void append(MetaspaceClosure::Ref* enclosing_ref, SourceObjInfo* src_info); ++ void remember_embedded_pointer(SourceObjInfo* pointing_obj, MetaspaceClosure::Ref* ref); ++ void relocate(int i, ArchiveBuilder* builder); ++ ++ // convenience accessor ++ SourceObjInfo* at(int i) const { return objs()->at(i); } ++ }; ++ ++ class SrcObjTableCleaner { ++ public: ++ bool do_entry(address key, const SourceObjInfo* value) { ++ delete value->ref(); ++ return true; ++ } ++ }; ++ ++ static const int INITIAL_TABLE_SIZE = 15889; ++ static const int MAX_TABLE_SIZE = 1000000; ++ ++ ReservedSpace _shared_rs; ++ VirtualSpace _shared_vs; ++ ++ DumpRegion _rw_region; ++ DumpRegion _ro_region; ++ BitMap _ptrmap; ++ ++ SourceObjList _rw_src_objs; // objs to put in rw region ++ SourceObjList _ro_src_objs; // objs to put in ro region ++ KVHashtable _src_obj_table; ++ GrowableArray* _klasses; ++ GrowableArray* _symbols; ++ ++ // statistics ++ int _num_instance_klasses; ++ int _num_obj_array_klasses; ++ int _num_type_array_klasses; ++ DumpAllocStats _alloc_stats; ++ ++ // For global access. ++ static ArchiveBuilder* _current; ++ ++public: ++ // Use this when you allocate space outside of ArchiveBuilder::dump_{rw,ro}_region. ++ // These are usually for misc tables that are allocated in the RO space. ++ class OtherROAllocMark { ++ char* _oldtop; ++ public: ++ OtherROAllocMark() { ++ _oldtop = _current->_ro_region.top(); ++ } ++ ~OtherROAllocMark(); ++ }; ++ ++private: ++ FollowMode get_follow_mode(MetaspaceClosure::Ref *ref); ++ ++ void iterate_sorted_roots(MetaspaceClosure* it, bool is_relocating_pointers); ++ void sort_klasses(); ++ static int compare_symbols_by_address(Symbol** a, Symbol** b); ++ static int compare_klass_by_name(Klass** a, Klass** b); ++ ++ bool is_excluded(Klass* k); ++ void clean_up_src_obj_table(); ++ ++ void make_shallow_copies(DumpRegion *dump_region, const SourceObjList* src_objs); ++ void make_shallow_copy(DumpRegion *dump_region, SourceObjInfo* src_info); ++ void relocate_embedded_pointers(SourceObjList* src_objs); ++ ++protected: ++ virtual void iterate_roots(MetaspaceClosure* it, bool is_relocating_pointers) = 0; ++ ++ // Conservative estimate for number of bytes needed for: ++ size_t _estimated_metaspaceobj_bytes; // all archived MetaspaceObj's. ++ size_t _estimated_hashtable_bytes; // symbol table and dictionaries ++ ++ size_t estimate_archive_size(); ++ ++ static const int _total_dump_regions = 2; ++ ++ void start_dump_space(DumpRegion* next); ++ void verify_estimate_size(size_t estimate, const char* which); ++ ++public: ++ address reserve_buffer(); ++ ++ address buffer_bottom() const { return _buffer_bottom; } ++ address buffer_top() const { return (address)current_dump_space()->top(); } ++ address requested_static_archive_bottom() const { return _requested_static_archive_bottom; } ++ address mapped_static_archive_bottom() const { return _mapped_static_archive_bottom; } ++ intx buffer_to_requested_delta() const { return _buffer_to_requested_delta; } ++ ++ bool is_in_buffer_space(address p) const { ++ return (buffer_bottom() <= p && p < buffer_top()); ++ } ++ ++ template bool is_in_buffer_space(T obj) const { ++ return is_in_buffer_space(address(obj)); ++ } ++ ++ template bool is_in_requested_static_archive(T p) const { ++ return _requested_static_archive_bottom <= (address)p && (address)p < _requested_static_archive_top; ++ } ++ ++ template bool is_in_mapped_static_archive(T p) const { ++ return _mapped_static_archive_bottom <= (address)p && (address)p < _mapped_static_archive_top; ++ } ++ ++ template T to_requested(T obj) const { ++ assert(is_in_buffer_space(obj), "must be"); ++ return (T)(address(obj) + _buffer_to_requested_delta); ++ } ++ ++public: ++ static const uintx MAX_SHARED_DELTA = 0x7FFFFFFF; ++ ++ // The address p points to an object inside the output buffer. When the archive is mapped ++ // at the requested address, what's the offset of this object from _requested_static_archive_bottom? ++ uintx buffer_to_offset(address p) const; ++ ++ // Same as buffer_to_offset, except that the address p points to either (a) an object ++ // inside the output buffer, or (b), an object in the currently mapped static archive. ++ uintx any_to_offset(address p) const; ++ ++ template ++ u4 buffer_to_offset_u4(T p) const { ++ uintx offset = buffer_to_offset((address)p); ++ guarantee(offset <= MAX_SHARED_DELTA, "must be 32-bit offset"); ++ return (u4)offset; ++ } ++ ++ template ++ u4 any_to_offset_u4(T p) const { ++ uintx offset = any_to_offset((address)p); ++ guarantee(offset <= MAX_SHARED_DELTA, "must be 32-bit offset"); ++ return (u4)offset; ++ } ++ ++ static void assert_is_vm_thread() PRODUCT_RETURN; ++ ++public: ++ ArchiveBuilder(); ++ ~ArchiveBuilder(); ++ ++ void gather_klasses_and_symbols(); ++ void replace_klass_in_constanPool(); ++ void gather_source_objs(); ++ bool gather_klass_and_symbol(MetaspaceClosure::Ref* ref, bool read_only); ++ bool gather_one_source_obj(MetaspaceClosure::Ref* enclosing_ref, MetaspaceClosure::Ref* ref, bool read_only); ++ void remember_embedded_pointer_in_copied_obj(MetaspaceClosure::Ref* enclosing_ref, MetaspaceClosure::Ref* ref); ++ ++ DumpRegion* rw_region() { return &_rw_region; } ++ DumpRegion* ro_region() { return &_ro_region; } ++ ++ static char* rw_region_alloc(size_t num_bytes) { ++ return current()->rw_region()->allocate(num_bytes); ++ } ++ static char* ro_region_alloc(size_t num_bytes) { ++ return current()->ro_region()->allocate(num_bytes); ++ } ++ ++ template ++ static Array* new_ro_array(int length) { ++ size_t byte_size = Array::byte_sizeof(length); ++ Array* array = (Array*)ro_region_alloc(byte_size); ++ array->initialize(length); ++ return array; ++ } ++ ++ template ++ static Array* new_rw_array(int length) { ++ size_t byte_size = Array::byte_sizeof(length); ++ Array* array = (Array*)rw_region_alloc(byte_size); ++ array->initialize(length); ++ return array; ++ } ++ ++ template ++ static size_t ro_array_bytesize(int length) { ++ size_t byte_size = Array::byte_sizeof(length); ++ return align_up(byte_size, KlassAlignmentInBytes); ++ } ++ ++ void dump_rw_metadata(); ++ void dump_ro_metadata(); ++ void relocate_metaspaceobj_embedded_pointers(); ++ void relocate_roots(); ++ void make_klasses_shareable(); ++ void relocate_to_requested(); ++ void write_archive(FileMapInfo* mapinfo); ++ void write_region(FileMapInfo* mapinfo, int region_idx, DumpRegion* dump_region, bool read_only, bool allow_exec); ++ address get_dumped_addr(address src_obj); ++ void patch_shared_obj_vtable(); ++ ++ // All klasses and symbols that will be copied into the archive ++ GrowableArray* klasses() const { return _klasses; } ++ GrowableArray* symbols() const { return _symbols; } ++ ++ static bool is_active() { ++ return (_current != NULL); ++ } ++ ++ static ArchiveBuilder* current() { ++ assert_is_vm_thread(); ++ assert(_current != NULL, "ArchiveBuilder must be active"); ++ return _current; ++ } ++ ++ static DumpAllocStats* alloc_stats() { ++ return &(current()->_alloc_stats); ++ } ++ ++ static Symbol* get_relocated_symbol(Symbol* orig_symbol) { ++ return (Symbol*)current()->get_dumped_addr((address)orig_symbol); ++ } ++ ++ static CompactHashtableStats* symbol_stats() { ++ return alloc_stats()->symbol_stats(); ++ } ++ ++ void print_stats(); ++}; ++ ++#endif // SHARE_VM_CDS_ARCHIVEBUILDER_HPP +diff --git a/hotspot/src/share/vm/cds/archiveUtils.cpp b/hotspot/src/share/vm/cds/archiveUtils.cpp +new file mode 100644 +index 000000000..88c04241d +--- /dev/null ++++ b/hotspot/src/share/vm/cds/archiveUtils.cpp +@@ -0,0 +1,247 @@ ++/* ++ * Copyright (c) 2019, 2021, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2022, Huawei Technologies Co., Ltd. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ * ++ */ ++ ++#include "precompiled.hpp" ++#include "cds/archiveBuilder.hpp" ++#include "cds/archiveUtils.hpp" ++#include "cds/dynamicArchive.hpp" ++#include "classfile/systemDictionaryShared.hpp" ++#include "memory/filemap.hpp" ++#include "memory/resourceArea.hpp" ++#include "runtime/arguments.hpp" ++#include "utilities/bitMap.inline.hpp" ++#include "utilities/align.hpp" ++ ++BitMap* ArchivePtrMarker::_ptrmap = NULL; ++VirtualSpace* ArchivePtrMarker::_vs; ++ ++bool ArchivePtrMarker::_compacted; ++ ++void ArchivePtrMarker::initialize(BitMap* ptrmap, VirtualSpace* vs) { ++ assert(_ptrmap == NULL, "initialize only once"); ++ _vs = vs; ++ _compacted = false; ++ _ptrmap = ptrmap; ++ ++ // Use this as initial guesstimate. We should need less space in the ++ // archive, but if we're wrong the bitmap will be expanded automatically. ++ size_t estimated_archive_size = MetaspaceGC::capacity_until_GC(); ++ // But set it smaller in debug builds so we always test the expansion code. ++ // (Default archive is about 12MB). ++ DEBUG_ONLY(estimated_archive_size = 6 * M); ++ ++ // We need one bit per pointer in the archive. ++ _ptrmap->resize(estimated_archive_size / sizeof(intptr_t), false); ++} ++ ++void ArchivePtrMarker::mark_pointer(address* ptr_loc) { ++ assert(_ptrmap != NULL, "not initialized"); ++ assert(!_compacted, "cannot mark anymore"); ++ ++ if (ptr_base() <= ptr_loc && ptr_loc < ptr_end()) { ++ address value = *ptr_loc; ++ // We don't want any pointer that points to very bottom of the archive, otherwise when ++ // MetaspaceShared::default_base_address()==0, we can't distinguish between a pointer ++ // to nothing (NULL) vs a pointer to an objects that happens to be at the very bottom ++ // of the archive. ++ assert(value != (address)ptr_base(), "don't point to the bottom of the archive"); ++ ++ if (value != NULL) { ++ assert(uintx(ptr_loc) % sizeof(intptr_t) == 0, "pointers must be stored in aligned addresses"); ++ size_t idx = ptr_loc - ptr_base(); ++ if (_ptrmap->size() <= idx) { ++ _ptrmap->resize((idx + 1) * 2, false); ++ } ++ assert(idx < _ptrmap->size(), "must be"); ++ _ptrmap->set_bit(idx); ++ if (TraceDynamicCDS) { ++ dynamic_cds_log->print_cr("Marking pointer [" PTR_FORMAT "] -> " PTR_FORMAT " @ " SIZE_FORMAT_W(5), p2i(ptr_loc), p2i(*ptr_loc), idx); ++ } ++ } ++ } ++} ++ ++void ArchivePtrMarker::clear_pointer(address* ptr_loc) { ++ assert(_ptrmap != NULL, "not initialized"); ++ assert(!_compacted, "cannot clear anymore"); ++ ++ assert(ptr_base() <= ptr_loc && ptr_loc < ptr_end(), "must be"); ++ assert(uintx(ptr_loc) % sizeof(intptr_t) == 0, "pointers must be stored in aligned addresses"); ++ size_t idx = ptr_loc - ptr_base(); ++ assert(idx < _ptrmap->size(), "cannot clear pointers that have not been marked"); ++ _ptrmap->clear_bit(idx); ++ if (TraceDynamicCDS) ++ dynamic_cds_log->print_cr("Clearing pointer [" PTR_FORMAT "] -> " PTR_FORMAT " @ " SIZE_FORMAT_W(5), p2i(ptr_loc), p2i(*ptr_loc), idx); ++} ++ ++class ArchivePtrBitmapCleaner: public BitMapClosure { ++ BitMap* _ptrmap; ++ address* _ptr_base; ++ address _relocatable_base; ++ address _relocatable_end; ++ size_t _max_non_null_offset; ++ ++public: ++ ArchivePtrBitmapCleaner(BitMap* ptrmap, address* ptr_base, address relocatable_base, address relocatable_end) : ++ _ptrmap(ptrmap), _ptr_base(ptr_base), ++ _relocatable_base(relocatable_base), _relocatable_end(relocatable_end), _max_non_null_offset(0) {} ++ ++ bool do_bit(size_t offset) { ++ address* ptr_loc = _ptr_base + offset; ++ address ptr_value = *ptr_loc; ++ if (ptr_value != NULL) { ++ assert(_relocatable_base <= ptr_value && ptr_value < _relocatable_end, "do not point to arbitrary locations!"); ++ if (_max_non_null_offset < offset) { ++ _max_non_null_offset = offset; ++ } ++ } else { ++ _ptrmap->clear_bit(offset); ++ } ++ ++ return true; ++ } ++ ++ size_t max_non_null_offset() const { return _max_non_null_offset; } ++}; ++ ++void ArchivePtrMarker::compact(address relocatable_base, address relocatable_end) { ++ assert(!_compacted, "cannot compact again"); ++ ArchivePtrBitmapCleaner cleaner(_ptrmap, ptr_base(), relocatable_base, relocatable_end); ++ _ptrmap->iterate(&cleaner); ++ compact(cleaner.max_non_null_offset()); ++} ++ ++void ArchivePtrMarker::compact(size_t max_non_null_offset) { ++ assert(!_compacted, "cannot compact again"); ++ _ptrmap->resize(max_non_null_offset + 1, false); ++ _compacted = true; ++} ++ ++char* DumpRegion::expand_top_to(char* newtop) { ++ assert(is_allocatable(), "must be initialized and not packed"); ++ assert(newtop >= _top, "must not grow backwards"); ++ if (newtop > _end) { ++ vm_exit_during_initialization("Unable to allocate memory", ++ "Please reduce the number of shared classes."); ++ ShouldNotReachHere(); ++ } ++ ++ commit_to(newtop); ++ _top = newtop; ++ ++ if (_max_delta > 0) { ++ uintx delta = ArchiveBuilder::current()->buffer_to_offset((address)(newtop-1)); ++ if (delta > _max_delta) { ++ // This is just a sanity check and should not appear in any real world usage. This ++ // happens only if you allocate more than 2GB of shared objects and would require ++ // millions of shared classes. ++ vm_exit_during_initialization("Out of memory in the CDS archive", ++ "Please reduce the number of shared classes."); ++ } ++ } ++ ++ return _top; ++} ++ ++void DumpRegion::commit_to(char* newtop) { ++ Arguments::assert_is_dumping_archive(); ++ char* base = _rs->base(); ++ size_t need_committed_size = newtop - base; ++ size_t has_committed_size = _vs->committed_size(); ++ if (need_committed_size < has_committed_size) { ++ return; ++ } ++ ++ size_t min_bytes = need_committed_size - has_committed_size; ++ size_t preferred_bytes = 1 * M; ++ size_t uncommitted = _vs->reserved_size() - has_committed_size; ++ ++ size_t commit = MAX2(min_bytes, preferred_bytes); ++ commit = MIN2(commit, uncommitted); ++ assert(commit <= uncommitted, "sanity"); ++ ++ if (!_vs->expand_by(commit, false)) { ++ vm_exit_during_initialization(err_msg("Failed to expand shared space to " SIZE_FORMAT " bytes", ++ need_committed_size)); ++ } ++ ++ if (DebugDynamicCDS) { ++ dynamic_cds_log->print_cr("Expanding shared spaces by " SIZE_FORMAT_W(7) " bytes [total " SIZE_FORMAT_W(9) " bytes ending at %p]", ++ commit, _vs->actual_committed_size(), _vs->high()); ++ } ++} ++ ++char* DumpRegion::allocate(size_t num_bytes) { ++ char* p = (char*)align_up(_top, (size_t)KlassAlignmentInBytes); ++ char* newtop = p + align_up(num_bytes, (size_t)KlassAlignmentInBytes); ++ expand_top_to(newtop); ++ memset(p, 0, newtop - p); ++ return p; ++} ++ ++void DumpRegion::append_intptr_t(intptr_t n, bool need_to_mark) { ++ assert(is_aligned(_top, sizeof(intptr_t)), "bad alignment"); ++ intptr_t *p = (intptr_t*)_top; ++ char* newtop = _top + sizeof(intptr_t); ++ expand_top_to(newtop); ++ *p = n; ++ if (need_to_mark) { ++ ArchivePtrMarker::mark_pointer(p); ++ } ++} ++ ++void DumpRegion::init(ReservedSpace* rs, VirtualSpace* vs) { ++ _rs = rs; ++ _vs = vs; ++ // Start with 0 committed bytes. The memory will be committed as needed. ++ if (!_vs->initialize(*_rs, 0)) { ++ fatal("Unable to allocate memory for shared space"); ++ } ++ _base = _top = _rs->base(); ++ _end = _rs->base() + _rs->size(); ++} ++ ++void DumpRegion::pack(DumpRegion* next) { ++ assert(!is_packed(), "sanity"); ++ _end = (char*)align_up(_top, (size_t)os::vm_allocation_granularity()); ++ _is_packed = true; ++ if (next != NULL) { ++ next->_rs = _rs; ++ next->_vs = _vs; ++ next->_base = next->_top = this->_end; ++ next->_end = _rs->base() + _rs->size(); ++ } ++} ++ ++void DynamicWriteClosure::do_region(u_char* start, size_t size) { ++ assert((intptr_t)start % sizeof(intptr_t) == 0, "bad alignment"); ++ assert(size % sizeof(intptr_t) == 0, "bad size"); ++ do_tag((int)size); ++ while (size > 0) { ++ _dump_region->append_intptr_t(*(intptr_t*)start, true); ++ start += sizeof(intptr_t); ++ size -= sizeof(intptr_t); ++ } ++} +diff --git a/hotspot/src/share/vm/cds/archiveUtils.hpp b/hotspot/src/share/vm/cds/archiveUtils.hpp +new file mode 100644 +index 000000000..55c2431a0 +--- /dev/null ++++ b/hotspot/src/share/vm/cds/archiveUtils.hpp +@@ -0,0 +1,141 @@ ++/* ++ * Copyright (c) 2019, 2021, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2022, Huawei Technologies Co., Ltd. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ * ++ */ ++ ++#ifndef SHARE_VM_CDS_ARCHIVEUTILS_HPP ++#define SHARE_VM_CDS_ARCHIVEUTILS_HPP ++ ++#include "memory/iterator.hpp" ++#include "runtime/virtualspace.hpp" ++#include "utilities/bitMap.hpp" ++ ++class ArchivePtrMarker : AllStatic { ++ static BitMap* _ptrmap; ++ static VirtualSpace* _vs; ++ ++ // Once _ptrmap is compacted, we don't allow bit marking anymore. This is to ++ // avoid unintentional copy operations after the bitmap has been finalized and written. ++ static bool _compacted; ++ ++ static address* ptr_base() { return (address*)_vs->low(); } // committed lower bound (inclusive) ++ static address* ptr_end() { return (address*)_vs->high(); } // committed upper bound (exclusive) ++ ++public: ++ static void initialize(BitMap* ptrmap, VirtualSpace* vs); ++ static void mark_pointer(address* ptr_loc); ++ static void clear_pointer(address* ptr_loc); ++ static void compact(address relocatable_base, address relocatable_end); ++ static void compact(size_t max_non_null_offset); ++ ++ template ++ static void mark_pointer(T* ptr_loc) { ++ mark_pointer((address*)ptr_loc); ++ } ++ ++ template ++ static void set_and_mark_pointer(T* ptr_loc, T ptr_value) { ++ *ptr_loc = ptr_value; ++ mark_pointer(ptr_loc); ++ } ++ ++ static BitMap* ptrmap() { ++ return _ptrmap; ++ } ++}; ++ ++class DumpRegion { ++private: ++ const char* _name; ++ char* _base; ++ char* _top; ++ char* _end; ++ uintx _max_delta; ++ bool _is_packed; ++ ReservedSpace* _rs; ++ VirtualSpace* _vs; ++ ++ void commit_to(char* newtop); ++ ++public: ++ DumpRegion(const char* name, uintx max_delta = 0) ++ : _name(name), _base(NULL), _top(NULL), _end(NULL), ++ _max_delta(max_delta), _is_packed(false) {} ++ ++ char* expand_top_to(char* newtop); ++ char* allocate(size_t num_bytes); ++ ++ void append_intptr_t(intptr_t n, bool need_to_mark = false); ++ ++ char* base() const { return _base; } ++ char* top() const { return _top; } ++ char* end() const { return _end; } ++ size_t reserved() const { return _end - _base; } ++ size_t used() const { return _top - _base; } ++ bool is_packed() const { return _is_packed; } ++ bool is_allocatable() const { ++ return !is_packed() && _base != NULL; ++ } ++ ++ void print(size_t total_bytes) const; ++ void print_out_of_space_msg(const char* failing_region, size_t needed_bytes); ++ ++ void init(ReservedSpace* rs, VirtualSpace* vs); ++ ++ void pack(DumpRegion* next = NULL); ++ ++ bool contains(char* p) const { ++ return base() <= p && p < top(); ++ } ++}; ++ ++// Closure for serializing initialization data out to a data area to be ++// written to the shared file. ++ ++class DynamicWriteClosure : public SerializeClosure { ++private: ++ DumpRegion* _dump_region; ++ ++public: ++ DynamicWriteClosure(DumpRegion* r) { ++ _dump_region = r; ++ } ++ ++ void do_ptr(void** p) { ++ _dump_region->append_intptr_t((intptr_t)*p, true); ++ } ++ ++ void do_u4(u4* p) { ++ _dump_region->append_intptr_t((intptr_t)(*p)); ++ } ++ ++ void do_tag(int tag) { ++ _dump_region->append_intptr_t((intptr_t)tag); ++ } ++ ++ //void do_oop(oop* o); ++ void do_region(u_char* start, size_t size); ++ bool reading() const { return false; } ++}; ++ ++#endif // SHARE_VM_CDS_ARCHIVEUTILS_HPP +diff --git a/hotspot/src/share/vm/cds/dumpAllocStats.cpp b/hotspot/src/share/vm/cds/dumpAllocStats.cpp +new file mode 100644 +index 000000000..e9146555d +--- /dev/null ++++ b/hotspot/src/share/vm/cds/dumpAllocStats.cpp +@@ -0,0 +1,109 @@ ++/* ++ * Copyright (c) 2020, 2021, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2022, Huawei Technologies Co., Ltd. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ * ++ */ ++ ++#include "precompiled.hpp" ++#include "cds/dumpAllocStats.hpp" ++ ++// Returns numerator/denominator as percentage value from 0 to 100. If denominator ++// is zero, return 0.0. ++static inline double percent_of(int numerator, int denominator) { ++ return denominator != 0 ? (double)numerator / denominator * 100.0 : 0.0; ++} ++ ++void DumpAllocStats::print_stats(int ro_all, int rw_all) { ++ if (!DebugDynamicCDS) { ++ return; ++ } ++ ++ // symbols ++ _counts[RO][SymbolHashentryType] = _symbol_stats.hashentry_count; ++ _bytes [RO][SymbolHashentryType] = _symbol_stats.hashentry_bytes; ++ ++ _counts[RO][SymbolBucketType] = _symbol_stats.bucket_count; ++ _bytes [RO][SymbolBucketType] = _symbol_stats.bucket_bytes; ++ ++ // prevent divide-by-zero ++ if (ro_all < 1) { ++ ro_all = 1; ++ } ++ if (rw_all < 1) { ++ rw_all = 1; ++ } ++ ++ int all_ro_count = 0; ++ int all_ro_bytes = 0; ++ int all_rw_count = 0; ++ int all_rw_bytes = 0; ++ ++// To make fmt_stats be a syntactic constant (for format warnings), use #define. ++#define fmt_stats "%-20s: %8d %10d %5.1f | %8d %10d %5.1f | %8d %10d %5.1f" ++ const char *sep = "--------------------+---------------------------+---------------------------+--------------------------"; ++ const char *hdr = " ro_cnt ro_bytes % | rw_cnt rw_bytes % | all_cnt all_bytes %"; ++ ++ dynamic_cds_log->print_cr("Detailed metadata info (excluding heap regions):"); ++ dynamic_cds_log->print_cr("%s", hdr); ++ dynamic_cds_log->print_cr("%s", sep); ++ for (int type = 0; type < int(_number_of_types); type ++) { ++ const char *name = type_name((Type)type); ++ int ro_count = _counts[RO][type]; ++ int ro_bytes = _bytes [RO][type]; ++ int rw_count = _counts[RW][type]; ++ int rw_bytes = _bytes [RW][type]; ++ int count = ro_count + rw_count; ++ int bytes = ro_bytes + rw_bytes; ++ ++ double ro_perc = percent_of(ro_bytes, ro_all); ++ double rw_perc = percent_of(rw_bytes, rw_all); ++ double perc = percent_of(bytes, ro_all + rw_all); ++ ++ dynamic_cds_log->print_cr(fmt_stats, name, ++ ro_count, ro_bytes, ro_perc, ++ rw_count, rw_bytes, rw_perc, ++ count, bytes, perc); ++ ++ all_ro_count += ro_count; ++ all_ro_bytes += ro_bytes; ++ all_rw_count += rw_count; ++ all_rw_bytes += rw_bytes; ++ } ++ ++ int all_count = all_ro_count + all_rw_count; ++ int all_bytes = all_ro_bytes + all_rw_bytes; ++ ++ double all_ro_perc = percent_of(all_ro_bytes, ro_all); ++ double all_rw_perc = percent_of(all_rw_bytes, rw_all); ++ double all_perc = percent_of(all_bytes, ro_all + rw_all); ++ ++ dynamic_cds_log->print_cr("%s", sep); ++ dynamic_cds_log->print_cr(fmt_stats, "Total", ++ all_ro_count, all_ro_bytes, all_ro_perc, ++ all_rw_count, all_rw_bytes, all_rw_perc, ++ all_count, all_bytes, all_perc); ++ ++ assert(all_ro_bytes == ro_all, "everything should have been counted"); ++ assert(all_rw_bytes == rw_all, "everything should have been counted"); ++ ++#undef fmt_stats ++} +diff --git a/hotspot/src/share/vm/cds/dumpAllocStats.hpp b/hotspot/src/share/vm/cds/dumpAllocStats.hpp +new file mode 100644 +index 000000000..2f9247bcb +--- /dev/null ++++ b/hotspot/src/share/vm/cds/dumpAllocStats.hpp +@@ -0,0 +1,88 @@ ++/* ++ * Copyright (c) 2020, 2021, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2022, Huawei Technologies Co., Ltd. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ * ++ */ ++ ++#ifndef SHARE_VM_CDS_DUMPALLOCSTATS_HPP ++#define SHARE_VM_CDS_DUMPALLOCSTATS_HPP ++ ++#include "classfile/compactHashtable.hpp" ++#include "memory/allocation.hpp" ++ ++// This is for dumping detailed statistics for the allocations ++// in the shared spaces. ++class DumpAllocStats : public ResourceObj { ++public: ++ // Here's poor man's enum inheritance ++#define SHAREDSPACE_OBJ_TYPES_DO(f) \ ++ METASPACE_OBJ_TYPES_DO(f) \ ++ f(SymbolHashentry) \ ++ f(SymbolBucket) \ ++ f(Other) ++ ++ enum Type { ++ // Types are MetaspaceObj::ClassType, MetaspaceObj::SymbolType, etc ++ SHAREDSPACE_OBJ_TYPES_DO(METASPACE_OBJ_TYPE_DECLARE) ++ _number_of_types ++ }; ++ ++ static const char* type_name(Type type) { ++ switch(type) { ++ SHAREDSPACE_OBJ_TYPES_DO(METASPACE_OBJ_TYPE_NAME_CASE) ++ default: ++ ShouldNotReachHere(); ++ return NULL; ++ } ++ } ++ ++ CompactHashtableStats _symbol_stats; ++ ++ int _counts[2][_number_of_types]; ++ int _bytes [2][_number_of_types]; ++ ++public: ++ enum { RO = 0, RW = 1 }; ++ ++ DumpAllocStats() { ++ memset(_counts, 0, sizeof(_counts)); ++ memset(_bytes, 0, sizeof(_bytes)); ++ }; ++ ++ CompactHashtableStats* symbol_stats() { return &_symbol_stats; } ++ ++ void record(MetaspaceObj::Type type, int byte_size, bool read_only) { ++ assert(int(type) >= 0 && type < MetaspaceObj::_number_of_types, "sanity"); ++ int which = (read_only) ? RO : RW; ++ _counts[which][type] ++; ++ _bytes [which][type] += byte_size; ++ } ++ ++ void record_other_type(int byte_size, bool read_only) { ++ int which = (read_only) ? RO : RW; ++ _bytes [which][OtherType] += byte_size; ++ } ++ ++ void print_stats(int ro_all, int rw_all); ++}; ++ ++#endif // SHARE_VM_CDS_DUMPALLOCSTATS_HPP +diff --git a/hotspot/src/share/vm/cds/dynamicArchive.cpp b/hotspot/src/share/vm/cds/dynamicArchive.cpp +new file mode 100644 +index 000000000..efed275c8 +--- /dev/null ++++ b/hotspot/src/share/vm/cds/dynamicArchive.cpp +@@ -0,0 +1,412 @@ ++/* ++ * Copyright (c) 2019, 2021, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2022, Huawei Technologies Co., Ltd. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ * ++ */ ++ ++#include "precompiled.hpp" ++#include "cds/archiveBuilder.hpp" ++#include "cds/archiveUtils.hpp" ++#include "cds/dynamicArchive.hpp" ++#include "classfile/systemDictionaryShared.hpp" ++#include "runtime/vm_operations.hpp" ++#include "runtime/arguments.hpp" ++#include "runtime/vmThread.hpp" ++#include "memory/metaspaceShared.hpp" ++#include "memory/filemap.hpp" ++#include "memory/metaspaceClosure.hpp" ++#include "utilities/exceptions.hpp" ++#include "utilities/align.hpp" ++#include "utilities/bitMap.hpp" ++#include "utilities/exceptions.hpp" ++ ++class DynamicArchiveBuilder : public ArchiveBuilder { ++public: ++ static int dynamic_dump_method_comparator(Method* a, Method* b) { ++ Symbol* a_name = a->name(); ++ Symbol* b_name = b->name(); ++ ++ if (a_name == b_name) { ++ return 0; ++ } ++ ++ u4 a_offset = ArchiveBuilder::current()->any_to_offset_u4(a_name); ++ u4 b_offset = ArchiveBuilder::current()->any_to_offset_u4(b_name); ++ ++ if (a_offset < b_offset) { ++ return -1; ++ } else { ++ assert(a_offset > b_offset, "must be"); ++ return 1; ++ } ++ } ++ ++public: ++ FileMapInfo::DynamicArchiveHeader* _header; ++ ++ void init_header(); ++ void release_header(); ++ void sort_methods(); ++ void sort_methods(InstanceKlass* ik) const; ++ void remark_pointers_for_instance_klass(InstanceKlass* k, bool should_mark) const; ++ void write_archive(char* serialized_data); ++ virtual void iterate_roots(MetaspaceClosure* it, bool is_relocating_pointers) { ++ SystemDictionaryShared::dumptime_classes_do(it); ++ } ++ ++ // Do this before and after the archive dump to see if any corruption ++ // is caused by dynamic dumping. ++ void verify_universe(const char* info) { ++ if (VerifyBeforeExit) { ++ if (InfoDynamicCDS) { ++ dynamic_cds_log->print_cr("Verify %s", info); ++ } ++ // Among other things, this ensures that Eden top is correct. ++ Universe::heap()->prepare_for_verify(); ++ Universe::verify(info); ++ } ++ } ++ ++ void doit() { ++ SystemDictionaryShared::start_dumping(); ++ ++ verify_universe("Before CDS dynamic dump"); ++ DEBUG_ONLY(SystemDictionaryShared::NoClassLoadingMark nclm); ++ ++ // No need DumpTimeTable_lock, since jdk8 doesn't support jcmd dump. ++ // Just remains this lock. ++ MutexLockerEx ml(DumpTimeTable_lock, Mutex::_no_safepoint_check_flag); ++ SystemDictionaryShared::check_excluded_classes(); ++ SystemDictionaryShared::replace_klass_in_constantPool(); ++ ++ init_header(); ++ gather_source_objs(); ++ if (klasses()->length() == 0) { ++ if (InfoDynamicCDS) { ++ dynamic_cds_log->print_cr("No classes gathered, so do not generate Dynamic CDS jsa"); ++ } ++ return; ++ } ++ reserve_buffer(); ++ ++ if (InfoDynamicCDS) { ++ dynamic_cds_log->print_cr("Copying %d klasses and %d symbols", ++ klasses()->length(), symbols()->length()); ++ } ++ dump_rw_metadata(); ++ dump_ro_metadata(); ++ relocate_metaspaceobj_embedded_pointers(); ++ relocate_roots(); ++ ++ verify_estimate_size(_estimated_metaspaceobj_bytes, "MetaspaceObjs"); ++ ++ char* serialized_data; ++ { ++ // Write the symbol table and system dictionaries to the RO space. ++ // Note that these tables still point to the *original* objects, so ++ // they would need to get the correct addresses. ++ assert(current_dump_space() == ro_region(), "Must be RO space"); ++ SymbolTable::write_to_archive(symbols()); ++ ++ ArchiveBuilder::OtherROAllocMark mark; ++ SystemDictionaryShared::write_to_archive(); ++ ++ serialized_data = ro_region()->top(); ++ DynamicWriteClosure wc(ro_region()); ++ SymbolTable::serialize_shared_table_header(&wc); ++ SystemDictionaryShared::serialize_dictionary_headers(&wc); ++ } ++ ++ verify_estimate_size(_estimated_hashtable_bytes, "Hashtables"); ++ ++ sort_methods(); ++ ++ if (InfoDynamicCDS) { ++ dynamic_cds_log->print_cr("Make classes shareable"); ++ } ++ make_klasses_shareable(); ++ ++ patch_shared_obj_vtable(); ++ ++ relocate_to_requested(); ++ ++ write_archive(serialized_data); ++ release_header(); ++ ++ assert(_num_dump_regions_used == _total_dump_regions, "must be"); ++ verify_universe("After CDS dynamic dump"); ++ } ++}; ++ ++void DynamicArchiveBuilder::init_header() { ++ FileMapInfo* mapinfo = new FileMapInfo(false); ++ assert(FileMapInfo::dynamic_info() == mapinfo, "must be"); ++ _header = mapinfo->dynamic_header(); ++ ++ FileMapInfo* base_info = FileMapInfo::current_info(); ++ _header->set_base_header_crc(base_info->header()->crc()); ++ for (int i = 0; i < MetaspaceShared::n_regions; i++) { ++ _header->set_base_region_crc(i, base_info->header()->space_crc(i)); ++ } ++ ++ _header->populate(base_info, base_info->alignment()); ++} ++ ++void DynamicArchiveBuilder::release_header() { ++ // We temporarily allocated a dynamic FileMapInfo for dumping, which makes it appear we ++ // have mapped a dynamic archive, but we actually have not. We are in a safepoint now. ++ // Let's free it so that if class loading happens after we leave the safepoint, nothing ++ // bad will happen. ++ assert(SafepointSynchronize::is_at_safepoint(), "must be"); ++ FileMapInfo *mapinfo = FileMapInfo::dynamic_info(); ++ assert(mapinfo != NULL && _header == mapinfo->dynamic_header(), "must be"); ++ delete mapinfo; ++ assert(!DynamicArchive::is_mapped(), "must be"); ++ _header = NULL; ++} ++ ++void DynamicArchiveBuilder::sort_methods() { ++ // Because high version support jcmd dynamic cds dump, jvm need go on after dump. ++ // Jdk8 no need as so, just exit after dump. ++ InstanceKlass::disable_method_binary_search(); ++ for (int i = 0; i < klasses()->length(); i++) { ++ Klass* k = klasses()->at(i); ++ if (k->oop_is_instance()) { ++ sort_methods(InstanceKlass::cast(k)); ++ } ++ } ++} ++ ++// The address order of the copied Symbols may be different than when the original ++// klasses were created. Re-sort all the tables. See Method::sort_methods(). ++void DynamicArchiveBuilder::sort_methods(InstanceKlass* ik) const { ++ assert(ik != NULL, "DynamicArchiveBuilder currently doesn't support dumping the base archive"); ++ if (MetaspaceShared::is_in_shared_space(ik)) { ++ // We have reached a supertype that's already in the base archive ++ return; ++ } ++ ++ if (ik->java_mirror() == NULL) { ++ // NULL mirror means this class has already been visited and methods are already sorted ++ return; ++ } ++ ik->remove_java_mirror(); ++ ++ if (DebugDynamicCDS) { ++ ResourceMark rm; ++ dynamic_cds_log->print_cr("sorting methods for " PTR_FORMAT " (" PTR_FORMAT ") %s", ++ p2i(ik), p2i(to_requested(ik)), ik->external_name()); ++ } ++ // Method sorting may re-layout the [iv]tables, which would change the offset(s) ++ // of the locations in an InstanceKlass that would contain pointers. Let's clear ++ // all the existing pointer marking bits, and re-mark the pointers after sorting. ++ remark_pointers_for_instance_klass(ik, false); ++ ++ // Make sure all supertypes have been sorted ++ sort_methods(ik->java_super()); ++ Array* interfaces = ik->local_interfaces(); ++ int len = interfaces->length(); ++ for (int i = 0; i < len; i++) { ++ sort_methods(InstanceKlass::cast(interfaces->at(i))); ++ } ++ ++#ifdef ASSERT ++ if (ik->methods() != NULL) { ++ for (int m = 0; m < ik->methods()->length(); m++) { ++ Symbol* name = ik->methods()->at(m)->name(); ++ assert(MetaspaceShared::is_in_shared_space(name) || is_in_buffer_space(name), "must be"); ++ } ++ } ++ if (ik->default_methods() != NULL) { ++ for (int m = 0; m < ik->default_methods()->length(); m++) { ++ Symbol* name = ik->default_methods()->at(m)->name(); ++ assert(MetaspaceShared::is_in_shared_space(name) || is_in_buffer_space(name), "must be"); ++ } ++ } ++#endif ++ ++ Method::sort_methods(ik->methods(), /*idempotent=*/false, /*set_idnums=*/true, dynamic_dump_method_comparator); ++ if (ik->default_methods() != NULL) { ++ Method::sort_methods(ik->default_methods(), /*idempotent=*/false, /*set_idnums=*/false, dynamic_dump_method_comparator); ++ } ++ ++ EXCEPTION_MARK; ++ ++ ik->vtable()->initialize_vtable(false, CATCH); // No need checkconstraints ++ CLEAR_PENDING_EXCEPTION; ++ ik->itable()->initialize_itable(false, CATCH); ++ CLEAR_PENDING_EXCEPTION; ++ ++ // Set all the pointer marking bits after sorting. ++ remark_pointers_for_instance_klass(ik, true); ++} ++ ++template ++class PointerRemarker: public MetaspaceClosure { ++public: ++ virtual bool do_ref(Ref* ref, bool read_only) { ++ if (should_mark) { ++ ArchivePtrMarker::mark_pointer(ref->addr()); ++ } else { ++ ArchivePtrMarker::clear_pointer(ref->addr()); ++ } ++ return false; // don't recurse ++ } ++}; ++ ++void DynamicArchiveBuilder::remark_pointers_for_instance_klass(InstanceKlass* k, bool should_mark) const { ++ if (should_mark) { ++ PointerRemarker marker; ++ k->metaspace_pointers_do(&marker); ++ marker.finish(); ++ } else { ++ PointerRemarker marker; ++ k->metaspace_pointers_do(&marker); ++ marker.finish(); ++ } ++} ++ ++void DynamicArchiveBuilder::write_archive(char* serialized_data) { ++ _header->set_serialized_data(serialized_data); ++ ++ FileMapInfo* dynamic_info = FileMapInfo::dynamic_info(); ++ assert(dynamic_info != NULL, "Sanity"); ++ ++ // Update file offset ++ ArchiveBuilder::write_archive(dynamic_info); ++ ++ // Write into file ++ dynamic_info->open_for_write(); ++ dynamic_info->set_requested_base((char*)MetaspaceShared::requested_base_address()); ++ dynamic_info->set_header_base_archive_name_size(strlen(Arguments::GetSharedArchivePath()) + 1); ++ dynamic_info->set_header_crc(dynamic_info->compute_header_crc()); ++ ArchiveBuilder::write_archive(dynamic_info); ++ ++ address base = _requested_dynamic_archive_bottom; ++ address top = _requested_dynamic_archive_top; ++ size_t file_size = pointer_delta(top, base, sizeof(char)); ++ ++ if (InfoDynamicCDS) { ++ dynamic_cds_log->print_cr("Written dynamic archive " PTR_FORMAT " - " PTR_FORMAT ++ " , " SIZE_FORMAT " bytes total]", ++ p2i(base), p2i(top), file_size); ++ ++ dynamic_cds_log->print_cr("%d klasses; %d symbols", klasses()->length(), symbols()->length()); ++ } ++} ++ ++class VM_GC_Sync_Operation : public VM_Operation { ++public: ++ ++ VM_GC_Sync_Operation() : VM_Operation() { } ++ ++ // Acquires the Heap_lock. ++ virtual bool doit_prologue() { ++ Heap_lock->lock(); ++ return true; ++ } ++ // Releases the Heap_lock. ++ virtual void doit_epilogue() { ++ Heap_lock->unlock(); ++ } ++}; ++ ++class VM_PopulateDynamicDumpSharedSpace : public VM_GC_Sync_Operation { ++ DynamicArchiveBuilder builder; ++public: ++ VM_PopulateDynamicDumpSharedSpace() : VM_GC_Sync_Operation() {} ++ VMOp_Type type() const { return VMOp_PopulateDumpSharedSpace; } ++ void doit() { ++ if (DynamicDumpSharedSpaces == false) { ++ return; ++ } ++ ResourceMark rm; ++ ++ if (SystemDictionaryShared::empty_dumptime_table()) { ++ tty->print_cr("There is no class to be included in the dynamic archive."); ++ return; ++ } ++ ++ builder.doit(); ++ ++ DynamicDumpSharedSpaces = false; ++ exit(0); ++ } ++}; ++ ++bool DynamicArchive::_has_been_dumped_once = false; ++ ++void DynamicArchive::prepare_for_dynamic_dumping_at_exit() { ++ { ++ MutexLockerEx ml(DumpTimeTable_lock, Mutex::_no_safepoint_check_flag); ++ if (DynamicArchive::has_been_dumped_once()) { ++ return; ++ } else { ++ DynamicArchive::set_has_been_dumped_once(); ++ } ++ } ++ EXCEPTION_MARK; ++ ResourceMark rm(THREAD); ++ MetaspaceShared::link_and_cleanup_shared_classes(THREAD); ++ ++ if (HAS_PENDING_EXCEPTION) { ++ tty->print_cr("ArchiveClassesAtExit has failed"); ++ tty->print_cr("%s: %s", PENDING_EXCEPTION->klass()->external_name(), ++ java_lang_String::as_utf8_string(java_lang_Throwable::message(PENDING_EXCEPTION))); ++ // We cannot continue to dump the archive anymore. ++ DynamicDumpSharedSpaces = false; ++ CLEAR_PENDING_EXCEPTION; ++ } ++} ++ ++void DynamicArchive::dump() { ++ if (Arguments::GetSharedDynamicArchivePath() == NULL) { ++ tty->print_cr("SharedDynamicArchivePath is not specified"); ++ return; ++ } ++ ++ VM_PopulateDynamicDumpSharedSpace op; ++ VMThread::execute(&op); ++} ++ ++bool DynamicArchive::validate(FileMapInfo* dynamic_info) { ++ assert(!dynamic_info->is_static(), "must be"); ++ // Check if the recorded base archive matches with the current one ++ FileMapInfo* base_info = FileMapInfo::current_info(); ++ FileMapInfo::DynamicArchiveHeader* dynamic_header = dynamic_info->dynamic_header(); ++ ++ // Check the header crc ++ if (dynamic_header->base_header_crc() != base_info->crc()) { ++ FileMapInfo::fail_continue("Dynamic archive cannot be used: static archive header checksum verification failed."); ++ return false; ++ } ++ ++ // Check each space's crc ++ for (int i = 0; i < MetaspaceShared::n_regions; i++) { ++ if (dynamic_header->base_region_crc(i) != base_info->space_crc(i)) { ++ FileMapInfo::fail_continue("Dynamic archive cannot be used: static archive region #%d checksum verification failed.", i); ++ return false; ++ } ++ } ++ ++ return true; ++} +diff --git a/hotspot/src/share/vm/cds/dynamicArchive.hpp b/hotspot/src/share/vm/cds/dynamicArchive.hpp +new file mode 100644 +index 000000000..1d5b71221 +--- /dev/null ++++ b/hotspot/src/share/vm/cds/dynamicArchive.hpp +@@ -0,0 +1,54 @@ ++/* ++ * Copyright (c) 2019, 2021, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2022, Huawei Technologies Co., Ltd. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ * ++ */ ++ ++#ifndef SHARE_VM_CDS_DYNAMICARCHIVE_HPP ++#define SHARE_VM_CDS_DYNAMICARCHIVE_HPP ++ ++//#include "classfile/compactHashtable.hpp" ++#include "memory/allocation.hpp" ++#include "memory/filemap.hpp" ++#include "memory/memRegion.hpp" ++#include "runtime/virtualspace.hpp" ++#include "oops/oop.hpp" ++#include "utilities/exceptions.hpp" ++#include "utilities/macros.hpp" ++#include "utilities/resourceHash.hpp" ++ ++#if INCLUDE_CDS ++ ++// Fixme ++class DynamicArchive : AllStatic { ++ static bool _has_been_dumped_once; ++public: ++ static void prepare_for_dynamic_dumping_at_exit(); ++ static void dump(); ++ static bool has_been_dumped_once() { return _has_been_dumped_once; } ++ static void set_has_been_dumped_once() { _has_been_dumped_once = true; } ++ static bool is_mapped() { return FileMapInfo::dynamic_info() != NULL; } ++ static bool validate(FileMapInfo* dynamic_info); ++}; ++ ++#endif // INCLUDE_CDS ++#endif // SHARE_VM_CDS_DYNAMICARCHIVE_HPP +diff --git a/hotspot/src/share/vm/classfile/classFileParser.cpp b/hotspot/src/share/vm/classfile/classFileParser.cpp +index 5c36a9d6f..ae9199525 100644 +--- a/hotspot/src/share/vm/classfile/classFileParser.cpp ++++ b/hotspot/src/share/vm/classfile/classFileParser.cpp +@@ -4376,6 +4376,13 @@ instanceKlassHandle ClassFileParser::parseClassFile(Symbol* name, + instanceKlassHandle this_klass (THREAD, preserve_this_klass); + debug_only(this_klass->verify();) + ++#if INCLUDE_CDS ++ if (DynamicDumpSharedSpaces && !SystemDictionary::is_builtin_loader(class_loader)) { ++ this_klass->set_shared_classpath_index(UNREGISTERED_INDEX); ++ SystemDictionaryShared::set_shared_class_misc_info(this_klass(), cfs); ++ } ++#endif // INCLUDE_CDS ++ + // Clear class if no error has occurred so destructor doesn't deallocate it + _klass = NULL; + return this_klass; +diff --git a/hotspot/src/share/vm/classfile/classLoaderExt.hpp b/hotspot/src/share/vm/classfile/classLoaderExt.hpp +index 7b2360af9..3bd4f3bde 100644 +--- a/hotspot/src/share/vm/classfile/classLoaderExt.hpp ++++ b/hotspot/src/share/vm/classfile/classLoaderExt.hpp +@@ -48,7 +48,7 @@ public: + instanceKlassHandle record_result(const int classpath_index, + ClassPathEntry* e, instanceKlassHandle result, TRAPS) { + if (ClassLoader::add_package(_file_name, classpath_index, THREAD)) { +- if (DumpSharedSpaces) { ++ if (DumpSharedSpaces || DynamicDumpSharedSpaces) { + result->set_shared_classpath_index(classpath_index); + } + return result; +diff --git a/hotspot/src/share/vm/classfile/compactHashtable.cpp b/hotspot/src/share/vm/classfile/compactHashtable.cpp +new file mode 100644 +index 000000000..232a89fa1 +--- /dev/null ++++ b/hotspot/src/share/vm/classfile/compactHashtable.cpp +@@ -0,0 +1,216 @@ ++/* ++ * Copyright (c) 1997, 2021, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2022, Huawei Technologies Co., Ltd. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ * ++ */ ++ ++#include "precompiled.hpp" ++#include "jvm.h" ++#include "cds/archiveBuilder.hpp" ++#include "classfile/compactHashtable.hpp" ++#include "classfile/javaClasses.hpp" ++#include "memory/metadataFactory.hpp" ++#include "runtime/arguments.hpp" ++#include "runtime/globals.hpp" ++#include "runtime/vmThread.hpp" ++#include "utilities/align.hpp" ++#include "utilities/numberSeq.hpp" ++ ++///////////////////////////////////////////////////// ++// ++// The compact hash table writer implementations ++// ++CompactHashtableWriter::CompactHashtableWriter(int num_entries, ++ CompactHashtableStats* stats) { ++ Arguments::assert_is_dumping_archive(); ++ assert(num_entries >= 0, "sanity"); ++ _num_buckets = calculate_num_buckets(num_entries); ++ assert(_num_buckets > 0, "no buckets"); ++ ++ _num_entries_written = 0; ++ _buckets = NEW_C_HEAP_ARRAY(GrowableArray*, _num_buckets, mtSymbol); ++ for (int i = 0; i < _num_buckets; i++) { ++ _buckets[i] = new (ResourceObj::C_HEAP, mtSymbol) GrowableArray(0, true, mtSymbol); ++ } ++ ++ _stats = stats; ++ _compact_buckets = NULL; ++ _compact_entries = NULL; ++ _num_empty_buckets = 0; ++ _num_value_only_buckets = 0; ++ _num_other_buckets = 0; ++} ++ ++CompactHashtableWriter::~CompactHashtableWriter() { ++ for (int index = 0; index < _num_buckets; index++) { ++ GrowableArray* bucket = _buckets[index]; ++ delete bucket; ++ } ++ ++ FREE_C_HEAP_ARRAY(GrowableArray*, _buckets, mtSymbol); ++} ++ ++size_t CompactHashtableWriter::estimate_size(int num_entries) { ++ int num_buckets = calculate_num_buckets(num_entries); ++ size_t bucket_bytes = ArchiveBuilder::ro_array_bytesize(num_buckets + 1); ++ ++ // In worst case, we have no VALUE_ONLY_BUCKET_TYPE, so each entry takes 2 slots ++ int entries_space = 2 * num_entries; ++ size_t entry_bytes = ArchiveBuilder::ro_array_bytesize(entries_space); ++ ++ return bucket_bytes ++ + entry_bytes ++ + SimpleCompactHashtable::calculate_header_size(); ++} ++ ++// Add a symbol entry to the temporary hash table ++void CompactHashtableWriter::add(unsigned int hash, u4 value) { ++ int index = hash % _num_buckets; ++ _buckets[index]->append_if_missing(Entry(hash, value)); ++ _num_entries_written++; ++} ++ ++void CompactHashtableWriter::allocate_table() { ++ int entries_space = 0; ++ for (int index = 0; index < _num_buckets; index++) { ++ GrowableArray* bucket = _buckets[index]; ++ int bucket_size = bucket->length(); ++ if (bucket_size == 1) { ++ entries_space++; ++ } else if (bucket_size > 1) { ++ entries_space += 2 * bucket_size; ++ } ++ } ++ ++ if (entries_space & ~BUCKET_OFFSET_MASK) { ++ vm_exit_during_initialization("CompactHashtableWriter::allocate_table: Overflow! " ++ "Too many entries."); ++ } ++ ++ _compact_buckets = ArchiveBuilder::new_ro_array(_num_buckets + 1); ++ _compact_entries = ArchiveBuilder::new_ro_array(entries_space); ++ ++ _stats->bucket_count = _num_buckets; ++ _stats->bucket_bytes = align_up(_compact_buckets->size() * BytesPerWord, ++ KlassAlignmentInBytes); ++ _stats->hashentry_count = _num_entries_written; ++ _stats->hashentry_bytes = align_up(_compact_entries->size() * BytesPerWord, ++ KlassAlignmentInBytes); ++} ++ ++// Write the compact table's buckets ++void CompactHashtableWriter::dump_table(NumberSeq* summary) { ++ u4 offset = 0; ++ for (int index = 0; index < _num_buckets; index++) { ++ GrowableArray* bucket = _buckets[index]; ++ int bucket_size = bucket->length(); ++ if (bucket_size == 1) { ++ // bucket with one entry is compacted and only has the symbol offset ++ _compact_buckets->at_put(index, BUCKET_INFO(offset, VALUE_ONLY_BUCKET_TYPE)); ++ ++ Entry ent = bucket->at(0); ++ _compact_entries->at_put(offset++, ent.value()); ++ _num_value_only_buckets++; ++ } else { ++ // regular bucket, each entry is a symbol (hash, offset) pair ++ _compact_buckets->at_put(index, BUCKET_INFO(offset, REGULAR_BUCKET_TYPE)); ++ ++ for (int i=0; iat(i); ++ _compact_entries->at_put(offset++, u4(ent.hash())); // write entry hash ++ _compact_entries->at_put(offset++, ent.value()); ++ } ++ if (bucket_size == 0) { ++ _num_empty_buckets++; ++ } else { ++ _num_other_buckets++; ++ } ++ } ++ summary->add(bucket_size); ++ } ++ ++ // Mark the end of the buckets ++ _compact_buckets->at_put(_num_buckets, BUCKET_INFO(offset, TABLEEND_BUCKET_TYPE)); ++ assert(offset == (u4)_compact_entries->length(), "sanity"); ++} ++ ++// Write the compact table ++void CompactHashtableWriter::dump(SimpleCompactHashtable *cht, const char* table_name) { ++ NumberSeq summary; ++ allocate_table(); ++ dump_table(&summary); ++ ++ int table_bytes = _stats->bucket_bytes + _stats->hashentry_bytes; ++ address base_address = address(SharedBaseAddress); ++ cht->init(base_address, _num_entries_written, _num_buckets, ++ _compact_buckets->data(), _compact_entries->data()); ++ ++ if (InfoDynamicCDS) { ++ double avg_cost = 0.0; ++ if (_num_entries_written > 0) { ++ avg_cost = double(table_bytes)/double(_num_entries_written); ++ } ++ dynamic_cds_log->print_cr("Shared %s table stats -------- base: " PTR_FORMAT, ++ table_name, (intptr_t)base_address); ++ dynamic_cds_log->print_cr("Number of entries : %9d", _num_entries_written); ++ dynamic_cds_log->print_cr("Total bytes used : %9d", table_bytes); ++ dynamic_cds_log->print_cr("Average bytes per entry : %9.3f", avg_cost); ++ dynamic_cds_log->print_cr("Average bucket size : %9.3f", summary.avg()); ++ dynamic_cds_log->print_cr("Variance of bucket size : %9.3f", summary.variance()); ++ dynamic_cds_log->print_cr("Std. dev. of bucket size: %9.3f", summary.sd()); ++ dynamic_cds_log->print_cr("Maximum bucket size : %9d", (int)summary.maximum()); ++ dynamic_cds_log->print_cr("Empty buckets : %9d", _num_empty_buckets); ++ dynamic_cds_log->print_cr("Value_Only buckets : %9d", _num_value_only_buckets); ++ dynamic_cds_log->print_cr("Other buckets : %9d", _num_other_buckets); ++ } ++} ++ ++///////////////////////////////////////////////////////////// ++// ++// The CompactHashtable implementation ++// ++ ++void SimpleCompactHashtable::init(address base_address, u4 entry_count, u4 bucket_count, u4* buckets, u4* entries) { ++ _bucket_count = bucket_count; ++ _entry_count = entry_count; ++ _base_address = base_address; ++ _buckets = buckets; ++ _entries = entries; ++} ++ ++size_t SimpleCompactHashtable::calculate_header_size() { ++ // We have 5 fields. Each takes up sizeof(intptr_t). See WriteClosure::do_u4 ++ size_t bytes = sizeof(intptr_t) * 5; ++ return bytes; ++} ++ ++void SimpleCompactHashtable::serialize_header(SerializeClosure* soc) { ++ // NOTE: if you change this function, you MUST change the number 5 in ++ // calculate_header_size() accordingly. ++ soc->do_u4(&_entry_count); ++ soc->do_u4(&_bucket_count); ++ soc->do_ptr((void**)&_buckets); ++ soc->do_ptr((void**)&_entries); ++ if (soc->reading()) { ++ _base_address = (address)SharedBaseAddress; ++ } ++} +diff --git a/hotspot/src/share/vm/classfile/compactHashtable.hpp b/hotspot/src/share/vm/classfile/compactHashtable.hpp +new file mode 100644 +index 000000000..727b3ebfb +--- /dev/null ++++ b/hotspot/src/share/vm/classfile/compactHashtable.hpp +@@ -0,0 +1,349 @@ ++/* ++ * Copyright (c) 1997, 2021, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2022, Huawei Technologies Co., Ltd. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ * ++ */ ++ ++#ifndef SHARE_VM_CLASSFILE_COMPACTHASHTABLE_HPP ++#define SHARE_VM_CLASSFILE_COMPACTHASHTABLE_HPP ++ ++#include "oops/symbol.hpp" ++#include "runtime/globals.hpp" ++#include "utilities/array.hpp" ++#include "utilities/growableArray.hpp" ++#include "utilities/numberSeq.hpp" ++ ++ ++template < ++ typename K, ++ typename V, ++ V (*DECODE)(address base_address, u4 offset), ++ bool (*EQUALS)(V value, K key, int len) ++ > ++class CompactHashtable; ++class NumberSeq; ++class SimpleCompactHashtable; ++ ++// Stats for symbol tables in the CDS archive ++class CompactHashtableStats { ++public: ++ int hashentry_count; ++ int hashentry_bytes; ++ int bucket_count; ++ int bucket_bytes; ++ ++ CompactHashtableStats() : ++ hashentry_count(0), hashentry_bytes(0), ++ bucket_count(0), bucket_bytes(0) {} ++}; ++ ++///////////////////////////////////////////////////////////////////////// ++// ++// The compact hash table writer. Used at dump time for writing out ++// the compact table to the shared archive. ++// ++// At dump time, the CompactHashtableWriter obtains all entries from the ++// symbol/string table and adds them to a new temporary hash table. The hash ++// table size (number of buckets) is calculated using ++// '(num_entries + bucket_size - 1) / bucket_size'. The default bucket ++// size is 4 and can be changed by -XX:SharedSymbolTableBucketSize option. ++// 4 is chosen because it produces smaller sized bucket on average for ++// faster lookup. It also has relatively small number of empty buckets and ++// good distribution of the entries. ++// ++// We use a simple hash function (hash % num_bucket) for the table. ++// The new table is compacted when written out. Please see comments ++// above the CompactHashtable class for the table layout detail. The bucket ++// offsets are written to the archive as part of the compact table. The ++// bucket offset is encoded in the low 30-bit (0-29) and the bucket type ++// (regular or compact) are encoded in bit[31, 30]. For buckets with more ++// than one entry, both hash and entry offset are written to the ++// table. For buckets with only one entry, only the entry offset is written ++// to the table and the buckets are tagged as compact in their type bits. ++// Buckets without entry are skipped from the table. Their offsets are ++// still written out for faster lookup. ++// ++class CompactHashtableWriter: public StackObj { ++public: ++ class Entry { ++ unsigned int _hash; ++ u4 _value; ++ ++ public: ++ Entry() {} ++ Entry(unsigned int hash, u4 val) : _hash(hash), _value(val) {} ++ ++ u4 value() { ++ return _value; ++ } ++ unsigned int hash() { ++ return _hash; ++ } ++ ++ bool operator==(const CompactHashtableWriter::Entry& other) { ++ return (_value == other._value && _hash == other._hash); ++ } ++ }; // class CompactHashtableWriter::Entry ++ ++private: ++ int _num_entries_written; ++ int _num_buckets; ++ int _num_empty_buckets; ++ int _num_value_only_buckets; ++ int _num_other_buckets; ++ GrowableArray** _buckets; ++ CompactHashtableStats* _stats; ++ Array* _compact_buckets; ++ Array* _compact_entries; ++ ++public: ++ // This is called at dump-time only ++ CompactHashtableWriter(int num_entries, CompactHashtableStats* stats); ++ ~CompactHashtableWriter(); ++ ++ void add(unsigned int hash, u4 value); ++ ++private: ++ void allocate_table(); ++ void dump_table(NumberSeq* summary); ++ ++ static int calculate_num_buckets(int num_entries) { ++ int num_buckets = num_entries / SharedSymbolTableBucketSize; ++ // calculation of num_buckets can result in zero buckets, we need at least one ++ return (num_buckets < 1) ? 1 : num_buckets; ++ } ++ ++public: ++ void dump(SimpleCompactHashtable *cht, const char* table_name); ++ ++ static size_t estimate_size(int num_entries); ++}; ++ ++#define REGULAR_BUCKET_TYPE 0 ++#define VALUE_ONLY_BUCKET_TYPE 1 ++#define TABLEEND_BUCKET_TYPE 3 ++#define BUCKET_OFFSET_MASK 0x3FFFFFFF ++#define BUCKET_OFFSET(info) ((info) & BUCKET_OFFSET_MASK) ++#define BUCKET_TYPE_SHIFT 30 ++#define BUCKET_TYPE(info) (((info) & ~BUCKET_OFFSET_MASK) >> BUCKET_TYPE_SHIFT) ++#define BUCKET_INFO(offset, type) (((type) << BUCKET_TYPE_SHIFT) | ((offset) & BUCKET_OFFSET_MASK)) ++ ++///////////////////////////////////////////////////////////////////////////// ++// ++// CompactHashtable is used to store the CDS archive's symbol/string tables. ++// ++// Because these tables are read-only (no entries can be added/deleted) at run-time ++// and tend to have large number of entries, we try to minimize the footprint ++// cost per entry. ++// ++// The CompactHashtable is split into two arrays ++// ++// u4 buckets[num_buckets+1]; // bit[31,30]: type; bit[29-0]: offset ++// u4 entries[] ++// ++// The size of buckets[] is 'num_buckets + 1'. Each entry of ++// buckets[] is a 32-bit encoding of the bucket type and bucket offset, ++// with the type in the left-most 2-bit and offset in the remaining 30-bit. ++// The last entry is a special type. It contains the end of the last ++// bucket. ++// ++// There are two types of buckets, regular buckets and value_only buckets. The ++// value_only buckets have '01' in their highest 2-bit, and regular buckets have ++// '00' in their highest 2-bit. ++// ++// For normal buckets, each entry is 8 bytes in the entries[]: ++// u4 hash; /* symbol/string hash */ ++// union { ++// u4 offset; /* Symbol* sym = (Symbol*)(base_address + offset) */ ++// narrowOop str; /* String narrowOop encoding */ ++// } ++// ++// ++// For value_only buckets, each entry has only the 4-byte 'offset' in the entries[]. ++// ++// Example -- note that the second bucket is a VALUE_ONLY_BUCKET_TYPE so the hash code ++// is skipped. ++// buckets[0, 4, 5, ....] ++// | | | ++// | | +---+ ++// | | | ++// | +----+ | ++// v v v ++// entries[H,O,H,O,O,H,O,H,O.....] ++// ++// See CompactHashtable::lookup() for how the table is searched at runtime. ++// See CompactHashtableWriter::dump() for how the table is written at CDS ++// dump time. ++// ++class SimpleCompactHashtable { ++protected: ++ address _base_address; ++ u4 _bucket_count; ++ u4 _entry_count; ++ u4* _buckets; ++ u4* _entries; ++ ++public: ++ SimpleCompactHashtable() { ++ _entry_count = 0; ++ _bucket_count = 0; ++ _buckets = 0; ++ _entries = 0; ++ } ++ ++ void reset() { ++ _bucket_count = 0; ++ _entry_count = 0; ++ _buckets = 0; ++ _entries = 0; ++ } ++ ++ void init(address base_address, u4 entry_count, u4 bucket_count, u4* buckets, u4* entries); ++ ++ // Read/Write the table's header from/to the CDS archive ++ void serialize_header(SerializeClosure* soc) NOT_CDS_RETURN; ++ ++ inline bool empty() const { ++ return (_entry_count == 0); ++ } ++ ++ inline size_t entry_count() const { ++ return _entry_count; ++ } ++ ++ static size_t calculate_header_size(); ++}; ++ ++template < ++ typename K, ++ typename V, ++ V (*DECODE)(address base_address, u4 offset), ++ bool (*EQUALS)(V value, K key, int len) ++ > ++class CompactHashtable : public SimpleCompactHashtable { ++ friend class VMStructs; ++ ++ V decode(u4 offset) const { ++ return DECODE(_base_address, offset); ++ } ++ ++public: ++ // Lookup a value V from the compact table using key K ++ inline V lookup(K key, unsigned int hash, int len) const { ++ if (_entry_count > 0) { ++ int index = hash % _bucket_count; ++ u4 bucket_info = _buckets[index]; ++ u4 bucket_offset = BUCKET_OFFSET(bucket_info); ++ int bucket_type = BUCKET_TYPE(bucket_info); ++ u4* entry = _entries + bucket_offset; ++ ++ if (bucket_type == VALUE_ONLY_BUCKET_TYPE) { ++ V value = decode(entry[0]); ++ if (EQUALS(value, key, len)) { ++ return value; ++ } ++ } else { ++ // This is a regular bucket, which has more than one ++ // entries. Each entry is a pair of entry (hash, offset). ++ // Seek until the end of the bucket. ++ u4* entry_max = _entries + BUCKET_OFFSET(_buckets[index + 1]); ++ while (entry < entry_max) { ++ unsigned int h = (unsigned int)(entry[0]); ++ if (h == hash) { ++ V value = decode(entry[1]); ++ if (EQUALS(value, key, len)) { ++ return value; ++ } ++ } ++ entry += 2; ++ } ++ } ++ } ++ return NULL; ++ } ++ ++ template ++ inline void iterate(ITER* iter) const { ++ for (u4 i = 0; i < _bucket_count; i++) { ++ u4 bucket_info = _buckets[i]; ++ u4 bucket_offset = BUCKET_OFFSET(bucket_info); ++ int bucket_type = BUCKET_TYPE(bucket_info); ++ u4* entry = _entries + bucket_offset; ++ ++ if (bucket_type == VALUE_ONLY_BUCKET_TYPE) { ++ iter->do_value(decode(entry[0])); ++ } else { ++ u4*entry_max = _entries + BUCKET_OFFSET(_buckets[i + 1]); ++ while (entry < entry_max) { ++ iter->do_value(decode(entry[1])); ++ entry += 2; ++ } ++ } ++ } ++ } ++ ++ void print_table_statistics(outputStream* st, const char* name) { ++ st->print_cr("%s statistics:", name); ++ int total_entries = 0; ++ int max_bucket = 0; ++ for (u4 i = 0; i < _bucket_count; i++) { ++ u4 bucket_info = _buckets[i]; ++ int bucket_type = BUCKET_TYPE(bucket_info); ++ int bucket_size; ++ ++ if (bucket_type == VALUE_ONLY_BUCKET_TYPE) { ++ bucket_size = 1; ++ } else { ++ bucket_size = (BUCKET_OFFSET(_buckets[i + 1]) - BUCKET_OFFSET(bucket_info)) / 2; ++ } ++ total_entries += bucket_size; ++ if (max_bucket < bucket_size) { ++ max_bucket = bucket_size; ++ } ++ } ++ st->print_cr("Number of buckets : %9d", _bucket_count); ++ st->print_cr("Number of entries : %9d", total_entries); ++ st->print_cr("Maximum bucket size : %9d", max_bucket); ++ } ++}; ++ ++//////////////////////////////////////////////////////////////////////// ++// ++// OffsetCompactHashtable -- This is used to store many types of objects ++// in the CDS archive. On 64-bit platforms, we save space by using a 32-bit ++// offset from the CDS base address. ++ ++template ++inline V read_value_from_compact_hashtable(address base_address, u4 offset) { ++ return (V)(base_address + offset); ++} ++ ++template < ++ typename K, ++ typename V, ++ bool (*EQUALS)(V value, K key, int len) ++ > ++class OffsetCompactHashtable : public CompactHashtable< ++ K, V, read_value_from_compact_hashtable, EQUALS> { ++}; ++ ++#endif // SHARE_VM_CLASSFILE_COMPACTHASHTABLE_HPP +diff --git a/hotspot/src/share/vm/classfile/sharedClassUtil.hpp b/hotspot/src/share/vm/classfile/sharedClassUtil.hpp +index 13be2b1b5..b24e84d45 100644 +--- a/hotspot/src/share/vm/classfile/sharedClassUtil.hpp ++++ b/hotspot/src/share/vm/classfile/sharedClassUtil.hpp +@@ -43,6 +43,10 @@ public: + return new FileMapInfo::FileMapHeader(); + } + ++ static FileMapInfo::DynamicArchiveHeader* allocate_dynamic_archive_header() { ++ return new FileMapInfo::DynamicArchiveHeader(); ++ } ++ + static size_t file_map_header_size() { + return sizeof(FileMapInfo::FileMapHeader); + } +diff --git a/hotspot/src/share/vm/classfile/symbolTable.cpp b/hotspot/src/share/vm/classfile/symbolTable.cpp +index 8dd4e6b21..6a2d8077f 100644 +--- a/hotspot/src/share/vm/classfile/symbolTable.cpp ++++ b/hotspot/src/share/vm/classfile/symbolTable.cpp +@@ -23,6 +23,8 @@ + */ + + #include "precompiled.hpp" ++#include "cds/archiveBuilder.hpp" ++#include "cds/dynamicArchive.hpp" + #include "classfile/altHashing.hpp" + #include "classfile/javaClasses.hpp" + #include "classfile/symbolTable.hpp" +@@ -42,6 +44,19 @@ + + PRAGMA_FORMAT_MUTE_WARNINGS_FOR_GCC + ++inline bool symbol_equals_compact_hashtable_entry(Symbol* value, const char* key, int len) { ++ if (value->equals(key, len)) { ++ return true; ++ } else { ++ return false; ++ } ++} ++ ++static OffsetCompactHashtable< ++ const char*, Symbol*, ++ symbol_equals_compact_hashtable_entry ++> _dynamic_shared_table; ++ + // -------------------------------------------------------------------------- + + // the number of buckets a thread claims +@@ -95,6 +110,7 @@ void SymbolTable::symbols_do(SymbolClosure *cl) { + int SymbolTable::_symbols_removed = 0; + int SymbolTable::_symbols_counted = 0; + volatile int SymbolTable::_parallel_claimed_idx = 0; ++volatile bool _lookup_shared_first = false; + + void SymbolTable::buckets_unlink(int start_idx, int end_idx, BucketUnlinkContext* context, size_t* memory_total) { + for (int i = start_idx; i < end_idx; ++i) { +@@ -225,10 +241,25 @@ Symbol* SymbolTable::lookup(int index, const char* name, + unsigned int SymbolTable::hash_symbol(const char* s, int len) { + return use_alternate_hashcode() ? + AltHashing::halfsiphash_32(seed(), (const uint8_t*)s, len) : +- java_lang_String::hash_code(s, len); ++ java_lang_String::hash_code((const jbyte*)s, len); + } + ++#if INCLUDE_CDS ++Symbol* SymbolTable::lookup_shared(const char* name, ++ int len, unsigned int hash) { ++ Symbol* sym = NULL; ++ if (DynamicArchive::is_mapped()) { ++ if (use_alternate_hashcode()) { ++ // hash_code parameter may use alternate hashing algorithm but the shared table ++ // always uses the same original hash code. ++ hash = java_lang_String::hash_code((const jbyte*)name, len); ++ } + ++ sym = _dynamic_shared_table.lookup(name, hash, len); ++ } ++ return sym; ++} ++#endif + // We take care not to be blocking while holding the + // SymbolTable_lock. Otherwise, the system might deadlock, since the + // symboltable is used during compilation (VM_thread) The lock free +@@ -236,12 +267,32 @@ unsigned int SymbolTable::hash_symbol(const char* s, int len) { + // entries in the symbol table during normal execution (only during + // safepoints). + +-Symbol* SymbolTable::lookup(const char* name, int len, TRAPS) { ++Symbol* SymbolTable::lookup_common(const char* name, int len) { + unsigned int hashValue = hash_symbol(name, len); + int index = the_table()->hash_to_index(hashValue); ++ Symbol* s; ++ if (_lookup_shared_first) { ++ s = lookup_shared(name, len, hashValue); ++ if (s == NULL) { ++ _lookup_shared_first = false; ++ s = the_table()->lookup(index, name, len, hashValue); ++ } ++ } else { ++ s = the_table()->lookup(index, name, len, hashValue); ++ if (s == NULL) { ++ s = lookup_shared(name, len, hashValue); ++ if (s!= NULL) { ++ _lookup_shared_first = true; ++ } ++ } ++ } ++ return s; ++} + +- Symbol* s = the_table()->lookup(index, name, len, hashValue); +- ++Symbol* SymbolTable::lookup(const char* name, int len, TRAPS) { ++ unsigned int hashValue = hash_symbol(name, len); ++ int index = the_table()->hash_to_index(hashValue); ++ Symbol* s = lookup_common(name, len); + // Found + if (s != NULL) return s; + +@@ -264,8 +315,7 @@ Symbol* SymbolTable::lookup(const Symbol* sym, int begin, int end, TRAPS) { + len = end - begin; + hashValue = hash_symbol(name, len); + index = the_table()->hash_to_index(hashValue); +- Symbol* s = the_table()->lookup(index, name, len, hashValue); +- ++ Symbol* s = lookup_common(name, len); + // Found + if (s != NULL) return s; + } +@@ -294,9 +344,7 @@ Symbol* SymbolTable::lookup(const Symbol* sym, int begin, int end, TRAPS) { + Symbol* SymbolTable::lookup_only(const char* name, int len, + unsigned int& hash) { + hash = hash_symbol(name, len); +- int index = the_table()->hash_to_index(hash); +- +- Symbol* s = the_table()->lookup(index, name, len, hash); ++ Symbol* s = lookup_common(name, len); + return s; + } + +@@ -501,6 +549,42 @@ void SymbolTable::dump(outputStream* st) { + the_table()->dump_table(st, "SymbolTable"); + } + ++static uintx hash_shared_symbol(const char* s, int len) { ++ return java_lang_String::hash_code((const jbyte*)s, len); ++} ++ ++void SymbolTable::copy_shared_symbol_table(GrowableArray* symbols, ++ CompactHashtableWriter* writer) { ++ ArchiveBuilder* builder = ArchiveBuilder::current(); ++ int len = symbols->length(); ++ for (int i = 0; i < len; i++) { ++ Symbol* sym = ArchiveBuilder::get_relocated_symbol(symbols->at(i)); ++ unsigned int fixed_hash = hash_shared_symbol((const char*)sym->bytes(), sym->utf8_length()); ++ assert(fixed_hash == hash_symbol((const char*)sym->bytes(), sym->utf8_length()), ++ "must not rehash during dumping"); ++ sym->set_permanent(); ++ writer->add(fixed_hash, builder->buffer_to_offset_u4((address)sym)); ++ } ++} ++ ++size_t SymbolTable::estimate_size_for_archive() { ++ return CompactHashtableWriter::estimate_size(the_table()->number_of_entries()); ++} ++ ++void SymbolTable::write_to_archive(GrowableArray* symbols) { ++ CompactHashtableWriter writer(symbols->length(), ArchiveBuilder::symbol_stats()); ++ copy_shared_symbol_table(symbols, &writer); ++ _dynamic_shared_table.reset(); ++ writer.dump(&_dynamic_shared_table, "symbol"); ++} ++ ++void SymbolTable::serialize_shared_table_header(SerializeClosure* soc) { ++ _dynamic_shared_table.serialize_header(soc); ++ if (soc->writing()) { ++ // Sanity. Make sure we don't use the shared table at dump time ++ _dynamic_shared_table.reset(); ++ } ++} + + //--------------------------------------------------------------------------- + // Non-product code +diff --git a/hotspot/src/share/vm/classfile/symbolTable.hpp b/hotspot/src/share/vm/classfile/symbolTable.hpp +index 58fd22343..96eb173d1 100644 +--- a/hotspot/src/share/vm/classfile/symbolTable.hpp ++++ b/hotspot/src/share/vm/classfile/symbolTable.hpp +@@ -25,6 +25,7 @@ + #ifndef SHARE_VM_CLASSFILE_SYMBOLTABLE_HPP + #define SHARE_VM_CLASSFILE_SYMBOLTABLE_HPP + ++#include "classfile/compactHashtable.hpp" + #include "memory/allocation.inline.hpp" + #include "oops/symbol.hpp" + #include "utilities/hashtable.hpp" +@@ -107,6 +108,10 @@ private: + add(loader_data, cp, names_count, name, lengths, cp_indices, hashValues, THREAD); + } + ++ static Symbol* lookup_shared(const char* name, int len, unsigned int hash) NOT_CDS_RETURN_(NULL); ++ ++ static Symbol* lookup_common(const char* name, int len); ++ + Symbol* lookup(int index, const char* name, int len, unsigned int hash); + + SymbolTable() +@@ -237,6 +242,10 @@ public: + static void dump(outputStream* st); + + // Sharing ++private: ++ static void copy_shared_symbol_table(GrowableArray* symbols, ++ CompactHashtableWriter* ch_table); ++public: + static void copy_buckets(char** top, char*end) { + the_table()->Hashtable::copy_buckets(top, end); + } +@@ -246,6 +255,9 @@ public: + static void reverse(void* boundary = NULL) { + the_table()->Hashtable::reverse(boundary); + } ++ static size_t estimate_size_for_archive(); ++ static void write_to_archive(GrowableArray* symbols); ++ static void serialize_shared_table_header(SerializeClosure* soc); + + // Rehash the symbol table if it gets out of balance + static void rehash_table(); +diff --git a/hotspot/src/share/vm/classfile/systemDictionary.cpp b/hotspot/src/share/vm/classfile/systemDictionary.cpp +index 0d937c3ba..0ea2d9b79 100644 +--- a/hotspot/src/share/vm/classfile/systemDictionary.cpp ++++ b/hotspot/src/share/vm/classfile/systemDictionary.cpp +@@ -31,6 +31,7 @@ + #include "classfile/resolutionErrors.hpp" + #include "classfile/systemDictionary.hpp" + #if INCLUDE_CDS ++#include "cds/dynamicArchive.hpp" + #include "classfile/sharedClassUtil.hpp" + #include "classfile/systemDictionaryShared.hpp" + #endif +@@ -185,6 +186,11 @@ bool SystemDictionary::is_app_class_loader(Handle class_loader) { + return (class_loader->klass()->name() == vmSymbols::sun_misc_Launcher_AppClassLoader()); + } + ++bool SystemDictionary::is_builtin_loader(Handle class_loader) { ++ return class_loader.is_null() || ++ class_loader->klass()->name() == vmSymbols::sun_misc_Launcher_AppClassLoader() || ++ class_loader->klass()->name() == vmSymbols::sun_misc_Launcher_ExtClassLoader(); ++} + // ---------------------------------------------------------------------------- + // Resolving of classes + +@@ -1131,76 +1137,92 @@ Klass* SystemDictionary::resolve_from_stream(Symbol* class_name, + check_loader_lock_contention(lockObject, THREAD); + ObjectLocker ol(lockObject, THREAD, DoObjectLock); + ++ instanceKlassHandle k; + TempNewSymbol parsed_name = NULL; + +- // Parse the stream. Note that we do this even though this klass might +- // already be present in the SystemDictionary, otherwise we would not +- // throw potential ClassFormatErrors. +- // +- // Note: "name" is updated. ++#if INCLUDE_CDS ++ if (DynamicArchive::is_mapped()) { ++ k = SystemDictionaryShared::lookup_from_stream(class_name, ++ class_loader, ++ protection_domain, ++ st, ++ CHECK_NULL); ++ } ++#endif + +- // Callers are expected to declare a ResourceMark to determine +- // the lifetime of any updated (resource) allocated under +- // this call to parseClassFile +- ResourceMark rm(THREAD); +- ClassFileParser parser(st); +- instanceKlassHandle k = parser.parseClassFile(class_name, +- loader_data, +- protection_domain, +- parsed_name, +- verify, +- THREAD); +- +- const char* pkg = "java/"; +- size_t pkglen = strlen(pkg); +- if (!HAS_PENDING_EXCEPTION && +- !class_loader.is_null() && +- parsed_name != NULL && +- parsed_name->utf8_length() >= (int)pkglen) { +- ResourceMark rm(THREAD); +- bool prohibited; +- const jbyte* base = parsed_name->base(); +- if ((base[0] | base[1] | base[2] | base[3] | base[4]) & 0x80) { +- prohibited = is_prohibited_package_slow(parsed_name); +- } else { +- char* name = parsed_name->as_C_string(); +- prohibited = (strncmp(name, pkg, pkglen) == 0); +- } +- if (prohibited) { +- // It is illegal to define classes in the "java." package from +- // JVM_DefineClass or jni_DefineClass unless you're the bootclassloader +- char* name = parsed_name->as_C_string(); +- char* index = strrchr(name, '/'); +- assert(index != NULL, "must be"); +- *index = '\0'; // chop to just the package name +- while ((index = strchr(name, '/')) != NULL) { +- *index = '.'; // replace '/' with '.' in package name ++ if (k() != NULL) { ++ parsed_name = k->name(); ++ } else { ++ // Parse the stream. Note that we do this even though this klass might ++ // already be present in the SystemDictionary, otherwise we would not ++ // throw potential ClassFormatErrors. ++ // ++ // Note: "name" is updated. ++ ++ // Callers are expected to declare a ResourceMark to determine ++ // the lifetime of any updated (resource) allocated under ++ // this call to parseClassFile ++ ResourceMark rm(THREAD); ++ ClassFileParser parser(st); ++ k = parser.parseClassFile(class_name, ++ loader_data, ++ protection_domain, ++ parsed_name, ++ verify, ++ THREAD); ++ const char* pkg = "java/"; ++ size_t pkglen = strlen(pkg); ++ if (!HAS_PENDING_EXCEPTION && ++ !class_loader.is_null() && ++ parsed_name != NULL && ++ parsed_name->utf8_length() >= (int)pkglen) { ++ ResourceMark rm(THREAD); ++ bool prohibited; ++ const jbyte* base = parsed_name->base(); ++ if ((base[0] | base[1] | base[2] | base[3] | base[4]) & 0x80) { ++ prohibited = is_prohibited_package_slow(parsed_name); ++ } else { ++ char* name = parsed_name->as_C_string(); ++ prohibited = (strncmp(name, pkg, pkglen) == 0); + } +- const char* fmt = "Prohibited package name: %s"; +- size_t len = strlen(fmt) + strlen(name); +- char* message = NEW_RESOURCE_ARRAY(char, len); +- jio_snprintf(message, len, fmt, name); +- Exceptions::_throw_msg(THREAD_AND_LOCATION, +- vmSymbols::java_lang_SecurityException(), message); +- } +- } ++ if (prohibited) { ++ // It is illegal to define classes in the "java." package from ++ // JVM_DefineClass or jni_DefineClass unless you're the bootclassloader ++ char* name = parsed_name->as_C_string(); ++ char* index = strrchr(name, '/'); ++ assert(index != NULL, "must be"); ++ *index = '\0'; // chop to just the package name ++ while ((index = strchr(name, '/')) != NULL) { ++ *index = '.'; // replace '/' with '.' in package name ++ } ++ const char* fmt = "Prohibited package name: %s"; ++ size_t len = strlen(fmt) + strlen(name); ++ char* message = NEW_RESOURCE_ARRAY(char, len); ++ jio_snprintf(message, len, fmt, name); ++ Exceptions::_throw_msg(THREAD_AND_LOCATION, ++ vmSymbols::java_lang_SecurityException(), message); ++ } ++ } + +- if (!HAS_PENDING_EXCEPTION) { +- assert(parsed_name != NULL, "Sanity"); +- assert(class_name == NULL || class_name == parsed_name, "name mismatch"); +- // Verification prevents us from creating names with dots in them, this +- // asserts that that's the case. +- assert(is_internal_format(parsed_name), +- "external class name format used internally"); ++ if (!HAS_PENDING_EXCEPTION) { ++ assert(parsed_name != NULL, "Sanity"); ++ assert(class_name == NULL || class_name == parsed_name, "name mismatch"); ++ // Verification prevents us from creating names with dots in them, this ++ // asserts that that's the case. ++ assert(is_internal_format(parsed_name), ++ "external class name format used internally"); + + #if INCLUDE_JFR +- { +- InstanceKlass* ik = k(); +- ON_KLASS_CREATION(ik, parser, THREAD); +- k = instanceKlassHandle(ik); +- } ++ { ++ InstanceKlass* ik = k(); ++ ON_KLASS_CREATION(ik, parser, THREAD); ++ k = instanceKlassHandle(ik); ++ } + #endif ++ } ++ } + ++ if (!HAS_PENDING_EXCEPTION) { + // Add class just loaded + // If a class loader supports parallel classloading handle parallel define requests + // find_or_define_instance_class may return a different InstanceKlass +@@ -1274,14 +1296,19 @@ Klass* SystemDictionary::find_shared_class(Symbol* class_name) { + + instanceKlassHandle SystemDictionary::load_shared_class( + Symbol* class_name, Handle class_loader, TRAPS) { +- if (!(class_loader.is_null() || SystemDictionary::is_app_class_loader(class_loader) || ++ if (!(class_loader.is_null() || SystemDictionary::is_app_class_loader(class_loader) || + SystemDictionary::is_ext_class_loader(class_loader))) { + return instanceKlassHandle(); + } + +- instanceKlassHandle ik (THREAD, find_shared_class(class_name)); // InstanceKlass is find with null class loader. ++ Klass* klass = SystemDictionaryShared::find_dynamic_builtin_class(class_name); ++ if (klass == NULL) { ++ klass = find_shared_class(class_name); ++ } ++ ++ instanceKlassHandle ik (THREAD, klass); // InstanceKlass is find with null class loader. + if (ik.not_null()) { +- if (!UseAppCDS) { ++ if (!(UseAppCDS || DynamicArchive::is_mapped())) { + // CDS logic + if (SharedClassUtil::is_shared_boot_class(ik()) && class_loader.is_null()) { + // CDS record boot class load index. +@@ -1289,7 +1316,7 @@ instanceKlassHandle SystemDictionary::load_shared_class( + return load_shared_class(ik, class_loader, protection_domain, THREAD); + } + } else { +- // AppCDS logic. Only use null loader only to load classes that ++ // AppCDS and dynamic CDS logic. Only use null loader only to load classes that + // have been dumped by null loader. For non-null class loaders, + // either the class loader data is not initialized (but also not + // null) or the same class loader is used to load previously +@@ -1424,7 +1451,7 @@ instanceKlassHandle SystemDictionary::load_shared_class(instanceKlassHandle ik, + true /* shared class */); + + // register package for this class, if necessary +- if (UseAppCDS && class_loader.not_null()) { ++ if (SystemDictionary::is_app_class_loader(class_loader) || SystemDictionary::is_ext_class_loader(class_loader)) { + + ResourceMark rm(THREAD); + char* name = ik->name()->as_C_string(); +diff --git a/hotspot/src/share/vm/classfile/systemDictionary.hpp b/hotspot/src/share/vm/classfile/systemDictionary.hpp +index 3b9be4430..320f71865 100644 +--- a/hotspot/src/share/vm/classfile/systemDictionary.hpp ++++ b/hotspot/src/share/vm/classfile/systemDictionary.hpp +@@ -652,6 +652,7 @@ public: + TRAPS); + static bool is_ext_class_loader(Handle class_loader); + static bool is_app_class_loader(Handle class_loader); ++ static bool is_builtin_loader(Handle class_loader); + + protected: + static Klass* find_shared_class(Symbol* class_name); +diff --git a/hotspot/src/share/vm/classfile/systemDictionaryShared.cpp b/hotspot/src/share/vm/classfile/systemDictionaryShared.cpp +new file mode 100644 +index 000000000..99354cd4b +--- /dev/null ++++ b/hotspot/src/share/vm/classfile/systemDictionaryShared.cpp +@@ -0,0 +1,911 @@ ++/* ++ * Copyright (c) 2014, 2021, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2022, Huawei Technologies Co., Ltd. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ * ++ */ ++ ++#include "precompiled.hpp" ++#include "cds/archiveBuilder.hpp" ++#include "cds/dynamicArchive.hpp" ++#include "classfile/systemDictionaryShared.hpp" ++#include "classfile/classLoaderData.inline.hpp" ++#include "runtime/arguments.hpp" ++#include "runtime/mutexLocker.hpp" ++#include "memory/metaspaceShared.hpp" ++#include "memory/metaspaceClosure.hpp" ++#include "utilities/resourceHash.hpp" ++#include "runtime/mutexLocker.hpp" ++#include "utilities/ostream.hpp" ++ ++DEBUG_ONLY(bool SystemDictionaryShared::_no_class_loading_should_happen = false;) ++bool SystemDictionaryShared::_dump_in_progress = false; ++ ++class DumpTimeSharedClassInfo: public CHeapObj { ++ bool _excluded; ++ bool _has_checked_exclusion; ++public: ++ struct DTLoaderConstraint { ++ Symbol* _name; ++ char _loader_type1; ++ char _loader_type2; ++ DTLoaderConstraint(Symbol* name, char l1, char l2) : _name(name), _loader_type1(l1), _loader_type2(l2) { ++ _name->increment_refcount(); ++ } ++ DTLoaderConstraint() : _name(NULL), _loader_type1('0'), _loader_type2('0') {} ++ bool equals(const DTLoaderConstraint& t) { ++ return t._name == _name && ++ ((t._loader_type1 == _loader_type1 && t._loader_type2 == _loader_type2) || ++ (t._loader_type2 == _loader_type1 && t._loader_type1 == _loader_type2)); ++ } ++ }; ++ ++ struct DTVerifierConstraint { ++ Symbol* _name; ++ Symbol* _from_name; ++ DTVerifierConstraint() : _name(NULL), _from_name(NULL) {} ++ DTVerifierConstraint(Symbol* n, Symbol* fn) : _name(n), _from_name(fn) { ++ _name->increment_refcount(); ++ _from_name->increment_refcount(); ++ } ++ }; ++ ++ InstanceKlass* _klass; ++ InstanceKlass* _nest_host; ++ bool _failed_verification; ++ bool _is_archived_lambda_proxy; ++ int _id; ++ int _clsfile_size; ++ int _clsfile_crc32; ++ GrowableArray* _verifier_constraints; ++ GrowableArray* _verifier_constraint_flags; ++ GrowableArray* _loader_constraints; ++ ++ DumpTimeSharedClassInfo() { ++ _klass = NULL; ++ _nest_host = NULL; ++ _failed_verification = false; ++ _is_archived_lambda_proxy = false; ++ _has_checked_exclusion = false; ++ _id = -1; ++ _clsfile_size = -1; ++ _clsfile_crc32 = -1; ++ _excluded = false; ++ _verifier_constraints = NULL; ++ _verifier_constraint_flags = NULL; ++ _loader_constraints = NULL; ++ } ++ ++ void add_verification_constraint(InstanceKlass* k, Symbol* name, ++ Symbol* from_name, bool from_field_is_protected, bool from_is_array, bool from_is_object); ++ void record_linking_constraint(Symbol* name, Handle loader1, Handle loader2); ++ ++ bool is_builtin() { ++ return SystemDictionaryShared::is_builtin(_klass); ++ } ++ ++ int num_verifier_constraints() { ++ if (_verifier_constraint_flags != NULL) { ++ return _verifier_constraint_flags->length(); ++ } else { ++ return 0; ++ } ++ } ++ ++ int num_loader_constraints() { ++ if (_loader_constraints != NULL) { ++ return _loader_constraints->length(); ++ } else { ++ return 0; ++ } ++ } ++ ++ void metaspace_pointers_do(MetaspaceClosure* it) { ++ it->push(&_klass); ++ it->push(&_nest_host); ++ if (_verifier_constraints != NULL) { ++ for (int i = 0; i < _verifier_constraints->length(); i++) { ++ DTVerifierConstraint* cons = _verifier_constraints->adr_at(i); ++ it->push(&cons->_name); ++ it->push(&cons->_from_name); ++ } ++ } ++ if (_loader_constraints != NULL) { ++ for (int i = 0; i < _loader_constraints->length(); i++) { ++ DTLoaderConstraint* lc = _loader_constraints->adr_at(i); ++ it->push(&lc->_name); ++ } ++ } ++ } ++ ++ bool is_excluded() { ++ // _klass may become NULL due to DynamicArchiveBuilder::set_to_null ++ return _excluded || _failed_verification || _klass == NULL; ++ } ++ ++ // simple accessors ++ void set_excluded() { _excluded = true; } ++ bool has_checked_exclusion() const { return _has_checked_exclusion; } ++ void set_has_checked_exclusion() { _has_checked_exclusion = true; } ++ bool failed_verification() const { return _failed_verification; } ++ void set_failed_verification() { _failed_verification = true; } ++ InstanceKlass* nest_host() const { return _nest_host; } ++ void set_nest_host(InstanceKlass* nest_host) { _nest_host = nest_host; } ++}; ++ ++inline unsigned DumpTimeSharedClassTable_hash(InstanceKlass* const& k) { ++ // Deterministic archive is not possible because classes can be loaded ++ // in multiple threads. ++ return primitive_hash(k); ++} ++ ++class DumpTimeSharedClassTable: public ResourceHashtable< ++ InstanceKlass*, ++ DumpTimeSharedClassInfo, ++ &DumpTimeSharedClassTable_hash, ++ primitive_equals, ++ 15889, // prime number ++ ResourceObj::C_HEAP> ++{ ++ int _builtin_count; ++ int _unregistered_count; ++public: ++ DumpTimeSharedClassInfo* find_or_allocate_info_for(InstanceKlass* k, bool dump_in_progress) { ++ bool created = false; ++ DumpTimeSharedClassInfo* p; ++ if (!dump_in_progress) { ++ p = put_if_absent(k, &created); ++ } else { ++ p = get(k); ++ } ++ if (created) { ++ assert(!SystemDictionaryShared::no_class_loading_should_happen(), ++ "no new classes can be loaded while dumping archive"); ++ p->_klass = k; ++ } else { ++ if (!dump_in_progress) { ++ assert(p->_klass == k, "Sanity"); ++ } ++ } ++ return p; ++ } ++ ++ class CountClassByCategory : StackObj { ++ DumpTimeSharedClassTable* _table; ++ public: ++ CountClassByCategory(DumpTimeSharedClassTable* table) : _table(table) {} ++ bool do_entry(InstanceKlass* k, DumpTimeSharedClassInfo& info) { ++ if (!info.is_excluded()) { ++ if (info.is_builtin()) { ++ ++ _table->_builtin_count; ++ } else { ++ ++ _table->_unregistered_count; ++ } ++ } ++ return true; // keep on iterating ++ } ++ }; ++ ++ void update_counts() { ++ _builtin_count = 0; ++ _unregistered_count = 0; ++ CountClassByCategory counter(this); ++ iterate(&counter); ++ } ++ ++ int count_of(bool is_builtin) const { ++ if (is_builtin) { ++ return _builtin_count; ++ } else { ++ return _unregistered_count; ++ } ++ } ++}; ++ ++class RunTimeSharedClassInfo { ++public: ++ struct CrcInfo { ++ int _clsfile_size; ++ int _clsfile_crc32; ++ }; ++ ++ // This is different than DumpTimeSharedClassInfo::DTVerifierConstraint. We use ++ // u4 instead of Symbol* to save space on 64-bit CPU. ++ struct RTVerifierConstraint { ++ u4 _name; ++ u4 _from_name; ++ Symbol* name() { return (Symbol*)(SharedBaseAddress + _name);} ++ Symbol* from_name() { return (Symbol*)(SharedBaseAddress + _from_name); } ++ }; ++ ++ struct RTLoaderConstraint { ++ u4 _name; ++ char _loader_type1; ++ char _loader_type2; ++ Symbol* constraint_name() { ++ return (Symbol*)(SharedBaseAddress + _name); ++ } ++ }; ++ ++ InstanceKlass* _klass; ++ int _num_verifier_constraints; ++ int _num_loader_constraints; ++ ++ // optional CrcInfo _crc; (only for UNREGISTERED classes) ++ // optional InstanceKlass* _nest_host ++ // optional RTLoaderConstraint _loader_constraint_types[_num_loader_constraints] ++ // optional RTVerifierConstraint _verifier_constraints[_num_verifier_constraints] ++ // optional char _verifier_constraint_flags[_num_verifier_constraints] ++ ++private: ++ static size_t header_size_size() { ++ return sizeof(RunTimeSharedClassInfo); ++ } ++ static size_t crc_size(InstanceKlass* klass) { ++ if (!SystemDictionaryShared::is_builtin(klass)) { ++ return sizeof(CrcInfo); ++ } else { ++ return 0; ++ } ++ } ++ static size_t verifier_constraints_size(int num_verifier_constraints) { ++ return sizeof(RTVerifierConstraint) * num_verifier_constraints; ++ } ++ static size_t verifier_constraint_flags_size(int num_verifier_constraints) { ++ return sizeof(char) * num_verifier_constraints; ++ } ++ static size_t loader_constraints_size(int num_loader_constraints) { ++ return sizeof(RTLoaderConstraint) * num_loader_constraints; ++ } ++ static size_t nest_host_size(InstanceKlass* klass) { ++ assert(!klass->is_anonymous(), "klass should not be hidden right now."); ++ if (klass->is_anonymous()) { ++ return sizeof(InstanceKlass*); ++ } else { ++ return 0; ++ } ++ } ++ ++public: ++ static size_t byte_size(InstanceKlass* klass, int num_verifier_constraints, int num_loader_constraints) { ++ return header_size_size() + ++ crc_size(klass) + ++ nest_host_size(klass) + ++ loader_constraints_size(num_loader_constraints) + ++ verifier_constraints_size(num_verifier_constraints) + ++ verifier_constraint_flags_size(num_verifier_constraints); ++ } ++ ++private: ++ size_t crc_offset() const { ++ return header_size_size(); ++ } ++ ++ size_t nest_host_offset() const { ++ return crc_offset() + crc_size(_klass); ++ } ++ ++ size_t loader_constraints_offset() const { ++ return nest_host_offset() + nest_host_size(_klass); ++ } ++ size_t verifier_constraints_offset() const { ++ return loader_constraints_offset() + loader_constraints_size(_num_loader_constraints); ++ } ++ size_t verifier_constraint_flags_offset() const { ++ return verifier_constraints_offset() + verifier_constraints_size(_num_verifier_constraints); ++ } ++ ++ void check_verifier_constraint_offset(int i) const { ++ assert(0 <= i && i < _num_verifier_constraints, "sanity"); ++ } ++ ++ void check_loader_constraint_offset(int i) const { ++ assert(0 <= i && i < _num_loader_constraints, "sanity"); ++ } ++ ++public: ++ CrcInfo* crc() const { ++ assert(crc_size(_klass) > 0, "must be"); ++ return (CrcInfo*)(address(this) + crc_offset()); ++ } ++ RTVerifierConstraint* verifier_constraints() { ++ assert(_num_verifier_constraints > 0, "sanity"); ++ return (RTVerifierConstraint*)(address(this) + verifier_constraints_offset()); ++ } ++ RTVerifierConstraint* verifier_constraint_at(int i) { ++ check_verifier_constraint_offset(i); ++ return verifier_constraints() + i; ++ } ++ ++ char* verifier_constraint_flags() { ++ assert(_num_verifier_constraints > 0, "sanity"); ++ return (char*)(address(this) + verifier_constraint_flags_offset()); ++ } ++ ++ RTLoaderConstraint* loader_constraints() { ++ assert(_num_loader_constraints > 0, "sanity"); ++ return (RTLoaderConstraint*)(address(this) + loader_constraints_offset()); ++ } ++ ++ RTLoaderConstraint* loader_constraint_at(int i) { ++ check_loader_constraint_offset(i); ++ return loader_constraints() + i; ++ } ++ ++ void init(DumpTimeSharedClassInfo& info) { ++ ArchiveBuilder* builder = ArchiveBuilder::current(); ++ assert(builder->is_in_buffer_space(info._klass), "must be"); ++ _klass = info._klass; ++ if (!SystemDictionaryShared::is_builtin(_klass)) { ++ CrcInfo* c = crc(); ++ c->_clsfile_size = info._clsfile_size; ++ c->_clsfile_crc32 = info._clsfile_crc32; ++ } ++ _num_verifier_constraints = info.num_verifier_constraints(); ++ _num_loader_constraints = info.num_loader_constraints(); ++ int i; ++ if (_num_verifier_constraints > 0) { ++ RTVerifierConstraint* vf_constraints = verifier_constraints(); ++ char* flags = verifier_constraint_flags(); ++ for (i = 0; i < _num_verifier_constraints; i++) { ++ vf_constraints[i]._name = builder->any_to_offset_u4(info._verifier_constraints->at(i)._name); ++ vf_constraints[i]._from_name = builder->any_to_offset_u4(info._verifier_constraints->at(i)._from_name); ++ } ++ for (i = 0; i < _num_verifier_constraints; i++) { ++ flags[i] = info._verifier_constraint_flags->at(i); ++ } ++ } ++ ++ if (_num_loader_constraints > 0) { ++ RTLoaderConstraint* ld_constraints = loader_constraints(); ++ for (i = 0; i < _num_loader_constraints; i++) { ++ ld_constraints[i]._name = builder->any_to_offset_u4(info._loader_constraints->at(i)._name); ++ ld_constraints[i]._loader_type1 = info._loader_constraints->at(i)._loader_type1; ++ ld_constraints[i]._loader_type2 = info._loader_constraints->at(i)._loader_type2; ++ } ++ } ++ ++ ArchivePtrMarker::mark_pointer(&_klass); ++ } ++ ++ bool matches(int clsfile_size, int clsfile_crc32) const { ++ return crc()->_clsfile_size == clsfile_size && ++ crc()->_clsfile_crc32 == clsfile_crc32; ++ } ++ ++ char verifier_constraint_flag(int i) { ++ check_verifier_constraint_offset(i); ++ return verifier_constraint_flags()[i]; ++ } ++ ++private: ++ // ArchiveBuilder::make_shallow_copy() has reserved a pointer immediately ++ // before archived InstanceKlasses. We can use this slot to do a quick ++ // lookup of InstanceKlass* -> RunTimeSharedClassInfo* without ++ // building a new hashtable. ++ // ++ // info_pointer_addr(klass) --> 0x0100 RunTimeSharedClassInfo* ++ // InstanceKlass* klass --> 0x0108 ++ // 0x0110 fields from Klass ... ++ static RunTimeSharedClassInfo** info_pointer_addr(InstanceKlass* klass) { ++ return &((RunTimeSharedClassInfo**)klass)[-1]; ++ } ++ ++public: ++ static RunTimeSharedClassInfo* get_for(InstanceKlass* klass) { ++ assert(klass->is_shared(), "don't call for non-shared class"); ++ return *info_pointer_addr(klass); ++ } ++ static void set_for(InstanceKlass* klass, RunTimeSharedClassInfo* record) { ++ assert(ArchiveBuilder::current()->is_in_buffer_space(klass), "must be"); ++ assert(ArchiveBuilder::current()->is_in_buffer_space(record), "must be"); ++ *info_pointer_addr(klass) = record; ++ ArchivePtrMarker::mark_pointer(info_pointer_addr(klass)); ++ } ++ ++ // Used by RunTimeSharedDictionary to implement OffsetCompactHashtable::EQUALS ++ static inline bool EQUALS( ++ const RunTimeSharedClassInfo* value, Symbol* key, int len_unused) { ++ return (value->_klass->name() == key); ++ } ++}; ++ ++class RunTimeSharedDictionary : public OffsetCompactHashtable< ++ Symbol*, ++ const RunTimeSharedClassInfo*, ++ RunTimeSharedClassInfo::EQUALS> {}; ++ ++static DumpTimeSharedClassTable* _dumptime_table = NULL; ++// SystemDictionaries in the top layer dynamic archive ++static RunTimeSharedDictionary _dynamic_builtin_dictionary; ++static RunTimeSharedDictionary _dynamic_unregistered_dictionary; ++ ++void SystemDictionaryShared::set_class_has_failed_verification(InstanceKlass* ik) { ++ Arguments::assert_is_dumping_archive(); ++ DumpTimeSharedClassInfo* p = find_or_allocate_info_for(ik); ++ if (p != NULL) { ++ p->set_failed_verification(); ++ } ++} ++ ++void SystemDictionaryShared::start_dumping() { ++ MutexLockerEx ml(DumpTimeTable_lock, Mutex::_no_safepoint_check_flag); ++ _dump_in_progress = true; ++} ++ ++void SystemDictionaryShared::init_dumptime_info(InstanceKlass* k) { ++ (void)find_or_allocate_info_for(k); ++} ++ ++void SystemDictionaryShared::remove_dumptime_info(InstanceKlass* k) { ++ MutexLockerEx ml(DumpTimeTable_lock, Mutex::_no_safepoint_check_flag); ++ DumpTimeSharedClassInfo* p = _dumptime_table->get(k); ++ if (p == NULL) { ++ return; ++ } ++ _dumptime_table->remove(k); ++} ++ ++DumpTimeSharedClassInfo* SystemDictionaryShared::find_or_allocate_info_for(InstanceKlass* k) { ++ MutexLockerEx ml(DumpTimeTable_lock, Mutex::_no_safepoint_check_flag); ++ return find_or_allocate_info_for_locked(k); ++} ++ ++DumpTimeSharedClassInfo* SystemDictionaryShared::find_or_allocate_info_for_locked(InstanceKlass* k) { ++ assert_lock_strong(DumpTimeTable_lock); ++ if (_dumptime_table == NULL) { ++ _dumptime_table = new (ResourceObj::C_HEAP, mtClass)DumpTimeSharedClassTable(); ++ } ++ return _dumptime_table->find_or_allocate_info_for(k, _dump_in_progress); ++} ++ ++bool SystemDictionaryShared::empty_dumptime_table() { ++ if (_dumptime_table == NULL) { ++ return true; ++ } ++ _dumptime_table->update_counts(); ++ if (_dumptime_table->count_of(true) == 0 && _dumptime_table->count_of(false) == 0) { ++ return true; ++ } ++ return false; ++} ++ ++class ExcludeDumpTimeSharedClasses : StackObj { ++public: ++ bool do_entry(InstanceKlass* k, DumpTimeSharedClassInfo& info) { ++ SystemDictionaryShared::check_for_exclusion(k, &info); ++ return true; // keep on iterating ++ } ++}; ++ ++class IterateDumpTimeSharedClassTable : StackObj { ++ MetaspaceClosure *_it; ++public: ++ IterateDumpTimeSharedClassTable(MetaspaceClosure* it) : _it(it) {} ++ ++ bool do_entry(InstanceKlass* k, DumpTimeSharedClassInfo& info) { ++ assert_lock_strong(DumpTimeTable_lock); ++ if (!info.is_excluded()) { ++ info.metaspace_pointers_do(_it); ++ } ++ return true; // keep on iterating ++ } ++}; ++ ++class IterateDumpTimeTableReplaceKlass : StackObj { ++public: ++ IterateDumpTimeTableReplaceKlass() { } ++ ++ bool do_entry(InstanceKlass* k, DumpTimeSharedClassInfo& info) { ++ if (k->oop_is_instance() && !info.is_excluded()) { ++ k->constants()->symbol_replace_excluded_klass(); ++ } ++ return true; ++ } ++}; ++ ++void SystemDictionaryShared::check_excluded_classes() { ++ assert(no_class_loading_should_happen(), "sanity"); ++ assert_lock_strong(DumpTimeTable_lock); ++ ExcludeDumpTimeSharedClasses excl; ++ _dumptime_table->iterate(&excl); ++ _dumptime_table->update_counts(); ++} ++ ++bool SystemDictionaryShared::check_for_exclusion(InstanceKlass* k, DumpTimeSharedClassInfo* info) { ++ if (MetaspaceShared::is_in_shared_space(k)) { ++ // We have reached a super type that's already in the base archive. Treat it ++ // as "not excluded". ++ assert(DynamicDumpSharedSpaces, "must be"); ++ return false; ++ } ++ ++ if (info == NULL) { ++ info = _dumptime_table->get(k); ++ assert(info != NULL, "supertypes of any classes in _dumptime_table must either be shared, or must also be in _dumptime_table"); ++ } ++ ++ if (!info->has_checked_exclusion()) { ++ if (check_for_exclusion_impl(k)) { ++ info->set_excluded(); ++ } ++ info->set_has_checked_exclusion(); ++ } ++ ++ return info->is_excluded(); ++} ++ ++// Check if a class or any of its supertypes has been redefined. ++bool SystemDictionaryShared::has_been_redefined(InstanceKlass* k) { ++ if (k->has_been_redefined()) { ++ return true; ++ } ++ if (k->java_super() != NULL && has_been_redefined(k->java_super())) { ++ return true; ++ } ++ Array* interfaces = k->local_interfaces(); ++ int len = interfaces->length(); ++ for (int i = 0; i < len; i++) { ++ if (has_been_redefined((InstanceKlass*)interfaces->at(i))) { ++ return true; ++ } ++ } ++ return false; ++} ++ ++bool SystemDictionaryShared::check_for_exclusion_impl(InstanceKlass* k) { ++ if (k->is_in_error_state()) { ++ return warn_excluded(k, "In error state"); ++ } ++ if (k->init_state() < InstanceKlass::loaded) { ++ return warn_excluded(k, "not loaded klass"); ++ } ++ if (has_been_redefined(k)) { ++ return warn_excluded(k, "Has been redefined"); ++ } ++ if (k->signers() != NULL) { ++ // We cannot include signed classes in the archive because the certificates ++ // used during dump time may be different than those used during ++ // runtime (due to expiration, etc). ++ return warn_excluded(k, "Signed JAR"); ++ } ++ if (is_jfr_event_class(k)) { ++ // We cannot include JFR event classes because they need runtime-specific ++ // instrumentation in order to work with -XX:FlightRecorderOptions:retransform=false. ++ // There are only a small number of these classes, so it's not worthwhile to ++ // support them and make CDS more complicated. ++ return warn_excluded(k, "JFR event class"); ++ } ++ if (k->init_state() < InstanceKlass::linked) { ++ // In CDS dumping, we will attempt to link all classes. Those that fail to link will ++ // be recorded in DumpTimeSharedClassInfo. ++ Arguments::assert_is_dumping_archive(); ++ ++ // TODO -- rethink how this can be handled. ++ // We should try to link ik, however, we can't do it here because ++ // 1. We are at VM exit ++ // 2. linking a class may cause other classes to be loaded, which means ++ // a custom ClassLoader.loadClass() may be called, at a point where the ++ // class loader doesn't expect it. ++ if (has_class_failed_verification(k)) { ++ return warn_excluded(k, "Failed verification"); ++ } else { ++ if (k->can_be_verified_at_dumptime()) { ++ return warn_excluded(k, "Not linked"); ++ } ++ } ++ } ++ if (DynamicDumpSharedSpaces && k->major_version() < 50 /*JAVA_6_VERSION*/) { ++ // In order to support old classes during dynamic dump, class rewriting needs to ++ // be reverted. This would result in more complex code and testing but not much gain. ++ ResourceMark rm; ++ dynamic_cds_log->print_cr("Pre JDK 6 class not supported by CDS: %u.%u %s", ++ k->major_version(), k->minor_version(), k->name()->as_C_string()); ++ return true; ++ } ++ ++ if (!k->can_be_verified_at_dumptime() && k->is_linked()) { ++ return warn_excluded(k, "Old class has been linked"); ++ } ++ ++ if (k->is_anonymous() /* && !is_registered_lambda_proxy_class(k) */) { ++ return warn_excluded(k, "Hidden class"); ++ } ++ ++ InstanceKlass* super = k->java_super(); ++ if (super != NULL && check_for_exclusion(super, NULL)) { ++ ResourceMark rm; ++ dynamic_cds_log->print_cr("Skipping %s: super class %s is excluded", k->name()->as_C_string(), super->name()->as_C_string()); ++ return true; ++ } ++ ++ Array* interfaces = k->local_interfaces(); ++ int len = interfaces->length(); ++ for (int i = 0; i < len; i++) { ++ InstanceKlass* intf = (InstanceKlass*)interfaces->at(i); ++ if (check_for_exclusion(intf, NULL)) { ++ dynamic_cds_log->print_cr("Skipping %s: interface %s is excluded", k->name()->as_C_string(), intf->name()->as_C_string()); ++ return true; ++ } ++ } ++ ++ return false; // false == k should NOT be excluded ++} ++ ++// Returns true so the caller can do: return warn_excluded("....."); ++bool SystemDictionaryShared::warn_excluded(InstanceKlass* k, const char* reason) { ++ ResourceMark rm; ++ dynamic_cds_log->print_cr("Skipping %s: %s", k->name()->as_C_string(), reason); ++ return true; ++} ++ ++bool SystemDictionaryShared::is_jfr_event_class(InstanceKlass *k) { ++ while (k) { ++ if (k->name()->equals("jdk/jfr/Event")) { ++ return true; ++ } ++ k = k->java_super(); ++ } ++ return false; ++} ++ ++bool SystemDictionaryShared::has_class_failed_verification(InstanceKlass* ik) { ++ if (_dumptime_table == NULL) { ++ assert(DynamicDumpSharedSpaces, "sanity"); ++ assert(ik->is_shared(), "must be a shared class in the static archive"); ++ return false; ++ } ++ DumpTimeSharedClassInfo* p = _dumptime_table->get(ik); ++ return (p == NULL) ? false : p->failed_verification(); ++} ++ ++void SystemDictionaryShared::dumptime_classes_do(class MetaspaceClosure* it) { ++ assert_lock_strong(DumpTimeTable_lock); ++ IterateDumpTimeSharedClassTable iter(it); ++ _dumptime_table->iterate(&iter); ++} ++ ++void SystemDictionaryShared::replace_klass_in_constantPool() { ++ IterateDumpTimeTableReplaceKlass iter; ++ _dumptime_table->iterate(&iter); ++} ++ ++bool SystemDictionaryShared::is_excluded_class(InstanceKlass* k) { ++ assert(_no_class_loading_should_happen, "sanity"); ++ assert_lock_strong(DumpTimeTable_lock); ++ Arguments::assert_is_dumping_archive(); ++ DumpTimeSharedClassInfo* p = find_or_allocate_info_for_locked(k); ++ return (p == NULL) ? true : p->is_excluded(); ++} ++ ++class EstimateSizeForArchive : StackObj { ++ size_t _shared_class_info_size; ++ int _num_builtin_klasses; ++ int _num_unregistered_klasses; ++ ++public: ++ EstimateSizeForArchive() { ++ _shared_class_info_size = 0; ++ _num_builtin_klasses = 0; ++ _num_unregistered_klasses = 0; ++ } ++ ++ bool do_entry(InstanceKlass* k, DumpTimeSharedClassInfo& info) { ++ if (!info.is_excluded()) { ++ size_t byte_size = RunTimeSharedClassInfo::byte_size(info._klass, info.num_verifier_constraints(), info.num_loader_constraints()); ++ _shared_class_info_size += align_up(byte_size, KlassAlignmentInBytes); ++ } ++ return true; // keep on iterating ++ } ++ ++ size_t total() { ++ return _shared_class_info_size; ++ } ++}; ++ ++size_t SystemDictionaryShared::estimate_size_for_archive() { ++ EstimateSizeForArchive est; ++ _dumptime_table->iterate(&est); ++ size_t total_size = est.total() + ++ CompactHashtableWriter::estimate_size(_dumptime_table->count_of(true)) + ++ CompactHashtableWriter::estimate_size(_dumptime_table->count_of(false)); ++ total_size += CompactHashtableWriter::estimate_size(0); ++ return total_size; ++} ++ ++unsigned int SystemDictionaryShared::hash_for_shared_dictionary(address ptr) { ++ if (ArchiveBuilder::is_active()) { ++ uintx offset = ArchiveBuilder::current()->any_to_offset(ptr); ++ unsigned int hash = primitive_hash(offset); ++ DEBUG_ONLY({ ++ if (((const MetaspaceObj*)ptr)->is_shared()) { ++ assert(hash == SystemDictionaryShared::hash_for_shared_dictionary_quick(ptr), "must be"); ++ } ++ }); ++ return hash; ++ } else { ++ return SystemDictionaryShared::hash_for_shared_dictionary_quick(ptr); ++ } ++} ++ ++class CopySharedClassInfoToArchive : StackObj { ++ CompactHashtableWriter* _writer; ++ bool _is_builtin; ++ ArchiveBuilder *_builder; ++public: ++ CopySharedClassInfoToArchive(CompactHashtableWriter* writer, ++ bool is_builtin) ++ : _writer(writer), _is_builtin(is_builtin), _builder(ArchiveBuilder::current()) {} ++ ++ bool do_entry(InstanceKlass* k, DumpTimeSharedClassInfo& info) { ++ if (!info.is_excluded() && info.is_builtin() == _is_builtin) { ++ size_t byte_size = RunTimeSharedClassInfo::byte_size(info._klass, info.num_verifier_constraints(), info.num_loader_constraints()); ++ RunTimeSharedClassInfo* record; ++ record = (RunTimeSharedClassInfo*)ArchiveBuilder::ro_region_alloc(byte_size); ++ record->init(info); ++ ++ unsigned int hash; ++ Symbol* name = info._klass->name(); ++ hash = SystemDictionaryShared::hash_for_shared_dictionary((address)name); ++ u4 delta = _builder->buffer_to_offset_u4((address)record); ++ if (_is_builtin && info._klass->is_anonymous()) { ++ // skip ++ } else { ++ _writer->add(hash, delta); ++ } ++ if (TraceDynamicCDS) { ++ ResourceMark rm; ++ dynamic_cds_log->print_cr("%s dictionary: %s", (_is_builtin ? "builtin" : "unregistered"), info._klass->external_name()); ++ } ++ ++ // Save this for quick runtime lookup of InstanceKlass* -> RunTimeSharedClassInfo* ++ RunTimeSharedClassInfo::set_for(info._klass, record); ++ } ++ return true; // keep on iterating ++ } ++}; ++ ++void SystemDictionaryShared::write_dictionary(RunTimeSharedDictionary* dictionary, ++ bool is_builtin) { ++ CompactHashtableStats stats; ++ dictionary->reset(); ++ CompactHashtableWriter writer(_dumptime_table->count_of(is_builtin), &stats); ++ CopySharedClassInfoToArchive copy(&writer, is_builtin); ++ assert_lock_strong(DumpTimeTable_lock); ++ _dumptime_table->iterate(©); ++ writer.dump(dictionary, is_builtin ? "builtin dictionary" : "unregistered dictionary"); ++} ++ ++void SystemDictionaryShared::write_to_archive() { ++ write_dictionary(&_dynamic_builtin_dictionary, true); ++ write_dictionary(&_dynamic_unregistered_dictionary, false); ++} ++ ++void SystemDictionaryShared::serialize_dictionary_headers(SerializeClosure* soc) { ++ _dynamic_builtin_dictionary.serialize_header(soc); ++ _dynamic_unregistered_dictionary.serialize_header(soc); ++} ++ ++void SystemDictionaryShared::set_shared_class_misc_info(InstanceKlass* k, ClassFileStream* cfs) { ++ Arguments::assert_is_dumping_archive(); ++ assert(!is_builtin(k), "must be unregistered class"); ++ DumpTimeSharedClassInfo* info = find_or_allocate_info_for(k); ++ if (info != NULL) { ++ info->_clsfile_size = cfs->length(); ++ info->_clsfile_crc32 = ClassLoader::crc32(0, (const char*)cfs->buffer(), cfs->length()); ++ } ++} ++ ++// This function is called for loading only UNREGISTERED classes ++InstanceKlass* SystemDictionaryShared::lookup_from_stream(Symbol* class_name, ++ Handle class_loader, ++ Handle protection_domain, ++ const ClassFileStream* cfs, ++ TRAPS) { ++ if (!UseSharedSpaces) { ++ return NULL; ++ } ++ if (class_name == NULL) { // don't do this for hidden classes ++ return NULL; ++ } ++ if (SystemDictionary::is_builtin_loader(class_loader)) { ++ // Do nothing for the BUILTIN loaders. ++ return NULL; ++ } ++ ++ const RunTimeSharedClassInfo* record = find_record(&_dynamic_unregistered_dictionary, class_name); ++ if (record == NULL) { ++ return NULL; ++ } ++ ++ int clsfile_size = cfs->length(); ++ int clsfile_crc32 = ClassLoader::crc32(0, (const char*)cfs->buffer(), cfs->length()); ++ ++ if (!record->matches(clsfile_size, clsfile_crc32)) { ++ return NULL; ++ } ++ ++ return acquire_class_for_current_thread(record->_klass, class_loader, ++ protection_domain, cfs, ++ THREAD); ++} ++ ++const RunTimeSharedClassInfo* ++SystemDictionaryShared::find_record(RunTimeSharedDictionary* dynamic_dict, Symbol* name) { ++ if (!UseSharedSpaces || !name->is_shared()) { ++ // The names of all shared classes must also be a shared Symbol. ++ return NULL; ++ } ++ ++ unsigned int hash = SystemDictionaryShared::hash_for_shared_dictionary_quick(name); ++ const RunTimeSharedClassInfo* record = NULL; ++ // AppCDS only support builtin classloader, customer class loader is just in dynamic archive. ++ if (DynamicArchive::is_mapped()) { ++ record = dynamic_dict->lookup(name, hash, 0); ++ } ++ ++ return record; ++} ++ ++InstanceKlass* SystemDictionaryShared::acquire_class_for_current_thread( ++ InstanceKlass *ik, ++ Handle class_loader, ++ Handle protection_domain, ++ const ClassFileStream *cfs, ++ TRAPS) { ++ ClassLoaderData* loader_data = ClassLoaderData::class_loader_data(class_loader()); ++ ++ { ++ MutexLocker mu(SharedDictionary_lock, THREAD); ++ if (ik->class_loader_data() != NULL) { ++ // ik is already loaded (by this loader or by a different loader) ++ // or ik is being loaded by a different thread (by this loader or by a different loader) ++ return NULL; ++ } ++ ++ // No other thread has acquired this yet, so give it to *this thread* ++ ik->set_class_loader_data(loader_data); ++ } ++ ++ // No longer holding SharedDictionary_lock ++ // No need to lock, as can be held only by a single thread. ++ loader_data->add_class(ik); ++ ++ // Load and check super/interfaces, restore unsharable info ++ instanceKlassHandle shared_klass = SystemDictionary::load_shared_class(ik, class_loader, protection_domain, THREAD); ++ if (shared_klass() == NULL || HAS_PENDING_EXCEPTION) { ++ // TODO: clean up so it can be used again ++ return NULL; ++ } ++ ++ return shared_klass(); ++} ++ ++InstanceKlass* SystemDictionaryShared::find_dynamic_builtin_class(Symbol* name) { ++ const RunTimeSharedClassInfo* record = find_record(&_dynamic_builtin_dictionary, name); ++ if (record != NULL) { ++ assert(!record->_klass->is_anonymous(), "hidden class cannot be looked up by name"); ++ assert(check_klass_alignment(record->_klass), "Address not aligned"); ++ return record->_klass; ++ } else { ++ return NULL; ++ } ++} +diff --git a/hotspot/src/share/vm/classfile/systemDictionaryShared.hpp b/hotspot/src/share/vm/classfile/systemDictionaryShared.hpp +index 1bd61b02..36423bee 100644 +--- a/hotspot/src/share/vm/classfile/systemDictionaryShared.hpp ++++ b/hotspot/src/share/vm/classfile/systemDictionaryShared.hpp +@@ -22,7 +22,6 @@ + * + */ + +- + #ifndef SHARE_VM_CLASSFILE_SYSTEMDICTIONARYSHARED_HPP + #define SHARE_VM_CLASSFILE_SYSTEMDICTIONARYSHARED_HPP + +@@ -30,13 +29,91 @@ + #include "classfile/systemDictionary.hpp" + #include "verifier.hpp" + ++/*=============================================================================== ++ ++ Handling of the classes in the AppCDS archive ++ ++ To ensure safety and to simplify the implementation, archived classes are ++ "segregated" into 2 types. The following rules describe how they ++ are stored and looked up. ++ ++[1] Category of archived classes ++ ++ There are 2 disjoint groups of classes stored in the AppCDS archive: ++ ++ BUILTIN: These classes may be defined ONLY by the BOOT/PLATFORM/APP ++ loaders. ++ ++ UNREGISTERED: These classes may be defined ONLY by a ClassLoader ++ instance that's not listed above (using fingerprint matching) ++ ++[2] How classes from different categories are specified in the classlist: ++ ++ Starting from JDK9, each class in the classlist may be specified with ++ these keywords: "id", "super", "interfaces", "loader" and "source". ++ ++ ++ BUILTIN Only the "id" keyword may be (optionally) specified. All other ++ keywords are forbidden. ++ ++ The named class is looked up from the jimage and from ++ Xbootclasspath/a and CLASSPATH. ++ ++ UNREGISTERED: The "id", "super", and "source" keywords must all be ++ specified. ++ ++ The "interfaces" keyword must be specified if the class implements ++ one or more local interfaces. The "interfaces" keyword must not be ++ specified if the class does not implement local interfaces. ++ ++ The named class is looked up from the location specified in the ++ "source" keyword. ++ ++ Example classlist: ++ ++ # BUILTIN ++ java/lang/Object id: 0 ++ java/lang/Cloneable id: 1 ++ java/lang/String ++ ++ # UNREGISTERED ++ Bar id: 3 super: 0 interfaces: 1 source: /foo.jar ++ ++ ++[3] Identifying the category of archived classes ++ ++ BUILTIN: (C->shared_classpath_index() >= 0) ++ UNREGISTERED: (C->shared_classpath_index() == UNREGISTERED_INDEX (-9999)) ++ ++[4] Lookup of archived classes at run time: ++ ++ (a) BUILTIN loaders: ++ ++ search _builtin_dictionary ++ ++ (b) UNREGISTERED loaders: ++ ++ search _unregistered_dictionary for an entry that matches the ++ (name, clsfile_len, clsfile_crc32). ++ ++===============================================================================*/ ++#define UNREGISTERED_INDEX -9999 ++ ++class DumpTimeSharedClassInfo; ++class RunTimeSharedClassInfo; ++class RunTimeSharedDictionary; ++ + class SystemDictionaryShared: public SystemDictionary { ++private: ++ static bool _dump_in_progress; ++ DEBUG_ONLY(static bool _no_class_loading_should_happen;) ++ + public: + static void initialize(TRAPS) {} + static instanceKlassHandle find_or_load_shared_class(Symbol* class_name, + Handle class_loader, + TRAPS) { +- if (UseAppCDS) { ++ if (UseSharedSpaces) { + instanceKlassHandle ik = load_shared_class(class_name, class_loader, CHECK_NULL); + if (!ik.is_null()) { + instanceKlassHandle nh = instanceKlassHandle(); // null Handle +@@ -48,7 +125,7 @@ public: + } + static void roots_oops_do(OopClosure* blk) {} + static void oops_do(OopClosure* f) {} +- ++ + static bool is_sharing_possible(ClassLoaderData* loader_data) { + oop class_loader = loader_data->class_loader(); + return (class_loader == NULL || +@@ -60,8 +137,43 @@ public: + static size_t dictionary_entry_size() { + return sizeof(DictionaryEntry); + } ++ + static void init_shared_dictionary_entry(Klass* k, DictionaryEntry* entry) {} + ++ static void init_dumptime_info(InstanceKlass* k) NOT_CDS_RETURN; ++ static void remove_dumptime_info(InstanceKlass* k) NOT_CDS_RETURN; ++ ++ static void start_dumping(); ++ ++ static DumpTimeSharedClassInfo* find_or_allocate_info_for(InstanceKlass* k); ++ ++ static DumpTimeSharedClassInfo* find_or_allocate_info_for_locked(InstanceKlass* k); ++ ++ static bool empty_dumptime_table(); ++ ++ static void check_excluded_classes(); ++ ++ static bool check_for_exclusion(InstanceKlass* k, DumpTimeSharedClassInfo* info); ++ ++ static bool has_been_redefined(InstanceKlass* k); ++ ++ static bool check_for_exclusion_impl(InstanceKlass* k); ++ ++ static bool warn_excluded(InstanceKlass* k, const char* reason); ++ ++ static bool is_jfr_event_class(InstanceKlass *k); ++ ++ static bool has_class_failed_verification(InstanceKlass* ik); ++ ++ static bool is_builtin(InstanceKlass* k) { ++ return (k->shared_classpath_index() != UNREGISTERED_INDEX); ++ } ++ ++ static void dumptime_classes_do(class MetaspaceClosure* it); ++ ++ static void replace_klass_in_constantPool(); ++ ++ static bool is_excluded_class(InstanceKlass* k); + // The (non-application) CDS implementation supports only classes in the boot + // class loader, which ensures that the verification dependencies are the same + // during archive creation time and runtime. Thus we can do the dependency checks +@@ -69,6 +181,7 @@ public: + static void add_verification_dependency(Klass* k, Symbol* accessor_clsname, + Symbol* target_clsname) {} + static void finalize_verification_dependencies() {} ++ static void set_class_has_failed_verification(InstanceKlass* ik); + static bool check_verification_dependencies(Klass* k, Handle class_loader, + Handle protection_domain, + char** message_buffer, TRAPS) { +@@ -81,6 +194,49 @@ public: + } + return true; + } ++ static size_t estimate_size_for_archive(); ++ static void write_to_archive(); ++ static void write_dictionary(RunTimeSharedDictionary* dictionary, bool is_builtin); ++ static void serialize_dictionary_headers(class SerializeClosure* soc); ++ static unsigned int hash_for_shared_dictionary(address ptr); ++ static void set_shared_class_misc_info(InstanceKlass* k, ClassFileStream* cfs); ++ static InstanceKlass* lookup_from_stream(Symbol* class_name, ++ Handle class_loader, ++ Handle protection_domain, ++ const ClassFileStream* cfs, ++ TRAPS); ++ ++ DEBUG_ONLY(static bool no_class_loading_should_happen() {return _no_class_loading_should_happen;}) ++ ++#ifdef ASSERT ++ class NoClassLoadingMark: public StackObj { ++ public: ++ NoClassLoadingMark() { ++ assert(!_no_class_loading_should_happen, "must not be nested"); ++ _no_class_loading_should_happen = true; ++ } ++ ~NoClassLoadingMark() { ++ _no_class_loading_should_happen = false; ++ } ++ }; ++#endif ++ ++ template ++ static unsigned int hash_for_shared_dictionary_quick(T* ptr) { ++ assert(((MetaspaceObj*)ptr)->is_shared(), "must be"); ++ assert(ptr > (T*)SharedBaseAddress, "must be"); ++ uintx offset = uintx(ptr) - uintx(SharedBaseAddress); ++ return primitive_hash(offset); ++ } ++ ++ static const RunTimeSharedClassInfo* find_record(RunTimeSharedDictionary* dynamic_dict, Symbol* name); ++ static InstanceKlass* acquire_class_for_current_thread(InstanceKlass *ik, ++ Handle class_loader, ++ Handle protection_domain, ++ const ClassFileStream *cfs, ++ TRAPS); ++ ++ static InstanceKlass* find_dynamic_builtin_class(Symbol* name); + }; + + #endif // SHARE_VM_CLASSFILE_SYSTEMDICTIONARYSHARED_HPP +diff --git a/hotspot/src/share/vm/memory/allocation.hpp b/hotspot/src/share/vm/memory/allocation.hpp +index aa8f02d09..4d324b442 100644 +--- a/hotspot/src/share/vm/memory/allocation.hpp ++++ b/hotspot/src/share/vm/memory/allocation.hpp +@@ -302,6 +302,11 @@ class MetaspaceObj { + Type type, Thread* thread) throw(); + // can't use TRAPS from this header file. + void operator delete(void* p) { ShouldNotCallThis(); } ++ ++ // Declare a *static* method with the same signature in any subclass of MetaspaceObj ++ // that should be read-only by default. See symbol.hpp for an example. This function ++ // is used by the templates in metaspaceClosure.hpp ++ static bool is_read_only_by_default() { return false; } + }; + + // Base class for classes that constitute name spaces. +@@ -728,6 +733,12 @@ class ArrayAllocator VALUE_OBJ_CLASS_SPEC { + bool _use_malloc; + size_t _size; + bool _free_in_destructor; ++ ++ static bool should_use_malloc(size_t size) { ++ return size < ArrayAllocatorMallocLimit; ++ } ++ ++ static char* allocate_inner(size_t& size, bool& use_malloc); + public: + ArrayAllocator(bool free_in_destructor = true) : + _addr(NULL), _use_malloc(false), _size(0), _free_in_destructor(free_in_destructor) { } +@@ -739,6 +750,7 @@ class ArrayAllocator VALUE_OBJ_CLASS_SPEC { + } + + E* allocate(size_t length); ++ E* reallocate(size_t new_length); + void free(); + }; + +diff --git a/hotspot/src/share/vm/memory/allocation.inline.hpp b/hotspot/src/share/vm/memory/allocation.inline.hpp +index 9f2e1655a..2e794a8b6 100644 +--- a/hotspot/src/share/vm/memory/allocation.inline.hpp ++++ b/hotspot/src/share/vm/memory/allocation.inline.hpp +@@ -151,35 +151,58 @@ template void CHeapObj::operator delete [](void* p){ + } + + template +-E* ArrayAllocator::allocate(size_t length) { +- assert(_addr == NULL, "Already in use"); ++char* ArrayAllocator::allocate_inner(size_t &size, bool &use_malloc) { ++ char* addr = NULL; + +- _size = sizeof(E) * length; +- _use_malloc = _size < ArrayAllocatorMallocLimit; +- +- if (_use_malloc) { +- _addr = AllocateHeap(_size, F); +- if (_addr == NULL && _size >= (size_t)os::vm_allocation_granularity()) { ++ if (use_malloc) { ++ addr = AllocateHeap(size, F); ++ if (addr == NULL && size >= (size_t)os::vm_allocation_granularity()) { + // malloc failed let's try with mmap instead +- _use_malloc = false; ++ use_malloc = false; + } else { +- return (E*)_addr; ++ return addr; + } + } + + int alignment = os::vm_allocation_granularity(); +- _size = align_size_up(_size, alignment); ++ size = align_size_up(size, alignment); + +- _addr = os::reserve_memory(_size, NULL, alignment, F); +- if (_addr == NULL) { +- vm_exit_out_of_memory(_size, OOM_MMAP_ERROR, "Allocator (reserve)"); ++ addr = os::reserve_memory(size, NULL, alignment, F); ++ if (addr == NULL) { ++ vm_exit_out_of_memory(size, OOM_MMAP_ERROR, "Allocator (reserve)"); + } + +- os::commit_memory_or_exit(_addr, _size, !ExecMem, "Allocator (commit)"); ++ os::commit_memory_or_exit(addr, size, !ExecMem, "Allocator (commit)"); ++ return addr; ++} ++ ++template ++E* ArrayAllocator::allocate(size_t length) { ++ assert(_addr == NULL, "Already in use"); + ++ _size = sizeof(E) * length; ++ ++ _use_malloc = should_use_malloc(_size); ++ _addr = allocate_inner(_size, _use_malloc); + return (E*)_addr; + } + ++template ++E* ArrayAllocator::reallocate(size_t new_length) { ++ size_t new_size = sizeof(E) * new_length; ++ bool use_malloc = should_use_malloc(new_size); ++ char* new_addr = allocate_inner(new_size, use_malloc); ++ ++ memcpy(new_addr, _addr, MIN2(new_size, _size)); ++ ++ free(); ++ _size = new_size; ++ _use_malloc = use_malloc; ++ _addr = new_addr; ++ return (E*)new_addr; ++} ++ ++ + template + void ArrayAllocator::free() { + if (_addr != NULL) { +diff --git a/hotspot/src/share/vm/memory/filemap.cpp b/hotspot/src/share/vm/memory/filemap.cpp +index 99b1f58d0..3f4106476 100644 +--- a/hotspot/src/share/vm/memory/filemap.cpp ++++ b/hotspot/src/share/vm/memory/filemap.cpp +@@ -24,6 +24,8 @@ + + #include "jvm.h" + #include "precompiled.hpp" ++#include "cds/archiveBuilder.hpp" ++#include "cds/dynamicArchive.hpp" + #include "classfile/classLoader.hpp" + #include "classfile/sharedClassUtil.hpp" + #include "classfile/symbolTable.hpp" +@@ -140,19 +142,33 @@ template static void get_header_version(char (&header_version) [N]) { + } + } + +-FileMapInfo::FileMapInfo() { +- assert(_current_info == NULL, "must be singleton"); // not thread safe +- _current_info = this; ++FileMapInfo::FileMapInfo(bool is_static) { + memset(this, 0, sizeof(FileMapInfo)); ++ _is_static = is_static; ++ ++ if (is_static) { ++ assert(_current_info == NULL, "must be singleton"); // not thread safe ++ _current_info = this; ++ _header = SharedClassUtil::allocate_file_map_header(); ++ } else { ++ assert(_dynamic_archive_info == NULL, "must be singleton"); // not thread safe ++ _dynamic_archive_info = this; ++ _header = SharedClassUtil::allocate_dynamic_archive_header(); ++ } ++ ++ _header->_version = _invalid_version; + _file_offset = 0; + _file_open = false; +- _header = SharedClassUtil::allocate_file_map_header(); +- _header->_version = _invalid_version; + } + + FileMapInfo::~FileMapInfo() { +- assert(_current_info == this, "must be singleton"); // not thread safe +- _current_info = NULL; ++ if (_is_static) { ++ assert(_current_info == this, "must be singleton"); // not thread safe ++ _current_info = NULL; ++ } else { ++ assert(_dynamic_archive_info == this, "must be singleton"); // not thread safe ++ _dynamic_archive_info = NULL; ++ } + } + + void FileMapInfo::populate_header(size_t alignment) { +@@ -163,14 +179,66 @@ size_t FileMapInfo::FileMapHeader::data_size() { + return SharedClassUtil::file_map_header_size() - sizeof(FileMapInfo::FileMapHeaderBase); + } + ++size_t FileMapInfo::DynamicArchiveHeader::data_size() { ++ return sizeof(FileMapInfo::DynamicArchiveHeader) - sizeof(FileMapInfo::FileMapHeaderBase); ++} ++ ++bool FileMapInfo::DynamicArchiveHeader::validate() { ++ if (_magic != CDS_DYNAMIC_ARCHIVE_MAGIC) { ++ FileMapInfo::fail_continue("The shared archive file has a bad magic number."); ++ return false; ++ } ++ if (VerifySharedSpaces && compute_crc() != _crc) { ++ fail_continue("Header checksum verification failed."); ++ return false; ++ } ++ if (_version != current_version()) { ++ FileMapInfo::fail_continue("The shared archive file is the wrong version."); ++ return false; ++ } ++ char header_version[JVM_IDENT_MAX]; ++ get_header_version(header_version); ++ if (strncmp(_jvm_ident, header_version, JVM_IDENT_MAX-1) != 0) { ++ if (TraceClassPaths) { ++ tty->print_cr("Expected: %s", header_version); ++ tty->print_cr("Actual: %s", _jvm_ident); ++ } ++ FileMapInfo::fail_continue("The shared archive file was created by a different" ++ " version or build of HotSpot"); ++ return false; ++ } ++ if (_obj_alignment != ObjectAlignmentInBytes) { ++ FileMapInfo::fail_continue("The shared archive file's ObjectAlignmentInBytes of %d" ++ " does not equal the current ObjectAlignmentInBytes of %d.", ++ _obj_alignment, ObjectAlignmentInBytes); ++ return false; ++ } ++ ++ // TODO: much more validate check ++ ++ return true; ++} ++ + void FileMapInfo::FileMapHeader::populate(FileMapInfo* mapinfo, size_t alignment) { +- _magic = 0xf00baba2; +- _version = _current_version; ++ if (DynamicDumpSharedSpaces) { ++ _magic = CDS_DYNAMIC_ARCHIVE_MAGIC; ++ } else { ++ _magic = CDS_ARCHIVE_MAGIC; ++ } ++ _version = current_version(); + _alignment = alignment; + _obj_alignment = ObjectAlignmentInBytes; +- _classpath_entry_table_size = mapinfo->_classpath_entry_table_size; +- _classpath_entry_table = mapinfo->_classpath_entry_table; +- _classpath_entry_size = mapinfo->_classpath_entry_size; ++ /* TODO ++ _compressed_oops = UseCompressedOops; ++ _compressed_class_ptrs = UseCompressedClassPointers; ++ _max_heap_size = MaxHeapSize; ++ _narrow_klass_shift = CompressedKlassPointers::shift(); ++ */ ++ if (!DynamicDumpSharedSpaces) { ++ _classpath_entry_table_size = mapinfo->_classpath_entry_table_size; ++ _classpath_entry_table = mapinfo->_classpath_entry_table; ++ _classpath_entry_size = mapinfo->_classpath_entry_size; ++ } + + // The following fields are for sanity checks for whether this archive + // will function correctly with this JVM and the bootclasspath it's +@@ -303,62 +371,174 @@ bool FileMapInfo::validate_classpath_entry_table() { + return true; + } + ++bool FileMapInfo::get_base_archive_name_from_header(const char* archive_name, ++ int* size, char** base_archive_name) { ++ int fd = os::open(archive_name, O_RDONLY | O_BINARY, 0); ++ if (fd < 0) { ++ *size = 0; ++ return false; ++ } + +-// Read the FileMapInfo information from the file. +- +-bool FileMapInfo::init_from_file(int fd) { +- size_t sz = _header->data_size(); +- char* addr = _header->data(); ++ // read the header as a dynamic archive header ++ DynamicArchiveHeader* dynamic_header = SharedClassUtil::allocate_dynamic_archive_header(); ++ size_t sz = dynamic_header->data_size(); ++ char* addr = dynamic_header->data(); + size_t n = os::read(fd, addr, (unsigned int)sz); + if (n != sz) { + fail_continue("Unable to read the file header."); ++ delete dynamic_header; ++ os::close(fd); + return false; + } +- if (_header->_version != current_version()) { +- fail_continue("The shared archive file has the wrong version."); ++ if (dynamic_header->magic() != CDS_DYNAMIC_ARCHIVE_MAGIC) { ++ // Not a dynamic header, no need to proceed further. ++ *size = 0; ++ delete dynamic_header; ++ os::close(fd); + return false; + } + +- size_t info_size = _header->_paths_misc_info_size; +- _paths_misc_info = NEW_C_HEAP_ARRAY_RETURN_NULL(char, info_size, mtClass); +- if (_paths_misc_info == NULL) { +- fail_continue("Unable to read the file header."); ++ // read the base archive name ++ size_t name_size = dynamic_header->base_archive_name_size(); ++ if (name_size == 0) { ++ delete dynamic_header; ++ os::close(fd); + return false; + } +- n = os::read(fd, _paths_misc_info, (unsigned int)info_size); +- if (n != info_size) { +- fail_continue("Unable to read the shared path info header."); +- FREE_C_HEAP_ARRAY(char, _paths_misc_info, mtClass); +- _paths_misc_info = NULL; ++ *base_archive_name = NEW_C_HEAP_ARRAY(char, name_size, mtInternal); ++ n = os::read(fd, *base_archive_name, (unsigned int)name_size); ++ if (n != name_size) { ++ fail_continue("Unable to read the base archive name from the header."); ++ FREE_C_HEAP_ARRAY(char, *base_archive_name, mtInternal); ++ *base_archive_name = NULL; ++ delete dynamic_header; ++ os::close(fd); + return false; + } + +- size_t len = lseek(fd, 0, SEEK_END); +- struct FileMapInfo::FileMapHeader::space_info* si = +- &_header->_space[MetaspaceShared::mc]; +- if (si->_file_offset >= len || len - si->_file_offset < si->_used) { +- fail_continue("The shared archive file has been truncated."); ++ delete dynamic_header; ++ os::close(fd); ++ return true; ++} ++ ++bool FileMapInfo::check_archive(const char* archive_name, bool is_static) { ++ int fd = os::open(archive_name, O_RDONLY | O_BINARY, 0); ++ if (fd < 0) { ++ // do not vm_exit_during_initialization here because Arguments::init_shared_archive_paths() ++ // requires a shared archive name. The open_for_read() function will log a message regarding ++ // failure in opening a shared archive. + return false; + } + +- _file_offset += (long)n; ++ FileMapHeader* header = NULL; ++ if (is_static) { ++ header = SharedClassUtil::allocate_file_map_header(); ++ } else { ++ header = SharedClassUtil::allocate_dynamic_archive_header(); ++ } ++ ++ size_t sz = header->data_size(); ++ size_t n = os::read(fd, header->data(), (unsigned int)sz); ++ if (n != sz) { ++ delete header; ++ os::close(fd); ++ vm_exit_during_initialization("Unable to read header from shared archive", archive_name); ++ return false; ++ } ++ if (is_static) { ++ FileMapHeader* static_header = (FileMapHeader*)header; ++ if (static_header->magic() != CDS_ARCHIVE_MAGIC) { ++ delete header; ++ os::close(fd); ++ vm_exit_during_initialization("Not a base shared archive", archive_name); ++ return false; ++ } ++ } else { ++ DynamicArchiveHeader* dynamic_header = (DynamicArchiveHeader*)header; ++ if (dynamic_header->magic() != CDS_DYNAMIC_ARCHIVE_MAGIC) { ++ delete header; ++ os::close(fd); ++ vm_exit_during_initialization("Not a top shared archive", archive_name); ++ return false; ++ } ++ } ++ delete header; ++ os::close(fd); ++ return true; ++} ++ ++// Read the FileMapInfo information from the file. ++ ++bool FileMapInfo::init_from_file(int fd) { ++ size_t sz = header()->data_size(); ++ char* addr = header()->data(); ++ size_t n = os::read(fd, addr, (unsigned int)sz); ++ if (n != sz) { ++ fail_continue("Unable to read the file header."); ++ return false; ++ } ++ ++ _file_offset += n; ++ ++ if (is_static()) { ++ size_t info_size = _header->_paths_misc_info_size; ++ _paths_misc_info = NEW_C_HEAP_ARRAY_RETURN_NULL(char, info_size, mtClass); ++ if (_paths_misc_info == NULL) { ++ fail_continue("Unable to read the file header."); ++ return false; ++ } ++ n = os::read(fd, _paths_misc_info, (unsigned int)info_size); ++ if (n != info_size) { ++ fail_continue("Unable to read the shared path info header."); ++ FREE_C_HEAP_ARRAY(char, _paths_misc_info, mtClass); ++ _paths_misc_info = NULL; ++ return false; ++ } ++ ++ // just checking the last region is sufficient since the archive is written ++ // in sequential order ++ size_t len = lseek(fd, 0, SEEK_END); ++ struct FileMapInfo::FileMapHeader::space_info* si = ++ &_header->_space[MetaspaceShared::mc]; ++ if (si->_file_offset >= len || len - si->_file_offset < si->_used) { ++ fail_continue("The shared archive file has been truncated."); ++ return false; ++ } ++ ++ _file_offset += n; ++ } else { ++ _file_offset += dynamic_header()->base_archive_name_size(); // accounts for the size of _base_archive_name ++ } ++ + return true; + } + + + // Read the FileMapInfo information from the file. + bool FileMapInfo::open_for_read() { +- _full_path = make_log_name(Arguments::GetSharedArchivePath(), NULL); +- int fd = open(_full_path, O_RDONLY | O_BINARY, 0); ++ if (_file_open) { ++ return true; ++ } ++ if (is_static()) { ++ _full_path = Arguments::GetSharedArchivePath(); ++ } else { ++ _full_path = Arguments::GetSharedDynamicArchivePath(); ++ } ++ if (InfoDynamicCDS) { ++ dynamic_cds_log->print_cr("trying to map %s", _full_path); ++ } ++ int fd = os::open(_full_path, O_RDONLY | O_BINARY, 0); + if (fd < 0) { + if (errno == ENOENT) { +- // Not locating the shared archive is ok. +- fail_continue("Specified shared archive not found. archive file path:%s", _full_path); ++ fail_continue("Specified shared archive not found (%s).", _full_path); + } else { +- fail_continue("Failed to open shared archive file (%s).", +- strerror(errno)); ++ fail_continue("Failed to open shared archive file (%s).", strerror(errno)); + } + return false; ++ } else { ++ if (InfoDynamicCDS) { ++ dynamic_cds_log->print_cr("Opened archive %s.", _full_path); ++ } + } + + _fd = fd; +@@ -368,7 +548,7 @@ bool FileMapInfo::open_for_read() { + + // Write the FileMapInfo information to the file. + void FileMapInfo::open_for_write() { +- if (UseAppCDS && AppCDSLockFile != NULL) { ++ if ((DynamicDumpSharedSpaces || UseAppCDS) && AppCDSLockFile != NULL) { + char* pos = strrchr(const_cast(AppCDSLockFile), '/'); + #ifdef __linux__ + if (pos != NULL && pos != AppCDSLockFile) { // No directory path specified +@@ -391,14 +571,18 @@ void FileMapInfo::open_for_write() { + int lock_fd = open(_appcds_file_lock_path, O_CREAT | O_WRONLY | O_EXCL, S_IRUSR | S_IWUSR); + if (lock_fd < 0) { + tty->print_cr("Failed to create jsa file !\n Please check: \n 1. The directory exists.\n " +- "2. You have the permission.\n 3. Make sure no other process using the same lock file.\n"); ++ "2. You have the permission.\n 3. Make sure no other process using the same lock file.\n"); + fail_stop("Failed to create appcds lock file, the lock path is: %s.", _appcds_file_lock_path); + } + tty->print_cr("You are using file lock %s in concurrent mode", AppCDSLockFile); + } + #endif + } +- _full_path = make_log_name(Arguments::GetSharedArchivePath(), NULL); ++ if (is_static()) { ++ _full_path = make_log_name(Arguments::GetSharedArchivePath(), NULL); ++ } else { ++ _full_path = make_log_name(Arguments::GetSharedDynamicArchivePath(), NULL); ++ } + if (PrintSharedSpaces) { + tty->print_cr("Dumping shared data to file: "); + tty->print_cr(" %s", _full_path); +@@ -436,6 +620,18 @@ void FileMapInfo::write_header() { + align_file_position(); + } + ++void FileMapInfo::write_dynamic_header() { ++ align_file_position(); ++ size_t sz = _header->data_size(); ++ char* addr = _header->data(); ++ write_bytes(addr, (int)sz); // skip the C++ vtable ++ ++ char* base_archive_name = (char*)Arguments::GetSharedArchivePath(); ++ if (base_archive_name != NULL) { ++ write_bytes(base_archive_name, dynamic_header()->base_archive_name_size()); ++ } ++ align_file_position(); ++} + + // Dump shared spaces to file. + +@@ -464,7 +660,15 @@ void FileMapInfo::write_region(int region, char* base, size_t size, + } else { + si->_file_offset = _file_offset; + } +- si->_base = base; ++ if (is_static()) { ++ si->_base = base; ++ } else { ++ if (region == MetaspaceShared::d_bm) { ++ si->_base = NULL; // always NULL for bm region ++ } else { ++ si->_base = ArchiveBuilder::current()->to_requested(base); ++ } ++ } + si->_used = size; + si->_capacity = capacity; + si->_read_only = read_only; +@@ -473,7 +677,16 @@ void FileMapInfo::write_region(int region, char* base, size_t size, + write_bytes_aligned(base, (int)size); + } + ++char* FileMapInfo::write_bitmap_region(const BitMap* ptrmap) { ++ size_t size_in_bits = ptrmap->size(); ++ size_t size_in_bytes = ptrmap->size_in_words() * BytesPerWord; ++ char* buffer = NEW_C_HEAP_ARRAY(char, size_in_bytes, mtClassShared); ++ ptrmap->write_to((BitMap::bm_word_t*)buffer, size_in_bytes); ++ dynamic_header()->set_ptrmap_size_in_bits(size_in_bits); + ++ write_region(MetaspaceShared::d_bm, (char*)buffer, size_in_bytes, size_in_bytes, /*read_only=*/true, /*allow_exec=*/false); ++ return buffer; ++} + // Dump bytes to file -- at the current file position. + + void FileMapInfo::write_bytes(const void* buffer, int nbytes) { +@@ -542,7 +755,7 @@ void FileMapInfo::close() { + // JVM/TI RedefineClasses() support: + // Remap the shared readonly space to shared readwrite, private. + bool FileMapInfo::remap_shared_readonly_as_readwrite() { +- struct FileMapInfo::FileMapHeader::space_info* si = &_header->_space[0]; ++ struct FileMapInfo::FileMapHeader::space_info* si = is_static() ? &_header->_space[0] : &_header->_space[1]; + if (!si->_read_only) { + // the space is already readwrite so we are done + return true; +@@ -570,10 +783,14 @@ bool FileMapInfo::remap_shared_readonly_as_readwrite() { + + // Map the whole region at once, assumed to be allocated contiguously. + ReservedSpace FileMapInfo::reserve_shared_memory() { +- struct FileMapInfo::FileMapHeader::space_info* si = &_header->_space[0]; +- char* requested_addr = si->_base; ++ char* requested_addr = region_base(0); ++ size_t size = 0; + +- size_t size = FileMapInfo::shared_spaces_size(); ++ if (is_static()) { ++ size = FileMapInfo::shared_spaces_size(); ++ } else { ++ size = align_up((uintptr_t)region_end(1) - (uintptr_t)region_base(0), (size_t)os::vm_allocation_granularity()); ++ } + + // Reserve the space first, then map otherwise map will go right over some + // other reserved memory (like the code cache). +@@ -648,6 +865,7 @@ void FileMapInfo::assert_mark(bool check) { + + + FileMapInfo* FileMapInfo::_current_info = NULL; ++FileMapInfo* FileMapInfo::_dynamic_archive_info = NULL; + SharedClassPathEntry* FileMapInfo::_classpath_entry_table = NULL; + int FileMapInfo::_classpath_entry_table_size = 0; + size_t FileMapInfo::_classpath_entry_size = 0x1234baad; +@@ -674,19 +892,26 @@ bool FileMapInfo::initialize() { + if (!open_for_read()) { + return false; + } +- +- init_from_file(_fd); ++ if (!init_from_file(_fd)) { ++ return false; ++ } + if (!validate_header()) { + return false; + } + +- SharedReadOnlySize = _header->_space[0]._capacity; +- SharedReadWriteSize = _header->_space[1]._capacity; +- SharedMiscDataSize = _header->_space[2]._capacity; +- SharedMiscCodeSize = _header->_space[3]._capacity; ++ if (is_static()) { ++ SharedReadOnlySize = _header->_space[0]._capacity; ++ SharedReadWriteSize = _header->_space[1]._capacity; ++ SharedMiscDataSize = _header->_space[2]._capacity; ++ SharedMiscCodeSize = _header->_space[3]._capacity; ++ } + return true; + } + ++void FileMapInfo::DynamicArchiveHeader::set_as_offset(char* p, size_t *offset) { ++ *offset = ArchiveBuilder::current()->any_to_offset((address)p); ++} ++ + int FileMapInfo::FileMapHeader::compute_crc() { + char* header = data(); + // start computing from the field after _crc +@@ -701,7 +926,7 @@ int FileMapInfo::compute_header_crc() { + } + + bool FileMapInfo::FileMapHeader::validate() { +- if (_magic != (int)0xf00baba2) { ++ if (_magic != CDS_ARCHIVE_MAGIC) { + FileMapInfo::fail_continue("The shared archive file has a bad magic number."); + return false; + } +@@ -738,6 +963,10 @@ bool FileMapInfo::FileMapHeader::validate() { + bool FileMapInfo::validate_header() { + bool status = _header->validate(); + ++ if (status && !is_static()) { ++ return DynamicArchive::validate(this); ++ } ++ + if (status) { + if (!ClassLoader::check_shared_paths_misc_info(_paths_misc_info, _header->_paths_misc_info_size)) { + if (!PrintSharedArchiveAndExit) { +@@ -761,7 +990,13 @@ bool FileMapInfo::validate_header() { + // Return: + // True if the p is within the mapped shared space, otherwise, false. + bool FileMapInfo::is_in_shared_space(const void* p) { +- for (int i = 0; i < MetaspaceShared::n_regions; i++) { ++ int count = 0; ++ if (is_static()) { ++ count = MetaspaceShared::n_regions; ++ } else { ++ count = MetaspaceShared::d_n_regions; ++ } ++ for (int i = 0; i < count; i++) { + if (p >= _header->_space[i]._base && + p < _header->_space[i]._base + _header->_space[i]._used) { + return true; +@@ -772,6 +1007,11 @@ bool FileMapInfo::is_in_shared_space(const void* p) { + } + + void FileMapInfo::print_shared_spaces() { ++ // TODO: support dynamic archive ++ if (!is_static()) { ++ return; ++ } ++ + gclog_or_tty->print_cr("Shared Spaces:"); + for (int i = 0; i < MetaspaceShared::n_regions; i++) { + struct FileMapInfo::FileMapHeader::space_info* si = &_header->_space[i]; +diff --git a/hotspot/src/share/vm/memory/filemap.hpp b/hotspot/src/share/vm/memory/filemap.hpp +index 0eee1c7ea..eab9ebcfc 100644 +--- a/hotspot/src/share/vm/memory/filemap.hpp ++++ b/hotspot/src/share/vm/memory/filemap.hpp +@@ -27,6 +27,8 @@ + + #include "memory/metaspaceShared.hpp" + #include "memory/metaspace.hpp" ++#include "runtime/os.hpp" ++#include "utilities/align.hpp" + + // Layout of the file: + // header: dump of archive instance plus versioning info, datestamp, etc. +@@ -37,8 +39,12 @@ + // misc data (block offset table, string table, symbols, dictionary, etc.) + // tag(666) + ++#define CDS_ARCHIVE_MAGIC 0xf00baba2 ++#define CDS_DYNAMIC_ARCHIVE_MAGIC 0xf00baba8 ++ + static const int JVM_IDENT_MAX = 256; + ++class BitMap; + class Metaspace; + + class SharedClassPathEntry VALUE_OBJ_CLASS_SPEC { +@@ -56,11 +62,13 @@ private: + friend class ManifestStream; + enum { + _invalid_version = -1, +- _current_version = 2 ++ _current_version = 3, + }; + +- bool _file_open; +- int _fd; ++ bool _is_static; ++ bool _file_open; ++ bool _is_mapped; ++ int _fd; + size_t _file_offset; + + private: +@@ -77,20 +85,21 @@ public: + struct FileMapHeaderBase : public CHeapObj { + virtual bool validate() = 0; + virtual void populate(FileMapInfo* info, size_t alignment) = 0; +- }; +- struct FileMapHeader : FileMapHeaderBase { + // Use data() and data_size() to memcopy to/from the FileMapHeader. We need to + // avoid read/writing the C++ vtable pointer. +- static size_t data_size(); ++ virtual size_t data_size() = 0; ++ }; ++ struct FileMapHeader : FileMapHeaderBase { ++ size_t data_size(); + char* data() { + return ((char*)this) + sizeof(FileMapHeaderBase); + } + +- int _magic; // identify file type. +- int _crc; // header crc checksum. +- int _version; // (from enum, above.) +- size_t _alignment; // how shared archive should be aligned +- int _obj_alignment; // value of ObjectAlignmentInBytes ++ unsigned int _magic; // identify file type. ++ int _crc; // header crc checksum. ++ int _version; // (from enum, above.) ++ size_t _alignment; // how shared archive should be aligned ++ int _obj_alignment; // value of ObjectAlignmentInBytes + + struct space_info { + int _crc; // crc checksum of the current space +@@ -137,7 +146,48 @@ public: + + virtual bool validate(); + virtual void populate(FileMapInfo* info, size_t alignment); ++ int crc() { return _crc; } ++ int space_crc(int i) { return _space[i]._crc; } + int compute_crc(); ++ unsigned int magic() const { return _magic; } ++ const char* jvm_ident() const { return _jvm_ident; } ++ }; ++ ++ // Fixme ++ struct DynamicArchiveHeader : FileMapHeader { ++ private: ++ int _base_header_crc; ++ int _base_region_crc[MetaspaceShared::n_regions]; ++ char* _requested_base_address; // Archive relocation is not necessary if we map with this base address. ++ size_t _ptrmap_size_in_bits; // Size of pointer relocation bitmap ++ size_t _base_archive_name_size; ++ size_t _serialized_data_offset; // Data accessed using {ReadClosure,WriteClosure}::serialize() ++ ++ public: ++ size_t data_size(); ++ int base_header_crc() const { return _base_header_crc; } ++ int base_region_crc(int i) const { ++ return _base_region_crc[i]; ++ } ++ ++ void set_base_header_crc(int c) { _base_header_crc = c; } ++ void set_base_region_crc(int i, int c) { ++ _base_region_crc[i] = c; ++ } ++ ++ void set_requested_base(char* b) { ++ _requested_base_address = b; ++ } ++ size_t ptrmap_size_in_bits() const { return _ptrmap_size_in_bits; } ++ void set_ptrmap_size_in_bits(size_t s) { _ptrmap_size_in_bits = s; } ++ void set_base_archive_name_size(size_t s) { _base_archive_name_size = s; } ++ size_t base_archive_name_size() { return _base_archive_name_size; } ++ void set_as_offset(char* p, size_t *offset); ++ char* from_mapped_offset(size_t offset) const { return _requested_base_address + offset; } ++ void set_serialized_data(char* p) { set_as_offset(p, &_serialized_data_offset); } ++ char* serialized_data() const { return from_mapped_offset(_serialized_data_offset); } ++ ++ virtual bool validate(); + }; + + FileMapHeader * _header; +@@ -147,32 +197,52 @@ public: + char* _paths_misc_info; + + static FileMapInfo* _current_info; ++ static FileMapInfo* _dynamic_archive_info; + ++ static bool get_base_archive_name_from_header(const char* archive_name, ++ int* size, char** base_archive_name); ++ static bool check_archive(const char* archive_name, bool is_static); + bool init_from_file(int fd); + void align_file_position(); + bool validate_header_impl(); + + public: +- FileMapInfo(); ++ FileMapInfo(bool is_static = true); + ~FileMapInfo(); + + static int current_version() { return _current_version; } + int compute_header_crc(); + void set_header_crc(int crc) { _header->_crc = crc; } ++ int space_crc(int i) { return _header->_space[i]._crc; } + void populate_header(size_t alignment); + bool validate_header(); + void invalidate(); ++ int crc() { return _header->_crc; } + int version() { return _header->_version; } + size_t alignment() { return _header->_alignment; } + size_t space_capacity(int i) { return _header->_space[i]._capacity; } ++ size_t used(int i) { return _header->_space[i]._used; } ++ size_t used_aligned(int i) { return align_up(used(i), (size_t)os::vm_allocation_granularity()); } + char* region_base(int i) { return _header->_space[i]._base; } ++ char* region_end(int i) { return region_base(i) + used_aligned(i); } + struct FileMapHeader* header() { return _header; } ++ struct DynamicArchiveHeader* dynamic_header() { ++ // assert(!is_static(), "must be"); ++ return (struct DynamicArchiveHeader*)header(); ++ } ++ ++ void set_header_base_archive_name_size(size_t size) { dynamic_header()->set_base_archive_name_size(size); } + + static FileMapInfo* current_info() { + CDS_ONLY(return _current_info;) + NOT_CDS(return NULL;) + } + ++ static FileMapInfo* dynamic_info() { ++ CDS_ONLY(return _dynamic_archive_info;) ++ NOT_CDS(return NULL;) ++ } ++ + static void assert_mark(bool check); + + // File manipulation. +@@ -180,18 +250,24 @@ public: + bool open_for_read(); + void open_for_write(); + void write_header(); ++ void write_dynamic_header(); + void write_space(int i, Metaspace* space, bool read_only); + void write_region(int region, char* base, size_t size, + size_t capacity, bool read_only, bool allow_exec); ++ char* write_bitmap_region(const BitMap* ptrmap); + void write_bytes(const void* buffer, int count); + void write_bytes_aligned(const void* buffer, int count); + char* map_region(int i); + void unmap_region(int i); + bool verify_region_checksum(int i); + void close(); +- bool is_open() { return _file_open; } ++ bool is_open() { return _file_open; } ++ bool is_static() const { return _is_static; } ++ bool is_mapped() const { return _is_mapped; } ++ void set_is_mapped(bool v) { _is_mapped = v; } + ReservedSpace reserve_shared_memory(); +- ++ void set_requested_base(char* b) { dynamic_header()->set_requested_base(b); } ++ char* serialized_data() { return dynamic_header()->serialized_data(); } + // JVM/TI RedefineClasses() support: + // Remap the shared readonly space to shared readwrite, private. + bool remap_shared_readonly_as_readwrite(); +diff --git a/hotspot/src/share/vm/memory/iterator.hpp b/hotspot/src/share/vm/memory/iterator.hpp +index 62204eea7..dc01186a2 100644 +--- a/hotspot/src/share/vm/memory/iterator.hpp ++++ b/hotspot/src/share/vm/memory/iterator.hpp +@@ -378,6 +378,13 @@ public: + // for verification that sections of the serialized data are of the + // correct length. + virtual void do_tag(int tag) = 0; ++ ++ // Read/write the 32-bit unsigned integer pointed to by p. ++ virtual void do_u4(u4* p) { } ++ ++ bool writing() { ++ return !reading(); ++ } + }; + + class SymbolClosure : public StackObj { +diff --git a/hotspot/src/share/vm/memory/metaspace.cpp b/hotspot/src/share/vm/memory/metaspace.cpp +index 2912f41b6..7e95b5c0b 100644 +--- a/hotspot/src/share/vm/memory/metaspace.cpp ++++ b/hotspot/src/share/vm/memory/metaspace.cpp +@@ -37,6 +37,7 @@ + #include "memory/metaspaceTracer.hpp" + #include "memory/resourceArea.hpp" + #include "memory/universe.hpp" ++#include "runtime/arguments.hpp" + #include "runtime/atomic.inline.hpp" + #include "runtime/globals.hpp" + #include "runtime/init.hpp" +@@ -426,8 +427,16 @@ VirtualSpaceNode::VirtualSpaceNode(size_t bytes) : _top(NULL), _next(NULL), _rs( + assert(shared_base == 0 || _rs.base() == shared_base, "should match"); + } else { + // Get a mmap region anywhere if the SharedBaseAddress fails. ++ if (InfoDynamicCDS) { ++ dynamic_cds_log->print_cr("Could not allocate static space at request address: " INTPTR_FORMAT, p2i(shared_base)); ++ } + _rs = ReservedSpace(bytes, Metaspace::reserve_alignment(), large_pages); + } ++ // ...failing that, give up. ++ if (!_rs.is_reserved()) { ++ vm_exit_during_initialization( ++ err_msg("Could not allocate static shared space: " SIZE_FORMAT " bytes", bytes)); ++ } + MetaspaceShared::set_shared_rs(&_rs); + } else + #endif +@@ -3322,21 +3331,80 @@ void Metaspace::global_initialize() { + // the addresses don't conflict) + address cds_address = NULL; + if (UseSharedSpaces) { +- FileMapInfo* mapinfo = new FileMapInfo(); ++ FileMapInfo* static_mapinfo = new FileMapInfo(); ++ FileMapInfo* dynamic_mapinfo = new FileMapInfo(false); + + // Open the shared archive file, read and validate the header. If + // initialization fails, shared spaces [UseSharedSpaces] are + // disabled and the file is closed. +- // Map in spaces now also +- if (mapinfo->initialize() && MetaspaceShared::map_shared_spaces(mapinfo)) { ++ // ++ // This will reserve two address spaces suitable to house Klass structures, one ++ // for the cds archives (static archive and optionally dynamic archive) and ++ // optionally one move for ccs. ++ // ++ // Since both spaces must fall within the compressed class pointer encoding ++ // range, they are allocated close to each other. ++ // ++ // Space for archives will be reserved first, followed by a potential gap, ++ // followed by the space for ccs: ++ // ++ // +-- Base address End ++ // | | ++ // v v ++ // +------------+ +-------------+ +--------------------+ ++ // | static arc | [align] | [dyn. arch] | [align] | compr. class space | ++ // +------------+ +-------------+ +--------------------+ ++ // ++ // (The gap may result from different alignment requirements between metaspace ++ // and CDS) ++ // ++ // If UseCompressedClassPointers is disabled, only one address space will be ++ // reserved: ++ // ++ // +-- Base address End ++ // | | ++ // v v ++ // +------------+ +-------------+ ++ // | static arc | [align] | [dyn. arch] | ++ // +------------+ +-------------+ ++ // ++ // If UseCompressedClassPointers=1, the range encompassing both spaces will be ++ // suitable to en/decode narrow Klass pointers: the base will be valid for ++ // encoding, the range [Base, End) not surpass KlassEncodingMetaspaceMax. ++ if (static_mapinfo->initialize() && MetaspaceShared::map_shared_spaces(static_mapinfo)) { + cds_total = FileMapInfo::shared_spaces_size(); +- cds_address = (address)mapinfo->region_base(0); ++ cds_address = (address)static_mapinfo->region_base(0); ++ MetaspaceShared::set_shared_metaspace_static_bottom(cds_address); ++ // Update SharedBaseAddress to the same value as the dump phase. ++ SharedBaseAddress = (size_t)cds_address; ++ if (!DynamicDumpSharedSpaces && ++ (Arguments::GetSharedDynamicArchivePath() != NULL) && ++ dynamic_mapinfo->initialize() && ++ MetaspaceShared::map_shared_spaces(dynamic_mapinfo)) { ++ cds_total += align_up(dynamic_mapinfo->region_end(1) - dynamic_mapinfo->region_base(0), ++ (size_t)os::vm_allocation_granularity()); ++ } else { ++ assert(!dynamic_mapinfo->is_open(), ++ "dynamic archive file not closed or shared spaces not disabled."); ++ } + } else { +- assert(!mapinfo->is_open() && !UseSharedSpaces, +- "archive file not closed or shared spaces not disabled."); ++ assert(!static_mapinfo->is_open() && !UseSharedSpaces, ++ "static archive file not closed or shared spaces not disabled."); ++ } ++ ++ if (static_mapinfo != NULL && !static_mapinfo->is_mapped()) { ++ delete static_mapinfo; ++ } ++ if (dynamic_mapinfo != NULL && !dynamic_mapinfo->is_mapped()) { ++ delete dynamic_mapinfo; + } + } ++ ++ if (DynamicDumpSharedSpaces && !UseSharedSpaces) { ++ vm_exit_during_initialization("DynamicDumpSharedSpaces is unsupported when base CDS archive is not loaded", NULL); ++ } + #endif // INCLUDE_CDS ++ + #ifdef _LP64 + // If UseCompressedClassPointers is set then allocate the metaspace area + // above the heap and above the CDS area (if it exists). +diff --git a/hotspot/src/share/vm/memory/metaspace.hpp b/hotspot/src/share/vm/memory/metaspace.hpp +index 3920004a8..2b06cb620 100644 +--- a/hotspot/src/share/vm/memory/metaspace.hpp ++++ b/hotspot/src/share/vm/memory/metaspace.hpp +@@ -82,6 +82,7 @@ class VirtualSpaceList; + // quantum of metadata. + + class Metaspace : public CHeapObj { ++ friend class ArchiveBuilder; + friend class VMStructs; + friend class SpaceManager; + friend class VM_CollectForMetadataAllocation; +diff --git a/hotspot/src/share/vm/memory/metaspaceClosure.cpp b/hotspot/src/share/vm/memory/metaspaceClosure.cpp +new file mode 100644 +index 000000000..00ec8fced +--- /dev/null ++++ b/hotspot/src/share/vm/memory/metaspaceClosure.cpp +@@ -0,0 +1,87 @@ ++#include "precompiled.hpp" ++#include "memory/metaspaceClosure.hpp" ++ ++// Update the reference to point to new_loc. ++void MetaspaceClosure::Ref::update(address new_loc) const { ++ if (TraceDynamicCDS) { ++ dynamic_cds_log->print_cr("Ref: [" PTR_FORMAT "] -> " PTR_FORMAT " => " PTR_FORMAT, ++ p2i(mpp()), p2i(obj()), p2i(new_loc)); ++ } ++ uintx p = (uintx)new_loc; ++ p |= flag_bits(); // Make sure the flag bits are copied to the new pointer. ++ *(address*)mpp() = (address)p; ++} ++ ++void MetaspaceClosure::push_impl(MetaspaceClosure::Ref* ref) { ++ if (_nest_level < MAX_NEST_LEVEL) { ++ do_push(ref); ++ if (!ref->keep_after_pushing()) { ++ delete ref; ++ } ++ } else { ++ do_pending_ref(ref); ++ ref->set_next(_pending_refs); ++ _pending_refs = ref; ++ } ++} ++ ++void MetaspaceClosure::do_push(MetaspaceClosure::Ref* ref) { ++ if (ref->not_null()) { ++ bool read_only; ++ Writability w = ref->writability(); ++ switch (w) { ++ case _writable: ++ read_only = false; ++ break; ++ case _not_writable: ++ read_only = true; ++ break; ++ default: ++ assert(w == _default, "must be"); ++ read_only = ref->is_read_only_by_default(); ++ } ++ if (_nest_level == 0) { ++ assert(_enclosing_ref == NULL, "must be"); ++ } ++ _nest_level ++; ++ if (do_ref(ref, read_only)) { // true means we want to iterate the embedded pointer in ++ Ref* saved = _enclosing_ref; ++ _enclosing_ref = ref; ++ ref->metaspace_pointers_do(this); ++ _enclosing_ref = saved; ++ } ++ _nest_level --; ++ } ++} ++ ++void MetaspaceClosure::finish() { ++ assert(_nest_level == 0, "must be"); ++ while (_pending_refs != NULL) { ++ Ref* ref = _pending_refs; ++ _pending_refs = _pending_refs->next(); ++ do_push(ref); ++ if (!ref->keep_after_pushing()) { ++ delete ref; ++ } ++ } ++} ++ ++MetaspaceClosure::~MetaspaceClosure() { ++ assert(_pending_refs == NULL, ++ "you must explicitly call MetaspaceClosure::finish() to process all refs!"); ++} ++ ++bool UniqueMetaspaceClosure::do_ref(MetaspaceClosure::Ref* ref, bool read_only) { ++ bool created; ++ _has_been_visited.add_if_absent(ref->obj(), read_only, &created); ++ if (!created) { ++ return false; // Already visited: no need to iterate embedded pointers. ++ } else { ++ if (_has_been_visited.maybe_grow(MAX_TABLE_SIZE)) { ++ if (InfoDynamicCDS) { ++ dynamic_cds_log->print_cr("Expanded _has_been_visited table to %d", _has_been_visited.table_size()); ++ } ++ } ++ return do_unique_ref(ref, read_only); ++ } ++} +diff --git a/hotspot/src/share/vm/memory/metaspaceClosure.hpp b/hotspot/src/share/vm/memory/metaspaceClosure.hpp +new file mode 100644 +index 000000000..f67d8d6fd +--- /dev/null ++++ b/hotspot/src/share/vm/memory/metaspaceClosure.hpp +@@ -0,0 +1,381 @@ ++ ++ ++#ifndef SHARE_VM_MEMORY_METASPACECLOSURE_HPP ++#define SHARE_VM_MEMORY_METASPACECLOSURE_HPP ++ ++#include "memory/allocation.hpp" ++#include "utilities/array.hpp" ++#include "utilities/globalDefinitions.hpp" ++#include "utilities/hashtable.hpp" ++ ++// The metadata hierarchy is separate from the oop hierarchy ++ class MetaspaceObj; // no C++ vtable ++//class Array; // no C++ vtable ++ class Annotations; // no C++ vtable ++ class ConstantPoolCache; // no C++ vtable ++ class ConstMethod; // no C++ vtable ++ class MethodCounters; // no C++ vtable ++ class Symbol; // no C++ vtable ++ class Metadata; // has C++ vtable (so do all subclasses) ++ class ConstantPool; ++ class MethodData; ++ class Method; ++ class Klass; ++ class InstanceKlass; ++ class InstanceMirrorKlass; ++ class InstanceClassLoaderKlass; ++ class InstanceRefKlass; ++ class ArrayKlass; ++ class ObjArrayKlass; ++ class TypeArrayKlass; ++ ++// class MetaspaceClosure -- ++// ++// This class is used for iterating the objects in the HotSpot Metaspaces. It ++// provides an API to walk all the reachable objects starting from a set of ++// root references (such as all Klass'es in the SystemDictionary). ++// ++// Currently it is used for compacting the CDS archive by eliminate temporary ++// objects allocated during archive creation time. See ArchiveBuilder for an example. ++// ++// To support MetaspaceClosure, each subclass of MetaspaceObj must provide ++// a method of the type void metaspace_pointers_do(MetaspaceClosure*). This method ++// should call MetaspaceClosure::push() on every pointer fields of this ++// class that points to a MetaspaceObj. See Annotations::metaspace_pointers_do() ++// for an example. ++ ++ ++class MetaspaceClosure : public StackObj { ++public: ++ enum Writability { ++ _writable, ++ _not_writable, ++ _default ++ }; ++ ++ enum SpecialRef { ++ _method_entry_ref ++ }; ++ ++ // class MetaspaceClosure::Ref -- ++ // ++ // MetaspaceClosure can be viewed as a very simple type of copying garbage ++ // collector. For it to function properly, it requires each subclass of ++ // MetaspaceObj to provide two methods: ++ // ++ // size_t size(); -- to determine how much data to copy ++ // void metaspace_pointers_do(MetaspaceClosure*); -- to locate all the embedded pointers ++ // ++ // Calling these methods would be trivial if these two were virtual methods. ++ // However, to save space, MetaspaceObj has NO vtable. The vtable is introduced ++ // only in the Metadata class. ++ // ++ // To work around the lack of a vtable, we use the Ref class with templates ++ // (see MSORef, OtherArrayRef, MSOArrayRef, and MSOPointerArrayRef) ++ // so that we can statically discover the type of a object. The use of Ref ++ // depends on the fact that: ++ // ++ // [1] We don't use polymorphic pointers for MetaspaceObj's that are not subclasses ++ // of Metadata. I.e., we don't do this: ++ // class Klass { ++ // MetaspaceObj *_obj; ++ // Array* foo() { return (Array*)_obj; } ++ // Symbol* bar() { return (Symbol*) _obj; } ++ // ++ // [2] All Array dimensions are statically declared. ++ class Ref : public CHeapObj { ++ Writability _writability; ++ bool _keep_after_pushing; ++ Ref* _next; ++ void* _user_data; ++ ++ protected: ++ virtual void** mpp() const = 0; ++ Ref(Writability w) : _writability(w), _keep_after_pushing(false), _next(NULL), _user_data(NULL) {} ++ public: ++ virtual bool not_null() const = 0; ++ virtual int size() const = 0; ++ virtual void metaspace_pointers_do(MetaspaceClosure *it) const = 0; ++ virtual void metaspace_pointers_do_at(MetaspaceClosure *it, address new_loc) const = 0; ++ virtual MetaspaceObj::Type msotype() const = 0; ++ virtual bool is_read_only_by_default() const = 0; ++ virtual ~Ref() {} ++ ++ address obj() const { ++ // In some rare cases (see CPSlot in constantPool.hpp) we store some flags in the lowest ++ // 2 bits of a MetaspaceObj pointer. Unmask these when manipulating the pointer. ++ uintx p = (uintx)*mpp(); ++ return (address)(p & (~FLAG_MASK)); ++ } ++ ++ address* addr() const { ++ return (address*)mpp(); ++ } ++ ++ void update(address new_loc) const; ++ ++ Writability writability() const { return _writability; }; ++ void set_keep_after_pushing() { _keep_after_pushing = true; } ++ bool keep_after_pushing() { return _keep_after_pushing; } ++ void set_user_data(void* data) { _user_data = data; } ++ void* user_data() { return _user_data; } ++ void set_next(Ref* n) { _next = n; } ++ Ref* next() const { return _next; } ++ ++ private: ++ static const uintx FLAG_MASK = 0x03; ++ ++ int flag_bits() const { ++ uintx p = (uintx)*mpp(); ++ return (int)(p & FLAG_MASK); ++ } ++ }; ++ ++private: ++ // MSORef -- iterate an instance of MetaspaceObj ++ template class MSORef : public Ref { ++ T** _mpp; ++ T* dereference() const { ++ return *_mpp; ++ } ++ protected: ++ virtual void** mpp() const { ++ return (void**)_mpp; ++ } ++ ++ public: ++ MSORef(T** mpp, Writability w) : Ref(w), _mpp(mpp) {} ++ ++ virtual bool is_read_only_by_default() const { return T::is_read_only_by_default(); } ++ virtual bool not_null() const { return dereference() != NULL; } ++ virtual int size() const { return dereference()->size(); } ++ virtual MetaspaceObj::Type msotype() const { return dereference()->type(); } ++ ++ virtual void metaspace_pointers_do(MetaspaceClosure *it) const { ++ dereference()->metaspace_pointers_do(it); ++ } ++ virtual void metaspace_pointers_do_at(MetaspaceClosure *it, address new_loc) const { ++ ((T*)new_loc)->metaspace_pointers_do(it); ++ } ++ }; ++ ++ // abstract base class for MSOArrayRef, MSOPointerArrayRef and OtherArrayRef ++ template class ArrayRef : public Ref { ++ Array** _mpp; ++ protected: ++ Array* dereference() const { ++ return *_mpp; ++ } ++ virtual void** mpp() const { ++ return (void**)_mpp; ++ } ++ ++ ArrayRef(Array** mpp, Writability w) : Ref(w), _mpp(mpp) {} ++ ++ // all Arrays are read-only by default ++ virtual bool is_read_only_by_default() const { return true; } ++ virtual bool not_null() const { return dereference() != NULL; } ++ virtual int size() const { return dereference()->size(); } ++ virtual MetaspaceObj::Type msotype() const { return MetaspaceObj::array_type(sizeof(T)); } ++ }; ++ ++ // OtherArrayRef -- iterate an instance of Array, where T is NOT a subtype of MetaspaceObj. ++ // T can be a primitive type, such as int, or a structure. However, we do not scan ++ // the fields inside T, so you should not embed any pointers inside T. ++ template class OtherArrayRef : public ArrayRef { ++ public: ++ OtherArrayRef(Array** mpp, Writability w) : ArrayRef(mpp, w) {} ++ ++ virtual void metaspace_pointers_do(MetaspaceClosure *it) const { ++ Array* array = ArrayRef::dereference(); ++ if (TraceDynamicCDS) ++ dynamic_cds_log->print_cr("Iter(OtherArray): %p [%d]", array, array->length()); ++ } ++ virtual void metaspace_pointers_do_at(MetaspaceClosure *it, address new_loc) const { ++ Array* array = (Array*)new_loc; ++ if (TraceDynamicCDS) ++ dynamic_cds_log->print_cr("Iter(OtherArray): %p [%d]", array, array->length()); ++ } ++ }; ++ ++ // MSOArrayRef -- iterate an instance of Array, where T is a subtype of MetaspaceObj. ++ // We recursively call T::metaspace_pointers_do() for each element in this array. ++ template class MSOArrayRef : public ArrayRef { ++ public: ++ MSOArrayRef(Array** mpp, Writability w) : ArrayRef(mpp, w) {} ++ ++ virtual void metaspace_pointers_do(MetaspaceClosure *it) const { ++ metaspace_pointers_do_at_impl(it, ArrayRef::dereference()); ++ } ++ virtual void metaspace_pointers_do_at(MetaspaceClosure *it, address new_loc) const { ++ metaspace_pointers_do_at_impl(it, (Array*)new_loc); ++ } ++ private: ++ void metaspace_pointers_do_at_impl(MetaspaceClosure *it, Array* array) const { ++ if (TraceDynamicCDS) { ++ dynamic_cds_log->print_cr("Iter(MSOArray): %p [%d]", array, array->length()); ++ } ++ for (int i = 0; i < array->length(); i++) { ++ T* elm = array->adr_at(i); ++ elm->metaspace_pointers_do(it); ++ } ++ } ++ }; ++ ++ // MSOPointerArrayRef -- iterate an instance of Array, where T is a subtype of MetaspaceObj. ++ // We recursively call MetaspaceClosure::push() for each pointer in this array. ++ template class MSOPointerArrayRef : public ArrayRef { ++ public: ++ MSOPointerArrayRef(Array** mpp, Writability w) : ArrayRef(mpp, w) {} ++ ++ virtual void metaspace_pointers_do(MetaspaceClosure *it) const { ++ metaspace_pointers_do_at_impl(it, ArrayRef::dereference()); ++ } ++ virtual void metaspace_pointers_do_at(MetaspaceClosure *it, address new_loc) const { ++ metaspace_pointers_do_at_impl(it, (Array*)new_loc); ++ } ++ private: ++ void metaspace_pointers_do_at_impl(MetaspaceClosure *it, Array* array) const { ++ if (TraceDynamicCDS) { ++ dynamic_cds_log->print_cr("Iter(MSOPointerArray): %p [%d]", array, array->length()); ++ } ++ for (int i = 0; i < array->length(); i++) { ++ T** mpp = array->adr_at(i); ++ it->push(mpp); ++ } ++ } ++ }; ++ ++ // Normally, chains of references like a->b->c->d are iterated recursively. However, ++ // if recursion is too deep, we save the Refs in _pending_refs, and push them later in ++ // MetaspaceClosure::finish(). This avoids overflowing the C stack. ++ static const int MAX_NEST_LEVEL = 5; ++ Ref* _pending_refs; ++ int _nest_level; ++ Ref* _enclosing_ref; ++ ++ void push_impl(Ref* ref); ++ void do_push(Ref* ref); ++ ++public: ++ MetaspaceClosure(): _pending_refs(NULL), _nest_level(0), _enclosing_ref(NULL) {} ++ ~MetaspaceClosure(); ++ ++ void finish(); ++ ++ // enclosing_ref() is used to compute the offset of a field in a C++ class. For example ++ // class Foo { intx scala; Bar* ptr; } ++ // Foo *f = 0x100; ++ // when the f->ptr field is iterated with do_ref() on 64-bit platforms, we will have ++ // do_ref(Ref* r) { ++ // r->addr() == 0x108; // == &f->ptr; ++ // enclosing_ref()->obj() == 0x100; // == foo ++ // So we know that we are iterating upon a field at offset 8 of the object at 0x100. ++ // ++ // Note that if we have stack overflow, do_pending_ref(r) will be called first and ++ // do_ref(r) will be called later, for the same r. In this case, enclosing_ref() is valid only ++ // when do_pending_ref(r) is called, and will return NULL when do_ref(r) is called. ++ Ref* enclosing_ref() const { ++ return _enclosing_ref; ++ } ++ ++ // This is called when a reference is placed in _pending_refs. Override this ++ // function if you're using enclosing_ref(). See notes above. ++ virtual void do_pending_ref(Ref* ref) {} ++ ++ // returns true if we want to keep iterating the pointers embedded inside ++ virtual bool do_ref(Ref* ref, bool read_only) = 0; ++ ++private: ++ template ++ void push_with_ref(T** mpp, Writability w) { ++ push_impl(new REF_TYPE(mpp, w)); ++ } ++ ++public: ++ // When MetaspaceClosure::push(...) is called, pick the correct Ref subtype to handle it: ++ // ++ // MetaspaceClosure* it = ...; ++ // Klass* o = ...; it->push(&o); => MSORef ++ // Array* a1 = ...; it->push(&a1); => OtherArrayRef ++ // Array* a2 = ...; it->push(&a2); => MSOArrayRef ++ // Array* a3 = ...; it->push(&a3); => MSOPointerArrayRef ++ // Array*>* a4 = ...; it->push(&a4); => MSOPointerArrayRef ++ // Array* a5 = ...; it->push(&a5); => MSOPointerArrayRef ++ // ++ // Note that the following will fail to compile (to prevent you from adding new fields ++ // into the MetaspaceObj subtypes that cannot be properly copied by CDS): ++ // ++ // Hashtable* h = ...; it->push(&h); => Hashtable is not a subclass of MetaspaceObj ++ // Array* a6 = ...; it->push(&a6); => Hashtable is not a subclass of MetaspaceObj ++ // Array* a7 = ...; it->push(&a7); => int is not a subclass of MetaspaceObj ++ ++ template ++ void push(T** mpp, Writability w = _default) { ++ push_with_ref >(mpp, w); ++ } ++ ++ void push(Array** mpp, Writability w = _default) { ++ push_with_ref >(mpp, w); ++ } ++ ++ void push(Array** mpp, Writability w = _default) { ++ push_with_ref >(mpp, w); ++ } ++ ++ void push(Array** mpp, Writability w = _default) { ++ push_with_ref >(mpp, w); ++ } ++ ++ void push(Array** mpp, Writability w = _default) { ++ push_with_ref >(mpp, w); ++ } ++ ++ void push(Array** mpp, Writability w = _default) { ++ push_with_ref >(mpp, w); ++ } ++ ++ template ++ void push(Array** mpp, Writability w = _default) { ++ push_with_ref >(mpp, w); ++ } ++ ++ template ++ void push(Array** mpp, Writability w = _default) { ++ push_with_ref >(mpp, w); ++ } ++ ++#if 0 ++ // Enable this block if you're changing the push(...) methods, to test for types that should be ++ // disallowed. Each of the following "push" calls should result in a compile-time error. ++ void test_disallowed_types(MetaspaceClosure* it) { ++ Hashtable* h = NULL; ++ it->push(&h); ++ ++ Array*>* a6 = NULL; ++ it->push(&a6); ++ ++ Array* a7 = NULL; ++ it->push(&a7); ++ } ++#endif ++}; ++ ++// This is a special MetaspaceClosure that visits each unique MetaspaceObj once. ++class UniqueMetaspaceClosure : public MetaspaceClosure { ++ static const int INITIAL_TABLE_SIZE = 15889; ++ static const int MAX_TABLE_SIZE = 1000000; ++ ++ // Do not override. Returns true if we are discovering ref->obj() for the first time. ++ virtual bool do_ref(Ref* ref, bool read_only); ++ ++public: ++ // Gets called the first time we discover an object. ++ virtual bool do_unique_ref(Ref* ref, bool read_only) = 0; ++ UniqueMetaspaceClosure() : _has_been_visited(INITIAL_TABLE_SIZE) {} ++ ++private: ++ KVHashtable _has_been_visited; ++}; ++ ++#endif // SHARE_MEMORY_METASPACECLOSURE_HPP +diff --git a/hotspot/src/share/vm/memory/metaspaceShared.cpp b/hotspot/src/share/vm/memory/metaspaceShared.cpp +index 9857b7577..00fb9fe91 100644 +--- a/hotspot/src/share/vm/memory/metaspaceShared.cpp ++++ b/hotspot/src/share/vm/memory/metaspaceShared.cpp +@@ -38,6 +38,7 @@ + #include "memory/metaspaceShared.hpp" + #include "oops/objArrayOop.hpp" + #include "oops/oop.inline.hpp" ++#include "runtime/arguments.hpp" + #include "runtime/signature.hpp" + #include "runtime/vm_operations.hpp" + #include "runtime/vmThread.hpp" +@@ -47,14 +48,17 @@ + PRAGMA_FORMAT_MUTE_WARNINGS_FOR_GCC + + int MetaspaceShared::_max_alignment = 0; +- + ReservedSpace* MetaspaceShared::_shared_rs = NULL; ++char* MetaspaceShared::_requested_base_address; + + bool MetaspaceShared::_link_classes_made_progress; + bool MetaspaceShared::_check_classes_made_progress; + bool MetaspaceShared::_has_error_classes; + bool MetaspaceShared::_archive_loading_failed = false; + bool MetaspaceShared::_remapped_readwrite = false; ++void* MetaspaceShared::_shared_metaspace_static_bottom = NULL; ++void* MetaspaceShared::_shared_metaspace_dynamic_base = NULL; ++void* MetaspaceShared::_shared_metaspace_dynamic_top = NULL; + // Read/write a data stream for restoring/preserving metadata pointers and + // miscellaneous data from/to the shared archive file. + +@@ -843,7 +847,7 @@ int MetaspaceShared::preload_and_dump(const char * class_list_path, + + // Returns true if the class's status has changed + bool MetaspaceShared::try_link_class(InstanceKlass* ik, TRAPS) { +- assert(DumpSharedSpaces, "should only be called during dumping"); ++// assert(DumpSharedSpaces, "should only be called during dumping"); + if (ik->init_state() < InstanceKlass::linked) { + bool saved = BytecodeVerificationLocal; + if (!SharedClassUtil::is_shared_boot_class(ik)) { +@@ -862,6 +866,7 @@ bool MetaspaceShared::try_link_class(InstanceKlass* ik, TRAPS) { + tty->print_cr("Preload Warning: Verification failed for %s", + ik->external_name()); + CLEAR_PENDING_EXCEPTION; ++ SystemDictionaryShared::set_class_has_failed_verification(ik); + ik->set_in_error_state(); + _has_error_classes = true; + } +@@ -902,6 +907,11 @@ public: + FileMapInfo::assert_mark(tag == old_tag); + } + ++ void do_u4(u4* p) { ++ intptr_t obj = nextPtr(); ++ *p = (u4)(uintx(obj)); ++ } ++ + void do_region(u_char* start, size_t size) { + assert((intptr_t)start % sizeof(intptr_t) == 0, "bad alignment"); + assert(size % sizeof(intptr_t) == 0, "bad size"); +@@ -918,7 +928,10 @@ public: + + // Return true if given address is in the mapped shared space. + bool MetaspaceShared::is_in_shared_space(const void* p) { +- return UseSharedSpaces && FileMapInfo::current_info()->is_in_shared_space(p); ++ return UseSharedSpaces && ((FileMapInfo::current_info() != NULL && ++ FileMapInfo::current_info()->is_mapped() && ++ FileMapInfo::current_info()->is_in_shared_space(p)) || ++ is_shared_dynamic(p)); + } + + void MetaspaceShared::print_shared_spaces() { +@@ -927,19 +940,34 @@ void MetaspaceShared::print_shared_spaces() { + } + } + +- + // Map shared spaces at requested addresses and return if succeeded. + // Need to keep the bounds of the ro and rw space for the Metaspace::contains + // call, or is_in_shared_space. + bool MetaspaceShared::map_shared_spaces(FileMapInfo* mapinfo) { + size_t image_alignment = mapinfo->alignment(); + ++ mapinfo->set_is_mapped(false); ++ + #ifndef _WINDOWS + // Map in the shared memory and then map the regions on top of it. + // On Windows, don't map the memory here because it will cause the + // mappings of the regions to fail. + ReservedSpace shared_rs = mapinfo->reserve_shared_memory(); +- if (!shared_rs.is_reserved()) return false; ++ if (!shared_rs.is_reserved()) { ++ FileMapInfo::fail_continue("Unable to reserve shared memory"); ++ FLAG_SET_DEFAULT(UseSharedSpaces, false); ++ return false; ++ } ++ if (InfoDynamicCDS) { ++ dynamic_cds_log->print_cr("Reserved archive_space_rs [" INTPTR_FORMAT " - " INTPTR_FORMAT "] (" SIZE_FORMAT ") bytes", ++ p2i(shared_rs.base()), p2i(shared_rs.base() + shared_rs.size()), shared_rs.size()); ++ } ++ if (mapinfo->is_static()) { ++ _requested_base_address = shared_rs.base(); ++ } else { ++ _shared_metaspace_dynamic_base = shared_rs.base(); ++ _shared_metaspace_dynamic_top = shared_rs.base() + shared_rs.size(); ++ } + #endif + + assert(!DumpSharedSpaces, "Should not be called with DumpSharedSpaces"); +@@ -950,40 +978,79 @@ bool MetaspaceShared::map_shared_spaces(FileMapInfo* mapinfo) { + char* _mc_base = NULL; + + // Map each shared region +- if ((_ro_base = mapinfo->map_region(ro)) != NULL && +- mapinfo->verify_region_checksum(ro) && +- (_rw_base = mapinfo->map_region(rw)) != NULL && +- mapinfo->verify_region_checksum(rw) && +- (_md_base = mapinfo->map_region(md)) != NULL && +- mapinfo->verify_region_checksum(md) && +- (_mc_base = mapinfo->map_region(mc)) != NULL && +- mapinfo->verify_region_checksum(mc) && +- (image_alignment == (size_t)max_alignment()) && +- mapinfo->validate_classpath_entry_table()) { +- // Success (no need to do anything) +- return true; ++ if (mapinfo->is_static()) { ++ if ((_ro_base = mapinfo->map_region(ro)) != NULL && ++ mapinfo->verify_region_checksum(ro) && ++ (_rw_base = mapinfo->map_region(rw)) != NULL && ++ mapinfo->verify_region_checksum(rw) && ++ (_md_base = mapinfo->map_region(md)) != NULL && ++ mapinfo->verify_region_checksum(md) && ++ (_mc_base = mapinfo->map_region(mc)) != NULL && ++ mapinfo->verify_region_checksum(mc) && ++ (image_alignment == (size_t)max_alignment()) && ++ mapinfo->validate_classpath_entry_table()) { ++ mapinfo->set_is_mapped(true); ++ return true; ++ } + } else { +- // If there was a failure in mapping any of the spaces, unmap the ones +- // that succeeded +- if (_ro_base != NULL) mapinfo->unmap_region(ro); +- if (_rw_base != NULL) mapinfo->unmap_region(rw); +- if (_md_base != NULL) mapinfo->unmap_region(md); +- if (_mc_base != NULL) mapinfo->unmap_region(mc); ++ if ((_rw_base = mapinfo->map_region(d_rw)) != NULL && ++ mapinfo->verify_region_checksum(d_rw) && ++ (_ro_base = mapinfo->map_region(d_ro)) != NULL && ++ mapinfo->verify_region_checksum(d_ro) && ++ (image_alignment == (size_t)max_alignment())) { ++ mapinfo->set_is_mapped(true); ++ return true; ++ } ++ } ++ ++ // If there was a failure in mapping any of the spaces, unmap the ones ++ // that succeeded ++ if (_ro_base != NULL) mapinfo->unmap_region(ro); ++ if (_rw_base != NULL) mapinfo->unmap_region(rw); ++ if (_md_base != NULL) mapinfo->unmap_region(md); ++ if (_mc_base != NULL) mapinfo->unmap_region(mc); + #ifndef _WINDOWS +- // Release the entire mapped region +- shared_rs.release(); ++ // Release the entire mapped region ++ shared_rs.release(); + #endif +- // If -Xshare:on is specified, print out the error message and exit VM, +- // otherwise, set UseSharedSpaces to false and continue. +- if (RequireSharedSpaces || PrintSharedArchiveAndExit) { +- vm_exit_during_initialization("Unable to use shared archive.", "Failed map_region for using -Xshare:on."); +- } else { +- FLAG_SET_DEFAULT(UseSharedSpaces, false); +- } +- return false; ++ // If -Xshare:on is specified, print out the error message and exit VM, ++ // otherwise, set UseSharedSpaces to false and continue. ++ if (RequireSharedSpaces || PrintSharedArchiveAndExit) { ++ vm_exit_during_initialization("Unable to use shared archive.", "Failed map_region for using -Xshare:on."); ++ } else { ++ FLAG_SET_DEFAULT(UseSharedSpaces, false); + } ++ return false; + } + ++void** MetaspaceShared::_vtbl_list = NULL; ++ ++intptr_t* MetaspaceShared::get_archived_vtable(MetaspaceObj::Type msotype, address obj) { ++ Arguments::assert_is_dumping_archive(); ++ switch (msotype) { ++ case MetaspaceObj::SymbolType: ++ case MetaspaceObj::TypeArrayU1Type: ++ case MetaspaceObj::TypeArrayU2Type: ++ case MetaspaceObj::TypeArrayU4Type: ++ case MetaspaceObj::TypeArrayU8Type: ++ case MetaspaceObj::TypeArrayOtherType: ++ case MetaspaceObj::ConstMethodType: ++ case MetaspaceObj::ConstantPoolCacheType: ++ case MetaspaceObj::AnnotationType: ++ case MetaspaceObj::MethodCountersType: ++ // These have no vtables. ++ break; ++ case MetaspaceObj::MethodDataType: ++ // We don't archive MethodData <-- should have been removed in removed_unsharable_info ++ ShouldNotReachHere(); ++ break; ++ default: ++ int vtable_offset = MetaspaceShared::vtbl_list_size * sizeof(void*) + sizeof(intptr_t); ++ char* vtable_start = (char*)_vtbl_list + vtable_offset; ++ return (intptr_t*)find_matching_vtbl_ptr(_vtbl_list, (void*)vtable_start, obj); ++ } ++ return NULL; ++} + // Read the miscellaneous data from the shared file, and + // serialize it out to its various destinations. + +@@ -996,6 +1063,7 @@ void MetaspaceShared::initialize_shared_spaces() { + // for Klass objects. They get filled in later. + + void** vtbl_list = (void**)buffer; ++ _vtbl_list = vtbl_list; + buffer += MetaspaceShared::vtbl_list_size * sizeof(void*); + Universe::init_self_patching_vtbl_list(vtbl_list, vtbl_list_size); + +@@ -1079,6 +1147,15 @@ void MetaspaceShared::initialize_shared_spaces() { + // Close the mapinfo file + mapinfo->close(); + ++ FileMapInfo *dynamic_mapinfo = FileMapInfo::dynamic_info(); ++ if (dynamic_mapinfo != NULL) { ++ intptr_t* buffer = (intptr_t*)dynamic_mapinfo->serialized_data(); ++ ReadClosure rc(&buffer); ++ SymbolTable::serialize_shared_table_header(&rc); ++ SystemDictionaryShared::serialize_dictionary_headers(&rc); ++ dynamic_mapinfo->close(); ++ } ++ + if (PrintSharedArchiveAndExit) { + if (PrintSharedDictionary) { + tty->print_cr("\nShared classes:\n"); +@@ -1104,6 +1181,11 @@ bool MetaspaceShared::remap_shared_readonly_as_readwrite() { + if (!mapinfo->remap_shared_readonly_as_readwrite()) { + return false; + } ++ ++ mapinfo = FileMapInfo::dynamic_info(); ++ if (mapinfo != NULL && !mapinfo->remap_shared_readonly_as_readwrite()) { ++ return false; ++ } + _remapped_readwrite = true; + } + return true; +diff --git a/hotspot/src/share/vm/memory/metaspaceShared.hpp b/hotspot/src/share/vm/memory/metaspaceShared.hpp +index d58ebecb2..a9dadfbb9 100644 +--- a/hotspot/src/share/vm/memory/metaspaceShared.hpp ++++ b/hotspot/src/share/vm/memory/metaspaceShared.hpp +@@ -28,6 +28,7 @@ + #include "memory/memRegion.hpp" + #include "runtime/virtualspace.hpp" + #include "utilities/exceptions.hpp" ++#include "utilities/growableArray.hpp" + #include "utilities/macros.hpp" + + #define LargeSharedArchiveSize (300*M) +@@ -44,6 +45,7 @@ + (uintx)(type ## SharedArchiveSize * region ## RegionPercentage) : Shared ## region ## Size + + class FileMapInfo; ++class SerializeClosure; + + // Class Data Sharing Support + class MetaspaceShared : AllStatic { +@@ -56,6 +58,11 @@ class MetaspaceShared : AllStatic { + static bool _has_error_classes; + static bool _archive_loading_failed; + static bool _remapped_readwrite; ++ static void* _shared_metaspace_static_bottom; ++ static void** _vtbl_list; // Remember the vtable start address for dynamic dump metadata ++ static char* _requested_base_address; ++ static void* _shared_metaspace_dynamic_base; ++ static void* _shared_metaspace_dynamic_top; + public: + enum { + vtbl_list_size = 17, // number of entries in the shared space vtable list. +@@ -71,11 +78,20 @@ class MetaspaceShared : AllStatic { + }; + + enum { +- ro = 0, // read-only shared space in the heap +- rw = 1, // read-write shared space in the heap +- md = 2, // miscellaneous data for initializing tables, etc. +- mc = 3, // miscellaneous code - vtable replacement. +- n_regions = 4 ++ // core archive spaces ++ ro = 0, // read-only shared space in the heap ++ rw = 1, // read-write shared space in the heap ++ md = 2, // miscellaneous data for initializing tables, etc. (static only) ++ mc = 3, // miscellaneous code - vtable replacement. (static only) ++ n_regions = 4 // total number of static regions ++ }; ++ ++ enum { ++ // core dynamic archive spaces ++ d_rw = 0, // read-write shared space in the heap ++ d_ro = 1, // read-only shared space in the heap ++ d_bm = 2, // relocation bitmaps (freed after file mapping is finished) ++ d_n_regions = 2 // d_rw and d_ro + }; + + // Accessor functions to save shared space created for metadata, which has +@@ -108,6 +124,28 @@ class MetaspaceShared : AllStatic { + _archive_loading_failed = true; + } + static bool map_shared_spaces(FileMapInfo* mapinfo) NOT_CDS_RETURN_(false); ++ ++ static bool is_shared_dynamic(const void* p) { ++ return p < _shared_metaspace_dynamic_top && p >= _shared_metaspace_dynamic_base; ++ } ++ ++ // This is the base address as specified by -XX:SharedBaseAddress during -Xshare:dump. ++ // Both the base/top archives are written using this as their base address. ++ // ++ // During static dump: _requested_base_address == SharedBaseAddress. ++ // ++ // During dynamic dump: _requested_base_address is not always the same as SharedBaseAddress: ++ // - SharedBaseAddress is used for *reading the base archive*. I.e., CompactHashtable uses ++ // it to convert offsets to pointers to Symbols in the base archive. ++ // The base archive may be mapped to an OS-selected address due to ASLR. E.g., ++ // you may have SharedBaseAddress == 0x00ff123400000000. ++ // - _requested_base_address is used for *writing the output archive*. It's usually ++ // 0x800000000 (unless it was set by -XX:SharedBaseAddress during -Xshare:dump). ++ static char* requested_base_address() { ++ return _requested_base_address; ++ } ++ ++ static intptr_t* get_archived_vtable(MetaspaceObj::Type msotype, address obj); + static void initialize_shared_spaces() NOT_CDS_RETURN; + + // Return true if given address is in the mapped shared space. +@@ -138,5 +176,8 @@ class MetaspaceShared : AllStatic { + + static int count_class(const char* classlist_file); + static void estimate_regions_size() NOT_CDS_RETURN; ++ ++ static void set_shared_metaspace_static_bottom(void* bottom) { _shared_metaspace_static_bottom = bottom; } ++ static void* shared_metaspace_static_bottom() { return _shared_metaspace_static_bottom; } + }; + #endif // SHARE_VM_MEMORY_METASPACE_SHARED_HPP +diff --git a/hotspot/src/share/vm/oops/annotations.cpp b/hotspot/src/share/vm/oops/annotations.cpp +index 776b8606b..6b3080f17 100644 +--- a/hotspot/src/share/vm/oops/annotations.cpp ++++ b/hotspot/src/share/vm/oops/annotations.cpp +@@ -27,6 +27,7 @@ + #include "memory/heapInspection.hpp" + #include "memory/metadataFactory.hpp" + #include "memory/oopFactory.hpp" ++#include "memory/metaspaceClosure.hpp" + #include "oops/annotations.hpp" + #include "oops/instanceKlass.hpp" + #include "utilities/ostream.hpp" +@@ -36,6 +37,17 @@ Annotations* Annotations::allocate(ClassLoaderData* loader_data, TRAPS) { + return new (loader_data, size(), true, MetaspaceObj::AnnotationType, THREAD) Annotations(); + } + ++void Annotations::metaspace_pointers_do(MetaspaceClosure* it) { ++ if (TraceDynamicCDS) { ++ dynamic_cds_log->print_cr("Iter(Annotations): %p", this); ++ } ++ ++ it->push(&_class_annotations); ++ it->push(&_fields_annotations); ++ it->push(&_class_type_annotations); ++ it->push(&_fields_type_annotations); // FIXME: need a test case where _fields_type_annotations != NULL ++} ++ + // helper + void Annotations::free_contents(ClassLoaderData* loader_data, Array* p) { + if (p != NULL) { +diff --git a/hotspot/src/share/vm/oops/annotations.hpp b/hotspot/src/share/vm/oops/annotations.hpp +index ad405a8db..d1f7bc71b 100644 +--- a/hotspot/src/share/vm/oops/annotations.hpp ++++ b/hotspot/src/share/vm/oops/annotations.hpp +@@ -35,6 +35,7 @@ + class ClassLoaderData; + class outputStream; + class KlassSizeStats; ++class MetaspaceClosure; + + typedef Array AnnotationArray; + +@@ -54,6 +55,8 @@ class Annotations: public MetaspaceObj { + Array* _fields_type_annotations; + + public: ++ void metaspace_pointers_do(MetaspaceClosure* it); ++ + // Allocate instance of this class + static Annotations* allocate(ClassLoaderData* loader_data, TRAPS); + +@@ -61,8 +64,14 @@ class Annotations: public MetaspaceObj { + void deallocate_contents(ClassLoaderData* loader_data); + DEBUG_ONLY(bool on_stack() { return false; }) // for template + ++ // Annotations should be stored in the read-only region of CDS archive. ++ static bool is_read_only_by_default() { return true; } ++ ++ MetaspaceObj::Type type() const { return AnnotationType; } ++ + // Sizing (in words) + static int size() { return sizeof(Annotations) / wordSize; } ++ + #if INCLUDE_SERVICES + void collect_statistics(KlassSizeStats *sz) const; + #endif +diff --git a/hotspot/src/share/vm/oops/arrayKlass.cpp b/hotspot/src/share/vm/oops/arrayKlass.cpp +index 129bce63d..9009d6972 100644 +--- a/hotspot/src/share/vm/oops/arrayKlass.cpp ++++ b/hotspot/src/share/vm/oops/arrayKlass.cpp +@@ -30,6 +30,7 @@ + #include "jvmtifiles/jvmti.h" + #include "memory/gcLocker.hpp" + #include "memory/universe.inline.hpp" ++#include "memory/metaspaceClosure.hpp" + #include "oops/arrayKlass.hpp" + #include "oops/arrayOop.hpp" + #include "oops/instanceKlass.hpp" +@@ -64,6 +65,19 @@ oop ArrayKlass::multi_allocate(int rank, jint* sizes, TRAPS) { + return NULL; + } + ++void ArrayKlass::metaspace_pointers_do(MetaspaceClosure* it) { ++ Klass::metaspace_pointers_do(it); ++ ++ if (TraceDynamicCDS) { ++ ResourceMark rm; ++ dynamic_cds_log->print_cr("Iter(InstanceKlass): %p (%s)", this, external_name()); ++ } ++ ++ // need to cast away volatile ++ it->push((Klass**)&_higher_dimension); ++ it->push((Klass**)&_lower_dimension); ++} ++ + // find field according to JVM spec 5.4.3.2, returns the klass in which the field is defined + Klass* ArrayKlass::find_field(Symbol* name, Symbol* sig, fieldDescriptor* fd) const { + // There are no fields in an array klass but look to the super class (Object) +@@ -203,6 +217,14 @@ void ArrayKlass::remove_unshareable_info() { + _higher_dimension = NULL; + } + ++void ArrayKlass::remove_java_mirror() { ++ Klass::remove_java_mirror(); ++ if (_higher_dimension != NULL) { ++ ArrayKlass *ak = ArrayKlass::cast(higher_dimension()); ++ ak->remove_java_mirror(); ++ } ++} ++ + void ArrayKlass::restore_unshareable_info(ClassLoaderData* loader_data, Handle protection_domain, TRAPS) { + assert(loader_data == ClassLoaderData::the_null_class_loader_data(), "array classes belong to null loader"); + Klass::restore_unshareable_info(loader_data, protection_domain, CHECK); +diff --git a/hotspot/src/share/vm/oops/arrayKlass.hpp b/hotspot/src/share/vm/oops/arrayKlass.hpp +index d28ece376..9b6fd9e0b 100644 +--- a/hotspot/src/share/vm/oops/arrayKlass.hpp ++++ b/hotspot/src/share/vm/oops/arrayKlass.hpp +@@ -100,7 +100,7 @@ class ArrayKlass: public Klass { + + GrowableArray* compute_secondary_supers(int num_extra_slots); + bool compute_is_subtype_of(Klass* k); +- ++ virtual void metaspace_pointers_do(MetaspaceClosure* it); + // Sizing + static int header_size() { return sizeof(ArrayKlass)/HeapWordSize; } + static int static_size(int header_size); +@@ -141,6 +141,7 @@ class ArrayKlass: public Klass { + + // CDS support - remove and restore oops from metadata. Oops are not shared. + virtual void remove_unshareable_info(); ++ virtual void remove_java_mirror(); + virtual void restore_unshareable_info(ClassLoaderData* loader_data, Handle protection_domain, TRAPS); + + // Printing +diff --git a/hotspot/src/share/vm/oops/constMethod.cpp b/hotspot/src/share/vm/oops/constMethod.cpp +index a496149df..fc7d74512 100644 +--- a/hotspot/src/share/vm/oops/constMethod.cpp ++++ b/hotspot/src/share/vm/oops/constMethod.cpp +@@ -26,6 +26,7 @@ + #include "interpreter/interpreter.hpp" + #include "memory/gcLocker.hpp" + #include "memory/heapInspection.hpp" ++#include "memory/metaspaceClosure.hpp" + #include "memory/metadataFactory.hpp" + #include "oops/constMethod.hpp" + #include "oops/method.hpp" +@@ -148,6 +149,31 @@ Method* ConstMethod::method() const { + return _constants->pool_holder()->method_with_idnum(_method_idnum); + } + ++void ConstMethod::metaspace_pointers_do(MetaspaceClosure* it) { ++ if (TraceDynamicCDS) { ++ dynamic_cds_log->print_cr("Iter(ConstMethod): %p", this); ++ } ++ ++ if (!method()->method_holder()->is_rewritten()) { ++ it->push(&_constants, MetaspaceClosure::_writable); ++ } else { ++ it->push(&_constants); ++ } ++ it->push(&_stackmap_data); ++ if (has_method_annotations()) { ++ it->push(method_annotations_addr()); ++ } ++ if (has_parameter_annotations()) { ++ it->push(parameter_annotations_addr()); ++ } ++ if (has_type_annotations()) { ++ it->push(type_annotations_addr()); ++ } ++ if (has_default_annotations()) { ++ it->push(default_annotations_addr()); ++ } ++} ++ + // linenumber table - note that length is unknown until decompression, + // see class CompressedLineNumberReadStream. + +diff --git a/hotspot/src/share/vm/oops/constMethod.hpp b/hotspot/src/share/vm/oops/constMethod.hpp +index 0caa3a26f..20cff631e 100644 +--- a/hotspot/src/share/vm/oops/constMethod.hpp ++++ b/hotspot/src/share/vm/oops/constMethod.hpp +@@ -129,7 +129,7 @@ class MethodParametersElement VALUE_OBJ_CLASS_SPEC { + }; + + class KlassSizeStats; +- ++class MetaspaceClosure; + // Class to collect the sizes of ConstMethod inline tables + #define INLINE_TABLES_DO(do_element) \ + do_element(localvariable_table_length) \ +@@ -344,6 +344,12 @@ public: + // Size needed + static int size(int code_size, InlineTableSizes* sizes); + ++ // ConstMethods should be stored in the read-only region of CDS archive. ++ static bool is_read_only_by_default() { return true; } ++ ++ void metaspace_pointers_do(MetaspaceClosure* it); ++ MetaspaceObj::Type type() const { return ConstMethodType; } ++ + int size() const { return _constMethod_size;} + void set_constMethod_size(int size) { _constMethod_size = size; } + #if INCLUDE_SERVICES +diff --git a/hotspot/src/share/vm/oops/constantPool.cpp b/hotspot/src/share/vm/oops/constantPool.cpp +index b6158e4e9..f8078bffa 100644 +--- a/hotspot/src/share/vm/oops/constantPool.cpp ++++ b/hotspot/src/share/vm/oops/constantPool.cpp +@@ -23,16 +23,19 @@ + */ + + #include "precompiled.hpp" ++#include "cds/archiveUtils.hpp" + #include "classfile/classLoaderData.hpp" + #include "classfile/javaClasses.hpp" + #include "classfile/metadataOnStackMark.hpp" + #include "classfile/symbolTable.hpp" + #include "classfile/systemDictionary.hpp" ++#include "classfile/systemDictionaryShared.hpp" + #include "classfile/vmSymbols.hpp" + #include "interpreter/linkResolver.hpp" + #include "memory/heapInspection.hpp" + #include "memory/metadataFactory.hpp" + #include "memory/oopFactory.hpp" ++#include "memory/metaspaceClosure.hpp" + #include "oops/constantPool.hpp" + #include "oops/instanceKlass.hpp" + #include "oops/objArrayKlass.hpp" +@@ -153,6 +156,52 @@ void ConstantPool::initialize_resolved_references(ClassLoaderData* loader_data, + } + } + ++void ConstantPool::metaspace_pointers_do(MetaspaceClosure* it) { ++ if (TraceDynamicCDS) { ++ dynamic_cds_log->print_cr("Iter(ConstantPool): %p", this); ++ } ++ ++ it->push(&_tags, MetaspaceClosure::_writable); ++ it->push(&_cache); ++ it->push(&_pool_holder); ++ it->push(&_operands); ++ it->push(&_reference_map, MetaspaceClosure::_writable); ++ ++ for (int i = 0; i < length(); i++) { ++ // About resolved klasses, we should be careful because of data structure difference ++ // between jdk8 and jdk17. ++ constantTag ctag = tag_at(i); ++ if (ctag.is_string() || ctag.is_utf8() || ctag.is_replaced_symbol()) { ++ it->push(symbol_at_addr(i)); ++ } else if (ctag.is_klass()) { ++ it->push((Klass**)obj_at_addr_raw(i)); ++ } ++ } ++} ++ ++// We replace data in base() by normal symbol in two conditions: ++// 1. resolved klass ++// The value is klass ptr, in remove_unshareable_info we need replace klass ptr by klass‘s ++// name. The klass may be excluded, hence klass ptr is NULL and lost klass'name ++// at the end. Replace excluded klasses by names. ++// 2. unresolved klass ++// The value is symbol ptr | 1, the data is unparseable pushed in MetaspaceClosure, we need ++// replace the data by a normal symbol ptr at first, and store value symbol ptr | 1 at last. ++void ConstantPool::symbol_replace_excluded_klass() { ++ for (int i = 0; i < length(); i++) { ++ constantTag ctag = tag_at(i); ++ if (ctag.is_klass()) { ++ Klass* klass = resolved_klass_at(i); ++ if (SystemDictionaryShared::is_excluded_class((InstanceKlass*)klass)) { ++ replaced_symbol_at_put(i, klass->name()); ++ } ++ } else if (ctag.is_unresolved_klass()) { ++ CPSlot entry = slot_at(i); ++ replaced_symbol_at_put(i, entry.get_symbol()); ++ } ++ } ++} ++ + // CDS support. Create a new resolved_references array. + void ConstantPool::restore_unshareable_info(TRAPS) { + +@@ -180,18 +229,30 @@ void ConstantPool::restore_unshareable_info(TRAPS) { + } + + void ConstantPool::remove_unshareable_info() { +- if (UseAppCDS) { +- if (cache() != NULL) { +- cache()->reset(); ++ if (cache() != NULL) { ++ cache()->remove_unshareable_info(); ++ } ++ ++ // Shared ConstantPools are in the RO region, so the _flags cannot be modified. ++ // The _on_stack flag is used to prevent ConstantPools from deallocation during ++ // class redefinition. Since shared ConstantPools cannot be deallocated anyway, ++ // we always set _on_stack to true to avoid having to change _flags during runtime. ++ _flags |= _on_stack; ++ int num_klasses = 0; ++ for (int index = 1; index < length(); index++) { // Index 0 is unused ++ if (tag_at(index).is_unresolved_klass_in_error()) { ++ tag_at_put(index, JVM_CONSTANT_UnresolvedClass); ++ } else if (tag_at(index).is_method_handle_in_error()) { ++ tag_at_put(index, JVM_CONSTANT_MethodHandle); ++ } else if (tag_at(index).is_method_type_in_error()) { ++ tag_at_put(index, JVM_CONSTANT_MethodType); + } +- for (int i = 0; i < _length; i++) { +- if (tag_at(i).is_klass()) { +- Klass* resolvedKlass = resolved_klass_at(i); +- ResourceMark rm; +- char* name = resolvedKlass->name()->as_C_string(); +- int len = strlen(name); +- unresolved_klass_at_put(i, resolvedKlass->name()); +- } ++ ++ if (tag_at(index).is_klass()) { ++ Klass* resolved_Klass = resolved_klass_at(index); ++ unresolved_klass_at_put(index, resolved_Klass->name()); ++ } else if (tag_at(index).is_replaced_symbol()) { ++ unresolved_klass_at_put(index, *symbol_at_addr(index)); + } + } + // Resolved references are not in the shared archive. +@@ -519,8 +580,14 @@ Klass* ConstantPool::klass_ref_at(int which, TRAPS) { + + + Symbol* ConstantPool::klass_name_at(int which) const { +- assert(tag_at(which).is_unresolved_klass() || tag_at(which).is_klass(), +- "Corrupted constant pool"); ++ // Dynamic CDS dump need call here in verify, release version no need do it. ++#ifndef PRODUCT ++ assert(tag_at(which).is_unresolved_klass() || tag_at(which).is_klass() || ++ tag_at(which).is_replaced_symbol(), "Corrupted constant pool"); ++ if (tag_at(which).is_replaced_symbol()) { ++ return *symbol_at_addr(which); ++ } ++#endif + // A resolved constantPool entry will contain a Klass*, otherwise a Symbol*. + // It is not safe to rely on the tag bit's here, since we don't have a lock, and the entry and + // tag is not updated atomicly. +diff --git a/hotspot/src/share/vm/oops/constantPool.hpp b/hotspot/src/share/vm/oops/constantPool.hpp +index ec111df04..b5b4db38b 100644 +--- a/hotspot/src/share/vm/oops/constantPool.hpp ++++ b/hotspot/src/share/vm/oops/constantPool.hpp +@@ -231,6 +231,9 @@ class ConstantPool : public Metadata { + return cache()->entry_at(cp_cache_index); + } + ++ virtual void metaspace_pointers_do(MetaspaceClosure* it); ++ void symbol_replace_excluded_klass(); ++ virtual MetaspaceObj::Type type() const { return ConstantPoolType; } + // Assembly code support + static int tags_offset_in_bytes() { return offset_of(ConstantPool, _tags); } + static int cache_offset_in_bytes() { return offset_of(ConstantPool, _cache); } +@@ -315,6 +318,11 @@ class ConstantPool : public Metadata { + *symbol_at_addr(which) = s; + } + ++ void replaced_symbol_at_put(int which, Symbol*s) { ++ tag_at_put(which, JVM_CONSTANT_ReplacedSymbol); ++ *symbol_at_addr(which) = s; ++ } ++ + void string_at_put(int which, int obj_index, oop str) { + resolved_references()->obj_at_put(obj_index, str); + } +@@ -747,6 +755,10 @@ class ConstantPool : public Metadata { + void collect_statistics(KlassSizeStats *sz) const; + #endif + ++ // ConstantPools should be stored in the read-only region of CDS archive. ++ // But the vtable will be patched in JDK8, so it must be writable. ++ static bool is_read_only_by_default() { return false; } ++ + friend class ClassFileParser; + friend class SystemDictionary; + +diff --git a/hotspot/src/share/vm/oops/cpCache.cpp b/hotspot/src/share/vm/oops/cpCache.cpp +index ebcf3d6a9..51f5397b8 100644 +--- a/hotspot/src/share/vm/oops/cpCache.cpp ++++ b/hotspot/src/share/vm/oops/cpCache.cpp +@@ -24,14 +24,17 @@ + + #include "precompiled.hpp" + #include "gc_implementation/shared/markSweep.inline.hpp" ++#include "interpreter/bytecodeStream.hpp" + #include "interpreter/interpreter.hpp" + #include "interpreter/rewriter.hpp" + #include "memory/universe.inline.hpp" ++#include "memory/metaspaceClosure.hpp" + #include "oops/cpCache.hpp" + #include "oops/objArrayOop.hpp" + #include "oops/oop.inline.hpp" + #include "prims/jvmtiRedefineClassesTrace.hpp" + #include "prims/methodHandles.hpp" ++#include "runtime/arguments.hpp" + #include "runtime/handles.inline.hpp" + #include "runtime/orderAccess.inline.hpp" + #include "utilities/macros.hpp" +@@ -602,6 +605,72 @@ void ConstantPoolCache::initialize(const intArray& inverse_index_map, + } + } + ++void ConstantPoolCache::metaspace_pointers_do(MetaspaceClosure* it) { ++ if (TraceDynamicCDS) { ++ dynamic_cds_log->print_cr("Iter(ConstantPoolCache): %p", this); ++ } ++ it->push(&_constant_pool); ++ // it->push(&_reference_map); ++} ++ ++void ConstantPoolCache::remove_unshareable_info() { ++ walk_entries_for_initialization(/*check_only = */ false); ++} ++ ++void ConstantPoolCache::walk_entries_for_initialization(bool check_only) { ++ Arguments::assert_is_dumping_archive(); ++ // When dumping the archive, we want to clean up the ConstantPoolCache ++ // to remove any effect of linking due to the execution of Java code -- ++ // each ConstantPoolCacheEntry will have the same contents as if ++ // ConstantPoolCache::initialize has just returned: ++ // ++ // - We keep the ConstantPoolCache::constant_pool_index() bits for all entries. ++ // - We keep the "f2" field for entries used by invokedynamic and invokehandle ++ // - All other bits in the entries are cleared to zero. ++ ResourceMark rm; ++ ++ InstanceKlass* ik = constant_pool()->pool_holder(); ++ bool* f2_used = NEW_RESOURCE_ARRAY(bool, length()); ++ memset(f2_used, 0, sizeof(bool) * length()); ++ ++ Thread* current = Thread::current(); ++ ++ // Find all the slots that we need to preserve f2 ++ for (int i = 0; i < ik->methods()->length(); i++) { ++ Method* m = ik->methods()->at(i); ++ RawBytecodeStream bcs(methodHandle(current, m)); ++ while (!bcs.is_last_bytecode()) { ++ Bytecodes::Code opcode = bcs.raw_next(); ++ switch (opcode) { ++ case Bytecodes::_invokedynamic: { ++ int index = Bytes::get_native_u4(bcs.bcp() + 1); ++ int cp_cache_index = constant_pool()->invokedynamic_cp_cache_index(index); ++ f2_used[cp_cache_index] = 1; ++ } ++ break; ++ case Bytecodes::_invokehandle: { ++ int cp_cache_index = Bytes::get_native_u2(bcs.bcp() + 1); ++ f2_used[cp_cache_index] = 1; ++ } ++ break; ++ default: ++ break; ++ } ++ } ++ } ++ ++ if (check_only) { ++ DEBUG_ONLY( ++ for (int i=0; iverify_just_initialized(f2_used[i]); ++ }) ++ } else { ++ for (int i=0; ireinitialize(f2_used[i]); ++ } ++ } ++} ++ + #if INCLUDE_JVMTI + // RedefineClasses() API support: + // If any entry of this ConstantPoolCache points to any of +diff --git a/hotspot/src/share/vm/oops/cpCache.hpp b/hotspot/src/share/vm/oops/cpCache.hpp +index 48f9bbd27..cb2fa43d6 100644 +--- a/hotspot/src/share/vm/oops/cpCache.hpp ++++ b/hotspot/src/share/vm/oops/cpCache.hpp +@@ -124,6 +124,7 @@ class PSPromotionManager; + // source code. The _indices field with the bytecode must be written last. + + class CallInfo; ++class MetaspaceClosure; + + class ConstantPoolCacheEntry VALUE_OBJ_CLASS_SPEC { + friend class VMStructs; +@@ -397,6 +398,24 @@ class ConstantPoolCacheEntry VALUE_OBJ_CLASS_SPEC { + // When shifting flags as a 32-bit int, make sure we don't need an extra mask for tos_state: + assert((((u4)-1 >> tos_state_shift) & ~tos_state_mask) == 0, "no need for tos_state mask"); + } ++ ++ void reinitialize(bool f2_used) { ++ _indices &= cp_index_mask; ++ _f1 = NULL; ++ _flags = 0; ++ if (!f2_used) { ++ _f2 = 0; ++ } ++ } ++ ++ void verify_just_initialized(bool f2_used) { ++ assert((_indices & (~cp_index_mask)) == 0, "sanity"); ++ assert(_f1 == NULL, "sanity"); ++ assert(_flags == 0, "sanity"); ++ if (!f2_used) { ++ assert(_f2 == 0, "sanity"); ++ } ++} + }; + + +@@ -468,6 +487,10 @@ class ConstantPoolCache: public MetaspaceObj { + return base() + i; + } + ++ void metaspace_pointers_do(MetaspaceClosure* it); ++ void remove_unshareable_info(); ++ void walk_entries_for_initialization(bool check_only); ++ MetaspaceObj::Type type() const { return ConstantPoolCacheType; } + // Code generation + static ByteSize base_offset() { return in_ByteSize(sizeof(ConstantPoolCache)); } + static ByteSize entry_offset(int raw_index) { +@@ -488,7 +511,7 @@ class ConstantPoolCache: public MetaspaceObj { + #endif // INCLUDE_JVMTI + + void reset(); +- ++ + // Deallocate - no fields to deallocate + DEBUG_ONLY(bool on_stack() { return false; }) + void deallocate_contents(ClassLoaderData* data) {} +diff --git a/hotspot/src/share/vm/oops/instanceKlass.cpp b/hotspot/src/share/vm/oops/instanceKlass.cpp +index 367c9a09d..0d1b1a8d0 100644 +--- a/hotspot/src/share/vm/oops/instanceKlass.cpp ++++ b/hotspot/src/share/vm/oops/instanceKlass.cpp +@@ -39,6 +39,7 @@ + #include "memory/iterator.inline.hpp" + #include "memory/metadataFactory.hpp" + #include "memory/oopFactory.hpp" ++#include "memory/metaspaceClosure.hpp" + #include "oops/fieldStreams.hpp" + #include "oops/instanceClassLoaderKlass.hpp" + #include "oops/instanceKlass.hpp" +@@ -53,6 +54,7 @@ + #include "prims/jvmtiRedefineClasses.hpp" + #include "prims/jvmtiThreadState.hpp" + #include "prims/methodComparator.hpp" ++#include "runtime/arguments.hpp" + #include "runtime/fieldDescriptor.hpp" + #include "runtime/handles.inline.hpp" + #include "runtime/javaCalls.hpp" +@@ -463,12 +465,73 @@ void InstanceKlass::deallocate_contents(ClassLoaderData* loader_data) { + MetadataFactory::free_metadata(loader_data, annotations()); + } + set_annotations(NULL); ++ ++ if (Arguments::is_dumping_archive()) { ++ SystemDictionaryShared::remove_dumptime_info(this); ++ } + } + + bool InstanceKlass::should_be_initialized() const { + return !is_initialized(); + } + ++void InstanceKlass::metaspace_pointers_do(MetaspaceClosure* it) { ++ Klass::metaspace_pointers_do(it); ++ ++ if (TraceDynamicCDS) { ++ ResourceMark rm; ++ dynamic_cds_log->print_cr("Iter(InstanceKlass): %p (%s)", this, external_name()); ++ } ++ ++ it->push(&_annotations); ++ it->push((Klass**)&_array_klasses); ++ if (!is_rewritten()) { ++ it->push(&_constants, MetaspaceClosure::_writable); ++ } else { ++ it->push(&_constants); ++ } ++ it->push(&_inner_classes); ++#if INCLUDE_JVMTI ++ it->push(&_previous_versions); ++#endif ++ it->push(&_array_name); ++ it->push(&_methods); ++ it->push(&_default_methods); ++ it->push(&_local_interfaces); ++ it->push(&_transitive_interfaces); ++ it->push(&_method_ordering); ++ if (!is_rewritten()) { ++ it->push(&_default_vtable_indices, MetaspaceClosure::_writable); ++ } else { ++ it->push(&_default_vtable_indices); ++ } ++ ++ // _fields might be written into by Rewriter::scan_method() -> fd.set_has_initialized_final_update() ++ it->push(&_fields, MetaspaceClosure::_writable); ++ ++ if (itable_length() > 0) { ++ itableOffsetEntry* ioe = (itableOffsetEntry*)start_of_itable(); ++ int method_table_offset_in_words = ioe->offset()/wordSize; ++ int nof_interfaces = (method_table_offset_in_words - itable_offset_in_words()) ++ / itableOffsetEntry::size(); ++ ++ for (int i = 0; i < nof_interfaces; i ++, ioe ++) { ++ if (ioe->interface_klass() != NULL) { ++ it->push(ioe->interface_klass_addr()); ++ itableMethodEntry* ime = ioe->first_method_entry(this); ++ int n = klassItable::method_count_for_interface(ioe->interface_klass()); ++ for (int index = 0; index < n; index ++) { ++ it->push(ime[index].method_addr()); ++ } ++ } ++ } ++ } ++ ++ // it->push(&_nest_members); ++ // it->push(&_permitted_subclasses); ++ // it->push(&_record_components); ++} ++ + klassVtable* InstanceKlass::vtable() const { + return new klassVtable(this, start_of_vtable(), vtable_length() / vtableEntry::size()); + } +@@ -765,6 +828,28 @@ bool InstanceKlass::link_class_impl( + } + + ++// Check if a class or any of its supertypes has a version older than 50. ++// CDS will not perform verification of old classes during dump time because ++// without changing the old verifier, the verification constraint cannot be ++// retrieved during dump time. ++// Verification of archived old classes will be performed during run time. ++bool InstanceKlass::can_be_verified_at_dumptime() const { ++ if (major_version() < 50 /*JAVA_6_VERSION*/) { ++ return false; ++ } ++ if (java_super() != NULL && !java_super()->can_be_verified_at_dumptime()) { ++ return false; ++ } ++ Array* interfaces = local_interfaces(); ++ int len = interfaces->length(); ++ for (int i = 0; i < len; i++) { ++ if (!((InstanceKlass*)interfaces->at(i))->can_be_verified_at_dumptime()) { ++ return false; ++ } ++ } ++ return true; ++} ++ + // Rewrite the byte codes of all of the methods of a class. + // The rewriter must be called exactly once. Rewriting must happen after + // verification but before the first method of the class is executed. +@@ -1459,7 +1544,32 @@ static int linear_search(Array* methods, Symbol* name, Symbol* signatur + } + #endif + ++bool InstanceKlass::_disable_method_binary_search = false; ++ ++NOINLINE int linear_search(const Array* methods, const Symbol* name) { ++ int len = methods->length(); ++ int l = 0; ++ int h = len - 1; ++ while (l <= h) { ++ Method* m = methods->at(l); ++ if (m->name() == name) { ++ return l; ++ } ++ l++; ++ } ++ return -1; ++} ++ + static int binary_search(Array* methods, Symbol* name) { ++ if (InstanceKlass::_disable_method_binary_search) { ++ assert(DynamicDumpSharedSpaces, "must be"); ++ // At the final stage of dynamic dumping, the methods array may not be sorted ++ // by ascending addresses of their names, so we can't use binary search anymore. ++ // However, methods with the same name are still laid out consecutively inside the ++ // methods array, so let's look for the first one that matches. ++ return linear_search(methods, name); ++ } ++ + int len = methods->length(); + // methods are sorted, so do binary search + int l = 0; +@@ -2455,24 +2565,37 @@ void InstanceKlass::remove_unshareable_info() { + m->remove_unshareable_info(); + } + +- if (UseAppCDS) { ++ if (UseAppCDS || DynamicDumpSharedSpaces) { + if (_oop_map_cache != NULL) { + delete _oop_map_cache; + _oop_map_cache = NULL; + } +- ++ + JNIid::deallocate(jni_ids()); + set_jni_ids(NULL); +- ++ + jmethodID* jmeths = methods_jmethod_ids_acquire(); + if (jmeths != (jmethodID*)NULL) { + release_set_methods_jmethod_ids(NULL); + FreeHeap(jmeths); + } + } +- + // do array classes also. + array_klasses_do(remove_unshareable_in_class); ++ // These are not allocated from metaspace. They are safe to set to NULL. ++ _member_names = NULL; ++ _dependencies = NULL; ++ _osr_nmethods_head = NULL; ++ _init_thread = NULL; ++} ++ ++void InstanceKlass::remove_java_mirror() { ++ Klass::remove_java_mirror(); ++ ++ // do array classes also. ++ if (array_klasses() != NULL) { ++ array_klasses()->remove_java_mirror(); ++ } + } + + static void restore_unshareable_in_class(Klass* k, TRAPS) { +diff --git a/hotspot/src/share/vm/oops/instanceKlass.hpp b/hotspot/src/share/vm/oops/instanceKlass.hpp +index 39d2c580c..43919e83d 100644 +--- a/hotspot/src/share/vm/oops/instanceKlass.hpp ++++ b/hotspot/src/share/vm/oops/instanceKlass.hpp +@@ -323,6 +323,7 @@ class InstanceKlass: public Klass { + friend class SystemDictionary; + + public: ++ static bool _disable_method_binary_search; + bool has_nonstatic_fields() const { + return (_misc_flags & _misc_has_nonstatic_fields) != 0; + } +@@ -488,6 +489,7 @@ class InstanceKlass: public Klass { + void link_class(TRAPS); + bool link_class_or_fail(TRAPS); // returns false on failure + void unlink_class(); ++ bool can_be_verified_at_dumptime() const; + void rewrite_class(TRAPS); + void link_methods(TRAPS); + Method* class_initializer(); +@@ -525,6 +527,10 @@ class InstanceKlass: public Klass { + Method* find_method(Symbol* name, Symbol* signature) const; + static Method* find_method(Array* methods, Symbol* name, Symbol* signature); + ++ static void disable_method_binary_search() { ++ _disable_method_binary_search = true; ++ } ++ + // find a local method, but skip static methods + Method* find_instance_method(Symbol* name, Symbol* signature, + PrivateLookupMode private_mode); +@@ -1001,7 +1007,8 @@ class InstanceKlass: public Klass { + bool can_be_fastpath_allocated() const { + return !layout_helper_needs_slow_path(layout_helper()); + } +- ++ ++ virtual void metaspace_pointers_do(MetaspaceClosure* iter); + // Java vtable/itable + klassVtable* vtable() const; // return new klassVtable wrapper + inline Method* method_at_vtable(int index); +@@ -1075,7 +1082,7 @@ class InstanceKlass: public Klass { + + public: + void set_in_error_state() { +- assert(DumpSharedSpaces, "only call this when dumping archive"); ++ assert(DumpSharedSpaces || DynamicDumpSharedSpaces, "only call this when dumping archive"); + _init_state = initialization_error; + } + bool check_sharing_error_state(); +@@ -1150,6 +1157,7 @@ private: + public: + // CDS support - remove and restore oops from metadata. Oops are not shared. + virtual void remove_unshareable_info(); ++ virtual void remove_java_mirror(); + virtual void restore_unshareable_info(ClassLoaderData* loader_data, Handle protection_domain, TRAPS); + + // jvm support +diff --git a/hotspot/src/share/vm/oops/klass.cpp b/hotspot/src/share/vm/oops/klass.cpp +index 5269060a4..34d9d9895 100644 +--- a/hotspot/src/share/vm/oops/klass.cpp ++++ b/hotspot/src/share/vm/oops/klass.cpp +@@ -26,16 +26,19 @@ + #include "classfile/javaClasses.hpp" + #include "classfile/dictionary.hpp" + #include "classfile/systemDictionary.hpp" ++#include "classfile/systemDictionaryShared.hpp" + #include "classfile/vmSymbols.hpp" + #include "gc_implementation/shared/markSweep.inline.hpp" + #include "gc_interface/collectedHeap.inline.hpp" + #include "memory/heapInspection.hpp" + #include "memory/metadataFactory.hpp" ++#include "memory/metaspaceClosure.hpp" + #include "memory/oopFactory.hpp" + #include "memory/resourceArea.hpp" + #include "oops/instanceKlass.hpp" + #include "oops/klass.inline.hpp" + #include "oops/oop.inline2.hpp" ++#include "runtime/arguments.hpp" + #include "runtime/atomic.inline.hpp" + #include "runtime/orderAccess.inline.hpp" + #include "utilities/stack.hpp" +@@ -69,6 +72,10 @@ ClassLoaderData *Klass::_fake_loader_data_Ext = reinterpret_castincrement_refcount(); ++ ++ if (Arguments::is_dumping_archive() && oop_is_instance()) { ++ SystemDictionaryShared::init_dumptime_info(InstanceKlass::cast(this)); ++ } + } + + bool Klass::is_subclass_of(const Klass* k) const { +@@ -369,6 +376,36 @@ GrowableArray* Klass::compute_secondary_supers(int num_extra_slots) { + return NULL; + } + ++void Klass::metaspace_pointers_do(MetaspaceClosure* it) { ++ if (TraceDynamicCDS) { ++ ResourceMark rm; ++ dynamic_cds_log->print_cr("Iter(Klass): %p (%s)", this, external_name()); ++ } ++ ++ it->push(&_name); ++ it->push(&_secondary_super_cache); ++ it->push(&_secondary_supers); ++ for (int i = 0; i < _primary_super_limit; i++) { ++ it->push(&_primary_supers[i]); ++ } ++ it->push(&_super); ++ it->push((Klass**)&_subklass); ++ it->push((Klass**)&_next_sibling); ++ it->push(&_next_link); ++ ++ vtableEntry* vt = start_of_vtable(); ++ for (int i = 0; i < vtable_length(); i++) { ++ it->push(vt[i].method_addr()); ++ } ++} ++ ++inline vtableEntry* Klass::start_of_vtable() const { ++ return (vtableEntry*) ((address)this + in_bytes(vtable_start_offset())); ++} ++ ++inline ByteSize Klass::vtable_start_offset() { ++ return in_ByteSize(InstanceKlass::header_size() * wordSize); ++} + + Klass* Klass::subklass() const { + return _subklass == NULL ? NULL : _subklass; +@@ -530,7 +567,7 @@ void Klass::oops_do(OopClosure* cl) { + } + + void Klass::remove_unshareable_info() { +- assert (DumpSharedSpaces, "only called for DumpSharedSpaces"); ++ assert (DumpSharedSpaces || DynamicDumpSharedSpaces, "only called for DumpSharedSpaces or DynamicDumpSharedSpaces"); + + JFR_ONLY(REMOVE_ID(this);) + set_subklass(NULL); +@@ -539,40 +576,46 @@ void Klass::remove_unshareable_info() { + set_java_mirror(NULL); + set_next_link(NULL); + +- if (!UseAppCDS) { +- // CDS logic ++ if (class_loader_data() == NULL) { ++ // Null out class loader data for classes loaded by bootstrap (null) loader ++ set_class_loader_data(NULL); ++ } else if (SystemDictionary::is_ext_class_loader(class_loader())) { ++ // Mark class loaded by system class loader ++ set_class_loader_data(_fake_loader_data_Ext); ++ } else if (SystemDictionary::is_app_class_loader(class_loader())) { ++ set_class_loader_data(_fake_loader_data_App); ++ } else { ++ // Class loader data for classes loaded by customer loader + set_class_loader_data(NULL); +- } else if (class_loader_data() != NULL) { +- // AppCDS logic +- if (class_loader() == NULL) { +- // Null out class loader data for classes loaded by bootstrap (null) loader +- set_class_loader_data(NULL); +- } else if(SystemDictionary::is_ext_class_loader(class_loader())) { +- // Mark class loaded by system class loader +- set_class_loader_data(_fake_loader_data_Ext); +- } else { +- set_class_loader_data(_fake_loader_data_App); +- } + } + } + ++void Klass::remove_java_mirror() { ++ Arguments::assert_is_dumping_archive(); ++ if (TraceDynamicCDS) { ++ ResourceMark rm; ++ dynamic_cds_log->print_cr("remove java_mirror: %s", external_name()); ++ } ++ // Just null out the mirror. The class_loader_data() no longer exists. ++ _java_mirror = NULL; ++} ++ + void Klass::restore_unshareable_info(ClassLoaderData* loader_data, Handle protection_domain, TRAPS) { + JFR_ONLY(RESTORE_ID(this);) ++ if (TraceDynamicCDS) { ++ ResourceMark rm(THREAD); ++ dynamic_cds_log->print_cr("restore: %s", external_name()); ++ } ++ + // If an exception happened during CDS restore, some of these fields may already be + // set. We leave the class on the CLD list, even if incomplete so that we don't + // modify the CLD list outside a safepoint. + if (class_loader_data() == NULL || has_fake_loader_data()) { +- // CDS should not set fake loader data +- assert(!has_fake_loader_data() || (has_fake_loader_data() && UseAppCDS), +- "setting fake loader data possible only with AppCDS enabled"); +- // Restore class_loader_data + set_class_loader_data(loader_data); +- + // Add to class loader list first before creating the mirror + // (same order as class file parsing) + loader_data->add_class(this); + } +- + // Recreate the class mirror. + // Only recreate it if not present. A previous attempt to restore may have + // gotten an OOM later but keep the mirror if it was created. +diff --git a/hotspot/src/share/vm/oops/klass.hpp b/hotspot/src/share/vm/oops/klass.hpp +index f70587eab..4e45a7756 100644 +--- a/hotspot/src/share/vm/oops/klass.hpp ++++ b/hotspot/src/share/vm/oops/klass.hpp +@@ -94,6 +94,8 @@ class ParCompactionManager; + class KlassSizeStats; + class fieldDescriptor; + class MarkSweep; ++class MetaspaceClosure; ++class vtableEntry; + + class Klass : public Metadata { + friend class VMStructs; +@@ -209,7 +211,7 @@ protected: + bool has_fake_loader_data_App() { return class_loader_data() == _fake_loader_data_App; } + bool has_fake_loader_data_Ext() { return class_loader_data() == _fake_loader_data_Ext; } + bool has_fake_loader_data() { return (has_fake_loader_data_App() || has_fake_loader_data_Ext()); } +- ++ + bool is_klass() const volatile { return true; } + + // super +@@ -316,6 +318,7 @@ protected: + _shared_class_path_index = index; + }; + ++ virtual void metaspace_pointers_do(MetaspaceClosure* it); + + protected: // internal accessors + Klass* subklass_oop() const { return _subklass; } +@@ -323,7 +326,10 @@ protected: + void set_subklass(Klass* s); + void set_next_sibling(Klass* s); + ++ vtableEntry* start_of_vtable() const; ++ + public: ++ static ByteSize vtable_start_offset(); + + // Compiler support + static ByteSize super_offset() { return in_ByteSize(offset_of(Klass, _super)); } +@@ -505,6 +511,7 @@ protected: + public: + // CDS support - remove and restore oops from metadata. Oops are not shared. + virtual void remove_unshareable_info(); ++ virtual void remove_java_mirror(); + virtual void restore_unshareable_info(ClassLoaderData* loader_data, Handle protection_domain, TRAPS); + + protected: +@@ -725,6 +732,7 @@ protected: + + virtual const char* internal_name() const = 0; + ++ virtual MetaspaceObj::Type type() const { return ClassType; } + // Verification + virtual void verify_on(outputStream* st); + void verify() { verify_on(tty); } +diff --git a/hotspot/src/share/vm/oops/klassVtable.hpp b/hotspot/src/share/vm/oops/klassVtable.hpp +index 244f3c0cc..9379bcca0 100644 +--- a/hotspot/src/share/vm/oops/klassVtable.hpp ++++ b/hotspot/src/share/vm/oops/klassVtable.hpp +@@ -176,6 +176,7 @@ class vtableEntry VALUE_OBJ_CLASS_SPEC { + } + static int method_offset_in_bytes() { return offset_of(vtableEntry, _method); } + Method* method() const { return _method; } ++ Method** method_addr() { return &_method; } + + private: + Method* _method; +@@ -216,6 +217,7 @@ class itableOffsetEntry VALUE_OBJ_CLASS_SPEC { + int _offset; + public: + Klass* interface_klass() const { return _interface; } ++ InstanceKlass**interface_klass_addr() { return(InstanceKlass**) &_interface; } + int offset() const { return _offset; } + + static itableMethodEntry* method_entry(Klass* k, int offset) { return (itableMethodEntry*)(((address)k) + offset); } +@@ -238,6 +240,7 @@ class itableMethodEntry VALUE_OBJ_CLASS_SPEC { + + public: + Method* method() const { return _method; } ++ Method**method_addr() { return &_method; } + + void clear() { _method = NULL; } + +diff --git a/hotspot/src/share/vm/oops/metadata.hpp b/hotspot/src/share/vm/oops/metadata.hpp +index dc52c452e..372faa953 100644 +--- a/hotspot/src/share/vm/oops/metadata.hpp ++++ b/hotspot/src/share/vm/oops/metadata.hpp +@@ -28,6 +28,7 @@ + #include "utilities/exceptions.hpp" + #include "utilities/globalDefinitions.hpp" + #include "utilities/ostream.hpp" ++class MetaspaceClosure; + + // This is the base class for an internal Class related metadata + class Metadata : public MetaspaceObj { +@@ -47,8 +48,9 @@ class Metadata : public MetaspaceObj { + virtual bool is_method() const volatile { return false; } + virtual bool is_methodData() const volatile { return false; } + virtual bool is_constantPool() const volatile { return false; } +- ++ virtual MetaspaceObj::Type type() const = 0; + virtual const char* internal_name() const = 0; ++ virtual void metaspace_pointers_do(MetaspaceClosure* iter) {} + + void print() const { print_on(tty); } + void print_value() const { print_value_on(tty); } +diff --git a/hotspot/src/share/vm/oops/method.cpp b/hotspot/src/share/vm/oops/method.cpp +index 64cdae9c7..305348bd0 100644 +--- a/hotspot/src/share/vm/oops/method.cpp ++++ b/hotspot/src/share/vm/oops/method.cpp +@@ -37,6 +37,7 @@ + #include "memory/heapInspection.hpp" + #include "memory/metadataFactory.hpp" + #include "memory/metaspaceShared.hpp" ++#include "memory/metaspaceClosure.hpp" + #include "memory/oopFactory.hpp" + #include "oops/constMethod.hpp" + #include "oops/methodData.hpp" +@@ -834,6 +835,20 @@ void Method::set_not_osr_compilable(int comp_level, bool report, const char* rea + assert(!CompilationPolicy::can_be_osr_compiled(this, comp_level), "sanity check"); + } + ++void Method::metaspace_pointers_do(MetaspaceClosure* it) { ++ if (TraceDynamicCDS) { ++ dynamic_cds_log->print_cr("Iter(Method): %p", this); ++ } ++ ++ if (!method_holder()->is_rewritten()) { ++ it->push(&_constMethod, MetaspaceClosure::_writable); ++ } else { ++ it->push(&_constMethod); ++ } ++ it->push(&_method_data); ++ it->push(&_method_counters); ++} ++ + // Revert to using the interpreter and clear out the nmethod + void Method::clear_code(bool acquire_lock /* = true */) { + MutexLockerEx pl(acquire_lock ? Patching_lock : NULL, Mutex::_no_safepoint_check_flag); +@@ -1421,12 +1436,15 @@ static int method_comparator(Method* a, Method* b) { + + // This is only done during class loading, so it is OK to assume method_idnum matches the methods() array + // default_methods also uses this without the ordering for fast find_method +-void Method::sort_methods(Array* methods, bool idempotent, bool set_idnums) { ++void Method::sort_methods(Array* methods, bool idempotent, bool set_idnums, method_comparator_func func) { + int length = methods->length(); + if (length > 1) { ++ if (func == NULL) { ++ func = method_comparator; ++ } + { + No_Safepoint_Verifier nsv; +- QuickSort::sort(methods->data(), length, method_comparator, idempotent); ++ QuickSort::sort(methods->data(), length, func, idempotent); + } + // Reset method ordering + if (set_idnums) { +diff --git a/hotspot/src/share/vm/oops/method.hpp b/hotspot/src/share/vm/oops/method.hpp +index 1f507ac0f..ec93f2fb4 100644 +--- a/hotspot/src/share/vm/oops/method.hpp ++++ b/hotspot/src/share/vm/oops/method.hpp +@@ -99,6 +99,7 @@ class MethodCounters; + class ConstMethod; + class InlineTableSizes; + class KlassSizeStats; ++class MetaspaceClosure; + + class Method : public Metadata { + friend class VMStructs; +@@ -857,6 +858,9 @@ class Method : public Metadata { + void print_made_not_compilable(int comp_level, bool is_osr, bool report, const char* reason); + + public: ++ void metaspace_pointers_do(MetaspaceClosure* it); ++ virtual MetaspaceObj::Type type() const { return MethodType; } ++ + MethodCounters* get_method_counters(TRAPS) { + if (_method_counters == NULL) { + build_method_counters(this, CHECK_AND_CLEAR_NULL); +@@ -897,8 +901,9 @@ class Method : public Metadata { + void print_name(outputStream* st = tty) PRODUCT_RETURN; // prints as "virtual void foo(int)" + #endif + ++ typedef int (*method_comparator_func)(Method* a, Method* b); + // Helper routine used for method sorting +- static void sort_methods(Array* methods, bool idempotent = false, bool set_idnums = true); ++ static void sort_methods(Array* methods, bool idempotent = false, bool set_idnums = true, method_comparator_func func = NULL); + + // Deallocation function for redefine classes or if an error occurs + void deallocate_contents(ClassLoaderData* loader_data); +diff --git a/hotspot/src/share/vm/oops/methodCounters.hpp b/hotspot/src/share/vm/oops/methodCounters.hpp +index b98644574..6a3f7a738 100644 +--- a/hotspot/src/share/vm/oops/methodCounters.hpp ++++ b/hotspot/src/share/vm/oops/methodCounters.hpp +@@ -129,5 +129,12 @@ class MethodCounters: public MetaspaceObj { + return offset_of(MethodCounters, _interpreter_invocation_count); + } + ++ MetaspaceObj::Type type() const { return MethodCountersType; } ++ ++ void metaspace_pointers_do(MetaspaceClosure* it) { ++ if (TraceDynamicCDS) { ++ dynamic_cds_log->print_cr("Iter(MethodCounters): %p", this); ++ } ++ } + }; + #endif //SHARE_VM_OOPS_METHODCOUNTERS_HPP +diff --git a/hotspot/src/share/vm/oops/methodData.cpp b/hotspot/src/share/vm/oops/methodData.cpp +index eb48188a6..bde6ca123 100644 +--- a/hotspot/src/share/vm/oops/methodData.cpp ++++ b/hotspot/src/share/vm/oops/methodData.cpp +@@ -29,6 +29,7 @@ + #include "interpreter/bytecodeStream.hpp" + #include "interpreter/linkResolver.hpp" + #include "memory/heapInspection.hpp" ++#include "memory/metaspaceClosure.hpp" + #include "oops/methodData.hpp" + #include "prims/jvmtiRedefineClasses.hpp" + #include "runtime/compilationPolicy.hpp" +@@ -1683,3 +1684,11 @@ void MethodData::clean_weak_method_links() { + clean_extra_data(&cl); + verify_extra_data_clean(&cl); + } ++ ++ ++void MethodData::metaspace_pointers_do(MetaspaceClosure* iter) { ++ if (TraceDynamicCDS) { ++ dynamic_cds_log->print_cr("Iter(MethodData): %p", this); ++ } ++ iter->push(&_method); ++} +diff --git a/hotspot/src/share/vm/oops/methodData.hpp b/hotspot/src/share/vm/oops/methodData.hpp +index 3cd7cd6f1..eb121268f 100644 +--- a/hotspot/src/share/vm/oops/methodData.hpp ++++ b/hotspot/src/share/vm/oops/methodData.hpp +@@ -67,7 +67,7 @@ class KlassSizeStats; + + // forward decl + class ProfileData; +- ++class MetaspaceClosure; + // DataLayout + // + // Overlay for generic profiling data. +@@ -2486,6 +2486,9 @@ public: + void clean_method_data(BoolObjectClosure* is_alive); + + void clean_weak_method_links(); ++ ++ virtual void metaspace_pointers_do(MetaspaceClosure* iter); ++ virtual MetaspaceObj::Type type() const { return MethodDataType; } + }; + + #endif // SHARE_VM_OOPS_METHODDATAOOP_HPP +diff --git a/hotspot/src/share/vm/oops/objArrayKlass.cpp b/hotspot/src/share/vm/oops/objArrayKlass.cpp +index 19abfbd5a..60d173e9e 100644 +--- a/hotspot/src/share/vm/oops/objArrayKlass.cpp ++++ b/hotspot/src/share/vm/oops/objArrayKlass.cpp +@@ -33,6 +33,7 @@ + #include "memory/metadataFactory.hpp" + #include "memory/resourceArea.hpp" + #include "memory/universe.inline.hpp" ++#include "memory/metaspaceClosure.hpp" + #include "oops/instanceKlass.hpp" + #include "oops/klass.inline.hpp" + #include "oops/objArrayKlass.hpp" +@@ -569,6 +570,12 @@ int ObjArrayKlass::oop_adjust_pointers(oop obj) { + return size; + } + ++void ObjArrayKlass::metaspace_pointers_do(MetaspaceClosure* it) { ++ ArrayKlass::metaspace_pointers_do(it); ++ it->push(&_element_klass); ++ it->push(&_bottom_klass); ++} ++ + #if INCLUDE_ALL_GCS + void ObjArrayKlass::oop_push_contents(PSPromotionManager* pm, oop obj) { + assert(obj->is_objArray(), "obj must be obj array"); +diff --git a/hotspot/src/share/vm/oops/objArrayKlass.hpp b/hotspot/src/share/vm/oops/objArrayKlass.hpp +index ab3cbc61c..c17adba70 100644 +--- a/hotspot/src/share/vm/oops/objArrayKlass.hpp ++++ b/hotspot/src/share/vm/oops/objArrayKlass.hpp +@@ -109,7 +109,8 @@ class ObjArrayKlass : public ArrayKlass { + template inline void objarray_follow_contents(oop obj, int index, MarkSweep* mark); + + int oop_adjust_pointers(oop obj); +- ++ ++ virtual void metaspace_pointers_do(MetaspaceClosure* iter); + // Parallel Scavenge and Parallel Old + PARALLEL_GC_DECLS + #if INCLUDE_ALL_GCS +diff --git a/hotspot/src/share/vm/oops/symbol.hpp b/hotspot/src/share/vm/oops/symbol.hpp +index aaa55c589..4b1b5cb5d 100644 +--- a/hotspot/src/share/vm/oops/symbol.hpp ++++ b/hotspot/src/share/vm/oops/symbol.hpp +@@ -25,9 +25,9 @@ + #ifndef SHARE_VM_OOPS_SYMBOL_HPP + #define SHARE_VM_OOPS_SYMBOL_HPP + +-#include "utilities/utf8.hpp" + #include "memory/allocation.hpp" + #include "runtime/atomic.hpp" ++#include "utilities/utf8.hpp" + + // A Symbol is a canonicalized string. + // All Symbols reside in global SymbolTable and are reference counted. +@@ -101,6 +101,7 @@ + // Since sometimes this is allocated from Metadata, pick a base allocation + // type without virtual functions. + class ClassLoaderData; ++class MetaspaceClosure; + + // We separate the fields in SymbolBase from Symbol::_body so that + // Symbol::size(int) can correctly calculate the space needed. +@@ -113,7 +114,7 @@ class SymbolBase : public MetaspaceObj { + int _identity_hash; + }; + +-class Symbol : private SymbolBase { ++class Symbol : public SymbolBase { + friend class VMStructs; + friend class SymbolTable; + friend class MoveSymbols; +@@ -160,6 +161,9 @@ class Symbol : private SymbolBase { + int refcount() const { return _refcount; } + void increment_refcount(); + void decrement_refcount(); ++ bool is_permanent() const { ++ return (refcount() == -1); ++ } + + int byte_at(int index) const { + assert(index >=0 && index < _length, "symbol index overflow"); +@@ -180,6 +184,17 @@ class Symbol : private SymbolBase { + return starts_with(prefix, (int) strlen(prefix)); + } + ++ void set_permanent() { ++ _refcount = -1; ++ } ++ ++ void metaspace_pointers_do(MetaspaceClosure* it) { ++ if (TraceDynamicCDS) { ++ dynamic_cds_log->print_cr("Iter(Symbol): %p", this); ++ } ++ } ++ ++ MetaspaceObj::Type type() const { return SymbolType; } + // Tests if the symbol starts with the given prefix. + int index_of_at(int i, const char* str, int len) const; + int index_of_at(int i, const char* str) const { +@@ -208,6 +223,9 @@ class Symbol : private SymbolBase { + + jchar* as_unicode(int& length) const; + ++ // Symbols should be stored in the read-only region of CDS archive. ++ static bool is_read_only_by_default() { return true; } ++ + // Treating this symbol as a class name, returns the Java name for the class. + // String is allocated in resource area if buffer is not provided. + // See Klass::external_name() +diff --git a/hotspot/src/share/vm/runtime/arguments.cpp b/hotspot/src/share/vm/runtime/arguments.cpp +index 6f5e75107..1f603021a 100644 +--- a/hotspot/src/share/vm/runtime/arguments.cpp ++++ b/hotspot/src/share/vm/runtime/arguments.cpp +@@ -29,6 +29,7 @@ + #include "compiler/compilerOracle.hpp" + #include "memory/allocation.inline.hpp" + #include "memory/cardTableRS.hpp" ++#include "memory/filemap.hpp" + #include "memory/genCollectedHeap.hpp" + #include "memory/referenceProcessor.hpp" + #include "memory/universe.inline.hpp" +@@ -126,6 +127,7 @@ bool Arguments::_BackgroundCompilation = BackgroundCompilation; + bool Arguments::_ClipInlining = ClipInlining; + + char* Arguments::SharedArchivePath = NULL; ++char* Arguments::SharedDynamicArchivePath = NULL; + + AgentLibraryList Arguments::_libraryList; + AgentLibraryList Arguments::_agentList; +@@ -179,6 +181,117 @@ static void logOption(const char* opt) { + } + } + ++#if INCLUDE_CDS ++// Sharing support ++// Construct the path to the archive ++int Arguments::num_archives(const char* archive_path) { ++ if (archive_path == NULL) { ++ return 0; ++ } ++ int npaths = 1; ++ char* p = (char*)archive_path; ++ while (*p != '\0') { ++ if (*p == os::path_separator()[0]) { ++ npaths++; ++ } ++ p++; ++ } ++ return npaths; ++} ++ ++void Arguments::extract_shared_archive_paths(const char* archive_path, ++ char** base_archive_path, ++ char** top_archive_path) { ++ char* begin_ptr = (char*)archive_path; ++ char* end_ptr = strchr((char*)archive_path, os::path_separator()[0]); ++ if (end_ptr == NULL || end_ptr == begin_ptr) { ++ vm_exit_during_initialization("Base archive was not specified", archive_path); ++ } ++ size_t len = end_ptr - begin_ptr; ++ char* cur_path = NEW_C_HEAP_ARRAY(char, len + 1, mtInternal); ++ strncpy(cur_path, begin_ptr, len); ++ cur_path[len] = '\0'; ++ FileMapInfo::check_archive((const char*)cur_path, true /*is_static*/); ++ *base_archive_path = cur_path; ++ ++ begin_ptr = ++end_ptr; ++ if (*begin_ptr == '\0') { ++ vm_exit_during_initialization("Top archive was not specified", archive_path); ++ } ++ end_ptr = strchr(begin_ptr, '\0'); ++ assert(end_ptr != NULL, "sanity"); ++ len = end_ptr - begin_ptr; ++ cur_path = NEW_C_HEAP_ARRAY(char, len + 1, mtInternal); ++ strncpy(cur_path, begin_ptr, len + 1); ++ ++ FileMapInfo::check_archive((const char*)cur_path, false /*is_static*/); ++ *top_archive_path = cur_path; ++} ++ ++bool Arguments::init_shared_archive_paths() { ++ if (ArchiveClassesAtExit != NULL) { ++ if (DumpSharedSpaces) { ++ vm_exit_during_initialization("-XX:ArchiveClassesAtExit cannot be used with -Xshare:dump"); ++ } ++ SharedDynamicArchivePath = os::strdup_check_oom(ArchiveClassesAtExit, mtClassShared); ++ } else { ++ if (SharedDynamicArchivePath != NULL) { ++ os::free(SharedDynamicArchivePath); ++ SharedDynamicArchivePath = NULL; ++ } ++ } ++ ++ if (SharedArchiveFile != NULL) { ++ int archives = num_archives(SharedArchiveFile); ++ if (is_dumping_archive()) { ++ if (archives > 1) { ++ vm_exit_during_initialization( ++ "Cannot have more than 1 archive file specified in -XX:SharedArchiveFile during CDS dumping"); ++ } ++ if (DynamicDumpSharedSpaces) { ++ if (strcmp(SharedArchiveFile, ArchiveClassesAtExit) == 0) { ++ vm_exit_during_initialization( ++ "Cannot have the same archive file specified for -XX:SharedArchiveFile and -XX:ArchiveClassesAtExit", ++ SharedArchiveFile); ++ } ++ } ++ } ++ ++ if (!is_dumping_archive()) { ++ if (archives > 2) { ++ vm_exit_during_initialization( ++ "Cannot have more than 2 archive files specified in the -XX:SharedArchiveFile option"); ++ } ++ if (archives == 1) { ++ char* temp_archive_path = os::strdup_check_oom(SharedArchiveFile, mtClassShared); ++ int name_size; ++ bool success = ++ FileMapInfo::get_base_archive_name_from_header(temp_archive_path, &name_size, &SharedArchivePath); ++ if (!success) { ++ SharedArchivePath = temp_archive_path; ++ } else { ++ SharedDynamicArchivePath = temp_archive_path; ++ } ++ } else { ++ extract_shared_archive_paths((const char*)SharedArchiveFile, ++ &SharedArchivePath, &SharedDynamicArchivePath); ++ } ++ ++ // We must use tty here instead of dynamic_cds_log for dynamic_cds_log is initialized after share path init. ++ if (InfoDynamicCDS && SharedArchivePath != NULL) { ++ tty->print_cr("SharedArchivePath: %s", SharedArchivePath); ++ } ++ if (InfoDynamicCDS && SharedDynamicArchivePath != NULL) { ++ tty->print_cr("SharedDynamicArchivePath: %s", SharedDynamicArchivePath); ++ } ++ } else { // CDS dumping ++ SharedArchivePath = os::strdup_check_oom(SharedArchiveFile, mtClassShared); ++ } ++ } ++ return (SharedArchivePath != NULL); ++} ++#endif // INCLUDE_CDS ++ + // Process java launcher properties. + void Arguments::process_sun_java_launcher_properties(JavaVMInitArgs* args) { + // See if sun.java.launcher or sun.java.launcher.pid is defined. +@@ -3724,6 +3837,30 @@ jint Arguments::finalize_vm_init_args(SysClassPath* scp_p, bool scp_assembly_req + set_mode_flags(_int); + } + ++#if INCLUDE_CDS ++ if (ArchiveClassesAtExit == NULL) { ++ FLAG_SET_DEFAULT(DynamicDumpSharedSpaces, false); ++ } else { ++ FLAG_SET_DEFAULT(DynamicDumpSharedSpaces, true); ++ // When Dynamic CDS dump is turned on, we will set ClassUnloading false, ++ // and there is no need to care if the class loader is alive. ++ FLAG_SET_DEFAULT(ClassUnloading, false); ++ } ++ ++ if (TraceDynamicCDS) { ++ FLAG_SET_DEFAULT(DebugDynamicCDS, true); ++ FLAG_SET_DEFAULT(InfoDynamicCDS, true); ++ } else if (DebugDynamicCDS) { ++ FLAG_SET_DEFAULT(InfoDynamicCDS, true); ++ } ++ ++#ifdef _LP64 ++ // We attempt to set SharedBaseAddress right above ++ // the java heap base on ObjectAlignmentInBytes. ++ FLAG_SET_DEFAULT(SharedBaseAddress, (ObjectAlignmentInBytes * 4 * G)); ++#endif // _LP64 ++#endif // INCLUDE_CDS ++ + // eventually fix up InitialTenuringThreshold if only MaxTenuringThreshold is set + if (FLAG_IS_DEFAULT(InitialTenuringThreshold) && (InitialTenuringThreshold > MaxTenuringThreshold)) { + FLAG_SET_ERGO(uintx, InitialTenuringThreshold, MaxTenuringThreshold); +@@ -3885,6 +4022,11 @@ void Arguments::set_shared_spaces_flags() { + } + #endif + } ++ ++#if INCLUDE_CDS ++ // Initialize shared archive paths which could include both base and dynamic archive paths ++ init_shared_archive_paths(); ++#endif // INCLUDE_CDS + } + + #if !INCLUDE_ALL_GCS +diff --git a/hotspot/src/share/vm/runtime/arguments.hpp b/hotspot/src/share/vm/runtime/arguments.hpp +index a1fcfc398..19f5cb60b 100644 +--- a/hotspot/src/share/vm/runtime/arguments.hpp ++++ b/hotspot/src/share/vm/runtime/arguments.hpp +@@ -443,7 +443,8 @@ class Arguments : AllStatic { + static bool CheckCompileOnly; + + static char* SharedArchivePath; +- static char* AppCDSLockPath; ++ ++ static char* SharedDynamicArchivePath; + + public: + // Parses the arguments, first phase +@@ -553,6 +554,22 @@ class Arguments : AllStatic { + + static const char* GetSharedArchivePath() { return SharedArchivePath; } + ++ static const char* GetSharedDynamicArchivePath() { return SharedDynamicArchivePath; } ++ ++ static bool init_shared_archive_paths(); ++ ++ static void extract_shared_archive_paths(const char* archive_path, ++ char** base_archive_path, ++ char** top_archive_path); ++ ++ static int num_archives(const char* archive_path); ++ ++ static bool is_dumping_archive() { return DumpSharedSpaces || DynamicDumpSharedSpaces; } ++ ++ static void assert_is_dumping_archive() { ++ assert(Arguments::is_dumping_archive(), "dump time only"); ++ } ++ + static bool CompileMethod(char* className, char* methodName) { + return + methodExists( +diff --git a/hotspot/src/share/vm/runtime/globals.hpp b/hotspot/src/share/vm/runtime/globals.hpp +index 91e52f033..eb13ee0d7 100644 +--- a/hotspot/src/share/vm/runtime/globals.hpp ++++ b/hotspot/src/share/vm/runtime/globals.hpp +@@ -3910,6 +3910,24 @@ class CommandLineFlags { + NOT_LP64(LINUX_ONLY(2*G) NOT_LINUX(0)), \ + "Address to allocate shared memory region for class data") \ + \ ++ experimental(ccstr, ArchiveClassesAtExit, NULL, \ ++ "The path and name of the dynamic archive file") \ ++ \ ++ product(bool, InfoDynamicCDS, false, \ ++ "Log info level in DynamicCDS") \ ++ \ ++ product(bool, TraceDynamicCDS, false, \ ++ "Trace details in DynamicCDS") \ ++ \ ++ product(bool, DebugDynamicCDS, false, \ ++ "Debug details in DynamicCDS") \ ++ \ ++ product(bool, DynamicDumpSharedSpaces, false, \ ++ "Dynamic archive") \ ++ \ ++ product(uintx, SharedSymbolTableBucketSize, 4, \ ++ "Average number of symbols per bucket in shared table") \ ++ \ + diagnostic(bool, EnableInvokeDynamic, true, \ + "support JSR 292 (method handles, invokedynamic, " \ + "anonymous classes") \ +@@ -4017,6 +4035,9 @@ class CommandLineFlags { + "Dump the names all loaded classes, that could be stored into " \ + "the CDS archive, in the specified file") \ + \ ++ product(ccstr, DynamicCDSLog, NULL, \ ++ "Dynamic CDS log path") \ ++ \ + product(ccstr, SharedClassListFile, NULL, \ + "Override the default CDS class list") \ + \ +diff --git a/hotspot/src/share/vm/runtime/java.cpp b/hotspot/src/share/vm/runtime/java.cpp +index 0a263b017..4f290c826 100644 +--- a/hotspot/src/share/vm/runtime/java.cpp ++++ b/hotspot/src/share/vm/runtime/java.cpp +@@ -23,6 +23,7 @@ + */ + + #include "precompiled.hpp" ++#include "cds/dynamicArchive.hpp" + #include "classfile/classLoader.hpp" + #include "classfile/symbolTable.hpp" + #include "classfile/systemDictionary.hpp" +@@ -546,6 +547,13 @@ void before_exit(JavaThread * thread) { + // Note: we don't wait until it actually dies. + os::terminate_signal_thread(); + ++#if INCLUDE_CDS ++ if (DynamicDumpSharedSpaces) { ++ DynamicArchive::dump(); ++ ShouldNotReachHere(); ++ } ++#endif ++ + print_statistics(); + Universe::heap()->print_tracing_info(); + +diff --git a/hotspot/src/share/vm/runtime/mutexLocker.cpp b/hotspot/src/share/vm/runtime/mutexLocker.cpp +index a96ae50eb..a1c61f864 100644 +--- a/hotspot/src/share/vm/runtime/mutexLocker.cpp ++++ b/hotspot/src/share/vm/runtime/mutexLocker.cpp +@@ -39,6 +39,7 @@ + + Mutex* Patching_lock = NULL; + Monitor* SystemDictionary_lock = NULL; ++Mutex* SharedDictionary_lock = NULL; + Mutex* PackageTable_lock = NULL; + Mutex* CompiledIC_lock = NULL; + Mutex* InlineCacheBuffer_lock = NULL; +@@ -129,6 +130,7 @@ Monitor* RedefineClasses_lock = NULL; + + Mutex* FreeHumongousRegions_lock = NULL; + ++Mutex* DumpTimeTable_lock = NULL; + #ifdef INCLUDE_JFR + Mutex* JfrStacktrace_lock = NULL; + Monitor* JfrMsg_lock = NULL; +@@ -224,6 +226,7 @@ void mutex_init() { + def(JmethodIdCreation_lock , Mutex , leaf, true ); // used for creating jmethodIDs. + + def(SystemDictionary_lock , Monitor, leaf, true ); // lookups done by VM thread ++ def(SharedDictionary_lock , Mutex , leaf, true ); + def(PackageTable_lock , Mutex , leaf, false); + def(InlineCacheBuffer_lock , Mutex , leaf, true ); + def(VMStatistic_lock , Mutex , leaf, false); +@@ -289,7 +292,7 @@ void mutex_init() { + def(RedefineClasses_lock , Monitor, nonleaf+5, true); + + def(FreeHumongousRegions_lock , Mutex , nonleaf, false); +- ++ def(DumpTimeTable_lock , Mutex , leaf - 1, true); + #if INCLUDE_JFR + def(JfrMsg_lock , Monitor, leaf, true); + def(JfrBuffer_lock , Mutex, leaf, true); +diff --git a/hotspot/src/share/vm/runtime/mutexLocker.hpp b/hotspot/src/share/vm/runtime/mutexLocker.hpp +index 428c80181..f28058b0e 100644 +--- a/hotspot/src/share/vm/runtime/mutexLocker.hpp ++++ b/hotspot/src/share/vm/runtime/mutexLocker.hpp +@@ -47,6 +47,7 @@ + + extern Mutex* Patching_lock; // a lock used to guard code patching of compiled code + extern Monitor* SystemDictionary_lock; // a lock on the system dictonary ++extern Mutex* SharedDictionary_lock; // a lock on the CDS shared dictionary + extern Mutex* PackageTable_lock; // a lock on the class loader package table + extern Mutex* CompiledIC_lock; // a lock used to guard compiled IC patching and access + extern Mutex* InlineCacheBuffer_lock; // a lock used to guard the InlineCacheBuffer +@@ -145,6 +146,8 @@ extern Monitor* RedefineClasses_lock; // locks classes from parallel + + extern Mutex* FreeHumongousRegions_lock; // locks humongous regions from freeing in parallel + ++extern Mutex* DumpTimeTable_lock; // SystemDictionaryShared::find_or_allocate_info_for ++ + #if INCLUDE_JFR + extern Mutex* JfrStacktrace_lock; // used to guard access to the JFR stacktrace table + extern Monitor* JfrMsg_lock; // protects JFR messaging +diff --git a/hotspot/src/share/vm/runtime/os.cpp b/hotspot/src/share/vm/runtime/os.cpp +index ed41265cc..5c5d60220 100644 +--- a/hotspot/src/share/vm/runtime/os.cpp ++++ b/hotspot/src/share/vm/runtime/os.cpp +@@ -568,7 +568,7 @@ bool os::find_builtin_agent(AgentLibrary *agent_lib, const char *syms[], + + // --------------------- heap allocation utilities --------------------- + +-char *os::strdup(const char *str, MEMFLAGS flags) { ++char* os::strdup(const char *str, MEMFLAGS flags) { + size_t size = strlen(str); + char *dup_str = (char *)malloc(size + 1, flags); + if (dup_str == NULL) return NULL; +@@ -576,6 +576,13 @@ char *os::strdup(const char *str, MEMFLAGS flags) { + return dup_str; + } + ++char* os::strdup_check_oom(const char* str, MEMFLAGS flags) { ++ char* p = os::strdup(str, flags); ++ if (p == NULL) { ++ vm_exit_out_of_memory(strlen(str) + 1, OOM_MALLOC_ERROR, "os::strdup_check_oom"); ++ } ++ return p; ++} + + + #define paranoid 0 /* only set to 1 if you suspect checking code has bug */ +diff --git a/hotspot/src/share/vm/runtime/os.hpp b/hotspot/src/share/vm/runtime/os.hpp +index 296380f39..7ae49fd5b 100644 +--- a/hotspot/src/share/vm/runtime/os.hpp ++++ b/hotspot/src/share/vm/runtime/os.hpp +@@ -731,6 +731,8 @@ class os: AllStatic { + static void free (void *memblock, MEMFLAGS flags = mtNone); + static bool check_heap(bool force = false); // verify C heap integrity + static char* strdup(const char *, MEMFLAGS flags = mtInternal); // Like strdup ++ // Like strdup, but exit VM when strdup() returns NULL ++ static char* strdup_check_oom(const char*, MEMFLAGS flags = mtInternal); + + #ifndef PRODUCT + static julong num_mallocs; // # of calls to malloc/realloc +diff --git a/hotspot/src/share/vm/runtime/thread.cpp b/hotspot/src/share/vm/runtime/thread.cpp +index 94b9e69d2..807786d98 100644 +--- a/hotspot/src/share/vm/runtime/thread.cpp ++++ b/hotspot/src/share/vm/runtime/thread.cpp +@@ -23,6 +23,7 @@ + */ + + #include "precompiled.hpp" ++#include "cds/dynamicArchive.hpp" + #include "classfile/classLoader.hpp" + #include "classfile/javaClasses.hpp" + #include "classfile/systemDictionary.hpp" +@@ -3934,6 +3935,15 @@ void JavaThread::invoke_shutdown_hooks() { + this->clear_pending_exception(); + } + ++#if INCLUDE_CDS ++ // Link all classes for dynamic CDS dumping before vm exit. ++ // Same operation is being done in JVM_BeforeHalt for handling the ++ // case where the application calls System.exit(). ++ if (DynamicDumpSharedSpaces) { ++ DynamicArchive::prepare_for_dynamic_dumping_at_exit(); ++ } ++#endif ++ + EXCEPTION_MARK; + Klass* k = + SystemDictionary::resolve_or_null(vmSymbols::java_lang_Shutdown(), +diff --git a/hotspot/src/share/vm/services/diagnosticCommand.cpp b/hotspot/src/share/vm/services/diagnosticCommand.cpp +index ede8db156..358ec6e09 100644 +--- a/hotspot/src/share/vm/services/diagnosticCommand.cpp ++++ b/hotspot/src/share/vm/services/diagnosticCommand.cpp +@@ -23,6 +23,7 @@ + */ + + #include "precompiled.hpp" ++#include "cds/dynamicArchive.hpp" + #include "classfile/classLoaderStats.hpp" + #include "gc_implementation/shared/vmGCOperations.hpp" + #include "runtime/javaCalls.hpp" +@@ -57,6 +58,7 @@ void DCmdRegistrant::register_dcmds(){ + DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl(full_export, true, false)); + #if INCLUDE_SERVICES // Heap dumping/inspection supported + DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl(DCmd_Source_Internal | DCmd_Source_AttachAPI, true, false)); ++ DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl(DCmd_Source_Internal | DCmd_Source_AttachAPI, true, false)); + DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl(full_export, true, false)); + DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl(full_export, true, false)); + #endif // INCLUDE_SERVICES +@@ -375,6 +377,17 @@ int HeapDumpDCmd::num_arguments() { + } + } + ++void DynamicCDSDumpDCmd::execute(DCmdSource source, TRAPS) { ++#if INCLUDE_CDS ++ if (DynamicDumpSharedSpaces) { ++ DynamicArchive::dump(); ++ ShouldNotReachHere(); ++ } else { ++ warning("Dynamic CDS is not enabled"); ++ } ++#endif ++} ++ + ClassHistogramDCmd::ClassHistogramDCmd(outputStream* output, bool heap) : + DCmdWithParser(output, heap), + _all("-all", "Inspect all objects, including unreachable objects", +diff --git a/hotspot/src/share/vm/services/diagnosticCommand.hpp b/hotspot/src/share/vm/services/diagnosticCommand.hpp +index b1fb57e53..e28011f25 100644 +--- a/hotspot/src/share/vm/services/diagnosticCommand.hpp ++++ b/hotspot/src/share/vm/services/diagnosticCommand.hpp +@@ -267,6 +267,29 @@ public: + }; + #endif // INCLUDE_SERVICES + ++class DynamicCDSDumpDCmd : public DCmdWithParser { ++public: ++ DynamicCDSDumpDCmd(outputStream* output, bool heap) : DCmdWithParser(output, heap) { } ++ static const char* name() { ++ return "GC.dynamic_cds_dump"; ++ } ++ static const char* description() { ++ return "Dynamic CDS dump"; ++ } ++ static const char* impact() { ++ return "Medium"; ++ } ++ static const JavaPermission permission() { ++ JavaPermission p = {"java.lang.management.ManagementPermission", ++ "monitor", NULL}; ++ return p; ++ } ++ static int num_arguments() { ++ return 0; ++ } ++ virtual void execute(DCmdSource source, TRAPS); ++}; ++ + // See also: inspectheap in attachListener.cpp + class ClassHistogramDCmd : public DCmdWithParser { + protected: +diff --git a/hotspot/src/share/vm/utilities/array.hpp b/hotspot/src/share/vm/utilities/array.hpp +index 920b87501..371876b56 100644 +--- a/hotspot/src/share/vm/utilities/array.hpp ++++ b/hotspot/src/share/vm/utilities/array.hpp +@@ -302,6 +302,7 @@ define_array(intArray , int ) define_stack(intStack , intArray ) + + template + class Array: public MetaspaceObj { ++ friend class ArchiveBuilder; + friend class MetadataFactory; + friend class VMStructs; + friend class MethodHandleCompiler; // special case +diff --git a/hotspot/src/share/vm/utilities/bitMap.cpp b/hotspot/src/share/vm/utilities/bitMap.cpp +index e64add155..12b4b4160 100644 +--- a/hotspot/src/share/vm/utilities/bitMap.cpp ++++ b/hotspot/src/share/vm/utilities/bitMap.cpp +@@ -67,16 +67,14 @@ void BitMap::resize(idx_t size_in_bits, bool in_resource_area) { + idx_t new_size_in_words = size_in_words(); + if (in_resource_area) { + _map = NEW_RESOURCE_ARRAY(bm_word_t, new_size_in_words); ++ Copy::disjoint_words((HeapWord*)old_map, (HeapWord*) _map, ++ MIN2(old_size_in_words, new_size_in_words)); + } else { +- if (old_map != NULL) { +- _map_allocator.free(); +- } +- _map = _map_allocator.allocate(new_size_in_words); ++ _map = _map_allocator.reallocate(new_size_in_words); + } +- Copy::disjoint_words((HeapWord*)old_map, (HeapWord*) _map, +- MIN2(old_size_in_words, new_size_in_words)); ++ + if (new_size_in_words > old_size_in_words) { +- clear_range_of_words(old_size_in_words, size_in_words()); ++ clear_range_of_words(old_size_in_words, new_size_in_words); + } + } + +@@ -454,6 +452,11 @@ bool BitMap::is_empty() const { + return rest == 0 || (*word & right_n_bits((int)rest)) == (bm_word_t) NoBits; + } + ++void BitMap::write_to(bm_word_t* buffer, size_t buffer_size_in_bytes) const { ++ assert(buffer_size_in_bytes == (size_in_words() * BytesPerWord), "must be"); ++ memcpy(buffer, _map, size_in_words() * BytesPerWord); ++} ++ + void BitMap::clear_large() { + clear_large_range_of_words(0, size_in_words()); + } +diff --git a/hotspot/src/share/vm/utilities/bitMap.hpp b/hotspot/src/share/vm/utilities/bitMap.hpp +index 51c58da8e..08452bd90 100644 +--- a/hotspot/src/share/vm/utilities/bitMap.hpp ++++ b/hotspot/src/share/vm/utilities/bitMap.hpp +@@ -269,6 +269,7 @@ class BitMap VALUE_OBJ_CLASS_SPEC { + bool is_full() const; + bool is_empty() const; + ++ void write_to(bm_word_t* buffer, size_t buffer_size_in_bytes) const; + void print_on_error(outputStream* st, const char* prefix) const; + + #ifndef PRODUCT +diff --git a/hotspot/src/share/vm/utilities/constantTag.hpp b/hotspot/src/share/vm/utilities/constantTag.hpp +index ae99d5706..07a873743 100644 +--- a/hotspot/src/share/vm/utilities/constantTag.hpp ++++ b/hotspot/src/share/vm/utilities/constantTag.hpp +@@ -43,7 +43,8 @@ enum { + JVM_CONSTANT_UnresolvedClassInError = 103, // Error tag due to resolution error + JVM_CONSTANT_MethodHandleInError = 104, // Error tag due to resolution error + JVM_CONSTANT_MethodTypeInError = 105, // Error tag due to resolution error +- JVM_CONSTANT_InternalMax = 105 // Last implementation tag ++ JVM_CONSTANT_ReplacedSymbol = 106, ++ JVM_CONSTANT_InternalMax = 106 // Last implementation tag + }; + + +@@ -62,7 +63,7 @@ class constantTag VALUE_OBJ_CLASS_SPEC { + bool is_double() const { return _tag == JVM_CONSTANT_Double; } + bool is_name_and_type() const { return _tag == JVM_CONSTANT_NameAndType; } + bool is_utf8() const { return _tag == JVM_CONSTANT_Utf8; } +- ++ bool is_replaced_symbol() const { return _tag == JVM_CONSTANT_ReplacedSymbol; } + bool is_invalid() const { return _tag == JVM_CONSTANT_Invalid; } + + bool is_unresolved_klass() const { +diff --git a/hotspot/src/share/vm/utilities/globalDefinitions.hpp b/hotspot/src/share/vm/utilities/globalDefinitions.hpp +index 81866b840..25f6f026c 100644 +--- a/hotspot/src/share/vm/utilities/globalDefinitions.hpp ++++ b/hotspot/src/share/vm/utilities/globalDefinitions.hpp +@@ -1511,6 +1511,16 @@ static inline void* dereference_vptr(const void* addr) { + return *(void**)addr; + } + ++ ++template unsigned primitive_hash(const K& k) { ++ unsigned hash = (unsigned)((uintptr_t)k); ++ return hash ^ (hash >> 3); // just in case we're dealing with aligned ptrs ++} ++ ++template bool primitive_equals(const K& k0, const K& k1) { ++ return k0 == k1; ++} ++ + #ifndef PRODUCT + + // For unit testing only +@@ -1519,7 +1529,6 @@ public: + static void test_globals(); + static void test_proper_unit(); + }; +- + #endif // PRODUCT + + #endif // SHARE_VM_UTILITIES_GLOBALDEFINITIONS_HPP +diff --git a/hotspot/src/share/vm/utilities/hashtable.cpp b/hotspot/src/share/vm/utilities/hashtable.cpp +index c026e6a0e..66df8f1f8 100644 +--- a/hotspot/src/share/vm/utilities/hashtable.cpp ++++ b/hotspot/src/share/vm/utilities/hashtable.cpp +@@ -34,7 +34,7 @@ + #include "utilities/hashtable.hpp" + #include "utilities/hashtable.inline.hpp" + #include "utilities/numberSeq.hpp" +- ++#include "utilities/align.hpp" + + // This hashtable is implemented as an open hash table with a fixed number of buckets. + +@@ -145,7 +145,7 @@ template void BasicHashtable::free_buckets() { + // Don't delete the buckets in the shared space. They aren't + // allocated by os::malloc + if (!UseSharedSpaces || +- !FileMapInfo::current_info()->is_in_shared_space(_buckets)) { ++ !MetaspaceShared::is_in_shared_space(_buckets)) { + FREE_C_HEAP_ARRAY(HashtableBucket, _buckets, F); + } + _buckets = NULL; +@@ -221,7 +221,7 @@ template void BasicHashtable::copy_table(char** top, char* end) + *top += entry_size(); + } + } +- *plen = (char*)(*top) - (char*)plen - sizeof(*plen); ++ *plen = ((char*)(*top) - (char*)plen) - sizeof(*plen); + + // Set the shared bit. + +@@ -317,7 +317,6 @@ template void RehashableHashtable::dump_table(output + st->print_cr("Maximum bucket size : %9d", (int)summary.maximum()); + } + +- + // Dump the hash table buckets. + + template void BasicHashtable::copy_buckets(char** top, char* end) { +@@ -335,6 +334,57 @@ template void BasicHashtable::copy_buckets(char** top, char* end + *top += len; + } + ++template bool BasicHashtable::resize(int new_size) { ++ ++ // Allocate new buckets ++ HashtableBucket* buckets_new = NEW_C_HEAP_ARRAY2_RETURN_NULL(HashtableBucket, new_size, F, CURRENT_PC); ++ if (buckets_new == NULL) { ++ return false; ++ } ++ ++ // Clear the new buckets ++ for (int i = 0; i < new_size; i++) { ++ buckets_new[i].clear(); ++ } ++ ++ int table_size_old = _table_size; ++ // hash_to_index() uses _table_size, so switch the sizes now ++ _table_size = new_size; ++ ++ // Move entries from the old table to a new table ++ for (int index_old = 0; index_old < table_size_old; index_old++) { ++ for (BasicHashtableEntry* p = _buckets[index_old].get_entry(); p != NULL; ) { ++ BasicHashtableEntry* next = p->next(); ++ int index_new = hash_to_index(p->hash()); ++ ++ p->set_next(buckets_new[index_new].get_entry()); ++ buckets_new[index_new].set_entry(p); ++ p = next; ++ } ++ } ++ ++ // The old backets now can be released ++ BasicHashtable::free_buckets(); ++ ++ // Switch to the new storage ++ _buckets = buckets_new; ++ ++ return true; ++} ++ ++template bool BasicHashtable::maybe_grow(int max_size, int load_factor) { ++ assert(SafepointSynchronize::is_at_safepoint(), "must be at safepoint"); ++ ++ if (table_size() >= max_size) { ++ return false; ++ } ++ if (number_of_entries() / table_size() > load_factor) { ++ resize(MIN2(table_size() * 2, max_size)); ++ return true; ++ } else { ++ return false; ++ } ++} + + #ifndef PRODUCT + +@@ -352,7 +402,6 @@ template void Hashtable::print() { + } + } + +- + template void BasicHashtable::verify() { + int count = 0; + for (int i = 0; i < table_size(); i++) { +@@ -406,3 +455,4 @@ template class BasicHashtable; + template class BasicHashtable; + template class BasicHashtable; + template class BasicHashtable; ++template class BasicHashtable; +diff --git a/hotspot/src/share/vm/utilities/hashtable.hpp b/hotspot/src/share/vm/utilities/hashtable.hpp +index 30e442d15..358b09c3d 100644 +--- a/hotspot/src/share/vm/utilities/hashtable.hpp ++++ b/hotspot/src/share/vm/utilities/hashtable.hpp +@@ -151,7 +151,7 @@ public: + void copy_table(char** top, char* end); + + // Bucket handling +- int hash_to_index(unsigned int full_hash) { ++ int hash_to_index(unsigned int full_hash) const { + int h = full_hash % _table_size; + assert(h >= 0 && h < _table_size, "Illegal hash value"); + return h; +@@ -184,7 +184,7 @@ protected: + int entry_size() const { return _entry_size; } + + // The following method is MT-safe and may be used with caution. +- BasicHashtableEntry* bucket(int i); ++ BasicHashtableEntry* bucket(int i) const; + + // The following method is not MT-safe and must be done under lock. + BasicHashtableEntry** bucket_addr(int i) { return _buckets[i].entry_addr(); } +@@ -234,7 +234,7 @@ protected: + // is mt-safe wrt. to other calls of this method. + void bulk_free_entries(BucketUnlinkContext* context); + public: +- int table_size() { return _table_size; } ++ int table_size() const { return _table_size; } + void set_entry(int index, BasicHashtableEntry* entry); + + void add_entry(int index, BasicHashtableEntry* entry); +@@ -243,6 +243,10 @@ public: + + int number_of_entries() { return _number_of_entries; } + ++ bool resize(int new_size); ++ ++ bool maybe_grow(int max_size, int load_factor = 0); ++ + void verify() PRODUCT_RETURN; + }; + +@@ -364,4 +368,92 @@ public: + } + }; + ++// A subclass of BasicHashtable that allows you to do a simple K -> V mapping ++// without using tons of boilerplate code. ++template< ++ typename K, typename V, MEMFLAGS F, ++ unsigned (*HASH) (K const&) = primitive_hash, ++ bool (*EQUALS)(K const&, K const&) = primitive_equals ++ > ++class KVHashtable : public BasicHashtable { ++ class KVHashtableEntry : public BasicHashtableEntry { ++ public: ++ K _key; ++ V _value; ++ KVHashtableEntry* next() { ++ return (KVHashtableEntry*)BasicHashtableEntry::next(); ++ } ++ }; ++ ++protected: ++ KVHashtableEntry* bucket(int i) const { ++ return (KVHashtableEntry*)BasicHashtable::bucket(i); ++ } ++ ++ KVHashtableEntry* new_entry(unsigned int hashValue, K key, V value) { ++ KVHashtableEntry* entry = (KVHashtableEntry*)BasicHashtable::new_entry(hashValue); ++ entry->_key = key; ++ entry->_value = value; ++ return entry; ++ } ++ ++public: ++ KVHashtable(int table_size) : BasicHashtable(table_size, sizeof(KVHashtableEntry)) {} ++ ++ V* add(K key, V value) { ++ unsigned int hash = HASH(key); ++ KVHashtableEntry* entry = new_entry(hash, key, value); ++ BasicHashtable::add_entry(BasicHashtable::hash_to_index(hash), entry); ++ return &(entry->_value); ++ } ++ ++ V* lookup(K key) const { ++ unsigned int hash = HASH(key); ++ int index = BasicHashtable::hash_to_index(hash); ++ for (KVHashtableEntry* e = bucket(index); e != NULL; e = e->next()) { ++ if (e->hash() == hash && EQUALS(e->_key, key)) { ++ return &(e->_value); ++ } ++ } ++ return NULL; ++ } ++ ++ // Look up the key. ++ // If an entry for the key exists, leave map unchanged and return a pointer to its value. ++ // If no entry for the key exists, create a new entry from key and value and return a ++ // pointer to the value. ++ // *p_created is true if entry was created, false if entry pre-existed. ++ V* add_if_absent(K key, V value, bool* p_created) { ++ unsigned int hash = HASH(key); ++ int index = BasicHashtable::hash_to_index(hash); ++ for (KVHashtableEntry* e = bucket(index); e != NULL; e = e->next()) { ++ if (e->hash() == hash && EQUALS(e->_key, key)) { ++ *p_created = false; ++ return &(e->_value); ++ } ++ } ++ KVHashtableEntry* entry = new_entry(hash, key, value); ++ BasicHashtable::add_entry(BasicHashtable::hash_to_index(hash), entry); ++ *p_created = true; ++ return &(entry->_value); ++ } ++ ++ int table_size() const { ++ return BasicHashtable::table_size(); ++ } ++ ++ // ITER contains bool do_entry(K, V const&), which will be ++ // called for each entry in the table. If do_entry() returns false, ++ // the iteration is cancelled. ++ template ++ void iterate(ITER* iter) const { ++ for (int index = 0; index < table_size(); index++) { ++ for (KVHashtableEntry* e = bucket(index); e != NULL; e = e->next()) { ++ bool cont = iter->do_entry(e->_key, &e->_value); ++ if (!cont) { return; } ++ } ++ } ++ } ++}; ++ + #endif // SHARE_VM_UTILITIES_HASHTABLE_HPP +diff --git a/hotspot/src/share/vm/utilities/hashtable.inline.hpp b/hotspot/src/share/vm/utilities/hashtable.inline.hpp +index 9356c985e..ee22ba835 100644 +--- a/hotspot/src/share/vm/utilities/hashtable.inline.hpp ++++ b/hotspot/src/share/vm/utilities/hashtable.inline.hpp +@@ -72,7 +72,7 @@ template inline void BasicHashtable::initialize(int table_size, + + + // The following method is MT-safe and may be used with caution. +-template inline BasicHashtableEntry* BasicHashtable::bucket(int i) { ++template inline BasicHashtableEntry* BasicHashtable::bucket(int i) const { + return _buckets[i].get_entry(); + } + +diff --git a/hotspot/src/share/vm/utilities/ostream.cpp b/hotspot/src/share/vm/utilities/ostream.cpp +index fa199a235..14d82ad0f 100644 +--- a/hotspot/src/share/vm/utilities/ostream.cpp ++++ b/hotspot/src/share/vm/utilities/ostream.cpp +@@ -379,6 +379,7 @@ xmlStream* xtty; + outputStream* tty; + outputStream* gclog_or_tty; + CDS_ONLY(jsaFileStream* classlist_file;) // Only dump the classes that can be stored into the CDS archive ++CDS_ONLY(outputStream* dynamic_cds_log;) + extern Mutex* tty_lock; + + #define EXTRACHARLEN 32 +@@ -1402,6 +1403,16 @@ void ostream_init_log() { + jsaFileStream(list_name); + FREE_C_HEAP_ARRAY(char, list_name, mtInternal); + } ++ ++ // For -XX:DynamicCDSLog= option ++ if (DynamicCDSLog != NULL) { ++ const char* log_name = make_log_name(DynamicCDSLog, NULL); ++ dynamic_cds_log = new(ResourceObj::C_HEAP, mtInternal) ++ fileStream(log_name); ++ FREE_C_HEAP_ARRAY(char, log_name, mtInternal); ++ } else { ++ dynamic_cds_log = tty; ++ } + #endif + + // If we haven't lazily initialized the logfile yet, do it now, +diff --git a/hotspot/src/share/vm/utilities/ostream.hpp b/hotspot/src/share/vm/utilities/ostream.hpp +index c69289fb5..d0f9aac57 100644 +--- a/hotspot/src/share/vm/utilities/ostream.hpp ++++ b/hotspot/src/share/vm/utilities/ostream.hpp +@@ -221,7 +221,7 @@ class jsaFileStream : public fileStream { + }; + + CDS_ONLY(extern jsaFileStream* classlist_file;) +- ++CDS_ONLY(extern outputStream* dynamic_cds_log;) + // unlike fileStream, fdStream does unbuffered I/O by calling + // open() and write() directly. It is async-safe, but output + // from multiple thread may be mixed together. Used by fatal +diff --git a/hotspot/src/share/vm/utilities/resourceHash.hpp b/hotspot/src/share/vm/utilities/resourceHash.hpp +index 82c1219b4..941f25996 100644 +--- a/hotspot/src/share/vm/utilities/resourceHash.hpp ++++ b/hotspot/src/share/vm/utilities/resourceHash.hpp +@@ -27,21 +27,13 @@ + + #include "memory/allocation.hpp" + #include "utilities/top.hpp" ++#include "utilities/globalDefinitions.hpp" + + template struct ResourceHashtableFns { + typedef unsigned (*hash_fn)(K const&); + typedef bool (*equals_fn)(K const&, K const&); + }; + +-template unsigned primitive_hash(const K& k) { +- unsigned hash = (unsigned)((uintptr_t)k); +- return hash ^ (hash >> 3); // just in case we're dealing with aligned ptrs +-} +- +-template bool primitive_equals(const K& k0, const K& k1) { +- return k0 == k1; +-} +- + template< + typename K, typename V, + // xlC does not compile this: +@@ -66,6 +58,10 @@ class ResourceHashtable : public ResourceObj { + + Node(unsigned hash, K const& key, V const& value) : + _hash(hash), _key(key), _value(value), _next(NULL) {} ++ ++ // Create a node with a default-constructed value. ++ Node(unsigned hash, K const& key) : ++ _hash(hash), _key(key), _value(), _next(NULL) {} + }; + + Node* _table[SIZE]; +@@ -139,6 +135,19 @@ class ResourceHashtable : public ResourceObj { + } + } + ++ V* put_if_absent(K const& key, bool* p_created) { ++ unsigned hv = HASH(key); ++ Node** ptr = lookup_node(hv, key); ++ if (*ptr == NULL) { ++ *ptr = new (ALLOC_TYPE, MEM_TYPE) Node(hv, key); ++ *p_created = true; ++ } else { ++ *p_created = false; ++ } ++ return &(*ptr)->_value; ++ } ++ ++ + bool remove(K const& key) { + unsigned hv = HASH(key); + Node** ptr = lookup_node(hv, key); +-- +2.17.1 + diff --git a/Fix-AsyncGCLog-s-content-consistent-bug.patch b/Fix-AsyncGCLog-s-content-consistent-bug.patch new file mode 100644 index 0000000000000000000000000000000000000000..bf8229d420ebd94dbd74efc4589c0b1bfb482fc0 --- /dev/null +++ b/Fix-AsyncGCLog-s-content-consistent-bug.patch @@ -0,0 +1,38 @@ +From a9c12b1881b227e537089c14bfcc3a00cfc7c1ac Mon Sep 17 00:00:00 2001 +From: eapen +Date: Mon, 19 Dec 2022 21:12:55 +0800 +Subject: [PATCH 33/33] I68TO2: Fix AsyncGCLog's content consistent bug +--- + hotspot/src/share/vm/runtime/java.cpp | 10 +++++----- + 1 file changed, 5 insertions(+), 5 deletions(-) + +diff --git a/hotspot/src/share/vm/runtime/java.cpp b/hotspot/src/share/vm/runtime/java.cpp +index e2194dd..5b82a7a 100644 +--- a/hotspot/src/share/vm/runtime/java.cpp ++++ b/hotspot/src/share/vm/runtime/java.cpp +@@ -516,11 +516,6 @@ void before_exit(JavaThread * thread) { + // Stop concurrent GC threads + Universe::heap()->stop(); + +- // Stop async log writer thread +- if (UseAsyncGCLog) { +- AsyncLogWriter::instance()->stop(); +- } +- + // Print GC/heap related information. + if (PrintGCDetails) { + Universe::print(); +@@ -584,6 +579,11 @@ void before_exit(JavaThread * thread) { + } + } + ++ // Stop async log writer thread ++ if (UseAsyncGCLog) { ++ AsyncLogWriter::instance()->stop(); ++ } ++ + #undef BEFORE_EXIT_NOT_RUN + #undef BEFORE_EXIT_RUNNING + #undef BEFORE_EXIT_DONE +-- +1.8.3.1 diff --git a/Fix-compactibleFreeListSpace-block_size_no_stall-cra.patch b/Fix-compactibleFreeListSpace-block_size_no_stall-cra.patch new file mode 100644 index 0000000000000000000000000000000000000000..d231dc324d9155b3d12fed8e82ae7c5fb721250c --- /dev/null +++ b/Fix-compactibleFreeListSpace-block_size_no_stall-cra.patch @@ -0,0 +1,30 @@ +From 3d9fd51e13f76861e21293143b23c6936e030dfc Mon Sep 17 00:00:00 2001 +From: eapen +Date: Wed, 14 Dec 2022 16:54:06 +0800 +Subject: [PATCH 16/33] I68TO2: Fix compactibleFreeListSpace::block_size_no_stall crash + when use JMap parallel inspection of CMS GC +--- + .../concurrentMarkSweep/concurrentMarkSweepGeneration.cpp | 7 ++++--- + 1 file changed, 4 insertions(+), 3 deletions(-) + +diff --git a/hotspot/src/share/vm/gc_implementation/concurrentMarkSweep/concurrentMarkSweepGeneration.cpp b/hotspot/src/share/vm/gc_implementation/concurrentMarkSweep/concurrentMarkSweepGeneration.cpp +index d31f9a5..c923e85 100644 +--- a/hotspot/src/share/vm/gc_implementation/concurrentMarkSweep/concurrentMarkSweepGeneration.cpp ++++ b/hotspot/src/share/vm/gc_implementation/concurrentMarkSweep/concurrentMarkSweepGeneration.cpp +@@ -2896,10 +2896,11 @@ void ConcurrentMarkSweepGeneration::object_iterate_block(ObjectClosure *cl, size + } + } + } +- if (prev_obj < span.end()) { +- HeapWord *cur, *limit; ++ HeapWord *limit = MIN2(cmsSpace()->end(), span.end()); ++ if (prev_obj < limit) { ++ HeapWord *cur; + size_t curSize; +- for (cur = prev_obj, limit = span.end(); cur < limit; cur += curSize) { ++ for (cur = prev_obj; cur < limit; cur += curSize) { + curSize = cmsSpace()->block_size_no_stall(cur, _collector); + if (curSize == 0) { + break; +-- +1.8.3.1 diff --git a/Fix-compile-and-runtime-failures-for-minimal1-versio.patch b/Fix-compile-and-runtime-failures-for-minimal1-versio.patch new file mode 100644 index 0000000000000000000000000000000000000000..8733ee419cf43981d1e493966c77ba9783552672 --- /dev/null +++ b/Fix-compile-and-runtime-failures-for-minimal1-versio.patch @@ -0,0 +1,183 @@ +From d915916d5a7f3280270ea4207e4d3892ffa7de04 Mon Sep 17 00:00:00 2001 +Date: Mon, 11 Apr 2022 17:14:06 +0800 +Subject: [PATCH] Fix compile and runtime failures for minimal1 version + +Reference: NA +Summary: < JDK> : Fix compile and runtime failures for minimal1 version +--- + .../src/share/vm/classfile/systemDictionary.cpp | 30 ++++++++++------------ + .../parallelScavenge/psMarkSweep.hpp | 2 +- + hotspot/src/share/vm/prims/jvm.cpp | 12 +++++++++ + hotspot/src/share/vm/prims/jvmtiImpl.hpp | 8 +++--- + hotspot/src/share/vm/runtime/memprofiler.cpp | 2 +- + hotspot/src/share/vm/utilities/taskqueue.cpp | 2 ++ + hotspot/src/share/vm/utilities/taskqueue.hpp | 4 +-- + .../com/huawei/jvm/gc/AdaptiveHeapMXBeanImpl.c | 1 - + 8 files changed, 36 insertions(+), 25 deletions(-) + +diff --git a/hotspot/src/share/vm/classfile/systemDictionary.cpp b/hotspot/src/share/vm/classfile/systemDictionary.cpp +index 0d11abfa..794ee9b1 100644 +--- a/hotspot/src/share/vm/classfile/systemDictionary.cpp ++++ b/hotspot/src/share/vm/classfile/systemDictionary.cpp +@@ -1093,19 +1093,6 @@ Klass* SystemDictionary::parse_stream(Symbol* class_name, + return k(); + } + +-static char* convert_into_package_name(char* name) { +- char* index = strrchr(name, '/'); +- if (index == NULL) { +- return NULL; +- } else { +- *index = '\0'; // chop to just the package name +- while ((index = strchr(name, '/')) != NULL) { +- *index = '.'; // replace '/' with '.' in package name +- } +- return name; +- } +-} +- + static bool is_prohibited_package_slow(Symbol* class_name) { + // Caller has ResourceMark + int length; +@@ -1252,6 +1239,18 @@ void SystemDictionary::set_shared_dictionary(HashtableBucket* t, int le + _shared_dictionary = new Dictionary(_nof_buckets, t, number_of_entries); + } + ++static char* convert_into_package_name(char* name) { ++ char* index = strrchr(name, '/'); ++ if (index == NULL) { ++ return NULL; ++ } else { ++ *index = '\0'; // chop to just the package name ++ while ((index = strchr(name, '/')) != NULL) { ++ *index = '.'; // replace '/' with '.' in package name ++ } ++ return name; ++ } ++} + + // If there is a shared dictionary, then find the entry for the + // given shared system class, if any. +@@ -1267,7 +1266,6 @@ Klass* SystemDictionary::find_shared_class(Symbol* class_name) { + } + } + +- + // Load a class from the shared spaces (found through the shared system + // dictionary). Force the superclass and all interfaces to be loaded. + // Update the class definition to include sibling classes and no +diff --git a/hotspot/src/share/vm/gc_implementation/parallelScavenge/psMarkSweep.hpp b/hotspot/src/share/vm/gc_implementation/parallelScavenge/psMarkSweep.hpp +index 01666ea4d..deeca7bb5 100644 +--- a/hotspot/src/share/vm/gc_implementation/parallelScavenge/psMarkSweep.hpp ++++ b/hotspot/src/share/vm/gc_implementation/parallelScavenge/psMarkSweep.hpp +@@ -77,7 +77,7 @@ class PSMarkSweep : public MarkSweep { + + // Reset time since last full gc + static void reset_millis_since_last_gc(); +- static void ps_marksweep_init(); ++ static void ps_marksweep_init() NOT_ALL_GCS_RETURN; + + public: + static inline PSMarkSweep* the_ps_mark() { return (PSMarkSweep*)_the_ps_mark; } +diff --git a/hotspot/src/share/vm/prims/jvm.cpp b/hotspot/src/share/vm/prims/jvm.cpp +index c27a534ef..f75501dba 100644 +--- a/hotspot/src/share/vm/prims/jvm.cpp ++++ b/hotspot/src/share/vm/prims/jvm.cpp +@@ -3303,20 +3303,32 @@ JVM_END + + JVM_ENTRY(void, JVM_AdaptiveHeapSetG1PeriodicGCInterval(JNIEnv *env, jclass klass, jint interval)) + JVMWrapper("JVM_AdaptiveHeapSetG1PeriodicGCInterval"); ++#if INCLUDE_ALL_GCS + G1PeriodicGCInterval = interval; ++#endif + JVM_END + JVM_ENTRY(jint, JVM_AdaptiveHeapGetG1PeriodicGCInterval(JNIEnv *env, jclass klass)) + JVMWrapper("JVM_AdaptiveHeapGetG1PeriodicGCInterval"); ++#if INCLUDE_ALL_GCS + return G1PeriodicGCInterval; ++#else ++ return -1; ++#endif + JVM_END + + JVM_ENTRY(void, JVM_AdaptiveHeapSetG1PeriodicGCLoadThreshold(JNIEnv *env, jclass clazz, jint loadThreshold)) + JVMWrapper("JVM_AdaptiveHeapSetG1PeriodicGCLoadThreshold"); ++#if INCLUDE_ALL_GCS + G1PeriodicGCLoadThreshold = loadThreshold; ++#endif + JVM_END + JVM_ENTRY(jint, JVM_AdaptiveHeapGetG1PeriodicGCLoadThreshold(JNIEnv *env, jclass clazz)) + JVMWrapper("JVM_AdaptiveHeapgetG1PeriodicGCLoadThreshold"); ++#if INCLUDE_ALL_GCS + return G1PeriodicGCLoadThreshold; ++#else ++ return -1; ++#endif + JVM_END + + JVM_ENTRY(void, JVM_Yield(JNIEnv *env, jclass threadClass)) +diff --git a/hotspot/src/share/vm/runtime/memprofiler.cpp b/hotspot/src/share/vm/runtime/memprofiler.cpp +index ddb22601f..a956c5252 100644 +--- a/hotspot/src/share/vm/runtime/memprofiler.cpp ++++ b/hotspot/src/share/vm/runtime/memprofiler.cpp +@@ -126,7 +126,7 @@ void MemProfiler::do_trace() { + + fprintf(_log_fp, UINTX_FORMAT_W(6) ",", CodeCache::capacity() / K); + +- fprintf(_log_fp, UINTX_FORMAT_W(6) "," UINTX_FORMAT_W(6) "," UINTX_FORMAT_W(6) "\n", ++ fprintf(_log_fp, UINTX_FORMAT_W(6) "," UINTX_FORMAT_W(6) ",%6ld\n", + handles_memory_usage / K, + resource_memory_usage / K, + 0L); +diff --git a/hotspot/src/share/vm/utilities/taskqueue.cpp b/hotspot/src/share/vm/utilities/taskqueue.cpp +index 120c65a60..7c6849c63 100644 +--- a/hotspot/src/share/vm/utilities/taskqueue.cpp ++++ b/hotspot/src/share/vm/utilities/taskqueue.cpp +@@ -273,10 +273,12 @@ void ParallelTaskTerminator::reset_for_reuse(int n_threads) { + _n_threads = n_threads; + } + ++#if INCLUDE_ALL_GCS + TaskTerminator::TaskTerminator(uint n_threads, TaskQueueSetSuper* queue_set) : + _terminator(UseOWSTTaskTerminator ? new OWSTTaskTerminator(n_threads, queue_set) + : new ParallelTaskTerminator(n_threads, queue_set)) { + } ++#endif + + TaskTerminator::~TaskTerminator() { + if (_terminator != NULL) { +diff --git a/hotspot/src/share/vm/utilities/taskqueue.hpp b/hotspot/src/share/vm/utilities/taskqueue.hpp +index 959d0dd21..284675708 100644 +--- a/hotspot/src/share/vm/utilities/taskqueue.hpp ++++ b/hotspot/src/share/vm/utilities/taskqueue.hpp +@@ -763,7 +763,7 @@ private: + TaskTerminator(const TaskTerminator& o) { } + TaskTerminator& operator=(TaskTerminator& o) { return *this; } + public: +- TaskTerminator(uint n_threads, TaskQueueSetSuper* queue_set); ++ TaskTerminator(uint n_threads, TaskQueueSetSuper* queue_set) NOT_ALL_GCS_RETURN; + ~TaskTerminator(); + + // Move assignment +@@ -929,4 +929,4 @@ typedef OverflowTaskQueue RegionTaskQueue; + typedef GenericTaskQueueSet RegionTaskQueueSet; + + +-#endif // SHARE_VM_UTILITIES_TASKQUEUE_HPP +\ No newline at end of file ++#endif // SHARE_VM_UTILITIES_TASKQUEUE_HPP +diff --git a/jdk/src/share/native/com/huawei/jvm/gc/AdaptiveHeapMXBeanImpl.c b/jdk/src/share/native/com/huawei/jvm/gc/AdaptiveHeapMXBeanImpl.c +index 99bfff885..0e365d7aa 100644 +--- a/jdk/src/share/native/com/huawei/jvm/gc/AdaptiveHeapMXBeanImpl.c ++++ b/jdk/src/share/native/com/huawei/jvm/gc/AdaptiveHeapMXBeanImpl.c +@@ -31,7 +31,6 @@ static JNINativeMethod methods[] = { + {"getG1PeriodicGCIntervalImpl", "()I", (void *)&JVM_AdaptiveHeapGetG1PeriodicGCInterval}, + {"setG1PeriodicGCLoadThresholdImpl", "(I)V", (void *)&JVM_AdaptiveHeapSetG1PeriodicGCLoadThreshold}, + {"getG1PeriodicGCLoadThresholdImpl", "()I", (void *)&JVM_AdaptiveHeapGetG1PeriodicGCLoadThreshold}, +- + }; + + JNIEXPORT void JNICALL +-- +2.12.3 + diff --git a/Fix-the-crash-that-occurs-when-the-process-exits-due.patch b/Fix-the-crash-that-occurs-when-the-process-exits-due.patch new file mode 100644 index 0000000000000000000000000000000000000000..47904e80d237b7002ef06289a92d927e23e067a5 --- /dev/null +++ b/Fix-the-crash-that-occurs-when-the-process-exits-due.patch @@ -0,0 +1,116 @@ +From e635dce083e968ed54f8c7b7b059ce8c3c9ee717 Mon Sep 17 00:00:00 2001 +From: eapen +Date: Fri, 16 Dec 2022 16:00:25 +0800 +Subject: [PATCH 32/33] I68TO2: Fix the crash that occurs when the process exits due to + the mixed use of GCTrimNativeHeap and UseAsyncGCLog +--- + hotspot/src/share/vm/runtime/java.cpp | 6 +++++ + hotspot/src/share/vm/runtime/logAsyncWriter.cpp | 35 ++++++++++++++++++++++++- + hotspot/src/share/vm/runtime/logAsyncWriter.hpp | 4 +++ + 3 files changed, 44 insertions(+), 1 deletion(-) + +diff --git a/hotspot/src/share/vm/runtime/java.cpp b/hotspot/src/share/vm/runtime/java.cpp +index 54b980d..e2194dd 100644 +--- a/hotspot/src/share/vm/runtime/java.cpp ++++ b/hotspot/src/share/vm/runtime/java.cpp +@@ -54,6 +54,7 @@ + #include "runtime/init.hpp" + #include "runtime/interfaceSupport.hpp" + #include "runtime/java.hpp" ++#include "runtime/logAsyncWriter.hpp" + #include "runtime/memprofiler.hpp" + #include "runtime/sharedRuntime.hpp" + #include "runtime/statSampler.hpp" +@@ -515,6 +516,11 @@ void before_exit(JavaThread * thread) { + // Stop concurrent GC threads + Universe::heap()->stop(); + ++ // Stop async log writer thread ++ if (UseAsyncGCLog) { ++ AsyncLogWriter::instance()->stop(); ++ } ++ + // Print GC/heap related information. + if (PrintGCDetails) { + Universe::print(); +diff --git a/hotspot/src/share/vm/runtime/logAsyncWriter.cpp b/hotspot/src/share/vm/runtime/logAsyncWriter.cpp +index 750a23f..7722020 100644 +--- a/hotspot/src/share/vm/runtime/logAsyncWriter.cpp ++++ b/hotspot/src/share/vm/runtime/logAsyncWriter.cpp +@@ -63,7 +63,7 @@ void AsyncLogWriter::enqueue(const char* msg) { + AsyncLogWriter::AsyncLogWriter() + : NamedThread(), + _lock(1), _sem(0), _io_sem(1), +- _initialized(false), ++ _initialized(false),_should_terminate(false),_has_terminated(false), + _buffer_max_size(AsyncLogBufferSize / sizeof(AsyncLogMessage)) { + if (os::create_thread(this, os::asynclog_thread)) { + _initialized = true; +@@ -124,6 +124,10 @@ void AsyncLogWriter::run() { + // The value of a semphore cannot be negative. Therefore, the current thread falls asleep + // when its value is zero. It will be waken up when new messages are enqueued. + _sem.wait(); ++ if (_should_terminate) { ++ terminate(); ++ break; ++ } + write(); + } + } +@@ -162,3 +166,32 @@ void AsyncLogWriter::print_on(outputStream* st) const{ + Thread::print_on(st); + st->cr(); + } ++ ++void AsyncLogWriter::stop() { ++ { ++ MutexLockerEx ml(Terminator_lock); ++ _should_terminate = true; ++ } ++ { ++ _sem.signal(); ++ } ++ { ++ MutexLockerEx ml(Terminator_lock); ++ while (!_has_terminated) { ++ Terminator_lock->wait(); ++ } ++ } ++} ++ ++void AsyncLogWriter::terminate() { ++ // Signal that it is terminated ++ { ++ MutexLockerEx mu(Terminator_lock, ++ Mutex::_no_safepoint_check_flag); ++ _has_terminated = true; ++ Terminator_lock->notify(); ++ } ++ ++ // Thread destructor usually does this.. ++ ThreadLocalStorage::set_thread(NULL); ++} +diff --git a/hotspot/src/share/vm/runtime/logAsyncWriter.hpp b/hotspot/src/share/vm/runtime/logAsyncWriter.hpp +index 5242426..54e5d48 100644 +--- a/hotspot/src/share/vm/runtime/logAsyncWriter.hpp ++++ b/hotspot/src/share/vm/runtime/logAsyncWriter.hpp +@@ -136,6 +136,8 @@ class AsyncLogWriter : public NamedThread { + Semaphore _io_sem; + + volatile bool _initialized; ++ volatile bool _should_terminate; ++ volatile bool _has_terminated; + AsyncLogBuffer _buffer; + + const size_t _buffer_max_size; +@@ -153,6 +155,8 @@ class AsyncLogWriter : public NamedThread { + static void flush(); + // Printing + void print_on(outputStream* st) const; ++ void stop(); ++ void terminate(); + + }; + +-- +1.8.3.1 diff --git a/Huawei-fix-windows-build-Dynamic-CDS-failure.patch b/Huawei-fix-windows-build-Dynamic-CDS-failure.patch new file mode 100644 index 0000000000000000000000000000000000000000..253dc9ed38395ed0b62211b96501e6796b0b0427 --- /dev/null +++ b/Huawei-fix-windows-build-Dynamic-CDS-failure.patch @@ -0,0 +1,44 @@ +From 90eec1e71cb818dae6d0ed1be7f7a7c3fe9da1cf Mon Sep 17 00:00:00 2001 +From: zhangyipeng +Date: Fri, 21 Oct 2022 11:24:48 +0800 +Subject: [PATCH] fix windows build Dynamic CDS failure + +--- + hotspot/make/windows/makefiles/vm.make | 7 +++++++ + 1 file changed, 7 insertions(+) + +diff --git a/hotspot/make/windows/makefiles/vm.make b/hotspot/make/windows/makefiles/vm.make +index e303da77b..5322a4b4b 100644 +--- a/hotspot/make/windows/makefiles/vm.make ++++ b/hotspot/make/windows/makefiles/vm.make +@@ -148,6 +148,7 @@ VM_PATH=$(VM_PATH);$(WorkSpace)/src/share/vm/code + VM_PATH=$(VM_PATH);$(WorkSpace)/src/share/vm/interpreter + VM_PATH=$(VM_PATH);$(WorkSpace)/src/share/vm/ci + VM_PATH=$(VM_PATH);$(WorkSpace)/src/share/vm/classfile ++VM_PATH=$(VM_PATH);$(WorkSpace)/src/share/vm/cds + VM_PATH=$(VM_PATH);$(WorkSpace)/src/share/vm/gc_implementation/parallelScavenge + VM_PATH=$(VM_PATH);$(WorkSpace)/src/share/vm/gc_implementation/shared + VM_PATH=$(VM_PATH);$(WorkSpace)/src/share/vm/gc_implementation/parNew +@@ -233,6 +234,9 @@ arguments.obj: $(WorkSpace)\src\share\vm\runtime\arguments.cpp + {$(COMMONSRC)\share\vm\classfile}.cpp.obj:: + $(CXX) $(CXX_FLAGS) $(CXX_USE_PCH) /c $< + ++{$(COMMONSRC)\share\vm\cds}.cpp.obj:: ++ $(CXX) $(CXX_FLAGS) $(CXX_USE_PCH) /c $< ++ + {$(COMMONSRC)\share\vm\gc_implementation\parallelScavenge}.cpp.obj:: + $(CXX) $(CXX_FLAGS) $(CXX_USE_PCH) /c $< + +@@ -316,6 +320,9 @@ arguments.obj: $(WorkSpace)\src\share\vm\runtime\arguments.cpp + {$(ALTSRC)\share\vm\classfile}.cpp.obj:: + $(CXX) $(CXX_FLAGS) $(CXX_USE_PCH) /c $< + ++{$(ALTSRC)\share\vm\cds}.cpp.obj:: ++ $(CXX) $(CXX_FLAGS) $(CXX_USE_PCH) /c $< ++ + {$(ALTSRC)\share\vm\gc_implementation\parallelScavenge}.cpp.obj:: + $(CXX) $(CXX_FLAGS) $(CXX_USE_PCH) /c $< + +-- +2.22.0 + diff --git a/improve_algorithmConstraints_checkAlgorithm_performance.patch b/Improve_AlgorithmConstraints_checkAlgorithm_performance.patch old mode 100755 new mode 100644 similarity index 90% rename from improve_algorithmConstraints_checkAlgorithm_performance.patch rename to Improve_AlgorithmConstraints_checkAlgorithm_performance.patch index 025a379bf25ec534b537a9b40c35897e2b2343b9..cf5c5e4aec61758983d6babbaea063ed07d2f9ae --- a/improve_algorithmConstraints_checkAlgorithm_performance.patch +++ b/Improve_AlgorithmConstraints_checkAlgorithm_performance.patch @@ -1,5 +1,17 @@ +From 4e520a51acbb192a0df844fcca247998d7fb8854 Mon Sep 17 00:00:00 2001 +From: wangkun +Date: Thu, 28 Jul 2022 17:19:32 +0800 +Subject: [PATCH 2/3] add + Improve-AlgorithmConstraints-checkAlgorithm-performa.patch + +--- + .../util/AbstractAlgorithmConstraints.java | 30 +++++++------------ + .../util/DisabledAlgorithmConstraints.java | 20 +++++++++---- + .../util/LegacyAlgorithmConstraints.java | 12 ++++++-- + 3 files changed, 35 insertions(+), 27 deletions(-) + diff --git a/jdk/src/share/classes/sun/security/util/AbstractAlgorithmConstraints.java b/jdk/src/share/classes/sun/security/util/AbstractAlgorithmConstraints.java -index 944958de4..5c7602925 100644 +index 944958de..5c760292 100644 --- a/jdk/src/share/classes/sun/security/util/AbstractAlgorithmConstraints.java +++ b/jdk/src/share/classes/sun/security/util/AbstractAlgorithmConstraints.java @@ -77,34 +77,26 @@ public abstract class AbstractAlgorithmConstraints @@ -49,7 +61,7 @@ index 944958de4..5c7602925 100644 return true; diff --git a/jdk/src/share/classes/sun/security/util/DisabledAlgorithmConstraints.java b/jdk/src/share/classes/sun/security/util/DisabledAlgorithmConstraints.java -index 51e625632..6ff26bf2f 100644 +index 51e62563..6ff26bf2 100644 --- a/jdk/src/share/classes/sun/security/util/DisabledAlgorithmConstraints.java +++ b/jdk/src/share/classes/sun/security/util/DisabledAlgorithmConstraints.java @@ -96,7 +96,7 @@ public class DisabledAlgorithmConstraints extends AbstractAlgorithmConstraints { @@ -99,7 +111,7 @@ index 51e625632..6ff26bf2f 100644 /* diff --git a/jdk/src/share/classes/sun/security/util/LegacyAlgorithmConstraints.java b/jdk/src/share/classes/sun/security/util/LegacyAlgorithmConstraints.java -index 4e7502fb5..01d0447ab 100644 +index 4e7502fb..01d0447a 100644 --- a/jdk/src/share/classes/sun/security/util/LegacyAlgorithmConstraints.java +++ b/jdk/src/share/classes/sun/security/util/LegacyAlgorithmConstraints.java @@ -28,6 +28,7 @@ package sun.security.util; @@ -132,3 +144,6 @@ index 4e7502fb5..01d0447ab 100644 } @Override +-- +2.22.0 + diff --git a/LoongArch64-support-jdk8u352b08.patch b/LoongArch64-support-jdk8u352b08.patch new file mode 100644 index 0000000000000000000000000000000000000000..796c7704923f3b14750ba690678be84ad5d3ef18 --- /dev/null +++ b/LoongArch64-support-jdk8u352b08.patch @@ -0,0 +1,116934 @@ +diff --git a/common/autoconf/build-aux/autoconf-config.guess b/common/autoconf/build-aux/autoconf-config.guess +index 15ee438926..3d7555b52d 100644 +--- a/common/autoconf/build-aux/autoconf-config.guess ++++ b/common/autoconf/build-aux/autoconf-config.guess +@@ -977,6 +977,9 @@ EOF + eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep '^CPU'` + test x"${CPU}" != x && { echo "${CPU}-unknown-linux-gnu"; exit; } + ;; ++ loongarch64:Linux:*:*) ++ echo ${UNAME_MACHINE}-unknown-linux-gnu ++ exit ;; + or32:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-gnu + exit ;; +diff --git a/common/autoconf/build-aux/autoconf-config.sub b/common/autoconf/build-aux/autoconf-config.sub +index 1aab2b303e..bd910bddbe 100644 +--- a/common/autoconf/build-aux/autoconf-config.sub ++++ b/common/autoconf/build-aux/autoconf-config.sub +@@ -275,6 +275,7 @@ case $basic_machine in + | h8300 | h8500 | hppa | hppa1.[01] | hppa2.0 | hppa2.0[nw] | hppa64 \ + | i370 | i860 | i960 | ia64 \ + | ip2k | iq2000 \ ++ | loongarch | loongarch64 \ + | m32c | m32r | m32rle | m68000 | m68k | m88k \ + | maxq | mb | microblaze | mcore | mep \ + | mips | mipsbe | mipseb | mipsel | mipsle \ +diff --git a/common/autoconf/build-aux/config.guess b/common/autoconf/build-aux/config.guess +index 355c91e4eb..d03d029ce3 100644 +--- a/common/autoconf/build-aux/config.guess ++++ b/common/autoconf/build-aux/config.guess +@@ -86,4 +86,15 @@ if [ "x$OUT" = x ]; then + fi + fi + ++# Test and fix little endian MIPS. ++if [ "x$OUT" = x ]; then ++ if [ `uname -s` = Linux ]; then ++ if [ `uname -m` = mipsel ]; then ++ OUT=mipsel-unknown-linux-gnu ++ elif [ `uname -m` = mips64el ]; then ++ OUT=mips64el-unknown-linux-gnu ++ fi ++ fi ++fi ++ + echo $OUT +diff --git a/common/autoconf/configure.ac b/common/autoconf/configure.ac +index 151e5a109f..5072409dd4 100644 +--- a/common/autoconf/configure.ac ++++ b/common/autoconf/configure.ac +@@ -23,6 +23,12 @@ + # questions. + # + ++# ++# This file has been modified by Loongson Technology in 2018. These ++# modifications are Copyright (c) 2018 Loongson Technology, and are made ++# available on the same license terms set forth above. ++# ++ + ############################################################################### + # + # Includes and boilerplate +@@ -186,6 +192,7 @@ FLAGS_SETUP_INIT_FLAGS + # Now we can test some aspects on the target using configure macros. + PLATFORM_SETUP_OPENJDK_TARGET_BITS + PLATFORM_SETUP_OPENJDK_TARGET_ENDIANNESS ++GET_BUILDER_AND_HOST_DATA + + # Configure flags for the tools + FLAGS_SETUP_COMPILER_FLAGS_FOR_LIBS +diff --git a/common/autoconf/generated-configure.sh b/common/autoconf/generated-configure.sh +index 71fabf4dbb..62537aced2 100644 +--- a/common/autoconf/generated-configure.sh ++++ b/common/autoconf/generated-configure.sh +@@ -715,6 +715,9 @@ SET_EXECUTABLE_ORIGIN + SHARED_LIBRARY_FLAGS + CXX_FLAG_REORDER + C_FLAG_REORDER ++HOST_NAME ++BUILDER_NAME ++BUILDER_ID + SYSROOT_LDFLAGS + SYSROOT_CFLAGS + RC_FLAGS +@@ -4074,6 +4077,12 @@ fi + # questions. + # + ++# ++# This file has been modified by Loongson Technology in 2022. These ++# modifications are Copyright (c) 2018, 2022, Loongson Technology, and are made ++# available on the same license terms set forth above. ++# ++ + # Support macro for PLATFORM_EXTRACT_TARGET_AND_BUILD. + # Converts autoconf style CPU name to OpenJDK style, into + # VAR_CPU, VAR_CPU_ARCH, VAR_CPU_BITS and VAR_CPU_ENDIAN. +@@ -13721,6 +13730,18 @@ test -n "$target_alias" && + VAR_CPU_BITS=64 + VAR_CPU_ENDIAN=big + ;; ++ mips64el) ++ VAR_CPU=mips64 ++ VAR_CPU_ARCH=mips ++ VAR_CPU_BITS=64 ++ VAR_CPU_ENDIAN=little ++ ;; ++ loongarch64) ++ VAR_CPU=loongarch64 ++ VAR_CPU_ARCH=loongarch ++ VAR_CPU_BITS=64 ++ VAR_CPU_ENDIAN=little ++ ;; + *) + as_fn_error $? "unsupported cpu $build_cpu" "$LINENO" 5 + ;; +@@ -13859,6 +13880,18 @@ $as_echo "$OPENJDK_BUILD_OS-$OPENJDK_BUILD_CPU" >&6; } + VAR_CPU_BITS=64 + VAR_CPU_ENDIAN=big + ;; ++ mips64el) ++ VAR_CPU=mips64 ++ VAR_CPU_ARCH=mips ++ VAR_CPU_BITS=64 ++ VAR_CPU_ENDIAN=little ++ ;; ++ loongarch64) ++ VAR_CPU=loongarch64 ++ VAR_CPU_ARCH=loongarch ++ VAR_CPU_BITS=64 ++ VAR_CPU_ENDIAN=little ++ ;; + *) + as_fn_error $? "unsupported cpu $host_cpu" "$LINENO" 5 + ;; +@@ -13981,6 +14014,8 @@ $as_echo "$COMPILE_TYPE" >&6; } + OPENJDK_TARGET_CPU_LEGACY_LIB="i386" + elif test "x$OPENJDK_TARGET_CPU" = xx86_64; then + OPENJDK_TARGET_CPU_LEGACY_LIB="amd64" ++ elif test "x$OPENJDK_TARGET_CPU" = xmips64 && test "x$OPENJDK_TARGET_CPU_ENDIAN" = xlittle; then ++ OPENJDK_TARGET_CPU_LEGACY_LIB="mips64el" + fi + + +@@ -14014,6 +14049,9 @@ $as_echo "$COMPILE_TYPE" >&6; } + elif test "x$OPENJDK_TARGET_OS" != xmacosx && test "x$OPENJDK_TARGET_CPU" = xx86_64; then + # On all platforms except macosx, we replace x86_64 with amd64. + OPENJDK_TARGET_CPU_OSARCH="amd64" ++ elif test "x$OPENJDK_TARGET_OS" = xlinux && test "x$OPENJDK_TARGET_CPU" = xmips64 && test "x$OPENJDK_TARGET_CPU_ENDIAN" = xlittle; then ++ # System.getProperty("os.arch"): mips64 -> mips64el ++ OPENJDK_TARGET_CPU_OSARCH="mips64el" + fi + + +@@ -14023,6 +14061,8 @@ $as_echo "$COMPILE_TYPE" >&6; } + elif test "x$OPENJDK_TARGET_OS" != xmacosx && test "x$OPENJDK_TARGET_CPU" = xx86_64; then + # On all platforms except macosx, we replace x86_64 with amd64. + OPENJDK_TARGET_CPU_JLI="amd64" ++ elif test "x$OPENJDK_TARGET_CPU" = xmips64 && test "x$OPENJDK_TARGET_CPU_ENDIAN" = xlittle; then ++ OPENJDK_TARGET_CPU_JLI="mips64el" + fi + # Now setup the -D flags for building libjli. + OPENJDK_TARGET_CPU_JLI_CFLAGS="-DLIBARCHNAME='\"$OPENJDK_TARGET_CPU_JLI\"'" +@@ -14035,6 +14075,9 @@ $as_echo "$COMPILE_TYPE" >&6; } + elif test "x$OPENJDK_TARGET_OS" = xmacosx && test "x$TOOLCHAIN_TYPE" = xclang ; then + OPENJDK_TARGET_CPU_JLI_CFLAGS="$OPENJDK_TARGET_CPU_JLI_CFLAGS -stdlib=libc++ -mmacosx-version-min=\$(MACOSX_VERSION_MIN)" + fi ++ if test "x$OPENJDK_TARGET_CPU" = xmips64 && test "x$OPENJDK_TARGET_CPU_ENDIAN" = xlittle; then ++ OPENJDK_TARGET_CPU_JLI_CFLAGS="$OPENJDK_TARGET_CPU_JLI_CFLAGS -DLIBARCH32NAME='\"mips32el\"' -DLIBARCH64NAME='\"mips64el\"'" ++ fi + + + # Setup OPENJDK_TARGET_OS_API_DIR, used in source paths. +@@ -41899,6 +41942,47 @@ $as_echo "$ac_cv_c_bigendian" >&6; } + fi + + ++BUILDER_NAME="$build_os" ++BUILDER_ID="Custom build ($(date))" ++if test -f /etc/issue; then ++ etc_issue_info=`cat /etc/issue` ++ if test -n "$etc_issue_info"; then ++ BUILDER_NAME=`cat /etc/issue | head -n 1 | cut -d " " -f 1` ++ fi ++fi ++if test -f /etc/redhat-release; then ++ etc_issue_info=`cat /etc/redhat-release` ++ if test -n "$etc_issue_info"; then ++ BUILDER_NAME=`cat /etc/redhat-release | head -n 1 | cut -d " " -f 1` ++ fi ++fi ++if test -f /etc/neokylin-release; then ++ etc_issue_info=`cat /etc/neokylin-release` ++ if test -n "$etc_issue_info"; then ++ BUILDER_NAME=`cat /etc/neokylin-release | head -n 1 | cut -d " " -f 1` ++ fi ++fi ++if test -z "$BUILDER_NAME"; then ++ BUILDER_NAME="unknown" ++fi ++BUILDER_NAME=`echo $BUILDER_NAME | sed -r "s/-//g"` ++if test -n "$OPENJDK_TARGET_CPU_OSARCH"; then ++ HOST_NAME="$OPENJDK_TARGET_CPU_OSARCH" ++else ++ HOST_NAME="unknown" ++fi ++if test -f "/usr/bin/cpp"; then ++ # gcc_with_arch_info=`gcc -v 2>&1 | grep '\-\-with-arch=' | sed 's/.*--with-arch=//;s/ .*$//'` ++ gcc_with_arch_info=`cpp -dM /dev/null | grep '\<_MIPS_ARCH\>' | sed 's/^#define _MIPS_ARCH "//;s/"$//'` ++ if test -n "$gcc_with_arch_info"; then ++ HOST_NAME="$gcc_with_arch_info" ++ fi ++fi ++ ++ ++ ++ ++ + # Configure flags for the tools + + ############################################################################### +diff --git a/common/autoconf/platform.m4 b/common/autoconf/platform.m4 +index 51df988f61..51cc28c312 100644 +--- a/common/autoconf/platform.m4 ++++ b/common/autoconf/platform.m4 +@@ -23,6 +23,12 @@ + # questions. + # + ++# ++# This file has been modified by Loongson Technology in 2022. These ++# modifications are Copyright (c) 2018, 2022, Loongson Technology, and are made ++# available on the same license terms set forth above. ++# ++ + # Support macro for PLATFORM_EXTRACT_TARGET_AND_BUILD. + # Converts autoconf style CPU name to OpenJDK style, into + # VAR_CPU, VAR_CPU_ARCH, VAR_CPU_BITS and VAR_CPU_ENDIAN. +@@ -96,6 +102,18 @@ AC_DEFUN([PLATFORM_EXTRACT_VARS_FROM_CPU], + VAR_CPU_BITS=64 + VAR_CPU_ENDIAN=big + ;; ++ mips64el) ++ VAR_CPU=mips64 ++ VAR_CPU_ARCH=mips ++ VAR_CPU_BITS=64 ++ VAR_CPU_ENDIAN=little ++ ;; ++ loongarch64) ++ VAR_CPU=loongarch64 ++ VAR_CPU_ARCH=loongarch ++ VAR_CPU_BITS=64 ++ VAR_CPU_ENDIAN=little ++ ;; + *) + AC_MSG_ERROR([unsupported cpu $1]) + ;; +@@ -283,6 +301,8 @@ AC_DEFUN([PLATFORM_SETUP_LEGACY_VARS], + OPENJDK_TARGET_CPU_LEGACY_LIB="i386" + elif test "x$OPENJDK_TARGET_CPU" = xx86_64; then + OPENJDK_TARGET_CPU_LEGACY_LIB="amd64" ++ elif test "x$OPENJDK_TARGET_CPU" = xmips64 && test "x$OPENJDK_TARGET_CPU_ENDIAN" = xlittle; then ++ OPENJDK_TARGET_CPU_LEGACY_LIB="mips64el" + fi + AC_SUBST(OPENJDK_TARGET_CPU_LEGACY_LIB) + +@@ -316,6 +336,9 @@ AC_DEFUN([PLATFORM_SETUP_LEGACY_VARS], + elif test "x$OPENJDK_TARGET_OS" != xmacosx && test "x$OPENJDK_TARGET_CPU" = xx86_64; then + # On all platforms except macosx, we replace x86_64 with amd64. + OPENJDK_TARGET_CPU_OSARCH="amd64" ++ elif test "x$OPENJDK_TARGET_OS" = xlinux && test "x$OPENJDK_TARGET_CPU" = xmips64 && test "x$OPENJDK_TARGET_CPU_ENDIAN" = xlittle; then ++ # System.getProperty("os.arch"): mips64 -> mips64el ++ OPENJDK_TARGET_CPU_OSARCH="mips64el" + fi + AC_SUBST(OPENJDK_TARGET_CPU_OSARCH) + +@@ -325,6 +348,8 @@ AC_DEFUN([PLATFORM_SETUP_LEGACY_VARS], + elif test "x$OPENJDK_TARGET_OS" != xmacosx && test "x$OPENJDK_TARGET_CPU" = xx86_64; then + # On all platforms except macosx, we replace x86_64 with amd64. + OPENJDK_TARGET_CPU_JLI="amd64" ++ elif test "x$OPENJDK_TARGET_CPU" = xmips64 && test "x$OPENJDK_TARGET_CPU_ENDIAN" = xlittle; then ++ OPENJDK_TARGET_CPU_JLI="mips64el" + fi + # Now setup the -D flags for building libjli. + OPENJDK_TARGET_CPU_JLI_CFLAGS="-DLIBARCHNAME='\"$OPENJDK_TARGET_CPU_JLI\"'" +@@ -337,6 +362,9 @@ AC_DEFUN([PLATFORM_SETUP_LEGACY_VARS], + elif test "x$OPENJDK_TARGET_OS" = xmacosx && test "x$TOOLCHAIN_TYPE" = xclang ; then + OPENJDK_TARGET_CPU_JLI_CFLAGS="$OPENJDK_TARGET_CPU_JLI_CFLAGS -stdlib=libc++ -mmacosx-version-min=\$(MACOSX_VERSION_MIN)" + fi ++ if test "x$OPENJDK_TARGET_CPU" = xmips64 && test "x$OPENJDK_TARGET_CPU_ENDIAN" = xlittle; then ++ OPENJDK_TARGET_CPU_JLI_CFLAGS="$OPENJDK_TARGET_CPU_JLI_CFLAGS -DLIBARCH32NAME='\"mips32el\"' -DLIBARCH64NAME='\"mips64el\"'" ++ fi + AC_SUBST(OPENJDK_TARGET_CPU_JLI_CFLAGS) + + # Setup OPENJDK_TARGET_OS_API_DIR, used in source paths. +@@ -550,3 +578,46 @@ AC_DEFUN_ONCE([PLATFORM_SETUP_OPENJDK_TARGET_ENDIANNESS], + AC_MSG_ERROR([The tested endian in the target ($ENDIAN) differs from the endian expected to be found in the target ($OPENJDK_TARGET_CPU_ENDIAN)]) + fi + ]) ++ ++AC_DEFUN([GET_BUILDER_AND_HOST_DATA], ++[ ++BUILDER_NAME="$build_os" ++BUILDER_ID="Custom build ($(date))" ++if test -f /etc/issue; then ++ etc_issue_info=`cat /etc/issue` ++ if test -n "$etc_issue_info"; then ++ BUILDER_NAME=`cat /etc/issue | head -n 1 | cut -d " " -f 1` ++ fi ++fi ++if test -f /etc/redhat-release; then ++ etc_issue_info=`cat /etc/redhat-release` ++ if test -n "$etc_issue_info"; then ++ BUILDER_NAME=`cat /etc/redhat-release | head -n 1 | cut -d " " -f 1` ++ fi ++fi ++if test -f /etc/neokylin-release; then ++ etc_issue_info=`cat /etc/neokylin-release` ++ if test -n "$etc_issue_info"; then ++ BUILDER_NAME=`cat /etc/neokylin-release | head -n 1 | cut -d " " -f 1` ++ fi ++fi ++if test -z "$BUILDER_NAME"; then ++ BUILDER_NAME="unknown" ++fi ++BUILDER_NAME=`echo $BUILDER_NAME | sed -r "s/-//g"` ++if test -n "$OPENJDK_TARGET_CPU_OSARCH"; then ++ HOST_NAME="$OPENJDK_TARGET_CPU_OSARCH" ++else ++ HOST_NAME="unknown" ++fi ++if test -f "/usr/bin/cpp"; then ++ # gcc_with_arch_info=`gcc -v 2>&1 | grep '\-\-with-arch=' | sed 's/.*--with-arch=//;s/ .*$//'` ++ gcc_with_arch_info=`cpp -dM /dev/null | grep '\<_MIPS_ARCH\>' | sed 's/^#define _MIPS_ARCH "//;s/"$//'` ++ if test -n "$gcc_with_arch_info"; then ++ HOST_NAME="$gcc_with_arch_info" ++ fi ++fi ++AC_SUBST(BUILDER_ID) ++AC_SUBST(BUILDER_NAME) ++AC_SUBST(HOST_NAME) ++]) +diff --git a/common/autoconf/spec.gmk.in b/common/autoconf/spec.gmk.in +index 506cf61708..40d0c152f1 100644 +--- a/common/autoconf/spec.gmk.in ++++ b/common/autoconf/spec.gmk.in +@@ -23,6 +23,12 @@ + # questions. + # + ++# ++# This file has been modified by Loongson Technology in 2022. These ++# modifications are Copyright (c) 2018, 2022, Loongson Technology, and are made ++# available on the same license terms set forth above. ++# ++ + # Configured @DATE_WHEN_CONFIGURED@ to build + # for target system @OPENJDK_TARGET_OS@-@OPENJDK_TARGET_CPU@ + # (called @OPENJDK_TARGET_AUTOCONF_NAME@ by autoconf) +@@ -219,6 +225,23 @@ else + endif + JRE_RELEASE_VERSION:=$(FULL_VERSION) + ++# Build OS and host values for use in Loongson OpenJDK release ++BUILDER_ID:=@BUILDER_ID@ ++BUILDER_NAME:=@BUILDER_NAME@ ++HOST_NAME:=@HOST_NAME@ ++ ++# Loongson OpenJDK Version info ++VER=8.1.12 ++ifeq ($(HOST_NAME), ) ++ HOST_NAME=unknown ++endif ++ifeq ($(BUILDER_NAME), ) ++ BUILDER_NAME=unknown ++endif ++HOST_NAME_STRING=-$(HOST_NAME) ++BUILDER_NAME_STRING=-$(BUILDER_NAME) ++LOONGSON_RUNTIME_NAME=Loongson $(VER)$(HOST_NAME_STRING)$(BUILDER_NAME_STRING) ++ + # How to compile the code: release, fastdebug or slowdebug + DEBUG_LEVEL:=@DEBUG_LEVEL@ + +diff --git a/hotspot/agent/make/saenv.sh b/hotspot/agent/make/saenv.sh +index ab9a0a431c..a2de3fc329 100644 +--- a/hotspot/agent/make/saenv.sh ++++ b/hotspot/agent/make/saenv.sh +@@ -23,6 +23,12 @@ + # + # + ++# ++# This file has been modified by Loongson Technology in 2020. These ++# modifications are Copyright (c) 2015, 2020, Loongson Technology, and are made ++# available on the same license terms set forth above. ++# ++ + # This file sets common environment variables for all SA scripts + + OS=`uname` +@@ -42,6 +48,14 @@ if [ "$OS" = "Linux" ]; then + SA_LIBPATH=$STARTDIR/../src/os/linux/amd64:$STARTDIR/linux/amd64 + OPTIONS="-Dsa.library.path=$SA_LIBPATH" + CPU=amd64 ++ elif [ "$ARCH" = "mips64" ] ; then ++ SA_LIBPATH=$STARTDIR/../src/os/linux/mips:$STARTDIR/linux/mips ++ OPTIONS="-Dsa.library.path=$SA_LIBPATH" ++ CPU=mips ++ elif [ "$ARCH" = "loongarch64" ] ; then ++ SA_LIBPATH=$STARTDIR/../src/os/linux/loongarch64:$STARTDIR/linux/loongarch64 ++ OPTIONS="-Dsa.library.path=$SA_LIBPATH" ++ CPU=loongarch64 + else + SA_LIBPATH=$STARTDIR/../src/os/linux/i386:$STARTDIR/linux/i386 + OPTIONS="-Dsa.library.path=$SA_LIBPATH" +diff --git a/hotspot/agent/src/os/linux/LinuxDebuggerLocal.c b/hotspot/agent/src/os/linux/LinuxDebuggerLocal.c +index d6a0c7d9a9..b3b1380b29 100644 +--- a/hotspot/agent/src/os/linux/LinuxDebuggerLocal.c ++++ b/hotspot/agent/src/os/linux/LinuxDebuggerLocal.c +@@ -22,6 +22,13 @@ + * + */ + ++/* ++ * This file has been modified by Loongson Technology in 2022. These ++ * modifications are Copyright (c) 2015, 2022, Loongson Technology, and are made ++ * available on the same license terms set forth above. ++ * ++ */ ++ + #include + #include "libproc.h" + +@@ -49,10 +56,18 @@ + #include "sun_jvm_hotspot_debugger_sparc_SPARCThreadContext.h" + #endif + ++#if defined(mips64el) || defined(mips64) ++#include "sun_jvm_hotspot_debugger_mips64_MIPS64ThreadContext.h" ++#endif ++ + #ifdef aarch64 + #include "sun_jvm_hotspot_debugger_aarch64_AARCH64ThreadContext.h" + #endif + ++#ifdef loongarch64 ++#include "sun_jvm_hotspot_debugger_loongarch64_LOONGARCH64ThreadContext.h" ++#endif ++ + static jfieldID p_ps_prochandle_ID = 0; + static jfieldID threadList_ID = 0; + static jfieldID loadObjectList_ID = 0; +@@ -337,7 +352,7 @@ JNIEXPORT jbyteArray JNICALL Java_sun_jvm_hotspot_debugger_linux_LinuxDebuggerLo + return (err == PS_OK)? array : 0; + } + +-#if defined(i386) || defined(amd64) || defined(sparc) || defined(sparcv9) || defined(aarch64) ++#if defined(i386) || defined(amd64) || defined(sparc) || defined(sparcv9) || defined(aarch64) || defined(loongarch64) + JNIEXPORT jlongArray JNICALL Java_sun_jvm_hotspot_debugger_linux_LinuxDebuggerLocal_getThreadIntegerRegisterSet0 + (JNIEnv *env, jobject this_obj, jint lwp_id) { + +@@ -364,6 +379,12 @@ JNIEXPORT jlongArray JNICALL Java_sun_jvm_hotspot_debugger_linux_LinuxDebuggerLo + #endif + #if defined(sparc) || defined(sparcv9) + #define NPRGREG sun_jvm_hotspot_debugger_sparc_SPARCThreadContext_NPRGREG ++#endif ++#ifdef loongarch64 ++#define NPRGREG sun_jvm_hotspot_debugger_loongarch64_LOONGARCH64ThreadContext_NPRGREG ++#endif ++#if defined(mips64) || defined(mips64el) ++#define NPRGREG sun_jvm_hotspot_debugger_mips64_MIPS64ThreadContext_NPRGREG + #endif + + array = (*env)->NewLongArray(env, NPRGREG); +@@ -470,6 +491,55 @@ JNIEXPORT jlongArray JNICALL Java_sun_jvm_hotspot_debugger_linux_LinuxDebuggerLo + } + #endif /* aarch64 */ + ++#if defined(loongarch64) ++ ++#define REG_INDEX(reg) sun_jvm_hotspot_debugger_loongarch64_LOONGARCH64ThreadContext_##reg ++ ++ { ++ int i; ++ for (i = 0; i < 31; i++) ++ regs[i] = gregs.regs[i]; ++ regs[REG_INDEX(PC)] = gregs.csr_era; ++ } ++#endif /* loongarch64 */ ++#if defined(mips64) || defined(mips64el) ++ ++#define REG_INDEX(reg) sun_jvm_hotspot_debugger_mips64_MIPS64ThreadContext_##reg ++ ++ regs[REG_INDEX(ZERO)] = gregs.regs[0]; ++ regs[REG_INDEX(AT)] = gregs.regs[1]; ++ regs[REG_INDEX(V0)] = gregs.regs[2]; ++ regs[REG_INDEX(V1)] = gregs.regs[3]; ++ regs[REG_INDEX(A0)] = gregs.regs[4]; ++ regs[REG_INDEX(A1)] = gregs.regs[5]; ++ regs[REG_INDEX(A2)] = gregs.regs[6]; ++ regs[REG_INDEX(A3)] = gregs.regs[7]; ++ regs[REG_INDEX(T0)] = gregs.regs[8]; ++ regs[REG_INDEX(T1)] = gregs.regs[9]; ++ regs[REG_INDEX(T2)] = gregs.regs[10]; ++ regs[REG_INDEX(T3)] = gregs.regs[11]; ++ regs[REG_INDEX(T4)] = gregs.regs[12]; ++ regs[REG_INDEX(T5)] = gregs.regs[13]; ++ regs[REG_INDEX(T6)] = gregs.regs[14]; ++ regs[REG_INDEX(T7)] = gregs.regs[15]; ++ regs[REG_INDEX(S0)] = gregs.regs[16]; ++ regs[REG_INDEX(S1)] = gregs.regs[17]; ++ regs[REG_INDEX(S2)] = gregs.regs[18]; ++ regs[REG_INDEX(S3)] = gregs.regs[19]; ++ regs[REG_INDEX(S4)] = gregs.regs[20]; ++ regs[REG_INDEX(S5)] = gregs.regs[21]; ++ regs[REG_INDEX(S6)] = gregs.regs[22]; ++ regs[REG_INDEX(S7)] = gregs.regs[23]; ++ regs[REG_INDEX(T8)] = gregs.regs[24]; ++ regs[REG_INDEX(T9)] = gregs.regs[25]; ++ regs[REG_INDEX(K0)] = gregs.regs[26]; ++ regs[REG_INDEX(K1)] = gregs.regs[27]; ++ regs[REG_INDEX(GP)] = gregs.regs[28]; ++ regs[REG_INDEX(SP)] = gregs.regs[29]; ++ regs[REG_INDEX(FP)] = gregs.regs[30]; ++ regs[REG_INDEX(S8)] = gregs.regs[30]; ++ regs[REG_INDEX(RA)] = gregs.regs[31]; ++#endif /* mips64 */ + + (*env)->ReleaseLongArrayElements(env, array, regs, JNI_COMMIT); + return array; +diff --git a/hotspot/agent/src/os/linux/Makefile b/hotspot/agent/src/os/linux/Makefile +index c0b5c869c1..2cc50b6fab 100644 +--- a/hotspot/agent/src/os/linux/Makefile ++++ b/hotspot/agent/src/os/linux/Makefile +@@ -22,7 +22,13 @@ + # + # + +-ARCH := $(shell if ([ `uname -m` = "ia64" ]) ; then echo ia64 ; elif ([ `uname -m` = "x86_64" ]) ; then echo amd64; elif ([ `uname -m` = "sparc64" ]) ; then echo sparc; else echo i386 ; fi ) ++# ++# This file has been modified by Loongson Technology in 2020. These ++# modifications are Copyright (c) 2015, 2020, Loongson Technology, and are made ++# available on the same license terms set forth above. ++# ++ ++ARCH := $(shell if ([ `uname -m` = "ia64" ]) ; then echo ia64 ; elif ([ `uname -m` = "mips64el" ]) ; then echo mips64 ; elif ([ `uname -m` = "x86_64" ]) ; then echo amd64; elif ([ `uname -m` = "sparc64" ]) ; then echo sparc; else echo i386 ; fi ) + GCC = gcc + + JAVAH = ${JAVA_HOME}/bin/javah +@@ -53,6 +59,8 @@ $(ARCH)/LinuxDebuggerLocal.o: LinuxDebuggerLocal.c + $(JAVAH) -jni -classpath ../../../build/classes -d $(ARCH) \ + sun.jvm.hotspot.debugger.x86.X86ThreadContext \ + sun.jvm.hotspot.debugger.sparc.SPARCThreadContext \ ++ sun.jvm.hotspot.debugger.mips64.MIPS64ThreadContext \ ++ sun.jvm.hotspot.debugger.loongarch64.LOONGARCH64ThreadContext \ + sun.jvm.hotspot.debugger.amd64.AMD64ThreadContext \ + sun.jvm.hotspot.debugger.aarch64.AARCH64ThreadContext + $(GCC) $(CFLAGS) $< -o $@ +diff --git a/hotspot/agent/src/os/linux/libproc.h b/hotspot/agent/src/os/linux/libproc.h +index 6b6e41cab4..5eb8211aa9 100644 +--- a/hotspot/agent/src/os/linux/libproc.h ++++ b/hotspot/agent/src/os/linux/libproc.h +@@ -22,6 +22,12 @@ + * + */ + ++/* ++ * This file has been modified by Loongson Technology in 2022. These ++ * modifications are Copyright (c) 2015, 2022, Loongson Technology, and are made ++ * available on the same license terms set forth above. ++ */ ++ + #ifndef _LIBPROC_H_ + #define _LIBPROC_H_ + +@@ -36,7 +42,7 @@ + + #include + +-#if defined(aarch64) ++#if defined(aarch64) || defined(loongarch64) + #include "asm/ptrace.h" + #endif + +@@ -76,7 +82,12 @@ combination of ptrace and /proc calls. + #include + #define user_regs_struct pt_regs + #endif +-#if defined(aarch64) ++ ++#if defined(mips) || defined(mipsel) || defined(mips64) || defined(mips64el) ++#include ++#define user_regs_struct pt_regs ++#endif ++#if defined(aarch64) || defined(loongarch64) + #define user_regs_struct user_pt_regs + #endif + +diff --git a/hotspot/agent/src/os/linux/ps_proc.c b/hotspot/agent/src/os/linux/ps_proc.c +index c4d6a9ecc5..7000e92723 100644 +--- a/hotspot/agent/src/os/linux/ps_proc.c ++++ b/hotspot/agent/src/os/linux/ps_proc.c +@@ -22,6 +22,12 @@ + * + */ + ++/* ++ * This file has been modified by Loongson Technology in 2022, These ++ * modifications are Copyright (c) 2022, Loongson Technology, and are made ++ * available on the same license terms set forth above. ++ */ ++ + #include + #include + #include +@@ -141,7 +147,7 @@ static bool process_get_lwp_regs(struct ps_prochandle* ph, pid_t pid, struct use + #define PTRACE_GETREGS_REQ PT_GETREGS + #endif + +-#ifdef PTRACE_GETREGS_REQ ++#if defined(PTRACE_GETREGS_REQ) && !defined(loongarch64) + if (ptrace_getregs(PTRACE_GETREGS_REQ, pid, user, NULL) < 0) { + print_debug("ptrace(PTRACE_GETREGS, ...) failed for lwp %d\n", pid); + return false; +diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/HotSpotAgent.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/HotSpotAgent.java +index c963350591..20e6f35b9c 100644 +--- a/hotspot/agent/src/share/classes/sun/jvm/hotspot/HotSpotAgent.java ++++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/HotSpotAgent.java +@@ -22,6 +22,12 @@ + * + */ + ++/* ++ * This file has been modified by Loongson Technology in 2020. These ++ * modifications are Copyright (c) 2018, 2020, Loongson Technology, and are made ++ * available on the same license terms set forth above. ++ * ++ */ + package sun.jvm.hotspot; + + import java.rmi.RemoteException; +@@ -37,6 +43,8 @@ import sun.jvm.hotspot.debugger.MachineDescriptionIA64; + import sun.jvm.hotspot.debugger.MachineDescriptionIntelX86; + import sun.jvm.hotspot.debugger.MachineDescriptionSPARC32Bit; + import sun.jvm.hotspot.debugger.MachineDescriptionSPARC64Bit; ++import sun.jvm.hotspot.debugger.MachineDescriptionMIPS64; ++import sun.jvm.hotspot.debugger.MachineDescriptionLOONGARCH64; + import sun.jvm.hotspot.debugger.NoSuchSymbolException; + import sun.jvm.hotspot.debugger.bsd.BsdDebuggerLocal; + import sun.jvm.hotspot.debugger.linux.LinuxDebuggerLocal; +@@ -594,6 +602,10 @@ public class HotSpotAgent { + } else { + machDesc = new MachineDescriptionSPARC32Bit(); + } ++ } else if (cpu.equals("mips64")) { ++ machDesc = new MachineDescriptionMIPS64(); ++ } else if (cpu.equals("loongarch64")) { ++ machDesc = new MachineDescriptionLOONGARCH64(); + } else { + try { + machDesc = (MachineDescription) +diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/Disassembler.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/Disassembler.java +index 993bf7bb47..1e075aa57e 100644 +--- a/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/Disassembler.java ++++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/asm/Disassembler.java +@@ -94,6 +94,12 @@ public class Disassembler { + } else if (arch.equals("amd64") || arch.equals("x86_64")) { + path.append(sep + "lib" + sep + "amd64" + sep); + libname += "-amd64.so"; ++ } else if (arch.equals("mips64") || arch.equals("mips64el")) { ++ path.append(sep + "lib" + sep + "mips64" + sep); ++ libname += "-mips64.so"; ++ } else if (arch.equals("loongarch64")) { ++ path.append(sep + "lib" + sep + "loongarch64" + sep); ++ libname += "-loongarch64.so"; + } else { + path.append(sep + "lib" + sep + arch + sep); + libname += "-" + arch + ".so"; +diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/MachineDescriptionLOONGARCH64.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/MachineDescriptionLOONGARCH64.java +new file mode 100644 +index 0000000000..0531427dab +--- /dev/null ++++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/MachineDescriptionLOONGARCH64.java +@@ -0,0 +1,41 @@ ++/* ++ * Copyright (c) 2000, 2008, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2018, 2020, Loongson Technology. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ * ++ */ ++ ++package sun.jvm.hotspot.debugger; ++ ++public class MachineDescriptionLOONGARCH64 extends MachineDescriptionTwosComplement implements MachineDescription { ++ public long getAddressSize() { ++ return 8; ++ } ++ ++ ++ public boolean isBigEndian() { ++ return false; ++ } ++ ++ public boolean isLP64() { ++ return true; ++ } ++} +diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/MachineDescriptionMIPS64.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/MachineDescriptionMIPS64.java +new file mode 100644 +index 0000000000..1b49efd201 +--- /dev/null ++++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/MachineDescriptionMIPS64.java +@@ -0,0 +1,41 @@ ++/* ++ * Copyright (c) 2000, 2008, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2018, Loongson Technology. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ * ++ */ ++ ++package sun.jvm.hotspot.debugger; ++ ++public class MachineDescriptionMIPS64 extends MachineDescriptionTwosComplement implements MachineDescription { ++ public long getAddressSize() { ++ return 8; ++ } ++ ++ ++ public boolean isBigEndian() { ++ return "big".equals(System.getProperty("sun.cpu.endian")); ++ } ++ ++ public boolean isLP64() { ++ return true; ++ } ++} +diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/linux/LinuxCDebugger.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/linux/LinuxCDebugger.java +index f178d6a6e7..019e794bbb 100644 +--- a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/linux/LinuxCDebugger.java ++++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/linux/LinuxCDebugger.java +@@ -32,11 +32,15 @@ import sun.jvm.hotspot.debugger.cdbg.*; + import sun.jvm.hotspot.debugger.x86.*; + import sun.jvm.hotspot.debugger.amd64.*; + import sun.jvm.hotspot.debugger.sparc.*; ++import sun.jvm.hotspot.debugger.mips64.*; ++import sun.jvm.hotspot.debugger.loongarch64.*; + import sun.jvm.hotspot.debugger.linux.x86.*; + import sun.jvm.hotspot.debugger.linux.amd64.*; + import sun.jvm.hotspot.debugger.aarch64.*; + import sun.jvm.hotspot.debugger.linux.aarch64.*; + import sun.jvm.hotspot.debugger.linux.sparc.*; ++import sun.jvm.hotspot.debugger.linux.mips64.*; ++import sun.jvm.hotspot.debugger.linux.loongarch64.*; + import sun.jvm.hotspot.utilities.*; + + class LinuxCDebugger implements CDebugger { +@@ -106,6 +110,20 @@ class LinuxCDebugger implements CDebugger { + Address pc = context.getRegisterAsAddress(AARCH64ThreadContext.PC); + if (pc == null) return null; + return new LinuxAARCH64CFrame(dbg, fp, pc); ++ } else if (cpu.equals("mips64")) { ++ MIPS64ThreadContext context = (MIPS64ThreadContext) thread.getContext(); ++ Address sp = context.getRegisterAsAddress(MIPS64ThreadContext.SP); ++ if (sp == null) return null; ++ Address pc = context.getRegisterAsAddress(MIPS64ThreadContext.PC); ++ if (pc == null) return null; ++ return new LinuxMIPS64CFrame(dbg, sp, pc); ++ } else if (cpu.equals("loongarch64")) { ++ LOONGARCH64ThreadContext context = (LOONGARCH64ThreadContext) thread.getContext(); ++ Address sp = context.getRegisterAsAddress(LOONGARCH64ThreadContext.SP); ++ if (sp == null) return null; ++ Address pc = context.getRegisterAsAddress(LOONGARCH64ThreadContext.PC); ++ if (pc == null) return null; ++ return new LinuxLOONGARCH64CFrame(dbg, sp, pc); + } else { + // Runtime exception thrown by LinuxThreadContextFactory if unknown cpu + ThreadContext context = (ThreadContext) thread.getContext(); +diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/linux/LinuxThreadContextFactory.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/linux/LinuxThreadContextFactory.java +index 44c2265d7a..3b6747ac0a 100644 +--- a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/linux/LinuxThreadContextFactory.java ++++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/linux/LinuxThreadContextFactory.java +@@ -30,6 +30,8 @@ import sun.jvm.hotspot.debugger.linux.amd64.*; + import sun.jvm.hotspot.debugger.linux.ia64.*; + import sun.jvm.hotspot.debugger.linux.x86.*; + import sun.jvm.hotspot.debugger.linux.sparc.*; ++import sun.jvm.hotspot.debugger.linux.mips64.*; ++import sun.jvm.hotspot.debugger.linux.loongarch64.*; + + class LinuxThreadContextFactory { + static ThreadContext createThreadContext(LinuxDebugger dbg) { +@@ -42,6 +44,10 @@ class LinuxThreadContextFactory { + return new LinuxIA64ThreadContext(dbg); + } else if (cpu.equals("sparc")) { + return new LinuxSPARCThreadContext(dbg); ++ } else if (cpu.equals("mips64")) { ++ return new LinuxMIPS64ThreadContext(dbg); ++ } else if (cpu.equals("loongarch64")) { ++ return new LinuxLOONGARCH64ThreadContext(dbg); + } else { + try { + Class tcc = Class.forName("sun.jvm.hotspot.debugger.linux." + +diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/linux/loongarch64/LinuxLOONGARCH64CFrame.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/linux/loongarch64/LinuxLOONGARCH64CFrame.java +new file mode 100644 +index 0000000000..3b20dbbd87 +--- /dev/null ++++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/linux/loongarch64/LinuxLOONGARCH64CFrame.java +@@ -0,0 +1,80 @@ ++/* ++ * Copyright (c) 2003, 2012, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2015, 2020, Loongson Technology. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ * ++ */ ++ ++package sun.jvm.hotspot.debugger.linux.loongarch64; ++ ++import sun.jvm.hotspot.debugger.*; ++import sun.jvm.hotspot.debugger.linux.*; ++import sun.jvm.hotspot.debugger.cdbg.*; ++import sun.jvm.hotspot.debugger.cdbg.basic.*; ++import sun.jvm.hotspot.debugger.loongarch64.*; ++ ++final public class LinuxLOONGARCH64CFrame extends BasicCFrame { ++ // package/class internals only ++ public LinuxLOONGARCH64CFrame(LinuxDebugger dbg, Address fp, Address pc) { ++ super(dbg.getCDebugger()); ++ this.fp = fp; ++ this.pc = pc; ++ this.dbg = dbg; ++ } ++ ++ // override base class impl to avoid ELF parsing ++ public ClosestSymbol closestSymbolToPC() { ++ // try native lookup in debugger. ++ return dbg.lookup(dbg.getAddressValue(pc())); ++ } ++ ++ public Address pc() { ++ return pc; ++ } ++ ++ public Address localVariableBase() { ++ return fp; ++ } ++ ++ public CFrame sender(ThreadProxy thread) { ++ LOONGARCH64ThreadContext context = (LOONGARCH64ThreadContext) thread.getContext(); ++ Address sp = context.getRegisterAsAddress(LOONGARCH64ThreadContext.SP); ++ ++ if ((fp == null) || fp.lessThan(sp)) { ++ return null; ++ } ++ ++ Address nextFP = fp.getAddressAt(-2 * ADDRESS_SIZE); ++ if (nextFP == null) { ++ return null; ++ } ++ Address nextPC = fp.getAddressAt(-1 * ADDRESS_SIZE); ++ if (nextPC == null) { ++ return null; ++ } ++ return new LinuxLOONGARCH64CFrame(dbg, nextFP, nextPC); ++ } ++ ++ private static final int ADDRESS_SIZE = 8; ++ private Address pc; ++ private Address fp; ++ private LinuxDebugger dbg; ++} +diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/linux/loongarch64/LinuxLOONGARCH64ThreadContext.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/linux/loongarch64/LinuxLOONGARCH64ThreadContext.java +new file mode 100644 +index 0000000000..9f22133eaf +--- /dev/null ++++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/linux/loongarch64/LinuxLOONGARCH64ThreadContext.java +@@ -0,0 +1,47 @@ ++/* ++ * Copyright (c) 2003, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2015, 2020, Loongson Technology. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ * ++ */ ++ ++package sun.jvm.hotspot.debugger.linux.loongarch64; ++ ++import sun.jvm.hotspot.debugger.*; ++import sun.jvm.hotspot.debugger.loongarch64.*; ++import sun.jvm.hotspot.debugger.linux.*; ++ ++public class LinuxLOONGARCH64ThreadContext extends LOONGARCH64ThreadContext { ++ private LinuxDebugger debugger; ++ ++ public LinuxLOONGARCH64ThreadContext(LinuxDebugger debugger) { ++ super(); ++ this.debugger = debugger; ++ } ++ ++ public void setRegisterAsAddress(int index, Address value) { ++ setRegister(index, debugger.getAddressValue(value)); ++ } ++ ++ public Address getRegisterAsAddress(int index) { ++ return debugger.newAddress(getRegister(index)); ++ } ++} +diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/linux/mips64/LinuxMIPS64CFrame.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/linux/mips64/LinuxMIPS64CFrame.java +new file mode 100644 +index 0000000000..2e3eb564da +--- /dev/null ++++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/linux/mips64/LinuxMIPS64CFrame.java +@@ -0,0 +1,80 @@ ++/* ++ * Copyright (c) 2003, 2012, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2015, 2018, Loongson Technology. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ * ++ */ ++ ++package sun.jvm.hotspot.debugger.linux.mips64; ++ ++import sun.jvm.hotspot.debugger.*; ++import sun.jvm.hotspot.debugger.linux.*; ++import sun.jvm.hotspot.debugger.cdbg.*; ++import sun.jvm.hotspot.debugger.cdbg.basic.*; ++import sun.jvm.hotspot.debugger.mips64.*; ++ ++final public class LinuxMIPS64CFrame extends BasicCFrame { ++ // package/class internals only ++ public LinuxMIPS64CFrame(LinuxDebugger dbg, Address ebp, Address pc) { ++ super(dbg.getCDebugger()); ++ this.ebp = ebp; ++ this.pc = pc; ++ this.dbg = dbg; ++ } ++ ++ // override base class impl to avoid ELF parsing ++ public ClosestSymbol closestSymbolToPC() { ++ // try native lookup in debugger. ++ return dbg.lookup(dbg.getAddressValue(pc())); ++ } ++ ++ public Address pc() { ++ return pc; ++ } ++ ++ public Address localVariableBase() { ++ return ebp; ++ } ++ ++ public CFrame sender(ThreadProxy thread) { ++ MIPS64ThreadContext context = (MIPS64ThreadContext) thread.getContext(); ++ Address esp = context.getRegisterAsAddress(MIPS64ThreadContext.SP); ++ ++ if ( (ebp == null) || ebp.lessThan(esp) ) { ++ return null; ++ } ++ ++ Address nextEBP = ebp.getAddressAt( 0 * ADDRESS_SIZE); ++ if (nextEBP == null) { ++ return null; ++ } ++ Address nextPC = ebp.getAddressAt( 1 * ADDRESS_SIZE); ++ if (nextPC == null) { ++ return null; ++ } ++ return new LinuxMIPS64CFrame(dbg, nextEBP, nextPC); ++ } ++ ++ private static final int ADDRESS_SIZE = 4; ++ private Address pc; ++ private Address ebp; ++ private LinuxDebugger dbg; ++} +diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/linux/mips64/LinuxMIPS64ThreadContext.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/linux/mips64/LinuxMIPS64ThreadContext.java +new file mode 100644 +index 0000000000..98e0f3f0bc +--- /dev/null ++++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/linux/mips64/LinuxMIPS64ThreadContext.java +@@ -0,0 +1,47 @@ ++/* ++ * Copyright (c) 2003, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2015, 2018, Loongson Technology. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ * ++ */ ++ ++package sun.jvm.hotspot.debugger.linux.mips64; ++ ++import sun.jvm.hotspot.debugger.*; ++import sun.jvm.hotspot.debugger.mips64.*; ++import sun.jvm.hotspot.debugger.linux.*; ++ ++public class LinuxMIPS64ThreadContext extends MIPS64ThreadContext { ++ private LinuxDebugger debugger; ++ ++ public LinuxMIPS64ThreadContext(LinuxDebugger debugger) { ++ super(); ++ this.debugger = debugger; ++ } ++ ++ public void setRegisterAsAddress(int index, Address value) { ++ setRegister(index, debugger.getAddressValue(value)); ++ } ++ ++ public Address getRegisterAsAddress(int index) { ++ return debugger.newAddress(getRegister(index)); ++ } ++} +diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/loongarch64/LOONGARCH64ThreadContext.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/loongarch64/LOONGARCH64ThreadContext.java +new file mode 100644 +index 0000000000..90b0cf97e3 +--- /dev/null ++++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/loongarch64/LOONGARCH64ThreadContext.java +@@ -0,0 +1,123 @@ ++/* ++ * Copyright (c) 2000, 2012, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2015, 2020, Loongson Technology. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ * ++ */ ++ ++package sun.jvm.hotspot.debugger.loongarch64; ++ ++import sun.jvm.hotspot.debugger.*; ++import sun.jvm.hotspot.debugger.cdbg.*; ++ ++/** Specifies the thread context on loongarch64 platforms; only a sub-portion ++ of the context is guaranteed to be present on all operating ++ systems. */ ++ ++public abstract class LOONGARCH64ThreadContext implements ThreadContext { ++ ++ // NOTE: the indices for the various registers must be maintained as ++ // listed across various operating systems. However, only a small ++ // subset of the registers' values are guaranteed to be present (and ++ // must be present for the SA's stack walking to work): EAX, EBX, ++ // ECX, EDX, ESI, EDI, EBP, ESP, and EIP. ++ ++ public static final int ZERO = 0; ++ public static final int RA = 1; ++ public static final int TP = 2; ++ public static final int SP = 3; ++ public static final int A0 = 4; ++ public static final int A1 = 5; ++ public static final int A2 = 6; ++ public static final int A3 = 7; ++ public static final int A4 = 8; ++ public static final int A5 = 9; ++ public static final int A6 = 10; ++ public static final int A7 = 11; ++ public static final int T0 = 12; ++ public static final int T1 = 13; ++ public static final int T2 = 14; ++ public static final int T3 = 15; ++ public static final int T4 = 16; ++ public static final int T5 = 17; ++ public static final int T6 = 18; ++ public static final int T7 = 19; ++ public static final int T8 = 20; ++ public static final int RX = 21; ++ public static final int FP = 22; ++ public static final int S0 = 23; ++ public static final int S1 = 24; ++ public static final int S2 = 25; ++ public static final int S3 = 26; ++ public static final int S4 = 27; ++ public static final int S5 = 28; ++ public static final int S6 = 29; ++ public static final int S7 = 30; ++ public static final int S8 = 31; ++ public static final int PC = 32; ++ public static final int NPRGREG = 33; ++ ++ private static final String[] regNames = { ++ "ZERO", "RA", "TP", "SP", ++ "A0", "A1", "A2", "A3", ++ "A4", "A5", "A6", "A7", ++ "T0", "T1", "T2", "T3", ++ "T4", "T5", "T6", "T7", ++ "T8", "RX", "FP", "S0", ++ "S1", "S2", "S3", "S4", ++ "S5", "S6", "S7", "S8", ++ "PC" ++ }; ++ ++ private long[] data; ++ ++ public LOONGARCH64ThreadContext() { ++ data = new long[NPRGREG]; ++ } ++ ++ public int getNumRegisters() { ++ return NPRGREG; ++ } ++ ++ public String getRegisterName(int index) { ++ return regNames[index]; ++ } ++ ++ public void setRegister(int index, long value) { ++ data[index] = value; ++ } ++ ++ public long getRegister(int index) { ++ return data[index]; ++ } ++ ++ public CFrame getTopFrame(Debugger dbg) { ++ return null; ++ } ++ ++ /** This can't be implemented in this class since we would have to ++ tie the implementation to, for example, the debugging system */ ++ public abstract void setRegisterAsAddress(int index, Address value); ++ ++ /** This can't be implemented in this class since we would have to ++ tie the implementation to, for example, the debugging system */ ++ public abstract Address getRegisterAsAddress(int index); ++} +diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/mips64/MIPS64ThreadContext.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/mips64/MIPS64ThreadContext.java +new file mode 100644 +index 0000000000..c57ee9dfc9 +--- /dev/null ++++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/mips64/MIPS64ThreadContext.java +@@ -0,0 +1,123 @@ ++/* ++ * Copyright (c) 2000, 2012, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2015, 2018, Loongson Technology. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ * ++ */ ++ ++package sun.jvm.hotspot.debugger.mips64; ++ ++import sun.jvm.hotspot.debugger.*; ++import sun.jvm.hotspot.debugger.cdbg.*; ++ ++/** Specifies the thread context on mips64 platforms; only a sub-portion ++ of the context is guaranteed to be present on all operating ++ systems. */ ++ ++public abstract class MIPS64ThreadContext implements ThreadContext { ++ ++ // NOTE: the indices for the various registers must be maintained as ++ // listed across various operating systems. However, only a small ++ // subset of the registers' values are guaranteed to be present (and ++ // must be present for the SA's stack walking to work): EAX, EBX, ++ // ECX, EDX, ESI, EDI, EBP, ESP, and EIP. ++ ++ public static final int ZERO = 0; ++ public static final int AT = 1; ++ public static final int V0 = 2; ++ public static final int V1 = 3; ++ public static final int A0 = 4; ++ public static final int A1 = 5; ++ public static final int A2 = 6; ++ public static final int A3 = 7; ++ public static final int T0 = 8; ++ public static final int T1 = 9; ++ public static final int T2 = 10; ++ public static final int T3 = 11; ++ public static final int T4 = 12; ++ public static final int T5 = 13; ++ public static final int T6 = 14; ++ public static final int T7 = 15; ++ public static final int S0 = 16; ++ public static final int S1 = 17; ++ public static final int S2 = 18; ++ public static final int S3 = 19; ++ public static final int S4 = 20; ++ public static final int S5 = 21; ++ public static final int S6 = 22; ++ public static final int S7 = 23; ++ public static final int T8 = 24; ++ public static final int T9 = 25; ++ public static final int K0 = 26; ++ public static final int K1 = 27; ++ public static final int GP = 28; ++ public static final int SP = 29; ++ public static final int FP = 30; ++ public static final int RA = 31; ++ public static final int PC = 32; ++ public static final int NPRGREG = 33; ++ ++ private static final String[] regNames = { ++ "ZERO", "AT", "V0", "V1", ++ "A0", "A1", "A2", "A3", ++ "T0", "T1", "T2", "T3", ++ "T4", "T5", "T6", "T7", ++ "S0", "S1", "S2", "S3", ++ "S4", "S5", "S6", "S7", ++ "T8", "T9", "K0", "K1", ++ "GP", "SP", "FP", "RA", ++ "PC" ++ }; ++ ++ private long[] data; ++ ++ public MIPS64ThreadContext() { ++ data = new long[NPRGREG]; ++ } ++ ++ public int getNumRegisters() { ++ return NPRGREG; ++ } ++ ++ public String getRegisterName(int index) { ++ return regNames[index]; ++ } ++ ++ public void setRegister(int index, long value) { ++ data[index] = value; ++ } ++ ++ public long getRegister(int index) { ++ return data[index]; ++ } ++ ++ public CFrame getTopFrame(Debugger dbg) { ++ return null; ++ } ++ ++ /** This can't be implemented in this class since we would have to ++ tie the implementation to, for example, the debugging system */ ++ public abstract void setRegisterAsAddress(int index, Address value); ++ ++ /** This can't be implemented in this class since we would have to ++ tie the implementation to, for example, the debugging system */ ++ public abstract Address getRegisterAsAddress(int index); ++} +diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/posix/elf/ELFHeader.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/posix/elf/ELFHeader.java +index 7113a3a497..24273888c2 100644 +--- a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/posix/elf/ELFHeader.java ++++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/posix/elf/ELFHeader.java +@@ -63,6 +63,8 @@ public interface ELFHeader { + public static final int ARCH_i860 = 7; + /** MIPS architecture type. */ + public static final int ARCH_MIPS = 8; ++ /** LOONGARCH architecture type. */ ++ public static final int ARCH_LOONGARCH = 9; + + /** Returns a file type which is defined by the file type constants. */ + public short getFileType(); +diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/proc/ProcDebuggerLocal.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/proc/ProcDebuggerLocal.java +index ca1a2575ff..2afa6c55f8 100644 +--- a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/proc/ProcDebuggerLocal.java ++++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/proc/ProcDebuggerLocal.java +@@ -34,10 +34,14 @@ import sun.jvm.hotspot.debugger.proc.amd64.*; + import sun.jvm.hotspot.debugger.proc.aarch64.*; + import sun.jvm.hotspot.debugger.proc.sparc.*; + import sun.jvm.hotspot.debugger.proc.x86.*; ++import sun.jvm.hotspot.debugger.proc.mips64.*; ++import sun.jvm.hotspot.debugger.proc.loongarch64.*; + import sun.jvm.hotspot.debugger.amd64.*; + import sun.jvm.hotspot.debugger.aarch64.*; + import sun.jvm.hotspot.debugger.sparc.*; + import sun.jvm.hotspot.debugger.x86.*; ++import sun.jvm.hotspot.debugger.mips64.*; ++import sun.jvm.hotspot.debugger.loongarch64.*; + import sun.jvm.hotspot.utilities.*; + + /**

An implementation of the JVMDebugger interface which sits on +@@ -92,6 +96,14 @@ public class ProcDebuggerLocal extends DebuggerBase implements ProcDebugger { + threadFactory = new ProcAARCH64ThreadFactory(this); + pcRegIndex = AARCH64ThreadContext.PC; + fpRegIndex = AARCH64ThreadContext.FP; ++ } else if (cpu.equals("mips64") || cpu.equals("mips64el")) { ++ threadFactory = new ProcMIPS64ThreadFactory(this); ++ pcRegIndex = MIPS64ThreadContext.PC; ++ fpRegIndex = MIPS64ThreadContext.FP; ++ } else if (cpu.equals("loongarch64")) { ++ threadFactory = new ProcLOONGARCH64ThreadFactory(this); ++ pcRegIndex = LOONGARCH64ThreadContext.PC; ++ fpRegIndex = LOONGARCH64ThreadContext.FP; + } else { + try { + Class tfc = Class.forName("sun.jvm.hotspot.debugger.proc." + +diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/proc/loongarch64/ProcLOONGARCH64Thread.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/proc/loongarch64/ProcLOONGARCH64Thread.java +new file mode 100644 +index 0000000000..42a31e3486 +--- /dev/null ++++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/proc/loongarch64/ProcLOONGARCH64Thread.java +@@ -0,0 +1,92 @@ ++/* ++ * Copyright (c) 2002, 2003, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2015, 2020, Loongson Technology. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ * ++ */ ++ ++package sun.jvm.hotspot.debugger.proc.loongarch64; ++ ++import sun.jvm.hotspot.debugger.*; ++import sun.jvm.hotspot.debugger.loongarch64.*; ++import sun.jvm.hotspot.debugger.proc.*; ++import sun.jvm.hotspot.utilities.*; ++ ++public class ProcLOONGARCH64Thread implements ThreadProxy { ++ private ProcDebugger debugger; ++ private int id; ++ ++ public ProcLOONGARCH64Thread(ProcDebugger debugger, Address addr) { ++ this.debugger = debugger; ++ ++ // FIXME: the size here should be configurable. However, making it ++ // so would produce a dependency on the "types" package from the ++ // debugger package, which is not desired. ++ this.id = (int) addr.getCIntegerAt(0, 4, true); ++ } ++ ++ public ProcLOONGARCH64Thread(ProcDebugger debugger, long id) { ++ this.debugger = debugger; ++ this.id = (int) id; ++ } ++ ++ public ThreadContext getContext() throws IllegalThreadStateException { ++ ProcLOONGARCH64ThreadContext context = new ProcLOONGARCH64ThreadContext(debugger); ++ long[] regs = debugger.getThreadIntegerRegisterSet(id); ++ /* ++ _NGREG in reg.h is defined to be 19. Because we have included ++ debug registers LOONGARCH64ThreadContext.NPRGREG is 25. ++ */ ++ ++ if (Assert.ASSERTS_ENABLED) { ++ Assert.that(regs.length <= LOONGARCH64ThreadContext.NPRGREG, "size of register set is greater than " + LOONGARCH64ThreadContext.NPRGREG); ++ } ++ for (int i = 0; i < regs.length; i++) { ++ context.setRegister(i, regs[i]); ++ } ++ return context; ++ } ++ ++ public boolean canSetContext() throws DebuggerException { ++ return false; ++ } ++ ++ public void setContext(ThreadContext context) ++ throws IllegalThreadStateException, DebuggerException { ++ throw new DebuggerException("Unimplemented"); ++ } ++ ++ public String toString() { ++ return "t@" + id; ++ } ++ ++ public boolean equals(Object obj) { ++ if ((obj == null) || !(obj instanceof ProcLOONGARCH64Thread)) { ++ return false; ++ } ++ ++ return (((ProcLOONGARCH64Thread) obj).id == id); ++ } ++ ++ public int hashCode() { ++ return id; ++ } ++} +diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/proc/loongarch64/ProcLOONGARCH64ThreadContext.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/proc/loongarch64/ProcLOONGARCH64ThreadContext.java +new file mode 100644 +index 0000000000..9054f16506 +--- /dev/null ++++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/proc/loongarch64/ProcLOONGARCH64ThreadContext.java +@@ -0,0 +1,47 @@ ++/* ++ * Copyright (c) 2002, 2003, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2015, 2020, Loongson Technology. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ * ++ */ ++ ++package sun.jvm.hotspot.debugger.proc.loongarch64; ++ ++import sun.jvm.hotspot.debugger.*; ++import sun.jvm.hotspot.debugger.loongarch64.*; ++import sun.jvm.hotspot.debugger.proc.*; ++ ++public class ProcLOONGARCH64ThreadContext extends LOONGARCH64ThreadContext { ++ private ProcDebugger debugger; ++ ++ public ProcLOONGARCH64ThreadContext(ProcDebugger debugger) { ++ super(); ++ this.debugger = debugger; ++ } ++ ++ public void setRegisterAsAddress(int index, Address value) { ++ setRegister(index, debugger.getAddressValue(value)); ++ } ++ ++ public Address getRegisterAsAddress(int index) { ++ return debugger.newAddress(getRegister(index)); ++ } ++} +diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/proc/loongarch64/ProcLOONGARCH64ThreadFactory.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/proc/loongarch64/ProcLOONGARCH64ThreadFactory.java +new file mode 100644 +index 0000000000..bc64335124 +--- /dev/null ++++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/proc/loongarch64/ProcLOONGARCH64ThreadFactory.java +@@ -0,0 +1,45 @@ ++/* ++ * Copyright (c) 2002, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2015, 2020, Loongson Technology. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ * ++ */ ++ ++package sun.jvm.hotspot.debugger.proc.loongarch64; ++ ++import sun.jvm.hotspot.debugger.*; ++import sun.jvm.hotspot.debugger.proc.*; ++ ++public class ProcLOONGARCH64ThreadFactory implements ProcThreadFactory { ++ private ProcDebugger debugger; ++ ++ public ProcLOONGARCH64ThreadFactory(ProcDebugger debugger) { ++ this.debugger = debugger; ++ } ++ ++ public ThreadProxy createThreadWrapper(Address threadIdentifierAddr) { ++ return new ProcLOONGARCH64Thread(debugger, threadIdentifierAddr); ++ } ++ ++ public ThreadProxy createThreadWrapper(long id) { ++ return new ProcLOONGARCH64Thread(debugger, id); ++ } ++} +diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/proc/mips64/ProcMIPS64Thread.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/proc/mips64/ProcMIPS64Thread.java +new file mode 100644 +index 0000000000..5c1e0be893 +--- /dev/null ++++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/proc/mips64/ProcMIPS64Thread.java +@@ -0,0 +1,92 @@ ++/* ++ * Copyright (c) 2002, 2003, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2015, 2018, Loongson Technology. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ * ++ */ ++ ++package sun.jvm.hotspot.debugger.proc.mips64; ++ ++import sun.jvm.hotspot.debugger.*; ++import sun.jvm.hotspot.debugger.mips64.*; ++import sun.jvm.hotspot.debugger.proc.*; ++import sun.jvm.hotspot.utilities.*; ++ ++public class ProcMIPS64Thread implements ThreadProxy { ++ private ProcDebugger debugger; ++ private int id; ++ ++ public ProcMIPS64Thread(ProcDebugger debugger, Address addr) { ++ this.debugger = debugger; ++ ++ // FIXME: the size here should be configurable. However, making it ++ // so would produce a dependency on the "types" package from the ++ // debugger package, which is not desired. ++ this.id = (int) addr.getCIntegerAt(0, 4, true); ++ } ++ ++ public ProcMIPS64Thread(ProcDebugger debugger, long id) { ++ this.debugger = debugger; ++ this.id = (int) id; ++ } ++ ++ public ThreadContext getContext() throws IllegalThreadStateException { ++ ProcMIPS64ThreadContext context = new ProcMIPS64ThreadContext(debugger); ++ long[] regs = debugger.getThreadIntegerRegisterSet(id); ++ /* ++ _NGREG in reg.h is defined to be 19. Because we have included ++ debug registers MIPS64ThreadContext.NPRGREG is 25. ++ */ ++ ++ if (Assert.ASSERTS_ENABLED) { ++ Assert.that(regs.length <= MIPS64ThreadContext.NPRGREG, "size of register set is greater than " + MIPS64ThreadContext.NPRGREG); ++ } ++ for (int i = 0; i < regs.length; i++) { ++ context.setRegister(i, regs[i]); ++ } ++ return context; ++ } ++ ++ public boolean canSetContext() throws DebuggerException { ++ return false; ++ } ++ ++ public void setContext(ThreadContext context) ++ throws IllegalThreadStateException, DebuggerException { ++ throw new DebuggerException("Unimplemented"); ++ } ++ ++ public String toString() { ++ return "t@" + id; ++ } ++ ++ public boolean equals(Object obj) { ++ if ((obj == null) || !(obj instanceof ProcMIPS64Thread)) { ++ return false; ++ } ++ ++ return (((ProcMIPS64Thread) obj).id == id); ++ } ++ ++ public int hashCode() { ++ return id; ++ } ++} +diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/proc/mips64/ProcMIPS64ThreadContext.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/proc/mips64/ProcMIPS64ThreadContext.java +new file mode 100644 +index 0000000000..d44223d768 +--- /dev/null ++++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/proc/mips64/ProcMIPS64ThreadContext.java +@@ -0,0 +1,47 @@ ++/* ++ * Copyright (c) 2002, 2003, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2015, 2018, Loongson Technology. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ * ++ */ ++ ++package sun.jvm.hotspot.debugger.proc.mips64; ++ ++import sun.jvm.hotspot.debugger.*; ++import sun.jvm.hotspot.debugger.mips64.*; ++import sun.jvm.hotspot.debugger.proc.*; ++ ++public class ProcMIPS64ThreadContext extends MIPS64ThreadContext { ++ private ProcDebugger debugger; ++ ++ public ProcMIPS64ThreadContext(ProcDebugger debugger) { ++ super(); ++ this.debugger = debugger; ++ } ++ ++ public void setRegisterAsAddress(int index, Address value) { ++ setRegister(index, debugger.getAddressValue(value)); ++ } ++ ++ public Address getRegisterAsAddress(int index) { ++ return debugger.newAddress(getRegister(index)); ++ } ++} +diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/proc/mips64/ProcMIPS64ThreadFactory.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/proc/mips64/ProcMIPS64ThreadFactory.java +new file mode 100644 +index 0000000000..bad478fc5c +--- /dev/null ++++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/proc/mips64/ProcMIPS64ThreadFactory.java +@@ -0,0 +1,45 @@ ++/* ++ * Copyright (c) 2002, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2015, 2018, Loongson Technology. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ * ++ */ ++ ++package sun.jvm.hotspot.debugger.proc.mips64; ++ ++import sun.jvm.hotspot.debugger.*; ++import sun.jvm.hotspot.debugger.proc.*; ++ ++public class ProcMIPS64ThreadFactory implements ProcThreadFactory { ++ private ProcDebugger debugger; ++ ++ public ProcMIPS64ThreadFactory(ProcDebugger debugger) { ++ this.debugger = debugger; ++ } ++ ++ public ThreadProxy createThreadWrapper(Address threadIdentifierAddr) { ++ return new ProcMIPS64Thread(debugger, threadIdentifierAddr); ++ } ++ ++ public ThreadProxy createThreadWrapper(long id) { ++ return new ProcMIPS64Thread(debugger, id); ++ } ++} +diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/remote/RemoteDebuggerClient.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/remote/RemoteDebuggerClient.java +index ffa61b548e..9cf3ee2da3 100644 +--- a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/remote/RemoteDebuggerClient.java ++++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/remote/RemoteDebuggerClient.java +@@ -33,6 +33,8 @@ import sun.jvm.hotspot.debugger.cdbg.*; + import sun.jvm.hotspot.debugger.remote.sparc.*; + import sun.jvm.hotspot.debugger.remote.x86.*; + import sun.jvm.hotspot.debugger.remote.amd64.*; ++import sun.jvm.hotspot.debugger.remote.mips64.*; ++import sun.jvm.hotspot.debugger.remote.loongarch64.*; + + /** An implementation of Debugger which wraps a + RemoteDebugger, providing remote debugging via RMI. +@@ -70,6 +72,16 @@ public class RemoteDebuggerClient extends DebuggerBase implements JVMDebugger { + cachePageSize = 4096; + cacheNumPages = parseCacheNumPagesProperty(cacheSize / cachePageSize); + unalignedAccessesOkay = true; ++ } else if (cpu.equals("mips64") || cpu.equals("mips64el")) { ++ threadFactory = new RemoteMIPS64ThreadFactory(this); ++ cachePageSize = 4096; ++ cacheNumPages = parseCacheNumPagesProperty(cacheSize / cachePageSize); ++ unalignedAccessesOkay = true; ++ } else if (cpu.equals("loongarch64")) { ++ threadFactory = new RemoteLOONGARCH64ThreadFactory(this); ++ cachePageSize = 4096; ++ cacheNumPages = parseCacheNumPagesProperty(cacheSize / cachePageSize); ++ unalignedAccessesOkay = true; + } else { + try { + Class tf = Class.forName("sun.jvm.hotspot.debugger.remote." + +diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/remote/loongarch64/RemoteLOONGARCH64Thread.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/remote/loongarch64/RemoteLOONGARCH64Thread.java +new file mode 100644 +index 0000000000..01e3f8954b +--- /dev/null ++++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/remote/loongarch64/RemoteLOONGARCH64Thread.java +@@ -0,0 +1,54 @@ ++/* ++ * Copyright (c) 2002, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2015, 2020, Loongson Technology. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ * ++ */ ++ ++package sun.jvm.hotspot.debugger.remote.loongarch64; ++ ++import sun.jvm.hotspot.debugger.*; ++import sun.jvm.hotspot.debugger.loongarch64.*; ++import sun.jvm.hotspot.debugger.remote.*; ++import sun.jvm.hotspot.utilities.*; ++ ++public class RemoteLOONGARCH64Thread extends RemoteThread { ++ public RemoteLOONGARCH64Thread(RemoteDebuggerClient debugger, Address addr) { ++ super(debugger, addr); ++ } ++ ++ public RemoteLOONGARCH64Thread(RemoteDebuggerClient debugger, long id) { ++ super(debugger, id); ++ } ++ ++ public ThreadContext getContext() throws IllegalThreadStateException { ++ RemoteLOONGARCH64ThreadContext context = new RemoteLOONGARCH64ThreadContext(debugger); ++ long[] regs = (addr != null)? debugger.getThreadIntegerRegisterSet(addr) : ++ debugger.getThreadIntegerRegisterSet(id); ++ if (Assert.ASSERTS_ENABLED) { ++ Assert.that(regs.length == LOONGARCH64ThreadContext.NPRGREG, "size of register set must match"); ++ } ++ for (int i = 0; i < regs.length; i++) { ++ context.setRegister(i, regs[i]); ++ } ++ return context; ++ } ++} +diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/remote/loongarch64/RemoteLOONGARCH64ThreadContext.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/remote/loongarch64/RemoteLOONGARCH64ThreadContext.java +new file mode 100644 +index 0000000000..ad25bccc8d +--- /dev/null ++++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/remote/loongarch64/RemoteLOONGARCH64ThreadContext.java +@@ -0,0 +1,51 @@ ++/* ++ * Copyright (c) 2002, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2015, 2020, Loongson Technology. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ * ++ */ ++ ++package sun.jvm.hotspot.debugger.remote.loongarch64; ++ ++import sun.jvm.hotspot.debugger.*; ++import sun.jvm.hotspot.debugger.loongarch64.*; ++import sun.jvm.hotspot.debugger.remote.*; ++ ++public class RemoteLOONGARCH64ThreadContext extends LOONGARCH64ThreadContext { ++ private RemoteDebuggerClient debugger; ++ ++ public RemoteLOONGARCH64ThreadContext(RemoteDebuggerClient debugger) { ++ super(); ++ this.debugger = debugger; ++ } ++ ++ /** This can't be implemented in this class since we would have to ++ tie the implementation to, for example, the debugging system */ ++ public void setRegisterAsAddress(int index, Address value) { ++ setRegister(index, debugger.getAddressValue(value)); ++ } ++ ++ /** This can't be implemented in this class since we would have to ++ tie the implementation to, for example, the debugging system */ ++ public Address getRegisterAsAddress(int index) { ++ return debugger.newAddress(getRegister(index)); ++ } ++} +diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/remote/loongarch64/RemoteLOONGARCH64ThreadFactory.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/remote/loongarch64/RemoteLOONGARCH64ThreadFactory.java +new file mode 100644 +index 0000000000..d8bf50ea5b +--- /dev/null ++++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/remote/loongarch64/RemoteLOONGARCH64ThreadFactory.java +@@ -0,0 +1,45 @@ ++/* ++ * Copyright (c) 2002, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2015, 2020, Loongson Technology. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ * ++ */ ++ ++package sun.jvm.hotspot.debugger.remote.loongarch64; ++ ++import sun.jvm.hotspot.debugger.*; ++import sun.jvm.hotspot.debugger.remote.*; ++ ++public class RemoteLOONGARCH64ThreadFactory implements RemoteThreadFactory { ++ private RemoteDebuggerClient debugger; ++ ++ public RemoteLOONGARCH64ThreadFactory(RemoteDebuggerClient debugger) { ++ this.debugger = debugger; ++ } ++ ++ public ThreadProxy createThreadWrapper(Address threadIdentifierAddr) { ++ return new RemoteLOONGARCH64Thread(debugger, threadIdentifierAddr); ++ } ++ ++ public ThreadProxy createThreadWrapper(long id) { ++ return new RemoteLOONGARCH64Thread(debugger, id); ++ } ++} +diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/remote/mips64/RemoteMIPS64Thread.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/remote/mips64/RemoteMIPS64Thread.java +new file mode 100644 +index 0000000000..a9285a3b94 +--- /dev/null ++++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/remote/mips64/RemoteMIPS64Thread.java +@@ -0,0 +1,54 @@ ++/* ++ * Copyright (c) 2002, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2015, 2018, Loongson Technology. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ * ++ */ ++ ++package sun.jvm.hotspot.debugger.remote.mips64; ++ ++import sun.jvm.hotspot.debugger.*; ++import sun.jvm.hotspot.debugger.mips64.*; ++import sun.jvm.hotspot.debugger.remote.*; ++import sun.jvm.hotspot.utilities.*; ++ ++public class RemoteMIPS64Thread extends RemoteThread { ++ public RemoteMIPS64Thread(RemoteDebuggerClient debugger, Address addr) { ++ super(debugger, addr); ++ } ++ ++ public RemoteMIPS64Thread(RemoteDebuggerClient debugger, long id) { ++ super(debugger, id); ++ } ++ ++ public ThreadContext getContext() throws IllegalThreadStateException { ++ RemoteMIPS64ThreadContext context = new RemoteMIPS64ThreadContext(debugger); ++ long[] regs = (addr != null)? debugger.getThreadIntegerRegisterSet(addr) : ++ debugger.getThreadIntegerRegisterSet(id); ++ if (Assert.ASSERTS_ENABLED) { ++ Assert.that(regs.length == MIPS64ThreadContext.NPRGREG, "size of register set must match"); ++ } ++ for (int i = 0; i < regs.length; i++) { ++ context.setRegister(i, regs[i]); ++ } ++ return context; ++ } ++} +diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/remote/mips64/RemoteMIPS64ThreadContext.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/remote/mips64/RemoteMIPS64ThreadContext.java +new file mode 100644 +index 0000000000..4d711f9ba7 +--- /dev/null ++++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/remote/mips64/RemoteMIPS64ThreadContext.java +@@ -0,0 +1,51 @@ ++/* ++ * Copyright (c) 2002, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2015, 2018, Loongson Technology. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ * ++ */ ++ ++package sun.jvm.hotspot.debugger.remote.mips64; ++ ++import sun.jvm.hotspot.debugger.*; ++import sun.jvm.hotspot.debugger.mips64.*; ++import sun.jvm.hotspot.debugger.remote.*; ++ ++public class RemoteMIPS64ThreadContext extends MIPS64ThreadContext { ++ private RemoteDebuggerClient debugger; ++ ++ public RemoteMIPS64ThreadContext(RemoteDebuggerClient debugger) { ++ super(); ++ this.debugger = debugger; ++ } ++ ++ /** This can't be implemented in this class since we would have to ++ tie the implementation to, for example, the debugging system */ ++ public void setRegisterAsAddress(int index, Address value) { ++ setRegister(index, debugger.getAddressValue(value)); ++ } ++ ++ /** This can't be implemented in this class since we would have to ++ tie the implementation to, for example, the debugging system */ ++ public Address getRegisterAsAddress(int index) { ++ return debugger.newAddress(getRegister(index)); ++ } ++} +diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/remote/mips64/RemoteMIPS64ThreadFactory.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/remote/mips64/RemoteMIPS64ThreadFactory.java +new file mode 100644 +index 0000000000..020a2f1ff9 +--- /dev/null ++++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/remote/mips64/RemoteMIPS64ThreadFactory.java +@@ -0,0 +1,45 @@ ++/* ++ * Copyright (c) 2002, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2015, 2018, Loongson Technology. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ * ++ */ ++ ++package sun.jvm.hotspot.debugger.remote.mips64; ++ ++import sun.jvm.hotspot.debugger.*; ++import sun.jvm.hotspot.debugger.remote.*; ++ ++public class RemoteMIPS64ThreadFactory implements RemoteThreadFactory { ++ private RemoteDebuggerClient debugger; ++ ++ public RemoteMIPS64ThreadFactory(RemoteDebuggerClient debugger) { ++ this.debugger = debugger; ++ } ++ ++ public ThreadProxy createThreadWrapper(Address threadIdentifierAddr) { ++ return new RemoteMIPS64Thread(debugger, threadIdentifierAddr); ++ } ++ ++ public ThreadProxy createThreadWrapper(long id) { ++ return new RemoteMIPS64Thread(debugger, id); ++ } ++} +diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/Threads.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/Threads.java +index 842a3b357d..81efdd02f8 100644 +--- a/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/Threads.java ++++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/Threads.java +@@ -34,6 +34,8 @@ import sun.jvm.hotspot.runtime.win32_amd64.Win32AMD64JavaThreadPDAccess; + import sun.jvm.hotspot.runtime.win32_x86.Win32X86JavaThreadPDAccess; + import sun.jvm.hotspot.runtime.linux_x86.LinuxX86JavaThreadPDAccess; + import sun.jvm.hotspot.runtime.linux_amd64.LinuxAMD64JavaThreadPDAccess; ++import sun.jvm.hotspot.runtime.linux_mips64.LinuxMIPS64JavaThreadPDAccess; ++import sun.jvm.hotspot.runtime.linux_loongarch64.LinuxLOONGARCH64JavaThreadPDAccess; + import sun.jvm.hotspot.runtime.linux_sparc.LinuxSPARCJavaThreadPDAccess; + import sun.jvm.hotspot.runtime.linux_aarch64.LinuxAARCH64JavaThreadPDAccess; + import sun.jvm.hotspot.runtime.bsd_x86.BsdX86JavaThreadPDAccess; +@@ -90,6 +92,10 @@ public class Threads { + access = new LinuxSPARCJavaThreadPDAccess(); + } else if (cpu.equals("aarch64")) { + access = new LinuxAARCH64JavaThreadPDAccess(); ++ } else if (cpu.equals("mips64")) { ++ access = new LinuxMIPS64JavaThreadPDAccess(); ++ } else if (cpu.equals("loongarch64")) { ++ access = new LinuxLOONGARCH64JavaThreadPDAccess(); + } else { + try { + access = (JavaThreadPDAccess) +diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/linux_loongarch64/LinuxLOONGARCH64JavaThreadPDAccess.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/linux_loongarch64/LinuxLOONGARCH64JavaThreadPDAccess.java +new file mode 100644 +index 0000000000..77c45c2e99 +--- /dev/null ++++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/linux_loongarch64/LinuxLOONGARCH64JavaThreadPDAccess.java +@@ -0,0 +1,133 @@ ++/* ++ * Copyright (c) 2003, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2015, 2021, Loongson Technology. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ * ++ */ ++ ++package sun.jvm.hotspot.runtime.linux_loongarch64; ++ ++import java.io.*; ++import java.util.*; ++ ++import sun.jvm.hotspot.debugger.*; ++import sun.jvm.hotspot.debugger.loongarch64.*; ++import sun.jvm.hotspot.runtime.*; ++import sun.jvm.hotspot.runtime.loongarch64.*; ++import sun.jvm.hotspot.types.*; ++import sun.jvm.hotspot.utilities.*; ++ ++public class LinuxLOONGARCH64JavaThreadPDAccess implements JavaThreadPDAccess { ++ private static AddressField lastJavaFPField; ++ private static AddressField osThreadField; ++ ++ // Field from OSThread ++ private static CIntegerField osThreadThreadIDField; ++ ++ // This is currently unneeded but is being kept in case we change ++ // the currentFrameGuess algorithm ++ private static final long GUESS_SCAN_RANGE = 128 * 1024; ++ ++ static { ++ VM.registerVMInitializedObserver(new Observer() { ++ public void update(Observable o, Object data) { ++ initialize(VM.getVM().getTypeDataBase()); ++ } ++ }); ++ } ++ ++ private static synchronized void initialize(TypeDataBase db) { ++ Type type = db.lookupType("JavaThread"); ++ osThreadField = type.getAddressField("_osthread"); ++ ++ Type anchorType = db.lookupType("JavaFrameAnchor"); ++ lastJavaFPField = anchorType.getAddressField("_last_Java_fp"); ++ ++ Type osThreadType = db.lookupType("OSThread"); ++ osThreadThreadIDField = osThreadType.getCIntegerField("_thread_id"); ++ } ++ ++ public Address getLastJavaFP(Address addr) { ++ return lastJavaFPField.getValue(addr.addOffsetTo(sun.jvm.hotspot.runtime.JavaThread.getAnchorField().getOffset())); ++ } ++ ++ public Address getLastJavaPC(Address addr) { ++ return null; ++ } ++ ++ public Address getBaseOfStackPointer(Address addr) { ++ return null; ++ } ++ ++ public Frame getLastFramePD(JavaThread thread, Address addr) { ++ Address fp = thread.getLastJavaFP(); ++ if (fp == null) { ++ return null; // no information ++ } ++ return new LOONGARCH64Frame(thread.getLastJavaSP(), fp); ++ } ++ ++ public RegisterMap newRegisterMap(JavaThread thread, boolean updateMap) { ++ return new LOONGARCH64RegisterMap(thread, updateMap); ++ } ++ ++ public Frame getCurrentFrameGuess(JavaThread thread, Address addr) { ++ ThreadProxy t = getThreadProxy(addr); ++ LOONGARCH64ThreadContext context = (LOONGARCH64ThreadContext) t.getContext(); ++ LOONGARCH64CurrentFrameGuess guesser = new LOONGARCH64CurrentFrameGuess(context, thread); ++ if (!guesser.run(GUESS_SCAN_RANGE)) { ++ return null; ++ } ++ if (guesser.getPC() == null) { ++ return new LOONGARCH64Frame(guesser.getSP(), guesser.getFP()); ++ } else { ++ return new LOONGARCH64Frame(guesser.getSP(), guesser.getFP(), guesser.getPC()); ++ } ++ } ++ ++ public void printThreadIDOn(Address addr, PrintStream tty) { ++ tty.print(getThreadProxy(addr)); ++ } ++ ++ public void printInfoOn(Address threadAddr, PrintStream tty) { ++ tty.print("Thread id: "); ++ printThreadIDOn(threadAddr, tty); ++ // tty.println("\nPostJavaState: " + getPostJavaState(threadAddr)); ++ } ++ ++ public Address getLastSP(Address addr) { ++ ThreadProxy t = getThreadProxy(addr); ++ LOONGARCH64ThreadContext context = (LOONGARCH64ThreadContext) t.getContext(); ++ return context.getRegisterAsAddress(LOONGARCH64ThreadContext.SP); ++ } ++ ++ public ThreadProxy getThreadProxy(Address addr) { ++ // Addr is the address of the JavaThread. ++ // Fetch the OSThread (for now and for simplicity, not making a ++ // separate "OSThread" class in this package) ++ Address osThreadAddr = osThreadField.getValue(addr); ++ // Get the address of the _thread_id from the OSThread ++ Address threadIdAddr = osThreadAddr.addOffsetTo(osThreadThreadIDField.getOffset()); ++ ++ JVMDebugger debugger = VM.getVM().getDebugger(); ++ return debugger.getThreadForIdentifierAddress(threadIdAddr); ++ } ++} +diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/linux_mips64/LinuxMIPS64JavaThreadPDAccess.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/linux_mips64/LinuxMIPS64JavaThreadPDAccess.java +new file mode 100644 +index 0000000000..a0fd73fa67 +--- /dev/null ++++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/linux_mips64/LinuxMIPS64JavaThreadPDAccess.java +@@ -0,0 +1,132 @@ ++/* ++ * Copyright (c) 2003, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2015, 2018, Loongson Technology. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ * ++ */ ++ ++package sun.jvm.hotspot.runtime.linux_mips64; ++ ++import java.io.*; ++import java.util.*; ++import sun.jvm.hotspot.debugger.*; ++import sun.jvm.hotspot.debugger.mips64.*; ++import sun.jvm.hotspot.runtime.*; ++import sun.jvm.hotspot.runtime.mips64.*; ++import sun.jvm.hotspot.types.*; ++import sun.jvm.hotspot.utilities.*; ++ ++public class LinuxMIPS64JavaThreadPDAccess implements JavaThreadPDAccess { ++ private static AddressField lastJavaFPField; ++ private static AddressField osThreadField; ++ ++ // Field from OSThread ++ private static CIntegerField osThreadThreadIDField; ++ ++ // This is currently unneeded but is being kept in case we change ++ // the currentFrameGuess algorithm ++ private static final long GUESS_SCAN_RANGE = 128 * 1024; ++ ++ static { ++ VM.registerVMInitializedObserver(new Observer() { ++ public void update(Observable o, Object data) { ++ initialize(VM.getVM().getTypeDataBase()); ++ } ++ }); ++ } ++ ++ private static synchronized void initialize(TypeDataBase db) { ++ Type type = db.lookupType("JavaThread"); ++ osThreadField = type.getAddressField("_osthread"); ++ ++ Type anchorType = db.lookupType("JavaFrameAnchor"); ++ lastJavaFPField = anchorType.getAddressField("_last_Java_fp"); ++ ++ Type osThreadType = db.lookupType("OSThread"); ++ osThreadThreadIDField = osThreadType.getCIntegerField("_thread_id"); ++ } ++ ++ public Address getLastJavaFP(Address addr) { ++ return lastJavaFPField.getValue(addr.addOffsetTo(sun.jvm.hotspot.runtime.JavaThread.getAnchorField().getOffset())); ++ } ++ ++ public Address getLastJavaPC(Address addr) { ++ return null; ++ } ++ ++ public Address getBaseOfStackPointer(Address addr) { ++ return null; ++ } ++ ++ public Frame getLastFramePD(JavaThread thread, Address addr) { ++ Address fp = thread.getLastJavaFP(); ++ if (fp == null) { ++ return null; // no information ++ } ++ return new MIPS64Frame(thread.getLastJavaSP(), fp); ++ } ++ ++ public RegisterMap newRegisterMap(JavaThread thread, boolean updateMap) { ++ return new MIPS64RegisterMap(thread, updateMap); ++ } ++ ++ public Frame getCurrentFrameGuess(JavaThread thread, Address addr) { ++ ThreadProxy t = getThreadProxy(addr); ++ MIPS64ThreadContext context = (MIPS64ThreadContext) t.getContext(); ++ MIPS64CurrentFrameGuess guesser = new MIPS64CurrentFrameGuess(context, thread); ++ if (!guesser.run(GUESS_SCAN_RANGE)) { ++ return null; ++ } ++ if (guesser.getPC() == null) { ++ return new MIPS64Frame(guesser.getSP(), guesser.getFP()); ++ } else { ++ return new MIPS64Frame(guesser.getSP(), guesser.getFP(), guesser.getPC()); ++ } ++ } ++ ++ public void printThreadIDOn(Address addr, PrintStream tty) { ++ tty.print(getThreadProxy(addr)); ++ } ++ ++ public void printInfoOn(Address threadAddr, PrintStream tty) { ++ tty.print("Thread id: "); ++ printThreadIDOn(threadAddr, tty); ++// tty.println("\nPostJavaState: " + getPostJavaState(threadAddr)); ++ } ++ ++ public Address getLastSP(Address addr) { ++ ThreadProxy t = getThreadProxy(addr); ++ MIPS64ThreadContext context = (MIPS64ThreadContext) t.getContext(); ++ return context.getRegisterAsAddress(MIPS64ThreadContext.SP); ++ } ++ ++ public ThreadProxy getThreadProxy(Address addr) { ++ // Addr is the address of the JavaThread. ++ // Fetch the OSThread (for now and for simplicity, not making a ++ // separate "OSThread" class in this package) ++ Address osThreadAddr = osThreadField.getValue(addr); ++ // Get the address of the _thread_id from the OSThread ++ Address threadIdAddr = osThreadAddr.addOffsetTo(osThreadThreadIDField.getOffset()); ++ ++ JVMDebugger debugger = VM.getVM().getDebugger(); ++ return debugger.getThreadForIdentifierAddress(threadIdAddr); ++ } ++} +diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/loongarch64/LOONGARCH64CurrentFrameGuess.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/loongarch64/LOONGARCH64CurrentFrameGuess.java +new file mode 100644 +index 0000000000..0208e6e224 +--- /dev/null ++++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/loongarch64/LOONGARCH64CurrentFrameGuess.java +@@ -0,0 +1,217 @@ ++/* ++ * Copyright (c) 2001, 2006, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2015, 2020, Loongson Technology. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ * ++ */ ++ ++package sun.jvm.hotspot.runtime.loongarch64; ++ ++import sun.jvm.hotspot.debugger.*; ++import sun.jvm.hotspot.debugger.loongarch64.*; ++import sun.jvm.hotspot.code.*; ++import sun.jvm.hotspot.interpreter.*; ++import sun.jvm.hotspot.runtime.*; ++ ++/**

Should be able to be used on all loongarch64 platforms we support ++ (Win32, Solaris/loongarch64, and soon Linux) to implement JavaThread's ++ "currentFrameGuess()" functionality. Input is an LOONGARCH64ThreadContext; ++ output is SP, FP, and PC for an LOONGARCH64Frame. Instantiation of the ++ LOONGARCH64Frame is left to the caller, since we may need to subclass ++ LOONGARCH64Frame to support signal handler frames on Unix platforms.

++ ++

Algorithm is to walk up the stack within a given range (say, ++ 512K at most) looking for a plausible PC and SP for a Java frame, ++ also considering those coming in from the context. If we find a PC ++ that belongs to the VM (i.e., in generated code like the ++ interpreter or CodeCache) then we try to find an associated EBP. ++ We repeat this until we either find a complete frame or run out of ++ stack to look at.

*/ ++ ++public class LOONGARCH64CurrentFrameGuess { ++ private LOONGARCH64ThreadContext context; ++ private JavaThread thread; ++ private Address spFound; ++ private Address fpFound; ++ private Address pcFound; ++ ++ private static final boolean DEBUG = System.getProperty("sun.jvm.hotspot.runtime.loongarch64.LOONGARCH64Frame.DEBUG") ++ != null; ++ ++ public LOONGARCH64CurrentFrameGuess(LOONGARCH64ThreadContext context, ++ JavaThread thread) { ++ this.context = context; ++ this.thread = thread; ++ } ++ ++ /** Returns false if not able to find a frame within a reasonable range. */ ++ public boolean run(long regionInBytesToSearch) { ++ Address sp = context.getRegisterAsAddress(LOONGARCH64ThreadContext.SP); ++ Address pc = context.getRegisterAsAddress(LOONGARCH64ThreadContext.PC); ++ Address fp = context.getRegisterAsAddress(LOONGARCH64ThreadContext.FP); ++ if (sp == null) { ++ // Bail out if no last java frame eithe ++ if (thread.getLastJavaSP() != null) { ++ setValues(thread.getLastJavaSP(), thread.getLastJavaFP(), null); ++ return true; ++ } ++ // Bail out ++ return false; ++ } ++ Address end = sp.addOffsetTo(regionInBytesToSearch); ++ VM vm = VM.getVM(); ++ ++ setValues(null, null, null); // Assume we're not going to find anything ++ ++ if (vm.isJavaPCDbg(pc)) { ++ if (vm.isClientCompiler()) { ++ // If the topmost frame is a Java frame, we are (pretty much) ++ // guaranteed to have a viable EBP. We should be more robust ++ // than this (we have the potential for losing entire threads' ++ // stack traces) but need to see how much work we really have ++ // to do here. Searching the stack for an (SP, FP) pair is ++ // hard since it's easy to misinterpret inter-frame stack ++ // pointers as base-of-frame pointers; we also don't know the ++ // sizes of C1 frames (not registered in the nmethod) so can't ++ // derive them from ESP. ++ ++ setValues(sp, fp, pc); ++ return true; ++ } else { ++ if (vm.getInterpreter().contains(pc)) { ++ if (DEBUG) { ++ System.out.println("CurrentFrameGuess: choosing interpreter frame: sp = " + ++ sp + ", fp = " + fp + ", pc = " + pc); ++ } ++ setValues(sp, fp, pc); ++ return true; ++ } ++ ++ // For the server compiler, EBP is not guaranteed to be valid ++ // for compiled code. In addition, an earlier attempt at a ++ // non-searching algorithm (see below) failed because the ++ // stack pointer from the thread context was pointing ++ // (considerably) beyond the ostensible end of the stack, into ++ // garbage; walking from the topmost frame back caused a crash. ++ // ++ // This algorithm takes the current PC as a given and tries to ++ // find the correct corresponding SP by walking up the stack ++ // and repeatedly performing stackwalks (very inefficient). ++ // ++ // FIXME: there is something wrong with stackwalking across ++ // adapter frames...this is likely to be the root cause of the ++ // failure with the simpler algorithm below. ++ ++ for (long offset = 0; ++ offset < regionInBytesToSearch; ++ offset += vm.getAddressSize()) { ++ try { ++ Address curSP = sp.addOffsetTo(offset); ++ Frame frame = new LOONGARCH64Frame(curSP, null, pc); ++ RegisterMap map = thread.newRegisterMap(false); ++ while (frame != null) { ++ if (frame.isEntryFrame() && frame.entryFrameIsFirst()) { ++ // We were able to traverse all the way to the ++ // bottommost Java frame. ++ // This sp looks good. Keep it. ++ if (DEBUG) { ++ System.out.println("CurrentFrameGuess: Choosing sp = " + curSP + ", pc = " + pc); ++ } ++ setValues(curSP, null, pc); ++ return true; ++ } ++ frame = frame.sender(map); ++ } ++ } catch (Exception e) { ++ if (DEBUG) { ++ System.out.println("CurrentFrameGuess: Exception " + e + " at offset " + offset); ++ } ++ // Bad SP. Try another. ++ } ++ } ++ ++ // Were not able to find a plausible SP to go with this PC. ++ // Bail out. ++ return false; ++ ++ /* ++ // Original algorithm which does not work because SP was ++ // pointing beyond where it should have: ++ ++ // For the server compiler, EBP is not guaranteed to be valid ++ // for compiled code. We see whether the PC is in the ++ // interpreter and take care of that, otherwise we run code ++ // (unfortunately) duplicated from LOONGARCH64Frame.senderForCompiledFrame. ++ ++ CodeCache cc = vm.getCodeCache(); ++ if (cc.contains(pc)) { ++ CodeBlob cb = cc.findBlob(pc); ++ ++ // See if we can derive a frame pointer from SP and PC ++ // NOTE: This is the code duplicated from LOONGARCH64Frame ++ Address saved_fp = null; ++ int llink_offset = cb.getLinkOffset(); ++ if (llink_offset >= 0) { ++ // Restore base-pointer, since next frame might be an interpreter frame. ++ Address fp_addr = sp.addOffsetTo(VM.getVM().getAddressSize() * llink_offset); ++ saved_fp = fp_addr.getAddressAt(0); ++ } ++ ++ setValues(sp, saved_fp, pc); ++ return true; ++ } ++ */ ++ } ++ } else { ++ // If the current program counter was not known to us as a Java ++ // PC, we currently assume that we are in the run-time system ++ // and attempt to look to thread-local storage for saved ESP and ++ // EBP. Note that if these are null (because we were, in fact, ++ // in Java code, i.e., vtable stubs or similar, and the SA ++ // didn't have enough insight into the target VM to understand ++ // that) then we are going to lose the entire stack trace for ++ // the thread, which is sub-optimal. FIXME. ++ ++ if (DEBUG) { ++ System.out.println("CurrentFrameGuess: choosing last Java frame: sp = " + ++ thread.getLastJavaSP() + ", fp = " + thread.getLastJavaFP()); ++ } ++ if (thread.getLastJavaSP() == null) { ++ return false; // No known Java frames on stack ++ } ++ setValues(thread.getLastJavaSP(), thread.getLastJavaFP(), null); ++ return true; ++ } ++ } ++ ++ public Address getSP() { return spFound; } ++ public Address getFP() { return fpFound; } ++ /** May be null if getting values from thread-local storage; take ++ care to call the correct LOONGARCH64Frame constructor to recover this if ++ necessary */ ++ public Address getPC() { return pcFound; } ++ ++ private void setValues(Address sp, Address fp, Address pc) { ++ spFound = sp; ++ fpFound = fp; ++ pcFound = pc; ++ } ++} +diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/loongarch64/LOONGARCH64Frame.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/loongarch64/LOONGARCH64Frame.java +new file mode 100644 +index 0000000000..fdf0c79c1a +--- /dev/null ++++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/loongarch64/LOONGARCH64Frame.java +@@ -0,0 +1,534 @@ ++/* ++ * Copyright (c) 2001, 2012, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2015, 2020, Loongson Technology. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ * ++ */ ++ ++package sun.jvm.hotspot.runtime.loongarch64; ++ ++import java.util.*; ++import sun.jvm.hotspot.code.*; ++import sun.jvm.hotspot.compiler.*; ++import sun.jvm.hotspot.debugger.*; ++import sun.jvm.hotspot.oops.*; ++import sun.jvm.hotspot.runtime.*; ++import sun.jvm.hotspot.types.*; ++import sun.jvm.hotspot.utilities.*; ++ ++/** Specialization of and implementation of abstract methods of the ++ Frame class for the loongarch64 family of CPUs. */ ++ ++public class LOONGARCH64Frame extends Frame { ++ private static final boolean DEBUG; ++ static { ++ DEBUG = System.getProperty("sun.jvm.hotspot.runtime.loongarch64.LOONGARCH64Frame.DEBUG") != null; ++ } ++ ++ // Java frames ++ private static final int JAVA_FRAME_LINK_OFFSET = 0; ++ private static final int JAVA_FRAME_RETURN_ADDR_OFFSET = 1; ++ private static final int JAVA_FRAME_SENDER_SP_OFFSET = 2; ++ ++ // Native frames ++ private static final int NATIVE_FRAME_LINK_OFFSET = -2; ++ private static final int NATIVE_FRAME_RETURN_ADDR_OFFSET = -1; ++ private static final int NATIVE_FRAME_SENDER_SP_OFFSET = 0; ++ ++ // Interpreter frames ++ private static final int INTERPRETER_FRAME_MIRROR_OFFSET = 2; // for native calls only ++ private static final int INTERPRETER_FRAME_SENDER_SP_OFFSET = -1; ++ private static final int INTERPRETER_FRAME_LAST_SP_OFFSET = INTERPRETER_FRAME_SENDER_SP_OFFSET - 1; ++ private static final int INTERPRETER_FRAME_LOCALS_OFFSET = INTERPRETER_FRAME_LAST_SP_OFFSET - 1; ++ private static final int INTERPRETER_FRAME_METHOD_OFFSET = INTERPRETER_FRAME_LOCALS_OFFSET - 1; ++ private static final int INTERPRETER_FRAME_MDX_OFFSET = INTERPRETER_FRAME_MIRROR_OFFSET - 1; ++ private static final int INTERPRETER_FRAME_CACHE_OFFSET = INTERPRETER_FRAME_MDX_OFFSET - 1; ++ private static final int INTERPRETER_FRAME_BCX_OFFSET = INTERPRETER_FRAME_CACHE_OFFSET - 1; ++ private static final int INTERPRETER_FRAME_INITIAL_SP_OFFSET = INTERPRETER_FRAME_BCX_OFFSET - 1; ++ private static final int INTERPRETER_FRAME_MONITOR_BLOCK_TOP_OFFSET = INTERPRETER_FRAME_INITIAL_SP_OFFSET; ++ private static final int INTERPRETER_FRAME_MONITOR_BLOCK_BOTTOM_OFFSET = INTERPRETER_FRAME_INITIAL_SP_OFFSET; ++ ++ // Entry frames ++ private static final int ENTRY_FRAME_CALL_WRAPPER_OFFSET = -9; ++ ++ // Native frames ++ private static final int NATIVE_FRAME_INITIAL_PARAM_OFFSET = 2; ++ ++ private static VMReg fp = new VMReg(22 << 1); ++ ++ // an additional field beyond sp and pc: ++ Address raw_fp; // frame pointer ++ private Address raw_unextendedSP; ++ ++ private LOONGARCH64Frame() { ++ } ++ ++ private void adjustForDeopt() { ++ if ( pc != null) { ++ // Look for a deopt pc and if it is deopted convert to original pc ++ CodeBlob cb = VM.getVM().getCodeCache().findBlob(pc); ++ if (cb != null && cb.isJavaMethod()) { ++ NMethod nm = (NMethod) cb; ++ if (pc.equals(nm.deoptHandlerBegin())) { ++ if (Assert.ASSERTS_ENABLED) { ++ Assert.that(this.getUnextendedSP() != null, "null SP in Java frame"); ++ } ++ // adjust pc if frame is deoptimized. ++ pc = this.getUnextendedSP().getAddressAt(nm.origPCOffset()); ++ deoptimized = true; ++ } ++ } ++ } ++ } ++ ++ public LOONGARCH64Frame(Address raw_sp, Address raw_fp, Address pc) { ++ this.raw_sp = raw_sp; ++ this.raw_unextendedSP = raw_sp; ++ this.raw_fp = raw_fp; ++ this.pc = pc; ++ adjustUnextendedSP(); ++ ++ // Frame must be fully constructed before this call ++ adjustForDeopt(); ++ ++ if (DEBUG) { ++ System.out.println("LOONGARCH64Frame(sp, fp, pc): " + this); ++ dumpStack(); ++ } ++ } ++ ++ public LOONGARCH64Frame(Address raw_sp, Address raw_fp) { ++ this.raw_sp = raw_sp; ++ this.raw_unextendedSP = raw_sp; ++ this.raw_fp = raw_fp; ++ this.pc = raw_fp.getAddressAt(1 * VM.getVM().getAddressSize()); ++ adjustUnextendedSP(); ++ ++ // Frame must be fully constructed before this call ++ adjustForDeopt(); ++ ++ if (DEBUG) { ++ System.out.println("LOONGARCH64Frame(sp, fp): " + this); ++ dumpStack(); ++ } ++ } ++ ++ public LOONGARCH64Frame(Address raw_sp, Address raw_unextendedSp, Address raw_fp, Address pc) { ++ this.raw_sp = raw_sp; ++ this.raw_unextendedSP = raw_unextendedSp; ++ this.raw_fp = raw_fp; ++ this.pc = pc; ++ adjustUnextendedSP(); ++ ++ // Frame must be fully constructed before this call ++ adjustForDeopt(); ++ ++ if (DEBUG) { ++ System.out.println("LOONGARCH64Frame(sp, unextendedSP, fp, pc): " + this); ++ dumpStack(); ++ } ++ ++ } ++ ++ public Object clone() { ++ LOONGARCH64Frame frame = new LOONGARCH64Frame(); ++ frame.raw_sp = raw_sp; ++ frame.raw_unextendedSP = raw_unextendedSP; ++ frame.raw_fp = raw_fp; ++ frame.pc = pc; ++ frame.deoptimized = deoptimized; ++ return frame; ++ } ++ ++ public boolean equals(Object arg) { ++ if (arg == null) { ++ return false; ++ } ++ ++ if (!(arg instanceof LOONGARCH64Frame)) { ++ return false; ++ } ++ ++ LOONGARCH64Frame other = (LOONGARCH64Frame) arg; ++ ++ return (AddressOps.equal(getSP(), other.getSP()) && ++ AddressOps.equal(getUnextendedSP(), other.getUnextendedSP()) && ++ AddressOps.equal(getFP(), other.getFP()) && ++ AddressOps.equal(getPC(), other.getPC())); ++ } ++ ++ public int hashCode() { ++ if (raw_sp == null) { ++ return 0; ++ } ++ ++ return raw_sp.hashCode(); ++ } ++ ++ public String toString() { ++ return "sp: " + (getSP() == null? "null" : getSP().toString()) + ++ ", unextendedSP: " + (getUnextendedSP() == null? "null" : getUnextendedSP().toString()) + ++ ", fp: " + (getFP() == null? "null" : getFP().toString()) + ++ ", pc: " + (pc == null? "null" : pc.toString()); ++ } ++ ++ // accessors for the instance variables ++ public Address getFP() { return raw_fp; } ++ public Address getSP() { return raw_sp; } ++ public Address getID() { return raw_sp; } ++ ++ // FIXME: not implemented yet (should be done for Solaris/LOONGARCH) ++ public boolean isSignalHandlerFrameDbg() { return false; } ++ public int getSignalNumberDbg() { return 0; } ++ public String getSignalNameDbg() { return null; } ++ ++ public boolean isInterpretedFrameValid() { ++ if (Assert.ASSERTS_ENABLED) { ++ Assert.that(isInterpretedFrame(), "Not an interpreted frame"); ++ } ++ ++ // These are reasonable sanity checks ++ if (getFP() == null || getFP().andWithMask(0x3) != null) { ++ return false; ++ } ++ ++ if (getSP() == null || getSP().andWithMask(0x3) != null) { ++ return false; ++ } ++ ++ if (getFP().addOffsetTo(INTERPRETER_FRAME_INITIAL_SP_OFFSET * VM.getVM().getAddressSize()).lessThan(getSP())) { ++ return false; ++ } ++ ++ // These are hacks to keep us out of trouble. ++ // The problem with these is that they mask other problems ++ if (getFP().lessThanOrEqual(getSP())) { ++ // this attempts to deal with unsigned comparison above ++ return false; ++ } ++ ++ if (getFP().minus(getSP()) > 4096 * VM.getVM().getAddressSize()) { ++ // stack frames shouldn't be large. ++ return false; ++ } ++ ++ return true; ++ } ++ ++ // FIXME: not applicable in current system ++ // void patch_pc(Thread* thread, address pc); ++ ++ public Frame sender(RegisterMap regMap, CodeBlob cb) { ++ LOONGARCH64RegisterMap map = (LOONGARCH64RegisterMap) regMap; ++ ++ if (Assert.ASSERTS_ENABLED) { ++ Assert.that(map != null, "map must be set"); ++ } ++ ++ // Default is we done have to follow them. The sender_for_xxx will ++ // update it accordingly ++ map.setIncludeArgumentOops(false); ++ ++ if (isEntryFrame()) return senderForEntryFrame(map); ++ if (isInterpretedFrame()) return senderForInterpreterFrame(map); ++ ++ if(cb == null) { ++ cb = VM.getVM().getCodeCache().findBlob(getPC()); ++ } else { ++ if (Assert.ASSERTS_ENABLED) { ++ Assert.that(cb.equals(VM.getVM().getCodeCache().findBlob(getPC())), "Must be the same"); ++ } ++ } ++ ++ if (cb != null) { ++ return senderForCompiledFrame(map, cb); ++ } ++ ++ // Must be native-compiled frame, i.e. the marshaling code for native ++ // methods that exists in the core system. ++ return new LOONGARCH64Frame(getSenderSP(), getLink(), getSenderPC()); ++ } ++ ++ private Frame senderForEntryFrame(LOONGARCH64RegisterMap map) { ++ if (DEBUG) { ++ System.out.println("senderForEntryFrame"); ++ } ++ if (Assert.ASSERTS_ENABLED) { ++ Assert.that(map != null, "map must be set"); ++ } ++ // Java frame called from C; skip all C frames and return top C ++ // frame of that chunk as the sender ++ LOONGARCH64JavaCallWrapper jcw = (LOONGARCH64JavaCallWrapper) getEntryFrameCallWrapper(); ++ if (Assert.ASSERTS_ENABLED) { ++ Assert.that(!entryFrameIsFirst(), "next Java fp must be non zero"); ++ Assert.that(jcw.getLastJavaSP().greaterThan(getSP()), "must be above this frame on stack"); ++ } ++ LOONGARCH64Frame fr; ++ if (jcw.getLastJavaPC() != null) { ++ fr = new LOONGARCH64Frame(jcw.getLastJavaSP(), jcw.getLastJavaFP(), jcw.getLastJavaPC()); ++ } else { ++ fr = new LOONGARCH64Frame(jcw.getLastJavaSP(), jcw.getLastJavaFP()); ++ } ++ map.clear(); ++ if (Assert.ASSERTS_ENABLED) { ++ Assert.that(map.getIncludeArgumentOops(), "should be set by clear"); ++ } ++ return fr; ++ } ++ ++ //------------------------------------------------------------------------------ ++ // frame::adjust_unextended_sp ++ private void adjustUnextendedSP() { ++ // On loongarch, sites calling method handle intrinsics and lambda forms are treated ++ // as any other call site. Therefore, no special action is needed when we are ++ // returning to any of these call sites. ++ ++ CodeBlob cb = cb(); ++ NMethod senderNm = (cb == null) ? null : cb.asNMethodOrNull(); ++ if (senderNm != null) { ++ // If the sender PC is a deoptimization point, get the original PC. ++ if (senderNm.isDeoptEntry(getPC()) || ++ senderNm.isDeoptMhEntry(getPC())) { ++ // DEBUG_ONLY(verifyDeoptriginalPc(senderNm, raw_unextendedSp)); ++ } ++ } ++ } ++ ++ private Frame senderForInterpreterFrame(LOONGARCH64RegisterMap map) { ++ if (DEBUG) { ++ System.out.println("senderForInterpreterFrame"); ++ } ++ Address unextendedSP = addressOfStackSlot(INTERPRETER_FRAME_SENDER_SP_OFFSET).getAddressAt(0); ++ Address sp = getSenderSP(); ++ // We do not need to update the callee-save register mapping because above ++ // us is either another interpreter frame or a converter-frame, but never ++ // directly a compiled frame. ++ // 11/24/04 SFG. With the removal of adapter frames this is no longer true. ++ // However c2 no longer uses callee save register for java calls so there ++ // are no callee register to find. ++ ++ if (map.getUpdateMap()) ++ updateMapWithSavedLink(map, addressOfStackSlot(JAVA_FRAME_LINK_OFFSET)); ++ ++ return new LOONGARCH64Frame(sp, unextendedSP, getLink(), getSenderPC()); ++ } ++ ++ private void updateMapWithSavedLink(RegisterMap map, Address savedFPAddr) { ++ map.setLocation(fp, savedFPAddr); ++ } ++ ++ private Frame senderForCompiledFrame(LOONGARCH64RegisterMap map, CodeBlob cb) { ++ if (DEBUG) { ++ System.out.println("senderForCompiledFrame"); ++ } ++ ++ // ++ // NOTE: some of this code is (unfortunately) duplicated in LOONGARCH64CurrentFrameGuess ++ // ++ ++ if (Assert.ASSERTS_ENABLED) { ++ Assert.that(map != null, "map must be set"); ++ } ++ ++ // frame owned by optimizing compiler ++ if (Assert.ASSERTS_ENABLED) { ++ Assert.that(cb.getFrameSize() >= 0, "must have non-zero frame size"); ++ } ++ Address senderSP = getUnextendedSP().addOffsetTo(cb.getFrameSize()); ++ ++ // On Intel the return_address is always the word on the stack ++ Address senderPC = senderSP.getAddressAt(-1 * VM.getVM().getAddressSize()); ++ ++ // This is the saved value of EBP which may or may not really be an FP. ++ // It is only an FP if the sender is an interpreter frame (or C1?). ++ Address savedFPAddr = senderSP.addOffsetTo(- JAVA_FRAME_SENDER_SP_OFFSET * VM.getVM().getAddressSize()); ++ ++ if (map.getUpdateMap()) { ++ // Tell GC to use argument oopmaps for some runtime stubs that need it. ++ // For C1, the runtime stub might not have oop maps, so set this flag ++ // outside of update_register_map. ++ map.setIncludeArgumentOops(cb.callerMustGCArguments()); ++ ++ if (cb.getOopMaps() != null) { ++ OopMapSet.updateRegisterMap(this, cb, map, true); ++ } ++ ++ // Since the prolog does the save and restore of EBP there is no oopmap ++ // for it so we must fill in its location as if there was an oopmap entry ++ // since if our caller was compiled code there could be live jvm state in it. ++ updateMapWithSavedLink(map, savedFPAddr); ++ } ++ ++ return new LOONGARCH64Frame(senderSP, savedFPAddr.getAddressAt(0), senderPC); ++ } ++ ++ protected boolean hasSenderPD() { ++ // FIXME ++ // Check for null ebp? Need to do some tests. ++ return true; ++ } ++ ++ public long frameSize() { ++ return (getSenderSP().minus(getSP()) / VM.getVM().getAddressSize()); ++ } ++ ++ public Address getLink() { ++ if (isJavaFrame()) ++ return addressOfStackSlot(JAVA_FRAME_LINK_OFFSET).getAddressAt(0); ++ return addressOfStackSlot(NATIVE_FRAME_LINK_OFFSET).getAddressAt(0); ++ } ++ ++ public Address getUnextendedSP() { return raw_unextendedSP; } ++ ++ // Return address: ++ public Address getSenderPCAddr() { ++ if (isJavaFrame()) ++ return addressOfStackSlot(JAVA_FRAME_RETURN_ADDR_OFFSET); ++ return addressOfStackSlot(NATIVE_FRAME_RETURN_ADDR_OFFSET); ++ } ++ ++ public Address getSenderPC() { return getSenderPCAddr().getAddressAt(0); } ++ ++ public Address getSenderSP() { ++ if (isJavaFrame()) ++ return addressOfStackSlot(JAVA_FRAME_SENDER_SP_OFFSET); ++ return addressOfStackSlot(NATIVE_FRAME_SENDER_SP_OFFSET); ++ } ++ ++ // return address of param, zero origin index. ++ public Address getNativeParamAddr(int idx) { ++ return addressOfStackSlot(NATIVE_FRAME_INITIAL_PARAM_OFFSET + idx); ++ } ++ ++ public Address addressOfInterpreterFrameLocals() { ++ return addressOfStackSlot(INTERPRETER_FRAME_LOCALS_OFFSET); ++ } ++ ++ private Address addressOfInterpreterFrameBCX() { ++ return addressOfStackSlot(INTERPRETER_FRAME_BCX_OFFSET); ++ } ++ ++ public int getInterpreterFrameBCI() { ++ // FIXME: this is not atomic with respect to GC and is unsuitable ++ // for use in a non-debugging, or reflective, system. Need to ++ // figure out how to express this. ++ Address bcp = addressOfInterpreterFrameBCX().getAddressAt(0); ++ Address methodHandle = addressOfInterpreterFrameMethod().getAddressAt(0); ++ Method method = (Method)Metadata.instantiateWrapperFor(methodHandle); ++ return bcpToBci(bcp, method); ++ } ++ ++ public Address addressOfInterpreterFrameMDX() { ++ return addressOfStackSlot(INTERPRETER_FRAME_MDX_OFFSET); ++ } ++ ++ // FIXME ++ //inline int frame::interpreter_frame_monitor_size() { ++ // return BasicObjectLock::size(); ++ //} ++ ++ // expression stack ++ // (the max_stack arguments are used by the GC; see class FrameClosure) ++ ++ public Address addressOfInterpreterFrameExpressionStack() { ++ Address monitorEnd = interpreterFrameMonitorEnd().address(); ++ return monitorEnd.addOffsetTo(-1 * VM.getVM().getAddressSize()); ++ } ++ ++ public int getInterpreterFrameExpressionStackDirection() { return -1; } ++ ++ // top of expression stack ++ public Address addressOfInterpreterFrameTOS() { ++ return getSP(); ++ } ++ ++ /** Expression stack from top down */ ++ public Address addressOfInterpreterFrameTOSAt(int slot) { ++ return addressOfInterpreterFrameTOS().addOffsetTo(slot * VM.getVM().getAddressSize()); ++ } ++ ++ public Address getInterpreterFrameSenderSP() { ++ if (Assert.ASSERTS_ENABLED) { ++ Assert.that(isInterpretedFrame(), "interpreted frame expected"); ++ } ++ return addressOfStackSlot(INTERPRETER_FRAME_SENDER_SP_OFFSET).getAddressAt(0); ++ } ++ ++ // Monitors ++ public BasicObjectLock interpreterFrameMonitorBegin() { ++ return new BasicObjectLock(addressOfStackSlot(INTERPRETER_FRAME_MONITOR_BLOCK_BOTTOM_OFFSET)); ++ } ++ ++ public BasicObjectLock interpreterFrameMonitorEnd() { ++ Address result = addressOfStackSlot(INTERPRETER_FRAME_MONITOR_BLOCK_TOP_OFFSET).getAddressAt(0); ++ if (Assert.ASSERTS_ENABLED) { ++ // make sure the pointer points inside the frame ++ Assert.that(AddressOps.gt(getFP(), result), "result must < than frame pointer"); ++ Assert.that(AddressOps.lte(getSP(), result), "result must >= than stack pointer"); ++ } ++ return new BasicObjectLock(result); ++ } ++ ++ public int interpreterFrameMonitorSize() { ++ return BasicObjectLock.size(); ++ } ++ ++ // Method ++ public Address addressOfInterpreterFrameMethod() { ++ return addressOfStackSlot(INTERPRETER_FRAME_METHOD_OFFSET); ++ } ++ ++ // Constant pool cache ++ public Address addressOfInterpreterFrameCPCache() { ++ return addressOfStackSlot(INTERPRETER_FRAME_CACHE_OFFSET); ++ } ++ ++ // Entry frames ++ public JavaCallWrapper getEntryFrameCallWrapper() { ++ return new LOONGARCH64JavaCallWrapper(addressOfStackSlot(ENTRY_FRAME_CALL_WRAPPER_OFFSET).getAddressAt(0)); ++ } ++ ++ protected Address addressOfSavedOopResult() { ++ // offset is 2 for compiler2 and 3 for compiler1 ++ return getSP().addOffsetTo((VM.getVM().isClientCompiler() ? 2 : 3) * ++ VM.getVM().getAddressSize()); ++ } ++ ++ protected Address addressOfSavedReceiver() { ++ return getSP().addOffsetTo(-4 * VM.getVM().getAddressSize()); ++ } ++ ++ private void dumpStack() { ++ if (getFP() != null) { ++ for (Address addr = getSP().addOffsetTo(-5 * VM.getVM().getAddressSize()); ++ AddressOps.lte(addr, getFP().addOffsetTo(5 * VM.getVM().getAddressSize())); ++ addr = addr.addOffsetTo(VM.getVM().getAddressSize())) { ++ System.out.println(addr + ": " + addr.getAddressAt(0)); ++ } ++ } else { ++ for (Address addr = getSP().addOffsetTo(-5 * VM.getVM().getAddressSize()); ++ AddressOps.lte(addr, getSP().addOffsetTo(20 * VM.getVM().getAddressSize())); ++ addr = addr.addOffsetTo(VM.getVM().getAddressSize())) { ++ System.out.println(addr + ": " + addr.getAddressAt(0)); ++ } ++ } ++ } ++} +diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/loongarch64/LOONGARCH64JavaCallWrapper.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/loongarch64/LOONGARCH64JavaCallWrapper.java +new file mode 100644 +index 0000000000..f7dbbcaacd +--- /dev/null ++++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/loongarch64/LOONGARCH64JavaCallWrapper.java +@@ -0,0 +1,57 @@ ++/* ++ * Copyright (c) 2001, 2002, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2015, 2020, Loongson Technology. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ * ++ */ ++ ++package sun.jvm.hotspot.runtime.loongarch64; ++ ++import java.util.*; ++import sun.jvm.hotspot.debugger.*; ++import sun.jvm.hotspot.types.*; ++import sun.jvm.hotspot.runtime.*; ++ ++public class LOONGARCH64JavaCallWrapper extends JavaCallWrapper { ++ private static AddressField lastJavaFPField; ++ ++ static { ++ VM.registerVMInitializedObserver(new Observer() { ++ public void update(Observable o, Object data) { ++ initialize(VM.getVM().getTypeDataBase()); ++ } ++ }); ++ } ++ ++ private static synchronized void initialize(TypeDataBase db) { ++ Type type = db.lookupType("JavaFrameAnchor"); ++ ++ lastJavaFPField = type.getAddressField("_last_Java_fp"); ++ } ++ ++ public LOONGARCH64JavaCallWrapper(Address addr) { ++ super(addr); ++ } ++ ++ public Address getLastJavaFP() { ++ return lastJavaFPField.getValue(addr.addOffsetTo(anchorField.getOffset())); ++ } ++} +diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/loongarch64/LOONGARCH64RegisterMap.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/loongarch64/LOONGARCH64RegisterMap.java +new file mode 100644 +index 0000000000..021ef523e3 +--- /dev/null ++++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/loongarch64/LOONGARCH64RegisterMap.java +@@ -0,0 +1,52 @@ ++/* ++ * Copyright (c) 2001, 2012, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2015, 2020, Loongson Technology. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ * ++ */ ++ ++package sun.jvm.hotspot.runtime.loongarch64; ++ ++import sun.jvm.hotspot.debugger.*; ++import sun.jvm.hotspot.runtime.*; ++ ++public class LOONGARCH64RegisterMap extends RegisterMap { ++ ++ /** This is the only public constructor */ ++ public LOONGARCH64RegisterMap(JavaThread thread, boolean updateMap) { ++ super(thread, updateMap); ++ } ++ ++ protected LOONGARCH64RegisterMap(RegisterMap map) { ++ super(map); ++ } ++ ++ public Object clone() { ++ LOONGARCH64RegisterMap retval = new LOONGARCH64RegisterMap(this); ++ return retval; ++ } ++ ++ // no PD state to clear or copy: ++ protected void clearPD() {} ++ protected void initializePD() {} ++ protected void initializeFromPD(RegisterMap map) {} ++ protected Address getLocationPD(VMReg reg) { return null; } ++} +diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/mips64/MIPS64CurrentFrameGuess.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/mips64/MIPS64CurrentFrameGuess.java +new file mode 100644 +index 0000000000..21259a4d32 +--- /dev/null ++++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/mips64/MIPS64CurrentFrameGuess.java +@@ -0,0 +1,217 @@ ++/* ++ * Copyright (c) 2001, 2006, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2015, 2018, Loongson Technology. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ * ++ */ ++ ++package sun.jvm.hotspot.runtime.mips64; ++ ++import sun.jvm.hotspot.debugger.*; ++import sun.jvm.hotspot.debugger.mips64.*; ++import sun.jvm.hotspot.code.*; ++import sun.jvm.hotspot.interpreter.*; ++import sun.jvm.hotspot.runtime.*; ++ ++/**

Should be able to be used on all mips64 platforms we support ++ (Win32, Solaris/mips64, and soon Linux) to implement JavaThread's ++ "currentFrameGuess()" functionality. Input is an MIPS64ThreadContext; ++ output is SP, FP, and PC for an MIPS64Frame. Instantiation of the ++ MIPS64Frame is left to the caller, since we may need to subclass ++ MIPS64Frame to support signal handler frames on Unix platforms.

++ ++

Algorithm is to walk up the stack within a given range (say, ++ 512K at most) looking for a plausible PC and SP for a Java frame, ++ also considering those coming in from the context. If we find a PC ++ that belongs to the VM (i.e., in generated code like the ++ interpreter or CodeCache) then we try to find an associated EBP. ++ We repeat this until we either find a complete frame or run out of ++ stack to look at.

*/ ++ ++public class MIPS64CurrentFrameGuess { ++ private MIPS64ThreadContext context; ++ private JavaThread thread; ++ private Address spFound; ++ private Address fpFound; ++ private Address pcFound; ++ ++ private static final boolean DEBUG = System.getProperty("sun.jvm.hotspot.runtime.mips64.MIPS64Frame.DEBUG") ++ != null; ++ ++ public MIPS64CurrentFrameGuess(MIPS64ThreadContext context, ++ JavaThread thread) { ++ this.context = context; ++ this.thread = thread; ++ } ++ ++ /** Returns false if not able to find a frame within a reasonable range. */ ++ public boolean run(long regionInBytesToSearch) { ++ Address sp = context.getRegisterAsAddress(MIPS64ThreadContext.SP); ++ Address pc = context.getRegisterAsAddress(MIPS64ThreadContext.PC); ++ Address fp = context.getRegisterAsAddress(MIPS64ThreadContext.FP); ++ if (sp == null) { ++ // Bail out if no last java frame eithe ++ if (thread.getLastJavaSP() != null) { ++ setValues(thread.getLastJavaSP(), thread.getLastJavaFP(), null); ++ return true; ++ } ++ // Bail out ++ return false; ++ } ++ Address end = sp.addOffsetTo(regionInBytesToSearch); ++ VM vm = VM.getVM(); ++ ++ setValues(null, null, null); // Assume we're not going to find anything ++ ++ if (vm.isJavaPCDbg(pc)) { ++ if (vm.isClientCompiler()) { ++ // If the topmost frame is a Java frame, we are (pretty much) ++ // guaranteed to have a viable EBP. We should be more robust ++ // than this (we have the potential for losing entire threads' ++ // stack traces) but need to see how much work we really have ++ // to do here. Searching the stack for an (SP, FP) pair is ++ // hard since it's easy to misinterpret inter-frame stack ++ // pointers as base-of-frame pointers; we also don't know the ++ // sizes of C1 frames (not registered in the nmethod) so can't ++ // derive them from ESP. ++ ++ setValues(sp, fp, pc); ++ return true; ++ } else { ++ if (vm.getInterpreter().contains(pc)) { ++ if (DEBUG) { ++ System.out.println("CurrentFrameGuess: choosing interpreter frame: sp = " + ++ sp + ", fp = " + fp + ", pc = " + pc); ++ } ++ setValues(sp, fp, pc); ++ return true; ++ } ++ ++ // For the server compiler, EBP is not guaranteed to be valid ++ // for compiled code. In addition, an earlier attempt at a ++ // non-searching algorithm (see below) failed because the ++ // stack pointer from the thread context was pointing ++ // (considerably) beyond the ostensible end of the stack, into ++ // garbage; walking from the topmost frame back caused a crash. ++ // ++ // This algorithm takes the current PC as a given and tries to ++ // find the correct corresponding SP by walking up the stack ++ // and repeatedly performing stackwalks (very inefficient). ++ // ++ // FIXME: there is something wrong with stackwalking across ++ // adapter frames...this is likely to be the root cause of the ++ // failure with the simpler algorithm below. ++ ++ for (long offset = 0; ++ offset < regionInBytesToSearch; ++ offset += vm.getAddressSize()) { ++ try { ++ Address curSP = sp.addOffsetTo(offset); ++ Frame frame = new MIPS64Frame(curSP, null, pc); ++ RegisterMap map = thread.newRegisterMap(false); ++ while (frame != null) { ++ if (frame.isEntryFrame() && frame.entryFrameIsFirst()) { ++ // We were able to traverse all the way to the ++ // bottommost Java frame. ++ // This sp looks good. Keep it. ++ if (DEBUG) { ++ System.out.println("CurrentFrameGuess: Choosing sp = " + curSP + ", pc = " + pc); ++ } ++ setValues(curSP, null, pc); ++ return true; ++ } ++ frame = frame.sender(map); ++ } ++ } catch (Exception e) { ++ if (DEBUG) { ++ System.out.println("CurrentFrameGuess: Exception " + e + " at offset " + offset); ++ } ++ // Bad SP. Try another. ++ } ++ } ++ ++ // Were not able to find a plausible SP to go with this PC. ++ // Bail out. ++ return false; ++ ++ /* ++ // Original algorithm which does not work because SP was ++ // pointing beyond where it should have: ++ ++ // For the server compiler, EBP is not guaranteed to be valid ++ // for compiled code. We see whether the PC is in the ++ // interpreter and take care of that, otherwise we run code ++ // (unfortunately) duplicated from MIPS64Frame.senderForCompiledFrame. ++ ++ CodeCache cc = vm.getCodeCache(); ++ if (cc.contains(pc)) { ++ CodeBlob cb = cc.findBlob(pc); ++ ++ // See if we can derive a frame pointer from SP and PC ++ // NOTE: This is the code duplicated from MIPS64Frame ++ Address saved_fp = null; ++ int llink_offset = cb.getLinkOffset(); ++ if (llink_offset >= 0) { ++ // Restore base-pointer, since next frame might be an interpreter frame. ++ Address fp_addr = sp.addOffsetTo(VM.getVM().getAddressSize() * llink_offset); ++ saved_fp = fp_addr.getAddressAt(0); ++ } ++ ++ setValues(sp, saved_fp, pc); ++ return true; ++ } ++ */ ++ } ++ } else { ++ // If the current program counter was not known to us as a Java ++ // PC, we currently assume that we are in the run-time system ++ // and attempt to look to thread-local storage for saved ESP and ++ // EBP. Note that if these are null (because we were, in fact, ++ // in Java code, i.e., vtable stubs or similar, and the SA ++ // didn't have enough insight into the target VM to understand ++ // that) then we are going to lose the entire stack trace for ++ // the thread, which is sub-optimal. FIXME. ++ ++ if (DEBUG) { ++ System.out.println("CurrentFrameGuess: choosing last Java frame: sp = " + ++ thread.getLastJavaSP() + ", fp = " + thread.getLastJavaFP()); ++ } ++ if (thread.getLastJavaSP() == null) { ++ return false; // No known Java frames on stack ++ } ++ setValues(thread.getLastJavaSP(), thread.getLastJavaFP(), null); ++ return true; ++ } ++ } ++ ++ public Address getSP() { return spFound; } ++ public Address getFP() { return fpFound; } ++ /** May be null if getting values from thread-local storage; take ++ care to call the correct MIPS64Frame constructor to recover this if ++ necessary */ ++ public Address getPC() { return pcFound; } ++ ++ private void setValues(Address sp, Address fp, Address pc) { ++ spFound = sp; ++ fpFound = fp; ++ pcFound = pc; ++ } ++} +diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/mips64/MIPS64Frame.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/mips64/MIPS64Frame.java +new file mode 100644 +index 0000000000..0cc5cf4e7c +--- /dev/null ++++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/mips64/MIPS64Frame.java +@@ -0,0 +1,547 @@ ++/* ++ * Copyright (c) 2001, 2012, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2015, 2018, Loongson Technology. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ * ++ */ ++ ++package sun.jvm.hotspot.runtime.mips64; ++ ++import java.util.*; ++import sun.jvm.hotspot.code.*; ++import sun.jvm.hotspot.compiler.*; ++import sun.jvm.hotspot.debugger.*; ++import sun.jvm.hotspot.oops.*; ++import sun.jvm.hotspot.runtime.*; ++import sun.jvm.hotspot.types.*; ++import sun.jvm.hotspot.utilities.*; ++ ++/** Specialization of and implementation of abstract methods of the ++ Frame class for the mips64 family of CPUs. */ ++ ++public class MIPS64Frame extends Frame { ++ private static final boolean DEBUG; ++ static { ++ DEBUG = System.getProperty("sun.jvm.hotspot.runtime.mips64.MIPS64Frame.DEBUG") != null; ++ } ++ ++ // All frames ++ private static final int LINK_OFFSET = 0; ++ private static final int RETURN_ADDR_OFFSET = 1; ++ private static final int SENDER_SP_OFFSET = 2; ++ ++ // Interpreter frames ++ private static final int INTERPRETER_FRAME_MIRROR_OFFSET = 2; // for native calls only ++ private static final int INTERPRETER_FRAME_SENDER_SP_OFFSET = -1; ++ private static final int INTERPRETER_FRAME_LAST_SP_OFFSET = INTERPRETER_FRAME_SENDER_SP_OFFSET - 1; ++ private static final int INTERPRETER_FRAME_METHOD_OFFSET = INTERPRETER_FRAME_LAST_SP_OFFSET - 1; ++ private static int INTERPRETER_FRAME_MDX_OFFSET; // Non-core builds only ++ private static int INTERPRETER_FRAME_CACHE_OFFSET; ++ private static int INTERPRETER_FRAME_LOCALS_OFFSET; ++ private static int INTERPRETER_FRAME_BCX_OFFSET; ++ private static int INTERPRETER_FRAME_INITIAL_SP_OFFSET; ++ private static int INTERPRETER_FRAME_MONITOR_BLOCK_TOP_OFFSET; ++ private static int INTERPRETER_FRAME_MONITOR_BLOCK_BOTTOM_OFFSET; ++ ++ // Entry frames ++ private static int ENTRY_FRAME_CALL_WRAPPER_OFFSET; ++ ++ // Native frames ++ private static final int NATIVE_FRAME_INITIAL_PARAM_OFFSET = 2; ++ ++ private static VMReg rbp; ++ ++ static { ++ VM.registerVMInitializedObserver(new Observer() { ++ public void update(Observable o, Object data) { ++ initialize(VM.getVM().getTypeDataBase()); ++ } ++ }); ++ } ++ ++ private static synchronized void initialize(TypeDataBase db) { ++ INTERPRETER_FRAME_MDX_OFFSET = INTERPRETER_FRAME_METHOD_OFFSET - 1; ++ INTERPRETER_FRAME_CACHE_OFFSET = INTERPRETER_FRAME_MDX_OFFSET - 1; ++ INTERPRETER_FRAME_LOCALS_OFFSET = INTERPRETER_FRAME_CACHE_OFFSET - 1; ++ INTERPRETER_FRAME_BCX_OFFSET = INTERPRETER_FRAME_LOCALS_OFFSET - 1; ++ INTERPRETER_FRAME_INITIAL_SP_OFFSET = INTERPRETER_FRAME_BCX_OFFSET - 1; ++ INTERPRETER_FRAME_MONITOR_BLOCK_TOP_OFFSET = INTERPRETER_FRAME_INITIAL_SP_OFFSET; ++ INTERPRETER_FRAME_MONITOR_BLOCK_BOTTOM_OFFSET = INTERPRETER_FRAME_INITIAL_SP_OFFSET; ++ ++ ENTRY_FRAME_CALL_WRAPPER_OFFSET = db.lookupIntConstant("frame::entry_frame_call_wrapper_offset"); ++ if (VM.getVM().getAddressSize() == 4) { ++ rbp = new VMReg(5); ++ } else { ++ rbp = new VMReg(5 << 1); ++ } ++ } ++ ++ ++ // an additional field beyond sp and pc: ++ Address raw_fp; // frame pointer ++ private Address raw_unextendedSP; ++ ++ private MIPS64Frame() { ++ } ++ ++ private void adjustForDeopt() { ++ if ( pc != null) { ++ // Look for a deopt pc and if it is deopted convert to original pc ++ CodeBlob cb = VM.getVM().getCodeCache().findBlob(pc); ++ if (cb != null && cb.isJavaMethod()) { ++ NMethod nm = (NMethod) cb; ++ if (pc.equals(nm.deoptHandlerBegin())) { ++ if (Assert.ASSERTS_ENABLED) { ++ Assert.that(this.getUnextendedSP() != null, "null SP in Java frame"); ++ } ++ // adjust pc if frame is deoptimized. ++ pc = this.getUnextendedSP().getAddressAt(nm.origPCOffset()); ++ deoptimized = true; ++ } ++ } ++ } ++ } ++ ++ public MIPS64Frame(Address raw_sp, Address raw_fp, Address pc) { ++ this.raw_sp = raw_sp; ++ this.raw_unextendedSP = raw_sp; ++ this.raw_fp = raw_fp; ++ this.pc = pc; ++ adjustUnextendedSP(); ++ ++ // Frame must be fully constructed before this call ++ adjustForDeopt(); ++ ++ if (DEBUG) { ++ System.out.println("MIPS64Frame(sp, fp, pc): " + this); ++ dumpStack(); ++ } ++ } ++ ++ public MIPS64Frame(Address raw_sp, Address raw_fp) { ++ this.raw_sp = raw_sp; ++ this.raw_unextendedSP = raw_sp; ++ this.raw_fp = raw_fp; ++ this.pc = raw_sp.getAddressAt(-1 * VM.getVM().getAddressSize()); ++ adjustUnextendedSP(); ++ ++ // Frame must be fully constructed before this call ++ adjustForDeopt(); ++ ++ if (DEBUG) { ++ System.out.println("MIPS64Frame(sp, fp): " + this); ++ dumpStack(); ++ } ++ } ++ ++ public MIPS64Frame(Address raw_sp, Address raw_unextendedSp, Address raw_fp, Address pc) { ++ this.raw_sp = raw_sp; ++ this.raw_unextendedSP = raw_unextendedSp; ++ this.raw_fp = raw_fp; ++ this.pc = pc; ++ adjustUnextendedSP(); ++ ++ // Frame must be fully constructed before this call ++ adjustForDeopt(); ++ ++ if (DEBUG) { ++ System.out.println("MIPS64Frame(sp, unextendedSP, fp, pc): " + this); ++ dumpStack(); ++ } ++ ++ } ++ ++ public Object clone() { ++ MIPS64Frame frame = new MIPS64Frame(); ++ frame.raw_sp = raw_sp; ++ frame.raw_unextendedSP = raw_unextendedSP; ++ frame.raw_fp = raw_fp; ++ frame.pc = pc; ++ frame.deoptimized = deoptimized; ++ return frame; ++ } ++ ++ public boolean equals(Object arg) { ++ if (arg == null) { ++ return false; ++ } ++ ++ if (!(arg instanceof MIPS64Frame)) { ++ return false; ++ } ++ ++ MIPS64Frame other = (MIPS64Frame) arg; ++ ++ return (AddressOps.equal(getSP(), other.getSP()) && ++ AddressOps.equal(getUnextendedSP(), other.getUnextendedSP()) && ++ AddressOps.equal(getFP(), other.getFP()) && ++ AddressOps.equal(getPC(), other.getPC())); ++ } ++ ++ public int hashCode() { ++ if (raw_sp == null) { ++ return 0; ++ } ++ ++ return raw_sp.hashCode(); ++ } ++ ++ public String toString() { ++ return "sp: " + (getSP() == null? "null" : getSP().toString()) + ++ ", unextendedSP: " + (getUnextendedSP() == null? "null" : getUnextendedSP().toString()) + ++ ", fp: " + (getFP() == null? "null" : getFP().toString()) + ++ ", pc: " + (pc == null? "null" : pc.toString()); ++ } ++ ++ // accessors for the instance variables ++ public Address getFP() { return raw_fp; } ++ public Address getSP() { return raw_sp; } ++ public Address getID() { return raw_sp; } ++ ++ // FIXME: not implemented yet (should be done for Solaris/MIPS64) ++ public boolean isSignalHandlerFrameDbg() { return false; } ++ public int getSignalNumberDbg() { return 0; } ++ public String getSignalNameDbg() { return null; } ++ ++ public boolean isInterpretedFrameValid() { ++ if (Assert.ASSERTS_ENABLED) { ++ Assert.that(isInterpretedFrame(), "Not an interpreted frame"); ++ } ++ ++ // These are reasonable sanity checks ++ if (getFP() == null || getFP().andWithMask(0x3) != null) { ++ return false; ++ } ++ ++ if (getSP() == null || getSP().andWithMask(0x3) != null) { ++ return false; ++ } ++ ++ if (getFP().addOffsetTo(INTERPRETER_FRAME_INITIAL_SP_OFFSET * VM.getVM().getAddressSize()).lessThan(getSP())) { ++ return false; ++ } ++ ++ // These are hacks to keep us out of trouble. ++ // The problem with these is that they mask other problems ++ if (getFP().lessThanOrEqual(getSP())) { ++ // this attempts to deal with unsigned comparison above ++ return false; ++ } ++ ++ if (getFP().minus(getSP()) > 4096 * VM.getVM().getAddressSize()) { ++ // stack frames shouldn't be large. ++ return false; ++ } ++ ++ return true; ++ } ++ ++ // FIXME: not applicable in current system ++ // void patch_pc(Thread* thread, address pc); ++ ++ public Frame sender(RegisterMap regMap, CodeBlob cb) { ++ MIPS64RegisterMap map = (MIPS64RegisterMap) regMap; ++ ++ if (Assert.ASSERTS_ENABLED) { ++ Assert.that(map != null, "map must be set"); ++ } ++ ++ // Default is we done have to follow them. The sender_for_xxx will ++ // update it accordingly ++ map.setIncludeArgumentOops(false); ++ ++ if (isEntryFrame()) return senderForEntryFrame(map); ++ if (isInterpretedFrame()) return senderForInterpreterFrame(map); ++ ++ if(cb == null) { ++ cb = VM.getVM().getCodeCache().findBlob(getPC()); ++ } else { ++ if (Assert.ASSERTS_ENABLED) { ++ Assert.that(cb.equals(VM.getVM().getCodeCache().findBlob(getPC())), "Must be the same"); ++ } ++ } ++ ++ if (cb != null) { ++ return senderForCompiledFrame(map, cb); ++ } ++ ++ // Must be native-compiled frame, i.e. the marshaling code for native ++ // methods that exists in the core system. ++ return new MIPS64Frame(getSenderSP(), getLink(), getSenderPC()); ++ } ++ ++ private Frame senderForEntryFrame(MIPS64RegisterMap map) { ++ if (DEBUG) { ++ System.out.println("senderForEntryFrame"); ++ } ++ if (Assert.ASSERTS_ENABLED) { ++ Assert.that(map != null, "map must be set"); ++ } ++ // Java frame called from C; skip all C frames and return top C ++ // frame of that chunk as the sender ++ MIPS64JavaCallWrapper jcw = (MIPS64JavaCallWrapper) getEntryFrameCallWrapper(); ++ if (Assert.ASSERTS_ENABLED) { ++ Assert.that(!entryFrameIsFirst(), "next Java fp must be non zero"); ++ Assert.that(jcw.getLastJavaSP().greaterThan(getSP()), "must be above this frame on stack"); ++ } ++ MIPS64Frame fr; ++ if (jcw.getLastJavaPC() != null) { ++ fr = new MIPS64Frame(jcw.getLastJavaSP(), jcw.getLastJavaFP(), jcw.getLastJavaPC()); ++ } else { ++ fr = new MIPS64Frame(jcw.getLastJavaSP(), jcw.getLastJavaFP()); ++ } ++ map.clear(); ++ if (Assert.ASSERTS_ENABLED) { ++ Assert.that(map.getIncludeArgumentOops(), "should be set by clear"); ++ } ++ return fr; ++ } ++ ++ //------------------------------------------------------------------------------ ++ // frame::adjust_unextended_sp ++ private void adjustUnextendedSP() { ++ // On mips64, sites calling method handle intrinsics and lambda forms are treated ++ // as any other call site. Therefore, no special action is needed when we are ++ // returning to any of these call sites. ++ ++ CodeBlob cb = cb(); ++ NMethod senderNm = (cb == null) ? null : cb.asNMethodOrNull(); ++ if (senderNm != null) { ++ // If the sender PC is a deoptimization point, get the original PC. ++ if (senderNm.isDeoptEntry(getPC()) || ++ senderNm.isDeoptMhEntry(getPC())) { ++ // DEBUG_ONLY(verifyDeoptriginalPc(senderNm, raw_unextendedSp)); ++ } ++ } ++ } ++ ++ private Frame senderForInterpreterFrame(MIPS64RegisterMap map) { ++ if (DEBUG) { ++ System.out.println("senderForInterpreterFrame"); ++ } ++ Address unextendedSP = addressOfStackSlot(INTERPRETER_FRAME_SENDER_SP_OFFSET).getAddressAt(0); ++ Address sp = addressOfStackSlot(SENDER_SP_OFFSET); ++ // We do not need to update the callee-save register mapping because above ++ // us is either another interpreter frame or a converter-frame, but never ++ // directly a compiled frame. ++ // 11/24/04 SFG. With the removal of adapter frames this is no longer true. ++ // However c2 no longer uses callee save register for java calls so there ++ // are no callee register to find. ++ ++ if (map.getUpdateMap()) ++ updateMapWithSavedLink(map, addressOfStackSlot(LINK_OFFSET)); ++ ++ return new MIPS64Frame(sp, unextendedSP, getLink(), getSenderPC()); ++ } ++ ++ private void updateMapWithSavedLink(RegisterMap map, Address savedFPAddr) { ++ map.setLocation(rbp, savedFPAddr); ++ } ++ ++ private Frame senderForCompiledFrame(MIPS64RegisterMap map, CodeBlob cb) { ++ if (DEBUG) { ++ System.out.println("senderForCompiledFrame"); ++ } ++ ++ // ++ // NOTE: some of this code is (unfortunately) duplicated in MIPS64CurrentFrameGuess ++ // ++ ++ if (Assert.ASSERTS_ENABLED) { ++ Assert.that(map != null, "map must be set"); ++ } ++ ++ // frame owned by optimizing compiler ++ if (Assert.ASSERTS_ENABLED) { ++ Assert.that(cb.getFrameSize() >= 0, "must have non-zero frame size"); ++ } ++ Address senderSP = getUnextendedSP().addOffsetTo(cb.getFrameSize()); ++ ++ // On Intel the return_address is always the word on the stack ++ Address senderPC = senderSP.getAddressAt(-1 * VM.getVM().getAddressSize()); ++ ++ // This is the saved value of EBP which may or may not really be an FP. ++ // It is only an FP if the sender is an interpreter frame (or C1?). ++ Address savedFPAddr = senderSP.addOffsetTo(- SENDER_SP_OFFSET * VM.getVM().getAddressSize()); ++ ++ if (map.getUpdateMap()) { ++ // Tell GC to use argument oopmaps for some runtime stubs that need it. ++ // For C1, the runtime stub might not have oop maps, so set this flag ++ // outside of update_register_map. ++ map.setIncludeArgumentOops(cb.callerMustGCArguments()); ++ ++ if (cb.getOopMaps() != null) { ++ OopMapSet.updateRegisterMap(this, cb, map, true); ++ } ++ ++ // Since the prolog does the save and restore of EBP there is no oopmap ++ // for it so we must fill in its location as if there was an oopmap entry ++ // since if our caller was compiled code there could be live jvm state in it. ++ updateMapWithSavedLink(map, savedFPAddr); ++ } ++ ++ return new MIPS64Frame(senderSP, savedFPAddr.getAddressAt(0), senderPC); ++ } ++ ++ protected boolean hasSenderPD() { ++ // FIXME ++ // Check for null ebp? Need to do some tests. ++ return true; ++ } ++ ++ public long frameSize() { ++ return (getSenderSP().minus(getSP()) / VM.getVM().getAddressSize()); ++ } ++ ++ public Address getLink() { ++ return addressOfStackSlot(LINK_OFFSET).getAddressAt(0); ++ } ++ ++ // FIXME: not implementable yet ++ //inline void frame::set_link(intptr_t* addr) { *(intptr_t **)addr_at(link_offset) = addr; } ++ ++ public Address getUnextendedSP() { return raw_unextendedSP; } ++ ++ // Return address: ++ public Address getSenderPCAddr() { return addressOfStackSlot(RETURN_ADDR_OFFSET); } ++ public Address getSenderPC() { return getSenderPCAddr().getAddressAt(0); } ++ ++ // return address of param, zero origin index. ++ public Address getNativeParamAddr(int idx) { ++ return addressOfStackSlot(NATIVE_FRAME_INITIAL_PARAM_OFFSET + idx); ++ } ++ ++ public Address getSenderSP() { return addressOfStackSlot(SENDER_SP_OFFSET); } ++ ++ public Address addressOfInterpreterFrameLocals() { ++ return addressOfStackSlot(INTERPRETER_FRAME_LOCALS_OFFSET); ++ } ++ ++ private Address addressOfInterpreterFrameBCX() { ++ return addressOfStackSlot(INTERPRETER_FRAME_BCX_OFFSET); ++ } ++ ++ public int getInterpreterFrameBCI() { ++ // FIXME: this is not atomic with respect to GC and is unsuitable ++ // for use in a non-debugging, or reflective, system. Need to ++ // figure out how to express this. ++ Address bcp = addressOfInterpreterFrameBCX().getAddressAt(0); ++ Address methodHandle = addressOfInterpreterFrameMethod().getAddressAt(0); ++ Method method = (Method)Metadata.instantiateWrapperFor(methodHandle); ++ return bcpToBci(bcp, method); ++ } ++ ++ public Address addressOfInterpreterFrameMDX() { ++ return addressOfStackSlot(INTERPRETER_FRAME_MDX_OFFSET); ++ } ++ ++ // FIXME ++ //inline int frame::interpreter_frame_monitor_size() { ++ // return BasicObjectLock::size(); ++ //} ++ ++ // expression stack ++ // (the max_stack arguments are used by the GC; see class FrameClosure) ++ ++ public Address addressOfInterpreterFrameExpressionStack() { ++ Address monitorEnd = interpreterFrameMonitorEnd().address(); ++ return monitorEnd.addOffsetTo(-1 * VM.getVM().getAddressSize()); ++ } ++ ++ public int getInterpreterFrameExpressionStackDirection() { return -1; } ++ ++ // top of expression stack ++ public Address addressOfInterpreterFrameTOS() { ++ return getSP(); ++ } ++ ++ /** Expression stack from top down */ ++ public Address addressOfInterpreterFrameTOSAt(int slot) { ++ return addressOfInterpreterFrameTOS().addOffsetTo(slot * VM.getVM().getAddressSize()); ++ } ++ ++ public Address getInterpreterFrameSenderSP() { ++ if (Assert.ASSERTS_ENABLED) { ++ Assert.that(isInterpretedFrame(), "interpreted frame expected"); ++ } ++ return addressOfStackSlot(INTERPRETER_FRAME_SENDER_SP_OFFSET).getAddressAt(0); ++ } ++ ++ // Monitors ++ public BasicObjectLock interpreterFrameMonitorBegin() { ++ return new BasicObjectLock(addressOfStackSlot(INTERPRETER_FRAME_MONITOR_BLOCK_BOTTOM_OFFSET)); ++ } ++ ++ public BasicObjectLock interpreterFrameMonitorEnd() { ++ Address result = addressOfStackSlot(INTERPRETER_FRAME_MONITOR_BLOCK_TOP_OFFSET).getAddressAt(0); ++ if (Assert.ASSERTS_ENABLED) { ++ // make sure the pointer points inside the frame ++ Assert.that(AddressOps.gt(getFP(), result), "result must < than frame pointer"); ++ Assert.that(AddressOps.lte(getSP(), result), "result must >= than stack pointer"); ++ } ++ return new BasicObjectLock(result); ++ } ++ ++ public int interpreterFrameMonitorSize() { ++ return BasicObjectLock.size(); ++ } ++ ++ // Method ++ public Address addressOfInterpreterFrameMethod() { ++ return addressOfStackSlot(INTERPRETER_FRAME_METHOD_OFFSET); ++ } ++ ++ // Constant pool cache ++ public Address addressOfInterpreterFrameCPCache() { ++ return addressOfStackSlot(INTERPRETER_FRAME_CACHE_OFFSET); ++ } ++ ++ // Entry frames ++ public JavaCallWrapper getEntryFrameCallWrapper() { ++ return new MIPS64JavaCallWrapper(addressOfStackSlot(ENTRY_FRAME_CALL_WRAPPER_OFFSET).getAddressAt(0)); ++ } ++ ++ protected Address addressOfSavedOopResult() { ++ // offset is 2 for compiler2 and 3 for compiler1 ++ return getSP().addOffsetTo((VM.getVM().isClientCompiler() ? 2 : 3) * ++ VM.getVM().getAddressSize()); ++ } ++ ++ protected Address addressOfSavedReceiver() { ++ return getSP().addOffsetTo(-4 * VM.getVM().getAddressSize()); ++ } ++ ++ private void dumpStack() { ++ if (getFP() != null) { ++ for (Address addr = getSP().addOffsetTo(-5 * VM.getVM().getAddressSize()); ++ AddressOps.lte(addr, getFP().addOffsetTo(5 * VM.getVM().getAddressSize())); ++ addr = addr.addOffsetTo(VM.getVM().getAddressSize())) { ++ System.out.println(addr + ": " + addr.getAddressAt(0)); ++ } ++ } else { ++ for (Address addr = getSP().addOffsetTo(-5 * VM.getVM().getAddressSize()); ++ AddressOps.lte(addr, getSP().addOffsetTo(20 * VM.getVM().getAddressSize())); ++ addr = addr.addOffsetTo(VM.getVM().getAddressSize())) { ++ System.out.println(addr + ": " + addr.getAddressAt(0)); ++ } ++ } ++ } ++} +diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/mips64/MIPS64JavaCallWrapper.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/mips64/MIPS64JavaCallWrapper.java +new file mode 100644 +index 0000000000..81fcb5b568 +--- /dev/null ++++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/mips64/MIPS64JavaCallWrapper.java +@@ -0,0 +1,57 @@ ++/* ++ * Copyright (c) 2001, 2002, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2015, 2018, Loongson Technology. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ * ++ */ ++ ++package sun.jvm.hotspot.runtime.mips64; ++ ++import java.util.*; ++import sun.jvm.hotspot.debugger.*; ++import sun.jvm.hotspot.types.*; ++import sun.jvm.hotspot.runtime.*; ++ ++public class MIPS64JavaCallWrapper extends JavaCallWrapper { ++ private static AddressField lastJavaFPField; ++ ++ static { ++ VM.registerVMInitializedObserver(new Observer() { ++ public void update(Observable o, Object data) { ++ initialize(VM.getVM().getTypeDataBase()); ++ } ++ }); ++ } ++ ++ private static synchronized void initialize(TypeDataBase db) { ++ Type type = db.lookupType("JavaFrameAnchor"); ++ ++ lastJavaFPField = type.getAddressField("_last_Java_fp"); ++ } ++ ++ public MIPS64JavaCallWrapper(Address addr) { ++ super(addr); ++ } ++ ++ public Address getLastJavaFP() { ++ return lastJavaFPField.getValue(addr.addOffsetTo(anchorField.getOffset())); ++ } ++} +diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/mips64/MIPS64RegisterMap.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/mips64/MIPS64RegisterMap.java +new file mode 100644 +index 0000000000..648503792d +--- /dev/null ++++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/mips64/MIPS64RegisterMap.java +@@ -0,0 +1,52 @@ ++/* ++ * Copyright (c) 2001, 2012, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2015, 2018, Loongson Technology. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ * ++ */ ++ ++package sun.jvm.hotspot.runtime.mips64; ++ ++import sun.jvm.hotspot.debugger.*; ++import sun.jvm.hotspot.runtime.*; ++ ++public class MIPS64RegisterMap extends RegisterMap { ++ ++ /** This is the only public constructor */ ++ public MIPS64RegisterMap(JavaThread thread, boolean updateMap) { ++ super(thread, updateMap); ++ } ++ ++ protected MIPS64RegisterMap(RegisterMap map) { ++ super(map); ++ } ++ ++ public Object clone() { ++ MIPS64RegisterMap retval = new MIPS64RegisterMap(this); ++ return retval; ++ } ++ ++ // no PD state to clear or copy: ++ protected void clearPD() {} ++ protected void initializePD() {} ++ protected void initializeFromPD(RegisterMap map) {} ++ protected Address getLocationPD(VMReg reg) { return null; } ++} +diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/PlatformInfo.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/PlatformInfo.java +index aa69257866..9c97d09bc3 100644 +--- a/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/PlatformInfo.java ++++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/PlatformInfo.java +@@ -22,6 +22,13 @@ + * + */ + ++/* ++ * This file has been modified by Loongson Technology in 2020. These ++ * modifications are Copyright (c) 2018, 2020, Loongson Technology, and are made ++ * available on the same license terms set forth above. ++ * ++ */ ++ + package sun.jvm.hotspot.utilities; + + /** Provides canonicalized OS and CPU information for the rest of the +@@ -65,6 +72,10 @@ public class PlatformInfo { + return cpu; + } else if (cpu.equals("aarch64")) { + return cpu; ++ } else if (cpu.equals("mips64") || cpu.equals("mips64el")) { ++ return "mips64"; ++ } else if (cpu.equals("loongarch64")) { ++ return "loongarch64"; + } else { + try { + Class pic = Class.forName("sun.jvm.hotspot.utilities.PlatformInfoClosed"); +diff --git a/hotspot/make/defs.make b/hotspot/make/defs.make +index a3573da56f..6e93182c92 100644 +--- a/hotspot/make/defs.make ++++ b/hotspot/make/defs.make +@@ -22,6 +22,12 @@ + # + # + ++# ++# This file has been modified by Loongson Technology in 2020. These ++# modifications are Copyright (c) 2015, 2020, Loongson Technology, and are made ++# available on the same license terms set forth above. ++# ++ + # The common definitions for hotspot builds. + + # Optionally include SPEC file generated by configure. +@@ -285,7 +291,7 @@ ifneq ($(OSNAME),windows) + + # Use uname output for SRCARCH, but deal with platform differences. If ARCH + # is not explicitly listed below, it is treated as x86. +- SRCARCH ?= $(ARCH/$(filter sparc sparc64 ia64 amd64 x86_64 ppc ppc64 ppc64le zero aarch64,$(ARCH))) ++ SRCARCH ?= $(ARCH/$(filter sparc sparc64 ia64 amd64 x86_64 ppc ppc64 ppc64le zero aarch64 mips64 loongarch64,$(ARCH))) + ARCH/ = x86 + ARCH/sparc = sparc + ARCH/sparc64= sparc +@@ -295,6 +301,10 @@ ifneq ($(OSNAME),windows) + ARCH/ppc64 = ppc + ARCH/ppc64le= ppc + ARCH/ppc = ppc ++ ARCH/mips64 = mips ++ ARCH/mips64el = mips ++ ARCH/loongarch64 = loongarch ++ ARCH/loongarch = loongarch + ARCH/zero = zero + ARCH/aarch64 = aarch64 + +@@ -317,6 +327,20 @@ ifneq ($(OSNAME),windows) + BUILDARCH = ppc64 + endif + endif ++ ifeq ($(BUILDARCH), mips) ++ ifdef LP64 ++# ifeq ($(OPENJDK_TARGET_CPU_ENDIAN), little) ++# BUILDARCH = mips64el ++# else ++ BUILDARCH = mips64 ++# endif ++ endif ++ endif ++ ifeq ($(BUILDARCH), loongarch) ++ ifdef LP64 ++ BUILDARCH = loongarch64 ++ endif ++ endif + + # LIBARCH is 1:1 mapping from BUILDARCH, except for ARCH=ppc64le + ifeq ($(ARCH),ppc64le) +@@ -332,9 +356,18 @@ ifneq ($(OSNAME),windows) + LIBARCH/sparcv9 = sparcv9 + LIBARCH/ia64 = ia64 + LIBARCH/ppc64 = ppc64 ++ LIBARCH/loongarch = loongarch64 + LIBARCH/zero = $(ZERO_LIBARCH) + +- LP64_ARCH += sparcv9 amd64 ia64 ppc64 aarch64 zero ++ ifeq ($(LIBARCH), mips64) ++ ifeq ($(OPENJDK_TARGET_CPU_ENDIAN), little) ++ LIBARCH = mips64el ++ else ++ LIBARCH = mips64 ++ endif ++ endif ++ ++ LP64_ARCH += sparcv9 amd64 ia64 ppc64 aarch64 mips64 mips64el loongarch64 zero + endif + + # Required make macro settings for all platforms +diff --git a/hotspot/make/linux/Makefile b/hotspot/make/linux/Makefile +index e8f2010412..5aff01e87d 100644 +--- a/hotspot/make/linux/Makefile ++++ b/hotspot/make/linux/Makefile +@@ -74,6 +74,10 @@ ifneq (,$(findstring $(ARCH), ppc ppc64)) + FORCE_TIERED=0 + endif + endif ++# C1 is not ported on mips64, so we cannot build a tiered VM: ++ifeq (mips64, $(findstring mips64, $(ARCH))) ++ FORCE_TIERED=0 ++endif + + ifdef LP64 + ifeq ("$(filter $(LP64_ARCH),$(BUILDARCH))","") +diff --git a/hotspot/make/linux/makefiles/defs.make b/hotspot/make/linux/makefiles/defs.make +index ec414639d2..9ade73ab34 100644 +--- a/hotspot/make/linux/makefiles/defs.make ++++ b/hotspot/make/linux/makefiles/defs.make +@@ -22,6 +22,12 @@ + # + # + ++# ++# This file has been modified by Loongson Technology in 2020. These ++# modifications are Copyright (c) 2018, 2020, Loongson Technology, and are made ++# available on the same license terms set forth above. ++# ++ + # The common definitions for hotspot linux builds. + # Include the top level defs.make under make directory instead of this one. + # This file is included into make/defs.make. +@@ -39,6 +45,18 @@ ifndef ARCH + ARCH := ppc64 + endif + endif ++ifeq ($(ARCH), mips64el) ++ ARCH=mips64 ++endif ++ifeq ($(LP64), 1) ++ ifeq ($(ARCH), mips) ++ ARCH=mips64 ++ endif ++endif ++ ++ifeq ($(ARCH), loongarch) ++ ARCH=loongarch64 ++endif + + PATH_SEP ?= : + +@@ -83,6 +101,36 @@ ifneq (,$(findstring $(ARCH), sparc)) + HS_ARCH = sparc + endif + ++# mips ++ifeq ($(ARCH), mips64) ++ ifeq ($(ARCH_DATA_MODEL), 64) ++ ARCH_DATA_MODEL = 64 ++ MAKE_ARGS += LP64=1 ++ PLATFORM = linux-mips64 ++ VM_PLATFORM = linux_mips64 ++ else ++ ARCH_DATA_MODEL = 32 ++ PLATFORM = linux-mips32 ++ VM_PLATFORM = linux_mips32 ++ endif ++ HS_ARCH = mips ++endif ++ ++# loongarch ++ifeq ($(ARCH), loongarch64) ++ ifeq ($(ARCH_DATA_MODEL), 64) ++ ARCH_DATA_MODEL = 64 ++ MAKE_ARGS += LP64=1 ++ PLATFORM = linux-loongarch64 ++ VM_PLATFORM = linux_loongarch64 ++ else ++ ARCH_DATA_MODEL = 32 ++ PLATFORM = linux-loongarch32 ++ VM_PLATFORM = linux_loongarch32 ++ endif ++ HS_ARCH = loongarch ++endif ++ + # i686/i586 and amd64/x86_64 + ifneq (,$(findstring $(ARCH), amd64 x86_64 i686 i586)) + ifeq ($(ARCH_DATA_MODEL), 64) +@@ -311,16 +359,24 @@ ADD_SA_BINARIES/sparc = $(EXPORT_JRE_LIB_ARCH_DIR)/libsaproc.$(LIBRARY_SUFFIX) \ + $(EXPORT_LIB_DIR)/sa-jdi.jar + ADD_SA_BINARIES/aarch64 = $(EXPORT_JRE_LIB_ARCH_DIR)/libsaproc.$(LIBRARY_SUFFIX) \ + $(EXPORT_LIB_DIR)/sa-jdi.jar ++ADD_SA_BINARIES/mips = $(EXPORT_JRE_LIB_ARCH_DIR)/libsaproc.$(LIBRARY_SUFFIX) \ ++ $(EXPORT_LIB_DIR)/sa-jdi.jar ++ADD_SA_BINARIES/loongarch = $(EXPORT_JRE_LIB_ARCH_DIR)/libsaproc.$(LIBRARY_SUFFIX) \ ++ $(EXPORT_LIB_DIR)/sa-jdi.jar + ifeq ($(ENABLE_FULL_DEBUG_SYMBOLS),1) + ifneq ($(STRIP_POLICY),no_strip) + ifeq ($(ZIP_DEBUGINFO_FILES),1) + ADD_SA_BINARIES/x86 += $(EXPORT_JRE_LIB_ARCH_DIR)/libsaproc.diz + ADD_SA_BINARIES/sparc += $(EXPORT_JRE_LIB_ARCH_DIR)/libsaproc.diz + ADD_SA_BINARIES/aarch64 += $(EXPORT_JRE_LIB_ARCH_DIR)/libsaproc.diz ++ ADD_SA_BINARIES/mips += $(EXPORT_JRE_LIB_ARCH_DIR)/libsaproc.diz ++ ADD_SA_BINARIES/loongarch += $(EXPORT_JRE_LIB_ARCH_DIR)/libsaproc.diz + else + ADD_SA_BINARIES/x86 += $(EXPORT_JRE_LIB_ARCH_DIR)/libsaproc.debuginfo + ADD_SA_BINARIES/sparc += $(EXPORT_JRE_LIB_ARCH_DIR)/libsaproc.debuginfo + ADD_SA_BINARIES/aarch64 += $(EXPORT_JRE_LIB_ARCH_DIR)/libsaproc.debuginfo ++ ADD_SA_BINARIES/mips += $(EXPORT_JRE_LIB_ARCH_DIR)/libsaproc.debuginfo ++ ADD_SA_BINARIES/loongarch += $(EXPORT_JRE_LIB_ARCH_DIR)/libsaproc.debuginfo + endif + endif + endif +diff --git a/hotspot/make/linux/makefiles/gcc.make b/hotspot/make/linux/makefiles/gcc.make +index 7dde7f0963..94c6d1d015 100644 +--- a/hotspot/make/linux/makefiles/gcc.make ++++ b/hotspot/make/linux/makefiles/gcc.make +@@ -22,6 +22,12 @@ + # + # + ++# ++# This file has been modified by Loongson Technology in 2020. These ++# modifications are Copyright (c) 2015, 2020, Loongson Technology, and are made ++# available on the same license terms set forth above. ++# ++ + #------------------------------------------------------------------------ + # CC, CXX & AS + +@@ -177,6 +183,9 @@ ARCHFLAG/aarch64 = + ARCHFLAG/ia64 = + ARCHFLAG/sparc = -m32 -mcpu=v9 + ARCHFLAG/sparcv9 = -m64 -mcpu=v9 ++ARCHFLAG/mips64 = -mabi=64 ++#ARCHFLAG/loongarch64 = -lp64 ++ARCHFLAG/loongarch64 = + ARCHFLAG/zero = $(ZERO_ARCHFLAG) + ARCHFLAG/ppc64 = -m64 + +@@ -202,7 +211,7 @@ else + endif + + # Compiler warnings are treated as errors +-WARNINGS_ARE_ERRORS = -Werror ++#WARNINGS_ARE_ERRORS = -Werror + + ifeq ($(USE_CLANG), true) + # However we need to clean the code up before we can unrestrictedly enable this option with Clang +diff --git a/hotspot/make/linux/makefiles/loongarch64.make b/hotspot/make/linux/makefiles/loongarch64.make +new file mode 100644 +index 0000000000..9e3cdb6f23 +--- /dev/null ++++ b/hotspot/make/linux/makefiles/loongarch64.make +@@ -0,0 +1,43 @@ ++# ++# Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved. ++# Copyright (c) 2015, 2020, Loongson Technology. All rights reserved. ++# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++# ++# This code is free software; you can redistribute it and/or modify it ++# under the terms of the GNU General Public License version 2 only, as ++# published by the Free Software Foundation. ++# ++# This code is distributed in the hope that it will be useful, but WITHOUT ++# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++# version 2 for more details (a copy is included in the LICENSE file that ++# accompanied this code). ++# ++# You should have received a copy of the GNU General Public License version ++# 2 along with this work; if not, write to the Free Software Foundation, ++# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++# ++# Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++# or visit www.oracle.com if you need additional information or have any ++# questions. ++# ++# ++ ++# Not included in includeDB because it has no dependencies ++Obj_Files += linux_loongarch.o ++ ++# The copied fdlibm routines in sharedRuntimeTrig.o must not be optimized ++OPT_CFLAGS/sharedRuntimeTrig.o = $(OPT_CFLAGS/NOOPT) ++# The copied fdlibm routines in sharedRuntimeTrans.o must not be optimized ++OPT_CFLAGS/sharedRuntimeTrans.o = $(OPT_CFLAGS/NOOPT) ++# Must also specify if CPU is little endian ++CFLAGS += -DVM_LITTLE_ENDIAN ++ ++CFLAGS += -DSICORTEX_ERRATA ++ ++CFLAGS += -D_LP64=1 ++ ++# The serviceability agent relies on frame pointer (%rbp) to walk thread stack ++CFLAGS += -fno-omit-frame-pointer ++ ++OPT_CFLAGS/compactingPermGenGen.o = -O1 +diff --git a/hotspot/make/linux/makefiles/mips64.make b/hotspot/make/linux/makefiles/mips64.make +new file mode 100644 +index 0000000000..d9af3b13ab +--- /dev/null ++++ b/hotspot/make/linux/makefiles/mips64.make +@@ -0,0 +1,43 @@ ++# ++# Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved. ++# Copyright (c) 2015, 2016, Loongson Technology. All rights reserved. ++# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++# ++# This code is free software; you can redistribute it and/or modify it ++# under the terms of the GNU General Public License version 2 only, as ++# published by the Free Software Foundation. ++# ++# This code is distributed in the hope that it will be useful, but WITHOUT ++# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++# version 2 for more details (a copy is included in the LICENSE file that ++# accompanied this code). ++# ++# You should have received a copy of the GNU General Public License version ++# 2 along with this work; if not, write to the Free Software Foundation, ++# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++# ++# Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++# or visit www.oracle.com if you need additional information or have any ++# questions. ++# ++# ++ ++# Not included in includeDB because it has no dependencies ++Obj_Files += linux_mips.o ++ ++# The copied fdlibm routines in sharedRuntimeTrig.o must not be optimized ++OPT_CFLAGS/sharedRuntimeTrig.o = $(OPT_CFLAGS/NOOPT) ++# The copied fdlibm routines in sharedRuntimeTrans.o must not be optimized ++OPT_CFLAGS/sharedRuntimeTrans.o = $(OPT_CFLAGS/NOOPT) ++# Must also specify if CPU is little endian ++CFLAGS += -DVM_LITTLE_ENDIAN ++ ++CFLAGS += -DSICORTEX_ERRATA ++ ++CFLAGS += -D_LP64=1 ++ ++# The serviceability agent relies on frame pointer (%rbp) to walk thread stack ++CFLAGS += -fno-omit-frame-pointer ++ ++OPT_CFLAGS/compactingPermGenGen.o = -O1 +diff --git a/hotspot/make/linux/makefiles/sa.make b/hotspot/make/linux/makefiles/sa.make +index cdcb16a1a3..34c71bd666 100644 +--- a/hotspot/make/linux/makefiles/sa.make ++++ b/hotspot/make/linux/makefiles/sa.make +@@ -22,6 +22,12 @@ + # + # + ++# ++# This file has been modified by Loongson Technology in 2020. These ++# modifications are Copyright (c) 2015, 2020, Loongson Technology, and are made ++# available on the same license terms set forth above. ++# ++ + # This makefile (sa.make) is included from the sa.make in the + # build directories. + +@@ -109,6 +115,8 @@ $(GENERATED)/sa-jdi.jar:: $(AGENT_FILES) + $(QUIETLY) $(REMOTE) $(RUN.JAVAH) -classpath $(SA_CLASSDIR) -d $(GENERATED) -jni sun.jvm.hotspot.debugger.x86.X86ThreadContext + $(QUIETLY) $(REMOTE) $(RUN.JAVAH) -classpath $(SA_CLASSDIR) -d $(GENERATED) -jni sun.jvm.hotspot.debugger.amd64.AMD64ThreadContext + $(QUIETLY) $(REMOTE) $(RUN.JAVAH) -classpath $(SA_CLASSDIR) -d $(GENERATED) -jni sun.jvm.hotspot.debugger.aarch64.AARCH64ThreadContext ++ $(QUIETLY) $(REMOTE) $(RUN.JAVAH) -classpath $(SA_CLASSDIR) -d $(GENERATED) -jni sun.jvm.hotspot.debugger.mips64.MIPS64ThreadContext ++ $(QUIETLY) $(REMOTE) $(RUN.JAVAH) -classpath $(SA_CLASSDIR) -d $(GENERATED) -jni sun.jvm.hotspot.debugger.loongarch64.LOONGARCH64ThreadContext + $(QUIETLY) $(REMOTE) $(RUN.JAVAH) -classpath $(SA_CLASSDIR) -d $(GENERATED) -jni sun.jvm.hotspot.debugger.sparc.SPARCThreadContext + $(QUIETLY) $(REMOTE) $(RUN.JAVAH) -classpath $(SA_CLASSDIR) -d $(GENERATED) -jni sun.jvm.hotspot.asm.Disassembler + +diff --git a/hotspot/make/linux/makefiles/saproc.make b/hotspot/make/linux/makefiles/saproc.make +index ffc0ec5ce5..c04a6765df 100644 +--- a/hotspot/make/linux/makefiles/saproc.make ++++ b/hotspot/make/linux/makefiles/saproc.make +@@ -21,6 +21,13 @@ + # questions. + # + # ++ ++# ++# This file has been modified by Loongson Technology in 2019. These ++# modifications are Copyright (c) 2018, 2019, Loongson Technology, and are made ++# available on the same license terms set forth above. ++# ++ + include $(GAMMADIR)/make/defs.make + include $(GAMMADIR)/make/altsrc.make + +@@ -81,7 +88,12 @@ endif + SA_LFLAGS = $(MAPFLAG:FILENAME=$(SAMAPFILE)) $(LDFLAGS_HASH_STYLE) \ + $(LDFLAGS_NO_EXEC_STACK) $(EXTRA_LDFLAGS) + ++ifneq (mips64, $(findstring mips64, $(BUILDARCH))) + SAARCH ?= $(BUILDARCH) ++else ++#If -Dmips64 is used, mips64 would be conflict with "struct mips64_watch_regs mips64" in /usr/include/asm/ptrace.h. ++SAARCH ?= mips ++endif + + $(LIBSAPROC): $(SASRCFILES) $(SAMAPFILE) + $(QUIETLY) if [ "$(BOOT_JAVA_HOME)" = "" ]; then \ +diff --git a/hotspot/make/linux/makefiles/sparcWorks.make b/hotspot/make/linux/makefiles/sparcWorks.make +index e39116023c..dbc2ace825 100644 +--- a/hotspot/make/linux/makefiles/sparcWorks.make ++++ b/hotspot/make/linux/makefiles/sparcWorks.make +@@ -22,6 +22,12 @@ + # + # + ++# ++# This file has been modified by Loongson Technology in 2015. These ++# modifications are Copyright (c) 2015 Loongson Technology, and are made ++# available on the same license terms set forth above. ++# ++ + #------------------------------------------------------------------------ + # CC, CXX & AS + +@@ -38,6 +44,7 @@ endif + ARCHFLAG = $(ARCHFLAG/$(BUILDARCH)) + ARCHFLAG/i486 = -m32 + ARCHFLAG/amd64 = -m64 ++ARCHFLAG/mips64 = -m64 + + CFLAGS += $(ARCHFLAG) + AOUT_FLAGS += $(ARCHFLAG) +diff --git a/hotspot/make/linux/makefiles/vm.make b/hotspot/make/linux/makefiles/vm.make +index 04b7c20287..5e428538a0 100644 +--- a/hotspot/make/linux/makefiles/vm.make ++++ b/hotspot/make/linux/makefiles/vm.make +@@ -22,6 +22,12 @@ + # + # + ++# ++# This file has been modified by Loongson Technology in 2020. These ++# modifications are Copyright (c) 2018, 2020, Loongson Technology, and are made ++# available on the same license terms set forth above. ++# ++ + # Rules to build JVM and related libraries, included from vm.make in the build + # directory. + +@@ -99,9 +105,22 @@ CXXFLAGS = \ + ${HS_LIB_ARCH} \ + ${VM_DISTRO} + ++ifeq ($(MIPS_ABI),n32) ++ CXXFLAGS += -DN32 ++else ++ ifeq ($(MIPS_ABI),n64) ++ CXXFLAGS += -DN64 ++ endif ++endif + # This is VERY important! The version define must only be supplied to vm_version.o + # If not, ccache will not re-use the cache at all, since the version string might contain + # a time and date. ++ifdef LOONGSON_RUNTIME_NAME ++ LOONGSON_VM_INFO = -DLOONGSON_RUNTIME_NAME="\"$(LOONGSON_RUNTIME_NAME)\"" ++else ++ LOONGSON_VM_INFO = -DLOONGSON_RUNTIME_NAME="\"\"" ++endif ++CXXFLAGS/vmError.o += ${LOONGSON_VM_INFO} + CXXFLAGS/vm_version.o += ${JRE_VERSION} ${VERSION_CFLAGS} + CXXFLAGS/arguments.o += ${VERSION_CFLAGS} + +@@ -211,6 +230,15 @@ endif + ifeq ($(Platform_arch_model), x86_64) + Src_Files_EXCLUDE += \*x86_32\* + endif ++ifeq ($(Platform_arch_model), mips_32) ++Src_Files_EXCLUDE += \*mips_64\* ++endif ++ifeq ($(Platform_arch_model), mips_64) ++Src_Files_EXCLUDE += \*mips_32\* ++endif ++ifeq ($(Platform_arch_model), loongarch_64) ++Src_Files_EXCLUDE += \*loongarch_32\* ++endif + + # Alternate vm.make + # This has to be included here to allow changes to the source +diff --git a/hotspot/make/linux/platform_loongarch64 b/hotspot/make/linux/platform_loongarch64 +new file mode 100644 +index 0000000000..d704cf389a +--- /dev/null ++++ b/hotspot/make/linux/platform_loongarch64 +@@ -0,0 +1,17 @@ ++os_family = linux ++ ++arch = loongarch ++ ++arch_model = loongarch_64 ++ ++os_arch = linux_loongarch ++ ++os_arch_model = linux_loongarch_64 ++ ++lib_arch = loongarch64 ++ ++compiler = gcc ++ ++gnu_dis_arch = loongarch64 ++ ++sysdefs = -DLINUX -D_GNU_SOURCE -DLOONGARCH64 +diff --git a/hotspot/make/linux/platform_mips64 b/hotspot/make/linux/platform_mips64 +new file mode 100644 +index 0000000000..c283671f82 +--- /dev/null ++++ b/hotspot/make/linux/platform_mips64 +@@ -0,0 +1,17 @@ ++os_family = linux ++ ++arch = mips ++ ++arch_model = mips_64 ++ ++os_arch = linux_mips ++ ++os_arch_model = linux_mips_64 ++ ++lib_arch = mips64 ++ ++compiler = gcc ++ ++gnu_dis_arch = mips64 ++ ++sysdefs = -DLINUX -D_GNU_SOURCE -DMIPS64 +diff --git a/hotspot/make/sa.files b/hotspot/make/sa.files +index d6e728a9a8..43b08e3ad1 100644 +--- a/hotspot/make/sa.files ++++ b/hotspot/make/sa.files +@@ -22,6 +22,12 @@ + # + # + ++# ++# This file has been modified by Loongson Technology in 2020. These ++# modifications are Copyright (c) 2015, 2020, Loongson Technology, and are made ++# available on the same license terms set forth above. ++# ++ + # This filelist macro is included in platform specific sa.make + # included all packages/*.java. package list can be generated by + # $(GAMMADIR)/agent/make/build-pkglist. +@@ -52,14 +58,20 @@ $(AGENT_SRC_DIR)/sun/jvm/hotspot/debugger/cdbg/basic/*.java \ + $(AGENT_SRC_DIR)/sun/jvm/hotspot/debugger/dummy/*.java \ + $(AGENT_SRC_DIR)/sun/jvm/hotspot/debugger/linux/*.java \ + $(AGENT_SRC_DIR)/sun/jvm/hotspot/debugger/linux/amd64/*.java \ ++$(AGENT_SRC_DIR)/sun/jvm/hotspot/debugger/linux/mips64/*.java \ ++$(AGENT_SRC_DIR)/sun/jvm/hotspot/debugger/linux/loongarch64/*.java \ + $(AGENT_SRC_DIR)/sun/jvm/hotspot/debugger/linux/x86/*.java \ + $(AGENT_SRC_DIR)/sun/jvm/hotspot/debugger/linux/aarch64/*.java \ + $(AGENT_SRC_DIR)/sun/jvm/hotspot/debugger/linux/sparc/*.java \ ++$(AGENT_SRC_DIR)/sun/jvm/hotspot/debugger/mips64/*.java \ ++$(AGENT_SRC_DIR)/sun/jvm/hotspot/debugger/loongarch64/*.java \ + $(AGENT_SRC_DIR)/sun/jvm/hotspot/debugger/posix/*.java \ + $(AGENT_SRC_DIR)/sun/jvm/hotspot/debugger/posix/elf/*.java \ + $(AGENT_SRC_DIR)/sun/jvm/hotspot/debugger/proc/*.java \ + $(AGENT_SRC_DIR)/sun/jvm/hotspot/debugger/proc/amd64/*.java \ + $(AGENT_SRC_DIR)/sun/jvm/hotspot/debugger/proc/aarch64/*.java \ ++$(AGENT_SRC_DIR)/sun/jvm/hotspot/debugger/proc/mips64/*.java \ ++$(AGENT_SRC_DIR)/sun/jvm/hotspot/debugger/proc/loongarch64/*.java \ + $(AGENT_SRC_DIR)/sun/jvm/hotspot/debugger/proc/sparc/*.java \ + $(AGENT_SRC_DIR)/sun/jvm/hotspot/debugger/proc/x86/*.java \ + $(AGENT_SRC_DIR)/sun/jvm/hotspot/debugger/remote/*.java \ +@@ -94,8 +106,12 @@ $(AGENT_SRC_DIR)/sun/jvm/hotspot/runtime/bsd_x86/*.java \ + $(AGENT_SRC_DIR)/sun/jvm/hotspot/runtime/linux/*.java \ + $(AGENT_SRC_DIR)/sun/jvm/hotspot/runtime/linux_amd64/*.java \ + $(AGENT_SRC_DIR)/sun/jvm/hotspot/runtime/linux_aarch64/*.java \ ++$(AGENT_SRC_DIR)/sun/jvm/hotspot/runtime/linux_mips64/*.java \ ++$(AGENT_SRC_DIR)/sun/jvm/hotspot/runtime/linux_loongarch64/*.java \ + $(AGENT_SRC_DIR)/sun/jvm/hotspot/runtime/linux_x86/*.java \ + $(AGENT_SRC_DIR)/sun/jvm/hotspot/runtime/linux_sparc/*.java \ ++$(AGENT_SRC_DIR)/sun/jvm/hotspot/runtime/mips64/*.java \ ++$(AGENT_SRC_DIR)/sun/jvm/hotspot/runtime/loongarch64/*.java \ + $(AGENT_SRC_DIR)/sun/jvm/hotspot/runtime/posix/*.java \ + $(AGENT_SRC_DIR)/sun/jvm/hotspot/runtime/solaris_amd64/*.java \ + $(AGENT_SRC_DIR)/sun/jvm/hotspot/runtime/solaris_sparc/*.java \ +diff --git a/hotspot/src/cpu/aarch64/vm/c1_LIRAssembler_aarch64.cpp b/hotspot/src/cpu/aarch64/vm/c1_LIRAssembler_aarch64.cpp +index 35d34a08ea..3b8cf4a11d 100644 +--- a/hotspot/src/cpu/aarch64/vm/c1_LIRAssembler_aarch64.cpp ++++ b/hotspot/src/cpu/aarch64/vm/c1_LIRAssembler_aarch64.cpp +@@ -1177,7 +1177,9 @@ void LIR_Assembler::emit_opBranch(LIR_OpBranch* op) { + } + } + +- ++void LIR_Assembler::emit_opCmpBranch(LIR_OpCmpBranch* op) { ++ ShouldNotReachHere(); ++} + + void LIR_Assembler::emit_opConvert(LIR_OpConvert* op) { + LIR_Opr src = op->in_opr(); +@@ -1242,7 +1244,7 @@ void LIR_Assembler::emit_opConvert(LIR_OpConvert* op) { + } + case Bytecodes::_d2l: + { +- Register tmp = op->tmp1()->as_register(); ++ Register tmp = op->tmp()->as_register(); + __ clear_fpsr(); + __ fcvtzd(dest->as_register_lo(), src->as_double_reg()); + __ get_fpsr(tmp); +@@ -1253,7 +1255,7 @@ void LIR_Assembler::emit_opConvert(LIR_OpConvert* op) { + } + case Bytecodes::_f2i: + { +- Register tmp = op->tmp1()->as_register(); ++ Register tmp = op->tmp()->as_register(); + __ clear_fpsr(); + __ fcvtzsw(dest->as_register(), src->as_float_reg()); + __ get_fpsr(tmp); +@@ -1264,7 +1266,7 @@ void LIR_Assembler::emit_opConvert(LIR_OpConvert* op) { + } + case Bytecodes::_f2l: + { +- Register tmp = op->tmp1()->as_register(); ++ Register tmp = op->tmp()->as_register(); + __ clear_fpsr(); + __ fcvtzs(dest->as_register_lo(), src->as_float_reg()); + __ get_fpsr(tmp); +@@ -1275,7 +1277,7 @@ void LIR_Assembler::emit_opConvert(LIR_OpConvert* op) { + } + case Bytecodes::_d2i: + { +- Register tmp = op->tmp1()->as_register(); ++ Register tmp = op->tmp()->as_register(); + __ clear_fpsr(); + __ fcvtzdw(dest->as_register(), src->as_double_reg()); + __ get_fpsr(tmp); +@@ -1731,6 +1733,11 @@ void LIR_Assembler::cmove(LIR_Condition condition, LIR_Opr opr1, LIR_Opr opr2, L + __ csel(result->as_register(), opr1->as_register(), opr2->as_register(), acond); + } + ++void LIR_Assembler::cmp_cmove(LIR_Condition condition, LIR_Opr left, LIR_Opr right, ++ LIR_Opr src1, LIR_Opr src2, LIR_Opr result, BasicType type) { ++ ShouldNotReachHere(); ++} ++ + void LIR_Assembler::arith_op(LIR_Code code, LIR_Opr left, LIR_Opr right, LIR_Opr dest, CodeEmitInfo* info, bool pop_fpu_stack) { + assert(info == NULL, "should never be used, idiv/irem and ldiv/lrem not handled by this method"); + +diff --git a/hotspot/src/cpu/aarch64/vm/c1_LIRGenerator_aarch64.cpp b/hotspot/src/cpu/aarch64/vm/c1_LIRGenerator_aarch64.cpp +index 120dd1a7df..6a3289022d 100644 +--- a/hotspot/src/cpu/aarch64/vm/c1_LIRGenerator_aarch64.cpp ++++ b/hotspot/src/cpu/aarch64/vm/c1_LIRGenerator_aarch64.cpp +@@ -277,18 +277,29 @@ void LIRGenerator::increment_counter(LIR_Address* addr, int step) { + __ store(reg, addr); + } + +-void LIRGenerator::cmp_mem_int(LIR_Condition condition, LIR_Opr base, int disp, int c, CodeEmitInfo* info) { ++template ++void LIRGenerator::cmp_mem_int_branch(LIR_Condition condition, LIR_Opr base, int disp, int c, T tgt, CodeEmitInfo* info) { + LIR_Opr reg = new_register(T_INT); + __ load(generate_address(base, disp, T_INT), reg, info); +- __ cmp(condition, reg, LIR_OprFact::intConst(c)); ++ __ cmp_branch(condition, reg, LIR_OprFact::intConst(c), T_INT, tgt); + } + +-void LIRGenerator::cmp_reg_mem(LIR_Condition condition, LIR_Opr reg, LIR_Opr base, int disp, BasicType type, CodeEmitInfo* info) { ++// Explicit instantiation for all supported types. ++template void LIRGenerator::cmp_mem_int_branch(LIR_Condition, LIR_Opr, int, int, Label*, CodeEmitInfo*); ++template void LIRGenerator::cmp_mem_int_branch(LIR_Condition, LIR_Opr, int, int, BlockBegin*, CodeEmitInfo*); ++template void LIRGenerator::cmp_mem_int_branch(LIR_Condition, LIR_Opr, int, int, CodeStub*, CodeEmitInfo*); ++ ++template ++void LIRGenerator::cmp_reg_mem_branch(LIR_Condition condition, LIR_Opr reg, LIR_Opr base, int disp, BasicType type, T tgt, CodeEmitInfo* info) { + LIR_Opr reg1 = new_register(T_INT); + __ load(generate_address(base, disp, type), reg1, info); +- __ cmp(condition, reg, reg1); ++ __ cmp_branch(condition, reg, reg1, type, tgt); + } + ++// Explicit instantiation for all supported types. ++template void LIRGenerator::cmp_reg_mem_branch(LIR_Condition, LIR_Opr, LIR_Opr, int, BasicType, Label*, CodeEmitInfo*); ++template void LIRGenerator::cmp_reg_mem_branch(LIR_Condition, LIR_Opr, LIR_Opr, int, BasicType, BlockBegin*, CodeEmitInfo*); ++template void LIRGenerator::cmp_reg_mem_branch(LIR_Condition, LIR_Opr, LIR_Opr, int, BasicType, CodeStub*, CodeEmitInfo*); + + bool LIRGenerator::strength_reduce_multiply(LIR_Opr left, int c, LIR_Opr result, LIR_Opr tmp) { + +diff --git a/hotspot/src/cpu/loongarch/vm/assembler_loongarch.cpp b/hotspot/src/cpu/loongarch/vm/assembler_loongarch.cpp +new file mode 100644 +index 0000000000..ab4ece6f81 +--- /dev/null ++++ b/hotspot/src/cpu/loongarch/vm/assembler_loongarch.cpp +@@ -0,0 +1,856 @@ ++/* ++ * Copyright (c) 1997, 2014, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2015, 2021, Loongson Technology. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ * ++ */ ++ ++#include "precompiled.hpp" ++#include "asm/assembler.hpp" ++#include "asm/assembler.inline.hpp" ++#include "gc_interface/collectedHeap.inline.hpp" ++#include "interpreter/interpreter.hpp" ++#include "memory/cardTableModRefBS.hpp" ++#include "memory/resourceArea.hpp" ++#include "prims/methodHandles.hpp" ++#include "runtime/biasedLocking.hpp" ++#include "runtime/interfaceSupport.hpp" ++#include "runtime/objectMonitor.hpp" ++#include "runtime/os.hpp" ++#include "runtime/sharedRuntime.hpp" ++#include "runtime/stubRoutines.hpp" ++#if INCLUDE_ALL_GCS ++#include "gc_implementation/g1/g1CollectedHeap.inline.hpp" ++#include "gc_implementation/g1/g1SATBCardTableModRefBS.hpp" ++#include "gc_implementation/g1/heapRegion.hpp" ++#endif // INCLUDE_ALL_GCS ++ ++#ifdef PRODUCT ++#define BLOCK_COMMENT(str) /* nothing */ ++#define STOP(error) stop(error) ++#else ++#define BLOCK_COMMENT(str) block_comment(str) ++#define STOP(error) block_comment(error); stop(error) ++#endif ++ ++#define BIND(label) bind(label); BLOCK_COMMENT(#label ":") ++ ++// Implementation of AddressLiteral ++ ++AddressLiteral::AddressLiteral(address target, relocInfo::relocType rtype) { ++ _is_lval = false; ++ _target = target; ++ _rspec = rspec_from_rtype(rtype, target); ++} ++ ++// Implementation of Address ++ ++Address Address::make_array(ArrayAddress adr) { ++ AddressLiteral base = adr.base(); ++ Address index = adr.index(); ++ assert(index._disp == 0, "must not have disp"); // maybe it can? ++ Address array(index._base, index._index, index._scale, (intptr_t) base.target()); ++ array._rspec = base._rspec; ++ return array; ++} ++ ++// exceedingly dangerous constructor ++Address::Address(address loc, RelocationHolder spec) { ++ _base = noreg; ++ _index = noreg; ++ _scale = no_scale; ++ _disp = (intptr_t) loc; ++ _rspec = spec; ++} ++ ++ ++int Assembler::is_int_mask(int x) { ++ int xx = x; ++ int count = 0; ++ ++ while (x != 0) { ++ x &= (x - 1); ++ count++; ++ } ++ ++ if ((1<> 12)); ++ if (split_low12(disp)) ++ ori(AT, AT, split_low12(disp)); ++ ++ if (scale == 0) { ++ add_d(AT, AT, index); ++ } else { ++ alsl_d(AT, index, AT, scale - 1); ++ } ++ ldx_b(dst, base, AT); ++ } ++ } else { ++ if (is_simm(disp, 12)) { ++ ld_b(dst, base, disp); ++ } else { ++ lu12i_w(AT, split_low20(disp >> 12)); ++ if (split_low12(disp)) ++ ori(AT, AT, split_low12(disp)); ++ ldx_b(dst, base, AT); ++ } ++ } ++} ++ ++void Assembler::ld_bu(Register rd, Address src) { ++ Register dst = rd; ++ Register base = src.base(); ++ Register index = src.index(); ++ ++ int scale = src.scale(); ++ int disp = src.disp(); ++ ++ if (index != noreg) { ++ if (is_simm(disp, 12)) { ++ if (scale == 0) { ++ if (disp == 0) { ++ ldx_bu(dst, base, index); ++ } else { ++ add_d(AT, base, index); ++ ld_bu(dst, AT, disp); ++ } ++ } else { ++ alsl_d(AT, index, base, scale - 1); ++ ld_bu(dst, AT, disp); ++ } ++ } else { ++ lu12i_w(AT, split_low20(disp >> 12)); ++ if (split_low12(disp)) ++ ori(AT, AT, split_low12(disp)); ++ ++ if (scale == 0) { ++ add_d(AT, AT, index); ++ } else { ++ alsl_d(AT, index, AT, scale - 1); ++ } ++ ldx_bu(dst, base, AT); ++ } ++ } else { ++ if (is_simm(disp, 12)) { ++ ld_bu(dst, base, disp); ++ } else { ++ lu12i_w(AT, split_low20(disp >> 12)); ++ if (split_low12(disp)) ++ ori(AT, AT, split_low12(disp)); ++ ldx_bu(dst, base, AT); ++ } ++ } ++} ++ ++void Assembler::ld_d(Register rd, Address src){ ++ Register dst = rd; ++ Register base = src.base(); ++ Register index = src.index(); ++ ++ int scale = src.scale(); ++ int disp = src.disp(); ++ ++ if (index != noreg) { ++ if (is_simm(disp, 12)) { ++ if (scale == 0) { ++ if (disp == 0) { ++ ldx_d(dst, base, index); ++ } else { ++ add_d(AT, base, index); ++ ld_d(dst, AT, disp); ++ } ++ } else { ++ alsl_d(AT, index, base, scale - 1); ++ ld_d(dst, AT, disp); ++ } ++ } else if (is_simm(disp, 16) && !(disp & 3)) { ++ if (scale == 0) { ++ add_d(AT, base, index); ++ } else { ++ alsl_d(AT, index, base, scale - 1); ++ } ++ ldptr_d(dst, AT, disp); ++ } else { ++ lu12i_w(AT, split_low20(disp >> 12)); ++ if (split_low12(disp)) ++ ori(AT, AT, split_low12(disp)); ++ ++ if (scale == 0) { ++ add_d(AT, AT, index); ++ } else { ++ alsl_d(AT, index, AT, scale - 1); ++ } ++ ldx_d(dst, base, AT); ++ } ++ } else { ++ if (is_simm(disp, 12)) { ++ ld_d(dst, base, disp); ++ } else if (is_simm(disp, 16) && !(disp & 3)) { ++ ldptr_d(dst, base, disp); ++ } else { ++ lu12i_w(AT, split_low20(disp >> 12)); ++ if (split_low12(disp)) ++ ori(AT, AT, split_low12(disp)); ++ ldx_d(dst, base, AT); ++ } ++ } ++} ++ ++void Assembler::ld_h(Register rd, Address src){ ++ Register dst = rd; ++ Register base = src.base(); ++ Register index = src.index(); ++ ++ int scale = src.scale(); ++ int disp = src.disp(); ++ ++ if (index != noreg) { ++ if (is_simm(disp, 12)) { ++ if (scale == 0) { ++ if (disp == 0) { ++ ldx_h(dst, base, index); ++ } else { ++ add_d(AT, base, index); ++ ld_h(dst, AT, disp); ++ } ++ } else { ++ alsl_d(AT, index, base, scale - 1); ++ ld_h(dst, AT, disp); ++ } ++ } else { ++ lu12i_w(AT, split_low20(disp >> 12)); ++ if (split_low12(disp)) ++ ori(AT, AT, split_low12(disp)); ++ ++ if (scale == 0) { ++ add_d(AT, AT, index); ++ } else { ++ alsl_d(AT, index, AT, scale - 1); ++ } ++ ldx_h(dst, base, AT); ++ } ++ } else { ++ if (is_simm(disp, 12)) { ++ ld_h(dst, base, disp); ++ } else { ++ lu12i_w(AT, split_low20(disp >> 12)); ++ if (split_low12(disp)) ++ ori(AT, AT, split_low12(disp)); ++ ldx_h(dst, base, AT); ++ } ++ } ++} ++ ++void Assembler::ld_hu(Register rd, Address src){ ++ Register dst = rd; ++ Register base = src.base(); ++ Register index = src.index(); ++ ++ int scale = src.scale(); ++ int disp = src.disp(); ++ ++ if (index != noreg) { ++ if (is_simm(disp, 12)) { ++ if (scale == 0) { ++ if (disp == 0) { ++ ldx_hu(dst, base, index); ++ } else { ++ add_d(AT, base, index); ++ ld_hu(dst, AT, disp); ++ } ++ } else { ++ alsl_d(AT, index, base, scale - 1); ++ ld_hu(dst, AT, disp); ++ } ++ } else { ++ lu12i_w(AT, split_low20(disp >> 12)); ++ if (split_low12(disp)) ++ ori(AT, AT, split_low12(disp)); ++ ++ if (scale == 0) { ++ add_d(AT, AT, index); ++ } else { ++ alsl_d(AT, index, AT, scale - 1); ++ } ++ ldx_hu(dst, base, AT); ++ } ++ } else { ++ if (is_simm(disp, 12)) { ++ ld_hu(dst, base, disp); ++ } else { ++ lu12i_w(AT, split_low20(disp >> 12)); ++ if (split_low12(disp)) ++ ori(AT, AT, split_low12(disp)); ++ ldx_hu(dst, base, AT); ++ } ++ } ++} ++ ++void Assembler::ll_w(Register rd, Address src){ ++ assert(src.index() == NOREG, "index is unimplemented"); ++ ll_w(rd, src.base(), src.disp()); ++} ++ ++void Assembler::ll_d(Register rd, Address src){ ++ assert(src.index() == NOREG, "index is unimplemented"); ++ ll_d(rd, src.base(), src.disp()); ++} ++ ++void Assembler::ld_w(Register rd, Address src){ ++ Register dst = rd; ++ Register base = src.base(); ++ Register index = src.index(); ++ ++ int scale = src.scale(); ++ int disp = src.disp(); ++ ++ if (index != noreg) { ++ if (is_simm(disp, 12)) { ++ if (scale == 0) { ++ if (disp == 0) { ++ ldx_w(dst, base, index); ++ } else { ++ add_d(AT, base, index); ++ ld_w(dst, AT, disp); ++ } ++ } else { ++ alsl_d(AT, index, base, scale - 1); ++ ld_w(dst, AT, disp); ++ } ++ } else if (is_simm(disp, 16) && !(disp & 3)) { ++ if (scale == 0) { ++ add_d(AT, base, index); ++ } else { ++ alsl_d(AT, index, base, scale - 1); ++ } ++ ldptr_w(dst, AT, disp); ++ } else { ++ lu12i_w(AT, split_low20(disp >> 12)); ++ if (split_low12(disp)) ++ ori(AT, AT, split_low12(disp)); ++ ++ if (scale == 0) { ++ add_d(AT, AT, index); ++ } else { ++ alsl_d(AT, index, AT, scale - 1); ++ } ++ ldx_w(dst, base, AT); ++ } ++ } else { ++ if (is_simm(disp, 12)) { ++ ld_w(dst, base, disp); ++ } else if (is_simm(disp, 16) && !(disp & 3)) { ++ ldptr_w(dst, base, disp); ++ } else { ++ lu12i_w(AT, split_low20(disp >> 12)); ++ if (split_low12(disp)) ++ ori(AT, AT, split_low12(disp)); ++ ldx_w(dst, base, AT); ++ } ++ } ++//Disassembler::decode(pc()-32, pc(), tty); ++} ++ ++void Assembler::ld_wu(Register rd, Address src){ ++ Register dst = rd; ++ Register base = src.base(); ++ Register index = src.index(); ++ ++ int scale = src.scale(); ++ int disp = src.disp(); ++ ++ if (index != noreg) { ++ if (is_simm(disp, 12)) { ++ if (scale == 0) { ++ if (disp == 0) { ++ ldx_wu(dst, base, index); ++ } else { ++ add_d(AT, base, index); ++ ld_wu(dst, AT, disp); ++ } ++ } else { ++ alsl_d(AT, index, base, scale - 1); ++ ld_wu(dst, AT, disp); ++ } ++ } else { ++ lu12i_w(AT, split_low20(disp >> 12)); ++ if (split_low12(disp)) ++ ori(AT, AT, split_low12(disp)); ++ ++ if (scale == 0) { ++ add_d(AT, AT, index); ++ } else { ++ alsl_d(AT, index, AT, scale - 1); ++ } ++ ldx_wu(dst, base, AT); ++ } ++ } else { ++ if (is_simm(disp, 12)) { ++ ld_wu(dst, base, disp); ++ } else { ++ lu12i_w(AT, split_low20(disp >> 12)); ++ if (split_low12(disp)) ++ ori(AT, AT, split_low12(disp)); ++ ldx_wu(dst, base, AT); ++ } ++ } ++} ++ ++void Assembler::st_b(Register rd, Address dst) { ++ Register src = rd; ++ Register base = dst.base(); ++ Register index = dst.index(); ++ ++ int scale = dst.scale(); ++ int disp = dst.disp(); ++ ++ if (index != noreg) { ++ assert_different_registers(src, AT); ++ if (is_simm(disp, 12)) { ++ if (scale == 0) { ++ if (disp == 0) { ++ stx_b(src, base, index); ++ } else { ++ add_d(AT, base, index); ++ st_b(src, AT, disp); ++ } ++ } else { ++ alsl_d(AT, index, base, scale - 1); ++ st_b(src, AT, disp); ++ } ++ } else { ++ lu12i_w(AT, split_low20(disp >> 12)); ++ if (split_low12(disp)) ++ ori(AT, AT, split_low12(disp)); ++ ++ if (scale == 0) { ++ add_d(AT, AT, index); ++ } else { ++ alsl_d(AT, index, AT, scale - 1); ++ } ++ stx_b(src, base, AT); ++ } ++ } else { ++ if (is_simm(disp, 12)) { ++ st_b(src, base, disp); ++ } else { ++ assert_different_registers(src, AT); ++ lu12i_w(AT, split_low20(disp >> 12)); ++ if (split_low12(disp)) ++ ori(AT, AT, split_low12(disp)); ++ stx_b(src, base, AT); ++ } ++ } ++} ++ ++void Assembler::sc_w(Register rd, Address dst) { ++ assert(dst.index() == NOREG, "index is unimplemented"); ++ sc_w(rd, dst.base(), dst.disp()); ++} ++ ++void Assembler::sc_d(Register rd, Address dst) { ++ assert(dst.index() == NOREG, "index is unimplemented"); ++ sc_d(rd, dst.base(), dst.disp()); ++} ++ ++void Assembler::st_d(Register rd, Address dst) { ++ Register src = rd; ++ Register base = dst.base(); ++ Register index = dst.index(); ++ ++ int scale = dst.scale(); ++ int disp = dst.disp(); ++ ++ if (index != noreg) { ++ assert_different_registers(src, AT); ++ if (is_simm(disp, 12)) { ++ if (scale == 0) { ++ if (disp == 0) { ++ stx_d(src, base, index); ++ } else { ++ add_d(AT, base, index); ++ st_d(src, AT, disp); ++ } ++ } else { ++ alsl_d(AT, index, base, scale - 1); ++ st_d(src, AT, disp); ++ } ++ } else if (is_simm(disp, 16) && !(disp & 3)) { ++ if (scale == 0) { ++ add_d(AT, base, index); ++ } else { ++ alsl_d(AT, index, base, scale - 1); ++ } ++ stptr_d(src, AT, disp); ++ } else { ++ lu12i_w(AT, split_low20(disp >> 12)); ++ if (split_low12(disp)) ++ ori(AT, AT, split_low12(disp)); ++ ++ if (scale == 0) { ++ add_d(AT, AT, index); ++ } else { ++ alsl_d(AT, index, AT, scale - 1); ++ } ++ stx_d(src, base, AT); ++ } ++ } else { ++ if (is_simm(disp, 12)) { ++ st_d(src, base, disp); ++ } else if (is_simm(disp, 16) && !(disp & 3)) { ++ stptr_d(src, base, disp); ++ } else { ++ assert_different_registers(src, AT); ++ lu12i_w(AT, split_low20(disp >> 12)); ++ if (split_low12(disp)) ++ ori(AT, AT, split_low12(disp)); ++ stx_d(src, base, AT); ++ } ++ } ++} ++ ++void Assembler::st_h(Register rd, Address dst) { ++ Register src = rd; ++ Register base = dst.base(); ++ Register index = dst.index(); ++ ++ int scale = dst.scale(); ++ int disp = dst.disp(); ++ ++ if (index != noreg) { ++ assert_different_registers(src, AT); ++ if (is_simm(disp, 12)) { ++ if (scale == 0) { ++ if (disp == 0) { ++ stx_h(src, base, index); ++ } else { ++ add_d(AT, base, index); ++ st_h(src, AT, disp); ++ } ++ } else { ++ alsl_d(AT, index, base, scale - 1); ++ st_h(src, AT, disp); ++ } ++ } else { ++ lu12i_w(AT, split_low20(disp >> 12)); ++ if (split_low12(disp)) ++ ori(AT, AT, split_low12(disp)); ++ ++ if (scale == 0) { ++ add_d(AT, AT, index); ++ } else { ++ alsl_d(AT, index, AT, scale - 1); ++ } ++ stx_h(src, base, AT); ++ } ++ } else { ++ if (is_simm(disp, 12)) { ++ st_h(src, base, disp); ++ } else { ++ assert_different_registers(src, AT); ++ lu12i_w(AT, split_low20(disp >> 12)); ++ if (split_low12(disp)) ++ ori(AT, AT, split_low12(disp)); ++ stx_h(src, base, AT); ++ } ++ } ++} ++ ++void Assembler::st_w(Register rd, Address dst) { ++ Register src = rd; ++ Register base = dst.base(); ++ Register index = dst.index(); ++ ++ int scale = dst.scale(); ++ int disp = dst.disp(); ++ ++ if (index != noreg) { ++ assert_different_registers(src, AT); ++ if (is_simm(disp, 12)) { ++ if (scale == 0) { ++ if (disp == 0) { ++ stx_w(src, base, index); ++ } else { ++ add_d(AT, base, index); ++ st_w(src, AT, disp); ++ } ++ } else { ++ alsl_d(AT, index, base, scale - 1); ++ st_w(src, AT, disp); ++ } ++ } else if (is_simm(disp, 16) && !(disp & 3)) { ++ if (scale == 0) { ++ add_d(AT, base, index); ++ } else { ++ alsl_d(AT, index, base, scale - 1); ++ } ++ stptr_w(src, AT, disp); ++ } else { ++ lu12i_w(AT, split_low20(disp >> 12)); ++ if (split_low12(disp)) ++ ori(AT, AT, split_low12(disp)); ++ ++ if (scale == 0) { ++ add_d(AT, AT, index); ++ } else { ++ alsl_d(AT, index, AT, scale - 1); ++ } ++ stx_w(src, base, AT); ++ } ++ } else { ++ if (is_simm(disp, 12)) { ++ st_w(src, base, disp); ++ } else if (is_simm(disp, 16) && !(disp & 3)) { ++ stptr_w(src, base, disp); ++ } else { ++ assert_different_registers(src, AT); ++ lu12i_w(AT, split_low20(disp >> 12)); ++ if (split_low12(disp)) ++ ori(AT, AT, split_low12(disp)); ++ stx_w(src, base, AT); ++ } ++ } ++} ++ ++void Assembler::fld_s(FloatRegister fd, Address src) { ++ Register base = src.base(); ++ Register index = src.index(); ++ ++ int scale = src.scale(); ++ int disp = src.disp(); ++ ++ if (index != noreg) { ++ if (is_simm(disp, 12)) { ++ if (scale == 0) { ++ if (disp == 0) { ++ fldx_s(fd, base, index); ++ } else { ++ add_d(AT, base, index); ++ fld_s(fd, AT, disp); ++ } ++ } else { ++ alsl_d(AT, index, base, scale - 1); ++ fld_s(fd, AT, disp); ++ } ++ } else { ++ lu12i_w(AT, split_low20(disp >> 12)); ++ if (split_low12(disp)) ++ ori(AT, AT, split_low12(disp)); ++ ++ if (scale == 0) { ++ add_d(AT, AT, index); ++ } else { ++ alsl_d(AT, index, AT, scale - 1); ++ } ++ fldx_s(fd, base, AT); ++ } ++ } else { ++ if (is_simm(disp, 12)) { ++ fld_s(fd, base, disp); ++ } else { ++ lu12i_w(AT, split_low20(disp >> 12)); ++ if (split_low12(disp)) ++ ori(AT, AT, split_low12(disp)); ++ fldx_s(fd, base, AT); ++ } ++ } ++} ++ ++void Assembler::fld_d(FloatRegister fd, Address src) { ++ Register base = src.base(); ++ Register index = src.index(); ++ ++ int scale = src.scale(); ++ int disp = src.disp(); ++ ++ if (index != noreg) { ++ if (is_simm(disp, 12)) { ++ if (scale == 0) { ++ if (disp == 0) { ++ fldx_d(fd, base, index); ++ } else { ++ add_d(AT, base, index); ++ fld_d(fd, AT, disp); ++ } ++ } else { ++ alsl_d(AT, index, base, scale - 1); ++ fld_d(fd, AT, disp); ++ } ++ } else { ++ lu12i_w(AT, split_low20(disp >> 12)); ++ if (split_low12(disp)) ++ ori(AT, AT, split_low12(disp)); ++ ++ if (scale == 0) { ++ add_d(AT, AT, index); ++ } else { ++ alsl_d(AT, index, AT, scale - 1); ++ } ++ fldx_d(fd, base, AT); ++ } ++ } else { ++ if (is_simm(disp, 12)) { ++ fld_d(fd, base, disp); ++ } else { ++ lu12i_w(AT, split_low20(disp >> 12)); ++ if (split_low12(disp)) ++ ori(AT, AT, split_low12(disp)); ++ fldx_d(fd, base, AT); ++ } ++ } ++} ++ ++void Assembler::fst_s(FloatRegister fd, Address dst) { ++ Register base = dst.base(); ++ Register index = dst.index(); ++ ++ int scale = dst.scale(); ++ int disp = dst.disp(); ++ ++ if (index != noreg) { ++ if (is_simm(disp, 12)) { ++ if (scale == 0) { ++ if (disp == 0) { ++ fstx_s(fd, base, index); ++ } else { ++ add_d(AT, base, index); ++ fst_s(fd, AT, disp); ++ } ++ } else { ++ alsl_d(AT, index, base, scale - 1); ++ fst_s(fd, AT, disp); ++ } ++ } else { ++ lu12i_w(AT, split_low20(disp >> 12)); ++ if (split_low12(disp)) ++ ori(AT, AT, split_low12(disp)); ++ ++ if (scale == 0) { ++ add_d(AT, AT, index); ++ } else { ++ alsl_d(AT, index, AT, scale - 1); ++ } ++ fstx_s(fd, base, AT); ++ } ++ } else { ++ if (is_simm(disp, 12)) { ++ fst_s(fd, base, disp); ++ } else { ++ lu12i_w(AT, split_low20(disp >> 12)); ++ if (split_low12(disp)) ++ ori(AT, AT, split_low12(disp)); ++ fstx_s(fd, base, AT); ++ } ++ } ++} ++ ++void Assembler::fst_d(FloatRegister fd, Address dst) { ++ Register base = dst.base(); ++ Register index = dst.index(); ++ ++ int scale = dst.scale(); ++ int disp = dst.disp(); ++ ++ if (index != noreg) { ++ if (is_simm(disp, 12)) { ++ if (scale == 0) { ++ if (disp == 0) { ++ fstx_d(fd, base, index); ++ } else { ++ add_d(AT, base, index); ++ fst_d(fd, AT, disp); ++ } ++ } else { ++ alsl_d(AT, index, base, scale - 1); ++ fst_d(fd, AT, disp); ++ } ++ } else { ++ lu12i_w(AT, split_low20(disp >> 12)); ++ if (split_low12(disp)) ++ ori(AT, AT, split_low12(disp)); ++ ++ if (scale == 0) { ++ add_d(AT, AT, index); ++ } else { ++ alsl_d(AT, index, AT, scale - 1); ++ } ++ fstx_d(fd, base, AT); ++ } ++ } else { ++ if (is_simm(disp, 12)) { ++ fst_d(fd, base, disp); ++ } else { ++ lu12i_w(AT, split_low20(disp >> 12)); ++ if (split_low12(disp)) ++ ori(AT, AT, split_low12(disp)); ++ fstx_d(fd, base, AT); ++ } ++ } ++} +diff --git a/hotspot/src/cpu/loongarch/vm/assembler_loongarch.hpp b/hotspot/src/cpu/loongarch/vm/assembler_loongarch.hpp +new file mode 100644 +index 0000000000..b81440a0ce +--- /dev/null ++++ b/hotspot/src/cpu/loongarch/vm/assembler_loongarch.hpp +@@ -0,0 +1,2799 @@ ++/* ++ * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2015, 2021, Loongson Technology. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ * ++ */ ++ ++#ifndef CPU_LOONGARCH_VM_ASSEMBLER_LOONGARCH_HPP ++#define CPU_LOONGARCH_VM_ASSEMBLER_LOONGARCH_HPP ++ ++#include "asm/register.hpp" ++ ++class BiasedLockingCounters; ++ ++ ++// Note: A register location is represented via a Register, not ++// via an address for efficiency & simplicity reasons. ++ ++class ArrayAddress; ++ ++class Address VALUE_OBJ_CLASS_SPEC { ++ public: ++ enum ScaleFactor { ++ no_scale = -1, ++ times_1 = 0, ++ times_2 = 1, ++ times_4 = 2, ++ times_8 = 3, ++ times_ptr = times_8 ++ }; ++ static ScaleFactor times(int size) { ++ assert(size >= 1 && size <= 8 && is_power_of_2(size), "bad scale size"); ++ if (size == 8) return times_8; ++ if (size == 4) return times_4; ++ if (size == 2) return times_2; ++ return times_1; ++ } ++ ++ private: ++ Register _base; ++ Register _index; ++ ScaleFactor _scale; ++ int _disp; ++ RelocationHolder _rspec; ++ ++ // Easily misused constructors make them private ++ Address(address loc, RelocationHolder spec); ++ Address(int disp, address loc, relocInfo::relocType rtype); ++ Address(int disp, address loc, RelocationHolder spec); ++ ++ public: ++ ++ // creation ++ Address() ++ : _base(noreg), ++ _index(noreg), ++ _scale(no_scale), ++ _disp(0) { ++ } ++ ++ // No default displacement otherwise Register can be implicitly ++ // converted to 0(Register) which is quite a different animal. ++ ++ Address(Register base, int disp = 0) ++ : _base(base), ++ _index(noreg), ++ _scale(no_scale), ++ _disp(disp) { ++ assert_different_registers(_base, AT); ++ } ++ ++ Address(Register base, Register index, ScaleFactor scale, int disp = 0) ++ : _base (base), ++ _index(index), ++ _scale(scale), ++ _disp (disp) { ++ assert(!index->is_valid() == (scale == Address::no_scale), "inconsistent address"); ++ assert_different_registers(_base, _index, AT); ++ } ++ ++ // The following two overloads are used in connection with the ++ // ByteSize type (see sizes.hpp). They simplify the use of ++ // ByteSize'd arguments in assembly code. Note that their equivalent ++ // for the optimized build are the member functions with int disp ++ // argument since ByteSize is mapped to an int type in that case. ++ // ++ // Note: DO NOT introduce similar overloaded functions for WordSize ++ // arguments as in the optimized mode, both ByteSize and WordSize ++ // are mapped to the same type and thus the compiler cannot make a ++ // distinction anymore (=> compiler errors). ++ ++#ifdef ASSERT ++ Address(Register base, ByteSize disp) ++ : _base(base), ++ _index(noreg), ++ _scale(no_scale), ++ _disp(in_bytes(disp)) { ++ assert_different_registers(_base, AT); ++ } ++ ++ Address(Register base, Register index, ScaleFactor scale, ByteSize disp) ++ : _base(base), ++ _index(index), ++ _scale(scale), ++ _disp(in_bytes(disp)) { ++ assert(!index->is_valid() == (scale == Address::no_scale), "inconsistent address"); ++ assert_different_registers(_base, _index, AT); ++ } ++#endif // ASSERT ++ ++ // accessors ++ bool uses(Register reg) const { return _base == reg || _index == reg; } ++ Register base() const { return _base; } ++ Register index() const { return _index; } ++ ScaleFactor scale() const { return _scale; } ++ int disp() const { return _disp; } ++ ++ static Address make_array(ArrayAddress); ++ ++ friend class Assembler; ++ friend class MacroAssembler; ++ friend class LIR_Assembler; // base/index/scale/disp ++}; ++ ++// Calling convention ++class Argument VALUE_OBJ_CLASS_SPEC { ++ public: ++ enum { ++ n_register_parameters = 8, // 8 integer registers used to pass parameters ++ n_float_register_parameters = 8 // 8 float registers used to pass parameters ++ }; ++}; ++ ++// ++// AddressLiteral has been split out from Address because operands of this type ++// need to be treated specially on 32bit vs. 64bit platforms. By splitting it out ++// the few instructions that need to deal with address literals are unique and the ++// MacroAssembler does not have to implement every instruction in the Assembler ++// in order to search for address literals that may need special handling depending ++// on the instruction and the platform. As small step on the way to merging i486/amd64 ++// directories. ++// ++class AddressLiteral VALUE_OBJ_CLASS_SPEC { ++ friend class ArrayAddress; ++ RelocationHolder _rspec; ++ // Typically we use AddressLiterals we want to use their rval ++ // However in some situations we want the lval (effect address) of the item. ++ // We provide a special factory for making those lvals. ++ bool _is_lval; ++ ++ // If the target is far we'll need to load the ea of this to ++ // a register to reach it. Otherwise if near we can do rip ++ // relative addressing. ++ ++ address _target; ++ ++ protected: ++ // creation ++ AddressLiteral() ++ : _is_lval(false), ++ _target(NULL) ++ {} ++ ++ public: ++ ++ ++ AddressLiteral(address target, relocInfo::relocType rtype); ++ ++ AddressLiteral(address target, RelocationHolder const& rspec) ++ : _rspec(rspec), ++ _is_lval(false), ++ _target(target) ++ {} ++ // 32-bit complains about a multiple declaration for int*. ++ AddressLiteral(intptr_t* addr, relocInfo::relocType rtype = relocInfo::none) ++ : _target((address) addr), ++ _rspec(rspec_from_rtype(rtype, (address) addr)) {} ++ ++ AddressLiteral addr() { ++ AddressLiteral ret = *this; ++ ret._is_lval = true; ++ return ret; ++ } ++ ++ ++ private: ++ ++ address target() { return _target; } ++ bool is_lval() { return _is_lval; } ++ ++ relocInfo::relocType reloc() const { return _rspec.type(); } ++ const RelocationHolder& rspec() const { return _rspec; } ++ ++ friend class Assembler; ++ friend class MacroAssembler; ++ friend class Address; ++ friend class LIR_Assembler; ++ RelocationHolder rspec_from_rtype(relocInfo::relocType rtype, address addr) { ++ switch (rtype) { ++ case relocInfo::external_word_type: ++ return external_word_Relocation::spec(addr); ++ case relocInfo::internal_word_type: ++ return internal_word_Relocation::spec(addr); ++ case relocInfo::opt_virtual_call_type: ++ return opt_virtual_call_Relocation::spec(); ++ case relocInfo::static_call_type: ++ return static_call_Relocation::spec(); ++ case relocInfo::runtime_call_type: ++ return runtime_call_Relocation::spec(); ++ case relocInfo::poll_type: ++ case relocInfo::poll_return_type: ++ return Relocation::spec_simple(rtype); ++ case relocInfo::none: ++ case relocInfo::oop_type: ++ // Oops are a special case. Normally they would be their own section ++ // but in cases like icBuffer they are literals in the code stream that ++ // we don't have a section for. We use none so that we get a literal address ++ // which is always patchable. ++ return RelocationHolder(); ++ default: ++ ShouldNotReachHere(); ++ return RelocationHolder(); ++ } ++ } ++ ++}; ++ ++// Convience classes ++class RuntimeAddress: public AddressLiteral { ++ ++ public: ++ ++ RuntimeAddress(address target) : AddressLiteral(target, relocInfo::runtime_call_type) {} ++ ++}; ++ ++class OopAddress: public AddressLiteral { ++ ++ public: ++ ++ OopAddress(address target) : AddressLiteral(target, relocInfo::oop_type){} ++ ++}; ++ ++class ExternalAddress: public AddressLiteral { ++ ++ public: ++ ++ ExternalAddress(address target) : AddressLiteral(target, relocInfo::external_word_type){} ++ ++}; ++ ++class InternalAddress: public AddressLiteral { ++ ++ public: ++ ++ InternalAddress(address target) : AddressLiteral(target, relocInfo::internal_word_type) {} ++ ++}; ++ ++// x86 can do array addressing as a single operation since disp can be an absolute ++// address amd64 can't. We create a class that expresses the concept but does extra ++// magic on amd64 to get the final result ++ ++class ArrayAddress VALUE_OBJ_CLASS_SPEC { ++ private: ++ ++ AddressLiteral _base; ++ Address _index; ++ ++ public: ++ ++ ArrayAddress() {}; ++ ArrayAddress(AddressLiteral base, Address index): _base(base), _index(index) {}; ++ AddressLiteral base() { return _base; } ++ Address index() { return _index; } ++ ++}; ++ ++// The LoongArch Assembler: Pure assembler doing NO optimizations on the instruction ++// level ; i.e., what you write is what you get. The Assembler is generating code into ++// a CodeBuffer. ++ ++class Assembler : public AbstractAssembler { ++ friend class AbstractAssembler; // for the non-virtual hack ++ friend class LIR_Assembler; // as_Address() ++ friend class StubGenerator; ++ ++ public: ++ // 22-bit opcode, highest 22 bits: bits[31...10] ++ enum ops22 { ++ clo_w_op = 0b0000000000000000000100, ++ clz_w_op = 0b0000000000000000000101, ++ cto_w_op = 0b0000000000000000000110, ++ ctz_w_op = 0b0000000000000000000111, ++ clo_d_op = 0b0000000000000000001000, ++ clz_d_op = 0b0000000000000000001001, ++ cto_d_op = 0b0000000000000000001010, ++ ctz_d_op = 0b0000000000000000001011, ++ revb_2h_op = 0b0000000000000000001100, ++ revb_4h_op = 0b0000000000000000001101, ++ revb_2w_op = 0b0000000000000000001110, ++ revb_d_op = 0b0000000000000000001111, ++ revh_2w_op = 0b0000000000000000010000, ++ revh_d_op = 0b0000000000000000010001, ++ bitrev_4b_op = 0b0000000000000000010010, ++ bitrev_8b_op = 0b0000000000000000010011, ++ bitrev_w_op = 0b0000000000000000010100, ++ bitrev_d_op = 0b0000000000000000010101, ++ ext_w_h_op = 0b0000000000000000010110, ++ ext_w_b_op = 0b0000000000000000010111, ++ rdtimel_w_op = 0b0000000000000000011000, ++ rdtimeh_w_op = 0b0000000000000000011001, ++ rdtime_d_op = 0b0000000000000000011010, ++ cpucfg_op = 0b0000000000000000011011, ++ fabs_s_op = 0b0000000100010100000001, ++ fabs_d_op = 0b0000000100010100000010, ++ fneg_s_op = 0b0000000100010100000101, ++ fneg_d_op = 0b0000000100010100000110, ++ flogb_s_op = 0b0000000100010100001001, ++ flogb_d_op = 0b0000000100010100001010, ++ fclass_s_op = 0b0000000100010100001101, ++ fclass_d_op = 0b0000000100010100001110, ++ fsqrt_s_op = 0b0000000100010100010001, ++ fsqrt_d_op = 0b0000000100010100010010, ++ frecip_s_op = 0b0000000100010100010101, ++ frecip_d_op = 0b0000000100010100010110, ++ frsqrt_s_op = 0b0000000100010100011001, ++ frsqrt_d_op = 0b0000000100010100011010, ++ fmov_s_op = 0b0000000100010100100101, ++ fmov_d_op = 0b0000000100010100100110, ++ movgr2fr_w_op = 0b0000000100010100101001, ++ movgr2fr_d_op = 0b0000000100010100101010, ++ movgr2frh_w_op = 0b0000000100010100101011, ++ movfr2gr_s_op = 0b0000000100010100101101, ++ movfr2gr_d_op = 0b0000000100010100101110, ++ movfrh2gr_s_op = 0b0000000100010100101111, ++ movgr2fcsr_op = 0b0000000100010100110000, ++ movfcsr2gr_op = 0b0000000100010100110010, ++ movfr2cf_op = 0b0000000100010100110100, ++ movcf2fr_op = 0b0000000100010100110101, ++ movgr2cf_op = 0b0000000100010100110110, ++ movcf2gr_op = 0b0000000100010100110111, ++ fcvt_s_d_op = 0b0000000100011001000110, ++ fcvt_d_s_op = 0b0000000100011001001001, ++ ftintrm_w_s_op = 0b0000000100011010000001, ++ ftintrm_w_d_op = 0b0000000100011010000010, ++ ftintrm_l_s_op = 0b0000000100011010001001, ++ ftintrm_l_d_op = 0b0000000100011010001010, ++ ftintrp_w_s_op = 0b0000000100011010010001, ++ ftintrp_w_d_op = 0b0000000100011010010010, ++ ftintrp_l_s_op = 0b0000000100011010011001, ++ ftintrp_l_d_op = 0b0000000100011010011010, ++ ftintrz_w_s_op = 0b0000000100011010100001, ++ ftintrz_w_d_op = 0b0000000100011010100010, ++ ftintrz_l_s_op = 0b0000000100011010101001, ++ ftintrz_l_d_op = 0b0000000100011010101010, ++ ftintrne_w_s_op = 0b0000000100011010110001, ++ ftintrne_w_d_op = 0b0000000100011010110010, ++ ftintrne_l_s_op = 0b0000000100011010111001, ++ ftintrne_l_d_op = 0b0000000100011010111010, ++ ftint_w_s_op = 0b0000000100011011000001, ++ ftint_w_d_op = 0b0000000100011011000010, ++ ftint_l_s_op = 0b0000000100011011001001, ++ ftint_l_d_op = 0b0000000100011011001010, ++ ffint_s_w_op = 0b0000000100011101000100, ++ ffint_s_l_op = 0b0000000100011101000110, ++ ffint_d_w_op = 0b0000000100011101001000, ++ ffint_d_l_op = 0b0000000100011101001010, ++ frint_s_op = 0b0000000100011110010001, ++ frint_d_op = 0b0000000100011110010010, ++ iocsrrd_b_op = 0b0000011001001000000000, ++ iocsrrd_h_op = 0b0000011001001000000001, ++ iocsrrd_w_op = 0b0000011001001000000010, ++ iocsrrd_d_op = 0b0000011001001000000011, ++ iocsrwr_b_op = 0b0000011001001000000100, ++ iocsrwr_h_op = 0b0000011001001000000101, ++ iocsrwr_w_op = 0b0000011001001000000110, ++ iocsrwr_d_op = 0b0000011001001000000111, ++ vpcnt_b_op = 0b0111001010011100001000, ++ vpcnt_h_op = 0b0111001010011100001001, ++ vpcnt_w_op = 0b0111001010011100001010, ++ vpcnt_d_op = 0b0111001010011100001011, ++ vneg_b_op = 0b0111001010011100001100, ++ vneg_h_op = 0b0111001010011100001101, ++ vneg_w_op = 0b0111001010011100001110, ++ vneg_d_op = 0b0111001010011100001111, ++ vfclass_s_op = 0b0111001010011100110101, ++ vfclass_d_op = 0b0111001010011100110110, ++ vfsqrt_s_op = 0b0111001010011100111001, ++ vfsqrt_d_op = 0b0111001010011100111010, ++ vfrint_s_op = 0b0111001010011101001101, ++ vfrint_d_op = 0b0111001010011101001110, ++ vfrintrm_s_op = 0b0111001010011101010001, ++ vfrintrm_d_op = 0b0111001010011101010010, ++ vfrintrp_s_op = 0b0111001010011101010101, ++ vfrintrp_d_op = 0b0111001010011101010110, ++ vfrintrz_s_op = 0b0111001010011101011001, ++ vfrintrz_d_op = 0b0111001010011101011010, ++ vfrintrne_s_op = 0b0111001010011101011101, ++ vfrintrne_d_op = 0b0111001010011101011110, ++ vfcvtl_s_h_op = 0b0111001010011101111010, ++ vfcvth_s_h_op = 0b0111001010011101111011, ++ vfcvtl_d_s_op = 0b0111001010011101111100, ++ vfcvth_d_s_op = 0b0111001010011101111101, ++ vffint_s_w_op = 0b0111001010011110000000, ++ vffint_s_wu_op = 0b0111001010011110000001, ++ vffint_d_l_op = 0b0111001010011110000010, ++ vffint_d_lu_op = 0b0111001010011110000011, ++ vffintl_d_w_op = 0b0111001010011110000100, ++ vffinth_d_w_op = 0b0111001010011110000101, ++ vftint_w_s_op = 0b0111001010011110001100, ++ vftint_l_d_op = 0b0111001010011110001101, ++ vftintrm_w_s_op = 0b0111001010011110001110, ++ vftintrm_l_d_op = 0b0111001010011110001111, ++ vftintrp_w_s_op = 0b0111001010011110010000, ++ vftintrp_l_d_op = 0b0111001010011110010001, ++ vftintrz_w_s_op = 0b0111001010011110010010, ++ vftintrz_l_d_op = 0b0111001010011110010011, ++ vftintrne_w_s_op = 0b0111001010011110010100, ++ vftintrne_l_d_op = 0b0111001010011110010101, ++ vftint_wu_s = 0b0111001010011110010110, ++ vftint_lu_d = 0b0111001010011110010111, ++ vftintrz_wu_f = 0b0111001010011110011100, ++ vftintrz_lu_d = 0b0111001010011110011101, ++ vftintl_l_s_op = 0b0111001010011110100000, ++ vftinth_l_s_op = 0b0111001010011110100001, ++ vftintrml_l_s_op = 0b0111001010011110100010, ++ vftintrmh_l_s_op = 0b0111001010011110100011, ++ vftintrpl_l_s_op = 0b0111001010011110100100, ++ vftintrph_l_s_op = 0b0111001010011110100101, ++ vftintrzl_l_s_op = 0b0111001010011110100110, ++ vftintrzh_l_s_op = 0b0111001010011110100111, ++ vftintrnel_l_s_op = 0b0111001010011110101000, ++ vftintrneh_l_s_op = 0b0111001010011110101001, ++ vreplgr2vr_b_op = 0b0111001010011111000000, ++ vreplgr2vr_h_op = 0b0111001010011111000001, ++ vreplgr2vr_w_op = 0b0111001010011111000010, ++ vreplgr2vr_d_op = 0b0111001010011111000011, ++ xvpcnt_b_op = 0b0111011010011100001000, ++ xvpcnt_h_op = 0b0111011010011100001001, ++ xvpcnt_w_op = 0b0111011010011100001010, ++ xvpcnt_d_op = 0b0111011010011100001011, ++ xvneg_b_op = 0b0111011010011100001100, ++ xvneg_h_op = 0b0111011010011100001101, ++ xvneg_w_op = 0b0111011010011100001110, ++ xvneg_d_op = 0b0111011010011100001111, ++ xvfclass_s_op = 0b0111011010011100110101, ++ xvfclass_d_op = 0b0111011010011100110110, ++ xvfsqrt_s_op = 0b0111011010011100111001, ++ xvfsqrt_d_op = 0b0111011010011100111010, ++ xvfrint_s_op = 0b0111011010011101001101, ++ xvfrint_d_op = 0b0111011010011101001110, ++ xvfrintrm_s_op = 0b0111011010011101010001, ++ xvfrintrm_d_op = 0b0111011010011101010010, ++ xvfrintrp_s_op = 0b0111011010011101010101, ++ xvfrintrp_d_op = 0b0111011010011101010110, ++ xvfrintrz_s_op = 0b0111011010011101011001, ++ xvfrintrz_d_op = 0b0111011010011101011010, ++ xvfrintrne_s_op = 0b0111011010011101011101, ++ xvfrintrne_d_op = 0b0111011010011101011110, ++ xvfcvtl_s_h_op = 0b0111011010011101111010, ++ xvfcvth_s_h_op = 0b0111011010011101111011, ++ xvfcvtl_d_s_op = 0b0111011010011101111100, ++ xvfcvth_d_s_op = 0b0111011010011101111101, ++ xvffint_s_w_op = 0b0111011010011110000000, ++ xvffint_s_wu_op = 0b0111011010011110000001, ++ xvffint_d_l_op = 0b0111011010011110000010, ++ xvffint_d_lu_op = 0b0111011010011110000011, ++ xvffintl_d_w_op = 0b0111011010011110000100, ++ xvffinth_d_w_op = 0b0111011010011110000101, ++ xvftint_w_s_op = 0b0111011010011110001100, ++ xvftint_l_d_op = 0b0111011010011110001101, ++ xvftintrm_w_s_op = 0b0111011010011110001110, ++ xvftintrm_l_d_op = 0b0111011010011110001111, ++ xvftintrp_w_s_op = 0b0111011010011110010000, ++ xvftintrp_l_d_op = 0b0111011010011110010001, ++ xvftintrz_w_s_op = 0b0111011010011110010010, ++ xvftintrz_l_d_op = 0b0111011010011110010011, ++ xvftintrne_w_s_op = 0b0111011010011110010100, ++ xvftintrne_l_d_op = 0b0111011010011110010101, ++ xvftint_wu_s = 0b0111011010011110010110, ++ xvftint_lu_d = 0b0111011010011110010111, ++ xvftintrz_wu_f = 0b0111011010011110011100, ++ xvftintrz_lu_d = 0b0111011010011110011101, ++ xvftintl_l_s_op = 0b0111011010011110100000, ++ xvftinth_l_s_op = 0b0111011010011110100001, ++ xvftintrml_l_s_op = 0b0111011010011110100010, ++ xvftintrmh_l_s_op = 0b0111011010011110100011, ++ xvftintrpl_l_s_op = 0b0111011010011110100100, ++ xvftintrph_l_s_op = 0b0111011010011110100101, ++ xvftintrzl_l_s_op = 0b0111011010011110100110, ++ xvftintrzh_l_s_op = 0b0111011010011110100111, ++ xvftintrnel_l_s_op = 0b0111011010011110101000, ++ xvftintrneh_l_s_op = 0b0111011010011110101001, ++ xvreplgr2vr_b_op = 0b0111011010011111000000, ++ xvreplgr2vr_h_op = 0b0111011010011111000001, ++ xvreplgr2vr_w_op = 0b0111011010011111000010, ++ xvreplgr2vr_d_op = 0b0111011010011111000011, ++ vext2xv_h_b_op = 0b0111011010011111000100, ++ vext2xv_w_b_op = 0b0111011010011111000101, ++ vext2xv_d_b_op = 0b0111011010011111000110, ++ vext2xv_w_h_op = 0b0111011010011111000111, ++ vext2xv_d_h_op = 0b0111011010011111001000, ++ vext2xv_d_w_op = 0b0111011010011111001001, ++ vext2xv_hu_bu_op = 0b0111011010011111001010, ++ vext2xv_wu_bu_op = 0b0111011010011111001011, ++ vext2xv_du_bu_op = 0b0111011010011111001100, ++ vext2xv_wu_hu_op = 0b0111011010011111001101, ++ vext2xv_du_hu_op = 0b0111011010011111001110, ++ vext2xv_du_wu_op = 0b0111011010011111001111, ++ xvreplve0_b_op = 0b0111011100000111000000, ++ xvreplve0_h_op = 0b0111011100000111100000, ++ xvreplve0_w_op = 0b0111011100000111110000, ++ xvreplve0_d_op = 0b0111011100000111111000, ++ xvreplve0_q_op = 0b0111011100000111111100, ++ ++ unknow_ops22 = 0b1111111111111111111111 ++ }; ++ ++ // 21-bit opcode, highest 21 bits: bits[31...11] ++ enum ops21 { ++ vinsgr2vr_d_op = 0b011100101110101111110, ++ vpickve2gr_d_op = 0b011100101110111111110, ++ vpickve2gr_du_op = 0b011100101111001111110, ++ vreplvei_d_op = 0b011100101111011111110, ++ ++ unknow_ops21 = 0b111111111111111111111 ++ }; ++ ++ // 20-bit opcode, highest 20 bits: bits[31...12] ++ enum ops20 { ++ vinsgr2vr_w_op = 0b01110010111010111110, ++ vpickve2gr_w_op = 0b01110010111011111110, ++ vpickve2gr_wu_op = 0b01110010111100111110, ++ vreplvei_w_op = 0b01110010111101111110, ++ xvinsgr2vr_d_op = 0b01110110111010111110, ++ xvpickve2gr_d_op = 0b01110110111011111110, ++ xvpickve2gr_du_op = 0b01110110111100111110, ++ xvinsve0_d_op = 0b01110110111111111110, ++ xvpickve_d_op = 0b01110111000000111110, ++ ++ unknow_ops20 = 0b11111111111111111111 ++ }; ++ ++ // 19-bit opcode, highest 19 bits: bits[31...13] ++ enum ops19 { ++ vrotri_b_op = 0b0111001010100000001, ++ vinsgr2vr_h_op = 0b0111001011101011110, ++ vpickve2gr_h_op = 0b0111001011101111110, ++ vpickve2gr_hu_op = 0b0111001011110011110, ++ vreplvei_h_op = 0b0111001011110111110, ++ vbitclri_b_op = 0b0111001100010000001, ++ vbitseti_b_op = 0b0111001100010100001, ++ vbitrevi_b_op = 0b0111001100011000001, ++ vslli_b_op = 0b0111001100101100001, ++ vsrli_b_op = 0b0111001100110000001, ++ vsrai_b_op = 0b0111001100110100001, ++ xvrotri_b_op = 0b0111011010100000001, ++ xvinsgr2vr_w_op = 0b0111011011101011110, ++ xvpickve2gr_w_op = 0b0111011011101111110, ++ xvpickve2gr_wu_op = 0b0111011011110011110, ++ xvinsve0_w_op = 0b0111011011111111110, ++ xvpickve_w_op = 0b0111011100000011110, ++ xvbitclri_b_op = 0b0111011100010000001, ++ xvbitseti_b_op = 0b0111011100010100001, ++ xvbitrevi_b_op = 0b0111011100011000001, ++ xvslli_b_op = 0b0111011100101100001, ++ xvsrli_b_op = 0b0111011100110000001, ++ xvsrai_b_op = 0b0111011100110100001, ++ ++ unknow_ops19 = 0b1111111111111111111 ++ }; ++ ++ // 18-bit opcode, highest 18 bits: bits[31...14] ++ enum ops18 { ++ vrotri_h_op = 0b011100101010000001, ++ vinsgr2vr_b_op = 0b011100101110101110, ++ vpickve2gr_b_op = 0b011100101110111110, ++ vpickve2gr_bu_op = 0b011100101111001110, ++ vreplvei_b_op = 0b011100101111011110, ++ vbitclri_h_op = 0b011100110001000001, ++ vbitseti_h_op = 0b011100110001010001, ++ vbitrevi_h_op = 0b011100110001100001, ++ vslli_h_op = 0b011100110010110001, ++ vsrli_h_op = 0b011100110011000001, ++ vsrai_h_op = 0b011100110011010001, ++ vsrlni_b_h_op = 0b011100110100000001, ++ xvrotri_h_op = 0b011101101010000001, ++ xvbitclri_h_op = 0b011101110001000001, ++ xvbitseti_h_op = 0b011101110001010001, ++ xvbitrevi_h_op = 0b011101110001100001, ++ xvslli_h_op = 0b011101110010110001, ++ xvsrli_h_op = 0b011101110011000001, ++ xvsrai_h_op = 0b011101110011010001, ++ ++ unknow_ops18 = 0b111111111111111111 ++ }; ++ ++ // 17-bit opcode, highest 17 bits: bits[31...15] ++ enum ops17 { ++ asrtle_d_op = 0b00000000000000010, ++ asrtgt_d_op = 0b00000000000000011, ++ add_w_op = 0b00000000000100000, ++ add_d_op = 0b00000000000100001, ++ sub_w_op = 0b00000000000100010, ++ sub_d_op = 0b00000000000100011, ++ slt_op = 0b00000000000100100, ++ sltu_op = 0b00000000000100101, ++ maskeqz_op = 0b00000000000100110, ++ masknez_op = 0b00000000000100111, ++ nor_op = 0b00000000000101000, ++ and_op = 0b00000000000101001, ++ or_op = 0b00000000000101010, ++ xor_op = 0b00000000000101011, ++ orn_op = 0b00000000000101100, ++ andn_op = 0b00000000000101101, ++ sll_w_op = 0b00000000000101110, ++ srl_w_op = 0b00000000000101111, ++ sra_w_op = 0b00000000000110000, ++ sll_d_op = 0b00000000000110001, ++ srl_d_op = 0b00000000000110010, ++ sra_d_op = 0b00000000000110011, ++ rotr_w_op = 0b00000000000110110, ++ rotr_d_op = 0b00000000000110111, ++ mul_w_op = 0b00000000000111000, ++ mulh_w_op = 0b00000000000111001, ++ mulh_wu_op = 0b00000000000111010, ++ mul_d_op = 0b00000000000111011, ++ mulh_d_op = 0b00000000000111100, ++ mulh_du_op = 0b00000000000111101, ++ mulw_d_w_op = 0b00000000000111110, ++ mulw_d_wu_op = 0b00000000000111111, ++ div_w_op = 0b00000000001000000, ++ mod_w_op = 0b00000000001000001, ++ div_wu_op = 0b00000000001000010, ++ mod_wu_op = 0b00000000001000011, ++ div_d_op = 0b00000000001000100, ++ mod_d_op = 0b00000000001000101, ++ div_du_op = 0b00000000001000110, ++ mod_du_op = 0b00000000001000111, ++ crc_w_b_w_op = 0b00000000001001000, ++ crc_w_h_w_op = 0b00000000001001001, ++ crc_w_w_w_op = 0b00000000001001010, ++ crc_w_d_w_op = 0b00000000001001011, ++ crcc_w_b_w_op = 0b00000000001001100, ++ crcc_w_h_w_op = 0b00000000001001101, ++ crcc_w_w_w_op = 0b00000000001001110, ++ crcc_w_d_w_op = 0b00000000001001111, ++ break_op = 0b00000000001010100, ++ fadd_s_op = 0b00000001000000001, ++ fadd_d_op = 0b00000001000000010, ++ fsub_s_op = 0b00000001000000101, ++ fsub_d_op = 0b00000001000000110, ++ fmul_s_op = 0b00000001000001001, ++ fmul_d_op = 0b00000001000001010, ++ fdiv_s_op = 0b00000001000001101, ++ fdiv_d_op = 0b00000001000001110, ++ fmax_s_op = 0b00000001000010001, ++ fmax_d_op = 0b00000001000010010, ++ fmin_s_op = 0b00000001000010101, ++ fmin_d_op = 0b00000001000010110, ++ fmaxa_s_op = 0b00000001000011001, ++ fmaxa_d_op = 0b00000001000011010, ++ fmina_s_op = 0b00000001000011101, ++ fmina_d_op = 0b00000001000011110, ++ fscaleb_s_op = 0b00000001000100001, ++ fscaleb_d_op = 0b00000001000100010, ++ fcopysign_s_op = 0b00000001000100101, ++ fcopysign_d_op = 0b00000001000100110, ++ ldx_b_op = 0b00111000000000000, ++ ldx_h_op = 0b00111000000001000, ++ ldx_w_op = 0b00111000000010000, ++ ldx_d_op = 0b00111000000011000, ++ stx_b_op = 0b00111000000100000, ++ stx_h_op = 0b00111000000101000, ++ stx_w_op = 0b00111000000110000, ++ stx_d_op = 0b00111000000111000, ++ ldx_bu_op = 0b00111000001000000, ++ ldx_hu_op = 0b00111000001001000, ++ ldx_wu_op = 0b00111000001010000, ++ fldx_s_op = 0b00111000001100000, ++ fldx_d_op = 0b00111000001101000, ++ fstx_s_op = 0b00111000001110000, ++ fstx_d_op = 0b00111000001111000, ++ vldx_op = 0b00111000010000000, ++ vstx_op = 0b00111000010001000, ++ xvldx_op = 0b00111000010010000, ++ xvstx_op = 0b00111000010011000, ++ amswap_w_op = 0b00111000011000000, ++ amswap_d_op = 0b00111000011000001, ++ amadd_w_op = 0b00111000011000010, ++ amadd_d_op = 0b00111000011000011, ++ amand_w_op = 0b00111000011000100, ++ amand_d_op = 0b00111000011000101, ++ amor_w_op = 0b00111000011000110, ++ amor_d_op = 0b00111000011000111, ++ amxor_w_op = 0b00111000011001000, ++ amxor_d_op = 0b00111000011001001, ++ ammax_w_op = 0b00111000011001010, ++ ammax_d_op = 0b00111000011001011, ++ ammin_w_op = 0b00111000011001100, ++ ammin_d_op = 0b00111000011001101, ++ ammax_wu_op = 0b00111000011001110, ++ ammax_du_op = 0b00111000011001111, ++ ammin_wu_op = 0b00111000011010000, ++ ammin_du_op = 0b00111000011010001, ++ amswap_db_w_op = 0b00111000011010010, ++ amswap_db_d_op = 0b00111000011010011, ++ amadd_db_w_op = 0b00111000011010100, ++ amadd_db_d_op = 0b00111000011010101, ++ amand_db_w_op = 0b00111000011010110, ++ amand_db_d_op = 0b00111000011010111, ++ amor_db_w_op = 0b00111000011011000, ++ amor_db_d_op = 0b00111000011011001, ++ amxor_db_w_op = 0b00111000011011010, ++ amxor_db_d_op = 0b00111000011011011, ++ ammax_db_w_op = 0b00111000011011100, ++ ammax_db_d_op = 0b00111000011011101, ++ ammin_db_w_op = 0b00111000011011110, ++ ammin_db_d_op = 0b00111000011011111, ++ ammax_db_wu_op = 0b00111000011100000, ++ ammax_db_du_op = 0b00111000011100001, ++ ammin_db_wu_op = 0b00111000011100010, ++ ammin_db_du_op = 0b00111000011100011, ++ dbar_op = 0b00111000011100100, ++ ibar_op = 0b00111000011100101, ++ fldgt_s_op = 0b00111000011101000, ++ fldgt_d_op = 0b00111000011101001, ++ fldle_s_op = 0b00111000011101010, ++ fldle_d_op = 0b00111000011101011, ++ fstgt_s_op = 0b00111000011101100, ++ fstgt_d_op = 0b00111000011101101, ++ fstle_s_op = 0b00111000011101110, ++ fstle_d_op = 0b00111000011101111, ++ ldgt_b_op = 0b00111000011110000, ++ ldgt_h_op = 0b00111000011110001, ++ ldgt_w_op = 0b00111000011110010, ++ ldgt_d_op = 0b00111000011110011, ++ ldle_b_op = 0b00111000011110100, ++ ldle_h_op = 0b00111000011110101, ++ ldle_w_op = 0b00111000011110110, ++ ldle_d_op = 0b00111000011110111, ++ stgt_b_op = 0b00111000011111000, ++ stgt_h_op = 0b00111000011111001, ++ stgt_w_op = 0b00111000011111010, ++ stgt_d_op = 0b00111000011111011, ++ stle_b_op = 0b00111000011111100, ++ stle_h_op = 0b00111000011111101, ++ stle_w_op = 0b00111000011111110, ++ stle_d_op = 0b00111000011111111, ++ vseq_b_op = 0b01110000000000000, ++ vseq_h_op = 0b01110000000000001, ++ vseq_w_op = 0b01110000000000010, ++ vseq_d_op = 0b01110000000000011, ++ vsle_b_op = 0b01110000000000100, ++ vsle_h_op = 0b01110000000000101, ++ vsle_w_op = 0b01110000000000110, ++ vsle_d_op = 0b01110000000000111, ++ vsle_bu_op = 0b01110000000001000, ++ vsle_hu_op = 0b01110000000001001, ++ vsle_wu_op = 0b01110000000001010, ++ vsle_du_op = 0b01110000000001011, ++ vslt_b_op = 0b01110000000001100, ++ vslt_h_op = 0b01110000000001101, ++ vslt_w_op = 0b01110000000001110, ++ vslt_d_op = 0b01110000000001111, ++ vslt_bu_op = 0b01110000000010000, ++ vslt_hu_op = 0b01110000000010001, ++ vslt_wu_op = 0b01110000000010010, ++ vslt_du_op = 0b01110000000010011, ++ vadd_b_op = 0b01110000000010100, ++ vadd_h_op = 0b01110000000010101, ++ vadd_w_op = 0b01110000000010110, ++ vadd_d_op = 0b01110000000010111, ++ vsub_b_op = 0b01110000000011000, ++ vsub_h_op = 0b01110000000011001, ++ vsub_w_op = 0b01110000000011010, ++ vsub_d_op = 0b01110000000011011, ++ vabsd_b_op = 0b01110000011000000, ++ vabsd_h_op = 0b01110000011000001, ++ vabsd_w_op = 0b01110000011000010, ++ vabsd_d_op = 0b01110000011000011, ++ vmax_b_op = 0b01110000011100000, ++ vmax_h_op = 0b01110000011100001, ++ vmax_w_op = 0b01110000011100010, ++ vmax_d_op = 0b01110000011100011, ++ vmin_b_op = 0b01110000011100100, ++ vmin_h_op = 0b01110000011100101, ++ vmin_w_op = 0b01110000011100110, ++ vmin_d_op = 0b01110000011100111, ++ vmul_b_op = 0b01110000100001000, ++ vmul_h_op = 0b01110000100001001, ++ vmul_w_op = 0b01110000100001010, ++ vmul_d_op = 0b01110000100001011, ++ vmuh_b_op = 0b01110000100001100, ++ vmuh_h_op = 0b01110000100001101, ++ vmuh_w_op = 0b01110000100001110, ++ vmuh_d_op = 0b01110000100001111, ++ vmuh_bu_op = 0b01110000100010000, ++ vmuh_hu_op = 0b01110000100010001, ++ vmuh_wu_op = 0b01110000100010010, ++ vmuh_du_op = 0b01110000100010011, ++ vmulwev_h_b_op = 0b01110000100100000, ++ vmulwev_w_h_op = 0b01110000100100001, ++ vmulwev_d_w_op = 0b01110000100100010, ++ vmulwev_q_d_op = 0b01110000100100011, ++ vmulwod_h_b_op = 0b01110000100100100, ++ vmulwod_w_h_op = 0b01110000100100101, ++ vmulwod_d_w_op = 0b01110000100100110, ++ vmulwod_q_d_op = 0b01110000100100111, ++ vmadd_b_op = 0b01110000101010000, ++ vmadd_h_op = 0b01110000101010001, ++ vmadd_w_op = 0b01110000101010010, ++ vmadd_d_op = 0b01110000101010011, ++ vmsub_b_op = 0b01110000101010100, ++ vmsub_h_op = 0b01110000101010101, ++ vmsub_w_op = 0b01110000101010110, ++ vmsub_d_op = 0b01110000101010111, ++ vsll_b_op = 0b01110000111010000, ++ vsll_h_op = 0b01110000111010001, ++ vsll_w_op = 0b01110000111010010, ++ vsll_d_op = 0b01110000111010011, ++ vsrl_b_op = 0b01110000111010100, ++ vsrl_h_op = 0b01110000111010101, ++ vsrl_w_op = 0b01110000111010110, ++ vsrl_d_op = 0b01110000111010111, ++ vsra_b_op = 0b01110000111011000, ++ vsra_h_op = 0b01110000111011001, ++ vsra_w_op = 0b01110000111011010, ++ vsra_d_op = 0b01110000111011011, ++ vrotr_b_op = 0b01110000111011100, ++ vrotr_h_op = 0b01110000111011101, ++ vrotr_w_op = 0b01110000111011110, ++ vrotr_d_op = 0b01110000111011111, ++ vbitclr_b_op = 0b01110001000011000, ++ vbitclr_h_op = 0b01110001000011001, ++ vbitclr_w_op = 0b01110001000011010, ++ vbitclr_d_op = 0b01110001000011011, ++ vbitset_b_op = 0b01110001000011100, ++ vbitset_h_op = 0b01110001000011101, ++ vbitset_w_op = 0b01110001000011110, ++ vbitset_d_op = 0b01110001000011111, ++ vbitrev_b_op = 0b01110001000100000, ++ vbitrev_h_op = 0b01110001000100001, ++ vbitrev_w_op = 0b01110001000100010, ++ vbitrev_d_op = 0b01110001000100011, ++ vand_v_op = 0b01110001001001100, ++ vor_v_op = 0b01110001001001101, ++ vxor_v_op = 0b01110001001001110, ++ vnor_v_op = 0b01110001001001111, ++ vandn_v_op = 0b01110001001010000, ++ vorn_v_op = 0b01110001001010001, ++ vadd_q_op = 0b01110001001011010, ++ vsub_q_op = 0b01110001001011011, ++ vfadd_s_op = 0b01110001001100001, ++ vfadd_d_op = 0b01110001001100010, ++ vfsub_s_op = 0b01110001001100101, ++ vfsub_d_op = 0b01110001001100110, ++ vfmul_s_op = 0b01110001001110001, ++ vfmul_d_op = 0b01110001001110010, ++ vfdiv_s_op = 0b01110001001110101, ++ vfdiv_d_op = 0b01110001001110110, ++ vfmax_s_op = 0b01110001001111001, ++ vfmax_d_op = 0b01110001001111010, ++ vfmin_s_op = 0b01110001001111101, ++ vfmin_d_op = 0b01110001001111110, ++ vfcvt_h_s_op = 0b01110001010001100, ++ vfcvt_s_d_op = 0b01110001010001101, ++ vffint_s_l_op = 0b01110001010010000, ++ vftint_w_d_op = 0b01110001010010011, ++ vftintrm_w_d_op = 0b01110001010010100, ++ vftintrp_w_d_op = 0b01110001010010101, ++ vftintrz_w_d_op = 0b01110001010010110, ++ vftintrne_w_d_op = 0b01110001010010111, ++ vshuf_h_op = 0b01110001011110101, ++ vshuf_w_op = 0b01110001011110110, ++ vshuf_d_op = 0b01110001011110111, ++ vslti_bu_op = 0b01110010100010000, ++ vslti_hu_op = 0b01110010100010001, ++ vslti_wu_op = 0b01110010100010010, ++ vslti_du_op = 0b01110010100010011, ++ vaddi_bu_op = 0b01110010100010100, ++ vaddi_hu_op = 0b01110010100010101, ++ vaddi_wu_op = 0b01110010100010110, ++ vaddi_du_op = 0b01110010100010111, ++ vsubi_bu_op = 0b01110010100011000, ++ vsubi_hu_op = 0b01110010100011001, ++ vsubi_wu_op = 0b01110010100011010, ++ vsubi_du_op = 0b01110010100011011, ++ vrotri_w_op = 0b01110010101000001, ++ vbitclri_w_op = 0b01110011000100001, ++ vbitseti_w_op = 0b01110011000101001, ++ vbitrevi_w_op = 0b01110011000110001, ++ vslli_w_op = 0b01110011001011001, ++ vsrli_w_op = 0b01110011001100001, ++ vsrai_w_op = 0b01110011001101001, ++ vsrlni_h_w_op = 0b01110011010000001, ++ xvseq_b_op = 0b01110100000000000, ++ xvseq_h_op = 0b01110100000000001, ++ xvseq_w_op = 0b01110100000000010, ++ xvseq_d_op = 0b01110100000000011, ++ xvsle_b_op = 0b01110100000000100, ++ xvsle_h_op = 0b01110100000000101, ++ xvsle_w_op = 0b01110100000000110, ++ xvsle_d_op = 0b01110100000000111, ++ xvsle_bu_op = 0b01110100000001000, ++ xvsle_hu_op = 0b01110100000001001, ++ xvsle_wu_op = 0b01110100000001010, ++ xvsle_du_op = 0b01110100000001011, ++ xvslt_b_op = 0b01110100000001100, ++ xvslt_h_op = 0b01110100000001101, ++ xvslt_w_op = 0b01110100000001110, ++ xvslt_d_op = 0b01110100000001111, ++ xvslt_bu_op = 0b01110100000010000, ++ xvslt_hu_op = 0b01110100000010001, ++ xvslt_wu_op = 0b01110100000010010, ++ xvslt_du_op = 0b01110100000010011, ++ xvadd_b_op = 0b01110100000010100, ++ xvadd_h_op = 0b01110100000010101, ++ xvadd_w_op = 0b01110100000010110, ++ xvadd_d_op = 0b01110100000010111, ++ xvsub_b_op = 0b01110100000011000, ++ xvsub_h_op = 0b01110100000011001, ++ xvsub_w_op = 0b01110100000011010, ++ xvsub_d_op = 0b01110100000011011, ++ xvabsd_b_op = 0b01110100011000000, ++ xvabsd_h_op = 0b01110100011000001, ++ xvabsd_w_op = 0b01110100011000010, ++ xvabsd_d_op = 0b01110100011000011, ++ xvmax_b_op = 0b01110100011100000, ++ xvmax_h_op = 0b01110100011100001, ++ xvmax_w_op = 0b01110100011100010, ++ xvmax_d_op = 0b01110100011100011, ++ xvmin_b_op = 0b01110100011100100, ++ xvmin_h_op = 0b01110100011100101, ++ xvmin_w_op = 0b01110100011100110, ++ xvmin_d_op = 0b01110100011100111, ++ xvmul_b_op = 0b01110100100001000, ++ xvmul_h_op = 0b01110100100001001, ++ xvmul_w_op = 0b01110100100001010, ++ xvmul_d_op = 0b01110100100001011, ++ xvmuh_b_op = 0b01110100100001100, ++ xvmuh_h_op = 0b01110100100001101, ++ xvmuh_w_op = 0b01110100100001110, ++ xvmuh_d_op = 0b01110100100001111, ++ xvmuh_bu_op = 0b01110100100010000, ++ xvmuh_hu_op = 0b01110100100010001, ++ xvmuh_wu_op = 0b01110100100010010, ++ xvmuh_du_op = 0b01110100100010011, ++ xvmulwev_h_b_op = 0b01110100100100000, ++ xvmulwev_w_h_op = 0b01110100100100001, ++ xvmulwev_d_w_op = 0b01110100100100010, ++ xvmulwev_q_d_op = 0b01110100100100011, ++ xvmulwod_h_b_op = 0b01110100100100100, ++ xvmulwod_w_h_op = 0b01110100100100101, ++ xvmulwod_d_w_op = 0b01110100100100110, ++ xvmulwod_q_d_op = 0b01110100100100111, ++ xvmadd_b_op = 0b01110100101010000, ++ xvmadd_h_op = 0b01110100101010001, ++ xvmadd_w_op = 0b01110100101010010, ++ xvmadd_d_op = 0b01110100101010011, ++ xvmsub_b_op = 0b01110100101010100, ++ xvmsub_h_op = 0b01110100101010101, ++ xvmsub_w_op = 0b01110100101010110, ++ xvmsub_d_op = 0b01110100101010111, ++ xvsll_b_op = 0b01110100111010000, ++ xvsll_h_op = 0b01110100111010001, ++ xvsll_w_op = 0b01110100111010010, ++ xvsll_d_op = 0b01110100111010011, ++ xvsrl_b_op = 0b01110100111010100, ++ xvsrl_h_op = 0b01110100111010101, ++ xvsrl_w_op = 0b01110100111010110, ++ xvsrl_d_op = 0b01110100111010111, ++ xvsra_b_op = 0b01110100111011000, ++ xvsra_h_op = 0b01110100111011001, ++ xvsra_w_op = 0b01110100111011010, ++ xvsra_d_op = 0b01110100111011011, ++ xvrotr_b_op = 0b01110100111011100, ++ xvrotr_h_op = 0b01110100111011101, ++ xvrotr_w_op = 0b01110100111011110, ++ xvrotr_d_op = 0b01110100111011111, ++ xvbitclr_b_op = 0b01110101000011000, ++ xvbitclr_h_op = 0b01110101000011001, ++ xvbitclr_w_op = 0b01110101000011010, ++ xvbitclr_d_op = 0b01110101000011011, ++ xvbitset_b_op = 0b01110101000011100, ++ xvbitset_h_op = 0b01110101000011101, ++ xvbitset_w_op = 0b01110101000011110, ++ xvbitset_d_op = 0b01110101000011111, ++ xvbitrev_b_op = 0b01110101000100000, ++ xvbitrev_h_op = 0b01110101000100001, ++ xvbitrev_w_op = 0b01110101000100010, ++ xvbitrev_d_op = 0b01110101000100011, ++ xvand_v_op = 0b01110101001001100, ++ xvor_v_op = 0b01110101001001101, ++ xvxor_v_op = 0b01110101001001110, ++ xvnor_v_op = 0b01110101001001111, ++ xvandn_v_op = 0b01110101001010000, ++ xvorn_v_op = 0b01110101001010001, ++ xvadd_q_op = 0b01110101001011010, ++ xvsub_q_op = 0b01110101001011011, ++ xvfadd_s_op = 0b01110101001100001, ++ xvfadd_d_op = 0b01110101001100010, ++ xvfsub_s_op = 0b01110101001100101, ++ xvfsub_d_op = 0b01110101001100110, ++ xvfmul_s_op = 0b01110101001110001, ++ xvfmul_d_op = 0b01110101001110010, ++ xvfdiv_s_op = 0b01110101001110101, ++ xvfdiv_d_op = 0b01110101001110110, ++ xvfmax_s_op = 0b01110101001111001, ++ xvfmax_d_op = 0b01110101001111010, ++ xvfmin_s_op = 0b01110101001111101, ++ xvfmin_d_op = 0b01110101001111110, ++ xvfcvt_h_s_op = 0b01110101010001100, ++ xvfcvt_s_d_op = 0b01110101010001101, ++ xvffint_s_l_op = 0b01110101010010000, ++ xvftint_w_d_op = 0b01110101010010011, ++ xvftintrm_w_d_op = 0b01110101010010100, ++ xvftintrp_w_d_op = 0b01110101010010101, ++ xvftintrz_w_d_op = 0b01110101010010110, ++ xvftintrne_w_d_op = 0b01110101010010111, ++ xvshuf_h_op = 0b01110101011110101, ++ xvshuf_w_op = 0b01110101011110110, ++ xvshuf_d_op = 0b01110101011110111, ++ xvperm_w_op = 0b01110101011111010, ++ xvslti_bu_op = 0b01110110100010000, ++ xvslti_hu_op = 0b01110110100010001, ++ xvslti_wu_op = 0b01110110100010010, ++ xvslti_du_op = 0b01110110100010011, ++ xvaddi_bu_op = 0b01110110100010100, ++ xvaddi_hu_op = 0b01110110100010101, ++ xvaddi_wu_op = 0b01110110100010110, ++ xvaddi_du_op = 0b01110110100010111, ++ xvsubi_bu_op = 0b01110110100011000, ++ xvsubi_hu_op = 0b01110110100011001, ++ xvsubi_wu_op = 0b01110110100011010, ++ xvsubi_du_op = 0b01110110100011011, ++ xvrotri_w_op = 0b01110110101000001, ++ xvbitclri_w_op = 0b01110111000100001, ++ xvbitseti_w_op = 0b01110111000101001, ++ xvbitrevi_w_op = 0b01110111000110001, ++ xvslli_w_op = 0b01110111001011001, ++ xvsrli_w_op = 0b01110111001100001, ++ xvsrai_w_op = 0b01110111001101001, ++ ++ unknow_ops17 = 0b11111111111111111 ++ }; ++ ++ // 16-bit opcode, highest 16 bits: bits[31...16] ++ enum ops16 { ++ vrotri_d_op = 0b0111001010100001, ++ vbitclri_d_op = 0b0111001100010001, ++ vbitseti_d_op = 0b0111001100010101, ++ vbitrevi_d_op = 0b0111001100011001, ++ vslli_d_op = 0b0111001100101101, ++ vsrli_d_op = 0b0111001100110001, ++ vsrai_d_op = 0b0111001100110101, ++ vsrlni_w_d_op = 0b0111001101000001, ++ xvrotri_d_op = 0b0111011010100001, ++ xvbitclri_d_op = 0b0111011100010001, ++ xvbitseti_d_op = 0b0111011100010101, ++ xvbitrevi_d_op = 0b0111011100011001, ++ xvslli_d_op = 0b0111011100101101, ++ xvsrli_d_op = 0b0111011100110001, ++ xvsrai_d_op = 0b0111011100110101, ++ ++ unknow_ops16 = 0b1111111111111111 ++ }; ++ ++ // 15-bit opcode, highest 15 bits: bits[31...17] ++ enum ops15 { ++ vsrlni_d_q_op = 0b011100110100001, ++ ++ unknow_ops15 = 0b111111111111111 ++ }; ++ ++ // 14-bit opcode, highest 14 bits: bits[31...18] ++ enum ops14 { ++ alsl_w_op = 0b00000000000001, ++ bytepick_w_op = 0b00000000000010, ++ bytepick_d_op = 0b00000000000011, ++ alsl_d_op = 0b00000000001011, ++ slli_op = 0b00000000010000, ++ srli_op = 0b00000000010001, ++ srai_op = 0b00000000010010, ++ rotri_op = 0b00000000010011, ++ lddir_op = 0b00000110010000, ++ ldpte_op = 0b00000110010001, ++ vshuf4i_b_op = 0b01110011100100, ++ vshuf4i_h_op = 0b01110011100101, ++ vshuf4i_w_op = 0b01110011100110, ++ vshuf4i_d_op = 0b01110011100111, ++ vandi_b_op = 0b01110011110100, ++ vori_b_op = 0b01110011110101, ++ vxori_b_op = 0b01110011110110, ++ vnori_b_op = 0b01110011110111, ++ vldi_op = 0b01110011111000, ++ vpermi_w_op = 0b01110011111001, ++ xvshuf4i_b_op = 0b01110111100100, ++ xvshuf4i_h_op = 0b01110111100101, ++ xvshuf4i_w_op = 0b01110111100110, ++ xvshuf4i_d_op = 0b01110111100111, ++ xvandi_b_op = 0b01110111110100, ++ xvori_b_op = 0b01110111110101, ++ xvxori_b_op = 0b01110111110110, ++ xvnori_b_op = 0b01110111110111, ++ xvldi_op = 0b01110111111000, ++ xvpermi_w_op = 0b01110111111001, ++ xvpermi_d_op = 0b01110111111010, ++ xvpermi_q_op = 0b01110111111011, ++ ++ unknow_ops14 = 0b11111111111111 ++ }; ++ ++ // 12-bit opcode, highest 12 bits: bits[31...20] ++ enum ops12 { ++ fmadd_s_op = 0b000010000001, ++ fmadd_d_op = 0b000010000010, ++ fmsub_s_op = 0b000010000101, ++ fmsub_d_op = 0b000010000110, ++ fnmadd_s_op = 0b000010001001, ++ fnmadd_d_op = 0b000010001010, ++ fnmsub_s_op = 0b000010001101, ++ fnmsub_d_op = 0b000010001110, ++ vfmadd_s_op = 0b000010010001, ++ vfmadd_d_op = 0b000010010010, ++ vfmsub_s_op = 0b000010010101, ++ vfmsub_d_op = 0b000010010110, ++ vfnmadd_s_op = 0b000010011001, ++ vfnmadd_d_op = 0b000010011010, ++ vfnmsub_s_op = 0b000010011101, ++ vfnmsub_d_op = 0b000010011110, ++ xvfmadd_s_op = 0b000010100001, ++ xvfmadd_d_op = 0b000010100010, ++ xvfmsub_s_op = 0b000010100101, ++ xvfmsub_d_op = 0b000010100110, ++ xvfnmadd_s_op = 0b000010101001, ++ xvfnmadd_d_op = 0b000010101010, ++ xvfnmsub_s_op = 0b000010101101, ++ xvfnmsub_d_op = 0b000010101110, ++ fcmp_cond_s_op = 0b000011000001, ++ fcmp_cond_d_op = 0b000011000010, ++ vfcmp_cond_s_op = 0b000011000101, ++ vfcmp_cond_d_op = 0b000011000110, ++ xvfcmp_cond_s_op = 0b000011001001, ++ xvfcmp_cond_d_op = 0b000011001010, ++ fsel_op = 0b000011010000, ++ vbitsel_v_op = 0b000011010001, ++ xvbitsel_v_op = 0b000011010010, ++ vshuf_b_op = 0b000011010101, ++ xvshuf_b_op = 0b000011010110, ++ ++ unknow_ops12 = 0b111111111111 ++ }; ++ ++ // 10-bit opcode, highest 10 bits: bits[31...22] ++ enum ops10 { ++ bstr_w_op = 0b0000000001, ++ bstrins_d_op = 0b0000000010, ++ bstrpick_d_op = 0b0000000011, ++ slti_op = 0b0000001000, ++ sltui_op = 0b0000001001, ++ addi_w_op = 0b0000001010, ++ addi_d_op = 0b0000001011, ++ lu52i_d_op = 0b0000001100, ++ andi_op = 0b0000001101, ++ ori_op = 0b0000001110, ++ xori_op = 0b0000001111, ++ ld_b_op = 0b0010100000, ++ ld_h_op = 0b0010100001, ++ ld_w_op = 0b0010100010, ++ ld_d_op = 0b0010100011, ++ st_b_op = 0b0010100100, ++ st_h_op = 0b0010100101, ++ st_w_op = 0b0010100110, ++ st_d_op = 0b0010100111, ++ ld_bu_op = 0b0010101000, ++ ld_hu_op = 0b0010101001, ++ ld_wu_op = 0b0010101010, ++ preld_op = 0b0010101011, ++ fld_s_op = 0b0010101100, ++ fst_s_op = 0b0010101101, ++ fld_d_op = 0b0010101110, ++ fst_d_op = 0b0010101111, ++ vld_op = 0b0010110000, ++ vst_op = 0b0010110001, ++ xvld_op = 0b0010110010, ++ xvst_op = 0b0010110011, ++ ldl_w_op = 0b0010111000, ++ ldr_w_op = 0b0010111001, ++ ++ unknow_ops10 = 0b1111111111 ++ }; ++ ++ // 8-bit opcode, highest 8 bits: bits[31...22] ++ enum ops8 { ++ ll_w_op = 0b00100000, ++ sc_w_op = 0b00100001, ++ ll_d_op = 0b00100010, ++ sc_d_op = 0b00100011, ++ ldptr_w_op = 0b00100100, ++ stptr_w_op = 0b00100101, ++ ldptr_d_op = 0b00100110, ++ stptr_d_op = 0b00100111, ++ ++ unknow_ops8 = 0b11111111 ++ }; ++ ++ // 7-bit opcode, highest 7 bits: bits[31...25] ++ enum ops7 { ++ lu12i_w_op = 0b0001010, ++ lu32i_d_op = 0b0001011, ++ pcaddi_op = 0b0001100, ++ pcalau12i_op = 0b0001101, ++ pcaddu12i_op = 0b0001110, ++ pcaddu18i_op = 0b0001111, ++ ++ unknow_ops7 = 0b1111111 ++ }; ++ ++ // 6-bit opcode, highest 6 bits: bits[31...25] ++ enum ops6 { ++ addu16i_d_op = 0b000100, ++ beqz_op = 0b010000, ++ bnez_op = 0b010001, ++ bccondz_op = 0b010010, ++ jirl_op = 0b010011, ++ b_op = 0b010100, ++ bl_op = 0b010101, ++ beq_op = 0b010110, ++ bne_op = 0b010111, ++ blt_op = 0b011000, ++ bge_op = 0b011001, ++ bltu_op = 0b011010, ++ bgeu_op = 0b011011, ++ ++ unknow_ops6 = 0b111111 ++ }; ++ ++ enum fcmp_cond { ++ fcmp_caf = 0x00, ++ fcmp_cun = 0x08, ++ fcmp_ceq = 0x04, ++ fcmp_cueq = 0x0c, ++ fcmp_clt = 0x02, ++ fcmp_cult = 0x0a, ++ fcmp_cle = 0x06, ++ fcmp_cule = 0x0e, ++ fcmp_cne = 0x10, ++ fcmp_cor = 0x14, ++ fcmp_cune = 0x18, ++ fcmp_saf = 0x01, ++ fcmp_sun = 0x09, ++ fcmp_seq = 0x05, ++ fcmp_sueq = 0x0d, ++ fcmp_slt = 0x03, ++ fcmp_sult = 0x0b, ++ fcmp_sle = 0x07, ++ fcmp_sule = 0x0f, ++ fcmp_sne = 0x11, ++ fcmp_sor = 0x15, ++ fcmp_sune = 0x19 ++ }; ++ ++ enum Condition { ++ zero , ++ notZero , ++ equal , ++ notEqual , ++ less , ++ lessEqual , ++ greater , ++ greaterEqual , ++ below , ++ belowEqual , ++ above , ++ aboveEqual ++ }; ++ ++ static const int LogInstructionSize = 2; ++ static const int InstructionSize = 1 << LogInstructionSize; ++ ++ enum WhichOperand { ++ // input to locate_operand, and format code for relocations ++ imm_operand = 0, // embedded 32-bit|64-bit immediate operand ++ disp32_operand = 1, // embedded 32-bit displacement or address ++ call32_operand = 2, // embedded 32-bit self-relative displacement ++ narrow_oop_operand = 3, // embedded 32-bit immediate narrow oop ++ _WhichOperand_limit = 4 ++ }; ++ ++ static int low (int x, int l) { return bitfield(x, 0, l); } ++ static int low16(int x) { return low(x, 16); } ++ static int low26(int x) { return low(x, 26); } ++ ++ static int high (int x, int l) { return bitfield(x, 32-l, l); } ++ static int high16(int x) { return high(x, 16); } ++ static int high6 (int x) { return high(x, 6); } ++ ++ ++ protected: ++ // help methods for instruction ejection ++ ++ // 2R-type ++ // 31 10 9 5 4 0 ++ // | opcode | rj | rd | ++ static inline int insn_RR (int op, int rj, int rd) { return (op<<10) | (rj<<5) | rd; } ++ ++ // 3R-type ++ // 31 15 14 10 9 5 4 0 ++ // | opcode | rk | rj | rd | ++ static inline int insn_RRR (int op, int rk, int rj, int rd) { return (op<<15) | (rk<<10) | (rj<<5) | rd; } ++ ++ // 4R-type ++ // 31 20 19 15 14 10 9 5 4 0 ++ // | opcode | ra | rk | rj | rd | ++ static inline int insn_RRRR (int op, int ra, int rk, int rj, int rd) { return (op<<20) | (ra << 15) | (rk<<10) | (rj<<5) | rd; } ++ ++ // 2RI1-type ++ // 31 11 10 9 5 4 0 ++ // | opcode | I1 | vj | rd | ++ static inline int insn_I1RR (int op, int ui1, int vj, int rd) { assert(is_uimm(ui1, 1), "not a unsigned 1-bit int"); return (op<<11) | (low(ui1, 1)<<10) | (vj<<5) | rd; } ++ ++ // 2RI2-type ++ // 31 12 11 10 9 5 4 0 ++ // | opcode | I2 | vj | rd | ++ static inline int insn_I2RR (int op, int ui2, int vj, int rd) { assert(is_uimm(ui2, 2), "not a unsigned 2-bit int"); return (op<<12) | (low(ui2, 2)<<10) | (vj<<5) | rd; } ++ ++ // 2RI3-type ++ // 31 13 12 10 9 5 4 0 ++ // | opcode | I3 | vj | vd | ++ static inline int insn_I3RR (int op, int ui3, int vj, int vd) { assert(is_uimm(ui3, 3), "not a unsigned 3-bit int"); return (op<<13) | (low(ui3, 3)<<10) | (vj<<5) | vd; } ++ ++ // 2RI4-type ++ // 31 14 13 10 9 5 4 0 ++ // | opcode | I4 | vj | vd | ++ static inline int insn_I4RR (int op, int ui4, int vj, int vd) { assert(is_uimm(ui4, 4), "not a unsigned 4-bit int"); return (op<<14) | (low(ui4, 4)<<10) | (vj<<5) | vd; } ++ ++ // 2RI5-type ++ // 31 15 14 10 9 5 4 0 ++ // | opcode | I5 | vj | vd | ++ static inline int insn_I5RR (int op, int ui5, int vj, int vd) { assert(is_uimm(ui5, 5), "not a unsigned 5-bit int"); return (op<<15) | (low(ui5, 5)<<10) | (vj<<5) | vd; } ++ ++ // 2RI6-type ++ // 31 16 15 10 9 5 4 0 ++ // | opcode | I6 | vj | vd | ++ static inline int insn_I6RR (int op, int ui6, int vj, int vd) { assert(is_uimm(ui6, 6), "not a unsigned 6-bit int"); return (op<<16) | (low(ui6, 6)<<10) | (vj<<5) | vd; } ++ ++ // 2RI7-type ++ // 31 17 16 10 9 5 4 0 ++ // | opcode | I7 | vj | vd | ++ static inline int insn_I7RR (int op, int ui7, int vj, int vd) { assert(is_uimm(ui7, 7), "not a unsigned 7-bit int"); return (op<<17) | (low(ui7, 6)<<10) | (vj<<5) | vd; } ++ ++ // 2RI8-type ++ // 31 18 17 10 9 5 4 0 ++ // | opcode | I8 | rj | rd | ++ static inline int insn_I8RR (int op, int imm8, int rj, int rd) { /*assert(is_simm(imm8, 8), "not a signed 8-bit int");*/ return (op<<18) | (low(imm8, 8)<<10) | (rj<<5) | rd; } ++ ++ // 2RI12-type ++ // 31 22 21 10 9 5 4 0 ++ // | opcode | I12 | rj | rd | ++ static inline int insn_I12RR(int op, int imm12, int rj, int rd) { /* assert(is_simm(imm12, 12), "not a signed 12-bit int");*/ return (op<<22) | (low(imm12, 12)<<10) | (rj<<5) | rd; } ++ ++ ++ // 2RI14-type ++ // 31 24 23 10 9 5 4 0 ++ // | opcode | I14 | rj | rd | ++ static inline int insn_I14RR(int op, int imm14, int rj, int rd) { assert(is_simm(imm14, 14), "not a signed 14-bit int"); return (op<<24) | (low(imm14, 14)<<10) | (rj<<5) | rd; } ++ ++ // 2RI16-type ++ // 31 26 25 10 9 5 4 0 ++ // | opcode | I16 | rj | rd | ++ static inline int insn_I16RR(int op, int imm16, int rj, int rd) { assert(is_simm16(imm16), "not a signed 16-bit int"); return (op<<26) | (low16(imm16)<<10) | (rj<<5) | rd; } ++ ++ // 1RI13-type (?) ++ // 31 18 17 5 4 0 ++ // | opcode | I13 | vd | ++ static inline int insn_I13R (int op, int imm13, int vd) { assert(is_simm(imm13, 13), "not a signed 13-bit int"); return (op<<18) | (low(imm13, 13)<<5) | vd; } ++ ++ // 1RI20-type (?) ++ // 31 25 24 5 4 0 ++ // | opcode | I20 | rd | ++ static inline int insn_I20R (int op, int imm20, int rd) { assert(is_simm(imm20, 20), "not a signed 20-bit int"); return (op<<25) | (low(imm20, 20)<<5) | rd; } ++ ++ // 1RI21-type ++ // 31 26 25 10 9 5 4 0 ++ // | opcode | I21[15:0] | rj |I21[20:16]| ++ static inline int insn_IRI(int op, int imm21, int rj) { assert(is_simm(imm21, 21), "not a signed 21-bit int"); return (op << 26) | (low16(imm21) << 10) | (rj << 5) | low(imm21 >> 16, 5); } ++ ++ // I26-type ++ // 31 26 25 10 9 0 ++ // | opcode | I26[15:0] | I26[25:16] | ++ static inline int insn_I26(int op, int imm26) { assert(is_simm(imm26, 26), "not a signed 26-bit int"); return (op << 26) | (low16(imm26) << 10) | low(imm26 >> 16, 10); } ++ ++ // imm15 ++ // 31 15 14 0 ++ // | opcode | I15 | ++ static inline int insn_I15 (int op, int imm15) { assert(is_uimm(imm15, 15), "not a unsigned 15-bit int"); return (op<<15) | low(imm15, 15); } ++ ++ ++ // get the offset field of beq, bne, blt[u], bge[u] instruction ++ int offset16(address entry) { ++ assert(is_simm16((entry - pc()) / 4), "change this code"); ++ if (!is_simm16((entry - pc()) / 4)) { ++ tty->print_cr("!!! is_simm16: %lx", (entry - pc()) / 4); ++ } ++ return (entry - pc()) / 4; ++ } ++ ++ // get the offset field of beqz, bnez instruction ++ int offset21(address entry) { ++ assert(is_simm((int)(entry - pc()) / 4, 21), "change this code"); ++ if (!is_simm((int)(entry - pc()) / 4, 21)) { ++ tty->print_cr("!!! is_simm21: %lx", (entry - pc()) / 4); ++ } ++ return (entry - pc()) / 4; ++ } ++ ++ // get the offset field of b instruction ++ int offset26(address entry) { ++ assert(is_simm((int)(entry - pc()) / 4, 26), "change this code"); ++ if (!is_simm((int)(entry - pc()) / 4, 26)) { ++ tty->print_cr("!!! is_simm26: %lx", (entry - pc()) / 4); ++ } ++ return (entry - pc()) / 4; ++ } ++ ++public: ++ using AbstractAssembler::offset; ++ ++ //sign expand with the sign bit is h ++ static int expand(int x, int h) { return -(x & (1<> 16; ++ } ++ ++ static int split_high16(int x) { ++ return ( (x >> 16) + ((x & 0x8000) != 0) ) & 0xffff; ++ } ++ ++ static int split_low20(int x) { ++ return (x & 0xfffff); ++ } ++ ++ // Convert 20-bit x to a sign-extended 20-bit integer ++ static int simm20(int x) { ++ assert(x == (x & 0xFFFFF), "must be 20-bit only"); ++ return (x << 12) >> 12; ++ } ++ ++ static int split_low12(int x) { ++ return (x & 0xfff); ++ } ++ ++ static inline void split_simm38(jlong si38, jint& si18, jint& si20) { ++ si18 = ((jint)(si38 & 0x3ffff) << 14) >> 14; ++ si38 += (si38 & 0x20000) << 1; ++ si20 = si38 >> 18; ++ } ++ ++ // Convert 12-bit x to a sign-extended 12-bit integer ++ static int simm12(int x) { ++ assert(x == (x & 0xFFF), "must be 12-bit only"); ++ return (x << 20) >> 20; ++ } ++ ++ // Convert 26-bit x to a sign-extended 26-bit integer ++ static int simm26(int x) { ++ assert(x == (x & 0x3FFFFFF), "must be 26-bit only"); ++ return (x << 6) >> 6; ++ } ++ ++ static intptr_t merge(intptr_t x0, intptr_t x12) { ++ //lu12i, ori ++ return (((x12 << 12) | x0) << 32) >> 32; ++ } ++ ++ static intptr_t merge(intptr_t x0, intptr_t x12, intptr_t x32) { ++ //lu32i, lu12i, ori ++ return (((x32 << 32) | (x12 << 12) | x0) << 12) >> 12; ++ } ++ ++ static intptr_t merge(intptr_t x0, intptr_t x12, intptr_t x32, intptr_t x52) { ++ //lu52i, lu32i, lu12i, ori ++ return (x52 << 52) | (x32 << 32) | (x12 << 12) | x0; ++ } ++ ++ // Test if x is within signed immediate range for nbits. ++ static bool is_simm (int x, unsigned int nbits) { ++ assert(0 < nbits && nbits < 32, "out of bounds"); ++ const int min = -( ((int)1) << nbits-1 ); ++ const int maxplus1 = ( ((int)1) << nbits-1 ); ++ return min <= x && x < maxplus1; ++ } ++ ++ static bool is_simm(jlong x, unsigned int nbits) { ++ assert(0 < nbits && nbits < 64, "out of bounds"); ++ const jlong min = -( ((jlong)1) << nbits-1 ); ++ const jlong maxplus1 = ( ((jlong)1) << nbits-1 ); ++ return min <= x && x < maxplus1; ++ } ++ ++ static bool is_simm16(int x) { return is_simm(x, 16); } ++ static bool is_simm16(long x) { return is_simm((jlong)x, (unsigned int)16); } ++ ++ // Test if x is within unsigned immediate range for nbits ++ static bool is_uimm(int x, unsigned int nbits) { ++ assert(0 < nbits && nbits < 32, "out of bounds"); ++ const int maxplus1 = ( ((int)1) << nbits ); ++ return 0 <= x && x < maxplus1; ++ } ++ ++ static bool is_uimm(jlong x, unsigned int nbits) { ++ assert(0 < nbits && nbits < 64, "out of bounds"); ++ const jlong maxplus1 = ( ((jlong)1) << nbits ); ++ return 0 <= x && x < maxplus1; ++ } ++ ++public: ++ ++ void flush() { ++ AbstractAssembler::flush(); ++ } ++ ++ inline void emit_int32(int); ++ inline void emit_data(int x) { emit_int32(x); } ++ inline void emit_data(int, RelocationHolder const&); ++ inline void emit_data(int, relocInfo::relocType rtype); ++ ++ ++ // Generic instructions ++ // Does 32bit or 64bit as needed for the platform. In some sense these ++ // belong in macro assembler but there is no need for both varieties to exist ++ ++ void clo_w (Register rd, Register rj) { emit_int32(insn_RR(clo_w_op, (int)rj->encoding(), (int)rd->encoding())); } ++ void clz_w (Register rd, Register rj) { emit_int32(insn_RR(clz_w_op, (int)rj->encoding(), (int)rd->encoding())); } ++ void cto_w (Register rd, Register rj) { emit_int32(insn_RR(cto_w_op, (int)rj->encoding(), (int)rd->encoding())); } ++ void ctz_w (Register rd, Register rj) { emit_int32(insn_RR(ctz_w_op, (int)rj->encoding(), (int)rd->encoding())); } ++ void clo_d (Register rd, Register rj) { emit_int32(insn_RR(clo_d_op, (int)rj->encoding(), (int)rd->encoding())); } ++ void clz_d (Register rd, Register rj) { emit_int32(insn_RR(clz_d_op, (int)rj->encoding(), (int)rd->encoding())); } ++ void cto_d (Register rd, Register rj) { emit_int32(insn_RR(cto_d_op, (int)rj->encoding(), (int)rd->encoding())); } ++ void ctz_d (Register rd, Register rj) { emit_int32(insn_RR(ctz_d_op, (int)rj->encoding(), (int)rd->encoding())); } ++ ++ void revb_2h(Register rd, Register rj) { emit_int32(insn_RR(revb_2h_op, (int)rj->encoding(), (int)rd->encoding())); } ++ void revb_4h(Register rd, Register rj) { emit_int32(insn_RR(revb_4h_op, (int)rj->encoding(), (int)rd->encoding())); } ++ void revb_2w(Register rd, Register rj) { emit_int32(insn_RR(revb_2w_op, (int)rj->encoding(), (int)rd->encoding())); } ++ void revb_d (Register rd, Register rj) { emit_int32(insn_RR( revb_d_op, (int)rj->encoding(), (int)rd->encoding())); } ++ void revh_2w(Register rd, Register rj) { emit_int32(insn_RR(revh_2w_op, (int)rj->encoding(), (int)rd->encoding())); } ++ void revh_d (Register rd, Register rj) { emit_int32(insn_RR( revh_d_op, (int)rj->encoding(), (int)rd->encoding())); } ++ ++ void bitrev_4b(Register rd, Register rj) { emit_int32(insn_RR(bitrev_4b_op, (int)rj->encoding(), (int)rd->encoding())); } ++ void bitrev_8b(Register rd, Register rj) { emit_int32(insn_RR(bitrev_8b_op, (int)rj->encoding(), (int)rd->encoding())); } ++ void bitrev_w (Register rd, Register rj) { emit_int32(insn_RR(bitrev_w_op, (int)rj->encoding(), (int)rd->encoding())); } ++ void bitrev_d (Register rd, Register rj) { emit_int32(insn_RR(bitrev_d_op, (int)rj->encoding(), (int)rd->encoding())); } ++ ++ void ext_w_h(Register rd, Register rj) { emit_int32(insn_RR(ext_w_h_op, (int)rj->encoding(), (int)rd->encoding())); } ++ void ext_w_b(Register rd, Register rj) { emit_int32(insn_RR(ext_w_b_op, (int)rj->encoding(), (int)rd->encoding())); } ++ ++ void rdtimel_w(Register rd, Register rj) { emit_int32(insn_RR(rdtimel_w_op, (int)rj->encoding(), (int)rd->encoding())); } ++ void rdtimeh_w(Register rd, Register rj) { emit_int32(insn_RR(rdtimeh_w_op, (int)rj->encoding(), (int)rd->encoding())); } ++ void rdtime_d(Register rd, Register rj) { emit_int32(insn_RR(rdtime_d_op, (int)rj->encoding(), (int)rd->encoding())); } ++ ++ void cpucfg(Register rd, Register rj) { emit_int32(insn_RR(cpucfg_op, (int)rj->encoding(), (int)rd->encoding())); } ++ ++ void asrtle_d (Register rj, Register rk) { emit_int32(insn_RRR(asrtle_d_op , (int)rk->encoding(), (int)rj->encoding(), 0)); } ++ void asrtgt_d (Register rj, Register rk) { emit_int32(insn_RRR(asrtgt_d_op , (int)rk->encoding(), (int)rj->encoding(), 0)); } ++ ++ void alsl_w(Register rd, Register rj, Register rk, int sa2) { assert(is_uimm(sa2, 2), "not a unsigned 2-bit int"); emit_int32(insn_I8RR(alsl_w_op, ( (0 << 7) | (sa2 << 5) | (int)rk->encoding() ), (int)rj->encoding(), (int)rd->encoding())); } ++ void alsl_wu(Register rd, Register rj, Register rk, int sa2) { assert(is_uimm(sa2, 2), "not a unsigned 2-bit int"); emit_int32(insn_I8RR(alsl_w_op, ( (1 << 7) | (sa2 << 5) | (int)rk->encoding() ), (int)rj->encoding(), (int)rd->encoding())); } ++ void bytepick_w(Register rd, Register rj, Register rk, int sa2) { assert(is_uimm(sa2, 2), "not a unsigned 2-bit int"); emit_int32(insn_I8RR(bytepick_w_op, ( (0 << 7) | (sa2 << 5) | (int)rk->encoding() ), (int)rj->encoding(), (int)rd->encoding())); } ++ void bytepick_d(Register rd, Register rj, Register rk, int sa3) { assert(is_uimm(sa3, 3), "not a unsigned 3-bit int"); emit_int32(insn_I8RR(bytepick_d_op, ( (sa3 << 5) | (int)rk->encoding() ), (int)rj->encoding(), (int)rd->encoding())); } ++ ++ void add_w (Register rd, Register rj, Register rk) { emit_int32(insn_RRR(add_w_op, (int)rk->encoding(), (int)rj->encoding(), (int)rd->encoding())); } ++ void add_d (Register rd, Register rj, Register rk) { emit_int32(insn_RRR(add_d_op, (int)rk->encoding(), (int)rj->encoding(), (int)rd->encoding())); } ++ void sub_w (Register rd, Register rj, Register rk) { emit_int32(insn_RRR(sub_w_op, (int)rk->encoding(), (int)rj->encoding(), (int)rd->encoding())); } ++ void sub_d (Register rd, Register rj, Register rk) { emit_int32(insn_RRR(sub_d_op, (int)rk->encoding(), (int)rj->encoding(), (int)rd->encoding())); } ++ void slt (Register rd, Register rj, Register rk) { emit_int32(insn_RRR(slt_op, (int)rk->encoding(), (int)rj->encoding(), (int)rd->encoding())); } ++ void sltu (Register rd, Register rj, Register rk) { emit_int32(insn_RRR(sltu_op, (int)rk->encoding(), (int)rj->encoding(), (int)rd->encoding())); } ++ ++ void maskeqz (Register rd, Register rj, Register rk) { emit_int32(insn_RRR(maskeqz_op, (int)rk->encoding(), (int)rj->encoding(), (int)rd->encoding())); } ++ void masknez (Register rd, Register rj, Register rk) { emit_int32(insn_RRR(masknez_op, (int)rk->encoding(), (int)rj->encoding(), (int)rd->encoding())); } ++ ++ void nor (Register rd, Register rj, Register rk) { emit_int32(insn_RRR(nor_op, (int)rk->encoding(), (int)rj->encoding(), (int)rd->encoding())); } ++ void AND (Register rd, Register rj, Register rk) { emit_int32(insn_RRR(and_op, (int)rk->encoding(), (int)rj->encoding(), (int)rd->encoding())); } ++ void OR (Register rd, Register rj, Register rk) { emit_int32(insn_RRR(or_op, (int)rk->encoding(), (int)rj->encoding(), (int)rd->encoding())); } ++ void XOR (Register rd, Register rj, Register rk) { emit_int32(insn_RRR(xor_op, (int)rk->encoding(), (int)rj->encoding(), (int)rd->encoding())); } ++ void orn (Register rd, Register rj, Register rk) { emit_int32(insn_RRR(orn_op, (int)rk->encoding(), (int)rj->encoding(), (int)rd->encoding())); } ++ void andn(Register rd, Register rj, Register rk) { emit_int32(insn_RRR(andn_op, (int)rk->encoding(), (int)rj->encoding(), (int)rd->encoding())); } ++ ++ void sll_w (Register rd, Register rj, Register rk) { emit_int32(insn_RRR(sll_w_op, (int)rk->encoding(), (int)rj->encoding(), (int)rd->encoding())); } ++ void srl_w (Register rd, Register rj, Register rk) { emit_int32(insn_RRR(srl_w_op, (int)rk->encoding(), (int)rj->encoding(), (int)rd->encoding())); } ++ void sra_w (Register rd, Register rj, Register rk) { emit_int32(insn_RRR(sra_w_op, (int)rk->encoding(), (int)rj->encoding(), (int)rd->encoding())); } ++ void sll_d (Register rd, Register rj, Register rk) { emit_int32(insn_RRR(sll_d_op, (int)rk->encoding(), (int)rj->encoding(), (int)rd->encoding())); } ++ void srl_d (Register rd, Register rj, Register rk) { emit_int32(insn_RRR(srl_d_op, (int)rk->encoding(), (int)rj->encoding(), (int)rd->encoding())); } ++ void sra_d (Register rd, Register rj, Register rk) { emit_int32(insn_RRR(sra_d_op, (int)rk->encoding(), (int)rj->encoding(), (int)rd->encoding())); } ++ ++ void rotr_w (Register rd, Register rj, Register rk) { emit_int32(insn_RRR(rotr_w_op, (int)rk->encoding(), (int)rj->encoding(), (int)rd->encoding())); } ++ void rotr_d (Register rd, Register rj, Register rk) { emit_int32(insn_RRR(rotr_d_op, (int)rk->encoding(), (int)rj->encoding(), (int)rd->encoding())); } ++ ++ void mul_w (Register rd, Register rj, Register rk) { emit_int32(insn_RRR(mul_w_op, (int)rk->encoding(), (int)rj->encoding(), (int)rd->encoding())); } ++ void mulh_w (Register rd, Register rj, Register rk) { emit_int32(insn_RRR(mulh_w_op, (int)rk->encoding(), (int)rj->encoding(), (int)rd->encoding())); } ++ void mulh_wu (Register rd, Register rj, Register rk) { emit_int32(insn_RRR(mulh_wu_op, (int)rk->encoding(), (int)rj->encoding(), (int)rd->encoding())); } ++ void mul_d (Register rd, Register rj, Register rk) { emit_int32(insn_RRR(mul_d_op, (int)rk->encoding(), (int)rj->encoding(), (int)rd->encoding())); } ++ void mulh_d (Register rd, Register rj, Register rk) { emit_int32(insn_RRR(mulh_d_op, (int)rk->encoding(), (int)rj->encoding(), (int)rd->encoding())); } ++ void mulh_du (Register rd, Register rj, Register rk) { emit_int32(insn_RRR(mulh_du_op, (int)rk->encoding(), (int)rj->encoding(), (int)rd->encoding())); } ++ void mulw_d_w (Register rd, Register rj, Register rk) { emit_int32(insn_RRR(mulw_d_w_op, (int)rk->encoding(), (int)rj->encoding(), (int)rd->encoding())); } ++ void mulw_d_wu (Register rd, Register rj, Register rk) { emit_int32(insn_RRR(mulw_d_wu_op, (int)rk->encoding(), (int)rj->encoding(), (int)rd->encoding())); } ++ ++ void div_w (Register rd, Register rj, Register rk) { emit_int32(insn_RRR(div_w_op, (int)rk->encoding(), (int)rj->encoding(), (int)rd->encoding())); } ++ void mod_w (Register rd, Register rj, Register rk) { emit_int32(insn_RRR(mod_w_op, (int)rk->encoding(), (int)rj->encoding(), (int)rd->encoding())); } ++ void div_wu(Register rd, Register rj, Register rk) { emit_int32(insn_RRR(div_wu_op, (int)rk->encoding(), (int)rj->encoding(), (int)rd->encoding())); } ++ void mod_wu(Register rd, Register rj, Register rk) { emit_int32(insn_RRR(mod_wu_op, (int)rk->encoding(), (int)rj->encoding(), (int)rd->encoding())); } ++ void div_d (Register rd, Register rj, Register rk) { emit_int32(insn_RRR(div_d_op, (int)rk->encoding(), (int)rj->encoding(), (int)rd->encoding())); } ++ void mod_d (Register rd, Register rj, Register rk) { emit_int32(insn_RRR(mod_d_op, (int)rk->encoding(), (int)rj->encoding(), (int)rd->encoding())); } ++ void div_du(Register rd, Register rj, Register rk) { emit_int32(insn_RRR(div_du_op, (int)rk->encoding(), (int)rj->encoding(), (int)rd->encoding())); } ++ void mod_du(Register rd, Register rj, Register rk) { emit_int32(insn_RRR(mod_du_op, (int)rk->encoding(), (int)rj->encoding(), (int)rd->encoding())); } ++ ++ void crc_w_b_w (Register rd, Register rj, Register rk) { emit_int32(insn_RRR(crc_w_b_w_op, (int)rk->encoding(), (int)rj->encoding(), (int)rd->encoding())); } ++ void crc_w_h_w (Register rd, Register rj, Register rk) { emit_int32(insn_RRR(crc_w_h_w_op, (int)rk->encoding(), (int)rj->encoding(), (int)rd->encoding())); } ++ void crc_w_w_w (Register rd, Register rj, Register rk) { emit_int32(insn_RRR(crc_w_w_w_op, (int)rk->encoding(), (int)rj->encoding(), (int)rd->encoding())); } ++ void crc_w_d_w (Register rd, Register rj, Register rk) { emit_int32(insn_RRR(crc_w_d_w_op, (int)rk->encoding(), (int)rj->encoding(), (int)rd->encoding())); } ++ void crcc_w_b_w (Register rd, Register rj, Register rk) { emit_int32(insn_RRR(crcc_w_b_w_op, (int)rk->encoding(), (int)rj->encoding(), (int)rd->encoding())); } ++ void crcc_w_h_w (Register rd, Register rj, Register rk) { emit_int32(insn_RRR(crcc_w_h_w_op, (int)rk->encoding(), (int)rj->encoding(), (int)rd->encoding())); } ++ void crcc_w_w_w (Register rd, Register rj, Register rk) { emit_int32(insn_RRR(crcc_w_w_w_op, (int)rk->encoding(), (int)rj->encoding(), (int)rd->encoding())); } ++ void crcc_w_d_w (Register rd, Register rj, Register rk) { emit_int32(insn_RRR(crcc_w_d_w_op, (int)rk->encoding(), (int)rj->encoding(), (int)rd->encoding())); } ++ ++ void brk(int code) { assert(is_uimm(code, 15), "not a unsigned 15-bit int"); emit_int32(insn_I15(break_op, code)); } ++ ++ void alsl_d(Register rd, Register rj, Register rk, int sa2) { assert(is_uimm(sa2, 2), "not a unsigned 2-bit int"); emit_int32(insn_I8RR(alsl_d_op, ( (sa2 << 5) | (int)rk->encoding() ), (int)rj->encoding(), (int)rd->encoding())); } ++ ++ void slli_w(Register rd, Register rj, int ui5) { assert(is_uimm(ui5, 5), "not a unsigned 5-bit int"); emit_int32(insn_I8RR(slli_op, ( (0b001 << 5) | ui5 ), (int)rj->encoding(), (int)rd->encoding())); } ++ void slli_d(Register rd, Register rj, int ui6) { assert(is_uimm(ui6, 6), "not a unsigned 6-bit int"); emit_int32(insn_I8RR(slli_op, ( (0b01 << 6) | ui6 ), (int)rj->encoding(), (int)rd->encoding())); } ++ void srli_w(Register rd, Register rj, int ui5) { assert(is_uimm(ui5, 5), "not a unsigned 5-bit int"); emit_int32(insn_I8RR(srli_op, ( (0b001 << 5) | ui5 ), (int)rj->encoding(), (int)rd->encoding())); } ++ void srli_d(Register rd, Register rj, int ui6) { assert(is_uimm(ui6, 6), "not a unsigned 6-bit int"); emit_int32(insn_I8RR(srli_op, ( (0b01 << 6) | ui6 ), (int)rj->encoding(), (int)rd->encoding())); } ++ void srai_w(Register rd, Register rj, int ui5) { assert(is_uimm(ui5, 5), "not a unsigned 5-bit int"); emit_int32(insn_I8RR(srai_op, ( (0b001 << 5) | ui5 ), (int)rj->encoding(), (int)rd->encoding())); } ++ void srai_d(Register rd, Register rj, int ui6) { assert(is_uimm(ui6, 6), "not a unsigned 6-bit int"); emit_int32(insn_I8RR(srai_op, ( (0b01 << 6) | ui6 ), (int)rj->encoding(), (int)rd->encoding())); } ++ void rotri_w(Register rd, Register rj, int ui5) { assert(is_uimm(ui5, 5), "not a unsigned 5-bit int"); emit_int32(insn_I8RR(rotri_op, ( (0b001 << 5) | ui5 ), (int)rj->encoding(), (int)rd->encoding())); } ++ void rotri_d(Register rd, Register rj, int ui6) { assert(is_uimm(ui6, 6), "not a unsigned 6-bit int"); emit_int32(insn_I8RR(rotri_op, ( (0b01 << 6) | ui6 ), (int)rj->encoding(), (int)rd->encoding())); } ++ ++ void bstrins_w (Register rd, Register rj, int msbw, int lsbw) { assert(is_uimm(msbw, 5) && is_uimm(lsbw, 5), "not a unsigned 5-bit int"); emit_int32(insn_I12RR(bstr_w_op, ( (1<<11) | (low(msbw, 5)<<6) | (0<<5) | low(lsbw, 5) ), (int)rj->encoding(), (int)rd->encoding())); } ++ void bstrpick_w (Register rd, Register rj, int msbw, int lsbw) { assert(is_uimm(msbw, 5) && is_uimm(lsbw, 5), "not a unsigned 5-bit int"); emit_int32(insn_I12RR(bstr_w_op, ( (1<<11) | (low(msbw, 5)<<6) | (1<<5) | low(lsbw, 5) ), (int)rj->encoding(), (int)rd->encoding())); } ++ void bstrins_d (Register rd, Register rj, int msbd, int lsbd) { assert(is_uimm(msbd, 6) && is_uimm(lsbd, 6), "not a unsigned 6-bit int"); emit_int32(insn_I12RR(bstrins_d_op, ( (low(msbd, 6)<<6) | low(lsbd, 6) ), (int)rj->encoding(), (int)rd->encoding())); } ++ void bstrpick_d (Register rd, Register rj, int msbd, int lsbd) { assert(is_uimm(msbd, 6) && is_uimm(lsbd, 6), "not a unsigned 6-bit int"); emit_int32(insn_I12RR(bstrpick_d_op, ( (low(msbd, 6)<<6) | low(lsbd, 6) ), (int)rj->encoding(), (int)rd->encoding())); } ++ ++ void fadd_s (FloatRegister fd, FloatRegister fj, FloatRegister fk) { emit_int32(insn_RRR(fadd_s_op, (int)fk->encoding(), (int)fj->encoding(), (int)fd->encoding())); } ++ void fadd_d (FloatRegister fd, FloatRegister fj, FloatRegister fk) { emit_int32(insn_RRR(fadd_d_op, (int)fk->encoding(), (int)fj->encoding(), (int)fd->encoding())); } ++ void fsub_s (FloatRegister fd, FloatRegister fj, FloatRegister fk) { emit_int32(insn_RRR(fsub_s_op, (int)fk->encoding(), (int)fj->encoding(), (int)fd->encoding())); } ++ void fsub_d (FloatRegister fd, FloatRegister fj, FloatRegister fk) { emit_int32(insn_RRR(fsub_d_op, (int)fk->encoding(), (int)fj->encoding(), (int)fd->encoding())); } ++ void fmul_s (FloatRegister fd, FloatRegister fj, FloatRegister fk) { emit_int32(insn_RRR(fmul_s_op, (int)fk->encoding(), (int)fj->encoding(), (int)fd->encoding())); } ++ void fmul_d (FloatRegister fd, FloatRegister fj, FloatRegister fk) { emit_int32(insn_RRR(fmul_d_op, (int)fk->encoding(), (int)fj->encoding(), (int)fd->encoding())); } ++ void fdiv_s (FloatRegister fd, FloatRegister fj, FloatRegister fk) { emit_int32(insn_RRR(fdiv_s_op, (int)fk->encoding(), (int)fj->encoding(), (int)fd->encoding())); } ++ void fdiv_d (FloatRegister fd, FloatRegister fj, FloatRegister fk) { emit_int32(insn_RRR(fdiv_d_op, (int)fk->encoding(), (int)fj->encoding(), (int)fd->encoding())); } ++ void fmax_s (FloatRegister fd, FloatRegister fj, FloatRegister fk) { emit_int32(insn_RRR(fmax_s_op, (int)fk->encoding(), (int)fj->encoding(), (int)fd->encoding())); } ++ void fmax_d (FloatRegister fd, FloatRegister fj, FloatRegister fk) { emit_int32(insn_RRR(fmax_d_op, (int)fk->encoding(), (int)fj->encoding(), (int)fd->encoding())); } ++ void fmin_s (FloatRegister fd, FloatRegister fj, FloatRegister fk) { emit_int32(insn_RRR(fmin_s_op, (int)fk->encoding(), (int)fj->encoding(), (int)fd->encoding())); } ++ void fmin_d (FloatRegister fd, FloatRegister fj, FloatRegister fk) { emit_int32(insn_RRR(fmin_d_op, (int)fk->encoding(), (int)fj->encoding(), (int)fd->encoding())); } ++ void fmaxa_s (FloatRegister fd, FloatRegister fj, FloatRegister fk) { emit_int32(insn_RRR(fmaxa_s_op, (int)fk->encoding(), (int)fj->encoding(), (int)fd->encoding())); } ++ void fmaxa_d (FloatRegister fd, FloatRegister fj, FloatRegister fk) { emit_int32(insn_RRR(fmaxa_d_op, (int)fk->encoding(), (int)fj->encoding(), (int)fd->encoding())); } ++ void fmina_s (FloatRegister fd, FloatRegister fj, FloatRegister fk) { emit_int32(insn_RRR(fmina_s_op, (int)fk->encoding(), (int)fj->encoding(), (int)fd->encoding())); } ++ void fmina_d (FloatRegister fd, FloatRegister fj, FloatRegister fk) { emit_int32(insn_RRR(fmina_d_op, (int)fk->encoding(), (int)fj->encoding(), (int)fd->encoding())); } ++ ++ void fscaleb_s (FloatRegister fd, FloatRegister fj, FloatRegister fk) { emit_int32(insn_RRR(fscaleb_s_op, (int)fk->encoding(), (int)fj->encoding(), (int)fd->encoding())); } ++ void fscaleb_d (FloatRegister fd, FloatRegister fj, FloatRegister fk) { emit_int32(insn_RRR(fscaleb_d_op, (int)fk->encoding(), (int)fj->encoding(), (int)fd->encoding())); } ++ void fcopysign_s (FloatRegister fd, FloatRegister fj, FloatRegister fk) { emit_int32(insn_RRR(fcopysign_s_op, (int)fk->encoding(), (int)fj->encoding(), (int)fd->encoding())); } ++ void fcopysign_d (FloatRegister fd, FloatRegister fj, FloatRegister fk) { emit_int32(insn_RRR(fcopysign_d_op, (int)fk->encoding(), (int)fj->encoding(), (int)fd->encoding())); } ++ ++ void fabs_s(FloatRegister fd, FloatRegister fj) { emit_int32(insn_RR(fabs_s_op, (int)fj->encoding(), (int)fd->encoding())); } ++ void fabs_d(FloatRegister fd, FloatRegister fj) { emit_int32(insn_RR(fabs_d_op, (int)fj->encoding(), (int)fd->encoding())); } ++ void fneg_s(FloatRegister fd, FloatRegister fj) { emit_int32(insn_RR(fneg_s_op, (int)fj->encoding(), (int)fd->encoding())); } ++ void fneg_d(FloatRegister fd, FloatRegister fj) { emit_int32(insn_RR(fneg_d_op, (int)fj->encoding(), (int)fd->encoding())); } ++ void flogb_s(FloatRegister fd, FloatRegister fj) { emit_int32(insn_RR(flogb_s_op, (int)fj->encoding(), (int)fd->encoding())); } ++ void flogb_d(FloatRegister fd, FloatRegister fj) { emit_int32(insn_RR(flogb_d_op, (int)fj->encoding(), (int)fd->encoding())); } ++ void fclass_s(FloatRegister fd, FloatRegister fj) { emit_int32(insn_RR(fclass_s_op, (int)fj->encoding(), (int)fd->encoding())); } ++ void fclass_d(FloatRegister fd, FloatRegister fj) { emit_int32(insn_RR(fclass_d_op, (int)fj->encoding(), (int)fd->encoding())); } ++ void fsqrt_s(FloatRegister fd, FloatRegister fj) { emit_int32(insn_RR(fsqrt_s_op, (int)fj->encoding(), (int)fd->encoding())); } ++ void fsqrt_d(FloatRegister fd, FloatRegister fj) { emit_int32(insn_RR(fsqrt_d_op, (int)fj->encoding(), (int)fd->encoding())); } ++ void frecip_s(FloatRegister fd, FloatRegister fj) { emit_int32(insn_RR(frecip_s_op, (int)fj->encoding(), (int)fd->encoding())); } ++ void frecip_d(FloatRegister fd, FloatRegister fj) { emit_int32(insn_RR(frecip_d_op, (int)fj->encoding(), (int)fd->encoding())); } ++ void frsqrt_s(FloatRegister fd, FloatRegister fj) { emit_int32(insn_RR(frsqrt_s_op, (int)fj->encoding(), (int)fd->encoding())); } ++ void frsqrt_d(FloatRegister fd, FloatRegister fj) { emit_int32(insn_RR(frsqrt_d_op, (int)fj->encoding(), (int)fd->encoding())); } ++ void fmov_s(FloatRegister fd, FloatRegister fj) { emit_int32(insn_RR(fmov_s_op, (int)fj->encoding(), (int)fd->encoding())); } ++ void fmov_d(FloatRegister fd, FloatRegister fj) { emit_int32(insn_RR(fmov_d_op, (int)fj->encoding(), (int)fd->encoding())); } ++ ++ void movgr2fr_w (FloatRegister fd, Register rj) { emit_int32(insn_RR(movgr2fr_w_op, (int)rj->encoding(), (int)fd->encoding())); } ++ void movgr2fr_d (FloatRegister fd, Register rj) { emit_int32(insn_RR(movgr2fr_d_op, (int)rj->encoding(), (int)fd->encoding())); } ++ void movgr2frh_w(FloatRegister fd, Register rj) { emit_int32(insn_RR(movgr2frh_w_op, (int)rj->encoding(), (int)fd->encoding())); } ++ void movfr2gr_s (Register rd, FloatRegister fj) { emit_int32(insn_RR(movfr2gr_s_op, (int)fj->encoding(), (int)rd->encoding())); } ++ void movfr2gr_d (Register rd, FloatRegister fj) { emit_int32(insn_RR(movfr2gr_d_op, (int)fj->encoding(), (int)rd->encoding())); } ++ void movfrh2gr_s(Register rd, FloatRegister fj) { emit_int32(insn_RR(movfrh2gr_s_op, (int)fj->encoding(), (int)rd->encoding())); } ++ void movgr2fcsr (int fcsr, Register rj) { assert(is_uimm(fcsr, 2), "not a unsigned 2-bit init: fcsr0-fcsr3"); emit_int32(insn_RR(movgr2fcsr_op, (int)rj->encoding(), fcsr)); } ++ void movfcsr2gr (Register rd, int fcsr) { assert(is_uimm(fcsr, 2), "not a unsigned 2-bit init: fcsr0-fcsr3"); emit_int32(insn_RR(movfcsr2gr_op, fcsr, (int)rd->encoding())); } ++ void movfr2cf (ConditionalFlagRegister cd, FloatRegister fj) { emit_int32(insn_RR(movfr2cf_op, (int)fj->encoding(), (int)cd->encoding())); } ++ void movcf2fr (FloatRegister fd, ConditionalFlagRegister cj) { emit_int32(insn_RR(movcf2fr_op, (int)cj->encoding(), (int)fd->encoding())); } ++ void movgr2cf (ConditionalFlagRegister cd, Register rj) { emit_int32(insn_RR(movgr2cf_op, (int)rj->encoding(), (int)cd->encoding())); } ++ void movcf2gr (Register rd, ConditionalFlagRegister cj) { emit_int32(insn_RR(movcf2gr_op, (int)cj->encoding(), (int)rd->encoding())); } ++ ++ void fcvt_s_d(FloatRegister fd, FloatRegister fj) { emit_int32(insn_RR(fcvt_s_d_op, (int)fj->encoding(), (int)fd->encoding())); } ++ void fcvt_d_s(FloatRegister fd, FloatRegister fj) { emit_int32(insn_RR(fcvt_d_s_op, (int)fj->encoding(), (int)fd->encoding())); } ++ ++ void ftintrm_w_s(FloatRegister fd, FloatRegister fj) { emit_int32(insn_RR(ftintrm_w_s_op, (int)fj->encoding(), (int)fd->encoding())); } ++ void ftintrm_w_d(FloatRegister fd, FloatRegister fj) { emit_int32(insn_RR(ftintrm_w_d_op, (int)fj->encoding(), (int)fd->encoding())); } ++ void ftintrm_l_s(FloatRegister fd, FloatRegister fj) { emit_int32(insn_RR(ftintrm_l_s_op, (int)fj->encoding(), (int)fd->encoding())); } ++ void ftintrm_l_d(FloatRegister fd, FloatRegister fj) { emit_int32(insn_RR(ftintrm_l_d_op, (int)fj->encoding(), (int)fd->encoding())); } ++ void ftintrp_w_s(FloatRegister fd, FloatRegister fj) { emit_int32(insn_RR(ftintrp_w_s_op, (int)fj->encoding(), (int)fd->encoding())); } ++ void ftintrp_w_d(FloatRegister fd, FloatRegister fj) { emit_int32(insn_RR(ftintrp_w_d_op, (int)fj->encoding(), (int)fd->encoding())); } ++ void ftintrp_l_s(FloatRegister fd, FloatRegister fj) { emit_int32(insn_RR(ftintrp_l_s_op, (int)fj->encoding(), (int)fd->encoding())); } ++ void ftintrp_l_d(FloatRegister fd, FloatRegister fj) { emit_int32(insn_RR(ftintrp_l_d_op, (int)fj->encoding(), (int)fd->encoding())); } ++ void ftintrz_w_s(FloatRegister fd, FloatRegister fj) { emit_int32(insn_RR(ftintrz_w_s_op, (int)fj->encoding(), (int)fd->encoding())); } ++ void ftintrz_w_d(FloatRegister fd, FloatRegister fj) { emit_int32(insn_RR(ftintrz_w_d_op, (int)fj->encoding(), (int)fd->encoding())); } ++ void ftintrz_l_s(FloatRegister fd, FloatRegister fj) { emit_int32(insn_RR(ftintrz_l_s_op, (int)fj->encoding(), (int)fd->encoding())); } ++ void ftintrz_l_d(FloatRegister fd, FloatRegister fj) { emit_int32(insn_RR(ftintrz_l_d_op, (int)fj->encoding(), (int)fd->encoding())); } ++ void ftintrne_w_s(FloatRegister fd, FloatRegister fj) { emit_int32(insn_RR(ftintrne_w_s_op, (int)fj->encoding(), (int)fd->encoding())); } ++ void ftintrne_w_d(FloatRegister fd, FloatRegister fj) { emit_int32(insn_RR(ftintrne_w_d_op, (int)fj->encoding(), (int)fd->encoding())); } ++ void ftintrne_l_s(FloatRegister fd, FloatRegister fj) { emit_int32(insn_RR(ftintrne_l_s_op, (int)fj->encoding(), (int)fd->encoding())); } ++ void ftintrne_l_d(FloatRegister fd, FloatRegister fj) { emit_int32(insn_RR(ftintrne_l_d_op, (int)fj->encoding(), (int)fd->encoding())); } ++ void ftint_w_s(FloatRegister fd, FloatRegister fj) { emit_int32(insn_RR(ftint_w_s_op, (int)fj->encoding(), (int)fd->encoding())); } ++ void ftint_w_d(FloatRegister fd, FloatRegister fj) { emit_int32(insn_RR(ftint_w_d_op, (int)fj->encoding(), (int)fd->encoding())); } ++ void ftint_l_s(FloatRegister fd, FloatRegister fj) { emit_int32(insn_RR(ftint_l_s_op, (int)fj->encoding(), (int)fd->encoding())); } ++ void ftint_l_d(FloatRegister fd, FloatRegister fj) { emit_int32(insn_RR(ftint_l_d_op, (int)fj->encoding(), (int)fd->encoding())); } ++ void ffint_s_w(FloatRegister fd, FloatRegister fj) { emit_int32(insn_RR(ffint_s_w_op, (int)fj->encoding(), (int)fd->encoding())); } ++ void ffint_s_l(FloatRegister fd, FloatRegister fj) { emit_int32(insn_RR(ffint_s_l_op, (int)fj->encoding(), (int)fd->encoding())); } ++ void ffint_d_w(FloatRegister fd, FloatRegister fj) { emit_int32(insn_RR(ffint_d_w_op, (int)fj->encoding(), (int)fd->encoding())); } ++ void ffint_d_l(FloatRegister fd, FloatRegister fj) { emit_int32(insn_RR(ffint_d_l_op, (int)fj->encoding(), (int)fd->encoding())); } ++ void frint_s(FloatRegister fd, FloatRegister fj) { emit_int32(insn_RR(frint_s_op, (int)fj->encoding(), (int)fd->encoding())); } ++ void frint_d(FloatRegister fd, FloatRegister fj) { emit_int32(insn_RR(frint_d_op, (int)fj->encoding(), (int)fd->encoding())); } ++ ++ void slti (Register rd, Register rj, int si12) { assert(is_simm(si12, 12), "not a signed 12-bit int"); emit_int32(insn_I12RR(slti_op, si12, (int)rj->encoding(), (int)rd->encoding())); } ++ void sltui (Register rd, Register rj, int si12) { assert(is_simm(si12, 12), "not a signed 12-bit int"); emit_int32(insn_I12RR(sltui_op, si12, (int)rj->encoding(), (int)rd->encoding())); } ++ void addi_w(Register rd, Register rj, int si12) { assert(is_simm(si12, 12), "not a signed 12-bit int"); emit_int32(insn_I12RR(addi_w_op, si12, (int)rj->encoding(), (int)rd->encoding())); } ++ void addi_d(Register rd, Register rj, int si12) { assert(is_simm(si12, 12), "not a signed 12-bit int"); emit_int32(insn_I12RR(addi_d_op, si12, (int)rj->encoding(), (int)rd->encoding())); } ++ void lu52i_d(Register rd, Register rj, int si12) { /*assert(is_simm(si12, 12), "not a signed 12-bit int");*/ emit_int32(insn_I12RR(lu52i_d_op, simm12(si12), (int)rj->encoding(), (int)rd->encoding())); } ++ void andi (Register rd, Register rj, int ui12) { assert(is_uimm(ui12, 12), "not a unsigned 12-bit int"); emit_int32(insn_I12RR(andi_op, ui12, (int)rj->encoding(), (int)rd->encoding())); } ++ void ori (Register rd, Register rj, int ui12) { assert(is_uimm(ui12, 12), "not a unsigned 12-bit int"); emit_int32(insn_I12RR(ori_op, ui12, (int)rj->encoding(), (int)rd->encoding())); } ++ void xori (Register rd, Register rj, int ui12) { assert(is_uimm(ui12, 12), "not a unsigned 12-bit int"); emit_int32(insn_I12RR(xori_op, ui12, (int)rj->encoding(), (int)rd->encoding())); } ++ ++ void fmadd_s (FloatRegister fd, FloatRegister fj, FloatRegister fk, FloatRegister fa) { emit_int32(insn_RRRR(fmadd_s_op , (int)fa->encoding(), (int)fk->encoding(), (int)fj->encoding(), (int)fd->encoding())); } ++ void fmadd_d (FloatRegister fd, FloatRegister fj, FloatRegister fk, FloatRegister fa) { emit_int32(insn_RRRR(fmadd_d_op , (int)fa->encoding(), (int)fk->encoding(), (int)fj->encoding(), (int)fd->encoding())); } ++ void fmsub_s (FloatRegister fd, FloatRegister fj, FloatRegister fk, FloatRegister fa) { emit_int32(insn_RRRR(fmsub_s_op , (int)fa->encoding(), (int)fk->encoding(), (int)fj->encoding(), (int)fd->encoding())); } ++ void fmsub_d (FloatRegister fd, FloatRegister fj, FloatRegister fk, FloatRegister fa) { emit_int32(insn_RRRR(fmsub_d_op , (int)fa->encoding(), (int)fk->encoding(), (int)fj->encoding(), (int)fd->encoding())); } ++ void fnmadd_s (FloatRegister fd, FloatRegister fj, FloatRegister fk, FloatRegister fa) { emit_int32(insn_RRRR(fnmadd_s_op , (int)fa->encoding(), (int)fk->encoding(), (int)fj->encoding(), (int)fd->encoding())); } ++ void fnmadd_d (FloatRegister fd, FloatRegister fj, FloatRegister fk, FloatRegister fa) { emit_int32(insn_RRRR(fnmadd_d_op , (int)fa->encoding(), (int)fk->encoding(), (int)fj->encoding(), (int)fd->encoding())); } ++ void fnmsub_s (FloatRegister fd, FloatRegister fj, FloatRegister fk, FloatRegister fa) { emit_int32(insn_RRRR(fnmsub_s_op , (int)fa->encoding(), (int)fk->encoding(), (int)fj->encoding(), (int)fd->encoding())); } ++ void fnmsub_d (FloatRegister fd, FloatRegister fj, FloatRegister fk, FloatRegister fa) { emit_int32(insn_RRRR(fnmsub_d_op , (int)fa->encoding(), (int)fk->encoding(), (int)fj->encoding(), (int)fd->encoding())); } ++ ++ void fcmp_caf_s (ConditionalFlagRegister cd, FloatRegister fj, FloatRegister fk) { emit_int32(insn_RRRR(fcmp_cond_s_op, fcmp_caf, (int)fk->encoding(), (int)fj->encoding(), (int)cd->encoding())); } ++ void fcmp_cun_s (ConditionalFlagRegister cd, FloatRegister fj, FloatRegister fk) { emit_int32(insn_RRRR(fcmp_cond_s_op, fcmp_cun , (int)fk->encoding(), (int)fj->encoding(), (int)cd->encoding())); } ++ void fcmp_ceq_s (ConditionalFlagRegister cd, FloatRegister fj, FloatRegister fk) { emit_int32(insn_RRRR(fcmp_cond_s_op, fcmp_ceq , (int)fk->encoding(), (int)fj->encoding(), (int)cd->encoding())); } ++ void fcmp_cueq_s (ConditionalFlagRegister cd, FloatRegister fj, FloatRegister fk) { emit_int32(insn_RRRR(fcmp_cond_s_op, fcmp_cueq, (int)fk->encoding(), (int)fj->encoding(), (int)cd->encoding())); } ++ void fcmp_clt_s (ConditionalFlagRegister cd, FloatRegister fj, FloatRegister fk) { emit_int32(insn_RRRR(fcmp_cond_s_op, fcmp_clt , (int)fk->encoding(), (int)fj->encoding(), (int)cd->encoding())); } ++ void fcmp_cult_s (ConditionalFlagRegister cd, FloatRegister fj, FloatRegister fk) { emit_int32(insn_RRRR(fcmp_cond_s_op, fcmp_cult, (int)fk->encoding(), (int)fj->encoding(), (int)cd->encoding())); } ++ void fcmp_cle_s (ConditionalFlagRegister cd, FloatRegister fj, FloatRegister fk) { emit_int32(insn_RRRR(fcmp_cond_s_op, fcmp_cle , (int)fk->encoding(), (int)fj->encoding(), (int)cd->encoding())); } ++ void fcmp_cule_s (ConditionalFlagRegister cd, FloatRegister fj, FloatRegister fk) { emit_int32(insn_RRRR(fcmp_cond_s_op, fcmp_cule, (int)fk->encoding(), (int)fj->encoding(), (int)cd->encoding())); } ++ void fcmp_cne_s (ConditionalFlagRegister cd, FloatRegister fj, FloatRegister fk) { emit_int32(insn_RRRR(fcmp_cond_s_op, fcmp_cne , (int)fk->encoding(), (int)fj->encoding(), (int)cd->encoding())); } ++ void fcmp_cor_s (ConditionalFlagRegister cd, FloatRegister fj, FloatRegister fk) { emit_int32(insn_RRRR(fcmp_cond_s_op, fcmp_cor , (int)fk->encoding(), (int)fj->encoding(), (int)cd->encoding())); } ++ void fcmp_cune_s (ConditionalFlagRegister cd, FloatRegister fj, FloatRegister fk) { emit_int32(insn_RRRR(fcmp_cond_s_op, fcmp_cune, (int)fk->encoding(), (int)fj->encoding(), (int)cd->encoding())); } ++ void fcmp_saf_s (ConditionalFlagRegister cd, FloatRegister fj, FloatRegister fk) { emit_int32(insn_RRRR(fcmp_cond_s_op, fcmp_saf , (int)fk->encoding(), (int)fj->encoding(), (int)cd->encoding())); } ++ void fcmp_sun_s (ConditionalFlagRegister cd, FloatRegister fj, FloatRegister fk) { emit_int32(insn_RRRR(fcmp_cond_s_op, fcmp_sun , (int)fk->encoding(), (int)fj->encoding(), (int)cd->encoding())); } ++ void fcmp_seq_s (ConditionalFlagRegister cd, FloatRegister fj, FloatRegister fk) { emit_int32(insn_RRRR(fcmp_cond_s_op, fcmp_seq , (int)fk->encoding(), (int)fj->encoding(), (int)cd->encoding())); } ++ void fcmp_sueq_s (ConditionalFlagRegister cd, FloatRegister fj, FloatRegister fk) { emit_int32(insn_RRRR(fcmp_cond_s_op, fcmp_sueq, (int)fk->encoding(), (int)fj->encoding(), (int)cd->encoding())); } ++ void fcmp_slt_s (ConditionalFlagRegister cd, FloatRegister fj, FloatRegister fk) { emit_int32(insn_RRRR(fcmp_cond_s_op, fcmp_slt , (int)fk->encoding(), (int)fj->encoding(), (int)cd->encoding())); } ++ void fcmp_sult_s (ConditionalFlagRegister cd, FloatRegister fj, FloatRegister fk) { emit_int32(insn_RRRR(fcmp_cond_s_op, fcmp_sult, (int)fk->encoding(), (int)fj->encoding(), (int)cd->encoding())); } ++ void fcmp_sle_s (ConditionalFlagRegister cd, FloatRegister fj, FloatRegister fk) { emit_int32(insn_RRRR(fcmp_cond_s_op, fcmp_sle , (int)fk->encoding(), (int)fj->encoding(), (int)cd->encoding())); } ++ void fcmp_sule_s (ConditionalFlagRegister cd, FloatRegister fj, FloatRegister fk) { emit_int32(insn_RRRR(fcmp_cond_s_op, fcmp_sule, (int)fk->encoding(), (int)fj->encoding(), (int)cd->encoding())); } ++ void fcmp_sne_s (ConditionalFlagRegister cd, FloatRegister fj, FloatRegister fk) { emit_int32(insn_RRRR(fcmp_cond_s_op, fcmp_sne , (int)fk->encoding(), (int)fj->encoding(), (int)cd->encoding())); } ++ void fcmp_sor_s (ConditionalFlagRegister cd, FloatRegister fj, FloatRegister fk) { emit_int32(insn_RRRR(fcmp_cond_s_op, fcmp_sor , (int)fk->encoding(), (int)fj->encoding(), (int)cd->encoding())); } ++ void fcmp_sune_s (ConditionalFlagRegister cd, FloatRegister fj, FloatRegister fk) { emit_int32(insn_RRRR(fcmp_cond_s_op, fcmp_sune, (int)fk->encoding(), (int)fj->encoding(), (int)cd->encoding())); } ++ ++ void fcmp_caf_d (ConditionalFlagRegister cd, FloatRegister fj, FloatRegister fk) { emit_int32(insn_RRRR(fcmp_cond_d_op, fcmp_caf, (int)fk->encoding(), (int)fj->encoding(), (int)cd->encoding())); } ++ void fcmp_cun_d (ConditionalFlagRegister cd, FloatRegister fj, FloatRegister fk) { emit_int32(insn_RRRR(fcmp_cond_d_op, fcmp_cun , (int)fk->encoding(), (int)fj->encoding(), (int)cd->encoding())); } ++ void fcmp_ceq_d (ConditionalFlagRegister cd, FloatRegister fj, FloatRegister fk) { emit_int32(insn_RRRR(fcmp_cond_d_op, fcmp_ceq , (int)fk->encoding(), (int)fj->encoding(), (int)cd->encoding())); } ++ void fcmp_cueq_d (ConditionalFlagRegister cd, FloatRegister fj, FloatRegister fk) { emit_int32(insn_RRRR(fcmp_cond_d_op, fcmp_cueq, (int)fk->encoding(), (int)fj->encoding(), (int)cd->encoding())); } ++ void fcmp_clt_d (ConditionalFlagRegister cd, FloatRegister fj, FloatRegister fk) { emit_int32(insn_RRRR(fcmp_cond_d_op, fcmp_clt , (int)fk->encoding(), (int)fj->encoding(), (int)cd->encoding())); } ++ void fcmp_cult_d (ConditionalFlagRegister cd, FloatRegister fj, FloatRegister fk) { emit_int32(insn_RRRR(fcmp_cond_d_op, fcmp_cult, (int)fk->encoding(), (int)fj->encoding(), (int)cd->encoding())); } ++ void fcmp_cle_d (ConditionalFlagRegister cd, FloatRegister fj, FloatRegister fk) { emit_int32(insn_RRRR(fcmp_cond_d_op, fcmp_cle , (int)fk->encoding(), (int)fj->encoding(), (int)cd->encoding())); } ++ void fcmp_cule_d (ConditionalFlagRegister cd, FloatRegister fj, FloatRegister fk) { emit_int32(insn_RRRR(fcmp_cond_d_op, fcmp_cule, (int)fk->encoding(), (int)fj->encoding(), (int)cd->encoding())); } ++ void fcmp_cne_d (ConditionalFlagRegister cd, FloatRegister fj, FloatRegister fk) { emit_int32(insn_RRRR(fcmp_cond_d_op, fcmp_cne , (int)fk->encoding(), (int)fj->encoding(), (int)cd->encoding())); } ++ void fcmp_cor_d (ConditionalFlagRegister cd, FloatRegister fj, FloatRegister fk) { emit_int32(insn_RRRR(fcmp_cond_d_op, fcmp_cor , (int)fk->encoding(), (int)fj->encoding(), (int)cd->encoding())); } ++ void fcmp_cune_d (ConditionalFlagRegister cd, FloatRegister fj, FloatRegister fk) { emit_int32(insn_RRRR(fcmp_cond_d_op, fcmp_cune, (int)fk->encoding(), (int)fj->encoding(), (int)cd->encoding())); } ++ void fcmp_saf_d (ConditionalFlagRegister cd, FloatRegister fj, FloatRegister fk) { emit_int32(insn_RRRR(fcmp_cond_d_op, fcmp_saf , (int)fk->encoding(), (int)fj->encoding(), (int)cd->encoding())); } ++ void fcmp_sun_d (ConditionalFlagRegister cd, FloatRegister fj, FloatRegister fk) { emit_int32(insn_RRRR(fcmp_cond_d_op, fcmp_sun , (int)fk->encoding(), (int)fj->encoding(), (int)cd->encoding())); } ++ void fcmp_seq_d (ConditionalFlagRegister cd, FloatRegister fj, FloatRegister fk) { emit_int32(insn_RRRR(fcmp_cond_d_op, fcmp_seq , (int)fk->encoding(), (int)fj->encoding(), (int)cd->encoding())); } ++ void fcmp_sueq_d (ConditionalFlagRegister cd, FloatRegister fj, FloatRegister fk) { emit_int32(insn_RRRR(fcmp_cond_d_op, fcmp_sueq, (int)fk->encoding(), (int)fj->encoding(), (int)cd->encoding())); } ++ void fcmp_slt_d (ConditionalFlagRegister cd, FloatRegister fj, FloatRegister fk) { emit_int32(insn_RRRR(fcmp_cond_d_op, fcmp_slt , (int)fk->encoding(), (int)fj->encoding(), (int)cd->encoding())); } ++ void fcmp_sult_d (ConditionalFlagRegister cd, FloatRegister fj, FloatRegister fk) { emit_int32(insn_RRRR(fcmp_cond_d_op, fcmp_sult, (int)fk->encoding(), (int)fj->encoding(), (int)cd->encoding())); } ++ void fcmp_sle_d (ConditionalFlagRegister cd, FloatRegister fj, FloatRegister fk) { emit_int32(insn_RRRR(fcmp_cond_d_op, fcmp_sle , (int)fk->encoding(), (int)fj->encoding(), (int)cd->encoding())); } ++ void fcmp_sule_d (ConditionalFlagRegister cd, FloatRegister fj, FloatRegister fk) { emit_int32(insn_RRRR(fcmp_cond_d_op, fcmp_sule, (int)fk->encoding(), (int)fj->encoding(), (int)cd->encoding())); } ++ void fcmp_sne_d (ConditionalFlagRegister cd, FloatRegister fj, FloatRegister fk) { emit_int32(insn_RRRR(fcmp_cond_d_op, fcmp_sne , (int)fk->encoding(), (int)fj->encoding(), (int)cd->encoding())); } ++ void fcmp_sor_d (ConditionalFlagRegister cd, FloatRegister fj, FloatRegister fk) { emit_int32(insn_RRRR(fcmp_cond_d_op, fcmp_sor , (int)fk->encoding(), (int)fj->encoding(), (int)cd->encoding())); } ++ void fcmp_sune_d (ConditionalFlagRegister cd, FloatRegister fj, FloatRegister fk) { emit_int32(insn_RRRR(fcmp_cond_d_op, fcmp_sune, (int)fk->encoding(), (int)fj->encoding(), (int)cd->encoding())); } ++ ++ void fsel (FloatRegister fd, FloatRegister fj, FloatRegister fk, ConditionalFlagRegister ca) { emit_int32(insn_RRRR(fsel_op, (int)ca->encoding(), (int)fk->encoding(), (int)fj->encoding(), (int)fd->encoding())); } ++ ++ void addu16i_d(Register rj, Register rd, int si16) { assert(is_simm(si16, 16), "not a signed 16-bit int"); emit_int32(insn_I16RR(addu16i_d_op, si16, (int)rj->encoding(), (int)rd->encoding())); } ++ ++ void lu12i_w(Register rj, int si20) { /*assert(is_simm(si20, 20), "not a signed 20-bit int");*/ emit_int32(insn_I20R(lu12i_w_op, simm20(si20), (int)rj->encoding())); } ++ void lu32i_d(Register rj, int si20) { /*assert(is_simm(si20, 20), "not a signed 20-bit int");*/ emit_int32(insn_I20R(lu32i_d_op, simm20(si20), (int)rj->encoding())); } ++ void pcaddi(Register rj, int si20) { assert(is_simm(si20, 20), "not a signed 20-bit int"); emit_int32(insn_I20R(pcaddi_op, si20, (int)rj->encoding())); } ++ void pcalau12i(Register rj, int si20) { assert(is_simm(si20, 20), "not a signed 20-bit int"); emit_int32(insn_I20R(pcalau12i_op, si20, (int)rj->encoding())); } ++ void pcaddu12i(Register rj, int si20) { assert(is_simm(si20, 20), "not a signed 20-bit int"); emit_int32(insn_I20R(pcaddu12i_op, si20, (int)rj->encoding())); } ++ void pcaddu18i(Register rj, int si20) { assert(is_simm(si20, 20), "not a signed 20-bit int"); emit_int32(insn_I20R(pcaddu18i_op, si20, (int)rj->encoding())); } ++ ++ void ll_w (Register rd, Register rj, int si16) { assert(is_simm(si16, 16) && ((si16 & 0x3) == 0), "not a signed 16-bit int"); emit_int32(insn_I14RR(ll_w_op, si16>>2, (int)rj->encoding(), (int)rd->encoding())); } ++ void sc_w (Register rd, Register rj, int si16) { assert(is_simm(si16, 16) && ((si16 & 0x3) == 0), "not a signed 16-bit int"); emit_int32(insn_I14RR(sc_w_op, si16>>2, (int)rj->encoding(), (int)rd->encoding())); } ++ void ll_d (Register rd, Register rj, int si16) { assert(is_simm(si16, 16) && ((si16 & 0x3) == 0), "not a signed 16-bit int"); emit_int32(insn_I14RR(ll_d_op, si16>>2, (int)rj->encoding(), (int)rd->encoding())); } ++ void sc_d (Register rd, Register rj, int si16) { assert(is_simm(si16, 16) && ((si16 & 0x3) == 0), "not a signed 16-bit int"); emit_int32(insn_I14RR(sc_d_op, si16>>2, (int)rj->encoding(), (int)rd->encoding())); } ++ void ldptr_w (Register rd, Register rj, int si16) { assert(is_simm(si16, 16) && ((si16 & 0x3) == 0), "not a signed 16-bit int"); emit_int32(insn_I14RR(ldptr_w_op, si16>>2, (int)rj->encoding(), (int)rd->encoding())); } ++ void stptr_w (Register rd, Register rj, int si16) { assert(is_simm(si16, 16) && ((si16 & 0x3) == 0), "not a signed 16-bit int"); emit_int32(insn_I14RR(stptr_w_op, si16>>2, (int)rj->encoding(), (int)rd->encoding())); } ++ void ldptr_d (Register rd, Register rj, int si16) { assert(is_simm(si16, 16) && ((si16 & 0x3) == 0), "not a signed 16-bit int"); emit_int32(insn_I14RR(ldptr_d_op, si16>>2, (int)rj->encoding(), (int)rd->encoding())); } ++ void stptr_d (Register rd, Register rj, int si16) { assert(is_simm(si16, 16) && ((si16 & 0x3) == 0), "not a signed 16-bit int"); emit_int32(insn_I14RR(stptr_d_op, si16>>2, (int)rj->encoding(), (int)rd->encoding())); } ++ ++ void ld_b (Register rd, Register rj, int si12) { assert(is_simm(si12, 12), "not a signed 12-bit int"); emit_int32(insn_I12RR(ld_b_op, si12, (int)rj->encoding(), (int)rd->encoding())); } ++ void ld_h (Register rd, Register rj, int si12) { assert(is_simm(si12, 12), "not a signed 12-bit int"); emit_int32(insn_I12RR(ld_h_op, si12, (int)rj->encoding(), (int)rd->encoding())); } ++ void ld_w (Register rd, Register rj, int si12) { assert(is_simm(si12, 12), "not a signed 12-bit int"); emit_int32(insn_I12RR(ld_w_op, si12, (int)rj->encoding(), (int)rd->encoding())); } ++ void ld_d (Register rd, Register rj, int si12) { assert(is_simm(si12, 12), "not a signed 12-bit int"); emit_int32(insn_I12RR(ld_d_op, si12, (int)rj->encoding(), (int)rd->encoding())); } ++ void st_b (Register rd, Register rj, int si12) { assert(is_simm(si12, 12), "not a signed 12-bit int"); emit_int32(insn_I12RR(st_b_op, si12, (int)rj->encoding(), (int)rd->encoding())); } ++ void st_h (Register rd, Register rj, int si12) { assert(is_simm(si12, 12), "not a signed 12-bit int"); emit_int32(insn_I12RR(st_h_op, si12, (int)rj->encoding(), (int)rd->encoding())); } ++ void st_w (Register rd, Register rj, int si12) { assert(is_simm(si12, 12), "not a signed 12-bit int"); emit_int32(insn_I12RR(st_w_op, si12, (int)rj->encoding(), (int)rd->encoding())); } ++ void st_d (Register rd, Register rj, int si12) { assert(is_simm(si12, 12), "not a signed 12-bit int"); emit_int32(insn_I12RR(st_d_op, si12, (int)rj->encoding(), (int)rd->encoding())); } ++ void ld_bu (Register rd, Register rj, int si12) { assert(is_simm(si12, 12), "not a signed 12-bit int"); emit_int32(insn_I12RR(ld_bu_op, si12, (int)rj->encoding(), (int)rd->encoding())); } ++ void ld_hu (Register rd, Register rj, int si12) { assert(is_simm(si12, 12), "not a signed 12-bit int"); emit_int32(insn_I12RR(ld_hu_op, si12, (int)rj->encoding(), (int)rd->encoding())); } ++ void ld_wu (Register rd, Register rj, int si12) { assert(is_simm(si12, 12), "not a signed 12-bit int"); emit_int32(insn_I12RR(ld_wu_op, si12, (int)rj->encoding(), (int)rd->encoding())); } ++ void preld (int hint, Register rj, int si12) { assert(is_uimm(hint, 5), "not a unsigned 5-bit int"); assert(is_simm(si12, 12), "not a signed 12-bit int"); emit_int32(insn_I12RR(preld_op, si12, (int)rj->encoding(), hint)); } ++ void fld_s (FloatRegister fd, Register rj, int si12) { assert(is_simm(si12, 12), "not a signed 12-bit int"); emit_int32(insn_I12RR(fld_s_op, si12, (int)rj->encoding(), (int)fd->encoding())); } ++ void fst_s (FloatRegister fd, Register rj, int si12) { assert(is_simm(si12, 12), "not a signed 12-bit int"); emit_int32(insn_I12RR(fst_s_op, si12, (int)rj->encoding(), (int)fd->encoding())); } ++ void fld_d (FloatRegister fd, Register rj, int si12) { assert(is_simm(si12, 12), "not a signed 12-bit int"); emit_int32(insn_I12RR(fld_d_op, si12, (int)rj->encoding(), (int)fd->encoding())); } ++ void fst_d (FloatRegister fd, Register rj, int si12) { assert(is_simm(si12, 12), "not a signed 12-bit int"); emit_int32(insn_I12RR(fst_d_op, si12, (int)rj->encoding(), (int)fd->encoding())); } ++ void ldl_w (Register rd, Register rj, int si12) { assert(is_simm(si12, 12), "not a signed 12-bit int"); emit_int32(insn_I12RR(ldl_w_op, si12, (int)rj->encoding(), (int)rd->encoding())); } ++ void ldr_w (Register rd, Register rj, int si12) { assert(is_simm(si12, 12), "not a signed 12-bit int"); emit_int32(insn_I12RR(ldr_w_op, si12, (int)rj->encoding(), (int)rd->encoding())); } ++ ++ void ldx_b (Register rd, Register rj, Register rk) { emit_int32(insn_RRR(ldx_b_op, (int)rk->encoding(), (int)rj->encoding(), (int)rd->encoding())); } ++ void ldx_h (Register rd, Register rj, Register rk) { emit_int32(insn_RRR(ldx_h_op, (int)rk->encoding(), (int)rj->encoding(), (int)rd->encoding())); } ++ void ldx_w (Register rd, Register rj, Register rk) { emit_int32(insn_RRR(ldx_w_op, (int)rk->encoding(), (int)rj->encoding(), (int)rd->encoding())); } ++ void ldx_d (Register rd, Register rj, Register rk) { emit_int32(insn_RRR(ldx_d_op, (int)rk->encoding(), (int)rj->encoding(), (int)rd->encoding())); } ++ void stx_b (Register rd, Register rj, Register rk) { emit_int32(insn_RRR(stx_b_op, (int)rk->encoding(), (int)rj->encoding(), (int)rd->encoding())); } ++ void stx_h (Register rd, Register rj, Register rk) { emit_int32(insn_RRR(stx_h_op, (int)rk->encoding(), (int)rj->encoding(), (int)rd->encoding())); } ++ void stx_w (Register rd, Register rj, Register rk) { emit_int32(insn_RRR(stx_w_op, (int)rk->encoding(), (int)rj->encoding(), (int)rd->encoding())); } ++ void stx_d (Register rd, Register rj, Register rk) { emit_int32(insn_RRR(stx_d_op, (int)rk->encoding(), (int)rj->encoding(), (int)rd->encoding())); } ++ void ldx_bu (Register rd, Register rj, Register rk) { emit_int32(insn_RRR(ldx_bu_op, (int)rk->encoding(), (int)rj->encoding(), (int)rd->encoding())); } ++ void ldx_hu (Register rd, Register rj, Register rk) { emit_int32(insn_RRR(ldx_hu_op, (int)rk->encoding(), (int)rj->encoding(), (int)rd->encoding())); } ++ void ldx_wu (Register rd, Register rj, Register rk) { emit_int32(insn_RRR(ldx_wu_op, (int)rk->encoding(), (int)rj->encoding(), (int)rd->encoding())); } ++ void fldx_s (FloatRegister fd, Register rj, Register rk) { emit_int32(insn_RRR(fldx_s_op, (int)rk->encoding(), (int)rj->encoding(), (int)fd->encoding())); } ++ void fldx_d (FloatRegister fd, Register rj, Register rk) { emit_int32(insn_RRR(fldx_d_op, (int)rk->encoding(), (int)rj->encoding(), (int)fd->encoding())); } ++ void fstx_s (FloatRegister fd, Register rj, Register rk) { emit_int32(insn_RRR(fstx_s_op, (int)rk->encoding(), (int)rj->encoding(), (int)fd->encoding())); } ++ void fstx_d (FloatRegister fd, Register rj, Register rk) { emit_int32(insn_RRR(fstx_d_op, (int)rk->encoding(), (int)rj->encoding(), (int)fd->encoding())); } ++ ++ void ld_b (Register rd, Address src); ++ void ld_bu (Register rd, Address src); ++ void ld_d (Register rd, Address src); ++ void ld_h (Register rd, Address src); ++ void ld_hu (Register rd, Address src); ++ void ll_w (Register rd, Address src); ++ void ll_d (Register rd, Address src); ++ void ld_wu (Register rd, Address src); ++ void ld_w (Register rd, Address src); ++ void st_b (Register rd, Address dst); ++ void st_d (Register rd, Address dst); ++ void st_w (Register rd, Address dst); ++ void sc_w (Register rd, Address dst); ++ void sc_d (Register rd, Address dst); ++ void st_h (Register rd, Address dst); ++ void fld_s (FloatRegister fd, Address src); ++ void fld_d (FloatRegister fd, Address src); ++ void fst_s (FloatRegister fd, Address dst); ++ void fst_d (FloatRegister fd, Address dst); ++ ++ void amswap_w (Register rd, Register rk, Register rj) { assert_different_registers(rd, rj); assert_different_registers(rd, rk); emit_int32(insn_RRR(amswap_w_op, (int)rk->encoding(), (int)rj->encoding(), (int)rd->encoding())); } ++ void amswap_d (Register rd, Register rk, Register rj) { assert_different_registers(rd, rj); assert_different_registers(rd, rk); emit_int32(insn_RRR(amswap_d_op, (int)rk->encoding(), (int)rj->encoding(), (int)rd->encoding())); } ++ void amadd_w (Register rd, Register rk, Register rj) { assert_different_registers(rd, rj); assert_different_registers(rd, rk); emit_int32(insn_RRR(amadd_w_op, (int)rk->encoding(), (int)rj->encoding(), (int)rd->encoding())); } ++ void amadd_d (Register rd, Register rk, Register rj) { assert_different_registers(rd, rj); assert_different_registers(rd, rk); emit_int32(insn_RRR(amadd_d_op, (int)rk->encoding(), (int)rj->encoding(), (int)rj->encoding())); } ++ void amand_w (Register rd, Register rk, Register rj) { assert_different_registers(rd, rj); assert_different_registers(rd, rk); emit_int32(insn_RRR(amand_w_op, (int)rk->encoding(), (int)rj->encoding(), (int)rd->encoding())); } ++ void amand_d (Register rd, Register rk, Register rj) { assert_different_registers(rd, rj); assert_different_registers(rd, rk); emit_int32(insn_RRR(amand_d_op, (int)rk->encoding(), (int)rj->encoding(), (int)rd->encoding())); } ++ void amor_w (Register rd, Register rk, Register rj) { assert_different_registers(rd, rj); assert_different_registers(rd, rk); emit_int32(insn_RRR(amor_w_op, (int)rk->encoding(), (int)rj->encoding(), (int)rd->encoding())); } ++ void amor_d (Register rd, Register rk, Register rj) { assert_different_registers(rd, rj); assert_different_registers(rd, rk); emit_int32(insn_RRR(amor_d_op, (int)rk->encoding(), (int)rj->encoding(), (int)rd->encoding())); } ++ void amxor_w (Register rd, Register rk, Register rj) { assert_different_registers(rd, rj); assert_different_registers(rd, rk); emit_int32(insn_RRR(amxor_w_op, (int)rk->encoding(), (int)rj->encoding(), (int)rd->encoding())); } ++ void amxor_d (Register rd, Register rk, Register rj) { assert_different_registers(rd, rj); assert_different_registers(rd, rk); emit_int32(insn_RRR(amxor_d_op, (int)rk->encoding(), (int)rj->encoding(), (int)rd->encoding())); } ++ void ammax_w (Register rd, Register rk, Register rj) { assert_different_registers(rd, rj); assert_different_registers(rd, rk); emit_int32(insn_RRR(ammax_w_op, (int)rk->encoding(), (int)rj->encoding(), (int)rd->encoding())); } ++ void ammax_d (Register rd, Register rk, Register rj) { assert_different_registers(rd, rj); assert_different_registers(rd, rk); emit_int32(insn_RRR(ammax_d_op, (int)rk->encoding(), (int)rj->encoding(), (int)rd->encoding())); } ++ void ammin_w (Register rd, Register rk, Register rj) { assert_different_registers(rd, rj); assert_different_registers(rd, rk); emit_int32(insn_RRR(ammin_w_op, (int)rk->encoding(), (int)rj->encoding(), (int)rd->encoding())); } ++ void ammin_d (Register rd, Register rk, Register rj) { assert_different_registers(rd, rj); assert_different_registers(rd, rk); emit_int32(insn_RRR(ammin_d_op, (int)rk->encoding(), (int)rj->encoding(), (int)rd->encoding())); } ++ void ammax_wu (Register rd, Register rk, Register rj) { assert_different_registers(rd, rj); assert_different_registers(rd, rk); emit_int32(insn_RRR(ammax_wu_op, (int)rk->encoding(), (int)rj->encoding(), (int)rd->encoding())); } ++ void ammax_du (Register rd, Register rk, Register rj) { assert_different_registers(rd, rj); assert_different_registers(rd, rk); emit_int32(insn_RRR(ammax_du_op, (int)rk->encoding(), (int)rj->encoding(), (int)rd->encoding())); } ++ void ammin_wu (Register rd, Register rk, Register rj) { assert_different_registers(rd, rj); assert_different_registers(rd, rk); emit_int32(insn_RRR(ammin_wu_op, (int)rk->encoding(), (int)rj->encoding(), (int)rd->encoding())); } ++ void ammin_du (Register rd, Register rk, Register rj) { assert_different_registers(rd, rj); assert_different_registers(rd, rk); emit_int32(insn_RRR(ammin_du_op, (int)rk->encoding(), (int)rj->encoding(), (int)rd->encoding())); } ++ void amswap_db_w(Register rd, Register rk, Register rj) { assert_different_registers(rd, rj); assert_different_registers(rd, rk); emit_int32(insn_RRR(amswap_db_w_op, (int)rk->encoding(), (int)rj->encoding(), (int)rd->encoding())); } ++ void amswap_db_d(Register rd, Register rk, Register rj) { assert_different_registers(rd, rj); assert_different_registers(rd, rk); emit_int32(insn_RRR(amswap_db_d_op, (int)rk->encoding(), (int)rj->encoding(), (int)rd->encoding())); } ++ void amadd_db_w (Register rd, Register rk, Register rj) { assert_different_registers(rd, rj); assert_different_registers(rd, rk); emit_int32(insn_RRR(amadd_db_w_op, (int)rk->encoding(), (int)rj->encoding(), (int)rd->encoding())); } ++ void amadd_db_d (Register rd, Register rk, Register rj) { assert_different_registers(rd, rj); assert_different_registers(rd, rk); emit_int32(insn_RRR(amadd_db_d_op, (int)rk->encoding(), (int)rj->encoding(), (int)rd->encoding())); } ++ void amand_db_w (Register rd, Register rk, Register rj) { assert_different_registers(rd, rj); assert_different_registers(rd, rk); emit_int32(insn_RRR(amand_db_w_op, (int)rk->encoding(), (int)rj->encoding(), (int)rd->encoding())); } ++ void amand_db_d (Register rd, Register rk, Register rj) { assert_different_registers(rd, rj); assert_different_registers(rd, rk); emit_int32(insn_RRR(amand_db_d_op, (int)rk->encoding(), (int)rj->encoding(), (int)rd->encoding())); } ++ void amor_db_w (Register rd, Register rk, Register rj) { assert_different_registers(rd, rj); assert_different_registers(rd, rk); emit_int32(insn_RRR(amor_db_w_op, (int)rk->encoding(), (int)rj->encoding(), (int)rd->encoding())); } ++ void amor_db_d (Register rd, Register rk, Register rj) { assert_different_registers(rd, rj); assert_different_registers(rd, rk); emit_int32(insn_RRR(amor_db_d_op, (int)rk->encoding(), (int)rj->encoding(), (int)rd->encoding())); } ++ void amxor_db_w (Register rd, Register rk, Register rj) { assert_different_registers(rd, rj); assert_different_registers(rd, rk); emit_int32(insn_RRR(amxor_db_w_op, (int)rk->encoding(), (int)rj->encoding(), (int)rd->encoding())); } ++ void amxor_db_d (Register rd, Register rk, Register rj) { assert_different_registers(rd, rj); assert_different_registers(rd, rk); emit_int32(insn_RRR(amxor_db_d_op, (int)rk->encoding(), (int)rj->encoding(), (int)rd->encoding())); } ++ void ammax_db_w (Register rd, Register rk, Register rj) { assert_different_registers(rd, rj); assert_different_registers(rd, rk); emit_int32(insn_RRR(ammax_db_w_op, (int)rk->encoding(), (int)rj->encoding(), (int)rd->encoding())); } ++ void ammax_db_d (Register rd, Register rk, Register rj) { assert_different_registers(rd, rj); assert_different_registers(rd, rk); emit_int32(insn_RRR(ammax_db_d_op, (int)rk->encoding(), (int)rj->encoding(), (int)rd->encoding())); } ++ void ammin_db_w (Register rd, Register rk, Register rj) { assert_different_registers(rd, rj); assert_different_registers(rd, rk); emit_int32(insn_RRR(ammin_db_w_op, (int)rk->encoding(), (int)rj->encoding(), (int)rd->encoding())); } ++ void ammin_db_d (Register rd, Register rk, Register rj) { assert_different_registers(rd, rj); assert_different_registers(rd, rk); emit_int32(insn_RRR(ammin_db_d_op, (int)rk->encoding(), (int)rj->encoding(), (int)rd->encoding())); } ++ void ammax_db_wu(Register rd, Register rk, Register rj) { assert_different_registers(rd, rj); assert_different_registers(rd, rk); emit_int32(insn_RRR(ammax_db_wu_op, (int)rk->encoding(), (int)rj->encoding(), (int)rd->encoding())); } ++ void ammax_db_du(Register rd, Register rk, Register rj) { assert_different_registers(rd, rj); assert_different_registers(rd, rk); emit_int32(insn_RRR(ammax_db_du_op, (int)rk->encoding(), (int)rj->encoding(), (int)rd->encoding())); } ++ void ammin_db_wu(Register rd, Register rk, Register rj) { assert_different_registers(rd, rj); assert_different_registers(rd, rk); emit_int32(insn_RRR(ammin_db_wu_op, (int)rk->encoding(), (int)rj->encoding(), (int)rd->encoding())); } ++ void ammin_db_du(Register rd, Register rk, Register rj) { assert_different_registers(rd, rj); assert_different_registers(rd, rk); emit_int32(insn_RRR(ammin_db_du_op, (int)rk->encoding(), (int)rj->encoding(), (int)rd->encoding())); } ++ ++ void dbar(int hint) { ++ assert(is_uimm(hint, 15), "not a unsigned 15-bit int"); ++ ++ if (os::is_ActiveCoresMP()) ++ andi(R0, R0, 0); ++ else ++ emit_int32(insn_I15(dbar_op, hint)); ++ } ++ void ibar(int hint) { assert(is_uimm(hint, 15), "not a unsigned 15-bit int"); emit_int32(insn_I15(ibar_op, hint)); } ++ ++ void fldgt_s (Register rd, Register rj, Register rk) { emit_int32(insn_RRR(fldgt_s_op, (int)rk->encoding(), (int)rj->encoding(), (int)rd->encoding())); } ++ void fldgt_d (Register rd, Register rj, Register rk) { emit_int32(insn_RRR(fldgt_d_op, (int)rk->encoding(), (int)rj->encoding(), (int)rd->encoding())); } ++ void fldle_s (Register rd, Register rj, Register rk) { emit_int32(insn_RRR(fldle_s_op, (int)rk->encoding(), (int)rj->encoding(), (int)rd->encoding())); } ++ void fldle_d (Register rd, Register rj, Register rk) { emit_int32(insn_RRR(fldle_d_op, (int)rk->encoding(), (int)rj->encoding(), (int)rd->encoding())); } ++ void fstgt_s (Register rd, Register rj, Register rk) { emit_int32(insn_RRR(fstgt_s_op, (int)rk->encoding(), (int)rj->encoding(), (int)rd->encoding())); } ++ void fstgt_d (Register rd, Register rj, Register rk) { emit_int32(insn_RRR(fstgt_d_op, (int)rk->encoding(), (int)rj->encoding(), (int)rd->encoding())); } ++ void fstle_s (Register rd, Register rj, Register rk) { emit_int32(insn_RRR(fstle_s_op, (int)rk->encoding(), (int)rj->encoding(), (int)rd->encoding())); } ++ void fstle_d (Register rd, Register rj, Register rk) { emit_int32(insn_RRR(fstle_d_op, (int)rk->encoding(), (int)rj->encoding(), (int)rd->encoding())); } ++ ++ void ldgt_b (Register rd, Register rj, Register rk) { emit_int32(insn_RRR(ldgt_b_op, (int)rk->encoding(), (int)rj->encoding(), (int)rd->encoding())); } ++ void ldgt_h (Register rd, Register rj, Register rk) { emit_int32(insn_RRR(ldgt_h_op, (int)rk->encoding(), (int)rj->encoding(), (int)rd->encoding())); } ++ void ldgt_w (Register rd, Register rj, Register rk) { emit_int32(insn_RRR(ldgt_w_op, (int)rk->encoding(), (int)rj->encoding(), (int)rd->encoding())); } ++ void ldgt_d (Register rd, Register rj, Register rk) { emit_int32(insn_RRR(ldgt_d_op, (int)rk->encoding(), (int)rj->encoding(), (int)rd->encoding())); } ++ void ldle_b (Register rd, Register rj, Register rk) { emit_int32(insn_RRR(ldle_b_op, (int)rk->encoding(), (int)rj->encoding(), (int)rd->encoding())); } ++ void ldle_h (Register rd, Register rj, Register rk) { emit_int32(insn_RRR(ldle_h_op, (int)rk->encoding(), (int)rj->encoding(), (int)rd->encoding())); } ++ void ldle_w (Register rd, Register rj, Register rk) { emit_int32(insn_RRR(ldle_w_op, (int)rk->encoding(), (int)rj->encoding(), (int)rd->encoding())); } ++ void ldle_d (Register rd, Register rj, Register rk) { emit_int32(insn_RRR(ldle_d_op, (int)rk->encoding(), (int)rj->encoding(), (int)rd->encoding())); } ++ void stgt_b (Register rd, Register rj, Register rk) { emit_int32(insn_RRR(stgt_b_op, (int)rk->encoding(), (int)rj->encoding(), (int)rd->encoding())); } ++ void stgt_h (Register rd, Register rj, Register rk) { emit_int32(insn_RRR(stgt_h_op, (int)rk->encoding(), (int)rj->encoding(), (int)rd->encoding())); } ++ void stgt_w (Register rd, Register rj, Register rk) { emit_int32(insn_RRR(stgt_w_op, (int)rk->encoding(), (int)rj->encoding(), (int)rd->encoding())); } ++ void stgt_d (Register rd, Register rj, Register rk) { emit_int32(insn_RRR(stgt_d_op, (int)rk->encoding(), (int)rj->encoding(), (int)rd->encoding())); } ++ void stle_b (Register rd, Register rj, Register rk) { emit_int32(insn_RRR(stle_b_op, (int)rk->encoding(), (int)rj->encoding(), (int)rd->encoding())); } ++ void stle_h (Register rd, Register rj, Register rk) { emit_int32(insn_RRR(stle_h_op, (int)rk->encoding(), (int)rj->encoding(), (int)rd->encoding())); } ++ void stle_w (Register rd, Register rj, Register rk) { emit_int32(insn_RRR(stle_w_op, (int)rk->encoding(), (int)rj->encoding(), (int)rd->encoding())); } ++ void stle_d (Register rd, Register rj, Register rk) { emit_int32(insn_RRR(stle_d_op, (int)rk->encoding(), (int)rj->encoding(), (int)rd->encoding())); } ++ ++ void beqz(Register rj, int offs) { assert(is_simm(offs, 21), "not a signed 21-bit int"); emit_int32(insn_IRI(beqz_op, offs, (int)rj->encoding())); } ++ void bnez(Register rj, int offs) { assert(is_simm(offs, 21), "not a signed 21-bit int"); emit_int32(insn_IRI(bnez_op, offs, (int)rj->encoding())); } ++ void bceqz(ConditionalFlagRegister cj, int offs) { assert(is_simm(offs, 21), "not a signed 21-bit int"); emit_int32(insn_IRI(bccondz_op, offs, ( (0b00<<3) | (int)cj->encoding()))); } ++ void bcnez(ConditionalFlagRegister cj, int offs) { assert(is_simm(offs, 21), "not a signed 21-bit int"); emit_int32(insn_IRI(bccondz_op, offs, ( (0b01<<3) | (int)cj->encoding()))); } ++ ++ void jirl(Register rd, Register rj, int offs) { assert(is_simm(offs, 18) && ((offs & 3) == 0), "not a signed 18-bit int"); emit_int32(insn_I16RR(jirl_op, offs >> 2, (int)rj->encoding(), (int)rd->encoding())); } ++ ++ void b(int offs) { assert(is_simm(offs, 26), "not a signed 26-bit int"); emit_int32(insn_I26(b_op, offs)); } ++ void bl(int offs) { assert(is_simm(offs, 26), "not a signed 26-bit int"); emit_int32(insn_I26(bl_op, offs)); } ++ ++ ++ void beq(Register rj, Register rd, int offs) { assert(is_simm(offs, 16), "not a signed 16-bit int"); emit_int32(insn_I16RR(beq_op, offs, (int)rj->encoding(), (int)rd->encoding())); } ++ void bne(Register rj, Register rd, int offs) { assert(is_simm(offs, 16), "not a signed 16-bit int"); emit_int32(insn_I16RR(bne_op, offs, (int)rj->encoding(), (int)rd->encoding())); } ++ void blt(Register rj, Register rd, int offs) { assert(is_simm(offs, 16), "not a signed 16-bit int"); emit_int32(insn_I16RR(blt_op, offs, (int)rj->encoding(), (int)rd->encoding())); } ++ void bge(Register rj, Register rd, int offs) { assert(is_simm(offs, 16), "not a signed 16-bit int"); emit_int32(insn_I16RR(bge_op, offs, (int)rj->encoding(), (int)rd->encoding())); } ++ void bltu(Register rj, Register rd, int offs) { assert(is_simm(offs, 16), "not a signed 16-bit int"); emit_int32(insn_I16RR(bltu_op, offs, (int)rj->encoding(), (int)rd->encoding())); } ++ void bgeu(Register rj, Register rd, int offs) { assert(is_simm(offs, 16), "not a signed 16-bit int"); emit_int32(insn_I16RR(bgeu_op, offs, (int)rj->encoding(), (int)rd->encoding())); } ++ ++ void beq (Register rj, Register rd, address entry) { beq (rj, rd, offset16(entry)); } ++ void bne (Register rj, Register rd, address entry) { bne (rj, rd, offset16(entry)); } ++ void blt (Register rj, Register rd, address entry) { blt (rj, rd, offset16(entry)); } ++ void bge (Register rj, Register rd, address entry) { bge (rj, rd, offset16(entry)); } ++ void bltu (Register rj, Register rd, address entry) { bltu (rj, rd, offset16(entry)); } ++ void bgeu (Register rj, Register rd, address entry) { bgeu (rj, rd, offset16(entry)); } ++ void beqz (Register rj, address entry) { beqz (rj, offset21(entry)); } ++ void bnez (Register rj, address entry) { bnez (rj, offset21(entry)); } ++ void b(address entry) { b(offset26(entry)); } ++ void bl(address entry) { bl(offset26(entry)); } ++ void bceqz(ConditionalFlagRegister cj, address entry) { bceqz(cj, offset21(entry)); } ++ void bcnez(ConditionalFlagRegister cj, address entry) { bcnez(cj, offset21(entry)); } ++ ++ void beq (Register rj, Register rd, Label& L) { beq (rj, rd, target(L)); } ++ void bne (Register rj, Register rd, Label& L) { bne (rj, rd, target(L)); } ++ void blt (Register rj, Register rd, Label& L) { blt (rj, rd, target(L)); } ++ void bge (Register rj, Register rd, Label& L) { bge (rj, rd, target(L)); } ++ void bltu (Register rj, Register rd, Label& L) { bltu (rj, rd, target(L)); } ++ void bgeu (Register rj, Register rd, Label& L) { bgeu (rj, rd, target(L)); } ++ void beqz (Register rj, Label& L) { beqz (rj, target(L)); } ++ void bnez (Register rj, Label& L) { bnez (rj, target(L)); } ++ void b(Label& L) { b(target(L)); } ++ void bl(Label& L) { bl(target(L)); } ++ void bceqz(ConditionalFlagRegister cj, Label& L) { bceqz(cj, target(L)); } ++ void bcnez(ConditionalFlagRegister cj, Label& L) { bcnez(cj, target(L)); } ++ ++ //1. Now Membar_mask_bits is 0,Need to fix it after LA6000 ++ //2. Also to fix *prev & 0x7FFF)== hin in MacroAssembler::membar(Membar_mask_bits hint) ++ typedef enum { ++ StoreStore = 0, ++ LoadStore = 0, ++ StoreLoad = 0, ++ LoadLoad = 0, ++ AnyAny = 0 ++ } Membar_mask_bits; ++ ++ // Serializes memory and blows flags ++ void membar(Membar_mask_bits hint) { ++ dbar(hint); ++ } ++ ++ // LSX and LASX ++#define ASSERT_LSX assert(UseLSX, ""); ++#define ASSERT_LASX assert(UseLASX, ""); ++ ++ void vadd_b(FloatRegister vd, FloatRegister vj, FloatRegister vk) { ASSERT_LSX emit_int32(insn_RRR( vadd_b_op, (int)vk->encoding(), (int)vj->encoding(), (int)vd->encoding())); } ++ void vadd_h(FloatRegister vd, FloatRegister vj, FloatRegister vk) { ASSERT_LSX emit_int32(insn_RRR( vadd_h_op, (int)vk->encoding(), (int)vj->encoding(), (int)vd->encoding())); } ++ void vadd_w(FloatRegister vd, FloatRegister vj, FloatRegister vk) { ASSERT_LSX emit_int32(insn_RRR( vadd_w_op, (int)vk->encoding(), (int)vj->encoding(), (int)vd->encoding())); } ++ void vadd_d(FloatRegister vd, FloatRegister vj, FloatRegister vk) { ASSERT_LSX emit_int32(insn_RRR( vadd_d_op, (int)vk->encoding(), (int)vj->encoding(), (int)vd->encoding())); } ++ void vadd_q(FloatRegister vd, FloatRegister vj, FloatRegister vk) { ASSERT_LSX emit_int32(insn_RRR( vadd_q_op, (int)vk->encoding(), (int)vj->encoding(), (int)vd->encoding())); } ++ void xvadd_b(FloatRegister xd, FloatRegister xj, FloatRegister xk) { ASSERT_LASX emit_int32(insn_RRR(xvadd_b_op, (int)xk->encoding(), (int)xj->encoding(), (int)xd->encoding())); } ++ void xvadd_h(FloatRegister xd, FloatRegister xj, FloatRegister xk) { ASSERT_LASX emit_int32(insn_RRR(xvadd_h_op, (int)xk->encoding(), (int)xj->encoding(), (int)xd->encoding())); } ++ void xvadd_w(FloatRegister xd, FloatRegister xj, FloatRegister xk) { ASSERT_LASX emit_int32(insn_RRR(xvadd_w_op, (int)xk->encoding(), (int)xj->encoding(), (int)xd->encoding())); } ++ void xvadd_d(FloatRegister xd, FloatRegister xj, FloatRegister xk) { ASSERT_LASX emit_int32(insn_RRR(xvadd_d_op, (int)xk->encoding(), (int)xj->encoding(), (int)xd->encoding())); } ++ void xvadd_q(FloatRegister xd, FloatRegister xj, FloatRegister xk) { ASSERT_LASX emit_int32(insn_RRR(xvadd_q_op, (int)xk->encoding(), (int)xj->encoding(), (int)xd->encoding())); } ++ ++ void vsub_b(FloatRegister vd, FloatRegister vj, FloatRegister vk) { ASSERT_LSX emit_int32(insn_RRR( vsub_b_op, (int)vk->encoding(), (int)vj->encoding(), (int)vd->encoding())); } ++ void vsub_h(FloatRegister vd, FloatRegister vj, FloatRegister vk) { ASSERT_LSX emit_int32(insn_RRR( vsub_h_op, (int)vk->encoding(), (int)vj->encoding(), (int)vd->encoding())); } ++ void vsub_w(FloatRegister vd, FloatRegister vj, FloatRegister vk) { ASSERT_LSX emit_int32(insn_RRR( vsub_w_op, (int)vk->encoding(), (int)vj->encoding(), (int)vd->encoding())); } ++ void vsub_d(FloatRegister vd, FloatRegister vj, FloatRegister vk) { ASSERT_LSX emit_int32(insn_RRR( vsub_d_op, (int)vk->encoding(), (int)vj->encoding(), (int)vd->encoding())); } ++ void vsub_q(FloatRegister vd, FloatRegister vj, FloatRegister vk) { ASSERT_LSX emit_int32(insn_RRR( vsub_q_op, (int)vk->encoding(), (int)vj->encoding(), (int)vd->encoding())); } ++ void xvsub_b(FloatRegister xd, FloatRegister xj, FloatRegister xk) { ASSERT_LASX emit_int32(insn_RRR(xvsub_b_op, (int)xk->encoding(), (int)xj->encoding(), (int)xd->encoding())); } ++ void xvsub_h(FloatRegister xd, FloatRegister xj, FloatRegister xk) { ASSERT_LASX emit_int32(insn_RRR(xvsub_h_op, (int)xk->encoding(), (int)xj->encoding(), (int)xd->encoding())); } ++ void xvsub_w(FloatRegister xd, FloatRegister xj, FloatRegister xk) { ASSERT_LASX emit_int32(insn_RRR(xvsub_w_op, (int)xk->encoding(), (int)xj->encoding(), (int)xd->encoding())); } ++ void xvsub_d(FloatRegister xd, FloatRegister xj, FloatRegister xk) { ASSERT_LASX emit_int32(insn_RRR(xvsub_d_op, (int)xk->encoding(), (int)xj->encoding(), (int)xd->encoding())); } ++ void xvsub_q(FloatRegister xd, FloatRegister xj, FloatRegister xk) { ASSERT_LASX emit_int32(insn_RRR(xvsub_q_op, (int)xk->encoding(), (int)xj->encoding(), (int)xd->encoding())); } ++ ++ void vaddi_bu(FloatRegister vd, FloatRegister vj, int ui5) { ASSERT_LSX emit_int32(insn_I5RR( vaddi_bu_op, ui5, (int)vj->encoding(), (int)vd->encoding())); } ++ void vaddi_hu(FloatRegister vd, FloatRegister vj, int ui5) { ASSERT_LSX emit_int32(insn_I5RR( vaddi_hu_op, ui5, (int)vj->encoding(), (int)vd->encoding())); } ++ void vaddi_wu(FloatRegister vd, FloatRegister vj, int ui5) { ASSERT_LSX emit_int32(insn_I5RR( vaddi_wu_op, ui5, (int)vj->encoding(), (int)vd->encoding())); } ++ void vaddi_du(FloatRegister vd, FloatRegister vj, int ui5) { ASSERT_LSX emit_int32(insn_I5RR( vaddi_du_op, ui5, (int)vj->encoding(), (int)vd->encoding())); } ++ void xvaddi_bu(FloatRegister xd, FloatRegister xj, int ui5) { ASSERT_LASX emit_int32(insn_I5RR(xvaddi_bu_op, ui5, (int)xj->encoding(), (int)xd->encoding())); } ++ void xvaddi_hu(FloatRegister xd, FloatRegister xj, int ui5) { ASSERT_LASX emit_int32(insn_I5RR(xvaddi_hu_op, ui5, (int)xj->encoding(), (int)xd->encoding())); } ++ void xvaddi_wu(FloatRegister xd, FloatRegister xj, int ui5) { ASSERT_LASX emit_int32(insn_I5RR(xvaddi_wu_op, ui5, (int)xj->encoding(), (int)xd->encoding())); } ++ void xvaddi_du(FloatRegister xd, FloatRegister xj, int ui5) { ASSERT_LASX emit_int32(insn_I5RR(xvaddi_du_op, ui5, (int)xj->encoding(), (int)xd->encoding())); } ++ ++ void vsubi_bu(FloatRegister vd, FloatRegister vj, int ui5) { ASSERT_LSX emit_int32(insn_I5RR( vsubi_bu_op, ui5, (int)vj->encoding(), (int)vd->encoding())); } ++ void vsubi_hu(FloatRegister vd, FloatRegister vj, int ui5) { ASSERT_LSX emit_int32(insn_I5RR( vsubi_hu_op, ui5, (int)vj->encoding(), (int)vd->encoding())); } ++ void vsubi_wu(FloatRegister vd, FloatRegister vj, int ui5) { ASSERT_LSX emit_int32(insn_I5RR( vsubi_wu_op, ui5, (int)vj->encoding(), (int)vd->encoding())); } ++ void vsubi_du(FloatRegister vd, FloatRegister vj, int ui5) { ASSERT_LSX emit_int32(insn_I5RR( vsubi_du_op, ui5, (int)vj->encoding(), (int)vd->encoding())); } ++ void xvsubi_bu(FloatRegister xd, FloatRegister xj, int ui5) { ASSERT_LASX emit_int32(insn_I5RR(xvsubi_bu_op, ui5, (int)xj->encoding(), (int)xd->encoding())); } ++ void xvsubi_hu(FloatRegister xd, FloatRegister xj, int ui5) { ASSERT_LASX emit_int32(insn_I5RR(xvsubi_hu_op, ui5, (int)xj->encoding(), (int)xd->encoding())); } ++ void xvsubi_wu(FloatRegister xd, FloatRegister xj, int ui5) { ASSERT_LASX emit_int32(insn_I5RR(xvsubi_wu_op, ui5, (int)xj->encoding(), (int)xd->encoding())); } ++ void xvsubi_du(FloatRegister xd, FloatRegister xj, int ui5) { ASSERT_LASX emit_int32(insn_I5RR(xvsubi_du_op, ui5, (int)xj->encoding(), (int)xd->encoding())); } ++ ++ void vneg_b(FloatRegister vd, FloatRegister vj) { ASSERT_LSX emit_int32(insn_RR( vneg_b_op, (int)vj->encoding(), (int)vd->encoding())); } ++ void vneg_h(FloatRegister vd, FloatRegister vj) { ASSERT_LSX emit_int32(insn_RR( vneg_h_op, (int)vj->encoding(), (int)vd->encoding())); } ++ void vneg_w(FloatRegister vd, FloatRegister vj) { ASSERT_LSX emit_int32(insn_RR( vneg_w_op, (int)vj->encoding(), (int)vd->encoding())); } ++ void vneg_d(FloatRegister vd, FloatRegister vj) { ASSERT_LSX emit_int32(insn_RR( vneg_d_op, (int)vj->encoding(), (int)vd->encoding())); } ++ void xvneg_b(FloatRegister xd, FloatRegister xj) { ASSERT_LASX emit_int32(insn_RR(xvneg_b_op, (int)xj->encoding(), (int)xd->encoding())); } ++ void xvneg_h(FloatRegister xd, FloatRegister xj) { ASSERT_LASX emit_int32(insn_RR(xvneg_h_op, (int)xj->encoding(), (int)xd->encoding())); } ++ void xvneg_w(FloatRegister xd, FloatRegister xj) { ASSERT_LASX emit_int32(insn_RR(xvneg_w_op, (int)xj->encoding(), (int)xd->encoding())); } ++ void xvneg_d(FloatRegister xd, FloatRegister xj) { ASSERT_LASX emit_int32(insn_RR(xvneg_d_op, (int)xj->encoding(), (int)xd->encoding())); } ++ ++ void vabsd_b(FloatRegister vd, FloatRegister vj, FloatRegister vk) { ASSERT_LSX emit_int32(insn_RRR( vabsd_b_op, (int)vk->encoding(), (int)vj->encoding(), (int)vd->encoding())); } ++ void vabsd_h(FloatRegister vd, FloatRegister vj, FloatRegister vk) { ASSERT_LSX emit_int32(insn_RRR( vabsd_h_op, (int)vk->encoding(), (int)vj->encoding(), (int)vd->encoding())); } ++ void vabsd_w(FloatRegister vd, FloatRegister vj, FloatRegister vk) { ASSERT_LSX emit_int32(insn_RRR( vabsd_w_op, (int)vk->encoding(), (int)vj->encoding(), (int)vd->encoding())); } ++ void vabsd_d(FloatRegister vd, FloatRegister vj, FloatRegister vk) { ASSERT_LSX emit_int32(insn_RRR( vabsd_d_op, (int)vk->encoding(), (int)vj->encoding(), (int)vd->encoding())); } ++ void xvabsd_b(FloatRegister xd, FloatRegister xj, FloatRegister xk) { ASSERT_LASX emit_int32(insn_RRR(xvabsd_b_op, (int)xk->encoding(), (int)xj->encoding(), (int)xd->encoding())); } ++ void xvabsd_h(FloatRegister xd, FloatRegister xj, FloatRegister xk) { ASSERT_LASX emit_int32(insn_RRR(xvabsd_h_op, (int)xk->encoding(), (int)xj->encoding(), (int)xd->encoding())); } ++ void xvabsd_w(FloatRegister xd, FloatRegister xj, FloatRegister xk) { ASSERT_LASX emit_int32(insn_RRR(xvabsd_w_op, (int)xk->encoding(), (int)xj->encoding(), (int)xd->encoding())); } ++ void xvabsd_d(FloatRegister xd, FloatRegister xj, FloatRegister xk) { ASSERT_LASX emit_int32(insn_RRR(xvabsd_d_op, (int)xk->encoding(), (int)xj->encoding(), (int)xd->encoding())); } ++ ++ void vmax_b(FloatRegister vd, FloatRegister vj, FloatRegister vk) { ASSERT_LSX emit_int32(insn_RRR( vmax_b_op, (int)vk->encoding(), (int)vj->encoding(), (int)vd->encoding())); } ++ void vmax_h(FloatRegister vd, FloatRegister vj, FloatRegister vk) { ASSERT_LSX emit_int32(insn_RRR( vmax_h_op, (int)vk->encoding(), (int)vj->encoding(), (int)vd->encoding())); } ++ void vmax_w(FloatRegister vd, FloatRegister vj, FloatRegister vk) { ASSERT_LSX emit_int32(insn_RRR( vmax_w_op, (int)vk->encoding(), (int)vj->encoding(), (int)vd->encoding())); } ++ void vmax_d(FloatRegister vd, FloatRegister vj, FloatRegister vk) { ASSERT_LSX emit_int32(insn_RRR( vmax_d_op, (int)vk->encoding(), (int)vj->encoding(), (int)vd->encoding())); } ++ void xvmax_b(FloatRegister xd, FloatRegister xj, FloatRegister xk) { ASSERT_LASX emit_int32(insn_RRR(xvmax_b_op, (int)xk->encoding(), (int)xj->encoding(), (int)xd->encoding())); } ++ void xvmax_h(FloatRegister xd, FloatRegister xj, FloatRegister xk) { ASSERT_LASX emit_int32(insn_RRR(xvmax_h_op, (int)xk->encoding(), (int)xj->encoding(), (int)xd->encoding())); } ++ void xvmax_w(FloatRegister xd, FloatRegister xj, FloatRegister xk) { ASSERT_LASX emit_int32(insn_RRR(xvmax_w_op, (int)xk->encoding(), (int)xj->encoding(), (int)xd->encoding())); } ++ void xvmax_d(FloatRegister xd, FloatRegister xj, FloatRegister xk) { ASSERT_LASX emit_int32(insn_RRR(xvmax_d_op, (int)xk->encoding(), (int)xj->encoding(), (int)xd->encoding())); } ++ ++ void vmin_b(FloatRegister vd, FloatRegister vj, FloatRegister vk) { ASSERT_LSX emit_int32(insn_RRR( vmin_b_op, (int)vk->encoding(), (int)vj->encoding(), (int)vd->encoding())); } ++ void vmin_h(FloatRegister vd, FloatRegister vj, FloatRegister vk) { ASSERT_LSX emit_int32(insn_RRR( vmin_h_op, (int)vk->encoding(), (int)vj->encoding(), (int)vd->encoding())); } ++ void vmin_w(FloatRegister vd, FloatRegister vj, FloatRegister vk) { ASSERT_LSX emit_int32(insn_RRR( vmin_w_op, (int)vk->encoding(), (int)vj->encoding(), (int)vd->encoding())); } ++ void vmin_d(FloatRegister vd, FloatRegister vj, FloatRegister vk) { ASSERT_LSX emit_int32(insn_RRR( vmin_d_op, (int)vk->encoding(), (int)vj->encoding(), (int)vd->encoding())); } ++ void xvmin_b(FloatRegister xd, FloatRegister xj, FloatRegister xk) { ASSERT_LASX emit_int32(insn_RRR(xvmin_b_op, (int)xk->encoding(), (int)xj->encoding(), (int)xd->encoding())); } ++ void xvmin_h(FloatRegister xd, FloatRegister xj, FloatRegister xk) { ASSERT_LASX emit_int32(insn_RRR(xvmin_h_op, (int)xk->encoding(), (int)xj->encoding(), (int)xd->encoding())); } ++ void xvmin_w(FloatRegister xd, FloatRegister xj, FloatRegister xk) { ASSERT_LASX emit_int32(insn_RRR(xvmin_w_op, (int)xk->encoding(), (int)xj->encoding(), (int)xd->encoding())); } ++ void xvmin_d(FloatRegister xd, FloatRegister xj, FloatRegister xk) { ASSERT_LASX emit_int32(insn_RRR(xvmin_d_op, (int)xk->encoding(), (int)xj->encoding(), (int)xd->encoding())); } ++ ++ void vmul_b(FloatRegister vd, FloatRegister vj, FloatRegister vk) { ASSERT_LSX emit_int32(insn_RRR( vmul_b_op, (int)vk->encoding(), (int)vj->encoding(), (int)vd->encoding())); } ++ void vmul_h(FloatRegister vd, FloatRegister vj, FloatRegister vk) { ASSERT_LSX emit_int32(insn_RRR( vmul_h_op, (int)vk->encoding(), (int)vj->encoding(), (int)vd->encoding())); } ++ void vmul_w(FloatRegister vd, FloatRegister vj, FloatRegister vk) { ASSERT_LSX emit_int32(insn_RRR( vmul_w_op, (int)vk->encoding(), (int)vj->encoding(), (int)vd->encoding())); } ++ void vmul_d(FloatRegister vd, FloatRegister vj, FloatRegister vk) { ASSERT_LSX emit_int32(insn_RRR( vmul_d_op, (int)vk->encoding(), (int)vj->encoding(), (int)vd->encoding())); } ++ void xvmul_b(FloatRegister xd, FloatRegister xj, FloatRegister xk) { ASSERT_LASX emit_int32(insn_RRR(xvmul_b_op, (int)xk->encoding(), (int)xj->encoding(), (int)xd->encoding())); } ++ void xvmul_h(FloatRegister xd, FloatRegister xj, FloatRegister xk) { ASSERT_LASX emit_int32(insn_RRR(xvmul_h_op, (int)xk->encoding(), (int)xj->encoding(), (int)xd->encoding())); } ++ void xvmul_w(FloatRegister xd, FloatRegister xj, FloatRegister xk) { ASSERT_LASX emit_int32(insn_RRR(xvmul_w_op, (int)xk->encoding(), (int)xj->encoding(), (int)xd->encoding())); } ++ void xvmul_d(FloatRegister xd, FloatRegister xj, FloatRegister xk) { ASSERT_LASX emit_int32(insn_RRR(xvmul_d_op, (int)xk->encoding(), (int)xj->encoding(), (int)xd->encoding())); } ++ ++ void vmuh_b(FloatRegister vd, FloatRegister vj, FloatRegister vk) { ASSERT_LSX emit_int32(insn_RRR( vmuh_b_op, (int)vk->encoding(), (int)vj->encoding(), (int)vd->encoding())); } ++ void vmuh_h(FloatRegister vd, FloatRegister vj, FloatRegister vk) { ASSERT_LSX emit_int32(insn_RRR( vmuh_h_op, (int)vk->encoding(), (int)vj->encoding(), (int)vd->encoding())); } ++ void vmuh_w(FloatRegister vd, FloatRegister vj, FloatRegister vk) { ASSERT_LSX emit_int32(insn_RRR( vmuh_w_op, (int)vk->encoding(), (int)vj->encoding(), (int)vd->encoding())); } ++ void vmuh_d(FloatRegister vd, FloatRegister vj, FloatRegister vk) { ASSERT_LSX emit_int32(insn_RRR( vmuh_d_op, (int)vk->encoding(), (int)vj->encoding(), (int)vd->encoding())); } ++ void xvmuh_b(FloatRegister xd, FloatRegister xj, FloatRegister xk) { ASSERT_LASX emit_int32(insn_RRR(xvmuh_b_op, (int)xk->encoding(), (int)xj->encoding(), (int)xd->encoding())); } ++ void xvmuh_h(FloatRegister xd, FloatRegister xj, FloatRegister xk) { ASSERT_LASX emit_int32(insn_RRR(xvmuh_h_op, (int)xk->encoding(), (int)xj->encoding(), (int)xd->encoding())); } ++ void xvmuh_w(FloatRegister xd, FloatRegister xj, FloatRegister xk) { ASSERT_LASX emit_int32(insn_RRR(xvmuh_w_op, (int)xk->encoding(), (int)xj->encoding(), (int)xd->encoding())); } ++ void xvmuh_d(FloatRegister xd, FloatRegister xj, FloatRegister xk) { ASSERT_LASX emit_int32(insn_RRR(xvmuh_d_op, (int)xk->encoding(), (int)xj->encoding(), (int)xd->encoding())); } ++ ++ void vmuh_bu(FloatRegister vd, FloatRegister vj, FloatRegister vk) { ASSERT_LSX emit_int32(insn_RRR( vmuh_bu_op, (int)vk->encoding(), (int)vj->encoding(), (int)vd->encoding())); } ++ void vmuh_hu(FloatRegister vd, FloatRegister vj, FloatRegister vk) { ASSERT_LSX emit_int32(insn_RRR( vmuh_hu_op, (int)vk->encoding(), (int)vj->encoding(), (int)vd->encoding())); } ++ void vmuh_wu(FloatRegister vd, FloatRegister vj, FloatRegister vk) { ASSERT_LSX emit_int32(insn_RRR( vmuh_wu_op, (int)vk->encoding(), (int)vj->encoding(), (int)vd->encoding())); } ++ void vmuh_du(FloatRegister vd, FloatRegister vj, FloatRegister vk) { ASSERT_LSX emit_int32(insn_RRR( vmuh_du_op, (int)vk->encoding(), (int)vj->encoding(), (int)vd->encoding())); } ++ void xvmuh_bu(FloatRegister xd, FloatRegister xj, FloatRegister xk) { ASSERT_LASX emit_int32(insn_RRR(xvmuh_bu_op, (int)xk->encoding(), (int)xj->encoding(), (int)xd->encoding())); } ++ void xvmuh_hu(FloatRegister xd, FloatRegister xj, FloatRegister xk) { ASSERT_LASX emit_int32(insn_RRR(xvmuh_hu_op, (int)xk->encoding(), (int)xj->encoding(), (int)xd->encoding())); } ++ void xvmuh_wu(FloatRegister xd, FloatRegister xj, FloatRegister xk) { ASSERT_LASX emit_int32(insn_RRR(xvmuh_wu_op, (int)xk->encoding(), (int)xj->encoding(), (int)xd->encoding())); } ++ void xvmuh_du(FloatRegister xd, FloatRegister xj, FloatRegister xk) { ASSERT_LASX emit_int32(insn_RRR(xvmuh_du_op, (int)xk->encoding(), (int)xj->encoding(), (int)xd->encoding())); } ++ ++ void vmulwev_h_b(FloatRegister vd, FloatRegister vj, FloatRegister vk) { ASSERT_LSX emit_int32(insn_RRR( vmulwev_h_b_op, (int)vk->encoding(), (int)vj->encoding(), (int)vd->encoding())); } ++ void vmulwev_w_h(FloatRegister vd, FloatRegister vj, FloatRegister vk) { ASSERT_LSX emit_int32(insn_RRR( vmulwev_w_h_op, (int)vk->encoding(), (int)vj->encoding(), (int)vd->encoding())); } ++ void vmulwev_d_w(FloatRegister vd, FloatRegister vj, FloatRegister vk) { ASSERT_LSX emit_int32(insn_RRR( vmulwev_d_w_op, (int)vk->encoding(), (int)vj->encoding(), (int)vd->encoding())); } ++ void vmulwev_q_d(FloatRegister vd, FloatRegister vj, FloatRegister vk) { ASSERT_LSX emit_int32(insn_RRR( vmulwev_q_d_op, (int)vk->encoding(), (int)vj->encoding(), (int)vd->encoding())); } ++ void xvmulwev_h_b(FloatRegister xd, FloatRegister xj, FloatRegister xk) { ASSERT_LASX emit_int32(insn_RRR(xvmulwev_h_b_op, (int)xk->encoding(), (int)xj->encoding(), (int)xd->encoding())); } ++ void xvmulwev_w_h(FloatRegister xd, FloatRegister xj, FloatRegister xk) { ASSERT_LASX emit_int32(insn_RRR(xvmulwev_w_h_op, (int)xk->encoding(), (int)xj->encoding(), (int)xd->encoding())); } ++ void xvmulwev_d_w(FloatRegister xd, FloatRegister xj, FloatRegister xk) { ASSERT_LASX emit_int32(insn_RRR(xvmulwev_d_w_op, (int)xk->encoding(), (int)xj->encoding(), (int)xd->encoding())); } ++ void xvmulwev_q_d(FloatRegister xd, FloatRegister xj, FloatRegister xk) { ASSERT_LASX emit_int32(insn_RRR(xvmulwev_q_d_op, (int)xk->encoding(), (int)xj->encoding(), (int)xd->encoding())); } ++ ++ void vmulwod_h_b(FloatRegister vd, FloatRegister vj, FloatRegister vk) { ASSERT_LSX emit_int32(insn_RRR( vmulwod_h_b_op, (int)vk->encoding(), (int)vj->encoding(), (int)vd->encoding())); } ++ void vmulwod_w_h(FloatRegister vd, FloatRegister vj, FloatRegister vk) { ASSERT_LSX emit_int32(insn_RRR( vmulwod_w_h_op, (int)vk->encoding(), (int)vj->encoding(), (int)vd->encoding())); } ++ void vmulwod_d_w(FloatRegister vd, FloatRegister vj, FloatRegister vk) { ASSERT_LSX emit_int32(insn_RRR( vmulwod_d_w_op, (int)vk->encoding(), (int)vj->encoding(), (int)vd->encoding())); } ++ void vmulwod_q_d(FloatRegister vd, FloatRegister vj, FloatRegister vk) { ASSERT_LSX emit_int32(insn_RRR( vmulwod_q_d_op, (int)vk->encoding(), (int)vj->encoding(), (int)vd->encoding())); } ++ void xvmulwod_h_b(FloatRegister xd, FloatRegister xj, FloatRegister xk) { ASSERT_LASX emit_int32(insn_RRR(xvmulwod_h_b_op, (int)xk->encoding(), (int)xj->encoding(), (int)xd->encoding())); } ++ void xvmulwod_w_h(FloatRegister xd, FloatRegister xj, FloatRegister xk) { ASSERT_LASX emit_int32(insn_RRR(xvmulwod_w_h_op, (int)xk->encoding(), (int)xj->encoding(), (int)xd->encoding())); } ++ void xvmulwod_d_w(FloatRegister xd, FloatRegister xj, FloatRegister xk) { ASSERT_LASX emit_int32(insn_RRR(xvmulwod_d_w_op, (int)xk->encoding(), (int)xj->encoding(), (int)xd->encoding())); } ++ void xvmulwod_q_d(FloatRegister xd, FloatRegister xj, FloatRegister xk) { ASSERT_LASX emit_int32(insn_RRR(xvmulwod_q_d_op, (int)xk->encoding(), (int)xj->encoding(), (int)xd->encoding())); } ++ ++ void vmadd_b(FloatRegister vd, FloatRegister vj, FloatRegister vk) { ASSERT_LSX emit_int32(insn_RRR( vmadd_b_op, (int)vk->encoding(), (int)vj->encoding(), (int)vd->encoding())); } ++ void vmadd_h(FloatRegister vd, FloatRegister vj, FloatRegister vk) { ASSERT_LSX emit_int32(insn_RRR( vmadd_h_op, (int)vk->encoding(), (int)vj->encoding(), (int)vd->encoding())); } ++ void vmadd_w(FloatRegister vd, FloatRegister vj, FloatRegister vk) { ASSERT_LSX emit_int32(insn_RRR( vmadd_w_op, (int)vk->encoding(), (int)vj->encoding(), (int)vd->encoding())); } ++ void vmadd_d(FloatRegister vd, FloatRegister vj, FloatRegister vk) { ASSERT_LSX emit_int32(insn_RRR( vmadd_d_op, (int)vk->encoding(), (int)vj->encoding(), (int)vd->encoding())); } ++ void xvmadd_b(FloatRegister xd, FloatRegister xj, FloatRegister xk) { ASSERT_LASX emit_int32(insn_RRR(xvmadd_b_op, (int)xk->encoding(), (int)xj->encoding(), (int)xd->encoding())); } ++ void xvmadd_h(FloatRegister xd, FloatRegister xj, FloatRegister xk) { ASSERT_LASX emit_int32(insn_RRR(xvmadd_h_op, (int)xk->encoding(), (int)xj->encoding(), (int)xd->encoding())); } ++ void xvmadd_w(FloatRegister xd, FloatRegister xj, FloatRegister xk) { ASSERT_LASX emit_int32(insn_RRR(xvmadd_w_op, (int)xk->encoding(), (int)xj->encoding(), (int)xd->encoding())); } ++ void xvmadd_d(FloatRegister xd, FloatRegister xj, FloatRegister xk) { ASSERT_LASX emit_int32(insn_RRR(xvmadd_d_op, (int)xk->encoding(), (int)xj->encoding(), (int)xd->encoding())); } ++ ++ void vmsub_b(FloatRegister vd, FloatRegister vj, FloatRegister vk) { ASSERT_LSX emit_int32(insn_RRR( vmsub_b_op, (int)vk->encoding(), (int)vj->encoding(), (int)vd->encoding())); } ++ void vmsub_h(FloatRegister vd, FloatRegister vj, FloatRegister vk) { ASSERT_LSX emit_int32(insn_RRR( vmsub_h_op, (int)vk->encoding(), (int)vj->encoding(), (int)vd->encoding())); } ++ void vmsub_w(FloatRegister vd, FloatRegister vj, FloatRegister vk) { ASSERT_LSX emit_int32(insn_RRR( vmsub_w_op, (int)vk->encoding(), (int)vj->encoding(), (int)vd->encoding())); } ++ void vmsub_d(FloatRegister vd, FloatRegister vj, FloatRegister vk) { ASSERT_LSX emit_int32(insn_RRR( vmsub_d_op, (int)vk->encoding(), (int)vj->encoding(), (int)vd->encoding())); } ++ void xvmsub_b(FloatRegister xd, FloatRegister xj, FloatRegister xk) { ASSERT_LASX emit_int32(insn_RRR(xvmsub_b_op, (int)xk->encoding(), (int)xj->encoding(), (int)xd->encoding())); } ++ void xvmsub_h(FloatRegister xd, FloatRegister xj, FloatRegister xk) { ASSERT_LASX emit_int32(insn_RRR(xvmsub_h_op, (int)xk->encoding(), (int)xj->encoding(), (int)xd->encoding())); } ++ void xvmsub_w(FloatRegister xd, FloatRegister xj, FloatRegister xk) { ASSERT_LASX emit_int32(insn_RRR(xvmsub_w_op, (int)xk->encoding(), (int)xj->encoding(), (int)xd->encoding())); } ++ void xvmsub_d(FloatRegister xd, FloatRegister xj, FloatRegister xk) { ASSERT_LASX emit_int32(insn_RRR(xvmsub_d_op, (int)xk->encoding(), (int)xj->encoding(), (int)xd->encoding())); } ++ ++ void vext2xv_h_b(FloatRegister xd, FloatRegister xj) { ASSERT_LASX emit_int32(insn_RR(vext2xv_h_b_op, (int)xj->encoding(), (int)xd->encoding())); } ++ void vext2xv_w_b(FloatRegister xd, FloatRegister xj) { ASSERT_LASX emit_int32(insn_RR(vext2xv_w_b_op, (int)xj->encoding(), (int)xd->encoding())); } ++ void vext2xv_d_b(FloatRegister xd, FloatRegister xj) { ASSERT_LASX emit_int32(insn_RR(vext2xv_d_b_op, (int)xj->encoding(), (int)xd->encoding())); } ++ void vext2xv_w_h(FloatRegister xd, FloatRegister xj) { ASSERT_LASX emit_int32(insn_RR(vext2xv_w_h_op, (int)xj->encoding(), (int)xd->encoding())); } ++ void vext2xv_d_h(FloatRegister xd, FloatRegister xj) { ASSERT_LASX emit_int32(insn_RR(vext2xv_d_h_op, (int)xj->encoding(), (int)xd->encoding())); } ++ void vext2xv_d_w(FloatRegister xd, FloatRegister xj) { ASSERT_LASX emit_int32(insn_RR(vext2xv_d_w_op, (int)xj->encoding(), (int)xd->encoding())); } ++ ++ void vext2xv_hu_bu(FloatRegister xd, FloatRegister xj) { ASSERT_LASX emit_int32(insn_RR(vext2xv_hu_bu_op, (int)xj->encoding(), (int)xd->encoding())); } ++ void vext2xv_wu_bu(FloatRegister xd, FloatRegister xj) { ASSERT_LASX emit_int32(insn_RR(vext2xv_wu_bu_op, (int)xj->encoding(), (int)xd->encoding())); } ++ void vext2xv_du_bu(FloatRegister xd, FloatRegister xj) { ASSERT_LASX emit_int32(insn_RR(vext2xv_du_bu_op, (int)xj->encoding(), (int)xd->encoding())); } ++ void vext2xv_wu_hu(FloatRegister xd, FloatRegister xj) { ASSERT_LASX emit_int32(insn_RR(vext2xv_wu_hu_op, (int)xj->encoding(), (int)xd->encoding())); } ++ void vext2xv_du_hu(FloatRegister xd, FloatRegister xj) { ASSERT_LASX emit_int32(insn_RR(vext2xv_du_hu_op, (int)xj->encoding(), (int)xd->encoding())); } ++ void vext2xv_du_wu(FloatRegister xd, FloatRegister xj) { ASSERT_LASX emit_int32(insn_RR(vext2xv_du_wu_op, (int)xj->encoding(), (int)xd->encoding())); } ++ ++ void vldi(FloatRegister vd, int i13) { ASSERT_LSX emit_int32(insn_I13R( vldi_op, i13, (int)vd->encoding())); } ++ void xvldi(FloatRegister xd, int i13) { ASSERT_LASX emit_int32(insn_I13R(xvldi_op, i13, (int)xd->encoding())); } ++ ++ void vand_v(FloatRegister vd, FloatRegister vj, FloatRegister vk) { ASSERT_LSX emit_int32(insn_RRR( vand_v_op, (int)vk->encoding(), (int)vj->encoding(), (int)vd->encoding())); } ++ void xvand_v(FloatRegister xd, FloatRegister xj, FloatRegister xk) { ASSERT_LASX emit_int32(insn_RRR(xvand_v_op, (int)xk->encoding(), (int)xj->encoding(), (int)xd->encoding())); } ++ ++ void vor_v(FloatRegister vd, FloatRegister vj, FloatRegister vk) { ASSERT_LSX emit_int32(insn_RRR( vor_v_op, (int)vk->encoding(), (int)vj->encoding(), (int)vd->encoding())); } ++ void xvor_v(FloatRegister xd, FloatRegister xj, FloatRegister xk) { ASSERT_LASX emit_int32(insn_RRR(xvor_v_op, (int)xk->encoding(), (int)xj->encoding(), (int)xd->encoding())); } ++ ++ void vxor_v(FloatRegister vd, FloatRegister vj, FloatRegister vk) { ASSERT_LSX emit_int32(insn_RRR( vxor_v_op, (int)vk->encoding(), (int)vj->encoding(), (int)vd->encoding())); } ++ void xvxor_v(FloatRegister xd, FloatRegister xj, FloatRegister xk) { ASSERT_LASX emit_int32(insn_RRR(xvxor_v_op, (int)xk->encoding(), (int)xj->encoding(), (int)xd->encoding())); } ++ ++ void vnor_v(FloatRegister vd, FloatRegister vj, FloatRegister vk) { ASSERT_LSX emit_int32(insn_RRR( vnor_v_op, (int)vk->encoding(), (int)vj->encoding(), (int)vd->encoding())); } ++ void xvnor_v(FloatRegister xd, FloatRegister xj, FloatRegister xk) { ASSERT_LASX emit_int32(insn_RRR(xvnor_v_op, (int)xk->encoding(), (int)xj->encoding(), (int)xd->encoding())); } ++ ++ void vandn_v(FloatRegister vd, FloatRegister vj, FloatRegister vk) { ASSERT_LSX emit_int32(insn_RRR( vandn_v_op, (int)vk->encoding(), (int)vj->encoding(), (int)vd->encoding())); } ++ void xvandn_v(FloatRegister xd, FloatRegister xj, FloatRegister xk) { ASSERT_LASX emit_int32(insn_RRR(xvandn_v_op, (int)xk->encoding(), (int)xj->encoding(), (int)xd->encoding())); } ++ ++ void vorn_v(FloatRegister vd, FloatRegister vj, FloatRegister vk) { ASSERT_LSX emit_int32(insn_RRR( vorn_v_op, (int)vk->encoding(), (int)vj->encoding(), (int)vd->encoding())); } ++ void xvorn_v(FloatRegister xd, FloatRegister xj, FloatRegister xk) { ASSERT_LASX emit_int32(insn_RRR(xvorn_v_op, (int)xk->encoding(), (int)xj->encoding(), (int)xd->encoding())); } ++ ++ void vandi_b(FloatRegister vd, FloatRegister vj, int ui8) { ASSERT_LSX assert(is_uimm(ui8, 8), "not a unsigned 8-bit int"); emit_int32(insn_I8RR( vandi_b_op, ui8, (int)vj->encoding(), (int)vd->encoding())); } ++ void xvandi_b(FloatRegister xd, FloatRegister xj, int ui8) { ASSERT_LASX assert(is_uimm(ui8, 8), "not a unsigned 8-bit int"); emit_int32(insn_I8RR(xvandi_b_op, ui8, (int)xj->encoding(), (int)xd->encoding())); } ++ ++ void vori_b(FloatRegister vd, FloatRegister vj, int ui8) { ASSERT_LSX assert(is_uimm(ui8, 8), "not a unsigned 8-bit int"); emit_int32(insn_I8RR( vori_b_op, ui8, (int)vj->encoding(), (int)vd->encoding())); } ++ void xvori_b(FloatRegister xd, FloatRegister xj, int ui8) { ASSERT_LASX assert(is_uimm(ui8, 8), "not a unsigned 8-bit int"); emit_int32(insn_I8RR(xvori_b_op, ui8, (int)xj->encoding(), (int)xd->encoding())); } ++ ++ void vxori_b(FloatRegister vd, FloatRegister vj, int ui8) { ASSERT_LSX assert(is_uimm(ui8, 8), "not a unsigned 8-bit int"); emit_int32(insn_I8RR( vxori_b_op, ui8, (int)vj->encoding(), (int)vd->encoding())); } ++ void xvxori_b(FloatRegister xd, FloatRegister xj, int ui8) { ASSERT_LASX assert(is_uimm(ui8, 8), "not a unsigned 8-bit int"); emit_int32(insn_I8RR(xvxori_b_op, ui8, (int)xj->encoding(), (int)xd->encoding())); } ++ ++ void vnori_b(FloatRegister vd, FloatRegister vj, int ui8) { ASSERT_LSX assert(is_uimm(ui8, 8), "not a unsigned 8-bit int"); emit_int32(insn_I8RR( vnori_b_op, ui8, (int)vj->encoding(), (int)vd->encoding())); } ++ void xvnori_b(FloatRegister xd, FloatRegister xj, int ui8) { ASSERT_LASX assert(is_uimm(ui8, 8), "not a unsigned 8-bit int"); emit_int32(insn_I8RR(xvnori_b_op, ui8, (int)xj->encoding(), (int)xd->encoding())); } ++ ++ void vsll_b(FloatRegister vd, FloatRegister vj, FloatRegister vk) { ASSERT_LSX emit_int32(insn_RRR( vsll_b_op, (int)vk->encoding(), (int)vj->encoding(), (int)vd->encoding())); } ++ void vsll_h(FloatRegister vd, FloatRegister vj, FloatRegister vk) { ASSERT_LSX emit_int32(insn_RRR( vsll_h_op, (int)vk->encoding(), (int)vj->encoding(), (int)vd->encoding())); } ++ void vsll_w(FloatRegister vd, FloatRegister vj, FloatRegister vk) { ASSERT_LSX emit_int32(insn_RRR( vsll_w_op, (int)vk->encoding(), (int)vj->encoding(), (int)vd->encoding())); } ++ void vsll_d(FloatRegister vd, FloatRegister vj, FloatRegister vk) { ASSERT_LSX emit_int32(insn_RRR( vsll_d_op, (int)vk->encoding(), (int)vj->encoding(), (int)vd->encoding())); } ++ void xvsll_b(FloatRegister xd, FloatRegister xj, FloatRegister xk) { ASSERT_LASX emit_int32(insn_RRR(xvsll_b_op, (int)xk->encoding(), (int)xj->encoding(), (int)xd->encoding())); } ++ void xvsll_h(FloatRegister xd, FloatRegister xj, FloatRegister xk) { ASSERT_LASX emit_int32(insn_RRR(xvsll_h_op, (int)xk->encoding(), (int)xj->encoding(), (int)xd->encoding())); } ++ void xvsll_w(FloatRegister xd, FloatRegister xj, FloatRegister xk) { ASSERT_LASX emit_int32(insn_RRR(xvsll_w_op, (int)xk->encoding(), (int)xj->encoding(), (int)xd->encoding())); } ++ void xvsll_d(FloatRegister xd, FloatRegister xj, FloatRegister xk) { ASSERT_LASX emit_int32(insn_RRR(xvsll_d_op, (int)xk->encoding(), (int)xj->encoding(), (int)xd->encoding())); } ++ ++ void vslli_b(FloatRegister vd, FloatRegister vj, int ui3) { ASSERT_LSX emit_int32(insn_I3RR( vslli_b_op, ui3, (int)vj->encoding(), (int)vd->encoding())); } ++ void vslli_h(FloatRegister vd, FloatRegister vj, int ui4) { ASSERT_LSX emit_int32(insn_I4RR( vslli_h_op, ui4, (int)vj->encoding(), (int)vd->encoding())); } ++ void vslli_w(FloatRegister vd, FloatRegister vj, int ui5) { ASSERT_LSX emit_int32(insn_I5RR( vslli_w_op, ui5, (int)vj->encoding(), (int)vd->encoding())); } ++ void vslli_d(FloatRegister vd, FloatRegister vj, int ui6) { ASSERT_LSX emit_int32(insn_I6RR( vslli_d_op, ui6, (int)vj->encoding(), (int)vd->encoding())); } ++ void xvslli_b(FloatRegister xd, FloatRegister xj, int ui3) { ASSERT_LASX emit_int32(insn_I3RR(xvslli_b_op, ui3, (int)xj->encoding(), (int)xd->encoding())); } ++ void xvslli_h(FloatRegister xd, FloatRegister xj, int ui4) { ASSERT_LASX emit_int32(insn_I4RR(xvslli_h_op, ui4, (int)xj->encoding(), (int)xd->encoding())); } ++ void xvslli_w(FloatRegister xd, FloatRegister xj, int ui5) { ASSERT_LASX emit_int32(insn_I5RR(xvslli_w_op, ui5, (int)xj->encoding(), (int)xd->encoding())); } ++ void xvslli_d(FloatRegister xd, FloatRegister xj, int ui6) { ASSERT_LASX emit_int32(insn_I6RR(xvslli_d_op, ui6, (int)xj->encoding(), (int)xd->encoding())); } ++ ++ void vsrl_b(FloatRegister vd, FloatRegister vj, FloatRegister vk) { ASSERT_LSX emit_int32(insn_RRR( vsrl_b_op, (int)vk->encoding(), (int)vj->encoding(), (int)vd->encoding())); } ++ void vsrl_h(FloatRegister vd, FloatRegister vj, FloatRegister vk) { ASSERT_LSX emit_int32(insn_RRR( vsrl_h_op, (int)vk->encoding(), (int)vj->encoding(), (int)vd->encoding())); } ++ void vsrl_w(FloatRegister vd, FloatRegister vj, FloatRegister vk) { ASSERT_LSX emit_int32(insn_RRR( vsrl_w_op, (int)vk->encoding(), (int)vj->encoding(), (int)vd->encoding())); } ++ void vsrl_d(FloatRegister vd, FloatRegister vj, FloatRegister vk) { ASSERT_LSX emit_int32(insn_RRR( vsrl_d_op, (int)vk->encoding(), (int)vj->encoding(), (int)vd->encoding())); } ++ void xvsrl_b(FloatRegister xd, FloatRegister xj, FloatRegister xk) { ASSERT_LASX emit_int32(insn_RRR(xvsrl_b_op, (int)xk->encoding(), (int)xj->encoding(), (int)xd->encoding())); } ++ void xvsrl_h(FloatRegister xd, FloatRegister xj, FloatRegister xk) { ASSERT_LASX emit_int32(insn_RRR(xvsrl_h_op, (int)xk->encoding(), (int)xj->encoding(), (int)xd->encoding())); } ++ void xvsrl_w(FloatRegister xd, FloatRegister xj, FloatRegister xk) { ASSERT_LASX emit_int32(insn_RRR(xvsrl_w_op, (int)xk->encoding(), (int)xj->encoding(), (int)xd->encoding())); } ++ void xvsrl_d(FloatRegister xd, FloatRegister xj, FloatRegister xk) { ASSERT_LASX emit_int32(insn_RRR(xvsrl_d_op, (int)xk->encoding(), (int)xj->encoding(), (int)xd->encoding())); } ++ ++ void vsrli_b(FloatRegister vd, FloatRegister vj, int ui3) { ASSERT_LSX emit_int32(insn_I3RR( vsrli_b_op, ui3, (int)vj->encoding(), (int)vd->encoding())); } ++ void vsrli_h(FloatRegister vd, FloatRegister vj, int ui4) { ASSERT_LSX emit_int32(insn_I4RR( vsrli_h_op, ui4, (int)vj->encoding(), (int)vd->encoding())); } ++ void vsrli_w(FloatRegister vd, FloatRegister vj, int ui5) { ASSERT_LSX emit_int32(insn_I5RR( vsrli_w_op, ui5, (int)vj->encoding(), (int)vd->encoding())); } ++ void vsrli_d(FloatRegister vd, FloatRegister vj, int ui6) { ASSERT_LSX emit_int32(insn_I6RR( vsrli_d_op, ui6, (int)vj->encoding(), (int)vd->encoding())); } ++ void xvsrli_b(FloatRegister xd, FloatRegister xj, int ui3) { ASSERT_LASX emit_int32(insn_I3RR(xvsrli_b_op, ui3, (int)xj->encoding(), (int)xd->encoding())); } ++ void xvsrli_h(FloatRegister xd, FloatRegister xj, int ui4) { ASSERT_LASX emit_int32(insn_I4RR(xvsrli_h_op, ui4, (int)xj->encoding(), (int)xd->encoding())); } ++ void xvsrli_w(FloatRegister xd, FloatRegister xj, int ui5) { ASSERT_LASX emit_int32(insn_I5RR(xvsrli_w_op, ui5, (int)xj->encoding(), (int)xd->encoding())); } ++ void xvsrli_d(FloatRegister xd, FloatRegister xj, int ui6) { ASSERT_LASX emit_int32(insn_I6RR(xvsrli_d_op, ui6, (int)xj->encoding(), (int)xd->encoding())); } ++ ++ void vsra_b(FloatRegister vd, FloatRegister vj, FloatRegister vk) { ASSERT_LSX emit_int32(insn_RRR( vsra_b_op, (int)vk->encoding(), (int)vj->encoding(), (int)vd->encoding())); } ++ void vsra_h(FloatRegister vd, FloatRegister vj, FloatRegister vk) { ASSERT_LSX emit_int32(insn_RRR( vsra_h_op, (int)vk->encoding(), (int)vj->encoding(), (int)vd->encoding())); } ++ void vsra_w(FloatRegister vd, FloatRegister vj, FloatRegister vk) { ASSERT_LSX emit_int32(insn_RRR( vsra_w_op, (int)vk->encoding(), (int)vj->encoding(), (int)vd->encoding())); } ++ void vsra_d(FloatRegister vd, FloatRegister vj, FloatRegister vk) { ASSERT_LSX emit_int32(insn_RRR( vsra_d_op, (int)vk->encoding(), (int)vj->encoding(), (int)vd->encoding())); } ++ void xvsra_b(FloatRegister xd, FloatRegister xj, FloatRegister xk) { ASSERT_LASX emit_int32(insn_RRR(xvsra_b_op, (int)xk->encoding(), (int)xj->encoding(), (int)xd->encoding())); } ++ void xvsra_h(FloatRegister xd, FloatRegister xj, FloatRegister xk) { ASSERT_LASX emit_int32(insn_RRR(xvsra_h_op, (int)xk->encoding(), (int)xj->encoding(), (int)xd->encoding())); } ++ void xvsra_w(FloatRegister xd, FloatRegister xj, FloatRegister xk) { ASSERT_LASX emit_int32(insn_RRR(xvsra_w_op, (int)xk->encoding(), (int)xj->encoding(), (int)xd->encoding())); } ++ void xvsra_d(FloatRegister xd, FloatRegister xj, FloatRegister xk) { ASSERT_LASX emit_int32(insn_RRR(xvsra_d_op, (int)xk->encoding(), (int)xj->encoding(), (int)xd->encoding())); } ++ ++ void vsrai_b(FloatRegister vd, FloatRegister vj, int ui3) { ASSERT_LSX emit_int32(insn_I3RR( vsrai_b_op, ui3, (int)vj->encoding(), (int)vd->encoding())); } ++ void vsrai_h(FloatRegister vd, FloatRegister vj, int ui4) { ASSERT_LSX emit_int32(insn_I4RR( vsrai_h_op, ui4, (int)vj->encoding(), (int)vd->encoding())); } ++ void vsrai_w(FloatRegister vd, FloatRegister vj, int ui5) { ASSERT_LSX emit_int32(insn_I5RR( vsrai_w_op, ui5, (int)vj->encoding(), (int)vd->encoding())); } ++ void vsrai_d(FloatRegister vd, FloatRegister vj, int ui6) { ASSERT_LSX emit_int32(insn_I6RR( vsrai_d_op, ui6, (int)vj->encoding(), (int)vd->encoding())); } ++ void xvsrai_b(FloatRegister xd, FloatRegister xj, int ui3) { ASSERT_LASX emit_int32(insn_I3RR(xvsrai_b_op, ui3, (int)xj->encoding(), (int)xd->encoding())); } ++ void xvsrai_h(FloatRegister xd, FloatRegister xj, int ui4) { ASSERT_LASX emit_int32(insn_I4RR(xvsrai_h_op, ui4, (int)xj->encoding(), (int)xd->encoding())); } ++ void xvsrai_w(FloatRegister xd, FloatRegister xj, int ui5) { ASSERT_LASX emit_int32(insn_I5RR(xvsrai_w_op, ui5, (int)xj->encoding(), (int)xd->encoding())); } ++ void xvsrai_d(FloatRegister xd, FloatRegister xj, int ui6) { ASSERT_LASX emit_int32(insn_I6RR(xvsrai_d_op, ui6, (int)xj->encoding(), (int)xd->encoding())); } ++ ++ void vrotr_b(FloatRegister vd, FloatRegister vj, FloatRegister vk) { ASSERT_LSX emit_int32(insn_RRR( vrotr_b_op, (int)vk->encoding(), (int)vj->encoding(), (int)vd->encoding())); } ++ void vrotr_h(FloatRegister vd, FloatRegister vj, FloatRegister vk) { ASSERT_LSX emit_int32(insn_RRR( vrotr_h_op, (int)vk->encoding(), (int)vj->encoding(), (int)vd->encoding())); } ++ void vrotr_w(FloatRegister vd, FloatRegister vj, FloatRegister vk) { ASSERT_LSX emit_int32(insn_RRR( vrotr_w_op, (int)vk->encoding(), (int)vj->encoding(), (int)vd->encoding())); } ++ void vrotr_d(FloatRegister vd, FloatRegister vj, FloatRegister vk) { ASSERT_LSX emit_int32(insn_RRR( vrotr_d_op, (int)vk->encoding(), (int)vj->encoding(), (int)vd->encoding())); } ++ void xvrotr_b(FloatRegister xd, FloatRegister xj, FloatRegister xk) { ASSERT_LASX emit_int32(insn_RRR(xvrotr_b_op, (int)xk->encoding(), (int)xj->encoding(), (int)xd->encoding())); } ++ void xvrotr_h(FloatRegister xd, FloatRegister xj, FloatRegister xk) { ASSERT_LASX emit_int32(insn_RRR(xvrotr_h_op, (int)xk->encoding(), (int)xj->encoding(), (int)xd->encoding())); } ++ void xvrotr_w(FloatRegister xd, FloatRegister xj, FloatRegister xk) { ASSERT_LASX emit_int32(insn_RRR(xvrotr_w_op, (int)xk->encoding(), (int)xj->encoding(), (int)xd->encoding())); } ++ void xvrotr_d(FloatRegister xd, FloatRegister xj, FloatRegister xk) { ASSERT_LASX emit_int32(insn_RRR(xvrotr_d_op, (int)xk->encoding(), (int)xj->encoding(), (int)xd->encoding())); } ++ ++ void vrotri_b(FloatRegister vd, FloatRegister vj, int ui3) { ASSERT_LSX emit_int32(insn_I3RR( vrotri_b_op, ui3, (int)vj->encoding(), (int)vd->encoding())); } ++ void vrotri_h(FloatRegister vd, FloatRegister vj, int ui4) { ASSERT_LSX emit_int32(insn_I4RR( vrotri_h_op, ui4, (int)vj->encoding(), (int)vd->encoding())); } ++ void vrotri_w(FloatRegister vd, FloatRegister vj, int ui5) { ASSERT_LSX emit_int32(insn_I5RR( vrotri_w_op, ui5, (int)vj->encoding(), (int)vd->encoding())); } ++ void vrotri_d(FloatRegister vd, FloatRegister vj, int ui6) { ASSERT_LSX emit_int32(insn_I6RR( vrotri_d_op, ui6, (int)vj->encoding(), (int)vd->encoding())); } ++ void xvrotri_b(FloatRegister xd, FloatRegister xj, int ui3) { ASSERT_LASX emit_int32(insn_I3RR(xvrotri_b_op, ui3, (int)xj->encoding(), (int)xd->encoding())); } ++ void xvrotri_h(FloatRegister xd, FloatRegister xj, int ui4) { ASSERT_LASX emit_int32(insn_I4RR(xvrotri_h_op, ui4, (int)xj->encoding(), (int)xd->encoding())); } ++ void xvrotri_w(FloatRegister xd, FloatRegister xj, int ui5) { ASSERT_LASX emit_int32(insn_I5RR(xvrotri_w_op, ui5, (int)xj->encoding(), (int)xd->encoding())); } ++ void xvrotri_d(FloatRegister xd, FloatRegister xj, int ui6) { ASSERT_LASX emit_int32(insn_I6RR(xvrotri_d_op, ui6, (int)xj->encoding(), (int)xd->encoding())); } ++ ++ void vsrlni_b_h(FloatRegister vd, FloatRegister vj, int ui4) { ASSERT_LSX emit_int32(insn_I4RR( vsrlni_b_h_op, ui4, (int)vj->encoding(), (int)vd->encoding())); } ++ void vsrlni_h_w(FloatRegister vd, FloatRegister vj, int ui5) { ASSERT_LSX emit_int32(insn_I5RR( vsrlni_h_w_op, ui5, (int)vj->encoding(), (int)vd->encoding())); } ++ void vsrlni_w_d(FloatRegister vd, FloatRegister vj, int ui6) { ASSERT_LSX emit_int32(insn_I6RR( vsrlni_w_d_op, ui6, (int)vj->encoding(), (int)vd->encoding())); } ++ void vsrlni_d_q(FloatRegister vd, FloatRegister vj, int ui7) { ASSERT_LSX emit_int32(insn_I7RR( vsrlni_d_q_op, ui7, (int)vj->encoding(), (int)vd->encoding())); } ++ ++ void vpcnt_b(FloatRegister vd, FloatRegister vj) { ASSERT_LSX emit_int32(insn_RR( vpcnt_b_op, (int)vj->encoding(), (int)vd->encoding())); } ++ void vpcnt_h(FloatRegister vd, FloatRegister vj) { ASSERT_LSX emit_int32(insn_RR( vpcnt_h_op, (int)vj->encoding(), (int)vd->encoding())); } ++ void vpcnt_w(FloatRegister vd, FloatRegister vj) { ASSERT_LSX emit_int32(insn_RR( vpcnt_w_op, (int)vj->encoding(), (int)vd->encoding())); } ++ void vpcnt_d(FloatRegister vd, FloatRegister vj) { ASSERT_LSX emit_int32(insn_RR( vpcnt_d_op, (int)vj->encoding(), (int)vd->encoding())); } ++ void xvpcnt_b(FloatRegister xd, FloatRegister xj) { ASSERT_LASX emit_int32(insn_RR(xvpcnt_b_op, (int)xj->encoding(), (int)xd->encoding())); } ++ void xvpcnt_h(FloatRegister xd, FloatRegister xj) { ASSERT_LASX emit_int32(insn_RR(xvpcnt_h_op, (int)xj->encoding(), (int)xd->encoding())); } ++ void xvpcnt_w(FloatRegister xd, FloatRegister xj) { ASSERT_LASX emit_int32(insn_RR(xvpcnt_w_op, (int)xj->encoding(), (int)xd->encoding())); } ++ void xvpcnt_d(FloatRegister xd, FloatRegister xj) { ASSERT_LASX emit_int32(insn_RR(xvpcnt_d_op, (int)xj->encoding(), (int)xd->encoding())); } ++ ++ void vbitclr_b(FloatRegister vd, FloatRegister vj, FloatRegister vk) { ASSERT_LSX emit_int32(insn_RRR( vbitclr_b_op, (int)vk->encoding(), (int)vj->encoding(), (int)vd->encoding())); } ++ void vbitclr_h(FloatRegister vd, FloatRegister vj, FloatRegister vk) { ASSERT_LSX emit_int32(insn_RRR( vbitclr_h_op, (int)vk->encoding(), (int)vj->encoding(), (int)vd->encoding())); } ++ void vbitclr_w(FloatRegister vd, FloatRegister vj, FloatRegister vk) { ASSERT_LSX emit_int32(insn_RRR( vbitclr_w_op, (int)vk->encoding(), (int)vj->encoding(), (int)vd->encoding())); } ++ void vbitclr_d(FloatRegister vd, FloatRegister vj, FloatRegister vk) { ASSERT_LSX emit_int32(insn_RRR( vbitclr_d_op, (int)vk->encoding(), (int)vj->encoding(), (int)vd->encoding())); } ++ void xvbitclr_b(FloatRegister xd, FloatRegister xj, FloatRegister xk) { ASSERT_LASX emit_int32(insn_RRR(xvbitclr_b_op, (int)xk->encoding(), (int)xj->encoding(), (int)xd->encoding())); } ++ void xvbitclr_h(FloatRegister xd, FloatRegister xj, FloatRegister xk) { ASSERT_LASX emit_int32(insn_RRR(xvbitclr_h_op, (int)xk->encoding(), (int)xj->encoding(), (int)xd->encoding())); } ++ void xvbitclr_w(FloatRegister xd, FloatRegister xj, FloatRegister xk) { ASSERT_LASX emit_int32(insn_RRR(xvbitclr_w_op, (int)xk->encoding(), (int)xj->encoding(), (int)xd->encoding())); } ++ void xvbitclr_d(FloatRegister xd, FloatRegister xj, FloatRegister xk) { ASSERT_LASX emit_int32(insn_RRR(xvbitclr_d_op, (int)xk->encoding(), (int)xj->encoding(), (int)xd->encoding())); } ++ ++ void vbitclri_b(FloatRegister vd, FloatRegister vj, int ui3) { ASSERT_LSX emit_int32(insn_I3RR( vbitclri_b_op, ui3, (int)vj->encoding(), (int)vd->encoding())); } ++ void vbitclri_h(FloatRegister vd, FloatRegister vj, int ui4) { ASSERT_LSX emit_int32(insn_I4RR( vbitclri_h_op, ui4, (int)vj->encoding(), (int)vd->encoding())); } ++ void vbitclri_w(FloatRegister vd, FloatRegister vj, int ui5) { ASSERT_LSX emit_int32(insn_I5RR( vbitclri_w_op, ui5, (int)vj->encoding(), (int)vd->encoding())); } ++ void vbitclri_d(FloatRegister vd, FloatRegister vj, int ui6) { ASSERT_LSX emit_int32(insn_I6RR( vbitclri_d_op, ui6, (int)vj->encoding(), (int)vd->encoding())); } ++ void xvbitclri_b(FloatRegister xd, FloatRegister xj, int ui3) { ASSERT_LASX emit_int32(insn_I3RR(xvbitclri_b_op, ui3, (int)xj->encoding(), (int)xd->encoding())); } ++ void xvbitclri_h(FloatRegister xd, FloatRegister xj, int ui4) { ASSERT_LASX emit_int32(insn_I4RR(xvbitclri_h_op, ui4, (int)xj->encoding(), (int)xd->encoding())); } ++ void xvbitclri_w(FloatRegister xd, FloatRegister xj, int ui5) { ASSERT_LASX emit_int32(insn_I5RR(xvbitclri_w_op, ui5, (int)xj->encoding(), (int)xd->encoding())); } ++ void xvbitclri_d(FloatRegister xd, FloatRegister xj, int ui6) { ASSERT_LASX emit_int32(insn_I6RR(xvbitclri_d_op, ui6, (int)xj->encoding(), (int)xd->encoding())); } ++ ++ void vbitset_b(FloatRegister vd, FloatRegister vj, FloatRegister vk) { ASSERT_LSX emit_int32(insn_RRR( vbitset_b_op, (int)vk->encoding(), (int)vj->encoding(), (int)vd->encoding())); } ++ void vbitset_h(FloatRegister vd, FloatRegister vj, FloatRegister vk) { ASSERT_LSX emit_int32(insn_RRR( vbitset_h_op, (int)vk->encoding(), (int)vj->encoding(), (int)vd->encoding())); } ++ void vbitset_w(FloatRegister vd, FloatRegister vj, FloatRegister vk) { ASSERT_LSX emit_int32(insn_RRR( vbitset_w_op, (int)vk->encoding(), (int)vj->encoding(), (int)vd->encoding())); } ++ void vbitset_d(FloatRegister vd, FloatRegister vj, FloatRegister vk) { ASSERT_LSX emit_int32(insn_RRR( vbitset_d_op, (int)vk->encoding(), (int)vj->encoding(), (int)vd->encoding())); } ++ void xvbitset_b(FloatRegister xd, FloatRegister xj, FloatRegister xk) { ASSERT_LASX emit_int32(insn_RRR(xvbitset_b_op, (int)xk->encoding(), (int)xj->encoding(), (int)xd->encoding())); } ++ void xvbitset_h(FloatRegister xd, FloatRegister xj, FloatRegister xk) { ASSERT_LASX emit_int32(insn_RRR(xvbitset_h_op, (int)xk->encoding(), (int)xj->encoding(), (int)xd->encoding())); } ++ void xvbitset_w(FloatRegister xd, FloatRegister xj, FloatRegister xk) { ASSERT_LASX emit_int32(insn_RRR(xvbitset_w_op, (int)xk->encoding(), (int)xj->encoding(), (int)xd->encoding())); } ++ void xvbitset_d(FloatRegister xd, FloatRegister xj, FloatRegister xk) { ASSERT_LASX emit_int32(insn_RRR(xvbitset_d_op, (int)xk->encoding(), (int)xj->encoding(), (int)xd->encoding())); } ++ ++ void vbitseti_b(FloatRegister vd, FloatRegister vj, int ui3) { ASSERT_LSX emit_int32(insn_I3RR( vbitseti_b_op, ui3, (int)vj->encoding(), (int)vd->encoding())); } ++ void vbitseti_h(FloatRegister vd, FloatRegister vj, int ui4) { ASSERT_LSX emit_int32(insn_I4RR( vbitseti_h_op, ui4, (int)vj->encoding(), (int)vd->encoding())); } ++ void vbitseti_w(FloatRegister vd, FloatRegister vj, int ui5) { ASSERT_LSX emit_int32(insn_I5RR( vbitseti_w_op, ui5, (int)vj->encoding(), (int)vd->encoding())); } ++ void vbitseti_d(FloatRegister vd, FloatRegister vj, int ui6) { ASSERT_LSX emit_int32(insn_I6RR( vbitseti_d_op, ui6, (int)vj->encoding(), (int)vd->encoding())); } ++ void xvbitseti_b(FloatRegister xd, FloatRegister xj, int ui3) { ASSERT_LASX emit_int32(insn_I3RR(xvbitseti_b_op, ui3, (int)xj->encoding(), (int)xd->encoding())); } ++ void xvbitseti_h(FloatRegister xd, FloatRegister xj, int ui4) { ASSERT_LASX emit_int32(insn_I4RR(xvbitseti_h_op, ui4, (int)xj->encoding(), (int)xd->encoding())); } ++ void xvbitseti_w(FloatRegister xd, FloatRegister xj, int ui5) { ASSERT_LASX emit_int32(insn_I5RR(xvbitseti_w_op, ui5, (int)xj->encoding(), (int)xd->encoding())); } ++ void xvbitseti_d(FloatRegister xd, FloatRegister xj, int ui6) { ASSERT_LASX emit_int32(insn_I6RR(xvbitseti_d_op, ui6, (int)xj->encoding(), (int)xd->encoding())); } ++ ++ void vbitrev_b(FloatRegister vd, FloatRegister vj, FloatRegister vk) { ASSERT_LSX emit_int32(insn_RRR( vbitrev_b_op, (int)vk->encoding(), (int)vj->encoding(), (int)vd->encoding())); } ++ void vbitrev_h(FloatRegister vd, FloatRegister vj, FloatRegister vk) { ASSERT_LSX emit_int32(insn_RRR( vbitrev_h_op, (int)vk->encoding(), (int)vj->encoding(), (int)vd->encoding())); } ++ void vbitrev_w(FloatRegister vd, FloatRegister vj, FloatRegister vk) { ASSERT_LSX emit_int32(insn_RRR( vbitrev_w_op, (int)vk->encoding(), (int)vj->encoding(), (int)vd->encoding())); } ++ void vbitrev_d(FloatRegister vd, FloatRegister vj, FloatRegister vk) { ASSERT_LSX emit_int32(insn_RRR( vbitrev_d_op, (int)vk->encoding(), (int)vj->encoding(), (int)vd->encoding())); } ++ void xvbitrev_b(FloatRegister xd, FloatRegister xj, FloatRegister xk) { ASSERT_LASX emit_int32(insn_RRR(xvbitrev_b_op, (int)xk->encoding(), (int)xj->encoding(), (int)xd->encoding())); } ++ void xvbitrev_h(FloatRegister xd, FloatRegister xj, FloatRegister xk) { ASSERT_LASX emit_int32(insn_RRR(xvbitrev_h_op, (int)xk->encoding(), (int)xj->encoding(), (int)xd->encoding())); } ++ void xvbitrev_w(FloatRegister xd, FloatRegister xj, FloatRegister xk) { ASSERT_LASX emit_int32(insn_RRR(xvbitrev_w_op, (int)xk->encoding(), (int)xj->encoding(), (int)xd->encoding())); } ++ void xvbitrev_d(FloatRegister xd, FloatRegister xj, FloatRegister xk) { ASSERT_LASX emit_int32(insn_RRR(xvbitrev_d_op, (int)xk->encoding(), (int)xj->encoding(), (int)xd->encoding())); } ++ ++ void vbitrevi_b(FloatRegister vd, FloatRegister vj, int ui3) { ASSERT_LSX emit_int32(insn_I3RR( vbitrevi_b_op, ui3, (int)vj->encoding(), (int)vd->encoding())); } ++ void vbitrevi_h(FloatRegister vd, FloatRegister vj, int ui4) { ASSERT_LSX emit_int32(insn_I4RR( vbitrevi_h_op, ui4, (int)vj->encoding(), (int)vd->encoding())); } ++ void vbitrevi_w(FloatRegister vd, FloatRegister vj, int ui5) { ASSERT_LSX emit_int32(insn_I5RR( vbitrevi_w_op, ui5, (int)vj->encoding(), (int)vd->encoding())); } ++ void vbitrevi_d(FloatRegister vd, FloatRegister vj, int ui6) { ASSERT_LSX emit_int32(insn_I6RR( vbitrevi_d_op, ui6, (int)vj->encoding(), (int)vd->encoding())); } ++ void xvbitrevi_b(FloatRegister xd, FloatRegister xj, int ui3) { ASSERT_LASX emit_int32(insn_I3RR(xvbitrevi_b_op, ui3, (int)xj->encoding(), (int)xd->encoding())); } ++ void xvbitrevi_h(FloatRegister xd, FloatRegister xj, int ui4) { ASSERT_LASX emit_int32(insn_I4RR(xvbitrevi_h_op, ui4, (int)xj->encoding(), (int)xd->encoding())); } ++ void xvbitrevi_w(FloatRegister xd, FloatRegister xj, int ui5) { ASSERT_LASX emit_int32(insn_I5RR(xvbitrevi_w_op, ui5, (int)xj->encoding(), (int)xd->encoding())); } ++ void xvbitrevi_d(FloatRegister xd, FloatRegister xj, int ui6) { ASSERT_LASX emit_int32(insn_I6RR(xvbitrevi_d_op, ui6, (int)xj->encoding(), (int)xd->encoding())); } ++ ++ void vfadd_s(FloatRegister vd, FloatRegister vj, FloatRegister vk) { ASSERT_LSX emit_int32(insn_RRR( vfadd_s_op, (int)vk->encoding(), (int)vj->encoding(), (int)vd->encoding())); } ++ void vfadd_d(FloatRegister vd, FloatRegister vj, FloatRegister vk) { ASSERT_LSX emit_int32(insn_RRR( vfadd_d_op, (int)vk->encoding(), (int)vj->encoding(), (int)vd->encoding())); } ++ void xvfadd_s(FloatRegister xd, FloatRegister xj, FloatRegister xk) { ASSERT_LASX emit_int32(insn_RRR(xvfadd_s_op, (int)xk->encoding(), (int)xj->encoding(), (int)xd->encoding())); } ++ void xvfadd_d(FloatRegister xd, FloatRegister xj, FloatRegister xk) { ASSERT_LASX emit_int32(insn_RRR(xvfadd_d_op, (int)xk->encoding(), (int)xj->encoding(), (int)xd->encoding())); } ++ ++ void vfsub_s(FloatRegister vd, FloatRegister vj, FloatRegister vk) { ASSERT_LSX emit_int32(insn_RRR( vfsub_s_op, (int)vk->encoding(), (int)vj->encoding(), (int)vd->encoding())); } ++ void vfsub_d(FloatRegister vd, FloatRegister vj, FloatRegister vk) { ASSERT_LSX emit_int32(insn_RRR( vfsub_d_op, (int)vk->encoding(), (int)vj->encoding(), (int)vd->encoding())); } ++ void xvfsub_s(FloatRegister xd, FloatRegister xj, FloatRegister xk) { ASSERT_LASX emit_int32(insn_RRR(xvfsub_s_op, (int)xk->encoding(), (int)xj->encoding(), (int)xd->encoding())); } ++ void xvfsub_d(FloatRegister xd, FloatRegister xj, FloatRegister xk) { ASSERT_LASX emit_int32(insn_RRR(xvfsub_d_op, (int)xk->encoding(), (int)xj->encoding(), (int)xd->encoding())); } ++ ++ void vfmul_s(FloatRegister vd, FloatRegister vj, FloatRegister vk) { ASSERT_LSX emit_int32(insn_RRR( vfmul_s_op, (int)vk->encoding(), (int)vj->encoding(), (int)vd->encoding())); } ++ void vfmul_d(FloatRegister vd, FloatRegister vj, FloatRegister vk) { ASSERT_LSX emit_int32(insn_RRR( vfmul_d_op, (int)vk->encoding(), (int)vj->encoding(), (int)vd->encoding())); } ++ void xvfmul_s(FloatRegister xd, FloatRegister xj, FloatRegister xk) { ASSERT_LASX emit_int32(insn_RRR(xvfmul_s_op, (int)xk->encoding(), (int)xj->encoding(), (int)xd->encoding())); } ++ void xvfmul_d(FloatRegister xd, FloatRegister xj, FloatRegister xk) { ASSERT_LASX emit_int32(insn_RRR(xvfmul_d_op, (int)xk->encoding(), (int)xj->encoding(), (int)xd->encoding())); } ++ ++ void vfdiv_s(FloatRegister vd, FloatRegister vj, FloatRegister vk) { ASSERT_LSX emit_int32(insn_RRR( vfdiv_s_op, (int)vk->encoding(), (int)vj->encoding(), (int)vd->encoding())); } ++ void vfdiv_d(FloatRegister vd, FloatRegister vj, FloatRegister vk) { ASSERT_LSX emit_int32(insn_RRR( vfdiv_d_op, (int)vk->encoding(), (int)vj->encoding(), (int)vd->encoding())); } ++ void xvfdiv_s(FloatRegister xd, FloatRegister xj, FloatRegister xk) { ASSERT_LASX emit_int32(insn_RRR(xvfdiv_s_op, (int)xk->encoding(), (int)xj->encoding(), (int)xd->encoding())); } ++ void xvfdiv_d(FloatRegister xd, FloatRegister xj, FloatRegister xk) { ASSERT_LASX emit_int32(insn_RRR(xvfdiv_d_op, (int)xk->encoding(), (int)xj->encoding(), (int)xd->encoding())); } ++ ++ void vfmadd_s(FloatRegister vd, FloatRegister vj, FloatRegister vk, FloatRegister va) { ASSERT_LSX emit_int32(insn_RRRR( vfmadd_s_op, (int)va->encoding(), (int)vk->encoding(), (int)vj->encoding(), (int)vd->encoding())); } ++ void vfmadd_d(FloatRegister vd, FloatRegister vj, FloatRegister vk, FloatRegister va) { ASSERT_LSX emit_int32(insn_RRRR( vfmadd_d_op, (int)va->encoding(), (int)vk->encoding(), (int)vj->encoding(), (int)vd->encoding())); } ++ void xvfmadd_s(FloatRegister xd, FloatRegister xj, FloatRegister xk, FloatRegister xa) { ASSERT_LASX emit_int32(insn_RRRR(xvfmadd_s_op, (int)xa->encoding(), (int)xk->encoding(), (int)xj->encoding(), (int)xd->encoding())); } ++ void xvfmadd_d(FloatRegister xd, FloatRegister xj, FloatRegister xk, FloatRegister xa) { ASSERT_LASX emit_int32(insn_RRRR(xvfmadd_d_op, (int)xa->encoding(), (int)xk->encoding(), (int)xj->encoding(), (int)xd->encoding())); } ++ ++ void vfmsub_s(FloatRegister vd, FloatRegister vj, FloatRegister vk, FloatRegister va) { ASSERT_LSX emit_int32(insn_RRRR( vfmsub_s_op, (int)va->encoding(), (int)vk->encoding(), (int)vj->encoding(), (int)vd->encoding())); } ++ void vfmsub_d(FloatRegister vd, FloatRegister vj, FloatRegister vk, FloatRegister va) { ASSERT_LSX emit_int32(insn_RRRR( vfmsub_d_op, (int)va->encoding(), (int)vk->encoding(), (int)vj->encoding(), (int)vd->encoding())); } ++ void xvfmsub_s(FloatRegister xd, FloatRegister xj, FloatRegister xk, FloatRegister xa) { ASSERT_LASX emit_int32(insn_RRRR(xvfmsub_s_op, (int)xa->encoding(), (int)xk->encoding(), (int)xj->encoding(), (int)xd->encoding())); } ++ void xvfmsub_d(FloatRegister xd, FloatRegister xj, FloatRegister xk, FloatRegister xa) { ASSERT_LASX emit_int32(insn_RRRR(xvfmsub_d_op, (int)xa->encoding(), (int)xk->encoding(), (int)xj->encoding(), (int)xd->encoding())); } ++ ++ void vfnmadd_s(FloatRegister vd, FloatRegister vj, FloatRegister vk, FloatRegister va) { ASSERT_LSX emit_int32(insn_RRRR( vfnmadd_s_op, (int)va->encoding(), (int)vk->encoding(), (int)vj->encoding(), (int)vd->encoding())); } ++ void vfnmadd_d(FloatRegister vd, FloatRegister vj, FloatRegister vk, FloatRegister va) { ASSERT_LSX emit_int32(insn_RRRR( vfnmadd_d_op, (int)va->encoding(), (int)vk->encoding(), (int)vj->encoding(), (int)vd->encoding())); } ++ void xvfnmadd_s(FloatRegister xd, FloatRegister xj, FloatRegister xk, FloatRegister xa) { ASSERT_LASX emit_int32(insn_RRRR(xvfnmadd_s_op, (int)xa->encoding(), (int)xk->encoding(), (int)xj->encoding(), (int)xd->encoding())); } ++ void xvfnmadd_d(FloatRegister xd, FloatRegister xj, FloatRegister xk, FloatRegister xa) { ASSERT_LASX emit_int32(insn_RRRR(xvfnmadd_d_op, (int)xa->encoding(), (int)xk->encoding(), (int)xj->encoding(), (int)xd->encoding())); } ++ ++ void vfnmsub_s(FloatRegister vd, FloatRegister vj, FloatRegister vk, FloatRegister va) { ASSERT_LSX emit_int32(insn_RRRR( vfnmsub_s_op, (int)va->encoding(), (int)vk->encoding(), (int)vj->encoding(), (int)vd->encoding())); } ++ void vfnmsub_d(FloatRegister vd, FloatRegister vj, FloatRegister vk, FloatRegister va) { ASSERT_LSX emit_int32(insn_RRRR( vfnmsub_d_op, (int)va->encoding(), (int)vk->encoding(), (int)vj->encoding(), (int)vd->encoding())); } ++ void xvfnmsub_s(FloatRegister xd, FloatRegister xj, FloatRegister xk, FloatRegister xa) { ASSERT_LASX emit_int32(insn_RRRR(xvfnmsub_s_op, (int)xa->encoding(), (int)xk->encoding(), (int)xj->encoding(), (int)xd->encoding())); } ++ void xvfnmsub_d(FloatRegister xd, FloatRegister xj, FloatRegister xk, FloatRegister xa) { ASSERT_LASX emit_int32(insn_RRRR(xvfnmsub_d_op, (int)xa->encoding(), (int)xk->encoding(), (int)xj->encoding(), (int)xd->encoding())); } ++ ++ void vfmax_s(FloatRegister vd, FloatRegister vj, FloatRegister vk) { ASSERT_LSX emit_int32(insn_RRR( vfmax_s_op, (int)vk->encoding(), (int)vj->encoding(), (int)vd->encoding())); } ++ void vfmax_d(FloatRegister vd, FloatRegister vj, FloatRegister vk) { ASSERT_LSX emit_int32(insn_RRR( vfmax_d_op, (int)vk->encoding(), (int)vj->encoding(), (int)vd->encoding())); } ++ void xvfmax_s(FloatRegister xd, FloatRegister xj, FloatRegister xk) { ASSERT_LASX emit_int32(insn_RRR(xvfmax_s_op, (int)xk->encoding(), (int)xj->encoding(), (int)xd->encoding())); } ++ void xvfmax_d(FloatRegister xd, FloatRegister xj, FloatRegister xk) { ASSERT_LASX emit_int32(insn_RRR(xvfmax_d_op, (int)xk->encoding(), (int)xj->encoding(), (int)xd->encoding())); } ++ ++ void vfmin_s(FloatRegister vd, FloatRegister vj, FloatRegister vk) { ASSERT_LSX emit_int32(insn_RRR( vfmin_s_op, (int)vk->encoding(), (int)vj->encoding(), (int)vd->encoding())); } ++ void vfmin_d(FloatRegister vd, FloatRegister vj, FloatRegister vk) { ASSERT_LSX emit_int32(insn_RRR( vfmin_d_op, (int)vk->encoding(), (int)vj->encoding(), (int)vd->encoding())); } ++ void xvfmin_s(FloatRegister xd, FloatRegister xj, FloatRegister xk) { ASSERT_LASX emit_int32(insn_RRR(xvfmin_s_op, (int)xk->encoding(), (int)xj->encoding(), (int)xd->encoding())); } ++ void xvfmin_d(FloatRegister xd, FloatRegister xj, FloatRegister xk) { ASSERT_LASX emit_int32(insn_RRR(xvfmin_d_op, (int)xk->encoding(), (int)xj->encoding(), (int)xd->encoding())); } ++ ++ void vfclass_s(FloatRegister vd, FloatRegister vj) { ASSERT_LSX emit_int32(insn_RR( vfclass_s_op, (int)vj->encoding(), (int)vd->encoding())); } ++ void vfclass_d(FloatRegister vd, FloatRegister vj) { ASSERT_LSX emit_int32(insn_RR( vfclass_d_op, (int)vj->encoding(), (int)vd->encoding())); } ++ void xvfclass_s(FloatRegister xd, FloatRegister xj) { ASSERT_LASX emit_int32(insn_RR(xvfclass_s_op, (int)xj->encoding(), (int)xd->encoding())); } ++ void xvfclass_d(FloatRegister xd, FloatRegister xj) { ASSERT_LASX emit_int32(insn_RR(xvfclass_d_op, (int)xj->encoding(), (int)xd->encoding())); } ++ ++ void vfsqrt_s(FloatRegister vd, FloatRegister vj) { ASSERT_LSX emit_int32(insn_RR( vfsqrt_s_op, (int)vj->encoding(), (int)vd->encoding())); } ++ void vfsqrt_d(FloatRegister vd, FloatRegister vj) { ASSERT_LSX emit_int32(insn_RR( vfsqrt_d_op, (int)vj->encoding(), (int)vd->encoding())); } ++ void xvfsqrt_s(FloatRegister xd, FloatRegister xj) { ASSERT_LASX emit_int32(insn_RR(xvfsqrt_s_op, (int)xj->encoding(), (int)xd->encoding())); } ++ void xvfsqrt_d(FloatRegister xd, FloatRegister xj) { ASSERT_LASX emit_int32(insn_RR(xvfsqrt_d_op, (int)xj->encoding(), (int)xd->encoding())); } ++ ++ void vfcvtl_s_h(FloatRegister vd, FloatRegister rj) { ASSERT_LSX emit_int32(insn_RR( vfcvtl_s_h_op, (int)rj->encoding(), (int)vd->encoding())); } ++ void vfcvtl_d_s(FloatRegister vd, FloatRegister rj) { ASSERT_LSX emit_int32(insn_RR( vfcvtl_d_s_op, (int)rj->encoding(), (int)vd->encoding())); } ++ void xvfcvtl_s_h(FloatRegister xd, FloatRegister xj) { ASSERT_LASX emit_int32(insn_RR(xvfcvtl_s_h_op, (int)xj->encoding(), (int)xd->encoding())); } ++ void xvfcvtl_d_s(FloatRegister xd, FloatRegister xj) { ASSERT_LASX emit_int32(insn_RR(xvfcvtl_d_s_op, (int)xj->encoding(), (int)xd->encoding())); } ++ ++ void vfcvth_s_h(FloatRegister vd, FloatRegister rj) { ASSERT_LSX emit_int32(insn_RR( vfcvth_s_h_op, (int)rj->encoding(), (int)vd->encoding())); } ++ void vfcvth_d_s(FloatRegister vd, FloatRegister rj) { ASSERT_LSX emit_int32(insn_RR( vfcvth_d_s_op, (int)rj->encoding(), (int)vd->encoding())); } ++ void xvfcvth_s_h(FloatRegister xd, FloatRegister xj) { ASSERT_LASX emit_int32(insn_RR(xvfcvth_s_h_op, (int)xj->encoding(), (int)xd->encoding())); } ++ void xvfcvth_d_s(FloatRegister xd, FloatRegister xj) { ASSERT_LASX emit_int32(insn_RR(xvfcvth_d_s_op, (int)xj->encoding(), (int)xd->encoding())); } ++ ++ void vfcvt_h_s(FloatRegister vd, FloatRegister vj, FloatRegister vk) { ASSERT_LSX emit_int32(insn_RRR( vfcvt_h_s_op, (int)vk->encoding(), (int)vj->encoding(), (int)vd->encoding())); } ++ void vfcvt_s_d(FloatRegister vd, FloatRegister vj, FloatRegister vk) { ASSERT_LSX emit_int32(insn_RRR( vfcvt_s_d_op, (int)vk->encoding(), (int)vj->encoding(), (int)vd->encoding())); } ++ void xvfcvt_h_s(FloatRegister xd, FloatRegister xj, FloatRegister xk) { ASSERT_LASX emit_int32(insn_RRR(xvfcvt_h_s_op, (int)xk->encoding(), (int)xj->encoding(), (int)xd->encoding())); } ++ void xvfcvt_s_d(FloatRegister xd, FloatRegister xj, FloatRegister xk) { ASSERT_LASX emit_int32(insn_RRR(xvfcvt_s_d_op, (int)xk->encoding(), (int)xj->encoding(), (int)xd->encoding())); } ++ ++ void vfrintrne_s(FloatRegister vd, FloatRegister vj) { ASSERT_LSX emit_int32(insn_RR( vfrintrne_s_op, (int)vj->encoding(), (int)vd->encoding())); } ++ void vfrintrne_d(FloatRegister vd, FloatRegister vj) { ASSERT_LSX emit_int32(insn_RR( vfrintrne_d_op, (int)vj->encoding(), (int)vd->encoding())); } ++ void xvfrintrne_s(FloatRegister xd, FloatRegister xj) { ASSERT_LASX emit_int32(insn_RR(xvfrintrne_s_op, (int)xj->encoding(), (int)xd->encoding())); } ++ void xvfrintrne_d(FloatRegister xd, FloatRegister xj) { ASSERT_LASX emit_int32(insn_RR(xvfrintrne_d_op, (int)xj->encoding(), (int)xd->encoding())); } ++ ++ void vfrintrz_s(FloatRegister vd, FloatRegister vj) { ASSERT_LSX emit_int32(insn_RR( vfrintrz_s_op, (int)vj->encoding(), (int)vd->encoding())); } ++ void vfrintrz_d(FloatRegister vd, FloatRegister vj) { ASSERT_LSX emit_int32(insn_RR( vfrintrz_d_op, (int)vj->encoding(), (int)vd->encoding())); } ++ void xvfrintrz_s(FloatRegister xd, FloatRegister xj) { ASSERT_LASX emit_int32(insn_RR(xvfrintrz_s_op, (int)xj->encoding(), (int)xd->encoding())); } ++ void xvfrintrz_d(FloatRegister xd, FloatRegister xj) { ASSERT_LASX emit_int32(insn_RR(xvfrintrz_d_op, (int)xj->encoding(), (int)xd->encoding())); } ++ ++ void vfrintrp_s(FloatRegister vd, FloatRegister vj) { ASSERT_LSX emit_int32(insn_RR( vfrintrp_s_op, (int)vj->encoding(), (int)vd->encoding())); } ++ void vfrintrp_d(FloatRegister vd, FloatRegister vj) { ASSERT_LSX emit_int32(insn_RR( vfrintrp_d_op, (int)vj->encoding(), (int)vd->encoding())); } ++ void xvfrintrp_s(FloatRegister xd, FloatRegister xj) { ASSERT_LASX emit_int32(insn_RR(xvfrintrp_s_op, (int)xj->encoding(), (int)xd->encoding())); } ++ void xvfrintrp_d(FloatRegister xd, FloatRegister xj) { ASSERT_LASX emit_int32(insn_RR(xvfrintrp_d_op, (int)xj->encoding(), (int)xd->encoding())); } ++ ++ void vfrintrm_s(FloatRegister vd, FloatRegister vj) { ASSERT_LSX emit_int32(insn_RR( vfrintrm_s_op, (int)vj->encoding(), (int)vd->encoding())); } ++ void vfrintrm_d(FloatRegister vd, FloatRegister vj) { ASSERT_LSX emit_int32(insn_RR( vfrintrm_d_op, (int)vj->encoding(), (int)vd->encoding())); } ++ void xvfrintrm_s(FloatRegister xd, FloatRegister xj) { ASSERT_LASX emit_int32(insn_RR(xvfrintrm_s_op, (int)xj->encoding(), (int)xd->encoding())); } ++ void xvfrintrm_d(FloatRegister xd, FloatRegister xj) { ASSERT_LASX emit_int32(insn_RR(xvfrintrm_d_op, (int)xj->encoding(), (int)xd->encoding())); } ++ ++ void vfrint_s(FloatRegister vd, FloatRegister vj) { ASSERT_LSX emit_int32(insn_RR( vfrint_s_op, (int)vj->encoding(), (int)vd->encoding())); } ++ void vfrint_d(FloatRegister vd, FloatRegister vj) { ASSERT_LSX emit_int32(insn_RR( vfrint_d_op, (int)vj->encoding(), (int)vd->encoding())); } ++ void xvfrint_s(FloatRegister xd, FloatRegister xj) { ASSERT_LASX emit_int32(insn_RR(xvfrint_s_op, (int)xj->encoding(), (int)xd->encoding())); } ++ void xvfrint_d(FloatRegister xd, FloatRegister xj) { ASSERT_LASX emit_int32(insn_RR(xvfrint_d_op, (int)xj->encoding(), (int)xd->encoding())); } ++ ++ void vftintrne_w_s(FloatRegister vd, FloatRegister rj) { ASSERT_LSX emit_int32(insn_RR( vftintrne_w_s_op, (int)rj->encoding(), (int)vd->encoding())); } ++ void vftintrne_l_d(FloatRegister vd, FloatRegister rj) { ASSERT_LSX emit_int32(insn_RR( vftintrne_l_d_op, (int)rj->encoding(), (int)vd->encoding())); } ++ void xvftintrne_w_s(FloatRegister xd, FloatRegister xj) { ASSERT_LASX emit_int32(insn_RR(xvftintrne_w_s_op, (int)xj->encoding(), (int)xd->encoding())); } ++ void xvftintrne_l_d(FloatRegister xd, FloatRegister xj) { ASSERT_LASX emit_int32(insn_RR(xvftintrne_l_d_op, (int)xj->encoding(), (int)xd->encoding())); } ++ ++ void vftintrz_w_s(FloatRegister vd, FloatRegister rj) { ASSERT_LSX emit_int32(insn_RR( vftintrz_w_s_op, (int)rj->encoding(), (int)vd->encoding())); } ++ void vftintrz_l_d(FloatRegister vd, FloatRegister rj) { ASSERT_LSX emit_int32(insn_RR( vftintrz_l_d_op, (int)rj->encoding(), (int)vd->encoding())); } ++ void xvftintrz_w_s(FloatRegister xd, FloatRegister xj) { ASSERT_LASX emit_int32(insn_RR(xvftintrz_w_s_op, (int)xj->encoding(), (int)xd->encoding())); } ++ void xvftintrz_l_d(FloatRegister xd, FloatRegister xj) { ASSERT_LASX emit_int32(insn_RR(xvftintrz_l_d_op, (int)xj->encoding(), (int)xd->encoding())); } ++ ++ void vftintrp_w_s(FloatRegister vd, FloatRegister rj) { ASSERT_LSX emit_int32(insn_RR( vftintrp_w_s_op, (int)rj->encoding(), (int)vd->encoding())); } ++ void vftintrp_l_d(FloatRegister vd, FloatRegister rj) { ASSERT_LSX emit_int32(insn_RR( vftintrp_l_d_op, (int)rj->encoding(), (int)vd->encoding())); } ++ void xvftintrp_w_s(FloatRegister xd, FloatRegister xj) { ASSERT_LASX emit_int32(insn_RR(xvftintrp_w_s_op, (int)xj->encoding(), (int)xd->encoding())); } ++ void xvftintrp_l_d(FloatRegister xd, FloatRegister xj) { ASSERT_LASX emit_int32(insn_RR(xvftintrp_l_d_op, (int)xj->encoding(), (int)xd->encoding())); } ++ ++ void vftintrm_w_s(FloatRegister vd, FloatRegister rj) { ASSERT_LSX emit_int32(insn_RR( vftintrm_w_s_op, (int)rj->encoding(), (int)vd->encoding())); } ++ void vftintrm_l_d(FloatRegister vd, FloatRegister rj) { ASSERT_LSX emit_int32(insn_RR( vftintrm_l_d_op, (int)rj->encoding(), (int)vd->encoding())); } ++ void xvftintrm_w_s(FloatRegister xd, FloatRegister xj) { ASSERT_LASX emit_int32(insn_RR(xvftintrm_w_s_op, (int)xj->encoding(), (int)xd->encoding())); } ++ void xvftintrm_l_d(FloatRegister xd, FloatRegister xj) { ASSERT_LASX emit_int32(insn_RR(xvftintrm_l_d_op, (int)xj->encoding(), (int)xd->encoding())); } ++ ++ void vftint_w_s(FloatRegister vd, FloatRegister rj) { ASSERT_LSX emit_int32(insn_RR( vftint_w_s_op, (int)rj->encoding(), (int)vd->encoding())); } ++ void vftint_l_d(FloatRegister vd, FloatRegister rj) { ASSERT_LSX emit_int32(insn_RR( vftint_l_d_op, (int)rj->encoding(), (int)vd->encoding())); } ++ void xvftint_w_s(FloatRegister xd, FloatRegister xj) { ASSERT_LASX emit_int32(insn_RR(xvftint_w_s_op, (int)xj->encoding(), (int)xd->encoding())); } ++ void xvftint_l_d(FloatRegister xd, FloatRegister xj) { ASSERT_LASX emit_int32(insn_RR(xvftint_l_d_op, (int)xj->encoding(), (int)xd->encoding())); } ++ ++ void vftintrne_w_d(FloatRegister vd, FloatRegister vj, FloatRegister vk) { ASSERT_LSX emit_int32(insn_RRR( vftintrne_w_d_op, (int)vk->encoding(), (int)vj->encoding(), (int)vd->encoding())); } ++ void xvftintrne_w_d(FloatRegister xd, FloatRegister xj, FloatRegister xk) { ASSERT_LASX emit_int32(insn_RRR(xvftintrne_w_d_op, (int)xk->encoding(), (int)xj->encoding(), (int)xd->encoding())); } ++ ++ void vftintrz_w_d(FloatRegister vd, FloatRegister vj, FloatRegister vk) { ASSERT_LSX emit_int32(insn_RRR( vftintrz_w_d_op, (int)vk->encoding(), (int)vj->encoding(), (int)vd->encoding())); } ++ void xvftintrz_w_d(FloatRegister xd, FloatRegister xj, FloatRegister xk) { ASSERT_LASX emit_int32(insn_RRR(xvftintrz_w_d_op, (int)xk->encoding(), (int)xj->encoding(), (int)xd->encoding())); } ++ ++ void vftintrp_w_d(FloatRegister vd, FloatRegister vj, FloatRegister vk) { ASSERT_LSX emit_int32(insn_RRR( vftintrp_w_d_op, (int)vk->encoding(), (int)vj->encoding(), (int)vd->encoding())); } ++ void xvftintrp_w_d(FloatRegister xd, FloatRegister xj, FloatRegister xk) { ASSERT_LASX emit_int32(insn_RRR(xvftintrp_w_d_op, (int)xk->encoding(), (int)xj->encoding(), (int)xd->encoding())); } ++ ++ void vftintrm_w_d(FloatRegister vd, FloatRegister vj, FloatRegister vk) { ASSERT_LSX emit_int32(insn_RRR( vftintrm_w_d_op, (int)vk->encoding(), (int)vj->encoding(), (int)vd->encoding())); } ++ void xvftintrm_w_d(FloatRegister xd, FloatRegister xj, FloatRegister xk) { ASSERT_LASX emit_int32(insn_RRR(xvftintrm_w_d_op, (int)xk->encoding(), (int)xj->encoding(), (int)xd->encoding())); } ++ ++ void vftint_w_d(FloatRegister vd, FloatRegister vj, FloatRegister vk) { ASSERT_LSX emit_int32(insn_RRR( vftint_w_d_op, (int)vk->encoding(), (int)vj->encoding(), (int)vd->encoding())); } ++ void xvftint_w_d(FloatRegister xd, FloatRegister xj, FloatRegister xk) { ASSERT_LASX emit_int32(insn_RRR(xvftint_w_d_op, (int)xk->encoding(), (int)xj->encoding(), (int)xd->encoding())); } ++ ++ void vftintrnel_l_s(FloatRegister vd, FloatRegister rj) { ASSERT_LSX emit_int32(insn_RR( vftintrnel_l_s_op, (int)rj->encoding(), (int)vd->encoding())); } ++ void xvftintrnel_l_s(FloatRegister xd, FloatRegister xj) { ASSERT_LASX emit_int32(insn_RR(xvftintrnel_l_s_op, (int)xj->encoding(), (int)xd->encoding())); } ++ ++ void vftintrneh_l_s(FloatRegister vd, FloatRegister rj) { ASSERT_LSX emit_int32(insn_RR( vftintrneh_l_s_op, (int)rj->encoding(), (int)vd->encoding())); } ++ void xvftintrneh_l_s(FloatRegister xd, FloatRegister xj) { ASSERT_LASX emit_int32(insn_RR(xvftintrneh_l_s_op, (int)xj->encoding(), (int)xd->encoding())); } ++ ++ void vftintrzl_l_s(FloatRegister vd, FloatRegister rj) { ASSERT_LSX emit_int32(insn_RR( vftintrzl_l_s_op, (int)rj->encoding(), (int)vd->encoding())); } ++ void xvftintrzl_l_s(FloatRegister xd, FloatRegister xj) { ASSERT_LASX emit_int32(insn_RR(xvftintrzl_l_s_op, (int)xj->encoding(), (int)xd->encoding())); } ++ ++ void vftintrzh_l_s(FloatRegister vd, FloatRegister rj) { ASSERT_LSX emit_int32(insn_RR( vftintrzh_l_s_op, (int)rj->encoding(), (int)vd->encoding())); } ++ void xvftintrzh_l_s(FloatRegister xd, FloatRegister xj) { ASSERT_LASX emit_int32(insn_RR(xvftintrzh_l_s_op, (int)xj->encoding(), (int)xd->encoding())); } ++ ++ void vftintrpl_l_s(FloatRegister vd, FloatRegister rj) { ASSERT_LSX emit_int32(insn_RR( vftintrpl_l_s_op, (int)rj->encoding(), (int)vd->encoding())); } ++ void xvftintrpl_l_s(FloatRegister xd, FloatRegister xj) { ASSERT_LASX emit_int32(insn_RR(xvftintrpl_l_s_op, (int)xj->encoding(), (int)xd->encoding())); } ++ ++ void vftintrph_l_s(FloatRegister vd, FloatRegister rj) { ASSERT_LSX emit_int32(insn_RR( vftintrph_l_s_op, (int)rj->encoding(), (int)vd->encoding())); } ++ void xvftintrph_l_s(FloatRegister xd, FloatRegister xj) { ASSERT_LASX emit_int32(insn_RR(xvftintrph_l_s_op, (int)xj->encoding(), (int)xd->encoding())); } ++ ++ void vftintrml_l_s(FloatRegister vd, FloatRegister rj) { ASSERT_LSX emit_int32(insn_RR( vftintrml_l_s_op, (int)rj->encoding(), (int)vd->encoding())); } ++ void xvftintrml_l_s(FloatRegister xd, FloatRegister xj) { ASSERT_LASX emit_int32(insn_RR(xvftintrml_l_s_op, (int)xj->encoding(), (int)xd->encoding())); } ++ ++ void vftintrmh_l_s(FloatRegister vd, FloatRegister rj) { ASSERT_LSX emit_int32(insn_RR( vftintrmh_l_s_op, (int)rj->encoding(), (int)vd->encoding())); } ++ void xvftintrmh_l_s(FloatRegister xd, FloatRegister xj) { ASSERT_LASX emit_int32(insn_RR(xvftintrmh_l_s_op, (int)xj->encoding(), (int)xd->encoding())); } ++ ++ void vftintl_l_s(FloatRegister vd, FloatRegister rj) { ASSERT_LSX emit_int32(insn_RR( vftintl_l_s_op, (int)rj->encoding(), (int)vd->encoding())); } ++ void xvftintl_l_s(FloatRegister xd, FloatRegister xj) { ASSERT_LASX emit_int32(insn_RR(xvftintl_l_s_op, (int)xj->encoding(), (int)xd->encoding())); } ++ ++ void vftinth_l_s(FloatRegister vd, FloatRegister rj) { ASSERT_LSX emit_int32(insn_RR( vftinth_l_s_op, (int)rj->encoding(), (int)vd->encoding())); } ++ void xvftinth_l_s(FloatRegister xd, FloatRegister xj) { ASSERT_LASX emit_int32(insn_RR(xvftinth_l_s_op, (int)xj->encoding(), (int)xd->encoding())); } ++ ++ void vffint_s_w(FloatRegister vd, FloatRegister rj) { ASSERT_LSX emit_int32(insn_RR( vffint_s_w_op, (int)rj->encoding(), (int)vd->encoding())); } ++ void vffint_d_l(FloatRegister vd, FloatRegister rj) { ASSERT_LSX emit_int32(insn_RR( vffint_d_l_op, (int)rj->encoding(), (int)vd->encoding())); } ++ void xvffint_s_w(FloatRegister xd, FloatRegister xj) { ASSERT_LASX emit_int32(insn_RR(xvffint_s_w_op, (int)xj->encoding(), (int)xd->encoding())); } ++ void xvffint_d_l(FloatRegister xd, FloatRegister xj) { ASSERT_LASX emit_int32(insn_RR(xvffint_d_l_op, (int)xj->encoding(), (int)xd->encoding())); } ++ ++ void vffint_s_l(FloatRegister vd, FloatRegister vj, FloatRegister vk) { ASSERT_LSX emit_int32(insn_RRR( vffint_s_l_op, (int)vk->encoding(), (int)vj->encoding(), (int)vd->encoding())); } ++ void xvffint_s_l(FloatRegister xd, FloatRegister xj, FloatRegister xk) { ASSERT_LASX emit_int32(insn_RRR(xvffint_s_l_op, (int)xk->encoding(), (int)xj->encoding(), (int)xd->encoding())); } ++ ++ void vffintl_d_w(FloatRegister vd, FloatRegister rj) { ASSERT_LSX emit_int32(insn_RR( vffintl_d_w_op, (int)rj->encoding(), (int)vd->encoding())); } ++ void xvffintl_d_w(FloatRegister xd, FloatRegister xj) { ASSERT_LASX emit_int32(insn_RR(xvffintl_d_w_op, (int)xj->encoding(), (int)xd->encoding())); } ++ ++ void vffinth_d_w(FloatRegister vd, FloatRegister rj) { ASSERT_LSX emit_int32(insn_RR( vffinth_d_w_op, (int)rj->encoding(), (int)vd->encoding())); } ++ void xvffinth_d_w(FloatRegister xd, FloatRegister xj) { ASSERT_LASX emit_int32(insn_RR(xvffinth_d_w_op, (int)xj->encoding(), (int)xd->encoding())); } ++ ++ void vseq_b(FloatRegister vd, FloatRegister vj, FloatRegister vk) { ASSERT_LSX emit_int32(insn_RRR( vseq_b_op, (int)vk->encoding(), (int)vj->encoding(), (int)vd->encoding())); } ++ void vseq_h(FloatRegister vd, FloatRegister vj, FloatRegister vk) { ASSERT_LSX emit_int32(insn_RRR( vseq_h_op, (int)vk->encoding(), (int)vj->encoding(), (int)vd->encoding())); } ++ void vseq_w(FloatRegister vd, FloatRegister vj, FloatRegister vk) { ASSERT_LSX emit_int32(insn_RRR( vseq_w_op, (int)vk->encoding(), (int)vj->encoding(), (int)vd->encoding())); } ++ void vseq_d(FloatRegister vd, FloatRegister vj, FloatRegister vk) { ASSERT_LSX emit_int32(insn_RRR( vseq_d_op, (int)vk->encoding(), (int)vj->encoding(), (int)vd->encoding())); } ++ void xvseq_b(FloatRegister xd, FloatRegister xj, FloatRegister xk) { ASSERT_LASX emit_int32(insn_RRR(xvseq_b_op, (int)xk->encoding(), (int)xj->encoding(), (int)xd->encoding())); } ++ void xvseq_h(FloatRegister xd, FloatRegister xj, FloatRegister xk) { ASSERT_LASX emit_int32(insn_RRR(xvseq_h_op, (int)xk->encoding(), (int)xj->encoding(), (int)xd->encoding())); } ++ void xvseq_w(FloatRegister xd, FloatRegister xj, FloatRegister xk) { ASSERT_LASX emit_int32(insn_RRR(xvseq_w_op, (int)xk->encoding(), (int)xj->encoding(), (int)xd->encoding())); } ++ void xvseq_d(FloatRegister xd, FloatRegister xj, FloatRegister xk) { ASSERT_LASX emit_int32(insn_RRR(xvseq_d_op, (int)xk->encoding(), (int)xj->encoding(), (int)xd->encoding())); } ++ ++ void vsle_b(FloatRegister vd, FloatRegister vj, FloatRegister vk) { ASSERT_LSX emit_int32(insn_RRR( vsle_b_op, (int)vk->encoding(), (int)vj->encoding(), (int)vd->encoding())); } ++ void vsle_h(FloatRegister vd, FloatRegister vj, FloatRegister vk) { ASSERT_LSX emit_int32(insn_RRR( vsle_h_op, (int)vk->encoding(), (int)vj->encoding(), (int)vd->encoding())); } ++ void vsle_w(FloatRegister vd, FloatRegister vj, FloatRegister vk) { ASSERT_LSX emit_int32(insn_RRR( vsle_w_op, (int)vk->encoding(), (int)vj->encoding(), (int)vd->encoding())); } ++ void vsle_d(FloatRegister vd, FloatRegister vj, FloatRegister vk) { ASSERT_LSX emit_int32(insn_RRR( vsle_d_op, (int)vk->encoding(), (int)vj->encoding(), (int)vd->encoding())); } ++ void xvsle_b(FloatRegister xd, FloatRegister xj, FloatRegister xk) { ASSERT_LASX emit_int32(insn_RRR(xvsle_b_op, (int)xk->encoding(), (int)xj->encoding(), (int)xd->encoding())); } ++ void xvsle_h(FloatRegister xd, FloatRegister xj, FloatRegister xk) { ASSERT_LASX emit_int32(insn_RRR(xvsle_h_op, (int)xk->encoding(), (int)xj->encoding(), (int)xd->encoding())); } ++ void xvsle_w(FloatRegister xd, FloatRegister xj, FloatRegister xk) { ASSERT_LASX emit_int32(insn_RRR(xvsle_w_op, (int)xk->encoding(), (int)xj->encoding(), (int)xd->encoding())); } ++ void xvsle_d(FloatRegister xd, FloatRegister xj, FloatRegister xk) { ASSERT_LASX emit_int32(insn_RRR(xvsle_d_op, (int)xk->encoding(), (int)xj->encoding(), (int)xd->encoding())); } ++ ++ void vsle_bu(FloatRegister vd, FloatRegister vj, FloatRegister vk) { ASSERT_LSX emit_int32(insn_RRR( vsle_bu_op, (int)vk->encoding(), (int)vj->encoding(), (int)vd->encoding())); } ++ void vsle_hu(FloatRegister vd, FloatRegister vj, FloatRegister vk) { ASSERT_LSX emit_int32(insn_RRR( vsle_hu_op, (int)vk->encoding(), (int)vj->encoding(), (int)vd->encoding())); } ++ void vsle_wu(FloatRegister vd, FloatRegister vj, FloatRegister vk) { ASSERT_LSX emit_int32(insn_RRR( vsle_wu_op, (int)vk->encoding(), (int)vj->encoding(), (int)vd->encoding())); } ++ void vsle_du(FloatRegister vd, FloatRegister vj, FloatRegister vk) { ASSERT_LSX emit_int32(insn_RRR( vsle_du_op, (int)vk->encoding(), (int)vj->encoding(), (int)vd->encoding())); } ++ void xvsle_bu(FloatRegister xd, FloatRegister xj, FloatRegister xk) { ASSERT_LASX emit_int32(insn_RRR(xvsle_bu_op, (int)xk->encoding(), (int)xj->encoding(), (int)xd->encoding())); } ++ void xvsle_hu(FloatRegister xd, FloatRegister xj, FloatRegister xk) { ASSERT_LASX emit_int32(insn_RRR(xvsle_hu_op, (int)xk->encoding(), (int)xj->encoding(), (int)xd->encoding())); } ++ void xvsle_wu(FloatRegister xd, FloatRegister xj, FloatRegister xk) { ASSERT_LASX emit_int32(insn_RRR(xvsle_wu_op, (int)xk->encoding(), (int)xj->encoding(), (int)xd->encoding())); } ++ void xvsle_du(FloatRegister xd, FloatRegister xj, FloatRegister xk) { ASSERT_LASX emit_int32(insn_RRR(xvsle_du_op, (int)xk->encoding(), (int)xj->encoding(), (int)xd->encoding())); } ++ ++ void vslt_b(FloatRegister vd, FloatRegister vj, FloatRegister vk) { ASSERT_LSX emit_int32(insn_RRR( vslt_b_op, (int)vk->encoding(), (int)vj->encoding(), (int)vd->encoding())); } ++ void vslt_h(FloatRegister vd, FloatRegister vj, FloatRegister vk) { ASSERT_LSX emit_int32(insn_RRR( vslt_h_op, (int)vk->encoding(), (int)vj->encoding(), (int)vd->encoding())); } ++ void vslt_w(FloatRegister vd, FloatRegister vj, FloatRegister vk) { ASSERT_LSX emit_int32(insn_RRR( vslt_w_op, (int)vk->encoding(), (int)vj->encoding(), (int)vd->encoding())); } ++ void vslt_d(FloatRegister vd, FloatRegister vj, FloatRegister vk) { ASSERT_LSX emit_int32(insn_RRR( vslt_d_op, (int)vk->encoding(), (int)vj->encoding(), (int)vd->encoding())); } ++ void xvslt_b(FloatRegister xd, FloatRegister xj, FloatRegister xk) { ASSERT_LASX emit_int32(insn_RRR(xvslt_b_op, (int)xk->encoding(), (int)xj->encoding(), (int)xd->encoding())); } ++ void xvslt_h(FloatRegister xd, FloatRegister xj, FloatRegister xk) { ASSERT_LASX emit_int32(insn_RRR(xvslt_h_op, (int)xk->encoding(), (int)xj->encoding(), (int)xd->encoding())); } ++ void xvslt_w(FloatRegister xd, FloatRegister xj, FloatRegister xk) { ASSERT_LASX emit_int32(insn_RRR(xvslt_w_op, (int)xk->encoding(), (int)xj->encoding(), (int)xd->encoding())); } ++ void xvslt_d(FloatRegister xd, FloatRegister xj, FloatRegister xk) { ASSERT_LASX emit_int32(insn_RRR(xvslt_d_op, (int)xk->encoding(), (int)xj->encoding(), (int)xd->encoding())); } ++ ++ void vslt_bu(FloatRegister vd, FloatRegister vj, FloatRegister vk) { ASSERT_LSX emit_int32(insn_RRR( vslt_bu_op, (int)vk->encoding(), (int)vj->encoding(), (int)vd->encoding())); } ++ void vslt_hu(FloatRegister vd, FloatRegister vj, FloatRegister vk) { ASSERT_LSX emit_int32(insn_RRR( vslt_hu_op, (int)vk->encoding(), (int)vj->encoding(), (int)vd->encoding())); } ++ void vslt_wu(FloatRegister vd, FloatRegister vj, FloatRegister vk) { ASSERT_LSX emit_int32(insn_RRR( vslt_wu_op, (int)vk->encoding(), (int)vj->encoding(), (int)vd->encoding())); } ++ void vslt_du(FloatRegister vd, FloatRegister vj, FloatRegister vk) { ASSERT_LSX emit_int32(insn_RRR( vslt_du_op, (int)vk->encoding(), (int)vj->encoding(), (int)vd->encoding())); } ++ void xvslt_bu(FloatRegister xd, FloatRegister xj, FloatRegister xk) { ASSERT_LASX emit_int32(insn_RRR(xvslt_bu_op, (int)xk->encoding(), (int)xj->encoding(), (int)xd->encoding())); } ++ void xvslt_hu(FloatRegister xd, FloatRegister xj, FloatRegister xk) { ASSERT_LASX emit_int32(insn_RRR(xvslt_hu_op, (int)xk->encoding(), (int)xj->encoding(), (int)xd->encoding())); } ++ void xvslt_wu(FloatRegister xd, FloatRegister xj, FloatRegister xk) { ASSERT_LASX emit_int32(insn_RRR(xvslt_wu_op, (int)xk->encoding(), (int)xj->encoding(), (int)xd->encoding())); } ++ void xvslt_du(FloatRegister xd, FloatRegister xj, FloatRegister xk) { ASSERT_LASX emit_int32(insn_RRR(xvslt_du_op, (int)xk->encoding(), (int)xj->encoding(), (int)xd->encoding())); } ++ ++ void vslti_bu(FloatRegister vd, FloatRegister vj, int ui5) { ASSERT_LSX emit_int32(insn_I5RR( vslti_bu_op, ui5, (int)vj->encoding(), (int)vd->encoding())); } ++ void vslti_hu(FloatRegister vd, FloatRegister vj, int ui5) { ASSERT_LSX emit_int32(insn_I5RR( vslti_hu_op, ui5, (int)vj->encoding(), (int)vd->encoding())); } ++ void vslti_wu(FloatRegister vd, FloatRegister vj, int ui5) { ASSERT_LSX emit_int32(insn_I5RR( vslti_wu_op, ui5, (int)vj->encoding(), (int)vd->encoding())); } ++ void vslti_du(FloatRegister vd, FloatRegister vj, int ui5) { ASSERT_LSX emit_int32(insn_I5RR( vslti_du_op, ui5, (int)vj->encoding(), (int)vd->encoding())); } ++ void xvslti_bu(FloatRegister xd, FloatRegister xj, int ui5) { ASSERT_LASX emit_int32(insn_I5RR(xvslti_bu_op, ui5, (int)xj->encoding(), (int)xd->encoding())); } ++ void xvslti_hu(FloatRegister xd, FloatRegister xj, int ui5) { ASSERT_LASX emit_int32(insn_I5RR(xvslti_hu_op, ui5, (int)xj->encoding(), (int)xd->encoding())); } ++ void xvslti_wu(FloatRegister xd, FloatRegister xj, int ui5) { ASSERT_LASX emit_int32(insn_I5RR(xvslti_wu_op, ui5, (int)xj->encoding(), (int)xd->encoding())); } ++ void xvslti_du(FloatRegister xd, FloatRegister xj, int ui5) { ASSERT_LASX emit_int32(insn_I5RR(xvslti_du_op, ui5, (int)xj->encoding(), (int)xd->encoding())); } ++ ++ void vfcmp_caf_s (FloatRegister vd, FloatRegister vj, FloatRegister vk) { ASSERT_LSX emit_int32(insn_RRRR( vfcmp_cond_s_op, fcmp_caf , (int)vk->encoding(), (int)vj->encoding(), (int)vd->encoding())); } ++ void vfcmp_cun_s (FloatRegister vd, FloatRegister vj, FloatRegister vk) { ASSERT_LSX emit_int32(insn_RRRR( vfcmp_cond_s_op, fcmp_cun , (int)vk->encoding(), (int)vj->encoding(), (int)vd->encoding())); } ++ void vfcmp_ceq_s (FloatRegister vd, FloatRegister vj, FloatRegister vk) { ASSERT_LSX emit_int32(insn_RRRR( vfcmp_cond_s_op, fcmp_ceq , (int)vk->encoding(), (int)vj->encoding(), (int)vd->encoding())); } ++ void vfcmp_cueq_s (FloatRegister vd, FloatRegister vj, FloatRegister vk) { ASSERT_LSX emit_int32(insn_RRRR( vfcmp_cond_s_op, fcmp_cueq, (int)vk->encoding(), (int)vj->encoding(), (int)vd->encoding())); } ++ void vfcmp_clt_s (FloatRegister vd, FloatRegister vj, FloatRegister vk) { ASSERT_LSX emit_int32(insn_RRRR( vfcmp_cond_s_op, fcmp_clt , (int)vk->encoding(), (int)vj->encoding(), (int)vd->encoding())); } ++ void vfcmp_cult_s (FloatRegister vd, FloatRegister vj, FloatRegister vk) { ASSERT_LSX emit_int32(insn_RRRR( vfcmp_cond_s_op, fcmp_cult, (int)vk->encoding(), (int)vj->encoding(), (int)vd->encoding())); } ++ void vfcmp_cle_s (FloatRegister vd, FloatRegister vj, FloatRegister vk) { ASSERT_LSX emit_int32(insn_RRRR( vfcmp_cond_s_op, fcmp_cle , (int)vk->encoding(), (int)vj->encoding(), (int)vd->encoding())); } ++ void vfcmp_cule_s (FloatRegister vd, FloatRegister vj, FloatRegister vk) { ASSERT_LSX emit_int32(insn_RRRR( vfcmp_cond_s_op, fcmp_cule, (int)vk->encoding(), (int)vj->encoding(), (int)vd->encoding())); } ++ void vfcmp_cne_s (FloatRegister vd, FloatRegister vj, FloatRegister vk) { ASSERT_LSX emit_int32(insn_RRRR( vfcmp_cond_s_op, fcmp_cne , (int)vk->encoding(), (int)vj->encoding(), (int)vd->encoding())); } ++ void vfcmp_cor_s (FloatRegister vd, FloatRegister vj, FloatRegister vk) { ASSERT_LSX emit_int32(insn_RRRR( vfcmp_cond_s_op, fcmp_cor , (int)vk->encoding(), (int)vj->encoding(), (int)vd->encoding())); } ++ void vfcmp_cune_s (FloatRegister vd, FloatRegister vj, FloatRegister vk) { ASSERT_LSX emit_int32(insn_RRRR( vfcmp_cond_s_op, fcmp_cune, (int)vk->encoding(), (int)vj->encoding(), (int)vd->encoding())); } ++ void vfcmp_saf_s (FloatRegister vd, FloatRegister vj, FloatRegister vk) { ASSERT_LSX emit_int32(insn_RRRR( vfcmp_cond_s_op, fcmp_saf , (int)vk->encoding(), (int)vj->encoding(), (int)vd->encoding())); } ++ void vfcmp_sun_s (FloatRegister vd, FloatRegister vj, FloatRegister vk) { ASSERT_LSX emit_int32(insn_RRRR( vfcmp_cond_s_op, fcmp_sun , (int)vk->encoding(), (int)vj->encoding(), (int)vd->encoding())); } ++ void vfcmp_seq_s (FloatRegister vd, FloatRegister vj, FloatRegister vk) { ASSERT_LSX emit_int32(insn_RRRR( vfcmp_cond_s_op, fcmp_seq , (int)vk->encoding(), (int)vj->encoding(), (int)vd->encoding())); } ++ void vfcmp_sueq_s (FloatRegister vd, FloatRegister vj, FloatRegister vk) { ASSERT_LSX emit_int32(insn_RRRR( vfcmp_cond_s_op, fcmp_sueq, (int)vk->encoding(), (int)vj->encoding(), (int)vd->encoding())); } ++ void vfcmp_slt_s (FloatRegister vd, FloatRegister vj, FloatRegister vk) { ASSERT_LSX emit_int32(insn_RRRR( vfcmp_cond_s_op, fcmp_slt , (int)vk->encoding(), (int)vj->encoding(), (int)vd->encoding())); } ++ void vfcmp_sult_s (FloatRegister vd, FloatRegister vj, FloatRegister vk) { ASSERT_LSX emit_int32(insn_RRRR( vfcmp_cond_s_op, fcmp_sult, (int)vk->encoding(), (int)vj->encoding(), (int)vd->encoding())); } ++ void vfcmp_sle_s (FloatRegister vd, FloatRegister vj, FloatRegister vk) { ASSERT_LSX emit_int32(insn_RRRR( vfcmp_cond_s_op, fcmp_sle , (int)vk->encoding(), (int)vj->encoding(), (int)vd->encoding())); } ++ void vfcmp_sule_s (FloatRegister vd, FloatRegister vj, FloatRegister vk) { ASSERT_LSX emit_int32(insn_RRRR( vfcmp_cond_s_op, fcmp_sule, (int)vk->encoding(), (int)vj->encoding(), (int)vd->encoding())); } ++ void vfcmp_sne_s (FloatRegister vd, FloatRegister vj, FloatRegister vk) { ASSERT_LSX emit_int32(insn_RRRR( vfcmp_cond_s_op, fcmp_sne , (int)vk->encoding(), (int)vj->encoding(), (int)vd->encoding())); } ++ void vfcmp_sor_s (FloatRegister vd, FloatRegister vj, FloatRegister vk) { ASSERT_LSX emit_int32(insn_RRRR( vfcmp_cond_s_op, fcmp_sor , (int)vk->encoding(), (int)vj->encoding(), (int)vd->encoding())); } ++ void vfcmp_sune_s (FloatRegister vd, FloatRegister vj, FloatRegister vk) { ASSERT_LSX emit_int32(insn_RRRR( vfcmp_cond_s_op, fcmp_sune, (int)vk->encoding(), (int)vj->encoding(), (int)vd->encoding())); } ++ ++ void vfcmp_caf_d (FloatRegister vd, FloatRegister vj, FloatRegister vk) { ASSERT_LSX emit_int32(insn_RRRR( vfcmp_cond_d_op, fcmp_caf , (int)vk->encoding(), (int)vj->encoding(), (int)vd->encoding())); } ++ void vfcmp_cun_d (FloatRegister vd, FloatRegister vj, FloatRegister vk) { ASSERT_LSX emit_int32(insn_RRRR( vfcmp_cond_d_op, fcmp_cun , (int)vk->encoding(), (int)vj->encoding(), (int)vd->encoding())); } ++ void vfcmp_ceq_d (FloatRegister vd, FloatRegister vj, FloatRegister vk) { ASSERT_LSX emit_int32(insn_RRRR( vfcmp_cond_d_op, fcmp_ceq , (int)vk->encoding(), (int)vj->encoding(), (int)vd->encoding())); } ++ void vfcmp_cueq_d (FloatRegister vd, FloatRegister vj, FloatRegister vk) { ASSERT_LSX emit_int32(insn_RRRR( vfcmp_cond_d_op, fcmp_cueq, (int)vk->encoding(), (int)vj->encoding(), (int)vd->encoding())); } ++ void vfcmp_clt_d (FloatRegister vd, FloatRegister vj, FloatRegister vk) { ASSERT_LSX emit_int32(insn_RRRR( vfcmp_cond_d_op, fcmp_clt , (int)vk->encoding(), (int)vj->encoding(), (int)vd->encoding())); } ++ void vfcmp_cult_d (FloatRegister vd, FloatRegister vj, FloatRegister vk) { ASSERT_LSX emit_int32(insn_RRRR( vfcmp_cond_d_op, fcmp_cult, (int)vk->encoding(), (int)vj->encoding(), (int)vd->encoding())); } ++ void vfcmp_cle_d (FloatRegister vd, FloatRegister vj, FloatRegister vk) { ASSERT_LSX emit_int32(insn_RRRR( vfcmp_cond_d_op, fcmp_cle , (int)vk->encoding(), (int)vj->encoding(), (int)vd->encoding())); } ++ void vfcmp_cule_d (FloatRegister vd, FloatRegister vj, FloatRegister vk) { ASSERT_LSX emit_int32(insn_RRRR( vfcmp_cond_d_op, fcmp_cule, (int)vk->encoding(), (int)vj->encoding(), (int)vd->encoding())); } ++ void vfcmp_cne_d (FloatRegister vd, FloatRegister vj, FloatRegister vk) { ASSERT_LSX emit_int32(insn_RRRR( vfcmp_cond_d_op, fcmp_cne , (int)vk->encoding(), (int)vj->encoding(), (int)vd->encoding())); } ++ void vfcmp_cor_d (FloatRegister vd, FloatRegister vj, FloatRegister vk) { ASSERT_LSX emit_int32(insn_RRRR( vfcmp_cond_d_op, fcmp_cor , (int)vk->encoding(), (int)vj->encoding(), (int)vd->encoding())); } ++ void vfcmp_cune_d (FloatRegister vd, FloatRegister vj, FloatRegister vk) { ASSERT_LSX emit_int32(insn_RRRR( vfcmp_cond_d_op, fcmp_cune, (int)vk->encoding(), (int)vj->encoding(), (int)vd->encoding())); } ++ void vfcmp_saf_d (FloatRegister vd, FloatRegister vj, FloatRegister vk) { ASSERT_LSX emit_int32(insn_RRRR( vfcmp_cond_d_op, fcmp_saf , (int)vk->encoding(), (int)vj->encoding(), (int)vd->encoding())); } ++ void vfcmp_sun_d (FloatRegister vd, FloatRegister vj, FloatRegister vk) { ASSERT_LSX emit_int32(insn_RRRR( vfcmp_cond_d_op, fcmp_sun , (int)vk->encoding(), (int)vj->encoding(), (int)vd->encoding())); } ++ void vfcmp_seq_d (FloatRegister vd, FloatRegister vj, FloatRegister vk) { ASSERT_LSX emit_int32(insn_RRRR( vfcmp_cond_d_op, fcmp_seq , (int)vk->encoding(), (int)vj->encoding(), (int)vd->encoding())); } ++ void vfcmp_sueq_d (FloatRegister vd, FloatRegister vj, FloatRegister vk) { ASSERT_LSX emit_int32(insn_RRRR( vfcmp_cond_d_op, fcmp_sueq, (int)vk->encoding(), (int)vj->encoding(), (int)vd->encoding())); } ++ void vfcmp_slt_d (FloatRegister vd, FloatRegister vj, FloatRegister vk) { ASSERT_LSX emit_int32(insn_RRRR( vfcmp_cond_d_op, fcmp_slt , (int)vk->encoding(), (int)vj->encoding(), (int)vd->encoding())); } ++ void vfcmp_sult_d (FloatRegister vd, FloatRegister vj, FloatRegister vk) { ASSERT_LSX emit_int32(insn_RRRR( vfcmp_cond_d_op, fcmp_sult, (int)vk->encoding(), (int)vj->encoding(), (int)vd->encoding())); } ++ void vfcmp_sle_d (FloatRegister vd, FloatRegister vj, FloatRegister vk) { ASSERT_LSX emit_int32(insn_RRRR( vfcmp_cond_d_op, fcmp_sle , (int)vk->encoding(), (int)vj->encoding(), (int)vd->encoding())); } ++ void vfcmp_sule_d (FloatRegister vd, FloatRegister vj, FloatRegister vk) { ASSERT_LSX emit_int32(insn_RRRR( vfcmp_cond_d_op, fcmp_sule, (int)vk->encoding(), (int)vj->encoding(), (int)vd->encoding())); } ++ void vfcmp_sne_d (FloatRegister vd, FloatRegister vj, FloatRegister vk) { ASSERT_LSX emit_int32(insn_RRRR( vfcmp_cond_d_op, fcmp_sne , (int)vk->encoding(), (int)vj->encoding(), (int)vd->encoding())); } ++ void vfcmp_sor_d (FloatRegister vd, FloatRegister vj, FloatRegister vk) { ASSERT_LSX emit_int32(insn_RRRR( vfcmp_cond_d_op, fcmp_sor , (int)vk->encoding(), (int)vj->encoding(), (int)vd->encoding())); } ++ void vfcmp_sune_d (FloatRegister vd, FloatRegister vj, FloatRegister vk) { ASSERT_LSX emit_int32(insn_RRRR( vfcmp_cond_d_op, fcmp_sune, (int)vk->encoding(), (int)vj->encoding(), (int)vd->encoding())); } ++ ++ void xvfcmp_caf_s (FloatRegister xd, FloatRegister xj, FloatRegister xk) { ASSERT_LASX emit_int32(insn_RRRR(xvfcmp_cond_s_op, fcmp_caf , (int)xk->encoding(), (int)xj->encoding(), (int)xd->encoding())); } ++ void xvfcmp_cun_s (FloatRegister xd, FloatRegister xj, FloatRegister xk) { ASSERT_LASX emit_int32(insn_RRRR(xvfcmp_cond_s_op, fcmp_cun , (int)xk->encoding(), (int)xj->encoding(), (int)xd->encoding())); } ++ void xvfcmp_ceq_s (FloatRegister xd, FloatRegister xj, FloatRegister xk) { ASSERT_LASX emit_int32(insn_RRRR(xvfcmp_cond_s_op, fcmp_ceq , (int)xk->encoding(), (int)xj->encoding(), (int)xd->encoding())); } ++ void xvfcmp_cueq_s (FloatRegister xd, FloatRegister xj, FloatRegister xk) { ASSERT_LASX emit_int32(insn_RRRR(xvfcmp_cond_s_op, fcmp_cueq, (int)xk->encoding(), (int)xj->encoding(), (int)xd->encoding())); } ++ void xvfcmp_clt_s (FloatRegister xd, FloatRegister xj, FloatRegister xk) { ASSERT_LASX emit_int32(insn_RRRR(xvfcmp_cond_s_op, fcmp_clt , (int)xk->encoding(), (int)xj->encoding(), (int)xd->encoding())); } ++ void xvfcmp_cult_s (FloatRegister xd, FloatRegister xj, FloatRegister xk) { ASSERT_LASX emit_int32(insn_RRRR(xvfcmp_cond_s_op, fcmp_cult, (int)xk->encoding(), (int)xj->encoding(), (int)xd->encoding())); } ++ void xvfcmp_cle_s (FloatRegister xd, FloatRegister xj, FloatRegister xk) { ASSERT_LASX emit_int32(insn_RRRR(xvfcmp_cond_s_op, fcmp_cle , (int)xk->encoding(), (int)xj->encoding(), (int)xd->encoding())); } ++ void xvfcmp_cule_s (FloatRegister xd, FloatRegister xj, FloatRegister xk) { ASSERT_LASX emit_int32(insn_RRRR(xvfcmp_cond_s_op, fcmp_cule, (int)xk->encoding(), (int)xj->encoding(), (int)xd->encoding())); } ++ void xvfcmp_cne_s (FloatRegister xd, FloatRegister xj, FloatRegister xk) { ASSERT_LASX emit_int32(insn_RRRR(xvfcmp_cond_s_op, fcmp_cne , (int)xk->encoding(), (int)xj->encoding(), (int)xd->encoding())); } ++ void xvfcmp_cor_s (FloatRegister xd, FloatRegister xj, FloatRegister xk) { ASSERT_LASX emit_int32(insn_RRRR(xvfcmp_cond_s_op, fcmp_cor , (int)xk->encoding(), (int)xj->encoding(), (int)xd->encoding())); } ++ void xvfcmp_cune_s (FloatRegister xd, FloatRegister xj, FloatRegister xk) { ASSERT_LASX emit_int32(insn_RRRR(xvfcmp_cond_s_op, fcmp_cune, (int)xk->encoding(), (int)xj->encoding(), (int)xd->encoding())); } ++ void xvfcmp_saf_s (FloatRegister xd, FloatRegister xj, FloatRegister xk) { ASSERT_LASX emit_int32(insn_RRRR(xvfcmp_cond_s_op, fcmp_saf , (int)xk->encoding(), (int)xj->encoding(), (int)xd->encoding())); } ++ void xvfcmp_sun_s (FloatRegister xd, FloatRegister xj, FloatRegister xk) { ASSERT_LASX emit_int32(insn_RRRR(xvfcmp_cond_s_op, fcmp_sun , (int)xk->encoding(), (int)xj->encoding(), (int)xd->encoding())); } ++ void xvfcmp_seq_s (FloatRegister xd, FloatRegister xj, FloatRegister xk) { ASSERT_LASX emit_int32(insn_RRRR(xvfcmp_cond_s_op, fcmp_seq , (int)xk->encoding(), (int)xj->encoding(), (int)xd->encoding())); } ++ void xvfcmp_sueq_s (FloatRegister xd, FloatRegister xj, FloatRegister xk) { ASSERT_LASX emit_int32(insn_RRRR(xvfcmp_cond_s_op, fcmp_sueq, (int)xk->encoding(), (int)xj->encoding(), (int)xd->encoding())); } ++ void xvfcmp_slt_s (FloatRegister xd, FloatRegister xj, FloatRegister xk) { ASSERT_LASX emit_int32(insn_RRRR(xvfcmp_cond_s_op, fcmp_slt , (int)xk->encoding(), (int)xj->encoding(), (int)xd->encoding())); } ++ void xvfcmp_sult_s (FloatRegister xd, FloatRegister xj, FloatRegister xk) { ASSERT_LASX emit_int32(insn_RRRR(xvfcmp_cond_s_op, fcmp_sult, (int)xk->encoding(), (int)xj->encoding(), (int)xd->encoding())); } ++ void xvfcmp_sle_s (FloatRegister xd, FloatRegister xj, FloatRegister xk) { ASSERT_LASX emit_int32(insn_RRRR(xvfcmp_cond_s_op, fcmp_sle , (int)xk->encoding(), (int)xj->encoding(), (int)xd->encoding())); } ++ void xvfcmp_sule_s (FloatRegister xd, FloatRegister xj, FloatRegister xk) { ASSERT_LASX emit_int32(insn_RRRR(xvfcmp_cond_s_op, fcmp_sule, (int)xk->encoding(), (int)xj->encoding(), (int)xd->encoding())); } ++ void xvfcmp_sne_s (FloatRegister xd, FloatRegister xj, FloatRegister xk) { ASSERT_LASX emit_int32(insn_RRRR(xvfcmp_cond_s_op, fcmp_sne , (int)xk->encoding(), (int)xj->encoding(), (int)xd->encoding())); } ++ void xvfcmp_sor_s (FloatRegister xd, FloatRegister xj, FloatRegister xk) { ASSERT_LASX emit_int32(insn_RRRR(xvfcmp_cond_s_op, fcmp_sor , (int)xk->encoding(), (int)xj->encoding(), (int)xd->encoding())); } ++ void xvfcmp_sune_s (FloatRegister xd, FloatRegister xj, FloatRegister xk) { ASSERT_LASX emit_int32(insn_RRRR(xvfcmp_cond_s_op, fcmp_sune, (int)xk->encoding(), (int)xj->encoding(), (int)xd->encoding())); } ++ ++ void xvfcmp_caf_d (FloatRegister xd, FloatRegister xj, FloatRegister xk) { ASSERT_LASX emit_int32(insn_RRRR(xvfcmp_cond_d_op, fcmp_caf , (int)xk->encoding(), (int)xj->encoding(), (int)xd->encoding())); } ++ void xvfcmp_cun_d (FloatRegister xd, FloatRegister xj, FloatRegister xk) { ASSERT_LASX emit_int32(insn_RRRR(xvfcmp_cond_d_op, fcmp_cun , (int)xk->encoding(), (int)xj->encoding(), (int)xd->encoding())); } ++ void xvfcmp_ceq_d (FloatRegister xd, FloatRegister xj, FloatRegister xk) { ASSERT_LASX emit_int32(insn_RRRR(xvfcmp_cond_d_op, fcmp_ceq , (int)xk->encoding(), (int)xj->encoding(), (int)xd->encoding())); } ++ void xvfcmp_cueq_d (FloatRegister xd, FloatRegister xj, FloatRegister xk) { ASSERT_LASX emit_int32(insn_RRRR(xvfcmp_cond_d_op, fcmp_cueq, (int)xk->encoding(), (int)xj->encoding(), (int)xd->encoding())); } ++ void xvfcmp_clt_d (FloatRegister xd, FloatRegister xj, FloatRegister xk) { ASSERT_LASX emit_int32(insn_RRRR(xvfcmp_cond_d_op, fcmp_clt , (int)xk->encoding(), (int)xj->encoding(), (int)xd->encoding())); } ++ void xvfcmp_cult_d (FloatRegister xd, FloatRegister xj, FloatRegister xk) { ASSERT_LASX emit_int32(insn_RRRR(xvfcmp_cond_d_op, fcmp_cult, (int)xk->encoding(), (int)xj->encoding(), (int)xd->encoding())); } ++ void xvfcmp_cle_d (FloatRegister xd, FloatRegister xj, FloatRegister xk) { ASSERT_LASX emit_int32(insn_RRRR(xvfcmp_cond_d_op, fcmp_cle , (int)xk->encoding(), (int)xj->encoding(), (int)xd->encoding())); } ++ void xvfcmp_cule_d (FloatRegister xd, FloatRegister xj, FloatRegister xk) { ASSERT_LASX emit_int32(insn_RRRR(xvfcmp_cond_d_op, fcmp_cule, (int)xk->encoding(), (int)xj->encoding(), (int)xd->encoding())); } ++ void xvfcmp_cne_d (FloatRegister xd, FloatRegister xj, FloatRegister xk) { ASSERT_LASX emit_int32(insn_RRRR(xvfcmp_cond_d_op, fcmp_cne , (int)xk->encoding(), (int)xj->encoding(), (int)xd->encoding())); } ++ void xvfcmp_cor_d (FloatRegister xd, FloatRegister xj, FloatRegister xk) { ASSERT_LASX emit_int32(insn_RRRR(xvfcmp_cond_d_op, fcmp_cor , (int)xk->encoding(), (int)xj->encoding(), (int)xd->encoding())); } ++ void xvfcmp_cune_d (FloatRegister xd, FloatRegister xj, FloatRegister xk) { ASSERT_LASX emit_int32(insn_RRRR(xvfcmp_cond_d_op, fcmp_cune, (int)xk->encoding(), (int)xj->encoding(), (int)xd->encoding())); } ++ void xvfcmp_saf_d (FloatRegister xd, FloatRegister xj, FloatRegister xk) { ASSERT_LASX emit_int32(insn_RRRR(xvfcmp_cond_d_op, fcmp_saf , (int)xk->encoding(), (int)xj->encoding(), (int)xd->encoding())); } ++ void xvfcmp_sun_d (FloatRegister xd, FloatRegister xj, FloatRegister xk) { ASSERT_LASX emit_int32(insn_RRRR(xvfcmp_cond_d_op, fcmp_sun , (int)xk->encoding(), (int)xj->encoding(), (int)xd->encoding())); } ++ void xvfcmp_seq_d (FloatRegister xd, FloatRegister xj, FloatRegister xk) { ASSERT_LASX emit_int32(insn_RRRR(xvfcmp_cond_d_op, fcmp_seq , (int)xk->encoding(), (int)xj->encoding(), (int)xd->encoding())); } ++ void xvfcmp_sueq_d (FloatRegister xd, FloatRegister xj, FloatRegister xk) { ASSERT_LASX emit_int32(insn_RRRR(xvfcmp_cond_d_op, fcmp_sueq, (int)xk->encoding(), (int)xj->encoding(), (int)xd->encoding())); } ++ void xvfcmp_slt_d (FloatRegister xd, FloatRegister xj, FloatRegister xk) { ASSERT_LASX emit_int32(insn_RRRR(xvfcmp_cond_d_op, fcmp_slt , (int)xk->encoding(), (int)xj->encoding(), (int)xd->encoding())); } ++ void xvfcmp_sult_d (FloatRegister xd, FloatRegister xj, FloatRegister xk) { ASSERT_LASX emit_int32(insn_RRRR(xvfcmp_cond_d_op, fcmp_sult, (int)xk->encoding(), (int)xj->encoding(), (int)xd->encoding())); } ++ void xvfcmp_sle_d (FloatRegister xd, FloatRegister xj, FloatRegister xk) { ASSERT_LASX emit_int32(insn_RRRR(xvfcmp_cond_d_op, fcmp_sle , (int)xk->encoding(), (int)xj->encoding(), (int)xd->encoding())); } ++ void xvfcmp_sule_d (FloatRegister xd, FloatRegister xj, FloatRegister xk) { ASSERT_LASX emit_int32(insn_RRRR(xvfcmp_cond_d_op, fcmp_sule, (int)xk->encoding(), (int)xj->encoding(), (int)xd->encoding())); } ++ void xvfcmp_sne_d (FloatRegister xd, FloatRegister xj, FloatRegister xk) { ASSERT_LASX emit_int32(insn_RRRR(xvfcmp_cond_d_op, fcmp_sne , (int)xk->encoding(), (int)xj->encoding(), (int)xd->encoding())); } ++ void xvfcmp_sor_d (FloatRegister xd, FloatRegister xj, FloatRegister xk) { ASSERT_LASX emit_int32(insn_RRRR(xvfcmp_cond_d_op, fcmp_sor , (int)xk->encoding(), (int)xj->encoding(), (int)xd->encoding())); } ++ void xvfcmp_sune_d (FloatRegister xd, FloatRegister xj, FloatRegister xk) { ASSERT_LASX emit_int32(insn_RRRR(xvfcmp_cond_d_op, fcmp_sune, (int)xk->encoding(), (int)xj->encoding(), (int)xd->encoding())); } ++ ++ void vbitsel_v(FloatRegister vd, FloatRegister vj, FloatRegister vk, FloatRegister va) { ASSERT_LSX emit_int32(insn_RRRR( vbitsel_v_op, (int)va->encoding(), (int)vk->encoding(), (int)vj->encoding(), (int)vd->encoding())); } ++ void xvbitsel_v(FloatRegister xd, FloatRegister xj, FloatRegister xk, FloatRegister xa) { ASSERT_LASX emit_int32(insn_RRRR(xvbitsel_v_op, (int)xa->encoding(), (int)xk->encoding(), (int)xj->encoding(), (int)xd->encoding())); } ++ ++ void vinsgr2vr_b(FloatRegister vd, Register rj, int ui4) { ASSERT_LSX emit_int32(insn_I4RR( vinsgr2vr_b_op, ui4, (int)rj->encoding(), (int)vd->encoding())); } ++ void vinsgr2vr_h(FloatRegister vd, Register rj, int ui3) { ASSERT_LSX emit_int32(insn_I3RR( vinsgr2vr_h_op, ui3, (int)rj->encoding(), (int)vd->encoding())); } ++ void vinsgr2vr_w(FloatRegister vd, Register rj, int ui2) { ASSERT_LSX emit_int32(insn_I2RR( vinsgr2vr_w_op, ui2, (int)rj->encoding(), (int)vd->encoding())); } ++ void vinsgr2vr_d(FloatRegister vd, Register rj, int ui1) { ASSERT_LSX emit_int32(insn_I1RR( vinsgr2vr_d_op, ui1, (int)rj->encoding(), (int)vd->encoding())); } ++ ++ void xvinsgr2vr_w(FloatRegister xd, Register rj, int ui3) { ASSERT_LASX emit_int32(insn_I3RR(xvinsgr2vr_w_op, ui3, (int)rj->encoding(), (int)xd->encoding())); } ++ void xvinsgr2vr_d(FloatRegister xd, Register rj, int ui2) { ASSERT_LASX emit_int32(insn_I2RR(xvinsgr2vr_d_op, ui2, (int)rj->encoding(), (int)xd->encoding())); } ++ ++ void vpickve2gr_b(Register rd, FloatRegister vj, int ui4) { ASSERT_LSX emit_int32(insn_I4RR( vpickve2gr_b_op, ui4, (int)vj->encoding(), (int)rd->encoding())); } ++ void vpickve2gr_h(Register rd, FloatRegister vj, int ui3) { ASSERT_LSX emit_int32(insn_I3RR( vpickve2gr_h_op, ui3, (int)vj->encoding(), (int)rd->encoding())); } ++ void vpickve2gr_w(Register rd, FloatRegister vj, int ui2) { ASSERT_LSX emit_int32(insn_I2RR( vpickve2gr_w_op, ui2, (int)vj->encoding(), (int)rd->encoding())); } ++ void vpickve2gr_d(Register rd, FloatRegister vj, int ui1) { ASSERT_LSX emit_int32(insn_I1RR( vpickve2gr_d_op, ui1, (int)vj->encoding(), (int)rd->encoding())); } ++ ++ void vpickve2gr_bu(Register rd, FloatRegister vj, int ui4) { ASSERT_LSX emit_int32(insn_I4RR( vpickve2gr_bu_op, ui4, (int)vj->encoding(), (int)rd->encoding())); } ++ void vpickve2gr_hu(Register rd, FloatRegister vj, int ui3) { ASSERT_LSX emit_int32(insn_I3RR( vpickve2gr_hu_op, ui3, (int)vj->encoding(), (int)rd->encoding())); } ++ void vpickve2gr_wu(Register rd, FloatRegister vj, int ui2) { ASSERT_LSX emit_int32(insn_I2RR( vpickve2gr_wu_op, ui2, (int)vj->encoding(), (int)rd->encoding())); } ++ void vpickve2gr_du(Register rd, FloatRegister vj, int ui1) { ASSERT_LSX emit_int32(insn_I1RR( vpickve2gr_du_op, ui1, (int)vj->encoding(), (int)rd->encoding())); } ++ ++ void xvpickve2gr_w(Register rd, FloatRegister xj, int ui3) { ASSERT_LASX emit_int32(insn_I3RR(xvpickve2gr_w_op, ui3, (int)xj->encoding(), (int)rd->encoding())); } ++ void xvpickve2gr_d(Register rd, FloatRegister xj, int ui2) { ASSERT_LASX emit_int32(insn_I2RR(xvpickve2gr_d_op, ui2, (int)xj->encoding(), (int)rd->encoding())); } ++ ++ void xvpickve2gr_wu(Register rd, FloatRegister xj, int ui3) { ASSERT_LASX emit_int32(insn_I3RR(xvpickve2gr_wu_op, ui3, (int)xj->encoding(), (int)rd->encoding())); } ++ void xvpickve2gr_du(Register rd, FloatRegister xj, int ui2) { ASSERT_LASX emit_int32(insn_I2RR(xvpickve2gr_du_op, ui2, (int)xj->encoding(), (int)rd->encoding())); } ++ ++ void vreplgr2vr_b(FloatRegister vd, Register rj) { ASSERT_LSX emit_int32(insn_RR( vreplgr2vr_b_op, (int)rj->encoding(), (int)vd->encoding())); } ++ void vreplgr2vr_h(FloatRegister vd, Register rj) { ASSERT_LSX emit_int32(insn_RR( vreplgr2vr_h_op, (int)rj->encoding(), (int)vd->encoding())); } ++ void vreplgr2vr_w(FloatRegister vd, Register rj) { ASSERT_LSX emit_int32(insn_RR( vreplgr2vr_w_op, (int)rj->encoding(), (int)vd->encoding())); } ++ void vreplgr2vr_d(FloatRegister vd, Register rj) { ASSERT_LSX emit_int32(insn_RR( vreplgr2vr_d_op, (int)rj->encoding(), (int)vd->encoding())); } ++ void xvreplgr2vr_b(FloatRegister xd, Register rj) { ASSERT_LASX emit_int32(insn_RR(xvreplgr2vr_b_op, (int)rj->encoding(), (int)xd->encoding())); } ++ void xvreplgr2vr_h(FloatRegister xd, Register rj) { ASSERT_LASX emit_int32(insn_RR(xvreplgr2vr_h_op, (int)rj->encoding(), (int)xd->encoding())); } ++ void xvreplgr2vr_w(FloatRegister xd, Register rj) { ASSERT_LASX emit_int32(insn_RR(xvreplgr2vr_w_op, (int)rj->encoding(), (int)xd->encoding())); } ++ void xvreplgr2vr_d(FloatRegister xd, Register rj) { ASSERT_LASX emit_int32(insn_RR(xvreplgr2vr_d_op, (int)rj->encoding(), (int)xd->encoding())); } ++ ++ void vreplvei_b(FloatRegister vd, FloatRegister vj, int ui4) { ASSERT_LSX emit_int32(insn_I4RR(vreplvei_b_op, ui4, (int)vj->encoding(), (int)vd->encoding())); } ++ void vreplvei_h(FloatRegister vd, FloatRegister vj, int ui3) { ASSERT_LSX emit_int32(insn_I3RR(vreplvei_h_op, ui3, (int)vj->encoding(), (int)vd->encoding())); } ++ void vreplvei_w(FloatRegister vd, FloatRegister vj, int ui2) { ASSERT_LSX emit_int32(insn_I2RR(vreplvei_w_op, ui2, (int)vj->encoding(), (int)vd->encoding())); } ++ void vreplvei_d(FloatRegister vd, FloatRegister vj, int ui1) { ASSERT_LSX emit_int32(insn_I1RR(vreplvei_d_op, ui1, (int)vj->encoding(), (int)vd->encoding())); } ++ ++ void xvreplve0_b(FloatRegister xd, FloatRegister xj) { ASSERT_LASX emit_int32(insn_RR(xvreplve0_b_op, (int)xj->encoding(), (int)xd->encoding())); } ++ void xvreplve0_h(FloatRegister xd, FloatRegister xj) { ASSERT_LASX emit_int32(insn_RR(xvreplve0_h_op, (int)xj->encoding(), (int)xd->encoding())); } ++ void xvreplve0_w(FloatRegister xd, FloatRegister xj) { ASSERT_LASX emit_int32(insn_RR(xvreplve0_w_op, (int)xj->encoding(), (int)xd->encoding())); } ++ void xvreplve0_d(FloatRegister xd, FloatRegister xj) { ASSERT_LASX emit_int32(insn_RR(xvreplve0_d_op, (int)xj->encoding(), (int)xd->encoding())); } ++ void xvreplve0_q(FloatRegister xd, FloatRegister xj) { ASSERT_LASX emit_int32(insn_RR(xvreplve0_q_op, (int)xj->encoding(), (int)xd->encoding())); } ++ ++ void xvinsve0_w(FloatRegister xd, FloatRegister xj, int ui3) { ASSERT_LASX emit_int32(insn_I3RR(xvinsve0_w_op, ui3, (int)xj->encoding(), (int)xd->encoding())); } ++ void xvinsve0_d(FloatRegister xd, FloatRegister xj, int ui2) { ASSERT_LASX emit_int32(insn_I2RR(xvinsve0_d_op, ui2, (int)xj->encoding(), (int)xd->encoding())); } ++ ++ void xvpickve_w(FloatRegister xd, FloatRegister xj, int ui3) { ASSERT_LASX emit_int32(insn_I3RR(xvpickve_w_op, ui3, (int)xj->encoding(), (int)xd->encoding())); } ++ void xvpickve_d(FloatRegister xd, FloatRegister xj, int ui2) { ASSERT_LASX emit_int32(insn_I2RR(xvpickve_d_op, ui2, (int)xj->encoding(), (int)xd->encoding())); } ++ ++ void vshuf_b(FloatRegister vd, FloatRegister vj, FloatRegister vk, FloatRegister va) { ASSERT_LSX emit_int32(insn_RRRR( vshuf_b_op, (int)va->encoding(), (int)vk->encoding(), (int)vj->encoding(), (int)vd->encoding())); } ++ void xvshuf_b(FloatRegister xd, FloatRegister xj, FloatRegister xk, FloatRegister xa) { ASSERT_LASX emit_int32(insn_RRRR(xvshuf_b_op, (int)xa->encoding(), (int)xk->encoding(), (int)xj->encoding(), (int)xd->encoding())); } ++ ++ void vshuf_h(FloatRegister vd, FloatRegister vj, FloatRegister vk) { ASSERT_LSX emit_int32(insn_RRR( vshuf_h_op, (int)vk->encoding(), (int)vj->encoding(), (int)vd->encoding())); } ++ void vshuf_w(FloatRegister vd, FloatRegister vj, FloatRegister vk) { ASSERT_LSX emit_int32(insn_RRR( vshuf_w_op, (int)vk->encoding(), (int)vj->encoding(), (int)vd->encoding())); } ++ void vshuf_d(FloatRegister vd, FloatRegister vj, FloatRegister vk) { ASSERT_LSX emit_int32(insn_RRR( vshuf_d_op, (int)vk->encoding(), (int)vj->encoding(), (int)vd->encoding())); } ++ ++ void xvshuf_h(FloatRegister xd, FloatRegister xj, FloatRegister xk) { ASSERT_LASX emit_int32(insn_RRR(xvshuf_h_op, (int)xk->encoding(), (int)xj->encoding(), (int)xd->encoding())); } ++ void xvshuf_w(FloatRegister xd, FloatRegister xj, FloatRegister xk) { ASSERT_LASX emit_int32(insn_RRR(xvshuf_w_op, (int)xk->encoding(), (int)xj->encoding(), (int)xd->encoding())); } ++ void xvshuf_d(FloatRegister xd, FloatRegister xj, FloatRegister xk) { ASSERT_LASX emit_int32(insn_RRR(xvshuf_d_op, (int)xk->encoding(), (int)xj->encoding(), (int)xd->encoding())); } ++ ++ void xvperm_w(FloatRegister xd, FloatRegister xj, FloatRegister xk) { ASSERT_LASX emit_int32(insn_RRR(xvperm_w_op, (int)xk->encoding(), (int)xj->encoding(), (int)xd->encoding())); } ++ ++ void vshuf4i_b(FloatRegister vd, FloatRegister vj, int ui8) { ASSERT_LSX assert(is_uimm(ui8, 8), "not a unsigned 8-bit int"); emit_int32(insn_I8RR( vshuf4i_b_op, ui8, (int)vj->encoding(), (int)vd->encoding())); } ++ void vshuf4i_h(FloatRegister vd, FloatRegister vj, int ui8) { ASSERT_LSX assert(is_uimm(ui8, 8), "not a unsigned 8-bit int"); emit_int32(insn_I8RR( vshuf4i_h_op, ui8, (int)vj->encoding(), (int)vd->encoding())); } ++ void vshuf4i_w(FloatRegister vd, FloatRegister vj, int ui8) { ASSERT_LSX assert(is_uimm(ui8, 8), "not a unsigned 8-bit int"); emit_int32(insn_I8RR( vshuf4i_w_op, ui8, (int)vj->encoding(), (int)vd->encoding())); } ++ void xvshuf4i_b(FloatRegister xd, FloatRegister xj, int ui8) { ASSERT_LASX assert(is_uimm(ui8, 8), "not a unsigned 8-bit int"); emit_int32(insn_I8RR(xvshuf4i_b_op, ui8, (int)xj->encoding(), (int)xd->encoding())); } ++ void xvshuf4i_h(FloatRegister xd, FloatRegister xj, int ui8) { ASSERT_LASX assert(is_uimm(ui8, 8), "not a unsigned 8-bit int"); emit_int32(insn_I8RR(xvshuf4i_h_op, ui8, (int)xj->encoding(), (int)xd->encoding())); } ++ void xvshuf4i_w(FloatRegister xd, FloatRegister xj, int ui8) { ASSERT_LASX assert(is_uimm(ui8, 8), "not a unsigned 8-bit int"); emit_int32(insn_I8RR(xvshuf4i_w_op, ui8, (int)xj->encoding(), (int)xd->encoding())); } ++ ++ void vshuf4i_d(FloatRegister vd, FloatRegister vj, int ui8) { ASSERT_LSX assert(is_uimm(ui8, 8), "not a unsigned 8-bit int"); emit_int32(insn_I8RR( vshuf4i_d_op, ui8, (int)vj->encoding(), (int)vd->encoding())); } ++ void xvshuf4i_d(FloatRegister xd, FloatRegister xj, int ui8) { ASSERT_LASX assert(is_uimm(ui8, 8), "not a unsigned 8-bit int"); emit_int32(insn_I8RR(xvshuf4i_d_op, ui8, (int)xj->encoding(), (int)xd->encoding())); } ++ ++ void vpermi_w(FloatRegister vd, FloatRegister vj, int ui8) { ASSERT_LSX assert(is_uimm(ui8, 8), "not a unsigned 8-bit int"); emit_int32(insn_I8RR( vpermi_w_op, ui8, (int)vj->encoding(), (int)vd->encoding())); } ++ void xvpermi_w(FloatRegister xd, FloatRegister xj, int ui8) { ASSERT_LASX assert(is_uimm(ui8, 8), "not a unsigned 8-bit int"); emit_int32(insn_I8RR(xvpermi_w_op, ui8, (int)xj->encoding(), (int)xd->encoding())); } ++ ++ void xvpermi_d(FloatRegister xd, FloatRegister xj, int ui8) { ASSERT_LASX assert(is_uimm(ui8, 8), "not a unsigned 8-bit int"); emit_int32(insn_I8RR(xvpermi_d_op, ui8, (int)xj->encoding(), (int)xd->encoding())); } ++ ++ void xvpermi_q(FloatRegister xd, FloatRegister xj, int ui8) { ASSERT_LASX assert(is_uimm(ui8, 8), "not a unsigned 8-bit int"); emit_int32(insn_I8RR(xvpermi_q_op, ui8, (int)xj->encoding(), (int)xd->encoding())); } ++ ++ void vld(FloatRegister vd, Register rj, int si12) { ASSERT_LSX assert(is_simm(si12, 12), "not a signed 12-bit int"); emit_int32(insn_I12RR( vld_op, si12, (int)rj->encoding(), (int)vd->encoding()));} ++ void xvld(FloatRegister xd, Register rj, int si12) { ASSERT_LASX assert(is_simm(si12, 12), "not a signed 12-bit int"); emit_int32(insn_I12RR(xvld_op, si12, (int)rj->encoding(), (int)xd->encoding()));} ++ ++ void vst(FloatRegister vd, Register rj, int si12) { ASSERT_LSX assert(is_simm(si12, 12), "not a signed 12-bit int"); emit_int32(insn_I12RR( vst_op, si12, (int)rj->encoding(), (int)vd->encoding()));} ++ void xvst(FloatRegister xd, Register rj, int si12) { ASSERT_LASX assert(is_simm(si12, 12), "not a signed 12-bit int"); emit_int32(insn_I12RR(xvst_op, si12, (int)rj->encoding(), (int)xd->encoding()));} ++ ++ void vldx(FloatRegister vd, Register rj, Register rk) { ASSERT_LSX emit_int32(insn_RRR( vldx_op, (int)rk->encoding(), (int)rj->encoding(), (int)vd->encoding())); } ++ void xvldx(FloatRegister xd, Register rj, Register rk) { ASSERT_LASX emit_int32(insn_RRR(xvldx_op, (int)rk->encoding(), (int)rj->encoding(), (int)xd->encoding())); } ++ ++ void vstx(FloatRegister vd, Register rj, Register rk) { ASSERT_LSX emit_int32(insn_RRR( vstx_op, (int)rk->encoding(), (int)rj->encoding(), (int)vd->encoding())); } ++ void xvstx(FloatRegister xd, Register rj, Register rk) { ASSERT_LASX emit_int32(insn_RRR(xvstx_op, (int)rk->encoding(), (int)rj->encoding(), (int)xd->encoding())); } ++ ++#undef ASSERT_LSX ++#undef ASSERT_LASX ++ ++public: ++ // Creation ++ Assembler(CodeBuffer* code) : AbstractAssembler(code) {} ++ ++ // Decoding ++ static address locate_operand(address inst, WhichOperand which); ++ static address locate_next_instruction(address inst); ++}; ++ ++#endif // CPU_LOONGARCH_VM_ASSEMBLER_LOONGARCH_HPP +diff --git a/hotspot/src/cpu/loongarch/vm/assembler_loongarch.inline.hpp b/hotspot/src/cpu/loongarch/vm/assembler_loongarch.inline.hpp +new file mode 100644 +index 0000000000..21f3f0ba85 +--- /dev/null ++++ b/hotspot/src/cpu/loongarch/vm/assembler_loongarch.inline.hpp +@@ -0,0 +1,47 @@ ++/* ++ * Copyright (c) 1997, 2010, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2015, 2020, Loongson Technology. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ * ++ */ ++ ++#ifndef CPU_LOONGARCH_VM_ASSEMBLER_LOONGARCH_INLINE_HPP ++#define CPU_LOONGARCH_VM_ASSEMBLER_LOONGARCH_INLINE_HPP ++ ++#include "asm/assembler.inline.hpp" ++#include "asm/codeBuffer.hpp" ++#include "code/codeCache.hpp" ++ ++inline void Assembler::emit_int32(int x) { ++ AbstractAssembler::emit_int32(x); ++} ++ ++inline void Assembler::emit_data(int x, relocInfo::relocType rtype) { ++ relocate(rtype); ++ emit_int32(x); ++} ++ ++inline void Assembler::emit_data(int x, RelocationHolder const& rspec) { ++ relocate(rspec); ++ emit_int32(x); ++} ++ ++#endif // CPU_LOONGARCH_VM_ASSEMBLER_LOONGARCH_INLINE_HPP +diff --git a/hotspot/src/cpu/loongarch/vm/bytecodeInterpreter_loongarch.hpp b/hotspot/src/cpu/loongarch/vm/bytecodeInterpreter_loongarch.hpp +new file mode 100644 +index 0000000000..32775e9bc3 +--- /dev/null ++++ b/hotspot/src/cpu/loongarch/vm/bytecodeInterpreter_loongarch.hpp +@@ -0,0 +1,110 @@ ++/* ++ * Copyright (c) 2002, 2010, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2015, 2020, Loongson Technology. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ * ++ */ ++ ++#ifndef CPU_LOONGARCH_VM_BYTECODEINTERPRETER_LOONGARCH_HPP ++#define CPU_LOONGARCH_VM_BYTECODEINTERPRETER_LOONGARCH_HPP ++ ++// Platform specific for C++ based Interpreter ++#define LOTS_OF_REGS /* Lets interpreter use plenty of registers */ ++ ++private: ++ ++ // save the bottom of the stack after frame manager setup. For ease of restoration after return ++ // from recursive interpreter call ++ intptr_t* _frame_bottom; /* saved bottom of frame manager frame */ ++ intptr_t* _last_Java_pc; /* pc to return to in frame manager */ ++ intptr_t* _sender_sp; /* sender's sp before stack (locals) extension */ ++ interpreterState _self_link; /* Previous interpreter state */ /* sometimes points to self??? */ ++ double _native_fresult; /* save result of native calls that might return floats */ ++ intptr_t _native_lresult; /* save result of native calls that might return handle/longs */ ++public: ++ ++ static void pd_layout_interpreterState(interpreterState istate, address last_Java_pc, intptr_t* last_Java_fp); ++ inline intptr_t* sender_sp() { ++ return _sender_sp; ++ } ++ ++ ++#define SET_LAST_JAVA_FRAME() ++ ++#define RESET_LAST_JAVA_FRAME() THREAD->frame_anchor()->set_flags(0); ++ ++/* ++ * Macros for accessing the stack. ++ */ ++#undef STACK_INT ++#undef STACK_FLOAT ++#undef STACK_ADDR ++#undef STACK_OBJECT ++#undef STACK_DOUBLE ++#undef STACK_LONG ++ ++// JavaStack Implementation ++ ++#define GET_STACK_SLOT(offset) (*((intptr_t*) &topOfStack[-(offset)])) ++#define STACK_SLOT(offset) ((address) &topOfStack[-(offset)]) ++#define STACK_ADDR(offset) (*((address *) &topOfStack[-(offset)])) ++#define STACK_INT(offset) (*((jint*) &topOfStack[-(offset)])) ++#define STACK_FLOAT(offset) (*((jfloat *) &topOfStack[-(offset)])) ++#define STACK_OBJECT(offset) (*((oop *) &topOfStack [-(offset)])) ++#define STACK_DOUBLE(offset) (((VMJavaVal64*) &topOfStack[-(offset)])->d) ++#define STACK_LONG(offset) (((VMJavaVal64 *) &topOfStack[-(offset)])->l) ++ ++#define SET_STACK_SLOT(value, offset) (*(intptr_t*)&topOfStack[-(offset)] = *(intptr_t*)(value)) ++#define SET_STACK_ADDR(value, offset) (*((address *)&topOfStack[-(offset)]) = (value)) ++#define SET_STACK_INT(value, offset) (*((jint *)&topOfStack[-(offset)]) = (value)) ++#define SET_STACK_FLOAT(value, offset) (*((jfloat *)&topOfStack[-(offset)]) = (value)) ++#define SET_STACK_OBJECT(value, offset) (*((oop *)&topOfStack[-(offset)]) = (value)) ++#define SET_STACK_DOUBLE(value, offset) (((VMJavaVal64*)&topOfStack[-(offset)])->d = (value)) ++#define SET_STACK_DOUBLE_FROM_ADDR(addr, offset) (((VMJavaVal64*)&topOfStack[-(offset)])->d = \ ++ ((VMJavaVal64*)(addr))->d) ++#define SET_STACK_LONG(value, offset) (((VMJavaVal64*)&topOfStack[-(offset)])->l = (value)) ++#define SET_STACK_LONG_FROM_ADDR(addr, offset) (((VMJavaVal64*)&topOfStack[-(offset)])->l = \ ++ ((VMJavaVal64*)(addr))->l) ++// JavaLocals implementation ++ ++#define LOCALS_SLOT(offset) ((intptr_t*)&locals[-(offset)]) ++#define LOCALS_ADDR(offset) ((address)locals[-(offset)]) ++#define LOCALS_INT(offset) (*((jint*)&locals[-(offset)])) ++#define LOCALS_FLOAT(offset) (*((jfloat*)&locals[-(offset)])) ++#define LOCALS_OBJECT(offset) ((oop)locals[-(offset)]) ++#define LOCALS_DOUBLE(offset) (((VMJavaVal64*)&locals[-((offset) + 1)])->d) ++#define LOCALS_LONG(offset) (((VMJavaVal64*)&locals[-((offset) + 1)])->l) ++#define LOCALS_LONG_AT(offset) (((address)&locals[-((offset) + 1)])) ++#define LOCALS_DOUBLE_AT(offset) (((address)&locals[-((offset) + 1)])) ++ ++#define SET_LOCALS_SLOT(value, offset) (*(intptr_t*)&locals[-(offset)] = *(intptr_t *)(value)) ++#define SET_LOCALS_ADDR(value, offset) (*((address *)&locals[-(offset)]) = (value)) ++#define SET_LOCALS_INT(value, offset) (*((jint *)&locals[-(offset)]) = (value)) ++#define SET_LOCALS_FLOAT(value, offset) (*((jfloat *)&locals[-(offset)]) = (value)) ++#define SET_LOCALS_OBJECT(value, offset) (*((oop *)&locals[-(offset)]) = (value)) ++#define SET_LOCALS_DOUBLE(value, offset) (((VMJavaVal64*)&locals[-((offset)+1)])->d = (value)) ++#define SET_LOCALS_LONG(value, offset) (((VMJavaVal64*)&locals[-((offset)+1)])->l = (value)) ++#define SET_LOCALS_DOUBLE_FROM_ADDR(addr, offset) (((VMJavaVal64*)&locals[-((offset)+1)])->d = \ ++ ((VMJavaVal64*)(addr))->d) ++#define SET_LOCALS_LONG_FROM_ADDR(addr, offset) (((VMJavaVal64*)&locals[-((offset)+1)])->l = \ ++ ((VMJavaVal64*)(addr))->l) ++ ++#endif // CPU_LOONGARCH_VM_BYTECODEINTERPRETER_LOONGARCH_HPP +diff --git a/hotspot/src/cpu/loongarch/vm/bytecodeInterpreter_loongarch.inline.hpp b/hotspot/src/cpu/loongarch/vm/bytecodeInterpreter_loongarch.inline.hpp +new file mode 100644 +index 0000000000..07df527e94 +--- /dev/null ++++ b/hotspot/src/cpu/loongarch/vm/bytecodeInterpreter_loongarch.inline.hpp +@@ -0,0 +1,286 @@ ++/* ++ * Copyright (c) 2002, 2010, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2015, 2020, Loongson Technology. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ * ++ */ ++ ++#ifndef CPU_LOONGARCH_VM_BYTECODEINTERPRETER_LOONGARCH_INLINE_HPP ++#define CPU_LOONGARCH_VM_BYTECODEINTERPRETER_LOONGARCH_INLINE_HPP ++ ++// Inline interpreter functions for LoongArch ++ ++inline jfloat BytecodeInterpreter::VMfloatAdd(jfloat op1, jfloat op2) { return op1 + op2; } ++inline jfloat BytecodeInterpreter::VMfloatSub(jfloat op1, jfloat op2) { return op1 - op2; } ++inline jfloat BytecodeInterpreter::VMfloatMul(jfloat op1, jfloat op2) { return op1 * op2; } ++inline jfloat BytecodeInterpreter::VMfloatDiv(jfloat op1, jfloat op2) { return op1 / op2; } ++inline jfloat BytecodeInterpreter::VMfloatRem(jfloat op1, jfloat op2) { return fmod(op1, op2); } ++ ++inline jfloat BytecodeInterpreter::VMfloatNeg(jfloat op) { return -op; } ++ ++inline int32_t BytecodeInterpreter::VMfloatCompare(jfloat op1, jfloat op2, int32_t direction) { ++ return ( op1 < op2 ? -1 : ++ op1 > op2 ? 1 : ++ op1 == op2 ? 0 : ++ (direction == -1 || direction == 1) ? direction : 0); ++ ++} ++ ++inline void BytecodeInterpreter::VMmemCopy64(uint32_t to[2], const uint32_t from[2]) { ++ // x86 can do unaligned copies but not 64bits at a time ++ to[0] = from[0]; to[1] = from[1]; ++} ++ ++// The long operations depend on compiler support for "long long" on x86 ++ ++inline jlong BytecodeInterpreter::VMlongAdd(jlong op1, jlong op2) { ++ return op1 + op2; ++} ++ ++inline jlong BytecodeInterpreter::VMlongAnd(jlong op1, jlong op2) { ++ return op1 & op2; ++} ++ ++inline jlong BytecodeInterpreter::VMlongDiv(jlong op1, jlong op2) { ++ // QQQ what about check and throw... ++ return op1 / op2; ++} ++ ++inline jlong BytecodeInterpreter::VMlongMul(jlong op1, jlong op2) { ++ return op1 * op2; ++} ++ ++inline jlong BytecodeInterpreter::VMlongOr(jlong op1, jlong op2) { ++ return op1 | op2; ++} ++ ++inline jlong BytecodeInterpreter::VMlongSub(jlong op1, jlong op2) { ++ return op1 - op2; ++} ++ ++inline jlong BytecodeInterpreter::VMlongXor(jlong op1, jlong op2) { ++ return op1 ^ op2; ++} ++ ++inline jlong BytecodeInterpreter::VMlongRem(jlong op1, jlong op2) { ++ return op1 % op2; ++} ++ ++inline jlong BytecodeInterpreter::VMlongUshr(jlong op1, jint op2) { ++ // CVM did this 0x3f mask, is the really needed??? QQQ ++ return ((unsigned long long) op1) >> (op2 & 0x3F); ++} ++ ++inline jlong BytecodeInterpreter::VMlongShr(jlong op1, jint op2) { ++ return op1 >> (op2 & 0x3F); ++} ++ ++inline jlong BytecodeInterpreter::VMlongShl(jlong op1, jint op2) { ++ return op1 << (op2 & 0x3F); ++} ++ ++inline jlong BytecodeInterpreter::VMlongNeg(jlong op) { ++ return -op; ++} ++ ++inline jlong BytecodeInterpreter::VMlongNot(jlong op) { ++ return ~op; ++} ++ ++inline int32_t BytecodeInterpreter::VMlongLtz(jlong op) { ++ return (op <= 0); ++} ++ ++inline int32_t BytecodeInterpreter::VMlongGez(jlong op) { ++ return (op >= 0); ++} ++ ++inline int32_t BytecodeInterpreter::VMlongEqz(jlong op) { ++ return (op == 0); ++} ++ ++inline int32_t BytecodeInterpreter::VMlongEq(jlong op1, jlong op2) { ++ return (op1 == op2); ++} ++ ++inline int32_t BytecodeInterpreter::VMlongNe(jlong op1, jlong op2) { ++ return (op1 != op2); ++} ++ ++inline int32_t BytecodeInterpreter::VMlongGe(jlong op1, jlong op2) { ++ return (op1 >= op2); ++} ++ ++inline int32_t BytecodeInterpreter::VMlongLe(jlong op1, jlong op2) { ++ return (op1 <= op2); ++} ++ ++inline int32_t BytecodeInterpreter::VMlongLt(jlong op1, jlong op2) { ++ return (op1 < op2); ++} ++ ++inline int32_t BytecodeInterpreter::VMlongGt(jlong op1, jlong op2) { ++ return (op1 > op2); ++} ++ ++inline int32_t BytecodeInterpreter::VMlongCompare(jlong op1, jlong op2) { ++ return (VMlongLt(op1, op2) ? -1 : VMlongGt(op1, op2) ? 1 : 0); ++} ++ ++// Long conversions ++ ++inline jdouble BytecodeInterpreter::VMlong2Double(jlong val) { ++ return (jdouble) val; ++} ++ ++inline jfloat BytecodeInterpreter::VMlong2Float(jlong val) { ++ return (jfloat) val; ++} ++ ++inline jint BytecodeInterpreter::VMlong2Int(jlong val) { ++ return (jint) val; ++} ++ ++// Double Arithmetic ++ ++inline jdouble BytecodeInterpreter::VMdoubleAdd(jdouble op1, jdouble op2) { ++ return op1 + op2; ++} ++ ++inline jdouble BytecodeInterpreter::VMdoubleDiv(jdouble op1, jdouble op2) { ++ // Divide by zero... QQQ ++ return op1 / op2; ++} ++ ++inline jdouble BytecodeInterpreter::VMdoubleMul(jdouble op1, jdouble op2) { ++ return op1 * op2; ++} ++ ++inline jdouble BytecodeInterpreter::VMdoubleNeg(jdouble op) { ++ return -op; ++} ++ ++inline jdouble BytecodeInterpreter::VMdoubleRem(jdouble op1, jdouble op2) { ++ return fmod(op1, op2); ++} ++ ++inline jdouble BytecodeInterpreter::VMdoubleSub(jdouble op1, jdouble op2) { ++ return op1 - op2; ++} ++ ++inline int32_t BytecodeInterpreter::VMdoubleCompare(jdouble op1, jdouble op2, int32_t direction) { ++ return ( op1 < op2 ? -1 : ++ op1 > op2 ? 1 : ++ op1 == op2 ? 0 : ++ (direction == -1 || direction == 1) ? direction : 0); ++} ++ ++// Double Conversions ++ ++inline jfloat BytecodeInterpreter::VMdouble2Float(jdouble val) { ++ return (jfloat) val; ++} ++ ++// Float Conversions ++ ++inline jdouble BytecodeInterpreter::VMfloat2Double(jfloat op) { ++ return (jdouble) op; ++} ++ ++// Integer Arithmetic ++ ++inline jint BytecodeInterpreter::VMintAdd(jint op1, jint op2) { ++ return op1 + op2; ++} ++ ++inline jint BytecodeInterpreter::VMintAnd(jint op1, jint op2) { ++ return op1 & op2; ++} ++ ++inline jint BytecodeInterpreter::VMintDiv(jint op1, jint op2) { ++ // it's possible we could catch this special case implicitly ++ if ((juint)op1 == 0x80000000 && op2 == -1) return op1; ++ else return op1 / op2; ++} ++ ++inline jint BytecodeInterpreter::VMintMul(jint op1, jint op2) { ++ return op1 * op2; ++} ++ ++inline jint BytecodeInterpreter::VMintNeg(jint op) { ++ return -op; ++} ++ ++inline jint BytecodeInterpreter::VMintOr(jint op1, jint op2) { ++ return op1 | op2; ++} ++ ++inline jint BytecodeInterpreter::VMintRem(jint op1, jint op2) { ++ // it's possible we could catch this special case implicitly ++ if ((juint)op1 == 0x80000000 && op2 == -1) return 0; ++ else return op1 % op2; ++} ++ ++inline jint BytecodeInterpreter::VMintShl(jint op1, jint op2) { ++ return op1 << op2; ++} ++ ++inline jint BytecodeInterpreter::VMintShr(jint op1, jint op2) { ++ return op1 >> (op2 & 0x1f); // QQ op2 & 0x1f?? ++} ++ ++inline jint BytecodeInterpreter::VMintSub(jint op1, jint op2) { ++ return op1 - op2; ++} ++ ++inline jint BytecodeInterpreter::VMintUshr(jint op1, jint op2) { ++ return ((juint) op1) >> (op2 & 0x1f); // QQ op2 & 0x1f?? ++} ++ ++inline jint BytecodeInterpreter::VMintXor(jint op1, jint op2) { ++ return op1 ^ op2; ++} ++ ++inline jdouble BytecodeInterpreter::VMint2Double(jint val) { ++ return (jdouble) val; ++} ++ ++inline jfloat BytecodeInterpreter::VMint2Float(jint val) { ++ return (jfloat) val; ++} ++ ++inline jlong BytecodeInterpreter::VMint2Long(jint val) { ++ return (jlong) val; ++} ++ ++inline jchar BytecodeInterpreter::VMint2Char(jint val) { ++ return (jchar) val; ++} ++ ++inline jshort BytecodeInterpreter::VMint2Short(jint val) { ++ return (jshort) val; ++} ++ ++inline jbyte BytecodeInterpreter::VMint2Byte(jint val) { ++ return (jbyte) val; ++} ++ ++#endif // CPU_LOONGARCH_VM_BYTECODEINTERPRETER_LOONGARCH_INLINE_HPP +diff --git a/hotspot/src/cpu/loongarch/vm/bytecodes_loongarch.cpp b/hotspot/src/cpu/loongarch/vm/bytecodes_loongarch.cpp +new file mode 100644 +index 0000000000..8641090584 +--- /dev/null ++++ b/hotspot/src/cpu/loongarch/vm/bytecodes_loongarch.cpp +@@ -0,0 +1,38 @@ ++/* ++ * Copyright (c) 1998, 2010, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2015, 2020, Loongson Technology. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ * ++ */ ++ ++#include "precompiled.hpp" ++#include "interpreter/bytecodes.hpp" ++ ++ ++void Bytecodes::pd_initialize() { ++ // No LoongArch specific initialization ++} ++ ++ ++Bytecodes::Code Bytecodes::pd_base_code_for(Code code) { ++ // No LoongArch specific bytecodes ++ return code; ++} +diff --git a/hotspot/src/cpu/loongarch/vm/bytecodes_loongarch.hpp b/hotspot/src/cpu/loongarch/vm/bytecodes_loongarch.hpp +new file mode 100644 +index 0000000000..fbdf531996 +--- /dev/null ++++ b/hotspot/src/cpu/loongarch/vm/bytecodes_loongarch.hpp +@@ -0,0 +1,31 @@ ++/* ++ * Copyright (c) 1998, 2010, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2015, 2020, Loongson Technology. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ * ++ */ ++ ++#ifndef CPU_LOONGARCH_VM_BYTECODES_LOONGARCH_HPP ++#define CPU_LOONGARCH_VM_BYTECODES_LOONGARCH_HPP ++ ++// No Loongson specific bytecodes ++ ++#endif // CPU_LOONGARCH_VM_BYTECODES_LOONGARCH_HPP +diff --git a/hotspot/src/cpu/loongarch/vm/bytes_loongarch.hpp b/hotspot/src/cpu/loongarch/vm/bytes_loongarch.hpp +new file mode 100644 +index 0000000000..8f766a617e +--- /dev/null ++++ b/hotspot/src/cpu/loongarch/vm/bytes_loongarch.hpp +@@ -0,0 +1,75 @@ ++/* ++ * Copyright (c) 1997, 2010, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2015, 2021, Loongson Technology. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ * ++ */ ++ ++#ifndef CPU_LOONGARCH_VM_BYTES_LOONGARCH_HPP ++#define CPU_LOONGARCH_VM_BYTES_LOONGARCH_HPP ++ ++#include "memory/allocation.hpp" ++ ++class Bytes: AllStatic { ++ public: ++ // Returns true if the byte ordering used by Java is different from the native byte ordering ++ // of the underlying machine. For example, this is true for Intel x86, but false for Solaris ++ // on Sparc. ++ // we use LoongArch, so return true ++ static inline bool is_Java_byte_ordering_different(){ return true; } ++ ++ ++ // Efficient reading and writing of unaligned unsigned data in platform-specific byte ordering ++ // (no special code is needed since LoongArch CPUs can access unaligned data) ++ static inline u2 get_native_u2(address p) { return *(u2*)p; } ++ static inline u4 get_native_u4(address p) { return *(u4*)p; } ++ static inline u8 get_native_u8(address p) { return *(u8*)p; } ++ ++ static inline void put_native_u2(address p, u2 x) { *(u2*)p = x; } ++ static inline void put_native_u4(address p, u4 x) { *(u4*)p = x; } ++ static inline void put_native_u8(address p, u8 x) { *(u8*)p = x; } ++ ++ ++ // Efficient reading and writing of unaligned unsigned data in Java ++ // byte ordering (i.e. big-endian ordering). Byte-order reversal is ++ // needed since LoongArch64 CPUs use little-endian format. ++ static inline u2 get_Java_u2(address p) { return swap_u2(get_native_u2(p)); } ++ static inline u4 get_Java_u4(address p) { return swap_u4(get_native_u4(p)); } ++ static inline u8 get_Java_u8(address p) { return swap_u8(get_native_u8(p)); } ++ ++ static inline void put_Java_u2(address p, u2 x) { put_native_u2(p, swap_u2(x)); } ++ static inline void put_Java_u4(address p, u4 x) { put_native_u4(p, swap_u4(x)); } ++ static inline void put_Java_u8(address p, u8 x) { put_native_u8(p, swap_u8(x)); } ++ ++ ++ // Efficient swapping of byte ordering ++ static inline u2 swap_u2(u2 x); // compiler-dependent implementation ++ static inline u4 swap_u4(u4 x); // compiler-dependent implementation ++ static inline u8 swap_u8(u8 x); ++}; ++ ++ ++// The following header contains the implementations of swap_u2, swap_u4, and swap_u8[_base] ++#ifdef TARGET_OS_ARCH_linux_loongarch ++# include "bytes_linux_loongarch.inline.hpp" ++#endif ++ ++#endif // CPU_LOONGARCH_VM_BYTES_LOONGARCH_HPP +diff --git a/hotspot/src/cpu/loongarch/vm/c1_CodeStubs_loongarch_64.cpp b/hotspot/src/cpu/loongarch/vm/c1_CodeStubs_loongarch_64.cpp +new file mode 100644 +index 0000000000..d14a213f38 +--- /dev/null ++++ b/hotspot/src/cpu/loongarch/vm/c1_CodeStubs_loongarch_64.cpp +@@ -0,0 +1,384 @@ ++/* ++ * Copyright (c) 1999, 2021, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2021, Loongson Technology. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ * ++ */ ++ ++#include "precompiled.hpp" ++#include "asm/macroAssembler.inline.hpp" ++#include "c1/c1_CodeStubs.hpp" ++#include "c1/c1_FrameMap.hpp" ++#include "c1/c1_LIRAssembler.hpp" ++#include "c1/c1_MacroAssembler.hpp" ++#include "c1/c1_Runtime1.hpp" ++#include "classfile/javaClasses.hpp" ++#include "nativeInst_loongarch.hpp" ++#include "runtime/sharedRuntime.hpp" ++#include "vmreg_loongarch.inline.hpp" ++ ++#define A0 RA0 ++#define A3 RA3 ++ ++#define __ ce->masm()-> ++ ++void CounterOverflowStub::emit_code(LIR_Assembler* ce) { ++ __ bind(_entry); ++ ce->store_parameter(_method->as_register(), 1); ++ ce->store_parameter(_bci, 0); ++ __ call(Runtime1::entry_for(Runtime1::counter_overflow_id), relocInfo::runtime_call_type); ++ ce->add_call_info_here(_info); ++ ce->verify_oop_map(_info); ++ __ b(_continuation); ++} ++ ++RangeCheckStub::RangeCheckStub(CodeEmitInfo* info, LIR_Opr index, ++ bool throw_index_out_of_bounds_exception) ++ : _throw_index_out_of_bounds_exception(throw_index_out_of_bounds_exception) ++ , _index(index) ++{ ++ assert(info != NULL, "must have info"); ++ _info = new CodeEmitInfo(info); ++} ++ ++void RangeCheckStub::emit_code(LIR_Assembler* ce) { ++ __ bind(_entry); ++ if (_info->deoptimize_on_exception()) { ++ address a = Runtime1::entry_for(Runtime1::predicate_failed_trap_id); ++ __ call(a, relocInfo::runtime_call_type); ++ ce->add_call_info_here(_info); ++ ce->verify_oop_map(_info); ++ debug_only(__ should_not_reach_here()); ++ return; ++ } ++ ++ if (_index->is_cpu_register()) { ++ __ move(SCR1, _index->as_register()); ++ } else { ++ __ li(SCR1, _index->as_jint()); ++ } ++ Runtime1::StubID stub_id; ++ if (_throw_index_out_of_bounds_exception) { ++ stub_id = Runtime1::throw_index_exception_id; ++ } else { ++ stub_id = Runtime1::throw_range_check_failed_id; ++ } ++ __ call(Runtime1::entry_for(stub_id), relocInfo::runtime_call_type); ++ ce->add_call_info_here(_info); ++ ce->verify_oop_map(_info); ++ debug_only(__ should_not_reach_here()); ++} ++ ++PredicateFailedStub::PredicateFailedStub(CodeEmitInfo* info) { ++ _info = new CodeEmitInfo(info); ++} ++ ++void PredicateFailedStub::emit_code(LIR_Assembler* ce) { ++ __ bind(_entry); ++ address a = Runtime1::entry_for(Runtime1::predicate_failed_trap_id); ++ __ call(a, relocInfo::runtime_call_type); ++ ce->add_call_info_here(_info); ++ ce->verify_oop_map(_info); ++ debug_only(__ should_not_reach_here()); ++} ++ ++void DivByZeroStub::emit_code(LIR_Assembler* ce) { ++ if (_offset != -1) { ++ ce->compilation()->implicit_exception_table()->append(_offset, __ offset()); ++ } ++ __ bind(_entry); ++ __ call(Runtime1::entry_for(Runtime1::throw_div0_exception_id), relocInfo::runtime_call_type); ++ ce->add_call_info_here(_info); ++ ce->verify_oop_map(_info); ++#ifdef ASSERT ++ __ should_not_reach_here(); ++#endif ++} ++ ++// Implementation of NewInstanceStub ++ ++NewInstanceStub::NewInstanceStub(LIR_Opr klass_reg, LIR_Opr result, ciInstanceKlass* klass, ++ CodeEmitInfo* info, Runtime1::StubID stub_id) { ++ _result = result; ++ _klass = klass; ++ _klass_reg = klass_reg; ++ _info = new CodeEmitInfo(info); ++ assert(stub_id == Runtime1::new_instance_id || ++ stub_id == Runtime1::fast_new_instance_id || ++ stub_id == Runtime1::fast_new_instance_init_check_id, ++ "need new_instance id"); ++ _stub_id = stub_id; ++} ++ ++void NewInstanceStub::emit_code(LIR_Assembler* ce) { ++ assert(__ rsp_offset() == 0, "frame size should be fixed"); ++ __ bind(_entry); ++ __ move(A3, _klass_reg->as_register()); ++ __ call(Runtime1::entry_for(_stub_id), relocInfo::runtime_call_type); ++ ce->add_call_info_here(_info); ++ ce->verify_oop_map(_info); ++ assert(_result->as_register() == A0, "result must in A0"); ++ __ b(_continuation); ++} ++ ++// Implementation of NewTypeArrayStub ++ ++NewTypeArrayStub::NewTypeArrayStub(LIR_Opr klass_reg, LIR_Opr length, LIR_Opr result, ++ CodeEmitInfo* info) { ++ _klass_reg = klass_reg; ++ _length = length; ++ _result = result; ++ _info = new CodeEmitInfo(info); ++} ++ ++void NewTypeArrayStub::emit_code(LIR_Assembler* ce) { ++ assert(__ rsp_offset() == 0, "frame size should be fixed"); ++ __ bind(_entry); ++ assert(_length->as_register() == S0, "length must in S0,"); ++ assert(_klass_reg->as_register() == A3, "klass_reg must in A3"); ++ __ call(Runtime1::entry_for(Runtime1::new_type_array_id), relocInfo::runtime_call_type); ++ ce->add_call_info_here(_info); ++ ce->verify_oop_map(_info); ++ assert(_result->as_register() == A0, "result must in A0"); ++ __ b(_continuation); ++} ++ ++// Implementation of NewObjectArrayStub ++ ++NewObjectArrayStub::NewObjectArrayStub(LIR_Opr klass_reg, LIR_Opr length, LIR_Opr result, ++ CodeEmitInfo* info) { ++ _klass_reg = klass_reg; ++ _result = result; ++ _length = length; ++ _info = new CodeEmitInfo(info); ++} ++ ++void NewObjectArrayStub::emit_code(LIR_Assembler* ce) { ++ assert(__ rsp_offset() == 0, "frame size should be fixed"); ++ __ bind(_entry); ++ assert(_length->as_register() == S0, "length must in S0,"); ++ assert(_klass_reg->as_register() == A3, "klass_reg must in A3"); ++ __ call(Runtime1::entry_for(Runtime1::new_object_array_id), relocInfo::runtime_call_type); ++ ce->add_call_info_here(_info); ++ ce->verify_oop_map(_info); ++ assert(_result->as_register() == A0, "result must in A0"); ++ __ b(_continuation); ++} ++ ++// Implementation of MonitorAccessStubs ++ ++MonitorEnterStub::MonitorEnterStub(LIR_Opr obj_reg, LIR_Opr lock_reg, CodeEmitInfo* info) ++ : MonitorAccessStub(obj_reg, lock_reg) { ++ _info = new CodeEmitInfo(info); ++} ++ ++void MonitorEnterStub::emit_code(LIR_Assembler* ce) { ++ assert(__ rsp_offset() == 0, "frame size should be fixed"); ++ __ bind(_entry); ++ ce->store_parameter(_obj_reg->as_register(), 1); ++ ce->store_parameter(_lock_reg->as_register(), 0); ++ Runtime1::StubID enter_id; ++ if (ce->compilation()->has_fpu_code()) { ++ enter_id = Runtime1::monitorenter_id; ++ } else { ++ enter_id = Runtime1::monitorenter_nofpu_id; ++ } ++ __ call(Runtime1::entry_for(enter_id), relocInfo::runtime_call_type); ++ ce->add_call_info_here(_info); ++ ce->verify_oop_map(_info); ++ __ b(_continuation); ++} ++ ++void MonitorExitStub::emit_code(LIR_Assembler* ce) { ++ __ bind(_entry); ++ if (_compute_lock) { ++ // lock_reg was destroyed by fast unlocking attempt => recompute it ++ ce->monitor_address(_monitor_ix, _lock_reg); ++ } ++ ce->store_parameter(_lock_reg->as_register(), 0); ++ // note: non-blocking leaf routine => no call info needed ++ Runtime1::StubID exit_id; ++ if (ce->compilation()->has_fpu_code()) { ++ exit_id = Runtime1::monitorexit_id; ++ } else { ++ exit_id = Runtime1::monitorexit_nofpu_id; ++ } ++ __ lipc(RA, _continuation); ++ __ jmp(Runtime1::entry_for(exit_id), relocInfo::runtime_call_type); ++} ++ ++// Implementation of patching: ++// - Copy the code at given offset to an inlined buffer (first the bytes, then the number of bytes) ++// - Replace original code with a call to the stub ++// At Runtime: ++// - call to stub, jump to runtime ++// - in runtime: preserve all registers (rspecially objects, i.e., source and destination object) ++// - in runtime: after initializing class, restore original code, reexecute instruction ++ ++int PatchingStub::_patch_info_offset = -NativeGeneralJump::instruction_size; ++ ++void PatchingStub::align_patch_site(MacroAssembler* masm) { ++} ++ ++void PatchingStub::emit_code(LIR_Assembler* ce) { ++ assert(false, "LoongArch64 should not use C1 runtime patching"); ++} ++ ++void DeoptimizeStub::emit_code(LIR_Assembler* ce) { ++ __ bind(_entry); ++ __ call(Runtime1::entry_for(Runtime1::deoptimize_id), relocInfo::runtime_call_type); ++ ce->add_call_info_here(_info); ++ DEBUG_ONLY(__ should_not_reach_here()); ++} ++ ++void ImplicitNullCheckStub::emit_code(LIR_Assembler* ce) { ++ address a; ++ if (_info->deoptimize_on_exception()) { ++ // Deoptimize, do not throw the exception, because it is probably wrong to do it here. ++ a = Runtime1::entry_for(Runtime1::predicate_failed_trap_id); ++ } else { ++ a = Runtime1::entry_for(Runtime1::throw_null_pointer_exception_id); ++ } ++ ++ ce->compilation()->implicit_exception_table()->append(_offset, __ offset()); ++ __ bind(_entry); ++ __ call(a, relocInfo::runtime_call_type); ++ ce->add_call_info_here(_info); ++ ce->verify_oop_map(_info); ++ debug_only(__ should_not_reach_here()); ++} ++ ++void SimpleExceptionStub::emit_code(LIR_Assembler* ce) { ++ assert(__ rsp_offset() == 0, "frame size should be fixed"); ++ ++ __ bind(_entry); ++ // pass the object in a scratch register because all other registers ++ // must be preserved ++ if (_obj->is_cpu_register()) { ++ __ move(SCR1, _obj->as_register()); ++ } ++ __ call(Runtime1::entry_for(_stub), relocInfo::runtime_call_type); ++ ce->add_call_info_here(_info); ++ debug_only(__ should_not_reach_here()); ++} ++ ++void ArrayCopyStub::emit_code(LIR_Assembler* ce) { ++ //---------------slow case: call to native----------------- ++ __ bind(_entry); ++ // Figure out where the args should go ++ // This should really convert the IntrinsicID to the Method* and signature ++ // but I don't know how to do that. ++ // ++ VMRegPair args[5]; ++ BasicType signature[5] = { T_OBJECT, T_INT, T_OBJECT, T_INT, T_INT}; ++ SharedRuntime::java_calling_convention(signature, args, 5, true); ++ ++ // push parameters ++ // (src, src_pos, dest, destPos, length) ++ Register r[5]; ++ r[0] = src()->as_register(); ++ r[1] = src_pos()->as_register(); ++ r[2] = dst()->as_register(); ++ r[3] = dst_pos()->as_register(); ++ r[4] = length()->as_register(); ++ ++ // next registers will get stored on the stack ++ for (int i = 0; i < 5 ; i++ ) { ++ VMReg r_1 = args[i].first(); ++ if (r_1->is_stack()) { ++ int st_off = r_1->reg2stack() * wordSize; ++ __ stptr_d (r[i], SP, st_off); ++ } else { ++ assert(r[i] == args[i].first()->as_Register(), "Wrong register for arg "); ++ } ++ } ++ ++ ce->align_call(lir_static_call); ++ ++ ce->emit_static_call_stub(); ++ if (ce->compilation()->bailed_out()) { ++ return; // CodeCache is full ++ } ++ AddressLiteral resolve(SharedRuntime::get_resolve_static_call_stub(), ++ relocInfo::static_call_type); ++ address call = __ trampoline_call(resolve); ++ if (call == NULL) { ++ ce->bailout("trampoline stub overflow"); ++ return; ++ } ++ ce->add_call_info_here(info()); ++ ++#ifndef PRODUCT ++ __ li(SCR2, (address)&Runtime1::_arraycopy_slowcase_cnt); ++ __ increment(Address(SCR2)); ++#endif ++ ++ __ b(_continuation); ++} ++ ++///////////////////////////////////////////////////////////////////////////// ++#if INCLUDE_ALL_GCS ++ ++void G1PreBarrierStub::emit_code(LIR_Assembler* ce) { ++ // At this point we know that marking is in progress. ++ // If do_load() is true then we have to emit the ++ // load of the previous value; otherwise it has already ++ // been loaded into _pre_val. ++ ++ __ bind(_entry); ++ assert(pre_val()->is_register(), "Precondition."); ++ ++ Register pre_val_reg = pre_val()->as_register(); ++ ++ if (do_load()) { ++ ce->mem2reg(addr(), pre_val(), T_OBJECT, patch_code(), info(), false /*wide*/, false /*unaligned*/); ++ } ++ __ beqz(pre_val_reg, _continuation); ++ ce->store_parameter(pre_val()->as_register(), 0); ++ __ call(Runtime1::entry_for(Runtime1::g1_pre_barrier_slow_id), relocInfo::runtime_call_type); ++ __ b(_continuation); ++} ++ ++jbyte* G1PostBarrierStub::_byte_map_base = NULL; ++ ++jbyte* G1PostBarrierStub::byte_map_base_slow() { ++ BarrierSet* bs = Universe::heap()->barrier_set(); ++ assert(bs->is_a(BarrierSet::G1SATBCTLogging), ++ "Must be if we're using this."); ++ return ((G1SATBCardTableModRefBS*)bs)->byte_map_base; ++} ++ ++ ++void G1PostBarrierStub::emit_code(LIR_Assembler* ce) { ++ __ bind(_entry); ++ assert(addr()->is_register(), "Precondition."); ++ assert(new_val()->is_register(), "Precondition."); ++ Register new_val_reg = new_val()->as_register(); ++ __ beqz(new_val_reg, _continuation); ++ ce->store_parameter(addr()->as_pointer_register(), 0); ++ __ call(Runtime1::entry_for(Runtime1::g1_post_barrier_slow_id), relocInfo::runtime_call_type); ++ __ b(_continuation); ++} ++ ++#endif // INCLUDE_ALL_GCS ++///////////////////////////////////////////////////////////////////////////// ++ ++#undef __ +diff --git a/hotspot/src/cpu/loongarch/vm/c1_Defs_loongarch.hpp b/hotspot/src/cpu/loongarch/vm/c1_Defs_loongarch.hpp +new file mode 100644 +index 0000000000..1140e44431 +--- /dev/null ++++ b/hotspot/src/cpu/loongarch/vm/c1_Defs_loongarch.hpp +@@ -0,0 +1,79 @@ ++/* ++ * Copyright (c) 2000, 2021, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2021, Loongson Technology. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ * ++ */ ++ ++#ifndef CPU_LOONGARCH_C1_DEFS_LOONGARCH_HPP ++#define CPU_LOONGARCH_C1_DEFS_LOONGARCH_HPP ++ ++// native word offsets from memory address (little endian) ++enum { ++ pd_lo_word_offset_in_bytes = 0, ++ pd_hi_word_offset_in_bytes = BytesPerWord ++}; ++ ++// explicit rounding operations are required to implement the strictFP mode ++enum { ++ pd_strict_fp_requires_explicit_rounding = false ++}; ++ ++// FIXME: There are no callee-saved ++ ++// registers ++enum { ++ pd_nof_cpu_regs_frame_map = RegisterImpl::number_of_registers, // number of registers used during code emission ++ pd_nof_fpu_regs_frame_map = FloatRegisterImpl::number_of_registers, // number of registers used during code emission ++ ++ pd_nof_caller_save_cpu_regs_frame_map = 15, // number of registers killed by calls ++ pd_nof_caller_save_fpu_regs_frame_map = 32, // number of registers killed by calls ++ ++ pd_first_callee_saved_reg = pd_nof_caller_save_cpu_regs_frame_map, ++ pd_last_callee_saved_reg = 21, ++ ++ pd_last_allocatable_cpu_reg = pd_nof_caller_save_cpu_regs_frame_map - 1, ++ ++ pd_nof_cpu_regs_reg_alloc = pd_nof_caller_save_cpu_regs_frame_map, // number of registers that are visible to register allocator ++ pd_nof_fpu_regs_reg_alloc = 32, // number of registers that are visible to register allocator ++ ++ pd_nof_cpu_regs_linearscan = 32, // number of registers visible to linear scan ++ pd_nof_fpu_regs_linearscan = pd_nof_fpu_regs_frame_map, // number of registers visible to linear scan ++ pd_nof_xmm_regs_linearscan = 0, // don't have vector registers ++ pd_first_cpu_reg = 0, ++ pd_last_cpu_reg = pd_nof_cpu_regs_reg_alloc - 1, ++ pd_first_byte_reg = 0, ++ pd_last_byte_reg = pd_nof_cpu_regs_reg_alloc - 1, ++ pd_first_fpu_reg = pd_nof_cpu_regs_frame_map, ++ pd_last_fpu_reg = pd_first_fpu_reg + 31, ++ ++ pd_first_callee_saved_fpu_reg = 24 + pd_first_fpu_reg, ++ pd_last_callee_saved_fpu_reg = 31 + pd_first_fpu_reg, ++}; ++ ++// Encoding of float value in debug info. This is true on x86 where ++// floats are extended to doubles when stored in the stack, false for ++// LoongArch64 where floats and doubles are stored in their native form. ++enum { ++ pd_float_saved_as_double = false ++}; ++ ++#endif // CPU_LOONGARCH_C1_DEFS_LOONGARCH_HPP +diff --git a/hotspot/src/cpu/loongarch/vm/c1_FpuStackSim_loongarch.hpp b/hotspot/src/cpu/loongarch/vm/c1_FpuStackSim_loongarch.hpp +new file mode 100644 +index 0000000000..bd8578c72a +--- /dev/null ++++ b/hotspot/src/cpu/loongarch/vm/c1_FpuStackSim_loongarch.hpp +@@ -0,0 +1,32 @@ ++/* ++ * Copyright (c) 2005, 2019, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2021, 2022, Loongson Technology. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ * ++ */ ++ ++#ifndef CPU_LOONGARCH_C1_FPUSTACKSIM_LOONGARCH_HPP ++#define CPU_LOONGARCH_C1_FPUSTACKSIM_LOONGARCH_HPP ++ ++// No FPU stack on LoongArch ++class FpuStackSim; ++ ++#endif // CPU_LOONGARCH_C1_FPUSTACKSIM_LOONGARCH_HPP +diff --git a/hotspot/src/cpu/loongarch/vm/c1_FpuStackSim_loongarch_64.cpp b/hotspot/src/cpu/loongarch/vm/c1_FpuStackSim_loongarch_64.cpp +new file mode 100644 +index 0000000000..1a89c437a8 +--- /dev/null ++++ b/hotspot/src/cpu/loongarch/vm/c1_FpuStackSim_loongarch_64.cpp +@@ -0,0 +1,31 @@ ++/* ++ * Copyright (c) 2005, 2017, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2021, Loongson Technology. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ * ++ */ ++ ++//-------------------------------------------------------- ++// FpuStackSim ++//-------------------------------------------------------- ++ ++// No FPU stack on LoongArch64 ++#include "precompiled.hpp" +diff --git a/hotspot/src/cpu/loongarch/vm/c1_FrameMap_loongarch.hpp b/hotspot/src/cpu/loongarch/vm/c1_FrameMap_loongarch.hpp +new file mode 100644 +index 0000000000..4f0cf05361 +--- /dev/null ++++ b/hotspot/src/cpu/loongarch/vm/c1_FrameMap_loongarch.hpp +@@ -0,0 +1,143 @@ ++/* ++ * Copyright (c) 1999, 2019, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2021, Loongson Technology. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ * ++ */ ++ ++#ifndef CPU_LOONGARCH_C1_FRAMEMAP_LOONGARCH_HPP ++#define CPU_LOONGARCH_C1_FRAMEMAP_LOONGARCH_HPP ++ ++// On LoongArch64 the frame looks as follows: ++// ++// +-----------------------------+---------+----------------------------------------+----------------+----------- ++// | size_arguments-nof_reg_args | 2 words | size_locals-size_arguments+numreg_args | _size_monitors | spilling . ++// +-----------------------------+---------+----------------------------------------+----------------+----------- ++ ++ public: ++ static const int pd_c_runtime_reserved_arg_size; ++ ++ enum { ++ first_available_sp_in_frame = 0, ++ frame_pad_in_bytes = 16, ++ nof_reg_args = 8 ++ }; ++ ++ public: ++ static LIR_Opr receiver_opr; ++ ++ static LIR_Opr r0_opr; ++ static LIR_Opr ra_opr; ++ static LIR_Opr tp_opr; ++ static LIR_Opr sp_opr; ++ static LIR_Opr a0_opr; ++ static LIR_Opr a1_opr; ++ static LIR_Opr a2_opr; ++ static LIR_Opr a3_opr; ++ static LIR_Opr a4_opr; ++ static LIR_Opr a5_opr; ++ static LIR_Opr a6_opr; ++ static LIR_Opr a7_opr; ++ static LIR_Opr t0_opr; ++ static LIR_Opr t1_opr; ++ static LIR_Opr t2_opr; ++ static LIR_Opr t3_opr; ++ static LIR_Opr t4_opr; ++ static LIR_Opr t5_opr; ++ static LIR_Opr t6_opr; ++ static LIR_Opr t7_opr; ++ static LIR_Opr t8_opr; ++ static LIR_Opr rx_opr; ++ static LIR_Opr fp_opr; ++ static LIR_Opr s0_opr; ++ static LIR_Opr s1_opr; ++ static LIR_Opr s2_opr; ++ static LIR_Opr s3_opr; ++ static LIR_Opr s4_opr; ++ static LIR_Opr s5_opr; ++ static LIR_Opr s6_opr; ++ static LIR_Opr s7_opr; ++ static LIR_Opr s8_opr; ++ ++ static LIR_Opr ra_oop_opr; ++ static LIR_Opr a0_oop_opr; ++ static LIR_Opr a1_oop_opr; ++ static LIR_Opr a2_oop_opr; ++ static LIR_Opr a3_oop_opr; ++ static LIR_Opr a4_oop_opr; ++ static LIR_Opr a5_oop_opr; ++ static LIR_Opr a6_oop_opr; ++ static LIR_Opr a7_oop_opr; ++ static LIR_Opr t0_oop_opr; ++ static LIR_Opr t1_oop_opr; ++ static LIR_Opr t2_oop_opr; ++ static LIR_Opr t3_oop_opr; ++ static LIR_Opr t4_oop_opr; ++ static LIR_Opr t5_oop_opr; ++ static LIR_Opr t6_oop_opr; ++ static LIR_Opr t7_oop_opr; ++ static LIR_Opr t8_oop_opr; ++ static LIR_Opr fp_oop_opr; ++ static LIR_Opr s0_oop_opr; ++ static LIR_Opr s1_oop_opr; ++ static LIR_Opr s2_oop_opr; ++ static LIR_Opr s3_oop_opr; ++ static LIR_Opr s4_oop_opr; ++ static LIR_Opr s5_oop_opr; ++ static LIR_Opr s6_oop_opr; ++ static LIR_Opr s7_oop_opr; ++ static LIR_Opr s8_oop_opr; ++ ++ static LIR_Opr scr1_opr; ++ static LIR_Opr scr2_opr; ++ static LIR_Opr scr1_long_opr; ++ static LIR_Opr scr2_long_opr; ++ ++ static LIR_Opr a0_metadata_opr; ++ static LIR_Opr a1_metadata_opr; ++ static LIR_Opr a2_metadata_opr; ++ static LIR_Opr a3_metadata_opr; ++ static LIR_Opr a4_metadata_opr; ++ static LIR_Opr a5_metadata_opr; ++ ++ static LIR_Opr long0_opr; ++ static LIR_Opr long1_opr; ++ static LIR_Opr fpu0_float_opr; ++ static LIR_Opr fpu0_double_opr; ++ ++ static LIR_Opr as_long_opr(Register r) { ++ return LIR_OprFact::double_cpu(cpu_reg2rnr(r), cpu_reg2rnr(r)); ++ } ++ static LIR_Opr as_pointer_opr(Register r) { ++ return LIR_OprFact::double_cpu(cpu_reg2rnr(r), cpu_reg2rnr(r)); ++ } ++ ++ // VMReg name for spilled physical FPU stack slot n ++ static VMReg fpu_regname (int n); ++ ++ static bool is_caller_save_register(LIR_Opr opr) { return true; } ++ static bool is_caller_save_register(Register r) { return true; } ++ ++ static int nof_caller_save_cpu_regs() { return pd_nof_caller_save_cpu_regs_frame_map; } ++ static int last_cpu_reg() { return pd_last_cpu_reg; } ++ static int last_byte_reg() { return pd_last_byte_reg; } ++ ++#endif // CPU_LOONGARCH_C1_FRAMEMAP_LOONGARCH_HPP +diff --git a/hotspot/src/cpu/loongarch/vm/c1_FrameMap_loongarch_64.cpp b/hotspot/src/cpu/loongarch/vm/c1_FrameMap_loongarch_64.cpp +new file mode 100644 +index 0000000000..25c90bcf98 +--- /dev/null ++++ b/hotspot/src/cpu/loongarch/vm/c1_FrameMap_loongarch_64.cpp +@@ -0,0 +1,362 @@ ++/* ++ * Copyright (c) 1999, 2019, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2021, Loongson Technology. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ * ++ */ ++ ++#include "precompiled.hpp" ++#include "c1/c1_FrameMap.hpp" ++#include "c1/c1_LIR.hpp" ++#include "runtime/sharedRuntime.hpp" ++#include "vmreg_loongarch.inline.hpp" ++ ++#define A0 RA0 ++#define A1 RA1 ++#define A2 RA2 ++#define A3 RA3 ++#define A4 RA4 ++#define A5 RA5 ++#define A6 RA6 ++#define A7 RA7 ++#define T0 RT0 ++#define T1 RT1 ++#define T2 RT2 ++#define T3 RT3 ++#define T4 RT4 ++#define T5 RT5 ++#define T6 RT6 ++#define T7 RT7 ++#define T8 RT8 ++ ++LIR_Opr FrameMap::map_to_opr(BasicType type, VMRegPair* reg, bool) { ++ LIR_Opr opr = LIR_OprFact::illegalOpr; ++ VMReg r_1 = reg->first(); ++ VMReg r_2 = reg->second(); ++ if (r_1->is_stack()) { ++ // Convert stack slot to an SP offset ++ // The calling convention does not count the SharedRuntime::out_preserve_stack_slots() value ++ // so we must add it in here. ++ int st_off = (r_1->reg2stack() + SharedRuntime::out_preserve_stack_slots()) * VMRegImpl::stack_slot_size; ++ opr = LIR_OprFact::address(new LIR_Address(sp_opr, st_off, type)); ++ } else if (r_1->is_Register()) { ++ Register reg = r_1->as_Register(); ++ if (r_2->is_Register() && (type == T_LONG || type == T_DOUBLE)) { ++ Register reg2 = r_2->as_Register(); ++ assert(reg2 == reg, "must be same register"); ++ opr = as_long_opr(reg); ++ } else if (is_reference_type(type)) { ++ opr = as_oop_opr(reg); ++ } else if (type == T_METADATA) { ++ opr = as_metadata_opr(reg); ++ } else if (type == T_ADDRESS) { ++ opr = as_address_opr(reg); ++ } else { ++ opr = as_opr(reg); ++ } ++ } else if (r_1->is_FloatRegister()) { ++ assert(type == T_DOUBLE || type == T_FLOAT, "wrong type"); ++ int num = r_1->as_FloatRegister()->encoding(); ++ if (type == T_FLOAT) { ++ opr = LIR_OprFact::single_fpu(num); ++ } else { ++ opr = LIR_OprFact::double_fpu(num); ++ } ++ } else { ++ ShouldNotReachHere(); ++ } ++ return opr; ++} ++ ++LIR_Opr FrameMap::r0_opr; ++LIR_Opr FrameMap::ra_opr; ++LIR_Opr FrameMap::tp_opr; ++LIR_Opr FrameMap::sp_opr; ++LIR_Opr FrameMap::a0_opr; ++LIR_Opr FrameMap::a1_opr; ++LIR_Opr FrameMap::a2_opr; ++LIR_Opr FrameMap::a3_opr; ++LIR_Opr FrameMap::a4_opr; ++LIR_Opr FrameMap::a5_opr; ++LIR_Opr FrameMap::a6_opr; ++LIR_Opr FrameMap::a7_opr; ++LIR_Opr FrameMap::t0_opr; ++LIR_Opr FrameMap::t1_opr; ++LIR_Opr FrameMap::t2_opr; ++LIR_Opr FrameMap::t3_opr; ++LIR_Opr FrameMap::t4_opr; ++LIR_Opr FrameMap::t5_opr; ++LIR_Opr FrameMap::t6_opr; ++LIR_Opr FrameMap::t7_opr; ++LIR_Opr FrameMap::t8_opr; ++LIR_Opr FrameMap::rx_opr; ++LIR_Opr FrameMap::fp_opr; ++LIR_Opr FrameMap::s0_opr; ++LIR_Opr FrameMap::s1_opr; ++LIR_Opr FrameMap::s2_opr; ++LIR_Opr FrameMap::s3_opr; ++LIR_Opr FrameMap::s4_opr; ++LIR_Opr FrameMap::s5_opr; ++LIR_Opr FrameMap::s6_opr; ++LIR_Opr FrameMap::s7_opr; ++LIR_Opr FrameMap::s8_opr; ++ ++LIR_Opr FrameMap::receiver_opr; ++ ++LIR_Opr FrameMap::ra_oop_opr; ++LIR_Opr FrameMap::a0_oop_opr; ++LIR_Opr FrameMap::a1_oop_opr; ++LIR_Opr FrameMap::a2_oop_opr; ++LIR_Opr FrameMap::a3_oop_opr; ++LIR_Opr FrameMap::a4_oop_opr; ++LIR_Opr FrameMap::a5_oop_opr; ++LIR_Opr FrameMap::a6_oop_opr; ++LIR_Opr FrameMap::a7_oop_opr; ++LIR_Opr FrameMap::t0_oop_opr; ++LIR_Opr FrameMap::t1_oop_opr; ++LIR_Opr FrameMap::t2_oop_opr; ++LIR_Opr FrameMap::t3_oop_opr; ++LIR_Opr FrameMap::t4_oop_opr; ++LIR_Opr FrameMap::t5_oop_opr; ++LIR_Opr FrameMap::t6_oop_opr; ++LIR_Opr FrameMap::t7_oop_opr; ++LIR_Opr FrameMap::t8_oop_opr; ++LIR_Opr FrameMap::fp_oop_opr; ++LIR_Opr FrameMap::s0_oop_opr; ++LIR_Opr FrameMap::s1_oop_opr; ++LIR_Opr FrameMap::s2_oop_opr; ++LIR_Opr FrameMap::s3_oop_opr; ++LIR_Opr FrameMap::s4_oop_opr; ++LIR_Opr FrameMap::s5_oop_opr; ++LIR_Opr FrameMap::s6_oop_opr; ++LIR_Opr FrameMap::s7_oop_opr; ++LIR_Opr FrameMap::s8_oop_opr; ++ ++LIR_Opr FrameMap::scr1_opr; ++LIR_Opr FrameMap::scr2_opr; ++LIR_Opr FrameMap::scr1_long_opr; ++LIR_Opr FrameMap::scr2_long_opr; ++ ++LIR_Opr FrameMap::a0_metadata_opr; ++LIR_Opr FrameMap::a1_metadata_opr; ++LIR_Opr FrameMap::a2_metadata_opr; ++LIR_Opr FrameMap::a3_metadata_opr; ++LIR_Opr FrameMap::a4_metadata_opr; ++LIR_Opr FrameMap::a5_metadata_opr; ++ ++LIR_Opr FrameMap::long0_opr; ++LIR_Opr FrameMap::long1_opr; ++LIR_Opr FrameMap::fpu0_float_opr; ++LIR_Opr FrameMap::fpu0_double_opr; ++ ++LIR_Opr FrameMap::_caller_save_cpu_regs[] = { 0 }; ++LIR_Opr FrameMap::_caller_save_fpu_regs[] = { 0 }; ++ ++//-------------------------------------------------------- ++// FrameMap ++//-------------------------------------------------------- ++ ++void FrameMap::initialize() { ++ assert(!_init_done, "once"); ++ int i = 0; ++ ++ // caller save register ++ map_register(i, A0); a0_opr = LIR_OprFact::single_cpu(i); i++; ++ map_register(i, A1); a1_opr = LIR_OprFact::single_cpu(i); i++; ++ map_register(i, A2); a2_opr = LIR_OprFact::single_cpu(i); i++; ++ map_register(i, A3); a3_opr = LIR_OprFact::single_cpu(i); i++; ++ map_register(i, A4); a4_opr = LIR_OprFact::single_cpu(i); i++; ++ map_register(i, A5); a5_opr = LIR_OprFact::single_cpu(i); i++; ++ map_register(i, A6); a6_opr = LIR_OprFact::single_cpu(i); i++; ++ map_register(i, A7); a7_opr = LIR_OprFact::single_cpu(i); i++; ++ map_register(i, T0); t0_opr = LIR_OprFact::single_cpu(i); i++; ++ map_register(i, T1); t1_opr = LIR_OprFact::single_cpu(i); i++; ++ map_register(i, T2); t2_opr = LIR_OprFact::single_cpu(i); i++; ++ map_register(i, T3); t3_opr = LIR_OprFact::single_cpu(i); i++; ++ map_register(i, T5); t5_opr = LIR_OprFact::single_cpu(i); i++; ++ map_register(i, T6); t6_opr = LIR_OprFact::single_cpu(i); i++; ++ map_register(i, T8); t8_opr = LIR_OprFact::single_cpu(i); i++; ++ ++ // callee save register ++ map_register(i, S0); s0_opr = LIR_OprFact::single_cpu(i); i++; ++ map_register(i, S1); s1_opr = LIR_OprFact::single_cpu(i); i++; ++ map_register(i, S2); s2_opr = LIR_OprFact::single_cpu(i); i++; ++ map_register(i, S3); s3_opr = LIR_OprFact::single_cpu(i); i++; ++ map_register(i, S4); s4_opr = LIR_OprFact::single_cpu(i); i++; ++ map_register(i, S7); s7_opr = LIR_OprFact::single_cpu(i); i++; ++ map_register(i, S8); s8_opr = LIR_OprFact::single_cpu(i); i++; ++ ++ // special register ++ map_register(i, S5); s5_opr = LIR_OprFact::single_cpu(i); i++; // heapbase ++ map_register(i, S6); s6_opr = LIR_OprFact::single_cpu(i); i++; // thread ++ map_register(i, TP); tp_opr = LIR_OprFact::single_cpu(i); i++; // tp ++ map_register(i, FP); fp_opr = LIR_OprFact::single_cpu(i); i++; // fp ++ map_register(i, RA); ra_opr = LIR_OprFact::single_cpu(i); i++; // ra ++ map_register(i, SP); sp_opr = LIR_OprFact::single_cpu(i); i++; // sp ++ ++ // tmp register ++ map_register(i, T7); t7_opr = LIR_OprFact::single_cpu(i); i++; // scr1 ++ map_register(i, T4); t4_opr = LIR_OprFact::single_cpu(i); i++; // scr2 ++ ++ scr1_opr = t7_opr; ++ scr2_opr = t4_opr; ++ scr1_long_opr = LIR_OprFact::double_cpu(t7_opr->cpu_regnr(), t7_opr->cpu_regnr()); ++ scr2_long_opr = LIR_OprFact::double_cpu(t4_opr->cpu_regnr(), t4_opr->cpu_regnr()); ++ ++ long0_opr = LIR_OprFact::double_cpu(a0_opr->cpu_regnr(), a0_opr->cpu_regnr()); ++ long1_opr = LIR_OprFact::double_cpu(a1_opr->cpu_regnr(), a1_opr->cpu_regnr()); ++ ++ fpu0_float_opr = LIR_OprFact::single_fpu(0); ++ fpu0_double_opr = LIR_OprFact::double_fpu(0); ++ ++ // scr1, scr2 not included ++ _caller_save_cpu_regs[0] = a0_opr; ++ _caller_save_cpu_regs[1] = a1_opr; ++ _caller_save_cpu_regs[2] = a2_opr; ++ _caller_save_cpu_regs[3] = a3_opr; ++ _caller_save_cpu_regs[4] = a4_opr; ++ _caller_save_cpu_regs[5] = a5_opr; ++ _caller_save_cpu_regs[6] = a6_opr; ++ _caller_save_cpu_regs[7] = a7_opr; ++ _caller_save_cpu_regs[8] = t0_opr; ++ _caller_save_cpu_regs[9] = t1_opr; ++ _caller_save_cpu_regs[10] = t2_opr; ++ _caller_save_cpu_regs[11] = t3_opr; ++ _caller_save_cpu_regs[12] = t5_opr; ++ _caller_save_cpu_regs[13] = t6_opr; ++ _caller_save_cpu_regs[14] = t8_opr; ++ ++ for (int i = 0; i < 8; i++) { ++ _caller_save_fpu_regs[i] = LIR_OprFact::single_fpu(i); ++ } ++ ++ _init_done = true; ++ ++ ra_oop_opr = as_oop_opr(RA); ++ a0_oop_opr = as_oop_opr(A0); ++ a1_oop_opr = as_oop_opr(A1); ++ a2_oop_opr = as_oop_opr(A2); ++ a3_oop_opr = as_oop_opr(A3); ++ a4_oop_opr = as_oop_opr(A4); ++ a5_oop_opr = as_oop_opr(A5); ++ a6_oop_opr = as_oop_opr(A6); ++ a7_oop_opr = as_oop_opr(A7); ++ t0_oop_opr = as_oop_opr(T0); ++ t1_oop_opr = as_oop_opr(T1); ++ t2_oop_opr = as_oop_opr(T2); ++ t3_oop_opr = as_oop_opr(T3); ++ t4_oop_opr = as_oop_opr(T4); ++ t5_oop_opr = as_oop_opr(T5); ++ t6_oop_opr = as_oop_opr(T6); ++ t7_oop_opr = as_oop_opr(T7); ++ t8_oop_opr = as_oop_opr(T8); ++ fp_oop_opr = as_oop_opr(FP); ++ s0_oop_opr = as_oop_opr(S0); ++ s1_oop_opr = as_oop_opr(S1); ++ s2_oop_opr = as_oop_opr(S2); ++ s3_oop_opr = as_oop_opr(S3); ++ s4_oop_opr = as_oop_opr(S4); ++ s5_oop_opr = as_oop_opr(S5); ++ s6_oop_opr = as_oop_opr(S6); ++ s7_oop_opr = as_oop_opr(S7); ++ s8_oop_opr = as_oop_opr(S8); ++ ++ a0_metadata_opr = as_metadata_opr(A0); ++ a1_metadata_opr = as_metadata_opr(A1); ++ a2_metadata_opr = as_metadata_opr(A2); ++ a3_metadata_opr = as_metadata_opr(A3); ++ a4_metadata_opr = as_metadata_opr(A4); ++ a5_metadata_opr = as_metadata_opr(A5); ++ ++ sp_opr = as_pointer_opr(SP); ++ fp_opr = as_pointer_opr(FP); ++ ++ VMRegPair regs; ++ BasicType sig_bt = T_OBJECT; ++ SharedRuntime::java_calling_convention(&sig_bt, ®s, 1, true); ++ receiver_opr = as_oop_opr(regs.first()->as_Register()); ++ ++ for (int i = 0; i < nof_caller_save_fpu_regs; i++) { ++ _caller_save_fpu_regs[i] = LIR_OprFact::single_fpu(i); ++ } ++} ++ ++Address FrameMap::make_new_address(ByteSize sp_offset) const { ++ // for sp, based address use this: ++ // return Address(sp, in_bytes(sp_offset) - (framesize() - 2) * 4); ++ return Address(SP, in_bytes(sp_offset)); ++} ++ ++// ----------------mapping----------------------- ++// all mapping is based on fp addressing, except for simple leaf methods where we access ++// the locals sp based (and no frame is built) ++ ++// Frame for simple leaf methods (quick entries) ++// ++// +----------+ ++// | ret addr | <- TOS ++// +----------+ ++// | args | ++// | ...... | ++ ++// Frame for standard methods ++// ++// | .........| <- TOS ++// | locals | ++// +----------+ ++// | old fp, | <- RFP ++// +----------+ ++// | ret addr | ++// +----------+ ++// | args | ++// | .........| ++ ++// For OopMaps, map a local variable or spill index to an VMRegImpl name. ++// This is the offset from sp() in the frame of the slot for the index, ++// skewed by VMRegImpl::stack0 to indicate a stack location (vs.a register.) ++// ++// framesize + ++// stack0 stack0 0 <- VMReg ++// | | | ++// ...........|..............|.............| ++// 0 1 2 3 x x 4 5 6 ... | <- local indices ++// ^ ^ sp() ( x x indicate link ++// | | and return addr) ++// arguments non-argument locals ++ ++VMReg FrameMap::fpu_regname(int n) { ++ // Return the OptoReg name for the fpu stack slot "n" ++ // A spilled fpu stack slot comprises to two single-word OptoReg's. ++ return as_FloatRegister(n)->as_VMReg(); ++} ++ ++LIR_Opr FrameMap::stack_pointer() { ++ return FrameMap::sp_opr; ++} ++ ++// JSR 292 ++LIR_Opr FrameMap::method_handle_invoke_SP_save_opr() { ++ return LIR_OprFact::illegalOpr; // Not needed on LoongArch64 ++} ++ ++bool FrameMap::validate_frame() { ++ return true; ++} +diff --git a/hotspot/src/cpu/loongarch/vm/c1_LIRAssembler_loongarch.hpp b/hotspot/src/cpu/loongarch/vm/c1_LIRAssembler_loongarch.hpp +new file mode 100644 +index 0000000000..38b0daa025 +--- /dev/null ++++ b/hotspot/src/cpu/loongarch/vm/c1_LIRAssembler_loongarch.hpp +@@ -0,0 +1,83 @@ ++/* ++ * Copyright (c) 2000, 2021, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2021, Loongson Technology. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ * ++ */ ++ ++#ifndef CPU_LOONGARCH_C1_LIRASSEMBLER_LOONGARCH_HPP ++#define CPU_LOONGARCH_C1_LIRASSEMBLER_LOONGARCH_HPP ++ ++// ArrayCopyStub needs access to bailout ++friend class ArrayCopyStub; ++ ++ private: ++ int array_element_size(BasicType type) const; ++ ++ void arith_fpu_implementation(LIR_Code code, int left_index, int right_index, ++ int dest_index, bool pop_fpu_stack); ++ ++ // helper functions which checks for overflow and sets bailout if it ++ // occurs. Always returns a valid embeddable pointer but in the ++ // bailout case the pointer won't be to unique storage. ++ address float_constant(float f); ++ address double_constant(double d); ++ ++ address int_constant(jlong n); ++ ++ bool is_literal_address(LIR_Address* addr); ++ ++ // Ensure we have a valid Address (base+offset) to a stack-slot. ++ Address stack_slot_address(int index, uint shift, int adjust = 0); ++ ++ // Record the type of the receiver in ReceiverTypeData ++ void type_profile_helper(Register mdo, ciMethodData *md, ciProfileData *data, ++ Register recv, Label* update_done); ++ void add_debug_info_for_branch(address adr, CodeEmitInfo* info); ++ ++ void casw(Register addr, Register newval, Register cmpval, bool sign); ++ void casl(Register addr, Register newval, Register cmpval); ++ ++ void poll_for_safepoint(relocInfo::relocType rtype, CodeEmitInfo* info = NULL); ++ ++ static const int max_tableswitches = 20; ++ struct tableswitch switches[max_tableswitches]; ++ int tableswitch_count; ++ ++ void init() { tableswitch_count = 0; } ++ ++ void deoptimize_trap(CodeEmitInfo *info); ++ ++public: ++ void store_parameter(Register r, int offset_from_sp_in_words); ++ void store_parameter(jint c, int offset_from_sp_in_words); ++ void store_parameter(jobject c, int offset_from_sp_in_words); ++ ++ enum { ++ // call stub: CompiledStaticCall::to_interp_stub_size() + ++ // NativeInstruction::nop_instruction_size + ++ // NativeCallTrampolineStub::instruction_size ++ call_stub_size = 13 * NativeInstruction::nop_instruction_size, ++ exception_handler_size = DEBUG_ONLY(1*K) NOT_DEBUG(175), ++ deopt_handler_size = 7 * NativeInstruction::nop_instruction_size ++ }; ++ ++#endif // CPU_LOONGARCH_C1_LIRASSEMBLER_LOONGARCH_HPP +diff --git a/hotspot/src/cpu/loongarch/vm/c1_LIRAssembler_loongarch_64.cpp b/hotspot/src/cpu/loongarch/vm/c1_LIRAssembler_loongarch_64.cpp +new file mode 100644 +index 0000000000..ee48326bec +--- /dev/null ++++ b/hotspot/src/cpu/loongarch/vm/c1_LIRAssembler_loongarch_64.cpp +@@ -0,0 +1,3377 @@ ++/* ++ * Copyright (c) 2000, 2021, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2021, Loongson Technology. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ * ++ */ ++ ++#include "precompiled.hpp" ++#include "asm/macroAssembler.inline.hpp" ++#include "asm/assembler.hpp" ++#include "c1/c1_CodeStubs.hpp" ++#include "c1/c1_Compilation.hpp" ++#include "c1/c1_LIRAssembler.hpp" ++#include "c1/c1_MacroAssembler.hpp" ++#include "c1/c1_Runtime1.hpp" ++#include "c1/c1_ValueStack.hpp" ++#include "ci/ciArrayKlass.hpp" ++#include "ci/ciInstance.hpp" ++#include "code/compiledIC.hpp" ++#include "nativeInst_loongarch.hpp" ++#include "oops/objArrayKlass.hpp" ++#include "runtime/frame.inline.hpp" ++#include "runtime/sharedRuntime.hpp" ++#include "runtime/stubRoutines.hpp" ++#include "vmreg_loongarch.inline.hpp" ++ ++#define A0 RA0 ++#define A1 RA1 ++#define A2 RA2 ++#define A3 RA3 ++#define A4 RA4 ++#define A5 RA5 ++#define A6 RA6 ++#define A7 RA7 ++#define T0 RT0 ++#define T1 RT1 ++#define T2 RT2 ++#define T3 RT3 ++#define T5 RT5 ++#define T6 RT6 ++#define T7 RT7 ++#define T8 RT8 ++ ++#ifndef PRODUCT ++#define COMMENT(x) do { __ block_comment(x); } while (0) ++#else ++#define COMMENT(x) ++#endif ++ ++NEEDS_CLEANUP // remove this definitions? ++ ++#define __ _masm-> ++ ++static void select_different_registers(Register preserve, Register extra, ++ Register &tmp1, Register &tmp2) { ++ if (tmp1 == preserve) { ++ assert_different_registers(tmp1, tmp2, extra); ++ tmp1 = extra; ++ } else if (tmp2 == preserve) { ++ assert_different_registers(tmp1, tmp2, extra); ++ tmp2 = extra; ++ } ++ assert_different_registers(preserve, tmp1, tmp2); ++} ++ ++static void select_different_registers(Register preserve, Register extra, ++ Register &tmp1, Register &tmp2, ++ Register &tmp3) { ++ if (tmp1 == preserve) { ++ assert_different_registers(tmp1, tmp2, tmp3, extra); ++ tmp1 = extra; ++ } else if (tmp2 == preserve) { ++ assert_different_registers(tmp1, tmp2, tmp3, extra); ++ tmp2 = extra; ++ } else if (tmp3 == preserve) { ++ assert_different_registers(tmp1, tmp2, tmp3, extra); ++ tmp3 = extra; ++ } ++ assert_different_registers(preserve, tmp1, tmp2, tmp3); ++} ++ ++bool LIR_Assembler::is_small_constant(LIR_Opr opr) { Unimplemented(); return false; } ++ ++LIR_Opr LIR_Assembler::receiverOpr() { ++ return FrameMap::receiver_opr; ++} ++ ++LIR_Opr LIR_Assembler::osrBufferPointer() { ++ return FrameMap::as_pointer_opr(receiverOpr()->as_register()); ++} ++ ++//--------------fpu register translations----------------------- ++ ++address LIR_Assembler::float_constant(float f) { ++ address const_addr = __ float_constant(f); ++ if (const_addr == NULL) { ++ bailout("const section overflow"); ++ return __ code()->consts()->start(); ++ } else { ++ return const_addr; ++ } ++} ++ ++address LIR_Assembler::double_constant(double d) { ++ address const_addr = __ double_constant(d); ++ if (const_addr == NULL) { ++ bailout("const section overflow"); ++ return __ code()->consts()->start(); ++ } else { ++ return const_addr; ++ } ++} ++ ++void LIR_Assembler::set_24bit_FPU() { Unimplemented(); } ++ ++void LIR_Assembler::reset_FPU() { Unimplemented(); } ++ ++void LIR_Assembler::fpop() { Unimplemented(); } ++ ++void LIR_Assembler::fxch(int i) { Unimplemented(); } ++ ++void LIR_Assembler::fld(int i) { Unimplemented(); } ++ ++void LIR_Assembler::ffree(int i) { Unimplemented(); } ++ ++void LIR_Assembler::breakpoint() { Unimplemented(); } ++ ++void LIR_Assembler::push(LIR_Opr opr) { Unimplemented(); } ++ ++void LIR_Assembler::pop(LIR_Opr opr) { Unimplemented(); } ++ ++bool LIR_Assembler::is_literal_address(LIR_Address* addr) { Unimplemented(); return false; } ++ ++static Register as_reg(LIR_Opr op) { ++ return op->is_double_cpu() ? op->as_register_lo() : op->as_register(); ++} ++ ++static jlong as_long(LIR_Opr data) { ++ jlong result; ++ switch (data->type()) { ++ case T_INT: ++ result = (data->as_jint()); ++ break; ++ case T_LONG: ++ result = (data->as_jlong()); ++ break; ++ default: ++ ShouldNotReachHere(); ++ result = 0; // unreachable ++ } ++ return result; ++} ++ ++Address LIR_Assembler::as_Address(LIR_Address* addr) { ++ Register base = addr->base()->as_pointer_register(); ++ LIR_Opr opr = addr->index(); ++ if (opr->is_cpu_register()) { ++ Register index; ++ if (opr->is_single_cpu()) ++ index = opr->as_register(); ++ else ++ index = opr->as_register_lo(); ++ assert(addr->disp() == 0, "must be"); ++ return Address(base, index, Address::ScaleFactor(addr->scale())); ++ } else { ++ assert(addr->scale() == 0, "must be"); ++ return Address(base, addr->disp()); ++ } ++ return Address(); ++} ++ ++Address LIR_Assembler::as_Address_hi(LIR_Address* addr) { ++ ShouldNotReachHere(); ++ return Address(); ++} ++ ++Address LIR_Assembler::as_Address_lo(LIR_Address* addr) { ++ return as_Address(addr); // Ouch ++ // FIXME: This needs to be much more clever. See x86. ++} ++ ++// Ensure a valid Address (base + offset) to a stack-slot. If stack access is ++// not encodable as a base + (immediate) offset, generate an explicit address ++// calculation to hold the address in a temporary register. ++Address LIR_Assembler::stack_slot_address(int index, uint size, int adjust) { ++ precond(size == 4 || size == 8); ++ Address addr = frame_map()->address_for_slot(index, adjust); ++ precond(addr.index() == noreg); ++ precond(addr.base() == SP); ++ precond(addr.disp() > 0); ++ uint mask = size - 1; ++ assert((addr.disp() & mask) == 0, "scaled offsets only"); ++ return addr; ++} ++ ++void LIR_Assembler::osr_entry() { ++ offsets()->set_value(CodeOffsets::OSR_Entry, code_offset()); ++ BlockBegin* osr_entry = compilation()->hir()->osr_entry(); ++ ValueStack* entry_state = osr_entry->state(); ++ int number_of_locks = entry_state->locks_size(); ++ ++ // we jump here if osr happens with the interpreter ++ // state set up to continue at the beginning of the ++ // loop that triggered osr - in particular, we have ++ // the following registers setup: ++ // ++ // A2: osr buffer ++ // ++ ++ // build frame ++ ciMethod* m = compilation()->method(); ++ __ build_frame(initial_frame_size_in_bytes(), bang_size_in_bytes()); ++ ++ // OSR buffer is ++ // ++ // locals[nlocals-1..0] ++ // monitors[0..number_of_locks] ++ // ++ // locals is a direct copy of the interpreter frame so in the osr buffer ++ // so first slot in the local array is the last local from the interpreter ++ // and last slot is local[0] (receiver) from the interpreter ++ // ++ // Similarly with locks. The first lock slot in the osr buffer is the nth lock ++ // from the interpreter frame, the nth lock slot in the osr buffer is 0th lock ++ // in the interpreter frame (the method lock if a sync method) ++ ++ // Initialize monitors in the compiled activation. ++ // A2: pointer to osr buffer ++ // ++ // All other registers are dead at this point and the locals will be ++ // copied into place by code emitted in the IR. ++ ++ Register OSR_buf = osrBufferPointer()->as_pointer_register(); ++ { ++ assert(frame::interpreter_frame_monitor_size() == BasicObjectLock::size(), "adjust code below"); ++ int monitor_offset = BytesPerWord * method()->max_locals() + (2 * BytesPerWord) * (number_of_locks - 1); ++ // SharedRuntime::OSR_migration_begin() packs BasicObjectLocks in ++ // the OSR buffer using 2 word entries: first the lock and then ++ // the oop. ++ for (int i = 0; i < number_of_locks; i++) { ++ int slot_offset = monitor_offset - ((i * 2) * BytesPerWord); ++#ifdef ASSERT ++ // verify the interpreter's monitor has a non-null object ++ { ++ Label L; ++ __ ld_ptr(SCR1, Address(OSR_buf, slot_offset + 1 * BytesPerWord)); ++ __ bnez(SCR1, L); ++ __ stop("locked object is NULL"); ++ __ bind(L); ++ } ++#endif ++ __ ld_ptr(S0, Address(OSR_buf, slot_offset + 0)); ++ __ st_ptr(S0, frame_map()->address_for_monitor_lock(i)); ++ __ ld_ptr(S0, Address(OSR_buf, slot_offset + 1*BytesPerWord)); ++ __ st_ptr(S0, frame_map()->address_for_monitor_object(i)); ++ } ++ } ++} ++ ++// inline cache check; done before the frame is built. ++int LIR_Assembler::check_icache() { ++ Register receiver = FrameMap::receiver_opr->as_register(); ++ Register ic_klass = IC_Klass; ++ int start_offset = __ offset(); ++ Label dont; ++ ++ __ verify_oop(receiver); ++ ++ // explicit NULL check not needed since load from [klass_offset] causes a trap ++ // check against inline cache ++ assert(!MacroAssembler::needs_explicit_null_check(oopDesc::klass_offset_in_bytes()), ++ "must add explicit null check"); ++ ++ __ load_klass(SCR2, receiver); ++ __ beq(SCR2, ic_klass, dont); ++ ++ // if icache check fails, then jump to runtime routine ++ // Note: RECEIVER must still contain the receiver! ++ __ jmp(SharedRuntime::get_ic_miss_stub(), relocInfo::runtime_call_type); ++ ++ // We align the verified entry point unless the method body ++ // (including its inline cache check) will fit in a single 64-byte ++ // icache line. ++ if (!method()->is_accessor() || __ offset() - start_offset > 4 * 4) { ++ // force alignment after the cache check. ++ __ align(CodeEntryAlignment); ++ } ++ ++ __ bind(dont); ++ return start_offset; ++} ++ ++void LIR_Assembler::jobject2reg(jobject o, Register reg) { ++ if (o == NULL) { ++ __ move(reg, R0); ++ } else { ++ int oop_index = __ oop_recorder()->find_index(o); ++ RelocationHolder rspec = oop_Relocation::spec(oop_index); ++ __ relocate(rspec); ++ __ patchable_li52(reg, (long)o); ++ } ++} ++ ++void LIR_Assembler::deoptimize_trap(CodeEmitInfo *info) { ++ address target = NULL; ++ ++ switch (patching_id(info)) { ++ case PatchingStub::access_field_id: ++ target = Runtime1::entry_for(Runtime1::access_field_patching_id); ++ break; ++ case PatchingStub::load_klass_id: ++ target = Runtime1::entry_for(Runtime1::load_klass_patching_id); ++ break; ++ case PatchingStub::load_mirror_id: ++ target = Runtime1::entry_for(Runtime1::load_mirror_patching_id); ++ break; ++ case PatchingStub::load_appendix_id: ++ target = Runtime1::entry_for(Runtime1::load_appendix_patching_id); ++ break; ++ default: ShouldNotReachHere(); ++ } ++ ++ __ call(target, relocInfo::runtime_call_type); ++ add_call_info_here(info); ++} ++ ++void LIR_Assembler::jobject2reg_with_patching(Register reg, CodeEmitInfo *info) { ++ deoptimize_trap(info); ++} ++ ++// This specifies the rsp decrement needed to build the frame ++int LIR_Assembler::initial_frame_size_in_bytes() const { ++ // if rounding, must let FrameMap know! ++ return in_bytes(frame_map()->framesize_in_bytes()); ++} ++ ++int LIR_Assembler::emit_exception_handler() { ++ // if the last instruction is a call (typically to do a throw which ++ // is coming at the end after block reordering) the return address ++ // must still point into the code area in order to avoid assertion ++ // failures when searching for the corresponding bci => add a nop ++ // (was bug 5/14/1999 - gri) ++ __ nop(); ++ ++ // generate code for exception handler ++ address handler_base = __ start_a_stub(exception_handler_size); ++ if (handler_base == NULL) { ++ // not enough space left for the handler ++ bailout("exception handler overflow"); ++ return -1; ++ } ++ ++ int offset = code_offset(); ++ ++ // the exception oop and pc are in A0, and A1 ++ // no other registers need to be preserved, so invalidate them ++ __ invalidate_registers(false, true, true, true, true, true); ++ ++ // check that there is really an exception ++ __ verify_not_null_oop(A0); ++ ++ // search an exception handler (A0: exception oop, A1: throwing pc) ++ __ call(Runtime1::entry_for(Runtime1::handle_exception_from_callee_id), relocInfo::runtime_call_type); ++ __ should_not_reach_here(); ++ guarantee(code_offset() - offset <= exception_handler_size, "overflow"); ++ __ end_a_stub(); ++ ++ return offset; ++} ++ ++// Emit the code to remove the frame from the stack in the exception unwind path. ++int LIR_Assembler::emit_unwind_handler() { ++#ifndef PRODUCT ++ if (CommentedAssembly) { ++ _masm->block_comment("Unwind handler"); ++ } ++#endif ++ ++ int offset = code_offset(); ++ ++ // Fetch the exception from TLS and clear out exception related thread state ++ __ ld_ptr(A0, Address(TREG, JavaThread::exception_oop_offset())); ++ __ st_ptr(R0, Address(TREG, JavaThread::exception_oop_offset())); ++ __ st_ptr(R0, Address(TREG, JavaThread::exception_pc_offset())); ++ ++ __ bind(_unwind_handler_entry); ++ __ verify_not_null_oop(V0); ++ if (method()->is_synchronized() || compilation()->env()->dtrace_method_probes()) { ++ __ move(S0, V0); // Preserve the exception ++ } ++ ++ // Perform needed unlocking ++ MonitorExitStub* stub = NULL; ++ if (method()->is_synchronized()) { ++ monitor_address(0, FrameMap::a0_opr); ++ stub = new MonitorExitStub(FrameMap::a0_opr, true, 0); ++ __ unlock_object(A5, A4, A0, *stub->entry()); ++ __ bind(*stub->continuation()); ++ } ++ ++ if (compilation()->env()->dtrace_method_probes()) { ++ __ mov_metadata(A1, method()->constant_encoding()); ++ __ call_VM_leaf(CAST_FROM_FN_PTR(address, SharedRuntime::dtrace_method_exit), TREG, A1); ++ } ++ ++ if (method()->is_synchronized() || compilation()->env()->dtrace_method_probes()) { ++ __ move(A0, S0); // Restore the exception ++ } ++ ++ // remove the activation and dispatch to the unwind handler ++ __ block_comment("remove_frame and dispatch to the unwind handler"); ++ __ remove_frame(initial_frame_size_in_bytes()); ++ __ jmp(Runtime1::entry_for(Runtime1::unwind_exception_id), relocInfo::runtime_call_type); ++ ++ // Emit the slow path assembly ++ if (stub != NULL) { ++ stub->emit_code(this); ++ } ++ ++ return offset; ++} ++ ++int LIR_Assembler::emit_deopt_handler() { ++ // if the last instruction is a call (typically to do a throw which ++ // is coming at the end after block reordering) the return address ++ // must still point into the code area in order to avoid assertion ++ // failures when searching for the corresponding bci => add a nop ++ // (was bug 5/14/1999 - gri) ++ __ nop(); ++ ++ // generate code for exception handler ++ address handler_base = __ start_a_stub(deopt_handler_size); ++ if (handler_base == NULL) { ++ // not enough space left for the handler ++ bailout("deopt handler overflow"); ++ return -1; ++ } ++ ++ int offset = code_offset(); ++ ++ __ call(SharedRuntime::deopt_blob()->unpack(), relocInfo::runtime_call_type); ++ guarantee(code_offset() - offset <= deopt_handler_size, "overflow"); ++ __ end_a_stub(); ++ ++ return offset; ++} ++ ++void LIR_Assembler::add_debug_info_for_branch(address adr, CodeEmitInfo* info) { ++ _masm->code_section()->relocate(adr, relocInfo::poll_type); ++ int pc_offset = code_offset(); ++ flush_debug_info(pc_offset); ++ info->record_debug_info(compilation()->debug_info_recorder(), pc_offset); ++ if (info->exception_handlers() != NULL) { ++ compilation()->add_exception_handlers_for_pco(pc_offset, info->exception_handlers()); ++ } ++} ++ ++void LIR_Assembler::return_op(LIR_Opr result) { ++ assert(result->is_illegal() || !result->is_single_cpu() || result->as_register() == V0, ++ "word returns are in V0,"); ++ ++ // Pop the stack before the safepoint code ++ __ remove_frame(initial_frame_size_in_bytes()); ++ ++ __ li(SCR2, os::get_polling_page()); ++ __ relocate(relocInfo::poll_return_type); ++ __ ld_w(SCR1, SCR2, 0); ++ __ jr(RA); ++} ++ ++int LIR_Assembler::safepoint_poll(LIR_Opr tmp, CodeEmitInfo* info) { ++ guarantee(info != NULL, "Shouldn't be NULL"); ++ __ li(SCR2, os::get_polling_page()); ++ add_debug_info_for_branch(info); // This isn't just debug info: it's the oop map ++ __ relocate(relocInfo::poll_type); ++ __ ld_w(SCR1, SCR2, 0); ++ return __ offset(); ++} ++ ++void LIR_Assembler::move_regs(Register from_reg, Register to_reg) { ++ __ move(to_reg, from_reg); ++} ++ ++void LIR_Assembler::swap_reg(Register a, Register b) { Unimplemented(); } ++ ++void LIR_Assembler::const2reg(LIR_Opr src, LIR_Opr dest, LIR_PatchCode patch_code, CodeEmitInfo* info) { ++ assert(src->is_constant(), "should not call otherwise"); ++ assert(dest->is_register(), "should not call otherwise"); ++ LIR_Const* c = src->as_constant_ptr(); ++ ++ switch (c->type()) { ++ case T_INT: ++ assert(patch_code == lir_patch_none, "no patching handled here"); ++ __ li(dest->as_register(), c->as_jint()); ++ break; ++ case T_ADDRESS: ++ assert(patch_code == lir_patch_none, "no patching handled here"); ++ __ li(dest->as_register(), c->as_jint()); ++ break; ++ case T_LONG: ++ assert(patch_code == lir_patch_none, "no patching handled here"); ++ __ li(dest->as_register_lo(), (intptr_t)c->as_jlong()); ++ break; ++ case T_OBJECT: ++ if (patch_code == lir_patch_none) { ++ jobject2reg(c->as_jobject(), dest->as_register()); ++ } else { ++ jobject2reg_with_patching(dest->as_register(), info); ++ } ++ break; ++ case T_METADATA: ++ if (patch_code != lir_patch_none) { ++ klass2reg_with_patching(dest->as_register(), info); ++ } else { ++ __ mov_metadata(dest->as_register(), c->as_metadata()); ++ } ++ break; ++ case T_FLOAT: ++ __ relocate(relocInfo::internal_word_type); ++ __ patchable_li52(SCR1, (jlong) float_constant(c->as_jfloat())); ++ __ fld_s(dest->as_float_reg(), SCR1, 0); ++ break; ++ case T_DOUBLE: ++ __ relocate(relocInfo::internal_word_type); ++ __ patchable_li52(SCR1, (jlong) double_constant(c->as_jdouble())); ++ __ fld_d(dest->as_double_reg(), SCR1, 0); ++ break; ++ default: ++ ShouldNotReachHere(); ++ } ++} ++ ++void LIR_Assembler::const2stack(LIR_Opr src, LIR_Opr dest) { ++ LIR_Const* c = src->as_constant_ptr(); ++ switch (c->type()) { ++ case T_OBJECT: ++ if (!c->as_jobject()) ++ __ st_ptr(R0, frame_map()->address_for_slot(dest->single_stack_ix())); ++ else { ++ const2reg(src, FrameMap::scr1_opr, lir_patch_none, NULL); ++ reg2stack(FrameMap::scr1_opr, dest, c->type(), false); ++ } ++ break; ++ case T_ADDRESS: ++ const2reg(src, FrameMap::scr1_opr, lir_patch_none, NULL); ++ reg2stack(FrameMap::scr1_opr, dest, c->type(), false); ++ case T_INT: ++ case T_FLOAT: ++ if (c->as_jint_bits() == 0) ++ __ st_w(R0, frame_map()->address_for_slot(dest->single_stack_ix())); ++ else { ++ __ li(SCR2, c->as_jint_bits()); ++ __ st_w(SCR2, frame_map()->address_for_slot(dest->single_stack_ix())); ++ } ++ break; ++ case T_LONG: ++ case T_DOUBLE: ++ if (c->as_jlong_bits() == 0) ++ __ st_ptr(R0, frame_map()->address_for_slot(dest->double_stack_ix(), ++ lo_word_offset_in_bytes)); ++ else { ++ __ li(SCR2, (intptr_t)c->as_jlong_bits()); ++ __ st_ptr(SCR2, frame_map()->address_for_slot(dest->double_stack_ix(), ++ lo_word_offset_in_bytes)); ++ } ++ break; ++ default: ++ ShouldNotReachHere(); ++ } ++} ++ ++void LIR_Assembler::const2mem(LIR_Opr src, LIR_Opr dest, BasicType type, ++ CodeEmitInfo* info, bool wide) { ++ assert(src->is_constant(), "should not call otherwise"); ++ LIR_Const* c = src->as_constant_ptr(); ++ LIR_Address* to_addr = dest->as_address_ptr(); ++ ++ void (Assembler::* insn)(Register Rt, Address adr); ++ ++ switch (type) { ++ case T_ADDRESS: ++ assert(c->as_jint() == 0, "should be"); ++ insn = &Assembler::st_d; ++ break; ++ case T_LONG: ++ assert(c->as_jlong() == 0, "should be"); ++ insn = &Assembler::st_d; ++ break; ++ case T_INT: ++ assert(c->as_jint() == 0, "should be"); ++ insn = &Assembler::st_w; ++ break; ++ case T_OBJECT: ++ case T_ARRAY: ++ assert(c->as_jobject() == 0, "should be"); ++ if (UseCompressedOops && !wide) { ++ insn = &Assembler::st_w; ++ } else { ++ insn = &Assembler::st_d; ++ } ++ break; ++ case T_CHAR: ++ case T_SHORT: ++ assert(c->as_jint() == 0, "should be"); ++ insn = &Assembler::st_h; ++ break; ++ case T_BOOLEAN: ++ case T_BYTE: ++ assert(c->as_jint() == 0, "should be"); ++ insn = &Assembler::st_b; ++ break; ++ default: ++ ShouldNotReachHere(); ++ insn = &Assembler::st_d; // unreachable ++ } ++ ++ if (info) add_debug_info_for_null_check_here(info); ++ (_masm->*insn)(R0, as_Address(to_addr)); ++} ++ ++void LIR_Assembler::reg2reg(LIR_Opr src, LIR_Opr dest) { ++ assert(src->is_register(), "should not call otherwise"); ++ assert(dest->is_register(), "should not call otherwise"); ++ ++ // move between cpu-registers ++ if (dest->is_single_cpu()) { ++ if (src->type() == T_LONG) { ++ // Can do LONG -> OBJECT ++ move_regs(src->as_register_lo(), dest->as_register()); ++ return; ++ } ++ assert(src->is_single_cpu(), "must match"); ++ if (src->type() == T_OBJECT) { ++ __ verify_oop(src->as_register()); ++ } ++ move_regs(src->as_register(), dest->as_register()); ++ } else if (dest->is_double_cpu()) { ++ if (is_reference_type(src->type())) { ++ // Surprising to me but we can see move of a long to t_object ++ __ verify_oop(src->as_register()); ++ move_regs(src->as_register(), dest->as_register_lo()); ++ return; ++ } ++ assert(src->is_double_cpu(), "must match"); ++ Register f_lo = src->as_register_lo(); ++ Register f_hi = src->as_register_hi(); ++ Register t_lo = dest->as_register_lo(); ++ Register t_hi = dest->as_register_hi(); ++ assert(f_hi == f_lo, "must be same"); ++ assert(t_hi == t_lo, "must be same"); ++ move_regs(f_lo, t_lo); ++ } else if (dest->is_single_fpu()) { ++ __ fmov_s(dest->as_float_reg(), src->as_float_reg()); ++ } else if (dest->is_double_fpu()) { ++ __ fmov_d(dest->as_double_reg(), src->as_double_reg()); ++ } else { ++ ShouldNotReachHere(); ++ } ++} ++ ++void LIR_Assembler::reg2stack(LIR_Opr src, LIR_Opr dest, BasicType type, bool pop_fpu_stack) { ++ precond(src->is_register() && dest->is_stack()); ++ ++ uint const c_sz32 = sizeof(uint32_t); ++ uint const c_sz64 = sizeof(uint64_t); ++ ++ if (src->is_single_cpu()) { ++ int index = dest->single_stack_ix(); ++ if (is_reference_type(type)) { ++ __ st_ptr(src->as_register(), stack_slot_address(index, c_sz64)); ++ __ verify_oop(src->as_register()); ++ } else if (type == T_METADATA || type == T_DOUBLE || type == T_ADDRESS) { ++ __ st_ptr(src->as_register(), stack_slot_address(index, c_sz64)); ++ } else { ++ __ st_w(src->as_register(), stack_slot_address(index, c_sz32)); ++ } ++ } else if (src->is_double_cpu()) { ++ int index = dest->double_stack_ix(); ++ Address dest_addr_LO = stack_slot_address(index, c_sz64, lo_word_offset_in_bytes); ++ __ st_ptr(src->as_register_lo(), dest_addr_LO); ++ } else if (src->is_single_fpu()) { ++ int index = dest->single_stack_ix(); ++ __ fst_s(src->as_float_reg(), stack_slot_address(index, c_sz32)); ++ } else if (src->is_double_fpu()) { ++ int index = dest->double_stack_ix(); ++ __ fst_d(src->as_double_reg(), stack_slot_address(index, c_sz64)); ++ } else { ++ ShouldNotReachHere(); ++ } ++} ++ ++void LIR_Assembler::reg2mem(LIR_Opr src, LIR_Opr dest, BasicType type, LIR_PatchCode patch_code, ++ CodeEmitInfo* info, bool pop_fpu_stack, bool wide, bool /* unaligned */) { ++ LIR_Address* to_addr = dest->as_address_ptr(); ++ PatchingStub* patch = NULL; ++ Register compressed_src = SCR2; ++ ++ if (patch_code != lir_patch_none) { ++ deoptimize_trap(info); ++ return; ++ } ++ ++ if (is_reference_type(type)) { ++ __ verify_oop(src->as_register()); ++ ++ if (UseCompressedOops && !wide) { ++ __ encode_heap_oop(compressed_src, src->as_register()); ++ } else { ++ compressed_src = src->as_register(); ++ } ++ } ++ ++ int null_check_here = code_offset(); ++ switch (type) { ++ case T_FLOAT: ++ __ fst_s(src->as_float_reg(), as_Address(to_addr)); ++ break; ++ case T_DOUBLE: ++ __ fst_d(src->as_double_reg(), as_Address(to_addr)); ++ break; ++ case T_ARRAY: // fall through ++ case T_OBJECT: // fall through ++ if (UseCompressedOops && !wide) { ++ __ st_w(compressed_src, as_Address(to_addr)); ++ } else { ++ __ st_ptr(compressed_src, as_Address(to_addr)); ++ } ++ break; ++ case T_METADATA: ++ // We get here to store a method pointer to the stack to pass to ++ // a dtrace runtime call. This can't work on 64 bit with ++ // compressed klass ptrs: T_METADATA can be a compressed klass ++ // ptr or a 64 bit method pointer. ++ ShouldNotReachHere(); ++ __ st_ptr(src->as_register(), as_Address(to_addr)); ++ break; ++ case T_ADDRESS: ++ __ st_ptr(src->as_register(), as_Address(to_addr)); ++ break; ++ case T_INT: ++ __ st_w(src->as_register(), as_Address(to_addr)); ++ break; ++ case T_LONG: ++ __ st_ptr(src->as_register_lo(), as_Address_lo(to_addr)); ++ break; ++ case T_BYTE: // fall through ++ case T_BOOLEAN: ++ __ st_b(src->as_register(), as_Address(to_addr)); ++ break; ++ case T_CHAR: // fall through ++ case T_SHORT: ++ __ st_h(src->as_register(), as_Address(to_addr)); ++ break; ++ default: ++ ShouldNotReachHere(); ++ } ++ if (info != NULL) { ++ add_debug_info_for_null_check(null_check_here, info); ++ } ++} ++ ++void LIR_Assembler::stack2reg(LIR_Opr src, LIR_Opr dest, BasicType type) { ++ precond(src->is_stack() && dest->is_register()); ++ ++ uint const c_sz32 = sizeof(uint32_t); ++ uint const c_sz64 = sizeof(uint64_t); ++ ++ if (dest->is_single_cpu()) { ++ int index = src->single_stack_ix(); ++ if (is_reference_type(type)) { ++ __ ld_ptr(dest->as_register(), stack_slot_address(index, c_sz64)); ++ __ verify_oop(dest->as_register()); ++ } else if (type == T_METADATA || type == T_ADDRESS) { ++ __ ld_ptr(dest->as_register(), stack_slot_address(index, c_sz64)); ++ } else { ++ __ ld_w(dest->as_register(), stack_slot_address(index, c_sz32)); ++ } ++ } else if (dest->is_double_cpu()) { ++ int index = src->double_stack_ix(); ++ Address src_addr_LO = stack_slot_address(index, c_sz64, lo_word_offset_in_bytes); ++ __ ld_ptr(dest->as_register_lo(), src_addr_LO); ++ } else if (dest->is_single_fpu()) { ++ int index = src->single_stack_ix(); ++ __ fld_s(dest->as_float_reg(), stack_slot_address(index, c_sz32)); ++ } else if (dest->is_double_fpu()) { ++ int index = src->double_stack_ix(); ++ __ fld_d(dest->as_double_reg(), stack_slot_address(index, c_sz64)); ++ } else { ++ ShouldNotReachHere(); ++ } ++} ++ ++void LIR_Assembler::klass2reg_with_patching(Register reg, CodeEmitInfo* info) { ++ address target = NULL; ++ ++ switch (patching_id(info)) { ++ case PatchingStub::access_field_id: ++ target = Runtime1::entry_for(Runtime1::access_field_patching_id); ++ break; ++ case PatchingStub::load_klass_id: ++ target = Runtime1::entry_for(Runtime1::load_klass_patching_id); ++ break; ++ case PatchingStub::load_mirror_id: ++ target = Runtime1::entry_for(Runtime1::load_mirror_patching_id); ++ break; ++ case PatchingStub::load_appendix_id: ++ target = Runtime1::entry_for(Runtime1::load_appendix_patching_id); ++ break; ++ default: ShouldNotReachHere(); ++ } ++ ++ __ call(target, relocInfo::runtime_call_type); ++ add_call_info_here(info); ++} ++ ++void LIR_Assembler::stack2stack(LIR_Opr src, LIR_Opr dest, BasicType type) { ++ LIR_Opr temp; ++ ++ if (type == T_LONG || type == T_DOUBLE) ++ temp = FrameMap::scr1_long_opr; ++ else ++ temp = FrameMap::scr1_opr; ++ ++ stack2reg(src, temp, src->type()); ++ reg2stack(temp, dest, dest->type(), false); ++} ++ ++void LIR_Assembler::mem2reg(LIR_Opr src, LIR_Opr dest, BasicType type, LIR_PatchCode patch_code, CodeEmitInfo* info, bool wide, bool /* unaligned */) { ++ LIR_Address* addr = src->as_address_ptr(); ++ LIR_Address* from_addr = src->as_address_ptr(); ++ ++ if (addr->base()->type() == T_OBJECT) { ++ __ verify_oop(addr->base()->as_pointer_register()); ++ } ++ ++ if (patch_code != lir_patch_none) { ++ deoptimize_trap(info); ++ return; ++ } ++ ++ if (info != NULL) { ++ add_debug_info_for_null_check_here(info); ++ } ++ int null_check_here = code_offset(); ++ switch (type) { ++ case T_FLOAT: ++ __ fld_s(dest->as_float_reg(), as_Address(from_addr)); ++ break; ++ case T_DOUBLE: ++ __ fld_d(dest->as_double_reg(), as_Address(from_addr)); ++ break; ++ case T_ARRAY: // fall through ++ case T_OBJECT: // fall through ++ if (UseCompressedOops && !wide) { ++ __ ld_wu(dest->as_register(), as_Address(from_addr)); ++ } else { ++ __ ld_ptr(dest->as_register(), as_Address(from_addr)); ++ } ++ break; ++ case T_METADATA: ++ // We get here to store a method pointer to the stack to pass to ++ // a dtrace runtime call. This can't work on 64 bit with ++ // compressed klass ptrs: T_METADATA can be a compressed klass ++ // ptr or a 64 bit method pointer. ++ ShouldNotReachHere(); ++ __ ld_ptr(dest->as_register(), as_Address(from_addr)); ++ break; ++ case T_ADDRESS: ++ // FIXME: OMG this is a horrible kludge. Any offset from an ++ // address that matches klass_offset_in_bytes() will be loaded ++ // as a word, not a long. ++ if (UseCompressedClassPointers && addr->disp() == oopDesc::klass_offset_in_bytes()) { ++ __ ld_wu(dest->as_register(), as_Address(from_addr)); ++ } else { ++ __ ld_ptr(dest->as_register(), as_Address(from_addr)); ++ } ++ break; ++ case T_INT: ++ __ ld_w(dest->as_register(), as_Address(from_addr)); ++ break; ++ case T_LONG: ++ __ ld_ptr(dest->as_register_lo(), as_Address_lo(from_addr)); ++ break; ++ case T_BYTE: ++ __ ld_b(dest->as_register(), as_Address(from_addr)); ++ break; ++ case T_BOOLEAN: ++ __ ld_bu(dest->as_register(), as_Address(from_addr)); ++ break; ++ case T_CHAR: ++ __ ld_hu(dest->as_register(), as_Address(from_addr)); ++ break; ++ case T_SHORT: ++ __ ld_h(dest->as_register(), as_Address(from_addr)); ++ break; ++ default: ++ ShouldNotReachHere(); ++ } ++ ++ if (is_reference_type(type)) { ++ if (UseCompressedOops && !wide) { ++ __ decode_heap_oop(dest->as_register()); ++ } ++ ++ // Load barrier has not yet been applied, so ZGC can't verify the oop here ++ __ verify_oop(dest->as_register()); ++ } else if (type == T_ADDRESS && addr->disp() == oopDesc::klass_offset_in_bytes()) { ++ if (UseCompressedClassPointers) { ++ __ decode_klass_not_null(dest->as_register()); ++ } ++ } ++} ++ ++void LIR_Assembler::prefetchr(LIR_Opr src) { Unimplemented(); } ++ ++void LIR_Assembler::prefetchw(LIR_Opr src) { Unimplemented(); } ++ ++int LIR_Assembler::array_element_size(BasicType type) const { ++ int elem_size = type2aelembytes(type); ++ return exact_log2(elem_size); ++} ++ ++void LIR_Assembler::emit_op3(LIR_Op3* op) { ++ switch (op->code()) { ++ case lir_idiv: ++ case lir_irem: ++ arithmetic_idiv(op->code(), op->in_opr1(), op->in_opr2(), op->in_opr3(), ++ op->result_opr(), op->info()); ++ break; ++ default: ++ ShouldNotReachHere(); ++ break; ++ } ++} ++ ++void LIR_Assembler::emit_opBranch(LIR_OpBranch* op) { ++#ifdef ASSERT ++ assert(op->block() == NULL || op->block()->label() == op->label(), "wrong label"); ++ if (op->block() != NULL) _branch_target_blocks.append(op->block()); ++ assert(op->cond() == lir_cond_always, "must be"); ++#endif ++ ++ if (op->info() != NULL) ++ add_debug_info_for_branch(op->info()); ++ ++ __ b_far(*(op->label())); ++} ++ ++void LIR_Assembler::emit_opCmpBranch(LIR_OpCmpBranch* op) { ++#ifdef ASSERT ++ assert(op->block() == NULL || op->block()->label() == op->label(), "wrong label"); ++ if (op->block() != NULL) _branch_target_blocks.append(op->block()); ++ if (op->ublock() != NULL) _branch_target_blocks.append(op->ublock()); ++#endif ++ ++ if (op->info() != NULL) { ++ assert(op->in_opr1()->is_address() || op->in_opr2()->is_address(), ++ "shouldn't be codeemitinfo for non-address operands"); ++ add_debug_info_for_null_check_here(op->info()); // exception possible ++ } ++ ++ Label& L = *(op->label()); ++ Assembler::Condition acond; ++ LIR_Opr opr1 = op->in_opr1(); ++ LIR_Opr opr2 = op->in_opr2(); ++ assert(op->condition() != lir_cond_always, "must be"); ++ ++ if (op->code() == lir_cmp_float_branch) { ++ bool is_unordered = (op->ublock() == op->block()); ++ if (opr1->is_single_fpu()) { ++ FloatRegister reg1 = opr1->as_float_reg(); ++ assert(opr2->is_single_fpu(), "expect single float register"); ++ FloatRegister reg2 = opr2->as_float_reg(); ++ switch(op->condition()) { ++ case lir_cond_equal: ++ if (is_unordered) ++ __ fcmp_cueq_s(FCC0, reg1, reg2); ++ else ++ __ fcmp_ceq_s(FCC0, reg1, reg2); ++ break; ++ case lir_cond_notEqual: ++ if (is_unordered) ++ __ fcmp_cune_s(FCC0, reg1, reg2); ++ else ++ __ fcmp_cne_s(FCC0, reg1, reg2); ++ break; ++ case lir_cond_less: ++ if (is_unordered) ++ __ fcmp_cult_s(FCC0, reg1, reg2); ++ else ++ __ fcmp_clt_s(FCC0, reg1, reg2); ++ break; ++ case lir_cond_lessEqual: ++ if (is_unordered) ++ __ fcmp_cule_s(FCC0, reg1, reg2); ++ else ++ __ fcmp_cle_s(FCC0, reg1, reg2); ++ break; ++ case lir_cond_greaterEqual: ++ if (is_unordered) ++ __ fcmp_cule_s(FCC0, reg2, reg1); ++ else ++ __ fcmp_cle_s(FCC0, reg2, reg1); ++ break; ++ case lir_cond_greater: ++ if (is_unordered) ++ __ fcmp_cult_s(FCC0, reg2, reg1); ++ else ++ __ fcmp_clt_s(FCC0, reg2, reg1); ++ break; ++ default: ++ ShouldNotReachHere(); ++ } ++ } else if (opr1->is_double_fpu()) { ++ FloatRegister reg1 = opr1->as_double_reg(); ++ assert(opr2->is_double_fpu(), "expect double float register"); ++ FloatRegister reg2 = opr2->as_double_reg(); ++ switch(op->condition()) { ++ case lir_cond_equal: ++ if (is_unordered) ++ __ fcmp_cueq_d(FCC0, reg1, reg2); ++ else ++ __ fcmp_ceq_d(FCC0, reg1, reg2); ++ break; ++ case lir_cond_notEqual: ++ if (is_unordered) ++ __ fcmp_cune_d(FCC0, reg1, reg2); ++ else ++ __ fcmp_cne_d(FCC0, reg1, reg2); ++ break; ++ case lir_cond_less: ++ if (is_unordered) ++ __ fcmp_cult_d(FCC0, reg1, reg2); ++ else ++ __ fcmp_clt_d(FCC0, reg1, reg2); ++ break; ++ case lir_cond_lessEqual: ++ if (is_unordered) ++ __ fcmp_cule_d(FCC0, reg1, reg2); ++ else ++ __ fcmp_cle_d(FCC0, reg1, reg2); ++ break; ++ case lir_cond_greaterEqual: ++ if (is_unordered) ++ __ fcmp_cule_d(FCC0, reg2, reg1); ++ else ++ __ fcmp_cle_d(FCC0, reg2, reg1); ++ break; ++ case lir_cond_greater: ++ if (is_unordered) ++ __ fcmp_cult_d(FCC0, reg2, reg1); ++ else ++ __ fcmp_clt_d(FCC0, reg2, reg1); ++ break; ++ default: ++ ShouldNotReachHere(); ++ } ++ } else { ++ ShouldNotReachHere(); ++ } ++ __ bcnez(FCC0, L); ++ } else { ++ if (opr1->is_constant() && opr2->is_single_cpu()) { ++ // tableswitch ++ Unimplemented(); ++ } else if (opr1->is_single_cpu() || opr1->is_double_cpu()) { ++ Register reg1 = as_reg(opr1); ++ Register reg2 = noreg; ++ jlong imm2 = 0; ++ if (opr2->is_single_cpu()) { ++ // cpu register - cpu register ++ reg2 = opr2->as_register(); ++ } else if (opr2->is_double_cpu()) { ++ // cpu register - cpu register ++ reg2 = opr2->as_register_lo(); ++ } else if (opr2->is_constant()) { ++ switch(opr2->type()) { ++ case T_INT: ++ case T_ADDRESS: ++ imm2 = opr2->as_constant_ptr()->as_jint(); ++ break; ++ case T_LONG: ++ imm2 = opr2->as_constant_ptr()->as_jlong(); ++ break; ++ case T_METADATA: ++ imm2 = (intptr_t)opr2->as_constant_ptr()->as_metadata(); ++ break; ++ case T_OBJECT: ++ case T_ARRAY: ++ if (opr2->as_constant_ptr()->as_jobject() != NULL) { ++ reg2 = SCR1; ++ jobject2reg(opr2->as_constant_ptr()->as_jobject(), reg2); ++ } else { ++ reg2 = R0; ++ } ++ break; ++ default: ++ ShouldNotReachHere(); ++ break; ++ } ++ } else { ++ ShouldNotReachHere(); ++ } ++ if (reg2 == noreg) { ++ if (imm2 == 0) { ++ reg2 = R0; ++ } else { ++ reg2 = SCR1; ++ __ li(reg2, imm2); ++ } ++ } ++ switch (op->condition()) { ++ case lir_cond_equal: ++ __ beq_far(reg1, reg2, L); break; ++ case lir_cond_notEqual: ++ __ bne_far(reg1, reg2, L); break; ++ case lir_cond_less: ++ __ blt_far(reg1, reg2, L, true); break; ++ case lir_cond_lessEqual: ++ __ bge_far(reg2, reg1, L, true); break; ++ case lir_cond_greaterEqual: ++ __ bge_far(reg1, reg2, L, true); break; ++ case lir_cond_greater: ++ __ blt_far(reg2, reg1, L, true); break; ++ case lir_cond_belowEqual: ++ __ bge_far(reg2, reg1, L, false); break; ++ case lir_cond_aboveEqual: ++ __ bge_far(reg1, reg2, L, false); break; ++ default: ++ ShouldNotReachHere(); ++ } ++ } ++ } ++} ++ ++void LIR_Assembler::emit_opConvert(LIR_OpConvert* op) { ++ LIR_Opr src = op->in_opr(); ++ LIR_Opr dest = op->result_opr(); ++ LIR_Opr tmp = op->tmp(); ++ ++ switch (op->bytecode()) { ++ case Bytecodes::_i2f: ++ __ movgr2fr_w(dest->as_float_reg(), src->as_register()); ++ __ ffint_s_w(dest->as_float_reg(), dest->as_float_reg()); ++ break; ++ case Bytecodes::_i2d: ++ __ movgr2fr_w(dest->as_double_reg(), src->as_register()); ++ __ ffint_d_w(dest->as_double_reg(), dest->as_double_reg()); ++ break; ++ case Bytecodes::_l2d: ++ __ movgr2fr_d(dest->as_double_reg(), src->as_register_lo()); ++ __ ffint_d_l(dest->as_double_reg(), dest->as_double_reg()); ++ break; ++ case Bytecodes::_l2f: ++ __ movgr2fr_d(dest->as_float_reg(), src->as_register_lo()); ++ __ ffint_s_l(dest->as_float_reg(), dest->as_float_reg()); ++ break; ++ case Bytecodes::_f2d: ++ __ fcvt_d_s(dest->as_double_reg(), src->as_float_reg()); ++ break; ++ case Bytecodes::_d2f: ++ __ fcvt_s_d(dest->as_float_reg(), src->as_double_reg()); ++ break; ++ case Bytecodes::_i2c: ++ __ bstrpick_w(dest->as_register(), src->as_register(), 15, 0); ++ break; ++ case Bytecodes::_i2l: ++ _masm->block_comment("FIXME: This could be a no-op"); ++ __ slli_w(dest->as_register_lo(), src->as_register(), 0); ++ break; ++ case Bytecodes::_i2s: ++ __ ext_w_h(dest->as_register(), src->as_register()); ++ break; ++ case Bytecodes::_i2b: ++ __ ext_w_b(dest->as_register(), src->as_register()); ++ break; ++ case Bytecodes::_l2i: ++ __ slli_w(dest->as_register(), src->as_register_lo(), 0); ++ break; ++ case Bytecodes::_d2l: ++ __ ftintrz_l_d(tmp->as_double_reg(), src->as_double_reg()); ++ __ movfr2gr_d(dest->as_register_lo(), tmp->as_double_reg()); ++ break; ++ case Bytecodes::_f2i: ++ __ ftintrz_w_s(tmp->as_float_reg(), src->as_float_reg()); ++ __ movfr2gr_s(dest->as_register(), tmp->as_float_reg()); ++ break; ++ case Bytecodes::_f2l: ++ __ ftintrz_l_s(tmp->as_float_reg(), src->as_float_reg()); ++ __ movfr2gr_d(dest->as_register_lo(), tmp->as_float_reg()); ++ break; ++ case Bytecodes::_d2i: ++ __ ftintrz_w_d(tmp->as_double_reg(), src->as_double_reg()); ++ __ movfr2gr_s(dest->as_register(), tmp->as_double_reg()); ++ break; ++ default: ShouldNotReachHere(); ++ } ++} ++ ++void LIR_Assembler::emit_alloc_obj(LIR_OpAllocObj* op) { ++ if (op->init_check()) { ++ __ ld_bu(SCR1, Address(op->klass()->as_register(), InstanceKlass::init_state_offset())); ++ __ li(SCR2, InstanceKlass::fully_initialized); ++ add_debug_info_for_null_check_here(op->stub()->info()); ++ __ bne_far(SCR1, SCR2, *op->stub()->entry()); ++ } ++ __ allocate_object(op->obj()->as_register(), op->tmp1()->as_register(), ++ op->tmp2()->as_register(), op->header_size(), ++ op->object_size(), op->klass()->as_register(), ++ *op->stub()->entry()); ++ __ bind(*op->stub()->continuation()); ++} ++ ++void LIR_Assembler::emit_alloc_array(LIR_OpAllocArray* op) { ++ Register len = op->len()->as_register(); ++ if (UseSlowPath || ++ (!UseFastNewObjectArray && is_reference_type(op->type())) || ++ (!UseFastNewTypeArray && !is_reference_type(op->type()))) { ++ __ b(*op->stub()->entry()); ++ } else { ++ Register tmp1 = op->tmp1()->as_register(); ++ Register tmp2 = op->tmp2()->as_register(); ++ Register tmp3 = op->tmp3()->as_register(); ++ if (len == tmp1) { ++ tmp1 = tmp3; ++ } else if (len == tmp2) { ++ tmp2 = tmp3; ++ } else if (len == tmp3) { ++ // everything is ok ++ } else { ++ __ move(tmp3, len); ++ } ++ __ allocate_array(op->obj()->as_register(), len, tmp1, tmp2, ++ arrayOopDesc::header_size(op->type()), ++ array_element_size(op->type()), ++ op->klass()->as_register(), ++ *op->stub()->entry()); ++ } ++ __ bind(*op->stub()->continuation()); ++} ++ ++void LIR_Assembler::type_profile_helper(Register mdo, ciMethodData *md, ciProfileData *data, ++ Register recv, Label* update_done) { ++ for (uint i = 0; i < ReceiverTypeData::row_limit(); i++) { ++ Label next_test; ++ // See if the receiver is receiver[n]. ++ __ lea(SCR2, Address(mdo, md->byte_offset_of_slot(data, ReceiverTypeData::receiver_offset(i)))); ++ __ ld_ptr(SCR1, Address(SCR2)); ++ __ bne(recv, SCR1, next_test); ++ Address data_addr(mdo, md->byte_offset_of_slot(data, ReceiverTypeData::receiver_count_offset(i))); ++ __ ld_ptr(SCR2, data_addr); ++ __ addi_d(SCR2, SCR2, DataLayout::counter_increment); ++ __ st_ptr(SCR2, data_addr); ++ __ b(*update_done); ++ __ bind(next_test); ++ } ++ ++ // Didn't find receiver; find next empty slot and fill it in ++ for (uint i = 0; i < ReceiverTypeData::row_limit(); i++) { ++ Label next_test; ++ __ lea(SCR2, Address(mdo, md->byte_offset_of_slot(data, ReceiverTypeData::receiver_offset(i)))); ++ Address recv_addr(SCR2); ++ __ ld_ptr(SCR1, recv_addr); ++ __ bnez(SCR1, next_test); ++ __ st_ptr(recv, recv_addr); ++ __ li(SCR1, DataLayout::counter_increment); ++ __ lea(SCR2, Address(mdo, md->byte_offset_of_slot(data, ReceiverTypeData::receiver_count_offset(i)))); ++ __ st_ptr(SCR1, Address(SCR2)); ++ __ b(*update_done); ++ __ bind(next_test); ++ } ++} ++ ++void LIR_Assembler::emit_typecheck_helper(LIR_OpTypeCheck *op, Label* success, ++ Label* failure, Label* obj_is_null) { ++ // we always need a stub for the failure case. ++ CodeStub* stub = op->stub(); ++ Register obj = op->object()->as_register(); ++ Register k_RInfo = op->tmp1()->as_register(); ++ Register klass_RInfo = op->tmp2()->as_register(); ++ Register dst = op->result_opr()->as_register(); ++ ciKlass* k = op->klass(); ++ Register Rtmp1 = noreg; ++ ++ // check if it needs to be profiled ++ ciMethodData* md; ++ ciProfileData* data; ++ ++ const bool should_profile = op->should_profile(); ++ ++ if (should_profile) { ++ ciMethod* method = op->profiled_method(); ++ assert(method != NULL, "Should have method"); ++ int bci = op->profiled_bci(); ++ md = method->method_data_or_null(); ++ assert(md != NULL, "Sanity"); ++ data = md->bci_to_data(bci); ++ assert(data != NULL, "need data for type check"); ++ assert(data->is_ReceiverTypeData(), "need ReceiverTypeData for type check"); ++ } ++ ++ Label profile_cast_success, profile_cast_failure; ++ Label *success_target = should_profile ? &profile_cast_success : success; ++ Label *failure_target = should_profile ? &profile_cast_failure : failure; ++ ++ if (obj == k_RInfo) { ++ k_RInfo = dst; ++ } else if (obj == klass_RInfo) { ++ klass_RInfo = dst; ++ } ++ if (k->is_loaded() && !UseCompressedClassPointers) { ++ select_different_registers(obj, dst, k_RInfo, klass_RInfo); ++ } else { ++ Rtmp1 = op->tmp3()->as_register(); ++ select_different_registers(obj, dst, k_RInfo, klass_RInfo, Rtmp1); ++ } ++ ++ assert_different_registers(obj, k_RInfo, klass_RInfo); ++ ++ if (should_profile) { ++ Label not_null; ++ __ bnez(obj, not_null); ++ // Object is null; update MDO and exit ++ Register mdo = klass_RInfo; ++ __ mov_metadata(mdo, md->constant_encoding()); ++ Address data_addr = Address(mdo, md->byte_offset_of_slot(data, DataLayout::flags_offset())); ++ __ ld_bu(SCR2, data_addr); ++ __ ori(SCR2, SCR2, BitData::null_seen_byte_constant()); ++ __ st_b(SCR2, data_addr); ++ __ b(*obj_is_null); ++ __ bind(not_null); ++ } else { ++ __ beqz(obj, *obj_is_null); ++ } ++ ++ if (!k->is_loaded()) { ++ klass2reg_with_patching(k_RInfo, op->info_for_patch()); ++ } else { ++ __ mov_metadata(k_RInfo, k->constant_encoding()); ++ } ++ __ verify_oop(obj); ++ ++ if (op->fast_check()) { ++ // get object class ++ // not a safepoint as obj null check happens earlier ++ __ load_klass(SCR2, obj); ++ __ bne_far(SCR2, k_RInfo, *failure_target); ++ // successful cast, fall through to profile or jump ++ } else { ++ // get object class ++ // not a safepoint as obj null check happens earlier ++ __ load_klass(klass_RInfo, obj); ++ if (k->is_loaded()) { ++ // See if we get an immediate positive hit ++ __ ld_ptr(SCR1, Address(klass_RInfo, int64_t(k->super_check_offset()))); ++ if ((juint)in_bytes(Klass::secondary_super_cache_offset()) != k->super_check_offset()) { ++ __ bne_far(k_RInfo, SCR1, *failure_target); ++ // successful cast, fall through to profile or jump ++ } else { ++ // See if we get an immediate positive hit ++ __ beq_far(k_RInfo, SCR1, *success_target); ++ // check for self ++ __ beq_far(klass_RInfo, k_RInfo, *success_target); ++ ++ __ addi_d(SP, SP, -2 * wordSize); ++ __ st_ptr(k_RInfo, Address(SP, 0 * wordSize)); ++ __ st_ptr(klass_RInfo, Address(SP, 1 * wordSize)); ++ __ call(Runtime1::entry_for(Runtime1::slow_subtype_check_id), relocInfo::runtime_call_type); ++ __ ld_ptr(klass_RInfo, Address(SP, 0 * wordSize)); ++ __ addi_d(SP, SP, 2 * wordSize); ++ // result is a boolean ++ __ beqz(klass_RInfo, *failure_target); ++ // successful cast, fall through to profile or jump ++ } ++ } else { ++ // perform the fast part of the checking logic ++ __ check_klass_subtype_fast_path(klass_RInfo, k_RInfo, Rtmp1, success_target, failure_target, NULL); ++ // call out-of-line instance of __ check_klass_subtype_slow_path(...): ++ __ addi_d(SP, SP, -2 * wordSize); ++ __ st_ptr(k_RInfo, Address(SP, 0 * wordSize)); ++ __ st_ptr(klass_RInfo, Address(SP, 1 * wordSize)); ++ __ call(Runtime1::entry_for(Runtime1::slow_subtype_check_id), relocInfo::runtime_call_type); ++ __ ld_ptr(k_RInfo, Address(SP, 0 * wordSize)); ++ __ ld_ptr(klass_RInfo, Address(SP, 1 * wordSize)); ++ __ addi_d(SP, SP, 2 * wordSize); ++ // result is a boolean ++ __ beqz(k_RInfo, *failure_target); ++ // successful cast, fall through to profile or jump ++ } ++ } ++ if (should_profile) { ++ Register mdo = klass_RInfo, recv = k_RInfo; ++ __ bind(profile_cast_success); ++ __ mov_metadata(mdo, md->constant_encoding()); ++ __ load_klass(recv, obj); ++ Label update_done; ++ type_profile_helper(mdo, md, data, recv, success); ++ __ b(*success); ++ ++ __ bind(profile_cast_failure); ++ __ mov_metadata(mdo, md->constant_encoding()); ++ Address counter_addr = Address(mdo, md->byte_offset_of_slot(data, CounterData::count_offset())); ++ __ ld_ptr(SCR2, counter_addr); ++ __ addi_d(SCR2, SCR2, -DataLayout::counter_increment); ++ __ st_ptr(SCR2, counter_addr); ++ __ b(*failure); ++ } ++ __ b(*success); ++} ++ ++void LIR_Assembler::emit_opTypeCheck(LIR_OpTypeCheck* op) { ++ const bool should_profile = op->should_profile(); ++ ++ LIR_Code code = op->code(); ++ if (code == lir_store_check) { ++ Register value = op->object()->as_register(); ++ Register array = op->array()->as_register(); ++ Register k_RInfo = op->tmp1()->as_register(); ++ Register klass_RInfo = op->tmp2()->as_register(); ++ Register Rtmp1 = op->tmp3()->as_register(); ++ CodeStub* stub = op->stub(); ++ ++ // check if it needs to be profiled ++ ciMethodData* md; ++ ciProfileData* data; ++ ++ if (should_profile) { ++ ciMethod* method = op->profiled_method(); ++ assert(method != NULL, "Should have method"); ++ int bci = op->profiled_bci(); ++ md = method->method_data_or_null(); ++ assert(md != NULL, "Sanity"); ++ data = md->bci_to_data(bci); ++ assert(data != NULL, "need data for type check"); ++ assert(data->is_ReceiverTypeData(), "need ReceiverTypeData for type check"); ++ } ++ Label profile_cast_success, profile_cast_failure, done; ++ Label *success_target = should_profile ? &profile_cast_success : &done; ++ Label *failure_target = should_profile ? &profile_cast_failure : stub->entry(); ++ ++ if (should_profile) { ++ Label not_null; ++ __ bnez(value, not_null); ++ // Object is null; update MDO and exit ++ Register mdo = klass_RInfo; ++ __ mov_metadata(mdo, md->constant_encoding()); ++ Address data_addr = Address(mdo, md->byte_offset_of_slot(data, DataLayout::flags_offset())); ++ __ ld_bu(SCR2, data_addr); ++ __ ori(SCR2, SCR2, BitData::null_seen_byte_constant()); ++ __ st_b(SCR2, data_addr); ++ __ b(done); ++ __ bind(not_null); ++ } else { ++ __ beqz(value, done); ++ } ++ ++ add_debug_info_for_null_check_here(op->info_for_exception()); ++ __ load_klass(k_RInfo, array); ++ __ load_klass(klass_RInfo, value); ++ ++ // get instance klass (it's already uncompressed) ++ __ ld_ptr(k_RInfo, Address(k_RInfo, ObjArrayKlass::element_klass_offset())); ++ // perform the fast part of the checking logic ++ __ check_klass_subtype_fast_path(klass_RInfo, k_RInfo, Rtmp1, success_target, failure_target, NULL); ++ // call out-of-line instance of __ check_klass_subtype_slow_path(...): ++ __ addi_d(SP, SP, -2 * wordSize); ++ __ st_ptr(k_RInfo, Address(SP, 0 * wordSize)); ++ __ st_ptr(klass_RInfo, Address(SP, 1 * wordSize)); ++ __ call(Runtime1::entry_for(Runtime1::slow_subtype_check_id), relocInfo::runtime_call_type); ++ __ ld_ptr(k_RInfo, Address(SP, 0 * wordSize)); ++ __ ld_ptr(klass_RInfo, Address(SP, 1 * wordSize)); ++ __ addi_d(SP, SP, 2 * wordSize); ++ // result is a boolean ++ __ beqz(k_RInfo, *failure_target); ++ // fall through to the success case ++ ++ if (should_profile) { ++ Register mdo = klass_RInfo, recv = k_RInfo; ++ __ bind(profile_cast_success); ++ __ mov_metadata(mdo, md->constant_encoding()); ++ __ load_klass(recv, value); ++ Label update_done; ++ type_profile_helper(mdo, md, data, recv, &done); ++ __ b(done); ++ ++ __ bind(profile_cast_failure); ++ __ mov_metadata(mdo, md->constant_encoding()); ++ Address counter_addr(mdo, md->byte_offset_of_slot(data, CounterData::count_offset())); ++ __ lea(SCR2, counter_addr); ++ __ ld_ptr(SCR1, Address(SCR2)); ++ __ addi_d(SCR1, SCR1, -DataLayout::counter_increment); ++ __ st_ptr(SCR1, Address(SCR2)); ++ __ b(*stub->entry()); ++ } ++ ++ __ bind(done); ++ } else if (code == lir_checkcast) { ++ Register obj = op->object()->as_register(); ++ Register dst = op->result_opr()->as_register(); ++ Label success; ++ emit_typecheck_helper(op, &success, op->stub()->entry(), &success); ++ __ bind(success); ++ if (dst != obj) { ++ __ move(dst, obj); ++ } ++ } else if (code == lir_instanceof) { ++ Register obj = op->object()->as_register(); ++ Register dst = op->result_opr()->as_register(); ++ Label success, failure, done; ++ emit_typecheck_helper(op, &success, &failure, &failure); ++ __ bind(failure); ++ __ move(dst, R0); ++ __ b(done); ++ __ bind(success); ++ __ li(dst, 1); ++ __ bind(done); ++ } else { ++ ShouldNotReachHere(); ++ } ++} ++ ++void LIR_Assembler::casw(Register addr, Register newval, Register cmpval, bool sign) { ++ __ cmpxchg32(Address(addr, 0), cmpval, newval, SCR1, sign, ++ /* retold */ false, /* barrier */ true); ++} ++ ++void LIR_Assembler::casl(Register addr, Register newval, Register cmpval) { ++ __ cmpxchg(Address(addr, 0), cmpval, newval, SCR1, ++ /* retold */ false, /* barrier */ true); ++} ++ ++void LIR_Assembler::emit_compare_and_swap(LIR_OpCompareAndSwap* op) { ++ assert(VM_Version::supports_cx8(), "wrong machine"); ++ Register addr; ++ if (op->addr()->is_register()) { ++ addr = as_reg(op->addr()); ++ } else { ++ assert(op->addr()->is_address(), "what else?"); ++ LIR_Address* addr_ptr = op->addr()->as_address_ptr(); ++ assert(addr_ptr->disp() == 0, "need 0 disp"); ++ assert(addr_ptr->index() == LIR_OprDesc::illegalOpr(), "need 0 index"); ++ addr = as_reg(addr_ptr->base()); ++ } ++ Register newval = as_reg(op->new_value()); ++ Register cmpval = as_reg(op->cmp_value()); ++ ++ if (op->code() == lir_cas_obj) { ++ if (UseCompressedOops) { ++ Register t1 = op->tmp1()->as_register(); ++ assert(op->tmp1()->is_valid(), "must be"); ++ __ encode_heap_oop(t1, cmpval); ++ cmpval = t1; ++ __ encode_heap_oop(SCR2, newval); ++ newval = SCR2; ++ casw(addr, newval, cmpval, false); ++ } else { ++ casl(addr, newval, cmpval); ++ } ++ } else if (op->code() == lir_cas_int) { ++ casw(addr, newval, cmpval, true); ++ } else { ++ casl(addr, newval, cmpval); ++ } ++} ++ ++void LIR_Assembler::cmove(LIR_Condition condition, LIR_Opr opr1, LIR_Opr opr2, ++ LIR_Opr result, BasicType type) { ++ Unimplemented(); ++} ++ ++void LIR_Assembler::cmp_cmove(LIR_Condition condition, LIR_Opr left, LIR_Opr right, ++ LIR_Opr src1, LIR_Opr src2, LIR_Opr result, BasicType type) { ++ assert(result->is_single_cpu() || result->is_double_cpu(), "expect single register for result"); ++ assert(left->is_single_cpu() || left->is_double_cpu(), "must be"); ++ Register regd = (result->type() == T_LONG) ? result->as_register_lo() : result->as_register(); ++ Register regl = as_reg(left); ++ Register regr = noreg; ++ Register reg1 = noreg; ++ Register reg2 = noreg; ++ jlong immr = 0; ++ ++ // comparison operands ++ if (right->is_single_cpu()) { ++ // cpu register - cpu register ++ regr = right->as_register(); ++ } else if (right->is_double_cpu()) { ++ // cpu register - cpu register ++ regr = right->as_register_lo(); ++ } else if (right->is_constant()) { ++ switch(right->type()) { ++ case T_INT: ++ case T_ADDRESS: ++ immr = right->as_constant_ptr()->as_jint(); ++ break; ++ case T_LONG: ++ immr = right->as_constant_ptr()->as_jlong(); ++ break; ++ case T_METADATA: ++ immr = (intptr_t)right->as_constant_ptr()->as_metadata(); ++ break; ++ case T_OBJECT: ++ case T_ARRAY: ++ if (right->as_constant_ptr()->as_jobject() != NULL) { ++ regr = SCR1; ++ jobject2reg(right->as_constant_ptr()->as_jobject(), regr); ++ } else { ++ immr = 0; ++ } ++ break; ++ default: ++ ShouldNotReachHere(); ++ break; ++ } ++ } else { ++ ShouldNotReachHere(); ++ } ++ ++ if (regr == noreg) { ++ switch (condition) { ++ case lir_cond_equal: ++ case lir_cond_notEqual: ++ if (!Assembler::is_simm(-immr, 12)) { ++ regr = SCR1; ++ __ li(regr, immr); ++ } ++ break; ++ default: ++ if (!Assembler::is_simm(immr, 12)) { ++ regr = SCR1; ++ __ li(regr, immr); ++ } ++ } ++ } ++ ++ // special cases ++ if (src1->is_constant() && src2->is_constant()) { ++ jlong val1 = 0, val2 = 0; ++ if (src1->type() == T_INT && src2->type() == T_INT) { ++ val1 = src1->as_jint(); ++ val2 = src2->as_jint(); ++ } else if (src1->type() == T_LONG && src2->type() == T_LONG) { ++ val1 = src1->as_jlong(); ++ val2 = src2->as_jlong(); ++ } ++ if (val1 == 0 && val2 == 1) { ++ if (regr == noreg) { ++ switch (condition) { ++ case lir_cond_equal: ++ if (immr == 0) { ++ __ sltu(regd, R0, regl); ++ } else { ++ __ addi_d(SCR1, regl, -immr); ++ __ li(regd, 1); ++ __ maskeqz(regd, regd, SCR1); ++ } ++ break; ++ case lir_cond_notEqual: ++ if (immr == 0) { ++ __ sltu(regd, R0, regl); ++ __ xori(regd, regd, 1); ++ } else { ++ __ addi_d(SCR1, regl, -immr); ++ __ li(regd, 1); ++ __ masknez(regd, regd, SCR1); ++ } ++ break; ++ case lir_cond_less: ++ __ slti(regd, regl, immr); ++ __ xori(regd, regd, 1); ++ break; ++ case lir_cond_lessEqual: ++ if (immr == 0) { ++ __ slt(regd, R0, regl); ++ } else { ++ __ li(SCR1, immr); ++ __ slt(regd, SCR1, regl); ++ } ++ break; ++ case lir_cond_greater: ++ if (immr == 0) { ++ __ slt(regd, R0, regl); ++ } else { ++ __ li(SCR1, immr); ++ __ slt(regd, SCR1, regl); ++ } ++ __ xori(regd, regd, 1); ++ break; ++ case lir_cond_greaterEqual: ++ __ slti(regd, regl, immr); ++ break; ++ case lir_cond_belowEqual: ++ if (immr == 0) { ++ __ sltu(regd, R0, regl); ++ } else { ++ __ li(SCR1, immr); ++ __ sltu(regd, SCR1, regl); ++ } ++ break; ++ case lir_cond_aboveEqual: ++ __ sltui(regd, regl, immr); ++ break; ++ default: ++ ShouldNotReachHere(); ++ } ++ } else { ++ switch (condition) { ++ case lir_cond_equal: ++ __ sub_d(SCR1, regl, regr); ++ __ li(regd, 1); ++ __ maskeqz(regd, regd, SCR1); ++ break; ++ case lir_cond_notEqual: ++ __ sub_d(SCR1, regl, regr); ++ __ li(regd, 1); ++ __ masknez(regd, regd, SCR1); ++ break; ++ case lir_cond_less: ++ __ slt(regd, regl, regr); ++ __ xori(regd, regd, 1); ++ break; ++ case lir_cond_lessEqual: ++ __ slt(regd, regr, regl); ++ break; ++ case lir_cond_greater: ++ __ slt(regd, regr, regl); ++ __ xori(regd, regd, 1); ++ break; ++ case lir_cond_greaterEqual: ++ __ slt(regd, regl, regr); ++ break; ++ case lir_cond_belowEqual: ++ __ sltu(regd, regr, regl); ++ break; ++ case lir_cond_aboveEqual: ++ __ sltu(regd, regl, regr); ++ break; ++ default: ++ ShouldNotReachHere(); ++ } ++ } ++ return; ++ } else if (val1 == 1 && val2 == 0) { ++ if (regr == noreg) { ++ switch (condition) { ++ case lir_cond_equal: ++ if (immr == 0) { ++ __ sltu(regd, R0, regl); ++ __ xori(regd, regd, 1); ++ } else { ++ __ addi_d(SCR1, regl, -immr); ++ __ li(regd, 1); ++ __ masknez(regd, regd, SCR1); ++ } ++ break; ++ case lir_cond_notEqual: ++ if (immr == 0) { ++ __ sltu(regd, R0, regl); ++ } else { ++ __ addi_d(SCR1, regl, -immr); ++ __ li(regd, 1); ++ __ maskeqz(regd, regd, SCR1); ++ } ++ break; ++ case lir_cond_less: ++ __ slti(regd, regl, immr); ++ break; ++ case lir_cond_lessEqual: ++ if (immr == 0) { ++ __ slt(regd, R0, regl); ++ } else { ++ __ li(SCR1, immr); ++ __ slt(regd, SCR1, regl); ++ } ++ __ xori(regd, regd, 1); ++ break; ++ case lir_cond_greater: ++ if (immr == 0) { ++ __ slt(regd, R0, regl); ++ } else { ++ __ li(SCR1, immr); ++ __ slt(regd, SCR1, regl); ++ } ++ break; ++ case lir_cond_greaterEqual: ++ __ slti(regd, regl, immr); ++ __ xori(regd, regd, 1); ++ break; ++ case lir_cond_belowEqual: ++ if (immr == 0) { ++ __ sltu(regd, R0, regl); ++ } else { ++ __ li(SCR1, immr); ++ __ sltu(regd, SCR1, regl); ++ } ++ __ xori(regd, regd, 1); ++ break; ++ case lir_cond_aboveEqual: ++ __ sltui(regd, regl, immr); ++ __ xori(regd, regd, 1); ++ break; ++ default: ++ ShouldNotReachHere(); ++ } ++ } else { ++ switch (condition) { ++ case lir_cond_equal: ++ __ sub_d(SCR1, regl, regr); ++ __ li(regd, 1); ++ __ masknez(regd, regd, SCR1); ++ break; ++ case lir_cond_notEqual: ++ __ sub_d(SCR1, regl, regr); ++ __ li(regd, 1); ++ __ maskeqz(regd, regd, SCR1); ++ break; ++ case lir_cond_less: ++ __ slt(regd, regl, regr); ++ break; ++ case lir_cond_lessEqual: ++ __ slt(regd, regr, regl); ++ __ xori(regd, regd, 1); ++ break; ++ case lir_cond_greater: ++ __ slt(regd, regr, regl); ++ break; ++ case lir_cond_greaterEqual: ++ __ slt(regd, regl, regr); ++ __ xori(regd, regd, 1); ++ break; ++ case lir_cond_belowEqual: ++ __ sltu(regd, regr, regl); ++ __ xori(regd, regd, 1); ++ break; ++ case lir_cond_aboveEqual: ++ __ sltu(regd, regl, regr); ++ __ xori(regd, regd, 1); ++ break; ++ default: ++ ShouldNotReachHere(); ++ } ++ } ++ return; ++ } ++ } ++ ++ // cmp ++ if (regr == noreg) { ++ switch (condition) { ++ case lir_cond_equal: ++ __ addi_d(SCR2, regl, -immr); ++ break; ++ case lir_cond_notEqual: ++ __ addi_d(SCR2, regl, -immr); ++ break; ++ case lir_cond_less: ++ __ slti(SCR2, regl, immr); ++ break; ++ case lir_cond_lessEqual: ++ __ li(SCR1, immr); ++ __ slt(SCR2, SCR1, regl); ++ break; ++ case lir_cond_greater: ++ __ li(SCR1, immr); ++ __ slt(SCR2, SCR1, regl); ++ break; ++ case lir_cond_greaterEqual: ++ __ slti(SCR2, regl, immr); ++ break; ++ case lir_cond_belowEqual: ++ __ li(SCR1, immr); ++ __ sltu(SCR2, SCR1, regl); ++ break; ++ case lir_cond_aboveEqual: ++ __ sltui(SCR2, regl, immr); ++ break; ++ default: ++ ShouldNotReachHere(); ++ } ++ } else { ++ switch (condition) { ++ case lir_cond_equal: ++ __ sub_d(SCR2, regl, regr); ++ break; ++ case lir_cond_notEqual: ++ __ sub_d(SCR2, regl, regr); ++ break; ++ case lir_cond_less: ++ __ slt(SCR2, regl, regr); ++ break; ++ case lir_cond_lessEqual: ++ __ slt(SCR2, regr, regl); ++ break; ++ case lir_cond_greater: ++ __ slt(SCR2, regr, regl); ++ break; ++ case lir_cond_greaterEqual: ++ __ slt(SCR2, regl, regr); ++ break; ++ case lir_cond_belowEqual: ++ __ sltu(SCR2, regr, regl); ++ break; ++ case lir_cond_aboveEqual: ++ __ sltu(SCR2, regl, regr); ++ break; ++ default: ++ ShouldNotReachHere(); ++ } ++ } ++ ++ // value operands ++ if (src1->is_stack()) { ++ stack2reg(src1, result, result->type()); ++ reg1 = regd; ++ } else if (src1->is_constant()) { ++ const2reg(src1, result, lir_patch_none, NULL); ++ reg1 = regd; ++ } else { ++ reg1 = (src1->type() == T_LONG) ? src1->as_register_lo() : src1->as_register(); ++ } ++ ++ if (src2->is_stack()) { ++ stack2reg(src2, FrameMap::scr1_opr, result->type()); ++ reg2 = SCR1; ++ } else if (src2->is_constant()) { ++ LIR_Opr tmp = src2->type() == T_LONG ? FrameMap::scr1_long_opr : FrameMap::scr1_opr; ++ const2reg(src2, tmp, lir_patch_none, NULL); ++ reg2 = SCR1; ++ } else { ++ reg2 = (src2->type() == T_LONG) ? src2->as_register_lo() : src2->as_register(); ++ } ++ ++ // cmove ++ switch (condition) { ++ case lir_cond_equal: ++ __ masknez(regd, reg1, SCR2); ++ __ maskeqz(SCR2, reg2, SCR2); ++ break; ++ case lir_cond_notEqual: ++ __ maskeqz(regd, reg1, SCR2); ++ __ masknez(SCR2, reg2, SCR2); ++ break; ++ case lir_cond_less: ++ __ maskeqz(regd, reg1, SCR2); ++ __ masknez(SCR2, reg2, SCR2); ++ break; ++ case lir_cond_lessEqual: ++ __ masknez(regd, reg1, SCR2); ++ __ maskeqz(SCR2, reg2, SCR2); ++ break; ++ case lir_cond_greater: ++ __ maskeqz(regd, reg1, SCR2); ++ __ masknez(SCR2, reg2, SCR2); ++ break; ++ case lir_cond_greaterEqual: ++ __ masknez(regd, reg1, SCR2); ++ __ maskeqz(SCR2, reg2, SCR2); ++ break; ++ case lir_cond_belowEqual: ++ __ masknez(regd, reg1, SCR2); ++ __ maskeqz(SCR2, reg2, SCR2); ++ break; ++ case lir_cond_aboveEqual: ++ __ masknez(regd, reg1, SCR2); ++ __ maskeqz(SCR2, reg2, SCR2); ++ break; ++ default: ++ ShouldNotReachHere(); ++ } ++ ++ __ OR(regd, regd, SCR2); ++} ++ ++void LIR_Assembler::arith_op(LIR_Code code, LIR_Opr left, LIR_Opr right, LIR_Opr dest, ++ CodeEmitInfo* info, bool pop_fpu_stack) { ++ assert(info == NULL, "should never be used, idiv/irem and ldiv/lrem not handled by this method"); ++ ++ if (left->is_single_cpu()) { ++ Register lreg = left->as_register(); ++ Register dreg = as_reg(dest); ++ ++ if (right->is_single_cpu()) { ++ // cpu register - cpu register ++ assert(left->type() == T_INT && right->type() == T_INT && dest->type() == T_INT, "should be"); ++ Register rreg = right->as_register(); ++ switch (code) { ++ case lir_add: __ add_w (dest->as_register(), lreg, rreg); break; ++ case lir_sub: __ sub_w (dest->as_register(), lreg, rreg); break; ++ case lir_mul: __ mul_w (dest->as_register(), lreg, rreg); break; ++ default: ShouldNotReachHere(); ++ } ++ } else if (right->is_double_cpu()) { ++ Register rreg = right->as_register_lo(); ++ // single_cpu + double_cpu: can happen with obj+long ++ assert(code == lir_add || code == lir_sub, "mismatched arithmetic op"); ++ switch (code) { ++ case lir_add: __ add_d(dreg, lreg, rreg); break; ++ case lir_sub: __ sub_d(dreg, lreg, rreg); break; ++ default: ShouldNotReachHere(); ++ } ++ } else if (right->is_constant()) { ++ // cpu register - constant ++ jlong c; ++ ++ // FIXME: This is fugly: we really need to factor all this logic. ++ switch(right->type()) { ++ case T_LONG: ++ c = right->as_constant_ptr()->as_jlong(); ++ break; ++ case T_INT: ++ case T_ADDRESS: ++ c = right->as_constant_ptr()->as_jint(); ++ break; ++ default: ++ ShouldNotReachHere(); ++ c = 0; // unreachable ++ break; ++ } ++ ++ assert(code == lir_add || code == lir_sub, "mismatched arithmetic op"); ++ if (c == 0 && dreg == lreg) { ++ COMMENT("effective nop elided"); ++ return; ++ } ++ ++ switch(left->type()) { ++ case T_INT: ++ switch (code) { ++ case lir_add: __ addi_w(dreg, lreg, c); break; ++ case lir_sub: __ addi_w(dreg, lreg, -c); break; ++ default: ShouldNotReachHere(); ++ } ++ break; ++ case T_OBJECT: ++ case T_ADDRESS: ++ switch (code) { ++ case lir_add: __ addi_d(dreg, lreg, c); break; ++ case lir_sub: __ addi_d(dreg, lreg, -c); break; ++ default: ShouldNotReachHere(); ++ } ++ break; ++ default: ++ ShouldNotReachHere(); ++ } ++ } else { ++ ShouldNotReachHere(); ++ } ++ } else if (left->is_double_cpu()) { ++ Register lreg_lo = left->as_register_lo(); ++ ++ if (right->is_double_cpu()) { ++ // cpu register - cpu register ++ Register rreg_lo = right->as_register_lo(); ++ switch (code) { ++ case lir_add: __ add_d(dest->as_register_lo(), lreg_lo, rreg_lo); break; ++ case lir_sub: __ sub_d(dest->as_register_lo(), lreg_lo, rreg_lo); break; ++ case lir_mul: __ mul_d(dest->as_register_lo(), lreg_lo, rreg_lo); break; ++ case lir_div: __ div_d(dest->as_register_lo(), lreg_lo, rreg_lo); break; ++ case lir_rem: __ mod_d(dest->as_register_lo(), lreg_lo, rreg_lo); break; ++ default: ShouldNotReachHere(); ++ } ++ ++ } else if (right->is_constant()) { ++ jlong c = right->as_constant_ptr()->as_jlong(); ++ Register dreg = as_reg(dest); ++ switch (code) { ++ case lir_add: ++ case lir_sub: ++ if (c == 0 && dreg == lreg_lo) { ++ COMMENT("effective nop elided"); ++ return; ++ } ++ code == lir_add ? __ addi_d(dreg, lreg_lo, c) : __ addi_d(dreg, lreg_lo, -c); ++ break; ++ case lir_div: ++ assert(c > 0 && is_power_of_2(c), "divisor must be power-of-2 constant"); ++ if (c == 1) { ++ // move lreg_lo to dreg if divisor is 1 ++ __ move(dreg, lreg_lo); ++ } else { ++ unsigned int shift = exact_log2(c); ++ // use scr1 as intermediate result register ++ __ srai_d(SCR1, lreg_lo, 63); ++ __ srli_d(SCR1, SCR1, 64 - shift); ++ __ add_d(SCR1, lreg_lo, SCR1); ++ __ srai_d(dreg, SCR1, shift); ++ } ++ break; ++ case lir_rem: ++ assert(c > 0 && is_power_of_2(c), "divisor must be power-of-2 constant"); ++ if (c == 1) { ++ // move 0 to dreg if divisor is 1 ++ __ move(dreg, R0); ++ } else { ++ // use scr1/2 as intermediate result register ++ __ sub_d(SCR1, R0, lreg_lo); ++ __ slt(SCR2, SCR1, R0); ++ __ andi(dreg, lreg_lo, c - 1); ++ __ andi(SCR1, SCR1, c - 1); ++ __ sub_d(SCR1, R0, SCR1); ++ __ maskeqz(dreg, dreg, SCR2); ++ __ masknez(SCR1, SCR1, SCR2); ++ __ OR(dreg, dreg, SCR1); ++ } ++ break; ++ default: ++ ShouldNotReachHere(); ++ } ++ } else { ++ ShouldNotReachHere(); ++ } ++ } else if (left->is_single_fpu()) { ++ assert(right->is_single_fpu(), "right hand side of float arithmetics needs to be float register"); ++ switch (code) { ++ case lir_add: __ fadd_s (dest->as_float_reg(), left->as_float_reg(), right->as_float_reg()); break; ++ case lir_sub: __ fsub_s (dest->as_float_reg(), left->as_float_reg(), right->as_float_reg()); break; ++ case lir_mul_strictfp: // fall through ++ case lir_mul: __ fmul_s (dest->as_float_reg(), left->as_float_reg(), right->as_float_reg()); break; ++ case lir_div_strictfp: // fall through ++ case lir_div: __ fdiv_s (dest->as_float_reg(), left->as_float_reg(), right->as_float_reg()); break; ++ default: ShouldNotReachHere(); ++ } ++ } else if (left->is_double_fpu()) { ++ if (right->is_double_fpu()) { ++ // fpu register - fpu register ++ switch (code) { ++ case lir_add: __ fadd_d (dest->as_double_reg(), left->as_double_reg(), right->as_double_reg()); break; ++ case lir_sub: __ fsub_d (dest->as_double_reg(), left->as_double_reg(), right->as_double_reg()); break; ++ case lir_mul_strictfp: // fall through ++ case lir_mul: __ fmul_d (dest->as_double_reg(), left->as_double_reg(), right->as_double_reg()); break; ++ case lir_div_strictfp: // fall through ++ case lir_div: __ fdiv_d (dest->as_double_reg(), left->as_double_reg(), right->as_double_reg()); break; ++ default: ShouldNotReachHere(); ++ } ++ } else { ++ if (right->is_constant()) { ++ ShouldNotReachHere(); ++ } ++ ShouldNotReachHere(); ++ } ++ } else if (left->is_single_stack() || left->is_address()) { ++ assert(left == dest, "left and dest must be equal"); ++ ShouldNotReachHere(); ++ } else { ++ ShouldNotReachHere(); ++ } ++} ++ ++void LIR_Assembler::arith_fpu_implementation(LIR_Code code, int left_index, int right_index, ++ int dest_index, bool pop_fpu_stack) { ++ Unimplemented(); ++} ++ ++void LIR_Assembler::intrinsic_op(LIR_Code code, LIR_Opr value, LIR_Opr unused, LIR_Opr dest, LIR_Op* op) { ++ switch(code) { ++ case lir_abs : __ fabs_d(dest->as_double_reg(), value->as_double_reg()); break; ++ case lir_sqrt: __ fsqrt_d(dest->as_double_reg(), value->as_double_reg()); break; ++ default : ShouldNotReachHere(); ++ } ++} ++ ++void LIR_Assembler::logic_op(LIR_Code code, LIR_Opr left, LIR_Opr right, LIR_Opr dst) { ++ assert(left->is_single_cpu() || left->is_double_cpu(), "expect single or double register"); ++ Register Rleft = left->is_single_cpu() ? left->as_register() : left->as_register_lo(); ++ ++ if (dst->is_single_cpu()) { ++ Register Rdst = dst->as_register(); ++ if (right->is_constant()) { ++ switch (code) { ++ case lir_logic_and: ++ if (Assembler::is_uimm(right->as_jint(), 12)) { ++ __ andi(Rdst, Rleft, right->as_jint()); ++ } else { ++ __ li(AT, right->as_jint()); ++ __ AND(Rdst, Rleft, AT); ++ } ++ break; ++ case lir_logic_or: __ ori(Rdst, Rleft, right->as_jint()); break; ++ case lir_logic_xor: __ xori(Rdst, Rleft, right->as_jint()); break; ++ default: ShouldNotReachHere(); break; ++ } ++ } else { ++ Register Rright = right->is_single_cpu() ? right->as_register() : right->as_register_lo(); ++ switch (code) { ++ case lir_logic_and: __ AND(Rdst, Rleft, Rright); break; ++ case lir_logic_or: __ OR(Rdst, Rleft, Rright); break; ++ case lir_logic_xor: __ XOR(Rdst, Rleft, Rright); break; ++ default: ShouldNotReachHere(); break; ++ } ++ } ++ } else { ++ Register Rdst = dst->as_register_lo(); ++ if (right->is_constant()) { ++ switch (code) { ++ case lir_logic_and: ++ if (Assembler::is_uimm(right->as_jlong(), 12)) { ++ __ andi(Rdst, Rleft, right->as_jlong()); ++ } else { ++ // We can guarantee that transform from HIR LogicOp is in range of ++ // uimm(12), but the common code directly generates LIR LogicAnd, ++ // and the right-operand is mask with all ones in the high bits. ++ __ li(AT, right->as_jlong()); ++ __ AND(Rdst, Rleft, AT); ++ } ++ break; ++ case lir_logic_or: __ ori(Rdst, Rleft, right->as_jlong()); break; ++ case lir_logic_xor: __ xori(Rdst, Rleft, right->as_jlong()); break; ++ default: ShouldNotReachHere(); break; ++ } ++ } else { ++ Register Rright = right->is_single_cpu() ? right->as_register() : right->as_register_lo(); ++ switch (code) { ++ case lir_logic_and: __ AND(Rdst, Rleft, Rright); break; ++ case lir_logic_or: __ OR(Rdst, Rleft, Rright); break; ++ case lir_logic_xor: __ XOR(Rdst, Rleft, Rright); break; ++ default: ShouldNotReachHere(); break; ++ } ++ } ++ } ++} ++ ++void LIR_Assembler::arithmetic_idiv(LIR_Code code, LIR_Opr left, LIR_Opr right, ++ LIR_Opr illegal, LIR_Opr result, CodeEmitInfo* info) { ++ // opcode check ++ assert((code == lir_idiv) || (code == lir_irem), "opcode must be idiv or irem"); ++ bool is_irem = (code == lir_irem); ++ ++ // operand check ++ assert(left->is_single_cpu(), "left must be register"); ++ assert(right->is_single_cpu() || right->is_constant(), "right must be register or constant"); ++ assert(result->is_single_cpu(), "result must be register"); ++ Register lreg = left->as_register(); ++ Register dreg = result->as_register(); ++ ++ // power-of-2 constant check and codegen ++ if (right->is_constant()) { ++ int c = right->as_constant_ptr()->as_jint(); ++ assert(c > 0 && is_power_of_2(c), "divisor must be power-of-2 constant"); ++ if (is_irem) { ++ if (c == 1) { ++ // move 0 to dreg if divisor is 1 ++ __ move(dreg, R0); ++ } else { ++ // use scr1/2 as intermediate result register ++ __ sub_w(SCR1, R0, lreg); ++ __ slt(SCR2, SCR1, R0); ++ __ andi(dreg, lreg, c - 1); ++ __ andi(SCR1, SCR1, c - 1); ++ __ sub_w(SCR1, R0, SCR1); ++ __ maskeqz(dreg, dreg, SCR2); ++ __ masknez(SCR1, SCR1, SCR2); ++ __ OR(dreg, dreg, SCR1); ++ } ++ } else { ++ if (c == 1) { ++ // move lreg to dreg if divisor is 1 ++ __ move(dreg, lreg); ++ } else { ++ unsigned int shift = exact_log2(c); ++ // use scr1 as intermediate result register ++ __ srai_w(SCR1, lreg, 31); ++ __ srli_w(SCR1, SCR1, 32 - shift); ++ __ add_w(SCR1, lreg, SCR1); ++ __ srai_w(dreg, SCR1, shift); ++ } ++ } ++ } else { ++ Register rreg = right->as_register(); ++ if (is_irem) ++ __ mod_w(dreg, lreg, rreg); ++ else ++ __ div_w(dreg, lreg, rreg); ++ } ++} ++ ++void LIR_Assembler::comp_op(LIR_Condition condition, LIR_Opr opr1, LIR_Opr opr2, LIR_Op2* op) { ++ Unimplemented(); ++} ++ ++void LIR_Assembler::comp_fl2i(LIR_Code code, LIR_Opr left, LIR_Opr right, LIR_Opr dst, LIR_Op2* op){ ++ if (code == lir_cmp_fd2i || code == lir_ucmp_fd2i) { ++ bool is_unordered_less = (code == lir_ucmp_fd2i); ++ if (left->is_single_fpu()) { ++ if (is_unordered_less) { ++ __ fcmp_clt_s(FCC0, right->as_float_reg(), left->as_float_reg()); ++ __ fcmp_cult_s(FCC1, left->as_float_reg(), right->as_float_reg()); ++ } else { ++ __ fcmp_cult_s(FCC0, right->as_float_reg(), left->as_float_reg()); ++ __ fcmp_clt_s(FCC1, left->as_float_reg(), right->as_float_reg()); ++ } ++ } else if (left->is_double_fpu()) { ++ if (is_unordered_less) { ++ __ fcmp_clt_d(FCC0, right->as_double_reg(), left->as_double_reg()); ++ __ fcmp_cult_d(FCC1, left->as_double_reg(), right->as_double_reg()); ++ } else { ++ __ fcmp_cult_d(FCC0, right->as_double_reg(), left->as_double_reg()); ++ __ fcmp_clt_d(FCC1, left->as_double_reg(), right->as_double_reg()); ++ } ++ } else { ++ ShouldNotReachHere(); ++ } ++ __ movcf2gr(dst->as_register(), FCC0); ++ __ movcf2gr(SCR1, FCC1); ++ __ sub_d(dst->as_register(), dst->as_register(), SCR1); ++ } else if (code == lir_cmp_l2i) { ++ __ slt(SCR1, left->as_register_lo(), right->as_register_lo()); ++ __ slt(dst->as_register(), right->as_register_lo(), left->as_register_lo()); ++ __ sub_d(dst->as_register(), dst->as_register(), SCR1); ++ } else { ++ ShouldNotReachHere(); ++ } ++} ++ ++void LIR_Assembler::align_call(LIR_Code code) {} ++ ++void LIR_Assembler::call(LIR_OpJavaCall* op, relocInfo::relocType rtype) { ++ address call = __ trampoline_call(AddressLiteral(op->addr(), rtype)); ++ if (call == NULL) { ++ bailout("trampoline stub overflow"); ++ return; ++ } ++ add_call_info(code_offset(), op->info()); ++} ++ ++void LIR_Assembler::ic_call(LIR_OpJavaCall* op) { ++ address call = __ ic_call(op->addr()); ++ if (call == NULL) { ++ bailout("trampoline stub overflow"); ++ return; ++ } ++ add_call_info(code_offset(), op->info()); ++} ++ ++/* Currently, vtable-dispatch is only enabled for sparc platforms */ ++void LIR_Assembler::vtable_call(LIR_OpJavaCall* op) { ++ ShouldNotReachHere(); ++} ++ ++void LIR_Assembler::emit_static_call_stub() { ++ address call_pc = __ pc(); ++ address stub = __ start_a_stub(call_stub_size); ++ if (stub == NULL) { ++ bailout("static call stub overflow"); ++ return; ++ } ++ ++ int start = __ offset(); ++ ++ __ relocate(static_stub_Relocation::spec(call_pc)); ++ ++ // Code stream for loading method may be changed. ++ __ ibar(0); ++ ++ // Rmethod contains Method*, it should be relocated for GC ++ // static stub relocation also tags the Method* in the code-stream. ++ __ mov_metadata(Rmethod, NULL); ++ // This is recognized as unresolved by relocs/nativeInst/ic code ++ __ patchable_jump(__ pc()); ++ ++ assert(__ offset() - start <= call_stub_size, "stub too big"); ++ __ end_a_stub(); ++} ++ ++void LIR_Assembler::throw_op(LIR_Opr exceptionPC, LIR_Opr exceptionOop, CodeEmitInfo* info) { ++ assert(exceptionOop->as_register() == A0, "must match"); ++ assert(exceptionPC->as_register() == A1, "must match"); ++ ++ // exception object is not added to oop map by LinearScan ++ // (LinearScan assumes that no oops are in fixed registers) ++ info->add_register_oop(exceptionOop); ++ Runtime1::StubID unwind_id; ++ ++ // get current pc information ++ // pc is only needed if the method has an exception handler, the unwind code does not need it. ++ if (compilation()->debug_info_recorder()->last_pc_offset() == __ offset()) { ++ // As no instructions have been generated yet for this LIR node it's ++ // possible that an oop map already exists for the current offset. ++ // In that case insert an dummy NOP here to ensure all oop map PCs ++ // are unique. See JDK-8237483. ++ __ nop(); ++ } ++ Label L; ++ int pc_for_athrow_offset = __ offset(); ++ __ bind(L); ++ __ lipc(exceptionPC->as_register(), L); ++ add_call_info(pc_for_athrow_offset, info); // for exception handler ++ ++ __ verify_not_null_oop(A0); ++ // search an exception handler (A0: exception oop, A1: throwing pc) ++ if (compilation()->has_fpu_code()) { ++ unwind_id = Runtime1::handle_exception_id; ++ } else { ++ unwind_id = Runtime1::handle_exception_nofpu_id; ++ } ++ __ call(Runtime1::entry_for(unwind_id), relocInfo::runtime_call_type); ++ ++ // FIXME: enough room for two byte trap ???? ++ __ nop(); ++} ++ ++void LIR_Assembler::unwind_op(LIR_Opr exceptionOop) { ++ assert(exceptionOop->as_register() == A0, "must match"); ++ __ b(_unwind_handler_entry); ++} ++ ++void LIR_Assembler::shift_op(LIR_Code code, LIR_Opr left, LIR_Opr count, LIR_Opr dest, LIR_Opr tmp) { ++ Register lreg = left->is_single_cpu() ? left->as_register() : left->as_register_lo(); ++ Register dreg = dest->is_single_cpu() ? dest->as_register() : dest->as_register_lo(); ++ ++ switch (left->type()) { ++ case T_INT: { ++ switch (code) { ++ case lir_shl: __ sll_w(dreg, lreg, count->as_register()); break; ++ case lir_shr: __ sra_w(dreg, lreg, count->as_register()); break; ++ case lir_ushr: __ srl_w(dreg, lreg, count->as_register()); break; ++ default: ShouldNotReachHere(); break; ++ } ++ break; ++ case T_LONG: ++ case T_ADDRESS: ++ case T_OBJECT: ++ switch (code) { ++ case lir_shl: __ sll_d(dreg, lreg, count->as_register()); break; ++ case lir_shr: __ sra_d(dreg, lreg, count->as_register()); break; ++ case lir_ushr: __ srl_d(dreg, lreg, count->as_register()); break; ++ default: ShouldNotReachHere(); break; ++ } ++ break; ++ default: ++ ShouldNotReachHere(); ++ break; ++ } ++ } ++} ++ ++void LIR_Assembler::shift_op(LIR_Code code, LIR_Opr left, jint count, LIR_Opr dest) { ++ Register dreg = dest->is_single_cpu() ? dest->as_register() : dest->as_register_lo(); ++ Register lreg = left->is_single_cpu() ? left->as_register() : left->as_register_lo(); ++ ++ switch (left->type()) { ++ case T_INT: { ++ switch (code) { ++ case lir_shl: __ slli_w(dreg, lreg, count); break; ++ case lir_shr: __ srai_w(dreg, lreg, count); break; ++ case lir_ushr: __ srli_w(dreg, lreg, count); break; ++ default: ShouldNotReachHere(); break; ++ } ++ break; ++ case T_LONG: ++ case T_ADDRESS: ++ case T_OBJECT: ++ switch (code) { ++ case lir_shl: __ slli_d(dreg, lreg, count); break; ++ case lir_shr: __ srai_d(dreg, lreg, count); break; ++ case lir_ushr: __ srli_d(dreg, lreg, count); break; ++ default: ShouldNotReachHere(); break; ++ } ++ break; ++ default: ++ ShouldNotReachHere(); ++ break; ++ } ++ } ++} ++ ++void LIR_Assembler::store_parameter(Register r, int offset_from_sp_in_words) { ++ assert(offset_from_sp_in_words >= 0, "invalid offset from sp"); ++ int offset_from_sp_in_bytes = offset_from_sp_in_words * BytesPerWord; ++ assert(offset_from_sp_in_bytes < frame_map()->reserved_argument_area_size(), "invalid offset"); ++ __ st_ptr(r, Address(SP, offset_from_sp_in_bytes)); ++} ++ ++void LIR_Assembler::store_parameter(jint c, int offset_from_sp_in_words) { ++ assert(offset_from_sp_in_words >= 0, "invalid offset from sp"); ++ int offset_from_sp_in_bytes = offset_from_sp_in_words * BytesPerWord; ++ assert(offset_from_sp_in_bytes < frame_map()->reserved_argument_area_size(), "invalid offset"); ++ __ li(SCR2, c); ++ __ st_ptr(SCR2, Address(SP, offset_from_sp_in_bytes)); ++} ++ ++void LIR_Assembler::store_parameter(jobject o, int offset_from_sp_in_words) { ++ ShouldNotReachHere(); ++} ++ ++// This code replaces a call to arraycopy; no exception may ++// be thrown in this code, they must be thrown in the System.arraycopy ++// activation frame; we could save some checks if this would not be the case ++void LIR_Assembler::emit_arraycopy(LIR_OpArrayCopy* op) { ++ Register j_rarg0 = T0; ++ Register j_rarg1 = A0; ++ Register j_rarg2 = A1; ++ Register j_rarg3 = A2; ++ Register j_rarg4 = A3; ++ ++ ciArrayKlass* default_type = op->expected_type(); ++ Register src = op->src()->as_register(); ++ Register dst = op->dst()->as_register(); ++ Register src_pos = op->src_pos()->as_register(); ++ Register dst_pos = op->dst_pos()->as_register(); ++ Register length = op->length()->as_register(); ++ Register tmp = op->tmp()->as_register(); ++ ++ CodeStub* stub = op->stub(); ++ int flags = op->flags(); ++ BasicType basic_type = default_type != NULL ? default_type->element_type()->basic_type() : T_ILLEGAL; ++ if (is_reference_type(basic_type)) ++ basic_type = T_OBJECT; ++ ++ // if we don't know anything, just go through the generic arraycopy ++ if (default_type == NULL) { ++ Label done; ++ assert(src == T0 && src_pos == A0, "mismatch in calling convention"); ++ ++ // Save the arguments in case the generic arraycopy fails and we ++ // have to fall back to the JNI stub ++ __ st_ptr(dst, Address(SP, 0 * BytesPerWord)); ++ __ st_ptr(dst_pos, Address(SP, 1 * BytesPerWord)); ++ __ st_ptr(length, Address(SP, 2 * BytesPerWord)); ++ __ st_ptr(src_pos, Address(SP, 3 * BytesPerWord)); ++ __ st_ptr(src, Address(SP, 4 * BytesPerWord)); ++ ++ address copyfunc_addr = StubRoutines::generic_arraycopy(); ++ ++ // FIXME: LA ++ if (copyfunc_addr == NULL) { ++ // Take a slow path for generic arraycopy. ++ __ b(*stub->entry()); ++ __ bind(*stub->continuation()); ++ return; ++ } ++ ++ // The arguments are in java calling convention so we shift them ++ // to C convention ++ assert_different_registers(A0, j_rarg1, j_rarg2, j_rarg3, j_rarg4); ++ __ move(A0, j_rarg0); ++ assert_different_registers(A1, j_rarg2, j_rarg3, j_rarg4); ++ __ move(A1, j_rarg1); ++ assert_different_registers(A2, j_rarg3, j_rarg4); ++ __ move(A2, j_rarg2); ++ assert_different_registers(A3, j_rarg4); ++ __ move(A3, j_rarg3); ++ __ move(A4, j_rarg4); ++#ifndef PRODUCT ++ if (PrintC1Statistics) { ++ __ li(SCR2, (address)&Runtime1::_generic_arraycopystub_cnt); ++ __ increment(SCR2, 1); ++ } ++#endif ++ __ call(copyfunc_addr, relocInfo::runtime_call_type); ++ ++ __ beqz(A0, *stub->continuation()); ++ ++ // Reload values from the stack so they are where the stub ++ // expects them. ++ __ ld_ptr(dst, Address(SP, 0 * BytesPerWord)); ++ __ ld_ptr(dst_pos, Address(SP, 1 * BytesPerWord)); ++ __ ld_ptr(length, Address(SP, 2 * BytesPerWord)); ++ __ ld_ptr(src_pos, Address(SP, 3 * BytesPerWord)); ++ __ ld_ptr(src, Address(SP, 4 * BytesPerWord)); ++ ++ // A0 is -1^K where K == partial copied count ++ __ nor(SCR1, A0, R0); ++ __ slli_w(SCR1, SCR1, 0); ++ // adjust length down and src/end pos up by partial copied count ++ __ sub_w(length, length, SCR1); ++ __ add_w(src_pos, src_pos, SCR1); ++ __ add_w(dst_pos, dst_pos, SCR1); ++ __ b(*stub->entry()); ++ ++ __ bind(*stub->continuation()); ++ return; ++ } ++ ++ assert(default_type != NULL && default_type->is_array_klass() && default_type->is_loaded(), ++ "must be true at this point"); ++ ++ int elem_size = type2aelembytes(basic_type); ++ Address::ScaleFactor scale = Address::times(elem_size); ++ ++ Address src_length_addr = Address(src, arrayOopDesc::length_offset_in_bytes()); ++ Address dst_length_addr = Address(dst, arrayOopDesc::length_offset_in_bytes()); ++ Address src_klass_addr = Address(src, oopDesc::klass_offset_in_bytes()); ++ Address dst_klass_addr = Address(dst, oopDesc::klass_offset_in_bytes()); ++ ++ // test for NULL ++ if (flags & LIR_OpArrayCopy::src_null_check) { ++ __ beqz(src, *stub->entry()); ++ } ++ if (flags & LIR_OpArrayCopy::dst_null_check) { ++ __ beqz(dst, *stub->entry()); ++ } ++ ++ // If the compiler was not able to prove that exact type of the source or the destination ++ // of the arraycopy is an array type, check at runtime if the source or the destination is ++ // an instance type. ++ if (flags & LIR_OpArrayCopy::type_check) { ++ if (!(flags & LIR_OpArrayCopy::LIR_OpArrayCopy::dst_objarray)) { ++ __ load_klass(tmp, dst); ++ __ ld_w(SCR1, Address(tmp, in_bytes(Klass::layout_helper_offset()))); ++ __ li(SCR2, Klass::_lh_neutral_value); ++ __ bge_far(SCR1, SCR2, *stub->entry(), true); ++ } ++ ++ if (!(flags & LIR_OpArrayCopy::LIR_OpArrayCopy::src_objarray)) { ++ __ load_klass(tmp, src); ++ __ ld_w(SCR1, Address(tmp, in_bytes(Klass::layout_helper_offset()))); ++ __ li(SCR2, Klass::_lh_neutral_value); ++ __ bge_far(SCR1, SCR2, *stub->entry(), true); ++ } ++ } ++ ++ // check if negative ++ if (flags & LIR_OpArrayCopy::src_pos_positive_check) { ++ __ blt_far(src_pos, R0, *stub->entry(), true); ++ } ++ if (flags & LIR_OpArrayCopy::dst_pos_positive_check) { ++ __ blt_far(dst_pos, R0, *stub->entry(), true); ++ } ++ ++ if (flags & LIR_OpArrayCopy::length_positive_check) { ++ __ blt_far(length, R0, *stub->entry(), true); ++ } ++ ++ if (flags & LIR_OpArrayCopy::src_range_check) { ++ __ add_w(tmp, src_pos, length); ++ __ ld_wu(SCR1, src_length_addr); ++ __ blt_far(SCR1, tmp, *stub->entry(), false); ++ } ++ if (flags & LIR_OpArrayCopy::dst_range_check) { ++ __ add_w(tmp, dst_pos, length); ++ __ ld_wu(SCR1, dst_length_addr); ++ __ blt_far(SCR1, tmp, *stub->entry(), false); ++ } ++ ++ if (flags & LIR_OpArrayCopy::type_check) { ++ // We don't know the array types are compatible ++ if (basic_type != T_OBJECT) { ++ // Simple test for basic type arrays ++ if (UseCompressedClassPointers) { ++ __ ld_wu(tmp, src_klass_addr); ++ __ ld_wu(SCR1, dst_klass_addr); ++ } else { ++ __ ld_ptr(tmp, src_klass_addr); ++ __ ld_ptr(SCR1, dst_klass_addr); ++ } ++ __ bne_far(tmp, SCR1, *stub->entry()); ++ } else { ++ // For object arrays, if src is a sub class of dst then we can ++ // safely do the copy. ++ Label cont, slow; ++ ++ __ addi_d(SP, SP, -2 * wordSize); ++ __ st_ptr(dst, Address(SP, 0 * wordSize)); ++ __ st_ptr(src, Address(SP, 1 * wordSize)); ++ ++ __ load_klass(src, src); ++ __ load_klass(dst, dst); ++ ++ __ check_klass_subtype_fast_path(src, dst, tmp, &cont, &slow, NULL); ++ ++ __ addi_d(SP, SP, -2 * wordSize); ++ __ st_ptr(dst, Address(SP, 0 * wordSize)); ++ __ st_ptr(src, Address(SP, 1 * wordSize)); ++ __ call(Runtime1::entry_for(Runtime1::slow_subtype_check_id), relocInfo::runtime_call_type); ++ __ ld_ptr(dst, Address(SP, 0 * wordSize)); ++ __ ld_ptr(src, Address(SP, 1 * wordSize)); ++ __ addi_d(SP, SP, 2 * wordSize); ++ ++ __ bnez(dst, cont); ++ ++ __ bind(slow); ++ __ ld_ptr(dst, Address(SP, 0 * wordSize)); ++ __ ld_ptr(src, Address(SP, 1 * wordSize)); ++ __ addi_d(SP, SP, 2 * wordSize); ++ ++ address copyfunc_addr = StubRoutines::checkcast_arraycopy(); ++ if (copyfunc_addr != NULL) { // use stub if available ++ // src is not a sub class of dst so we have to do a ++ // per-element check. ++ ++ int mask = LIR_OpArrayCopy::src_objarray|LIR_OpArrayCopy::dst_objarray; ++ if ((flags & mask) != mask) { ++ // Check that at least both of them object arrays. ++ assert(flags & mask, "one of the two should be known to be an object array"); ++ ++ if (!(flags & LIR_OpArrayCopy::src_objarray)) { ++ __ load_klass(tmp, src); ++ } else if (!(flags & LIR_OpArrayCopy::dst_objarray)) { ++ __ load_klass(tmp, dst); ++ } ++ int lh_offset = in_bytes(Klass::layout_helper_offset()); ++ Address klass_lh_addr(tmp, lh_offset); ++ jint objArray_lh = Klass::array_layout_helper(T_OBJECT); ++ __ ld_w(SCR1, klass_lh_addr); ++ __ li(SCR2, objArray_lh); ++ __ XOR(SCR1, SCR1, SCR2); ++ __ bnez(SCR1, *stub->entry()); ++ } ++ ++ // Spill because stubs can use any register they like and it's ++ // easier to restore just those that we care about. ++ __ st_ptr(dst, Address(SP, 0 * BytesPerWord)); ++ __ st_ptr(dst_pos, Address(SP, 1 * BytesPerWord)); ++ __ st_ptr(length, Address(SP, 2 * BytesPerWord)); ++ __ st_ptr(src_pos, Address(SP, 3 * BytesPerWord)); ++ __ st_ptr(src, Address(SP, 4 * BytesPerWord)); ++ ++ __ lea(A0, Address(src, src_pos, scale)); ++ __ addi_d(A0, A0, arrayOopDesc::base_offset_in_bytes(basic_type)); ++ assert_different_registers(A0, dst, dst_pos, length); ++ __ lea(A1, Address(dst, dst_pos, scale)); ++ __ addi_d(A1, A1, arrayOopDesc::base_offset_in_bytes(basic_type)); ++ assert_different_registers(A1, dst, length); ++ __ bstrpick_d(A2, length, 31, 0); ++ assert_different_registers(A2, dst); ++ ++ __ load_klass(A4, dst); ++ __ ld_ptr(A4, Address(A4, ObjArrayKlass::element_klass_offset())); ++ __ ld_w(A3, Address(A4, Klass::super_check_offset_offset())); ++ __ call(copyfunc_addr, relocInfo::runtime_call_type); ++ ++#ifndef PRODUCT ++ if (PrintC1Statistics) { ++ Label failed; ++ __ bnez(A0, failed); ++ __ li(SCR2, (address)&Runtime1::_arraycopy_checkcast_cnt); ++ __ increment(SCR2, 1); ++ __ bind(failed); ++ } ++#endif ++ ++ __ beqz(A0, *stub->continuation()); ++ ++#ifndef PRODUCT ++ if (PrintC1Statistics) { ++ __ li(SCR2, (address)&Runtime1::_arraycopy_checkcast_attempt_cnt); ++ __ increment(SCR2, 1); ++ } ++#endif ++ assert_different_registers(dst, dst_pos, length, src_pos, src, A0, SCR1); ++ ++ // Restore previously spilled arguments ++ __ ld_ptr(dst, Address(SP, 0 * BytesPerWord)); ++ __ ld_ptr(dst_pos, Address(SP, 1 * BytesPerWord)); ++ __ ld_ptr(length, Address(SP, 2 * BytesPerWord)); ++ __ ld_ptr(src_pos, Address(SP, 3 * BytesPerWord)); ++ __ ld_ptr(src, Address(SP, 4 * BytesPerWord)); ++ ++ // return value is -1^K where K is partial copied count ++ __ nor(SCR1, A0, R0); ++ __ slli_w(SCR1, SCR1, 0); ++ // adjust length down and src/end pos up by partial copied count ++ __ sub_w(length, length, SCR1); ++ __ add_w(src_pos, src_pos, SCR1); ++ __ add_w(dst_pos, dst_pos, SCR1); ++ } ++ ++ __ b(*stub->entry()); ++ ++ __ bind(cont); ++ __ ld_ptr(dst, Address(SP, 0 * wordSize)); ++ __ ld_ptr(src, Address(SP, 1 * wordSize)); ++ __ addi_d(SP, SP, 2 * wordSize); ++ } ++ } ++ ++#ifdef ASSERT ++ if (basic_type != T_OBJECT || !(flags & LIR_OpArrayCopy::type_check)) { ++ // Sanity check the known type with the incoming class. For the ++ // primitive case the types must match exactly with src.klass and ++ // dst.klass each exactly matching the default type. For the ++ // object array case, if no type check is needed then either the ++ // dst type is exactly the expected type and the src type is a ++ // subtype which we can't check or src is the same array as dst ++ // but not necessarily exactly of type default_type. ++ Label known_ok, halt; ++ __ mov_metadata(tmp, default_type->constant_encoding()); ++ if (UseCompressedClassPointers) { ++ __ encode_klass_not_null(tmp); ++ } ++ ++ if (basic_type != T_OBJECT) { ++ ++ if (UseCompressedClassPointers) { ++ __ ld_wu(SCR1, dst_klass_addr); ++ } else { ++ __ ld_ptr(SCR1, dst_klass_addr); ++ } ++ __ bne(tmp, SCR1, halt); ++ if (UseCompressedClassPointers) { ++ __ ld_wu(SCR1, src_klass_addr); ++ } else { ++ __ ld_ptr(SCR1, src_klass_addr); ++ } ++ __ beq(tmp, SCR1, known_ok); ++ } else { ++ if (UseCompressedClassPointers) { ++ __ ld_wu(SCR1, dst_klass_addr); ++ } else { ++ __ ld_ptr(SCR1, dst_klass_addr); ++ } ++ __ beq(tmp, SCR1, known_ok); ++ __ beq(src, dst, known_ok); ++ } ++ __ bind(halt); ++ __ stop("incorrect type information in arraycopy"); ++ __ bind(known_ok); ++ } ++#endif ++ ++#ifndef PRODUCT ++ if (PrintC1Statistics) { ++ __ li(SCR2, Runtime1::arraycopy_count_address(basic_type)); ++ __ increment(SCR2, 1); ++ } ++#endif ++ ++ __ lea(A0, Address(src, src_pos, scale)); ++ __ addi_d(A0, A0, arrayOopDesc::base_offset_in_bytes(basic_type)); ++ assert_different_registers(A0, dst, dst_pos, length); ++ __ lea(A1, Address(dst, dst_pos, scale)); ++ __ addi_d(A1, A1, arrayOopDesc::base_offset_in_bytes(basic_type)); ++ assert_different_registers(A1, length); ++ __ bstrpick_d(A2, length, 31, 0); ++ ++ bool disjoint = (flags & LIR_OpArrayCopy::overlapping) == 0; ++ bool aligned = (flags & LIR_OpArrayCopy::unaligned) == 0; ++ const char *name; ++ address entry = StubRoutines::select_arraycopy_function(basic_type, aligned, disjoint, name, false); ++ ++ CodeBlob *cb = CodeCache::find_blob(entry); ++ if (cb) { ++ __ call(entry, relocInfo::runtime_call_type); ++ } else { ++ __ call_VM_leaf(entry, 3); ++ } ++ ++ __ bind(*stub->continuation()); ++} ++ ++void LIR_Assembler::emit_lock(LIR_OpLock* op) { ++ Register obj = op->obj_opr()->as_register(); // may not be an oop ++ Register hdr = op->hdr_opr()->as_register(); ++ Register lock = op->lock_opr()->as_register(); ++ if (!UseFastLocking) { ++ __ b(*op->stub()->entry()); ++ } else if (op->code() == lir_lock) { ++ Register scratch = noreg; ++ if (UseBiasedLocking) { ++ scratch = op->scratch_opr()->as_register(); ++ } ++ assert(BasicLock::displaced_header_offset_in_bytes() == 0, ++ "lock_reg must point to the displaced header"); ++ // add debug info for NullPointerException only if one is possible ++ int null_check_offset = __ lock_object(hdr, obj, lock, scratch, *op->stub()->entry()); ++ if (op->info() != NULL) { ++ add_debug_info_for_null_check(null_check_offset, op->info()); ++ } ++ // done ++ } else if (op->code() == lir_unlock) { ++ assert(BasicLock::displaced_header_offset_in_bytes() == 0, ++ "lock_reg must point to the displaced header"); ++ __ unlock_object(hdr, obj, lock, *op->stub()->entry()); ++ } else { ++ Unimplemented(); ++ } ++ __ bind(*op->stub()->continuation()); ++} ++ ++void LIR_Assembler::emit_profile_call(LIR_OpProfileCall* op) { ++ ciMethod* method = op->profiled_method(); ++ ciMethod* callee = op->profiled_callee(); ++ int bci = op->profiled_bci(); ++ ++ // Update counter for all call types ++ ciMethodData* md = method->method_data_or_null(); ++ assert(md != NULL, "Sanity"); ++ ciProfileData* data = md->bci_to_data(bci); ++ assert(data != NULL && data->is_CounterData(), "need CounterData for calls"); ++ assert(op->mdo()->is_single_cpu(), "mdo must be allocated"); ++ Register mdo = op->mdo()->as_register(); ++ __ mov_metadata(mdo, md->constant_encoding()); ++ Address counter_addr(mdo, md->byte_offset_of_slot(data, CounterData::count_offset())); ++ Bytecodes::Code bc = method->java_code_at_bci(bci); ++ const bool callee_is_static = callee->is_loaded() && callee->is_static(); ++ // Perform additional virtual call profiling for invokevirtual and ++ // invokeinterface bytecodes ++ if ((bc == Bytecodes::_invokevirtual || bc == Bytecodes::_invokeinterface) && ++ !callee_is_static && // required for optimized MH invokes ++ C1ProfileVirtualCalls) { ++ assert(op->recv()->is_single_cpu(), "recv must be allocated"); ++ Register recv = op->recv()->as_register(); ++ assert_different_registers(mdo, recv); ++ assert(data->is_VirtualCallData(), "need VirtualCallData for virtual calls"); ++ ciKlass* known_klass = op->known_holder(); ++ if (C1OptimizeVirtualCallProfiling && known_klass != NULL) { ++ // We know the type that will be seen at this call site; we can ++ // statically update the MethodData* rather than needing to do ++ // dynamic tests on the receiver type ++ ++ // NOTE: we should probably put a lock around this search to ++ // avoid collisions by concurrent compilations ++ ciVirtualCallData* vc_data = (ciVirtualCallData*) data; ++ uint i; ++ for (i = 0; i < VirtualCallData::row_limit(); i++) { ++ ciKlass* receiver = vc_data->receiver(i); ++ if (known_klass->equals(receiver)) { ++ Address data_addr(mdo, md->byte_offset_of_slot(data, VirtualCallData::receiver_count_offset(i))); ++ __ ld_ptr(SCR2, data_addr); ++ __ addi_d(SCR2, SCR2, DataLayout::counter_increment); ++ __ st_ptr(SCR2, data_addr); ++ return; ++ } ++ } ++ ++ // Receiver type not found in profile data; select an empty slot ++ ++ // Note that this is less efficient than it should be because it ++ // always does a write to the receiver part of the ++ // VirtualCallData rather than just the first time ++ for (i = 0; i < VirtualCallData::row_limit(); i++) { ++ ciKlass* receiver = vc_data->receiver(i); ++ if (receiver == NULL) { ++ Address recv_addr(mdo, md->byte_offset_of_slot(data, VirtualCallData::receiver_offset(i))); ++ __ mov_metadata(SCR2, known_klass->constant_encoding()); ++ __ lea(SCR1, recv_addr); ++ __ st_ptr(SCR2, SCR1, 0); ++ Address data_addr(mdo, md->byte_offset_of_slot(data, VirtualCallData::receiver_count_offset(i))); ++ __ ld_ptr(SCR2, data_addr); ++ __ addi_d(SCR2, SCR1, DataLayout::counter_increment); ++ __ st_ptr(SCR2, data_addr); ++ return; ++ } ++ } ++ } else { ++ __ load_klass(recv, recv); ++ Label update_done; ++ type_profile_helper(mdo, md, data, recv, &update_done); ++ // Receiver did not match any saved receiver and there is no empty row for it. ++ // Increment total counter to indicate polymorphic case. ++ __ ld_ptr(SCR2, counter_addr); ++ __ addi_d(SCR2, SCR2, DataLayout::counter_increment); ++ __ st_ptr(SCR2, counter_addr); ++ ++ __ bind(update_done); ++ } ++ } else { ++ // Static call ++ __ ld_ptr(SCR2, counter_addr); ++ __ addi_d(SCR2, SCR2, DataLayout::counter_increment); ++ __ st_ptr(SCR2, counter_addr); ++ } ++} ++ ++void LIR_Assembler::emit_delay(LIR_OpDelay*) { ++ Unimplemented(); ++} ++ ++void LIR_Assembler::monitor_address(int monitor_no, LIR_Opr dst) { ++ __ lea(dst->as_register(), frame_map()->address_for_monitor_lock(monitor_no)); ++} ++ ++void LIR_Assembler::emit_updatecrc32(LIR_OpUpdateCRC32* op) { ++ assert(op->crc()->is_single_cpu(), "crc must be register"); ++ assert(op->val()->is_single_cpu(), "byte value must be register"); ++ assert(op->result_opr()->is_single_cpu(), "result must be register"); ++ Register crc = op->crc()->as_register(); ++ Register val = op->val()->as_register(); ++ Register res = op->result_opr()->as_register(); ++ ++ assert_different_registers(val, crc, res); ++ __ li(res, StubRoutines::crc_table_addr()); ++ __ nor(crc, crc, R0); // ~crc ++ __ update_byte_crc32(crc, val, res); ++ __ nor(res, crc, R0); // ~crc ++} ++ ++void LIR_Assembler::emit_profile_type(LIR_OpProfileType* op) { ++ COMMENT("emit_profile_type {"); ++ Register obj = op->obj()->as_register(); ++ Register tmp = op->tmp()->as_pointer_register(); ++ Address mdo_addr = as_Address(op->mdp()->as_address_ptr()); ++ ciKlass* exact_klass = op->exact_klass(); ++ intptr_t current_klass = op->current_klass(); ++ bool not_null = op->not_null(); ++ bool no_conflict = op->no_conflict(); ++ ++ Label update, next, none; ++ ++ bool do_null = !not_null; ++ bool exact_klass_set = exact_klass != NULL && ciTypeEntries::valid_ciklass(current_klass) == exact_klass; ++ bool do_update = !TypeEntries::is_type_unknown(current_klass) && !exact_klass_set; ++ ++ assert(do_null || do_update, "why are we here?"); ++ assert(!TypeEntries::was_null_seen(current_klass) || do_update, "why are we here?"); ++ assert(mdo_addr.base() != SCR1, "wrong register"); ++ ++ __ verify_oop(obj); ++ ++ if (tmp != obj) { ++ __ move(tmp, obj); ++ } ++ if (do_null) { ++ __ bnez(tmp, update); ++ if (!TypeEntries::was_null_seen(current_klass)) { ++ __ ld_ptr(SCR2, mdo_addr); ++ __ ori(SCR2, SCR2, TypeEntries::null_seen); ++ __ st_ptr(SCR2, mdo_addr); ++ } ++ if (do_update) { ++#ifndef ASSERT ++ __ b(next); ++ } ++#else ++ __ b(next); ++ } ++ } else { ++ __ bnez(tmp, update); ++ __ stop("unexpected null obj"); ++#endif ++ } ++ ++ __ bind(update); ++ ++ if (do_update) { ++#ifdef ASSERT ++ if (exact_klass != NULL) { ++ Label ok; ++ __ load_klass(tmp, tmp); ++ __ mov_metadata(SCR1, exact_klass->constant_encoding()); ++ __ XOR(SCR1, tmp, SCR1); ++ __ beqz(SCR1, ok); ++ __ stop("exact klass and actual klass differ"); ++ __ bind(ok); ++ } ++#endif ++ if (!no_conflict) { ++ if (exact_klass == NULL || TypeEntries::is_type_none(current_klass)) { ++ if (exact_klass != NULL) { ++ __ mov_metadata(tmp, exact_klass->constant_encoding()); ++ } else { ++ __ load_klass(tmp, tmp); ++ } ++ ++ __ ld_ptr(SCR2, mdo_addr); ++ __ XOR(tmp, tmp, SCR2); ++ assert(TypeEntries::type_klass_mask == -4, "must be"); ++ __ bstrpick_d(SCR1, tmp, 63, 2); ++ // klass seen before, nothing to do. The unknown bit may have been ++ // set already but no need to check. ++ __ beqz(SCR1, next); ++ ++ __ andi(SCR1, tmp, TypeEntries::type_unknown); ++ __ bnez(SCR1, next); // already unknown. Nothing to do anymore. ++ ++ if (TypeEntries::is_type_none(current_klass)) { ++ __ beqz(SCR2, none); ++ __ li(SCR1, (u1)TypeEntries::null_seen); ++ __ beq(SCR2, SCR1, none); ++ // There is a chance that the checks above (re-reading profiling ++ // data from memory) fail if another thread has just set the ++ // profiling to this obj's klass ++ membar_acquire(); ++ __ ld_ptr(SCR2, mdo_addr); ++ __ XOR(tmp, tmp, SCR2); ++ assert(TypeEntries::type_klass_mask == -4, "must be"); ++ __ bstrpick_d(SCR1, tmp, 63, 2); ++ __ beqz(SCR1, next); ++ } ++ } else { ++ assert(ciTypeEntries::valid_ciklass(current_klass) != NULL && ++ ciTypeEntries::valid_ciklass(current_klass) != exact_klass, "conflict only"); ++ ++ __ ld_ptr(tmp, mdo_addr); ++ __ andi(SCR2, tmp, TypeEntries::type_unknown); ++ __ bnez(SCR2, next); // already unknown. Nothing to do anymore. ++ } ++ ++ // different than before. Cannot keep accurate profile. ++ __ ld_ptr(SCR2, mdo_addr); ++ __ ori(SCR2, SCR2, TypeEntries::type_unknown); ++ __ st_ptr(SCR2, mdo_addr); ++ ++ if (TypeEntries::is_type_none(current_klass)) { ++ __ b(next); ++ ++ __ bind(none); ++ // first time here. Set profile type. ++ __ st_ptr(tmp, mdo_addr); ++ } ++ } else { ++ // There's a single possible klass at this profile point ++ assert(exact_klass != NULL, "should be"); ++ if (TypeEntries::is_type_none(current_klass)) { ++ __ mov_metadata(tmp, exact_klass->constant_encoding()); ++ __ ld_ptr(SCR2, mdo_addr); ++ __ XOR(tmp, tmp, SCR2); ++ assert(TypeEntries::type_klass_mask == -4, "must be"); ++ __ bstrpick_d(SCR1, tmp, 63, 2); ++ __ beqz(SCR1, next); ++#ifdef ASSERT ++ { ++ Label ok; ++ __ ld_ptr(SCR1, mdo_addr); ++ __ beqz(SCR1, ok); ++ __ li(SCR2, (u1)TypeEntries::null_seen); ++ __ beq(SCR1, SCR2, ok); ++ // may have been set by another thread ++ membar_acquire(); ++ __ mov_metadata(SCR1, exact_klass->constant_encoding()); ++ __ ld_ptr(SCR2, mdo_addr); ++ __ XOR(SCR2, SCR1, SCR2); ++ assert(TypeEntries::type_mask == -2, "must be"); ++ __ bstrpick_d(SCR2, SCR2, 63, 1); ++ __ beqz(SCR2, ok); ++ ++ __ stop("unexpected profiling mismatch"); ++ __ bind(ok); ++ } ++#endif ++ // first time here. Set profile type. ++ __ st_ptr(tmp, mdo_addr); ++ } else { ++ assert(ciTypeEntries::valid_ciklass(current_klass) != NULL && ++ ciTypeEntries::valid_ciklass(current_klass) != exact_klass, "inconsistent"); ++ ++ __ ld_ptr(tmp, mdo_addr); ++ __ andi(SCR1, tmp, TypeEntries::type_unknown); ++ __ bnez(SCR1, next); // already unknown. Nothing to do anymore. ++ ++ __ ori(tmp, tmp, TypeEntries::type_unknown); ++ __ st_ptr(tmp, mdo_addr); ++ // FIXME: Write barrier needed here? ++ } ++ } ++ ++ __ bind(next); ++ } ++ COMMENT("} emit_profile_type"); ++} ++ ++void LIR_Assembler::align_backward_branch_target() {} ++ ++void LIR_Assembler::negate(LIR_Opr left, LIR_Opr dest) { ++ if (left->is_single_cpu()) { ++ assert(dest->is_single_cpu(), "expect single result reg"); ++ __ sub_w(dest->as_register(), R0, left->as_register()); ++ } else if (left->is_double_cpu()) { ++ assert(dest->is_double_cpu(), "expect double result reg"); ++ __ sub_d(dest->as_register_lo(), R0, left->as_register_lo()); ++ } else if (left->is_single_fpu()) { ++ assert(dest->is_single_fpu(), "expect single float result reg"); ++ __ fneg_s(dest->as_float_reg(), left->as_float_reg()); ++ } else { ++ assert(left->is_double_fpu(), "expect double float operand reg"); ++ assert(dest->is_double_fpu(), "expect double float result reg"); ++ __ fneg_d(dest->as_double_reg(), left->as_double_reg()); ++ } ++} ++ ++void LIR_Assembler::leal(LIR_Opr addr, LIR_Opr dest) { ++ __ lea(dest->as_register_lo(), as_Address(addr->as_address_ptr())); ++} ++ ++void LIR_Assembler::rt_call(LIR_Opr result, address dest, const LIR_OprList* args, ++ LIR_Opr tmp, CodeEmitInfo* info) { ++ assert(!tmp->is_valid(), "don't need temporary"); ++ __ call(dest, relocInfo::runtime_call_type); ++ if (info != NULL) { ++ add_call_info_here(info); ++ } ++} ++ ++void LIR_Assembler::volatile_move_op(LIR_Opr src, LIR_Opr dest, BasicType type, ++ CodeEmitInfo* info) { ++ if (dest->is_address() || src->is_address()) { ++ move_op(src, dest, type, lir_patch_none, info, ++ /*pop_fpu_stack*/false, /*unaligned*/false, /*wide*/false); ++ } else { ++ ShouldNotReachHere(); ++ } ++} ++ ++#ifdef ASSERT ++// emit run-time assertion ++void LIR_Assembler::emit_assert(LIR_OpAssert* op) { ++ assert(op->code() == lir_assert, "must be"); ++ Label ok; ++ ++ if (op->in_opr1()->is_valid()) { ++ assert(op->in_opr2()->is_valid(), "both operands must be valid"); ++ assert(op->in_opr1()->is_cpu_register() || op->in_opr2()->is_cpu_register(), "must be"); ++ Register reg1 = as_reg(op->in_opr1()); ++ Register reg2 = as_reg(op->in_opr2()); ++ switch (op->condition()) { ++ case lir_cond_equal: __ beq(reg1, reg2, ok); break; ++ case lir_cond_notEqual: __ bne(reg1, reg2, ok); break; ++ case lir_cond_less: __ blt(reg1, reg2, ok); break; ++ case lir_cond_lessEqual: __ bge(reg2, reg1, ok); break; ++ case lir_cond_greaterEqual: __ bge(reg1, reg2, ok); break; ++ case lir_cond_greater: __ blt(reg2, reg1, ok); break; ++ case lir_cond_belowEqual: __ bgeu(reg2, reg1, ok); break; ++ case lir_cond_aboveEqual: __ bgeu(reg1, reg2, ok); break; ++ default: ShouldNotReachHere(); ++ } ++ } else { ++ assert(op->in_opr2()->is_illegal(), "both operands must be illegal"); ++ assert(op->condition() == lir_cond_always, "no other conditions allowed"); ++ } ++ if (op->halt()) { ++ const char* str = __ code_string(op->msg()); ++ __ stop(str); ++ } else { ++ breakpoint(); ++ } ++ __ bind(ok); ++} ++#endif ++ ++#ifndef PRODUCT ++#define COMMENT(x) do { __ block_comment(x); } while (0) ++#else ++#define COMMENT(x) ++#endif ++ ++void LIR_Assembler::membar() { ++ COMMENT("membar"); ++ __ membar(Assembler::AnyAny); ++} ++ ++void LIR_Assembler::membar_acquire() { ++ __ membar(Assembler::Membar_mask_bits(Assembler::LoadLoad | Assembler::LoadStore)); ++} ++ ++void LIR_Assembler::membar_release() { ++ __ membar(Assembler::Membar_mask_bits(Assembler::LoadStore|Assembler::StoreStore)); ++} ++ ++void LIR_Assembler::membar_loadload() { ++ __ membar(Assembler::LoadLoad); ++} ++ ++void LIR_Assembler::membar_storestore() { ++ __ membar(MacroAssembler::StoreStore); ++} ++ ++void LIR_Assembler::membar_loadstore() { ++ __ membar(MacroAssembler::LoadStore); ++} ++ ++void LIR_Assembler::membar_storeload() { ++ __ membar(MacroAssembler::StoreLoad); ++} ++ ++void LIR_Assembler::get_thread(LIR_Opr result_reg) { ++ __ move(result_reg->as_register(), TREG); ++} ++ ++void LIR_Assembler::peephole(LIR_List *lir) { ++} ++ ++void LIR_Assembler::atomic_op(LIR_Code code, LIR_Opr src, LIR_Opr data, ++ LIR_Opr dest, LIR_Opr tmp_op) { ++ Address addr = as_Address(src->as_address_ptr()); ++ BasicType type = src->type(); ++ Register dst = as_reg(dest); ++ Register tmp = as_reg(tmp_op); ++ bool is_oop = is_reference_type(type); ++ ++ if (Assembler::is_simm(addr.disp(), 12)) { ++ __ addi_d(tmp, addr.base(), addr.disp()); ++ } else { ++ __ li(tmp, addr.disp()); ++ __ add_d(tmp, addr.base(), tmp); ++ } ++ if (addr.index() != noreg) { ++ if (addr.scale() > Address::times_1) ++ __ alsl_d(tmp, addr.index(), tmp, addr.scale() - 1); ++ else ++ __ add_d(tmp, tmp, addr.index()); ++ } ++ ++ switch(type) { ++ case T_INT: ++ break; ++ case T_LONG: ++ break; ++ case T_OBJECT: ++ case T_ARRAY: ++ if (UseCompressedOops) { ++ // unsigned int ++ } else { ++ // long ++ } ++ break; ++ default: ++ ShouldNotReachHere(); ++ } ++ ++ if (code == lir_xadd) { ++ Register inc = noreg; ++ if (data->is_constant()) { ++ inc = SCR1; ++ __ li(inc, as_long(data)); ++ } else { ++ inc = as_reg(data); ++ } ++ switch(type) { ++ case T_INT: ++ __ amadd_db_w(dst, inc, tmp); ++ break; ++ case T_LONG: ++ __ amadd_db_d(dst, inc, tmp); ++ break; ++ case T_OBJECT: ++ case T_ARRAY: ++ if (UseCompressedOops) { ++ __ amadd_db_w(dst, inc, tmp); ++ __ lu32i_d(dst, 0); ++ } else { ++ __ amadd_db_d(dst, inc, tmp); ++ } ++ break; ++ default: ++ ShouldNotReachHere(); ++ } ++ } else if (code == lir_xchg) { ++ Register obj = as_reg(data); ++ if (is_oop && UseCompressedOops) { ++ __ encode_heap_oop(SCR2, obj); ++ obj = SCR2; ++ } ++ switch(type) { ++ case T_INT: ++ __ amswap_db_w(dst, obj, tmp); ++ break; ++ case T_LONG: ++ __ amswap_db_d(dst, obj, tmp); ++ break; ++ case T_OBJECT: ++ case T_ARRAY: ++ if (UseCompressedOops) { ++ __ amswap_db_w(dst, obj, tmp); ++ __ lu32i_d(dst, 0); ++ } else { ++ __ amswap_db_d(dst, obj, tmp); ++ } ++ break; ++ default: ++ ShouldNotReachHere(); ++ } ++ if (is_oop && UseCompressedOops) { ++ __ decode_heap_oop(dst); ++ } ++ } else { ++ ShouldNotReachHere(); ++ } ++} ++ ++#undef __ +diff --git a/hotspot/src/cpu/loongarch/vm/c1_LIRGenerator_loongarch_64.cpp b/hotspot/src/cpu/loongarch/vm/c1_LIRGenerator_loongarch_64.cpp +new file mode 100644 +index 0000000000..7cb15f689f +--- /dev/null ++++ b/hotspot/src/cpu/loongarch/vm/c1_LIRGenerator_loongarch_64.cpp +@@ -0,0 +1,1442 @@ ++/* ++ * Copyright (c) 2005, 2021, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2021, Loongson Technology. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ * ++ */ ++ ++#include "precompiled.hpp" ++#include "asm/macroAssembler.inline.hpp" ++#include "c1/c1_Compilation.hpp" ++#include "c1/c1_FrameMap.hpp" ++#include "c1/c1_Instruction.hpp" ++#include "c1/c1_LIRAssembler.hpp" ++#include "c1/c1_LIRGenerator.hpp" ++#include "c1/c1_Runtime1.hpp" ++#include "c1/c1_ValueStack.hpp" ++#include "ci/ciArray.hpp" ++#include "ci/ciObjArrayKlass.hpp" ++#include "ci/ciTypeArrayKlass.hpp" ++#include "runtime/sharedRuntime.hpp" ++#include "runtime/stubRoutines.hpp" ++#include "vmreg_loongarch.inline.hpp" ++ ++#ifdef ASSERT ++#define __ gen()->lir(__FILE__, __LINE__)-> ++#else ++#define __ gen()->lir()-> ++#endif ++ ++// Item will be loaded into a byte register; Intel only ++void LIRItem::load_byte_item() { ++ load_item(); ++} ++ ++void LIRItem::load_nonconstant() { ++ LIR_Opr r = value()->operand(); ++ if (r->is_constant()) { ++ _result = r; ++ } else { ++ load_item(); ++ } ++} ++ ++//-------------------------------------------------------------- ++// LIRGenerator ++//-------------------------------------------------------------- ++ ++LIR_Opr LIRGenerator::exceptionOopOpr() { return FrameMap::a0_oop_opr; } ++LIR_Opr LIRGenerator::exceptionPcOpr() { return FrameMap::a1_opr; } ++LIR_Opr LIRGenerator::divInOpr() { Unimplemented(); return LIR_OprFact::illegalOpr; } ++LIR_Opr LIRGenerator::divOutOpr() { Unimplemented(); return LIR_OprFact::illegalOpr; } ++LIR_Opr LIRGenerator::remOutOpr() { Unimplemented(); return LIR_OprFact::illegalOpr; } ++LIR_Opr LIRGenerator::shiftCountOpr() { Unimplemented(); return LIR_OprFact::illegalOpr; } ++LIR_Opr LIRGenerator::syncTempOpr() { return FrameMap::a0_opr; } ++LIR_Opr LIRGenerator::getThreadTemp() { return LIR_OprFact::illegalOpr; } ++ ++LIR_Opr LIRGenerator::result_register_for(ValueType* type, bool callee) { ++ LIR_Opr opr; ++ switch (type->tag()) { ++ case intTag: opr = FrameMap::a0_opr; break; ++ case objectTag: opr = FrameMap::a0_oop_opr; break; ++ case longTag: opr = FrameMap::long0_opr; break; ++ case floatTag: opr = FrameMap::fpu0_float_opr; break; ++ case doubleTag: opr = FrameMap::fpu0_double_opr; break; ++ case addressTag: ++ default: ShouldNotReachHere(); return LIR_OprFact::illegalOpr; ++ } ++ ++ assert(opr->type_field() == as_OprType(as_BasicType(type)), "type mismatch"); ++ return opr; ++} ++ ++LIR_Opr LIRGenerator::rlock_byte(BasicType type) { ++ LIR_Opr reg = new_register(T_INT); ++ set_vreg_flag(reg, LIRGenerator::byte_reg); ++ return reg; ++} ++ ++//--------- loading items into registers -------------------------------- ++ ++bool LIRGenerator::can_store_as_constant(Value v, BasicType type) const { ++ if (v->type()->as_IntConstant() != NULL) { ++ return v->type()->as_IntConstant()->value() == 0L; ++ } else if (v->type()->as_LongConstant() != NULL) { ++ return v->type()->as_LongConstant()->value() == 0L; ++ } else if (v->type()->as_ObjectConstant() != NULL) { ++ return v->type()->as_ObjectConstant()->value()->is_null_object(); ++ } else { ++ return false; ++ } ++} ++ ++bool LIRGenerator::can_inline_as_constant(Value v) const { ++ // FIXME: Just a guess ++ if (v->type()->as_IntConstant() != NULL) { ++ return Assembler::is_simm(v->type()->as_IntConstant()->value(), 12); ++ } else if (v->type()->as_LongConstant() != NULL) { ++ return v->type()->as_LongConstant()->value() == 0L; ++ } else if (v->type()->as_ObjectConstant() != NULL) { ++ return v->type()->as_ObjectConstant()->value()->is_null_object(); ++ } else { ++ return false; ++ } ++} ++ ++bool LIRGenerator::can_inline_as_constant(LIR_Const* c) const { return false; } ++ ++LIR_Opr LIRGenerator::safepoint_poll_register() { ++ return LIR_OprFact::illegalOpr; ++} ++ ++LIR_Address* LIRGenerator::generate_address(LIR_Opr base, LIR_Opr index, ++ int shift, int disp, BasicType type) { ++ assert(base->is_register(), "must be"); ++ intx large_disp = disp; ++ ++ // accumulate fixed displacements ++ if (index->is_constant()) { ++ LIR_Const *constant = index->as_constant_ptr(); ++ if (constant->type() == T_INT) { ++ large_disp += index->as_jint() << shift; ++ } else { ++ assert(constant->type() == T_LONG, "should be"); ++ jlong c = index->as_jlong() << shift; ++ if ((jlong)((jint)c) == c) { ++ large_disp += c; ++ index = LIR_OprFact::illegalOpr; ++ } else { ++ LIR_Opr tmp = new_register(T_LONG); ++ __ move(index, tmp); ++ index = tmp; ++ // apply shift and displacement below ++ } ++ } ++ } ++ ++ if (index->is_register()) { ++ // apply the shift and accumulate the displacement ++ if (shift > 0) { ++ LIR_Opr tmp = new_pointer_register(); ++ __ shift_left(index, shift, tmp); ++ index = tmp; ++ } ++ if (large_disp != 0) { ++ LIR_Opr tmp = new_pointer_register(); ++ if (Assembler::is_simm(large_disp, 12)) { ++ __ add(index, LIR_OprFact::intptrConst(large_disp), tmp); ++ index = tmp; ++ } else { ++ __ move(LIR_OprFact::intptrConst(large_disp), tmp); ++ __ add(tmp, index, tmp); ++ index = tmp; ++ } ++ large_disp = 0; ++ } ++ } else if (large_disp != 0 && !Assembler::is_simm(large_disp, 12)) { ++ // index is illegal so replace it with the displacement loaded into a register ++ index = new_pointer_register(); ++ __ move(LIR_OprFact::intptrConst(large_disp), index); ++ large_disp = 0; ++ } ++ ++ // at this point we either have base + index or base + displacement ++ if (large_disp == 0 && index->is_register()) { ++ return new LIR_Address(base, index, type); ++ } else { ++ assert(Assembler::is_simm(large_disp, 12), "must be"); ++ return new LIR_Address(base, large_disp, type); ++ } ++} ++ ++LIR_Address* LIRGenerator::emit_array_address(LIR_Opr array_opr, LIR_Opr index_opr, BasicType type, bool needs_card_mark) { ++ int offset_in_bytes = arrayOopDesc::base_offset_in_bytes(type); ++ int elem_size = type2aelembytes(type); ++ int shift = exact_log2(elem_size); ++ ++ LIR_Address* addr; ++ if (index_opr->is_constant()) { ++ addr = new LIR_Address(array_opr, offset_in_bytes + (intx)(index_opr->as_jint()) * elem_size, type); ++ } else { ++ if (offset_in_bytes) { ++ LIR_Opr tmp = new_pointer_register(); ++ __ add(array_opr, LIR_OprFact::intConst(offset_in_bytes), tmp); ++ array_opr = tmp; ++ offset_in_bytes = 0; ++ } ++ addr = new LIR_Address(array_opr, index_opr, LIR_Address::scale(type), offset_in_bytes, type); ++ } ++ if (needs_card_mark) { ++ // This store will need a precise card mark, so go ahead and ++ // compute the full adddres instead of computing once for the ++ // store and again for the card mark. ++ LIR_Opr tmp = new_pointer_register(); ++ __ leal(LIR_OprFact::address(addr), tmp); ++ return new LIR_Address(tmp, type); ++ } else { ++ return addr; ++ } ++} ++ ++LIR_Opr LIRGenerator::load_immediate(int x, BasicType type) { ++ LIR_Opr r; ++ if (type == T_LONG) { ++ r = LIR_OprFact::longConst(x); ++ if (!Assembler::is_simm(x, 12)) { ++ LIR_Opr tmp = new_register(type); ++ __ move(r, tmp); ++ return tmp; ++ } ++ } else if (type == T_INT) { ++ r = LIR_OprFact::intConst(x); ++ if (!Assembler::is_simm(x, 12)) { ++ // This is all rather nasty. We don't know whether our constant ++ // is required for a logical or an arithmetic operation, wo we ++ // don't know what the range of valid values is!! ++ LIR_Opr tmp = new_register(type); ++ __ move(r, tmp); ++ return tmp; ++ } ++ } else { ++ ShouldNotReachHere(); ++ r = NULL; // unreachable ++ } ++ return r; ++} ++ ++void LIRGenerator::increment_counter(address counter, BasicType type, int step) { ++ LIR_Opr pointer = new_pointer_register(); ++ __ move(LIR_OprFact::intptrConst(counter), pointer); ++ LIR_Address* addr = new LIR_Address(pointer, type); ++ increment_counter(addr, step); ++} ++ ++void LIRGenerator::increment_counter(LIR_Address* addr, int step) { ++ LIR_Opr imm = NULL; ++ switch(addr->type()) { ++ case T_INT: ++ imm = LIR_OprFact::intConst(step); ++ break; ++ case T_LONG: ++ imm = LIR_OprFact::longConst(step); ++ break; ++ default: ++ ShouldNotReachHere(); ++ } ++ LIR_Opr reg = new_register(addr->type()); ++ __ load(addr, reg); ++ __ add(reg, imm, reg); ++ __ store(reg, addr); ++} ++ ++template ++void LIRGenerator::cmp_mem_int_branch(LIR_Condition condition, LIR_Opr base, ++ int disp, int c, T tgt, CodeEmitInfo* info) { ++ LIR_Opr reg = new_register(T_INT); ++ __ load(generate_address(base, disp, T_INT), reg, info); ++ __ cmp_branch(condition, reg, LIR_OprFact::intConst(c), T_INT, tgt); ++} ++ ++// Explicit instantiation for all supported types. ++template void LIRGenerator::cmp_mem_int_branch(LIR_Condition, LIR_Opr, int, int, Label*, CodeEmitInfo*); ++template void LIRGenerator::cmp_mem_int_branch(LIR_Condition, LIR_Opr, int, int, BlockBegin*, CodeEmitInfo*); ++template void LIRGenerator::cmp_mem_int_branch(LIR_Condition, LIR_Opr, int, int, CodeStub*, CodeEmitInfo*); ++ ++template ++void LIRGenerator::cmp_reg_mem_branch(LIR_Condition condition, LIR_Opr reg, LIR_Opr base, ++ int disp, BasicType type, T tgt, CodeEmitInfo* info) { ++ LIR_Opr reg1 = new_register(T_INT); ++ __ load(generate_address(base, disp, type), reg1, info); ++ __ cmp_branch(condition, reg, reg1, type, tgt); ++} ++ ++// Explicit instantiation for all supported types. ++template void LIRGenerator::cmp_reg_mem_branch(LIR_Condition, LIR_Opr, LIR_Opr, int, BasicType, Label*, CodeEmitInfo*); ++template void LIRGenerator::cmp_reg_mem_branch(LIR_Condition, LIR_Opr, LIR_Opr, int, BasicType, BlockBegin*, CodeEmitInfo*); ++template void LIRGenerator::cmp_reg_mem_branch(LIR_Condition, LIR_Opr, LIR_Opr, int, BasicType, CodeStub*, CodeEmitInfo*); ++ ++bool LIRGenerator::strength_reduce_multiply(LIR_Opr left, jint c, LIR_Opr result, LIR_Opr tmp) { ++ if (is_power_of_2(c - 1)) { ++ __ shift_left(left, exact_log2(c - 1), tmp); ++ __ add(tmp, left, result); ++ return true; ++ } else if (is_power_of_2(c + 1)) { ++ __ shift_left(left, exact_log2(c + 1), tmp); ++ __ sub(tmp, left, result); ++ return true; ++ } else { ++ return false; ++ } ++} ++ ++void LIRGenerator::store_stack_parameter (LIR_Opr item, ByteSize offset_from_sp) { ++ BasicType type = item->type(); ++ __ store(item, new LIR_Address(FrameMap::sp_opr, in_bytes(offset_from_sp), type)); ++} ++ ++//---------------------------------------------------------------------- ++// visitor functions ++//---------------------------------------------------------------------- ++ ++void LIRGenerator::do_StoreIndexed(StoreIndexed* x) { ++ assert(x->is_pinned(),""); ++ bool needs_range_check = x->compute_needs_range_check(); ++ bool use_length = x->length() != NULL; ++ bool obj_store = x->elt_type() == T_ARRAY || x->elt_type() == T_OBJECT; ++ bool needs_store_check = obj_store && (x->value()->as_Constant() == NULL || ++ !get_jobject_constant(x->value())->is_null_object() || ++ x->should_profile()); ++ ++ LIRItem array(x->array(), this); ++ LIRItem index(x->index(), this); ++ LIRItem value(x->value(), this); ++ LIRItem length(this); ++ ++ array.load_item(); ++ index.load_nonconstant(); ++ ++ if (use_length && needs_range_check) { ++ length.set_instruction(x->length()); ++ length.load_item(); ++ ++ } ++ if (needs_store_check || x->check_boolean()) { ++ value.load_item(); ++ } else { ++ value.load_for_store(x->elt_type()); ++ } ++ ++ set_no_result(x); ++ ++ // the CodeEmitInfo must be duplicated for each different ++ // LIR-instruction because spilling can occur anywhere between two ++ // instructions and so the debug information must be different ++ CodeEmitInfo* range_check_info = state_for(x); ++ CodeEmitInfo* null_check_info = NULL; ++ if (x->needs_null_check()) { ++ null_check_info = new CodeEmitInfo(range_check_info); ++ } ++ ++ // emit array address setup early so it schedules better ++ // FIXME? No harm in this on aarch64, and it might help ++ LIR_Address* array_addr = emit_array_address(array.result(), index.result(), x->elt_type(), obj_store); ++ ++ if (GenerateRangeChecks && needs_range_check) { ++ if (use_length) { ++ __ cmp_branch(lir_cond_belowEqual, length.result(), index.result(), x->elt_type(), new RangeCheckStub(range_check_info, index.result())); ++ } else { ++ array_range_check(array.result(), index.result(), null_check_info, range_check_info); ++ // range_check also does the null check ++ null_check_info = NULL; ++ } ++ } ++ ++ if (GenerateArrayStoreCheck && needs_store_check) { ++ LIR_Opr tmp1 = new_register(objectType); ++ LIR_Opr tmp2 = new_register(objectType); ++ LIR_Opr tmp3 = new_register(objectType); ++ ++ CodeEmitInfo* store_check_info = new CodeEmitInfo(range_check_info); ++ __ store_check(value.result(), array.result(), tmp1, tmp2, tmp3, store_check_info, x->profiled_method(), x->profiled_bci()); ++ } ++ ++ if (obj_store) { ++ // Needs GC write barriers. ++ pre_barrier(LIR_OprFact::address(array_addr), LIR_OprFact::illegalOpr /* pre_val */, ++ true /* do_load */, false /* patch */, NULL); ++ __ move(value.result(), array_addr, null_check_info); ++ // Seems to be a precise ++ post_barrier(LIR_OprFact::address(array_addr), value.result()); ++ } else { ++ LIR_Opr result = maybe_mask_boolean(x, array.result(), value.result(), null_check_info); ++ __ move(result, array_addr, null_check_info); ++ } ++} ++ ++void LIRGenerator::do_MonitorEnter(MonitorEnter* x) { ++ assert(x->is_pinned(),""); ++ LIRItem obj(x->obj(), this); ++ obj.load_item(); ++ ++ set_no_result(x); ++ ++ // "lock" stores the address of the monitor stack slot, so this is not an oop ++ LIR_Opr lock = new_register(T_INT); ++ // Need a scratch register for biased locking ++ LIR_Opr scratch = LIR_OprFact::illegalOpr; ++ if (UseBiasedLocking) { ++ scratch = new_register(T_INT); ++ } ++ ++ CodeEmitInfo* info_for_exception = NULL; ++ if (x->needs_null_check()) { ++ info_for_exception = state_for(x); ++ } ++ // this CodeEmitInfo must not have the xhandlers because here the ++ // object is already locked (xhandlers expect object to be unlocked) ++ CodeEmitInfo* info = state_for(x, x->state(), true); ++ monitor_enter(obj.result(), lock, syncTempOpr(), scratch, ++ x->monitor_no(), info_for_exception, info); ++} ++ ++void LIRGenerator::do_MonitorExit(MonitorExit* x) { ++ assert(x->is_pinned(),""); ++ ++ LIRItem obj(x->obj(), this); ++ obj.dont_load_item(); ++ ++ LIR_Opr lock = new_register(T_INT); ++ LIR_Opr obj_temp = new_register(T_INT); ++ set_no_result(x); ++ monitor_exit(obj_temp, lock, syncTempOpr(), LIR_OprFact::illegalOpr, x->monitor_no()); ++} ++ ++void LIRGenerator::do_NegateOp(NegateOp* x) { ++ LIRItem from(x->x(), this); ++ from.load_item(); ++ LIR_Opr result = rlock_result(x); ++ __ negate (from.result(), result); ++} ++ ++// for _fadd, _fmul, _fsub, _fdiv, _frem ++// _dadd, _dmul, _dsub, _ddiv, _drem ++void LIRGenerator::do_ArithmeticOp_FPU(ArithmeticOp* x) { ++ if (x->op() == Bytecodes::_frem || x->op() == Bytecodes::_drem) { ++ // float remainder is implemented as a direct call into the runtime ++ LIRItem right(x->x(), this); ++ LIRItem left(x->y(), this); ++ ++ BasicTypeList signature(2); ++ if (x->op() == Bytecodes::_frem) { ++ signature.append(T_FLOAT); ++ signature.append(T_FLOAT); ++ } else { ++ signature.append(T_DOUBLE); ++ signature.append(T_DOUBLE); ++ } ++ CallingConvention* cc = frame_map()->c_calling_convention(&signature); ++ ++ const LIR_Opr result_reg = result_register_for(x->type()); ++ left.load_item_force(cc->at(1)); ++ right.load_item(); ++ ++ __ move(right.result(), cc->at(0)); ++ ++ address entry; ++ if (x->op() == Bytecodes::_frem) { ++ entry = CAST_FROM_FN_PTR(address, SharedRuntime::frem); ++ } else { ++ entry = CAST_FROM_FN_PTR(address, SharedRuntime::drem); ++ } ++ ++ LIR_Opr result = rlock_result(x); ++ __ call_runtime_leaf(entry, getThreadTemp(), result_reg, cc->args()); ++ __ move(result_reg, result); ++ return; ++ } ++ ++ LIRItem left(x->x(), this); ++ LIRItem right(x->y(), this); ++ LIRItem* left_arg = &left; ++ LIRItem* right_arg = &right; ++ ++ // Always load right hand side. ++ right.load_item(); ++ ++ if (!left.is_register()) ++ left.load_item(); ++ ++ LIR_Opr reg = rlock(x); ++ ++ arithmetic_op_fpu(x->op(), reg, left.result(), right.result(), x->is_strictfp()); ++ ++ set_result(x, round_item(reg)); ++} ++ ++// for _ladd, _lmul, _lsub, _ldiv, _lrem ++void LIRGenerator::do_ArithmeticOp_Long(ArithmeticOp* x) { ++ // missing test if instr is commutative and if we should swap ++ LIRItem left(x->x(), this); ++ LIRItem right(x->y(), this); ++ ++ if (x->op() == Bytecodes::_ldiv || x->op() == Bytecodes::_lrem) { ++ left.load_item(); ++ bool need_zero_check = true; ++ if (right.is_constant()) { ++ jlong c = right.get_jlong_constant(); ++ // no need to do div-by-zero check if the divisor is a non-zero constant ++ if (c != 0) need_zero_check = false; ++ // do not load right if the divisor is a power-of-2 constant ++ if (c > 0 && is_power_of_2(c) && Assembler::is_uimm(c - 1, 12)) { ++ right.dont_load_item(); ++ } else { ++ right.load_item(); ++ } ++ } else { ++ right.load_item(); ++ } ++ if (need_zero_check) { ++ CodeEmitInfo* info = state_for(x); ++ CodeStub* stub = new DivByZeroStub(info); ++ __ cmp_branch(lir_cond_equal, right.result(), LIR_OprFact::longConst(0), T_LONG, stub); ++ } ++ ++ rlock_result(x); ++ switch (x->op()) { ++ case Bytecodes::_lrem: ++ __ rem (left.result(), right.result(), x->operand()); ++ break; ++ case Bytecodes::_ldiv: ++ __ div (left.result(), right.result(), x->operand()); ++ break; ++ default: ++ ShouldNotReachHere(); ++ break; ++ } ++ } else { ++ assert(x->op() == Bytecodes::_lmul || x->op() == Bytecodes::_ladd || x->op() == Bytecodes::_lsub, ++ "expect lmul, ladd or lsub"); ++ // add, sub, mul ++ left.load_item(); ++ if (!right.is_register()) { ++ if (x->op() == Bytecodes::_lmul || !right.is_constant() || ++ (x->op() == Bytecodes::_ladd && !Assembler::is_simm(right.get_jlong_constant(), 12)) || ++ (x->op() == Bytecodes::_lsub && !Assembler::is_simm(-right.get_jlong_constant(), 12))) { ++ right.load_item(); ++ } else { // add, sub ++ assert(x->op() == Bytecodes::_ladd || x->op() == Bytecodes::_lsub, "expect ladd or lsub"); ++ // don't load constants to save register ++ right.load_nonconstant(); ++ } ++ } ++ rlock_result(x); ++ arithmetic_op_long(x->op(), x->operand(), left.result(), right.result(), NULL); ++ } ++} ++ ++// for: _iadd, _imul, _isub, _idiv, _irem ++void LIRGenerator::do_ArithmeticOp_Int(ArithmeticOp* x) { ++ // Test if instr is commutative and if we should swap ++ LIRItem left(x->x(), this); ++ LIRItem right(x->y(), this); ++ LIRItem* left_arg = &left; ++ LIRItem* right_arg = &right; ++ if (x->is_commutative() && left.is_stack() && right.is_register()) { ++ // swap them if left is real stack (or cached) and right is real register(not cached) ++ left_arg = &right; ++ right_arg = &left; ++ } ++ ++ left_arg->load_item(); ++ ++ // do not need to load right, as we can handle stack and constants ++ if (x->op() == Bytecodes::_idiv || x->op() == Bytecodes::_irem) { ++ rlock_result(x); ++ bool need_zero_check = true; ++ if (right.is_constant()) { ++ jint c = right.get_jint_constant(); ++ // no need to do div-by-zero check if the divisor is a non-zero constant ++ if (c != 0) need_zero_check = false; ++ // do not load right if the divisor is a power-of-2 constant ++ if (c > 0 && is_power_of_2(c) && Assembler::is_uimm(c - 1, 12)) { ++ right_arg->dont_load_item(); ++ } else { ++ right_arg->load_item(); ++ } ++ } else { ++ right_arg->load_item(); ++ } ++ if (need_zero_check) { ++ CodeEmitInfo* info = state_for(x); ++ CodeStub* stub = new DivByZeroStub(info); ++ __ cmp_branch(lir_cond_equal, right_arg->result(), LIR_OprFact::longConst(0), T_INT, stub); ++ } ++ ++ LIR_Opr ill = LIR_OprFact::illegalOpr; ++ if (x->op() == Bytecodes::_irem) { ++ __ irem(left_arg->result(), right_arg->result(), x->operand(), ill, NULL); ++ } else if (x->op() == Bytecodes::_idiv) { ++ __ idiv(left_arg->result(), right_arg->result(), x->operand(), ill, NULL); ++ } ++ } else if (x->op() == Bytecodes::_iadd || x->op() == Bytecodes::_isub) { ++ if (right.is_constant() && ++ ((x->op() == Bytecodes::_iadd && Assembler::is_simm(right.get_jint_constant(), 12)) || ++ (x->op() == Bytecodes::_isub && Assembler::is_simm(-right.get_jint_constant(), 12)))) { ++ right.load_nonconstant(); ++ } else { ++ right.load_item(); ++ } ++ rlock_result(x); ++ arithmetic_op_int(x->op(), x->operand(), left_arg->result(), right_arg->result(), LIR_OprFact::illegalOpr); ++ } else { ++ assert (x->op() == Bytecodes::_imul, "expect imul"); ++ if (right.is_constant()) { ++ jint c = right.get_jint_constant(); ++ if (c > 0 && c < max_jint && (is_power_of_2(c) || is_power_of_2(c - 1) || is_power_of_2(c + 1))) { ++ right_arg->dont_load_item(); ++ } else { ++ // Cannot use constant op. ++ right_arg->load_item(); ++ } ++ } else { ++ right.load_item(); ++ } ++ rlock_result(x); ++ arithmetic_op_int(x->op(), x->operand(), left_arg->result(), right_arg->result(), new_register(T_INT)); ++ } ++} ++ ++void LIRGenerator::do_ArithmeticOp(ArithmeticOp* x) { ++ // when an operand with use count 1 is the left operand, then it is ++ // likely that no move for 2-operand-LIR-form is necessary ++ if (x->is_commutative() && x->y()->as_Constant() == NULL && x->x()->use_count() > x->y()->use_count()) { ++ x->swap_operands(); ++ } ++ ++ ValueTag tag = x->type()->tag(); ++ assert(x->x()->type()->tag() == tag && x->y()->type()->tag() == tag, "wrong parameters"); ++ switch (tag) { ++ case floatTag: ++ case doubleTag: do_ArithmeticOp_FPU(x); return; ++ case longTag: do_ArithmeticOp_Long(x); return; ++ case intTag: do_ArithmeticOp_Int(x); return; ++ default: ShouldNotReachHere(); return; ++ } ++} ++ ++// _ishl, _lshl, _ishr, _lshr, _iushr, _lushr ++void LIRGenerator::do_ShiftOp(ShiftOp* x) { ++ LIRItem left(x->x(), this); ++ LIRItem right(x->y(), this); ++ ++ left.load_item(); ++ ++ rlock_result(x); ++ if (right.is_constant()) { ++ right.dont_load_item(); ++ int c; ++ switch (x->op()) { ++ case Bytecodes::_ishl: ++ c = right.get_jint_constant() & 0x1f; ++ __ shift_left(left.result(), c, x->operand()); ++ break; ++ case Bytecodes::_ishr: ++ c = right.get_jint_constant() & 0x1f; ++ __ shift_right(left.result(), c, x->operand()); ++ break; ++ case Bytecodes::_iushr: ++ c = right.get_jint_constant() & 0x1f; ++ __ unsigned_shift_right(left.result(), c, x->operand()); ++ break; ++ case Bytecodes::_lshl: ++ c = right.get_jint_constant() & 0x3f; ++ __ shift_left(left.result(), c, x->operand()); ++ break; ++ case Bytecodes::_lshr: ++ c = right.get_jint_constant() & 0x3f; ++ __ shift_right(left.result(), c, x->operand()); ++ break; ++ case Bytecodes::_lushr: ++ c = right.get_jint_constant() & 0x3f; ++ __ unsigned_shift_right(left.result(), c, x->operand()); ++ break; ++ default: ++ ShouldNotReachHere(); ++ } ++ } else { ++ right.load_item(); ++ LIR_Opr tmp = new_register(T_INT); ++ switch (x->op()) { ++ case Bytecodes::_ishl: ++ __ logical_and(right.result(), LIR_OprFact::intConst(0x1f), tmp); ++ __ shift_left(left.result(), tmp, x->operand(), tmp); ++ break; ++ case Bytecodes::_ishr: ++ __ logical_and(right.result(), LIR_OprFact::intConst(0x1f), tmp); ++ __ shift_right(left.result(), tmp, x->operand(), tmp); ++ break; ++ case Bytecodes::_iushr: ++ __ logical_and(right.result(), LIR_OprFact::intConst(0x1f), tmp); ++ __ unsigned_shift_right(left.result(), tmp, x->operand(), tmp); ++ break; ++ case Bytecodes::_lshl: ++ __ logical_and(right.result(), LIR_OprFact::intConst(0x3f), tmp); ++ __ shift_left(left.result(), tmp, x->operand(), tmp); ++ break; ++ case Bytecodes::_lshr: ++ __ logical_and(right.result(), LIR_OprFact::intConst(0x3f), tmp); ++ __ shift_right(left.result(), tmp, x->operand(), tmp); ++ break; ++ case Bytecodes::_lushr: ++ __ logical_and(right.result(), LIR_OprFact::intConst(0x3f), tmp); ++ __ unsigned_shift_right(left.result(), tmp, x->operand(), tmp); ++ break; ++ default: ++ ShouldNotReachHere(); ++ } ++ } ++} ++ ++// _iand, _land, _ior, _lor, _ixor, _lxor ++void LIRGenerator::do_LogicOp(LogicOp* x) { ++ LIRItem left(x->x(), this); ++ LIRItem right(x->y(), this); ++ ++ left.load_item(); ++ ++ rlock_result(x); ++ if (right.is_constant() ++ && ((right.type()->tag() == intTag ++ && Assembler::is_uimm(right.get_jint_constant(), 12)) ++ || (right.type()->tag() == longTag ++ && Assembler::is_uimm(right.get_jlong_constant(), 12)))) { ++ right.dont_load_item(); ++ } else { ++ right.load_item(); ++ } ++ switch (x->op()) { ++ case Bytecodes::_iand: ++ case Bytecodes::_land: ++ __ logical_and(left.result(), right.result(), x->operand()); break; ++ case Bytecodes::_ior: ++ case Bytecodes::_lor: ++ __ logical_or (left.result(), right.result(), x->operand()); break; ++ case Bytecodes::_ixor: ++ case Bytecodes::_lxor: ++ __ logical_xor(left.result(), right.result(), x->operand()); break; ++ default: Unimplemented(); ++ } ++} ++ ++// _lcmp, _fcmpl, _fcmpg, _dcmpl, _dcmpg ++void LIRGenerator::do_CompareOp(CompareOp* x) { ++ LIRItem left(x->x(), this); ++ LIRItem right(x->y(), this); ++ ValueTag tag = x->x()->type()->tag(); ++ if (tag == longTag) { ++ left.set_destroys_register(); ++ } ++ left.load_item(); ++ right.load_item(); ++ LIR_Opr reg = rlock_result(x); ++ ++ if (x->x()->type()->is_float_kind()) { ++ Bytecodes::Code code = x->op(); ++ __ fcmp2int(left.result(), right.result(), reg, ++ (code == Bytecodes::_fcmpl || code == Bytecodes::_dcmpl)); ++ } else if (x->x()->type()->tag() == longTag) { ++ __ lcmp2int(left.result(), right.result(), reg); ++ } else { ++ Unimplemented(); ++ } ++} ++ ++void LIRGenerator::do_LibmIntrinsic(Intrinsic* x) { ++ LIRItem value(x->argument_at(0), this); ++ value.set_destroys_register(); ++ ++ LIR_Opr calc_result = rlock_result(x); ++ LIR_Opr result_reg = result_register_for(x->type()); ++ ++ CallingConvention* cc = NULL; ++ ++ if (x->id() == vmIntrinsics::_dpow) { ++ LIRItem value1(x->argument_at(1), this); ++ ++ value1.set_destroys_register(); ++ ++ BasicTypeList signature(2); ++ signature.append(T_DOUBLE); ++ signature.append(T_DOUBLE); ++ cc = frame_map()->c_calling_convention(&signature); ++ value.load_item_force(cc->at(0)); ++ value1.load_item_force(cc->at(1)); ++ } else { ++ BasicTypeList signature(1); ++ signature.append(T_DOUBLE); ++ cc = frame_map()->c_calling_convention(&signature); ++ value.load_item_force(cc->at(0)); ++ } ++ ++ switch (x->id()) { ++ case vmIntrinsics::_dexp: ++ __ call_runtime_leaf(CAST_FROM_FN_PTR(address, SharedRuntime::dexp), getThreadTemp(), result_reg, cc->args()); ++ break; ++ case vmIntrinsics::_dlog: ++ __ call_runtime_leaf(CAST_FROM_FN_PTR(address, SharedRuntime::dlog), getThreadTemp(), result_reg, cc->args()); ++ break; ++ case vmIntrinsics::_dlog10: ++ __ call_runtime_leaf(CAST_FROM_FN_PTR(address, SharedRuntime::dlog10), getThreadTemp(), result_reg, cc->args()); ++ break; ++ case vmIntrinsics::_dpow: ++ __ call_runtime_leaf(CAST_FROM_FN_PTR(address, SharedRuntime::dpow), getThreadTemp(), result_reg, cc->args()); ++ break; ++ case vmIntrinsics::_dsin: ++ __ call_runtime_leaf(CAST_FROM_FN_PTR(address, SharedRuntime::dsin), getThreadTemp(), result_reg, cc->args()); ++ break; ++ case vmIntrinsics::_dcos: ++ __ call_runtime_leaf(CAST_FROM_FN_PTR(address, SharedRuntime::dcos), getThreadTemp(), result_reg, cc->args()); ++ break; ++ case vmIntrinsics::_dtan: ++ __ call_runtime_leaf(CAST_FROM_FN_PTR(address, SharedRuntime::dtan), getThreadTemp(), result_reg, cc->args()); ++ break; ++ default: ShouldNotReachHere(); ++ } ++ __ move(result_reg, calc_result); ++} ++ ++void LIRGenerator::do_CompareAndSwap(Intrinsic* x, ValueType* type) { ++ assert(x->number_of_arguments() == 4, "wrong type"); ++ LIRItem obj (x->argument_at(0), this); // object ++ LIRItem offset(x->argument_at(1), this); // offset of field ++ LIRItem cmp (x->argument_at(2), this); // value to compare with field ++ LIRItem val (x->argument_at(3), this); // replace field with val if matches cmp ++ ++ assert(obj.type()->tag() == objectTag, "invalid type"); ++ ++ // In 64bit the type can be long, sparc doesn't have this assert ++ // assert(offset.type()->tag() == intTag, "invalid type"); ++ ++ assert(cmp.type()->tag() == type->tag(), "invalid type"); ++ assert(val.type()->tag() == type->tag(), "invalid type"); ++ ++ // get address of field ++ obj.load_item(); ++ offset.load_nonconstant(); ++ val.load_item(); ++ cmp.load_item(); ++ ++ LIR_Address* a; ++ if(offset.result()->is_constant()) { ++ jlong c = offset.result()->as_jlong(); ++ if ((jlong)((jint)c) == c) { ++ a = new LIR_Address(obj.result(), ++ (jint)c, ++ as_BasicType(type)); ++ } else { ++ LIR_Opr tmp = new_register(T_LONG); ++ __ move(offset.result(), tmp); ++ a = new LIR_Address(obj.result(), ++ tmp, ++ as_BasicType(type)); ++ } ++ } else { ++ a = new LIR_Address(obj.result(), ++ offset.result(), ++ LIR_Address::times_1, ++ 0, ++ as_BasicType(type)); ++ } ++ LIR_Opr addr = new_pointer_register(); ++ __ leal(LIR_OprFact::address(a), addr); ++ ++ if (type == objectType) { // Write-barrier needed for Object fields. ++ // Do the pre-write barrier, if any. ++ pre_barrier(addr, LIR_OprFact::illegalOpr /* pre_val */, ++ true /* do_load */, false /* patch */, NULL); ++ } ++ ++ LIR_Opr result = rlock_result(x); ++ ++ LIR_Opr ill = LIR_OprFact::illegalOpr; // for convenience ++ if (type == objectType) ++ __ cas_obj(addr, cmp.result(), val.result(), new_register(T_INT), new_register(T_INT), ++ result); ++ else if (type == intType) ++ __ cas_int(addr, cmp.result(), val.result(), ill, ill); ++ else if (type == longType) ++ __ cas_long(addr, cmp.result(), val.result(), ill, ill); ++ else { ++ ShouldNotReachHere(); ++ } ++ ++ __ move(FrameMap::scr1_opr, result); ++ ++ if (type == objectType) { // Write-barrier needed for Object fields. ++ // Seems to be precise ++ post_barrier(addr, val.result()); ++ } ++} ++ ++void LIRGenerator::do_MathIntrinsic(Intrinsic* x) { ++ assert(x->number_of_arguments() == 1 || (x->number_of_arguments() == 2 && x->id() == vmIntrinsics::_dpow), ++ "wrong type"); ++ if (x->id() == vmIntrinsics::_dexp || x->id() == vmIntrinsics::_dlog || ++ x->id() == vmIntrinsics::_dpow || x->id() == vmIntrinsics::_dcos || ++ x->id() == vmIntrinsics::_dsin || x->id() == vmIntrinsics::_dtan || ++ x->id() == vmIntrinsics::_dlog10) { ++ do_LibmIntrinsic(x); ++ return; ++ } ++ switch (x->id()) { ++ case vmIntrinsics::_dabs: ++ case vmIntrinsics::_dsqrt: { ++ assert(x->number_of_arguments() == 1, "wrong type"); ++ LIRItem value(x->argument_at(0), this); ++ value.load_item(); ++ LIR_Opr dst = rlock_result(x); ++ ++ switch (x->id()) { ++ case vmIntrinsics::_dsqrt: ++ __ sqrt(value.result(), dst, LIR_OprFact::illegalOpr); ++ break; ++ case vmIntrinsics::_dabs: ++ __ abs(value.result(), dst, LIR_OprFact::illegalOpr); ++ break; ++ default: ++ ShouldNotReachHere(); ++ } ++ break; ++ } ++ default: ++ ShouldNotReachHere(); ++ } ++} ++ ++void LIRGenerator::do_ArrayCopy(Intrinsic* x) { ++ Register j_rarg0 = RT0; ++ Register j_rarg1 = RA0; ++ Register j_rarg2 = RA1; ++ Register j_rarg3 = RA2; ++ Register j_rarg4 = RA3; ++ Register j_rarg5 = RA4; ++ ++ assert(x->number_of_arguments() == 5, "wrong type"); ++ ++ // Make all state_for calls early since they can emit code ++ CodeEmitInfo* info = state_for(x, x->state()); ++ ++ LIRItem src(x->argument_at(0), this); ++ LIRItem src_pos(x->argument_at(1), this); ++ LIRItem dst(x->argument_at(2), this); ++ LIRItem dst_pos(x->argument_at(3), this); ++ LIRItem length(x->argument_at(4), this); ++ ++ // operands for arraycopy must use fixed registers, otherwise ++ // LinearScan will fail allocation (because arraycopy always needs a ++ // call) ++ ++ // The java calling convention will give us enough registers ++ // so that on the stub side the args will be perfect already. ++ // On the other slow/special case side we call C and the arg ++ // positions are not similar enough to pick one as the best. ++ // Also because the java calling convention is a "shifted" version ++ // of the C convention we can process the java args trivially into C ++ // args without worry of overwriting during the xfer ++ ++ src.load_item_force (FrameMap::as_oop_opr(j_rarg0)); ++ src_pos.load_item_force (FrameMap::as_opr(j_rarg1)); ++ dst.load_item_force (FrameMap::as_oop_opr(j_rarg2)); ++ dst_pos.load_item_force (FrameMap::as_opr(j_rarg3)); ++ length.load_item_force (FrameMap::as_opr(j_rarg4)); ++ ++ LIR_Opr tmp = FrameMap::as_opr(j_rarg5); ++ ++ set_no_result(x); ++ ++ int flags; ++ ciArrayKlass* expected_type; ++ arraycopy_helper(x, &flags, &expected_type); ++ ++ __ arraycopy(src.result(), src_pos.result(), dst.result(), dst_pos.result(), ++ length.result(), tmp, expected_type, flags, info); // does add_safepoint ++} ++ ++void LIRGenerator::do_update_CRC32(Intrinsic* x) { ++ assert(UseCRC32Intrinsics, "why are we here?"); ++ // Make all state_for calls early since they can emit code ++ LIR_Opr result = rlock_result(x); ++ int flags = 0; ++ switch (x->id()) { ++ case vmIntrinsics::_updateCRC32: { ++ LIRItem crc(x->argument_at(0), this); ++ LIRItem val(x->argument_at(1), this); ++ // val is destroyed by update_crc32 ++ val.set_destroys_register(); ++ crc.load_item(); ++ val.load_item(); ++ __ update_crc32(crc.result(), val.result(), result); ++ break; ++ } ++ case vmIntrinsics::_updateBytesCRC32: ++ case vmIntrinsics::_updateByteBufferCRC32: { ++ bool is_updateBytes = (x->id() == vmIntrinsics::_updateBytesCRC32); ++ ++ LIRItem crc(x->argument_at(0), this); ++ LIRItem buf(x->argument_at(1), this); ++ LIRItem off(x->argument_at(2), this); ++ LIRItem len(x->argument_at(3), this); ++ buf.load_item(); ++ off.load_nonconstant(); ++ ++ LIR_Opr index = off.result(); ++ int offset = is_updateBytes ? arrayOopDesc::base_offset_in_bytes(T_BYTE) : 0; ++ if(off.result()->is_constant()) { ++ index = LIR_OprFact::illegalOpr; ++ offset += off.result()->as_jint(); ++ } ++ LIR_Opr base_op = buf.result(); ++ ++ if (index->is_valid()) { ++ LIR_Opr tmp = new_register(T_LONG); ++ __ convert(Bytecodes::_i2l, index, tmp); ++ index = tmp; ++ } ++ ++ if (offset) { ++ LIR_Opr tmp = new_pointer_register(); ++ __ add(base_op, LIR_OprFact::intConst(offset), tmp); ++ base_op = tmp; ++ offset = 0; ++ } ++ ++ LIR_Address* a = new LIR_Address(base_op, index, LIR_Address::times_1, offset, T_BYTE); ++ BasicTypeList signature(3); ++ signature.append(T_INT); ++ signature.append(T_ADDRESS); ++ signature.append(T_INT); ++ CallingConvention* cc = frame_map()->c_calling_convention(&signature); ++ const LIR_Opr result_reg = result_register_for(x->type()); ++ ++ LIR_Opr addr = new_pointer_register(); ++ __ leal(LIR_OprFact::address(a), addr); ++ ++ crc.load_item_force(cc->at(0)); ++ __ move(addr, cc->at(1)); ++ len.load_item_force(cc->at(2)); ++ ++ __ call_runtime_leaf(StubRoutines::updateBytesCRC32(), getThreadTemp(), result_reg, cc->args()); ++ __ move(result_reg, result); ++ ++ break; ++ } ++ default: { ++ ShouldNotReachHere(); ++ } ++ } ++} ++ ++// _i2l, _i2f, _i2d, _l2i, _l2f, _l2d, _f2i, _f2l, _f2d, _d2i, _d2l, _d2f ++// _i2b, _i2c, _i2s ++void LIRGenerator::do_Convert(Convert* x) { ++ LIRItem value(x->value(), this); ++ value.load_item(); ++ LIR_Opr input = value.result(); ++ LIR_Opr result = rlock(x); ++ ++ // arguments of lir_convert ++ LIR_Opr conv_input = input; ++ LIR_Opr conv_result = result; ++ ++ switch (x->op()) { ++ case Bytecodes::_f2i: ++ case Bytecodes::_f2l: ++ __ convert(x->op(), conv_input, conv_result, NULL, new_register(T_FLOAT)); ++ break; ++ case Bytecodes::_d2i: ++ case Bytecodes::_d2l: ++ __ convert(x->op(), conv_input, conv_result, NULL, new_register(T_DOUBLE)); ++ break; ++ default: ++ __ convert(x->op(), conv_input, conv_result); ++ break; ++ } ++ ++ assert(result->is_virtual(), "result must be virtual register"); ++ set_result(x, result); ++} ++ ++void LIRGenerator::do_NewInstance(NewInstance* x) { ++#ifndef PRODUCT ++ if (PrintNotLoaded && !x->klass()->is_loaded()) { ++ tty->print_cr(" ###class not loaded at new bci %d", x->printable_bci()); ++ } ++#endif ++ CodeEmitInfo* info = state_for(x, x->state()); ++ LIR_Opr reg = result_register_for(x->type()); ++ new_instance(reg, x->klass(), x->is_unresolved(), ++ FrameMap::t0_oop_opr, ++ FrameMap::t1_oop_opr, ++ FrameMap::a4_oop_opr, ++ LIR_OprFact::illegalOpr, ++ FrameMap::a3_metadata_opr, info); ++ LIR_Opr result = rlock_result(x); ++ __ move(reg, result); ++} ++ ++void LIRGenerator::do_NewTypeArray(NewTypeArray* x) { ++ CodeEmitInfo* info = state_for(x, x->state()); ++ ++ LIRItem length(x->length(), this); ++ length.load_item_force(FrameMap::s0_opr); ++ ++ LIR_Opr reg = result_register_for(x->type()); ++ LIR_Opr tmp1 = FrameMap::t0_oop_opr; ++ LIR_Opr tmp2 = FrameMap::t1_oop_opr; ++ LIR_Opr tmp3 = FrameMap::a5_oop_opr; ++ LIR_Opr tmp4 = reg; ++ LIR_Opr klass_reg = FrameMap::a3_metadata_opr; ++ LIR_Opr len = length.result(); ++ BasicType elem_type = x->elt_type(); ++ ++ __ metadata2reg(ciTypeArrayKlass::make(elem_type)->constant_encoding(), klass_reg); ++ ++ CodeStub* slow_path = new NewTypeArrayStub(klass_reg, len, reg, info); ++ __ allocate_array(reg, len, tmp1, tmp2, tmp3, tmp4, elem_type, klass_reg, slow_path); ++ ++ LIR_Opr result = rlock_result(x); ++ __ move(reg, result); ++} ++ ++void LIRGenerator::do_NewObjectArray(NewObjectArray* x) { ++ LIRItem length(x->length(), this); ++ // in case of patching (i.e., object class is not yet loaded), we need to reexecute the instruction ++ // and therefore provide the state before the parameters have been consumed ++ CodeEmitInfo* patching_info = NULL; ++ if (!x->klass()->is_loaded() || PatchALot) { ++ patching_info = state_for(x, x->state_before()); ++ } ++ ++ CodeEmitInfo* info = state_for(x, x->state()); ++ ++ LIR_Opr reg = result_register_for(x->type()); ++ LIR_Opr tmp1 = FrameMap::t0_oop_opr; ++ LIR_Opr tmp2 = FrameMap::t1_oop_opr; ++ LIR_Opr tmp3 = FrameMap::a5_oop_opr; ++ LIR_Opr tmp4 = reg; ++ LIR_Opr klass_reg = FrameMap::a3_metadata_opr; ++ ++ length.load_item_force(FrameMap::s0_opr); ++ LIR_Opr len = length.result(); ++ ++ CodeStub* slow_path = new NewObjectArrayStub(klass_reg, len, reg, info); ++ ciKlass* obj = (ciKlass*) ciObjArrayKlass::make(x->klass()); ++ if (obj == ciEnv::unloaded_ciobjarrayklass()) { ++ BAILOUT("encountered unloaded_ciobjarrayklass due to out of memory error"); ++ } ++ klass2reg_with_patching(klass_reg, obj, patching_info); ++ __ allocate_array(reg, len, tmp1, tmp2, tmp3, tmp4, T_OBJECT, klass_reg, slow_path); ++ ++ LIR_Opr result = rlock_result(x); ++ __ move(reg, result); ++} ++ ++void LIRGenerator::do_NewMultiArray(NewMultiArray* x) { ++ Values* dims = x->dims(); ++ int i = dims->length(); ++ LIRItemList* items = new LIRItemList(i, NULL); ++ while (i-- > 0) { ++ LIRItem* size = new LIRItem(dims->at(i), this); ++ items->at_put(i, size); ++ } ++ ++ // Evaluate state_for early since it may emit code. ++ CodeEmitInfo* patching_info = NULL; ++ if (!x->klass()->is_loaded() || PatchALot) { ++ patching_info = state_for(x, x->state_before()); ++ ++ // Cannot re-use same xhandlers for multiple CodeEmitInfos, so ++ // clone all handlers (NOTE: Usually this is handled transparently ++ // by the CodeEmitInfo cloning logic in CodeStub constructors but ++ // is done explicitly here because a stub isn't being used). ++ x->set_exception_handlers(new XHandlers(x->exception_handlers())); ++ } ++ CodeEmitInfo* info = state_for(x, x->state()); ++ ++ i = dims->length(); ++ while (i-- > 0) { ++ LIRItem* size = items->at(i); ++ size->load_item(); ++ ++ store_stack_parameter(size->result(), in_ByteSize(i*4)); ++ } ++ ++ LIR_Opr klass_reg = FrameMap::a0_metadata_opr; ++ klass2reg_with_patching(klass_reg, x->klass(), patching_info); ++ ++ LIR_Opr rank = FrameMap::s0_opr; ++ __ move(LIR_OprFact::intConst(x->rank()), rank); ++ LIR_Opr varargs = FrameMap::a2_opr; ++ __ move(FrameMap::sp_opr, varargs); ++ LIR_OprList* args = new LIR_OprList(3); ++ args->append(klass_reg); ++ args->append(rank); ++ args->append(varargs); ++ LIR_Opr reg = result_register_for(x->type()); ++ __ call_runtime(Runtime1::entry_for(Runtime1::new_multi_array_id), ++ LIR_OprFact::illegalOpr, ++ reg, args, info); ++ ++ LIR_Opr result = rlock_result(x); ++ __ move(reg, result); ++} ++ ++void LIRGenerator::do_BlockBegin(BlockBegin* x) { ++ // nothing to do for now ++} ++ ++void LIRGenerator::do_CheckCast(CheckCast* x) { ++ LIRItem obj(x->obj(), this); ++ ++ CodeEmitInfo* patching_info = NULL; ++ if (!x->klass()->is_loaded() || ++ (PatchALot && !x->is_incompatible_class_change_check() && ++ !x->is_invokespecial_receiver_check())) { ++ // must do this before locking the destination register as an oop register, ++ // and before the obj is loaded (the latter is for deoptimization) ++ patching_info = state_for(x, x->state_before()); ++ } ++ obj.load_item(); ++ ++ // info for exceptions ++ CodeEmitInfo* info_for_exception = ++ (x->needs_exception_state() ? state_for(x) : ++ state_for(x, x->state_before(), true /*ignore_xhandler*/)); ++ ++ CodeStub* stub; ++ if (x->is_incompatible_class_change_check()) { ++ assert(patching_info == NULL, "can't patch this"); ++ stub = new SimpleExceptionStub(Runtime1::throw_incompatible_class_change_error_id, ++ LIR_OprFact::illegalOpr, info_for_exception); ++ } else if (x->is_invokespecial_receiver_check()) { ++ assert(patching_info == NULL, "can't patch this"); ++ stub = new DeoptimizeStub(info_for_exception); ++ } else { ++ stub = new SimpleExceptionStub(Runtime1::throw_class_cast_exception_id, ++ obj.result(), info_for_exception); ++ } ++ LIR_Opr reg = rlock_result(x); ++ LIR_Opr tmp3 = LIR_OprFact::illegalOpr; ++ if (!x->klass()->is_loaded() || UseCompressedClassPointers) { ++ tmp3 = new_register(objectType); ++ } ++ __ checkcast(reg, obj.result(), x->klass(), ++ new_register(objectType), new_register(objectType), tmp3, ++ x->direct_compare(), info_for_exception, patching_info, stub, ++ x->profiled_method(), x->profiled_bci()); ++} ++ ++void LIRGenerator::do_InstanceOf(InstanceOf* x) { ++ LIRItem obj(x->obj(), this); ++ ++ // result and test object may not be in same register ++ LIR_Opr reg = rlock_result(x); ++ CodeEmitInfo* patching_info = NULL; ++ if ((!x->klass()->is_loaded() || PatchALot)) { ++ // must do this before locking the destination register as an oop register ++ patching_info = state_for(x, x->state_before()); ++ } ++ obj.load_item(); ++ LIR_Opr tmp3 = LIR_OprFact::illegalOpr; ++ if (!x->klass()->is_loaded() || UseCompressedClassPointers) { ++ tmp3 = new_register(objectType); ++ } ++ __ instanceof(reg, obj.result(), x->klass(), ++ new_register(objectType), new_register(objectType), tmp3, ++ x->direct_compare(), patching_info, x->profiled_method(), x->profiled_bci()); ++} ++ ++void LIRGenerator::do_If(If* x) { ++ assert(x->number_of_sux() == 2, "inconsistency"); ++ ValueTag tag = x->x()->type()->tag(); ++ bool is_safepoint = x->is_safepoint(); ++ ++ If::Condition cond = x->cond(); ++ ++ LIRItem xitem(x->x(), this); ++ LIRItem yitem(x->y(), this); ++ LIRItem* xin = &xitem; ++ LIRItem* yin = &yitem; ++ ++ if (tag == longTag) { ++ // for longs, only conditions "eql", "neq", "lss", "geq" are valid; ++ // mirror for other conditions ++ if (cond == If::gtr || cond == If::leq) { ++ cond = Instruction::mirror(cond); ++ xin = &yitem; ++ yin = &xitem; ++ } ++ xin->set_destroys_register(); ++ } ++ xin->load_item(); ++ ++ if (tag == longTag) { ++ if (yin->is_constant() && yin->get_jlong_constant() == 0) { ++ yin->dont_load_item(); ++ } else { ++ yin->load_item(); ++ } ++ } else if (tag == intTag) { ++ if (yin->is_constant() && yin->get_jint_constant() == 0) { ++ yin->dont_load_item(); ++ } else { ++ yin->load_item(); ++ } ++ } else { ++ yin->load_item(); ++ } ++ ++ set_no_result(x); ++ ++ LIR_Opr left = xin->result(); ++ LIR_Opr right = yin->result(); ++ ++ // add safepoint before generating condition code so it can be recomputed ++ if (x->is_safepoint()) { ++ // increment backedge counter if needed ++ increment_backedge_counter(state_for(x, x->state_before()), x->profiled_bci()); ++ __ safepoint(LIR_OprFact::illegalOpr, state_for(x, x->state_before())); ++ } ++ ++ // Generate branch profiling. Profiling code doesn't kill flags. ++ profile_branch(x, cond, left, right); ++ move_to_phi(x->state()); ++ if (x->x()->type()->is_float_kind()) { ++ __ cmp_branch(lir_cond(cond), left, right, right->type(), x->tsux(), x->usux()); ++ } else { ++ __ cmp_branch(lir_cond(cond), left, right, right->type(), x->tsux()); ++ } ++ assert(x->default_sux() == x->fsux(), "wrong destination above"); ++ __ jump(x->default_sux()); ++} ++ ++LIR_Opr LIRGenerator::getThreadPointer() { ++ return FrameMap::as_pointer_opr(TREG); ++} ++ ++void LIRGenerator::trace_block_entry(BlockBegin* block) { Unimplemented(); } ++ ++void LIRGenerator::volatile_field_store(LIR_Opr value, LIR_Address* address, ++ CodeEmitInfo* info) { ++ __ volatile_store_mem_reg(value, address, info); ++} ++ ++void LIRGenerator::volatile_field_load(LIR_Address* address, LIR_Opr result, ++ CodeEmitInfo* info) { ++ // 8179954: We need to make sure that the code generated for ++ // volatile accesses forms a sequentially-consistent set of ++ // operations when combined with STLR and LDAR. Without a leading ++ // membar it's possible for a simple Dekker test to fail if loads ++ // use LD;DMB but stores use STLR. This can happen if C2 compiles ++ // the stores in one method and C1 compiles the loads in another. ++ __ membar(); ++ __ volatile_load_mem_reg(address, result, info); ++} ++ ++void LIRGenerator::get_Object_unsafe(LIR_Opr dst, LIR_Opr src, LIR_Opr offset, ++ BasicType type, bool is_volatile) { ++ LIR_Address* addr = new LIR_Address(src, offset, type); ++ __ load(addr, dst); ++} ++ ++void LIRGenerator::put_Object_unsafe(LIR_Opr src, LIR_Opr offset, LIR_Opr data, ++ BasicType type, bool is_volatile) { ++ LIR_Address* addr = new LIR_Address(src, offset, type); ++ bool is_obj = (type == T_ARRAY || type == T_OBJECT); ++ if (is_obj) { ++ // Do the pre-write barrier, if any. ++ pre_barrier(LIR_OprFact::address(addr), LIR_OprFact::illegalOpr /* pre_val */, ++ true /* do_load */, false /* patch */, NULL); ++ __ move(data, addr); ++ assert(src->is_register(), "must be register"); ++ // Seems to be a precise address ++ post_barrier(LIR_OprFact::address(addr), data); ++ } else { ++ __ move(data, addr); ++ } ++} ++ ++void LIRGenerator::do_UnsafeGetAndSetObject(UnsafeGetAndSetObject* x) { ++ BasicType type = x->basic_type(); ++ LIRItem src(x->object(), this); ++ LIRItem off(x->offset(), this); ++ LIRItem value(x->value(), this); ++ ++ src.load_item(); ++ off.load_nonconstant(); ++ ++ // We can cope with a constant increment in an xadd ++ if (! (x->is_add() ++ && value.is_constant() ++ && can_inline_as_constant(x->value()))) { ++ value.load_item(); ++ } ++ ++ LIR_Opr dst = rlock_result(x, type); ++ LIR_Opr data = value.result(); ++ bool is_obj = (type == T_ARRAY || type == T_OBJECT); ++ LIR_Opr offset = off.result(); ++ ++ if (data == dst) { ++ LIR_Opr tmp = new_register(data->type()); ++ __ move(data, tmp); ++ data = tmp; ++ } ++ ++ LIR_Address* addr; ++ if (offset->is_constant()) { ++ jlong l = offset->as_jlong(); ++ assert((jlong)((jint)l) == l, "offset too large for constant"); ++ jint c = (jint)l; ++ addr = new LIR_Address(src.result(), c, type); ++ } else { ++ addr = new LIR_Address(src.result(), offset, type); ++ } ++ ++ LIR_Opr tmp = new_register(T_INT); ++ LIR_Opr ptr = LIR_OprFact::illegalOpr; ++ ++ if (x->is_add()) { ++ __ xadd(LIR_OprFact::address(addr), data, dst, tmp); ++ } else { ++ if (is_obj) { ++ // Do the pre-write barrier, if any. ++ ptr = new_pointer_register(); ++ __ add(src.result(), off.result(), ptr); ++ pre_barrier(ptr, LIR_OprFact::illegalOpr /* pre_val */, ++ true /* do_load */, false /* patch */, NULL); ++ } ++ __ xchg(LIR_OprFact::address(addr), data, dst, tmp); ++ if (is_obj) { ++ post_barrier(ptr, data); ++ } ++ } ++} +diff --git a/hotspot/src/cpu/loongarch/vm/c1_LinearScan_loongarch.hpp b/hotspot/src/cpu/loongarch/vm/c1_LinearScan_loongarch.hpp +new file mode 100644 +index 0000000000..f15dacafeb +--- /dev/null ++++ b/hotspot/src/cpu/loongarch/vm/c1_LinearScan_loongarch.hpp +@@ -0,0 +1,70 @@ ++/* ++ * Copyright (c) 2005, 2021, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2021, Loongson Technology. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ * ++ */ ++ ++#ifndef CPU_LOONGARCH_C1_LINEARSCAN_LOONGARCH_HPP ++#define CPU_LOONGARCH_C1_LINEARSCAN_LOONGARCH_HPP ++ ++inline bool LinearScan::is_processed_reg_num(int reg_num) { ++ return reg_num <= FrameMap::last_cpu_reg() || reg_num >= pd_nof_cpu_regs_frame_map; ++} ++ ++inline int LinearScan::num_physical_regs(BasicType type) { ++ return 1; ++} ++ ++inline bool LinearScan::requires_adjacent_regs(BasicType type) { ++ return false; ++} ++ ++inline bool LinearScan::is_caller_save(int assigned_reg) { ++ assert(assigned_reg >= 0 && assigned_reg < nof_regs, "should call this only for registers"); ++ if (assigned_reg < pd_first_callee_saved_reg) ++ return true; ++ if (assigned_reg > pd_last_callee_saved_reg && assigned_reg < pd_first_callee_saved_fpu_reg) ++ return true; ++ if (assigned_reg > pd_last_callee_saved_fpu_reg && assigned_reg < pd_last_fpu_reg) ++ return true; ++ return false; ++} ++ ++inline void LinearScan::pd_add_temps(LIR_Op* op) {} ++ ++// Implementation of LinearScanWalker ++inline bool LinearScanWalker::pd_init_regs_for_alloc(Interval* cur) { ++ if (allocator()->gen()->is_vreg_flag_set(cur->reg_num(), LIRGenerator::callee_saved)) { ++ assert(cur->type() != T_FLOAT && cur->type() != T_DOUBLE, "cpu regs only"); ++ _first_reg = pd_first_callee_saved_reg; ++ _last_reg = pd_last_callee_saved_reg; ++ return true; ++ } else if (cur->type() == T_INT || cur->type() == T_LONG || cur->type() == T_OBJECT || ++ cur->type() == T_ADDRESS || cur->type() == T_METADATA) { ++ _first_reg = pd_first_cpu_reg; ++ _last_reg = pd_last_allocatable_cpu_reg; ++ return true; ++ } ++ return false; ++} ++ ++#endif // CPU_LOONGARCH_C1_LINEARSCAN_LOONGARCH_HPP +diff --git a/hotspot/src/cpu/loongarch/vm/c1_LinearScan_loongarch_64.cpp b/hotspot/src/cpu/loongarch/vm/c1_LinearScan_loongarch_64.cpp +new file mode 100644 +index 0000000000..219b2e3671 +--- /dev/null ++++ b/hotspot/src/cpu/loongarch/vm/c1_LinearScan_loongarch_64.cpp +@@ -0,0 +1,33 @@ ++/* ++ * Copyright (c) 2005, 2011, Oracle and/or its affiliates. All rights reserved. All rights reserved. ++ * Copyright (c) 2021, Loongson Technology. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ * ++ */ ++ ++#include "precompiled.hpp" ++#include "c1/c1_Instruction.hpp" ++#include "c1/c1_LinearScan.hpp" ++#include "utilities/bitMap.inline.hpp" ++ ++void LinearScan::allocate_fpu_stack() { ++ // No FPU stack on LoongArch64 ++} +diff --git a/hotspot/src/cpu/loongarch/vm/c1_MacroAssembler_loongarch.hpp b/hotspot/src/cpu/loongarch/vm/c1_MacroAssembler_loongarch.hpp +new file mode 100644 +index 0000000000..38ff4c5836 +--- /dev/null ++++ b/hotspot/src/cpu/loongarch/vm/c1_MacroAssembler_loongarch.hpp +@@ -0,0 +1,112 @@ ++/* ++ * Copyright (c) 1999, 2021, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2021, Loongson Technology. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ * ++ */ ++ ++#ifndef CPU_LOONGARCH_C1_MACROASSEMBLER_LOONGARCH_HPP ++#define CPU_LOONGARCH_C1_MACROASSEMBLER_LOONGARCH_HPP ++ ++using MacroAssembler::build_frame; ++using MacroAssembler::null_check; ++ ++// C1_MacroAssembler contains high-level macros for C1 ++ ++ private: ++ int _rsp_offset; // track rsp changes ++ // initialization ++ void pd_init() { _rsp_offset = 0; } ++ ++ public: ++ void try_allocate( ++ Register obj, // result: pointer to object after successful allocation ++ Register var_size_in_bytes, // object size in bytes if unknown at compile time; invalid otherwise ++ int con_size_in_bytes, // object size in bytes if known at compile time ++ Register t1, // temp register ++ Register t2, // temp register ++ Label& slow_case // continuation point if fast allocation fails ++ ); ++ ++ void initialize_header(Register obj, Register klass, Register len, Register t1, Register t2); ++ void initialize_body(Register obj, Register len_in_bytes, int hdr_size_in_bytes, Register t1, Register t2); ++ ++ // locking ++ // hdr : must be A0, contents destroyed ++ // obj : must point to the object to lock, contents preserved ++ // disp_hdr: must point to the displaced header location, contents preserved ++ // scratch : scratch register, contents destroyed ++ // returns code offset at which to add null check debug information ++ int lock_object (Register swap, Register obj, Register disp_hdr, Register scratch, Label& slow_case); ++ ++ // unlocking ++ // hdr : contents destroyed ++ // obj : must point to the object to lock, contents preserved ++ // disp_hdr: must be A0 & must point to the displaced header location, contents destroyed ++ void unlock_object(Register swap, Register obj, Register lock, Label& slow_case); ++ ++ void initialize_object( ++ Register obj, // result: pointer to object after successful allocation ++ Register klass, // object klass ++ Register var_size_in_bytes, // object size in bytes if unknown at compile time; invalid otherwise ++ int con_size_in_bytes, // object size in bytes if known at compile time ++ Register t1, // temp register ++ Register t2, // temp register ++ bool is_tlab_allocated // the object was allocated in a TLAB; relevant for the implementation of ZeroTLAB ++ ); ++ ++ // allocation of fixed-size objects ++ // (can also be used to allocate fixed-size arrays, by setting ++ // hdr_size correctly and storing the array length afterwards) ++ // obj : will contain pointer to allocated object ++ // t1, t2 : scratch registers - contents destroyed ++ // header_size: size of object header in words ++ // object_size: total size of object in words ++ // slow_case : exit to slow case implementation if fast allocation fails ++ void allocate_object(Register obj, Register t1, Register t2, int header_size, ++ int object_size, Register klass, Label& slow_case); ++ ++ enum { ++ max_array_allocation_length = 0x00FFFFFF ++ }; ++ ++ // allocation of arrays ++ // obj : will contain pointer to allocated object ++ // len : array length in number of elements ++ // t : scratch register - contents destroyed ++ // header_size: size of object header in words ++ // f : element scale factor ++ // slow_case : exit to slow case implementation if fast allocation fails ++ void allocate_array(Register obj, Register len, Register t, Register t2, int header_size, ++ int f, Register klass, Label& slow_case); ++ ++ int rsp_offset() const { return _rsp_offset; } ++ void set_rsp_offset(int n) { _rsp_offset = n; } ++ ++ void invalidate_registers(bool inv_a0, bool inv_s0, bool inv_a2, bool inv_a3, ++ bool inv_a4, bool inv_a5) PRODUCT_RETURN; ++ ++ // This platform only uses signal-based null checks. The Label is not needed. ++ void null_check(Register r, Label *Lnull = NULL) { MacroAssembler::null_check(r); } ++ ++ void load_parameter(int offset_in_words, Register reg); ++ ++#endif // CPU_LOONGARCH_C1_MACROASSEMBLER_LOONGARCH_HPP +diff --git a/hotspot/src/cpu/loongarch/vm/c1_MacroAssembler_loongarch_64.cpp b/hotspot/src/cpu/loongarch/vm/c1_MacroAssembler_loongarch_64.cpp +new file mode 100644 +index 0000000000..51befaed6c +--- /dev/null ++++ b/hotspot/src/cpu/loongarch/vm/c1_MacroAssembler_loongarch_64.cpp +@@ -0,0 +1,346 @@ ++/* ++ * Copyright (c) 1999, 2021, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2021, Loongson Technology. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ * ++ */ ++ ++#include "precompiled.hpp" ++#include "c1/c1_MacroAssembler.hpp" ++#include "c1/c1_Runtime1.hpp" ++#include "interpreter/interpreter.hpp" ++#include "oops/arrayOop.hpp" ++#include "runtime/basicLock.hpp" ++#include "runtime/os.hpp" ++#include "runtime/sharedRuntime.hpp" ++#include "runtime/stubRoutines.hpp" ++ ++#define A0 RA0 ++#define A1 RA1 ++#define A2 RA2 ++#define A3 RA3 ++#define A4 RA4 ++#define A5 RA5 ++#define A6 RA6 ++#define A7 RA7 ++#define T0 RT0 ++#define T1 RT1 ++#define T4 RT4 ++ ++int C1_MacroAssembler::lock_object(Register hdr, Register obj, Register disp_hdr, Register scratch, Label& slow_case) { ++ const int aligned_mask = BytesPerWord -1; ++ const int hdr_offset = oopDesc::mark_offset_in_bytes(); ++ assert(hdr != obj && hdr != disp_hdr && obj != disp_hdr, "registers must be different"); ++ int null_check_offset = -1; ++ Label done; ++ ++ verify_oop(obj); ++ ++ // save object being locked into the BasicObjectLock ++ st_ptr(obj, Address(disp_hdr, BasicObjectLock::obj_offset_in_bytes())); ++ ++ if (UseBiasedLocking) { ++ assert(scratch != noreg, "should have scratch register at this point"); ++ null_check_offset = biased_locking_enter(disp_hdr, obj, hdr, scratch, false, done, &slow_case); ++ } else { ++ null_check_offset = offset(); ++ } ++ ++ // Load object header ++ ld_ptr(hdr, Address(obj, hdr_offset)); ++ // and mark it as unlocked ++ ori(hdr, hdr, markOopDesc::unlocked_value); ++ // save unlocked object header into the displaced header location on the stack ++ st_ptr(hdr, Address(disp_hdr, 0)); ++ // test if object header is still the same (i.e. unlocked), and if so, store the ++ // displaced header address in the object header - if it is not the same, get the ++ // object header instead ++ lea(SCR2, Address(obj, hdr_offset)); ++ cmpxchg(Address(SCR2, 0), hdr, disp_hdr, SCR1, true, false, done); ++ // if the object header was the same, we're done ++ // if the object header was not the same, it is now in the hdr register ++ // => test if it is a stack pointer into the same stack (recursive locking), i.e.: ++ // ++ // 1) (hdr & aligned_mask) == 0 ++ // 2) sp <= hdr ++ // 3) hdr <= sp + page_size ++ // ++ // these 3 tests can be done by evaluating the following expression: ++ // ++ // (hdr - sp) & (aligned_mask - page_size) ++ // ++ // assuming both the stack pointer and page_size have their least ++ // significant 2 bits cleared and page_size is a power of 2 ++ sub_d(hdr, hdr, SP); ++ li(SCR1, aligned_mask - os::vm_page_size()); ++ andr(hdr, hdr, SCR1); ++ // for recursive locking, the result is zero => save it in the displaced header ++ // location (NULL in the displaced hdr location indicates recursive locking) ++ st_ptr(hdr, Address(disp_hdr, 0)); ++ // otherwise we don't care about the result and handle locking via runtime call ++ bnez(hdr, slow_case); ++ // done ++ bind(done); ++ return null_check_offset; ++} ++ ++void C1_MacroAssembler::unlock_object(Register hdr, Register obj, Register disp_hdr, Label& slow_case) { ++ const int aligned_mask = BytesPerWord -1; ++ const int hdr_offset = oopDesc::mark_offset_in_bytes(); ++ assert(hdr != obj && hdr != disp_hdr && obj != disp_hdr, "registers must be different"); ++ Label done; ++ ++ if (UseBiasedLocking) { ++ // load object ++ ld_ptr(obj, Address(disp_hdr, BasicObjectLock::obj_offset_in_bytes())); ++ biased_locking_exit(obj, hdr, done); ++ } ++ ++ // load displaced header ++ ld_ptr(hdr, Address(disp_hdr, 0)); ++ // if the loaded hdr is NULL we had recursive locking ++ // if we had recursive locking, we are done ++ beqz(hdr, done); ++ if (!UseBiasedLocking) { ++ // load object ++ ld_ptr(obj, Address(disp_hdr, BasicObjectLock::obj_offset_in_bytes())); ++ } ++ verify_oop(obj); ++ // test if object header is pointing to the displaced header, and if so, restore ++ // the displaced header in the object - if the object header is not pointing to ++ // the displaced header, get the object header instead ++ // if the object header was not pointing to the displaced header, ++ // we do unlocking via runtime call ++ if (hdr_offset) { ++ lea(SCR1, Address(obj, hdr_offset)); ++ cmpxchg(Address(SCR1, 0), disp_hdr, hdr, SCR2, false, false, done, &slow_case); ++ } else { ++ cmpxchg(Address(obj, 0), disp_hdr, hdr, SCR2, false, false, done, &slow_case); ++ } ++ // done ++ bind(done); ++} ++ ++// Defines obj, preserves var_size_in_bytes ++void C1_MacroAssembler::try_allocate(Register obj, Register var_size_in_bytes, ++ int con_size_in_bytes, Register t1, Register t2, ++ Label& slow_case) { ++ if (UseTLAB) { ++ tlab_allocate(obj, var_size_in_bytes, con_size_in_bytes, t1, t2, slow_case); ++ } else { ++ eden_allocate(obj, var_size_in_bytes, con_size_in_bytes, t1, slow_case); ++ } ++} ++ ++void C1_MacroAssembler::initialize_header(Register obj, Register klass, Register len, ++ Register t1, Register t2) { ++ assert_different_registers(obj, klass, len); ++ if (UseBiasedLocking && !len->is_valid()) { ++ assert_different_registers(obj, klass, len, t1, t2); ++ ld_ptr(t1, Address(klass, Klass::prototype_header_offset())); ++ } else { ++ // This assumes that all prototype bits fit in an int32_t ++ li(t1, (int32_t)(intptr_t)markOopDesc::prototype()); ++ } ++ st_ptr(t1, Address(obj, oopDesc::mark_offset_in_bytes())); ++ ++ if (UseCompressedClassPointers) { // Take care not to kill klass ++ encode_klass_not_null(t1, klass); ++ st_w(t1, Address(obj, oopDesc::klass_offset_in_bytes())); ++ } else { ++ st_ptr(klass, Address(obj, oopDesc::klass_offset_in_bytes())); ++ } ++ ++ if (len->is_valid()) { ++ st_w(len, Address(obj, arrayOopDesc::length_offset_in_bytes())); ++ } else if (UseCompressedClassPointers) { ++ store_klass_gap(obj, R0); ++ } ++} ++ ++// preserves obj, destroys len_in_bytes ++// ++// Scratch registers: t1 = T0, t2 = T1 ++// ++void C1_MacroAssembler::initialize_body(Register obj, Register len_in_bytes, ++ int hdr_size_in_bytes, Register t1, Register t2) { ++ assert(hdr_size_in_bytes >= 0, "header size must be positive or 0"); ++ assert(t1 == T0 && t2 == T1, "must be"); ++ Label done; ++ ++ // len_in_bytes is positive and ptr sized ++ addi_d(len_in_bytes, len_in_bytes, -hdr_size_in_bytes); ++ beqz(len_in_bytes, done); ++ ++ // zero_words() takes ptr in t1 and count in bytes in t2 ++ lea(t1, Address(obj, hdr_size_in_bytes)); ++ addi_d(t2, len_in_bytes, -BytesPerWord); ++ ++ Label loop; ++ bind(loop); ++ stx_d(R0, t1, t2); ++ addi_d(t2, t2, -BytesPerWord); ++ bge(t2, R0, loop); ++ ++ bind(done); ++} ++ ++void C1_MacroAssembler::allocate_object(Register obj, Register t1, Register t2, int header_size, ++ int object_size, Register klass, Label& slow_case) { ++ assert_different_registers(obj, t1, t2); ++ assert(header_size >= 0 && object_size >= header_size, "illegal sizes"); ++ ++ try_allocate(obj, noreg, object_size * BytesPerWord, t1, t2, slow_case); ++ ++ initialize_object(obj, klass, noreg, object_size * HeapWordSize, t1, t2, UseTLAB); ++} ++ ++// Scratch registers: t1 = T0, t2 = T1 ++void C1_MacroAssembler::initialize_object(Register obj, Register klass, Register var_size_in_bytes, ++ int con_size_in_bytes, Register t1, Register t2, ++ bool is_tlab_allocated) { ++ assert((con_size_in_bytes & MinObjAlignmentInBytesMask) == 0, ++ "con_size_in_bytes is not multiple of alignment"); ++ const int hdr_size_in_bytes = instanceOopDesc::header_size() * HeapWordSize; ++ ++ initialize_header(obj, klass, noreg, t1, t2); ++ ++ if (!(UseTLAB && ZeroTLAB && is_tlab_allocated)) { ++ // clear rest of allocated space ++ const Register index = t2; ++ if (var_size_in_bytes != noreg) { ++ move(index, var_size_in_bytes); ++ initialize_body(obj, index, hdr_size_in_bytes, t1, t2); ++ } else if (con_size_in_bytes > hdr_size_in_bytes) { ++ con_size_in_bytes -= hdr_size_in_bytes; ++ lea(t1, Address(obj, hdr_size_in_bytes)); ++ Label loop; ++ li(SCR1, con_size_in_bytes - BytesPerWord); ++ bind(loop); ++ stx_d(R0, t1, SCR1); ++ addi_d(SCR1, SCR1, -BytesPerWord); ++ bge(SCR1, R0, loop); ++ } ++ } ++ ++ dbar(0); ++ ++ if (CURRENT_ENV->dtrace_alloc_probes()) { ++ assert(obj == A0, "must be"); ++ call(Runtime1::entry_for(Runtime1::dtrace_object_alloc_id), relocInfo::runtime_call_type); ++ } ++ ++ verify_oop(obj); ++} ++ ++void C1_MacroAssembler::allocate_array(Register obj, Register len, Register t1, Register t2, ++ int header_size, int f, Register klass, Label& slow_case) { ++ assert_different_registers(obj, len, t1, t2, klass); ++ ++ // determine alignment mask ++ assert(!(BytesPerWord & 1), "must be a multiple of 2 for masking code to work"); ++ ++ // check for negative or excessive length ++ li(SCR1, (int32_t)max_array_allocation_length); ++ bge_far(len, SCR1, slow_case, false); ++ ++ const Register arr_size = t2; // okay to be the same ++ // align object end ++ li(arr_size, (int32_t)header_size * BytesPerWord + MinObjAlignmentInBytesMask); ++ slli_w(SCR1, len, f); ++ add_d(arr_size, arr_size, SCR1); ++ bstrins_d(arr_size, R0, exact_log2(MinObjAlignmentInBytesMask + 1) - 1, 0); ++ ++ try_allocate(obj, arr_size, 0, t1, t2, slow_case); ++ ++ initialize_header(obj, klass, len, t1, t2); ++ ++ // clear rest of allocated space ++ initialize_body(obj, arr_size, header_size * BytesPerWord, t1, t2); ++ ++ dbar(0); ++ ++ if (CURRENT_ENV->dtrace_alloc_probes()) { ++ assert(obj == A0, "must be"); ++ call(Runtime1::entry_for(Runtime1::dtrace_object_alloc_id), relocInfo::runtime_call_type); ++ } ++ ++ verify_oop(obj); ++} ++ ++void C1_MacroAssembler::build_frame(int framesize, int bang_size_in_bytes) { ++ assert(bang_size_in_bytes >= framesize, "stack bang size incorrect"); ++ // Make sure there is enough stack space for this method's activation. ++ // Note that we do this before creating a frame. ++ generate_stack_overflow_check(bang_size_in_bytes); ++ MacroAssembler::build_frame(framesize); ++} ++ ++void C1_MacroAssembler::remove_frame(int framesize) { ++ MacroAssembler::remove_frame(framesize); ++} ++ ++void C1_MacroAssembler::verified_entry() { ++ // If we have to make this method not-entrant we'll overwrite its ++ // first instruction with a jump. For this action to be legal we ++ // must ensure that this first instruction is a b, bl, nop, break. ++ // Make it a NOP. ++ nop(); ++} ++ ++void C1_MacroAssembler::load_parameter(int offset_in_words, Register reg) { ++ // rbp, + 0: link ++ // + 1: return address ++ // + 2: argument with offset 0 ++ // + 3: argument with offset 1 ++ // + 4: ... ++ ++ ld_ptr(reg, Address(FP, (offset_in_words + 2) * BytesPerWord)); ++} ++ ++#ifndef PRODUCT ++void C1_MacroAssembler::verify_stack_oop(int stack_offset) { ++ if (!VerifyOops) return; ++ verify_oop_addr(Address(SP, stack_offset), "oop"); ++} ++ ++void C1_MacroAssembler::verify_not_null_oop(Register r) { ++ if (!VerifyOops) return; ++ Label not_null; ++ bnez(r, not_null); ++ stop("non-null oop required"); ++ bind(not_null); ++ verify_oop(r); ++} ++ ++void C1_MacroAssembler::invalidate_registers(bool inv_a0, bool inv_s0, bool inv_a2, ++ bool inv_a3, bool inv_a4, bool inv_a5) { ++#ifdef ASSERT ++ static int nn; ++ if (inv_a0) li(A0, 0xDEAD); ++ if (inv_s0) li(S0, 0xDEAD); ++ if (inv_a2) li(A2, nn++); ++ if (inv_a3) li(A3, 0xDEAD); ++ if (inv_a4) li(A4, 0xDEAD); ++ if (inv_a5) li(A5, 0xDEAD); ++#endif ++} ++#endif // ifndef PRODUCT +diff --git a/hotspot/src/cpu/loongarch/vm/c1_Runtime1_loongarch_64.cpp b/hotspot/src/cpu/loongarch/vm/c1_Runtime1_loongarch_64.cpp +new file mode 100644 +index 0000000000..96cf39cfa2 +--- /dev/null ++++ b/hotspot/src/cpu/loongarch/vm/c1_Runtime1_loongarch_64.cpp +@@ -0,0 +1,1249 @@ ++/* ++ * Copyright (c) 1999, 2021, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2021, 2022, Loongson Technology. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ * ++ */ ++ ++#include "precompiled.hpp" ++#include "asm/assembler.hpp" ++#include "c1/c1_CodeStubs.hpp" ++#include "c1/c1_Defs.hpp" ++#include "c1/c1_MacroAssembler.hpp" ++#include "c1/c1_Runtime1.hpp" ++#include "compiler/disassembler.hpp" ++#include "compiler/oopMap.hpp" ++#include "interpreter/interpreter.hpp" ++#include "memory/universe.hpp" ++#include "nativeInst_loongarch.hpp" ++#include "oops/compiledICHolder.hpp" ++#include "oops/oop.inline.hpp" ++#include "prims/jvmtiExport.hpp" ++#include "register_loongarch.hpp" ++#include "runtime/sharedRuntime.hpp" ++#include "runtime/signature.hpp" ++#include "runtime/stubRoutines.hpp" ++#include "runtime/vframe.hpp" ++#include "runtime/vframeArray.hpp" ++#include "vmreg_loongarch.inline.hpp" ++ ++#define A0 RA0 ++#define A1 RA1 ++#define A2 RA2 ++#define A3 RA3 ++#define A4 RA4 ++#define A5 RA5 ++#define A6 RA6 ++#define A7 RA7 ++#define T0 RT0 ++#define T1 RT1 ++#define T2 RT2 ++#define T3 RT3 ++#define T5 RT5 ++#define T6 RT6 ++#define T8 RT8 ++ ++// Implementation of StubAssembler ++ ++int StubAssembler::call_RT(Register oop_result1, Register metadata_result, address entry, int args_size) { ++ // setup registers ++ assert(!(oop_result1->is_valid() || metadata_result->is_valid()) || oop_result1 != metadata_result, ++ "registers must be different"); ++ assert(oop_result1 != TREG && metadata_result != TREG, "registers must be different"); ++ assert(args_size >= 0, "illegal args_size"); ++ bool align_stack = false; ++ ++ move(A0, TREG); ++ set_num_rt_args(0); // Nothing on stack ++ ++ Label retaddr; ++ set_last_Java_frame(SP, FP, retaddr); ++ ++ // do the call ++ call(entry, relocInfo::runtime_call_type); ++ bind(retaddr); ++ int call_offset = offset(); ++ // verify callee-saved register ++#ifdef ASSERT ++ { Label L; ++ get_thread(SCR1); ++ beq(TREG, SCR1, L); ++ stop("StubAssembler::call_RT: TREG not callee saved?"); ++ bind(L); ++ } ++#endif ++ reset_last_Java_frame(true); ++ ++ // check for pending exceptions ++ { Label L; ++ // check for pending exceptions (java_thread is set upon return) ++ ld_ptr(SCR1, Address(TREG, in_bytes(Thread::pending_exception_offset()))); ++ beqz(SCR1, L); ++ // exception pending => remove activation and forward to exception handler ++ // make sure that the vm_results are cleared ++ if (oop_result1->is_valid()) { ++ st_ptr(R0, Address(TREG, JavaThread::vm_result_offset())); ++ } ++ if (metadata_result->is_valid()) { ++ st_ptr(R0, Address(TREG, JavaThread::vm_result_2_offset())); ++ } ++ if (frame_size() == no_frame_size) { ++ leave(); ++ jmp(StubRoutines::forward_exception_entry(), relocInfo::runtime_call_type); ++ } else if (_stub_id == Runtime1::forward_exception_id) { ++ should_not_reach_here(); ++ } else { ++ jmp(Runtime1::entry_for(Runtime1::forward_exception_id), relocInfo::runtime_call_type); ++ } ++ bind(L); ++ } ++ // get oop results if there are any and reset the values in the thread ++ if (oop_result1->is_valid()) { ++ get_vm_result(oop_result1, TREG); ++ } ++ if (metadata_result->is_valid()) { ++ get_vm_result_2(metadata_result, TREG); ++ } ++ return call_offset; ++} ++ ++int StubAssembler::call_RT(Register oop_result1, Register metadata_result, ++ address entry, Register arg1) { ++ move(A1, arg1); ++ return call_RT(oop_result1, metadata_result, entry, 1); ++} ++ ++int StubAssembler::call_RT(Register oop_result1, Register metadata_result, ++ address entry, Register arg1, Register arg2) { ++ if (A1 == arg2) { ++ if (A2 == arg1) { ++ move(SCR1, arg1); ++ move(arg1, arg2); ++ move(arg2, SCR1); ++ } else { ++ move(A2, arg2); ++ move(A1, arg1); ++ } ++ } else { ++ move(A1, arg1); ++ move(A2, arg2); ++ } ++ return call_RT(oop_result1, metadata_result, entry, 2); ++} ++ ++int StubAssembler::call_RT(Register oop_result1, Register metadata_result, ++ address entry, Register arg1, Register arg2, Register arg3) { ++ // if there is any conflict use the stack ++ if (arg1 == A2 || arg1 == A3 || ++ arg2 == A1 || arg2 == A3 || ++ arg3 == A1 || arg3 == A2) { ++ addi_d(SP, SP, -4 * wordSize); ++ st_ptr(arg1, Address(SP, 0 * wordSize)); ++ st_ptr(arg2, Address(SP, 1 * wordSize)); ++ st_ptr(arg3, Address(SP, 2 * wordSize)); ++ ld_ptr(arg1, Address(SP, 0 * wordSize)); ++ ld_ptr(arg2, Address(SP, 1 * wordSize)); ++ ld_ptr(arg3, Address(SP, 2 * wordSize)); ++ addi_d(SP, SP, 4 * wordSize); ++ } else { ++ move(A1, arg1); ++ move(A2, arg2); ++ move(A3, arg3); ++ } ++ return call_RT(oop_result1, metadata_result, entry, 3); ++} ++ ++// Implementation of StubFrame ++ ++class StubFrame: public StackObj { ++ private: ++ StubAssembler* _sasm; ++ ++ public: ++ StubFrame(StubAssembler* sasm, const char* name, bool must_gc_arguments); ++ void load_argument(int offset_in_words, Register reg); ++ ++ ~StubFrame(); ++};; ++ ++#define __ _sasm-> ++ ++StubFrame::StubFrame(StubAssembler* sasm, const char* name, bool must_gc_arguments) { ++ _sasm = sasm; ++ __ set_info(name, must_gc_arguments); ++ __ enter(); ++} ++ ++// load parameters that were stored with LIR_Assembler::store_parameter ++// Note: offsets for store_parameter and load_argument must match ++void StubFrame::load_argument(int offset_in_words, Register reg) { ++ __ load_parameter(offset_in_words, reg); ++} ++ ++StubFrame::~StubFrame() { ++ __ leave(); ++ __ jr(RA); ++} ++ ++#undef __ ++ ++// Implementation of Runtime1 ++ ++#define __ sasm-> ++ ++const int float_regs_as_doubles_size_in_slots = pd_nof_fpu_regs_frame_map * 2; ++ ++// Stack layout for saving/restoring all the registers needed during a runtime ++// call (this includes deoptimization) ++// Note: note that users of this frame may well have arguments to some runtime ++// while these values are on the stack. These positions neglect those arguments ++// but the code in save_live_registers will take the argument count into ++// account. ++// ++ ++enum reg_save_layout { ++ reg_save_frame_size = 32 /* float */ + 30 /* integer, except zr, tp */ ++}; ++ ++// Save off registers which might be killed by calls into the runtime. ++// Tries to smart of about FP registers. In particular we separate ++// saving and describing the FPU registers for deoptimization since we ++// have to save the FPU registers twice if we describe them. The ++// deopt blob is the only thing which needs to describe FPU registers. ++// In all other cases it should be sufficient to simply save their ++// current value. ++ ++static int cpu_reg_save_offsets[FrameMap::nof_cpu_regs]; ++static int fpu_reg_save_offsets[FrameMap::nof_fpu_regs]; ++static int reg_save_size_in_words; ++static int frame_size_in_bytes = -1; ++ ++static OopMap* generate_oop_map(StubAssembler* sasm, bool save_fpu_registers) { ++ int frame_size_in_bytes = reg_save_frame_size * BytesPerWord; ++ sasm->set_frame_size(frame_size_in_bytes / BytesPerWord); ++ int frame_size_in_slots = frame_size_in_bytes / VMRegImpl::stack_slot_size; ++ OopMap* oop_map = new OopMap(frame_size_in_slots, 0); ++ ++ for (int i = A0->encoding(); i <= T8->encoding(); i++) { ++ Register r = as_Register(i); ++ if (i != SCR1->encoding() && i != SCR2->encoding()) { ++ int sp_offset = cpu_reg_save_offsets[i]; ++ oop_map->set_callee_saved(VMRegImpl::stack2reg(sp_offset), r->as_VMReg()); ++ } ++ } ++ ++ if (save_fpu_registers) { ++ for (int i = 0; i < FrameMap::nof_fpu_regs; i++) { ++ FloatRegister r = as_FloatRegister(i); ++ int sp_offset = fpu_reg_save_offsets[i]; ++ oop_map->set_callee_saved(VMRegImpl::stack2reg(sp_offset), r->as_VMReg()); ++ } ++ } ++ ++ return oop_map; ++} ++ ++static OopMap* save_live_registers(StubAssembler* sasm, ++ bool save_fpu_registers = true) { ++ __ block_comment("save_live_registers"); ++ ++ // integer registers except zr & ra & tp & sp ++ __ addi_d(SP, SP, -(32 - 4 + 32) * wordSize); ++ ++ for (int i = 4; i < 32; i++) ++ __ st_ptr(as_Register(i), Address(SP, (32 + i - 4) * wordSize)); ++ ++ if (save_fpu_registers) { ++ for (int i = 0; i < 32; i++) ++ __ fst_d(as_FloatRegister(i), Address(SP, i * wordSize)); ++ } ++ ++ return generate_oop_map(sasm, save_fpu_registers); ++} ++ ++static void restore_live_registers(StubAssembler* sasm, bool restore_fpu_registers = true) { ++ if (restore_fpu_registers) { ++ for (int i = 0; i < 32; i ++) ++ __ fld_d(as_FloatRegister(i), Address(SP, i * wordSize)); ++ } ++ ++ for (int i = 4; i < 32; i++) ++ __ ld_ptr(as_Register(i), Address(SP, (32 + i - 4) * wordSize)); ++ ++ __ addi_d(SP, SP, (32 - 4 + 32) * wordSize); ++} ++ ++static void restore_live_registers_except_a0(StubAssembler* sasm, bool restore_fpu_registers = true) { ++ if (restore_fpu_registers) { ++ for (int i = 0; i < 32; i ++) ++ __ fld_d(as_FloatRegister(i), Address(SP, i * wordSize)); ++ } ++ ++ for (int i = 5; i < 32; i++) ++ __ ld_ptr(as_Register(i), Address(SP, (32 + i - 4) * wordSize)); ++ ++ __ addi_d(SP, SP, (32 - 4 + 32) * wordSize); ++} ++ ++void Runtime1::initialize_pd() { ++ int sp_offset = 0; ++ int i; ++ ++ // all float registers are saved explicitly ++ assert(FrameMap::nof_fpu_regs == 32, "double registers not handled here"); ++ for (i = 0; i < FrameMap::nof_fpu_regs; i++) { ++ fpu_reg_save_offsets[i] = sp_offset; ++ sp_offset += 2; // SP offsets are in halfwords ++ } ++ ++ for (i = 4; i < FrameMap::nof_cpu_regs; i++) { ++ Register r = as_Register(i); ++ cpu_reg_save_offsets[i] = sp_offset; ++ sp_offset += 2; // SP offsets are in halfwords ++ } ++} ++ ++// target: the entry point of the method that creates and posts the exception oop ++// has_argument: true if the exception needs arguments (passed in SCR1 and SCR2) ++ ++OopMapSet* Runtime1::generate_exception_throw(StubAssembler* sasm, address target, ++ bool has_argument) { ++ // make a frame and preserve the caller's caller-save registers ++ OopMap* oop_map = save_live_registers(sasm); ++ int call_offset; ++ if (!has_argument) { ++ call_offset = __ call_RT(noreg, noreg, target); ++ } else { ++ __ move(A1, SCR1); ++ __ move(A2, SCR2); ++ call_offset = __ call_RT(noreg, noreg, target); ++ } ++ OopMapSet* oop_maps = new OopMapSet(); ++ oop_maps->add_gc_map(call_offset, oop_map); ++ return oop_maps; ++} ++ ++OopMapSet* Runtime1::generate_handle_exception(StubID id, StubAssembler *sasm) { ++ __ block_comment("generate_handle_exception"); ++ ++ // incoming parameters ++ const Register exception_oop = A0; ++ const Register exception_pc = A1; ++ // other registers used in this stub ++ ++ // Save registers, if required. ++ OopMapSet* oop_maps = new OopMapSet(); ++ OopMap* oop_map = NULL; ++ switch (id) { ++ case forward_exception_id: ++ // We're handling an exception in the context of a compiled frame. ++ // The registers have been saved in the standard places. Perform ++ // an exception lookup in the caller and dispatch to the handler ++ // if found. Otherwise unwind and dispatch to the callers ++ // exception handler. ++ oop_map = generate_oop_map(sasm, 1 /*thread*/); ++ ++ // load and clear pending exception oop into A0 ++ __ ld_ptr(exception_oop, Address(TREG, Thread::pending_exception_offset())); ++ __ st_ptr(R0, Address(TREG, Thread::pending_exception_offset())); ++ ++ // load issuing PC (the return address for this stub) into A1 ++ __ ld_ptr(exception_pc, Address(FP, 1 * BytesPerWord)); ++ ++ // make sure that the vm_results are cleared (may be unnecessary) ++ __ st_ptr(R0, Address(TREG, JavaThread::vm_result_offset())); ++ __ st_ptr(R0, Address(TREG, JavaThread::vm_result_2_offset())); ++ break; ++ case handle_exception_nofpu_id: ++ case handle_exception_id: ++ // At this point all registers MAY be live. ++ oop_map = save_live_registers(sasm, id != handle_exception_nofpu_id); ++ break; ++ case handle_exception_from_callee_id: { ++ // At this point all registers except exception oop (A0) and ++ // exception pc (RA) are dead. ++ const int frame_size = 2 /*fp, return address*/; ++ oop_map = new OopMap(frame_size * VMRegImpl::slots_per_word, 0); ++ sasm->set_frame_size(frame_size); ++ break; ++ } ++ default: ShouldNotReachHere(); ++ } ++ ++ // verify that only A0 and A1 are valid at this time ++ __ invalidate_registers(false, true, true, true, true, true); ++ // verify that A0 contains a valid exception ++ __ verify_not_null_oop(exception_oop); ++ ++#ifdef ASSERT ++ // check that fields in JavaThread for exception oop and issuing pc are ++ // empty before writing to them ++ Label oop_empty; ++ __ ld_ptr(SCR1, Address(TREG, JavaThread::exception_oop_offset())); ++ __ beqz(SCR1, oop_empty); ++ __ stop("exception oop already set"); ++ __ bind(oop_empty); ++ ++ Label pc_empty; ++ __ ld_ptr(SCR1, Address(TREG, JavaThread::exception_pc_offset())); ++ __ beqz(SCR1, pc_empty); ++ __ stop("exception pc already set"); ++ __ bind(pc_empty); ++#endif ++ ++ // save exception oop and issuing pc into JavaThread ++ // (exception handler will load it from here) ++ __ st_ptr(exception_oop, Address(TREG, JavaThread::exception_oop_offset())); ++ __ st_ptr(exception_pc, Address(TREG, JavaThread::exception_pc_offset())); ++ ++ // patch throwing pc into return address (has bci & oop map) ++ __ st_ptr(exception_pc, Address(FP, 1 * BytesPerWord)); ++ ++ // compute the exception handler. ++ // the exception oop and the throwing pc are read from the fields in JavaThread ++ int call_offset = __ call_RT(noreg, noreg, CAST_FROM_FN_PTR(address, exception_handler_for_pc)); ++ oop_maps->add_gc_map(call_offset, oop_map); ++ ++ // A0: handler address ++ // will be the deopt blob if nmethod was deoptimized while we looked up ++ // handler regardless of whether handler existed in the nmethod. ++ ++ // only A0 is valid at this time, all other registers have been destroyed by the runtime call ++ __ invalidate_registers(false, true, true, true, true, true); ++ ++ // patch the return address, this stub will directly return to the exception handler ++ __ st_ptr(A0, Address(FP, 1 * BytesPerWord)); ++ ++ switch (id) { ++ case forward_exception_id: ++ case handle_exception_nofpu_id: ++ case handle_exception_id: ++ // Restore the registers that were saved at the beginning. ++ restore_live_registers(sasm, id != handle_exception_nofpu_id); ++ break; ++ case handle_exception_from_callee_id: ++ break; ++ default: ShouldNotReachHere(); ++ } ++ ++ return oop_maps; ++} ++ ++void Runtime1::generate_unwind_exception(StubAssembler *sasm) { ++ // incoming parameters ++ const Register exception_oop = A0; ++ // callee-saved copy of exception_oop during runtime call ++ const Register exception_oop_callee_saved = S0; ++ // other registers used in this stub ++ const Register exception_pc = A1; ++ const Register handler_addr = A3; ++ ++ // verify that only A0, is valid at this time ++ __ invalidate_registers(false, true, true, true, true, true); ++ ++#ifdef ASSERT ++ // check that fields in JavaThread for exception oop and issuing pc are empty ++ Label oop_empty; ++ __ ld_ptr(SCR1, Address(TREG, JavaThread::exception_oop_offset())); ++ __ beqz(SCR1, oop_empty); ++ __ stop("exception oop must be empty"); ++ __ bind(oop_empty); ++ ++ Label pc_empty; ++ __ ld_ptr(SCR1, Address(TREG, JavaThread::exception_pc_offset())); ++ __ beqz(SCR1, pc_empty); ++ __ stop("exception pc must be empty"); ++ __ bind(pc_empty); ++#endif ++ ++ // Save our return address because ++ // exception_handler_for_return_address will destroy it. We also ++ // save exception_oop ++ __ addi_d(SP, SP, -2 * wordSize); ++ __ st_ptr(RA, Address(SP, 0 * wordSize)); ++ __ st_ptr(exception_oop, Address(SP, 1 * wordSize)); ++ ++ // search the exception handler address of the caller (using the return address) ++ __ call_VM_leaf(CAST_FROM_FN_PTR(address, SharedRuntime::exception_handler_for_return_address), TREG, RA); ++ // V0: exception handler address of the caller ++ ++ // Only V0 is valid at this time; all other registers have been ++ // destroyed by the call. ++ __ invalidate_registers(false, true, true, true, false, true); ++ ++ // move result of call into correct register ++ __ move(handler_addr, A0); ++ ++ // get throwing pc (= return address). ++ // RA has been destroyed by the call ++ __ ld_ptr(RA, Address(SP, 0 * wordSize)); ++ __ ld_ptr(exception_oop, Address(SP, 1 * wordSize)); ++ __ addi_d(SP, SP, 2 * wordSize); ++ __ move(A1, RA); ++ ++ __ verify_not_null_oop(exception_oop); ++ ++ // continue at exception handler (return address removed) ++ // note: do *not* remove arguments when unwinding the ++ // activation since the caller assumes having ++ // all arguments on the stack when entering the ++ // runtime to determine the exception handler ++ // (GC happens at call site with arguments!) ++ // A0: exception oop ++ // A1: throwing pc ++ // A3: exception handler ++ __ jr(handler_addr); ++} ++ ++OopMapSet* Runtime1::generate_patching(StubAssembler* sasm, address target) { ++ // use the maximum number of runtime-arguments here because it is difficult to ++ // distinguish each RT-Call. ++ // Note: This number affects also the RT-Call in generate_handle_exception because ++ // the oop-map is shared for all calls. ++ DeoptimizationBlob* deopt_blob = SharedRuntime::deopt_blob(); ++ assert(deopt_blob != NULL, "deoptimization blob must have been created"); ++ ++ OopMap* oop_map = save_live_registers(sasm); ++ ++ __ move(A0, TREG); ++ Label retaddr; ++ __ set_last_Java_frame(SP, FP, retaddr); ++ // do the call ++ __ call(target, relocInfo::runtime_call_type); ++ __ bind(retaddr); ++ OopMapSet* oop_maps = new OopMapSet(); ++ oop_maps->add_gc_map(__ offset(), oop_map); ++ // verify callee-saved register ++#ifdef ASSERT ++ { Label L; ++ __ get_thread(SCR1); ++ __ beq(TREG, SCR1, L); ++ __ stop("StubAssembler::call_RT: rthread not callee saved?"); ++ __ bind(L); ++ } ++#endif ++ ++ __ reset_last_Java_frame(true); ++ ++#ifdef ASSERT ++ // check that fields in JavaThread for exception oop and issuing pc are empty ++ Label oop_empty; ++ __ ld_ptr(SCR1, Address(TREG, Thread::pending_exception_offset())); ++ __ beqz(SCR1, oop_empty); ++ __ stop("exception oop must be empty"); ++ __ bind(oop_empty); ++ ++ Label pc_empty; ++ __ ld_ptr(SCR1, Address(TREG, JavaThread::exception_pc_offset())); ++ __ beqz(SCR1, pc_empty); ++ __ stop("exception pc must be empty"); ++ __ bind(pc_empty); ++#endif ++ ++ // Runtime will return true if the nmethod has been deoptimized, this is the ++ // expected scenario and anything else is an error. Note that we maintain a ++ // check on the result purely as a defensive measure. ++ Label no_deopt; ++ __ beqz(A0, no_deopt); // Have we deoptimized? ++ ++ // Perform a re-execute. The proper return address is already on the stack, ++ // we just need to restore registers, pop all of our frame but the return ++ // address and jump to the deopt blob. ++ restore_live_registers(sasm); ++ __ leave(); ++ __ jmp(deopt_blob->unpack_with_reexecution(), relocInfo::runtime_call_type); ++ ++ __ bind(no_deopt); ++ restore_live_registers(sasm); ++ __ leave(); ++ __ jr(RA); ++ ++ return oop_maps; ++} ++ ++OopMapSet* Runtime1::generate_code_for(StubID id, StubAssembler* sasm) { ++ // for better readability ++ const bool must_gc_arguments = true; ++ const bool dont_gc_arguments = false; ++ ++ // default value; overwritten for some optimized stubs that are called ++ // from methods that do not use the fpu ++ bool save_fpu_registers = true; ++ ++ // stub code & info for the different stubs ++ OopMapSet* oop_maps = NULL; ++ OopMap* oop_map = NULL; ++ switch (id) { ++ { ++ case forward_exception_id: ++ { ++ oop_maps = generate_handle_exception(id, sasm); ++ __ leave(); ++ __ jr(RA); ++ } ++ break; ++ ++ case throw_div0_exception_id: ++ { ++ StubFrame f(sasm, "throw_div0_exception", dont_gc_arguments); ++ oop_maps = generate_exception_throw(sasm, CAST_FROM_FN_PTR(address, throw_div0_exception), false); ++ } ++ break; ++ ++ case throw_null_pointer_exception_id: ++ { ++ StubFrame f(sasm, "throw_null_pointer_exception", dont_gc_arguments); ++ oop_maps = generate_exception_throw(sasm, CAST_FROM_FN_PTR(address, throw_null_pointer_exception), false); ++ } ++ break; ++ ++ case new_instance_id: ++ case fast_new_instance_id: ++ case fast_new_instance_init_check_id: ++ { ++ Register klass = A3; // Incoming ++ Register obj = A0; // Result ++ ++ if (id == new_instance_id) { ++ __ set_info("new_instance", dont_gc_arguments); ++ } else if (id == fast_new_instance_id) { ++ __ set_info("fast new_instance", dont_gc_arguments); ++ } else { ++ assert(id == fast_new_instance_init_check_id, "bad StubID"); ++ __ set_info("fast new_instance init check", dont_gc_arguments); ++ } ++ ++ // If TLAB is disabled, see if there is support for inlining contiguous ++ // allocations. ++ // Otherwise, just go to the slow path. ++ if ((id == fast_new_instance_id || id == fast_new_instance_init_check_id) && ++ !UseTLAB && Universe::heap()->supports_inline_contig_alloc()) { ++ Label slow_path; ++ Register obj_size = S0; ++ Register t1 = T0; ++ Register t2 = T1; ++ assert_different_registers(klass, obj, obj_size, t1, t2); ++ ++ __ addi_d(SP, SP, -2 * wordSize); ++ __ st_ptr(S0, Address(SP, 0)); ++ ++ if (id == fast_new_instance_init_check_id) { ++ // make sure the klass is initialized ++ __ ld_bu(SCR1, Address(klass, InstanceKlass::init_state_offset())); ++ __ li(SCR2, InstanceKlass::fully_initialized); ++ __ bne_far(SCR1, SCR2, slow_path); ++ } ++ ++#ifdef ASSERT ++ // assert object can be fast path allocated ++ { ++ Label ok, not_ok; ++ __ ld_w(obj_size, Address(klass, Klass::layout_helper_offset())); ++ __ bge(R0, obj_size, not_ok); // make sure it's an instance (LH > 0) ++ __ andi(SCR1, obj_size, Klass::_lh_instance_slow_path_bit); ++ __ beqz(SCR1, ok); ++ __ bind(not_ok); ++ __ stop("assert(can be fast path allocated)"); ++ __ should_not_reach_here(); ++ __ bind(ok); ++ } ++#endif // ASSERT ++ ++ // get the instance size (size is postive so movl is fine for 64bit) ++ __ ld_w(obj_size, Address(klass, Klass::layout_helper_offset())); ++ ++ __ eden_allocate(obj, obj_size, 0, t1, slow_path); ++ ++ __ initialize_object(obj, klass, obj_size, 0, t1, t2, /* is_tlab_allocated */ false); ++ __ verify_oop(obj); ++ __ ld_ptr(S0, Address(SP, 0)); ++ __ addi_d(SP, SP, 2 * wordSize); ++ __ jr(RA); ++ ++ __ bind(slow_path); ++ __ ld_ptr(S0, Address(SP, 0)); ++ __ addi_d(SP, SP, 2 * wordSize); ++ } ++ ++ __ enter(); ++ OopMap* map = save_live_registers(sasm); ++ int call_offset = __ call_RT(obj, noreg, CAST_FROM_FN_PTR(address, new_instance), klass); ++ oop_maps = new OopMapSet(); ++ oop_maps->add_gc_map(call_offset, map); ++ restore_live_registers_except_a0(sasm); ++ __ verify_oop(obj); ++ __ leave(); ++ __ jr(RA); ++ ++ // A0,: new instance ++ } ++ ++ break; ++ ++ case counter_overflow_id: ++ { ++ Register bci = A0, method = A1; ++ __ enter(); ++ OopMap* map = save_live_registers(sasm); ++ // Retrieve bci ++ __ ld_w(bci, Address(FP, 2 * BytesPerWord)); ++ // And a pointer to the Method* ++ __ ld_d(method, Address(FP, 3 * BytesPerWord)); ++ int call_offset = __ call_RT(noreg, noreg, CAST_FROM_FN_PTR(address, counter_overflow), bci, method); ++ oop_maps = new OopMapSet(); ++ oop_maps->add_gc_map(call_offset, map); ++ restore_live_registers(sasm); ++ __ leave(); ++ __ jr(RA); ++ } ++ break; ++ ++ case new_type_array_id: ++ case new_object_array_id: ++ { ++ Register length = S0; // Incoming ++ Register klass = A3; // Incoming ++ Register obj = A0; // Result ++ ++ if (id == new_type_array_id) { ++ __ set_info("new_type_array", dont_gc_arguments); ++ } else { ++ __ set_info("new_object_array", dont_gc_arguments); ++ } ++ ++#ifdef ASSERT ++ // assert object type is really an array of the proper kind ++ { ++ Label ok; ++ Register t0 = obj; ++ __ ld_w(t0, Address(klass, Klass::layout_helper_offset())); ++ __ srai_w(t0, t0, Klass::_lh_array_tag_shift); ++ int tag = ((id == new_type_array_id) ++ ? Klass::_lh_array_tag_type_value ++ : Klass::_lh_array_tag_obj_value); ++ __ li(SCR1, tag); ++ __ beq(t0, SCR1, ok); ++ __ stop("assert(is an array klass)"); ++ __ should_not_reach_here(); ++ __ bind(ok); ++ } ++#endif // ASSERT ++ ++ // If TLAB is disabled, see if there is support for inlining contiguous ++ // allocations. ++ // Otherwise, just go to the slow path. ++ if (!UseTLAB && Universe::heap()->supports_inline_contig_alloc()) { ++ Register arr_size = A5; ++ Register t1 = T0; ++ Register t2 = T1; ++ Label slow_path; ++ assert_different_registers(length, klass, obj, arr_size, t1, t2); ++ ++ // check that array length is small enough for fast path. ++ __ li(SCR1, C1_MacroAssembler::max_array_allocation_length); ++ __ blt_far(SCR1, length, slow_path, false); ++ ++ // get the allocation size: round_up(hdr + length << (layout_helper & 0x1F)) ++ // since size is positive ldrw does right thing on 64bit ++ __ ld_w(t1, Address(klass, Klass::layout_helper_offset())); ++ // since size is positive movw does right thing on 64bit ++ __ move(arr_size, length); ++ __ sll_w(arr_size, length, t1); ++ __ bstrpick_d(t1, t1, Klass::_lh_header_size_shift + ++ exact_log2(Klass::_lh_header_size_mask + 1) - 1, ++ Klass::_lh_header_size_shift); ++ __ add_d(arr_size, arr_size, t1); ++ __ addi_d(arr_size, arr_size, MinObjAlignmentInBytesMask); // align up ++ __ bstrins_d(arr_size, R0, exact_log2(MinObjAlignmentInBytesMask + 1) - 1, 0); ++ ++ __ eden_allocate(obj, arr_size, 0, t1, slow_path); // preserves arr_size ++ ++ __ initialize_header(obj, klass, length, t1, t2); ++ __ ld_bu(t1, Address(klass, in_bytes(Klass::layout_helper_offset()) + (Klass::_lh_header_size_shift / BitsPerByte))); ++ assert(Klass::_lh_header_size_shift % BitsPerByte == 0, "bytewise"); ++ assert(Klass::_lh_header_size_mask <= 0xFF, "bytewise"); ++ __ andi(t1, t1, Klass::_lh_header_size_mask); ++ __ sub_d(arr_size, arr_size, t1); // body length ++ __ add_d(t1, t1, obj); // body start ++ __ initialize_body(t1, arr_size, 0, t1, t2); ++ __ membar(Assembler::StoreStore); ++ __ verify_oop(obj); ++ ++ __ jr(RA); ++ ++ __ bind(slow_path); ++ } ++ ++ __ enter(); ++ OopMap* map = save_live_registers(sasm); ++ int call_offset; ++ if (id == new_type_array_id) { ++ call_offset = __ call_RT(obj, noreg, CAST_FROM_FN_PTR(address, new_type_array), klass, length); ++ } else { ++ call_offset = __ call_RT(obj, noreg, CAST_FROM_FN_PTR(address, new_object_array), klass, length); ++ } ++ ++ oop_maps = new OopMapSet(); ++ oop_maps->add_gc_map(call_offset, map); ++ restore_live_registers_except_a0(sasm); ++ ++ __ verify_oop(obj); ++ __ leave(); ++ __ jr(RA); ++ ++ // A0: new array ++ } ++ break; ++ ++ case new_multi_array_id: ++ { ++ StubFrame f(sasm, "new_multi_array", dont_gc_arguments); ++ // A0,: klass ++ // S0,: rank ++ // A2: address of 1st dimension ++ OopMap* map = save_live_registers(sasm); ++ __ move(A1, A0); ++ __ move(A3, A2); ++ __ move(A2, S0); ++ int call_offset = __ call_RT(A0, noreg, CAST_FROM_FN_PTR(address, new_multi_array), A1, A2, A3); ++ ++ oop_maps = new OopMapSet(); ++ oop_maps->add_gc_map(call_offset, map); ++ restore_live_registers_except_a0(sasm); ++ ++ // A0,: new multi array ++ __ verify_oop(A0); ++ } ++ break; ++ ++ case register_finalizer_id: ++ { ++ __ set_info("register_finalizer", dont_gc_arguments); ++ ++ // This is called via call_runtime so the arguments ++ // will be place in C abi locations ++ ++ __ verify_oop(A0); ++ ++ // load the klass and check the has finalizer flag ++ Label register_finalizer; ++ Register t = A5; ++ __ load_klass(t, A0); ++ __ ld_w(t, Address(t, Klass::access_flags_offset())); ++ __ li(SCR1, JVM_ACC_HAS_FINALIZER); ++ __ andr(SCR1, t, SCR1); ++ __ bnez(SCR1, register_finalizer); ++ __ jr(RA); ++ ++ __ bind(register_finalizer); ++ __ enter(); ++ OopMap* oop_map = save_live_registers(sasm); ++ int call_offset = __ call_RT(noreg, noreg, CAST_FROM_FN_PTR(address, SharedRuntime::register_finalizer), A0); ++ oop_maps = new OopMapSet(); ++ oop_maps->add_gc_map(call_offset, oop_map); ++ ++ // Now restore all the live registers ++ restore_live_registers(sasm); ++ ++ __ leave(); ++ __ jr(RA); ++ } ++ break; ++ ++ case throw_class_cast_exception_id: ++ { ++ StubFrame f(sasm, "throw_class_cast_exception", dont_gc_arguments); ++ oop_maps = generate_exception_throw(sasm, CAST_FROM_FN_PTR(address, throw_class_cast_exception), true); ++ } ++ break; ++ ++ case throw_incompatible_class_change_error_id: ++ { ++ StubFrame f(sasm, "throw_incompatible_class_cast_exception", dont_gc_arguments); ++ oop_maps = generate_exception_throw(sasm, CAST_FROM_FN_PTR(address, throw_incompatible_class_change_error), false); ++ } ++ break; ++ ++ case slow_subtype_check_id: ++ { ++ // Typical calling sequence: ++ // __ push(klass_RInfo); // object klass or other subclass ++ // __ push(sup_k_RInfo); // array element klass or other superclass ++ // __ bl(slow_subtype_check); ++ // Note that the subclass is pushed first, and is therefore deepest. ++ enum layout { ++ a0_off, a0_off_hi, ++ a2_off, a2_off_hi, ++ a4_off, a4_off_hi, ++ a5_off, a5_off_hi, ++ sup_k_off, sup_k_off_hi, ++ klass_off, klass_off_hi, ++ framesize, ++ result_off = sup_k_off ++ }; ++ ++ __ set_info("slow_subtype_check", dont_gc_arguments); ++ __ addi_d(SP, SP, -4 * wordSize); ++ __ st_ptr(A0, Address(SP, a0_off * VMRegImpl::stack_slot_size)); ++ __ st_ptr(A2, Address(SP, a2_off * VMRegImpl::stack_slot_size)); ++ __ st_ptr(A4, Address(SP, a4_off * VMRegImpl::stack_slot_size)); ++ __ st_ptr(A5, Address(SP, a5_off * VMRegImpl::stack_slot_size)); ++ ++ // This is called by pushing args and not with C abi ++ __ ld_ptr(A4, Address(SP, klass_off * VMRegImpl::stack_slot_size)); // subclass ++ __ ld_ptr(A0, Address(SP, sup_k_off * VMRegImpl::stack_slot_size)); // superclass ++ ++ Label miss; ++ __ check_klass_subtype_slow_path(A4, A0, A2, A5, NULL, &miss); ++ ++ // fallthrough on success: ++ __ li(SCR1, 1); ++ __ st_ptr(SCR1, Address(SP, result_off * VMRegImpl::stack_slot_size)); // result ++ __ ld_ptr(A0, Address(SP, a0_off * VMRegImpl::stack_slot_size)); ++ __ ld_ptr(A2, Address(SP, a2_off * VMRegImpl::stack_slot_size)); ++ __ ld_ptr(A4, Address(SP, a4_off * VMRegImpl::stack_slot_size)); ++ __ ld_ptr(A5, Address(SP, a5_off * VMRegImpl::stack_slot_size)); ++ __ addi_d(SP, SP, 4 * wordSize); ++ __ jr(RA); ++ ++ __ bind(miss); ++ __ st_ptr(R0, Address(SP, result_off * VMRegImpl::stack_slot_size)); // result ++ __ ld_ptr(A0, Address(SP, a0_off * VMRegImpl::stack_slot_size)); ++ __ ld_ptr(A2, Address(SP, a2_off * VMRegImpl::stack_slot_size)); ++ __ ld_ptr(A4, Address(SP, a4_off * VMRegImpl::stack_slot_size)); ++ __ ld_ptr(A5, Address(SP, a5_off * VMRegImpl::stack_slot_size)); ++ __ addi_d(SP, SP, 4 * wordSize); ++ __ jr(RA); ++ } ++ break; ++ ++ case monitorenter_nofpu_id: ++ save_fpu_registers = false; ++ // fall through ++ case monitorenter_id: ++ { ++ StubFrame f(sasm, "monitorenter", dont_gc_arguments); ++ OopMap* map = save_live_registers(sasm, save_fpu_registers); ++ ++ // Called with store_parameter and not C abi ++ ++ f.load_argument(1, A0); // A0,: object ++ f.load_argument(0, A1); // A1,: lock address ++ ++ int call_offset = __ call_RT(noreg, noreg, CAST_FROM_FN_PTR(address, monitorenter), A0, A1); ++ ++ oop_maps = new OopMapSet(); ++ oop_maps->add_gc_map(call_offset, map); ++ restore_live_registers(sasm, save_fpu_registers); ++ } ++ break; ++ ++ case monitorexit_nofpu_id: ++ save_fpu_registers = false; ++ // fall through ++ case monitorexit_id: ++ { ++ StubFrame f(sasm, "monitorexit", dont_gc_arguments); ++ OopMap* map = save_live_registers(sasm, save_fpu_registers); ++ ++ // Called with store_parameter and not C abi ++ ++ f.load_argument(0, A0); // A0,: lock address ++ ++ // note: really a leaf routine but must setup last java sp ++ // => use call_RT for now (speed can be improved by ++ // doing last java sp setup manually) ++ int call_offset = __ call_RT(noreg, noreg, CAST_FROM_FN_PTR(address, monitorexit), A0); ++ ++ oop_maps = new OopMapSet(); ++ oop_maps->add_gc_map(call_offset, map); ++ restore_live_registers(sasm, save_fpu_registers); ++ } ++ break; ++ ++ case deoptimize_id: ++ { ++ StubFrame f(sasm, "deoptimize", dont_gc_arguments); ++ OopMap* oop_map = save_live_registers(sasm); ++ f.load_argument(0, A1); ++ int call_offset = __ call_RT(noreg, noreg, CAST_FROM_FN_PTR(address, deoptimize), A1); ++ ++ oop_maps = new OopMapSet(); ++ oop_maps->add_gc_map(call_offset, oop_map); ++ restore_live_registers(sasm); ++ DeoptimizationBlob* deopt_blob = SharedRuntime::deopt_blob(); ++ assert(deopt_blob != NULL, "deoptimization blob must have been created"); ++ __ leave(); ++ __ jmp(deopt_blob->unpack_with_reexecution(), relocInfo::runtime_call_type); ++ } ++ break; ++ ++ case throw_range_check_failed_id: ++ { ++ StubFrame f(sasm, "range_check_failed", dont_gc_arguments); ++ oop_maps = generate_exception_throw(sasm, CAST_FROM_FN_PTR(address, throw_range_check_exception), true); ++ } ++ break; ++ ++ case unwind_exception_id: ++ { ++ __ set_info("unwind_exception", dont_gc_arguments); ++ // note: no stubframe since we are about to leave the current ++ // activation and we are calling a leaf VM function only. ++ generate_unwind_exception(sasm); ++ } ++ break; ++ ++ case access_field_patching_id: ++ { ++ StubFrame f(sasm, "access_field_patching", dont_gc_arguments); ++ // we should set up register map ++ oop_maps = generate_patching(sasm, CAST_FROM_FN_PTR(address, access_field_patching)); ++ } ++ break; ++ ++ case load_klass_patching_id: ++ { ++ StubFrame f(sasm, "load_klass_patching", dont_gc_arguments); ++ // we should set up register map ++ oop_maps = generate_patching(sasm, CAST_FROM_FN_PTR(address, move_klass_patching)); ++ } ++ break; ++ ++ case load_mirror_patching_id: ++ { ++ StubFrame f(sasm, "load_mirror_patching", dont_gc_arguments); ++ // we should set up register map ++ oop_maps = generate_patching(sasm, CAST_FROM_FN_PTR(address, move_mirror_patching)); ++ } ++ break; ++ ++ case load_appendix_patching_id: ++ { ++ StubFrame f(sasm, "load_appendix_patching", dont_gc_arguments); ++ // we should set up register map ++ oop_maps = generate_patching(sasm, CAST_FROM_FN_PTR(address, move_appendix_patching)); ++ } ++ break; ++ ++ case handle_exception_nofpu_id: ++ case handle_exception_id: ++ { ++ StubFrame f(sasm, "handle_exception", dont_gc_arguments); ++ oop_maps = generate_handle_exception(id, sasm); ++ } ++ break; ++ ++ case handle_exception_from_callee_id: ++ { ++ StubFrame f(sasm, "handle_exception_from_callee", dont_gc_arguments); ++ oop_maps = generate_handle_exception(id, sasm); ++ } ++ break; ++ ++ case throw_index_exception_id: ++ { ++ StubFrame f(sasm, "index_range_check_failed", dont_gc_arguments); ++ oop_maps = generate_exception_throw(sasm, CAST_FROM_FN_PTR(address, throw_index_exception), true); ++ } ++ break; ++ ++ case throw_array_store_exception_id: ++ { ++ StubFrame f(sasm, "throw_array_store_exception", dont_gc_arguments); ++ // tos + 0: link ++ // + 1: return address ++ oop_maps = generate_exception_throw(sasm, CAST_FROM_FN_PTR(address, throw_array_store_exception), true); ++ } ++ break; ++ ++#if INCLUDE_ALL_GCS ++ ++ case g1_pre_barrier_slow_id: ++ { ++ StubFrame f(sasm, "g1_pre_barrier", dont_gc_arguments); ++ // arg0 : previous value of memory ++ ++ BarrierSet* bs = Universe::heap()->barrier_set(); ++ if (bs->kind() != BarrierSet::G1SATBCTLogging) { ++ __ li(A0, (int)id); ++ __ call_RT(noreg, noreg, CAST_FROM_FN_PTR(address, unimplemented_entry), A0); ++ __ should_not_reach_here(); ++ break; ++ } ++ ++ const Register pre_val = A0; ++ const Register thread = TREG; ++ const Register tmp = SCR2; ++ ++ Address in_progress(thread, in_bytes(JavaThread::satb_mark_queue_offset() + ++ PtrQueue::byte_offset_of_active())); ++ ++ Address queue_index(thread, in_bytes(JavaThread::satb_mark_queue_offset() + ++ PtrQueue::byte_offset_of_index())); ++ Address buffer(thread, in_bytes(JavaThread::satb_mark_queue_offset() + ++ PtrQueue::byte_offset_of_buf())); ++ ++ Label done; ++ Label runtime; ++ ++ // Can we store original value in the thread's buffer? ++ __ ld_ptr(tmp, queue_index); ++ __ beqz(tmp, runtime); ++ ++ __ addi_d(tmp, tmp, -wordSize); ++ __ st_ptr(tmp, queue_index); ++ __ ld_ptr(SCR1, buffer); ++ __ add_d(tmp, tmp, SCR1); ++ f.load_argument(0, SCR1); ++ __ st_ptr(SCR1, Address(tmp, 0)); ++ __ b(done); ++ ++ __ bind(runtime); ++ __ pushad(); ++ f.load_argument(0, pre_val); ++ __ call_VM_leaf(CAST_FROM_FN_PTR(address, SharedRuntime::g1_wb_pre), pre_val, thread); ++ __ popad(); ++ __ bind(done); ++ } ++ break; ++ case g1_post_barrier_slow_id: ++ { ++ StubFrame f(sasm, "g1_post_barrier", dont_gc_arguments); ++ ++ // arg0: store_address ++ Address store_addr(FP, 2*BytesPerWord); ++ ++ BarrierSet* bs = Universe::heap()->barrier_set(); ++ CardTableModRefBS* ct = (CardTableModRefBS*)bs; ++ assert(sizeof(*ct->byte_map_base) == sizeof(jbyte), "adjust this code"); ++ ++ Label done; ++ Label runtime; ++ ++ // At this point we know new_value is non-NULL and the new_value crosses regions. ++ // Must check to see if card is already dirty ++ ++ const Register thread = TREG; ++ ++ Address queue_index(thread, in_bytes(JavaThread::dirty_card_queue_offset() + ++ PtrQueue::byte_offset_of_index())); ++ Address buffer(thread, in_bytes(JavaThread::dirty_card_queue_offset() + ++ PtrQueue::byte_offset_of_buf())); ++ ++ const Register card_offset = SCR2; ++ // RA is free here, so we can use it to hold the byte_map_base. ++ const Register byte_map_base = RA; ++ ++ assert_different_registers(card_offset, byte_map_base, SCR1); ++ ++ f.load_argument(0, card_offset); ++ __ srli_d(card_offset, card_offset, CardTableModRefBS::card_shift); ++ __ load_byte_map_base(byte_map_base); ++ __ ldx_bu(SCR1, byte_map_base, card_offset); ++ __ addi_d(SCR1, SCR1, -(int)G1SATBCardTableModRefBS::g1_young_card_val()); ++ __ beqz(SCR1, done); ++ ++ assert((int)CardTableModRefBS::dirty_card_val() == 0, "must be 0"); ++ ++ __ membar(Assembler::StoreLoad); ++ __ ldx_bu(SCR1, byte_map_base, card_offset); ++ __ beqz(SCR1, done); ++ ++ // storing region crossing non-NULL, card is clean. ++ // dirty card and log. ++ __ stx_b(R0, byte_map_base, card_offset); ++ ++ // Convert card offset into an address in card_addr ++ Register card_addr = card_offset; ++ __ add_d(card_addr, byte_map_base, card_addr); ++ ++ __ ld_ptr(SCR1, queue_index); ++ __ beqz(SCR1, runtime); ++ __ addi_d(SCR1, SCR1, -wordSize); ++ __ st_ptr(SCR1, queue_index); ++ ++ // Reuse RA to hold buffer_addr ++ const Register buffer_addr = RA; ++ ++ __ ld_ptr(buffer_addr, buffer); ++ __ stx_d(card_addr, buffer_addr, SCR1); ++ __ b(done); ++ ++ __ bind(runtime); ++ __ pushad(); ++ __ call_VM_leaf(CAST_FROM_FN_PTR(address, SharedRuntime::g1_wb_post), card_addr, thread); ++ __ popad(); ++ __ bind(done); ++ ++ } ++ break; ++#endif ++ ++ case predicate_failed_trap_id: ++ { ++ StubFrame f(sasm, "predicate_failed_trap", dont_gc_arguments); ++ ++ OopMap* map = save_live_registers(sasm); ++ ++ int call_offset = __ call_RT(noreg, noreg, CAST_FROM_FN_PTR(address, predicate_failed_trap)); ++ oop_maps = new OopMapSet(); ++ oop_maps->add_gc_map(call_offset, map); ++ restore_live_registers(sasm); ++ __ leave(); ++ DeoptimizationBlob* deopt_blob = SharedRuntime::deopt_blob(); ++ assert(deopt_blob != NULL, "deoptimization blob must have been created"); ++ ++ __ jmp(deopt_blob->unpack_with_reexecution(), relocInfo::runtime_call_type); ++ } ++ break; ++ ++ case dtrace_object_alloc_id: ++ { ++ // A0: object ++ StubFrame f(sasm, "dtrace_object_alloc", dont_gc_arguments); ++ save_live_registers(sasm); ++ ++ __ call_VM_leaf(CAST_FROM_FN_PTR(address, SharedRuntime::dtrace_object_alloc), A0); ++ ++ restore_live_registers(sasm); ++ } ++ break; ++ ++ default: ++ { ++ StubFrame f(sasm, "unimplemented entry", dont_gc_arguments); ++ __ li(A0, (int)id); ++ __ call_RT(noreg, noreg, CAST_FROM_FN_PTR(address, unimplemented_entry), A0); ++ __ should_not_reach_here(); ++ } ++ break; ++ } ++ } ++ return oop_maps; ++} ++ ++#undef __ ++ ++const char *Runtime1::pd_name_for_address(address entry) { ++ Unimplemented(); ++ return 0; ++} +diff --git a/hotspot/src/cpu/loongarch/vm/c1_globals_loongarch.hpp b/hotspot/src/cpu/loongarch/vm/c1_globals_loongarch.hpp +new file mode 100644 +index 0000000000..df052a058c +--- /dev/null ++++ b/hotspot/src/cpu/loongarch/vm/c1_globals_loongarch.hpp +@@ -0,0 +1,69 @@ ++/* ++ * Copyright (c) 2000, 2021, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2021, Loongson Technology. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ * ++ */ ++ ++#ifndef CPU_LOONGARCH_C1_GLOBALS_LOONGARCH_HPP ++#define CPU_LOONGARCH_C1_GLOBALS_LOONGARCH_HPP ++ ++#include "utilities/globalDefinitions.hpp" ++#include "utilities/macros.hpp" ++ ++// Sets the default values for platform dependent flags used by the client compiler. ++// (see c1_globals.hpp) ++ ++#ifndef COMPILER2 ++define_pd_global(bool, BackgroundCompilation, true ); ++define_pd_global(bool, InlineIntrinsics, true ); ++define_pd_global(bool, PreferInterpreterNativeStubs, false); ++define_pd_global(bool, ProfileTraps, false); ++define_pd_global(bool, UseOnStackReplacement, true ); ++define_pd_global(bool, TieredCompilation, false); ++define_pd_global(intx, CompileThreshold, 1500 ); ++ ++define_pd_global(intx, OnStackReplacePercentage, 933 ); ++define_pd_global(intx, NewSizeThreadIncrease, 4*K ); ++define_pd_global(intx, InitialCodeCacheSize, 160*K); ++define_pd_global(intx, ReservedCodeCacheSize, 32*M ); ++define_pd_global(intx, NonProfiledCodeHeapSize, 13*M ); ++define_pd_global(intx, ProfiledCodeHeapSize, 14*M ); ++define_pd_global(intx, NonNMethodCodeHeapSize, 5*M ); ++define_pd_global(bool, ProfileInterpreter, false); ++define_pd_global(intx, CodeCacheExpansionSize, 32*K ); ++define_pd_global(uintx, CodeCacheMinBlockLength, 1); ++define_pd_global(uintx, CodeCacheMinimumUseSpace, 400*K); ++define_pd_global(bool, NeverActAsServerClassMachine, true ); ++define_pd_global(uint64_t,MaxRAM, 1ULL*G); ++define_pd_global(bool, CICompileOSR, true ); ++#endif // !COMPILER2 ++define_pd_global(bool, UseTypeProfile, false); ++define_pd_global(bool, RoundFPResults, true ); ++ ++define_pd_global(bool, LIRFillDelaySlots, false); ++define_pd_global(bool, OptimizeSinglePrecision, true ); ++define_pd_global(bool, CSEArrayLength, false); ++define_pd_global(bool, TwoOperandLIRForm, false ); ++ ++define_pd_global(intx, SafepointPollOffset, 0 ); ++ ++#endif // CPU_LOONGARCH_C1_GLOBALS_LOONGARCH_HPP +diff --git a/hotspot/src/cpu/loongarch/vm/c2_globals_loongarch.hpp b/hotspot/src/cpu/loongarch/vm/c2_globals_loongarch.hpp +new file mode 100644 +index 0000000000..044b0d2536 +--- /dev/null ++++ b/hotspot/src/cpu/loongarch/vm/c2_globals_loongarch.hpp +@@ -0,0 +1,87 @@ ++/* ++ * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2015, 2021, Loongson Technology. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ * ++ */ ++ ++#ifndef CPU_LOONGARCH_VM_C2_GLOBALS_LOONGARCH_HPP ++#define CPU_LOONGARCH_VM_C2_GLOBALS_LOONGARCH_HPP ++ ++#include "utilities/globalDefinitions.hpp" ++#include "utilities/macros.hpp" ++ ++// Sets the default values for platform dependent flags used by the server compiler. ++// (see c2_globals.hpp). Alpha-sorted. ++define_pd_global(bool, BackgroundCompilation, true); ++define_pd_global(bool, UseTLAB, true); ++define_pd_global(bool, ResizeTLAB, true); ++define_pd_global(bool, CICompileOSR, true); ++define_pd_global(bool, InlineIntrinsics, true); ++define_pd_global(bool, PreferInterpreterNativeStubs, false); ++define_pd_global(bool, ProfileTraps, true); ++define_pd_global(bool, UseOnStackReplacement, true); ++#ifdef CC_INTERP ++define_pd_global(bool, ProfileInterpreter, false); ++#else ++define_pd_global(bool, ProfileInterpreter, true); ++#endif // CC_INTERP ++define_pd_global(bool, TieredCompilation, true); ++define_pd_global(intx, CompileThreshold, 10000); ++define_pd_global(intx, BackEdgeThreshold, 100000); ++ ++define_pd_global(intx, OnStackReplacePercentage, 140); ++define_pd_global(intx, ConditionalMoveLimit, 3); ++define_pd_global(intx, FLOATPRESSURE, 6); ++define_pd_global(intx, FreqInlineSize, 325); ++define_pd_global(intx, MinJumpTableSize, 10); ++define_pd_global(intx, INTPRESSURE, 13); ++define_pd_global(intx, InteriorEntryAlignment, 16); ++define_pd_global(intx, NewSizeThreadIncrease, ScaleForWordSize(4*K)); ++define_pd_global(intx, LoopUnrollLimit, 60); ++// InitialCodeCacheSize derived from specjbb2000 run. ++define_pd_global(intx, InitialCodeCacheSize, 2496*K); // Integral multiple of CodeCacheExpansionSize ++define_pd_global(intx, CodeCacheExpansionSize, 64*K); ++ ++// Ergonomics related flags ++define_pd_global(uint64_t,MaxRAM, 128ULL*G); ++define_pd_global(intx, RegisterCostAreaRatio, 16000); ++ ++// Peephole and CISC spilling both break the graph, and so makes the ++// scheduler sick. ++define_pd_global(bool, OptoPeephole, false); ++define_pd_global(bool, UseCISCSpill, false); ++define_pd_global(bool, OptoScheduling, false); ++define_pd_global(bool, OptoBundling, false); ++ ++define_pd_global(intx, ReservedCodeCacheSize, 48*M); ++define_pd_global(uintx, CodeCacheMinBlockLength, 4); ++define_pd_global(uintx, CodeCacheMinimumUseSpace, 400*K); ++ ++define_pd_global(bool, TrapBasedRangeChecks, false); ++ ++// Heap related flags ++define_pd_global(uintx,MetaspaceSize, ScaleForWordSize(16*M)); ++ ++// Ergonomics related flags ++define_pd_global(bool, NeverActAsServerClassMachine, false); ++ ++#endif // CPU_LOONGARCH_VM_C2_GLOBALS_LOONGARCH_HPP +diff --git a/hotspot/src/cpu/loongarch/vm/c2_init_loongarch.cpp b/hotspot/src/cpu/loongarch/vm/c2_init_loongarch.cpp +new file mode 100644 +index 0000000000..c7bf590b60 +--- /dev/null ++++ b/hotspot/src/cpu/loongarch/vm/c2_init_loongarch.cpp +@@ -0,0 +1,34 @@ ++/* ++ * Copyright (c) 2000, 2010, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2015, 2020, Loongson Technology. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ * ++ */ ++ ++#include "precompiled.hpp" ++#include "opto/compile.hpp" ++#include "opto/node.hpp" ++ ++// processor dependent initialization for LoongArch ++ ++void Compile::pd_compiler2_init() { ++ guarantee(CodeEntryAlignment >= InteriorEntryAlignment, "" ); ++} +diff --git a/hotspot/src/cpu/loongarch/vm/codeBuffer_loongarch.hpp b/hotspot/src/cpu/loongarch/vm/codeBuffer_loongarch.hpp +new file mode 100644 +index 0000000000..652f6c1092 +--- /dev/null ++++ b/hotspot/src/cpu/loongarch/vm/codeBuffer_loongarch.hpp +@@ -0,0 +1,35 @@ ++/* ++ * Copyright (c) 2002, 2010, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2015, 2020, Loongson Technology. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ * ++ */ ++ ++#ifndef CPU_LOONGARCH_VM_CODEBUFFER_LOONGARCH_HPP ++#define CPU_LOONGARCH_VM_CODEBUFFER_LOONGARCH_HPP ++ ++private: ++ void pd_initialize() {} ++ ++public: ++ void flush_bundle(bool start_new_bundle) {} ++ ++#endif // CPU_LOONGARCH_VM_CODEBUFFER_LOONGARCH_HPP +diff --git a/hotspot/src/cpu/loongarch/vm/compiledIC_loongarch.cpp b/hotspot/src/cpu/loongarch/vm/compiledIC_loongarch.cpp +new file mode 100644 +index 0000000000..70a47fc772 +--- /dev/null ++++ b/hotspot/src/cpu/loongarch/vm/compiledIC_loongarch.cpp +@@ -0,0 +1,167 @@ ++/* ++ * Copyright (c) 1997, 2014, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2015, 2021, Loongson Technology. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ * ++ */ ++ ++#include "precompiled.hpp" ++#include "asm/macroAssembler.inline.hpp" ++#include "code/compiledIC.hpp" ++#include "code/icBuffer.hpp" ++#include "code/nmethod.hpp" ++#include "memory/resourceArea.hpp" ++#include "runtime/mutexLocker.hpp" ++#include "runtime/safepoint.hpp" ++ ++// Release the CompiledICHolder* associated with this call site is there is one. ++void CompiledIC::cleanup_call_site(virtual_call_Relocation* call_site) { ++ // This call site might have become stale so inspect it carefully. ++ NativeCall* call = nativeCall_at(call_site->addr()); ++ if (is_icholder_entry(call->destination())) { ++ NativeMovConstReg* value = nativeMovConstReg_at(call_site->cached_value()); ++ InlineCacheBuffer::queue_for_release((CompiledICHolder*)value->data()); ++ } ++} ++ ++bool CompiledIC::is_icholder_call_site(virtual_call_Relocation* call_site) { ++ // This call site might have become stale so inspect it carefully. ++ NativeCall* call = nativeCall_at(call_site->addr()); ++ return is_icholder_entry(call->destination()); ++} ++ ++// ---------------------------------------------------------------------------- ++ ++#define __ _masm. ++address CompiledStaticCall::emit_to_interp_stub(CodeBuffer &cbuf) { ++ address mark = cbuf.insts_mark(); // get mark within main instrs section ++ ++ // Note that the code buffer's insts_mark is always relative to insts. ++ // That's why we must use the macroassembler to generate a stub. ++ MacroAssembler _masm(&cbuf); ++ ++ address base = __ start_a_stub(CompiledStaticCall::to_interp_stub_size()); ++ if (base == NULL) return NULL; // CodeBuffer::expand failed ++ ++ // static stub relocation stores the instruction address of the call ++ __ relocate(static_stub_Relocation::spec(mark), 0); ++ ++ // Code stream for loading method may be changed. ++ __ ibar(0); ++ ++ // Rmethod contains methodOop, it should be relocated for GC ++ // static stub relocation also tags the methodOop in the code-stream. ++ __ mov_metadata(Rmethod, NULL); ++ // This is recognized as unresolved by relocs/nativeInst/ic code ++ ++ cbuf.set_insts_mark(); ++ __ patchable_jump(__ pc()); ++ // Update current stubs pointer and restore code_end. ++ __ end_a_stub(); ++ return base; ++} ++#undef __ ++ ++int CompiledStaticCall::to_interp_stub_size() { ++ return NativeInstruction::nop_instruction_size + NativeMovConstReg::instruction_size + NativeGeneralJump::instruction_size; ++} ++ ++// Relocation entries for call stub, compiled java to interpreter. ++int CompiledStaticCall::reloc_to_interp_stub() { ++ return 16; ++} ++ ++void CompiledStaticCall::set_to_interpreted(methodHandle callee, address entry) { ++ address stub = find_stub(); ++ guarantee(stub != NULL, "stub not found"); ++ ++ if (TraceICs) { ++ ResourceMark rm; ++ tty->print_cr("CompiledStaticCall@" INTPTR_FORMAT ": set_to_interpreted %s", ++ p2i(instruction_address()), ++ callee->name_and_sig_as_C_string()); ++ } ++ ++ // Creation also verifies the object. ++ NativeMovConstReg* method_holder = nativeMovConstReg_at(stub + NativeInstruction::nop_instruction_size); ++#ifndef LOONGARCH64 ++ NativeJump* jump = nativeJump_at(method_holder->next_instruction_address()); ++#else ++ NativeGeneralJump* jump = nativeGeneralJump_at(method_holder->next_instruction_address()); ++#endif ++ ++ assert(method_holder->data() == 0 || method_holder->data() == (intptr_t)callee(), ++ "a) MT-unsafe modification of inline cache"); ++ assert(jump->jump_destination() == jump->instruction_address() || jump->jump_destination() == entry, ++ "b) MT-unsafe modification of inline cache"); ++ ++ // Update stub. ++ method_holder->set_data((intptr_t)callee()); ++ jump->set_jump_destination(entry); ++ ++ // Update jump to call. ++ set_destination_mt_safe(stub); ++} ++ ++void CompiledStaticCall::set_stub_to_clean(static_stub_Relocation* static_stub) { ++ assert (CompiledIC_lock->is_locked() || SafepointSynchronize::is_at_safepoint(), "mt unsafe call"); ++ // Reset stub. ++ address stub = static_stub->addr(); ++ assert(stub != NULL, "stub not found"); ++ // Creation also verifies the object. ++ NativeMovConstReg* method_holder = nativeMovConstReg_at(stub + NativeInstruction::nop_instruction_size); ++#ifndef LOONGARCH64 ++ NativeJump* jump = nativeJump_at(method_holder->next_instruction_address()); ++#else ++ NativeGeneralJump* jump = nativeGeneralJump_at(method_holder->next_instruction_address()); ++#endif ++ method_holder->set_data(0); ++ jump->set_jump_destination(jump->instruction_address()); ++} ++ ++//----------------------------------------------------------------------------- ++// Non-product mode code ++#ifndef PRODUCT ++ ++void CompiledStaticCall::verify() { ++ // Verify call. ++ NativeCall::verify(); ++ if (os::is_MP()) { ++ verify_alignment(); ++ } ++ ++ // Verify stub. ++ address stub = find_stub(); ++ assert(stub != NULL, "no stub found for static call"); ++ // Creation also verifies the object. ++ NativeMovConstReg* method_holder = nativeMovConstReg_at(stub + NativeInstruction::nop_instruction_size); ++#ifndef LOONGARCH64 ++ NativeJump* jump = nativeJump_at(method_holder->next_instruction_address()); ++#else ++ NativeGeneralJump* jump = nativeGeneralJump_at(method_holder->next_instruction_address()); ++#endif ++ ++ ++ // Verify state. ++ assert(is_clean() || is_call_to_compiled() || is_call_to_interpreted(), "sanity check"); ++} ++ ++#endif // !PRODUCT +diff --git a/hotspot/src/cpu/loongarch/vm/copy_loongarch.hpp b/hotspot/src/cpu/loongarch/vm/copy_loongarch.hpp +new file mode 100644 +index 0000000000..1b40eab95b +--- /dev/null ++++ b/hotspot/src/cpu/loongarch/vm/copy_loongarch.hpp +@@ -0,0 +1,72 @@ ++/* ++ * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2015, 2020, Loongson Technology. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ * ++ */ ++ ++#ifndef CPU_LOONGARCH_VM_COPY_LOONGARCH_HPP ++#define CPU_LOONGARCH_VM_COPY_LOONGARCH_HPP ++ ++// Inline functions for memory copy and fill. ++ ++// Contains inline asm implementations ++#ifdef TARGET_OS_ARCH_linux_loongarch ++# include "copy_linux_loongarch.inline.hpp" ++#endif ++#ifdef TARGET_OS_ARCH_solaris_loongarch ++# include "copy_solaris_loongarch.inline.hpp" ++#endif ++#ifdef TARGET_OS_ARCH_windows_loongarch ++# include "copy_windows_loongarch.inline.hpp" ++#endif ++#ifdef TARGET_OS_ARCH_bsd_loongarch ++# include "copy_bsd_loongarch.inline.hpp" ++#endif ++// Inline functions for memory copy and fill. ++ ++// Contains inline asm implementations ++ ++static void pd_fill_to_words(HeapWord* tohw, size_t count, juint value) { ++ julong* to = (julong*) tohw; ++ julong v = ((julong) value << 32) | value; ++ while (count-- > 0) { ++ *to++ = v; ++ } ++} ++ ++static void pd_fill_to_aligned_words(HeapWord* tohw, size_t count, juint value) { ++ pd_fill_to_words(tohw, count, value); ++} ++ ++static void pd_fill_to_bytes(void* to, size_t count, jubyte value) { ++ (void)memset(to, value, count); ++} ++ ++static void pd_zero_to_words(HeapWord* tohw, size_t count) { ++ pd_fill_to_words(tohw, count, 0); ++} ++ ++static void pd_zero_to_bytes(void* to, size_t count) { ++ (void)memset(to, 0, count); ++} ++ ++#endif //CPU_LOONGARCH_VM_COPY_LOONGARCH_HPP +diff --git a/hotspot/src/cpu/loongarch/vm/cppInterpreterGenerator_loongarch.hpp b/hotspot/src/cpu/loongarch/vm/cppInterpreterGenerator_loongarch.hpp +new file mode 100644 +index 0000000000..45d86f5bfe +--- /dev/null ++++ b/hotspot/src/cpu/loongarch/vm/cppInterpreterGenerator_loongarch.hpp +@@ -0,0 +1,53 @@ ++/* ++ * Copyright (c) 1997, 2011, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2015, 2020, Loongson Technology. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ * ++ */ ++ ++#ifndef CPU_LOONGARCH_VM_CPPINTERPRETERGENERATOR_LOONGARCH_HPP ++#define CPU_LOONGARCH_VM_CPPINTERPRETERGENERATOR_LOONGARCH_HPP ++ ++ protected: ++ ++#if 0 ++ address generate_asm_interpreter_entry(bool synchronized); ++ address generate_native_entry(bool synchronized); ++ address generate_abstract_entry(void); ++ address generate_math_entry(AbstractInterpreter::MethodKind kind); ++ address generate_empty_entry(void); ++ address generate_accessor_entry(void); ++ void lock_method(void); ++ void generate_stack_overflow_check(void); ++ ++ void generate_counter_incr(Label* overflow, Label* profile_method, Label* profile_method_continue); ++ void generate_counter_overflow(Label* do_continue); ++#endif ++ ++ void generate_more_monitors(); ++ void generate_deopt_handling(); ++ address generate_interpreter_frame_manager(bool synchronized); // C++ interpreter only ++ void generate_compute_interpreter_state(const Register state, ++ const Register prev_state, ++ const Register sender_sp, ++ bool native); // C++ interpreter only ++ ++#endif // CPU_LOONGARCH_VM_CPPINTERPRETERGENERATOR_LOONGARCH_HPP +diff --git a/hotspot/src/cpu/loongarch/vm/cppInterpreter_loongarch.cpp b/hotspot/src/cpu/loongarch/vm/cppInterpreter_loongarch.cpp +new file mode 100644 +index 0000000000..d6c0df3b77 +--- /dev/null ++++ b/hotspot/src/cpu/loongarch/vm/cppInterpreter_loongarch.cpp +@@ -0,0 +1,215 @@ ++/* ++ * Copyright (c) 2007, 2013, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2015, 2020, Loongson Technology. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ * ++ */ ++ ++#include "precompiled.hpp" ++#include "asm/macroAssembler.hpp" ++#include "interpreter/bytecodeHistogram.hpp" ++#include "interpreter/cppInterpreter.hpp" ++#include "interpreter/interpreter.hpp" ++#include "interpreter/interpreterGenerator.hpp" ++#include "interpreter/interpreterRuntime.hpp" ++#include "oops/arrayOop.hpp" ++#include "oops/methodData.hpp" ++#include "oops/method.hpp" ++#include "oops/oop.inline.hpp" ++#include "prims/jvmtiExport.hpp" ++#include "prims/jvmtiThreadState.hpp" ++#include "runtime/arguments.hpp" ++#include "runtime/deoptimization.hpp" ++#include "runtime/frame.inline.hpp" ++#include "runtime/interfaceSupport.hpp" ++#include "runtime/sharedRuntime.hpp" ++#include "runtime/stubRoutines.hpp" ++#include "runtime/synchronizer.hpp" ++#include "runtime/timer.hpp" ++#include "runtime/vframeArray.hpp" ++#include "utilities/debug.hpp" ++#ifdef SHARK ++#include "shark/shark_globals.hpp" ++#endif ++ ++#ifdef CC_INTERP ++ ++// Routine exists to make tracebacks look decent in debugger ++// while "shadow" interpreter frames are on stack. It is also ++// used to distinguish interpreter frames. ++ ++extern "C" void RecursiveInterpreterActivation(interpreterState istate) { ++ ShouldNotReachHere(); ++} ++ ++bool CppInterpreter::contains(address pc) { ++ Unimplemented(); ++} ++ ++#define STATE(field_name) Lstate, in_bytes(byte_offset_of(BytecodeInterpreter, field_name)) ++#define __ _masm-> ++ ++Label frame_manager_entry; ++Label fast_accessor_slow_entry_path; // fast accessor methods need to be able to jmp to unsynchronized ++ // c++ interpreter entry point this holds that entry point label. ++ ++static address unctrap_frame_manager_entry = NULL; ++ ++static address interpreter_return_address = NULL; ++static address deopt_frame_manager_return_atos = NULL; ++static address deopt_frame_manager_return_btos = NULL; ++static address deopt_frame_manager_return_itos = NULL; ++static address deopt_frame_manager_return_ltos = NULL; ++static address deopt_frame_manager_return_ftos = NULL; ++static address deopt_frame_manager_return_dtos = NULL; ++static address deopt_frame_manager_return_vtos = NULL; ++ ++const Register prevState = G1_scratch; ++ ++void InterpreterGenerator::save_native_result(void) { ++ Unimplemented(); ++} ++ ++void InterpreterGenerator::restore_native_result(void) { ++ Unimplemented(); ++} ++ ++// A result handler converts/unboxes a native call result into ++// a java interpreter/compiler result. The current frame is an ++// interpreter frame. The activation frame unwind code must be ++// consistent with that of TemplateTable::_return(...). In the ++// case of native methods, the caller's SP was not modified. ++address CppInterpreterGenerator::generate_result_handler_for(BasicType type) { ++ Unimplemented(); ++} ++ ++address CppInterpreterGenerator::generate_tosca_to_stack_converter(BasicType type) { ++ Unimplemented(); ++} ++ ++address CppInterpreterGenerator::generate_stack_to_stack_converter(BasicType type) { ++ Unimplemented(); ++} ++ ++address CppInterpreterGenerator::generate_stack_to_native_abi_converter(BasicType type) { ++ Unimplemented(); ++} ++ ++address CppInterpreter::return_entry(TosState state, int length) { ++ Unimplemented(); ++} ++ ++address CppInterpreter::deopt_entry(TosState state, int length) { ++ Unimplemented(); ++} ++ ++void InterpreterGenerator::generate_counter_incr(Label* overflow, Label* profile_method, Label* profile_method_continue) { ++ Unimplemented(); ++} ++ ++address InterpreterGenerator::generate_empty_entry(void) { ++ Unimplemented(); ++} ++ ++address InterpreterGenerator::generate_accessor_entry(void) { ++ Unimplemented(); ++} ++ ++address InterpreterGenerator::generate_native_entry(bool synchronized) { ++ Unimplemented(); ++} ++ ++void CppInterpreterGenerator::generate_compute_interpreter_state(const Register state, ++ const Register prev_state, ++ bool native) { ++ Unimplemented(); ++} ++ ++void InterpreterGenerator::lock_method(void) { ++ Unimplemented(); ++} ++ ++void CppInterpreterGenerator::generate_deopt_handling() { ++ Unimplemented(); ++} ++ ++void CppInterpreterGenerator::generate_more_monitors() { ++ Unimplemented(); ++} ++ ++ ++static address interpreter_frame_manager = NULL; ++ ++void CppInterpreterGenerator::adjust_callers_stack(Register args) { ++ Unimplemented(); ++} ++ ++address InterpreterGenerator::generate_normal_entry(bool synchronized) { ++ Unimplemented(); ++} ++ ++InterpreterGenerator::InterpreterGenerator(StubQueue* code) ++ : CppInterpreterGenerator(code) { ++ Unimplemented(); ++} ++ ++ ++static int size_activation_helper(int callee_extra_locals, int max_stack, int monitor_size) { ++ Unimplemented(); ++} ++ ++int AbstractInterpreter::size_top_interpreter_activation(methodOop method) { ++ Unimplemented(); ++} ++ ++void BytecodeInterpreter::layout_interpreterState(interpreterState to_fill, ++ frame* caller, ++ frame* current, ++ methodOop method, ++ intptr_t* locals, ++ intptr_t* stack, ++ intptr_t* stack_base, ++ intptr_t* monitor_base, ++ intptr_t* frame_bottom, ++ bool is_top_frame ++ ) ++{ ++ Unimplemented(); ++} ++ ++void BytecodeInterpreter::pd_layout_interpreterState(interpreterState istate, address last_Java_pc, intptr_t* last_Java_fp) { ++ Unimplemented(); ++} ++ ++ ++int AbstractInterpreter::layout_activation(methodOop method, ++ int tempcount, // Number of slots on java expression stack in use ++ int popframe_extra_args, ++ int moncount, // Number of active monitors ++ int callee_param_size, ++ int callee_locals_size, ++ frame* caller, ++ frame* interpreter_frame, ++ bool is_top_frame) { ++ Unimplemented(); ++} ++ ++#endif // CC_INTERP +diff --git a/hotspot/src/cpu/loongarch/vm/debug_loongarch.cpp b/hotspot/src/cpu/loongarch/vm/debug_loongarch.cpp +new file mode 100644 +index 0000000000..50de03653b +--- /dev/null ++++ b/hotspot/src/cpu/loongarch/vm/debug_loongarch.cpp +@@ -0,0 +1,51 @@ ++/* ++ * Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2015, 2021, Loongson Technology. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ * ++ */ ++ ++#include "precompiled.hpp" ++#include "code/codeCache.hpp" ++#include "code/nmethod.hpp" ++#include "runtime/frame.hpp" ++#include "runtime/init.hpp" ++#include "runtime/os.hpp" ++#include "utilities/debug.hpp" ++#include "utilities/top.hpp" ++ ++#ifndef PRODUCT ++ ++void pd_ps(frame f) { ++ intptr_t* sp = f.sp(); ++ intptr_t* prev_sp = sp - 1; ++ intptr_t *pc = NULL; ++ intptr_t *next_pc = NULL; ++ int count = 0; ++ tty->print("register window backtrace from %#lx:\n", p2i(sp)); ++} ++ ++// This function is used to add platform specific info ++// to the error reporting code. ++ ++void pd_obfuscate_location(char *buf,int buflen) {} ++ ++#endif // PRODUCT +diff --git a/hotspot/src/cpu/loongarch/vm/depChecker_loongarch.cpp b/hotspot/src/cpu/loongarch/vm/depChecker_loongarch.cpp +new file mode 100644 +index 0000000000..62478be3dc +--- /dev/null ++++ b/hotspot/src/cpu/loongarch/vm/depChecker_loongarch.cpp +@@ -0,0 +1,30 @@ ++/* ++ * Copyright (c) 2002, 2010, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2015, 2020, Loongson Technology. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ * ++ */ ++ ++#include "precompiled.hpp" ++#include "compiler/disassembler.hpp" ++#include "depChecker_loongarch.hpp" ++ ++// Nothing to do on LoongArch +diff --git a/hotspot/src/cpu/loongarch/vm/depChecker_loongarch.hpp b/hotspot/src/cpu/loongarch/vm/depChecker_loongarch.hpp +new file mode 100644 +index 0000000000..598be0ee6f +--- /dev/null ++++ b/hotspot/src/cpu/loongarch/vm/depChecker_loongarch.hpp +@@ -0,0 +1,31 @@ ++/* ++ * Copyright (c) 2002, 2010, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2015, 2020, Loongson Technology. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ * ++ */ ++ ++#ifndef CPU_LOONGARCH_VM_DEPCHECKER_LOONGARCH_HPP ++#define CPU_LOONGARCH_VM_DEPCHECKER_LOONGARCH_HPP ++ ++// Nothing to do on LoongArch ++ ++#endif // CPU_LOONGARCH_VM_DEPCHECKER_LOONGARCH_HPP +diff --git a/hotspot/src/cpu/loongarch/vm/disassembler_loongarch.hpp b/hotspot/src/cpu/loongarch/vm/disassembler_loongarch.hpp +new file mode 100644 +index 0000000000..ccd89e8d6d +--- /dev/null ++++ b/hotspot/src/cpu/loongarch/vm/disassembler_loongarch.hpp +@@ -0,0 +1,37 @@ ++/* ++ * Copyright (c) 1997, 2010, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2015, 2021, Loongson Technology. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ * ++ */ ++ ++#ifndef CPU_LOONGARCH_VM_DISASSEMBLER_LOONGARCH_HPP ++#define CPU_LOONGARCH_VM_DISASSEMBLER_LOONGARCH_HPP ++ ++ static int pd_instruction_alignment() { ++ return sizeof(int); ++ } ++ ++ static const char* pd_cpu_opts() { ++ return "gpr-names=64"; ++ } ++ ++#endif // CPU_LOONGARCH_VM_DISASSEMBLER_LOONGARCH_HPP +diff --git a/hotspot/src/cpu/loongarch/vm/frame_loongarch.cpp b/hotspot/src/cpu/loongarch/vm/frame_loongarch.cpp +new file mode 100644 +index 0000000000..0f50a5715d +--- /dev/null ++++ b/hotspot/src/cpu/loongarch/vm/frame_loongarch.cpp +@@ -0,0 +1,711 @@ ++/* ++ * Copyright (c) 1997, 2014, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2015, 2021, Loongson Technology. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ * ++ */ ++ ++#include "precompiled.hpp" ++#include "interpreter/interpreter.hpp" ++#include "memory/resourceArea.hpp" ++#include "oops/markOop.hpp" ++#include "oops/method.hpp" ++#include "oops/oop.inline.hpp" ++#include "prims/methodHandles.hpp" ++#include "runtime/frame.inline.hpp" ++#include "runtime/handles.inline.hpp" ++#include "runtime/javaCalls.hpp" ++#include "runtime/monitorChunk.hpp" ++#include "runtime/signature.hpp" ++#include "runtime/stubCodeGenerator.hpp" ++#include "runtime/stubRoutines.hpp" ++#include "vmreg_loongarch.inline.hpp" ++ ++#ifdef ASSERT ++void RegisterMap::check_location_valid() { ++} ++#endif ++ ++ ++// Profiling/safepoint support ++// for Profiling - acting on another frame. walks sender frames ++// if valid. ++// frame profile_find_Java_sender_frame(JavaThread *thread); ++ ++bool frame::safe_for_sender(JavaThread *thread) { ++ address sp = (address)_sp; ++ address fp = (address)_fp; ++ address unextended_sp = (address)_unextended_sp; ++ ++ // consider stack guards when trying to determine "safe" stack pointers ++ static size_t stack_guard_size = os::uses_stack_guard_pages() ? (StackYellowPages + StackRedPages) * os::vm_page_size() : 0; ++ size_t usable_stack_size = thread->stack_size() - stack_guard_size; ++ ++ // sp must be within the usable part of the stack (not in guards) ++ bool sp_safe = (sp < thread->stack_base()) && ++ (sp >= thread->stack_base() - usable_stack_size); ++ ++ ++ if (!sp_safe) { ++ return false; ++ } ++ ++ // unextended sp must be within the stack and above or equal sp ++ bool unextended_sp_safe = (unextended_sp < thread->stack_base()) && ++ (unextended_sp >= sp); ++ ++ if (!unextended_sp_safe) { ++ return false; ++ } ++ ++ // an fp must be within the stack and above (but not equal) sp ++ // second evaluation on fp+ is added to handle situation where fp is -1 ++ bool fp_safe = (fp < thread->stack_base() && (fp > sp) && (((fp + (return_addr_offset * sizeof(void*))) < thread->stack_base()))); ++ ++ // We know sp/unextended_sp are safe only fp is questionable here ++ ++ // If the current frame is known to the code cache then we can attempt to ++ // construct the sender and do some validation of it. This goes a long way ++ // toward eliminating issues when we get in frame construction code ++ ++ if (_cb != NULL ) { ++ ++ // First check if frame is complete and tester is reliable ++ // Unfortunately we can only check frame complete for runtime stubs and nmethod ++ // other generic buffer blobs are more problematic so we just assume they are ++ // ok. adapter blobs never have a frame complete and are never ok. ++ ++ if (!_cb->is_frame_complete_at(_pc)) { ++ if (_cb->is_nmethod() || _cb->is_adapter_blob() || _cb->is_runtime_stub()) { ++ return false; ++ } ++ } ++ ++ // Could just be some random pointer within the codeBlob ++ if (!_cb->code_contains(_pc)) { ++ return false; ++ } ++ ++ // Entry frame checks ++ if (is_entry_frame()) { ++ // an entry frame must have a valid fp. ++ return fp_safe && is_entry_frame_valid(thread); ++ } ++ ++ intptr_t* sender_sp = NULL; ++ intptr_t* sender_unextended_sp = NULL; ++ address sender_pc = NULL; ++ intptr_t* saved_fp = NULL; ++ ++ if (is_interpreted_frame()) { ++ // fp must be safe ++ if (!fp_safe) { ++ return false; ++ } ++ ++ sender_pc = (address) this->fp()[return_addr_offset]; ++ // for interpreted frames, the value below is the sender "raw" sp, ++ // which can be different from the sender unextended sp (the sp seen ++ // by the sender) because of current frame local variables ++ sender_sp = (intptr_t*) addr_at(sender_sp_offset); ++ sender_unextended_sp = (intptr_t*) this->fp()[interpreter_frame_sender_sp_offset]; ++ saved_fp = (intptr_t*) this->fp()[link_offset]; ++ ++ } else { ++ // must be some sort of compiled/runtime frame ++ // fp does not have to be safe (although it could be check for c1?) ++ ++ // check for a valid frame_size, otherwise we are unlikely to get a valid sender_pc ++ if (_cb->frame_size() <= 0) { ++ return false; ++ } ++ ++ sender_sp = _unextended_sp + _cb->frame_size(); ++ sender_unextended_sp = sender_sp; ++ // On LA the return_address is always the word on the stack ++ sender_pc = (address) *(sender_sp-1); ++ // Note: frame::sender_sp_offset is only valid for compiled frame ++ saved_fp = (intptr_t*) *(sender_sp - frame::sender_sp_offset); ++ } ++ ++ ++ // If the potential sender is the interpreter then we can do some more checking ++ if (Interpreter::contains(sender_pc)) { ++ ++ // FP is always saved in a recognizable place in any code we generate. However ++ // only if the sender is interpreted/call_stub (c1 too?) are we certain that the saved FP ++ // is really a frame pointer. ++ ++ bool saved_fp_safe = ((address)saved_fp < thread->stack_base()) && (saved_fp > sender_sp); ++ ++ if (!saved_fp_safe) { ++ return false; ++ } ++ ++ // construct the potential sender ++ ++ frame sender(sender_sp, sender_unextended_sp, saved_fp, sender_pc); ++ ++ return sender.is_interpreted_frame_valid(thread); ++ ++ } ++ ++ // We must always be able to find a recognizable pc ++ CodeBlob* sender_blob = CodeCache::find_blob_unsafe(sender_pc); ++ if (sender_pc == NULL || sender_blob == NULL) { ++ return false; ++ } ++ ++ // Could be a zombie method ++ if (sender_blob->is_zombie() || sender_blob->is_unloaded()) { ++ return false; ++ } ++ ++ // Could just be some random pointer within the codeBlob ++ if (!sender_blob->code_contains(sender_pc)) { ++ return false; ++ } ++ ++ // We should never be able to see an adapter if the current frame is something from code cache ++ if (sender_blob->is_adapter_blob()) { ++ return false; ++ } ++ ++ // Could be the call_stub ++ if (StubRoutines::returns_to_call_stub(sender_pc)) { ++ bool saved_fp_safe = ((address)saved_fp < thread->stack_base()) && (saved_fp > sender_sp); ++ ++ if (!saved_fp_safe) { ++ return false; ++ } ++ ++ // construct the potential sender ++ ++ frame sender(sender_sp, sender_unextended_sp, saved_fp, sender_pc); ++ ++ // Validate the JavaCallWrapper an entry frame must have ++ address jcw = (address)sender.entry_frame_call_wrapper(); ++ ++ bool jcw_safe = (jcw < thread->stack_base()) && ( jcw > (address)sender.fp()); ++ ++ return jcw_safe; ++ } ++ ++ if (sender_blob->is_nmethod()) { ++ nmethod* nm = sender_blob->as_nmethod_or_null(); ++ if (nm != NULL) { ++ if (nm->is_deopt_mh_entry(sender_pc) || nm->is_deopt_entry(sender_pc)) { ++ return false; ++ } ++ } ++ } ++ ++ // If the frame size is 0 something (or less) is bad because every nmethod has a non-zero frame size ++ // because the return address counts against the callee's frame. ++ ++ if (sender_blob->frame_size() <= 0) { ++ assert(!sender_blob->is_nmethod(), "should count return address at least"); ++ return false; ++ } ++ ++ // We should never be able to see anything here except an nmethod. If something in the ++ // code cache (current frame) is called by an entity within the code cache that entity ++ // should not be anything but the call stub (already covered), the interpreter (already covered) ++ // or an nmethod. ++ ++ if (!sender_blob->is_nmethod()) { ++ return false; ++ } ++ ++ // Could put some more validation for the potential non-interpreted sender ++ // frame we'd create by calling sender if I could think of any. Wait for next crash in forte... ++ ++ // One idea is seeing if the sender_pc we have is one that we'd expect to call to current cb ++ ++ // We've validated the potential sender that would be created ++ return true; ++ } ++ // Note: fp == NULL is not really a prerequisite for this to be safe to ++ // walk for c2. However we've modified the code such that if we get ++ // a failure with fp != NULL that we then try with FP == NULL. ++ // This is basically to mimic what a last_frame would look like if ++ // c2 had generated it. ++ ++ // Must be native-compiled frame. Since sender will try and use fp to find ++ // linkages it must be safe ++ ++ if (!fp_safe) { ++ return false; ++ } ++ ++ // Will the pc we fetch be non-zero (which we'll find at the oldest frame) ++ ++ if ( (address) this->fp()[return_addr_offset] == NULL) return false; ++ ++ ++ // could try and do some more potential verification of native frame if we could think of some... ++ ++ return true; ++ ++} ++ ++void frame::patch_pc(Thread* thread, address pc) { ++ assert(_cb == CodeCache::find_blob(pc), "unexpected pc"); ++ address* pc_addr = &(((address*) sp())[-1]); ++ if (TracePcPatching) { ++ tty->print_cr("patch_pc at address " INTPTR_FORMAT " [" INTPTR_FORMAT " -> " INTPTR_FORMAT "]", ++ p2i(pc_addr), p2i(*pc_addr), p2i(pc)); ++ } ++ ++ // Either the return address is the original one or we are going to ++ // patch in the same address that's already there. ++ assert(_pc == *pc_addr || pc == *pc_addr, "must be"); ++ *pc_addr = pc; ++ _cb = CodeCache::find_blob(pc); ++ address original_pc = nmethod::get_deopt_original_pc(this); ++ if (original_pc != NULL) { ++ assert(original_pc == _pc, "expected original PC to be stored before patching"); ++ _deopt_state = is_deoptimized; ++ // leave _pc as is ++ } else { ++ _deopt_state = not_deoptimized; ++ _pc = pc; ++ } ++} ++ ++bool frame::is_interpreted_frame() const { ++ return Interpreter::contains(pc()); ++} ++ ++int frame::frame_size(RegisterMap* map) const { ++ frame sender = this->sender(map); ++ return sender.sp() - sp(); ++} ++ ++intptr_t* frame::entry_frame_argument_at(int offset) const { ++ // convert offset to index to deal with tsi ++ int index = (Interpreter::expr_offset_in_bytes(offset)/wordSize); ++ // Entry frame's arguments are always in relation to unextended_sp() ++ return &unextended_sp()[index]; ++} ++ ++// sender_sp ++#ifdef CC_INTERP ++intptr_t* frame::interpreter_frame_sender_sp() const { ++ assert(is_interpreted_frame(), "interpreted frame expected"); ++ // QQQ why does this specialize method exist if frame::sender_sp() does same thing? ++ // seems odd and if we always know interpreted vs. non then sender_sp() is really ++ // doing too much work. ++ return get_interpreterState()->sender_sp(); ++} ++ ++// monitor elements ++ ++BasicObjectLock* frame::interpreter_frame_monitor_begin() const { ++ return get_interpreterState()->monitor_base(); ++} ++ ++BasicObjectLock* frame::interpreter_frame_monitor_end() const { ++ return (BasicObjectLock*) get_interpreterState()->stack_base(); ++} ++ ++#else // CC_INTERP ++ ++intptr_t* frame::interpreter_frame_sender_sp() const { ++ assert(is_interpreted_frame(), "interpreted frame expected"); ++ return (intptr_t*) at(interpreter_frame_sender_sp_offset); ++} ++ ++void frame::set_interpreter_frame_sender_sp(intptr_t* sender_sp) { ++ assert(is_interpreted_frame(), "interpreted frame expected"); ++ int_at_put(interpreter_frame_sender_sp_offset, (intptr_t) sender_sp); ++} ++ ++ ++// monitor elements ++ ++BasicObjectLock* frame::interpreter_frame_monitor_begin() const { ++ return (BasicObjectLock*) addr_at(interpreter_frame_monitor_block_bottom_offset); ++} ++ ++BasicObjectLock* frame::interpreter_frame_monitor_end() const { ++ BasicObjectLock* result = (BasicObjectLock*) *addr_at(interpreter_frame_monitor_block_top_offset); ++ // make sure the pointer points inside the frame ++ assert((intptr_t) fp() > (intptr_t) result, "result must < than frame pointer"); ++ assert((intptr_t) sp() <= (intptr_t) result, "result must >= than stack pointer"); ++ return result; ++} ++ ++void frame::interpreter_frame_set_monitor_end(BasicObjectLock* value) { ++ *((BasicObjectLock**)addr_at(interpreter_frame_monitor_block_top_offset)) = value; ++} ++ ++// Used by template based interpreter deoptimization ++void frame::interpreter_frame_set_last_sp(intptr_t* sp) { ++ *((intptr_t**)addr_at(interpreter_frame_last_sp_offset)) = sp; ++} ++#endif // CC_INTERP ++ ++frame frame::sender_for_entry_frame(RegisterMap* map) const { ++ assert(map != NULL, "map must be set"); ++ // Java frame called from C; skip all C frames and return top C ++ // frame of that chunk as the sender ++ JavaFrameAnchor* jfa = entry_frame_call_wrapper()->anchor(); ++ assert(!entry_frame_is_first(), "next Java fp must be non zero"); ++ assert(jfa->last_Java_sp() > sp(), "must be above this frame on stack"); ++ map->clear(); ++ assert(map->include_argument_oops(), "should be set by clear"); ++ if (jfa->last_Java_pc() != NULL ) { ++ frame fr(jfa->last_Java_sp(), jfa->last_Java_fp(), jfa->last_Java_pc()); ++ return fr; ++ } ++ frame fr(jfa->last_Java_sp(), jfa->last_Java_fp()); ++ return fr; ++} ++ ++frame frame::sender_for_interpreter_frame(RegisterMap* map) const { ++ // sp is the raw sp from the sender after adapter or interpreter extension ++ intptr_t* sender_sp = this->sender_sp(); ++ ++ // This is the sp before any possible extension (adapter/locals). ++ intptr_t* unextended_sp = interpreter_frame_sender_sp(); ++ ++ // The interpreter and compiler(s) always save FP in a known ++ // location on entry. We must record where that location is ++ // so this if FP was live on callout from c2 we can find ++ // the saved copy no matter what it called. ++ ++ // Since the interpreter always saves FP if we record where it is then ++ // we don't have to always save FP on entry and exit to c2 compiled ++ // code, on entry will be enough. ++#ifdef COMPILER2 ++ if (map->update_map()) { ++ update_map_with_saved_link(map, (intptr_t**) addr_at(link_offset)); ++ } ++#endif /* COMPILER2 */ ++ return frame(sender_sp, unextended_sp, link(), sender_pc()); ++} ++ ++ ++//------------------------------------------------------------------------------ ++// frame::verify_deopt_original_pc ++// ++// Verifies the calculated original PC of a deoptimization PC for the ++// given unextended SP. The unextended SP might also be the saved SP ++// for MethodHandle call sites. ++#ifdef ASSERT ++void frame::verify_deopt_original_pc(nmethod* nm, intptr_t* unextended_sp, bool is_method_handle_return) { ++ frame fr; ++ ++ // This is ugly but it's better than to change {get,set}_original_pc ++ // to take an SP value as argument. And it's only a debugging ++ // method anyway. ++ fr._unextended_sp = unextended_sp; ++ ++ address original_pc = nm->get_original_pc(&fr); ++ assert(nm->insts_contains(original_pc), "original PC must be in nmethod"); ++ assert(nm->is_method_handle_return(original_pc) == is_method_handle_return, "must be"); ++} ++#endif ++ ++ ++//------------------------------------------------------------------------------ ++// frame::adjust_unextended_sp ++void frame::adjust_unextended_sp() { ++ // On LoongArch, sites calling method handle intrinsics and lambda forms are treated ++ // as any other call site. Therefore, no special action is needed when we are ++ // returning to any of these call sites. ++ ++ nmethod* sender_nm = (_cb == NULL) ? NULL : _cb->as_nmethod_or_null(); ++ if (sender_nm != NULL) { ++ // If the sender PC is a deoptimization point, get the original PC. ++ if (sender_nm->is_deopt_entry(_pc) || ++ sender_nm->is_deopt_mh_entry(_pc)) { ++ DEBUG_ONLY(verify_deopt_original_pc(sender_nm, _unextended_sp)); ++ } ++ } ++} ++ ++//------------------------------------------------------------------------------ ++// frame::update_map_with_saved_link ++void frame::update_map_with_saved_link(RegisterMap* map, intptr_t** link_addr) { ++ // The interpreter and compiler(s) always save fp in a known ++ // location on entry. We must record where that location is ++ // so that if fp was live on callout from c2 we can find ++ // the saved copy no matter what it called. ++ ++ // Since the interpreter always saves fp if we record where it is then ++ // we don't have to always save fp on entry and exit to c2 compiled ++ // code, on entry will be enough. ++ map->set_location(FP->as_VMReg(), (address) link_addr); ++ // this is weird "H" ought to be at a higher address however the ++ // oopMaps seems to have the "H" regs at the same address and the ++ // vanilla register. ++ // XXXX make this go away ++ if (true) { ++ map->set_location(FP->as_VMReg()->next(), (address) link_addr); ++ } ++} ++ ++//------------------------------sender_for_compiled_frame----------------------- ++frame frame::sender_for_compiled_frame(RegisterMap* map) const { ++ assert(map != NULL, "map must be set"); ++ ++ // frame owned by optimizing compiler ++ assert(_cb->frame_size() >= 0, "must have non-zero frame size"); ++ ++ intptr_t* sender_sp = unextended_sp() + _cb->frame_size(); ++ intptr_t* unextended_sp = sender_sp; ++ ++#ifdef ASSERT ++ const bool c1_compiled = _cb->is_compiled_by_c1(); ++ bool native = _cb->is_nmethod() && ((nmethod*)_cb)->is_native_method(); ++ if (c1_compiled && native) { ++ assert(sender_sp == fp() + frame::sender_sp_offset, "incorrect frame size"); ++ } ++#endif // ASSERT ++ // On Intel the return_address is always the word on the stack ++ // the fp in compiler points to sender fp, but in interpreter, fp points to return address, ++ // so getting sender for compiled frame is not same as interpreter frame. ++ // we hard code here temporarily ++ // spark ++ address sender_pc = (address) *(sender_sp-1); ++ ++ intptr_t** saved_fp_addr = (intptr_t**) (sender_sp - frame::sender_sp_offset); ++ ++ if (map->update_map()) { ++ // Tell GC to use argument oopmaps for some runtime stubs that need it. ++ // For C1, the runtime stub might not have oop maps, so set this flag ++ // outside of update_register_map. ++ map->set_include_argument_oops(_cb->caller_must_gc_arguments(map->thread())); ++ if (_cb->oop_maps() != NULL) { ++ OopMapSet::update_register_map(this, map); ++ } ++ ++ // Since the prolog does the save and restore of epb there is no oopmap ++ // for it so we must fill in its location as if there was an oopmap entry ++ // since if our caller was compiled code there could be live jvm state in it. ++ update_map_with_saved_link(map, saved_fp_addr); ++ } ++ assert(sender_sp != sp(), "must have changed"); ++ return frame(sender_sp, unextended_sp, *saved_fp_addr, sender_pc); ++} ++ ++frame frame::sender(RegisterMap* map) const { ++ // Default is we done have to follow them. The sender_for_xxx will ++ // update it accordingly ++ map->set_include_argument_oops(false); ++ ++ if (is_entry_frame()) return sender_for_entry_frame(map); ++ if (is_interpreted_frame()) return sender_for_interpreter_frame(map); ++ assert(_cb == CodeCache::find_blob(pc()),"Must be the same"); ++ ++ if (_cb != NULL) { ++ return sender_for_compiled_frame(map); ++ } ++ // Must be native-compiled frame, i.e. the marshaling code for native ++ // methods that exists in the core system. ++ return frame(sender_sp(), link(), sender_pc()); ++} ++ ++ ++bool frame::interpreter_frame_equals_unpacked_fp(intptr_t* fp) { ++ assert(is_interpreted_frame(), "must be interpreter frame"); ++ Method* method = interpreter_frame_method(); ++ // When unpacking an optimized frame the frame pointer is ++ // adjusted with: ++ int diff = (method->max_locals() - method->size_of_parameters()) * ++ Interpreter::stackElementWords; ++ printf("^^^^^^^^^^^^^^^adjust fp in deopt fp = 0%lx \n", (intptr_t)(fp - diff)); ++ return _fp == (fp - diff); ++} ++ ++void frame::pd_gc_epilog() { ++ // nothing done here now ++} ++ ++bool frame::is_interpreted_frame_valid(JavaThread* thread) const { ++// QQQ ++#ifdef CC_INTERP ++#else ++ assert(is_interpreted_frame(), "Not an interpreted frame"); ++ // These are reasonable sanity checks ++ if (fp() == 0 || (intptr_t(fp()) & (wordSize-1)) != 0) { ++ return false; ++ } ++ if (sp() == 0 || (intptr_t(sp()) & (wordSize-1)) != 0) { ++ return false; ++ } ++ if (fp() + interpreter_frame_initial_sp_offset < sp()) { ++ return false; ++ } ++ // These are hacks to keep us out of trouble. ++ // The problem with these is that they mask other problems ++ if (fp() <= sp()) { // this attempts to deal with unsigned comparison above ++ return false; ++ } ++ ++ // do some validation of frame elements ++ ++ // first the method ++ ++ Method* m = *interpreter_frame_method_addr(); ++ ++ // validate the method we'd find in this potential sender ++ if (!m->is_valid_method()) return false; ++ ++ // stack frames shouldn't be much larger than max_stack elements ++ ++ //if (fp() - sp() > 1024 + m->max_stack()*Interpreter::stackElementSize()) { ++ if (fp() - sp() > 4096) { // stack frames shouldn't be large. ++ return false; ++ } ++ ++ // validate bci/bcx ++ ++ intptr_t bcx = interpreter_frame_bcx(); ++ if (m->validate_bci_from_bcx(bcx) < 0) { ++ return false; ++ } ++ ++ // validate ConstantPoolCache* ++ ++ ConstantPoolCache* cp = *interpreter_frame_cache_addr(); ++ ++ if (cp == NULL || !cp->is_metaspace_object()) return false; ++ ++ // validate locals ++ ++ address locals = (address) *interpreter_frame_locals_addr(); ++ ++ if (locals > thread->stack_base() || locals < (address) fp()) return false; ++ ++ // We'd have to be pretty unlucky to be mislead at this point ++ ++#endif // CC_INTERP ++ return true; ++} ++ ++BasicType frame::interpreter_frame_result(oop* oop_result, jvalue* value_result) { ++#ifdef CC_INTERP ++ // Needed for JVMTI. The result should always be in the interpreterState object ++ assert(false, "NYI"); ++ interpreterState istate = get_interpreterState(); ++#endif // CC_INTERP ++ assert(is_interpreted_frame(), "interpreted frame expected"); ++ Method* method = interpreter_frame_method(); ++ BasicType type = method->result_type(); ++ ++ intptr_t* tos_addr; ++ if (method->is_native()) { ++ // Prior to calling into the runtime to report the method_exit the possible ++ // return value is pushed to the native stack. If the result is a jfloat/jdouble ++ // then ST0 is saved. See the note in generate_native_result ++ tos_addr = (intptr_t*)sp(); ++ if (type == T_FLOAT || type == T_DOUBLE) { ++ tos_addr += 2; ++ } ++ } else { ++ tos_addr = (intptr_t*)interpreter_frame_tos_address(); ++ } ++ ++ switch (type) { ++ case T_OBJECT : ++ case T_ARRAY : { ++ oop obj; ++ if (method->is_native()) { ++#ifdef CC_INTERP ++ obj = istate->_oop_temp; ++#else ++ obj = cast_to_oop(at(interpreter_frame_oop_temp_offset)); ++#endif // CC_INTERP ++ } else { ++ oop* obj_p = (oop*)tos_addr; ++ obj = (obj_p == NULL) ? (oop)NULL : *obj_p; ++ } ++ assert(obj == NULL || Universe::heap()->is_in(obj), "sanity check"); ++ *oop_result = obj; ++ break; ++ } ++ case T_BOOLEAN : value_result->z = *(jboolean*)tos_addr; break; ++ case T_BYTE : value_result->b = *(jbyte*)tos_addr; break; ++ case T_CHAR : value_result->c = *(jchar*)tos_addr; break; ++ case T_SHORT : value_result->s = *(jshort*)tos_addr; break; ++ case T_INT : value_result->i = *(jint*)tos_addr; break; ++ case T_LONG : value_result->j = *(jlong*)tos_addr; break; ++ case T_FLOAT : value_result->f = *(jfloat*)tos_addr; break; ++ case T_DOUBLE : value_result->d = *(jdouble*)tos_addr; break; ++ case T_VOID : /* Nothing to do */ break; ++ default : ShouldNotReachHere(); ++ } ++ ++ return type; ++} ++ ++ ++intptr_t* frame::interpreter_frame_tos_at(jint offset) const { ++ int index = (Interpreter::expr_offset_in_bytes(offset)/wordSize); ++ return &interpreter_frame_tos_address()[index]; ++} ++ ++#ifndef PRODUCT ++ ++#define DESCRIBE_FP_OFFSET(name) \ ++ values.describe(frame_no, fp() + frame::name##_offset, #name) ++ ++void frame::describe_pd(FrameValues& values, int frame_no) { ++ if (is_interpreted_frame()) { ++ DESCRIBE_FP_OFFSET(interpreter_frame_sender_sp); ++ DESCRIBE_FP_OFFSET(interpreter_frame_last_sp); ++ DESCRIBE_FP_OFFSET(interpreter_frame_method); ++ DESCRIBE_FP_OFFSET(interpreter_frame_mdx); ++ DESCRIBE_FP_OFFSET(interpreter_frame_cache); ++ DESCRIBE_FP_OFFSET(interpreter_frame_locals); ++ DESCRIBE_FP_OFFSET(interpreter_frame_bcx); ++ DESCRIBE_FP_OFFSET(interpreter_frame_initial_sp); ++ } ++} ++#endif ++ ++intptr_t *frame::initial_deoptimization_info() { ++ // used to reset the saved FP ++ return fp(); ++} ++ ++intptr_t* frame::real_fp() const { ++ if (_cb != NULL) { ++ // use the frame size if valid ++ int size = _cb->frame_size(); ++ if (size > 0) { ++ return unextended_sp() + size; ++ } ++ } ++ // else rely on fp() ++ assert(! is_compiled_frame(), "unknown compiled frame size"); ++ return fp(); ++} ++ ++#ifndef PRODUCT ++// This is a generic constructor which is only used by pns() in debug.cpp. ++frame::frame(void* sp, void* fp, void* pc) { ++ init((intptr_t*)sp, (intptr_t*)fp, (address)pc); ++} ++#endif +diff --git a/hotspot/src/cpu/loongarch/vm/frame_loongarch.hpp b/hotspot/src/cpu/loongarch/vm/frame_loongarch.hpp +new file mode 100644 +index 0000000000..964026e621 +--- /dev/null ++++ b/hotspot/src/cpu/loongarch/vm/frame_loongarch.hpp +@@ -0,0 +1,229 @@ ++/* ++ * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2015, 2020, Loongson Technology. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ * ++ */ ++ ++#ifndef CPU_LOONGARCH_VM_FRAME_LOONGARCH_HPP ++#define CPU_LOONGARCH_VM_FRAME_LOONGARCH_HPP ++ ++#include "runtime/synchronizer.hpp" ++#include "utilities/top.hpp" ++ ++// A frame represents a physical stack frame (an activation). Frames can be ++// C or Java frames, and the Java frames can be interpreted or compiled. ++// In contrast, vframes represent source-level activations, so that one physical frame ++// can correspond to multiple source level frames because of inlining. ++// A frame is comprised of {pc, fp, sp} ++// ------------------------------ Asm interpreter ---------------------------------------- ++// Layout of asm interpreter frame: ++// [expression stack ] * <- sp ++// [monitors ] \ ++// ... | monitor block size ++// [monitors ] / ++// [monitor block size ] ++// [byte code index/pointr] = bcx() bcx_offset ++// [pointer to locals ] = locals() locals_offset ++// [constant pool cache ] = cache() cache_offset ++// [methodData ] = mdp() mdx_offset ++// [methodOop ] = method() method_offset ++// [last sp ] = last_sp() last_sp_offset ++// [old stack pointer ] (sender_sp) sender_sp_offset ++// [old frame pointer ] <- fp = link() ++// [return pc ] ++// [oop temp ] (only for native calls) ++// [locals and parameters ] ++// <- sender sp ++// ------------------------------ Asm interpreter ---------------------------------------- ++ ++// ------------------------------ C++ interpreter ---------------------------------------- ++// ++// Layout of C++ interpreter frame: (While executing in BytecodeInterpreter::run) ++// ++// <- SP (current sp) ++// [local variables ] BytecodeInterpreter::run local variables ++// ... BytecodeInterpreter::run local variables ++// [local variables ] BytecodeInterpreter::run local variables ++// [old frame pointer ] fp [ BytecodeInterpreter::run's fp ] ++// [return pc ] (return to frame manager) ++// [interpreter_state* ] (arg to BytecodeInterpreter::run) -------------- ++// [expression stack ] <- last_Java_sp | ++// [... ] * <- interpreter_state.stack | ++// [expression stack ] * <- interpreter_state.stack_base | ++// [monitors ] \ | ++// ... | monitor block size | ++// [monitors ] / <- interpreter_state.monitor_base | ++// [struct interpretState ] <-----------------------------------------| ++// [return pc ] (return to callee of frame manager [1] ++// [locals and parameters ] ++// <- sender sp ++ ++// [1] When the c++ interpreter calls a new method it returns to the frame ++// manager which allocates a new frame on the stack. In that case there ++// is no real callee of this newly allocated frame. The frame manager is ++// aware of the additional frame(s) and will pop them as nested calls ++// complete. Howevers tTo make it look good in the debugger the frame ++// manager actually installs a dummy pc pointing to RecursiveInterpreterActivation ++// with a fake interpreter_state* parameter to make it easy to debug ++// nested calls. ++ ++// Note that contrary to the layout for the assembly interpreter the ++// expression stack allocated for the C++ interpreter is full sized. ++// However this is not as bad as it seems as the interpreter frame_manager ++// will truncate the unused space on succesive method calls. ++// ++// ------------------------------ C++ interpreter ---------------------------------------- ++ ++// Layout of interpreter frame: ++// ++// [ monitor entry ] <--- sp ++// ... ++// [ monitor entry ] ++// -9 [ monitor block top ] ( the top monitor entry ) ++// -8 [ byte code pointer ] (if native, bcp = 0) ++// -7 [ constant pool cache ] ++// -6 [ methodData ] mdx_offset(not core only) ++// -5 [ mirror ] ++// -4 [ methodOop ] ++// -3 [ locals offset ] ++// -2 [ last_sp ] ++// -1 [ sender's sp ] ++// 0 [ sender's fp ] <--- fp ++// 1 [ return address ] ++// 2 [ oop temp offset ] (only for native calls) ++// 3 [ result handler offset ] (only for native calls) ++// 4 [ result type info ] (only for native calls) ++// [ local var m-1 ] ++// ... ++// [ local var 0 ] ++// [ argumnet word n-1 ] <--- ( sender's sp ) ++// ... ++// [ argument word 0 ] <--- S7 ++ ++ public: ++ enum { ++ pc_return_offset = 0, ++ // All frames ++ link_offset = 0, ++ return_addr_offset = 1, ++ // non-interpreter frames ++ sender_sp_offset = 2, ++ ++#ifndef CC_INTERP ++ ++ // Interpreter frames ++ interpreter_frame_return_addr_offset = 1, ++ interpreter_frame_result_handler_offset = 3, // for native calls only ++ interpreter_frame_oop_temp_offset = 2, // for native calls only ++ ++ interpreter_frame_sender_fp_offset = 0, ++ interpreter_frame_sender_sp_offset = -1, ++ // outgoing sp before a call to an invoked method ++ interpreter_frame_last_sp_offset = interpreter_frame_sender_sp_offset - 1, ++ interpreter_frame_locals_offset = interpreter_frame_last_sp_offset - 1, ++ interpreter_frame_method_offset = interpreter_frame_locals_offset - 1, ++ interpreter_frame_mdx_offset = interpreter_frame_method_offset - 1, ++ interpreter_frame_cache_offset = interpreter_frame_mdx_offset - 1, ++ interpreter_frame_bcx_offset = interpreter_frame_cache_offset - 1, ++ interpreter_frame_initial_sp_offset = interpreter_frame_bcx_offset - 1, ++ ++ interpreter_frame_monitor_block_top_offset = interpreter_frame_initial_sp_offset, ++ interpreter_frame_monitor_block_bottom_offset = interpreter_frame_initial_sp_offset, ++ ++#endif // CC_INTERP ++ ++ // Entry frames ++ entry_frame_call_wrapper_offset = -9, ++ ++ // Native frames ++ ++ native_frame_initial_param_offset = 2 ++ ++ }; ++ ++ intptr_t ptr_at(int offset) const { ++ return *ptr_at_addr(offset); ++ } ++ ++ void ptr_at_put(int offset, intptr_t value) { ++ *ptr_at_addr(offset) = value; ++ } ++ ++ private: ++ // an additional field beyond _sp and _pc: ++ intptr_t* _fp; // frame pointer ++ // The interpreter and adapters will extend the frame of the caller. ++ // Since oopMaps are based on the sp of the caller before extension ++ // we need to know that value. However in order to compute the address ++ // of the return address we need the real "raw" sp. Since sparc already ++ // uses sp() to mean "raw" sp and unextended_sp() to mean the caller's ++ // original sp we use that convention. ++ ++ intptr_t* _unextended_sp; ++ void adjust_unextended_sp(); ++ ++ intptr_t* ptr_at_addr(int offset) const { ++ return (intptr_t*) addr_at(offset); ++ } ++#ifdef ASSERT ++ // Used in frame::sender_for_{interpreter,compiled}_frame ++ static void verify_deopt_original_pc( nmethod* nm, intptr_t* unextended_sp, bool is_method_handle_return = false); ++ static void verify_deopt_mh_original_pc(nmethod* nm, intptr_t* unextended_sp) { ++ verify_deopt_original_pc(nm, unextended_sp, true); ++ } ++#endif ++ ++ public: ++ // Constructors ++ ++ frame(intptr_t* sp, intptr_t* fp, address pc); ++ ++ frame(intptr_t* sp, intptr_t* unextended_sp, intptr_t* fp, address pc); ++ ++ frame(intptr_t* sp, intptr_t* fp); ++ ++ void init(intptr_t* sp, intptr_t* fp, address pc); ++ ++ // accessors for the instance variables ++ intptr_t* fp() const { return _fp; } ++ ++ inline address* sender_pc_addr() const; ++ ++ // return address of param, zero origin index. ++ inline address* native_param_addr(int idx) const; ++ ++ // expression stack tos if we are nested in a java call ++ intptr_t* interpreter_frame_last_sp() const; ++ ++ // helper to update a map with callee-saved FP ++ static void update_map_with_saved_link(RegisterMap* map, intptr_t** link_addr); ++ ++#ifndef CC_INTERP ++ // deoptimization support ++ void interpreter_frame_set_last_sp(intptr_t* sp); ++#endif // CC_INTERP ++ ++#ifdef CC_INTERP ++ inline interpreterState get_interpreterState() const; ++#endif // CC_INTERP ++ ++#endif // CPU_LOONGARCH_VM_FRAME_LOONGARCH_HPP +diff --git a/hotspot/src/cpu/loongarch/vm/frame_loongarch.inline.hpp b/hotspot/src/cpu/loongarch/vm/frame_loongarch.inline.hpp +new file mode 100644 +index 0000000000..3d22339ad7 +--- /dev/null ++++ b/hotspot/src/cpu/loongarch/vm/frame_loongarch.inline.hpp +@@ -0,0 +1,312 @@ ++/* ++ * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2015, 2020, Loongson Technology. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ * ++ */ ++ ++#ifndef CPU_LOONGARCH_VM_FRAME_LOONGARCH_INLINE_HPP ++#define CPU_LOONGARCH_VM_FRAME_LOONGARCH_INLINE_HPP ++ ++#include "code/codeCache.hpp" ++ ++// Inline functions for Loongson frames: ++ ++// Constructors: ++ ++inline frame::frame() { ++ _pc = NULL; ++ _sp = NULL; ++ _unextended_sp = NULL; ++ _fp = NULL; ++ _cb = NULL; ++ _deopt_state = unknown; ++} ++ ++inline void frame::init(intptr_t* sp, intptr_t* fp, address pc) { ++ _sp = sp; ++ _unextended_sp = sp; ++ _fp = fp; ++ _pc = pc; ++ assert(pc != NULL, "no pc?"); ++ _cb = CodeCache::find_blob(pc); ++ adjust_unextended_sp(); ++ ++ address original_pc = nmethod::get_deopt_original_pc(this); ++ if (original_pc != NULL) { ++ _pc = original_pc; ++ _deopt_state = is_deoptimized; ++ } else { ++ _deopt_state = not_deoptimized; ++ } ++} ++ ++inline frame::frame(intptr_t* sp, intptr_t* fp, address pc) { ++ init(sp, fp, pc); ++} ++ ++inline frame::frame(intptr_t* sp, intptr_t* unextended_sp, intptr_t* fp, address pc) { ++ _sp = sp; ++ _unextended_sp = unextended_sp; ++ _fp = fp; ++ _pc = pc; ++ assert(pc != NULL, "no pc?"); ++ _cb = CodeCache::find_blob(pc); ++ adjust_unextended_sp(); ++ ++ address original_pc = nmethod::get_deopt_original_pc(this); ++ if (original_pc != NULL) { ++ _pc = original_pc; ++ _deopt_state = is_deoptimized; ++ } else { ++ _deopt_state = not_deoptimized; ++ } ++} ++ ++inline frame::frame(intptr_t* sp, intptr_t* fp) { ++ _sp = sp; ++ _unextended_sp = sp; ++ _fp = fp; ++ _pc = (address)(sp[-1]); ++ ++ // Here's a sticky one. This constructor can be called via AsyncGetCallTrace ++ // when last_Java_sp is non-null but the pc fetched is junk. If we are truly ++ // unlucky the junk value could be to a zombied method and we'll die on the ++ // find_blob call. This is also why we can have no asserts on the validity ++ // of the pc we find here. AsyncGetCallTrace -> pd_get_top_frame_for_signal_handler ++ // -> pd_last_frame should use a specialized version of pd_last_frame which could ++ // call a specilaized frame constructor instead of this one. ++ // Then we could use the assert below. However this assert is of somewhat dubious ++ // value. ++ // assert(_pc != NULL, "no pc?"); ++ ++ _cb = CodeCache::find_blob(_pc); ++ adjust_unextended_sp(); ++ address original_pc = nmethod::get_deopt_original_pc(this); ++ if (original_pc != NULL) { ++ _pc = original_pc; ++ _deopt_state = is_deoptimized; ++ } else { ++ _deopt_state = not_deoptimized; ++ } ++} ++ ++// Accessors ++ ++inline bool frame::equal(frame other) const { ++ bool ret = sp() == other.sp() ++ && unextended_sp() == other.unextended_sp() ++ && fp() == other.fp() ++ && pc() == other.pc(); ++ assert(!ret || ret && cb() == other.cb() && _deopt_state == other._deopt_state, "inconsistent construction"); ++ return ret; ++} ++ ++// Return unique id for this frame. The id must have a value where we can distinguish ++// identity and younger/older relationship. NULL represents an invalid (incomparable) ++// frame. ++inline intptr_t* frame::id(void) const { return unextended_sp(); } ++ ++// Relationals on frames based ++// Return true if the frame is younger (more recent activation) than the frame represented by id ++inline bool frame::is_younger(intptr_t* id) const { assert(this->id() != NULL && id != NULL, "NULL frame id"); ++ return this->id() < id ; } ++ ++// Return true if the frame is older (less recent activation) than the frame represented by id ++inline bool frame::is_older(intptr_t* id) const { assert(this->id() != NULL && id != NULL, "NULL frame id"); ++ return this->id() > id ; } ++ ++ ++ ++inline intptr_t* frame::link() const { return (intptr_t*) *(intptr_t **)addr_at(link_offset); } ++inline void frame::set_link(intptr_t* addr) { *(intptr_t **)addr_at(link_offset) = addr; } ++ ++ ++inline intptr_t* frame::unextended_sp() const { return _unextended_sp; } ++ ++// Return address: ++ ++inline address* frame::sender_pc_addr() const { return (address*) addr_at( return_addr_offset); } ++inline address frame::sender_pc() const { return *sender_pc_addr(); } ++ ++// return address of param, zero origin index. ++inline address* frame::native_param_addr(int idx) const { return (address*) addr_at( native_frame_initial_param_offset+idx); } ++ ++#ifdef CC_INTERP ++ ++inline interpreterState frame::get_interpreterState() const { ++ return ((interpreterState)addr_at( -sizeof(BytecodeInterpreter)/wordSize )); ++} ++ ++inline intptr_t* frame::sender_sp() const { ++ // Hmm this seems awfully expensive QQQ, is this really called with interpreted frames? ++ if (is_interpreted_frame()) { ++ assert(false, "should never happen"); ++ return get_interpreterState()->sender_sp(); ++ } else { ++ return addr_at(sender_sp_offset); ++ } ++} ++ ++inline intptr_t** frame::interpreter_frame_locals_addr() const { ++ assert(is_interpreted_frame(), "must be interpreted"); ++ return &(get_interpreterState()->_locals); ++} ++ ++inline intptr_t* frame::interpreter_frame_bcx_addr() const { ++ assert(is_interpreted_frame(), "must be interpreted"); ++ return (intptr_t*) &(get_interpreterState()->_bcp); ++} ++ ++ ++// Constant pool cache ++ ++inline ConstantPoolCache** frame::interpreter_frame_cache_addr() const { ++ assert(is_interpreted_frame(), "must be interpreted"); ++ return &(get_interpreterState()->_constants); ++} ++ ++// Method ++ ++inline Method** frame::interpreter_frame_method_addr() const { ++ assert(is_interpreted_frame(), "must be interpreted"); ++ return &(get_interpreterState()->_method); ++} ++ ++inline intptr_t* frame::interpreter_frame_mdx_addr() const { ++ assert(is_interpreted_frame(), "must be interpreted"); ++ return (intptr_t*) &(get_interpreterState()->_mdx); ++} ++ ++// top of expression stack ++inline intptr_t* frame::interpreter_frame_tos_address() const { ++ assert(is_interpreted_frame(), "wrong frame type"); ++ return get_interpreterState()->_stack + 1; ++} ++ ++#else // asm interpreter ++inline intptr_t* frame::sender_sp() const { return addr_at( sender_sp_offset); } ++ ++inline intptr_t** frame::interpreter_frame_locals_addr() const { ++ return (intptr_t**)addr_at(interpreter_frame_locals_offset); ++} ++ ++inline intptr_t* frame::interpreter_frame_last_sp() const { ++ return *(intptr_t**)addr_at(interpreter_frame_last_sp_offset); ++} ++ ++inline intptr_t* frame::interpreter_frame_bcx_addr() const { ++ return (intptr_t*)addr_at(interpreter_frame_bcx_offset); ++} ++ ++ ++inline intptr_t* frame::interpreter_frame_mdx_addr() const { ++ return (intptr_t*)addr_at(interpreter_frame_mdx_offset); ++} ++ ++ ++ ++// Constant pool cache ++ ++inline ConstantPoolCache** frame::interpreter_frame_cache_addr() const { ++ return (ConstantPoolCache**)addr_at(interpreter_frame_cache_offset); ++} ++ ++// Method ++ ++inline Method** frame::interpreter_frame_method_addr() const { ++ return (Method**)addr_at(interpreter_frame_method_offset); ++} ++ ++// top of expression stack ++inline intptr_t* frame::interpreter_frame_tos_address() const { ++ intptr_t* last_sp = interpreter_frame_last_sp(); ++ if (last_sp == NULL ) { ++ return sp(); ++ } else { ++ // sp() may have been extended by an adapter ++ assert(last_sp <= (intptr_t*)interpreter_frame_monitor_end(), "bad tos"); ++ return last_sp; ++ } ++} ++ ++inline oop* frame::interpreter_frame_temp_oop_addr() const { ++ return (oop *)(fp() + interpreter_frame_oop_temp_offset); ++} ++ ++#endif // CC_INTERP ++ ++inline int frame::pd_oop_map_offset_adjustment() const { ++ return 0; ++} ++ ++inline int frame::interpreter_frame_monitor_size() { ++ return BasicObjectLock::size(); ++} ++ ++ ++// expression stack ++// (the max_stack arguments are used by the GC; see class FrameClosure) ++ ++inline intptr_t* frame::interpreter_frame_expression_stack() const { ++ intptr_t* monitor_end = (intptr_t*) interpreter_frame_monitor_end(); ++ return monitor_end-1; ++} ++ ++ ++inline jint frame::interpreter_frame_expression_stack_direction() { return -1; } ++ ++ ++// Entry frames ++ ++inline JavaCallWrapper** frame::entry_frame_call_wrapper_addr() const { ++ return (JavaCallWrapper**)addr_at(entry_frame_call_wrapper_offset); ++} ++ ++// Compiled frames ++ ++inline int frame::local_offset_for_compiler(int local_index, int nof_args, int max_nof_locals, int max_nof_monitors) { ++ return (nof_args - local_index + (local_index < nof_args ? 1: -1)); ++} ++ ++inline int frame::monitor_offset_for_compiler(int local_index, int nof_args, int max_nof_locals, int max_nof_monitors) { ++ return local_offset_for_compiler(local_index, nof_args, max_nof_locals, max_nof_monitors); ++} ++ ++inline int frame::min_local_offset_for_compiler(int nof_args, int max_nof_locals, int max_nof_monitors) { ++ return (nof_args - (max_nof_locals + max_nof_monitors*2) - 1); ++} ++ ++inline bool frame::volatile_across_calls(Register reg) { ++ return true; ++} ++ ++ ++ ++inline oop frame::saved_oop_result(RegisterMap* map) const { ++ return *((oop*) map->location(V0->as_VMReg())); ++} ++ ++inline void frame::set_saved_oop_result(RegisterMap* map, oop obj) { ++ *((oop*) map->location(V0->as_VMReg())) = obj; ++} ++ ++#endif // CPU_LOONGARCH_VM_FRAME_LOONGARCH_INLINE_HPP +diff --git a/hotspot/src/cpu/loongarch/vm/globalDefinitions_loongarch.hpp b/hotspot/src/cpu/loongarch/vm/globalDefinitions_loongarch.hpp +new file mode 100644 +index 0000000000..f9f93b9e65 +--- /dev/null ++++ b/hotspot/src/cpu/loongarch/vm/globalDefinitions_loongarch.hpp +@@ -0,0 +1,41 @@ ++/* ++ * Copyright (c) 1999, 2013, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2015, 2020, Loongson Technology. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ * ++ */ ++ ++#ifndef CPU_LOONGARCH_VM_GLOBALDEFINITIONS_LOONGARCH_HPP ++#define CPU_LOONGARCH_VM_GLOBALDEFINITIONS_LOONGARCH_HPP ++// Size of LoongArch Instructions ++const int BytesPerInstWord = 4; ++ ++const int StackAlignmentInBytes = (2*wordSize); ++ ++// Indicates whether the C calling conventions require that ++// 32-bit integer argument values are properly extended to 64 bits. ++// If set, SharedRuntime::c_calling_convention() must adapt ++// signatures accordingly. ++const bool CCallingConventionRequiresIntsAsLongs = false; ++ ++#define SUPPORTS_NATIVE_CX8 ++ ++#endif // CPU_LOONGARCH_VM_GLOBALDEFINITIONS_LOONGARCH_HPP +diff --git a/hotspot/src/cpu/loongarch/vm/globals_loongarch.hpp b/hotspot/src/cpu/loongarch/vm/globals_loongarch.hpp +new file mode 100644 +index 0000000000..182be608a3 +--- /dev/null ++++ b/hotspot/src/cpu/loongarch/vm/globals_loongarch.hpp +@@ -0,0 +1,103 @@ ++/* ++ * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2015, 2022, Loongson Technology. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ * ++ */ ++ ++#ifndef CPU_LOONGARCH_VM_GLOBALS_LOONGARCH_HPP ++#define CPU_LOONGARCH_VM_GLOBALS_LOONGARCH_HPP ++ ++#include "utilities/globalDefinitions.hpp" ++#include "utilities/macros.hpp" ++ ++// Sets the default values for platform dependent flags used by the runtime system. ++// (see globals.hpp) ++ ++#ifdef CORE ++define_pd_global(bool, UseSSE, 0); ++#endif /* CORE */ ++define_pd_global(bool, ConvertSleepToYield, true); ++define_pd_global(bool, ShareVtableStubs, true); ++define_pd_global(bool, CountInterpCalls, true); ++ ++define_pd_global(bool, ImplicitNullChecks, true); // Generate code for implicit null checks ++define_pd_global(bool, TrapBasedNullChecks, false); // Not needed on x86. ++define_pd_global(bool, UncommonNullCast, true); // Uncommon-trap NULLs passed to check cast ++define_pd_global(bool, NeedsDeoptSuspend, false); // only register window machines need this ++ ++define_pd_global(intx, CodeEntryAlignment, 16); ++define_pd_global(intx, OptoLoopAlignment, 16); ++define_pd_global(intx, InlineFrequencyCount, 100); ++define_pd_global(intx, InlineSmallCode, 2000); ++ ++define_pd_global(uintx, TLABSize, 0); ++define_pd_global(uintx, NewSize, 1024 * K); ++define_pd_global(intx, PreInflateSpin, 10); ++ ++define_pd_global(intx, PrefetchFieldsAhead, -1); ++ ++define_pd_global(intx, StackYellowPages, 2); ++define_pd_global(intx, StackRedPages, 1); ++define_pd_global(intx, StackShadowPages, 3 DEBUG_ONLY(+1)); ++ ++define_pd_global(bool, RewriteBytecodes, true); ++define_pd_global(bool, RewriteFrequentPairs, true); ++define_pd_global(bool, UseMembar, true); ++// GC Ergo Flags ++define_pd_global(intx, CMSYoungGenPerWorker, 64*M); // default max size of CMS young gen, per GC worker thread ++ ++define_pd_global(uintx, TypeProfileLevel, 111); ++ ++define_pd_global(bool, PreserveFramePointer, false); ++// Only c2 cares about this at the moment ++define_pd_global(intx, AllocatePrefetchStyle, 2); ++define_pd_global(intx, AllocatePrefetchDistance, -1); ++ ++#define ARCH_FLAGS(develop, product, diagnostic, experimental, notproduct) \ ++ \ ++ product(bool, UseCodeCacheAllocOpt, true, \ ++ "Allocate code cache within 32-bit memory address space") \ ++ \ ++ product(bool, UseLSX, false, \ ++ "Use LSX 128-bit vector instructions") \ ++ \ ++ product(bool, UseLASX, false, \ ++ "Use LASX 256-bit vector instructions") \ ++ \ ++ product(intx, UseSyncLevel, 10000, \ ++ "The sync level on Loongson CPUs" \ ++ "UseSyncLevel == 10000, 111, for all Loongson CPUs, " \ ++ "UseSyncLevel == 4000, 101, maybe for GS464V" \ ++ "UseSyncLevel == 3000, 001, maybe for GS464V" \ ++ "UseSyncLevel == 2000, 011, maybe for GS464E/GS264" \ ++ "UseSyncLevel == 1000, 110, maybe for GS464") \ ++ \ ++ product(bool, UseUnalignedAccesses, false, \ ++ "Use unaligned memory accesses in Unsafe") \ ++ \ ++ product(bool, UseCRC32, false, \ ++ "Use CRC32 instructions for CRC32 computation") \ ++ \ ++ product(bool, UseActiveCoresMP, false, \ ++ "Eliminate barriers for single active cpu") ++ ++#endif // CPU_LOONGARCH_VM_GLOBALS_LOONGARCH_HPP +diff --git a/hotspot/src/cpu/loongarch/vm/icBuffer_loongarch.cpp b/hotspot/src/cpu/loongarch/vm/icBuffer_loongarch.cpp +new file mode 100644 +index 0000000000..8c78225346 +--- /dev/null ++++ b/hotspot/src/cpu/loongarch/vm/icBuffer_loongarch.cpp +@@ -0,0 +1,101 @@ ++/* ++ * Copyright (c) 1997, 2012, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2015, 2021, Loongson Technology. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ * ++ */ ++ ++#include "precompiled.hpp" ++#include "asm/macroAssembler.hpp" ++#include "asm/macroAssembler.inline.hpp" ++#include "code/icBuffer.hpp" ++#include "gc_interface/collectedHeap.inline.hpp" ++#include "interpreter/bytecodes.hpp" ++#include "memory/resourceArea.hpp" ++#include "nativeInst_loongarch.hpp" ++#include "oops/oop.inline.hpp" ++#include "oops/oop.inline2.hpp" ++ ++#define A0 RA0 ++#define A1 RA1 ++#define A2 RA2 ++#define A3 RA3 ++#define A4 RA4 ++#define A5 RA5 ++#define A6 RA6 ++#define A7 RA7 ++#define T0 RT0 ++#define T1 RT1 ++#define T2 RT2 ++#define T3 RT3 ++#define T4 RT4 ++#define T5 RT5 ++#define T6 RT6 ++#define T7 RT7 ++#define T8 RT8 ++ ++int InlineCacheBuffer::ic_stub_code_size() { ++ return NativeMovConstReg::instruction_size + ++ NativeGeneralJump::instruction_size + ++ 1; ++ // so that code_end can be set in CodeBuffer ++ // 64bit 15 = 6 + 8 bytes + 1 byte ++ // 32bit 7 = 2 + 4 bytes + 1 byte ++} ++ ++ ++// we use T1 as cached oop(klass) now. this is the target of virtual call, ++// when reach here, the receiver in T0 ++// refer to shareRuntime_loongarch.cpp,gen_i2c2i_adapters ++void InlineCacheBuffer::assemble_ic_buffer_code(address code_begin, void* cached_value, ++ address entry_point) { ++ ResourceMark rm; ++ CodeBuffer code(code_begin, ic_stub_code_size()); ++ MacroAssembler* masm = new MacroAssembler(&code); ++ // note: even though the code contains an embedded oop, we do not need reloc info ++ // because ++ // (1) the oop is old (i.e., doesn't matter for scavenges) ++ // (2) these ICStubs are removed *before* a GC happens, so the roots disappear ++ // assert(cached_oop == NULL || cached_oop->is_perm(), "must be perm oop"); ++#define __ masm-> ++ __ patchable_li52(T1, (long)cached_value); ++ // TODO: confirm reloc ++ __ jmp(entry_point, relocInfo::runtime_call_type); ++ __ flush(); ++#undef __ ++} ++ ++ ++address InlineCacheBuffer::ic_buffer_entry_point(address code_begin) { ++ NativeMovConstReg* move = nativeMovConstReg_at(code_begin); // creation also verifies the object ++ NativeGeneralJump* jump = nativeGeneralJump_at(move->next_instruction_address()); ++ return jump->jump_destination(); ++} ++ ++ ++void* InlineCacheBuffer::ic_buffer_cached_value(address code_begin) { ++ // creation also verifies the object ++ NativeMovConstReg* move = nativeMovConstReg_at(code_begin); ++ // Verifies the jump ++ NativeGeneralJump* jump = nativeGeneralJump_at(move->next_instruction_address()); ++ void* o= (void*)move->data(); ++ return o; ++} +diff --git a/hotspot/src/cpu/loongarch/vm/icache_loongarch.cpp b/hotspot/src/cpu/loongarch/vm/icache_loongarch.cpp +new file mode 100644 +index 0000000000..d577e41f59 +--- /dev/null ++++ b/hotspot/src/cpu/loongarch/vm/icache_loongarch.cpp +@@ -0,0 +1,42 @@ ++/* ++ * Copyright (c) 1997, 2010, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2015, 2021, Loongson Technology. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ * ++ */ ++ ++#include "precompiled.hpp" ++#include "asm/macroAssembler.hpp" ++#include "runtime/icache.hpp" ++ ++void ICacheStubGenerator::generate_icache_flush(ICache::flush_icache_stub_t* flush_icache_stub) ++{ ++#define __ _masm-> ++ StubCodeMark mark(this, "ICache", "flush_icache_stub"); ++ address start = __ pc(); ++ ++ __ ibar(0); ++ __ ori(V0, RA2, 0); ++ __ jr(RA); ++ ++ *flush_icache_stub = (ICache::flush_icache_stub_t)start; ++#undef __ ++} +diff --git a/hotspot/src/cpu/loongarch/vm/icache_loongarch.hpp b/hotspot/src/cpu/loongarch/vm/icache_loongarch.hpp +new file mode 100644 +index 0000000000..15e45cb350 +--- /dev/null ++++ b/hotspot/src/cpu/loongarch/vm/icache_loongarch.hpp +@@ -0,0 +1,41 @@ ++/* ++ * Copyright (c) 1997, 2010, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2015, 2021, Loongson Technology. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ * ++ */ ++ ++#ifndef CPU_LOONGARCH_VM_ICACHE_LOONGARCH_HPP ++#define CPU_LOONGARCH_VM_ICACHE_LOONGARCH_HPP ++ ++// Interface for updating the instruction cache. Whenever the VM modifies ++// code, part of the processor instruction cache potentially has to be flushed. ++ ++class ICache : public AbstractICache { ++ public: ++ enum { ++ stub_size = 3 * BytesPerInstWord, // Size of the icache flush stub in bytes ++ line_size = 32, // flush instruction affects a dword ++ log2_line_size = 5 // log2(line_size) ++ }; ++}; ++ ++#endif // CPU_LOONGARCH_VM_ICACHE_LOONGARCH_HPP +diff --git a/hotspot/src/cpu/loongarch/vm/interp_masm_loongarch_64.cpp b/hotspot/src/cpu/loongarch/vm/interp_masm_loongarch_64.cpp +new file mode 100644 +index 0000000000..8c84f21511 +--- /dev/null ++++ b/hotspot/src/cpu/loongarch/vm/interp_masm_loongarch_64.cpp +@@ -0,0 +1,1960 @@ ++/* ++ * Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2015, 2021, Loongson Technology. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ * ++ */ ++ ++#include "precompiled.hpp" ++#include "interp_masm_loongarch_64.hpp" ++#include "interpreter/interpreter.hpp" ++#include "interpreter/interpreterRuntime.hpp" ++#include "oops/arrayOop.hpp" ++#include "oops/markOop.hpp" ++#include "oops/methodData.hpp" ++#include "oops/method.hpp" ++#include "prims/jvmtiExport.hpp" ++#include "prims/jvmtiRedefineClassesTrace.hpp" ++#include "prims/jvmtiThreadState.hpp" ++#include "runtime/basicLock.hpp" ++#include "runtime/biasedLocking.hpp" ++#include "runtime/sharedRuntime.hpp" ++#include "runtime/thread.inline.hpp" ++ ++#define A0 RA0 ++#define A1 RA1 ++#define A2 RA2 ++#define A3 RA3 ++#define A4 RA4 ++#define A5 RA5 ++#define A6 RA6 ++#define A7 RA7 ++#define T0 RT0 ++#define T1 RT1 ++#define T2 RT2 ++#define T3 RT3 ++#define T4 RT4 ++#define T5 RT5 ++#define T6 RT6 ++#define T7 RT7 ++#define T8 RT8 ++ ++// Implementation of InterpreterMacroAssembler ++ ++#ifdef CC_INTERP ++void InterpreterMacroAssembler::get_method(Register reg) { ++} ++#endif // CC_INTERP ++ ++void InterpreterMacroAssembler::get_2_byte_integer_at_bcp(Register reg, Register tmp, int offset) { ++ if (UseUnalignedAccesses) { ++ ld_hu(reg, BCP, offset); ++ } else { ++ ld_bu(reg, BCP, offset); ++ ld_bu(tmp, BCP, offset + 1); ++ bstrins_d(reg, tmp, 15, 8); ++ } ++} ++ ++void InterpreterMacroAssembler::get_4_byte_integer_at_bcp(Register reg, int offset) { ++ if (UseUnalignedAccesses) { ++ ld_wu(reg, BCP, offset); ++ } else { ++ ldr_w(reg, BCP, offset); ++ ldl_w(reg, BCP, offset + 3); ++ lu32i_d(reg, 0); ++ } ++} ++ ++#ifndef CC_INTERP ++ ++void InterpreterMacroAssembler::call_VM_leaf_base(address entry_point, ++ int number_of_arguments) { ++ // interpreter specific ++ // ++ // Note: No need to save/restore bcp & locals pointer ++ // since these are callee saved registers and no blocking/ ++ // GC can happen in leaf calls. ++ // Further Note: DO NOT save/restore bcp/locals. If a caller has ++ // already saved them so that it can use BCP/LVP as temporaries ++ // then a save/restore here will DESTROY the copy the caller ++ // saved! There used to be a save_bcp() that only happened in ++ // the ASSERT path (no restore_bcp). Which caused bizarre failures ++ // when jvm built with ASSERTs. ++#ifdef ASSERT ++ save_bcp(); ++ { ++ Label L; ++ ld_d(AT,FP,frame::interpreter_frame_last_sp_offset * wordSize); ++ beq(AT,R0,L); ++ stop("InterpreterMacroAssembler::call_VM_leaf_base: last_sp != NULL"); ++ bind(L); ++ } ++#endif ++ // super call ++ MacroAssembler::call_VM_leaf_base(entry_point, number_of_arguments); ++ // interpreter specific ++ // Used to ASSERT that BCP/LVP were equal to frame's bcp/locals ++ // but since they may not have been saved (and we don't want to ++ // save them here (see note above) the assert is invalid. ++} ++ ++void InterpreterMacroAssembler::call_VM_base(Register oop_result, ++ Register java_thread, ++ Register last_java_sp, ++ address entry_point, ++ int number_of_arguments, ++ bool check_exceptions) { ++ // interpreter specific ++ // ++ // Note: Could avoid restoring locals ptr (callee saved) - however doesn't ++ // really make a difference for these runtime calls, since they are ++ // slow anyway. Btw., bcp must be saved/restored since it may change ++ // due to GC. ++ assert(java_thread == noreg , "not expecting a precomputed java thread"); ++ save_bcp(); ++#ifdef ASSERT ++ { ++ Label L; ++ ld_d(AT, FP, frame::interpreter_frame_last_sp_offset * wordSize); ++ beq(AT, R0, L); ++ stop("InterpreterMacroAssembler::call_VM_base: last_sp != NULL"); ++ bind(L); ++ } ++#endif /* ASSERT */ ++ // super call ++ MacroAssembler::call_VM_base(oop_result, java_thread, last_java_sp, ++ entry_point, number_of_arguments, ++ check_exceptions); ++ // interpreter specific ++ restore_bcp(); ++ restore_locals(); ++} ++ ++ ++void InterpreterMacroAssembler::check_and_handle_popframe(Register java_thread) { ++ if (JvmtiExport::can_pop_frame()) { ++ Label L; ++ // Initiate popframe handling only if it is not already being ++ // processed. If the flag has the popframe_processing bit set, it ++ // means that this code is called *during* popframe handling - we ++ // don't want to reenter. ++ // This method is only called just after the call into the vm in ++ // call_VM_base, so the arg registers are available. ++ // Not clear if any other register is available, so load AT twice ++ assert(AT != java_thread, "check"); ++ ld_w(AT, java_thread, in_bytes(JavaThread::popframe_condition_offset())); ++ andi(AT, AT, JavaThread::popframe_pending_bit); ++ beq(AT, R0, L); ++ ++ ld_w(AT, java_thread, in_bytes(JavaThread::popframe_condition_offset())); ++ andi(AT, AT, JavaThread::popframe_processing_bit); ++ bne(AT, R0, L); ++ call_VM_leaf(CAST_FROM_FN_PTR(address, Interpreter::remove_activation_preserving_args_entry)); ++ jr(V0); ++ bind(L); ++ } ++} ++ ++ ++void InterpreterMacroAssembler::load_earlyret_value(TosState state) { ++ Register thread = T8; ++#ifndef OPT_THREAD ++ get_thread(thread); ++#else ++ move(T8, TREG); ++#endif ++ ld_ptr(thread, thread, in_bytes(JavaThread::jvmti_thread_state_offset())); ++ const Address tos_addr (thread, in_bytes(JvmtiThreadState::earlyret_tos_offset())); ++ const Address oop_addr (thread, in_bytes(JvmtiThreadState::earlyret_oop_offset())); ++ const Address val_addr (thread, in_bytes(JvmtiThreadState::earlyret_value_offset())); ++ //V0, oop_addr,V1,val_addr ++ switch (state) { ++ case atos: ++ ld_ptr(V0, oop_addr); ++ st_ptr(R0, oop_addr); ++ verify_oop(V0, state); ++ break; ++ case ltos: ++ ld_ptr(V0, val_addr); // fall through ++ break; ++ case btos: // fall through ++ case ztos: // fall through ++ case ctos: // fall through ++ case stos: // fall through ++ case itos: ++ ld_w(V0, val_addr); ++ break; ++ case ftos: ++ fld_s(F0, thread, in_bytes(JvmtiThreadState::earlyret_value_offset())); ++ break; ++ case dtos: ++ fld_d(F0, thread, in_bytes(JvmtiThreadState::earlyret_value_offset())); ++ break; ++ case vtos: /* nothing to do */ break; ++ default : ShouldNotReachHere(); ++ } ++ // Clean up tos value in the thread object ++ li(AT, (int)ilgl); ++ st_w(AT, tos_addr); ++ st_w(R0, thread, in_bytes(JvmtiThreadState::earlyret_value_offset())); ++} ++ ++ ++void InterpreterMacroAssembler::check_and_handle_earlyret(Register java_thread) { ++ if (JvmtiExport::can_force_early_return()) { ++ Label L; ++ Register tmp = T4; ++ ++ assert(java_thread != AT, "check"); ++ assert(java_thread != tmp, "check"); ++ ld_ptr(AT, java_thread, in_bytes(JavaThread::jvmti_thread_state_offset())); ++ beq(AT, R0, L); ++ ++ // Initiate earlyret handling only if it is not already being processed. ++ // If the flag has the earlyret_processing bit set, it means that this code ++ // is called *during* earlyret handling - we don't want to reenter. ++ ld_w(AT, AT, in_bytes(JvmtiThreadState::earlyret_state_offset())); ++ li(tmp, JvmtiThreadState::earlyret_pending); ++ bne(tmp, AT, L); ++ ++ // Call Interpreter::remove_activation_early_entry() to get the address of the ++ // same-named entrypoint in the generated interpreter code. ++ ld_ptr(tmp, java_thread, in_bytes(JavaThread::jvmti_thread_state_offset())); ++ ld_w(AT, tmp, in_bytes(JvmtiThreadState::earlyret_tos_offset())); ++ move(A0, AT); ++ call_VM_leaf(CAST_FROM_FN_PTR(address, Interpreter::remove_activation_early_entry), A0); ++ jr(V0); ++ bind(L); ++ } ++} ++ ++ ++void InterpreterMacroAssembler::get_unsigned_2_byte_index_at_bcp(Register reg, ++ int bcp_offset) { ++ assert(bcp_offset >= 0, "bcp is still pointing to start of bytecode"); ++ ld_bu(AT, BCP, bcp_offset); ++ ld_bu(reg, BCP, bcp_offset + 1); ++ bstrins_w(reg, AT, 15, 8); ++} ++ ++ ++void InterpreterMacroAssembler::get_cache_index_at_bcp(Register index, ++ int bcp_offset, ++ size_t index_size) { ++ assert(bcp_offset > 0, "bcp is still pointing to start of bytecode"); ++ if (index_size == sizeof(u2)) { ++ get_2_byte_integer_at_bcp(index, AT, bcp_offset); ++ } else if (index_size == sizeof(u4)) { ++ assert(EnableInvokeDynamic, "giant index used only for JSR 292"); ++ get_4_byte_integer_at_bcp(index, bcp_offset); ++ // Check if the secondary index definition is still ~x, otherwise ++ // we have to change the following assembler code to calculate the ++ // plain index. ++ assert(ConstantPool::decode_invokedynamic_index(~123) == 123, "else change next line"); ++ nor(index, index, R0); ++ slli_w(index, index, 0); ++ } else if (index_size == sizeof(u1)) { ++ ld_bu(index, BCP, bcp_offset); ++ } else { ++ ShouldNotReachHere(); ++ } ++} ++ ++ ++void InterpreterMacroAssembler::get_cache_and_index_at_bcp(Register cache, ++ Register index, ++ int bcp_offset, ++ size_t index_size) { ++ assert_different_registers(cache, index); ++ get_cache_index_at_bcp(index, bcp_offset, index_size); ++ ld_d(cache, FP, frame::interpreter_frame_cache_offset * wordSize); ++ assert(sizeof(ConstantPoolCacheEntry) == 4 * wordSize, "adjust code below"); ++ assert(exact_log2(in_words(ConstantPoolCacheEntry::size())) == 2, "else change next line"); ++ shl(index, 2); ++} ++ ++ ++void InterpreterMacroAssembler::get_cache_and_index_and_bytecode_at_bcp(Register cache, ++ Register index, ++ Register bytecode, ++ int byte_no, ++ int bcp_offset, ++ size_t index_size) { ++ get_cache_and_index_at_bcp(cache, index, bcp_offset, index_size); ++ // We use a 32-bit load here since the layout of 64-bit words on ++ // little-endian machines allow us that. ++ alsl_d(AT, index, cache, Address::times_ptr - 1); ++ ld_w(bytecode, AT, in_bytes(ConstantPoolCache::base_offset() + ConstantPoolCacheEntry::indices_offset())); ++ if(os::is_MP()) { ++ membar(Assembler::Membar_mask_bits(LoadLoad|LoadStore)); ++ } ++ ++ const int shift_count = (1 + byte_no) * BitsPerByte; ++ assert((byte_no == TemplateTable::f1_byte && shift_count == ConstantPoolCacheEntry::bytecode_1_shift) || ++ (byte_no == TemplateTable::f2_byte && shift_count == ConstantPoolCacheEntry::bytecode_2_shift), ++ "correct shift count"); ++ srli_d(bytecode, bytecode, shift_count); ++ assert(ConstantPoolCacheEntry::bytecode_1_mask == ConstantPoolCacheEntry::bytecode_2_mask, "common mask"); ++ li(AT, ConstantPoolCacheEntry::bytecode_1_mask); ++ andr(bytecode, bytecode, AT); ++} ++ ++void InterpreterMacroAssembler::get_cache_entry_pointer_at_bcp(Register cache, ++ Register tmp, ++ int bcp_offset, ++ size_t index_size) { ++ assert(bcp_offset > 0, "bcp is still pointing to start of bytecode"); ++ assert(cache != tmp, "must use different register"); ++ get_cache_index_at_bcp(tmp, bcp_offset, index_size); ++ assert(sizeof(ConstantPoolCacheEntry) == 4 * wordSize, "adjust code below"); ++ // convert from field index to ConstantPoolCacheEntry index ++ // and from word offset to byte offset ++ assert(exact_log2(in_bytes(ConstantPoolCacheEntry::size_in_bytes())) == 2 + LogBytesPerWord, "else change next line"); ++ shl(tmp, 2 + LogBytesPerWord); ++ ld_d(cache, FP, frame::interpreter_frame_cache_offset * wordSize); ++ // skip past the header ++ addi_d(cache, cache, in_bytes(ConstantPoolCache::base_offset())); ++ add_d(cache, cache, tmp); ++} ++ ++void InterpreterMacroAssembler::get_method_counters(Register method, ++ Register mcs, Label& skip) { ++ Label has_counters; ++ ld_d(mcs, method, in_bytes(Method::method_counters_offset())); ++ bne(mcs, R0, has_counters); ++ call_VM(noreg, CAST_FROM_FN_PTR(address, ++ InterpreterRuntime::build_method_counters), method); ++ ld_d(mcs, method, in_bytes(Method::method_counters_offset())); ++ beq(mcs, R0, skip); // No MethodCounters allocated, OutOfMemory ++ bind(has_counters); ++} ++ ++// Load object from cpool->resolved_references(index) ++void InterpreterMacroAssembler::load_resolved_reference_at_index( ++ Register result, Register index) { ++ assert_different_registers(result, index); ++ // convert from field index to resolved_references() index and from ++ // word index to byte offset. Since this is a java object, it can be compressed ++ Register tmp = index; // reuse ++ shl(tmp, LogBytesPerHeapOop); ++ ++ get_constant_pool(result); ++ // load pointer for resolved_references[] objArray ++ ld_d(result, result, ConstantPool::resolved_references_offset_in_bytes()); ++ // JNIHandles::resolve(obj); ++ ld_d(result, result, 0); //? is needed? ++ // Add in the index ++ add_d(result, result, tmp); ++ load_heap_oop(result, Address(result, arrayOopDesc::base_offset_in_bytes(T_OBJECT))); ++} ++ ++// Resets LVP to locals. Register sub_klass cannot be any of the above. ++void InterpreterMacroAssembler::gen_subtype_check( Register Rsup_klass, Register Rsub_klass, Label &ok_is_subtype ) { ++ ++ assert( Rsub_klass != Rsup_klass, "Rsup_klass holds superklass" ); ++ assert( Rsub_klass != T1, "T1 holds 2ndary super array length" ); ++ assert( Rsub_klass != T0, "T0 holds 2ndary super array scan ptr" ); ++ // Profile the not-null value's klass. ++ // Here T4 and T1 are used as temporary registers. ++ profile_typecheck(T4, Rsub_klass, T1); // blows T4, reloads T1 ++ ++ // Do the check. ++ check_klass_subtype(Rsub_klass, Rsup_klass, T1, ok_is_subtype); // blows T1 ++ ++ // Profile the failure of the check. ++ profile_typecheck_failed(T4); // blows T4 ++ ++} ++ ++ ++ ++// Java Expression Stack ++ ++void InterpreterMacroAssembler::pop_ptr(Register r) { ++ ld_d(r, SP, 0); ++ addi_d(SP, SP, Interpreter::stackElementSize); ++} ++ ++void InterpreterMacroAssembler::pop_i(Register r) { ++ ld_w(r, SP, 0); ++ addi_d(SP, SP, Interpreter::stackElementSize); ++} ++ ++void InterpreterMacroAssembler::pop_l(Register r) { ++ ld_d(r, SP, 0); ++ addi_d(SP, SP, 2 * Interpreter::stackElementSize); ++} ++ ++void InterpreterMacroAssembler::pop_f(FloatRegister r) { ++ fld_s(r, SP, 0); ++ addi_d(SP, SP, Interpreter::stackElementSize); ++} ++ ++void InterpreterMacroAssembler::pop_d(FloatRegister r) { ++ fld_d(r, SP, 0); ++ addi_d(SP, SP, 2 * Interpreter::stackElementSize); ++} ++ ++void InterpreterMacroAssembler::push_ptr(Register r) { ++ addi_d(SP, SP, - Interpreter::stackElementSize); ++ st_d(r, SP, 0); ++} ++ ++void InterpreterMacroAssembler::push_i(Register r) { ++ // For compatibility reason, don't change to sw. ++ addi_d(SP, SP, - Interpreter::stackElementSize); ++ st_d(r, SP, 0); ++} ++ ++void InterpreterMacroAssembler::push_l(Register r) { ++ addi_d(SP, SP, -2 * Interpreter::stackElementSize); ++ st_d(r, SP, 0); ++ st_d(R0, SP, Interpreter::stackElementSize); ++} ++ ++void InterpreterMacroAssembler::push_f(FloatRegister r) { ++ addi_d(SP, SP, - Interpreter::stackElementSize); ++ fst_s(r, SP, 0); ++} ++ ++void InterpreterMacroAssembler::push_d(FloatRegister r) { ++ addi_d(SP, SP, -2 * Interpreter::stackElementSize); ++ fst_d(r, SP, 0); ++ st_d(R0, SP, Interpreter::stackElementSize); ++} ++ ++void InterpreterMacroAssembler::pop(TosState state) { ++ switch (state) { ++ case atos: pop_ptr(); break; ++ case btos: ++ case ztos: ++ case ctos: ++ case stos: ++ case itos: pop_i(); break; ++ case ltos: pop_l(); break; ++ case ftos: pop_f(); break; ++ case dtos: pop_d(); break; ++ case vtos: /* nothing to do */ break; ++ default: ShouldNotReachHere(); ++ } ++ verify_oop(FSR, state); ++} ++ ++//FSR=V0,SSR=V1 ++void InterpreterMacroAssembler::push(TosState state) { ++ verify_oop(FSR, state); ++ switch (state) { ++ case atos: push_ptr(); break; ++ case btos: ++ case ztos: ++ case ctos: ++ case stos: ++ case itos: push_i(); break; ++ case ltos: push_l(); break; ++ case ftos: push_f(); break; ++ case dtos: push_d(); break; ++ case vtos: /* nothing to do */ break; ++ default : ShouldNotReachHere(); ++ } ++} ++ ++void InterpreterMacroAssembler::load_ptr(int n, Register val) { ++ ld_d(val, SP, Interpreter::expr_offset_in_bytes(n)); ++} ++ ++void InterpreterMacroAssembler::store_ptr(int n, Register val) { ++ st_d(val, SP, Interpreter::expr_offset_in_bytes(n)); ++} ++ ++// Jump to from_interpreted entry of a call unless single stepping is possible ++// in this thread in which case we must call the i2i entry ++void InterpreterMacroAssembler::jump_from_interpreted(Register method, Register temp) { ++ // record last_sp ++ move(Rsender, SP); ++ st_d(SP, FP, frame::interpreter_frame_last_sp_offset * wordSize); ++ ++ if (JvmtiExport::can_post_interpreter_events()) { ++ Label run_compiled_code; ++ // JVMTI events, such as single-stepping, are implemented partly by avoiding running ++ // compiled code in threads for which the event is enabled. Check here for ++ // interp_only_mode if these events CAN be enabled. ++#ifndef OPT_THREAD ++ get_thread(temp); ++#else ++ move(temp, TREG); ++#endif ++ // interp_only is an int, on little endian it is sufficient to test the byte only ++ // Is a cmpl faster? ++ ld_w(AT, temp, in_bytes(JavaThread::interp_only_mode_offset())); ++ beq(AT, R0, run_compiled_code); ++ ld_d(AT, method, in_bytes(Method::interpreter_entry_offset())); ++ jr(AT); ++ bind(run_compiled_code); ++ } ++ ++ ld_d(AT, method, in_bytes(Method::from_interpreted_offset())); ++ jr(AT); ++} ++ ++ ++// The following two routines provide a hook so that an implementation ++// can schedule the dispatch in two parts. LoongArch64 does not do this. ++void InterpreterMacroAssembler::dispatch_prolog(TosState state, int step) { ++ // Nothing LoongArch64 specific to be done here ++} ++ ++void InterpreterMacroAssembler::dispatch_epilog(TosState state, int step) { ++ dispatch_next(state, step); ++} ++ ++// assume the next bytecode in T8. ++void InterpreterMacroAssembler::dispatch_base(TosState state, ++ address* table, ++ bool verifyoop) { ++ if (VerifyActivationFrameSize) { ++ Label L; ++ ++ sub_d(T2, FP, SP); ++ int min_frame_size = (frame::link_offset - ++ frame::interpreter_frame_initial_sp_offset) * wordSize; ++ addi_d(T2, T2, -min_frame_size); ++ bge(T2, R0, L); ++ stop("broken stack frame"); ++ bind(L); ++ } ++ // FIXME: I do not know which register should pass to verify_oop ++ if (verifyoop) verify_oop(FSR, state); ++ ++ if((long)table >= (long)Interpreter::dispatch_table(btos) && ++ (long)table <= (long)Interpreter::dispatch_table(vtos)) { ++ int table_size = (long)Interpreter::dispatch_table(itos) - ++ (long)Interpreter::dispatch_table(stos); ++ int table_offset = ((int)state - (int)itos) * table_size; ++ ++ // S8 points to the starting address of Interpreter::dispatch_table(itos). ++ // See StubGenerator::generate_call_stub(address& return_address) for the initialization of S8. ++ if (table_offset != 0) { ++ if (is_simm(table_offset, 12)) { ++ alsl_d(T3, Rnext, S8, LogBytesPerWord - 1); ++ ld_d(T3, T3, table_offset); ++ } else { ++ li(T2, table_offset); ++ alsl_d(T3, Rnext, S8, LogBytesPerWord - 1); ++ ldx_d(T3, T2, T3); ++ } ++ } else { ++ slli_d(T2, Rnext, LogBytesPerWord); ++ ldx_d(T3, S8, T2); ++ } ++ } else { ++ li(T3, (long)table); ++ slli_d(T2, Rnext, LogBytesPerWord); ++ ldx_d(T3, T2, T3); ++ } ++ jr(T3); ++} ++ ++void InterpreterMacroAssembler::dispatch_only(TosState state) { ++ dispatch_base(state, Interpreter::dispatch_table(state)); ++} ++ ++void InterpreterMacroAssembler::dispatch_only_normal(TosState state) { ++ dispatch_base(state, Interpreter::normal_table(state)); ++} ++ ++void InterpreterMacroAssembler::dispatch_only_noverify(TosState state) { ++ dispatch_base(state, Interpreter::normal_table(state), false); ++} ++ ++ ++void InterpreterMacroAssembler::dispatch_next(TosState state, int step) { ++ // load next bytecode ++ ld_bu(Rnext, BCP, step); ++ increment(BCP, step); ++ dispatch_base(state, Interpreter::dispatch_table(state)); ++} ++ ++void InterpreterMacroAssembler::dispatch_via(TosState state, address* table) { ++ // load current bytecode ++ ld_bu(Rnext, BCP, 0); ++ dispatch_base(state, table); ++} ++ ++// remove activation ++// ++// Unlock the receiver if this is a synchronized method. ++// Unlock any Java monitors from syncronized blocks. ++// Remove the activation from the stack. ++// ++// If there are locked Java monitors ++// If throw_monitor_exception ++// throws IllegalMonitorStateException ++// Else if install_monitor_exception ++// installs IllegalMonitorStateException ++// Else ++// no error processing ++// used registers : T1, T2, T3, T8 ++// T1 : thread, method access flags ++// T2 : monitor entry pointer ++// T3 : method, monitor top ++// T8 : unlock flag ++void InterpreterMacroAssembler::remove_activation( ++ TosState state, ++ Register ret_addr, ++ bool throw_monitor_exception, ++ bool install_monitor_exception, ++ bool notify_jvmdi) { ++ // Note: Registers V0, V1 and F0, F1 may be in use for the result ++ // check if synchronized method ++ Label unlocked, unlock, no_unlock; ++ ++ // get the value of _do_not_unlock_if_synchronized into T8 ++#ifndef OPT_THREAD ++ Register thread = T1; ++ get_thread(thread); ++#else ++ Register thread = TREG; ++#endif ++ ld_b(T8, thread, in_bytes(JavaThread::do_not_unlock_if_synchronized_offset())); ++ // reset the flag ++ st_b(R0, thread, in_bytes(JavaThread::do_not_unlock_if_synchronized_offset())); ++ // get method access flags ++ ld_d(T3, FP, frame::interpreter_frame_method_offset * wordSize); ++ ld_w(T1, T3, in_bytes(Method::access_flags_offset())); ++ andi(T1, T1, JVM_ACC_SYNCHRONIZED); ++ beq(T1, R0, unlocked); ++ ++ // Don't unlock anything if the _do_not_unlock_if_synchronized flag is set. ++ bne(T8, R0, no_unlock); ++ // unlock monitor ++ push(state); // save result ++ ++ // BasicObjectLock will be first in list, since this is a ++ // synchronized method. However, need to check that the object has ++ // not been unlocked by an explicit monitorexit bytecode. ++ addi_d(c_rarg0, FP, frame::interpreter_frame_initial_sp_offset * wordSize ++ - (int)sizeof(BasicObjectLock)); ++ // address of first monitor ++ ld_d(T1, c_rarg0, BasicObjectLock::obj_offset_in_bytes()); ++ bne(T1, R0, unlock); ++ pop(state); ++ if (throw_monitor_exception) { ++ // Entry already unlocked, need to throw exception ++ // I think LA do not need empty_FPU_stack ++ // remove possible return value from FPU-stack, otherwise stack could overflow ++ empty_FPU_stack(); ++ call_VM(NOREG, CAST_FROM_FN_PTR(address, ++ InterpreterRuntime::throw_illegal_monitor_state_exception)); ++ should_not_reach_here(); ++ } else { ++ // Monitor already unlocked during a stack unroll. If requested, ++ // install an illegal_monitor_state_exception. Continue with ++ // stack unrolling. ++ if (install_monitor_exception) { ++ // remove possible return value from FPU-stack, ++ // otherwise stack could overflow ++ empty_FPU_stack(); ++ call_VM(NOREG, CAST_FROM_FN_PTR(address, ++ InterpreterRuntime::new_illegal_monitor_state_exception)); ++ ++ } ++ ++ b(unlocked); ++ } ++ ++ bind(unlock); ++ unlock_object(c_rarg0); ++ pop(state); ++ ++ // Check that for block-structured locking (i.e., that all locked ++ // objects has been unlocked) ++ bind(unlocked); ++ ++ // V0, V1: Might contain return value ++ ++ // Check that all monitors are unlocked ++ { ++ Label loop, exception, entry, restart; ++ const int entry_size = frame::interpreter_frame_monitor_size() * wordSize; ++ const Address monitor_block_top(FP, ++ frame::interpreter_frame_monitor_block_top_offset * wordSize); ++ ++ bind(restart); ++ // points to current entry, starting with top-most entry ++ ld_d(c_rarg0, monitor_block_top); ++ // points to word before bottom of monitor block ++ addi_d(T3, FP, frame::interpreter_frame_initial_sp_offset * wordSize); ++ b(entry); ++ ++ // Entry already locked, need to throw exception ++ bind(exception); ++ ++ if (throw_monitor_exception) { ++ // Throw exception ++ // remove possible return value from FPU-stack, ++ // otherwise stack could overflow ++ empty_FPU_stack(); ++ MacroAssembler::call_VM(NOREG, CAST_FROM_FN_PTR(address, ++ InterpreterRuntime::throw_illegal_monitor_state_exception)); ++ should_not_reach_here(); ++ } else { ++ // Stack unrolling. Unlock object and install illegal_monitor_exception ++ // Unlock does not block, so don't have to worry about the frame ++ // We don't have to preserve c_rarg0, since we are going to ++ // throw an exception ++ ++ push(state); ++ unlock_object(c_rarg0); ++ pop(state); ++ ++ if (install_monitor_exception) { ++ empty_FPU_stack(); ++ call_VM(NOREG, CAST_FROM_FN_PTR(address, ++ InterpreterRuntime::new_illegal_monitor_state_exception)); ++ } ++ ++ b(restart); ++ } ++ ++ bind(loop); ++ ld_d(T1, c_rarg0, BasicObjectLock::obj_offset_in_bytes()); ++ bne(T1, R0, exception);// check if current entry is used ++ ++ addi_d(c_rarg0, c_rarg0, entry_size);// otherwise advance to next entry ++ bind(entry); ++ bne(c_rarg0, T3, loop); // check if bottom reached ++ } ++ ++ bind(no_unlock); ++ ++ // jvmpi support (jvmdi does not generate MethodExit on exception / popFrame) ++ if (notify_jvmdi) { ++ notify_method_exit(state, NotifyJVMTI); // preserve TOSCA ++ } else { ++ notify_method_exit(state, SkipNotifyJVMTI); // preserve TOSCA ++ } ++ ++ // remove activation ++ ld_d(SP, FP, frame::interpreter_frame_sender_sp_offset * wordSize); ++ ld_d(ret_addr, FP, frame::interpreter_frame_return_addr_offset * wordSize); ++ ld_d(FP, FP, frame::interpreter_frame_sender_fp_offset * wordSize); ++} ++ ++#endif // C_INTERP ++ ++// Lock object ++// ++// Args: ++// c_rarg0: BasicObjectLock to be used for locking ++// ++// Kills: ++// T1 ++// T2 ++void InterpreterMacroAssembler::lock_object(Register lock_reg) { ++ assert(lock_reg == c_rarg0, "The argument is only for looks. It must be c_rarg0"); ++ ++ if (UseHeavyMonitors) { ++ call_VM(NOREG, CAST_FROM_FN_PTR(address, InterpreterRuntime::monitorenter), lock_reg); ++ } else { ++ Label done, slow_case; ++ const Register tmp_reg = T2; ++ const Register scr_reg = T1; ++ const int obj_offset = BasicObjectLock::obj_offset_in_bytes(); ++ const int lock_offset = BasicObjectLock::lock_offset_in_bytes (); ++ const int mark_offset = lock_offset + BasicLock::displaced_header_offset_in_bytes(); ++ ++ // Load object pointer into scr_reg ++ ld_d(scr_reg, lock_reg, obj_offset); ++ ++ if (UseBiasedLocking) { ++ // Note: we use noreg for the temporary register since it's hard ++ // to come up with a free register on all incoming code paths ++ biased_locking_enter(lock_reg, scr_reg, tmp_reg, noreg, false, done, &slow_case); ++ } ++ ++ // Load (object->mark() | 1) into tmp_reg ++ ld_d(AT, scr_reg, 0); ++ ori(tmp_reg, AT, 1); ++ ++ // Save (object->mark() | 1) into BasicLock's displaced header ++ st_d(tmp_reg, lock_reg, mark_offset); ++ ++ assert(lock_offset == 0, "displached header must be first word in BasicObjectLock"); ++ ++ if (PrintBiasedLockingStatistics) { ++ Label succ, fail; ++ cmpxchg(Address(scr_reg, 0), tmp_reg, lock_reg, AT, true, false, succ, &fail); ++ bind(succ); ++ atomic_inc32((address)BiasedLocking::fast_path_entry_count_addr(), 1, AT, scr_reg); ++ b(done); ++ bind(fail); ++ } else { ++ cmpxchg(Address(scr_reg, 0), tmp_reg, lock_reg, AT, true, false, done); ++ } ++ ++ // Test if the oopMark is an obvious stack pointer, i.e., ++ // 1) (mark & 3) == 0, and ++ // 2) SP <= mark < SP + os::pagesize() ++ // ++ // These 3 tests can be done by evaluating the following ++ // expression: ((mark - sp) & (3 - os::vm_page_size())), ++ // assuming both stack pointer and pagesize have their ++ // least significant 2 bits clear. ++ // NOTE: the oopMark is in tmp_reg as the result of cmpxchg ++ sub_d(tmp_reg, tmp_reg, SP); ++ li(AT, 7 - os::vm_page_size()); ++ andr(tmp_reg, tmp_reg, AT); ++ // Save the test result, for recursive case, the result is zero ++ st_d(tmp_reg, lock_reg, mark_offset); ++ if (PrintBiasedLockingStatistics) { ++ bnez(tmp_reg, slow_case); ++ atomic_inc32((address)BiasedLocking::fast_path_entry_count_addr(), 1, AT, scr_reg); ++ } ++ beqz(tmp_reg, done); ++ ++ bind(slow_case); ++ // Call the runtime routine for slow case ++ call_VM(NOREG, CAST_FROM_FN_PTR(address, InterpreterRuntime::monitorenter), lock_reg); ++ ++ bind(done); ++ } ++} ++ ++// Unlocks an object. Used in monitorexit bytecode and ++// remove_activation. Throws an IllegalMonitorException if object is ++// not locked by current thread. ++// ++// Args: ++// c_rarg0: BasicObjectLock for lock ++// ++// Kills: ++// T1 ++// T2 ++// T3 ++// Throw an IllegalMonitorException if object is not locked by current thread ++void InterpreterMacroAssembler::unlock_object(Register lock_reg) { ++ assert(lock_reg == c_rarg0, "The argument is only for looks. It must be c_rarg0"); ++ ++ if (UseHeavyMonitors) { ++ call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::monitorexit), lock_reg); ++ } else { ++ Label done; ++ const Register tmp_reg = T1; ++ const Register scr_reg = T2; ++ const Register hdr_reg = T3; ++ ++ save_bcp(); // Save in case of exception ++ ++ // Convert from BasicObjectLock structure to object and BasicLock structure ++ // Store the BasicLock address into tmp_reg ++ addi_d(tmp_reg, lock_reg, BasicObjectLock::lock_offset_in_bytes()); ++ ++ // Load oop into scr_reg ++ ld_d(scr_reg, lock_reg, BasicObjectLock::obj_offset_in_bytes()); ++ // free entry ++ st_d(R0, lock_reg, BasicObjectLock::obj_offset_in_bytes()); ++ if (UseBiasedLocking) { ++ biased_locking_exit(scr_reg, hdr_reg, done); ++ } ++ ++ // Load the old header from BasicLock structure ++ ld_d(hdr_reg, tmp_reg, BasicLock::displaced_header_offset_in_bytes()); ++ // zero for recursive case ++ beqz(hdr_reg, done); ++ ++ // Atomic swap back the old header ++ cmpxchg(Address(scr_reg, 0), tmp_reg, hdr_reg, AT, false, false, done); ++ ++ // Call the runtime routine for slow case. ++ st_d(scr_reg, lock_reg, BasicObjectLock::obj_offset_in_bytes()); // restore obj ++ call_VM(NOREG, ++ CAST_FROM_FN_PTR(address, InterpreterRuntime::monitorexit), ++ lock_reg); ++ ++ bind(done); ++ ++ restore_bcp(); ++ } ++} ++ ++#ifndef CC_INTERP ++ ++void InterpreterMacroAssembler::test_method_data_pointer(Register mdp, ++ Label& zero_continue) { ++ assert(ProfileInterpreter, "must be profiling interpreter"); ++ ld_d(mdp, Address(FP, frame::interpreter_frame_mdx_offset * wordSize)); ++ beq(mdp, R0, zero_continue); ++} ++ ++ ++// Set the method data pointer for the current bcp. ++void InterpreterMacroAssembler::set_method_data_pointer_for_bcp() { ++ assert(ProfileInterpreter, "must be profiling interpreter"); ++ Label set_mdp; ++ ++ // V0 and T0 will be used as two temporary registers. ++ push2(V0, T0); ++ ++ get_method(T0); ++ // Test MDO to avoid the call if it is NULL. ++ ld_d(V0, T0, in_bytes(Method::method_data_offset())); ++ beq(V0, R0, set_mdp); ++ ++ // method: T0 ++ // bcp: BCP --> S0 ++ call_VM_leaf(CAST_FROM_FN_PTR(address, InterpreterRuntime::bcp_to_di), T0, BCP); ++ // mdi: V0 ++ // mdo is guaranteed to be non-zero here, we checked for it before the call. ++ get_method(T0); ++ ld_d(T0, T0, in_bytes(Method::method_data_offset())); ++ addi_d(T0, T0, in_bytes(MethodData::data_offset())); ++ add_d(V0, T0, V0); ++ bind(set_mdp); ++ st_d(V0, FP, frame::interpreter_frame_mdx_offset * wordSize); ++ pop2(T0, V0); ++} ++ ++void InterpreterMacroAssembler::verify_method_data_pointer() { ++ assert(ProfileInterpreter, "must be profiling interpreter"); ++#ifdef ASSERT ++ Label verify_continue; ++ Register method = T5; ++ Register mdp = T6; ++ Register tmp = A0; ++ push(method); ++ push(mdp); ++ push(tmp); ++ test_method_data_pointer(mdp, verify_continue); // If mdp is zero, continue ++ get_method(method); ++ ++ // If the mdp is valid, it will point to a DataLayout header which is ++ // consistent with the bcp. The converse is highly probable also. ++ ld_hu(tmp, mdp, in_bytes(DataLayout::bci_offset())); ++ ld_d(AT, method, in_bytes(Method::const_offset())); ++ add_d(tmp, tmp, AT); ++ addi_d(tmp, tmp, in_bytes(ConstMethod::codes_offset())); ++ beq(tmp, BCP, verify_continue); ++ call_VM_leaf(CAST_FROM_FN_PTR(address, InterpreterRuntime::verify_mdp), method, BCP, mdp); ++ bind(verify_continue); ++ pop(tmp); ++ pop(mdp); ++ pop(method); ++#endif // ASSERT ++} ++ ++ ++void InterpreterMacroAssembler::set_mdp_data_at(Register mdp_in, ++ int constant, ++ Register value) { ++ assert(ProfileInterpreter, "must be profiling interpreter"); ++ Address data(mdp_in, constant); ++ st_d(value, data); ++} ++ ++ ++void InterpreterMacroAssembler::increment_mdp_data_at(Register mdp_in, ++ int constant, ++ bool decrement) { ++ // Counter address ++ Address data(mdp_in, constant); ++ ++ increment_mdp_data_at(data, decrement); ++} ++ ++void InterpreterMacroAssembler::increment_mdp_data_at(Address data, ++ bool decrement) { ++ assert(ProfileInterpreter, "must be profiling interpreter"); ++ // %%% this does 64bit counters at best it is wasting space ++ // at worst it is a rare bug when counters overflow ++ Register tmp = S0; ++ push(tmp); ++ if (decrement) { ++ // Decrement the register. ++ ld_d(AT, data); ++ addi_d(tmp, AT, (int32_t) -DataLayout::counter_increment); ++ // If the decrement causes the counter to overflow, stay negative ++ Label L; ++ blt(tmp, R0, L); ++ addi_d(tmp, tmp, (int32_t) DataLayout::counter_increment); ++ bind(L); ++ st_d(tmp, data); ++ } else { ++ assert(DataLayout::counter_increment == 1, ++ "flow-free idiom only works with 1"); ++ ld_d(AT, data); ++ // Increment the register. ++ addi_d(tmp, AT, DataLayout::counter_increment); ++ // If the increment causes the counter to overflow, pull back by 1. ++ slt(AT, tmp, R0); ++ sub_d(tmp, tmp, AT); ++ st_d(tmp, data); ++ } ++ pop(tmp); ++} ++ ++ ++void InterpreterMacroAssembler::increment_mdp_data_at(Register mdp_in, ++ Register reg, ++ int constant, ++ bool decrement) { ++ Register tmp = S0; ++ push(S0); ++ if (decrement) { ++ // Decrement the register. ++ add_d(AT, mdp_in, reg); ++ assert(Assembler::is_simm(constant, 12), "constant is not a simm12 !"); ++ ld_d(AT, AT, constant); ++ ++ addi_d(tmp, AT, (int32_t) -DataLayout::counter_increment); ++ // If the decrement causes the counter to overflow, stay negative ++ Label L; ++ blt(tmp, R0, L); ++ addi_d(tmp, tmp, (int32_t) DataLayout::counter_increment); ++ bind(L); ++ ++ add_d(AT, mdp_in, reg); ++ st_d(tmp, AT, constant); ++ } else { ++ add_d(AT, mdp_in, reg); ++ assert(Assembler::is_simm(constant, 12), "constant is not a simm12 !"); ++ ld_d(AT, AT, constant); ++ ++ // Increment the register. ++ addi_d(tmp, AT, DataLayout::counter_increment); ++ // If the increment causes the counter to overflow, pull back by 1. ++ slt(AT, tmp, R0); ++ sub_d(tmp, tmp, AT); ++ ++ add_d(AT, mdp_in, reg); ++ st_d(tmp, AT, constant); ++ } ++ pop(S0); ++} ++ ++void InterpreterMacroAssembler::set_mdp_flag_at(Register mdp_in, ++ int flag_byte_constant) { ++ assert(ProfileInterpreter, "must be profiling interpreter"); ++ int header_offset = in_bytes(DataLayout::header_offset()); ++ int header_bits = DataLayout::flag_mask_to_header_mask(flag_byte_constant); ++ // Set the flag ++ ld_w(AT, Address(mdp_in, header_offset)); ++ if(Assembler::is_simm(header_bits, 12)) { ++ ori(AT, AT, header_bits); ++ } else { ++ push(T8); ++ // T8 is used as a temporary register. ++ li(T8, header_bits); ++ orr(AT, AT, T8); ++ pop(T8); ++ } ++ st_w(AT, Address(mdp_in, header_offset)); ++} ++ ++ ++void InterpreterMacroAssembler::test_mdp_data_at(Register mdp_in, ++ int offset, ++ Register value, ++ Register test_value_out, ++ Label& not_equal_continue) { ++ assert(ProfileInterpreter, "must be profiling interpreter"); ++ if (test_value_out == noreg) { ++ ld_d(AT, Address(mdp_in, offset)); ++ bne(AT, value, not_equal_continue); ++ } else { ++ // Put the test value into a register, so caller can use it: ++ ld_d(test_value_out, Address(mdp_in, offset)); ++ bne(value, test_value_out, not_equal_continue); ++ } ++} ++ ++ ++void InterpreterMacroAssembler::update_mdp_by_offset(Register mdp_in, ++ int offset_of_disp) { ++ assert(ProfileInterpreter, "must be profiling interpreter"); ++ assert(Assembler::is_simm(offset_of_disp, 12), "offset is not an simm12"); ++ ld_d(AT, mdp_in, offset_of_disp); ++ add_d(mdp_in, mdp_in, AT); ++ st_d(mdp_in, Address(FP, frame::interpreter_frame_mdx_offset * wordSize)); ++} ++ ++ ++void InterpreterMacroAssembler::update_mdp_by_offset(Register mdp_in, ++ Register reg, ++ int offset_of_disp) { ++ assert(ProfileInterpreter, "must be profiling interpreter"); ++ add_d(AT, reg, mdp_in); ++ assert(Assembler::is_simm(offset_of_disp, 12), "offset is not an simm12"); ++ ld_d(AT, AT, offset_of_disp); ++ add_d(mdp_in, mdp_in, AT); ++ st_d(mdp_in, Address(FP, frame::interpreter_frame_mdx_offset * wordSize)); ++} ++ ++ ++void InterpreterMacroAssembler::update_mdp_by_constant(Register mdp_in, ++ int constant) { ++ assert(ProfileInterpreter, "must be profiling interpreter"); ++ if(Assembler::is_simm(constant, 12)) { ++ addi_d(mdp_in, mdp_in, constant); ++ } else { ++ li(AT, constant); ++ add_d(mdp_in, mdp_in, AT); ++ } ++ st_d(mdp_in, Address(FP, frame::interpreter_frame_mdx_offset * wordSize)); ++} ++ ++ ++void InterpreterMacroAssembler::update_mdp_for_ret(Register return_bci) { ++ assert(ProfileInterpreter, "must be profiling interpreter"); ++ push(return_bci); // save/restore across call_VM ++ call_VM(noreg, ++ CAST_FROM_FN_PTR(address, InterpreterRuntime::update_mdp_for_ret), ++ return_bci); ++ pop(return_bci); ++} ++ ++ ++void InterpreterMacroAssembler::profile_taken_branch(Register mdp, ++ Register bumped_count) { ++ if (ProfileInterpreter) { ++ Label profile_continue; ++ ++ // If no method data exists, go to profile_continue. ++ // Otherwise, assign to mdp ++ test_method_data_pointer(mdp, profile_continue); ++ ++ // We are taking a branch. Increment the taken count. ++ // We inline increment_mdp_data_at to return bumped_count in a register ++ //increment_mdp_data_at(mdp, in_bytes(JumpData::taken_offset())); ++ ld_d(bumped_count, mdp, in_bytes(JumpData::taken_offset())); ++ assert(DataLayout::counter_increment == 1, ++ "flow-free idiom only works with 1"); ++ push(T8); ++ // T8 is used as a temporary register. ++ addi_d(T8, bumped_count, DataLayout::counter_increment); ++ slt(AT, T8, R0); ++ sub_d(bumped_count, T8, AT); ++ pop(T8); ++ st_d(bumped_count, mdp, in_bytes(JumpData::taken_offset())); // Store back out ++ // The method data pointer needs to be updated to reflect the new target. ++ update_mdp_by_offset(mdp, in_bytes(JumpData::displacement_offset())); ++ bind(profile_continue); ++ } ++} ++ ++ ++void InterpreterMacroAssembler::profile_not_taken_branch(Register mdp) { ++ if (ProfileInterpreter) { ++ Label profile_continue; ++ ++ // If no method data exists, go to profile_continue. ++ test_method_data_pointer(mdp, profile_continue); ++ ++ // We are taking a branch. Increment the not taken count. ++ increment_mdp_data_at(mdp, in_bytes(BranchData::not_taken_offset())); ++ ++ // The method data pointer needs to be updated to correspond to ++ // the next bytecode ++ update_mdp_by_constant(mdp, in_bytes(BranchData::branch_data_size())); ++ bind(profile_continue); ++ } ++} ++ ++ ++void InterpreterMacroAssembler::profile_call(Register mdp) { ++ if (ProfileInterpreter) { ++ Label profile_continue; ++ ++ // If no method data exists, go to profile_continue. ++ test_method_data_pointer(mdp, profile_continue); ++ ++ // We are making a call. Increment the count. ++ increment_mdp_data_at(mdp, in_bytes(CounterData::count_offset())); ++ ++ // The method data pointer needs to be updated to reflect the new target. ++ update_mdp_by_constant(mdp, in_bytes(CounterData::counter_data_size())); ++ bind(profile_continue); ++ } ++} ++ ++ ++void InterpreterMacroAssembler::profile_final_call(Register mdp) { ++ if (ProfileInterpreter) { ++ Label profile_continue; ++ ++ // If no method data exists, go to profile_continue. ++ test_method_data_pointer(mdp, profile_continue); ++ ++ // We are making a call. Increment the count. ++ increment_mdp_data_at(mdp, in_bytes(CounterData::count_offset())); ++ ++ // The method data pointer needs to be updated to reflect the new target. ++ update_mdp_by_constant(mdp, ++ in_bytes(VirtualCallData:: ++ virtual_call_data_size())); ++ bind(profile_continue); ++ } ++} ++ ++ ++void InterpreterMacroAssembler::profile_virtual_call(Register receiver, ++ Register mdp, ++ Register reg2, ++ bool receiver_can_be_null) { ++ if (ProfileInterpreter) { ++ Label profile_continue; ++ ++ // If no method data exists, go to profile_continue. ++ test_method_data_pointer(mdp, profile_continue); ++ ++ Label skip_receiver_profile; ++ if (receiver_can_be_null) { ++ Label not_null; ++ bnez(receiver, not_null); ++ // We are making a call. Increment the count. ++ increment_mdp_data_at(mdp, in_bytes(CounterData::count_offset())); ++ b(skip_receiver_profile); ++ bind(not_null); ++ } ++ ++ // Record the receiver type. ++ record_klass_in_profile(receiver, mdp, reg2, true); ++ bind(skip_receiver_profile); ++ ++ // The method data pointer needs to be updated to reflect the new target. ++ update_mdp_by_constant(mdp, ++ in_bytes(VirtualCallData:: ++ virtual_call_data_size())); ++ bind(profile_continue); ++ } ++} ++ ++// This routine creates a state machine for updating the multi-row ++// type profile at a virtual call site (or other type-sensitive bytecode). ++// The machine visits each row (of receiver/count) until the receiver type ++// is found, or until it runs out of rows. At the same time, it remembers ++// the location of the first empty row. (An empty row records null for its ++// receiver, and can be allocated for a newly-observed receiver type.) ++// Because there are two degrees of freedom in the state, a simple linear ++// search will not work; it must be a decision tree. Hence this helper ++// function is recursive, to generate the required tree structured code. ++// It's the interpreter, so we are trading off code space for speed. ++// See below for example code. ++void InterpreterMacroAssembler::record_klass_in_profile_helper( ++ Register receiver, Register mdp, ++ Register reg2, int start_row, ++ Label& done, bool is_virtual_call) { ++ if (TypeProfileWidth == 0) { ++ if (is_virtual_call) { ++ increment_mdp_data_at(mdp, in_bytes(CounterData::count_offset())); ++ } ++ return; ++ } ++ ++ int last_row = VirtualCallData::row_limit() - 1; ++ assert(start_row <= last_row, "must be work left to do"); ++ // Test this row for both the receiver and for null. ++ // Take any of three different outcomes: ++ // 1. found receiver => increment count and goto done ++ // 2. found null => keep looking for case 1, maybe allocate this cell ++ // 3. found something else => keep looking for cases 1 and 2 ++ // Case 3 is handled by a recursive call. ++ for (int row = start_row; row <= last_row; row++) { ++ Label next_test; ++ bool test_for_null_also = (row == start_row); ++ ++ // See if the receiver is receiver[n]. ++ int recvr_offset = in_bytes(VirtualCallData::receiver_offset(row)); ++ test_mdp_data_at(mdp, recvr_offset, receiver, ++ (test_for_null_also ? reg2 : noreg), ++ next_test); ++ // (Reg2 now contains the receiver from the CallData.) ++ ++ // The receiver is receiver[n]. Increment count[n]. ++ int count_offset = in_bytes(VirtualCallData::receiver_count_offset(row)); ++ increment_mdp_data_at(mdp, count_offset); ++ beq(R0, R0, done); ++ bind(next_test); ++ ++ if (test_for_null_also) { ++ Label found_null; ++ // Failed the equality check on receiver[n]... Test for null. ++ if (start_row == last_row) { ++ // The only thing left to do is handle the null case. ++ if (is_virtual_call) { ++ beq(reg2, R0, found_null); ++ // Receiver did not match any saved receiver and there is no empty row for it. ++ // Increment total counter to indicate polymorphic case. ++ increment_mdp_data_at(mdp, in_bytes(CounterData::count_offset())); ++ beq(R0, R0, done); ++ bind(found_null); ++ } else { ++ bne(reg2, R0, done); ++ } ++ break; ++ } ++ // Since null is rare, make it be the branch-taken case. ++ beq(reg2, R0, found_null); ++ ++ // Put all the "Case 3" tests here. ++ record_klass_in_profile_helper(receiver, mdp, reg2, start_row + 1, done, is_virtual_call); ++ ++ // Found a null. Keep searching for a matching receiver, ++ // but remember that this is an empty (unused) slot. ++ bind(found_null); ++ } ++ } ++ ++ // In the fall-through case, we found no matching receiver, but we ++ // observed the receiver[start_row] is NULL. ++ ++ // Fill in the receiver field and increment the count. ++ int recvr_offset = in_bytes(VirtualCallData::receiver_offset(start_row)); ++ set_mdp_data_at(mdp, recvr_offset, receiver); ++ int count_offset = in_bytes(VirtualCallData::receiver_count_offset(start_row)); ++ li(reg2, DataLayout::counter_increment); ++ set_mdp_data_at(mdp, count_offset, reg2); ++ if (start_row > 0) { ++ beq(R0, R0, done); ++ } ++} ++ ++// Example state machine code for three profile rows: ++// // main copy of decision tree, rooted at row[1] ++// if (row[0].rec == rec) { row[0].incr(); goto done; } ++// if (row[0].rec != NULL) { ++// // inner copy of decision tree, rooted at row[1] ++// if (row[1].rec == rec) { row[1].incr(); goto done; } ++// if (row[1].rec != NULL) { ++// // degenerate decision tree, rooted at row[2] ++// if (row[2].rec == rec) { row[2].incr(); goto done; } ++// if (row[2].rec != NULL) { goto done; } // overflow ++// row[2].init(rec); goto done; ++// } else { ++// // remember row[1] is empty ++// if (row[2].rec == rec) { row[2].incr(); goto done; } ++// row[1].init(rec); goto done; ++// } ++// } else { ++// // remember row[0] is empty ++// if (row[1].rec == rec) { row[1].incr(); goto done; } ++// if (row[2].rec == rec) { row[2].incr(); goto done; } ++// row[0].init(rec); goto done; ++// } ++// done: ++ ++void InterpreterMacroAssembler::record_klass_in_profile(Register receiver, ++ Register mdp, Register reg2, ++ bool is_virtual_call) { ++ assert(ProfileInterpreter, "must be profiling"); ++ Label done; ++ ++ record_klass_in_profile_helper(receiver, mdp, reg2, 0, done, is_virtual_call); ++ ++ bind (done); ++} ++ ++void InterpreterMacroAssembler::profile_ret(Register return_bci, ++ Register mdp) { ++ if (ProfileInterpreter) { ++ Label profile_continue; ++ uint row; ++ ++ // If no method data exists, go to profile_continue. ++ test_method_data_pointer(mdp, profile_continue); ++ ++ // Update the total ret count. ++ increment_mdp_data_at(mdp, in_bytes(CounterData::count_offset())); ++ ++ for (row = 0; row < RetData::row_limit(); row++) { ++ Label next_test; ++ ++ // See if return_bci is equal to bci[n]: ++ test_mdp_data_at(mdp, ++ in_bytes(RetData::bci_offset(row)), ++ return_bci, noreg, ++ next_test); ++ ++ // return_bci is equal to bci[n]. Increment the count. ++ increment_mdp_data_at(mdp, in_bytes(RetData::bci_count_offset(row))); ++ ++ // The method data pointer needs to be updated to reflect the new target. ++ update_mdp_by_offset(mdp, ++ in_bytes(RetData::bci_displacement_offset(row))); ++ b(profile_continue); ++ bind(next_test); ++ } ++ ++ update_mdp_for_ret(return_bci); ++ ++ bind(profile_continue); ++ } ++} ++ ++ ++void InterpreterMacroAssembler::profile_null_seen(Register mdp) { ++ if (ProfileInterpreter) { ++ Label profile_continue; ++ ++ // If no method data exists, go to profile_continue. ++ test_method_data_pointer(mdp, profile_continue); ++ ++ set_mdp_flag_at(mdp, BitData::null_seen_byte_constant()); ++ ++ // The method data pointer needs to be updated. ++ int mdp_delta = in_bytes(BitData::bit_data_size()); ++ if (TypeProfileCasts) { ++ mdp_delta = in_bytes(VirtualCallData::virtual_call_data_size()); ++ } ++ update_mdp_by_constant(mdp, mdp_delta); ++ ++ bind(profile_continue); ++ } ++} ++ ++ ++void InterpreterMacroAssembler::profile_typecheck_failed(Register mdp) { ++ if (ProfileInterpreter && TypeProfileCasts) { ++ Label profile_continue; ++ ++ // If no method data exists, go to profile_continue. ++ test_method_data_pointer(mdp, profile_continue); ++ ++ int count_offset = in_bytes(CounterData::count_offset()); ++ // Back up the address, since we have already bumped the mdp. ++ count_offset -= in_bytes(VirtualCallData::virtual_call_data_size()); ++ ++ // *Decrement* the counter. We expect to see zero or small negatives. ++ increment_mdp_data_at(mdp, count_offset, true); ++ ++ bind (profile_continue); ++ } ++} ++ ++ ++void InterpreterMacroAssembler::profile_typecheck(Register mdp, Register klass, Register reg2) { ++ if (ProfileInterpreter) { ++ Label profile_continue; ++ ++ // If no method data exists, go to profile_continue. ++ test_method_data_pointer(mdp, profile_continue); ++ ++ // The method data pointer needs to be updated. ++ int mdp_delta = in_bytes(BitData::bit_data_size()); ++ if (TypeProfileCasts) { ++ mdp_delta = in_bytes(VirtualCallData::virtual_call_data_size()); ++ ++ // Record the object type. ++ record_klass_in_profile(klass, mdp, reg2, false); ++ } ++ update_mdp_by_constant(mdp, mdp_delta); ++ ++ bind(profile_continue); ++ } ++} ++ ++ ++void InterpreterMacroAssembler::profile_switch_default(Register mdp) { ++ if (ProfileInterpreter) { ++ Label profile_continue; ++ ++ // If no method data exists, go to profile_continue. ++ test_method_data_pointer(mdp, profile_continue); ++ ++ // Update the default case count ++ increment_mdp_data_at(mdp, ++ in_bytes(MultiBranchData::default_count_offset())); ++ ++ // The method data pointer needs to be updated. ++ update_mdp_by_offset(mdp, ++ in_bytes(MultiBranchData:: ++ default_displacement_offset())); ++ ++ bind(profile_continue); ++ } ++} ++ ++ ++void InterpreterMacroAssembler::profile_switch_case(Register index, ++ Register mdp, ++ Register reg2) { ++ if (ProfileInterpreter) { ++ Label profile_continue; ++ ++ // If no method data exists, go to profile_continue. ++ test_method_data_pointer(mdp, profile_continue); ++ ++ // Build the base (index * per_case_size_in_bytes()) + ++ // case_array_offset_in_bytes() ++ li(reg2, in_bytes(MultiBranchData::per_case_size())); ++ mul_d(index, index, reg2); ++ addi_d(index, index, in_bytes(MultiBranchData::case_array_offset())); ++ ++ // Update the case count ++ increment_mdp_data_at(mdp, ++ index, ++ in_bytes(MultiBranchData::relative_count_offset())); ++ ++ // The method data pointer needs to be updated. ++ update_mdp_by_offset(mdp, ++ index, ++ in_bytes(MultiBranchData:: ++ relative_displacement_offset())); ++ ++ bind(profile_continue); ++ } ++} ++ ++ ++void InterpreterMacroAssembler::narrow(Register result) { ++ // Get method->_constMethod->_result_type ++ ld_d(T4, FP, frame::interpreter_frame_method_offset * wordSize); ++ ld_d(T4, T4, in_bytes(Method::const_offset())); ++ ld_bu(T4, T4, in_bytes(ConstMethod::result_type_offset())); ++ ++ Label done, notBool, notByte, notChar; ++ ++ // common case first ++ addi_d(AT, T4, -T_INT); ++ beq(AT, R0, done); ++ ++ // mask integer result to narrower return type. ++ addi_d(AT, T4, -T_BOOLEAN); ++ bne(AT, R0, notBool); ++ andi(result, result, 0x1); ++ beq(R0, R0, done); ++ ++ bind(notBool); ++ addi_d(AT, T4, -T_BYTE); ++ bne(AT, R0, notByte); ++ ext_w_b(result, result); ++ beq(R0, R0, done); ++ ++ bind(notByte); ++ addi_d(AT, T4, -T_CHAR); ++ bne(AT, R0, notChar); ++ bstrpick_d(result, result, 15, 0); ++ beq(R0, R0, done); ++ ++ bind(notChar); ++ ext_w_h(result, result); ++ ++ // Nothing to do for T_INT ++ bind(done); ++} ++ ++ ++void InterpreterMacroAssembler::profile_obj_type(Register obj, const Address& mdo_addr) { ++ Label update, next, none; ++ ++ verify_oop(obj); ++ ++ if (mdo_addr.index() != noreg) { ++ guarantee(T0 != mdo_addr.base(), "The base register will be corrupted !"); ++ guarantee(T0 != mdo_addr.index(), "The index register will be corrupted !"); ++ push(T0); ++ alsl_d(T0, mdo_addr.index(), mdo_addr.base(), mdo_addr.scale() - 1); ++ } ++ ++ bnez(obj, update); ++ ++ if (mdo_addr.index() == noreg) { ++ ld_d(AT, mdo_addr); ++ } else { ++ ld_d(AT, T0, mdo_addr.disp()); ++ } ++ ori(AT, AT, TypeEntries::null_seen); ++ if (mdo_addr.index() == noreg) { ++ st_d(AT, mdo_addr); ++ } else { ++ st_d(AT, T0, mdo_addr.disp()); ++ } ++ ++ b(next); ++ ++ bind(update); ++ load_klass(obj, obj); ++ ++ if (mdo_addr.index() == noreg) { ++ ld_d(AT, mdo_addr); ++ } else { ++ ld_d(AT, T0, mdo_addr.disp()); ++ } ++ xorr(obj, obj, AT); ++ ++ assert(TypeEntries::type_klass_mask == -4, "must be"); ++ bstrpick_d(AT, obj, 63, 2); ++ beqz(AT, next); ++ ++ andi(AT, obj, TypeEntries::type_unknown); ++ bnez(AT, next); ++ ++ if (mdo_addr.index() == noreg) { ++ ld_d(AT, mdo_addr); ++ } else { ++ ld_d(AT, T0, mdo_addr.disp()); ++ } ++ beqz(AT, none); ++ ++ addi_d(AT, AT, -(TypeEntries::null_seen)); ++ beqz(AT, none); ++ ++ // There is a chance that the checks above (re-reading profiling ++ // data from memory) fail if another thread has just set the ++ // profiling to this obj's klass ++ if (mdo_addr.index() == noreg) { ++ ld_d(AT, mdo_addr); ++ } else { ++ ld_d(AT, T0, mdo_addr.disp()); ++ } ++ xorr(obj, obj, AT); ++ assert(TypeEntries::type_klass_mask == -4, "must be"); ++ bstrpick_d(AT, obj, 63, 2); ++ beqz(AT, next); ++ ++ // different than before. Cannot keep accurate profile. ++ if (mdo_addr.index() == noreg) { ++ ld_d(AT, mdo_addr); ++ } else { ++ ld_d(AT, T0, mdo_addr.disp()); ++ } ++ ori(AT, AT, TypeEntries::type_unknown); ++ if (mdo_addr.index() == noreg) { ++ st_d(AT, mdo_addr); ++ } else { ++ st_d(AT, T0, mdo_addr.disp()); ++ } ++ b(next); ++ ++ bind(none); ++ // first time here. Set profile type. ++ if (mdo_addr.index() == noreg) { ++ st_d(obj, mdo_addr); ++ } else { ++ st_d(obj, T0, mdo_addr.disp()); ++ } ++ ++ bind(next); ++ if (mdo_addr.index() != noreg) { ++ pop(T0); ++ } ++} ++ ++void InterpreterMacroAssembler::profile_arguments_type(Register mdp, Register callee, Register tmp, bool is_virtual) { ++ if (!ProfileInterpreter) { ++ return; ++ } ++ ++ if (MethodData::profile_arguments() || MethodData::profile_return()) { ++ Label profile_continue; ++ ++ test_method_data_pointer(mdp, profile_continue); ++ ++ int off_to_start = is_virtual ? in_bytes(VirtualCallData::virtual_call_data_size()) : in_bytes(CounterData::counter_data_size()); ++ ++ ld_b(AT, mdp, in_bytes(DataLayout::tag_offset()) - off_to_start); ++ li(tmp, is_virtual ? DataLayout::virtual_call_type_data_tag : DataLayout::call_type_data_tag); ++ bne(tmp, AT, profile_continue); ++ ++ ++ if (MethodData::profile_arguments()) { ++ Label done; ++ int off_to_args = in_bytes(TypeEntriesAtCall::args_data_offset()); ++ if (Assembler::is_simm(off_to_args, 12)) { ++ addi_d(mdp, mdp, off_to_args); ++ } else { ++ li(AT, off_to_args); ++ add_d(mdp, mdp, AT); ++ } ++ ++ ++ for (int i = 0; i < TypeProfileArgsLimit; i++) { ++ if (i > 0 || MethodData::profile_return()) { ++ // If return value type is profiled we may have no argument to profile ++ ld_d(tmp, mdp, in_bytes(TypeEntriesAtCall::cell_count_offset())-off_to_args); ++ ++ if (Assembler::is_simm(-1 * i * TypeStackSlotEntries::per_arg_count(), 12)) { ++ addi_w(tmp, tmp, -1 * i * TypeStackSlotEntries::per_arg_count()); ++ } else { ++ li(AT, i*TypeStackSlotEntries::per_arg_count()); ++ sub_w(tmp, tmp, AT); ++ } ++ ++ li(AT, TypeStackSlotEntries::per_arg_count()); ++ blt(tmp, AT, done); ++ } ++ ld_d(tmp, callee, in_bytes(Method::const_offset())); ++ ++ ld_hu(tmp, tmp, in_bytes(ConstMethod::size_of_parameters_offset())); ++ ++ // stack offset o (zero based) from the start of the argument ++ // list, for n arguments translates into offset n - o - 1 from ++ // the end of the argument list ++ ld_d(AT, mdp, in_bytes(TypeEntriesAtCall::stack_slot_offset(i))-off_to_args); ++ sub_d(tmp, tmp, AT); ++ ++ addi_w(tmp, tmp, -1); ++ ++ Address arg_addr = argument_address(tmp); ++ ld_d(tmp, arg_addr); ++ ++ Address mdo_arg_addr(mdp, in_bytes(TypeEntriesAtCall::argument_type_offset(i))-off_to_args); ++ profile_obj_type(tmp, mdo_arg_addr); ++ ++ int to_add = in_bytes(TypeStackSlotEntries::per_arg_size()); ++ if (Assembler::is_simm(to_add, 12)) { ++ addi_d(mdp, mdp, to_add); ++ } else { ++ li(AT, to_add); ++ add_d(mdp, mdp, AT); ++ } ++ ++ off_to_args += to_add; ++ } ++ ++ if (MethodData::profile_return()) { ++ ld_d(tmp, mdp, in_bytes(TypeEntriesAtCall::cell_count_offset())-off_to_args); ++ ++ int tmp_arg_counts = TypeProfileArgsLimit*TypeStackSlotEntries::per_arg_count(); ++ if (Assembler::is_simm(-1 * tmp_arg_counts, 12)) { ++ addi_w(tmp, tmp, -1 * tmp_arg_counts); ++ } else { ++ li(AT, tmp_arg_counts); ++ sub_w(mdp, mdp, AT); ++ } ++ } ++ ++ bind(done); ++ ++ if (MethodData::profile_return()) { ++ // We're right after the type profile for the last ++ // argument. tmp is the number of cells left in the ++ // CallTypeData/VirtualCallTypeData to reach its end. Non null ++ // if there's a return to profile. ++ assert(ReturnTypeEntry::static_cell_count() < TypeStackSlotEntries::per_arg_count(), "can't move past ret type"); ++ slli_w(tmp, tmp, exact_log2(DataLayout::cell_size)); ++ add_d(mdp, mdp, tmp); ++ } ++ st_d(mdp, FP, frame::interpreter_frame_mdx_offset * wordSize); ++ } else { ++ assert(MethodData::profile_return(), "either profile call args or call ret"); ++ update_mdp_by_constant(mdp, in_bytes(TypeEntriesAtCall::return_only_size())); ++ } ++ ++ // mdp points right after the end of the ++ // CallTypeData/VirtualCallTypeData, right after the cells for the ++ // return value type if there's one ++ ++ bind(profile_continue); ++ } ++} ++ ++void InterpreterMacroAssembler::profile_return_type(Register mdp, Register ret, Register tmp) { ++ assert_different_registers(mdp, ret, tmp, _bcp_register); ++ if (ProfileInterpreter && MethodData::profile_return()) { ++ Label profile_continue, done; ++ ++ test_method_data_pointer(mdp, profile_continue); ++ ++ if (MethodData::profile_return_jsr292_only()) { ++ // If we don't profile all invoke bytecodes we must make sure ++ // it's a bytecode we indeed profile. We can't go back to the ++ // begining of the ProfileData we intend to update to check its ++ // type because we're right after it and we don't known its ++ // length ++ Label do_profile; ++ ld_b(tmp, _bcp_register, 0); ++ addi_d(AT, tmp, -1 * Bytecodes::_invokedynamic); ++ beqz(AT, do_profile); ++ addi_d(AT, tmp, -1 * Bytecodes::_invokehandle); ++ beqz(AT, do_profile); ++ ++ get_method(tmp); ++ ld_b(tmp, tmp, Method::intrinsic_id_offset_in_bytes()); ++ li(AT, vmIntrinsics::_compiledLambdaForm); ++ bne(tmp, AT, profile_continue); ++ ++ bind(do_profile); ++ } ++ ++ Address mdo_ret_addr(mdp, -in_bytes(ReturnTypeEntry::size())); ++ add_d(tmp, ret, R0); ++ profile_obj_type(tmp, mdo_ret_addr); ++ ++ bind(profile_continue); ++ } ++} ++ ++void InterpreterMacroAssembler::profile_parameters_type(Register mdp, Register tmp1, Register tmp2) { ++ guarantee(T4 == tmp1, "You are reqired to use T4 as the index register for LoongArch !"); ++ ++ if (ProfileInterpreter && MethodData::profile_parameters()) { ++ Label profile_continue, done; ++ ++ test_method_data_pointer(mdp, profile_continue); ++ ++ // Load the offset of the area within the MDO used for ++ // parameters. If it's negative we're not profiling any parameters ++ ld_w(tmp1, mdp, in_bytes(MethodData::parameters_type_data_di_offset()) - in_bytes(MethodData::data_offset())); ++ blt(tmp1, R0, profile_continue); ++ ++ // Compute a pointer to the area for parameters from the offset ++ // and move the pointer to the slot for the last ++ // parameters. Collect profiling from last parameter down. ++ // mdo start + parameters offset + array length - 1 ++ add_d(mdp, mdp, tmp1); ++ ld_d(tmp1, mdp, in_bytes(ArrayData::array_len_offset())); ++ decrement(tmp1, TypeStackSlotEntries::per_arg_count()); ++ ++ ++ Label loop; ++ bind(loop); ++ ++ int off_base = in_bytes(ParametersTypeData::stack_slot_offset(0)); ++ int type_base = in_bytes(ParametersTypeData::type_offset(0)); ++ Address::ScaleFactor per_arg_scale = Address::times(DataLayout::cell_size); ++ Address arg_type(mdp, tmp1, per_arg_scale, type_base); ++ ++ // load offset on the stack from the slot for this parameter ++ alsl_d(AT, tmp1, mdp, per_arg_scale - 1); ++ ld_d(tmp2, AT, off_base); ++ ++ sub_d(tmp2, R0, tmp2); ++ ++ // read the parameter from the local area ++ slli_d(AT, tmp2, Interpreter::stackElementScale()); ++ ldx_d(tmp2, AT, _locals_register); ++ ++ // profile the parameter ++ profile_obj_type(tmp2, arg_type); ++ ++ // go to next parameter ++ decrement(tmp1, TypeStackSlotEntries::per_arg_count()); ++ blt(R0, tmp1, loop); ++ ++ bind(profile_continue); ++ } ++} ++ ++void InterpreterMacroAssembler::verify_oop(Register reg, TosState state) { ++ if (state == atos) { ++ MacroAssembler::verify_oop(reg); ++ } ++} ++ ++void InterpreterMacroAssembler::verify_FPU(int stack_depth, TosState state) { ++} ++#endif // !CC_INTERP ++ ++ ++void InterpreterMacroAssembler::notify_method_entry() { ++ // Whenever JVMTI is interp_only_mode, method entry/exit events are sent to ++ // track stack depth. If it is possible to enter interp_only_mode we add ++ // the code to check if the event should be sent. ++ Register tempreg = T0; ++#ifndef OPT_THREAD ++ get_thread(T8); ++#else ++ move(T8, TREG); ++#endif ++ if (JvmtiExport::can_post_interpreter_events()) { ++ Label L; ++ ld_w(tempreg, T8, in_bytes(JavaThread::interp_only_mode_offset())); ++ beq(tempreg, R0, L); ++ call_VM(noreg, CAST_FROM_FN_PTR(address, ++ InterpreterRuntime::post_method_entry)); ++ bind(L); ++ } ++ ++ { ++ SkipIfEqual skip_if(this, &DTraceMethodProbes, 0); ++ get_method(S3); ++ call_VM_leaf(CAST_FROM_FN_PTR(address, SharedRuntime::dtrace_method_entry), ++ //Rthread, ++ T8, ++ //Rmethod); ++ S3); ++ } ++} ++ ++void InterpreterMacroAssembler::notify_method_exit( ++ TosState state, NotifyMethodExitMode mode) { ++ Register tempreg = T0; ++#ifndef OPT_THREAD ++ get_thread(T8); ++#else ++ move(T8, TREG); ++#endif ++ // Whenever JVMTI is interp_only_mode, method entry/exit events are sent to ++ // track stack depth. If it is possible to enter interp_only_mode we add ++ // the code to check if the event should be sent. ++ if (mode == NotifyJVMTI && JvmtiExport::can_post_interpreter_events()) { ++ Label skip; ++ // Note: frame::interpreter_frame_result has a dependency on how the ++ // method result is saved across the call to post_method_exit. If this ++ // is changed then the interpreter_frame_result implementation will ++ // need to be updated too. ++ ++ // For c++ interpreter the result is always stored at a known location in the frame ++ // template interpreter will leave it on the top of the stack. ++ NOT_CC_INTERP(push(state);) ++ ld_w(tempreg, T8, in_bytes(JavaThread::interp_only_mode_offset())); ++ beq(tempreg, R0, skip); ++ call_VM(noreg, ++ CAST_FROM_FN_PTR(address, InterpreterRuntime::post_method_exit)); ++ bind(skip); ++ NOT_CC_INTERP(pop(state)); ++ } ++ ++ { ++ // Dtrace notification ++ SkipIfEqual skip_if(this, &DTraceMethodProbes, 0); ++ NOT_CC_INTERP(push(state)); ++ get_method(S3); ++ call_VM_leaf(CAST_FROM_FN_PTR(address, SharedRuntime::dtrace_method_exit), ++ //Rthread, Rmethod); ++ T8, S3); ++ NOT_CC_INTERP(pop(state)); ++ } ++} ++ ++// Jump if ((*counter_addr += increment) & mask) satisfies the condition. ++void InterpreterMacroAssembler::increment_mask_and_jump(Address counter_addr, ++ int increment, int mask, ++ Register scratch, bool preloaded, ++ Condition cond, Label* where) { ++ assert_different_registers(scratch, AT); ++ ++ if (!preloaded) { ++ ld_w(scratch, counter_addr); ++ } ++ addi_w(scratch, scratch, increment); ++ st_w(scratch, counter_addr); ++ ++ li(AT, mask); ++ andr(scratch, scratch, AT); ++ ++ if (cond == Assembler::zero) { ++ beq(scratch, R0, *where); ++ } else { ++ unimplemented(); ++ } ++} +diff --git a/hotspot/src/cpu/loongarch/vm/interp_masm_loongarch_64.hpp b/hotspot/src/cpu/loongarch/vm/interp_masm_loongarch_64.hpp +new file mode 100644 +index 0000000000..9113da54ff +--- /dev/null ++++ b/hotspot/src/cpu/loongarch/vm/interp_masm_loongarch_64.hpp +@@ -0,0 +1,269 @@ ++/* ++ * Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2015, 2021, Loongson Technology. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ * ++ */ ++ ++#ifndef CPU_LOONGARCH_VM_INTERP_MASM_LOONGARCH_64_HPP ++#define CPU_LOONGARCH_VM_INTERP_MASM_LOONGARCH_64_HPP ++ ++#include "asm/assembler.hpp" ++#include "asm/macroAssembler.hpp" ++#include "asm/macroAssembler.inline.hpp" ++#include "interpreter/invocationCounter.hpp" ++#include "runtime/frame.hpp" ++ ++// This file specializes the assember with interpreter-specific macros ++ ++ ++class InterpreterMacroAssembler: public MacroAssembler { ++#ifndef CC_INTERP ++ private: ++ ++ Register _locals_register; // register that contains the pointer to the locals ++ Register _bcp_register; // register that contains the bcp ++ ++ protected: ++ // Interpreter specific version of call_VM_base ++ virtual void call_VM_leaf_base(address entry_point, ++ int number_of_arguments); ++ ++ virtual void call_VM_base(Register oop_result, ++ Register java_thread, ++ Register last_java_sp, ++ address entry_point, ++ int number_of_arguments, ++ bool check_exceptions); ++ ++ virtual void check_and_handle_popframe(Register java_thread); ++ virtual void check_and_handle_earlyret(Register java_thread); ++ ++ // base routine for all dispatches ++ void dispatch_base(TosState state, address* table, bool verifyoop = true); ++#endif // CC_INTERP ++ ++ public: ++ // narrow int return value ++ void narrow(Register result); ++ ++ InterpreterMacroAssembler(CodeBuffer* code) : MacroAssembler(code), _locals_register(LVP), _bcp_register(BCP) {} ++ ++ void get_2_byte_integer_at_bcp(Register reg, Register tmp, int offset); ++ void get_4_byte_integer_at_bcp(Register reg, int offset); ++ ++ void load_earlyret_value(TosState state); ++ ++#ifdef CC_INTERP ++ void save_bcp() { /* not needed in c++ interpreter and harmless */ } ++ void restore_bcp() { /* not needed in c++ interpreter and harmless */ } ++ ++ // Helpers for runtime call arguments/results ++ void get_method(Register reg); ++ ++#else ++ ++ // Interpreter-specific registers ++ void save_bcp() { ++ st_d(BCP, FP, frame::interpreter_frame_bcx_offset * wordSize); ++ } ++ ++ void restore_bcp() { ++ ld_d(BCP, FP, frame::interpreter_frame_bcx_offset * wordSize); ++ } ++ ++ void restore_locals() { ++ ld_d(LVP, FP, frame::interpreter_frame_locals_offset * wordSize); ++ } ++ ++ // Helpers for runtime call arguments/results ++ void get_method(Register reg) { ++ ld_d(reg, FP, frame::interpreter_frame_method_offset * wordSize); ++ } ++ ++ void get_const(Register reg){ ++ get_method(reg); ++ ld_d(reg, reg, in_bytes(Method::const_offset())); ++ } ++ ++ void get_constant_pool(Register reg) { ++ get_const(reg); ++ ld_d(reg, reg, in_bytes(ConstMethod::constants_offset())); ++ } ++ ++ void get_constant_pool_cache(Register reg) { ++ get_constant_pool(reg); ++ ld_d(reg, reg, ConstantPool::cache_offset_in_bytes()); ++ } ++ ++ void get_cpool_and_tags(Register cpool, Register tags) { ++ get_constant_pool(cpool); ++ ld_d(tags, cpool, ConstantPool::tags_offset_in_bytes()); ++ } ++ ++ void get_unsigned_2_byte_index_at_bcp(Register reg, int bcp_offset); ++ void get_cache_and_index_at_bcp(Register cache, Register index, int bcp_offset, size_t index_size = sizeof(u2)); ++ void get_cache_and_index_and_bytecode_at_bcp(Register cache, Register index, Register bytecode, int byte_no, int bcp_offset, size_t index_size = sizeof(u2)); ++ void get_cache_entry_pointer_at_bcp(Register cache, Register tmp, int bcp_offset, size_t index_size = sizeof(u2)); ++ void get_cache_index_at_bcp(Register index, int bcp_offset, size_t index_size = sizeof(u2)); ++ void get_method_counters(Register method, Register mcs, Label& skip); ++ ++ // load cpool->resolved_references(index); ++ void load_resolved_reference_at_index(Register result, Register index); ++ ++ void pop_ptr( Register r = FSR); ++ void pop_i( Register r = FSR); ++ void pop_l( Register r = FSR); ++ void pop_f(FloatRegister r = FSF); ++ void pop_d(FloatRegister r = FSF); ++ ++ void push_ptr( Register r = FSR); ++ void push_i( Register r = FSR); ++ void push_l( Register r = FSR); ++ void push_f(FloatRegister r = FSF); ++ void push_d(FloatRegister r = FSF); ++ ++ void pop(Register r ) { ((MacroAssembler*)this)->pop(r); } ++ ++ void push(Register r ) { ((MacroAssembler*)this)->push(r); } ++ ++ void pop(TosState state); // transition vtos -> state ++ void push(TosState state); // transition state -> vtos ++ ++ void empty_expression_stack() { ++ ld_d(SP, FP, frame::interpreter_frame_monitor_block_top_offset * wordSize); ++ // NULL last_sp until next java call ++ st_d(R0, FP, frame::interpreter_frame_last_sp_offset * wordSize); ++ } ++ ++ // Super call_VM calls - correspond to MacroAssembler::call_VM(_leaf) calls ++ void load_ptr(int n, Register val); ++ void store_ptr(int n, Register val); ++ ++ // Generate a subtype check: branch to ok_is_subtype if sub_klass is ++ // a subtype of super_klass. ++ //void gen_subtype_check( Register sub_klass, Label &ok_is_subtype ); ++ void gen_subtype_check( Register Rsup_klass, Register sub_klass, Label &ok_is_subtype ); ++ ++ // Dispatching ++ void dispatch_prolog(TosState state, int step = 0); ++ void dispatch_epilog(TosState state, int step = 0); ++ void dispatch_only(TosState state); ++ void dispatch_only_normal(TosState state); ++ void dispatch_only_noverify(TosState state); ++ void dispatch_next(TosState state, int step = 0); ++ void dispatch_via (TosState state, address* table); ++ ++ // jump to an invoked target ++ void prepare_to_jump_from_interpreted(); ++ void jump_from_interpreted(Register method, Register temp); ++ ++ ++ // Returning from interpreted functions ++ // ++ // Removes the current activation (incl. unlocking of monitors) ++ // and sets up the return address. This code is also used for ++ // exception unwindwing. In that case, we do not want to throw ++ // IllegalMonitorStateExceptions, since that might get us into an ++ // infinite rethrow exception loop. ++ // Additionally this code is used for popFrame and earlyReturn. ++ // In popFrame case we want to skip throwing an exception, ++ // installing an exception, and notifying jvmdi. ++ // In earlyReturn case we only want to skip throwing an exception ++ // and installing an exception. ++ void remove_activation(TosState state, Register ret_addr, ++ bool throw_monitor_exception = true, ++ bool install_monitor_exception = true, ++ bool notify_jvmdi = true); ++#endif // CC_INTERP ++ ++ // Object locking ++ void lock_object (Register lock_reg); ++ void unlock_object(Register lock_reg); ++ ++#ifndef CC_INTERP ++ ++ // Interpreter profiling operations ++ void set_method_data_pointer_for_bcp(); ++ void test_method_data_pointer(Register mdp, Label& zero_continue); ++ void verify_method_data_pointer(); ++ ++ void set_mdp_data_at(Register mdp_in, int constant, Register value); ++ void increment_mdp_data_at(Address data, bool decrement = false); ++ void increment_mdp_data_at(Register mdp_in, int constant, ++ bool decrement = false); ++ void increment_mdp_data_at(Register mdp_in, Register reg, int constant, ++ bool decrement = false); ++ void increment_mask_and_jump(Address counter_addr, ++ int increment, int mask, ++ Register scratch, bool preloaded, ++ Condition cond, Label* where); ++ void set_mdp_flag_at(Register mdp_in, int flag_constant); ++ void test_mdp_data_at(Register mdp_in, int offset, Register value, ++ Register test_value_out, ++ Label& not_equal_continue); ++ ++ void record_klass_in_profile(Register receiver, Register mdp, ++ Register reg2, bool is_virtual_call); ++ void record_klass_in_profile_helper(Register receiver, Register mdp, ++ Register reg2, int start_row, ++ Label& done, bool is_virtual_call); ++ ++ void update_mdp_by_offset(Register mdp_in, int offset_of_offset); ++ void update_mdp_by_offset(Register mdp_in, Register reg, int offset_of_disp); ++ void update_mdp_by_constant(Register mdp_in, int constant); ++ void update_mdp_for_ret(Register return_bci); ++ ++ void profile_taken_branch(Register mdp, Register bumped_count); ++ void profile_not_taken_branch(Register mdp); ++ void profile_call(Register mdp); ++ void profile_final_call(Register mdp); ++ void profile_virtual_call(Register receiver, Register mdp, ++ Register scratch2, ++ bool receiver_can_be_null = false); ++ void profile_ret(Register return_bci, Register mdp); ++ void profile_null_seen(Register mdp); ++ void profile_typecheck(Register mdp, Register klass, Register scratch); ++ void profile_typecheck_failed(Register mdp); ++ void profile_switch_default(Register mdp); ++ void profile_switch_case(Register index_in_scratch, Register mdp, ++ Register scratch2); ++ ++ // Debugging ++ // only if +VerifyOops && state == atos ++ void verify_oop(Register reg, TosState state = atos); ++ // only if +VerifyFPU && (state == ftos || state == dtos) ++ void verify_FPU(int stack_depth, TosState state = ftos); ++ ++ void profile_obj_type(Register obj, const Address& mdo_addr); ++ void profile_arguments_type(Register mdp, Register callee, Register tmp, bool is_virtual); ++ void profile_return_type(Register mdp, Register ret, Register tmp); ++ void profile_parameters_type(Register mdp, Register tmp1, Register tmp2); ++#endif // !CC_INTERP ++ ++ typedef enum { NotifyJVMTI, SkipNotifyJVMTI } NotifyMethodExitMode; ++ ++ // support for jvmti/dtrace ++ void notify_method_entry(); ++ void notify_method_exit(TosState state, NotifyMethodExitMode mode); ++}; ++ ++#endif // CPU_LOONGARCH_VM_INTERP_MASM_LOONGARCH_64_HPP +diff --git a/hotspot/src/cpu/loongarch/vm/interpreterGenerator_loongarch.hpp b/hotspot/src/cpu/loongarch/vm/interpreterGenerator_loongarch.hpp +new file mode 100644 +index 0000000000..7f253b2d51 +--- /dev/null ++++ b/hotspot/src/cpu/loongarch/vm/interpreterGenerator_loongarch.hpp +@@ -0,0 +1,51 @@ ++/* ++ * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2015, 2020, Loongson Technology. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ * ++ */ ++ ++#ifndef CPU_LOONGARCH_VM_INTERPRETERGENERATOR_LOONGARCH_HPP ++#define CPU_LOONGARCH_VM_INTERPRETERGENERATOR_LOONGARCH_HPP ++ ++ ++// Generation of Interpreter ++// ++ friend class AbstractInterpreterGenerator; ++ ++ private: ++ ++ address generate_normal_entry(bool synchronized); ++ address generate_native_entry(bool synchronized); ++ address generate_abstract_entry(void); ++ address generate_math_entry(AbstractInterpreter::MethodKind kind); ++ address generate_empty_entry(void); ++ address generate_accessor_entry(void); ++ address generate_Reference_get_entry(); ++ address generate_CRC32_update_entry(); ++ address generate_CRC32_updateBytes_entry(AbstractInterpreter::MethodKind kind); ++ void lock_method(void); ++ void generate_stack_overflow_check(void); ++ ++ void generate_counter_incr(Label* overflow, Label* profile_method, Label* profile_method_continue); ++ void generate_counter_overflow(Label* do_continue); ++ ++#endif // CPU_LOONGARCH_VM_INTERPRETERGENERATOR_LOONGARCH_HPP +diff --git a/hotspot/src/cpu/loongarch/vm/interpreterRT_loongarch.hpp b/hotspot/src/cpu/loongarch/vm/interpreterRT_loongarch.hpp +new file mode 100644 +index 0000000000..052eb997e4 +--- /dev/null ++++ b/hotspot/src/cpu/loongarch/vm/interpreterRT_loongarch.hpp +@@ -0,0 +1,66 @@ ++/* ++ * Copyright (c) 1998, 2010, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2015, 2021, Loongson Technology. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ * ++ */ ++ ++#ifndef CPU_LOONGARCH_VM_INTERPRETERRT_LOONGARCH_HPP ++#define CPU_LOONGARCH_VM_INTERPRETERRT_LOONGARCH_HPP ++ ++#include "memory/allocation.hpp" ++ ++// native method calls ++ ++class SignatureHandlerGenerator: public NativeSignatureIterator { ++ private: ++ MacroAssembler* _masm; ++ unsigned int _num_fp_args; ++ unsigned int _num_int_args; ++ int _stack_offset; ++ ++ void move(int from_offset, int to_offset); ++ void box(int from_offset, int to_offset); ++ void pass_int(); ++ void pass_long(); ++ void pass_object(); ++ void pass_float(); ++ void pass_double(); ++ ++ public: ++ // Creation ++ SignatureHandlerGenerator(methodHandle method, CodeBuffer* buffer) : NativeSignatureIterator(method) { ++ _masm = new MacroAssembler(buffer); ++ _num_int_args = (method->is_static() ? 1 : 0); ++ _num_fp_args = 0; ++ _stack_offset = 0; ++ } ++ ++ // Code generation ++ void generate(uint64_t fingerprint); ++ ++ // Code generation support ++ static Register from(); ++ static Register to(); ++ static Register temp(); ++}; ++ ++#endif // CPU_LOONGARCH_VM_INTERPRETERRT_LOONGARCH_HPP +diff --git a/hotspot/src/cpu/loongarch/vm/interpreterRT_loongarch_64.cpp b/hotspot/src/cpu/loongarch/vm/interpreterRT_loongarch_64.cpp +new file mode 100644 +index 0000000000..0c9df4aa71 +--- /dev/null ++++ b/hotspot/src/cpu/loongarch/vm/interpreterRT_loongarch_64.cpp +@@ -0,0 +1,274 @@ ++/* ++ * Copyright (c) 2003, 2012, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2015, 2021, Loongson Technology. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ * ++ */ ++ ++#include "precompiled.hpp" ++#include "interpreter/interpreter.hpp" ++#include "interpreter/interpreterRuntime.hpp" ++#include "memory/allocation.inline.hpp" ++#include "memory/universe.inline.hpp" ++#include "oops/method.hpp" ++#include "oops/oop.inline.hpp" ++#include "runtime/handles.inline.hpp" ++#include "runtime/icache.hpp" ++#include "runtime/interfaceSupport.hpp" ++#include "runtime/signature.hpp" ++ ++#define __ _masm-> ++ ++#define A0 RA0 ++#define A1 RA1 ++#define A2 RA2 ++#define A3 RA3 ++#define A4 RA4 ++#define A5 RA5 ++#define A6 RA6 ++#define A7 RA7 ++#define T0 RT0 ++#define T1 RT1 ++#define T2 RT2 ++#define T3 RT3 ++#define T4 RT4 ++#define T5 RT5 ++#define T6 RT6 ++#define T7 RT7 ++#define T8 RT8 ++ ++// Implementation of SignatureHandlerGenerator ++ ++void InterpreterRuntime::SignatureHandlerGenerator::move(int from_offset, int to_offset) { ++ __ ld_d(temp(), from(), Interpreter::local_offset_in_bytes(from_offset)); ++ __ st_d(temp(), to(), to_offset * longSize); ++} ++ ++void InterpreterRuntime::SignatureHandlerGenerator::box(int from_offset, int to_offset) { ++ __ addi_d(temp(), from(),Interpreter::local_offset_in_bytes(from_offset) ); ++ __ ld_w(AT, from(), Interpreter::local_offset_in_bytes(from_offset) ); ++ ++ __ maskeqz(temp(), temp(), AT); ++ __ st_w(temp(), to(), to_offset * wordSize); ++} ++ ++void InterpreterRuntime::SignatureHandlerGenerator::generate(uint64_t fingerprint) { ++ // generate code to handle arguments ++ iterate(fingerprint); ++ // return result handler ++ __ li(V0, AbstractInterpreter::result_handler(method()->result_type())); ++ // return ++ __ jr(RA); ++ ++ __ flush(); ++} ++ ++void InterpreterRuntime::SignatureHandlerGenerator::pass_int() { ++ if (_num_int_args < Argument::n_register_parameters - 1) { ++ __ ld_w(as_Register(++_num_int_args + RA0->encoding()), from(), Interpreter::local_offset_in_bytes(offset())); ++ } else { ++ __ ld_w(AT, from(), Interpreter::local_offset_in_bytes(offset())); ++ __ st_w(AT, to(), _stack_offset); ++ _stack_offset += wordSize; ++ } ++} ++ ++// the jvm specifies that long type takes 2 stack spaces, so in do_long(), _offset += 2. ++void InterpreterRuntime::SignatureHandlerGenerator::pass_long() { ++ if (_num_int_args < Argument::n_register_parameters - 1) { ++ __ ld_d(as_Register(++_num_int_args + RA0->encoding()), from(), Interpreter::local_offset_in_bytes(offset() + 1)); ++ } else { ++ __ ld_d(AT, from(), Interpreter::local_offset_in_bytes(offset() + 1)); ++ __ st_d(AT, to(), _stack_offset); ++ _stack_offset += wordSize; ++ } ++} ++ ++void InterpreterRuntime::SignatureHandlerGenerator::pass_object() { ++ if (_num_int_args < Argument::n_register_parameters - 1) { ++ Register reg = as_Register(++_num_int_args + RA0->encoding()); ++ if (_num_int_args == 1) { ++ assert(offset() == 0, "argument register 1 can only be (non-null) receiver"); ++ __ addi_d(reg, from(), Interpreter::local_offset_in_bytes(offset())); ++ } else { ++ __ ld_d(reg, from(), Interpreter::local_offset_in_bytes(offset())); ++ __ addi_d(AT, from(), Interpreter::local_offset_in_bytes(offset())); ++ __ maskeqz(reg, AT, reg); ++ } ++ } else { ++ __ ld_d(temp(), from(), Interpreter::local_offset_in_bytes(offset())); ++ __ addi_d(AT, from(), Interpreter::local_offset_in_bytes(offset())); ++ __ maskeqz(temp(), AT, temp()); ++ __ st_d(temp(), to(), _stack_offset); ++ _stack_offset += wordSize; ++ } ++} ++ ++void InterpreterRuntime::SignatureHandlerGenerator::pass_float() { ++ if (_num_fp_args < Argument::n_float_register_parameters) { ++ __ fld_s(as_FloatRegister(_num_fp_args++), from(), Interpreter::local_offset_in_bytes(offset())); ++ } else if (_num_int_args < Argument::n_register_parameters - 1) { ++ __ ld_w(as_Register(++_num_int_args + RA0->encoding()), from(), Interpreter::local_offset_in_bytes(offset())); ++ } else { ++ __ ld_w(AT, from(), Interpreter::local_offset_in_bytes(offset())); ++ __ st_w(AT, to(), _stack_offset); ++ _stack_offset += wordSize; ++ } ++} ++ ++// the jvm specifies that double type takes 2 stack spaces, so in do_double(), _offset += 2. ++void InterpreterRuntime::SignatureHandlerGenerator::pass_double() { ++ if (_num_fp_args < Argument::n_float_register_parameters) { ++ __ fld_d(as_FloatRegister(_num_fp_args++), from(), Interpreter::local_offset_in_bytes(offset() + 1)); ++ } else if (_num_int_args < Argument::n_register_parameters - 1) { ++ __ ld_d(as_Register(++_num_int_args + RA0->encoding()), from(), Interpreter::local_offset_in_bytes(offset() + 1)); ++ } else { ++ __ ld_d(AT, from(), Interpreter::local_offset_in_bytes(offset() + 1)); ++ __ st_d(AT, to(), _stack_offset); ++ _stack_offset += wordSize; ++ } ++} ++ ++ ++Register InterpreterRuntime::SignatureHandlerGenerator::from() { return LVP; } ++Register InterpreterRuntime::SignatureHandlerGenerator::to() { return SP; } ++Register InterpreterRuntime::SignatureHandlerGenerator::temp() { return T8; } ++ ++// Implementation of SignatureHandlerLibrary ++ ++void SignatureHandlerLibrary::pd_set_handler(address handler) {} ++ ++ ++class SlowSignatureHandler ++ : public NativeSignatureIterator { ++ private: ++ address _from; ++ intptr_t* _to; ++ intptr_t* _int_args; ++ intptr_t* _fp_args; ++ intptr_t* _fp_identifiers; ++ unsigned int _num_int_args; ++ unsigned int _num_fp_args; ++ ++ virtual void pass_int() ++ { ++ jint from_obj = *(jint *)(_from+Interpreter::local_offset_in_bytes(0)); ++ _from -= Interpreter::stackElementSize; ++ ++ if (_num_int_args < Argument::n_register_parameters - 1) { ++ *_int_args++ = from_obj; ++ _num_int_args++; ++ } else { ++ *_to++ = from_obj; ++ } ++ } ++ ++ virtual void pass_long() ++ { ++ intptr_t from_obj = *(intptr_t*)(_from+Interpreter::local_offset_in_bytes(1)); ++ _from -= 2 * Interpreter::stackElementSize; ++ ++ if (_num_int_args < Argument::n_register_parameters - 1) { ++ *_int_args++ = from_obj; ++ _num_int_args++; ++ } else { ++ *_to++ = from_obj; ++ } ++ } ++ ++ virtual void pass_object() ++ { ++ intptr_t *from_addr = (intptr_t*)(_from + Interpreter::local_offset_in_bytes(0)); ++ _from -= Interpreter::stackElementSize; ++ ++ if (_num_int_args < Argument::n_register_parameters - 1) { ++ *_int_args++ = (*from_addr == 0) ? NULL : (intptr_t) from_addr; ++ _num_int_args++; ++ } else { ++ *_to++ = (*from_addr == 0) ? NULL : (intptr_t) from_addr; ++ } ++ } ++ ++ virtual void pass_float() ++ { ++ jint from_obj = *(jint *)(_from+Interpreter::local_offset_in_bytes(0)); ++ _from -= Interpreter::stackElementSize; ++ ++ if (_num_fp_args < Argument::n_float_register_parameters) { ++ *_fp_args++ = from_obj; ++ _num_fp_args++; ++ } else if (_num_int_args < Argument::n_register_parameters - 1) { ++ *_int_args++ = from_obj; ++ _num_int_args++; ++ } else { ++ *_to++ = from_obj; ++ } ++ } ++ ++ virtual void pass_double() ++ { ++ intptr_t from_obj = *(intptr_t*)(_from+Interpreter::local_offset_in_bytes(1)); ++ _from -= 2*Interpreter::stackElementSize; ++ ++ if (_num_fp_args < Argument::n_float_register_parameters) { ++ *_fp_args++ = from_obj; ++ *_fp_identifiers |= (1 << _num_fp_args); // mark as double ++ _num_fp_args++; ++ } else if (_num_int_args < Argument::n_register_parameters - 1) { ++ *_int_args++ = from_obj; ++ _num_int_args++; ++ } else { ++ *_to++ = from_obj; ++ } ++ } ++ ++ public: ++ SlowSignatureHandler(methodHandle method, address from, intptr_t* to) ++ : NativeSignatureIterator(method) ++ { ++ _from = from; ++ _to = to; ++ ++ // see TemplateInterpreterGenerator::generate_slow_signature_handler() ++ _int_args = to - (method->is_static() ? 15 : 16); ++ _fp_args = to - 8; ++ _fp_identifiers = to - 9; ++ *(int*) _fp_identifiers = 0; ++ _num_int_args = (method->is_static() ? 1 : 0); ++ _num_fp_args = 0; ++ } ++}; ++ ++ ++IRT_ENTRY(address, ++ InterpreterRuntime::slow_signature_handler(JavaThread* thread, ++ Method* method, ++ intptr_t* from, ++ intptr_t* to)) ++ methodHandle m(thread, (Method*)method); ++ assert(m->is_native(), "sanity check"); ++ ++ // handle arguments ++ SlowSignatureHandler(m, (address)from, to).iterate(UCONST64(-1)); ++ ++ // return result handler ++ return Interpreter::result_handler(m->result_type()); ++IRT_END +diff --git a/hotspot/src/cpu/loongarch/vm/interpreter_loongarch.hpp b/hotspot/src/cpu/loongarch/vm/interpreter_loongarch.hpp +new file mode 100644 +index 0000000000..c83afbdaf0 +--- /dev/null ++++ b/hotspot/src/cpu/loongarch/vm/interpreter_loongarch.hpp +@@ -0,0 +1,50 @@ ++/* ++ * Copyright (c) 1997, 2011, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2015, 2020, Loongson Technology. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ * ++ */ ++ ++#ifndef CPU_LOONGARCH_VM_INTERPRETER_LOONGARCH_HPP ++#define CPU_LOONGARCH_VM_INTERPRETER_LOONGARCH_HPP ++ ++ public: ++ ++ // Sentinel placed in the code for interpreter returns so ++ // that i2c adapters and osr code can recognize an interpreter ++ // return address and convert the return to a specialized ++ // block of code to handle compiedl return values and cleaning ++ // the fpu stack. ++ static const int return_sentinel; ++ ++ static Address::ScaleFactor stackElementScale() { ++ return Address::times_8; ++ } ++ ++ // Offset from sp (which points to the last stack element) ++ static int expr_offset_in_bytes(int i) { return stackElementSize * i; } ++ // Size of interpreter code. Increase if too small. Interpreter will ++ // fail with a guarantee ("not enough space for interpreter generation"); ++ // if too small. ++ // Run with +PrintInterpreterSize to get the VM to print out the size. ++ // Max size with JVMTI and TaggedStackInterpreter ++ const static int InterpreterCodeSize = 168 * 1024; ++#endif // CPU_LOONGARCH_VM_INTERPRETER_LOONGARCH_HPP +diff --git a/hotspot/src/cpu/loongarch/vm/interpreter_loongarch_64.cpp b/hotspot/src/cpu/loongarch/vm/interpreter_loongarch_64.cpp +new file mode 100644 +index 0000000000..5a4f102cfd +--- /dev/null ++++ b/hotspot/src/cpu/loongarch/vm/interpreter_loongarch_64.cpp +@@ -0,0 +1,277 @@ ++/* ++ * Copyright (c) 2003, 2014, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2015, 2022, Loongson Technology. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ * ++ */ ++ ++#include "precompiled.hpp" ++#include "asm/macroAssembler.hpp" ++#include "interpreter/bytecodeHistogram.hpp" ++#include "interpreter/interpreter.hpp" ++#include "interpreter/interpreterGenerator.hpp" ++#include "interpreter/interpreterRuntime.hpp" ++#include "interpreter/templateTable.hpp" ++#include "oops/arrayOop.hpp" ++#include "oops/methodData.hpp" ++#include "oops/method.hpp" ++#include "oops/oop.inline.hpp" ++#include "prims/jvmtiExport.hpp" ++#include "prims/jvmtiThreadState.hpp" ++#include "prims/methodHandles.hpp" ++#include "runtime/arguments.hpp" ++#include "runtime/deoptimization.hpp" ++#include "runtime/frame.inline.hpp" ++#include "runtime/sharedRuntime.hpp" ++#include "runtime/stubRoutines.hpp" ++#include "runtime/synchronizer.hpp" ++#include "runtime/timer.hpp" ++#include "runtime/vframeArray.hpp" ++#include "utilities/debug.hpp" ++ ++#define __ _masm-> ++ ++#define A0 RA0 ++#define A1 RA1 ++#define A2 RA2 ++#define A3 RA3 ++#define A4 RA4 ++#define A5 RA5 ++#define A6 RA6 ++#define A7 RA7 ++#define T0 RT0 ++#define T1 RT1 ++#define T2 RT2 ++#define T3 RT3 ++#define T4 RT4 ++#define T5 RT5 ++#define T6 RT6 ++#define T7 RT7 ++#define T8 RT8 ++ ++address AbstractInterpreterGenerator::generate_slow_signature_handler() { ++ address entry = __ pc(); ++ // Rmethod: method ++ // LVP: pointer to locals ++ // A3: first stack arg ++ __ move(A3, SP); ++ __ addi_d(SP, SP, -18 * wordSize); ++ __ st_d(RA, SP, 0); ++ __ call_VM(noreg, ++ CAST_FROM_FN_PTR(address, ++ InterpreterRuntime::slow_signature_handler), ++ Rmethod, LVP, A3); ++ ++ // V0: result handler ++ ++ // Stack layout: ++ // ... ++ // 18 stack arg0 <--- old sp ++ // 17 floatReg arg7 ++ // ... ++ // 10 floatReg arg0 ++ // 9 float/double identifiers ++ // 8 IntReg arg7 ++ // ... ++ // 2 IntReg arg1 ++ // 1 aligned slot ++ // SP: 0 return address ++ ++ // Do FP first so we can use A3 as temp ++ __ ld_d(A3, Address(SP, 9 * wordSize)); // float/double identifiers ++ ++ for (int i= 0; i < Argument::n_float_register_parameters; i++) { ++ FloatRegister floatreg = as_FloatRegister(i + FA0->encoding()); ++ Label isdouble, done; ++ ++ __ andi(AT, A3, 1 << i); ++ __ bnez(AT, isdouble); ++ __ fld_s(floatreg, SP, (10 + i) * wordSize); ++ __ b(done); ++ __ bind(isdouble); ++ __ fld_d(floatreg, SP, (10 + i) * wordSize); ++ __ bind(done); ++ } ++ ++ // A0 is for env. ++ // If the mothed is not static, A1 will be corrected in generate_native_entry. ++ for (int i= 1; i < Argument::n_register_parameters; i++) { ++ Register reg = as_Register(i + A0->encoding()); ++ ++ __ ld_d(reg, SP, (1 + i) * wordSize); ++ } ++ ++ // A0/V0 contains the result from the call of ++ // InterpreterRuntime::slow_signature_handler so we don't touch it ++ // here. It will be loaded with the JNIEnv* later. ++ __ ld_d(RA, SP, 0); ++ __ addi_d(SP, SP, 18 * wordSize); ++ __ jr(RA); ++ return entry; ++} ++ ++ ++// ++// Various method entries ++// ++ ++address InterpreterGenerator::generate_math_entry(AbstractInterpreter::MethodKind kind) { ++ ++ // Rmethod: methodOop ++ // V0: scratrch ++ // Rsender: send 's sp ++ ++ if (!InlineIntrinsics) return NULL; // Generate a vanilla entry ++ ++ address entry_point = __ pc(); ++ //guarantee(0, "LA not implemented yet"); ++ // These don't need a safepoint check because they aren't virtually ++ // callable. We won't enter these intrinsics from compiled code. ++ // If in the future we added an intrinsic which was virtually callable ++ // we'd have to worry about how to safepoint so that this code is used. ++ ++ // mathematical functions inlined by compiler ++ // (interpreter must provide identical implementation ++ // in order to avoid monotonicity bugs when switching ++ // from interpreter to compiler in the middle of some ++ // computation) ++ // ++ // stack: [ lo(arg) ] <-- sp ++ // [ hi(arg) ] ++ { ++ // Note: For JDK 1.3 StrictMath exists and Math.sin/cos/sqrt are ++ // java methods. Interpreter::method_kind(...) will select ++ // this entry point for the corresponding methods in JDK 1.3. ++ __ fld_d(FA0, SP, 0 * wordSize); ++ __ fld_d(FA1, SP, 1 * wordSize); ++ __ push2(RA, FP); ++ __ addi_d(FP, SP, 2 * wordSize); ++ ++ // [ fp ] <-- sp ++ // [ ra ] ++ // [ lo ] <-- fp ++ // [ hi ] ++ //FIXME, need consider this ++ switch (kind) { ++ case Interpreter::java_lang_math_sin : ++ __ trigfunc('s'); ++ break; ++ case Interpreter::java_lang_math_cos : ++ __ trigfunc('c'); ++ break; ++ case Interpreter::java_lang_math_tan : ++ __ trigfunc('t'); ++ break; ++ case Interpreter::java_lang_math_sqrt: ++ __ fsqrt_d(F0, FA0); ++ break; ++ case Interpreter::java_lang_math_abs: ++ __ fabs_d(F0, FA0); ++ break; ++ case Interpreter::java_lang_math_log: ++ // Store to stack to convert 80bit precision back to 64bits ++ break; ++ case Interpreter::java_lang_math_log10: ++ // Store to stack to convert 80bit precision back to 64bits ++ break; ++ case Interpreter::java_lang_math_pow: ++ break; ++ case Interpreter::java_lang_math_exp: ++ break; ++ ++ default : ++ ShouldNotReachHere(); ++ } ++ ++ // must maintain return value in F0:F1 ++ __ ld_d(RA, FP, (-1) * wordSize); ++ //FIXME ++ __ ld_d(FP, FP, (-2) * wordSize); ++ __ move(SP, Rsender); ++ __ jr(RA); ++ } ++ return entry_point; ++} ++ ++ ++// Abstract method entry ++// Attempt to execute abstract method. Throw exception ++address InterpreterGenerator::generate_abstract_entry(void) { ++ ++ // Rmethod: methodOop ++ // V0: receiver (unused) ++ // Rsender : sender 's sp ++ address entry_point = __ pc(); ++ ++ // abstract method entry ++ // throw exception ++ // adjust stack to what a normal return would do ++ __ empty_expression_stack(); ++ __ restore_bcp(); ++ __ restore_locals(); ++ __ call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::throw_AbstractMethodError)); ++ // the call_VM checks for exception, so we should never return here. ++ __ should_not_reach_here(); ++ ++ return entry_point; ++} ++ ++ ++// Empty method, generate a very fast return. ++ ++address InterpreterGenerator::generate_empty_entry(void) { ++ ++ // Rmethod: methodOop ++ // V0: receiver (unused) ++ // Rsender: sender 's sp, must set sp to this value on return, on LoongArch, now use T0, as it right? ++ if (!UseFastEmptyMethods) return NULL; ++ ++ address entry_point = __ pc(); ++ //TODO: LA ++ //guarantee(0, "LA not implemented yet"); ++ Label slow_path; ++ __ li(RT0, SafepointSynchronize::address_of_state()); ++ __ ld_w(AT, RT0, 0); ++ __ li(RT0, (SafepointSynchronize::_not_synchronized)); ++ __ bne(AT, RT0,slow_path); ++ __ move(SP, Rsender); ++ __ jr(RA); ++ __ bind(slow_path); ++ (void) generate_normal_entry(false); ++ return entry_point; ++ ++} ++ ++void Deoptimization::unwind_callee_save_values(frame* f, vframeArray* vframe_array) { ++ ++ // This code is sort of the equivalent of C2IAdapter::setup_stack_frame back in ++ // the days we had adapter frames. When we deoptimize a situation where a ++ // compiled caller calls a compiled caller will have registers it expects ++ // to survive the call to the callee. If we deoptimize the callee the only ++ // way we can restore these registers is to have the oldest interpreter ++ // frame that we create restore these values. That is what this routine ++ // will accomplish. ++ ++ // At the moment we have modified c2 to not have any callee save registers ++ // so this problem does not exist and this routine is just a place holder. ++ ++ assert(f->is_interpreted_frame(), "must be interpreted"); ++} +diff --git a/hotspot/src/cpu/loongarch/vm/javaFrameAnchor_loongarch.hpp b/hotspot/src/cpu/loongarch/vm/javaFrameAnchor_loongarch.hpp +new file mode 100644 +index 0000000000..de97de5804 +--- /dev/null ++++ b/hotspot/src/cpu/loongarch/vm/javaFrameAnchor_loongarch.hpp +@@ -0,0 +1,87 @@ ++/* ++ * Copyright (c) 2002, 2010, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2015, 2020, Loongson Technology. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ * ++ */ ++ ++#ifndef CPU_LOONGARCH_VM_JAVAFRAMEANCHOR_LOONGARCH_HPP ++#define CPU_LOONGARCH_VM_JAVAFRAMEANCHOR_LOONGARCH_HPP ++ ++private: ++ ++ // FP value associated with _last_Java_sp: ++ intptr_t* volatile _last_Java_fp; // pointer is volatile not what it points to ++ ++public: ++ // Each arch must define reset, save, restore ++ // These are used by objects that only care about: ++ // 1 - initializing a new state (thread creation, javaCalls) ++ // 2 - saving a current state (javaCalls) ++ // 3 - restoring an old state (javaCalls) ++ ++ void clear(void) { ++ // clearing _last_Java_sp must be first ++ _last_Java_sp = NULL; ++ // fence? ++ _last_Java_fp = NULL; ++ _last_Java_pc = NULL; ++ } ++ ++ void copy(JavaFrameAnchor* src) { ++ // In order to make sure the transition state is valid for "this" ++ // We must clear _last_Java_sp before copying the rest of the new data ++ // ++ // Hack Alert: Temporary bugfix for 4717480/4721647 ++ // To act like previous version (pd_cache_state) don't NULL _last_Java_sp ++ // unless the value is changing ++ // ++ if (_last_Java_sp != src->_last_Java_sp) ++ _last_Java_sp = NULL; ++ ++ _last_Java_fp = src->_last_Java_fp; ++ _last_Java_pc = src->_last_Java_pc; ++ // Must be last so profiler will always see valid frame if has_last_frame() is true ++ _last_Java_sp = src->_last_Java_sp; ++ } ++ ++ // Always walkable ++ bool walkable(void) { return true; } ++ // Never any thing to do since we are always walkable and can find address of return addresses ++ void make_walkable(JavaThread* thread) { } ++ ++ intptr_t* last_Java_sp(void) const { return _last_Java_sp; } ++ ++ address last_Java_pc(void) { return _last_Java_pc; } ++ ++private: ++ ++ static ByteSize last_Java_fp_offset() { return byte_offset_of(JavaFrameAnchor, _last_Java_fp); } ++ ++public: ++ ++ void set_last_Java_sp(intptr_t* sp) { _last_Java_sp = sp; } ++ ++ intptr_t* last_Java_fp(void) { return _last_Java_fp; } ++ // Assert (last_Java_sp == NULL || fp == NULL) ++ void set_last_Java_fp(intptr_t* fp) { _last_Java_fp = fp; } ++ ++#endif // CPU_LOONGARCH_VM_JAVAFRAMEANCHOR_LOONGARCH_HPP +diff --git a/hotspot/src/cpu/loongarch/vm/jniFastGetField_loongarch_64.cpp b/hotspot/src/cpu/loongarch/vm/jniFastGetField_loongarch_64.cpp +new file mode 100644 +index 0000000000..96b89a681f +--- /dev/null ++++ b/hotspot/src/cpu/loongarch/vm/jniFastGetField_loongarch_64.cpp +@@ -0,0 +1,168 @@ ++/* ++ * Copyright (c) 2004, 2010, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2015, 2021, Loongson Technology. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ * ++ */ ++ ++#include "precompiled.hpp" ++#include "asm/macroAssembler.hpp" ++#include "memory/resourceArea.hpp" ++#include "prims/jniFastGetField.hpp" ++#include "prims/jvm_misc.hpp" ++#include "runtime/safepoint.hpp" ++ ++#define __ masm-> ++ ++#define A0 RA0 ++#define A1 RA1 ++#define A2 RA2 ++#define A3 RA3 ++#define A4 RA4 ++#define A5 RA5 ++#define A6 RA6 ++#define A7 RA7 ++#define T0 RT0 ++#define T1 RT1 ++#define T2 RT2 ++#define T3 RT3 ++#define T4 RT4 ++#define T5 RT5 ++#define T6 RT6 ++#define T7 RT7 ++#define T8 RT8 ++ ++#define BUFFER_SIZE 30*wordSize ++ ++// Instead of issuing lfence for LoadLoad barrier, we create data dependency ++// between loads, which is more efficient than lfence. ++ ++address JNI_FastGetField::generate_fast_get_int_field0(BasicType type) { ++ const char *name = NULL; ++ switch (type) { ++ case T_BOOLEAN: name = "jni_fast_GetBooleanField"; break; ++ case T_BYTE: name = "jni_fast_GetByteField"; break; ++ case T_CHAR: name = "jni_fast_GetCharField"; break; ++ case T_SHORT: name = "jni_fast_GetShortField"; break; ++ case T_INT: name = "jni_fast_GetIntField"; break; ++ case T_LONG: name = "jni_fast_GetLongField"; break; ++ case T_FLOAT: name = "jni_fast_GetFloatField"; break; ++ case T_DOUBLE: name = "jni_fast_GetDoubleField"; break; ++ default: ShouldNotReachHere(); ++ } ++ ResourceMark rm; ++ BufferBlob* blob = BufferBlob::create(name, BUFFER_SIZE); ++ CodeBuffer cbuf(blob); ++ MacroAssembler* masm = new MacroAssembler(&cbuf); ++ address fast_entry = __ pc(); ++ Label slow; ++ ++ // return pc RA ++ // jni env A0 ++ // obj A1 ++ // jfieldID A2 ++ ++ address counter_addr = SafepointSynchronize::safepoint_counter_addr(); ++ __ li(AT, (long)counter_addr); ++ __ ld_w(T1, AT, 0); ++ ++ // Parameters(A0~A3) should not be modified, since they will be used in slow path ++ __ andi(AT, T1, 1); ++ __ bne(AT, R0, slow); ++ ++ __ move(T0, A1); ++ __ clear_jweak_tag(T0); ++ ++ __ ld_d(T0, T0, 0); // unbox, *obj ++ __ srli_d(T2, A2, 2); // offset ++ __ add_d(T0, T0, T2); ++ ++ __ li(AT, (long)counter_addr); ++ __ ld_w(AT, AT, 0); ++ __ bne(T1, AT, slow); ++ ++ assert(count < LIST_CAPACITY, "LIST_CAPACITY too small"); ++ speculative_load_pclist[count] = __ pc(); ++ switch (type) { ++ case T_BOOLEAN: __ ld_bu (V0, T0, 0); break; ++ case T_BYTE: __ ld_b (V0, T0, 0); break; ++ case T_CHAR: __ ld_hu (V0, T0, 0); break; ++ case T_SHORT: __ ld_h (V0, T0, 0); break; ++ case T_INT: __ ld_w (V0, T0, 0); break; ++ case T_LONG: __ ld_d (V0, T0, 0); break; ++ case T_FLOAT: __ fld_s (F0, T0, 0); break; ++ case T_DOUBLE: __ fld_d (F0, T0, 0); break; ++ default: ShouldNotReachHere(); ++ } ++ ++ __ jr(RA); ++ ++ slowcase_entry_pclist[count++] = __ pc(); ++ __ bind (slow); ++ address slow_case_addr = NULL; ++ switch (type) { ++ case T_BOOLEAN: slow_case_addr = jni_GetBooleanField_addr(); break; ++ case T_BYTE: slow_case_addr = jni_GetByteField_addr(); break; ++ case T_CHAR: slow_case_addr = jni_GetCharField_addr(); break; ++ case T_SHORT: slow_case_addr = jni_GetShortField_addr(); break; ++ case T_INT: slow_case_addr = jni_GetIntField_addr(); break; ++ case T_LONG: slow_case_addr = jni_GetLongField_addr(); break; ++ case T_FLOAT: slow_case_addr = jni_GetFloatField_addr(); break; ++ case T_DOUBLE: slow_case_addr = jni_GetDoubleField_addr(); break; ++ default: ShouldNotReachHere(); ++ } ++ __ jmp(slow_case_addr); ++ ++ __ flush (); ++ return fast_entry; ++} ++ ++address JNI_FastGetField::generate_fast_get_boolean_field() { ++ return generate_fast_get_int_field0(T_BOOLEAN); ++} ++ ++address JNI_FastGetField::generate_fast_get_byte_field() { ++ return generate_fast_get_int_field0(T_BYTE); ++} ++ ++address JNI_FastGetField::generate_fast_get_char_field() { ++ return generate_fast_get_int_field0(T_CHAR); ++} ++ ++address JNI_FastGetField::generate_fast_get_short_field() { ++ return generate_fast_get_int_field0(T_SHORT); ++} ++ ++address JNI_FastGetField::generate_fast_get_int_field() { ++ return generate_fast_get_int_field0(T_INT); ++} ++ ++address JNI_FastGetField::generate_fast_get_long_field() { ++ return generate_fast_get_int_field0(T_LONG); ++} ++ ++address JNI_FastGetField::generate_fast_get_float_field() { ++ return generate_fast_get_int_field0(T_FLOAT); ++} ++ ++address JNI_FastGetField::generate_fast_get_double_field() { ++ return generate_fast_get_int_field0(T_DOUBLE); ++} +diff --git a/hotspot/src/cpu/loongarch/vm/jniTypes_loongarch.hpp b/hotspot/src/cpu/loongarch/vm/jniTypes_loongarch.hpp +new file mode 100644 +index 0000000000..554ff216ac +--- /dev/null ++++ b/hotspot/src/cpu/loongarch/vm/jniTypes_loongarch.hpp +@@ -0,0 +1,144 @@ ++/* ++ * Copyright (c) 1998, 2010, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2015, 2020, Loongson Technology. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ * ++ */ ++ ++#ifndef CPU_LOONGARCH_VM_JNITYPES_LOONGARCH_HPP ++#define CPU_LOONGARCH_VM_JNITYPES_LOONGARCH_HPP ++ ++#include "memory/allocation.hpp" ++#include "oops/oop.hpp" ++#include "prims/jni.h" ++ ++// This file holds platform-dependent routines used to write primitive jni ++// types to the array of arguments passed into JavaCalls::call ++ ++class JNITypes : AllStatic { ++ // These functions write a java primitive type (in native format) ++ // to a java stack slot array to be passed as an argument to JavaCalls:calls. ++ // I.e., they are functionally 'push' operations if they have a 'pos' ++ // formal parameter. Note that jlong's and jdouble's are written ++ // _in reverse_ of the order in which they appear in the interpreter ++ // stack. This is because call stubs (see stubGenerator_sparc.cpp) ++ // reverse the argument list constructed by JavaCallArguments (see ++ // javaCalls.hpp). ++ ++private: ++ ++ // 32bit Helper routines. ++ static inline void put_int2r(jint *from, intptr_t *to) { *(jint *)(to++) = from[1]; ++ *(jint *)(to ) = from[0]; } ++ static inline void put_int2r(jint *from, intptr_t *to, int& pos) { put_int2r(from, to + pos); pos += 2; } ++ ++public: ++ // In LoongArch64, the sizeof intptr_t is 8 bytes, and each unit in JavaCallArguments::_value_buffer[] ++ // is 8 bytes. ++ // If we only write the low 4 bytes with (jint *), the high 4-bits will be left with uncertain values. ++ // Then, in JavaCallArguments::parameters(), the whole 8 bytes of a T_INT parameter is loaded. ++ // This error occurs in ReflectInvoke.java ++ // The parameter of DD(int) should be 4 instead of 0x550000004. ++ // ++ // See: [runtime/javaCalls.hpp] ++ ++ static inline void put_int(jint from, intptr_t *to) { *(intptr_t *)(to + 0 ) = from; } ++ static inline void put_int(jint from, intptr_t *to, int& pos) { *(intptr_t *)(to + pos++) = from; } ++ static inline void put_int(jint *from, intptr_t *to, int& pos) { *(intptr_t *)(to + pos++) = *from; } ++ ++ // Longs are stored in native format in one JavaCallArgument slot at ++ // *(to). ++ // In theory, *(to + 1) is an empty slot. But, for several Java2D testing programs (TestBorderLayout, SwingTest), ++ // *(to + 1) must contains a copy of the long value. Otherwise it will corrupts. ++ static inline void put_long(jlong from, intptr_t *to) { ++ *(jlong*) (to + 1) = from; ++ *(jlong*) (to) = from; ++ } ++ ++ // A long parameter occupies two slot. ++ // It must fit the layout rule in methodHandle. ++ // ++ // See: [runtime/reflection.cpp] Reflection::invoke() ++ // assert(java_args.size_of_parameters() == method->size_of_parameters(), "just checking"); ++ ++ static inline void put_long(jlong from, intptr_t *to, int& pos) { ++ *(jlong*) (to + 1 + pos) = from; ++ *(jlong*) (to + pos) = from; ++ pos += 2; ++ } ++ ++ static inline void put_long(jlong *from, intptr_t *to, int& pos) { ++ *(jlong*) (to + 1 + pos) = *from; ++ *(jlong*) (to + pos) = *from; ++ pos += 2; ++ } ++ ++ // Oops are stored in native format in one JavaCallArgument slot at *to. ++ static inline void put_obj(oop from, intptr_t *to) { *(oop *)(to + 0 ) = from; } ++ static inline void put_obj(oop from, intptr_t *to, int& pos) { *(oop *)(to + pos++) = from; } ++ static inline void put_obj(oop *from, intptr_t *to, int& pos) { *(oop *)(to + pos++) = *from; } ++ ++ // Floats are stored in native format in one JavaCallArgument slot at *to. ++ static inline void put_float(jfloat from, intptr_t *to) { *(jfloat *)(to + 0 ) = from; } ++ static inline void put_float(jfloat from, intptr_t *to, int& pos) { *(jfloat *)(to + pos++) = from; } ++ static inline void put_float(jfloat *from, intptr_t *to, int& pos) { *(jfloat *)(to + pos++) = *from; } ++ ++#undef _JNI_SLOT_OFFSET ++#define _JNI_SLOT_OFFSET 0 ++ ++ // Longs are stored in native format in one JavaCallArgument slot at ++ // *(to). ++ // In theory, *(to + 1) is an empty slot. But, for several Java2D testing programs (TestBorderLayout, SwingTest), ++ // *(to + 1) must contains a copy of the long value. Otherwise it will corrupts. ++ static inline void put_double(jdouble from, intptr_t *to) { ++ *(jdouble*) (to + 1) = from; ++ *(jdouble*) (to) = from; ++ } ++ ++ // A long parameter occupies two slot. ++ // It must fit the layout rule in methodHandle. ++ // ++ // See: [runtime/reflection.cpp] Reflection::invoke() ++ // assert(java_args.size_of_parameters() == method->size_of_parameters(), "just checking"); ++ ++ static inline void put_double(jdouble from, intptr_t *to, int& pos) { ++ *(jdouble*) (to + 1 + pos) = from; ++ *(jdouble*) (to + pos) = from; ++ pos += 2; ++ } ++ ++ static inline void put_double(jdouble *from, intptr_t *to, int& pos) { ++ *(jdouble*) (to + 1 + pos) = *from; ++ *(jdouble*) (to + pos) = *from; ++ pos += 2; ++ } ++ ++ // The get_xxx routines, on the other hand, actually _do_ fetch ++ // java primitive types from the interpreter stack. ++ static inline jint get_int (intptr_t *from) { return *(jint *) from; } ++ static inline jlong get_long (intptr_t *from) { return *(jlong *) (from + _JNI_SLOT_OFFSET); } ++ static inline oop get_obj (intptr_t *from) { return *(oop *) from; } ++ static inline jfloat get_float (intptr_t *from) { return *(jfloat *) from; } ++ static inline jdouble get_double(intptr_t *from) { return *(jdouble *)(from + _JNI_SLOT_OFFSET); } ++#undef _JNI_SLOT_OFFSET ++}; ++ ++#endif // CPU_LOONGARCH_VM_JNITYPES_LOONGARCH_HPP +diff --git a/hotspot/src/cpu/loongarch/vm/jni_loongarch.h b/hotspot/src/cpu/loongarch/vm/jni_loongarch.h +new file mode 100644 +index 0000000000..eb25cbc354 +--- /dev/null ++++ b/hotspot/src/cpu/loongarch/vm/jni_loongarch.h +@@ -0,0 +1,51 @@ ++/* ++ * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2015, 2020, Loongson Technology. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. Oracle designates this ++ * particular file as subject to the "Classpath" exception as provided ++ * by Oracle in the LICENSE file that accompanied this code. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ */ ++ ++#ifndef _JAVASOFT_JNI_MD_H_ ++#define _JAVASOFT_JNI_MD_H_ ++ ++// Note: please do not change these without also changing jni_md.h in the JDK ++// repository ++#ifndef __has_attribute ++ #define __has_attribute(x) 0 ++#endif ++#if (defined(__GNUC__) && ((__GNUC__ > 4) || (__GNUC__ == 4) && (__GNUC_MINOR__ > 2))) || __has_attribute(visibility) ++ #define JNIEXPORT __attribute__((visibility("default"))) ++ #define JNIIMPORT __attribute__((visibility("default"))) ++#else ++ #define JNIEXPORT ++ #define JNIIMPORT ++#endif ++ ++#define JNICALL ++ ++typedef int jint; ++ ++ typedef long jlong; ++ ++typedef signed char jbyte; ++ ++#endif +diff --git a/hotspot/src/cpu/loongarch/vm/loongarch.ad b/hotspot/src/cpu/loongarch/vm/loongarch.ad +new file mode 100644 +index 0000000000..48c44779e7 +--- /dev/null ++++ b/hotspot/src/cpu/loongarch/vm/loongarch.ad +@@ -0,0 +1,24 @@ ++// ++// Copyright (c) 2011, 2012, Oracle and/or its affiliates. All rights reserved. ++// Copyright (c) 2015, 2020, Loongson Technology. All rights reserved. ++// DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++// ++// This code is free software; you can redistribute it and/or modify it ++// under the terms of the GNU General Public License version 2 only, as ++// published by the Free Software Foundation. ++// ++// This code is distributed in the hope that it will be useful, but WITHOUT ++// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++// version 2 for more details (a copy is included in the LICENSE file that ++// accompanied this code). ++// ++// You should have received a copy of the GNU General Public License version ++// 2 along with this work; if not, write to the Free Software Foundation, ++// Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++// ++// Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++// or visit www.oracle.com if you need additional information or have any ++// questions. ++// ++// +diff --git a/hotspot/src/cpu/loongarch/vm/loongarch_64.ad b/hotspot/src/cpu/loongarch/vm/loongarch_64.ad +new file mode 100644 +index 0000000000..c3514373a6 +--- /dev/null ++++ b/hotspot/src/cpu/loongarch/vm/loongarch_64.ad +@@ -0,0 +1,12851 @@ ++// ++// Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved. ++// Copyright (c) 2015, 2022, Loongson Technology. All rights reserved. ++// DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++// ++// This code is free software; you can redistribute it and/or modify it ++// under the terms of the GNU General Public License version 2 only, as ++// published by the Free Software Foundation. ++// ++// This code is distributed in the hope that it will be useful, but WITHOUT ++// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++// version 2 for more details (a copy is included in the LICENSE file that ++// accompanied this code). ++// ++// You should have received a copy of the GNU General Public License version ++// 2 along with this work; if not, write to the Free Software Foundation, ++// Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++// ++// Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++// or visit www.oracle.com if you need additional information or have any ++// questions. ++// ++// ++ ++// GodSon3 Architecture Description File ++ ++//----------REGISTER DEFINITION BLOCK------------------------------------------ ++// This information is used by the matcher and the register allocator to ++// describe individual registers and classes of registers within the target ++// archtecture. ++ ++// format: ++// reg_def name (call convention, c-call convention, ideal type, encoding); ++// call convention : ++// NS = No-Save ++// SOC = Save-On-Call ++// SOE = Save-On-Entry ++// AS = Always-Save ++// ideal type : ++// see opto/opcodes.hpp for more info ++// reg_class name (reg, ...); ++// alloc_class name (reg, ...); ++register %{ ++ ++// General Registers ++// Integer Registers ++ reg_def R0 ( NS, NS, Op_RegI, 0, VMRegImpl::Bad()); ++ reg_def RA ( NS, NS, Op_RegI, 1, RA->as_VMReg()); ++ reg_def RA_H ( NS, NS, Op_RegI, 1, RA->as_VMReg()->next()); ++ // TODO: LA ++ reg_def TP ( NS, NS, Op_RegI, 2, TP->as_VMReg()); ++ reg_def TP_H ( NS, NS, Op_RegI, 2, TP->as_VMReg()->next()); ++ reg_def SP ( NS, NS, Op_RegI, 3, SP->as_VMReg()); ++ reg_def SP_H ( NS, NS, Op_RegI, 3, SP->as_VMReg()->next()); ++ reg_def A0 (SOC, SOC, Op_RegI, 4, A0->as_VMReg()); ++ reg_def A0_H (SOC, SOC, Op_RegI, 4, A0->as_VMReg()->next()); ++ reg_def A1 (SOC, SOC, Op_RegI, 5, A1->as_VMReg()); ++ reg_def A1_H (SOC, SOC, Op_RegI, 5, A1->as_VMReg()->next()); ++ reg_def A2 (SOC, SOC, Op_RegI, 6, A2->as_VMReg()); ++ reg_def A2_H (SOC, SOC, Op_RegI, 6, A2->as_VMReg()->next()); ++ reg_def A3 (SOC, SOC, Op_RegI, 7, A3->as_VMReg()); ++ reg_def A3_H (SOC, SOC, Op_RegI, 7, A3->as_VMReg()->next()); ++ reg_def A4 (SOC, SOC, Op_RegI, 8, A4->as_VMReg()); ++ reg_def A4_H (SOC, SOC, Op_RegI, 8, A4->as_VMReg()->next()); ++ reg_def A5 (SOC, SOC, Op_RegI, 9, A5->as_VMReg()); ++ reg_def A5_H (SOC, SOC, Op_RegI, 9, A5->as_VMReg()->next()); ++ reg_def A6 (SOC, SOC, Op_RegI, 10, A6->as_VMReg()); ++ reg_def A6_H (SOC, SOC, Op_RegI, 10, A6->as_VMReg()->next()); ++ reg_def A7 (SOC, SOC, Op_RegI, 11, A7->as_VMReg()); ++ reg_def A7_H (SOC, SOC, Op_RegI, 11, A7->as_VMReg()->next()); ++ reg_def T0 (SOC, SOC, Op_RegI, 12, T0->as_VMReg()); ++ reg_def T0_H (SOC, SOC, Op_RegI, 12, T0->as_VMReg()->next()); ++ reg_def T1 (SOC, SOC, Op_RegI, 13, T1->as_VMReg()); ++ reg_def T1_H (SOC, SOC, Op_RegI, 13, T1->as_VMReg()->next()); ++ reg_def T2 (SOC, SOC, Op_RegI, 14, T2->as_VMReg()); ++ reg_def T2_H (SOC, SOC, Op_RegI, 14, T2->as_VMReg()->next()); ++ reg_def T3 (SOC, SOC, Op_RegI, 15, T3->as_VMReg()); ++ reg_def T3_H (SOC, SOC, Op_RegI, 15, T3->as_VMReg()->next()); ++ reg_def T4 (SOC, SOC, Op_RegI, 16, T4->as_VMReg()); ++ reg_def T4_H (SOC, SOC, Op_RegI, 16, T4->as_VMReg()->next()); ++ reg_def T5 (SOC, SOC, Op_RegI, 17, T5->as_VMReg()); ++ reg_def T5_H (SOC, SOC, Op_RegI, 17, T5->as_VMReg()->next()); ++ reg_def T6 (SOC, SOC, Op_RegI, 18, T6->as_VMReg()); ++ reg_def T6_H (SOC, SOC, Op_RegI, 18, T6->as_VMReg()->next()); ++ reg_def T7 (SOC, SOC, Op_RegI, 19, T7->as_VMReg()); ++ reg_def T7_H (SOC, SOC, Op_RegI, 19, T7->as_VMReg()->next()); ++ reg_def T8 (SOC, SOC, Op_RegI, 20, T8->as_VMReg()); ++ reg_def T8_H (SOC, SOC, Op_RegI, 20, T8->as_VMReg()->next()); ++ reg_def RX ( NS, NS, Op_RegI, 21, RX->as_VMReg()); ++ reg_def RX_H ( NS, NS, Op_RegI, 21, RX->as_VMReg()->next()); ++ reg_def FP ( NS, NS, Op_RegI, 22, FP->as_VMReg()); ++ reg_def FP_H ( NS, NS, Op_RegI, 22, FP->as_VMReg()->next()); ++ reg_def S0 (SOC, SOE, Op_RegI, 23, S0->as_VMReg()); ++ reg_def S0_H (SOC, SOE, Op_RegI, 23, S0->as_VMReg()->next()); ++ reg_def S1 (SOC, SOE, Op_RegI, 24, S1->as_VMReg()); ++ reg_def S1_H (SOC, SOE, Op_RegI, 24, S1->as_VMReg()->next()); ++ reg_def S2 (SOC, SOE, Op_RegI, 25, S2->as_VMReg()); ++ reg_def S2_H (SOC, SOE, Op_RegI, 25, S2->as_VMReg()->next()); ++ reg_def S3 (SOC, SOE, Op_RegI, 26, S3->as_VMReg()); ++ reg_def S3_H (SOC, SOE, Op_RegI, 26, S3->as_VMReg()->next()); ++ reg_def S4 (SOC, SOE, Op_RegI, 27, S4->as_VMReg()); ++ reg_def S4_H (SOC, SOE, Op_RegI, 27, S4->as_VMReg()->next()); ++ reg_def S5 (SOC, SOE, Op_RegI, 28, S5->as_VMReg()); ++ reg_def S5_H (SOC, SOE, Op_RegI, 28, S5->as_VMReg()->next()); ++ reg_def S6 (SOC, SOE, Op_RegI, 29, S6->as_VMReg()); ++ reg_def S6_H (SOC, SOE, Op_RegI, 29, S6->as_VMReg()->next()); ++ reg_def S7 (SOC, SOE, Op_RegI, 30, S7->as_VMReg()); ++ reg_def S7_H (SOC, SOE, Op_RegI, 30, S7->as_VMReg()->next()); ++ // TODO: LA ++ reg_def S8 ( NS, NS, Op_RegI, 31, S8->as_VMReg()); ++ reg_def S8_H ( NS, NS, Op_RegI, 31, S8->as_VMReg()->next()); ++ ++ ++// Floating/Vector registers. ++reg_def F0 ( SOC, SOC, Op_RegF, 0, F0->as_VMReg() ); ++reg_def F0_H ( SOC, SOC, Op_RegF, 0, F0->as_VMReg()->next() ); ++reg_def F0_J ( SOC, SOC, Op_RegF, 0, F0->as_VMReg()->next(2) ); ++reg_def F0_K ( SOC, SOC, Op_RegF, 0, F0->as_VMReg()->next(3) ); ++reg_def F0_L ( SOC, SOC, Op_RegF, 0, F0->as_VMReg()->next(4) ); ++reg_def F0_M ( SOC, SOC, Op_RegF, 0, F0->as_VMReg()->next(5) ); ++reg_def F0_N ( SOC, SOC, Op_RegF, 0, F0->as_VMReg()->next(6) ); ++reg_def F0_O ( SOC, SOC, Op_RegF, 0, F0->as_VMReg()->next(7) ); ++ ++reg_def F1 ( SOC, SOC, Op_RegF, 1, F1->as_VMReg() ); ++reg_def F1_H ( SOC, SOC, Op_RegF, 1, F1->as_VMReg()->next() ); ++reg_def F1_J ( SOC, SOC, Op_RegF, 1, F1->as_VMReg()->next(2) ); ++reg_def F1_K ( SOC, SOC, Op_RegF, 1, F1->as_VMReg()->next(3) ); ++reg_def F1_L ( SOC, SOC, Op_RegF, 1, F1->as_VMReg()->next(4) ); ++reg_def F1_M ( SOC, SOC, Op_RegF, 1, F1->as_VMReg()->next(5) ); ++reg_def F1_N ( SOC, SOC, Op_RegF, 1, F1->as_VMReg()->next(6) ); ++reg_def F1_O ( SOC, SOC, Op_RegF, 1, F1->as_VMReg()->next(7) ); ++ ++reg_def F2 ( SOC, SOC, Op_RegF, 2, F2->as_VMReg() ); ++reg_def F2_H ( SOC, SOC, Op_RegF, 2, F2->as_VMReg()->next() ); ++reg_def F2_J ( SOC, SOC, Op_RegF, 2, F2->as_VMReg()->next(2) ); ++reg_def F2_K ( SOC, SOC, Op_RegF, 2, F2->as_VMReg()->next(3) ); ++reg_def F2_L ( SOC, SOC, Op_RegF, 2, F2->as_VMReg()->next(4) ); ++reg_def F2_M ( SOC, SOC, Op_RegF, 2, F2->as_VMReg()->next(5) ); ++reg_def F2_N ( SOC, SOC, Op_RegF, 2, F2->as_VMReg()->next(6) ); ++reg_def F2_O ( SOC, SOC, Op_RegF, 2, F2->as_VMReg()->next(7) ); ++ ++reg_def F3 ( SOC, SOC, Op_RegF, 3, F3->as_VMReg() ); ++reg_def F3_H ( SOC, SOC, Op_RegF, 3, F3->as_VMReg()->next() ); ++reg_def F3_J ( SOC, SOC, Op_RegF, 3, F3->as_VMReg()->next(2) ); ++reg_def F3_K ( SOC, SOC, Op_RegF, 3, F3->as_VMReg()->next(3) ); ++reg_def F3_L ( SOC, SOC, Op_RegF, 3, F3->as_VMReg()->next(4) ); ++reg_def F3_M ( SOC, SOC, Op_RegF, 3, F3->as_VMReg()->next(5) ); ++reg_def F3_N ( SOC, SOC, Op_RegF, 3, F3->as_VMReg()->next(6) ); ++reg_def F3_O ( SOC, SOC, Op_RegF, 3, F3->as_VMReg()->next(7) ); ++ ++reg_def F4 ( SOC, SOC, Op_RegF, 4, F4->as_VMReg() ); ++reg_def F4_H ( SOC, SOC, Op_RegF, 4, F4->as_VMReg()->next() ); ++reg_def F4_J ( SOC, SOC, Op_RegF, 4, F4->as_VMReg()->next(2) ); ++reg_def F4_K ( SOC, SOC, Op_RegF, 4, F4->as_VMReg()->next(3) ); ++reg_def F4_L ( SOC, SOC, Op_RegF, 4, F4->as_VMReg()->next(4) ); ++reg_def F4_M ( SOC, SOC, Op_RegF, 4, F4->as_VMReg()->next(5) ); ++reg_def F4_N ( SOC, SOC, Op_RegF, 4, F4->as_VMReg()->next(6) ); ++reg_def F4_O ( SOC, SOC, Op_RegF, 4, F4->as_VMReg()->next(7) ); ++ ++reg_def F5 ( SOC, SOC, Op_RegF, 5, F5->as_VMReg() ); ++reg_def F5_H ( SOC, SOC, Op_RegF, 5, F5->as_VMReg()->next() ); ++reg_def F5_J ( SOC, SOC, Op_RegF, 5, F5->as_VMReg()->next(2) ); ++reg_def F5_K ( SOC, SOC, Op_RegF, 5, F5->as_VMReg()->next(3) ); ++reg_def F5_L ( SOC, SOC, Op_RegF, 5, F5->as_VMReg()->next(4) ); ++reg_def F5_M ( SOC, SOC, Op_RegF, 5, F5->as_VMReg()->next(5) ); ++reg_def F5_N ( SOC, SOC, Op_RegF, 5, F5->as_VMReg()->next(6) ); ++reg_def F5_O ( SOC, SOC, Op_RegF, 5, F5->as_VMReg()->next(7) ); ++ ++reg_def F6 ( SOC, SOC, Op_RegF, 6, F6->as_VMReg() ); ++reg_def F6_H ( SOC, SOC, Op_RegF, 6, F6->as_VMReg()->next() ); ++reg_def F6_J ( SOC, SOC, Op_RegF, 6, F6->as_VMReg()->next(2) ); ++reg_def F6_K ( SOC, SOC, Op_RegF, 6, F6->as_VMReg()->next(3) ); ++reg_def F6_L ( SOC, SOC, Op_RegF, 6, F6->as_VMReg()->next(4) ); ++reg_def F6_M ( SOC, SOC, Op_RegF, 6, F6->as_VMReg()->next(5) ); ++reg_def F6_N ( SOC, SOC, Op_RegF, 6, F6->as_VMReg()->next(6) ); ++reg_def F6_O ( SOC, SOC, Op_RegF, 6, F6->as_VMReg()->next(7) ); ++ ++reg_def F7 ( SOC, SOC, Op_RegF, 7, F7->as_VMReg() ); ++reg_def F7_H ( SOC, SOC, Op_RegF, 7, F7->as_VMReg()->next() ); ++reg_def F7_J ( SOC, SOC, Op_RegF, 7, F7->as_VMReg()->next(2) ); ++reg_def F7_K ( SOC, SOC, Op_RegF, 7, F7->as_VMReg()->next(3) ); ++reg_def F7_L ( SOC, SOC, Op_RegF, 7, F7->as_VMReg()->next(4) ); ++reg_def F7_M ( SOC, SOC, Op_RegF, 7, F7->as_VMReg()->next(5) ); ++reg_def F7_N ( SOC, SOC, Op_RegF, 7, F7->as_VMReg()->next(6) ); ++reg_def F7_O ( SOC, SOC, Op_RegF, 7, F7->as_VMReg()->next(7) ); ++ ++reg_def F8 ( SOC, SOC, Op_RegF, 8, F8->as_VMReg() ); ++reg_def F8_H ( SOC, SOC, Op_RegF, 8, F8->as_VMReg()->next() ); ++reg_def F8_J ( SOC, SOC, Op_RegF, 8, F8->as_VMReg()->next(2) ); ++reg_def F8_K ( SOC, SOC, Op_RegF, 8, F8->as_VMReg()->next(3) ); ++reg_def F8_L ( SOC, SOC, Op_RegF, 8, F8->as_VMReg()->next(4) ); ++reg_def F8_M ( SOC, SOC, Op_RegF, 8, F8->as_VMReg()->next(5) ); ++reg_def F8_N ( SOC, SOC, Op_RegF, 8, F8->as_VMReg()->next(6) ); ++reg_def F8_O ( SOC, SOC, Op_RegF, 8, F8->as_VMReg()->next(7) ); ++ ++reg_def F9 ( SOC, SOC, Op_RegF, 9, F9->as_VMReg() ); ++reg_def F9_H ( SOC, SOC, Op_RegF, 9, F9->as_VMReg()->next() ); ++reg_def F9_J ( SOC, SOC, Op_RegF, 9, F9->as_VMReg()->next(2) ); ++reg_def F9_K ( SOC, SOC, Op_RegF, 9, F9->as_VMReg()->next(3) ); ++reg_def F9_L ( SOC, SOC, Op_RegF, 9, F9->as_VMReg()->next(4) ); ++reg_def F9_M ( SOC, SOC, Op_RegF, 9, F9->as_VMReg()->next(5) ); ++reg_def F9_N ( SOC, SOC, Op_RegF, 9, F9->as_VMReg()->next(6) ); ++reg_def F9_O ( SOC, SOC, Op_RegF, 9, F9->as_VMReg()->next(7) ); ++ ++reg_def F10 ( SOC, SOC, Op_RegF, 10, F10->as_VMReg() ); ++reg_def F10_H ( SOC, SOC, Op_RegF, 10, F10->as_VMReg()->next() ); ++reg_def F10_J ( SOC, SOC, Op_RegF, 10, F10->as_VMReg()->next(2) ); ++reg_def F10_K ( SOC, SOC, Op_RegF, 10, F10->as_VMReg()->next(3) ); ++reg_def F10_L ( SOC, SOC, Op_RegF, 10, F10->as_VMReg()->next(4) ); ++reg_def F10_M ( SOC, SOC, Op_RegF, 10, F10->as_VMReg()->next(5) ); ++reg_def F10_N ( SOC, SOC, Op_RegF, 10, F10->as_VMReg()->next(6) ); ++reg_def F10_O ( SOC, SOC, Op_RegF, 10, F10->as_VMReg()->next(7) ); ++ ++reg_def F11 ( SOC, SOC, Op_RegF, 11, F11->as_VMReg() ); ++reg_def F11_H ( SOC, SOC, Op_RegF, 11, F11->as_VMReg()->next() ); ++reg_def F11_J ( SOC, SOC, Op_RegF, 11, F11->as_VMReg()->next(2) ); ++reg_def F11_K ( SOC, SOC, Op_RegF, 11, F11->as_VMReg()->next(3) ); ++reg_def F11_L ( SOC, SOC, Op_RegF, 11, F11->as_VMReg()->next(4) ); ++reg_def F11_M ( SOC, SOC, Op_RegF, 11, F11->as_VMReg()->next(5) ); ++reg_def F11_N ( SOC, SOC, Op_RegF, 11, F11->as_VMReg()->next(6) ); ++reg_def F11_O ( SOC, SOC, Op_RegF, 11, F11->as_VMReg()->next(7) ); ++ ++reg_def F12 ( SOC, SOC, Op_RegF, 12, F12->as_VMReg() ); ++reg_def F12_H ( SOC, SOC, Op_RegF, 12, F12->as_VMReg()->next() ); ++reg_def F12_J ( SOC, SOC, Op_RegF, 12, F12->as_VMReg()->next(2) ); ++reg_def F12_K ( SOC, SOC, Op_RegF, 12, F12->as_VMReg()->next(3) ); ++reg_def F12_L ( SOC, SOC, Op_RegF, 12, F12->as_VMReg()->next(4) ); ++reg_def F12_M ( SOC, SOC, Op_RegF, 12, F12->as_VMReg()->next(5) ); ++reg_def F12_N ( SOC, SOC, Op_RegF, 12, F12->as_VMReg()->next(6) ); ++reg_def F12_O ( SOC, SOC, Op_RegF, 12, F12->as_VMReg()->next(7) ); ++ ++reg_def F13 ( SOC, SOC, Op_RegF, 13, F13->as_VMReg() ); ++reg_def F13_H ( SOC, SOC, Op_RegF, 13, F13->as_VMReg()->next() ); ++reg_def F13_J ( SOC, SOC, Op_RegF, 13, F13->as_VMReg()->next(2) ); ++reg_def F13_K ( SOC, SOC, Op_RegF, 13, F13->as_VMReg()->next(3) ); ++reg_def F13_L ( SOC, SOC, Op_RegF, 13, F13->as_VMReg()->next(4) ); ++reg_def F13_M ( SOC, SOC, Op_RegF, 13, F13->as_VMReg()->next(5) ); ++reg_def F13_N ( SOC, SOC, Op_RegF, 13, F13->as_VMReg()->next(6) ); ++reg_def F13_O ( SOC, SOC, Op_RegF, 13, F13->as_VMReg()->next(7) ); ++ ++reg_def F14 ( SOC, SOC, Op_RegF, 14, F14->as_VMReg() ); ++reg_def F14_H ( SOC, SOC, Op_RegF, 14, F14->as_VMReg()->next() ); ++reg_def F14_J ( SOC, SOC, Op_RegF, 14, F14->as_VMReg()->next(2) ); ++reg_def F14_K ( SOC, SOC, Op_RegF, 14, F14->as_VMReg()->next(3) ); ++reg_def F14_L ( SOC, SOC, Op_RegF, 14, F14->as_VMReg()->next(4) ); ++reg_def F14_M ( SOC, SOC, Op_RegF, 14, F14->as_VMReg()->next(5) ); ++reg_def F14_N ( SOC, SOC, Op_RegF, 14, F14->as_VMReg()->next(6) ); ++reg_def F14_O ( SOC, SOC, Op_RegF, 14, F14->as_VMReg()->next(7) ); ++ ++reg_def F15 ( SOC, SOC, Op_RegF, 15, F15->as_VMReg() ); ++reg_def F15_H ( SOC, SOC, Op_RegF, 15, F15->as_VMReg()->next() ); ++reg_def F15_J ( SOC, SOC, Op_RegF, 15, F15->as_VMReg()->next(2) ); ++reg_def F15_K ( SOC, SOC, Op_RegF, 15, F15->as_VMReg()->next(3) ); ++reg_def F15_L ( SOC, SOC, Op_RegF, 15, F15->as_VMReg()->next(4) ); ++reg_def F15_M ( SOC, SOC, Op_RegF, 15, F15->as_VMReg()->next(5) ); ++reg_def F15_N ( SOC, SOC, Op_RegF, 15, F15->as_VMReg()->next(6) ); ++reg_def F15_O ( SOC, SOC, Op_RegF, 15, F15->as_VMReg()->next(7) ); ++ ++reg_def F16 ( SOC, SOC, Op_RegF, 16, F16->as_VMReg() ); ++reg_def F16_H ( SOC, SOC, Op_RegF, 16, F16->as_VMReg()->next() ); ++reg_def F16_J ( SOC, SOC, Op_RegF, 16, F16->as_VMReg()->next(2) ); ++reg_def F16_K ( SOC, SOC, Op_RegF, 16, F16->as_VMReg()->next(3) ); ++reg_def F16_L ( SOC, SOC, Op_RegF, 16, F16->as_VMReg()->next(4) ); ++reg_def F16_M ( SOC, SOC, Op_RegF, 16, F16->as_VMReg()->next(5) ); ++reg_def F16_N ( SOC, SOC, Op_RegF, 16, F16->as_VMReg()->next(6) ); ++reg_def F16_O ( SOC, SOC, Op_RegF, 16, F16->as_VMReg()->next(7) ); ++ ++reg_def F17 ( SOC, SOC, Op_RegF, 17, F17->as_VMReg() ); ++reg_def F17_H ( SOC, SOC, Op_RegF, 17, F17->as_VMReg()->next() ); ++reg_def F17_J ( SOC, SOC, Op_RegF, 17, F17->as_VMReg()->next(2) ); ++reg_def F17_K ( SOC, SOC, Op_RegF, 17, F17->as_VMReg()->next(3) ); ++reg_def F17_L ( SOC, SOC, Op_RegF, 17, F17->as_VMReg()->next(4) ); ++reg_def F17_M ( SOC, SOC, Op_RegF, 17, F17->as_VMReg()->next(5) ); ++reg_def F17_N ( SOC, SOC, Op_RegF, 17, F17->as_VMReg()->next(6) ); ++reg_def F17_O ( SOC, SOC, Op_RegF, 17, F17->as_VMReg()->next(7) ); ++ ++reg_def F18 ( SOC, SOC, Op_RegF, 18, F18->as_VMReg() ); ++reg_def F18_H ( SOC, SOC, Op_RegF, 18, F18->as_VMReg()->next() ); ++reg_def F18_J ( SOC, SOC, Op_RegF, 18, F18->as_VMReg()->next(2) ); ++reg_def F18_K ( SOC, SOC, Op_RegF, 18, F18->as_VMReg()->next(3) ); ++reg_def F18_L ( SOC, SOC, Op_RegF, 18, F18->as_VMReg()->next(4) ); ++reg_def F18_M ( SOC, SOC, Op_RegF, 18, F18->as_VMReg()->next(5) ); ++reg_def F18_N ( SOC, SOC, Op_RegF, 18, F18->as_VMReg()->next(6) ); ++reg_def F18_O ( SOC, SOC, Op_RegF, 18, F18->as_VMReg()->next(7) ); ++ ++reg_def F19 ( SOC, SOC, Op_RegF, 19, F19->as_VMReg() ); ++reg_def F19_H ( SOC, SOC, Op_RegF, 19, F19->as_VMReg()->next() ); ++reg_def F19_J ( SOC, SOC, Op_RegF, 19, F19->as_VMReg()->next(2) ); ++reg_def F19_K ( SOC, SOC, Op_RegF, 19, F19->as_VMReg()->next(3) ); ++reg_def F19_L ( SOC, SOC, Op_RegF, 19, F19->as_VMReg()->next(4) ); ++reg_def F19_M ( SOC, SOC, Op_RegF, 19, F19->as_VMReg()->next(5) ); ++reg_def F19_N ( SOC, SOC, Op_RegF, 19, F19->as_VMReg()->next(6) ); ++reg_def F19_O ( SOC, SOC, Op_RegF, 19, F19->as_VMReg()->next(7) ); ++ ++reg_def F20 ( SOC, SOC, Op_RegF, 20, F20->as_VMReg() ); ++reg_def F20_H ( SOC, SOC, Op_RegF, 20, F20->as_VMReg()->next() ); ++reg_def F20_J ( SOC, SOC, Op_RegF, 20, F20->as_VMReg()->next(2) ); ++reg_def F20_K ( SOC, SOC, Op_RegF, 20, F20->as_VMReg()->next(3) ); ++reg_def F20_L ( SOC, SOC, Op_RegF, 20, F20->as_VMReg()->next(4) ); ++reg_def F20_M ( SOC, SOC, Op_RegF, 20, F20->as_VMReg()->next(5) ); ++reg_def F20_N ( SOC, SOC, Op_RegF, 20, F20->as_VMReg()->next(6) ); ++reg_def F20_O ( SOC, SOC, Op_RegF, 20, F20->as_VMReg()->next(7) ); ++ ++reg_def F21 ( SOC, SOC, Op_RegF, 21, F21->as_VMReg() ); ++reg_def F21_H ( SOC, SOC, Op_RegF, 21, F21->as_VMReg()->next() ); ++reg_def F21_J ( SOC, SOC, Op_RegF, 21, F21->as_VMReg()->next(2) ); ++reg_def F21_K ( SOC, SOC, Op_RegF, 21, F21->as_VMReg()->next(3) ); ++reg_def F21_L ( SOC, SOC, Op_RegF, 21, F21->as_VMReg()->next(4) ); ++reg_def F21_M ( SOC, SOC, Op_RegF, 21, F21->as_VMReg()->next(5) ); ++reg_def F21_N ( SOC, SOC, Op_RegF, 21, F21->as_VMReg()->next(6) ); ++reg_def F21_O ( SOC, SOC, Op_RegF, 21, F21->as_VMReg()->next(7) ); ++ ++reg_def F22 ( SOC, SOC, Op_RegF, 22, F22->as_VMReg() ); ++reg_def F22_H ( SOC, SOC, Op_RegF, 22, F22->as_VMReg()->next() ); ++reg_def F22_J ( SOC, SOC, Op_RegF, 22, F22->as_VMReg()->next(2) ); ++reg_def F22_K ( SOC, SOC, Op_RegF, 22, F22->as_VMReg()->next(3) ); ++reg_def F22_L ( SOC, SOC, Op_RegF, 22, F22->as_VMReg()->next(4) ); ++reg_def F22_M ( SOC, SOC, Op_RegF, 22, F22->as_VMReg()->next(5) ); ++reg_def F22_N ( SOC, SOC, Op_RegF, 22, F22->as_VMReg()->next(6) ); ++reg_def F22_O ( SOC, SOC, Op_RegF, 22, F22->as_VMReg()->next(7) ); ++ ++reg_def F23 ( SOC, SOC, Op_RegF, 23, F23->as_VMReg() ); ++reg_def F23_H ( SOC, SOC, Op_RegF, 23, F23->as_VMReg()->next() ); ++reg_def F23_J ( SOC, SOC, Op_RegF, 23, F23->as_VMReg()->next(2) ); ++reg_def F23_K ( SOC, SOC, Op_RegF, 23, F23->as_VMReg()->next(3) ); ++reg_def F23_L ( SOC, SOC, Op_RegF, 23, F23->as_VMReg()->next(4) ); ++reg_def F23_M ( SOC, SOC, Op_RegF, 23, F23->as_VMReg()->next(5) ); ++reg_def F23_N ( SOC, SOC, Op_RegF, 23, F23->as_VMReg()->next(6) ); ++reg_def F23_O ( SOC, SOC, Op_RegF, 23, F23->as_VMReg()->next(7) ); ++ ++reg_def F24 ( SOC, SOC, Op_RegF, 24, F24->as_VMReg() ); ++reg_def F24_H ( SOC, SOC, Op_RegF, 24, F24->as_VMReg()->next() ); ++reg_def F24_J ( SOC, SOC, Op_RegF, 24, F24->as_VMReg()->next(2) ); ++reg_def F24_K ( SOC, SOC, Op_RegF, 24, F24->as_VMReg()->next(3) ); ++reg_def F24_L ( SOC, SOC, Op_RegF, 24, F24->as_VMReg()->next(4) ); ++reg_def F24_M ( SOC, SOC, Op_RegF, 24, F24->as_VMReg()->next(5) ); ++reg_def F24_N ( SOC, SOC, Op_RegF, 24, F24->as_VMReg()->next(6) ); ++reg_def F24_O ( SOC, SOC, Op_RegF, 24, F24->as_VMReg()->next(7) ); ++ ++reg_def F25 ( SOC, SOC, Op_RegF, 25, F25->as_VMReg() ); ++reg_def F25_H ( SOC, SOC, Op_RegF, 25, F25->as_VMReg()->next() ); ++reg_def F25_J ( SOC, SOC, Op_RegF, 25, F25->as_VMReg()->next(2) ); ++reg_def F25_K ( SOC, SOC, Op_RegF, 25, F25->as_VMReg()->next(3) ); ++reg_def F25_L ( SOC, SOC, Op_RegF, 25, F25->as_VMReg()->next(4) ); ++reg_def F25_M ( SOC, SOC, Op_RegF, 25, F25->as_VMReg()->next(5) ); ++reg_def F25_N ( SOC, SOC, Op_RegF, 25, F25->as_VMReg()->next(6) ); ++reg_def F25_O ( SOC, SOC, Op_RegF, 25, F25->as_VMReg()->next(7) ); ++ ++reg_def F26 ( SOC, SOC, Op_RegF, 26, F26->as_VMReg() ); ++reg_def F26_H ( SOC, SOC, Op_RegF, 26, F26->as_VMReg()->next() ); ++reg_def F26_J ( SOC, SOC, Op_RegF, 26, F26->as_VMReg()->next(2) ); ++reg_def F26_K ( SOC, SOC, Op_RegF, 26, F26->as_VMReg()->next(3) ); ++reg_def F26_L ( SOC, SOC, Op_RegF, 26, F26->as_VMReg()->next(4) ); ++reg_def F26_M ( SOC, SOC, Op_RegF, 26, F26->as_VMReg()->next(5) ); ++reg_def F26_N ( SOC, SOC, Op_RegF, 26, F26->as_VMReg()->next(6) ); ++reg_def F26_O ( SOC, SOC, Op_RegF, 26, F26->as_VMReg()->next(7) ); ++ ++reg_def F27 ( SOC, SOC, Op_RegF, 27, F27->as_VMReg() ); ++reg_def F27_H ( SOC, SOC, Op_RegF, 27, F27->as_VMReg()->next() ); ++reg_def F27_J ( SOC, SOC, Op_RegF, 27, F27->as_VMReg()->next(2) ); ++reg_def F27_K ( SOC, SOC, Op_RegF, 27, F27->as_VMReg()->next(3) ); ++reg_def F27_L ( SOC, SOC, Op_RegF, 27, F27->as_VMReg()->next(4) ); ++reg_def F27_M ( SOC, SOC, Op_RegF, 27, F27->as_VMReg()->next(5) ); ++reg_def F27_N ( SOC, SOC, Op_RegF, 27, F27->as_VMReg()->next(6) ); ++reg_def F27_O ( SOC, SOC, Op_RegF, 27, F27->as_VMReg()->next(7) ); ++ ++reg_def F28 ( SOC, SOC, Op_RegF, 28, F28->as_VMReg() ); ++reg_def F28_H ( SOC, SOC, Op_RegF, 28, F28->as_VMReg()->next() ); ++reg_def F28_J ( SOC, SOC, Op_RegF, 28, F28->as_VMReg()->next(2) ); ++reg_def F28_K ( SOC, SOC, Op_RegF, 28, F28->as_VMReg()->next(3) ); ++reg_def F28_L ( SOC, SOC, Op_RegF, 28, F28->as_VMReg()->next(4) ); ++reg_def F28_M ( SOC, SOC, Op_RegF, 28, F28->as_VMReg()->next(5) ); ++reg_def F28_N ( SOC, SOC, Op_RegF, 28, F28->as_VMReg()->next(6) ); ++reg_def F28_O ( SOC, SOC, Op_RegF, 28, F28->as_VMReg()->next(7) ); ++ ++reg_def F29 ( SOC, SOC, Op_RegF, 29, F29->as_VMReg() ); ++reg_def F29_H ( SOC, SOC, Op_RegF, 29, F29->as_VMReg()->next() ); ++reg_def F29_J ( SOC, SOC, Op_RegF, 29, F29->as_VMReg()->next(2) ); ++reg_def F29_K ( SOC, SOC, Op_RegF, 29, F29->as_VMReg()->next(3) ); ++reg_def F29_L ( SOC, SOC, Op_RegF, 29, F29->as_VMReg()->next(4) ); ++reg_def F29_M ( SOC, SOC, Op_RegF, 29, F29->as_VMReg()->next(5) ); ++reg_def F29_N ( SOC, SOC, Op_RegF, 29, F29->as_VMReg()->next(6) ); ++reg_def F29_O ( SOC, SOC, Op_RegF, 29, F29->as_VMReg()->next(7) ); ++ ++reg_def F30 ( SOC, SOC, Op_RegF, 30, F30->as_VMReg() ); ++reg_def F30_H ( SOC, SOC, Op_RegF, 30, F30->as_VMReg()->next() ); ++reg_def F30_J ( SOC, SOC, Op_RegF, 30, F30->as_VMReg()->next(2) ); ++reg_def F30_K ( SOC, SOC, Op_RegF, 30, F30->as_VMReg()->next(3) ); ++reg_def F30_L ( SOC, SOC, Op_RegF, 30, F30->as_VMReg()->next(4) ); ++reg_def F30_M ( SOC, SOC, Op_RegF, 30, F30->as_VMReg()->next(5) ); ++reg_def F30_N ( SOC, SOC, Op_RegF, 30, F30->as_VMReg()->next(6) ); ++reg_def F30_O ( SOC, SOC, Op_RegF, 30, F30->as_VMReg()->next(7) ); ++ ++reg_def F31 ( SOC, SOC, Op_RegF, 31, F31->as_VMReg() ); ++reg_def F31_H ( SOC, SOC, Op_RegF, 31, F31->as_VMReg()->next() ); ++reg_def F31_J ( SOC, SOC, Op_RegF, 31, F31->as_VMReg()->next(2) ); ++reg_def F31_K ( SOC, SOC, Op_RegF, 31, F31->as_VMReg()->next(3) ); ++reg_def F31_L ( SOC, SOC, Op_RegF, 31, F31->as_VMReg()->next(4) ); ++reg_def F31_M ( SOC, SOC, Op_RegF, 31, F31->as_VMReg()->next(5) ); ++reg_def F31_N ( SOC, SOC, Op_RegF, 31, F31->as_VMReg()->next(6) ); ++reg_def F31_O ( SOC, SOC, Op_RegF, 31, F31->as_VMReg()->next(7) ); ++ ++ ++// ---------------------------- ++// Special Registers ++//S6 is used for get_thread(S6) ++//S5 is uesd for heapbase of compressed oop ++alloc_class chunk0( ++ S7, S7_H, ++ S0, S0_H, ++ S1, S1_H, ++ S2, S2_H, ++ S4, S4_H, ++ S5, S5_H, ++ S6, S6_H, ++ S3, S3_H, ++ T2, T2_H, ++ T3, T3_H, ++ T8, T8_H, ++ T4, T4_H, ++ T1, T1_H, // inline_cache_reg ++ T6, T6_H, ++ A7, A7_H, ++ A6, A6_H, ++ A5, A5_H, ++ A4, A4_H, ++ T5, T5_H, ++ A3, A3_H, ++ A2, A2_H, ++ A1, A1_H, ++ A0, A0_H, ++ T0, T0_H, ++ S8, S8_H ++ RA, RA_H, ++ SP, SP_H, // stack_pointer ++ FP, FP_H // frame_pointer ++ ); ++ ++// F23 is scratch reg ++alloc_class chunk1( F0, F0_H, F0_J, F0_K, F0_L, F0_M, F0_N, F0_O, ++ F1, F1_H, F1_J, F1_K, F1_L, F1_M, F1_N, F1_O, ++ F2, F2_H, F2_J, F2_K, F2_L, F2_M, F2_N, F2_O, ++ F3, F3_H, F3_J, F3_K, F3_L, F3_M, F3_N, F3_O, ++ F4, F4_H, F4_J, F4_K, F4_L, F4_M, F4_N, F4_O, ++ F5, F5_H, F5_J, F5_K, F5_L, F5_M, F5_N, F5_O, ++ F6, F6_H, F6_J, F6_K, F6_L, F6_M, F6_N, F6_O, ++ F7, F7_H, F7_J, F7_K, F7_L, F7_M, F7_N, F7_O, ++ F8, F8_H, F8_J, F8_K, F8_L, F8_M, F8_N, F8_O, ++ F9, F9_H, F9_J, F9_K, F9_L, F9_M, F9_N, F9_O, ++ F10, F10_H, F10_J, F10_K, F10_L, F10_M, F10_N, F10_O, ++ F11, F11_H, F11_J, F11_K, F11_L, F11_M, F11_N, F11_O, ++ F12, F12_H, F12_J, F12_K, F12_L, F12_M, F12_N, F12_O, ++ F13, F13_H, F13_J, F13_K, F13_L, F13_M, F13_N, F13_O, ++ F14, F14_H, F14_J, F14_K, F14_L, F14_M, F14_N, F14_O, ++ F15, F15_H, F15_J, F15_K, F15_L, F15_M, F15_N, F15_O, ++ F16, F16_H, F16_J, F16_K, F16_L, F16_M, F16_N, F16_O, ++ F17, F17_H, F17_J, F17_K, F17_L, F17_M, F17_N, F17_O, ++ F18, F18_H, F18_J, F18_K, F18_L, F18_M, F18_N, F18_O, ++ F19, F19_H, F19_J, F19_K, F19_L, F19_M, F19_N, F19_O, ++ F20, F20_H, F20_J, F20_K, F20_L, F20_M, F20_N, F20_O, ++ F21, F21_H, F21_J, F21_K, F21_L, F21_M, F21_N, F21_O, ++ F22, F22_H, F22_J, F22_K, F22_L, F22_M, F22_N, F22_O, ++ F24, F24_H, F24_J, F24_K, F24_L, F24_M, F24_N, F24_O, ++ F25, F25_H, F25_J, F25_K, F25_L, F25_M, F25_N, F25_O, ++ F26, F26_H, F26_J, F26_K, F26_L, F26_M, F26_N, F26_O, ++ F27, F27_H, F27_J, F27_K, F27_L, F27_M, F27_N, F27_O, ++ F28, F28_H, F28_J, F28_K, F28_L, F28_M, F28_N, F28_O, ++ F29, F29_H, F29_J, F29_K, F29_L, F29_M, F29_N, F29_O, ++ F30, F30_H, F30_J, F30_K, F30_L, F30_M, F30_N, F30_O, ++ F31, F31_H, F31_J, F31_K, F31_L, F31_M, F31_N, F31_O); ++ ++reg_class s_reg( S0, S1, S2, S3, S4, S5, S6, S7 ); ++reg_class s0_reg( S0 ); ++reg_class s1_reg( S1 ); ++reg_class s2_reg( S2 ); ++reg_class s3_reg( S3 ); ++reg_class s4_reg( S4 ); ++reg_class s5_reg( S5 ); ++reg_class s6_reg( S6 ); ++reg_class s7_reg( S7 ); ++ ++reg_class t_reg( T0, T1, T2, T3, T8, T4 ); ++reg_class t0_reg( T0 ); ++reg_class t1_reg( T1 ); ++reg_class t2_reg( T2 ); ++reg_class t3_reg( T3 ); ++reg_class t8_reg( T8 ); ++reg_class t4_reg( T4 ); ++ ++reg_class a_reg( A0, A1, A2, A3, A4, A5, A6, A7 ); ++reg_class a0_reg( A0 ); ++reg_class a1_reg( A1 ); ++reg_class a2_reg( A2 ); ++reg_class a3_reg( A3 ); ++reg_class a4_reg( A4 ); ++reg_class a5_reg( A5 ); ++reg_class a6_reg( A6 ); ++reg_class a7_reg( A7 ); ++ ++// TODO: LA ++//reg_class v0_reg( A0 ); ++//reg_class v1_reg( A1 ); ++ ++reg_class sp_reg( SP, SP_H ); ++reg_class fp_reg( FP, FP_H ); ++ ++reg_class v0_long_reg( A0, A0_H ); ++reg_class v1_long_reg( A1, A1_H ); ++reg_class a0_long_reg( A0, A0_H ); ++reg_class a1_long_reg( A1, A1_H ); ++reg_class a2_long_reg( A2, A2_H ); ++reg_class a3_long_reg( A3, A3_H ); ++reg_class a4_long_reg( A4, A4_H ); ++reg_class a5_long_reg( A5, A5_H ); ++reg_class a6_long_reg( A6, A6_H ); ++reg_class a7_long_reg( A7, A7_H ); ++reg_class t0_long_reg( T0, T0_H ); ++reg_class t1_long_reg( T1, T1_H ); ++reg_class t2_long_reg( T2, T2_H ); ++reg_class t3_long_reg( T3, T3_H ); ++reg_class t8_long_reg( T8, T8_H ); ++reg_class t4_long_reg( T4, T4_H ); ++reg_class s0_long_reg( S0, S0_H ); ++reg_class s1_long_reg( S1, S1_H ); ++reg_class s2_long_reg( S2, S2_H ); ++reg_class s3_long_reg( S3, S3_H ); ++reg_class s4_long_reg( S4, S4_H ); ++reg_class s5_long_reg( S5, S5_H ); ++reg_class s6_long_reg( S6, S6_H ); ++reg_class s7_long_reg( S7, S7_H ); ++ ++reg_class int_reg( S7, S0, S1, S2, S4, S3, T8, T2, T3, T1, T6, A7, A6, A5, A4, T5, A3, A2, A1, A0, T0 ); ++ ++reg_class no_Ax_int_reg( S7, S0, S1, S2, S4, S3, T8, T2, T3, T1, T6, T5, T0 ); ++ ++reg_class p_reg( ++ S7, S7_H, ++ S0, S0_H, ++ S1, S1_H, ++ S2, S2_H, ++ S4, S4_H, ++ S3, S3_H, ++ T8, T8_H, ++ T2, T2_H, ++ T3, T3_H, ++ T1, T1_H, ++ A7, A7_H, ++ A6, A6_H, ++ A5, A5_H, ++ A4, A4_H, ++ A3, A3_H, ++ A2, A2_H, ++ A1, A1_H, ++ A0, A0_H, ++ T0, T0_H ++ ); ++ ++reg_class no_T8_p_reg( ++ S7, S7_H, ++ S0, S0_H, ++ S1, S1_H, ++ S2, S2_H, ++ S4, S4_H, ++ S3, S3_H, ++ T2, T2_H, ++ T3, T3_H, ++ T1, T1_H, ++ A7, A7_H, ++ A6, A6_H, ++ A5, A5_H, ++ A4, A4_H, ++ A3, A3_H, ++ A2, A2_H, ++ A1, A1_H, ++ A0, A0_H, ++ T0, T0_H ++ ); ++ ++reg_class no_Ax_p_reg( ++ S7, S7_H, ++ S0, S0_H, ++ S1, S1_H, ++ S2, S2_H, ++ S4, S4_H, ++ S3, S3_H, ++ T2, T2_H, ++ T3, T3_H, ++ T1, T1_H, ++ T0, T0_H ++ ); ++ ++reg_class long_reg( ++ S7, S7_H, ++ S0, S0_H, ++ S1, S1_H, ++ S2, S2_H, ++ S4, S4_H, ++ S3, S3_H, ++ T8, T8_H, ++ T2, T2_H, ++ T3, T3_H, ++ T1, T1_H, ++ A7, A7_H, ++ A6, A6_H, ++ A5, A5_H, ++ A4, A4_H, ++ A3, A3_H, ++ A2, A2_H, ++ A1, A1_H, ++ A0, A0_H, ++ T0, T0_H ++ ); ++ ++ ++// Floating point registers. ++// F31 are not used as temporary registers in D2I ++reg_class flt_reg( F0, F1, F2, F3, F4, F5, F6, F7, F8, F9, F10, F11, F12, F13, F14, F15, F16, F17, F18, F19, F20, F21, F22, F24, F25, F26, F27, F28, F29, F30, F31); ++reg_class dbl_reg( F0, F0_H, ++ F1, F1_H, ++ F2, F2_H, ++ F3, F3_H, ++ F4, F4_H, ++ F5, F5_H, ++ F6, F6_H, ++ F7, F7_H, ++ F8, F8_H, ++ F9, F9_H, ++ F10, F10_H, ++ F11, F11_H, ++ F12, F12_H, ++ F13, F13_H, ++ F14, F14_H, ++ F15, F15_H, ++ F16, F16_H, ++ F17, F17_H, ++ F18, F18_H, ++ F19, F19_H, ++ F20, F20_H, ++ F21, F21_H, ++ F22, F22_H, ++ F24, F24_H, ++ F25, F25_H, ++ F26, F26_H, ++ F27, F27_H, ++ F28, F28_H, ++ F29, F29_H, ++ F30, F30_H, ++ F31, F31_H); ++ ++// Class for all 128bit vector registers ++reg_class vectorx_reg( F0, F0_H, F0_J, F0_K, ++ F1, F1_H, F1_J, F1_K, ++ F2, F2_H, F2_J, F2_K, ++ F3, F3_H, F3_J, F3_K, ++ F4, F4_H, F4_J, F4_K, ++ F5, F5_H, F5_J, F5_K, ++ F6, F6_H, F6_J, F6_K, ++ F7, F7_H, F7_J, F7_K, ++ F8, F8_H, F8_J, F8_K, ++ F9, F9_H, F9_J, F9_K, ++ F10, F10_H, F10_J, F10_K, ++ F11, F11_H, F11_J, F11_K, ++ F12, F12_H, F12_J, F12_K, ++ F13, F13_H, F13_J, F13_K, ++ F14, F14_H, F14_J, F14_K, ++ F15, F15_H, F15_J, F15_K, ++ F16, F16_H, F16_J, F16_K, ++ F17, F17_H, F17_J, F17_K, ++ F18, F18_H, F18_J, F18_K, ++ F19, F19_H, F19_J, F19_K, ++ F20, F20_H, F20_J, F20_K, ++ F21, F21_H, F21_J, F21_K, ++ F22, F22_H, F22_J, F22_K, ++ F24, F24_H, F24_J, F24_K, ++ F25, F25_H, F25_J, F25_K, ++ F26, F26_H, F26_J, F26_K, ++ F27, F27_H, F27_J, F27_K, ++ F28, F28_H, F28_J, F28_K, ++ F29, F29_H, F29_J, F29_K, ++ F30, F30_H, F30_J, F30_K, ++ F31, F31_H, F31_J, F31_K); ++ ++// Class for all 256bit vector registers ++reg_class vectory_reg( F0, F0_H, F0_J, F0_K, F0_L, F0_M, F0_N, F0_O, ++ F1, F1_H, F1_J, F1_K, F1_L, F1_M, F1_N, F1_O, ++ F2, F2_H, F2_J, F2_K, F2_L, F2_M, F2_N, F2_O, ++ F3, F3_H, F3_J, F3_K, F3_L, F3_M, F3_N, F3_O, ++ F4, F4_H, F4_J, F4_K, F4_L, F4_M, F4_N, F4_O, ++ F5, F5_H, F5_J, F5_K, F5_L, F5_M, F5_N, F5_O, ++ F6, F6_H, F6_J, F6_K, F6_L, F6_M, F6_N, F6_O, ++ F7, F7_H, F7_J, F7_K, F7_L, F7_M, F7_N, F7_O, ++ F8, F8_H, F8_J, F8_K, F8_L, F8_M, F8_N, F8_O, ++ F9, F9_H, F9_J, F9_K, F9_L, F9_M, F9_N, F9_O, ++ F10, F10_H, F10_J, F10_K, F10_L, F10_M, F10_N, F10_O, ++ F11, F11_H, F11_J, F11_K, F11_L, F11_M, F11_N, F11_O, ++ F12, F12_H, F12_J, F12_K, F12_L, F12_M, F12_N, F12_O, ++ F13, F13_H, F13_J, F13_K, F13_L, F13_M, F13_N, F13_O, ++ F14, F14_H, F14_J, F14_K, F14_L, F14_M, F14_N, F14_O, ++ F15, F15_H, F15_J, F15_K, F15_L, F15_M, F15_N, F15_O, ++ F16, F16_H, F16_J, F16_K, F16_L, F16_M, F16_N, F16_O, ++ F17, F17_H, F17_J, F17_K, F17_L, F17_M, F17_N, F17_O, ++ F18, F18_H, F18_J, F18_K, F18_L, F18_M, F18_N, F18_O, ++ F19, F19_H, F19_J, F19_K, F19_L, F19_M, F19_N, F19_O, ++ F20, F20_H, F20_J, F20_K, F20_L, F20_M, F20_N, F20_O, ++ F21, F21_H, F21_J, F21_K, F21_L, F21_M, F21_N, F21_O, ++ F22, F22_H, F22_J, F22_K, F22_L, F22_M, F22_N, F22_O, ++ F24, F24_H, F24_J, F24_K, F24_L, F24_M, F24_N, F24_O, ++ F25, F25_H, F25_J, F25_K, F25_L, F25_M, F25_N, F25_O, ++ F26, F26_H, F26_J, F26_K, F26_L, F26_M, F26_N, F26_O, ++ F27, F27_H, F27_J, F27_K, F27_L, F27_M, F27_N, F27_O, ++ F28, F28_H, F28_J, F28_K, F28_L, F28_M, F28_N, F28_O, ++ F29, F29_H, F29_J, F29_K, F29_L, F29_M, F29_N, F29_O, ++ F30, F30_H, F30_J, F30_K, F30_L, F30_M, F30_N, F30_O, ++ F31, F31_H, F31_J, F31_K, F31_L, F31_M, F31_N, F31_O); ++ ++// TODO: LA ++//reg_class flt_arg0( F0 ); ++//reg_class dbl_arg0( F0, F0_H ); ++//reg_class dbl_arg1( F1, F1_H ); ++ ++%} ++ ++//----------DEFINITION BLOCK--------------------------------------------------- ++// Define name --> value mappings to inform the ADLC of an integer valued name ++// Current support includes integer values in the range [0, 0x7FFFFFFF] ++// Format: ++// int_def ( , ); ++// Generated Code in ad_.hpp ++// #define () ++// // value == ++// Generated code in ad_.cpp adlc_verification() ++// assert( == , "Expect () to equal "); ++// ++definitions %{ ++ int_def DEFAULT_COST ( 100, 100); ++ int_def HUGE_COST (1000000, 1000000); ++ ++ // Memory refs are twice as expensive as run-of-the-mill. ++ int_def MEMORY_REF_COST ( 200, DEFAULT_COST * 2); ++ ++ // Branches are even more expensive. ++ int_def BRANCH_COST ( 300, DEFAULT_COST * 3); ++ // we use jr instruction to construct call, so more expensive ++ int_def CALL_COST ( 500, DEFAULT_COST * 5); ++/* ++ int_def EQUAL ( 1, 1 ); ++ int_def NOT_EQUAL ( 2, 2 ); ++ int_def GREATER ( 3, 3 ); ++ int_def GREATER_EQUAL ( 4, 4 ); ++ int_def LESS ( 5, 5 ); ++ int_def LESS_EQUAL ( 6, 6 ); ++*/ ++%} ++ ++ ++ ++//----------SOURCE BLOCK------------------------------------------------------- ++// This is a block of C++ code which provides values, functions, and ++// definitions necessary in the rest of the architecture description ++ ++source_hpp %{ ++// Header information of the source block. ++// Method declarations/definitions which are used outside ++// the ad-scope can conveniently be defined here. ++// ++// To keep related declarations/definitions/uses close together, ++// we switch between source %{ }% and source_hpp %{ }% freely as needed. ++ ++class CallStubImpl { ++ ++ //-------------------------------------------------------------- ++ //---< Used for optimization in Compile::shorten_branches >--- ++ //-------------------------------------------------------------- ++ ++ public: ++ // Size of call trampoline stub. ++ static uint size_call_trampoline() { ++ return 0; // no call trampolines on this platform ++ } ++ ++ // number of relocations needed by a call trampoline stub ++ static uint reloc_call_trampoline() { ++ return 0; // no call trampolines on this platform ++ } ++}; ++ ++class HandlerImpl { ++ ++ public: ++ ++ static int emit_exception_handler(CodeBuffer &cbuf); ++ static int emit_deopt_handler(CodeBuffer& cbuf); ++ ++ static uint size_exception_handler() { ++ // NativeCall instruction size is the same as NativeJump. ++ // exception handler starts out as jump and can be patched to ++ // a call be deoptimization. (4932387) ++ // Note that this value is also credited (in output.cpp) to ++ // the size of the code section. ++ int size = NativeFarCall::instruction_size; ++ return round_to(size, 16); ++ } ++ ++ static uint size_deopt_handler() { ++ int size = NativeFarCall::instruction_size; ++ return round_to(size, 16); ++ } ++}; ++ ++%} // end source_hpp ++ ++source %{ ++ ++#define NO_INDEX 0 ++#define RELOC_IMM64 Assembler::imm_operand ++#define RELOC_DISP32 Assembler::disp32_operand ++ ++#define V0_num A0_num ++#define V0_H_num A0_H_num ++ ++#define __ _masm. ++ ++#define A0 RA0 ++#define A1 RA1 ++#define A2 RA2 ++#define A3 RA3 ++#define A4 RA4 ++#define A5 RA5 ++#define A6 RA6 ++#define A7 RA7 ++#define T0 RT0 ++#define T1 RT1 ++#define T2 RT2 ++#define T3 RT3 ++#define T4 RT4 ++#define T5 RT5 ++#define T6 RT6 ++#define T7 RT7 ++#define T8 RT8 ++ ++// Emit exception handler code. ++// Stuff framesize into a register and call a VM stub routine. ++int HandlerImpl::emit_exception_handler(CodeBuffer& cbuf) { ++ // Note that the code buffer's insts_mark is always relative to insts. ++ // That's why we must use the macroassembler to generate a handler. ++ MacroAssembler _masm(&cbuf); ++ address base = __ start_a_stub(size_exception_handler()); ++ if (base == NULL) { ++ ciEnv::current()->record_failure("CodeCache is full"); ++ return 0; // CodeBuffer::expand failed ++ } ++ ++ int offset = __ offset(); ++ ++ __ block_comment("; emit_exception_handler"); ++ ++ cbuf.set_insts_mark(); ++ __ relocate(relocInfo::runtime_call_type); ++ __ patchable_jump((address)OptoRuntime::exception_blob()->entry_point()); ++ assert(__ offset() - offset <= (int) size_exception_handler(), "overflow"); ++ __ end_a_stub(); ++ return offset; ++} ++ ++// Emit deopt handler code. ++int HandlerImpl::emit_deopt_handler(CodeBuffer& cbuf) { ++ // Note that the code buffer's insts_mark is always relative to insts. ++ // That's why we must use the macroassembler to generate a handler. ++ MacroAssembler _masm(&cbuf); ++ address base = __ start_a_stub(size_deopt_handler()); ++ if (base == NULL) { ++ ciEnv::current()->record_failure("CodeCache is full"); ++ return 0; // CodeBuffer::expand failed ++ } ++ ++ int offset = __ offset(); ++ ++ __ block_comment("; emit_deopt_handler"); ++ ++ cbuf.set_insts_mark(); ++ __ relocate(relocInfo::runtime_call_type); ++ __ patchable_call(SharedRuntime::deopt_blob()->unpack()); ++ assert(__ offset() - offset <= (int) size_deopt_handler(), "overflow"); ++ __ end_a_stub(); ++ return offset; ++} ++ ++ ++const bool Matcher::match_rule_supported(int opcode) { ++ if (!has_match_rule(opcode)) ++ return false; ++ ++ return true; // Per default match rules are supported. ++} ++ ++bool Matcher::is_short_branch_offset(int rule, int br_size, int offset) { ++ const int safety_zone = 3 * BytesPerInstWord; ++ int offs = offset - br_size + 4; ++ // To be conservative on LoongArch ++ // branch node should be end with: ++ // branch inst ++ offs = (offs < 0 ? offs - safety_zone : offs + safety_zone) >> 2; ++ switch (rule) { ++ case jmpDir_long_rule: ++ case jmpDir_short_rule: ++ return Assembler::is_simm(offs, 26); ++ case jmpCon_flags_long_rule: ++ case jmpCon_flags_short_rule: ++ case branchConP_0_long_rule: ++ case branchConP_0_short_rule: ++ case branchConN2P_0_long_rule: ++ case branchConN2P_0_short_rule: ++ case cmpN_null_branch_long_rule: ++ case cmpN_null_branch_short_rule: ++ case branchConIU_reg_immI_0_long_rule: ++ case branchConIU_reg_immI_0_short_rule: ++ case branchConF_reg_reg_long_rule: ++ case branchConF_reg_reg_short_rule: ++ case branchConD_reg_reg_long_rule: ++ case branchConD_reg_reg_short_rule: ++ return Assembler::is_simm(offs, 21); ++ default: ++ return Assembler::is_simm(offs, 16); ++ } ++ return false; ++} ++ ++ ++// No additional cost for CMOVL. ++const int Matcher::long_cmove_cost() { return 0; } ++ ++// No CMOVF/CMOVD with SSE2 ++const int Matcher::float_cmove_cost() { return ConditionalMoveLimit; } ++ ++// Does the CPU require late expand (see block.cpp for description of late expand)? ++const bool Matcher::require_postalloc_expand = false; ++ ++// Should the Matcher clone shifts on addressing modes, expecting them ++// to be subsumed into complex addressing expressions or compute them ++// into registers? True for Intel but false for most RISCs ++const bool Matcher::clone_shift_expressions = false; ++ ++// Do we need to mask the count passed to shift instructions or does ++// the cpu only look at the lower 5/6 bits anyway? ++const bool Matcher::need_masked_shift_count = false; ++ ++bool Matcher::narrow_oop_use_complex_address() { ++ assert(UseCompressedOops, "only for compressed oops code"); ++ return false; ++} ++ ++bool Matcher::narrow_klass_use_complex_address() { ++ assert(UseCompressedClassPointers, "only for compressed klass code"); ++ return false; ++} ++ ++// This is UltraSparc specific, true just means we have fast l2f conversion ++const bool Matcher::convL2FSupported(void) { ++ return true; ++} ++ ++// Vector ideal reg ++const uint Matcher::vector_ideal_reg(int size) { ++ assert(MaxVectorSize == 16 || MaxVectorSize == 32, ""); ++ switch(size) { ++ case 16: return Op_VecX; ++ case 32: return Op_VecY; ++ } ++ ShouldNotReachHere(); ++ return 0; ++} ++ ++// Only lowest bits of xmm reg are used for vector shift count. ++const uint Matcher::vector_shift_count_ideal_reg(int size) { ++ assert(MaxVectorSize == 16 || MaxVectorSize == 32, ""); ++ switch(size) { ++ case 16: return Op_VecX; ++ case 32: return Op_VecY; ++ } ++ ShouldNotReachHere(); ++ return 0; ++} ++ ++// Max vector size in bytes. 0 if not supported. ++const int Matcher::vector_width_in_bytes(BasicType bt) { ++ return (int)MaxVectorSize; ++} ++ ++// Limits on vector size (number of elements) loaded into vector. ++const int Matcher::max_vector_size(const BasicType bt) { ++ assert(is_java_primitive(bt), "only primitive type vectors"); ++ return vector_width_in_bytes(bt)/type2aelembytes(bt); ++} ++ ++const int Matcher::min_vector_size(const BasicType bt) { ++ int max_size = max_vector_size(bt); ++ int size = 0; ++ ++ if (UseLSX) size = 16; ++ size = size / type2aelembytes(bt); ++ return MIN2(size,max_size); ++} ++ ++// LoongArch supports misaligned vectors store/load? ++const bool Matcher::misaligned_vectors_ok() { ++ return false; ++ //return !AlignVector; // can be changed by flag ++} ++ ++// Register for DIVI projection of divmodI ++RegMask Matcher::divI_proj_mask() { ++ ShouldNotReachHere(); ++ return RegMask(); ++} ++ ++// Register for MODI projection of divmodI ++RegMask Matcher::modI_proj_mask() { ++ ShouldNotReachHere(); ++ return RegMask(); ++} ++ ++// Register for DIVL projection of divmodL ++RegMask Matcher::divL_proj_mask() { ++ ShouldNotReachHere(); ++ return RegMask(); ++} ++ ++int Matcher::regnum_to_fpu_offset(int regnum) { ++ return regnum - 32; // The FP registers are in the second chunk ++} ++ ++ ++const bool Matcher::isSimpleConstant64(jlong value) { ++ // Will one (StoreL ConL) be cheaper than two (StoreI ConI)?. ++ return true; ++} ++ ++ ++// Return whether or not this register is ever used as an argument. This ++// function is used on startup to build the trampoline stubs in generateOptoStub. ++// Registers not mentioned will be killed by the VM call in the trampoline, and ++// arguments in those registers not be available to the callee. ++bool Matcher::can_be_java_arg( int reg ) { ++ // Refer to: [sharedRuntime_loongarch_64.cpp] SharedRuntime::java_calling_convention() ++ if ( reg == T0_num || reg == T0_H_num ++ || reg == A0_num || reg == A0_H_num ++ || reg == A1_num || reg == A1_H_num ++ || reg == A2_num || reg == A2_H_num ++ || reg == A3_num || reg == A3_H_num ++ || reg == A4_num || reg == A4_H_num ++ || reg == A5_num || reg == A5_H_num ++ || reg == A6_num || reg == A6_H_num ++ || reg == A7_num || reg == A7_H_num ) ++ return true; ++ ++ if ( reg == F0_num || reg == F0_H_num ++ || reg == F1_num || reg == F1_H_num ++ || reg == F2_num || reg == F2_H_num ++ || reg == F3_num || reg == F3_H_num ++ || reg == F4_num || reg == F4_H_num ++ || reg == F5_num || reg == F5_H_num ++ || reg == F6_num || reg == F6_H_num ++ || reg == F7_num || reg == F7_H_num ) ++ return true; ++ ++ return false; ++} ++ ++bool Matcher::is_spillable_arg( int reg ) { ++ return can_be_java_arg(reg); ++} ++ ++bool Matcher::use_asm_for_ldiv_by_con( jlong divisor ) { ++ return false; ++} ++ ++// Register for MODL projection of divmodL ++RegMask Matcher::modL_proj_mask() { ++ ShouldNotReachHere(); ++ return RegMask(); ++} ++ ++const RegMask Matcher::method_handle_invoke_SP_save_mask() { ++ return FP_REG_mask(); ++} ++ ++// LoongArch doesn't support AES intrinsics ++const bool Matcher::pass_original_key_for_aes() { ++ return false; ++} ++ ++int CallStaticJavaDirectNode::compute_padding(int current_offset) const { ++ return round_to(current_offset, alignment_required()) - current_offset; ++} ++ ++int CallDynamicJavaDirectNode::compute_padding(int current_offset) const { ++ return round_to(current_offset, alignment_required()) - current_offset; ++} ++ ++int CallLeafNoFPDirectNode::compute_padding(int current_offset) const { ++ return round_to(current_offset, alignment_required()) - current_offset; ++} ++ ++int CallLeafDirectNode::compute_padding(int current_offset) const { ++ return round_to(current_offset, alignment_required()) - current_offset; ++} ++ ++int CallRuntimeDirectNode::compute_padding(int current_offset) const { ++ return round_to(current_offset, alignment_required()) - current_offset; ++} ++ ++// If CPU can load and store mis-aligned doubles directly then no fixup is ++// needed. Else we split the double into 2 integer pieces and move it ++// piece-by-piece. Only happens when passing doubles into C code as the ++// Java calling convention forces doubles to be aligned. ++const bool Matcher::misaligned_doubles_ok = false; ++// Do floats take an entire double register or just half? ++//const bool Matcher::float_in_double = true; ++bool Matcher::float_in_double() { return false; } ++// Threshold size for cleararray. ++const int Matcher::init_array_short_size = 8 * BytesPerLong; ++// Do ints take an entire long register or just half? ++const bool Matcher::int_in_long = true; ++// Is it better to copy float constants, or load them directly from memory? ++// Intel can load a float constant from a direct address, requiring no ++// extra registers. Most RISCs will have to materialize an address into a ++// register first, so they would do better to copy the constant from stack. ++const bool Matcher::rematerialize_float_constants = false; ++// Advertise here if the CPU requires explicit rounding operations ++// to implement the UseStrictFP mode. ++const bool Matcher::strict_fp_requires_explicit_rounding = false; ++// false => size gets scaled to BytesPerLong, ok. ++const bool Matcher::init_array_count_is_in_bytes = false; ++ ++// Indicate if the safepoint node needs the polling page as an input. ++// Since LA doesn't have absolute addressing, it needs. ++bool SafePointNode::needs_polling_address_input() { ++ return false; ++} ++ ++// !!!!! Special hack to get all type of calls to specify the byte offset ++// from the start of the call to the point where the return address ++// will point. ++int MachCallStaticJavaNode::ret_addr_offset() { ++ // bl ++ return NativeCall::instruction_size; ++} ++ ++int MachCallDynamicJavaNode::ret_addr_offset() { ++ // lu12i_w IC_Klass, ++ // ori IC_Klass, ++ // lu32i_d IC_Klass ++ // lu52i_d IC_Klass ++ ++ // bl ++ return NativeMovConstReg::instruction_size + NativeCall::instruction_size; ++} ++ ++//============================================================================= ++ ++// Figure out which register class each belongs in: rc_int, rc_float, rc_stack ++enum RC { rc_bad, rc_int, rc_float, rc_stack }; ++static enum RC rc_class( OptoReg::Name reg ) { ++ if( !OptoReg::is_valid(reg) ) return rc_bad; ++ if (OptoReg::is_stack(reg)) return rc_stack; ++ VMReg r = OptoReg::as_VMReg(reg); ++ if (r->is_Register()) return rc_int; ++ assert(r->is_FloatRegister(), "must be"); ++ return rc_float; ++} ++ ++// Helper methods for MachSpillCopyNode::implementation(). ++static int vec_mov_helper(CodeBuffer *cbuf, bool do_size, int src_lo, int dst_lo, ++ int src_hi, int dst_hi, uint ireg, outputStream* st) { ++ int size = 0; ++ if (cbuf) { ++ MacroAssembler _masm(cbuf); ++ int offset = __ offset(); ++ switch (ireg) { ++ case Op_VecX: ++ __ vori_b(as_FloatRegister(Matcher::_regEncode[dst_lo]), as_FloatRegister(Matcher::_regEncode[src_lo]), 0); ++ break; ++ case Op_VecY: ++ __ xvori_b(as_FloatRegister(Matcher::_regEncode[dst_lo]), as_FloatRegister(Matcher::_regEncode[src_lo]), 0); ++ break; ++ default: ++ ShouldNotReachHere(); ++ } ++#ifndef PRODUCT ++ } else if (!do_size) { ++ switch (ireg) { ++ case Op_VecX: ++ st->print("vori.b %s, %s, 0\t# spill", Matcher::regName[dst_lo], Matcher::regName[src_lo]); ++ break; ++ case Op_VecY: ++ st->print("xvori.b %s, %s, 0\t# spill", Matcher::regName[dst_lo], Matcher::regName[src_lo]); ++ break; ++ default: ++ ShouldNotReachHere(); ++ } ++#endif ++ } ++ size += 4; ++ return size; ++} ++ ++static int vec_spill_helper(CodeBuffer *cbuf, bool do_size, bool is_load, ++ int stack_offset, int reg, uint ireg, outputStream* st) { ++ int size = 0; ++ if (cbuf) { ++ MacroAssembler _masm(cbuf); ++ int offset = __ offset(); ++ if (is_load) { ++ switch (ireg) { ++ case Op_VecX: ++ __ vld(as_FloatRegister(Matcher::_regEncode[reg]), SP, stack_offset); ++ break; ++ case Op_VecY: ++ __ xvld(as_FloatRegister(Matcher::_regEncode[reg]), SP, stack_offset); ++ break; ++ default: ++ ShouldNotReachHere(); ++ } ++ } else { // store ++ switch (ireg) { ++ case Op_VecX: ++ __ vst(as_FloatRegister(Matcher::_regEncode[reg]), SP, stack_offset); ++ break; ++ case Op_VecY: ++ __ xvst(as_FloatRegister(Matcher::_regEncode[reg]), SP, stack_offset); ++ break; ++ default: ++ ShouldNotReachHere(); ++ } ++ } ++#ifndef PRODUCT ++ } else if (!do_size) { ++ if (is_load) { ++ switch (ireg) { ++ case Op_VecX: ++ st->print("vld %s, [SP + %d]\t# spill", Matcher::regName[reg], stack_offset); ++ break; ++ case Op_VecY: ++ st->print("xvld %s, [SP + %d]\t# spill", Matcher::regName[reg], stack_offset); ++ break; ++ default: ++ ShouldNotReachHere(); ++ } ++ } else { // store ++ switch (ireg) { ++ case Op_VecX: ++ st->print("vst %s, [SP + %d]\t# spill", Matcher::regName[reg], stack_offset); ++ break; ++ case Op_VecY: ++ st->print("xvst %s, [SP + %d]\t# spill", Matcher::regName[reg], stack_offset); ++ break; ++ default: ++ ShouldNotReachHere(); ++ } ++ } ++#endif ++ } ++ size += 4; ++ return size; ++} ++ ++static int vec_stack_to_stack_helper(CodeBuffer *cbuf, int src_offset, ++ int dst_offset, uint ireg, outputStream* st) { ++ int size = 0; ++ if (cbuf) { ++ MacroAssembler _masm(cbuf); ++ switch (ireg) { ++ case Op_VecX: ++ __ vld(F23, SP, src_offset); ++ __ vst(F23, SP, dst_offset); ++ break; ++ case Op_VecY: ++ __ xvld(F23, SP, src_offset); ++ __ xvst(F23, SP, dst_offset); ++ break; ++ default: ++ ShouldNotReachHere(); ++ } ++#ifndef PRODUCT ++ } else { ++ switch (ireg) { ++ case Op_VecX: ++ st->print("vld f23, %d(sp)\n\t" ++ "vst f23, %d(sp)\t# 128-bit mem-mem spill", ++ src_offset, dst_offset); ++ break; ++ case Op_VecY: ++ st->print("xvld f23, %d(sp)\n\t" ++ "xvst f23, %d(sp)\t# 256-bit mem-mem spill", ++ src_offset, dst_offset); ++ break; ++ default: ++ ShouldNotReachHere(); ++ } ++#endif ++ } ++ size += 8; ++ return size; ++} ++ ++uint MachSpillCopyNode::implementation( CodeBuffer *cbuf, PhaseRegAlloc *ra_, bool do_size, outputStream* st ) const { ++ // Get registers to move ++ OptoReg::Name src_second = ra_->get_reg_second(in(1)); ++ OptoReg::Name src_first = ra_->get_reg_first(in(1)); ++ OptoReg::Name dst_second = ra_->get_reg_second(this ); ++ OptoReg::Name dst_first = ra_->get_reg_first(this ); ++ ++ enum RC src_second_rc = rc_class(src_second); ++ enum RC src_first_rc = rc_class(src_first); ++ enum RC dst_second_rc = rc_class(dst_second); ++ enum RC dst_first_rc = rc_class(dst_first); ++ ++ assert(OptoReg::is_valid(src_first) && OptoReg::is_valid(dst_first), "must move at least 1 register" ); ++ ++ // Generate spill code! ++ ++ if( src_first == dst_first && src_second == dst_second ) ++ return 0; // Self copy, no move ++ ++ if (bottom_type()->isa_vect() != NULL) { ++ uint ireg = ideal_reg(); ++ assert((src_first_rc != rc_int && dst_first_rc != rc_int), "sanity"); ++ if (src_first_rc == rc_stack && dst_first_rc == rc_stack) { ++ // mem -> mem ++ int src_offset = ra_->reg2offset(src_first); ++ int dst_offset = ra_->reg2offset(dst_first); ++ vec_stack_to_stack_helper(cbuf, src_offset, dst_offset, ireg, st); ++ } else if (src_first_rc == rc_float && dst_first_rc == rc_float) { ++ vec_mov_helper(cbuf, do_size, src_first, dst_first, src_second, dst_second, ireg, st); ++ } else if (src_first_rc == rc_float && dst_first_rc == rc_stack) { ++ int stack_offset = ra_->reg2offset(dst_first); ++ vec_spill_helper(cbuf, do_size, false, stack_offset, src_first, ireg, st); ++ } else if (src_first_rc == rc_stack && dst_first_rc == rc_float) { ++ int stack_offset = ra_->reg2offset(src_first); ++ vec_spill_helper(cbuf, do_size, true, stack_offset, dst_first, ireg, st); ++ } else { ++ ShouldNotReachHere(); ++ } ++ return 0; ++ } ++ ++ if (src_first_rc == rc_stack) { ++ // mem -> ++ if (dst_first_rc == rc_stack) { ++ // mem -> mem ++ assert(src_second != dst_first, "overlap"); ++ if ((src_first & 1) == 0 && src_first + 1 == src_second && ++ (dst_first & 1) == 0 && dst_first + 1 == dst_second) { ++ // 64-bit ++ int src_offset = ra_->reg2offset(src_first); ++ int dst_offset = ra_->reg2offset(dst_first); ++ if (cbuf) { ++ MacroAssembler _masm(cbuf); ++ __ ld_d(AT, Address(SP, src_offset)); ++ __ st_d(AT, Address(SP, dst_offset)); ++#ifndef PRODUCT ++ } else { ++ st->print("\n\t"); ++ st->print("ld_d AT, [SP + #%d]\t# 64-bit mem-mem spill 1\n\t" ++ "st_d AT, [SP + #%d]", ++ src_offset, dst_offset); ++#endif ++ } ++ } else { ++ // 32-bit ++ assert(!((src_first & 1) == 0 && src_first + 1 == src_second), "no transform"); ++ assert(!((dst_first & 1) == 0 && dst_first + 1 == dst_second), "no transform"); ++ // No pushl/popl, so: ++ int src_offset = ra_->reg2offset(src_first); ++ int dst_offset = ra_->reg2offset(dst_first); ++ if (cbuf) { ++ MacroAssembler _masm(cbuf); ++ __ ld_w(AT, Address(SP, src_offset)); ++ __ st_w(AT, Address(SP, dst_offset)); ++#ifndef PRODUCT ++ } else { ++ st->print("\n\t"); ++ st->print("ld_w AT, [SP + #%d] spill 2\n\t" ++ "st_w AT, [SP + #%d]\n\t", ++ src_offset, dst_offset); ++#endif ++ } ++ } ++ return 0; ++ } else if (dst_first_rc == rc_int) { ++ // mem -> gpr ++ if ((src_first & 1) == 0 && src_first + 1 == src_second && ++ (dst_first & 1) == 0 && dst_first + 1 == dst_second) { ++ // 64-bit ++ int offset = ra_->reg2offset(src_first); ++ if (cbuf) { ++ MacroAssembler _masm(cbuf); ++ __ ld_d(as_Register(Matcher::_regEncode[dst_first]), Address(SP, offset)); ++#ifndef PRODUCT ++ } else { ++ st->print("\n\t"); ++ st->print("ld_d %s, [SP + #%d]\t# spill 3", ++ Matcher::regName[dst_first], ++ offset); ++#endif ++ } ++ } else { ++ // 32-bit ++ assert(!((src_first & 1) == 0 && src_first + 1 == src_second), "no transform"); ++ assert(!((dst_first & 1) == 0 && dst_first + 1 == dst_second), "no transform"); ++ int offset = ra_->reg2offset(src_first); ++ if (cbuf) { ++ MacroAssembler _masm(cbuf); ++ if (this->ideal_reg() == Op_RegI) ++ __ ld_w(as_Register(Matcher::_regEncode[dst_first]), Address(SP, offset)); ++ else { ++ if (Assembler::is_simm(offset, 12)) { ++ __ ld_wu(as_Register(Matcher::_regEncode[dst_first]), Address(SP, offset)); ++ } else { ++ __ li(AT, offset); ++ __ ldx_wu(as_Register(Matcher::_regEncode[dst_first]), SP, AT); ++ } ++ } ++#ifndef PRODUCT ++ } else { ++ st->print("\n\t"); ++ if (this->ideal_reg() == Op_RegI) ++ st->print("ld_w %s, [SP + #%d]\t# spill 4", ++ Matcher::regName[dst_first], ++ offset); ++ else ++ st->print("ld_wu %s, [SP + #%d]\t# spill 5", ++ Matcher::regName[dst_first], ++ offset); ++#endif ++ } ++ } ++ return 0; ++ } else if (dst_first_rc == rc_float) { ++ // mem-> xmm ++ if ((src_first & 1) == 0 && src_first + 1 == src_second && ++ (dst_first & 1) == 0 && dst_first + 1 == dst_second) { ++ // 64-bit ++ int offset = ra_->reg2offset(src_first); ++ if (cbuf) { ++ MacroAssembler _masm(cbuf); ++ __ fld_d( as_FloatRegister(Matcher::_regEncode[dst_first]), Address(SP, offset)); ++#ifndef PRODUCT ++ } else { ++ st->print("\n\t"); ++ st->print("fld_d %s, [SP + #%d]\t# spill 6", ++ Matcher::regName[dst_first], ++ offset); ++#endif ++ } ++ } else { ++ // 32-bit ++ assert(!((src_first & 1) == 0 && src_first + 1 == src_second), "no transform"); ++ assert(!((dst_first & 1) == 0 && dst_first + 1 == dst_second), "no transform"); ++ int offset = ra_->reg2offset(src_first); ++ if (cbuf) { ++ MacroAssembler _masm(cbuf); ++ __ fld_s( as_FloatRegister(Matcher::_regEncode[dst_first]), Address(SP, offset)); ++#ifndef PRODUCT ++ } else { ++ st->print("\n\t"); ++ st->print("fld_s %s, [SP + #%d]\t# spill 7", ++ Matcher::regName[dst_first], ++ offset); ++#endif ++ } ++ } ++ } ++ return 0; ++ } else if (src_first_rc == rc_int) { ++ // gpr -> ++ if (dst_first_rc == rc_stack) { ++ // gpr -> mem ++ if ((src_first & 1) == 0 && src_first + 1 == src_second && ++ (dst_first & 1) == 0 && dst_first + 1 == dst_second) { ++ // 64-bit ++ int offset = ra_->reg2offset(dst_first); ++ if (cbuf) { ++ MacroAssembler _masm(cbuf); ++ __ st_d(as_Register(Matcher::_regEncode[src_first]), Address(SP, offset)); ++#ifndef PRODUCT ++ } else { ++ st->print("\n\t"); ++ st->print("st_d %s, [SP + #%d] # spill 8", ++ Matcher::regName[src_first], ++ offset); ++#endif ++ } ++ } else { ++ // 32-bit ++ assert(!((src_first & 1) == 0 && src_first + 1 == src_second), "no transform"); ++ assert(!((dst_first & 1) == 0 && dst_first + 1 == dst_second), "no transform"); ++ int offset = ra_->reg2offset(dst_first); ++ if (cbuf) { ++ MacroAssembler _masm(cbuf); ++ __ st_w(as_Register(Matcher::_regEncode[src_first]), Address(SP, offset)); ++#ifndef PRODUCT ++ } else { ++ st->print("\n\t"); ++ st->print("st_w %s, [SP + #%d]\t# spill 9", ++ Matcher::regName[src_first], offset); ++#endif ++ } ++ } ++ return 0; ++ } else if (dst_first_rc == rc_int) { ++ // gpr -> gpr ++ if ((src_first & 1) == 0 && src_first + 1 == src_second && ++ (dst_first & 1) == 0 && dst_first + 1 == dst_second) { ++ // 64-bit ++ if (cbuf) { ++ MacroAssembler _masm(cbuf); ++ __ move(as_Register(Matcher::_regEncode[dst_first]), ++ as_Register(Matcher::_regEncode[src_first])); ++#ifndef PRODUCT ++ } else { ++ st->print("\n\t"); ++ st->print("move(64bit) %s <-- %s\t# spill 10", ++ Matcher::regName[dst_first], ++ Matcher::regName[src_first]); ++#endif ++ } ++ return 0; ++ } else { ++ // 32-bit ++ assert(!((src_first & 1) == 0 && src_first + 1 == src_second), "no transform"); ++ assert(!((dst_first & 1) == 0 && dst_first + 1 == dst_second), "no transform"); ++ if (cbuf) { ++ MacroAssembler _masm(cbuf); ++ if (this->ideal_reg() == Op_RegI) ++ __ move_u32(as_Register(Matcher::_regEncode[dst_first]), as_Register(Matcher::_regEncode[src_first])); ++ else ++ __ add_d(as_Register(Matcher::_regEncode[dst_first]), as_Register(Matcher::_regEncode[src_first]), R0); ++#ifndef PRODUCT ++ } else { ++ st->print("\n\t"); ++ st->print("move(32-bit) %s <-- %s\t# spill 11", ++ Matcher::regName[dst_first], ++ Matcher::regName[src_first]); ++#endif ++ } ++ return 0; ++ } ++ } else if (dst_first_rc == rc_float) { ++ // gpr -> xmm ++ if ((src_first & 1) == 0 && src_first + 1 == src_second && ++ (dst_first & 1) == 0 && dst_first + 1 == dst_second) { ++ // 64-bit ++ if (cbuf) { ++ MacroAssembler _masm(cbuf); ++ __ movgr2fr_d(as_FloatRegister(Matcher::_regEncode[dst_first]), as_Register(Matcher::_regEncode[src_first])); ++#ifndef PRODUCT ++ } else { ++ st->print("\n\t"); ++ st->print("movgr2fr_d %s, %s\t# spill 12", ++ Matcher::regName[dst_first], ++ Matcher::regName[src_first]); ++#endif ++ } ++ } else { ++ // 32-bit ++ assert(!((src_first & 1) == 0 && src_first + 1 == src_second), "no transform"); ++ assert(!((dst_first & 1) == 0 && dst_first + 1 == dst_second), "no transform"); ++ if (cbuf) { ++ MacroAssembler _masm(cbuf); ++ __ movgr2fr_w(as_FloatRegister(Matcher::_regEncode[dst_first]), as_Register(Matcher::_regEncode[src_first])); ++#ifndef PRODUCT ++ } else { ++ st->print("\n\t"); ++ st->print("movgr2fr_w %s, %s\t# spill 13", ++ Matcher::regName[dst_first], ++ Matcher::regName[src_first]); ++#endif ++ } ++ } ++ return 0; ++ } ++ } else if (src_first_rc == rc_float) { ++ // xmm -> ++ if (dst_first_rc == rc_stack) { ++ // xmm -> mem ++ if ((src_first & 1) == 0 && src_first + 1 == src_second && ++ (dst_first & 1) == 0 && dst_first + 1 == dst_second) { ++ // 64-bit ++ int offset = ra_->reg2offset(dst_first); ++ if (cbuf) { ++ MacroAssembler _masm(cbuf); ++ __ fst_d( as_FloatRegister(Matcher::_regEncode[src_first]), Address(SP, offset) ); ++#ifndef PRODUCT ++ } else { ++ st->print("\n\t"); ++ st->print("fst_d %s, [SP + #%d]\t# spill 14", ++ Matcher::regName[src_first], ++ offset); ++#endif ++ } ++ } else { ++ // 32-bit ++ assert(!((src_first & 1) == 0 && src_first + 1 == src_second), "no transform"); ++ assert(!((dst_first & 1) == 0 && dst_first + 1 == dst_second), "no transform"); ++ int offset = ra_->reg2offset(dst_first); ++ if (cbuf) { ++ MacroAssembler _masm(cbuf); ++ __ fst_s(as_FloatRegister(Matcher::_regEncode[src_first]), Address(SP, offset)); ++#ifndef PRODUCT ++ } else { ++ st->print("\n\t"); ++ st->print("fst_s %s, [SP + #%d]\t# spill 15", ++ Matcher::regName[src_first], ++ offset); ++#endif ++ } ++ } ++ return 0; ++ } else if (dst_first_rc == rc_int) { ++ // xmm -> gpr ++ if ((src_first & 1) == 0 && src_first + 1 == src_second && ++ (dst_first & 1) == 0 && dst_first + 1 == dst_second) { ++ // 64-bit ++ if (cbuf) { ++ MacroAssembler _masm(cbuf); ++ __ movfr2gr_d( as_Register(Matcher::_regEncode[dst_first]), as_FloatRegister(Matcher::_regEncode[src_first])); ++#ifndef PRODUCT ++ } else { ++ st->print("\n\t"); ++ st->print("movfr2gr_d %s, %s\t# spill 16", ++ Matcher::regName[dst_first], ++ Matcher::regName[src_first]); ++#endif ++ } ++ } else { ++ // 32-bit ++ assert(!((src_first & 1) == 0 && src_first + 1 == src_second), "no transform"); ++ assert(!((dst_first & 1) == 0 && dst_first + 1 == dst_second), "no transform"); ++ if (cbuf) { ++ MacroAssembler _masm(cbuf); ++ __ movfr2gr_s( as_Register(Matcher::_regEncode[dst_first]), as_FloatRegister(Matcher::_regEncode[src_first])); ++#ifndef PRODUCT ++ } else { ++ st->print("\n\t"); ++ st->print("movfr2gr_s %s, %s\t# spill 17", ++ Matcher::regName[dst_first], ++ Matcher::regName[src_first]); ++#endif ++ } ++ } ++ return 0; ++ } else if (dst_first_rc == rc_float) { ++ // xmm -> xmm ++ if ((src_first & 1) == 0 && src_first + 1 == src_second && ++ (dst_first & 1) == 0 && dst_first + 1 == dst_second) { ++ // 64-bit ++ if (cbuf) { ++ MacroAssembler _masm(cbuf); ++ __ fmov_d( as_FloatRegister(Matcher::_regEncode[dst_first]), as_FloatRegister(Matcher::_regEncode[src_first])); ++#ifndef PRODUCT ++ } else { ++ st->print("\n\t"); ++ st->print("fmov_d %s <-- %s\t# spill 18", ++ Matcher::regName[dst_first], ++ Matcher::regName[src_first]); ++#endif ++ } ++ } else { ++ // 32-bit ++ assert(!((src_first & 1) == 0 && src_first + 1 == src_second), "no transform"); ++ assert(!((dst_first & 1) == 0 && dst_first + 1 == dst_second), "no transform"); ++ if (cbuf) { ++ MacroAssembler _masm(cbuf); ++ __ fmov_s( as_FloatRegister(Matcher::_regEncode[dst_first]), as_FloatRegister(Matcher::_regEncode[src_first])); ++#ifndef PRODUCT ++ } else { ++ st->print("\n\t"); ++ st->print("fmov_s %s <-- %s\t# spill 19", ++ Matcher::regName[dst_first], ++ Matcher::regName[src_first]); ++#endif ++ } ++ } ++ return 0; ++ } ++ } ++ ++ assert(0," foo "); ++ Unimplemented(); ++ return 0; ++} ++ ++#ifndef PRODUCT ++void MachSpillCopyNode::format( PhaseRegAlloc *ra_, outputStream* st ) const { ++ implementation( NULL, ra_, false, st ); ++} ++#endif ++ ++void MachSpillCopyNode::emit(CodeBuffer &cbuf, PhaseRegAlloc *ra_) const { ++ implementation( &cbuf, ra_, false, NULL ); ++} ++ ++uint MachSpillCopyNode::size(PhaseRegAlloc *ra_) const { ++ return MachNode::size(ra_); ++} ++ ++//============================================================================= ++# ++ ++#ifndef PRODUCT ++void MachBreakpointNode::format( PhaseRegAlloc *, outputStream* st ) const { ++ st->print("BRK"); ++} ++#endif ++ ++void MachBreakpointNode::emit(CodeBuffer &cbuf, PhaseRegAlloc* ra_) const { ++ MacroAssembler _masm(&cbuf); ++ __ brk(5); ++} ++ ++uint MachBreakpointNode::size(PhaseRegAlloc* ra_) const { ++ return MachNode::size(ra_); ++} ++ ++ ++//============================================================================= ++#ifndef PRODUCT ++void MachEpilogNode::format( PhaseRegAlloc *ra_, outputStream* st ) const { ++ Compile *C = ra_->C; ++ int framesize = C->frame_size_in_bytes(); ++ ++ assert((framesize & (StackAlignmentInBytes-1)) == 0, "frame size not aligned"); ++ ++ st->print_cr("addi_d SP, SP, %d # Rlease stack @ MachEpilogNode", framesize); ++ st->print("\t"); ++ st->print_cr("ld_d RA, SP, %d # Restore RA @ MachEpilogNode", -wordSize); ++ st->print("\t"); ++ st->print_cr("ld_d FP, SP, %d # Restore FP @ MachEpilogNode", -wordSize*2); ++ if( do_polling() && C->is_method_compilation() ) { ++ st->print("\t"); ++ st->print_cr("Poll Safepoint # MachEpilogNode"); ++ } ++} ++#endif ++ ++void MachEpilogNode::emit(CodeBuffer &cbuf, PhaseRegAlloc *ra_) const { ++ Compile *C = ra_->C; ++ MacroAssembler _masm(&cbuf); ++ int framesize = C->frame_size_in_bytes(); ++ ++ assert((framesize & (StackAlignmentInBytes-1)) == 0, "frame size not aligned"); ++ ++ __ ld_d(RA, Address(SP, framesize - wordSize)); ++ __ ld_d(FP, Address(SP, framesize - wordSize * 2)); ++ if (Assembler::is_simm(framesize, 12)) { ++ __ addi_d(SP, SP, framesize); ++ } else { ++ __ li(AT, framesize); ++ __ add_d(SP, SP, AT); ++ } ++ ++ if( do_polling() && C->is_method_compilation() ) { ++ __ li(AT, (long)os::get_polling_page()); ++ __ relocate(relocInfo::poll_return_type); ++ __ ld_w(AT, AT, 0); ++ } ++} ++ ++uint MachEpilogNode::size(PhaseRegAlloc *ra_) const { ++ return MachNode::size(ra_); // too many variables; just compute it the hard way ++} ++ ++int MachEpilogNode::reloc() const { ++ return 0; // a large enough number ++} ++ ++const Pipeline * MachEpilogNode::pipeline() const { ++ return MachNode::pipeline_class(); ++} ++ ++int MachEpilogNode::safepoint_offset() const { return 0; } ++ ++//============================================================================= ++ ++#ifndef PRODUCT ++void BoxLockNode::format( PhaseRegAlloc *ra_, outputStream* st ) const { ++ int offset = ra_->reg2offset(in_RegMask(0).find_first_elem()); ++ int reg = ra_->get_reg_first(this); ++ st->print("ADDI_D %s, SP, %d @BoxLockNode",Matcher::regName[reg],offset); ++} ++#endif ++ ++ ++uint BoxLockNode::size(PhaseRegAlloc *ra_) const { ++ int offset = ra_->reg2offset(in_RegMask(0).find_first_elem()); ++ ++ if (Assembler::is_simm(offset, 12)) ++ return 4; ++ else ++ return 3 * 4; ++} ++ ++void BoxLockNode::emit(CodeBuffer &cbuf, PhaseRegAlloc *ra_) const { ++ MacroAssembler _masm(&cbuf); ++ int offset = ra_->reg2offset(in_RegMask(0).find_first_elem()); ++ int reg = ra_->get_encode(this); ++ ++ if (Assembler::is_simm(offset, 12)) { ++ __ addi_d(as_Register(reg), SP, offset); ++ } else { ++ __ lu12i_w(AT, Assembler::split_low20(offset >> 12)); ++ __ ori(AT, AT, Assembler::split_low12(offset)); ++ __ add_d(as_Register(reg), SP, AT); ++ } ++} ++ ++int MachCallRuntimeNode::ret_addr_offset() { ++ // pcaddu18i ++ // jirl ++ return NativeFarCall::instruction_size; ++} ++ ++ ++//============================================================================= ++#ifndef PRODUCT ++void MachNopNode::format( PhaseRegAlloc *, outputStream* st ) const { ++ st->print("NOP \t# %d bytes pad for loops and calls", 4 * _count); ++} ++#endif ++ ++void MachNopNode::emit(CodeBuffer &cbuf, PhaseRegAlloc * ) const { ++ MacroAssembler _masm(&cbuf); ++ int i = 0; ++ for(i = 0; i < _count; i++) ++ __ nop(); ++} ++ ++uint MachNopNode::size(PhaseRegAlloc *) const { ++ return 4 * _count; ++} ++const Pipeline* MachNopNode::pipeline() const { ++ return MachNode::pipeline_class(); ++} ++ ++//============================================================================= ++ ++//============================================================================= ++#ifndef PRODUCT ++void MachUEPNode::format( PhaseRegAlloc *ra_, outputStream* st ) const { ++ st->print_cr("load_klass(T4, T0)"); ++ st->print_cr("\tbeq(T4, iCache, L)"); ++ st->print_cr("\tjmp(SharedRuntime::get_ic_miss_stub(), relocInfo::runtime_call_type)"); ++ st->print_cr(" L:"); ++} ++#endif ++ ++ ++void MachUEPNode::emit(CodeBuffer &cbuf, PhaseRegAlloc *ra_) const { ++ MacroAssembler _masm(&cbuf); ++ int ic_reg = Matcher::inline_cache_reg_encode(); ++ Label L; ++ Register receiver = T0; ++ Register iCache = as_Register(ic_reg); ++ ++ __ load_klass(T4, receiver); ++ __ beq(T4, iCache, L); ++ __ jmp((address)SharedRuntime::get_ic_miss_stub(), relocInfo::runtime_call_type); ++ __ bind(L); ++} ++ ++uint MachUEPNode::size(PhaseRegAlloc *ra_) const { ++ return MachNode::size(ra_); ++} ++ ++ ++ ++//============================================================================= ++ ++const RegMask& MachConstantBaseNode::_out_RegMask = P_REG_mask(); ++ ++int Compile::ConstantTable::calculate_table_base_offset() const { ++ return 0; // absolute addressing, no offset ++} ++ ++bool MachConstantBaseNode::requires_postalloc_expand() const { return false; } ++void MachConstantBaseNode::postalloc_expand(GrowableArray *nodes, PhaseRegAlloc *ra_) { ++ ShouldNotReachHere(); ++} ++ ++void MachConstantBaseNode::emit(CodeBuffer& cbuf, PhaseRegAlloc* ra_) const { ++ Compile* C = ra_->C; ++ Compile::ConstantTable& constant_table = C->constant_table(); ++ MacroAssembler _masm(&cbuf); ++ ++ Register Rtoc = as_Register(ra_->get_encode(this)); ++ CodeSection* consts_section = cbuf.consts(); ++ int consts_size = consts_section->align_at_start(consts_section->size()); ++ assert(constant_table.size() == consts_size, "must be equal"); ++ ++ if (consts_section->size()) { ++ assert((CodeBuffer::SECT_CONSTS + 1) == CodeBuffer::SECT_INSTS, ++ "insts must be immediately follow consts"); ++ // Materialize the constant table base. ++ address baseaddr = cbuf.insts()->start() - consts_size + -(constant_table.table_base_offset()); ++ jint offs = (baseaddr - __ pc()) >> 2; ++ guarantee(Assembler::is_simm(offs, 20), "Not signed 20-bit offset"); ++ __ pcaddi(Rtoc, offs); ++ } ++} ++ ++uint MachConstantBaseNode::size(PhaseRegAlloc* ra_) const { ++ // pcaddi ++ return 1 * BytesPerInstWord; ++} ++ ++#ifndef PRODUCT ++void MachConstantBaseNode::format(PhaseRegAlloc* ra_, outputStream* st) const { ++ Register r = as_Register(ra_->get_encode(this)); ++ st->print("pcaddi %s, &constanttable (constant table base) @ MachConstantBaseNode", r->name()); ++} ++#endif ++ ++ ++//============================================================================= ++#ifndef PRODUCT ++void MachPrologNode::format( PhaseRegAlloc *ra_, outputStream* st ) const { ++ Compile* C = ra_->C; ++ ++ int framesize = C->frame_size_in_bytes(); ++ int bangsize = C->bang_size_in_bytes(); ++ assert((framesize & (StackAlignmentInBytes-1)) == 0, "frame size not aligned"); ++ ++ // Calls to C2R adapters often do not accept exceptional returns. ++ // We require that their callers must bang for them. But be careful, because ++ // some VM calls (such as call site linkage) can use several kilobytes of ++ // stack. But the stack safety zone should account for that. ++ // See bugs 4446381, 4468289, 4497237. ++ if (C->need_stack_bang(bangsize)) { ++ st->print_cr("# stack bang"); st->print("\t"); ++ } ++ st->print("st_d RA, %d(SP) @ MachPrologNode\n\t", -wordSize); ++ st->print("st_d FP, %d(SP) @ MachPrologNode\n\t", -wordSize*2); ++ st->print("addi_d FP, SP, -%d \n\t", wordSize*2); ++ st->print("addi_d SP, SP, -%d \t",framesize); ++} ++#endif ++ ++ ++void MachPrologNode::emit(CodeBuffer &cbuf, PhaseRegAlloc *ra_) const { ++ Compile* C = ra_->C; ++ MacroAssembler _masm(&cbuf); ++ ++ int framesize = C->frame_size_in_bytes(); ++ int bangsize = C->bang_size_in_bytes(); ++ ++ assert((framesize & (StackAlignmentInBytes-1)) == 0, "frame size not aligned"); ++ ++#ifdef ASSERT ++ address start = __ pc(); ++#endif ++ ++ if (C->need_stack_bang(bangsize)) { ++ __ generate_stack_overflow_check(bangsize); ++ } ++ ++ if (Assembler::is_simm(-framesize, 12)) { ++ __ addi_d(SP, SP, -framesize); ++ } else { ++ __ li(AT, -framesize); ++ __ add_d(SP, SP, AT); ++ } ++ __ st_d(RA, Address(SP, framesize - wordSize)); ++ __ st_d(FP, Address(SP, framesize - wordSize * 2)); ++ if (Assembler::is_simm(framesize - wordSize * 2, 12)) { ++ __ addi_d(FP, SP, framesize - wordSize * 2); ++ } else { ++ __ li(AT, framesize - wordSize * 2); ++ __ add_d(FP, SP, AT); ++ } ++ ++ assert((__ pc() - start) >= 1 * BytesPerInstWord, "No enough room for patch_verified_entry"); ++ ++ C->set_frame_complete(cbuf.insts_size()); ++ if (C->has_mach_constant_base_node()) { ++ // NOTE: We set the table base offset here because users might be ++ // emitted before MachConstantBaseNode. ++ Compile::ConstantTable& constant_table = C->constant_table(); ++ constant_table.set_table_base_offset(constant_table.calculate_table_base_offset()); ++ } ++} ++ ++ ++uint MachPrologNode::size(PhaseRegAlloc *ra_) const { ++ return MachNode::size(ra_); // too many variables; just compute it the hard way ++} ++ ++int MachPrologNode::reloc() const { ++ return 0; // a large enough number ++} ++ ++%} ++ ++//----------ENCODING BLOCK----------------------------------------------------- ++// This block specifies the encoding classes used by the compiler to output ++// byte streams. Encoding classes generate functions which are called by ++// Machine Instruction Nodes in order to generate the bit encoding of the ++// instruction. Operands specify their base encoding interface with the ++// interface keyword. There are currently supported four interfaces, ++// REG_INTER, CONST_INTER, MEMORY_INTER, & COND_INTER. REG_INTER causes an ++// operand to generate a function which returns its register number when ++// queried. CONST_INTER causes an operand to generate a function which ++// returns the value of the constant when queried. MEMORY_INTER causes an ++// operand to generate four functions which return the Base Register, the ++// Index Register, the Scale Value, and the Offset Value of the operand when ++// queried. COND_INTER causes an operand to generate six functions which ++// return the encoding code (ie - encoding bits for the instruction) ++// associated with each basic boolean condition for a conditional instruction. ++// Instructions specify two basic values for encoding. They use the ++// ins_encode keyword to specify their encoding class (which must be one of ++// the class names specified in the encoding block), and they use the ++// opcode keyword to specify, in order, their primary, secondary, and ++// tertiary opcode. Only the opcode sections which a particular instruction ++// needs for encoding need to be specified. ++encode %{ ++ ++ enc_class Java_To_Runtime (method meth) %{ // CALL Java_To_Runtime, Java_To_Runtime_Leaf ++ MacroAssembler _masm(&cbuf); ++ // This is the instruction starting address for relocation info. ++ __ block_comment("Java_To_Runtime"); ++ cbuf.set_insts_mark(); ++ __ relocate(relocInfo::runtime_call_type); ++ __ patchable_call((address)$meth$$method); ++ %} ++ ++ enc_class Java_Static_Call (method meth) %{ // JAVA STATIC CALL ++ // CALL to fixup routine. Fixup routine uses ScopeDesc info to determine ++ // who we intended to call. ++ MacroAssembler _masm(&cbuf); ++ address addr = (address)$meth$$method; ++ address call; ++ __ block_comment("Java_Static_Call"); ++ ++ if ( !_method ) { ++ // A call to a runtime wrapper, e.g. new, new_typeArray_Java, uncommon_trap. ++ call = __ trampoline_call(AddressLiteral(addr, relocInfo::runtime_call_type), &cbuf); ++ } else if(_optimized_virtual) { ++ call = __ trampoline_call(AddressLiteral(addr, relocInfo::opt_virtual_call_type), &cbuf); ++ } else { ++ call = __ trampoline_call(AddressLiteral(addr, relocInfo::static_call_type), &cbuf); ++ } ++ ++ if (call == NULL) { ++ ciEnv::current()->record_failure("CodeCache is full"); ++ return; ++ } ++ ++ if( _method ) { // Emit stub for static call ++ address stub = CompiledStaticCall::emit_to_interp_stub(cbuf); ++ if (stub == NULL) { ++ ciEnv::current()->record_failure("CodeCache is full"); ++ return; ++ } ++ } ++ %} ++ ++ ++ // ++ // [Ref: LIR_Assembler::ic_call() ] ++ // ++ enc_class Java_Dynamic_Call (method meth) %{ // JAVA DYNAMIC CALL ++ MacroAssembler _masm(&cbuf); ++ __ block_comment("Java_Dynamic_Call"); ++ address call = __ ic_call((address)$meth$$method); ++ if (call == NULL) { ++ ciEnv::current()->record_failure("CodeCache is full"); ++ return; ++ } ++ %} ++ ++ ++ enc_class enc_PartialSubtypeCheck(mRegP result, mRegP sub, mRegP super, mRegI tmp) %{ ++ Register result = $result$$Register; ++ Register sub = $sub$$Register; ++ Register super = $super$$Register; ++ Register length = $tmp$$Register; ++ Register tmp = T4; ++ Label miss; ++ ++ // result may be the same as sub ++ // 47c B40: # B21 B41 <- B20 Freq: 0.155379 ++ // 47c partialSubtypeCheck result=S1, sub=S1, super=S3, length=S0 ++ // 4bc mov S2, NULL #@loadConP ++ // 4c0 beq S1, S2, B21 #@branchConP P=0.999999 C=-1.000000 ++ // ++ MacroAssembler _masm(&cbuf); ++ Label done; ++ __ check_klass_subtype_slow_path(sub, super, length, tmp, ++ NULL, &miss, ++ /*set_cond_codes:*/ true); ++ // Refer to X86_64's RDI ++ __ move(result, 0); ++ __ b(done); ++ ++ __ bind(miss); ++ __ li(result, 1); ++ __ bind(done); ++ %} ++ ++%} ++ ++ ++//---------LOONGARCH FRAME-------------------------------------------------------------- ++// Definition of frame structure and management information. ++// ++// S T A C K L A Y O U T Allocators stack-slot number ++// | (to get allocators register number ++// G Owned by | | v add SharedInfo::stack0) ++// r CALLER | | ++// o | +--------+ pad to even-align allocators stack-slot ++// w V | pad0 | numbers; owned by CALLER ++// t -----------+--------+----> Matcher::_in_arg_limit, unaligned ++// h ^ | in | 5 ++// | | args | 4 Holes in incoming args owned by SELF ++// | | old | | 3 ++// | | SP-+--------+----> Matcher::_old_SP, even aligned ++// v | | ret | 3 return address ++// Owned by +--------+ ++// Self | pad2 | 2 pad to align old SP ++// | +--------+ 1 ++// | | locks | 0 ++// | +--------+----> SharedInfo::stack0, even aligned ++// | | pad1 | 11 pad to align new SP ++// | +--------+ ++// | | | 10 ++// | | spills | 9 spills ++// V | | 8 (pad0 slot for callee) ++// -----------+--------+----> Matcher::_out_arg_limit, unaligned ++// ^ | out | 7 ++// | | args | 6 Holes in outgoing args owned by CALLEE ++// Owned by new | | ++// Callee SP-+--------+----> Matcher::_new_SP, even aligned ++// | | ++// ++// Note 1: Only region 8-11 is determined by the allocator. Region 0-5 is ++// known from SELF's arguments and the Java calling convention. ++// Region 6-7 is determined per call site. ++// Note 2: If the calling convention leaves holes in the incoming argument ++// area, those holes are owned by SELF. Holes in the outgoing area ++// are owned by the CALLEE. Holes should not be nessecary in the ++// incoming area, as the Java calling convention is completely under ++// the control of the AD file. Doubles can be sorted and packed to ++// avoid holes. Holes in the outgoing arguments may be nessecary for ++// varargs C calling conventions. ++// Note 3: Region 0-3 is even aligned, with pad2 as needed. Region 3-5 is ++// even aligned with pad0 as needed. ++// Region 6 is even aligned. Region 6-7 is NOT even aligned; ++// region 6-11 is even aligned; it may be padded out more so that ++// the region from SP to FP meets the minimum stack alignment. ++// Note 4: For I2C adapters, the incoming FP may not meet the minimum stack ++// alignment. Region 11, pad1, may be dynamically extended so that ++// SP meets the minimum alignment. ++ ++ ++frame %{ ++ ++ stack_direction(TOWARDS_LOW); ++ ++ // These two registers define part of the calling convention ++ // between compiled code and the interpreter. ++ // SEE StartI2CNode::calling_convention & StartC2INode::calling_convention & StartOSRNode::calling_convention ++ // for more information. ++ ++ inline_cache_reg(T1); // Inline Cache Register ++ interpreter_method_oop_reg(S3); // Method Oop Register when calling interpreter ++ ++ // Optional: name the operand used by cisc-spilling to access [stack_pointer + offset] ++ cisc_spilling_operand_name(indOffset32); ++ ++ // Number of stack slots consumed by locking an object ++ // generate Compile::sync_stack_slots ++ sync_stack_slots(2); ++ ++ frame_pointer(SP); ++ ++ // Interpreter stores its frame pointer in a register which is ++ // stored to the stack by I2CAdaptors. ++ // I2CAdaptors convert from interpreted java to compiled java. ++ ++ interpreter_frame_pointer(FP); ++ ++ // generate Matcher::stack_alignment ++ stack_alignment(StackAlignmentInBytes); //wordSize = sizeof(char*); ++ ++ // Number of stack slots between incoming argument block and the start of ++ // a new frame. The PROLOG must add this many slots to the stack. The ++ // EPILOG must remove this many slots. ++ in_preserve_stack_slots(4); //Now VerifyStackAtCalls is defined as false ! Leave two stack slots for ra and fp ++ ++ // Number of outgoing stack slots killed above the out_preserve_stack_slots ++ // for calls to C. Supports the var-args backing area for register parms. ++ varargs_C_out_slots_killed(0); ++ ++ // The after-PROLOG location of the return address. Location of ++ // return address specifies a type (REG or STACK) and a number ++ // representing the register number (i.e. - use a register name) or ++ // stack slot. ++ // Ret Addr is on stack in slot 0 if no locks or verification or alignment. ++ // Otherwise, it is above the locks and verification slot and alignment word ++ //return_addr(STACK -1+ round_to(1+VerifyStackAtCalls+Compile::current()->sync()*Compile::current()->sync_stack_slots(),WordsPerLong)); ++ return_addr(REG RA); ++ ++ // Body of function which returns an integer array locating ++ // arguments either in registers or in stack slots. Passed an array ++ // of ideal registers called "sig" and a "length" count. Stack-slot ++ // offsets are based on outgoing arguments, i.e. a CALLER setting up ++ // arguments for a CALLEE. Incoming stack arguments are ++ // automatically biased by the preserve_stack_slots field above. ++ ++ ++ // will generated to Matcher::calling_convention(OptoRegPair *sig, uint length, bool is_outgoing) ++ // StartNode::calling_convention call this. ++ calling_convention %{ ++ SharedRuntime::java_calling_convention(sig_bt, regs, length, false); ++ %} ++ ++ ++ ++ ++ // Body of function which returns an integer array locating ++ // arguments either in registers or in stack slots. Passed an array ++ // of ideal registers called "sig" and a "length" count. Stack-slot ++ // offsets are based on outgoing arguments, i.e. a CALLER setting up ++ // arguments for a CALLEE. Incoming stack arguments are ++ // automatically biased by the preserve_stack_slots field above. ++ ++ ++ // SEE CallRuntimeNode::calling_convention for more information. ++ c_calling_convention %{ ++ (void) SharedRuntime::c_calling_convention(sig_bt, regs, /*regs2=*/NULL, length); ++ %} ++ ++ ++ // Location of C & interpreter return values ++ // register(s) contain(s) return value for Op_StartI2C and Op_StartOSR. ++ // SEE Matcher::match. ++ c_return_value %{ ++ assert( ideal_reg >= Op_RegI && ideal_reg <= Op_RegL, "only return normal values" ); ++ /* -- , -- , Op_RegN, Op_RegI, Op_RegP, Op_RegF, Op_RegD, Op_RegL */ ++ static int lo[Op_RegL+1] = { 0, 0, V0_num, V0_num, V0_num, F0_num, F0_num, V0_num }; ++ static int hi[Op_RegL+1] = { 0, 0, OptoReg::Bad, OptoReg::Bad, V0_H_num, OptoReg::Bad, F0_H_num, V0_H_num }; ++ return OptoRegPair(hi[ideal_reg],lo[ideal_reg]); ++ %} ++ ++ // Location of return values ++ // register(s) contain(s) return value for Op_StartC2I and Op_Start. ++ // SEE Matcher::match. ++ ++ return_value %{ ++ assert( ideal_reg >= Op_RegI && ideal_reg <= Op_RegL, "only return normal values" ); ++ /* -- , -- , Op_RegN, Op_RegI, Op_RegP, Op_RegF, Op_RegD, Op_RegL */ ++ static int lo[Op_RegL+1] = { 0, 0, V0_num, V0_num, V0_num, F0_num, F0_num, V0_num }; ++ static int hi[Op_RegL+1] = { 0, 0, OptoReg::Bad, OptoReg::Bad, V0_H_num, OptoReg::Bad, F0_H_num, V0_H_num}; ++ return OptoRegPair(hi[ideal_reg],lo[ideal_reg]); ++ %} ++ ++%} ++ ++//----------ATTRIBUTES--------------------------------------------------------- ++//----------Operand Attributes------------------------------------------------- ++op_attrib op_cost(0); // Required cost attribute ++ ++//----------Instruction Attributes--------------------------------------------- ++ins_attrib ins_cost(100); // Required cost attribute ++ins_attrib ins_size(32); // Required size attribute (in bits) ++ins_attrib ins_pc_relative(0); // Required PC Relative flag ++ins_attrib ins_short_branch(0); // Required flag: is this instruction a ++ // non-matching short branch variant of some ++ // long branch? ++ins_attrib ins_alignment(4); // Required alignment attribute (must be a power of 2) ++ // specifies the alignment that some part of the instruction (not ++ // necessarily the start) requires. If > 1, a compute_padding() ++ // function must be provided for the instruction ++ ++//----------OPERANDS----------------------------------------------------------- ++// Operand definitions must precede instruction definitions for correct parsing ++// in the ADLC because operands constitute user defined types which are used in ++// instruction definitions. ++ ++// Vectors ++ ++operand vecX() %{ ++ constraint(ALLOC_IN_RC(vectorx_reg)); ++ match(VecX); ++ ++ format %{ %} ++ interface(REG_INTER); ++%} ++ ++operand vecY() %{ ++ constraint(ALLOC_IN_RC(vectory_reg)); ++ match(VecY); ++ ++ format %{ %} ++ interface(REG_INTER); ++%} ++ ++// Flags register, used as output of compare instructions ++operand FlagsReg() %{ ++ constraint(ALLOC_IN_RC(t0_reg)); ++ match(RegFlags); ++ ++ format %{ "T0" %} ++ interface(REG_INTER); ++%} ++ ++//----------Simple Operands---------------------------------------------------- ++// TODO: Should we need to define some more special immediate number ? ++// Immediate Operands ++// Integer Immediate ++operand immI() %{ ++ match(ConI); ++ ++ op_cost(20); ++ format %{ %} ++ interface(CONST_INTER); ++%} ++ ++operand immIU1() %{ ++ predicate((0 <= n->get_int()) && (n->get_int() <= 1)); ++ match(ConI); ++ ++ op_cost(5); ++ format %{ %} ++ interface(CONST_INTER); ++%} ++ ++operand immIU2() %{ ++ predicate((0 <= n->get_int()) && (n->get_int() <= 3)); ++ match(ConI); ++ ++ op_cost(5); ++ format %{ %} ++ interface(CONST_INTER); ++%} ++ ++operand immIU3() %{ ++ predicate((0 <= n->get_int()) && (n->get_int() <= 7)); ++ match(ConI); ++ ++ op_cost(5); ++ format %{ %} ++ interface(CONST_INTER); ++%} ++ ++operand immIU4() %{ ++ predicate((0 <= n->get_int()) && (n->get_int() <= 15)); ++ match(ConI); ++ ++ op_cost(5); ++ format %{ %} ++ interface(CONST_INTER); ++%} ++ ++operand immIU5() %{ ++ predicate((0 <= n->get_int()) && (n->get_int() <= 31)); ++ match(ConI); ++ ++ op_cost(5); ++ format %{ %} ++ interface(CONST_INTER); ++%} ++ ++operand immIU6() %{ ++ predicate((0 <= n->get_int()) && (n->get_int() <= 63)); ++ match(ConI); ++ ++ op_cost(5); ++ format %{ %} ++ interface(CONST_INTER); ++%} ++ ++operand immIU8() %{ ++ predicate((0 <= n->get_int()) && (n->get_int() <= 255)); ++ match(ConI); ++ ++ op_cost(5); ++ format %{ %} ++ interface(CONST_INTER); ++%} ++ ++operand immI10() %{ ++ predicate((-512 <= n->get_int()) && (n->get_int() <= 511)); ++ match(ConI); ++ ++ op_cost(5); ++ format %{ %} ++ interface(CONST_INTER); ++%} ++ ++operand immI12() %{ ++ predicate((-2048 <= n->get_int()) && (n->get_int() <= 2047)); ++ match(ConI); ++ ++ op_cost(5); ++ format %{ %} ++ interface(CONST_INTER); ++%} ++ ++operand immI_M65536() %{ ++ predicate(n->get_int() == -65536); ++ match(ConI); ++ ++ op_cost(5); ++ format %{ %} ++ interface(CONST_INTER); ++%} ++ ++// Constant for decrement ++operand immI_M1() %{ ++ predicate(n->get_int() == -1); ++ match(ConI); ++ ++ op_cost(0); ++ format %{ %} ++ interface(CONST_INTER); ++%} ++ ++// Constant for zero ++operand immI_0() %{ ++ predicate(n->get_int() == 0); ++ match(ConI); ++ ++ op_cost(0); ++ format %{ %} ++ interface(CONST_INTER); ++%} ++ ++operand immI_1() %{ ++ predicate(n->get_int() == 1); ++ match(ConI); ++ ++ op_cost(0); ++ format %{ %} ++ interface(CONST_INTER); ++%} ++ ++operand immI_2() %{ ++ predicate(n->get_int() == 2); ++ match(ConI); ++ ++ op_cost(0); ++ format %{ %} ++ interface(CONST_INTER); ++%} ++ ++operand immI_16() %{ ++ predicate(n->get_int() == 16); ++ match(ConI); ++ ++ format %{ %} ++ interface(CONST_INTER); ++%} ++ ++operand immI_24() %{ ++ predicate(n->get_int() == 24); ++ match(ConI); ++ ++ format %{ %} ++ interface(CONST_INTER); ++%} ++ ++// Constant for long shifts ++operand immI_32() %{ ++ predicate(n->get_int() == 32); ++ match(ConI); ++ ++ op_cost(0); ++ format %{ %} ++ interface(CONST_INTER); ++%} ++ ++// Constant for byte-wide masking ++operand immI_255() %{ ++ predicate(n->get_int() == 255); ++ match(ConI); ++ ++ op_cost(0); ++ format %{ %} ++ interface(CONST_INTER); ++%} ++ ++operand immI_65535() %{ ++ predicate(n->get_int() == 65535); ++ match(ConI); ++ ++ op_cost(5); ++ format %{ %} ++ interface(CONST_INTER); ++%} ++ ++operand immI_MaxI() %{ ++ predicate(n->get_int() == 2147483647); ++ match(ConI); ++ ++ op_cost(0); ++ format %{ %} ++ interface(CONST_INTER); ++%} ++ ++operand immI_M2047_2048() %{ ++ predicate((-2047 <= n->get_int()) && (n->get_int() <= 2048)); ++ match(ConI); ++ ++ op_cost(10); ++ format %{ %} ++ interface(CONST_INTER); ++%} ++ ++// Valid scale values for addressing modes ++operand immI_0_3() %{ ++ predicate(0 <= n->get_int() && (n->get_int() <= 3)); ++ match(ConI); ++ ++ format %{ %} ++ interface(CONST_INTER); ++%} ++ ++operand immI_0_31() %{ ++ predicate(n->get_int() >= 0 && n->get_int() <= 31); ++ match(ConI); ++ ++ op_cost(0); ++ format %{ %} ++ interface(CONST_INTER); ++%} ++ ++operand immI_0_4095() %{ ++ predicate(n->get_int() >= 0 && n->get_int() <= 4095); ++ match(ConI); ++ op_cost(0); ++ ++ format %{ %} ++ interface(CONST_INTER); ++%} ++ ++operand immI_1_4() %{ ++ predicate(1 <= n->get_int() && (n->get_int() <= 4)); ++ match(ConI); ++ ++ format %{ %} ++ interface(CONST_INTER); ++%} ++ ++operand immI_32_63() %{ ++ predicate(n->get_int() >= 32 && n->get_int() <= 63); ++ match(ConI); ++ op_cost(0); ++ ++ format %{ %} ++ interface(CONST_INTER); ++%} ++ ++operand immI_M128_255() %{ ++ predicate((-128 <= n->get_int()) && (n->get_int() <= 255)); ++ match(ConI); ++ ++ op_cost(5); ++ format %{ %} ++ interface(CONST_INTER); ++%} ++ ++// Operand for non-negtive integer mask ++operand immI_nonneg_mask() %{ ++ predicate((n->get_int() >= 0) && (Assembler::is_int_mask(n->get_int()) != -1)); ++ match(ConI); ++ ++ op_cost(0); ++ format %{ %} ++ interface(CONST_INTER); ++%} ++ ++// Long Immediate ++operand immL() %{ ++ match(ConL); ++ ++ op_cost(20); ++ format %{ %} ++ interface(CONST_INTER); ++%} ++ ++operand immLU5() %{ ++ predicate((0 <= n->get_long()) && (n->get_long() <= 31)); ++ match(ConL); ++ ++ op_cost(5); ++ format %{ %} ++ interface(CONST_INTER); ++%} ++ ++operand immL10() %{ ++ predicate((-512 <= n->get_long()) && (n->get_long() <= 511)); ++ match(ConL); ++ ++ op_cost(5); ++ format %{ %} ++ interface(CONST_INTER); ++%} ++ ++operand immL12() %{ ++ predicate((-2048 <= n->get_long()) && (n->get_long() <= 2047)); ++ match(ConL); ++ ++ op_cost(10); ++ format %{ %} ++ interface(CONST_INTER); ++%} ++ ++// Long Immediate 32-bit signed ++operand immL32() ++%{ ++ predicate(n->get_long() == (int)n->get_long()); ++ match(ConL); ++ ++ op_cost(15); ++ format %{ %} ++ interface(CONST_INTER); ++%} ++ ++// bit 3..6 zero ++operand immL_M121() %{ ++ predicate(n->get_long() == -121L); ++ match(ConL); ++ op_cost(0); ++ ++ format %{ %} ++ interface(CONST_INTER); ++%} ++ ++// bit 0..2 zero ++operand immL_M8() %{ ++ predicate(n->get_long() == -8L); ++ match(ConL); ++ op_cost(0); ++ ++ format %{ %} ++ interface(CONST_INTER); ++%} ++ ++// bit 1..2 zero ++operand immL_M7() %{ ++ predicate(n->get_long() == -7L); ++ match(ConL); ++ op_cost(0); ++ ++ format %{ %} ++ interface(CONST_INTER); ++%} ++ ++// bit 2 zero ++operand immL_M5() %{ ++ predicate(n->get_long() == -5L); ++ match(ConL); ++ op_cost(0); ++ ++ format %{ %} ++ interface(CONST_INTER); ++%} ++ ++// bit 0..1 zero ++operand immL_M4() %{ ++ predicate(n->get_long() == -4L); ++ match(ConL); ++ op_cost(0); ++ ++ format %{ %} ++ interface(CONST_INTER); ++%} ++ ++// Long Immediate zero ++operand immL_0() %{ ++ predicate(n->get_long() == 0L); ++ match(ConL); ++ op_cost(0); ++ ++ format %{ %} ++ interface(CONST_INTER); ++%} ++ ++operand immL_7() %{ ++ predicate(n->get_long() == 7L); ++ match(ConL); ++ op_cost(0); ++ ++ format %{ %} ++ interface(CONST_INTER); ++%} ++ ++operand immL_MaxUI() %{ ++ predicate(n->get_long() == 0xFFFFFFFFL); ++ match(ConL); ++ op_cost(20); ++ ++ format %{ %} ++ interface(CONST_INTER); ++%} ++ ++operand immL_M2047_2048() %{ ++ predicate((-2047 <= n->get_long()) && (n->get_long() <= 2048)); ++ match(ConL); ++ ++ op_cost(10); ++ format %{ %} ++ interface(CONST_INTER); ++%} ++ ++operand immL_0_4095() %{ ++ predicate(n->get_long() >= 0 && n->get_long() <= 4095); ++ match(ConL); ++ op_cost(0); ++ ++ format %{ %} ++ interface(CONST_INTER); ++%} ++ ++// Operand for non-negtive long mask ++operand immL_nonneg_mask() %{ ++ predicate((n->get_long() >= 0) && (Assembler::is_jlong_mask(n->get_long()) != -1)); ++ match(ConL); ++ ++ op_cost(0); ++ format %{ %} ++ interface(CONST_INTER); ++%} ++ ++// Pointer Immediate ++operand immP() %{ ++ match(ConP); ++ ++ op_cost(10); ++ format %{ %} ++ interface(CONST_INTER); ++%} ++ ++// NULL Pointer Immediate ++operand immP_0() %{ ++ predicate(n->get_ptr() == 0); ++ match(ConP); ++ op_cost(0); ++ ++ format %{ %} ++ interface(CONST_INTER); ++%} ++ ++// Pointer Immediate ++operand immP_no_oop_cheap() %{ ++ predicate(!n->bottom_type()->isa_oop_ptr()); ++ match(ConP); ++ ++ op_cost(5); ++ // formats are generated automatically for constants and base registers ++ format %{ %} ++ interface(CONST_INTER); ++%} ++ ++// Pointer for polling page ++operand immP_poll() %{ ++ predicate(n->get_ptr() != 0 && n->get_ptr() == (intptr_t)os::get_polling_page()); ++ match(ConP); ++ op_cost(5); ++ ++ format %{ %} ++ interface(CONST_INTER); ++%} ++ ++// Pointer Immediate ++operand immN() %{ ++ match(ConN); ++ ++ op_cost(10); ++ format %{ %} ++ interface(CONST_INTER); ++%} ++ ++// NULL Pointer Immediate ++operand immN_0() %{ ++ predicate(n->get_narrowcon() == 0); ++ match(ConN); ++ ++ op_cost(5); ++ format %{ %} ++ interface(CONST_INTER); ++%} ++ ++operand immNKlass() %{ ++ match(ConNKlass); ++ ++ op_cost(10); ++ format %{ %} ++ interface(CONST_INTER); ++%} ++ ++// Single-precision floating-point immediate ++operand immF() %{ ++ match(ConF); ++ ++ op_cost(20); ++ format %{ %} ++ interface(CONST_INTER); ++%} ++ ++// Single-precision floating-point zero ++operand immF_0() %{ ++ predicate(jint_cast(n->getf()) == 0); ++ match(ConF); ++ ++ op_cost(5); ++ format %{ %} ++ interface(CONST_INTER); ++%} ++ ++// Double-precision floating-point immediate ++operand immD() %{ ++ match(ConD); ++ ++ op_cost(20); ++ format %{ %} ++ interface(CONST_INTER); ++%} ++ ++// Double-precision floating-point zero ++operand immD_0() %{ ++ predicate(jlong_cast(n->getd()) == 0); ++ match(ConD); ++ ++ op_cost(5); ++ format %{ %} ++ interface(CONST_INTER); ++%} ++ ++// Register Operands ++// Integer Register ++operand mRegI() %{ ++ constraint(ALLOC_IN_RC(int_reg)); ++ match(RegI); ++ ++ format %{ %} ++ interface(REG_INTER); ++%} ++ ++operand no_Ax_mRegI() %{ ++ constraint(ALLOC_IN_RC(no_Ax_int_reg)); ++ match(RegI); ++ match(mRegI); ++ ++ format %{ %} ++ interface(REG_INTER); ++%} ++ ++operand mS0RegI() %{ ++ constraint(ALLOC_IN_RC(s0_reg)); ++ match(RegI); ++ match(mRegI); ++ ++ format %{ "S0" %} ++ interface(REG_INTER); ++%} ++ ++operand mS1RegI() %{ ++ constraint(ALLOC_IN_RC(s1_reg)); ++ match(RegI); ++ match(mRegI); ++ ++ format %{ "S1" %} ++ interface(REG_INTER); ++%} ++ ++operand mS3RegI() %{ ++ constraint(ALLOC_IN_RC(s3_reg)); ++ match(RegI); ++ match(mRegI); ++ ++ format %{ "S3" %} ++ interface(REG_INTER); ++%} ++ ++operand mS4RegI() %{ ++ constraint(ALLOC_IN_RC(s4_reg)); ++ match(RegI); ++ match(mRegI); ++ ++ format %{ "S4" %} ++ interface(REG_INTER); ++%} ++ ++operand mS5RegI() %{ ++ constraint(ALLOC_IN_RC(s5_reg)); ++ match(RegI); ++ match(mRegI); ++ ++ format %{ "S5" %} ++ interface(REG_INTER); ++%} ++ ++operand mS6RegI() %{ ++ constraint(ALLOC_IN_RC(s6_reg)); ++ match(RegI); ++ match(mRegI); ++ ++ format %{ "S6" %} ++ interface(REG_INTER); ++%} ++ ++operand mS7RegI() %{ ++ constraint(ALLOC_IN_RC(s7_reg)); ++ match(RegI); ++ match(mRegI); ++ ++ format %{ "S7" %} ++ interface(REG_INTER); ++%} ++ ++ ++operand mT0RegI() %{ ++ constraint(ALLOC_IN_RC(t0_reg)); ++ match(RegI); ++ match(mRegI); ++ ++ format %{ "T0" %} ++ interface(REG_INTER); ++%} ++ ++operand mT1RegI() %{ ++ constraint(ALLOC_IN_RC(t1_reg)); ++ match(RegI); ++ match(mRegI); ++ ++ format %{ "T1" %} ++ interface(REG_INTER); ++%} ++ ++operand mT2RegI() %{ ++ constraint(ALLOC_IN_RC(t2_reg)); ++ match(RegI); ++ match(mRegI); ++ ++ format %{ "T2" %} ++ interface(REG_INTER); ++%} ++ ++operand mT3RegI() %{ ++ constraint(ALLOC_IN_RC(t3_reg)); ++ match(RegI); ++ match(mRegI); ++ ++ format %{ "T3" %} ++ interface(REG_INTER); ++%} ++ ++operand mT8RegI() %{ ++ constraint(ALLOC_IN_RC(t8_reg)); ++ match(RegI); ++ match(mRegI); ++ ++ format %{ "T8" %} ++ interface(REG_INTER); ++%} ++ ++operand mT4RegI() %{ ++ constraint(ALLOC_IN_RC(t4_reg)); ++ match(RegI); ++ match(mRegI); ++ ++ format %{ "T4" %} ++ interface(REG_INTER); ++%} ++ ++operand mA0RegI() %{ ++ constraint(ALLOC_IN_RC(a0_reg)); ++ match(RegI); ++ match(mRegI); ++ ++ format %{ "A0" %} ++ interface(REG_INTER); ++%} ++ ++operand mA1RegI() %{ ++ constraint(ALLOC_IN_RC(a1_reg)); ++ match(RegI); ++ match(mRegI); ++ ++ format %{ "A1" %} ++ interface(REG_INTER); ++%} ++ ++operand mA2RegI() %{ ++ constraint(ALLOC_IN_RC(a2_reg)); ++ match(RegI); ++ match(mRegI); ++ ++ format %{ "A2" %} ++ interface(REG_INTER); ++%} ++ ++operand mA3RegI() %{ ++ constraint(ALLOC_IN_RC(a3_reg)); ++ match(RegI); ++ match(mRegI); ++ ++ format %{ "A3" %} ++ interface(REG_INTER); ++%} ++ ++operand mA4RegI() %{ ++ constraint(ALLOC_IN_RC(a4_reg)); ++ match(RegI); ++ match(mRegI); ++ ++ format %{ "A4" %} ++ interface(REG_INTER); ++%} ++ ++operand mA5RegI() %{ ++ constraint(ALLOC_IN_RC(a5_reg)); ++ match(RegI); ++ match(mRegI); ++ ++ format %{ "A5" %} ++ interface(REG_INTER); ++%} ++ ++operand mA6RegI() %{ ++ constraint(ALLOC_IN_RC(a6_reg)); ++ match(RegI); ++ match(mRegI); ++ ++ format %{ "A6" %} ++ interface(REG_INTER); ++%} ++ ++operand mA7RegI() %{ ++ constraint(ALLOC_IN_RC(a7_reg)); ++ match(RegI); ++ match(mRegI); ++ ++ format %{ "A7" %} ++ interface(REG_INTER); ++%} ++ ++operand mRegN() %{ ++ constraint(ALLOC_IN_RC(int_reg)); ++ match(RegN); ++ ++ format %{ %} ++ interface(REG_INTER); ++%} ++ ++operand t0_RegN() %{ ++ constraint(ALLOC_IN_RC(t0_reg)); ++ match(RegN); ++ match(mRegN); ++ ++ format %{ %} ++ interface(REG_INTER); ++%} ++ ++operand t1_RegN() %{ ++ constraint(ALLOC_IN_RC(t1_reg)); ++ match(RegN); ++ match(mRegN); ++ ++ format %{ %} ++ interface(REG_INTER); ++%} ++ ++operand t3_RegN() %{ ++ constraint(ALLOC_IN_RC(t3_reg)); ++ match(RegN); ++ match(mRegN); ++ ++ format %{ %} ++ interface(REG_INTER); ++%} ++ ++operand t8_RegN() %{ ++ constraint(ALLOC_IN_RC(t8_reg)); ++ match(RegN); ++ match(mRegN); ++ ++ format %{ %} ++ interface(REG_INTER); ++%} ++ ++operand a0_RegN() %{ ++ constraint(ALLOC_IN_RC(a0_reg)); ++ match(RegN); ++ match(mRegN); ++ ++ format %{ %} ++ interface(REG_INTER); ++%} ++ ++operand a1_RegN() %{ ++ constraint(ALLOC_IN_RC(a1_reg)); ++ match(RegN); ++ match(mRegN); ++ ++ format %{ %} ++ interface(REG_INTER); ++%} ++ ++operand a2_RegN() %{ ++ constraint(ALLOC_IN_RC(a2_reg)); ++ match(RegN); ++ match(mRegN); ++ ++ format %{ %} ++ interface(REG_INTER); ++%} ++ ++operand a3_RegN() %{ ++ constraint(ALLOC_IN_RC(a3_reg)); ++ match(RegN); ++ match(mRegN); ++ ++ format %{ %} ++ interface(REG_INTER); ++%} ++ ++operand a4_RegN() %{ ++ constraint(ALLOC_IN_RC(a4_reg)); ++ match(RegN); ++ match(mRegN); ++ ++ format %{ %} ++ interface(REG_INTER); ++%} ++ ++operand a5_RegN() %{ ++ constraint(ALLOC_IN_RC(a5_reg)); ++ match(RegN); ++ match(mRegN); ++ ++ format %{ %} ++ interface(REG_INTER); ++%} ++ ++operand a6_RegN() %{ ++ constraint(ALLOC_IN_RC(a6_reg)); ++ match(RegN); ++ match(mRegN); ++ ++ format %{ %} ++ interface(REG_INTER); ++%} ++ ++operand a7_RegN() %{ ++ constraint(ALLOC_IN_RC(a7_reg)); ++ match(RegN); ++ match(mRegN); ++ ++ format %{ %} ++ interface(REG_INTER); ++%} ++ ++operand s0_RegN() %{ ++ constraint(ALLOC_IN_RC(s0_reg)); ++ match(RegN); ++ match(mRegN); ++ ++ format %{ %} ++ interface(REG_INTER); ++%} ++ ++operand s1_RegN() %{ ++ constraint(ALLOC_IN_RC(s1_reg)); ++ match(RegN); ++ match(mRegN); ++ ++ format %{ %} ++ interface(REG_INTER); ++%} ++ ++operand s2_RegN() %{ ++ constraint(ALLOC_IN_RC(s2_reg)); ++ match(RegN); ++ match(mRegN); ++ ++ format %{ %} ++ interface(REG_INTER); ++%} ++ ++operand s3_RegN() %{ ++ constraint(ALLOC_IN_RC(s3_reg)); ++ match(RegN); ++ match(mRegN); ++ ++ format %{ %} ++ interface(REG_INTER); ++%} ++ ++operand s4_RegN() %{ ++ constraint(ALLOC_IN_RC(s4_reg)); ++ match(RegN); ++ match(mRegN); ++ ++ format %{ %} ++ interface(REG_INTER); ++%} ++ ++operand s5_RegN() %{ ++ constraint(ALLOC_IN_RC(s5_reg)); ++ match(RegN); ++ match(mRegN); ++ ++ format %{ %} ++ interface(REG_INTER); ++%} ++ ++operand s6_RegN() %{ ++ constraint(ALLOC_IN_RC(s6_reg)); ++ match(RegN); ++ match(mRegN); ++ ++ format %{ %} ++ interface(REG_INTER); ++%} ++ ++operand s7_RegN() %{ ++ constraint(ALLOC_IN_RC(s7_reg)); ++ match(RegN); ++ match(mRegN); ++ ++ format %{ %} ++ interface(REG_INTER); ++%} ++ ++// Pointer Register ++operand mRegP() %{ ++ constraint(ALLOC_IN_RC(p_reg)); ++ match(RegP); ++ match(a0_RegP); ++ ++ format %{ %} ++ interface(REG_INTER); ++%} ++ ++operand no_T8_mRegP() %{ ++ constraint(ALLOC_IN_RC(no_T8_p_reg)); ++ match(RegP); ++ match(mRegP); ++ ++ format %{ %} ++ interface(REG_INTER); ++%} ++ ++operand no_Ax_mRegP() %{ ++ constraint(ALLOC_IN_RC(no_Ax_p_reg)); ++ match(RegP); ++ match(mRegP); ++ ++ format %{ %} ++ interface(REG_INTER); ++%} ++ ++operand s1_RegP() ++%{ ++ constraint(ALLOC_IN_RC(s1_long_reg)); ++ match(RegP); ++ match(mRegP); ++ match(no_T8_mRegP); ++ ++ format %{ %} ++ interface(REG_INTER); ++%} ++ ++operand s3_RegP() ++%{ ++ constraint(ALLOC_IN_RC(s3_long_reg)); ++ match(RegP); ++ match(mRegP); ++ match(no_T8_mRegP); ++ ++ format %{ %} ++ interface(REG_INTER); ++%} ++ ++operand s4_RegP() ++%{ ++ constraint(ALLOC_IN_RC(s4_long_reg)); ++ match(RegP); ++ match(mRegP); ++ match(no_T8_mRegP); ++ ++ format %{ %} ++ interface(REG_INTER); ++%} ++ ++operand s5_RegP() ++%{ ++ constraint(ALLOC_IN_RC(s5_long_reg)); ++ match(RegP); ++ match(mRegP); ++ match(no_T8_mRegP); ++ ++ format %{ %} ++ interface(REG_INTER); ++%} ++ ++operand s6_RegP() ++%{ ++ constraint(ALLOC_IN_RC(s6_long_reg)); ++ match(RegP); ++ match(mRegP); ++ match(no_T8_mRegP); ++ ++ format %{ %} ++ interface(REG_INTER); ++%} ++ ++operand s7_RegP() ++%{ ++ constraint(ALLOC_IN_RC(s7_long_reg)); ++ match(RegP); ++ match(mRegP); ++ match(no_T8_mRegP); ++ ++ format %{ %} ++ interface(REG_INTER); ++%} ++ ++operand t0_RegP() ++%{ ++ constraint(ALLOC_IN_RC(t0_long_reg)); ++ match(RegP); ++ match(mRegP); ++ match(no_T8_mRegP); ++ ++ format %{ %} ++ interface(REG_INTER); ++%} ++ ++operand t1_RegP() ++%{ ++ constraint(ALLOC_IN_RC(t1_long_reg)); ++ match(RegP); ++ match(mRegP); ++ match(no_T8_mRegP); ++ ++ format %{ %} ++ interface(REG_INTER); ++%} ++ ++operand t2_RegP() ++%{ ++ constraint(ALLOC_IN_RC(t2_long_reg)); ++ match(RegP); ++ match(mRegP); ++ match(no_T8_mRegP); ++ ++ format %{ %} ++ interface(REG_INTER); ++%} ++ ++operand t3_RegP() ++%{ ++ constraint(ALLOC_IN_RC(t3_long_reg)); ++ match(RegP); ++ match(mRegP); ++ match(no_T8_mRegP); ++ ++ format %{ %} ++ interface(REG_INTER); ++%} ++ ++operand t8_RegP() ++%{ ++ constraint(ALLOC_IN_RC(t8_long_reg)); ++ match(RegP); ++ match(mRegP); ++ ++ format %{ %} ++ interface(REG_INTER); ++%} ++ ++operand a0_RegP() ++%{ ++ constraint(ALLOC_IN_RC(a0_long_reg)); ++ match(RegP); ++ match(mRegP); ++ match(no_T8_mRegP); ++ ++ format %{ %} ++ interface(REG_INTER); ++%} ++ ++operand a1_RegP() ++%{ ++ constraint(ALLOC_IN_RC(a1_long_reg)); ++ match(RegP); ++ match(mRegP); ++ match(no_T8_mRegP); ++ ++ format %{ %} ++ interface(REG_INTER); ++%} ++ ++operand a2_RegP() ++%{ ++ constraint(ALLOC_IN_RC(a2_long_reg)); ++ match(RegP); ++ match(mRegP); ++ match(no_T8_mRegP); ++ ++ format %{ %} ++ interface(REG_INTER); ++%} ++ ++operand a3_RegP() ++%{ ++ constraint(ALLOC_IN_RC(a3_long_reg)); ++ match(RegP); ++ match(mRegP); ++ match(no_T8_mRegP); ++ ++ format %{ %} ++ interface(REG_INTER); ++%} ++ ++operand a4_RegP() ++%{ ++ constraint(ALLOC_IN_RC(a4_long_reg)); ++ match(RegP); ++ match(mRegP); ++ match(no_T8_mRegP); ++ ++ format %{ %} ++ interface(REG_INTER); ++%} ++ ++ ++operand a5_RegP() ++%{ ++ constraint(ALLOC_IN_RC(a5_long_reg)); ++ match(RegP); ++ match(mRegP); ++ match(no_T8_mRegP); ++ ++ format %{ %} ++ interface(REG_INTER); ++%} ++ ++operand a6_RegP() ++%{ ++ constraint(ALLOC_IN_RC(a6_long_reg)); ++ match(RegP); ++ match(mRegP); ++ match(no_T8_mRegP); ++ ++ format %{ %} ++ interface(REG_INTER); ++%} ++ ++operand a7_RegP() ++%{ ++ constraint(ALLOC_IN_RC(a7_long_reg)); ++ match(RegP); ++ match(mRegP); ++ match(no_T8_mRegP); ++ ++ format %{ %} ++ interface(REG_INTER); ++%} ++ ++operand v0_RegP() ++%{ ++ constraint(ALLOC_IN_RC(v0_long_reg)); ++ match(RegP); ++ match(mRegP); ++ match(no_T8_mRegP); ++ ++ format %{ %} ++ interface(REG_INTER); ++%} ++ ++operand v1_RegP() ++%{ ++ constraint(ALLOC_IN_RC(v1_long_reg)); ++ match(RegP); ++ match(mRegP); ++ match(no_T8_mRegP); ++ ++ format %{ %} ++ interface(REG_INTER); ++%} ++ ++operand mRegL() %{ ++ constraint(ALLOC_IN_RC(long_reg)); ++ match(RegL); ++ ++ format %{ %} ++ interface(REG_INTER); ++%} ++ ++operand mRegI2L(mRegI reg) %{ ++ match(ConvI2L reg); ++ ++ format %{ %} ++ interface(REG_INTER); ++%} ++ ++operand v0RegL() %{ ++ constraint(ALLOC_IN_RC(v0_long_reg)); ++ match(RegL); ++ match(mRegL); ++ ++ format %{ %} ++ interface(REG_INTER); ++%} ++ ++operand v1RegL() %{ ++ constraint(ALLOC_IN_RC(v1_long_reg)); ++ match(RegL); ++ match(mRegL); ++ ++ format %{ %} ++ interface(REG_INTER); ++%} ++ ++operand a0RegL() %{ ++ constraint(ALLOC_IN_RC(a0_long_reg)); ++ match(RegL); ++ match(mRegL); ++ ++ format %{ "A0" %} ++ interface(REG_INTER); ++%} ++ ++operand a1RegL() %{ ++ constraint(ALLOC_IN_RC(a1_long_reg)); ++ match(RegL); ++ match(mRegL); ++ ++ format %{ %} ++ interface(REG_INTER); ++%} ++ ++operand a2RegL() %{ ++ constraint(ALLOC_IN_RC(a2_long_reg)); ++ match(RegL); ++ match(mRegL); ++ ++ format %{ %} ++ interface(REG_INTER); ++%} ++ ++operand a3RegL() %{ ++ constraint(ALLOC_IN_RC(a3_long_reg)); ++ match(RegL); ++ match(mRegL); ++ ++ format %{ %} ++ interface(REG_INTER); ++%} ++ ++operand t0RegL() %{ ++ constraint(ALLOC_IN_RC(t0_long_reg)); ++ match(RegL); ++ match(mRegL); ++ ++ format %{ %} ++ interface(REG_INTER); ++%} ++ ++operand t1RegL() %{ ++ constraint(ALLOC_IN_RC(t1_long_reg)); ++ match(RegL); ++ match(mRegL); ++ ++ format %{ %} ++ interface(REG_INTER); ++%} ++ ++operand t3RegL() %{ ++ constraint(ALLOC_IN_RC(t3_long_reg)); ++ match(RegL); ++ match(mRegL); ++ ++ format %{ %} ++ interface(REG_INTER); ++%} ++ ++operand t8RegL() %{ ++ constraint(ALLOC_IN_RC(t8_long_reg)); ++ match(RegL); ++ match(mRegL); ++ ++ format %{ %} ++ interface(REG_INTER); ++%} ++ ++operand a4RegL() %{ ++ constraint(ALLOC_IN_RC(a4_long_reg)); ++ match(RegL); ++ match(mRegL); ++ ++ format %{ %} ++ interface(REG_INTER); ++%} ++ ++operand a5RegL() %{ ++ constraint(ALLOC_IN_RC(a5_long_reg)); ++ match(RegL); ++ match(mRegL); ++ ++ format %{ %} ++ interface(REG_INTER); ++%} ++ ++operand a6RegL() %{ ++ constraint(ALLOC_IN_RC(a6_long_reg)); ++ match(RegL); ++ match(mRegL); ++ ++ format %{ %} ++ interface(REG_INTER); ++%} ++ ++operand a7RegL() %{ ++ constraint(ALLOC_IN_RC(a7_long_reg)); ++ match(RegL); ++ match(mRegL); ++ ++ format %{ %} ++ interface(REG_INTER); ++%} ++ ++operand s0RegL() %{ ++ constraint(ALLOC_IN_RC(s0_long_reg)); ++ match(RegL); ++ match(mRegL); ++ ++ format %{ %} ++ interface(REG_INTER); ++%} ++ ++operand s1RegL() %{ ++ constraint(ALLOC_IN_RC(s1_long_reg)); ++ match(RegL); ++ match(mRegL); ++ ++ format %{ %} ++ interface(REG_INTER); ++%} ++ ++operand s3RegL() %{ ++ constraint(ALLOC_IN_RC(s3_long_reg)); ++ match(RegL); ++ match(mRegL); ++ ++ format %{ %} ++ interface(REG_INTER); ++%} ++ ++operand s4RegL() %{ ++ constraint(ALLOC_IN_RC(s4_long_reg)); ++ match(RegL); ++ match(mRegL); ++ ++ format %{ %} ++ interface(REG_INTER); ++%} ++ ++operand s7RegL() %{ ++ constraint(ALLOC_IN_RC(s7_long_reg)); ++ match(RegL); ++ match(mRegL); ++ ++ format %{ %} ++ interface(REG_INTER); ++%} ++ ++// Floating register operands ++operand regF() %{ ++ constraint(ALLOC_IN_RC(flt_reg)); ++ match(RegF); ++ ++ format %{ %} ++ interface(REG_INTER); ++%} ++ ++//Double Precision Floating register operands ++operand regD() %{ ++ constraint(ALLOC_IN_RC(dbl_reg)); ++ match(RegD); ++ ++ format %{ %} ++ interface(REG_INTER); ++%} ++ ++//----------Memory Operands---------------------------------------------------- ++// Indirect Memory Operand ++operand indirect(mRegP reg) %{ ++ constraint(ALLOC_IN_RC(p_reg)); ++ match(reg); ++ ++ format %{ "[$reg] @ indirect" %} ++ interface(MEMORY_INTER) %{ ++ base($reg); ++ index(0x0); /* NO_INDEX */ ++ scale(0x0); ++ disp(0x0); ++ %} ++%} ++ ++// Indirect Memory Plus Short Offset Operand ++operand indOffset12(mRegP reg, immL12 off) ++%{ ++ constraint(ALLOC_IN_RC(p_reg)); ++ match(AddP reg off); ++ ++ op_cost(10); ++ format %{ "[$reg + $off (12-bit)] @ indOffset12" %} ++ interface(MEMORY_INTER) %{ ++ base($reg); ++ index(0x0); /* NO_INDEX */ ++ scale(0x0); ++ disp($off); ++ %} ++%} ++ ++operand indOffset12I2L(mRegP reg, immI12 off) ++%{ ++ constraint(ALLOC_IN_RC(p_reg)); ++ match(AddP reg (ConvI2L off)); ++ ++ op_cost(10); ++ format %{ "[$reg + $off (12-bit)] @ indOffset12I2L" %} ++ interface(MEMORY_INTER) %{ ++ base($reg); ++ index(0x0); /* NO_INDEX */ ++ scale(0x0); ++ disp($off); ++ %} ++%} ++ ++// Indirect Memory Plus Index Register ++operand indIndex(mRegP addr, mRegL index) %{ ++ constraint(ALLOC_IN_RC(p_reg)); ++ match(AddP addr index); ++ ++ op_cost(20); ++ format %{"[$addr + $index] @ indIndex" %} ++ interface(MEMORY_INTER) %{ ++ base($addr); ++ index($index); ++ scale(0x0); ++ disp(0x0); ++ %} ++%} ++ ++operand indIndexI2L(mRegP reg, mRegI ireg) ++%{ ++ constraint(ALLOC_IN_RC(ptr_reg)); ++ match(AddP reg (ConvI2L ireg)); ++ op_cost(10); ++ format %{ "[$reg + $ireg] @ indIndexI2L" %} ++ interface(MEMORY_INTER) %{ ++ base($reg); ++ index($ireg); ++ scale(0x0); ++ disp(0x0); ++ %} ++%} ++ ++// Indirect Memory Operand ++operand indirectNarrow(mRegN reg) ++%{ ++ predicate(Universe::narrow_oop_shift() == 0); ++ constraint(ALLOC_IN_RC(p_reg)); ++ op_cost(10); ++ match(DecodeN reg); ++ ++ format %{ "[$reg] @ indirectNarrow" %} ++ interface(MEMORY_INTER) %{ ++ base($reg); ++ index(0x0); ++ scale(0x0); ++ disp(0x0); ++ %} ++%} ++ ++// Indirect Memory Plus Short Offset Operand ++operand indOffset12Narrow(mRegN reg, immL12 off) ++%{ ++ predicate(Universe::narrow_oop_shift() == 0); ++ constraint(ALLOC_IN_RC(p_reg)); ++ op_cost(10); ++ match(AddP (DecodeN reg) off); ++ ++ format %{ "[$reg + $off (12-bit)] @ indOffset12Narrow" %} ++ interface(MEMORY_INTER) %{ ++ base($reg); ++ index(0x0); ++ scale(0x0); ++ disp($off); ++ %} ++%} ++ ++//----------Conditional Branch Operands---------------------------------------- ++// Comparison Op - This is the operation of the comparison, and is limited to ++// the following set of codes: ++// L (<), LE (<=), G (>), GE (>=), E (==), NE (!=) ++// ++// Other attributes of the comparison, such as unsignedness, are specified ++// by the comparison instruction that sets a condition code flags register. ++// That result is represented by a flags operand whose subtype is appropriate ++// to the unsignedness (etc.) of the comparison. ++// ++// Later, the instruction which matches both the Comparison Op (a Bool) and ++// the flags (produced by the Cmp) specifies the coding of the comparison op ++// by matching a specific subtype of Bool operand below, such as cmpOpU. ++ ++// Comparision Code ++operand cmpOp() %{ ++ match(Bool); ++ ++ format %{ "" %} ++ interface(COND_INTER) %{ ++ equal(0x01); ++ not_equal(0x02); ++ greater(0x03); ++ greater_equal(0x04); ++ less(0x05); ++ less_equal(0x06); ++ overflow(0x7); ++ no_overflow(0x8); ++ %} ++%} ++ ++ ++// Comparision Code ++// Comparison Code, unsigned compare. Used by FP also, with ++// C2 (unordered) turned into GT or LT already. The other bits ++// C0 and C3 are turned into Carry & Zero flags. ++operand cmpOpU() %{ ++ match(Bool); ++ ++ format %{ "" %} ++ interface(COND_INTER) %{ ++ equal(0x01); ++ not_equal(0x02); ++ greater(0x03); ++ greater_equal(0x04); ++ less(0x05); ++ less_equal(0x06); ++ overflow(0x7); ++ no_overflow(0x8); ++ %} ++%} ++ ++ ++//----------Special Memory Operands-------------------------------------------- ++// Stack Slot Operand - This operand is used for loading and storing temporary ++// values on the stack where a match requires a value to ++// flow through memory. ++operand stackSlotP(sRegP reg) %{ ++ constraint(ALLOC_IN_RC(stack_slots)); ++ // No match rule because this operand is only generated in matching ++ op_cost(50); ++ format %{ "[$reg]" %} ++ interface(MEMORY_INTER) %{ ++ base(0x1d); // SP ++ index(0x0); // No Index ++ scale(0x0); // No Scale ++ disp($reg); // Stack Offset ++ %} ++%} ++ ++operand stackSlotI(sRegI reg) %{ ++ constraint(ALLOC_IN_RC(stack_slots)); ++ // No match rule because this operand is only generated in matching ++ op_cost(50); ++ format %{ "[$reg]" %} ++ interface(MEMORY_INTER) %{ ++ base(0x1d); // SP ++ index(0x0); // No Index ++ scale(0x0); // No Scale ++ disp($reg); // Stack Offset ++ %} ++%} ++ ++operand stackSlotF(sRegF reg) %{ ++ constraint(ALLOC_IN_RC(stack_slots)); ++ // No match rule because this operand is only generated in matching ++ op_cost(50); ++ format %{ "[$reg]" %} ++ interface(MEMORY_INTER) %{ ++ base(0x1d); // SP ++ index(0x0); // No Index ++ scale(0x0); // No Scale ++ disp($reg); // Stack Offset ++ %} ++%} ++ ++operand stackSlotD(sRegD reg) %{ ++ constraint(ALLOC_IN_RC(stack_slots)); ++ // No match rule because this operand is only generated in matching ++ op_cost(50); ++ format %{ "[$reg]" %} ++ interface(MEMORY_INTER) %{ ++ base(0x1d); // SP ++ index(0x0); // No Index ++ scale(0x0); // No Scale ++ disp($reg); // Stack Offset ++ %} ++%} ++ ++operand stackSlotL(sRegL reg) %{ ++ constraint(ALLOC_IN_RC(stack_slots)); ++ // No match rule because this operand is only generated in matching ++ op_cost(50); ++ format %{ "[$reg]" %} ++ interface(MEMORY_INTER) %{ ++ base(0x1d); // SP ++ index(0x0); // No Index ++ scale(0x0); // No Scale ++ disp($reg); // Stack Offset ++ %} ++%} ++ ++ ++//------------------------OPERAND CLASSES-------------------------------------- ++opclass memory( indirect, indOffset12, indOffset12I2L, indIndex, indIndexI2L, ++ indirectNarrow, indOffset12Narrow); ++opclass memory_loadRange(indOffset12, indirect); ++ ++opclass mRegLorI2L(mRegI2L, mRegL); ++//----------PIPELINE----------------------------------------------------------- ++// Rules which define the behavior of the target architectures pipeline. ++ ++pipeline %{ ++ ++ //----------ATTRIBUTES--------------------------------------------------------- ++ attributes %{ ++ fixed_size_instructions; // Fixed size instructions ++ max_instructions_per_bundle = 1; // 1 instruction per bundle ++ max_bundles_per_cycle = 4; // Up to 4 bundles per cycle ++ bundle_unit_size=4; ++ instruction_unit_size = 4; // An instruction is 4 bytes long ++ instruction_fetch_unit_size = 16; // The processor fetches one line ++ instruction_fetch_units = 1; // of 16 bytes ++ ++ // List of nop instructions ++ nops( MachNop ); ++ %} ++ ++ //----------RESOURCES---------------------------------------------------------- ++ // Resources are the functional units available to the machine ++ ++ resources(D1, D2, D3, D4, DECODE = D1 | D2 | D3| D4, ALU1, ALU2, ALU = ALU1 | ALU2, FPU1, FPU2, FPU = FPU1 | FPU2, MEM, BR); ++ ++ //----------PIPELINE DESCRIPTION----------------------------------------------- ++ // Pipeline Description specifies the stages in the machine's pipeline ++ ++ // IF: fetch ++ // ID: decode ++ // RD: read ++ // CA: caculate ++ // WB: write back ++ // CM: commit ++ ++ pipe_desc(IF, ID, RD, CA, WB, CM); ++ ++ ++ //----------PIPELINE CLASSES--------------------------------------------------- ++ // Pipeline Classes describe the stages in which input and output are ++ // referenced by the hardware pipeline. ++ ++ //No.1 Integer ALU reg-reg operation : dst <-- reg1 op reg2 ++ pipe_class ialu_regI_regI(mRegI dst, mRegI src1, mRegI src2) %{ ++ single_instruction; ++ src1 : RD(read); ++ src2 : RD(read); ++ dst : WB(write)+1; ++ DECODE : ID; ++ ALU : CA; ++ %} ++ ++ //No.19 Integer mult operation : dst <-- reg1 mult reg2 ++ pipe_class ialu_mult(mRegI dst, mRegI src1, mRegI src2) %{ ++ src1 : RD(read); ++ src2 : RD(read); ++ dst : WB(write)+5; ++ DECODE : ID; ++ ALU2 : CA; ++ %} ++ ++ pipe_class mulL_reg_reg(mRegL dst, mRegL src1, mRegL src2) %{ ++ src1 : RD(read); ++ src2 : RD(read); ++ dst : WB(write)+10; ++ DECODE : ID; ++ ALU2 : CA; ++ %} ++ ++ //No.19 Integer div operation : dst <-- reg1 div reg2 ++ pipe_class ialu_div(mRegI dst, mRegI src1, mRegI src2) %{ ++ src1 : RD(read); ++ src2 : RD(read); ++ dst : WB(write)+10; ++ DECODE : ID; ++ ALU2 : CA; ++ %} ++ ++ //No.19 Integer mod operation : dst <-- reg1 mod reg2 ++ pipe_class ialu_mod(mRegI dst, mRegI src1, mRegI src2) %{ ++ instruction_count(2); ++ src1 : RD(read); ++ src2 : RD(read); ++ dst : WB(write)+10; ++ DECODE : ID; ++ ALU2 : CA; ++ %} ++ ++ //No.15 Long ALU reg-reg operation : dst <-- reg1 op reg2 ++ pipe_class ialu_regL_regL(mRegL dst, mRegL src1, mRegL src2) %{ ++ instruction_count(2); ++ src1 : RD(read); ++ src2 : RD(read); ++ dst : WB(write); ++ DECODE : ID; ++ ALU : CA; ++ %} ++ ++ //No.18 Long ALU reg-imm16 operation : dst <-- reg1 op imm16 ++ pipe_class ialu_regL_imm16(mRegL dst, mRegL src) %{ ++ instruction_count(2); ++ src : RD(read); ++ dst : WB(write); ++ DECODE : ID; ++ ALU : CA; ++ %} ++ ++ //no.16 load Long from memory : ++ pipe_class ialu_loadL(mRegL dst, memory mem) %{ ++ instruction_count(2); ++ mem : RD(read); ++ dst : WB(write)+5; ++ DECODE : ID; ++ MEM : RD; ++ %} ++ ++ //No.17 Store Long to Memory : ++ pipe_class ialu_storeL(mRegL src, memory mem) %{ ++ instruction_count(2); ++ mem : RD(read); ++ src : RD(read); ++ DECODE : ID; ++ MEM : RD; ++ %} ++ ++ //No.2 Integer ALU reg-imm16 operation : dst <-- reg1 op imm16 ++ pipe_class ialu_regI_imm16(mRegI dst, mRegI src) %{ ++ single_instruction; ++ src : RD(read); ++ dst : WB(write); ++ DECODE : ID; ++ ALU : CA; ++ %} ++ ++ //No.3 Integer move operation : dst <-- reg ++ pipe_class ialu_regI_mov(mRegI dst, mRegI src) %{ ++ src : RD(read); ++ dst : WB(write); ++ DECODE : ID; ++ ALU : CA; ++ %} ++ ++ //No.4 No instructions : do nothing ++ pipe_class empty( ) %{ ++ instruction_count(0); ++ %} ++ ++ //No.5 UnConditional branch : ++ pipe_class pipe_jump( label labl ) %{ ++ multiple_bundles; ++ DECODE : ID; ++ BR : RD; ++ %} ++ ++ //No.6 ALU Conditional branch : ++ pipe_class pipe_alu_branch(mRegI src1, mRegI src2, label labl ) %{ ++ multiple_bundles; ++ src1 : RD(read); ++ src2 : RD(read); ++ DECODE : ID; ++ BR : RD; ++ %} ++ ++ //no.7 load integer from memory : ++ pipe_class ialu_loadI(mRegI dst, memory mem) %{ ++ mem : RD(read); ++ dst : WB(write)+3; ++ DECODE : ID; ++ MEM : RD; ++ %} ++ ++ //No.8 Store Integer to Memory : ++ pipe_class ialu_storeI(mRegI src, memory mem) %{ ++ mem : RD(read); ++ src : RD(read); ++ DECODE : ID; ++ MEM : RD; ++ %} ++ ++ ++ //No.10 Floating FPU reg-reg operation : dst <-- reg1 op reg2 ++ pipe_class fpu_regF_regF(regF dst, regF src1, regF src2) %{ ++ src1 : RD(read); ++ src2 : RD(read); ++ dst : WB(write); ++ DECODE : ID; ++ FPU : CA; ++ %} ++ ++ //No.22 Floating div operation : dst <-- reg1 div reg2 ++ pipe_class fpu_div(regF dst, regF src1, regF src2) %{ ++ src1 : RD(read); ++ src2 : RD(read); ++ dst : WB(write); ++ DECODE : ID; ++ FPU2 : CA; ++ %} ++ ++ pipe_class fcvt_I2D(regD dst, mRegI src) %{ ++ src : RD(read); ++ dst : WB(write); ++ DECODE : ID; ++ FPU1 : CA; ++ %} ++ ++ pipe_class fcvt_D2I(mRegI dst, regD src) %{ ++ src : RD(read); ++ dst : WB(write); ++ DECODE : ID; ++ FPU1 : CA; ++ %} ++ ++ pipe_class pipe_mfc1(mRegI dst, regD src) %{ ++ src : RD(read); ++ dst : WB(write); ++ DECODE : ID; ++ MEM : RD; ++ %} ++ ++ pipe_class pipe_mtc1(regD dst, mRegI src) %{ ++ src : RD(read); ++ dst : WB(write); ++ DECODE : ID; ++ MEM : RD(5); ++ %} ++ ++ //No.23 Floating sqrt operation : dst <-- reg1 sqrt reg2 ++ pipe_class fpu_sqrt(regF dst, regF src1, regF src2) %{ ++ multiple_bundles; ++ src1 : RD(read); ++ src2 : RD(read); ++ dst : WB(write); ++ DECODE : ID; ++ FPU2 : CA; ++ %} ++ ++ //No.11 Load Floating from Memory : ++ pipe_class fpu_loadF(regF dst, memory mem) %{ ++ instruction_count(1); ++ mem : RD(read); ++ dst : WB(write)+3; ++ DECODE : ID; ++ MEM : RD; ++ %} ++ ++ //No.12 Store Floating to Memory : ++ pipe_class fpu_storeF(regF src, memory mem) %{ ++ instruction_count(1); ++ mem : RD(read); ++ src : RD(read); ++ DECODE : ID; ++ MEM : RD; ++ %} ++ ++ //No.13 FPU Conditional branch : ++ pipe_class pipe_fpu_branch(regF src1, regF src2, label labl ) %{ ++ multiple_bundles; ++ src1 : RD(read); ++ src2 : RD(read); ++ DECODE : ID; ++ BR : RD; ++ %} ++ ++//No.14 Floating FPU reg operation : dst <-- op reg ++ pipe_class fpu1_regF(regF dst, regF src) %{ ++ src : RD(read); ++ dst : WB(write); ++ DECODE : ID; ++ FPU : CA; ++ %} ++ ++ pipe_class long_memory_op() %{ ++ instruction_count(10); multiple_bundles; force_serialization; ++ fixed_latency(30); ++ %} ++ ++ pipe_class simple_call() %{ ++ instruction_count(10); multiple_bundles; force_serialization; ++ fixed_latency(200); ++ BR : RD; ++ %} ++ ++ pipe_class call() %{ ++ instruction_count(10); multiple_bundles; force_serialization; ++ fixed_latency(200); ++ %} ++ ++ //FIXME: ++ //No.9 Piple slow : for multi-instructions ++ pipe_class pipe_slow( ) %{ ++ instruction_count(20); ++ force_serialization; ++ multiple_bundles; ++ fixed_latency(50); ++ %} ++ ++%} ++ ++ ++ ++//----------INSTRUCTIONS------------------------------------------------------- ++// ++// match -- States which machine-independent subtree may be replaced ++// by this instruction. ++// ins_cost -- The estimated cost of this instruction is used by instruction ++// selection to identify a minimum cost tree of machine ++// instructions that matches a tree of machine-independent ++// instructions. ++// format -- A string providing the disassembly for this instruction. ++// The value of an instruction's operand may be inserted ++// by referring to it with a '$' prefix. ++// opcode -- Three instruction opcodes may be provided. These are referred ++// to within an encode class as $primary, $secondary, and $tertiary ++// respectively. The primary opcode is commonly used to ++// indicate the type of machine instruction, while secondary ++// and tertiary are often used for prefix options or addressing ++// modes. ++// ins_encode -- A list of encode classes with parameters. The encode class ++// name must have been defined in an 'enc_class' specification ++// in the encode section of the architecture description. ++ ++ ++// Load Integer ++instruct loadI(mRegI dst, memory mem) %{ ++ match(Set dst (LoadI mem)); ++ ++ ins_cost(125); ++ format %{ "ld_w $dst, $mem #@loadI" %} ++ ins_encode %{ ++ __ loadstore_enc($dst$$Register, $mem$$base, $mem$$index, $mem$$scale, $mem$$disp, MacroAssembler::LOAD_INT); ++ %} ++ ins_pipe( ialu_loadI ); ++%} ++ ++instruct loadI_convI2L(mRegL dst, memory mem) %{ ++ match(Set dst (ConvI2L (LoadI mem))); ++ ++ ins_cost(125); ++ format %{ "ld_w $dst, $mem #@loadI_convI2L" %} ++ ins_encode %{ ++ __ loadstore_enc($dst$$Register, $mem$$base, $mem$$index, $mem$$scale, $mem$$disp, MacroAssembler::LOAD_INT); ++ %} ++ ins_pipe( ialu_loadI ); ++%} ++ ++// Load Integer (32 bit signed) to Byte (8 bit signed) ++instruct loadI2B(mRegI dst, memory mem, immI_24 twentyfour) %{ ++ match(Set dst (RShiftI (LShiftI (LoadI mem) twentyfour) twentyfour)); ++ ++ ins_cost(125); ++ format %{ "ld_b $dst, $mem\t# int -> byte #@loadI2B" %} ++ ins_encode %{ ++ __ loadstore_enc($dst$$Register, $mem$$base, $mem$$index, $mem$$scale, $mem$$disp, MacroAssembler::LOAD_BYTE); ++ %} ++ ins_pipe(ialu_loadI); ++%} ++ ++// Load Integer (32 bit signed) to Unsigned Byte (8 bit UNsigned) ++instruct loadI2UB(mRegI dst, memory mem, immI_255 mask) %{ ++ match(Set dst (AndI (LoadI mem) mask)); ++ ++ ins_cost(125); ++ format %{ "ld_bu $dst, $mem\t# int -> ubyte #@loadI2UB" %} ++ ins_encode %{ ++ __ loadstore_enc($dst$$Register, $mem$$base, $mem$$index, $mem$$scale, $mem$$disp, MacroAssembler::LOAD_U_BYTE); ++ %} ++ ins_pipe(ialu_loadI); ++%} ++ ++// Load Integer (32 bit signed) to Short (16 bit signed) ++instruct loadI2S(mRegI dst, memory mem, immI_16 sixteen) %{ ++ match(Set dst (RShiftI (LShiftI (LoadI mem) sixteen) sixteen)); ++ ++ ins_cost(125); ++ format %{ "ld_h $dst, $mem\t# int -> short #@loadI2S" %} ++ ins_encode %{ ++ __ loadstore_enc($dst$$Register, $mem$$base, $mem$$index, $mem$$scale, $mem$$disp, MacroAssembler::LOAD_SHORT); ++ %} ++ ins_pipe(ialu_loadI); ++%} ++ ++// Load Integer (32 bit signed) to Unsigned Short/Char (16 bit UNsigned) ++instruct loadI2US(mRegI dst, memory mem, immI_65535 mask) %{ ++ match(Set dst (AndI (LoadI mem) mask)); ++ ++ ins_cost(125); ++ format %{ "ld_hu $dst, $mem\t# int -> ushort/char #@loadI2US" %} ++ ins_encode %{ ++ __ loadstore_enc($dst$$Register, $mem$$base, $mem$$index, $mem$$scale, $mem$$disp, MacroAssembler::LOAD_U_SHORT); ++ %} ++ ins_pipe(ialu_loadI); ++%} ++ ++// Load Long. ++instruct loadL(mRegL dst, memory mem) %{ ++// predicate(!((LoadLNode*)n)->require_atomic_access()); ++ match(Set dst (LoadL mem)); ++ ++ ins_cost(250); ++ format %{ "ld_d $dst, $mem #@loadL" %} ++ ins_encode %{ ++ __ loadstore_enc($dst$$Register, $mem$$base, $mem$$index, $mem$$scale, $mem$$disp, MacroAssembler::LOAD_LONG); ++ %} ++ ins_pipe( ialu_loadL ); ++%} ++ ++// Load Long - UNaligned ++instruct loadL_unaligned(mRegL dst, memory mem) %{ ++ match(Set dst (LoadL_unaligned mem)); ++ ++ // FIXME: Need more effective ldl/ldr ++ ins_cost(450); ++ format %{ "ld_d $dst, $mem #@loadL_unaligned\n\t" %} ++ ins_encode %{ ++ __ loadstore_enc($dst$$Register, $mem$$base, $mem$$index, $mem$$scale, $mem$$disp, MacroAssembler::LOAD_LONG); ++ %} ++ ins_pipe( ialu_loadL ); ++%} ++ ++// Store Long ++instruct storeL_reg(memory mem, mRegL src) %{ ++ match(Set mem (StoreL mem src)); ++ ++ ins_cost(200); ++ format %{ "st_d $mem, $src #@storeL_reg\n" %} ++ ins_encode %{ ++ __ loadstore_enc($src$$Register, $mem$$base, $mem$$index, $mem$$scale, $mem$$disp, MacroAssembler::STORE_LONG); ++ %} ++ ins_pipe( ialu_storeL ); ++%} ++ ++instruct storeL_immL_0(memory mem, immL_0 zero) %{ ++ match(Set mem (StoreL mem zero)); ++ ++ ins_cost(180); ++ format %{ "st_d zero, $mem #@storeL_immL_0" %} ++ ins_encode %{ ++ __ loadstore_enc(R0, $mem$$base, $mem$$index, $mem$$scale, $mem$$disp, MacroAssembler::STORE_LONG); ++ %} ++ ins_pipe( ialu_storeL ); ++%} ++ ++// Load Compressed Pointer ++instruct loadN(mRegN dst, memory mem) ++%{ ++ match(Set dst (LoadN mem)); ++ ++ ins_cost(125); // XXX ++ format %{ "ld_wu $dst, $mem\t# compressed ptr @ loadN" %} ++ ins_encode %{ ++ relocInfo::relocType disp_reloc = $mem->disp_reloc(); ++ assert(disp_reloc == relocInfo::none, "cannot have disp"); ++ __ loadstore_enc($dst$$Register, $mem$$base, $mem$$index, $mem$$scale, $mem$$disp, MacroAssembler::LOAD_U_INT); ++ %} ++ ins_pipe( ialu_loadI ); // XXX ++%} ++ ++instruct loadN2P(mRegP dst, memory mem) ++%{ ++ match(Set dst (DecodeN (LoadN mem))); ++ predicate(Universe::narrow_oop_base() == NULL && Universe::narrow_oop_shift() == 0); ++ ++ ins_cost(125); // XXX ++ format %{ "ld_wu $dst, $mem\t# @ loadN2P" %} ++ ins_encode %{ ++ relocInfo::relocType disp_reloc = $mem->disp_reloc(); ++ assert(disp_reloc == relocInfo::none, "cannot have disp"); ++ __ loadstore_enc($dst$$Register, $mem$$base, $mem$$index, $mem$$scale, $mem$$disp, MacroAssembler::LOAD_U_INT); ++ %} ++ ins_pipe( ialu_loadI ); // XXX ++%} ++ ++// Load Pointer ++instruct loadP(mRegP dst, memory mem) %{ ++ match(Set dst (LoadP mem)); ++ ++ ins_cost(125); ++ format %{ "ld_d $dst, $mem #@loadP" %} ++ ins_encode %{ ++ relocInfo::relocType disp_reloc = $mem->disp_reloc(); ++ assert(disp_reloc == relocInfo::none, "cannot have disp"); ++ __ loadstore_enc($dst$$Register, $mem$$base, $mem$$index, $mem$$scale, $mem$$disp, MacroAssembler::LOAD_LONG); ++ %} ++ ins_pipe( ialu_loadI ); ++%} ++ ++// Load Klass Pointer ++instruct loadKlass(mRegP dst, memory mem) %{ ++ match(Set dst (LoadKlass mem)); ++ ++ ins_cost(125); ++ format %{ "MOV $dst,$mem @ loadKlass" %} ++ ins_encode %{ ++ relocInfo::relocType disp_reloc = $mem->disp_reloc(); ++ assert(disp_reloc == relocInfo::none, "cannot have disp"); ++ __ loadstore_enc($dst$$Register, $mem$$base, $mem$$index, $mem$$scale, $mem$$disp, MacroAssembler::LOAD_LONG); ++ %} ++ ins_pipe( ialu_loadI ); ++%} ++ ++// Load narrow Klass Pointer ++instruct loadNKlass(mRegN dst, memory mem) ++%{ ++ match(Set dst (LoadNKlass mem)); ++ ++ ins_cost(125); // XXX ++ format %{ "ld_wu $dst, $mem\t# compressed klass ptr @ loadNKlass" %} ++ ins_encode %{ ++ relocInfo::relocType disp_reloc = $mem->disp_reloc(); ++ assert(disp_reloc == relocInfo::none, "cannot have disp"); ++ __ loadstore_enc($dst$$Register, $mem$$base, $mem$$index, $mem$$scale, $mem$$disp, MacroAssembler::LOAD_U_INT); ++ %} ++ ins_pipe( ialu_loadI ); // XXX ++%} ++ ++instruct loadN2PKlass(mRegP dst, memory mem) ++%{ ++ match(Set dst (DecodeNKlass (LoadNKlass mem))); ++ predicate(Universe::narrow_klass_base() == NULL && Universe::narrow_klass_shift() == 0); ++ ++ ins_cost(125); // XXX ++ format %{ "ld_wu $dst, $mem\t# compressed klass ptr @ loadN2PKlass" %} ++ ins_encode %{ ++ relocInfo::relocType disp_reloc = $mem->disp_reloc(); ++ assert(disp_reloc == relocInfo::none, "cannot have disp"); ++ __ loadstore_enc($dst$$Register, $mem$$base, $mem$$index, $mem$$scale, $mem$$disp, MacroAssembler::LOAD_U_INT); ++ %} ++ ins_pipe( ialu_loadI ); // XXX ++%} ++ ++// Load Constant ++instruct loadConI(mRegI dst, immI src) %{ ++ match(Set dst src); ++ ++ ins_cost(120); ++ format %{ "mov $dst, $src #@loadConI" %} ++ ins_encode %{ ++ Register dst = $dst$$Register; ++ int value = $src$$constant; ++ __ li(dst, value); ++ %} ++ ins_pipe( ialu_regI_regI ); ++%} ++ ++ ++instruct loadConL(mRegL dst, immL src) %{ ++ match(Set dst src); ++ ins_cost(120); ++ format %{ "li $dst, $src @ loadConL" %} ++ ins_encode %{ ++ __ li($dst$$Register, $src$$constant); ++ %} ++ ins_pipe(ialu_regL_regL); ++%} ++ ++// Load Range ++instruct loadRange(mRegI dst, memory_loadRange mem) %{ ++ match(Set dst (LoadRange mem)); ++ ++ ins_cost(125); ++ format %{ "MOV $dst,$mem @ loadRange" %} ++ ins_encode %{ ++ __ loadstore_enc($dst$$Register, $mem$$base, $mem$$index, $mem$$scale, $mem$$disp, MacroAssembler::LOAD_INT); ++ %} ++ ins_pipe( ialu_loadI ); ++%} ++ ++ ++instruct storeP(memory mem, mRegP src ) %{ ++ match(Set mem (StoreP mem src)); ++ ++ ins_cost(125); ++ format %{ "st_d $src, $mem #@storeP" %} ++ ins_encode %{ ++ __ loadstore_enc($src$$Register, $mem$$base, $mem$$index, $mem$$scale, $mem$$disp, MacroAssembler::STORE_LONG); ++ %} ++ ins_pipe( ialu_storeI ); ++%} ++ ++// Store NULL Pointer, mark word, or other simple pointer constant. ++instruct storeImmP_immP_0(memory mem, immP_0 zero) %{ ++ match(Set mem (StoreP mem zero)); ++ ++ ins_cost(125); ++ format %{ "mov $mem, $zero #@storeImmP_0" %} ++ ins_encode %{ ++ __ loadstore_enc(R0, $mem$$base, $mem$$index, $mem$$scale, $mem$$disp, MacroAssembler::STORE_LONG); ++ %} ++ ins_pipe( ialu_storeI ); ++%} ++ ++// Store Compressed Pointer ++instruct storeN(memory mem, mRegN src) ++%{ ++ match(Set mem (StoreN mem src)); ++ ++ ins_cost(125); // XXX ++ format %{ "st_w $mem, $src\t# compressed ptr @ storeN" %} ++ ins_encode %{ ++ __ loadstore_enc($src$$Register, $mem$$base, $mem$$index, $mem$$scale, $mem$$disp, MacroAssembler::STORE_INT); ++ %} ++ ins_pipe( ialu_storeI ); ++%} ++ ++instruct storeP2N(memory mem, mRegP src) ++%{ ++ match(Set mem (StoreN mem (EncodeP src))); ++ predicate(Universe::narrow_oop_base() == NULL && Universe::narrow_oop_shift() == 0); ++ ++ ins_cost(125); // XXX ++ format %{ "st_w $mem, $src\t# @ storeP2N" %} ++ ins_encode %{ ++ __ loadstore_enc($src$$Register, $mem$$base, $mem$$index, $mem$$scale, $mem$$disp, MacroAssembler::STORE_INT); ++ %} ++ ins_pipe( ialu_storeI ); ++%} ++ ++instruct storeNKlass(memory mem, mRegN src) ++%{ ++ match(Set mem (StoreNKlass mem src)); ++ ++ ins_cost(125); // XXX ++ format %{ "st_w $mem, $src\t# compressed klass ptr @ storeNKlass" %} ++ ins_encode %{ ++ __ loadstore_enc($src$$Register, $mem$$base, $mem$$index, $mem$$scale, $mem$$disp, MacroAssembler::STORE_INT); ++ %} ++ ins_pipe( ialu_storeI ); ++%} ++ ++instruct storeP2NKlass(memory mem, mRegP src) ++%{ ++ match(Set mem (StoreNKlass mem (EncodePKlass src))); ++ predicate(Universe::narrow_klass_base() == NULL && Universe::narrow_klass_shift() == 0); ++ ++ ins_cost(125); // XXX ++ format %{ "st_w $mem, $src\t# @ storeP2NKlass" %} ++ ins_encode %{ ++ __ loadstore_enc($src$$Register, $mem$$base, $mem$$index, $mem$$scale, $mem$$disp, MacroAssembler::STORE_INT); ++ %} ++ ins_pipe( ialu_storeI ); ++%} ++ ++instruct storeImmN_immN_0(memory mem, immN_0 zero) ++%{ ++ match(Set mem (StoreN mem zero)); ++ ++ ins_cost(125); // XXX ++ format %{ "storeN0 zero, $mem\t# compressed ptr" %} ++ ins_encode %{ ++ __ loadstore_enc(R0, $mem$$base, $mem$$index, $mem$$scale, $mem$$disp, MacroAssembler::STORE_INT); ++ %} ++ ins_pipe( ialu_storeI ); ++%} ++ ++// Store Byte ++instruct storeB_immB_0(memory mem, immI_0 zero) %{ ++ match(Set mem (StoreB mem zero)); ++ ++ format %{ "mov $mem, zero #@storeB_immB_0" %} ++ ins_encode %{ ++ __ loadstore_enc(R0, $mem$$base, $mem$$index, $mem$$scale, $mem$$disp, MacroAssembler::STORE_BYTE); ++ %} ++ ins_pipe( ialu_storeI ); ++%} ++ ++instruct storeB(memory mem, mRegI src) %{ ++ match(Set mem (StoreB mem src)); ++ ++ ins_cost(125); ++ format %{ "st_b $src, $mem #@storeB" %} ++ ins_encode %{ ++ __ loadstore_enc($src$$Register, $mem$$base, $mem$$index, $mem$$scale, $mem$$disp, MacroAssembler::STORE_BYTE); ++ %} ++ ins_pipe( ialu_storeI ); ++%} ++ ++instruct storeB_convL2I(memory mem, mRegL src) %{ ++ match(Set mem (StoreB mem (ConvL2I src))); ++ ++ ins_cost(125); ++ format %{ "st_b $src, $mem #@storeB_convL2I" %} ++ ins_encode %{ ++ __ loadstore_enc($src$$Register, $mem$$base, $mem$$index, $mem$$scale, $mem$$disp, MacroAssembler::STORE_BYTE); ++ %} ++ ins_pipe( ialu_storeI ); ++%} ++ ++// Load Byte (8bit signed) ++instruct loadB(mRegI dst, memory mem) %{ ++ match(Set dst (LoadB mem)); ++ ++ ins_cost(125); ++ format %{ "ld_b $dst, $mem #@loadB" %} ++ ins_encode %{ ++ __ loadstore_enc($dst$$Register, $mem$$base, $mem$$index, $mem$$scale, $mem$$disp, MacroAssembler::LOAD_BYTE); ++ %} ++ ins_pipe( ialu_loadI ); ++%} ++ ++instruct loadB_convI2L(mRegL dst, memory mem) %{ ++ match(Set dst (ConvI2L (LoadB mem))); ++ ++ ins_cost(125); ++ format %{ "ld_b $dst, $mem #@loadB_convI2L" %} ++ ins_encode %{ ++ __ loadstore_enc($dst$$Register, $mem$$base, $mem$$index, $mem$$scale, $mem$$disp, MacroAssembler::LOAD_BYTE); ++ %} ++ ins_pipe( ialu_loadI ); ++%} ++ ++// Load Byte (8bit UNsigned) ++instruct loadUB(mRegI dst, memory mem) %{ ++ match(Set dst (LoadUB mem)); ++ ++ ins_cost(125); ++ format %{ "ld_bu $dst, $mem #@loadUB" %} ++ ins_encode %{ ++ __ loadstore_enc($dst$$Register, $mem$$base, $mem$$index, $mem$$scale, $mem$$disp, MacroAssembler::LOAD_U_BYTE); ++ %} ++ ins_pipe( ialu_loadI ); ++%} ++ ++instruct loadUB_convI2L(mRegL dst, memory mem) %{ ++ match(Set dst (ConvI2L (LoadUB mem))); ++ ++ ins_cost(125); ++ format %{ "ld_bu $dst, $mem #@loadUB_convI2L" %} ++ ins_encode %{ ++ __ loadstore_enc($dst$$Register, $mem$$base, $mem$$index, $mem$$scale, $mem$$disp, MacroAssembler::LOAD_U_BYTE); ++ %} ++ ins_pipe( ialu_loadI ); ++%} ++ ++// Load Short (16bit signed) ++instruct loadS(mRegI dst, memory mem) %{ ++ match(Set dst (LoadS mem)); ++ ++ ins_cost(125); ++ format %{ "ld_h $dst, $mem #@loadS" %} ++ ins_encode %{ ++ __ loadstore_enc($dst$$Register, $mem$$base, $mem$$index, $mem$$scale, $mem$$disp, MacroAssembler::LOAD_SHORT); ++ %} ++ ins_pipe( ialu_loadI ); ++%} ++ ++// Load Short (16 bit signed) to Byte (8 bit signed) ++instruct loadS2B(mRegI dst, memory mem, immI_24 twentyfour) %{ ++ match(Set dst (RShiftI (LShiftI (LoadS mem) twentyfour) twentyfour)); ++ ++ ins_cost(125); ++ format %{ "ld_b $dst, $mem\t# short -> byte #@loadS2B" %} ++ ins_encode %{ ++ __ loadstore_enc($dst$$Register, $mem$$base, $mem$$index, $mem$$scale, $mem$$disp, MacroAssembler::LOAD_BYTE); ++ %} ++ ins_pipe(ialu_loadI); ++%} ++ ++instruct loadS_convI2L(mRegL dst, memory mem) %{ ++ match(Set dst (ConvI2L (LoadS mem))); ++ ++ ins_cost(125); ++ format %{ "ld_h $dst, $mem #@loadS_convI2L" %} ++ ins_encode %{ ++ __ loadstore_enc($dst$$Register, $mem$$base, $mem$$index, $mem$$scale, $mem$$disp, MacroAssembler::LOAD_SHORT); ++ %} ++ ins_pipe( ialu_loadI ); ++%} ++ ++// Store Integer Immediate ++instruct storeI_immI_0(memory mem, immI_0 zero) %{ ++ match(Set mem (StoreI mem zero)); ++ ++ format %{ "mov $mem, zero #@storeI_immI_0" %} ++ ins_encode %{ ++ __ loadstore_enc(R0, $mem$$base, $mem$$index, $mem$$scale, $mem$$disp, MacroAssembler::STORE_INT); ++ %} ++ ins_pipe( ialu_storeI ); ++%} ++ ++// Store Integer ++instruct storeI(memory mem, mRegI src) %{ ++ match(Set mem (StoreI mem src)); ++ ++ ins_cost(125); ++ format %{ "st_w $mem, $src #@storeI" %} ++ ins_encode %{ ++ __ loadstore_enc($src$$Register, $mem$$base, $mem$$index, $mem$$scale, $mem$$disp, MacroAssembler::STORE_INT); ++ %} ++ ins_pipe( ialu_storeI ); ++%} ++ ++instruct storeI_convL2I(memory mem, mRegL src) %{ ++ match(Set mem (StoreI mem (ConvL2I src))); ++ ++ ins_cost(125); ++ format %{ "st_w $mem, $src #@storeI_convL2I" %} ++ ins_encode %{ ++ __ loadstore_enc($src$$Register, $mem$$base, $mem$$index, $mem$$scale, $mem$$disp, MacroAssembler::STORE_INT); ++ %} ++ ins_pipe( ialu_storeI ); ++%} ++ ++// Load Float ++instruct loadF(regF dst, memory mem) %{ ++ match(Set dst (LoadF mem)); ++ ++ ins_cost(150); ++ format %{ "loadF $dst, $mem #@loadF" %} ++ ins_encode %{ ++ __ loadstore_enc($dst$$FloatRegister, $mem$$base, $mem$$index, $mem$$scale, $mem$$disp, MacroAssembler::LOAD_FLOAT); ++ %} ++ ins_pipe( ialu_loadI ); ++%} ++ ++instruct loadConP_general(mRegP dst, immP src) %{ ++ match(Set dst src); ++ ++ ins_cost(120); ++ format %{ "li $dst, $src #@loadConP_general" %} ++ ++ ins_encode %{ ++ Register dst = $dst$$Register; ++ long* value = (long*)$src$$constant; ++ ++ if($src->constant_reloc() == relocInfo::metadata_type){ ++ int klass_index = __ oop_recorder()->find_index((Klass*)value); ++ RelocationHolder rspec = metadata_Relocation::spec(klass_index); ++ ++ __ relocate(rspec); ++ __ patchable_li52(dst, (long)value); ++ } else if($src->constant_reloc() == relocInfo::oop_type){ ++ int oop_index = __ oop_recorder()->find_index((jobject)value); ++ RelocationHolder rspec = oop_Relocation::spec(oop_index); ++ ++ __ relocate(rspec); ++ __ patchable_li52(dst, (long)value); ++ } else if ($src->constant_reloc() == relocInfo::none) { ++ __ li(dst, (long)value); ++ } ++ %} ++ ++ ins_pipe( ialu_regI_regI ); ++%} ++ ++instruct loadConP_no_oop_cheap(mRegP dst, immP_no_oop_cheap src) %{ ++ match(Set dst src); ++ ++ ins_cost(80); ++ format %{ "li $dst, $src @ loadConP_no_oop_cheap" %} ++ ++ ins_encode %{ ++ if ($src->constant_reloc() == relocInfo::metadata_type) { ++ __ mov_metadata($dst$$Register, (Metadata*)$src$$constant); ++ } else { ++ __ li($dst$$Register, $src$$constant); ++ } ++ %} ++ ++ ins_pipe(ialu_regI_regI); ++%} ++ ++ ++instruct loadConP_poll(mRegP dst, immP_poll src) %{ ++ match(Set dst src); ++ ++ ins_cost(50); ++ format %{ "li $dst, $src #@loadConP_poll" %} ++ ++ ins_encode %{ ++ Register dst = $dst$$Register; ++ intptr_t value = (intptr_t)$src$$constant; ++ ++ __ li(dst, (jlong)value); ++ %} ++ ++ ins_pipe( ialu_regI_regI ); ++%} ++ ++instruct loadConP_immP_0(mRegP dst, immP_0 src) ++%{ ++ match(Set dst src); ++ ++ ins_cost(50); ++ format %{ "mov $dst, R0\t# ptr" %} ++ ins_encode %{ ++ Register dst_reg = $dst$$Register; ++ __ add_d(dst_reg, R0, R0); ++ %} ++ ins_pipe( ialu_regI_regI ); ++%} ++ ++instruct loadConN_immN_0(mRegN dst, immN_0 src) %{ ++ match(Set dst src); ++ format %{ "move $dst, R0\t# compressed NULL ptr" %} ++ ins_encode %{ ++ __ move($dst$$Register, R0); ++ %} ++ ins_pipe( ialu_regI_regI ); ++%} ++ ++instruct loadConN(mRegN dst, immN src) %{ ++ match(Set dst src); ++ ++ ins_cost(125); ++ format %{ "li $dst, $src\t# compressed ptr @ loadConN" %} ++ ins_encode %{ ++ Register dst = $dst$$Register; ++ __ set_narrow_oop(dst, (jobject)$src$$constant); ++ %} ++ ins_pipe( ialu_regI_regI ); // XXX ++%} ++ ++instruct loadConNKlass(mRegN dst, immNKlass src) %{ ++ match(Set dst src); ++ ++ ins_cost(125); ++ format %{ "li $dst, $src\t# compressed klass ptr @ loadConNKlass" %} ++ ins_encode %{ ++ Register dst = $dst$$Register; ++ __ set_narrow_klass(dst, (Klass*)$src$$constant); ++ %} ++ ins_pipe( ialu_regI_regI ); // XXX ++%} ++ ++//FIXME ++// Tail Call; Jump from runtime stub to Java code. ++// Also known as an 'interprocedural jump'. ++// Target of jump will eventually return to caller. ++// TailJump below removes the return address. ++instruct TailCalljmpInd(mRegP jump_target, mRegP method_oop) %{ ++ match(TailCall jump_target method_oop ); ++ ins_cost(300); ++ format %{ "JMP $jump_target \t# @TailCalljmpInd" %} ++ ++ ins_encode %{ ++ Register target = $jump_target$$Register; ++ Register oop = $method_oop$$Register; ++ ++ // RA will be used in generate_forward_exception() ++ __ push(RA); ++ ++ __ move(S3, oop); ++ __ jr(target); ++ %} ++ ++ ins_pipe( pipe_jump ); ++%} ++ ++// Create exception oop: created by stack-crawling runtime code. ++// Created exception is now available to this handler, and is setup ++// just prior to jumping to this handler. No code emitted. ++instruct CreateException( a0_RegP ex_oop ) ++%{ ++ match(Set ex_oop (CreateEx)); ++ ++ // use the following format syntax ++ format %{ "# exception oop is in A0; no code emitted @CreateException" %} ++ ins_encode %{ ++ // X86 leaves this function empty ++ __ block_comment("CreateException is empty in LA"); ++ %} ++ ins_pipe( empty ); ++// ins_pipe( pipe_jump ); ++%} ++ ++ ++/* The mechanism of exception handling is clear now. ++ ++- Common try/catch: ++ [stubGenerator_loongarch.cpp] generate_forward_exception() ++ |- V0, V1 are created ++ |- T4 <= SharedRuntime::exception_handler_for_return_address ++ `- jr T4 ++ `- the caller's exception_handler ++ `- jr OptoRuntime::exception_blob ++ `- here ++- Rethrow(e.g. 'unwind'): ++ * The callee: ++ |- an exception is triggered during execution ++ `- exits the callee method through RethrowException node ++ |- The callee pushes exception_oop(T0) and exception_pc(RA) ++ `- The callee jumps to OptoRuntime::rethrow_stub() ++ * In OptoRuntime::rethrow_stub: ++ |- The VM calls _rethrow_Java to determine the return address in the caller method ++ `- exits the stub with tailjmpInd ++ |- pops exception_oop(V0) and exception_pc(V1) ++ `- jumps to the return address(usually an exception_handler) ++ * The caller: ++ `- continues processing the exception_blob with V0/V1 ++*/ ++ ++// Rethrow exception: ++// The exception oop will come in the first argument position. ++// Then JUMP (not call) to the rethrow stub code. ++instruct RethrowException() ++%{ ++ match(Rethrow); ++ ++ // use the following format syntax ++ format %{ "JMP rethrow_stub #@RethrowException" %} ++ ins_encode %{ ++ __ block_comment("@ RethrowException"); ++ ++ cbuf.set_insts_mark(); ++ cbuf.relocate(cbuf.insts_mark(), runtime_call_Relocation::spec()); ++ ++ // call OptoRuntime::rethrow_stub to get the exception handler in parent method ++ __ patchable_jump((address)OptoRuntime::rethrow_stub()); ++ %} ++ ins_pipe( pipe_jump ); ++%} ++ ++// ============================================================================ ++// Branch Instructions --- long offset versions ++ ++// Jump Direct ++instruct jmpDir_long(label labl) %{ ++ match(Goto); ++ effect(USE labl); ++ ++ ins_cost(300); ++ format %{ "JMP $labl #@jmpDir_long" %} ++ ++ ins_encode %{ ++ Label* L = $labl$$label; ++ __ jmp_far(*L); ++ %} ++ ++ ins_pipe( pipe_jump ); ++ //ins_pc_relative(1); ++%} ++ ++// Jump Direct Conditional - Label defines a relative address from Jcc+1 ++instruct jmpLoopEnd_long(cmpOp cop, mRegI src1, mRegI src2, label labl) %{ ++ match(CountedLoopEnd cop (CmpI src1 src2)); ++ effect(USE labl); ++ ++ ins_cost(300); ++ format %{ "J$cop $src1, $src2, $labl\t# Loop end @ jmpLoopEnd_long" %} ++ ins_encode %{ ++ Register op1 = $src1$$Register; ++ Register op2 = $src2$$Register; ++ Label* L = $labl$$label; ++ int flag = $cop$$cmpcode; ++ ++ switch(flag) { ++ case 0x01: //equal ++ __ beq_long(op1, op2, *L); ++ break; ++ case 0x02: //not_equal ++ __ bne_long(op1, op2, *L); ++ break; ++ case 0x03: //above ++ __ blt_long(op2, op1, *L, true /* signed */); ++ break; ++ case 0x04: //above_equal ++ __ bge_long(op1, op2, *L, true /* signed */); ++ break; ++ case 0x05: //below ++ __ blt_long(op1, op2, *L, true /* signed */); ++ break; ++ case 0x06: //below_equal ++ __ bge_long(op2, op1, *L, true /* signed */); ++ break; ++ default: ++ Unimplemented(); ++ } ++ %} ++ ins_pipe( pipe_jump ); ++ ins_pc_relative(1); ++%} ++ ++instruct jmpLoopEnd_reg_immI_long(cmpOp cop, mRegI src1, immI src2, label labl) %{ ++ match(CountedLoopEnd cop (CmpI src1 src2)); ++ effect(USE labl); ++ ++ ins_cost(300); ++ format %{ "J$cop $src1, $src2, $labl\t# Loop end @ jmpLoopEnd_reg_immI_long" %} ++ ins_encode %{ ++ Register op1 = $src1$$Register; ++ Register op2 = AT; ++ Label* L = $labl$$label; ++ int flag = $cop$$cmpcode; ++ ++ __ li(op2, $src2$$constant); ++ ++ switch(flag) { ++ case 0x01: //equal ++ __ beq_long(op1, op2, *L); ++ break; ++ case 0x02: //not_equal ++ __ bne_long(op1, op2, *L); ++ break; ++ case 0x03: //above ++ __ blt_long(op2, op1, *L, true /* signed */); ++ break; ++ case 0x04: //above_equal ++ __ bge_long(op1, op2, *L, true /* signed */); ++ break; ++ case 0x05: //below ++ __ blt_long(op1, op2, *L, true /* signed */); ++ break; ++ case 0x06: //below_equal ++ __ bge_long(op2, op1, *L, true /* signed */); ++ break; ++ default: ++ Unimplemented(); ++ } ++ %} ++ ins_pipe( pipe_jump ); ++ ins_pc_relative(1); ++%} ++ ++ ++// This match pattern is created for StoreIConditional since I cannot match IfNode without a RegFlags! ++instruct jmpCon_flags_long(cmpOp cop, FlagsReg cr, label labl) %{ ++ match(If cop cr); ++ effect(USE labl); ++ ++ ins_cost(300); ++ format %{ "J$cop $labl #LoongArch uses T0 as equivalent to eflag @jmpCon_flags_long" %} ++ ++ ins_encode %{ ++ Label* L = $labl$$label; ++ switch($cop$$cmpcode) { ++ case 0x01: //equal ++ __ bne_long($cr$$Register, R0, *L); ++ break; ++ case 0x02: //not equal ++ __ beq_long($cr$$Register, R0, *L); ++ break; ++ default: ++ Unimplemented(); ++ } ++ %} ++ ++ ins_pipe( pipe_jump ); ++ ins_pc_relative(1); ++%} ++ ++// Conditional jumps ++instruct branchConP_0_long(cmpOpU cmp, mRegP op1, immP_0 zero, label labl) %{ ++ match(If cmp (CmpP op1 zero)); ++ effect(USE labl); ++ ++ ins_cost(180); ++ format %{ "b$cmp $op1, R0, $labl #@branchConP_0_long" %} ++ ++ ins_encode %{ ++ Register op1 = $op1$$Register; ++ Register op2 = R0; ++ Label* L = $labl$$label; ++ int flag = $cmp$$cmpcode; ++ ++ switch(flag) { ++ case 0x01: //equal ++ __ beq_long(op1, op2, *L); ++ break; ++ case 0x02: //not_equal ++ __ bne_long(op1, op2, *L); ++ break; ++ default: ++ Unimplemented(); ++ } ++ %} ++ ++ ins_pc_relative(1); ++ ins_pipe( pipe_alu_branch ); ++%} ++ ++instruct branchConN2P_0_long(cmpOpU cmp, mRegN op1, immP_0 zero, label labl) %{ ++ match(If cmp (CmpP (DecodeN op1) zero)); ++ predicate(Universe::narrow_oop_base() == NULL && Universe::narrow_oop_shift() == 0); ++ effect(USE labl); ++ ++ ins_cost(180); ++ format %{ "b$cmp $op1, R0, $labl #@branchConN2P_0_long" %} ++ ++ ins_encode %{ ++ Register op1 = $op1$$Register; ++ Register op2 = R0; ++ Label* L = $labl$$label; ++ int flag = $cmp$$cmpcode; ++ ++ switch(flag) ++ { ++ case 0x01: //equal ++ __ beq_long(op1, op2, *L); ++ break; ++ case 0x02: //not_equal ++ __ bne_long(op1, op2, *L); ++ break; ++ default: ++ Unimplemented(); ++ } ++ %} ++ ++ ins_pc_relative(1); ++ ins_pipe( pipe_alu_branch ); ++%} ++ ++ ++instruct branchConP_long(cmpOpU cmp, mRegP op1, mRegP op2, label labl) %{ ++ match(If cmp (CmpP op1 op2)); ++// predicate(can_branch_register(_kids[0]->_leaf, _kids[1]->_leaf)); ++ effect(USE labl); ++ ++ ins_cost(200); ++ format %{ "b$cmp $op1, $op2, $labl #@branchConP_long" %} ++ ++ ins_encode %{ ++ Register op1 = $op1$$Register; ++ Register op2 = $op2$$Register; ++ Label* L = $labl$$label; ++ int flag = $cmp$$cmpcode; ++ ++ switch(flag) { ++ case 0x01: //equal ++ __ beq_long(op1, op2, *L); ++ break; ++ case 0x02: //not_equal ++ __ bne_long(op1, op2, *L); ++ break; ++ case 0x03: //above ++ __ blt_long(op2, op1, *L, false /* unsigned */); ++ break; ++ case 0x04: //above_equal ++ __ bge_long(op1, op2, *L, false /* unsigned */); ++ break; ++ case 0x05: //below ++ __ blt_long(op1, op2, *L, false /* unsigned */); ++ break; ++ case 0x06: //below_equal ++ __ bge_long(op2, op1, *L, false /* unsigned */); ++ break; ++ default: ++ Unimplemented(); ++ } ++ %} ++ ++ ins_pc_relative(1); ++ ins_pipe( pipe_alu_branch ); ++%} ++ ++instruct cmpN_null_branch_long(cmpOp cmp, mRegN op1, immN_0 null, label labl) %{ ++ match(If cmp (CmpN op1 null)); ++ effect(USE labl); ++ ++ ins_cost(180); ++ format %{ "CMP $op1,0\t! compressed ptr\n\t" ++ "BP$cmp $labl @ cmpN_null_branch_long" %} ++ ins_encode %{ ++ Register op1 = $op1$$Register; ++ Register op2 = R0; ++ Label* L = $labl$$label; ++ int flag = $cmp$$cmpcode; ++ ++ switch(flag) { ++ case 0x01: //equal ++ __ beq_long(op1, op2, *L); ++ break; ++ case 0x02: //not_equal ++ __ bne_long(op1, op2, *L); ++ break; ++ default: ++ Unimplemented(); ++ } ++ %} ++//TODO: pipe_branchP or create pipe_branchN LEE ++ ins_pc_relative(1); ++ ins_pipe( pipe_alu_branch ); ++%} ++ ++instruct cmpN_reg_branch_long(cmpOp cmp, mRegN op1, mRegN op2, label labl) %{ ++ match(If cmp (CmpN op1 op2)); ++ effect(USE labl); ++ ++ ins_cost(180); ++ format %{ "CMP $op1,$op2\t! compressed ptr\n\t" ++ "BP$cmp $labl @ cmpN_reg_branch_long" %} ++ ins_encode %{ ++ Register op1_reg = $op1$$Register; ++ Register op2_reg = $op2$$Register; ++ Label* L = $labl$$label; ++ int flag = $cmp$$cmpcode; ++ ++ switch(flag) { ++ case 0x01: //equal ++ __ beq_long(op1_reg, op2_reg, *L); ++ break; ++ case 0x02: //not_equal ++ __ bne_long(op1_reg, op2_reg, *L); ++ break; ++ case 0x03: //above ++ __ blt_long(op2_reg, op1_reg, *L, false /* unsigned */); ++ break; ++ case 0x04: //above_equal ++ __ bge_long(op1_reg, op2_reg, *L, false /* unsigned */); ++ break; ++ case 0x05: //below ++ __ blt_long(op1_reg, op2_reg, *L, false /* unsigned */); ++ break; ++ case 0x06: //below_equal ++ __ bge_long(op2_reg, op1_reg, *L, false /* unsigned */); ++ break; ++ default: ++ Unimplemented(); ++ } ++ %} ++ ins_pc_relative(1); ++ ins_pipe( pipe_alu_branch ); ++%} ++ ++instruct branchConIU_reg_reg_long(cmpOpU cmp, mRegI src1, mRegI src2, label labl) %{ ++ match( If cmp (CmpU src1 src2) ); ++ effect(USE labl); ++ format %{ "BR$cmp $src1, $src2, $labl #@branchConIU_reg_reg_long" %} ++ ++ ins_encode %{ ++ Register op1 = $src1$$Register; ++ Register op2 = $src2$$Register; ++ Label* L = $labl$$label; ++ int flag = $cmp$$cmpcode; ++ ++ switch(flag) { ++ case 0x01: //equal ++ __ beq_long(op1, op2, *L); ++ break; ++ case 0x02: //not_equal ++ __ bne_long(op1, op2, *L); ++ break; ++ case 0x03: //above ++ __ blt_long(op2, op1, *L, false /* unsigned */); ++ break; ++ case 0x04: //above_equal ++ __ bge_long(op1, op2, *L, false /* unsigned */); ++ break; ++ case 0x05: //below ++ __ blt_long(op1, op2, *L, false /* unsigned */); ++ break; ++ case 0x06: //below_equal ++ __ bge_long(op2, op1, *L, false /* unsigned */); ++ break; ++ default: ++ Unimplemented(); ++ } ++ %} ++ ++ ins_pc_relative(1); ++ ins_pipe( pipe_alu_branch ); ++%} ++ ++ ++instruct branchConIU_reg_imm_long(cmpOpU cmp, mRegI src1, immI src2, label labl) %{ ++ match( If cmp (CmpU src1 src2) ); ++ effect(USE labl); ++ format %{ "BR$cmp $src1, $src2, $labl #@branchConIU_reg_imm_long" %} ++ ++ ins_encode %{ ++ Register op1 = $src1$$Register; ++ int val = $src2$$constant; ++ Label* L = $labl$$label; ++ int flag = $cmp$$cmpcode; ++ ++ __ li(AT, val); ++ switch(flag) { ++ case 0x01: //equal ++ __ beq_long(op1, AT, *L); ++ break; ++ case 0x02: //not_equal ++ __ bne_long(op1, AT, *L); ++ break; ++ case 0x03: //above ++ __ blt_long(AT, op1, *L, false /* unsigned */); ++ break; ++ case 0x04: //above_equal ++ __ bge_long(op1, AT, *L, false /* unsigned */); ++ break; ++ case 0x05: //below ++ __ blt_long(op1, AT, *L, false /* unsigned */); ++ break; ++ case 0x06: //below_equal ++ __ bge_long(AT, op1, *L, false /* unsigned */); ++ break; ++ default: ++ Unimplemented(); ++ } ++ %} ++ ++ ins_pc_relative(1); ++ ins_pipe( pipe_alu_branch ); ++%} ++ ++instruct branchConI_reg_reg_long(cmpOp cmp, mRegI src1, mRegI src2, label labl) %{ ++ match( If cmp (CmpI src1 src2) ); ++ effect(USE labl); ++ format %{ "BR$cmp $src1, $src2, $labl #@branchConI_reg_reg_long" %} ++ ++ ins_encode %{ ++ Register op1 = $src1$$Register; ++ Register op2 = $src2$$Register; ++ Label* L = $labl$$label; ++ int flag = $cmp$$cmpcode; ++ ++ switch(flag) { ++ case 0x01: //equal ++ __ beq_long(op1, op2, *L); ++ break; ++ case 0x02: //not_equal ++ __ bne_long(op1, op2, *L); ++ break; ++ case 0x03: //above ++ __ blt_long(op2, op1, *L, true /* signed */); ++ break; ++ case 0x04: //above_equal ++ __ bge_long(op1, op2, *L, true /* signed */); ++ break; ++ case 0x05: //below ++ __ blt_long(op1, op2, *L, true /* signed */); ++ break; ++ case 0x06: //below_equal ++ __ bge_long(op2, op1, *L, true /* signed */); ++ break; ++ default: ++ Unimplemented(); ++ } ++ %} ++ ++ ins_pc_relative(1); ++ ins_pipe( pipe_alu_branch ); ++%} ++ ++instruct branchConI_reg_immI_0_long(cmpOp cmp, mRegI src1, immI_0 src2, label labl) %{ ++ match( If cmp (CmpI src1 src2) ); ++ effect(USE labl); ++ ins_cost(170); ++ format %{ "BR$cmp $src1, $src2, $labl #@branchConI_reg_immI_0_long" %} ++ ++ ins_encode %{ ++ Register op1 = $src1$$Register; ++ Label* L = $labl$$label; ++ int flag = $cmp$$cmpcode; ++ ++ switch(flag) { ++ case 0x01: //equal ++ __ beq_long(op1, R0, *L); ++ break; ++ case 0x02: //not_equal ++ __ bne_long(op1, R0, *L); ++ break; ++ case 0x03: //greater ++ __ blt_long(R0, op1, *L, true /* signed */); ++ break; ++ case 0x04: //greater_equal ++ __ bge_long(op1, R0, *L, true /* signed */); ++ break; ++ case 0x05: //less ++ __ blt_long(op1, R0, *L, true /* signed */); ++ break; ++ case 0x06: //less_equal ++ __ bge_long(R0, op1, *L, true /* signed */); ++ break; ++ default: ++ Unimplemented(); ++ } ++ %} ++ ++ ins_pc_relative(1); ++ ins_pipe( pipe_alu_branch ); ++%} ++ ++instruct branchConI_reg_imm_long(cmpOp cmp, mRegI src1, immI src2, label labl) %{ ++ match( If cmp (CmpI src1 src2) ); ++ effect(USE labl); ++ ins_cost(200); ++ format %{ "BR$cmp $src1, $src2, $labl #@branchConI_reg_imm_long" %} ++ ++ ins_encode %{ ++ Register op1 = $src1$$Register; ++ int val = $src2$$constant; ++ Label* L = $labl$$label; ++ int flag = $cmp$$cmpcode; ++ ++ __ li(AT, val); ++ switch(flag) { ++ case 0x01: //equal ++ __ beq_long(op1, AT, *L); ++ break; ++ case 0x02: //not_equal ++ __ bne_long(op1, AT, *L); ++ break; ++ case 0x03: //greater ++ __ blt_long(AT, op1, *L, true /* signed */); ++ break; ++ case 0x04: //greater_equal ++ __ bge_long(op1, AT, *L, true /* signed */); ++ break; ++ case 0x05: //less ++ __ blt_long(op1, AT, *L, true /* signed */); ++ break; ++ case 0x06: //less_equal ++ __ bge_long(AT, op1, *L, true /* signed */); ++ break; ++ default: ++ Unimplemented(); ++ } ++ %} ++ ++ ins_pc_relative(1); ++ ins_pipe( pipe_alu_branch ); ++%} ++ ++instruct branchConIU_reg_immI_0_long(cmpOpU cmp, mRegI src1, immI_0 zero, label labl) %{ ++ match( If cmp (CmpU src1 zero) ); ++ effect(USE labl); ++ format %{ "BR$cmp $src1, zero, $labl #@branchConIU_reg_immI_0_long" %} ++ ++ ins_encode %{ ++ Register op1 = $src1$$Register; ++ Label* L = $labl$$label; ++ int flag = $cmp$$cmpcode; ++ ++ switch(flag) { ++ case 0x01: //equal ++ __ beq_long(op1, R0, *L); ++ break; ++ case 0x02: //not_equal ++ __ bne_long(op1, R0, *L); ++ break; ++ case 0x03: //above ++ __ bne_long(R0, op1, *L); ++ break; ++ case 0x04: //above_equal ++ __ beq_long(R0, R0, *L); ++ break; ++ case 0x05: //below ++ return; ++ break; ++ case 0x06: //below_equal ++ __ beq_long(op1, R0, *L); ++ break; ++ default: ++ Unimplemented(); ++ } ++ %} ++ ++ ins_pc_relative(1); ++ ins_pipe( pipe_alu_branch ); ++%} ++ ++ ++instruct branchConL_regL_regL_long(cmpOp cmp, mRegLorI2L src1, mRegLorI2L src2, label labl) %{ ++ match( If cmp (CmpL src1 src2) ); ++ effect(USE labl); ++ format %{ "BR$cmp $src1, $src2, $labl #@branchConL_regL_regL_long" %} ++ ins_cost(250); ++ ++ ins_encode %{ ++ Register opr1_reg = as_Register($src1$$reg); ++ Register opr2_reg = as_Register($src2$$reg); ++ ++ Label* target = $labl$$label; ++ int flag = $cmp$$cmpcode; ++ ++ switch(flag) { ++ case 0x01: //equal ++ __ beq_long(opr1_reg, opr2_reg, *target); ++ break; ++ ++ case 0x02: //not_equal ++ __ bne_long(opr1_reg, opr2_reg, *target); ++ break; ++ ++ case 0x03: //greater ++ __ blt_long(opr2_reg, opr1_reg, *target, true /* signed */); ++ break; ++ ++ case 0x04: //greater_equal ++ __ bge_long(opr1_reg, opr2_reg, *target, true /* signed */); ++ break; ++ ++ case 0x05: //less ++ __ blt_long(opr1_reg, opr2_reg, *target, true /* signed */); ++ break; ++ ++ case 0x06: //less_equal ++ __ bge_long(opr2_reg, opr1_reg, *target, true /* signed */); ++ break; ++ ++ default: ++ Unimplemented(); ++ } ++ %} ++ ++ ++ ins_pc_relative(1); ++ ins_pipe( pipe_alu_branch ); ++%} ++ ++instruct branchConUL_regL_regL_long(cmpOp cmp, mRegLorI2L src1, mRegLorI2L src2, label labl) %{ ++ match( If cmp (CmpUL src1 src2) ); ++ effect(USE labl); ++ format %{ "BR$cmp $src1, $src2, $labl #@branchConUL_regL_regL_long" %} ++ ins_cost(250); ++ ++ ins_encode %{ ++ Register opr1_reg = as_Register($src1$$reg); ++ Register opr2_reg = as_Register($src2$$reg); ++ ++ Label* target = $labl$$label; ++ int flag = $cmp$$cmpcode; ++ ++ switch(flag) { ++ case 0x01: //equal ++ __ beq_long(opr1_reg, opr2_reg, *target); ++ break; ++ ++ case 0x02: //not_equal ++ __ bne_long(opr1_reg, opr2_reg, *target); ++ break; ++ ++ case 0x03: //greater ++ __ blt_long(opr2_reg, opr1_reg, *target, false /* signed */); ++ break; ++ ++ case 0x04: //greater_equal ++ __ bge_long(opr1_reg, opr2_reg, *target, false /* signed */); ++ break; ++ ++ case 0x05: //less ++ __ blt_long(opr1_reg, opr2_reg, *target, false /* signed */); ++ break; ++ ++ case 0x06: //less_equal ++ __ bge_long(opr2_reg, opr1_reg, *target, false /* signed */); ++ break; ++ ++ default: ++ Unimplemented(); ++ } ++ %} ++ ++ ++ ins_pc_relative(1); ++ ins_pipe( pipe_alu_branch ); ++%} ++ ++instruct branchConL_regL_immL_0_long(cmpOp cmp, mRegL src1, immL_0 zero, label labl) %{ ++ match( If cmp (CmpL src1 zero) ); ++ effect(USE labl); ++ format %{ "BR$cmp $src1, zero, $labl #@branchConL_regL_immL_0_long" %} ++ ins_cost(150); ++ ++ ins_encode %{ ++ Register opr1_reg = as_Register($src1$$reg); ++ Register opr2_reg = R0; ++ ++ Label* target = $labl$$label; ++ int flag = $cmp$$cmpcode; ++ ++ switch(flag) { ++ case 0x01: //equal ++ __ beq_long(opr1_reg, opr2_reg, *target); ++ break; ++ ++ case 0x02: //not_equal ++ __ bne_long(opr1_reg, opr2_reg, *target); ++ break; ++ ++ case 0x03: //greater ++ __ blt_long(opr2_reg, opr1_reg, *target, true /* signed */); ++ break; ++ ++ case 0x04: //greater_equal ++ __ bge_long(opr1_reg, opr2_reg, *target, true /* signed */); ++ break; ++ ++ case 0x05: //less ++ __ blt_long(opr1_reg, opr2_reg, *target, true /* signed */); ++ break; ++ ++ case 0x06: //less_equal ++ __ bge_long(opr2_reg, opr1_reg, *target, true /* signed */); ++ break; ++ ++ default: ++ Unimplemented(); ++ } ++ %} ++ ++ ++ ins_pc_relative(1); ++ ins_pipe( pipe_alu_branch ); ++%} ++ ++instruct branchConUL_regL_immL_0_long(cmpOp cmp, mRegL src1, immL_0 zero, label labl) %{ ++ match( If cmp (CmpUL src1 zero) ); ++ effect(USE labl); ++ format %{ "BR$cmp $src1, zero, $labl #@branchConUL_regL_immL_0_long" %} ++ ins_cost(150); ++ ++ ins_encode %{ ++ Register opr1_reg = as_Register($src1$$reg); ++ Register opr2_reg = R0; ++ ++ Label* target = $labl$$label; ++ int flag = $cmp$$cmpcode; ++ ++ switch(flag) { ++ case 0x01: //equal ++ __ beq_long(opr1_reg, opr2_reg, *target); ++ break; ++ ++ case 0x02: //not_equal ++ __ bne_long(opr1_reg, opr2_reg, *target); ++ break; ++ ++ case 0x03: //greater ++ __ blt_long(opr2_reg, opr1_reg, *target, false /* signed */); ++ break; ++ ++ case 0x04: //greater_equal ++ __ bge_long(opr1_reg, opr2_reg, *target, false /* signed */); ++ break; ++ ++ case 0x05: //less ++ __ blt_long(opr1_reg, opr2_reg, *target, false /* signed */); ++ break; ++ ++ case 0x06: //less_equal ++ __ bge_long(opr2_reg, opr1_reg, *target, false /* signed */); ++ break; ++ ++ default: ++ Unimplemented(); ++ } ++ %} ++ ++ ++ ins_pc_relative(1); ++ ins_pipe( pipe_alu_branch ); ++%} ++ ++instruct branchConL_regL_immL_long(cmpOp cmp, mRegL src1, immL src2, label labl) %{ ++ match( If cmp (CmpL src1 src2) ); ++ effect(USE labl); ++ format %{ "BR$cmp $src1, $src2, $labl #@branchConL_regL_immL_long" %} ++ ins_cost(180); ++ ++ ins_encode %{ ++ Register opr1_reg = as_Register($src1$$reg); ++ Register opr2_reg = AT; ++ ++ Label* target = $labl$$label; ++ int flag = $cmp$$cmpcode; ++ ++ __ li(opr2_reg, $src2$$constant); ++ ++ switch(flag) { ++ case 0x01: //equal ++ __ beq_long(opr1_reg, opr2_reg, *target); ++ break; ++ ++ case 0x02: //not_equal ++ __ bne_long(opr1_reg, opr2_reg, *target); ++ break; ++ ++ case 0x03: //greater ++ __ blt_long(opr2_reg, opr1_reg, *target, true /* signed */); ++ break; ++ ++ case 0x04: //greater_equal ++ __ bge_long(opr1_reg, opr2_reg, *target, true /* signed */); ++ break; ++ ++ case 0x05: //less ++ __ blt_long(opr1_reg, opr2_reg, *target, true /* signed */); ++ break; ++ ++ case 0x06: //less_equal ++ __ bge_long(opr2_reg, opr1_reg, *target, true /* signed */); ++ break; ++ ++ default: ++ Unimplemented(); ++ } ++ %} ++ ++ ++ ins_pc_relative(1); ++ ins_pipe( pipe_alu_branch ); ++%} ++ ++instruct branchConUL_regL_immL_long(cmpOp cmp, mRegL src1, immL src2, label labl) %{ ++ match( If cmp (CmpUL src1 src2) ); ++ effect(USE labl); ++ format %{ "BR$cmp $src1, $src2, $labl #@branchConUL_regL_immL_long" %} ++ ins_cost(180); ++ ++ ins_encode %{ ++ Register opr1_reg = as_Register($src1$$reg); ++ Register opr2_reg = AT; ++ ++ Label* target = $labl$$label; ++ int flag = $cmp$$cmpcode; ++ ++ __ li(opr2_reg, $src2$$constant); ++ ++ switch(flag) { ++ case 0x01: //equal ++ __ beq_long(opr1_reg, opr2_reg, *target); ++ break; ++ ++ case 0x02: //not_equal ++ __ bne_long(opr1_reg, opr2_reg, *target); ++ break; ++ ++ case 0x03: //greater ++ __ blt_long(opr2_reg, opr1_reg, *target, false /* signed */); ++ break; ++ ++ case 0x04: //greater_equal ++ __ bge_long(opr1_reg, opr2_reg, *target, false /* signed */); ++ break; ++ ++ case 0x05: //less ++ __ blt_long(opr1_reg, opr2_reg, *target, false /* signed */); ++ break; ++ ++ case 0x06: //less_equal ++ __ bge_long(opr2_reg, opr1_reg, *target, false /* signed */); ++ break; ++ ++ default: ++ Unimplemented(); ++ } ++ %} ++ ++ ++ ins_pc_relative(1); ++ ins_pipe( pipe_alu_branch ); ++%} ++ ++//FIXME ++instruct branchConF_reg_reg_long(cmpOp cmp, regF src1, regF src2, label labl) %{ ++ match( If cmp (CmpF src1 src2) ); ++ effect(USE labl); ++ format %{ "BR$cmp $src1, $src2, $labl #@branchConF_reg_reg_long" %} ++ ++ ins_encode %{ ++ FloatRegister reg_op1 = $src1$$FloatRegister; ++ FloatRegister reg_op2 = $src2$$FloatRegister; ++ Label* L = $labl$$label; ++ int flag = $cmp$$cmpcode; ++ ++ switch(flag) { ++ case 0x01: //equal ++ __ fcmp_ceq_s(FCC0, reg_op1, reg_op2); ++ __ bc1t_long(*L); ++ break; ++ case 0x02: //not_equal ++ __ fcmp_ceq_s(FCC0, reg_op1, reg_op2); ++ __ bc1f_long(*L); ++ break; ++ case 0x03: //greater ++ __ fcmp_cule_s(FCC0, reg_op1, reg_op2); ++ __ bc1f_long(*L); ++ break; ++ case 0x04: //greater_equal ++ __ fcmp_cult_s(FCC0, reg_op1, reg_op2); ++ __ bc1f_long(*L); ++ break; ++ case 0x05: //less ++ __ fcmp_cult_s(FCC0, reg_op1, reg_op2); ++ __ bc1t_long(*L); ++ break; ++ case 0x06: //less_equal ++ __ fcmp_cule_s(FCC0, reg_op1, reg_op2); ++ __ bc1t_long(*L); ++ break; ++ default: ++ Unimplemented(); ++ } ++ %} ++ ++ ins_pc_relative(1); ++ ins_pipe(pipe_slow); ++%} ++ ++instruct branchConD_reg_reg_long(cmpOp cmp, regD src1, regD src2, label labl) %{ ++ match( If cmp (CmpD src1 src2) ); ++ effect(USE labl); ++ format %{ "BR$cmp $src1, $src2, $labl #@branchConD_reg_reg_long" %} ++ ++ ins_encode %{ ++ FloatRegister reg_op1 = $src1$$FloatRegister; ++ FloatRegister reg_op2 = $src2$$FloatRegister; ++ Label* L = $labl$$label; ++ int flag = $cmp$$cmpcode; ++ ++ switch(flag) { ++ case 0x01: //equal ++ __ fcmp_ceq_d(FCC0, reg_op1, reg_op2); ++ __ bc1t_long(*L); ++ break; ++ case 0x02: //not_equal ++ // c_ueq_d cannot distinguish NaN from equal. Double.isNaN(Double) is implemented by 'f != f', so the use of c_ueq_d causes bugs. ++ __ fcmp_ceq_d(FCC0, reg_op1, reg_op2); ++ __ bc1f_long(*L); ++ break; ++ case 0x03: //greater ++ __ fcmp_cule_d(FCC0, reg_op1, reg_op2); ++ __ bc1f_long(*L); ++ break; ++ case 0x04: //greater_equal ++ __ fcmp_cult_d(FCC0, reg_op1, reg_op2); ++ __ bc1f_long(*L); ++ break; ++ case 0x05: //less ++ __ fcmp_cult_d(FCC0, reg_op1, reg_op2); ++ __ bc1t_long(*L); ++ break; ++ case 0x06: //less_equal ++ __ fcmp_cule_d(FCC0, reg_op1, reg_op2); ++ __ bc1t_long(*L); ++ break; ++ default: ++ Unimplemented(); ++ } ++ %} ++ ++ ins_pc_relative(1); ++ ins_pipe(pipe_slow); ++%} ++ ++ ++// ============================================================================ ++// Branch Instructions -- short offset versions ++ ++// Jump Direct ++instruct jmpDir_short(label labl) %{ ++ match(Goto); ++ effect(USE labl); ++ ++ ins_cost(300); ++ format %{ "JMP $labl #@jmpDir_short" %} ++ ++ ins_encode %{ ++ Label &L = *($labl$$label); ++ if(&L) ++ __ b(L); ++ else ++ __ b(int(0)); ++ %} ++ ++ ins_pipe( pipe_jump ); ++ ins_pc_relative(1); ++ ins_short_branch(1); ++%} ++ ++// Jump Direct Conditional - Label defines a relative address from Jcc+1 ++instruct jmpLoopEnd_short(cmpOp cop, mRegI src1, mRegI src2, label labl) %{ ++ match(CountedLoopEnd cop (CmpI src1 src2)); ++ effect(USE labl); ++ ++ ins_cost(300); ++ format %{ "J$cop $src1, $src2, $labl\t# Loop end @ jmpLoopEnd_short" %} ++ ins_encode %{ ++ Register op1 = $src1$$Register; ++ Register op2 = $src2$$Register; ++ Label &L = *($labl$$label); ++ int flag = $cop$$cmpcode; ++ ++ switch(flag) { ++ case 0x01: //equal ++ if (&L) ++ __ beq(op1, op2, L); ++ else ++ __ beq(op1, op2, (int)0); ++ break; ++ case 0x02: //not_equal ++ if (&L) ++ __ bne(op1, op2, L); ++ else ++ __ bne(op1, op2, (int)0); ++ break; ++ case 0x03: //above ++ if (&L) ++ __ blt(op2, op1, L); ++ else ++ __ blt(op2, op1, (int)0); ++ break; ++ case 0x04: //above_equal ++ if (&L) ++ __ bge(op1, op2, L); ++ else ++ __ bge(op1, op2, (int)0); ++ break; ++ case 0x05: //below ++ if (&L) ++ __ blt(op1, op2, L); ++ else ++ __ blt(op1, op2, (int)0); ++ break; ++ case 0x06: //below_equal ++ if (&L) ++ __ bge(op2, op1, L); ++ else ++ __ bge(op2, op1, (int)0); ++ break; ++ default: ++ Unimplemented(); ++ } ++ %} ++ ins_pipe( pipe_jump ); ++ ins_pc_relative(1); ++ ins_short_branch(1); ++%} ++ ++instruct jmpLoopEnd_reg_immI_short(cmpOp cop, mRegI src1, immI src2, label labl) %{ ++ match(CountedLoopEnd cop (CmpI src1 src2)); ++ effect(USE labl); ++ ++ ins_cost(300); ++ format %{ "J$cop $src1, $src2, $labl\t# Loop end @ jmpLoopEnd_reg_immI_short" %} ++ ins_encode %{ ++ Register op1 = $src1$$Register; ++ Register op2 = AT; ++ Label &L = *($labl$$label); ++ int flag = $cop$$cmpcode; ++ ++ __ li(op2, $src2$$constant); ++ ++ switch(flag) { ++ case 0x01: //equal ++ if (&L) ++ __ beq(op1, op2, L); ++ else ++ __ beq(op1, op2, (int)0); ++ break; ++ case 0x02: //not_equal ++ if (&L) ++ __ bne(op1, op2, L); ++ else ++ __ bne(op1, op2, (int)0); ++ break; ++ case 0x03: //above ++ if (&L) ++ __ blt(op2, op1, L); ++ else ++ __ blt(op2, op1, (int)0); ++ break; ++ case 0x04: //above_equal ++ if (&L) ++ __ bge(op1, op2, L); ++ else ++ __ bge(op1, op2, (int)0); ++ break; ++ case 0x05: //below ++ if (&L) ++ __ blt(op1, op2, L); ++ else ++ __ blt(op1, op2, (int)0); ++ break; ++ case 0x06: //below_equal ++ if (&L) ++ __ bge(op2, op1, L); ++ else ++ __ bge(op2, op1, (int)0); ++ break; ++ default: ++ Unimplemented(); ++ } ++ %} ++ ins_pipe( pipe_jump ); ++ ins_pc_relative(1); ++ ins_short_branch(1); ++%} ++ ++ ++// This match pattern is created for StoreIConditional since I cannot match IfNode without a RegFlags! ++instruct jmpCon_flags_short(cmpOp cop, FlagsReg cr, label labl) %{ ++ match(If cop cr); ++ effect(USE labl); ++ ++ ins_cost(300); ++ format %{ "J$cop $labl #LoongArch uses T0 as equivalent to eflag @jmpCon_flags_short" %} ++ ++ ins_encode %{ ++ Label &L = *($labl$$label); ++ switch($cop$$cmpcode) { ++ case 0x01: //equal ++ if (&L) ++ __ bnez($cr$$Register, L); ++ else ++ __ bnez($cr$$Register, (int)0); ++ break; ++ case 0x02: //not equal ++ if (&L) ++ __ beqz($cr$$Register, L); ++ else ++ __ beqz($cr$$Register, (int)0); ++ break; ++ default: ++ Unimplemented(); ++ } ++ %} ++ ++ ins_pipe( pipe_jump ); ++ ins_pc_relative(1); ++ ins_short_branch(1); ++%} ++ ++// Conditional jumps ++instruct branchConP_0_short(cmpOpU cmp, mRegP op1, immP_0 zero, label labl) %{ ++ match(If cmp (CmpP op1 zero)); ++ effect(USE labl); ++ ++ ins_cost(180); ++ format %{ "b$cmp $op1, R0, $labl #@branchConP_0_short" %} ++ ++ ins_encode %{ ++ Register op1 = $op1$$Register; ++ Label &L = *($labl$$label); ++ int flag = $cmp$$cmpcode; ++ ++ switch(flag) { ++ case 0x01: //equal ++ if (&L) ++ __ beqz(op1, L); ++ else ++ __ beqz(op1, (int)0); ++ break; ++ case 0x02: //not_equal ++ if (&L) ++ __ bnez(op1, L); ++ else ++ __ bnez(op1, (int)0); ++ break; ++ default: ++ Unimplemented(); ++ } ++ %} ++ ++ ins_pc_relative(1); ++ ins_pipe( pipe_alu_branch ); ++ ins_short_branch(1); ++%} ++ ++instruct branchConN2P_0_short(cmpOpU cmp, mRegN op1, immP_0 zero, label labl) %{ ++ match(If cmp (CmpP (DecodeN op1) zero)); ++ predicate(Universe::narrow_oop_base() == NULL && Universe::narrow_oop_shift() == 0); ++ effect(USE labl); ++ ++ ins_cost(180); ++ format %{ "b$cmp $op1, R0, $labl #@branchConN2P_0_short" %} ++ ++ ins_encode %{ ++ Register op1 = $op1$$Register; ++ Label &L = *($labl$$label); ++ int flag = $cmp$$cmpcode; ++ ++ switch(flag) ++ { ++ case 0x01: //equal ++ if (&L) ++ __ beqz(op1, L); ++ else ++ __ beqz(op1, (int)0); ++ break; ++ case 0x02: //not_equal ++ if (&L) ++ __ bnez(op1, L); ++ else ++ __ bnez(op1, (int)0); ++ break; ++ default: ++ Unimplemented(); ++ } ++ %} ++ ++ ins_pc_relative(1); ++ ins_pipe( pipe_alu_branch ); ++ ins_short_branch(1); ++%} ++ ++ ++instruct branchConP_short(cmpOpU cmp, mRegP op1, mRegP op2, label labl) %{ ++ match(If cmp (CmpP op1 op2)); ++// predicate(can_branch_register(_kids[0]->_leaf, _kids[1]->_leaf)); ++ effect(USE labl); ++ ++ ins_cost(200); ++ format %{ "b$cmp $op1, $op2, $labl #@branchConP_short" %} ++ ++ ins_encode %{ ++ Register op1 = $op1$$Register; ++ Register op2 = $op2$$Register; ++ Label &L = *($labl$$label); ++ int flag = $cmp$$cmpcode; ++ ++ switch(flag) { ++ case 0x01: //equal ++ if (&L) ++ __ beq(op1, op2, L); ++ else ++ __ beq(op1, op2, (int)0); ++ break; ++ case 0x02: //not_equal ++ if (&L) ++ __ bne(op1, op2, L); ++ else ++ __ bne(op1, op2, (int)0); ++ break; ++ case 0x03: //above ++ if (&L) ++ __ bltu(op2, op1, L); ++ else ++ __ bltu(op2, op1, (int)0); ++ break; ++ case 0x04: //above_equal ++ if (&L) ++ __ bgeu(op1, op2, L); ++ else ++ __ bgeu(op1, op2, (int)0); ++ break; ++ case 0x05: //below ++ if (&L) ++ __ bltu(op1, op2, L); ++ else ++ __ bltu(op1, op2, (int)0); ++ break; ++ case 0x06: //below_equal ++ if (&L) ++ __ bgeu(op2, op1, L); ++ else ++ __ bgeu(op2, op1, (int)0); ++ break; ++ default: ++ Unimplemented(); ++ } ++ %} ++ ++ ins_pc_relative(1); ++ ins_pipe( pipe_alu_branch ); ++ ins_short_branch(1); ++%} ++ ++instruct cmpN_null_branch_short(cmpOp cmp, mRegN op1, immN_0 null, label labl) %{ ++ match(If cmp (CmpN op1 null)); ++ effect(USE labl); ++ ++ ins_cost(180); ++ format %{ "CMP $op1,0\t! compressed ptr\n\t" ++ "BP$cmp $labl @ cmpN_null_branch_short" %} ++ ins_encode %{ ++ Register op1 = $op1$$Register; ++ Label &L = *($labl$$label); ++ int flag = $cmp$$cmpcode; ++ ++ switch(flag) { ++ case 0x01: //equal ++ if (&L) ++ __ beqz(op1, L); ++ else ++ __ beqz(op1, (int)0); ++ break; ++ case 0x02: //not_equal ++ if (&L) ++ __ bnez(op1, L); ++ else ++ __ bnez(op1, (int)0); ++ break; ++ default: ++ Unimplemented(); ++ } ++ %} ++//TODO: pipe_branchP or create pipe_branchN LEE ++ ins_pc_relative(1); ++ ins_pipe( pipe_alu_branch ); ++ ins_short_branch(1); ++%} ++ ++instruct cmpN_reg_branch_short(cmpOp cmp, mRegN op1, mRegN op2, label labl) %{ ++ match(If cmp (CmpN op1 op2)); ++ effect(USE labl); ++ ++ ins_cost(180); ++ format %{ "CMP $op1,$op2\t! compressed ptr\n\t" ++ "BP$cmp $labl @ cmpN_reg_branch_short" %} ++ ins_encode %{ ++ Register op1_reg = $op1$$Register; ++ Register op2_reg = $op2$$Register; ++ Label &L = *($labl$$label); ++ int flag = $cmp$$cmpcode; ++ ++ switch(flag) { ++ case 0x01: //equal ++ if (&L) ++ __ beq(op1_reg, op2_reg, L); ++ else ++ __ beq(op1_reg, op2_reg, (int)0); ++ break; ++ case 0x02: //not_equal ++ if (&L) ++ __ bne(op1_reg, op2_reg, L); ++ else ++ __ bne(op1_reg, op2_reg, (int)0); ++ break; ++ case 0x03: //above ++ if (&L) ++ __ bltu(op2_reg, op1_reg, L); ++ else ++ __ bltu(op2_reg, op1_reg, (int)0); ++ break; ++ case 0x04: //above_equal ++ if (&L) ++ __ bgeu(op1_reg, op2_reg, L); ++ else ++ __ bgeu(op1_reg, op2_reg, (int)0); ++ break; ++ case 0x05: //below ++ if (&L) ++ __ bltu(op1_reg, op2_reg, L); ++ else ++ __ bltu(op1_reg, op2_reg, (int)0); ++ break; ++ case 0x06: //below_equal ++ if (&L) ++ __ bgeu(op2_reg, op1_reg, L); ++ else ++ __ bgeu(op2_reg, op1_reg, (int)0); ++ break; ++ default: ++ Unimplemented(); ++ } ++ %} ++ ins_pc_relative(1); ++ ins_pipe( pipe_alu_branch ); ++ ins_short_branch(1); ++%} ++ ++instruct branchConIU_reg_reg_short(cmpOpU cmp, mRegI src1, mRegI src2, label labl) %{ ++ match( If cmp (CmpU src1 src2) ); ++ effect(USE labl); ++ format %{ "BR$cmp $src1, $src2, $labl #@branchConIU_reg_reg_short" %} ++ ++ ins_encode %{ ++ Register op1 = $src1$$Register; ++ Register op2 = $src2$$Register; ++ Label &L = *($labl$$label); ++ int flag = $cmp$$cmpcode; ++ ++ switch(flag) { ++ case 0x01: //equal ++ if (&L) ++ __ beq(op1, op2, L); ++ else ++ __ beq(op1, op2, (int)0); ++ break; ++ case 0x02: //not_equal ++ if (&L) ++ __ bne(op1, op2, L); ++ else ++ __ bne(op1, op2, (int)0); ++ break; ++ case 0x03: //above ++ if (&L) ++ __ bltu(op2, op1, L); ++ else ++ __ bltu(op2, op1, (int)0); ++ break; ++ case 0x04: //above_equal ++ if (&L) ++ __ bgeu(op1, op2, L); ++ else ++ __ bgeu(op1, op2, (int)0); ++ break; ++ case 0x05: //below ++ if (&L) ++ __ bltu(op1, op2, L); ++ else ++ __ bltu(op1, op2, (int)0); ++ break; ++ case 0x06: //below_equal ++ if (&L) ++ __ bgeu(op2, op1, L); ++ else ++ __ bgeu(op2, op1, (int)0); ++ break; ++ default: ++ Unimplemented(); ++ } ++ %} ++ ++ ins_pc_relative(1); ++ ins_pipe( pipe_alu_branch ); ++ ins_short_branch(1); ++%} ++ ++ ++instruct branchConIU_reg_imm_short(cmpOpU cmp, mRegI src1, immI src2, label labl) %{ ++ match( If cmp (CmpU src1 src2) ); ++ effect(USE labl); ++ format %{ "BR$cmp $src1, $src2, $labl #@branchConIU_reg_imm_short" %} ++ ++ ins_encode %{ ++ Register op1 = $src1$$Register; ++ int val = $src2$$constant; ++ Label &L = *($labl$$label); ++ int flag = $cmp$$cmpcode; ++ ++ __ li(AT, val); ++ switch(flag) { ++ case 0x01: //equal ++ if (&L) ++ __ beq(op1, AT, L); ++ else ++ __ beq(op1, AT, (int)0); ++ break; ++ case 0x02: //not_equal ++ if (&L) ++ __ bne(op1, AT, L); ++ else ++ __ bne(op1, AT, (int)0); ++ break; ++ case 0x03: //above ++ if (&L) ++ __ bltu(AT, op1, L); ++ else ++ __ bltu(AT, op1, (int)0); ++ break; ++ case 0x04: //above_equal ++ if (&L) ++ __ bgeu(op1, AT, L); ++ else ++ __ bgeu(op1, AT, (int)0); ++ break; ++ case 0x05: //below ++ if (&L) ++ __ bltu(op1, AT, L); ++ else ++ __ bltu(op1, AT, (int)0); ++ break; ++ case 0x06: //below_equal ++ if (&L) ++ __ bgeu(AT, op1, L); ++ else ++ __ bgeu(AT, op1, (int)0); ++ break; ++ default: ++ Unimplemented(); ++ } ++ %} ++ ++ ins_pc_relative(1); ++ ins_pipe( pipe_alu_branch ); ++ ins_short_branch(1); ++%} ++ ++instruct branchConI_reg_reg_short(cmpOp cmp, mRegI src1, mRegI src2, label labl) %{ ++ match( If cmp (CmpI src1 src2) ); ++ effect(USE labl); ++ format %{ "BR$cmp $src1, $src2, $labl #@branchConI_reg_reg_short" %} ++ ++ ins_encode %{ ++ Register op1 = $src1$$Register; ++ Register op2 = $src2$$Register; ++ Label &L = *($labl$$label); ++ int flag = $cmp$$cmpcode; ++ ++ switch(flag) { ++ case 0x01: //equal ++ if (&L) ++ __ beq(op1, op2, L); ++ else ++ __ beq(op1, op2, (int)0); ++ break; ++ case 0x02: //not_equal ++ if (&L) ++ __ bne(op1, op2, L); ++ else ++ __ bne(op1, op2, (int)0); ++ break; ++ case 0x03: //above ++ if (&L) ++ __ blt(op2, op1, L); ++ else ++ __ blt(op2, op1, (int)0); ++ break; ++ case 0x04: //above_equal ++ if (&L) ++ __ bge(op1, op2, L); ++ else ++ __ bge(op1, op2, (int)0); ++ break; ++ case 0x05: //below ++ if (&L) ++ __ blt(op1, op2, L); ++ else ++ __ blt(op1, op2, (int)0); ++ break; ++ case 0x06: //below_equal ++ if (&L) ++ __ bge(op2, op1, L); ++ else ++ __ bge(op2, op1, (int)0); ++ break; ++ default: ++ Unimplemented(); ++ } ++ %} ++ ++ ins_pc_relative(1); ++ ins_pipe( pipe_alu_branch ); ++ ins_short_branch(1); ++%} ++ ++instruct branchConI_reg_immI_0_short(cmpOp cmp, mRegI src1, immI_0 src2, label labl) %{ ++ match( If cmp (CmpI src1 src2) ); ++ effect(USE labl); ++ ins_cost(170); ++ format %{ "BR$cmp $src1, $src2, $labl #@branchConI_reg_immI_0_short" %} ++ ++ ins_encode %{ ++ Register op1 = $src1$$Register; ++ Label &L = *($labl$$label); ++ int flag = $cmp$$cmpcode; ++ ++ switch(flag) { ++ case 0x01: //equal ++ if (&L) ++ __ beqz(op1, L); ++ else ++ __ beqz(op1, (int)0); ++ break; ++ case 0x02: //not_equal ++ if (&L) ++ __ bnez(op1, L); ++ else ++ __ bnez(op1, (int)0); ++ break; ++ case 0x03: //greater ++ if (&L) ++ __ blt(R0, op1, L); ++ else ++ __ blt(R0, op1, (int)0); ++ break; ++ case 0x04: //greater_equal ++ if (&L) ++ __ bge(op1, R0, L); ++ else ++ __ bge(op1, R0, (int)0); ++ break; ++ case 0x05: //less ++ if (&L) ++ __ blt(op1, R0, L); ++ else ++ __ blt(op1, R0, (int)0); ++ break; ++ case 0x06: //less_equal ++ if (&L) ++ __ bge(R0, op1, L); ++ else ++ __ bge(R0, op1, (int)0); ++ break; ++ default: ++ Unimplemented(); ++ } ++ %} ++ ++ ins_pc_relative(1); ++ ins_pipe( pipe_alu_branch ); ++ ins_short_branch(1); ++%} ++ ++ ++instruct branchConI_reg_imm_short(cmpOp cmp, mRegI src1, immI src2, label labl) %{ ++ match( If cmp (CmpI src1 src2) ); ++ effect(USE labl); ++ ins_cost(200); ++ format %{ "BR$cmp $src1, $src2, $labl #@branchConI_reg_imm_short" %} ++ ++ ins_encode %{ ++ Register op1 = $src1$$Register; ++ int val = $src2$$constant; ++ Label &L = *($labl$$label); ++ int flag = $cmp$$cmpcode; ++ ++ __ li(AT, val); ++ switch(flag) { ++ case 0x01: //equal ++ if (&L) ++ __ beq(op1, AT, L); ++ else ++ __ beq(op1, AT, (int)0); ++ break; ++ case 0x02: //not_equal ++ if (&L) ++ __ bne(op1, AT, L); ++ else ++ __ bne(op1, AT, (int)0); ++ break; ++ case 0x03: //greater ++ if (&L) ++ __ blt(AT, op1, L); ++ else ++ __ blt(AT, op1, (int)0); ++ break; ++ case 0x04: //greater_equal ++ if (&L) ++ __ bge(op1, AT, L); ++ else ++ __ bge(op1, AT, (int)0); ++ break; ++ case 0x05: //less ++ if (&L) ++ __ blt(op1, AT, L); ++ else ++ __ blt(op1, AT, (int)0); ++ break; ++ case 0x06: //less_equal ++ if (&L) ++ __ bge(AT, op1, L); ++ else ++ __ bge(AT, op1, (int)0); ++ break; ++ default: ++ Unimplemented(); ++ } ++ %} ++ ++ ins_pc_relative(1); ++ ins_pipe( pipe_alu_branch ); ++ ins_short_branch(1); ++%} ++ ++instruct branchConIU_reg_immI_0_short(cmpOpU cmp, mRegI src1, immI_0 zero, label labl) %{ ++ match( If cmp (CmpU src1 zero) ); ++ effect(USE labl); ++ format %{ "BR$cmp $src1, zero, $labl #@branchConIU_reg_immI_0_short" %} ++ ++ ins_encode %{ ++ Register op1 = $src1$$Register; ++ Label &L = *($labl$$label); ++ int flag = $cmp$$cmpcode; ++ ++ switch(flag) { ++ case 0x01: //equal ++ if (&L) ++ __ beqz(op1, L); ++ else ++ __ beqz(op1, (int)0); ++ break; ++ case 0x02: //not_equal ++ if (&L) ++ __ bnez(op1, L); ++ else ++ __ bnez(op1, (int)0); ++ break; ++ case 0x03: //above ++ if (&L) ++ __ bnez(op1, L); ++ else ++ __ bnez(op1, (int)0); ++ break; ++ case 0x04: //above_equal ++ if (&L) ++ __ b(L); ++ else ++ __ b((int)0); ++ break; ++ case 0x05: //below ++ return; ++ break; ++ case 0x06: //below_equal ++ if (&L) ++ __ beqz(op1, L); ++ else ++ __ beqz(op1, (int)0); ++ break; ++ default: ++ Unimplemented(); ++ } ++ %} ++ ++ ins_pc_relative(1); ++ ins_pipe( pipe_alu_branch ); ++ ins_short_branch(1); ++%} ++ ++ ++instruct branchConL_regL_regL_short(cmpOp cmp, mRegLorI2L src1, mRegLorI2L src2, label labl) %{ ++ match( If cmp (CmpL src1 src2) ); ++ effect(USE labl); ++ format %{ "BR$cmp $src1, $src2, $labl #@branchConL_regL_regL_short" %} ++ ins_cost(250); ++ ++ ins_encode %{ ++ Register opr1_reg = as_Register($src1$$reg); ++ Register opr2_reg = as_Register($src2$$reg); ++ ++ Label &target = *($labl$$label); ++ int flag = $cmp$$cmpcode; ++ ++ switch(flag) { ++ case 0x01: //equal ++ if (&target) ++ __ beq(opr1_reg, opr2_reg, target); ++ else ++ __ beq(opr1_reg, opr2_reg, (int)0); ++ break; ++ case 0x02: //not_equal ++ if(&target) ++ __ bne(opr1_reg, opr2_reg, target); ++ else ++ __ bne(opr1_reg, opr2_reg, (int)0); ++ break; ++ case 0x03: //greater ++ if (&target) ++ __ blt(opr2_reg, opr1_reg, target); ++ else ++ __ blt(opr2_reg, opr1_reg, (int)0); ++ break; ++ case 0x04: //greater_equal ++ if (&target) ++ __ bge(opr1_reg, opr2_reg, target); ++ else ++ __ bge(opr1_reg, opr2_reg, (int)0); ++ break; ++ case 0x05: //less ++ if (&target) ++ __ blt(opr1_reg, opr2_reg, target); ++ else ++ __ blt(opr1_reg, opr2_reg, (int)0); ++ break; ++ case 0x06: //less_equal ++ if (&target) ++ __ bge(opr2_reg, opr1_reg, target); ++ else ++ __ bge(opr2_reg, opr1_reg, (int)0); ++ break; ++ default: ++ Unimplemented(); ++ } ++ %} ++ ++ ++ ins_pc_relative(1); ++ ins_pipe( pipe_alu_branch ); ++ ins_short_branch(1); ++%} ++ ++instruct branchConUL_regL_regL_short(cmpOp cmp, mRegLorI2L src1, mRegLorI2L src2, label labl) %{ ++ match( If cmp (CmpUL src1 src2) ); ++ effect(USE labl); ++ format %{ "BR$cmp $src1, $src2, $labl #@branchConUL_regL_regL_short" %} ++ ins_cost(250); ++ ++ ins_encode %{ ++ Register opr1_reg = as_Register($src1$$reg); ++ Register opr2_reg = as_Register($src2$$reg); ++ ++ Label &target = *($labl$$label); ++ int flag = $cmp$$cmpcode; ++ ++ switch(flag) { ++ case 0x01: //equal ++ if (&target) ++ __ beq(opr1_reg, opr2_reg, target); ++ else ++ __ beq(opr1_reg, opr2_reg, (int)0); ++ break; ++ case 0x02: //not_equal ++ if(&target) ++ __ bne(opr1_reg, opr2_reg, target); ++ else ++ __ bne(opr1_reg, opr2_reg, (int)0); ++ break; ++ case 0x03: //greater ++ if (&target) ++ __ bltu(opr2_reg, opr1_reg, target); ++ else ++ __ bltu(opr2_reg, opr1_reg, (int)0); ++ break; ++ case 0x04: //greater_equal ++ if (&target) ++ __ bgeu(opr1_reg, opr2_reg, target); ++ else ++ __ bgeu(opr1_reg, opr2_reg, (int)0); ++ break; ++ case 0x05: //less ++ if (&target) ++ __ bltu(opr1_reg, opr2_reg, target); ++ else ++ __ bltu(opr1_reg, opr2_reg, (int)0); ++ break; ++ case 0x06: //less_equal ++ if (&target) ++ __ bgeu(opr2_reg, opr1_reg, target); ++ else ++ __ bgeu(opr2_reg, opr1_reg, (int)0); ++ break; ++ default: ++ Unimplemented(); ++ } ++ %} ++ ++ ++ ins_pc_relative(1); ++ ins_pipe( pipe_alu_branch ); ++ ins_short_branch(1); ++%} ++ ++instruct branchConL_regL_immL_0_short(cmpOp cmp, mRegL src1, immL_0 zero, label labl) %{ ++ match( If cmp (CmpL src1 zero) ); ++ effect(USE labl); ++ format %{ "BR$cmp $src1, zero, $labl #@branchConL_regL_immL_0_short" %} ++ ins_cost(150); ++ ++ ins_encode %{ ++ Register opr1_reg = as_Register($src1$$reg); ++ Label &target = *($labl$$label); ++ int flag = $cmp$$cmpcode; ++ ++ switch(flag) { ++ case 0x01: //equal ++ if (&target) ++ __ beqz(opr1_reg, target); ++ else ++ __ beqz(opr1_reg, int(0)); ++ break; ++ ++ case 0x02: //not_equal ++ if (&target) ++ __ bnez(opr1_reg, target); ++ else ++ __ bnez(opr1_reg, (int)0); ++ break; ++ ++ case 0x03: //greater ++ if (&target) ++ __ blt(R0, opr1_reg, target); ++ else ++ __ blt(R0, opr1_reg, (int)0); ++ break; ++ ++ case 0x04: //greater_equal ++ if (&target) ++ __ bge(opr1_reg, R0, target); ++ else ++ __ bge(opr1_reg, R0, (int)0); ++ break; ++ ++ case 0x05: //less ++ if (&target) ++ __ blt(opr1_reg, R0, target); ++ else ++ __ blt(opr1_reg, R0, (int)0); ++ break; ++ ++ case 0x06: //less_equal ++ if (&target) ++ __ bge(R0, opr1_reg, target); ++ else ++ __ bge(R0, opr1_reg, int(0)); ++ break; ++ ++ default: ++ Unimplemented(); ++ } ++ %} ++ ++ ++ ins_pc_relative(1); ++ ins_pipe( pipe_alu_branch ); ++ ins_short_branch(1); ++%} ++ ++instruct branchConUL_regL_immL_0_short(cmpOp cmp, mRegL src1, immL_0 zero, label labl) %{ ++ match( If cmp (CmpUL src1 zero) ); ++ effect(USE labl); ++ format %{ "BR$cmp $src1, zero, $labl #@branchConUL_regL_immL_0_short" %} ++ ins_cost(150); ++ ++ ins_encode %{ ++ Register opr1_reg = as_Register($src1$$reg); ++ Label &target = *($labl$$label); ++ int flag = $cmp$$cmpcode; ++ ++ switch(flag) { ++ case 0x01: //equal ++ if (&target) ++ __ beqz(opr1_reg, target); ++ else ++ __ beqz(opr1_reg, int(0)); ++ break; ++ ++ case 0x02: //not_equal ++ if (&target) ++ __ bnez(opr1_reg, target); ++ else ++ __ bnez(opr1_reg, (int)0); ++ break; ++ ++ case 0x03: //greater ++ if (&target) ++ __ bltu(R0, opr1_reg, target); ++ else ++ __ bltu(R0, opr1_reg, (int)0); ++ break; ++ ++ case 0x04: //greater_equal ++ if (&target) ++ __ bgeu(opr1_reg, R0, target); ++ else ++ __ bgeu(opr1_reg, R0, (int)0); ++ break; ++ ++ case 0x05: //less ++ if (&target) ++ __ bltu(opr1_reg, R0, target); ++ else ++ __ bltu(opr1_reg, R0, (int)0); ++ break; ++ ++ case 0x06: //less_equal ++ if (&target) ++ __ bgeu(R0, opr1_reg, target); ++ else ++ __ bgeu(R0, opr1_reg, int(0)); ++ break; ++ ++ default: ++ Unimplemented(); ++ } ++ %} ++ ++ ++ ins_pc_relative(1); ++ ins_pipe( pipe_alu_branch ); ++ ins_short_branch(1); ++%} ++ ++instruct branchConL_regL_immL_short(cmpOp cmp, mRegL src1, immL src2, label labl) %{ ++ match( If cmp (CmpL src1 src2) ); ++ effect(USE labl); ++ format %{ "BR$cmp $src1, $src2, $labl #@branchConL_regL_immL_short" %} ++ ins_cost(180); ++ ++ ins_encode %{ ++ Register opr1_reg = as_Register($src1$$reg); ++ Register opr2_reg = AT; ++ ++ Label &target = *($labl$$label); ++ int flag = $cmp$$cmpcode; ++ ++ __ li(opr2_reg, $src2$$constant); ++ ++ switch(flag) { ++ case 0x01: //equal ++ if (&target) ++ __ beq(opr1_reg, opr2_reg, target); ++ else ++ __ beq(opr1_reg, opr2_reg, (int)0); ++ break; ++ ++ case 0x02: //not_equal ++ if(&target) ++ __ bne(opr1_reg, opr2_reg, target); ++ else ++ __ bne(opr1_reg, opr2_reg, (int)0); ++ break; ++ ++ case 0x03: //greater ++ if (&target) ++ __ blt(opr2_reg, opr1_reg, target); ++ else ++ __ blt(opr2_reg, opr1_reg, (int)0); ++ break; ++ ++ case 0x04: //greater_equal ++ if (&target) ++ __ bge(opr1_reg, opr2_reg, target); ++ else ++ __ bge(opr1_reg, opr2_reg, (int)0); ++ break; ++ ++ case 0x05: //less ++ if (&target) ++ __ blt(opr1_reg, opr2_reg, target); ++ else ++ __ blt(opr1_reg, opr2_reg, (int)0); ++ break; ++ ++ case 0x06: //less_equal ++ if (&target) ++ __ bge(opr2_reg, opr1_reg, target); ++ else ++ __ bge(opr2_reg, opr1_reg, (int)0); ++ break; ++ ++ default: ++ Unimplemented(); ++ } ++ %} ++ ++ ++ ins_pc_relative(1); ++ ins_pipe( pipe_alu_branch ); ++ ins_short_branch(1); ++%} ++ ++instruct branchConUL_regL_immL_short(cmpOp cmp, mRegL src1, immL src2, label labl) %{ ++ match( If cmp (CmpUL src1 src2) ); ++ effect(USE labl); ++ format %{ "BR$cmp $src1, $src2, $labl #@branchConUL_regL_immL_short" %} ++ ins_cost(180); ++ ++ ins_encode %{ ++ Register opr1_reg = as_Register($src1$$reg); ++ Register opr2_reg = AT; ++ ++ Label &target = *($labl$$label); ++ int flag = $cmp$$cmpcode; ++ ++ __ li(opr2_reg, $src2$$constant); ++ ++ switch(flag) { ++ case 0x01: //equal ++ if (&target) ++ __ beq(opr1_reg, opr2_reg, target); ++ else ++ __ beq(opr1_reg, opr2_reg, (int)0); ++ break; ++ ++ case 0x02: //not_equal ++ if(&target) ++ __ bne(opr1_reg, opr2_reg, target); ++ else ++ __ bne(opr1_reg, opr2_reg, (int)0); ++ break; ++ ++ case 0x03: //greater ++ if (&target) ++ __ bltu(opr2_reg, opr1_reg, target); ++ else ++ __ bltu(opr2_reg, opr1_reg, (int)0); ++ break; ++ ++ case 0x04: //greater_equal ++ if (&target) ++ __ bgeu(opr1_reg, opr2_reg, target); ++ else ++ __ bgeu(opr1_reg, opr2_reg, (int)0); ++ break; ++ ++ case 0x05: //less ++ if (&target) ++ __ bltu(opr1_reg, opr2_reg, target); ++ else ++ __ bltu(opr1_reg, opr2_reg, (int)0); ++ break; ++ ++ case 0x06: //less_equal ++ if (&target) ++ __ bgeu(opr2_reg, opr1_reg, target); ++ else ++ __ bgeu(opr2_reg, opr1_reg, (int)0); ++ break; ++ ++ default: ++ Unimplemented(); ++ } ++ %} ++ ++ ++ ins_pc_relative(1); ++ ins_pipe( pipe_alu_branch ); ++ ins_short_branch(1); ++%} ++ ++//FIXME ++instruct branchConF_reg_reg_short(cmpOp cmp, regF src1, regF src2, label labl) %{ ++ match( If cmp (CmpF src1 src2) ); ++ effect(USE labl); ++ format %{ "BR$cmp $src1, $src2, $labl #@branchConF_reg_reg_short" %} ++ ++ ins_encode %{ ++ FloatRegister reg_op1 = $src1$$FloatRegister; ++ FloatRegister reg_op2 = $src2$$FloatRegister; ++ Label &L = *($labl$$label); ++ int flag = $cmp$$cmpcode; ++ ++ switch(flag) { ++ case 0x01: //equal ++ __ fcmp_ceq_s(FCC0, reg_op1, reg_op2); ++ if (&L) ++ __ bcnez(FCC0, L); ++ else ++ __ bcnez(FCC0, (int)0); ++ break; ++ case 0x02: //not_equal ++ __ fcmp_ceq_s(FCC0, reg_op1, reg_op2); ++ if (&L) ++ __ bceqz(FCC0, L); ++ else ++ __ bceqz(FCC0, (int)0); ++ break; ++ case 0x03: //greater ++ __ fcmp_cule_s(FCC0, reg_op1, reg_op2); ++ if(&L) ++ __ bceqz(FCC0, L); ++ else ++ __ bceqz(FCC0, (int)0); ++ break; ++ case 0x04: //greater_equal ++ __ fcmp_cult_s(FCC0, reg_op1, reg_op2); ++ if(&L) ++ __ bceqz(FCC0, L); ++ else ++ __ bceqz(FCC0, (int)0); ++ break; ++ case 0x05: //less ++ __ fcmp_cult_s(FCC0, reg_op1, reg_op2); ++ if(&L) ++ __ bcnez(FCC0, L); ++ else ++ __ bcnez(FCC0, (int)0); ++ break; ++ case 0x06: //less_equal ++ __ fcmp_cule_s(FCC0, reg_op1, reg_op2); ++ if(&L) ++ __ bcnez(FCC0, L); ++ else ++ __ bcnez(FCC0, (int)0); ++ break; ++ default: ++ Unimplemented(); ++ } ++ %} ++ ++ ins_pc_relative(1); ++ ins_pipe(pipe_fpu_branch); ++ ins_short_branch(1); ++%} ++ ++instruct branchConD_reg_reg_short(cmpOp cmp, regD src1, regD src2, label labl) %{ ++ match( If cmp (CmpD src1 src2) ); ++ effect(USE labl); ++ format %{ "BR$cmp $src1, $src2, $labl #@branchConD_reg_reg_short" %} ++ ++ ins_encode %{ ++ FloatRegister reg_op1 = $src1$$FloatRegister; ++ FloatRegister reg_op2 = $src2$$FloatRegister; ++ Label &L = *($labl$$label); ++ int flag = $cmp$$cmpcode; ++ ++ switch(flag) { ++ case 0x01: //equal ++ __ fcmp_ceq_d(FCC0, reg_op1, reg_op2); ++ if (&L) ++ __ bcnez(FCC0, L); ++ else ++ __ bcnez(FCC0, (int)0); ++ break; ++ case 0x02: //not_equal ++ // c_ueq_d cannot distinguish NaN from equal. Double.isNaN(Double) is implemented by 'f != f', so the use of c_ueq_d causes bugs. ++ __ fcmp_ceq_d(FCC0, reg_op1, reg_op2); ++ if (&L) ++ __ bceqz(FCC0, L); ++ else ++ __ bceqz(FCC0, (int)0); ++ break; ++ case 0x03: //greater ++ __ fcmp_cule_d(FCC0, reg_op1, reg_op2); ++ if(&L) ++ __ bceqz(FCC0, L); ++ else ++ __ bceqz(FCC0, (int)0); ++ break; ++ case 0x04: //greater_equal ++ __ fcmp_cult_d(FCC0, reg_op1, reg_op2); ++ if(&L) ++ __ bceqz(FCC0, L); ++ else ++ __ bceqz(FCC0, (int)0); ++ break; ++ case 0x05: //less ++ __ fcmp_cult_d(FCC0, reg_op1, reg_op2); ++ if(&L) ++ __ bcnez(FCC0, L); ++ else ++ __ bcnez(FCC0, (int)0); ++ break; ++ case 0x06: //less_equal ++ __ fcmp_cule_d(FCC0, reg_op1, reg_op2); ++ if(&L) ++ __ bcnez(FCC0, L); ++ else ++ __ bcnez(FCC0, (int)0); ++ break; ++ default: ++ Unimplemented(); ++ } ++ %} ++ ++ ins_pc_relative(1); ++ ins_pipe(pipe_fpu_branch); ++ ins_short_branch(1); ++%} ++ ++// =================== End of branch instructions ========================== ++ ++// Call Runtime Instruction ++instruct CallRuntimeDirect(method meth) %{ ++ match(CallRuntime ); ++ effect(USE meth); ++ ++ ins_cost(300); ++ format %{ "CALL,runtime #@CallRuntimeDirect" %} ++ ins_encode( Java_To_Runtime( meth ) ); ++ ins_pipe( pipe_slow ); ++ ins_alignment(4); ++%} ++ ++ ++ ++//------------------------MemBar Instructions------------------------------- ++//Memory barrier flavors ++ ++instruct membar_acquire() %{ ++ match(MemBarAcquire); ++ ins_cost(400); ++ ++ format %{ "MEMBAR-acquire @ membar_acquire" %} ++ ins_encode %{ ++ __ membar(Assembler::Membar_mask_bits(__ LoadLoad|__ LoadStore)); ++ %} ++ ins_pipe(empty); ++%} ++ ++instruct load_fence() %{ ++ match(LoadFence); ++ ins_cost(400); ++ ++ format %{ "MEMBAR @ load_fence" %} ++ ins_encode %{ ++ __ membar(Assembler::Membar_mask_bits(__ LoadLoad|__ LoadStore)); ++ %} ++ ins_pipe(pipe_slow); ++%} ++ ++instruct membar_acquire_lock() ++%{ ++ match(MemBarAcquireLock); ++ ins_cost(0); ++ ++ size(0); ++ format %{ "MEMBAR-acquire (acquire as part of CAS in prior FastLock so empty encoding) @ membar_acquire_lock" %} ++ ins_encode(); ++ ins_pipe(empty); ++%} ++ ++instruct membar_release() %{ ++ match(MemBarRelease); ++ ins_cost(400); ++ ++ format %{ "MEMBAR-release @ membar_release" %} ++ ++ ins_encode %{ ++ // Attention: DO NOT DELETE THIS GUY! ++ __ membar(Assembler::Membar_mask_bits(__ LoadStore|__ StoreStore)); ++ %} ++ ++ ins_pipe(pipe_slow); ++%} ++ ++instruct store_fence() %{ ++ match(StoreFence); ++ ins_cost(400); ++ ++ format %{ "MEMBAR @ store_fence" %} ++ ++ ins_encode %{ ++ __ membar(Assembler::Membar_mask_bits(__ LoadStore|__ StoreStore)); ++ %} ++ ++ ins_pipe(pipe_slow); ++%} ++ ++instruct membar_release_lock() ++%{ ++ match(MemBarReleaseLock); ++ ins_cost(0); ++ ++ size(0); ++ format %{ "MEMBAR-release-lock (release in FastUnlock so empty) @ membar_release_lock" %} ++ ins_encode(); ++ ins_pipe(empty); ++%} ++ ++ ++instruct membar_volatile() %{ ++ match(MemBarVolatile); ++ ins_cost(400); ++ ++ format %{ "MEMBAR-volatile" %} ++ ins_encode %{ ++ if( !os::is_MP() ) return; // Not needed on single CPU ++ __ membar(__ StoreLoad); ++ ++ %} ++ ins_pipe(pipe_slow); ++%} ++ ++instruct unnecessary_membar_volatile() %{ ++ match(MemBarVolatile); ++ predicate(Matcher::post_store_load_barrier(n)); ++ ins_cost(0); ++ ++ size(0); ++ format %{ "MEMBAR-volatile (unnecessary so empty encoding) @ unnecessary_membar_volatile" %} ++ ins_encode( ); ++ ins_pipe(empty); ++%} ++ ++instruct membar_storestore() %{ ++ match(MemBarStoreStore); ++ ++ ins_cost(400); ++ format %{ "MEMBAR-storestore @ membar_storestore" %} ++ ins_encode %{ ++ __ membar(__ StoreStore); ++ %} ++ ins_pipe(empty); ++%} ++ ++//----------Move Instructions-------------------------------------------------- ++instruct castX2P(mRegP dst, mRegL src) %{ ++ match(Set dst (CastX2P src)); ++ format %{ "castX2P $dst, $src @ castX2P" %} ++ ins_encode %{ ++ Register src = $src$$Register; ++ Register dst = $dst$$Register; ++ ++ if(src != dst) ++ __ move(dst, src); ++ %} ++ ins_cost(10); ++ ins_pipe( ialu_regI_mov ); ++%} ++ ++instruct castP2X(mRegL dst, mRegP src ) %{ ++ match(Set dst (CastP2X src)); ++ ++ format %{ "mov $dst, $src\t #@castP2X" %} ++ ins_encode %{ ++ Register src = $src$$Register; ++ Register dst = $dst$$Register; ++ ++ if(src != dst) ++ __ move(dst, src); ++ %} ++ ins_pipe( ialu_regI_mov ); ++%} ++ ++instruct MoveF2I_reg_reg(mRegI dst, regF src) %{ ++ match(Set dst (MoveF2I src)); ++ effect(DEF dst, USE src); ++ ins_cost(85); ++ format %{ "MoveF2I $dst, $src @ MoveF2I_reg_reg" %} ++ ins_encode %{ ++ Register dst = as_Register($dst$$reg); ++ FloatRegister src = as_FloatRegister($src$$reg); ++ ++ __ movfr2gr_s(dst, src); ++ %} ++ ins_pipe( pipe_slow ); ++%} ++ ++instruct MoveI2F_reg_reg(regF dst, mRegI src) %{ ++ match(Set dst (MoveI2F src)); ++ effect(DEF dst, USE src); ++ ins_cost(85); ++ format %{ "MoveI2F $dst, $src @ MoveI2F_reg_reg" %} ++ ins_encode %{ ++ Register src = as_Register($src$$reg); ++ FloatRegister dst = as_FloatRegister($dst$$reg); ++ ++ __ movgr2fr_w(dst, src); ++ %} ++ ins_pipe( pipe_slow ); ++%} ++ ++instruct MoveD2L_reg_reg(mRegL dst, regD src) %{ ++ match(Set dst (MoveD2L src)); ++ effect(DEF dst, USE src); ++ ins_cost(85); ++ format %{ "MoveD2L $dst, $src @ MoveD2L_reg_reg" %} ++ ins_encode %{ ++ Register dst = as_Register($dst$$reg); ++ FloatRegister src = as_FloatRegister($src$$reg); ++ ++ __ movfr2gr_d(dst, src); ++ %} ++ ins_pipe( pipe_slow ); ++%} ++ ++instruct MoveL2D_reg_reg(regD dst, mRegL src) %{ ++ match(Set dst (MoveL2D src)); ++ effect(DEF dst, USE src); ++ ins_cost(85); ++ format %{ "MoveL2D $dst, $src @ MoveL2D_reg_reg" %} ++ ins_encode %{ ++ FloatRegister dst = as_FloatRegister($dst$$reg); ++ Register src = as_Register($src$$reg); ++ ++ __ movgr2fr_d(dst, src); ++ %} ++ ins_pipe( pipe_slow ); ++%} ++ ++//----------Conditional Move--------------------------------------------------- ++// Conditional move ++instruct cmovI_cmpI_reg_reg(mRegI dst, mRegI src, mRegI tmp1, mRegI tmp2, cmpOp cop ) %{ ++ match(Set dst (CMoveI (Binary cop (CmpI tmp1 tmp2)) (Binary dst src))); ++ ins_cost(80); ++ format %{ ++ "CMP$cop $tmp1, $tmp2\t @cmovI_cmpI_reg_reg\n" ++ "\tCMOV $dst,$src \t @cmovI_cmpI_reg_reg" ++ %} ++ ++ ins_encode %{ ++ Register op1 = $tmp1$$Register; ++ Register op2 = $tmp2$$Register; ++ Register dst = $dst$$Register; ++ Register src = $src$$Register; ++ int flag = $cop$$cmpcode; ++ ++ __ cmp_cmov(op1, op2, dst, src, (MacroAssembler::CMCompare) flag, true /* is_signed */); ++ %} ++ ++ ins_pipe( pipe_slow ); ++%} ++ ++instruct cmovI_cmpP_reg_reg(mRegI dst, mRegI src, mRegP tmp1, mRegP tmp2, cmpOpU cop ) %{ ++ match(Set dst (CMoveI (Binary cop (CmpP tmp1 tmp2)) (Binary dst src))); ++ ins_cost(80); ++ format %{ ++ "CMPU$cop $tmp1,$tmp2\t @cmovI_cmpP_reg_reg\n\t" ++ "CMOV $dst,$src\t @cmovI_cmpP_reg_reg" ++ %} ++ ins_encode %{ ++ Register op1 = $tmp1$$Register; ++ Register op2 = $tmp2$$Register; ++ Register dst = $dst$$Register; ++ Register src = $src$$Register; ++ int flag = $cop$$cmpcode; ++ ++ __ cmp_cmov(op1, op2, dst, src, (MacroAssembler::CMCompare) flag, false /* is_signed */); ++ %} ++ ++ ins_pipe( pipe_slow ); ++%} ++ ++instruct cmovI_cmpN_reg_reg(mRegI dst, mRegI src, mRegN tmp1, mRegN tmp2, cmpOpU cop ) %{ ++ match(Set dst (CMoveI (Binary cop (CmpN tmp1 tmp2)) (Binary dst src))); ++ ins_cost(80); ++ format %{ ++ "CMPU$cop $tmp1,$tmp2\t @cmovI_cmpN_reg_reg\n\t" ++ "CMOV $dst,$src\t @cmovI_cmpN_reg_reg" ++ %} ++ ins_encode %{ ++ Register op1 = $tmp1$$Register; ++ Register op2 = $tmp2$$Register; ++ Register dst = $dst$$Register; ++ Register src = $src$$Register; ++ int flag = $cop$$cmpcode; ++ ++ __ cmp_cmov(op1, op2, dst, src, (MacroAssembler::CMCompare) flag, false /* is_signed */); ++ %} ++ ++ ins_pipe( pipe_slow ); ++%} ++ ++instruct cmovP_cmpU_reg_reg(mRegP dst, mRegP src, mRegI tmp1, mRegI tmp2, cmpOpU cop ) %{ ++ match(Set dst (CMoveP (Binary cop (CmpU tmp1 tmp2)) (Binary dst src))); ++ ins_cost(80); ++ format %{ ++ "CMPU$cop $tmp1,$tmp2\t @cmovP_cmpU_reg_reg\n\t" ++ "CMOV $dst,$src\t @cmovP_cmpU_reg_reg" ++ %} ++ ins_encode %{ ++ Register op1 = $tmp1$$Register; ++ Register op2 = $tmp2$$Register; ++ Register dst = $dst$$Register; ++ Register src = $src$$Register; ++ int flag = $cop$$cmpcode; ++ ++ __ cmp_cmov(op1, op2, dst, src, (MacroAssembler::CMCompare) flag, false /* is_signed */); ++ %} ++ ++ ins_pipe( pipe_slow ); ++%} ++ ++instruct cmovP_cmpF_reg_reg(mRegP dst, mRegP src, regF tmp1, regF tmp2, cmpOp cop, regD tmp3, regD tmp4) %{ ++ match(Set dst (CMoveP (Binary cop (CmpF tmp1 tmp2)) (Binary dst src))); ++ effect(TEMP tmp3, TEMP tmp4); ++ ins_cost(80); ++ format %{ ++ "CMP$cop $tmp1, $tmp2\t @cmovP_cmpF_reg_reg\n" ++ "\tCMOV $dst,$src \t @cmovP_cmpF_reg_reg" ++ %} ++ ++ ins_encode %{ ++ FloatRegister reg_op1 = $tmp1$$FloatRegister; ++ FloatRegister reg_op2 = $tmp2$$FloatRegister; ++ FloatRegister tmp1 = $tmp3$$FloatRegister; ++ FloatRegister tmp2 = $tmp4$$FloatRegister; ++ Register dst = $dst$$Register; ++ Register src = $src$$Register; ++ int flag = $cop$$cmpcode; ++ ++ __ cmp_cmov(reg_op1, reg_op2, dst, src, tmp1, tmp2, (MacroAssembler::CMCompare) flag, true /* is_float */); ++ %} ++ ins_pipe( pipe_slow ); ++%} ++ ++instruct cmovP_cmpN_reg_reg(mRegP dst, mRegP src, mRegN tmp1, mRegN tmp2, cmpOpU cop ) %{ ++ match(Set dst (CMoveP (Binary cop (CmpN tmp1 tmp2)) (Binary dst src))); ++ ins_cost(80); ++ format %{ ++ "CMPU$cop $tmp1,$tmp2\t @cmovP_cmpN_reg_reg\n\t" ++ "CMOV $dst,$src\t @cmovP_cmpN_reg_reg" ++ %} ++ ins_encode %{ ++ Register op1 = $tmp1$$Register; ++ Register op2 = $tmp2$$Register; ++ Register dst = $dst$$Register; ++ Register src = $src$$Register; ++ int flag = $cop$$cmpcode; ++ ++ __ cmp_cmov(op1, op2, dst, src, (MacroAssembler::CMCompare) flag, false /* is_signed */); ++ %} ++ ++ ins_pipe( pipe_slow ); ++%} ++ ++instruct cmovN_cmpP_reg_reg(mRegN dst, mRegN src, mRegP tmp1, mRegP tmp2, cmpOpU cop ) %{ ++ match(Set dst (CMoveN (Binary cop (CmpP tmp1 tmp2)) (Binary dst src))); ++ ins_cost(80); ++ format %{ ++ "CMPU$cop $tmp1,$tmp2\t @cmovN_cmpP_reg_reg\n\t" ++ "CMOV $dst,$src\t @cmovN_cmpP_reg_reg" ++ %} ++ ins_encode %{ ++ Register op1 = $tmp1$$Register; ++ Register op2 = $tmp2$$Register; ++ Register dst = $dst$$Register; ++ Register src = $src$$Register; ++ int flag = $cop$$cmpcode; ++ ++ __ cmp_cmov(op1, op2, dst, src, (MacroAssembler::CMCompare) flag, false /* is_signed */); ++ %} ++ ++ ins_pipe( pipe_slow ); ++%} ++ ++instruct cmovP_cmpD_reg_reg(mRegP dst, mRegP src, regD tmp1, regD tmp2, cmpOp cop, regD tmp3, regD tmp4) %{ ++ match(Set dst (CMoveP (Binary cop (CmpD tmp1 tmp2)) (Binary dst src))); ++ effect(TEMP tmp3, TEMP tmp4); ++ ins_cost(80); ++ format %{ ++ "CMP$cop $tmp1, $tmp2\t @cmovP_cmpD_reg_reg\n" ++ "\tCMOV $dst,$src \t @cmovP_cmpD_reg_reg" ++ %} ++ ins_encode %{ ++ FloatRegister reg_op1 = as_FloatRegister($tmp1$$reg); ++ FloatRegister reg_op2 = as_FloatRegister($tmp2$$reg); ++ FloatRegister tmp1 = $tmp3$$FloatRegister; ++ FloatRegister tmp2 = $tmp4$$FloatRegister; ++ Register dst = as_Register($dst$$reg); ++ Register src = as_Register($src$$reg); ++ int flag = $cop$$cmpcode; ++ ++ __ cmp_cmov(reg_op1, reg_op2, dst, src, tmp1, tmp2, (MacroAssembler::CMCompare) flag, false /* is_float */); ++ %} ++ ++ ins_pipe( pipe_slow ); ++%} ++ ++ ++instruct cmovN_cmpN_reg_reg(mRegN dst, mRegN src, mRegN tmp1, mRegN tmp2, cmpOpU cop ) %{ ++ match(Set dst (CMoveN (Binary cop (CmpN tmp1 tmp2)) (Binary dst src))); ++ ins_cost(80); ++ format %{ ++ "CMPU$cop $tmp1,$tmp2\t @cmovN_cmpN_reg_reg\n\t" ++ "CMOV $dst,$src\t @cmovN_cmpN_reg_reg" ++ %} ++ ins_encode %{ ++ Register op1 = $tmp1$$Register; ++ Register op2 = $tmp2$$Register; ++ Register dst = $dst$$Register; ++ Register src = $src$$Register; ++ int flag = $cop$$cmpcode; ++ ++ __ cmp_cmov(op1, op2, dst, src, (MacroAssembler::CMCompare) flag, false /* is_signed */); ++ %} ++ ++ ins_pipe( pipe_slow ); ++%} ++ ++ ++instruct cmovI_cmpU_reg_reg(mRegI dst, mRegI src, mRegI tmp1, mRegI tmp2, cmpOpU cop ) %{ ++ match(Set dst (CMoveI (Binary cop (CmpU tmp1 tmp2)) (Binary dst src))); ++ ins_cost(80); ++ format %{ ++ "CMPU$cop $tmp1,$tmp2\t @cmovI_cmpU_reg_reg\n\t" ++ "CMOV $dst,$src\t @cmovI_cmpU_reg_reg" ++ %} ++ ins_encode %{ ++ Register op1 = $tmp1$$Register; ++ Register op2 = $tmp2$$Register; ++ Register dst = $dst$$Register; ++ Register src = $src$$Register; ++ int flag = $cop$$cmpcode; ++ ++ __ cmp_cmov(op1, op2, dst, src, (MacroAssembler::CMCompare) flag, false /* is_signed */); ++ %} ++ ++ ins_pipe( pipe_slow ); ++%} ++ ++instruct cmovI_cmpL_reg_reg(mRegI dst, mRegI src, mRegLorI2L tmp1, mRegLorI2L tmp2, cmpOp cop ) %{ ++ match(Set dst (CMoveI (Binary cop (CmpL tmp1 tmp2)) (Binary dst src))); ++ ins_cost(80); ++ format %{ ++ "CMP$cop $tmp1, $tmp2\t @cmovI_cmpL_reg_reg\n" ++ "\tCMOV $dst,$src \t @cmovI_cmpL_reg_reg" ++ %} ++ ins_encode %{ ++ Register opr1 = as_Register($tmp1$$reg); ++ Register opr2 = as_Register($tmp2$$reg); ++ Register dst = $dst$$Register; ++ Register src = $src$$Register; ++ int flag = $cop$$cmpcode; ++ ++ __ cmp_cmov(opr1, opr2, dst, src, (MacroAssembler::CMCompare) flag, true /* is_signed */); ++ %} ++ ++ ins_pipe( pipe_slow ); ++%} ++ ++instruct cmovI_cmpUL_reg_reg(mRegI dst, mRegI src, mRegLorI2L tmp1, mRegLorI2L tmp2, cmpOp cop ) %{ ++ match(Set dst (CMoveI (Binary cop (CmpUL tmp1 tmp2)) (Binary dst src))); ++ ins_cost(80); ++ format %{ ++ "CMP$cop $tmp1, $tmp2\t @cmovI_cmpUL_reg_reg\n" ++ "\tCMOV $dst,$src \t @cmovI_cmpUL_reg_reg" ++ %} ++ ins_encode %{ ++ Register opr1 = as_Register($tmp1$$reg); ++ Register opr2 = as_Register($tmp2$$reg); ++ Register dst = $dst$$Register; ++ Register src = $src$$Register; ++ int flag = $cop$$cmpcode; ++ ++ __ cmp_cmov(opr1, opr2, dst, src, (MacroAssembler::CMCompare) flag, false /* is_signed */); ++ %} ++ ++ ins_pipe( pipe_slow ); ++%} ++ ++instruct cmovP_cmpL_reg_reg(mRegP dst, mRegP src, mRegLorI2L tmp1, mRegLorI2L tmp2, cmpOp cop ) %{ ++ match(Set dst (CMoveP (Binary cop (CmpL tmp1 tmp2)) (Binary dst src))); ++ ins_cost(80); ++ format %{ ++ "CMP$cop $tmp1, $tmp2\t @cmovP_cmpL_reg_reg\n" ++ "\tCMOV $dst,$src \t @cmovP_cmpL_reg_reg" ++ %} ++ ins_encode %{ ++ Register opr1 = as_Register($tmp1$$reg); ++ Register opr2 = as_Register($tmp2$$reg); ++ Register dst = $dst$$Register; ++ Register src = $src$$Register; ++ int flag = $cop$$cmpcode; ++ ++ __ cmp_cmov(opr1, opr2, dst, src, (MacroAssembler::CMCompare) flag, true /* is_signed */); ++ %} ++ ++ ins_pipe( pipe_slow ); ++%} ++ ++instruct cmovP_cmpUL_reg_reg(mRegP dst, mRegP src, mRegLorI2L tmp1, mRegLorI2L tmp2, cmpOp cop ) %{ ++ match(Set dst (CMoveP (Binary cop (CmpUL tmp1 tmp2)) (Binary dst src))); ++ ins_cost(80); ++ format %{ ++ "CMP$cop $tmp1, $tmp2\t @cmovP_cmpUL_reg_reg\n" ++ "\tCMOV $dst,$src \t @cmovP_cmpUL_reg_reg" ++ %} ++ ins_encode %{ ++ Register opr1 = as_Register($tmp1$$reg); ++ Register opr2 = as_Register($tmp2$$reg); ++ Register dst = $dst$$Register; ++ Register src = $src$$Register; ++ int flag = $cop$$cmpcode; ++ ++ __ cmp_cmov(opr1, opr2, dst, src, (MacroAssembler::CMCompare) flag, false /* is_signed */); ++ %} ++ ++ ins_pipe( pipe_slow ); ++%} ++ ++instruct cmovI_cmpD_reg_reg(mRegI dst, mRegI src, regD tmp1, regD tmp2, cmpOp cop, regD tmp3, regD tmp4) %{ ++ match(Set dst (CMoveI (Binary cop (CmpD tmp1 tmp2)) (Binary dst src))); ++ effect(TEMP tmp3, TEMP tmp4); ++ ins_cost(80); ++ format %{ ++ "CMP$cop $tmp1, $tmp2\t @cmovI_cmpD_reg_reg\n" ++ "\tCMOV $dst,$src \t @cmovI_cmpD_reg_reg" ++ %} ++ ins_encode %{ ++ FloatRegister reg_op1 = as_FloatRegister($tmp1$$reg); ++ FloatRegister reg_op2 = as_FloatRegister($tmp2$$reg); ++ FloatRegister tmp1 = $tmp3$$FloatRegister; ++ FloatRegister tmp2 = $tmp4$$FloatRegister; ++ Register dst = as_Register($dst$$reg); ++ Register src = as_Register($src$$reg); ++ int flag = $cop$$cmpcode; ++ ++ __ cmp_cmov(reg_op1, reg_op2, dst, src, tmp1, tmp2, (MacroAssembler::CMCompare) flag, false /* is_float */); ++ %} ++ ++ ins_pipe( pipe_slow ); ++%} ++ ++ ++instruct cmovP_cmpP_reg_reg(mRegP dst, mRegP src, mRegP tmp1, mRegP tmp2, cmpOpU cop ) %{ ++ match(Set dst (CMoveP (Binary cop (CmpP tmp1 tmp2)) (Binary dst src))); ++ ins_cost(80); ++ format %{ ++ "CMPU$cop $tmp1,$tmp2\t @cmovP_cmpP_reg_reg\n\t" ++ "CMOV $dst,$src\t @cmovP_cmpP_reg_reg" ++ %} ++ ins_encode %{ ++ Register op1 = $tmp1$$Register; ++ Register op2 = $tmp2$$Register; ++ Register dst = $dst$$Register; ++ Register src = $src$$Register; ++ int flag = $cop$$cmpcode; ++ ++ __ cmp_cmov(op1, op2, dst, src, (MacroAssembler::CMCompare) flag, false /* is_signed */); ++ %} ++ ++ ins_pipe( pipe_slow ); ++%} ++ ++instruct cmovP_cmpI_reg_reg(mRegP dst, mRegP src, mRegI tmp1, mRegI tmp2, cmpOp cop ) %{ ++ match(Set dst (CMoveP (Binary cop (CmpI tmp1 tmp2)) (Binary dst src))); ++ ins_cost(80); ++ format %{ ++ "CMP$cop $tmp1,$tmp2\t @cmovP_cmpI_reg_reg\n\t" ++ "CMOV $dst,$src\t @cmovP_cmpI_reg_reg" ++ %} ++ ins_encode %{ ++ Register op1 = $tmp1$$Register; ++ Register op2 = $tmp2$$Register; ++ Register dst = $dst$$Register; ++ Register src = $src$$Register; ++ int flag = $cop$$cmpcode; ++ ++ __ cmp_cmov(op1, op2, dst, src, (MacroAssembler::CMCompare) flag, true /* is_signed */); ++ %} ++ ++ ins_pipe( pipe_slow ); ++%} ++ ++instruct cmovL_cmpP_reg_reg(mRegL dst, mRegL src, mRegP tmp1, mRegP tmp2, cmpOpU cop ) %{ ++ match(Set dst (CMoveL (Binary cop (CmpP tmp1 tmp2)) (Binary dst src))); ++ ins_cost(80); ++ format %{ ++ "CMPU$cop $tmp1,$tmp2\t @cmovL_cmpP_reg_reg\n\t" ++ "CMOV $dst,$src\t @cmovL_cmpP_reg_reg" ++ %} ++ ins_encode %{ ++ Register op1 = $tmp1$$Register; ++ Register op2 = $tmp2$$Register; ++ Register dst = $dst$$Register; ++ Register src = $src$$Register; ++ int flag = $cop$$cmpcode; ++ Label L; ++ ++ __ cmp_cmov(op1, op2, dst, src, (MacroAssembler::CMCompare) flag, false /* is_signed */); ++ %} ++ ++ ins_pipe( pipe_slow ); ++%} ++ ++instruct cmovN_cmpU_reg_reg(mRegN dst, mRegN src, mRegI tmp1, mRegI tmp2, cmpOpU cop ) %{ ++ match(Set dst (CMoveN (Binary cop (CmpU tmp1 tmp2)) (Binary dst src))); ++ ins_cost(80); ++ format %{ ++ "CMPU$cop $tmp1,$tmp2\t @cmovN_cmpU_reg_reg\n\t" ++ "CMOV $dst,$src\t @cmovN_cmpU_reg_reg" ++ %} ++ ins_encode %{ ++ Register op1 = $tmp1$$Register; ++ Register op2 = $tmp2$$Register; ++ Register dst = $dst$$Register; ++ Register src = $src$$Register; ++ int flag = $cop$$cmpcode; ++ ++ __ cmp_cmov(op1, op2, dst, src, (MacroAssembler::CMCompare) flag, false /* is_signed */); ++ %} ++ ++ ins_pipe( pipe_slow ); ++%} ++ ++instruct cmovN_cmpL_reg_reg(mRegN dst, mRegN src, mRegL tmp1, mRegL tmp2, cmpOp cop) %{ ++ match(Set dst (CMoveN (Binary cop (CmpL tmp1 tmp2)) (Binary dst src))); ++ ins_cost(80); ++ format %{ ++ "CMP$cop $tmp1, $tmp2\t @cmovN_cmpL_reg_reg\n" ++ "\tCMOV $dst,$src \t @cmovN_cmpL_reg_reg" ++ %} ++ ins_encode %{ ++ Register opr1 = as_Register($tmp1$$reg); ++ Register opr2 = as_Register($tmp2$$reg); ++ Register dst = $dst$$Register; ++ Register src = $src$$Register; ++ int flag = $cop$$cmpcode; ++ ++ __ cmp_cmov(opr1, opr2, dst, src, (MacroAssembler::CMCompare) flag, true /* is_signed */); ++ %} ++ ++ ins_pipe( pipe_slow ); ++%} ++ ++instruct cmovN_cmpUL_reg_reg(mRegN dst, mRegN src, mRegL tmp1, mRegL tmp2, cmpOp cop) %{ ++ match(Set dst (CMoveN (Binary cop (CmpUL tmp1 tmp2)) (Binary dst src))); ++ ins_cost(80); ++ format %{ ++ "CMP$cop $tmp1, $tmp2\t @cmovN_cmpUL_reg_reg\n" ++ "\tCMOV $dst,$src \t @cmovN_cmpUL_reg_reg" ++ %} ++ ins_encode %{ ++ Register opr1 = as_Register($tmp1$$reg); ++ Register opr2 = as_Register($tmp2$$reg); ++ Register dst = $dst$$Register; ++ Register src = $src$$Register; ++ int flag = $cop$$cmpcode; ++ ++ __ cmp_cmov(opr1, opr2, dst, src, (MacroAssembler::CMCompare) flag, false /* is_signed */); ++ %} ++ ++ ins_pipe( pipe_slow ); ++%} ++ ++instruct cmovN_cmpI_reg_reg(mRegN dst, mRegN src, mRegI tmp1, mRegI tmp2, cmpOp cop ) %{ ++ match(Set dst (CMoveN (Binary cop (CmpI tmp1 tmp2)) (Binary dst src))); ++ ins_cost(80); ++ format %{ ++ "CMP$cop $tmp1,$tmp2\t @cmovN_cmpI_reg_reg\n\t" ++ "CMOV $dst,$src\t @cmovN_cmpI_reg_reg" ++ %} ++ ins_encode %{ ++ Register op1 = $tmp1$$Register; ++ Register op2 = $tmp2$$Register; ++ Register dst = $dst$$Register; ++ Register src = $src$$Register; ++ int flag = $cop$$cmpcode; ++ ++ __ cmp_cmov(op1, op2, dst, src, (MacroAssembler::CMCompare) flag, true /* is_signed */); ++ %} ++ ++ ins_pipe( pipe_slow ); ++%} ++ ++instruct cmovL_cmpU_reg_reg(mRegL dst, mRegL src, mRegI tmp1, mRegI tmp2, cmpOpU cop ) %{ ++ match(Set dst (CMoveL (Binary cop (CmpU tmp1 tmp2)) (Binary dst src))); ++ ins_cost(80); ++ format %{ ++ "CMPU$cop $tmp1,$tmp2\t @cmovL_cmpU_reg_reg\n\t" ++ "CMOV $dst,$src\t @cmovL_cmpU_reg_reg" ++ %} ++ ins_encode %{ ++ Register op1 = $tmp1$$Register; ++ Register op2 = $tmp2$$Register; ++ Register dst = $dst$$Register; ++ Register src = $src$$Register; ++ int flag = $cop$$cmpcode; ++ ++ __ cmp_cmov(op1, op2, dst, src, (MacroAssembler::CMCompare) flag, false /* is_signed */); ++ %} ++ ++ ins_pipe( pipe_slow ); ++%} ++ ++instruct cmovL_cmpF_reg_reg(mRegL dst, mRegL src, regF tmp1, regF tmp2, cmpOp cop, regD tmp3, regD tmp4) %{ ++ match(Set dst (CMoveL (Binary cop (CmpF tmp1 tmp2)) (Binary dst src))); ++ effect(TEMP tmp3, TEMP tmp4); ++ ins_cost(80); ++ format %{ ++ "CMP$cop $tmp1, $tmp2\t @cmovL_cmpF_reg_reg\n" ++ "\tCMOV $dst,$src \t @cmovL_cmpF_reg_reg" ++ %} ++ ++ ins_encode %{ ++ FloatRegister reg_op1 = $tmp1$$FloatRegister; ++ FloatRegister reg_op2 = $tmp2$$FloatRegister; ++ FloatRegister tmp1 = $tmp3$$FloatRegister; ++ FloatRegister tmp2 = $tmp4$$FloatRegister; ++ Register dst = $dst$$Register; ++ Register src = $src$$Register; ++ int flag = $cop$$cmpcode; ++ ++ __ cmp_cmov(reg_op1, reg_op2, dst, src, tmp1, tmp2, (MacroAssembler::CMCompare) flag, true /* is_float */); ++ %} ++ ins_pipe( pipe_slow ); ++%} ++ ++instruct cmovL_cmpI_reg_reg(mRegL dst, mRegL src, mRegI tmp1, mRegI tmp2, cmpOp cop ) %{ ++ match(Set dst (CMoveL (Binary cop (CmpI tmp1 tmp2)) (Binary dst src))); ++ ins_cost(80); ++ format %{ ++ "CMP$cop $tmp1, $tmp2\t @cmovL_cmpI_reg_reg\n" ++ "\tCMOV $dst,$src \t @cmovL_cmpI_reg_reg" ++ %} ++ ++ ins_encode %{ ++ Register op1 = $tmp1$$Register; ++ Register op2 = $tmp2$$Register; ++ Register dst = as_Register($dst$$reg); ++ Register src = as_Register($src$$reg); ++ int flag = $cop$$cmpcode; ++ ++ __ cmp_cmov(op1, op2, dst, src, (MacroAssembler::CMCompare) flag, true /* is_signed */); ++ %} ++ ++ ins_pipe( pipe_slow ); ++%} ++ ++instruct cmovL_cmpL_reg_reg(mRegL dst, mRegL src, mRegL tmp1, mRegL tmp2, cmpOp cop ) %{ ++ match(Set dst (CMoveL (Binary cop (CmpL tmp1 tmp2)) (Binary dst src))); ++ ins_cost(80); ++ format %{ ++ "CMP$cop $tmp1, $tmp2\t @cmovL_cmpL_reg_reg\n" ++ "\tCMOV $dst,$src \t @cmovL_cmpL_reg_reg" ++ %} ++ ins_encode %{ ++ Register opr1 = as_Register($tmp1$$reg); ++ Register opr2 = as_Register($tmp2$$reg); ++ Register dst = as_Register($dst$$reg); ++ Register src = as_Register($src$$reg); ++ int flag = $cop$$cmpcode; ++ ++ __ cmp_cmov(opr1, opr2, dst, src, (MacroAssembler::CMCompare) flag, true /* is_signed */); ++ %} ++ ++ ins_pipe( pipe_slow ); ++%} ++ ++instruct cmovL_cmpUL_reg_reg(mRegL dst, mRegL src, mRegL tmp1, mRegL tmp2, cmpOp cop ) %{ ++ match(Set dst (CMoveL (Binary cop (CmpUL tmp1 tmp2)) (Binary dst src))); ++ ins_cost(80); ++ format %{ ++ "CMP$cop $tmp1, $tmp2\t @cmovL_cmpUL_reg_reg\n" ++ "\tCMOV $dst,$src \t @cmovL_cmpUL_reg_reg" ++ %} ++ ins_encode %{ ++ Register opr1 = as_Register($tmp1$$reg); ++ Register opr2 = as_Register($tmp2$$reg); ++ Register dst = as_Register($dst$$reg); ++ Register src = as_Register($src$$reg); ++ int flag = $cop$$cmpcode; ++ ++ __ cmp_cmov(opr1, opr2, dst, src, (MacroAssembler::CMCompare) flag, false /* is_signed */); ++ %} ++ ++ ins_pipe( pipe_slow ); ++%} ++ ++instruct cmovL_cmpN_reg_reg(mRegL dst, mRegL src, mRegN tmp1, mRegN tmp2, cmpOpU cop ) %{ ++ match(Set dst (CMoveL (Binary cop (CmpN tmp1 tmp2)) (Binary dst src))); ++ ins_cost(80); ++ format %{ ++ "CMPU$cop $tmp1,$tmp2\t @cmovL_cmpN_reg_reg\n\t" ++ "CMOV $dst,$src\t @cmovL_cmpN_reg_reg" ++ %} ++ ins_encode %{ ++ Register op1 = $tmp1$$Register; ++ Register op2 = $tmp2$$Register; ++ Register dst = $dst$$Register; ++ Register src = $src$$Register; ++ int flag = $cop$$cmpcode; ++ ++ __ cmp_cmov(op1, op2, dst, src, (MacroAssembler::CMCompare) flag, false /* is_signed */); ++ %} ++ ++ ins_pipe( pipe_slow ); ++%} ++ ++ ++instruct cmovL_cmpD_reg_reg(mRegL dst, mRegL src, regD tmp1, regD tmp2, cmpOp cop, regD tmp3, regD tmp4) %{ ++ match(Set dst (CMoveL (Binary cop (CmpD tmp1 tmp2)) (Binary dst src))); ++ effect(TEMP tmp3, TEMP tmp4); ++ ins_cost(80); ++ format %{ ++ "CMP$cop $tmp1, $tmp2\t @cmovL_cmpD_reg_reg\n" ++ "\tCMOV $dst,$src \t @cmovL_cmpD_reg_reg" ++ %} ++ ins_encode %{ ++ FloatRegister reg_op1 = as_FloatRegister($tmp1$$reg); ++ FloatRegister reg_op2 = as_FloatRegister($tmp2$$reg); ++ FloatRegister tmp1 = $tmp3$$FloatRegister; ++ FloatRegister tmp2 = $tmp4$$FloatRegister; ++ Register dst = as_Register($dst$$reg); ++ Register src = as_Register($src$$reg); ++ int flag = $cop$$cmpcode; ++ ++ __ cmp_cmov(reg_op1, reg_op2, dst, src, tmp1, tmp2, (MacroAssembler::CMCompare) flag, false /* is_float */); ++ %} ++ ++ ins_pipe( pipe_slow ); ++%} ++ ++instruct cmovD_cmpD_reg_reg(regD dst, regD src, regD tmp1, regD tmp2, cmpOp cop ) %{ ++ match(Set dst (CMoveD (Binary cop (CmpD tmp1 tmp2)) (Binary dst src))); ++ ins_cost(200); ++ format %{ ++ "CMP$cop $tmp1, $tmp2\t @cmovD_cmpD_reg_reg\n" ++ "\tCMOV $dst,$src \t @cmovD_cmpD_reg_reg" ++ %} ++ ins_encode %{ ++ FloatRegister reg_op1 = as_FloatRegister($tmp1$$reg); ++ FloatRegister reg_op2 = as_FloatRegister($tmp2$$reg); ++ FloatRegister dst = as_FloatRegister($dst$$reg); ++ FloatRegister src = as_FloatRegister($src$$reg); ++ int flag = $cop$$cmpcode; ++ ++ __ cmp_cmov(reg_op1, reg_op2, dst, src, (MacroAssembler::CMCompare) flag, false /* is_float */); ++ %} ++ ++ ins_pipe( pipe_slow ); ++%} ++ ++instruct cmovF_cmpI_reg_reg(regF dst, regF src, mRegI tmp1, mRegI tmp2, cmpOp cop, regF tmp3, regF tmp4) %{ ++ match(Set dst (CMoveF (Binary cop (CmpI tmp1 tmp2)) (Binary dst src))); ++ effect(TEMP tmp3, TEMP tmp4); ++ ins_cost(200); ++ format %{ ++ "CMP$cop $tmp1, $tmp2\t @cmovF_cmpI_reg_reg\n" ++ "\tCMOV $dst, $src \t @cmovF_cmpI_reg_reg" ++ %} ++ ++ ins_encode %{ ++ Register op1 = $tmp1$$Register; ++ Register op2 = $tmp2$$Register; ++ FloatRegister dst = as_FloatRegister($dst$$reg); ++ FloatRegister src = as_FloatRegister($src$$reg); ++ FloatRegister tmp1 = as_FloatRegister($tmp3$$reg); ++ FloatRegister tmp2 = as_FloatRegister($tmp4$$reg); ++ int flag = $cop$$cmpcode; ++ ++ __ cmp_cmov(op1, op2, dst, src, tmp1, tmp2, (MacroAssembler::CMCompare) flag); ++ %} ++ ++ ins_pipe( pipe_slow ); ++%} ++ ++instruct cmovD_cmpI_reg_reg(regD dst, regD src, mRegI tmp1, mRegI tmp2, cmpOp cop, regF tmp3, regF tmp4) %{ ++ match(Set dst (CMoveD (Binary cop (CmpI tmp1 tmp2)) (Binary dst src))); ++ effect(TEMP tmp3, TEMP tmp4); ++ ins_cost(200); ++ format %{ ++ "CMP$cop $tmp1, $tmp2\t @cmovD_cmpI_reg_reg\n" ++ "\tCMOV $dst, $src \t @cmovD_cmpI_reg_reg" ++ %} ++ ++ ins_encode %{ ++ Register op1 = $tmp1$$Register; ++ Register op2 = $tmp2$$Register; ++ FloatRegister dst = as_FloatRegister($dst$$reg); ++ FloatRegister src = as_FloatRegister($src$$reg); ++ FloatRegister tmp1 = as_FloatRegister($tmp3$$reg); ++ FloatRegister tmp2 = as_FloatRegister($tmp4$$reg); ++ int flag = $cop$$cmpcode; ++ ++ __ cmp_cmov(op1, op2, dst, src, tmp1, tmp2, (MacroAssembler::CMCompare) flag); ++ %} ++ ++ ins_pipe( pipe_slow ); ++%} ++ ++instruct cmovD_cmpP_reg_reg(regD dst, regD src, mRegP tmp1, mRegP tmp2, cmpOp cop, regF tmp3, regF tmp4) %{ ++ match(Set dst (CMoveD (Binary cop (CmpP tmp1 tmp2)) (Binary dst src))); ++ effect(TEMP tmp3, TEMP tmp4); ++ ins_cost(200); ++ format %{ ++ "CMP$cop $tmp1, $tmp2\t @cmovD_cmpP_reg_reg\n" ++ "\tCMOV $dst, $src \t @cmovD_cmpP_reg_reg" ++ %} ++ ++ ins_encode %{ ++ Register op1 = $tmp1$$Register; ++ Register op2 = $tmp2$$Register; ++ FloatRegister dst = as_FloatRegister($dst$$reg); ++ FloatRegister src = as_FloatRegister($src$$reg); ++ FloatRegister tmp1 = as_FloatRegister($tmp3$$reg); ++ FloatRegister tmp2 = as_FloatRegister($tmp4$$reg); ++ int flag = $cop$$cmpcode; ++ ++ // Use signed comparison here, because the most significant bit of the ++ // user-space virtual address must be 0. ++ __ cmp_cmov(op1, op2, dst, src, tmp1, tmp2, (MacroAssembler::CMCompare) flag); ++ %} ++ ++ ins_pipe( pipe_slow ); ++%} ++ ++//FIXME ++instruct cmovI_cmpF_reg_reg(mRegI dst, mRegI src, regF tmp1, regF tmp2, cmpOp cop, regD tmp3, regD tmp4) %{ ++ match(Set dst (CMoveI (Binary cop (CmpF tmp1 tmp2)) (Binary dst src))); ++ effect(TEMP tmp3, TEMP tmp4); ++ ins_cost(80); ++ format %{ ++ "CMP$cop $tmp1, $tmp2\t @cmovI_cmpF_reg_reg\n" ++ "\tCMOV $dst,$src \t @cmovI_cmpF_reg_reg" ++ %} ++ ++ ins_encode %{ ++ FloatRegister reg_op1 = $tmp1$$FloatRegister; ++ FloatRegister reg_op2 = $tmp2$$FloatRegister; ++ FloatRegister tmp1 = $tmp3$$FloatRegister; ++ FloatRegister tmp2 = $tmp4$$FloatRegister; ++ Register dst = $dst$$Register; ++ Register src = $src$$Register; ++ int flag = $cop$$cmpcode; ++ ++ __ cmp_cmov(reg_op1, reg_op2, dst, src, tmp1, tmp2, (MacroAssembler::CMCompare) flag, true /* is_float */); ++ %} ++ ins_pipe( pipe_slow ); ++%} ++ ++instruct cmovF_cmpF_reg_reg(regF dst, regF src, regF tmp1, regF tmp2, cmpOp cop ) %{ ++ match(Set dst (CMoveF (Binary cop (CmpF tmp1 tmp2)) (Binary dst src))); ++ ins_cost(200); ++ format %{ ++ "CMP$cop $tmp1, $tmp2\t @cmovF_cmpF_reg_reg\n" ++ "\tCMOV $dst,$src \t @cmovF_cmpF_reg_reg" ++ %} ++ ++ ins_encode %{ ++ FloatRegister reg_op1 = $tmp1$$FloatRegister; ++ FloatRegister reg_op2 = $tmp2$$FloatRegister; ++ FloatRegister dst = $dst$$FloatRegister; ++ FloatRegister src = $src$$FloatRegister; ++ int flag = $cop$$cmpcode; ++ ++ __ cmp_cmov(reg_op1, reg_op2, dst, src, (MacroAssembler::CMCompare) flag, true /* is_float */); ++ %} ++ ins_pipe( pipe_slow ); ++%} ++ ++// Manifest a CmpL result in an integer register. Very painful. ++// This is the test to avoid. ++instruct cmpL3_reg_reg(mRegI dst, mRegL src1, mRegL src2) %{ ++ match(Set dst (CmpL3 src1 src2)); ++ ins_cost(1000); ++ format %{ "cmpL3 $dst, $src1, $src2 @ cmpL3_reg_reg" %} ++ ins_encode %{ ++ Register opr1 = as_Register($src1$$reg); ++ Register opr2 = as_Register($src2$$reg); ++ Register dst = as_Register($dst$$reg); ++ ++ __ slt(AT, opr1, opr2); ++ __ slt(dst, opr2, opr1); ++ __ sub_d(dst, dst, AT); ++ %} ++ ins_pipe( pipe_slow ); ++%} ++ ++// ++// less_rsult = -1 ++// greater_result = 1 ++// equal_result = 0 ++// nan_result = -1 ++// ++instruct cmpF3_reg_reg(mRegI dst, regF src1, regF src2) %{ ++ match(Set dst (CmpF3 src1 src2)); ++ ins_cost(1000); ++ format %{ "cmpF3 $dst, $src1, $src2 @ cmpF3_reg_reg" %} ++ ins_encode %{ ++ FloatRegister src1 = as_FloatRegister($src1$$reg); ++ FloatRegister src2 = as_FloatRegister($src2$$reg); ++ Register dst = as_Register($dst$$reg); ++ ++ __ fcmp_clt_s(FCC0, src2, src1); ++ __ fcmp_cult_s(FCC1, src1, src2); ++ __ movcf2gr(dst, FCC0); ++ __ movcf2gr(AT, FCC1); ++ __ sub_d(dst, dst, AT); ++ ++ %} ++ ins_pipe( pipe_slow ); ++%} ++ ++instruct cmpD3_reg_reg(mRegI dst, regD src1, regD src2) %{ ++ match(Set dst (CmpD3 src1 src2)); ++ ins_cost(1000); ++ format %{ "cmpD3 $dst, $src1, $src2 @ cmpD3_reg_reg" %} ++ ins_encode %{ ++ FloatRegister src1 = as_FloatRegister($src1$$reg); ++ FloatRegister src2 = as_FloatRegister($src2$$reg); ++ Register dst = as_Register($dst$$reg); ++ ++ __ fcmp_clt_d(FCC0, src2, src1); ++ __ fcmp_cult_d(FCC1, src1, src2); ++ __ movcf2gr(dst, FCC0); ++ __ movcf2gr(AT, FCC1); ++ __ sub_d(dst, dst, AT); ++ %} ++ ins_pipe( pipe_slow ); ++%} ++ ++instruct clear_array(mRegL cnt, mRegP base, Universe dummy) %{ ++ match(Set dummy (ClearArray cnt base)); ++ format %{ "CLEAR_ARRAY base = $base, cnt = $cnt # Clear doublewords" %} ++ ins_encode %{ ++ //Assume cnt is the number of bytes in an array to be cleared, ++ //and base points to the starting address of the array. ++ Register base = $base$$Register; ++ Register num = $cnt$$Register; ++ Label Loop, done; ++ ++ __ add_d(AT, base, R0); ++ __ beq(num, R0, done); ++ ++ __ move(T4, num); /* T4 = words */ ++ ++ __ bind(Loop); ++ __ st_d(R0, AT, 0); ++ __ addi_d(T4, T4, -1); ++ __ addi_d(AT, AT, wordSize); ++ __ bne(T4, R0, Loop); ++ ++ __ bind(done); ++ %} ++ ins_pipe( pipe_slow ); ++%} ++ ++instruct string_compare(a4_RegP str1, mA5RegI cnt1, a6_RegP str2, mA7RegI cnt2, no_Ax_mRegI result) %{ ++ match(Set result (StrComp (Binary str1 cnt1) (Binary str2 cnt2))); ++ effect(USE_KILL str1, USE_KILL str2, USE_KILL cnt1, USE_KILL cnt2); ++ ++ format %{ "String Compare $str1[len: $cnt1], $str2[len: $cnt2] -> $result @ string_compare" %} ++ ins_encode %{ ++ // Get the first character position in both strings ++ // [8] char array, [12] offset, [16] count ++ Register str1 = $str1$$Register; ++ Register str2 = $str2$$Register; ++ Register cnt1 = $cnt1$$Register; ++ Register cnt2 = $cnt2$$Register; ++ Register result = $result$$Register; ++ ++ Label L, Loop, haveResult, done; ++ ++ // compute the and difference of lengths (in result) ++ __ sub_d(result, cnt1, cnt2); // result holds the difference of two lengths ++ ++ // compute the shorter length (in cnt1) ++ __ bge(cnt2, cnt1, Loop); ++ __ move(cnt1, cnt2); ++ ++ // Now the shorter length is in cnt1 and cnt2 can be used as a tmp register ++ __ bind(Loop); // Loop begin ++ __ ld_hu(AT, str1, 0); ++ __ beq(cnt1, R0, done); ++ ++ // compare current character ++ __ ld_hu(cnt2, str2, 0); ++ __ addi_d(str1, str1, 2); ++ __ bne(AT, cnt2, haveResult); ++ __ addi_d(str2, str2, 2); ++ __ addi_d(cnt1, cnt1, -1); // Loop end ++ __ b(Loop); ++ ++ __ bind(haveResult); ++ __ sub_d(result, AT, cnt2); ++ ++ __ bind(done); ++ %} ++ ++ ins_pipe( pipe_slow ); ++%} ++ ++// intrinsic optimization ++instruct string_equals(a4_RegP str1, a5_RegP str2, mA6RegI cnt, mA7RegI temp, no_Ax_mRegI result) %{ ++ match(Set result (StrEquals (Binary str1 str2) cnt)); ++ effect(USE_KILL str1, USE_KILL str2, USE_KILL cnt, KILL temp); ++ ++ format %{ "String Equal $str1, $str2, len:$cnt tmp:$temp -> $result @ string_equals" %} ++ ins_encode %{ ++ // Get the first character position in both strings ++ // [8] char array, [12] offset, [16] count ++ Register str1 = $str1$$Register; ++ Register str2 = $str2$$Register; ++ Register cnt = $cnt$$Register; ++ Register tmp = $temp$$Register; ++ Register result = $result$$Register; ++ ++ Label Loop, True, False; ++ ++ __ addi_d(result, R0, 1); ++ __ beq(str1, str2, True); // same char[] ? ++ ++ __ beq(cnt, R0, True); ++ ++ __ bind(Loop); ++ ++ // compare current character ++ __ ld_hu(AT, str1, 0); ++ __ ld_hu(tmp, str2, 0); ++ __ addi_d(str1, str1, 2); ++ __ bne(AT, tmp, False); ++ __ addi_d(cnt, cnt, -1); ++ __ addi_d(str2, str2, 2); ++ __ bne(cnt, R0, Loop); ++ ++ __ b(True); ++ ++ __ bind(False); ++ __ addi_d(result, R0, 0); ++ ++ __ bind(True); ++ %} ++ ++ ins_pipe( pipe_slow ); ++%} ++ ++//----------Arithmetic Instructions------------------------------------------- ++//----------Addition Instructions--------------------------------------------- ++instruct addI_Reg_Reg(mRegI dst, mRegI src1, mRegI src2) %{ ++ match(Set dst (AddI src1 src2)); ++ ++ format %{ "add $dst, $src1, $src2 #@addI_Reg_Reg" %} ++ ins_encode %{ ++ Register dst = $dst$$Register; ++ Register src1 = $src1$$Register; ++ Register src2 = $src2$$Register; ++ __ add_w(dst, src1, src2); ++ %} ++ ins_pipe( ialu_regI_regI ); ++%} ++ ++instruct addI_Reg_imm(mRegI dst, mRegI src1, immI12 src2) %{ ++ match(Set dst (AddI src1 src2)); ++ ++ format %{ "add $dst, $src1, $src2 #@addI_Reg_imm12" %} ++ ins_encode %{ ++ Register dst = $dst$$Register; ++ Register src1 = $src1$$Register; ++ int imm = $src2$$constant; ++ ++ __ addi_w(dst, src1, imm); ++ %} ++ ins_pipe( ialu_regI_regI ); ++%} ++ ++instruct addI_salI_Reg_Reg_immI_1_4(mRegI dst, mRegI src1, mRegI src2, immI_1_4 shift) %{ ++ match(Set dst (AddI src1 (LShiftI src2 shift))); ++ ++ format %{ "alsl $dst, $src1, $src2, $shift #@addI_salI_Reg_Reg_immI_1_4" %} ++ ins_encode %{ ++ Register dst = $dst$$Register; ++ Register src1 = $src1$$Register; ++ Register src2 = $src2$$Register; ++ int sh = $shift$$constant; ++ __ alsl_w(dst, src2, src1, sh - 1); ++ %} ++ ins_pipe(ialu_regI_regI); ++%} ++ ++instruct addP_reg_reg(mRegP dst, mRegP src1, mRegLorI2L src2) %{ ++ match(Set dst (AddP src1 src2)); ++ ++ format %{ "ADD $dst, $src1, $src2 #@addP_reg_reg" %} ++ ++ ins_encode %{ ++ Register dst = $dst$$Register; ++ Register src1 = $src1$$Register; ++ Register src2 = $src2$$Register; ++ __ add_d(dst, src1, src2); ++ %} ++ ++ ins_pipe( ialu_regI_regI ); ++%} ++ ++instruct addP_reg_reg_M8(mRegP dst, mRegP src1, mRegLorI2L src2, immL_M8 M8) %{ ++ match(Set dst (AddP src1 (AndL src2 M8))); ++ format %{ "ADD $dst, $src1, $src2 #@addP_reg_reg_M8" %} ++ ++ ins_encode %{ ++ Register dst = $dst$$Register; ++ Register src1 = $src1$$Register; ++ Register src2 = $src2$$Register; ++ __ bstrins_d(src2, R0, 2, 0); ++ __ add_d(dst, src1, src2); ++ %} ++ ++ ins_pipe( ialu_regI_regI ); ++%} ++ ++instruct addP_reg_imm12(mRegP dst, mRegP src1, immL12 src2) %{ ++ match(Set dst (AddP src1 src2)); ++ ++ format %{ "ADD $dst, $src1, $src2 #@addP_reg_imm12" %} ++ ins_encode %{ ++ Register src1 = $src1$$Register; ++ long src2 = $src2$$constant; ++ Register dst = $dst$$Register; ++ ++ __ addi_d(dst, src1, src2); ++ %} ++ ins_pipe( ialu_regI_imm16 ); ++%} ++ ++instruct addP_salL_Reg_RegI2L_immI_1_4(mRegP dst, mRegP src1, mRegI src2, immI_1_4 shift) %{ ++ match(Set dst (AddP src1 (LShiftL (ConvI2L src2) shift))); ++ ++ format %{ "alsl $dst, $src1, $src2, $shift #@addP_salL_Reg_RegI2L_immI_1_4" %} ++ ++ ins_encode %{ ++ Register dst = $dst$$Register; ++ Register src1 = $src1$$Register; ++ Register src2 = $src2$$Register; ++ int sh = $shift$$constant; ++ __ alsl_d(dst, src2, src1, sh - 1); ++ %} ++ ++ ins_pipe(ialu_regI_regI); ++%} ++ ++// Add Long Register with Register ++instruct addL_Reg_Reg(mRegL dst, mRegLorI2L src1, mRegLorI2L src2) %{ ++ match(Set dst (AddL src1 src2)); ++ ins_cost(200); ++ format %{ "ADD $dst, $src1, $src2 #@addL_Reg_Reg\t" %} ++ ++ ins_encode %{ ++ Register dst_reg = as_Register($dst$$reg); ++ Register src1_reg = as_Register($src1$$reg); ++ Register src2_reg = as_Register($src2$$reg); ++ ++ __ add_d(dst_reg, src1_reg, src2_reg); ++ %} ++ ++ ins_pipe( ialu_regL_regL ); ++%} ++ ++instruct addL_Reg_imm(mRegL dst, mRegLorI2L src1, immL12 src2) ++%{ ++ match(Set dst (AddL src1 src2)); ++ ++ format %{ "ADD $dst, $src1, $src2 #@addL_Reg_imm " %} ++ ins_encode %{ ++ Register dst_reg = as_Register($dst$$reg); ++ Register src1_reg = as_Register($src1$$reg); ++ int src2_imm = $src2$$constant; ++ ++ __ addi_d(dst_reg, src1_reg, src2_imm); ++ %} ++ ++ ins_pipe( ialu_regL_regL ); ++%} ++ ++//----------Subtraction Instructions------------------------------------------- ++// Integer Subtraction Instructions ++instruct subI_Reg_Reg(mRegI dst, mRegI src1, mRegI src2) %{ ++ match(Set dst (SubI src1 src2)); ++ ins_cost(100); ++ ++ format %{ "sub $dst, $src1, $src2 #@subI_Reg_Reg" %} ++ ins_encode %{ ++ Register dst = $dst$$Register; ++ Register src1 = $src1$$Register; ++ Register src2 = $src2$$Register; ++ __ sub_w(dst, src1, src2); ++ %} ++ ins_pipe( ialu_regI_regI ); ++%} ++ ++instruct subI_Reg_immI_M2047_2048(mRegI dst, mRegI src1, immI_M2047_2048 src2) %{ ++ match(Set dst (SubI src1 src2)); ++ ins_cost(80); ++ ++ format %{ "sub $dst, $src1, $src2 #@subI_Reg_immI_M2047_2048" %} ++ ins_encode %{ ++ Register dst = $dst$$Register; ++ Register src1 = $src1$$Register; ++ __ addi_w(dst, src1, -1 * $src2$$constant); ++ %} ++ ins_pipe( ialu_regI_regI ); ++%} ++ ++instruct negI_Reg(mRegI dst, immI_0 zero, mRegI src) %{ ++ match(Set dst (SubI zero src)); ++ ins_cost(80); ++ ++ format %{ "neg $dst, $src #@negI_Reg" %} ++ ins_encode %{ ++ Register dst = $dst$$Register; ++ Register src = $src$$Register; ++ __ sub_w(dst, R0, src); ++ %} ++ ins_pipe( ialu_regI_regI ); ++%} ++ ++instruct negL_Reg(mRegL dst, immL_0 zero, mRegLorI2L src) %{ ++ match(Set dst (SubL zero src)); ++ ins_cost(80); ++ ++ format %{ "neg $dst, $src #@negL_Reg" %} ++ ins_encode %{ ++ Register dst = $dst$$Register; ++ Register src = $src$$Register; ++ __ sub_d(dst, R0, src); ++ %} ++ ins_pipe( ialu_regI_regI ); ++%} ++ ++instruct subL_Reg_immL_M2047_2048(mRegL dst, mRegL src1, immL_M2047_2048 src2) %{ ++ match(Set dst (SubL src1 src2)); ++ ins_cost(80); ++ ++ format %{ "sub $dst, $src1, $src2 #@subL_Reg_immL_M2047_2048" %} ++ ins_encode %{ ++ Register dst = $dst$$Register; ++ Register src1 = $src1$$Register; ++ __ addi_d(dst, src1, -1 * $src2$$constant); ++ %} ++ ins_pipe( ialu_regI_regI ); ++%} ++ ++// Subtract Long Register with Register. ++instruct subL_Reg_Reg(mRegL dst, mRegLorI2L src1, mRegLorI2L src2) %{ ++ match(Set dst (SubL src1 src2)); ++ ins_cost(100); ++ format %{ "SubL $dst, $src1, $src2 @ subL_Reg_Reg" %} ++ ins_encode %{ ++ Register dst = as_Register($dst$$reg); ++ Register src1 = as_Register($src1$$reg); ++ Register src2 = as_Register($src2$$reg); ++ ++ __ sub_d(dst, src1, src2); ++ %} ++ ins_pipe( ialu_regL_regL ); ++%} ++ ++// Integer MOD with Register ++instruct modI_Reg_Reg(mRegI dst, mRegI src1, mRegI src2) %{ ++ match(Set dst (ModI src1 src2)); ++ ins_cost(300); ++ format %{ "modi $dst, $src1, $src2 @ modI_Reg_Reg" %} ++ ins_encode %{ ++ Register dst = $dst$$Register; ++ Register src1 = $src1$$Register; ++ Register src2 = $src2$$Register; ++ ++ __ mod_w(dst, src1, src2); ++ %} ++ ++ //ins_pipe( ialu_mod ); ++ ins_pipe( ialu_regI_regI ); ++%} ++ ++instruct modL_reg_reg(mRegL dst, mRegLorI2L src1, mRegLorI2L src2) %{ ++ match(Set dst (ModL src1 src2)); ++ format %{ "modL $dst, $src1, $src2 @modL_reg_reg" %} ++ ++ ins_encode %{ ++ Register dst = as_Register($dst$$reg); ++ Register op1 = as_Register($src1$$reg); ++ Register op2 = as_Register($src2$$reg); ++ ++ __ mod_d(dst, op1, op2); ++ %} ++ ins_pipe( pipe_slow ); ++%} ++ ++instruct mulI_Reg_Reg(mRegI dst, mRegI src1, mRegI src2) %{ ++ match(Set dst (MulI src1 src2)); ++ ++ ins_cost(300); ++ format %{ "mul $dst, $src1, $src2 @ mulI_Reg_Reg" %} ++ ins_encode %{ ++ Register src1 = $src1$$Register; ++ Register src2 = $src2$$Register; ++ Register dst = $dst$$Register; ++ ++ __ mul_w(dst, src1, src2); ++ %} ++ ins_pipe( ialu_mult ); ++%} ++ ++instruct divI_Reg_Reg(mRegI dst, mRegI src1, mRegI src2) %{ ++ match(Set dst (DivI src1 src2)); ++ ++ ins_cost(300); ++ format %{ "div $dst, $src1, $src2 @ divI_Reg_Reg" %} ++ ins_encode %{ ++ Register src1 = $src1$$Register; ++ Register src2 = $src2$$Register; ++ Register dst = $dst$$Register; ++ ++ __ div_w(dst, src1, src2); ++ ++ %} ++ ins_pipe( ialu_mod ); ++%} ++ ++instruct divF_Reg_Reg(regF dst, regF src1, regF src2) %{ ++ match(Set dst (DivF src1 src2)); ++ ++ ins_cost(300); ++ format %{ "divF $dst, $src1, $src2 @ divF_Reg_Reg" %} ++ ins_encode %{ ++ FloatRegister src1 = $src1$$FloatRegister; ++ FloatRegister src2 = $src2$$FloatRegister; ++ FloatRegister dst = $dst$$FloatRegister; ++ ++ __ fdiv_s(dst, src1, src2); ++ %} ++ ins_pipe( pipe_slow ); ++%} ++ ++instruct divD_Reg_Reg(regD dst, regD src1, regD src2) %{ ++ match(Set dst (DivD src1 src2)); ++ ++ ins_cost(300); ++ format %{ "divD $dst, $src1, $src2 @ divD_Reg_Reg" %} ++ ins_encode %{ ++ FloatRegister src1 = $src1$$FloatRegister; ++ FloatRegister src2 = $src2$$FloatRegister; ++ FloatRegister dst = $dst$$FloatRegister; ++ ++ __ fdiv_d(dst, src1, src2); ++ %} ++ ins_pipe( pipe_slow ); ++%} ++ ++instruct mulL_reg_reg(mRegL dst, mRegLorI2L src1, mRegLorI2L src2) %{ ++ match(Set dst (MulL src1 src2)); ++ format %{ "mulL $dst, $src1, $src2 @mulL_reg_reg" %} ++ ins_encode %{ ++ Register dst = as_Register($dst$$reg); ++ Register op1 = as_Register($src1$$reg); ++ Register op2 = as_Register($src2$$reg); ++ ++ __ mul_d(dst, op1, op2); ++ %} ++ ins_pipe( pipe_slow ); ++%} ++ ++instruct mulHiL_reg_reg(mRegL dst, mRegL src1, mRegL src2) %{ ++ match(Set dst (MulHiL src1 src2)); ++ format %{ "mulHiL $dst, $src1, $src2 @mulL_reg_reg" %} ++ ins_encode %{ ++ Register dst = as_Register($dst$$reg); ++ Register op1 = as_Register($src1$$reg); ++ Register op2 = as_Register($src2$$reg); ++ ++ __ mulh_d(dst, op1, op2); ++ %} ++ ins_pipe( pipe_slow ); ++%} ++ ++instruct divL_reg_reg(mRegL dst, mRegL src1, mRegL src2) %{ ++ match(Set dst (DivL src1 src2)); ++ format %{ "divL $dst, $src1, $src2 @divL_reg_reg" %} ++ ++ ins_encode %{ ++ Register dst = as_Register($dst$$reg); ++ Register op1 = as_Register($src1$$reg); ++ Register op2 = as_Register($src2$$reg); ++ ++ __ div_d(dst, op1, op2); ++ %} ++ ins_pipe( pipe_slow ); ++%} ++ ++instruct addF_reg_reg(regF dst, regF src1, regF src2) %{ ++ match(Set dst (AddF src1 src2)); ++ format %{ "AddF $dst, $src1, $src2 @addF_reg_reg" %} ++ ins_encode %{ ++ FloatRegister src1 = as_FloatRegister($src1$$reg); ++ FloatRegister src2 = as_FloatRegister($src2$$reg); ++ FloatRegister dst = as_FloatRegister($dst$$reg); ++ ++ __ fadd_s(dst, src1, src2); ++ %} ++ ins_pipe( fpu_regF_regF ); ++%} ++ ++instruct subF_reg_reg(regF dst, regF src1, regF src2) %{ ++ match(Set dst (SubF src1 src2)); ++ format %{ "SubF $dst, $src1, $src2 @subF_reg_reg" %} ++ ins_encode %{ ++ FloatRegister src1 = as_FloatRegister($src1$$reg); ++ FloatRegister src2 = as_FloatRegister($src2$$reg); ++ FloatRegister dst = as_FloatRegister($dst$$reg); ++ ++ __ fsub_s(dst, src1, src2); ++ %} ++ ins_pipe( fpu_regF_regF ); ++%} ++instruct addD_reg_reg(regD dst, regD src1, regD src2) %{ ++ match(Set dst (AddD src1 src2)); ++ format %{ "AddD $dst, $src1, $src2 @addD_reg_reg" %} ++ ins_encode %{ ++ FloatRegister src1 = as_FloatRegister($src1$$reg); ++ FloatRegister src2 = as_FloatRegister($src2$$reg); ++ FloatRegister dst = as_FloatRegister($dst$$reg); ++ ++ __ fadd_d(dst, src1, src2); ++ %} ++ ins_pipe( fpu_regF_regF ); ++%} ++ ++instruct subD_reg_reg(regD dst, regD src1, regD src2) %{ ++ match(Set dst (SubD src1 src2)); ++ format %{ "SubD $dst, $src1, $src2 @subD_reg_reg" %} ++ ins_encode %{ ++ FloatRegister src1 = as_FloatRegister($src1$$reg); ++ FloatRegister src2 = as_FloatRegister($src2$$reg); ++ FloatRegister dst = as_FloatRegister($dst$$reg); ++ ++ __ fsub_d(dst, src1, src2); ++ %} ++ ins_pipe( fpu_regF_regF ); ++%} ++ ++instruct negF_reg(regF dst, regF src) %{ ++ match(Set dst (NegF src)); ++ format %{ "negF $dst, $src @negF_reg" %} ++ ins_encode %{ ++ FloatRegister src = as_FloatRegister($src$$reg); ++ FloatRegister dst = as_FloatRegister($dst$$reg); ++ ++ __ fneg_s(dst, src); ++ %} ++ ins_pipe( fpu_regF_regF ); ++%} ++ ++instruct negD_reg(regD dst, regD src) %{ ++ match(Set dst (NegD src)); ++ format %{ "negD $dst, $src @negD_reg" %} ++ ins_encode %{ ++ FloatRegister src = as_FloatRegister($src$$reg); ++ FloatRegister dst = as_FloatRegister($dst$$reg); ++ ++ __ fneg_d(dst, src); ++ %} ++ ins_pipe( fpu_regF_regF ); ++%} ++ ++ ++instruct mulF_reg_reg(regF dst, regF src1, regF src2) %{ ++ match(Set dst (MulF src1 src2)); ++ format %{ "MULF $dst, $src1, $src2 @mulF_reg_reg" %} ++ ins_encode %{ ++ FloatRegister src1 = $src1$$FloatRegister; ++ FloatRegister src2 = $src2$$FloatRegister; ++ FloatRegister dst = $dst$$FloatRegister; ++ ++ __ fmul_s(dst, src1, src2); ++ %} ++ ins_pipe( fpu_regF_regF ); ++%} ++ ++instruct maddF_reg_reg(regF dst, regF src1, regF src2, regF src3) %{ ++ match(Set dst (AddF (MulF src1 src2) src3)); ++ // For compatibility reason (e.g. on the Loongson platform), disable this guy. ++ ins_cost(44444); ++ format %{ "maddF $dst, $src1, $src2, $src3 @maddF_reg_reg" %} ++ ins_encode %{ ++ FloatRegister src1 = $src1$$FloatRegister; ++ FloatRegister src2 = $src2$$FloatRegister; ++ FloatRegister src3 = $src3$$FloatRegister; ++ FloatRegister dst = $dst$$FloatRegister; ++ ++ __ fmadd_s(dst, src1, src2, src3); ++ %} ++ ins_pipe( fpu_regF_regF ); ++%} ++ ++// Mul two double precision floating piont number ++instruct mulD_reg_reg(regD dst, regD src1, regD src2) %{ ++ match(Set dst (MulD src1 src2)); ++ format %{ "MULD $dst, $src1, $src2 @mulD_reg_reg" %} ++ ins_encode %{ ++ FloatRegister src1 = $src1$$FloatRegister; ++ FloatRegister src2 = $src2$$FloatRegister; ++ FloatRegister dst = $dst$$FloatRegister; ++ ++ __ fmul_d(dst, src1, src2); ++ %} ++ ins_pipe( fpu_regF_regF ); ++%} ++ ++instruct maddD_reg_reg(regD dst, regD src1, regD src2, regD src3) %{ ++ match(Set dst (AddD (MulD src1 src2) src3)); ++ // For compatibility reason (e.g. on the Loongson platform), disable this guy. ++ ins_cost(44444); ++ format %{ "maddD $dst, $src1, $src2, $src3 @maddD_reg_reg" %} ++ ins_encode %{ ++ FloatRegister src1 = $src1$$FloatRegister; ++ FloatRegister src2 = $src2$$FloatRegister; ++ FloatRegister src3 = $src3$$FloatRegister; ++ FloatRegister dst = $dst$$FloatRegister; ++ ++ __ fmadd_d(dst, src1, src2, src3); ++ %} ++ ins_pipe( fpu_regF_regF ); ++%} ++ ++instruct absF_reg(regF dst, regF src) %{ ++ match(Set dst (AbsF src)); ++ ins_cost(100); ++ format %{ "absF $dst, $src @absF_reg" %} ++ ins_encode %{ ++ FloatRegister src = as_FloatRegister($src$$reg); ++ FloatRegister dst = as_FloatRegister($dst$$reg); ++ ++ __ fabs_s(dst, src); ++ %} ++ ins_pipe( fpu_regF_regF ); ++%} ++ ++ ++// intrinsics for math_native. ++// AbsD SqrtD CosD SinD TanD LogD Log10D ++ ++instruct absD_reg(regD dst, regD src) %{ ++ match(Set dst (AbsD src)); ++ ins_cost(100); ++ format %{ "absD $dst, $src @absD_reg" %} ++ ins_encode %{ ++ FloatRegister src = as_FloatRegister($src$$reg); ++ FloatRegister dst = as_FloatRegister($dst$$reg); ++ ++ __ fabs_d(dst, src); ++ %} ++ ins_pipe( fpu_regF_regF ); ++%} ++ ++instruct sqrtD_reg(regD dst, regD src) %{ ++ match(Set dst (SqrtD src)); ++ ins_cost(100); ++ format %{ "SqrtD $dst, $src @sqrtD_reg" %} ++ ins_encode %{ ++ FloatRegister src = as_FloatRegister($src$$reg); ++ FloatRegister dst = as_FloatRegister($dst$$reg); ++ ++ __ fsqrt_d(dst, src); ++ %} ++ ins_pipe( fpu_regF_regF ); ++%} ++ ++instruct sqrtF_reg(regF dst, regF src) %{ ++ match(Set dst (ConvD2F (SqrtD (ConvF2D src)))); ++ ins_cost(100); ++ format %{ "SqrtF $dst, $src @sqrtF_reg" %} ++ ins_encode %{ ++ FloatRegister src = as_FloatRegister($src$$reg); ++ FloatRegister dst = as_FloatRegister($dst$$reg); ++ ++ __ fsqrt_s(dst, src); ++ %} ++ ins_pipe( fpu_regF_regF ); ++%} ++//----------------------------------Logical Instructions---------------------- ++//__________________________________Integer Logical Instructions------------- ++ ++//And Instuctions ++// And Register with Immediate ++instruct andI_Reg_imm_0_4095(mRegI dst, mRegI src1, immI_0_4095 src2) %{ ++ match(Set dst (AndI src1 src2)); ++ ins_cost(60); ++ ++ format %{ "and $dst, $src1, $src2 #@andI_Reg_imm_0_4095" %} ++ ins_encode %{ ++ Register dst = $dst$$Register; ++ Register src = $src1$$Register; ++ int val = $src2$$constant; ++ ++ __ andi(dst, src, val); ++ ++ %} ++ ins_pipe( ialu_regI_regI ); ++%} ++ ++instruct andI_Reg_immI_nonneg_mask(mRegI dst, mRegI src1, immI_nonneg_mask mask) %{ ++ match(Set dst (AndI src1 mask)); ++ ins_cost(60); ++ ++ format %{ "and $dst, $src1, $mask #@andI_Reg_immI_nonneg_mask" %} ++ ins_encode %{ ++ Register dst = $dst$$Register; ++ Register src = $src1$$Register; ++ int size = Assembler::is_int_mask($mask$$constant); ++ ++ __ bstrpick_w(dst, src, size-1, 0); ++ %} ++ ins_pipe( ialu_regI_regI ); ++%} ++ ++instruct andL_Reg_immL_nonneg_mask(mRegL dst, mRegL src1, immL_nonneg_mask mask) %{ ++ match(Set dst (AndL src1 mask)); ++ ins_cost(60); ++ ++ format %{ "and $dst, $src1, $mask #@andL_Reg_immL_nonneg_mask" %} ++ ins_encode %{ ++ Register dst = $dst$$Register; ++ Register src = $src1$$Register; ++ int size = Assembler::is_jlong_mask($mask$$constant); ++ ++ __ bstrpick_d(dst, src, size-1, 0); ++ %} ++ ins_pipe( ialu_regI_regI ); ++%} ++ ++instruct xorI_Reg_imm_0_4095(mRegI dst, mRegI src1, immI_0_4095 src2) %{ ++ match(Set dst (XorI src1 src2)); ++ ins_cost(60); ++ ++ format %{ "xori $dst, $src1, $src2 #@xorI_Reg_imm_0_4095" %} ++ ins_encode %{ ++ Register dst = $dst$$Register; ++ Register src = $src1$$Register; ++ int val = $src2$$constant; ++ ++ __ xori(dst, src, val); ++ %} ++ ins_pipe( ialu_regI_regI ); ++%} ++ ++instruct xorI_Reg_immI_M1(mRegI dst, mRegI src1, immI_M1 M1) %{ ++ match(Set dst (XorI src1 M1)); ++ ins_cost(60); ++ ++ format %{ "xor $dst, $src1, $M1 #@xorI_Reg_immI_M1" %} ++ ins_encode %{ ++ Register dst = $dst$$Register; ++ Register src = $src1$$Register; ++ ++ __ orn(dst, R0, src); ++ %} ++ ins_pipe( ialu_regI_regI ); ++%} ++ ++instruct xorL2I_Reg_immI_M1(mRegI dst, mRegL src1, immI_M1 M1) %{ ++ match(Set dst (XorI (ConvL2I src1) M1)); ++ ins_cost(60); ++ ++ format %{ "xor $dst, $src1, $M1 #@xorL2I_Reg_immI_M1" %} ++ ins_encode %{ ++ Register dst = $dst$$Register; ++ Register src = $src1$$Register; ++ ++ __ orn(dst, R0, src); ++ %} ++ ins_pipe( ialu_regI_regI ); ++%} ++ ++instruct xorL_Reg_imm_0_4095(mRegL dst, mRegL src1, immL_0_4095 src2) %{ ++ match(Set dst (XorL src1 src2)); ++ ins_cost(60); ++ ++ format %{ "xori $dst, $src1, $src2 #@xorL_Reg_imm_0_4095" %} ++ ins_encode %{ ++ Register dst = $dst$$Register; ++ Register src = $src1$$Register; ++ int val = $src2$$constant; ++ ++ __ xori(dst, src, val); ++ %} ++ ins_pipe( ialu_regI_regI ); ++%} ++ ++ ++instruct lbu_and_lmask(mRegI dst, memory mem, immI_255 mask) %{ ++ match(Set dst (AndI mask (LoadB mem))); ++ ins_cost(60); ++ ++ format %{ "lhu $dst, $mem #@lbu_and_lmask" %} ++ ins_encode %{ ++ __ loadstore_enc($dst$$Register, $mem$$base, $mem$$index, $mem$$scale, $mem$$disp, MacroAssembler::LOAD_U_BYTE); ++ %} ++ ins_pipe( ialu_loadI ); ++%} ++ ++instruct lbu_and_rmask(mRegI dst, memory mem, immI_255 mask) %{ ++ match(Set dst (AndI (LoadB mem) mask)); ++ ins_cost(60); ++ ++ format %{ "lhu $dst, $mem #@lbu_and_rmask" %} ++ ins_encode %{ ++ __ loadstore_enc($dst$$Register, $mem$$base, $mem$$index, $mem$$scale, $mem$$disp, MacroAssembler::LOAD_U_BYTE); ++ %} ++ ins_pipe( ialu_loadI ); ++%} ++ ++instruct andI_Reg_Reg(mRegI dst, mRegI src1, mRegI src2) %{ ++ match(Set dst (AndI src1 src2)); ++ ++ format %{ "and $dst, $src1, $src2 #@andI_Reg_Reg" %} ++ ins_encode %{ ++ Register dst = $dst$$Register; ++ Register src1 = $src1$$Register; ++ Register src2 = $src2$$Register; ++ ++ __ andr(dst, src1, src2); ++ %} ++ ins_pipe( ialu_regI_regI ); ++%} ++ ++instruct andnI_Reg_nReg(mRegI dst, mRegI src1, mRegI src2, immI_M1 M1) %{ ++ match(Set dst (AndI src1 (XorI src2 M1))); ++ ++ format %{ "andn $dst, $src1, $src2 #@andnI_Reg_nReg" %} ++ ins_encode %{ ++ Register dst = $dst$$Register; ++ Register src1 = $src1$$Register; ++ Register src2 = $src2$$Register; ++ ++ __ andn(dst, src1, src2); ++ %} ++ ins_pipe( ialu_regI_regI ); ++%} ++ ++instruct ornI_Reg_nReg(mRegI dst, mRegI src1, mRegI src2, immI_M1 M1) %{ ++ match(Set dst (OrI src1 (XorI src2 M1))); ++ ++ format %{ "orn $dst, $src1, $src2 #@ornI_Reg_nReg" %} ++ ins_encode %{ ++ Register dst = $dst$$Register; ++ Register src1 = $src1$$Register; ++ Register src2 = $src2$$Register; ++ ++ __ orn(dst, src1, src2); ++ %} ++ ins_pipe( ialu_regI_regI ); ++%} ++ ++instruct andnI_nReg_Reg(mRegI dst, mRegI src1, mRegI src2, immI_M1 M1) %{ ++ match(Set dst (AndI (XorI src1 M1) src2)); ++ ++ format %{ "andn $dst, $src2, $src1 #@andnI_nReg_Reg" %} ++ ins_encode %{ ++ Register dst = $dst$$Register; ++ Register src1 = $src1$$Register; ++ Register src2 = $src2$$Register; ++ ++ __ andn(dst, src2, src1); ++ %} ++ ins_pipe( ialu_regI_regI ); ++%} ++ ++instruct ornI_nReg_Reg(mRegI dst, mRegI src1, mRegI src2, immI_M1 M1) %{ ++ match(Set dst (OrI (XorI src1 M1) src2)); ++ ++ format %{ "orn $dst, $src2, $src1 #@ornI_nReg_Reg" %} ++ ins_encode %{ ++ Register dst = $dst$$Register; ++ Register src1 = $src1$$Register; ++ Register src2 = $src2$$Register; ++ ++ __ orn(dst, src2, src1); ++ %} ++ ins_pipe( ialu_regI_regI ); ++%} ++ ++// And Long Register with Register ++instruct andL_Reg_Reg(mRegL dst, mRegL src1, mRegLorI2L src2) %{ ++ match(Set dst (AndL src1 src2)); ++ format %{ "AND $dst, $src1, $src2 @ andL_Reg_Reg\n\t" %} ++ ins_encode %{ ++ Register dst_reg = as_Register($dst$$reg); ++ Register src1_reg = as_Register($src1$$reg); ++ Register src2_reg = as_Register($src2$$reg); ++ ++ __ andr(dst_reg, src1_reg, src2_reg); ++ %} ++ ins_pipe( ialu_regL_regL ); ++%} ++ ++instruct andL_Reg_imm_0_4095(mRegL dst, mRegL src1, immL_0_4095 src2) %{ ++ match(Set dst (AndL src1 src2)); ++ ins_cost(60); ++ ++ format %{ "and $dst, $src1, $src2 #@andL_Reg_imm_0_4095" %} ++ ins_encode %{ ++ Register dst = $dst$$Register; ++ Register src = $src1$$Register; ++ long val = $src2$$constant; ++ ++ __ andi(dst, src, val); ++ %} ++ ins_pipe( ialu_regI_regI ); ++%} ++ ++instruct andL2I_Reg_imm_0_4095(mRegI dst, mRegL src1, immL_0_4095 src2) %{ ++ match(Set dst (ConvL2I (AndL src1 src2))); ++ ins_cost(60); ++ ++ format %{ "and $dst, $src1, $src2 #@andL2I_Reg_imm_0_4095" %} ++ ins_encode %{ ++ Register dst = $dst$$Register; ++ Register src = $src1$$Register; ++ long val = $src2$$constant; ++ ++ __ andi(dst, src, val); ++ %} ++ ins_pipe( ialu_regI_regI ); ++%} ++ ++ ++instruct andL_Reg_immL_M8(mRegL dst, immL_M8 M8) %{ ++ match(Set dst (AndL dst M8)); ++ ins_cost(60); ++ ++ format %{ "and $dst, $dst, $M8 #@andL_Reg_immL_M8" %} ++ ins_encode %{ ++ Register dst = $dst$$Register; ++ ++ __ bstrins_d(dst, R0, 2, 0); ++ %} ++ ins_pipe( ialu_regI_regI ); ++%} ++ ++instruct andL_Reg_immL_M5(mRegL dst, immL_M5 M5) %{ ++ match(Set dst (AndL dst M5)); ++ ins_cost(60); ++ ++ format %{ "and $dst, $dst, $M5 #@andL_Reg_immL_M5" %} ++ ins_encode %{ ++ Register dst = $dst$$Register; ++ ++ __ bstrins_d(dst, R0, 2, 2); ++ %} ++ ins_pipe( ialu_regI_regI ); ++%} ++ ++instruct andL_Reg_immL_M7(mRegL dst, immL_M7 M7) %{ ++ match(Set dst (AndL dst M7)); ++ ins_cost(60); ++ ++ format %{ "and $dst, $dst, $M7 #@andL_Reg_immL_M7" %} ++ ins_encode %{ ++ Register dst = $dst$$Register; ++ ++ __ bstrins_d(dst, R0, 2, 1); ++ %} ++ ins_pipe( ialu_regI_regI ); ++%} ++ ++instruct andL_Reg_immL_M4(mRegL dst, immL_M4 M4) %{ ++ match(Set dst (AndL dst M4)); ++ ins_cost(60); ++ ++ format %{ "and $dst, $dst, $M4 #@andL_Reg_immL_M4" %} ++ ins_encode %{ ++ Register dst = $dst$$Register; ++ ++ __ bstrins_d(dst, R0, 1, 0); ++ %} ++ ins_pipe( ialu_regI_regI ); ++%} ++ ++instruct andL_Reg_immL_M121(mRegL dst, immL_M121 M121) %{ ++ match(Set dst (AndL dst M121)); ++ ins_cost(60); ++ ++ format %{ "and $dst, $dst, $M121 #@andL_Reg_immL_M121" %} ++ ins_encode %{ ++ Register dst = $dst$$Register; ++ ++ __ bstrins_d(dst, R0, 6, 3); ++ %} ++ ins_pipe( ialu_regI_regI ); ++%} ++ ++// Or Long Register with Register ++instruct orL_Reg_Reg(mRegL dst, mRegLorI2L src1, mRegLorI2L src2) %{ ++ match(Set dst (OrL src1 src2)); ++ format %{ "OR $dst, $src1, $src2 @ orL_Reg_Reg\t" %} ++ ins_encode %{ ++ Register dst_reg = $dst$$Register; ++ Register src1_reg = $src1$$Register; ++ Register src2_reg = $src2$$Register; ++ ++ __ orr(dst_reg, src1_reg, src2_reg); ++ %} ++ ins_pipe( ialu_regL_regL ); ++%} ++ ++instruct orL_Reg_P2XReg(mRegL dst, mRegP src1, mRegLorI2L src2) %{ ++ match(Set dst (OrL (CastP2X src1) src2)); ++ format %{ "OR $dst, $src1, $src2 @ orL_Reg_P2XReg\t" %} ++ ins_encode %{ ++ Register dst_reg = $dst$$Register; ++ Register src1_reg = $src1$$Register; ++ Register src2_reg = $src2$$Register; ++ ++ __ orr(dst_reg, src1_reg, src2_reg); ++ %} ++ ins_pipe( ialu_regL_regL ); ++%} ++ ++// Xor Long Register with Register ++instruct xorL_Reg_Reg(mRegL dst, mRegL src1, mRegL src2) %{ ++ match(Set dst (XorL src1 src2)); ++ format %{ "XOR $dst, $src1, $src2 @ xorL_Reg_Reg\t" %} ++ ins_encode %{ ++ Register dst_reg = as_Register($dst$$reg); ++ Register src1_reg = as_Register($src1$$reg); ++ Register src2_reg = as_Register($src2$$reg); ++ ++ __ xorr(dst_reg, src1_reg, src2_reg); ++ %} ++ ins_pipe( ialu_regL_regL ); ++%} ++ ++// Shift Left by 5-bit immediate ++instruct salI_Reg_imm(mRegI dst, mRegI src, immIU5 shift) %{ ++ match(Set dst (LShiftI src shift)); ++ ++ format %{ "SHL $dst, $src, $shift #@salI_Reg_imm" %} ++ ins_encode %{ ++ Register src = $src$$Register; ++ Register dst = $dst$$Register; ++ int shamt = $shift$$constant; ++ ++ __ slli_w(dst, src, shamt); ++ %} ++ ins_pipe( ialu_regI_regI ); ++%} ++ ++instruct salL2I_Reg_imm(mRegI dst, mRegL src, immIU5 shift) %{ ++ match(Set dst (LShiftI (ConvL2I src) shift)); ++ ++ format %{ "SHL $dst, $src, $shift #@salL2I_Reg_imm" %} ++ ins_encode %{ ++ Register src = $src$$Register; ++ Register dst = $dst$$Register; ++ int shamt = $shift$$constant; ++ ++ __ slli_w(dst, src, shamt); ++ %} ++ ins_pipe( ialu_regI_regI ); ++%} ++ ++instruct salI_Reg_imm_and_M65536(mRegI dst, mRegI src, immI_16 shift, immI_M65536 mask) %{ ++ match(Set dst (AndI (LShiftI src shift) mask)); ++ ++ format %{ "SHL $dst, $src, $shift #@salI_Reg_imm_and_M65536" %} ++ ins_encode %{ ++ Register src = $src$$Register; ++ Register dst = $dst$$Register; ++ ++ __ slli_w(dst, src, 16); ++ %} ++ ins_pipe( ialu_regI_regI ); ++%} ++ ++instruct land7_2_s(mRegI dst, mRegL src, immL_7 seven, immI_16 sixteen) ++%{ ++ match(Set dst (RShiftI (LShiftI (ConvL2I (AndL src seven)) sixteen) sixteen)); ++ ++ format %{ "andi $dst, $src, 7\t# @land7_2_s" %} ++ ins_encode %{ ++ Register src = $src$$Register; ++ Register dst = $dst$$Register; ++ ++ __ andi(dst, src, 7); ++ %} ++ ins_pipe(ialu_regI_regI); ++%} ++ ++// Logical Shift Right by 16, followed by Arithmetic Shift Left by 16. ++// This idiom is used by the compiler the i2s bytecode. ++instruct i2s(mRegI dst, mRegI src, immI_16 sixteen) ++%{ ++ match(Set dst (RShiftI (LShiftI src sixteen) sixteen)); ++ ++ format %{ "i2s $dst, $src\t# @i2s" %} ++ ins_encode %{ ++ Register src = $src$$Register; ++ Register dst = $dst$$Register; ++ ++ __ ext_w_h(dst, src); ++ %} ++ ins_pipe(ialu_regI_regI); ++%} ++ ++// Logical Shift Right by 24, followed by Arithmetic Shift Left by 24. ++// This idiom is used by the compiler for the i2b bytecode. ++instruct i2b(mRegI dst, mRegI src, immI_24 twentyfour) ++%{ ++ match(Set dst (RShiftI (LShiftI src twentyfour) twentyfour)); ++ ++ format %{ "i2b $dst, $src\t# @i2b" %} ++ ins_encode %{ ++ Register src = $src$$Register; ++ Register dst = $dst$$Register; ++ ++ __ ext_w_b(dst, src); ++ %} ++ ins_pipe(ialu_regI_regI); ++%} ++ ++ ++instruct salI_RegL2I_imm(mRegI dst, mRegL src, immIU5 shift) %{ ++ match(Set dst (LShiftI (ConvL2I src) shift)); ++ ++ format %{ "SHL $dst, $src, $shift #@salI_RegL2I_imm" %} ++ ins_encode %{ ++ Register src = $src$$Register; ++ Register dst = $dst$$Register; ++ int shamt = $shift$$constant; ++ ++ __ slli_w(dst, src, shamt); ++ %} ++ ins_pipe( ialu_regI_regI ); ++%} ++ ++// Shift Left by 8-bit immediate ++instruct salI_Reg_Reg(mRegI dst, mRegI src, mRegI shift) %{ ++ match(Set dst (LShiftI src shift)); ++ ++ format %{ "SHL $dst, $src, $shift #@salI_Reg_Reg" %} ++ ins_encode %{ ++ Register src = $src$$Register; ++ Register dst = $dst$$Register; ++ Register shamt = $shift$$Register; ++ __ sll_w(dst, src, shamt); ++ %} ++ ins_pipe( ialu_regI_regI ); ++%} ++ ++ ++// Shift Left Long 6-bit immI ++instruct salL_Reg_imm(mRegL dst, mRegLorI2L src, immIU6 shift) %{ ++ match(Set dst (LShiftL src shift)); ++ ins_cost(100); ++ format %{ "salL $dst, $src, $shift @ salL_Reg_imm" %} ++ ins_encode %{ ++ Register src_reg = as_Register($src$$reg); ++ Register dst_reg = as_Register($dst$$reg); ++ int shamt = $shift$$constant; ++ ++ __ slli_d(dst_reg, src_reg, shamt); ++ %} ++ ins_pipe( ialu_regL_regL ); ++%} ++ ++// Shift Left Long ++instruct salL_Reg_Reg(mRegL dst, mRegLorI2L src, mRegI shift) %{ ++ match(Set dst (LShiftL src shift)); ++ ins_cost(100); ++ format %{ "salL $dst, $src, $shift @ salL_Reg_Reg" %} ++ ins_encode %{ ++ Register src_reg = as_Register($src$$reg); ++ Register dst_reg = as_Register($dst$$reg); ++ ++ __ sll_d(dst_reg, src_reg, $shift$$Register); ++ %} ++ ins_pipe( ialu_regL_regL ); ++%} ++ ++// Shift Right Long 6-bit ++instruct sarL_Reg_imm(mRegL dst, mRegLorI2L src, immIU6 shift) %{ ++ match(Set dst (RShiftL src shift)); ++ ins_cost(100); ++ format %{ "sarL $dst, $src, $shift @ sarL_Reg_imm" %} ++ ins_encode %{ ++ Register src_reg = as_Register($src$$reg); ++ Register dst_reg = as_Register($dst$$reg); ++ int shamt = $shift$$constant; ++ ++ __ srai_d(dst_reg, src_reg, shamt); ++ %} ++ ins_pipe( ialu_regL_regL ); ++%} ++ ++instruct sarL2I_Reg_immI_32_63(mRegI dst, mRegLorI2L src, immI_32_63 shift) %{ ++ match(Set dst (ConvL2I (RShiftL src shift))); ++ ins_cost(100); ++ format %{ "sarL $dst, $src, $shift @ sarL2I_Reg_immI_32_63" %} ++ ins_encode %{ ++ Register src_reg = as_Register($src$$reg); ++ Register dst_reg = as_Register($dst$$reg); ++ int shamt = $shift$$constant; ++ ++ __ srai_d(dst_reg, src_reg, shamt); ++ %} ++ ins_pipe( ialu_regL_regL ); ++%} ++ ++// Shift Right Long arithmetically ++instruct sarL_Reg_Reg(mRegL dst, mRegLorI2L src, mRegI shift) %{ ++ match(Set dst (RShiftL src shift)); ++ ins_cost(100); ++ format %{ "sarL $dst, $src, $shift @ sarL_Reg_Reg" %} ++ ins_encode %{ ++ Register src_reg = as_Register($src$$reg); ++ Register dst_reg = as_Register($dst$$reg); ++ ++ __ sra_d(dst_reg, src_reg, $shift$$Register); ++ %} ++ ins_pipe( ialu_regL_regL ); ++%} ++ ++// Shift Right Long logically ++instruct slrL_Reg_Reg(mRegL dst, mRegL src, mRegI shift) %{ ++ match(Set dst (URShiftL src shift)); ++ ins_cost(100); ++ format %{ "slrL $dst, $src, $shift @ slrL_Reg_Reg" %} ++ ins_encode %{ ++ Register src_reg = as_Register($src$$reg); ++ Register dst_reg = as_Register($dst$$reg); ++ ++ __ srl_d(dst_reg, src_reg, $shift$$Register); ++ %} ++ ins_pipe( ialu_regL_regL ); ++%} ++ ++instruct slrL_Reg_immI_0_31(mRegL dst, mRegLorI2L src, immI_0_31 shift) %{ ++ match(Set dst (URShiftL src shift)); ++ ins_cost(80); ++ format %{ "slrL $dst, $src, $shift @ slrL_Reg_immI_0_31" %} ++ ins_encode %{ ++ Register src_reg = as_Register($src$$reg); ++ Register dst_reg = as_Register($dst$$reg); ++ int shamt = $shift$$constant; ++ ++ __ srli_d(dst_reg, src_reg, shamt); ++ %} ++ ins_pipe( ialu_regL_regL ); ++%} ++ ++instruct slrL_Reg_immI_0_31_and_max_int(mRegI dst, mRegLorI2L src, immI_0_31 shift, immI_MaxI max_int) %{ ++ match(Set dst (AndI (ConvL2I (URShiftL src shift)) max_int)); ++ ins_cost(80); ++ format %{ "bstrpick_d $dst, $src, $shift+30, shift @ slrL_Reg_immI_0_31_and_max_int" %} ++ ins_encode %{ ++ Register src_reg = as_Register($src$$reg); ++ Register dst_reg = as_Register($dst$$reg); ++ int shamt = $shift$$constant; ++ ++ __ bstrpick_d(dst_reg, src_reg, shamt+30, shamt); ++ %} ++ ins_pipe( ialu_regL_regL ); ++%} ++ ++instruct slrL_P2XReg_immI_0_31(mRegL dst, mRegP src, immI_0_31 shift) %{ ++ match(Set dst (URShiftL (CastP2X src) shift)); ++ ins_cost(80); ++ format %{ "slrL $dst, $src, $shift @ slrL_P2XReg_immI_0_31" %} ++ ins_encode %{ ++ Register src_reg = as_Register($src$$reg); ++ Register dst_reg = as_Register($dst$$reg); ++ int shamt = $shift$$constant; ++ ++ __ srli_d(dst_reg, src_reg, shamt); ++ %} ++ ins_pipe( ialu_regL_regL ); ++%} ++ ++instruct slrL_Reg_immI_32_63(mRegL dst, mRegLorI2L src, immI_32_63 shift) %{ ++ match(Set dst (URShiftL src shift)); ++ ins_cost(80); ++ format %{ "slrL $dst, $src, $shift @ slrL_Reg_immI_32_63" %} ++ ins_encode %{ ++ Register src_reg = as_Register($src$$reg); ++ Register dst_reg = as_Register($dst$$reg); ++ int shamt = $shift$$constant; ++ ++ __ srli_d(dst_reg, src_reg, shamt); ++ %} ++ ins_pipe( ialu_regL_regL ); ++%} ++ ++instruct slrL_Reg_immI_convL2I(mRegI dst, mRegLorI2L src, immI_32_63 shift) %{ ++ match(Set dst (ConvL2I (URShiftL src shift))); ++ predicate(n->in(1)->in(2)->get_int() > 32); ++ ins_cost(80); ++ format %{ "slrL $dst, $src, $shift @ slrL_Reg_immI_convL2I" %} ++ ins_encode %{ ++ Register src_reg = as_Register($src$$reg); ++ Register dst_reg = as_Register($dst$$reg); ++ int shamt = $shift$$constant; ++ ++ __ srli_d(dst_reg, src_reg, shamt); ++ %} ++ ins_pipe( ialu_regL_regL ); ++%} ++ ++instruct slrL_P2XReg_immI_32_63(mRegL dst, mRegP src, immI_32_63 shift) %{ ++ match(Set dst (URShiftL (CastP2X src) shift)); ++ ins_cost(80); ++ format %{ "slrL $dst, $src, $shift @ slrL_P2XReg_immI_32_63" %} ++ ins_encode %{ ++ Register src_reg = as_Register($src$$reg); ++ Register dst_reg = as_Register($dst$$reg); ++ int shamt = $shift$$constant; ++ ++ __ srli_d(dst_reg, src_reg, shamt); ++ %} ++ ins_pipe( ialu_regL_regL ); ++%} ++ ++// Xor Instructions ++// Xor Register with Register ++instruct xorI_Reg_Reg(mRegI dst, mRegI src1, mRegI src2) %{ ++ match(Set dst (XorI src1 src2)); ++ ++ format %{ "XOR $dst, $src1, $src2 #@xorI_Reg_Reg" %} ++ ++ ins_encode %{ ++ Register dst = $dst$$Register; ++ Register src1 = $src1$$Register; ++ Register src2 = $src2$$Register; ++ __ xorr(dst, src1, src2); ++ %} ++ ++ ins_pipe( ialu_regI_regI ); ++%} ++ ++// Or Instructions ++instruct orI_Reg_imm(mRegI dst, mRegI src1, immI_0_4095 src2) %{ ++ match(Set dst (OrI src1 src2)); ++ ++ format %{ "OR $dst, $src1, $src2 #@orI_Reg_imm" %} ++ ins_encode %{ ++ __ ori($dst$$Register, $src1$$Register, $src2$$constant); ++ %} ++ ++ ins_pipe( ialu_regI_regI ); ++%} ++ ++// Or Register with Register ++instruct orI_Reg_Reg(mRegI dst, mRegI src1, mRegI src2) %{ ++ match(Set dst (OrI src1 src2)); ++ ++ format %{ "OR $dst, $src1, $src2 #@orI_Reg_Reg" %} ++ ins_encode %{ ++ Register dst = $dst$$Register; ++ Register src1 = $src1$$Register; ++ Register src2 = $src2$$Register; ++ __ orr(dst, src1, src2); ++ %} ++ ++ ins_pipe( ialu_regI_regI ); ++%} ++ ++instruct rotI_shr_logical_Reg(mRegI dst, mRegI src, immI_0_31 rshift, immI_0_31 lshift, immI_1 one) %{ ++ match(Set dst (OrI (URShiftI src rshift) (LShiftI (AndI src one) lshift))); ++ predicate(32 == ((n->in(1)->in(2)->get_int() + n->in(2)->in(2)->get_int()))); ++ ++ format %{ "rotri_w $dst, $src, 1 ...\n\t" ++ "srli_w $dst, $dst, ($rshift-1) @ rotI_shr_logical_Reg" %} ++ ins_encode %{ ++ Register dst = $dst$$Register; ++ Register src = $src$$Register; ++ int rshift = $rshift$$constant; ++ ++ __ rotri_w(dst, src, 1); ++ if (rshift - 1) { ++ __ srli_w(dst, dst, rshift - 1); ++ } ++ %} ++ ++ ins_pipe( ialu_regI_regI ); ++%} ++ ++instruct orI_Reg_castP2X(mRegL dst, mRegL src1, mRegP src2) %{ ++ match(Set dst (OrI src1 (CastP2X src2))); ++ ++ format %{ "OR $dst, $src1, $src2 #@orI_Reg_castP2X" %} ++ ins_encode %{ ++ Register dst = $dst$$Register; ++ Register src1 = $src1$$Register; ++ Register src2 = $src2$$Register; ++ __ orr(dst, src1, src2); ++ %} ++ ++ ins_pipe( ialu_regI_regI ); ++%} ++ ++// Logical Shift Right by 5-bit immediate ++instruct shr_logical_Reg_imm(mRegI dst, mRegI src, immIU5 shift) %{ ++ match(Set dst (URShiftI src shift)); ++ //effect(KILL cr); ++ ++ format %{ "SRLI_W $dst, $src, $shift #@shr_logical_Reg_imm" %} ++ ins_encode %{ ++ Register src = $src$$Register; ++ Register dst = $dst$$Register; ++ int shift = $shift$$constant; ++ ++ __ srli_w(dst, src, shift); ++ %} ++ ins_pipe( ialu_regI_regI ); ++%} ++ ++instruct shr_logical_Reg_imm_nonneg_mask(mRegI dst, mRegI src, immI_0_31 shift, immI_nonneg_mask mask) %{ ++ match(Set dst (AndI (URShiftI src shift) mask)); ++ ++ format %{ "bstrpick_w $dst, $src, $shift+one-bits($mask)-1, shift #@shr_logical_Reg_imm_nonneg_mask" %} ++ ins_encode %{ ++ Register src = $src$$Register; ++ Register dst = $dst$$Register; ++ int pos = $shift$$constant; ++ int size = Assembler::is_int_mask($mask$$constant); ++ ++ __ bstrpick_w(dst, src, pos+size-1, pos); ++ %} ++ ins_pipe( ialu_regI_regI ); ++%} ++ ++instruct rolI_Reg_immI_0_31(mRegI dst, mRegI src, immI_0_31 lshift, immI_0_31 rshift) ++%{ ++ predicate(0 == ((n->in(1)->in(2)->get_int() + n->in(2)->in(2)->get_int()) & 0x1f)); ++ match(Set dst (OrI (LShiftI src lshift) (URShiftI src rshift))); ++ ++ ins_cost(100); ++ format %{ "rotri_w $dst, $src, $rshift #@rolI_Reg_immI_0_31" %} ++ ins_encode %{ ++ Register dst = $dst$$Register; ++ Register src = $src$$Register; ++ int sa = $rshift$$constant; ++ ++ __ rotri_w(dst, src, sa); ++ %} ++ ins_pipe( ialu_regI_regI ); ++%} ++ ++instruct rolL_Reg_immI_0_31(mRegL dst, mRegLorI2L src, immI_32_63 lshift, immI_0_31 rshift) ++%{ ++ predicate(0 == ((n->in(1)->in(2)->get_int() + n->in(2)->in(2)->get_int()) & 0x3f)); ++ match(Set dst (OrL (LShiftL src lshift) (URShiftL src rshift))); ++ ++ ins_cost(100); ++ format %{ "rotri_d $dst, $src, $rshift #@rolL_Reg_immI_0_31" %} ++ ins_encode %{ ++ Register dst = $dst$$Register; ++ Register src = $src$$Register; ++ int sa = $rshift$$constant; ++ ++ __ rotri_d(dst, src, sa); ++ %} ++ ins_pipe( ialu_regI_regI ); ++%} ++ ++instruct rolL_Reg_immI_32_63(mRegL dst, mRegLorI2L src, immI_0_31 lshift, immI_32_63 rshift) ++%{ ++ predicate(0 == ((n->in(1)->in(2)->get_int() + n->in(2)->in(2)->get_int()) & 0x3f)); ++ match(Set dst (OrL (LShiftL src lshift) (URShiftL src rshift))); ++ ++ ins_cost(100); ++ format %{ "rotri_d $dst, $src, $rshift #@rolL_Reg_immI_32_63" %} ++ ins_encode %{ ++ Register dst = $dst$$Register; ++ Register src = $src$$Register; ++ int sa = $rshift$$constant; ++ ++ __ rotri_d(dst, src, sa); ++ %} ++ ins_pipe( ialu_regI_regI ); ++%} ++ ++instruct rorI_Reg_immI_0_31(mRegI dst, mRegI src, immI_0_31 rshift, immI_0_31 lshift) ++%{ ++ predicate(0 == ((n->in(1)->in(2)->get_int() + n->in(2)->in(2)->get_int()) & 0x1f)); ++ match(Set dst (OrI (URShiftI src rshift) (LShiftI src lshift))); ++ ++ ins_cost(100); ++ format %{ "rotri_w $dst, $src, $rshift #@rorI_Reg_immI_0_31" %} ++ ins_encode %{ ++ Register dst = $dst$$Register; ++ Register src = $src$$Register; ++ int sa = $rshift$$constant; ++ ++ __ rotri_w(dst, src, sa); ++ %} ++ ins_pipe( ialu_regI_regI ); ++%} ++ ++instruct rorL_Reg_immI_0_31(mRegL dst, mRegLorI2L src, immI_0_31 rshift, immI_32_63 lshift) ++%{ ++ predicate(0 == ((n->in(1)->in(2)->get_int() + n->in(2)->in(2)->get_int()) & 0x3f)); ++ match(Set dst (OrL (URShiftL src rshift) (LShiftL src lshift))); ++ ++ ins_cost(100); ++ format %{ "rotri_d $dst, $src, $rshift #@rorL_Reg_immI_0_31" %} ++ ins_encode %{ ++ Register dst = $dst$$Register; ++ Register src = $src$$Register; ++ int sa = $rshift$$constant; ++ ++ __ rotri_d(dst, src, sa); ++ %} ++ ins_pipe( ialu_regI_regI ); ++%} ++ ++instruct rorL_Reg_immI_32_63(mRegL dst, mRegLorI2L src, immI_32_63 rshift, immI_0_31 lshift) ++%{ ++ predicate(0 == ((n->in(1)->in(2)->get_int() + n->in(2)->in(2)->get_int()) & 0x3f)); ++ match(Set dst (OrL (URShiftL src rshift) (LShiftL src lshift))); ++ ++ ins_cost(100); ++ format %{ "rotri_d $dst, $src, $rshift #@rorL_Reg_immI_32_63" %} ++ ins_encode %{ ++ Register dst = $dst$$Register; ++ Register src = $src$$Register; ++ int sa = $rshift$$constant; ++ ++ __ rotri_d(dst, src, sa); ++ %} ++ ins_pipe( ialu_regI_regI ); ++%} ++ ++// Logical Shift Right ++instruct shr_logical_Reg_Reg(mRegI dst, mRegI src, mRegI shift) %{ ++ match(Set dst (URShiftI src shift)); ++ ++ format %{ "SRL_W $dst, $src, $shift #@shr_logical_Reg_Reg" %} ++ ins_encode %{ ++ Register src = $src$$Register; ++ Register dst = $dst$$Register; ++ Register shift = $shift$$Register; ++ __ srl_w(dst, src, shift); ++ %} ++ ins_pipe( ialu_regI_regI ); ++%} ++ ++ ++instruct shr_arith_Reg_imm(mRegI dst, mRegI src, immIU5 shift) %{ ++ match(Set dst (RShiftI src shift)); ++ // effect(KILL cr); ++ ++ format %{ "SRAI_W $dst, $src, $shift #@shr_arith_Reg_imm" %} ++ ins_encode %{ ++ Register src = $src$$Register; ++ Register dst = $dst$$Register; ++ int shift = $shift$$constant; ++ __ srai_w(dst, src, shift); ++ %} ++ ins_pipe( ialu_regI_regI ); ++%} ++ ++instruct shr_arith_Reg_Reg(mRegI dst, mRegI src, mRegI shift) %{ ++ match(Set dst (RShiftI src shift)); ++ // effect(KILL cr); ++ ++ format %{ "SRA_W $dst, $src, $shift #@shr_arith_Reg_Reg" %} ++ ins_encode %{ ++ Register src = $src$$Register; ++ Register dst = $dst$$Register; ++ Register shift = $shift$$Register; ++ __ sra_w(dst, src, shift); ++ %} ++ ins_pipe( ialu_regI_regI ); ++%} ++ ++//----------Convert Int to Boolean--------------------------------------------- ++ ++instruct convI2B(mRegI dst, mRegI src) %{ ++ match(Set dst (Conv2B src)); ++ ++ ins_cost(100); ++ format %{ "convI2B $dst, $src @ convI2B" %} ++ ins_encode %{ ++ Register dst = as_Register($dst$$reg); ++ Register src = as_Register($src$$reg); ++ ++ if (dst != src) { ++ __ addi_d(dst, R0, 1); ++ __ maskeqz(dst, dst, src); ++ } else { ++ __ move(AT, src); ++ __ addi_d(dst, R0, 1); ++ __ maskeqz(dst, dst, AT); ++ } ++ %} ++ ++ ins_pipe( ialu_regL_regL ); ++%} ++ ++instruct convI2L_reg( mRegL dst, mRegI src) %{ ++ match(Set dst (ConvI2L src)); ++ ++ ins_cost(100); ++ format %{ "SLLI_W $dst, $src @ convI2L_reg\t" %} ++ ++ ins_encode %{ ++ Register dst = as_Register($dst$$reg); ++ Register src = as_Register($src$$reg); ++ ++ if(dst != src) __ slli_w(dst, src, 0); ++ %} ++ ins_pipe( ialu_regL_regL ); ++%} ++ ++instruct convL2I_reg( mRegI dst, mRegLorI2L src ) %{ ++ match(Set dst (ConvL2I src)); ++ ++ format %{ "MOV $dst, $src @ convL2I_reg" %} ++ ins_encode %{ ++ Register dst = as_Register($dst$$reg); ++ Register src = as_Register($src$$reg); ++ ++ __ slli_w(dst, src, 0); ++ %} ++ ++ ins_pipe( ialu_regI_regI ); ++%} ++ ++instruct convL2D_reg( regD dst, mRegL src ) %{ ++ match(Set dst (ConvL2D src)); ++ format %{ "convL2D $dst, $src @ convL2D_reg" %} ++ ins_encode %{ ++ Register src = as_Register($src$$reg); ++ FloatRegister dst = as_FloatRegister($dst$$reg); ++ ++ __ movgr2fr_d(dst, src); ++ __ ffint_d_l(dst, dst); ++ %} ++ ++ ins_pipe( pipe_slow ); ++%} ++ ++ ++// Convert double to int. ++// If the double is NaN, stuff a zero in instead. ++instruct convD2I_reg_reg(mRegI dst, regD src, regD tmp) %{ ++ match(Set dst (ConvD2I src)); ++ effect(USE src, TEMP tmp); ++ ++ format %{ "convd2i $dst, $src, using $tmp as TEMP @ convD2I_reg_reg" %} ++ ++ ins_encode %{ ++ __ ftintrz_w_d($tmp$$FloatRegister, $src$$FloatRegister); ++ __ movfr2gr_s($dst$$Register, $tmp$$FloatRegister); ++ %} ++ ++ ins_pipe( pipe_slow ); ++%} ++ ++instruct convD2L_reg_reg(mRegL dst, regD src, regD tmp) %{ ++ match(Set dst (ConvD2L src)); ++ effect(USE src, TEMP tmp); ++ ++ format %{ "convd2l $dst, $src, using $tmp as TEMP @ convD2L_reg_reg" %} ++ ++ ins_encode %{ ++ __ ftintrz_l_d($tmp$$FloatRegister, $src$$FloatRegister); ++ __ movfr2gr_d($dst$$Register, $tmp$$FloatRegister); ++ %} ++ ++ ins_pipe( pipe_slow ); ++%} ++ ++// Convert float to int. ++// If the float is NaN, stuff a zero in instead. ++instruct convF2I_reg_reg(mRegI dst, regF src, regF tmp) %{ ++ match(Set dst (ConvF2I src)); ++ effect(USE src, TEMP tmp); ++ ++ format %{ "convf2i $dst, $src, using $tmp as TEMP @ convF2I_reg_reg" %} ++ ++ ins_encode %{ ++ __ ftintrz_w_s($tmp$$FloatRegister, $src$$FloatRegister); ++ __ movfr2gr_s($dst$$Register, $tmp$$FloatRegister); ++ %} ++ ++ ins_pipe( pipe_slow ); ++%} ++ ++instruct convF2L_reg_reg(mRegL dst, regF src, regF tmp) %{ ++ match(Set dst (ConvF2L src)); ++ effect(USE src, TEMP tmp); ++ ++ format %{ "convf2l $dst, $src, using $tmp as TEMP @ convF2L_reg_reg" %} ++ ++ ins_encode %{ ++ __ ftintrz_l_s($tmp$$FloatRegister, $src$$FloatRegister); ++ __ movfr2gr_d($dst$$Register, $tmp$$FloatRegister); ++ %} ++ ++ ins_pipe( pipe_slow ); ++%} ++ ++ ++instruct convL2F_reg( regF dst, mRegL src ) %{ ++ match(Set dst (ConvL2F src)); ++ format %{ "convl2f $dst, $src @ convL2F_reg" %} ++ ins_encode %{ ++ FloatRegister dst = $dst$$FloatRegister; ++ Register src = as_Register($src$$reg); ++ Label L; ++ ++ __ movgr2fr_d(dst, src); ++ __ ffint_s_l(dst, dst); ++ %} ++ ++ ins_pipe( pipe_slow ); ++%} ++ ++instruct convI2F_reg( regF dst, mRegI src ) %{ ++ match(Set dst (ConvI2F src)); ++ format %{ "convi2f $dst, $src @ convI2F_reg" %} ++ ins_encode %{ ++ Register src = $src$$Register; ++ FloatRegister dst = $dst$$FloatRegister; ++ ++ __ movgr2fr_w(dst, src); ++ __ ffint_s_w(dst, dst); ++ %} ++ ++ ins_pipe( fpu_regF_regF ); ++%} ++ ++instruct cmpLTMask_immI_0( mRegI dst, mRegI p, immI_0 zero ) %{ ++ match(Set dst (CmpLTMask p zero)); ++ ins_cost(100); ++ ++ format %{ "srai_w $dst, $p, 31 @ cmpLTMask_immI_0" %} ++ ins_encode %{ ++ Register src = $p$$Register; ++ Register dst = $dst$$Register; ++ ++ __ srai_w(dst, src, 31); ++ %} ++ ins_pipe( pipe_slow ); ++%} ++ ++ ++instruct cmpLTMask( mRegI dst, mRegI p, mRegI q ) %{ ++ match(Set dst (CmpLTMask p q)); ++ ins_cost(400); ++ ++ format %{ "cmpLTMask $dst, $p, $q @ cmpLTMask" %} ++ ins_encode %{ ++ Register p = $p$$Register; ++ Register q = $q$$Register; ++ Register dst = $dst$$Register; ++ ++ __ slt(dst, p, q); ++ __ sub_d(dst, R0, dst); ++ %} ++ ins_pipe( pipe_slow ); ++%} ++ ++instruct convP2B(mRegI dst, mRegP src) %{ ++ match(Set dst (Conv2B src)); ++ ++ ins_cost(100); ++ format %{ "convP2B $dst, $src @ convP2B" %} ++ ins_encode %{ ++ Register dst = as_Register($dst$$reg); ++ Register src = as_Register($src$$reg); ++ ++ if (dst != src) { ++ __ addi_d(dst, R0, 1); ++ __ maskeqz(dst, dst, src); ++ } else { ++ __ move(AT, src); ++ __ addi_d(dst, R0, 1); ++ __ maskeqz(dst, dst, AT); ++ } ++ %} ++ ++ ins_pipe( ialu_regL_regL ); ++%} ++ ++ ++instruct convI2D_reg_reg(regD dst, mRegI src) %{ ++ match(Set dst (ConvI2D src)); ++ format %{ "conI2D $dst, $src @convI2D_reg" %} ++ ins_encode %{ ++ Register src = $src$$Register; ++ FloatRegister dst = $dst$$FloatRegister; ++ __ movgr2fr_w(dst ,src); ++ __ ffint_d_w(dst, dst); ++ %} ++ ins_pipe( fpu_regF_regF ); ++%} ++ ++instruct convF2D_reg_reg(regD dst, regF src) %{ ++ match(Set dst (ConvF2D src)); ++ format %{ "convF2D $dst, $src\t# @convF2D_reg_reg" %} ++ ins_encode %{ ++ FloatRegister dst = $dst$$FloatRegister; ++ FloatRegister src = $src$$FloatRegister; ++ ++ __ fcvt_d_s(dst, src); ++ %} ++ ins_pipe( fpu_regF_regF ); ++%} ++ ++instruct convD2F_reg_reg(regF dst, regD src) %{ ++ match(Set dst (ConvD2F src)); ++ format %{ "convD2F $dst, $src\t# @convD2F_reg_reg" %} ++ ins_encode %{ ++ FloatRegister dst = $dst$$FloatRegister; ++ FloatRegister src = $src$$FloatRegister; ++ ++ __ fcvt_s_d(dst, src); ++ %} ++ ins_pipe( fpu_regF_regF ); ++%} ++ ++ ++// Convert oop pointer into compressed form ++instruct encodeHeapOop(mRegN dst, mRegP src) %{ ++ predicate(n->bottom_type()->make_ptr()->ptr() != TypePtr::NotNull); ++ match(Set dst (EncodeP src)); ++ format %{ "encode_heap_oop $dst,$src" %} ++ ins_encode %{ ++ Register src = $src$$Register; ++ Register dst = $dst$$Register; ++ ++ __ encode_heap_oop(dst, src); ++ %} ++ ins_pipe( ialu_regL_regL ); ++%} ++ ++instruct encodeHeapOop_not_null(mRegN dst, mRegP src) %{ ++ predicate(n->bottom_type()->make_ptr()->ptr() == TypePtr::NotNull); ++ match(Set dst (EncodeP src)); ++ format %{ "encode_heap_oop_not_null $dst,$src @ encodeHeapOop_not_null" %} ++ ins_encode %{ ++ __ encode_heap_oop_not_null($dst$$Register, $src$$Register); ++ %} ++ ins_pipe( ialu_regL_regL ); ++%} ++ ++instruct decodeHeapOop(mRegP dst, mRegN src) %{ ++ predicate(n->bottom_type()->is_ptr()->ptr() != TypePtr::NotNull && ++ n->bottom_type()->is_ptr()->ptr() != TypePtr::Constant); ++ match(Set dst (DecodeN src)); ++ format %{ "decode_heap_oop $dst,$src @ decodeHeapOop" %} ++ ins_encode %{ ++ Register s = $src$$Register; ++ Register d = $dst$$Register; ++ ++ __ decode_heap_oop(d, s); ++ %} ++ ins_pipe( ialu_regL_regL ); ++%} ++ ++instruct decodeHeapOop_not_null(mRegP dst, mRegN src) %{ ++ predicate(n->bottom_type()->is_ptr()->ptr() == TypePtr::NotNull || ++ n->bottom_type()->is_ptr()->ptr() == TypePtr::Constant); ++ match(Set dst (DecodeN src)); ++ format %{ "decode_heap_oop_not_null $dst,$src @ decodeHeapOop_not_null" %} ++ ins_encode %{ ++ Register s = $src$$Register; ++ Register d = $dst$$Register; ++ if (s != d) { ++ __ decode_heap_oop_not_null(d, s); ++ } else { ++ __ decode_heap_oop_not_null(d); ++ } ++ %} ++ ins_pipe( ialu_regL_regL ); ++%} ++ ++instruct encodeKlass_not_null(mRegN dst, mRegP src) %{ ++ match(Set dst (EncodePKlass src)); ++ format %{ "encode_heap_oop_not_null $dst,$src @ encodeKlass_not_null" %} ++ ins_encode %{ ++ __ encode_klass_not_null($dst$$Register, $src$$Register); ++ %} ++ ins_pipe( ialu_regL_regL ); ++%} ++ ++instruct decodeKlass_not_null(mRegP dst, mRegN src) %{ ++ match(Set dst (DecodeNKlass src)); ++ format %{ "decode_heap_klass_not_null $dst,$src" %} ++ ins_encode %{ ++ Register s = $src$$Register; ++ Register d = $dst$$Register; ++ if (s != d) { ++ __ decode_klass_not_null(d, s); ++ } else { ++ __ decode_klass_not_null(d); ++ } ++ %} ++ ins_pipe( ialu_regL_regL ); ++%} ++ ++//FIXME ++instruct tlsLoadP(mRegP dst) %{ ++ match(Set dst (ThreadLocal)); ++ ++ ins_cost(0); ++ format %{ " get_thread in $dst #@tlsLoadP" %} ++ ins_encode %{ ++ Register dst = $dst$$Register; ++#ifdef OPT_THREAD ++ __ move(dst, TREG); ++#else ++ __ get_thread(dst); ++#endif ++ %} ++ ++ ins_pipe( ialu_loadI ); ++%} ++ ++ ++instruct checkCastPP( mRegP dst ) %{ ++ match(Set dst (CheckCastPP dst)); ++ ++ format %{ "#checkcastPP of $dst (empty encoding) #@chekCastPP" %} ++ ins_encode( /*empty encoding*/ ); ++ ins_pipe( empty ); ++%} ++ ++instruct castPP(mRegP dst) ++%{ ++ match(Set dst (CastPP dst)); ++ ++ size(0); ++ format %{ "# castPP of $dst" %} ++ ins_encode(/* empty encoding */); ++ ins_pipe(empty); ++%} ++ ++instruct castII( mRegI dst ) %{ ++ match(Set dst (CastII dst)); ++ format %{ "#castII of $dst empty encoding" %} ++ ins_encode( /*empty encoding*/ ); ++ ins_cost(0); ++ ins_pipe( empty ); ++%} ++ ++// Return Instruction ++// Remove the return address & jump to it. ++instruct Ret() %{ ++ match(Return); ++ format %{ "RET #@Ret" %} ++ ++ ins_encode %{ ++ __ jr(RA); ++ %} ++ ++ ins_pipe( pipe_jump ); ++%} ++ ++ ++ ++// Tail Jump; remove the return address; jump to target. ++// TailCall above leaves the return address around. ++// TailJump is used in only one place, the rethrow_Java stub (fancy_jump=2). ++// ex_oop (Exception Oop) is needed in %o0 at the jump. As there would be a ++// "restore" before this instruction (in Epilogue), we need to materialize it ++// in %i0. ++//FIXME ++instruct tailjmpInd(no_Ax_mRegP jump_target, mRegP ex_oop) %{ ++ match( TailJump jump_target ex_oop ); ++ ins_cost(200); ++ format %{ "Jmp $jump_target ; ex_oop = $ex_oop #@tailjmpInd" %} ++ ins_encode %{ ++ Register target = $jump_target$$Register; ++ ++ // V0, V1 are indicated in: ++ // [stubGenerator_loongarch.cpp] generate_forward_exception() ++ // [runtime_loongarch.cpp] OptoRuntime::generate_exception_blob() ++ // ++ Register oop = $ex_oop$$Register; ++ Register exception_oop = V0; ++ Register exception_pc = V1; ++ ++ __ move(exception_pc, RA); ++ __ move(exception_oop, oop); ++ ++ __ jr(target); ++ %} ++ ins_pipe( pipe_jump ); ++%} ++ ++// ============================================================================ ++// Procedure Call/Return Instructions ++// Call Java Static Instruction ++// Note: If this code changes, the corresponding ret_addr_offset() and ++// compute_padding() functions will have to be adjusted. ++instruct CallStaticJavaDirect(method meth) %{ ++ match(CallStaticJava); ++ effect(USE meth); ++ ++ ins_cost(300); ++ format %{ "CALL,static #@CallStaticJavaDirect " %} ++ ins_encode( Java_Static_Call( meth ) ); ++ ins_pipe( pipe_slow ); ++ ins_pc_relative(1); ++ ins_alignment(4); ++%} ++ ++// Call Java Dynamic Instruction ++// Note: If this code changes, the corresponding ret_addr_offset() and ++// compute_padding() functions will have to be adjusted. ++instruct CallDynamicJavaDirect(method meth) %{ ++ match(CallDynamicJava); ++ effect(USE meth); ++ ++ ins_cost(300); ++ format %{"MOV IC_Klass, #Universe::non_oop_word()\n\t" ++ "CallDynamic @ CallDynamicJavaDirect" %} ++ ins_encode( Java_Dynamic_Call( meth ) ); ++ ins_pipe( pipe_slow ); ++ ins_pc_relative(1); ++ ins_alignment(4); ++%} ++ ++instruct CallLeafNoFPDirect(method meth) %{ ++ match(CallLeafNoFP); ++ effect(USE meth); ++ ++ ins_cost(300); ++ format %{ "CALL_LEAF_NOFP,runtime " %} ++ ins_encode(Java_To_Runtime(meth)); ++ ins_pipe( pipe_slow ); ++ ins_pc_relative(1); ++ ins_alignment(4); ++%} ++ ++// Prefetch instructions. ++ ++instruct prefetchr( memory mem ) %{ ++ match(PrefetchRead mem); ++ ins_cost(125); ++ ++ format %{ "pref $mem\t# Prefetch into temporal cache for read @ prefetchr" %} ++ ins_encode %{ ++ int base = $mem$$base; ++ int index = $mem$$index; ++ int scale = $mem$$scale; ++ int disp = $mem$$disp; ++ ++ if( index != 0 ) { ++ if (scale == 0) { ++ __ add_d(AT, as_Register(base), as_Register(index)); ++ } else { ++ __ alsl_d(AT, as_Register(index), as_Register(base), scale - 1); ++ } ++ } else { ++ __ move(AT, as_Register(base)); ++ } ++ if( Assembler::is_simm(disp, 12) ) { ++ __ addi_d(AT, AT, disp); ++ } else { ++ __ li(T4, disp); ++ __ add_d(AT, AT, T4); ++ } ++ __ preld(0, AT, 0); //hint: 0:load ++ %} ++ ins_pipe(pipe_slow); ++%} ++ ++instruct prefetchw( memory mem ) %{ ++ match(PrefetchWrite mem); ++ ins_cost(125); ++ format %{ "pref $mem\t# Prefetch to temporal cache for write @ prefetchw" %} ++ ins_encode %{ ++ int base = $mem$$base; ++ int index = $mem$$index; ++ int scale = $mem$$scale; ++ int disp = $mem$$disp; ++ ++ if( index != 0 ) { ++ if (scale == 0) { ++ __ add_d(AT, as_Register(base), as_Register(index)); ++ } else { ++ __ alsl_d(AT, as_Register(index), as_Register(base), scale - 1); ++ } ++ } else { ++ __ move(AT, as_Register(base)); ++ } ++ if( Assembler::is_simm(disp, 12) ) { ++ __ addi_d(AT, AT, disp); ++ } else { ++ __ li(T4, disp); ++ __ add_d(AT, AT, T4); ++ } ++ __ preld(8, AT, 0); //hint: 8:store ++ %} ++ ins_pipe(pipe_slow); ++%} ++ ++// Prefetch instructions for allocation. ++ ++instruct prefetchAlloc(memory mem) %{ ++ match(PrefetchAllocation mem); ++ ins_cost(125); ++ format %{ "preld $mem\t# Prefetch allocation @ prefetchAlloc" %} ++ ins_encode %{ ++ int base = $mem$$base; ++ int index = $mem$$index; ++ int scale = $mem$$scale; ++ int disp = $mem$$disp; ++ ++ if (index != 0) { ++ if (scale == 0) { ++ __ add_d(AT, as_Register(base), as_Register(index)); ++ } else { ++ __ alsl_d(AT, as_Register(index), as_Register(base), scale - 1); ++ } ++ ++ if (Assembler::is_simm(disp, 12)) { ++ __ preld(8, AT, disp); ++ } else { ++ __ li(T4, disp); ++ __ add_d(AT, AT, T4); ++ __ preld(8, AT, 0); ++ } ++ } else { ++ if (Assembler::is_simm(disp, 12)) { ++ __ preld(8, as_Register(base), disp); ++ } else { ++ __ li(T4, disp); ++ __ add_d(AT, as_Register(base), T4); ++ __ preld(8, AT, 0); ++ } ++ } ++ %} ++ ins_pipe(pipe_slow); ++%} ++ ++ ++// Call runtime without safepoint ++instruct CallLeafDirect(method meth) %{ ++ match(CallLeaf); ++ effect(USE meth); ++ ++ ins_cost(300); ++ format %{ "CALL_LEAF,runtime #@CallLeafDirect " %} ++ ins_encode(Java_To_Runtime(meth)); ++ ins_pipe( pipe_slow ); ++ ins_pc_relative(1); ++ ins_alignment(4); ++%} ++ ++// Load Char (16bit unsigned) ++instruct loadUS(mRegI dst, memory mem) %{ ++ match(Set dst (LoadUS mem)); ++ ++ ins_cost(125); ++ format %{ "loadUS $dst,$mem @ loadC" %} ++ ins_encode %{ ++ __ loadstore_enc($dst$$Register, $mem$$base, $mem$$index, $mem$$scale, $mem$$disp, MacroAssembler::LOAD_U_SHORT); ++ %} ++ ins_pipe( ialu_loadI ); ++%} ++ ++instruct loadUS_convI2L(mRegL dst, memory mem) %{ ++ match(Set dst (ConvI2L (LoadUS mem))); ++ ++ ins_cost(125); ++ format %{ "loadUS $dst,$mem @ loadUS_convI2L" %} ++ ins_encode %{ ++ __ loadstore_enc($dst$$Register, $mem$$base, $mem$$index, $mem$$scale, $mem$$disp, MacroAssembler::LOAD_U_SHORT); ++ %} ++ ins_pipe( ialu_loadI ); ++%} ++ ++// Store Char (16bit unsigned) ++instruct storeC(memory mem, mRegI src) %{ ++ match(Set mem (StoreC mem src)); ++ ++ ins_cost(125); ++ format %{ "storeC $src, $mem @ storeC" %} ++ ins_encode %{ ++ __ loadstore_enc($src$$Register, $mem$$base, $mem$$index, $mem$$scale, $mem$$disp, MacroAssembler::STORE_CHAR); ++ %} ++ ins_pipe( ialu_loadI ); ++%} ++ ++instruct storeC_0(memory mem, immI_0 zero) %{ ++ match(Set mem (StoreC mem zero)); ++ ++ ins_cost(125); ++ format %{ "storeC $zero, $mem @ storeC_0" %} ++ ins_encode %{ ++ __ loadstore_enc(R0, $mem$$base, $mem$$index, $mem$$scale, $mem$$disp, MacroAssembler::STORE_SHORT); ++ %} ++ ins_pipe( ialu_loadI ); ++%} ++ ++ ++instruct loadConF_immF_0(regF dst, immF_0 zero) %{ ++ match(Set dst zero); ++ ins_cost(100); ++ ++ format %{ "mov $dst, zero @ loadConF_immF_0\n"%} ++ ins_encode %{ ++ FloatRegister dst = $dst$$FloatRegister; ++ ++ __ movgr2fr_w(dst, R0); ++ %} ++ ins_pipe( fpu_loadF ); ++%} ++ ++ ++instruct loadConF(regF dst, immF src) %{ ++ match(Set dst src); ++ ins_cost(125); ++ ++ format %{ "fld_s $dst, $constantoffset[$constanttablebase] # load FLOAT $src from table @ loadConF" %} ++ ins_encode %{ ++ int con_offset = $constantoffset($src); ++ ++ if (Assembler::is_simm(con_offset, 12)) { ++ __ fld_s($dst$$FloatRegister, $constanttablebase, con_offset); ++ } else { ++ __ li(AT, con_offset); ++ __ fldx_s($dst$$FloatRegister, $constanttablebase, AT); ++ } ++ %} ++ ins_pipe( fpu_loadF ); ++%} ++ ++ ++instruct loadConD_immD_0(regD dst, immD_0 zero) %{ ++ match(Set dst zero); ++ ins_cost(100); ++ ++ format %{ "mov $dst, zero @ loadConD_immD_0"%} ++ ins_encode %{ ++ FloatRegister dst = as_FloatRegister($dst$$reg); ++ ++ __ movgr2fr_d(dst, R0); ++ %} ++ ins_pipe( fpu_loadF ); ++%} ++ ++instruct loadConD(regD dst, immD src) %{ ++ match(Set dst src); ++ ins_cost(125); ++ ++ format %{ "fld_d $dst, $constantoffset[$constanttablebase] # load DOUBLE $src from table @ loadConD" %} ++ ins_encode %{ ++ int con_offset = $constantoffset($src); ++ ++ if (Assembler::is_simm(con_offset, 12)) { ++ __ fld_d($dst$$FloatRegister, $constanttablebase, con_offset); ++ } else { ++ __ li(AT, con_offset); ++ __ fldx_d($dst$$FloatRegister, $constanttablebase, AT); ++ } ++ %} ++ ins_pipe( fpu_loadF ); ++%} ++ ++// Store register Float value (it is faster than store from FPU register) ++instruct storeF_reg( memory mem, regF src) %{ ++ match(Set mem (StoreF mem src)); ++ ++ ins_cost(50); ++ format %{ "store $mem, $src\t# store float @ storeF_reg" %} ++ ins_encode %{ ++ __ loadstore_enc($src$$FloatRegister, $mem$$base, $mem$$index, $mem$$scale, $mem$$disp, MacroAssembler::STORE_FLOAT); ++ %} ++ ins_pipe( fpu_storeF ); ++%} ++ ++instruct storeF_immF_0( memory mem, immF_0 zero) %{ ++ match(Set mem (StoreF mem zero)); ++ ++ ins_cost(40); ++ format %{ "store $mem, zero\t# store float @ storeF_immF_0" %} ++ ins_encode %{ ++ __ loadstore_enc(R0, $mem$$base, $mem$$index, $mem$$scale, $mem$$disp, MacroAssembler::STORE_INT); ++ %} ++ ins_pipe( ialu_storeI ); ++%} ++ ++// Load Double ++instruct loadD(regD dst, memory mem) %{ ++ match(Set dst (LoadD mem)); ++ ++ ins_cost(150); ++ format %{ "loadD $dst, $mem #@loadD" %} ++ ins_encode %{ ++ __ loadstore_enc($dst$$FloatRegister, $mem$$base, $mem$$index, $mem$$scale, $mem$$disp, MacroAssembler::LOAD_DOUBLE); ++ %} ++ ins_pipe( ialu_loadI ); ++%} ++ ++// Load Double - UNaligned ++instruct loadD_unaligned(regD dst, memory mem ) %{ ++ match(Set dst (LoadD_unaligned mem)); ++ ins_cost(250); ++ // FIXME: Need more effective ldl/ldr ++ format %{ "loadD_unaligned $dst, $mem #@loadD_unaligned" %} ++ ins_encode %{ ++ __ loadstore_enc($dst$$FloatRegister, $mem$$base, $mem$$index, $mem$$scale, $mem$$disp, MacroAssembler::LOAD_DOUBLE); ++ %} ++ ins_pipe( ialu_loadI ); ++%} ++ ++instruct storeD_reg( memory mem, regD src) %{ ++ match(Set mem (StoreD mem src)); ++ ++ ins_cost(50); ++ format %{ "store $mem, $src\t# store float @ storeD_reg" %} ++ ins_encode %{ ++ __ loadstore_enc($src$$FloatRegister, $mem$$base, $mem$$index, $mem$$scale, $mem$$disp, MacroAssembler::STORE_DOUBLE); ++ %} ++ ins_pipe( fpu_storeF ); ++%} ++ ++instruct storeD_immD_0( memory mem, immD_0 zero) %{ ++ match(Set mem (StoreD mem zero)); ++ ++ ins_cost(40); ++ format %{ "store $mem, zero\t# store float @ storeD_immD_0" %} ++ ins_encode %{ ++ __ loadstore_enc(R0, $mem$$base, $mem$$index, $mem$$scale, $mem$$disp, MacroAssembler::STORE_LONG); ++ %} ++ ins_pipe( ialu_storeI ); ++%} ++ ++instruct loadSSI(mRegI dst, stackSlotI src) ++%{ ++ match(Set dst src); ++ ++ ins_cost(125); ++ format %{ "ld_w $dst, $src\t# int stk @ loadSSI" %} ++ ins_encode %{ ++ guarantee( Assembler::is_simm($src$$disp, 12), "disp too long (loadSSI) !"); ++ __ ld_w($dst$$Register, SP, $src$$disp); ++ %} ++ ins_pipe(ialu_loadI); ++%} ++ ++instruct storeSSI(stackSlotI dst, mRegI src) ++%{ ++ match(Set dst src); ++ ++ ins_cost(100); ++ format %{ "st_w $dst, $src\t# int stk @ storeSSI" %} ++ ins_encode %{ ++ guarantee( Assembler::is_simm($dst$$disp, 12), "disp too long (storeSSI) !"); ++ __ st_w($src$$Register, SP, $dst$$disp); ++ %} ++ ins_pipe(ialu_storeI); ++%} ++ ++instruct loadSSL(mRegL dst, stackSlotL src) ++%{ ++ match(Set dst src); ++ ++ ins_cost(125); ++ format %{ "ld_d $dst, $src\t# long stk @ loadSSL" %} ++ ins_encode %{ ++ guarantee( Assembler::is_simm($src$$disp, 12), "disp too long (loadSSL) !"); ++ __ ld_d($dst$$Register, SP, $src$$disp); ++ %} ++ ins_pipe(ialu_loadI); ++%} ++ ++instruct storeSSL(stackSlotL dst, mRegL src) ++%{ ++ match(Set dst src); ++ ++ ins_cost(100); ++ format %{ "st_d $dst, $src\t# long stk @ storeSSL" %} ++ ins_encode %{ ++ guarantee( Assembler::is_simm($dst$$disp, 12), "disp too long (storeSSL) !"); ++ __ st_d($src$$Register, SP, $dst$$disp); ++ %} ++ ins_pipe(ialu_storeI); ++%} ++ ++instruct loadSSP(mRegP dst, stackSlotP src) ++%{ ++ match(Set dst src); ++ ++ ins_cost(125); ++ format %{ "ld_d $dst, $src\t# ptr stk @ loadSSP" %} ++ ins_encode %{ ++ guarantee( Assembler::is_simm($src$$disp, 12), "disp too long (loadSSP) !"); ++ __ ld_d($dst$$Register, SP, $src$$disp); ++ %} ++ ins_pipe(ialu_loadI); ++%} ++ ++instruct storeSSP(stackSlotP dst, mRegP src) ++%{ ++ match(Set dst src); ++ ++ ins_cost(100); ++ format %{ "sd $dst, $src\t# ptr stk @ storeSSP" %} ++ ins_encode %{ ++ guarantee( Assembler::is_simm($dst$$disp, 12), "disp too long (storeSSP) !"); ++ __ st_d($src$$Register, SP, $dst$$disp); ++ %} ++ ins_pipe(ialu_storeI); ++%} ++ ++instruct loadSSF(regF dst, stackSlotF src) ++%{ ++ match(Set dst src); ++ ++ ins_cost(125); ++ format %{ "fld_s $dst, $src\t# float stk @ loadSSF" %} ++ ins_encode %{ ++ guarantee( Assembler::is_simm($src$$disp, 12), "disp too long (loadSSF) !"); ++ __ fld_s($dst$$FloatRegister, SP, $src$$disp); ++ %} ++ ins_pipe(ialu_loadI); ++%} ++ ++instruct storeSSF(stackSlotF dst, regF src) ++%{ ++ match(Set dst src); ++ ++ ins_cost(100); ++ format %{ "fst_s $dst, $src\t# float stk @ storeSSF" %} ++ ins_encode %{ ++ guarantee( Assembler::is_simm($dst$$disp, 12), "disp too long (storeSSF) !"); ++ __ fst_s($src$$FloatRegister, SP, $dst$$disp); ++ %} ++ ins_pipe(fpu_storeF); ++%} ++ ++// Use the same format since predicate() can not be used here. ++instruct loadSSD(regD dst, stackSlotD src) ++%{ ++ match(Set dst src); ++ ++ ins_cost(125); ++ format %{ "fld_d $dst, $src\t# double stk @ loadSSD" %} ++ ins_encode %{ ++ guarantee( Assembler::is_simm($src$$disp, 12), "disp too long (loadSSD) !"); ++ __ fld_d($dst$$FloatRegister, SP, $src$$disp); ++ %} ++ ins_pipe(ialu_loadI); ++%} ++ ++instruct storeSSD(stackSlotD dst, regD src) ++%{ ++ match(Set dst src); ++ ++ ins_cost(100); ++ format %{ "sdc1 $dst, $src\t# double stk @ storeSSD" %} ++ ins_encode %{ ++ guarantee( Assembler::is_simm($dst$$disp, 12), "disp too long (storeSSD) !"); ++ __ fst_d($src$$FloatRegister, SP, $dst$$disp); ++ %} ++ ins_pipe(fpu_storeF); ++%} ++ ++instruct cmpFastLock(FlagsReg cr, mRegP object, mRegP box, mRegI tmp, mRegI scr) %{ ++ match(Set cr (FastLock object box)); ++ effect(TEMP tmp, TEMP scr); ++ ins_cost(300); ++ format %{ "FASTLOCK $cr <-- $object, $box, $tmp, $scr #@ cmpFastLock" %} ++ ins_encode %{ ++ __ fast_lock($object$$Register, $box$$Register, $cr$$Register, $tmp$$Register, $scr$$Register); ++ %} ++ ++ ins_pipe( pipe_slow ); ++ ins_pc_relative(1); ++%} ++ ++instruct cmpFastUnlock(FlagsReg cr, mRegP object, mRegP box, mRegI tmp, mRegI scr) %{ ++ match(Set cr (FastUnlock object box)); ++ effect(TEMP tmp, TEMP scr); ++ ins_cost(300); ++ format %{ "FASTUNLOCK $cr <-- $object, $box, $tmp #@cmpFastUnlock" %} ++ ins_encode %{ ++ __ fast_unlock($object$$Register, $box$$Register, $cr$$Register, $tmp$$Register, $scr$$Register); ++ %} ++ ++ ins_pipe( pipe_slow ); ++ ins_pc_relative(1); ++%} ++ ++// Store CMS card-mark Immediate 0 ++instruct storeImmCM(memory mem, immI_0 zero) %{ ++ match(Set mem (StoreCM mem zero)); ++ ++ ins_cost(150); ++ format %{ "StoreCM MEMBAR loadstore\n\t" ++ "st_b $mem, zero\t! CMS card-mark imm0" %} ++ ins_encode %{ ++ __ membar(__ StoreStore); ++ __ loadstore_enc(R0, $mem$$base, $mem$$index, $mem$$scale, $mem$$disp, MacroAssembler::STORE_BYTE); ++ %} ++ ins_pipe( ialu_storeI ); ++%} ++ ++// Die now ++instruct ShouldNotReachHere( ) ++%{ ++ match(Halt); ++ ins_cost(300); ++ ++ // Use the following format syntax ++ format %{ "ILLTRAP ;#@ShouldNotReachHere" %} ++ ins_encode %{ ++ // Here we should emit illtrap! ++ __ brk(18); ++ %} ++ ins_pipe( pipe_jump ); ++%} ++ ++instruct leaP12Narrow(mRegP dst, indOffset12Narrow mem) ++%{ ++ predicate(Universe::narrow_oop_shift() == 0); ++ match(Set dst mem); ++ ++ ins_cost(110); ++ format %{ "leaq $dst, $mem\t# ptr off12narrow @ leaP12Narrow" %} ++ ins_encode %{ ++ Register dst = $dst$$Register; ++ Register base = as_Register($mem$$base); ++ int disp = $mem$$disp; ++ ++ __ addi_d(dst, base, disp); ++ %} ++ ins_pipe( ialu_regI_imm16 ); ++%} ++ ++instruct leaPIdxScale(mRegP dst, mRegP reg, mRegLorI2L lreg, immI_0_3 scale) ++%{ ++ match(Set dst (AddP reg (LShiftL lreg scale))); ++ ++ ins_cost(110); ++ format %{ "leaq $dst, [$reg + $lreg << $scale]\t# @ leaPIdxScale" %} ++ ins_encode %{ ++ Register dst = $dst$$Register; ++ Register base = $reg$$Register; ++ Register index = $lreg$$Register; ++ int scale = $scale$$constant; ++ ++ if (scale == 0) { ++ __ add_d($dst$$Register, $reg$$Register, index); ++ } else { ++ __ alsl_d(dst, index, base, scale - 1); ++ } ++ %} ++ ++ ins_pipe( ialu_regI_imm16 ); ++%} ++ ++ ++// ============================================================================ ++// The 2nd slow-half of a subtype check. Scan the subklass's 2ndary superklass ++// array for an instance of the superklass. Set a hidden internal cache on a ++// hit (cache is checked with exposed code in gen_subtype_check()). Return ++// NZ for a miss or zero for a hit. The encoding ALSO sets flags. ++instruct partialSubtypeCheck( mRegP result, no_T8_mRegP sub, no_T8_mRegP super, mT8RegI tmp ) %{ ++ match(Set result (PartialSubtypeCheck sub super)); ++ effect(KILL tmp); ++ ins_cost(1100); // slightly larger than the next version ++ format %{ "partialSubtypeCheck result=$result, sub=$sub, super=$super, tmp=$tmp " %} ++ ++ ins_encode( enc_PartialSubtypeCheck(result, sub, super, tmp) ); ++ ins_pipe( pipe_slow ); ++%} ++ ++// Conditional-store of the updated heap-top. ++// Used during allocation of the shared heap. ++ ++instruct storePConditional(memory heap_top_ptr, mRegP oldval, mRegP newval, FlagsReg cr) %{ ++ match(Set cr (StorePConditional heap_top_ptr (Binary oldval newval))); ++ ++ format %{ "move AT, $newval\n\t" ++ "sc_d $heap_top_ptr, AT\t# (ptr) @storePConditional \n\t" ++ "move $cr, AT\n" %} ++ ins_encode%{ ++ Register oldval = $oldval$$Register; ++ Register newval = $newval$$Register; ++ Address addr(as_Register($heap_top_ptr$$base), $heap_top_ptr$$disp); ++ ++ int index = $heap_top_ptr$$index; ++ int scale = $heap_top_ptr$$scale; ++ int disp = $heap_top_ptr$$disp; ++ ++ guarantee(Assembler::is_simm(disp, 12), ""); ++ ++ if (index != 0) { ++ __ stop("in storePConditional: index != 0"); ++ } else { ++ __ move(AT, newval); ++ __ sc_d(AT, addr); ++ __ move($cr$$Register, AT); ++ } ++ %} ++ ins_pipe(long_memory_op); ++%} ++ ++// Conditional-store of an int value. ++// AT flag is set on success, reset otherwise. ++instruct storeIConditional(memory mem, mRegI oldval, mRegI newval, FlagsReg cr) %{ ++ match(Set cr (StoreIConditional mem (Binary oldval newval))); ++ format %{ "CMPXCHG $newval, $mem, $oldval \t# @storeIConditional" %} ++ ++ ins_encode %{ ++ Register oldval = $oldval$$Register; ++ Register newval = $newval$$Register; ++ Register cr = $cr$$Register; ++ Address addr(as_Register($mem$$base), $mem$$disp); ++ ++ int index = $mem$$index; ++ int scale = $mem$$scale; ++ int disp = $mem$$disp; ++ ++ guarantee(Assembler::is_simm(disp, 12), ""); ++ ++ if (index != 0) { ++ __ stop("in storeIConditional: index != 0"); ++ } else { ++ if (cr != addr.base() && cr != oldval && cr != newval) { ++ __ cmpxchg32(addr, oldval, newval, cr, true, false, true); ++ } else { ++ __ cmpxchg32(addr, oldval, newval, AT, true, false, true); ++ __ move(cr, AT); ++ } ++ } ++ %} ++ ++ ins_pipe(long_memory_op); ++%} ++ ++// Conditional-store of a long value. ++// ZF flag is set on success, reset otherwise. Implemented with a CMPXCHG. ++instruct storeLConditional(memory mem, mRegL oldval, mRegL newval, FlagsReg cr) ++%{ ++ match(Set cr (StoreLConditional mem (Binary oldval newval))); ++ ++ format %{ "cmpxchg $mem, $newval\t# If $oldval == $mem then store $newval into $mem" %} ++ ins_encode%{ ++ Register oldval = $oldval$$Register; ++ Register newval = $newval$$Register; ++ Register cr = $cr$$Register; ++ Address addr(as_Register($mem$$base), $mem$$disp); ++ ++ int index = $mem$$index; ++ int scale = $mem$$scale; ++ int disp = $mem$$disp; ++ ++ guarantee(Assembler::is_simm(disp, 12), ""); ++ ++ if (index != 0) { ++ __ stop("in storeIConditional: index != 0"); ++ } else { ++ if (cr != addr.base() && cr != oldval && cr != newval) { ++ __ cmpxchg(addr, oldval, newval, cr, false, true); ++ } else { ++ __ cmpxchg(addr, oldval, newval, AT, false, true); ++ __ move(cr, AT); ++ } ++ } ++ %} ++ ins_pipe(long_memory_op); ++%} ++ ++// Implement LoadPLocked. Must be ordered against changes of the memory location ++// by storePConditional. ++instruct loadPLocked(mRegP dst, memory mem) %{ ++ match(Set dst (LoadPLocked mem)); ++ ins_cost(MEMORY_REF_COST); ++ ++ format %{ "ll_d $dst, $mem #@loadPLocked\n\t" %} ++ size(12); ++ ins_encode %{ ++ relocInfo::relocType disp_reloc = $mem->disp_reloc(); ++ assert(disp_reloc == relocInfo::none, "cannot have disp"); ++ __ loadstore_enc($dst$$Register, $mem$$base, $mem$$index, $mem$$scale, $mem$$disp, MacroAssembler::LOAD_LINKED_LONG); ++ %} ++ ins_pipe( ialu_loadI ); ++%} ++ ++ ++instruct compareAndSwapI(mRegI res, mRegP mem_ptr, mRegI oldval, mRegI newval) %{ ++ match(Set res (CompareAndSwapI mem_ptr (Binary oldval newval))); ++ format %{ "CMPXCHG $newval, [$mem_ptr], $oldval @ compareAndSwapI" %} ++ ins_encode %{ ++ Register newval = $newval$$Register; ++ Register oldval = $oldval$$Register; ++ Register res = $res$$Register; ++ Address addr($mem_ptr$$Register, 0); ++ ++ if (res != addr.base() && res != oldval && res != newval) { ++ __ cmpxchg32(addr, oldval, newval, res, true, false, true); ++ } else { ++ __ cmpxchg32(addr, oldval, newval, AT, true, false, true); ++ __ move(res, AT); ++ } ++ %} ++ ins_pipe(long_memory_op); ++%} ++ ++instruct compareAndSwapL(mRegI res, mRegP mem_ptr, mRegL oldval, mRegL newval) %{ ++ predicate(VM_Version::supports_cx8()); ++ match(Set res (CompareAndSwapL mem_ptr (Binary oldval newval))); ++ format %{ "CMPXCHG $newval, [$mem_ptr], $oldval @ compareAndSwapL" %} ++ ins_encode %{ ++ Register newval = $newval$$Register; ++ Register oldval = $oldval$$Register; ++ Register res = $res$$Register; ++ Address addr($mem_ptr$$Register, 0); ++ ++ if (res != addr.base() && res != oldval && res != newval) { ++ __ cmpxchg(addr, oldval, newval, res, false, true); ++ } else { ++ __ cmpxchg(addr, oldval, newval, AT, false, true); ++ __ move(res, AT); ++ } ++ %} ++ ins_pipe(long_memory_op); ++%} ++ ++instruct compareAndSwapP(mRegI res, mRegP mem_ptr, mRegP oldval, mRegP newval) %{ ++ match(Set res (CompareAndSwapP mem_ptr (Binary oldval newval))); ++ format %{ "CMPXCHG $newval, [$mem_ptr], $oldval @ compareAndSwapP" %} ++ ins_encode %{ ++ Register newval = $newval$$Register; ++ Register oldval = $oldval$$Register; ++ Register res = $res$$Register; ++ Address addr($mem_ptr$$Register, 0); ++ ++ if (res != addr.base() && res != oldval && res != newval) { ++ __ cmpxchg(addr, oldval, newval, res, false, true); ++ } else { ++ __ cmpxchg(addr, oldval, newval, AT, false, true); ++ __ move(res, AT); ++ } ++ %} ++ ins_pipe(long_memory_op); ++%} ++ ++instruct compareAndSwapN(mRegI res, mRegP mem_ptr, mRegN oldval, mRegN newval) %{ ++ match(Set res (CompareAndSwapN mem_ptr (Binary oldval newval))); ++ format %{ "CMPXCHG $newval, [$mem_ptr], $oldval @ compareAndSwapN" %} ++ ins_encode %{ ++ Register newval = $newval$$Register; ++ Register oldval = $oldval$$Register; ++ Register res = $res$$Register; ++ Address addr($mem_ptr$$Register, 0); ++ ++ if (res != addr.base() && res != oldval && res != newval) { ++ __ cmpxchg32(addr, oldval, newval, res, false, false, true); ++ } else { ++ __ cmpxchg32(addr, oldval, newval, AT, false, false, true); ++ __ move(res, AT); ++ } ++ %} ++ ins_pipe(long_memory_op); ++%} ++ ++//----------Max and Min-------------------------------------------------------- ++ ++// Min Register with Register (generic version) ++instruct minI_Reg_Reg(mRegI dst, mRegI src) %{ ++ match(Set dst (MinI dst src)); ++ //effect(KILL flags); ++ ins_cost(80); ++ ++ format %{ "MIN $dst, $src @minI_Reg_Reg" %} ++ ins_encode %{ ++ Register dst = $dst$$Register; ++ Register src = $src$$Register; ++ ++ __ slt(AT, src, dst); ++ __ masknez(dst, dst, AT); ++ __ maskeqz(AT, src, AT); ++ __ OR(dst, dst, AT); ++ %} ++ ++ ins_pipe( pipe_slow ); ++%} ++ ++// Max Register with Register (generic version) ++instruct maxI_Reg_Reg(mRegI dst, mRegI src) %{ ++ match(Set dst (MaxI dst src)); ++ ins_cost(80); ++ ++ format %{ "MAX $dst, $src @maxI_Reg_Reg" %} ++ ++ ins_encode %{ ++ Register dst = $dst$$Register; ++ Register src = $src$$Register; ++ ++ __ slt(AT, dst, src); ++ __ masknez(dst, dst, AT); ++ __ maskeqz(AT, src, AT); ++ __ OR(dst, dst, AT); ++ %} ++ ++ ins_pipe( pipe_slow ); ++%} ++ ++instruct maxI_Reg_zero(mRegI dst, immI_0 zero) %{ ++ match(Set dst (MaxI dst zero)); ++ ins_cost(50); ++ ++ format %{ "MAX $dst, 0 @maxI_Reg_zero" %} ++ ++ ins_encode %{ ++ Register dst = $dst$$Register; ++ ++ __ slt(AT, dst, R0); ++ __ masknez(dst, dst, AT); ++ %} ++ ++ ins_pipe( pipe_slow ); ++%} ++ ++instruct zerox_long_reg_reg(mRegL dst, mRegL src, immL_MaxUI mask) ++%{ ++ match(Set dst (AndL src mask)); ++ ++ format %{ "movl $dst, $src\t# zero-extend long @ zerox_long_reg_reg" %} ++ ins_encode %{ ++ Register dst = $dst$$Register; ++ Register src = $src$$Register; ++ ++ __ bstrpick_d(dst, src, 31, 0); ++ %} ++ ins_pipe(ialu_regI_regI); ++%} ++ ++instruct combine_i2l(mRegL dst, mRegI src1, immL_MaxUI mask, mRegI src2, immI_32 shift32) ++%{ ++ match(Set dst (OrL (AndL (ConvI2L src1) mask) (LShiftL (ConvI2L src2) shift32))); ++ ++ format %{ "combine_i2l $dst, $src2(H), $src1(L) @ combine_i2l" %} ++ ins_encode %{ ++ Register dst = $dst$$Register; ++ Register src1 = $src1$$Register; ++ Register src2 = $src2$$Register; ++ ++ if (src1 == dst) { ++ __ bstrins_d(dst, src2, 63, 32); ++ } else if (src2 == dst) { ++ __ slli_d(dst, dst, 32); ++ __ bstrins_d(dst, src1, 31, 0); ++ } else { ++ __ bstrpick_d(dst, src1, 31, 0); ++ __ bstrins_d(dst, src2, 63, 32); ++ } ++ %} ++ ins_pipe(ialu_regI_regI); ++%} ++ ++// Zero-extend convert int to long ++instruct convI2L_reg_reg_zex(mRegL dst, mRegI src, immL_MaxUI mask) ++%{ ++ match(Set dst (AndL (ConvI2L src) mask)); ++ ++ format %{ "movl $dst, $src\t# i2l zero-extend @ convI2L_reg_reg_zex" %} ++ ins_encode %{ ++ Register dst = $dst$$Register; ++ Register src = $src$$Register; ++ ++ __ bstrpick_d(dst, src, 31, 0); ++ %} ++ ins_pipe(ialu_regI_regI); ++%} ++ ++instruct convL2I2L_reg_reg_zex(mRegL dst, mRegL src, immL_MaxUI mask) ++%{ ++ match(Set dst (AndL (ConvI2L (ConvL2I src)) mask)); ++ ++ format %{ "movl $dst, $src\t# i2l zero-extend @ convL2I2L_reg_reg_zex" %} ++ ins_encode %{ ++ Register dst = $dst$$Register; ++ Register src = $src$$Register; ++ ++ __ bstrpick_d(dst, src, 31, 0); ++ %} ++ ins_pipe(ialu_regI_regI); ++%} ++ ++// Match loading integer and casting it to unsigned int in long register. ++// LoadI + ConvI2L + AndL 0xffffffff. ++instruct loadUI2L_rmask(mRegL dst, memory mem, immL_MaxUI mask) %{ ++ match(Set dst (AndL (ConvI2L (LoadI mem)) mask)); ++ ++ format %{ "ld_wu $dst, $mem \t// zero-extend to long @ loadUI2L_rmask" %} ++ ins_encode %{ ++ relocInfo::relocType disp_reloc = $mem->disp_reloc(); ++ assert(disp_reloc == relocInfo::none, "cannot have disp"); ++ __ loadstore_enc($dst$$Register, $mem$$base, $mem$$index, $mem$$scale, $mem$$disp, MacroAssembler::LOAD_U_INT); ++ %} ++ ins_pipe(ialu_loadI); ++%} ++ ++instruct loadUI2L_lmask(mRegL dst, memory mem, immL_MaxUI mask) %{ ++ match(Set dst (AndL mask (ConvI2L (LoadI mem)))); ++ ++ format %{ "ld_wu $dst, $mem \t// zero-extend to long @ loadUI2L_lmask" %} ++ ins_encode %{ ++ relocInfo::relocType disp_reloc = $mem->disp_reloc(); ++ assert(disp_reloc == relocInfo::none, "cannot have disp"); ++ __ loadstore_enc($dst$$Register, $mem$$base, $mem$$index, $mem$$scale, $mem$$disp, MacroAssembler::LOAD_U_INT); ++ %} ++ ins_pipe(ialu_loadI); ++%} ++ ++ ++// ============================================================================ ++// Safepoint Instruction ++instruct safePoint_poll_reg(mRegP poll) %{ ++ match(SafePoint poll); ++ predicate(false); ++ effect(USE poll); ++ ++ ins_cost(125); ++ format %{ "Safepoint @ [$poll] : poll for GC @ safePoint_poll_reg" %} ++ ++ ins_encode %{ ++ Register poll_reg = $poll$$Register; ++ ++ __ block_comment("Safepoint:"); ++ __ relocate(relocInfo::poll_type); ++ __ ld_w(AT, poll_reg, 0); ++ %} ++ ++ ins_pipe( ialu_storeI ); ++%} ++ ++instruct safePoint_poll() %{ ++ match(SafePoint); ++ ++ ins_cost(105); ++ format %{ "poll for GC @ safePoint_poll" %} ++ ++ ins_encode %{ ++ __ block_comment("Safepoint:"); ++ __ li(T4, (long)os::get_polling_page()); ++ __ relocate(relocInfo::poll_type); ++ __ ld_w(AT, T4, 0); ++ %} ++ ++ ins_pipe( ialu_storeI ); ++%} ++ ++//----------Arithmetic Conversion Instructions--------------------------------- ++ ++instruct roundFloat_nop(regF dst) ++%{ ++ match(Set dst (RoundFloat dst)); ++ ++ ins_cost(0); ++ ins_encode(); ++ ins_pipe(empty); ++%} ++ ++instruct roundDouble_nop(regD dst) ++%{ ++ match(Set dst (RoundDouble dst)); ++ ++ ins_cost(0); ++ ins_encode(); ++ ins_pipe(empty); ++%} ++ ++//---------- Zeros Count Instructions ------------------------------------------ ++// CountLeadingZerosINode CountTrailingZerosINode ++instruct countLeadingZerosI(mRegI dst, mRegI src) %{ ++ match(Set dst (CountLeadingZerosI src)); ++ ++ format %{ "clz_w $dst, $src\t# count leading zeros (int)" %} ++ ins_encode %{ ++ __ clz_w($dst$$Register, $src$$Register); ++ %} ++ ins_pipe( ialu_regL_regL ); ++%} ++ ++instruct countLeadingZerosL(mRegI dst, mRegL src) %{ ++ match(Set dst (CountLeadingZerosL src)); ++ ++ format %{ "clz_d $dst, $src\t# count leading zeros (long)" %} ++ ins_encode %{ ++ __ clz_d($dst$$Register, $src$$Register); ++ %} ++ ins_pipe( ialu_regL_regL ); ++%} ++ ++instruct countTrailingZerosI(mRegI dst, mRegI src) %{ ++ match(Set dst (CountTrailingZerosI src)); ++ ++ format %{ "ctz_w $dst, $src\t# count trailing zeros (int)" %} ++ ins_encode %{ ++ __ ctz_w($dst$$Register, $src$$Register); ++ %} ++ ins_pipe( ialu_regL_regL ); ++%} ++ ++instruct countTrailingZerosL(mRegI dst, mRegL src) %{ ++ match(Set dst (CountTrailingZerosL src)); ++ ++ format %{ "ctz_d $dst, $src\t# count trailing zeros (long)" %} ++ ins_encode %{ ++ __ ctz_d($dst$$Register, $src$$Register); ++ %} ++ ins_pipe( ialu_regL_regL ); ++%} ++ ++// ====================VECTOR INSTRUCTIONS===================================== ++ ++// --------------------------------- Load ------------------------------------- ++ ++instruct loadV16(vecX dst, memory mem) %{ ++ predicate(n->as_LoadVector()->memory_size() == 16); ++ match(Set dst (LoadVector mem)); ++ format %{ "vload $dst, $mem\t# @loadV16" %} ++ ins_encode %{ ++ __ loadstore_enc($dst$$FloatRegister, $mem$$base, $mem$$index, $mem$$scale, $mem$$disp, MacroAssembler::LOAD_VECTORX); ++ %} ++ ins_pipe( pipe_slow ); ++%} ++ ++instruct loadV32(vecY dst, memory mem) %{ ++ predicate(n->as_LoadVector()->memory_size() == 32); ++ match(Set dst (LoadVector mem)); ++ format %{ "xvload $dst, $mem\t# @loadV32" %} ++ ins_encode %{ ++ __ loadstore_enc($dst$$FloatRegister, $mem$$base, $mem$$index, $mem$$scale, $mem$$disp, MacroAssembler::LOAD_VECTORY); ++ %} ++ ins_pipe( pipe_slow ); ++%} ++ ++// --------------------------------- Store ------------------------------------ ++ ++instruct storeV16(memory mem, vecX src) %{ ++ predicate(n->as_StoreVector()->memory_size() == 16); ++ match(Set mem (StoreVector mem src)); ++ format %{ "vstore $src, $mem\t# @storeV16" %} ++ ins_encode %{ ++ __ loadstore_enc($src$$FloatRegister, $mem$$base, $mem$$index, $mem$$scale, $mem$$disp, MacroAssembler::STORE_VECTORX); ++ %} ++ ins_pipe( pipe_slow ); ++%} ++ ++instruct storeV32(memory mem, vecY src) %{ ++ predicate(n->as_StoreVector()->memory_size() == 32); ++ match(Set mem (StoreVector mem src)); ++ format %{ "xvstore $src, $mem\t# @storeV32" %} ++ ins_encode %{ ++ __ loadstore_enc($src$$FloatRegister, $mem$$base, $mem$$index, $mem$$scale, $mem$$disp, MacroAssembler::STORE_VECTORY); ++ %} ++ ins_pipe( pipe_slow ); ++%} ++ ++// ------------------------------- Replicate ---------------------------------- ++ ++instruct repl16B(vecX dst, mRegI src) %{ ++ predicate(n->as_Vector()->length() == 16); ++ match(Set dst (ReplicateB src)); ++ format %{ "vreplgr2vr.b $dst, $src\t# @repl16B" %} ++ ins_encode %{ ++ __ vreplgr2vr_b($dst$$FloatRegister, $src$$Register); ++ %} ++ ins_pipe( pipe_slow ); ++%} ++ ++instruct repl16B_imm(vecX dst, immI_M128_255 imm) %{ ++ predicate(n->as_Vector()->length() == 16); ++ match(Set dst (ReplicateB imm)); ++ format %{ "vldi $dst, $imm\t# @repl16B_imm" %} ++ ins_encode %{ ++ __ vldi($dst$$FloatRegister, ($imm$$constant & 0xff)); ++ %} ++ ins_pipe( pipe_slow ); ++%} ++ ++instruct repl8S(vecX dst, mRegI src) %{ ++ predicate(n->as_Vector()->length() == 8); ++ match(Set dst (ReplicateS src)); ++ format %{ "vreplgr2vr.h $dst, $src\t# @repl8S" %} ++ ins_encode %{ ++ __ vreplgr2vr_h($dst$$FloatRegister, $src$$Register); ++ %} ++ ins_pipe( pipe_slow ); ++%} ++ ++instruct repl8S_imm(vecX dst, immI10 imm) %{ ++ predicate(n->as_Vector()->length() == 8); ++ match(Set dst (ReplicateS imm)); ++ format %{ "vldi $dst, $imm\t# @repl8S_imm" %} ++ ins_encode %{ ++ __ vldi($dst$$FloatRegister, (0b001 << 10 ) | ($imm$$constant & 0x3ff)); ++ %} ++ ins_pipe( pipe_slow ); ++%} ++ ++instruct repl4I(vecX dst, mRegI src) %{ ++ predicate(n->as_Vector()->length() == 4); ++ match(Set dst (ReplicateI src)); ++ format %{ "vreplgr2vr.w $dst, $src\t# @repl4I" %} ++ ins_encode %{ ++ __ vreplgr2vr_w($dst$$FloatRegister, $src$$Register); ++ %} ++ ins_pipe( pipe_slow ); ++%} ++ ++instruct repl4I_imm(vecX dst, immI10 imm) %{ ++ predicate(n->as_Vector()->length() == 4); ++ match(Set dst (ReplicateI imm)); ++ format %{ "vldi $dst, $imm\t# @repl4I_imm" %} ++ ins_encode %{ ++ __ vldi($dst$$FloatRegister, (0b010 << 10 ) | ($imm$$constant & 0x3ff)); ++ %} ++ ins_pipe( pipe_slow ); ++%} ++ ++instruct repl2L(vecX dst, mRegL src) %{ ++ predicate(n->as_Vector()->length() == 2); ++ match(Set dst (ReplicateL src)); ++ format %{ "vreplgr2vr.d $dst, $src\t# @repl2L" %} ++ ins_encode %{ ++ __ vreplgr2vr_d($dst$$FloatRegister, $src$$Register); ++ %} ++ ins_pipe( pipe_slow ); ++%} ++ ++instruct repl2L_imm(vecX dst, immL10 imm) %{ ++ predicate(n->as_Vector()->length() == 2); ++ match(Set dst (ReplicateL imm)); ++ format %{ "vldi $dst, $imm\t# @repl2L_imm" %} ++ ins_encode %{ ++ __ vldi($dst$$FloatRegister, (0b011 << 10 ) | ($imm$$constant & 0x3ff)); ++ %} ++ ins_pipe( pipe_slow ); ++%} ++ ++instruct repl4F(vecX dst, regF src) %{ ++ predicate(n->as_Vector()->length() == 4); ++ match(Set dst (ReplicateF src)); ++ format %{ "vreplvei.w $dst, $src, 0\t# @repl4F" %} ++ ins_encode %{ ++ __ vreplvei_w($dst$$FloatRegister, $src$$FloatRegister, 0); ++ %} ++ ins_pipe( pipe_slow ); ++%} ++ ++instruct repl2D(vecX dst, regD src) %{ ++ predicate(n->as_Vector()->length() == 2); ++ match(Set dst (ReplicateD src)); ++ format %{ "vreplvei.d $dst, $src, 0\t# @repl2D" %} ++ ins_encode %{ ++ __ vreplvei_d($dst$$FloatRegister, $src$$FloatRegister, 0); ++ %} ++ ins_pipe( pipe_slow ); ++%} ++ ++instruct repl32B(vecY dst, mRegI src) %{ ++ predicate(n->as_Vector()->length() == 32); ++ match(Set dst (ReplicateB src)); ++ format %{ "xvreplgr2vr.b $dst, $src\t# @repl32B" %} ++ ins_encode %{ ++ __ xvreplgr2vr_b($dst$$FloatRegister, $src$$Register); ++ %} ++ ins_pipe( pipe_slow ); ++%} ++ ++instruct repl32B_imm(vecY dst, immI_M128_255 imm) %{ ++ predicate(n->as_Vector()->length() == 32); ++ match(Set dst (ReplicateB imm)); ++ format %{ "xvldi $dst, $imm\t# @repl32B_imm" %} ++ ins_encode %{ ++ __ xvldi($dst$$FloatRegister, ($imm$$constant & 0xff)); ++ %} ++ ins_pipe( pipe_slow ); ++%} ++ ++instruct repl16S(vecY dst, mRegI src) %{ ++ predicate(n->as_Vector()->length() == 16); ++ match(Set dst (ReplicateS src)); ++ format %{ "xvreplgr2vr.h $dst, $src\t# @repl16S" %} ++ ins_encode %{ ++ __ xvreplgr2vr_h($dst$$FloatRegister, $src$$Register); ++ %} ++ ins_pipe( pipe_slow ); ++%} ++ ++instruct repl16S_imm(vecY dst, immI10 imm) %{ ++ predicate(n->as_Vector()->length() == 16); ++ match(Set dst (ReplicateS imm)); ++ format %{ "xvldi $dst, $imm\t# @repl16S_imm" %} ++ ins_encode %{ ++ __ xvldi($dst$$FloatRegister, (0b001 << 10 ) | ($imm$$constant & 0x3ff)); ++ %} ++ ins_pipe( pipe_slow ); ++%} ++ ++instruct repl8I(vecY dst, mRegI src) %{ ++ predicate(n->as_Vector()->length() == 8); ++ match(Set dst (ReplicateI src)); ++ format %{ "xvreplgr2vr.w $dst, $src\t# @repl8I" %} ++ ins_encode %{ ++ __ xvreplgr2vr_w($dst$$FloatRegister, $src$$Register); ++ %} ++ ins_pipe( pipe_slow ); ++%} ++ ++instruct repl8I_imm(vecY dst, immI10 imm) %{ ++ predicate(n->as_Vector()->length() == 8); ++ match(Set dst (ReplicateI imm)); ++ format %{ "xvldi $dst, $imm\t# @repl8I_imm" %} ++ ins_encode %{ ++ __ xvldi($dst$$FloatRegister, (0b010 << 10 ) | ($imm$$constant & 0x3ff)); ++ %} ++ ins_pipe( pipe_slow ); ++%} ++ ++instruct repl4L(vecY dst, mRegL src) %{ ++ predicate(n->as_Vector()->length() == 4); ++ match(Set dst (ReplicateL src)); ++ format %{ "xvreplgr2vr.d $dst, $src\t# @repl4L" %} ++ ins_encode %{ ++ __ xvreplgr2vr_d($dst$$FloatRegister, $src$$Register); ++ %} ++ ins_pipe( pipe_slow ); ++%} ++ ++instruct repl4L_imm(vecY dst, immL10 imm) %{ ++ predicate(n->as_Vector()->length() == 4); ++ match(Set dst (ReplicateL imm)); ++ format %{ "xvldi $dst, $imm\t# @repl4L_imm" %} ++ ins_encode %{ ++ __ xvldi($dst$$FloatRegister, (0b011 << 10 ) | ($imm$$constant & 0x3ff)); ++ %} ++ ins_pipe( pipe_slow ); ++%} ++ ++instruct repl8F(vecY dst, regF src) %{ ++ predicate(n->as_Vector()->length() == 8); ++ match(Set dst (ReplicateF src)); ++ format %{ "xvreplve0.w $dst, $src\t# @repl8F" %} ++ ins_encode %{ ++ __ xvreplve0_w($dst$$FloatRegister, $src$$FloatRegister); ++ %} ++ ins_pipe( pipe_slow ); ++%} ++ ++instruct repl4D(vecY dst, regD src) %{ ++ predicate(n->as_Vector()->length() == 4); ++ match(Set dst (ReplicateD src)); ++ format %{ "xvreplve0.d $dst, $src\t# @repl4D" %} ++ ins_encode %{ ++ __ xvreplve0_d($dst$$FloatRegister, $src$$FloatRegister); ++ %} ++ ins_pipe( pipe_slow ); ++%} ++ ++// --------------------------------- ADD -------------------------------------- ++ ++instruct add16B(vecX dst, vecX src1, vecX src2) %{ ++ predicate(n->as_Vector()->length() == 16); ++ match(Set dst (AddVB src1 src2)); ++ format %{ "vadd.b $dst, $src1, $src2\t# @add16B" %} ++ ins_encode %{ ++ __ vadd_b($dst$$FloatRegister, $src1$$FloatRegister, $src2$$FloatRegister); ++ %} ++ ins_pipe( pipe_slow ); ++%} ++ ++instruct add16B_imm(vecX dst, vecX src, immIU5 imm) %{ ++ predicate(n->as_Vector()->length() == 16); ++ match(Set dst (AddVB src (ReplicateB imm))); ++ format %{ "vaddi.bu $dst, $src, $imm\t# @add16B_imm" %} ++ ins_encode %{ ++ __ vaddi_bu($dst$$FloatRegister, $src$$FloatRegister, $imm$$constant); ++ %} ++ ins_pipe( pipe_slow ); ++%} ++ ++instruct add8S(vecX dst, vecX src1, vecX src2) %{ ++ predicate(n->as_Vector()->length() == 8); ++ match(Set dst (AddVS src1 src2)); ++ format %{ "vadd.h $dst, $src1, $src2\t# @add8S" %} ++ ins_encode %{ ++ __ vadd_h($dst$$FloatRegister, $src1$$FloatRegister, $src2$$FloatRegister); ++ %} ++ ins_pipe( pipe_slow ); ++%} ++ ++instruct add8S_imm(vecX dst, vecX src, immIU5 imm) %{ ++ predicate(n->as_Vector()->length() == 8); ++ match(Set dst (AddVS src (ReplicateS imm))); ++ format %{ "vaddi.hu $dst, $src, $imm\t# @add8S_imm" %} ++ ins_encode %{ ++ __ vaddi_hu($dst$$FloatRegister, $src$$FloatRegister, $imm$$constant); ++ %} ++ ins_pipe( pipe_slow ); ++%} ++ ++instruct add4I(vecX dst, vecX src1, vecX src2) %{ ++ predicate(n->as_Vector()->length() == 4); ++ match(Set dst (AddVI src1 src2)); ++ format %{ "vadd.w $dst, $src1, src2\t# @add4I" %} ++ ins_encode %{ ++ __ vadd_w($dst$$FloatRegister, $src1$$FloatRegister, $src2$$FloatRegister); ++ %} ++ ins_pipe( pipe_slow ); ++%} ++ ++instruct add4I_imm(vecX dst, vecX src, immIU5 imm) %{ ++ predicate(n->as_Vector()->length() == 4); ++ match(Set dst (AddVI src (ReplicateI imm))); ++ format %{ "vaddi.wu $dst, $src, $imm\t# @add4I_imm" %} ++ ins_encode %{ ++ __ vaddi_wu($dst$$FloatRegister, $src$$FloatRegister, $imm$$constant); ++ %} ++ ins_pipe( pipe_slow ); ++%} ++ ++instruct add2L(vecX dst, vecX src1, vecX src2) %{ ++ predicate(n->as_Vector()->length() == 2); ++ match(Set dst (AddVL src1 src2)); ++ format %{ "vadd.d $dst, $src1, $src2\t# @add2L" %} ++ ins_encode %{ ++ __ vadd_d($dst$$FloatRegister, $src1$$FloatRegister, $src2$$FloatRegister); ++ %} ++ ins_pipe( pipe_slow ); ++%} ++ ++instruct add2L_imm(vecX dst, vecX src, immLU5 imm) %{ ++ predicate(n->as_Vector()->length() == 2); ++ match(Set dst (AddVL src (ReplicateL imm))); ++ format %{ "vaddi.du $dst, $src, $imm\t# @add2L_imm" %} ++ ins_encode %{ ++ __ vaddi_du($dst$$FloatRegister, $src$$FloatRegister, $imm$$constant); ++ %} ++ ins_pipe( pipe_slow ); ++%} ++ ++instruct add4F(vecX dst, vecX src1, vecX src2) %{ ++ predicate(n->as_Vector()->length() == 4); ++ match(Set dst (AddVF src1 src2)); ++ format %{ "vfadd.s $dst, $src1, $src2\t# @add4F" %} ++ ins_encode %{ ++ __ vfadd_s($dst$$FloatRegister, $src1$$FloatRegister, $src2$$FloatRegister); ++ %} ++ ins_pipe( pipe_slow ); ++%} ++ ++instruct add2D(vecX dst, vecX src1, vecX src2) %{ ++ predicate(n->as_Vector()->length() == 2); ++ match(Set dst (AddVD src1 src2)); ++ format %{ "vfadd.d $dst, $src1, $src2\t# @add2D" %} ++ ins_encode %{ ++ __ vfadd_d($dst$$FloatRegister, $src1$$FloatRegister, $src2$$FloatRegister); ++ %} ++ ins_pipe( pipe_slow ); ++%} ++ ++instruct add32B(vecY dst, vecY src1, vecY src2) %{ ++ predicate(n->as_Vector()->length() == 32); ++ match(Set dst (AddVB src1 src2)); ++ format %{ "xvadd.b $dst, $src1, $src2\t# @add32B" %} ++ ins_encode %{ ++ __ xvadd_b($dst$$FloatRegister, $src1$$FloatRegister, $src2$$FloatRegister); ++ %} ++ ins_pipe( pipe_slow ); ++%} ++ ++instruct add32B_imm(vecY dst, vecY src, immIU5 imm) %{ ++ predicate(n->as_Vector()->length() == 32); ++ match(Set dst (AddVB src (ReplicateB imm))); ++ format %{ "xvaddi.bu $dst, $src, $imm\t# @add32B_imm" %} ++ ins_encode %{ ++ __ xvaddi_bu($dst$$FloatRegister, $src$$FloatRegister, $imm$$constant); ++ %} ++ ins_pipe( pipe_slow ); ++%} ++ ++instruct add16S(vecY dst, vecY src1, vecY src2) %{ ++ predicate(n->as_Vector()->length() == 16); ++ match(Set dst (AddVS src1 src2)); ++ format %{ "xvadd.h $dst, $src1, $src2\t# @add16S" %} ++ ins_encode %{ ++ __ xvadd_h($dst$$FloatRegister, $src1$$FloatRegister, $src2$$FloatRegister); ++ %} ++ ins_pipe( pipe_slow ); ++%} ++ ++instruct add16S_imm(vecY dst, vecY src, immIU5 imm) %{ ++ predicate(n->as_Vector()->length() == 16); ++ match(Set dst (AddVS src (ReplicateS imm))); ++ format %{ "xvaddi.hu $dst, $src, $imm\t# @add16S_imm" %} ++ ins_encode %{ ++ __ xvaddi_hu($dst$$FloatRegister, $src$$FloatRegister, $imm$$constant); ++ %} ++ ins_pipe( pipe_slow ); ++%} ++ ++instruct add8I(vecY dst, vecY src1, vecY src2) %{ ++ predicate(n->as_Vector()->length() == 8); ++ match(Set dst (AddVI src1 src2)); ++ format %{ "xvadd.wu $dst, $src1, $src2\t# @add8I" %} ++ ins_encode %{ ++ __ xvadd_w($dst$$FloatRegister, $src1$$FloatRegister, $src2$$FloatRegister); ++ %} ++ ins_pipe( pipe_slow ); ++%} ++ ++instruct add8I_imm(vecY dst, vecY src, immIU5 imm) %{ ++ predicate(n->as_Vector()->length() == 8); ++ match(Set dst (AddVI src (ReplicateI imm))); ++ format %{ "xvaddi.wu $dst, $src, $imm\t# @add8I_imm" %} ++ ins_encode %{ ++ __ xvaddi_wu($dst$$FloatRegister, $src$$FloatRegister, $imm$$constant); ++ %} ++ ins_pipe( pipe_slow ); ++%} ++ ++instruct add4L(vecY dst, vecY src1, vecY src2) %{ ++ predicate(n->as_Vector()->length() == 4); ++ match(Set dst (AddVL src1 src2)); ++ format %{ "xvadd.d $dst, $src1, $src2\t# @add4L" %} ++ ins_encode %{ ++ __ xvadd_d($dst$$FloatRegister, $src1$$FloatRegister, $src2$$FloatRegister); ++ %} ++ ins_pipe( pipe_slow ); ++%} ++ ++instruct add4L_imm(vecY dst, vecY src, immLU5 imm) %{ ++ predicate(n->as_Vector()->length() == 4); ++ match(Set dst (AddVL src (ReplicateL imm))); ++ format %{ "xvaddi.du $dst, $src, $imm\t# @add4L_imm" %} ++ ins_encode %{ ++ __ xvaddi_du($dst$$FloatRegister, $src$$FloatRegister, $imm$$constant); ++ %} ++ ins_pipe( pipe_slow ); ++%} ++ ++instruct add8F(vecY dst, vecY src1, vecY src2) %{ ++ predicate(n->as_Vector()->length() == 8); ++ match(Set dst (AddVF src1 src2)); ++ format %{ "xvfadd.s $dst, $src1, $src2\t# @add8F" %} ++ ins_encode %{ ++ __ xvfadd_s($dst$$FloatRegister, $src1$$FloatRegister, $src2$$FloatRegister); ++ %} ++ ins_pipe( pipe_slow ); ++%} ++ ++instruct add4D(vecY dst, vecY src1, vecY src2) %{ ++ predicate(n->as_Vector()->length() == 4); ++ match(Set dst (AddVD src1 src2)); ++ format %{ "xvfadd.d $dst, $src1, $src2\t# @add4D" %} ++ ins_encode %{ ++ __ xvfadd_d($dst$$FloatRegister, $src1$$FloatRegister, $src2$$FloatRegister); ++ %} ++ ins_pipe( pipe_slow ); ++%} ++ ++// --------------------------------- SUB -------------------------------------- ++ ++instruct sub16B(vecX dst, vecX src1, vecX src2) %{ ++ predicate(n->as_Vector()->length() == 16); ++ match(Set dst (SubVB src1 src2)); ++ format %{ "vsub.b $dst, $src1, $src2\t# @sub16B" %} ++ ins_encode %{ ++ __ vsub_b($dst$$FloatRegister, $src1$$FloatRegister, $src2$$FloatRegister); ++ %} ++ ins_pipe( pipe_slow ); ++%} ++ ++instruct sub16B_imm(vecX dst, vecX src, immIU5 imm) %{ ++ predicate(n->as_Vector()->length() == 16); ++ match(Set dst (SubVB src (ReplicateB imm))); ++ format %{ "vsubi.bu $dst, $src, $imm\t# @sub16B_imm" %} ++ ins_encode %{ ++ __ vsubi_bu($dst$$FloatRegister, $src$$FloatRegister, $imm$$constant); ++ %} ++ ins_pipe( pipe_slow ); ++%} ++ ++instruct sub8S(vecX dst, vecX src1, vecX src2) %{ ++ predicate(n->as_Vector()->length() == 8); ++ match(Set dst (SubVS src1 src2)); ++ format %{ "vsub.h $dst, $src1, $src2\t# @sub8S" %} ++ ins_encode %{ ++ __ vsub_h($dst$$FloatRegister, $src1$$FloatRegister, $src2$$FloatRegister); ++ %} ++ ins_pipe( pipe_slow ); ++%} ++ ++instruct sub8S_imm(vecX dst, vecX src, immIU5 imm) %{ ++ predicate(n->as_Vector()->length() == 8); ++ match(Set dst (SubVS src (ReplicateS imm))); ++ format %{ "vsubi.hu $dst, $src, $imm\t# @sub8S_imm" %} ++ ins_encode %{ ++ __ vsubi_hu($dst$$FloatRegister, $src$$FloatRegister, $imm$$constant); ++ %} ++ ins_pipe( pipe_slow ); ++%} ++ ++instruct sub4I(vecX dst, vecX src1, vecX src2) %{ ++ predicate(n->as_Vector()->length() == 4); ++ match(Set dst (SubVI src1 src2)); ++ format %{ "vsub.w $dst, $src1, src2\t# @sub4I" %} ++ ins_encode %{ ++ __ vsub_w($dst$$FloatRegister, $src1$$FloatRegister, $src2$$FloatRegister); ++ %} ++ ins_pipe( pipe_slow ); ++%} ++ ++instruct sub4I_imm(vecX dst, vecX src, immIU5 imm) %{ ++ predicate(n->as_Vector()->length() == 4); ++ match(Set dst (SubVI src (ReplicateI imm))); ++ format %{ "vsubi.wu $dst, $src, $imm\t# @sub4I_imm" %} ++ ins_encode %{ ++ __ vsubi_wu($dst$$FloatRegister, $src$$FloatRegister, $imm$$constant); ++ %} ++ ins_pipe( pipe_slow ); ++%} ++ ++instruct sub2L(vecX dst, vecX src1, vecX src2) %{ ++ predicate(n->as_Vector()->length() == 2); ++ match(Set dst (SubVL src1 src2)); ++ format %{ "vsub.d $dst, $src1, $src2\t# @sub2L" %} ++ ins_encode %{ ++ __ vsub_d($dst$$FloatRegister, $src1$$FloatRegister, $src2$$FloatRegister); ++ %} ++ ins_pipe( pipe_slow ); ++%} ++ ++instruct sub2L_imm(vecX dst, vecX src, immLU5 imm) %{ ++ predicate(n->as_Vector()->length() == 2); ++ match(Set dst (SubVL src (ReplicateL imm))); ++ format %{ "vsubi.du $dst, $src, $imm\t# @sub2L_imm" %} ++ ins_encode %{ ++ __ vsubi_du($dst$$FloatRegister, $src$$FloatRegister, $imm$$constant); ++ %} ++ ins_pipe( pipe_slow ); ++%} ++ ++instruct sub4F(vecX dst, vecX src1, vecX src2) %{ ++ predicate(n->as_Vector()->length() == 4); ++ match(Set dst (SubVF src1 src2)); ++ format %{ "vfsub.s $dst, $src1, $src2\t# @sub4F" %} ++ ins_encode %{ ++ __ vfsub_s($dst$$FloatRegister, $src1$$FloatRegister, $src2$$FloatRegister); ++ %} ++ ins_pipe( pipe_slow ); ++%} ++ ++instruct sub2D(vecX dst, vecX src1, vecX src2) %{ ++ predicate(n->as_Vector()->length() == 2); ++ match(Set dst (SubVD src1 src2)); ++ format %{ "vfsub.d $dst, $src1, $src2\t# @sub2D" %} ++ ins_encode %{ ++ __ vfsub_d($dst$$FloatRegister, $src1$$FloatRegister, $src2$$FloatRegister); ++ %} ++ ins_pipe( pipe_slow ); ++%} ++ ++instruct sub32B(vecY dst, vecY src1, vecY src2) %{ ++ predicate(n->as_Vector()->length() == 32); ++ match(Set dst (SubVB src1 src2)); ++ format %{ "xvsub.b $dst, $src1, $src2\t# @sub32B" %} ++ ins_encode %{ ++ __ xvsub_b($dst$$FloatRegister, $src1$$FloatRegister, $src2$$FloatRegister); ++ %} ++ ins_pipe( pipe_slow ); ++%} ++ ++instruct sub32B_imm(vecY dst, vecY src, immIU5 imm) %{ ++ predicate(n->as_Vector()->length() == 32); ++ match(Set dst (SubVB src (ReplicateB imm))); ++ format %{ "xvsubi.bu $dst, $src, $imm\t# @sub32B_imm" %} ++ ins_encode %{ ++ __ xvsubi_bu($dst$$FloatRegister, $src$$FloatRegister, $imm$$constant); ++ %} ++ ins_pipe( pipe_slow ); ++%} ++ ++instruct sub16S(vecY dst, vecY src1, vecY src2) %{ ++ predicate(n->as_Vector()->length() == 16); ++ match(Set dst (SubVS src1 src2)); ++ format %{ "xvsub.h $dst, $src1, $src2\t# @sub16S" %} ++ ins_encode %{ ++ __ xvsub_h($dst$$FloatRegister, $src1$$FloatRegister, $src2$$FloatRegister); ++ %} ++ ins_pipe( pipe_slow ); ++%} ++ ++instruct sub16S_imm(vecY dst, vecY src, immIU5 imm) %{ ++ predicate(n->as_Vector()->length() == 16); ++ match(Set dst (SubVS src (ReplicateS imm))); ++ format %{ "xvsubi.hu $dst, $src, $imm\t# @sub16S_imm" %} ++ ins_encode %{ ++ __ xvsubi_hu($dst$$FloatRegister, $src$$FloatRegister, $imm$$constant); ++ %} ++ ins_pipe( pipe_slow ); ++%} ++ ++instruct sub8I(vecY dst, vecY src1, vecY src2) %{ ++ predicate(n->as_Vector()->length() == 8); ++ match(Set dst (SubVI src1 src2)); ++ format %{ "xvsub.w $dst, $src1, $src2\t# @sub8I" %} ++ ins_encode %{ ++ __ xvsub_w($dst$$FloatRegister, $src1$$FloatRegister, $src2$$FloatRegister); ++ %} ++ ins_pipe( pipe_slow ); ++%} ++ ++instruct sub8I_imm(vecY dst, vecY src, immIU5 imm) %{ ++ predicate(n->as_Vector()->length() == 8); ++ match(Set dst (SubVI src (ReplicateI imm))); ++ format %{ "xvsubi.wu $dst, $src, $imm\t# @sub8I_imm" %} ++ ins_encode %{ ++ __ xvsubi_wu($dst$$FloatRegister, $src$$FloatRegister, $imm$$constant); ++ %} ++ ins_pipe( pipe_slow ); ++%} ++ ++instruct sub4L(vecY dst, vecY src1, vecY src2) %{ ++ predicate(n->as_Vector()->length() == 4); ++ match(Set dst (SubVL src1 src2)); ++ format %{ "xvsub.d $dst, $src1, $src2\t# @sub4L" %} ++ ins_encode %{ ++ __ xvsub_d($dst$$FloatRegister, $src1$$FloatRegister, $src2$$FloatRegister); ++ %} ++ ins_pipe( pipe_slow ); ++%} ++ ++instruct sub4L_imm(vecY dst, vecY src, immLU5 imm) %{ ++ predicate(n->as_Vector()->length() == 4); ++ match(Set dst (SubVL src (ReplicateL imm))); ++ format %{ "xvsubi.du $dst, $src, $imm\t# @sub4L_imm" %} ++ ins_encode %{ ++ __ xvsubi_du($dst$$FloatRegister, $src$$FloatRegister, $imm$$constant); ++ %} ++ ins_pipe( pipe_slow ); ++%} ++ ++instruct sub8F(vecY dst, vecY src1, vecY src2) %{ ++ predicate(n->as_Vector()->length() == 8); ++ match(Set dst (SubVF src1 src2)); ++ format %{ "xvfsub.s $dst, $src1, $src2\t# @sub8F" %} ++ ins_encode %{ ++ __ xvfsub_s($dst$$FloatRegister, $src1$$FloatRegister, $src2$$FloatRegister); ++ %} ++ ins_pipe( pipe_slow ); ++%} ++ ++instruct sub4D(vecY dst, vecY src1, vecY src2) %{ ++ predicate(n->as_Vector()->length() == 4); ++ match(Set dst (SubVD src1 src2)); ++ format %{ "xvfsub.d $dst,$src1,$src2\t# @sub4D" %} ++ ins_encode %{ ++ __ xvfsub_d($dst$$FloatRegister, $src1$$FloatRegister, $src2$$FloatRegister); ++ %} ++ ins_pipe( pipe_slow ); ++%} ++ ++// --------------------------------- MUL -------------------------------------- ++instruct mul8S(vecX dst, vecX src1, vecX src2) %{ ++ predicate(n->as_Vector()->length() == 8); ++ match(Set dst (MulVS src1 src2)); ++ format %{ "vmul.h $dst, $src1, $src2\t# @mul8S" %} ++ ins_encode %{ ++ __ vmul_h($dst$$FloatRegister, $src1$$FloatRegister, $src2$$FloatRegister); ++ %} ++ ins_pipe( pipe_slow ); ++%} ++ ++instruct mul4I(vecX dst, vecX src1, vecX src2) %{ ++ predicate(n->as_Vector()->length() == 4); ++ match(Set dst (MulVI src1 src2)); ++ format %{ "vmul.w $dst, $src1, $src2\t# @mul4I" %} ++ ins_encode %{ ++ __ vmul_w($dst$$FloatRegister, $src1$$FloatRegister, $src2$$FloatRegister); ++ %} ++ ins_pipe( pipe_slow ); ++%} ++ ++instruct mul4F(vecX dst, vecX src1, vecX src2) %{ ++ predicate(n->as_Vector()->length() == 4); ++ match(Set dst (MulVF src1 src2)); ++ format %{ "vfmul.s $dst, $src1, $src2\t# @mul4F" %} ++ ins_encode %{ ++ __ vfmul_s($dst$$FloatRegister, $src1$$FloatRegister, $src2$$FloatRegister); ++ %} ++ ins_pipe( pipe_slow ); ++%} ++ ++instruct mul2D(vecX dst, vecX src1, vecX src2) %{ ++ predicate(n->as_Vector()->length() == 2); ++ match(Set dst (MulVD src1 src2)); ++ format %{ "vfmul.d $dst, $src1, $src2\t# @mul2D" %} ++ ins_encode %{ ++ __ vfmul_d($dst$$FloatRegister, $src1$$FloatRegister, $src2$$FloatRegister); ++ %} ++ ins_pipe( pipe_slow ); ++%} ++ ++instruct mul16S(vecY dst, vecY src1, vecY src2) %{ ++ predicate(n->as_Vector()->length() == 16); ++ match(Set dst (MulVS src1 src2)); ++ format %{ "xvmul.h $dst, $src1, $src2\t# @mul16S" %} ++ ins_encode %{ ++ __ xvmul_h($dst$$FloatRegister, $src1$$FloatRegister, $src2$$FloatRegister); ++ %} ++ ins_pipe( pipe_slow ); ++%} ++ ++instruct mul8I(vecY dst, vecY src1, vecY src2) %{ ++ predicate(n->as_Vector()->length() == 8); ++ match(Set dst (MulVI src1 src2)); ++ format %{ "xvmul.w $dst, $src1, $src2\t# @mul8I" %} ++ ins_encode %{ ++ __ xvmul_w($dst$$FloatRegister, $src1$$FloatRegister, $src2$$FloatRegister); ++ %} ++ ins_pipe( pipe_slow ); ++%} ++ ++instruct mul8F(vecY dst, vecY src1, vecY src2) %{ ++ predicate(n->as_Vector()->length() == 8); ++ match(Set dst (MulVF src1 src2)); ++ format %{ "xvfmul.s $dst, $src1, $src2\t# @mul8F" %} ++ ins_encode %{ ++ __ xvfmul_s($dst$$FloatRegister, $src1$$FloatRegister, $src2$$FloatRegister); ++ %} ++ ins_pipe( pipe_slow ); ++%} ++ ++instruct mul4D(vecY dst, vecY src1, vecY src2) %{ ++ predicate(n->as_Vector()->length() == 4); ++ match(Set dst (MulVD src1 src2)); ++ format %{ "xvfmul.d $dst, $src1, $src2\t# @mul4D" %} ++ ins_encode %{ ++ __ xvfmul_d($dst$$FloatRegister, $src1$$FloatRegister, $src2$$FloatRegister); ++ %} ++ ins_pipe( pipe_slow ); ++%} ++ ++// --------------------------------- DIV -------------------------------------- ++instruct div4F(vecX dst, vecX src1, vecX src2) %{ ++ predicate(n->as_Vector()->length() == 4); ++ match(Set dst (DivVF src1 src2)); ++ format %{ "vfdiv.s $dst, $src1, $src2\t# @div4F" %} ++ ins_encode %{ ++ __ vfdiv_s($dst$$FloatRegister, $src1$$FloatRegister, $src2$$FloatRegister); ++ %} ++ ins_pipe( pipe_slow ); ++%} ++ ++instruct div2D(vecX dst, vecX src1, vecX src2) %{ ++ predicate(n->as_Vector()->length() == 2); ++ match(Set dst (DivVD src1 src2)); ++ format %{ "vfdiv.d $dst, $src1, $src2\t# @div2D" %} ++ ins_encode %{ ++ __ vfdiv_d($dst$$FloatRegister, $src1$$FloatRegister, $src2$$FloatRegister); ++ %} ++ ins_pipe( pipe_slow ); ++%} ++ ++instruct div8F(vecY dst, vecY src1, vecY src2) %{ ++ predicate(n->as_Vector()->length() == 8); ++ match(Set dst (DivVF src1 src2)); ++ format %{ "xvfdiv.s $dst, $src1, $src2\t# @div8F" %} ++ ins_encode %{ ++ __ xvfdiv_s($dst$$FloatRegister, $src1$$FloatRegister, $src2$$FloatRegister); ++ %} ++ ins_pipe( pipe_slow ); ++%} ++ ++instruct div4D(vecY dst, vecY src1, vecY src2) %{ ++ predicate(n->as_Vector()->length() == 4); ++ match(Set dst (DivVD src1 src2)); ++ format %{ "xvfdiv.d $dst, $src1, $src2\t# @div4D" %} ++ ins_encode %{ ++ __ xvfdiv_d($dst$$FloatRegister, $src1$$FloatRegister, $src2$$FloatRegister); ++ %} ++ ins_pipe( pipe_slow ); ++%} ++ ++// ------------------------------ Shift --------------------------------------- ++ ++instruct shiftcntX(vecX dst, mRegI cnt) %{ ++ predicate(n->as_Vector()->length_in_bytes() == 16); ++ match(Set dst (LShiftCntV cnt)); ++ match(Set dst (RShiftCntV cnt)); ++ format %{ "vreplgr2vr.b $dst, $cnt\t# @shiftcntX" %} ++ ins_encode %{ ++ __ vreplgr2vr_b($dst$$FloatRegister, $cnt$$Register); ++ %} ++ ins_pipe( pipe_slow ); ++%} ++ ++instruct shiftcntY(vecY dst, mRegI cnt) %{ ++ predicate(n->as_Vector()->length_in_bytes() == 32); ++ match(Set dst (LShiftCntV cnt)); ++ match(Set dst (RShiftCntV cnt)); ++ format %{ "xvreplgr2vr.b $dst, $cnt\t# @shiftcntY" %} ++ ins_encode %{ ++ __ xvreplgr2vr_b($dst$$FloatRegister, $cnt$$Register); ++ %} ++ ins_pipe( pipe_slow ); ++%} ++ ++// ------------------------------ LeftShift ----------------------------------- ++ ++instruct sll16B(vecX dst, vecX src, vecX shift, vecX tmp) %{ ++ predicate(n->as_Vector()->length() == 16); ++ match(Set dst (LShiftVB src shift)); ++ effect(TEMP dst, TEMP tmp); ++ format %{ "vsll $dst, $src, $shift\t# TEMP($tmp) @sll16B" %} ++ ins_encode %{ ++ __ vsll_b($tmp$$FloatRegister, $src$$FloatRegister, $shift$$FloatRegister); ++ __ vslti_bu($dst$$FloatRegister, $shift$$FloatRegister, 0x8); ++ __ vand_v($dst$$FloatRegister, $dst$$FloatRegister, $tmp$$FloatRegister); ++ %} ++ ins_pipe( pipe_slow ); ++%} ++ ++instruct sll16B_imm(vecX dst, vecX src, immI shift) %{ ++ predicate(n->as_Vector()->length() == 16); ++ match(Set dst (LShiftVB src shift)); ++ format %{ "vslli.b $dst, $src, $shift\t# @sll16B_imm" %} ++ ins_encode %{ ++ if ($shift$$constant >= 8) { ++ __ vxor_v($dst$$FloatRegister, $dst$$FloatRegister, $dst$$FloatRegister); ++ } else { ++ __ vslli_b($dst$$FloatRegister, $src$$FloatRegister, $shift$$constant); ++ } ++ %} ++ ins_pipe( pipe_slow ); ++%} ++ ++instruct sll8S(vecX dst, vecX src, vecX shift, vecX tmp) %{ ++ predicate(n->as_Vector()->length() == 8); ++ match(Set dst (LShiftVS src shift)); ++ effect(TEMP dst, TEMP tmp); ++ format %{ "vsll $dst, $src, $shift\t# TEMP($tmp) @sll8S" %} ++ ins_encode %{ ++ __ vsll_h($tmp$$FloatRegister, $src$$FloatRegister, $shift$$FloatRegister); ++ __ vslti_bu($dst$$FloatRegister, $shift$$FloatRegister, 0x10); ++ __ vand_v($dst$$FloatRegister, $dst$$FloatRegister, $tmp$$FloatRegister); ++ %} ++ ins_pipe( pipe_slow ); ++%} ++ ++instruct sll8S_imm(vecX dst, vecX src, immI shift) %{ ++ predicate(n->as_Vector()->length() == 8); ++ match(Set dst (LShiftVS src shift)); ++ format %{ "vslli.h $dst, $src, $shift\t# @sll8S_imm" %} ++ ins_encode %{ ++ if ($shift$$constant >= 16) { ++ __ vxor_v($dst$$FloatRegister, $dst$$FloatRegister, $dst$$FloatRegister); ++ } else { ++ __ vslli_h($dst$$FloatRegister, $src$$FloatRegister, $shift$$constant); ++ } ++ %} ++ ins_pipe( pipe_slow ); ++%} ++ ++instruct sll4I(vecX dst, vecX src, vecX shift) %{ ++ predicate(n->as_Vector()->length() == 4); ++ match(Set dst (LShiftVI src shift)); ++ format %{ "vsll.w $dst, $src, $shift\t# @sll4I" %} ++ ins_encode %{ ++ __ vsll_w($dst$$FloatRegister, $src$$FloatRegister, $shift$$FloatRegister); ++ %} ++ ins_pipe( pipe_slow ); ++%} ++ ++instruct sll4I_imm(vecX dst, vecX src, immI shift) %{ ++ predicate(n->as_Vector()->length() == 4); ++ match(Set dst (LShiftVI src shift)); ++ format %{ "vslli.w $dst, $src, $shift\t# @sll4I_imm" %} ++ ins_encode %{ ++ __ vslli_w($dst$$FloatRegister, $src$$FloatRegister, $shift$$constant); ++ %} ++ ins_pipe( pipe_slow ); ++%} ++ ++instruct sll2L(vecX dst, vecX src, vecX shift) %{ ++ predicate(n->as_Vector()->length() == 2); ++ match(Set dst (LShiftVL src shift)); ++ format %{ "vsll.d $dst, $src, $shift\t# @sll2L" %} ++ ins_encode %{ ++ __ vsll_d($dst$$FloatRegister, $src$$FloatRegister, $shift$$FloatRegister); ++ %} ++ ins_pipe( pipe_slow ); ++%} ++ ++instruct sll2L_imm(vecX dst, vecX src, immI shift) %{ ++ predicate(n->as_Vector()->length() == 2); ++ match(Set dst (LShiftVL src shift)); ++ format %{ "vslli.d $dst, $src, $shift\t# @sll2L_imm" %} ++ ins_encode %{ ++ __ vslli_d($dst$$FloatRegister, $src$$FloatRegister, $shift$$constant); ++ %} ++ ins_pipe( pipe_slow ); ++%} ++ ++instruct sll32B(vecY dst, vecY src, vecY shift, vecY tmp) %{ ++ predicate(n->as_Vector()->length() == 32); ++ match(Set dst (LShiftVB src shift)); ++ effect(TEMP dst, TEMP tmp); ++ format %{ "xvsll $dst, $src, $shift\t# TEMP($tmp) @sll32B" %} ++ ins_encode %{ ++ __ xvsll_b($tmp$$FloatRegister, $src$$FloatRegister, $shift$$FloatRegister); ++ __ xvslti_bu($dst$$FloatRegister, $shift$$FloatRegister, 0x8); ++ __ xvand_v($dst$$FloatRegister, $dst$$FloatRegister, $tmp$$FloatRegister); ++ %} ++ ins_pipe( pipe_slow ); ++%} ++ ++instruct sll32B_imm(vecY dst, vecY src, immI shift) %{ ++ predicate(n->as_Vector()->length() == 32); ++ match(Set dst (LShiftVB src shift)); ++ format %{ "xvslli.b $dst, $src, $shift\t# @sll32B_imm" %} ++ ins_encode %{ ++ if ($shift$$constant >= 8) { ++ __ xvxor_v($dst$$FloatRegister, $dst$$FloatRegister, $dst$$FloatRegister); ++ } else { ++ __ xvslli_b($dst$$FloatRegister, $src$$FloatRegister, $shift$$constant); ++ } ++ %} ++ ins_pipe( pipe_slow ); ++%} ++ ++instruct sll16S(vecY dst, vecY src, vecY shift, vecY tmp) %{ ++ predicate(n->as_Vector()->length() == 16); ++ match(Set dst (LShiftVS src shift)); ++ effect(TEMP dst, TEMP tmp); ++ format %{ "xvsll $dst, $src, $shift\t# TEMP($tmp) @sll16S" %} ++ ins_encode %{ ++ __ xvsll_h($tmp$$FloatRegister, $src$$FloatRegister, $shift$$FloatRegister); ++ __ xvslti_bu($dst$$FloatRegister, $shift$$FloatRegister, 0x10); ++ __ xvand_v($dst$$FloatRegister, $dst$$FloatRegister, $tmp$$FloatRegister); ++ %} ++ ins_pipe( pipe_slow ); ++%} ++ ++instruct sll16S_imm(vecY dst, vecY src, immI shift) %{ ++ predicate(n->as_Vector()->length() == 16); ++ match(Set dst (LShiftVS src shift)); ++ format %{ "xvslli.h $dst, $src, $shift\t# @sll16S_imm" %} ++ ins_encode %{ ++ if ($shift$$constant >= 16) { ++ __ xvxor_v($dst$$FloatRegister, $dst$$FloatRegister, $dst$$FloatRegister); ++ } else { ++ __ xvslli_h($dst$$FloatRegister, $src$$FloatRegister, $shift$$constant); ++ } ++ %} ++ ins_pipe( pipe_slow ); ++%} ++ ++instruct sll8I(vecY dst, vecY src, vecY shift) %{ ++ predicate(n->as_Vector()->length() == 8); ++ match(Set dst (LShiftVI src shift)); ++ format %{ "xvsll.w $dst, $src, $shift\t# @sll8I" %} ++ ins_encode %{ ++ __ xvsll_w($dst$$FloatRegister, $src$$FloatRegister, $shift$$FloatRegister); ++ %} ++ ins_pipe( pipe_slow ); ++%} ++ ++instruct sll8I_imm(vecY dst, vecY src, immI shift) %{ ++ predicate(n->as_Vector()->length() == 8); ++ match(Set dst (LShiftVI src shift)); ++ format %{ "xvslli.w $dst, $src, $shift\t# @sll8I_imm" %} ++ ins_encode %{ ++ __ xvslli_w($dst$$FloatRegister, $src$$FloatRegister, $shift$$constant); ++ %} ++ ins_pipe( pipe_slow ); ++%} ++ ++instruct sll4L(vecY dst, vecY src, vecY shift) %{ ++ predicate(n->as_Vector()->length() == 4); ++ match(Set dst (LShiftVL src shift)); ++ format %{ "xvsll.d $dst, $src, $shift\t# @sll4L" %} ++ ins_encode %{ ++ __ xvsll_d($dst$$FloatRegister, $src$$FloatRegister, $shift$$FloatRegister); ++ %} ++ ins_pipe( pipe_slow ); ++%} ++ ++instruct sll4L_imm(vecY dst, vecY src, immI shift) %{ ++ predicate(n->as_Vector()->length() == 4); ++ match(Set dst (LShiftVL src shift)); ++ format %{ "xvslli.d $dst, $src, $shift\t# @sll4L_imm" %} ++ ins_encode %{ ++ __ xvslli_d($dst$$FloatRegister, $src$$FloatRegister, $shift$$constant); ++ %} ++ ins_pipe( pipe_slow ); ++%} ++ ++// ----------------------- LogicalRightShift ---------------------------------- ++ ++instruct srl16B(vecX dst, vecX src, vecX shift, vecX tmp) %{ ++ predicate(n->as_Vector()->length() == 16); ++ match(Set dst (URShiftVB src shift)); ++ effect(TEMP dst, TEMP tmp); ++ format %{ "vsrl $dst, $src, $shift\t# TEMP($tmp) @srl16B" %} ++ ins_encode %{ ++ __ vsrl_b($tmp$$FloatRegister, $src$$FloatRegister, $shift$$FloatRegister); ++ __ vslti_bu($dst$$FloatRegister, $shift$$FloatRegister, 0x8); ++ __ vand_v($dst$$FloatRegister, $dst$$FloatRegister, $tmp$$FloatRegister); ++ %} ++ ins_pipe( pipe_slow ); ++%} ++ ++instruct srl16B_imm(vecX dst, vecX src, immI shift) %{ ++ predicate(n->as_Vector()->length() == 16); ++ match(Set dst (URShiftVB src shift)); ++ format %{ "vsrli.b $dst, $src, $shift\t# @srl16B_imm" %} ++ ins_encode %{ ++ if ($shift$$constant >= 8) { ++ __ vxor_v($dst$$FloatRegister, $dst$$FloatRegister, $dst$$FloatRegister); ++ } else { ++ __ vsrli_b($dst$$FloatRegister, $src$$FloatRegister, $shift$$constant); ++ } ++ %} ++ ins_pipe( pipe_slow ); ++%} ++ ++instruct srl8S(vecX dst, vecX src, vecX shift, vecX tmp) %{ ++ predicate(n->as_Vector()->length() == 8); ++ match(Set dst (URShiftVS src shift)); ++ effect(TEMP dst, TEMP tmp); ++ format %{ "vsrl $dst, $src, $shift\t# TEMP($tmp) @srl8S" %} ++ ins_encode %{ ++ __ vsrl_h($tmp$$FloatRegister, $src$$FloatRegister, $shift$$FloatRegister); ++ __ vslti_bu($dst$$FloatRegister, $shift$$FloatRegister, 0x10); ++ __ vand_v($dst$$FloatRegister, $dst$$FloatRegister, $tmp$$FloatRegister); ++ %} ++ ins_pipe( pipe_slow ); ++%} ++ ++instruct srl8S_imm(vecX dst, vecX src, immI shift) %{ ++ predicate(n->as_Vector()->length() == 8); ++ match(Set dst (URShiftVS src shift)); ++ format %{ "vsrli.h $dst, $src, $shift\t# @srl8S_imm" %} ++ ins_encode %{ ++ if ($shift$$constant >= 16) { ++ __ vxor_v($dst$$FloatRegister, $dst$$FloatRegister, $dst$$FloatRegister); ++ } else { ++ __ vsrli_h($dst$$FloatRegister, $src$$FloatRegister, $shift$$constant); ++ } ++ %} ++ ins_pipe( pipe_slow ); ++%} ++ ++instruct srl4I(vecX dst, vecX src, vecX shift) %{ ++ predicate(n->as_Vector()->length() == 4); ++ match(Set dst (URShiftVI src shift)); ++ format %{ "vsrl.w $dst, $src, $shift\t# @srl4I" %} ++ ins_encode %{ ++ __ vsrl_w($dst$$FloatRegister, $src$$FloatRegister, $shift$$FloatRegister); ++ %} ++ ins_pipe( pipe_slow ); ++%} ++ ++instruct srl4I_imm(vecX dst, vecX src, immI shift) %{ ++ predicate(n->as_Vector()->length() == 4); ++ match(Set dst (URShiftVI src shift)); ++ format %{ "vsrli.w $dst, $src, $shift\t# @srl4I_imm" %} ++ ins_encode %{ ++ __ vsrli_w($dst$$FloatRegister, $src$$FloatRegister, $shift$$constant); ++ %} ++ ins_pipe( pipe_slow ); ++%} ++ ++instruct srl2L(vecX dst, vecX src, vecX shift) %{ ++ predicate(n->as_Vector()->length() == 2); ++ match(Set dst (URShiftVL src shift)); ++ format %{ "vsrl.d $dst, $src, $shift\t# @srl2L" %} ++ ins_encode %{ ++ __ vsrl_d($dst$$FloatRegister, $src$$FloatRegister, $shift$$FloatRegister); ++ %} ++ ins_pipe( pipe_slow ); ++%} ++ ++instruct srl2L_imm(vecX dst, vecX src, immI shift) %{ ++ predicate(n->as_Vector()->length() == 2); ++ match(Set dst (URShiftVL src shift)); ++ format %{ "vsrli.d $dst, $src, $shift\t# @srl2L_imm" %} ++ ins_encode %{ ++ __ vsrli_d($dst$$FloatRegister, $src$$FloatRegister, $shift$$constant); ++ %} ++ ins_pipe( pipe_slow ); ++%} ++ ++instruct srl32B(vecY dst, vecY src, vecY shift, vecY tmp) %{ ++ predicate(n->as_Vector()->length() == 32); ++ match(Set dst (URShiftVB src shift)); ++ effect(TEMP dst, TEMP tmp); ++ format %{ "xvsrl $dst, $src, $shift\t# TEMP($tmp) @srl32B" %} ++ ins_encode %{ ++ __ xvsrl_b($tmp$$FloatRegister, $src$$FloatRegister, $shift$$FloatRegister); ++ __ xvslti_bu($dst$$FloatRegister, $shift$$FloatRegister, 0x8); ++ __ xvand_v($dst$$FloatRegister, $dst$$FloatRegister, $tmp$$FloatRegister); ++ %} ++ ins_pipe( pipe_slow ); ++%} ++ ++instruct srl32B_imm(vecY dst, vecY src, immI shift) %{ ++ predicate(n->as_Vector()->length() == 32); ++ match(Set dst (URShiftVB src shift)); ++ format %{ "xvsrli.b $dst, $src, $shift\t# @srl32B_imm" %} ++ ins_encode %{ ++ if ($shift$$constant >= 8) { ++ __ xvxor_v($dst$$FloatRegister, $dst$$FloatRegister, $dst$$FloatRegister); ++ } else { ++ __ xvsrli_b($dst$$FloatRegister, $src$$FloatRegister, $shift$$constant); ++ } ++ %} ++ ins_pipe( pipe_slow ); ++%} ++ ++instruct srl16S(vecY dst, vecY src, vecY shift, vecY tmp) %{ ++ predicate(n->as_Vector()->length() == 16); ++ match(Set dst (URShiftVS src shift)); ++ effect(TEMP dst, TEMP tmp); ++ format %{ "xvsrl $dst, $src, $shift\t# TEMP($tmp) @srl16S" %} ++ ins_encode %{ ++ __ xvsrl_h($tmp$$FloatRegister, $src$$FloatRegister, $shift$$FloatRegister); ++ __ xvslti_bu($dst$$FloatRegister, $shift$$FloatRegister, 0x10); ++ __ xvand_v($dst$$FloatRegister, $dst$$FloatRegister, $tmp$$FloatRegister); ++ %} ++ ins_pipe( pipe_slow ); ++%} ++ ++instruct srl16S_imm(vecY dst, vecY src, immI shift) %{ ++ predicate(n->as_Vector()->length() == 16); ++ match(Set dst (URShiftVS src shift)); ++ format %{ "xvsrli.h $dst, $src, $shift\t# @srl16S_imm" %} ++ ins_encode %{ ++ if ($shift$$constant >= 16) { ++ __ xvxor_v($dst$$FloatRegister, $dst$$FloatRegister, $dst$$FloatRegister); ++ } else { ++ __ xvsrli_h($dst$$FloatRegister, $src$$FloatRegister, $shift$$constant); ++ } ++ %} ++ ins_pipe( pipe_slow ); ++%} ++ ++instruct srl8I(vecY dst, vecY src, vecY shift) %{ ++ predicate(n->as_Vector()->length() == 8); ++ match(Set dst (URShiftVI src shift)); ++ format %{ "xvsrl.w $dst, $src, $shift\t# @srl8I" %} ++ ins_encode %{ ++ __ xvsrl_w($dst$$FloatRegister, $src$$FloatRegister, $shift$$FloatRegister); ++ %} ++ ins_pipe( pipe_slow ); ++%} ++ ++instruct srl8I_imm(vecY dst, vecY src, immI shift) %{ ++ predicate(n->as_Vector()->length() == 8); ++ match(Set dst (URShiftVI src shift)); ++ format %{ "xvsrli.w $dst, $src, $shift\t# @srl8I_imm" %} ++ ins_encode %{ ++ __ xvsrli_w($dst$$FloatRegister, $src$$FloatRegister, $shift$$constant); ++ %} ++ ins_pipe( pipe_slow ); ++%} ++ ++instruct srl4L(vecY dst, vecY src, vecY shift) %{ ++ predicate(n->as_Vector()->length() == 4); ++ match(Set dst (URShiftVL src shift)); ++ format %{ "xvsrl.d $dst, $src, $shift\t# @srl4L" %} ++ ins_encode %{ ++ __ xvsrl_d($dst$$FloatRegister, $src$$FloatRegister, $shift$$FloatRegister); ++ %} ++ ins_pipe( pipe_slow ); ++%} ++ ++instruct srl4L_imm(vecY dst, vecY src, immI shift) %{ ++ predicate(n->as_Vector()->length() == 4); ++ match(Set dst (URShiftVL src shift)); ++ format %{ "xvsrli.d $dst, $src, $shift\t# @srl4L_imm" %} ++ ins_encode %{ ++ __ xvsrli_d($dst$$FloatRegister, $src$$FloatRegister, $shift$$constant); ++ %} ++ ins_pipe( pipe_slow ); ++%} ++ ++// ------------------------- ArithmeticRightShift ----------------------------- ++ ++instruct sra16B(vecX dst, vecX src, vecX shift, vecX tmp) %{ ++ predicate(n->as_Vector()->length() == 16); ++ match(Set dst (RShiftVB src shift)); ++ effect(TEMP tmp); ++ format %{ "vsra $dst, $src, $shift\t# TEMP($tmp) @sra16B" %} ++ ins_encode %{ ++ __ vslti_bu($tmp$$FloatRegister, $shift$$FloatRegister, 0x8); ++ __ vorn_v($tmp$$FloatRegister, $shift$$FloatRegister, $tmp$$FloatRegister); ++ __ vsra_b($dst$$FloatRegister, $src$$FloatRegister, $tmp$$FloatRegister); ++ %} ++ ins_pipe( pipe_slow ); ++%} ++ ++instruct sra16B_imm(vecX dst, vecX src, immI shift) %{ ++ predicate(n->as_Vector()->length() == 16); ++ match(Set dst (RShiftVB src shift)); ++ format %{ "vsrai.b $dst, $src, $shift\t# @sra16B_imm" %} ++ ins_encode %{ ++ if ($shift$$constant >= 8) { ++ __ vsrai_b($dst$$FloatRegister, $src$$FloatRegister, 7); ++ } else { ++ __ vsrai_b($dst$$FloatRegister, $src$$FloatRegister, $shift$$constant); ++ } ++ %} ++ ins_pipe( pipe_slow ); ++%} ++ ++instruct sra8S(vecX dst, vecX src, vecX shift, vecX tmp) %{ ++ predicate(n->as_Vector()->length() == 8); ++ match(Set dst (RShiftVS src shift)); ++ effect(TEMP tmp); ++ format %{ "vsra $dst, $src, $shift\t# TEMP($tmp) @sra8S" %} ++ ins_encode %{ ++ __ vslti_bu($tmp$$FloatRegister, $shift$$FloatRegister, 0x10); ++ __ vorn_v($tmp$$FloatRegister, $shift$$FloatRegister, $tmp$$FloatRegister); ++ __ vsra_h($dst$$FloatRegister, $src$$FloatRegister, $tmp$$FloatRegister); ++ %} ++ ins_pipe( pipe_slow ); ++%} ++ ++instruct sra8S_imm(vecX dst, vecX src, immI shift) %{ ++ predicate(n->as_Vector()->length() == 8); ++ match(Set dst (RShiftVS src shift)); ++ format %{ "vsrai.h $dst, $src, $shift\t# @sra8S_imm" %} ++ ins_encode %{ ++ if ($shift$$constant >= 16) { ++ __ vsrai_h($dst$$FloatRegister, $src$$FloatRegister, 15); ++ } else { ++ __ vsrai_h($dst$$FloatRegister, $src$$FloatRegister, $shift$$constant); ++ } ++ %} ++ ins_pipe( pipe_slow ); ++%} ++ ++instruct sra4I(vecX dst, vecX src, vecX shift) %{ ++ predicate(n->as_Vector()->length() == 4); ++ match(Set dst (RShiftVI src shift)); ++ format %{ "vsra.w $dst, $src, $shift\t# @sra4I" %} ++ ins_encode %{ ++ __ vsra_w($dst$$FloatRegister, $src$$FloatRegister, $shift$$FloatRegister); ++ %} ++ ins_pipe( pipe_slow ); ++%} ++ ++instruct sra4I_imm(vecX dst, vecX src, immI shift) %{ ++ predicate(n->as_Vector()->length() == 4); ++ match(Set dst (RShiftVI src shift)); ++ format %{ "vsrai.w $dst, $src, $shift\t# @sra4I_imm" %} ++ ins_encode %{ ++ __ vsrai_w($dst$$FloatRegister, $src$$FloatRegister, $shift$$constant); ++ %} ++ ins_pipe( pipe_slow ); ++%} ++ ++instruct sra2L(vecX dst, vecX src, vecX shift) %{ ++ predicate(n->as_Vector()->length() == 2); ++ match(Set dst (RShiftVL src shift)); ++ format %{ "vsra.d $dst, $src, $shift\t# @sra2L" %} ++ ins_encode %{ ++ __ vsra_d($dst$$FloatRegister, $src$$FloatRegister, $shift$$FloatRegister); ++ %} ++ ins_pipe( pipe_slow ); ++%} ++ ++instruct sra2L_imm(vecX dst, vecX src, immI shift) %{ ++ predicate(n->as_Vector()->length() == 2); ++ match(Set dst (RShiftVL src shift)); ++ format %{ "vsrai.d $dst, $src, $shift\t# @sra2L_imm" %} ++ ins_encode %{ ++ __ vsrai_d($dst$$FloatRegister, $src$$FloatRegister, $shift$$constant); ++ %} ++ ins_pipe( pipe_slow ); ++%} ++ ++instruct sra32B(vecY dst, vecY src, vecY shift, vecY tmp) %{ ++ predicate(n->as_Vector()->length() == 32); ++ match(Set dst (RShiftVB src shift)); ++ effect(TEMP tmp); ++ format %{ "xvsra $dst, $src, $shift\t# TEMP($tmp) @sra32B" %} ++ ins_encode %{ ++ __ xvslti_bu($tmp$$FloatRegister, $shift$$FloatRegister, 0x8); ++ __ xvorn_v($tmp$$FloatRegister, $shift$$FloatRegister, $tmp$$FloatRegister); ++ __ xvsra_b($dst$$FloatRegister, $src$$FloatRegister, $tmp$$FloatRegister); ++ %} ++ ins_pipe( pipe_slow ); ++%} ++ ++instruct sra32B_imm(vecY dst, vecY src, immI shift) %{ ++ predicate(n->as_Vector()->length() == 32); ++ match(Set dst (RShiftVB src shift)); ++ format %{ "xvsrai.b $dst, $src, $shift\t# @sra32B_imm" %} ++ ins_encode %{ ++ if ($shift$$constant >= 8) { ++ __ xvsrai_b($dst$$FloatRegister, $src$$FloatRegister, 7); ++ } else { ++ __ xvsrai_b($dst$$FloatRegister, $src$$FloatRegister, $shift$$constant); ++ } ++ %} ++ ins_pipe( pipe_slow ); ++%} ++ ++instruct sra16S(vecY dst, vecY src, vecY shift, vecY tmp) %{ ++ predicate(n->as_Vector()->length() == 16); ++ match(Set dst (RShiftVS src shift)); ++ effect(TEMP tmp); ++ format %{ "xvsra $dst, $src, $shift\t# TEMP($tmp) @sra16S" %} ++ ins_encode %{ ++ __ xvslti_bu($tmp$$FloatRegister, $shift$$FloatRegister, 0x10); ++ __ xvorn_v($tmp$$FloatRegister, $shift$$FloatRegister, $tmp$$FloatRegister); ++ __ xvsra_h($dst$$FloatRegister, $src$$FloatRegister, $tmp$$FloatRegister); ++ %} ++ ins_pipe( pipe_slow ); ++%} ++ ++instruct sra16S_imm(vecY dst, vecY src, immI shift) %{ ++ predicate(n->as_Vector()->length() == 16); ++ match(Set dst (RShiftVS src shift)); ++ format %{ "xvsrai.h $dst, $src, $shift\t# @sra16S_imm" %} ++ ins_encode %{ ++ if ($shift$$constant >= 16) { ++ __ xvsrai_h($dst$$FloatRegister, $src$$FloatRegister, 15); ++ } else { ++ __ xvsrai_h($dst$$FloatRegister, $src$$FloatRegister, $shift$$constant); ++ } ++ %} ++ ins_pipe( pipe_slow ); ++%} ++ ++instruct sra8I(vecY dst, vecY src, vecY shift) %{ ++ predicate(n->as_Vector()->length() == 8); ++ match(Set dst (RShiftVI src shift)); ++ format %{ "xvsra.w $dst, $src, $shift\t# @sra8I" %} ++ ins_encode %{ ++ __ xvsra_w($dst$$FloatRegister, $src$$FloatRegister, $shift$$FloatRegister); ++ %} ++ ins_pipe( pipe_slow ); ++%} ++ ++instruct sra8I_imm(vecY dst, vecY src, immI shift) %{ ++ predicate(n->as_Vector()->length() == 8); ++ match(Set dst (RShiftVI src shift)); ++ format %{ "xvsrai.w $dst, $src, $shift\t# @sra8I_imm" %} ++ ins_encode %{ ++ __ xvsrai_w($dst$$FloatRegister, $src$$FloatRegister, $shift$$constant); ++ %} ++ ins_pipe( pipe_slow ); ++%} ++ ++instruct sra4L(vecY dst, vecY src, vecY shift) %{ ++ predicate(n->as_Vector()->length() == 4); ++ match(Set dst (RShiftVL src shift)); ++ format %{ "xvsra.d $dst, $src, $shift\t# @sra4L" %} ++ ins_encode %{ ++ __ xvsra_d($dst$$FloatRegister, $src$$FloatRegister, $shift$$FloatRegister); ++ %} ++ ins_pipe( pipe_slow ); ++%} ++ ++instruct sra4L_imm(vecY dst, vecY src, immI shift) %{ ++ predicate(n->as_Vector()->length() == 4); ++ match(Set dst (RShiftVL src shift)); ++ format %{ "xvsrai.d $dst, $src, $shift\t# @sra4L_imm" %} ++ ins_encode %{ ++ __ xvsrai_d($dst$$FloatRegister, $src$$FloatRegister, $shift$$constant); ++ %} ++ ins_pipe( pipe_slow ); ++%} ++ ++// --------------------------------- AND -------------------------------------- ++ ++instruct andV16(vecX dst, vecX src1, vecX src2) %{ ++ predicate(n->as_Vector()->length_in_bytes() == 16); ++ match(Set dst (AndV src1 src2)); ++ format %{ "vand.v $dst, $src1, $src2\t# @andV16" %} ++ ins_encode %{ ++ __ vand_v($dst$$FloatRegister, $src1$$FloatRegister, $src2$$FloatRegister); ++ %} ++ ins_pipe( pipe_slow ); ++%} ++ ++instruct and16B_imm(vecX dst, vecX src, immIU8 imm) %{ ++ predicate(n->as_Vector()->length() == 16); ++ match(Set dst (AndV src (ReplicateB imm))); ++ format %{ "vandi.b $dst, $src, $imm\t# @and16B_imm" %} ++ ins_encode %{ ++ __ vandi_b($dst$$FloatRegister, $src$$FloatRegister, $imm$$constant); ++ %} ++ ins_pipe( pipe_slow ); ++%} ++ ++instruct andV32(vecY dst, vecY src1, vecY src2) %{ ++ predicate(n->as_Vector()->length_in_bytes() == 32); ++ match(Set dst (AndV src1 src2)); ++ format %{ "xvand.v $dst, $src1, $src2\t# @andV32" %} ++ ins_encode %{ ++ __ xvand_v($dst$$FloatRegister, $src1$$FloatRegister, $src2$$FloatRegister); ++ %} ++ ins_pipe( pipe_slow ); ++%} ++ ++instruct and32B_imm(vecY dst, vecY src, immIU8 imm) %{ ++ predicate(n->as_Vector()->length() == 32); ++ match(Set dst (AndV src (ReplicateB imm))); ++ format %{ "xvandi.b $dst, $src, $imm\t# @and32B_imm" %} ++ ins_encode %{ ++ __ xvandi_b($dst$$FloatRegister, $src$$FloatRegister, $imm$$constant); ++ %} ++ ins_pipe( pipe_slow ); ++%} ++ ++// --------------------------------- OR --------------------------------------- ++ ++instruct orV16(vecX dst, vecX src1, vecX src2) %{ ++ predicate(n->as_Vector()->length_in_bytes() == 16); ++ match(Set dst (OrV src1 src2)); ++ format %{ "vor.v $dst, $src1, $src2\t# @orV16" %} ++ ins_encode %{ ++ __ vor_v($dst$$FloatRegister, $src1$$FloatRegister, $src2$$FloatRegister); ++ %} ++ ins_pipe( pipe_slow ); ++%} ++ ++instruct or16B_imm(vecX dst, vecX src, immIU8 imm) %{ ++ predicate(n->as_Vector()->length() == 16); ++ match(Set dst (OrV src (ReplicateB imm))); ++ format %{ "vori.b $dst, $src, $imm\t# @or16B_imm" %} ++ ins_encode %{ ++ __ vori_b($dst$$FloatRegister, $src$$FloatRegister, $imm$$constant); ++ %} ++ ins_pipe( pipe_slow ); ++%} ++ ++instruct orV32(vecY dst, vecY src1, vecY src2) %{ ++ predicate(n->as_Vector()->length_in_bytes() == 32); ++ match(Set dst (OrV src1 src2)); ++ format %{ "xvor.v $dst, $src1, $src2\t# @orV32" %} ++ ins_encode %{ ++ __ xvor_v($dst$$FloatRegister, $src1$$FloatRegister, $src2$$FloatRegister); ++ %} ++ ins_pipe( pipe_slow ); ++%} ++ ++instruct or32B_imm(vecY dst, vecY src, immIU8 imm) %{ ++ predicate(n->as_Vector()->length() == 32); ++ match(Set dst (OrV src (ReplicateB imm))); ++ format %{ "xvori.b $dst, $src, $imm\t# @or32B_imm" %} ++ ins_encode %{ ++ __ xvori_b($dst$$FloatRegister, $src$$FloatRegister, $imm$$constant); ++ %} ++ ins_pipe( pipe_slow ); ++%} ++ ++// --------------------------------- XOR -------------------------------------- ++ ++instruct xorV16(vecX dst, vecX src1, vecX src2) %{ ++ predicate(n->as_Vector()->length_in_bytes() == 16); ++ match(Set dst (XorV src1 src2)); ++ format %{ "vxor.v $dst, $src1, $src2\t# @xorV16" %} ++ ins_encode %{ ++ __ vxor_v($dst$$FloatRegister, $src1$$FloatRegister, $src2$$FloatRegister); ++ %} ++ ins_pipe( pipe_slow ); ++%} ++ ++instruct xor16B_imm(vecX dst, vecX src, immIU8 imm) %{ ++ predicate(n->as_Vector()->length() == 16); ++ match(Set dst (XorV src (ReplicateB imm))); ++ format %{ "vxori.b $dst, $src, $imm\t# @xor16B_imm" %} ++ ins_encode %{ ++ __ vxori_b($dst$$FloatRegister, $src$$FloatRegister, $imm$$constant); ++ %} ++ ins_pipe( pipe_slow ); ++%} ++ ++instruct xorV32(vecY dst, vecY src1, vecY src2) %{ ++ predicate(n->as_Vector()->length_in_bytes() == 32); ++ match(Set dst (XorV src1 src2)); ++ format %{ "xvxor.v $dst, $src1, $src2\t# @xorV32" %} ++ ins_encode %{ ++ __ xvxor_v($dst$$FloatRegister, $src1$$FloatRegister, $src2$$FloatRegister); ++ %} ++ ins_pipe( pipe_slow ); ++%} ++ ++instruct xor32B_imm(vecX dst, vecX src, immIU8 imm) %{ ++ predicate(n->as_Vector()->length() == 32); ++ match(Set dst (XorV src (ReplicateB imm))); ++ format %{ "xvxori.b $dst, $src, $imm\t# @xor32B_imm" %} ++ ins_encode %{ ++ __ xvxori_b($dst$$FloatRegister, $src$$FloatRegister, $imm$$constant); ++ %} ++ ins_pipe( pipe_slow ); ++%} ++ ++// --------------------------------- NOR -------------------------------------- ++ ++instruct norV16(vecX dst, vecX src1, vecX src2, immI_M1 m1) %{ ++ predicate(n->as_Vector()->length_in_bytes() == 16); ++ match(Set dst (XorV (OrV src1 src2) (ReplicateB m1))); ++ match(Set dst (XorV (OrV src1 src2) (ReplicateS m1))); ++ match(Set dst (XorV (OrV src1 src2) (ReplicateI m1))); ++ format %{ "vnor.v $dst, $src1, $src2\t# @norV16" %} ++ ins_encode %{ ++ __ vnor_v($dst$$FloatRegister, $src1$$FloatRegister, $src2$$FloatRegister); ++ %} ++ ins_pipe( pipe_slow ); ++%} ++ ++instruct nor16B_imm(vecX dst, vecX src, immIU8 imm, immI_M1 m1) %{ ++ predicate(n->as_Vector()->length() == 16); ++ match(Set dst (XorV (OrV src (ReplicateB imm)) (ReplicateB m1))); ++ format %{ "vnori.b $dst, $src, $imm\t# @nor16B_imm" %} ++ ins_encode %{ ++ __ vnori_b($dst$$FloatRegister, $src$$FloatRegister, $imm$$constant); ++ %} ++ ins_pipe( pipe_slow ); ++%} ++ ++instruct norV32(vecY dst, vecY src1, vecY src2, immI_M1 m1) %{ ++ predicate(n->as_Vector()->length_in_bytes() == 32); ++ match(Set dst (XorV (OrV src1 src2) (ReplicateB m1))); ++ match(Set dst (XorV (OrV src1 src2) (ReplicateS m1))); ++ match(Set dst (XorV (OrV src1 src2) (ReplicateI m1))); ++ format %{ "xvnor.v $dst, $src1, $src2\t# @norV32" %} ++ ins_encode %{ ++ __ xvnor_v($dst$$FloatRegister, $src1$$FloatRegister, $src2$$FloatRegister); ++ %} ++ ins_pipe( pipe_slow ); ++%} ++ ++instruct nor32B_imm(vecY dst, vecY src, immIU8 imm, immI_M1 m1) %{ ++ predicate(n->as_Vector()->length() == 32); ++ match(Set dst (XorV (OrV src (ReplicateB imm)) (ReplicateB m1))); ++ format %{ "xvnori.b $dst, $src, $imm\t# @nor32B_imm" %} ++ ins_encode %{ ++ __ xvnori_b($dst$$FloatRegister, $src$$FloatRegister, $imm$$constant); ++ %} ++ ins_pipe( pipe_slow ); ++%} ++ ++// --------------------------------- ANDN ------------------------------------- ++ ++instruct andnV16(vecX dst, vecX src1, vecX src2, immI_M1 m1) %{ ++ predicate(n->as_Vector()->length_in_bytes() == 16); ++ match(Set dst (AndV src2 (XorV src1 (ReplicateB m1)))); ++ match(Set dst (AndV src2 (XorV src1 (ReplicateS m1)))); ++ match(Set dst (AndV src2 (XorV src1 (ReplicateI m1)))); ++ format %{ "vandn.v $dst, $src1, $src2\t# @andnV16" %} ++ ins_encode %{ ++ __ vandn_v($dst$$FloatRegister, $src1$$FloatRegister, $src2$$FloatRegister); ++ %} ++ ins_pipe( pipe_slow ); ++%} ++ ++instruct andnV32(vecY dst, vecY src1, vecY src2, immI_M1 m1) %{ ++ predicate(n->as_Vector()->length_in_bytes() == 32); ++ match(Set dst (AndV src2 (XorV src1 (ReplicateB m1)))); ++ match(Set dst (AndV src2 (XorV src1 (ReplicateS m1)))); ++ match(Set dst (AndV src2 (XorV src1 (ReplicateI m1)))); ++ format %{ "xvandn.v $dst, $src1, $src2\t# @andnV32" %} ++ ins_encode %{ ++ __ xvandn_v($dst$$FloatRegister, $src1$$FloatRegister, $src2$$FloatRegister); ++ %} ++ ins_pipe( pipe_slow ); ++%} ++ ++// --------------------------------- ORN -------------------------------------- ++ ++instruct ornV16(vecX dst, vecX src1, vecX src2, immI_M1 m1) %{ ++ predicate(n->as_Vector()->length_in_bytes() == 16); ++ match(Set dst (OrV src1 (XorV src2 (ReplicateB m1)))); ++ match(Set dst (OrV src1 (XorV src2 (ReplicateS m1)))); ++ match(Set dst (OrV src1 (XorV src2 (ReplicateI m1)))); ++ format %{ "vorn.v $dst, $src1, $src2\t# @ornV16" %} ++ ins_encode %{ ++ __ vorn_v($dst$$FloatRegister, $src1$$FloatRegister, $src2$$FloatRegister); ++ %} ++ ins_pipe( pipe_slow ); ++%} ++ ++instruct ornV32(vecY dst, vecY src1, vecY src2, immI_M1 m1) %{ ++ predicate(n->as_Vector()->length_in_bytes() == 32); ++ match(Set dst (OrV src1 (XorV src2 (ReplicateB m1)))); ++ match(Set dst (OrV src1 (XorV src2 (ReplicateS m1)))); ++ match(Set dst (OrV src1 (XorV src2 (ReplicateI m1)))); ++ format %{ "xvorn.v $dst, $src1, $src2\t# @ornV32" %} ++ ins_encode %{ ++ __ xvorn_v($dst$$FloatRegister, $src1$$FloatRegister, $src2$$FloatRegister); ++ %} ++ ins_pipe( pipe_slow ); ++%} ++ ++ ++//----------PEEPHOLE RULES----------------------------------------------------- ++// These must follow all instruction definitions as they use the names ++// defined in the instructions definitions. ++// ++// peepmatch ( root_instr_name [preceeding_instruction]* ); ++// ++// peepconstraint %{ ++// (instruction_number.operand_name relational_op instruction_number.operand_name ++// [, ...] ); ++// // instruction numbers are zero-based using left to right order in peepmatch ++// ++// peepreplace ( instr_name ( [instruction_number.operand_name]* ) ); ++// // provide an instruction_number.operand_name for each operand that appears ++// // in the replacement instruction's match rule ++// ++// ---------VM FLAGS--------------------------------------------------------- ++// ++// All peephole optimizations can be turned off using -XX:-OptoPeephole ++// ++// Each peephole rule is given an identifying number starting with zero and ++// increasing by one in the order seen by the parser. An individual peephole ++// can be enabled, and all others disabled, by using -XX:OptoPeepholeAt=# ++// on the command-line. ++// ++// ---------CURRENT LIMITATIONS---------------------------------------------- ++// ++// Only match adjacent instructions in same basic block ++// Only equality constraints ++// Only constraints between operands, not (0.dest_reg == EAX_enc) ++// Only one replacement instruction ++// ++// ---------EXAMPLE---------------------------------------------------------- ++// ++// // pertinent parts of existing instructions in architecture description ++// instruct movI(eRegI dst, eRegI src) %{ ++// match(Set dst (CopyI src)); ++// %} ++// ++// instruct incI_eReg(eRegI dst, immI_1 src, eFlagsReg cr) %{ ++// match(Set dst (AddI dst src)); ++// effect(KILL cr); ++// %} ++// ++// // Change (inc mov) to lea ++// peephole %{ ++// // increment preceeded by register-register move ++// peepmatch ( incI_eReg movI ); ++// // require that the destination register of the increment ++// // match the destination register of the move ++// peepconstraint ( 0.dst == 1.dst ); ++// // construct a replacement instruction that sets ++// // the destination to ( move's source register + one ) ++// peepreplace ( leaI_eReg_immI( 0.dst 1.src 0.src ) ); ++// %} ++// ++// Implementation no longer uses movX instructions since ++// machine-independent system no longer uses CopyX nodes. ++// ++// peephole %{ ++// peepmatch ( incI_eReg movI ); ++// peepconstraint ( 0.dst == 1.dst ); ++// peepreplace ( leaI_eReg_immI( 0.dst 1.src 0.src ) ); ++// %} ++// ++// peephole %{ ++// peepmatch ( decI_eReg movI ); ++// peepconstraint ( 0.dst == 1.dst ); ++// peepreplace ( leaI_eReg_immI( 0.dst 1.src 0.src ) ); ++// %} ++// ++// peephole %{ ++// peepmatch ( addI_eReg_imm movI ); ++// peepconstraint ( 0.dst == 1.dst ); ++// peepreplace ( leaI_eReg_immI( 0.dst 1.src 0.src ) ); ++// %} ++// ++// peephole %{ ++// peepmatch ( addP_eReg_imm movP ); ++// peepconstraint ( 0.dst == 1.dst ); ++// peepreplace ( leaP_eReg_immI( 0.dst 1.src 0.src ) ); ++// %} ++ ++// // Change load of spilled value to only a spill ++// instruct storeI(memory mem, eRegI src) %{ ++// match(Set mem (StoreI mem src)); ++// %} ++// ++// instruct loadI(eRegI dst, memory mem) %{ ++// match(Set dst (LoadI mem)); ++// %} ++// ++//peephole %{ ++// peepmatch ( loadI storeI ); ++// peepconstraint ( 1.src == 0.dst, 1.mem == 0.mem ); ++// peepreplace ( storeI( 1.mem 1.mem 1.src ) ); ++//%} ++ ++//----------SMARTSPILL RULES--------------------------------------------------- ++// These must follow all instruction definitions as they use the names ++// defined in the instructions definitions. ++ +diff --git a/hotspot/src/cpu/loongarch/vm/macroAssembler_loongarch.cpp b/hotspot/src/cpu/loongarch/vm/macroAssembler_loongarch.cpp +new file mode 100644 +index 0000000000..0845052666 +--- /dev/null ++++ b/hotspot/src/cpu/loongarch/vm/macroAssembler_loongarch.cpp +@@ -0,0 +1,3894 @@ ++/* ++ * Copyright (c) 1997, 2014, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2017, 2022, Loongson Technology. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ * ++ */ ++ ++#include "precompiled.hpp" ++#include "asm/assembler.hpp" ++#include "asm/assembler.inline.hpp" ++#include "asm/macroAssembler.inline.hpp" ++#include "compiler/disassembler.hpp" ++#include "gc_interface/collectedHeap.inline.hpp" ++#include "interpreter/interpreter.hpp" ++#include "memory/cardTableModRefBS.hpp" ++#include "memory/resourceArea.hpp" ++#include "memory/universe.hpp" ++#include "prims/methodHandles.hpp" ++#include "runtime/biasedLocking.hpp" ++#include "runtime/interfaceSupport.hpp" ++#include "runtime/objectMonitor.hpp" ++#include "runtime/os.hpp" ++#include "runtime/sharedRuntime.hpp" ++#include "runtime/stubRoutines.hpp" ++#include "utilities/macros.hpp" ++#if INCLUDE_ALL_GCS ++#include "gc_implementation/g1/g1CollectedHeap.inline.hpp" ++#include "gc_implementation/g1/g1SATBCardTableModRefBS.hpp" ++#include "gc_implementation/g1/heapRegion.hpp" ++#endif // INCLUDE_ALL_GCS ++ ++#ifdef COMPILER2 ++#include "opto/compile.hpp" ++#endif ++ ++#define A0 RA0 ++#define A1 RA1 ++#define A2 RA2 ++#define A3 RA3 ++#define A4 RA4 ++#define A5 RA5 ++#define A6 RA6 ++#define A7 RA7 ++#define T0 RT0 ++#define T1 RT1 ++#define T2 RT2 ++#define T3 RT3 ++#define T4 RT4 ++#define T5 RT5 ++#define T6 RT6 ++#define T7 RT7 ++#define T8 RT8 ++ ++// Implementation of MacroAssembler ++ ++intptr_t MacroAssembler::i[32] = {0}; ++float MacroAssembler::f[32] = {0.0}; ++ ++void MacroAssembler::print(outputStream *s) { ++ unsigned int k; ++ for(k=0; kprint_cr("i%d = 0x%.16lx", k, i[k]); ++ } ++ s->cr(); ++ ++ for(k=0; kprint_cr("f%d = %f", k, f[k]); ++ } ++ s->cr(); ++} ++ ++int MacroAssembler::i_offset(unsigned int k) { return (intptr_t)&((MacroAssembler*)0)->i[k]; } ++int MacroAssembler::f_offset(unsigned int k) { return (intptr_t)&((MacroAssembler*)0)->f[k]; } ++ ++void MacroAssembler::save_registers(MacroAssembler *masm) { ++#define __ masm-> ++ for(int k=0; k<32; k++) { ++ __ st_w (as_Register(k), A0, i_offset(k)); ++ } ++ ++ for(int k=0; k<32; k++) { ++ __ fst_s (as_FloatRegister(k), A0, f_offset(k)); ++ } ++#undef __ ++} ++ ++void MacroAssembler::restore_registers(MacroAssembler *masm) { ++#define __ masm-> ++ for(int k=0; k<32; k++) { ++ __ ld_w (as_Register(k), A0, i_offset(k)); ++ } ++ ++ for(int k=0; k<32; k++) { ++ __ fld_s (as_FloatRegister(k), A0, f_offset(k)); ++ } ++#undef __ ++} ++ ++ ++void MacroAssembler::pd_patch_instruction(address branch, address target) { ++ jint& stub_inst = *(jint*)branch; ++ jint* pc = (jint*)branch; ++ ++ if (high(stub_inst, 7) == pcaddu18i_op) { ++ // far: ++ // pcaddu18i reg, si20 ++ // jirl r0, reg, si18 ++ ++ assert(high(pc[1], 6) == jirl_op, "Not a branch label patch"); ++ jlong offs = target - branch; ++ CodeBuffer cb(branch, 2 * BytesPerInstWord); ++ MacroAssembler masm(&cb); ++ if (reachable_from_branch_short(offs)) { ++ // convert far to short ++#define __ masm. ++ __ b(target); ++ __ nop(); ++#undef __ ++ } else { ++ masm.patchable_jump_far(R0, offs); ++ } ++ return; ++ } else if (high(stub_inst, 7) == pcaddi_op) { ++ // see MacroAssembler::set_last_Java_frame: ++ // pcaddi reg, si20 ++ ++ jint offs = (target - branch) >> 2; ++ guarantee(is_simm(offs, 20), "Not signed 20-bit offset"); ++ CodeBuffer cb(branch, 1 * BytesPerInstWord); ++ MacroAssembler masm(&cb); ++ masm.pcaddi(as_Register(low(stub_inst, 5)), offs); ++ return; ++ } ++ ++ stub_inst = patched_branch(target - branch, stub_inst, 0); ++} ++ ++bool MacroAssembler::reachable_from_branch_short(jlong offs) { ++ if (ForceUnreachable) { ++ return false; ++ } ++ return is_simm(offs >> 2, 26); ++} ++ ++void MacroAssembler::patchable_jump_far(Register ra, jlong offs) { ++ jint si18, si20; ++ guarantee(is_simm(offs, 38), "Not signed 38-bit offset"); ++ split_simm38(offs, si18, si20); ++ pcaddu18i(T4, si20); ++ jirl(ra, T4, si18); ++} ++ ++void MacroAssembler::patchable_jump(address target, bool force_patchable) { ++ assert(ReservedCodeCacheSize < 4*G, "branch out of range"); ++ assert(CodeCache::find_blob(target) != NULL, ++ "destination of jump not found in code cache"); ++ if (force_patchable || patchable_branches()) { ++ jlong offs = target - pc(); ++ if (reachable_from_branch_short(offs)) { // Short jump ++ b(offset26(target)); ++ nop(); ++ } else { // Far jump ++ patchable_jump_far(R0, offs); ++ } ++ } else { // Real short jump ++ b(offset26(target)); ++ } ++} ++ ++void MacroAssembler::patchable_call(address target, address call_site) { ++ jlong offs = target - (call_site ? call_site : pc()); ++ if (reachable_from_branch_short(offs - BytesPerInstWord)) { // Short call ++ nop(); ++ bl((offs - BytesPerInstWord) >> 2); ++ } else { // Far call ++ patchable_jump_far(RA, offs); ++ } ++} ++ ++// Maybe emit a call via a trampoline. If the code cache is small ++// trampolines won't be emitted. ++ ++address MacroAssembler::trampoline_call(AddressLiteral entry, CodeBuffer *cbuf) { ++ assert(JavaThread::current()->is_Compiler_thread(), "just checking"); ++ assert(entry.rspec().type() == relocInfo::runtime_call_type ++ || entry.rspec().type() == relocInfo::opt_virtual_call_type ++ || entry.rspec().type() == relocInfo::static_call_type ++ || entry.rspec().type() == relocInfo::virtual_call_type, "wrong reloc type"); ++ ++ // We need a trampoline if branches are far. ++ if (far_branches()) { ++ bool in_scratch_emit_size = false; ++#ifdef COMPILER2 ++ // We don't want to emit a trampoline if C2 is generating dummy ++ // code during its branch shortening phase. ++ CompileTask* task = ciEnv::current()->task(); ++ in_scratch_emit_size = ++ (task != NULL && is_c2_compile(task->comp_level()) && ++ Compile::current()->in_scratch_emit_size()); ++#endif ++ if (!in_scratch_emit_size) { ++ address stub = emit_trampoline_stub(offset(), entry.target()); ++ if (stub == NULL) { ++ return NULL; // CodeCache is full ++ } ++ } ++ } ++ ++ if (cbuf) cbuf->set_insts_mark(); ++ relocate(entry.rspec()); ++ if (!far_branches()) { ++ bl(entry.target()); ++ } else { ++ bl(pc()); ++ } ++ // just need to return a non-null address ++ return pc(); ++} ++ ++// Emit a trampoline stub for a call to a target which is too far away. ++// ++// code sequences: ++// ++// call-site: ++// branch-and-link to or ++// ++// Related trampoline stub for this call site in the stub section: ++// load the call target from the constant pool ++// branch (RA still points to the call site above) ++ ++address MacroAssembler::emit_trampoline_stub(int insts_call_instruction_offset, ++ address dest) { ++ // Start the stub ++ address stub = start_a_stub(NativeInstruction::nop_instruction_size ++ + NativeCallTrampolineStub::instruction_size); ++ if (stub == NULL) { ++ return NULL; // CodeBuffer::expand failed ++ } ++ ++ // Create a trampoline stub relocation which relates this trampoline stub ++ // with the call instruction at insts_call_instruction_offset in the ++ // instructions code-section. ++ align(wordSize); ++ relocate(trampoline_stub_Relocation::spec(code()->insts()->start() ++ + insts_call_instruction_offset)); ++ const int stub_start_offset = offset(); ++ ++ // Now, create the trampoline stub's code: ++ // - load the call ++ // - call ++ pcaddi(T4, 0); ++ ld_d(T4, T4, 16); ++ jr(T4); ++ nop(); //align ++ assert(offset() - stub_start_offset == NativeCallTrampolineStub::data_offset, ++ "should be"); ++ emit_int64((int64_t)dest); ++ ++ const address stub_start_addr = addr_at(stub_start_offset); ++ ++ NativeInstruction* ni = nativeInstruction_at(stub_start_addr); ++ assert(ni->is_NativeCallTrampolineStub_at(), "doesn't look like a trampoline"); ++ ++ end_a_stub(); ++ return stub_start_addr; ++} ++ ++void MacroAssembler::beq_far(Register rs, Register rt, address entry) { ++ if (is_simm16((entry - pc()) >> 2)) { // Short jump ++ beq(rs, rt, offset16(entry)); ++ } else { // Far jump ++ Label not_jump; ++ bne(rs, rt, not_jump); ++ b_far(entry); ++ bind(not_jump); ++ } ++} ++ ++void MacroAssembler::beq_far(Register rs, Register rt, Label& L) { ++ if (L.is_bound()) { ++ beq_far(rs, rt, target(L)); ++ } else { ++ Label not_jump; ++ bne(rs, rt, not_jump); ++ b_far(L); ++ bind(not_jump); ++ } ++} ++ ++void MacroAssembler::bne_far(Register rs, Register rt, address entry) { ++ if (is_simm16((entry - pc()) >> 2)) { // Short jump ++ bne(rs, rt, offset16(entry)); ++ } else { // Far jump ++ Label not_jump; ++ beq(rs, rt, not_jump); ++ b_far(entry); ++ bind(not_jump); ++ } ++} ++ ++void MacroAssembler::bne_far(Register rs, Register rt, Label& L) { ++ if (L.is_bound()) { ++ bne_far(rs, rt, target(L)); ++ } else { ++ Label not_jump; ++ beq(rs, rt, not_jump); ++ b_far(L); ++ bind(not_jump); ++ } ++} ++ ++void MacroAssembler::blt_far(Register rs, Register rt, address entry, bool is_signed) { ++ if (is_simm16((entry - pc()) >> 2)) { // Short jump ++ if (is_signed) { ++ blt(rs, rt, offset16(entry)); ++ } else { ++ bltu(rs, rt, offset16(entry)); ++ } ++ } else { // Far jump ++ Label not_jump; ++ if (is_signed) { ++ bge(rs, rt, not_jump); ++ } else { ++ bgeu(rs, rt, not_jump); ++ } ++ b_far(entry); ++ bind(not_jump); ++ } ++} ++ ++void MacroAssembler::blt_far(Register rs, Register rt, Label& L, bool is_signed) { ++ if (L.is_bound()) { ++ blt_far(rs, rt, target(L), is_signed); ++ } else { ++ Label not_jump; ++ if (is_signed) { ++ bge(rs, rt, not_jump); ++ } else { ++ bgeu(rs, rt, not_jump); ++ } ++ b_far(L); ++ bind(not_jump); ++ } ++} ++ ++void MacroAssembler::bge_far(Register rs, Register rt, address entry, bool is_signed) { ++ if (is_simm16((entry - pc()) >> 2)) { // Short jump ++ if (is_signed) { ++ bge(rs, rt, offset16(entry)); ++ } else { ++ bgeu(rs, rt, offset16(entry)); ++ } ++ } else { // Far jump ++ Label not_jump; ++ if (is_signed) { ++ blt(rs, rt, not_jump); ++ } else { ++ bltu(rs, rt, not_jump); ++ } ++ b_far(entry); ++ bind(not_jump); ++ } ++} ++ ++void MacroAssembler::bge_far(Register rs, Register rt, Label& L, bool is_signed) { ++ if (L.is_bound()) { ++ bge_far(rs, rt, target(L), is_signed); ++ } else { ++ Label not_jump; ++ if (is_signed) { ++ blt(rs, rt, not_jump); ++ } else { ++ bltu(rs, rt, not_jump); ++ } ++ b_far(L); ++ bind(not_jump); ++ } ++} ++ ++void MacroAssembler::beq_long(Register rs, Register rt, Label& L) { ++ Label not_taken; ++ bne(rs, rt, not_taken); ++ jmp_far(L); ++ bind(not_taken); ++} ++ ++void MacroAssembler::bne_long(Register rs, Register rt, Label& L) { ++ Label not_taken; ++ beq(rs, rt, not_taken); ++ jmp_far(L); ++ bind(not_taken); ++} ++ ++void MacroAssembler::blt_long(Register rs, Register rt, Label& L, bool is_signed) { ++ Label not_taken; ++ if (is_signed) { ++ bge(rs, rt, not_taken); ++ } else { ++ bgeu(rs, rt, not_taken); ++ } ++ jmp_far(L); ++ bind(not_taken); ++} ++ ++void MacroAssembler::bge_long(Register rs, Register rt, Label& L, bool is_signed) { ++ Label not_taken; ++ if (is_signed) { ++ blt(rs, rt, not_taken); ++ } else { ++ bltu(rs, rt, not_taken); ++ } ++ jmp_far(L); ++ bind(not_taken); ++} ++ ++void MacroAssembler::bc1t_long(Label& L) { ++ Label not_taken; ++ bceqz(FCC0, not_taken); ++ jmp_far(L); ++ bind(not_taken); ++} ++ ++void MacroAssembler::bc1f_long(Label& L) { ++ Label not_taken; ++ bcnez(FCC0, not_taken); ++ jmp_far(L); ++ bind(not_taken); ++} ++ ++void MacroAssembler::b_far(Label& L) { ++ if (L.is_bound()) { ++ b_far(target(L)); ++ } else { ++ L.add_patch_at(code(), locator()); ++ if (ForceUnreachable) { ++ patchable_jump_far(R0, 0); ++ } else { ++ b(0); ++ } ++ } ++} ++ ++void MacroAssembler::b_far(address entry) { ++ jlong offs = entry - pc(); ++ if (reachable_from_branch_short(offs)) { // Short jump ++ b(offset26(entry)); ++ } else { // Far jump ++ patchable_jump_far(R0, offs); ++ } ++} ++ ++void MacroAssembler::ld_ptr(Register rt, Register base, Register offset) { ++ ldx_d(rt, base, offset); ++} ++ ++void MacroAssembler::st_ptr(Register rt, Register base, Register offset) { ++ stx_d(rt, base, offset); ++} ++ ++void MacroAssembler::ld_long(Register rt, Register offset, Register base) { ++ //TODO: LA ++ guarantee(0, "LA not implemented yet"); ++#if 0 ++ add_d(AT, base, offset); ++ ld_long(rt, 0, AT); ++#endif ++} ++ ++void MacroAssembler::st_long(Register rt, Register offset, Register base) { ++ //TODO: LA ++ guarantee(0, "LA not implemented yet"); ++#if 0 ++ add_d(AT, base, offset); ++ st_long(rt, 0, AT); ++#endif ++} ++ ++Address MacroAssembler::as_Address(AddressLiteral adr) { ++ return Address(adr.target(), adr.rspec()); ++} ++ ++Address MacroAssembler::as_Address(ArrayAddress adr) { ++ return Address::make_array(adr); ++} ++ ++// tmp_reg1 and tmp_reg2 should be saved outside of atomic_inc32 (caller saved). ++void MacroAssembler::atomic_inc32(address counter_addr, int inc, Register tmp_reg1, Register tmp_reg2) { ++ li(tmp_reg1, inc); ++ li(tmp_reg2, counter_addr); ++ amadd_w(R0, tmp_reg1, tmp_reg2); ++} ++ ++int MacroAssembler::biased_locking_enter(Register lock_reg, ++ Register obj_reg, ++ Register swap_reg, ++ Register tmp_reg, ++ bool swap_reg_contains_mark, ++ Label& done, ++ Label* slow_case, ++ BiasedLockingCounters* counters) { ++ assert(UseBiasedLocking, "why call this otherwise?"); ++ bool need_tmp_reg = false; ++ if (tmp_reg == noreg) { ++ need_tmp_reg = true; ++ tmp_reg = T4; ++ } ++ assert_different_registers(lock_reg, obj_reg, swap_reg, tmp_reg, AT); ++ assert(markOopDesc::age_shift == markOopDesc::lock_bits + markOopDesc::biased_lock_bits, "biased locking makes assumptions about bit layout"); ++ Address mark_addr (obj_reg, oopDesc::mark_offset_in_bytes()); ++ Address saved_mark_addr(lock_reg, 0); ++ ++ // Biased locking ++ // See whether the lock is currently biased toward our thread and ++ // whether the epoch is still valid ++ // Note that the runtime guarantees sufficient alignment of JavaThread ++ // pointers to allow age to be placed into low bits ++ // First check to see whether biasing is even enabled for this object ++ Label cas_label; ++ int null_check_offset = -1; ++ if (!swap_reg_contains_mark) { ++ null_check_offset = offset(); ++ ld_ptr(swap_reg, mark_addr); ++ } ++ ++ if (need_tmp_reg) { ++ push(tmp_reg); ++ } ++ move(tmp_reg, swap_reg); ++ andi(tmp_reg, tmp_reg, markOopDesc::biased_lock_mask_in_place); ++ addi_d(AT, R0, markOopDesc::biased_lock_pattern); ++ sub_d(AT, AT, tmp_reg); ++ if (need_tmp_reg) { ++ pop(tmp_reg); ++ } ++ ++ bne(AT, R0, cas_label); ++ ++ ++ // The bias pattern is present in the object's header. Need to check ++ // whether the bias owner and the epoch are both still current. ++ // Note that because there is no current thread register on LA we ++ // need to store off the mark word we read out of the object to ++ // avoid reloading it and needing to recheck invariants below. This ++ // store is unfortunate but it makes the overall code shorter and ++ // simpler. ++ st_ptr(swap_reg, saved_mark_addr); ++ if (need_tmp_reg) { ++ push(tmp_reg); ++ } ++ if (swap_reg_contains_mark) { ++ null_check_offset = offset(); ++ } ++ load_prototype_header(tmp_reg, obj_reg); ++ xorr(tmp_reg, tmp_reg, swap_reg); ++ get_thread(swap_reg); ++ xorr(swap_reg, swap_reg, tmp_reg); ++ ++ li(AT, ~((int) markOopDesc::age_mask_in_place)); ++ andr(swap_reg, swap_reg, AT); ++ ++ if (PrintBiasedLockingStatistics) { ++ Label L; ++ bne(swap_reg, R0, L); ++ push(tmp_reg); ++ push(A0); ++ atomic_inc32((address)BiasedLocking::biased_lock_entry_count_addr(), 1, A0, tmp_reg); ++ pop(A0); ++ pop(tmp_reg); ++ bind(L); ++ } ++ if (need_tmp_reg) { ++ pop(tmp_reg); ++ } ++ beq(swap_reg, R0, done); ++ Label try_revoke_bias; ++ Label try_rebias; ++ ++ // At this point we know that the header has the bias pattern and ++ // that we are not the bias owner in the current epoch. We need to ++ // figure out more details about the state of the header in order to ++ // know what operations can be legally performed on the object's ++ // header. ++ ++ // If the low three bits in the xor result aren't clear, that means ++ // the prototype header is no longer biased and we have to revoke ++ // the bias on this object. ++ ++ li(AT, markOopDesc::biased_lock_mask_in_place); ++ andr(AT, swap_reg, AT); ++ bne(AT, R0, try_revoke_bias); ++ // Biasing is still enabled for this data type. See whether the ++ // epoch of the current bias is still valid, meaning that the epoch ++ // bits of the mark word are equal to the epoch bits of the ++ // prototype header. (Note that the prototype header's epoch bits ++ // only change at a safepoint.) If not, attempt to rebias the object ++ // toward the current thread. Note that we must be absolutely sure ++ // that the current epoch is invalid in order to do this because ++ // otherwise the manipulations it performs on the mark word are ++ // illegal. ++ ++ li(AT, markOopDesc::epoch_mask_in_place); ++ andr(AT,swap_reg, AT); ++ bne(AT, R0, try_rebias); ++ // The epoch of the current bias is still valid but we know nothing ++ // about the owner; it might be set or it might be clear. Try to ++ // acquire the bias of the object using an atomic operation. If this ++ // fails we will go in to the runtime to revoke the object's bias. ++ // Note that we first construct the presumed unbiased header so we ++ // don't accidentally blow away another thread's valid bias. ++ ++ ld_ptr(swap_reg, saved_mark_addr); ++ ++ li(AT, markOopDesc::biased_lock_mask_in_place | markOopDesc::age_mask_in_place | markOopDesc::epoch_mask_in_place); ++ andr(swap_reg, swap_reg, AT); ++ ++ if (need_tmp_reg) { ++ push(tmp_reg); ++ } ++ get_thread(tmp_reg); ++ orr(tmp_reg, tmp_reg, swap_reg); ++ cmpxchg(Address(obj_reg, 0), swap_reg, tmp_reg, AT, false, false); ++ if (need_tmp_reg) { ++ pop(tmp_reg); ++ } ++ // If the biasing toward our thread failed, this means that ++ // another thread succeeded in biasing it toward itself and we ++ // need to revoke that bias. The revocation will occur in the ++ // interpreter runtime in the slow case. ++ if (PrintBiasedLockingStatistics) { ++ Label L; ++ bne(AT, R0, L); ++ push(tmp_reg); ++ push(A0); ++ atomic_inc32((address)BiasedLocking::anonymously_biased_lock_entry_count_addr(), 1, A0, tmp_reg); ++ pop(A0); ++ pop(tmp_reg); ++ bind(L); ++ } ++ if (slow_case != NULL) { ++ beq_far(AT, R0, *slow_case); ++ } ++ b(done); ++ ++ bind(try_rebias); ++ // At this point we know the epoch has expired, meaning that the ++ // current "bias owner", if any, is actually invalid. Under these ++ // circumstances _only_, we are allowed to use the current header's ++ // value as the comparison value when doing the cas to acquire the ++ // bias in the current epoch. In other words, we allow transfer of ++ // the bias from one thread to another directly in this situation. ++ // ++ // FIXME: due to a lack of registers we currently blow away the age ++ // bits in this situation. Should attempt to preserve them. ++ if (need_tmp_reg) { ++ push(tmp_reg); ++ } ++ load_prototype_header(tmp_reg, obj_reg); ++ get_thread(swap_reg); ++ orr(tmp_reg, tmp_reg, swap_reg); ++ ld_ptr(swap_reg, saved_mark_addr); ++ ++ cmpxchg(Address(obj_reg, 0), swap_reg, tmp_reg, AT, false, false); ++ if (need_tmp_reg) { ++ pop(tmp_reg); ++ } ++ // If the biasing toward our thread failed, then another thread ++ // succeeded in biasing it toward itself and we need to revoke that ++ // bias. The revocation will occur in the runtime in the slow case. ++ if (PrintBiasedLockingStatistics) { ++ Label L; ++ bne(AT, R0, L); ++ push(AT); ++ push(tmp_reg); ++ atomic_inc32((address)BiasedLocking::rebiased_lock_entry_count_addr(), 1, AT, tmp_reg); ++ pop(tmp_reg); ++ pop(AT); ++ bind(L); ++ } ++ if (slow_case != NULL) { ++ beq_far(AT, R0, *slow_case); ++ } ++ ++ b(done); ++ bind(try_revoke_bias); ++ // The prototype mark in the klass doesn't have the bias bit set any ++ // more, indicating that objects of this data type are not supposed ++ // to be biased any more. We are going to try to reset the mark of ++ // this object to the prototype value and fall through to the ++ // CAS-based locking scheme. Note that if our CAS fails, it means ++ // that another thread raced us for the privilege of revoking the ++ // bias of this particular object, so it's okay to continue in the ++ // normal locking code. ++ // ++ // FIXME: due to a lack of registers we currently blow away the age ++ // bits in this situation. Should attempt to preserve them. ++ ld_ptr(swap_reg, saved_mark_addr); ++ ++ if (need_tmp_reg) { ++ push(tmp_reg); ++ } ++ load_prototype_header(tmp_reg, obj_reg); ++ cmpxchg(Address(obj_reg, 0), swap_reg, tmp_reg, AT, false, false); ++ if (need_tmp_reg) { ++ pop(tmp_reg); ++ } ++ // Fall through to the normal CAS-based lock, because no matter what ++ // the result of the above CAS, some thread must have succeeded in ++ // removing the bias bit from the object's header. ++ if (PrintBiasedLockingStatistics) { ++ Label L; ++ bne(AT, R0, L); ++ push(AT); ++ push(tmp_reg); ++ atomic_inc32((address)BiasedLocking::revoked_lock_entry_count_addr(), 1, AT, tmp_reg); ++ pop(tmp_reg); ++ pop(AT); ++ bind(L); ++ } ++ ++ bind(cas_label); ++ return null_check_offset; ++} ++ ++void MacroAssembler::biased_locking_exit(Register obj_reg, Register temp_reg, Label& done) { ++ assert(UseBiasedLocking, "why call this otherwise?"); ++ ++ // Check for biased locking unlock case, which is a no-op ++ // Note: we do not have to check the thread ID for two reasons. ++ // First, the interpreter checks for IllegalMonitorStateException at ++ // a higher level. Second, if the bias was revoked while we held the ++ // lock, the object could not be rebiased toward another thread, so ++ // the bias bit would be clear. ++ ld_d(temp_reg, Address(obj_reg, oopDesc::mark_offset_in_bytes())); ++ andi(temp_reg, temp_reg, markOopDesc::biased_lock_mask_in_place); ++ addi_d(AT, R0, markOopDesc::biased_lock_pattern); ++ ++ beq(AT, temp_reg, done); ++} ++ ++// the stack pointer adjustment is needed. see InterpreterMacroAssembler::super_call_VM_leaf ++// this method will handle the stack problem, you need not to preserve the stack space for the argument now ++void MacroAssembler::call_VM_leaf_base(address entry_point, int number_of_arguments) { ++ Label L, E; ++ ++ assert(number_of_arguments <= 4, "just check"); ++ ++ andi(AT, SP, 0xf); ++ beq(AT, R0, L); ++ addi_d(SP, SP, -8); ++ call(entry_point, relocInfo::runtime_call_type); ++ addi_d(SP, SP, 8); ++ b(E); ++ ++ bind(L); ++ call(entry_point, relocInfo::runtime_call_type); ++ bind(E); ++} ++ ++ ++void MacroAssembler::jmp(address entry) { ++ jlong offs = entry - pc(); ++ if (reachable_from_branch_short(offs)) { // Short jump ++ b(offset26(entry)); ++ } else { // Far jump ++ patchable_jump_far(R0, offs); ++ } ++} ++ ++void MacroAssembler::jmp(address entry, relocInfo::relocType rtype) { ++ switch (rtype) { ++ case relocInfo::none: ++ jmp(entry); ++ break; ++ default: ++ { ++ InstructionMark im(this); ++ relocate(rtype); ++ patchable_jump(entry); ++ } ++ break; ++ } ++} ++ ++void MacroAssembler::jmp_far(Label& L) { ++ if (L.is_bound()) { ++ assert(target(L) != NULL, "jmp most probably wrong"); ++ patchable_jump(target(L), true /* force patchable */); ++ } else { ++ L.add_patch_at(code(), locator()); ++ patchable_jump_far(R0, 0); ++ } ++} ++ ++void MacroAssembler::mov_metadata(Address dst, Metadata* obj) { ++ int oop_index; ++ if (obj) { ++ oop_index = oop_recorder()->find_index(obj); ++ } else { ++ oop_index = oop_recorder()->allocate_metadata_index(obj); ++ } ++ relocate(metadata_Relocation::spec(oop_index)); ++ patchable_li52(AT, (long)obj); ++ st_d(AT, dst); ++} ++ ++void MacroAssembler::mov_metadata(Register dst, Metadata* obj) { ++ int oop_index; ++ if (obj) { ++ oop_index = oop_recorder()->find_index(obj); ++ } else { ++ oop_index = oop_recorder()->allocate_metadata_index(obj); ++ } ++ relocate(metadata_Relocation::spec(oop_index)); ++ patchable_li52(dst, (long)obj); ++} ++ ++void MacroAssembler::call(address entry) { ++ jlong offs = entry - pc(); ++ if (reachable_from_branch_short(offs)) { // Short call (pc-rel) ++ bl(offset26(entry)); ++ } else if (is_simm(offs, 38)) { // Far call (pc-rel) ++ patchable_jump_far(RA, offs); ++ } else { // Long call (absolute) ++ call_long(entry); ++ } ++} ++ ++void MacroAssembler::call(address entry, relocInfo::relocType rtype) { ++ switch (rtype) { ++ case relocInfo::none: ++ call(entry); ++ break; ++ case relocInfo::runtime_call_type: ++ if (!is_simm(entry - pc(), 38)) { ++ call_long(entry); ++ break; ++ } ++ // fallthrough ++ default: ++ { ++ InstructionMark im(this); ++ relocate(rtype); ++ patchable_call(entry); ++ } ++ break; ++ } ++} ++ ++void MacroAssembler::call(address entry, RelocationHolder& rh) { ++ switch (rh.type()) { ++ case relocInfo::none: ++ call(entry); ++ break; ++ case relocInfo::runtime_call_type: ++ if (!is_simm(entry - pc(), 38)) { ++ call_long(entry); ++ break; ++ } ++ // fallthrough ++ default: ++ { ++ InstructionMark im(this); ++ relocate(rh); ++ patchable_call(entry); ++ } ++ break; ++ } ++} ++ ++void MacroAssembler::call_long(address entry) { ++ jlong value = (jlong)entry; ++ lu12i_w(T4, split_low20(value >> 12)); ++ lu32i_d(T4, split_low20(value >> 32)); ++ jirl(RA, T4, split_low12(value)); ++} ++ ++address MacroAssembler::ic_call(address entry) { ++ RelocationHolder rh = virtual_call_Relocation::spec(pc()); ++ patchable_li52(IC_Klass, (long)Universe::non_oop_word()); ++ assert(entry != NULL, "call most probably wrong"); ++ InstructionMark im(this); ++ return trampoline_call(AddressLiteral(entry, rh)); ++} ++ ++void MacroAssembler::c2bool(Register r) { ++ sltu(r, R0, r); ++} ++ ++#ifndef PRODUCT ++extern "C" void findpc(intptr_t x); ++#endif ++ ++void MacroAssembler::debug(char* msg/*, RegistersForDebugging* regs*/) { ++ if ( ShowMessageBoxOnError ) { ++ JavaThreadState saved_state = JavaThread::current()->thread_state(); ++ JavaThread::current()->set_thread_state(_thread_in_vm); ++ { ++ // In order to get locks work, we need to fake a in_VM state ++ ttyLocker ttyl; ++ ::tty->print_cr("EXECUTION STOPPED: %s\n", msg); ++ if (CountBytecodes || TraceBytecodes || StopInterpreterAt) { ++ BytecodeCounter::print(); ++ } ++ ++ } ++ ThreadStateTransition::transition(JavaThread::current(), _thread_in_vm, saved_state); ++ } ++ else ++ ::tty->print_cr("=============== DEBUG MESSAGE: %s ================\n", msg); ++} ++ ++ ++void MacroAssembler::stop(const char* msg) { ++ li(A0, (long)msg); ++ call(CAST_FROM_FN_PTR(address, MacroAssembler::debug), relocInfo::runtime_call_type); ++ brk(17); ++} ++ ++void MacroAssembler::warn(const char* msg) { ++ pushad(); ++ li(A0, (long)msg); ++ push(S2); ++ li(AT, -(StackAlignmentInBytes)); ++ move(S2, SP); // use S2 as a sender SP holder ++ andr(SP, SP, AT); // align stack as required by ABI ++ call(CAST_FROM_FN_PTR(address, MacroAssembler::debug), relocInfo::runtime_call_type); ++ move(SP, S2); // use S2 as a sender SP holder ++ pop(S2); ++ popad(); ++} ++ ++void MacroAssembler::increment(Register reg, int imm) { ++ if (!imm) return; ++ if (is_simm(imm, 12)) { ++ addi_d(reg, reg, imm); ++ } else { ++ li(AT, imm); ++ add_d(reg, reg, AT); ++ } ++} ++ ++void MacroAssembler::decrement(Register reg, int imm) { ++ increment(reg, -imm); ++} ++ ++void MacroAssembler::increment(Address addr, int imm) { ++ if (!imm) return; ++ assert(is_simm(imm, 12), "must be"); ++ ld_ptr(AT, addr); ++ addi_d(AT, AT, imm); ++ st_ptr(AT, addr); ++} ++ ++void MacroAssembler::decrement(Address addr, int imm) { ++ increment(addr, -imm); ++} ++ ++void MacroAssembler::call_VM(Register oop_result, ++ address entry_point, ++ bool check_exceptions) { ++ call_VM_helper(oop_result, entry_point, 0, check_exceptions); ++} ++ ++void MacroAssembler::call_VM(Register oop_result, ++ address entry_point, ++ Register arg_1, ++ bool check_exceptions) { ++ if (arg_1!=A1) move(A1, arg_1); ++ call_VM_helper(oop_result, entry_point, 1, check_exceptions); ++} ++ ++void MacroAssembler::call_VM(Register oop_result, ++ address entry_point, ++ Register arg_1, ++ Register arg_2, ++ bool check_exceptions) { ++ if (arg_1!=A1) move(A1, arg_1); ++ if (arg_2!=A2) move(A2, arg_2); ++ assert(arg_2 != A1, "smashed argument"); ++ call_VM_helper(oop_result, entry_point, 2, check_exceptions); ++} ++ ++void MacroAssembler::call_VM(Register oop_result, ++ address entry_point, ++ Register arg_1, ++ Register arg_2, ++ Register arg_3, ++ bool check_exceptions) { ++ if (arg_1!=A1) move(A1, arg_1); ++ if (arg_2!=A2) move(A2, arg_2); assert(arg_2 != A1, "smashed argument"); ++ if (arg_3!=A3) move(A3, arg_3); assert(arg_3 != A1 && arg_3 != A2, "smashed argument"); ++ call_VM_helper(oop_result, entry_point, 3, check_exceptions); ++} ++ ++void MacroAssembler::call_VM(Register oop_result, ++ Register last_java_sp, ++ address entry_point, ++ int number_of_arguments, ++ bool check_exceptions) { ++ call_VM_base(oop_result, NOREG, last_java_sp, entry_point, number_of_arguments, check_exceptions); ++} ++ ++void MacroAssembler::call_VM(Register oop_result, ++ Register last_java_sp, ++ address entry_point, ++ Register arg_1, ++ bool check_exceptions) { ++ if (arg_1 != A1) move(A1, arg_1); ++ call_VM(oop_result, last_java_sp, entry_point, 1, check_exceptions); ++} ++ ++void MacroAssembler::call_VM(Register oop_result, ++ Register last_java_sp, ++ address entry_point, ++ Register arg_1, ++ Register arg_2, ++ bool check_exceptions) { ++ if (arg_1 != A1) move(A1, arg_1); ++ if (arg_2 != A2) move(A2, arg_2); assert(arg_2 != A1, "smashed argument"); ++ call_VM(oop_result, last_java_sp, entry_point, 2, check_exceptions); ++} ++ ++void MacroAssembler::call_VM(Register oop_result, ++ Register last_java_sp, ++ address entry_point, ++ Register arg_1, ++ Register arg_2, ++ Register arg_3, ++ bool check_exceptions) { ++ if (arg_1 != A1) move(A1, arg_1); ++ if (arg_2 != A2) move(A2, arg_2); assert(arg_2 != A1, "smashed argument"); ++ if (arg_3 != A3) move(A3, arg_3); assert(arg_3 != A1 && arg_3 != A2, "smashed argument"); ++ call_VM(oop_result, last_java_sp, entry_point, 3, check_exceptions); ++} ++ ++void MacroAssembler::call_VM_base(Register oop_result, ++ Register java_thread, ++ Register last_java_sp, ++ address entry_point, ++ int number_of_arguments, ++ bool check_exceptions) { ++ // determine java_thread register ++ if (!java_thread->is_valid()) { ++#ifndef OPT_THREAD ++ java_thread = T2; ++ get_thread(java_thread); ++#else ++ java_thread = TREG; ++#endif ++ } ++ // determine last_java_sp register ++ if (!last_java_sp->is_valid()) { ++ last_java_sp = SP; ++ } ++ // debugging support ++ assert(number_of_arguments >= 0 , "cannot have negative number of arguments"); ++ assert(number_of_arguments <= 4 , "cannot have negative number of arguments"); ++ assert(java_thread != oop_result , "cannot use the same register for java_thread & oop_result"); ++ assert(java_thread != last_java_sp, "cannot use the same register for java_thread & last_java_sp"); ++ ++ assert(last_java_sp != FP, "this code doesn't work for last_java_sp == fp, which currently can't portably work anyway since C2 doesn't save fp"); ++ ++ // set last Java frame before call ++ Label before_call; ++ bind(before_call); ++ set_last_Java_frame(java_thread, last_java_sp, FP, before_call); ++ ++ // do the call ++ move(A0, java_thread); ++ call(entry_point, relocInfo::runtime_call_type); ++ ++ // restore the thread (cannot use the pushed argument since arguments ++ // may be overwritten by C code generated by an optimizing compiler); ++ // however can use the register value directly if it is callee saved. ++#ifndef OPT_THREAD ++ get_thread(java_thread); ++#else ++#ifdef ASSERT ++ { ++ Label L; ++ get_thread(AT); ++ beq(java_thread, AT, L); ++ stop("MacroAssembler::call_VM_base: TREG not callee saved?"); ++ bind(L); ++ } ++#endif ++#endif ++ ++ // discard thread and arguments ++ ld_ptr(SP, java_thread, in_bytes(JavaThread::last_Java_sp_offset())); ++ // reset last Java frame ++ reset_last_Java_frame(java_thread, false); ++ ++ check_and_handle_popframe(java_thread); ++ check_and_handle_earlyret(java_thread); ++ if (check_exceptions) { ++ // check for pending exceptions (java_thread is set upon return) ++ Label L; ++ ld_d(AT, java_thread, in_bytes(Thread::pending_exception_offset())); ++ beq(AT, R0, L); ++ li(AT, target(before_call)); ++ push(AT); ++ jmp(StubRoutines::forward_exception_entry(), relocInfo::runtime_call_type); ++ bind(L); ++ } ++ ++ // get oop result if there is one and reset the value in the thread ++ if (oop_result->is_valid()) { ++ ld_d(oop_result, java_thread, in_bytes(JavaThread::vm_result_offset())); ++ st_d(R0, java_thread, in_bytes(JavaThread::vm_result_offset())); ++ verify_oop(oop_result); ++ } ++} ++ ++void MacroAssembler::call_VM_helper(Register oop_result, address entry_point, int number_of_arguments, bool check_exceptions) { ++ move(V0, SP); ++ //we also reserve space for java_thread here ++ li(AT, -(StackAlignmentInBytes)); ++ andr(SP, SP, AT); ++ call_VM_base(oop_result, NOREG, V0, entry_point, number_of_arguments, check_exceptions); ++} ++ ++void MacroAssembler::call_VM_leaf(address entry_point, int number_of_arguments) { ++ call_VM_leaf_base(entry_point, number_of_arguments); ++} ++ ++void MacroAssembler::call_VM_leaf(address entry_point, Register arg_0) { ++ if (arg_0 != A0) move(A0, arg_0); ++ call_VM_leaf(entry_point, 1); ++} ++ ++void MacroAssembler::call_VM_leaf(address entry_point, Register arg_0, Register arg_1) { ++ if (arg_0 != A0) move(A0, arg_0); ++ if (arg_1 != A1) move(A1, arg_1); assert(arg_1 != A0, "smashed argument"); ++ call_VM_leaf(entry_point, 2); ++} ++ ++void MacroAssembler::call_VM_leaf(address entry_point, Register arg_0, Register arg_1, Register arg_2) { ++ if (arg_0 != A0) move(A0, arg_0); ++ if (arg_1 != A1) move(A1, arg_1); assert(arg_1 != A0, "smashed argument"); ++ if (arg_2 != A2) move(A2, arg_2); assert(arg_2 != A0 && arg_2 != A1, "smashed argument"); ++ call_VM_leaf(entry_point, 3); ++} ++ ++void MacroAssembler::super_call_VM_leaf(address entry_point) { ++ MacroAssembler::call_VM_leaf_base(entry_point, 0); ++} ++ ++void MacroAssembler::super_call_VM_leaf(address entry_point, ++ Register arg_1) { ++ if (arg_1 != A0) move(A0, arg_1); ++ MacroAssembler::call_VM_leaf_base(entry_point, 1); ++} ++ ++void MacroAssembler::super_call_VM_leaf(address entry_point, ++ Register arg_1, ++ Register arg_2) { ++ if (arg_1 != A0) move(A0, arg_1); ++ if (arg_2 != A1) move(A1, arg_2); assert(arg_2 != A0, "smashed argument"); ++ MacroAssembler::call_VM_leaf_base(entry_point, 2); ++} ++ ++void MacroAssembler::super_call_VM_leaf(address entry_point, ++ Register arg_1, ++ Register arg_2, ++ Register arg_3) { ++ if (arg_1 != A0) move(A0, arg_1); ++ if (arg_2 != A1) move(A1, arg_2); assert(arg_2 != A0, "smashed argument"); ++ if (arg_3 != A2) move(A2, arg_3); assert(arg_3 != A0 && arg_3 != A1, "smashed argument"); ++ MacroAssembler::call_VM_leaf_base(entry_point, 3); ++} ++ ++void MacroAssembler::check_and_handle_earlyret(Register java_thread) { ++} ++ ++void MacroAssembler::check_and_handle_popframe(Register java_thread) { ++} ++ ++void MacroAssembler::null_check(Register reg, int offset) { ++ if (needs_explicit_null_check(offset)) { ++ // provoke OS NULL exception if reg = NULL by ++ // accessing M[reg] w/o changing any (non-CC) registers ++ // NOTE: cmpl is plenty here to provoke a segv ++ ld_w(AT, reg, 0); ++ } else { ++ // nothing to do, (later) access of M[reg + offset] ++ // will provoke OS NULL exception if reg = NULL ++ } ++} ++ ++void MacroAssembler::enter() { ++ push2(RA, FP); ++ move(FP, SP); ++} ++ ++void MacroAssembler::leave() { ++ move(SP, FP); ++ pop2(RA, FP); ++} ++ ++void MacroAssembler::build_frame(int framesize) { ++ assert(framesize >= 2 * wordSize, "framesize must include space for FP/RA"); ++ assert(framesize % (2 * wordSize) == 0, "must preserve 2 * wordSize alignment"); ++ if (Assembler::is_simm(-framesize, 12)) { ++ addi_d(SP, SP, -framesize); ++ st_ptr(FP, Address(SP, framesize - 2 * wordSize)); ++ st_ptr(RA, Address(SP, framesize - 1 * wordSize)); ++ if (PreserveFramePointer) ++ addi_d(FP, SP, framesize - 2 * wordSize); ++ } else { ++ addi_d(SP, SP, -2 * wordSize); ++ st_ptr(FP, Address(SP, 0 * wordSize)); ++ st_ptr(RA, Address(SP, 1 * wordSize)); ++ if (PreserveFramePointer) ++ move(FP, SP); ++ li(SCR1, framesize - 2 * wordSize); ++ sub_d(SP, SP, SCR1); ++ } ++} ++ ++void MacroAssembler::remove_frame(int framesize) { ++ assert(framesize >= 2 * wordSize, "framesize must include space for FP/RA"); ++ assert(framesize % (2*wordSize) == 0, "must preserve 2*wordSize alignment"); ++ if (Assembler::is_simm(framesize, 12)) { ++ ld_ptr(FP, Address(SP, framesize - 2 * wordSize)); ++ ld_ptr(RA, Address(SP, framesize - 1 * wordSize)); ++ addi_d(SP, SP, framesize); ++ } else { ++ li(SCR1, framesize - 2 * wordSize); ++ add_d(SP, SP, SCR1); ++ ld_ptr(FP, Address(SP, 0 * wordSize)); ++ ld_ptr(RA, Address(SP, 1 * wordSize)); ++ addi_d(SP, SP, 2 * wordSize); ++ } ++} ++ ++void MacroAssembler::reset_last_Java_frame(Register java_thread, bool clear_fp) { ++ // determine java_thread register ++ if (!java_thread->is_valid()) { ++#ifndef OPT_THREAD ++ java_thread = T1; ++ get_thread(java_thread); ++#else ++ java_thread = TREG; ++#endif ++ } ++ // we must set sp to zero to clear frame ++ st_ptr(R0, java_thread, in_bytes(JavaThread::last_Java_sp_offset())); ++ // must clear fp, so that compiled frames are not confused; it is possible ++ // that we need it only for debugging ++ if(clear_fp) { ++ st_ptr(R0, java_thread, in_bytes(JavaThread::last_Java_fp_offset())); ++ } ++ ++ // Always clear the pc because it could have been set by make_walkable() ++ st_ptr(R0, java_thread, in_bytes(JavaThread::last_Java_pc_offset())); ++} ++ ++void MacroAssembler::reset_last_Java_frame(bool clear_fp) { ++ Register thread = TREG; ++#ifndef OPT_THREAD ++ get_thread(thread); ++#endif ++ // we must set sp to zero to clear frame ++ st_d(R0, thread, in_bytes(JavaThread::last_Java_sp_offset())); ++ // must clear fp, so that compiled frames are not confused; it is ++ // possible that we need it only for debugging ++ if (clear_fp) { ++ st_d(R0, thread, in_bytes(JavaThread::last_Java_fp_offset())); ++ } ++ ++ // Always clear the pc because it could have been set by make_walkable() ++ st_d(R0, thread, in_bytes(JavaThread::last_Java_pc_offset())); ++} ++ ++// Write serialization page so VM thread can do a pseudo remote membar. ++// We use the current thread pointer to calculate a thread specific ++// offset to write to within the page. This minimizes bus traffic ++// due to cache line collision. ++void MacroAssembler::serialize_memory(Register thread, Register tmp) { ++ assert_different_registers(AT, tmp); ++ juint sps = os::get_serialize_page_shift_count(); ++ juint lsb = sps + 2; ++ juint msb = sps + log2_uint(os::vm_page_size()) - 1; ++ bstrpick_w(AT, thread, msb, lsb); ++ li(tmp, os::get_memory_serialize_page()); ++ alsl_d(tmp, AT, tmp, Address::times_2 - 1); ++ st_w(R0, tmp, 0); ++} ++ ++// Calls to C land ++// ++// When entering C land, the fp, & sp of the last Java frame have to be recorded ++// in the (thread-local) JavaThread object. When leaving C land, the last Java fp ++// has to be reset to 0. This is required to allow proper stack traversal. ++void MacroAssembler::set_last_Java_frame(Register java_thread, ++ Register last_java_sp, ++ Register last_java_fp, ++ Label& last_java_pc) { ++ // determine java_thread register ++ if (!java_thread->is_valid()) { ++#ifndef OPT_THREAD ++ java_thread = T2; ++ get_thread(java_thread); ++#else ++ java_thread = TREG; ++#endif ++ } ++ ++ // determine last_java_sp register ++ if (!last_java_sp->is_valid()) { ++ last_java_sp = SP; ++ } ++ ++ // last_java_fp is optional ++ if (last_java_fp->is_valid()) { ++ st_ptr(last_java_fp, java_thread, in_bytes(JavaThread::last_Java_fp_offset())); ++ } ++ ++ // last_java_pc ++ lipc(AT, last_java_pc); ++ st_ptr(AT, java_thread, in_bytes(JavaThread::frame_anchor_offset() + ++ JavaFrameAnchor::last_Java_pc_offset())); ++ ++ st_ptr(last_java_sp, java_thread, in_bytes(JavaThread::last_Java_sp_offset())); ++} ++ ++void MacroAssembler::set_last_Java_frame(Register last_java_sp, ++ Register last_java_fp, ++ Label& last_java_pc) { ++ set_last_Java_frame(NOREG, last_java_sp, last_java_fp, last_java_pc); ++} ++ ++////////////////////////////////////////////////////////////////////////////////// ++#if INCLUDE_ALL_GCS ++ ++void MacroAssembler::g1_write_barrier_pre(Register obj, ++ Register pre_val, ++ Register thread, ++ Register tmp, ++ bool tosca_live, ++ bool expand_call) { ++ ++ // If expand_call is true then we expand the call_VM_leaf macro ++ // directly to skip generating the check by ++ // InterpreterMacroAssembler::call_VM_leaf_base that checks _last_sp. ++ ++ assert(thread == TREG, "must be"); ++ ++ Label done; ++ Label runtime; ++ ++ assert(pre_val != noreg, "check this code"); ++ ++ if (obj != noreg) { ++ assert_different_registers(obj, pre_val, tmp); ++ assert(pre_val != V0, "check this code"); ++ } ++ ++ Address in_progress(thread, in_bytes(JavaThread::satb_mark_queue_offset() + ++ PtrQueue::byte_offset_of_active())); ++ Address index(thread, in_bytes(JavaThread::satb_mark_queue_offset() + ++ PtrQueue::byte_offset_of_index())); ++ Address buffer(thread, in_bytes(JavaThread::satb_mark_queue_offset() + ++ PtrQueue::byte_offset_of_buf())); ++ ++ // Is marking active? ++ if (in_bytes(PtrQueue::byte_width_of_active()) == 4) { ++ ld_w(AT, in_progress); ++ } else { ++ assert(in_bytes(PtrQueue::byte_width_of_active()) == 1, "Assumption"); ++ ld_b(AT, in_progress); ++ } ++ beqz(AT, done); ++ ++ // Do we need to load the previous value? ++ if (obj != noreg) { ++ load_heap_oop(pre_val, Address(obj, 0)); ++ } ++ ++ // Is the previous value null? ++ beqz(pre_val, done); ++ ++ // Can we store original value in the thread's buffer? ++ // Is index == 0? ++ // (The index field is typed as size_t.) ++ ++ ld_d(tmp, index); ++ beqz(tmp, runtime); ++ ++ addi_d(tmp, tmp, -1 * wordSize); ++ st_d(tmp, index); ++ ld_d(AT, buffer); ++ ++ // Record the previous value ++ stx_d(pre_val, tmp, AT); ++ b(done); ++ ++ bind(runtime); ++ // save the live input values ++ if (tosca_live) push(V0); ++ ++ if (obj != noreg && obj != V0) push(obj); ++ ++ if (pre_val != V0) push(pre_val); ++ ++ // Calling the runtime using the regular call_VM_leaf mechanism generates ++ // code (generated by InterpreterMacroAssember::call_VM_leaf_base) ++ // that checks that the *(fp+frame::interpreter_frame_last_sp) == NULL. ++ // ++ // If we care generating the pre-barrier without a frame (e.g. in the ++ // intrinsified Reference.get() routine) then fp might be pointing to ++ // the caller frame and so this check will most likely fail at runtime. ++ // ++ // Expanding the call directly bypasses the generation of the check. ++ // So when we do not have have a full interpreter frame on the stack ++ // expand_call should be passed true. ++ ++ if (expand_call) { ++ assert(pre_val != A1, "smashed arg"); ++ if (thread != A1) move(A1, thread); ++ if (pre_val != A0) move(A0, pre_val); ++ MacroAssembler::call_VM_leaf_base(CAST_FROM_FN_PTR(address, SharedRuntime::g1_wb_pre), 2); ++ } else { ++ call_VM_leaf(CAST_FROM_FN_PTR(address, SharedRuntime::g1_wb_pre), pre_val, thread); ++ } ++ ++ // save the live input values ++ if (pre_val != V0) ++ pop(pre_val); ++ ++ if (obj != noreg && obj != V0) ++ pop(obj); ++ ++ if(tosca_live) pop(V0); ++ ++ bind(done); ++} ++ ++void MacroAssembler::g1_write_barrier_post(Register store_addr, ++ Register new_val, ++ Register thread, ++ Register tmp, ++ Register tmp2) { ++ assert(tmp != AT, "must be"); ++ assert(tmp2 != AT, "must be"); ++ assert(thread == TREG, "must be"); ++ ++ Address queue_index(thread, in_bytes(JavaThread::dirty_card_queue_offset() + ++ PtrQueue::byte_offset_of_index())); ++ Address buffer(thread, in_bytes(JavaThread::dirty_card_queue_offset() + ++ PtrQueue::byte_offset_of_buf())); ++ ++ BarrierSet* bs = Universe::heap()->barrier_set(); ++ CardTableModRefBS* ct = (CardTableModRefBS*)bs; ++ assert(sizeof(*ct->byte_map_base) == sizeof(jbyte), "adjust this code"); ++ Label done; ++ Label runtime; ++ ++ // Does store cross heap regions? ++ xorr(AT, store_addr, new_val); ++ srli_d(AT, AT, HeapRegion::LogOfHRGrainBytes); ++ beqz(AT, done); ++ ++ ++ // crosses regions, storing NULL? ++ beq(new_val, R0, done); ++ ++ // storing region crossing non-NULL, is card already dirty? ++ const Register card_addr = tmp; ++ const Register cardtable = tmp2; ++ ++ move(card_addr, store_addr); ++ srli_d(card_addr, card_addr, CardTableModRefBS::card_shift); ++ // Do not use ExternalAddress to load 'byte_map_base', since 'byte_map_base' is NOT ++ // a valid address and therefore is not properly handled by the relocation code. ++ li(cardtable, (intptr_t)ct->byte_map_base); ++ add_d(card_addr, card_addr, cardtable); ++ ++ ld_b(AT, card_addr, 0); ++ addi_d(AT, AT, -1 * (int)G1SATBCardTableModRefBS::g1_young_card_val()); ++ beqz(AT, done); ++ ++ membar(StoreLoad); ++ ld_b(AT, card_addr, 0); ++ addi_d(AT, AT, -1 * (int)(int)CardTableModRefBS::dirty_card_val()); ++ beqz(AT, done); ++ ++ ++ // storing a region crossing, non-NULL oop, card is clean. ++ // dirty card and log. ++ li(AT, (int)CardTableModRefBS::dirty_card_val()); ++ st_b(AT, card_addr, 0); ++ ++ ld_w(AT, queue_index); ++ beqz(AT, runtime); ++ addi_d(AT, AT, -1 * wordSize); ++ st_w(AT, queue_index); ++ ld_d(tmp2, buffer); ++ ld_d(AT, queue_index); ++ stx_d(card_addr, tmp2, AT); ++ b(done); ++ ++ bind(runtime); ++ // save the live input values ++ push(store_addr); ++ push(new_val); ++ call_VM_leaf(CAST_FROM_FN_PTR(address, SharedRuntime::g1_wb_post), card_addr, TREG); ++ pop(new_val); ++ pop(store_addr); ++ ++ bind(done); ++} ++ ++#endif // INCLUDE_ALL_GCS ++////////////////////////////////////////////////////////////////////////////////// ++ ++ ++void MacroAssembler::store_check(Register obj) { ++ // Does a store check for the oop in register obj. The content of ++ // register obj is destroyed afterwards. ++ store_check_part_1(obj); ++ store_check_part_2(obj); ++} ++ ++void MacroAssembler::store_check(Register obj, Address dst) { ++ store_check(obj); ++} ++ ++ ++// split the store check operation so that other instructions can be scheduled inbetween ++void MacroAssembler::store_check_part_1(Register obj) { ++ BarrierSet* bs = Universe::heap()->barrier_set(); ++ assert(bs->kind() == BarrierSet::CardTableModRef, "Wrong barrier set kind"); ++ srli_d(obj, obj, CardTableModRefBS::card_shift); ++} ++ ++void MacroAssembler::store_check_part_2(Register obj) { ++ BarrierSet* bs = Universe::heap()->barrier_set(); ++ assert(bs->kind() == BarrierSet::CardTableModRef, "Wrong barrier set kind"); ++ CardTableModRefBS* ct = (CardTableModRefBS*)bs; ++ assert(sizeof(*ct->byte_map_base) == sizeof(jbyte), "adjust this code"); ++ li(AT, (long)ct->byte_map_base); ++ add_d(AT, AT, obj); ++ if (UseConcMarkSweepGC) membar(StoreStore); ++ st_b(R0, AT, 0); ++} ++ ++// Defines obj, preserves var_size_in_bytes, okay for t2 == var_size_in_bytes. ++void MacroAssembler::tlab_allocate(Register obj, Register var_size_in_bytes, int con_size_in_bytes, ++ Register t1, Register t2, Label& slow_case) { ++ assert_different_registers(obj, t2); ++ assert_different_registers(obj, var_size_in_bytes); ++ ++ Register end = t2; ++ // verify_tlab(); ++ ++ ld_ptr(obj, Address(TREG, JavaThread::tlab_top_offset())); ++ if (var_size_in_bytes == noreg) { ++ lea(end, Address(obj, con_size_in_bytes)); ++ } else { ++ lea(end, Address(obj, var_size_in_bytes, Address::times_1, 0)); ++ } ++ ++ ld_ptr(SCR1, Address(TREG, JavaThread::tlab_end_offset())); ++ blt_far(SCR1, end, slow_case, false); ++ ++ // update the tlab top pointer ++ st_ptr(end, Address(TREG, JavaThread::tlab_top_offset())); ++ ++ // recover var_size_in_bytes if necessary ++ if (var_size_in_bytes == end) { ++ sub_d(var_size_in_bytes, var_size_in_bytes, obj); ++ } ++ // verify_tlab(); ++} ++ ++// Defines obj, preserves var_size_in_bytes ++void MacroAssembler::eden_allocate(Register obj, Register var_size_in_bytes, int con_size_in_bytes, ++ Register t1, Label& slow_case) { ++ assert_different_registers(obj, var_size_in_bytes, t1, AT); ++ if (CMSIncrementalMode || !Universe::heap()->supports_inline_contig_alloc()) { ++ // No allocation in the shared eden. ++ b_far(slow_case); ++ } else { ++ Register end = t1; ++ Register heap_end = SCR2; ++ Label retry; ++ bind(retry); ++ ++ li(SCR1, (address)Universe::heap()->end_addr()); ++ ld_d(heap_end, SCR1, 0); ++ ++ // Get the current top of the heap ++ li(SCR1, (address) Universe::heap()->top_addr()); ++ ll_d(obj, SCR1, 0); ++ ++ // Adjust it my the size of our new object ++ if (var_size_in_bytes == noreg) ++ addi_d(end, obj, con_size_in_bytes); ++ else ++ add_d(end, obj, var_size_in_bytes); ++ ++ // if end < obj then we wrapped around high memory ++ blt_far(end, obj, slow_case, false); ++ blt_far(heap_end, end, slow_case, false); ++ ++ // If heap top hasn't been changed by some other thread, update it. ++ sc_d(end, SCR1, 0); ++ beqz(end, retry); ++ ++ incr_allocated_bytes(TREG, var_size_in_bytes, con_size_in_bytes, t1); ++ } ++} ++ ++void MacroAssembler::incr_allocated_bytes(Register thread, ++ Register var_size_in_bytes, ++ int con_size_in_bytes, ++ Register t1) { ++ if (!thread->is_valid()) { ++#ifndef OPT_THREAD ++ assert(t1->is_valid(), "need temp reg"); ++ thread = t1; ++ get_thread(thread); ++#else ++ thread = TREG; ++#endif ++ } ++ ++ ld_ptr(AT, thread, in_bytes(JavaThread::allocated_bytes_offset())); ++ if (var_size_in_bytes->is_valid()) { ++ add_d(AT, AT, var_size_in_bytes); ++ } else { ++ addi_d(AT, AT, con_size_in_bytes); ++ } ++ st_ptr(AT, thread, in_bytes(JavaThread::allocated_bytes_offset())); ++} ++ ++static const double pi_4 = 0.7853981633974483; ++ ++// must get argument(a double) in FA0/FA1 ++//void MacroAssembler::trigfunc(char trig, bool preserve_cpu_regs, int num_fpu_regs_in_use) { ++//We need to preseve the register which maybe modified during the Call ++void MacroAssembler::trigfunc(char trig, int num_fpu_regs_in_use) { ++ // save all modified register here ++ // FIXME, in the disassembly of tirgfunc, only used V0, V1, T4, SP, RA, so we ony save V0, V1, T4 ++ guarantee(0, "LA not implemented yet"); ++#if 0 ++ pushad(); ++ // we should preserve the stack space before we call ++ addi_d(SP, SP, -wordSize * 2); ++ switch (trig){ ++ case 's' : ++ call( CAST_FROM_FN_PTR(address, SharedRuntime::dsin), relocInfo::runtime_call_type ); ++ break; ++ case 'c': ++ call( CAST_FROM_FN_PTR(address, SharedRuntime::dcos), relocInfo::runtime_call_type ); ++ break; ++ case 't': ++ call( CAST_FROM_FN_PTR(address, SharedRuntime::dtan), relocInfo::runtime_call_type ); ++ break; ++ default:assert (false, "bad intrinsic"); ++ break; ++ ++ } ++ ++ addi_d(SP, SP, wordSize * 2); ++ popad(); ++#endif ++} ++ ++void MacroAssembler::li(Register rd, jlong value) { ++ jlong hi12 = bitfield(value, 52, 12); ++ jlong lo52 = bitfield(value, 0, 52); ++ ++ if (hi12 != 0 && lo52 == 0) { ++ lu52i_d(rd, R0, hi12); ++ } else { ++ jlong hi20 = bitfield(value, 32, 20); ++ jlong lo20 = bitfield(value, 12, 20); ++ jlong lo12 = bitfield(value, 0, 12); ++ ++ if (lo20 == 0) { ++ ori(rd, R0, lo12); ++ } else if (bitfield(simm12(lo12), 12, 20) == lo20) { ++ addi_w(rd, R0, simm12(lo12)); ++ } else { ++ lu12i_w(rd, lo20); ++ if (lo12 != 0) ++ ori(rd, rd, lo12); ++ } ++ if (hi20 != bitfield(simm20(lo20), 20, 20)) ++ lu32i_d(rd, hi20); ++ if (hi12 != bitfield(simm20(hi20), 20, 12)) ++ lu52i_d(rd, rd, hi12); ++ } ++} ++ ++void MacroAssembler::patchable_li52(Register rd, jlong value) { ++ int count = 0; ++ ++ if (value <= max_jint && value >= min_jint) { ++ if (is_simm(value, 12)) { ++ addi_d(rd, R0, value); ++ count++; ++ } else { ++ lu12i_w(rd, split_low20(value >> 12)); ++ count++; ++ if (split_low12(value)) { ++ ori(rd, rd, split_low12(value)); ++ count++; ++ } ++ } ++ } else if (is_simm(value, 52)) { ++ lu12i_w(rd, split_low20(value >> 12)); ++ count++; ++ if (split_low12(value)) { ++ ori(rd, rd, split_low12(value)); ++ count++; ++ } ++ lu32i_d(rd, split_low20(value >> 32)); ++ count++; ++ } else { ++ tty->print_cr("value = 0x%lx", value); ++ guarantee(false, "Not supported yet !"); ++ } ++ ++ while (count < 3) { ++ nop(); ++ count++; ++ } ++} ++ ++void MacroAssembler::set_narrow_klass(Register dst, Klass* k) { ++ assert(UseCompressedClassPointers, "should only be used for compressed header"); ++ assert(oop_recorder() != NULL, "this assembler needs an OopRecorder"); ++ ++ int klass_index = oop_recorder()->find_index(k); ++ RelocationHolder rspec = metadata_Relocation::spec(klass_index); ++ long narrowKlass = (long)Klass::encode_klass(k); ++ ++ relocate(rspec, Assembler::narrow_oop_operand); ++ patchable_li52(dst, narrowKlass); ++} ++ ++void MacroAssembler::set_narrow_oop(Register dst, jobject obj) { ++ assert(UseCompressedOops, "should only be used for compressed header"); ++ assert(oop_recorder() != NULL, "this assembler needs an OopRecorder"); ++ ++ int oop_index = oop_recorder()->find_index(obj); ++ RelocationHolder rspec = oop_Relocation::spec(oop_index); ++ ++ relocate(rspec, Assembler::narrow_oop_operand); ++ patchable_li52(dst, oop_index); ++} ++ ++void MacroAssembler::lipc(Register rd, Label& L) { ++ if (L.is_bound()) { ++ jint offs = (target(L) - pc()) >> 2; ++ guarantee(is_simm(offs, 20), "Not signed 20-bit offset"); ++ pcaddi(rd, offs); ++ } else { ++ InstructionMark im(this); ++ L.add_patch_at(code(), locator()); ++ pcaddi(rd, 0); ++ } ++} ++ ++void MacroAssembler::verify_oop(Register reg, const char* s) { ++ if (!VerifyOops) return; ++ const char * b = NULL; ++ stringStream ss; ++ ss.print("verify_oop: %s: %s", reg->name(), s); ++ b = code_string(ss.as_string()); ++ pushad(); ++ move(A1, reg); ++ patchable_li52(A0, (long)b); ++ li(AT, (long)StubRoutines::verify_oop_subroutine_entry_address()); ++ ld_d(T4, AT, 0); ++ jalr(T4); ++ popad(); ++} ++ ++void MacroAssembler::verify_oop_addr(Address addr, const char* s) { ++ //TODO: LA ++ guarantee(0, "LA not implemented yet"); ++#if 0 ++ if (!VerifyOops) { ++ nop(); ++ return; ++ } ++ // Pass register number to verify_oop_subroutine ++ const char * b = NULL; ++ stringStream ss; ++ ss.print("verify_oop_addr: %s", s); ++ b = code_string(ss.as_string()); ++ ++ st_ptr(T0, SP, - wordSize); ++ st_ptr(T1, SP, - 2*wordSize); ++ st_ptr(RA, SP, - 3*wordSize); ++ st_ptr(A0, SP, - 4*wordSize); ++ st_ptr(A1, SP, - 5*wordSize); ++ st_ptr(AT, SP, - 6*wordSize); ++ st_ptr(T9, SP, - 7*wordSize); ++ ld_ptr(A1, addr); // addr may use SP, so load from it before change SP ++ addiu(SP, SP, - 7 * wordSize); ++ ++ patchable_li52(A0, (long)b); ++ // call indirectly to solve generation ordering problem ++ li(AT, (long)StubRoutines::verify_oop_subroutine_entry_address()); ++ ld_ptr(T9, AT, 0); ++ jalr(T9); ++ delayed()->nop(); ++ ld_ptr(T0, SP, 6* wordSize); ++ ld_ptr(T1, SP, 5* wordSize); ++ ld_ptr(RA, SP, 4* wordSize); ++ ld_ptr(A0, SP, 3* wordSize); ++ ld_ptr(A1, SP, 2* wordSize); ++ ld_ptr(AT, SP, 1* wordSize); ++ ld_ptr(T9, SP, 0* wordSize); ++ addiu(SP, SP, 7 * wordSize); ++#endif ++} ++ ++// used registers : T0, T1 ++void MacroAssembler::verify_oop_subroutine() { ++ // RA: ra ++ // A0: char* error message ++ // A1: oop object to verify ++ Label exit, error; ++ // increment counter ++ li(T0, (long)StubRoutines::verify_oop_count_addr()); ++ ld_w(AT, T0, 0); ++ addi_d(AT, AT, 1); ++ st_w(AT, T0, 0); ++ ++ // make sure object is 'reasonable' ++ beq(A1, R0, exit); // if obj is NULL it is ok ++ ++ // Check if the oop is in the right area of memory ++ // const int oop_mask = Universe::verify_oop_mask(); ++ // const int oop_bits = Universe::verify_oop_bits(); ++ const uintptr_t oop_mask = Universe::verify_oop_mask(); ++ const uintptr_t oop_bits = Universe::verify_oop_bits(); ++ li(AT, oop_mask); ++ andr(T0, A1, AT); ++ li(AT, oop_bits); ++ bne(T0, AT, error); ++ ++ // make sure klass is 'reasonable' ++ // add for compressedoops ++ reinit_heapbase(); ++ // add for compressedoops ++ load_klass(T0, A1); ++ beq(T0, R0, error); // if klass is NULL it is broken ++ // return if everything seems ok ++ bind(exit); ++ ++ jr(RA); ++ ++ // handle errors ++ bind(error); ++ pushad(); ++ call(CAST_FROM_FN_PTR(address, MacroAssembler::debug), relocInfo::runtime_call_type); ++ popad(); ++ jr(RA); ++} ++ ++void MacroAssembler::verify_tlab(Register t1, Register t2) { ++#ifdef ASSERT ++ assert_different_registers(t1, t2, AT); ++ if (UseTLAB && VerifyOops) { ++ Label next, ok; ++ ++ get_thread(t1); ++ ++ ld_ptr(t2, t1, in_bytes(JavaThread::tlab_top_offset())); ++ ld_ptr(AT, t1, in_bytes(JavaThread::tlab_start_offset())); ++ bgeu(t2, AT, next); ++ ++ stop("assert(top >= start)"); ++ ++ bind(next); ++ ld_ptr(AT, t1, in_bytes(JavaThread::tlab_end_offset())); ++ bgeu(AT, t2, ok); ++ ++ stop("assert(top <= end)"); ++ ++ bind(ok); ++ ++ } ++#endif ++} ++ ++RegisterOrConstant MacroAssembler::delayed_value_impl(intptr_t* delayed_value_addr, ++ Register tmp, ++ int offset) { ++ //TODO: LA ++ guarantee(0, "LA not implemented yet"); ++ return RegisterOrConstant(tmp); ++} ++ ++void MacroAssembler::hswap(Register reg) { ++ // TODO LA opt ++ //short ++ srli_w(AT, reg, 8); ++ slli_w(reg, reg, 24); ++ srai_w(reg, reg, 16); ++ orr(reg, reg, AT); ++} ++ ++void MacroAssembler::huswap(Register reg) { ++ // TODO LA opt ++ srli_d(AT, reg, 8); ++ slli_d(reg, reg, 24); ++ srli_d(reg, reg, 16); ++ orr(reg, reg, AT); ++ bstrpick_d(reg, reg, 15, 0); ++} ++ ++// something funny to do this will only one more register AT ++// 32 bits ++void MacroAssembler::swap(Register reg) { ++ //TODO: LA opt ++ srli_w(AT, reg, 8); ++ slli_w(reg, reg, 24); ++ orr(reg, reg, AT); ++ //reg : 4 1 2 3 ++ srli_w(AT, AT, 16); ++ xorr(AT, AT, reg); ++ andi(AT, AT, 0xff); ++ //AT : 0 0 0 1^3); ++ xorr(reg, reg, AT); ++ //reg : 4 1 2 1 ++ slli_w(AT, AT, 16); ++ xorr(reg, reg, AT); ++ //reg : 4 3 2 1 ++} ++ ++void MacroAssembler::cmpxchg(Address addr, Register oldval, Register newval, ++ Register resflag, bool retold, bool barrier) { ++ assert(oldval != resflag, "oldval != resflag"); ++ assert(newval != resflag, "newval != resflag"); ++ Label again, succ, fail; ++ ++ bind(again); ++ ll_d(resflag, addr); ++ bne(resflag, oldval, fail); ++ move(resflag, newval); ++ sc_d(resflag, addr); ++ beqz(resflag, again); ++ b(succ); ++ ++ bind(fail); ++ if (barrier) ++ membar(LoadLoad); ++ if (retold && oldval != R0) ++ move(oldval, resflag); ++ move(resflag, R0); ++ bind(succ); ++} ++ ++void MacroAssembler::cmpxchg(Address addr, Register oldval, Register newval, ++ Register tmp, bool retold, bool barrier, Label& succ, Label* fail) { ++ assert(oldval != tmp, "oldval != tmp"); ++ assert(newval != tmp, "newval != tmp"); ++ Label again, neq; ++ ++ bind(again); ++ ll_d(tmp, addr); ++ bne(tmp, oldval, neq); ++ move(tmp, newval); ++ sc_d(tmp, addr); ++ beqz(tmp, again); ++ b(succ); ++ ++ bind(neq); ++ if (barrier) ++ membar(LoadLoad); ++ if (retold && oldval != R0) ++ move(oldval, tmp); ++ if (fail) ++ b(*fail); ++} ++ ++void MacroAssembler::cmpxchg32(Address addr, Register oldval, Register newval, ++ Register resflag, bool sign, bool retold, bool barrier) { ++ assert(oldval != resflag, "oldval != resflag"); ++ assert(newval != resflag, "newval != resflag"); ++ Label again, succ, fail; ++ ++ bind(again); ++ ll_w(resflag, addr); ++ if (!sign) ++ lu32i_d(resflag, 0); ++ bne(resflag, oldval, fail); ++ move(resflag, newval); ++ sc_w(resflag, addr); ++ beqz(resflag, again); ++ b(succ); ++ ++ bind(fail); ++ if (barrier) ++ membar(LoadLoad); ++ if (retold && oldval != R0) ++ move(oldval, resflag); ++ move(resflag, R0); ++ bind(succ); ++} ++ ++void MacroAssembler::cmpxchg32(Address addr, Register oldval, Register newval, Register tmp, ++ bool sign, bool retold, bool barrier, Label& succ, Label* fail) { ++ assert(oldval != tmp, "oldval != tmp"); ++ assert(newval != tmp, "newval != tmp"); ++ Label again, neq; ++ ++ bind(again); ++ ll_w(tmp, addr); ++ if (!sign) ++ lu32i_d(tmp, 0); ++ bne(tmp, oldval, neq); ++ move(tmp, newval); ++ sc_w(tmp, addr); ++ beqz(tmp, again); ++ b(succ); ++ ++ bind(neq); ++ if (barrier) ++ membar(LoadLoad); ++ if (retold && oldval != R0) ++ move(oldval, tmp); ++ if (fail) ++ b(*fail); ++} ++ ++// be sure the three register is different ++void MacroAssembler::rem_s(FloatRegister fd, FloatRegister fs, FloatRegister ft, FloatRegister tmp) { ++ //TODO: LA ++ guarantee(0, "LA not implemented yet"); ++} ++ ++// be sure the three register is different ++void MacroAssembler::rem_d(FloatRegister fd, FloatRegister fs, FloatRegister ft, FloatRegister tmp) { ++ //TODO: LA ++ guarantee(0, "LA not implemented yet"); ++} ++ ++// Fast_Lock and Fast_Unlock used by C2 ++ ++// Because the transitions from emitted code to the runtime ++// monitorenter/exit helper stubs are so slow it's critical that ++// we inline both the stack-locking fast-path and the inflated fast path. ++// ++// See also: cmpFastLock and cmpFastUnlock. ++// ++// What follows is a specialized inline transliteration of the code ++// in slow_enter() and slow_exit(). If we're concerned about I$ bloat ++// another option would be to emit TrySlowEnter and TrySlowExit methods ++// at startup-time. These methods would accept arguments as ++// (Obj, Self, box, Scratch) and return success-failure ++// indications in the icc.ZFlag. Fast_Lock and Fast_Unlock would simply ++// marshal the arguments and emit calls to TrySlowEnter and TrySlowExit. ++// In practice, however, the # of lock sites is bounded and is usually small. ++// Besides the call overhead, TrySlowEnter and TrySlowExit might suffer ++// if the processor uses simple bimodal branch predictors keyed by EIP ++// Since the helper routines would be called from multiple synchronization ++// sites. ++// ++// An even better approach would be write "MonitorEnter()" and "MonitorExit()" ++// in java - using j.u.c and unsafe - and just bind the lock and unlock sites ++// to those specialized methods. That'd give us a mostly platform-independent ++// implementation that the JITs could optimize and inline at their pleasure. ++// Done correctly, the only time we'd need to cross to native could would be ++// to park() or unpark() threads. We'd also need a few more unsafe operators ++// to (a) prevent compiler-JIT reordering of non-volatile accesses, and ++// (b) explicit barriers or fence operations. ++// ++// TODO: ++// ++// * Arrange for C2 to pass "Self" into Fast_Lock and Fast_Unlock in one of the registers (scr). ++// This avoids manifesting the Self pointer in the Fast_Lock and Fast_Unlock terminals. ++// Given TLAB allocation, Self is usually manifested in a register, so passing it into ++// the lock operators would typically be faster than reifying Self. ++// ++// * Ideally I'd define the primitives as: ++// fast_lock (nax Obj, nax box, res, tmp, nax scr) where tmp and scr are KILLED. ++// fast_unlock (nax Obj, box, res, nax tmp) where tmp are KILLED ++// Unfortunately ADLC bugs prevent us from expressing the ideal form. ++// Instead, we're stuck with a rather awkward and brittle register assignments below. ++// Furthermore the register assignments are overconstrained, possibly resulting in ++// sub-optimal code near the synchronization site. ++// ++// * Eliminate the sp-proximity tests and just use "== Self" tests instead. ++// Alternately, use a better sp-proximity test. ++// ++// * Currently ObjectMonitor._Owner can hold either an sp value or a (THREAD *) value. ++// Either one is sufficient to uniquely identify a thread. ++// TODO: eliminate use of sp in _owner and use get_thread(tr) instead. ++// ++// * Intrinsify notify() and notifyAll() for the common cases where the ++// object is locked by the calling thread but the waitlist is empty. ++// avoid the expensive JNI call to JVM_Notify() and JVM_NotifyAll(). ++// ++// * use jccb and jmpb instead of jcc and jmp to improve code density. ++// But beware of excessive branch density on AMD Opterons. ++// ++// * Both Fast_Lock and Fast_Unlock set the ICC.ZF to indicate success ++// or failure of the fast-path. If the fast-path fails then we pass ++// control to the slow-path, typically in C. In Fast_Lock and ++// Fast_Unlock we often branch to DONE_LABEL, just to find that C2 ++// will emit a conditional branch immediately after the node. ++// So we have branches to branches and lots of ICC.ZF games. ++// Instead, it might be better to have C2 pass a "FailureLabel" ++// into Fast_Lock and Fast_Unlock. In the case of success, control ++// will drop through the node. ICC.ZF is undefined at exit. ++// In the case of failure, the node will branch directly to the ++// FailureLabel ++ ++// obj: object to lock ++// box: on-stack box address (displaced header location) ++// tmp: tmp -- KILLED ++// scr: tmp -- KILLED ++void MacroAssembler::fast_lock(Register objReg, Register boxReg, Register resReg, ++ Register tmpReg, Register scrReg) { ++ Label IsInflated, DONE, DONE_SET; ++ ++ // Ensure the register assignents are disjoint ++ guarantee(objReg != boxReg, ""); ++ guarantee(objReg != tmpReg, ""); ++ guarantee(objReg != scrReg, ""); ++ guarantee(boxReg != tmpReg, ""); ++ guarantee(boxReg != scrReg, ""); ++ ++ block_comment("FastLock"); ++ ++ if (PrintBiasedLockingStatistics) { ++ atomic_inc32((address)BiasedLocking::total_entry_count_addr(), 1, tmpReg, scrReg); ++ } ++ ++ if (EmitSync & 1) { ++ move(AT, R0); ++ return; ++ } else ++ if (EmitSync & 2) { ++ Label DONE_LABEL ; ++ if (UseBiasedLocking) { ++ // Note: tmpReg maps to the swap_reg argument and scrReg to the tmp_reg argument. ++ biased_locking_enter(boxReg, objReg, tmpReg, scrReg, false, DONE_LABEL, NULL); ++ } ++ ++ ld_d(tmpReg, Address(objReg, 0)) ; // fetch markword ++ ori(tmpReg, tmpReg, 0x1); ++ st_d(tmpReg, Address(boxReg, 0)); // Anticipate successful CAS ++ ++ cmpxchg(Address(objReg, 0), tmpReg, boxReg, scrReg, true, false, DONE_LABEL); // Updates tmpReg ++ ++ // Recursive locking ++ sub_d(tmpReg, tmpReg, SP); ++ li(AT, (7 - os::vm_page_size() )); ++ andr(tmpReg, tmpReg, AT); ++ st_d(tmpReg, Address(boxReg, 0)); ++ bind(DONE_LABEL) ; ++ } else { ++ // Possible cases that we'll encounter in fast_lock ++ // ------------------------------------------------ ++ // * Inflated ++ // -- unlocked ++ // -- Locked ++ // = by self ++ // = by other ++ // * biased ++ // -- by Self ++ // -- by other ++ // * neutral ++ // * stack-locked ++ // -- by self ++ // = sp-proximity test hits ++ // = sp-proximity test generates false-negative ++ // -- by other ++ // ++ ++ // TODO: optimize away redundant LDs of obj->mark and improve the markword triage ++ // order to reduce the number of conditional branches in the most common cases. ++ // Beware -- there's a subtle invariant that fetch of the markword ++ // at [FETCH], below, will never observe a biased encoding (*101b). ++ // If this invariant is not held we risk exclusion (safety) failure. ++ if (UseBiasedLocking && !UseOptoBiasInlining) { ++ Label succ, fail; ++ biased_locking_enter(boxReg, objReg, tmpReg, scrReg, false, succ, NULL); ++ b(fail); ++ bind(succ); ++ li(resReg, 1); ++ b(DONE); ++ bind(fail); ++ } ++ ++ ld_d(tmpReg, Address(objReg, 0)); //Fetch the markword of the object. ++ andi(AT, tmpReg, markOopDesc::monitor_value); ++ bnez(AT, IsInflated); // inflated vs stack-locked|neutral|bias ++ ++ // Attempt stack-locking ... ++ ori(tmpReg, tmpReg, markOopDesc::unlocked_value); ++ st_d(tmpReg, Address(boxReg, 0)); // Anticipate successful CAS ++ ++ if (PrintBiasedLockingStatistics) { ++ Label SUCC, FAIL; ++ cmpxchg(Address(objReg, 0), tmpReg, boxReg, scrReg, true, false, SUCC, &FAIL); // Updates tmpReg ++ bind(SUCC); ++ atomic_inc32((address)BiasedLocking::fast_path_entry_count_addr(), 1, AT, scrReg); ++ li(resReg, 1); ++ b(DONE); ++ bind(FAIL); ++ } else { ++ // If cmpxchg is succ, then scrReg = 1 ++ cmpxchg(Address(objReg, 0), tmpReg, boxReg, scrReg, true, false, DONE_SET); // Updates tmpReg ++ } ++ ++ // Recursive locking ++ // The object is stack-locked: markword contains stack pointer to BasicLock. ++ // Locked by current thread if difference with current SP is less than one page. ++ sub_d(tmpReg, tmpReg, SP); ++ li(AT, 7 - os::vm_page_size()); ++ andr(tmpReg, tmpReg, AT); ++ st_d(tmpReg, Address(boxReg, 0)); ++ ++ if (PrintBiasedLockingStatistics) { ++ Label L; ++ // tmpReg == 0 => BiasedLocking::_fast_path_entry_count++ ++ bnez(tmpReg, L); ++ atomic_inc32((address)BiasedLocking::fast_path_entry_count_addr(), 1, AT, scrReg); ++ bind(L); ++ } ++ ++ sltui(resReg, tmpReg, 1); // resReg = (tmpReg == 0) ? 1 : 0 ++ b(DONE); ++ ++ bind(IsInflated); ++ // The object's monitor m is unlocked iff m->owner == NULL, ++ // otherwise m->owner may contain a thread or a stack address. ++ ++ // TODO: someday avoid the ST-before-CAS penalty by ++ // relocating (deferring) the following ST. ++ // We should also think about trying a CAS without having ++ // fetched _owner. If the CAS is successful we may ++ // avoid an RTO->RTS upgrade on the $line. ++ // Without cast to int32_t a movptr will destroy r10 which is typically obj ++ li(AT, (int32_t)intptr_t(markOopDesc::unused_mark())); ++ st_d(AT, Address(boxReg, 0)); ++ ++ ld_d(AT, Address(tmpReg, ObjectMonitor::owner_offset_in_bytes() - 2)); ++ // if (m->owner != 0) => AT = 0, goto slow path. ++ move(scrReg, R0); ++ bnez(AT, DONE_SET); ++ ++#ifndef OPT_THREAD ++ get_thread(TREG) ; ++#endif ++ // It's inflated and appears unlocked ++ addi_d(tmpReg, tmpReg, ObjectMonitor::owner_offset_in_bytes() - 2); ++ cmpxchg(Address(tmpReg, 0), R0, TREG, scrReg, false, false); ++ // Intentional fall-through into DONE ... ++ ++ bind(DONE_SET); ++ move(resReg, scrReg); ++ ++ // DONE is a hot target - we'd really like to place it at the ++ // start of cache line by padding with NOPs. ++ // See the AMD and Intel software optimization manuals for the ++ // most efficient "long" NOP encodings. ++ // Unfortunately none of our alignment mechanisms suffice. ++ bind(DONE); ++ // At DONE the resReg is set as follows ... ++ // Fast_Unlock uses the same protocol. ++ // resReg == 1 -> Success ++ // resREg == 0 -> Failure - force control through the slow-path ++ ++ // Avoid branch-to-branch on AMD processors ++ // This appears to be superstition. ++ if (EmitSync & 32) nop() ; ++ ++ } ++} ++ ++// obj: object to unlock ++// box: box address (displaced header location), killed. ++// tmp: killed tmp; cannot be obj nor box. ++// ++// Some commentary on balanced locking: ++// ++// Fast_Lock and Fast_Unlock are emitted only for provably balanced lock sites. ++// Methods that don't have provably balanced locking are forced to run in the ++// interpreter - such methods won't be compiled to use fast_lock and fast_unlock. ++// The interpreter provides two properties: ++// I1: At return-time the interpreter automatically and quietly unlocks any ++// objects acquired the current activation (frame). Recall that the ++// interpreter maintains an on-stack list of locks currently held by ++// a frame. ++// I2: If a method attempts to unlock an object that is not held by the ++// the frame the interpreter throws IMSX. ++// ++// Lets say A(), which has provably balanced locking, acquires O and then calls B(). ++// B() doesn't have provably balanced locking so it runs in the interpreter. ++// Control returns to A() and A() unlocks O. By I1 and I2, above, we know that O ++// is still locked by A(). ++// ++// The only other source of unbalanced locking would be JNI. The "Java Native Interface: ++// Programmer's Guide and Specification" claims that an object locked by jni_monitorenter ++// should not be unlocked by "normal" java-level locking and vice-versa. The specification ++// doesn't specify what will occur if a program engages in such mixed-mode locking, however. ++ ++void MacroAssembler::fast_unlock(Register objReg, Register boxReg, Register resReg, ++ Register tmpReg, Register scrReg) { ++ Label DONE, DONE_SET, Stacked, Inflated; ++ ++ guarantee(objReg != boxReg, ""); ++ guarantee(objReg != tmpReg, ""); ++ guarantee(objReg != scrReg, ""); ++ guarantee(boxReg != tmpReg, ""); ++ guarantee(boxReg != scrReg, ""); ++ ++ block_comment("FastUnlock"); ++ ++ if (EmitSync & 4) { ++ // Disable - inhibit all inlining. Force control through the slow-path ++ move(AT, R0); ++ return; ++ } else ++ if (EmitSync & 8) { ++ Label DONE_LABEL ; ++ if (UseBiasedLocking) { ++ biased_locking_exit(objReg, tmpReg, DONE_LABEL); ++ } ++ // classic stack-locking code ... ++ ld_d(tmpReg, Address(boxReg, 0)) ; ++ assert_different_registers(AT, tmpReg); ++ li(AT, 0x1); ++ beq(tmpReg, R0, DONE_LABEL) ; ++ ++ cmpxchg(Address(objReg, 0), boxReg, tmpReg, AT, false, false); ++ bind(DONE_LABEL); ++ } else { ++ Label CheckSucc; ++ ++ // Critically, the biased locking test must have precedence over ++ // and appear before the (box->dhw == 0) recursive stack-lock test. ++ if (UseBiasedLocking && !UseOptoBiasInlining) { ++ Label succ, fail; ++ biased_locking_exit(objReg, tmpReg, succ); ++ b(fail); ++ bind(succ); ++ li(resReg, 1); ++ b(DONE); ++ bind(fail); ++ } ++ ++ ld_d(tmpReg, Address(boxReg, 0)); // Examine the displaced header ++ sltui(AT, tmpReg, 1); ++ beqz(tmpReg, DONE_SET); // 0 indicates recursive stack-lock ++ ++ ld_d(tmpReg, Address(objReg, 0)); // Examine the object's markword ++ andi(AT, tmpReg, markOopDesc::monitor_value); ++ beqz(AT, Stacked); // Inflated? ++ ++ bind(Inflated); ++ // It's inflated. ++ // Despite our balanced locking property we still check that m->_owner == Self ++ // as java routines or native JNI code called by this thread might ++ // have released the lock. ++ // Refer to the comments in synchronizer.cpp for how we might encode extra ++ // state in _succ so we can avoid fetching EntryList|cxq. ++ // ++ // I'd like to add more cases in fast_lock() and fast_unlock() -- ++ // such as recursive enter and exit -- but we have to be wary of ++ // I$ bloat, T$ effects and BP$ effects. ++ // ++ // If there's no contention try a 1-0 exit. That is, exit without ++ // a costly MEMBAR or CAS. See synchronizer.cpp for details on how ++ // we detect and recover from the race that the 1-0 exit admits. ++ // ++ // Conceptually Fast_Unlock() must execute a STST|LDST "release" barrier ++ // before it STs null into _owner, releasing the lock. Updates ++ // to data protected by the critical section must be visible before ++ // we drop the lock (and thus before any other thread could acquire ++ // the lock and observe the fields protected by the lock). ++#ifndef OPT_THREAD ++ get_thread(TREG); ++#endif ++ ++ // It's inflated ++ ld_d(scrReg, Address(tmpReg, ObjectMonitor::owner_offset_in_bytes() - 2)); ++ xorr(scrReg, scrReg, TREG); ++ ++ ld_d(AT, Address(tmpReg, ObjectMonitor::recursions_offset_in_bytes() - 2)); ++ orr(scrReg, scrReg, AT); ++ ++ move(AT, R0); ++ bnez(scrReg, DONE_SET); ++ ++ ld_d(scrReg, Address(tmpReg, ObjectMonitor::cxq_offset_in_bytes() - 2)); ++ ld_d(AT, Address(tmpReg, ObjectMonitor::EntryList_offset_in_bytes() - 2)); ++ orr(scrReg, scrReg, AT); ++ ++ move(AT, R0); ++ bnez(scrReg, DONE_SET); ++ ++ membar(Assembler::Membar_mask_bits(LoadLoad|LoadStore)); ++ st_d(R0, Address(tmpReg, ObjectMonitor::owner_offset_in_bytes() - 2)); ++ li(resReg, 1); ++ b(DONE); ++ ++ bind(Stacked); ++ ld_d(tmpReg, Address(boxReg, 0)); ++ cmpxchg(Address(objReg, 0), boxReg, tmpReg, AT, false, false); ++ ++ bind(DONE_SET); ++ move(resReg, AT); ++ ++ if (EmitSync & 65536) { ++ bind (CheckSucc); ++ } ++ ++ bind(DONE); ++ ++ // Avoid branch to branch on AMD processors ++ if (EmitSync & 32768) { nop() ; } ++ } ++} ++ ++void MacroAssembler::align(int modulus) { ++ while (offset() % modulus != 0) nop(); ++} ++ ++ ++void MacroAssembler::verify_FPU(int stack_depth, const char* s) { ++ //Unimplemented(); ++} ++ ++Register caller_saved_registers[] = {T7, T5, T6, A0, A1, A2, A3, A4, A5, A6, A7, T0, T1, T2, T3, T8, T4, S8, RA, FP}; ++Register caller_saved_registers_except_v0[] = {T7, T5, T6, A1, A2, A3, A4, A5, A6, A7, T0, T1, T2, T3, T8, T4, S8, RA, FP}; ++ ++ //TODO: LA ++//In LA, F0~23 are all caller-saved registers ++FloatRegister caller_saved_fpu_registers[] = {F0, F12, F13}; ++ ++// We preserve all caller-saved register ++void MacroAssembler::pushad(){ ++ int i; ++ // Fixed-point registers ++ int len = sizeof(caller_saved_registers) / sizeof(caller_saved_registers[0]); ++ addi_d(SP, SP, -1 * len * wordSize); ++ for (i = 0; i < len; i++) { ++ st_d(caller_saved_registers[i], SP, (len - i - 1) * wordSize); ++ } ++ ++ // Floating-point registers ++ len = sizeof(caller_saved_fpu_registers) / sizeof(caller_saved_fpu_registers[0]); ++ addi_d(SP, SP, -1 * len * wordSize); ++ for (i = 0; i < len; i++) { ++ fst_d(caller_saved_fpu_registers[i], SP, (len - i - 1) * wordSize); ++ } ++}; ++ ++void MacroAssembler::popad(){ ++ int i; ++ // Floating-point registers ++ int len = sizeof(caller_saved_fpu_registers) / sizeof(caller_saved_fpu_registers[0]); ++ for (i = 0; i < len; i++) ++ { ++ fld_d(caller_saved_fpu_registers[i], SP, (len - i - 1) * wordSize); ++ } ++ addi_d(SP, SP, len * wordSize); ++ ++ // Fixed-point registers ++ len = sizeof(caller_saved_registers) / sizeof(caller_saved_registers[0]); ++ for (i = 0; i < len; i++) ++ { ++ ld_d(caller_saved_registers[i], SP, (len - i - 1) * wordSize); ++ } ++ addi_d(SP, SP, len * wordSize); ++}; ++ ++// We preserve all caller-saved register except V0 ++void MacroAssembler::pushad_except_v0() { ++ int i; ++ // Fixed-point registers ++ int len = sizeof(caller_saved_registers_except_v0) / sizeof(caller_saved_registers_except_v0[0]); ++ addi_d(SP, SP, -1 * len * wordSize); ++ for (i = 0; i < len; i++) { ++ st_d(caller_saved_registers_except_v0[i], SP, (len - i - 1) * wordSize); ++ } ++ ++ // Floating-point registers ++ len = sizeof(caller_saved_fpu_registers) / sizeof(caller_saved_fpu_registers[0]); ++ addi_d(SP, SP, -1 * len * wordSize); ++ for (i = 0; i < len; i++) { ++ fst_d(caller_saved_fpu_registers[i], SP, (len - i - 1) * wordSize); ++ } ++} ++ ++void MacroAssembler::popad_except_v0() { ++ int i; ++ // Floating-point registers ++ int len = sizeof(caller_saved_fpu_registers) / sizeof(caller_saved_fpu_registers[0]); ++ for (i = 0; i < len; i++) { ++ fld_d(caller_saved_fpu_registers[i], SP, (len - i - 1) * wordSize); ++ } ++ addi_d(SP, SP, len * wordSize); ++ ++ // Fixed-point registers ++ len = sizeof(caller_saved_registers_except_v0) / sizeof(caller_saved_registers_except_v0[0]); ++ for (i = 0; i < len; i++) { ++ ld_d(caller_saved_registers_except_v0[i], SP, (len - i - 1) * wordSize); ++ } ++ addi_d(SP, SP, len * wordSize); ++} ++ ++void MacroAssembler::push2(Register reg1, Register reg2) { ++ addi_d(SP, SP, -16); ++ st_d(reg1, SP, 8); ++ st_d(reg2, SP, 0); ++} ++ ++void MacroAssembler::pop2(Register reg1, Register reg2) { ++ ld_d(reg1, SP, 8); ++ ld_d(reg2, SP, 0); ++ addi_d(SP, SP, 16); ++} ++ ++// for UseCompressedOops Option ++void MacroAssembler::load_klass(Register dst, Register src) { ++ if(UseCompressedClassPointers){ ++ ld_wu(dst, Address(src, oopDesc::klass_offset_in_bytes())); ++ decode_klass_not_null(dst); ++ } else { ++ ld_d(dst, src, oopDesc::klass_offset_in_bytes()); ++ } ++} ++ ++void MacroAssembler::store_klass(Register dst, Register src) { ++ if(UseCompressedClassPointers){ ++ encode_klass_not_null(src); ++ st_w(src, dst, oopDesc::klass_offset_in_bytes()); ++ } else { ++ st_d(src, dst, oopDesc::klass_offset_in_bytes()); ++ } ++} ++ ++void MacroAssembler::load_prototype_header(Register dst, Register src) { ++ load_klass(dst, src); ++ ld_d(dst, Address(dst, Klass::prototype_header_offset())); ++} ++ ++void MacroAssembler::store_klass_gap(Register dst, Register src) { ++ if (UseCompressedClassPointers) { ++ st_w(src, dst, oopDesc::klass_gap_offset_in_bytes()); ++ } ++} ++ ++void MacroAssembler::load_heap_oop(Register dst, Address src) { ++ if(UseCompressedOops){ ++ ld_wu(dst, src); ++ decode_heap_oop(dst); ++ } else { ++ ld_d(dst, src); ++ } ++} ++ ++void MacroAssembler::store_heap_oop(Address dst, Register src){ ++ if(UseCompressedOops){ ++ assert(!dst.uses(src), "not enough registers"); ++ encode_heap_oop(src); ++ st_w(src, dst); ++ } else { ++ st_d(src, dst); ++ } ++} ++ ++void MacroAssembler::store_heap_oop_null(Address dst){ ++ if(UseCompressedOops){ ++ st_w(R0, dst); ++ } else { ++ st_d(R0, dst); ++ } ++} ++ ++#ifdef ASSERT ++void MacroAssembler::verify_heapbase(const char* msg) { ++ assert (UseCompressedOops || UseCompressedClassPointers, "should be compressed"); ++ assert (Universe::heap() != NULL, "java heap should be initialized"); ++} ++#endif ++ ++// Algorithm must match oop.inline.hpp encode_heap_oop. ++void MacroAssembler::encode_heap_oop(Register r) { ++#ifdef ASSERT ++ verify_heapbase("MacroAssembler::encode_heap_oop:heap base corrupted?"); ++#endif ++ verify_oop(r, "broken oop in encode_heap_oop"); ++ if (Universe::narrow_oop_base() == NULL) { ++ if (Universe::narrow_oop_shift() != 0) { ++ assert(LogMinObjAlignmentInBytes == Universe::narrow_oop_shift(), "decode alg wrong"); ++ shr(r, LogMinObjAlignmentInBytes); ++ } ++ return; ++ } ++ ++ sub_d(AT, r, S5_heapbase); ++ maskeqz(r, AT, r); ++ if (Universe::narrow_oop_shift() != 0) { ++ assert(LogMinObjAlignmentInBytes == Universe::narrow_oop_shift(), "decode alg wrong"); ++ shr(r, LogMinObjAlignmentInBytes); ++ } ++} ++ ++void MacroAssembler::encode_heap_oop(Register dst, Register src) { ++#ifdef ASSERT ++ verify_heapbase("MacroAssembler::encode_heap_oop:heap base corrupted?"); ++#endif ++ verify_oop(src, "broken oop in encode_heap_oop"); ++ if (Universe::narrow_oop_base() == NULL) { ++ if (Universe::narrow_oop_shift() != 0) { ++ assert(LogMinObjAlignmentInBytes == Universe::narrow_oop_shift(), "decode alg wrong"); ++ srli_d(dst, src, LogMinObjAlignmentInBytes); ++ } else { ++ if (dst != src) { ++ move(dst, src); ++ } ++ } ++ return; ++ } ++ ++ sub_d(AT, src, S5_heapbase); ++ maskeqz(dst, AT, src); ++ if (Universe::narrow_oop_shift() != 0) { ++ assert(LogMinObjAlignmentInBytes == Universe::narrow_oop_shift(), "decode alg wrong"); ++ shr(dst, LogMinObjAlignmentInBytes); ++ } ++} ++ ++void MacroAssembler::encode_heap_oop_not_null(Register r) { ++ assert (UseCompressedOops, "should be compressed"); ++#ifdef ASSERT ++ if (CheckCompressedOops) { ++ Label ok; ++ bne(r, R0, ok); ++ stop("null oop passed to encode_heap_oop_not_null"); ++ bind(ok); ++ } ++#endif ++ verify_oop(r, "broken oop in encode_heap_oop_not_null"); ++ if (Universe::narrow_oop_base() != NULL) { ++ sub_d(r, r, S5_heapbase); ++ } ++ if (Universe::narrow_oop_shift() != 0) { ++ assert(LogMinObjAlignmentInBytes == Universe::narrow_oop_shift(), "decode alg wrong"); ++ shr(r, LogMinObjAlignmentInBytes); ++ } ++} ++ ++void MacroAssembler::encode_heap_oop_not_null(Register dst, Register src) { ++ assert (UseCompressedOops, "should be compressed"); ++#ifdef ASSERT ++ if (CheckCompressedOops) { ++ Label ok; ++ bne(src, R0, ok); ++ stop("null oop passed to encode_heap_oop_not_null2"); ++ bind(ok); ++ } ++#endif ++ verify_oop(src, "broken oop in encode_heap_oop_not_null2"); ++ if (Universe::narrow_oop_base() == NULL) { ++ if (Universe::narrow_oop_shift() != 0) { ++ assert(LogMinObjAlignmentInBytes == Universe::narrow_oop_shift(), "decode alg wrong"); ++ srli_d(dst, src, LogMinObjAlignmentInBytes); ++ } else { ++ if (dst != src) { ++ move(dst, src); ++ } ++ } ++ return; ++ } ++ sub_d(dst, src, S5_heapbase); ++ if (Universe::narrow_oop_shift() != 0) { ++ assert(LogMinObjAlignmentInBytes == Universe::narrow_oop_shift(), "decode alg wrong"); ++ shr(dst, LogMinObjAlignmentInBytes); ++ } ++} ++ ++void MacroAssembler::decode_heap_oop(Register r) { ++#ifdef ASSERT ++ verify_heapbase("MacroAssembler::decode_heap_oop corrupted?"); ++#endif ++ if (Universe::narrow_oop_base() == NULL) { ++ if (Universe::narrow_oop_shift() != 0) { ++ assert(LogMinObjAlignmentInBytes == Universe::narrow_oop_shift(), "decode alg wrong"); ++ shl(r, LogMinObjAlignmentInBytes); ++ } ++ return; ++ } ++ ++ move(AT, r); ++ if (Universe::narrow_oop_shift() != 0) { ++ assert(LogMinObjAlignmentInBytes == Universe::narrow_oop_shift(), "decode alg wrong"); ++ if (LogMinObjAlignmentInBytes <= 4) { ++ alsl_d(r, r, S5_heapbase, LogMinObjAlignmentInBytes - 1); ++ } else { ++ shl(r, LogMinObjAlignmentInBytes); ++ add_d(r, r, S5_heapbase); ++ } ++ } else { ++ add_d(r, r, S5_heapbase); ++ } ++ maskeqz(r, r, AT); ++ verify_oop(r, "broken oop in decode_heap_oop"); ++} ++ ++void MacroAssembler::decode_heap_oop(Register dst, Register src) { ++#ifdef ASSERT ++ verify_heapbase("MacroAssembler::decode_heap_oop corrupted?"); ++#endif ++ if (Universe::narrow_oop_base() == NULL) { ++ if (Universe::narrow_oop_shift() != 0) { ++ assert(LogMinObjAlignmentInBytes == Universe::narrow_oop_shift(), "decode alg wrong"); ++ slli_d(dst, src, LogMinObjAlignmentInBytes); ++ } else { ++ if (dst != src) { ++ move(dst, src); ++ } ++ } ++ return; ++ } ++ ++ Register cond; ++ if (dst == src) { ++ cond = AT; ++ move(cond, src); ++ } else { ++ cond = src; ++ } ++ if (Universe::narrow_oop_shift() != 0) { ++ assert(LogMinObjAlignmentInBytes == Universe::narrow_oop_shift(), "decode alg wrong"); ++ if (LogMinObjAlignmentInBytes <= 4) { ++ alsl_d(dst, src, S5_heapbase, LogMinObjAlignmentInBytes - 1); ++ } else { ++ slli_d(dst, src, LogMinObjAlignmentInBytes); ++ add_d(dst, dst, S5_heapbase); ++ } ++ } else { ++ add_d(dst, src, S5_heapbase); ++ } ++ maskeqz(dst, dst, cond); ++ verify_oop(dst, "broken oop in decode_heap_oop"); ++} ++ ++void MacroAssembler::decode_heap_oop_not_null(Register r) { ++ // Note: it will change flags ++ assert(UseCompressedOops, "should only be used for compressed headers"); ++ assert(Universe::heap() != NULL, "java heap should be initialized"); ++ // Cannot assert, unverified entry point counts instructions (see .ad file) ++ // vtableStubs also counts instructions in pd_code_size_limit. ++ // Also do not verify_oop as this is called by verify_oop. ++ if (Universe::narrow_oop_shift() != 0) { ++ assert(LogMinObjAlignmentInBytes == Universe::narrow_oop_shift(), "decode alg wrong"); ++ if (Universe::narrow_oop_base() != NULL) { ++ if (LogMinObjAlignmentInBytes <= 4) { ++ alsl_d(r, r, S5_heapbase, LogMinObjAlignmentInBytes - 1); ++ } else { ++ shl(r, LogMinObjAlignmentInBytes); ++ add_d(r, r, S5_heapbase); ++ } ++ } else { ++ shl(r, LogMinObjAlignmentInBytes); ++ } ++ } else { ++ assert(Universe::narrow_oop_base() == NULL, "sanity"); ++ } ++} ++ ++void MacroAssembler::decode_heap_oop_not_null(Register dst, Register src) { ++ assert(UseCompressedOops, "should only be used for compressed headers"); ++ assert(Universe::heap() != NULL, "java heap should be initialized"); ++ // Cannot assert, unverified entry point counts instructions (see .ad file) ++ // vtableStubs also counts instructions in pd_code_size_limit. ++ // Also do not verify_oop as this is called by verify_oop. ++ if (Universe::narrow_oop_shift() != 0) { ++ assert(LogMinObjAlignmentInBytes == Universe::narrow_oop_shift(), "decode alg wrong"); ++ if (Universe::narrow_oop_base() != NULL) { ++ if (LogMinObjAlignmentInBytes <= 4) { ++ alsl_d(dst, src, S5_heapbase, LogMinObjAlignmentInBytes - 1); ++ } else { ++ slli_d(dst, src, LogMinObjAlignmentInBytes); ++ add_d(dst, dst, S5_heapbase); ++ } ++ } else { ++ slli_d(dst, src, LogMinObjAlignmentInBytes); ++ } ++ } else { ++ assert (Universe::narrow_oop_base() == NULL, "sanity"); ++ if (dst != src) { ++ move(dst, src); ++ } ++ } ++} ++ ++void MacroAssembler::encode_klass_not_null(Register r) { ++ if (Universe::narrow_klass_base() != NULL) { ++ assert(r != AT, "Encoding a klass in AT"); ++ li(AT, (int64_t)Universe::narrow_klass_base()); ++ sub_d(r, r, AT); ++ } ++ if (Universe::narrow_klass_shift() != 0) { ++ assert (LogKlassAlignmentInBytes == Universe::narrow_klass_shift(), "decode alg wrong"); ++ shr(r, LogKlassAlignmentInBytes); ++ } ++} ++ ++void MacroAssembler::encode_klass_not_null(Register dst, Register src) { ++ if (dst == src) { ++ encode_klass_not_null(src); ++ } else { ++ if (Universe::narrow_klass_base() != NULL) { ++ li(dst, (int64_t)Universe::narrow_klass_base()); ++ sub_d(dst, src, dst); ++ if (Universe::narrow_klass_shift() != 0) { ++ assert (LogKlassAlignmentInBytes == Universe::narrow_klass_shift(), "decode alg wrong"); ++ shr(dst, LogKlassAlignmentInBytes); ++ } ++ } else { ++ if (Universe::narrow_klass_shift() != 0) { ++ assert (LogKlassAlignmentInBytes == Universe::narrow_klass_shift(), "decode alg wrong"); ++ srli_d(dst, src, LogKlassAlignmentInBytes); ++ } else { ++ move(dst, src); ++ } ++ } ++ } ++} ++ ++// Function instr_size_for_decode_klass_not_null() counts the instructions ++// generated by decode_klass_not_null(register r) and reinit_heapbase(), ++// when (Universe::heap() != NULL). Hence, if the instructions they ++// generate change, then this method needs to be updated. ++int MacroAssembler::instr_size_for_decode_klass_not_null() { ++ assert (UseCompressedClassPointers, "only for compressed klass ptrs"); ++ if (Universe::narrow_klass_base() != NULL) { ++ // mov64 + addq + shlq? + mov64 (for reinit_heapbase()). ++ return (Universe::narrow_klass_shift() == 0 ? 4 * 9 : 4 * 10); ++ } else { ++ // longest load decode klass function, mov64, leaq ++ return (Universe::narrow_klass_shift() == 0 ? 4 * 0 : 4 * 1); ++ } ++} ++ ++void MacroAssembler::decode_klass_not_null(Register r) { ++ assert(UseCompressedClassPointers, "should only be used for compressed headers"); ++ assert(r != AT, "Decoding a klass in AT"); ++ // Cannot assert, unverified entry point counts instructions (see .ad file) ++ // vtableStubs also counts instructions in pd_code_size_limit. ++ // Also do not verify_oop as this is called by verify_oop. ++ if (Universe::narrow_klass_shift() != 0) { ++ assert(LogKlassAlignmentInBytes == Universe::narrow_klass_shift(), "decode alg wrong"); ++ shl(r, LogKlassAlignmentInBytes); ++ } ++ if (Universe::narrow_klass_base() != NULL) { ++ li(AT, (int64_t)Universe::narrow_klass_base()); ++ add_d(r, r, AT); ++ } ++} ++ ++void MacroAssembler::decode_klass_not_null(Register dst, Register src) { ++ assert(UseCompressedClassPointers, "should only be used for compressed headers"); ++ if (dst == src) { ++ decode_klass_not_null(dst); ++ } else { ++ // Cannot assert, unverified entry point counts instructions (see .ad file) ++ // vtableStubs also counts instructions in pd_code_size_limit. ++ // Also do not verify_oop as this is called by verify_oop. ++ li(dst, (int64_t)Universe::narrow_klass_base()); ++ if (Universe::narrow_klass_shift() != 0) { ++ assert(LogKlassAlignmentInBytes == Universe::narrow_klass_shift(), "decode alg wrong"); ++ assert(LogKlassAlignmentInBytes == Address::times_8, "klass not aligned on 64bits?"); ++ alsl_d(dst, src, dst, Address::times_8 - 1); ++ } else { ++ add_d(dst, src, dst); ++ } ++ } ++} ++ ++void MacroAssembler::reinit_heapbase() { ++ if (UseCompressedOops || UseCompressedClassPointers) { ++ if (Universe::heap() != NULL) { ++ if (Universe::narrow_oop_base() == NULL) { ++ move(S5_heapbase, R0); ++ } else { ++ li(S5_heapbase, (int64_t)Universe::narrow_ptrs_base()); ++ } ++ } else { ++ li(S5_heapbase, (intptr_t)Universe::narrow_ptrs_base_addr()); ++ ld_d(S5_heapbase, S5_heapbase, 0); ++ } ++ } ++} ++ ++void MacroAssembler::check_klass_subtype(Register sub_klass, ++ Register super_klass, ++ Register temp_reg, ++ Label& L_success) { ++//implement ind gen_subtype_check ++ Label L_failure; ++ check_klass_subtype_fast_path(sub_klass, super_klass, temp_reg, &L_success, &L_failure, NULL); ++ check_klass_subtype_slow_path(sub_klass, super_klass, temp_reg, noreg, &L_success, NULL); ++ bind(L_failure); ++} ++ ++SkipIfEqual::SkipIfEqual( ++ MacroAssembler* masm, const bool* flag_addr, bool value) { ++ _masm = masm; ++ _masm->li(AT, (address)flag_addr); ++ _masm->ld_b(AT, AT, 0); ++ _masm->addi_d(AT, AT, -value); ++ _masm->beq(AT, R0, _label); ++} ++ ++void MacroAssembler::check_klass_subtype_fast_path(Register sub_klass, ++ Register super_klass, ++ Register temp_reg, ++ Label* L_success, ++ Label* L_failure, ++ Label* L_slow_path, ++ RegisterOrConstant super_check_offset) { ++ assert_different_registers(sub_klass, super_klass, temp_reg); ++ bool must_load_sco = (super_check_offset.constant_or_zero() == -1); ++ if (super_check_offset.is_register()) { ++ assert_different_registers(sub_klass, super_klass, ++ super_check_offset.as_register()); ++ } else if (must_load_sco) { ++ assert(temp_reg != noreg, "supply either a temp or a register offset"); ++ } ++ ++ Label L_fallthrough; ++ int label_nulls = 0; ++ if (L_success == NULL) { L_success = &L_fallthrough; label_nulls++; } ++ if (L_failure == NULL) { L_failure = &L_fallthrough; label_nulls++; } ++ if (L_slow_path == NULL) { L_slow_path = &L_fallthrough; label_nulls++; } ++ assert(label_nulls <= 1, "at most one NULL in the batch"); ++ ++ int sc_offset = in_bytes(Klass::secondary_super_cache_offset()); ++ int sco_offset = in_bytes(Klass::super_check_offset_offset()); ++ // If the pointers are equal, we are done (e.g., String[] elements). ++ // This self-check enables sharing of secondary supertype arrays among ++ // non-primary types such as array-of-interface. Otherwise, each such ++ // type would need its own customized SSA. ++ // We move this check to the front of the fast path because many ++ // type checks are in fact trivially successful in this manner, ++ // so we get a nicely predicted branch right at the start of the check. ++ beq(sub_klass, super_klass, *L_success); ++ // Check the supertype display: ++ if (must_load_sco) { ++ ld_wu(temp_reg, super_klass, sco_offset); ++ super_check_offset = RegisterOrConstant(temp_reg); ++ } ++ add_d(AT, sub_klass, super_check_offset.register_or_noreg()); ++ ld_d(AT, AT, super_check_offset.constant_or_zero()); ++ ++ // This check has worked decisively for primary supers. ++ // Secondary supers are sought in the super_cache ('super_cache_addr'). ++ // (Secondary supers are interfaces and very deeply nested subtypes.) ++ // This works in the same check above because of a tricky aliasing ++ // between the super_cache and the primary super display elements. ++ // (The 'super_check_addr' can address either, as the case requires.) ++ // Note that the cache is updated below if it does not help us find ++ // what we need immediately. ++ // So if it was a primary super, we can just fail immediately. ++ // Otherwise, it's the slow path for us (no success at this point). ++ ++ if (super_check_offset.is_register()) { ++ beq(super_klass, AT, *L_success); ++ addi_d(AT, super_check_offset.as_register(), -sc_offset); ++ if (L_failure == &L_fallthrough) { ++ beq(AT, R0, *L_slow_path); ++ } else { ++ bne_far(AT, R0, *L_failure); ++ b(*L_slow_path); ++ } ++ } else if (super_check_offset.as_constant() == sc_offset) { ++ // Need a slow path; fast failure is impossible. ++ if (L_slow_path == &L_fallthrough) { ++ beq(super_klass, AT, *L_success); ++ } else { ++ bne(super_klass, AT, *L_slow_path); ++ b(*L_success); ++ } ++ } else { ++ // No slow path; it's a fast decision. ++ if (L_failure == &L_fallthrough) { ++ beq(super_klass, AT, *L_success); ++ } else { ++ bne_far(super_klass, AT, *L_failure); ++ b(*L_success); ++ } ++ } ++ ++ bind(L_fallthrough); ++} ++ ++void MacroAssembler::check_klass_subtype_slow_path(Register sub_klass, ++ Register super_klass, ++ Register temp_reg, ++ Register temp2_reg, ++ Label* L_success, ++ Label* L_failure, ++ bool set_cond_codes) { ++ if (temp2_reg == noreg) ++ temp2_reg = TSR; ++ assert_different_registers(sub_klass, super_klass, temp_reg, temp2_reg); ++#define IS_A_TEMP(reg) ((reg) == temp_reg || (reg) == temp2_reg) ++ ++ Label L_fallthrough; ++ int label_nulls = 0; ++ if (L_success == NULL) { L_success = &L_fallthrough; label_nulls++; } ++ if (L_failure == NULL) { L_failure = &L_fallthrough; label_nulls++; } ++ assert(label_nulls <= 1, "at most one NULL in the batch"); ++ ++ // a couple of useful fields in sub_klass: ++ int ss_offset = in_bytes(Klass::secondary_supers_offset()); ++ int sc_offset = in_bytes(Klass::secondary_super_cache_offset()); ++ Address secondary_supers_addr(sub_klass, ss_offset); ++ Address super_cache_addr( sub_klass, sc_offset); ++ ++ // Do a linear scan of the secondary super-klass chain. ++ // This code is rarely used, so simplicity is a virtue here. ++ // The repne_scan instruction uses fixed registers, which we must spill. ++ // Don't worry too much about pre-existing connections with the input regs. ++ ++#ifndef PRODUCT ++ int* pst_counter = &SharedRuntime::_partial_subtype_ctr; ++ ExternalAddress pst_counter_addr((address) pst_counter); ++#endif //PRODUCT ++ ++ // We will consult the secondary-super array. ++ ld_d(temp_reg, secondary_supers_addr); ++ // Load the array length. ++ ld_w(temp2_reg, Address(temp_reg, Array::length_offset_in_bytes())); ++ // Skip to start of data. ++ addi_d(temp_reg, temp_reg, Array::base_offset_in_bytes()); ++ ++ Label Loop, subtype; ++ bind(Loop); ++ beq(temp2_reg, R0, *L_failure); ++ ld_d(AT, temp_reg, 0); ++ addi_d(temp_reg, temp_reg, 1 * wordSize); ++ beq(AT, super_klass, subtype); ++ addi_d(temp2_reg, temp2_reg, -1); ++ b(Loop); ++ ++ bind(subtype); ++ st_d(super_klass, super_cache_addr); ++ if (L_success != &L_fallthrough) { ++ b(*L_success); ++ } ++ ++ // Success. Cache the super we found and proceed in triumph. ++#undef IS_A_TEMP ++ ++ bind(L_fallthrough); ++} ++ ++void MacroAssembler::get_vm_result(Register oop_result, Register java_thread) { ++ ld_d(oop_result, Address(java_thread, JavaThread::vm_result_offset())); ++ st_d(R0, Address(java_thread, JavaThread::vm_result_offset())); ++ verify_oop(oop_result, "broken oop in call_VM_base"); ++} ++ ++void MacroAssembler::get_vm_result_2(Register metadata_result, Register java_thread) { ++ ld_d(metadata_result, Address(java_thread, JavaThread::vm_result_2_offset())); ++ st_d(R0, Address(java_thread, JavaThread::vm_result_2_offset())); ++} ++ ++Address MacroAssembler::argument_address(RegisterOrConstant arg_slot, ++ int extra_slot_offset) { ++ // cf. TemplateTable::prepare_invoke(), if (load_receiver). ++ int stackElementSize = Interpreter::stackElementSize; ++ int offset = Interpreter::expr_offset_in_bytes(extra_slot_offset+0); ++#ifdef ASSERT ++ int offset1 = Interpreter::expr_offset_in_bytes(extra_slot_offset+1); ++ assert(offset1 - offset == stackElementSize, "correct arithmetic"); ++#endif ++ Register scale_reg = NOREG; ++ Address::ScaleFactor scale_factor = Address::no_scale; ++ if (arg_slot.is_constant()) { ++ offset += arg_slot.as_constant() * stackElementSize; ++ } else { ++ scale_reg = arg_slot.as_register(); ++ scale_factor = Address::times_8; ++ } ++ // We don't push RA on stack in prepare_invoke. ++ // offset += wordSize; // return PC is on stack ++ if(scale_reg==NOREG) return Address(SP, offset); ++ else { ++ alsl_d(scale_reg, scale_reg, SP, scale_factor - 1); ++ return Address(scale_reg, offset); ++ } ++} ++ ++SkipIfEqual::~SkipIfEqual() { ++ _masm->bind(_label); ++} ++ ++void MacroAssembler::load_sized_value(Register dst, Address src, size_t size_in_bytes, bool is_signed, Register dst2) { ++ switch (size_in_bytes) { ++ case 8: ld_d(dst, src); break; ++ case 4: ld_w(dst, src); break; ++ case 2: is_signed ? ld_h(dst, src) : ld_hu(dst, src); break; ++ case 1: is_signed ? ld_b( dst, src) : ld_bu( dst, src); break; ++ default: ShouldNotReachHere(); ++ } ++} ++ ++void MacroAssembler::store_sized_value(Address dst, Register src, size_t size_in_bytes, Register src2) { ++ switch (size_in_bytes) { ++ case 8: st_d(src, dst); break; ++ case 4: st_w(src, dst); break; ++ case 2: st_h(src, dst); break; ++ case 1: st_b(src, dst); break; ++ default: ShouldNotReachHere(); ++ } ++} ++ ++// Look up the method for a megamorphic invokeinterface call. ++// The target method is determined by . ++// The receiver klass is in recv_klass. ++// On success, the result will be in method_result, and execution falls through. ++// On failure, execution transfers to the given label. ++void MacroAssembler::lookup_interface_method(Register recv_klass, ++ Register intf_klass, ++ RegisterOrConstant itable_index, ++ Register method_result, ++ Register scan_temp, ++ Label& L_no_such_interface, ++ bool return_method) { ++ assert_different_registers(recv_klass, intf_klass, scan_temp, AT); ++ assert_different_registers(method_result, intf_klass, scan_temp, AT); ++ assert(recv_klass != method_result || !return_method, ++ "recv_klass can be destroyed when method isn't needed"); ++ ++ assert(itable_index.is_constant() || itable_index.as_register() == method_result, ++ "caller must use same register for non-constant itable index as for method"); ++ ++ // Compute start of first itableOffsetEntry (which is at the end of the vtable) ++ int vtable_base = InstanceKlass::vtable_start_offset() * wordSize; ++ int itentry_off = itableMethodEntry::method_offset_in_bytes(); ++ int scan_step = itableOffsetEntry::size() * wordSize; ++ int vte_size = vtableEntry::size() * wordSize; ++ Address::ScaleFactor times_vte_scale = Address::times_ptr; ++ assert(vte_size == wordSize, "else adjust times_vte_scale"); ++ ++ ld_w(scan_temp, Address(recv_klass, InstanceKlass::vtable_length_offset() * wordSize)); ++ ++ // %%% Could store the aligned, prescaled offset in the klassoop. ++ alsl_d(scan_temp, scan_temp, recv_klass, times_vte_scale - 1); ++ addi_d(scan_temp, scan_temp, vtable_base); ++ if (HeapWordsPerLong > 1) { ++ // Round up to align_object_offset boundary ++ // see code for InstanceKlass::start_of_itable! ++ round_to(scan_temp, BytesPerLong); ++ } ++ ++ if (return_method) { ++ // Adjust recv_klass by scaled itable_index, so we can free itable_index. ++ assert(itableMethodEntry::size() * wordSize == wordSize, "adjust the scaling in the code below"); ++ if (itable_index.is_constant()) { ++ li(AT, (int)itable_index.is_constant()); ++ alsl_d(AT, AT, recv_klass, (int)Address::times_ptr - 1); ++ } else { ++ alsl_d(AT, itable_index.as_register(), recv_klass, (int)Address::times_ptr - 1); ++ } ++ addi_d(recv_klass, AT, itentry_off); ++ } ++ ++ Label search, found_method; ++ ++ for (int peel = 1; peel >= 0; peel--) { ++ ld_d(method_result, Address(scan_temp, itableOffsetEntry::interface_offset_in_bytes())); ++ ++ if (peel) { ++ beq(intf_klass, method_result, found_method); ++ } else { ++ bne(intf_klass, method_result, search); ++ // (invert the test to fall through to found_method...) ++ } ++ ++ if (!peel) break; ++ ++ bind(search); ++ ++ // Check that the previous entry is non-null. A null entry means that ++ // the receiver class doesn't implement the interface, and wasn't the ++ // same as when the caller was compiled. ++ beq(method_result, R0, L_no_such_interface); ++ addi_d(scan_temp, scan_temp, scan_step); ++ } ++ ++ bind(found_method); ++ ++ if (return_method) { ++ // Got a hit. ++ ld_w(scan_temp, Address(scan_temp, itableOffsetEntry::offset_offset_in_bytes())); ++ ldx_d(method_result, recv_klass, scan_temp); ++ } ++} ++ ++// virtual method calling ++void MacroAssembler::lookup_virtual_method(Register recv_klass, ++ RegisterOrConstant vtable_index, ++ Register method_result) { ++ Register tmp = S8; ++ push(tmp); ++ ++ if (vtable_index.is_constant()) { ++ assert_different_registers(recv_klass, method_result, tmp); ++ } else { ++ assert_different_registers(recv_klass, method_result, vtable_index.as_register(), tmp); ++ } ++ const int base = InstanceKlass::vtable_start_offset() * wordSize; ++ assert(vtableEntry::size() * wordSize == wordSize, "else adjust the scaling in the code below"); ++ if (vtable_index.is_constant()) { ++ li(AT, vtable_index.as_constant()); ++ slli_d(AT, AT, (int)Address::times_ptr); ++ } else { ++ slli_d(AT, vtable_index.as_register(), (int)Address::times_ptr); ++ } ++ li(tmp, base + vtableEntry::method_offset_in_bytes()); ++ add_d(tmp, tmp, AT); ++ add_d(tmp, tmp, recv_klass); ++ ld_d(method_result, tmp, 0); ++ ++ pop(tmp); ++} ++ ++void MacroAssembler::load_byte_map_base(Register reg) { ++ jbyte *byte_map_base = ++ ((CardTableModRefBS*)(Universe::heap()->barrier_set()))->byte_map_base; ++ ++ // Strictly speaking the byte_map_base isn't an address at all, and it might ++ // even be negative. It is thus materialised as a constant. ++ li(reg, (uint64_t)byte_map_base); ++} ++ ++void MacroAssembler::clear_jweak_tag(Register possibly_jweak) { ++ const int32_t inverted_jweak_mask = ~static_cast(JNIHandles::weak_tag_mask); ++ STATIC_ASSERT(inverted_jweak_mask == -2); // otherwise check this code ++ // The inverted mask is sign-extended ++ li(AT, inverted_jweak_mask); ++ andr(possibly_jweak, AT, possibly_jweak); ++} ++ ++void MacroAssembler::resolve_jobject(Register value, ++ Register thread, ++ Register tmp) { ++ assert_different_registers(value, thread, tmp); ++ Label done, not_weak; ++ beq(value, R0, done); // Use NULL as-is. ++ li(AT, JNIHandles::weak_tag_mask); // Test for jweak tag. ++ andr(AT, value, AT); ++ beq(AT, R0, not_weak); ++ // Resolve jweak. ++ ld_d(value, value, -JNIHandles::weak_tag_value); ++ verify_oop(value); ++ #if INCLUDE_ALL_GCS ++ if (UseG1GC) { ++ g1_write_barrier_pre(noreg /* obj */, ++ value /* pre_val */, ++ thread /* thread */, ++ tmp /* tmp */, ++ true /* tosca_live */, ++ true /* expand_call */); ++ } ++ #endif // INCLUDE_ALL_GCS ++ b(done); ++ bind(not_weak); ++ // Resolve (untagged) jobject. ++ ld_d(value, value, 0); ++ verify_oop(value); ++ bind(done); ++} ++ ++void MacroAssembler::lea(Register rd, Address src) { ++ Register dst = rd; ++ Register base = src.base(); ++ Register index = src.index(); ++ ++ int scale = src.scale(); ++ int disp = src.disp(); ++ ++ if (index == noreg) { ++ if (is_simm(disp, 12)) { ++ addi_d(dst, base, disp); ++ } else { ++ lu12i_w(AT, split_low20(disp >> 12)); ++ if (split_low12(disp)) ++ ori(AT, AT, split_low12(disp)); ++ add_d(dst, base, AT); ++ } ++ } else { ++ if (scale == 0) { ++ if (is_simm(disp, 12)) { ++ add_d(AT, base, index); ++ addi_d(dst, AT, disp); ++ } else { ++ lu12i_w(AT, split_low20(disp >> 12)); ++ if (split_low12(disp)) ++ ori(AT, AT, split_low12(disp)); ++ add_d(AT, base, AT); ++ add_d(dst, AT, index); ++ } ++ } else { ++ if (is_simm(disp, 12)) { ++ alsl_d(AT, index, base, scale - 1); ++ addi_d(dst, AT, disp); ++ } else { ++ lu12i_w(AT, split_low20(disp >> 12)); ++ if (split_low12(disp)) ++ ori(AT, AT, split_low12(disp)); ++ add_d(AT, AT, base); ++ alsl_d(dst, index, AT, scale - 1); ++ } ++ } ++ } ++} ++ ++void MacroAssembler::lea(Register dst, AddressLiteral adr) { ++ code_section()->relocate(pc(), adr.rspec()); ++ pcaddi(dst, (adr.target() - pc()) >> 2); ++} ++ ++int MacroAssembler::patched_branch(int dest_pos, int inst, int inst_pos) { ++ int v = (dest_pos - inst_pos) >> 2; ++ switch(high(inst, 6)) { ++ case beq_op: ++ case bne_op: ++ case blt_op: ++ case bge_op: ++ case bltu_op: ++ case bgeu_op: ++ assert(is_simm16(v), "must be simm16"); ++#ifndef PRODUCT ++ if(!is_simm16(v)) ++ { ++ tty->print_cr("must be simm16"); ++ tty->print_cr("Inst: %x", inst); ++ } ++#endif ++ ++ inst &= 0xfc0003ff; ++ inst |= ((v & 0xffff) << 10); ++ break; ++ case beqz_op: ++ case bnez_op: ++ case bccondz_op: ++ assert(is_simm(v, 21), "must be simm21"); ++#ifndef PRODUCT ++ if(!is_simm(v, 21)) ++ { ++ tty->print_cr("must be simm21"); ++ tty->print_cr("Inst: %x", inst); ++ } ++#endif ++ ++ inst &= 0xfc0003e0; ++ inst |= ( ((v & 0xffff) << 10) | ((v >> 16) & 0x1f) ); ++ break; ++ case b_op: ++ case bl_op: ++ assert(is_simm(v, 26), "must be simm26"); ++#ifndef PRODUCT ++ if(!is_simm(v, 26)) ++ { ++ tty->print_cr("must be simm26"); ++ tty->print_cr("Inst: %x", inst); ++ } ++#endif ++ ++ inst &= 0xfc000000; ++ inst |= ( ((v & 0xffff) << 10) | ((v >> 16) & 0x3ff) ); ++ break; ++ default: ++ ShouldNotReachHere(); ++ break; ++ } ++ return inst; ++} ++ ++void MacroAssembler::cmp_cmov(Register op1, ++ Register op2, ++ Register dst, ++ Register src, ++ CMCompare cmp, ++ bool is_signed) { ++ switch (cmp) { ++ case EQ: ++ sub_d(AT, op1, op2); ++ maskeqz(dst, dst, AT); ++ masknez(AT, src, AT); ++ break; ++ ++ case NE: ++ sub_d(AT, op1, op2); ++ masknez(dst, dst, AT); ++ maskeqz(AT, src, AT); ++ break; ++ ++ case GT: ++ if (is_signed) { ++ slt(AT, op2, op1); ++ } else { ++ sltu(AT, op2, op1); ++ } ++ masknez(dst, dst, AT); ++ maskeqz(AT, src, AT); ++ break; ++ ++ case GE: ++ if (is_signed) { ++ slt(AT, op1, op2); ++ } else { ++ sltu(AT, op1, op2); ++ } ++ maskeqz(dst, dst, AT); ++ masknez(AT, src, AT); ++ break; ++ ++ case LT: ++ if (is_signed) { ++ slt(AT, op1, op2); ++ } else { ++ sltu(AT, op1, op2); ++ } ++ masknez(dst, dst, AT); ++ maskeqz(AT, src, AT); ++ break; ++ ++ case LE: ++ if (is_signed) { ++ slt(AT, op2, op1); ++ } else { ++ sltu(AT, op2, op1); ++ } ++ maskeqz(dst, dst, AT); ++ masknez(AT, src, AT); ++ break; ++ ++ default: ++ Unimplemented(); ++ } ++ OR(dst, dst, AT); ++} ++ ++ ++void MacroAssembler::cmp_cmov(FloatRegister op1, ++ FloatRegister op2, ++ Register dst, ++ Register src, ++ FloatRegister tmp1, ++ FloatRegister tmp2, ++ CMCompare cmp, ++ bool is_float) { ++ movgr2fr_d(tmp1, dst); ++ movgr2fr_d(tmp2, src); ++ ++ switch(cmp) { ++ case EQ: ++ if (is_float) { ++ fcmp_ceq_s(FCC0, op1, op2); ++ } else { ++ fcmp_ceq_d(FCC0, op1, op2); ++ } ++ fsel(tmp1, tmp1, tmp2, FCC0); ++ break; ++ ++ case NE: ++ if (is_float) { ++ fcmp_ceq_s(FCC0, op1, op2); ++ } else { ++ fcmp_ceq_d(FCC0, op1, op2); ++ } ++ fsel(tmp1, tmp2, tmp1, FCC0); ++ break; ++ ++ case GT: ++ if (is_float) { ++ fcmp_cule_s(FCC0, op1, op2); ++ } else { ++ fcmp_cule_d(FCC0, op1, op2); ++ } ++ fsel(tmp1, tmp2, tmp1, FCC0); ++ break; ++ ++ case GE: ++ if (is_float) { ++ fcmp_cult_s(FCC0, op1, op2); ++ } else { ++ fcmp_cult_d(FCC0, op1, op2); ++ } ++ fsel(tmp1, tmp2, tmp1, FCC0); ++ break; ++ ++ case LT: ++ if (is_float) { ++ fcmp_cult_s(FCC0, op1, op2); ++ } else { ++ fcmp_cult_d(FCC0, op1, op2); ++ } ++ fsel(tmp1, tmp1, tmp2, FCC0); ++ break; ++ ++ case LE: ++ if (is_float) { ++ fcmp_cule_s(FCC0, op1, op2); ++ } else { ++ fcmp_cule_d(FCC0, op1, op2); ++ } ++ fsel(tmp1, tmp1, tmp2, FCC0); ++ break; ++ ++ default: ++ Unimplemented(); ++ } ++ ++ movfr2gr_d(dst, tmp1); ++} ++ ++void MacroAssembler::cmp_cmov(FloatRegister op1, ++ FloatRegister op2, ++ FloatRegister dst, ++ FloatRegister src, ++ CMCompare cmp, ++ bool is_float) { ++ switch(cmp) { ++ case EQ: ++ if (!is_float) { ++ fcmp_ceq_d(FCC0, op1, op2); ++ } else { ++ fcmp_ceq_s(FCC0, op1, op2); ++ } ++ fsel(dst, dst, src, FCC0); ++ break; ++ ++ case NE: ++ if (!is_float) { ++ fcmp_ceq_d(FCC0, op1, op2); ++ } else { ++ fcmp_ceq_s(FCC0, op1, op2); ++ } ++ fsel(dst, src, dst, FCC0); ++ break; ++ ++ case GT: ++ if (!is_float) { ++ fcmp_cule_d(FCC0, op1, op2); ++ } else { ++ fcmp_cule_s(FCC0, op1, op2); ++ } ++ fsel(dst, src, dst, FCC0); ++ break; ++ ++ case GE: ++ if (!is_float) { ++ fcmp_cult_d(FCC0, op1, op2); ++ } else { ++ fcmp_cult_s(FCC0, op1, op2); ++ } ++ fsel(dst, src, dst, FCC0); ++ break; ++ ++ case LT: ++ if (!is_float) { ++ fcmp_cult_d(FCC0, op1, op2); ++ } else { ++ fcmp_cult_s(FCC0, op1, op2); ++ } ++ fsel(dst, dst, src, FCC0); ++ break; ++ ++ case LE: ++ if (!is_float) { ++ fcmp_cule_d(FCC0, op1, op2); ++ } else { ++ fcmp_cule_s(FCC0, op1, op2); ++ } ++ fsel(dst, dst, src, FCC0); ++ break; ++ ++ default: ++ Unimplemented(); ++ } ++} ++ ++void MacroAssembler::cmp_cmov(Register op1, ++ Register op2, ++ FloatRegister dst, ++ FloatRegister src, ++ FloatRegister tmp1, ++ FloatRegister tmp2, ++ CMCompare cmp) { ++ movgr2fr_w(tmp1, R0); ++ ++ switch (cmp) { ++ case EQ: ++ sub_d(AT, op1, op2); ++ movgr2fr_w(tmp2, AT); ++ fcmp_ceq_s(FCC0, tmp1, tmp2); ++ fsel(dst, dst, src, FCC0); ++ break; ++ ++ case NE: ++ sub_d(AT, op1, op2); ++ movgr2fr_w(tmp2, AT); ++ fcmp_ceq_s(FCC0, tmp1, tmp2); ++ fsel(dst, src, dst, FCC0); ++ break; ++ ++ case GT: ++ slt(AT, op2, op1); ++ movgr2fr_w(tmp2, AT); ++ fcmp_ceq_s(FCC0, tmp1, tmp2); ++ fsel(dst, src, dst, FCC0); ++ break; ++ ++ case GE: ++ slt(AT, op1, op2); ++ movgr2fr_w(tmp2, AT); ++ fcmp_ceq_s(FCC0, tmp1, tmp2); ++ fsel(dst, dst, src, FCC0); ++ break; ++ ++ case LT: ++ slt(AT, op1, op2); ++ movgr2fr_w(tmp2, AT); ++ fcmp_ceq_s(FCC0, tmp1, tmp2); ++ fsel(dst, src, dst, FCC0); ++ break; ++ ++ case LE: ++ slt(AT, op2, op1); ++ movgr2fr_w(tmp2, AT); ++ fcmp_ceq_s(FCC0, tmp1, tmp2); ++ fsel(dst, dst, src, FCC0); ++ break; ++ ++ default: ++ Unimplemented(); ++ } ++} ++ ++void MacroAssembler::loadstore(Register reg, Register base, int disp, int type) { ++ switch (type) { ++ case STORE_BYTE: st_b (reg, base, disp); break; ++ case STORE_CHAR: ++ case STORE_SHORT: st_h (reg, base, disp); break; ++ case STORE_INT: st_w (reg, base, disp); break; ++ case STORE_LONG: st_d (reg, base, disp); break; ++ case LOAD_BYTE: ld_b (reg, base, disp); break; ++ case LOAD_U_BYTE: ld_bu(reg, base, disp); break; ++ case LOAD_SHORT: ld_h (reg, base, disp); break; ++ case LOAD_U_SHORT: ld_hu(reg, base, disp); break; ++ case LOAD_INT: ld_w (reg, base, disp); break; ++ case LOAD_U_INT: ld_wu(reg, base, disp); break; ++ case LOAD_LONG: ld_d (reg, base, disp); break; ++ case LOAD_LINKED_LONG: ++ ll_d(reg, base, disp); ++ break; ++ default: ++ ShouldNotReachHere(); ++ } ++} ++ ++void MacroAssembler::loadstore(Register reg, Register base, Register disp, int type) { ++ switch (type) { ++ case STORE_BYTE: stx_b (reg, base, disp); break; ++ case STORE_CHAR: ++ case STORE_SHORT: stx_h (reg, base, disp); break; ++ case STORE_INT: stx_w (reg, base, disp); break; ++ case STORE_LONG: stx_d (reg, base, disp); break; ++ case LOAD_BYTE: ldx_b (reg, base, disp); break; ++ case LOAD_U_BYTE: ldx_bu(reg, base, disp); break; ++ case LOAD_SHORT: ldx_h (reg, base, disp); break; ++ case LOAD_U_SHORT: ldx_hu(reg, base, disp); break; ++ case LOAD_INT: ldx_w (reg, base, disp); break; ++ case LOAD_U_INT: ldx_wu(reg, base, disp); break; ++ case LOAD_LONG: ldx_d (reg, base, disp); break; ++ case LOAD_LINKED_LONG: ++ add_d(AT, base, disp); ++ ll_d(reg, AT, 0); ++ break; ++ default: ++ ShouldNotReachHere(); ++ } ++} ++ ++void MacroAssembler::loadstore(FloatRegister reg, Register base, int disp, int type) { ++ switch (type) { ++ case STORE_FLOAT: fst_s(reg, base, disp); break; ++ case STORE_DOUBLE: fst_d(reg, base, disp); break; ++ case STORE_VECTORX: vst (reg, base, disp); break; ++ case STORE_VECTORY: xvst (reg, base, disp); break; ++ case LOAD_FLOAT: fld_s(reg, base, disp); break; ++ case LOAD_DOUBLE: fld_d(reg, base, disp); break; ++ case LOAD_VECTORX: vld (reg, base, disp); break; ++ case LOAD_VECTORY: xvld (reg, base, disp); break; ++ default: ++ ShouldNotReachHere(); ++ } ++} ++ ++void MacroAssembler::loadstore(FloatRegister reg, Register base, Register disp, int type) { ++ switch (type) { ++ case STORE_FLOAT: fstx_s(reg, base, disp); break; ++ case STORE_DOUBLE: fstx_d(reg, base, disp); break; ++ case STORE_VECTORX: vstx (reg, base, disp); break; ++ case STORE_VECTORY: xvstx (reg, base, disp); break; ++ case LOAD_FLOAT: fldx_s(reg, base, disp); break; ++ case LOAD_DOUBLE: fldx_d(reg, base, disp); break; ++ case LOAD_VECTORX: vldx (reg, base, disp); break; ++ case LOAD_VECTORY: xvldx (reg, base, disp); break; ++ default: ++ ShouldNotReachHere(); ++ } ++} ++ ++/** ++ * Emits code to update CRC-32 with a byte value according to constants in table ++ * ++ * @param [in,out]crc Register containing the crc. ++ * @param [in]val Register containing the byte to fold into the CRC. ++ * @param [in]table Register containing the table of crc constants. ++ * ++ * uint32_t crc; ++ * val = crc_table[(val ^ crc) & 0xFF]; ++ * crc = val ^ (crc >> 8); ++**/ ++void MacroAssembler::update_byte_crc32(Register crc, Register val, Register table) { ++ xorr(val, val, crc); ++ andi(val, val, 0xff); ++ ld_w(val, Address(table, val, Address::times_4, 0)); ++ srli_w(crc, crc, 8); ++ xorr(crc, val, crc); ++} ++ ++/** ++ * @param crc register containing existing CRC (32-bit) ++ * @param buf register pointing to input byte buffer (byte*) ++ * @param len register containing number of bytes ++ * @param tmp scratch register ++**/ ++void MacroAssembler::kernel_crc32(Register crc, Register buf, Register len, Register tmp) { ++ Label CRC_by64_loop, CRC_by4_loop, CRC_by1_loop, CRC_less64, CRC_by64_pre, CRC_by32_loop, CRC_less32, L_exit; ++ assert_different_registers(crc, buf, len, tmp); ++ ++ nor(crc, crc, R0); ++ ++ addi_d(len, len, -64); ++ bge(len, R0, CRC_by64_loop); ++ addi_d(len, len, 64-4); ++ bge(len, R0, CRC_by4_loop); ++ addi_d(len, len, 4); ++ blt(R0, len, CRC_by1_loop); ++ b(L_exit); ++ ++ bind(CRC_by64_loop); ++ ld_d(tmp, buf, 0); ++ crc_w_d_w(crc, tmp, crc); ++ ld_d(tmp, buf, 8); ++ crc_w_d_w(crc, tmp, crc); ++ ld_d(tmp, buf, 16); ++ crc_w_d_w(crc, tmp, crc); ++ ld_d(tmp, buf, 24); ++ crc_w_d_w(crc, tmp, crc); ++ ld_d(tmp, buf, 32); ++ crc_w_d_w(crc, tmp, crc); ++ ld_d(tmp, buf, 40); ++ crc_w_d_w(crc, tmp, crc); ++ ld_d(tmp, buf, 48); ++ crc_w_d_w(crc, tmp, crc); ++ ld_d(tmp, buf, 56); ++ crc_w_d_w(crc, tmp, crc); ++ addi_d(buf, buf, 64); ++ addi_d(len, len, -64); ++ bge(len, R0, CRC_by64_loop); ++ addi_d(len, len, 64-4); ++ bge(len, R0, CRC_by4_loop); ++ addi_d(len, len, 4); ++ blt(R0, len, CRC_by1_loop); ++ b(L_exit); ++ ++ bind(CRC_by4_loop); ++ ld_w(tmp, buf, 0); ++ crc_w_w_w(crc, tmp, crc); ++ addi_d(buf, buf, 4); ++ addi_d(len, len, -4); ++ bge(len, R0, CRC_by4_loop); ++ addi_d(len, len, 4); ++ bge(R0, len, L_exit); ++ ++ bind(CRC_by1_loop); ++ ld_b(tmp, buf, 0); ++ crc_w_b_w(crc, tmp, crc); ++ addi_d(buf, buf, 1); ++ addi_d(len, len, -1); ++ blt(R0, len, CRC_by1_loop); ++ ++ bind(L_exit); ++ nor(crc, crc, R0); ++} ++ ++/** ++ * @param crc register containing existing CRC (32-bit) ++ * @param buf register pointing to input byte buffer (byte*) ++ * @param len register containing number of bytes ++ * @param tmp scratch register ++**/ ++void MacroAssembler::kernel_crc32c(Register crc, Register buf, Register len, Register tmp) { ++ Label CRC_by64_loop, CRC_by4_loop, CRC_by1_loop, CRC_less64, CRC_by64_pre, CRC_by32_loop, CRC_less32, L_exit; ++ assert_different_registers(crc, buf, len, tmp); ++ ++ addi_d(len, len, -64); ++ bge(len, R0, CRC_by64_loop); ++ addi_d(len, len, 64-4); ++ bge(len, R0, CRC_by4_loop); ++ addi_d(len, len, 4); ++ blt(R0, len, CRC_by1_loop); ++ b(L_exit); ++ ++ bind(CRC_by64_loop); ++ ld_d(tmp, buf, 0); ++ crcc_w_d_w(crc, tmp, crc); ++ ld_d(tmp, buf, 8); ++ crcc_w_d_w(crc, tmp, crc); ++ ld_d(tmp, buf, 16); ++ crcc_w_d_w(crc, tmp, crc); ++ ld_d(tmp, buf, 24); ++ crcc_w_d_w(crc, tmp, crc); ++ ld_d(tmp, buf, 32); ++ crcc_w_d_w(crc, tmp, crc); ++ ld_d(tmp, buf, 40); ++ crcc_w_d_w(crc, tmp, crc); ++ ld_d(tmp, buf, 48); ++ crcc_w_d_w(crc, tmp, crc); ++ ld_d(tmp, buf, 56); ++ crcc_w_d_w(crc, tmp, crc); ++ addi_d(buf, buf, 64); ++ addi_d(len, len, -64); ++ bge(len, R0, CRC_by64_loop); ++ addi_d(len, len, 64-4); ++ bge(len, R0, CRC_by4_loop); ++ addi_d(len, len, 4); ++ blt(R0, len, CRC_by1_loop); ++ b(L_exit); ++ ++ bind(CRC_by4_loop); ++ ld_w(tmp, buf, 0); ++ crcc_w_w_w(crc, tmp, crc); ++ addi_d(buf, buf, 4); ++ addi_d(len, len, -4); ++ bge(len, R0, CRC_by4_loop); ++ addi_d(len, len, 4); ++ bge(R0, len, L_exit); ++ ++ bind(CRC_by1_loop); ++ ld_b(tmp, buf, 0); ++ crcc_w_b_w(crc, tmp, crc); ++ addi_d(buf, buf, 1); ++ addi_d(len, len, -1); ++ blt(R0, len, CRC_by1_loop); ++ ++ bind(L_exit); ++} +diff --git a/hotspot/src/cpu/loongarch/vm/macroAssembler_loongarch.hpp b/hotspot/src/cpu/loongarch/vm/macroAssembler_loongarch.hpp +new file mode 100644 +index 0000000000..8b123c2906 +--- /dev/null ++++ b/hotspot/src/cpu/loongarch/vm/macroAssembler_loongarch.hpp +@@ -0,0 +1,771 @@ ++/* ++ * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2015, 2022, Loongson Technology. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ * ++ */ ++ ++#ifndef CPU_LOONGARCH_VM_MACROASSEMBLER_LOONGARCH_HPP ++#define CPU_LOONGARCH_VM_MACROASSEMBLER_LOONGARCH_HPP ++ ++#include "asm/assembler.hpp" ++#include "utilities/macros.hpp" ++#include "runtime/rtmLocking.hpp" ++ ++ ++// MacroAssembler extends Assembler by frequently used macros. ++// ++// Instructions for which a 'better' code sequence exists depending ++// on arguments should also go in here. ++ ++class MacroAssembler: public Assembler { ++ friend class LIR_Assembler; ++ friend class Runtime1; // as_Address() ++ ++ public: ++ // Compare code ++ typedef enum { ++ EQ = 0x01, ++ NE = 0x02, ++ GT = 0x03, ++ GE = 0x04, ++ LT = 0x05, ++ LE = 0x06 ++ } CMCompare; ++ ++ protected: ++ ++ Address as_Address(AddressLiteral adr); ++ Address as_Address(ArrayAddress adr); ++ ++ // Support for VM calls ++ // ++ // This is the base routine called by the different versions of call_VM_leaf. The interpreter ++ // may customize this version by overriding it for its purposes (e.g., to save/restore ++ // additional registers when doing a VM call). ++#ifdef CC_INTERP ++ // c++ interpreter never wants to use interp_masm version of call_VM ++ #define VIRTUAL ++#else ++ #define VIRTUAL virtual ++#endif ++ ++ VIRTUAL void call_VM_leaf_base( ++ address entry_point, // the entry point ++ int number_of_arguments // the number of arguments to pop after the call ++ ); ++ ++ // This is the base routine called by the different versions of call_VM. The interpreter ++ // may customize this version by overriding it for its purposes (e.g., to save/restore ++ // additional registers when doing a VM call). ++ // ++ // If no java_thread register is specified (noreg) than TREG will be used instead. call_VM_base ++ // returns the register which contains the thread upon return. If a thread register has been ++ // specified, the return value will correspond to that register. If no last_java_sp is specified ++ // (noreg) than sp will be used instead. ++ VIRTUAL void call_VM_base( // returns the register containing the thread upon return ++ Register oop_result, // where an oop-result ends up if any; use noreg otherwise ++ Register java_thread, // the thread if computed before ; use noreg otherwise ++ Register last_java_sp, // to set up last_Java_frame in stubs; use noreg otherwise ++ address entry_point, // the entry point ++ int number_of_arguments, // the number of arguments (w/o thread) to pop after the call ++ bool check_exceptions // whether to check for pending exceptions after return ++ ); ++ ++ // These routines should emit JVMTI PopFrame and ForceEarlyReturn handling code. ++ // The implementation is only non-empty for the InterpreterMacroAssembler, ++ // as only the interpreter handles PopFrame and ForceEarlyReturn requests. ++ virtual void check_and_handle_popframe(Register java_thread); ++ virtual void check_and_handle_earlyret(Register java_thread); ++ ++ void call_VM_helper(Register oop_result, address entry_point, int number_of_arguments, bool check_exceptions = true); ++ ++ // helpers for FPU flag access ++ // tmp is a temporary register, if none is available use noreg ++ ++ public: ++ static intptr_t i[32]; ++ static float f[32]; ++ static void print(outputStream *s); ++ ++ static int i_offset(unsigned int k); ++ static int f_offset(unsigned int k); ++ ++ static void save_registers(MacroAssembler *masm); ++ static void restore_registers(MacroAssembler *masm); ++ ++ MacroAssembler(CodeBuffer* code) : Assembler(code) {} ++ ++ // Support for NULL-checks ++ // ++ // Generates code that causes a NULL OS exception if the content of reg is NULL. ++ // If the accessed location is M[reg + offset] and the offset is known, provide the ++ // offset. No explicit code generation is needed if the offset is within a certain ++ // range (0 <= offset <= page_size). ++ ++ void null_check(Register reg, int offset = -1); ++ static bool needs_explicit_null_check(intptr_t offset); ++ ++ // Required platform-specific helpers for Label::patch_instructions. ++ // They _shadow_ the declarations in AbstractAssembler, which are undefined. ++ void pd_patch_instruction(address branch, address target); ++ ++ address emit_trampoline_stub(int insts_call_instruction_offset, address target); ++ ++ // Support for inc/dec with optimal instruction selection depending on value ++ // void incrementl(Register reg, int value = 1); ++ // void decrementl(Register reg, int value = 1); ++ ++ ++ // Alignment ++ void align(int modulus); ++ ++ ++ // Stack frame creation/removal ++ void enter(); ++ void leave(); ++ ++ // Frame creation and destruction shared between JITs. ++ void build_frame(int framesize); ++ void remove_frame(int framesize); ++ ++ // Support for getting the JavaThread pointer (i.e.; a reference to thread-local information) ++ // The pointer will be loaded into the thread register. ++ void get_thread(Register thread); ++ ++ ++ // Support for VM calls ++ // ++ // It is imperative that all calls into the VM are handled via the call_VM macros. ++ // They make sure that the stack linkage is setup correctly. call_VM's correspond ++ // to ENTRY/ENTRY_X entry points while call_VM_leaf's correspond to LEAF entry points. ++ ++ ++ void call_VM(Register oop_result, ++ address entry_point, ++ bool check_exceptions = true); ++ void call_VM(Register oop_result, ++ address entry_point, ++ Register arg_1, ++ bool check_exceptions = true); ++ void call_VM(Register oop_result, ++ address entry_point, ++ Register arg_1, Register arg_2, ++ bool check_exceptions = true); ++ void call_VM(Register oop_result, ++ address entry_point, ++ Register arg_1, Register arg_2, Register arg_3, ++ bool check_exceptions = true); ++ ++ // Overloadings with last_Java_sp ++ void call_VM(Register oop_result, ++ Register last_java_sp, ++ address entry_point, ++ int number_of_arguments = 0, ++ bool check_exceptions = true); ++ void call_VM(Register oop_result, ++ Register last_java_sp, ++ address entry_point, ++ Register arg_1, bool ++ check_exceptions = true); ++ void call_VM(Register oop_result, ++ Register last_java_sp, ++ address entry_point, ++ Register arg_1, Register arg_2, ++ bool check_exceptions = true); ++ void call_VM(Register oop_result, ++ Register last_java_sp, ++ address entry_point, ++ Register arg_1, Register arg_2, Register arg_3, ++ bool check_exceptions = true); ++ ++ void get_vm_result (Register oop_result, Register thread); ++ void get_vm_result_2(Register metadata_result, Register thread); ++ void call_VM_leaf(address entry_point, ++ int number_of_arguments = 0); ++ void call_VM_leaf(address entry_point, ++ Register arg_1); ++ void call_VM_leaf(address entry_point, ++ Register arg_1, Register arg_2); ++ void call_VM_leaf(address entry_point, ++ Register arg_1, Register arg_2, Register arg_3); ++ ++ // Super call_VM calls - correspond to MacroAssembler::call_VM(_leaf) calls ++ void super_call_VM_leaf(address entry_point); ++ void super_call_VM_leaf(address entry_point, Register arg_1); ++ void super_call_VM_leaf(address entry_point, Register arg_1, Register arg_2); ++ void super_call_VM_leaf(address entry_point, Register arg_1, Register arg_2, Register arg_3); ++ ++ // last Java Frame (fills frame anchor) ++ void set_last_Java_frame(Register thread, ++ Register last_java_sp, ++ Register last_java_fp, ++ Label& last_java_pc); ++ ++ // thread in the default location (S6) ++ void set_last_Java_frame(Register last_java_sp, ++ Register last_java_fp, ++ Label& last_java_pc); ++ ++ void reset_last_Java_frame(Register thread, bool clear_fp); ++ ++ // thread in the default location (S6) ++ void reset_last_Java_frame(bool clear_fp); ++ ++ // Stores ++ void store_check(Register obj); // store check for obj - register is destroyed afterwards ++ void store_check(Register obj, Address dst); // same as above, dst is exact store location (reg. is destroyed) ++ ++ void resolve_jobject(Register value, Register thread, Register tmp); ++ void clear_jweak_tag(Register possibly_jweak); ++ ++#if INCLUDE_ALL_GCS ++ ++ void g1_write_barrier_pre(Register obj, ++ Register pre_val, ++ Register thread, ++ Register tmp, ++ bool tosca_live, ++ bool expand_call); ++ ++ void g1_write_barrier_post(Register store_addr, ++ Register new_val, ++ Register thread, ++ Register tmp, ++ Register tmp2); ++ ++#endif // INCLUDE_ALL_GCS ++ ++ // split store_check(Register obj) to enhance instruction interleaving ++ void store_check_part_1(Register obj); ++ void store_check_part_2(Register obj); ++ ++ // C 'boolean' to Java boolean: x == 0 ? 0 : 1 ++ void c2bool(Register x); ++ //add for compressedoops ++ void load_klass(Register dst, Register src); ++ void store_klass(Register dst, Register src); ++ void load_prototype_header(Register dst, Register src); ++ ++ void store_klass_gap(Register dst, Register src); ++ ++ void load_heap_oop(Register dst, Address src); ++ void store_heap_oop(Address dst, Register src); ++ void store_heap_oop_null(Address dst); ++ void encode_heap_oop(Register r); ++ void encode_heap_oop(Register dst, Register src); ++ void decode_heap_oop(Register r); ++ void decode_heap_oop(Register dst, Register src); ++ void encode_heap_oop_not_null(Register r); ++ void decode_heap_oop_not_null(Register r); ++ void encode_heap_oop_not_null(Register dst, Register src); ++ void decode_heap_oop_not_null(Register dst, Register src); ++ ++ void encode_klass_not_null(Register r); ++ void decode_klass_not_null(Register r); ++ void encode_klass_not_null(Register dst, Register src); ++ void decode_klass_not_null(Register dst, Register src); ++ ++ // Returns the byte size of the instructions generated by decode_klass_not_null() ++ // when compressed klass pointers are being used. ++ static int instr_size_for_decode_klass_not_null(); ++ ++ // if heap base register is used - reinit it with the correct value ++ void reinit_heapbase(); ++ ++ DEBUG_ONLY(void verify_heapbase(const char* msg);) ++ ++ void set_narrow_klass(Register dst, Klass* k); ++ void set_narrow_oop(Register dst, jobject obj); ++ ++ // Sign extension ++ void sign_extend_short(Register reg) { ext_w_h(reg, reg); } ++ void sign_extend_byte(Register reg) { ext_w_b(reg, reg); } ++ void rem_s(FloatRegister fd, FloatRegister fs, FloatRegister ft, FloatRegister tmp); ++ void rem_d(FloatRegister fd, FloatRegister fs, FloatRegister ft, FloatRegister tmp); ++ ++ void trigfunc(char trig, int num_fpu_regs_in_use = 1); ++ // allocation ++ void eden_allocate( ++ Register obj, // result: pointer to object after successful allocation ++ Register var_size_in_bytes, // object size in bytes if unknown at compile time; invalid otherwise ++ int con_size_in_bytes, // object size in bytes if known at compile time ++ Register t1, // temp register ++ Label& slow_case // continuation point if fast allocation fails ++ ); ++ void tlab_allocate( ++ Register obj, // result: pointer to object after successful allocation ++ Register var_size_in_bytes, // object size in bytes if unknown at compile time; invalid otherwise ++ int con_size_in_bytes, // object size in bytes if known at compile time ++ Register t1, // temp register ++ Register t2, // temp register ++ Label& slow_case // continuation point if fast allocation fails ++ ); ++ void incr_allocated_bytes(Register thread, ++ Register var_size_in_bytes, int con_size_in_bytes, ++ Register t1 = noreg); ++ // interface method calling ++ void lookup_interface_method(Register recv_klass, ++ Register intf_klass, ++ RegisterOrConstant itable_index, ++ Register method_result, ++ Register scan_temp, ++ Label& no_such_interface, ++ bool return_method = true); ++ ++ // virtual method calling ++ void lookup_virtual_method(Register recv_klass, ++ RegisterOrConstant vtable_index, ++ Register method_result); ++ ++ // Test sub_klass against super_klass, with fast and slow paths. ++ ++ // The fast path produces a tri-state answer: yes / no / maybe-slow. ++ // One of the three labels can be NULL, meaning take the fall-through. ++ // If super_check_offset is -1, the value is loaded up from super_klass. ++ // No registers are killed, except temp_reg. ++ void check_klass_subtype_fast_path(Register sub_klass, ++ Register super_klass, ++ Register temp_reg, ++ Label* L_success, ++ Label* L_failure, ++ Label* L_slow_path, ++ RegisterOrConstant super_check_offset = RegisterOrConstant(-1)); ++ ++ // The rest of the type check; must be wired to a corresponding fast path. ++ // It does not repeat the fast path logic, so don't use it standalone. ++ // The temp_reg and temp2_reg can be noreg, if no temps are available. ++ // Updates the sub's secondary super cache as necessary. ++ // If set_cond_codes, condition codes will be Z on success, NZ on failure. ++ void check_klass_subtype_slow_path(Register sub_klass, ++ Register super_klass, ++ Register temp_reg, ++ Register temp2_reg, ++ Label* L_success, ++ Label* L_failure, ++ bool set_cond_codes = false); ++ ++ // Simplified, combined version, good for typical uses. ++ // Falls through on failure. ++ void check_klass_subtype(Register sub_klass, ++ Register super_klass, ++ Register temp_reg, ++ Label& L_success); ++ ++ ++ // Debugging ++ ++ // only if +VerifyOops ++ void verify_oop(Register reg, const char* s = "broken oop"); ++ void verify_oop_addr(Address addr, const char * s = "broken oop addr"); ++ void verify_oop_subroutine(); ++ // TODO: verify method and klass metadata (compare against vptr?) ++ void _verify_method_ptr(Register reg, const char * msg, const char * file, int line) {} ++ void _verify_klass_ptr(Register reg, const char * msg, const char * file, int line){} ++ ++ #define verify_method_ptr(reg) _verify_method_ptr(reg, "broken method " #reg, __FILE__, __LINE__) ++ #define verify_klass_ptr(reg) _verify_klass_ptr(reg, "broken klass " #reg, __FILE__, __LINE__) ++ ++ // only if +VerifyFPU ++ void verify_FPU(int stack_depth, const char* s = "illegal FPU state"); ++ ++ // prints msg, dumps registers and stops execution ++ void stop(const char* msg); ++ ++ // prints msg and continues ++ void warn(const char* msg); ++ ++ static void debug(char* msg/*, RegistersForDebugging* regs*/); ++ static void debug64(char* msg, int64_t pc, int64_t regs[]); ++ ++ void untested() { stop("untested"); } ++ ++ void unimplemented(const char* what = "") { char* b = new char[1024]; jio_snprintf(b, sizeof(b), "unimplemented: %s", what); stop(b); } ++ ++ void should_not_reach_here() { stop("should not reach here"); } ++ ++ void print_CPU_state(); ++ ++ // Stack overflow checking ++ void bang_stack_with_offset(int offset) { ++ // stack grows down, caller passes positive offset ++ assert(offset > 0, "must bang with negative offset"); ++ if (offset <= 2048) { ++ st_w(RA0, SP, -offset); ++ } else if (offset <= 32768 && !(offset & 3)) { ++ stptr_w(RA0, SP, -offset); ++ } else { ++ li(AT, offset); ++ sub_d(AT, SP, AT); ++ st_w(RA0, AT, 0); ++ } ++ } ++ ++ // Writes to stack successive pages until offset reached to check for ++ // stack overflow + shadow pages. Also, clobbers tmp ++ void bang_stack_size(Register size, Register tmp); ++ ++ virtual RegisterOrConstant delayed_value_impl(intptr_t* delayed_value_addr, ++ Register tmp, ++ int offset); ++ ++ // Support for serializing memory accesses between threads ++ void serialize_memory(Register thread, Register tmp); ++ ++ //void verify_tlab(); ++ void verify_tlab(Register t1, Register t2); ++ ++ // Biased locking support ++ // lock_reg and obj_reg must be loaded up with the appropriate values. ++ // tmp_reg is optional. If it is supplied (i.e., != noreg) it will ++ // be killed; if not supplied, push/pop will be used internally to ++ // allocate a temporary (inefficient, avoid if possible). ++ // Optional slow case is for implementations (interpreter and C1) which branch to ++ // slow case directly. Leaves condition codes set for C2's Fast_Lock node. ++ // Returns offset of first potentially-faulting instruction for null ++ // check info (currently consumed only by C1). If ++ // swap_reg_contains_mark is true then returns -1 as it is assumed ++ // the calling code has already passed any potential faults. ++ int biased_locking_enter(Register lock_reg, Register obj_reg, ++ Register swap_reg, Register tmp_reg, ++ bool swap_reg_contains_mark, ++ Label& done, Label* slow_case = NULL, ++ BiasedLockingCounters* counters = NULL); ++ void biased_locking_exit (Register obj_reg, Register temp_reg, Label& done); ++#ifdef COMPILER2 ++ void fast_lock(Register obj, Register box, Register res, Register tmp, Register scr); ++ void fast_unlock(Register obj, Register box, Register res, Register tmp, Register scr); ++#endif ++ ++ void round_to(Register reg, int modulus) { ++ //TODO: LA ++ guarantee(0, "LA not implemented yet"); ++#if 0 ++ assert_different_registers(reg, AT); ++ increment(reg, modulus - 1); ++ move(AT, - modulus); ++ andr(reg, reg, AT); ++#endif ++ } ++ ++ // the follow two might use AT register, be sure you have no meanful data in AT before you call them ++ void increment(Register reg, int imm); ++ void decrement(Register reg, int imm); ++ void increment(Address addr, int imm = 1); ++ void decrement(Address addr, int imm = 1); ++ void shl(Register reg, int sa) { slli_d(reg, reg, sa); } ++ void shr(Register reg, int sa) { srli_d(reg, reg, sa); } ++ void sar(Register reg, int sa) { srai_d(reg, reg, sa); } ++ // Helper functions for statistics gathering. ++ void atomic_inc32(address counter_addr, int inc, Register tmp_reg1, Register tmp_reg2); ++ ++ // Calls ++ void call(address entry); ++ void call(address entry, relocInfo::relocType rtype); ++ void call(address entry, RelocationHolder& rh); ++ void call_long(address entry); ++ ++ address trampoline_call(AddressLiteral entry, CodeBuffer *cbuf = NULL); ++ ++ static const unsigned long branch_range = NOT_DEBUG(128 * M) DEBUG_ONLY(2 * M); ++ ++ static bool far_branches() { ++ if (ForceUnreachable) { ++ return true; ++ } else { ++ return ReservedCodeCacheSize > branch_range; ++ } ++ } ++ ++ // Emit the CompiledIC call idiom ++ address ic_call(address entry); ++ ++ // Jumps ++ void jmp(address entry); ++ void jmp(address entry, relocInfo::relocType rtype); ++ void jmp_far(Label& L); // patchable ++ ++ /* branches may exceed 16-bit offset */ ++ void b_far(address entry); ++ void b_far(Label& L); ++ ++ void bne_far (Register rs, Register rt, address entry); ++ void bne_far (Register rs, Register rt, Label& L); ++ ++ void beq_far (Register rs, Register rt, address entry); ++ void beq_far (Register rs, Register rt, Label& L); ++ ++ void blt_far (Register rs, Register rt, address entry, bool is_signed); ++ void blt_far (Register rs, Register rt, Label& L, bool is_signed); ++ ++ void bge_far (Register rs, Register rt, address entry, bool is_signed); ++ void bge_far (Register rs, Register rt, Label& L, bool is_signed); ++ ++ // For C2 to support long branches ++ void beq_long (Register rs, Register rt, Label& L); ++ void bne_long (Register rs, Register rt, Label& L); ++ void blt_long (Register rs, Register rt, Label& L, bool is_signed); ++ void bge_long (Register rs, Register rt, Label& L, bool is_signed); ++ void bc1t_long (Label& L); ++ void bc1f_long (Label& L); ++ ++ static bool patchable_branches() { ++ const unsigned long branch_range = NOT_DEBUG(128 * M) DEBUG_ONLY(2 * M); ++ return ReservedCodeCacheSize > branch_range; ++ } ++ ++ static bool reachable_from_branch_short(jlong offs); ++ ++ void patchable_jump_far(Register ra, jlong offs); ++ void patchable_jump(address target, bool force_patchable = false); ++ void patchable_call(address target, address call_size = 0); ++ ++ // Floating ++ // Data ++ ++ // Load and store values by size and signed-ness ++ void load_sized_value(Register dst, Address src, size_t size_in_bytes, bool is_signed, Register dst2 = noreg); ++ void store_sized_value(Address dst, Register src, size_t size_in_bytes, Register src2 = noreg); ++ ++ // ld_ptr will perform lw for 32 bit VMs and ld for 64 bit VMs ++ inline void ld_ptr(Register rt, Address a) { ++ ld_d(rt, a); ++ } ++ ++ inline void ld_ptr(Register rt, Register base, int offset16) { ++ ld_d(rt, base, offset16); ++ } ++ ++ // st_ptr will perform sw for 32 bit VMs and sd for 64 bit VMs ++ inline void st_ptr(Register rt, Address a) { ++ st_d(rt, a); ++ } ++ ++ inline void st_ptr(Register rt, Register base, int offset16) { ++ st_d(rt, base, offset16); ++ } ++ ++ void ld_ptr(Register rt, Register base, Register offset); ++ void st_ptr(Register rt, Register base, Register offset); ++ ++ // ld_long will perform lw for 32 bit VMs and ld for 64 bit VMs ++ // st_long will perform sw for 32 bit VMs and sd for 64 bit VMs ++ inline void ld_long(Register rt, Register base, int offset16); ++ inline void st_long(Register rt, Register base, int offset16); ++ inline void ld_long(Register rt, Address a); ++ inline void st_long(Register rt, Address a); ++ void ld_long(Register rt, Register offset, Register base); ++ void st_long(Register rt, Register offset, Register base); ++ ++ // swap the two byte of the low 16-bit halfword ++ // this directive will use AT, be sure the high 16-bit of reg is zero ++ void hswap(Register reg); ++ void huswap(Register reg); ++ ++ // convert big endian integer to little endian integer ++ void swap(Register reg); ++ ++ void cmpxchg(Address addr, Register oldval, Register newval, Register resflag, ++ bool retold, bool barrier); ++ void cmpxchg(Address addr, Register oldval, Register newval, Register tmp, ++ bool retold, bool barrier, Label& succ, Label* fail = NULL); ++ void cmpxchg32(Address addr, Register oldval, Register newval, Register resflag, ++ bool sign, bool retold, bool barrier); ++ void cmpxchg32(Address addr, Register oldval, Register newval, Register tmp, ++ bool sign, bool retold, bool barrier, Label& succ, Label* fail = NULL); ++ ++ void extend_sign(Register rh, Register rl) { /*stop("extend_sign");*/ guarantee(0, "LA not implemented yet");} ++ void neg(Register reg) { /*dsubu(reg, R0, reg);*/ guarantee(0, "LA not implemented yet");} ++ void push (Register reg) { addi_d(SP, SP, -8); st_d (reg, SP, 0); } ++ void push (FloatRegister reg) { addi_d(SP, SP, -8); fst_d (reg, SP, 0); } ++ void pop (Register reg) { ld_d (reg, SP, 0); addi_d(SP, SP, 8); } ++ void pop (FloatRegister reg) { fld_d (reg, SP, 0); addi_d(SP, SP, 8); } ++ void pop () { addi_d(SP, SP, 8); } ++ void pop2 () { addi_d(SP, SP, 16); } ++ void push2(Register reg1, Register reg2); ++ void pop2 (Register reg1, Register reg2); ++ //we need 2 fun to save and resotre general register ++ void pushad(); ++ void popad(); ++ void pushad_except_v0(); ++ void popad_except_v0(); ++ ++ void li(Register rd, jlong value); ++ void li(Register rd, address addr) { li(rd, (long)addr); } ++ void patchable_li52(Register rd, jlong value); ++ void lipc(Register rd, Label& L); ++ void move(Register rd, Register rs) { orr(rd, rs, R0); } ++ void move_u32(Register rd, Register rs) { add_w(rd, rs, R0); } ++ void mov_metadata(Register dst, Metadata* obj); ++ void mov_metadata(Address dst, Metadata* obj); ++ ++ // Load the base of the cardtable byte map into reg. ++ void load_byte_map_base(Register reg); ++ ++ //FIXME ++ void empty_FPU_stack(){/*need implemented*/}; ++ ++ ++ // method handles (JSR 292) ++ Address argument_address(RegisterOrConstant arg_slot, int extra_slot_offset = 0); ++ ++ ++ // LA added: ++ void jr (Register reg) { jirl(R0, reg, 0); } ++ void jalr(Register reg) { jirl(RA, reg, 0); } ++ void nop () { andi(R0, R0, 0); } ++ void andr(Register rd, Register rj, Register rk) { AND(rd, rj, rk); } ++ void xorr(Register rd, Register rj, Register rk) { XOR(rd, rj, rk); } ++ void orr (Register rd, Register rj, Register rk) { OR(rd, rj, rk); } ++ void lea (Register rd, Address src); ++ void lea (Register dst, AddressLiteral adr); ++ static int patched_branch(int dest_pos, int inst, int inst_pos); ++ ++ // Conditional move ++ void cmp_cmov(Register op1, ++ Register op2, ++ Register dst, ++ Register src, ++ CMCompare cmp = EQ, ++ bool is_signed = true); ++ void cmp_cmov(FloatRegister op1, ++ FloatRegister op2, ++ Register dst, ++ Register src, ++ FloatRegister tmp1, ++ FloatRegister tmp2, ++ CMCompare cmp = EQ, ++ bool is_float = true); ++ void cmp_cmov(FloatRegister op1, ++ FloatRegister op2, ++ FloatRegister dst, ++ FloatRegister src, ++ CMCompare cmp = EQ, ++ bool is_float = true); ++ void cmp_cmov(Register op1, ++ Register op2, ++ FloatRegister dst, ++ FloatRegister src, ++ FloatRegister tmp1, ++ FloatRegister tmp2, ++ CMCompare cmp = EQ); ++ ++ // CRC32 code for java.util.zip.CRC32::update() instrinsic. ++ void update_byte_crc32(Register crc, Register val, Register table); ++ ++ // CRC32 code for java.util.zip.CRC32::updateBytes() instrinsic. ++ void kernel_crc32(Register crc, Register buf, Register len, Register tmp); ++ ++ // CRC32C code for java.util.zip.CRC32C::updateBytes() instrinsic. ++ void kernel_crc32c(Register crc, Register buf, Register len, Register tmp); ++ ++#undef VIRTUAL ++ ++ public: ++// Memory Data Type ++#define INT_TYPE 0x100 ++#define FLOAT_TYPE 0x200 ++#define SIGNED_TYPE 0x10 ++#define UNSIGNED_TYPE 0x20 ++ ++ typedef enum { ++ LOAD_BYTE = INT_TYPE | SIGNED_TYPE | 0x1, ++ LOAD_CHAR = INT_TYPE | SIGNED_TYPE | 0x2, ++ LOAD_SHORT = INT_TYPE | SIGNED_TYPE | 0x3, ++ LOAD_INT = INT_TYPE | SIGNED_TYPE | 0x4, ++ LOAD_LONG = INT_TYPE | SIGNED_TYPE | 0x5, ++ STORE_BYTE = INT_TYPE | SIGNED_TYPE | 0x6, ++ STORE_CHAR = INT_TYPE | SIGNED_TYPE | 0x7, ++ STORE_SHORT = INT_TYPE | SIGNED_TYPE | 0x8, ++ STORE_INT = INT_TYPE | SIGNED_TYPE | 0x9, ++ STORE_LONG = INT_TYPE | SIGNED_TYPE | 0xa, ++ LOAD_LINKED_LONG = INT_TYPE | SIGNED_TYPE | 0xb, ++ ++ LOAD_U_BYTE = INT_TYPE | UNSIGNED_TYPE | 0x1, ++ LOAD_U_SHORT = INT_TYPE | UNSIGNED_TYPE | 0x2, ++ LOAD_U_INT = INT_TYPE | UNSIGNED_TYPE | 0x3, ++ ++ LOAD_FLOAT = FLOAT_TYPE | SIGNED_TYPE | 0x1, ++ LOAD_DOUBLE = FLOAT_TYPE | SIGNED_TYPE | 0x2, ++ LOAD_VECTORX = FLOAT_TYPE | SIGNED_TYPE | 0x3, ++ LOAD_VECTORY = FLOAT_TYPE | SIGNED_TYPE | 0x4, ++ STORE_FLOAT = FLOAT_TYPE | SIGNED_TYPE | 0x5, ++ STORE_DOUBLE = FLOAT_TYPE | SIGNED_TYPE | 0x6, ++ STORE_VECTORX = FLOAT_TYPE | SIGNED_TYPE | 0x7, ++ STORE_VECTORY = FLOAT_TYPE | SIGNED_TYPE | 0x8 ++ } CMLoadStoreDataType; ++ ++ void loadstore_enc(Register reg, int base, int index, int scale, int disp, int type) { ++ assert((type & INT_TYPE), "must be General reg type"); ++ loadstore_t(reg, base, index, scale, disp, type); ++ } ++ ++ void loadstore_enc(FloatRegister reg, int base, int index, int scale, int disp, int type) { ++ assert((type & FLOAT_TYPE), "must be Float reg type"); ++ loadstore_t(reg, base, index, scale, disp, type); ++ } ++ ++private: ++ template ++ void loadstore_t(T reg, int base, int index, int scale, int disp, int type) { ++ if (index != 0) { ++ assert(((scale==0)&&(disp==0)), "only support base+index"); ++ loadstore(reg, as_Register(base), as_Register(index), type); ++ } else { ++ loadstore(reg, as_Register(base), disp, type); ++ } ++ } ++ void loadstore(Register reg, Register base, int disp, int type); ++ void loadstore(Register reg, Register base, Register disp, int type); ++ void loadstore(FloatRegister reg, Register base, int disp, int type); ++ void loadstore(FloatRegister reg, Register base, Register disp, int type); ++}; ++ ++/** ++ * class SkipIfEqual: ++ * ++ * Instantiating this class will result in assembly code being output that will ++ * jump around any code emitted between the creation of the instance and it's ++ * automatic destruction at the end of a scope block, depending on the value of ++ * the flag passed to the constructor, which will be checked at run-time. ++ */ ++class SkipIfEqual { ++ private: ++ MacroAssembler* _masm; ++ Label _label; ++ ++ public: ++ SkipIfEqual(MacroAssembler*, const bool* flag_addr, bool value); ++ ~SkipIfEqual(); ++}; ++ ++#ifdef ASSERT ++inline bool AbstractAssembler::pd_check_instruction_mark() { return true; } ++#endif ++ ++struct tableswitch { ++ Register _reg; ++ int _insn_index; jint _first_key; jint _last_key; ++ Label _after; ++ Label _branches; ++}; ++ ++#endif // CPU_LOONGARCH_VM_MACROASSEMBLER_LOONGARCH_HPP +diff --git a/hotspot/src/cpu/loongarch/vm/macroAssembler_loongarch.inline.hpp b/hotspot/src/cpu/loongarch/vm/macroAssembler_loongarch.inline.hpp +new file mode 100644 +index 0000000000..0b265a4def +--- /dev/null ++++ b/hotspot/src/cpu/loongarch/vm/macroAssembler_loongarch.inline.hpp +@@ -0,0 +1,34 @@ ++/* ++ * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2017, 2020, Loongson Technology. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ * ++ */ ++ ++#ifndef CPU_LOONGARCH_VM_MACROASSEMBLER_LOONGARCH_INLINE_HPP ++#define CPU_LOONGARCH_VM_MACROASSEMBLER_LOONGARCH_INLINE_HPP ++ ++#include "asm/assembler.inline.hpp" ++#include "asm/macroAssembler.hpp" ++#include "asm/codeBuffer.hpp" ++#include "code/codeCache.hpp" ++ ++#endif // CPU_LOONGARCH_VM_MACROASSEMBLER_LOONGARCH_INLINE_HPP +diff --git a/hotspot/src/cpu/loongarch/vm/metaspaceShared_loongarch_64.cpp b/hotspot/src/cpu/loongarch/vm/metaspaceShared_loongarch_64.cpp +new file mode 100644 +index 0000000000..b36216c533 +--- /dev/null ++++ b/hotspot/src/cpu/loongarch/vm/metaspaceShared_loongarch_64.cpp +@@ -0,0 +1,120 @@ ++/* ++ * Copyright (c) 2004, 2012, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2015, 2020, Loongson Technology. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ * ++ */ ++ ++#include "precompiled.hpp" ++#include "asm/macroAssembler.hpp" ++#include "asm/codeBuffer.hpp" ++#include "memory/metaspaceShared.hpp" ++ ++// Generate the self-patching vtable method: ++// ++// This method will be called (as any other Klass virtual method) with ++// the Klass itself as the first argument. Example: ++// ++// oop obj; ++// int size = obj->klass()->klass_part()->oop_size(this); ++// ++// for which the virtual method call is Klass::oop_size(); ++// ++// The dummy method is called with the Klass object as the first ++// operand, and an object as the second argument. ++// ++ ++//===================================================================== ++ ++// All of the dummy methods in the vtable are essentially identical, ++// differing only by an ordinal constant, and they bear no releationship ++// to the original method which the caller intended. Also, there needs ++// to be 'vtbl_list_size' instances of the vtable in order to ++// differentiate between the 'vtable_list_size' original Klass objects. ++ ++#define __ masm-> ++ ++#define A0 RA0 ++#define A1 RA1 ++#define A2 RA2 ++#define A3 RA3 ++#define A4 RA4 ++#define A5 RA5 ++#define A6 RA6 ++#define A7 RA7 ++#define T0 RT0 ++#define T1 RT1 ++#define T2 RT2 ++#define T3 RT3 ++#define T4 RT4 ++#define T5 RT5 ++#define T6 RT6 ++#define T7 RT7 ++#define T8 RT8 ++ ++void MetaspaceShared::generate_vtable_methods(void** vtbl_list, ++ void** vtable, ++ char** md_top, ++ char* md_end, ++ char** mc_top, ++ char* mc_end) { ++ intptr_t vtable_bytes = (num_virtuals * vtbl_list_size) * sizeof(void*); ++ *(intptr_t *)(*md_top) = vtable_bytes; ++ *md_top += sizeof(intptr_t); ++ void** dummy_vtable = (void**)*md_top; ++ *vtable = dummy_vtable; ++ *md_top += vtable_bytes; ++ ++ // Get ready to generate dummy methods. ++ ++ CodeBuffer cb((unsigned char*)*mc_top, mc_end - *mc_top); ++ MacroAssembler* masm = new MacroAssembler(&cb); ++ Label common_code; ++ for (int i = 0; i < vtbl_list_size; ++i) { ++ for (int j = 0; j < num_virtuals; ++j) { ++ dummy_vtable[num_virtuals * i + j] = (void*)masm->pc(); ++ ++ // Load T5 with a value indicating vtable/offset pair. ++ // -- bits[ 7..0] (8 bits) which virtual method in table? ++ // -- bits[12..8] (5 bits) which virtual method table? ++ // -- must fit in 13-bit instruction immediate field. ++ __ li(T5, (i << 8) + j); ++ __ b(common_code); ++ } ++ } ++ ++ __ bind(common_code); ++ ++ __ srli_d(T4, T5, 8); // isolate vtable identifier. ++ __ shl(T4, LogBytesPerWord); ++ __ li(AT, (long)vtbl_list); ++ __ ldx_d(T4, AT, T4); // get correct vtable address. ++ __ st_d(T4, A0, 0); // update vtable pointer. ++ ++ __ andi(T5, T5, 0x00ff); // isolate vtable method index ++ __ shl(T5, LogBytesPerWord); ++ __ ldx_d(T4, T4, T5); // address of real method pointer. ++ __ jr(T4); // get real method pointer. ++ ++ __ flush(); ++ ++ *mc_top = (char*)__ pc(); ++} +diff --git a/hotspot/src/cpu/loongarch/vm/methodHandles_loongarch.cpp b/hotspot/src/cpu/loongarch/vm/methodHandles_loongarch.cpp +new file mode 100644 +index 0000000000..cb31ca5ad5 +--- /dev/null ++++ b/hotspot/src/cpu/loongarch/vm/methodHandles_loongarch.cpp +@@ -0,0 +1,566 @@ ++/* ++ * Copyright (c) 1997, 2014, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2015, 2021, Loongson Technology. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ * ++ */ ++ ++#include "precompiled.hpp" ++#include "asm/macroAssembler.hpp" ++#include "interpreter/interpreter.hpp" ++#include "interpreter/interpreterRuntime.hpp" ++#include "memory/allocation.inline.hpp" ++#include "prims/methodHandles.hpp" ++ ++#define __ _masm-> ++ ++#define A0 RA0 ++#define A1 RA1 ++#define A2 RA2 ++#define A3 RA3 ++#define A4 RA4 ++#define A5 RA5 ++#define A6 RA6 ++#define A7 RA7 ++#define T0 RT0 ++#define T1 RT1 ++#define T2 RT2 ++#define T3 RT3 ++#define T4 RT4 ++#define T5 RT5 ++#define T6 RT6 ++#define T7 RT7 ++#define T8 RT8 ++ ++#ifdef PRODUCT ++#define BLOCK_COMMENT(str) /* nothing */ ++#define STOP(error) stop(error) ++#else ++#define BLOCK_COMMENT(str) __ block_comment(str) ++#define STOP(error) block_comment(error); __ stop(error) ++#endif ++ ++#define BIND(label) bind(label); BLOCK_COMMENT(#label ":") ++ ++void MethodHandles::load_klass_from_Class(MacroAssembler* _masm, Register klass_reg) { ++ if (VerifyMethodHandles) ++ verify_klass(_masm, klass_reg, SystemDictionary::WK_KLASS_ENUM_NAME(java_lang_Class), ++ "MH argument is a Class"); ++ __ ld_d(klass_reg, Address(klass_reg, java_lang_Class::klass_offset_in_bytes())); ++} ++ ++#ifdef ASSERT ++static int check_nonzero(const char* xname, int x) { ++ assert(x != 0, err_msg("%s should be nonzero", xname)); ++ return x; ++} ++#define NONZERO(x) check_nonzero(#x, x) ++#else //ASSERT ++#define NONZERO(x) (x) ++#endif //ASSERT ++ ++#ifdef ASSERT ++void MethodHandles::verify_klass(MacroAssembler* _masm, ++ Register obj, SystemDictionary::WKID klass_id, ++ const char* error_message) { ++} ++ ++void MethodHandles::verify_ref_kind(MacroAssembler* _masm, int ref_kind, Register member_reg, Register temp) { ++ Label L; ++ BLOCK_COMMENT("verify_ref_kind {"); ++ __ ld_w(temp, Address(member_reg, NONZERO(java_lang_invoke_MemberName::flags_offset_in_bytes()))); ++ __ srai_w(temp, temp, java_lang_invoke_MemberName::MN_REFERENCE_KIND_SHIFT); ++ __ li(AT, java_lang_invoke_MemberName::MN_REFERENCE_KIND_MASK); ++ __ andr(temp, temp, AT); ++ __ li(AT, ref_kind); ++ __ beq(temp, AT, L); ++ { char* buf = NEW_C_HEAP_ARRAY(char, 100, mtInternal); ++ jio_snprintf(buf, 100, "verify_ref_kind expected %x", ref_kind); ++ if (ref_kind == JVM_REF_invokeVirtual || ++ ref_kind == JVM_REF_invokeSpecial) ++ // could do this for all ref_kinds, but would explode assembly code size ++ trace_method_handle(_masm, buf); ++ __ STOP(buf); ++ } ++ BLOCK_COMMENT("} verify_ref_kind"); ++ __ bind(L); ++} ++ ++#endif //ASSERT ++ ++void MethodHandles::jump_from_method_handle(MacroAssembler* _masm, Register method, Register temp, ++ bool for_compiler_entry) { ++ assert(method == Rmethod, "interpreter calling convention"); ++ ++ Label L_no_such_method; ++ __ beq(method, R0, L_no_such_method); ++ ++ __ verify_method_ptr(method); ++ ++ if (!for_compiler_entry && JvmtiExport::can_post_interpreter_events()) { ++ Label run_compiled_code; ++ // JVMTI events, such as single-stepping, are implemented partly by avoiding running ++ // compiled code in threads for which the event is enabled. Check here for ++ // interp_only_mode if these events CAN be enabled. ++ Register rthread = TREG; ++ // interp_only is an int, on little endian it is sufficient to test the byte only ++ // Is a cmpl faster? ++ __ ld_bu(AT, rthread, in_bytes(JavaThread::interp_only_mode_offset())); ++ __ beq(AT, R0, run_compiled_code); ++ __ ld_d(T4, method, in_bytes(Method::interpreter_entry_offset())); ++ __ jr(T4); ++ __ BIND(run_compiled_code); ++ } ++ ++ const ByteSize entry_offset = for_compiler_entry ? Method::from_compiled_offset() : ++ Method::from_interpreted_offset(); ++ __ ld_d(T4, method, in_bytes(entry_offset)); ++ __ jr(T4); ++ ++ __ bind(L_no_such_method); ++ address wrong_method = StubRoutines::throw_AbstractMethodError_entry(); ++ __ jmp(wrong_method, relocInfo::runtime_call_type); ++} ++ ++void MethodHandles::jump_to_lambda_form(MacroAssembler* _masm, ++ Register recv, Register method_temp, ++ Register temp2, ++ bool for_compiler_entry) { ++ BLOCK_COMMENT("jump_to_lambda_form {"); ++ // This is the initial entry point of a lazy method handle. ++ // After type checking, it picks up the invoker from the LambdaForm. ++ assert_different_registers(recv, method_temp, temp2); ++ assert(recv != noreg, "required register"); ++ assert(method_temp == Rmethod, "required register for loading method"); ++ ++ //NOT_PRODUCT({ FlagSetting fs(TraceMethodHandles, true); trace_method_handle(_masm, "LZMH"); }); ++ ++ // Load the invoker, as MH -> MH.form -> LF.vmentry ++ __ verify_oop(recv); ++ __ load_heap_oop(method_temp, Address(recv, NONZERO(java_lang_invoke_MethodHandle::form_offset_in_bytes()))); ++ __ verify_oop(method_temp); ++ __ load_heap_oop(method_temp, Address(method_temp, NONZERO(java_lang_invoke_LambdaForm::vmentry_offset_in_bytes()))); ++ __ verify_oop(method_temp); ++ // the following assumes that a Method* is normally compressed in the vmtarget field: ++ __ ld_d(method_temp, Address(method_temp, NONZERO(java_lang_invoke_MemberName::vmtarget_offset_in_bytes()))); ++ ++ if (VerifyMethodHandles && !for_compiler_entry) { ++ // make sure recv is already on stack ++ __ ld_d(temp2, Address(method_temp, Method::const_offset())); ++ __ load_sized_value(temp2, ++ Address(temp2, ConstMethod::size_of_parameters_offset()), ++ sizeof(u2), false); ++ // assert(sizeof(u2) == sizeof(Method::_size_of_parameters), ""); ++ Label L; ++ Address recv_addr = __ argument_address(temp2, -1); ++ __ ld_d(AT, recv_addr); ++ __ beq(recv, AT, L); ++ ++ recv_addr = __ argument_address(temp2, -1); ++ __ ld_d(V0, recv_addr); ++ __ STOP("receiver not on stack"); ++ __ BIND(L); ++ } ++ ++ jump_from_method_handle(_masm, method_temp, temp2, for_compiler_entry); ++ BLOCK_COMMENT("} jump_to_lambda_form"); ++} ++ ++ ++// Code generation ++address MethodHandles::generate_method_handle_interpreter_entry(MacroAssembler* _masm, ++ vmIntrinsics::ID iid) { ++ const bool not_for_compiler_entry = false; // this is the interpreter entry ++ assert(is_signature_polymorphic(iid), "expected invoke iid"); ++ if (iid == vmIntrinsics::_invokeGeneric || ++ iid == vmIntrinsics::_compiledLambdaForm) { ++ // Perhaps surprisingly, the symbolic references visible to Java are not directly used. ++ // They are linked to Java-generated adapters via MethodHandleNatives.linkMethod. ++ // They all allow an appendix argument. ++ __ stop("empty stubs make SG sick"); ++ return NULL; ++ } ++ ++ // Rmethod: Method* ++ // T4: argument locator (parameter slot count, added to sp) ++ // S7: used as temp to hold mh or receiver ++ Register t4_argp = T4; // argument list ptr, live on error paths ++ Register s7_mh = S7; // MH receiver; dies quickly and is recycled ++ Register rm_method = Rmethod; // eventual target of this invocation ++ ++ // here's where control starts out: ++ __ align(CodeEntryAlignment); ++ address entry_point = __ pc(); ++ ++ if (VerifyMethodHandles) { ++ Label L; ++ BLOCK_COMMENT("verify_intrinsic_id {"); ++ __ ld_bu(AT, rm_method, Method::intrinsic_id_offset_in_bytes()); ++ guarantee(Assembler::is_simm(iid, 12), "Oops, iid is not simm16! Change the instructions."); ++ __ addi_d(AT, AT, -1 * (int) iid); ++ __ beq(AT, R0, L); ++ if (iid == vmIntrinsics::_linkToVirtual || ++ iid == vmIntrinsics::_linkToSpecial) { ++ // could do this for all kinds, but would explode assembly code size ++ trace_method_handle(_masm, "bad Method*::intrinsic_id"); ++ } ++ __ STOP("bad Method*::intrinsic_id"); ++ __ bind(L); ++ BLOCK_COMMENT("} verify_intrinsic_id"); ++ } ++ ++ // First task: Find out how big the argument list is. ++ Address t4_first_arg_addr; ++ int ref_kind = signature_polymorphic_intrinsic_ref_kind(iid); ++ assert(ref_kind != 0 || iid == vmIntrinsics::_invokeBasic, "must be _invokeBasic or a linkTo intrinsic"); ++ if (ref_kind == 0 || MethodHandles::ref_kind_has_receiver(ref_kind)) { ++ __ ld_d(t4_argp, Address(rm_method, Method::const_offset())); ++ __ load_sized_value(t4_argp, ++ Address(t4_argp, ConstMethod::size_of_parameters_offset()), ++ sizeof(u2), false); ++ // assert(sizeof(u2) == sizeof(Method::_size_of_parameters), ""); ++ t4_first_arg_addr = __ argument_address(t4_argp, -1); ++ } else { ++ DEBUG_ONLY(t4_argp = noreg); ++ } ++ ++ if (!is_signature_polymorphic_static(iid)) { ++ __ ld_d(s7_mh, t4_first_arg_addr); ++ DEBUG_ONLY(t4_argp = noreg); ++ } ++ ++ // t4_first_arg_addr is live! ++ ++ trace_method_handle_interpreter_entry(_masm, iid); ++ ++ if (iid == vmIntrinsics::_invokeBasic) { ++ generate_method_handle_dispatch(_masm, iid, s7_mh, noreg, not_for_compiler_entry); ++ ++ } else { ++ // Adjust argument list by popping the trailing MemberName argument. ++ Register r_recv = noreg; ++ if (MethodHandles::ref_kind_has_receiver(ref_kind)) { ++ // Load the receiver (not the MH; the actual MemberName's receiver) up from the interpreter stack. ++ __ ld_d(r_recv = T2, t4_first_arg_addr); ++ } ++ DEBUG_ONLY(t4_argp = noreg); ++ Register rm_member = rm_method; // MemberName ptr; incoming method ptr is dead now ++ __ pop(rm_member); // extract last argument ++ generate_method_handle_dispatch(_masm, iid, r_recv, rm_member, not_for_compiler_entry); ++ } ++ ++ return entry_point; ++} ++ ++void MethodHandles::generate_method_handle_dispatch(MacroAssembler* _masm, ++ vmIntrinsics::ID iid, ++ Register receiver_reg, ++ Register member_reg, ++ bool for_compiler_entry) { ++ assert(is_signature_polymorphic(iid), "expected invoke iid"); ++ Register rm_method = Rmethod; // eventual target of this invocation ++ // temps used in this code are not used in *either* compiled or interpreted calling sequences ++ Register j_rarg0 = T0; ++ Register j_rarg1 = A0; ++ Register j_rarg2 = A1; ++ Register j_rarg3 = A2; ++ Register j_rarg4 = A3; ++ Register j_rarg5 = A4; ++ ++ Register temp1 = T8; ++ Register temp2 = T4; ++ Register temp3 = T5; ++ if (for_compiler_entry) { ++ assert(receiver_reg == (iid == vmIntrinsics::_linkToStatic ? noreg : j_rarg0), "only valid assignment"); ++ assert_different_registers(temp1, j_rarg0, j_rarg1, j_rarg2, j_rarg3, j_rarg4, j_rarg5); ++ assert_different_registers(temp2, j_rarg0, j_rarg1, j_rarg2, j_rarg3, j_rarg4, j_rarg5); ++ assert_different_registers(temp3, j_rarg0, j_rarg1, j_rarg2, j_rarg3, j_rarg4, j_rarg5); ++ } ++ else { ++ assert_different_registers(temp1, temp2, temp3, saved_last_sp_register()); // don't trash lastSP ++ } ++ assert_different_registers(temp1, temp2, temp3, receiver_reg); ++ assert_different_registers(temp1, temp2, temp3, member_reg); ++ ++ if (iid == vmIntrinsics::_invokeBasic) { ++ // indirect through MH.form.vmentry.vmtarget ++ jump_to_lambda_form(_masm, receiver_reg, rm_method, temp1, for_compiler_entry); ++ ++ } else { ++ // The method is a member invoker used by direct method handles. ++ if (VerifyMethodHandles) { ++ // make sure the trailing argument really is a MemberName (caller responsibility) ++ verify_klass(_masm, member_reg, SystemDictionary::WK_KLASS_ENUM_NAME(java_lang_invoke_MemberName), ++ "MemberName required for invokeVirtual etc."); ++ } ++ ++ Address member_clazz( member_reg, NONZERO(java_lang_invoke_MemberName::clazz_offset_in_bytes())); ++ Address member_vmindex( member_reg, NONZERO(java_lang_invoke_MemberName::vmindex_offset_in_bytes())); ++ Address member_vmtarget( member_reg, NONZERO(java_lang_invoke_MemberName::vmtarget_offset_in_bytes())); ++ ++ Register temp1_recv_klass = temp1; ++ if (iid != vmIntrinsics::_linkToStatic) { ++ __ verify_oop(receiver_reg); ++ if (iid == vmIntrinsics::_linkToSpecial) { ++ // Don't actually load the klass; just null-check the receiver. ++ __ null_check(receiver_reg); ++ } else { ++ // load receiver klass itself ++ __ null_check(receiver_reg, oopDesc::klass_offset_in_bytes()); ++ __ load_klass(temp1_recv_klass, receiver_reg); ++ __ verify_klass_ptr(temp1_recv_klass); ++ } ++ BLOCK_COMMENT("check_receiver {"); ++ // The receiver for the MemberName must be in receiver_reg. ++ // Check the receiver against the MemberName.clazz ++ if (VerifyMethodHandles && iid == vmIntrinsics::_linkToSpecial) { ++ // Did not load it above... ++ __ load_klass(temp1_recv_klass, receiver_reg); ++ __ verify_klass_ptr(temp1_recv_klass); ++ } ++ if (VerifyMethodHandles && iid != vmIntrinsics::_linkToInterface) { ++ Label L_ok; ++ Register temp2_defc = temp2; ++ __ load_heap_oop(temp2_defc, member_clazz); ++ load_klass_from_Class(_masm, temp2_defc); ++ __ verify_klass_ptr(temp2_defc); ++ __ check_klass_subtype(temp1_recv_klass, temp2_defc, temp3, L_ok); ++ // If we get here, the type check failed! ++ __ STOP("receiver class disagrees with MemberName.clazz"); ++ __ bind(L_ok); ++ } ++ BLOCK_COMMENT("} check_receiver"); ++ } ++ if (iid == vmIntrinsics::_linkToSpecial || ++ iid == vmIntrinsics::_linkToStatic) { ++ DEBUG_ONLY(temp1_recv_klass = noreg); // these guys didn't load the recv_klass ++ } ++ ++ // Live registers at this point: ++ // member_reg - MemberName that was the trailing argument ++ // temp1_recv_klass - klass of stacked receiver, if needed ++ ++ Label L_incompatible_class_change_error; ++ switch (iid) { ++ case vmIntrinsics::_linkToSpecial: ++ if (VerifyMethodHandles) { ++ verify_ref_kind(_masm, JVM_REF_invokeSpecial, member_reg, temp3); ++ } ++ __ ld_d(rm_method, member_vmtarget); ++ break; ++ ++ case vmIntrinsics::_linkToStatic: ++ if (VerifyMethodHandles) { ++ verify_ref_kind(_masm, JVM_REF_invokeStatic, member_reg, temp3); ++ } ++ __ ld_d(rm_method, member_vmtarget); ++ break; ++ ++ case vmIntrinsics::_linkToVirtual: ++ { ++ // same as TemplateTable::invokevirtual, ++ // minus the CP setup and profiling: ++ ++ if (VerifyMethodHandles) { ++ verify_ref_kind(_masm, JVM_REF_invokeVirtual, member_reg, temp3); ++ } ++ ++ // pick out the vtable index from the MemberName, and then we can discard it: ++ Register temp2_index = temp2; ++ __ ld_d(temp2_index, member_vmindex); ++ ++ if (VerifyMethodHandles) { ++ Label L_index_ok; ++ __ blt(R0, temp2_index, L_index_ok); ++ __ STOP("no virtual index"); ++ __ BIND(L_index_ok); ++ } ++ ++ // Note: The verifier invariants allow us to ignore MemberName.clazz and vmtarget ++ // at this point. And VerifyMethodHandles has already checked clazz, if needed. ++ ++ // get target Method* & entry point ++ __ lookup_virtual_method(temp1_recv_klass, temp2_index, rm_method); ++ break; ++ } ++ ++ case vmIntrinsics::_linkToInterface: ++ { ++ // same as TemplateTable::invokeinterface ++ // (minus the CP setup and profiling, with different argument motion) ++ if (VerifyMethodHandles) { ++ verify_ref_kind(_masm, JVM_REF_invokeInterface, member_reg, temp3); ++ } ++ ++ Register temp3_intf = temp3; ++ __ load_heap_oop(temp3_intf, member_clazz); ++ load_klass_from_Class(_masm, temp3_intf); ++ __ verify_klass_ptr(temp3_intf); ++ ++ Register rm_index = rm_method; ++ __ ld_d(rm_index, member_vmindex); ++ if (VerifyMethodHandles) { ++ Label L; ++ __ bge(rm_index, R0, L); ++ __ STOP("invalid vtable index for MH.invokeInterface"); ++ __ bind(L); ++ } ++ ++ // given intf, index, and recv klass, dispatch to the implementation method ++ __ lookup_interface_method(temp1_recv_klass, temp3_intf, ++ // note: next two args must be the same: ++ rm_index, rm_method, ++ temp2, ++ L_incompatible_class_change_error); ++ break; ++ } ++ ++ default: ++ fatal(err_msg_res("unexpected intrinsic %d: %s", iid, vmIntrinsics::name_at(iid))); ++ break; ++ } ++ ++ // Live at this point: ++ // rm_method ++ ++ // After figuring out which concrete method to call, jump into it. ++ // Note that this works in the interpreter with no data motion. ++ // But the compiled version will require that r_recv be shifted out. ++ __ verify_method_ptr(rm_method); ++ jump_from_method_handle(_masm, rm_method, temp1, for_compiler_entry); ++ ++ if (iid == vmIntrinsics::_linkToInterface) { ++ __ bind(L_incompatible_class_change_error); ++ address icce_entry= StubRoutines::throw_IncompatibleClassChangeError_entry(); ++ __ jmp(icce_entry, relocInfo::runtime_call_type); ++ } ++ } ++} ++ ++#ifndef PRODUCT ++void trace_method_handle_stub(const char* adaptername, ++ oop mh, ++ intptr_t* saved_regs, ++ intptr_t* entry_sp) { ++ // called as a leaf from native code: do not block the JVM! ++ bool has_mh = (strstr(adaptername, "/static") == NULL && ++ strstr(adaptername, "linkTo") == NULL); // static linkers don't have MH ++ const char* mh_reg_name = has_mh ? "s7_mh" : "s7"; ++ tty->print_cr("MH %s %s="PTR_FORMAT" sp="PTR_FORMAT, ++ adaptername, mh_reg_name, ++ p2i(mh), p2i(entry_sp)); ++ ++ if (Verbose) { ++ tty->print_cr("Registers:"); ++ const int saved_regs_count = RegisterImpl::number_of_registers; ++ for (int i = 0; i < saved_regs_count; i++) { ++ Register r = as_Register(i); ++ // The registers are stored in reverse order on the stack (by pusha). ++ tty->print("%3s=" PTR_FORMAT, r->name(), saved_regs[((saved_regs_count - 1) - i)]); ++ if ((i + 1) % 4 == 0) { ++ tty->cr(); ++ } else { ++ tty->print(", "); ++ } ++ } ++ tty->cr(); ++ ++ { ++ // dumping last frame with frame::describe ++ ++ JavaThread* p = JavaThread::active(); ++ ++ ResourceMark rm; ++ PRESERVE_EXCEPTION_MARK; // may not be needed by safer and unexpensive here ++ FrameValues values; ++ ++ // Note: We want to allow trace_method_handle from any call site. ++ // While trace_method_handle creates a frame, it may be entered ++ // without a PC on the stack top (e.g. not just after a call). ++ // Walking that frame could lead to failures due to that invalid PC. ++ // => carefully detect that frame when doing the stack walking ++ ++ // Current C frame ++ frame cur_frame = os::current_frame(); ++ ++ // Robust search of trace_calling_frame (independant of inlining). ++ // Assumes saved_regs comes from a pusha in the trace_calling_frame. ++ assert(cur_frame.sp() < saved_regs, "registers not saved on stack ?"); ++ frame trace_calling_frame = os::get_sender_for_C_frame(&cur_frame); ++ while (trace_calling_frame.fp() < saved_regs) { ++ trace_calling_frame = os::get_sender_for_C_frame(&trace_calling_frame); ++ } ++ ++ // safely create a frame and call frame::describe ++ intptr_t *dump_sp = trace_calling_frame.sender_sp(); ++ intptr_t *dump_fp = trace_calling_frame.link(); ++ ++ bool walkable = has_mh; // whether the traced frame shoud be walkable ++ ++ if (walkable) { ++ // The previous definition of walkable may have to be refined ++ // if new call sites cause the next frame constructor to start ++ // failing. Alternatively, frame constructors could be ++ // modified to support the current or future non walkable ++ // frames (but this is more intrusive and is not considered as ++ // part of this RFE, which will instead use a simpler output). ++ frame dump_frame = frame(dump_sp, dump_fp); ++ dump_frame.describe(values, 1); ++ } else { ++ // Stack may not be walkable (invalid PC above FP): ++ // Add descriptions without building a Java frame to avoid issues ++ values.describe(-1, dump_fp, "fp for #1 "); ++ values.describe(-1, dump_sp, "sp for #1"); ++ } ++ values.describe(-1, entry_sp, "raw top of stack"); ++ ++ tty->print_cr("Stack layout:"); ++ values.print(p); ++ } ++ if (has_mh && mh->is_oop()) { ++ mh->print(); ++ if (java_lang_invoke_MethodHandle::is_instance(mh)) { ++ if (java_lang_invoke_MethodHandle::form_offset_in_bytes() != 0) ++ java_lang_invoke_MethodHandle::form(mh)->print(); ++ } ++ } ++ } ++} ++ ++// The stub wraps the arguments in a struct on the stack to avoid ++// dealing with the different calling conventions for passing 6 ++// arguments. ++struct MethodHandleStubArguments { ++ const char* adaptername; ++ oopDesc* mh; ++ intptr_t* saved_regs; ++ intptr_t* entry_sp; ++}; ++void trace_method_handle_stub_wrapper(MethodHandleStubArguments* args) { ++ trace_method_handle_stub(args->adaptername, ++ args->mh, ++ args->saved_regs, ++ args->entry_sp); ++} ++ ++void MethodHandles::trace_method_handle(MacroAssembler* _masm, const char* adaptername) { ++} ++#endif //PRODUCT +diff --git a/hotspot/src/cpu/loongarch/vm/methodHandles_loongarch.hpp b/hotspot/src/cpu/loongarch/vm/methodHandles_loongarch.hpp +new file mode 100644 +index 0000000000..f84337424b +--- /dev/null ++++ b/hotspot/src/cpu/loongarch/vm/methodHandles_loongarch.hpp +@@ -0,0 +1,62 @@ ++/* ++ * Copyright (c) 2010, 2012, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2015, 2021, Loongson Technology. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ * ++ */ ++ ++// Platform-specific definitions for method handles. ++// These definitions are inlined into class MethodHandles. ++ ++// Adapters ++enum /* platform_dependent_constants */ { ++ adapter_code_size = 32000 DEBUG_ONLY(+ 150000) ++}; ++ ++// Additional helper methods for MethodHandles code generation: ++public: ++ static void load_klass_from_Class(MacroAssembler* _masm, Register klass_reg); ++ ++ static void verify_klass(MacroAssembler* _masm, ++ Register obj, SystemDictionary::WKID klass_id, ++ const char* error_message = "wrong klass") NOT_DEBUG_RETURN; ++ ++ static void verify_method_handle(MacroAssembler* _masm, Register mh_reg) { ++ verify_klass(_masm, mh_reg, SystemDictionary::WK_KLASS_ENUM_NAME(java_lang_invoke_MethodHandle), ++ "reference is a MH"); ++ } ++ ++ static void verify_ref_kind(MacroAssembler* _masm, int ref_kind, Register member_reg, Register temp) NOT_DEBUG_RETURN; ++ ++ // Similar to InterpreterMacroAssembler::jump_from_interpreted. ++ // Takes care of special dispatch from single stepping too. ++ static void jump_from_method_handle(MacroAssembler* _masm, Register method, Register temp, ++ bool for_compiler_entry); ++ ++ static void jump_to_lambda_form(MacroAssembler* _masm, ++ Register recv, Register method_temp, ++ Register temp2, ++ bool for_compiler_entry); ++ ++ static Register saved_last_sp_register() { ++ // Should be in sharedRuntime, not here. ++ return R3; ++ } +diff --git a/hotspot/src/cpu/loongarch/vm/nativeInst_loongarch.cpp b/hotspot/src/cpu/loongarch/vm/nativeInst_loongarch.cpp +new file mode 100644 +index 0000000000..dd940e18e0 +--- /dev/null ++++ b/hotspot/src/cpu/loongarch/vm/nativeInst_loongarch.cpp +@@ -0,0 +1,475 @@ ++/* ++ * Copyright (c) 1997, 2014, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2015, 2021, Loongson Technology. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ * ++ */ ++ ++#include "precompiled.hpp" ++#include "asm/macroAssembler.hpp" ++#include "memory/resourceArea.hpp" ++#include "nativeInst_loongarch.hpp" ++#include "oops/oop.inline.hpp" ++#include "runtime/handles.hpp" ++#include "runtime/sharedRuntime.hpp" ++#include "runtime/stubRoutines.hpp" ++#include "utilities/ostream.hpp" ++ ++#include ++ ++#define A0 RA0 ++#define A1 RA1 ++#define A2 RA2 ++#define A3 RA3 ++#define A4 RA4 ++#define A5 RA5 ++#define A6 RA6 ++#define A7 RA7 ++#define T0 RT0 ++#define T1 RT1 ++#define T2 RT2 ++#define T3 RT3 ++#define T4 RT4 ++#define T5 RT5 ++#define T6 RT6 ++#define T7 RT7 ++#define T8 RT8 ++ ++void NativeInstruction::wrote(int offset) { ++ ICache::invalidate_word(addr_at(offset)); ++} ++ ++void NativeInstruction::set_long_at(int offset, long i) { ++ address addr = addr_at(offset); ++ *(long*)addr = i; ++ ICache::invalidate_range(addr, 8); ++} ++ ++bool NativeInstruction::is_int_branch() { ++ int op = Assembler::high(insn_word(), 6); ++ return op == Assembler::beqz_op || op == Assembler::bnez_op || ++ op == Assembler::beq_op || op == Assembler::bne_op || ++ op == Assembler::blt_op || op == Assembler::bge_op || ++ op == Assembler::bltu_op || op == Assembler::bgeu_op; ++} ++ ++bool NativeInstruction::is_float_branch() { ++ return Assembler::high(insn_word(), 6) == Assembler::bccondz_op; ++} ++ ++bool NativeCall::is_bl() const { ++ return Assembler::high(int_at(0), 6) == Assembler::bl_op; ++} ++ ++void NativeCall::verify() { ++ assert(is_bl(), "not a NativeCall"); ++} ++ ++address NativeCall::target_addr_for_bl(address orig_addr) const { ++ address addr = orig_addr ? orig_addr : addr_at(0); ++ ++ // bl ++ if (is_bl()) { ++ return addr + (Assembler::simm26(((int_at(0) & 0x3ff) << 16) | ++ ((int_at(0) >> 10) & 0xffff)) << 2); ++ } ++ ++ fatal("not a NativeCall"); ++ return NULL; ++} ++ ++address NativeCall::destination() const { ++ address addr = (address)this; ++ address destination = target_addr_for_bl(); ++ // Do we use a trampoline stub for this call? ++ // Trampoline stubs are located behind the main code. ++ if (destination > addr) { ++ // Filter out recursive method invocation (call to verified/unverified entry point). ++ CodeBlob* cb = CodeCache::find_blob_unsafe(addr); // Else we get assertion if nmethod is zombie. ++ assert(cb && cb->is_nmethod(), "sanity"); ++ nmethod *nm = (nmethod *)cb; ++ NativeInstruction* ni = nativeInstruction_at(destination); ++ if (nm->stub_contains(destination) && ni->is_NativeCallTrampolineStub_at()) { ++ // Yes we do, so get the destination from the trampoline stub. ++ const address trampoline_stub_addr = destination; ++ destination = nativeCallTrampolineStub_at(trampoline_stub_addr)->destination(); ++ } ++ } ++ return destination; ++} ++ ++// Similar to replace_mt_safe, but just changes the destination. The ++// important thing is that free-running threads are able to execute this ++// call instruction at all times. ++// ++// Used in the runtime linkage of calls; see class CompiledIC. ++// ++// Add parameter assert_lock to switch off assertion ++// during code generation, where no patching lock is needed. ++void NativeCall::set_destination_mt_safe(address dest, bool assert_lock) { ++ assert(!assert_lock || ++ (Patching_lock->is_locked() || SafepointSynchronize::is_at_safepoint()), ++ "concurrent code patching"); ++ ++ ResourceMark rm; ++ address addr_call = addr_at(0); ++ bool reachable = MacroAssembler::reachable_from_branch_short(dest - addr_call); ++ assert(NativeCall::is_call_at(addr_call), "unexpected code at call site"); ++ ++ // Patch the call. ++ if (!reachable) { ++ address trampoline_stub_addr = get_trampoline(); ++ assert (trampoline_stub_addr != NULL, "we need a trampoline"); ++ guarantee(Assembler::is_simm((trampoline_stub_addr - addr_call) >> 2, 26), "cannot reach trampoline stub"); ++ ++ // Patch the constant in the call's trampoline stub. ++ NativeInstruction* ni = nativeInstruction_at(dest); ++ assert (! ni->is_NativeCallTrampolineStub_at(), "chained trampolines"); ++ nativeCallTrampolineStub_at(trampoline_stub_addr)->set_destination(dest); ++ dest = trampoline_stub_addr; ++ } ++ set_destination(dest); ++} ++ ++address NativeCall::get_trampoline() { ++ address call_addr = addr_at(0); ++ ++ CodeBlob *code = CodeCache::find_blob(call_addr); ++ assert(code != NULL, "Could not find the containing code blob"); ++ ++ address bl_destination ++ = nativeCall_at(call_addr)->target_addr_for_bl(); ++ NativeInstruction* ni = nativeInstruction_at(bl_destination); ++ if (code->contains(bl_destination) && ++ ni->is_NativeCallTrampolineStub_at()) ++ return bl_destination; ++ ++ // If the codeBlob is not a nmethod, this is because we get here from the ++ // CodeBlob constructor, which is called within the nmethod constructor. ++ return trampoline_stub_Relocation::get_trampoline_for(call_addr, (nmethod*)code); ++} ++ ++void NativeCall::set_destination(address dest) { ++ address addr_call = addr_at(0); ++ CodeBuffer cb(addr_call, instruction_size); ++ MacroAssembler masm(&cb); ++ assert(is_call_at(addr_call), "unexpected call type"); ++ jlong offs = dest - addr_call; ++ masm.bl(offs >> 2); ++ ICache::invalidate_range(addr_call, instruction_size); ++} ++ ++void NativeCall::print() { ++ tty->print_cr(PTR_FORMAT ": call " PTR_FORMAT, ++ p2i(instruction_address()), p2i(destination())); ++} ++ ++// Inserts a native call instruction at a given pc ++void NativeCall::insert(address code_pos, address entry) { ++ //TODO: LA ++ guarantee(0, "LA not implemented yet"); ++} ++ ++// MT-safe patching of a call instruction. ++// First patches first word of instruction to two jmp's that jmps to them ++// selfs (spinlock). Then patches the last byte, and then atomicly replaces ++// the jmp's with the first 4 byte of the new instruction. ++void NativeCall::replace_mt_safe(address instr_addr, address code_buffer) { ++ Unimplemented(); ++} ++ ++bool NativeFarCall::is_short() const { ++ return Assembler::high(int_at(0), 10) == Assembler::andi_op && ++ Assembler::low(int_at(0), 22) == 0 && ++ Assembler::high(int_at(4), 6) == Assembler::bl_op; ++} ++ ++bool NativeFarCall::is_far() const { ++ return Assembler::high(int_at(0), 7) == Assembler::pcaddu18i_op && ++ Assembler::high(int_at(4), 6) == Assembler::jirl_op && ++ Assembler::low(int_at(4), 5) == RA->encoding(); ++} ++ ++address NativeFarCall::destination(address orig_addr) const { ++ address addr = orig_addr ? orig_addr : addr_at(0); ++ ++ if (is_short()) { ++ // short ++ return addr + BytesPerInstWord + ++ (Assembler::simm26(((int_at(4) & 0x3ff) << 16) | ++ ((int_at(4) >> 10) & 0xffff)) << 2); ++ } ++ ++ if (is_far()) { ++ // far ++ return addr + ((intptr_t)Assembler::simm20(int_at(0) >> 5 & 0xfffff) << 18) + ++ (Assembler::simm16(int_at(4) >> 10 & 0xffff) << 2); ++ } ++ ++ fatal("not a NativeFarCall"); ++ return NULL; ++} ++ ++void NativeFarCall::set_destination(address dest) { ++ address addr_call = addr_at(0); ++ CodeBuffer cb(addr_call, instruction_size); ++ MacroAssembler masm(&cb); ++ assert(is_far_call_at(addr_call), "unexpected call type"); ++ masm.patchable_call(dest, addr_call); ++ ICache::invalidate_range(addr_call, instruction_size); ++} ++ ++void NativeFarCall::verify() { ++ assert(is_short() || is_far(), "not a NativeFarcall"); ++} ++ ++//------------------------------------------------------------------- ++ ++bool NativeMovConstReg::is_lu12iw_ori_lu32id() const { ++ return Assembler::high(int_at(0), 7) == Assembler::lu12i_w_op && ++ Assembler::high(int_at(4), 10) == Assembler::ori_op && ++ Assembler::high(int_at(8), 7) == Assembler::lu32i_d_op; ++} ++ ++bool NativeMovConstReg::is_lu12iw_lu32id_nop() const { ++ return Assembler::high(int_at(0), 7) == Assembler::lu12i_w_op && ++ Assembler::high(int_at(4), 7) == Assembler::lu32i_d_op && ++ Assembler::high(int_at(8), 10) == Assembler::andi_op; ++} ++ ++bool NativeMovConstReg::is_lu12iw_2nop() const { ++ return Assembler::high(int_at(0), 7) == Assembler::lu12i_w_op && ++ Assembler::high(int_at(4), 10) == Assembler::andi_op && ++ Assembler::high(int_at(8), 10) == Assembler::andi_op; ++} ++ ++bool NativeMovConstReg::is_lu12iw_ori_nop() const { ++ return Assembler::high(int_at(0), 7) == Assembler::lu12i_w_op && ++ Assembler::high(int_at(4), 10) == Assembler::ori_op && ++ Assembler::high(int_at(8), 10) == Assembler::andi_op; ++} ++ ++bool NativeMovConstReg::is_addid_2nop() const { ++ return Assembler::high(int_at(0), 10) == Assembler::addi_d_op && ++ Assembler::high(int_at(4), 10) == Assembler::andi_op && ++ Assembler::high(int_at(8), 10) == Assembler::andi_op; ++} ++ ++void NativeMovConstReg::verify() { ++ assert(is_li52(), "not a mov reg, imm52"); ++} ++ ++void NativeMovConstReg::print() { ++ tty->print_cr(PTR_FORMAT ": mov reg, " INTPTR_FORMAT, ++ p2i(instruction_address()), data()); ++} ++ ++intptr_t NativeMovConstReg::data() const { ++ if (is_lu12iw_ori_lu32id()) { ++ return Assembler::merge((intptr_t)((int_at(4) >> 10) & 0xfff), ++ (intptr_t)((int_at(0) >> 5) & 0xfffff), ++ (intptr_t)((int_at(8) >> 5) & 0xfffff)); ++ } ++ ++ if (is_lu12iw_lu32id_nop()) { ++ return Assembler::merge((intptr_t)0, ++ (intptr_t)((int_at(0) >> 5) & 0xfffff), ++ (intptr_t)((int_at(4) >> 5) & 0xfffff)); ++ } ++ ++ if (is_lu12iw_2nop()) { ++ return Assembler::merge((intptr_t)0, ++ (intptr_t)((int_at(0) >> 5) & 0xfffff)); ++ } ++ ++ if (is_lu12iw_ori_nop()) { ++ return Assembler::merge((intptr_t)((int_at(4) >> 10) & 0xfff), ++ (intptr_t)((int_at(0) >> 5) & 0xfffff)); ++ } ++ ++ if (is_addid_2nop()) { ++ return Assembler::simm12((int_at(0) >> 10) & 0xfff); ++ } ++ ++ Disassembler::decode(addr_at(0), addr_at(0) + 16, tty); ++ fatal("not a mov reg, imm52"); ++ return 0; // unreachable ++} ++ ++void NativeMovConstReg::set_data(intptr_t x, intptr_t o) { ++ CodeBuffer cb(addr_at(0), instruction_size); ++ MacroAssembler masm(&cb); ++ masm.patchable_li52(as_Register(int_at(0) & 0x1f), x); ++ ICache::invalidate_range(addr_at(0), instruction_size); ++ ++ // Find and replace the oop/metadata corresponding to this ++ // instruction in oops section. ++ CodeBlob* blob = CodeCache::find_blob_unsafe(instruction_address()); ++ nmethod* nm = blob->as_nmethod_or_null(); ++ if (nm != NULL) { ++ o = o ? o : x; ++ RelocIterator iter(nm, instruction_address(), next_instruction_address()); ++ while (iter.next()) { ++ if (iter.type() == relocInfo::oop_type) { ++ oop* oop_addr = iter.oop_reloc()->oop_addr(); ++ *oop_addr = cast_to_oop(o); ++ break; ++ } else if (iter.type() == relocInfo::metadata_type) { ++ Metadata** metadata_addr = iter.metadata_reloc()->metadata_addr(); ++ *metadata_addr = (Metadata*)o; ++ break; ++ } ++ } ++ } ++} ++ ++//------------------------------------------------------------------- ++ ++int NativeMovRegMem::offset() const{ ++ //TODO: LA ++ guarantee(0, "LA not implemented yet"); ++ return 0; // mute compiler ++} ++ ++void NativeMovRegMem::set_offset(int x) { ++ //TODO: LA ++ guarantee(0, "LA not implemented yet"); ++} ++ ++void NativeMovRegMem::verify() { ++ //TODO: LA ++ guarantee(0, "LA not implemented yet"); ++} ++ ++ ++void NativeMovRegMem::print() { ++ //TODO: LA ++ guarantee(0, "LA not implemented yet"); ++} ++ ++bool NativeInstruction::is_sigill_zombie_not_entrant() { ++ return uint_at(0) == NativeIllegalInstruction::instruction_code; ++} ++ ++void NativeIllegalInstruction::insert(address code_pos) { ++ *(juint*)code_pos = instruction_code; ++ ICache::invalidate_range(code_pos, instruction_size); ++} ++ ++void NativeJump::verify() { ++ assert(is_short() || is_far(), "not a general jump instruction"); ++} ++ ++bool NativeJump::is_short() { ++ return Assembler::high(insn_word(), 6) == Assembler::b_op; ++} ++ ++bool NativeJump::is_far() { ++ return Assembler::high(int_at(0), 7) == Assembler::pcaddu18i_op && ++ Assembler::high(int_at(4), 6) == Assembler::jirl_op && ++ Assembler::low(int_at(4), 5) == R0->encoding(); ++} ++ ++address NativeJump::jump_destination(address orig_addr) { ++ address addr = orig_addr ? orig_addr : addr_at(0); ++ ++ // short ++ if (is_short()) { ++ return addr + (Assembler::simm26(((int_at(0) & 0x3ff) << 16) | ++ ((int_at(0) >> 10) & 0xffff)) << 2); ++ } ++ ++ // far ++ if (is_far()) { ++ return addr + ((intptr_t)Assembler::simm20(int_at(0) >> 5 & 0xfffff) << 18) + ++ (Assembler::simm16(int_at(4) >> 10 & 0xffff) << 2); ++ } ++ ++ fatal("not a jump"); ++ return NULL; ++} ++ ++void NativeJump::set_jump_destination(address dest) { ++ OrderAccess::fence(); ++ ++ CodeBuffer cb(addr_at(0), instruction_size); ++ MacroAssembler masm(&cb); ++ masm.patchable_jump(dest); ++ ICache::invalidate_range(addr_at(0), instruction_size); ++} ++ ++void NativeGeneralJump::insert_unconditional(address code_pos, address entry) { ++ //TODO: LA ++ guarantee(0, "LA not implemented yet"); ++} ++ ++// MT-safe patching of a long jump instruction. ++// First patches first word of instruction to two jmp's that jmps to them ++// selfs (spinlock). Then patches the last byte, and then atomicly replaces ++// the jmp's with the first 4 byte of the new instruction. ++void NativeGeneralJump::replace_mt_safe(address instr_addr, address code_buffer) { ++ //TODO: LA ++ guarantee(0, "LA not implemented yet"); ++} ++ ++// Must ensure atomicity ++void NativeJump::patch_verified_entry(address entry, address verified_entry, address dest) { ++ assert(dest == SharedRuntime::get_handle_wrong_method_stub(), "expected fixed destination of patch"); ++ jlong offs = dest - verified_entry; ++ ++ if (MacroAssembler::reachable_from_branch_short(offs)) { ++ CodeBuffer cb(verified_entry, 1 * BytesPerInstWord); ++ MacroAssembler masm(&cb); ++ masm.b(dest); ++ } else { ++ // We use an illegal instruction for marking a method as ++ // not_entrant or zombie ++ NativeIllegalInstruction::insert(verified_entry); ++ } ++ ICache::invalidate_range(verified_entry, 1 * BytesPerInstWord); ++} ++ ++bool NativeInstruction::is_dtrace_trap() { ++ //return (*(int32_t*)this & 0xff) == 0xcc; ++ Unimplemented(); ++ return false; ++} ++ ++bool NativeInstruction::is_safepoint_poll() { ++ // ++ // 390 li T2, 0x0000000000400000 #@loadConP ++ // 394 st_w [SP + #12], V1 # spill 9 ++ // 398 Safepoint @ [T2] : poll for GC @ safePoint_poll # spec.benchmarks.compress.Decompressor::decompress @ bci:224 L[0]=A6 L[1]=_ L[2]=sp + #28 L[3]=_ L[4]=V1 ++ // ++ // 0x000000ffe5815130: lu12i_w t2, 0x400 ++ // 0x000000ffe5815134: st_w v1, 0xc(sp) ; OopMap{a6=Oop off=920} ++ // ;*goto ++ // ; - spec.benchmarks.compress.Decompressor::decompress@224 (line 584) ++ // ++ // 0x000000ffe5815138: ld_w at, 0x0(t2) ;*goto <--- PC ++ // ; - spec.benchmarks.compress.Decompressor::decompress@224 (line 584) ++ // ++ ++ // Since there may be some spill instructions between the safePoint_poll and loadConP, ++ // we check the safepoint instruction like this. ++ return Assembler::high(insn_word(), 10) == Assembler::ld_w_op && ++ Assembler::low(insn_word(), 5) == AT->encoding(); ++} +diff --git a/hotspot/src/cpu/loongarch/vm/nativeInst_loongarch.hpp b/hotspot/src/cpu/loongarch/vm/nativeInst_loongarch.hpp +new file mode 100644 +index 0000000000..493239923b +--- /dev/null ++++ b/hotspot/src/cpu/loongarch/vm/nativeInst_loongarch.hpp +@@ -0,0 +1,513 @@ ++/* ++ * Copyright (c) 1997, 2011, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2015, 2021, Loongson Technology. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ * ++ */ ++ ++#ifndef CPU_LOONGARCH_VM_NATIVEINST_LOONGARCH_HPP ++#define CPU_LOONGARCH_VM_NATIVEINST_LOONGARCH_HPP ++ ++#include "asm/assembler.hpp" ++#include "memory/allocation.hpp" ++#include "runtime/icache.hpp" ++#include "runtime/os.hpp" ++#include "utilities/top.hpp" ++ ++// We have interfaces for the following instructions: ++// - NativeInstruction ++// - - NativeCall ++// - - NativeMovConstReg ++// - - NativeMovConstRegPatching ++// - - NativeMovRegMem ++// - - NativeMovRegMemPatching ++// - - NativeIllegalOpCode ++// - - NativeGeneralJump ++// - - NativePushConst ++// - - NativeTstRegMem ++ ++// The base class for different kinds of native instruction abstractions. ++// Provides the primitive operations to manipulate code relative to this. ++ ++class NativeInstruction VALUE_OBJ_CLASS_SPEC { ++ friend class Relocation; ++ ++ public: ++ enum loongarch_specific_constants { ++ nop_instruction_code = 0, ++ nop_instruction_size = 4, ++ sync_instruction_code = 0xf ++ }; ++ ++ bool is_nop() { guarantee(0, "LA not implemented yet"); return long_at(0) == nop_instruction_code; } ++ bool is_sync() { return Assembler::high(insn_word(), 17) == Assembler::dbar_op; } ++ bool is_dtrace_trap(); ++ inline bool is_call(); ++ inline bool is_far_call(); ++ inline bool is_illegal(); ++ bool is_jump(); ++ bool is_safepoint_poll(); ++ ++ // LoongArch has no instruction to generate a illegal instrucion exception? ++ // But `break 11` is not illegal instruction for LoongArch. ++ static int illegal_instruction(); ++ ++ bool is_int_branch(); ++ bool is_float_branch(); ++ ++ inline bool is_NativeCallTrampolineStub_at(); ++ //We use an illegal instruction for marking a method as not_entrant or zombie. ++ bool is_sigill_zombie_not_entrant(); ++ ++ protected: ++ address addr_at(int offset) const { return address(this) + offset; } ++ address instruction_address() const { return addr_at(0); } ++ address next_instruction_address() const { return addr_at(BytesPerInstWord); } ++ address prev_instruction_address() const { return addr_at(-BytesPerInstWord); } ++ ++ s_char sbyte_at(int offset) const { return *(s_char*) addr_at(offset); } ++ u_char ubyte_at(int offset) const { return *(u_char*) addr_at(offset); } ++ ++ jint int_at(int offset) const { return *(jint*) addr_at(offset); } ++ juint uint_at(int offset) const { return *(juint*) addr_at(offset); } ++ ++ intptr_t ptr_at(int offset) const { return *(intptr_t*) addr_at(offset); } ++ ++ oop oop_at (int offset) const { return *(oop*) addr_at(offset); } ++ int long_at(int offset) const { return *(jint*)addr_at(offset); } ++ ++ ++ void set_char_at(int offset, char c) { *addr_at(offset) = (u_char)c; wrote(offset); } ++ void set_int_at(int offset, jint i) { *(jint*)addr_at(offset) = i; wrote(offset); } ++ void set_ptr_at (int offset, intptr_t ptr) { *(intptr_t*) addr_at(offset) = ptr; wrote(offset); } ++ void set_oop_at (int offset, oop o) { *(oop*) addr_at(offset) = o; wrote(offset); } ++ void set_long_at(int offset, long i); ++ ++ int insn_word() const { return long_at(0); } ++ ++ void wrote(int offset); ++ ++ public: ++ ++ // unit test stuff ++ static void test() {} // override for testing ++ ++ inline friend NativeInstruction* nativeInstruction_at(address address); ++}; ++ ++inline NativeInstruction* nativeInstruction_at(address address) { ++ NativeInstruction* inst = (NativeInstruction*)address; ++#ifdef ASSERT ++ //inst->verify(); ++#endif ++ return inst; ++} ++ ++inline NativeCall* nativeCall_at(address address); ++ ++// The NativeCall is an abstraction for accessing/manipulating native call ++// instructions (used to manipulate inline caches, primitive & dll calls, etc.). ++class NativeCall: public NativeInstruction { ++ public: ++ enum loongarch_specific_constants { ++ instruction_offset = 0, ++ instruction_size = 1 * BytesPerInstWord, ++ return_address_offset = 1 * BytesPerInstWord, ++ displacement_offset = 0 ++ }; ++ ++ // We have only bl. ++ bool is_bl() const; ++ ++ address instruction_address() const { return addr_at(instruction_offset); } ++ ++ address next_instruction_address() const { ++ return addr_at(return_address_offset); ++ } ++ ++ address return_address() const { ++ return next_instruction_address(); ++ } ++ ++ address target_addr_for_bl(address orig_addr = 0) const; ++ address destination() const; ++ void set_destination(address dest); ++ ++ void verify_alignment() {} ++ void verify(); ++ void print(); ++ ++ // Creation ++ inline friend NativeCall* nativeCall_at(address address); ++ inline friend NativeCall* nativeCall_before(address return_address); ++ ++ static bool is_call_at(address instr) { ++ return nativeInstruction_at(instr)->is_call(); ++ } ++ ++ static bool is_call_before(address return_address) { ++ return is_call_at(return_address - return_address_offset); ++ } ++ ++ // MT-safe patching of a call instruction. ++ static void insert(address code_pos, address entry); ++ static void replace_mt_safe(address instr_addr, address code_buffer); ++ ++ // Similar to replace_mt_safe, but just changes the destination. The ++ // important thing is that free-running threads are able to execute ++ // this call instruction at all times. If the call is an immediate bl ++ // instruction we can simply rely on atomicity of 32-bit writes to ++ // make sure other threads will see no intermediate states. ++ ++ // We cannot rely on locks here, since the free-running threads must run at ++ // full speed. ++ // ++ // Used in the runtime linkage of calls; see class CompiledIC. ++ ++ // The parameter assert_lock disables the assertion during code generation. ++ void set_destination_mt_safe(address dest, bool assert_lock = true); ++ ++ address get_trampoline(); ++ ++}; ++ ++inline NativeCall* nativeCall_at(address address) { ++ NativeCall* call = (NativeCall*)(address - NativeCall::instruction_offset); ++#ifdef ASSERT ++ call->verify(); ++#endif ++ return call; ++} ++ ++inline NativeCall* nativeCall_before(address return_address) { ++ NativeCall* call = (NativeCall*)(return_address - NativeCall::return_address_offset); ++#ifdef ASSERT ++ call->verify(); ++#endif ++ return call; ++} ++ ++// The NativeFarCall is an abstraction for accessing/manipulating native ++// call-anywhere instructions. ++// Used to call native methods which may be loaded anywhere in the address ++// space, possibly out of reach of a call instruction. ++class NativeFarCall: public NativeInstruction { ++ public: ++ enum loongarch_specific_constants { ++ instruction_size = 2 * BytesPerInstWord, ++ }; ++ ++ // We use MacroAssembler::patchable_call() for implementing a ++ // call-anywhere instruction. ++ bool is_short() const; ++ bool is_far() const; ++ ++ // Checks whether instr points at a NativeFarCall instruction. ++ static bool is_far_call_at(address address) { ++ return nativeInstruction_at(address)->is_far_call(); ++ } ++ ++ // Returns the NativeFarCall's destination. ++ address destination(address orig_addr = 0) const; ++ ++ // Sets the NativeFarCall's destination, not necessarily mt-safe. ++ // Used when relocating code. ++ void set_destination(address dest); ++ ++ void verify(); ++}; ++ ++// Instantiates a NativeFarCall object starting at the given instruction ++// address and returns the NativeFarCall object. ++inline NativeFarCall* nativeFarCall_at(address address) { ++ NativeFarCall* call = (NativeFarCall*)address; ++#ifdef ASSERT ++ call->verify(); ++#endif ++ return call; ++} ++ ++// An interface for accessing/manipulating native set_oop imm, reg instructions ++// (used to manipulate inlined data references, etc.). ++class NativeMovConstReg: public NativeInstruction { ++ public: ++ enum loongarch_specific_constants { ++ instruction_offset = 0, ++ instruction_size = 3 * BytesPerInstWord, ++ next_instruction_offset = 3 * BytesPerInstWord, ++ }; ++ ++ int insn_word() const { return long_at(instruction_offset); } ++ address instruction_address() const { return addr_at(0); } ++ address next_instruction_address() const { return addr_at(next_instruction_offset); } ++ intptr_t data() const; ++ void set_data(intptr_t x, intptr_t o = 0); ++ ++ bool is_li52() const { ++ return is_lu12iw_ori_lu32id() || ++ is_lu12iw_lu32id_nop() || ++ is_lu12iw_2nop() || ++ is_lu12iw_ori_nop() || ++ is_addid_2nop(); ++ } ++ bool is_lu12iw_ori_lu32id() const; ++ bool is_lu12iw_lu32id_nop() const; ++ bool is_lu12iw_2nop() const; ++ bool is_lu12iw_ori_nop() const; ++ bool is_addid_2nop() const; ++ void verify(); ++ void print(); ++ ++ // unit test stuff ++ static void test() {} ++ ++ // Creation ++ inline friend NativeMovConstReg* nativeMovConstReg_at(address address); ++ inline friend NativeMovConstReg* nativeMovConstReg_before(address address); ++}; ++ ++inline NativeMovConstReg* nativeMovConstReg_at(address address) { ++ NativeMovConstReg* test = (NativeMovConstReg*)(address - NativeMovConstReg::instruction_offset); ++#ifdef ASSERT ++ test->verify(); ++#endif ++ return test; ++} ++ ++inline NativeMovConstReg* nativeMovConstReg_before(address address) { ++ NativeMovConstReg* test = (NativeMovConstReg*)(address - NativeMovConstReg::instruction_size - NativeMovConstReg::instruction_offset); ++#ifdef ASSERT ++ test->verify(); ++#endif ++ return test; ++} ++ ++class NativeMovConstRegPatching: public NativeMovConstReg { ++ private: ++ friend NativeMovConstRegPatching* nativeMovConstRegPatching_at(address address) { ++ NativeMovConstRegPatching* test = (NativeMovConstRegPatching*)(address - instruction_offset); ++ #ifdef ASSERT ++ test->verify(); ++ #endif ++ return test; ++ } ++}; ++ ++class NativeMovRegMem: public NativeInstruction { ++ public: ++ enum loongarch_specific_constants { ++ instruction_offset = 0, ++ instruction_size = 4, ++ hiword_offset = 4, ++ ldst_offset = 12, ++ immediate_size = 4, ++ ldst_size = 16 ++ }; ++ ++ address instruction_address() const { return addr_at(instruction_offset); } ++ ++ int num_bytes_to_end_of_patch() const { return instruction_offset + instruction_size; } ++ ++ int offset() const; ++ ++ void set_offset(int x); ++ ++ void add_offset_in_bytes(int add_offset) { set_offset ( ( offset() + add_offset ) ); } ++ ++ void verify(); ++ void print (); ++ ++ // unit test stuff ++ static void test() {} ++ ++ private: ++ inline friend NativeMovRegMem* nativeMovRegMem_at (address address); ++}; ++ ++inline NativeMovRegMem* nativeMovRegMem_at (address address) { ++ NativeMovRegMem* test = (NativeMovRegMem*)(address - NativeMovRegMem::instruction_offset); ++#ifdef ASSERT ++ test->verify(); ++#endif ++ return test; ++} ++ ++class NativeMovRegMemPatching: public NativeMovRegMem { ++ private: ++ friend NativeMovRegMemPatching* nativeMovRegMemPatching_at (address address) { ++ NativeMovRegMemPatching* test = (NativeMovRegMemPatching*)(address - instruction_offset); ++ #ifdef ASSERT ++ test->verify(); ++ #endif ++ return test; ++ } ++}; ++ ++ ++// Handles all kinds of jump on Loongson. ++// short: ++// b offs26 ++// nop ++// ++// far: ++// pcaddu18i reg, si20 ++// jirl r0, reg, si18 ++// ++class NativeJump: public NativeInstruction { ++ public: ++ enum loongarch_specific_constants { ++ instruction_offset = 0, ++ instruction_size = 2 * BytesPerInstWord ++ }; ++ ++ bool is_short(); ++ bool is_far(); ++ ++ address instruction_address() const { return addr_at(instruction_offset); } ++ address jump_destination(address orig_addr = 0); ++ void set_jump_destination(address dest); ++ ++ // Creation ++ inline friend NativeJump* nativeJump_at(address address); ++ ++ // Insertion of native jump instruction ++ static void insert(address code_pos, address entry) { Unimplemented(); } ++ // MT-safe insertion of native jump at verified method entry ++ static void check_verified_entry_alignment(address entry, address verified_entry){} ++ static void patch_verified_entry(address entry, address verified_entry, address dest); ++ ++ void verify(); ++}; ++ ++inline NativeJump* nativeJump_at(address address) { ++ NativeJump* jump = (NativeJump*)(address - NativeJump::instruction_offset); ++ debug_only(jump->verify();) ++ return jump; ++} ++ ++class NativeGeneralJump: public NativeJump { ++ public: ++ // Creation ++ inline friend NativeGeneralJump* nativeGeneralJump_at(address address); ++ ++ // Insertion of native general jump instruction ++ static void insert_unconditional(address code_pos, address entry); ++ static void replace_mt_safe(address instr_addr, address code_buffer); ++}; ++ ++inline NativeGeneralJump* nativeGeneralJump_at(address address) { ++ NativeGeneralJump* jump = (NativeGeneralJump*)(address); ++ debug_only(jump->verify();) ++ return jump; ++} ++ ++class NativeIllegalInstruction: public NativeInstruction { ++public: ++ enum loongarch_specific_constants { ++ instruction_code = 0xbadc0de0, // TODO: LA ++ // Temporary LoongArch reserved instruction ++ instruction_size = 4, ++ instruction_offset = 0, ++ next_instruction_offset = 4 ++ }; ++ ++ // Insert illegal opcode as specific address ++ static void insert(address code_pos); ++}; ++ ++inline bool NativeInstruction::is_illegal() { return insn_word() == illegal_instruction(); } ++ ++inline bool NativeInstruction::is_call() { ++ NativeCall *call = (NativeCall*)instruction_address(); ++ return call->is_bl(); ++} ++ ++inline bool NativeInstruction::is_far_call() { ++ NativeFarCall *call = (NativeFarCall*)instruction_address(); ++ ++ // short ++ if (call->is_short()) { ++ return true; ++ } ++ ++ // far ++ if (call->is_far()) { ++ return true; ++ } ++ ++ return false; ++} ++ ++inline bool NativeInstruction::is_jump() ++{ ++ NativeGeneralJump *jump = (NativeGeneralJump*)instruction_address(); ++ ++ // short ++ if (jump->is_short()) { ++ return true; ++ } ++ ++ // far ++ if (jump->is_far()) { ++ return true; ++ } ++ ++ return false; ++} ++ ++// Call trampoline stubs. ++class NativeCallTrampolineStub : public NativeInstruction { ++ public: ++ ++ enum la_specific_constants { ++ instruction_size = 6 * 4, ++ instruction_offset = 0, ++ data_offset = 4 * 4, ++ next_instruction_offset = 6 * 4 ++ }; ++ ++ address destination() const { ++ return (address)ptr_at(data_offset); ++ } ++ ++ void set_destination(address new_destination) { ++ set_ptr_at(data_offset, (intptr_t)new_destination); ++ OrderAccess::fence(); ++ } ++}; ++ ++// Note: Other stubs must not begin with this pattern. ++inline bool NativeInstruction::is_NativeCallTrampolineStub_at() { ++ // pcaddi ++ // ld_d ++ // jirl ++ return Assembler::high(int_at(0), 7) == Assembler::pcaddi_op && ++ Assembler::high(int_at(4), 10) == Assembler::ld_d_op && ++ Assembler::high(int_at(8), 6) == Assembler::jirl_op && ++ Assembler::low(int_at(8), 5) == R0->encoding(); ++} ++ ++inline NativeCallTrampolineStub* nativeCallTrampolineStub_at(address addr) { ++ NativeInstruction* ni = nativeInstruction_at(addr); ++ assert(ni->is_NativeCallTrampolineStub_at(), "no call trampoline found"); ++ return (NativeCallTrampolineStub*)addr; ++} ++#endif // CPU_LOONGARCH_VM_NATIVEINST_LOONGARCH_HPP +diff --git a/hotspot/src/cpu/loongarch/vm/registerMap_loongarch.hpp b/hotspot/src/cpu/loongarch/vm/registerMap_loongarch.hpp +new file mode 100644 +index 0000000000..5ff7555d2f +--- /dev/null ++++ b/hotspot/src/cpu/loongarch/vm/registerMap_loongarch.hpp +@@ -0,0 +1,45 @@ ++/* ++ * Copyright (c) 1998, 2010, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2015, 2020, Loongson Technology. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ * ++ */ ++ ++#ifndef CPU_LOONGARCH_VM_REGISTERMAP_LOONGARCH_HPP ++#define CPU_LOONGARCH_VM_REGISTERMAP_LOONGARCH_HPP ++ ++// machine-dependent implemention for register maps ++ friend class frame; ++ ++ private: ++ // This is the hook for finding a register in an "well-known" location, ++ // such as a register block of a predetermined format. ++ // Since there is none, we just return NULL. ++ // See registerMap_sparc.hpp for an example of grabbing registers ++ // from register save areas of a standard layout. ++ address pd_location(VMReg reg) const {return NULL;} ++ ++ // no PD state to clear or copy: ++ void pd_clear() {} ++ void pd_initialize() {} ++ void pd_initialize_from(const RegisterMap* map) {} ++ ++#endif // CPU_LOONGARCH_VM_REGISTERMAP_LOONGARCH_HPP +diff --git a/hotspot/src/cpu/loongarch/vm/register_definitions_loongarch.cpp b/hotspot/src/cpu/loongarch/vm/register_definitions_loongarch.cpp +new file mode 100644 +index 0000000000..c6424c321f +--- /dev/null ++++ b/hotspot/src/cpu/loongarch/vm/register_definitions_loongarch.cpp +@@ -0,0 +1,103 @@ ++/* ++ * Copyright (c) 2002, 2013, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2015, 2020, Loongson Technology. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ * ++ */ ++ ++#include "precompiled.hpp" ++#include "asm/assembler.hpp" ++#include "asm/register.hpp" ++#include "register_loongarch.hpp" ++#ifdef TARGET_ARCH_MODEL_loongarch_32 ++# include "interp_masm_loongarch_32.hpp" ++#endif ++#ifdef TARGET_ARCH_MODEL_loongarch_64 ++# include "interp_masm_loongarch_64.hpp" ++#endif ++ ++REGISTER_DEFINITION(Register, noreg); ++REGISTER_DEFINITION(Register, r0); ++REGISTER_DEFINITION(Register, r1); ++REGISTER_DEFINITION(Register, r2); ++REGISTER_DEFINITION(Register, r3); ++REGISTER_DEFINITION(Register, r4); ++REGISTER_DEFINITION(Register, r5); ++REGISTER_DEFINITION(Register, r6); ++REGISTER_DEFINITION(Register, r7); ++REGISTER_DEFINITION(Register, r8); ++REGISTER_DEFINITION(Register, r9); ++REGISTER_DEFINITION(Register, r10); ++REGISTER_DEFINITION(Register, r11); ++REGISTER_DEFINITION(Register, r12); ++REGISTER_DEFINITION(Register, r13); ++REGISTER_DEFINITION(Register, r14); ++REGISTER_DEFINITION(Register, r15); ++REGISTER_DEFINITION(Register, r16); ++REGISTER_DEFINITION(Register, r17); ++REGISTER_DEFINITION(Register, r18); ++REGISTER_DEFINITION(Register, r19); ++REGISTER_DEFINITION(Register, r20); ++REGISTER_DEFINITION(Register, r21); ++REGISTER_DEFINITION(Register, r22); ++REGISTER_DEFINITION(Register, r23); ++REGISTER_DEFINITION(Register, r24); ++REGISTER_DEFINITION(Register, r25); ++REGISTER_DEFINITION(Register, r26); ++REGISTER_DEFINITION(Register, r27); ++REGISTER_DEFINITION(Register, r28); ++REGISTER_DEFINITION(Register, r29); ++REGISTER_DEFINITION(Register, r30); ++REGISTER_DEFINITION(Register, r31); ++ ++REGISTER_DEFINITION(FloatRegister, fnoreg); ++REGISTER_DEFINITION(FloatRegister, f0); ++REGISTER_DEFINITION(FloatRegister, f1); ++REGISTER_DEFINITION(FloatRegister, f2); ++REGISTER_DEFINITION(FloatRegister, f3); ++REGISTER_DEFINITION(FloatRegister, f4); ++REGISTER_DEFINITION(FloatRegister, f5); ++REGISTER_DEFINITION(FloatRegister, f6); ++REGISTER_DEFINITION(FloatRegister, f7); ++REGISTER_DEFINITION(FloatRegister, f8); ++REGISTER_DEFINITION(FloatRegister, f9); ++REGISTER_DEFINITION(FloatRegister, f10); ++REGISTER_DEFINITION(FloatRegister, f11); ++REGISTER_DEFINITION(FloatRegister, f12); ++REGISTER_DEFINITION(FloatRegister, f13); ++REGISTER_DEFINITION(FloatRegister, f14); ++REGISTER_DEFINITION(FloatRegister, f15); ++REGISTER_DEFINITION(FloatRegister, f16); ++REGISTER_DEFINITION(FloatRegister, f17); ++REGISTER_DEFINITION(FloatRegister, f18); ++REGISTER_DEFINITION(FloatRegister, f19); ++REGISTER_DEFINITION(FloatRegister, f20); ++REGISTER_DEFINITION(FloatRegister, f21); ++REGISTER_DEFINITION(FloatRegister, f22); ++REGISTER_DEFINITION(FloatRegister, f23); ++REGISTER_DEFINITION(FloatRegister, f24); ++REGISTER_DEFINITION(FloatRegister, f25); ++REGISTER_DEFINITION(FloatRegister, f26); ++REGISTER_DEFINITION(FloatRegister, f27); ++REGISTER_DEFINITION(FloatRegister, f28); ++REGISTER_DEFINITION(FloatRegister, f29); ++REGISTER_DEFINITION(FloatRegister, f30); ++REGISTER_DEFINITION(FloatRegister, f31); +diff --git a/hotspot/src/cpu/loongarch/vm/register_loongarch.cpp b/hotspot/src/cpu/loongarch/vm/register_loongarch.cpp +new file mode 100644 +index 0000000000..3104cd1cc5 +--- /dev/null ++++ b/hotspot/src/cpu/loongarch/vm/register_loongarch.cpp +@@ -0,0 +1,59 @@ ++/* ++ * Copyright (c) 2000, 2012, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2015, 2020, Loongson Technology. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ * ++ */ ++ ++#include "precompiled.hpp" ++#include "register_loongarch.hpp" ++ ++const int ConcreteRegisterImpl::max_gpr = RegisterImpl::number_of_registers << 1; ++const int ConcreteRegisterImpl::max_fpr = ConcreteRegisterImpl::max_gpr + ++ 2 * FloatRegisterImpl::number_of_registers; ++ ++ ++const char* RegisterImpl::name() const { ++ const char* names[number_of_registers] = { ++ "zero", "ra", "tp", "sp", "a0/v0", "a1/v1", "a2", "a3", ++ "a4", "a5", "a6", "a7", "t0", "t1", "t2", "t3", ++ "t4", "t5", "t6", "t7", "t8", "x", "fp", "s0", ++ "s1", "s2", "s3", "s4", "s5", "s6", "s7", "s8" ++ }; ++ return is_valid() ? names[encoding()] : "noreg"; ++} ++ ++const char* FloatRegisterImpl::name() const { ++ const char* names[number_of_registers] = { ++ "f0", "f1", "f2", "f3", "f4", "f5", "f6", "f7", ++ "f8", "f9", "f10", "f11", "f12", "f13", "f14", "f15", ++ "f16", "f17", "f18", "f19", "f20", "f21", "f22", "f23", ++ "f24", "f25", "f26", "f27", "f28", "f29", "f30", "f31", ++ }; ++ return is_valid() ? names[encoding()] : "fnoreg"; ++} ++ ++const char* ConditionalFlagRegisterImpl::name() const { ++ const char* names[number_of_registers] = { ++ "fcc0", "fcc1", "fcc2", "fcc3", "fcc4", "fcc5", "fcc6", "fcc7", ++ }; ++ return is_valid() ? names[encoding()] : "fccnoreg"; ++} +diff --git a/hotspot/src/cpu/loongarch/vm/register_loongarch.hpp b/hotspot/src/cpu/loongarch/vm/register_loongarch.hpp +new file mode 100644 +index 0000000000..37b39f9129 +--- /dev/null ++++ b/hotspot/src/cpu/loongarch/vm/register_loongarch.hpp +@@ -0,0 +1,436 @@ ++/* ++ * Copyright (c) 2000, 2012, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2015, 2020, Loongson Technology. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ * ++ */ ++ ++#ifndef CPU_LOONGARCH_VM_REGISTER_LOONGARCH_HPP ++#define CPU_LOONGARCH_VM_REGISTER_LOONGARCH_HPP ++ ++#include "asm/register.hpp" ++#include "vm_version_loongarch.hpp" ++ ++class VMRegImpl; ++typedef VMRegImpl* VMReg; ++ ++// Use Register as shortcut ++class RegisterImpl; ++typedef RegisterImpl* Register; ++ ++ ++// The implementation of integer registers for the LoongArch architecture ++inline Register as_Register(int encoding) { ++ return (Register)(intptr_t) encoding; ++} ++ ++class RegisterImpl: public AbstractRegisterImpl { ++ public: ++ enum { ++ number_of_registers = 32, ++ max_slots_per_register = 2 ++ }; ++ ++ // derived registers, offsets, and addresses ++ Register successor() const { return as_Register(encoding() + 1); } ++ ++ // construction ++ inline friend Register as_Register(int encoding); ++ ++ VMReg as_VMReg(); ++ ++ // accessors ++ int encoding() const { assert(is_valid(),err_msg( "invalid register (%d)", (int)(intptr_t)this)); return (intptr_t)this; } ++ bool is_valid() const { return 0 <= (intptr_t)this && (intptr_t)this < number_of_registers; } ++ const char* name() const; ++}; ++ ++ ++// The integer registers of the LoongArch architecture ++CONSTANT_REGISTER_DECLARATION(Register, noreg, (-1)); ++ ++ ++CONSTANT_REGISTER_DECLARATION(Register, r0, (0)); ++CONSTANT_REGISTER_DECLARATION(Register, r1, (1)); ++CONSTANT_REGISTER_DECLARATION(Register, r2, (2)); ++CONSTANT_REGISTER_DECLARATION(Register, r3, (3)); ++CONSTANT_REGISTER_DECLARATION(Register, r4, (4)); ++CONSTANT_REGISTER_DECLARATION(Register, r5, (5)); ++CONSTANT_REGISTER_DECLARATION(Register, r6, (6)); ++CONSTANT_REGISTER_DECLARATION(Register, r7, (7)); ++CONSTANT_REGISTER_DECLARATION(Register, r8, (8)); ++CONSTANT_REGISTER_DECLARATION(Register, r9, (9)); ++CONSTANT_REGISTER_DECLARATION(Register, r10, (10)); ++CONSTANT_REGISTER_DECLARATION(Register, r11, (11)); ++CONSTANT_REGISTER_DECLARATION(Register, r12, (12)); ++CONSTANT_REGISTER_DECLARATION(Register, r13, (13)); ++CONSTANT_REGISTER_DECLARATION(Register, r14, (14)); ++CONSTANT_REGISTER_DECLARATION(Register, r15, (15)); ++CONSTANT_REGISTER_DECLARATION(Register, r16, (16)); ++CONSTANT_REGISTER_DECLARATION(Register, r17, (17)); ++CONSTANT_REGISTER_DECLARATION(Register, r18, (18)); ++CONSTANT_REGISTER_DECLARATION(Register, r19, (19)); ++CONSTANT_REGISTER_DECLARATION(Register, r20, (20)); ++CONSTANT_REGISTER_DECLARATION(Register, r21, (21)); ++CONSTANT_REGISTER_DECLARATION(Register, r22, (22)); ++CONSTANT_REGISTER_DECLARATION(Register, r23, (23)); ++CONSTANT_REGISTER_DECLARATION(Register, r24, (24)); ++CONSTANT_REGISTER_DECLARATION(Register, r25, (25)); ++CONSTANT_REGISTER_DECLARATION(Register, r26, (26)); ++CONSTANT_REGISTER_DECLARATION(Register, r27, (27)); ++CONSTANT_REGISTER_DECLARATION(Register, r28, (28)); ++CONSTANT_REGISTER_DECLARATION(Register, r29, (29)); ++CONSTANT_REGISTER_DECLARATION(Register, r30, (30)); ++CONSTANT_REGISTER_DECLARATION(Register, r31, (31)); ++ ++#ifndef DONT_USE_REGISTER_DEFINES ++#define NOREG ((Register)(noreg_RegisterEnumValue)) ++ ++#define R0 ((Register)(r0_RegisterEnumValue)) ++#define R1 ((Register)(r1_RegisterEnumValue)) ++#define R2 ((Register)(r2_RegisterEnumValue)) ++#define R3 ((Register)(r3_RegisterEnumValue)) ++#define R4 ((Register)(r4_RegisterEnumValue)) ++#define R5 ((Register)(r5_RegisterEnumValue)) ++#define R6 ((Register)(r6_RegisterEnumValue)) ++#define R7 ((Register)(r7_RegisterEnumValue)) ++#define R8 ((Register)(r8_RegisterEnumValue)) ++#define R9 ((Register)(r9_RegisterEnumValue)) ++#define R10 ((Register)(r10_RegisterEnumValue)) ++#define R11 ((Register)(r11_RegisterEnumValue)) ++#define R12 ((Register)(r12_RegisterEnumValue)) ++#define R13 ((Register)(r13_RegisterEnumValue)) ++#define R14 ((Register)(r14_RegisterEnumValue)) ++#define R15 ((Register)(r15_RegisterEnumValue)) ++#define R16 ((Register)(r16_RegisterEnumValue)) ++#define R17 ((Register)(r17_RegisterEnumValue)) ++#define R18 ((Register)(r18_RegisterEnumValue)) ++#define R19 ((Register)(r19_RegisterEnumValue)) ++#define R20 ((Register)(r20_RegisterEnumValue)) ++#define R21 ((Register)(r21_RegisterEnumValue)) ++#define R22 ((Register)(r22_RegisterEnumValue)) ++#define R23 ((Register)(r23_RegisterEnumValue)) ++#define R24 ((Register)(r24_RegisterEnumValue)) ++#define R25 ((Register)(r25_RegisterEnumValue)) ++#define R26 ((Register)(r26_RegisterEnumValue)) ++#define R27 ((Register)(r27_RegisterEnumValue)) ++#define R28 ((Register)(r28_RegisterEnumValue)) ++#define R29 ((Register)(r29_RegisterEnumValue)) ++#define R30 ((Register)(r30_RegisterEnumValue)) ++#define R31 ((Register)(r31_RegisterEnumValue)) ++ ++ ++#define RA R1 ++#define TP R2 ++#define SP R3 ++#define RA0 R4 ++#define RA1 R5 ++#define RA2 R6 ++#define RA3 R7 ++#define RA4 R8 ++#define RA5 R9 ++#define RA6 R10 ++#define RA7 R11 ++#define RT0 R12 ++#define RT1 R13 ++#define RT2 R14 ++#define RT3 R15 ++#define RT4 R16 ++#define RT5 R17 ++#define RT6 R18 ++#define RT7 R19 ++#define RT8 R20 ++#define RX R21 ++#define FP R22 ++#define S0 R23 ++#define S1 R24 ++#define S2 R25 ++#define S3 R26 ++#define S4 R27 ++#define S5 R28 ++#define S6 R29 ++#define S7 R30 ++#define S8 R31 ++ ++#define c_rarg0 RT0 ++#define c_rarg1 RT1 ++#define Rmethod S3 ++#define Rsender S4 ++#define Rnext S1 ++ ++#define V0 RA0 ++#define V1 RA1 ++ ++#define SCR1 RT7 ++#define SCR2 RT4 ++ ++//for interpreter frame ++// bytecode pointer register ++#define BCP S0 ++// local variable pointer register ++#define LVP S7 ++// temperary callee saved register, we use this register to save the register maybe blowed cross call_VM ++// be sure to save and restore its value in call_stub ++#define TSR S2 ++ ++//OPT_SAFEPOINT not supported yet ++#define OPT_SAFEPOINT 1 ++ ++#define OPT_THREAD 1 ++ ++#define TREG S6 ++ ++#define S5_heapbase S5 ++ ++#define FSR V0 ++#define SSR T6 ++#define FSF FV0 ++ ++#define RECEIVER T0 ++#define IC_Klass T1 ++ ++#define SHIFT_count T3 ++ ++// ---------- Scratch Register ---------- ++#define AT RT7 ++#define fscratch F23 ++ ++#endif // DONT_USE_REGISTER_DEFINES ++ ++// Use FloatRegister as shortcut ++class FloatRegisterImpl; ++typedef FloatRegisterImpl* FloatRegister; ++ ++inline FloatRegister as_FloatRegister(int encoding) { ++ return (FloatRegister)(intptr_t) encoding; ++} ++ ++// The implementation of floating point registers for the LoongArch architecture ++class FloatRegisterImpl: public AbstractRegisterImpl { ++ public: ++ enum { ++ number_of_registers = 32, ++ save_slots_per_register = 2, ++ slots_per_lsx_register = 4, ++ slots_per_lasx_register = 8, ++ max_slots_per_register = 8 ++ }; ++ ++ // construction ++ inline friend FloatRegister as_FloatRegister(int encoding); ++ ++ VMReg as_VMReg(); ++ ++ // derived registers, offsets, and addresses ++ FloatRegister successor() const { return as_FloatRegister(encoding() + 1); } ++ ++ // accessors ++ int encoding() const { assert(is_valid(), "invalid register"); return (intptr_t)this; } ++ bool is_valid() const { return 0 <= (intptr_t)this && (intptr_t)this < number_of_registers; } ++ const char* name() const; ++ ++}; ++ ++CONSTANT_REGISTER_DECLARATION(FloatRegister, fnoreg , (-1)); ++ ++CONSTANT_REGISTER_DECLARATION(FloatRegister, f0 , ( 0)); ++CONSTANT_REGISTER_DECLARATION(FloatRegister, f1 , ( 1)); ++CONSTANT_REGISTER_DECLARATION(FloatRegister, f2 , ( 2)); ++CONSTANT_REGISTER_DECLARATION(FloatRegister, f3 , ( 3)); ++CONSTANT_REGISTER_DECLARATION(FloatRegister, f4 , ( 4)); ++CONSTANT_REGISTER_DECLARATION(FloatRegister, f5 , ( 5)); ++CONSTANT_REGISTER_DECLARATION(FloatRegister, f6 , ( 6)); ++CONSTANT_REGISTER_DECLARATION(FloatRegister, f7 , ( 7)); ++CONSTANT_REGISTER_DECLARATION(FloatRegister, f8 , ( 8)); ++CONSTANT_REGISTER_DECLARATION(FloatRegister, f9 , ( 9)); ++CONSTANT_REGISTER_DECLARATION(FloatRegister, f10 , (10)); ++CONSTANT_REGISTER_DECLARATION(FloatRegister, f11 , (11)); ++CONSTANT_REGISTER_DECLARATION(FloatRegister, f12 , (12)); ++CONSTANT_REGISTER_DECLARATION(FloatRegister, f13 , (13)); ++CONSTANT_REGISTER_DECLARATION(FloatRegister, f14 , (14)); ++CONSTANT_REGISTER_DECLARATION(FloatRegister, f15 , (15)); ++CONSTANT_REGISTER_DECLARATION(FloatRegister, f16 , (16)); ++CONSTANT_REGISTER_DECLARATION(FloatRegister, f17 , (17)); ++CONSTANT_REGISTER_DECLARATION(FloatRegister, f18 , (18)); ++CONSTANT_REGISTER_DECLARATION(FloatRegister, f19 , (19)); ++CONSTANT_REGISTER_DECLARATION(FloatRegister, f20 , (20)); ++CONSTANT_REGISTER_DECLARATION(FloatRegister, f21 , (21)); ++CONSTANT_REGISTER_DECLARATION(FloatRegister, f22 , (22)); ++CONSTANT_REGISTER_DECLARATION(FloatRegister, f23 , (23)); ++CONSTANT_REGISTER_DECLARATION(FloatRegister, f24 , (24)); ++CONSTANT_REGISTER_DECLARATION(FloatRegister, f25 , (25)); ++CONSTANT_REGISTER_DECLARATION(FloatRegister, f26 , (26)); ++CONSTANT_REGISTER_DECLARATION(FloatRegister, f27 , (27)); ++CONSTANT_REGISTER_DECLARATION(FloatRegister, f28 , (28)); ++CONSTANT_REGISTER_DECLARATION(FloatRegister, f29 , (29)); ++CONSTANT_REGISTER_DECLARATION(FloatRegister, f30 , (30)); ++CONSTANT_REGISTER_DECLARATION(FloatRegister, f31 , (31)); ++ ++#ifndef DONT_USE_REGISTER_DEFINES ++#define FNOREG ((FloatRegister)(fnoreg_FloatRegisterEnumValue)) ++#define F0 ((FloatRegister)( f0_FloatRegisterEnumValue)) ++#define F1 ((FloatRegister)( f1_FloatRegisterEnumValue)) ++#define F2 ((FloatRegister)( f2_FloatRegisterEnumValue)) ++#define F3 ((FloatRegister)( f3_FloatRegisterEnumValue)) ++#define F4 ((FloatRegister)( f4_FloatRegisterEnumValue)) ++#define F5 ((FloatRegister)( f5_FloatRegisterEnumValue)) ++#define F6 ((FloatRegister)( f6_FloatRegisterEnumValue)) ++#define F7 ((FloatRegister)( f7_FloatRegisterEnumValue)) ++#define F8 ((FloatRegister)( f8_FloatRegisterEnumValue)) ++#define F9 ((FloatRegister)( f9_FloatRegisterEnumValue)) ++#define F10 ((FloatRegister)( f10_FloatRegisterEnumValue)) ++#define F11 ((FloatRegister)( f11_FloatRegisterEnumValue)) ++#define F12 ((FloatRegister)( f12_FloatRegisterEnumValue)) ++#define F13 ((FloatRegister)( f13_FloatRegisterEnumValue)) ++#define F14 ((FloatRegister)( f14_FloatRegisterEnumValue)) ++#define F15 ((FloatRegister)( f15_FloatRegisterEnumValue)) ++#define F16 ((FloatRegister)( f16_FloatRegisterEnumValue)) ++#define F17 ((FloatRegister)( f17_FloatRegisterEnumValue)) ++#define F18 ((FloatRegister)( f18_FloatRegisterEnumValue)) ++#define F19 ((FloatRegister)( f19_FloatRegisterEnumValue)) ++#define F20 ((FloatRegister)( f20_FloatRegisterEnumValue)) ++#define F21 ((FloatRegister)( f21_FloatRegisterEnumValue)) ++#define F22 ((FloatRegister)( f22_FloatRegisterEnumValue)) ++#define F23 ((FloatRegister)( f23_FloatRegisterEnumValue)) ++#define F24 ((FloatRegister)( f24_FloatRegisterEnumValue)) ++#define F25 ((FloatRegister)( f25_FloatRegisterEnumValue)) ++#define F26 ((FloatRegister)( f26_FloatRegisterEnumValue)) ++#define F27 ((FloatRegister)( f27_FloatRegisterEnumValue)) ++#define F28 ((FloatRegister)( f28_FloatRegisterEnumValue)) ++#define F29 ((FloatRegister)( f29_FloatRegisterEnumValue)) ++#define F30 ((FloatRegister)( f30_FloatRegisterEnumValue)) ++#define F31 ((FloatRegister)( f31_FloatRegisterEnumValue)) ++ ++#define FA0 F0 ++#define FA1 F1 ++#define FA2 F2 ++#define FA3 F3 ++#define FA4 F4 ++#define FA5 F5 ++#define FA6 F6 ++#define FA7 F7 ++ ++#define FV0 F0 ++#define FV1 F1 ++ ++#define FT0 F8 ++#define FT1 F9 ++#define FT2 F10 ++#define FT3 F11 ++#define FT4 F12 ++#define FT5 F13 ++#define FT6 F14 ++#define FT7 F15 ++#define FT8 F16 ++#define FT9 F17 ++#define FT10 F18 ++#define FT11 F19 ++#define FT12 F20 ++#define FT13 F21 ++#define FT14 F22 ++#define FT15 F23 ++ ++#define FS0 F24 ++#define FS1 F25 ++#define FS2 F26 ++#define FS3 F27 ++#define FS4 F28 ++#define FS5 F29 ++#define FS6 F30 ++#define FS7 F31 ++ ++#endif // DONT_USE_REGISTER_DEFINES ++ ++// Use ConditionalFlagRegister as shortcut ++class ConditionalFlagRegisterImpl; ++typedef ConditionalFlagRegisterImpl* ConditionalFlagRegister; ++ ++inline ConditionalFlagRegister as_ConditionalFlagRegister(int encoding) { ++ return (ConditionalFlagRegister)(intptr_t) encoding; ++} ++ ++// The implementation of floating point registers for the LoongArch architecture ++class ConditionalFlagRegisterImpl: public AbstractRegisterImpl { ++ public: ++ enum { ++// conditionalflag_arg_base = 12, ++ number_of_registers = 8 ++ }; ++ ++ // construction ++ inline friend ConditionalFlagRegister as_ConditionalFlagRegister(int encoding); ++ ++ VMReg as_VMReg(); ++ ++ // derived registers, offsets, and addresses ++ ConditionalFlagRegister successor() const { return as_ConditionalFlagRegister(encoding() + 1); } ++ ++ // accessors ++ int encoding() const { assert(is_valid(), "invalid register"); return (intptr_t)this; } ++ bool is_valid() const { return 0 <= (intptr_t)this && (intptr_t)this < number_of_registers; } ++ const char* name() const; ++ ++}; ++ ++CONSTANT_REGISTER_DECLARATION(ConditionalFlagRegister, fccnoreg , (-1)); ++ ++CONSTANT_REGISTER_DECLARATION(ConditionalFlagRegister, fcc0 , ( 0)); ++CONSTANT_REGISTER_DECLARATION(ConditionalFlagRegister, fcc1 , ( 1)); ++CONSTANT_REGISTER_DECLARATION(ConditionalFlagRegister, fcc2 , ( 2)); ++CONSTANT_REGISTER_DECLARATION(ConditionalFlagRegister, fcc3 , ( 3)); ++CONSTANT_REGISTER_DECLARATION(ConditionalFlagRegister, fcc4 , ( 4)); ++CONSTANT_REGISTER_DECLARATION(ConditionalFlagRegister, fcc5 , ( 5)); ++CONSTANT_REGISTER_DECLARATION(ConditionalFlagRegister, fcc6 , ( 6)); ++CONSTANT_REGISTER_DECLARATION(ConditionalFlagRegister, fcc7 , ( 7)); ++ ++#ifndef DONT_USE_REGISTER_DEFINES ++#define FCCNOREG ((ConditionalFlagRegister)(fccnoreg_ConditionalFlagRegisterEnumValue)) ++#define FCC0 ((ConditionalFlagRegister)( fcc0_ConditionalFlagRegisterEnumValue)) ++#define FCC1 ((ConditionalFlagRegister)( fcc1_ConditionalFlagRegisterEnumValue)) ++#define FCC2 ((ConditionalFlagRegister)( fcc2_ConditionalFlagRegisterEnumValue)) ++#define FCC3 ((ConditionalFlagRegister)( fcc3_ConditionalFlagRegisterEnumValue)) ++#define FCC4 ((ConditionalFlagRegister)( fcc4_ConditionalFlagRegisterEnumValue)) ++#define FCC5 ((ConditionalFlagRegister)( fcc5_ConditionalFlagRegisterEnumValue)) ++#define FCC6 ((ConditionalFlagRegister)( fcc6_ConditionalFlagRegisterEnumValue)) ++#define FCC7 ((ConditionalFlagRegister)( fcc7_ConditionalFlagRegisterEnumValue)) ++ ++#endif // DONT_USE_REGISTER_DEFINES ++ ++// Need to know the total number of registers of all sorts for SharedInfo. ++// Define a class that exports it. ++class ConcreteRegisterImpl : public AbstractRegisterImpl { ++ public: ++ enum { ++ // A big enough number for C2: all the registers plus flags ++ // This number must be large enough to cover REG_COUNT (defined by c2) registers. ++ // There is no requirement that any ordering here matches any ordering c2 gives ++ // it's optoregs. ++ number_of_registers = RegisterImpl::max_slots_per_register * RegisterImpl::number_of_registers + ++ FloatRegisterImpl::max_slots_per_register * FloatRegisterImpl::number_of_registers ++ }; ++ ++ static const int max_gpr; ++ static const int max_fpr; ++ ++ ++}; ++ ++#endif //CPU_LOONGARCH_VM_REGISTER_LOONGARCH_HPP +diff --git a/hotspot/src/cpu/loongarch/vm/relocInfo_loongarch.cpp b/hotspot/src/cpu/loongarch/vm/relocInfo_loongarch.cpp +new file mode 100644 +index 0000000000..8db5e71562 +--- /dev/null ++++ b/hotspot/src/cpu/loongarch/vm/relocInfo_loongarch.cpp +@@ -0,0 +1,129 @@ ++/* ++ * Copyright (c) 1998, 2013, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2015, 2021, Loongson Technology. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ * ++ */ ++ ++#include "precompiled.hpp" ++#include "asm/macroAssembler.hpp" ++#include "code/relocInfo.hpp" ++#include "nativeInst_loongarch.hpp" ++#include "oops/oop.inline.hpp" ++#include "runtime/safepoint.hpp" ++ ++ ++void Relocation::pd_set_data_value(address x, intptr_t o, bool verify_only) { ++ x += o; ++ typedef Assembler::WhichOperand WhichOperand; ++ WhichOperand which = (WhichOperand) format(); // that is, disp32 or imm, call32, narrow oop ++ assert(which == Assembler::disp32_operand || ++ which == Assembler::narrow_oop_operand || ++ which == Assembler::imm_operand, "format unpacks ok"); ++ if (which == Assembler::imm_operand) { ++ if (verify_only) { ++ assert(nativeMovConstReg_at(addr())->data() == (long)x, "instructions must match"); ++ } else { ++ nativeMovConstReg_at(addr())->set_data((intptr_t)(x)); ++ } ++ } else if (which == Assembler::narrow_oop_operand) { ++ // both compressed oops and compressed classes look the same ++ if (Universe::heap()->is_in_reserved((oop)x)) { ++ if (verify_only) { ++ assert(nativeMovConstReg_at(addr())->data() == (long)oopDesc::encode_heap_oop((oop)x), "instructions must match"); ++ } else { ++ nativeMovConstReg_at(addr())->set_data((intptr_t)(oopDesc::encode_heap_oop((oop)x)), (intptr_t)(x)); ++ } ++ } else { ++ if (verify_only) { ++ assert(nativeMovConstReg_at(addr())->data() == (long)Klass::encode_klass((Klass*)x), "instructions must match"); ++ } else { ++ nativeMovConstReg_at(addr())->set_data((intptr_t)(Klass::encode_klass((Klass*)x)), (intptr_t)(x)); ++ } ++ } ++ } else { ++ // Note: Use runtime_call_type relocations for call32_operand. ++ assert(0, "call32_operand not supported in LoongArch64"); ++ } ++} ++ ++ ++address Relocation::pd_call_destination(address orig_addr) { ++ NativeInstruction* ni = nativeInstruction_at(addr()); ++ if (ni->is_far_call()) { ++ return nativeFarCall_at(addr())->destination(orig_addr); ++ } else if (ni->is_call()) { ++ address trampoline = nativeCall_at(addr())->get_trampoline(); ++ if (trampoline) { ++ return nativeCallTrampolineStub_at(trampoline)->destination(); ++ } else { ++ address new_addr = nativeCall_at(addr())->target_addr_for_bl(orig_addr); ++ // If call is branch to self, don't try to relocate it, just leave it ++ // as branch to self. This happens during code generation if the code ++ // buffer expands. It will be relocated to the trampoline above once ++ // code generation is complete. ++ return (new_addr == orig_addr) ? addr() : new_addr; ++ } ++ } else if (ni->is_jump()) { ++ return nativeGeneralJump_at(addr())->jump_destination(orig_addr); ++ } else { ++ tty->print_cr("\nError!\ncall destination: 0x%lx", p2i(addr())); ++ Disassembler::decode(addr() - 10 * BytesPerInstWord, addr() + 10 * BytesPerInstWord, tty); ++ ShouldNotReachHere(); ++ return NULL; ++ } ++} ++ ++void Relocation::pd_set_call_destination(address x) { ++ NativeInstruction* ni = nativeInstruction_at(addr()); ++ if (ni->is_far_call()) { ++ nativeFarCall_at(addr())->set_destination(x); ++ } else if (ni->is_call()) { ++ address trampoline = nativeCall_at(addr())->get_trampoline(); ++ if (trampoline) { ++ nativeCall_at(addr())->set_destination_mt_safe(x, false); ++ } else { ++ nativeCall_at(addr())->set_destination(x); ++ } ++ } else if (ni->is_jump()) { ++ nativeGeneralJump_at(addr())->set_jump_destination(x); ++ } else { ++ ShouldNotReachHere(); ++ } ++} ++ ++address* Relocation::pd_address_in_code() { ++ return (address*)addr(); ++} ++ ++address Relocation::pd_get_address_from_code() { ++ NativeMovConstReg* ni = nativeMovConstReg_at(addr()); ++ return (address)ni->data(); ++} ++ ++void poll_Relocation::fix_relocation_after_move(const CodeBuffer* src, CodeBuffer* dest) { ++} ++ ++void poll_return_Relocation::fix_relocation_after_move(const CodeBuffer* src, CodeBuffer* dest) { ++} ++ ++void metadata_Relocation::pd_fix_value(address x) { ++} +diff --git a/hotspot/src/cpu/loongarch/vm/relocInfo_loongarch.hpp b/hotspot/src/cpu/loongarch/vm/relocInfo_loongarch.hpp +new file mode 100644 +index 0000000000..211242f3fb +--- /dev/null ++++ b/hotspot/src/cpu/loongarch/vm/relocInfo_loongarch.hpp +@@ -0,0 +1,40 @@ ++/* ++ * Copyright (c) 1997, 2010, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2015, 2021, Loongson Technology. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ * ++ */ ++ ++#ifndef CPU_LOONGARCH_VM_RELOCINFO_LOONGARCH_HPP ++#define CPU_LOONGARCH_VM_RELOCINFO_LOONGARCH_HPP ++ ++ // machine-dependent parts of class relocInfo ++ private: ++ enum { ++ // Since LoongArch instructions are whole words, ++ // the two low-order offset bits can always be discarded. ++ offset_unit = 4, ++ ++ // imm_oop_operand vs. narrow_oop_operand ++ format_width = 2 ++ }; ++ ++#endif // CPU_LOONGARCH_VM_RELOCINFO_LOONGARCH_HPP +diff --git a/hotspot/src/cpu/loongarch/vm/runtime_loongarch_64.cpp b/hotspot/src/cpu/loongarch/vm/runtime_loongarch_64.cpp +new file mode 100644 +index 0000000000..e6ee65f367 +--- /dev/null ++++ b/hotspot/src/cpu/loongarch/vm/runtime_loongarch_64.cpp +@@ -0,0 +1,199 @@ ++/* ++ * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2015, 2021, Loongson Technology. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ * ++ */ ++ ++#include "precompiled.hpp" ++#ifdef COMPILER2 ++#include "asm/macroAssembler.hpp" ++#include "asm/macroAssembler.inline.hpp" ++#include "classfile/systemDictionary.hpp" ++#include "code/vmreg.hpp" ++#include "interpreter/interpreter.hpp" ++#include "opto/runtime.hpp" ++#include "runtime/interfaceSupport.hpp" ++#include "runtime/sharedRuntime.hpp" ++#include "runtime/stubRoutines.hpp" ++#include "runtime/vframeArray.hpp" ++#include "utilities/globalDefinitions.hpp" ++#include "vmreg_loongarch.inline.hpp" ++#endif ++ ++#define __ masm-> ++ ++#define A0 RA0 ++#define A1 RA1 ++#define A2 RA2 ++#define A3 RA3 ++#define A4 RA4 ++#define A5 RA5 ++#define A6 RA6 ++#define A7 RA7 ++#define T0 RT0 ++#define T1 RT1 ++#define T2 RT2 ++#define T3 RT3 ++#define T4 RT4 ++#define T5 RT5 ++#define T6 RT6 ++#define T7 RT7 ++#define T8 RT8 ++ ++//-------------- generate_exception_blob ----------- ++// creates _exception_blob. ++// The exception blob is jumped to from a compiled method. ++// (see emit_exception_handler in sparc.ad file) ++// ++// Given an exception pc at a call we call into the runtime for the ++// handler in this method. This handler might merely restore state ++// (i.e. callee save registers) unwind the frame and jump to the ++// exception handler for the nmethod if there is no Java level handler ++// for the nmethod. ++// ++// This code is entered with a jump, and left with a jump. ++// ++// Arguments: ++// V0: exception oop ++// V1: exception pc ++// ++// Results: ++// A0: exception oop ++// A1: exception pc in caller or ??? ++// jumps to: exception handler of caller ++// ++// Note: the exception pc MUST be at a call (precise debug information) ++// ++// [stubGenerator_loongarch_64.cpp] generate_forward_exception() ++// |- V0, V1 are created ++// |- T4 <= SharedRuntime::exception_handler_for_return_address ++// `- jr T4 ++// `- the caller's exception_handler ++// `- jr OptoRuntime::exception_blob ++// `- here ++// ++void OptoRuntime::generate_exception_blob() { ++ // Capture info about frame layout ++ enum layout { ++ fp_off, ++ return_off, // slot for return address ++ framesize ++ }; ++ ++ // allocate space for the code ++ ResourceMark rm; ++ // setup code generation tools ++ CodeBuffer buffer("exception_blob", 5120, 5120); ++ MacroAssembler* masm = new MacroAssembler(&buffer); ++ ++ address start = __ pc(); ++ ++ __ addi_d(SP, SP, -1 * framesize * wordSize); // Prolog! ++ ++ // this frame will be treated as the original caller method. ++ // So, the return pc should be filled with the original exception pc. ++ // ref: X86's implementation ++ __ st_d(V1, SP, return_off * wordSize); // return address ++ __ st_d(FP, SP, fp_off * wordSize); ++ ++ // Save callee saved registers. None for UseSSE=0, ++ // floats-only for UseSSE=1, and doubles for UseSSE=2. ++ ++ __ addi_d(FP, SP, fp_off * wordSize); ++ ++ // Store exception in Thread object. We cannot pass any arguments to the ++ // handle_exception call, since we do not want to make any assumption ++ // about the size of the frame where the exception happened in. ++ Register thread = TREG; ++ ++#ifndef OPT_THREAD ++ __ get_thread(thread); ++#endif ++ ++ __ st_d(V0, Address(thread, JavaThread::exception_oop_offset())); ++ __ st_d(V1, Address(thread, JavaThread::exception_pc_offset())); ++ ++ // This call does all the hard work. It checks if an exception handler ++ // exists in the method. ++ // If so, it returns the handler address. ++ // If not, it prepares for stack-unwinding, restoring the callee-save ++ // registers of the frame being removed. ++ Label L; ++ address the_pc = __ pc(); ++ __ bind(L); ++ __ set_last_Java_frame(thread, NOREG, NOREG, L); ++ ++ __ li(AT, -(StackAlignmentInBytes)); ++ __ andr(SP, SP, AT); // Fix stack alignment as required by ABI ++ ++ __ move(A0, thread); ++ // TODO: confirm reloc ++ __ call((address)OptoRuntime::handle_exception_C, relocInfo::runtime_call_type); ++ ++ // Set an oopmap for the call site ++ OopMapSet *oop_maps = new OopMapSet(); ++ ++ oop_maps->add_gc_map(the_pc - start, new OopMap(framesize, 0)); ++ ++#ifndef OPT_THREAD ++ __ get_thread(thread); ++#endif ++ __ reset_last_Java_frame(thread, true); ++ ++ // Pop self-frame. ++ __ leave(); // Epilog! ++ ++ // V0: exception handler ++ ++ // We have a handler in V0, (could be deopt blob) ++ __ move(T4, V0); ++ ++#ifndef OPT_THREAD ++ __ get_thread(thread); ++#endif ++ // Get the exception ++ __ ld_d(A0, Address(thread, JavaThread::exception_oop_offset())); ++ // Get the exception pc in case we are deoptimized ++ __ ld_d(A1, Address(thread, JavaThread::exception_pc_offset())); ++#ifdef ASSERT ++ __ st_d(R0, Address(thread, JavaThread::exception_handler_pc_offset())); ++ __ st_d(R0, Address(thread, JavaThread::exception_pc_offset())); ++#endif ++ // Clear the exception oop so GC no longer processes it as a root. ++ __ st_d(R0, Address(thread, JavaThread::exception_oop_offset())); ++ ++ // Fix seg fault when running: ++ // Eclipse + Plugin + Debug As ++ // This is the only condition where C2 calls SharedRuntime::generate_deopt_blob() ++ // ++ __ move(V0, A0); ++ __ move(V1, A1); ++ ++ // V0: exception oop ++ // T4: exception handler ++ // A1: exception pc ++ __ jr(T4); ++ ++ // make sure all code is generated ++ masm->flush(); ++ _exception_blob = ExceptionBlob::create(&buffer, oop_maps, framesize); ++} +diff --git a/hotspot/src/cpu/loongarch/vm/sharedRuntime_loongarch_64.cpp b/hotspot/src/cpu/loongarch/vm/sharedRuntime_loongarch_64.cpp +new file mode 100644 +index 0000000000..948191009a +--- /dev/null ++++ b/hotspot/src/cpu/loongarch/vm/sharedRuntime_loongarch_64.cpp +@@ -0,0 +1,3453 @@ ++/* ++ * Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2015, 2021, Loongson Technology. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ * ++ */ ++ ++#include "precompiled.hpp" ++#include "asm/macroAssembler.hpp" ++#include "asm/macroAssembler.inline.hpp" ++#include "code/debugInfoRec.hpp" ++#include "code/icBuffer.hpp" ++#include "code/vtableStubs.hpp" ++#include "interpreter/interpreter.hpp" ++#include "oops/compiledICHolder.hpp" ++#include "prims/jvmtiRedefineClassesTrace.hpp" ++#include "runtime/sharedRuntime.hpp" ++#include "runtime/vframeArray.hpp" ++#include "vmreg_loongarch.inline.hpp" ++#ifdef COMPILER2 ++#include "opto/runtime.hpp" ++#endif ++ ++#include ++ ++#define __ masm-> ++ ++#define A0 RA0 ++#define A1 RA1 ++#define A2 RA2 ++#define A3 RA3 ++#define A4 RA4 ++#define A5 RA5 ++#define A6 RA6 ++#define A7 RA7 ++#define T0 RT0 ++#define T1 RT1 ++#define T2 RT2 ++#define T3 RT3 ++#define T4 RT4 ++#define T5 RT5 ++#define T6 RT6 ++#define T7 RT7 ++#define T8 RT8 ++ ++const int StackAlignmentInSlots = StackAlignmentInBytes / VMRegImpl::stack_slot_size; ++ ++class RegisterSaver { ++ // Capture info about frame layout ++ enum layout { ++ fpr0_off = 0, ++ fpr1_off, ++ fpr2_off, ++ fpr3_off, ++ fpr4_off, ++ fpr5_off, ++ fpr6_off, ++ fpr7_off, ++ fpr8_off, ++ fpr9_off, ++ fpr10_off, ++ fpr11_off, ++ fpr12_off, ++ fpr13_off, ++ fpr14_off, ++ fpr15_off, ++ fpr16_off, ++ fpr17_off, ++ fpr18_off, ++ fpr19_off, ++ fpr20_off, ++ fpr21_off, ++ fpr22_off, ++ fpr23_off, ++ fpr24_off, ++ fpr25_off, ++ fpr26_off, ++ fpr27_off, ++ fpr28_off, ++ fpr29_off, ++ fpr30_off, ++ fpr31_off, ++ a0_off, ++ a1_off, ++ a2_off, ++ a3_off, ++ a4_off, ++ a5_off, ++ a6_off, ++ a7_off, ++ t0_off, ++ t1_off, ++ t2_off, ++ t3_off, ++ t4_off, ++ t5_off, ++ t6_off, ++ t7_off, ++ t8_off, ++ s0_off, ++ s1_off, ++ s2_off, ++ s3_off, ++ s4_off, ++ s5_off, ++ s6_off, ++ s7_off, ++ s8_off, ++ fp_off, ++ ra_off, ++ fpr_size = fpr31_off - fpr0_off + 1, ++ gpr_size = ra_off - a0_off + 1, ++ }; ++ ++ const bool _save_vectors; ++ public: ++ RegisterSaver(bool save_vectors) : _save_vectors(save_vectors) {} ++ ++ OopMap* save_live_registers(MacroAssembler* masm, int additional_frame_words, int* total_frame_words); ++ void restore_live_registers(MacroAssembler* masm); ++ ++ int slots_save() { ++ int slots = gpr_size * VMRegImpl::slots_per_word; ++ ++ if (_save_vectors && UseLASX) ++ slots += FloatRegisterImpl::slots_per_lasx_register * fpr_size; ++ else if (_save_vectors && UseLSX) ++ slots += FloatRegisterImpl::slots_per_lsx_register * fpr_size; ++ else ++ slots += FloatRegisterImpl::save_slots_per_register * fpr_size; ++ ++ return slots; ++ } ++ ++ int gpr_offset(int off) { ++ int slots_per_fpr = FloatRegisterImpl::save_slots_per_register; ++ int slots_per_gpr = VMRegImpl::slots_per_word; ++ ++ if (_save_vectors && UseLASX) ++ slots_per_fpr = FloatRegisterImpl::slots_per_lasx_register; ++ else if (_save_vectors && UseLSX) ++ slots_per_fpr = FloatRegisterImpl::slots_per_lsx_register; ++ ++ return (fpr_size * slots_per_fpr + (off - a0_off) * slots_per_gpr) * VMRegImpl::stack_slot_size; ++ } ++ ++ int fpr_offset(int off) { ++ int slots_per_fpr = FloatRegisterImpl::save_slots_per_register; ++ ++ if (_save_vectors && UseLASX) ++ slots_per_fpr = FloatRegisterImpl::slots_per_lasx_register; ++ else if (_save_vectors && UseLSX) ++ slots_per_fpr = FloatRegisterImpl::slots_per_lsx_register; ++ ++ return off * slots_per_fpr * VMRegImpl::stack_slot_size; ++ } ++ ++ int ra_offset() { return gpr_offset(ra_off); } ++ int t5_offset() { return gpr_offset(t5_off); } ++ int s3_offset() { return gpr_offset(s3_off); } ++ int v0_offset() { return gpr_offset(a0_off); } ++ int v1_offset() { return gpr_offset(a1_off); } ++ ++ int fpr0_offset() { return fpr_offset(fpr0_off); } ++ int fpr1_offset() { return fpr_offset(fpr1_off); } ++ ++ // During deoptimization only the result register need to be restored ++ // all the other values have already been extracted. ++ void restore_result_registers(MacroAssembler* masm); ++}; ++ ++OopMap* RegisterSaver::save_live_registers(MacroAssembler* masm, int additional_frame_words, int* total_frame_words) { ++ ++ // Always make the frame size 16-byte aligned ++ int frame_size_in_bytes = round_to(additional_frame_words * wordSize + slots_save() * VMRegImpl::stack_slot_size, StackAlignmentInBytes); ++ // OopMap frame size is in compiler stack slots (jint's) not bytes or words ++ int frame_size_in_slots = frame_size_in_bytes / VMRegImpl::stack_slot_size; ++ // The caller will allocate additional_frame_words ++ int additional_frame_slots = additional_frame_words * wordSize / VMRegImpl::stack_slot_size; ++ // CodeBlob frame size is in words. ++ int frame_size_in_words = frame_size_in_bytes / wordSize; ++ ++ *total_frame_words = frame_size_in_words; ++ ++ OopMapSet *oop_maps = new OopMapSet(); ++ OopMap* map = new OopMap(frame_size_in_slots, 0); ++ ++ // save registers ++ __ addi_d(SP, SP, -slots_save() * VMRegImpl::stack_slot_size); ++ ++ for (int i = 0; i < fpr_size; i++) { ++ FloatRegister fpr = as_FloatRegister(i); ++ int off = fpr_offset(i); ++ ++ if (_save_vectors && UseLASX) ++ __ xvst(fpr, SP, off); ++ else if (_save_vectors && UseLSX) ++ __ vst(fpr, SP, off); ++ else ++ __ fst_d(fpr, SP, off); ++ map->set_callee_saved(VMRegImpl::stack2reg(off / VMRegImpl::stack_slot_size + additional_frame_slots), fpr->as_VMReg()); ++ } ++ ++ for (int i = a0_off; i <= a7_off; i++) { ++ Register gpr = as_Register(A0->encoding() + (i - a0_off)); ++ int off = gpr_offset(i); ++ ++ __ st_d(gpr, SP, gpr_offset(i)); ++ map->set_callee_saved(VMRegImpl::stack2reg(off / VMRegImpl::stack_slot_size + additional_frame_slots), gpr->as_VMReg()); ++ } ++ ++ for (int i = t0_off; i <= t6_off; i++) { ++ Register gpr = as_Register(T0->encoding() + (i - t0_off)); ++ int off = gpr_offset(i); ++ ++ __ st_d(gpr, SP, gpr_offset(i)); ++ map->set_callee_saved(VMRegImpl::stack2reg(off / VMRegImpl::stack_slot_size + additional_frame_slots), gpr->as_VMReg()); ++ } ++ __ st_d(T8, SP, gpr_offset(t8_off)); ++ map->set_callee_saved(VMRegImpl::stack2reg(gpr_offset(t8_off) / VMRegImpl::stack_slot_size + additional_frame_slots), T8->as_VMReg()); ++ ++ for (int i = s0_off; i <= s8_off; i++) { ++ Register gpr = as_Register(S0->encoding() + (i - s0_off)); ++ int off = gpr_offset(i); ++ ++ __ st_d(gpr, SP, gpr_offset(i)); ++ map->set_callee_saved(VMRegImpl::stack2reg(off / VMRegImpl::stack_slot_size + additional_frame_slots), gpr->as_VMReg()); ++ } ++ ++ __ st_d(FP, SP, gpr_offset(fp_off)); ++ map->set_callee_saved(VMRegImpl::stack2reg(gpr_offset(fp_off) / VMRegImpl::stack_slot_size + additional_frame_slots), FP->as_VMReg()); ++ __ st_d(RA, SP, gpr_offset(ra_off)); ++ map->set_callee_saved(VMRegImpl::stack2reg(gpr_offset(ra_off) / VMRegImpl::stack_slot_size + additional_frame_slots), RA->as_VMReg()); ++ ++ __ addi_d(FP, SP, gpr_offset(fp_off)); ++ ++ return map; ++} ++ ++ ++// Pop the current frame and restore all the registers that we ++// saved. ++void RegisterSaver::restore_live_registers(MacroAssembler* masm) { ++ for (int i = 0; i < fpr_size; i++) { ++ FloatRegister fpr = as_FloatRegister(i); ++ int off = fpr_offset(i); ++ ++ if (_save_vectors && UseLASX) ++ __ xvld(fpr, SP, off); ++ else if (_save_vectors && UseLSX) ++ __ vld(fpr, SP, off); ++ else ++ __ fld_d(fpr, SP, off); ++ } ++ ++ for (int i = a0_off; i <= a7_off; i++) { ++ Register gpr = as_Register(A0->encoding() + (i - a0_off)); ++ int off = gpr_offset(i); ++ ++ __ ld_d(gpr, SP, gpr_offset(i)); ++ } ++ ++ for (int i = t0_off; i <= t6_off; i++) { ++ Register gpr = as_Register(T0->encoding() + (i - t0_off)); ++ int off = gpr_offset(i); ++ ++ __ ld_d(gpr, SP, gpr_offset(i)); ++ } ++ __ ld_d(T8, SP, gpr_offset(t8_off)); ++ ++ for (int i = s0_off; i <= s8_off; i++) { ++ Register gpr = as_Register(S0->encoding() + (i - s0_off)); ++ int off = gpr_offset(i); ++ ++ __ ld_d(gpr, SP, gpr_offset(i)); ++ } ++ ++ __ ld_d(FP, SP, gpr_offset(fp_off)); ++ __ ld_d(RA, SP, gpr_offset(ra_off)); ++ ++ __ addi_d(SP, SP, slots_save() * VMRegImpl::stack_slot_size); ++} ++ ++// Pop the current frame and restore the registers that might be holding ++// a result. ++void RegisterSaver::restore_result_registers(MacroAssembler* masm) { ++ // Just restore result register. Only used by deoptimization. By ++ // now any callee save register that needs to be restore to a c2 ++ // caller of the deoptee has been extracted into the vframeArray ++ // and will be stuffed into the c2i adapter we create for later ++ // restoration so only result registers need to be restored here. ++ ++ __ ld_d(V0, SP, gpr_offset(a0_off)); ++ __ ld_d(V1, SP, gpr_offset(a1_off)); ++ ++ __ fld_d(F0, SP, fpr_offset(fpr0_off)); ++ __ fld_d(F1, SP, fpr_offset(fpr1_off)); ++ ++ __ addi_d(SP, SP, gpr_offset(ra_off)); ++} ++ ++// Is vector's size (in bytes) bigger than a size saved by default? ++// 16 bytes XMM registers are saved by default using fxsave/fxrstor instructions. ++bool SharedRuntime::is_wide_vector(int size) { ++ return size > 16; ++} ++ ++// The java_calling_convention describes stack locations as ideal slots on ++// a frame with no abi restrictions. Since we must observe abi restrictions ++// (like the placement of the register window) the slots must be biased by ++// the following value. ++ ++static int reg2offset_in(VMReg r) { ++ // Account for saved fp and return address ++ // This should really be in_preserve_stack_slots ++ return (r->reg2stack() + 2 * VMRegImpl::slots_per_word) * VMRegImpl::stack_slot_size; // + 2 * VMRegImpl::stack_slot_size); ++} ++ ++static int reg2offset_out(VMReg r) { ++ return (r->reg2stack() + SharedRuntime::out_preserve_stack_slots()) * VMRegImpl::stack_slot_size; ++} ++ ++// --------------------------------------------------------------------------- ++// Read the array of BasicTypes from a signature, and compute where the ++// arguments should go. Values in the VMRegPair regs array refer to 4-byte ++// quantities. Values less than SharedInfo::stack0 are registers, those above ++// refer to 4-byte stack slots. All stack slots are based off of the stack pointer ++// as framesizes are fixed. ++// VMRegImpl::stack0 refers to the first slot 0(sp). ++// and VMRegImpl::stack0+1 refers to the memory word 4-byes higher. Register ++// up to RegisterImpl::number_of_registers) are the 32-bit ++// integer registers. ++ ++// Pass first five oop/int args in registers T0, A0 - A3. ++// Pass float/double/long args in stack. ++// Doubles have precedence, so if you pass a mix of floats and doubles ++// the doubles will grab the registers before the floats will. ++ ++// Note: the INPUTS in sig_bt are in units of Java argument words, which are ++// either 32-bit or 64-bit depending on the build. The OUTPUTS are in 32-bit ++// units regardless of build. ++ ++ ++// --------------------------------------------------------------------------- ++// The compiled Java calling convention. ++// Pass first five oop/int args in registers T0, A0 - A3. ++// Pass float/double/long args in stack. ++// Doubles have precedence, so if you pass a mix of floats and doubles ++// the doubles will grab the registers before the floats will. ++ ++int SharedRuntime::java_calling_convention(const BasicType *sig_bt, ++ VMRegPair *regs, ++ int total_args_passed, ++ int is_outgoing) { ++ ++ // Create the mapping between argument positions and registers. ++ static const Register INT_ArgReg[Argument::n_register_parameters + 1] = { ++ T0, A0, A1, A2, A3, A4, A5, A6, A7 ++ }; ++ static const FloatRegister FP_ArgReg[Argument::n_float_register_parameters] = { ++ FA0, FA1, FA2, FA3, FA4, FA5, FA6, FA7 ++ }; ++ ++ uint int_args = 0; ++ uint fp_args = 0; ++ uint stk_args = 0; // inc by 2 each time ++ ++ for (int i = 0; i < total_args_passed; i++) { ++ switch (sig_bt[i]) { ++ case T_VOID: ++ // halves of T_LONG or T_DOUBLE ++ assert(i != 0 && (sig_bt[i - 1] == T_LONG || sig_bt[i - 1] == T_DOUBLE), "expecting half"); ++ regs[i].set_bad(); ++ break; ++ case T_BOOLEAN: ++ case T_CHAR: ++ case T_BYTE: ++ case T_SHORT: ++ case T_INT: ++ if (int_args < Argument::n_register_parameters + 1) { ++ regs[i].set1(INT_ArgReg[int_args++]->as_VMReg()); ++ } else { ++ regs[i].set1(VMRegImpl::stack2reg(stk_args)); ++ stk_args += 2; ++ } ++ break; ++ case T_LONG: ++ assert(sig_bt[i + 1] == T_VOID, "expecting half"); ++ // fall through ++ case T_OBJECT: ++ case T_ARRAY: ++ case T_ADDRESS: ++ if (int_args < Argument::n_register_parameters + 1) { ++ regs[i].set2(INT_ArgReg[int_args++]->as_VMReg()); ++ } else { ++ regs[i].set2(VMRegImpl::stack2reg(stk_args)); ++ stk_args += 2; ++ } ++ break; ++ case T_FLOAT: ++ if (fp_args < Argument::n_float_register_parameters) { ++ regs[i].set1(FP_ArgReg[fp_args++]->as_VMReg()); ++ } else { ++ regs[i].set1(VMRegImpl::stack2reg(stk_args)); ++ stk_args += 2; ++ } ++ break; ++ case T_DOUBLE: ++ assert(sig_bt[i + 1] == T_VOID, "expecting half"); ++ if (fp_args < Argument::n_float_register_parameters) { ++ regs[i].set2(FP_ArgReg[fp_args++]->as_VMReg()); ++ } else { ++ regs[i].set2(VMRegImpl::stack2reg(stk_args)); ++ stk_args += 2; ++ } ++ break; ++ default: ++ ShouldNotReachHere(); ++ break; ++ } ++ } ++ ++ return round_to(stk_args, 2); ++} ++ ++// Patch the callers callsite with entry to compiled code if it exists. ++static void patch_callers_callsite(MacroAssembler *masm) { ++ Label L; ++ __ verify_oop(Rmethod); ++ __ ld_ptr(AT, Rmethod, in_bytes(Method::code_offset())); ++ __ beq(AT, R0, L); ++ // Schedule the branch target address early. ++ // Call into the VM to patch the caller, then jump to compiled callee ++ // T5 isn't live so capture return address while we easily can ++ __ move(T5, RA); ++ ++ __ pushad(); ++#ifdef COMPILER2 ++ // C2 may leave the stack dirty if not in SSE2+ mode ++ __ empty_FPU_stack(); ++#endif ++ ++ // VM needs caller's callsite ++ // VM needs target method ++ ++ __ move(A0, Rmethod); ++ __ move(A1, T5); ++ // we should preserve the return address ++ __ verify_oop(Rmethod); ++ __ move(S0, SP); ++ __ li(AT, -(StackAlignmentInBytes)); // align the stack ++ __ andr(SP, SP, AT); ++ __ call(CAST_FROM_FN_PTR(address, SharedRuntime::fixup_callers_callsite), ++ relocInfo::runtime_call_type); ++ ++ __ move(SP, S0); ++ __ popad(); ++ __ bind(L); ++} ++ ++static void gen_c2i_adapter(MacroAssembler *masm, ++ int total_args_passed, ++ int comp_args_on_stack, ++ const BasicType *sig_bt, ++ const VMRegPair *regs, ++ Label& skip_fixup) { ++ ++ // Before we get into the guts of the C2I adapter, see if we should be here ++ // at all. We've come from compiled code and are attempting to jump to the ++ // interpreter, which means the caller made a static call to get here ++ // (vcalls always get a compiled target if there is one). Check for a ++ // compiled target. If there is one, we need to patch the caller's call. ++ // However we will run interpreted if we come thru here. The next pass ++ // thru the call site will run compiled. If we ran compiled here then ++ // we can (theorectically) do endless i2c->c2i->i2c transitions during ++ // deopt/uncommon trap cycles. If we always go interpreted here then ++ // we can have at most one and don't need to play any tricks to keep ++ // from endlessly growing the stack. ++ // ++ // Actually if we detected that we had an i2c->c2i transition here we ++ // ought to be able to reset the world back to the state of the interpreted ++ // call and not bother building another interpreter arg area. We don't ++ // do that at this point. ++ ++ patch_callers_callsite(masm); ++ __ bind(skip_fixup); ++ ++#ifdef COMPILER2 ++ __ empty_FPU_stack(); ++#endif ++ //this is for native ? ++ // Since all args are passed on the stack, total_args_passed * interpreter_ ++ // stack_element_size is the ++ // space we need. ++ int extraspace = total_args_passed * Interpreter::stackElementSize; ++ ++ // stack is aligned, keep it that way ++ extraspace = round_to(extraspace, 2*wordSize); ++ ++ // Get return address ++ __ move(T5, RA); ++ // set senderSP value ++ //refer to interpreter_loongarch.cpp:generate_asm_entry ++ __ move(Rsender, SP); ++ __ addi_d(SP, SP, -extraspace); ++ ++ // Now write the args into the outgoing interpreter space ++ for (int i = 0; i < total_args_passed; i++) { ++ if (sig_bt[i] == T_VOID) { ++ assert(i > 0 && (sig_bt[i-1] == T_LONG || sig_bt[i-1] == T_DOUBLE), "missing half"); ++ continue; ++ } ++ ++ // st_off points to lowest address on stack. ++ int st_off = ((total_args_passed - 1) - i) * Interpreter::stackElementSize; ++ // Say 4 args: ++ // i st_off ++ // 0 12 T_LONG ++ // 1 8 T_VOID ++ // 2 4 T_OBJECT ++ // 3 0 T_BOOL ++ VMReg r_1 = regs[i].first(); ++ VMReg r_2 = regs[i].second(); ++ if (!r_1->is_valid()) { ++ assert(!r_2->is_valid(), ""); ++ continue; ++ } ++ if (r_1->is_stack()) { ++ // memory to memory use fpu stack top ++ int ld_off = r_1->reg2stack() * VMRegImpl::stack_slot_size + extraspace; ++ if (!r_2->is_valid()) { ++ __ ld_ptr(AT, Address(SP, ld_off)); ++ __ st_ptr(AT, Address(SP, st_off)); ++ ++ } else { ++ ++ ++ int next_off = st_off - Interpreter::stackElementSize; ++ __ ld_ptr(AT, Address(SP, ld_off)); ++ __ st_ptr(AT, Address(SP, st_off)); ++ ++ // Ref to is_Register condition ++ if(sig_bt[i] == T_LONG || sig_bt[i] == T_DOUBLE) ++ __ st_ptr(AT, SP, st_off - 8); ++ } ++ } else if (r_1->is_Register()) { ++ Register r = r_1->as_Register(); ++ if (!r_2->is_valid()) { ++ __ st_d(r, SP, st_off); ++ } else { ++ //FIXME, LA will not enter here ++ // long/double in gpr ++ __ st_d(r, SP, st_off); ++ // In [java/util/zip/ZipFile.java] ++ // ++ // private static native long open(String name, int mode, long lastModified); ++ // private static native int getTotal(long jzfile); ++ // ++ // We need to transfer T_LONG paramenters from a compiled method to a native method. ++ // It's a complex process: ++ // ++ // Caller -> lir_static_call -> gen_resolve_stub ++ // -> -- resolve_static_call_C ++ // `- gen_c2i_adapter() [*] ++ // | ++ // `- AdapterHandlerLibrary::get_create_apapter_index ++ // -> generate_native_entry ++ // -> InterpreterRuntime::SignatureHandlerGenerator::pass_long [**] ++ // ++ // In [**], T_Long parameter is stored in stack as: ++ // ++ // (high) ++ // | | ++ // ----------- ++ // | 8 bytes | ++ // | (void) | ++ // ----------- ++ // | 8 bytes | ++ // | (long) | ++ // ----------- ++ // | | ++ // (low) ++ // ++ // However, the sequence is reversed here: ++ // ++ // (high) ++ // | | ++ // ----------- ++ // | 8 bytes | ++ // | (long) | ++ // ----------- ++ // | 8 bytes | ++ // | (void) | ++ // ----------- ++ // | | ++ // (low) ++ // ++ // So I stored another 8 bytes in the T_VOID slot. It then can be accessed from generate_native_entry(). ++ // ++ if (sig_bt[i] == T_LONG) ++ __ st_d(r, SP, st_off - 8); ++ } ++ } else if (r_1->is_FloatRegister()) { ++ assert(sig_bt[i] == T_FLOAT || sig_bt[i] == T_DOUBLE, "Must be a float register"); ++ ++ FloatRegister fr = r_1->as_FloatRegister(); ++ if (sig_bt[i] == T_FLOAT) ++ __ fst_s(fr, SP, st_off); ++ else { ++ __ fst_d(fr, SP, st_off); ++ __ fst_d(fr, SP, st_off - 8); // T_DOUBLE needs two slots ++ } ++ } ++ } ++ ++ // Schedule the branch target address early. ++ __ ld_ptr(AT, Rmethod, in_bytes(Method::interpreter_entry_offset()) ); ++ // And repush original return address ++ __ move(RA, T5); ++ __ jr (AT); ++} ++ ++static void gen_i2c_adapter(MacroAssembler *masm, ++ int total_args_passed, ++ int comp_args_on_stack, ++ const BasicType *sig_bt, ++ const VMRegPair *regs) { ++ ++ // Generate an I2C adapter: adjust the I-frame to make space for the C-frame ++ // layout. Lesp was saved by the calling I-frame and will be restored on ++ // return. Meanwhile, outgoing arg space is all owned by the callee ++ // C-frame, so we can mangle it at will. After adjusting the frame size, ++ // hoist register arguments and repack other args according to the compiled ++ // code convention. Finally, end in a jump to the compiled code. The entry ++ // point address is the start of the buffer. ++ ++ // We will only enter here from an interpreted frame and never from after ++ // passing thru a c2i. Azul allowed this but we do not. If we lose the ++ // race and use a c2i we will remain interpreted for the race loser(s). ++ // This removes all sorts of headaches on the LA side and also eliminates ++ // the possibility of having c2i -> i2c -> c2i -> ... endless transitions. ++ ++ __ move(T4, SP); ++ ++ // Cut-out for having no stack args. Since up to 2 int/oop args are passed ++ // in registers, we will occasionally have no stack args. ++ int comp_words_on_stack = 0; ++ if (comp_args_on_stack) { ++ // Sig words on the stack are greater-than VMRegImpl::stack0. Those in ++ // registers are below. By subtracting stack0, we either get a negative ++ // number (all values in registers) or the maximum stack slot accessed. ++ // int comp_args_on_stack = VMRegImpl::reg2stack(max_arg); ++ // Convert 4-byte stack slots to words. ++ // did LA need round? FIXME ++ comp_words_on_stack = round_to(comp_args_on_stack*4, wordSize)>>LogBytesPerWord; ++ // Round up to miminum stack alignment, in wordSize ++ comp_words_on_stack = round_to(comp_words_on_stack, 2); ++ __ addi_d(SP, SP, -comp_words_on_stack * wordSize); ++ } ++ ++ // Align the outgoing SP ++ __ li(AT, -(StackAlignmentInBytes)); ++ __ andr(SP, SP, AT); ++ // push the return address on the stack (note that pushing, rather ++ // than storing it, yields the correct frame alignment for the callee) ++ // Put saved SP in another register ++ const Register saved_sp = T5; ++ __ move(saved_sp, T4); ++ ++ ++ // Will jump to the compiled code just as if compiled code was doing it. ++ // Pre-load the register-jump target early, to schedule it better. ++ __ ld_d(T4, Rmethod, in_bytes(Method::from_compiled_offset())); ++ ++ // Now generate the shuffle code. Pick up all register args and move the ++ // rest through the floating point stack top. ++ for (int i = 0; i < total_args_passed; i++) { ++ if (sig_bt[i] == T_VOID) { ++ // Longs and doubles are passed in native word order, but misaligned ++ // in the 32-bit build. ++ assert(i > 0 && (sig_bt[i-1] == T_LONG || sig_bt[i-1] == T_DOUBLE), "missing half"); ++ continue; ++ } ++ ++ // Pick up 0, 1 or 2 words from SP+offset. ++ ++ assert(!regs[i].second()->is_valid() || regs[i].first()->next() == regs[i].second(), "scrambled load targets?"); ++ // Load in argument order going down. ++ int ld_off = (total_args_passed -1 - i)*Interpreter::stackElementSize; ++ // Point to interpreter value (vs. tag) ++ int next_off = ld_off - Interpreter::stackElementSize; ++ VMReg r_1 = regs[i].first(); ++ VMReg r_2 = regs[i].second(); ++ if (!r_1->is_valid()) { ++ assert(!r_2->is_valid(), ""); ++ continue; ++ } ++ if (r_1->is_stack()) { ++ // Convert stack slot to an SP offset (+ wordSize to ++ // account for return address ) ++ // NOTICE HERE!!!! I sub a wordSize here ++ int st_off = regs[i].first()->reg2stack()*VMRegImpl::stack_slot_size; ++ //+ wordSize; ++ ++ if (!r_2->is_valid()) { ++ __ ld_d(AT, saved_sp, ld_off); ++ __ st_d(AT, SP, st_off); ++ } else { ++ // Interpreter local[n] == MSW, local[n+1] == LSW however locals ++ // are accessed as negative so LSW is at LOW address ++ ++ // ld_off is MSW so get LSW ++ // st_off is LSW (i.e. reg.first()) ++ ++ // [./org/eclipse/swt/graphics/GC.java] ++ // void drawImageXRender(Image srcImage, int srcX, int srcY, int srcWidth, int srcHeight, ++ // int destX, int destY, int destWidth, int destHeight, ++ // boolean simple, ++ // int imgWidth, int imgHeight, ++ // long maskPixmap, <-- Pass T_LONG in stack ++ // int maskType); ++ // Before this modification, Eclipse displays icons with solid black background. ++ // ++ __ ld_d(AT, saved_sp, ld_off); ++ if (sig_bt[i] == T_LONG || sig_bt[i] == T_DOUBLE) ++ __ ld_d(AT, saved_sp, ld_off - 8); ++ __ st_d(AT, SP, st_off); ++ } ++ } else if (r_1->is_Register()) { // Register argument ++ Register r = r_1->as_Register(); ++ if (r_2->is_valid()) { ++ // Remember r_1 is low address (and LSB on LA) ++ // So r_2 gets loaded from high address regardless of the platform ++ assert(r_2->as_Register() == r_1->as_Register(), ""); ++ __ ld_d(r, saved_sp, ld_off); ++ ++ // ++ // For T_LONG type, the real layout is as below: ++ // ++ // (high) ++ // | | ++ // ----------- ++ // | 8 bytes | ++ // | (void) | ++ // ----------- ++ // | 8 bytes | ++ // | (long) | ++ // ----------- ++ // | | ++ // (low) ++ // ++ // We should load the low-8 bytes. ++ // ++ if (sig_bt[i] == T_LONG) ++ __ ld_d(r, saved_sp, ld_off - 8); ++ } else { ++ __ ld_w(r, saved_sp, ld_off); ++ } ++ } else if (r_1->is_FloatRegister()) { // Float Register ++ assert(sig_bt[i] == T_FLOAT || sig_bt[i] == T_DOUBLE, "Must be a float register"); ++ ++ FloatRegister fr = r_1->as_FloatRegister(); ++ if (sig_bt[i] == T_FLOAT) ++ __ fld_s(fr, saved_sp, ld_off); ++ else { ++ __ fld_d(fr, saved_sp, ld_off); ++ __ fld_d(fr, saved_sp, ld_off - 8); ++ } ++ } ++ } ++ ++ // 6243940 We might end up in handle_wrong_method if ++ // the callee is deoptimized as we race thru here. If that ++ // happens we don't want to take a safepoint because the ++ // caller frame will look interpreted and arguments are now ++ // "compiled" so it is much better to make this transition ++ // invisible to the stack walking code. Unfortunately if ++ // we try and find the callee by normal means a safepoint ++ // is possible. So we stash the desired callee in the thread ++ // and the vm will find there should this case occur. ++ __ get_thread(T8); ++ __ st_d(Rmethod, T8, in_bytes(JavaThread::callee_target_offset())); ++ ++ // move methodOop to T5 in case we end up in an c2i adapter. ++ // the c2i adapters expect methodOop in T5 (c2) because c2's ++ // resolve stubs return the result (the method) in T5. ++ // I'd love to fix this. ++ __ move(T5, Rmethod); ++ __ jr(T4); ++} ++ ++// --------------------------------------------------------------- ++AdapterHandlerEntry* SharedRuntime::generate_i2c2i_adapters(MacroAssembler *masm, ++ int total_args_passed, ++ int comp_args_on_stack, ++ const BasicType *sig_bt, ++ const VMRegPair *regs, ++ AdapterFingerPrint* fingerprint) { ++ address i2c_entry = __ pc(); ++ ++ gen_i2c_adapter(masm, total_args_passed, comp_args_on_stack, sig_bt, regs); ++ ++ // ------------------------------------------------------------------------- ++ // Generate a C2I adapter. On entry we know G5 holds the methodOop. The ++ // args start out packed in the compiled layout. They need to be unpacked ++ // into the interpreter layout. This will almost always require some stack ++ // space. We grow the current (compiled) stack, then repack the args. We ++ // finally end in a jump to the generic interpreter entry point. On exit ++ // from the interpreter, the interpreter will restore our SP (lest the ++ // compiled code, which relys solely on SP and not FP, get sick). ++ ++ address c2i_unverified_entry = __ pc(); ++ Label skip_fixup; ++ { ++ Register holder = T1; ++ Register receiver = T0; ++ Register temp = T8; ++ address ic_miss = SharedRuntime::get_ic_miss_stub(); ++ ++ Label missed; ++ ++ __ verify_oop(holder); ++ //add for compressedoops ++ __ load_klass(temp, receiver); ++ __ verify_oop(temp); ++ ++ __ ld_ptr(AT, holder, CompiledICHolder::holder_klass_offset()); ++ __ ld_ptr(Rmethod, holder, CompiledICHolder::holder_metadata_offset()); ++ __ bne(AT, temp, missed); ++ // Method might have been compiled since the call site was patched to ++ // interpreted if that is the case treat it as a miss so we can get ++ // the call site corrected. ++ __ ld_ptr(AT, Rmethod, in_bytes(Method::code_offset())); ++ __ beq(AT, R0, skip_fixup); ++ __ bind(missed); ++ ++ __ jmp(ic_miss, relocInfo::runtime_call_type); ++ } ++ address c2i_entry = __ pc(); ++ ++ gen_c2i_adapter(masm, total_args_passed, comp_args_on_stack, sig_bt, regs, skip_fixup); ++ ++ __ flush(); ++ return AdapterHandlerLibrary::new_entry(fingerprint, i2c_entry, c2i_entry, c2i_unverified_entry); ++} ++ ++int SharedRuntime::c_calling_convention(const BasicType *sig_bt, ++ VMRegPair *regs, ++ VMRegPair *regs2, ++ int total_args_passed) { ++ assert(regs2 == NULL, "not needed on LA"); ++ // Return the number of VMReg stack_slots needed for the args. ++ // This value does not include an abi space (like register window ++ // save area). ++ ++ // We return the amount of VMReg stack slots we need to reserve for all ++ // the arguments NOT counting out_preserve_stack_slots. Since we always ++ // have space for storing at least 6 registers to memory we start with that. ++ // See int_stk_helper for a further discussion. ++ // We return the amount of VMRegImpl stack slots we need to reserve for all ++ // the arguments NOT counting out_preserve_stack_slots. ++ static const Register INT_ArgReg[Argument::n_register_parameters] = { ++ A0, A1, A2, A3, A4, A5, A6, A7 ++ }; ++ static const FloatRegister FP_ArgReg[Argument::n_float_register_parameters] = { ++ FA0, FA1, FA2, FA3, FA4, FA5, FA6, FA7 ++ }; ++ uint int_args = 0; ++ uint fp_args = 0; ++ uint stk_args = 0; // inc by 2 each time ++ ++// Example: ++// n java.lang.UNIXProcess::forkAndExec ++// private native int forkAndExec(byte[] prog, ++// byte[] argBlock, int argc, ++// byte[] envBlock, int envc, ++// byte[] dir, ++// boolean redirectErrorStream, ++// FileDescriptor stdin_fd, ++// FileDescriptor stdout_fd, ++// FileDescriptor stderr_fd) ++// JNIEXPORT jint JNICALL ++// Java_java_lang_UNIXProcess_forkAndExec(JNIEnv *env, ++// jobject process, ++// jbyteArray prog, ++// jbyteArray argBlock, jint argc, ++// jbyteArray envBlock, jint envc, ++// jbyteArray dir, ++// jboolean redirectErrorStream, ++// jobject stdin_fd, ++// jobject stdout_fd, ++// jobject stderr_fd) ++// ++// ::c_calling_convention ++// 0: // env <-- a0 ++// 1: L // klass/obj <-- t0 => a1 ++// 2: [ // prog[] <-- a0 => a2 ++// 3: [ // argBlock[] <-- a1 => a3 ++// 4: I // argc <-- a2 => a4 ++// 5: [ // envBlock[] <-- a3 => a5 ++// 6: I // envc <-- a4 => a5 ++// 7: [ // dir[] <-- a5 => a7 ++// 8: Z // redirectErrorStream <-- a6 => sp[0] ++// 9: L // stdin <-- a7 => sp[8] ++// 10: L // stdout fp[16] => sp[16] ++// 11: L // stderr fp[24] => sp[24] ++// ++ for (int i = 0; i < total_args_passed; i++) { ++ switch (sig_bt[i]) { ++ case T_VOID: // Halves of longs and doubles ++ assert(i != 0 && (sig_bt[i - 1] == T_LONG || sig_bt[i - 1] == T_DOUBLE), "expecting half"); ++ regs[i].set_bad(); ++ break; ++ case T_BOOLEAN: ++ case T_CHAR: ++ case T_BYTE: ++ case T_SHORT: ++ case T_INT: ++ if (int_args < Argument::n_register_parameters) { ++ regs[i].set1(INT_ArgReg[int_args++]->as_VMReg()); ++ } else { ++ regs[i].set1(VMRegImpl::stack2reg(stk_args)); ++ stk_args += 2; ++ } ++ break; ++ case T_LONG: ++ assert(sig_bt[i + 1] == T_VOID, "expecting half"); ++ // fall through ++ case T_OBJECT: ++ case T_ARRAY: ++ case T_ADDRESS: ++ case T_METADATA: ++ if (int_args < Argument::n_register_parameters) { ++ regs[i].set2(INT_ArgReg[int_args++]->as_VMReg()); ++ } else { ++ regs[i].set2(VMRegImpl::stack2reg(stk_args)); ++ stk_args += 2; ++ } ++ break; ++ case T_FLOAT: ++ if (fp_args < Argument::n_float_register_parameters) { ++ regs[i].set1(FP_ArgReg[fp_args++]->as_VMReg()); ++ } else if (int_args < Argument::n_register_parameters) { ++ regs[i].set1(INT_ArgReg[int_args++]->as_VMReg()); ++ } else { ++ regs[i].set1(VMRegImpl::stack2reg(stk_args)); ++ stk_args += 2; ++ } ++ break; ++ case T_DOUBLE: ++ assert(sig_bt[i + 1] == T_VOID, "expecting half"); ++ if (fp_args < Argument::n_float_register_parameters) { ++ regs[i].set2(FP_ArgReg[fp_args++]->as_VMReg()); ++ } else if (int_args < Argument::n_register_parameters) { ++ regs[i].set2(INT_ArgReg[int_args++]->as_VMReg()); ++ } else { ++ regs[i].set2(VMRegImpl::stack2reg(stk_args)); ++ stk_args += 2; ++ } ++ break; ++ default: ++ ShouldNotReachHere(); ++ break; ++ } ++ } ++ ++ return round_to(stk_args, 2); ++} ++ ++// --------------------------------------------------------------------------- ++void SharedRuntime::save_native_result(MacroAssembler *masm, BasicType ret_type, int frame_slots) { ++ // We always ignore the frame_slots arg and just use the space just below frame pointer ++ // which by this time is free to use ++ switch (ret_type) { ++ case T_FLOAT: ++ __ fst_s(FSF, FP, -wordSize); ++ break; ++ case T_DOUBLE: ++ __ fst_d(FSF, FP, -wordSize ); ++ break; ++ case T_VOID: break; ++ case T_LONG: ++ __ st_d(V0, FP, -wordSize); ++ break; ++ case T_OBJECT: ++ case T_ARRAY: ++ __ st_d(V0, FP, -wordSize); ++ break; ++ default: { ++ __ st_w(V0, FP, -wordSize); ++ } ++ } ++} ++ ++void SharedRuntime::restore_native_result(MacroAssembler *masm, BasicType ret_type, int frame_slots) { ++ // We always ignore the frame_slots arg and just use the space just below frame pointer ++ // which by this time is free to use ++ switch (ret_type) { ++ case T_FLOAT: ++ __ fld_s(FSF, FP, -wordSize); ++ break; ++ case T_DOUBLE: ++ __ fld_d(FSF, FP, -wordSize ); ++ break; ++ case T_LONG: ++ __ ld_d(V0, FP, -wordSize); ++ break; ++ case T_VOID: break; ++ case T_OBJECT: ++ case T_ARRAY: ++ __ ld_d(V0, FP, -wordSize); ++ break; ++ default: { ++ __ ld_w(V0, FP, -wordSize); ++ } ++ } ++} ++ ++static void save_args(MacroAssembler *masm, int arg_count, int first_arg, VMRegPair *args) { ++ for ( int i = first_arg ; i < arg_count ; i++ ) { ++ if (args[i].first()->is_Register()) { ++ __ push(args[i].first()->as_Register()); ++ } else if (args[i].first()->is_FloatRegister()) { ++ __ push(args[i].first()->as_FloatRegister()); ++ } ++ } ++} ++ ++static void restore_args(MacroAssembler *masm, int arg_count, int first_arg, VMRegPair *args) { ++ for ( int i = arg_count - 1 ; i >= first_arg ; i-- ) { ++ if (args[i].first()->is_Register()) { ++ __ pop(args[i].first()->as_Register()); ++ } else if (args[i].first()->is_FloatRegister()) { ++ __ pop(args[i].first()->as_FloatRegister()); ++ } ++ } ++} ++ ++// A simple move of integer like type ++static void simple_move32(MacroAssembler* masm, VMRegPair src, VMRegPair dst) { ++ if (src.first()->is_stack()) { ++ if (dst.first()->is_stack()) { ++ // stack to stack ++ __ ld_w(AT, FP, reg2offset_in(src.first())); ++ __ st_d(AT, SP, reg2offset_out(dst.first())); ++ } else { ++ // stack to reg ++ __ ld_w(dst.first()->as_Register(), FP, reg2offset_in(src.first())); ++ } ++ } else if (dst.first()->is_stack()) { ++ // reg to stack ++ __ st_d(src.first()->as_Register(), SP, reg2offset_out(dst.first())); ++ } else { ++ if (dst.first() != src.first()){ ++ __ move(dst.first()->as_Register(), src.first()->as_Register()); ++ } ++ } ++} ++ ++// An oop arg. Must pass a handle not the oop itself ++static void object_move(MacroAssembler* masm, ++ OopMap* map, ++ int oop_handle_offset, ++ int framesize_in_slots, ++ VMRegPair src, ++ VMRegPair dst, ++ bool is_receiver, ++ int* receiver_offset) { ++ ++ // must pass a handle. First figure out the location we use as a handle ++ ++ if (src.first()->is_stack()) { ++ // Oop is already on the stack as an argument ++ Register rHandle = T5; ++ Label nil; ++ __ xorr(rHandle, rHandle, rHandle); ++ __ ld_d(AT, FP, reg2offset_in(src.first())); ++ __ beq(AT, R0, nil); ++ __ lea(rHandle, Address(FP, reg2offset_in(src.first()))); ++ __ bind(nil); ++ if(dst.first()->is_stack())__ st_d( rHandle, SP, reg2offset_out(dst.first())); ++ else __ move( (dst.first())->as_Register(), rHandle); ++ ++ int offset_in_older_frame = src.first()->reg2stack() + SharedRuntime::out_preserve_stack_slots(); ++ map->set_oop(VMRegImpl::stack2reg(offset_in_older_frame + framesize_in_slots)); ++ if (is_receiver) { ++ *receiver_offset = (offset_in_older_frame + framesize_in_slots) * VMRegImpl::stack_slot_size; ++ } ++ } else { ++ // Oop is in an a register we must store it to the space we reserve ++ // on the stack for oop_handles ++ const Register rOop = src.first()->as_Register(); ++ assert( (rOop->encoding() >= A0->encoding()) && (rOop->encoding() <= T0->encoding()),"wrong register"); ++ const Register rHandle = T5; ++ //Important: refer to java_calling_convertion ++ int oop_slot = (rOop->encoding() - A0->encoding()) * VMRegImpl::slots_per_word + oop_handle_offset; ++ int offset = oop_slot*VMRegImpl::stack_slot_size; ++ Label skip; ++ __ st_d( rOop , SP, offset ); ++ map->set_oop(VMRegImpl::stack2reg(oop_slot)); ++ __ xorr( rHandle, rHandle, rHandle); ++ __ beq(rOop, R0, skip); ++ __ lea(rHandle, Address(SP, offset)); ++ __ bind(skip); ++ // Store the handle parameter ++ if(dst.first()->is_stack())__ st_d( rHandle, SP, reg2offset_out(dst.first())); ++ else __ move((dst.first())->as_Register(), rHandle); ++ ++ if (is_receiver) { ++ *receiver_offset = offset; ++ } ++ } ++} ++ ++// A float arg may have to do float reg int reg conversion ++static void float_move(MacroAssembler* masm, VMRegPair src, VMRegPair dst) { ++ assert(!src.second()->is_valid() && !dst.second()->is_valid(), "bad float_move"); ++ if (src.first()->is_stack()) { ++ // stack to stack/reg ++ if (dst.first()->is_stack()) { ++ __ ld_w(AT, FP, reg2offset_in(src.first())); ++ __ st_w(AT, SP, reg2offset_out(dst.first())); ++ } else if (dst.first()->is_FloatRegister()) { ++ __ fld_s(dst.first()->as_FloatRegister(), FP, reg2offset_in(src.first())); ++ } else { ++ __ ld_w(dst.first()->as_Register(), FP, reg2offset_in(src.first())); ++ } ++ } else { ++ // reg to stack/reg ++ if(dst.first()->is_stack()) { ++ __ fst_s(src.first()->as_FloatRegister(), SP, reg2offset_out(dst.first())); ++ } else if (dst.first()->is_FloatRegister()) { ++ __ fmov_s(dst.first()->as_FloatRegister(), src.first()->as_FloatRegister()); ++ } else { ++ __ movfr2gr_s(dst.first()->as_Register(), src.first()->as_FloatRegister()); ++ } ++ } ++} ++ ++// A long move ++static void long_move(MacroAssembler* masm, VMRegPair src, VMRegPair dst) { ++ ++ // The only legal possibility for a long_move VMRegPair is: ++ // 1: two stack slots (possibly unaligned) ++ // as neither the java or C calling convention will use registers ++ // for longs. ++ if (src.first()->is_stack()) { ++ assert(src.second()->is_stack() && dst.second()->is_stack(), "must be all stack"); ++ if( dst.first()->is_stack()){ ++ __ ld_d(AT, FP, reg2offset_in(src.first())); ++ __ st_d(AT, SP, reg2offset_out(dst.first())); ++ } else { ++ __ ld_d(dst.first()->as_Register(), FP, reg2offset_in(src.first())); ++ } ++ } else { ++ if( dst.first()->is_stack()){ ++ __ st_d(src.first()->as_Register(), SP, reg2offset_out(dst.first())); ++ } else { ++ __ move(dst.first()->as_Register(), src.first()->as_Register()); ++ } ++ } ++} ++ ++// A double move ++static void double_move(MacroAssembler* masm, VMRegPair src, VMRegPair dst) { ++ ++ // The only legal possibilities for a double_move VMRegPair are: ++ // The painful thing here is that like long_move a VMRegPair might be ++ ++ // Because of the calling convention we know that src is either ++ // 1: a single physical register (xmm registers only) ++ // 2: two stack slots (possibly unaligned) ++ // dst can only be a pair of stack slots. ++ ++ if (src.first()->is_stack()) { ++ // source is all stack ++ if( dst.first()->is_stack()){ ++ __ ld_d(AT, FP, reg2offset_in(src.first())); ++ __ st_d(AT, SP, reg2offset_out(dst.first())); ++ } else if (dst.first()->is_FloatRegister()) { ++ __ fld_d(dst.first()->as_FloatRegister(), FP, reg2offset_in(src.first())); ++ } else { ++ __ ld_d(dst.first()->as_Register(), FP, reg2offset_in(src.first())); ++ } ++ } else { ++ // reg to stack/reg ++ // No worries about stack alignment ++ if( dst.first()->is_stack()){ ++ __ fst_d(src.first()->as_FloatRegister(), SP, reg2offset_out(dst.first())); ++ } else if (dst.first()->is_FloatRegister()) { ++ __ fmov_d(dst.first()->as_FloatRegister(), src.first()->as_FloatRegister()); ++ } else { ++ __ movfr2gr_d(dst.first()->as_Register(), src.first()->as_FloatRegister()); ++ } ++ } ++} ++ ++static void verify_oop_args(MacroAssembler* masm, ++ methodHandle method, ++ const BasicType* sig_bt, ++ const VMRegPair* regs) { ++ Register temp_reg = T4; // not part of any compiled calling seq ++ if (VerifyOops) { ++ for (int i = 0; i < method->size_of_parameters(); i++) { ++ if (sig_bt[i] == T_OBJECT || ++ sig_bt[i] == T_ARRAY) { ++ VMReg r = regs[i].first(); ++ assert(r->is_valid(), "bad oop arg"); ++ if (r->is_stack()) { ++ __ ld_d(temp_reg, Address(SP, r->reg2stack() * VMRegImpl::stack_slot_size + wordSize)); ++ __ verify_oop(temp_reg); ++ } else { ++ __ verify_oop(r->as_Register()); ++ } ++ } ++ } ++ } ++} ++ ++static void gen_special_dispatch(MacroAssembler* masm, ++ methodHandle method, ++ const BasicType* sig_bt, ++ const VMRegPair* regs) { ++ verify_oop_args(masm, method, sig_bt, regs); ++ vmIntrinsics::ID iid = method->intrinsic_id(); ++ ++ // Now write the args into the outgoing interpreter space ++ bool has_receiver = false; ++ Register receiver_reg = noreg; ++ int member_arg_pos = -1; ++ Register member_reg = noreg; ++ int ref_kind = MethodHandles::signature_polymorphic_intrinsic_ref_kind(iid); ++ if (ref_kind != 0) { ++ member_arg_pos = method->size_of_parameters() - 1; // trailing MemberName argument ++ member_reg = S3; // known to be free at this point ++ has_receiver = MethodHandles::ref_kind_has_receiver(ref_kind); ++ } else if (iid == vmIntrinsics::_invokeBasic) { ++ has_receiver = true; ++ } else { ++ fatal(err_msg_res("unexpected intrinsic id %d", iid)); ++ } ++ ++ if (member_reg != noreg) { ++ // Load the member_arg into register, if necessary. ++ SharedRuntime::check_member_name_argument_is_last_argument(method, sig_bt, regs); ++ VMReg r = regs[member_arg_pos].first(); ++ if (r->is_stack()) { ++ __ ld_d(member_reg, Address(SP, r->reg2stack() * VMRegImpl::stack_slot_size)); ++ } else { ++ // no data motion is needed ++ member_reg = r->as_Register(); ++ } ++ } ++ ++ if (has_receiver) { ++ // Make sure the receiver is loaded into a register. ++ assert(method->size_of_parameters() > 0, "oob"); ++ assert(sig_bt[0] == T_OBJECT, "receiver argument must be an object"); ++ VMReg r = regs[0].first(); ++ assert(r->is_valid(), "bad receiver arg"); ++ if (r->is_stack()) { ++ // Porting note: This assumes that compiled calling conventions always ++ // pass the receiver oop in a register. If this is not true on some ++ // platform, pick a temp and load the receiver from stack. ++ fatal("receiver always in a register"); ++ receiver_reg = SSR; // known to be free at this point ++ __ ld_d(receiver_reg, Address(SP, r->reg2stack() * VMRegImpl::stack_slot_size)); ++ } else { ++ // no data motion is needed ++ receiver_reg = r->as_Register(); ++ } ++ } ++ ++ // Figure out which address we are really jumping to: ++ MethodHandles::generate_method_handle_dispatch(masm, iid, ++ receiver_reg, member_reg, /*for_compiler_entry:*/ true); ++} ++ ++// --------------------------------------------------------------------------- ++// Generate a native wrapper for a given method. The method takes arguments ++// in the Java compiled code convention, marshals them to the native ++// convention (handlizes oops, etc), transitions to native, makes the call, ++// returns to java state (possibly blocking), unhandlizes any result and ++// returns. ++nmethod *SharedRuntime::generate_native_wrapper(MacroAssembler* masm, ++ methodHandle method, ++ int compile_id, ++ BasicType* in_sig_bt, ++ VMRegPair* in_regs, ++ BasicType ret_type) { ++ if (method->is_method_handle_intrinsic()) { ++ vmIntrinsics::ID iid = method->intrinsic_id(); ++ intptr_t start = (intptr_t)__ pc(); ++ int vep_offset = ((intptr_t)__ pc()) - start; ++ gen_special_dispatch(masm, ++ method, ++ in_sig_bt, ++ in_regs); ++ assert(((intptr_t)__ pc() - start - vep_offset) >= 1 * BytesPerInstWord, ++ "valid size for make_non_entrant"); ++ int frame_complete = ((intptr_t)__ pc()) - start; // not complete, period ++ __ flush(); ++ int stack_slots = SharedRuntime::out_preserve_stack_slots(); // no out slots at all, actually ++ return nmethod::new_native_nmethod(method, ++ compile_id, ++ masm->code(), ++ vep_offset, ++ frame_complete, ++ stack_slots / VMRegImpl::slots_per_word, ++ in_ByteSize(-1), ++ in_ByteSize(-1), ++ (OopMapSet*)NULL); ++ } ++ ++ bool is_critical_native = true; ++ address native_func = method->critical_native_function(); ++ if (native_func == NULL) { ++ native_func = method->native_function(); ++ is_critical_native = false; ++ } ++ assert(native_func != NULL, "must have function"); ++ ++ // Native nmethod wrappers never take possesion of the oop arguments. ++ // So the caller will gc the arguments. The only thing we need an ++ // oopMap for is if the call is static ++ // ++ // An OopMap for lock (and class if static), and one for the VM call itself ++ OopMapSet *oop_maps = new OopMapSet(); ++ ++ // We have received a description of where all the java arg are located ++ // on entry to the wrapper. We need to convert these args to where ++ // the jni function will expect them. To figure out where they go ++ // we convert the java signature to a C signature by inserting ++ // the hidden arguments as arg[0] and possibly arg[1] (static method) ++ ++ const int total_in_args = method->size_of_parameters(); ++ int total_c_args = total_in_args; ++ if (!is_critical_native) { ++ total_c_args += 1; ++ if (method->is_static()) { ++ total_c_args++; ++ } ++ } else { ++ for (int i = 0; i < total_in_args; i++) { ++ if (in_sig_bt[i] == T_ARRAY) { ++ total_c_args++; ++ } ++ } ++ } ++ ++ BasicType* out_sig_bt = NEW_RESOURCE_ARRAY(BasicType, total_c_args); ++ VMRegPair* out_regs = NEW_RESOURCE_ARRAY(VMRegPair, total_c_args); ++ BasicType* in_elem_bt = NULL; ++ ++ int argc = 0; ++ if (!is_critical_native) { ++ out_sig_bt[argc++] = T_ADDRESS; ++ if (method->is_static()) { ++ out_sig_bt[argc++] = T_OBJECT; ++ } ++ ++ for (int i = 0; i < total_in_args ; i++ ) { ++ out_sig_bt[argc++] = in_sig_bt[i]; ++ } ++ } else { ++ Thread* THREAD = Thread::current(); ++ in_elem_bt = NEW_RESOURCE_ARRAY(BasicType, total_in_args); ++ SignatureStream ss(method->signature()); ++ for (int i = 0; i < total_in_args ; i++ ) { ++ if (in_sig_bt[i] == T_ARRAY) { ++ // Arrays are passed as int, elem* pair ++ out_sig_bt[argc++] = T_INT; ++ out_sig_bt[argc++] = T_ADDRESS; ++ Symbol* atype = ss.as_symbol(CHECK_NULL); ++ const char* at = atype->as_C_string(); ++ if (strlen(at) == 2) { ++ assert(at[0] == '[', "must be"); ++ switch (at[1]) { ++ case 'B': in_elem_bt[i] = T_BYTE; break; ++ case 'C': in_elem_bt[i] = T_CHAR; break; ++ case 'D': in_elem_bt[i] = T_DOUBLE; break; ++ case 'F': in_elem_bt[i] = T_FLOAT; break; ++ case 'I': in_elem_bt[i] = T_INT; break; ++ case 'J': in_elem_bt[i] = T_LONG; break; ++ case 'S': in_elem_bt[i] = T_SHORT; break; ++ case 'Z': in_elem_bt[i] = T_BOOLEAN; break; ++ default: ShouldNotReachHere(); ++ } ++ } ++ } else { ++ out_sig_bt[argc++] = in_sig_bt[i]; ++ in_elem_bt[i] = T_VOID; ++ } ++ if (in_sig_bt[i] != T_VOID) { ++ assert(in_sig_bt[i] == ss.type(), "must match"); ++ ss.next(); ++ } ++ } ++ } ++ ++ // Now figure out where the args must be stored and how much stack space ++ // they require (neglecting out_preserve_stack_slots but space for storing ++ // the 1st six register arguments). It's weird see int_stk_helper. ++ // ++ int out_arg_slots; ++ out_arg_slots = c_calling_convention(out_sig_bt, out_regs, NULL, total_c_args); ++ ++ // Compute framesize for the wrapper. We need to handlize all oops in ++ // registers. We must create space for them here that is disjoint from ++ // the windowed save area because we have no control over when we might ++ // flush the window again and overwrite values that gc has since modified. ++ // (The live window race) ++ // ++ // We always just allocate 6 word for storing down these object. This allow ++ // us to simply record the base and use the Ireg number to decide which ++ // slot to use. (Note that the reg number is the inbound number not the ++ // outbound number). ++ // We must shuffle args to match the native convention, and include var-args space. ++ ++ // Calculate the total number of stack slots we will need. ++ ++ // First count the abi requirement plus all of the outgoing args ++ int stack_slots = SharedRuntime::out_preserve_stack_slots() + out_arg_slots; ++ ++ // Now the space for the inbound oop handle area ++ int total_save_slots = 9 * VMRegImpl::slots_per_word; // 9 arguments passed in registers ++ if (is_critical_native) { ++ // Critical natives may have to call out so they need a save area ++ // for register arguments. ++ int double_slots = 0; ++ int single_slots = 0; ++ for ( int i = 0; i < total_in_args; i++) { ++ if (in_regs[i].first()->is_Register()) { ++ const Register reg = in_regs[i].first()->as_Register(); ++ switch (in_sig_bt[i]) { ++ case T_BOOLEAN: ++ case T_BYTE: ++ case T_SHORT: ++ case T_CHAR: ++ case T_INT: single_slots++; break; ++ case T_ARRAY: ++ case T_LONG: double_slots++; break; ++ default: ShouldNotReachHere(); ++ } ++ } else if (in_regs[i].first()->is_FloatRegister()) { ++ switch (in_sig_bt[i]) { ++ case T_FLOAT: single_slots++; break; ++ case T_DOUBLE: double_slots++; break; ++ default: ShouldNotReachHere(); ++ } ++ } ++ } ++ total_save_slots = double_slots * 2 + single_slots; ++ // align the save area ++ if (double_slots != 0) { ++ stack_slots = round_to(stack_slots, 2); ++ } ++ } ++ ++ int oop_handle_offset = stack_slots; ++ stack_slots += total_save_slots; ++ ++ // Now any space we need for handlizing a klass if static method ++ ++ int klass_slot_offset = 0; ++ int klass_offset = -1; ++ int lock_slot_offset = 0; ++ bool is_static = false; ++ ++ if (method->is_static()) { ++ klass_slot_offset = stack_slots; ++ stack_slots += VMRegImpl::slots_per_word; ++ klass_offset = klass_slot_offset * VMRegImpl::stack_slot_size; ++ is_static = true; ++ } ++ ++ // Plus a lock if needed ++ ++ if (method->is_synchronized()) { ++ lock_slot_offset = stack_slots; ++ stack_slots += VMRegImpl::slots_per_word; ++ } ++ ++ // Now a place to save return value or as a temporary for any gpr -> fpr moves ++ // + 2 for return address (which we own) and saved fp ++ stack_slots += 2 + 9 * VMRegImpl::slots_per_word; // (T0, A0, A1, A2, A3, A4, A5, A6, A7) ++ ++ // Ok The space we have allocated will look like: ++ // ++ // ++ // FP-> | | ++ // |---------------------| ++ // | 2 slots for moves | ++ // |---------------------| ++ // | lock box (if sync) | ++ // |---------------------| <- lock_slot_offset ++ // | klass (if static) | ++ // |---------------------| <- klass_slot_offset ++ // | oopHandle area | ++ // |---------------------| <- oop_handle_offset ++ // | outbound memory | ++ // | based arguments | ++ // | | ++ // |---------------------| ++ // | vararg area | ++ // |---------------------| ++ // | | ++ // SP-> | out_preserved_slots | ++ // ++ // ++ ++ ++ // Now compute actual number of stack words we need rounding to make ++ // stack properly aligned. ++ stack_slots = round_to(stack_slots, StackAlignmentInSlots); ++ ++ int stack_size = stack_slots * VMRegImpl::stack_slot_size; ++ ++ intptr_t start = (intptr_t)__ pc(); ++ ++ ++ ++ // First thing make an ic check to see if we should even be here ++ address ic_miss = SharedRuntime::get_ic_miss_stub(); ++ ++ // We are free to use all registers as temps without saving them and ++ // restoring them except fp. fp is the only callee save register ++ // as far as the interpreter and the compiler(s) are concerned. ++ ++ //refer to register_loongarch.hpp:IC_Klass ++ const Register ic_reg = T1; ++ const Register receiver = T0; ++ ++ Label hit; ++ Label exception_pending; ++ ++ __ verify_oop(receiver); ++ //add for compressedoops ++ __ load_klass(T4, receiver); ++ __ beq(T4, ic_reg, hit); ++ __ jmp(ic_miss, relocInfo::runtime_call_type); ++ __ bind(hit); ++ ++ int vep_offset = ((intptr_t)__ pc()) - start; ++ ++ // Generate stack overflow check ++ if (UseStackBanging) { ++ __ bang_stack_with_offset(StackShadowPages*os::vm_page_size()); ++ } ++ ++ // The instruction at the verified entry point must be 4 bytes or longer ++ // because it can be patched on the fly by make_non_entrant. ++ if (((intptr_t)__ pc() - start - vep_offset) < 1 * BytesPerInstWord) { ++ __ nop(); ++ } ++ ++ // Generate a new frame for the wrapper. ++ // do LA need this ? ++#ifndef OPT_THREAD ++ __ get_thread(TREG); ++#endif ++ __ st_ptr(SP, TREG, in_bytes(JavaThread::last_Java_sp_offset())); ++ __ li(AT, -(StackAlignmentInBytes)); ++ __ andr(SP, SP, AT); ++ ++ __ enter(); ++ // -2 because return address is already present and so is saved fp ++ __ addi_d(SP, SP, -1 * (stack_size - 2*wordSize)); ++ ++ // Frame is now completed as far a size and linkage. ++ ++ int frame_complete = ((intptr_t)__ pc()) - start; ++ ++ // Calculate the difference between sp and fp. We need to know it ++ // after the native call because on windows Java Natives will pop ++ // the arguments and it is painful to do sp relative addressing ++ // in a platform independent way. So after the call we switch to ++ // fp relative addressing. ++ //FIXME actually , the fp_adjustment may not be the right, because andr(sp, sp, at) may change ++ //the SP ++ int fp_adjustment = stack_size - 2*wordSize; ++ ++#ifdef COMPILER2 ++ // C2 may leave the stack dirty if not in SSE2+ mode ++ __ empty_FPU_stack(); ++#endif ++ ++ // Compute the fp offset for any slots used after the jni call ++ ++ int lock_slot_fp_offset = (lock_slot_offset*VMRegImpl::stack_slot_size) - fp_adjustment; ++ // We use TREG as a thread pointer because it is callee save and ++ // if we load it once it is usable thru the entire wrapper ++ const Register thread = TREG; ++ ++ // We use S4 as the oop handle for the receiver/klass ++ // It is callee save so it survives the call to native ++ ++ const Register oop_handle_reg = S4; ++ if (is_critical_native) { ++ __ stop("generate_native_wrapper in sharedRuntime <2>"); ++ // check_needs_gc_for_critical_native(masm, stack_slots, total_c_args, total_in_args, ++ // oop_handle_offset, oop_maps, in_regs, in_sig_bt); ++ } ++ ++#ifndef OPT_THREAD ++ __ get_thread(thread); ++#endif ++ ++ // ++ // We immediately shuffle the arguments so that any vm call we have to ++ // make from here on out (sync slow path, jvmpi, etc.) we will have ++ // captured the oops from our caller and have a valid oopMap for ++ // them. ++ ++ // ----------------- ++ // The Grand Shuffle ++ // ++ // Natives require 1 or 2 extra arguments over the normal ones: the JNIEnv* ++ // and, if static, the class mirror instead of a receiver. This pretty much ++ // guarantees that register layout will not match (and LA doesn't use reg ++ // parms though amd does). Since the native abi doesn't use register args ++ // and the java conventions does we don't have to worry about collisions. ++ // All of our moved are reg->stack or stack->stack. ++ // We ignore the extra arguments during the shuffle and handle them at the ++ // last moment. The shuffle is described by the two calling convention ++ // vectors we have in our possession. We simply walk the java vector to ++ // get the source locations and the c vector to get the destinations. ++ ++ int c_arg = method->is_static() ? 2 : 1 ; ++ ++ // Record sp-based slot for receiver on stack for non-static methods ++ int receiver_offset = -1; ++ ++ // This is a trick. We double the stack slots so we can claim ++ // the oops in the caller's frame. Since we are sure to have ++ // more args than the caller doubling is enough to make ++ // sure we can capture all the incoming oop args from the ++ // caller. ++ // ++ OopMap* map = new OopMap(stack_slots * 2, 0 /* arg_slots*/); ++ ++ // Mark location of fp (someday) ++ // map->set_callee_saved(VMRegImpl::stack2reg( stack_slots - 2), stack_slots * 2, 0, vmreg(fp)); ++ ++#ifdef ASSERT ++ bool reg_destroyed[RegisterImpl::number_of_registers]; ++ bool freg_destroyed[FloatRegisterImpl::number_of_registers]; ++ for ( int r = 0 ; r < RegisterImpl::number_of_registers ; r++ ) { ++ reg_destroyed[r] = false; ++ } ++ for ( int f = 0 ; f < FloatRegisterImpl::number_of_registers ; f++ ) { ++ freg_destroyed[f] = false; ++ } ++ ++#endif /* ASSERT */ ++ ++ // This may iterate in two different directions depending on the ++ // kind of native it is. The reason is that for regular JNI natives ++ // the incoming and outgoing registers are offset upwards and for ++ // critical natives they are offset down. ++ GrowableArray arg_order(2 * total_in_args); ++ VMRegPair tmp_vmreg; ++ tmp_vmreg.set1(T8->as_VMReg()); ++ ++ if (!is_critical_native) { ++ for (int i = total_in_args - 1, c_arg = total_c_args - 1; i >= 0; i--, c_arg--) { ++ arg_order.push(i); ++ arg_order.push(c_arg); ++ } ++ } else { ++ // Compute a valid move order, using tmp_vmreg to break any cycles ++ __ stop("generate_native_wrapper in sharedRuntime <2>"); ++ // ComputeMoveOrder cmo(total_in_args, in_regs, total_c_args, out_regs, in_sig_bt, arg_order, tmp_vmreg); ++ } ++ ++ int temploc = -1; ++ for (int ai = 0; ai < arg_order.length(); ai += 2) { ++ int i = arg_order.at(ai); ++ int c_arg = arg_order.at(ai + 1); ++ __ block_comment(err_msg("move %d -> %d", i, c_arg)); ++ if (c_arg == -1) { ++ assert(is_critical_native, "should only be required for critical natives"); ++ // This arg needs to be moved to a temporary ++ __ move(tmp_vmreg.first()->as_Register(), in_regs[i].first()->as_Register()); ++ in_regs[i] = tmp_vmreg; ++ temploc = i; ++ continue; ++ } else if (i == -1) { ++ assert(is_critical_native, "should only be required for critical natives"); ++ // Read from the temporary location ++ assert(temploc != -1, "must be valid"); ++ i = temploc; ++ temploc = -1; ++ } ++#ifdef ASSERT ++ if (in_regs[i].first()->is_Register()) { ++ assert(!reg_destroyed[in_regs[i].first()->as_Register()->encoding()], "destroyed reg!"); ++ } else if (in_regs[i].first()->is_FloatRegister()) { ++ assert(!freg_destroyed[in_regs[i].first()->as_FloatRegister()->encoding()], "destroyed reg!"); ++ } ++ if (out_regs[c_arg].first()->is_Register()) { ++ reg_destroyed[out_regs[c_arg].first()->as_Register()->encoding()] = true; ++ } else if (out_regs[c_arg].first()->is_FloatRegister()) { ++ freg_destroyed[out_regs[c_arg].first()->as_FloatRegister()->encoding()] = true; ++ } ++#endif /* ASSERT */ ++ switch (in_sig_bt[i]) { ++ case T_ARRAY: ++ if (is_critical_native) { ++ __ stop("generate_native_wrapper in sharedRuntime <2>"); ++ // unpack_array_argument(masm, in_regs[i], in_elem_bt[i], out_regs[c_arg + 1], out_regs[c_arg]); ++ c_arg++; ++#ifdef ASSERT ++ if (out_regs[c_arg].first()->is_Register()) { ++ reg_destroyed[out_regs[c_arg].first()->as_Register()->encoding()] = true; ++ } else if (out_regs[c_arg].first()->is_FloatRegister()) { ++ freg_destroyed[out_regs[c_arg].first()->as_FloatRegister()->encoding()] = true; ++ } ++#endif ++ break; ++ } ++ case T_OBJECT: ++ assert(!is_critical_native, "no oop arguments"); ++ object_move(masm, map, oop_handle_offset, stack_slots, in_regs[i], out_regs[c_arg], ++ ((i == 0) && (!is_static)), ++ &receiver_offset); ++ break; ++ case T_VOID: ++ break; ++ ++ case T_FLOAT: ++ float_move(masm, in_regs[i], out_regs[c_arg]); ++ break; ++ ++ case T_DOUBLE: ++ assert( i + 1 < total_in_args && ++ in_sig_bt[i + 1] == T_VOID && ++ out_sig_bt[c_arg+1] == T_VOID, "bad arg list"); ++ double_move(masm, in_regs[i], out_regs[c_arg]); ++ break; ++ ++ case T_LONG : ++ long_move(masm, in_regs[i], out_regs[c_arg]); ++ break; ++ ++ case T_ADDRESS: assert(false, "found T_ADDRESS in java args"); ++ ++ default: ++ simple_move32(masm, in_regs[i], out_regs[c_arg]); ++ } ++ } ++ ++ // point c_arg at the first arg that is already loaded in case we ++ // need to spill before we call out ++ c_arg = total_c_args - total_in_args; ++ // Pre-load a static method's oop. Used both by locking code and ++ // the normal JNI call code. ++ ++ __ move(oop_handle_reg, A1); ++ ++ if (method->is_static() && !is_critical_native) { ++ ++ // load opp into a register ++ int oop_index = __ oop_recorder()->find_index(JNIHandles::make_local( ++ (method->method_holder())->java_mirror())); ++ ++ ++ RelocationHolder rspec = oop_Relocation::spec(oop_index); ++ __ relocate(rspec); ++ __ patchable_li52(oop_handle_reg, (long)JNIHandles::make_local((method->method_holder())->java_mirror())); ++ // Now handlize the static class mirror it's known not-null. ++ __ st_d( oop_handle_reg, SP, klass_offset); ++ map->set_oop(VMRegImpl::stack2reg(klass_slot_offset)); ++ ++ // Now get the handle ++ __ lea(oop_handle_reg, Address(SP, klass_offset)); ++ // store the klass handle as second argument ++ __ move(A1, oop_handle_reg); ++ // and protect the arg if we must spill ++ c_arg--; ++ } ++ ++ // Change state to native (we save the return address in the thread, since it might not ++ // be pushed on the stack when we do a a stack traversal). It is enough that the pc() ++ // points into the right code segment. It does not have to be the correct return pc. ++ // We use the same pc/oopMap repeatedly when we call out ++ ++ Label native_return; ++ __ set_last_Java_frame(SP, noreg, native_return); ++ ++ // We have all of the arguments setup at this point. We must not touch any register ++ // argument registers at this point (what if we save/restore them there are no oop? ++ { ++ SkipIfEqual skip_if(masm, &DTraceMethodProbes, 0); ++ save_args(masm, total_c_args, c_arg, out_regs); ++ int metadata_index = __ oop_recorder()->find_index(method()); ++ RelocationHolder rspec = metadata_Relocation::spec(metadata_index); ++ __ relocate(rspec); ++ __ patchable_li52(AT, (long)(method())); ++ ++ __ call_VM_leaf( ++ CAST_FROM_FN_PTR(address, SharedRuntime::dtrace_method_entry), ++ thread, AT); ++ ++ restore_args(masm, total_c_args, c_arg, out_regs); ++ } ++ ++ // These are register definitions we need for locking/unlocking ++ const Register swap_reg = T8; // Must use T8 for cmpxchg instruction ++ const Register obj_reg = T4; // Will contain the oop ++ //const Register lock_reg = T6; // Address of compiler lock object (BasicLock) ++ const Register lock_reg = c_rarg0; // Address of compiler lock object (BasicLock) ++ ++ ++ ++ Label slow_path_lock; ++ Label lock_done; ++ ++ // Lock a synchronized method ++ if (method->is_synchronized()) { ++ assert(!is_critical_native, "unhandled"); ++ ++ const int mark_word_offset = BasicLock::displaced_header_offset_in_bytes(); ++ ++ // Get the handle (the 2nd argument) ++ __ move(oop_handle_reg, A1); ++ ++ // Get address of the box ++ __ lea(lock_reg, Address(FP, lock_slot_fp_offset)); ++ ++ // Load the oop from the handle ++ __ ld_d(obj_reg, oop_handle_reg, 0); ++ ++ if (UseBiasedLocking) { ++ // Note that oop_handle_reg is trashed during this call ++ __ biased_locking_enter(lock_reg, obj_reg, swap_reg, A1, false, lock_done, &slow_path_lock); ++ } ++ ++ // Load immediate 1 into swap_reg %T8 ++ __ li(swap_reg, 1); ++ ++ __ ld_d(AT, obj_reg, 0); ++ __ orr(swap_reg, swap_reg, AT); ++ ++ __ st_d(swap_reg, lock_reg, mark_word_offset); ++ __ cmpxchg(Address(obj_reg, 0), swap_reg, lock_reg, AT, true, false, lock_done); ++ // Test if the oopMark is an obvious stack pointer, i.e., ++ // 1) (mark & 3) == 0, and ++ // 2) sp <= mark < mark + os::pagesize() ++ // These 3 tests can be done by evaluating the following ++ // expression: ((mark - sp) & (3 - os::vm_page_size())), ++ // assuming both stack pointer and pagesize have their ++ // least significant 2 bits clear. ++ // NOTE: the oopMark is in swap_reg %T8 as the result of cmpxchg ++ ++ __ sub_d(swap_reg, swap_reg, SP); ++ __ li(AT, 3 - os::vm_page_size()); ++ __ andr(swap_reg , swap_reg, AT); ++ // Save the test result, for recursive case, the result is zero ++ __ st_d(swap_reg, lock_reg, mark_word_offset); ++ __ bne(swap_reg, R0, slow_path_lock); ++ // Slow path will re-enter here ++ __ bind(lock_done); ++ ++ if (UseBiasedLocking) { ++ // Re-fetch oop_handle_reg as we trashed it above ++ __ move(A1, oop_handle_reg); ++ } ++ } ++ ++ ++ // Finally just about ready to make the JNI call ++ ++ ++ // get JNIEnv* which is first argument to native ++ if (!is_critical_native) { ++ __ addi_d(A0, thread, in_bytes(JavaThread::jni_environment_offset())); ++ } ++ ++ // Example: Java_java_lang_ref_Finalizer_invokeFinalizeMethod(JNIEnv *env, jclass clazz, jobject ob) ++ // Load the second arguments into A1 ++ //__ ld(A1, SP , wordSize ); // klass ++ ++ // Now set thread in native ++ __ addi_d(AT, R0, _thread_in_native); ++ if (os::is_MP()) { ++ __ membar(Assembler::Membar_mask_bits(__ LoadStore|__ StoreStore)); // store release ++ } ++ __ st_w(AT, thread, in_bytes(JavaThread::thread_state_offset())); ++ // do the call ++ __ call(method->native_function(), relocInfo::runtime_call_type); ++ __ bind(native_return); ++ ++ oop_maps->add_gc_map(((intptr_t)__ pc()) - start, map); ++ ++ // WARNING - on Windows Java Natives use pascal calling convention and pop the ++ // arguments off of the stack. We could just re-adjust the stack pointer here ++ // and continue to do SP relative addressing but we instead switch to FP ++ // relative addressing. ++ ++ // Unpack native results. ++ switch (ret_type) { ++ case T_BOOLEAN: __ c2bool(V0); break; ++ case T_CHAR : __ bstrpick_d(V0, V0, 15, 0); break; ++ case T_BYTE : __ sign_extend_byte (V0); break; ++ case T_SHORT : __ sign_extend_short(V0); break; ++ case T_INT : // nothing to do break; ++ case T_DOUBLE : ++ case T_FLOAT : ++ // Result is in st0 we'll save as needed ++ break; ++ case T_ARRAY: // Really a handle ++ case T_OBJECT: // Really a handle ++ break; // can't de-handlize until after safepoint check ++ case T_VOID: break; ++ case T_LONG: break; ++ default : ShouldNotReachHere(); ++ } ++ // Switch thread to "native transition" state before reading the synchronization state. ++ // This additional state is necessary because reading and testing the synchronization ++ // state is not atomic w.r.t. GC, as this scenario demonstrates: ++ // Java thread A, in _thread_in_native state, loads _not_synchronized and is preempted. ++ // VM thread changes sync state to synchronizing and suspends threads for GC. ++ // Thread A is resumed to finish this native method, but doesn't block here since it ++ // didn't see any synchronization is progress, and escapes. ++ __ addi_d(AT, R0, _thread_in_native_trans); ++ if (os::is_MP()) { ++ __ membar(Assembler::Membar_mask_bits(__ LoadStore|__ StoreStore)); // store release ++ } ++ __ st_w(AT, thread, in_bytes(JavaThread::thread_state_offset())); ++ ++ if(os::is_MP()) { ++ if (UseMembar) { ++ // Force this write out before the read below ++ __ membar(__ AnyAny); ++ } else { ++ // Write serialization page so VM thread can do a pseudo remote membar. ++ // We use the current thread pointer to calculate a thread specific ++ // offset to write to within the page. This minimizes bus traffic ++ // due to cache line collision. ++ __ serialize_memory(thread, T5); ++ } ++ } ++ ++ Label after_transition; ++ ++ // check for safepoint operation in progress and/or pending suspend requests ++ { ++ Label Continue; ++ __ li(AT, SafepointSynchronize::address_of_state()); ++ __ ld_w(T5, AT, 0); ++ __ addi_d(AT, T5, -SafepointSynchronize::_not_synchronized); ++ Label L; ++ __ bne(AT, R0, L); ++ __ ld_w(AT, thread, in_bytes(JavaThread::suspend_flags_offset())); ++ __ beq(AT, R0, Continue); ++ __ bind(L); ++ ++ // Don't use call_VM as it will see a possible pending exception and forward it ++ // and never return here preventing us from clearing _last_native_pc down below. ++ // ++ save_native_result(masm, ret_type, stack_slots); ++ __ move(A0, thread); ++ __ addi_d(SP, SP, -wordSize); ++ __ push(S2); ++ __ li(AT, -(StackAlignmentInBytes)); ++ __ move(S2, SP); // use S2 as a sender SP holder ++ __ andr(SP, SP, AT); // align stack as required by ABI ++ if (!is_critical_native) { ++ __ call(CAST_FROM_FN_PTR(address, JavaThread::check_special_condition_for_native_trans), relocInfo::runtime_call_type); ++ } else { ++ __ call(CAST_FROM_FN_PTR(address, JavaThread::check_special_condition_for_native_trans_and_transition), relocInfo::runtime_call_type); ++ } ++ __ move(SP, S2); // use S2 as a sender SP holder ++ __ pop(S2); ++ __ addi_d(SP, SP, wordSize); ++ //add for compressedoops ++ __ reinit_heapbase(); ++ // Restore any method result value ++ restore_native_result(masm, ret_type, stack_slots); ++ ++ if (is_critical_native) { ++ // The call above performed the transition to thread_in_Java so ++ // skip the transition logic below. ++ __ beq(R0, R0, after_transition); ++ } ++ ++ __ bind(Continue); ++ } ++ ++ // change thread state ++ __ addi_d(AT, R0, _thread_in_Java); ++ if (os::is_MP()) { ++ __ membar(Assembler::Membar_mask_bits(__ LoadStore|__ StoreStore)); // store release ++ } ++ __ st_w(AT, thread, in_bytes(JavaThread::thread_state_offset())); ++ __ bind(after_transition); ++ Label reguard; ++ Label reguard_done; ++ __ ld_w(AT, thread, in_bytes(JavaThread::stack_guard_state_offset())); ++ __ addi_d(AT, AT, -JavaThread::stack_guard_yellow_disabled); ++ __ beq(AT, R0, reguard); ++ // slow path reguard re-enters here ++ __ bind(reguard_done); ++ ++ // Handle possible exception (will unlock if necessary) ++ ++ // native result if any is live ++ ++ // Unlock ++ Label slow_path_unlock; ++ Label unlock_done; ++ if (method->is_synchronized()) { ++ ++ Label done; ++ ++ // Get locked oop from the handle we passed to jni ++ __ ld_d( obj_reg, oop_handle_reg, 0); ++ if (UseBiasedLocking) { ++ __ biased_locking_exit(obj_reg, T8, done); ++ ++ } ++ ++ // Simple recursive lock? ++ ++ __ ld_d(AT, FP, lock_slot_fp_offset); ++ __ beq(AT, R0, done); ++ // Must save FSF if if it is live now because cmpxchg must use it ++ if (ret_type != T_FLOAT && ret_type != T_DOUBLE && ret_type != T_VOID) { ++ save_native_result(masm, ret_type, stack_slots); ++ } ++ ++ // get old displaced header ++ __ ld_d (T8, FP, lock_slot_fp_offset); ++ // get address of the stack lock ++ __ addi_d (c_rarg0, FP, lock_slot_fp_offset); ++ // Atomic swap old header if oop still contains the stack lock ++ __ cmpxchg(Address(obj_reg, 0), c_rarg0, T8, AT, false, false, unlock_done, &slow_path_unlock); ++ ++ // slow path re-enters here ++ __ bind(unlock_done); ++ if (ret_type != T_FLOAT && ret_type != T_DOUBLE && ret_type != T_VOID) { ++ restore_native_result(masm, ret_type, stack_slots); ++ } ++ ++ __ bind(done); ++ ++ } ++ { ++ SkipIfEqual skip_if(masm, &DTraceMethodProbes, 0); ++ // Tell dtrace about this method exit ++ save_native_result(masm, ret_type, stack_slots); ++ int metadata_index = __ oop_recorder()->find_index( (method())); ++ RelocationHolder rspec = metadata_Relocation::spec(metadata_index); ++ __ relocate(rspec); ++ __ patchable_li52(AT, (long)(method())); ++ ++ __ call_VM_leaf( ++ CAST_FROM_FN_PTR(address, SharedRuntime::dtrace_method_exit), ++ thread, AT); ++ restore_native_result(masm, ret_type, stack_slots); ++ } ++ ++ // We can finally stop using that last_Java_frame we setup ages ago ++ ++ __ reset_last_Java_frame(false); ++ ++ // Unpack oop result, e.g. JNIHandles::resolve value. ++ if (ret_type == T_OBJECT || ret_type == T_ARRAY) { ++ __ resolve_jobject(V0, thread, T4); ++ } ++ ++ if (!is_critical_native) { ++ // reset handle block ++ __ ld_d(AT, thread, in_bytes(JavaThread::active_handles_offset())); ++ __ st_w(R0, AT, JNIHandleBlock::top_offset_in_bytes()); ++ } ++ ++ if (!is_critical_native) { ++ // Any exception pending? ++ __ ld_d(AT, thread, in_bytes(Thread::pending_exception_offset())); ++ __ bne(AT, R0, exception_pending); ++ } ++ // no exception, we're almost done ++ ++ // check that only result value is on FPU stack ++ __ verify_FPU(ret_type == T_FLOAT || ret_type == T_DOUBLE ? 1 : 0, "native_wrapper normal exit"); ++ ++ // Return ++#ifndef OPT_THREAD ++ __ get_thread(TREG); ++#endif ++ //__ ld_ptr(SP, TREG, in_bytes(JavaThread::last_Java_sp_offset())); ++ __ leave(); ++ ++ __ jr(RA); ++ // Unexpected paths are out of line and go here ++ // Slow path locking & unlocking ++ if (method->is_synchronized()) { ++ ++ // BEGIN Slow path lock ++ __ bind(slow_path_lock); ++ ++ // protect the args we've loaded ++ save_args(masm, total_c_args, c_arg, out_regs); ++ ++ // has last_Java_frame setup. No exceptions so do vanilla call not call_VM ++ // args are (oop obj, BasicLock* lock, JavaThread* thread) ++ ++ __ move(A0, obj_reg); ++ __ move(A1, lock_reg); ++ __ move(A2, thread); ++ __ addi_d(SP, SP, - 3*wordSize); ++ ++ __ li(AT, -(StackAlignmentInBytes)); ++ __ move(S2, SP); // use S2 as a sender SP holder ++ __ andr(SP, SP, AT); // align stack as required by ABI ++ ++ __ call(CAST_FROM_FN_PTR(address, SharedRuntime::complete_monitor_locking_C), relocInfo::runtime_call_type); ++ __ move(SP, S2); ++ __ addi_d(SP, SP, 3*wordSize); ++ ++ restore_args(masm, total_c_args, c_arg, out_regs); ++ ++#ifdef ASSERT ++ { Label L; ++ __ ld_d(AT, thread, in_bytes(Thread::pending_exception_offset())); ++ __ beq(AT, R0, L); ++ __ stop("no pending exception allowed on exit from monitorenter"); ++ __ bind(L); ++ } ++#endif ++ __ b(lock_done); ++ // END Slow path lock ++ ++ // BEGIN Slow path unlock ++ __ bind(slow_path_unlock); ++ ++ // Slow path unlock ++ ++ if (ret_type == T_FLOAT || ret_type == T_DOUBLE ) { ++ save_native_result(masm, ret_type, stack_slots); ++ } ++ // Save pending exception around call to VM (which contains an EXCEPTION_MARK) ++ ++ __ ld_d(AT, thread, in_bytes(Thread::pending_exception_offset())); ++ __ push(AT); ++ __ st_d(R0, thread, in_bytes(Thread::pending_exception_offset())); ++ ++ __ li(AT, -(StackAlignmentInBytes)); ++ __ move(S2, SP); // use S2 as a sender SP holder ++ __ andr(SP, SP, AT); // align stack as required by ABI ++ ++ // should be a peal ++ // +wordSize because of the push above ++ __ addi_d(A1, FP, lock_slot_fp_offset); ++ ++ __ move(A0, obj_reg); ++ __ addi_d(SP,SP, -2*wordSize); ++ __ call(CAST_FROM_FN_PTR(address, SharedRuntime::complete_monitor_unlocking_C), ++ relocInfo::runtime_call_type); ++ __ addi_d(SP, SP, 2*wordSize); ++ __ move(SP, S2); ++ //add for compressedoops ++ __ reinit_heapbase(); ++#ifdef ASSERT ++ { ++ Label L; ++ __ ld_d( AT, thread, in_bytes(Thread::pending_exception_offset())); ++ __ beq(AT, R0, L); ++ __ stop("no pending exception allowed on exit complete_monitor_unlocking_C"); ++ __ bind(L); ++ } ++#endif /* ASSERT */ ++ ++ __ pop(AT); ++ __ st_d(AT, thread, in_bytes(Thread::pending_exception_offset())); ++ if (ret_type == T_FLOAT || ret_type == T_DOUBLE ) { ++ restore_native_result(masm, ret_type, stack_slots); ++ } ++ __ b(unlock_done); ++ // END Slow path unlock ++ ++ } ++ ++ // SLOW PATH Reguard the stack if needed ++ ++ __ bind(reguard); ++ save_native_result(masm, ret_type, stack_slots); ++ __ call(CAST_FROM_FN_PTR(address, SharedRuntime::reguard_yellow_pages), ++ relocInfo::runtime_call_type); ++ //add for compressedoops ++ __ reinit_heapbase(); ++ restore_native_result(masm, ret_type, stack_slots); ++ __ b(reguard_done); ++ ++ // BEGIN EXCEPTION PROCESSING ++ if (!is_critical_native) { ++ // Forward the exception ++ __ bind(exception_pending); ++ ++ // remove possible return value from FPU register stack ++ __ empty_FPU_stack(); ++ ++ // pop our frame ++ //forward_exception_entry need return address on stack ++ __ move(SP, FP); ++ __ pop(FP); ++ ++ // and forward the exception ++ __ jmp(StubRoutines::forward_exception_entry(), relocInfo::runtime_call_type); ++ } ++ __ flush(); ++ ++ nmethod *nm = nmethod::new_native_nmethod(method, ++ compile_id, ++ masm->code(), ++ vep_offset, ++ frame_complete, ++ stack_slots / VMRegImpl::slots_per_word, ++ (is_static ? in_ByteSize(klass_offset) : in_ByteSize(receiver_offset)), ++ in_ByteSize(lock_slot_offset*VMRegImpl::stack_slot_size), ++ oop_maps); ++ ++ if (is_critical_native) { ++ nm->set_lazy_critical_native(true); ++ } ++ return nm; ++} ++ ++#ifdef HAVE_DTRACE_H ++// --------------------------------------------------------------------------- ++// Generate a dtrace nmethod for a given signature. The method takes arguments ++// in the Java compiled code convention, marshals them to the native ++// abi and then leaves nops at the position you would expect to call a native ++// function. When the probe is enabled the nops are replaced with a trap ++// instruction that dtrace inserts and the trace will cause a notification ++// to dtrace. ++// ++// The probes are only able to take primitive types and java/lang/String as ++// arguments. No other java types are allowed. Strings are converted to utf8 ++// strings so that from dtrace point of view java strings are converted to C ++// strings. There is an arbitrary fixed limit on the total space that a method ++// can use for converting the strings. (256 chars per string in the signature). ++// So any java string larger then this is truncated. ++ ++static int fp_offset[ConcreteRegisterImpl::number_of_registers] = { 0 }; ++static bool offsets_initialized = false; ++ ++static VMRegPair reg64_to_VMRegPair(Register r) { ++ VMRegPair ret; ++ if (wordSize == 8) { ++ ret.set2(r->as_VMReg()); ++ } else { ++ ret.set_pair(r->successor()->as_VMReg(), r->as_VMReg()); ++ } ++ return ret; ++} ++ ++ ++nmethod *SharedRuntime::generate_dtrace_nmethod(MacroAssembler *masm, ++ methodHandle method) { ++ ++ ++ // generate_dtrace_nmethod is guarded by a mutex so we are sure to ++ // be single threaded in this method. ++ assert(AdapterHandlerLibrary_lock->owned_by_self(), "must be"); ++ ++ // Fill in the signature array, for the calling-convention call. ++ int total_args_passed = method->size_of_parameters(); ++ ++ BasicType* in_sig_bt = NEW_RESOURCE_ARRAY(BasicType, total_args_passed); ++ VMRegPair *in_regs = NEW_RESOURCE_ARRAY(VMRegPair, total_args_passed); ++ ++ // The signature we are going to use for the trap that dtrace will see ++ // java/lang/String is converted. We drop "this" and any other object ++ // is converted to NULL. (A one-slot java/lang/Long object reference ++ // is converted to a two-slot long, which is why we double the allocation). ++ BasicType* out_sig_bt = NEW_RESOURCE_ARRAY(BasicType, total_args_passed * 2); ++ VMRegPair* out_regs = NEW_RESOURCE_ARRAY(VMRegPair, total_args_passed * 2); ++ ++ int i=0; ++ int total_strings = 0; ++ int first_arg_to_pass = 0; ++ int total_c_args = 0; ++ ++ // Skip the receiver as dtrace doesn't want to see it ++ if( !method->is_static() ) { ++ in_sig_bt[i++] = T_OBJECT; ++ first_arg_to_pass = 1; ++ } ++ ++ SignatureStream ss(method->signature()); ++ for ( ; !ss.at_return_type(); ss.next()) { ++ BasicType bt = ss.type(); ++ in_sig_bt[i++] = bt; // Collect remaining bits of signature ++ out_sig_bt[total_c_args++] = bt; ++ if( bt == T_OBJECT) { ++ symbolOop s = ss.as_symbol_or_null(); ++ if (s == vmSymbols::java_lang_String()) { ++ total_strings++; ++ out_sig_bt[total_c_args-1] = T_ADDRESS; ++ } else if (s == vmSymbols::java_lang_Boolean() || ++ s == vmSymbols::java_lang_Byte()) { ++ out_sig_bt[total_c_args-1] = T_BYTE; ++ } else if (s == vmSymbols::java_lang_Character() || ++ s == vmSymbols::java_lang_Short()) { ++ out_sig_bt[total_c_args-1] = T_SHORT; ++ } else if (s == vmSymbols::java_lang_Integer() || ++ s == vmSymbols::java_lang_Float()) { ++ out_sig_bt[total_c_args-1] = T_INT; ++ } else if (s == vmSymbols::java_lang_Long() || ++ s == vmSymbols::java_lang_Double()) { ++ out_sig_bt[total_c_args-1] = T_LONG; ++ out_sig_bt[total_c_args++] = T_VOID; ++ } ++ } else if ( bt == T_LONG || bt == T_DOUBLE ) { ++ in_sig_bt[i++] = T_VOID; // Longs & doubles take 2 Java slots ++ // We convert double to long ++ out_sig_bt[total_c_args-1] = T_LONG; ++ out_sig_bt[total_c_args++] = T_VOID; ++ } else if ( bt == T_FLOAT) { ++ // We convert float to int ++ out_sig_bt[total_c_args-1] = T_INT; ++ } ++ } ++ ++ assert(i==total_args_passed, "validly parsed signature"); ++ ++ // Now get the compiled-Java layout as input arguments ++ int comp_args_on_stack; ++ comp_args_on_stack = SharedRuntime::java_calling_convention( ++ in_sig_bt, in_regs, total_args_passed, false); ++ ++ // We have received a description of where all the java arg are located ++ // on entry to the wrapper. We need to convert these args to where ++ // the a native (non-jni) function would expect them. To figure out ++ // where they go we convert the java signature to a C signature and remove ++ // T_VOID for any long/double we might have received. ++ ++ ++ // Now figure out where the args must be stored and how much stack space ++ // they require (neglecting out_preserve_stack_slots but space for storing ++ // the 1st six register arguments). It's weird see int_stk_helper. ++ ++ int out_arg_slots; ++ out_arg_slots = c_calling_convention(out_sig_bt, out_regs, NULL, total_c_args); ++ ++ // Calculate the total number of stack slots we will need. ++ ++ // First count the abi requirement plus all of the outgoing args ++ int stack_slots = SharedRuntime::out_preserve_stack_slots() + out_arg_slots; ++ ++ // Plus a temp for possible converion of float/double/long register args ++ ++ int conversion_temp = stack_slots; ++ stack_slots += 2; ++ ++ ++ // Now space for the string(s) we must convert ++ ++ int string_locs = stack_slots; ++ stack_slots += total_strings * ++ (max_dtrace_string_size / VMRegImpl::stack_slot_size); ++ ++ // Ok The space we have allocated will look like: ++ // ++ // ++ // FP-> | | ++ // |---------------------| ++ // | string[n] | ++ // |---------------------| <- string_locs[n] ++ // | string[n-1] | ++ // |---------------------| <- string_locs[n-1] ++ // | ... | ++ // | ... | ++ // |---------------------| <- string_locs[1] ++ // | string[0] | ++ // |---------------------| <- string_locs[0] ++ // | temp | ++ // |---------------------| <- conversion_temp ++ // | outbound memory | ++ // | based arguments | ++ // | | ++ // |---------------------| ++ // | | ++ // SP-> | out_preserved_slots | ++ // ++ // ++ ++ // Now compute actual number of stack words we need rounding to make ++ // stack properly aligned. ++ stack_slots = round_to(stack_slots, 4 * VMRegImpl::slots_per_word); ++ ++ int stack_size = stack_slots * VMRegImpl::stack_slot_size; ++ intptr_t start = (intptr_t)__ pc(); ++ ++ // First thing make an ic check to see if we should even be here ++ ++ { ++ Label L; ++ const Register temp_reg = G3_scratch; ++ Address ic_miss(temp_reg, SharedRuntime::get_ic_miss_stub()); ++ __ verify_oop(O0); ++ __ ld_ptr(O0, oopDesc::klass_offset_in_bytes(), temp_reg); ++ __ cmp(temp_reg, G5_inline_cache_reg); ++ __ brx(Assembler::equal, true, Assembler::pt, L); ++ ++ __ jump_to(ic_miss, 0); ++ __ align(CodeEntryAlignment); ++ __ bind(L); ++ } ++ ++ int vep_offset = ((intptr_t)__ pc()) - start; ++ ++ // The instruction at the verified entry point must be 4 bytes or longer ++ // because it can be patched on the fly by make_non_entrant. The stack bang ++ // instruction fits that requirement. ++ ++ // Generate stack overflow check before creating frame ++ __ generate_stack_overflow_check(stack_size); ++ ++ assert(((intptr_t)__ pc() - start - vep_offset) >= 1 * BytesPerInstWord, ++ "valid size for make_non_entrant"); ++ ++ // Generate a new frame for the wrapper. ++ __ save(SP, -stack_size, SP); ++ ++ // Frame is now completed as far a size and linkage. ++ ++ int frame_complete = ((intptr_t)__ pc()) - start; ++ ++#ifdef ASSERT ++ bool reg_destroyed[RegisterImpl::number_of_registers]; ++ bool freg_destroyed[FloatRegisterImpl::number_of_registers]; ++ for ( int r = 0 ; r < RegisterImpl::number_of_registers ; r++ ) { ++ reg_destroyed[r] = false; ++ } ++ for ( int f = 0 ; f < FloatRegisterImpl::number_of_registers ; f++ ) { ++ freg_destroyed[f] = false; ++ } ++ ++#endif /* ASSERT */ ++ ++ VMRegPair zero; ++ const Register g0 = G0; // without this we get a compiler warning (why??) ++ zero.set2(g0->as_VMReg()); ++ ++ int c_arg, j_arg; ++ ++ Register conversion_off = noreg; ++ ++ for (j_arg = first_arg_to_pass, c_arg = 0 ; ++ j_arg < total_args_passed ; j_arg++, c_arg++ ) { ++ ++ VMRegPair src = in_regs[j_arg]; ++ VMRegPair dst = out_regs[c_arg]; ++ ++#ifdef ASSERT ++ if (src.first()->is_Register()) { ++ assert(!reg_destroyed[src.first()->as_Register()->encoding()], "ack!"); ++ } else if (src.first()->is_FloatRegister()) { ++ assert(!freg_destroyed[src.first()->as_FloatRegister()->encoding( ++ FloatRegisterImpl::S)], "ack!"); ++ } ++ if (dst.first()->is_Register()) { ++ reg_destroyed[dst.first()->as_Register()->encoding()] = true; ++ } else if (dst.first()->is_FloatRegister()) { ++ freg_destroyed[dst.first()->as_FloatRegister()->encoding( ++ FloatRegisterImpl::S)] = true; ++ } ++#endif /* ASSERT */ ++ ++ switch (in_sig_bt[j_arg]) { ++ case T_ARRAY: ++ case T_OBJECT: ++ { ++ if (out_sig_bt[c_arg] == T_BYTE || out_sig_bt[c_arg] == T_SHORT || ++ out_sig_bt[c_arg] == T_INT || out_sig_bt[c_arg] == T_LONG) { ++ // need to unbox a one-slot value ++ Register in_reg = L0; ++ Register tmp = L2; ++ if ( src.first()->is_reg() ) { ++ in_reg = src.first()->as_Register(); ++ } else { ++ assert(Assembler::is_simm13(reg2offset(src.first()) + STACK_BIAS), ++ "must be"); ++ __ ld_ptr(FP, reg2offset(src.first()) + STACK_BIAS, in_reg); ++ } ++ // If the final destination is an acceptable register ++ if ( dst.first()->is_reg() ) { ++ if ( dst.is_single_phys_reg() || out_sig_bt[c_arg] != T_LONG ) { ++ tmp = dst.first()->as_Register(); ++ } ++ } ++ ++ Label skipUnbox; ++ if ( wordSize == 4 && out_sig_bt[c_arg] == T_LONG ) { ++ __ mov(G0, tmp->successor()); ++ } ++ __ mov(G0, tmp); ++ __ br_null(in_reg, true, Assembler::pn, skipUnbox); ++ ++ BasicType bt = out_sig_bt[c_arg]; ++ int box_offset = java_lang_boxing_object::value_offset_in_bytes(bt); ++ switch (bt) { ++ case T_BYTE: ++ __ ldub(in_reg, box_offset, tmp); break; ++ case T_SHORT: ++ __ lduh(in_reg, box_offset, tmp); break; ++ case T_INT: ++ __ ld(in_reg, box_offset, tmp); break; ++ case T_LONG: ++ __ ld_long(in_reg, box_offset, tmp); break; ++ default: ShouldNotReachHere(); ++ } ++ ++ __ bind(skipUnbox); ++ // If tmp wasn't final destination copy to final destination ++ if (tmp == L2) { ++ VMRegPair tmp_as_VM = reg64_to_VMRegPair(L2); ++ if (out_sig_bt[c_arg] == T_LONG) { ++ long_move(masm, tmp_as_VM, dst); ++ } else { ++ move32_64(masm, tmp_as_VM, out_regs[c_arg]); ++ } ++ } ++ if (out_sig_bt[c_arg] == T_LONG) { ++ assert(out_sig_bt[c_arg+1] == T_VOID, "must be"); ++ ++c_arg; // move over the T_VOID to keep the loop indices in sync ++ } ++ } else if (out_sig_bt[c_arg] == T_ADDRESS) { ++ Register s = ++ src.first()->is_reg() ? src.first()->as_Register() : L2; ++ Register d = ++ dst.first()->is_reg() ? dst.first()->as_Register() : L2; ++ ++ // We store the oop now so that the conversion pass can reach ++ // while in the inner frame. This will be the only store if ++ // the oop is NULL. ++ if (s != L2) { ++ // src is register ++ if (d != L2) { ++ // dst is register ++ __ mov(s, d); ++ } else { ++ assert(Assembler::is_simm13(reg2offset(dst.first()) + ++ STACK_BIAS), "must be"); ++ __ st_ptr(s, SP, reg2offset(dst.first()) + STACK_BIAS); ++ } ++ } else { ++ // src not a register ++ assert(Assembler::is_simm13(reg2offset(src.first()) + ++ STACK_BIAS), "must be"); ++ __ ld_ptr(FP, reg2offset(src.first()) + STACK_BIAS, d); ++ if (d == L2) { ++ assert(Assembler::is_simm13(reg2offset(dst.first()) + ++ STACK_BIAS), "must be"); ++ __ st_ptr(d, SP, reg2offset(dst.first()) + STACK_BIAS); ++ } ++ } ++ } else if (out_sig_bt[c_arg] != T_VOID) { ++ // Convert the arg to NULL ++ if (dst.first()->is_reg()) { ++ __ mov(G0, dst.first()->as_Register()); ++ } else { ++ assert(Assembler::is_simm13(reg2offset(dst.first()) + ++ STACK_BIAS), "must be"); ++ __ st_ptr(G0, SP, reg2offset(dst.first()) + STACK_BIAS); ++ } ++ } ++ } ++ break; ++ case T_VOID: ++ break; ++ ++ case T_FLOAT: ++ if (src.first()->is_stack()) { ++ // Stack to stack/reg is simple ++ move32_64(masm, src, dst); ++ } else { ++ if (dst.first()->is_reg()) { ++ // freg -> reg ++ int off = ++ STACK_BIAS + conversion_temp * VMRegImpl::stack_slot_size; ++ Register d = dst.first()->as_Register(); ++ if (Assembler::is_simm13(off)) { ++ __ stf(FloatRegisterImpl::S, src.first()->as_FloatRegister(), ++ SP, off); ++ __ ld(SP, off, d); ++ } else { ++ if (conversion_off == noreg) { ++ __ set(off, L6); ++ conversion_off = L6; ++ } ++ __ stf(FloatRegisterImpl::S, src.first()->as_FloatRegister(), ++ SP, conversion_off); ++ __ ld(SP, conversion_off , d); ++ } ++ } else { ++ // freg -> mem ++ int off = STACK_BIAS + reg2offset(dst.first()); ++ if (Assembler::is_simm13(off)) { ++ __ stf(FloatRegisterImpl::S, src.first()->as_FloatRegister(), ++ SP, off); ++ } else { ++ if (conversion_off == noreg) { ++ __ set(off, L6); ++ conversion_off = L6; ++ } ++ __ stf(FloatRegisterImpl::S, src.first()->as_FloatRegister(), ++ SP, conversion_off); ++ } ++ } ++ } ++ break; ++ ++ case T_DOUBLE: ++ assert( j_arg + 1 < total_args_passed && ++ in_sig_bt[j_arg + 1] == T_VOID && ++ out_sig_bt[c_arg+1] == T_VOID, "bad arg list"); ++ if (src.first()->is_stack()) { ++ // Stack to stack/reg is simple ++ long_move(masm, src, dst); ++ } else { ++ Register d = dst.first()->is_reg() ? dst.first()->as_Register() : L2; ++ ++ // Destination could be an odd reg on 32bit in which case ++ // we can't load direct to the destination. ++ ++ if (!d->is_even() && wordSize == 4) { ++ d = L2; ++ } ++ int off = STACK_BIAS + conversion_temp * VMRegImpl::stack_slot_size; ++ if (Assembler::is_simm13(off)) { ++ __ stf(FloatRegisterImpl::D, src.first()->as_FloatRegister(), ++ SP, off); ++ __ ld_long(SP, off, d); ++ } else { ++ if (conversion_off == noreg) { ++ __ set(off, L6); ++ conversion_off = L6; ++ } ++ __ stf(FloatRegisterImpl::D, src.first()->as_FloatRegister(), ++ SP, conversion_off); ++ __ ld_long(SP, conversion_off, d); ++ } ++ if (d == L2) { ++ long_move(masm, reg64_to_VMRegPair(L2), dst); ++ } ++ } ++ break; ++ ++ case T_LONG : ++ // 32bit can't do a split move of something like g1 -> O0, O1 ++ // so use a memory temp ++ if (src.is_single_phys_reg() && wordSize == 4) { ++ Register tmp = L2; ++ if (dst.first()->is_reg() && ++ (wordSize == 8 || dst.first()->as_Register()->is_even())) { ++ tmp = dst.first()->as_Register(); ++ } ++ ++ int off = STACK_BIAS + conversion_temp * VMRegImpl::stack_slot_size; ++ if (Assembler::is_simm13(off)) { ++ __ stx(src.first()->as_Register(), SP, off); ++ __ ld_long(SP, off, tmp); ++ } else { ++ if (conversion_off == noreg) { ++ __ set(off, L6); ++ conversion_off = L6; ++ } ++ __ stx(src.first()->as_Register(), SP, conversion_off); ++ __ ld_long(SP, conversion_off, tmp); ++ } ++ ++ if (tmp == L2) { ++ long_move(masm, reg64_to_VMRegPair(L2), dst); ++ } ++ } else { ++ long_move(masm, src, dst); ++ } ++ break; ++ ++ case T_ADDRESS: assert(false, "found T_ADDRESS in java args"); ++ ++ default: ++ move32_64(masm, src, dst); ++ } ++ } ++ ++ ++ // If we have any strings we must store any register based arg to the stack ++ // This includes any still live xmm registers too. ++ ++ if (total_strings > 0 ) { ++ ++ // protect all the arg registers ++ __ save_frame(0); ++ __ mov(G2_thread, L7_thread_cache); ++ const Register L2_string_off = L2; ++ ++ // Get first string offset ++ __ set(string_locs * VMRegImpl::stack_slot_size, L2_string_off); ++ ++ for (c_arg = 0 ; c_arg < total_c_args ; c_arg++ ) { ++ if (out_sig_bt[c_arg] == T_ADDRESS) { ++ ++ VMRegPair dst = out_regs[c_arg]; ++ const Register d = dst.first()->is_reg() ? ++ dst.first()->as_Register()->after_save() : noreg; ++ ++ // It's a string the oop and it was already copied to the out arg ++ // position ++ if (d != noreg) { ++ __ mov(d, O0); ++ } else { ++ assert(Assembler::is_simm13(reg2offset(dst.first()) + STACK_BIAS), ++ "must be"); ++ __ ld_ptr(FP, reg2offset(dst.first()) + STACK_BIAS, O0); ++ } ++ Label skip; ++ ++ __ add_d(FP, L2_string_off, O1); ++ __ br_null(O0, false, Assembler::pn, skip); ++ ++ if (d != noreg) { ++ __ mov(O1, d); ++ } else { ++ assert(Assembler::is_simm13(reg2offset(dst.first()) + STACK_BIAS), ++ "must be"); ++ __ st_ptr(O1, FP, reg2offset(dst.first()) + STACK_BIAS); ++ } ++ ++ __ addi_d(L2_string_off, max_dtrace_string_size, L2_string_off); ++ __ call(CAST_FROM_FN_PTR(address, SharedRuntime::get_utf), ++ relocInfo::runtime_call_type); ++ ++ __ bind(skip); ++ ++ } ++ ++ } ++ __ mov(L7_thread_cache, G2_thread); ++ __ restore(); ++ ++ } ++ ++ ++ // Ok now we are done. Need to place the nop that dtrace wants in order to ++ // patch in the trap ++ ++ int patch_offset = ((intptr_t)__ pc()) - start; ++ ++ __ nop(); ++ ++ ++ // Return ++ ++ __ restore(); ++ __ ret(); ++ ++ __ flush(); ++ nmethod *nm = nmethod::new_dtrace_nmethod( ++ method, masm->code(), vep_offset, patch_offset, frame_complete, ++ stack_slots / VMRegImpl::slots_per_word); ++ return nm; ++} ++ ++#endif // HAVE_DTRACE_H ++ ++// this function returns the adjust size (in number of words) to a c2i adapter ++// activation for use during deoptimization ++int Deoptimization::last_frame_adjust(int callee_parameters, int callee_locals) { ++ return (callee_locals - callee_parameters) * Interpreter::stackElementWords; ++} ++ ++// "Top of Stack" slots that may be unused by the calling convention but must ++// otherwise be preserved. ++// On Intel these are not necessary and the value can be zero. ++// On Sparc this describes the words reserved for storing a register window ++// when an interrupt occurs. ++uint SharedRuntime::out_preserve_stack_slots() { ++ return 0; ++} ++ ++//------------------------------generate_deopt_blob---------------------------- ++// Ought to generate an ideal graph & compile, but here's some SPARC ASM ++// instead. ++void SharedRuntime::generate_deopt_blob() { ++ // allocate space for the code ++ ResourceMark rm; ++ // setup code generation tools ++ //CodeBuffer buffer ("deopt_blob", 4000, 2048); ++ CodeBuffer buffer ("deopt_blob", 8000, 2048); // FIXME for debug ++ MacroAssembler* masm = new MacroAssembler( & buffer); ++ int frame_size_in_words; ++ OopMap* map = NULL; ++ // Account for the extra args we place on the stack ++ // by the time we call fetch_unroll_info ++ const int additional_words = 2; // deopt kind, thread ++ ++ OopMapSet *oop_maps = new OopMapSet(); ++ RegisterSaver reg_save(false); ++ ++ address start = __ pc(); ++ Label cont; ++ // we use S3 for DeOpt reason register ++ Register reason = S3; ++ // use S6 for thread register ++ Register thread = TREG; ++ // use S7 for fetch_unroll_info returned UnrollBlock ++ Register unroll = S7; ++ // Prolog for non exception case! ++ ++ // We have been called from the deopt handler of the deoptee. ++ // ++ // deoptee: ++ // ... ++ // call X ++ // ... ++ // deopt_handler: call_deopt_stub ++ // cur. return pc --> ... ++ // ++ // So currently RA points behind the call in the deopt handler. ++ // We adjust it such that it points to the start of the deopt handler. ++ // The return_pc has been stored in the frame of the deoptee and ++ // will replace the address of the deopt_handler in the call ++ // to Deoptimization::fetch_unroll_info below. ++ ++ // HandlerImpl::size_deopt_handler() ++ __ addi_d(RA, RA, - NativeFarCall::instruction_size); ++ // Save everything in sight. ++ map = reg_save.save_live_registers(masm, additional_words, &frame_size_in_words); ++ // Normal deoptimization ++ __ li(reason, Deoptimization::Unpack_deopt); ++ __ b(cont); ++ ++ int reexecute_offset = __ pc() - start; ++ ++ // Reexecute case ++ // return address is the pc describes what bci to do re-execute at ++ ++ // No need to update map as each call to save_live_registers will produce identical oopmap ++ (void) reg_save.save_live_registers(masm, additional_words, &frame_size_in_words); ++ __ li(reason, Deoptimization::Unpack_reexecute); ++ __ b(cont); ++ ++ int exception_offset = __ pc() - start; ++ // Prolog for exception case ++ ++ // all registers are dead at this entry point, except for V0 and ++ // V1 which contain the exception oop and exception pc ++ // respectively. Set them in TLS and fall thru to the ++ // unpack_with_exception_in_tls entry point. ++ ++ __ get_thread(thread); ++ __ st_ptr(V1, thread, in_bytes(JavaThread::exception_pc_offset())); ++ __ st_ptr(V0, thread, in_bytes(JavaThread::exception_oop_offset())); ++ int exception_in_tls_offset = __ pc() - start; ++ // new implementation because exception oop is now passed in JavaThread ++ ++ // Prolog for exception case ++ // All registers must be preserved because they might be used by LinearScan ++ // Exceptiop oop and throwing PC are passed in JavaThread ++ // tos: stack at point of call to method that threw the exception (i.e. only ++ // args are on the stack, no return address) ++ ++ // Return address will be patched later with the throwing pc. The correct value is not ++ // available now because loading it from memory would destroy registers. ++ // Save everything in sight. ++ // No need to update map as each call to save_live_registers will produce identical oopmap ++ (void) reg_save.save_live_registers(masm, additional_words, &frame_size_in_words); ++ ++ // Now it is safe to overwrite any register ++ // store the correct deoptimization type ++ __ li(reason, Deoptimization::Unpack_exception); ++ // load throwing pc from JavaThread and patch it as the return address ++ // of the current frame. Then clear the field in JavaThread ++ __ get_thread(thread); ++ __ ld_ptr(V1, thread, in_bytes(JavaThread::exception_pc_offset())); ++ __ st_ptr(V1, SP, reg_save.ra_offset()); //save ra ++ __ st_ptr(R0, thread, in_bytes(JavaThread::exception_pc_offset())); ++ ++ ++#ifdef ASSERT ++ // verify that there is really an exception oop in JavaThread ++ __ ld_ptr(AT, thread, in_bytes(JavaThread::exception_oop_offset())); ++ __ verify_oop(AT); ++ // verify that there is no pending exception ++ Label no_pending_exception; ++ __ ld_ptr(AT, thread, in_bytes(Thread::pending_exception_offset())); ++ __ beq(AT, R0, no_pending_exception); ++ __ stop("must not have pending exception here"); ++ __ bind(no_pending_exception); ++#endif ++ __ bind(cont); ++ // Compiled code leaves the floating point stack dirty, empty it. ++ __ empty_FPU_stack(); ++ ++ ++ // Call C code. Need thread and this frame, but NOT official VM entry ++ // crud. We cannot block on this call, no GC can happen. ++#ifndef OPT_THREAD ++ __ get_thread(thread); ++#endif ++ ++ __ move(A0, thread); ++ __ addi_d(SP, SP, -additional_words * wordSize); ++ ++ Label retaddr; ++ __ set_last_Java_frame(NOREG, NOREG, retaddr); ++ ++ // Call fetch_unroll_info(). Need thread and this frame, but NOT official VM entry - cannot block on ++ // this call, no GC can happen. Call should capture return values. ++ ++ // TODO: confirm reloc ++ __ call(CAST_FROM_FN_PTR(address, Deoptimization::fetch_unroll_info), relocInfo::runtime_call_type); ++ __ bind(retaddr); ++ oop_maps->add_gc_map(__ pc() - start, map); ++ __ addi_d(SP, SP, additional_words * wordSize); ++ __ get_thread(thread); ++ __ reset_last_Java_frame(false); ++ ++ // Load UnrollBlock into S7 ++ __ move(unroll, V0); ++ ++ ++ // Move the unpack kind to a safe place in the UnrollBlock because ++ // we are very short of registers ++ ++ Address unpack_kind(unroll, Deoptimization::UnrollBlock::unpack_kind_offset_in_bytes()); ++ __ st_w(reason, unpack_kind); ++ // save the unpack_kind value ++ // Retrieve the possible live values (return values) ++ // All callee save registers representing jvm state ++ // are now in the vframeArray. ++ ++ Label noException; ++ __ li(AT, Deoptimization::Unpack_exception); ++ __ bne(AT, reason, noException);// Was exception pending? ++ __ ld_ptr(V0, thread, in_bytes(JavaThread::exception_oop_offset())); ++ __ ld_ptr(V1, thread, in_bytes(JavaThread::exception_pc_offset())); ++ __ st_ptr(R0, thread, in_bytes(JavaThread::exception_pc_offset())); ++ __ st_ptr(R0, thread, in_bytes(JavaThread::exception_oop_offset())); ++ ++ __ verify_oop(V0); ++ ++ // Overwrite the result registers with the exception results. ++ __ st_ptr(V0, SP, reg_save.v0_offset()); ++ __ st_ptr(V1, SP, reg_save.v1_offset()); ++ ++ __ bind(noException); ++ ++ ++ // Stack is back to only having register save data on the stack. ++ // Now restore the result registers. Everything else is either dead or captured ++ // in the vframeArray. ++ ++ reg_save.restore_result_registers(masm); ++ // All of the register save area has been popped of the stack. Only the ++ // return address remains. ++ // Pop all the frames we must move/replace. ++ // Frame picture (youngest to oldest) ++ // 1: self-frame (no frame link) ++ // 2: deopting frame (no frame link) ++ // 3: caller of deopting frame (could be compiled/interpreted). ++ // ++ // Note: by leaving the return address of self-frame on the stack ++ // and using the size of frame 2 to adjust the stack ++ // when we are done the return to frame 3 will still be on the stack. ++ ++ // register for the sender's sp ++ Register sender_sp = Rsender; ++ // register for frame pcs ++ Register pcs = T0; ++ // register for frame sizes ++ Register sizes = T1; ++ // register for frame count ++ Register count = T3; ++ ++ // Pop deoptimized frame ++ __ ld_w(AT, unroll, Deoptimization::UnrollBlock::size_of_deoptimized_frame_offset_in_bytes()); ++ __ add_d(SP, SP, AT); ++ // sp should be pointing at the return address to the caller (3) ++ ++ // Load array of frame pcs into pcs ++ __ ld_ptr(pcs, unroll, Deoptimization::UnrollBlock::frame_pcs_offset_in_bytes()); ++ __ addi_d(SP, SP, wordSize); // trash the old pc ++ // Load array of frame sizes into T6 ++ __ ld_ptr(sizes, unroll, Deoptimization::UnrollBlock::frame_sizes_offset_in_bytes()); ++ ++ ++ ++ // Load count of frams into T3 ++ __ ld_w(count, unroll, Deoptimization::UnrollBlock::number_of_frames_offset_in_bytes()); ++ // Pick up the initial fp we should save ++ __ ld_d(FP, unroll, Deoptimization::UnrollBlock::initial_info_offset_in_bytes()); ++ // Now adjust the caller's stack to make up for the extra locals ++ // but record the original sp so that we can save it in the skeletal interpreter ++ // frame and the stack walking of interpreter_sender will get the unextended sp ++ // value and not the "real" sp value. ++ __ move(sender_sp, SP); ++ __ ld_w(AT, unroll, Deoptimization::UnrollBlock::caller_adjustment_offset_in_bytes()); ++ __ sub_d(SP, SP, AT); ++ ++ Label loop; ++ __ bind(loop); ++ __ ld_d(T2, sizes, 0); // Load frame size ++ __ ld_ptr(AT, pcs, 0); // save return address ++ __ addi_d(T2, T2, -2 * wordSize); // we'll push pc and fp, by hand ++ __ push2(AT, FP); ++ __ move(FP, SP); ++ __ sub_d(SP, SP, T2); // Prolog! ++ // This value is corrected by layout_activation_impl ++ __ st_d(R0, FP, frame::interpreter_frame_last_sp_offset * wordSize); ++ __ st_d(sender_sp, FP, frame::interpreter_frame_sender_sp_offset * wordSize);// Make it walkable ++ __ move(sender_sp, SP); // pass to next frame ++ __ addi_d(count, count, -1); // decrement counter ++ __ addi_d(sizes, sizes, wordSize); // Bump array pointer (sizes) ++ __ addi_d(pcs, pcs, wordSize); // Bump array pointer (pcs) ++ __ bne(count, R0, loop); ++ __ ld_d(AT, pcs, 0); // frame_pcs[number_of_frames] = Interpreter::deopt_entry(vtos, 0); ++ // Re-push self-frame ++ __ push2(AT, FP); ++ __ move(FP, SP); ++ __ st_d(R0, FP, frame::interpreter_frame_last_sp_offset * wordSize); ++ __ st_d(sender_sp, FP, frame::interpreter_frame_sender_sp_offset * wordSize); ++ __ addi_d(SP, SP, -(frame_size_in_words - 2 - additional_words) * wordSize); ++ ++ // Restore frame locals after moving the frame ++ __ st_d(V0, SP, reg_save.v0_offset()); ++ __ st_d(V1, SP, reg_save.v1_offset()); ++ __ fst_d(F0, SP, reg_save.fpr0_offset()); ++ __ fst_d(F1, SP, reg_save.fpr1_offset()); ++ ++ // Call unpack_frames(). Need thread and this frame, but NOT official VM entry - cannot block on ++ // this call, no GC can happen. ++ __ move(A1, reason); // exec_mode ++ __ get_thread(thread); ++ __ move(A0, thread); // thread ++ __ addi_d(SP, SP, (-additional_words) *wordSize); ++ ++ // set last_Java_sp, last_Java_fp ++ Label L; ++ address the_pc = __ pc(); ++ __ bind(L); ++ __ set_last_Java_frame(NOREG, FP, L); ++ ++ __ li(AT, -(StackAlignmentInBytes)); ++ __ andr(SP, SP, AT); // Fix stack alignment as required by ABI ++ ++ __ call(CAST_FROM_FN_PTR(address, Deoptimization::unpack_frames), relocInfo::runtime_call_type); ++ // Revert SP alignment after call since we're going to do some SP relative addressing below ++ __ ld_d(SP, thread, in_bytes(JavaThread::last_Java_sp_offset())); ++ // Set an oopmap for the call site ++ oop_maps->add_gc_map(the_pc - start, new OopMap(frame_size_in_words, 0)); ++ ++ __ push(V0); ++ ++ __ get_thread(thread); ++ __ reset_last_Java_frame(true); ++ ++ // Collect return values ++ __ ld_d(V0, SP, reg_save.v0_offset() + (additional_words + 1) * wordSize); ++ __ ld_d(V1, SP, reg_save.v1_offset() + (additional_words + 1) * wordSize); ++ // Pop float stack and store in local ++ __ fld_d(F0, SP, reg_save.fpr0_offset() + (additional_words + 1) * wordSize); ++ __ fld_d(F1, SP, reg_save.fpr1_offset() + (additional_words + 1) * wordSize); ++ ++ //FIXME, ++ // Clear floating point stack before returning to interpreter ++ __ empty_FPU_stack(); ++ //FIXME, we should consider about float and double ++ // Push a float or double return value if necessary. ++ __ leave(); ++ ++ // Jump to interpreter ++ __ jr(RA); ++ ++ masm->flush(); ++ _deopt_blob = DeoptimizationBlob::create(&buffer, oop_maps, 0, exception_offset, reexecute_offset, frame_size_in_words); ++ _deopt_blob->set_unpack_with_exception_in_tls_offset(exception_in_tls_offset); ++} ++ ++#ifdef COMPILER2 ++ ++//------------------------------generate_uncommon_trap_blob-------------------- ++// Ought to generate an ideal graph & compile, but here's some SPARC ASM ++// instead. ++void SharedRuntime::generate_uncommon_trap_blob() { ++ // allocate space for the code ++ ResourceMark rm; ++ // setup code generation tools ++ CodeBuffer buffer ("uncommon_trap_blob", 512*80 , 512*40 ); ++ MacroAssembler* masm = new MacroAssembler(&buffer); ++ ++ enum frame_layout { ++ fp_off, fp_off2, ++ return_off, return_off2, ++ framesize ++ }; ++ assert(framesize % 4 == 0, "sp not 16-byte aligned"); ++ address start = __ pc(); ++ ++ // Push self-frame. ++ __ addi_d(SP, SP, -framesize * BytesPerInt); ++ ++ __ st_d(RA, SP, return_off * BytesPerInt); ++ __ st_d(FP, SP, fp_off * BytesPerInt); ++ ++ __ addi_d(FP, SP, fp_off * BytesPerInt); ++ ++ // Clear the floating point exception stack ++ __ empty_FPU_stack(); ++ ++ Register thread = TREG; ++ ++#ifndef OPT_THREAD ++ __ get_thread(thread); ++#endif ++ // set last_Java_sp ++ Label retaddr; ++ __ set_last_Java_frame(NOREG, FP, retaddr); ++ // Call C code. Need thread but NOT official VM entry ++ // crud. We cannot block on this call, no GC can happen. Call should ++ // capture callee-saved registers as well as return values. ++ __ move(A0, thread); ++ // argument already in T0 ++ __ move(A1, T0); ++ __ call((address)Deoptimization::uncommon_trap, relocInfo::runtime_call_type); ++ __ bind(retaddr); ++ ++ // Set an oopmap for the call site ++ OopMapSet *oop_maps = new OopMapSet(); ++ OopMap* map = new OopMap( framesize, 0 ); ++ ++ oop_maps->add_gc_map(__ pc() - start, map); ++ ++#ifndef OPT_THREAD ++ __ get_thread(thread); ++#endif ++ __ reset_last_Java_frame(false); ++ ++ // Load UnrollBlock into S7 ++ Register unroll = S7; ++ __ move(unroll, V0); ++ ++ // Pop all the frames we must move/replace. ++ // ++ // Frame picture (youngest to oldest) ++ // 1: self-frame (no frame link) ++ // 2: deopting frame (no frame link) ++ // 3: possible-i2c-adapter-frame ++ // 4: caller of deopting frame (could be compiled/interpreted. If interpreted we will create an ++ // and c2i here) ++ ++ __ addi_d(SP, SP, framesize * BytesPerInt); ++ ++ // Pop deoptimized frame ++ __ ld_w(AT, unroll, Deoptimization::UnrollBlock::size_of_deoptimized_frame_offset_in_bytes()); ++ __ add_d(SP, SP, AT); ++ ++ // register for frame pcs ++ Register pcs = T8; ++ // register for frame sizes ++ Register sizes = T4; ++ // register for frame count ++ Register count = T3; ++ // register for the sender's sp ++ Register sender_sp = T1; ++ ++ // sp should be pointing at the return address to the caller (4) ++ // Load array of frame pcs ++ __ ld_d(pcs, unroll, Deoptimization::UnrollBlock::frame_pcs_offset_in_bytes()); ++ ++ // Load array of frame sizes ++ __ ld_d(sizes, unroll, Deoptimization::UnrollBlock::frame_sizes_offset_in_bytes()); ++ __ ld_wu(count, unroll, Deoptimization::UnrollBlock::number_of_frames_offset_in_bytes()); ++ ++ // Pick up the initial fp we should save ++ __ ld_d(FP, unroll, Deoptimization::UnrollBlock::initial_info_offset_in_bytes()); ++ // Now adjust the caller's stack to make up for the extra locals ++ // but record the original sp so that we can save it in the skeletal interpreter ++ // frame and the stack walking of interpreter_sender will get the unextended sp ++ // value and not the "real" sp value. ++ ++ __ move(sender_sp, SP); ++ __ ld_w(AT, unroll, Deoptimization::UnrollBlock::caller_adjustment_offset_in_bytes()); ++ __ sub_d(SP, SP, AT); ++ // Push interpreter frames in a loop ++ Label loop; ++ __ bind(loop); ++ __ ld_d(T2, sizes, 0); // Load frame size ++ __ ld_d(AT, pcs, 0); // save return address ++ __ addi_d(T2, T2, -2*wordSize); // we'll push pc and fp, by hand ++ __ push2(AT, FP); ++ __ move(FP, SP); ++ __ sub_d(SP, SP, T2); // Prolog! ++ // This value is corrected by layout_activation_impl ++ __ st_d(R0, FP, frame::interpreter_frame_last_sp_offset * wordSize); ++ __ st_d(sender_sp, FP, frame::interpreter_frame_sender_sp_offset * wordSize);// Make it walkable ++ __ move(sender_sp, SP); // pass to next frame ++ __ addi_d(count, count, -1); // decrement counter ++ __ addi_d(sizes, sizes, wordSize); // Bump array pointer (sizes) ++ __ addi_d(pcs, pcs, wordSize); // Bump array pointer (pcs) ++ __ bne(count, R0, loop); ++ ++ __ ld_d(RA, pcs, 0); ++ ++ // Re-push self-frame ++ // save old & set new FP ++ // save final return address ++ __ enter(); ++ ++ // Use FP because the frames look interpreted now ++ // Save "the_pc" since it cannot easily be retrieved using the last_java_SP after we aligned SP. ++ // Don't need the precise return PC here, just precise enough to point into this code blob. ++ Label L; ++ address the_pc = __ pc(); ++ __ bind(L); ++ __ set_last_Java_frame(NOREG, FP, L); ++ ++ __ li(AT, -(StackAlignmentInBytes)); ++ __ andr(SP, SP, AT); // Fix stack alignment as required by ABI ++ ++ // Call C code. Need thread but NOT official VM entry ++ // crud. We cannot block on this call, no GC can happen. Call should ++ // restore return values to their stack-slots with the new SP. ++ __ move(A0, thread); ++ __ li(A1, Deoptimization::Unpack_uncommon_trap); ++ __ call((address)Deoptimization::unpack_frames, relocInfo::runtime_call_type); ++ // Set an oopmap for the call site ++ oop_maps->add_gc_map(the_pc - start, new OopMap(framesize, 0)); ++ ++ __ reset_last_Java_frame(true); ++ ++ // Pop self-frame. ++ __ leave(); // Epilog! ++ ++ // Jump to interpreter ++ __ jr(RA); ++ // ------------- ++ // make sure all code is generated ++ masm->flush(); ++ _uncommon_trap_blob = UncommonTrapBlob::create(&buffer, oop_maps, framesize / 2); ++} ++ ++#endif // COMPILER2 ++ ++//------------------------------generate_handler_blob------------------- ++// ++// Generate a special Compile2Runtime blob that saves all registers, and sets ++// up an OopMap and calls safepoint code to stop the compiled code for ++// a safepoint. ++// ++// This blob is jumped to (via a breakpoint and the signal handler) from a ++// safepoint in compiled code. ++ ++SafepointBlob* SharedRuntime::generate_handler_blob(address call_ptr, int poll_type) { ++ ++ // Account for thread arg in our frame ++ const int additional_words = 0; ++ int frame_size_in_words; ++ ++ assert (StubRoutines::forward_exception_entry() != NULL, "must be generated before"); ++ ++ ResourceMark rm; ++ OopMapSet *oop_maps = new OopMapSet(); ++ OopMap* map; ++ ++ // allocate space for the code ++ // setup code generation tools ++ CodeBuffer buffer ("handler_blob", 2048, 512); ++ MacroAssembler* masm = new MacroAssembler( &buffer); ++ ++ const Register thread = TREG; ++ address start = __ pc(); ++ bool cause_return = (poll_type == POLL_AT_RETURN); ++ RegisterSaver reg_save(poll_type == POLL_AT_VECTOR_LOOP /* save_vectors */); ++ ++ // If cause_return is true we are at a poll_return and there is ++ // the return address in RA to the caller on the nmethod ++ // that is safepoint. We can leave this return in RA and ++ // effectively complete the return and safepoint in the caller. ++ // Otherwise we load exception pc to RA. ++ __ push(thread); ++#ifndef OPT_THREAD ++ __ get_thread(thread); ++#endif ++ ++ if(!cause_return) { ++ __ ld_ptr(RA, Address(thread, JavaThread::saved_exception_pc_offset())); ++ } ++ ++ __ pop(thread); ++ map = reg_save.save_live_registers(masm, additional_words, &frame_size_in_words); ++ ++#ifndef OPT_THREAD ++ __ get_thread(thread); ++#endif ++ ++ // The following is basically a call_VM. However, we need the precise ++ // address of the call in order to generate an oopmap. Hence, we do all the ++ // work outselvs. ++ ++ __ move(A0, thread); ++ Label retaddr; ++ __ set_last_Java_frame(NOREG, NOREG, retaddr); ++ ++ // Do the call ++ // TODO: confirm reloc ++ __ call(call_ptr, relocInfo::runtime_call_type); ++ __ bind(retaddr); ++ ++ // Set an oopmap for the call site. This oopmap will map all ++ // oop-registers and debug-info registers as callee-saved. This ++ // will allow deoptimization at this safepoint to find all possible ++ // debug-info recordings, as well as let GC find all oops. ++ oop_maps->add_gc_map(__ pc() - start, map); ++ ++ Label noException; ++ ++ // Clear last_Java_sp again ++ __ reset_last_Java_frame(false); ++ ++ __ ld_ptr(AT, thread, in_bytes(Thread::pending_exception_offset())); ++ __ beq(AT, R0, noException); ++ ++ // Exception pending ++ ++ reg_save.restore_live_registers(masm); ++ //forward_exception_entry need return address on the stack ++ __ push(RA); ++ // TODO: confirm reloc ++ __ jmp((address)StubRoutines::forward_exception_entry(), relocInfo::runtime_call_type); ++ ++ // No exception case ++ __ bind(noException); ++ // Normal exit, register restoring and exit ++ reg_save.restore_live_registers(masm); ++ __ jr(RA); ++ ++ masm->flush(); ++ // Fill-out other meta info ++ return SafepointBlob::create(&buffer, oop_maps, frame_size_in_words); ++} ++ ++// ++// generate_resolve_blob - call resolution (static/virtual/opt-virtual/ic-miss ++// ++// Generate a stub that calls into vm to find out the proper destination ++// of a java call. All the argument registers are live at this point ++// but since this is generic code we don't know what they are and the caller ++// must do any gc of the args. ++// ++RuntimeStub* SharedRuntime::generate_resolve_blob(address destination, const char* name) { ++ assert (StubRoutines::forward_exception_entry() != NULL, "must be generated before"); ++ ++ // allocate space for the code ++ ResourceMark rm; ++ ++ //CodeBuffer buffer(name, 1000, 512); ++ //FIXME. code_size ++ CodeBuffer buffer(name, 2000, 2048); ++ MacroAssembler* masm = new MacroAssembler(&buffer); ++ ++ int frame_size_words; ++ RegisterSaver reg_save(false /* save_vectors */); ++ //we put the thread in A0 ++ ++ OopMapSet *oop_maps = new OopMapSet(); ++ OopMap* map = NULL; ++ ++ address start = __ pc(); ++ map = reg_save.save_live_registers(masm, 0, &frame_size_words); ++ ++ ++ int frame_complete = __ offset(); ++ const Register thread = T8; ++ __ get_thread(thread); ++ ++ __ move(A0, thread); ++ Label retaddr; ++ __ set_last_Java_frame(noreg, FP, retaddr); ++ // align the stack before invoke native ++ __ li(AT, -(StackAlignmentInBytes)); ++ __ andr(SP, SP, AT); ++ ++ // TODO: confirm reloc ++ __ call(destination, relocInfo::runtime_call_type); ++ __ bind(retaddr); ++ ++ // Set an oopmap for the call site. ++ // We need this not only for callee-saved registers, but also for volatile ++ // registers that the compiler might be keeping live across a safepoint. ++ oop_maps->add_gc_map(__ pc() - start, map); ++ // V0 contains the address we are going to jump to assuming no exception got installed ++ __ get_thread(thread); ++ __ ld_ptr(SP, thread, in_bytes(JavaThread::last_Java_sp_offset())); ++ // clear last_Java_sp ++ __ reset_last_Java_frame(true); ++ // check for pending exceptions ++ Label pending; ++ __ ld_ptr(AT, thread, in_bytes(Thread::pending_exception_offset())); ++ __ bne(AT, R0, pending); ++ // get the returned Method* ++ __ get_vm_result_2(Rmethod, thread); ++ __ st_ptr(Rmethod, SP, reg_save.s3_offset()); ++ __ st_ptr(V0, SP, reg_save.t5_offset()); ++ reg_save.restore_live_registers(masm); ++ ++ // We are back the the original state on entry and ready to go the callee method. ++ __ jr(T5); ++ // Pending exception after the safepoint ++ ++ __ bind(pending); ++ ++ reg_save.restore_live_registers(masm); ++ ++ // exception pending => remove activation and forward to exception handler ++ //forward_exception_entry need return address on the stack ++ __ push(RA); ++ __ get_thread(thread); ++ __ st_ptr(R0, thread, in_bytes(JavaThread::vm_result_offset())); ++ __ ld_ptr(V0, thread, in_bytes(Thread::pending_exception_offset())); ++ __ jmp(StubRoutines::forward_exception_entry(), relocInfo::runtime_call_type); ++ // ++ // make sure all code is generated ++ masm->flush(); ++ RuntimeStub* tmp= RuntimeStub::new_runtime_stub(name, &buffer, frame_complete, frame_size_words, oop_maps, true); ++ return tmp; ++} ++ ++extern "C" int SpinPause() {return 0;} +diff --git a/hotspot/src/cpu/loongarch/vm/stubGenerator_loongarch_64.cpp b/hotspot/src/cpu/loongarch/vm/stubGenerator_loongarch_64.cpp +new file mode 100644 +index 0000000000..b54857d4e7 +--- /dev/null ++++ b/hotspot/src/cpu/loongarch/vm/stubGenerator_loongarch_64.cpp +@@ -0,0 +1,3445 @@ ++/* ++ * Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2015, 2021, Loongson Technology. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ * ++ */ ++ ++#include "precompiled.hpp" ++#include "asm/macroAssembler.hpp" ++#include "asm/macroAssembler.inline.hpp" ++#include "interpreter/interpreter.hpp" ++#include "nativeInst_loongarch.hpp" ++#include "oops/instanceOop.hpp" ++#include "oops/method.hpp" ++#include "oops/objArrayKlass.hpp" ++#include "oops/oop.inline.hpp" ++#include "prims/methodHandles.hpp" ++#include "runtime/frame.inline.hpp" ++#include "runtime/handles.inline.hpp" ++#include "runtime/sharedRuntime.hpp" ++#include "runtime/stubCodeGenerator.hpp" ++#include "runtime/stubRoutines.hpp" ++#include "runtime/thread.inline.hpp" ++#include "utilities/top.hpp" ++#ifdef COMPILER2 ++#include "opto/runtime.hpp" ++#endif ++ ++// Declaration and definition of StubGenerator (no .hpp file). ++// For a more detailed description of the stub routine structure ++// see the comment in stubRoutines.hpp ++ ++#define __ _masm-> ++ ++#define A0 RA0 ++#define A1 RA1 ++#define A2 RA2 ++#define A3 RA3 ++#define A4 RA4 ++#define A5 RA5 ++#define A6 RA6 ++#define A7 RA7 ++#define T0 RT0 ++#define T1 RT1 ++#define T2 RT2 ++#define T3 RT3 ++#define T4 RT4 ++#define T5 RT5 ++#define T6 RT6 ++#define T7 RT7 ++#define T8 RT8 ++ ++#define TIMES_OOP (UseCompressedOops ? Address::times_4 : Address::times_8) ++ ++//#ifdef PRODUCT ++//#define BLOCK_COMMENT(str) /* nothing */ ++//#else ++//#define BLOCK_COMMENT(str) __ block_comment(str) ++//#endif ++ ++//#define BIND(label) bind(label); BLOCK_COMMENT(#label ":") ++const int MXCSR_MASK = 0xFFC0; // Mask out any pending exceptions ++ ++// Stub Code definitions ++ ++static address handle_unsafe_access() { ++ JavaThread* thread = JavaThread::current(); ++ address pc = thread->saved_exception_pc(); ++ // pc is the instruction which we must emulate ++ // doing a no-op is fine: return garbage from the load ++ // therefore, compute npc ++ address npc = (address)((unsigned long)pc + sizeof(unsigned int)); ++ ++ // request an async exception ++ thread->set_pending_unsafe_access_error(); ++ ++ // return address of next instruction to execute ++ return npc; ++} ++ ++class StubGenerator: public StubCodeGenerator { ++ private: ++ ++ // This fig is not LA ABI. It is call Java from C ABI. ++ // Call stubs are used to call Java from C ++ // ++ // [ return_from_Java ] ++ // [ argument word n-1 ] <--- sp ++ // ... ++ // [ argument word 0 ] ++ // ... ++ // -8 [ S6 ] ++ // -7 [ S5 ] ++ // -6 [ S4 ] ++ // -5 [ S3 ] ++ // -4 [ S1 ] ++ // -3 [ TSR(S2) ] ++ // -2 [ LVP(S7) ] ++ // -1 [ BCP(S1) ] ++ // 0 [ saved fp ] <--- fp_after_call ++ // 1 [ return address ] ++ // 2 [ ptr. to call wrapper ] <--- a0 (old sp -->)fp ++ // 3 [ result ] <--- a1 ++ // 4 [ result_type ] <--- a2 ++ // 5 [ method ] <--- a3 ++ // 6 [ entry_point ] <--- a4 ++ // 7 [ parameters ] <--- a5 ++ // 8 [ parameter_size ] <--- a6 ++ // 9 [ thread ] <--- a7 ++ ++ // ++ // LA ABI does not save paras in sp. ++ // ++ // [ return_from_Java ] ++ // [ argument word n-1 ] <--- sp ++ // ... ++ // [ argument word 0 ] ++ // ... ++ //-13 [ thread ] ++ //-12 [ result_type ] <--- a2 ++ //-11 [ result ] <--- a1 ++ //-10 [ ] ++ // -9 [ ptr. to call wrapper ] <--- a0 ++ // -8 [ S6 ] ++ // -7 [ S5 ] ++ // -6 [ S4 ] ++ // -5 [ S3 ] ++ // -4 [ S1 ] ++ // -3 [ TSR(S2) ] ++ // -2 [ LVP(S7) ] ++ // -1 [ BCP(S1) ] ++ // 0 [ saved fp ] <--- fp_after_call ++ // 1 [ return address ] ++ // 2 [ ] <--- old sp ++ // ++ // Find a right place in the call_stub for S8. ++ // S8 will point to the starting point of Interpreter::dispatch_table(itos). ++ // It should be saved/restored before/after Java calls. ++ // ++ enum call_stub_layout { ++ RA_off = 1, ++ FP_off = 0, ++ BCP_off = -1, ++ LVP_off = -2, ++ TSR_off = -3, ++ S1_off = -4, ++ S3_off = -5, ++ S4_off = -6, ++ S5_off = -7, ++ S6_off = -8, ++ call_wrapper_off = -9, ++ result_off = -11, ++ result_type_off = -12, ++ thread_off = -13, ++ total_off = thread_off - 1, ++ S8_off = -14, ++ }; ++ ++ address generate_call_stub(address& return_address) { ++ assert((int)frame::entry_frame_call_wrapper_offset == (int)call_wrapper_off, "adjust this code"); ++ StubCodeMark mark(this, "StubRoutines", "call_stub"); ++ address start = __ pc(); ++ ++ // same as in generate_catch_exception()! ++ ++ // stub code ++ // save ra and fp ++ __ enter(); ++ // I think 14 is the max gap between argument and callee saved register ++ __ addi_d(SP, SP, total_off * wordSize); ++ __ st_d(BCP, FP, BCP_off * wordSize); ++ __ st_d(LVP, FP, LVP_off * wordSize); ++ __ st_d(TSR, FP, TSR_off * wordSize); ++ __ st_d(S1, FP, S1_off * wordSize); ++ __ st_d(S3, FP, S3_off * wordSize); ++ __ st_d(S4, FP, S4_off * wordSize); ++ __ st_d(S5, FP, S5_off * wordSize); ++ __ st_d(S6, FP, S6_off * wordSize); ++ __ st_d(A0, FP, call_wrapper_off * wordSize); ++ __ st_d(A1, FP, result_off * wordSize); ++ __ st_d(A2, FP, result_type_off * wordSize); ++ __ st_d(A7, FP, thread_off * wordSize); ++ __ st_d(S8, FP, S8_off * wordSize); ++ ++ __ li(S8, (long)Interpreter::dispatch_table(itos)); ++ ++#ifdef OPT_THREAD ++ __ move(TREG, A7); ++#endif ++ //add for compressedoops ++ __ reinit_heapbase(); ++ ++#ifdef ASSERT ++ // make sure we have no pending exceptions ++ { ++ Label L; ++ __ ld_d(AT, A7, in_bytes(Thread::pending_exception_offset())); ++ __ beq(AT, R0, L); ++ /* FIXME: I do not know how to realize stop in LA, do it in the future */ ++ __ stop("StubRoutines::call_stub: entered with pending exception"); ++ __ bind(L); ++ } ++#endif ++ ++ // pass parameters if any ++ // A5: parameter ++ // A6: parameter_size ++ // T0: parameter_size_tmp(--) ++ // T2: offset(++) ++ // T3: tmp ++ Label parameters_done; ++ // judge if the parameter_size equals 0 ++ __ beq(A6, R0, parameters_done); ++ __ slli_d(AT, A6, Interpreter::logStackElementSize); ++ __ sub_d(SP, SP, AT); ++ __ li(AT, -StackAlignmentInBytes); ++ __ andr(SP, SP, AT); ++ // Copy Java parameters in reverse order (receiver last) ++ // Note that the argument order is inverted in the process ++ Label loop; ++ __ move(T0, A6); ++ __ move(T2, R0); ++ __ bind(loop); ++ ++ // get parameter ++ __ alsl_d(T3, T0, A5, LogBytesPerWord - 1); ++ __ ld_d(AT, T3, -wordSize); ++ __ alsl_d(T3, T2, SP, LogBytesPerWord - 1); ++ __ st_d(AT, T3, Interpreter::expr_offset_in_bytes(0)); ++ __ addi_d(T2, T2, 1); ++ __ addi_d(T0, T0, -1); ++ __ bne(T0, R0, loop); ++ // advance to next parameter ++ ++ // call Java function ++ __ bind(parameters_done); ++ ++ // receiver in V0, methodOop in Rmethod ++ ++ __ move(Rmethod, A3); ++ __ move(Rsender, SP); //set sender sp ++ __ jalr(A4); ++ return_address = __ pc(); ++ ++ Label common_return; ++ __ bind(common_return); ++ ++ // store result depending on type ++ // (everything that is not T_LONG, T_FLOAT or T_DOUBLE is treated as T_INT) ++ __ ld_d(T0, FP, result_off * wordSize); // result --> T0 ++ Label is_long, is_float, is_double, exit; ++ __ ld_d(T2, FP, result_type_off * wordSize); // result_type --> T2 ++ __ addi_d(T3, T2, (-1) * T_LONG); ++ __ beq(T3, R0, is_long); ++ __ addi_d(T3, T2, (-1) * T_FLOAT); ++ __ beq(T3, R0, is_float); ++ __ addi_d(T3, T2, (-1) * T_DOUBLE); ++ __ beq(T3, R0, is_double); ++ ++ // handle T_INT case ++ __ st_d(V0, T0, 0 * wordSize); ++ __ bind(exit); ++ ++ // restore ++ __ ld_d(BCP, FP, BCP_off * wordSize); ++ __ ld_d(LVP, FP, LVP_off * wordSize); ++ __ ld_d(S8, FP, S8_off * wordSize); ++ __ ld_d(TSR, FP, TSR_off * wordSize); ++ ++ __ ld_d(S1, FP, S1_off * wordSize); ++ __ ld_d(S3, FP, S3_off * wordSize); ++ __ ld_d(S4, FP, S4_off * wordSize); ++ __ ld_d(S5, FP, S5_off * wordSize); ++ __ ld_d(S6, FP, S6_off * wordSize); ++ ++ __ leave(); ++ ++ // return ++ __ jr(RA); ++ ++ // handle return types different from T_INT ++ __ bind(is_long); ++ __ st_d(V0, T0, 0 * wordSize); ++ __ b(exit); ++ ++ __ bind(is_float); ++ __ fst_s(FV0, T0, 0 * wordSize); ++ __ b(exit); ++ ++ __ bind(is_double); ++ __ fst_d(FV0, T0, 0 * wordSize); ++ __ b(exit); ++ StubRoutines::la::set_call_stub_compiled_return(__ pc()); ++ __ b(common_return); ++ return start; ++ } ++ ++ // Return point for a Java call if there's an exception thrown in ++ // Java code. The exception is caught and transformed into a ++ // pending exception stored in JavaThread that can be tested from ++ // within the VM. ++ // ++ // Note: Usually the parameters are removed by the callee. In case ++ // of an exception crossing an activation frame boundary, that is ++ // not the case if the callee is compiled code => need to setup the ++ // sp. ++ // ++ // V0: exception oop ++ ++ address generate_catch_exception() { ++ StubCodeMark mark(this, "StubRoutines", "catch_exception"); ++ address start = __ pc(); ++ ++ Register thread = TREG; ++ ++ // get thread directly ++#ifndef OPT_THREAD ++ __ ld_d(thread, FP, thread_off * wordSize); ++#endif ++ ++#ifdef ASSERT ++ // verify that threads correspond ++ { Label L; ++ __ get_thread(T8); ++ __ beq(T8, thread, L); ++ __ stop("StubRoutines::catch_exception: threads must correspond"); ++ __ bind(L); ++ } ++#endif ++ // set pending exception ++ __ verify_oop(V0); ++ __ st_d(V0, thread, in_bytes(Thread::pending_exception_offset())); ++ __ li(AT, (long)__FILE__); ++ __ st_d(AT, thread, in_bytes(Thread::exception_file_offset ())); ++ __ li(AT, (long)__LINE__); ++ __ st_d(AT, thread, in_bytes(Thread::exception_line_offset ())); ++ ++ // complete return to VM ++ assert(StubRoutines::_call_stub_return_address != NULL, "_call_stub_return_address must have been generated before"); ++ __ jmp(StubRoutines::_call_stub_return_address, relocInfo::none); ++ return start; ++ } ++ ++ // Continuation point for runtime calls returning with a pending ++ // exception. The pending exception check happened in the runtime ++ // or native call stub. The pending exception in Thread is ++ // converted into a Java-level exception. ++ // ++ // Contract with Java-level exception handlers: ++ // V0: exception ++ // V1: throwing pc ++ // ++ // NOTE: At entry of this stub, exception-pc must be on stack !! ++ ++ address generate_forward_exception() { ++ StubCodeMark mark(this, "StubRoutines", "forward exception"); ++ //Register thread = TREG; ++ Register thread = TREG; ++ address start = __ pc(); ++ ++ // Upon entry, the sp points to the return address returning into ++ // Java (interpreted or compiled) code; i.e., the return address ++ // throwing pc. ++ // ++ // Arguments pushed before the runtime call are still on the stack ++ // but the exception handler will reset the stack pointer -> ++ // ignore them. A potential result in registers can be ignored as ++ // well. ++ ++#ifndef OPT_THREAD ++ __ get_thread(thread); ++#endif ++#ifdef ASSERT ++ // make sure this code is only executed if there is a pending exception ++ { ++ Label L; ++ __ ld_d(AT, thread, in_bytes(Thread::pending_exception_offset())); ++ __ bne(AT, R0, L); ++ __ stop("StubRoutines::forward exception: no pending exception (1)"); ++ __ bind(L); ++ } ++#endif ++ ++ // compute exception handler into T4 ++ __ ld_d(A1, SP, 0); ++ __ call_VM_leaf(CAST_FROM_FN_PTR(address, SharedRuntime::exception_handler_for_return_address), thread, A1); ++ __ move(T4, V0); ++ __ pop(V1); ++ ++#ifndef OPT_THREAD ++ __ get_thread(thread); ++#endif ++ __ ld_d(V0, thread, in_bytes(Thread::pending_exception_offset())); ++ __ st_d(R0, thread, in_bytes(Thread::pending_exception_offset())); ++ ++#ifdef ASSERT ++ // make sure exception is set ++ { ++ Label L; ++ __ bne(V0, R0, L); ++ __ stop("StubRoutines::forward exception: no pending exception (2)"); ++ __ bind(L); ++ } ++#endif ++ ++ // continue at exception handler (return address removed) ++ // V0: exception ++ // T4: exception handler ++ // V1: throwing pc ++ __ verify_oop(V0); ++ __ jr(T4); ++ return start; ++ } ++ ++ // The following routine generates a subroutine to throw an ++ // asynchronous UnknownError when an unsafe access gets a fault that ++ // could not be reasonably prevented by the programmer. (Example: ++ // SIGBUS/OBJERR.) ++ address generate_handler_for_unsafe_access() { ++ StubCodeMark mark(this, "StubRoutines", "handler_for_unsafe_access"); ++ address start = __ pc(); ++ __ push(V0); ++ __ pushad_except_v0(); // push registers ++ __ call(CAST_FROM_FN_PTR(address, handle_unsafe_access), relocInfo::runtime_call_type); ++ __ popad_except_v0(); ++ __ move(RA, V0); ++ __ pop(V0); ++ __ jr(RA); ++ return start; ++ } ++ ++ // Non-destructive plausibility checks for oops ++ // ++ address generate_verify_oop() { ++ StubCodeMark mark(this, "StubRoutines", "verify_oop"); ++ address start = __ pc(); ++ __ reinit_heapbase(); ++ __ verify_oop_subroutine(); ++ address end = __ pc(); ++ return start; ++ } ++ ++ // ++ // Generate stub for array fill. If "aligned" is true, the ++ // "to" address is assumed to be heapword aligned. ++ // ++ // Arguments for generated stub: ++ // to: A0 ++ // value: A1 ++ // count: A2 treated as signed ++ // ++ address generate_fill(BasicType t, bool aligned, const char *name) { ++ __ align(CodeEntryAlignment); ++ StubCodeMark mark(this, "StubRoutines", name); ++ address start = __ pc(); ++ ++ const Register to = A0; // source array address ++ const Register value = A1; // value ++ const Register count = A2; // elements count ++ ++ const Register end = T5; // source array address end ++ const Register tmp = T8; // temp register ++ ++ Label L_fill_elements; ++ ++ int shift = -1; ++ switch (t) { ++ case T_BYTE: ++ shift = 0; ++ __ slti(AT, count, 9); // Short arrays (<= 8 bytes) fill by element ++ __ bstrins_d(value, value, 15, 8); // 8 bit -> 16 bit ++ __ bstrins_d(value, value, 31, 16); // 16 bit -> 32 bit ++ __ bstrins_d(value, value, 63, 32); // 32 bit -> 64 bit ++ __ bnez(AT, L_fill_elements); ++ break; ++ case T_SHORT: ++ shift = 1; ++ __ slti(AT, count, 5); // Short arrays (<= 8 bytes) fill by element ++ __ bstrins_d(value, value, 31, 16); // 16 bit -> 32 bit ++ __ bstrins_d(value, value, 63, 32); // 32 bit -> 64 bit ++ __ bnez(AT, L_fill_elements); ++ break; ++ case T_INT: ++ shift = 2; ++ __ slti(AT, count, 3); // Short arrays (<= 8 bytes) fill by element ++ __ bstrins_d(value, value, 63, 32); // 32 bit -> 64 bit ++ __ bnez(AT, L_fill_elements); ++ break; ++ default: ShouldNotReachHere(); ++ } ++ ++ switch (t) { ++ case T_BYTE: ++ __ add_d(end, to, count); ++ break; ++ case T_SHORT: ++ case T_INT: ++ __ alsl_d(end, count, to, shift-1); ++ break; ++ default: ShouldNotReachHere(); ++ } ++ if (!aligned) { ++ __ st_d(value, to, 0); ++ __ bstrins_d(to, R0, 2, 0); ++ __ addi_d(to, to, 8); ++ } ++ __ st_d(value, end, -8); ++ __ bstrins_d(end, R0, 2, 0); ++ ++ // ++ // Fill large chunks ++ // ++ Label L_loop_begin, L_not_64bytes_fill, L_loop_end; ++ __ addi_d(AT, to, 64); ++ __ blt(end, AT, L_not_64bytes_fill); ++ __ addi_d(to, to, 64); ++ __ bind(L_loop_begin); ++ __ st_d(value, to, -8); ++ __ st_d(value, to, -16); ++ __ st_d(value, to, -24); ++ __ st_d(value, to, -32); ++ __ st_d(value, to, -40); ++ __ st_d(value, to, -48); ++ __ st_d(value, to, -56); ++ __ st_d(value, to, -64); ++ __ addi_d(to, to, 64); ++ __ bge(end, to, L_loop_begin); ++ __ addi_d(to, to, -64); ++ __ beq(to, end, L_loop_end); ++ ++ __ bind(L_not_64bytes_fill); ++ // There are 0 - 7 words ++ __ pcaddi(AT, 4); ++ __ sub_d(tmp, end, to); ++ __ alsl_d(AT, tmp, AT, 1); ++ __ jr(AT); ++ ++ // 0: ++ __ jr(RA); ++ __ nop(); ++ __ nop(); ++ __ nop(); ++ __ nop(); ++ __ nop(); ++ __ nop(); ++ __ nop(); ++ ++ // 1: ++ __ st_d(value, to, 0); ++ __ jr(RA); ++ __ nop(); ++ __ nop(); ++ __ nop(); ++ __ nop(); ++ __ nop(); ++ __ nop(); ++ ++ // 2: ++ __ st_d(value, to, 0); ++ __ st_d(value, to, 8); ++ __ jr(RA); ++ __ nop(); ++ __ nop(); ++ __ nop(); ++ __ nop(); ++ __ nop(); ++ ++ // 3: ++ __ st_d(value, to, 0); ++ __ st_d(value, to, 8); ++ __ st_d(value, to, 16); ++ __ jr(RA); ++ __ nop(); ++ __ nop(); ++ __ nop(); ++ __ nop(); ++ ++ // 4: ++ __ st_d(value, to, 0); ++ __ st_d(value, to, 8); ++ __ st_d(value, to, 16); ++ __ st_d(value, to, 24); ++ __ jr(RA); ++ __ nop(); ++ __ nop(); ++ __ nop(); ++ ++ // 5: ++ __ st_d(value, to, 0); ++ __ st_d(value, to, 8); ++ __ st_d(value, to, 16); ++ __ st_d(value, to, 24); ++ __ st_d(value, to, 32); ++ __ jr(RA); ++ __ nop(); ++ __ nop(); ++ ++ // 6: ++ __ st_d(value, to, 0); ++ __ st_d(value, to, 8); ++ __ st_d(value, to, 16); ++ __ st_d(value, to, 24); ++ __ st_d(value, to, 32); ++ __ st_d(value, to, 40); ++ __ jr(RA); ++ __ nop(); ++ ++ // 7: ++ __ st_d(value, to, 0); ++ __ st_d(value, to, 8); ++ __ st_d(value, to, 16); ++ __ st_d(value, to, 24); ++ __ st_d(value, to, 32); ++ __ st_d(value, to, 40); ++ __ st_d(value, to, 48); ++ ++ __ bind(L_loop_end); ++ __ jr(RA); ++ ++ // Short arrays (<= 8 bytes) ++ __ bind(L_fill_elements); ++ __ pcaddi(AT, 4); ++ __ slli_d(tmp, count, 4 + shift); ++ __ add_d(AT, AT, tmp); ++ __ jr(AT); ++ ++ // 0: ++ __ jr(RA); ++ __ nop(); ++ __ nop(); ++ __ nop(); ++ ++ // 1: ++ __ st_b(value, to, 0); ++ __ jr(RA); ++ __ nop(); ++ __ nop(); ++ ++ // 2: ++ __ st_h(value, to, 0); ++ __ jr(RA); ++ __ nop(); ++ __ nop(); ++ ++ // 3: ++ __ st_h(value, to, 0); ++ __ st_b(value, to, 2); ++ __ jr(RA); ++ __ nop(); ++ ++ // 4: ++ __ st_w(value, to, 0); ++ __ jr(RA); ++ __ nop(); ++ __ nop(); ++ ++ // 5: ++ __ st_w(value, to, 0); ++ __ st_b(value, to, 4); ++ __ jr(RA); ++ __ nop(); ++ ++ // 6: ++ __ st_w(value, to, 0); ++ __ st_h(value, to, 4); ++ __ jr(RA); ++ __ nop(); ++ ++ // 7: ++ __ st_w(value, to, 0); ++ __ st_w(value, to, 3); ++ __ jr(RA); ++ __ nop(); ++ ++ // 8: ++ __ st_d(value, to, 0); ++ __ jr(RA); ++ return start; ++ } ++ ++ // ++ // Generate overlap test for array copy stubs ++ // ++ // Input: ++ // A0 - source array address ++ // A1 - destination array address ++ // A2 - element count ++ // ++ // Temp: ++ // AT - destination array address - source array address ++ // T4 - element count * element size ++ // ++ void array_overlap_test(address no_overlap_target, int log2_elem_size) { ++ __ slli_d(T4, A2, log2_elem_size); ++ __ sub_d(AT, A1, A0); ++ __ bgeu(AT, T4, no_overlap_target); ++ } ++ ++ // Generate code for an array write pre barrier ++ // ++ // Input: ++ // addr - starting address ++ // count - element count ++ // ++ // Temp: ++ // AT - used to swap addr and count ++ // ++ void gen_write_ref_array_pre_barrier(Register addr, Register count, bool dest_uninitialized) { ++ BarrierSet* bs = Universe::heap()->barrier_set(); ++ switch (bs->kind()) { ++ case BarrierSet::G1SATBCT: ++ case BarrierSet::G1SATBCTLogging: ++ // With G1, don't generate the call if we statically know that the target in uninitialized ++ if (!dest_uninitialized) { ++ if (count == A0) { ++ if (addr == A1) { ++ // exactly backwards!! ++ __ move(AT, A0); ++ __ move(A0, A1); ++ __ move(A1, AT); ++ } else { ++ __ move(A1, count); ++ __ move(A0, addr); ++ } ++ } else { ++ __ move(A0, addr); ++ __ move(A1, count); ++ } ++ __ call_VM_leaf(CAST_FROM_FN_PTR(address, BarrierSet::static_write_ref_array_pre), 2); ++ } ++ break; ++ case BarrierSet::CardTableModRef: ++ case BarrierSet::CardTableExtension: ++ case BarrierSet::ModRef: ++ break; ++ default: ++ ShouldNotReachHere(); ++ } ++ } ++ ++ // ++ // Generate code for an array write post barrier ++ // ++ // Input: ++ // start - register containing starting address of destination array ++ // count - elements count ++ // scratch - scratch register ++ // ++ // Temp: ++ // AT - used to swap addr and count ++ // ++ // The input registers are overwritten. ++ // ++ void gen_write_ref_array_post_barrier(Register start, Register count, Register scratch) { ++ assert_different_registers(start, count, scratch, AT); ++ BarrierSet* bs = Universe::heap()->barrier_set(); ++ switch (bs->kind()) { ++ case BarrierSet::G1SATBCT: ++ case BarrierSet::G1SATBCTLogging: ++ { ++ if (count == A0) { ++ if (start == A1) { ++ // exactly backwards!! ++ __ move(AT, A0); ++ __ move(A0, A1); ++ __ move(A1, AT); ++ } else { ++ __ move(A1, count); ++ __ move(A0, start); ++ } ++ } else { ++ __ move(A0, start); ++ __ move(A1, count); ++ } ++ __ call_VM_leaf(CAST_FROM_FN_PTR(address, BarrierSet::static_write_ref_array_post), 2); ++ } ++ break; ++ case BarrierSet::CardTableModRef: ++ case BarrierSet::CardTableExtension: ++ { ++ CardTableModRefBS* ct = (CardTableModRefBS*)bs; ++ assert(sizeof(*ct->byte_map_base) == sizeof(jbyte), "adjust this code"); ++ ++ Label L_loop; ++ const Register end = count; ++ ++ if (UseConcMarkSweepGC) { ++ __ membar(__ StoreLoad); ++ } ++ ++ int64_t disp = (int64_t) ct->byte_map_base; ++ __ li(scratch, disp); ++ ++ __ lea(end, Address(start, count, TIMES_OOP, 0)); // end == start + count * oop_size ++ __ addi_d(end, end, -BytesPerHeapOop); // end - 1 to make inclusive ++ __ shr(start, CardTableModRefBS::card_shift); ++ __ shr(end, CardTableModRefBS::card_shift); ++ __ sub_d(end, end, start); // end --> cards count ++ ++ __ add_d(start, start, scratch); ++ ++ __ bind(L_loop); ++ __ stx_b(R0, start, count); ++ __ addi_d(count, count, -1); ++ __ bge(count, R0, L_loop); ++ } ++ break; ++ default: ++ ShouldNotReachHere(); ++ } ++ } ++ ++ // disjoint large copy ++ void generate_disjoint_large_copy(Label &entry, const char *name) { ++ StubCodeMark mark(this, "StubRoutines", name); ++ __ align(CodeEntryAlignment); ++ ++ Label loop, le32, le16, le8, lt8; ++ ++ __ bind(entry); ++ __ add_d(A3, A1, A2); ++ __ add_d(A2, A0, A2); ++ __ ld_d(A6, A0, 0); ++ __ ld_d(A7, A2, -8); ++ ++ __ andi(T1, A0, 7); ++ __ sub_d(T0, R0, T1); ++ __ addi_d(T0, T0, 8); ++ ++ __ add_d(A0, A0, T0); ++ __ add_d(A5, A1, T0); ++ ++ __ addi_d(A4, A2, -64); ++ __ bgeu(A0, A4, le32); ++ ++ __ bind(loop); ++ __ ld_d(T0, A0, 0); ++ __ ld_d(T1, A0, 8); ++ __ ld_d(T2, A0, 16); ++ __ ld_d(T3, A0, 24); ++ __ ld_d(T4, A0, 32); ++ __ ld_d(T5, A0, 40); ++ __ ld_d(T6, A0, 48); ++ __ ld_d(T7, A0, 56); ++ __ addi_d(A0, A0, 64); ++ __ st_d(T0, A5, 0); ++ __ st_d(T1, A5, 8); ++ __ st_d(T2, A5, 16); ++ __ st_d(T3, A5, 24); ++ __ st_d(T4, A5, 32); ++ __ st_d(T5, A5, 40); ++ __ st_d(T6, A5, 48); ++ __ st_d(T7, A5, 56); ++ __ addi_d(A5, A5, 64); ++ __ bltu(A0, A4, loop); ++ ++ __ bind(le32); ++ __ addi_d(A4, A2, -32); ++ __ bgeu(A0, A4, le16); ++ __ ld_d(T0, A0, 0); ++ __ ld_d(T1, A0, 8); ++ __ ld_d(T2, A0, 16); ++ __ ld_d(T3, A0, 24); ++ __ addi_d(A0, A0, 32); ++ __ st_d(T0, A5, 0); ++ __ st_d(T1, A5, 8); ++ __ st_d(T2, A5, 16); ++ __ st_d(T3, A5, 24); ++ __ addi_d(A5, A5, 32); ++ ++ __ bind(le16); ++ __ addi_d(A4, A2, -16); ++ __ bgeu(A0, A4, le8); ++ __ ld_d(T0, A0, 0); ++ __ ld_d(T1, A0, 8); ++ __ addi_d(A0, A0, 16); ++ __ st_d(T0, A5, 0); ++ __ st_d(T1, A5, 8); ++ __ addi_d(A5, A5, 16); ++ ++ __ bind(le8); ++ __ addi_d(A4, A2, -8); ++ __ bgeu(A0, A4, lt8); ++ __ ld_d(T0, A0, 0); ++ __ st_d(T0, A5, 0); ++ ++ __ bind(lt8); ++ __ st_d(A6, A1, 0); ++ __ st_d(A7, A3, -8); ++ __ jr(RA); ++ } ++ ++ // conjoint large copy ++ void generate_conjoint_large_copy(Label &entry, const char *name) { ++ StubCodeMark mark(this, "StubRoutines", name); ++ __ align(CodeEntryAlignment); ++ ++ Label loop, le32, le16, le8, lt8; ++ ++ __ bind(entry); ++ __ add_d(A3, A1, A2); ++ __ add_d(A2, A0, A2); ++ __ ld_d(A6, A0, 0); ++ __ ld_d(A7, A2, -8); ++ ++ __ andi(T1, A0, 7); ++ __ sub_d(A2, A2, T1); ++ __ sub_d(A5, A3, T1); ++ ++ __ addi_d(A4, A0, 64); ++ __ bgeu(A4, A2, le32); ++ ++ __ bind(loop); ++ __ ld_d(T0, A2, -8); ++ __ ld_d(T1, A2, -16); ++ __ ld_d(T2, A2, -24); ++ __ ld_d(T3, A2, -32); ++ __ ld_d(T4, A2, -40); ++ __ ld_d(T5, A2, -48); ++ __ ld_d(T6, A2, -56); ++ __ ld_d(T7, A2, -64); ++ __ addi_d(A2, A2, -64); ++ __ st_d(T0, A5, -8); ++ __ st_d(T1, A5, -16); ++ __ st_d(T2, A5, -24); ++ __ st_d(T3, A5, -32); ++ __ st_d(T4, A5, -40); ++ __ st_d(T5, A5, -48); ++ __ st_d(T6, A5, -56); ++ __ st_d(T7, A5, -64); ++ __ addi_d(A5, A5, -64); ++ __ bltu(A4, A2, loop); ++ ++ __ bind(le32); ++ __ addi_d(A4, A0, 32); ++ __ bgeu(A4, A2, le16); ++ __ ld_d(T0, A2, -8); ++ __ ld_d(T1, A2, -16); ++ __ ld_d(T2, A2, -24); ++ __ ld_d(T3, A2, -32); ++ __ addi_d(A2, A2, -32); ++ __ st_d(T0, A5, -8); ++ __ st_d(T1, A5, -16); ++ __ st_d(T2, A5, -24); ++ __ st_d(T3, A5, -32); ++ __ addi_d(A5, A5, -32); ++ ++ __ bind(le16); ++ __ addi_d(A4, A0, 16); ++ __ bgeu(A4, A2, le8); ++ __ ld_d(T0, A2, -8); ++ __ ld_d(T1, A2, -16); ++ __ addi_d(A2, A2, -16); ++ __ st_d(T0, A5, -8); ++ __ st_d(T1, A5, -16); ++ __ addi_d(A5, A5, -16); ++ ++ __ bind(le8); ++ __ addi_d(A4, A0, 8); ++ __ bgeu(A4, A2, lt8); ++ __ ld_d(T0, A2, -8); ++ __ st_d(T0, A5, -8); ++ ++ __ bind(lt8); ++ __ st_d(A6, A1, 0); ++ __ st_d(A7, A3, -8); ++ __ jr(RA); ++ } ++ ++ // Byte small copy: less than 9 elements. ++ void generate_byte_small_copy(Label &entry, const char *name) { ++ StubCodeMark mark(this, "StubRoutines", name); ++ __ align(CodeEntryAlignment); ++ ++ Label L; ++ __ bind(entry); ++ __ lipc(AT, L); ++ __ slli_d(A2, A2, 5); ++ __ add_d(AT, AT, A2); ++ __ jr(AT); ++ ++ __ bind(L); ++ // 0: ++ __ jr(RA); ++ __ nop(); ++ __ nop(); ++ __ nop(); ++ __ nop(); ++ __ nop(); ++ __ nop(); ++ __ nop(); ++ ++ // 1: ++ __ ld_b(AT, A0, 0); ++ __ st_b(AT, A1, 0); ++ __ jr(RA); ++ __ nop(); ++ __ nop(); ++ __ nop(); ++ __ nop(); ++ __ nop(); ++ ++ // 2: ++ __ ld_h(AT, A0, 0); ++ __ st_h(AT, A1, 0); ++ __ jr(RA); ++ __ nop(); ++ __ nop(); ++ __ nop(); ++ __ nop(); ++ __ nop(); ++ ++ // 3: ++ __ ld_h(AT, A0, 0); ++ __ ld_b(A2, A0, 2); ++ __ st_h(AT, A1, 0); ++ __ st_b(A2, A1, 2); ++ __ jr(RA); ++ __ nop(); ++ __ nop(); ++ __ nop(); ++ ++ // 4: ++ __ ld_w(AT, A0, 0); ++ __ st_w(AT, A1, 0); ++ __ jr(RA); ++ __ nop(); ++ __ nop(); ++ __ nop(); ++ __ nop(); ++ __ nop(); ++ ++ // 5: ++ __ ld_w(AT, A0, 0); ++ __ ld_b(A2, A0, 4); ++ __ st_w(AT, A1, 0); ++ __ st_b(A2, A1, 4); ++ __ jr(RA); ++ __ nop(); ++ __ nop(); ++ __ nop(); ++ ++ // 6: ++ __ ld_w(AT, A0, 0); ++ __ ld_h(A2, A0, 4); ++ __ st_w(AT, A1, 0); ++ __ st_h(A2, A1, 4); ++ __ jr(RA); ++ __ nop(); ++ __ nop(); ++ __ nop(); ++ ++ // 7: ++ __ ld_w(AT, A0, 0); ++ __ ld_w(A2, A0, 3); ++ __ st_w(AT, A1, 0); ++ __ st_w(A2, A1, 3); ++ __ jr(RA); ++ __ nop(); ++ __ nop(); ++ __ nop(); ++ ++ // 8: ++ __ ld_d(AT, A0, 0); ++ __ st_d(AT, A1, 0); ++ __ jr(RA); ++ } ++ ++ // Arguments: ++ // aligned - true => Input and output aligned on a HeapWord == 8-byte boundary ++ // ignored ++ // name - stub name string ++ // ++ // Inputs: ++ // A0 - source array address ++ // A1 - destination array address ++ // A2 - element count, treated as ssize_t, can be zero ++ // ++ // If 'from' and/or 'to' are aligned on 4-, 2-, or 1-byte boundaries, ++ // we let the hardware handle it. The one to eight bytes within words, ++ // dwords or qwords that span cache line boundaries will still be loaded ++ // and stored atomically. ++ // ++ // Side Effects: ++ // disjoint_byte_copy_entry is set to the no-overlap entry point ++ // used by generate_conjoint_byte_copy(). ++ // ++ address generate_disjoint_byte_copy(bool aligned, Label &small, Label &large, ++ const char * name) { ++ StubCodeMark mark(this, "StubRoutines", name); ++ __ align(CodeEntryAlignment); ++ address start = __ pc(); ++ ++ __ sltui(T0, A2, 9); ++ __ bnez(T0, small); ++ ++ __ b(large); ++ ++ return start; ++ } ++ ++ // Arguments: ++ // aligned - true => Input and output aligned on a HeapWord == 8-byte boundary ++ // ignored ++ // name - stub name string ++ // ++ // Inputs: ++ // A0 - source array address ++ // A1 - destination array address ++ // A2 - element count, treated as ssize_t, can be zero ++ // ++ // If 'from' and/or 'to' are aligned on 4-, 2-, or 1-byte boundaries, ++ // we let the hardware handle it. The one to eight bytes within words, ++ // dwords or qwords that span cache line boundaries will still be loaded ++ // and stored atomically. ++ // ++ address generate_conjoint_byte_copy(bool aligned, Label &small, Label &large, ++ const char *name) { ++ StubCodeMark mark(this, "StubRoutines", name); ++ __ align(CodeEntryAlignment); ++ address start = __ pc(); ++ ++ array_overlap_test(StubRoutines::jbyte_disjoint_arraycopy(), 0); ++ ++ __ sltui(T0, A2, 9); ++ __ bnez(T0, small); ++ ++ __ b(large); ++ ++ return start; ++ } ++ ++ // Short small copy: less than 9 elements. ++ void generate_short_small_copy(Label &entry, const char *name) { ++ StubCodeMark mark(this, "StubRoutines", name); ++ __ align(CodeEntryAlignment); ++ ++ Label L; ++ __ bind(entry); ++ __ lipc(AT, L); ++ __ slli_d(A2, A2, 5); ++ __ add_d(AT, AT, A2); ++ __ jr(AT); ++ ++ __ bind(L); ++ // 0: ++ __ jr(RA); ++ __ nop(); ++ __ nop(); ++ __ nop(); ++ __ nop(); ++ __ nop(); ++ __ nop(); ++ __ nop(); ++ ++ // 1: ++ __ ld_h(AT, A0, 0); ++ __ st_h(AT, A1, 0); ++ __ jr(RA); ++ __ nop(); ++ __ nop(); ++ __ nop(); ++ __ nop(); ++ __ nop(); ++ ++ // 2: ++ __ ld_w(AT, A0, 0); ++ __ st_w(AT, A1, 0); ++ __ jr(RA); ++ __ nop(); ++ __ nop(); ++ __ nop(); ++ __ nop(); ++ __ nop(); ++ ++ // 3: ++ __ ld_w(AT, A0, 0); ++ __ ld_h(A2, A0, 4); ++ __ st_w(AT, A1, 0); ++ __ st_h(A2, A1, 4); ++ __ jr(RA); ++ __ nop(); ++ __ nop(); ++ __ nop(); ++ ++ // 4: ++ __ ld_d(AT, A0, 0); ++ __ st_d(AT, A1, 0); ++ __ jr(RA); ++ __ nop(); ++ __ nop(); ++ __ nop(); ++ __ nop(); ++ __ nop(); ++ ++ // 5: ++ __ ld_d(AT, A0, 0); ++ __ ld_h(A2, A0, 8); ++ __ st_d(AT, A1, 0); ++ __ st_h(A2, A1, 8); ++ __ jr(RA); ++ __ nop(); ++ __ nop(); ++ __ nop(); ++ ++ // 6: ++ __ ld_d(AT, A0, 0); ++ __ ld_w(A2, A0, 8); ++ __ st_d(AT, A1, 0); ++ __ st_w(A2, A1, 8); ++ __ jr(RA); ++ __ nop(); ++ __ nop(); ++ __ nop(); ++ ++ // 7: ++ __ ld_d(AT, A0, 0); ++ __ ld_d(A2, A0, 6); ++ __ st_d(AT, A1, 0); ++ __ st_d(A2, A1, 6); ++ __ jr(RA); ++ __ nop(); ++ __ nop(); ++ __ nop(); ++ ++ // 8: ++ __ ld_d(AT, A0, 0); ++ __ ld_d(A2, A0, 8); ++ __ st_d(AT, A1, 0); ++ __ st_d(A2, A1, 8); ++ __ jr(RA); ++ } ++ ++ // Arguments: ++ // aligned - true => Input and output aligned on a HeapWord == 8-byte boundary ++ // ignored ++ // name - stub name string ++ // ++ // Inputs: ++ // A0 - source array address ++ // A1 - destination array address ++ // A2 - element count, treated as ssize_t, can be zero ++ // ++ // If 'from' and/or 'to' are aligned on 4-, 2-, or 1-byte boundaries, ++ // we let the hardware handle it. The one to eight bytes within words, ++ // dwords or qwords that span cache line boundaries will still be loaded ++ // and stored atomically. ++ // ++ // Side Effects: ++ // disjoint_short_copy_entry is set to the no-overlap entry point ++ // used by generate_conjoint_short_copy(). ++ // ++ address generate_disjoint_short_copy(bool aligned, Label &small, Label &large, ++ const char * name) { ++ StubCodeMark mark(this, "StubRoutines", name); ++ __ align(CodeEntryAlignment); ++ address start = __ pc(); ++ ++ __ sltui(T0, A2, 9); ++ __ bnez(T0, small); ++ ++ __ slli_d(A2, A2, 1); ++ __ b(large); ++ ++ return start; ++ } ++ ++ // Arguments: ++ // aligned - true => Input and output aligned on a HeapWord == 8-byte boundary ++ // ignored ++ // name - stub name string ++ // ++ // Inputs: ++ // A0 - source array address ++ // A1 - destination array address ++ // A2 - element count, treated as ssize_t, can be zero ++ // ++ // If 'from' and/or 'to' are aligned on 4- or 2-byte boundaries, we ++ // let the hardware handle it. The two or four words within dwords ++ // or qwords that span cache line boundaries will still be loaded ++ // and stored atomically. ++ // ++ address generate_conjoint_short_copy(bool aligned, Label &small, Label &large, ++ const char *name) { ++ StubCodeMark mark(this, "StubRoutines", name); ++ __ align(CodeEntryAlignment); ++ address start = __ pc(); ++ ++ array_overlap_test(StubRoutines::jshort_disjoint_arraycopy(), 1); ++ ++ __ sltui(T0, A2, 9); ++ __ bnez(T0, small); ++ ++ __ slli_d(A2, A2, 1); ++ __ b(large); ++ ++ return start; ++ } ++ ++ // Short small copy: less than 7 elements. ++ void generate_int_small_copy(Label &entry, const char *name) { ++ StubCodeMark mark(this, "StubRoutines", name); ++ __ align(CodeEntryAlignment); ++ ++ Label L; ++ __ bind(entry); ++ __ lipc(AT, L); ++ __ slli_d(A2, A2, 5); ++ __ add_d(AT, AT, A2); ++ __ jr(AT); ++ ++ __ bind(L); ++ // 0: ++ __ jr(RA); ++ __ nop(); ++ __ nop(); ++ __ nop(); ++ __ nop(); ++ __ nop(); ++ __ nop(); ++ __ nop(); ++ ++ // 1: ++ __ ld_w(AT, A0, 0); ++ __ st_w(AT, A1, 0); ++ __ jr(RA); ++ __ nop(); ++ __ nop(); ++ __ nop(); ++ __ nop(); ++ __ nop(); ++ ++ // 2: ++ __ ld_d(AT, A0, 0); ++ __ st_d(AT, A1, 0); ++ __ jr(RA); ++ __ nop(); ++ __ nop(); ++ __ nop(); ++ __ nop(); ++ __ nop(); ++ ++ // 3: ++ __ ld_d(AT, A0, 0); ++ __ ld_w(A2, A0, 8); ++ __ st_d(AT, A1, 0); ++ __ st_w(A2, A1, 8); ++ __ jr(RA); ++ __ nop(); ++ __ nop(); ++ __ nop(); ++ ++ // 4: ++ __ ld_d(AT, A0, 0); ++ __ ld_d(A2, A0, 8); ++ __ st_d(AT, A1, 0); ++ __ st_d(A2, A1, 8); ++ __ jr(RA); ++ __ nop(); ++ __ nop(); ++ __ nop(); ++ ++ // 5: ++ __ ld_d(AT, A0, 0); ++ __ ld_d(A2, A0, 8); ++ __ ld_w(A3, A0, 16); ++ __ st_d(AT, A1, 0); ++ __ st_d(A2, A1, 8); ++ __ st_w(A3, A1, 16); ++ __ jr(RA); ++ __ nop(); ++ ++ // 6: ++ __ ld_d(AT, A0, 0); ++ __ ld_d(A2, A0, 8); ++ __ ld_d(A3, A0, 16); ++ __ st_d(AT, A1, 0); ++ __ st_d(A2, A1, 8); ++ __ st_d(A3, A1, 16); ++ __ jr(RA); ++ } ++ ++ // Generate maybe oop copy ++ void gen_maybe_oop_copy(bool is_oop, Label &small, Label &large, ++ const char *name, int small_limit, int log2_elem_size, ++ bool dest_uninitialized = false) { ++ Label post, _large; ++ ++ if (is_oop) { ++ __ addi_d(SP, SP, -4 * wordSize); ++ __ st_d(A2, SP, 3 * wordSize); ++ __ st_d(A1, SP, 2 * wordSize); ++ __ st_d(A0, SP, 1 * wordSize); ++ __ st_d(RA, SP, 0 * wordSize); ++ ++ gen_write_ref_array_pre_barrier(A1, A2, dest_uninitialized); ++ ++ __ ld_d(A2, SP, 3 * wordSize); ++ __ ld_d(A1, SP, 2 * wordSize); ++ __ ld_d(A0, SP, 1 * wordSize); ++ } ++ ++ __ sltui(T0, A2, small_limit); ++ if (is_oop) { ++ __ beqz(T0, _large); ++ __ bl(small); ++ __ b(post); ++ } else { ++ __ bnez(T0, small); ++ } ++ ++ __ bind(_large); ++ __ slli_d(A2, A2, log2_elem_size); ++ ++ if (is_oop) { ++ __ bl(large); ++ } else { ++ __ b(large); ++ } ++ ++ if (is_oop) { ++ __ bind(post); ++ __ ld_d(A2, SP, 3 * wordSize); ++ __ ld_d(A1, SP, 2 * wordSize); ++ ++ gen_write_ref_array_post_barrier(A1, A2, T1); ++ ++ __ ld_d(RA, SP, 0 * wordSize); ++ __ addi_d(SP, SP, 4 * wordSize); ++ __ jr(RA); ++ } ++ } ++ ++ // Arguments: ++ // aligned - true => Input and output aligned on a HeapWord == 8-byte boundary ++ // ignored ++ // is_oop - true => oop array, so generate store check code ++ // name - stub name string ++ // ++ // Inputs: ++ // A0 - source array address ++ // A1 - destination array address ++ // A2 - element count, treated as ssize_t, can be zero ++ // ++ // If 'from' and/or 'to' are aligned on 4-byte boundaries, we let ++ // the hardware handle it. The two dwords within qwords that span ++ // cache line boundaries will still be loaded and stored atomicly. ++ // ++ // Side Effects: ++ // disjoint_int_copy_entry is set to the no-overlap entry point ++ // used by generate_conjoint_int_oop_copy(). ++ // ++ address generate_disjoint_int_oop_copy(bool aligned, bool is_oop, Label &small, ++ Label &large, const char *name, ++ bool dest_uninitialized = false) { ++ StubCodeMark mark(this, "StubRoutines", name); ++ __ align(CodeEntryAlignment); ++ address start = __ pc(); ++ ++ gen_maybe_oop_copy(is_oop, small, large, name, 7, 2, dest_uninitialized); ++ ++ return start; ++ } ++ ++ // Arguments: ++ // aligned - true => Input and output aligned on a HeapWord == 8-byte boundary ++ // ignored ++ // is_oop - true => oop array, so generate store check code ++ // name - stub name string ++ // ++ // Inputs: ++ // A0 - source array address ++ // A1 - destination array address ++ // A2 - element count, treated as ssize_t, can be zero ++ // ++ // If 'from' and/or 'to' are aligned on 4-byte boundaries, we let ++ // the hardware handle it. The two dwords within qwords that span ++ // cache line boundaries will still be loaded and stored atomicly. ++ // ++ address generate_conjoint_int_oop_copy(bool aligned, bool is_oop, ++ Label &small, Label &large, const char *name, ++ bool dest_uninitialized = false) { ++ StubCodeMark mark(this, "StubRoutines", name); ++ __ align(CodeEntryAlignment); ++ address start = __ pc(); ++ ++ if (is_oop) { ++ array_overlap_test(StubRoutines::oop_disjoint_arraycopy(), 2); ++ } else { ++ array_overlap_test(StubRoutines::jint_disjoint_arraycopy(), 2); ++ } ++ ++ gen_maybe_oop_copy(is_oop, small, large, name, 7, 2, dest_uninitialized); ++ ++ return start; ++ } ++ ++ // Long small copy: less than 4 elements. ++ void generate_long_small_copy(Label &entry, const char *name) { ++ StubCodeMark mark(this, "StubRoutines", name); ++ __ align(CodeEntryAlignment); ++ ++ Label L; ++ __ bind(entry); ++ __ lipc(AT, L); ++ __ slli_d(A2, A2, 5); ++ __ add_d(AT, AT, A2); ++ __ jr(AT); ++ ++ __ bind(L); ++ // 0: ++ __ jr(RA); ++ __ nop(); ++ __ nop(); ++ __ nop(); ++ __ nop(); ++ __ nop(); ++ __ nop(); ++ __ nop(); ++ ++ // 1: ++ __ ld_d(AT, A0, 0); ++ __ st_d(AT, A1, 0); ++ __ jr(RA); ++ __ nop(); ++ __ nop(); ++ __ nop(); ++ __ nop(); ++ __ nop(); ++ ++ // 2: ++ __ ld_d(AT, A0, 0); ++ __ ld_d(A2, A0, 8); ++ __ st_d(AT, A1, 0); ++ __ st_d(A2, A1, 8); ++ __ jr(RA); ++ __ nop(); ++ __ nop(); ++ __ nop(); ++ ++ // 3: ++ __ ld_d(AT, A0, 0); ++ __ ld_d(A2, A0, 8); ++ __ ld_d(A3, A0, 16); ++ __ st_d(AT, A1, 0); ++ __ st_d(A2, A1, 8); ++ __ st_d(A3, A1, 16); ++ __ jr(RA); ++ __ nop(); ++ } ++ ++ // Arguments: ++ // aligned - true => Input and output aligned on a HeapWord == 8-byte boundary ++ // ignored ++ // is_oop - true => oop array, so generate store check code ++ // name - stub name string ++ // ++ // Inputs: ++ // A0 - source array address ++ // A1 - destination array address ++ // A2 - element count, treated as ssize_t, can be zero ++ // ++ // If 'from' and/or 'to' are aligned on 4-byte boundaries, we let ++ // the hardware handle it. The two dwords within qwords that span ++ // cache line boundaries will still be loaded and stored atomicly. ++ // ++ // Side Effects: ++ // disjoint_int_copy_entry is set to the no-overlap entry point ++ // used by generate_conjoint_int_oop_copy(). ++ // ++ address generate_disjoint_long_oop_copy(bool aligned, bool is_oop, Label &small, ++ Label &large, const char *name, ++ bool dest_uninitialized = false) { ++ StubCodeMark mark(this, "StubRoutines", name); ++ __ align(CodeEntryAlignment); ++ address start = __ pc(); ++ ++ gen_maybe_oop_copy(is_oop, small, large, name, 4, 3, dest_uninitialized); ++ ++ return start; ++ } ++ ++ // Arguments: ++ // aligned - true => Input and output aligned on a HeapWord == 8-byte boundary ++ // ignored ++ // is_oop - true => oop array, so generate store check code ++ // name - stub name string ++ // ++ // Inputs: ++ // A0 - source array address ++ // A1 - destination array address ++ // A2 - element count, treated as ssize_t, can be zero ++ // ++ // If 'from' and/or 'to' are aligned on 4-byte boundaries, we let ++ // the hardware handle it. The two dwords within qwords that span ++ // cache line boundaries will still be loaded and stored atomicly. ++ // ++ address generate_conjoint_long_oop_copy(bool aligned, bool is_oop, Label &small, ++ Label &large, const char *name, ++ bool dest_uninitialized = false) { ++ StubCodeMark mark(this, "StubRoutines", name); ++ __ align(CodeEntryAlignment); ++ address start = __ pc(); ++ ++ if (is_oop) { ++ array_overlap_test(StubRoutines::oop_disjoint_arraycopy(), 3); ++ } else { ++ array_overlap_test(StubRoutines::jlong_disjoint_arraycopy(), 3); ++ } ++ ++ gen_maybe_oop_copy(is_oop, small, large, name, 4, 3, dest_uninitialized); ++ ++ return start; ++ } ++ ++ void generate_arraycopy_stubs() { ++ Label disjoint_large_copy, conjoint_large_copy; ++ Label byte_small_copy, short_small_copy, int_small_copy, long_small_copy; ++ ++ generate_disjoint_large_copy(disjoint_large_copy, "disjoint_large_copy"); ++ generate_conjoint_large_copy(conjoint_large_copy, "conjoint_large_copy"); ++ generate_byte_small_copy(byte_small_copy, "jbyte_small_copy"); ++ generate_short_small_copy(short_small_copy, "jshort_small_copy"); ++ generate_int_small_copy(int_small_copy, "jint_small_copy"); ++ generate_long_small_copy(long_small_copy, "jlong_small_copy"); ++ ++ if (UseCompressedOops) { ++ StubRoutines::_oop_disjoint_arraycopy = generate_disjoint_int_oop_copy(false, true, int_small_copy, disjoint_large_copy, "oop_disjoint_arraycopy"); ++ StubRoutines::_oop_arraycopy = generate_conjoint_int_oop_copy(false, true, int_small_copy, conjoint_large_copy, "oop_arraycopy"); ++ StubRoutines::_oop_disjoint_arraycopy_uninit = generate_disjoint_int_oop_copy(false, true, int_small_copy, disjoint_large_copy, "oop_disjoint_arraycopy_uninit", true); ++ StubRoutines::_oop_arraycopy_uninit = generate_conjoint_int_oop_copy(false, true, int_small_copy, conjoint_large_copy, "oop_arraycopy_uninit", true); ++ } else { ++ StubRoutines::_oop_disjoint_arraycopy = generate_disjoint_long_oop_copy(false, true, long_small_copy, disjoint_large_copy, "oop_disjoint_arraycopy"); ++ StubRoutines::_oop_arraycopy = generate_conjoint_long_oop_copy(false, true, long_small_copy, conjoint_large_copy, "oop_arraycopy"); ++ StubRoutines::_oop_disjoint_arraycopy_uninit = generate_disjoint_long_oop_copy(false, true, long_small_copy, disjoint_large_copy, "oop_disjoint_arraycopy_uninit", true); ++ StubRoutines::_oop_arraycopy_uninit = generate_conjoint_long_oop_copy(false, true, long_small_copy, conjoint_large_copy, "oop_arraycopy_uninit", true); ++ } ++ ++ StubRoutines::_jbyte_disjoint_arraycopy = generate_disjoint_byte_copy(false, byte_small_copy, disjoint_large_copy, "jbyte_disjoint_arraycopy"); ++ StubRoutines::_jshort_disjoint_arraycopy = generate_disjoint_short_copy(false, short_small_copy, disjoint_large_copy, "jshort_disjoint_arraycopy"); ++ StubRoutines::_jint_disjoint_arraycopy = generate_disjoint_int_oop_copy(false, false, int_small_copy, disjoint_large_copy, "jint_disjoint_arraycopy"); ++ StubRoutines::_jlong_disjoint_arraycopy = generate_disjoint_long_oop_copy(false, false, long_small_copy, disjoint_large_copy, "jlong_disjoint_arraycopy", false); ++ ++ StubRoutines::_jbyte_arraycopy = generate_conjoint_byte_copy(false, byte_small_copy, conjoint_large_copy, "jbyte_arraycopy"); ++ StubRoutines::_jshort_arraycopy = generate_conjoint_short_copy(false, short_small_copy, conjoint_large_copy, "jshort_arraycopy"); ++ StubRoutines::_jint_arraycopy = generate_conjoint_int_oop_copy(false, false, int_small_copy, conjoint_large_copy, "jint_arraycopy"); ++ StubRoutines::_jlong_arraycopy = generate_conjoint_long_oop_copy(false, false, long_small_copy, conjoint_large_copy, "jlong_arraycopy", false); ++ ++ // We don't generate specialized code for HeapWord-aligned source ++ // arrays, so just use the code we've already generated ++ StubRoutines::_arrayof_jbyte_disjoint_arraycopy = StubRoutines::_jbyte_disjoint_arraycopy; ++ StubRoutines::_arrayof_jbyte_arraycopy = StubRoutines::_jbyte_arraycopy; ++ ++ StubRoutines::_arrayof_jshort_disjoint_arraycopy = StubRoutines::_jshort_disjoint_arraycopy; ++ StubRoutines::_arrayof_jshort_arraycopy = StubRoutines::_jshort_arraycopy; ++ ++ StubRoutines::_arrayof_jint_disjoint_arraycopy = StubRoutines::_jint_disjoint_arraycopy; ++ StubRoutines::_arrayof_jint_arraycopy = StubRoutines::_jint_arraycopy; ++ ++ StubRoutines::_arrayof_jlong_disjoint_arraycopy = StubRoutines::_jlong_disjoint_arraycopy; ++ StubRoutines::_arrayof_jlong_arraycopy = StubRoutines::_jlong_arraycopy; ++ ++ StubRoutines::_arrayof_oop_disjoint_arraycopy = StubRoutines::_oop_disjoint_arraycopy; ++ StubRoutines::_arrayof_oop_arraycopy = StubRoutines::_oop_arraycopy; ++ ++ StubRoutines::_arrayof_oop_disjoint_arraycopy_uninit = StubRoutines::_oop_disjoint_arraycopy_uninit; ++ StubRoutines::_arrayof_oop_arraycopy_uninit = StubRoutines::_oop_arraycopy_uninit; ++ ++ StubRoutines::_jbyte_fill = generate_fill(T_BYTE, false, "jbyte_fill"); ++ StubRoutines::_jshort_fill = generate_fill(T_SHORT, false, "jshort_fill"); ++ StubRoutines::_jint_fill = generate_fill(T_INT, false, "jint_fill"); ++ StubRoutines::_arrayof_jbyte_fill = generate_fill(T_BYTE, true, "arrayof_jbyte_fill"); ++ StubRoutines::_arrayof_jshort_fill = generate_fill(T_SHORT, true, "arrayof_jshort_fill"); ++ StubRoutines::_arrayof_jint_fill = generate_fill(T_INT, true, "arrayof_jint_fill"); ++ } ++ ++ // Arguments: ++ // ++ // Inputs: ++ // A0 - source byte array address ++ // A1 - destination byte array address ++ // A2 - K (key) in little endian int array ++ // A3 - r vector byte array address ++ // A4 - input length ++ // ++ // Output: ++ // A0 - input length ++ // ++ address generate_aescrypt_encryptBlock(bool cbc) { ++ static const uint32_t ft_consts[256] = { ++ 0xc66363a5, 0xf87c7c84, 0xee777799, 0xf67b7b8d, ++ 0xfff2f20d, 0xd66b6bbd, 0xde6f6fb1, 0x91c5c554, ++ 0x60303050, 0x02010103, 0xce6767a9, 0x562b2b7d, ++ 0xe7fefe19, 0xb5d7d762, 0x4dababe6, 0xec76769a, ++ 0x8fcaca45, 0x1f82829d, 0x89c9c940, 0xfa7d7d87, ++ 0xeffafa15, 0xb25959eb, 0x8e4747c9, 0xfbf0f00b, ++ 0x41adadec, 0xb3d4d467, 0x5fa2a2fd, 0x45afafea, ++ 0x239c9cbf, 0x53a4a4f7, 0xe4727296, 0x9bc0c05b, ++ 0x75b7b7c2, 0xe1fdfd1c, 0x3d9393ae, 0x4c26266a, ++ 0x6c36365a, 0x7e3f3f41, 0xf5f7f702, 0x83cccc4f, ++ 0x6834345c, 0x51a5a5f4, 0xd1e5e534, 0xf9f1f108, ++ 0xe2717193, 0xabd8d873, 0x62313153, 0x2a15153f, ++ 0x0804040c, 0x95c7c752, 0x46232365, 0x9dc3c35e, ++ 0x30181828, 0x379696a1, 0x0a05050f, 0x2f9a9ab5, ++ 0x0e070709, 0x24121236, 0x1b80809b, 0xdfe2e23d, ++ 0xcdebeb26, 0x4e272769, 0x7fb2b2cd, 0xea75759f, ++ 0x1209091b, 0x1d83839e, 0x582c2c74, 0x341a1a2e, ++ 0x361b1b2d, 0xdc6e6eb2, 0xb45a5aee, 0x5ba0a0fb, ++ 0xa45252f6, 0x763b3b4d, 0xb7d6d661, 0x7db3b3ce, ++ 0x5229297b, 0xdde3e33e, 0x5e2f2f71, 0x13848497, ++ 0xa65353f5, 0xb9d1d168, 0x00000000, 0xc1eded2c, ++ 0x40202060, 0xe3fcfc1f, 0x79b1b1c8, 0xb65b5bed, ++ 0xd46a6abe, 0x8dcbcb46, 0x67bebed9, 0x7239394b, ++ 0x944a4ade, 0x984c4cd4, 0xb05858e8, 0x85cfcf4a, ++ 0xbbd0d06b, 0xc5efef2a, 0x4faaaae5, 0xedfbfb16, ++ 0x864343c5, 0x9a4d4dd7, 0x66333355, 0x11858594, ++ 0x8a4545cf, 0xe9f9f910, 0x04020206, 0xfe7f7f81, ++ 0xa05050f0, 0x783c3c44, 0x259f9fba, 0x4ba8a8e3, ++ 0xa25151f3, 0x5da3a3fe, 0x804040c0, 0x058f8f8a, ++ 0x3f9292ad, 0x219d9dbc, 0x70383848, 0xf1f5f504, ++ 0x63bcbcdf, 0x77b6b6c1, 0xafdada75, 0x42212163, ++ 0x20101030, 0xe5ffff1a, 0xfdf3f30e, 0xbfd2d26d, ++ 0x81cdcd4c, 0x180c0c14, 0x26131335, 0xc3ecec2f, ++ 0xbe5f5fe1, 0x359797a2, 0x884444cc, 0x2e171739, ++ 0x93c4c457, 0x55a7a7f2, 0xfc7e7e82, 0x7a3d3d47, ++ 0xc86464ac, 0xba5d5de7, 0x3219192b, 0xe6737395, ++ 0xc06060a0, 0x19818198, 0x9e4f4fd1, 0xa3dcdc7f, ++ 0x44222266, 0x542a2a7e, 0x3b9090ab, 0x0b888883, ++ 0x8c4646ca, 0xc7eeee29, 0x6bb8b8d3, 0x2814143c, ++ 0xa7dede79, 0xbc5e5ee2, 0x160b0b1d, 0xaddbdb76, ++ 0xdbe0e03b, 0x64323256, 0x743a3a4e, 0x140a0a1e, ++ 0x924949db, 0x0c06060a, 0x4824246c, 0xb85c5ce4, ++ 0x9fc2c25d, 0xbdd3d36e, 0x43acacef, 0xc46262a6, ++ 0x399191a8, 0x319595a4, 0xd3e4e437, 0xf279798b, ++ 0xd5e7e732, 0x8bc8c843, 0x6e373759, 0xda6d6db7, ++ 0x018d8d8c, 0xb1d5d564, 0x9c4e4ed2, 0x49a9a9e0, ++ 0xd86c6cb4, 0xac5656fa, 0xf3f4f407, 0xcfeaea25, ++ 0xca6565af, 0xf47a7a8e, 0x47aeaee9, 0x10080818, ++ 0x6fbabad5, 0xf0787888, 0x4a25256f, 0x5c2e2e72, ++ 0x381c1c24, 0x57a6a6f1, 0x73b4b4c7, 0x97c6c651, ++ 0xcbe8e823, 0xa1dddd7c, 0xe874749c, 0x3e1f1f21, ++ 0x964b4bdd, 0x61bdbddc, 0x0d8b8b86, 0x0f8a8a85, ++ 0xe0707090, 0x7c3e3e42, 0x71b5b5c4, 0xcc6666aa, ++ 0x904848d8, 0x06030305, 0xf7f6f601, 0x1c0e0e12, ++ 0xc26161a3, 0x6a35355f, 0xae5757f9, 0x69b9b9d0, ++ 0x17868691, 0x99c1c158, 0x3a1d1d27, 0x279e9eb9, ++ 0xd9e1e138, 0xebf8f813, 0x2b9898b3, 0x22111133, ++ 0xd26969bb, 0xa9d9d970, 0x078e8e89, 0x339494a7, ++ 0x2d9b9bb6, 0x3c1e1e22, 0x15878792, 0xc9e9e920, ++ 0x87cece49, 0xaa5555ff, 0x50282878, 0xa5dfdf7a, ++ 0x038c8c8f, 0x59a1a1f8, 0x09898980, 0x1a0d0d17, ++ 0x65bfbfda, 0xd7e6e631, 0x844242c6, 0xd06868b8, ++ 0x824141c3, 0x299999b0, 0x5a2d2d77, 0x1e0f0f11, ++ 0x7bb0b0cb, 0xa85454fc, 0x6dbbbbd6, 0x2c16163a ++ }; ++ static const uint8_t fsb_consts[256] = { ++ 0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, ++ 0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76, ++ 0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, ++ 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0, ++ 0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc, ++ 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15, ++ 0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a, ++ 0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75, ++ 0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0, ++ 0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84, ++ 0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b, ++ 0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf, ++ 0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85, ++ 0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8, ++ 0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5, ++ 0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2, ++ 0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17, ++ 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73, ++ 0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88, ++ 0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb, ++ 0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c, ++ 0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79, ++ 0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9, ++ 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08, ++ 0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6, ++ 0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a, ++ 0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e, ++ 0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e, ++ 0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94, ++ 0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf, ++ 0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, ++ 0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16 ++ }; ++ ++ __ align(CodeEntryAlignment); ++ StubCodeMark mark(this, "StubRoutines", "aescrypt_encryptBlock"); ++ ++ // Allocate registers ++ Register src = A0; ++ Register dst = A1; ++ Register key = A2; ++ Register rve = A3; ++ Register srclen = A4; ++ Register keylen = T8; ++ Register srcend = A5; ++ Register keyold = A6; ++ Register t0 = A7; ++ Register t1, t2, t3, ftp; ++ Register xa[4] = { T0, T1, T2, T3 }; ++ Register ya[4] = { T4, T5, T6, T7 }; ++ ++ Label loop, tail, done; ++ address start = __ pc(); ++ ++ if (cbc) { ++ t1 = S0; ++ t2 = S1; ++ t3 = S2; ++ ftp = S3; ++ ++ __ beqz(srclen, done); ++ ++ __ addi_d(SP, SP, -4 * wordSize); ++ __ st_d(S3, SP, 3 * wordSize); ++ __ st_d(S2, SP, 2 * wordSize); ++ __ st_d(S1, SP, 1 * wordSize); ++ __ st_d(S0, SP, 0 * wordSize); ++ ++ __ add_d(srcend, src, srclen); ++ __ move(keyold, key); ++ } else { ++ t1 = A3; ++ t2 = A4; ++ t3 = A5; ++ ftp = A6; ++ } ++ ++ __ ld_w(keylen, key, arrayOopDesc::length_offset_in_bytes() - arrayOopDesc::base_offset_in_bytes(T_INT)); ++ ++ // Round 1 ++ if (cbc) { ++ for (int i = 0; i < 4; i++) { ++ __ ld_w(xa[i], rve, 4 * i); ++ } ++ ++ __ bind(loop); ++ ++ for (int i = 0; i < 4; i++) { ++ __ ld_w(ya[i], src, 4 * i); ++ } ++ for (int i = 0; i < 4; i++) { ++ __ XOR(xa[i], xa[i], ya[i]); ++ } ++ } else { ++ for (int i = 0; i < 4; i++) { ++ __ ld_w(xa[i], src, 4 * i); ++ } ++ } ++ for (int i = 0; i < 4; i++) { ++ __ ld_w(ya[i], key, 4 * i); ++ } ++ for (int i = 0; i < 4; i++) { ++ __ revb_2h(xa[i], xa[i]); ++ } ++ for (int i = 0; i < 4; i++) { ++ __ rotri_w(xa[i], xa[i], 16); ++ } ++ for (int i = 0; i < 4; i++) { ++ __ XOR(xa[i], xa[i], ya[i]); ++ } ++ ++ __ li(ftp, (intptr_t)ft_consts); ++ ++ // Round 2 - (N-1) ++ for (int r = 0; r < 14; r++) { ++ Register *xp; ++ Register *yp; ++ ++ if (r & 1) { ++ xp = xa; ++ yp = ya; ++ } else { ++ xp = ya; ++ yp = xa; ++ } ++ ++ for (int i = 0; i < 4; i++) { ++ __ ld_w(xp[i], key, 4 * (4 * (r + 1) + i)); ++ } ++ ++ for (int i = 0; i < 4; i++) { ++ __ bstrpick_d(t0, yp[(i + 3) & 3], 7, 0); ++ __ bstrpick_d(t1, yp[(i + 2) & 3], 15, 8); ++ __ bstrpick_d(t2, yp[(i + 1) & 3], 23, 16); ++ __ bstrpick_d(t3, yp[(i + 0) & 3], 31, 24); ++ __ slli_w(t0, t0, 2); ++ __ slli_w(t1, t1, 2); ++ __ slli_w(t2, t2, 2); ++ __ slli_w(t3, t3, 2); ++ __ ldx_w(t0, ftp, t0); ++ __ ldx_w(t1, ftp, t1); ++ __ ldx_w(t2, ftp, t2); ++ __ ldx_w(t3, ftp, t3); ++ __ rotri_w(t0, t0, 24); ++ __ rotri_w(t1, t1, 16); ++ __ rotri_w(t2, t2, 8); ++ __ XOR(xp[i], xp[i], t0); ++ __ XOR(t0, t1, t2); ++ __ XOR(xp[i], xp[i], t3); ++ __ XOR(xp[i], xp[i], t0); ++ } ++ ++ if (r == 8) { ++ // AES 128 ++ __ li(t0, 44); ++ __ beq(t0, keylen, tail); ++ } else if (r == 10) { ++ // AES 192 ++ __ li(t0, 52); ++ __ beq(t0, keylen, tail); ++ } ++ } ++ ++ __ bind(tail); ++ __ li(ftp, (intptr_t)fsb_consts); ++ __ alsl_d(key, keylen, key, 2 - 1); ++ ++ // Round N ++ for (int i = 0; i < 4; i++) { ++ __ bstrpick_d(t0, ya[(i + 3) & 3], 7, 0); ++ __ bstrpick_d(t1, ya[(i + 2) & 3], 15, 8); ++ __ bstrpick_d(t2, ya[(i + 1) & 3], 23, 16); ++ __ bstrpick_d(t3, ya[(i + 0) & 3], 31, 24); ++ __ ldx_bu(t0, ftp, t0); ++ __ ldx_bu(t1, ftp, t1); ++ __ ldx_bu(t2, ftp, t2); ++ __ ldx_bu(t3, ftp, t3); ++ __ ld_w(xa[i], key, 4 * i - 16); ++ __ slli_w(t1, t1, 8); ++ __ slli_w(t2, t2, 16); ++ __ slli_w(t3, t3, 24); ++ __ XOR(xa[i], xa[i], t0); ++ __ XOR(t0, t1, t2); ++ __ XOR(xa[i], xa[i], t3); ++ __ XOR(xa[i], xa[i], t0); ++ } ++ ++ for (int i = 0; i < 4; i++) { ++ __ revb_2h(xa[i], xa[i]); ++ } ++ for (int i = 0; i < 4; i++) { ++ __ rotri_w(xa[i], xa[i], 16); ++ } ++ for (int i = 0; i < 4; i++) { ++ __ st_w(xa[i], dst, 4 * i); ++ } ++ ++ if (cbc) { ++ __ move(key, keyold); ++ __ addi_d(src, src, 16); ++ __ addi_d(dst, dst, 16); ++ __ blt(src, srcend, loop); ++ ++ for (int i = 0; i < 4; i++) { ++ __ st_w(xa[i], rve, 4 * i); ++ } ++ ++ __ ld_d(S3, SP, 3 * wordSize); ++ __ ld_d(S2, SP, 2 * wordSize); ++ __ ld_d(S1, SP, 1 * wordSize); ++ __ ld_d(S0, SP, 0 * wordSize); ++ __ addi_d(SP, SP, 4 * wordSize); ++ ++ __ bind(done); ++ __ move(A0, srclen); ++ } ++ ++ __ jr(RA); ++ ++ return start; ++ } ++ ++ // Arguments: ++ // ++ // Inputs: ++ // A0 - source byte array address ++ // A1 - destination byte array address ++ // A2 - K (key) in little endian int array ++ // A3 - r vector byte array address ++ // A4 - input length ++ // ++ // Output: ++ // A0 - input length ++ // ++ address generate_aescrypt_decryptBlock(bool cbc) { ++ static const uint32_t rt_consts[256] = { ++ 0x51f4a750, 0x7e416553, 0x1a17a4c3, 0x3a275e96, ++ 0x3bab6bcb, 0x1f9d45f1, 0xacfa58ab, 0x4be30393, ++ 0x2030fa55, 0xad766df6, 0x88cc7691, 0xf5024c25, ++ 0x4fe5d7fc, 0xc52acbd7, 0x26354480, 0xb562a38f, ++ 0xdeb15a49, 0x25ba1b67, 0x45ea0e98, 0x5dfec0e1, ++ 0xc32f7502, 0x814cf012, 0x8d4697a3, 0x6bd3f9c6, ++ 0x038f5fe7, 0x15929c95, 0xbf6d7aeb, 0x955259da, ++ 0xd4be832d, 0x587421d3, 0x49e06929, 0x8ec9c844, ++ 0x75c2896a, 0xf48e7978, 0x99583e6b, 0x27b971dd, ++ 0xbee14fb6, 0xf088ad17, 0xc920ac66, 0x7dce3ab4, ++ 0x63df4a18, 0xe51a3182, 0x97513360, 0x62537f45, ++ 0xb16477e0, 0xbb6bae84, 0xfe81a01c, 0xf9082b94, ++ 0x70486858, 0x8f45fd19, 0x94de6c87, 0x527bf8b7, ++ 0xab73d323, 0x724b02e2, 0xe31f8f57, 0x6655ab2a, ++ 0xb2eb2807, 0x2fb5c203, 0x86c57b9a, 0xd33708a5, ++ 0x302887f2, 0x23bfa5b2, 0x02036aba, 0xed16825c, ++ 0x8acf1c2b, 0xa779b492, 0xf307f2f0, 0x4e69e2a1, ++ 0x65daf4cd, 0x0605bed5, 0xd134621f, 0xc4a6fe8a, ++ 0x342e539d, 0xa2f355a0, 0x058ae132, 0xa4f6eb75, ++ 0x0b83ec39, 0x4060efaa, 0x5e719f06, 0xbd6e1051, ++ 0x3e218af9, 0x96dd063d, 0xdd3e05ae, 0x4de6bd46, ++ 0x91548db5, 0x71c45d05, 0x0406d46f, 0x605015ff, ++ 0x1998fb24, 0xd6bde997, 0x894043cc, 0x67d99e77, ++ 0xb0e842bd, 0x07898b88, 0xe7195b38, 0x79c8eedb, ++ 0xa17c0a47, 0x7c420fe9, 0xf8841ec9, 0x00000000, ++ 0x09808683, 0x322bed48, 0x1e1170ac, 0x6c5a724e, ++ 0xfd0efffb, 0x0f853856, 0x3daed51e, 0x362d3927, ++ 0x0a0fd964, 0x685ca621, 0x9b5b54d1, 0x24362e3a, ++ 0x0c0a67b1, 0x9357e70f, 0xb4ee96d2, 0x1b9b919e, ++ 0x80c0c54f, 0x61dc20a2, 0x5a774b69, 0x1c121a16, ++ 0xe293ba0a, 0xc0a02ae5, 0x3c22e043, 0x121b171d, ++ 0x0e090d0b, 0xf28bc7ad, 0x2db6a8b9, 0x141ea9c8, ++ 0x57f11985, 0xaf75074c, 0xee99ddbb, 0xa37f60fd, ++ 0xf701269f, 0x5c72f5bc, 0x44663bc5, 0x5bfb7e34, ++ 0x8b432976, 0xcb23c6dc, 0xb6edfc68, 0xb8e4f163, ++ 0xd731dcca, 0x42638510, 0x13972240, 0x84c61120, ++ 0x854a247d, 0xd2bb3df8, 0xaef93211, 0xc729a16d, ++ 0x1d9e2f4b, 0xdcb230f3, 0x0d8652ec, 0x77c1e3d0, ++ 0x2bb3166c, 0xa970b999, 0x119448fa, 0x47e96422, ++ 0xa8fc8cc4, 0xa0f03f1a, 0x567d2cd8, 0x223390ef, ++ 0x87494ec7, 0xd938d1c1, 0x8ccaa2fe, 0x98d40b36, ++ 0xa6f581cf, 0xa57ade28, 0xdab78e26, 0x3fadbfa4, ++ 0x2c3a9de4, 0x5078920d, 0x6a5fcc9b, 0x547e4662, ++ 0xf68d13c2, 0x90d8b8e8, 0x2e39f75e, 0x82c3aff5, ++ 0x9f5d80be, 0x69d0937c, 0x6fd52da9, 0xcf2512b3, ++ 0xc8ac993b, 0x10187da7, 0xe89c636e, 0xdb3bbb7b, ++ 0xcd267809, 0x6e5918f4, 0xec9ab701, 0x834f9aa8, ++ 0xe6956e65, 0xaaffe67e, 0x21bccf08, 0xef15e8e6, ++ 0xbae79bd9, 0x4a6f36ce, 0xea9f09d4, 0x29b07cd6, ++ 0x31a4b2af, 0x2a3f2331, 0xc6a59430, 0x35a266c0, ++ 0x744ebc37, 0xfc82caa6, 0xe090d0b0, 0x33a7d815, ++ 0xf104984a, 0x41ecdaf7, 0x7fcd500e, 0x1791f62f, ++ 0x764dd68d, 0x43efb04d, 0xccaa4d54, 0xe49604df, ++ 0x9ed1b5e3, 0x4c6a881b, 0xc12c1fb8, 0x4665517f, ++ 0x9d5eea04, 0x018c355d, 0xfa877473, 0xfb0b412e, ++ 0xb3671d5a, 0x92dbd252, 0xe9105633, 0x6dd64713, ++ 0x9ad7618c, 0x37a10c7a, 0x59f8148e, 0xeb133c89, ++ 0xcea927ee, 0xb761c935, 0xe11ce5ed, 0x7a47b13c, ++ 0x9cd2df59, 0x55f2733f, 0x1814ce79, 0x73c737bf, ++ 0x53f7cdea, 0x5ffdaa5b, 0xdf3d6f14, 0x7844db86, ++ 0xcaaff381, 0xb968c43e, 0x3824342c, 0xc2a3405f, ++ 0x161dc372, 0xbce2250c, 0x283c498b, 0xff0d9541, ++ 0x39a80171, 0x080cb3de, 0xd8b4e49c, 0x6456c190, ++ 0x7bcb8461, 0xd532b670, 0x486c5c74, 0xd0b85742 ++ }; ++ static const uint8_t rsb_consts[256] = { ++ 0x52, 0x09, 0x6a, 0xd5, 0x30, 0x36, 0xa5, 0x38, ++ 0xbf, 0x40, 0xa3, 0x9e, 0x81, 0xf3, 0xd7, 0xfb, ++ 0x7c, 0xe3, 0x39, 0x82, 0x9b, 0x2f, 0xff, 0x87, ++ 0x34, 0x8e, 0x43, 0x44, 0xc4, 0xde, 0xe9, 0xcb, ++ 0x54, 0x7b, 0x94, 0x32, 0xa6, 0xc2, 0x23, 0x3d, ++ 0xee, 0x4c, 0x95, 0x0b, 0x42, 0xfa, 0xc3, 0x4e, ++ 0x08, 0x2e, 0xa1, 0x66, 0x28, 0xd9, 0x24, 0xb2, ++ 0x76, 0x5b, 0xa2, 0x49, 0x6d, 0x8b, 0xd1, 0x25, ++ 0x72, 0xf8, 0xf6, 0x64, 0x86, 0x68, 0x98, 0x16, ++ 0xd4, 0xa4, 0x5c, 0xcc, 0x5d, 0x65, 0xb6, 0x92, ++ 0x6c, 0x70, 0x48, 0x50, 0xfd, 0xed, 0xb9, 0xda, ++ 0x5e, 0x15, 0x46, 0x57, 0xa7, 0x8d, 0x9d, 0x84, ++ 0x90, 0xd8, 0xab, 0x00, 0x8c, 0xbc, 0xd3, 0x0a, ++ 0xf7, 0xe4, 0x58, 0x05, 0xb8, 0xb3, 0x45, 0x06, ++ 0xd0, 0x2c, 0x1e, 0x8f, 0xca, 0x3f, 0x0f, 0x02, ++ 0xc1, 0xaf, 0xbd, 0x03, 0x01, 0x13, 0x8a, 0x6b, ++ 0x3a, 0x91, 0x11, 0x41, 0x4f, 0x67, 0xdc, 0xea, ++ 0x97, 0xf2, 0xcf, 0xce, 0xf0, 0xb4, 0xe6, 0x73, ++ 0x96, 0xac, 0x74, 0x22, 0xe7, 0xad, 0x35, 0x85, ++ 0xe2, 0xf9, 0x37, 0xe8, 0x1c, 0x75, 0xdf, 0x6e, ++ 0x47, 0xf1, 0x1a, 0x71, 0x1d, 0x29, 0xc5, 0x89, ++ 0x6f, 0xb7, 0x62, 0x0e, 0xaa, 0x18, 0xbe, 0x1b, ++ 0xfc, 0x56, 0x3e, 0x4b, 0xc6, 0xd2, 0x79, 0x20, ++ 0x9a, 0xdb, 0xc0, 0xfe, 0x78, 0xcd, 0x5a, 0xf4, ++ 0x1f, 0xdd, 0xa8, 0x33, 0x88, 0x07, 0xc7, 0x31, ++ 0xb1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xec, 0x5f, ++ 0x60, 0x51, 0x7f, 0xa9, 0x19, 0xb5, 0x4a, 0x0d, ++ 0x2d, 0xe5, 0x7a, 0x9f, 0x93, 0xc9, 0x9c, 0xef, ++ 0xa0, 0xe0, 0x3b, 0x4d, 0xae, 0x2a, 0xf5, 0xb0, ++ 0xc8, 0xeb, 0xbb, 0x3c, 0x83, 0x53, 0x99, 0x61, ++ 0x17, 0x2b, 0x04, 0x7e, 0xba, 0x77, 0xd6, 0x26, ++ 0xe1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0c, 0x7d ++ }; ++ ++ __ align(CodeEntryAlignment); ++ StubCodeMark mark(this, "StubRoutines", "aescrypt_decryptBlock"); ++ ++ // Allocate registers ++ Register src = A0; ++ Register dst = A1; ++ Register key = A2; ++ Register rve = A3; ++ Register srclen = A4; ++ Register keylen = T8; ++ Register srcend = A5; ++ Register t0 = A6; ++ Register t1 = A7; ++ Register t2, t3, rtp, rvp; ++ Register xa[4] = { T0, T1, T2, T3 }; ++ Register ya[4] = { T4, T5, T6, T7 }; ++ ++ Label loop, tail, done; ++ address start = __ pc(); ++ ++ if (cbc) { ++ t2 = S0; ++ t3 = S1; ++ rtp = S2; ++ rvp = S3; ++ ++ __ beqz(srclen, done); ++ ++ __ addi_d(SP, SP, -4 * wordSize); ++ __ st_d(S3, SP, 3 * wordSize); ++ __ st_d(S2, SP, 2 * wordSize); ++ __ st_d(S1, SP, 1 * wordSize); ++ __ st_d(S0, SP, 0 * wordSize); ++ ++ __ add_d(srcend, src, srclen); ++ __ move(rvp, rve); ++ } else { ++ t2 = A3; ++ t3 = A4; ++ rtp = A5; ++ } ++ ++ __ ld_w(keylen, key, arrayOopDesc::length_offset_in_bytes() - arrayOopDesc::base_offset_in_bytes(T_INT)); ++ ++ __ bind(loop); ++ ++ // Round 1 ++ for (int i = 0; i < 4; i++) { ++ __ ld_w(xa[i], src, 4 * i); ++ } ++ for (int i = 0; i < 4; i++) { ++ __ ld_w(ya[i], key, 4 * (4 + i)); ++ } ++ for (int i = 0; i < 4; i++) { ++ __ revb_2h(xa[i], xa[i]); ++ } ++ for (int i = 0; i < 4; i++) { ++ __ rotri_w(xa[i], xa[i], 16); ++ } ++ for (int i = 0; i < 4; i++) { ++ __ XOR(xa[i], xa[i], ya[i]); ++ } ++ ++ __ li(rtp, (intptr_t)rt_consts); ++ ++ // Round 2 - (N-1) ++ for (int r = 0; r < 14; r++) { ++ Register *xp; ++ Register *yp; ++ ++ if (r & 1) { ++ xp = xa; ++ yp = ya; ++ } else { ++ xp = ya; ++ yp = xa; ++ } ++ ++ for (int i = 0; i < 4; i++) { ++ __ ld_w(xp[i], key, 4 * (4 * (r + 1) + 4 + i)); ++ } ++ ++ for (int i = 0; i < 4; i++) { ++ __ bstrpick_d(t0, yp[(i + 1) & 3], 7, 0); ++ __ bstrpick_d(t1, yp[(i + 2) & 3], 15, 8); ++ __ bstrpick_d(t2, yp[(i + 3) & 3], 23, 16); ++ __ bstrpick_d(t3, yp[(i + 0) & 3], 31, 24); ++ __ slli_w(t0, t0, 2); ++ __ slli_w(t1, t1, 2); ++ __ slli_w(t2, t2, 2); ++ __ slli_w(t3, t3, 2); ++ __ ldx_w(t0, rtp, t0); ++ __ ldx_w(t1, rtp, t1); ++ __ ldx_w(t2, rtp, t2); ++ __ ldx_w(t3, rtp, t3); ++ __ rotri_w(t0, t0, 24); ++ __ rotri_w(t1, t1, 16); ++ __ rotri_w(t2, t2, 8); ++ __ XOR(xp[i], xp[i], t0); ++ __ XOR(t0, t1, t2); ++ __ XOR(xp[i], xp[i], t3); ++ __ XOR(xp[i], xp[i], t0); ++ } ++ ++ if (r == 8) { ++ // AES 128 ++ __ li(t0, 44); ++ __ beq(t0, keylen, tail); ++ } else if (r == 10) { ++ // AES 192 ++ __ li(t0, 52); ++ __ beq(t0, keylen, tail); ++ } ++ } ++ ++ __ bind(tail); ++ __ li(rtp, (intptr_t)rsb_consts); ++ ++ // Round N ++ for (int i = 0; i < 4; i++) { ++ __ bstrpick_d(t0, ya[(i + 1) & 3], 7, 0); ++ __ bstrpick_d(t1, ya[(i + 2) & 3], 15, 8); ++ __ bstrpick_d(t2, ya[(i + 3) & 3], 23, 16); ++ __ bstrpick_d(t3, ya[(i + 0) & 3], 31, 24); ++ __ ldx_bu(t0, rtp, t0); ++ __ ldx_bu(t1, rtp, t1); ++ __ ldx_bu(t2, rtp, t2); ++ __ ldx_bu(t3, rtp, t3); ++ __ ld_w(xa[i], key, 4 * i); ++ __ slli_w(t1, t1, 8); ++ __ slli_w(t2, t2, 16); ++ __ slli_w(t3, t3, 24); ++ __ XOR(xa[i], xa[i], t0); ++ __ XOR(t0, t1, t2); ++ __ XOR(xa[i], xa[i], t3); ++ __ XOR(xa[i], xa[i], t0); ++ } ++ ++ if (cbc) { ++ for (int i = 0; i < 4; i++) { ++ __ ld_w(ya[i], rvp, 4 * i); ++ } ++ } ++ for (int i = 0; i < 4; i++) { ++ __ revb_2h(xa[i], xa[i]); ++ } ++ for (int i = 0; i < 4; i++) { ++ __ rotri_w(xa[i], xa[i], 16); ++ } ++ if (cbc) { ++ for (int i = 0; i < 4; i++) { ++ __ XOR(xa[i], xa[i], ya[i]); ++ } ++ } ++ for (int i = 0; i < 4; i++) { ++ __ st_w(xa[i], dst, 4 * i); ++ } ++ ++ if (cbc) { ++ __ move(rvp, src); ++ __ addi_d(src, src, 16); ++ __ addi_d(dst, dst, 16); ++ __ blt(src, srcend, loop); ++ ++ __ ld_d(t0, src, -16); ++ __ ld_d(t1, src, -8); ++ __ st_d(t0, rve, 0); ++ __ st_d(t1, rve, 8); ++ ++ __ ld_d(S3, SP, 3 * wordSize); ++ __ ld_d(S2, SP, 2 * wordSize); ++ __ ld_d(S1, SP, 1 * wordSize); ++ __ ld_d(S0, SP, 0 * wordSize); ++ __ addi_d(SP, SP, 4 * wordSize); ++ ++ __ bind(done); ++ __ move(A0, srclen); ++ } ++ ++ __ jr(RA); ++ ++ return start; ++ } ++ ++ // Arguments: ++ // ++ // Inputs: ++ // A0 - byte[] source+offset ++ // A1 - int[] SHA.state ++ // A2 - int offset ++ // A3 - int limit ++ // ++ void generate_sha1_implCompress(const char *name, address &entry, address &entry_mb) { ++ __ align(CodeEntryAlignment); ++ StubCodeMark mark(this, "StubRoutines", name); ++ Label keys, loop; ++ ++ // Keys ++ __ bind(keys); ++ __ emit_int32(0x5a827999); ++ __ emit_int32(0x6ed9eba1); ++ __ emit_int32(0x8f1bbcdc); ++ __ emit_int32(0xca62c1d6); ++ ++ // Allocate registers ++ Register t0 = T5; ++ Register t1 = T6; ++ Register t2 = T7; ++ Register t3 = T8; ++ Register buf = A0; ++ Register state = A1; ++ Register ofs = A2; ++ Register limit = A3; ++ Register ka[4] = { A4, A5, A6, A7 }; ++ Register sa[5] = { T0, T1, T2, T3, T4 }; ++ ++ // Entry ++ entry = __ pc(); ++ __ move(ofs, R0); ++ __ move(limit, R0); ++ ++ // Entry MB ++ entry_mb = __ pc(); ++ ++ // Allocate scratch space ++ __ addi_d(SP, SP, -64); ++ ++ // Load keys ++ __ lipc(t0, keys); ++ __ ld_w(ka[0], t0, 0); ++ __ ld_w(ka[1], t0, 4); ++ __ ld_w(ka[2], t0, 8); ++ __ ld_w(ka[3], t0, 12); ++ ++ __ bind(loop); ++ // Load arguments ++ __ ld_w(sa[0], state, 0); ++ __ ld_w(sa[1], state, 4); ++ __ ld_w(sa[2], state, 8); ++ __ ld_w(sa[3], state, 12); ++ __ ld_w(sa[4], state, 16); ++ ++ // 80 rounds of hashing ++ for (int i = 0; i < 80; i++) { ++ Register a = sa[(5 - (i % 5)) % 5]; ++ Register b = sa[(6 - (i % 5)) % 5]; ++ Register c = sa[(7 - (i % 5)) % 5]; ++ Register d = sa[(8 - (i % 5)) % 5]; ++ Register e = sa[(9 - (i % 5)) % 5]; ++ ++ if (i < 16) { ++ __ ld_w(t0, buf, i * 4); ++ __ revb_2h(t0, t0); ++ __ rotri_w(t0, t0, 16); ++ __ add_w(e, e, t0); ++ __ st_w(t0, SP, i * 4); ++ __ XOR(t0, c, d); ++ __ AND(t0, t0, b); ++ __ XOR(t0, t0, d); ++ } else { ++ __ ld_w(t0, SP, ((i - 3) & 0xF) * 4); ++ __ ld_w(t1, SP, ((i - 8) & 0xF) * 4); ++ __ ld_w(t2, SP, ((i - 14) & 0xF) * 4); ++ __ ld_w(t3, SP, ((i - 16) & 0xF) * 4); ++ __ XOR(t0, t0, t1); ++ __ XOR(t0, t0, t2); ++ __ XOR(t0, t0, t3); ++ __ rotri_w(t0, t0, 31); ++ __ add_w(e, e, t0); ++ __ st_w(t0, SP, (i & 0xF) * 4); ++ ++ if (i < 20) { ++ __ XOR(t0, c, d); ++ __ AND(t0, t0, b); ++ __ XOR(t0, t0, d); ++ } else if (i < 40 || i >= 60) { ++ __ XOR(t0, b, c); ++ __ XOR(t0, t0, d); ++ } else if (i < 60) { ++ __ OR(t0, c, d); ++ __ AND(t0, t0, b); ++ __ AND(t2, c, d); ++ __ OR(t0, t0, t2); ++ } ++ } ++ ++ __ rotri_w(b, b, 2); ++ __ add_w(e, e, t0); ++ __ add_w(e, e, ka[i / 20]); ++ __ rotri_w(t0, a, 27); ++ __ add_w(e, e, t0); ++ } ++ ++ // Save updated state ++ __ ld_w(t0, state, 0); ++ __ ld_w(t1, state, 4); ++ __ ld_w(t2, state, 8); ++ __ ld_w(t3, state, 12); ++ __ add_w(sa[0], sa[0], t0); ++ __ ld_w(t0, state, 16); ++ __ add_w(sa[1], sa[1], t1); ++ __ add_w(sa[2], sa[2], t2); ++ __ add_w(sa[3], sa[3], t3); ++ __ add_w(sa[4], sa[4], t0); ++ __ st_w(sa[0], state, 0); ++ __ st_w(sa[1], state, 4); ++ __ st_w(sa[2], state, 8); ++ __ st_w(sa[3], state, 12); ++ __ st_w(sa[4], state, 16); ++ ++ __ addi_w(ofs, ofs, 64); ++ __ addi_d(buf, buf, 64); ++ __ bge(limit, ofs, loop); ++ __ move(V0, ofs); // return ofs ++ ++ __ addi_d(SP, SP, 64); ++ __ jr(RA); ++ } ++ ++ // Arguments: ++ // ++ // Inputs: ++ // A0 - byte[] source+offset ++ // A1 - int[] SHA.state ++ // A2 - int offset ++ // A3 - int limit ++ // ++ void generate_sha256_implCompress(const char *name, address &entry, address &entry_mb) { ++ static const uint32_t round_consts[64] = { ++ 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, ++ 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5, ++ 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, ++ 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, ++ 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, ++ 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, ++ 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, ++ 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967, ++ 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, ++ 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, ++ 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, ++ 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, ++ 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, ++ 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3, ++ 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, ++ 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2, ++ }; ++ __ align(CodeEntryAlignment); ++ StubCodeMark mark(this, "StubRoutines", name); ++ Label loop; ++ ++ // Allocate registers ++ Register t0 = A4; ++ Register t1 = A5; ++ Register t2 = A6; ++ Register t3 = A7; ++ Register buf = A0; ++ Register state = A1; ++ Register ofs = A2; ++ Register limit = A3; ++ Register kptr = T8; ++ Register sa[8] = { T0, T1, T2, T3, T4, T5, T6, T7 }; ++ ++ // Entry ++ entry = __ pc(); ++ __ move(ofs, R0); ++ __ move(limit, R0); ++ ++ // Entry MB ++ entry_mb = __ pc(); ++ ++ // Allocate scratch space ++ __ addi_d(SP, SP, -64); ++ ++ // Load keys base address ++ __ li(kptr, (intptr_t)round_consts); ++ ++ __ bind(loop); ++ // Load state ++ __ ld_w(sa[0], state, 0); ++ __ ld_w(sa[1], state, 4); ++ __ ld_w(sa[2], state, 8); ++ __ ld_w(sa[3], state, 12); ++ __ ld_w(sa[4], state, 16); ++ __ ld_w(sa[5], state, 20); ++ __ ld_w(sa[6], state, 24); ++ __ ld_w(sa[7], state, 28); ++ ++ // Do 64 rounds of hashing ++ for (int i = 0; i < 64; i++) { ++ Register a = sa[(0 - i) & 7]; ++ Register b = sa[(1 - i) & 7]; ++ Register c = sa[(2 - i) & 7]; ++ Register d = sa[(3 - i) & 7]; ++ Register e = sa[(4 - i) & 7]; ++ Register f = sa[(5 - i) & 7]; ++ Register g = sa[(6 - i) & 7]; ++ Register h = sa[(7 - i) & 7]; ++ ++ if (i < 16) { ++ __ ld_w(t1, buf, i * 4); ++ __ revb_2h(t1, t1); ++ __ rotri_w(t1, t1, 16); ++ } else { ++ __ ld_w(t0, SP, ((i - 15) & 0xF) * 4); ++ __ ld_w(t1, SP, ((i - 16) & 0xF) * 4); ++ __ ld_w(t2, SP, ((i - 7) & 0xF) * 4); ++ __ add_w(t1, t1, t2); ++ __ rotri_w(t2, t0, 18); ++ __ srli_w(t3, t0, 3); ++ __ rotri_w(t0, t0, 7); ++ __ XOR(t2, t2, t3); ++ __ XOR(t0, t0, t2); ++ __ add_w(t1, t1, t0); ++ __ ld_w(t0, SP, ((i - 2) & 0xF) * 4); ++ __ rotri_w(t2, t0, 19); ++ __ srli_w(t3, t0, 10); ++ __ rotri_w(t0, t0, 17); ++ __ XOR(t2, t2, t3); ++ __ XOR(t0, t0, t2); ++ __ add_w(t1, t1, t0); ++ } ++ ++ __ rotri_w(t2, e, 11); ++ __ rotri_w(t3, e, 25); ++ __ rotri_w(t0, e, 6); ++ __ XOR(t2, t2, t3); ++ __ XOR(t0, t0, t2); ++ __ XOR(t2, g, f); ++ __ ld_w(t3, kptr, i * 4); ++ __ AND(t2, t2, e); ++ __ XOR(t2, t2, g); ++ __ add_w(t0, t0, t2); ++ __ add_w(t0, t0, t3); ++ __ add_w(h, h, t1); ++ __ add_w(h, h, t0); ++ __ add_w(d, d, h); ++ __ rotri_w(t2, a, 13); ++ __ rotri_w(t3, a, 22); ++ __ rotri_w(t0, a, 2); ++ __ XOR(t2, t2, t3); ++ __ XOR(t0, t0, t2); ++ __ add_w(h, h, t0); ++ __ OR(t0, c, b); ++ __ AND(t2, c, b); ++ __ AND(t0, t0, a); ++ __ OR(t0, t0, t2); ++ __ add_w(h, h, t0); ++ __ st_w(t1, SP, (i & 0xF) * 4); ++ } ++ ++ // Add to state ++ __ ld_w(t0, state, 0); ++ __ ld_w(t1, state, 4); ++ __ ld_w(t2, state, 8); ++ __ ld_w(t3, state, 12); ++ __ add_w(sa[0], sa[0], t0); ++ __ add_w(sa[1], sa[1], t1); ++ __ add_w(sa[2], sa[2], t2); ++ __ add_w(sa[3], sa[3], t3); ++ __ ld_w(t0, state, 16); ++ __ ld_w(t1, state, 20); ++ __ ld_w(t2, state, 24); ++ __ ld_w(t3, state, 28); ++ __ add_w(sa[4], sa[4], t0); ++ __ add_w(sa[5], sa[5], t1); ++ __ add_w(sa[6], sa[6], t2); ++ __ add_w(sa[7], sa[7], t3); ++ __ st_w(sa[0], state, 0); ++ __ st_w(sa[1], state, 4); ++ __ st_w(sa[2], state, 8); ++ __ st_w(sa[3], state, 12); ++ __ st_w(sa[4], state, 16); ++ __ st_w(sa[5], state, 20); ++ __ st_w(sa[6], state, 24); ++ __ st_w(sa[7], state, 28); ++ ++ __ addi_w(ofs, ofs, 64); ++ __ addi_d(buf, buf, 64); ++ __ bge(limit, ofs, loop); ++ __ move(V0, ofs); // return ofs ++ ++ __ addi_d(SP, SP, 64); ++ __ jr(RA); ++ } ++ ++ // Do NOT delete this node which stands for stub routine placeholder ++ address generate_updateBytesCRC32() { ++ assert(UseCRC32Intrinsics, "need CRC32 instructions support"); ++ ++ __ align(CodeEntryAlignment); ++ StubCodeMark mark(this, "StubRoutines", "updateBytesCRC32"); ++ ++ address start = __ pc(); ++ ++ const Register crc = A0; // crc ++ const Register buf = A1; // source java byte array address ++ const Register len = A2; // length ++ const Register tmp = A3; ++ ++ __ enter(); // required for proper stackwalking of RuntimeStub frame ++ ++ __ kernel_crc32(crc, buf, len, tmp); ++ ++ __ leave(); // required for proper stackwalking of RuntimeStub frame ++ __ jr(RA); ++ ++ return start; ++ } ++ ++ // add a function to implement SafeFetch32 and SafeFetchN ++ void generate_safefetch(const char* name, int size, address* entry, ++ address* fault_pc, address* continuation_pc) { ++ // safefetch signatures: ++ // int SafeFetch32(int* adr, int errValue); ++ // intptr_t SafeFetchN (intptr_t* adr, intptr_t errValue); ++ // ++ // arguments: ++ // A0 = adr ++ // A1 = errValue ++ // ++ // result: ++ // PPC_RET = *adr or errValue ++ StubCodeMark mark(this, "StubRoutines", name); ++ ++ // Entry point, pc or function descriptor. ++ *entry = __ pc(); ++ ++ // Load *adr into A1, may fault. ++ *fault_pc = __ pc(); ++ switch (size) { ++ case 4: ++ // int32_t ++ __ ld_w(A1, A0, 0); ++ break; ++ case 8: ++ // int64_t ++ __ ld_d(A1, A0, 0); ++ break; ++ default: ++ ShouldNotReachHere(); ++ } ++ ++ // return errValue or *adr ++ *continuation_pc = __ pc(); ++ __ add_d(V0, A1, R0); ++ __ jr(RA); ++ } ++ ++ ++#undef __ ++#define __ masm-> ++ ++ // Continuation point for throwing of implicit exceptions that are ++ // not handled in the current activation. Fabricates an exception ++ // oop and initiates normal exception dispatching in this ++ // frame. Since we need to preserve callee-saved values (currently ++ // only for C2, but done for C1 as well) we need a callee-saved oop ++ // map and therefore have to make these stubs into RuntimeStubs ++ // rather than BufferBlobs. If the compiler needs all registers to ++ // be preserved between the fault point and the exception handler ++ // then it must assume responsibility for that in ++ // AbstractCompiler::continuation_for_implicit_null_exception or ++ // continuation_for_implicit_division_by_zero_exception. All other ++ // implicit exceptions (e.g., NullPointerException or ++ // AbstractMethodError on entry) are either at call sites or ++ // otherwise assume that stack unwinding will be initiated, so ++ // caller saved registers were assumed volatile in the compiler. ++ address generate_throw_exception(const char* name, ++ address runtime_entry, ++ bool restore_saved_exception_pc) { ++ // Information about frame layout at time of blocking runtime call. ++ // Note that we only have to preserve callee-saved registers since ++ // the compilers are responsible for supplying a continuation point ++ // if they expect all registers to be preserved. ++ enum layout { ++ thread_off, // last_java_sp ++ S7_off, // callee saved register sp + 1 ++ S6_off, // callee saved register sp + 2 ++ S5_off, // callee saved register sp + 3 ++ S4_off, // callee saved register sp + 4 ++ S3_off, // callee saved register sp + 5 ++ S2_off, // callee saved register sp + 6 ++ S1_off, // callee saved register sp + 7 ++ S0_off, // callee saved register sp + 8 ++ FP_off, ++ ret_address, ++ framesize ++ }; ++ ++ int insts_size = 2048; ++ int locs_size = 32; ++ ++ // CodeBuffer* code = new CodeBuffer(insts_size, locs_size, 0, 0, 0, false, ++ // NULL, NULL, NULL, false, NULL, name, false); ++ CodeBuffer code (name , insts_size, locs_size); ++ OopMapSet* oop_maps = new OopMapSet(); ++ MacroAssembler* masm = new MacroAssembler(&code); ++ ++ address start = __ pc(); ++ ++ // This is an inlined and slightly modified version of call_VM ++ // which has the ability to fetch the return PC out of ++ // thread-local storage and also sets up last_Java_sp slightly ++ // differently than the real call_VM ++#ifndef OPT_THREAD ++ Register java_thread = TREG; ++ __ get_thread(java_thread); ++#else ++ Register java_thread = TREG; ++#endif ++ if (restore_saved_exception_pc) { ++ __ ld_d(RA, java_thread, in_bytes(JavaThread::saved_exception_pc_offset())); ++ } ++ __ enter(); // required for proper stackwalking of RuntimeStub frame ++ ++ __ addi_d(SP, SP, (-1) * (framesize-2) * wordSize); // prolog ++ __ st_d(S0, SP, S0_off * wordSize); ++ __ st_d(S1, SP, S1_off * wordSize); ++ __ st_d(S2, SP, S2_off * wordSize); ++ __ st_d(S3, SP, S3_off * wordSize); ++ __ st_d(S4, SP, S4_off * wordSize); ++ __ st_d(S5, SP, S5_off * wordSize); ++ __ st_d(S6, SP, S6_off * wordSize); ++ __ st_d(S7, SP, S7_off * wordSize); ++ ++ int frame_complete = __ pc() - start; ++ // push java thread (becomes first argument of C function) ++ __ st_d(java_thread, SP, thread_off * wordSize); ++ if (java_thread != A0) ++ __ move(A0, java_thread); ++ ++ // Set up last_Java_sp and last_Java_fp ++ Label before_call; ++ address the_pc = __ pc(); ++ __ bind(before_call); ++ __ set_last_Java_frame(java_thread, SP, FP, before_call); ++ // Align stack ++ __ li(AT, -(StackAlignmentInBytes)); ++ __ andr(SP, SP, AT); ++ ++ // Call runtime ++ // TODO: confirm reloc ++ __ call(runtime_entry, relocInfo::runtime_call_type); ++ // Generate oop map ++ OopMap* map = new OopMap(framesize, 0); ++ oop_maps->add_gc_map(the_pc - start, map); ++ ++ // restore the thread (cannot use the pushed argument since arguments ++ // may be overwritten by C code generated by an optimizing compiler); ++ // however can use the register value directly if it is callee saved. ++#ifndef OPT_THREAD ++ __ get_thread(java_thread); ++#endif ++ ++ __ ld_d(SP, java_thread, in_bytes(JavaThread::last_Java_sp_offset())); ++ __ reset_last_Java_frame(java_thread, true); ++ ++ // Restore callee save registers. This must be done after resetting the Java frame ++ __ ld_d(S0, SP, S0_off * wordSize); ++ __ ld_d(S1, SP, S1_off * wordSize); ++ __ ld_d(S2, SP, S2_off * wordSize); ++ __ ld_d(S3, SP, S3_off * wordSize); ++ __ ld_d(S4, SP, S4_off * wordSize); ++ __ ld_d(S5, SP, S5_off * wordSize); ++ __ ld_d(S6, SP, S6_off * wordSize); ++ __ ld_d(S7, SP, S7_off * wordSize); ++ ++ // discard arguments ++ __ move(SP, FP); // epilog ++ __ pop(FP); ++ // check for pending exceptions ++#ifdef ASSERT ++ Label L; ++ __ ld_d(AT, java_thread, in_bytes(Thread::pending_exception_offset())); ++ __ bne(AT, R0, L); ++ __ should_not_reach_here(); ++ __ bind(L); ++#endif //ASSERT ++ __ jmp(StubRoutines::forward_exception_entry(), relocInfo::runtime_call_type); ++ ++ RuntimeStub* stub = RuntimeStub::new_runtime_stub(name, ++ &code, ++ frame_complete, ++ framesize, ++ oop_maps, false); ++ return stub->entry_point(); ++ } ++ ++ class MontgomeryMultiplyGenerator : public MacroAssembler { ++ ++ Register Pa_base, Pb_base, Pn_base, Pm_base, inv, Rlen, Rlen2, Ra, Rb, Rm, ++ Rn, Iam, Ibn, Rhi_ab, Rlo_ab, Rhi_mn, Rlo_mn, t0, t1, t2, Ri, Rj; ++ ++ bool _squaring; ++ ++ public: ++ MontgomeryMultiplyGenerator (Assembler *as, bool squaring) ++ : MacroAssembler(as->code()), _squaring(squaring) { ++ ++ // Register allocation ++ ++ Register reg = A0; ++ Pa_base = reg; // Argument registers: ++ if (squaring) ++ Pb_base = Pa_base; ++ else ++ Pb_base = ++reg; ++ Pn_base = ++reg; ++ Rlen = ++reg; ++ inv = ++reg; ++ Rlen2 = inv; // Reuse inv ++ Pm_base = ++reg; ++ ++ // Working registers: ++ Ra = ++reg; // The current digit of a, b, n, and m. ++ Rb = ++reg; ++ Rm = ++reg; ++ Rn = ++reg; ++ ++ Iam = ++reg; // Index to the current/next digit of a, b, n, and m. ++ Ibn = ++reg; ++ ++ t0 = ++reg; // Three registers which form a ++ t1 = ++reg; // triple-precision accumuator. ++ t2 = ++reg; ++ ++ Ri = ++reg; // Inner and outer loop indexes. ++ Rj = ++reg; ++ ++ if (squaring) { ++ Rhi_ab = ++reg; // Product registers: low and high parts ++ reg = S0; ++ Rlo_ab = ++reg; // of a*b and m*n. ++ } else { ++ reg = S0; ++ Rhi_ab = reg; // Product registers: low and high parts ++ Rlo_ab = ++reg; // of a*b and m*n. ++ } ++ ++ Rhi_mn = ++reg; ++ Rlo_mn = ++reg; ++ } ++ ++ private: ++ void enter() { ++ addi_d(SP, SP, -6 * wordSize); ++ st_d(FP, SP, 0 * wordSize); ++ move(FP, SP); ++ } ++ ++ void leave() { ++ addi_d(T0, FP, 6 * wordSize); ++ ld_d(FP, FP, 0 * wordSize); ++ move(SP, T0); ++ } ++ ++ void save_regs() { ++ if (!_squaring) ++ st_d(Rhi_ab, FP, 5 * wordSize); ++ st_d(Rlo_ab, FP, 4 * wordSize); ++ st_d(Rhi_mn, FP, 3 * wordSize); ++ st_d(Rlo_mn, FP, 2 * wordSize); ++ st_d(Pm_base, FP, 1 * wordSize); ++ } ++ ++ void restore_regs() { ++ if (!_squaring) ++ ld_d(Rhi_ab, FP, 5 * wordSize); ++ ld_d(Rlo_ab, FP, 4 * wordSize); ++ ld_d(Rhi_mn, FP, 3 * wordSize); ++ ld_d(Rlo_mn, FP, 2 * wordSize); ++ ld_d(Pm_base, FP, 1 * wordSize); ++ } ++ ++ template ++ void unroll_2(Register count, T block, Register tmp) { ++ Label loop, end, odd; ++ andi(tmp, count, 1); ++ bnez(tmp, odd); ++ beqz(count, end); ++ align(16); ++ bind(loop); ++ (this->*block)(); ++ bind(odd); ++ (this->*block)(); ++ addi_w(count, count, -2); ++ blt(R0, count, loop); ++ bind(end); ++ } ++ ++ template ++ void unroll_2(Register count, T block, Register d, Register s, Register tmp) { ++ Label loop, end, odd; ++ andi(tmp, count, 1); ++ bnez(tmp, odd); ++ beqz(count, end); ++ align(16); ++ bind(loop); ++ (this->*block)(d, s, tmp); ++ bind(odd); ++ (this->*block)(d, s, tmp); ++ addi_w(count, count, -2); ++ blt(R0, count, loop); ++ bind(end); ++ } ++ ++ void acc(Register Rhi, Register Rlo, ++ Register t0, Register t1, Register t2, Register t, Register c) { ++ add_d(t0, t0, Rlo); ++ OR(t, t1, Rhi); ++ sltu(c, t0, Rlo); ++ add_d(t1, t1, Rhi); ++ add_d(t1, t1, c); ++ sltu(c, t1, t); ++ add_d(t2, t2, c); ++ } ++ ++ void pre1(Register i) { ++ block_comment("pre1"); ++ // Iam = 0; ++ // Ibn = i; ++ ++ slli_w(Ibn, i, LogBytesPerWord); ++ ++ // Ra = Pa_base[Iam]; ++ // Rb = Pb_base[Ibn]; ++ // Rm = Pm_base[Iam]; ++ // Rn = Pn_base[Ibn]; ++ ++ ld_d(Ra, Pa_base, 0); ++ ldx_d(Rb, Pb_base, Ibn); ++ ld_d(Rm, Pm_base, 0); ++ ldx_d(Rn, Pn_base, Ibn); ++ ++ move(Iam, R0); ++ ++ // Zero the m*n result. ++ move(Rhi_mn, R0); ++ move(Rlo_mn, R0); ++ } ++ ++ // The core multiply-accumulate step of a Montgomery ++ // multiplication. The idea is to schedule operations as a ++ // pipeline so that instructions with long latencies (loads and ++ // multiplies) have time to complete before their results are ++ // used. This most benefits in-order implementations of the ++ // architecture but out-of-order ones also benefit. ++ void step() { ++ block_comment("step"); ++ // MACC(Ra, Rb, t0, t1, t2); ++ // Ra = Pa_base[++Iam]; ++ // Rb = Pb_base[--Ibn]; ++ addi_d(Iam, Iam, wordSize); ++ addi_d(Ibn, Ibn, -wordSize); ++ mul_d(Rlo_ab, Ra, Rb); ++ mulh_du(Rhi_ab, Ra, Rb); ++ acc(Rhi_mn, Rlo_mn, t0, t1, t2, Ra, Rb); // The pending m*n from the ++ // previous iteration. ++ ldx_d(Ra, Pa_base, Iam); ++ ldx_d(Rb, Pb_base, Ibn); ++ ++ // MACC(Rm, Rn, t0, t1, t2); ++ // Rm = Pm_base[Iam]; ++ // Rn = Pn_base[Ibn]; ++ mul_d(Rlo_mn, Rm, Rn); ++ mulh_du(Rhi_mn, Rm, Rn); ++ acc(Rhi_ab, Rlo_ab, t0, t1, t2, Rm, Rn); ++ ldx_d(Rm, Pm_base, Iam); ++ ldx_d(Rn, Pn_base, Ibn); ++ } ++ ++ void post1() { ++ block_comment("post1"); ++ ++ // MACC(Ra, Rb, t0, t1, t2); ++ mul_d(Rlo_ab, Ra, Rb); ++ mulh_du(Rhi_ab, Ra, Rb); ++ acc(Rhi_mn, Rlo_mn, t0, t1, t2, Ra, Rb); // The pending m*n ++ acc(Rhi_ab, Rlo_ab, t0, t1, t2, Ra, Rb); ++ ++ // Pm_base[Iam] = Rm = t0 * inv; ++ mul_d(Rm, t0, inv); ++ stx_d(Rm, Pm_base, Iam); ++ ++ // MACC(Rm, Rn, t0, t1, t2); ++ // t0 = t1; t1 = t2; t2 = 0; ++ mulh_du(Rhi_mn, Rm, Rn); ++ ++#ifndef PRODUCT ++ // assert(m[i] * n[0] + t0 == 0, "broken Montgomery multiply"); ++ { ++ mul_d(Rlo_mn, Rm, Rn); ++ add_d(Rlo_mn, t0, Rlo_mn); ++ Label ok; ++ beqz(Rlo_mn, ok); { ++ stop("broken Montgomery multiply"); ++ } bind(ok); ++ } ++#endif ++ ++ // We have very carefully set things up so that ++ // m[i]*n[0] + t0 == 0 (mod b), so we don't have to calculate ++ // the lower half of Rm * Rn because we know the result already: ++ // it must be -t0. t0 + (-t0) must generate a carry iff ++ // t0 != 0. So, rather than do a mul and an adds we just set ++ // the carry flag iff t0 is nonzero. ++ // ++ // mul_d(Rlo_mn, Rm, Rn); ++ // add_d(t0, t0, Rlo_mn); ++ OR(Ra, t1, Rhi_mn); ++ sltu(Rb, R0, t0); ++ add_d(t0, t1, Rhi_mn); ++ add_d(t0, t0, Rb); ++ sltu(Rb, t0, Ra); ++ add_d(t1, t2, Rb); ++ move(t2, R0); ++ } ++ ++ void pre2(Register i, Register len) { ++ block_comment("pre2"); ++ ++ // Rj == i-len ++ sub_w(Rj, i, len); ++ ++ // Iam = i - len; ++ // Ibn = len; ++ slli_w(Iam, Rj, LogBytesPerWord); ++ slli_w(Ibn, len, LogBytesPerWord); ++ ++ // Ra = Pa_base[++Iam]; ++ // Rb = Pb_base[--Ibn]; ++ // Rm = Pm_base[++Iam]; ++ // Rn = Pn_base[--Ibn]; ++ addi_d(Iam, Iam, wordSize); ++ addi_d(Ibn, Ibn, -wordSize); ++ ++ ldx_d(Ra, Pa_base, Iam); ++ ldx_d(Rb, Pb_base, Ibn); ++ ldx_d(Rm, Pm_base, Iam); ++ ldx_d(Rn, Pn_base, Ibn); ++ ++ move(Rhi_mn, R0); ++ move(Rlo_mn, R0); ++ } ++ ++ void post2(Register i, Register len) { ++ block_comment("post2"); ++ ++ sub_w(Rj, i, len); ++ slli_w(Iam, Rj, LogBytesPerWord); ++ ++ add_d(t0, t0, Rlo_mn); // The pending m*n, low part ++ ++ // As soon as we know the least significant digit of our result, ++ // store it. ++ // Pm_base[i-len] = t0; ++ stx_d(t0, Pm_base, Iam); ++ ++ // t0 = t1; t1 = t2; t2 = 0; ++ OR(Ra, t1, Rhi_mn); ++ sltu(Rb, t0, Rlo_mn); ++ add_d(t0, t1, Rhi_mn); // The pending m*n, high part ++ add_d(t0, t0, Rb); ++ sltu(Rb, t0, Ra); ++ add_d(t1, t2, Rb); ++ move(t2, R0); ++ } ++ ++ // A carry in t0 after Montgomery multiplication means that we ++ // should subtract multiples of n from our result in m. We'll ++ // keep doing that until there is no carry. ++ void normalize(Register len) { ++ block_comment("normalize"); ++ // while (t0) ++ // t0 = sub(Pm_base, Pn_base, t0, len); ++ Label loop, post, again; ++ Register cnt = t1, i = t2, b = Ra, t = Rb; // Re-use registers; we're done with them now ++ beqz(t0, post); { ++ bind(again); { ++ move(i, R0); ++ move(b, R0); ++ slli_w(cnt, len, LogBytesPerWord); ++ align(16); ++ bind(loop); { ++ ldx_d(Rm, Pm_base, i); ++ ldx_d(Rn, Pn_base, i); ++ sltu(t, Rm, b); ++ sub_d(Rm, Rm, b); ++ sltu(b, Rm, Rn); ++ sub_d(Rm, Rm, Rn); ++ OR(b, b, t); ++ stx_d(Rm, Pm_base, i); ++ addi_w(i, i, BytesPerWord); ++ } blt(i, cnt, loop); ++ sub_d(t0, t0, b); ++ } bnez(t0, again); ++ } bind(post); ++ } ++ ++ // Move memory at s to d, reversing words. ++ // Increments d to end of copied memory ++ // Destroys tmp1, tmp2, tmp3 ++ // Preserves len ++ // Leaves s pointing to the address which was in d at start ++ void reverse(Register d, Register s, Register len, Register tmp1, Register tmp2) { ++ assert(tmp1 < S0 && tmp2 < S0, "register corruption"); ++ ++ alsl_d(s, len, s, LogBytesPerWord - 1); ++ move(tmp1, len); ++ unroll_2(tmp1, &MontgomeryMultiplyGenerator::reverse1, d, s, tmp2); ++ slli_w(s, len, LogBytesPerWord); ++ sub_d(s, d, s); ++ } ++ ++ // where ++ void reverse1(Register d, Register s, Register tmp) { ++ ld_d(tmp, s, -wordSize); ++ addi_d(s, s, -wordSize); ++ addi_d(d, d, wordSize); ++ rotri_d(tmp, tmp, 32); ++ st_d(tmp, d, -wordSize); ++ } ++ ++ public: ++ /** ++ * Fast Montgomery multiplication. The derivation of the ++ * algorithm is in A Cryptographic Library for the Motorola ++ * DSP56000, Dusse and Kaliski, Proc. EUROCRYPT 90, pp. 230-237. ++ * ++ * Arguments: ++ * ++ * Inputs for multiplication: ++ * A0 - int array elements a ++ * A1 - int array elements b ++ * A2 - int array elements n (the modulus) ++ * A3 - int length ++ * A4 - int inv ++ * A5 - int array elements m (the result) ++ * ++ * Inputs for squaring: ++ * A0 - int array elements a ++ * A1 - int array elements n (the modulus) ++ * A2 - int length ++ * A3 - int inv ++ * A4 - int array elements m (the result) ++ * ++ */ ++ address generate_multiply() { ++ Label argh, nothing; ++ bind(argh); ++ stop("MontgomeryMultiply total_allocation must be <= 8192"); ++ ++ align(CodeEntryAlignment); ++ address entry = pc(); ++ ++ beqz(Rlen, nothing); ++ ++ enter(); ++ ++ // Make room. ++ sltui(Ra, Rlen, 513); ++ beqz(Ra, argh); ++ slli_w(Ra, Rlen, exact_log2(4 * sizeof (jint))); ++ sub_d(Ra, SP, Ra); ++ ++ srli_w(Rlen, Rlen, 1); // length in longwords = len/2 ++ ++ { ++ // Copy input args, reversing as we go. We use Ra as a ++ // temporary variable. ++ reverse(Ra, Pa_base, Rlen, t0, t1); ++ if (!_squaring) ++ reverse(Ra, Pb_base, Rlen, t0, t1); ++ reverse(Ra, Pn_base, Rlen, t0, t1); ++ } ++ ++ // Push all call-saved registers and also Pm_base which we'll need ++ // at the end. ++ save_regs(); ++ ++#ifndef PRODUCT ++ // assert(inv * n[0] == -1UL, "broken inverse in Montgomery multiply"); ++ { ++ ld_d(Rn, Pn_base, 0); ++ li(t0, -1); ++ mul_d(Rlo_mn, Rn, inv); ++ Label ok; ++ beq(Rlo_mn, t0, ok); { ++ stop("broken inverse in Montgomery multiply"); ++ } bind(ok); ++ } ++#endif ++ ++ move(Pm_base, Ra); ++ ++ move(t0, R0); ++ move(t1, R0); ++ move(t2, R0); ++ ++ block_comment("for (int i = 0; i < len; i++) {"); ++ move(Ri, R0); { ++ Label loop, end; ++ bge(Ri, Rlen, end); ++ ++ bind(loop); ++ pre1(Ri); ++ ++ block_comment(" for (j = i; j; j--) {"); { ++ move(Rj, Ri); ++ unroll_2(Rj, &MontgomeryMultiplyGenerator::step, Rlo_ab); ++ } block_comment(" } // j"); ++ ++ post1(); ++ addi_w(Ri, Ri, 1); ++ blt(Ri, Rlen, loop); ++ bind(end); ++ block_comment("} // i"); ++ } ++ ++ block_comment("for (int i = len; i < 2*len; i++) {"); ++ move(Ri, Rlen); ++ slli_w(Rlen2, Rlen, 1); { ++ Label loop, end; ++ bge(Ri, Rlen2, end); ++ ++ bind(loop); ++ pre2(Ri, Rlen); ++ ++ block_comment(" for (j = len*2-i-1; j; j--) {"); { ++ sub_w(Rj, Rlen2, Ri); ++ addi_w(Rj, Rj, -1); ++ unroll_2(Rj, &MontgomeryMultiplyGenerator::step, Rlo_ab); ++ } block_comment(" } // j"); ++ ++ post2(Ri, Rlen); ++ addi_w(Ri, Ri, 1); ++ blt(Ri, Rlen2, loop); ++ bind(end); ++ } ++ block_comment("} // i"); ++ ++ normalize(Rlen); ++ ++ move(Ra, Pm_base); // Save Pm_base in Ra ++ restore_regs(); // Restore caller's Pm_base ++ ++ // Copy our result into caller's Pm_base ++ reverse(Pm_base, Ra, Rlen, t0, t1); ++ ++ leave(); ++ bind(nothing); ++ jr(RA); ++ ++ return entry; ++ } ++ // In C, approximately: ++ ++ // void ++ // montgomery_multiply(unsigned long Pa_base[], unsigned long Pb_base[], ++ // unsigned long Pn_base[], unsigned long Pm_base[], ++ // unsigned long inv, int len) { ++ // unsigned long t0 = 0, t1 = 0, t2 = 0; // Triple-precision accumulator ++ // unsigned long Ra, Rb, Rn, Rm; ++ // int i, Iam, Ibn; ++ ++ // assert(inv * Pn_base[0] == -1UL, "broken inverse in Montgomery multiply"); ++ ++ // for (i = 0; i < len; i++) { ++ // int j; ++ ++ // Iam = 0; ++ // Ibn = i; ++ ++ // Ra = Pa_base[Iam]; ++ // Rb = Pb_base[Iam]; ++ // Rm = Pm_base[Ibn]; ++ // Rn = Pn_base[Ibn]; ++ ++ // int iters = i; ++ // for (j = 0; iters--; j++) { ++ // assert(Ra == Pa_base[j] && Rb == Pb_base[i-j], "must be"); ++ // MACC(Ra, Rb, t0, t1, t2); ++ // Ra = Pa_base[++Iam]; ++ // Rb = pb_base[--Ibn]; ++ // assert(Rm == Pm_base[j] && Rn == Pn_base[i-j], "must be"); ++ // MACC(Rm, Rn, t0, t1, t2); ++ // Rm = Pm_base[++Iam]; ++ // Rn = Pn_base[--Ibn]; ++ // } ++ ++ // assert(Ra == Pa_base[i] && Rb == Pb_base[0], "must be"); ++ // MACC(Ra, Rb, t0, t1, t2); ++ // Pm_base[Iam] = Rm = t0 * inv; ++ // assert(Rm == Pm_base[i] && Rn == Pn_base[0], "must be"); ++ // MACC(Rm, Rn, t0, t1, t2); ++ ++ // assert(t0 == 0, "broken Montgomery multiply"); ++ ++ // t0 = t1; t1 = t2; t2 = 0; ++ // } ++ ++ // for (i = len; i < 2*len; i++) { ++ // int j; ++ ++ // Iam = i - len; ++ // Ibn = len; ++ ++ // Ra = Pa_base[++Iam]; ++ // Rb = Pb_base[--Ibn]; ++ // Rm = Pm_base[++Iam]; ++ // Rn = Pn_base[--Ibn]; ++ ++ // int iters = len*2-i-1; ++ // for (j = i-len+1; iters--; j++) { ++ // assert(Ra == Pa_base[j] && Rb == Pb_base[i-j], "must be"); ++ // MACC(Ra, Rb, t0, t1, t2); ++ // Ra = Pa_base[++Iam]; ++ // Rb = Pb_base[--Ibn]; ++ // assert(Rm == Pm_base[j] && Rn == Pn_base[i-j], "must be"); ++ // MACC(Rm, Rn, t0, t1, t2); ++ // Rm = Pm_base[++Iam]; ++ // Rn = Pn_base[--Ibn]; ++ // } ++ ++ // Pm_base[i-len] = t0; ++ // t0 = t1; t1 = t2; t2 = 0; ++ // } ++ ++ // while (t0) ++ // t0 = sub(Pm_base, Pn_base, t0, len); ++ // } ++ }; ++ ++ // Initialization ++ void generate_initial() { ++ // Generates all stubs and initializes the entry points ++ ++ //------------------------------------------------------------- ++ //----------------------------------------------------------- ++ // entry points that exist in all platforms ++ // Note: This is code that could be shared among different platforms - however the benefit seems to be smaller ++ // than the disadvantage of having a much more complicated generator structure. ++ // See also comment in stubRoutines.hpp. ++ StubRoutines::_forward_exception_entry = generate_forward_exception(); ++ StubRoutines::_call_stub_entry = generate_call_stub(StubRoutines::_call_stub_return_address); ++ // is referenced by megamorphic call ++ StubRoutines::_catch_exception_entry = generate_catch_exception(); ++ ++ StubRoutines::_handler_for_unsafe_access_entry = generate_handler_for_unsafe_access(); ++ ++ StubRoutines::_throw_StackOverflowError_entry = generate_throw_exception("StackOverflowError throw_exception", ++ CAST_FROM_FN_PTR(address, SharedRuntime::throw_StackOverflowError), false); ++ } ++ ++ void generate_all() { ++ // Generates all stubs and initializes the entry points ++ ++ // These entry points require SharedInfo::stack0 to be set up in ++ // non-core builds and need to be relocatable, so they each ++ // fabricate a RuntimeStub internally. ++ StubRoutines::_throw_AbstractMethodError_entry = generate_throw_exception("AbstractMethodError throw_exception", ++ CAST_FROM_FN_PTR(address, SharedRuntime::throw_AbstractMethodError), false); ++ ++ StubRoutines::_throw_IncompatibleClassChangeError_entry = generate_throw_exception("IncompatibleClassChangeError throw_exception", ++ CAST_FROM_FN_PTR(address, SharedRuntime:: throw_IncompatibleClassChangeError), false); ++ ++ StubRoutines::_throw_NullPointerException_at_call_entry = generate_throw_exception("NullPointerException at call throw_exception", ++ CAST_FROM_FN_PTR(address, SharedRuntime::throw_NullPointerException_at_call), false); ++ ++ // entry points that are platform specific ++ ++ // support for verify_oop (must happen after universe_init) ++ StubRoutines::_verify_oop_subroutine_entry = generate_verify_oop(); ++#ifndef CORE ++ // arraycopy stubs used by compilers ++ generate_arraycopy_stubs(); ++#endif ++ ++ // Safefetch stubs. ++ generate_safefetch("SafeFetch32", sizeof(int), &StubRoutines::_safefetch32_entry, ++ &StubRoutines::_safefetch32_fault_pc, ++ &StubRoutines::_safefetch32_continuation_pc); ++ generate_safefetch("SafeFetchN", sizeof(intptr_t), &StubRoutines::_safefetchN_entry, ++ &StubRoutines::_safefetchN_fault_pc, ++ &StubRoutines::_safefetchN_continuation_pc); ++ ++ if (UseMontgomeryMultiplyIntrinsic) { ++ StubCodeMark mark(this, "StubRoutines", "montgomeryMultiply"); ++ MontgomeryMultiplyGenerator g(_masm, false /* squaring */); ++ StubRoutines::_montgomeryMultiply = g.generate_multiply(); ++ } ++ ++ if (UseMontgomerySquareIntrinsic) { ++ StubCodeMark mark(this, "StubRoutines", "montgomerySquare"); ++ MontgomeryMultiplyGenerator g(_masm, true /* squaring */); ++ // We use generate_multiply() rather than generate_square() ++ // because it's faster for the sizes of modulus we care about. ++ StubRoutines::_montgomerySquare = g.generate_multiply(); ++ } ++ ++ if (UseAESIntrinsics) { ++ StubRoutines::_aescrypt_encryptBlock = generate_aescrypt_encryptBlock(false); ++ StubRoutines::_aescrypt_decryptBlock = generate_aescrypt_decryptBlock(false); ++ StubRoutines::_cipherBlockChaining_encryptAESCrypt = generate_aescrypt_encryptBlock(true); ++ StubRoutines::_cipherBlockChaining_decryptAESCrypt = generate_aescrypt_decryptBlock(true); ++ } ++ ++ if (UseSHA1Intrinsics) { ++ generate_sha1_implCompress("sha1_implCompress", StubRoutines::_sha1_implCompress, StubRoutines::_sha1_implCompressMB); ++ } ++ ++ if (UseSHA256Intrinsics) { ++ generate_sha256_implCompress("sha256_implCompress", StubRoutines::_sha256_implCompress, StubRoutines::_sha256_implCompressMB); ++ } ++ ++ if (UseCRC32Intrinsics) { ++ // set table address before stub generation which use it ++ StubRoutines::_crc_table_adr = (address)StubRoutines::la::_crc_table; ++ StubRoutines::_updateBytesCRC32 = generate_updateBytesCRC32(); ++ } ++ } ++ ++ public: ++ StubGenerator(CodeBuffer* code, bool all) : StubCodeGenerator(code) { ++ if (all) { ++ generate_all(); ++ } else { ++ generate_initial(); ++ } ++ } ++}; // end class declaration ++ ++void StubGenerator_generate(CodeBuffer* code, bool all) { ++ StubGenerator g(code, all); ++} +diff --git a/hotspot/src/cpu/loongarch/vm/stubRoutines_loongarch_64.cpp b/hotspot/src/cpu/loongarch/vm/stubRoutines_loongarch_64.cpp +new file mode 100644 +index 0000000000..f0f3d55a4e +--- /dev/null ++++ b/hotspot/src/cpu/loongarch/vm/stubRoutines_loongarch_64.cpp +@@ -0,0 +1,264 @@ ++/* ++ * Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2015, 2020, Loongson Technology. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ * ++ */ ++ ++#include "precompiled.hpp" ++#include "runtime/deoptimization.hpp" ++#include "runtime/frame.inline.hpp" ++#include "runtime/stubRoutines.hpp" ++#include "runtime/thread.inline.hpp" ++ ++// a description of how to extend it, see the stubRoutines.hpp file. ++ ++//find the last fp value ++address StubRoutines::la::_call_stub_compiled_return = NULL; ++ ++/** ++ * crc_table[] from jdk/src/share/native/java/util/zip/zlib-1.2.5/crc32.h ++ */ ++juint StubRoutines::la::_crc_table[] = ++{ ++ // Table 0 ++ 0x00000000UL, 0x77073096UL, 0xee0e612cUL, 0x990951baUL, 0x076dc419UL, ++ 0x706af48fUL, 0xe963a535UL, 0x9e6495a3UL, 0x0edb8832UL, 0x79dcb8a4UL, ++ 0xe0d5e91eUL, 0x97d2d988UL, 0x09b64c2bUL, 0x7eb17cbdUL, 0xe7b82d07UL, ++ 0x90bf1d91UL, 0x1db71064UL, 0x6ab020f2UL, 0xf3b97148UL, 0x84be41deUL, ++ 0x1adad47dUL, 0x6ddde4ebUL, 0xf4d4b551UL, 0x83d385c7UL, 0x136c9856UL, ++ 0x646ba8c0UL, 0xfd62f97aUL, 0x8a65c9ecUL, 0x14015c4fUL, 0x63066cd9UL, ++ 0xfa0f3d63UL, 0x8d080df5UL, 0x3b6e20c8UL, 0x4c69105eUL, 0xd56041e4UL, ++ 0xa2677172UL, 0x3c03e4d1UL, 0x4b04d447UL, 0xd20d85fdUL, 0xa50ab56bUL, ++ 0x35b5a8faUL, 0x42b2986cUL, 0xdbbbc9d6UL, 0xacbcf940UL, 0x32d86ce3UL, ++ 0x45df5c75UL, 0xdcd60dcfUL, 0xabd13d59UL, 0x26d930acUL, 0x51de003aUL, ++ 0xc8d75180UL, 0xbfd06116UL, 0x21b4f4b5UL, 0x56b3c423UL, 0xcfba9599UL, ++ 0xb8bda50fUL, 0x2802b89eUL, 0x5f058808UL, 0xc60cd9b2UL, 0xb10be924UL, ++ 0x2f6f7c87UL, 0x58684c11UL, 0xc1611dabUL, 0xb6662d3dUL, 0x76dc4190UL, ++ 0x01db7106UL, 0x98d220bcUL, 0xefd5102aUL, 0x71b18589UL, 0x06b6b51fUL, ++ 0x9fbfe4a5UL, 0xe8b8d433UL, 0x7807c9a2UL, 0x0f00f934UL, 0x9609a88eUL, ++ 0xe10e9818UL, 0x7f6a0dbbUL, 0x086d3d2dUL, 0x91646c97UL, 0xe6635c01UL, ++ 0x6b6b51f4UL, 0x1c6c6162UL, 0x856530d8UL, 0xf262004eUL, 0x6c0695edUL, ++ 0x1b01a57bUL, 0x8208f4c1UL, 0xf50fc457UL, 0x65b0d9c6UL, 0x12b7e950UL, ++ 0x8bbeb8eaUL, 0xfcb9887cUL, 0x62dd1ddfUL, 0x15da2d49UL, 0x8cd37cf3UL, ++ 0xfbd44c65UL, 0x4db26158UL, 0x3ab551ceUL, 0xa3bc0074UL, 0xd4bb30e2UL, ++ 0x4adfa541UL, 0x3dd895d7UL, 0xa4d1c46dUL, 0xd3d6f4fbUL, 0x4369e96aUL, ++ 0x346ed9fcUL, 0xad678846UL, 0xda60b8d0UL, 0x44042d73UL, 0x33031de5UL, ++ 0xaa0a4c5fUL, 0xdd0d7cc9UL, 0x5005713cUL, 0x270241aaUL, 0xbe0b1010UL, ++ 0xc90c2086UL, 0x5768b525UL, 0x206f85b3UL, 0xb966d409UL, 0xce61e49fUL, ++ 0x5edef90eUL, 0x29d9c998UL, 0xb0d09822UL, 0xc7d7a8b4UL, 0x59b33d17UL, ++ 0x2eb40d81UL, 0xb7bd5c3bUL, 0xc0ba6cadUL, 0xedb88320UL, 0x9abfb3b6UL, ++ 0x03b6e20cUL, 0x74b1d29aUL, 0xead54739UL, 0x9dd277afUL, 0x04db2615UL, ++ 0x73dc1683UL, 0xe3630b12UL, 0x94643b84UL, 0x0d6d6a3eUL, 0x7a6a5aa8UL, ++ 0xe40ecf0bUL, 0x9309ff9dUL, 0x0a00ae27UL, 0x7d079eb1UL, 0xf00f9344UL, ++ 0x8708a3d2UL, 0x1e01f268UL, 0x6906c2feUL, 0xf762575dUL, 0x806567cbUL, ++ 0x196c3671UL, 0x6e6b06e7UL, 0xfed41b76UL, 0x89d32be0UL, 0x10da7a5aUL, ++ 0x67dd4accUL, 0xf9b9df6fUL, 0x8ebeeff9UL, 0x17b7be43UL, 0x60b08ed5UL, ++ 0xd6d6a3e8UL, 0xa1d1937eUL, 0x38d8c2c4UL, 0x4fdff252UL, 0xd1bb67f1UL, ++ 0xa6bc5767UL, 0x3fb506ddUL, 0x48b2364bUL, 0xd80d2bdaUL, 0xaf0a1b4cUL, ++ 0x36034af6UL, 0x41047a60UL, 0xdf60efc3UL, 0xa867df55UL, 0x316e8eefUL, ++ 0x4669be79UL, 0xcb61b38cUL, 0xbc66831aUL, 0x256fd2a0UL, 0x5268e236UL, ++ 0xcc0c7795UL, 0xbb0b4703UL, 0x220216b9UL, 0x5505262fUL, 0xc5ba3bbeUL, ++ 0xb2bd0b28UL, 0x2bb45a92UL, 0x5cb36a04UL, 0xc2d7ffa7UL, 0xb5d0cf31UL, ++ 0x2cd99e8bUL, 0x5bdeae1dUL, 0x9b64c2b0UL, 0xec63f226UL, 0x756aa39cUL, ++ 0x026d930aUL, 0x9c0906a9UL, 0xeb0e363fUL, 0x72076785UL, 0x05005713UL, ++ 0x95bf4a82UL, 0xe2b87a14UL, 0x7bb12baeUL, 0x0cb61b38UL, 0x92d28e9bUL, ++ 0xe5d5be0dUL, 0x7cdcefb7UL, 0x0bdbdf21UL, 0x86d3d2d4UL, 0xf1d4e242UL, ++ 0x68ddb3f8UL, 0x1fda836eUL, 0x81be16cdUL, 0xf6b9265bUL, 0x6fb077e1UL, ++ 0x18b74777UL, 0x88085ae6UL, 0xff0f6a70UL, 0x66063bcaUL, 0x11010b5cUL, ++ 0x8f659effUL, 0xf862ae69UL, 0x616bffd3UL, 0x166ccf45UL, 0xa00ae278UL, ++ 0xd70dd2eeUL, 0x4e048354UL, 0x3903b3c2UL, 0xa7672661UL, 0xd06016f7UL, ++ 0x4969474dUL, 0x3e6e77dbUL, 0xaed16a4aUL, 0xd9d65adcUL, 0x40df0b66UL, ++ 0x37d83bf0UL, 0xa9bcae53UL, 0xdebb9ec5UL, 0x47b2cf7fUL, 0x30b5ffe9UL, ++ 0xbdbdf21cUL, 0xcabac28aUL, 0x53b39330UL, 0x24b4a3a6UL, 0xbad03605UL, ++ 0xcdd70693UL, 0x54de5729UL, 0x23d967bfUL, 0xb3667a2eUL, 0xc4614ab8UL, ++ 0x5d681b02UL, 0x2a6f2b94UL, 0xb40bbe37UL, 0xc30c8ea1UL, 0x5a05df1bUL, ++ 0x2d02ef8dUL, ++ ++ // Table 1 ++ 0x00000000UL, 0x191b3141UL, 0x32366282UL, 0x2b2d53c3UL, 0x646cc504UL, ++ 0x7d77f445UL, 0x565aa786UL, 0x4f4196c7UL, 0xc8d98a08UL, 0xd1c2bb49UL, ++ 0xfaefe88aUL, 0xe3f4d9cbUL, 0xacb54f0cUL, 0xb5ae7e4dUL, 0x9e832d8eUL, ++ 0x87981ccfUL, 0x4ac21251UL, 0x53d92310UL, 0x78f470d3UL, 0x61ef4192UL, ++ 0x2eaed755UL, 0x37b5e614UL, 0x1c98b5d7UL, 0x05838496UL, 0x821b9859UL, ++ 0x9b00a918UL, 0xb02dfadbUL, 0xa936cb9aUL, 0xe6775d5dUL, 0xff6c6c1cUL, ++ 0xd4413fdfUL, 0xcd5a0e9eUL, 0x958424a2UL, 0x8c9f15e3UL, 0xa7b24620UL, ++ 0xbea97761UL, 0xf1e8e1a6UL, 0xe8f3d0e7UL, 0xc3de8324UL, 0xdac5b265UL, ++ 0x5d5daeaaUL, 0x44469febUL, 0x6f6bcc28UL, 0x7670fd69UL, 0x39316baeUL, ++ 0x202a5aefUL, 0x0b07092cUL, 0x121c386dUL, 0xdf4636f3UL, 0xc65d07b2UL, ++ 0xed705471UL, 0xf46b6530UL, 0xbb2af3f7UL, 0xa231c2b6UL, 0x891c9175UL, ++ 0x9007a034UL, 0x179fbcfbUL, 0x0e848dbaUL, 0x25a9de79UL, 0x3cb2ef38UL, ++ 0x73f379ffUL, 0x6ae848beUL, 0x41c51b7dUL, 0x58de2a3cUL, 0xf0794f05UL, ++ 0xe9627e44UL, 0xc24f2d87UL, 0xdb541cc6UL, 0x94158a01UL, 0x8d0ebb40UL, ++ 0xa623e883UL, 0xbf38d9c2UL, 0x38a0c50dUL, 0x21bbf44cUL, 0x0a96a78fUL, ++ 0x138d96ceUL, 0x5ccc0009UL, 0x45d73148UL, 0x6efa628bUL, 0x77e153caUL, ++ 0xbabb5d54UL, 0xa3a06c15UL, 0x888d3fd6UL, 0x91960e97UL, 0xded79850UL, ++ 0xc7cca911UL, 0xece1fad2UL, 0xf5facb93UL, 0x7262d75cUL, 0x6b79e61dUL, ++ 0x4054b5deUL, 0x594f849fUL, 0x160e1258UL, 0x0f152319UL, 0x243870daUL, ++ 0x3d23419bUL, 0x65fd6ba7UL, 0x7ce65ae6UL, 0x57cb0925UL, 0x4ed03864UL, ++ 0x0191aea3UL, 0x188a9fe2UL, 0x33a7cc21UL, 0x2abcfd60UL, 0xad24e1afUL, ++ 0xb43fd0eeUL, 0x9f12832dUL, 0x8609b26cUL, 0xc94824abUL, 0xd05315eaUL, ++ 0xfb7e4629UL, 0xe2657768UL, 0x2f3f79f6UL, 0x362448b7UL, 0x1d091b74UL, ++ 0x04122a35UL, 0x4b53bcf2UL, 0x52488db3UL, 0x7965de70UL, 0x607eef31UL, ++ 0xe7e6f3feUL, 0xfefdc2bfUL, 0xd5d0917cUL, 0xcccba03dUL, 0x838a36faUL, ++ 0x9a9107bbUL, 0xb1bc5478UL, 0xa8a76539UL, 0x3b83984bUL, 0x2298a90aUL, ++ 0x09b5fac9UL, 0x10aecb88UL, 0x5fef5d4fUL, 0x46f46c0eUL, 0x6dd93fcdUL, ++ 0x74c20e8cUL, 0xf35a1243UL, 0xea412302UL, 0xc16c70c1UL, 0xd8774180UL, ++ 0x9736d747UL, 0x8e2de606UL, 0xa500b5c5UL, 0xbc1b8484UL, 0x71418a1aUL, ++ 0x685abb5bUL, 0x4377e898UL, 0x5a6cd9d9UL, 0x152d4f1eUL, 0x0c367e5fUL, ++ 0x271b2d9cUL, 0x3e001cddUL, 0xb9980012UL, 0xa0833153UL, 0x8bae6290UL, ++ 0x92b553d1UL, 0xddf4c516UL, 0xc4eff457UL, 0xefc2a794UL, 0xf6d996d5UL, ++ 0xae07bce9UL, 0xb71c8da8UL, 0x9c31de6bUL, 0x852aef2aUL, 0xca6b79edUL, ++ 0xd37048acUL, 0xf85d1b6fUL, 0xe1462a2eUL, 0x66de36e1UL, 0x7fc507a0UL, ++ 0x54e85463UL, 0x4df36522UL, 0x02b2f3e5UL, 0x1ba9c2a4UL, 0x30849167UL, ++ 0x299fa026UL, 0xe4c5aeb8UL, 0xfdde9ff9UL, 0xd6f3cc3aUL, 0xcfe8fd7bUL, ++ 0x80a96bbcUL, 0x99b25afdUL, 0xb29f093eUL, 0xab84387fUL, 0x2c1c24b0UL, ++ 0x350715f1UL, 0x1e2a4632UL, 0x07317773UL, 0x4870e1b4UL, 0x516bd0f5UL, ++ 0x7a468336UL, 0x635db277UL, 0xcbfad74eUL, 0xd2e1e60fUL, 0xf9ccb5ccUL, ++ 0xe0d7848dUL, 0xaf96124aUL, 0xb68d230bUL, 0x9da070c8UL, 0x84bb4189UL, ++ 0x03235d46UL, 0x1a386c07UL, 0x31153fc4UL, 0x280e0e85UL, 0x674f9842UL, ++ 0x7e54a903UL, 0x5579fac0UL, 0x4c62cb81UL, 0x8138c51fUL, 0x9823f45eUL, ++ 0xb30ea79dUL, 0xaa1596dcUL, 0xe554001bUL, 0xfc4f315aUL, 0xd7626299UL, ++ 0xce7953d8UL, 0x49e14f17UL, 0x50fa7e56UL, 0x7bd72d95UL, 0x62cc1cd4UL, ++ 0x2d8d8a13UL, 0x3496bb52UL, 0x1fbbe891UL, 0x06a0d9d0UL, 0x5e7ef3ecUL, ++ 0x4765c2adUL, 0x6c48916eUL, 0x7553a02fUL, 0x3a1236e8UL, 0x230907a9UL, ++ 0x0824546aUL, 0x113f652bUL, 0x96a779e4UL, 0x8fbc48a5UL, 0xa4911b66UL, ++ 0xbd8a2a27UL, 0xf2cbbce0UL, 0xebd08da1UL, 0xc0fdde62UL, 0xd9e6ef23UL, ++ 0x14bce1bdUL, 0x0da7d0fcUL, 0x268a833fUL, 0x3f91b27eUL, 0x70d024b9UL, ++ 0x69cb15f8UL, 0x42e6463bUL, 0x5bfd777aUL, 0xdc656bb5UL, 0xc57e5af4UL, ++ 0xee530937UL, 0xf7483876UL, 0xb809aeb1UL, 0xa1129ff0UL, 0x8a3fcc33UL, ++ 0x9324fd72UL, ++ ++ // Table 2 ++ 0x00000000UL, 0x01c26a37UL, 0x0384d46eUL, 0x0246be59UL, 0x0709a8dcUL, ++ 0x06cbc2ebUL, 0x048d7cb2UL, 0x054f1685UL, 0x0e1351b8UL, 0x0fd13b8fUL, ++ 0x0d9785d6UL, 0x0c55efe1UL, 0x091af964UL, 0x08d89353UL, 0x0a9e2d0aUL, ++ 0x0b5c473dUL, 0x1c26a370UL, 0x1de4c947UL, 0x1fa2771eUL, 0x1e601d29UL, ++ 0x1b2f0bacUL, 0x1aed619bUL, 0x18abdfc2UL, 0x1969b5f5UL, 0x1235f2c8UL, ++ 0x13f798ffUL, 0x11b126a6UL, 0x10734c91UL, 0x153c5a14UL, 0x14fe3023UL, ++ 0x16b88e7aUL, 0x177ae44dUL, 0x384d46e0UL, 0x398f2cd7UL, 0x3bc9928eUL, ++ 0x3a0bf8b9UL, 0x3f44ee3cUL, 0x3e86840bUL, 0x3cc03a52UL, 0x3d025065UL, ++ 0x365e1758UL, 0x379c7d6fUL, 0x35dac336UL, 0x3418a901UL, 0x3157bf84UL, ++ 0x3095d5b3UL, 0x32d36beaUL, 0x331101ddUL, 0x246be590UL, 0x25a98fa7UL, ++ 0x27ef31feUL, 0x262d5bc9UL, 0x23624d4cUL, 0x22a0277bUL, 0x20e69922UL, ++ 0x2124f315UL, 0x2a78b428UL, 0x2bbade1fUL, 0x29fc6046UL, 0x283e0a71UL, ++ 0x2d711cf4UL, 0x2cb376c3UL, 0x2ef5c89aUL, 0x2f37a2adUL, 0x709a8dc0UL, ++ 0x7158e7f7UL, 0x731e59aeUL, 0x72dc3399UL, 0x7793251cUL, 0x76514f2bUL, ++ 0x7417f172UL, 0x75d59b45UL, 0x7e89dc78UL, 0x7f4bb64fUL, 0x7d0d0816UL, ++ 0x7ccf6221UL, 0x798074a4UL, 0x78421e93UL, 0x7a04a0caUL, 0x7bc6cafdUL, ++ 0x6cbc2eb0UL, 0x6d7e4487UL, 0x6f38fadeUL, 0x6efa90e9UL, 0x6bb5866cUL, ++ 0x6a77ec5bUL, 0x68315202UL, 0x69f33835UL, 0x62af7f08UL, 0x636d153fUL, ++ 0x612bab66UL, 0x60e9c151UL, 0x65a6d7d4UL, 0x6464bde3UL, 0x662203baUL, ++ 0x67e0698dUL, 0x48d7cb20UL, 0x4915a117UL, 0x4b531f4eUL, 0x4a917579UL, ++ 0x4fde63fcUL, 0x4e1c09cbUL, 0x4c5ab792UL, 0x4d98dda5UL, 0x46c49a98UL, ++ 0x4706f0afUL, 0x45404ef6UL, 0x448224c1UL, 0x41cd3244UL, 0x400f5873UL, ++ 0x4249e62aUL, 0x438b8c1dUL, 0x54f16850UL, 0x55330267UL, 0x5775bc3eUL, ++ 0x56b7d609UL, 0x53f8c08cUL, 0x523aaabbUL, 0x507c14e2UL, 0x51be7ed5UL, ++ 0x5ae239e8UL, 0x5b2053dfUL, 0x5966ed86UL, 0x58a487b1UL, 0x5deb9134UL, ++ 0x5c29fb03UL, 0x5e6f455aUL, 0x5fad2f6dUL, 0xe1351b80UL, 0xe0f771b7UL, ++ 0xe2b1cfeeUL, 0xe373a5d9UL, 0xe63cb35cUL, 0xe7fed96bUL, 0xe5b86732UL, ++ 0xe47a0d05UL, 0xef264a38UL, 0xeee4200fUL, 0xeca29e56UL, 0xed60f461UL, ++ 0xe82fe2e4UL, 0xe9ed88d3UL, 0xebab368aUL, 0xea695cbdUL, 0xfd13b8f0UL, ++ 0xfcd1d2c7UL, 0xfe976c9eUL, 0xff5506a9UL, 0xfa1a102cUL, 0xfbd87a1bUL, ++ 0xf99ec442UL, 0xf85cae75UL, 0xf300e948UL, 0xf2c2837fUL, 0xf0843d26UL, ++ 0xf1465711UL, 0xf4094194UL, 0xf5cb2ba3UL, 0xf78d95faUL, 0xf64fffcdUL, ++ 0xd9785d60UL, 0xd8ba3757UL, 0xdafc890eUL, 0xdb3ee339UL, 0xde71f5bcUL, ++ 0xdfb39f8bUL, 0xddf521d2UL, 0xdc374be5UL, 0xd76b0cd8UL, 0xd6a966efUL, ++ 0xd4efd8b6UL, 0xd52db281UL, 0xd062a404UL, 0xd1a0ce33UL, 0xd3e6706aUL, ++ 0xd2241a5dUL, 0xc55efe10UL, 0xc49c9427UL, 0xc6da2a7eUL, 0xc7184049UL, ++ 0xc25756ccUL, 0xc3953cfbUL, 0xc1d382a2UL, 0xc011e895UL, 0xcb4dafa8UL, ++ 0xca8fc59fUL, 0xc8c97bc6UL, 0xc90b11f1UL, 0xcc440774UL, 0xcd866d43UL, ++ 0xcfc0d31aUL, 0xce02b92dUL, 0x91af9640UL, 0x906dfc77UL, 0x922b422eUL, ++ 0x93e92819UL, 0x96a63e9cUL, 0x976454abUL, 0x9522eaf2UL, 0x94e080c5UL, ++ 0x9fbcc7f8UL, 0x9e7eadcfUL, 0x9c381396UL, 0x9dfa79a1UL, 0x98b56f24UL, ++ 0x99770513UL, 0x9b31bb4aUL, 0x9af3d17dUL, 0x8d893530UL, 0x8c4b5f07UL, ++ 0x8e0de15eUL, 0x8fcf8b69UL, 0x8a809decUL, 0x8b42f7dbUL, 0x89044982UL, ++ 0x88c623b5UL, 0x839a6488UL, 0x82580ebfUL, 0x801eb0e6UL, 0x81dcdad1UL, ++ 0x8493cc54UL, 0x8551a663UL, 0x8717183aUL, 0x86d5720dUL, 0xa9e2d0a0UL, ++ 0xa820ba97UL, 0xaa6604ceUL, 0xaba46ef9UL, 0xaeeb787cUL, 0xaf29124bUL, ++ 0xad6fac12UL, 0xacadc625UL, 0xa7f18118UL, 0xa633eb2fUL, 0xa4755576UL, ++ 0xa5b73f41UL, 0xa0f829c4UL, 0xa13a43f3UL, 0xa37cfdaaUL, 0xa2be979dUL, ++ 0xb5c473d0UL, 0xb40619e7UL, 0xb640a7beUL, 0xb782cd89UL, 0xb2cddb0cUL, ++ 0xb30fb13bUL, 0xb1490f62UL, 0xb08b6555UL, 0xbbd72268UL, 0xba15485fUL, ++ 0xb853f606UL, 0xb9919c31UL, 0xbcde8ab4UL, 0xbd1ce083UL, 0xbf5a5edaUL, ++ 0xbe9834edUL, ++ ++ // Table 3 ++ 0x00000000UL, 0xb8bc6765UL, 0xaa09c88bUL, 0x12b5afeeUL, 0x8f629757UL, ++ 0x37def032UL, 0x256b5fdcUL, 0x9dd738b9UL, 0xc5b428efUL, 0x7d084f8aUL, ++ 0x6fbde064UL, 0xd7018701UL, 0x4ad6bfb8UL, 0xf26ad8ddUL, 0xe0df7733UL, ++ 0x58631056UL, 0x5019579fUL, 0xe8a530faUL, 0xfa109f14UL, 0x42acf871UL, ++ 0xdf7bc0c8UL, 0x67c7a7adUL, 0x75720843UL, 0xcdce6f26UL, 0x95ad7f70UL, ++ 0x2d111815UL, 0x3fa4b7fbUL, 0x8718d09eUL, 0x1acfe827UL, 0xa2738f42UL, ++ 0xb0c620acUL, 0x087a47c9UL, 0xa032af3eUL, 0x188ec85bUL, 0x0a3b67b5UL, ++ 0xb28700d0UL, 0x2f503869UL, 0x97ec5f0cUL, 0x8559f0e2UL, 0x3de59787UL, ++ 0x658687d1UL, 0xdd3ae0b4UL, 0xcf8f4f5aUL, 0x7733283fUL, 0xeae41086UL, ++ 0x525877e3UL, 0x40edd80dUL, 0xf851bf68UL, 0xf02bf8a1UL, 0x48979fc4UL, ++ 0x5a22302aUL, 0xe29e574fUL, 0x7f496ff6UL, 0xc7f50893UL, 0xd540a77dUL, ++ 0x6dfcc018UL, 0x359fd04eUL, 0x8d23b72bUL, 0x9f9618c5UL, 0x272a7fa0UL, ++ 0xbafd4719UL, 0x0241207cUL, 0x10f48f92UL, 0xa848e8f7UL, 0x9b14583dUL, ++ 0x23a83f58UL, 0x311d90b6UL, 0x89a1f7d3UL, 0x1476cf6aUL, 0xaccaa80fUL, ++ 0xbe7f07e1UL, 0x06c36084UL, 0x5ea070d2UL, 0xe61c17b7UL, 0xf4a9b859UL, ++ 0x4c15df3cUL, 0xd1c2e785UL, 0x697e80e0UL, 0x7bcb2f0eUL, 0xc377486bUL, ++ 0xcb0d0fa2UL, 0x73b168c7UL, 0x6104c729UL, 0xd9b8a04cUL, 0x446f98f5UL, ++ 0xfcd3ff90UL, 0xee66507eUL, 0x56da371bUL, 0x0eb9274dUL, 0xb6054028UL, ++ 0xa4b0efc6UL, 0x1c0c88a3UL, 0x81dbb01aUL, 0x3967d77fUL, 0x2bd27891UL, ++ 0x936e1ff4UL, 0x3b26f703UL, 0x839a9066UL, 0x912f3f88UL, 0x299358edUL, ++ 0xb4446054UL, 0x0cf80731UL, 0x1e4da8dfUL, 0xa6f1cfbaUL, 0xfe92dfecUL, ++ 0x462eb889UL, 0x549b1767UL, 0xec277002UL, 0x71f048bbUL, 0xc94c2fdeUL, ++ 0xdbf98030UL, 0x6345e755UL, 0x6b3fa09cUL, 0xd383c7f9UL, 0xc1366817UL, ++ 0x798a0f72UL, 0xe45d37cbUL, 0x5ce150aeUL, 0x4e54ff40UL, 0xf6e89825UL, ++ 0xae8b8873UL, 0x1637ef16UL, 0x048240f8UL, 0xbc3e279dUL, 0x21e91f24UL, ++ 0x99557841UL, 0x8be0d7afUL, 0x335cb0caUL, 0xed59b63bUL, 0x55e5d15eUL, ++ 0x47507eb0UL, 0xffec19d5UL, 0x623b216cUL, 0xda874609UL, 0xc832e9e7UL, ++ 0x708e8e82UL, 0x28ed9ed4UL, 0x9051f9b1UL, 0x82e4565fUL, 0x3a58313aUL, ++ 0xa78f0983UL, 0x1f336ee6UL, 0x0d86c108UL, 0xb53aa66dUL, 0xbd40e1a4UL, ++ 0x05fc86c1UL, 0x1749292fUL, 0xaff54e4aUL, 0x322276f3UL, 0x8a9e1196UL, ++ 0x982bbe78UL, 0x2097d91dUL, 0x78f4c94bUL, 0xc048ae2eUL, 0xd2fd01c0UL, ++ 0x6a4166a5UL, 0xf7965e1cUL, 0x4f2a3979UL, 0x5d9f9697UL, 0xe523f1f2UL, ++ 0x4d6b1905UL, 0xf5d77e60UL, 0xe762d18eUL, 0x5fdeb6ebUL, 0xc2098e52UL, ++ 0x7ab5e937UL, 0x680046d9UL, 0xd0bc21bcUL, 0x88df31eaUL, 0x3063568fUL, ++ 0x22d6f961UL, 0x9a6a9e04UL, 0x07bda6bdUL, 0xbf01c1d8UL, 0xadb46e36UL, ++ 0x15080953UL, 0x1d724e9aUL, 0xa5ce29ffUL, 0xb77b8611UL, 0x0fc7e174UL, ++ 0x9210d9cdUL, 0x2aacbea8UL, 0x38191146UL, 0x80a57623UL, 0xd8c66675UL, ++ 0x607a0110UL, 0x72cfaefeUL, 0xca73c99bUL, 0x57a4f122UL, 0xef189647UL, ++ 0xfdad39a9UL, 0x45115eccUL, 0x764dee06UL, 0xcef18963UL, 0xdc44268dUL, ++ 0x64f841e8UL, 0xf92f7951UL, 0x41931e34UL, 0x5326b1daUL, 0xeb9ad6bfUL, ++ 0xb3f9c6e9UL, 0x0b45a18cUL, 0x19f00e62UL, 0xa14c6907UL, 0x3c9b51beUL, ++ 0x842736dbUL, 0x96929935UL, 0x2e2efe50UL, 0x2654b999UL, 0x9ee8defcUL, ++ 0x8c5d7112UL, 0x34e11677UL, 0xa9362eceUL, 0x118a49abUL, 0x033fe645UL, ++ 0xbb838120UL, 0xe3e09176UL, 0x5b5cf613UL, 0x49e959fdUL, 0xf1553e98UL, ++ 0x6c820621UL, 0xd43e6144UL, 0xc68bceaaUL, 0x7e37a9cfUL, 0xd67f4138UL, ++ 0x6ec3265dUL, 0x7c7689b3UL, 0xc4caeed6UL, 0x591dd66fUL, 0xe1a1b10aUL, ++ 0xf3141ee4UL, 0x4ba87981UL, 0x13cb69d7UL, 0xab770eb2UL, 0xb9c2a15cUL, ++ 0x017ec639UL, 0x9ca9fe80UL, 0x241599e5UL, 0x36a0360bUL, 0x8e1c516eUL, ++ 0x866616a7UL, 0x3eda71c2UL, 0x2c6fde2cUL, 0x94d3b949UL, 0x090481f0UL, ++ 0xb1b8e695UL, 0xa30d497bUL, 0x1bb12e1eUL, 0x43d23e48UL, 0xfb6e592dUL, ++ 0xe9dbf6c3UL, 0x516791a6UL, 0xccb0a91fUL, 0x740cce7aUL, 0x66b96194UL, ++ 0xde0506f1UL, ++ // Constants for Neon CRC232 implementation ++ // k3 = 0x78ED02D5 = x^288 mod poly - bit reversed ++ // k4 = 0xED627DAE = x^256 mod poly - bit reversed ++ 0x78ED02D5UL, 0xED627DAEUL, // k4:k3 ++ 0xED78D502UL, 0x62EDAE7DUL, // byte swap ++ 0x02D578EDUL, 0x7DAEED62UL, // word swap ++ 0xD502ED78UL, 0xAE7D62EDUL, // byte swap of word swap ++}; +diff --git a/hotspot/src/cpu/loongarch/vm/stubRoutines_loongarch_64.hpp b/hotspot/src/cpu/loongarch/vm/stubRoutines_loongarch_64.hpp +new file mode 100644 +index 0000000000..d020a527e4 +--- /dev/null ++++ b/hotspot/src/cpu/loongarch/vm/stubRoutines_loongarch_64.hpp +@@ -0,0 +1,60 @@ ++/* ++ * Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2015, 2020, Loongson Technology. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ * ++ */ ++ ++#ifndef CPU_LOONGARCH_VM_STUBROUTINES_LOONGARCH_64_HPP ++#define CPU_LOONGARCH_VM_STUBROUTINES_LOONGARCH_64_HPP ++ ++// This file holds the platform specific parts of the StubRoutines ++// definition. See stubRoutines.hpp for a description on how to ++// extend it. ++ ++static bool returns_to_call_stub(address return_pc){ ++ return return_pc == _call_stub_return_address||return_pc == la::get_call_stub_compiled_return(); ++} ++ ++enum platform_dependent_constants { ++ code_size1 = 20000, // simply increase if too small (assembler will crash if too small) ++ code_size2 = 60000 // simply increase if too small (assembler will crash if too small) ++}; ++ ++class la { ++ friend class StubGenerator; ++ friend class VMStructs; ++ private: ++ // If we call compiled code directly from the call stub we will ++ // need to adjust the return back to the call stub to a specialized ++ // piece of code that can handle compiled results and cleaning the fpu ++ // stack. The variable holds that location. ++ static address _call_stub_compiled_return; ++ static juint _crc_table[]; ++ ++public: ++ // Call back points for traps in compiled code ++ static address get_call_stub_compiled_return() { return _call_stub_compiled_return; } ++ static void set_call_stub_compiled_return(address ret){ _call_stub_compiled_return = ret; } ++ ++}; ++ ++#endif // CPU_LOONGARCH_VM_STUBROUTINES_LOONGARCH_64_HPP +diff --git a/hotspot/src/cpu/loongarch/vm/templateInterpreterGenerator_loongarch.hpp b/hotspot/src/cpu/loongarch/vm/templateInterpreterGenerator_loongarch.hpp +new file mode 100644 +index 0000000000..213e69b0b2 +--- /dev/null ++++ b/hotspot/src/cpu/loongarch/vm/templateInterpreterGenerator_loongarch.hpp +@@ -0,0 +1,35 @@ ++/* ++ * Copyright (c) 1997, 2010, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2015, 2020, Loongson Technology. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ * ++ */ ++ ++#ifndef CPU_LOONGARCH_VM_TEMPLATEINTERPRETERGENERATOR_LOONGARCH_HPP ++#define CPU_LOONGARCH_VM_TEMPLATEINTERPRETERGENERATOR_LOONGARCH_HPP ++ ++ protected: ++ ++ void generate_fixed_frame(bool native_call); ++ ++ // address generate_asm_interpreter_entry(bool synchronized); ++ ++#endif // CPU_LOONGARCH_VM_TEMPLATEINTERPRETERGENERATOR_LOONGARCH_HPP +diff --git a/hotspot/src/cpu/loongarch/vm/templateInterpreter_loongarch.hpp b/hotspot/src/cpu/loongarch/vm/templateInterpreter_loongarch.hpp +new file mode 100644 +index 0000000000..39e3ad7bb5 +--- /dev/null ++++ b/hotspot/src/cpu/loongarch/vm/templateInterpreter_loongarch.hpp +@@ -0,0 +1,41 @@ ++/* ++ * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2015, 2020, Loongson Technology. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ * ++ */ ++ ++#ifndef CPU_LOONGARCH_VM_TEMPLATEINTERPRETER_LOONGARCH_HPP ++#define CPU_LOONGARCH_VM_TEMPLATEINTERPRETER_LOONGARCH_HPP ++ ++ ++ protected: ++ ++ // Size of interpreter code. Increase if too small. Interpreter will ++ // fail with a guarantee ("not enough space for interpreter generation"); ++ // if too small. ++ // Run with +PrintInterpreter to get the VM to print out the size. ++ // Max size with JVMTI ++ // The sethi() instruction generates lots more instructions when shell ++ // stack limit is unlimited, so that's why this is much bigger. ++ const static int InterpreterCodeSize = 500 * K; ++ ++#endif // CPU_LOONGARCH_VM_TEMPLATEINTERPRETER_LOONGARCH_HPP +diff --git a/hotspot/src/cpu/loongarch/vm/templateInterpreter_loongarch_64.cpp b/hotspot/src/cpu/loongarch/vm/templateInterpreter_loongarch_64.cpp +new file mode 100644 +index 0000000000..b25086a399 +--- /dev/null ++++ b/hotspot/src/cpu/loongarch/vm/templateInterpreter_loongarch_64.cpp +@@ -0,0 +1,2335 @@ ++/* ++ * Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2015, 2022, Loongson Technology. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ * ++ */ ++ ++#include "precompiled.hpp" ++#include "asm/macroAssembler.hpp" ++#include "interpreter/bytecodeHistogram.hpp" ++#include "interpreter/interpreter.hpp" ++#include "interpreter/interpreterGenerator.hpp" ++#include "interpreter/interpreterRuntime.hpp" ++#include "interpreter/templateTable.hpp" ++#include "oops/arrayOop.hpp" ++#include "oops/methodData.hpp" ++#include "oops/method.hpp" ++#include "oops/oop.inline.hpp" ++#include "prims/jvmtiExport.hpp" ++#include "prims/jvmtiThreadState.hpp" ++#include "runtime/arguments.hpp" ++#include "runtime/deoptimization.hpp" ++#include "runtime/frame.inline.hpp" ++#include "runtime/sharedRuntime.hpp" ++#include "runtime/stubRoutines.hpp" ++#include "runtime/synchronizer.hpp" ++#include "runtime/timer.hpp" ++#include "runtime/vframeArray.hpp" ++#include "utilities/debug.hpp" ++ ++#define __ _masm-> ++ ++#define A0 RA0 ++#define A1 RA1 ++#define A2 RA2 ++#define A3 RA3 ++#define A4 RA4 ++#define A5 RA5 ++#define A6 RA6 ++#define A7 RA7 ++#define T0 RT0 ++#define T1 RT1 ++#define T2 RT2 ++#define T3 RT3 ++#define T4 RT4 ++#define T5 RT5 ++#define T6 RT6 ++#define T7 RT7 ++#define T8 RT8 ++ ++#ifndef CC_INTERP ++ ++// asm based interpreter deoptimization helpers ++int AbstractInterpreter::size_activation(int max_stack, ++ int temps, ++ int extra_args, ++ int monitors, ++ int callee_params, ++ int callee_locals, ++ bool is_top_frame) { ++ // Note: This calculation must exactly parallel the frame setup ++ // in AbstractInterpreterGenerator::generate_method_entry. ++ ++ // fixed size of an interpreter frame: ++ int overhead = frame::sender_sp_offset - ++ frame::interpreter_frame_initial_sp_offset; ++ // Our locals were accounted for by the caller (or last_frame_adjust ++ // on the transistion) Since the callee parameters already account ++ // for the callee's params we only need to account for the extra ++ // locals. ++ int size = overhead + ++ (callee_locals - callee_params)*Interpreter::stackElementWords + ++ monitors * frame::interpreter_frame_monitor_size() + ++ temps* Interpreter::stackElementWords + extra_args; ++ ++ return size; ++} ++ ++ ++const int Interpreter::return_sentinel = 0xfeedbeed; ++const int method_offset = frame::interpreter_frame_method_offset * wordSize; ++const int bci_offset = frame::interpreter_frame_bcx_offset * wordSize; ++const int locals_offset = frame::interpreter_frame_locals_offset * wordSize; ++ ++//----------------------------------------------------------------------------- ++ ++address TemplateInterpreterGenerator::generate_StackOverflowError_handler() { ++ address entry = __ pc(); ++ ++#ifdef ASSERT ++ { ++ Label L; ++ __ addi_d(T1, FP, frame::interpreter_frame_monitor_block_top_offset * wordSize); ++ __ sub_d(T1, T1, SP); // T1 = maximal sp for current fp ++ __ bge(T1, R0, L); // check if frame is complete ++ __ stop("interpreter frame not set up"); ++ __ bind(L); ++ } ++#endif // ASSERT ++ // Restore bcp under the assumption that the current frame is still ++ // interpreted ++ // FIXME: please change the func restore_bcp ++ // S0 is the conventional register for bcp ++ __ restore_bcp(); ++ ++ // expression stack must be empty before entering the VM if an ++ // exception happened ++ __ empty_expression_stack(); ++ // throw exception ++ // FIXME: why do not pass parameter thread ? ++ __ call_VM(NOREG, CAST_FROM_FN_PTR(address, InterpreterRuntime::throw_StackOverflowError)); ++ return entry; ++} ++ ++address TemplateInterpreterGenerator::generate_ArrayIndexOutOfBounds_handler( ++ const char* name) { ++ address entry = __ pc(); ++ // expression stack must be empty before entering the VM if an ++ // exception happened ++ __ empty_expression_stack(); ++ __ li(A1, (long)name); ++ __ call_VM(noreg, CAST_FROM_FN_PTR(address, ++ InterpreterRuntime::throw_ArrayIndexOutOfBoundsException), A1, A2); ++ return entry; ++} ++ ++address TemplateInterpreterGenerator::generate_ClassCastException_handler() { ++ address entry = __ pc(); ++ // expression stack must be empty before entering the VM if an ++ // exception happened ++ __ empty_expression_stack(); ++ __ empty_FPU_stack(); ++ __ call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::throw_ClassCastException), FSR); ++ return entry; ++} ++ ++address TemplateInterpreterGenerator::generate_exception_handler_common( ++ const char* name, const char* message, bool pass_oop) { ++ assert(!pass_oop || message == NULL, "either oop or message but not both"); ++ address entry = __ pc(); ++ ++ // expression stack must be empty before entering the VM if an exception happened ++ __ empty_expression_stack(); ++ // setup parameters ++ __ li(A1, (long)name); ++ if (pass_oop) { ++ __ call_VM(V0, ++ CAST_FROM_FN_PTR(address, InterpreterRuntime::create_klass_exception), A1, FSR); ++ } else { ++ __ li(A2, (long)message); ++ __ call_VM(V0, ++ CAST_FROM_FN_PTR(address, InterpreterRuntime::create_exception), A1, A2); ++ } ++ // throw exception ++ __ jmp(Interpreter::throw_exception_entry(), relocInfo::none); ++ return entry; ++} ++ ++ ++address TemplateInterpreterGenerator::generate_continuation_for(TosState state) { ++ address entry = __ pc(); ++ // NULL last_sp until next java call ++ __ st_d(R0,Address(FP, frame::interpreter_frame_last_sp_offset * wordSize)); ++ __ dispatch_next(state); ++ return entry; ++} ++ ++ ++address TemplateInterpreterGenerator::generate_return_entry_for(TosState state, int step, size_t index_size) { ++ address entry = __ pc(); ++ ++ // Restore stack bottom in case i2c adjusted stack ++ __ ld_d(SP, Address(FP, frame::interpreter_frame_last_sp_offset * wordSize)); ++ // and NULL it as marker that sp is now tos until next java call ++ __ st_d(R0, FP, frame::interpreter_frame_last_sp_offset * wordSize); ++ ++ __ restore_bcp(); ++ __ restore_locals(); ++ ++ // mdp: T8 ++ // ret: FSR ++ // tmp: T4 ++ if (state == atos) { ++ Register mdp = T8; ++ Register tmp = T4; ++ __ profile_return_type(mdp, FSR, tmp); ++ } ++ ++ ++ const Register cache = T4; ++ const Register index = T3; ++ __ get_cache_and_index_at_bcp(cache, index, 1, index_size); ++ ++ const Register flags = cache; ++ __ alsl_d(AT, index, cache, Address::times_ptr - 1); ++ __ ld_w(flags, AT, in_bytes(ConstantPoolCache::base_offset() + ConstantPoolCacheEntry::flags_offset())); ++ __ andi(flags, flags, ConstantPoolCacheEntry::parameter_size_mask); ++ __ alsl_d(SP, flags, SP, Interpreter::stackElementScale() - 1); ++ ++ __ dispatch_next(state, step); ++ ++ return entry; ++} ++ ++ ++address TemplateInterpreterGenerator::generate_deopt_entry_for(TosState state, ++ int step) { ++ address entry = __ pc(); ++ // NULL last_sp until next java call ++ __ st_d(R0, FP, frame::interpreter_frame_last_sp_offset * wordSize); ++ __ restore_bcp(); ++ __ restore_locals(); ++ // handle exceptions ++ { ++ Label L; ++ const Register thread = TREG; ++#ifndef OPT_THREAD ++ __ get_thread(thread); ++#endif ++ __ ld_d(AT, thread, in_bytes(Thread::pending_exception_offset())); ++ __ beq(AT, R0, L); ++ __ call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::throw_pending_exception)); ++ __ should_not_reach_here(); ++ __ bind(L); ++ } ++ __ dispatch_next(state, step); ++ return entry; ++} ++ ++int AbstractInterpreter::BasicType_as_index(BasicType type) { ++ int i = 0; ++ switch (type) { ++ case T_BOOLEAN: i = 0; break; ++ case T_CHAR : i = 1; break; ++ case T_BYTE : i = 2; break; ++ case T_SHORT : i = 3; break; ++ case T_INT : // fall through ++ case T_LONG : // fall through ++ case T_VOID : i = 4; break; ++ case T_FLOAT : i = 5; break; ++ case T_DOUBLE : i = 6; break; ++ case T_OBJECT : // fall through ++ case T_ARRAY : i = 7; break; ++ default : ShouldNotReachHere(); ++ } ++ assert(0 <= i && i < AbstractInterpreter::number_of_result_handlers, ++ "index out of bounds"); ++ return i; ++} ++ ++ ++address TemplateInterpreterGenerator::generate_result_handler_for( ++ BasicType type) { ++ address entry = __ pc(); ++ switch (type) { ++ case T_BOOLEAN: __ c2bool(V0); break; ++ case T_CHAR : __ bstrpick_d(V0, V0, 15, 0); break; ++ case T_BYTE : __ sign_extend_byte (V0); break; ++ case T_SHORT : __ sign_extend_short(V0); break; ++ case T_INT : /* nothing to do */ break; ++ case T_FLOAT : /* nothing to do */ break; ++ case T_DOUBLE : /* nothing to do */ break; ++ case T_OBJECT : ++ { ++ __ ld_d(V0, FP, frame::interpreter_frame_oop_temp_offset * wordSize); ++ __ verify_oop(V0); // and verify it ++ } ++ break; ++ default : ShouldNotReachHere(); ++ } ++ __ jr(RA); // return from result handler ++ return entry; ++} ++ ++address TemplateInterpreterGenerator::generate_safept_entry_for( ++ TosState state, ++ address runtime_entry) { ++ address entry = __ pc(); ++ __ push(state); ++ __ call_VM(noreg, runtime_entry); ++ __ dispatch_via(vtos, Interpreter::_normal_table.table_for(vtos)); ++ return entry; ++} ++ ++ ++ ++// Helpers for commoning out cases in the various type of method entries. ++// ++ ++ ++// increment invocation count & check for overflow ++// ++// Note: checking for negative value instead of overflow ++// so we have a 'sticky' overflow test ++// ++// Rmethod: method ++// T3 : invocation counter ++// ++void InterpreterGenerator::generate_counter_incr( ++ Label* overflow, ++ Label* profile_method, ++ Label* profile_method_continue) { ++ Label done; ++ if (TieredCompilation) { ++ int increment = InvocationCounter::count_increment; ++ int mask = ((1 << Tier0InvokeNotifyFreqLog) - 1) << InvocationCounter::count_shift; ++ Label no_mdo; ++ if (ProfileInterpreter) { ++ // Are we profiling? ++ __ ld_d(FSR, Address(Rmethod, Method::method_data_offset())); ++ __ beq(FSR, R0, no_mdo); ++ // Increment counter in the MDO ++ const Address mdo_invocation_counter(FSR, in_bytes(MethodData::invocation_counter_offset()) + ++ in_bytes(InvocationCounter::counter_offset())); ++ __ increment_mask_and_jump(mdo_invocation_counter, increment, mask, T3, false, Assembler::zero, overflow); ++ __ beq(R0, R0, done); ++ } ++ __ bind(no_mdo); ++ // Increment counter in MethodCounters ++ const Address invocation_counter(FSR, ++ MethodCounters::invocation_counter_offset() + ++ InvocationCounter::counter_offset()); ++ __ get_method_counters(Rmethod, FSR, done); ++ __ increment_mask_and_jump(invocation_counter, increment, mask, T3, false, Assembler::zero, overflow); ++ __ bind(done); ++ } else { ++ const Address invocation_counter(FSR, in_bytes(MethodCounters::invocation_counter_offset()) ++ + in_bytes(InvocationCounter::counter_offset())); ++ const Address backedge_counter (FSR, in_bytes(MethodCounters::backedge_counter_offset()) ++ + in_bytes(InvocationCounter::counter_offset())); ++ ++ __ get_method_counters(Rmethod, FSR, done); ++ ++ if (ProfileInterpreter) { // %%% Merge this into methodDataOop ++ __ ld_w(T4, FSR, in_bytes(MethodCounters::interpreter_invocation_counter_offset())); ++ __ addi_d(T4, T4, 1); ++ __ st_w(T4, FSR, in_bytes(MethodCounters::interpreter_invocation_counter_offset())); ++ } ++ // Update standard invocation counters ++ __ ld_w(T3, invocation_counter); ++ __ increment(T3, InvocationCounter::count_increment); ++ __ st_w(T3, invocation_counter); // save invocation count ++ ++ __ ld_w(FSR, backedge_counter); // load backedge counter ++ __ li(AT, InvocationCounter::count_mask_value); // mask out the status bits ++ __ andr(FSR, FSR, AT); ++ ++ __ add_d(T3, T3, FSR); // add both counters ++ ++ if (ProfileInterpreter && profile_method != NULL) { ++ // Test to see if we should create a method data oop ++ if (Assembler::is_simm(InvocationCounter::InterpreterProfileLimit, 12)) { ++ __ slti(AT, T3, InvocationCounter::InterpreterProfileLimit); ++ __ bne_far(AT, R0, *profile_method_continue); ++ } else { ++ __ li(AT, (long)&InvocationCounter::InterpreterProfileLimit); ++ __ ld_w(AT, AT, 0); ++ __ blt_far(T3, AT, *profile_method_continue, true /* signed */); ++ } ++ ++ // if no method data exists, go to profile_method ++ __ test_method_data_pointer(FSR, *profile_method); ++ } ++ ++ if (Assembler::is_simm(CompileThreshold, 12)) { ++ __ srli_w(AT, T3, InvocationCounter::count_shift); ++ __ slti(AT, AT, CompileThreshold); ++ __ beq_far(AT, R0, *overflow); ++ } else { ++ __ li(AT, (long)&InvocationCounter::InterpreterInvocationLimit); ++ __ ld_w(AT, AT, 0); ++ __ bge_far(T3, AT, *overflow, true /* signed */); ++ } ++ ++ __ bind(done); ++ } ++} ++ ++void InterpreterGenerator::generate_counter_overflow(Label* do_continue) { ++ ++ // Asm interpreter on entry ++ // S7 - locals ++ // S0 - bcp ++ // Rmethod - method ++ // FP - interpreter frame ++ ++ // On return (i.e. jump to entry_point) ++ // Rmethod - method ++ // RA - return address of interpreter caller ++ // tos - the last parameter to Java method ++ // SP - sender_sp ++ ++ // the bcp is valid if and only if it's not null ++ __ call_VM(NOREG, CAST_FROM_FN_PTR(address, ++ InterpreterRuntime::frequency_counter_overflow), R0); ++ __ ld_d(Rmethod, FP, method_offset); ++ // Preserve invariant that S0/S7 contain bcp/locals of sender frame ++ __ b_far(*do_continue); ++} ++ ++// See if we've got enough room on the stack for locals plus overhead. ++// The expression stack grows down incrementally, so the normal guard ++// page mechanism will work for that. ++// ++// NOTE: Since the additional locals are also always pushed (wasn't ++// obvious in generate_method_entry) so the guard should work for them ++// too. ++// ++// Args: ++// T2: number of additional locals this frame needs (what we must check) ++// T0: Method* ++// ++void InterpreterGenerator::generate_stack_overflow_check(void) { ++ // see if we've got enough room on the stack for locals plus overhead. ++ // the expression stack grows down incrementally, so the normal guard ++ // page mechanism will work for that. ++ // ++ // Registers live on entry: ++ // ++ // T0: Method* ++ // T2: number of additional locals this frame needs (what we must check) ++ ++ // NOTE: since the additional locals are also always pushed (wasn't obvious in ++ // generate_method_entry) so the guard should work for them too. ++ // ++ ++ const int entry_size = frame::interpreter_frame_monitor_size() * wordSize; ++ ++ // total overhead size: entry_size + (saved fp thru expr stack bottom). ++ // be sure to change this if you add/subtract anything to/from the overhead area ++ const int overhead_size = -(frame::interpreter_frame_initial_sp_offset*wordSize) ++ + entry_size; ++ ++ const int page_size = os::vm_page_size(); ++ Label after_frame_check; ++ ++ // see if the frame is greater than one page in size. If so, ++ // then we need to verify there is enough stack space remaining ++ // for the additional locals. ++ __ li(AT, (page_size - overhead_size) / Interpreter::stackElementSize); ++ __ bge(AT, T2, after_frame_check); ++ ++ // compute sp as if this were going to be the last frame on ++ // the stack before the red zone ++#ifndef OPT_THREAD ++ Register thread = T1; ++ __ get_thread(thread); ++#else ++ Register thread = TREG; ++#endif ++ ++ // locals + overhead, in bytes ++ __ slli_d(T3, T2, Interpreter::stackElementScale()); ++ __ addi_d(T3, T3, overhead_size); // locals * 4 + overhead_size --> T3 ++ ++#ifdef ASSERT ++ Label stack_base_okay, stack_size_okay; ++ // verify that thread stack base is non-zero ++ __ ld_d(AT, thread, in_bytes(Thread::stack_base_offset())); ++ __ bne(AT, R0, stack_base_okay); ++ __ stop("stack base is zero"); ++ __ bind(stack_base_okay); ++ // verify that thread stack size is non-zero ++ __ ld_d(AT, thread, in_bytes(Thread::stack_size_offset())); ++ __ bne(AT, R0, stack_size_okay); ++ __ stop("stack size is zero"); ++ __ bind(stack_size_okay); ++#endif ++ ++ // Add stack base to locals and subtract stack size ++ __ ld_d(AT, thread, in_bytes(Thread::stack_base_offset())); // stack_base --> AT ++ __ add_d(T3, T3, AT); // locals * 4 + overhead_size + stack_base--> T3 ++ __ ld_d(AT, thread, in_bytes(Thread::stack_size_offset())); // stack_size --> AT ++ __ sub_d(T3, T3, AT); // locals * 4 + overhead_size + stack_base - stack_size --> T3 ++ ++ ++ // add in the redzone and yellow size ++ __ li(AT, (StackRedPages+StackYellowPages) * page_size); ++ __ add_d(T3, T3, AT); ++ ++ // check against the current stack bottom ++ __ blt(T3, SP, after_frame_check); ++ ++ // Note: the restored frame is not necessarily interpreted. ++ // Use the shared runtime version of the StackOverflowError. ++ __ move(SP, Rsender); ++ assert(StubRoutines::throw_StackOverflowError_entry() != NULL, "stub not yet generated"); ++ __ jmp(StubRoutines::throw_StackOverflowError_entry(), relocInfo::runtime_call_type); ++ ++ // all done with frame size check ++ __ bind(after_frame_check); ++} ++ ++// Allocate monitor and lock method (asm interpreter) ++// Rmethod - Method* ++void InterpreterGenerator::lock_method(void) { ++ // synchronize method ++ const int entry_size = frame::interpreter_frame_monitor_size() * wordSize; ++ ++#ifdef ASSERT ++ { Label L; ++ __ ld_w(T0, Rmethod, in_bytes(Method::access_flags_offset())); ++ __ andi(T0, T0, JVM_ACC_SYNCHRONIZED); ++ __ bne(T0, R0, L); ++ __ stop("method doesn't need synchronization"); ++ __ bind(L); ++ } ++#endif // ASSERT ++ // get synchronization object ++ { ++ Label done; ++ const int mirror_offset = in_bytes(Klass::java_mirror_offset()); ++ __ ld_w(T0, Rmethod, in_bytes(Method::access_flags_offset())); ++ __ andi(T2, T0, JVM_ACC_STATIC); ++ __ ld_d(T0, LVP, Interpreter::local_offset_in_bytes(0)); ++ __ beq(T2, R0, done); ++ __ ld_d(T0, Rmethod, in_bytes(Method::const_offset())); ++ __ ld_d(T0, T0, in_bytes(ConstMethod::constants_offset())); ++ __ ld_d(T0, T0, ConstantPool::pool_holder_offset_in_bytes()); ++ __ ld_d(T0, T0, mirror_offset); ++ __ bind(done); ++ } ++ // add space for monitor & lock ++ __ addi_d(SP, SP, (-1) * entry_size); // add space for a monitor entry ++ __ st_d(SP, FP, frame::interpreter_frame_monitor_block_top_offset * wordSize); ++ // set new monitor block top ++ __ st_d(T0, SP, BasicObjectLock::obj_offset_in_bytes()); // store object ++ // FIXME: I do not know what lock_object will do and what it will need ++ __ move(c_rarg0, SP); // object address ++ __ lock_object(c_rarg0); ++} ++ ++/** ++ * Method entry for static native methods: ++ * int java.util.zip.CRC32.update(int crc, int b) ++ */ ++address InterpreterGenerator::generate_CRC32_update_entry() { ++ if (UseCRC32Intrinsics) { ++ address entry = __ pc(); ++ ++ // rmethod: Method* ++ // Rsender: senderSP must preserved for slow path ++ // SP: args ++ ++ Label slow_path; ++ // If we need a safepoint check, generate full interpreter entry. ++ __ li(AT, SafepointSynchronize::_not_synchronized); ++ __ li(T8, (long)SafepointSynchronize::address_of_state()); ++ __ bne(T8, AT, slow_path); ++ ++ // We don't generate local frame and don't align stack because ++ // we call stub code and there is no safepoint on this path. ++ ++ const Register crc = A0; // crc ++ const Register val = A1; // source java byte value ++ const Register tbl = A2; // scratch ++ ++ // Arguments are reversed on java expression stack ++ __ ld_w(val, SP, 0); // byte value ++ __ ld_w(crc, SP, wordSize); // Initial CRC ++ ++ __ li(tbl, (long)StubRoutines::crc_table_addr()); ++ ++ __ nor(crc, crc, R0); // ~crc ++ __ update_byte_crc32(crc, val, tbl); ++ __ nor(crc, crc, R0); // ~crc ++ ++ // restore caller SP ++ __ move(SP, Rsender); ++ __ jr(RA); ++ ++ // generate a vanilla native entry as the slow path ++ __ bind(slow_path); ++ ++ (void) generate_native_entry(false); ++ ++ return entry; ++ } ++ return generate_native_entry(false); ++} ++ ++/** ++ * Method entry for static native methods: ++ * int java.util.zip.CRC32.updateBytes(int crc, byte[] b, int off, int len) ++ * int java.util.zip.CRC32.updateByteBuffer(int crc, long buf, int off, int len) ++ */ ++address InterpreterGenerator::generate_CRC32_updateBytes_entry(AbstractInterpreter::MethodKind kind) { ++ if (UseCRC32Intrinsics) { ++ address entry = __ pc(); ++ ++ // rmethod: Method* ++ // Rsender: senderSP must preserved for slow path ++ // SP: args ++ ++ Label slow_path; ++ // If we need a safepoint check, generate full interpreter entry. ++ __ li(AT, SafepointSynchronize::_not_synchronized); ++ __ li(T8, (long)SafepointSynchronize::address_of_state()); ++ __ bne(T8, AT, slow_path); ++ ++ // We don't generate local frame and don't align stack because ++ // we call stub code and there is no safepoint on this path. ++ ++ const Register crc = A0; // crc ++ const Register buf = A1; // source java byte array address ++ const Register len = A2; // length ++ const Register tmp = A3; ++ ++ const Register off = len; // offset (never overlaps with 'len') ++ ++ // Arguments are reversed on java expression stack ++ // Calculate address of start element ++ __ ld_w(off, SP, wordSize); // int offset ++ __ ld_d(buf, SP, 2 * wordSize); // byte[] buf | long buf ++ __ add_d(buf, buf, off); // + offset ++ if (kind == Interpreter::java_util_zip_CRC32_updateByteBuffer) { ++ __ ld_w(crc, SP, 4 * wordSize); // long crc ++ } else { ++ __ addi_d(buf, buf, arrayOopDesc::base_offset_in_bytes(T_BYTE)); // + header size ++ __ ld_w(crc, SP, 3 * wordSize); // long crc ++ } ++ ++ // Can now load 'len' since we're finished with 'off' ++ __ ld_w(len, SP, 0); // length ++ ++ __ kernel_crc32(crc, buf, len, tmp); ++ ++ // restore caller SP ++ __ move(SP, Rsender); ++ __ jr(RA); ++ ++ // generate a vanilla native entry as the slow path ++ __ bind(slow_path); ++ ++ (void) generate_native_entry(false); ++ ++ return entry; ++ } ++ return generate_native_entry(false); ++} ++ ++// Generate a fixed interpreter frame. This is identical setup for ++// interpreted methods and for native methods hence the shared code. ++void TemplateInterpreterGenerator::generate_fixed_frame(bool native_call) { ++ ++ // [ local var m-1 ] <--- sp ++ // ... ++ // [ local var 0 ] ++ // [ argumnet word n-1 ] <--- T0(sender's sp) ++ // ... ++ // [ argument word 0 ] <--- S7 ++ ++ // initialize fixed part of activation frame ++ // sender's sp in Rsender ++ int i = 0; ++ int frame_size = 9; ++#ifndef CORE ++ ++frame_size; ++#endif ++ __ addi_d(SP, SP, (-frame_size) * wordSize); ++ __ st_d(RA, SP, (frame_size - 1) * wordSize); // save return address ++ __ st_d(FP, SP, (frame_size - 2) * wordSize); // save sender's fp ++ __ addi_d(FP, SP, (frame_size - 2) * wordSize); ++ __ st_d(Rsender, FP, (-++i) * wordSize); // save sender's sp ++ __ st_d(R0, FP,(-++i) * wordSize); //save last_sp as null ++ __ st_d(LVP, FP, (-++i) * wordSize); // save locals offset ++ __ ld_d(BCP, Rmethod, in_bytes(Method::const_offset())); // get constMethodOop ++ __ addi_d(BCP, BCP, in_bytes(ConstMethod::codes_offset())); // get codebase ++ __ st_d(Rmethod, FP, (-++i) * wordSize); // save Method* ++#ifndef CORE ++ if (ProfileInterpreter) { ++ Label method_data_continue; ++ __ ld_d(AT, Rmethod, in_bytes(Method::method_data_offset())); ++ __ beq(AT, R0, method_data_continue); ++ __ addi_d(AT, AT, in_bytes(MethodData::data_offset())); ++ __ bind(method_data_continue); ++ __ st_d(AT, FP, (-++i) * wordSize); ++ } else { ++ __ st_d(R0, FP, (-++i) * wordSize); ++ } ++#endif // !CORE ++ ++ __ ld_d(T2, Rmethod, in_bytes(Method::const_offset())); ++ __ ld_d(T2, T2, in_bytes(ConstMethod::constants_offset())); ++ __ ld_d(T2, T2, ConstantPool::cache_offset_in_bytes()); ++ __ st_d(T2, FP, (-++i) * wordSize); // set constant pool cache ++ if (native_call) { ++ __ st_d(R0, FP, (-++i) * wordSize); // no bcp ++ } else { ++ __ st_d(BCP, FP, (-++i) * wordSize); // set bcp ++ } ++ __ st_d(SP, FP, (-++i) * wordSize); // reserve word for pointer to expression stack bottom ++ assert(i + 2 == frame_size, "i + 2 should be equal to frame_size"); ++} ++ ++// End of helpers ++ ++// Various method entries ++//------------------------------------------------------------------------------------------------------------------------ ++// ++// ++ ++// Call an accessor method (assuming it is resolved, otherwise drop ++// into vanilla (slow path) entry ++address InterpreterGenerator::generate_accessor_entry(void) { ++ // Rmethod: Method* ++ // V0: receiver (preserve for slow entry into asm interpreter) ++ // Rsender: senderSP must preserved for slow path, set SP to it on fast path ++ ++ address entry_point = __ pc(); ++ Label xreturn_path; ++ // do fastpath for resolved accessor methods ++ if (UseFastAccessorMethods) { ++ Label slow_path; ++ __ li(T2, SafepointSynchronize::address_of_state()); ++ __ ld_w(AT, T2, 0); ++ __ addi_d(AT, AT, -(SafepointSynchronize::_not_synchronized)); ++ __ bne(AT, R0, slow_path); ++ // Code: _aload_0, _(i|a)getfield, _(i|a)return or any rewrites thereof; ++ // parameter size = 1 ++ // Note: We can only use this code if the getfield has been resolved ++ // and if we don't have a null-pointer exception => check for ++ // these conditions first and use slow path if necessary. ++ // Rmethod: method ++ // V0: receiver ++ ++ // [ receiver ] <-- sp ++ __ ld_d(T0, SP, 0); ++ ++ // check if local 0 != NULL and read field ++ __ beq(T0, R0, slow_path); ++ __ ld_d(T2, Rmethod, in_bytes(Method::const_offset())); ++ __ ld_d(T2, T2, in_bytes(ConstMethod::constants_offset())); ++ // read first instruction word and extract bytecode @ 1 and index @ 2 ++ __ ld_d(T3, Rmethod, in_bytes(Method::const_offset())); ++ __ ld_w(T3, T3, in_bytes(ConstMethod::codes_offset())); ++ // Shift codes right to get the index on the right. ++ // The bytecode fetched looks like <0xb4><0x2a> ++ __ srli_d(T3, T3, 2 * BitsPerByte); ++ // FIXME: maybe it's wrong ++ __ slli_d(T3, T3, exact_log2(in_words(ConstantPoolCacheEntry::size()))); ++ __ ld_d(T2, T2, ConstantPool::cache_offset_in_bytes()); ++ ++ // T0: local 0 ++ // Rmethod: method ++ // V0: receiver - do not destroy since it is needed for slow path! ++ // T1: scratch use which register instead ? ++ // T3: constant pool cache index ++ // T2: constant pool cache ++ // Rsender: send's sp ++ // check if getfield has been resolved and read constant pool cache entry ++ // check the validity of the cache entry by testing whether _indices field ++ // contains Bytecode::_getfield in b1 byte. ++ assert(in_words(ConstantPoolCacheEntry::size()) == 4, "adjust shift below"); ++ ++ __ slli_d(T8, T3, Address::times_8); ++ __ li(T1, in_bytes(ConstantPoolCache::base_offset() ++ + ConstantPoolCacheEntry::indices_offset())); ++ __ add_d(T1, T8, T1); ++ __ ldx_w(T1, T1, T2); ++ __ srli_d(T1, T1, 2 * BitsPerByte); ++ __ andi(T1, T1, 0xFF); ++ __ addi_d(T1, T1, (-1) * Bytecodes::_getfield); ++ __ bne(T1, R0, slow_path); ++ ++ // Note: constant pool entry is not valid before bytecode is resolved ++ ++ __ li(T1, in_bytes(ConstantPoolCache::base_offset() + ConstantPoolCacheEntry::f2_offset())); ++ __ add_d(T1, T1, T8); ++ __ ldx_w(AT, T1, T2); ++ ++ __ li(T1, in_bytes(ConstantPoolCache::base_offset() + ConstantPoolCacheEntry::flags_offset())); ++ __ add_d(T1, T1, T8); ++ __ ldx_w(T3, T1, T2); ++ ++ Label notByte, notBool, notShort, notChar, notObj; ++ ++ // Need to differentiate between igetfield, agetfield, bgetfield etc. ++ // because they are different sizes. ++ // Use the type from the constant pool cache ++ __ srli_w(T3, T3, ConstantPoolCacheEntry::tos_state_shift); ++ // Make sure we don't need to mask T3 for tosBits after the above shift ++ ConstantPoolCacheEntry::verify_tos_state_shift(); ++ // btos = 0 ++ __ add_d(T0, T0, AT); ++ __ bne(T3, R0, notByte); ++ ++ __ ld_b(V0, T0, 0); ++ __ b(xreturn_path); ++ ++ //ztos ++ __ bind(notByte); ++ __ addi_d(T1, T3, (-1) * ztos); ++ __ bne(T1, R0, notBool); ++ __ ld_b(V0, T0, 0); ++ __ b(xreturn_path); ++ ++ //stos ++ __ bind(notBool); ++ __ addi_d(T1, T3, (-1) * stos); ++ __ bne(T1, R0, notShort); ++ __ ld_h(V0, T0, 0); ++ __ b(xreturn_path); ++ ++ //ctos ++ __ bind(notShort); ++ __ addi_d(T1, T3, (-1) * ctos); ++ __ bne(T1, R0, notChar); ++ __ ld_hu(V0, T0, 0); ++ __ b(xreturn_path); ++ ++ //atos ++ __ bind(notChar); ++ __ addi_d(T1, T3, (-1) * atos); ++ __ bne(T1, R0, notObj); ++ //add for compressedoops ++ __ load_heap_oop(V0, Address(T0, 0)); ++ __ b(xreturn_path); ++ ++ //itos ++ __ bind(notObj); ++#ifdef ASSERT ++ Label okay; ++ __ addi_d(T1, T3, (-1) * itos); ++ __ beq(T1, R0, okay); ++ __ stop("what type is this?"); ++ __ bind(okay); ++#endif // ASSERT ++ __ ld_w(V0, T0, 0); ++ ++ __ bind(xreturn_path); ++ ++ // _ireturn/_areturn ++ //FIXME ++ __ move(SP, Rsender);//FIXME, set sender's fp to SP ++ __ jr(RA); ++ ++ // generate a vanilla interpreter entry as the slow path ++ __ bind(slow_path); ++ (void) generate_normal_entry(false); ++ } else { ++ (void) generate_normal_entry(false); ++ } ++ return entry_point; ++} ++ ++// Method entry for java.lang.ref.Reference.get. ++address InterpreterGenerator::generate_Reference_get_entry(void) { ++#if INCLUDE_ALL_GCS ++ // Code: _aload_0, _getfield, _areturn ++ // parameter size = 1 ++ // ++ // The code that gets generated by this routine is split into 2 parts: ++ // 1. The "intrinsified" code for G1 (or any SATB based GC), ++ // 2. The slow path - which is an expansion of the regular method entry. ++ // ++ // Notes:- ++ // * In the G1 code we do not check whether we need to block for ++ // a safepoint. If G1 is enabled then we must execute the specialized ++ // code for Reference.get (except when the Reference object is null) ++ // so that we can log the value in the referent field with an SATB ++ // update buffer. ++ // If the code for the getfield template is modified so that the ++ // G1 pre-barrier code is executed when the current method is ++ // Reference.get() then going through the normal method entry ++ // will be fine. ++ // * The G1 code can, however, check the receiver object (the instance ++ // of java.lang.Reference) and jump to the slow path if null. If the ++ // Reference object is null then we obviously cannot fetch the referent ++ // and so we don't need to call the G1 pre-barrier. Thus we can use the ++ // regular method entry code to generate the NPE. ++ // ++ // This code is based on generate_accessor_enty. ++ // ++ // Rmethod: Method* ++ ++ // Rsender: senderSP must preserve for slow path, set SP to it on fast path (Rsender) ++ ++ address entry = __ pc(); ++ ++ const int referent_offset = java_lang_ref_Reference::referent_offset; ++ guarantee(referent_offset > 0, "referent offset not initialized"); ++ if (UseG1GC) { ++ Label slow_path; ++ ++ // Check if local 0 != NULL ++ // If the receiver is null then it is OK to jump to the slow path. ++ __ ld_d(V0, SP, 0); ++ ++ __ beq(V0, R0, slow_path); ++ ++ // Generate the G1 pre-barrier code to log the value of ++ // the referent field in an SATB buffer. ++ ++ // Load the value of the referent field. ++ const Address field_address(V0, referent_offset); ++ __ load_heap_oop(V0, field_address); ++ ++ __ push(RA); ++ // Generate the G1 pre-barrier code to log the value of ++ // the referent field in an SATB buffer. ++ __ g1_write_barrier_pre(noreg /* obj */, ++ V0 /* pre_val */, ++ TREG /* thread */, ++ Rmethod /* tmp */, ++ true /* tosca_live */, ++ true /* expand_call */); ++ __ pop(RA); ++ ++ __ add_d(SP, Rsender, R0); // set sp to sender sp ++ __ jr(RA); ++ ++ // generate a vanilla interpreter entry as the slow path ++ __ bind(slow_path); ++ (void) generate_normal_entry(false); ++ ++ return entry; ++ } ++#endif // INCLUDE_ALL_GCS ++ ++ // If G1 is not enabled then attempt to go through the accessor entry point ++ // Reference.get is an accessor ++ return generate_accessor_entry(); ++} ++ ++// Interpreter stub for calling a native method. (asm interpreter) ++// This sets up a somewhat different looking stack for calling the ++// native method than the typical interpreter frame setup. ++address InterpreterGenerator::generate_native_entry(bool synchronized) { ++ // determine code generation flags ++ bool inc_counter = UseCompiler || CountCompiledCalls; ++ // Rsender: sender's sp ++ // Rmethod: Method* ++ address entry_point = __ pc(); ++ ++#ifndef CORE ++ const Address invocation_counter(Rmethod,in_bytes(MethodCounters::invocation_counter_offset() + ++ InvocationCounter::counter_offset())); ++#endif ++ // get parameter size (always needed) ++ // the size in the java stack ++ __ ld_d(V0, Rmethod, in_bytes(Method::const_offset())); ++ __ ld_hu(V0, V0, in_bytes(ConstMethod::size_of_parameters_offset())); ++ ++ // native calls don't need the stack size check since they have no expression stack ++ // and the arguments are already on the stack and we only add a handful of words ++ // to the stack ++ ++ // Rmethod: Method* ++ // V0: size of parameters ++ // Layout of frame at this point ++ // ++ // [ argument word n-1 ] <--- sp ++ // ... ++ // [ argument word 0 ] ++ ++ // for natives the size of locals is zero ++ ++ // compute beginning of parameters (S7) ++ __ slli_d(LVP, V0, Address::times_8); ++ __ addi_d(LVP, LVP, (-1) * wordSize); ++ __ add_d(LVP, LVP, SP); ++ ++ ++ // add 2 zero-initialized slots for native calls ++ // 1 slot for native oop temp offset (setup via runtime) ++ // 1 slot for static native result handler3 (setup via runtime) ++ __ push2(R0, R0); ++ ++ // Layout of frame at this point ++ // [ method holder mirror ] <--- sp ++ // [ result type info ] ++ // [ argument word n-1 ] <--- T0 ++ // ... ++ // [ argument word 0 ] <--- LVP ++ ++ ++#ifndef CORE ++ if (inc_counter) __ ld_w(T3, invocation_counter); // (pre-)fetch invocation count ++#endif ++ ++ // initialize fixed part of activation frame ++ generate_fixed_frame(true); ++ // after this function, the layout of frame is as following ++ // ++ // [ monitor block top ] <--- sp ( the top monitor entry ) ++ // [ byte code pointer (0) ] (if native, bcp = 0) ++ // [ constant pool cache ] ++ // [ Method* ] ++ // [ locals offset ] ++ // [ sender's sp ] ++ // [ sender's fp ] ++ // [ return address ] <--- fp ++ // [ method holder mirror ] ++ // [ result type info ] ++ // [ argumnet word n-1 ] <--- sender's sp ++ // ... ++ // [ argument word 0 ] <--- S7 ++ ++ ++ // make sure method is native & not abstract ++#ifdef ASSERT ++ __ ld_w(T0, Rmethod, in_bytes(Method::access_flags_offset())); ++ { ++ Label L; ++ __ andi(AT, T0, JVM_ACC_NATIVE); ++ __ bne(AT, R0, L); ++ __ stop("tried to execute native method as non-native"); ++ __ bind(L); ++ } ++ { ++ Label L; ++ __ andi(AT, T0, JVM_ACC_ABSTRACT); ++ __ beq(AT, R0, L); ++ __ stop("tried to execute abstract method in interpreter"); ++ __ bind(L); ++ } ++#endif ++ ++ // Since at this point in the method invocation the exception handler ++ // would try to exit the monitor of synchronized methods which hasn't ++ // been entered yet, we set the thread local variable ++ // _do_not_unlock_if_synchronized to true. The remove_activation will ++ // check this flag. ++ Register thread = TREG; ++#ifndef OPT_THREAD ++ __ get_thread(thread); ++#endif ++ __ li(AT, (int)true); ++ __ st_b(AT, thread, in_bytes(JavaThread::do_not_unlock_if_synchronized_offset())); ++ ++#ifndef CORE ++ // increment invocation count & check for overflow ++ Label invocation_counter_overflow; ++ if (inc_counter) { ++ generate_counter_incr(&invocation_counter_overflow, NULL, NULL); ++ } ++ ++ Label continue_after_compile; ++ __ bind(continue_after_compile); ++#endif // CORE ++ ++ bang_stack_shadow_pages(true); ++ ++ // reset the _do_not_unlock_if_synchronized flag ++#ifndef OPT_THREAD ++ __ get_thread(thread); ++#endif ++ __ st_b(R0, thread, in_bytes(JavaThread::do_not_unlock_if_synchronized_offset())); ++ ++ // check for synchronized methods ++ // Must happen AFTER invocation_counter check and stack overflow check, ++ // so method is not locked if overflows. ++ if (synchronized) { ++ lock_method(); ++ } else { ++ // no synchronization necessary ++#ifdef ASSERT ++ { ++ Label L; ++ __ ld_w(T0, Rmethod, in_bytes(Method::access_flags_offset())); ++ __ andi(AT, T0, JVM_ACC_SYNCHRONIZED); ++ __ beq(AT, R0, L); ++ __ stop("method needs synchronization"); ++ __ bind(L); ++ } ++#endif ++ } ++ ++ // after method_lock, the layout of frame is as following ++ // ++ // [ monitor entry ] <--- sp ++ // ... ++ // [ monitor entry ] ++ // [ monitor block top ] ( the top monitor entry ) ++ // [ byte code pointer (0) ] (if native, bcp = 0) ++ // [ constant pool cache ] ++ // [ Method* ] ++ // [ locals offset ] ++ // [ sender's sp ] ++ // [ sender's fp ] ++ // [ return address ] <--- fp ++ // [ method holder mirror ] ++ // [ result type info ] ++ // [ argumnet word n-1 ] <--- ( sender's sp ) ++ // ... ++ // [ argument word 0 ] <--- S7 ++ ++ // start execution ++#ifdef ASSERT ++ { ++ Label L; ++ __ ld_d(AT, FP, frame::interpreter_frame_monitor_block_top_offset * wordSize); ++ __ beq(AT, SP, L); ++ __ stop("broken stack frame setup in interpreter in asm"); ++ __ bind(L); ++ } ++#endif ++ ++ // jvmti/jvmpi support ++ __ notify_method_entry(); ++ ++ // work registers ++ const Register method = Rmethod; ++ //const Register thread = T2; ++ const Register t = T8; ++ ++ __ get_method(method); ++ __ verify_oop(method); ++ { ++ Label L, Lstatic; ++ __ ld_d(t,method,in_bytes(Method::const_offset())); ++ __ ld_hu(t, t, in_bytes(ConstMethod::size_of_parameters_offset())); ++ // LoongArch ABI: caller does not reserve space for the register auguments. ++ // A0 and A1(if needed) ++ __ ld_w(AT, Rmethod, in_bytes(Method::access_flags_offset())); ++ __ andi(AT, AT, JVM_ACC_STATIC); ++ __ beq(AT, R0, Lstatic); ++ __ addi_d(t, t, 1); ++ __ bind(Lstatic); ++ __ addi_d(t, t, -7); ++ __ bge(R0, t, L); ++ __ slli_d(t, t, Address::times_8); ++ __ sub_d(SP, SP, t); ++ __ bind(L); ++ } ++ __ li(AT, -(StackAlignmentInBytes)); ++ __ andr(SP, SP, AT); ++ __ move(AT, SP); ++ // [ ] <--- sp ++ // ... (size of parameters - 8 ) ++ // [ monitor entry ] ++ // ... ++ // [ monitor entry ] ++ // [ monitor block top ] ( the top monitor entry ) ++ // [ byte code pointer (0) ] (if native, bcp = 0) ++ // [ constant pool cache ] ++ // [ Method* ] ++ // [ locals offset ] ++ // [ sender's sp ] ++ // [ sender's fp ] ++ // [ return address ] <--- fp ++ // [ method holder mirror ] ++ // [ result type info ] ++ // [ argumnet word n-1 ] <--- ( sender's sp ) ++ // ... ++ // [ argument word 0 ] <--- LVP ++ ++ // get signature handler ++ { ++ Label L; ++ __ ld_d(T4, method, in_bytes(Method::signature_handler_offset())); ++ __ bne(T4, R0, L); ++ __ call_VM(NOREG, CAST_FROM_FN_PTR(address, ++ InterpreterRuntime::prepare_native_call), method); ++ __ get_method(method); ++ __ ld_d(T4, method, in_bytes(Method::signature_handler_offset())); ++ __ bind(L); ++ } ++ ++ // call signature handler ++ // FIXME: when change codes in InterpreterRuntime, note this point ++ // from: begin of parameters ++ assert(InterpreterRuntime::SignatureHandlerGenerator::from() == LVP, "adjust this code"); ++ // to: current sp ++ assert(InterpreterRuntime::SignatureHandlerGenerator::to () == SP, "adjust this code"); ++ // temp: T3 ++ assert(InterpreterRuntime::SignatureHandlerGenerator::temp() == t , "adjust this code"); ++ ++ __ jalr(T4); ++ __ get_method(method); ++ ++ // ++ // if native function is static, and its second parameter has type length of double word, ++ // and first parameter has type length of word, we have to reserve one word ++ // for the first parameter, according to LoongArch abi. ++ // if native function is not static, and its third parameter has type length of double word, ++ // and second parameter has type length of word, we have to reserve one word for the second ++ // parameter. ++ // ++ ++ ++ // result handler is in V0 ++ // set result handler ++ __ st_d(V0, FP, (frame::interpreter_frame_result_handler_offset)*wordSize); ++ ++#define FIRSTPARA_SHIFT_COUNT 5 ++#define SECONDPARA_SHIFT_COUNT 9 ++#define THIRDPARA_SHIFT_COUNT 13 ++#define PARA_MASK 0xf ++ ++ // pass mirror handle if static call ++ { ++ Label L; ++ const int mirror_offset = in_bytes(Klass::java_mirror_offset()); ++ __ ld_w(t, method, in_bytes(Method::access_flags_offset())); ++ __ andi(AT, t, JVM_ACC_STATIC); ++ __ beq(AT, R0, L); ++ ++ // get mirror ++ __ ld_d(t, method, in_bytes(Method:: const_offset())); ++ __ ld_d(t, t, in_bytes(ConstMethod::constants_offset())); //?? ++ __ ld_d(t, t, ConstantPool::pool_holder_offset_in_bytes()); ++ __ ld_d(t, t, mirror_offset); ++ // copy mirror into activation frame ++ //__ st_w(t, FP, frame::interpreter_frame_oop_temp_offset * wordSize); ++ // pass handle to mirror ++ __ st_d(t, FP, frame::interpreter_frame_oop_temp_offset * wordSize); ++ __ addi_d(t, FP, frame::interpreter_frame_oop_temp_offset * wordSize); ++ __ move(A1, t); ++ __ bind(L); ++ } ++ ++ // [ mthd holder mirror ptr ] <--- sp --------------------| (only for static method) ++ // [ ] | ++ // ... size of parameters(or +1) | ++ // [ monitor entry ] | ++ // ... | ++ // [ monitor entry ] | ++ // [ monitor block top ] ( the top monitor entry ) | ++ // [ byte code pointer (0) ] (if native, bcp = 0) | ++ // [ constant pool cache ] | ++ // [ Method* ] | ++ // [ locals offset ] | ++ // [ sender's sp ] | ++ // [ sender's fp ] | ++ // [ return address ] <--- fp | ++ // [ method holder mirror ] <----------------------------| ++ // [ result type info ] ++ // [ argumnet word n-1 ] <--- ( sender's sp ) ++ // ... ++ // [ argument word 0 ] <--- S7 ++ ++ // get native function entry point ++ { Label L; ++ __ ld_d(T4, method, in_bytes(Method::native_function_offset())); ++ __ li(T6, SharedRuntime::native_method_throw_unsatisfied_link_error_entry()); ++ __ bne(T6, T4, L); ++ __ call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::prepare_native_call), method); ++ __ get_method(method); ++ __ verify_oop(method); ++ __ ld_d(T4, method, in_bytes(Method::native_function_offset())); ++ __ bind(L); ++ } ++ ++ // pass JNIEnv ++ // native function in T4 ++#ifndef OPT_THREAD ++ __ get_thread(thread); ++#endif ++ __ addi_d(t, thread, in_bytes(JavaThread::jni_environment_offset())); ++ __ move(A0, t); ++ // [ jni environment ] <--- sp ++ // [ mthd holder mirror ptr ] ---------------------------->| (only for static method) ++ // [ ] | ++ // ... size of parameters | ++ // [ monitor entry ] | ++ // ... | ++ // [ monitor entry ] | ++ // [ monitor block top ] ( the top monitor entry ) | ++ // [ byte code pointer (0) ] (if native, bcp = 0) | ++ // [ constant pool cache ] | ++ // [ Method* ] | ++ // [ locals offset ] | ++ // [ sender's sp ] | ++ // [ sender's fp ] | ++ // [ return address ] <--- fp | ++ // [ method holder mirror ] <----------------------------| ++ // [ result type info ] ++ // [ argumnet word n-1 ] <--- ( sender's sp ) ++ // ... ++ // [ argument word 0 ] <--- S7 ++ ++ // Set the last Java PC in the frame anchor to be the return address from ++ // the call to the native method: this will allow the debugger to ++ // generate an accurate stack trace. ++ Label native_return; ++ __ set_last_Java_frame(thread, SP, FP, native_return); ++ ++ // change thread state ++#ifdef ASSERT ++ { ++ Label L; ++ __ ld_w(t, thread, in_bytes(JavaThread::thread_state_offset())); ++ __ addi_d(t, t, (-1) * _thread_in_Java); ++ __ beq(t, R0, L); ++ __ stop("Wrong thread state in native stub"); ++ __ bind(L); ++ } ++#endif ++ ++ __ li(t, _thread_in_native); ++ if (os::is_MP()) { ++ __ membar(Assembler::Membar_mask_bits(__ LoadStore|__ StoreStore)); // store release ++ } ++ __ st_w(t, thread, in_bytes(JavaThread::thread_state_offset())); ++ ++ // call native method ++ __ jalr(T4); ++ __ bind(native_return); ++ // result potentially in V0 or F0 ++ ++ ++ // via _last_native_pc and not via _last_jave_sp ++ // NOTE: the order of theses push(es) is known to frame::interpreter_frame_result. ++ // If the order changes or anything else is added to the stack the code in ++ // interpreter_frame_result will have to be changed. ++ //FIXME, should modify here ++ // save return value to keep the value from being destroyed by other calls ++ __ push(dtos); ++ __ push(ltos); ++ ++ // change thread state ++ __ get_thread(thread); ++ __ li(t, _thread_in_native_trans); ++ if (os::is_MP()) { ++ __ membar(Assembler::Membar_mask_bits(__ LoadStore|__ StoreStore)); // store release ++ } ++ __ st_w(t, thread, in_bytes(JavaThread::thread_state_offset())); ++ ++ if(os::is_MP()) { ++ if (UseMembar) { ++ // Force this write out before the read below ++ __ membar(__ AnyAny); ++ } else { ++ // Write serialization page so VM thread can do a pseudo remote membar. ++ // We use the current thread pointer to calculate a thread specific ++ // offset to write to within the page. This minimizes bus traffic ++ // due to cache line collision. ++ __ serialize_memory(thread, A0); ++ } ++ } ++ ++ // check for safepoint operation in progress and/or pending suspend requests ++ { Label Continue; ++ ++ // Don't use call_VM as it will see a possible pending exception and forward it ++ // and never return here preventing us from clearing _last_native_pc down below. ++ // Also can't use call_VM_leaf either as it will check to see if BCP & LVP are ++ // preserved and correspond to the bcp/locals pointers. So we do a runtime call ++ // by hand. ++ // ++ Label L; ++ __ li(AT, SafepointSynchronize::address_of_state()); ++ __ ld_w(AT, AT, 0); ++ __ bne(AT, R0, L); ++ __ ld_w(AT, thread, in_bytes(JavaThread::suspend_flags_offset())); ++ __ beq(AT, R0, Continue); ++ __ bind(L); ++ __ move(A0, thread); ++ __ call(CAST_FROM_FN_PTR(address, JavaThread::check_special_condition_for_native_trans), ++ relocInfo::runtime_call_type); ++ ++#ifndef OPT_THREAD ++ __ get_thread(thread); ++#endif ++ //add for compressedoops ++ __ reinit_heapbase(); ++ __ bind(Continue); ++ } ++ ++ // change thread state ++ __ li(t, _thread_in_Java); ++ if (os::is_MP()) { ++ __ membar(Assembler::Membar_mask_bits(__ LoadStore|__ StoreStore)); // store release ++ } ++ __ st_w(t, thread, in_bytes(JavaThread::thread_state_offset())); ++ __ reset_last_Java_frame(thread, true); ++ ++ // reset handle block ++ __ ld_d(t, thread, in_bytes(JavaThread::active_handles_offset())); ++ __ st_w(R0, t, JNIHandleBlock::top_offset_in_bytes()); ++ ++ // If result was an oop then unbox and save it in the frame ++ { ++ Label no_oop; ++ //FIXME, addi only support 12-bit imeditate ++ __ ld_d(AT, FP, frame::interpreter_frame_result_handler_offset*wordSize); ++ __ li(T0, AbstractInterpreter::result_handler(T_OBJECT)); ++ __ bne(AT, T0, no_oop); ++ __ pop(ltos); ++ // Unbox oop result, e.g. JNIHandles::resolve value. ++ __ resolve_jobject(V0, thread, T4); ++ __ st_d(V0, FP, (frame::interpreter_frame_oop_temp_offset)*wordSize); ++ // keep stack depth as expected by pushing oop which will eventually be discarded ++ __ push(ltos); ++ __ bind(no_oop); ++ } ++ { ++ Label no_reguard; ++ __ ld_w(t, thread, in_bytes(JavaThread::stack_guard_state_offset())); ++ __ li(AT,(int) JavaThread::stack_guard_yellow_disabled); ++ __ bne(t, AT, no_reguard); ++ __ pushad(); ++ __ move(S5_heapbase, SP); ++ __ li(AT, -StackAlignmentInBytes); ++ __ andr(SP, SP, AT); ++ __ call(CAST_FROM_FN_PTR(address, SharedRuntime::reguard_yellow_pages), relocInfo::runtime_call_type); ++ __ move(SP, S5_heapbase); ++ __ popad(); ++ //add for compressedoops ++ __ reinit_heapbase(); ++ __ bind(no_reguard); ++ } ++ // restore BCP to have legal interpreter frame, ++ // i.e., bci == 0 <=> BCP == code_base() ++ // Can't call_VM until bcp is within reasonable. ++ __ get_method(method); // method is junk from thread_in_native to now. ++ __ verify_oop(method); ++ __ ld_d(BCP, method, in_bytes(Method::const_offset())); ++ __ lea(BCP, Address(BCP, in_bytes(ConstMethod::codes_offset()))); ++ // handle exceptions (exception handling will handle unlocking!) ++ { ++ Label L; ++ __ ld_d(t, thread, in_bytes(Thread::pending_exception_offset())); ++ __ beq(t, R0, L); ++ // Note: At some point we may want to unify this with the code used in ++ // call_VM_base(); ++ // i.e., we should use the StubRoutines::forward_exception code. For now this ++ // doesn't work here because the sp is not correctly set at this point. ++ __ MacroAssembler::call_VM(noreg, ++ CAST_FROM_FN_PTR(address, ++ InterpreterRuntime::throw_pending_exception)); ++ __ should_not_reach_here(); ++ __ bind(L); ++ } ++ ++ // do unlocking if necessary ++ { ++ Label L; ++ __ ld_w(t, method, in_bytes(Method::access_flags_offset())); ++ __ andi(t, t, JVM_ACC_SYNCHRONIZED); ++ __ addi_d(c_rarg0, FP, frame::interpreter_frame_initial_sp_offset * wordSize - (int)sizeof(BasicObjectLock)); ++ __ beq(t, R0, L); ++ // the code below should be shared with interpreter macro assembler implementation ++ { ++ Label unlock; ++ // BasicObjectLock will be first in list, ++ // since this is a synchronized method. However, need ++ // to check that the object has not been unlocked by ++ // an explicit monitorexit bytecode. ++ // address of first monitor ++ ++ __ ld_d(t, c_rarg0, BasicObjectLock::obj_offset_in_bytes()); ++ __ bne(t, R0, unlock); ++ ++ // Entry already unlocked, need to throw exception ++ __ MacroAssembler::call_VM(NOREG, CAST_FROM_FN_PTR(address, ++ InterpreterRuntime::throw_illegal_monitor_state_exception)); ++ __ should_not_reach_here(); ++ ++ __ bind(unlock); ++ __ unlock_object(c_rarg0); ++ } ++ __ bind(L); ++ } ++ ++ // jvmti/jvmpi support ++ // Note: This must happen _after_ handling/throwing any exceptions since ++ // the exception handler code notifies the runtime of method exits ++ // too. If this happens before, method entry/exit notifications are ++ // not properly paired (was bug - gri 11/22/99). ++ __ notify_method_exit(vtos, InterpreterMacroAssembler::NotifyJVMTI); ++ ++ // restore potential result in V0, ++ // call result handler to restore potential result in ST0 & handle result ++ ++ __ pop(ltos); ++ __ pop(dtos); ++ ++ __ ld_d(t, FP, (frame::interpreter_frame_result_handler_offset) * wordSize); ++ __ jalr(t); ++ ++ ++ // remove activation ++ __ ld_d(SP, FP, frame::interpreter_frame_sender_sp_offset * wordSize); // get sender sp ++ __ ld_d(RA, FP, frame::interpreter_frame_return_addr_offset * wordSize); // get return address ++ __ ld_d(FP, FP, frame::interpreter_frame_sender_fp_offset * wordSize); // restore sender's fp ++ __ jr(RA); ++ ++#ifndef CORE ++ if (inc_counter) { ++ // Handle overflow of counter and compile method ++ __ bind(invocation_counter_overflow); ++ generate_counter_overflow(&continue_after_compile); ++ // entry_point is the beginning of this ++ // function and checks again for compiled code ++ } ++#endif ++ return entry_point; ++} ++ ++// ++// Generic interpreted method entry to (asm) interpreter ++// ++// Layout of frame just at the entry ++// ++// [ argument word n-1 ] <--- sp ++// ... ++// [ argument word 0 ] ++// assume Method* in Rmethod before call this method. ++// prerequisites to the generated stub : the callee Method* in Rmethod ++// note you must save the caller bcp before call the generated stub ++// ++address InterpreterGenerator::generate_normal_entry(bool synchronized) { ++ // determine code generation flags ++ bool inc_counter = UseCompiler || CountCompiledCalls; ++ ++ // Rmethod: Method* ++ // Rsender: sender 's sp ++ address entry_point = __ pc(); ++ ++ const Address invocation_counter(Rmethod, ++ in_bytes(MethodCounters::invocation_counter_offset() + InvocationCounter::counter_offset())); ++ ++ // get parameter size (always needed) ++ __ ld_d(T3, Rmethod, in_bytes(Method::const_offset())); //T3 --> Rmethod._constMethod ++ __ ld_hu(V0, T3, in_bytes(ConstMethod::size_of_parameters_offset())); ++ ++ // Rmethod: Method* ++ // V0: size of parameters ++ // Rsender: sender 's sp ,could be different frome sp+ wordSize if we call via c2i ++ // get size of locals in words to T2 ++ __ ld_hu(T2, T3, in_bytes(ConstMethod::size_of_locals_offset())); ++ // T2 = no. of additional locals, locals include parameters ++ __ sub_d(T2, T2, V0); ++ ++ // see if we've got enough room on the stack for locals plus overhead. ++ // Layout of frame at this point ++ // ++ // [ argument word n-1 ] <--- sp ++ // ... ++ // [ argument word 0 ] ++ generate_stack_overflow_check(); ++ // after this function, the layout of frame does not change ++ ++ // compute beginning of parameters (LVP) ++ __ slli_d(LVP, V0, LogBytesPerWord); ++ __ addi_d(LVP, LVP, (-1) * wordSize); ++ __ add_d(LVP, LVP, SP); ++ ++ // T2 - # of additional locals ++ // allocate space for locals ++ // explicitly initialize locals ++ { ++ Label exit, loop; ++ __ beq(T2, R0, exit); ++ ++ __ bind(loop); ++ __ addi_d(SP, SP, (-1) * wordSize); ++ __ addi_d(T2, T2, -1); // until everything initialized ++ __ st_d(R0, SP, 0); // initialize local variables ++ __ bne(T2, R0, loop); ++ ++ __ bind(exit); ++ } ++ ++ // ++ // [ local var m-1 ] <--- sp ++ // ... ++ // [ local var 0 ] ++ // [ argument word n-1 ] <--- T0? ++ // ... ++ // [ argument word 0 ] <--- LVP ++ ++ // initialize fixed part of activation frame ++ ++ generate_fixed_frame(false); ++ ++ ++ // after this function, the layout of frame is as following ++ // ++ // [ monitor block top ] <--- sp ( the top monitor entry ) ++ // [ byte code pointer ] (if native, bcp = 0) ++ // [ constant pool cache ] ++ // [ Method* ] ++ // [ locals offset ] ++ // [ sender's sp ] ++ // [ sender's fp ] <--- fp ++ // [ return address ] ++ // [ local var m-1 ] ++ // ... ++ // [ local var 0 ] ++ // [ argumnet word n-1 ] <--- ( sender's sp ) ++ // ... ++ // [ argument word 0 ] <--- LVP ++ ++ ++ // make sure method is not native & not abstract ++#ifdef ASSERT ++ __ ld_d(AT, Rmethod, in_bytes(Method::access_flags_offset())); ++ { ++ Label L; ++ __ andi(T2, AT, JVM_ACC_NATIVE); ++ __ beq(T2, R0, L); ++ __ stop("tried to execute native method as non-native"); ++ __ bind(L); ++ } ++ { ++ Label L; ++ __ andi(T2, AT, JVM_ACC_ABSTRACT); ++ __ beq(T2, R0, L); ++ __ stop("tried to execute abstract method in interpreter"); ++ __ bind(L); ++ } ++#endif ++ ++ // Since at this point in the method invocation the exception handler ++ // would try to exit the monitor of synchronized methods which hasn't ++ // been entered yet, we set the thread local variable ++ // _do_not_unlock_if_synchronized to true. The remove_activation will ++ // check this flag. ++ ++#ifndef OPT_THREAD ++ Register thread = T8; ++ __ get_thread(thread); ++#else ++ Register thread = TREG; ++#endif ++ __ li(AT, (int)true); ++ __ st_b(AT, thread, in_bytes(JavaThread::do_not_unlock_if_synchronized_offset())); ++ ++#ifndef CORE ++ ++ // mdp : T8 ++ // tmp1: T4 ++ // tmp2: T2 ++ __ profile_parameters_type(T8, T4, T2); ++ ++ // increment invocation count & check for overflow ++ Label invocation_counter_overflow; ++ Label profile_method; ++ Label profile_method_continue; ++ if (inc_counter) { ++ generate_counter_incr(&invocation_counter_overflow, ++ &profile_method, ++ &profile_method_continue); ++ if (ProfileInterpreter) { ++ __ bind(profile_method_continue); ++ } ++ } ++ ++ Label continue_after_compile; ++ __ bind(continue_after_compile); ++ ++#endif // CORE ++ ++ bang_stack_shadow_pages(false); ++ ++ // reset the _do_not_unlock_if_synchronized flag ++#ifndef OPT_THREAD ++ __ get_thread(thread); ++#endif ++ __ st_b(R0, thread, in_bytes(JavaThread::do_not_unlock_if_synchronized_offset())); ++ ++ // check for synchronized methods ++ // Must happen AFTER invocation_counter check and stack overflow check, ++ // so method is not locked if overflows. ++ // ++ if (synchronized) { ++ // Allocate monitor and lock method ++ lock_method(); ++ } else { ++ // no synchronization necessary ++#ifdef ASSERT ++ { Label L; ++ __ ld_w(AT, Rmethod, in_bytes(Method::access_flags_offset())); ++ __ andi(T2, AT, JVM_ACC_SYNCHRONIZED); ++ __ beq(T2, R0, L); ++ __ stop("method needs synchronization"); ++ __ bind(L); ++ } ++#endif ++ } ++ ++ // layout of frame after lock_method ++ // [ monitor entry ] <--- sp ++ // ... ++ // [ monitor entry ] ++ // [ monitor block top ] ( the top monitor entry ) ++ // [ byte code pointer ] (if native, bcp = 0) ++ // [ constant pool cache ] ++ // [ Method* ] ++ // [ locals offset ] ++ // [ sender's sp ] ++ // [ sender's fp ] ++ // [ return address ] <--- fp ++ // [ local var m-1 ] ++ // ... ++ // [ local var 0 ] ++ // [ argumnet word n-1 ] <--- ( sender's sp ) ++ // ... ++ // [ argument word 0 ] <--- LVP ++ ++ ++ // start execution ++#ifdef ASSERT ++ { ++ Label L; ++ __ ld_d(AT, FP, frame::interpreter_frame_monitor_block_top_offset * wordSize); ++ __ beq(AT, SP, L); ++ __ stop("broken stack frame setup in interpreter in native"); ++ __ bind(L); ++ } ++#endif ++ ++ // jvmti/jvmpi support ++ __ notify_method_entry(); ++ ++ __ dispatch_next(vtos); ++ ++ // invocation counter overflow ++ if (inc_counter) { ++ if (ProfileInterpreter) { ++ // We have decided to profile this method in the interpreter ++ __ bind(profile_method); ++ __ call_VM(noreg, CAST_FROM_FN_PTR(address, ++ InterpreterRuntime::profile_method)); ++ __ set_method_data_pointer_for_bcp(); ++ __ get_method(Rmethod); ++ __ b(profile_method_continue); ++ } ++ // Handle overflow of counter and compile method ++ __ bind(invocation_counter_overflow); ++ generate_counter_overflow(&continue_after_compile); ++ } ++ ++ return entry_point; ++} ++ ++// Entry points ++// ++// Here we generate the various kind of entries into the interpreter. ++// The two main entry type are generic bytecode methods and native ++// call method. These both come in synchronized and non-synchronized ++// versions but the frame layout they create is very similar. The ++// other method entry types are really just special purpose entries ++// that are really entry and interpretation all in one. These are for ++// trivial methods like accessor, empty, or special math methods. ++// ++// When control flow reaches any of the entry types for the interpreter ++// the following holds -> ++// ++// Arguments: ++// ++// Rmethod: Method* ++// V0: receiver ++// ++// ++// Stack layout immediately at entry ++// ++// [ parameter n-1 ] <--- sp ++// ... ++// [ parameter 0 ] ++// [ expression stack ] (caller's java expression stack) ++ ++// Assuming that we don't go to one of the trivial specialized entries ++// the stack will look like below when we are ready to execute the ++// first bytecode (or call the native routine). The register usage ++// will be as the template based interpreter expects (see ++// interpreter_loongarch_64.hpp). ++// ++// local variables follow incoming parameters immediately; i.e. ++// the return address is moved to the end of the locals). ++// ++// [ monitor entry ] <--- sp ++// ... ++// [ monitor entry ] ++// [ monitor block top ] ( the top monitor entry ) ++// [ byte code pointer ] (if native, bcp = 0) ++// [ constant pool cache ] ++// [ Method* ] ++// [ locals offset ] ++// [ sender's sp ] ++// [ sender's fp ] ++// [ return address ] <--- fp ++// [ local var m-1 ] ++// ... ++// [ local var 0 ] ++// [ argumnet word n-1 ] <--- ( sender's sp ) ++// ... ++// [ argument word 0 ] <--- S7 ++ ++address AbstractInterpreterGenerator::generate_method_entry( ++ AbstractInterpreter::MethodKind kind) { ++ // determine code generation flags ++ bool synchronized = false; ++ address entry_point = NULL; ++ switch (kind) { ++ case Interpreter::zerolocals : ++ break; ++ case Interpreter::zerolocals_synchronized: ++ synchronized = true; ++ break; ++ case Interpreter::native : ++ entry_point = ((InterpreterGenerator*)this)->generate_native_entry(false); ++ break; ++ case Interpreter::native_synchronized : ++ entry_point = ((InterpreterGenerator*)this)->generate_native_entry(true); ++ break; ++ case Interpreter::empty : ++ entry_point = ((InterpreterGenerator*)this)->generate_empty_entry(); ++ break; ++ case Interpreter::accessor : ++ entry_point = ((InterpreterGenerator*)this)->generate_accessor_entry(); ++ break; ++ case Interpreter::abstract : ++ entry_point = ((InterpreterGenerator*)this)->generate_abstract_entry(); ++ break; ++ ++ case Interpreter::java_lang_math_sin : // fall thru ++ case Interpreter::java_lang_math_cos : // fall thru ++ case Interpreter::java_lang_math_tan : // fall thru ++ case Interpreter::java_lang_math_log : // fall thru ++ case Interpreter::java_lang_math_log10 : // fall thru ++ case Interpreter::java_lang_math_pow : // fall thru ++ case Interpreter::java_lang_math_exp : break; ++ case Interpreter::java_lang_math_abs : // fall thru ++ case Interpreter::java_lang_math_sqrt : ++ entry_point = ((InterpreterGenerator*)this)->generate_math_entry(kind); break; ++ case Interpreter::java_lang_ref_reference_get: ++ entry_point = ((InterpreterGenerator*)this)->generate_Reference_get_entry(); break; ++ case Interpreter::java_util_zip_CRC32_update: ++ entry_point = ((InterpreterGenerator*)this)->generate_CRC32_update_entry(); break; ++ case Interpreter::java_util_zip_CRC32_updateBytes: // fall thru ++ case Interpreter::java_util_zip_CRC32_updateByteBuffer: ++ entry_point = ((InterpreterGenerator*)this)->generate_CRC32_updateBytes_entry(kind); break; ++ default: ++ fatal(err_msg("unexpected method kind: %d", kind)); ++ break; ++ } ++ if (entry_point) return entry_point; ++ ++ return ((InterpreterGenerator*)this)->generate_normal_entry(synchronized); ++} ++ ++// These should never be compiled since the interpreter will prefer ++// the compiled version to the intrinsic version. ++bool AbstractInterpreter::can_be_compiled(methodHandle m) { ++ switch (method_kind(m)) { ++ case Interpreter::java_lang_math_sin : // fall thru ++ case Interpreter::java_lang_math_cos : // fall thru ++ case Interpreter::java_lang_math_tan : // fall thru ++ case Interpreter::java_lang_math_abs : // fall thru ++ case Interpreter::java_lang_math_log : // fall thru ++ case Interpreter::java_lang_math_log10 : // fall thru ++ case Interpreter::java_lang_math_sqrt : // fall thru ++ case Interpreter::java_lang_math_pow : // fall thru ++ case Interpreter::java_lang_math_exp : ++ return false; ++ default: ++ return true; ++ } ++} ++ ++// How much stack a method activation needs in words. ++int AbstractInterpreter::size_top_interpreter_activation(Method* method) { ++ ++ const int entry_size = frame::interpreter_frame_monitor_size(); ++ ++ // total overhead size: entry_size + (saved fp thru expr stack bottom). ++ // be sure to change this if you add/subtract anything to/from the overhead area ++ const int overhead_size = -(frame::interpreter_frame_initial_sp_offset) + entry_size; ++ ++ const int stub_code = 6; // see generate_call_stub ++ // return overhead_size + method->max_locals() + method->max_stack() + stub_code; ++ const int method_stack = (method->max_locals() + method->max_stack()) * ++ Interpreter::stackElementWords; ++ return overhead_size + method_stack + stub_code; ++} ++ ++void AbstractInterpreter::layout_activation(Method* method, ++ int tempcount, ++ int popframe_extra_args, ++ int moncount, ++ int caller_actual_parameters, ++ int callee_param_count, ++ int callee_locals, ++ frame* caller, ++ frame* interpreter_frame, ++ bool is_top_frame, ++ bool is_bottom_frame) { ++ // Note: This calculation must exactly parallel the frame setup ++ // in AbstractInterpreterGenerator::generate_method_entry. ++ // If interpreter_frame!=NULL, set up the method, locals, and monitors. ++ // The frame interpreter_frame, if not NULL, is guaranteed to be the ++ // right size, as determined by a previous call to this method. ++ // It is also guaranteed to be walkable even though it is in a skeletal state ++ ++ // fixed size of an interpreter frame: ++ ++ int max_locals = method->max_locals() * Interpreter::stackElementWords; ++ int extra_locals = (method->max_locals() - method->size_of_parameters()) * Interpreter::stackElementWords; ++ ++#ifdef ASSERT ++ if (!EnableInvokeDynamic) { ++ // @@@ FIXME: Should we correct interpreter_frame_sender_sp in the calling sequences? ++ // Probably, since deoptimization doesn't work yet. ++ assert(caller->unextended_sp() == interpreter_frame->interpreter_frame_sender_sp(), "Frame not properly walkable"); ++ } ++ assert(caller->sp() == interpreter_frame->sender_sp(), "Frame not properly walkable(2)"); ++#endif ++ ++ interpreter_frame->interpreter_frame_set_method(method); ++ // NOTE the difference in using sender_sp and interpreter_frame_sender_sp ++ // interpreter_frame_sender_sp is the original sp of the caller (the unextended_sp) ++ // and sender_sp is fp+8 ++ intptr_t* locals = interpreter_frame->sender_sp() + max_locals - 1; ++ ++#ifdef ASSERT ++ if (caller->is_interpreted_frame()) { ++ assert(locals < caller->fp() + frame::interpreter_frame_initial_sp_offset, "bad placement"); ++ } ++#endif ++ ++ interpreter_frame->interpreter_frame_set_locals(locals); ++ BasicObjectLock* montop = interpreter_frame->interpreter_frame_monitor_begin(); ++ BasicObjectLock* monbot = montop - moncount; ++ interpreter_frame->interpreter_frame_set_monitor_end(montop - moncount); ++ ++ //set last sp; ++ intptr_t* sp = (intptr_t*) monbot - tempcount*Interpreter::stackElementWords - ++ popframe_extra_args; ++ interpreter_frame->interpreter_frame_set_last_sp(sp); ++ // All frames but the initial interpreter frame we fill in have a ++ // value for sender_sp that allows walking the stack but isn't ++ // truly correct. Correct the value here. ++ // ++ if (extra_locals != 0 && ++ interpreter_frame->sender_sp() == interpreter_frame->interpreter_frame_sender_sp() ) { ++ interpreter_frame->set_interpreter_frame_sender_sp(caller->sp() + extra_locals); ++ } ++ *interpreter_frame->interpreter_frame_cache_addr() = method->constants()->cache(); ++} ++ ++//----------------------------------------------------------------------------- ++// Exceptions ++ ++void TemplateInterpreterGenerator::generate_throw_exception() { ++ // Entry point in previous activation (i.e., if the caller was ++ // interpreted) ++ Interpreter::_rethrow_exception_entry = __ pc(); ++ // Restore sp to interpreter_frame_last_sp even though we are going ++ // to empty the expression stack for the exception processing. ++ __ st_d(R0,FP, frame::interpreter_frame_last_sp_offset * wordSize); ++ ++ // V0: exception ++ // V1: return address/pc that threw exception ++ __ restore_bcp(); // BCP points to call/send ++ __ restore_locals(); ++ ++ //add for compressedoops ++ __ reinit_heapbase(); ++ // Entry point for exceptions thrown within interpreter code ++ Interpreter::_throw_exception_entry = __ pc(); ++ // expression stack is undefined here ++ // V0: exception ++ // BCP: exception bcp ++ __ verify_oop(V0); ++ ++ // expression stack must be empty before entering the VM in case of an exception ++ __ empty_expression_stack(); ++ // find exception handler address and preserve exception oop ++ __ move(A1, V0); ++ __ call_VM(V1, CAST_FROM_FN_PTR(address, InterpreterRuntime::exception_handler_for_exception), A1); ++ // V0: exception handler entry point ++ // V1: preserved exception oop ++ // S0: bcp for exception handler ++ __ push(V1); // push exception which is now the only value on the stack ++ __ jr(V0); // jump to exception handler (may be _remove_activation_entry!) ++ ++ // If the exception is not handled in the current frame the frame is removed and ++ // the exception is rethrown (i.e. exception continuation is _rethrow_exception). ++ // ++ // Note: At this point the bci is still the bxi for the instruction which caused ++ // the exception and the expression stack is empty. Thus, for any VM calls ++ // at this point, GC will find a legal oop map (with empty expression stack). ++ ++ // In current activation ++ // V0: exception ++ // BCP: exception bcp ++ ++ // ++ // JVMTI PopFrame support ++ // ++ ++ Interpreter::_remove_activation_preserving_args_entry = __ pc(); ++ __ empty_expression_stack(); ++ // Set the popframe_processing bit in pending_popframe_condition indicating that we are ++ // currently handling popframe, so that call_VMs that may happen later do not trigger new ++ // popframe handling cycles. ++#ifndef OPT_THREAD ++ Register thread = T2; ++ __ get_thread(T2); ++#else ++ Register thread = TREG; ++#endif ++ __ ld_w(T3, thread, in_bytes(JavaThread::popframe_condition_offset())); ++ __ ori(T3, T3, JavaThread::popframe_processing_bit); ++ __ st_w(T3, thread, in_bytes(JavaThread::popframe_condition_offset())); ++ ++#ifndef CORE ++ { ++ // Check to see whether we are returning to a deoptimized frame. ++ // (The PopFrame call ensures that the caller of the popped frame is ++ // either interpreted or compiled and deoptimizes it if compiled.) ++ // In this case, we can't call dispatch_next() after the frame is ++ // popped, but instead must save the incoming arguments and restore ++ // them after deoptimization has occurred. ++ // ++ // Note that we don't compare the return PC against the ++ // deoptimization blob's unpack entry because of the presence of ++ // adapter frames in C2. ++ Label caller_not_deoptimized; ++ __ ld_d(A0, FP, frame::return_addr_offset * wordSize); ++ __ super_call_VM_leaf(CAST_FROM_FN_PTR(address, InterpreterRuntime::interpreter_contains), A0); ++ __ bne(V0, R0, caller_not_deoptimized); ++ ++ // Compute size of arguments for saving when returning to deoptimized caller ++ __ get_method(A1); ++ __ verify_oop(A1); ++ __ ld_d(A1, A1, in_bytes(Method::const_offset())); ++ __ ld_hu(A1, A1, in_bytes(ConstMethod::size_of_parameters_offset())); ++ __ shl(A1, Interpreter::logStackElementSize); ++ __ restore_locals(); ++ __ sub_d(A2, LVP, A1); ++ __ addi_d(A2, A2, wordSize); ++ // Save these arguments ++#ifndef OPT_THREAD ++ __ get_thread(A0); ++#else ++ __ move(A0, TREG); ++#endif ++ __ super_call_VM_leaf(CAST_FROM_FN_PTR(address, Deoptimization::popframe_preserve_args), A0, A1, A2); ++ ++ __ remove_activation(vtos, T4, false, false, false); ++ ++ // Inform deoptimization that it is responsible for restoring these arguments ++#ifndef OPT_THREAD ++ __ get_thread(thread); ++#endif ++ __ li(AT, JavaThread::popframe_force_deopt_reexecution_bit); ++ __ st_w(AT, thread, in_bytes(JavaThread::popframe_condition_offset())); ++ // Continue in deoptimization handler ++ __ jr(T4); ++ ++ __ bind(caller_not_deoptimized); ++ } ++#endif /* !CORE */ ++ ++ __ remove_activation(vtos, T3, ++ /* throw_monitor_exception */ false, ++ /* install_monitor_exception */ false, ++ /* notify_jvmdi */ false); ++ ++ // Clear the popframe condition flag ++ // Finish with popframe handling ++ // A previous I2C followed by a deoptimization might have moved the ++ // outgoing arguments further up the stack. PopFrame expects the ++ // mutations to those outgoing arguments to be preserved and other ++ // constraints basically require this frame to look exactly as ++ // though it had previously invoked an interpreted activation with ++ // no space between the top of the expression stack (current ++ // last_sp) and the top of stack. Rather than force deopt to ++ // maintain this kind of invariant all the time we call a small ++ // fixup routine to move the mutated arguments onto the top of our ++ // expression stack if necessary. ++ __ move(T8, SP); ++ __ ld_d(A2, FP, frame::interpreter_frame_last_sp_offset * wordSize); ++#ifndef OPT_THREAD ++ __ get_thread(thread); ++#endif ++ // PC must point into interpreter here ++ Label L; ++ __ bind(L); ++ __ set_last_Java_frame(thread, noreg, FP, L); ++ __ super_call_VM_leaf(CAST_FROM_FN_PTR(address, InterpreterRuntime::popframe_move_outgoing_args), thread, T8, A2); ++ __ reset_last_Java_frame(thread, true); ++ // Restore the last_sp and null it out ++ __ ld_d(SP, FP, frame::interpreter_frame_last_sp_offset * wordSize); ++ __ st_d(R0, FP, frame::interpreter_frame_last_sp_offset * wordSize); ++ ++ ++ ++ __ li(AT, JavaThread::popframe_inactive); ++ __ st_w(AT, thread, in_bytes(JavaThread::popframe_condition_offset())); ++ ++ // Finish with popframe handling ++ __ restore_bcp(); ++ __ restore_locals(); ++#ifndef CORE ++ // The method data pointer was incremented already during ++ // call profiling. We have to restore the mdp for the current bcp. ++ if (ProfileInterpreter) { ++ __ set_method_data_pointer_for_bcp(); ++ } ++#endif // !CORE ++ // Clear the popframe condition flag ++#ifndef OPT_THREAD ++ __ get_thread(thread); ++#endif ++ __ li(AT, JavaThread::popframe_inactive); ++ __ st_w(AT, thread, in_bytes(JavaThread::popframe_condition_offset())); ++ ++#if INCLUDE_JVMTI ++ { ++ Label L_done; ++ ++ __ ld_bu(AT, BCP, 0); ++ __ addi_d(AT, AT, -1 * Bytecodes::_invokestatic); ++ __ bne(AT, R0, L_done); ++ ++ // The member name argument must be restored if _invokestatic is re-executed after a PopFrame call. ++ // Detect such a case in the InterpreterRuntime function and return the member name argument, or NULL. ++ ++ __ get_method(T4); ++ __ ld_d(T8, LVP, 0); ++ __ call_VM(T8, CAST_FROM_FN_PTR(address, InterpreterRuntime::member_name_arg_or_null), T8, T4, BCP); ++ ++ __ beq(T8, R0, L_done); ++ ++ __ st_d(T8, SP, 0); ++ __ bind(L_done); ++ } ++#endif // INCLUDE_JVMTI ++ ++ __ dispatch_next(vtos); ++ // end of PopFrame support ++ ++ Interpreter::_remove_activation_entry = __ pc(); ++ ++ // preserve exception over this code sequence ++ __ pop(T0); ++#ifndef OPT_THREAD ++ __ get_thread(thread); ++#endif ++ __ st_d(T0, thread, in_bytes(JavaThread::vm_result_offset())); ++ // remove the activation (without doing throws on illegalMonitorExceptions) ++ __ remove_activation(vtos, T3, false, true, false); ++ // restore exception ++ __ get_vm_result(T0, thread); ++ __ verify_oop(T0); ++ ++ // In between activations - previous activation type unknown yet ++ // compute continuation point - the continuation point expects ++ // the following registers set up: ++ // ++ // T0: exception ++ // T1: return address/pc that threw exception ++ // SP: expression stack of caller ++ // FP: fp of caller ++ __ push2(T0, T3); // save exception and return address ++ __ move(A1, T3); ++ __ super_call_VM_leaf(CAST_FROM_FN_PTR(address, SharedRuntime::exception_handler_for_return_address), thread, A1); ++ __ move(T4, V0); // save exception handler ++ __ pop2(V0, V1); // restore return address and exception ++ ++ // Note that an "issuing PC" is actually the next PC after the call ++ __ jr(T4); // jump to exception handler of caller ++} ++ ++ ++// ++// JVMTI ForceEarlyReturn support ++// ++address TemplateInterpreterGenerator::generate_earlyret_entry_for(TosState state) { ++ address entry = __ pc(); ++ __ restore_bcp(); ++ __ restore_locals(); ++ __ empty_expression_stack(); ++ __ empty_FPU_stack(); ++ __ load_earlyret_value(state); ++ ++#ifndef OPT_THREAD ++ __ get_thread(TREG); ++#endif ++ __ ld_ptr(T4, TREG, in_bytes(JavaThread::jvmti_thread_state_offset())); ++ const Address cond_addr(T4, in_bytes(JvmtiThreadState::earlyret_state_offset())); ++ // Clear the earlyret state ++ __ li(AT, JvmtiThreadState::earlyret_inactive); ++ __ st_w(AT, cond_addr); ++ __ membar(__ AnyAny);//no membar here for aarch64 ++ ++ ++ __ remove_activation(state, T0, ++ false, /* throw_monitor_exception */ ++ false, /* install_monitor_exception */ ++ true); /* notify_jvmdi */ ++ __ membar(__ AnyAny); ++ __ jr(T0); ++ ++ return entry; ++} // end of ForceEarlyReturn support ++ ++ ++//----------------------------------------------------------------------------- ++// Helper for vtos entry point generation ++ ++void TemplateInterpreterGenerator::set_vtos_entry_points(Template* t, ++ address& bep, ++ address& cep, ++ address& sep, ++ address& aep, ++ address& iep, ++ address& lep, ++ address& fep, ++ address& dep, ++ address& vep) { ++ assert(t->is_valid() && t->tos_in() == vtos, "illegal template"); ++ Label L; ++ fep = __ pc(); __ push(ftos); __ b(L); ++ dep = __ pc(); __ push(dtos); __ b(L); ++ lep = __ pc(); __ push(ltos); __ b(L); ++ aep =__ pc(); __ push(atos); __ b(L); ++ bep = cep = sep = ++ iep = __ pc(); __ push(itos); ++ vep = __ pc(); ++ __ bind(L); ++ generate_and_dispatch(t); ++} ++ ++ ++//----------------------------------------------------------------------------- ++// Generation of individual instructions ++ ++// helpers for generate_and_dispatch ++ ++ ++InterpreterGenerator::InterpreterGenerator(StubQueue* code) ++ : TemplateInterpreterGenerator(code) { ++ generate_all(); // down here so it can be "virtual" ++} ++ ++//----------------------------------------------------------------------------- ++ ++// Non-product code ++#ifndef PRODUCT ++address TemplateInterpreterGenerator::generate_trace_code(TosState state) { ++ address entry = __ pc(); ++ ++ // prepare expression stack ++ __ push(state); // save tosca ++ ++ // tos & tos2 ++ // trace_bytecode need actually 4 args, the last two is tos&tos2 ++ // this work fine for x86. but LA ABI calling convention will store A2-A3 ++ // to the stack position it think is the tos&tos2 ++ // when the expression stack have no more than 2 data, error occur. ++ __ ld_d(A2, SP, 0); ++ __ ld_d(A3, SP, 1 * wordSize); ++ ++ // pass arguments & call tracer ++ __ call_VM(noreg, CAST_FROM_FN_PTR(address, SharedRuntime::trace_bytecode), RA, A2, A3); ++ __ move(RA, V0); // make sure return address is not destroyed by pop(state) ++ ++ // restore expression stack ++ __ pop(state); // restore tosca ++ ++ // return ++ __ jr(RA); ++ return entry; ++} ++ ++void TemplateInterpreterGenerator::count_bytecode() { ++ __ li(T8, (long)&BytecodeCounter::_counter_value); ++ __ ld_w(AT, T8, 0); ++ __ addi_d(AT, AT, 1); ++ __ st_w(AT, T8, 0); ++} ++ ++void TemplateInterpreterGenerator::histogram_bytecode(Template* t) { ++ __ li(T8, (long)&BytecodeHistogram::_counters[t->bytecode()]); ++ __ ld_w(AT, T8, 0); ++ __ addi_d(AT, AT, 1); ++ __ st_w(AT, T8, 0); ++} ++ ++void TemplateInterpreterGenerator::histogram_bytecode_pair(Template* t) { ++ __ li(T8, (long)&BytecodePairHistogram::_index); ++ __ ld_w(T4, T8, 0); ++ __ srli_d(T4, T4, BytecodePairHistogram::log2_number_of_codes); ++ __ li(T8, ((long)t->bytecode()) << BytecodePairHistogram::log2_number_of_codes); ++ __ orr(T4, T4, T8); ++ __ li(T8, (long)&BytecodePairHistogram::_index); ++ __ st_w(T4, T8, 0); ++ __ slli_d(T4, T4, 2); ++ __ li(T8, (long)BytecodePairHistogram::_counters); ++ __ add_d(T8, T8, T4); ++ __ ld_w(AT, T8, 0); ++ __ addi_d(AT, AT, 1); ++ __ st_w(AT, T8, 0); ++} ++ ++ ++void TemplateInterpreterGenerator::trace_bytecode(Template* t) { ++ // Call a little run-time stub to avoid blow-up for each bytecode. ++ // The run-time runtime saves the right registers, depending on ++ // the tosca in-state for the given template. ++ address entry = Interpreter::trace_code(t->tos_in()); ++ assert(entry != NULL, "entry must have been generated"); ++ __ call(entry, relocInfo::none); ++ //add for compressedoops ++ __ reinit_heapbase(); ++} ++ ++ ++void TemplateInterpreterGenerator::stop_interpreter_at() { ++ Label L; ++ __ li(T8, long(&BytecodeCounter::_counter_value)); ++ __ ld_w(T8, T8, 0); ++ __ li(AT, StopInterpreterAt); ++ __ bne(T8, AT, L); ++ __ brk(5); ++ __ bind(L); ++} ++#endif // !PRODUCT ++#endif // ! CC_INTERP +diff --git a/hotspot/src/cpu/loongarch/vm/templateTable_loongarch_64.cpp b/hotspot/src/cpu/loongarch/vm/templateTable_loongarch_64.cpp +new file mode 100644 +index 0000000000..5f6b706258 +--- /dev/null ++++ b/hotspot/src/cpu/loongarch/vm/templateTable_loongarch_64.cpp +@@ -0,0 +1,4056 @@ ++/* ++ * Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2015, 2022, Loongson Technology. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ * ++ */ ++ ++#include "precompiled.hpp" ++#include "asm/macroAssembler.hpp" ++#include "interpreter/interpreter.hpp" ++#include "interpreter/interpreterRuntime.hpp" ++#include "interpreter/templateTable.hpp" ++#include "memory/universe.inline.hpp" ++#include "oops/methodData.hpp" ++#include "oops/objArrayKlass.hpp" ++#include "oops/oop.inline.hpp" ++#include "prims/methodHandles.hpp" ++#include "runtime/sharedRuntime.hpp" ++#include "runtime/stubRoutines.hpp" ++#include "runtime/synchronizer.hpp" ++#include "utilities/macros.hpp" ++ ++ ++#ifndef CC_INTERP ++ ++#define __ _masm-> ++ ++#define A0 RA0 ++#define A1 RA1 ++#define A2 RA2 ++#define A3 RA3 ++#define A4 RA4 ++#define A5 RA5 ++#define A6 RA6 ++#define A7 RA7 ++#define T0 RT0 ++#define T1 RT1 ++#define T2 RT2 ++#define T3 RT3 ++#define T4 RT4 ++#define T5 RT5 ++#define T6 RT6 ++#define T7 RT7 ++#define T8 RT8 ++ ++// Platform-dependent initialization ++ ++void TemplateTable::pd_initialize() { ++ // No LoongArch specific initialization ++} ++ ++// Address computation: local variables ++ ++static inline Address iaddress(int n) { ++ return Address(LVP, Interpreter::local_offset_in_bytes(n)); ++} ++ ++static inline Address laddress(int n) { ++ return iaddress(n + 1); ++} ++ ++static inline Address faddress(int n) { ++ return iaddress(n); ++} ++ ++static inline Address daddress(int n) { ++ return laddress(n); ++} ++ ++static inline Address aaddress(int n) { ++ return iaddress(n); ++} ++static inline Address haddress(int n) { return iaddress(n + 0); } ++ ++ ++static inline Address at_sp() { return Address(SP, 0); } ++static inline Address at_sp_p1() { return Address(SP, 1 * wordSize); } ++static inline Address at_sp_p2() { return Address(SP, 2 * wordSize); } ++ ++// At top of Java expression stack which may be different than sp(). ++// It isn't for category 1 objects. ++static inline Address at_tos () { ++ Address tos = Address(SP, Interpreter::expr_offset_in_bytes(0)); ++ return tos; ++} ++ ++static inline Address at_tos_p1() { ++ return Address(SP, Interpreter::expr_offset_in_bytes(1)); ++} ++ ++static inline Address at_tos_p2() { ++ return Address(SP, Interpreter::expr_offset_in_bytes(2)); ++} ++ ++static inline Address at_tos_p3() { ++ return Address(SP, Interpreter::expr_offset_in_bytes(3)); ++} ++ ++// we use S0 as bcp, be sure you have bcp in S0 before you call any of the Template generator ++Address TemplateTable::at_bcp(int offset) { ++ assert(_desc->uses_bcp(), "inconsistent uses_bcp information"); ++ return Address(BCP, offset); ++} ++ ++// Miscelaneous helper routines ++// Store an oop (or NULL) at the address described by obj. ++// If val == noreg this means store a NULL ++ ++static void do_oop_store(InterpreterMacroAssembler* _masm, ++ Address obj, ++ Register val, ++ BarrierSet::Name barrier, ++ bool precise) { ++ assert(val == noreg || val == FSR, "parameter is just for looks"); ++ switch (barrier) { ++#if INCLUDE_ALL_GCS ++ case BarrierSet::G1SATBCT: ++ case BarrierSet::G1SATBCTLogging: ++ { ++ // flatten object address if needed ++ if (obj.index() == noreg && obj.disp() == 0) { ++ if (obj.base() != T3) { ++ __ move(T3, obj.base()); ++ } ++ } else { ++ __ lea(T3, obj); ++ } ++ __ g1_write_barrier_pre(T3 /* obj */, ++ T1 /* pre_val */, ++ TREG /* thread */, ++ T4 /* tmp */, ++ val != noreg /* tosca_live */, ++ false /* expand_call */); ++ if (val == noreg) { ++ __ store_heap_oop_null(Address(T3, 0)); ++ } else { ++ // G1 barrier needs uncompressed oop for region cross check. ++ Register new_val = val; ++ if (UseCompressedOops) { ++ new_val = T1; ++ __ move(new_val, val); ++ } ++ __ store_heap_oop(Address(T3, 0), val); ++ __ g1_write_barrier_post(T3 /* store_adr */, ++ new_val /* new_val */, ++ TREG /* thread */, ++ T4 /* tmp */, ++ T1 /* tmp2 */); ++ } ++ } ++ break; ++#endif // INCLUDE_ALL_GCS ++ case BarrierSet::CardTableModRef: ++ case BarrierSet::CardTableExtension: ++ { ++ if (val == noreg) { ++ __ store_heap_oop_null(obj); ++ } else { ++ __ store_heap_oop(obj, val); ++ // flatten object address if needed ++ if (!precise || (obj.index() == noreg && obj.disp() == 0)) { ++ __ store_check(obj.base()); ++ } else { ++ //TODO: LA ++ __ lea(T4, obj); ++ __ store_check(T4); ++ } ++ } ++ } ++ break; ++ case BarrierSet::ModRef: ++ case BarrierSet::Other: ++ if (val == noreg) { ++ __ store_heap_oop_null(obj); ++ } else { ++ __ store_heap_oop(obj, val); ++ } ++ break; ++ default : ++ ShouldNotReachHere(); ++ ++ } ++} ++ ++// bytecode folding ++void TemplateTable::patch_bytecode(Bytecodes::Code bc, Register bc_reg, ++ Register tmp_reg, bool load_bc_into_bc_reg/*=true*/, ++ int byte_no) { ++ if (!RewriteBytecodes) return; ++ Label L_patch_done; ++ ++ switch (bc) { ++ case Bytecodes::_fast_aputfield: ++ case Bytecodes::_fast_bputfield: ++ case Bytecodes::_fast_zputfield: ++ case Bytecodes::_fast_cputfield: ++ case Bytecodes::_fast_dputfield: ++ case Bytecodes::_fast_fputfield: ++ case Bytecodes::_fast_iputfield: ++ case Bytecodes::_fast_lputfield: ++ case Bytecodes::_fast_sputfield: ++ { ++ // We skip bytecode quickening for putfield instructions when ++ // the put_code written to the constant pool cache is zero. ++ // This is required so that every execution of this instruction ++ // calls out to InterpreterRuntime::resolve_get_put to do ++ // additional, required work. ++ assert(byte_no == f1_byte || byte_no == f2_byte, "byte_no out of range"); ++ assert(load_bc_into_bc_reg, "we use bc_reg as temp"); ++ __ get_cache_and_index_and_bytecode_at_bcp(tmp_reg, bc_reg, tmp_reg, byte_no, 1); ++ __ addi_d(bc_reg, R0, bc); ++ __ beq(tmp_reg, R0, L_patch_done); ++ } ++ break; ++ default: ++ assert(byte_no == -1, "sanity"); ++ // the pair bytecodes have already done the load. ++ if (load_bc_into_bc_reg) { ++ __ li(bc_reg, bc); ++ } ++ } ++ ++ if (JvmtiExport::can_post_breakpoint()) { ++ Label L_fast_patch; ++ // if a breakpoint is present we can't rewrite the stream directly ++ __ ld_bu(tmp_reg, at_bcp(0)); ++ __ li(AT, Bytecodes::_breakpoint); ++ __ bne(tmp_reg, AT, L_fast_patch); ++ ++ __ get_method(tmp_reg); ++ // Let breakpoint table handling rewrite to quicker bytecode ++ __ call_VM(NOREG, CAST_FROM_FN_PTR(address, ++ InterpreterRuntime::set_original_bytecode_at), tmp_reg, BCP, bc_reg); ++ ++ __ b(L_patch_done); ++ __ bind(L_fast_patch); ++ } ++ ++#ifdef ASSERT ++ Label L_okay; ++ __ ld_bu(tmp_reg, at_bcp(0)); ++ __ li(AT, (int)Bytecodes::java_code(bc)); ++ __ beq(tmp_reg, AT, L_okay); ++ __ beq(tmp_reg, bc_reg, L_patch_done); ++ __ stop("patching the wrong bytecode"); ++ __ bind(L_okay); ++#endif ++ ++ // patch bytecode ++ __ st_b(bc_reg, at_bcp(0)); ++ __ bind(L_patch_done); ++} ++ ++ ++// Individual instructions ++ ++void TemplateTable::nop() { ++ transition(vtos, vtos); ++ // nothing to do ++} ++ ++void TemplateTable::shouldnotreachhere() { ++ transition(vtos, vtos); ++ __ stop("shouldnotreachhere bytecode"); ++} ++ ++void TemplateTable::aconst_null() { ++ transition(vtos, atos); ++ __ move(FSR, R0); ++} ++ ++void TemplateTable::iconst(int value) { ++ transition(vtos, itos); ++ if (value == 0) { ++ __ move(FSR, R0); ++ } else { ++ __ li(FSR, value); ++ } ++} ++ ++void TemplateTable::lconst(int value) { ++ transition(vtos, ltos); ++ if (value == 0) { ++ __ move(FSR, R0); ++ } else { ++ __ li(FSR, value); ++ } ++} ++ ++void TemplateTable::fconst(int value) { ++ transition(vtos, ftos); ++ switch( value ) { ++ case 0: __ movgr2fr_w(FSF, R0); return; ++ case 1: __ addi_d(AT, R0, 1); break; ++ case 2: __ addi_d(AT, R0, 2); break; ++ default: ShouldNotReachHere(); ++ } ++ __ movgr2fr_w(FSF, AT); ++ __ ffint_s_w(FSF, FSF); ++} ++ ++void TemplateTable::dconst(int value) { ++ transition(vtos, dtos); ++ switch( value ) { ++ case 0: __ movgr2fr_d(FSF, R0); ++ return; ++ case 1: __ addi_d(AT, R0, 1); ++ __ movgr2fr_d(FSF, AT); ++ __ ffint_d_w(FSF, FSF); ++ break; ++ default: ShouldNotReachHere(); ++ } ++} ++ ++void TemplateTable::bipush() { ++ transition(vtos, itos); ++ __ ld_b(FSR, at_bcp(1)); ++} ++ ++void TemplateTable::sipush() { ++ transition(vtos, itos); ++ __ ld_b(FSR, BCP, 1); ++ __ ld_bu(AT, BCP, 2); ++ __ slli_d(FSR, FSR, 8); ++ __ orr(FSR, FSR, AT); ++} ++ ++// T1 : tags ++// T2 : index ++// T3 : cpool ++// T8 : tag ++void TemplateTable::ldc(bool wide) { ++ transition(vtos, vtos); ++ Label call_ldc, notFloat, notClass, Done; ++ // get index in cpool ++ if (wide) { ++ __ get_unsigned_2_byte_index_at_bcp(T2, 1); ++ } else { ++ __ ld_bu(T2, at_bcp(1)); ++ } ++ ++ __ get_cpool_and_tags(T3, T1); ++ ++ const int base_offset = ConstantPool::header_size() * wordSize; ++ const int tags_offset = Array::base_offset_in_bytes(); ++ ++ // get type ++ __ add_d(AT, T1, T2); ++ __ ld_b(T1, AT, tags_offset); ++ if(os::is_MP()) { ++ __ membar(Assembler::Membar_mask_bits(__ LoadLoad|__ LoadStore)); ++ } ++ //now T1 is the tag ++ ++ // unresolved class - get the resolved class ++ __ addi_d(AT, T1, - JVM_CONSTANT_UnresolvedClass); ++ __ beq(AT, R0, call_ldc); ++ ++ // unresolved class in error (resolution failed) - call into runtime ++ // so that the same error from first resolution attempt is thrown. ++ __ addi_d(AT, T1, -JVM_CONSTANT_UnresolvedClassInError); ++ __ beq(AT, R0, call_ldc); ++ ++ // resolved class - need to call vm to get java mirror of the class ++ __ addi_d(AT, T1, - JVM_CONSTANT_Class); ++ __ slli_d(T2, T2, Address::times_8); ++ __ bne(AT, R0, notClass); ++ ++ __ bind(call_ldc); ++ __ li(A1, wide); ++ call_VM(FSR, CAST_FROM_FN_PTR(address, InterpreterRuntime::ldc), A1); ++ //__ push(atos); ++ __ addi_d(SP, SP, - Interpreter::stackElementSize); ++ __ st_d(FSR, SP, 0); ++ __ b(Done); ++ ++ __ bind(notClass); ++ __ addi_d(AT, T1, -JVM_CONSTANT_Float); ++ __ bne(AT, R0, notFloat); ++ // ftos ++ __ add_d(AT, T3, T2); ++ __ fld_s(FSF, AT, base_offset); ++ //__ push_f(); ++ __ addi_d(SP, SP, - Interpreter::stackElementSize); ++ __ fst_s(FSF, SP, 0); ++ __ b(Done); ++ ++ __ bind(notFloat); ++#ifdef ASSERT ++ { ++ Label L; ++ __ addi_d(AT, T1, -JVM_CONSTANT_Integer); ++ __ beq(AT, R0, L); ++ __ stop("unexpected tag type in ldc"); ++ __ bind(L); ++ } ++#endif ++ // itos JVM_CONSTANT_Integer only ++ __ add_d(T0, T3, T2); ++ __ ld_w(FSR, T0, base_offset); ++ __ push(itos); ++ __ bind(Done); ++} ++ ++// Fast path for caching oop constants. ++void TemplateTable::fast_aldc(bool wide) { ++ transition(vtos, atos); ++ ++ Register result = FSR; ++ Register tmp = SSR; ++ int index_size = wide ? sizeof(u2) : sizeof(u1); ++ ++ Label resolved; ++ ++ // We are resolved if the resolved reference cache entry contains a ++ // non-null object (String, MethodType, etc.) ++ assert_different_registers(result, tmp); ++ __ get_cache_index_at_bcp(tmp, 1, index_size); ++ __ load_resolved_reference_at_index(result, tmp); ++ __ bne(result, R0, resolved); ++ ++ address entry = CAST_FROM_FN_PTR(address, InterpreterRuntime::resolve_ldc); ++ // first time invocation - must resolve first ++ int i = (int)bytecode(); ++ __ li(tmp, i); ++ __ call_VM(result, entry, tmp); ++ ++ __ bind(resolved); ++ ++ if (VerifyOops) { ++ __ verify_oop(result); ++ } ++} ++ ++ ++// used register: T2, T3, T1 ++// T2 : index ++// T3 : cpool ++// T1 : tag ++void TemplateTable::ldc2_w() { ++ transition(vtos, vtos); ++ Label Long, Done; ++ ++ // get index in cpool ++ __ get_unsigned_2_byte_index_at_bcp(T2, 1); ++ ++ __ get_cpool_and_tags(T3, T1); ++ ++ const int base_offset = ConstantPool::header_size() * wordSize; ++ const int tags_offset = Array::base_offset_in_bytes(); ++ ++ // get type in T1 ++ __ add_d(AT, T1, T2); ++ __ ld_b(T1, AT, tags_offset); ++ ++ __ addi_d(AT, T1, - JVM_CONSTANT_Double); ++ __ slli_d(T2, T2, Address::times_8); ++ __ bne(AT, R0, Long); ++ ++ // dtos ++ __ add_d(AT, T3, T2); ++ __ fld_d(FSF, AT, base_offset); ++ __ push(dtos); ++ __ b(Done); ++ ++ // ltos ++ __ bind(Long); ++ __ add_d(AT, T3, T2); ++ __ ld_d(FSR, AT, base_offset); ++ __ push(ltos); ++ ++ __ bind(Done); ++} ++ ++// we compute the actual local variable address here ++void TemplateTable::locals_index(Register reg, int offset) { ++ __ ld_bu(reg, at_bcp(offset)); ++ __ slli_d(reg, reg, Address::times_8); ++ __ sub_d(reg, LVP, reg); ++} ++ ++// this method will do bytecode folding of the two form: ++// iload iload iload caload ++// used register : T2, T3 ++// T2 : bytecode ++// T3 : folded code ++void TemplateTable::iload() { ++ transition(vtos, itos); ++ if (RewriteFrequentPairs) { ++ Label rewrite, done; ++ // get the next bytecode in T2 ++ __ ld_bu(T2, at_bcp(Bytecodes::length_for(Bytecodes::_iload))); ++ // if _iload, wait to rewrite to iload2. We only want to rewrite the ++ // last two iloads in a pair. Comparing against fast_iload means that ++ // the next bytecode is neither an iload or a caload, and therefore ++ // an iload pair. ++ __ li(AT, Bytecodes::_iload); ++ __ beq(AT, T2, done); ++ ++ __ li(T3, Bytecodes::_fast_iload2); ++ __ li(AT, Bytecodes::_fast_iload); ++ __ beq(AT, T2, rewrite); ++ ++ // if _caload, rewrite to fast_icaload ++ __ li(T3, Bytecodes::_fast_icaload); ++ __ li(AT, Bytecodes::_caload); ++ __ beq(AT, T2, rewrite); ++ ++ // rewrite so iload doesn't check again. ++ __ li(T3, Bytecodes::_fast_iload); ++ ++ // rewrite ++ // T3 : fast bytecode ++ __ bind(rewrite); ++ patch_bytecode(Bytecodes::_iload, T3, T2, false); ++ __ bind(done); ++ } ++ ++ // Get the local value into tos ++ locals_index(T2); ++ __ ld_w(FSR, T2, 0); ++} ++ ++// used register T2 ++// T2 : index ++void TemplateTable::fast_iload2() { ++ transition(vtos, itos); ++ locals_index(T2); ++ __ ld_w(FSR, T2, 0); ++ __ push(itos); ++ locals_index(T2, 3); ++ __ ld_w(FSR, T2, 0); ++} ++ ++// used register T2 ++// T2 : index ++void TemplateTable::fast_iload() { ++ transition(vtos, itos); ++ locals_index(T2); ++ __ ld_w(FSR, T2, 0); ++} ++ ++// used register T2 ++// T2 : index ++void TemplateTable::lload() { ++ transition(vtos, ltos); ++ locals_index(T2); ++ __ ld_d(FSR, T2, -wordSize); ++} ++ ++// used register T2 ++// T2 : index ++void TemplateTable::fload() { ++ transition(vtos, ftos); ++ locals_index(T2); ++ __ fld_s(FSF, T2, 0); ++} ++ ++// used register T2 ++// T2 : index ++void TemplateTable::dload() { ++ transition(vtos, dtos); ++ locals_index(T2); ++ __ fld_d(FSF, T2, -wordSize); ++} ++ ++// used register T2 ++// T2 : index ++void TemplateTable::aload() { ++ transition(vtos, atos); ++ locals_index(T2); ++ __ ld_d(FSR, T2, 0); ++} ++ ++void TemplateTable::locals_index_wide(Register reg) { ++ __ get_unsigned_2_byte_index_at_bcp(reg, 2); ++ __ slli_d(reg, reg, Address::times_8); ++ __ sub_d(reg, LVP, reg); ++} ++ ++// used register T2 ++// T2 : index ++void TemplateTable::wide_iload() { ++ transition(vtos, itos); ++ locals_index_wide(T2); ++ __ ld_d(FSR, T2, 0); ++} ++ ++// used register T2 ++// T2 : index ++void TemplateTable::wide_lload() { ++ transition(vtos, ltos); ++ locals_index_wide(T2); ++ __ ld_d(FSR, T2, -wordSize); ++} ++ ++// used register T2 ++// T2 : index ++void TemplateTable::wide_fload() { ++ transition(vtos, ftos); ++ locals_index_wide(T2); ++ __ fld_s(FSF, T2, 0); ++} ++ ++// used register T2 ++// T2 : index ++void TemplateTable::wide_dload() { ++ transition(vtos, dtos); ++ locals_index_wide(T2); ++ __ fld_d(FSF, T2, -wordSize); ++} ++ ++// used register T2 ++// T2 : index ++void TemplateTable::wide_aload() { ++ transition(vtos, atos); ++ locals_index_wide(T2); ++ __ ld_d(FSR, T2, 0); ++} ++ ++// we use A2 as the regiser for index, BE CAREFUL! ++// we dont use our tge 29 now, for later optimization ++void TemplateTable::index_check(Register array, Register index) { ++ // Pop ptr into array ++ __ pop_ptr(array); ++ index_check_without_pop(array, index); ++} ++ ++void TemplateTable::index_check_without_pop(Register array, Register index) { ++ // destroys A2 ++ // check array ++ __ null_check(array, arrayOopDesc::length_offset_in_bytes()); ++ ++ // sign extend since tos (index) might contain garbage in upper bits ++ __ slli_w(index, index, 0); ++ ++ // check index ++ Label ok; ++ __ ld_w(AT, array, arrayOopDesc::length_offset_in_bytes()); ++ __ bltu(index, AT, ok); ++ ++ //throw_ArrayIndexOutOfBoundsException assume abberrant index in A2 ++ if (A2 != index) __ move(A2, index); ++ __ jmp(Interpreter::_throw_ArrayIndexOutOfBoundsException_entry); ++ __ bind(ok); ++} ++ ++void TemplateTable::iaload() { ++ transition(itos, itos); ++ index_check(SSR, FSR); ++ __ alsl_d(FSR, FSR, SSR, 1); ++ __ ld_w(FSR, FSR, arrayOopDesc::base_offset_in_bytes(T_INT)); ++} ++ ++void TemplateTable::laload() { ++ transition(itos, ltos); ++ index_check(SSR, FSR); ++ __ alsl_d(AT, FSR, SSR, Address::times_8 - 1); ++ __ ld_d(FSR, AT, arrayOopDesc::base_offset_in_bytes(T_LONG)); ++} ++ ++void TemplateTable::faload() { ++ transition(itos, ftos); ++ index_check(SSR, FSR); ++ __ shl(FSR, 2); ++ __ add_d(FSR, SSR, FSR); ++ __ fld_s(FSF, FSR, arrayOopDesc::base_offset_in_bytes(T_FLOAT)); ++} ++ ++void TemplateTable::daload() { ++ transition(itos, dtos); ++ index_check(SSR, FSR); ++ __ alsl_d(AT, FSR, SSR, 2); ++ __ fld_d(FSF, AT, arrayOopDesc::base_offset_in_bytes(T_DOUBLE)); ++} ++ ++void TemplateTable::aaload() { ++ transition(itos, atos); ++ index_check(SSR, FSR); ++ __ alsl_d(FSR, FSR, SSR, (UseCompressedOops ? Address::times_4 : Address::times_8) - 1); ++ //add for compressedoops ++ __ load_heap_oop(FSR, Address(FSR, arrayOopDesc::base_offset_in_bytes(T_OBJECT))); ++} ++ ++void TemplateTable::baload() { ++ transition(itos, itos); ++ index_check(SSR, FSR); ++ __ add_d(FSR, SSR, FSR); ++ __ ld_b(FSR, FSR, arrayOopDesc::base_offset_in_bytes(T_BYTE)); ++} ++ ++void TemplateTable::caload() { ++ transition(itos, itos); ++ index_check(SSR, FSR); ++ __ alsl_d(FSR, FSR, SSR, Address::times_2 - 1); ++ __ ld_hu(FSR, FSR, arrayOopDesc::base_offset_in_bytes(T_CHAR)); ++} ++ ++// iload followed by caload frequent pair ++// used register : T2 ++// T2 : index ++void TemplateTable::fast_icaload() { ++ transition(vtos, itos); ++ // load index out of locals ++ locals_index(T2); ++ __ ld_w(FSR, T2, 0); ++ index_check(SSR, FSR); ++ __ alsl_d(FSR, FSR, SSR, 0); ++ __ ld_hu(FSR, FSR, arrayOopDesc::base_offset_in_bytes(T_CHAR)); ++} ++ ++void TemplateTable::saload() { ++ transition(itos, itos); ++ index_check(SSR, FSR); ++ __ alsl_d(FSR, FSR, SSR, Address::times_2 - 1); ++ __ ld_h(FSR, FSR, arrayOopDesc::base_offset_in_bytes(T_SHORT)); ++} ++ ++void TemplateTable::iload(int n) { ++ transition(vtos, itos); ++ __ ld_w(FSR, iaddress(n)); ++} ++ ++void TemplateTable::lload(int n) { ++ transition(vtos, ltos); ++ __ ld_d(FSR, laddress(n)); ++} ++ ++void TemplateTable::fload(int n) { ++ transition(vtos, ftos); ++ __ fld_s(FSF, faddress(n)); ++} ++ ++void TemplateTable::dload(int n) { ++ transition(vtos, dtos); ++ __ fld_d(FSF, laddress(n)); ++} ++ ++void TemplateTable::aload(int n) { ++ transition(vtos, atos); ++ __ ld_d(FSR, aaddress(n)); ++} ++ ++// used register : T2, T3 ++// T2 : bytecode ++// T3 : folded code ++void TemplateTable::aload_0() { ++ transition(vtos, atos); ++ // According to bytecode histograms, the pairs: ++ // ++ // _aload_0, _fast_igetfield ++ // _aload_0, _fast_agetfield ++ // _aload_0, _fast_fgetfield ++ // ++ // occur frequently. If RewriteFrequentPairs is set, the (slow) ++ // _aload_0 bytecode checks if the next bytecode is either ++ // _fast_igetfield, _fast_agetfield or _fast_fgetfield and then ++ // rewrites the current bytecode into a pair bytecode; otherwise it ++ // rewrites the current bytecode into _fast_aload_0 that doesn't do ++ // the pair check anymore. ++ // ++ // Note: If the next bytecode is _getfield, the rewrite must be ++ // delayed, otherwise we may miss an opportunity for a pair. ++ // ++ // Also rewrite frequent pairs ++ // aload_0, aload_1 ++ // aload_0, iload_1 ++ // These bytecodes with a small amount of code are most profitable ++ // to rewrite ++ if (RewriteFrequentPairs) { ++ Label rewrite, done; ++ // get the next bytecode in T2 ++ __ ld_bu(T2, at_bcp(Bytecodes::length_for(Bytecodes::_aload_0))); ++ ++ // do actual aload_0 ++ aload(0); ++ ++ // if _getfield then wait with rewrite ++ __ li(AT, Bytecodes::_getfield); ++ __ beq(AT, T2, done); ++ ++ // if _igetfield then reqrite to _fast_iaccess_0 ++ assert(Bytecodes::java_code(Bytecodes::_fast_iaccess_0) == ++ Bytecodes::_aload_0, ++ "fix bytecode definition"); ++ __ li(T3, Bytecodes::_fast_iaccess_0); ++ __ li(AT, Bytecodes::_fast_igetfield); ++ __ beq(AT, T2, rewrite); ++ ++ // if _agetfield then reqrite to _fast_aaccess_0 ++ assert(Bytecodes::java_code(Bytecodes::_fast_aaccess_0) == ++ Bytecodes::_aload_0, ++ "fix bytecode definition"); ++ __ li(T3, Bytecodes::_fast_aaccess_0); ++ __ li(AT, Bytecodes::_fast_agetfield); ++ __ beq(AT, T2, rewrite); ++ ++ // if _fgetfield then reqrite to _fast_faccess_0 ++ assert(Bytecodes::java_code(Bytecodes::_fast_faccess_0) == ++ Bytecodes::_aload_0, ++ "fix bytecode definition"); ++ __ li(T3, Bytecodes::_fast_faccess_0); ++ __ li(AT, Bytecodes::_fast_fgetfield); ++ __ beq(AT, T2, rewrite); ++ ++ // else rewrite to _fast_aload0 ++ assert(Bytecodes::java_code(Bytecodes::_fast_aload_0) == ++ Bytecodes::_aload_0, ++ "fix bytecode definition"); ++ __ li(T3, Bytecodes::_fast_aload_0); ++ ++ // rewrite ++ __ bind(rewrite); ++ patch_bytecode(Bytecodes::_aload_0, T3, T2, false); ++ ++ __ bind(done); ++ } else { ++ aload(0); ++ } ++} ++ ++void TemplateTable::istore() { ++ transition(itos, vtos); ++ locals_index(T2); ++ __ st_w(FSR, T2, 0); ++} ++ ++void TemplateTable::lstore() { ++ transition(ltos, vtos); ++ locals_index(T2); ++ __ st_d(FSR, T2, -wordSize); ++} ++ ++void TemplateTable::fstore() { ++ transition(ftos, vtos); ++ locals_index(T2); ++ __ fst_s(FSF, T2, 0); ++} ++ ++void TemplateTable::dstore() { ++ transition(dtos, vtos); ++ locals_index(T2); ++ __ fst_d(FSF, T2, -wordSize); ++} ++ ++void TemplateTable::astore() { ++ transition(vtos, vtos); ++ __ pop_ptr(FSR); ++ locals_index(T2); ++ __ st_d(FSR, T2, 0); ++} ++ ++void TemplateTable::wide_istore() { ++ transition(vtos, vtos); ++ __ pop_i(FSR); ++ locals_index_wide(T2); ++ __ st_d(FSR, T2, 0); ++} ++ ++void TemplateTable::wide_lstore() { ++ transition(vtos, vtos); ++ __ pop_l(FSR); ++ locals_index_wide(T2); ++ __ st_d(FSR, T2, -wordSize); ++} ++ ++void TemplateTable::wide_fstore() { ++ wide_istore(); ++} ++ ++void TemplateTable::wide_dstore() { ++ wide_lstore(); ++} ++ ++void TemplateTable::wide_astore() { ++ transition(vtos, vtos); ++ __ pop_ptr(FSR); ++ locals_index_wide(T2); ++ __ st_d(FSR, T2, 0); ++} ++ ++// used register : T2 ++void TemplateTable::iastore() { ++ transition(itos, vtos); ++ __ pop_i(SSR); // T2: array SSR: index ++ index_check(T2, SSR); // prefer index in SSR ++ __ slli_d(SSR, SSR, Address::times_4); ++ __ add_d(T2, T2, SSR); ++ __ st_w(FSR, T2, arrayOopDesc::base_offset_in_bytes(T_INT)); ++} ++ ++ ++ ++// used register T2, T3 ++void TemplateTable::lastore() { ++ transition(ltos, vtos); ++ __ pop_i (T2); ++ index_check(T3, T2); ++ __ slli_d(T2, T2, Address::times_8); ++ __ add_d(T3, T3, T2); ++ __ st_d(FSR, T3, arrayOopDesc::base_offset_in_bytes(T_LONG)); ++} ++ ++// used register T2 ++void TemplateTable::fastore() { ++ transition(ftos, vtos); ++ __ pop_i(SSR); ++ index_check(T2, SSR); ++ __ slli_d(SSR, SSR, Address::times_4); ++ __ add_d(T2, T2, SSR); ++ __ fst_s(FSF, T2, arrayOopDesc::base_offset_in_bytes(T_FLOAT)); ++} ++ ++// used register T2, T3 ++void TemplateTable::dastore() { ++ transition(dtos, vtos); ++ __ pop_i (T2); ++ index_check(T3, T2); ++ __ slli_d(T2, T2, Address::times_8); ++ __ add_d(T3, T3, T2); ++ __ fst_d(FSF, T3, arrayOopDesc::base_offset_in_bytes(T_DOUBLE)); ++} ++ ++// used register : T2, T3, T8 ++// T2 : array ++// T3 : subklass ++// T8 : supklass ++void TemplateTable::aastore() { ++ Label is_null, ok_is_subtype, done; ++ transition(vtos, vtos); ++ // stack: ..., array, index, value ++ __ ld_d(FSR, at_tos()); // Value ++ __ ld_w(SSR, at_tos_p1()); // Index ++ __ ld_d(T2, at_tos_p2()); // Array ++ ++ // index_check(T2, SSR); ++ index_check_without_pop(T2, SSR); ++ // do array store check - check for NULL value first ++ __ beq(FSR, R0, is_null); ++ ++ // Move subklass into T3 ++ //add for compressedoops ++ __ load_klass(T3, FSR); ++ // Move superklass into T8 ++ //add for compressedoops ++ __ load_klass(T8, T2); ++ __ ld_d(T8, Address(T8, ObjArrayKlass::element_klass_offset())); ++ // Compress array+index*4+12 into a single register. T2 ++ __ alsl_d(T2, SSR, T2, (UseCompressedOops? Address::times_4 : Address::times_8) - 1); ++ __ addi_d(T2, T2, arrayOopDesc::base_offset_in_bytes(T_OBJECT)); ++ ++ // Generate subtype check. ++ // Superklass in T8. Subklass in T3. ++ __ gen_subtype_check(T8, T3, ok_is_subtype); ++ // Come here on failure ++ // object is at FSR ++ __ jmp(Interpreter::_throw_ArrayStoreException_entry); ++ // Come here on success ++ __ bind(ok_is_subtype); ++ do_oop_store(_masm, Address(T2, 0), FSR, _bs->kind(), true); ++ __ b(done); ++ ++ // Have a NULL in FSR, T2=array, SSR=index. Store NULL at ary[idx] ++ __ bind(is_null); ++ __ profile_null_seen(T4); ++ __ alsl_d(T2, SSR, T2, (UseCompressedOops? Address::times_4 : Address::times_8) - 1); ++ do_oop_store(_masm, Address(T2, arrayOopDesc::base_offset_in_bytes(T_OBJECT)), noreg, _bs->kind(), true); ++ ++ __ bind(done); ++ __ addi_d(SP, SP, 3 * Interpreter::stackElementSize); ++} ++ ++void TemplateTable::bastore() { ++ transition(itos, vtos); ++ __ pop_i(SSR); ++ index_check(T2, SSR); ++ ++ // Need to check whether array is boolean or byte ++ // since both types share the bastore bytecode. ++ __ load_klass(T4, T2); ++ __ ld_w(T4, T4, in_bytes(Klass::layout_helper_offset())); ++ ++ int diffbit = Klass::layout_helper_boolean_diffbit(); ++ __ li(AT, diffbit); ++ ++ Label L_skip; ++ __ andr(AT, T4, AT); ++ __ beq(AT, R0, L_skip); ++ __ andi(FSR, FSR, 0x1); ++ __ bind(L_skip); ++ ++ __ add_d(SSR, T2, SSR); ++ __ st_b(FSR, SSR, arrayOopDesc::base_offset_in_bytes(T_BYTE)); ++} ++ ++void TemplateTable::castore() { ++ transition(itos, vtos); ++ __ pop_i(SSR); ++ index_check(T2, SSR); ++ __ alsl_d(SSR, SSR, T2, Address::times_2 - 1); ++ __ st_h(FSR, SSR, arrayOopDesc::base_offset_in_bytes(T_CHAR)); ++} ++ ++void TemplateTable::sastore() { ++ castore(); ++} ++ ++void TemplateTable::istore(int n) { ++ transition(itos, vtos); ++ __ st_w(FSR, iaddress(n)); ++} ++ ++void TemplateTable::lstore(int n) { ++ transition(ltos, vtos); ++ __ st_d(FSR, laddress(n)); ++} ++ ++void TemplateTable::fstore(int n) { ++ transition(ftos, vtos); ++ __ fst_s(FSF, faddress(n)); ++} ++ ++void TemplateTable::dstore(int n) { ++ transition(dtos, vtos); ++ __ fst_d(FSF, laddress(n)); ++} ++ ++void TemplateTable::astore(int n) { ++ transition(vtos, vtos); ++ __ pop_ptr(FSR); ++ __ st_d(FSR, aaddress(n)); ++} ++ ++void TemplateTable::pop() { ++ transition(vtos, vtos); ++ __ addi_d(SP, SP, Interpreter::stackElementSize); ++} ++ ++void TemplateTable::pop2() { ++ transition(vtos, vtos); ++ __ addi_d(SP, SP, 2 * Interpreter::stackElementSize); ++} ++ ++void TemplateTable::dup() { ++ transition(vtos, vtos); ++ // stack: ..., a ++ __ load_ptr(0, FSR); ++ __ push_ptr(FSR); ++ // stack: ..., a, a ++} ++ ++// blows FSR ++void TemplateTable::dup_x1() { ++ transition(vtos, vtos); ++ // stack: ..., a, b ++ __ load_ptr(0, FSR); // load b ++ __ load_ptr(1, A5); // load a ++ __ store_ptr(1, FSR); // store b ++ __ store_ptr(0, A5); // store a ++ __ push_ptr(FSR); // push b ++ // stack: ..., b, a, b ++} ++ ++// blows FSR ++void TemplateTable::dup_x2() { ++ transition(vtos, vtos); ++ // stack: ..., a, b, c ++ __ load_ptr(0, FSR); // load c ++ __ load_ptr(2, A5); // load a ++ __ store_ptr(2, FSR); // store c in a ++ __ push_ptr(FSR); // push c ++ // stack: ..., c, b, c, c ++ __ load_ptr(2, FSR); // load b ++ __ store_ptr(2, A5); // store a in b ++ // stack: ..., c, a, c, c ++ __ store_ptr(1, FSR); // store b in c ++ // stack: ..., c, a, b, c ++} ++ ++// blows FSR ++void TemplateTable::dup2() { ++ transition(vtos, vtos); ++ // stack: ..., a, b ++ __ load_ptr(1, FSR); // load a ++ __ push_ptr(FSR); // push a ++ __ load_ptr(1, FSR); // load b ++ __ push_ptr(FSR); // push b ++ // stack: ..., a, b, a, b ++} ++ ++// blows FSR ++void TemplateTable::dup2_x1() { ++ transition(vtos, vtos); ++ // stack: ..., a, b, c ++ __ load_ptr(0, T2); // load c ++ __ load_ptr(1, FSR); // load b ++ __ push_ptr(FSR); // push b ++ __ push_ptr(T2); // push c ++ // stack: ..., a, b, c, b, c ++ __ store_ptr(3, T2); // store c in b ++ // stack: ..., a, c, c, b, c ++ __ load_ptr(4, T2); // load a ++ __ store_ptr(2, T2); // store a in 2nd c ++ // stack: ..., a, c, a, b, c ++ __ store_ptr(4, FSR); // store b in a ++ // stack: ..., b, c, a, b, c ++ ++ // stack: ..., b, c, a, b, c ++} ++ ++// blows FSR, SSR ++void TemplateTable::dup2_x2() { ++ transition(vtos, vtos); ++ // stack: ..., a, b, c, d ++ // stack: ..., a, b, c, d ++ __ load_ptr(0, T2); // load d ++ __ load_ptr(1, FSR); // load c ++ __ push_ptr(FSR); // push c ++ __ push_ptr(T2); // push d ++ // stack: ..., a, b, c, d, c, d ++ __ load_ptr(4, FSR); // load b ++ __ store_ptr(2, FSR); // store b in d ++ __ store_ptr(4, T2); // store d in b ++ // stack: ..., a, d, c, b, c, d ++ __ load_ptr(5, T2); // load a ++ __ load_ptr(3, FSR); // load c ++ __ store_ptr(3, T2); // store a in c ++ __ store_ptr(5, FSR); // store c in a ++ // stack: ..., c, d, a, b, c, d ++ ++ // stack: ..., c, d, a, b, c, d ++} ++ ++// blows FSR ++void TemplateTable::swap() { ++ transition(vtos, vtos); ++ // stack: ..., a, b ++ ++ __ load_ptr(1, A5); // load a ++ __ load_ptr(0, FSR); // load b ++ __ store_ptr(0, A5); // store a in b ++ __ store_ptr(1, FSR); // store b in a ++ ++ // stack: ..., b, a ++} ++ ++void TemplateTable::iop2(Operation op) { ++ transition(itos, itos); ++ ++ __ pop_i(SSR); ++ switch (op) { ++ case add : __ add_w(FSR, SSR, FSR); break; ++ case sub : __ sub_w(FSR, SSR, FSR); break; ++ case mul : __ mul_w(FSR, SSR, FSR); break; ++ case _and : __ andr(FSR, SSR, FSR); break; ++ case _or : __ orr(FSR, SSR, FSR); break; ++ case _xor : __ xorr(FSR, SSR, FSR); break; ++ case shl : __ sll_w(FSR, SSR, FSR); break; ++ case shr : __ sra_w(FSR, SSR, FSR); break; ++ case ushr : __ srl_w(FSR, SSR, FSR); break; ++ default : ShouldNotReachHere(); ++ } ++} ++ ++// the result stored in FSR, SSR, ++// used registers : T2, T3 ++void TemplateTable::lop2(Operation op) { ++ transition(ltos, ltos); ++ __ pop_l(T2); ++ ++ switch (op) { ++ case add : __ add_d(FSR, T2, FSR); break; ++ case sub : __ sub_d(FSR, T2, FSR); break; ++ case _and: __ andr(FSR, T2, FSR); break; ++ case _or : __ orr(FSR, T2, FSR); break; ++ case _xor: __ xorr(FSR, T2, FSR); break; ++ default : ShouldNotReachHere(); ++ } ++} ++ ++// java require this bytecode could handle 0x80000000/-1, dont cause a overflow exception, ++// the result is 0x80000000 ++// the godson2 cpu do the same, so we need not handle this specially like x86 ++void TemplateTable::idiv() { ++ transition(itos, itos); ++ Label not_zero; ++ ++ __ bne(FSR, R0, not_zero); ++ __ jmp(Interpreter::_throw_ArithmeticException_entry); ++ __ bind(not_zero); ++ ++ __ pop_i(SSR); ++ __ div_w(FSR, SSR, FSR); ++} ++ ++void TemplateTable::irem() { ++ transition(itos, itos); ++ Label not_zero; ++ __ pop_i(SSR); ++ ++ __ bne(FSR, R0, not_zero); ++ //__ brk(7); ++ __ jmp(Interpreter::_throw_ArithmeticException_entry); ++ ++ __ bind(not_zero); ++ __ mod_w(FSR, SSR, FSR); ++} ++ ++void TemplateTable::lmul() { ++ transition(ltos, ltos); ++ __ pop_l(T2); ++ __ mul_d(FSR, T2, FSR); ++} ++ ++// NOTE: i DONT use the Interpreter::_throw_ArithmeticException_entry ++void TemplateTable::ldiv() { ++ transition(ltos, ltos); ++ Label normal; ++ ++ __ bne(FSR, R0, normal); ++ ++ //__ brk(7); //generate FPE ++ __ jmp(Interpreter::_throw_ArithmeticException_entry); ++ ++ __ bind(normal); ++ __ pop_l(A2); ++ __ div_d(FSR, A2, FSR); ++} ++ ++// NOTE: i DONT use the Interpreter::_throw_ArithmeticException_entry ++void TemplateTable::lrem() { ++ transition(ltos, ltos); ++ Label normal; ++ ++ __ bne(FSR, R0, normal); ++ ++ __ jmp(Interpreter::_throw_ArithmeticException_entry); ++ ++ __ bind(normal); ++ __ pop_l (A2); ++ ++ __ mod_d(FSR, A2, FSR); ++} ++ ++// result in FSR ++// used registers : T0 ++void TemplateTable::lshl() { ++ transition(itos, ltos); ++ __ pop_l(T0); ++ __ sll_d(FSR, T0, FSR); ++} ++ ++// used registers : T0 ++void TemplateTable::lshr() { ++ transition(itos, ltos); ++ __ pop_l(T0); ++ __ sra_d(FSR, T0, FSR); ++} ++ ++// used registers : T0 ++void TemplateTable::lushr() { ++ transition(itos, ltos); ++ __ pop_l(T0); ++ __ srl_d(FSR, T0, FSR); ++} ++ ++// result in FSF ++void TemplateTable::fop2(Operation op) { ++ transition(ftos, ftos); ++ switch (op) { ++ case add: ++ __ fld_s(fscratch, at_sp()); ++ __ fadd_s(FSF, fscratch, FSF); ++ break; ++ case sub: ++ __ fld_s(fscratch, at_sp()); ++ __ fsub_s(FSF, fscratch, FSF); ++ break; ++ case mul: ++ __ fld_s(fscratch, at_sp()); ++ __ fmul_s(FSF, fscratch, FSF); ++ break; ++ case div: ++ __ fld_s(fscratch, at_sp()); ++ __ fdiv_s(FSF, fscratch, FSF); ++ break; ++ case rem: ++ __ fmov_s(FA1, FSF); ++ __ fld_s(FA0, at_sp()); ++ __ call_VM_leaf(CAST_FROM_FN_PTR(address, SharedRuntime::frem), 2); ++ break; ++ default : ShouldNotReachHere(); ++ } ++ ++ __ addi_d(SP, SP, 1 * wordSize); ++} ++ ++// result in SSF||FSF ++// i dont handle the strict flags ++void TemplateTable::dop2(Operation op) { ++ transition(dtos, dtos); ++ switch (op) { ++ case add: ++ __ fld_d(fscratch, at_sp()); ++ __ fadd_d(FSF, fscratch, FSF); ++ break; ++ case sub: ++ __ fld_d(fscratch, at_sp()); ++ __ fsub_d(FSF, fscratch, FSF); ++ break; ++ case mul: ++ __ fld_d(fscratch, at_sp()); ++ __ fmul_d(FSF, fscratch, FSF); ++ break; ++ case div: ++ __ fld_d(fscratch, at_sp()); ++ __ fdiv_d(FSF, fscratch, FSF); ++ break; ++ case rem: ++ __ fmov_d(FA1, FSF); ++ __ fld_d(FA0, at_sp()); ++ __ call_VM_leaf(CAST_FROM_FN_PTR(address, SharedRuntime::drem), 2); ++ break; ++ default : ShouldNotReachHere(); ++ } ++ ++ __ addi_d(SP, SP, 2 * wordSize); ++} ++ ++void TemplateTable::ineg() { ++ transition(itos, itos); ++ __ sub_w(FSR, R0, FSR); ++} ++ ++void TemplateTable::lneg() { ++ transition(ltos, ltos); ++ __ sub_d(FSR, R0, FSR); ++} ++ ++void TemplateTable::fneg() { ++ transition(ftos, ftos); ++ __ fneg_s(FSF, FSF); ++} ++ ++void TemplateTable::dneg() { ++ transition(dtos, dtos); ++ __ fneg_d(FSF, FSF); ++} ++ ++// used registers : T2 ++void TemplateTable::iinc() { ++ transition(vtos, vtos); ++ locals_index(T2); ++ __ ld_w(FSR, T2, 0); ++ __ ld_b(AT, at_bcp(2)); // get constant ++ __ add_d(FSR, FSR, AT); ++ __ st_w(FSR, T2, 0); ++} ++ ++// used register : T2 ++void TemplateTable::wide_iinc() { ++ transition(vtos, vtos); ++ locals_index_wide(T2); ++ __ get_2_byte_integer_at_bcp(FSR, AT, 4); ++ __ hswap(FSR); ++ __ ld_w(AT, T2, 0); ++ __ add_d(FSR, AT, FSR); ++ __ st_w(FSR, T2, 0); ++} ++ ++void TemplateTable::convert() { ++ // Checking ++#ifdef ASSERT ++ { ++ TosState tos_in = ilgl; ++ TosState tos_out = ilgl; ++ switch (bytecode()) { ++ case Bytecodes::_i2l: // fall through ++ case Bytecodes::_i2f: // fall through ++ case Bytecodes::_i2d: // fall through ++ case Bytecodes::_i2b: // fall through ++ case Bytecodes::_i2c: // fall through ++ case Bytecodes::_i2s: tos_in = itos; break; ++ case Bytecodes::_l2i: // fall through ++ case Bytecodes::_l2f: // fall through ++ case Bytecodes::_l2d: tos_in = ltos; break; ++ case Bytecodes::_f2i: // fall through ++ case Bytecodes::_f2l: // fall through ++ case Bytecodes::_f2d: tos_in = ftos; break; ++ case Bytecodes::_d2i: // fall through ++ case Bytecodes::_d2l: // fall through ++ case Bytecodes::_d2f: tos_in = dtos; break; ++ default : ShouldNotReachHere(); ++ } ++ switch (bytecode()) { ++ case Bytecodes::_l2i: // fall through ++ case Bytecodes::_f2i: // fall through ++ case Bytecodes::_d2i: // fall through ++ case Bytecodes::_i2b: // fall through ++ case Bytecodes::_i2c: // fall through ++ case Bytecodes::_i2s: tos_out = itos; break; ++ case Bytecodes::_i2l: // fall through ++ case Bytecodes::_f2l: // fall through ++ case Bytecodes::_d2l: tos_out = ltos; break; ++ case Bytecodes::_i2f: // fall through ++ case Bytecodes::_l2f: // fall through ++ case Bytecodes::_d2f: tos_out = ftos; break; ++ case Bytecodes::_i2d: // fall through ++ case Bytecodes::_l2d: // fall through ++ case Bytecodes::_f2d: tos_out = dtos; break; ++ default : ShouldNotReachHere(); ++ } ++ transition(tos_in, tos_out); ++ } ++#endif // ASSERT ++ // Conversion ++ switch (bytecode()) { ++ case Bytecodes::_i2l: ++ __ slli_w(FSR, FSR, 0); ++ break; ++ case Bytecodes::_i2f: ++ __ movgr2fr_w(FSF, FSR); ++ __ ffint_s_w(FSF, FSF); ++ break; ++ case Bytecodes::_i2d: ++ __ movgr2fr_w(FSF, FSR); ++ __ ffint_d_w(FSF, FSF); ++ break; ++ case Bytecodes::_i2b: ++ __ ext_w_b(FSR, FSR); ++ break; ++ case Bytecodes::_i2c: ++ __ bstrpick_d(FSR, FSR, 15, 0); // truncate upper 56 bits ++ break; ++ case Bytecodes::_i2s: ++ __ ext_w_h(FSR, FSR); ++ break; ++ case Bytecodes::_l2i: ++ __ slli_w(FSR, FSR, 0); ++ break; ++ case Bytecodes::_l2f: ++ __ movgr2fr_d(FSF, FSR); ++ __ ffint_s_l(FSF, FSF); ++ break; ++ case Bytecodes::_l2d: ++ __ movgr2fr_d(FSF, FSR); ++ __ ffint_d_l(FSF, FSF); ++ break; ++ case Bytecodes::_f2i: ++ __ ftintrz_w_s(fscratch, FSF); ++ __ movfr2gr_s(FSR, fscratch); ++ break; ++ case Bytecodes::_f2l: ++ __ ftintrz_l_s(fscratch, FSF); ++ __ movfr2gr_d(FSR, fscratch); ++ break; ++ case Bytecodes::_f2d: ++ __ fcvt_d_s(FSF, FSF); ++ break; ++ case Bytecodes::_d2i: ++ __ ftintrz_w_d(fscratch, FSF); ++ __ movfr2gr_s(FSR, fscratch); ++ break; ++ case Bytecodes::_d2l: ++ __ ftintrz_l_d(fscratch, FSF); ++ __ movfr2gr_d(FSR, fscratch); ++ break; ++ case Bytecodes::_d2f: ++ __ fcvt_s_d(FSF, FSF); ++ break; ++ default : ++ ShouldNotReachHere(); ++ } ++} ++ ++void TemplateTable::lcmp() { ++ transition(ltos, itos); ++ ++ __ pop(T0); ++ __ pop(R0); ++ ++ __ slt(AT, T0, FSR); ++ __ slt(FSR, FSR, T0); ++ __ sub_d(FSR, FSR, AT); ++} ++ ++void TemplateTable::float_cmp(bool is_float, int unordered_result) { ++ if (is_float) { ++ __ fld_s(fscratch, at_sp()); ++ __ addi_d(SP, SP, 1 * wordSize); ++ ++ if (unordered_result < 0) { ++ __ fcmp_clt_s(FCC0, FSF, fscratch); ++ __ fcmp_cult_s(FCC1, fscratch, FSF); ++ } else { ++ __ fcmp_cult_s(FCC0, FSF, fscratch); ++ __ fcmp_clt_s(FCC1, fscratch, FSF); ++ } ++ } else { ++ __ fld_d(fscratch, at_sp()); ++ __ addi_d(SP, SP, 2 * wordSize); ++ ++ if (unordered_result < 0) { ++ __ fcmp_clt_d(FCC0, FSF, fscratch); ++ __ fcmp_cult_d(FCC1, fscratch, FSF); ++ } else { ++ __ fcmp_cult_d(FCC0, FSF, fscratch); ++ __ fcmp_clt_d(FCC1, fscratch, FSF); ++ } ++ } ++ ++ __ movcf2gr(FSR, FCC0); ++ __ movcf2gr(AT, FCC1); ++ __ sub_d(FSR, FSR, AT); ++} ++ ++ ++// used registers : T3, A7, Rnext ++// FSR : return bci, this is defined by the vm specification ++// T2 : MDO taken count ++// T3 : method ++// A7 : offset ++// Rnext : next bytecode, this is required by dispatch_base ++void TemplateTable::branch(bool is_jsr, bool is_wide) { ++ __ get_method(T3); ++ __ profile_taken_branch(A7, T2); // only C2 meaningful ++ ++ const ByteSize be_offset = MethodCounters::backedge_counter_offset() + ++ InvocationCounter::counter_offset(); ++ const ByteSize inv_offset = MethodCounters::invocation_counter_offset() + ++ InvocationCounter::counter_offset(); ++ ++ // Load up T4 with the branch displacement ++ if (!is_wide) { ++ __ ld_b(A7, BCP, 1); ++ __ ld_bu(AT, BCP, 2); ++ __ slli_d(A7, A7, 8); ++ __ orr(A7, A7, AT); ++ } else { ++ __ get_4_byte_integer_at_bcp(A7, 1); ++ __ swap(A7); ++ } ++ ++ // Handle all the JSR stuff here, then exit. ++ // It's much shorter and cleaner than intermingling with the non-JSR ++ // normal-branch stuff occuring below. ++ if (is_jsr) { ++ // Pre-load the next target bytecode into Rnext ++ __ ldx_bu(Rnext, BCP, A7); ++ ++ // compute return address as bci in FSR ++ __ addi_d(FSR, BCP, (is_wide?5:3) - in_bytes(ConstMethod::codes_offset())); ++ __ ld_d(AT, T3, in_bytes(Method::const_offset())); ++ __ sub_d(FSR, FSR, AT); ++ // Adjust the bcp in BCP by the displacement in A7 ++ __ add_d(BCP, BCP, A7); ++ // jsr returns atos that is not an oop ++ // Push return address ++ __ push_i(FSR); ++ // jsr returns vtos ++ __ dispatch_only_noverify(vtos); ++ ++ return; ++ } ++ ++ // Normal (non-jsr) branch handling ++ ++ // Adjust the bcp in S0 by the displacement in T4 ++ __ add_d(BCP, BCP, A7); ++ ++ assert(UseLoopCounter || !UseOnStackReplacement, "on-stack-replacement requires loop counters"); ++ Label backedge_counter_overflow; ++ Label profile_method; ++ Label dispatch; ++ if (UseLoopCounter) { ++ // increment backedge counter for backward branches ++ // T3: method ++ // T4: target offset ++ // BCP: target bcp ++ // LVP: locals pointer ++ __ blt(R0, A7, dispatch); // check if forward or backward branch ++ ++ // check if MethodCounters exists ++ Label has_counters; ++ __ ld_d(AT, T3, in_bytes(Method::method_counters_offset())); // use AT as MDO, TEMP ++ __ bne(AT, R0, has_counters); ++ __ push2(T3, A7); ++ __ call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::build_method_counters), ++ T3); ++ __ pop2(T3, A7); ++ __ ld_d(AT, T3, in_bytes(Method::method_counters_offset())); // use AT as MDO, TEMP ++ __ beq(AT, R0, dispatch); ++ __ bind(has_counters); ++ ++ if (TieredCompilation) { ++ Label no_mdo; ++ int increment = InvocationCounter::count_increment; ++ int mask = ((1 << Tier0BackedgeNotifyFreqLog) - 1) << InvocationCounter::count_shift; ++ if (ProfileInterpreter) { ++ // Are we profiling? ++ __ ld_d(T0, Address(T3, in_bytes(Method::method_data_offset()))); ++ __ beq(T0, R0, no_mdo); ++ // Increment the MDO backedge counter ++ const Address mdo_backedge_counter(T0, in_bytes(MethodData::backedge_counter_offset()) + ++ in_bytes(InvocationCounter::counter_offset())); ++ __ increment_mask_and_jump(mdo_backedge_counter, increment, mask, ++ T1, false, Assembler::zero, &backedge_counter_overflow); ++ __ beq(R0, R0, dispatch); ++ } ++ __ bind(no_mdo); ++ // Increment backedge counter in MethodCounters* ++ __ ld_d(T0, Address(T3, Method::method_counters_offset())); ++ __ increment_mask_and_jump(Address(T0, be_offset), increment, mask, ++ T1, false, Assembler::zero, &backedge_counter_overflow); ++ if (!UseOnStackReplacement) { ++ __ bind(backedge_counter_overflow); ++ } ++ } else { ++ // increment back edge counter ++ __ ld_d(T1, T3, in_bytes(Method::method_counters_offset())); ++ __ ld_w(T0, T1, in_bytes(be_offset)); ++ __ increment(T0, InvocationCounter::count_increment); ++ __ st_w(T0, T1, in_bytes(be_offset)); ++ ++ // load invocation counter ++ __ ld_w(T1, T1, in_bytes(inv_offset)); ++ // buffer bit added, mask no needed ++ ++ // dadd backedge counter & invocation counter ++ __ add_d(T1, T1, T0); ++ ++ if (ProfileInterpreter) { ++ // Test to see if we should create a method data oop ++ // T1 : backedge counter & invocation counter ++ if (Assembler::is_simm(InvocationCounter::InterpreterProfileLimit, 12)) { ++ __ slti(AT, T1, InvocationCounter::InterpreterProfileLimit); ++ __ bne(AT, R0, dispatch); ++ } else { ++ __ li(AT, (long)&InvocationCounter::InterpreterProfileLimit); ++ __ ld_w(AT, AT, 0); ++ __ blt(T1, AT, dispatch); ++ } ++ ++ // if no method data exists, go to profile method ++ __ test_method_data_pointer(T1, profile_method); ++ ++ if (UseOnStackReplacement) { ++ if (Assembler::is_simm(InvocationCounter::InterpreterBackwardBranchLimit, 12)) { ++ __ slti(AT, T2, InvocationCounter::InterpreterBackwardBranchLimit); ++ __ bne(AT, R0, dispatch); ++ } else { ++ __ li(AT, (long)&InvocationCounter::InterpreterBackwardBranchLimit); ++ __ ld_w(AT, AT, 0); ++ __ blt(T2, AT, dispatch); ++ } ++ ++ // When ProfileInterpreter is on, the backedge_count comes ++ // from the methodDataOop, which value does not get reset on ++ // the call to frequency_counter_overflow(). ++ // To avoid excessive calls to the overflow routine while ++ // the method is being compiled, dadd a second test to make ++ // sure the overflow function is called only once every ++ // overflow_frequency. ++ const int overflow_frequency = 1024; ++ __ andi(AT, T2, overflow_frequency-1); ++ __ beq(AT, R0, backedge_counter_overflow); ++ } ++ } else { ++ if (UseOnStackReplacement) { ++ // check for overflow against AT, which is the sum of the counters ++ __ li(AT, (long)&InvocationCounter::InterpreterBackwardBranchLimit); ++ __ ld_w(AT, AT, 0); ++ __ bge(T1, AT, backedge_counter_overflow); ++ } ++ } ++ } ++ __ bind(dispatch); ++ } ++ ++ // Pre-load the next target bytecode into Rnext ++ __ ld_bu(Rnext, BCP, 0); ++ ++ // continue with the bytecode @ target ++ // FSR: return bci for jsr's, unused otherwise ++ // Rnext: target bytecode ++ // BCP: target bcp ++ __ dispatch_only(vtos); ++ ++ if (UseLoopCounter) { ++ if (ProfileInterpreter) { ++ // Out-of-line code to allocate method data oop. ++ __ bind(profile_method); ++ __ call_VM(NOREG, CAST_FROM_FN_PTR(address, InterpreterRuntime::profile_method)); ++ __ ld_bu(Rnext, BCP, 0); ++ __ set_method_data_pointer_for_bcp(); ++ __ b(dispatch); ++ } ++ ++ if (UseOnStackReplacement) { ++ // invocation counter overflow ++ __ bind(backedge_counter_overflow); ++ __ sub_d(A7, BCP, A7); // branch bcp ++ call_VM(NOREG, CAST_FROM_FN_PTR(address, ++ InterpreterRuntime::frequency_counter_overflow), A7); ++ __ ld_bu(Rnext, BCP, 0); ++ ++ // V0: osr nmethod (osr ok) or NULL (osr not possible) ++ // V1: osr adapter frame return address ++ // Rnext: target bytecode ++ // LVP: locals pointer ++ // BCP: bcp ++ __ beq(V0, R0, dispatch); ++ // nmethod may have been invalidated (VM may block upon call_VM return) ++ __ ld_w(T3, V0, nmethod::entry_bci_offset()); ++ __ li(AT, InvalidOSREntryBci); ++ __ beq(AT, T3, dispatch); ++ // We need to prepare to execute the OSR method. First we must ++ // migrate the locals and monitors off of the stack. ++ //V0: osr nmethod (osr ok) or NULL (osr not possible) ++ //V1: osr adapter frame return address ++ //Rnext: target bytecode ++ //LVP: locals pointer ++ //BCP: bcp ++ __ move(BCP, V0); ++ const Register thread = TREG; ++#ifndef OPT_THREAD ++ __ get_thread(thread); ++#endif ++ call_VM(noreg, CAST_FROM_FN_PTR(address, SharedRuntime::OSR_migration_begin)); ++ ++ // V0 is OSR buffer, move it to expected parameter location ++ // refer to osrBufferPointer in c1_LIRAssembler_loongarch.cpp ++ __ move(T0, V0); ++ ++ // pop the interpreter frame ++ __ ld_d(A7, Address(FP, frame::interpreter_frame_sender_sp_offset * wordSize)); ++ // remove frame anchor ++ __ leave(); ++ __ move(LVP, RA); ++ __ move(SP, A7); ++ ++ __ li(AT, -(StackAlignmentInBytes)); ++ __ andr(SP , SP , AT); ++ ++ // push the (possibly adjusted) return address ++ // refer to osr_entry in c1_LIRAssembler_loongarch.cpp ++ __ ld_d(AT, BCP, nmethod::osr_entry_point_offset()); ++ __ jr(AT); ++ } ++ } ++} ++ ++ ++void TemplateTable::if_0cmp(Condition cc) { ++ transition(itos, vtos); ++ // assume branch is more often taken than not (loops use backward branches) ++ Label not_taken; ++ switch(cc) { ++ case not_equal: ++ __ beq(FSR, R0, not_taken); ++ break; ++ case equal: ++ __ bne(FSR, R0, not_taken); ++ break; ++ case less: ++ __ bge(FSR, R0, not_taken); ++ break; ++ case less_equal: ++ __ blt(R0, FSR, not_taken); ++ break; ++ case greater: ++ __ bge(R0, FSR, not_taken); ++ break; ++ case greater_equal: ++ __ blt(FSR, R0, not_taken); ++ break; ++ } ++ ++ branch(false, false); ++ ++ __ bind(not_taken); ++ __ profile_not_taken_branch(FSR); ++} ++ ++void TemplateTable::if_icmp(Condition cc) { ++ transition(itos, vtos); ++ // assume branch is more often taken than not (loops use backward branches) ++ Label not_taken; ++ ++ __ pop_i(SSR); ++ switch(cc) { ++ case not_equal: ++ __ beq(SSR, FSR, not_taken); ++ break; ++ case equal: ++ __ bne(SSR, FSR, not_taken); ++ break; ++ case less: ++ __ bge(SSR, FSR, not_taken); ++ break; ++ case less_equal: ++ __ blt(FSR, SSR, not_taken); ++ break; ++ case greater: ++ __ bge(FSR, SSR, not_taken); ++ break; ++ case greater_equal: ++ __ blt(SSR, FSR, not_taken); ++ break; ++ } ++ ++ branch(false, false); ++ __ bind(not_taken); ++ __ profile_not_taken_branch(FSR); ++} ++ ++void TemplateTable::if_nullcmp(Condition cc) { ++ transition(atos, vtos); ++ // assume branch is more often taken than not (loops use backward branches) ++ Label not_taken; ++ switch(cc) { ++ case not_equal: ++ __ beq(FSR, R0, not_taken); ++ break; ++ case equal: ++ __ bne(FSR, R0, not_taken); ++ break; ++ default: ++ ShouldNotReachHere(); ++ } ++ ++ branch(false, false); ++ __ bind(not_taken); ++ __ profile_not_taken_branch(FSR); ++} ++ ++ ++void TemplateTable::if_acmp(Condition cc) { ++ transition(atos, vtos); ++ // assume branch is more often taken than not (loops use backward branches) ++ Label not_taken; ++ // __ ld_w(SSR, SP, 0); ++ __ pop_ptr(SSR); ++ switch(cc) { ++ case not_equal: ++ __ beq(SSR, FSR, not_taken); ++ break; ++ case equal: ++ __ bne(SSR, FSR, not_taken); ++ break; ++ default: ++ ShouldNotReachHere(); ++ } ++ ++ branch(false, false); ++ ++ __ bind(not_taken); ++ __ profile_not_taken_branch(FSR); ++} ++ ++// used registers : T1, T2, T3 ++// T1 : method ++// T2 : returb bci ++void TemplateTable::ret() { ++ transition(vtos, vtos); ++ ++ locals_index(T2); ++ __ ld_d(T2, T2, 0); ++ __ profile_ret(T2, T3); ++ ++ __ get_method(T1); ++ __ ld_d(BCP, T1, in_bytes(Method::const_offset())); ++ __ add_d(BCP, BCP, T2); ++ __ addi_d(BCP, BCP, in_bytes(ConstMethod::codes_offset())); ++ ++ __ dispatch_next(vtos); ++} ++ ++// used registers : T1, T2, T3 ++// T1 : method ++// T2 : returb bci ++void TemplateTable::wide_ret() { ++ transition(vtos, vtos); ++ ++ locals_index_wide(T2); ++ __ ld_d(T2, T2, 0); // get return bci, compute return bcp ++ __ profile_ret(T2, T3); ++ ++ __ get_method(T1); ++ __ ld_d(BCP, T1, in_bytes(Method::const_offset())); ++ __ add_d(BCP, BCP, T2); ++ __ addi_d(BCP, BCP, in_bytes(ConstMethod::codes_offset())); ++ ++ __ dispatch_next(vtos); ++} ++ ++// used register T2, T3, A7, Rnext ++// T2 : bytecode pointer ++// T3 : low ++// A7 : high ++// Rnext : dest bytecode, required by dispatch_base ++void TemplateTable::tableswitch() { ++ Label default_case, continue_execution; ++ transition(itos, vtos); ++ ++ // align BCP ++ __ addi_d(T2, BCP, BytesPerInt); ++ __ li(AT, -BytesPerInt); ++ __ andr(T2, T2, AT); ++ ++ // load lo & hi ++ __ ld_w(T3, T2, 1 * BytesPerInt); ++ __ swap(T3); ++ __ ld_w(A7, T2, 2 * BytesPerInt); ++ __ swap(A7); ++ ++ // check against lo & hi ++ __ blt(FSR, T3, default_case); ++ __ blt(A7, FSR, default_case); ++ ++ // lookup dispatch offset, in A7 big endian ++ __ sub_d(FSR, FSR, T3); ++ __ alsl_d(AT, FSR, T2, Address::times_4 - 1); ++ __ ld_w(A7, AT, 3 * BytesPerInt); ++ __ profile_switch_case(FSR, T4, T3); ++ ++ __ bind(continue_execution); ++ __ swap(A7); ++ __ add_d(BCP, BCP, A7); ++ __ ld_bu(Rnext, BCP, 0); ++ __ dispatch_only(vtos); ++ ++ // handle default ++ __ bind(default_case); ++ __ profile_switch_default(FSR); ++ __ ld_w(A7, T2, 0); ++ __ b(continue_execution); ++} ++ ++void TemplateTable::lookupswitch() { ++ transition(itos, itos); ++ __ stop("lookupswitch bytecode should have been rewritten"); ++} ++ ++// used registers : T2, T3, A7, Rnext ++// T2 : bytecode pointer ++// T3 : pair index ++// A7 : offset ++// Rnext : dest bytecode ++// the data after the opcode is the same as lookupswitch ++// see Rewriter::rewrite_method for more information ++void TemplateTable::fast_linearswitch() { ++ transition(itos, vtos); ++ Label loop_entry, loop, found, continue_execution; ++ ++ // swap FSR so we can avoid swapping the table entries ++ __ swap(FSR); ++ ++ // align BCP ++ __ addi_d(T2, BCP, BytesPerInt); ++ __ li(AT, -BytesPerInt); ++ __ andr(T2, T2, AT); ++ ++ // set counter ++ __ ld_w(T3, T2, BytesPerInt); ++ __ swap(T3); ++ __ b(loop_entry); ++ ++ // table search ++ __ bind(loop); ++ // get the entry value ++ __ alsl_d(AT, T3, T2, Address::times_8 - 1); ++ __ ld_w(AT, AT, 2 * BytesPerInt); ++ ++ // found? ++ __ beq(FSR, AT, found); ++ ++ __ bind(loop_entry); ++ Label L1; ++ __ bge(R0, T3, L1); ++ __ addi_d(T3, T3, -1); ++ __ b(loop); ++ __ bind(L1); ++ __ addi_d(T3, T3, -1); ++ ++ // default case ++ __ profile_switch_default(FSR); ++ __ ld_w(A7, T2, 0); ++ __ b(continue_execution); ++ ++ // entry found -> get offset ++ __ bind(found); ++ __ alsl_d(AT, T3, T2, Address::times_8 - 1); ++ __ ld_w(A7, AT, 3 * BytesPerInt); ++ __ profile_switch_case(T3, FSR, T2); ++ ++ // continue execution ++ __ bind(continue_execution); ++ __ swap(A7); ++ __ add_d(BCP, BCP, A7); ++ __ ld_bu(Rnext, BCP, 0); ++ __ dispatch_only(vtos); ++} ++ ++// used registers : T0, T1, T2, T3, A7, Rnext ++// T2 : pairs address(array) ++// Rnext : dest bytecode ++// the data after the opcode is the same as lookupswitch ++// see Rewriter::rewrite_method for more information ++void TemplateTable::fast_binaryswitch() { ++ transition(itos, vtos); ++ // Implementation using the following core algorithm: ++ // ++ // int binary_search(int key, LookupswitchPair* array, int n) { ++ // // Binary search according to "Methodik des Programmierens" by ++ // // Edsger W. Dijkstra and W.H.J. Feijen, Addison Wesley Germany 1985. ++ // int i = 0; ++ // int j = n; ++ // while (i+1 < j) { ++ // // invariant P: 0 <= i < j <= n and (a[i] <= key < a[j] or Q) ++ // // with Q: for all i: 0 <= i < n: key < a[i] ++ // // where a stands for the array and assuming that the (inexisting) ++ // // element a[n] is infinitely big. ++ // int h = (i + j) >> 1; ++ // // i < h < j ++ // if (key < array[h].fast_match()) { ++ // j = h; ++ // } else { ++ // i = h; ++ // } ++ // } ++ // // R: a[i] <= key < a[i+1] or Q ++ // // (i.e., if key is within array, i is the correct index) ++ // return i; ++ // } ++ ++ // register allocation ++ const Register array = T2; ++ const Register i = T3, j = A7; ++ const Register h = T1; ++ const Register temp = T0; ++ const Register key = FSR; ++ ++ // setup array ++ __ addi_d(array, BCP, 3*BytesPerInt); ++ __ li(AT, -BytesPerInt); ++ __ andr(array, array, AT); ++ ++ // initialize i & j ++ __ move(i, R0); ++ __ ld_w(j, array, - 1 * BytesPerInt); ++ // Convert j into native byteordering ++ __ swap(j); ++ ++ // and start ++ Label entry; ++ __ b(entry); ++ ++ // binary search loop ++ { ++ Label loop; ++ __ bind(loop); ++ // int h = (i + j) >> 1; ++ __ add_d(h, i, j); ++ __ srli_d(h, h, 1); ++ // if (key < array[h].fast_match()) { ++ // j = h; ++ // } else { ++ // i = h; ++ // } ++ // Convert array[h].match to native byte-ordering before compare ++ __ alsl_d(AT, h, array, Address::times_8 - 1); ++ __ ld_w(temp, AT, 0 * BytesPerInt); ++ __ swap(temp); ++ ++ __ slt(AT, key, temp); ++ __ maskeqz(i, i, AT); ++ __ masknez(temp, h, AT); ++ __ OR(i, i, temp); ++ __ masknez(j, j, AT); ++ __ maskeqz(temp, h, AT); ++ __ OR(j, j, temp); ++ ++ // while (i+1 < j) ++ __ bind(entry); ++ __ addi_d(h, i, 1); ++ __ blt(h, j, loop); ++ } ++ ++ // end of binary search, result index is i (must check again!) ++ Label default_case; ++ // Convert array[i].match to native byte-ordering before compare ++ __ alsl_d(AT, i, array, Address::times_8 - 1); ++ __ ld_w(temp, AT, 0 * BytesPerInt); ++ __ swap(temp); ++ __ bne(key, temp, default_case); ++ ++ // entry found -> j = offset ++ __ alsl_d(AT, i, array, Address::times_8 - 1); ++ __ ld_w(j, AT, 1 * BytesPerInt); ++ __ profile_switch_case(i, key, array); ++ __ swap(j); ++ ++ __ add_d(BCP, BCP, j); ++ __ ld_bu(Rnext, BCP, 0); ++ __ dispatch_only(vtos); ++ ++ // default case -> j = default offset ++ __ bind(default_case); ++ __ profile_switch_default(i); ++ __ ld_w(j, array, - 2 * BytesPerInt); ++ __ swap(j); ++ __ add_d(BCP, BCP, j); ++ __ ld_bu(Rnext, BCP, 0); ++ __ dispatch_only(vtos); ++} ++ ++void TemplateTable::_return(TosState state) { ++ transition(state, state); ++ assert(_desc->calls_vm(), ++ "inconsistent calls_vm information"); // call in remove_activation ++ ++ if (_desc->bytecode() == Bytecodes::_return_register_finalizer) { ++ assert(state == vtos, "only valid state"); ++ __ ld_d(T1, aaddress(0)); ++ __ load_klass(LVP, T1); ++ __ ld_w(LVP, LVP, in_bytes(Klass::access_flags_offset())); ++ __ li(AT, JVM_ACC_HAS_FINALIZER); ++ __ andr(AT, AT, LVP); ++ Label skip_register_finalizer; ++ __ beq(AT, R0, skip_register_finalizer); ++ __ call_VM(noreg, CAST_FROM_FN_PTR(address, ++ InterpreterRuntime::register_finalizer), T1); ++ __ bind(skip_register_finalizer); ++ } ++ ++ // Narrow result if state is itos but result type is smaller. ++ // Need to narrow in the return bytecode rather than in generate_return_entry ++ // since compiled code callers expect the result to already be narrowed. ++ if (state == itos) { ++ __ narrow(FSR); ++ } ++ ++ __ remove_activation(state, T4); ++ __ membar(__ StoreStore); ++ ++ __ jr(T4); ++} ++ ++// ---------------------------------------------------------------------------- ++// Volatile variables demand their effects be made known to all CPU's ++// in order. Store buffers on most chips allow reads & writes to ++// reorder; the JMM's ReadAfterWrite.java test fails in -Xint mode ++// without some kind of memory barrier (i.e., it's not sufficient that ++// the interpreter does not reorder volatile references, the hardware ++// also must not reorder them). ++// ++// According to the new Java Memory Model (JMM): ++// (1) All volatiles are serialized wrt to each other. ALSO reads & ++// writes act as aquire & release, so: ++// (2) A read cannot let unrelated NON-volatile memory refs that ++// happen after the read float up to before the read. It's OK for ++// non-volatile memory refs that happen before the volatile read to ++// float down below it. ++// (3) Similar a volatile write cannot let unrelated NON-volatile ++// memory refs that happen BEFORE the write float down to after the ++// write. It's OK for non-volatile memory refs that happen after the ++// volatile write to float up before it. ++// ++// We only put in barriers around volatile refs (they are expensive), ++// not _between_ memory refs (that would require us to track the ++// flavor of the previous memory refs). Requirements (2) and (3) ++// require some barriers before volatile stores and after volatile ++// loads. These nearly cover requirement (1) but miss the ++// volatile-store-volatile-load case. This final case is placed after ++// volatile-stores although it could just as well go before ++// volatile-loads. ++void TemplateTable::volatile_barrier() { ++ if(os::is_MP()) __ membar(__ StoreLoad); ++} ++ ++// we dont shift left 2 bits in get_cache_and_index_at_bcp ++// for we always need shift the index we use it. the ConstantPoolCacheEntry ++// is 16-byte long, index is the index in ++// ConstantPoolCache, so cache + base_offset() + index * 16 is ++// the corresponding ConstantPoolCacheEntry ++// used registers : T2 ++// NOTE : the returned index need also shift left 4 to get the address! ++void TemplateTable::resolve_cache_and_index(int byte_no, ++ Register Rcache, ++ Register index, ++ size_t index_size) { ++ assert(byte_no == f1_byte || byte_no == f2_byte, "byte_no out of range"); ++ const Register temp = A1; ++ assert_different_registers(Rcache, index); ++ ++ Label resolved; ++ __ get_cache_and_index_and_bytecode_at_bcp(Rcache, index, temp, byte_no, 1, index_size); ++ // is resolved? ++ int i = (int)bytecode(); ++ __ addi_d(temp, temp, -i); ++ __ beq(temp, R0, resolved); ++ // resolve first time through ++ address entry; ++ switch (bytecode()) { ++ case Bytecodes::_getstatic : // fall through ++ case Bytecodes::_putstatic : // fall through ++ case Bytecodes::_getfield : // fall through ++ case Bytecodes::_putfield : ++ entry = CAST_FROM_FN_PTR(address, InterpreterRuntime::resolve_get_put); ++ break; ++ case Bytecodes::_invokevirtual : // fall through ++ case Bytecodes::_invokespecial : // fall through ++ case Bytecodes::_invokestatic : // fall through ++ case Bytecodes::_invokeinterface: ++ entry = CAST_FROM_FN_PTR(address, InterpreterRuntime::resolve_invoke); ++ break; ++ case Bytecodes::_invokehandle: ++ entry = CAST_FROM_FN_PTR(address, InterpreterRuntime::resolve_invokehandle); ++ break; ++ case Bytecodes::_invokedynamic: ++ entry = CAST_FROM_FN_PTR(address, InterpreterRuntime::resolve_invokedynamic); ++ break; ++ default : ++ fatal(err_msg("unexpected bytecode: %s", Bytecodes::name(bytecode()))); ++ break; ++ } ++ ++ __ li(temp, i); ++ __ call_VM(NOREG, entry, temp); ++ ++ // Update registers with resolved info ++ __ get_cache_and_index_at_bcp(Rcache, index, 1, index_size); ++ __ bind(resolved); ++} ++ ++// The Rcache and index registers must be set before call ++void TemplateTable::load_field_cp_cache_entry(Register obj, ++ Register cache, ++ Register index, ++ Register off, ++ Register flags, ++ bool is_static = false) { ++ assert_different_registers(cache, index, flags, off); ++ ++ ByteSize cp_base_offset = ConstantPoolCache::base_offset(); ++ // Field offset ++ __ alsl_d(AT, index, cache, Address::times_ptr - 1); ++ __ ld_d(off, AT, in_bytes(cp_base_offset + ConstantPoolCacheEntry::f2_offset())); ++ // Flags ++ __ ld_d(flags, AT, in_bytes(cp_base_offset + ConstantPoolCacheEntry::flags_offset())); ++ ++ // klass overwrite register ++ if (is_static) { ++ __ ld_d(obj, AT, in_bytes(cp_base_offset + ConstantPoolCacheEntry::f1_offset())); ++ const int mirror_offset = in_bytes(Klass::java_mirror_offset()); ++ __ ld_d(obj, Address(obj, mirror_offset)); ++ ++ __ verify_oop(obj); ++ } ++} ++ ++// get the method, itable_index and flags of the current invoke ++void TemplateTable::load_invoke_cp_cache_entry(int byte_no, ++ Register method, ++ Register itable_index, ++ Register flags, ++ bool is_invokevirtual, ++ bool is_invokevfinal, /*unused*/ ++ bool is_invokedynamic) { ++ // setup registers ++ const Register cache = T3; ++ const Register index = T1; ++ assert_different_registers(method, flags); ++ assert_different_registers(method, cache, index); ++ assert_different_registers(itable_index, flags); ++ assert_different_registers(itable_index, cache, index); ++ assert(is_invokevirtual == (byte_no == f2_byte), "is invokevirtual flag redundant"); ++ // determine constant pool cache field offsets ++ const int method_offset = in_bytes( ++ ConstantPoolCache::base_offset() + ++ ((byte_no == f2_byte) ++ ? ConstantPoolCacheEntry::f2_offset() ++ : ConstantPoolCacheEntry::f1_offset())); ++ const int flags_offset = in_bytes(ConstantPoolCache::base_offset() + ++ ConstantPoolCacheEntry::flags_offset()); ++ // access constant pool cache fields ++ const int index_offset = in_bytes(ConstantPoolCache::base_offset() + ++ ConstantPoolCacheEntry::f2_offset()); ++ ++ size_t index_size = (is_invokedynamic ? sizeof(u4): sizeof(u2)); ++ resolve_cache_and_index(byte_no, cache, index, index_size); ++ ++ __ alsl_d(AT, index, cache, Address::times_ptr - 1); ++ __ ld_d(method, AT, method_offset); ++ ++ if (itable_index != NOREG) { ++ __ ld_d(itable_index, AT, index_offset); ++ } ++ __ ld_d(flags, AT, flags_offset); ++} ++ ++// The registers cache and index expected to be set before call. ++// Correct values of the cache and index registers are preserved. ++void TemplateTable::jvmti_post_field_access(Register cache, Register index, ++ bool is_static, bool has_tos) { ++ // do the JVMTI work here to avoid disturbing the register state below ++ // We use c_rarg registers here because we want to use the register used in ++ // the call to the VM ++ if (JvmtiExport::can_post_field_access()) { ++ // Check to see if a field access watch has been set before we ++ // take the time to call into the VM. ++ Label L1; ++ // kill FSR ++ Register tmp1 = T2; ++ Register tmp2 = T1; ++ Register tmp3 = T3; ++ assert_different_registers(cache, index, AT); ++ __ li(AT, (intptr_t)JvmtiExport::get_field_access_count_addr()); ++ __ ld_w(AT, AT, 0); ++ __ beq(AT, R0, L1); ++ ++ __ get_cache_and_index_at_bcp(tmp2, tmp3, 1); ++ ++ // cache entry pointer ++ __ addi_d(tmp2, tmp2, in_bytes(ConstantPoolCache::base_offset())); ++ __ shl(tmp3, LogBytesPerWord); ++ __ add_d(tmp2, tmp2, tmp3); ++ if (is_static) { ++ __ move(tmp1, R0); ++ } else { ++ __ ld_d(tmp1, SP, 0); ++ __ verify_oop(tmp1); ++ } ++ // tmp1: object pointer or NULL ++ // tmp2: cache entry pointer ++ // tmp3: jvalue object on the stack ++ __ call_VM(NOREG, CAST_FROM_FN_PTR(address, ++ InterpreterRuntime::post_field_access), ++ tmp1, tmp2, tmp3); ++ __ get_cache_and_index_at_bcp(cache, index, 1); ++ __ bind(L1); ++ } ++} ++ ++void TemplateTable::pop_and_check_object(Register r) { ++ __ pop_ptr(r); ++ __ null_check(r); // for field access must check obj. ++ __ verify_oop(r); ++} ++ ++// used registers : T1, T2, T3, T1 ++// T1 : flags ++// T2 : off ++// T3 : obj ++// T1 : field address ++// The flags 31, 30, 29, 28 together build a 4 bit number 0 to 8 with the ++// following mapping to the TosState states: ++// btos: 0 ++// ctos: 1 ++// stos: 2 ++// itos: 3 ++// ltos: 4 ++// ftos: 5 ++// dtos: 6 ++// atos: 7 ++// vtos: 8 ++// see ConstantPoolCacheEntry::set_field for more info ++void TemplateTable::getfield_or_static(int byte_no, bool is_static) { ++ transition(vtos, vtos); ++ ++ const Register cache = T3; ++ const Register index = T0; ++ ++ const Register obj = T3; ++ const Register off = T2; ++ const Register flags = T1; ++ ++ const Register scratch = T8; ++ ++ resolve_cache_and_index(byte_no, cache, index, sizeof(u2)); ++ jvmti_post_field_access(cache, index, is_static, false); ++ load_field_cp_cache_entry(obj, cache, index, off, flags, is_static); ++ ++ { ++ __ li(scratch, 1 << ConstantPoolCacheEntry::is_volatile_shift); ++ __ andr(scratch, scratch, flags); ++ ++ Label notVolatile; ++ __ beq(scratch, R0, notVolatile); ++ volatile_barrier(); ++ __ bind(notVolatile); ++ } ++ ++ if (!is_static) pop_and_check_object(obj); ++ __ add_d(index, obj, off); ++ ++ ++ Label Done, notByte, notBool, notInt, notShort, notChar, ++ notLong, notFloat, notObj, notDouble; ++ ++ assert(btos == 0, "change code, btos != 0"); ++ __ srli_d(flags, flags, ConstantPoolCacheEntry::tos_state_shift); ++ __ andi(flags, flags, ConstantPoolCacheEntry::tos_state_mask); ++ __ bne(flags, R0, notByte); ++ ++ // btos ++ __ ld_b(FSR, index, 0); ++ __ push(btos); ++ ++ // Rewrite bytecode to be faster ++ if (!is_static) { ++ patch_bytecode(Bytecodes::_fast_bgetfield, T3, T2); ++ } ++ __ b(Done); ++ ++ __ bind(notByte); ++ __ li(AT, ztos); ++ __ bne(flags, AT, notBool); ++ ++ // ztos ++ __ ld_b(FSR, index, 0); ++ __ push(ztos); ++ ++ // Rewrite bytecode to be faster ++ if (!is_static) { ++ patch_bytecode(Bytecodes::_fast_bgetfield, T3, T2); ++ } ++ __ b(Done); ++ ++ __ bind(notBool); ++ __ li(AT, itos); ++ __ bne(flags, AT, notInt); ++ ++ // itos ++ __ ld_w(FSR, index, 0); ++ __ push(itos); ++ ++ // Rewrite bytecode to be faster ++ if (!is_static) { ++ patch_bytecode(Bytecodes::_fast_igetfield, T3, T2); ++ } ++ __ b(Done); ++ ++ __ bind(notInt); ++ __ li(AT, atos); ++ __ bne(flags, AT, notObj); ++ ++ // atos ++ //add for compressedoops ++ __ load_heap_oop(FSR, Address(index, 0)); ++ __ push(atos); ++ ++ if (!is_static) { ++ patch_bytecode(Bytecodes::_fast_agetfield, T3, T2); ++ } ++ __ b(Done); ++ ++ __ bind(notObj); ++ __ li(AT, ctos); ++ __ bne(flags, AT, notChar); ++ ++ // ctos ++ __ ld_hu(FSR, index, 0); ++ __ push(ctos); ++ ++ if (!is_static) { ++ patch_bytecode(Bytecodes::_fast_cgetfield, T3, T2); ++ } ++ __ b(Done); ++ ++ __ bind(notChar); ++ __ li(AT, stos); ++ __ bne(flags, AT, notShort); ++ ++ // stos ++ __ ld_h(FSR, index, 0); ++ __ push(stos); ++ ++ if (!is_static) { ++ patch_bytecode(Bytecodes::_fast_sgetfield, T3, T2); ++ } ++ __ b(Done); ++ ++ __ bind(notShort); ++ __ li(AT, ltos); ++ __ bne(flags, AT, notLong); ++ ++ // ltos ++ __ ld_d(FSR, index, 0 * wordSize); ++ __ push(ltos); ++ ++ // Don't rewrite to _fast_lgetfield for potential volatile case. ++ __ b(Done); ++ ++ __ bind(notLong); ++ __ li(AT, ftos); ++ __ bne(flags, AT, notFloat); ++ ++ // ftos ++ __ fld_s(FSF, index, 0); ++ __ push(ftos); ++ ++ if (!is_static) { ++ patch_bytecode(Bytecodes::_fast_fgetfield, T3, T2); ++ } ++ __ b(Done); ++ ++ __ bind(notFloat); ++ __ li(AT, dtos); ++#ifdef ASSERT ++ __ bne(flags, AT, notDouble); ++#endif ++ ++ // dtos ++ __ fld_d(FSF, index, 0 * wordSize); ++ __ push(dtos); ++ ++ if (!is_static) { ++ patch_bytecode(Bytecodes::_fast_dgetfield, T3, T2); ++ } ++ ++#ifdef ASSERT ++ __ b(Done); ++ __ bind(notDouble); ++ __ stop("Bad state"); ++#endif ++ ++ __ bind(Done); ++ ++ { ++ Label notVolatile; ++ __ beq(scratch, R0, notVolatile); ++ volatile_barrier(); ++ __ bind(notVolatile); ++ } ++} ++ ++ ++void TemplateTable::getfield(int byte_no) { ++ getfield_or_static(byte_no, false); ++} ++ ++void TemplateTable::getstatic(int byte_no) { ++ getfield_or_static(byte_no, true); ++} ++ ++// The registers cache and index expected to be set before call. ++// The function may destroy various registers, just not the cache and index registers. ++void TemplateTable::jvmti_post_field_mod(Register cache, Register index, bool is_static) { ++ transition(vtos, vtos); ++ ++ ByteSize cp_base_offset = ConstantPoolCache::base_offset(); ++ ++ if (JvmtiExport::can_post_field_modification()) { ++ // Check to see if a field modification watch has been set before ++ // we take the time to call into the VM. ++ Label L1; ++ //kill AT, T1, T2, T3, T4 ++ Register tmp1 = T2; ++ Register tmp2 = T1; ++ Register tmp3 = T3; ++ Register tmp4 = T4; ++ assert_different_registers(cache, index, tmp4); ++ ++ __ li(AT, JvmtiExport::get_field_modification_count_addr()); ++ __ ld_w(AT, AT, 0); ++ __ beq(AT, R0, L1); ++ ++ __ get_cache_and_index_at_bcp(tmp2, tmp4, 1); ++ ++ if (is_static) { ++ __ move(tmp1, R0); ++ } else { ++ // Life is harder. The stack holds the value on top, followed by ++ // the object. We don't know the size of the value, though; it ++ // could be one or two words depending on its type. As a result, ++ // we must find the type to determine where the object is. ++ Label two_word, valsize_known; ++ __ alsl_d(AT, tmp4, tmp2, Address::times_8 - 1); ++ __ ld_d(tmp3, AT, in_bytes(cp_base_offset + ++ ConstantPoolCacheEntry::flags_offset())); ++ __ shr(tmp3, ConstantPoolCacheEntry::tos_state_shift); ++ ++ ConstantPoolCacheEntry::verify_tos_state_shift(); ++ __ move(tmp1, SP); ++ __ li(AT, ltos); ++ __ beq(tmp3, AT, two_word); ++ __ li(AT, dtos); ++ __ beq(tmp3, AT, two_word); ++ __ addi_d(tmp1, tmp1, Interpreter::expr_offset_in_bytes(1) ); ++ __ b(valsize_known); ++ ++ __ bind(two_word); ++ __ addi_d(tmp1, tmp1, Interpreter::expr_offset_in_bytes(2)); ++ ++ __ bind(valsize_known); ++ // setup object pointer ++ __ ld_d(tmp1, tmp1, 0 * wordSize); ++ } ++ // cache entry pointer ++ __ addi_d(tmp2, tmp2, in_bytes(cp_base_offset)); ++ __ shl(tmp4, LogBytesPerWord); ++ __ add_d(tmp2, tmp2, tmp4); ++ // object (tos) ++ __ move(tmp3, SP); ++ // tmp1: object pointer set up above (NULL if static) ++ // tmp2: cache entry pointer ++ // tmp3: jvalue object on the stack ++ __ call_VM(NOREG, ++ CAST_FROM_FN_PTR(address, ++ InterpreterRuntime::post_field_modification), ++ tmp1, tmp2, tmp3); ++ __ get_cache_and_index_at_bcp(cache, index, 1); ++ __ bind(L1); ++ } ++} ++ ++// used registers : T0, T1, T2, T3, T8 ++// T1 : flags ++// T2 : off ++// T3 : obj ++// T8 : volatile bit ++// see ConstantPoolCacheEntry::set_field for more info ++void TemplateTable::putfield_or_static(int byte_no, bool is_static) { ++ transition(vtos, vtos); ++ ++ const Register cache = T3; ++ const Register index = T0; ++ const Register obj = T3; ++ const Register off = T2; ++ const Register flags = T1; ++ const Register bc = T3; ++ ++ const Register scratch = T8; ++ ++ resolve_cache_and_index(byte_no, cache, index, sizeof(u2)); ++ jvmti_post_field_mod(cache, index, is_static); ++ load_field_cp_cache_entry(obj, cache, index, off, flags, is_static); ++ ++ Label Done; ++ { ++ __ li(scratch, 1 << ConstantPoolCacheEntry::is_volatile_shift); ++ __ andr(scratch, scratch, flags); ++ ++ Label notVolatile; ++ __ beq(scratch, R0, notVolatile); ++ volatile_barrier(); ++ __ bind(notVolatile); ++ } ++ ++ Label notByte, notBool, notInt, notShort, notChar, notLong, notFloat, notObj, notDouble; ++ ++ assert(btos == 0, "change code, btos != 0"); ++ ++ // btos ++ __ srli_d(flags, flags, ConstantPoolCacheEntry::tos_state_shift); ++ __ andi(flags, flags, ConstantPoolCacheEntry::tos_state_mask); ++ __ bne(flags, R0, notByte); ++ ++ __ pop(btos); ++ if (!is_static) { ++ pop_and_check_object(obj); ++ } ++ __ add_d(AT, obj, off); ++ __ st_b(FSR, AT, 0); ++ ++ if (!is_static) { ++ patch_bytecode(Bytecodes::_fast_bputfield, bc, off, true, byte_no); ++ } ++ __ b(Done); ++ ++ // ztos ++ __ bind(notByte); ++ __ li(AT, ztos); ++ __ bne(flags, AT, notBool); ++ ++ __ pop(ztos); ++ if (!is_static) { ++ pop_and_check_object(obj); ++ } ++ __ add_d(AT, obj, off); ++ __ andi(FSR, FSR, 0x1); ++ __ st_b(FSR, AT, 0); ++ ++ if (!is_static) { ++ patch_bytecode(Bytecodes::_fast_zputfield, bc, off, true, byte_no); ++ } ++ __ b(Done); ++ ++ // itos ++ __ bind(notBool); ++ __ li(AT, itos); ++ __ bne(flags, AT, notInt); ++ ++ __ pop(itos); ++ if (!is_static) { ++ pop_and_check_object(obj); ++ } ++ __ add_d(AT, obj, off); ++ __ st_w(FSR, AT, 0); ++ ++ if (!is_static) { ++ patch_bytecode(Bytecodes::_fast_iputfield, bc, off, true, byte_no); ++ } ++ __ b(Done); ++ ++ // atos ++ __ bind(notInt); ++ __ li(AT, atos); ++ __ bne(flags, AT, notObj); ++ ++ __ pop(atos); ++ if (!is_static) { ++ pop_and_check_object(obj); ++ } ++ ++ do_oop_store(_masm, Address(obj, off, Address::times_1, 0), FSR, _bs->kind(), false); ++ ++ if (!is_static) { ++ patch_bytecode(Bytecodes::_fast_aputfield, bc, off, true, byte_no); ++ } ++ __ b(Done); ++ ++ // ctos ++ __ bind(notObj); ++ __ li(AT, ctos); ++ __ bne(flags, AT, notChar); ++ ++ __ pop(ctos); ++ if (!is_static) { ++ pop_and_check_object(obj); ++ } ++ __ add_d(AT, obj, off); ++ __ st_h(FSR, AT, 0); ++ if (!is_static) { ++ patch_bytecode(Bytecodes::_fast_cputfield, bc, off, true, byte_no); ++ } ++ __ b(Done); ++ ++ // stos ++ __ bind(notChar); ++ __ li(AT, stos); ++ __ bne(flags, AT, notShort); ++ ++ __ pop(stos); ++ if (!is_static) { ++ pop_and_check_object(obj); ++ } ++ __ add_d(AT, obj, off); ++ __ st_h(FSR, AT, 0); ++ if (!is_static) { ++ patch_bytecode(Bytecodes::_fast_sputfield, bc, off, true, byte_no); ++ } ++ __ b(Done); ++ ++ // ltos ++ __ bind(notShort); ++ __ li(AT, ltos); ++ __ bne(flags, AT, notLong); ++ ++ __ pop(ltos); ++ if (!is_static) { ++ pop_and_check_object(obj); ++ } ++ __ add_d(AT, obj, off); ++ __ st_d(FSR, AT, 0); ++ if (!is_static) { ++ patch_bytecode(Bytecodes::_fast_lputfield, bc, off, true, byte_no); ++ } ++ __ b(Done); ++ ++ // ftos ++ __ bind(notLong); ++ __ li(AT, ftos); ++ __ bne(flags, AT, notFloat); ++ ++ __ pop(ftos); ++ if (!is_static) { ++ pop_and_check_object(obj); ++ } ++ __ add_d(AT, obj, off); ++ __ fst_s(FSF, AT, 0); ++ if (!is_static) { ++ patch_bytecode(Bytecodes::_fast_fputfield, bc, off, true, byte_no); ++ } ++ __ b(Done); ++ ++ ++ // dtos ++ __ bind(notFloat); ++ __ li(AT, dtos); ++#ifdef ASSERT ++ __ bne(flags, AT, notDouble); ++#endif ++ ++ __ pop(dtos); ++ if (!is_static) { ++ pop_and_check_object(obj); ++ } ++ __ add_d(AT, obj, off); ++ __ fst_d(FSF, AT, 0); ++ if (!is_static) { ++ patch_bytecode(Bytecodes::_fast_dputfield, bc, off, true, byte_no); ++ } ++ ++#ifdef ASSERT ++ __ b(Done); ++ ++ __ bind(notDouble); ++ __ stop("Bad state"); ++#endif ++ ++ __ bind(Done); ++ ++ { ++ Label notVolatile; ++ __ beq(scratch, R0, notVolatile); ++ volatile_barrier(); ++ __ bind(notVolatile); ++ } ++} ++ ++void TemplateTable::putfield(int byte_no) { ++ putfield_or_static(byte_no, false); ++} ++ ++void TemplateTable::putstatic(int byte_no) { ++ putfield_or_static(byte_no, true); ++} ++ ++// used registers : T1, T2, T3 ++// T1 : cp_entry ++// T2 : obj ++// T3 : value pointer ++void TemplateTable::jvmti_post_fast_field_mod() { ++ if (JvmtiExport::can_post_field_modification()) { ++ // Check to see if a field modification watch has been set before ++ // we take the time to call into the VM. ++ Label L2; ++ //kill AT, T1, T2, T3, T4 ++ Register tmp1 = T2; ++ Register tmp2 = T1; ++ Register tmp3 = T3; ++ Register tmp4 = T4; ++ __ li(AT, JvmtiExport::get_field_modification_count_addr()); ++ __ ld_w(tmp3, AT, 0); ++ __ beq(tmp3, R0, L2); ++ __ pop_ptr(tmp1); ++ __ verify_oop(tmp1); ++ __ push_ptr(tmp1); ++ switch (bytecode()) { // load values into the jvalue object ++ case Bytecodes::_fast_aputfield: __ push_ptr(FSR); break; ++ case Bytecodes::_fast_bputfield: // fall through ++ case Bytecodes::_fast_zputfield: // fall through ++ case Bytecodes::_fast_sputfield: // fall through ++ case Bytecodes::_fast_cputfield: // fall through ++ case Bytecodes::_fast_iputfield: __ push_i(FSR); break; ++ case Bytecodes::_fast_dputfield: __ push_d(FSF); break; ++ case Bytecodes::_fast_fputfield: __ push_f(); break; ++ case Bytecodes::_fast_lputfield: __ push_l(FSR); break; ++ default: ShouldNotReachHere(); ++ } ++ __ move(tmp3, SP); ++ // access constant pool cache entry ++ __ get_cache_entry_pointer_at_bcp(tmp2, FSR, 1); ++ __ verify_oop(tmp1); ++ // tmp1: object pointer copied above ++ // tmp2: cache entry pointer ++ // tmp3: jvalue object on the stack ++ __ call_VM(NOREG, ++ CAST_FROM_FN_PTR(address, ++ InterpreterRuntime::post_field_modification), ++ tmp1, tmp2, tmp3); ++ ++ switch (bytecode()) { // restore tos values ++ case Bytecodes::_fast_aputfield: __ pop_ptr(FSR); break; ++ case Bytecodes::_fast_bputfield: // fall through ++ case Bytecodes::_fast_zputfield: // fall through ++ case Bytecodes::_fast_sputfield: // fall through ++ case Bytecodes::_fast_cputfield: // fall through ++ case Bytecodes::_fast_iputfield: __ pop_i(FSR); break; ++ case Bytecodes::_fast_dputfield: __ pop_d(); break; ++ case Bytecodes::_fast_fputfield: __ pop_f(); break; ++ case Bytecodes::_fast_lputfield: __ pop_l(FSR); break; ++ } ++ __ bind(L2); ++ } ++} ++ ++// used registers : T2, T3, T1 ++// T2 : index & off & field address ++// T3 : cache & obj ++// T1 : flags ++void TemplateTable::fast_storefield(TosState state) { ++ transition(state, vtos); ++ ++ const Register scratch = T8; ++ ++ ByteSize base = ConstantPoolCache::base_offset(); ++ ++ jvmti_post_fast_field_mod(); ++ ++ // access constant pool cache ++ __ get_cache_and_index_at_bcp(T3, T2, 1); ++ ++ // Must prevent reordering of the following cp cache loads with bytecode load ++ __ membar(__ LoadLoad); ++ ++ // test for volatile with T1 ++ __ alsl_d(AT, T2, T3, Address::times_8 - 1); ++ __ ld_d(T1, AT, in_bytes(base + ConstantPoolCacheEntry::flags_offset())); ++ ++ // replace index with field offset from cache entry ++ __ ld_d(T2, AT, in_bytes(base + ConstantPoolCacheEntry::f2_offset())); ++ ++ Label Done; ++ { ++ __ li(scratch, 1 << ConstantPoolCacheEntry::is_volatile_shift); ++ __ andr(scratch, scratch, T1); ++ ++ Label notVolatile; ++ __ beq(scratch, R0, notVolatile); ++ volatile_barrier(); ++ __ bind(notVolatile); ++ } ++ ++ // Get object from stack ++ pop_and_check_object(T3); ++ ++ if (bytecode() != Bytecodes::_fast_aputfield) { ++ // field address ++ __ add_d(T2, T3, T2); ++ } ++ ++ // access field ++ switch (bytecode()) { ++ case Bytecodes::_fast_zputfield: ++ __ andi(FSR, FSR, 0x1); // boolean is true if LSB is 1 ++ // fall through to bputfield ++ case Bytecodes::_fast_bputfield: ++ __ st_b(FSR, T2, 0); ++ break; ++ case Bytecodes::_fast_sputfield: // fall through ++ case Bytecodes::_fast_cputfield: ++ __ st_h(FSR, T2, 0); ++ break; ++ case Bytecodes::_fast_iputfield: ++ __ st_w(FSR, T2, 0); ++ break; ++ case Bytecodes::_fast_lputfield: ++ __ st_d(FSR, T2, 0 * wordSize); ++ break; ++ case Bytecodes::_fast_fputfield: ++ __ fst_s(FSF, T2, 0); ++ break; ++ case Bytecodes::_fast_dputfield: ++ __ fst_d(FSF, T2, 0 * wordSize); ++ break; ++ case Bytecodes::_fast_aputfield: ++ do_oop_store(_masm, Address(T3, T2, Address::times_1, 0), FSR, _bs->kind(), false); ++ break; ++ default: ++ ShouldNotReachHere(); ++ } ++ ++ { ++ Label notVolatile; ++ __ beq(scratch, R0, notVolatile); ++ volatile_barrier(); ++ __ bind(notVolatile); ++ } ++} ++ ++// used registers : T2, T3, T1 ++// T3 : cp_entry & cache ++// T2 : index & offset ++void TemplateTable::fast_accessfield(TosState state) { ++ transition(atos, state); ++ ++ const Register scratch = T8; ++ ++ // do the JVMTI work here to avoid disturbing the register state below ++ if (JvmtiExport::can_post_field_access()) { ++ // Check to see if a field access watch has been set before we take ++ // the time to call into the VM. ++ Label L1; ++ __ li(AT, (intptr_t)JvmtiExport::get_field_access_count_addr()); ++ __ ld_w(T3, AT, 0); ++ __ beq(T3, R0, L1); ++ // access constant pool cache entry ++ __ get_cache_entry_pointer_at_bcp(T3, T1, 1); ++ __ move(TSR, FSR); ++ __ verify_oop(FSR); ++ // FSR: object pointer copied above ++ // T3: cache entry pointer ++ __ call_VM(NOREG, ++ CAST_FROM_FN_PTR(address, InterpreterRuntime::post_field_access), ++ FSR, T3); ++ __ move(FSR, TSR); ++ __ bind(L1); ++ } ++ ++ // access constant pool cache ++ __ get_cache_and_index_at_bcp(T3, T2, 1); ++ ++ // Must prevent reordering of the following cp cache loads with bytecode load ++ __ membar(__ LoadLoad); ++ ++ // replace index with field offset from cache entry ++ __ alsl_d(AT, T2, T3, Address::times_8 - 1); ++ __ ld_d(T2, AT, in_bytes(ConstantPoolCache::base_offset() + ConstantPoolCacheEntry::f2_offset())); ++ ++ { ++ __ ld_d(AT, AT, in_bytes(ConstantPoolCache::base_offset() + ConstantPoolCacheEntry::flags_offset())); ++ __ li(scratch, 1 << ConstantPoolCacheEntry::is_volatile_shift); ++ __ andr(scratch, scratch, AT); ++ ++ Label notVolatile; ++ __ beq(scratch, R0, notVolatile); ++ volatile_barrier(); ++ __ bind(notVolatile); ++ } ++ ++ // FSR: object ++ __ verify_oop(FSR); ++ __ null_check(FSR); ++ // field addresses ++ __ add_d(FSR, FSR, T2); ++ ++ // access field ++ switch (bytecode()) { ++ case Bytecodes::_fast_bgetfield: ++ __ ld_b(FSR, FSR, 0); ++ break; ++ case Bytecodes::_fast_sgetfield: ++ __ ld_h(FSR, FSR, 0); ++ break; ++ case Bytecodes::_fast_cgetfield: ++ __ ld_hu(FSR, FSR, 0); ++ break; ++ case Bytecodes::_fast_igetfield: ++ __ ld_w(FSR, FSR, 0); ++ break; ++ case Bytecodes::_fast_lgetfield: ++ __ stop("should not be rewritten"); ++ break; ++ case Bytecodes::_fast_fgetfield: ++ __ fld_s(FSF, FSR, 0); ++ break; ++ case Bytecodes::_fast_dgetfield: ++ __ fld_d(FSF, FSR, 0); ++ break; ++ case Bytecodes::_fast_agetfield: ++ __ load_heap_oop(FSR, Address(FSR, 0)); ++ __ verify_oop(FSR); ++ break; ++ default: ++ ShouldNotReachHere(); ++ } ++ ++ { ++ Label notVolatile; ++ __ beq(scratch, R0, notVolatile); ++ volatile_barrier(); ++ __ bind(notVolatile); ++ } ++} ++ ++// generator for _fast_iaccess_0, _fast_aaccess_0, _fast_faccess_0 ++// used registers : T1, T2, T3, T1 ++// T1 : obj & field address ++// T2 : off ++// T3 : cache ++// T1 : index ++void TemplateTable::fast_xaccess(TosState state) { ++ transition(vtos, state); ++ ++ const Register scratch = T8; ++ ++ // get receiver ++ __ ld_d(T1, aaddress(0)); ++ // access constant pool cache ++ __ get_cache_and_index_at_bcp(T3, T2, 2); ++ __ alsl_d(AT, T2, T3, Address::times_8 - 1); ++ __ ld_d(T2, AT, in_bytes(ConstantPoolCache::base_offset() + ConstantPoolCacheEntry::f2_offset())); ++ ++ { ++ __ ld_d(AT, AT, in_bytes(ConstantPoolCache::base_offset() + ConstantPoolCacheEntry::flags_offset())); ++ __ li(scratch, 1 << ConstantPoolCacheEntry::is_volatile_shift); ++ __ andr(scratch, scratch, AT); ++ ++ Label notVolatile; ++ __ beq(scratch, R0, notVolatile); ++ volatile_barrier(); ++ __ bind(notVolatile); ++ } ++ ++ // make sure exception is reported in correct bcp range (getfield is ++ // next instruction) ++ __ addi_d(BCP, BCP, 1); ++ __ null_check(T1); ++ __ add_d(T1, T1, T2); ++ ++ if (state == itos) { ++ __ ld_w(FSR, T1, 0); ++ } else if (state == atos) { ++ __ load_heap_oop(FSR, Address(T1, 0)); ++ __ verify_oop(FSR); ++ } else if (state == ftos) { ++ __ fld_s(FSF, T1, 0); ++ } else { ++ ShouldNotReachHere(); ++ } ++ __ addi_d(BCP, BCP, -1); ++ ++ { ++ Label notVolatile; ++ __ beq(scratch, R0, notVolatile); ++ volatile_barrier(); ++ __ bind(notVolatile); ++ } ++} ++ ++ ++ ++//----------------------------------------------------------------------------- ++// Calls ++ ++void TemplateTable::count_calls(Register method, Register temp) { ++ // implemented elsewhere ++ ShouldNotReachHere(); ++} ++ ++// method, index, recv, flags: T1, T2, T3, T1 ++// byte_no = 2 for _invokevirtual, 1 else ++// T0 : return address ++// get the method & index of the invoke, and push the return address of ++// the invoke(first word in the frame) ++// this address is where the return code jmp to. ++// NOTE : this method will set T3&T1 as recv&flags ++void TemplateTable::prepare_invoke(int byte_no, ++ Register method, // linked method (or i-klass) ++ Register index, // itable index, MethodType, etc. ++ Register recv, // if caller wants to see it ++ Register flags // if caller wants to test it ++ ) { ++ ++ ++ // determine flags ++ const Bytecodes::Code code = bytecode(); ++ const bool is_invokeinterface = code == Bytecodes::_invokeinterface; ++ const bool is_invokedynamic = code == Bytecodes::_invokedynamic; ++ const bool is_invokehandle = code == Bytecodes::_invokehandle; ++ const bool is_invokevirtual = code == Bytecodes::_invokevirtual; ++ const bool is_invokespecial = code == Bytecodes::_invokespecial; ++ const bool load_receiver = (recv != noreg); ++ const bool save_flags = (flags != noreg); ++ assert(load_receiver == (code != Bytecodes::_invokestatic && code != Bytecodes::_invokedynamic),""); ++ assert(save_flags == (is_invokeinterface || is_invokevirtual), "need flags for vfinal"); ++ assert(flags == noreg || flags == T1, "error flags reg."); ++ assert(recv == noreg || recv == T3, "error recv reg."); ++ ++ // setup registers & access constant pool cache ++ if(recv == noreg) recv = T3; ++ if(flags == noreg) flags = T1; ++ assert_different_registers(method, index, recv, flags); ++ ++ // save 'interpreter return address' ++ __ save_bcp(); ++ ++ load_invoke_cp_cache_entry(byte_no, method, index, flags, is_invokevirtual, false, is_invokedynamic); ++ ++ if (is_invokedynamic || is_invokehandle) { ++ Label L_no_push; ++ __ li(AT, (1 << ConstantPoolCacheEntry::has_appendix_shift)); ++ __ andr(AT, AT, flags); ++ __ beq(AT, R0, L_no_push); ++ // Push the appendix as a trailing parameter. ++ // This must be done before we get the receiver, ++ // since the parameter_size includes it. ++ Register tmp = SSR; ++ __ push(tmp); ++ __ move(tmp, index); ++ assert(ConstantPoolCacheEntry::_indy_resolved_references_appendix_offset == 0, "appendix expected at index+0"); ++ __ load_resolved_reference_at_index(index, tmp); ++ __ pop(tmp); ++ __ push(index); // push appendix (MethodType, CallSite, etc.) ++ __ bind(L_no_push); ++ } ++ ++ // load receiver if needed (after appendix is pushed so parameter size is correct) ++ // Note: no return address pushed yet ++ if (load_receiver) { ++ __ li(AT, ConstantPoolCacheEntry::parameter_size_mask); ++ __ andr(recv, flags, AT); ++ // Since we won't push RA on stack, no_return_pc_pushed_yet should be 0. ++ const int no_return_pc_pushed_yet = 0; // argument slot correction before we push return address ++ const int receiver_is_at_end = -1; // back off one slot to get receiver ++ Address recv_addr = __ argument_address(recv, no_return_pc_pushed_yet + receiver_is_at_end); ++ __ ld_d(recv, recv_addr); ++ __ verify_oop(recv); ++ } ++ if(save_flags) { ++ __ move(BCP, flags); ++ } ++ ++ // compute return type ++ __ srli_d(flags, flags, ConstantPoolCacheEntry::tos_state_shift); ++ __ andi(flags, flags, 0xf); ++ ++ // Make sure we don't need to mask flags for tos_state_shift after the above shift ++ ConstantPoolCacheEntry::verify_tos_state_shift(); ++ // load return address ++ { ++ const address table = (address) Interpreter::invoke_return_entry_table_for(code); ++ __ li(AT, (long)table); ++ __ slli_d(flags, flags, LogBytesPerWord); ++ __ add_d(AT, AT, flags); ++ __ ld_d(RA, AT, 0); ++ } ++ ++ if (save_flags) { ++ __ move(flags, BCP); ++ __ restore_bcp(); ++ } ++} ++ ++// used registers : T0, T3, T1, T2 ++// T3 : recv, this two register using convention is by prepare_invoke ++// T1 : flags, klass ++// Rmethod : method, index must be Rmethod ++void TemplateTable::invokevirtual_helper(Register index, ++ Register recv, ++ Register flags) { ++ ++ assert_different_registers(index, recv, flags, T2); ++ ++ // Test for an invoke of a final method ++ Label notFinal; ++ __ li(AT, (1 << ConstantPoolCacheEntry::is_vfinal_shift)); ++ __ andr(AT, flags, AT); ++ __ beq(AT, R0, notFinal); ++ ++ Register method = index; // method must be Rmethod ++ assert(method == Rmethod, "methodOop must be Rmethod for interpreter calling convention"); ++ ++ // do the call - the index is actually the method to call ++ // the index is indeed methodOop, for this is vfinal, ++ // see ConstantPoolCacheEntry::set_method for more info ++ ++ __ verify_oop(method); ++ ++ // It's final, need a null check here! ++ __ null_check(recv); ++ ++ // profile this call ++ __ profile_final_call(T2); ++ ++ // T2: tmp, used for mdp ++ // method: callee ++ // T4: tmp ++ // is_virtual: true ++ __ profile_arguments_type(T2, method, T4, true); ++ ++ __ jump_from_interpreted(method, T2); ++ ++ __ bind(notFinal); ++ ++ // get receiver klass ++ __ null_check(recv, oopDesc::klass_offset_in_bytes()); ++ __ load_klass(T2, recv); ++ __ verify_oop(T2); ++ ++ // profile this call ++ __ profile_virtual_call(T2, T0, T1); ++ ++ // get target methodOop & entry point ++ const int base = InstanceKlass::vtable_start_offset() * wordSize; ++ assert(vtableEntry::size() * wordSize == wordSize, "adjust the scaling in the code below"); ++ // T2: receiver ++ __ alsl_d(AT, index, T2, Address::times_ptr - 1); ++ //this is a ualign read ++ __ ld_d(method, AT, base + vtableEntry::method_offset_in_bytes()); ++ __ profile_arguments_type(T2, method, T4, true); ++ __ jump_from_interpreted(method, T2); ++} ++ ++void TemplateTable::invokevirtual(int byte_no) { ++ transition(vtos, vtos); ++ assert(byte_no == f2_byte, "use this argument"); ++ prepare_invoke(byte_no, Rmethod, NOREG, T3, T1); ++ // now recv & flags in T3, T1 ++ invokevirtual_helper(Rmethod, T3, T1); ++} ++ ++// T4 : entry ++// Rmethod : method ++void TemplateTable::invokespecial(int byte_no) { ++ transition(vtos, vtos); ++ assert(byte_no == f1_byte, "use this argument"); ++ prepare_invoke(byte_no, Rmethod, NOREG, T3); ++ // now recv & flags in T3, T1 ++ __ verify_oop(T3); ++ __ null_check(T3); ++ __ profile_call(T4); ++ ++ // T8: tmp, used for mdp ++ // Rmethod: callee ++ // T4: tmp ++ // is_virtual: false ++ __ profile_arguments_type(T8, Rmethod, T4, false); ++ ++ __ jump_from_interpreted(Rmethod, T4); ++ __ move(T0, T3); ++} ++ ++void TemplateTable::invokestatic(int byte_no) { ++ transition(vtos, vtos); ++ assert(byte_no == f1_byte, "use this argument"); ++ prepare_invoke(byte_no, Rmethod, NOREG); ++ __ verify_oop(Rmethod); ++ ++ __ profile_call(T4); ++ ++ // T8: tmp, used for mdp ++ // Rmethod: callee ++ // T4: tmp ++ // is_virtual: false ++ __ profile_arguments_type(T8, Rmethod, T4, false); ++ ++ __ jump_from_interpreted(Rmethod, T4); ++} ++ ++// i have no idea what to do here, now. for future change. FIXME. ++void TemplateTable::fast_invokevfinal(int byte_no) { ++ transition(vtos, vtos); ++ assert(byte_no == f2_byte, "use this argument"); ++ __ stop("fast_invokevfinal not used on LoongArch64"); ++} ++ ++// used registers : T0, T1, T2, T3, T1, A7 ++// T0 : itable, vtable, entry ++// T1 : interface ++// T3 : receiver ++// T1 : flags, klass ++// Rmethod : index, method, this is required by interpreter_entry ++void TemplateTable::invokeinterface(int byte_no) { ++ transition(vtos, vtos); ++ //this method will use T1-T4 and T0 ++ assert(byte_no == f1_byte, "use this argument"); ++ prepare_invoke(byte_no, T2, Rmethod, T3, T1); ++ // T2: reference klass ++ // Rmethod: method ++ // T3: receiver ++ // T1: flags ++ ++ // Special case of invokeinterface called for virtual method of ++ // java.lang.Object. See cpCacheOop.cpp for details. ++ // This code isn't produced by javac, but could be produced by ++ // another compliant java compiler. ++ Label notMethod; ++ __ li(AT, (1 << ConstantPoolCacheEntry::is_forced_virtual_shift)); ++ __ andr(AT, T1, AT); ++ __ beq(AT, R0, notMethod); ++ ++ invokevirtual_helper(Rmethod, T3, T1); ++ __ bind(notMethod); ++ // Get receiver klass into T1 - also a null check ++ //add for compressedoops ++ __ load_klass(T1, T3); ++ __ verify_oop(T1); ++ ++ Label no_such_interface, no_such_method; ++ ++ // Receiver subtype check against REFC. ++ // Superklass in T2. Subklass in T1. ++ __ lookup_interface_method(// inputs: rec. class, interface, itable index ++ T1, T2, noreg, ++ // outputs: scan temp. reg, scan temp. reg ++ T0, FSR, ++ no_such_interface, ++ /*return_method=*/false); ++ ++ // profile this call ++ __ profile_virtual_call(T1, T0, FSR); ++ ++ // Get declaring interface class from method, and itable index ++ __ ld_ptr(T2, Rmethod, in_bytes(Method::const_offset())); ++ __ ld_ptr(T2, T2, in_bytes(ConstMethod::constants_offset())); ++ __ ld_ptr(T2, T2, ConstantPool::pool_holder_offset_in_bytes()); ++ __ ld_w(Rmethod, Rmethod, in_bytes(Method::itable_index_offset())); ++ __ addi_d(Rmethod, Rmethod, (-1) * Method::itable_index_max); ++ __ sub_w(Rmethod, R0, Rmethod); ++ ++ __ lookup_interface_method(// inputs: rec. class, interface, itable index ++ T1, T2, Rmethod, ++ // outputs: method, scan temp. reg ++ Rmethod, T0, ++ no_such_interface); ++ ++ // Rmethod: Method* to call ++ // T3: receiver ++ // Check for abstract method error ++ // Note: This should be done more efficiently via a throw_abstract_method_error ++ // interpreter entry point and a conditional jump to it in case of a null ++ // method. ++ __ beq(Rmethod, R0, no_such_method); ++ ++ __ profile_arguments_type(T1, Rmethod, T0, true); ++ ++ // do the call ++ // T3: receiver ++ // Rmethod: Method* ++ __ jump_from_interpreted(Rmethod, T1); ++ __ should_not_reach_here(); ++ ++ // exception handling code follows... ++ // note: must restore interpreter registers to canonical ++ // state for exception handling to work correctly! ++ ++ __ bind(no_such_method); ++ // throw exception ++ __ call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::throw_AbstractMethodError)); ++ // the call_VM checks for exception, so we should never return here. ++ __ should_not_reach_here(); ++ ++ __ bind(no_such_interface); ++ // throw exception ++ __ call_VM(noreg, CAST_FROM_FN_PTR(address, ++ InterpreterRuntime::throw_IncompatibleClassChangeError)); ++ // the call_VM checks for exception, so we should never return here. ++ __ should_not_reach_here(); ++} ++ ++ ++void TemplateTable::invokehandle(int byte_no) { ++ transition(vtos, vtos); ++ assert(byte_no == f1_byte, "use this argument"); ++ const Register T2_method = Rmethod; ++ const Register FSR_mtype = FSR; ++ const Register T3_recv = T3; ++ ++ if (!EnableInvokeDynamic) { ++ // rewriter does not generate this bytecode ++ __ should_not_reach_here(); ++ return; ++ } ++ ++ prepare_invoke(byte_no, T2_method, FSR_mtype, T3_recv); ++ //??__ verify_method_ptr(T2_method); ++ __ verify_oop(T3_recv); ++ __ null_check(T3_recv); ++ ++ // T4: MethodType object (from cpool->resolved_references[f1], if necessary) ++ // T2_method: MH.invokeExact_MT method (from f2) ++ ++ // Note: T4 is already pushed (if necessary) by prepare_invoke ++ ++ // FIXME: profile the LambdaForm also ++ __ profile_final_call(T4); ++ ++ // T8: tmp, used for mdp ++ // T2_method: callee ++ // T4: tmp ++ // is_virtual: true ++ __ profile_arguments_type(T8, T2_method, T4, true); ++ ++ __ jump_from_interpreted(T2_method, T4); ++} ++ ++ void TemplateTable::invokedynamic(int byte_no) { ++ transition(vtos, vtos); ++ assert(byte_no == f1_byte, "use this argument"); ++ ++ if (!EnableInvokeDynamic) { ++ // We should not encounter this bytecode if !EnableInvokeDynamic. ++ // The verifier will stop it. However, if we get past the verifier, ++ // this will stop the thread in a reasonable way, without crashing the JVM. ++ __ call_VM(noreg, CAST_FROM_FN_PTR(address, ++ InterpreterRuntime::throw_IncompatibleClassChangeError)); ++ // the call_VM checks for exception, so we should never return here. ++ __ should_not_reach_here(); ++ return; ++ } ++ ++ const Register T2_callsite = T2; ++ ++ prepare_invoke(byte_no, Rmethod, T2_callsite); ++ ++ // T2: CallSite object (from cpool->resolved_references[f1]) ++ // Rmethod: MH.linkToCallSite method (from f2) ++ ++ // Note: T2_callsite is already pushed by prepare_invoke ++ // %%% should make a type profile for any invokedynamic that takes a ref argument ++ // profile this call ++ __ profile_call(T4); ++ ++ // T8: tmp, used for mdp ++ // Rmethod: callee ++ // T4: tmp ++ // is_virtual: false ++ __ profile_arguments_type(T8, Rmethod, T4, false); ++ ++ __ verify_oop(T2_callsite); ++ ++ __ jump_from_interpreted(Rmethod, T4); ++ } ++ ++//----------------------------------------------------------------------------- ++// Allocation ++// T1 : tags & buffer end & thread ++// T2 : object end ++// T3 : klass ++// T1 : object size ++// A1 : cpool ++// A2 : cp index ++// return object in FSR ++void TemplateTable::_new() { ++ transition(vtos, atos); ++ __ get_unsigned_2_byte_index_at_bcp(A2, 1); ++ ++ Label slow_case; ++ Label done; ++ Label initialize_header; ++ Label initialize_object; // including clearing the fields ++ Label allocate_shared; ++ ++ // get InstanceKlass in T3 ++ __ get_cpool_and_tags(A1, T1); ++ ++ __ alsl_d(AT, A2, A1, Address::times_8 - 1); ++ __ ld_d(T3, AT, sizeof(ConstantPool)); ++ ++ // make sure the class we're about to instantiate has been resolved. ++ // Note: slow_case does a pop of stack, which is why we loaded class/pushed above ++ const int tags_offset = Array::base_offset_in_bytes(); ++ __ add_d(T1, T1, A2); ++ __ ld_b(AT, T1, tags_offset); ++ if(os::is_MP()) { ++ __ membar(Assembler::Membar_mask_bits(__ LoadLoad|__ LoadStore)); ++ } ++ __ addi_d(AT, AT, -(int)JVM_CONSTANT_Class); ++ __ bne(AT, R0, slow_case); ++ ++ // make sure klass is initialized & doesn't have finalizer ++ // make sure klass is fully initialized ++ __ ld_hu(T1, T3, in_bytes(InstanceKlass::init_state_offset())); ++ __ addi_d(AT, T1, - (int)InstanceKlass::fully_initialized); ++ __ bne(AT, R0, slow_case); ++ ++ // has_finalizer ++ __ ld_w(T0, T3, in_bytes(Klass::layout_helper_offset()) ); ++ __ andi(AT, T0, Klass::_lh_instance_slow_path_bit); ++ __ bne(AT, R0, slow_case); ++ ++ // Allocate the instance ++ // 1) Try to allocate in the TLAB ++ // 2) if fail and the object is large allocate in the shared Eden ++ // 3) if the above fails (or is not applicable), go to a slow case ++ // (creates a new TLAB, etc.) ++ ++ const bool allow_shared_alloc = ++ Universe::heap()->supports_inline_contig_alloc() && !CMSIncrementalMode; ++ ++#ifndef OPT_THREAD ++ const Register thread = T8; ++ if (UseTLAB || allow_shared_alloc) { ++ __ get_thread(thread); ++ } ++#else ++ const Register thread = TREG; ++#endif ++ ++ if (UseTLAB) { ++ // get tlab_top ++ __ ld_d(FSR, thread, in_bytes(JavaThread::tlab_top_offset())); ++ // get tlab_end ++ __ ld_d(AT, thread, in_bytes(JavaThread::tlab_end_offset())); ++ __ add_d(T2, FSR, T0); ++ __ blt(AT, T2, allow_shared_alloc ? allocate_shared : slow_case); ++ __ st_d(T2, thread, in_bytes(JavaThread::tlab_top_offset())); ++ ++ if (ZeroTLAB) { ++ // the fields have been already cleared ++ __ beq(R0, R0, initialize_header); ++ } else { ++ // initialize both the header and fields ++ __ beq(R0, R0, initialize_object); ++ } ++ } ++ ++ // Allocation in the shared Eden , if allowed ++ // T0 : instance size in words ++ if(allow_shared_alloc){ ++ __ bind(allocate_shared); ++ ++ Label done, retry; ++ Address heap_top(T1); ++ __ li(T1, (long)Universe::heap()->top_addr()); ++ __ ld_d(FSR, heap_top); ++ ++ __ bind(retry); ++ __ li(AT, (long)Universe::heap()->end_addr()); ++ __ ld_d(AT, AT, 0); ++ __ add_d(T2, FSR, T0); ++ __ blt(AT, T2, slow_case); ++ ++ // Compare FSR with the top addr, and if still equal, store the new ++ // top addr in T2 at the address of the top addr pointer. Sets AT if was ++ // equal, and clears it otherwise. Use lock prefix for atomicity on MPs. ++ // ++ // FSR: object begin ++ // T2: object end ++ // T0: instance size in words ++ ++ // if someone beat us on the allocation, try again, otherwise continue ++ __ cmpxchg(heap_top, FSR, T2, AT, true, true, done, &retry); ++ ++ __ bind(done); ++ __ incr_allocated_bytes(thread, T0, 0); ++ } ++ ++ if (UseTLAB || Universe::heap()->supports_inline_contig_alloc()) { ++ // The object is initialized before the header. If the object size is ++ // zero, go directly to the header initialization. ++ __ bind(initialize_object); ++ __ li(AT, - sizeof(oopDesc)); ++ __ add_d(T0, T0, AT); ++ __ beq(T0, R0, initialize_header); ++ ++ // initialize remaining object fields: T0 is a multiple of 2 ++ { ++ Label loop; ++ __ add_d(T1, FSR, T0); ++ __ addi_d(T1, T1, -oopSize); ++ ++ __ bind(loop); ++ __ st_d(R0, T1, sizeof(oopDesc) + 0 * oopSize); ++ Label L1; ++ __ beq(T1, FSR, L1); //dont clear header ++ __ addi_d(T1, T1, -oopSize); ++ __ b(loop); ++ __ bind(L1); ++ __ addi_d(T1, T1, -oopSize); ++ } ++ ++ // klass in T3, ++ // initialize object header only. ++ __ bind(initialize_header); ++ if (UseBiasedLocking) { ++ __ ld_d(AT, T3, in_bytes(Klass::prototype_header_offset())); ++ __ st_d(AT, FSR, oopDesc::mark_offset_in_bytes ()); ++ } else { ++ __ li(AT, (long)markOopDesc::prototype()); ++ __ st_d(AT, FSR, oopDesc::mark_offset_in_bytes()); ++ } ++ ++ __ store_klass_gap(FSR, R0); ++ __ store_klass(FSR, T3); ++ ++ { ++ SkipIfEqual skip_if(_masm, &DTraceAllocProbes, 0); ++ // Trigger dtrace event for fastpath ++ __ push(atos); ++ __ call_VM_leaf( ++ CAST_FROM_FN_PTR(address, SharedRuntime::dtrace_object_alloc), FSR); ++ __ pop(atos); ++ ++ } ++ __ b(done); ++ } ++ ++ // slow case ++ __ bind(slow_case); ++ call_VM(FSR, CAST_FROM_FN_PTR(address, InterpreterRuntime::_new), A1, A2); ++ ++ // continue ++ __ bind(done); ++ __ membar(__ StoreStore); ++} ++ ++void TemplateTable::newarray() { ++ transition(itos, atos); ++ __ ld_bu(A1, at_bcp(1)); ++ // type, count ++ call_VM(FSR, CAST_FROM_FN_PTR(address, InterpreterRuntime::newarray), A1, FSR); ++ __ membar(__ StoreStore); ++} ++ ++void TemplateTable::anewarray() { ++ transition(itos, atos); ++ __ get_2_byte_integer_at_bcp(A2, AT, 1); ++ __ huswap(A2); ++ __ get_constant_pool(A1); ++ // cp, index, count ++ call_VM(FSR, CAST_FROM_FN_PTR(address, InterpreterRuntime::anewarray), A1, A2, FSR); ++ __ membar(__ StoreStore); ++} ++ ++void TemplateTable::arraylength() { ++ transition(atos, itos); ++ __ null_check(FSR, arrayOopDesc::length_offset_in_bytes()); ++ __ ld_w(FSR, FSR, arrayOopDesc::length_offset_in_bytes()); ++} ++ ++// when invoke gen_subtype_check, super in T3, sub in T2, object in FSR(it's always) ++// T2 : sub klass ++// T3 : cpool ++// T3 : super klass ++void TemplateTable::checkcast() { ++ transition(atos, atos); ++ Label done, is_null, ok_is_subtype, quicked, resolved; ++ __ beq(FSR, R0, is_null); ++ ++ // Get cpool & tags index ++ __ get_cpool_and_tags(T3, T1); ++ __ get_2_byte_integer_at_bcp(T2, AT, 1); ++ __ huswap(T2); ++ ++ // See if bytecode has already been quicked ++ __ add_d(AT, T1, T2); ++ __ ld_b(AT, AT, Array::base_offset_in_bytes()); ++ if(os::is_MP()) { ++ __ membar(Assembler::Membar_mask_bits(__ LoadLoad|__ LoadStore)); ++ } ++ __ addi_d(AT, AT, - (int)JVM_CONSTANT_Class); ++ __ beq(AT, R0, quicked); ++ ++ // In InterpreterRuntime::quicken_io_cc, lots of new classes may be loaded. ++ // Then, GC will move the object in V0 to another places in heap. ++ // Therefore, We should never save such an object in register. ++ // Instead, we should save it in the stack. It can be modified automatically by the GC thread. ++ // After GC, the object address in FSR is changed to a new place. ++ // ++ __ push(atos); ++ const Register thread = TREG; ++#ifndef OPT_THREAD ++ __ get_thread(thread); ++#endif ++ call_VM(NOREG, CAST_FROM_FN_PTR(address, InterpreterRuntime::quicken_io_cc)); ++ __ get_vm_result_2(T3, thread); ++ __ pop_ptr(FSR); ++ __ b(resolved); ++ ++ // klass already in cp, get superklass in T3 ++ __ bind(quicked); ++ __ alsl_d(AT, T2, T3, Address::times_8 - 1); ++ __ ld_d(T3, AT, sizeof(ConstantPool)); ++ ++ __ bind(resolved); ++ ++ // get subklass in T2 ++ __ load_klass(T2, FSR); ++ // Superklass in T3. Subklass in T2. ++ __ gen_subtype_check(T3, T2, ok_is_subtype); ++ ++ // Come here on failure ++ // object is at FSR ++ __ jmp(Interpreter::_throw_ClassCastException_entry); ++ ++ // Come here on success ++ __ bind(ok_is_subtype); ++ ++ // Collect counts on whether this check-cast sees NULLs a lot or not. ++ if (ProfileInterpreter) { ++ __ b(done); ++ __ bind(is_null); ++ __ profile_null_seen(T3); ++ } else { ++ __ bind(is_null); ++ } ++ __ bind(done); ++} ++ ++// T3 as cpool, T1 as tags, T2 as index ++// object always in FSR, superklass in T3, subklass in T2 ++void TemplateTable::instanceof() { ++ transition(atos, itos); ++ Label done, is_null, ok_is_subtype, quicked, resolved; ++ ++ __ beq(FSR, R0, is_null); ++ ++ // Get cpool & tags index ++ __ get_cpool_and_tags(T3, T1); ++ // get index ++ __ get_2_byte_integer_at_bcp(T2, AT, 1); ++ __ hswap(T2); ++ ++ // See if bytecode has already been quicked ++ // quicked ++ __ add_d(AT, T1, T2); ++ __ ld_b(AT, AT, Array::base_offset_in_bytes()); ++ if(os::is_MP()) { ++ __ membar(Assembler::Membar_mask_bits(__ LoadLoad|__ LoadStore)); ++ } ++ __ addi_d(AT, AT, -(int)JVM_CONSTANT_Class); ++ __ beq(AT, R0, quicked); ++ ++ __ push(atos); ++ const Register thread = TREG; ++#ifndef OPT_THREAD ++ __ get_thread(thread); ++#endif ++ call_VM(NOREG, CAST_FROM_FN_PTR(address, InterpreterRuntime::quicken_io_cc)); ++ __ get_vm_result_2(T3, thread); ++ __ pop_ptr(FSR); ++ __ b(resolved); ++ ++ // get superklass in T3, subklass in T2 ++ __ bind(quicked); ++ __ alsl_d(AT, T2, T3, Address::times_8 - 1); ++ __ ld_d(T3, AT, sizeof(ConstantPool)); ++ ++ __ bind(resolved); ++ // get subklass in T2 ++ __ load_klass(T2, FSR); ++ ++ // Superklass in T3. Subklass in T2. ++ __ gen_subtype_check(T3, T2, ok_is_subtype); ++ // Come here on failure ++ __ move(FSR, R0); ++ __ b(done); ++ ++ // Come here on success ++ __ bind(ok_is_subtype); ++ __ li(FSR, 1); ++ ++ // Collect counts on whether this test sees NULLs a lot or not. ++ if (ProfileInterpreter) { ++ __ beq(R0, R0, done); ++ __ bind(is_null); ++ __ profile_null_seen(T3); ++ } else { ++ __ bind(is_null); // same as 'done' ++ } ++ __ bind(done); ++ // FSR = 0: obj == NULL or obj is not an instanceof the specified klass ++ // FSR = 1: obj != NULL and obj is an instanceof the specified klass ++} ++ ++//-------------------------------------------------------- ++//-------------------------------------------- ++// Breakpoints ++void TemplateTable::_breakpoint() { ++ // Note: We get here even if we are single stepping.. ++ // jbug inists on setting breakpoints at every bytecode ++ // even if we are in single step mode. ++ ++ transition(vtos, vtos); ++ ++ // get the unpatched byte code ++ __ get_method(A1); ++ __ call_VM(NOREG, ++ CAST_FROM_FN_PTR(address, ++ InterpreterRuntime::get_original_bytecode_at), ++ A1, BCP); ++ __ move(Rnext, V0); // Rnext will be used in dispatch_only_normal ++ ++ // post the breakpoint event ++ __ get_method(A1); ++ __ call_VM(NOREG, CAST_FROM_FN_PTR(address, InterpreterRuntime::_breakpoint), A1, BCP); ++ ++ // complete the execution of original bytecode ++ __ dispatch_only_normal(vtos); ++} ++ ++//----------------------------------------------------------------------------- ++// Exceptions ++ ++void TemplateTable::athrow() { ++ transition(atos, vtos); ++ __ null_check(FSR); ++ __ jmp(Interpreter::throw_exception_entry()); ++} ++ ++//----------------------------------------------------------------------------- ++// Synchronization ++// ++// Note: monitorenter & exit are symmetric routines; which is reflected ++// in the assembly code structure as well ++// ++// Stack layout: ++// ++// [expressions ] <--- SP = expression stack top ++// .. ++// [expressions ] ++// [monitor entry] <--- monitor block top = expression stack bot ++// .. ++// [monitor entry] ++// [frame data ] <--- monitor block bot ++// ... ++// [return addr ] <--- FP ++ ++// we use T2 as monitor entry pointer, T3 as monitor top pointer, c_rarg0 as free slot pointer ++// object always in FSR ++void TemplateTable::monitorenter() { ++ transition(atos, vtos); ++ ++ // check for NULL object ++ __ null_check(FSR); ++ ++ const Address monitor_block_top(FP, frame::interpreter_frame_monitor_block_top_offset ++ * wordSize); ++ const int entry_size = (frame::interpreter_frame_monitor_size()* wordSize); ++ Label allocated; ++ ++ // initialize entry pointer ++ __ move(c_rarg0, R0); ++ ++ // find a free slot in the monitor block (result in c_rarg0) ++ { ++ Label entry, loop, exit, next; ++ __ ld_d(T2, monitor_block_top); ++ __ addi_d(T3, FP, frame::interpreter_frame_initial_sp_offset * wordSize); ++ __ b(entry); ++ ++ // free slot? ++ __ bind(loop); ++ __ ld_d(AT, T2, BasicObjectLock::obj_offset_in_bytes()); ++ __ bne(AT, R0, next); ++ __ move(c_rarg0, T2); ++ ++ __ bind(next); ++ __ beq(FSR, AT, exit); ++ __ addi_d(T2, T2, entry_size); ++ ++ __ bind(entry); ++ __ bne(T3, T2, loop); ++ __ bind(exit); ++ } ++ ++ __ bne(c_rarg0, R0, allocated); ++ ++ // allocate one if there's no free slot ++ { ++ Label entry, loop; ++ // 1. compute new pointers // SP: old expression stack top ++ __ ld_d(c_rarg0, monitor_block_top); ++ __ addi_d(SP, SP, -entry_size); ++ __ addi_d(c_rarg0, c_rarg0, -entry_size); ++ __ st_d(c_rarg0, monitor_block_top); ++ __ move(T3, SP); ++ __ b(entry); ++ ++ // 2. move expression stack contents ++ __ bind(loop); ++ __ ld_d(AT, T3, entry_size); ++ __ st_d(AT, T3, 0); ++ __ addi_d(T3, T3, wordSize); ++ __ bind(entry); ++ __ bne(T3, c_rarg0, loop); ++ } ++ ++ __ bind(allocated); ++ // Increment bcp to point to the next bytecode, ++ // so exception handling for async. exceptions work correctly. ++ // The object has already been poped from the stack, so the ++ // expression stack looks correct. ++ __ addi_d(BCP, BCP, 1); ++ __ st_d(FSR, c_rarg0, BasicObjectLock::obj_offset_in_bytes()); ++ __ lock_object(c_rarg0); ++ // check to make sure this monitor doesn't cause stack overflow after locking ++ __ save_bcp(); // in case of exception ++ __ generate_stack_overflow_check(0); ++ // The bcp has already been incremented. Just need to dispatch to next instruction. ++ ++ __ dispatch_next(vtos); ++} ++ ++// T2 : top ++// c_rarg0 : entry ++void TemplateTable::monitorexit() { ++ transition(atos, vtos); ++ ++ __ null_check(FSR); ++ ++ const int entry_size =(frame::interpreter_frame_monitor_size()* wordSize); ++ Label found; ++ ++ // find matching slot ++ { ++ Label entry, loop; ++ __ ld_d(c_rarg0, FP, frame::interpreter_frame_monitor_block_top_offset * wordSize); ++ __ addi_d(T2, FP, frame::interpreter_frame_initial_sp_offset * wordSize); ++ __ b(entry); ++ ++ __ bind(loop); ++ __ ld_d(AT, c_rarg0, BasicObjectLock::obj_offset_in_bytes()); ++ __ beq(FSR, AT, found); ++ __ addi_d(c_rarg0, c_rarg0, entry_size); ++ __ bind(entry); ++ __ bne(T2, c_rarg0, loop); ++ } ++ ++ // error handling. Unlocking was not block-structured ++ Label end; ++ __ call_VM(NOREG, CAST_FROM_FN_PTR(address, ++ InterpreterRuntime::throw_illegal_monitor_state_exception)); ++ __ should_not_reach_here(); ++ ++ // call run-time routine ++ // c_rarg0: points to monitor entry ++ __ bind(found); ++ __ move(TSR, FSR); ++ __ unlock_object(c_rarg0); ++ __ move(FSR, TSR); ++ __ bind(end); ++} ++ ++ ++// Wide instructions ++void TemplateTable::wide() { ++ transition(vtos, vtos); ++ __ ld_bu(Rnext, at_bcp(1)); ++ __ slli_d(T4, Rnext, Address::times_8); ++ __ li(AT, (long)Interpreter::_wentry_point); ++ __ add_d(AT, T4, AT); ++ __ ld_d(T4, AT, 0); ++ __ jr(T4); ++} ++ ++ ++void TemplateTable::multianewarray() { ++ transition(vtos, atos); ++ // last dim is on top of stack; we want address of first one: ++ // first_addr = last_addr + (ndims - 1) * wordSize ++ __ ld_bu(A1, at_bcp(3)); // dimension ++ __ addi_d(A1, A1, -1); ++ __ slli_d(A1, A1, Address::times_8); ++ __ add_d(A1, SP, A1); // now A1 pointer to the count array on the stack ++ call_VM(FSR, CAST_FROM_FN_PTR(address, InterpreterRuntime::multianewarray), A1); ++ __ ld_bu(AT, at_bcp(3)); ++ __ slli_d(AT, AT, Address::times_8); ++ __ add_d(SP, SP, AT); ++ __ membar(__ AnyAny);//no membar here for aarch64 ++} ++#endif // !CC_INTERP +diff --git a/hotspot/src/cpu/loongarch/vm/templateTable_loongarch_64.hpp b/hotspot/src/cpu/loongarch/vm/templateTable_loongarch_64.hpp +new file mode 100644 +index 0000000000..c48d76e0a2 +--- /dev/null ++++ b/hotspot/src/cpu/loongarch/vm/templateTable_loongarch_64.hpp +@@ -0,0 +1,44 @@ ++/* ++ * Copyright (c) 2003, 2012, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2015, 2020, Loongson Technology. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ * ++ */ ++ ++#ifndef CPU_LOONGARCH_VM_TEMPLATETABLE_LOONGARCH_64_HPP ++#define CPU_LOONGARCH_VM_TEMPLATETABLE_LOONGARCH_64_HPP ++ ++ static void prepare_invoke(int byte_no, ++ Register method, ++ Register index = noreg, ++ Register recv = noreg, ++ Register flags = noreg ++ ); ++ static void invokevirtual_helper(Register index, Register recv, ++ Register flags); ++ //static void volatile_barrier(Assembler::Membar_mask_bits order_constraint); ++ static void volatile_barrier(); ++ ++ // Helpers ++ static void index_check(Register array, Register index); ++ static void index_check_without_pop(Register array, Register index); ++ ++#endif // CPU_LOONGARCH_VM_TEMPLATETABLE_LOONGARCH_64_HPP +diff --git a/hotspot/src/cpu/loongarch/vm/vmStructs_loongarch.hpp b/hotspot/src/cpu/loongarch/vm/vmStructs_loongarch.hpp +new file mode 100644 +index 0000000000..7c3ce68010 +--- /dev/null ++++ b/hotspot/src/cpu/loongarch/vm/vmStructs_loongarch.hpp +@@ -0,0 +1,68 @@ ++/* ++ * Copyright (c) 2001, 2013, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2015, 2020, Loongson Technology. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ * ++ */ ++ ++#ifndef CPU_LOONGARCH_VM_VMSTRUCTS_LOONGARCH_HPP ++#define CPU_LOONGARCH_VM_VMSTRUCTS_LOONGARCH_HPP ++ ++// These are the CPU-specific fields, types and integer ++// constants required by the Serviceability Agent. This file is ++// referenced by vmStructs.cpp. ++ ++#define VM_STRUCTS_CPU(nonstatic_field, static_field, unchecked_nonstatic_field, volatile_nonstatic_field, nonproduct_nonstatic_field, c2_nonstatic_field, unchecked_c1_static_field, unchecked_c2_static_field) \ ++ \ ++ /******************************/ \ ++ /* JavaCallWrapper */ \ ++ /******************************/ \ ++ /******************************/ \ ++ /* JavaFrameAnchor */ \ ++ /******************************/ \ ++ volatile_nonstatic_field(JavaFrameAnchor, _last_Java_fp, intptr_t*) \ ++ \ ++ ++ /* NOTE that we do not use the last_entry() macro here; it is used */ ++ /* in vmStructs__.hpp's VM_STRUCTS_OS_CPU macro (and must */ ++ /* be present there) */ ++ ++ ++#define VM_TYPES_CPU(declare_type, declare_toplevel_type, declare_oop_type, declare_integer_type, declare_unsigned_integer_type, declare_c1_toplevel_type, declare_c2_type, declare_c2_toplevel_type) \ ++ ++ /* NOTE that we do not use the last_entry() macro here; it is used */ ++ /* in vmStructs__.hpp's VM_TYPES_OS_CPU macro (and must */ ++ /* be present there) */ ++ ++ ++#define VM_INT_CONSTANTS_CPU(declare_constant, declare_preprocessor_constant, declare_c1_constant, declare_c2_constant, declare_c2_preprocessor_constant) \ ++ ++ /* NOTE that we do not use the last_entry() macro here; it is used */ ++ /* in vmStructs__.hpp's VM_INT_CONSTANTS_OS_CPU macro (and must */ ++ /* be present there) */ ++ ++#define VM_LONG_CONSTANTS_CPU(declare_constant, declare_preprocessor_constant, declare_c1_constant, declare_c2_constant, declare_c2_preprocessor_constant) \ ++ ++ /* NOTE that we do not use the last_entry() macro here; it is used */ ++ /* in vmStructs__.hpp's VM_LONG_CONSTANTS_OS_CPU macro (and must */ ++ /* be present there) */ ++ ++#endif // CPU_LOONGARCH_VM_VMSTRUCTS_LOONGARCH_HPP +diff --git a/hotspot/src/cpu/loongarch/vm/vm_version_ext_loongarch.cpp b/hotspot/src/cpu/loongarch/vm/vm_version_ext_loongarch.cpp +new file mode 100644 +index 0000000000..c71f64e132 +--- /dev/null ++++ b/hotspot/src/cpu/loongarch/vm/vm_version_ext_loongarch.cpp +@@ -0,0 +1,84 @@ ++/* ++ * Copyright (c) 2013, 2018, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2018, 2020, Loongson Technology. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ * ++ */ ++ ++#include "memory/allocation.inline.hpp" ++#include "vm_version_ext_loongarch.hpp" ++ ++// VM_Version_Ext statics ++int VM_Version_Ext::_no_of_threads = 0; ++int VM_Version_Ext::_no_of_cores = 0; ++int VM_Version_Ext::_no_of_sockets = 0; ++bool VM_Version_Ext::_initialized = false; ++char VM_Version_Ext::_cpu_name[CPU_TYPE_DESC_BUF_SIZE] = {0}; ++char VM_Version_Ext::_cpu_desc[CPU_DETAILED_DESC_BUF_SIZE] = {0}; ++ ++void VM_Version_Ext::initialize_cpu_information(void) { ++ // do nothing if cpu info has been initialized ++ if (_initialized) { ++ return; ++ } ++ ++ _no_of_cores = os::processor_count(); ++ _no_of_threads = _no_of_cores; ++ _no_of_sockets = _no_of_cores; ++ snprintf(_cpu_name, CPU_TYPE_DESC_BUF_SIZE - 1, "LoongArch"); ++ snprintf(_cpu_desc, CPU_DETAILED_DESC_BUF_SIZE, "LoongArch %s", cpu_features()); ++ _initialized = true; ++} ++ ++int VM_Version_Ext::number_of_threads(void) { ++ initialize_cpu_information(); ++ return _no_of_threads; ++} ++ ++int VM_Version_Ext::number_of_cores(void) { ++ initialize_cpu_information(); ++ return _no_of_cores; ++} ++ ++int VM_Version_Ext::number_of_sockets(void) { ++ initialize_cpu_information(); ++ return _no_of_sockets; ++} ++ ++const char* VM_Version_Ext::cpu_name(void) { ++ initialize_cpu_information(); ++ char* tmp = NEW_C_HEAP_ARRAY_RETURN_NULL(char, CPU_TYPE_DESC_BUF_SIZE, mtTracing); ++ if (NULL == tmp) { ++ return NULL; ++ } ++ strncpy(tmp, _cpu_name, CPU_TYPE_DESC_BUF_SIZE); ++ return tmp; ++} ++ ++const char* VM_Version_Ext::cpu_description(void) { ++ initialize_cpu_information(); ++ char* tmp = NEW_C_HEAP_ARRAY_RETURN_NULL(char, CPU_DETAILED_DESC_BUF_SIZE, mtTracing); ++ if (NULL == tmp) { ++ return NULL; ++ } ++ strncpy(tmp, _cpu_desc, CPU_DETAILED_DESC_BUF_SIZE); ++ return tmp; ++} +diff --git a/hotspot/src/cpu/loongarch/vm/vm_version_ext_loongarch.hpp b/hotspot/src/cpu/loongarch/vm/vm_version_ext_loongarch.hpp +new file mode 100644 +index 0000000000..682dd9c78f +--- /dev/null ++++ b/hotspot/src/cpu/loongarch/vm/vm_version_ext_loongarch.hpp +@@ -0,0 +1,54 @@ ++/* ++ * Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2018, 2020, Loongson Technology. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ * ++ */ ++ ++#ifndef CPU_LOONGARCH_VM_VM_VERSION_EXT_LOONGARCH_HPP ++#define CPU_LOONGARCH_VM_VM_VERSION_EXT_LOONGARCH_HPP ++ ++#include "runtime/vm_version.hpp" ++#include "utilities/macros.hpp" ++ ++class VM_Version_Ext : public VM_Version { ++ private: ++ static const size_t CPU_TYPE_DESC_BUF_SIZE = 256; ++ static const size_t CPU_DETAILED_DESC_BUF_SIZE = 4096; ++ ++ static int _no_of_threads; ++ static int _no_of_cores; ++ static int _no_of_sockets; ++ static bool _initialized; ++ static char _cpu_name[CPU_TYPE_DESC_BUF_SIZE]; ++ static char _cpu_desc[CPU_DETAILED_DESC_BUF_SIZE]; ++ ++ public: ++ static int number_of_threads(void); ++ static int number_of_cores(void); ++ static int number_of_sockets(void); ++ ++ static const char* cpu_name(void); ++ static const char* cpu_description(void); ++ static void initialize_cpu_information(void); ++}; ++ ++#endif // CPU_LOONGARCH_VM_VM_VERSION_EXT_LOONGARCH_HPP +diff --git a/hotspot/src/cpu/loongarch/vm/vm_version_loongarch.cpp b/hotspot/src/cpu/loongarch/vm/vm_version_loongarch.cpp +new file mode 100644 +index 0000000000..19e090e4de +--- /dev/null ++++ b/hotspot/src/cpu/loongarch/vm/vm_version_loongarch.cpp +@@ -0,0 +1,434 @@ ++/* ++ * Copyright (c) 1997, 2014, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2015, 2020, Loongson Technology. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ * ++ */ ++ ++#include "precompiled.hpp" ++#include "asm/macroAssembler.hpp" ++#include "asm/macroAssembler.inline.hpp" ++#include "memory/resourceArea.hpp" ++#include "runtime/java.hpp" ++#include "runtime/stubCodeGenerator.hpp" ++#include "vm_version_loongarch.hpp" ++#ifdef TARGET_OS_FAMILY_linux ++# include "os_linux.inline.hpp" ++#endif ++ ++#define A0 RA0 ++#define A1 RA1 ++#define A2 RA2 ++#define A3 RA3 ++#define A4 RA4 ++#define A5 RA5 ++#define A6 RA6 ++#define A7 RA7 ++#define T0 RT0 ++#define T1 RT1 ++#define T2 RT2 ++#define T3 RT3 ++#define T4 RT4 ++#define T5 RT5 ++#define T6 RT6 ++#define T7 RT7 ++#define T8 RT8 ++ ++int VM_Version::_cpuFeatures; ++const char* VM_Version::_features_str = ""; ++VM_Version::CpuidInfo VM_Version::_cpuid_info = { 0, }; ++bool VM_Version::_cpu_info_is_initialized = false; ++ ++static BufferBlob* stub_blob; ++static const int stub_size = 600; ++ ++extern "C" { ++ typedef void (*get_cpu_info_stub_t)(void*); ++} ++static get_cpu_info_stub_t get_cpu_info_stub = NULL; ++ ++ ++class VM_Version_StubGenerator: public StubCodeGenerator { ++ public: ++ ++ VM_Version_StubGenerator(CodeBuffer *c) : StubCodeGenerator(c) {} ++ ++ address generate_get_cpu_info() { ++ assert(!VM_Version::cpu_info_is_initialized(), "VM_Version should not be initialized"); ++ StubCodeMark mark(this, "VM_Version", "get_cpu_info_stub"); ++# define __ _masm-> ++ ++ address start = __ pc(); ++ ++ __ enter(); ++ __ push(AT); ++ __ push(T5); ++ ++ __ li(AT, (long)0); ++ __ cpucfg(T5, AT); ++ __ st_w(T5, A0, in_bytes(VM_Version::Loongson_Cpucfg_id0_offset())); ++ ++ __ li(AT, 1); ++ __ cpucfg(T5, AT); ++ __ st_w(T5, A0, in_bytes(VM_Version::Loongson_Cpucfg_id1_offset())); ++ ++ __ li(AT, 2); ++ __ cpucfg(T5, AT); ++ __ st_w(T5, A0, in_bytes(VM_Version::Loongson_Cpucfg_id2_offset())); ++ ++ __ li(AT, 3); ++ __ cpucfg(T5, AT); ++ __ st_w(T5, A0, in_bytes(VM_Version::Loongson_Cpucfg_id3_offset())); ++ ++ __ li(AT, 4); ++ __ cpucfg(T5, AT); ++ __ st_w(T5, A0, in_bytes(VM_Version::Loongson_Cpucfg_id4_offset())); ++ ++ __ li(AT, 5); ++ __ cpucfg(T5, AT); ++ __ st_w(T5, A0, in_bytes(VM_Version::Loongson_Cpucfg_id5_offset())); ++ ++ __ li(AT, 6); ++ __ cpucfg(T5, AT); ++ __ st_w(T5, A0, in_bytes(VM_Version::Loongson_Cpucfg_id6_offset())); ++ ++ __ li(AT, 10); ++ __ cpucfg(T5, AT); ++ __ st_w(T5, A0, in_bytes(VM_Version::Loongson_Cpucfg_id10_offset())); ++ ++ __ li(AT, 11); ++ __ cpucfg(T5, AT); ++ __ st_w(T5, A0, in_bytes(VM_Version::Loongson_Cpucfg_id11_offset())); ++ ++ __ li(AT, 12); ++ __ cpucfg(T5, AT); ++ __ st_w(T5, A0, in_bytes(VM_Version::Loongson_Cpucfg_id12_offset())); ++ ++ __ li(AT, 13); ++ __ cpucfg(T5, AT); ++ __ st_w(T5, A0, in_bytes(VM_Version::Loongson_Cpucfg_id13_offset())); ++ ++ __ li(AT, 14); ++ __ cpucfg(T5, AT); ++ __ st_w(T5, A0, in_bytes(VM_Version::Loongson_Cpucfg_id14_offset())); ++ ++ __ pop(T5); ++ __ pop(AT); ++ __ leave(); ++ __ jr(RA); ++# undef __ ++ return start; ++ }; ++}; ++ ++uint32_t VM_Version::get_feature_flags_by_cpucfg() { ++ uint32_t result = 0; ++ if (_cpuid_info.cpucfg_info_id1.bits.ARCH == 0b00 || _cpuid_info.cpucfg_info_id1.bits.ARCH == 0b01 ) { ++ result |= CPU_LA32; ++ } else if (_cpuid_info.cpucfg_info_id1.bits.ARCH == 0b10 ) { ++ result |= CPU_LA64; ++ } ++ if (_cpuid_info.cpucfg_info_id1.bits.UAL != 0) ++ result |= CPU_UAL; ++ ++ if (_cpuid_info.cpucfg_info_id2.bits.FP_CFG != 0) ++ result |= CPU_FP; ++ if (_cpuid_info.cpucfg_info_id2.bits.LSX != 0) ++ result |= CPU_LSX; ++ if (_cpuid_info.cpucfg_info_id2.bits.LASX != 0) ++ result |= CPU_LASX; ++ if (_cpuid_info.cpucfg_info_id2.bits.COMPLEX != 0) ++ result |= CPU_COMPLEX; ++ if (_cpuid_info.cpucfg_info_id2.bits.CRYPTO != 0) ++ result |= CPU_CRYPTO; ++ if (_cpuid_info.cpucfg_info_id2.bits.LBT_X86 != 0) ++ result |= CPU_LBT_X86; ++ if (_cpuid_info.cpucfg_info_id2.bits.LBT_ARM != 0) ++ result |= CPU_LBT_ARM; ++ if (_cpuid_info.cpucfg_info_id2.bits.LBT_MIPS != 0) ++ result |= CPU_LBT_MIPS; ++ if (_cpuid_info.cpucfg_info_id2.bits.LAM != 0) ++ result |= CPU_LAM; ++ ++ if (_cpuid_info.cpucfg_info_id3.bits.CCDMA != 0) ++ result |= CPU_CCDMA; ++ if (_cpuid_info.cpucfg_info_id3.bits.LLDBAR != 0) ++ result |= CPU_LLDBAR; ++ if (_cpuid_info.cpucfg_info_id3.bits.SCDLY != 0) ++ result |= CPU_SCDLY; ++ if (_cpuid_info.cpucfg_info_id3.bits.LLEXC != 0) ++ result |= CPU_LLEXC; ++ ++ result |= CPU_ULSYNC; ++ ++ return result; ++} ++ ++void VM_Version::get_processor_features() { ++ ++ clean_cpuFeatures(); ++ ++ get_cpu_info_stub(&_cpuid_info); ++ _cpuFeatures = get_feature_flags_by_cpucfg(); ++ ++ _supports_cx8 = true; ++ ++ if (UseG1GC && FLAG_IS_DEFAULT(MaxGCPauseMillis)) { ++ FLAG_SET_CMDLINE(uintx, MaxGCPauseMillis, 650); ++ } ++ ++ if (supports_lsx()) { ++ if (FLAG_IS_DEFAULT(UseLSX)) { ++ FLAG_SET_DEFAULT(UseLSX, true); ++ } ++ } else if (UseLSX) { ++ warning("LSX instructions are not available on this CPU"); ++ FLAG_SET_DEFAULT(UseLSX, false); ++ } ++ ++ if (supports_lasx()) { ++ if (FLAG_IS_DEFAULT(UseLASX)) { ++ FLAG_SET_DEFAULT(UseLASX, true); ++ } ++ } else if (UseLASX) { ++ warning("LASX instructions are not available on this CPU"); ++ FLAG_SET_DEFAULT(UseLASX, false); ++ } ++ ++ if (UseLASX && !UseLSX) { ++ warning("LASX instructions depends on LSX, setting UseLASX to false"); ++ FLAG_SET_DEFAULT(UseLASX, false); ++ } ++ ++#ifdef COMPILER2 ++ int max_vector_size = 0; ++ int min_vector_size = 0; ++ if (UseLASX) { ++ max_vector_size = 32; ++ min_vector_size = 16; ++ } ++ else if (UseLSX) { ++ max_vector_size = 16; ++ min_vector_size = 16; ++ } ++ ++ if (!FLAG_IS_DEFAULT(MaxVectorSize)) { ++ if (MaxVectorSize == 0) { ++ // do nothing ++ } else if (MaxVectorSize > max_vector_size) { ++ warning("MaxVectorSize must be at most %i on this platform", max_vector_size); ++ FLAG_SET_DEFAULT(MaxVectorSize, max_vector_size); ++ } else if (MaxVectorSize < min_vector_size) { ++ warning("MaxVectorSize must be at least %i or 0 on this platform, setting to: %i", min_vector_size, min_vector_size); ++ FLAG_SET_DEFAULT(MaxVectorSize, min_vector_size); ++ } else if (!is_power_of_2(MaxVectorSize)) { ++ warning("MaxVectorSize must be a power of 2, setting to default: %i", max_vector_size); ++ FLAG_SET_DEFAULT(MaxVectorSize, max_vector_size); ++ } ++ } else { ++ // If default, use highest supported configuration ++ FLAG_SET_DEFAULT(MaxVectorSize, max_vector_size); ++ } ++#endif ++ ++ if (needs_llsync() && needs_tgtsync() && !needs_ulsync()) { ++ if (FLAG_IS_DEFAULT(UseSyncLevel)) { ++ FLAG_SET_DEFAULT(UseSyncLevel, 1000); ++ } ++ } else if (!needs_llsync() && needs_tgtsync() && needs_ulsync()) { ++ if (FLAG_IS_DEFAULT(UseSyncLevel)) { ++ FLAG_SET_DEFAULT(UseSyncLevel, 2000); ++ } ++ } else if (!needs_llsync() && !needs_tgtsync() && needs_ulsync()) { ++ if (FLAG_IS_DEFAULT(UseSyncLevel)) { ++ FLAG_SET_DEFAULT(UseSyncLevel, 3000); ++ } ++ } else if (needs_llsync() && !needs_tgtsync() && needs_ulsync()) { ++ if (FLAG_IS_DEFAULT(UseSyncLevel)) { ++ FLAG_SET_DEFAULT(UseSyncLevel, 4000); ++ } ++ } else if (needs_llsync() && needs_tgtsync() && needs_ulsync()) { ++ if (FLAG_IS_DEFAULT(UseSyncLevel)) { ++ FLAG_SET_DEFAULT(UseSyncLevel, 10000); ++ } ++ } else { ++ assert(false, "Should Not Reach Here, what is the cpu type?"); ++ if (FLAG_IS_DEFAULT(UseSyncLevel)) { ++ FLAG_SET_DEFAULT(UseSyncLevel, 10000); ++ } ++ } ++ ++ char buf[256]; ++ ++ // A note on the _features_string format: ++ // There are jtreg tests checking the _features_string for various properties. ++ // For some strange reason, these tests require the string to contain ++ // only _lowercase_ characters. Keep that in mind when being surprised ++ // about the unusual notation of features - and when adding new ones. ++ // Features may have one comma at the end. ++ // Furthermore, use one, and only one, separator space between features. ++ // Multiple spaces are considered separate tokens, messing up everything. ++ jio_snprintf(buf, sizeof(buf), "%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s, " ++ "0x%lx, fp_ver: %d, lvz_ver: %d, " ++ "usesynclevel:%d", ++ (is_la64() ? "la64" : ""), ++ (is_la32() ? "la32" : ""), ++ (supports_lsx() ? ", lsx" : ""), ++ (supports_lasx() ? ", lasx" : ""), ++ (supports_crypto() ? ", crypto" : ""), ++ (supports_lam() ? ", am" : ""), ++ (supports_ual() ? ", ual" : ""), ++ (supports_lldbar() ? ", lldbar" : ""), ++ (supports_scdly() ? ", scdly" : ""), ++ (supports_llexc() ? ", llexc" : ""), ++ (supports_lbt_x86() ? ", lbt_x86" : ""), ++ (supports_lbt_arm() ? ", lbt_arm" : ""), ++ (supports_lbt_mips() ? ", lbt_mips" : ""), ++ (needs_llsync() ? ", needs_llsync" : ""), ++ (needs_tgtsync() ? ", needs_tgtsync": ""), ++ (needs_ulsync() ? ", needs_ulsync": ""), ++ _cpuid_info.cpucfg_info_id0.bits.PRID, ++ _cpuid_info.cpucfg_info_id2.bits.FP_VER, ++ _cpuid_info.cpucfg_info_id2.bits.LVZ_VER, ++ UseSyncLevel); ++ _features_str = strdup(buf); ++ ++ assert(!is_la32(), "Should Not Reach Here, what is the cpu type?"); ++ assert( is_la64(), "Should be LoongArch64"); ++ ++ if (FLAG_IS_DEFAULT(AllocatePrefetchStyle)) { ++ FLAG_SET_DEFAULT(AllocatePrefetchStyle, 1); ++ } ++ ++ if (FLAG_IS_DEFAULT(AllocatePrefetchLines)) { ++ FLAG_SET_DEFAULT(AllocatePrefetchLines, 3); ++ } ++ ++ if (FLAG_IS_DEFAULT(AllocatePrefetchStepSize)) { ++ FLAG_SET_DEFAULT(AllocatePrefetchStepSize, 64); ++ } ++ ++ if (FLAG_IS_DEFAULT(AllocatePrefetchDistance)) { ++ FLAG_SET_DEFAULT(AllocatePrefetchDistance, 192); ++ } ++ ++ if (FLAG_IS_DEFAULT(AllocateInstancePrefetchLines)) { ++ FLAG_SET_DEFAULT(AllocateInstancePrefetchLines, 1); ++ } ++ ++ // Basic instructions are used to implement SHA Intrinsics on LA, so sha ++ // instructions support is not needed. ++ if (/*supports_crypto()*/ 1) { ++ if (FLAG_IS_DEFAULT(UseSHA)) { ++ FLAG_SET_DEFAULT(UseSHA, true); ++ } ++ } else if (UseSHA) { ++ warning("SHA instructions are not available on this CPU"); ++ FLAG_SET_DEFAULT(UseSHA, false); ++ } ++ ++ if (UseSHA/* && supports_crypto()*/) { ++ if (FLAG_IS_DEFAULT(UseSHA1Intrinsics)) { ++ FLAG_SET_DEFAULT(UseSHA1Intrinsics, true); ++ } ++ } else if (UseSHA1Intrinsics) { ++ warning("Intrinsics for SHA-1 crypto hash functions not available on this CPU."); ++ FLAG_SET_DEFAULT(UseSHA1Intrinsics, false); ++ } ++ ++ if (UseSHA/* && supports_crypto()*/) { ++ if (FLAG_IS_DEFAULT(UseSHA256Intrinsics)) { ++ FLAG_SET_DEFAULT(UseSHA256Intrinsics, true); ++ } ++ } else if (UseSHA256Intrinsics) { ++ warning("Intrinsics for SHA-224 and SHA-256 crypto hash functions not available on this CPU."); ++ FLAG_SET_DEFAULT(UseSHA256Intrinsics, false); ++ } ++ ++ if (UseSHA512Intrinsics) { ++ warning("Intrinsics for SHA-384 and SHA-512 crypto hash functions not available on this CPU."); ++ FLAG_SET_DEFAULT(UseSHA512Intrinsics, false); ++ } ++ ++ if (!(UseSHA1Intrinsics || UseSHA256Intrinsics || UseSHA512Intrinsics)) { ++ FLAG_SET_DEFAULT(UseSHA, false); ++ } ++ ++ // Basic instructions are used to implement AES Intrinsics on LA, so AES ++ // instructions support is not needed. ++ if (/*supports_crypto()*/ 1) { ++ if (FLAG_IS_DEFAULT(UseAES)) { ++ FLAG_SET_DEFAULT(UseAES, true); ++ } ++ } else if (UseAES) { ++ if (!FLAG_IS_DEFAULT(UseAES)) ++ warning("AES instructions are not available on this CPU"); ++ FLAG_SET_DEFAULT(UseAES, false); ++ } ++ ++ if (UseAES/* && supports_crypto()*/) { ++ if (FLAG_IS_DEFAULT(UseAESIntrinsics)) { ++ FLAG_SET_DEFAULT(UseAESIntrinsics, true); ++ } ++ } else if (UseAESIntrinsics) { ++ if (!FLAG_IS_DEFAULT(UseAESIntrinsics)) ++ warning("AES intrinsics are not available on this CPU"); ++ FLAG_SET_DEFAULT(UseAESIntrinsics, false); ++ } ++ ++ if (FLAG_IS_DEFAULT(UseCRC32)) { ++ FLAG_SET_DEFAULT(UseCRC32, true); ++ } ++ ++ if (UseCRC32) { ++ if (FLAG_IS_DEFAULT(UseCRC32Intrinsics)) { ++ UseCRC32Intrinsics = true; ++ } ++ } ++ ++ if (FLAG_IS_DEFAULT(UseMontgomeryMultiplyIntrinsic)) { ++ UseMontgomeryMultiplyIntrinsic = true; ++ } ++ if (FLAG_IS_DEFAULT(UseMontgomerySquareIntrinsic)) { ++ UseMontgomerySquareIntrinsic = true; ++ } ++ ++ // This machine allows unaligned memory accesses ++ if (FLAG_IS_DEFAULT(UseUnalignedAccesses)) { ++ FLAG_SET_DEFAULT(UseUnalignedAccesses, true); ++ } ++} ++ ++void VM_Version::initialize() { ++ ResourceMark rm; ++ // Making this stub must be FIRST use of assembler ++ ++ stub_blob = BufferBlob::create("get_cpu_info_stub", stub_size); ++ if (stub_blob == NULL) { ++ vm_exit_during_initialization("Unable to allocate get_cpu_info_stub"); ++ } ++ CodeBuffer c(stub_blob); ++ VM_Version_StubGenerator g(&c); ++ get_cpu_info_stub = CAST_TO_FN_PTR(get_cpu_info_stub_t, ++ g.generate_get_cpu_info()); ++ ++ get_processor_features(); ++} +diff --git a/hotspot/src/cpu/loongarch/vm/vm_version_loongarch.hpp b/hotspot/src/cpu/loongarch/vm/vm_version_loongarch.hpp +new file mode 100644 +index 0000000000..23c38fdbe7 +--- /dev/null ++++ b/hotspot/src/cpu/loongarch/vm/vm_version_loongarch.hpp +@@ -0,0 +1,290 @@ ++/* ++ * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2015, 2021, Loongson Technology. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ * ++ */ ++ ++#ifndef CPU_LOONGARCH_VM_VM_VERSION_LOONGARCH_HPP ++#define CPU_LOONGARCH_VM_VM_VERSION_LOONGARCH_HPP ++ ++#include "runtime/globals_extension.hpp" ++#include "runtime/vm_version.hpp" ++ ++ ++class VM_Version: public Abstract_VM_Version { ++public: ++ ++ union LoongArch_Cpucfg_Id0 { ++ uint32_t value; ++ struct { ++ uint32_t PRID : 32; ++ } bits; ++ }; ++ ++ union LoongArch_Cpucfg_Id1 { ++ uint32_t value; ++ struct { ++ uint32_t ARCH : 2, ++ PGMMU : 1, ++ IOCSR : 1, ++ PALEN : 8, ++ VALEN : 8, ++ UAL : 1, // unaligned access ++ RI : 1, ++ EP : 1, ++ RPLV : 1, ++ HP : 1, ++ IOCSR_BRD : 1, ++ MSG_INT : 1, ++ : 5; ++ } bits; ++ }; ++ ++ union LoongArch_Cpucfg_Id2 { ++ uint32_t value; ++ struct { ++ uint32_t FP_CFG : 1, // FP is used, use FP_CFG instead ++ FP_SP : 1, ++ FP_DP : 1, ++ FP_VER : 3, ++ LSX : 1, ++ LASX : 1, ++ COMPLEX : 1, ++ CRYPTO : 1, ++ LVZ : 1, ++ LVZ_VER : 3, ++ LLFTP : 1, ++ LLFTP_VER : 3, ++ LBT_X86 : 1, ++ LBT_ARM : 1, ++ LBT_MIPS : 1, ++ LSPW : 1, ++ LAM : 1, ++ : 9; ++ } bits; ++ }; ++ ++ union LoongArch_Cpucfg_Id3 { ++ uint32_t value; ++ struct { ++ uint32_t CCDMA : 1, ++ SFB : 1, ++ UCACC : 1, ++ LLEXC : 1, ++ SCDLY : 1, ++ LLDBAR : 1, ++ ITLBHMC : 1, ++ ICHMC : 1, ++ SPW_LVL : 3, ++ SPW_HP_HF : 1, ++ RVA : 1, ++ RVAMAXM1 : 4, ++ : 15; ++ } bits; ++ }; ++ ++ union LoongArch_Cpucfg_Id4 { ++ uint32_t value; ++ struct { ++ uint32_t CC_FREQ : 32; ++ } bits; ++ }; ++ ++ union LoongArch_Cpucfg_Id5 { ++ uint32_t value; ++ struct { ++ uint32_t CC_MUL : 16, ++ CC_DIV : 16; ++ } bits; ++ }; ++ ++ union LoongArch_Cpucfg_Id6 { ++ uint32_t value; ++ struct { ++ uint32_t PMP : 1, ++ PMVER : 3, ++ PMNUM : 4, ++ PMBITS : 6, ++ UPM : 1, ++ : 17; ++ } bits; ++ }; ++ ++ union LoongArch_Cpucfg_Id10 { ++ uint32_t value; ++ struct { ++ uint32_t L1IU_PRESENT : 1, ++ L1IU_UNIFY : 1, ++ L1D_PRESENT : 1, ++ L2IU_PRESENT : 1, ++ L2IU_UNIFY : 1, ++ L2IU_PRIVATE : 1, ++ L2IU_INCLUSIVE : 1, ++ L2D_PRESENT : 1, ++ L2D_PRIVATE : 1, ++ L2D_INCLUSIVE : 1, ++ L3IU_PRESENT : 1, ++ L3IU_UNIFY : 1, ++ L3IU_PRIVATE : 1, ++ L3IU_INCLUSIVE : 1, ++ L3D_PRESENT : 1, ++ L3D_PRIVATE : 1, ++ L3D_INCLUSIVE : 1, ++ : 15; ++ } bits; ++ }; ++ ++ union LoongArch_Cpucfg_Id11 { ++ uint32_t value; ++ struct { ++ uint32_t WAYM1 : 16, ++ INDEXMLOG2 : 8, ++ LINESIZELOG2 : 7, ++ : 1; ++ } bits; ++ }; ++ ++ union LoongArch_Cpucfg_Id12 { ++ uint32_t value; ++ struct { ++ uint32_t WAYM1 : 16, ++ INDEXMLOG2 : 8, ++ LINESIZELOG2 : 7, ++ : 1; ++ } bits; ++ }; ++ ++ union LoongArch_Cpucfg_Id13 { ++ uint32_t value; ++ struct { ++ uint32_t WAYM1 : 16, ++ INDEXMLOG2 : 8, ++ LINESIZELOG2 : 7, ++ : 1; ++ } bits; ++ }; ++ ++ union LoongArch_Cpucfg_Id14 { ++ uint32_t value; ++ struct { ++ uint32_t WAYM1 : 16, ++ INDEXMLOG2 : 8, ++ LINESIZELOG2 : 7, ++ : 1; ++ } bits; ++ }; ++ ++protected: ++ ++ enum { ++ CPU_LA32 = (1 << 1), ++ CPU_LA64 = (1 << 2), ++ CPU_LLEXC = (1 << 3), ++ CPU_SCDLY = (1 << 4), ++ CPU_LLDBAR = (1 << 5), ++ CPU_LBT_X86 = (1 << 6), ++ CPU_LBT_ARM = (1 << 7), ++ CPU_LBT_MIPS = (1 << 8), ++ CPU_CCDMA = (1 << 9), ++ CPU_COMPLEX = (1 << 10), ++ CPU_FP = (1 << 11), ++ CPU_CRYPTO = (1 << 14), ++ CPU_LSX = (1 << 15), ++ CPU_LASX = (1 << 17), ++ CPU_LAM = (1 << 21), ++ CPU_LLSYNC = (1 << 23), ++ CPU_TGTSYNC = (1 << 24), ++ CPU_ULSYNC = (1 << 25), ++ CPU_UAL = (1 << 26), ++ ++ //////////////////////add some other feature here////////////////// ++ } cpuFeatureFlags; ++ ++ static int _cpuFeatures; ++ static const char* _features_str; ++ static bool _cpu_info_is_initialized; ++ ++ struct CpuidInfo { ++ LoongArch_Cpucfg_Id0 cpucfg_info_id0; ++ LoongArch_Cpucfg_Id1 cpucfg_info_id1; ++ LoongArch_Cpucfg_Id2 cpucfg_info_id2; ++ LoongArch_Cpucfg_Id3 cpucfg_info_id3; ++ LoongArch_Cpucfg_Id4 cpucfg_info_id4; ++ LoongArch_Cpucfg_Id5 cpucfg_info_id5; ++ LoongArch_Cpucfg_Id6 cpucfg_info_id6; ++ LoongArch_Cpucfg_Id10 cpucfg_info_id10; ++ LoongArch_Cpucfg_Id11 cpucfg_info_id11; ++ LoongArch_Cpucfg_Id12 cpucfg_info_id12; ++ LoongArch_Cpucfg_Id13 cpucfg_info_id13; ++ LoongArch_Cpucfg_Id14 cpucfg_info_id14; ++ }; ++ ++ // The actual cpuid info block ++ static CpuidInfo _cpuid_info; ++ ++ static uint32_t get_feature_flags_by_cpucfg(); ++ static int get_feature_flags_by_cpuinfo(int features); ++ static void get_processor_features(); ++ ++public: ++ // Offsets for cpuid asm stub ++ static ByteSize Loongson_Cpucfg_id0_offset() { return byte_offset_of(CpuidInfo, cpucfg_info_id0); } ++ static ByteSize Loongson_Cpucfg_id1_offset() { return byte_offset_of(CpuidInfo, cpucfg_info_id1); } ++ static ByteSize Loongson_Cpucfg_id2_offset() { return byte_offset_of(CpuidInfo, cpucfg_info_id2); } ++ static ByteSize Loongson_Cpucfg_id3_offset() { return byte_offset_of(CpuidInfo, cpucfg_info_id3); } ++ static ByteSize Loongson_Cpucfg_id4_offset() { return byte_offset_of(CpuidInfo, cpucfg_info_id4); } ++ static ByteSize Loongson_Cpucfg_id5_offset() { return byte_offset_of(CpuidInfo, cpucfg_info_id5); } ++ static ByteSize Loongson_Cpucfg_id6_offset() { return byte_offset_of(CpuidInfo, cpucfg_info_id6); } ++ static ByteSize Loongson_Cpucfg_id10_offset() { return byte_offset_of(CpuidInfo, cpucfg_info_id10); } ++ static ByteSize Loongson_Cpucfg_id11_offset() { return byte_offset_of(CpuidInfo, cpucfg_info_id11); } ++ static ByteSize Loongson_Cpucfg_id12_offset() { return byte_offset_of(CpuidInfo, cpucfg_info_id12); } ++ static ByteSize Loongson_Cpucfg_id13_offset() { return byte_offset_of(CpuidInfo, cpucfg_info_id13); } ++ static ByteSize Loongson_Cpucfg_id14_offset() { return byte_offset_of(CpuidInfo, cpucfg_info_id14); } ++ ++ static void clean_cpuFeatures() { _cpuFeatures = 0; } ++ ++ // Initialization ++ static void initialize(); ++ ++ static bool cpu_info_is_initialized() { return _cpu_info_is_initialized; } ++ ++ static bool is_la32() { return _cpuFeatures & CPU_LA32; } ++ static bool is_la64() { return _cpuFeatures & CPU_LA64; } ++ static bool supports_crypto() { return _cpuFeatures & CPU_CRYPTO; } ++ static bool supports_lsx() { return _cpuFeatures & CPU_LSX; } ++ static bool supports_lasx() { return _cpuFeatures & CPU_LASX; } ++ static bool supports_lam() { return _cpuFeatures & CPU_LAM; } ++ static bool supports_llexc() { return _cpuFeatures & CPU_LLEXC; } ++ static bool supports_scdly() { return _cpuFeatures & CPU_SCDLY; } ++ static bool supports_lldbar() { return _cpuFeatures & CPU_LLDBAR; } ++ static bool supports_ual() { return _cpuFeatures & CPU_UAL; } ++ static bool supports_lbt_x86() { return _cpuFeatures & CPU_LBT_X86; } ++ static bool supports_lbt_arm() { return _cpuFeatures & CPU_LBT_ARM; } ++ static bool supports_lbt_mips() { return _cpuFeatures & CPU_LBT_MIPS; } ++ static bool needs_llsync() { return !supports_lldbar(); } ++ static bool needs_tgtsync() { return 1; } ++ static bool needs_ulsync() { return 1; } ++ ++ static const char* cpu_features() { return _features_str; } ++}; ++ ++#endif // CPU_LOONGARCH_VM_VM_VERSION_LOONGARCH_HPP +diff --git a/hotspot/src/cpu/loongarch/vm/vmreg_loongarch.cpp b/hotspot/src/cpu/loongarch/vm/vmreg_loongarch.cpp +new file mode 100644 +index 0000000000..52bccfc183 +--- /dev/null ++++ b/hotspot/src/cpu/loongarch/vm/vmreg_loongarch.cpp +@@ -0,0 +1,51 @@ ++/* ++ * Copyright (c) 2006, 2012, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2015, 2020, Loongson Technology. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ * ++ */ ++ ++#include "precompiled.hpp" ++#include "asm/assembler.hpp" ++#include "code/vmreg.hpp" ++ ++ ++ ++void VMRegImpl::set_regName() { ++ Register reg = ::as_Register(0); ++ int i; ++ for (i = 0; i < ConcreteRegisterImpl::max_gpr ; ) { ++ regName[i++] = reg->name(); ++ regName[i++] = reg->name(); ++ reg = reg->successor(); ++ } ++ ++ FloatRegister freg = ::as_FloatRegister(0); ++ for ( ; i < ConcreteRegisterImpl::max_fpr ; ) { ++ regName[i++] = freg->name(); ++ regName[i++] = freg->name(); ++ freg = freg->successor(); ++ } ++ ++ for ( ; i < ConcreteRegisterImpl::number_of_registers ; i ++ ) { ++ regName[i] = "NON-GPR-FPR"; ++ } ++} +diff --git a/hotspot/src/cpu/loongarch/vm/vmreg_loongarch.hpp b/hotspot/src/cpu/loongarch/vm/vmreg_loongarch.hpp +new file mode 100644 +index 0000000000..80a1fc57de +--- /dev/null ++++ b/hotspot/src/cpu/loongarch/vm/vmreg_loongarch.hpp +@@ -0,0 +1,35 @@ ++/* ++ * Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2015, 2020, Loongson Technology. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ * ++ */ ++ ++#ifndef CPU_LOONGARCH_VM_VMREG_LOONGARCH_HPP ++#define CPU_LOONGARCH_VM_VMREG_LOONGARCH_HPP ++ ++bool is_Register(); ++Register as_Register(); ++ ++bool is_FloatRegister(); ++FloatRegister as_FloatRegister(); ++ ++#endif // CPU_LOONGARCH_VM_VMREG_LOONGARCH_HPP +diff --git a/hotspot/src/cpu/loongarch/vm/vmreg_loongarch.inline.hpp b/hotspot/src/cpu/loongarch/vm/vmreg_loongarch.inline.hpp +new file mode 100644 +index 0000000000..f822d4c355 +--- /dev/null ++++ b/hotspot/src/cpu/loongarch/vm/vmreg_loongarch.inline.hpp +@@ -0,0 +1,66 @@ ++/* ++ * Copyright (c) 2006, 2012, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2015, 2020, Loongson Technology. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ * ++ */ ++ ++#ifndef CPU_LOONGARCH_VM_VMREG_LOONGARCH_INLINE_HPP ++#define CPU_LOONGARCH_VM_VMREG_LOONGARCH_INLINE_HPP ++ ++inline VMReg RegisterImpl::as_VMReg() { ++ if( this==noreg ) return VMRegImpl::Bad(); ++ return VMRegImpl::as_VMReg(encoding() << 1 ); ++} ++ ++inline VMReg FloatRegisterImpl::as_VMReg() { ++ return VMRegImpl::as_VMReg((encoding() << 1) + ConcreteRegisterImpl::max_gpr); ++} ++ ++inline bool VMRegImpl::is_Register() { ++ return (unsigned int) value() < (unsigned int) ConcreteRegisterImpl::max_gpr; ++} ++ ++inline bool VMRegImpl::is_FloatRegister() { ++ return value() >= ConcreteRegisterImpl::max_gpr && value() < ConcreteRegisterImpl::max_fpr; ++} ++ ++inline Register VMRegImpl::as_Register() { ++ ++ assert( is_Register(), "must be"); ++ return ::as_Register(value() >> 1); ++} ++ ++inline FloatRegister VMRegImpl::as_FloatRegister() { ++ assert( is_FloatRegister(), "must be" ); ++ assert( is_even(value()), "must be" ); ++ return ::as_FloatRegister((value() - ConcreteRegisterImpl::max_gpr) >> 1); ++} ++ ++inline bool VMRegImpl::is_concrete() { ++ assert(is_reg(), "must be"); ++ if(is_Register()) return true; ++ if(is_FloatRegister()) return true; ++ assert(false, "what register?"); ++ return false; ++} ++ ++#endif // CPU_LOONGARCH_VM_VMREG_LOONGARCH_INLINE_HPP +diff --git a/hotspot/src/cpu/loongarch/vm/vtableStubs_loongarch_64.cpp b/hotspot/src/cpu/loongarch/vm/vtableStubs_loongarch_64.cpp +new file mode 100644 +index 0000000000..df0d176b8b +--- /dev/null ++++ b/hotspot/src/cpu/loongarch/vm/vtableStubs_loongarch_64.cpp +@@ -0,0 +1,300 @@ ++/* ++ * Copyright (c) 2003, 2014, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2015, 2021, Loongson Technology. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ * ++ */ ++ ++#include "precompiled.hpp" ++#include "asm/macroAssembler.hpp" ++#include "code/vtableStubs.hpp" ++#include "interp_masm_loongarch_64.hpp" ++#include "memory/resourceArea.hpp" ++#include "oops/compiledICHolder.hpp" ++#include "oops/klassVtable.hpp" ++#include "runtime/sharedRuntime.hpp" ++#include "vmreg_loongarch.inline.hpp" ++#ifdef COMPILER2 ++#include "opto/runtime.hpp" ++#endif ++ ++ ++// machine-dependent part of VtableStubs: create VtableStub of correct size and ++// initialize its code ++ ++#define __ masm-> ++ ++#define A0 RA0 ++#define A1 RA1 ++#define A2 RA2 ++#define A3 RA3 ++#define A4 RA4 ++#define A5 RA5 ++#define A6 RA6 ++#define A7 RA7 ++#define T0 RT0 ++#define T1 RT1 ++#define T2 RT2 ++#define T3 RT3 ++#define T4 RT4 ++#define T5 RT5 ++#define T6 RT6 ++#define T7 RT7 ++#define T8 RT8 ++ ++#ifndef PRODUCT ++extern "C" void bad_compiled_vtable_index(JavaThread* thread, ++ oop receiver, ++ int index); ++#endif ++ ++// used by compiler only; reciever in T0. ++// used registers : ++// Rmethod : receiver klass & method ++// NOTE: If this code is used by the C1, the receiver_location is always 0. ++// when reach here, receiver in T0, klass in T8 ++VtableStub* VtableStubs::create_vtable_stub(int vtable_index) { ++ const int la_code_length = VtableStub::pd_code_size_limit(true); ++ VtableStub* s = new(la_code_length) VtableStub(true, vtable_index); ++ ResourceMark rm; ++ CodeBuffer cb(s->entry_point(), la_code_length); ++ MacroAssembler* masm = new MacroAssembler(&cb); ++ Register t1 = T8, t2 = Rmethod; ++#ifndef PRODUCT ++ if (CountCompiledCalls) { ++ __ li(AT, SharedRuntime::nof_megamorphic_calls_addr()); ++ __ ld_w(t1, AT , 0); ++ __ addi_w(t1, t1, 1); ++ __ st_w(t1, AT,0); ++ } ++#endif ++ ++ // get receiver (need to skip return address on top of stack) ++ //assert(receiver_location == T0->as_VMReg(), "receiver expected in T0"); ++ ++ // get receiver klass ++ address npe_addr = __ pc(); ++ __ load_klass(t1, T0); ++ // compute entry offset (in words) ++ int entry_offset = InstanceKlass::vtable_start_offset() + vtable_index*vtableEntry::size(); ++#ifndef PRODUCT ++ if (DebugVtables) { ++ Label L; ++ // check offset vs vtable length ++ __ ld_w(t2, t1, InstanceKlass::vtable_length_offset()*wordSize); ++ assert(Assembler::is_simm16(vtable_index*vtableEntry::size()), "change this code"); ++ __ li(AT, vtable_index*vtableEntry::size()); ++ __ blt(AT, t2, L); ++ __ li(A2, vtable_index); ++ __ move(A1, A0); ++ __ call_VM(noreg, CAST_FROM_FN_PTR(address, bad_compiled_vtable_index), A1, A2); ++ __ bind(L); ++ } ++#endif // PRODUCT ++ // load methodOop and target address ++ const Register method = Rmethod; ++ int offset = entry_offset*wordSize + vtableEntry::method_offset_in_bytes(); ++ if (Assembler::is_simm(offset, 12)) { ++ __ ld_ptr(method, t1, offset); ++ } else { ++ __ li(AT, offset); ++ __ ld_ptr(method, t1, AT); ++ } ++ if (DebugVtables) { ++ Label L; ++ __ beq(method, R0, L); ++ __ ld_d(AT, method,in_bytes(Method::from_compiled_offset())); ++ __ bne(AT, R0, L); ++ __ stop("Vtable entry is NULL"); ++ __ bind(L); ++ } ++ // T8: receiver klass ++ // T0: receiver ++ // Rmethod: methodOop ++ // T4: entry ++ address ame_addr = __ pc(); ++ __ ld_ptr(T4, method,in_bytes(Method::from_compiled_offset())); ++ __ jr(T4); ++ masm->flush(); ++ s->set_exception_points(npe_addr, ame_addr); ++ return s; ++} ++ ++ ++// used registers : ++// T1 T2 ++// when reach here, the receiver in T0, klass in T1 ++VtableStub* VtableStubs::create_itable_stub(int itable_index) { ++ // Note well: pd_code_size_limit is the absolute minimum we can get ++ // away with. If you add code here, bump the code stub size ++ // returned by pd_code_size_limit! ++ const int la_code_length = VtableStub::pd_code_size_limit(false); ++ VtableStub* s = new(la_code_length) VtableStub(false, itable_index); ++ ResourceMark rm; ++ CodeBuffer cb(s->entry_point(), la_code_length); ++ MacroAssembler* masm = new MacroAssembler(&cb); ++ // we T8,T4 as temparary register, they are free from register allocator ++ Register t1 = T8, t2 = T2; ++ // Entry arguments: ++ // T1: Interface ++ // T0: Receiver ++ ++#ifndef PRODUCT ++ if (CountCompiledCalls) { ++ __ li(AT, SharedRuntime::nof_megamorphic_calls_addr()); ++ __ ld_w(T8, AT, 0); ++ __ addi_w(T8, T8, 1); ++ __ st_w(T8, AT, 0); ++ } ++#endif /* PRODUCT */ ++ const Register holder_klass_reg = T1; // declaring interface klass (DECC) ++ const Register resolved_klass_reg = Rmethod; // resolved interface klass (REFC) ++ const Register icholder_reg = T1; ++ __ ld_ptr(resolved_klass_reg, icholder_reg, CompiledICHolder::holder_klass_offset()); ++ __ ld_ptr(holder_klass_reg, icholder_reg, CompiledICHolder::holder_metadata_offset()); ++ ++ // get receiver klass (also an implicit null-check) ++ address npe_addr = __ pc(); ++ __ load_klass(t1, T0); ++ { ++ // x86 use lookup_interface_method, but lookup_interface_method does not work on LoongArch. ++ const int base = InstanceKlass::vtable_start_offset() * wordSize; ++ assert(vtableEntry::size() * wordSize == 8, "adjust the scaling in the code below"); ++ assert(Assembler::is_simm16(base), "change this code"); ++ __ addi_d(t2, t1, base); ++ assert(Assembler::is_simm16(InstanceKlass::vtable_length_offset() * wordSize), "change this code"); ++ __ ld_w(AT, t1, InstanceKlass::vtable_length_offset() * wordSize); ++ __ alsl_d(t2, AT, t2, Address::times_8 - 1); ++ if (HeapWordsPerLong > 1) { ++ __ round_to(t2, BytesPerLong); ++ } ++ ++ Label hit, entry; ++ assert(Assembler::is_simm16(itableOffsetEntry::size() * wordSize), "change this code"); ++ __ bind(entry); ++ ++#ifdef ASSERT ++ // Check that the entry is non-null ++ if (DebugVtables) { ++ Label L; ++ assert(Assembler::is_simm16(itableOffsetEntry::interface_offset_in_bytes()), "change this code"); ++ __ ld_w(AT, t1, itableOffsetEntry::interface_offset_in_bytes()); ++ __ bne(AT, R0, L); ++ __ stop("null entry point found in itable's offset table"); ++ __ bind(L); ++ } ++#endif ++ assert(Assembler::is_simm16(itableOffsetEntry::interface_offset_in_bytes()), "change this code"); ++ __ ld_ptr(AT, t2, itableOffsetEntry::interface_offset_in_bytes()); ++ __ addi_d(t2, t2, itableOffsetEntry::size() * wordSize); ++ __ bne(AT, resolved_klass_reg, entry); ++ ++ } ++ ++ // add for compressedoops ++ __ load_klass(t1, T0); ++ // compute itable entry offset (in words) ++ const int base = InstanceKlass::vtable_start_offset() * wordSize; ++ assert(vtableEntry::size() * wordSize == 8, "adjust the scaling in the code below"); ++ assert(Assembler::is_simm16(base), "change this code"); ++ __ addi_d(t2, t1, base); ++ assert(Assembler::is_simm16(InstanceKlass::vtable_length_offset() * wordSize), "change this code"); ++ __ ld_w(AT, t1, InstanceKlass::vtable_length_offset() * wordSize); ++ __ alsl_d(t2, AT, t2, Address::times_8 - 1); ++ if (HeapWordsPerLong > 1) { ++ __ round_to(t2, BytesPerLong); ++ } ++ ++ Label hit, entry; ++ assert(Assembler::is_simm16(itableOffsetEntry::size() * wordSize), "change this code"); ++ __ bind(entry); ++ ++#ifdef ASSERT ++ // Check that the entry is non-null ++ if (DebugVtables) { ++ Label L; ++ assert(Assembler::is_simm16(itableOffsetEntry::interface_offset_in_bytes()), "change this code"); ++ __ ld_w(AT, t1, itableOffsetEntry::interface_offset_in_bytes()); ++ __ bne(AT, R0, L); ++ __ stop("null entry point found in itable's offset table"); ++ __ bind(L); ++ } ++#endif ++ assert(Assembler::is_simm16(itableOffsetEntry::interface_offset_in_bytes()), "change this code"); ++ __ ld_ptr(AT, t2, itableOffsetEntry::interface_offset_in_bytes()); ++ __ addi_d(t2, t2, itableOffsetEntry::size() * wordSize); ++ __ bne(AT, holder_klass_reg, entry); ++ ++ // We found a hit, move offset into T4 ++ __ ld_ptr(t2, t2, itableOffsetEntry::offset_offset_in_bytes() - itableOffsetEntry::size() * wordSize); ++ ++ // Compute itableMethodEntry. ++ const int method_offset = (itableMethodEntry::size() * wordSize * itable_index) + ++ itableMethodEntry::method_offset_in_bytes(); ++ ++ // Get methodOop and entrypoint for compiler ++ const Register method = Rmethod; ++ ++ __ slli_d(AT, t2, Address::times_1); ++ __ add_d(AT, AT, t1 ); ++ if (Assembler::is_simm(method_offset, 12)) { ++ __ ld_ptr(method, AT, method_offset); ++ } else { ++ __ li(t1, method_offset); ++ __ ld_ptr(method, AT, t1); ++ } ++ ++#ifdef ASSERT ++ if (DebugVtables) { ++ Label L1; ++ __ beq(method, R0, L1); ++ __ ld_d(AT, method,in_bytes(Method::from_compiled_offset())); ++ __ bne(AT, R0, L1); ++ __ stop("methodOop is null"); ++ __ bind(L1); ++ } ++#endif // ASSERT ++ ++ // Rmethod: methodOop ++ // T0: receiver ++ // T4: entry point ++ address ame_addr = __ pc(); ++ __ ld_ptr(T4, method,in_bytes(Method::from_compiled_offset())); ++ __ jr(T4); ++ masm->flush(); ++ s->set_exception_points(npe_addr, ame_addr); ++ return s; ++} ++ ++// NOTE : whenever you change the code above, dont forget to change the const here ++int VtableStub::pd_code_size_limit(bool is_vtable_stub) { ++ if (is_vtable_stub) { ++ return ( DebugVtables ? 600 : 28) + (CountCompiledCalls ? 24 : 0)+ ++ (UseCompressedOops ? 16 : 0); ++ } else { ++ return ( DebugVtables ? 636 : 152) + (CountCompiledCalls ? 24 : 0)+ ++ (UseCompressedOops ? 32 : 0); ++ } ++} ++ ++int VtableStub::pd_code_alignment() { ++ return wordSize; ++} +diff --git a/hotspot/src/cpu/mips/vm/assembler_mips.cpp b/hotspot/src/cpu/mips/vm/assembler_mips.cpp +new file mode 100644 +index 0000000000..51ef7f472b +--- /dev/null ++++ b/hotspot/src/cpu/mips/vm/assembler_mips.cpp +@@ -0,0 +1,748 @@ ++/* ++ * Copyright (c) 1997, 2014, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2015, 2022, Loongson Technology. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ * ++ */ ++ ++#include "precompiled.hpp" ++#include "asm/assembler.hpp" ++#include "asm/assembler.inline.hpp" ++#include "gc_interface/collectedHeap.inline.hpp" ++#include "interpreter/interpreter.hpp" ++#include "memory/cardTableModRefBS.hpp" ++#include "memory/resourceArea.hpp" ++#include "prims/methodHandles.hpp" ++#include "runtime/biasedLocking.hpp" ++#include "runtime/interfaceSupport.hpp" ++#include "runtime/objectMonitor.hpp" ++#include "runtime/os.hpp" ++#include "runtime/sharedRuntime.hpp" ++#include "runtime/stubRoutines.hpp" ++#if INCLUDE_ALL_GCS ++#include "gc_implementation/g1/g1CollectedHeap.inline.hpp" ++#include "gc_implementation/g1/g1SATBCardTableModRefBS.hpp" ++#include "gc_implementation/g1/heapRegion.hpp" ++#endif // INCLUDE_ALL_GCS ++ ++#ifdef PRODUCT ++#define BLOCK_COMMENT(str) /* nothing */ ++#define STOP(error) stop(error) ++#else ++#define BLOCK_COMMENT(str) block_comment(str) ++#define STOP(error) block_comment(error); stop(error) ++#endif ++ ++#define BIND(label) bind(label); BLOCK_COMMENT(#label ":") ++ ++// Implementation of AddressLiteral ++ ++AddressLiteral::AddressLiteral(address target, relocInfo::relocType rtype) { ++ _is_lval = false; ++ _target = target; ++ _rspec = rspec_from_rtype(rtype, target); ++} ++ ++// Implementation of Address ++ ++Address Address::make_array(ArrayAddress adr) { ++ AddressLiteral base = adr.base(); ++ Address index = adr.index(); ++ assert(index._disp == 0, "must not have disp"); // maybe it can? ++ Address array(index._base, index._index, index._scale, (intptr_t) base.target()); ++ array._rspec = base._rspec; ++ return array; ++} ++ ++// exceedingly dangerous constructor ++Address::Address(address loc, RelocationHolder spec) { ++ _base = noreg; ++ _index = noreg; ++ _scale = no_scale; ++ _disp = (intptr_t) loc; ++ _rspec = spec; ++} ++ ++ ++#define A0 RA0 ++#define A1 RA1 ++#define A2 RA2 ++#define A3 RA3 ++#define A4 RA4 ++#define A5 RA5 ++#define A6 RA6 ++#define A7 RA7 ++#define T0 RT0 ++#define T1 RT1 ++#define T2 RT2 ++#define T3 RT3 ++#define T8 RT8 ++#define T9 RT9 ++ ++// Implementation of Assembler ++const char *Assembler::ops_name[] = { ++ "special", "regimm", "j", "jal", "beq", "bne", "blez", "bgtz", ++ "addi", "addiu", "slti", "sltiu", "andi", "ori", "xori", "lui", ++ "cop0", "cop1", "cop2", "cop3", "beql", "bnel", "bleql", "bgtzl", ++ "daddi", "daddiu", "ldl", "ldr", "", "", "", "", ++ "lb", "lh", "lwl", "lw", "lbu", "lhu", "lwr", "lwu", ++ "sb", "sh", "swl", "sw", "sdl", "sdr", "swr", "cache", ++ "ll", "lwc1", "", "", "lld", "ldc1", "", "ld", ++ "sc", "swc1", "", "", "scd", "sdc1", "", "sd" ++}; ++ ++const char* Assembler::special_name[] = { ++ "sll", "", "srl", "sra", "sllv", "", "srlv", "srav", ++ "jr", "jalr", "movz", "movn", "syscall", "break", "", "sync", ++ "mfhi", "mthi", "mflo", "mtlo", "dsll", "", "dsrl", "dsra", ++ "mult", "multu", "div", "divu", "dmult", "dmultu", "ddiv", "ddivu", ++ "add", "addu", "sub", "subu", "and", "or", "xor", "nor", ++ "", "", "slt", "sltu", "dadd", "daddu", "dsub", "dsubu", ++ "tge", "tgeu", "tlt", "tltu", "teq", "", "tne", "", ++ "dsll", "", "dsrl", "dsra", "dsll32", "", "dsrl32", "dsra32" ++}; ++ ++const char* Assembler::cop1_name[] = { ++ "add", "sub", "mul", "div", "sqrt", "abs", "mov", "neg", ++ "round.l", "trunc.l", "ceil.l", "floor.l", "round.w", "trunc.w", "ceil.w", "floor.w", ++ "", "", "", "", "", "", "", "", ++ "", "", "", "", "", "", "", "", ++ "", "", "", "", "", "", "", "", ++ "", "", "", "", "", "", "", "", ++ "c.f", "c.un", "c.eq", "c.ueq", "c.olt", "c.ult", "c.ole", "c.ule", ++ "c.sf", "c.ngle", "c.seq", "c.ngl", "c.lt", "c.nge", "c.le", "c.ngt" ++}; ++ ++const char* Assembler::cop1x_name[] = { ++ "lwxc1", "ldxc1", "", "", "", "luxc1", "", "", ++ "swxc1", "sdxc1", "", "", "", "suxc1", "", "prefx", ++ "", "", "", "", "", "", "alnv.ps", "", ++ "", "", "", "", "", "", "", "", ++ "madd.s", "madd.d", "", "", "", "", "madd.ps", "", ++ "msub.s", "msub.d", "", "", "", "", "msub.ps", "", ++ "nmadd.s", "nmadd.d", "", "", "", "", "nmadd.ps", "", ++ "nmsub.s", "nmsub.d", "", "", "", "", "nmsub.ps", "" ++}; ++ ++const char* Assembler::special2_name[] = { ++ "madd", "", "mul", "", "msub", "", "", "", ++ "", "", "", "", "", "", "", "", ++ "", "gsdmult", "", "", "gsdiv", "gsddiv", "", "", ++ "", "", "", "", "gsmod", "gsdmod", "", "", ++ "", "", "", "", "", "", "", "", ++ "", "", "", "", "", "", "", "", ++ "", "", "", "", "", "", "", "", ++ "", "", "", "", "", "", "", "" ++}; ++ ++const char* Assembler::special3_name[] = { ++ "ext", "", "", "", "ins", "dinsm", "dinsu", "dins", ++ "", "", "", "", "", "", "", "", ++ "", "", "", "", "", "", "", "", ++ "", "", "", "", "", "", "", "", ++ "bshfl", "", "", "", "", "", "", "", ++ "", "", "", "", "", "", "", "", ++ "", "", "", "", "", "", "", "", ++ "", "", "", "", "", "", "", "", ++}; ++ ++const char* Assembler::regimm_name[] = { ++ "bltz", "bgez", "bltzl", "bgezl", "", "", "", "", ++ "tgei", "tgeiu", "tlti", "tltiu", "teqi", "", "tnei", "", ++ "bltzal", "bgezal", "bltzall", "bgezall" ++}; ++ ++const char* Assembler::gs_ldc2_name[] = { ++ "gslbx", "gslhx", "gslwx", "gsldx", "", "", "gslwxc1", "gsldxc1" ++}; ++ ++ ++const char* Assembler::gs_lwc2_name[] = { ++ "", "", "", "", "", "", "", "", ++ "", "", "", "", "", "", "", "", ++ "gslble", "gslbgt", "gslhle", "gslhgt", "gslwle", "gslwgt", "gsldle", "gsldgt", ++ "", "", "", "gslwlec1", "gslwgtc1", "gsldlec1", "gsldgtc1", "",/*LWDIR, LWPTE, LDDIR and LDPTE have the same low 6 bits.*/ ++ "gslq", "" ++}; ++ ++const char* Assembler::gs_sdc2_name[] = { ++ "gssbx", "gsshx", "gsswx", "gssdx", "", "", "gsswxc1", "gssdxc1" ++}; ++ ++const char* Assembler::gs_swc2_name[] = { ++ "", "", "", "", "", "", "", "", ++ "", "", "", "", "", "", "", "", ++ "gssble", "gssbgt", "gsshle", "gsshgt", "gsswle", "gsswgt", "gssdle", "gssdgt", ++ "", "", "", "", "gsswlec1", "gsswgtc1", "gssdlec1", "gssdgtc1", ++ "gssq", "" ++}; ++ ++//misleading name, print only branch/jump instruction ++void Assembler::print_instruction(int inst) { ++ const char *s; ++ switch( opcode(inst) ) { ++ default: ++ s = ops_name[opcode(inst)]; ++ break; ++ case special_op: ++ s = special_name[special(inst)]; ++ break; ++ case regimm_op: ++ s = special_name[rt(inst)]; ++ break; ++ } ++ ++ ::tty->print("%s", s); ++} ++ ++int Assembler::is_int_mask(int x) { ++ int xx = x; ++ int count = 0; ++ ++ while (x != 0) { ++ x &= (x - 1); ++ count++; ++ } ++ ++ if ((1<>2; ++ switch(opcode(inst)) { ++ case j_op: ++ case jal_op: ++ case lui_op: ++ case ori_op: ++ case daddiu_op: ++ ShouldNotReachHere(); ++ break; ++ default: ++ assert(is_simm16(v), "must be simm16"); ++#ifndef PRODUCT ++ if(!is_simm16(v)) ++ { ++ tty->print_cr("must be simm16"); ++ tty->print_cr("Inst: %x", inst); ++ } ++#endif ++ ++ v = low16(v); ++ inst &= 0xffff0000; ++ break; ++ } ++ ++ return inst | v; ++} ++ ++int Assembler::branch_destination(int inst, int pos) { ++ int off; ++ ++ switch(opcode(inst)) { ++ case j_op: ++ case jal_op: ++ assert(false, "should not use j/jal here"); ++ break; ++ default: ++ off = expand(low16(inst), 15); ++ break; ++ } ++ ++ return off ? pos + 4 + (off<<2) : 0; ++} ++ ++int AbstractAssembler::code_fill_byte() { ++ return 0x00; // illegal instruction 0x00000000 ++} ++ ++// Now the Assembler instruction (identical for 32/64 bits) ++ ++void Assembler::lb(Register rt, Address src) { ++ assert(src.index() == NOREG, "index is unimplemented"); ++ lb(rt, src.base(), src.disp()); ++} ++ ++void Assembler::lbu(Register rt, Address src) { ++ assert(src.index() == NOREG, "index is unimplemented"); ++ lbu(rt, src.base(), src.disp()); ++} ++ ++void Assembler::ld(Register rt, Address dst){ ++ Register src = rt; ++ Register base = dst.base(); ++ Register index = dst.index(); ++ ++ int scale = dst.scale(); ++ int disp = dst.disp(); ++ ++ if (index != noreg) { ++ if (Assembler::is_simm16(disp)) { ++ if ( UseLEXT1 && Assembler::is_simm(disp, 8) ) { ++ if (scale == 0) { ++ gsldx(src, base, index, disp); ++ } else { ++ dsll(AT, index, scale); ++ gsldx(src, base, AT, disp); ++ } ++ } else { ++ if (scale == 0) { ++ daddu(AT, base, index); ++ } else { ++ dsll(AT, index, scale); ++ daddu(AT, base, AT); ++ } ++ ld(src, AT, disp); ++ } ++ } else { ++ if (scale == 0) { ++ lui(AT, split_low(disp >> 16)); ++ if (split_low(disp)) ori(AT, AT, split_low(disp)); ++ daddu(AT, AT, base); ++ if (UseLEXT1) { ++ gsldx(src, AT, index, 0); ++ } else { ++ daddu(AT, AT, index); ++ ld(src, AT, 0); ++ } ++ } else { ++ assert_different_registers(src, AT); ++ dsll(AT, index, scale); ++ daddu(AT, base, AT); ++ lui(src, split_low(disp >> 16)); ++ if (split_low(disp)) ori(src, src, split_low(disp)); ++ if (UseLEXT1) { ++ gsldx(src, AT, src, 0); ++ } else { ++ daddu(AT, AT, src); ++ ld(src, AT, 0); ++ } ++ } ++ } ++ } else { ++ if (Assembler::is_simm16(disp)) { ++ ld(src, base, disp); ++ } else { ++ lui(AT, split_low(disp >> 16)); ++ if (split_low(disp)) ori(AT, AT, split_low(disp)); ++ ++ if (UseLEXT1) { ++ gsldx(src, base, AT, 0); ++ } else { ++ daddu(AT, base, AT); ++ ld(src, AT, 0); ++ } ++ } ++ } ++} ++ ++void Assembler::ldl(Register rt, Address src){ ++ assert(src.index() == NOREG, "index is unimplemented"); ++ ldl(rt, src.base(), src.disp()); ++} ++ ++void Assembler::ldr(Register rt, Address src){ ++ assert(src.index() == NOREG, "index is unimplemented"); ++ ldr(rt, src.base(), src.disp()); ++} ++ ++void Assembler::lh(Register rt, Address src){ ++ assert(src.index() == NOREG, "index is unimplemented"); ++ lh(rt, src.base(), src.disp()); ++} ++ ++void Assembler::lhu(Register rt, Address src){ ++ assert(src.index() == NOREG, "index is unimplemented"); ++ lhu(rt, src.base(), src.disp()); ++} ++ ++void Assembler::ll(Register rt, Address src){ ++ assert(src.index() == NOREG, "index is unimplemented"); ++ ll(rt, src.base(), src.disp()); ++} ++ ++void Assembler::lld(Register rt, Address src){ ++ assert(src.index() == NOREG, "index is unimplemented"); ++ lld(rt, src.base(), src.disp()); ++} ++ ++void Assembler::lw(Register rt, Address dst){ ++ Register src = rt; ++ Register base = dst.base(); ++ Register index = dst.index(); ++ ++ int scale = dst.scale(); ++ int disp = dst.disp(); ++ ++ if (index != noreg) { ++ if (Assembler::is_simm16(disp)) { ++ if ( UseLEXT1 && Assembler::is_simm(disp, 8) ) { ++ if (scale == 0) { ++ gslwx(src, base, index, disp); ++ } else { ++ dsll(AT, index, scale); ++ gslwx(src, base, AT, disp); ++ } ++ } else { ++ if (scale == 0) { ++ daddu(AT, base, index); ++ } else { ++ dsll(AT, index, scale); ++ daddu(AT, base, AT); ++ } ++ lw(src, AT, disp); ++ } ++ } else { ++ if (scale == 0) { ++ lui(AT, split_low(disp >> 16)); ++ if (split_low(disp)) ori(AT, AT, split_low(disp)); ++ daddu(AT, AT, base); ++ if (UseLEXT1) { ++ gslwx(src, AT, index, 0); ++ } else { ++ daddu(AT, AT, index); ++ lw(src, AT, 0); ++ } ++ } else { ++ assert_different_registers(src, AT); ++ dsll(AT, index, scale); ++ daddu(AT, base, AT); ++ lui(src, split_low(disp >> 16)); ++ if (split_low(disp)) ori(src, src, split_low(disp)); ++ if (UseLEXT1) { ++ gslwx(src, AT, src, 0); ++ } else { ++ daddu(AT, AT, src); ++ lw(src, AT, 0); ++ } ++ } ++ } ++ } else { ++ if (Assembler::is_simm16(disp)) { ++ lw(src, base, disp); ++ } else { ++ lui(AT, split_low(disp >> 16)); ++ if (split_low(disp)) ori(AT, AT, split_low(disp)); ++ ++ if (UseLEXT1) { ++ gslwx(src, base, AT, 0); ++ } else { ++ daddu(AT, base, AT); ++ lw(src, AT, 0); ++ } ++ } ++ } ++} ++ ++void Assembler::lea(Register rt, Address src) { ++ Register dst = rt; ++ Register base = src.base(); ++ Register index = src.index(); ++ ++ int scale = src.scale(); ++ int disp = src.disp(); ++ ++ if (index == noreg) { ++ if (is_simm16(disp)) { ++ daddiu(dst, base, disp); ++ } else { ++ lui(AT, split_low(disp >> 16)); ++ if (split_low(disp)) ori(AT, AT, split_low(disp)); ++ daddu(dst, base, AT); ++ } ++ } else { ++ if (scale == 0) { ++ if (is_simm16(disp)) { ++ daddu(AT, base, index); ++ daddiu(dst, AT, disp); ++ } else { ++ lui(AT, split_low(disp >> 16)); ++ if (split_low(disp)) ori(AT, AT, split_low(disp)); ++ daddu(AT, base, AT); ++ daddu(dst, AT, index); ++ } ++ } else { ++ if (is_simm16(disp)) { ++ dsll(AT, index, scale); ++ daddu(AT, AT, base); ++ daddiu(dst, AT, disp); ++ } else { ++ assert_different_registers(dst, AT); ++ lui(AT, split_low(disp >> 16)); ++ if (split_low(disp)) ori(AT, AT, split_low(disp)); ++ daddu(AT, AT, base); ++ dsll(dst, index, scale); ++ daddu(dst, dst, AT); ++ } ++ } ++ } ++} ++ ++void Assembler::lwl(Register rt, Address src){ ++ assert(src.index() == NOREG, "index is unimplemented"); ++ lwl(rt, src.base(), src.disp()); ++} ++ ++void Assembler::lwr(Register rt, Address src){ ++ assert(src.index() == NOREG, "index is unimplemented"); ++ lwr(rt, src.base(), src.disp()); ++} ++ ++void Assembler::lwu(Register rt, Address src){ ++ assert(src.index() == NOREG, "index is unimplemented"); ++ lwu(rt, src.base(), src.disp()); ++} ++ ++void Assembler::sb(Register rt, Address dst) { ++ assert(dst.index() == NOREG, "index is unimplemented"); ++ sb(rt, dst.base(), dst.disp()); ++} ++ ++void Assembler::sc(Register rt, Address dst) { ++ assert(dst.index() == NOREG, "index is unimplemented"); ++ sc(rt, dst.base(), dst.disp()); ++} ++ ++void Assembler::scd(Register rt, Address dst) { ++ assert(dst.index() == NOREG, "index is unimplemented"); ++ scd(rt, dst.base(), dst.disp()); ++} ++ ++void Assembler::sd(Register rt, Address dst) { ++ Register src = rt; ++ Register base = dst.base(); ++ Register index = dst.index(); ++ ++ int scale = dst.scale(); ++ int disp = dst.disp(); ++ ++ if (index != noreg) { ++ if (is_simm16(disp)) { ++ if ( UseLEXT1 && is_simm(disp, 8)) { ++ if (scale == 0) { ++ gssdx(src, base, index, disp); ++ } else { ++ assert_different_registers(rt, AT); ++ dsll(AT, index, scale); ++ gssdx(src, base, AT, disp); ++ } ++ } else { ++ assert_different_registers(rt, AT); ++ if (scale == 0) { ++ daddu(AT, base, index); ++ } else { ++ dsll(AT, index, scale); ++ daddu(AT, base, AT); ++ } ++ sd(src, AT, disp); ++ } ++ } else { ++ assert_different_registers(rt, AT); ++ if (scale == 0) { ++ lui(AT, split_low(disp >> 16)); ++ if (split_low(disp)) ori(AT, AT, split_low(disp)); ++ daddu(AT, AT, base); ++ if (UseLEXT1) { ++ gssdx(src, AT, index, 0); ++ } else { ++ daddu(AT, AT, index); ++ sd(src, AT, 0); ++ } ++ } else { ++ daddiu(SP, SP, -wordSize); ++ sd(T9, SP, 0); ++ ++ dsll(AT, index, scale); ++ daddu(AT, base, AT); ++ lui(T9, split_low(disp >> 16)); ++ if (split_low(disp)) ori(T9, T9, split_low(disp)); ++ daddu(AT, AT, T9); ++ ld(T9, SP, 0); ++ daddiu(SP, SP, wordSize); ++ sd(src, AT, 0); ++ } ++ } ++ } else { ++ if (is_simm16(disp)) { ++ sd(src, base, disp); ++ } else { ++ assert_different_registers(rt, AT); ++ lui(AT, split_low(disp >> 16)); ++ if (split_low(disp)) ori(AT, AT, split_low(disp)); ++ ++ if (UseLEXT1) { ++ gssdx(src, base, AT, 0); ++ } else { ++ daddu(AT, base, AT); ++ sd(src, AT, 0); ++ } ++ } ++ } ++} ++ ++void Assembler::sdl(Register rt, Address dst) { ++ assert(dst.index() == NOREG, "index is unimplemented"); ++ sdl(rt, dst.base(), dst.disp()); ++} ++ ++void Assembler::sdr(Register rt, Address dst) { ++ assert(dst.index() == NOREG, "index is unimplemented"); ++ sdr(rt, dst.base(), dst.disp()); ++} ++ ++void Assembler::sh(Register rt, Address dst) { ++ assert(dst.index() == NOREG, "index is unimplemented"); ++ sh(rt, dst.base(), dst.disp()); ++} ++ ++void Assembler::sw(Register rt, Address dst) { ++ Register src = rt; ++ Register base = dst.base(); ++ Register index = dst.index(); ++ ++ int scale = dst.scale(); ++ int disp = dst.disp(); ++ ++ if (index != noreg) { ++ if ( Assembler::is_simm16(disp) ) { ++ if ( UseLEXT1 && Assembler::is_simm(disp, 8) ) { ++ if (scale == 0) { ++ gsswx(src, base, index, disp); ++ } else { ++ assert_different_registers(rt, AT); ++ dsll(AT, index, scale); ++ gsswx(src, base, AT, disp); ++ } ++ } else { ++ assert_different_registers(rt, AT); ++ if (scale == 0) { ++ daddu(AT, base, index); ++ } else { ++ dsll(AT, index, scale); ++ daddu(AT, base, AT); ++ } ++ sw(src, AT, disp); ++ } ++ } else { ++ assert_different_registers(rt, AT); ++ if (scale == 0) { ++ lui(AT, split_low(disp >> 16)); ++ if (split_low(disp)) ori(AT, AT, split_low(disp)); ++ daddu(AT, AT, base); ++ if (UseLEXT1) { ++ gsswx(src, AT, index, 0); ++ } else { ++ daddu(AT, AT, index); ++ sw(src, AT, 0); ++ } ++ } else { ++ daddiu(SP, SP, -wordSize); ++ sd(T9, SP, 0); ++ ++ dsll(AT, index, scale); ++ daddu(AT, base, AT); ++ lui(T9, split_low(disp >> 16)); ++ if (split_low(disp)) ori(T9, T9, split_low(disp)); ++ daddu(AT, AT, T9); ++ ld(T9, SP, 0); ++ daddiu(SP, SP, wordSize); ++ sw(src, AT, 0); ++ } ++ } ++ } else { ++ if (Assembler::is_simm16(disp)) { ++ sw(src, base, disp); ++ } else { ++ assert_different_registers(rt, AT); ++ lui(AT, split_low(disp >> 16)); ++ if (split_low(disp)) ori(AT, AT, split_low(disp)); ++ ++ if (UseLEXT1) { ++ gsswx(src, base, AT, 0); ++ } else { ++ daddu(AT, base, AT); ++ sw(src, AT, 0); ++ } ++ } ++ } ++} ++ ++void Assembler::swl(Register rt, Address dst) { ++ assert(dst.index() == NOREG, "index is unimplemented"); ++ swl(rt, dst.base(), dst.disp()); ++} ++ ++void Assembler::swr(Register rt, Address dst) { ++ assert(dst.index() == NOREG, "index is unimplemented"); ++ swr(rt, dst.base(), dst.disp()); ++} ++ ++void Assembler::lwc1(FloatRegister rt, Address src) { ++ assert(src.index() == NOREG, "index is unimplemented"); ++ lwc1(rt, src.base(), src.disp()); ++} ++ ++void Assembler::ldc1(FloatRegister rt, Address src) { ++ assert(src.index() == NOREG, "index is unimplemented"); ++ ldc1(rt, src.base(), src.disp()); ++} ++ ++void Assembler::swc1(FloatRegister rt, Address dst) { ++ assert(dst.index() == NOREG, "index is unimplemented"); ++ swc1(rt, dst.base(), dst.disp()); ++} ++ ++void Assembler::sdc1(FloatRegister rt, Address dst) { ++ assert(dst.index() == NOREG, "index is unimplemented"); ++ sdc1(rt, dst.base(), dst.disp()); ++} ++ ++void Assembler::j(address entry) { ++ int dest = ((intptr_t)entry & (intptr_t)0xfffffff)>>2; ++ emit_long((j_op<<26) | dest); ++ has_delay_slot(); ++} ++ ++void Assembler::jal(address entry) { ++ int dest = ((intptr_t)entry & (intptr_t)0xfffffff)>>2; ++ emit_long((jal_op<<26) | dest); ++ has_delay_slot(); ++} +diff --git a/hotspot/src/cpu/mips/vm/assembler_mips.hpp b/hotspot/src/cpu/mips/vm/assembler_mips.hpp +new file mode 100644 +index 0000000000..e46d0a8164 +--- /dev/null ++++ b/hotspot/src/cpu/mips/vm/assembler_mips.hpp +@@ -0,0 +1,1789 @@ ++/* ++ * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2015, 2022, Loongson Technology. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ * ++ */ ++ ++#ifndef CPU_MIPS_VM_ASSEMBLER_MIPS_HPP ++#define CPU_MIPS_VM_ASSEMBLER_MIPS_HPP ++ ++#include "asm/register.hpp" ++ ++class BiasedLockingCounters; ++ ++ ++// Note: A register location is represented via a Register, not ++// via an address for efficiency & simplicity reasons. ++ ++class ArrayAddress; ++ ++class Address VALUE_OBJ_CLASS_SPEC { ++ public: ++ enum ScaleFactor { ++ no_scale = -1, ++ times_1 = 0, ++ times_2 = 1, ++ times_4 = 2, ++ times_8 = 3, ++ times_ptr = times_8 ++ }; ++ static ScaleFactor times(int size) { ++ assert(size >= 1 && size <= 8 && is_power_of_2(size), "bad scale size"); ++ if (size == 8) return times_8; ++ if (size == 4) return times_4; ++ if (size == 2) return times_2; ++ return times_1; ++ } ++ ++ private: ++ Register _base; ++ Register _index; ++ ScaleFactor _scale; ++ int _disp; ++ RelocationHolder _rspec; ++ ++ // Easily misused constructors make them private ++ Address(address loc, RelocationHolder spec); ++ Address(int disp, address loc, relocInfo::relocType rtype); ++ Address(int disp, address loc, RelocationHolder spec); ++ ++ public: ++ ++ // creation ++ Address() ++ : _base(noreg), ++ _index(noreg), ++ _scale(no_scale), ++ _disp(0) { ++ } ++ ++ // No default displacement otherwise Register can be implicitly ++ // converted to 0(Register) which is quite a different animal. ++ ++ Address(Register base, int disp = 0) ++ : _base(base), ++ _index(noreg), ++ _scale(no_scale), ++ _disp(disp) { ++ assert_different_registers(_base, AT); ++ } ++ ++ Address(Register base, Register index, ScaleFactor scale, int disp = 0) ++ : _base (base), ++ _index(index), ++ _scale(scale), ++ _disp (disp) { ++ assert(!index->is_valid() == (scale == Address::no_scale), "inconsistent address"); ++ assert_different_registers(_base, _index, AT); ++ } ++ ++ // The following two overloads are used in connection with the ++ // ByteSize type (see sizes.hpp). They simplify the use of ++ // ByteSize'd arguments in assembly code. Note that their equivalent ++ // for the optimized build are the member functions with int disp ++ // argument since ByteSize is mapped to an int type in that case. ++ // ++ // Note: DO NOT introduce similar overloaded functions for WordSize ++ // arguments as in the optimized mode, both ByteSize and WordSize ++ // are mapped to the same type and thus the compiler cannot make a ++ // distinction anymore (=> compiler errors). ++ ++#ifdef ASSERT ++ Address(Register base, ByteSize disp) ++ : _base(base), ++ _index(noreg), ++ _scale(no_scale), ++ _disp(in_bytes(disp)) { ++ assert_different_registers(_base, AT); ++ } ++ ++ Address(Register base, Register index, ScaleFactor scale, ByteSize disp) ++ : _base(base), ++ _index(index), ++ _scale(scale), ++ _disp(in_bytes(disp)) { ++ assert(!index->is_valid() == (scale == Address::no_scale), "inconsistent address"); ++ assert_different_registers(_base, _index, AT); ++ } ++#endif // ASSERT ++ ++ // accessors ++ bool uses(Register reg) const { return _base == reg || _index == reg; } ++ Register base() const { return _base; } ++ Register index() const { return _index; } ++ ScaleFactor scale() const { return _scale; } ++ int disp() const { return _disp; } ++ ++ static Address make_array(ArrayAddress); ++ ++ friend class Assembler; ++ friend class MacroAssembler; ++ friend class LIR_Assembler; // base/index/scale/disp ++}; ++ ++// Calling convention ++class Argument VALUE_OBJ_CLASS_SPEC { ++ private: ++ int _number; ++ public: ++ enum { ++ n_register_parameters = 8, // 8 integer registers used to pass parameters ++ n_float_register_parameters = 8 // 8 float registers used to pass parameters ++ }; ++ ++ Argument(int number):_number(number){ } ++ Argument successor() {return Argument(number() + 1);} ++ ++ int number()const {return _number;} ++ bool is_Register()const {return _number < n_register_parameters;} ++ bool is_FloatRegister()const {return _number < n_float_register_parameters;} ++ ++ Register as_Register()const { ++ assert(is_Register(), "must be a register argument"); ++ return ::as_Register(RA0->encoding() + _number); ++ } ++ FloatRegister as_FloatRegister()const { ++ assert(is_FloatRegister(), "must be a float register argument"); ++ return ::as_FloatRegister(F12->encoding() + _number); ++ } ++ ++ Address as_caller_address()const {return Address(SP, (number() - n_register_parameters) * wordSize);} ++}; ++ ++// ++// AddressLiteral has been split out from Address because operands of this type ++// need to be treated specially on 32bit vs. 64bit platforms. By splitting it out ++// the few instructions that need to deal with address literals are unique and the ++// MacroAssembler does not have to implement every instruction in the Assembler ++// in order to search for address literals that may need special handling depending ++// on the instruction and the platform. As small step on the way to merging i486/amd64 ++// directories. ++// ++class AddressLiteral VALUE_OBJ_CLASS_SPEC { ++ friend class ArrayAddress; ++ RelocationHolder _rspec; ++ // Typically we use AddressLiterals we want to use their rval ++ // However in some situations we want the lval (effect address) of the item. ++ // We provide a special factory for making those lvals. ++ bool _is_lval; ++ ++ // If the target is far we'll need to load the ea of this to ++ // a register to reach it. Otherwise if near we can do rip ++ // relative addressing. ++ ++ address _target; ++ ++ protected: ++ // creation ++ AddressLiteral() ++ : _is_lval(false), ++ _target(NULL) ++ {} ++ ++ public: ++ ++ ++ AddressLiteral(address target, relocInfo::relocType rtype); ++ ++ AddressLiteral(address target, RelocationHolder const& rspec) ++ : _rspec(rspec), ++ _is_lval(false), ++ _target(target) ++ {} ++ // 32-bit complains about a multiple declaration for int*. ++ AddressLiteral(intptr_t* addr, relocInfo::relocType rtype = relocInfo::none) ++ : _target((address) addr), ++ _rspec(rspec_from_rtype(rtype, (address) addr)) {} ++ ++ AddressLiteral addr() { ++ AddressLiteral ret = *this; ++ ret._is_lval = true; ++ return ret; ++ } ++ ++ ++ private: ++ ++ address target() { return _target; } ++ bool is_lval() { return _is_lval; } ++ ++ relocInfo::relocType reloc() const { return _rspec.type(); } ++ const RelocationHolder& rspec() const { return _rspec; } ++ ++ friend class Assembler; ++ friend class MacroAssembler; ++ friend class Address; ++ friend class LIR_Assembler; ++ RelocationHolder rspec_from_rtype(relocInfo::relocType rtype, address addr) { ++ switch (rtype) { ++ case relocInfo::external_word_type: ++ return external_word_Relocation::spec(addr); ++ case relocInfo::internal_word_type: ++ return internal_word_Relocation::spec(addr); ++ case relocInfo::opt_virtual_call_type: ++ return opt_virtual_call_Relocation::spec(); ++ case relocInfo::static_call_type: ++ return static_call_Relocation::spec(); ++ case relocInfo::runtime_call_type: ++ return runtime_call_Relocation::spec(); ++ case relocInfo::poll_type: ++ case relocInfo::poll_return_type: ++ return Relocation::spec_simple(rtype); ++ case relocInfo::none: ++ case relocInfo::oop_type: ++ // Oops are a special case. Normally they would be their own section ++ // but in cases like icBuffer they are literals in the code stream that ++ // we don't have a section for. We use none so that we get a literal address ++ // which is always patchable. ++ return RelocationHolder(); ++ default: ++ ShouldNotReachHere(); ++ return RelocationHolder(); ++ } ++ } ++ ++}; ++ ++// Convience classes ++class RuntimeAddress: public AddressLiteral { ++ ++ public: ++ ++ RuntimeAddress(address target) : AddressLiteral(target, relocInfo::runtime_call_type) {} ++ ++}; ++ ++class OopAddress: public AddressLiteral { ++ ++ public: ++ ++ OopAddress(address target) : AddressLiteral(target, relocInfo::oop_type){} ++ ++}; ++ ++class ExternalAddress: public AddressLiteral { ++ ++ public: ++ ++ ExternalAddress(address target) : AddressLiteral(target, relocInfo::external_word_type){} ++ ++}; ++ ++class InternalAddress: public AddressLiteral { ++ ++ public: ++ ++ InternalAddress(address target) : AddressLiteral(target, relocInfo::internal_word_type) {} ++ ++}; ++ ++// x86 can do array addressing as a single operation since disp can be an absolute ++// address amd64 can't. We create a class that expresses the concept but does extra ++// magic on amd64 to get the final result ++ ++class ArrayAddress VALUE_OBJ_CLASS_SPEC { ++ private: ++ ++ AddressLiteral _base; ++ Address _index; ++ ++ public: ++ ++ ArrayAddress() {}; ++ ArrayAddress(AddressLiteral base, Address index): _base(base), _index(index) {}; ++ AddressLiteral base() { return _base; } ++ Address index() { return _index; } ++ ++}; ++ ++const int FPUStateSizeInWords = 512 / wordSize; ++ ++// The MIPS LOONGSON Assembler: Pure assembler doing NO optimizations on the instruction ++// level ; i.e., what you write is what you get. The Assembler is generating code into ++// a CodeBuffer. ++ ++class Assembler : public AbstractAssembler { ++ friend class AbstractAssembler; // for the non-virtual hack ++ friend class LIR_Assembler; // as_Address() ++ friend class StubGenerator; ++ ++ public: ++ enum Condition { ++ zero , ++ notZero , ++ equal , ++ notEqual , ++ less , ++ lessEqual , ++ greater , ++ greaterEqual , ++ below , ++ belowEqual , ++ above , ++ aboveEqual ++ }; ++ ++ static const int LogInstructionSize = 2; ++ static const int InstructionSize = 1 << LogInstructionSize; ++ ++ // opcode, highest 6 bits: bits[31...26] ++ enum ops { ++ special_op = 0x00, // special_ops ++ regimm_op = 0x01, // regimm_ops ++ j_op = 0x02, ++ jal_op = 0x03, ++ beq_op = 0x04, ++ bne_op = 0x05, ++ blez_op = 0x06, ++ bgtz_op = 0x07, ++ addiu_op = 0x09, ++ slti_op = 0x0a, ++ sltiu_op = 0x0b, ++ andi_op = 0x0c, ++ ori_op = 0x0d, ++ xori_op = 0x0e, ++ lui_op = 0x0f, ++ cop0_op = 0x10, // cop0_ops ++ cop1_op = 0x11, // cop1_ops ++ gs_cop2_op = 0x12, // gs_cop2_ops ++ cop1x_op = 0x13, // cop1x_ops ++ beql_op = 0x14, ++ bnel_op = 0x15, ++ blezl_op = 0x16, ++ bgtzl_op = 0x17, ++ daddiu_op = 0x19, ++ ldl_op = 0x1a, ++ ldr_op = 0x1b, ++ special2_op = 0x1c, // special2_ops ++ msa_op = 0x1e, // msa_ops ++ special3_op = 0x1f, // special3_ops ++ lb_op = 0x20, ++ lh_op = 0x21, ++ lwl_op = 0x22, ++ lw_op = 0x23, ++ lbu_op = 0x24, ++ lhu_op = 0x25, ++ lwr_op = 0x26, ++ lwu_op = 0x27, ++ sb_op = 0x28, ++ sh_op = 0x29, ++ swl_op = 0x2a, ++ sw_op = 0x2b, ++ sdl_op = 0x2c, ++ sdr_op = 0x2d, ++ swr_op = 0x2e, ++ cache_op = 0x2f, ++ ll_op = 0x30, ++ lwc1_op = 0x31, ++ gs_lwc2_op = 0x32, //gs_lwc2_ops ++ pref_op = 0x33, ++ lld_op = 0x34, ++ ldc1_op = 0x35, ++ gs_ldc2_op = 0x36, //gs_ldc2_ops ++ ld_op = 0x37, ++ sc_op = 0x38, ++ swc1_op = 0x39, ++ gs_swc2_op = 0x3a, //gs_swc2_ops ++ scd_op = 0x3c, ++ sdc1_op = 0x3d, ++ gs_sdc2_op = 0x3e, //gs_sdc2_ops ++ sd_op = 0x3f ++ }; ++ ++ static const char *ops_name[]; ++ ++ //special family, the opcode is in low 6 bits. ++ enum special_ops { ++ sll_op = 0x00, ++ movci_op = 0x01, ++ srl_op = 0x02, ++ sra_op = 0x03, ++ sllv_op = 0x04, ++ srlv_op = 0x06, ++ srav_op = 0x07, ++ jr_op = 0x08, ++ jalr_op = 0x09, ++ movz_op = 0x0a, ++ movn_op = 0x0b, ++ syscall_op = 0x0c, ++ break_op = 0x0d, ++ sync_op = 0x0f, ++ mfhi_op = 0x10, ++ mthi_op = 0x11, ++ mflo_op = 0x12, ++ mtlo_op = 0x13, ++ dsllv_op = 0x14, ++ dsrlv_op = 0x16, ++ dsrav_op = 0x17, ++ mult_op = 0x18, ++ multu_op = 0x19, ++ div_op = 0x1a, ++ divu_op = 0x1b, ++ dmult_op = 0x1c, ++ dmultu_op = 0x1d, ++ ddiv_op = 0x1e, ++ ddivu_op = 0x1f, ++ addu_op = 0x21, ++ subu_op = 0x23, ++ and_op = 0x24, ++ or_op = 0x25, ++ xor_op = 0x26, ++ nor_op = 0x27, ++ slt_op = 0x2a, ++ sltu_op = 0x2b, ++ daddu_op = 0x2d, ++ dsubu_op = 0x2f, ++ tge_op = 0x30, ++ tgeu_op = 0x31, ++ tlt_op = 0x32, ++ tltu_op = 0x33, ++ teq_op = 0x34, ++ tne_op = 0x36, ++ dsll_op = 0x38, ++ dsrl_op = 0x3a, ++ dsra_op = 0x3b, ++ dsll32_op = 0x3c, ++ dsrl32_op = 0x3e, ++ dsra32_op = 0x3f ++ }; ++ ++ static const char* special_name[]; ++ ++ //regimm family, the opcode is in rt[16...20], 5 bits ++ enum regimm_ops { ++ bltz_op = 0x00, ++ bgez_op = 0x01, ++ bltzl_op = 0x02, ++ bgezl_op = 0x03, ++ tgei_op = 0x08, ++ tgeiu_op = 0x09, ++ tlti_op = 0x0a, ++ tltiu_op = 0x0b, ++ teqi_op = 0x0c, ++ tnei_op = 0x0e, ++ bltzal_op = 0x10, ++ bgezal_op = 0x11, ++ bltzall_op = 0x12, ++ bgezall_op = 0x13, ++ bposge32_op = 0x1c, ++ bposge64_op = 0x1d, ++ synci_op = 0x1f, ++ }; ++ ++ static const char* regimm_name[]; ++ ++ //cop0 family, the ops is in bits[25...21], 5 bits ++ enum cop0_ops { ++ mfc0_op = 0x00, ++ dmfc0_op = 0x01, ++ // ++ mxgc0_op = 0x03, //MFGC0, DMFGC0, MTGC0 ++ mtc0_op = 0x04, ++ dmtc0_op = 0x05, ++ rdpgpr_op = 0x0a, ++ inter_op = 0x0b, ++ wrpgpr_op = 0x0c ++ }; ++ ++ //cop1 family, the ops is in bits[25...21], 5 bits ++ enum cop1_ops { ++ mfc1_op = 0x00, ++ dmfc1_op = 0x01, ++ cfc1_op = 0x02, ++ mfhc1_op = 0x03, ++ mtc1_op = 0x04, ++ dmtc1_op = 0x05, ++ ctc1_op = 0x06, ++ mthc1_op = 0x07, ++ bc1f_op = 0x08, ++ single_fmt = 0x10, ++ double_fmt = 0x11, ++ word_fmt = 0x14, ++ long_fmt = 0x15, ++ ps_fmt = 0x16 ++ }; ++ ++ ++ //2 bist (bits[17...16]) of bc1x instructions (cop1) ++ enum bc_ops { ++ bcf_op = 0x0, ++ bct_op = 0x1, ++ bcfl_op = 0x2, ++ bctl_op = 0x3, ++ }; ++ ++ // low 6 bits of c_x_fmt instructions (cop1) ++ enum c_conds { ++ f_cond = 0x30, ++ un_cond = 0x31, ++ eq_cond = 0x32, ++ ueq_cond = 0x33, ++ olt_cond = 0x34, ++ ult_cond = 0x35, ++ ole_cond = 0x36, ++ ule_cond = 0x37, ++ sf_cond = 0x38, ++ ngle_cond = 0x39, ++ seq_cond = 0x3a, ++ ngl_cond = 0x3b, ++ lt_cond = 0x3c, ++ nge_cond = 0x3d, ++ le_cond = 0x3e, ++ ngt_cond = 0x3f ++ }; ++ ++ // low 6 bits of cop1 instructions ++ enum float_ops { ++ fadd_op = 0x00, ++ fsub_op = 0x01, ++ fmul_op = 0x02, ++ fdiv_op = 0x03, ++ fsqrt_op = 0x04, ++ fabs_op = 0x05, ++ fmov_op = 0x06, ++ fneg_op = 0x07, ++ froundl_op = 0x08, ++ ftruncl_op = 0x09, ++ fceill_op = 0x0a, ++ ffloorl_op = 0x0b, ++ froundw_op = 0x0c, ++ ftruncw_op = 0x0d, ++ fceilw_op = 0x0e, ++ ffloorw_op = 0x0f, ++ movf_f_op = 0x11, ++ movt_f_op = 0x11, ++ movz_f_op = 0x12, ++ movn_f_op = 0x13, ++ frecip_op = 0x15, ++ frsqrt_op = 0x16, ++ fcvts_op = 0x20, ++ fcvtd_op = 0x21, ++ fcvtw_op = 0x24, ++ fcvtl_op = 0x25, ++ fcvtps_op = 0x26, ++ fcvtspl_op = 0x28, ++ fpll_op = 0x2c, ++ fplu_op = 0x2d, ++ fpul_op = 0x2e, ++ fpuu_op = 0x2f ++ }; ++ ++ static const char* cop1_name[]; ++ ++ //cop1x family, the opcode is in low 6 bits. ++ enum cop1x_ops { ++ lwxc1_op = 0x00, ++ ldxc1_op = 0x01, ++ luxc1_op = 0x05, ++ swxc1_op = 0x08, ++ sdxc1_op = 0x09, ++ suxc1_op = 0x0d, ++ prefx_op = 0x0f, ++ ++ alnv_ps_op = 0x1e, ++ madd_s_op = 0x20, ++ madd_d_op = 0x21, ++ madd_ps_op = 0x26, ++ msub_s_op = 0x28, ++ msub_d_op = 0x29, ++ msub_ps_op = 0x2e, ++ nmadd_s_op = 0x30, ++ nmadd_d_op = 0x31, ++ nmadd_ps_op = 0x36, ++ nmsub_s_op = 0x38, ++ nmsub_d_op = 0x39, ++ nmsub_ps_op = 0x3e ++ }; ++ ++ static const char* cop1x_name[]; ++ ++ //special2 family, the opcode is in low 6 bits. ++ enum special2_ops { ++ madd_op = 0x00, ++ maddu_op = 0x01, ++ mul_op = 0x02, ++ gs0x03_op = 0x03, ++ msub_op = 0x04, ++ msubu_op = 0x05, ++ gs0x06_op = 0x06, ++ gsemul2_op = 0x07, ++ gsemul3_op = 0x08, ++ gsemul4_op = 0x09, ++ gsemul5_op = 0x0a, ++ gsemul6_op = 0x0b, ++ gsemul7_op = 0x0c, ++ gsemul8_op = 0x0d, ++ gsemul9_op = 0x0e, ++ gsemul10_op = 0x0f, ++ gsmult_op = 0x10, ++ gsdmult_op = 0x11, ++ gsmultu_op = 0x12, ++ gsdmultu_op = 0x13, ++ gsdiv_op = 0x14, ++ gsddiv_op = 0x15, ++ gsdivu_op = 0x16, ++ gsddivu_op = 0x17, ++ gsmod_op = 0x1c, ++ gsdmod_op = 0x1d, ++ gsmodu_op = 0x1e, ++ gsdmodu_op = 0x1f, ++ clz_op = 0x20, ++ clo_op = 0x21, ++ xctx_op = 0x22, //ctz, cto, dctz, dcto, gsX ++ gsrxr_x_op = 0x23, //gsX ++ dclz_op = 0x24, ++ dclo_op = 0x25, ++ gsle_op = 0x26, ++ gsgt_op = 0x27, ++ gs86j_op = 0x28, ++ gsloop_op = 0x29, ++ gsaj_op = 0x2a, ++ gsldpc_op = 0x2b, ++ gs86set_op = 0x30, ++ gstm_op = 0x31, ++ gscvt_ld_op = 0x32, ++ gscvt_ud_op = 0x33, ++ gseflag_op = 0x34, ++ gscam_op = 0x35, ++ gstop_op = 0x36, ++ gssettag_op = 0x37, ++ gssdbbp_op = 0x38 ++ }; ++ ++ static const char* special2_name[]; ++ ++ // special3 family, the opcode is in low 6 bits. ++ enum special3_ops { ++ ext_op = 0x00, ++ dextm_op = 0x01, ++ dextu_op = 0x02, ++ dext_op = 0x03, ++ ins_op = 0x04, ++ dinsm_op = 0x05, ++ dinsu_op = 0x06, ++ dins_op = 0x07, ++ lxx_op = 0x0a, //lwx, lhx, lbux, ldx ++ insv_op = 0x0c, ++ dinsv_op = 0x0d, ++ ar1_op = 0x10, //MIPS DSP ++ cmp1_op = 0x11, //MIPS DSP ++ re1_op = 0x12, //MIPS DSP, re1_ops ++ sh1_op = 0x13, //MIPS DSP ++ ar2_op = 0x14, //MIPS DSP ++ cmp2_op = 0x15, //MIPS DSP ++ re2_op = 0x16, //MIPS DSP, re2_ops ++ sh2_op = 0x17, //MIPS DSP ++ ar3_op = 0x18, //MIPS DSP ++ bshfl_op = 0x20 //seb, seh ++ }; ++ ++ // re1_ops ++ enum re1_ops { ++ absq_s_qb_op = 0x01, ++ repl_qb_op = 0x02, ++ replv_qb_op = 0x03, ++ absq_s_ph_op = 0x09, ++ repl_ph_op = 0x0a, ++ replv_ph_op = 0x0b, ++ absq_s_w_op = 0x11, ++ bitrev_op = 0x1b ++ }; ++ ++ // re2_ops ++ enum re2_ops { ++ repl_ob_op = 0x02, ++ replv_ob_op = 0x03, ++ absq_s_qh_op = 0x09, ++ repl_qh_op = 0x0a, ++ replv_qh_op = 0x0b, ++ absq_s_pw_op = 0x11, ++ repl_pw_op = 0x12, ++ replv_pw_op = 0x13, ++ }; ++ ++ static const char* special3_name[]; ++ ++ // lwc2/gs_lwc2 family, the opcode is in low 6 bits. ++ enum gs_lwc2_ops { ++ gslble_op = 0x10, ++ gslbgt_op = 0x11, ++ gslhle_op = 0x12, ++ gslhgt_op = 0x13, ++ gslwle_op = 0x14, ++ gslwgt_op = 0x15, ++ gsldle_op = 0x16, ++ gsldgt_op = 0x17, ++ gslwlec1_op = 0x1c, ++ gslwgtc1_op = 0x1d, ++ gsldlec1_op = 0x1e, ++ gsldgtc1_op = 0x1f, ++ gslq_op = 0x20 ++ }; ++ ++ static const char* gs_lwc2_name[]; ++ ++ // ldc2/gs_ldc2 family, the opcode is in low 3 bits. ++ enum gs_ldc2_ops { ++ gslbx_op = 0x0, ++ gslhx_op = 0x1, ++ gslwx_op = 0x2, ++ gsldx_op = 0x3, ++ gslwxc1_op = 0x6, ++ gsldxc1_op = 0x7 ++ }; ++ ++ static const char* gs_ldc2_name[]; ++ ++ // swc2/gs_swc2 family, the opcode is in low 6 bits. ++ enum gs_swc2_ops { ++ gssble_op = 0x10, ++ gssbgt_op = 0x11, ++ gsshle_op = 0x12, ++ gsshgt_op = 0x13, ++ gsswle_op = 0x14, ++ gsswgt_op = 0x15, ++ gssdle_op = 0x16, ++ gssdgt_op = 0x17, ++ gsswlec1_op = 0x1c, ++ gsswgtc1_op = 0x1d, ++ gssdlec1_op = 0x1e, ++ gssdgtc1_op = 0x1f, ++ gssq_op = 0x20 ++ }; ++ ++ static const char* gs_swc2_name[]; ++ ++ // sdc2/gs_sdc2 family, the opcode is in low 3 bits. ++ enum gs_sdc2_ops { ++ gssbx_op = 0x0, ++ gsshx_op = 0x1, ++ gsswx_op = 0x2, ++ gssdx_op = 0x3, ++ gsswxc1_op = 0x6, ++ gssdxc1_op = 0x7 ++ }; ++ ++ static const char* gs_sdc2_name[]; ++ ++ enum WhichOperand { ++ // input to locate_operand, and format code for relocations ++ imm_operand = 0, // embedded 32-bit|64-bit immediate operand ++ disp32_operand = 1, // embedded 32-bit displacement or address ++ call32_operand = 2, // embedded 32-bit self-relative displacement ++ narrow_oop_operand = 3, // embedded 32-bit immediate narrow oop ++ _WhichOperand_limit = 4 ++ }; ++ ++ static int opcode(int insn) { return (insn>>26)&0x3f; } ++ static int rs(int insn) { return (insn>>21)&0x1f; } ++ static int rt(int insn) { return (insn>>16)&0x1f; } ++ static int rd(int insn) { return (insn>>11)&0x1f; } ++ static int sa(int insn) { return (insn>>6)&0x1f; } ++ static int special(int insn) { return insn&0x3f; } ++ static int imm_off(int insn) { return (short)low16(insn); } ++ ++ static int low (int x, int l) { return bitfield(x, 0, l); } ++ static int low16(int x) { return low(x, 16); } ++ static int low26(int x) { return low(x, 26); } ++ ++ protected: ++ //help methods for instruction ejection ++ ++ // I-Type (Immediate) ++ // 31 26 25 21 20 16 15 0 ++ //| opcode | rs | rt | immediat | ++ //| | | | | ++ // 6 5 5 16 ++ static int insn_ORRI(int op, int rs, int rt, int imm) { assert(is_simm16(imm), "not a signed 16-bit int"); return (op<<26) | (rs<<21) | (rt<<16) | low16(imm); } ++ ++ // R-Type (Register) ++ // 31 26 25 21 20 16 15 11 10 6 5 0 ++ //| special | rs | rt | rd | 0 | opcode | ++ //| 0 0 0 0 0 0 | | | | 0 0 0 0 0 | | ++ // 6 5 5 5 5 6 ++ static int insn_RRRO(int rs, int rt, int rd, int op) { return (rs<<21) | (rt<<16) | (rd<<11) | op; } ++ static int insn_RRSO(int rt, int rd, int sa, int op) { return (rt<<16) | (rd<<11) | (sa<<6) | op; } ++ static int insn_RRCO(int rs, int rt, int code, int op) { return (rs<<21) | (rt<<16) | (code<<6) | op; } ++ ++ static int insn_COP0(int op, int rt, int rd) { return (cop0_op<<26) | (op<<21) | (rt<<16) | (rd<<11); } ++ static int insn_COP1(int op, int rt, int fs) { return (cop1_op<<26) | (op<<21) | (rt<<16) | (fs<<11); } ++ ++ static int insn_F3RO(int fmt, int ft, int fs, int fd, int func) { ++ return (cop1_op<<26) | (fmt<<21) | (ft<<16) | (fs<<11) | (fd<<6) | func; ++ } ++ static int insn_F3ROX(int fmt, int ft, int fs, int fd, int func) { ++ return (cop1x_op<<26) | (fmt<<21) | (ft<<16) | (fs<<11) | (fd<<6) | func; ++ } ++ ++ static int high (int x, int l) { return bitfield(x, 32-l, l); } ++ static int high16(int x) { return high(x, 16); } ++ static int high6 (int x) { return high(x, 6); } ++ ++ //get the offset field of jump/branch instruction ++ int offset(address entry) { ++ assert(is_simm16((entry - pc() - 4) / 4), "change this code"); ++ if (!is_simm16((entry - pc() - 4) / 4)) { ++ tty->print_cr("!!! is_simm16: %lx", (entry - pc() - 4) / 4); ++ } ++ return (entry - pc() - 4) / 4; ++ } ++ ++ ++public: ++ using AbstractAssembler::offset; ++ ++ //sign expand with the sign bit is h ++ static int expand(int x, int h) { return -(x & (1<> 16; ++ } ++ ++ static int split_high(int x) { ++ return ( (x >> 16) + ((x & 0x8000) != 0) ) & 0xffff; ++ } ++ ++ static int merge(int low, int high) { ++ return expand(low, 15) + (high<<16); ++ } ++ ++ static intptr_t merge(intptr_t x0, intptr_t x16, intptr_t x32, intptr_t x48) { ++ return (x48 << 48) | (x32 << 32) | (x16 << 16) | x0; ++ } ++ ++ // Test if x is within signed immediate range for nbits. ++ static bool is_simm (int x, int nbits) { ++ assert(0 < nbits && nbits < 32, "out of bounds"); ++ const int min = -( ((int)1) << nbits-1 ); ++ const int maxplus1 = ( ((int)1) << nbits-1 ); ++ return min <= x && x < maxplus1; ++ } ++ ++ static bool is_simm(jlong x, unsigned int nbits) { ++ assert(0 < nbits && nbits < 64, "out of bounds"); ++ const jlong min = -( ((jlong)1) << nbits-1 ); ++ const jlong maxplus1 = ( ((jlong)1) << nbits-1 ); ++ return min <= x && x < maxplus1; ++ } ++ ++ // Test if x is within unsigned immediate range for nbits ++ static bool is_uimm(int x, unsigned int nbits) { ++ assert(0 < nbits && nbits < 32, "out of bounds"); ++ const int maxplus1 = ( ((int)1) << nbits ); ++ return 0 <= x && x < maxplus1; ++ } ++ ++ static bool is_uimm(jlong x, unsigned int nbits) { ++ assert(0 < nbits && nbits < 64, "out of bounds"); ++ const jlong maxplus1 = ( ((jlong)1) << nbits ); ++ return 0 <= x && x < maxplus1; ++ } ++ ++ static bool is_simm16(int x) { return is_simm(x, 16); } ++ static bool is_simm16(long x) { return is_simm((jlong)x, (unsigned int)16); } ++ ++ static bool fit_in_jal(address target, address pc) { ++ intptr_t mask = 0xfffffffff0000000; ++ return ((intptr_t)(pc + 4) & mask) == ((intptr_t)target & mask); ++ } ++ ++ bool fit_int_branch(address entry) { ++ return is_simm16(offset(entry)); ++ } ++ ++protected: ++#ifdef ASSERT ++ #define CHECK_DELAY ++#endif ++#ifdef CHECK_DELAY ++ enum Delay_state { no_delay, at_delay_slot, filling_delay_slot } delay_state; ++#endif ++ ++public: ++ void assert_not_delayed() { ++#ifdef CHECK_DELAY ++ assert_not_delayed("next instruction should not be a delay slot"); ++#endif ++ } ++ ++ void assert_not_delayed(const char* msg) { ++#ifdef CHECK_DELAY ++ assert(delay_state == no_delay, msg); ++#endif ++ } ++ ++protected: ++ // Delay slot helpers ++ // cti is called when emitting control-transfer instruction, ++ // BEFORE doing the emitting. ++ // Only effective when assertion-checking is enabled. ++ ++ // called when emitting cti with a delay slot, AFTER emitting ++ void has_delay_slot() { ++#ifdef CHECK_DELAY ++ assert_not_delayed("just checking"); ++ delay_state = at_delay_slot; ++#endif ++ } ++ ++public: ++ Assembler* delayed() { ++#ifdef CHECK_DELAY ++ guarantee( delay_state == at_delay_slot, "delayed instructition is not in delay slot"); ++ delay_state = filling_delay_slot; ++#endif ++ return this; ++ } ++ ++ void flush() { ++#ifdef CHECK_DELAY ++ guarantee( delay_state == no_delay, "ending code with a delay slot"); ++#endif ++ AbstractAssembler::flush(); ++ } ++ ++ inline void emit_long(int); // shadows AbstractAssembler::emit_long ++ inline void emit_data(int x) { emit_long(x); } ++ inline void emit_data(int, RelocationHolder const&); ++ inline void emit_data(int, relocInfo::relocType rtype); ++ inline void check_delay(); ++ ++ ++ // Generic instructions ++ // Does 32bit or 64bit as needed for the platform. In some sense these ++ // belong in macro assembler but there is no need for both varieties to exist ++ ++ void addu32(Register rd, Register rs, Register rt){ emit_long(insn_RRRO((int)rs->encoding(), (int)rt->encoding(), (int)rd->encoding(), addu_op)); } ++ void addiu32(Register rt, Register rs, int imm) { emit_long(insn_ORRI(addiu_op, (int)rs->encoding(), (int)rt->encoding(), imm)); } ++ void addiu(Register rt, Register rs, int imm) { daddiu (rt, rs, imm);} ++ void addu(Register rd, Register rs, Register rt) { daddu (rd, rs, rt); } ++ ++ void andr(Register rd, Register rs, Register rt) { emit_long(insn_RRRO((int)rs->encoding(), (int)rt->encoding(), (int)rd->encoding(), and_op)); } ++ void andi(Register rt, Register rs, int imm) { emit_long(insn_ORRI(andi_op, (int)rs->encoding(), (int)rt->encoding(), simm16(imm))); } ++ ++ void beq (Register rs, Register rt, int off) { emit_long(insn_ORRI(beq_op, (int)rs->encoding(), (int)rt->encoding(), off)); has_delay_slot(); } ++ void beql (Register rs, Register rt, int off) { emit_long(insn_ORRI(beql_op, (int)rs->encoding(), (int)rt->encoding(), off)); has_delay_slot(); } ++ void bgez (Register rs, int off) { emit_long(insn_ORRI(regimm_op, (int)rs->encoding(), bgez_op, off)); has_delay_slot(); } ++ void bgezal (Register rs, int off) { emit_long(insn_ORRI(regimm_op, (int)rs->encoding(), bgezal_op, off)); has_delay_slot(); } ++ void bgezall(Register rs, int off) { emit_long(insn_ORRI(regimm_op, (int)rs->encoding(), bgezall_op, off)); has_delay_slot(); } ++ void bgezl (Register rs, int off) { emit_long(insn_ORRI(regimm_op, (int)rs->encoding(), bgezl_op, off)); has_delay_slot(); } ++ void bgtz (Register rs, int off) { emit_long(insn_ORRI(bgtz_op, (int)rs->encoding(), 0, off)); has_delay_slot(); } ++ void bgtzl (Register rs, int off) { emit_long(insn_ORRI(bgtzl_op, (int)rs->encoding(), 0, off)); has_delay_slot(); } ++ void blez (Register rs, int off) { emit_long(insn_ORRI(blez_op, (int)rs->encoding(), 0, off)); has_delay_slot(); } ++ void blezl (Register rs, int off) { emit_long(insn_ORRI(blezl_op, (int)rs->encoding(), 0, off)); has_delay_slot(); } ++ void bltz (Register rs, int off) { emit_long(insn_ORRI(regimm_op, (int)rs->encoding(), bltz_op, off)); has_delay_slot(); } ++ void bltzal (Register rs, int off) { emit_long(insn_ORRI(regimm_op, (int)rs->encoding(), bltzal_op, off)); has_delay_slot(); } ++ void bltzall(Register rs, int off) { emit_long(insn_ORRI(regimm_op, (int)rs->encoding(), bltzall_op, off)); has_delay_slot(); } ++ void bltzl (Register rs, int off) { emit_long(insn_ORRI(regimm_op, (int)rs->encoding(), bltzl_op, off)); has_delay_slot(); } ++ void bne (Register rs, Register rt, int off) { emit_long(insn_ORRI(bne_op, (int)rs->encoding(), (int)rt->encoding(), off)); has_delay_slot(); } ++ void bnel (Register rs, Register rt, int off) { emit_long(insn_ORRI(bnel_op, (int)rs->encoding(), (int)rt->encoding(), off)); has_delay_slot(); } ++ // two versions of brk: ++ // the brk(code) version is according to MIPS64 Architecture For Programmers Volume II: The MIPS64 Instruction Set ++ // the brk(code1, code2) is according to disassembler of hsdis (binutils-2.27) ++ // both versions work ++ void brk (int code) { assert(is_uimm(code, 20), "code is 20 bits"); emit_long( (low(code, 20)<<6) | break_op ); } ++ void brk (int code1, int code2) { assert(is_uimm(code1, 10) && is_uimm(code2, 10), "code is 20 bits"); emit_long( (low(code1, 10)<<16) | (low(code2, 10)<<6) | break_op ); } ++ ++ void beq (Register rs, Register rt, address entry) { beq(rs, rt, offset(entry)); } ++ void beql (Register rs, Register rt, address entry) { beql(rs, rt, offset(entry));} ++ void bgez (Register rs, address entry) { bgez (rs, offset(entry)); } ++ void bgezal (Register rs, address entry) { bgezal (rs, offset(entry)); } ++ void bgezall(Register rs, address entry) { bgezall(rs, offset(entry)); } ++ void bgezl (Register rs, address entry) { bgezl (rs, offset(entry)); } ++ void bgtz (Register rs, address entry) { bgtz (rs, offset(entry)); } ++ void bgtzl (Register rs, address entry) { bgtzl (rs, offset(entry)); } ++ void blez (Register rs, address entry) { blez (rs, offset(entry)); } ++ void blezl (Register rs, address entry) { blezl (rs, offset(entry)); } ++ void bltz (Register rs, address entry) { bltz (rs, offset(entry)); } ++ void bltzal (Register rs, address entry) { bltzal (rs, offset(entry)); } ++ void bltzall(Register rs, address entry) { bltzall(rs, offset(entry)); } ++ void bltzl (Register rs, address entry) { bltzl (rs, offset(entry)); } ++ void bne (Register rs, Register rt, address entry) { bne(rs, rt, offset(entry)); } ++ void bnel (Register rs, Register rt, address entry) { bnel(rs, rt, offset(entry)); } ++ ++ void beq (Register rs, Register rt, Label& L) { beq(rs, rt, target(L)); } ++ void beql (Register rs, Register rt, Label& L) { beql(rs, rt, target(L)); } ++ void bgez (Register rs, Label& L){ bgez (rs, target(L)); } ++ void bgezal (Register rs, Label& L){ bgezal (rs, target(L)); } ++ void bgezall(Register rs, Label& L){ bgezall(rs, target(L)); } ++ void bgezl (Register rs, Label& L){ bgezl (rs, target(L)); } ++ void bgtz (Register rs, Label& L){ bgtz (rs, target(L)); } ++ void bgtzl (Register rs, Label& L){ bgtzl (rs, target(L)); } ++ void blez (Register rs, Label& L){ blez (rs, target(L)); } ++ void blezl (Register rs, Label& L){ blezl (rs, target(L)); } ++ void bltz (Register rs, Label& L){ bltz (rs, target(L)); } ++ void bltzal (Register rs, Label& L){ bltzal (rs, target(L)); } ++ void bltzall(Register rs, Label& L){ bltzall(rs, target(L)); } ++ void bltzl (Register rs, Label& L){ bltzl (rs, target(L)); } ++ void bne (Register rs, Register rt, Label& L){ bne(rs, rt, target(L)); } ++ void bnel (Register rs, Register rt, Label& L){ bnel(rs, rt, target(L)); } ++ ++ void daddiu(Register rt, Register rs, int imm) { emit_long(insn_ORRI(daddiu_op, (int)rs->encoding(), (int)rt->encoding(), imm)); } ++ void daddu (Register rd, Register rs, Register rt) { emit_long(insn_RRRO((int)rs->encoding(), (int)rt->encoding(), (int)rd->encoding(), daddu_op)); } ++ void ddiv (Register rs, Register rt) { emit_long(insn_RRRO((int)rs->encoding(), (int)rt->encoding(), 0, ddiv_op)); } ++ void ddivu (Register rs, Register rt) { emit_long(insn_RRRO((int)rs->encoding(), (int)rt->encoding(), 0, ddivu_op)); } ++ ++ void movz (Register rd, Register rs, Register rt) { emit_long(insn_RRRO((int)rs->encoding(), (int)rt->encoding(), (int)rd->encoding(), movz_op)); } ++ void movn (Register rd, Register rs, Register rt) { emit_long(insn_RRRO((int)rs->encoding(), (int)rt->encoding(), (int)rd->encoding(), movn_op)); } ++ ++ void movt (Register rd, Register rs) { emit_long(((int)rs->encoding() << 21) | (1 << 16) | ((int)rd->encoding() << 11) | movci_op); } ++ void movf (Register rd, Register rs) { emit_long(((int)rs->encoding() << 21) | ((int)rd->encoding() << 11) | movci_op); } ++ ++ enum bshfl_ops { ++ seb_op = 0x10, ++ seh_op = 0x18 ++ }; ++ void seb (Register rd, Register rt) { emit_long((special3_op << 26) | ((int)rt->encoding() << 16) | ((int)rd->encoding() << 11) | (seb_op << 6) | bshfl_op); } ++ void seh (Register rd, Register rt) { emit_long((special3_op << 26) | ((int)rt->encoding() << 16) | ((int)rd->encoding() << 11) | (seh_op << 6) | bshfl_op); } ++ ++ void ext (Register rt, Register rs, int pos, int size) { ++ guarantee((0 <= pos) && (pos < 32), "pos must be in [0, 32)"); ++ guarantee((0 < size) && (size <= 32), "size must be in (0, 32]"); ++ guarantee((0 < pos + size) && (pos + size <= 32), "pos + size must be in (0, 32]"); ++ ++ int lsb = pos; ++ int msbd = size - 1; ++ ++ emit_long((special3_op << 26) | ((int)rs->encoding() << 21) | ((int)rt->encoding() << 16) | (msbd << 11) | (lsb << 6) | ext_op); ++ } ++ ++ void dext (Register rt, Register rs, int pos, int size) { ++ guarantee((0 <= pos) && (pos < 32), "pos must be in [0, 32)"); ++ guarantee((0 < size) && (size <= 32), "size must be in (0, 32]"); ++ guarantee((0 < pos + size) && (pos + size <= 63), "pos + size must be in (0, 63]"); ++ ++ int lsb = pos; ++ int msbd = size - 1; ++ ++ emit_long((special3_op << 26) | ((int)rs->encoding() << 21) | ((int)rt->encoding() << 16) | (msbd << 11) | (lsb << 6) | dext_op); ++ } ++ ++ void dextm (Register rt, Register rs, int pos, int size) { ++ guarantee((0 <= pos) && (pos < 32), "pos must be in [0, 32)"); ++ guarantee((32 < size) && (size <= 64), "size must be in (32, 64]"); ++ guarantee((32 < pos + size) && (pos + size <= 64), "pos + size must be in (32, 64]"); ++ ++ int lsb = pos; ++ int msbd = size - 1 - 32; ++ ++ emit_long((special3_op << 26) | ((int)rs->encoding() << 21) | ((int)rt->encoding() << 16) | (msbd << 11) | (lsb << 6) | dextm_op); ++ } ++ ++ void rotr (Register rd, Register rt, int sa) { ++ emit_long((special_op << 26) | (1 << 21) | ((int)rt->encoding() << 16) | ((int)rd->encoding() << 11) | (low(sa, 5) << 6) | srl_op); ++ } ++ ++ void drotr (Register rd, Register rt, int sa) { ++ emit_long((special_op << 26) | (1 << 21) | ((int)rt->encoding() << 16) | ((int)rd->encoding() << 11) | (low(sa, 5) << 6) | dsrl_op); ++ } ++ ++ void drotr32 (Register rd, Register rt, int sa) { ++ emit_long((special_op << 26) | (1 << 21) | ((int)rt->encoding() << 16) | ((int)rd->encoding() << 11) | (low(sa, 5) << 6) | dsrl32_op); ++ } ++ ++ void rotrv (Register rd, Register rt, Register rs) { ++ emit_long((special_op << 26) | ((int)rs->encoding() << 21) | ((int)rt->encoding() << 16) | ((int)rd->encoding() << 11) | (1 << 6) | srlv_op); ++ } ++ ++ void drotrv (Register rd, Register rt, Register rs) { ++ emit_long((special_op << 26) | ((int)rs->encoding() << 21) | ((int)rt->encoding() << 16) | ((int)rd->encoding() << 11) | (1 << 6) | dsrlv_op); ++ } ++ ++ void div (Register rs, Register rt) { emit_long(insn_RRRO((int)rs->encoding(), (int)rt->encoding(), 0, div_op)); } ++ void divu (Register rs, Register rt) { emit_long(insn_RRRO((int)rs->encoding(), (int)rt->encoding(), 0, divu_op)); } ++ void dmult (Register rs, Register rt) { emit_long(insn_RRRO((int)rs->encoding(), (int)rt->encoding(), 0, dmult_op)); } ++ void dmultu(Register rs, Register rt) { emit_long(insn_RRRO((int)rs->encoding(), (int)rt->encoding(), 0, dmultu_op)); } ++ void dsll (Register rd, Register rt , int sa) { emit_long(insn_RRSO((int)rt->encoding(), (int)rd->encoding(), low(sa, 5), dsll_op)); } ++ void dsllv (Register rd, Register rt, Register rs) { emit_long(insn_RRRO((int)rs->encoding(), (int)rt->encoding(), (int)rd->encoding(), dsllv_op)); } ++ void dsll32(Register rd, Register rt , int sa) { emit_long(insn_RRSO((int)rt->encoding(), (int)rd->encoding(), low(sa, 5), dsll32_op)); } ++ void dsra (Register rd, Register rt , int sa) { emit_long(insn_RRSO((int)rt->encoding(), (int)rd->encoding(), low(sa, 5), dsra_op)); } ++ void dsrav (Register rd, Register rt, Register rs) { emit_long(insn_RRRO((int)rs->encoding(), (int)rt->encoding(), (int)rd->encoding(), dsrav_op)); } ++ void dsra32(Register rd, Register rt , int sa) { emit_long(insn_RRSO((int)rt->encoding(), (int)rd->encoding(), low(sa, 5), dsra32_op)); } ++ void dsrl (Register rd, Register rt , int sa) { emit_long(insn_RRSO((int)rt->encoding(), (int)rd->encoding(), low(sa, 5), dsrl_op)); } ++ void dsrlv (Register rd, Register rt, Register rs) { emit_long(insn_RRRO((int)rs->encoding(), (int)rt->encoding(), (int)rd->encoding(), dsrlv_op)); } ++ void dsrl32(Register rd, Register rt , int sa) { emit_long(insn_RRSO((int)rt->encoding(), (int)rd->encoding(), low(sa, 5), dsrl32_op)); } ++ void dsubu (Register rd, Register rs, Register rt) { emit_long(insn_RRRO((int)rs->encoding(), (int)rt->encoding(), (int)rd->encoding(), dsubu_op)); } ++ ++ void b(int off) { beq(R0, R0, off); } ++ void b(address entry) { b(offset(entry)); } ++ void b(Label& L) { b(target(L)); } ++ ++ void j(address entry); ++ void jal(address entry); ++ ++ void jalr(Register rd, Register rs) { emit_long( ((int)rs->encoding()<<21) | ((int)rd->encoding()<<11) | jalr_op); has_delay_slot(); } ++ void jalr(Register rs) { jalr(RA, rs); } ++ void jalr() { jalr(RT9); } ++ ++ void jr(Register rs) { emit_long(((int)rs->encoding()<<21) | jr_op); has_delay_slot(); } ++ void jr_hb(Register rs) { emit_long(((int)rs->encoding()<<21) | (1 << 10) | jr_op); has_delay_slot(); } ++ ++ void lb (Register rt, Register base, int off) { emit_long(insn_ORRI(lb_op, (int)base->encoding(), (int)rt->encoding(), off)); } ++ void lbu(Register rt, Register base, int off) { emit_long(insn_ORRI(lbu_op, (int)base->encoding(), (int)rt->encoding(), off)); } ++ void ld (Register rt, Register base, int off) { emit_long(insn_ORRI(ld_op, (int)base->encoding(), (int)rt->encoding(), off)); } ++ void ldl(Register rt, Register base, int off) { emit_long(insn_ORRI(ldl_op, (int)base->encoding(), (int)rt->encoding(), off)); } ++ void ldr(Register rt, Register base, int off) { emit_long(insn_ORRI(ldr_op, (int)base->encoding(), (int)rt->encoding(), off)); } ++ void lh (Register rt, Register base, int off) { emit_long(insn_ORRI(lh_op, (int)base->encoding(), (int)rt->encoding(), off)); } ++ void lhu(Register rt, Register base, int off) { emit_long(insn_ORRI(lhu_op, (int)base->encoding(), (int)rt->encoding(), off)); } ++ void ll (Register rt, Register base, int off) { emit_long(insn_ORRI(ll_op, (int)base->encoding(), (int)rt->encoding(), off)); } ++ void lld(Register rt, Register base, int off) { emit_long(insn_ORRI(lld_op, (int)base->encoding(), (int)rt->encoding(), off)); } ++ void lui(Register rt, int imm) { emit_long(insn_ORRI(lui_op, 0, (int)rt->encoding(), simm16(imm))); } ++ void lw (Register rt, Register base, int off) { emit_long(insn_ORRI(lw_op, (int)base->encoding(), (int)rt->encoding(), off)); } ++ void lwl(Register rt, Register base, int off) { emit_long(insn_ORRI(lwl_op, (int)base->encoding(), (int)rt->encoding(), off)); } ++ void lwr(Register rt, Register base, int off) { emit_long(insn_ORRI(lwr_op, (int)base->encoding(), (int)rt->encoding(), off)); } ++ void lwu(Register rt, Register base, int off) { emit_long(insn_ORRI(lwu_op, (int)base->encoding(), (int)rt->encoding(), off)); } ++ ++ void lb (Register rt, Address src); ++ void lbu(Register rt, Address src); ++ void ld (Register rt, Address src); ++ void ldl(Register rt, Address src); ++ void ldr(Register rt, Address src); ++ void lh (Register rt, Address src); ++ void lhu(Register rt, Address src); ++ void ll (Register rt, Address src); ++ void lld(Register rt, Address src); ++ void lw (Register rt, Address src); ++ void lwl(Register rt, Address src); ++ void lwr(Register rt, Address src); ++ void lwu(Register rt, Address src); ++ void lea(Register rt, Address src); ++ void pref(int hint, Register base, int off) { emit_long(insn_ORRI(pref_op, (int)base->encoding(), low(hint, 5), low(off, 16))); } ++ ++ void mfhi (Register rd) { emit_long( ((int)rd->encoding()<<11) | mfhi_op ); } ++ void mflo (Register rd) { emit_long( ((int)rd->encoding()<<11) | mflo_op ); } ++ void mthi (Register rs) { emit_long( ((int)rs->encoding()<<21) | mthi_op ); } ++ void mtlo (Register rs) { emit_long( ((int)rs->encoding()<<21) | mtlo_op ); } ++ ++ void mult (Register rs, Register rt) { emit_long(insn_RRRO((int)rs->encoding(), (int)rt->encoding(), 0, mult_op)); } ++ void multu(Register rs, Register rt) { emit_long(insn_RRRO((int)rs->encoding(), (int)rt->encoding(), 0, multu_op)); } ++ ++ void nor(Register rd, Register rs, Register rt) { emit_long(insn_RRRO((int)rs->encoding(), (int)rt->encoding(), (int)rd->encoding(), nor_op)); } ++ ++ void orr(Register rd, Register rs, Register rt) { emit_long(insn_RRRO((int)rs->encoding(), (int)rt->encoding(), (int)rd->encoding(), or_op)); } ++ void ori(Register rt, Register rs, int imm) { emit_long(insn_ORRI(ori_op, (int)rs->encoding(), (int)rt->encoding(), simm16(imm))); } ++ ++ void sb (Register rt, Register base, int off) { emit_long(insn_ORRI(sb_op, (int)base->encoding(), (int)rt->encoding(), off)); } ++ void sc (Register rt, Register base, int off) { emit_long(insn_ORRI(sc_op, (int)base->encoding(), (int)rt->encoding(), off)); } ++ void scd (Register rt, Register base, int off) { emit_long(insn_ORRI(scd_op, (int)base->encoding(), (int)rt->encoding(), off)); } ++ void sd (Register rt, Register base, int off) { emit_long(insn_ORRI(sd_op, (int)base->encoding(), (int)rt->encoding(), off)); } ++ void sdl (Register rt, Register base, int off) { emit_long(insn_ORRI(sdl_op, (int)base->encoding(), (int)rt->encoding(), off)); } ++ void sdr (Register rt, Register base, int off) { emit_long(insn_ORRI(sdr_op, (int)base->encoding(), (int)rt->encoding(), off)); } ++ void sh (Register rt, Register base, int off) { emit_long(insn_ORRI(sh_op, (int)base->encoding(), (int)rt->encoding(), off)); } ++ void sll (Register rd, Register rt , int sa) { emit_long(insn_RRSO((int)rt->encoding(), (int)rd->encoding(), low(sa, 5), sll_op)); } ++ void sllv (Register rd, Register rt, Register rs) { emit_long(insn_RRRO((int)rs->encoding(), (int)rt->encoding(), (int)rd->encoding(), sllv_op)); } ++ void slt (Register rd, Register rs, Register rt) { emit_long(insn_RRRO((int)rs->encoding(), (int)rt->encoding(), (int)rd->encoding(), slt_op)); } ++ void slti (Register rt, Register rs, int imm) { emit_long(insn_ORRI(slti_op, (int)rs->encoding(), (int)rt->encoding(), imm)); } ++ void sltiu(Register rt, Register rs, int imm) { emit_long(insn_ORRI(sltiu_op, (int)rs->encoding(), (int)rt->encoding(), imm)); } ++ void sltu (Register rd, Register rs, Register rt) { emit_long(insn_RRRO((int)rs->encoding(), (int)rt->encoding(), (int)rd->encoding(), sltu_op)); } ++ void sra (Register rd, Register rt , int sa) { emit_long(insn_RRSO((int)rt->encoding(), (int)rd->encoding(), low(sa, 5), sra_op)); } ++ void srav (Register rd, Register rt, Register rs) { emit_long(insn_RRRO((int)rs->encoding(), (int)rt->encoding(), (int)rd->encoding(), srav_op)); } ++ void srl (Register rd, Register rt , int sa) { emit_long(insn_RRSO((int)rt->encoding(), (int)rd->encoding(), low(sa, 5), srl_op)); } ++ void srlv (Register rd, Register rt, Register rs) { emit_long(insn_RRRO((int)rs->encoding(), (int)rt->encoding(), (int)rd->encoding(), srlv_op)); } ++ ++ void subu (Register rd, Register rs, Register rt) { dsubu (rd, rs, rt); } ++ void subu32 (Register rd, Register rs, Register rt) { emit_long(insn_RRRO((int)rs->encoding(), (int)rt->encoding(), (int)rd->encoding(), subu_op)); } ++ void sw (Register rt, Register base, int off) { emit_long(insn_ORRI(sw_op, (int)base->encoding(), (int)rt->encoding(), off)); } ++ void swl (Register rt, Register base, int off) { emit_long(insn_ORRI(swl_op, (int)base->encoding(), (int)rt->encoding(), off)); } ++ void swr (Register rt, Register base, int off) { emit_long(insn_ORRI(swr_op, (int)base->encoding(), (int)rt->encoding(), off)); } ++ void synci(Register base, int off) { emit_long(insn_ORRI(regimm_op, (int)base->encoding(), synci_op, off)); } ++ void sync () { ++ if (os::is_ActiveCoresMP()) ++ emit_long(0); ++ else ++ emit_long(sync_op); ++ } ++ void syscall(int code) { emit_long( (code<<6) | syscall_op ); } ++ ++ void sb(Register rt, Address dst); ++ void sc(Register rt, Address dst); ++ void scd(Register rt, Address dst); ++ void sd(Register rt, Address dst); ++ void sdl(Register rt, Address dst); ++ void sdr(Register rt, Address dst); ++ void sh(Register rt, Address dst); ++ void sw(Register rt, Address dst); ++ void swl(Register rt, Address dst); ++ void swr(Register rt, Address dst); ++ ++ void teq (Register rs, Register rt, int code) { emit_long(insn_RRCO((int)rs->encoding(), (int)rt->encoding(), code, teq_op)); } ++ void teqi (Register rs, int imm) { emit_long(insn_ORRI(regimm_op, (int)rs->encoding(), teqi_op, imm)); } ++ void tge (Register rs, Register rt, int code) { emit_long(insn_RRCO((int)rs->encoding(), (int)rt->encoding(), code, tge_op)); } ++ void tgei (Register rs, int imm) { emit_long(insn_ORRI(regimm_op, (int)rs->encoding(), tgei_op, imm)); } ++ void tgeiu(Register rs, int imm) { emit_long(insn_ORRI(regimm_op, (int)rs->encoding(), tgeiu_op, imm)); } ++ void tgeu (Register rs, Register rt, int code) { emit_long(insn_RRCO((int)rs->encoding(), (int)rt->encoding(), code, tgeu_op)); } ++ void tlt (Register rs, Register rt, int code) { emit_long(insn_RRCO((int)rs->encoding(), (int)rt->encoding(), code, tlt_op)); } ++ void tlti (Register rs, int imm) { emit_long(insn_ORRI(regimm_op, (int)rs->encoding(), tlti_op, imm)); } ++ void tltiu(Register rs, int imm) { emit_long(insn_ORRI(regimm_op, (int)rs->encoding(), tltiu_op, imm)); } ++ void tltu (Register rs, Register rt, int code) { emit_long(insn_RRCO((int)rs->encoding(), (int)rt->encoding(), code, tltu_op)); } ++ void tne (Register rs, Register rt, int code) { emit_long(insn_RRCO((int)rs->encoding(), (int)rt->encoding(), code, tne_op)); } ++ void tnei (Register rs, int imm) { emit_long(insn_ORRI(regimm_op, (int)rs->encoding(), tnei_op, imm)); } ++ ++ void xorr(Register rd, Register rs, Register rt) { emit_long(insn_RRRO((int)rs->encoding(), (int)rt->encoding(), (int)rd->encoding(), xor_op)); } ++ void xori(Register rt, Register rs, int imm) { emit_long(insn_ORRI(xori_op, (int)rs->encoding(), (int)rt->encoding(), simm16(imm))); } ++ ++ void nop() { emit_long(0); } ++ ++ ++ ++ void ldc1(FloatRegister ft, Register base, int off) { emit_long(insn_ORRI(ldc1_op, (int)base->encoding(), (int)ft->encoding(), off)); } ++ void lwc1(FloatRegister ft, Register base, int off) { emit_long(insn_ORRI(lwc1_op, (int)base->encoding(), (int)ft->encoding(), off)); } ++ void ldc1(FloatRegister ft, Address src); ++ void lwc1(FloatRegister ft, Address src); ++ ++ //COP0 ++ void mfc0 (Register rt, Register rd) { emit_long(insn_COP0( mfc0_op, (int)rt->encoding(), (int)rd->encoding())); } ++ void dmfc0 (Register rt, FloatRegister rd) { emit_long(insn_COP0(dmfc0_op, (int)rt->encoding(), (int)rd->encoding())); } ++ // MFGC0, DMFGC0, MTGC0, DMTGC0 not implemented yet ++ void mtc0 (Register rt, Register rd) { emit_long(insn_COP0( mtc0_op, (int)rt->encoding(), (int)rd->encoding())); } ++ void dmtc0 (Register rt, FloatRegister rd) { emit_long(insn_COP0(dmtc0_op, (int)rt->encoding(), (int)rd->encoding())); } ++ //COP0 end ++ ++ ++ //COP1 ++ void mfc1 (Register rt, FloatRegister fs) { emit_long(insn_COP1 (mfc1_op, (int)rt->encoding(), (int)fs->encoding())); } ++ void dmfc1(Register rt, FloatRegister fs) { emit_long(insn_COP1(dmfc1_op, (int)rt->encoding(), (int)fs->encoding())); } ++ void cfc1 (Register rt, int fs) { emit_long(insn_COP1( cfc1_op, (int)rt->encoding(), fs)); } ++ void mfhc1(Register rt, int fs) { emit_long(insn_COP1(mfhc1_op, (int)rt->encoding(), fs)); } ++ void mtc1 (Register rt, FloatRegister fs) { emit_long(insn_COP1( mtc1_op, (int)rt->encoding(), (int)fs->encoding())); } ++ void dmtc1(Register rt, FloatRegister fs) { emit_long(insn_COP1(dmtc1_op, (int)rt->encoding(), (int)fs->encoding())); } ++ void ctc1 (Register rt, FloatRegister fs) { emit_long(insn_COP1( ctc1_op, (int)rt->encoding(), (int)fs->encoding())); } ++ void ctc1 (Register rt, int fs) { emit_long(insn_COP1(ctc1_op, (int)rt->encoding(), fs)); } ++ void mthc1(Register rt, int fs) { emit_long(insn_COP1(mthc1_op, (int)rt->encoding(), fs)); } ++ ++ void bc1f (int off) { emit_long(insn_ORRI(cop1_op, bc1f_op, bcf_op, off)); has_delay_slot(); } ++ void bc1fl(int off) { emit_long(insn_ORRI(cop1_op, bc1f_op, bcfl_op, off)); has_delay_slot(); } ++ void bc1t (int off) { emit_long(insn_ORRI(cop1_op, bc1f_op, bct_op, off)); has_delay_slot(); } ++ void bc1tl(int off) { emit_long(insn_ORRI(cop1_op, bc1f_op, bctl_op, off)); has_delay_slot(); } ++ ++ void bc1f (address entry) { bc1f(offset(entry)); } ++ void bc1fl(address entry) { bc1fl(offset(entry)); } ++ void bc1t (address entry) { bc1t(offset(entry)); } ++ void bc1tl(address entry) { bc1tl(offset(entry)); } ++ ++ void bc1f (Label& L) { bc1f(target(L)); } ++ void bc1fl(Label& L) { bc1fl(target(L)); } ++ void bc1t (Label& L) { bc1t(target(L)); } ++ void bc1tl(Label& L) { bc1tl(target(L)); } ++ ++//R0->encoding() is 0; INSN_SINGLE is enclosed by {} for ctags. ++#define INSN_SINGLE(r1, r2, r3, op) \ ++ { emit_long(insn_F3RO(single_fmt, (int)r1->encoding(), (int)r2->encoding(), (int)r3->encoding(), op));} ++ void add_s (FloatRegister fd, FloatRegister fs, FloatRegister ft) {INSN_SINGLE(ft, fs, fd, fadd_op)} ++ void sub_s (FloatRegister fd, FloatRegister fs, FloatRegister ft) {INSN_SINGLE(ft, fs, fd, fsub_op)} ++ void mul_s (FloatRegister fd, FloatRegister fs, FloatRegister ft) {INSN_SINGLE(ft, fs, fd, fmul_op)} ++ void div_s (FloatRegister fd, FloatRegister fs, FloatRegister ft) {INSN_SINGLE(ft, fs, fd, fdiv_op)} ++ void sqrt_s (FloatRegister fd, FloatRegister fs) {INSN_SINGLE(R0, fs, fd, fsqrt_op)} ++ void abs_s (FloatRegister fd, FloatRegister fs) {INSN_SINGLE(R0, fs, fd, fabs_op)} ++ void mov_s (FloatRegister fd, FloatRegister fs) {INSN_SINGLE(R0, fs, fd, fmov_op)} ++ void neg_s (FloatRegister fd, FloatRegister fs) {INSN_SINGLE(R0, fs, fd, fneg_op)} ++ void round_l_s(FloatRegister fd, FloatRegister fs) {INSN_SINGLE(R0, fs, fd, froundl_op)} ++ void trunc_l_s(FloatRegister fd, FloatRegister fs) {INSN_SINGLE(R0, fs, fd, ftruncl_op)} ++ void ceil_l_s (FloatRegister fd, FloatRegister fs) {INSN_SINGLE(R0, fs, fd, fceill_op)} ++ void floor_l_s(FloatRegister fd, FloatRegister fs) {INSN_SINGLE(R0, fs, fd, ffloorl_op)} ++ void round_w_s(FloatRegister fd, FloatRegister fs) {INSN_SINGLE(R0, fs, fd, froundw_op)} ++ void trunc_w_s(FloatRegister fd, FloatRegister fs) {INSN_SINGLE(R0, fs, fd, ftruncw_op)} ++ void ceil_w_s (FloatRegister fd, FloatRegister fs) {INSN_SINGLE(R0, fs, fd, fceilw_op)} ++ void floor_w_s(FloatRegister fd, FloatRegister fs) {INSN_SINGLE(R0, fs, fd, ffloorw_op)} ++ //null ++ void movf_s(FloatRegister fs, FloatRegister fd, int cc = 0) { ++ assert(cc >= 0 && cc <= 7, "cc is 3 bits"); ++ emit_long((cop1_op<<26) | (single_fmt<<21) | (cc<<18) | ((int)fs->encoding()<<11) | ((int)fd->encoding()<<6) | movf_f_op );} ++ void movt_s(FloatRegister fs, FloatRegister fd, int cc = 0) { ++ assert(cc >= 0 && cc <= 7, "cc is 3 bits"); ++ emit_long((cop1_op<<26) | (single_fmt<<21) | (cc<<18) | 1<<16 | ((int)fs->encoding()<<11) | ((int)fd->encoding()<<6) | movf_f_op );} ++ void movz_s (FloatRegister fd, FloatRegister fs, Register rt) {INSN_SINGLE(rt, fs, fd, movz_f_op)} ++ void movn_s (FloatRegister fd, FloatRegister fs, Register rt) {INSN_SINGLE(rt, fs, fd, movn_f_op)} ++ //null ++ void recip_s (FloatRegister fd, FloatRegister fs) {INSN_SINGLE(R0, fs, fd, frecip_op)} ++ void rsqrt_s (FloatRegister fd, FloatRegister fs) {INSN_SINGLE(R0, fs, fd, frsqrt_op)} ++ //null ++ void cvt_d_s (FloatRegister fd, FloatRegister fs) {INSN_SINGLE(R0, fs, fd, fcvtd_op)} ++ //null ++ void cvt_w_s (FloatRegister fd, FloatRegister fs) {INSN_SINGLE(R0, fs, fd, fcvtw_op)} ++ void cvt_l_s (FloatRegister fd, FloatRegister fs) {INSN_SINGLE(R0, fs, fd, fcvtl_op)} ++ void cvt_ps_s(FloatRegister fd, FloatRegister fs, FloatRegister ft) {INSN_SINGLE(ft, fs, fd, fcvtps_op)} ++ //null ++ void c_f_s (FloatRegister fs, FloatRegister ft) {INSN_SINGLE(ft, fs, R0, f_cond)} ++ void c_un_s (FloatRegister fs, FloatRegister ft) {INSN_SINGLE(ft, fs, R0, un_cond)} ++ void c_eq_s (FloatRegister fs, FloatRegister ft) {INSN_SINGLE(ft, fs, R0, eq_cond)} ++ void c_ueq_s (FloatRegister fs, FloatRegister ft) {INSN_SINGLE(ft, fs, R0, ueq_cond)} ++ void c_olt_s (FloatRegister fs, FloatRegister ft) {INSN_SINGLE(ft, fs, R0, olt_cond)} ++ void c_ult_s (FloatRegister fs, FloatRegister ft) {INSN_SINGLE(ft, fs, R0, ult_cond)} ++ void c_ole_s (FloatRegister fs, FloatRegister ft) {INSN_SINGLE(ft, fs, R0, ole_cond)} ++ void c_ule_s (FloatRegister fs, FloatRegister ft) {INSN_SINGLE(ft, fs, R0, ule_cond)} ++ void c_sf_s (FloatRegister fs, FloatRegister ft) {INSN_SINGLE(ft, fs, R0, sf_cond)} ++ void c_ngle_s(FloatRegister fs, FloatRegister ft) {INSN_SINGLE(ft, fs, R0, ngle_cond)} ++ void c_seq_s (FloatRegister fs, FloatRegister ft) {INSN_SINGLE(ft, fs, R0, seq_cond)} ++ void c_ngl_s (FloatRegister fs, FloatRegister ft) {INSN_SINGLE(ft, fs, R0, ngl_cond)} ++ void c_lt_s (FloatRegister fs, FloatRegister ft) {INSN_SINGLE(ft, fs, R0, lt_cond)} ++ void c_nge_s (FloatRegister fs, FloatRegister ft) {INSN_SINGLE(ft, fs, R0, nge_cond)} ++ void c_le_s (FloatRegister fs, FloatRegister ft) {INSN_SINGLE(ft, fs, R0, le_cond)} ++ void c_ngt_s (FloatRegister fs, FloatRegister ft) {INSN_SINGLE(ft, fs, R0, ngt_cond)} ++ ++#undef INSN_SINGLE ++ ++ ++//R0->encoding() is 0; INSN_DOUBLE is enclosed by {} for ctags. ++#define INSN_DOUBLE(r1, r2, r3, op) \ ++ { emit_long(insn_F3RO(double_fmt, (int)r1->encoding(), (int)r2->encoding(), (int)r3->encoding(), op));} ++ ++ void add_d (FloatRegister fd, FloatRegister fs, FloatRegister ft) {INSN_DOUBLE(ft, fs, fd, fadd_op)} ++ void sub_d (FloatRegister fd, FloatRegister fs, FloatRegister ft) {INSN_DOUBLE(ft, fs, fd, fsub_op)} ++ void mul_d (FloatRegister fd, FloatRegister fs, FloatRegister ft) {INSN_DOUBLE(ft, fs, fd, fmul_op)} ++ void div_d (FloatRegister fd, FloatRegister fs, FloatRegister ft) {INSN_DOUBLE(ft, fs, fd, fdiv_op)} ++ void sqrt_d (FloatRegister fd, FloatRegister fs) {INSN_DOUBLE(R0, fs, fd, fsqrt_op)} ++ void abs_d (FloatRegister fd, FloatRegister fs) {INSN_DOUBLE(R0, fs, fd, fabs_op)} ++ void mov_d (FloatRegister fd, FloatRegister fs) {INSN_DOUBLE(R0, fs, fd, fmov_op)} ++ void neg_d (FloatRegister fd, FloatRegister fs) {INSN_DOUBLE(R0, fs, fd, fneg_op)} ++ void round_l_d(FloatRegister fd, FloatRegister fs) {INSN_DOUBLE(R0, fs, fd, froundl_op)} ++ void trunc_l_d(FloatRegister fd, FloatRegister fs) {INSN_DOUBLE(R0, fs, fd, ftruncl_op)} ++ void ceil_l_d (FloatRegister fd, FloatRegister fs) {INSN_DOUBLE(R0, fs, fd, fceill_op)} ++ void floor_l_d(FloatRegister fd, FloatRegister fs) {INSN_DOUBLE(R0, fs, fd, ffloorl_op)} ++ void round_w_d(FloatRegister fd, FloatRegister fs) {INSN_DOUBLE(R0, fs, fd, froundw_op)} ++ void trunc_w_d(FloatRegister fd, FloatRegister fs) {INSN_DOUBLE(R0, fs, fd, ftruncw_op)} ++ void ceil_w_d (FloatRegister fd, FloatRegister fs) {INSN_DOUBLE(R0, fs, fd, fceilw_op)} ++ void floor_w_d(FloatRegister fd, FloatRegister fs) {INSN_DOUBLE(R0, fs, fd, ffloorw_op)} ++ //null ++ void movf_d(FloatRegister fs, FloatRegister fd, int cc = 0) { ++ assert(cc >= 0 && cc <= 7, "cc is 3 bits"); ++ emit_long((cop1_op<<26) | (double_fmt<<21) | (cc<<18) | ((int)fs->encoding()<<11) | ((int)fd->encoding()<<6) | movf_f_op );} ++ void movt_d(FloatRegister fs, FloatRegister fd, int cc = 0) { ++ assert(cc >= 0 && cc <= 7, "cc is 3 bits"); ++ emit_long((cop1_op<<26) | (double_fmt<<21) | (cc<<18) | 1<<16 | ((int)fs->encoding()<<11) | ((int)fd->encoding()<<6) | movf_f_op );} ++ void movz_d (FloatRegister fd, FloatRegister fs, Register rt) {INSN_DOUBLE(rt, fs, fd, movz_f_op)} ++ void movn_d (FloatRegister fd, FloatRegister fs, Register rt) {INSN_DOUBLE(rt, fs, fd, movn_f_op)} ++ //null ++ void recip_d (FloatRegister fd, FloatRegister fs) {INSN_DOUBLE(R0, fs, fd, frecip_op)} ++ void rsqrt_d (FloatRegister fd, FloatRegister fs) {INSN_DOUBLE(R0, fs, fd, frsqrt_op)} ++ //null ++ void cvt_s_d (FloatRegister fd, FloatRegister fs) {INSN_DOUBLE(R0, fs, fd, fcvts_op)} ++ void cvt_l_d (FloatRegister fd, FloatRegister fs) {INSN_DOUBLE(R0, fs, fd, fcvtl_op)} ++ //null ++ void cvt_w_d (FloatRegister fd, FloatRegister fs) {INSN_DOUBLE(R0, fs, fd, fcvtw_op)} ++ //null ++ void c_f_d (FloatRegister fs, FloatRegister ft) {INSN_DOUBLE(ft, fs, R0, f_cond)} ++ void c_un_d (FloatRegister fs, FloatRegister ft) {INSN_DOUBLE(ft, fs, R0, un_cond)} ++ void c_eq_d (FloatRegister fs, FloatRegister ft) {INSN_DOUBLE(ft, fs, R0, eq_cond)} ++ void c_ueq_d (FloatRegister fs, FloatRegister ft) {INSN_DOUBLE(ft, fs, R0, ueq_cond)} ++ void c_olt_d (FloatRegister fs, FloatRegister ft) {INSN_DOUBLE(ft, fs, R0, olt_cond)} ++ void c_ult_d (FloatRegister fs, FloatRegister ft) {INSN_DOUBLE(ft, fs, R0, ult_cond)} ++ void c_ole_d (FloatRegister fs, FloatRegister ft) {INSN_DOUBLE(ft, fs, R0, ole_cond)} ++ void c_ule_d (FloatRegister fs, FloatRegister ft) {INSN_DOUBLE(ft, fs, R0, ule_cond)} ++ void c_sf_d (FloatRegister fs, FloatRegister ft) {INSN_DOUBLE(ft, fs, R0, sf_cond)} ++ void c_ngle_d(FloatRegister fs, FloatRegister ft) {INSN_DOUBLE(ft, fs, R0, ngle_cond)} ++ void c_seq_d (FloatRegister fs, FloatRegister ft) {INSN_DOUBLE(ft, fs, R0, seq_cond)} ++ void c_ngl_d (FloatRegister fs, FloatRegister ft) {INSN_DOUBLE(ft, fs, R0, ngl_cond)} ++ void c_lt_d (FloatRegister fs, FloatRegister ft) {INSN_DOUBLE(ft, fs, R0, lt_cond)} ++ void c_nge_d (FloatRegister fs, FloatRegister ft) {INSN_DOUBLE(ft, fs, R0, nge_cond)} ++ void c_le_d (FloatRegister fs, FloatRegister ft) {INSN_DOUBLE(ft, fs, R0, le_cond)} ++ void c_ngt_d (FloatRegister fs, FloatRegister ft) {INSN_DOUBLE(ft, fs, R0, ngt_cond)} ++ ++#undef INSN_DOUBLE ++ ++ ++ //null ++ void cvt_s_w(FloatRegister fd, FloatRegister fs) { emit_long(insn_F3RO(word_fmt, 0, (int)fs->encoding(), (int)fd->encoding(), fcvts_op)); } ++ void cvt_d_w(FloatRegister fd, FloatRegister fs) { emit_long(insn_F3RO(word_fmt, 0, (int)fs->encoding(), (int)fd->encoding(), fcvtd_op)); } ++ //null ++ void cvt_s_l(FloatRegister fd, FloatRegister fs) { emit_long(insn_F3RO(long_fmt, 0, (int)fs->encoding(), (int)fd->encoding(), fcvts_op)); } ++ void cvt_d_l(FloatRegister fd, FloatRegister fs) { emit_long(insn_F3RO(long_fmt, 0, (int)fs->encoding(), (int)fd->encoding(), fcvtd_op)); } ++ //null ++ ++ ++//R0->encoding() is 0; INSN_PS is enclosed by {} for ctags. ++#define INSN_PS(r1, r2, r3, op) \ ++ { emit_long(insn_F3RO(ps_fmt, (int)r1->encoding(), (int)r2->encoding(), (int)r3->encoding(), op));} ++ ++ void add_ps (FloatRegister fd, FloatRegister fs, FloatRegister ft) {INSN_PS(ft, fs, fd, fadd_op)} ++ void sub_ps (FloatRegister fd, FloatRegister fs, FloatRegister ft) {INSN_PS(ft, fs, fd, fsub_op)} ++ void mul_ps (FloatRegister fd, FloatRegister fs, FloatRegister ft) {INSN_PS(ft, fs, fd, fmul_op)} ++ //null ++ void abs_ps (FloatRegister fd, FloatRegister fs) {INSN_PS(R0, fs, fd, fabs_op)} ++ void mov_ps (FloatRegister fd, FloatRegister fs) {INSN_PS(R0, fs, fd, fmov_op)} ++ void neg_ps (FloatRegister fd, FloatRegister fs) {INSN_PS(R0, fs, fd, fneg_op)} ++ //null ++ //void movf_ps(FloatRegister rd, FloatRegister rs, FPConditionCode cc) { unimplemented(" movf_ps")} ++ //void movt_ps(FloatRegister rd, FloatRegister rs, FPConditionCode cc) { unimplemented(" movt_ps") } ++ void movz_ps (FloatRegister fd, FloatRegister fs, Register rt) {INSN_PS(rt, fs, fd, movz_f_op)} ++ void movn_ps (FloatRegister fd, FloatRegister fs, Register rt) {INSN_PS(rt, fs, fd, movn_f_op)} ++ //null ++ void cvt_s_pu (FloatRegister fd, FloatRegister fs) {INSN_PS(R0, fs, fd, fcvts_op)} ++ //null ++ void cvt_s_pl (FloatRegister fd, FloatRegister fs) {INSN_PS(R0, fs, fd, fcvtspl_op)} ++ //null ++ void pll_ps (FloatRegister fd, FloatRegister fs, FloatRegister ft) {INSN_PS(ft, fs, fd, fpll_op)} ++ void plu_ps (FloatRegister fd, FloatRegister fs, FloatRegister ft) {INSN_PS(ft, fs, fd, fplu_op)} ++ void pul_ps (FloatRegister fd, FloatRegister fs, FloatRegister ft) {INSN_PS(ft, fs, fd, fpul_op)} ++ void puu_ps (FloatRegister fd, FloatRegister fs, FloatRegister ft) {INSN_PS(ft, fs, fd, fpuu_op)} ++ void c_f_ps (FloatRegister fs, FloatRegister ft) {INSN_PS(ft, fs, R0, f_cond)} ++ void c_un_ps (FloatRegister fs, FloatRegister ft) {INSN_PS(ft, fs, R0, un_cond)} ++ void c_eq_ps (FloatRegister fs, FloatRegister ft) {INSN_PS(ft, fs, R0, eq_cond)} ++ void c_ueq_ps (FloatRegister fs, FloatRegister ft) {INSN_PS(ft, fs, R0, ueq_cond)} ++ void c_olt_ps (FloatRegister fs, FloatRegister ft) {INSN_PS(ft, fs, R0, olt_cond)} ++ void c_ult_ps (FloatRegister fs, FloatRegister ft) {INSN_PS(ft, fs, R0, ult_cond)} ++ void c_ole_ps (FloatRegister fs, FloatRegister ft) {INSN_PS(ft, fs, R0, ole_cond)} ++ void c_ule_ps (FloatRegister fs, FloatRegister ft) {INSN_PS(ft, fs, R0, ule_cond)} ++ void c_sf_ps (FloatRegister fs, FloatRegister ft) {INSN_PS(ft, fs, R0, sf_cond)} ++ void c_ngle_ps(FloatRegister fs, FloatRegister ft) {INSN_PS(ft, fs, R0, ngle_cond)} ++ void c_seq_ps (FloatRegister fs, FloatRegister ft) {INSN_PS(ft, fs, R0, seq_cond)} ++ void c_ngl_ps (FloatRegister fs, FloatRegister ft) {INSN_PS(ft, fs, R0, ngl_cond)} ++ void c_lt_ps (FloatRegister fs, FloatRegister ft) {INSN_PS(ft, fs, R0, lt_cond)} ++ void c_nge_ps (FloatRegister fs, FloatRegister ft) {INSN_PS(ft, fs, R0, nge_cond)} ++ void c_le_ps (FloatRegister fs, FloatRegister ft) {INSN_PS(ft, fs, R0, le_cond)} ++ void c_ngt_ps (FloatRegister fs, FloatRegister ft) {INSN_PS(ft, fs, R0, ngt_cond)} ++ //null ++#undef INSN_PS ++ //COP1 end ++ ++ ++ //COP1X ++//R0->encoding() is 0; INSN_SINGLE is enclosed by {} for ctags. ++#define INSN_COP1X(r0, r1, r2, r3, op) \ ++ { emit_long(insn_F3ROX((int)r0->encoding(), (int)r1->encoding(), (int)r2->encoding(), (int)r3->encoding(), op));} ++ void madd_s(FloatRegister fd, FloatRegister fr, FloatRegister fs, FloatRegister ft) {INSN_COP1X(fr, ft, fs, fd, madd_s_op) } ++ void madd_d(FloatRegister fd, FloatRegister fr, FloatRegister fs, FloatRegister ft) {INSN_COP1X(fr, ft, fs, fd, madd_d_op) } ++ void madd_ps(FloatRegister fd, FloatRegister fr, FloatRegister fs, FloatRegister ft){INSN_COP1X(fr, ft, fs, fd, madd_ps_op) } ++ void msub_s(FloatRegister fd, FloatRegister fr, FloatRegister fs, FloatRegister ft) {INSN_COP1X(fr, ft, fs, fd, msub_s_op) } ++ void msub_d(FloatRegister fd, FloatRegister fr, FloatRegister fs, FloatRegister ft) {INSN_COP1X(fr, ft, fs, fd, msub_d_op) } ++ void msub_ps(FloatRegister fd, FloatRegister fr, FloatRegister fs, FloatRegister ft){INSN_COP1X(fr, ft, fs, fd, msub_ps_op) } ++ void nmadd_s(FloatRegister fd, FloatRegister fr, FloatRegister fs, FloatRegister ft) {INSN_COP1X(fr, ft, fs, fd, nmadd_s_op) } ++ void nmadd_d(FloatRegister fd, FloatRegister fr, FloatRegister fs, FloatRegister ft) {INSN_COP1X(fr, ft, fs, fd, nmadd_d_op) } ++ void nmadd_ps(FloatRegister fd, FloatRegister fr, FloatRegister fs, FloatRegister ft){INSN_COP1X(fr, ft, fs, fd, nmadd_ps_op) } ++ void nmsub_s(FloatRegister fd, FloatRegister fr, FloatRegister fs, FloatRegister ft) {INSN_COP1X(fr, ft, fs, fd, nmsub_s_op) } ++ void nmsub_d(FloatRegister fd, FloatRegister fr, FloatRegister fs, FloatRegister ft) {INSN_COP1X(fr, ft, fs, fd, nmsub_d_op) } ++ void nmsub_ps(FloatRegister fd, FloatRegister fr, FloatRegister fs, FloatRegister ft){INSN_COP1X(fr, ft, fs, fd, nmsub_ps_op) } ++#undef INSN_COP1X ++ //COP1X end ++ ++ //SPECIAL2 ++//R0->encoding() is 0; INSN_PS is enclosed by {} for ctags. ++#define INSN_S2(op) \ ++ { emit_long((special2_op << 26) | ((int)rs->encoding() << 21) | ((int)rt->encoding() << 16) | ((int)rd->encoding() << 11) | op);} ++ ++ void madd (Register rs, Register rt) { emit_long((special2_op << 26) | ((int)rs->encoding() << 21) | ((int)rt->encoding() << 16) | madd_op); } ++ void maddu (Register rs, Register rt) { emit_long((special2_op << 26) | ((int)rs->encoding() << 21) | ((int)rt->encoding() << 16) | maddu_op); } ++ void mul (Register rd, Register rs, Register rt) { INSN_S2(mul_op) } ++ void gsandn (Register rd, Register rs, Register rt) { INSN_S2((0x12 << 6) | gs0x03_op) } ++ void msub (Register rs, Register rt) { emit_long((special2_op << 26) | ((int)rs->encoding() << 21) | ((int)rt->encoding() << 16) | msub_op); } ++ void msubu (Register rs, Register rt) { emit_long((special2_op << 26) | ((int)rs->encoding() << 21) | ((int)rt->encoding() << 16) | msubu_op); } ++ void gsorn (Register rd, Register rs, Register rt) { INSN_S2((0x12 << 6) | gs0x06_op) } ++ ++ void gsmult (Register rd, Register rs, Register rt) { INSN_S2(gsmult_op) } ++ void gsdmult (Register rd, Register rs, Register rt) { INSN_S2(gsdmult_op) } ++ void gsmultu (Register rd, Register rs, Register rt) { INSN_S2(gsmultu_op) } ++ void gsdmultu(Register rd, Register rs, Register rt) { INSN_S2(gsdmultu_op)} ++ void gsdiv (Register rd, Register rs, Register rt) { INSN_S2(gsdiv_op) } ++ void gsddiv (Register rd, Register rs, Register rt) { INSN_S2(gsddiv_op) } ++ void gsdivu (Register rd, Register rs, Register rt) { INSN_S2(gsdivu_op) } ++ void gsddivu (Register rd, Register rs, Register rt) { INSN_S2(gsddivu_op) } ++ void gsmod (Register rd, Register rs, Register rt) { INSN_S2(gsmod_op) } ++ void gsdmod (Register rd, Register rs, Register rt) { INSN_S2(gsdmod_op) } ++ void gsmodu (Register rd, Register rs, Register rt) { INSN_S2(gsmodu_op) } ++ void gsdmodu (Register rd, Register rs, Register rt) { INSN_S2(gsdmodu_op) } ++ void clz (Register rd, Register rs) { emit_long((special2_op << 26) | ((int)rs->encoding() << 21) | ((int)rd->encoding() << 16) | ((int)rd->encoding() << 11) | clz_op); } ++ void clo (Register rd, Register rs) { emit_long((special2_op << 26) | ((int)rs->encoding() << 21) | ((int)rd->encoding() << 16) | ((int)rd->encoding() << 11) | clo_op); } ++ void ctz (Register rd, Register rs) { emit_long((special2_op << 26) | ((int)rs->encoding() << 21) | ((int)rd->encoding() << 16) | ((int)rd->encoding() << 11) | 0 << 6| xctx_op); } ++ void cto (Register rd, Register rs) { emit_long((special2_op << 26) | ((int)rs->encoding() << 21) | ((int)rd->encoding() << 16) | ((int)rd->encoding() << 11) | 1 << 6| xctx_op); } ++ void dctz(Register rd, Register rs) { emit_long((special2_op << 26) | ((int)rs->encoding() << 21) | ((int)rd->encoding() << 16) | ((int)rd->encoding() << 11) | 2 << 6| xctx_op); } ++ void dcto(Register rd, Register rs) { emit_long((special2_op << 26) | ((int)rs->encoding() << 21) | ((int)rd->encoding() << 16) | ((int)rd->encoding() << 11) | 3 << 6| xctx_op); } ++ void dclz(Register rd, Register rs) { emit_long((special2_op << 26) | ((int)rs->encoding() << 21) | ((int)rd->encoding() << 16) | ((int)rd->encoding() << 11) | dclz_op); } ++ void dclo(Register rd, Register rs) { emit_long((special2_op << 26) | ((int)rs->encoding() << 21) | ((int)rd->encoding() << 16) | ((int)rd->encoding() << 11) | dclo_op); } ++ ++#undef INSN_S2 ++ ++ //SPECIAL3 ++/* ++// FIXME ++#define is_0_to_32(a, b) \ ++ assert (a >= 0, " just a check"); \ ++ assert (a <= 0, " just a check"); \ ++ assert (b >= 0, " just a check"); \ ++ assert (b <= 0, " just a check"); \ ++ assert (a+b >= 0, " just a check"); \ ++ assert (a+b <= 0, " just a check"); ++ */ ++#define is_0_to_32(a, b) ++ ++ void ins (Register rt, Register rs, int pos, int size) { is_0_to_32(pos, size); emit_long((special3_op << 26) | ((int)rs->encoding() << 21) | ((int)rt->encoding() << 16) | (low(pos+size-1, 5) << 11) | (low(pos, 5) << 6) | ins_op); } ++ void dinsm(Register rt, Register rs, int pos, int size) { is_0_to_32(pos, size); emit_long((special3_op << 26) | ((int)rs->encoding() << 21) | ((int)rt->encoding() << 16) | (low(pos+size-33, 5) << 11) | (low(pos, 5) << 6) | dinsm_op); } ++ void dinsu(Register rt, Register rs, int pos, int size) { is_0_to_32(pos, size); emit_long((special3_op << 26) | ((int)rs->encoding() << 21) | ((int)rt->encoding() << 16) | (low(pos+size-33, 5) << 11) | (low(pos-32, 5) << 6) | dinsu_op); } ++ void dins (Register rt, Register rs, int pos, int size) { ++ guarantee((0 <= pos) && (pos < 32), "pos must be in [0, 32)"); ++ guarantee((0 < size) && (size <= 32), "size must be in (0, 32]"); ++ guarantee((0 < pos + size) && (pos + size <= 32), "pos + size must be in (0, 32]"); ++ ++ emit_long((special3_op << 26) | ((int)rs->encoding() << 21) | ((int)rt->encoding() << 16) | (low(pos+size-1, 5) << 11) | (low(pos, 5) << 6) | dins_op); ++ } ++ ++ void repl_qb (Register rd, int const8) { assert(VM_Version::supports_dsp(), ""); emit_long((special3_op << 26) | (low(const8, 8) << 16) | ((int)rd->encoding() << 11) | repl_qb_op << 6 | re1_op); } ++ void replv_qb(Register rd, Register rt) { assert(VM_Version::supports_dsp(), ""); emit_long((special3_op << 26) | ((int)rt->encoding() << 16) | ((int)rd->encoding() << 11) | replv_qb_op << 6 | re1_op ); } ++ void repl_ph (Register rd, int const10) { assert(VM_Version::supports_dsp(), ""); emit_long((special3_op << 26) | (low(const10, 10) << 16) | ((int)rd->encoding() << 11) | repl_ph_op << 6 | re1_op); } ++ void replv_ph(Register rd, Register rt) { assert(VM_Version::supports_dsp(), ""); emit_long((special3_op << 26) | ((int)rt->encoding() << 16) | ((int)rd->encoding() << 11) | replv_ph_op << 6 | re1_op ); } ++ ++ void repl_ob (Register rd, int const8) { assert(VM_Version::supports_dsp(), ""); emit_long((special3_op << 26) | (low(const8, 8) << 16) | ((int)rd->encoding() << 11) | repl_ob_op << 6 | re2_op); } ++ void replv_ob(Register rd, Register rt) { assert(VM_Version::supports_dsp(), ""); emit_long((special3_op << 26) | ((int)rt->encoding() << 16) | ((int)rd->encoding() << 11) | replv_ob_op << 6 | re2_op ); } ++ void repl_qh (Register rd, int const10) { assert(VM_Version::supports_dsp(), ""); emit_long((special3_op << 26) | (low(const10, 10) << 16) | ((int)rd->encoding() << 11) | repl_qh_op << 6 | re2_op); } ++ void replv_qh(Register rd, Register rt) { assert(VM_Version::supports_dsp(), ""); emit_long((special3_op << 26) | ((int)rt->encoding() << 16) | ((int)rd->encoding() << 11) | replv_qh_op << 6 | re2_op ); } ++ void repl_pw (Register rd, int const10) { assert(VM_Version::supports_dsp(), ""); emit_long((special3_op << 26) | (low(const10, 10) << 16) | ((int)rd->encoding() << 11) | repl_pw_op << 6 | re2_op); } ++ void replv_pw(Register rd, Register rt) { assert(VM_Version::supports_dsp(), ""); emit_long((special3_op << 26) | ((int)rt->encoding() << 16) | ((int)rd->encoding() << 11) | replv_pw_op << 6 | re2_op ); } ++ ++ void sdc1(FloatRegister ft, Register base, int off) { emit_long(insn_ORRI(sdc1_op, (int)base->encoding(), (int)ft->encoding(), off)); } ++ void sdc1(FloatRegister ft, Address dst); ++ void swc1(FloatRegister ft, Register base, int off) { emit_long(insn_ORRI(swc1_op, (int)base->encoding(), (int)ft->encoding(), off)); } ++ void swc1(FloatRegister ft, Address dst); ++ ++ ++ static void print_instruction(int); ++ int patched_branch(int dest_pos, int inst, int inst_pos); ++ int branch_destination(int inst, int pos); ++ ++ // Loongson extension ++ ++ // gssq/gslq/gssqc1/gslqc1: vAddr = sign_extend(offset << 4 ) + GPR[base]. Therefore, the off should be ">> 4". ++ void gslble(Register rt, Register base, Register bound) { ++ emit_long((gs_lwc2_op << 26) | ((int)base->encoding() << 21) | ((int)rt->encoding() << 16) | ((int)bound->encoding() << 11) | 0 << 6 | gslble_op); ++ } ++ ++ void gslbgt(Register rt, Register base, Register bound) { ++ emit_long((gs_lwc2_op << 26) | ((int)base->encoding() << 21) | ((int)rt->encoding() << 16) | ((int)bound->encoding() << 11) | 0 << 6 | gslbgt_op); ++ } ++ ++ void gslhle(Register rt, Register base, Register bound) { ++ emit_long((gs_lwc2_op << 26) | ((int)base->encoding() << 21) | ((int)rt->encoding() << 16) | ((int)bound->encoding() << 11) | 0 << 6 | gslhle_op); ++ } ++ ++ void gslhgt(Register rt, Register base, Register bound) { ++ emit_long((gs_lwc2_op << 26) | ((int)base->encoding() << 21) | ((int)rt->encoding() << 16) | ((int)bound->encoding() << 11) | 0 << 6 | gslhgt_op); ++ } ++ ++ void gslwle(Register rt, Register base, Register bound) { ++ emit_long((gs_lwc2_op << 26) | ((int)base->encoding() << 21) | ((int)rt->encoding() << 16) | ((int)bound->encoding() << 11) | 0 << 6 | gslwle_op); ++ } ++ ++ void gslwgt(Register rt, Register base, Register bound) { ++ emit_long((gs_lwc2_op << 26) | ((int)base->encoding() << 21) | ((int)rt->encoding() << 16) | ((int)bound->encoding() << 11) | 0 << 6 | gslwgt_op); ++ } ++ ++ void gsldle(Register rt, Register base, Register bound) { ++ emit_long((gs_lwc2_op << 26) | ((int)base->encoding() << 21) | ((int)rt->encoding() << 16) | ((int)bound->encoding() << 11) | 0 << 6 | gsldle_op); ++ } ++ ++ void gsldgt(Register rt, Register base, Register bound) { ++ emit_long((gs_lwc2_op << 26) | ((int)base->encoding() << 21) | ((int)rt->encoding() << 16) | ((int)bound->encoding() << 11) | 0 << 6 | gsldgt_op); ++ } ++ ++ void gslwlec1(FloatRegister rt, Register base, Register bound) { ++ emit_long((gs_lwc2_op << 26) | ((int)base->encoding() << 21) | ((int)rt->encoding() << 16) | ((int)bound->encoding() << 11) | 0 << 6 | gslwlec1_op); ++ } ++ ++ void gslwgtc1(FloatRegister rt, Register base, Register bound) { ++ emit_long((gs_lwc2_op << 26) | ((int)base->encoding() << 21) | ((int)rt->encoding() << 16) | ((int)bound->encoding() << 11) | 0 << 6 | gslwgtc1_op); ++ } ++ ++ void gsldlec1(FloatRegister rt, Register base, Register bound) { ++ emit_long((gs_lwc2_op << 26) | ((int)base->encoding() << 21) | ((int)rt->encoding() << 16) | ((int)bound->encoding() << 11) | 0 << 6 | gsldlec1_op); ++ } ++ ++ void gsldgtc1(FloatRegister rt, Register base, Register bound) { ++ emit_long((gs_lwc2_op << 26) | ((int)base->encoding() << 21) | ((int)rt->encoding() << 16) | ((int)bound->encoding() << 11) | 0 << 6 | gsldgtc1_op); ++ } ++ ++ void gslq(Register rq, Register rt, Register base, int off) { ++ assert(!(off & 0xF), "gslq: the low 4 bits of off must be 0"); ++ off = off >> 4; ++ assert(is_simm(off, 9),"gslq: off exceeds 9 bits"); ++ emit_long((gs_lwc2_op << 26) | ((int)base->encoding() << 21) | ((int)rt->encoding() << 16) | 0 << 15 | (low(off, 9) << 6) | gslq_op | (int)rq->encoding() ); ++ } ++ ++ void gslqc1(FloatRegister rq, FloatRegister rt, Register base, int off) { ++ assert(!(off & 0xF), "gslqc1: the low 4 bits of off must be 0"); ++ off = off >> 4; ++ assert(is_simm(off, 9),"gslqc1: off exceeds 9 bits"); ++ emit_long((gs_lwc2_op << 26) | ((int)base->encoding() << 21) | ((int)rt->encoding() << 16) | 1 << 15 | (low(off, 9) << 6) | gslq_op | (int)rq->encoding() ); ++ } ++ ++ void gssble(Register rt, Register base, Register bound) { ++ emit_long((gs_swc2_op << 26) | ((int)base->encoding() << 21) | ((int)rt->encoding() << 16) | ((int)bound->encoding() << 11) | 0 << 6 | gssble_op); ++ } ++ ++ void gssbgt(Register rt, Register base, Register bound) { ++ emit_long((gs_swc2_op << 26) | ((int)base->encoding() << 21) | ((int)rt->encoding() << 16) | ((int)bound->encoding() << 11) | 0 << 6 | gssbgt_op); ++ } ++ ++ void gsshle(Register rt, Register base, Register bound) { ++ emit_long((gs_swc2_op << 26) | ((int)base->encoding() << 21) | ((int)rt->encoding() << 16) | ((int)bound->encoding() << 11) | 0 << 6 | gsshle_op); ++ } ++ ++ void gsshgt(Register rt, Register base, Register bound) { ++ emit_long((gs_swc2_op << 26) | ((int)base->encoding() << 21) | ((int)rt->encoding() << 16) | ((int)bound->encoding() << 11) | 0 << 6 | gsshgt_op); ++ } ++ ++ void gsswle(Register rt, Register base, Register bound) { ++ emit_long((gs_swc2_op << 26) | ((int)base->encoding() << 21) | ((int)rt->encoding() << 16) | ((int)bound->encoding() << 11) | 0 << 6 | gsswle_op); ++ } ++ ++ void gsswgt(Register rt, Register base, Register bound) { ++ emit_long((gs_swc2_op << 26) | ((int)base->encoding() << 21) | ((int)rt->encoding() << 16) | ((int)bound->encoding() << 11) | 0 << 6 | gsswgt_op); ++ } ++ ++ void gssdle(Register rt, Register base, Register bound) { ++ emit_long((gs_swc2_op << 26) | ((int)base->encoding() << 21) | ((int)rt->encoding() << 16) | ((int)bound->encoding() << 11) | 0 << 6 | gssdle_op); ++ } ++ ++ void gssdgt(Register rt, Register base, Register bound) { ++ emit_long((gs_swc2_op << 26) | ((int)base->encoding() << 21) | ((int)rt->encoding() << 16) | ((int)bound->encoding() << 11) | 0 << 6 | gssdgt_op); ++ } ++ ++ void gsswlec1(FloatRegister rt, Register base, Register bound) { ++ emit_long((gs_swc2_op << 26) | ((int)base->encoding() << 21) | ((int)rt->encoding() << 16) | ((int)bound->encoding() << 11) | 0 << 6 | gsswlec1_op); ++ } ++ ++ void gsswgtc1(FloatRegister rt, Register base, Register bound) { ++ emit_long((gs_swc2_op << 26) | ((int)base->encoding() << 21) | ((int)rt->encoding() << 16) | ((int)bound->encoding() << 11) | 0 << 6 | gsswgtc1_op); ++ } ++ ++ void gssdlec1(FloatRegister rt, Register base, Register bound) { ++ emit_long((gs_swc2_op << 26) | ((int)base->encoding() << 21) | ((int)rt->encoding() << 16) | ((int)bound->encoding() << 11) | 0 << 6 | gssdlec1_op); ++ } ++ ++ void gssdgtc1(FloatRegister rt, Register base, Register bound) { ++ emit_long((gs_swc2_op << 26) | ((int)base->encoding() << 21) | ((int)rt->encoding() << 16) | ((int)bound->encoding() << 11) | 0 << 6 | gssdgtc1_op); ++ } ++ ++ void gssq(Register rq, Register rt, Register base, int off) { ++ assert(!(off & 0xF), "gssq: the low 4 bits of off must be 0"); ++ off = off >> 4; ++ assert(is_simm(off, 9),"gssq: off exceeds 9 bits"); ++ emit_long((gs_swc2_op << 26) | ((int)base->encoding() << 21) | ((int)rt->encoding() << 16) | 0 << 15 | (low(off, 9) << 6) | gssq_op | (int)rq->encoding() ); ++ } ++ ++ void gssqc1(FloatRegister rq, FloatRegister rt, Register base, int off) { ++ assert(!(off & 0xF), "gssqc1: the low 4 bits of off must be 0"); ++ off = off >> 4; ++ assert(is_simm(off, 9),"gssqc1: off exceeds 9 bits"); ++ emit_long((gs_swc2_op << 26) | ((int)base->encoding() << 21) | ((int)rt->encoding() << 16) | 1 << 15 | (low(off, 9) << 6) | gssq_op | (int)rq->encoding() ); ++ } ++ ++ //LDC2 & SDC2 ++#define INSN(OPS, OP) \ ++ assert(is_simm(off, 8), "NAME: off exceeds 8 bits"); \ ++ assert(UseLEXT1, "check UseLEXT1"); \ ++ emit_long( (OPS << 26) | ((int)base->encoding() << 21) | ((int)rt->encoding() << 16) | \ ++ ((int)index->encoding() << 11) | (low(off, 8) << 3) | OP); ++ ++#define INSN_LDC2(NAME, op) \ ++ void NAME(Register rt, Register base, Register index, int off) { \ ++ INSN(gs_ldc2_op, op) \ ++ } ++ ++#define INSN_LDC2_F(NAME, op) \ ++ void NAME(FloatRegister rt, Register base, Register index, int off) { \ ++ INSN(gs_ldc2_op, op) \ ++ } ++ ++#define INSN_SDC2(NAME, op) \ ++ void NAME(Register rt, Register base, Register index, int off) { \ ++ INSN(gs_sdc2_op, op) \ ++ } ++ ++#define INSN_SDC2_F(NAME, op) \ ++ void NAME(FloatRegister rt, Register base, Register index, int off) { \ ++ INSN(gs_sdc2_op, op) \ ++ } ++ ++/* ++ void gslbx(Register rt, Register base, Register index, int off) { ++ assert(is_simm(off, 8), "gslbx: off exceeds 8 bits"); ++ assert(UseLEXT1, "check UseLEXT1"); ++ emit_long( (gs_ldc2_op << 26) | ((int)base->encoding() << 21) | ((int)rt->encoding() << 16) | ++ ((int)index->encoding() << 11) | (low(off, 8) << 3) | gslbx_op); ++ void gslbx(Register rt, Register base, Register index, int off) {INSN(gs_ldc2_op, gslbx_op);} ++ ++ INSN_LDC2(gslbx, gslbx_op) ++ INSN_LDC2(gslhx, gslhx_op) ++ INSN_LDC2(gslwx, gslwx_op) ++ INSN_LDC2(gsldx, gsldx_op) ++ INSN_LDC2_F(gslwxc1, gslwxc1_op) ++ INSN_LDC2_F(gsldxc1, gsldxc1_op) ++ ++ INSN_SDC2(gssbx, gssbx_op) ++ INSN_SDC2(gsshx, gsshx_op) ++ INSN_SDC2(gsswx, gsswx_op) ++ INSN_SDC2(gssdx, gssdx_op) ++ INSN_SDC2_F(gsswxc1, gsswxc1_op) ++ INSN_SDC2_F(gssdxc1, gssdxc1_op) ++*/ ++ void gslbx(Register rt, Register base, Register index, int off) {INSN(gs_ldc2_op, gslbx_op) } ++ void gslhx(Register rt, Register base, Register index, int off) {INSN(gs_ldc2_op, gslhx_op) } ++ void gslwx(Register rt, Register base, Register index, int off) {INSN(gs_ldc2_op, gslwx_op) } ++ void gsldx(Register rt, Register base, Register index, int off) {INSN(gs_ldc2_op, gsldx_op) } ++ void gslwxc1(FloatRegister rt, Register base, Register index, int off) {INSN(gs_ldc2_op, gslwxc1_op) } ++ void gsldxc1(FloatRegister rt, Register base, Register index, int off) {INSN(gs_ldc2_op, gsldxc1_op) } ++ ++ void gssbx(Register rt, Register base, Register index, int off) {INSN(gs_sdc2_op, gssbx_op) } ++ void gsshx(Register rt, Register base, Register index, int off) {INSN(gs_sdc2_op, gsshx_op) } ++ void gsswx(Register rt, Register base, Register index, int off) {INSN(gs_sdc2_op, gsswx_op) } ++ void gssdx(Register rt, Register base, Register index, int off) {INSN(gs_sdc2_op, gssdx_op) } ++ void gsswxc1(FloatRegister rt, Register base, Register index, int off) {INSN(gs_sdc2_op, gsswxc1_op) } ++ void gssdxc1(FloatRegister rt, Register base, Register index, int off) {INSN(gs_sdc2_op, gssdxc1_op) } ++ ++#undef INSN ++#undef INSN_LDC2 ++#undef INSN_LDC2_F ++#undef INSN_SDC2 ++#undef INSN_SDC2_F ++ ++ // cpucfg on Loongson CPUs above 3A4000 ++ void cpucfg(Register rd, Register rs) { emit_long((gs_lwc2_op << 26) | ((int)rs->encoding() << 21) | (0b01000 << 16) | ((int)rd->encoding() << 11) | ( 0b00100 << 6) | 0b011000);} ++ ++ ++public: ++ // Creation ++ Assembler(CodeBuffer* code) : AbstractAssembler(code) { ++#ifdef CHECK_DELAY ++ delay_state = no_delay; ++#endif ++ } ++ ++ // Decoding ++ static address locate_operand(address inst, WhichOperand which); ++ static address locate_next_instruction(address inst); ++}; ++ ++ ++ ++#endif // CPU_MIPS_VM_ASSEMBLER_MIPS_HPP +diff --git a/hotspot/src/cpu/mips/vm/assembler_mips.inline.hpp b/hotspot/src/cpu/mips/vm/assembler_mips.inline.hpp +new file mode 100644 +index 0000000000..ece9183cf0 +--- /dev/null ++++ b/hotspot/src/cpu/mips/vm/assembler_mips.inline.hpp +@@ -0,0 +1,57 @@ ++/* ++ * Copyright (c) 1997, 2010, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2015, 2021, Loongson Technology. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ * ++ */ ++ ++#ifndef CPU_MIPS_VM_ASSEMBLER_MIPS_INLINE_HPP ++#define CPU_MIPS_VM_ASSEMBLER_MIPS_INLINE_HPP ++ ++#include "asm/assembler.inline.hpp" ++#include "asm/codeBuffer.hpp" ++#include "code/codeCache.hpp" ++ ++ ++ ++inline void Assembler::check_delay() { ++# ifdef CHECK_DELAY ++ guarantee(delay_state != at_delay_slot, "must say delayed() when filling delay slot"); ++ delay_state = no_delay; ++# endif ++} ++ ++inline void Assembler::emit_long(int x) { ++ check_delay(); ++ AbstractAssembler::emit_int32(x); ++} ++ ++inline void Assembler::emit_data(int x, relocInfo::relocType rtype) { ++ relocate(rtype); ++ emit_long(x); ++} ++ ++inline void Assembler::emit_data(int x, RelocationHolder const& rspec) { ++ relocate(rspec); ++ emit_long(x); ++} ++ ++#endif // CPU_MIPS_VM_ASSEMBLER_MIPS_INLINE_HPP +diff --git a/hotspot/src/cpu/mips/vm/bytecodeInterpreter_mips.cpp b/hotspot/src/cpu/mips/vm/bytecodeInterpreter_mips.cpp +new file mode 100644 +index 0000000000..a4a1b28c2d +--- /dev/null ++++ b/hotspot/src/cpu/mips/vm/bytecodeInterpreter_mips.cpp +@@ -0,0 +1,53 @@ ++/* ++ * Copyright (c) 2007, 2013, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2015, 2016, Loongson Technology. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ * ++ */ ++ ++#include "precompiled.hpp" ++#include "asm/assembler.hpp" ++#include "interpreter/bytecodeInterpreter.hpp" ++#include "interpreter/bytecodeInterpreter.inline.hpp" ++#include "interpreter/interpreter.hpp" ++#include "interpreter/interpreterRuntime.hpp" ++#include "oops/methodData.hpp" ++#include "oops/method.hpp" ++#include "oops/oop.inline.hpp" ++#include "prims/jvmtiExport.hpp" ++#include "prims/jvmtiThreadState.hpp" ++#include "runtime/deoptimization.hpp" ++#include "runtime/frame.inline.hpp" ++#include "runtime/sharedRuntime.hpp" ++#include "runtime/stubRoutines.hpp" ++#include "runtime/synchronizer.hpp" ++#include "runtime/vframeArray.hpp" ++#include "utilities/debug.hpp" ++#ifdef TARGET_ARCH_MODEL_mips_32 ++# include "interp_masm_mips_32.hpp" ++#endif ++#ifdef TARGET_ARCH_MODEL_mips_64 ++# include "interp_masm_mips_64.hpp" ++#endif ++ ++#ifdef CC_INTERP ++ ++#endif // CC_INTERP (all) +diff --git a/hotspot/src/cpu/mips/vm/bytecodeInterpreter_mips.hpp b/hotspot/src/cpu/mips/vm/bytecodeInterpreter_mips.hpp +new file mode 100644 +index 0000000000..aac8b7a2b7 +--- /dev/null ++++ b/hotspot/src/cpu/mips/vm/bytecodeInterpreter_mips.hpp +@@ -0,0 +1,110 @@ ++/* ++ * Copyright (c) 2002, 2010, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2015, 2016, Loongson Technology. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ * ++ */ ++ ++#ifndef CPU_MIPS_VM_BYTECODEINTERPRETER_MIPS_HPP ++#define CPU_MIPS_VM_BYTECODEINTERPRETER_MIPS_HPP ++ ++// Platform specific for C++ based Interpreter ++#define LOTS_OF_REGS /* Lets interpreter use plenty of registers */ ++ ++private: ++ ++ // save the bottom of the stack after frame manager setup. For ease of restoration after return ++ // from recursive interpreter call ++ intptr_t* _frame_bottom; /* saved bottom of frame manager frame */ ++ intptr_t* _last_Java_pc; /* pc to return to in frame manager */ ++ intptr_t* _sender_sp; /* sender's sp before stack (locals) extension */ ++ interpreterState _self_link; /* Previous interpreter state */ /* sometimes points to self??? */ ++ double _native_fresult; /* save result of native calls that might return floats */ ++ intptr_t _native_lresult; /* save result of native calls that might return handle/longs */ ++public: ++ ++ static void pd_layout_interpreterState(interpreterState istate, address last_Java_pc, intptr_t* last_Java_fp); ++ inline intptr_t* sender_sp() { ++ return _sender_sp; ++ } ++ ++ ++#define SET_LAST_JAVA_FRAME() ++ ++#define RESET_LAST_JAVA_FRAME() THREAD->frame_anchor()->set_flags(0); ++ ++/* ++ * Macros for accessing the stack. ++ */ ++#undef STACK_INT ++#undef STACK_FLOAT ++#undef STACK_ADDR ++#undef STACK_OBJECT ++#undef STACK_DOUBLE ++#undef STACK_LONG ++ ++// JavaStack Implementation ++ ++#define GET_STACK_SLOT(offset) (*((intptr_t*) &topOfStack[-(offset)])) ++#define STACK_SLOT(offset) ((address) &topOfStack[-(offset)]) ++#define STACK_ADDR(offset) (*((address *) &topOfStack[-(offset)])) ++#define STACK_INT(offset) (*((jint*) &topOfStack[-(offset)])) ++#define STACK_FLOAT(offset) (*((jfloat *) &topOfStack[-(offset)])) ++#define STACK_OBJECT(offset) (*((oop *) &topOfStack [-(offset)])) ++#define STACK_DOUBLE(offset) (((VMJavaVal64*) &topOfStack[-(offset)])->d) ++#define STACK_LONG(offset) (((VMJavaVal64 *) &topOfStack[-(offset)])->l) ++ ++#define SET_STACK_SLOT(value, offset) (*(intptr_t*)&topOfStack[-(offset)] = *(intptr_t*)(value)) ++#define SET_STACK_ADDR(value, offset) (*((address *)&topOfStack[-(offset)]) = (value)) ++#define SET_STACK_INT(value, offset) (*((jint *)&topOfStack[-(offset)]) = (value)) ++#define SET_STACK_FLOAT(value, offset) (*((jfloat *)&topOfStack[-(offset)]) = (value)) ++#define SET_STACK_OBJECT(value, offset) (*((oop *)&topOfStack[-(offset)]) = (value)) ++#define SET_STACK_DOUBLE(value, offset) (((VMJavaVal64*)&topOfStack[-(offset)])->d = (value)) ++#define SET_STACK_DOUBLE_FROM_ADDR(addr, offset) (((VMJavaVal64*)&topOfStack[-(offset)])->d = \ ++ ((VMJavaVal64*)(addr))->d) ++#define SET_STACK_LONG(value, offset) (((VMJavaVal64*)&topOfStack[-(offset)])->l = (value)) ++#define SET_STACK_LONG_FROM_ADDR(addr, offset) (((VMJavaVal64*)&topOfStack[-(offset)])->l = \ ++ ((VMJavaVal64*)(addr))->l) ++// JavaLocals implementation ++ ++#define LOCALS_SLOT(offset) ((intptr_t*)&locals[-(offset)]) ++#define LOCALS_ADDR(offset) ((address)locals[-(offset)]) ++#define LOCALS_INT(offset) (*((jint*)&locals[-(offset)])) ++#define LOCALS_FLOAT(offset) (*((jfloat*)&locals[-(offset)])) ++#define LOCALS_OBJECT(offset) ((oop)locals[-(offset)]) ++#define LOCALS_DOUBLE(offset) (((VMJavaVal64*)&locals[-((offset) + 1)])->d) ++#define LOCALS_LONG(offset) (((VMJavaVal64*)&locals[-((offset) + 1)])->l) ++#define LOCALS_LONG_AT(offset) (((address)&locals[-((offset) + 1)])) ++#define LOCALS_DOUBLE_AT(offset) (((address)&locals[-((offset) + 1)])) ++ ++#define SET_LOCALS_SLOT(value, offset) (*(intptr_t*)&locals[-(offset)] = *(intptr_t *)(value)) ++#define SET_LOCALS_ADDR(value, offset) (*((address *)&locals[-(offset)]) = (value)) ++#define SET_LOCALS_INT(value, offset) (*((jint *)&locals[-(offset)]) = (value)) ++#define SET_LOCALS_FLOAT(value, offset) (*((jfloat *)&locals[-(offset)]) = (value)) ++#define SET_LOCALS_OBJECT(value, offset) (*((oop *)&locals[-(offset)]) = (value)) ++#define SET_LOCALS_DOUBLE(value, offset) (((VMJavaVal64*)&locals[-((offset)+1)])->d = (value)) ++#define SET_LOCALS_LONG(value, offset) (((VMJavaVal64*)&locals[-((offset)+1)])->l = (value)) ++#define SET_LOCALS_DOUBLE_FROM_ADDR(addr, offset) (((VMJavaVal64*)&locals[-((offset)+1)])->d = \ ++ ((VMJavaVal64*)(addr))->d) ++#define SET_LOCALS_LONG_FROM_ADDR(addr, offset) (((VMJavaVal64*)&locals[-((offset)+1)])->l = \ ++ ((VMJavaVal64*)(addr))->l) ++ ++#endif // CPU_MIPS_VM_BYTECODEINTERPRETER_MIPS_HPP +diff --git a/hotspot/src/cpu/mips/vm/bytecodeInterpreter_mips.inline.hpp b/hotspot/src/cpu/mips/vm/bytecodeInterpreter_mips.inline.hpp +new file mode 100644 +index 0000000000..8ce77ab92f +--- /dev/null ++++ b/hotspot/src/cpu/mips/vm/bytecodeInterpreter_mips.inline.hpp +@@ -0,0 +1,286 @@ ++/* ++ * Copyright (c) 2002, 2010, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2015, 2019, Loongson Technology. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ * ++ */ ++ ++#ifndef CPU_MIPS_VM_BYTECODEINTERPRETER_MIPS_INLINE_HPP ++#define CPU_MIPS_VM_BYTECODEINTERPRETER_MIPS_INLINE_HPP ++ ++// Inline interpreter functions for MIPS ++ ++inline jfloat BytecodeInterpreter::VMfloatAdd(jfloat op1, jfloat op2) { return op1 + op2; } ++inline jfloat BytecodeInterpreter::VMfloatSub(jfloat op1, jfloat op2) { return op1 - op2; } ++inline jfloat BytecodeInterpreter::VMfloatMul(jfloat op1, jfloat op2) { return op1 * op2; } ++inline jfloat BytecodeInterpreter::VMfloatDiv(jfloat op1, jfloat op2) { return op1 / op2; } ++inline jfloat BytecodeInterpreter::VMfloatRem(jfloat op1, jfloat op2) { return fmod(op1, op2); } ++ ++inline jfloat BytecodeInterpreter::VMfloatNeg(jfloat op) { return -op; } ++ ++inline int32_t BytecodeInterpreter::VMfloatCompare(jfloat op1, jfloat op2, int32_t direction) { ++ return ( op1 < op2 ? -1 : ++ op1 > op2 ? 1 : ++ op1 == op2 ? 0 : ++ (direction == -1 || direction == 1) ? direction : 0); ++ ++} ++ ++inline void BytecodeInterpreter::VMmemCopy64(uint32_t to[2], const uint32_t from[2]) { ++ // x86 can do unaligned copies but not 64bits at a time ++ to[0] = from[0]; to[1] = from[1]; ++} ++ ++// The long operations depend on compiler support for "long long" on x86 ++ ++inline jlong BytecodeInterpreter::VMlongAdd(jlong op1, jlong op2) { ++ return op1 + op2; ++} ++ ++inline jlong BytecodeInterpreter::VMlongAnd(jlong op1, jlong op2) { ++ return op1 & op2; ++} ++ ++inline jlong BytecodeInterpreter::VMlongDiv(jlong op1, jlong op2) { ++ // QQQ what about check and throw... ++ return op1 / op2; ++} ++ ++inline jlong BytecodeInterpreter::VMlongMul(jlong op1, jlong op2) { ++ return op1 * op2; ++} ++ ++inline jlong BytecodeInterpreter::VMlongOr(jlong op1, jlong op2) { ++ return op1 | op2; ++} ++ ++inline jlong BytecodeInterpreter::VMlongSub(jlong op1, jlong op2) { ++ return op1 - op2; ++} ++ ++inline jlong BytecodeInterpreter::VMlongXor(jlong op1, jlong op2) { ++ return op1 ^ op2; ++} ++ ++inline jlong BytecodeInterpreter::VMlongRem(jlong op1, jlong op2) { ++ return op1 % op2; ++} ++ ++inline jlong BytecodeInterpreter::VMlongUshr(jlong op1, jint op2) { ++ // CVM did this 0x3f mask, is the really needed??? QQQ ++ return ((unsigned long long) op1) >> (op2 & 0x3F); ++} ++ ++inline jlong BytecodeInterpreter::VMlongShr(jlong op1, jint op2) { ++ return op1 >> (op2 & 0x3F); ++} ++ ++inline jlong BytecodeInterpreter::VMlongShl(jlong op1, jint op2) { ++ return op1 << (op2 & 0x3F); ++} ++ ++inline jlong BytecodeInterpreter::VMlongNeg(jlong op) { ++ return -op; ++} ++ ++inline jlong BytecodeInterpreter::VMlongNot(jlong op) { ++ return ~op; ++} ++ ++inline int32_t BytecodeInterpreter::VMlongLtz(jlong op) { ++ return (op <= 0); ++} ++ ++inline int32_t BytecodeInterpreter::VMlongGez(jlong op) { ++ return (op >= 0); ++} ++ ++inline int32_t BytecodeInterpreter::VMlongEqz(jlong op) { ++ return (op == 0); ++} ++ ++inline int32_t BytecodeInterpreter::VMlongEq(jlong op1, jlong op2) { ++ return (op1 == op2); ++} ++ ++inline int32_t BytecodeInterpreter::VMlongNe(jlong op1, jlong op2) { ++ return (op1 != op2); ++} ++ ++inline int32_t BytecodeInterpreter::VMlongGe(jlong op1, jlong op2) { ++ return (op1 >= op2); ++} ++ ++inline int32_t BytecodeInterpreter::VMlongLe(jlong op1, jlong op2) { ++ return (op1 <= op2); ++} ++ ++inline int32_t BytecodeInterpreter::VMlongLt(jlong op1, jlong op2) { ++ return (op1 < op2); ++} ++ ++inline int32_t BytecodeInterpreter::VMlongGt(jlong op1, jlong op2) { ++ return (op1 > op2); ++} ++ ++inline int32_t BytecodeInterpreter::VMlongCompare(jlong op1, jlong op2) { ++ return (VMlongLt(op1, op2) ? -1 : VMlongGt(op1, op2) ? 1 : 0); ++} ++ ++// Long conversions ++ ++inline jdouble BytecodeInterpreter::VMlong2Double(jlong val) { ++ return (jdouble) val; ++} ++ ++inline jfloat BytecodeInterpreter::VMlong2Float(jlong val) { ++ return (jfloat) val; ++} ++ ++inline jint BytecodeInterpreter::VMlong2Int(jlong val) { ++ return (jint) val; ++} ++ ++// Double Arithmetic ++ ++inline jdouble BytecodeInterpreter::VMdoubleAdd(jdouble op1, jdouble op2) { ++ return op1 + op2; ++} ++ ++inline jdouble BytecodeInterpreter::VMdoubleDiv(jdouble op1, jdouble op2) { ++ // Divide by zero... QQQ ++ return op1 / op2; ++} ++ ++inline jdouble BytecodeInterpreter::VMdoubleMul(jdouble op1, jdouble op2) { ++ return op1 * op2; ++} ++ ++inline jdouble BytecodeInterpreter::VMdoubleNeg(jdouble op) { ++ return -op; ++} ++ ++inline jdouble BytecodeInterpreter::VMdoubleRem(jdouble op1, jdouble op2) { ++ return fmod(op1, op2); ++} ++ ++inline jdouble BytecodeInterpreter::VMdoubleSub(jdouble op1, jdouble op2) { ++ return op1 - op2; ++} ++ ++inline int32_t BytecodeInterpreter::VMdoubleCompare(jdouble op1, jdouble op2, int32_t direction) { ++ return ( op1 < op2 ? -1 : ++ op1 > op2 ? 1 : ++ op1 == op2 ? 0 : ++ (direction == -1 || direction == 1) ? direction : 0); ++} ++ ++// Double Conversions ++ ++inline jfloat BytecodeInterpreter::VMdouble2Float(jdouble val) { ++ return (jfloat) val; ++} ++ ++// Float Conversions ++ ++inline jdouble BytecodeInterpreter::VMfloat2Double(jfloat op) { ++ return (jdouble) op; ++} ++ ++// Integer Arithmetic ++ ++inline jint BytecodeInterpreter::VMintAdd(jint op1, jint op2) { ++ return op1 + op2; ++} ++ ++inline jint BytecodeInterpreter::VMintAnd(jint op1, jint op2) { ++ return op1 & op2; ++} ++ ++inline jint BytecodeInterpreter::VMintDiv(jint op1, jint op2) { ++ // it's possible we could catch this special case implicitly ++ if ((juint)op1 == 0x80000000 && op2 == -1) return op1; ++ else return op1 / op2; ++} ++ ++inline jint BytecodeInterpreter::VMintMul(jint op1, jint op2) { ++ return op1 * op2; ++} ++ ++inline jint BytecodeInterpreter::VMintNeg(jint op) { ++ return -op; ++} ++ ++inline jint BytecodeInterpreter::VMintOr(jint op1, jint op2) { ++ return op1 | op2; ++} ++ ++inline jint BytecodeInterpreter::VMintRem(jint op1, jint op2) { ++ // it's possible we could catch this special case implicitly ++ if ((juint)op1 == 0x80000000 && op2 == -1) return 0; ++ else return op1 % op2; ++} ++ ++inline jint BytecodeInterpreter::VMintShl(jint op1, jint op2) { ++ return op1 << op2; ++} ++ ++inline jint BytecodeInterpreter::VMintShr(jint op1, jint op2) { ++ return op1 >> (op2 & 0x1f); // QQ op2 & 0x1f?? ++} ++ ++inline jint BytecodeInterpreter::VMintSub(jint op1, jint op2) { ++ return op1 - op2; ++} ++ ++inline jint BytecodeInterpreter::VMintUshr(jint op1, jint op2) { ++ return ((juint) op1) >> (op2 & 0x1f); // QQ op2 & 0x1f?? ++} ++ ++inline jint BytecodeInterpreter::VMintXor(jint op1, jint op2) { ++ return op1 ^ op2; ++} ++ ++inline jdouble BytecodeInterpreter::VMint2Double(jint val) { ++ return (jdouble) val; ++} ++ ++inline jfloat BytecodeInterpreter::VMint2Float(jint val) { ++ return (jfloat) val; ++} ++ ++inline jlong BytecodeInterpreter::VMint2Long(jint val) { ++ return (jlong) val; ++} ++ ++inline jchar BytecodeInterpreter::VMint2Char(jint val) { ++ return (jchar) val; ++} ++ ++inline jshort BytecodeInterpreter::VMint2Short(jint val) { ++ return (jshort) val; ++} ++ ++inline jbyte BytecodeInterpreter::VMint2Byte(jint val) { ++ return (jbyte) val; ++} ++ ++#endif // CPU_MIPS_VM_BYTECODEINTERPRETER_MIPS_INLINE_HPP +diff --git a/hotspot/src/cpu/mips/vm/bytecodes_mips.cpp b/hotspot/src/cpu/mips/vm/bytecodes_mips.cpp +new file mode 100644 +index 0000000000..61efd1f561 +--- /dev/null ++++ b/hotspot/src/cpu/mips/vm/bytecodes_mips.cpp +@@ -0,0 +1,38 @@ ++/* ++ * Copyright (c) 1998, 2010, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2015, 2016, Loongson Technology. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ * ++ */ ++ ++#include "precompiled.hpp" ++#include "interpreter/bytecodes.hpp" ++ ++ ++void Bytecodes::pd_initialize() { ++ // No mips specific initialization ++} ++ ++ ++Bytecodes::Code Bytecodes::pd_base_code_for(Code code) { ++ // No mips specific bytecodes ++ return code; ++} +diff --git a/hotspot/src/cpu/mips/vm/bytecodes_mips.hpp b/hotspot/src/cpu/mips/vm/bytecodes_mips.hpp +new file mode 100644 +index 0000000000..25a9562acd +--- /dev/null ++++ b/hotspot/src/cpu/mips/vm/bytecodes_mips.hpp +@@ -0,0 +1,31 @@ ++/* ++ * Copyright (c) 1998, 2010, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2015, 2016, Loongson Technology. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ * ++ */ ++ ++#ifndef CPU_MIPS_VM_BYTECODES_MIPS_HPP ++#define CPU_MIPS_VM_BYTECODES_MIPS_HPP ++ ++// No Loongson specific bytecodes ++ ++#endif // CPU_MIPS_VM_BYTECODES_MIPS_HPP +diff --git a/hotspot/src/cpu/mips/vm/bytes_mips.hpp b/hotspot/src/cpu/mips/vm/bytes_mips.hpp +new file mode 100644 +index 0000000000..515ffad4b0 +--- /dev/null ++++ b/hotspot/src/cpu/mips/vm/bytes_mips.hpp +@@ -0,0 +1,193 @@ ++/* ++ * Copyright (c) 1997, 2010, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2015, 2021, Loongson Technology. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ * ++ */ ++ ++#ifndef CPU_MIPS_VM_BYTES_MIPS_HPP ++#define CPU_MIPS_VM_BYTES_MIPS_HPP ++ ++#include "memory/allocation.hpp" ++ ++class Bytes: AllStatic { ++ public: ++ // Returns true if the byte ordering used by Java is different from the native byte ordering ++ // of the underlying machine. For example, this is true for Intel x86, but false for Solaris ++ // on Sparc. ++ // we use mipsel, so return true ++ static inline bool is_Java_byte_ordering_different(){ return true; } ++ ++ ++ // Efficient reading and writing of unaligned unsigned data in platform-specific byte ordering ++ // (no special code is needed since x86 CPUs can access unaligned data) ++ static inline u2 get_native_u2(address p) { ++ if ((intptr_t)p & 0x1) { ++ return ((u2)p[1] << 8) | (u2)p[0]; ++ } else { ++ return *(u2*)p; ++ } ++ } ++ ++ static inline u4 get_native_u4(address p) { ++ if ((intptr_t)p & 3) { ++ u4 res; ++ __asm__ __volatile__ ( ++ " .set push\n" ++ " .set mips64\n" ++ " .set noreorder\n" ++ ++ " lwr %[res], 0(%[addr]) \n" ++ " lwl %[res], 3(%[addr]) \n" ++ ++ " .set pop" ++ : [res] "=&r" (res) ++ : [addr] "r" (p) ++ : "memory" ++ ); ++ return res; ++ } else { ++ return *(u4*)p; ++ } ++ } ++ ++ static inline u8 get_native_u8(address p) { ++ u8 res; ++ u8 temp; ++ // u4 tp;//tmp register ++ __asm__ __volatile__ ( ++ " .set push\n" ++ " .set mips64\n" ++ " .set noreorder\n" ++ " .set noat\n" ++ " andi $1,%[addr],0x7 \n" ++ " beqz $1,1f \n" ++ " nop \n" ++ " ldr %[temp], 0(%[addr]) \n" ++ " ldl %[temp], 7(%[addr]) \n" ++ " b 2f \n" ++ " nop \n" ++ " 1:\t ld %[temp],0(%[addr]) \n" ++ " 2:\t sd %[temp], %[res] \n" ++ ++ " .set at\n" ++ " .set pop\n" ++ : [addr]"=r"(p), [temp]"=r" (temp) ++ : "[addr]"(p), "[temp]" (temp), [res]"m" (*(volatile jint*)&res) ++ : "memory" ++ ); ++ ++ return res; ++ } ++ ++ //use mips unaligned load instructions ++ static inline void put_native_u2(address p, u2 x) { ++ if((intptr_t)p & 0x1) { ++ p[0] = (u_char)(x); ++ p[1] = (u_char)(x>>8); ++ } else { ++ *(u2*)p = x; ++ } ++ } ++ ++ static inline void put_native_u4(address p, u4 x) { ++ // refer to sparc implementation. ++ // Note that sparc is big-endian, while mips is little-endian ++ switch ( intptr_t(p) & 3 ) { ++ case 0: *(u4*)p = x; ++ break; ++ ++ case 2: ((u2*)p)[1] = x >> 16; ++ ((u2*)p)[0] = x; ++ break; ++ ++ default: ((u1*)p)[3] = x >> 24; ++ ((u1*)p)[2] = x >> 16; ++ ((u1*)p)[1] = x >> 8; ++ ((u1*)p)[0] = x; ++ break; ++ } ++ } ++ ++ static inline void put_native_u8(address p, u8 x) { ++ // refer to sparc implementation. ++ // Note that sparc is big-endian, while mips is little-endian ++ switch ( intptr_t(p) & 7 ) { ++ case 0: *(u8*)p = x; ++ break; ++ ++ case 4: ((u4*)p)[1] = x >> 32; ++ ((u4*)p)[0] = x; ++ break; ++ ++ case 2: ((u2*)p)[3] = x >> 48; ++ ((u2*)p)[2] = x >> 32; ++ ((u2*)p)[1] = x >> 16; ++ ((u2*)p)[0] = x; ++ break; ++ ++ default: ((u1*)p)[7] = x >> 56; ++ ((u1*)p)[6] = x >> 48; ++ ((u1*)p)[5] = x >> 40; ++ ((u1*)p)[4] = x >> 32; ++ ((u1*)p)[3] = x >> 24; ++ ((u1*)p)[2] = x >> 16; ++ ((u1*)p)[1] = x >> 8; ++ ((u1*)p)[0] = x; ++ } ++ } ++ ++ ++ // Efficient reading and writing of unaligned unsigned data in Java ++ // byte ordering (i.e. big-endian ordering). Byte-order reversal is ++ // needed since MIPS64EL CPUs use little-endian format. ++ static inline u2 get_Java_u2(address p) { return swap_u2(get_native_u2(p)); } ++ static inline u4 get_Java_u4(address p) { return swap_u4(get_native_u4(p)); } ++ static inline u8 get_Java_u8(address p) { return swap_u8(get_native_u8(p)); } ++ ++ static inline void put_Java_u2(address p, u2 x) { put_native_u2(p, swap_u2(x)); } ++ static inline void put_Java_u4(address p, u4 x) { put_native_u4(p, swap_u4(x)); } ++ static inline void put_Java_u8(address p, u8 x) { put_native_u8(p, swap_u8(x)); } ++ ++ ++ // Efficient swapping of byte ordering ++ static inline u2 swap_u2(u2 x); // compiler-dependent implementation ++ static inline u4 swap_u4(u4 x); // compiler-dependent implementation ++ static inline u8 swap_u8(u8 x); ++}; ++ ++ ++// The following header contains the implementations of swap_u2, swap_u4, and swap_u8[_base] ++#ifdef TARGET_OS_ARCH_linux_mips ++# include "bytes_linux_mips.inline.hpp" ++#endif ++#ifdef TARGET_OS_ARCH_solaris_mips ++# include "bytes_solaris_mips.inline.hpp" ++#endif ++#ifdef TARGET_OS_ARCH_windows_mips ++# include "bytes_windows_mips.inline.hpp" ++#endif ++#ifdef TARGET_OS_ARCH_bsd_mips ++# include "bytes_bsd_mips.inline.hpp" ++#endif ++ ++ ++#endif // CPU_MIPS_VM_BYTES_MIPS_HPP +diff --git a/hotspot/src/cpu/mips/vm/c2_globals_mips.hpp b/hotspot/src/cpu/mips/vm/c2_globals_mips.hpp +new file mode 100644 +index 0000000000..f254e07abd +--- /dev/null ++++ b/hotspot/src/cpu/mips/vm/c2_globals_mips.hpp +@@ -0,0 +1,100 @@ ++/* ++ * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2015, 2022, Loongson Technology. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ * ++ */ ++ ++#ifndef CPU_MIPS_VM_C2_GLOBALS_MIPS_HPP ++#define CPU_MIPS_VM_C2_GLOBALS_MIPS_HPP ++ ++#include "utilities/globalDefinitions.hpp" ++#include "utilities/macros.hpp" ++ ++// Sets the default values for platform dependent flags used by the server compiler. ++// (see c2_globals.hpp). Alpha-sorted. ++define_pd_global(bool, BackgroundCompilation, true); ++define_pd_global(bool, UseTLAB, true); ++define_pd_global(bool, ResizeTLAB, true); ++define_pd_global(bool, CICompileOSR, true); ++define_pd_global(bool, InlineIntrinsics, true); ++define_pd_global(bool, PreferInterpreterNativeStubs, false); ++define_pd_global(bool, ProfileTraps, true); ++define_pd_global(bool, UseOnStackReplacement, true); ++#ifdef CC_INTERP ++define_pd_global(bool, ProfileInterpreter, false); ++#else ++define_pd_global(bool, ProfileInterpreter, true); ++#endif // CC_INTERP ++define_pd_global(bool, TieredCompilation, false); // Disable C1 in server JIT ++define_pd_global(intx, CompileThreshold, 10000); ++define_pd_global(intx, BackEdgeThreshold, 100000); ++ ++define_pd_global(intx, OnStackReplacePercentage, 140); ++define_pd_global(intx, ConditionalMoveLimit, 3); ++define_pd_global(intx, FLOATPRESSURE, 6); ++define_pd_global(intx, FreqInlineSize, 325); ++define_pd_global(intx, MinJumpTableSize, 10); ++#ifdef MIPS64 ++define_pd_global(intx, INTPRESSURE, 13); ++define_pd_global(intx, InteriorEntryAlignment, 16); ++define_pd_global(intx, NewSizeThreadIncrease, ScaleForWordSize(4*K)); ++define_pd_global(intx, LoopUnrollLimit, 60); ++// InitialCodeCacheSize derived from specjbb2000 run. ++define_pd_global(intx, InitialCodeCacheSize, 2496*K); // Integral multiple of CodeCacheExpansionSize ++define_pd_global(intx, CodeCacheExpansionSize, 64*K); ++ ++// Ergonomics related flags ++define_pd_global(uint64_t,MaxRAM, 128ULL*G); ++#else ++define_pd_global(intx, INTPRESSURE, 6); ++define_pd_global(intx, InteriorEntryAlignment, 4); ++define_pd_global(intx, NewSizeThreadIncrease, 4*K); ++define_pd_global(intx, LoopUnrollLimit, 50); // Design center runs on 1.3.1 ++// InitialCodeCacheSize derived from specjbb2000 run. ++define_pd_global(intx, InitialCodeCacheSize, 2304*K); // Integral multiple of CodeCacheExpansionSize ++define_pd_global(intx, CodeCacheExpansionSize, 32*K); ++ ++// Ergonomics related flags ++define_pd_global(uint64_t,MaxRAM, 4ULL*G); ++#endif // MIPS64 ++define_pd_global(intx, RegisterCostAreaRatio, 16000); ++ ++// Peephole and CISC spilling both break the graph, and so makes the ++// scheduler sick. ++define_pd_global(bool, OptoPeephole, false); ++define_pd_global(bool, UseCISCSpill, false); ++define_pd_global(bool, OptoScheduling, false); ++define_pd_global(bool, OptoBundling, false); ++ ++define_pd_global(intx, ReservedCodeCacheSize, 120*M); ++define_pd_global(uintx, CodeCacheMinBlockLength, 4); ++define_pd_global(uintx, CodeCacheMinimumUseSpace, 400*K); ++ ++define_pd_global(bool, TrapBasedRangeChecks, false); // Not needed on x86. ++ ++// Heap related flags ++define_pd_global(uintx,MetaspaceSize, ScaleForWordSize(16*M)); ++ ++// Ergonomics related flags ++define_pd_global(bool, NeverActAsServerClassMachine, false); ++ ++#endif // CPU_MIPS_VM_C2_GLOBALS_MIPS_HPP +diff --git a/hotspot/src/cpu/mips/vm/c2_init_mips.cpp b/hotspot/src/cpu/mips/vm/c2_init_mips.cpp +new file mode 100644 +index 0000000000..e6d5815f42 +--- /dev/null ++++ b/hotspot/src/cpu/mips/vm/c2_init_mips.cpp +@@ -0,0 +1,34 @@ ++/* ++ * Copyright (c) 2000, 2010, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2015, 2019, Loongson Technology. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ * ++ */ ++ ++#include "precompiled.hpp" ++#include "opto/compile.hpp" ++#include "opto/node.hpp" ++ ++// processor dependent initialization for mips ++ ++void Compile::pd_compiler2_init() { ++ guarantee(CodeEntryAlignment >= InteriorEntryAlignment, "" ); ++} +diff --git a/hotspot/src/cpu/mips/vm/codeBuffer_mips.hpp b/hotspot/src/cpu/mips/vm/codeBuffer_mips.hpp +new file mode 100644 +index 0000000000..1836b7a921 +--- /dev/null ++++ b/hotspot/src/cpu/mips/vm/codeBuffer_mips.hpp +@@ -0,0 +1,35 @@ ++/* ++ * Copyright (c) 2002, 2010, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2015, 2017, Loongson Technology. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ * ++ */ ++ ++#ifndef CPU_MIPS_VM_CODEBUFFER_MIPS_HPP ++#define CPU_MIPS_VM_CODEBUFFER_MIPS_HPP ++ ++private: ++ void pd_initialize() {} ++ ++public: ++ void flush_bundle(bool start_new_bundle) {} ++ ++#endif // CPU_MIPS_VM_CODEBUFFER_MIPS_HPP +diff --git a/hotspot/src/cpu/mips/vm/compiledIC_mips.cpp b/hotspot/src/cpu/mips/vm/compiledIC_mips.cpp +new file mode 100644 +index 0000000000..8ffaaaf841 +--- /dev/null ++++ b/hotspot/src/cpu/mips/vm/compiledIC_mips.cpp +@@ -0,0 +1,173 @@ ++/* ++ * Copyright (c) 1997, 2014, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2015, 2021, Loongson Technology. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ * ++ */ ++ ++#include "precompiled.hpp" ++#include "asm/macroAssembler.inline.hpp" ++#include "code/compiledIC.hpp" ++#include "code/icBuffer.hpp" ++#include "code/nmethod.hpp" ++#include "memory/resourceArea.hpp" ++#include "runtime/mutexLocker.hpp" ++#include "runtime/safepoint.hpp" ++ ++// Release the CompiledICHolder* associated with this call site is there is one. ++void CompiledIC::cleanup_call_site(virtual_call_Relocation* call_site) { ++ // This call site might have become stale so inspect it carefully. ++ NativeCall* call = nativeCall_at(call_site->addr()); ++ if (is_icholder_entry(call->destination())) { ++ NativeMovConstReg* value = nativeMovConstReg_at(call_site->cached_value()); ++ InlineCacheBuffer::queue_for_release((CompiledICHolder*)value->data()); ++ } ++} ++ ++bool CompiledIC::is_icholder_call_site(virtual_call_Relocation* call_site) { ++ // This call site might have become stale so inspect it carefully. ++ NativeCall* call = nativeCall_at(call_site->addr()); ++ return is_icholder_entry(call->destination()); ++} ++ ++// ---------------------------------------------------------------------------- ++ ++#define __ _masm. ++address CompiledStaticCall::emit_to_interp_stub(CodeBuffer &cbuf) { ++ ++ address mark = cbuf.insts_mark(); // get mark within main instrs section ++ ++ // Note that the code buffer's insts_mark is always relative to insts. ++ // That's why we must use the macroassembler to generate a stub. ++ MacroAssembler _masm(&cbuf); ++ ++ address base = __ start_a_stub(CompiledStaticCall::to_interp_stub_size()); ++ if (base == NULL) return NULL; // CodeBuffer::expand failed ++ // static stub relocation stores the instruction address of the call ++ ++ __ relocate(static_stub_Relocation::spec(mark), 0); ++ ++ // Code stream for loading method may be changed. ++ __ synci(R0, 0); ++ ++ // Rmethod contains methodOop, it should be relocated for GC ++ // static stub relocation also tags the methodOop in the code-stream. ++ __ mov_metadata(Rmethod, NULL); ++ // This is recognized as unresolved by relocs/nativeInst/ic code ++ ++ __ relocate(relocInfo::runtime_call_type); ++ ++ cbuf.set_insts_mark(); ++ address call_pc = (address)-1; ++ __ patchable_jump(call_pc); ++ __ align(16); ++ // Update current stubs pointer and restore code_end. ++ __ end_a_stub(); ++ return base; ++} ++#undef __ ++ ++int CompiledStaticCall::to_interp_stub_size() { ++ int size = NativeInstruction::nop_instruction_size + NativeMovConstReg::instruction_size + NativeCall::instruction_size; ++ return round_to(size, 16); ++} ++ ++// Relocation entries for call stub, compiled java to interpreter. ++int CompiledStaticCall::reloc_to_interp_stub() { ++ return 16; ++} ++ ++void CompiledStaticCall::set_to_interpreted(methodHandle callee, address entry) { ++ address stub = find_stub(); ++ guarantee(stub != NULL, "stub not found"); ++ ++ if (TraceICs) { ++ ResourceMark rm; ++ tty->print_cr("CompiledStaticCall@" INTPTR_FORMAT ": set_to_interpreted %s", ++ p2i(instruction_address()), ++ callee->name_and_sig_as_C_string()); ++ } ++ ++ // Creation also verifies the object. ++ NativeMovConstReg* method_holder = nativeMovConstReg_at(stub + NativeInstruction::nop_instruction_size); ++#ifndef MIPS64 ++ NativeJump* jump = nativeJump_at(method_holder->next_instruction_address()); ++#else ++ NativeGeneralJump* jump = nativeGeneralJump_at(method_holder->next_instruction_address()); ++#endif ++ ++ assert(method_holder->data() == 0 || method_holder->data() == (intptr_t)callee(), ++ "a) MT-unsafe modification of inline cache"); ++ assert(jump->jump_destination() == (address)-1 || jump->jump_destination() == entry, ++ "b) MT-unsafe modification of inline cache"); ++ ++ // Update stub. ++ method_holder->set_data((intptr_t)callee()); ++ jump->set_jump_destination(entry); ++ ++ // Update jump to call. ++ set_destination_mt_safe(stub); ++} ++ ++void CompiledStaticCall::set_stub_to_clean(static_stub_Relocation* static_stub) { ++ assert (CompiledIC_lock->is_locked() || SafepointSynchronize::is_at_safepoint(), "mt unsafe call"); ++ // Reset stub. ++ address stub = static_stub->addr(); ++ assert(stub != NULL, "stub not found"); ++ // Creation also verifies the object. ++ NativeMovConstReg* method_holder = nativeMovConstReg_at(stub + NativeInstruction::nop_instruction_size); ++#ifndef MIPS64 ++ NativeJump* jump = nativeJump_at(method_holder->next_instruction_address()); ++#else ++ NativeGeneralJump* jump = nativeGeneralJump_at(method_holder->next_instruction_address()); ++#endif ++ method_holder->set_data(0); ++ jump->set_jump_destination((address)-1); ++} ++ ++//----------------------------------------------------------------------------- ++// Non-product mode code ++#ifndef PRODUCT ++ ++void CompiledStaticCall::verify() { ++ // Verify call. ++ NativeCall::verify(); ++ if (os::is_MP()) { ++ verify_alignment(); ++ } ++ ++ // Verify stub. ++ address stub = find_stub(); ++ assert(stub != NULL, "no stub found for static call"); ++ // Creation also verifies the object. ++ NativeMovConstReg* method_holder = nativeMovConstReg_at(stub + NativeInstruction::nop_instruction_size); ++#ifndef MIPS64 ++ NativeJump* jump = nativeJump_at(method_holder->next_instruction_address()); ++#else ++ NativeGeneralJump* jump = nativeGeneralJump_at(method_holder->next_instruction_address()); ++#endif ++ ++ ++ // Verify state. ++ assert(is_clean() || is_call_to_compiled() || is_call_to_interpreted(), "sanity check"); ++} ++ ++#endif // !PRODUCT +diff --git a/hotspot/src/cpu/mips/vm/copy_mips.hpp b/hotspot/src/cpu/mips/vm/copy_mips.hpp +new file mode 100644 +index 0000000000..49fde17923 +--- /dev/null ++++ b/hotspot/src/cpu/mips/vm/copy_mips.hpp +@@ -0,0 +1,72 @@ ++/* ++ * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2015, 2016, Loongson Technology. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ * ++ */ ++ ++#ifndef CPU_MIPS_VM_COPY_MIPS_HPP ++#define CPU_MIPS_VM_COPY_MIPS_HPP ++ ++// Inline functions for memory copy and fill. ++ ++// Contains inline asm implementations ++#ifdef TARGET_OS_ARCH_linux_mips ++# include "copy_linux_mips.inline.hpp" ++#endif ++#ifdef TARGET_OS_ARCH_solaris_mips ++# include "copy_solaris_mips.inline.hpp" ++#endif ++#ifdef TARGET_OS_ARCH_windows_mips ++# include "copy_windows_mips.inline.hpp" ++#endif ++#ifdef TARGET_OS_ARCH_bsd_mips ++# include "copy_bsd_mips.inline.hpp" ++#endif ++// Inline functions for memory copy and fill. ++ ++// Contains inline asm implementations ++ ++static void pd_fill_to_words(HeapWord* tohw, size_t count, juint value) { ++ julong* to = (julong*) tohw; ++ julong v = ((julong) value << 32) | value; ++ while (count-- > 0) { ++ *to++ = v; ++ } ++} ++ ++static void pd_fill_to_aligned_words(HeapWord* tohw, size_t count, juint value) { ++ pd_fill_to_words(tohw, count, value); ++} ++ ++static void pd_fill_to_bytes(void* to, size_t count, jubyte value) { ++ (void)memset(to, value, count); ++} ++ ++static void pd_zero_to_words(HeapWord* tohw, size_t count) { ++ pd_fill_to_words(tohw, count, 0); ++} ++ ++static void pd_zero_to_bytes(void* to, size_t count) { ++ (void)memset(to, 0, count); ++} ++ ++#endif //CPU_MIPS_VM_COPY_MIPS_HPP +diff --git a/hotspot/src/cpu/mips/vm/cppInterpreterGenerator_mips.hpp b/hotspot/src/cpu/mips/vm/cppInterpreterGenerator_mips.hpp +new file mode 100644 +index 0000000000..37bd03b00b +--- /dev/null ++++ b/hotspot/src/cpu/mips/vm/cppInterpreterGenerator_mips.hpp +@@ -0,0 +1,53 @@ ++/* ++ * Copyright (c) 1997, 2011, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2015, 2016, Loongson Technology. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ * ++ */ ++ ++#ifndef CPU_MIPS_VM_CPPINTERPRETERGENERATOR_MIPS_HPP ++#define CPU_MIPS_VM_CPPINTERPRETERGENERATOR_MIPS_HPP ++ ++ protected: ++ ++#if 0 ++ address generate_asm_interpreter_entry(bool synchronized); ++ address generate_native_entry(bool synchronized); ++ address generate_abstract_entry(void); ++ address generate_math_entry(AbstractInterpreter::MethodKind kind); ++ address generate_empty_entry(void); ++ address generate_accessor_entry(void); ++ void lock_method(void); ++ void generate_stack_overflow_check(void); ++ ++ void generate_counter_incr(Label* overflow, Label* profile_method, Label* profile_method_continue); ++ void generate_counter_overflow(Label* do_continue); ++#endif ++ ++ void generate_more_monitors(); ++ void generate_deopt_handling(); ++ address generate_interpreter_frame_manager(bool synchronized); // C++ interpreter only ++ void generate_compute_interpreter_state(const Register state, ++ const Register prev_state, ++ const Register sender_sp, ++ bool native); // C++ interpreter only ++ ++#endif // CPU_MIPS_VM_CPPINTERPRETERGENERATOR_MIPS_HPP +diff --git a/hotspot/src/cpu/mips/vm/cppInterpreter_mips.cpp b/hotspot/src/cpu/mips/vm/cppInterpreter_mips.cpp +new file mode 100644 +index 0000000000..1f8d75d593 +--- /dev/null ++++ b/hotspot/src/cpu/mips/vm/cppInterpreter_mips.cpp +@@ -0,0 +1,215 @@ ++/* ++ * Copyright (c) 2007, 2013, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2015, 2016, Loongson Technology. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ * ++ */ ++ ++#include "precompiled.hpp" ++#include "asm/macroAssembler.hpp" ++#include "interpreter/bytecodeHistogram.hpp" ++#include "interpreter/cppInterpreter.hpp" ++#include "interpreter/interpreter.hpp" ++#include "interpreter/interpreterGenerator.hpp" ++#include "interpreter/interpreterRuntime.hpp" ++#include "oops/arrayOop.hpp" ++#include "oops/methodData.hpp" ++#include "oops/method.hpp" ++#include "oops/oop.inline.hpp" ++#include "prims/jvmtiExport.hpp" ++#include "prims/jvmtiThreadState.hpp" ++#include "runtime/arguments.hpp" ++#include "runtime/deoptimization.hpp" ++#include "runtime/frame.inline.hpp" ++#include "runtime/interfaceSupport.hpp" ++#include "runtime/sharedRuntime.hpp" ++#include "runtime/stubRoutines.hpp" ++#include "runtime/synchronizer.hpp" ++#include "runtime/timer.hpp" ++#include "runtime/vframeArray.hpp" ++#include "utilities/debug.hpp" ++#ifdef SHARK ++#include "shark/shark_globals.hpp" ++#endif ++ ++#ifdef CC_INTERP ++ ++// Routine exists to make tracebacks look decent in debugger ++// while "shadow" interpreter frames are on stack. It is also ++// used to distinguish interpreter frames. ++ ++extern "C" void RecursiveInterpreterActivation(interpreterState istate) { ++ ShouldNotReachHere(); ++} ++ ++bool CppInterpreter::contains(address pc) { ++ Unimplemented(); ++} ++ ++#define STATE(field_name) Lstate, in_bytes(byte_offset_of(BytecodeInterpreter, field_name)) ++#define __ _masm-> ++ ++Label frame_manager_entry; ++Label fast_accessor_slow_entry_path; // fast accessor methods need to be able to jmp to unsynchronized ++ // c++ interpreter entry point this holds that entry point label. ++ ++static address unctrap_frame_manager_entry = NULL; ++ ++static address interpreter_return_address = NULL; ++static address deopt_frame_manager_return_atos = NULL; ++static address deopt_frame_manager_return_btos = NULL; ++static address deopt_frame_manager_return_itos = NULL; ++static address deopt_frame_manager_return_ltos = NULL; ++static address deopt_frame_manager_return_ftos = NULL; ++static address deopt_frame_manager_return_dtos = NULL; ++static address deopt_frame_manager_return_vtos = NULL; ++ ++const Register prevState = G1_scratch; ++ ++void InterpreterGenerator::save_native_result(void) { ++ Unimplemented(); ++} ++ ++void InterpreterGenerator::restore_native_result(void) { ++ Unimplemented(); ++} ++ ++// A result handler converts/unboxes a native call result into ++// a java interpreter/compiler result. The current frame is an ++// interpreter frame. The activation frame unwind code must be ++// consistent with that of TemplateTable::_return(...). In the ++// case of native methods, the caller's SP was not modified. ++address CppInterpreterGenerator::generate_result_handler_for(BasicType type) { ++ Unimplemented(); ++} ++ ++address CppInterpreterGenerator::generate_tosca_to_stack_converter(BasicType type) { ++ Unimplemented(); ++} ++ ++address CppInterpreterGenerator::generate_stack_to_stack_converter(BasicType type) { ++ Unimplemented(); ++} ++ ++address CppInterpreterGenerator::generate_stack_to_native_abi_converter(BasicType type) { ++ Unimplemented(); ++} ++ ++address CppInterpreter::return_entry(TosState state, int length) { ++ Unimplemented(); ++} ++ ++address CppInterpreter::deopt_entry(TosState state, int length) { ++ Unimplemented(); ++} ++ ++void InterpreterGenerator::generate_counter_incr(Label* overflow, Label* profile_method, Label* profile_method_continue) { ++ Unimplemented(); ++} ++ ++address InterpreterGenerator::generate_empty_entry(void) { ++ Unimplemented(); ++} ++ ++address InterpreterGenerator::generate_accessor_entry(void) { ++ Unimplemented(); ++} ++ ++address InterpreterGenerator::generate_native_entry(bool synchronized) { ++ Unimplemented(); ++} ++ ++void CppInterpreterGenerator::generate_compute_interpreter_state(const Register state, ++ const Register prev_state, ++ bool native) { ++ Unimplemented(); ++} ++ ++void InterpreterGenerator::lock_method(void) { ++ Unimplemented(); ++} ++ ++void CppInterpreterGenerator::generate_deopt_handling() { ++ Unimplemented(); ++} ++ ++void CppInterpreterGenerator::generate_more_monitors() { ++ Unimplemented(); ++} ++ ++ ++static address interpreter_frame_manager = NULL; ++ ++void CppInterpreterGenerator::adjust_callers_stack(Register args) { ++ Unimplemented(); ++} ++ ++address InterpreterGenerator::generate_normal_entry(bool synchronized) { ++ Unimplemented(); ++} ++ ++InterpreterGenerator::InterpreterGenerator(StubQueue* code) ++ : CppInterpreterGenerator(code) { ++ Unimplemented(); ++} ++ ++ ++static int size_activation_helper(int callee_extra_locals, int max_stack, int monitor_size) { ++ Unimplemented(); ++} ++ ++int AbstractInterpreter::size_top_interpreter_activation(methodOop method) { ++ Unimplemented(); ++} ++ ++void BytecodeInterpreter::layout_interpreterState(interpreterState to_fill, ++ frame* caller, ++ frame* current, ++ methodOop method, ++ intptr_t* locals, ++ intptr_t* stack, ++ intptr_t* stack_base, ++ intptr_t* monitor_base, ++ intptr_t* frame_bottom, ++ bool is_top_frame ++ ) ++{ ++ Unimplemented(); ++} ++ ++void BytecodeInterpreter::pd_layout_interpreterState(interpreterState istate, address last_Java_pc, intptr_t* last_Java_fp) { ++ Unimplemented(); ++} ++ ++ ++int AbstractInterpreter::layout_activation(methodOop method, ++ int tempcount, // Number of slots on java expression stack in use ++ int popframe_extra_args, ++ int moncount, // Number of active monitors ++ int callee_param_size, ++ int callee_locals_size, ++ frame* caller, ++ frame* interpreter_frame, ++ bool is_top_frame) { ++ Unimplemented(); ++} ++ ++#endif // CC_INTERP +diff --git a/hotspot/src/cpu/mips/vm/cppInterpreter_mips.hpp b/hotspot/src/cpu/mips/vm/cppInterpreter_mips.hpp +new file mode 100644 +index 0000000000..49c4733049 +--- /dev/null ++++ b/hotspot/src/cpu/mips/vm/cppInterpreter_mips.hpp +@@ -0,0 +1,40 @@ ++/* ++ * Copyright (c) 1997, 2010, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2015, 2022, Loongson Technology. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ * ++ */ ++ ++#ifndef CPU_MIPS_VM_CPPINTERPRETER_MIPS_HPP ++#define CPU_MIPS_VM_CPPINTERPRETER_MIPS_HPP ++ // Size of interpreter code. Increase if too small. Interpreter will ++ // fail with a guarantee ("not enough space for interpreter generation"); ++ // if too small. ++ // Run with +PrintInterpreter to get the VM to print out the size. ++ // Max size with JVMTI and TaggedStackInterpreter ++ ++ // QQQ this is proably way too large for c++ interpreter ++ ++ // The sethi() instruction generates lots more instructions when shell ++ // stack limit is unlimited, so that's why this is much bigger. ++ const static int InterpreterCodeSize = 210 * K; ++ ++#endif // CPU_MIPS_VM_CPPINTERPRETER_MIPS_HPP +diff --git a/hotspot/src/cpu/mips/vm/debug_mips.cpp b/hotspot/src/cpu/mips/vm/debug_mips.cpp +new file mode 100644 +index 0000000000..50de03653b +--- /dev/null ++++ b/hotspot/src/cpu/mips/vm/debug_mips.cpp +@@ -0,0 +1,51 @@ ++/* ++ * Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2015, 2021, Loongson Technology. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ * ++ */ ++ ++#include "precompiled.hpp" ++#include "code/codeCache.hpp" ++#include "code/nmethod.hpp" ++#include "runtime/frame.hpp" ++#include "runtime/init.hpp" ++#include "runtime/os.hpp" ++#include "utilities/debug.hpp" ++#include "utilities/top.hpp" ++ ++#ifndef PRODUCT ++ ++void pd_ps(frame f) { ++ intptr_t* sp = f.sp(); ++ intptr_t* prev_sp = sp - 1; ++ intptr_t *pc = NULL; ++ intptr_t *next_pc = NULL; ++ int count = 0; ++ tty->print("register window backtrace from %#lx:\n", p2i(sp)); ++} ++ ++// This function is used to add platform specific info ++// to the error reporting code. ++ ++void pd_obfuscate_location(char *buf,int buflen) {} ++ ++#endif // PRODUCT +diff --git a/hotspot/src/cpu/mips/vm/depChecker_mips.cpp b/hotspot/src/cpu/mips/vm/depChecker_mips.cpp +new file mode 100644 +index 0000000000..756ccb68f9 +--- /dev/null ++++ b/hotspot/src/cpu/mips/vm/depChecker_mips.cpp +@@ -0,0 +1,30 @@ ++/* ++ * Copyright (c) 2002, 2010, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2015, 2016, Loongson Technology. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ * ++ */ ++ ++#include "precompiled.hpp" ++#include "compiler/disassembler.hpp" ++#include "depChecker_mips.hpp" ++ ++// Nothing to do on mips +diff --git a/hotspot/src/cpu/mips/vm/depChecker_mips.hpp b/hotspot/src/cpu/mips/vm/depChecker_mips.hpp +new file mode 100644 +index 0000000000..11e52b4e8f +--- /dev/null ++++ b/hotspot/src/cpu/mips/vm/depChecker_mips.hpp +@@ -0,0 +1,31 @@ ++/* ++ * Copyright (c) 2002, 2010, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2015, 2016, Loongson Technology. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ * ++ */ ++ ++#ifndef CPU_MIPS_VM_DEPCHECKER_MIPS_HPP ++#define CPU_MIPS_VM_DEPCHECKER_MIPS_HPP ++ ++// Nothing to do on MIPS ++ ++#endif // CPU_MIPS_VM_DEPCHECKER_MIPS_HPP +diff --git a/hotspot/src/cpu/mips/vm/disassembler_mips.hpp b/hotspot/src/cpu/mips/vm/disassembler_mips.hpp +new file mode 100644 +index 0000000000..c5f3a8888d +--- /dev/null ++++ b/hotspot/src/cpu/mips/vm/disassembler_mips.hpp +@@ -0,0 +1,37 @@ ++/* ++ * Copyright (c) 1997, 2010, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2015, 2022, Loongson Technology. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ * ++ */ ++ ++#ifndef CPU_MIPS_VM_DISASSEMBLER_MIPS_HPP ++#define CPU_MIPS_VM_DISASSEMBLER_MIPS_HPP ++ ++ static int pd_instruction_alignment() { ++ return sizeof(int); ++ } ++ ++ static const char* pd_cpu_opts() { ++ return "gpr-names=64"; ++ } ++ ++#endif // CPU_MIPS_VM_DISASSEMBLER_MIPS_HPP +diff --git a/hotspot/src/cpu/mips/vm/frame_mips.cpp b/hotspot/src/cpu/mips/vm/frame_mips.cpp +new file mode 100644 +index 0000000000..1c928976fc +--- /dev/null ++++ b/hotspot/src/cpu/mips/vm/frame_mips.cpp +@@ -0,0 +1,711 @@ ++/* ++ * Copyright (c) 1997, 2014, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2015, 2022, Loongson Technology. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ * ++ */ ++ ++#include "precompiled.hpp" ++#include "interpreter/interpreter.hpp" ++#include "memory/resourceArea.hpp" ++#include "oops/markOop.hpp" ++#include "oops/method.hpp" ++#include "oops/oop.inline.hpp" ++#include "prims/methodHandles.hpp" ++#include "runtime/frame.inline.hpp" ++#include "runtime/handles.inline.hpp" ++#include "runtime/javaCalls.hpp" ++#include "runtime/monitorChunk.hpp" ++#include "runtime/signature.hpp" ++#include "runtime/stubCodeGenerator.hpp" ++#include "runtime/stubRoutines.hpp" ++#include "vmreg_mips.inline.hpp" ++ ++#ifdef ASSERT ++void RegisterMap::check_location_valid() { ++} ++#endif ++ ++ ++// Profiling/safepoint support ++// for Profiling - acting on another frame. walks sender frames ++// if valid. ++// frame profile_find_Java_sender_frame(JavaThread *thread); ++ ++bool frame::safe_for_sender(JavaThread *thread) { ++ address sp = (address)_sp; ++ address fp = (address)_fp; ++ address unextended_sp = (address)_unextended_sp; ++ ++ // consider stack guards when trying to determine "safe" stack pointers ++ static size_t stack_guard_size = os::uses_stack_guard_pages() ? (StackYellowPages + StackRedPages) * os::vm_page_size() : 0; ++ size_t usable_stack_size = thread->stack_size() - stack_guard_size; ++ ++ // sp must be within the usable part of the stack (not in guards) ++ bool sp_safe = (sp < thread->stack_base()) && ++ (sp >= thread->stack_base() - usable_stack_size); ++ ++ ++ if (!sp_safe) { ++ return false; ++ } ++ ++ // unextended sp must be within the stack and above or equal sp ++ bool unextended_sp_safe = (unextended_sp < thread->stack_base()) && ++ (unextended_sp >= sp); ++ ++ if (!unextended_sp_safe) { ++ return false; ++ } ++ ++ // an fp must be within the stack and above (but not equal) sp ++ // second evaluation on fp+ is added to handle situation where fp is -1 ++ bool fp_safe = (fp < thread->stack_base() && (fp > sp) && (((fp + (return_addr_offset * sizeof(void*))) < thread->stack_base()))); ++ ++ // We know sp/unextended_sp are safe only fp is questionable here ++ ++ // If the current frame is known to the code cache then we can attempt to ++ // construct the sender and do some validation of it. This goes a long way ++ // toward eliminating issues when we get in frame construction code ++ ++ if (_cb != NULL ) { ++ ++ // First check if frame is complete and tester is reliable ++ // Unfortunately we can only check frame complete for runtime stubs and nmethod ++ // other generic buffer blobs are more problematic so we just assume they are ++ // ok. adapter blobs never have a frame complete and are never ok. ++ ++ if (!_cb->is_frame_complete_at(_pc)) { ++ if (_cb->is_nmethod() || _cb->is_adapter_blob() || _cb->is_runtime_stub()) { ++ return false; ++ } ++ } ++ ++ // Could just be some random pointer within the codeBlob ++ if (!_cb->code_contains(_pc)) { ++ return false; ++ } ++ ++ // Entry frame checks ++ if (is_entry_frame()) { ++ // an entry frame must have a valid fp. ++ return fp_safe && is_entry_frame_valid(thread); ++ } ++ ++ intptr_t* sender_sp = NULL; ++ intptr_t* sender_unextended_sp = NULL; ++ address sender_pc = NULL; ++ intptr_t* saved_fp = NULL; ++ ++ if (is_interpreted_frame()) { ++ // fp must be safe ++ if (!fp_safe) { ++ return false; ++ } ++ ++ sender_pc = (address) this->fp()[return_addr_offset]; ++ // for interpreted frames, the value below is the sender "raw" sp, ++ // which can be different from the sender unextended sp (the sp seen ++ // by the sender) because of current frame local variables ++ sender_sp = (intptr_t*) addr_at(sender_sp_offset); ++ sender_unextended_sp = (intptr_t*) this->fp()[interpreter_frame_sender_sp_offset]; ++ saved_fp = (intptr_t*) this->fp()[link_offset]; ++ ++ } else { ++ // must be some sort of compiled/runtime frame ++ // fp does not have to be safe (although it could be check for c1?) ++ ++ // check for a valid frame_size, otherwise we are unlikely to get a valid sender_pc ++ if (_cb->frame_size() <= 0) { ++ return false; ++ } ++ ++ sender_sp = _unextended_sp + _cb->frame_size(); ++ sender_unextended_sp = sender_sp; ++ // On MIPS the return_address is always the word on the stack ++ sender_pc = (address) *(sender_sp-1); ++ // Note: frame::sender_sp_offset is only valid for compiled frame ++ saved_fp = (intptr_t*) *(sender_sp - frame::sender_sp_offset); ++ } ++ ++ ++ // If the potential sender is the interpreter then we can do some more checking ++ if (Interpreter::contains(sender_pc)) { ++ ++ // FP is always saved in a recognizable place in any code we generate. However ++ // only if the sender is interpreted/call_stub (c1 too?) are we certain that the saved FP ++ // is really a frame pointer. ++ ++ bool saved_fp_safe = ((address)saved_fp < thread->stack_base()) && (saved_fp > sender_sp); ++ ++ if (!saved_fp_safe) { ++ return false; ++ } ++ ++ // construct the potential sender ++ ++ frame sender(sender_sp, sender_unextended_sp, saved_fp, sender_pc); ++ ++ return sender.is_interpreted_frame_valid(thread); ++ ++ } ++ ++ // We must always be able to find a recognizable pc ++ CodeBlob* sender_blob = CodeCache::find_blob_unsafe(sender_pc); ++ if (sender_pc == NULL || sender_blob == NULL) { ++ return false; ++ } ++ ++ // Could be a zombie method ++ if (sender_blob->is_zombie() || sender_blob->is_unloaded()) { ++ return false; ++ } ++ ++ // Could just be some random pointer within the codeBlob ++ if (!sender_blob->code_contains(sender_pc)) { ++ return false; ++ } ++ ++ // We should never be able to see an adapter if the current frame is something from code cache ++ if (sender_blob->is_adapter_blob()) { ++ return false; ++ } ++ ++ // Could be the call_stub ++ if (StubRoutines::returns_to_call_stub(sender_pc)) { ++ bool saved_fp_safe = ((address)saved_fp < thread->stack_base()) && (saved_fp > sender_sp); ++ ++ if (!saved_fp_safe) { ++ return false; ++ } ++ ++ // construct the potential sender ++ ++ frame sender(sender_sp, sender_unextended_sp, saved_fp, sender_pc); ++ ++ // Validate the JavaCallWrapper an entry frame must have ++ address jcw = (address)sender.entry_frame_call_wrapper(); ++ ++ bool jcw_safe = (jcw < thread->stack_base()) && ( jcw > (address)sender.fp()); ++ ++ return jcw_safe; ++ } ++ ++ if (sender_blob->is_nmethod()) { ++ nmethod* nm = sender_blob->as_nmethod_or_null(); ++ if (nm != NULL) { ++ if (nm->is_deopt_mh_entry(sender_pc) || nm->is_deopt_entry(sender_pc)) { ++ return false; ++ } ++ } ++ } ++ ++ // If the frame size is 0 something (or less) is bad because every nmethod has a non-zero frame size ++ // because the return address counts against the callee's frame. ++ ++ if (sender_blob->frame_size() <= 0) { ++ assert(!sender_blob->is_nmethod(), "should count return address at least"); ++ return false; ++ } ++ ++ // We should never be able to see anything here except an nmethod. If something in the ++ // code cache (current frame) is called by an entity within the code cache that entity ++ // should not be anything but the call stub (already covered), the interpreter (already covered) ++ // or an nmethod. ++ ++ if (!sender_blob->is_nmethod()) { ++ return false; ++ } ++ ++ // Could put some more validation for the potential non-interpreted sender ++ // frame we'd create by calling sender if I could think of any. Wait for next crash in forte... ++ ++ // One idea is seeing if the sender_pc we have is one that we'd expect to call to current cb ++ ++ // We've validated the potential sender that would be created ++ return true; ++ } ++ // Note: fp == NULL is not really a prerequisite for this to be safe to ++ // walk for c2. However we've modified the code such that if we get ++ // a failure with fp != NULL that we then try with FP == NULL. ++ // This is basically to mimic what a last_frame would look like if ++ // c2 had generated it. ++ ++ // Must be native-compiled frame. Since sender will try and use fp to find ++ // linkages it must be safe ++ ++ if (!fp_safe) { ++ return false; ++ } ++ ++ // Will the pc we fetch be non-zero (which we'll find at the oldest frame) ++ ++ if ( (address) this->fp()[return_addr_offset] == NULL) return false; ++ ++ ++ // could try and do some more potential verification of native frame if we could think of some... ++ ++ return true; ++ ++} ++ ++void frame::patch_pc(Thread* thread, address pc) { ++ assert(_cb == CodeCache::find_blob(pc), "unexpected pc"); ++ address* pc_addr = &(((address*) sp())[-1]); ++ if (TracePcPatching) { ++ tty->print_cr("patch_pc at address " INTPTR_FORMAT " [" INTPTR_FORMAT " -> " INTPTR_FORMAT "]", ++ p2i(pc_addr), p2i(*pc_addr), p2i(pc)); ++ } ++ ++ // Either the return address is the original one or we are going to ++ // patch in the same address that's already there. ++ assert(_pc == *pc_addr || pc == *pc_addr, "must be"); ++ *pc_addr = pc; ++ _cb = CodeCache::find_blob(pc); ++ address original_pc = nmethod::get_deopt_original_pc(this); ++ if (original_pc != NULL) { ++ assert(original_pc == _pc, "expected original PC to be stored before patching"); ++ _deopt_state = is_deoptimized; ++ // leave _pc as is ++ } else { ++ _deopt_state = not_deoptimized; ++ _pc = pc; ++ } ++} ++ ++bool frame::is_interpreted_frame() const { ++ return Interpreter::contains(pc()); ++} ++ ++int frame::frame_size(RegisterMap* map) const { ++ frame sender = this->sender(map); ++ return sender.sp() - sp(); ++} ++ ++intptr_t* frame::entry_frame_argument_at(int offset) const { ++ // convert offset to index to deal with tsi ++ int index = (Interpreter::expr_offset_in_bytes(offset)/wordSize); ++ // Entry frame's arguments are always in relation to unextended_sp() ++ return &unextended_sp()[index]; ++} ++ ++// sender_sp ++#ifdef CC_INTERP ++intptr_t* frame::interpreter_frame_sender_sp() const { ++ assert(is_interpreted_frame(), "interpreted frame expected"); ++ // QQQ why does this specialize method exist if frame::sender_sp() does same thing? ++ // seems odd and if we always know interpreted vs. non then sender_sp() is really ++ // doing too much work. ++ return get_interpreterState()->sender_sp(); ++} ++ ++// monitor elements ++ ++BasicObjectLock* frame::interpreter_frame_monitor_begin() const { ++ return get_interpreterState()->monitor_base(); ++} ++ ++BasicObjectLock* frame::interpreter_frame_monitor_end() const { ++ return (BasicObjectLock*) get_interpreterState()->stack_base(); ++} ++ ++#else // CC_INTERP ++ ++intptr_t* frame::interpreter_frame_sender_sp() const { ++ assert(is_interpreted_frame(), "interpreted frame expected"); ++ return (intptr_t*) at(interpreter_frame_sender_sp_offset); ++} ++ ++void frame::set_interpreter_frame_sender_sp(intptr_t* sender_sp) { ++ assert(is_interpreted_frame(), "interpreted frame expected"); ++ int_at_put(interpreter_frame_sender_sp_offset, (intptr_t) sender_sp); ++} ++ ++ ++// monitor elements ++ ++BasicObjectLock* frame::interpreter_frame_monitor_begin() const { ++ return (BasicObjectLock*) addr_at(interpreter_frame_monitor_block_bottom_offset); ++} ++ ++BasicObjectLock* frame::interpreter_frame_monitor_end() const { ++ BasicObjectLock* result = (BasicObjectLock*) *addr_at(interpreter_frame_monitor_block_top_offset); ++ // make sure the pointer points inside the frame ++ assert((intptr_t) fp() > (intptr_t) result, "result must < than frame pointer"); ++ assert((intptr_t) sp() <= (intptr_t) result, "result must >= than stack pointer"); ++ return result; ++} ++ ++void frame::interpreter_frame_set_monitor_end(BasicObjectLock* value) { ++ *((BasicObjectLock**)addr_at(interpreter_frame_monitor_block_top_offset)) = value; ++} ++ ++// Used by template based interpreter deoptimization ++void frame::interpreter_frame_set_last_sp(intptr_t* sp) { ++ *((intptr_t**)addr_at(interpreter_frame_last_sp_offset)) = sp; ++} ++#endif // CC_INTERP ++ ++frame frame::sender_for_entry_frame(RegisterMap* map) const { ++ assert(map != NULL, "map must be set"); ++ // Java frame called from C; skip all C frames and return top C ++ // frame of that chunk as the sender ++ JavaFrameAnchor* jfa = entry_frame_call_wrapper()->anchor(); ++ assert(!entry_frame_is_first(), "next Java fp must be non zero"); ++ assert(jfa->last_Java_sp() > sp(), "must be above this frame on stack"); ++ map->clear(); ++ assert(map->include_argument_oops(), "should be set by clear"); ++ if (jfa->last_Java_pc() != NULL ) { ++ frame fr(jfa->last_Java_sp(), jfa->last_Java_fp(), jfa->last_Java_pc()); ++ return fr; ++ } ++ frame fr(jfa->last_Java_sp(), jfa->last_Java_fp()); ++ return fr; ++} ++ ++frame frame::sender_for_interpreter_frame(RegisterMap* map) const { ++ // sp is the raw sp from the sender after adapter or interpreter extension ++ intptr_t* sender_sp = this->sender_sp(); ++ ++ // This is the sp before any possible extension (adapter/locals). ++ intptr_t* unextended_sp = interpreter_frame_sender_sp(); ++ ++ // The interpreter and compiler(s) always save FP in a known ++ // location on entry. We must record where that location is ++ // so this if FP was live on callout from c2 we can find ++ // the saved copy no matter what it called. ++ ++ // Since the interpreter always saves FP if we record where it is then ++ // we don't have to always save FP on entry and exit to c2 compiled ++ // code, on entry will be enough. ++#ifdef COMPILER2 ++ if (map->update_map()) { ++ update_map_with_saved_link(map, (intptr_t**) addr_at(link_offset)); ++ } ++#endif /* COMPILER2 */ ++ return frame(sender_sp, unextended_sp, link(), sender_pc()); ++} ++ ++ ++//------------------------------------------------------------------------------ ++// frame::verify_deopt_original_pc ++// ++// Verifies the calculated original PC of a deoptimization PC for the ++// given unextended SP. The unextended SP might also be the saved SP ++// for MethodHandle call sites. ++#ifdef ASSERT ++void frame::verify_deopt_original_pc(nmethod* nm, intptr_t* unextended_sp, bool is_method_handle_return) { ++ frame fr; ++ ++ // This is ugly but it's better than to change {get,set}_original_pc ++ // to take an SP value as argument. And it's only a debugging ++ // method anyway. ++ fr._unextended_sp = unextended_sp; ++ ++ address original_pc = nm->get_original_pc(&fr); ++ assert(nm->insts_contains(original_pc), "original PC must be in nmethod"); ++ assert(nm->is_method_handle_return(original_pc) == is_method_handle_return, "must be"); ++} ++#endif ++ ++ ++//------------------------------------------------------------------------------ ++// frame::adjust_unextended_sp ++void frame::adjust_unextended_sp() { ++ // On MIPS, sites calling method handle intrinsics and lambda forms are treated ++ // as any other call site. Therefore, no special action is needed when we are ++ // returning to any of these call sites. ++ ++ nmethod* sender_nm = (_cb == NULL) ? NULL : _cb->as_nmethod_or_null(); ++ if (sender_nm != NULL) { ++ // If the sender PC is a deoptimization point, get the original PC. ++ if (sender_nm->is_deopt_entry(_pc) || ++ sender_nm->is_deopt_mh_entry(_pc)) { ++ DEBUG_ONLY(verify_deopt_original_pc(sender_nm, _unextended_sp)); ++ } ++ } ++} ++ ++//------------------------------------------------------------------------------ ++// frame::update_map_with_saved_link ++void frame::update_map_with_saved_link(RegisterMap* map, intptr_t** link_addr) { ++ // The interpreter and compiler(s) always save fp in a known ++ // location on entry. We must record where that location is ++ // so that if fp was live on callout from c2 we can find ++ // the saved copy no matter what it called. ++ ++ // Since the interpreter always saves fp if we record where it is then ++ // we don't have to always save fp on entry and exit to c2 compiled ++ // code, on entry will be enough. ++ map->set_location(FP->as_VMReg(), (address) link_addr); ++ // this is weird "H" ought to be at a higher address however the ++ // oopMaps seems to have the "H" regs at the same address and the ++ // vanilla register. ++ // XXXX make this go away ++ if (true) { ++ map->set_location(FP->as_VMReg()->next(), (address) link_addr); ++ } ++} ++ ++//------------------------------sender_for_compiled_frame----------------------- ++frame frame::sender_for_compiled_frame(RegisterMap* map) const { ++ assert(map != NULL, "map must be set"); ++ ++ // frame owned by optimizing compiler ++ assert(_cb->frame_size() >= 0, "must have non-zero frame size"); ++ ++ intptr_t* sender_sp = unextended_sp() + _cb->frame_size(); ++ intptr_t* unextended_sp = sender_sp; ++ ++#ifdef ASSERT ++ const bool c1_compiled = _cb->is_compiled_by_c1(); ++ bool native = _cb->is_nmethod() && ((nmethod*)_cb)->is_native_method(); ++ if (c1_compiled && native) { ++ assert(sender_sp == fp() + frame::sender_sp_offset, "incorrect frame size"); ++ } ++#endif // ASSERT ++ // On Intel the return_address is always the word on the stack ++ // the fp in compiler points to sender fp, but in interpreter, fp points to return address, ++ // so getting sender for compiled frame is not same as interpreter frame. ++ // we hard code here temporarily ++ // spark ++ address sender_pc = (address) *(sender_sp-1); ++ ++ intptr_t** saved_fp_addr = (intptr_t**) (sender_sp - frame::sender_sp_offset); ++ ++ if (map->update_map()) { ++ // Tell GC to use argument oopmaps for some runtime stubs that need it. ++ // For C1, the runtime stub might not have oop maps, so set this flag ++ // outside of update_register_map. ++ map->set_include_argument_oops(_cb->caller_must_gc_arguments(map->thread())); ++ if (_cb->oop_maps() != NULL) { ++ OopMapSet::update_register_map(this, map); ++ } ++ ++ // Since the prolog does the save and restore of epb there is no oopmap ++ // for it so we must fill in its location as if there was an oopmap entry ++ // since if our caller was compiled code there could be live jvm state in it. ++ update_map_with_saved_link(map, saved_fp_addr); ++ } ++ assert(sender_sp != sp(), "must have changed"); ++ return frame(sender_sp, unextended_sp, *saved_fp_addr, sender_pc); ++} ++ ++frame frame::sender(RegisterMap* map) const { ++ // Default is we done have to follow them. The sender_for_xxx will ++ // update it accordingly ++ map->set_include_argument_oops(false); ++ ++ if (is_entry_frame()) return sender_for_entry_frame(map); ++ if (is_interpreted_frame()) return sender_for_interpreter_frame(map); ++ assert(_cb == CodeCache::find_blob(pc()),"Must be the same"); ++ ++ if (_cb != NULL) { ++ return sender_for_compiled_frame(map); ++ } ++ // Must be native-compiled frame, i.e. the marshaling code for native ++ // methods that exists in the core system. ++ return frame(sender_sp(), link(), sender_pc()); ++} ++ ++ ++bool frame::interpreter_frame_equals_unpacked_fp(intptr_t* fp) { ++ assert(is_interpreted_frame(), "must be interpreter frame"); ++ Method* method = interpreter_frame_method(); ++ // When unpacking an optimized frame the frame pointer is ++ // adjusted with: ++ int diff = (method->max_locals() - method->size_of_parameters()) * ++ Interpreter::stackElementWords; ++ printf("^^^^^^^^^^^^^^^adjust fp in deopt fp = 0%lx \n", (intptr_t)(fp - diff)); ++ return _fp == (fp - diff); ++} ++ ++void frame::pd_gc_epilog() { ++ // nothing done here now ++} ++ ++bool frame::is_interpreted_frame_valid(JavaThread* thread) const { ++// QQQ ++#ifdef CC_INTERP ++#else ++ assert(is_interpreted_frame(), "Not an interpreted frame"); ++ // These are reasonable sanity checks ++ if (fp() == 0 || (intptr_t(fp()) & (wordSize-1)) != 0) { ++ return false; ++ } ++ if (sp() == 0 || (intptr_t(sp()) & (wordSize-1)) != 0) { ++ return false; ++ } ++ if (fp() + interpreter_frame_initial_sp_offset < sp()) { ++ return false; ++ } ++ // These are hacks to keep us out of trouble. ++ // The problem with these is that they mask other problems ++ if (fp() <= sp()) { // this attempts to deal with unsigned comparison above ++ return false; ++ } ++ ++ // do some validation of frame elements ++ ++ // first the method ++ ++ Method* m = *interpreter_frame_method_addr(); ++ ++ // validate the method we'd find in this potential sender ++ if (!m->is_valid_method()) return false; ++ ++ // stack frames shouldn't be much larger than max_stack elements ++ ++ //if (fp() - sp() > 1024 + m->max_stack()*Interpreter::stackElementSize()) { ++ if (fp() - sp() > 4096) { // stack frames shouldn't be large. ++ return false; ++ } ++ ++ // validate bci/bcx ++ ++ intptr_t bcx = interpreter_frame_bcx(); ++ if (m->validate_bci_from_bcx(bcx) < 0) { ++ return false; ++ } ++ ++ // validate ConstantPoolCache* ++ ++ ConstantPoolCache* cp = *interpreter_frame_cache_addr(); ++ ++ if (cp == NULL || !cp->is_metaspace_object()) return false; ++ ++ // validate locals ++ ++ address locals = (address) *interpreter_frame_locals_addr(); ++ ++ if (locals > thread->stack_base() || locals < (address) fp()) return false; ++ ++ // We'd have to be pretty unlucky to be mislead at this point ++ ++#endif // CC_INTERP ++ return true; ++} ++ ++BasicType frame::interpreter_frame_result(oop* oop_result, jvalue* value_result) { ++#ifdef CC_INTERP ++ // Needed for JVMTI. The result should always be in the interpreterState object ++ assert(false, "NYI"); ++ interpreterState istate = get_interpreterState(); ++#endif // CC_INTERP ++ assert(is_interpreted_frame(), "interpreted frame expected"); ++ Method* method = interpreter_frame_method(); ++ BasicType type = method->result_type(); ++ ++ intptr_t* tos_addr; ++ if (method->is_native()) { ++ // Prior to calling into the runtime to report the method_exit the possible ++ // return value is pushed to the native stack. If the result is a jfloat/jdouble ++ // then ST0 is saved. See the note in generate_native_result ++ tos_addr = (intptr_t*)sp(); ++ if (type == T_FLOAT || type == T_DOUBLE) { ++ tos_addr += 2; ++ } ++ } else { ++ tos_addr = (intptr_t*)interpreter_frame_tos_address(); ++ } ++ ++ switch (type) { ++ case T_OBJECT : ++ case T_ARRAY : { ++ oop obj; ++ if (method->is_native()) { ++#ifdef CC_INTERP ++ obj = istate->_oop_temp; ++#else ++ obj = cast_to_oop(at(interpreter_frame_oop_temp_offset)); ++#endif // CC_INTERP ++ } else { ++ oop* obj_p = (oop*)tos_addr; ++ obj = (obj_p == NULL) ? (oop)NULL : *obj_p; ++ } ++ assert(obj == NULL || Universe::heap()->is_in(obj), "sanity check"); ++ *oop_result = obj; ++ break; ++ } ++ case T_BOOLEAN : value_result->z = *(jboolean*)tos_addr; break; ++ case T_BYTE : value_result->b = *(jbyte*)tos_addr; break; ++ case T_CHAR : value_result->c = *(jchar*)tos_addr; break; ++ case T_SHORT : value_result->s = *(jshort*)tos_addr; break; ++ case T_INT : value_result->i = *(jint*)tos_addr; break; ++ case T_LONG : value_result->j = *(jlong*)tos_addr; break; ++ case T_FLOAT : value_result->f = *(jfloat*)tos_addr; break; ++ case T_DOUBLE : value_result->d = *(jdouble*)tos_addr; break; ++ case T_VOID : /* Nothing to do */ break; ++ default : ShouldNotReachHere(); ++ } ++ ++ return type; ++} ++ ++ ++intptr_t* frame::interpreter_frame_tos_at(jint offset) const { ++ int index = (Interpreter::expr_offset_in_bytes(offset)/wordSize); ++ return &interpreter_frame_tos_address()[index]; ++} ++ ++#ifndef PRODUCT ++ ++#define DESCRIBE_FP_OFFSET(name) \ ++ values.describe(frame_no, fp() + frame::name##_offset, #name) ++ ++void frame::describe_pd(FrameValues& values, int frame_no) { ++ if (is_interpreted_frame()) { ++ DESCRIBE_FP_OFFSET(interpreter_frame_sender_sp); ++ DESCRIBE_FP_OFFSET(interpreter_frame_last_sp); ++ DESCRIBE_FP_OFFSET(interpreter_frame_method); ++ DESCRIBE_FP_OFFSET(interpreter_frame_mdx); ++ DESCRIBE_FP_OFFSET(interpreter_frame_cache); ++ DESCRIBE_FP_OFFSET(interpreter_frame_locals); ++ DESCRIBE_FP_OFFSET(interpreter_frame_bcx); ++ DESCRIBE_FP_OFFSET(interpreter_frame_initial_sp); ++ } ++} ++#endif ++ ++intptr_t *frame::initial_deoptimization_info() { ++ // used to reset the saved FP ++ return fp(); ++} ++ ++intptr_t* frame::real_fp() const { ++ if (_cb != NULL) { ++ // use the frame size if valid ++ int size = _cb->frame_size(); ++ if (size > 0) { ++ return unextended_sp() + size; ++ } ++ } ++ // else rely on fp() ++ assert(! is_compiled_frame(), "unknown compiled frame size"); ++ return fp(); ++} ++ ++#ifndef PRODUCT ++// This is a generic constructor which is only used by pns() in debug.cpp. ++frame::frame(void* sp, void* fp, void* pc) { ++ init((intptr_t*)sp, (intptr_t*)fp, (address)pc); ++} ++#endif +diff --git a/hotspot/src/cpu/mips/vm/frame_mips.hpp b/hotspot/src/cpu/mips/vm/frame_mips.hpp +new file mode 100644 +index 0000000000..9e684a8dc3 +--- /dev/null ++++ b/hotspot/src/cpu/mips/vm/frame_mips.hpp +@@ -0,0 +1,229 @@ ++/* ++ * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2015, 2022, Loongson Technology. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ * ++ */ ++ ++#ifndef CPU_MIPS_VM_FRAME_MIPS_HPP ++#define CPU_MIPS_VM_FRAME_MIPS_HPP ++ ++#include "runtime/synchronizer.hpp" ++#include "utilities/top.hpp" ++ ++// A frame represents a physical stack frame (an activation). Frames can be ++// C or Java frames, and the Java frames can be interpreted or compiled. ++// In contrast, vframes represent source-level activations, so that one physical frame ++// can correspond to multiple source level frames because of inlining. ++// A frame is comprised of {pc, fp, sp} ++// ------------------------------ Asm interpreter ---------------------------------------- ++// Layout of asm interpreter frame: ++// [expression stack ] * <- sp ++// [monitors ] \ ++// ... | monitor block size ++// [monitors ] / ++// [monitor block size ] ++// [byte code index/pointr] = bcx() bcx_offset ++// [pointer to locals ] = locals() locals_offset ++// [constant pool cache ] = cache() cache_offset ++// [methodData ] = mdp() mdx_offset ++// [methodOop ] = method() method_offset ++// [last sp ] = last_sp() last_sp_offset ++// [old stack pointer ] (sender_sp) sender_sp_offset ++// [old frame pointer ] <- fp = link() ++// [return pc ] ++// [oop temp ] (only for native calls) ++// [locals and parameters ] ++// <- sender sp ++// ------------------------------ Asm interpreter ---------------------------------------- ++ ++// ------------------------------ C++ interpreter ---------------------------------------- ++// ++// Layout of C++ interpreter frame: (While executing in BytecodeInterpreter::run) ++// ++// <- SP (current sp) ++// [local variables ] BytecodeInterpreter::run local variables ++// ... BytecodeInterpreter::run local variables ++// [local variables ] BytecodeInterpreter::run local variables ++// [old frame pointer ] fp [ BytecodeInterpreter::run's fp ] ++// [return pc ] (return to frame manager) ++// [interpreter_state* ] (arg to BytecodeInterpreter::run) -------------- ++// [expression stack ] <- last_Java_sp | ++// [... ] * <- interpreter_state.stack | ++// [expression stack ] * <- interpreter_state.stack_base | ++// [monitors ] \ | ++// ... | monitor block size | ++// [monitors ] / <- interpreter_state.monitor_base | ++// [struct interpretState ] <-----------------------------------------| ++// [return pc ] (return to callee of frame manager [1] ++// [locals and parameters ] ++// <- sender sp ++ ++// [1] When the c++ interpreter calls a new method it returns to the frame ++// manager which allocates a new frame on the stack. In that case there ++// is no real callee of this newly allocated frame. The frame manager is ++// aware of the additional frame(s) and will pop them as nested calls ++// complete. Howevers tTo make it look good in the debugger the frame ++// manager actually installs a dummy pc pointing to RecursiveInterpreterActivation ++// with a fake interpreter_state* parameter to make it easy to debug ++// nested calls. ++ ++// Note that contrary to the layout for the assembly interpreter the ++// expression stack allocated for the C++ interpreter is full sized. ++// However this is not as bad as it seems as the interpreter frame_manager ++// will truncate the unused space on succesive method calls. ++// ++// ------------------------------ C++ interpreter ---------------------------------------- ++ ++// Layout of interpreter frame: ++// ++// [ monitor entry ] <--- sp ++// ... ++// [ monitor entry ] ++// -9 [ monitor block top ] ( the top monitor entry ) ++// -8 [ byte code pointer ] (if native, bcp = 0) ++// -7 [ constant pool cache ] ++// -6 [ methodData ] mdx_offset(not core only) ++// -5 [ mirror ] ++// -4 [ methodOop ] ++// -3 [ locals offset ] ++// -2 [ last_sp ] ++// -1 [ sender's sp ] ++// 0 [ sender's fp ] <--- fp ++// 1 [ return address ] ++// 2 [ oop temp offset ] (only for native calls) ++// 3 [ result handler offset ] (only for native calls) ++// 4 [ result type info ] (only for native calls) ++// [ local var m-1 ] ++// ... ++// [ local var 0 ] ++// [ argumnet word n-1 ] <--- ( sender's sp ) ++// ... ++// [ argument word 0 ] <--- S7 ++ ++ public: ++ enum { ++ pc_return_offset = 0, ++ // All frames ++ link_offset = 0, ++ return_addr_offset = 1, ++ // non-interpreter frames ++ sender_sp_offset = 2, ++ ++#ifndef CC_INTERP ++ ++ // Interpreter frames ++ interpreter_frame_return_addr_offset = 1, ++ interpreter_frame_result_handler_offset = 3, // for native calls only ++ interpreter_frame_oop_temp_offset = 2, // for native calls only ++ ++ interpreter_frame_sender_fp_offset = 0, ++ interpreter_frame_sender_sp_offset = -1, ++ // outgoing sp before a call to an invoked method ++ interpreter_frame_last_sp_offset = interpreter_frame_sender_sp_offset - 1, ++ interpreter_frame_locals_offset = interpreter_frame_last_sp_offset - 1, ++ interpreter_frame_method_offset = interpreter_frame_locals_offset - 1, ++ interpreter_frame_mdx_offset = interpreter_frame_method_offset - 1, ++ interpreter_frame_cache_offset = interpreter_frame_mdx_offset - 1, ++ interpreter_frame_bcx_offset = interpreter_frame_cache_offset - 1, ++ interpreter_frame_initial_sp_offset = interpreter_frame_bcx_offset - 1, ++ ++ interpreter_frame_monitor_block_top_offset = interpreter_frame_initial_sp_offset, ++ interpreter_frame_monitor_block_bottom_offset = interpreter_frame_initial_sp_offset, ++ ++#endif // CC_INTERP ++ ++ // Entry frames ++ entry_frame_call_wrapper_offset = -9, ++ ++ // Native frames ++ ++ native_frame_initial_param_offset = 2 ++ ++ }; ++ ++ intptr_t ptr_at(int offset) const { ++ return *ptr_at_addr(offset); ++ } ++ ++ void ptr_at_put(int offset, intptr_t value) { ++ *ptr_at_addr(offset) = value; ++ } ++ ++ private: ++ // an additional field beyond _sp and _pc: ++ intptr_t* _fp; // frame pointer ++ // The interpreter and adapters will extend the frame of the caller. ++ // Since oopMaps are based on the sp of the caller before extension ++ // we need to know that value. However in order to compute the address ++ // of the return address we need the real "raw" sp. Since sparc already ++ // uses sp() to mean "raw" sp and unextended_sp() to mean the caller's ++ // original sp we use that convention. ++ ++ intptr_t* _unextended_sp; ++ void adjust_unextended_sp(); ++ ++ intptr_t* ptr_at_addr(int offset) const { ++ return (intptr_t*) addr_at(offset); ++ } ++#ifdef ASSERT ++ // Used in frame::sender_for_{interpreter,compiled}_frame ++ static void verify_deopt_original_pc( nmethod* nm, intptr_t* unextended_sp, bool is_method_handle_return = false); ++ static void verify_deopt_mh_original_pc(nmethod* nm, intptr_t* unextended_sp) { ++ verify_deopt_original_pc(nm, unextended_sp, true); ++ } ++#endif ++ ++ public: ++ // Constructors ++ ++ frame(intptr_t* sp, intptr_t* fp, address pc); ++ ++ frame(intptr_t* sp, intptr_t* unextended_sp, intptr_t* fp, address pc); ++ ++ frame(intptr_t* sp, intptr_t* fp); ++ ++ void init(intptr_t* sp, intptr_t* fp, address pc); ++ ++ // accessors for the instance variables ++ intptr_t* fp() const { return _fp; } ++ ++ inline address* sender_pc_addr() const; ++ ++ // return address of param, zero origin index. ++ inline address* native_param_addr(int idx) const; ++ ++ // expression stack tos if we are nested in a java call ++ intptr_t* interpreter_frame_last_sp() const; ++ ++ // helper to update a map with callee-saved FP ++ static void update_map_with_saved_link(RegisterMap* map, intptr_t** link_addr); ++ ++#ifndef CC_INTERP ++ // deoptimization support ++ void interpreter_frame_set_last_sp(intptr_t* sp); ++#endif // CC_INTERP ++ ++#ifdef CC_INTERP ++ inline interpreterState get_interpreterState() const; ++#endif // CC_INTERP ++ ++#endif // CPU_MIPS_VM_FRAME_MIPS_HPP +diff --git a/hotspot/src/cpu/mips/vm/frame_mips.inline.hpp b/hotspot/src/cpu/mips/vm/frame_mips.inline.hpp +new file mode 100644 +index 0000000000..60e56ac7ab +--- /dev/null ++++ b/hotspot/src/cpu/mips/vm/frame_mips.inline.hpp +@@ -0,0 +1,312 @@ ++/* ++ * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2015, 2019, Loongson Technology. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ * ++ */ ++ ++#ifndef CPU_MIPS_VM_FRAME_MIPS_INLINE_HPP ++#define CPU_MIPS_VM_FRAME_MIPS_INLINE_HPP ++ ++#include "code/codeCache.hpp" ++ ++// Inline functions for Loongson frames: ++ ++// Constructors: ++ ++inline frame::frame() { ++ _pc = NULL; ++ _sp = NULL; ++ _unextended_sp = NULL; ++ _fp = NULL; ++ _cb = NULL; ++ _deopt_state = unknown; ++} ++ ++inline void frame::init(intptr_t* sp, intptr_t* fp, address pc) { ++ _sp = sp; ++ _unextended_sp = sp; ++ _fp = fp; ++ _pc = pc; ++ assert(pc != NULL, "no pc?"); ++ _cb = CodeCache::find_blob(pc); ++ adjust_unextended_sp(); ++ ++ address original_pc = nmethod::get_deopt_original_pc(this); ++ if (original_pc != NULL) { ++ _pc = original_pc; ++ _deopt_state = is_deoptimized; ++ } else { ++ _deopt_state = not_deoptimized; ++ } ++} ++ ++inline frame::frame(intptr_t* sp, intptr_t* fp, address pc) { ++ init(sp, fp, pc); ++} ++ ++inline frame::frame(intptr_t* sp, intptr_t* unextended_sp, intptr_t* fp, address pc) { ++ _sp = sp; ++ _unextended_sp = unextended_sp; ++ _fp = fp; ++ _pc = pc; ++ assert(pc != NULL, "no pc?"); ++ _cb = CodeCache::find_blob(pc); ++ adjust_unextended_sp(); ++ ++ address original_pc = nmethod::get_deopt_original_pc(this); ++ if (original_pc != NULL) { ++ _pc = original_pc; ++ _deopt_state = is_deoptimized; ++ } else { ++ _deopt_state = not_deoptimized; ++ } ++} ++ ++inline frame::frame(intptr_t* sp, intptr_t* fp) { ++ _sp = sp; ++ _unextended_sp = sp; ++ _fp = fp; ++ _pc = (address)(sp[-1]); ++ ++ // Here's a sticky one. This constructor can be called via AsyncGetCallTrace ++ // when last_Java_sp is non-null but the pc fetched is junk. If we are truly ++ // unlucky the junk value could be to a zombied method and we'll die on the ++ // find_blob call. This is also why we can have no asserts on the validity ++ // of the pc we find here. AsyncGetCallTrace -> pd_get_top_frame_for_signal_handler ++ // -> pd_last_frame should use a specialized version of pd_last_frame which could ++ // call a specilaized frame constructor instead of this one. ++ // Then we could use the assert below. However this assert is of somewhat dubious ++ // value. ++ // assert(_pc != NULL, "no pc?"); ++ ++ _cb = CodeCache::find_blob(_pc); ++ adjust_unextended_sp(); ++ address original_pc = nmethod::get_deopt_original_pc(this); ++ if (original_pc != NULL) { ++ _pc = original_pc; ++ _deopt_state = is_deoptimized; ++ } else { ++ _deopt_state = not_deoptimized; ++ } ++} ++ ++// Accessors ++ ++inline bool frame::equal(frame other) const { ++ bool ret = sp() == other.sp() ++ && unextended_sp() == other.unextended_sp() ++ && fp() == other.fp() ++ && pc() == other.pc(); ++ assert(!ret || ret && cb() == other.cb() && _deopt_state == other._deopt_state, "inconsistent construction"); ++ return ret; ++} ++ ++// Return unique id for this frame. The id must have a value where we can distinguish ++// identity and younger/older relationship. NULL represents an invalid (incomparable) ++// frame. ++inline intptr_t* frame::id(void) const { return unextended_sp(); } ++ ++// Relationals on frames based ++// Return true if the frame is younger (more recent activation) than the frame represented by id ++inline bool frame::is_younger(intptr_t* id) const { assert(this->id() != NULL && id != NULL, "NULL frame id"); ++ return this->id() < id ; } ++ ++// Return true if the frame is older (less recent activation) than the frame represented by id ++inline bool frame::is_older(intptr_t* id) const { assert(this->id() != NULL && id != NULL, "NULL frame id"); ++ return this->id() > id ; } ++ ++ ++ ++inline intptr_t* frame::link() const { return (intptr_t*) *(intptr_t **)addr_at(link_offset); } ++inline void frame::set_link(intptr_t* addr) { *(intptr_t **)addr_at(link_offset) = addr; } ++ ++ ++inline intptr_t* frame::unextended_sp() const { return _unextended_sp; } ++ ++// Return address: ++ ++inline address* frame::sender_pc_addr() const { return (address*) addr_at( return_addr_offset); } ++inline address frame::sender_pc() const { return *sender_pc_addr(); } ++ ++// return address of param, zero origin index. ++inline address* frame::native_param_addr(int idx) const { return (address*) addr_at( native_frame_initial_param_offset+idx); } ++ ++#ifdef CC_INTERP ++ ++inline interpreterState frame::get_interpreterState() const { ++ return ((interpreterState)addr_at( -sizeof(BytecodeInterpreter)/wordSize )); ++} ++ ++inline intptr_t* frame::sender_sp() const { ++ // Hmm this seems awfully expensive QQQ, is this really called with interpreted frames? ++ if (is_interpreted_frame()) { ++ assert(false, "should never happen"); ++ return get_interpreterState()->sender_sp(); ++ } else { ++ return addr_at(sender_sp_offset); ++ } ++} ++ ++inline intptr_t** frame::interpreter_frame_locals_addr() const { ++ assert(is_interpreted_frame(), "must be interpreted"); ++ return &(get_interpreterState()->_locals); ++} ++ ++inline intptr_t* frame::interpreter_frame_bcx_addr() const { ++ assert(is_interpreted_frame(), "must be interpreted"); ++ return (intptr_t*) &(get_interpreterState()->_bcp); ++} ++ ++ ++// Constant pool cache ++ ++inline ConstantPoolCache** frame::interpreter_frame_cache_addr() const { ++ assert(is_interpreted_frame(), "must be interpreted"); ++ return &(get_interpreterState()->_constants); ++} ++ ++// Method ++ ++inline Method** frame::interpreter_frame_method_addr() const { ++ assert(is_interpreted_frame(), "must be interpreted"); ++ return &(get_interpreterState()->_method); ++} ++ ++inline intptr_t* frame::interpreter_frame_mdx_addr() const { ++ assert(is_interpreted_frame(), "must be interpreted"); ++ return (intptr_t*) &(get_interpreterState()->_mdx); ++} ++ ++// top of expression stack ++inline intptr_t* frame::interpreter_frame_tos_address() const { ++ assert(is_interpreted_frame(), "wrong frame type"); ++ return get_interpreterState()->_stack + 1; ++} ++ ++#else // asm interpreter ++inline intptr_t* frame::sender_sp() const { return addr_at( sender_sp_offset); } ++ ++inline intptr_t** frame::interpreter_frame_locals_addr() const { ++ return (intptr_t**)addr_at(interpreter_frame_locals_offset); ++} ++ ++inline intptr_t* frame::interpreter_frame_last_sp() const { ++ return *(intptr_t**)addr_at(interpreter_frame_last_sp_offset); ++} ++ ++inline intptr_t* frame::interpreter_frame_bcx_addr() const { ++ return (intptr_t*)addr_at(interpreter_frame_bcx_offset); ++} ++ ++ ++inline intptr_t* frame::interpreter_frame_mdx_addr() const { ++ return (intptr_t*)addr_at(interpreter_frame_mdx_offset); ++} ++ ++ ++ ++// Constant pool cache ++ ++inline ConstantPoolCache** frame::interpreter_frame_cache_addr() const { ++ return (ConstantPoolCache**)addr_at(interpreter_frame_cache_offset); ++} ++ ++// Method ++ ++inline Method** frame::interpreter_frame_method_addr() const { ++ return (Method**)addr_at(interpreter_frame_method_offset); ++} ++ ++// top of expression stack ++inline intptr_t* frame::interpreter_frame_tos_address() const { ++ intptr_t* last_sp = interpreter_frame_last_sp(); ++ if (last_sp == NULL ) { ++ return sp(); ++ } else { ++ // sp() may have been extended by an adapter ++ assert(last_sp <= (intptr_t*)interpreter_frame_monitor_end(), "bad tos"); ++ return last_sp; ++ } ++} ++ ++inline oop* frame::interpreter_frame_temp_oop_addr() const { ++ return (oop *)(fp() + interpreter_frame_oop_temp_offset); ++} ++ ++#endif // CC_INTERP ++ ++inline int frame::pd_oop_map_offset_adjustment() const { ++ return 0; ++} ++ ++inline int frame::interpreter_frame_monitor_size() { ++ return BasicObjectLock::size(); ++} ++ ++ ++// expression stack ++// (the max_stack arguments are used by the GC; see class FrameClosure) ++ ++inline intptr_t* frame::interpreter_frame_expression_stack() const { ++ intptr_t* monitor_end = (intptr_t*) interpreter_frame_monitor_end(); ++ return monitor_end-1; ++} ++ ++ ++inline jint frame::interpreter_frame_expression_stack_direction() { return -1; } ++ ++ ++// Entry frames ++ ++inline JavaCallWrapper** frame::entry_frame_call_wrapper_addr() const { ++ return (JavaCallWrapper**)addr_at(entry_frame_call_wrapper_offset); ++} ++ ++// Compiled frames ++ ++inline int frame::local_offset_for_compiler(int local_index, int nof_args, int max_nof_locals, int max_nof_monitors) { ++ return (nof_args - local_index + (local_index < nof_args ? 1: -1)); ++} ++ ++inline int frame::monitor_offset_for_compiler(int local_index, int nof_args, int max_nof_locals, int max_nof_monitors) { ++ return local_offset_for_compiler(local_index, nof_args, max_nof_locals, max_nof_monitors); ++} ++ ++inline int frame::min_local_offset_for_compiler(int nof_args, int max_nof_locals, int max_nof_monitors) { ++ return (nof_args - (max_nof_locals + max_nof_monitors*2) - 1); ++} ++ ++inline bool frame::volatile_across_calls(Register reg) { ++ return true; ++} ++ ++ ++ ++inline oop frame::saved_oop_result(RegisterMap* map) const { ++ return *((oop*) map->location(V0->as_VMReg())); ++} ++ ++inline void frame::set_saved_oop_result(RegisterMap* map, oop obj) { ++ *((oop*) map->location(V0->as_VMReg())) = obj; ++} ++ ++#endif // CPU_MIPS_VM_FRAME_MIPS_INLINE_HPP +diff --git a/hotspot/src/cpu/mips/vm/globalDefinitions_mips.hpp b/hotspot/src/cpu/mips/vm/globalDefinitions_mips.hpp +new file mode 100644 +index 0000000000..bd00a8d473 +--- /dev/null ++++ b/hotspot/src/cpu/mips/vm/globalDefinitions_mips.hpp +@@ -0,0 +1,41 @@ ++/* ++ * Copyright (c) 1999, 2013, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2015, 2020, Loongson Technology. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ * ++ */ ++ ++#ifndef CPU_MIPS_VM_GLOBALDEFINITIONS_MIPS_HPP ++#define CPU_MIPS_VM_GLOBALDEFINITIONS_MIPS_HPP ++// Size of MIPS Instructions ++const int BytesPerInstWord = 4; ++ ++const int StackAlignmentInBytes = (2*wordSize); ++ ++// Indicates whether the C calling conventions require that ++// 32-bit integer argument values are properly extended to 64 bits. ++// If set, SharedRuntime::c_calling_convention() must adapt ++// signatures accordingly. ++const bool CCallingConventionRequiresIntsAsLongs = false; ++ ++#define SUPPORTS_NATIVE_CX8 ++ ++#endif // CPU_MIPS_VM_GLOBALDEFINITIONS_MIPS_HPP +diff --git a/hotspot/src/cpu/mips/vm/globals_mips.hpp b/hotspot/src/cpu/mips/vm/globals_mips.hpp +new file mode 100644 +index 0000000000..988bc35137 +--- /dev/null ++++ b/hotspot/src/cpu/mips/vm/globals_mips.hpp +@@ -0,0 +1,124 @@ ++/* ++ * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2015, 2022, Loongson Technology. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ * ++ */ ++ ++#ifndef CPU_MIPS_VM_GLOBALS_MIPS_HPP ++#define CPU_MIPS_VM_GLOBALS_MIPS_HPP ++ ++#include "utilities/globalDefinitions.hpp" ++#include "utilities/macros.hpp" ++ ++// Sets the default values for platform dependent flags used by the runtime system. ++// (see globals.hpp) ++ ++#ifdef CORE ++define_pd_global(bool, UseSSE, 0); ++#endif /* CORE */ ++define_pd_global(bool, ConvertSleepToYield, true); ++define_pd_global(bool, ShareVtableStubs, true); ++define_pd_global(bool, CountInterpCalls, true); ++ ++define_pd_global(bool, ImplicitNullChecks, true); // Generate code for implicit null checks ++define_pd_global(bool, TrapBasedNullChecks, false); // Not needed on x86. ++define_pd_global(bool, UncommonNullCast, true); // Uncommon-trap NULLs passed to check cast ++define_pd_global(bool, NeedsDeoptSuspend, false); // only register window machines need this ++ ++// See 4827828 for this change. There is no globals_core_i486.hpp. I can't ++// assign a different value for C2 without touching a number of files. Use ++// #ifdef to minimize the change as it's late in Mantis. -- FIXME. ++// c1 doesn't have this problem because the fix to 4858033 assures us ++// the the vep is aligned at CodeEntryAlignment whereas c2 only aligns ++// the uep and the vep doesn't get real alignment but just slops on by ++// only assured that the entry instruction meets the 5 byte size requirement. ++define_pd_global(intx, CodeEntryAlignment, 16); ++define_pd_global(intx, OptoLoopAlignment, 16); ++define_pd_global(intx, InlineFrequencyCount, 100); ++define_pd_global(intx, InlineSmallCode, 4000); // MIPS generates 3x instructions than X86 ++ ++define_pd_global(uintx, TLABSize, 0); ++define_pd_global(uintx, NewSize, 1024 * K); ++define_pd_global(intx, PreInflateSpin, 10); ++ ++define_pd_global(intx, PrefetchCopyIntervalInBytes, -1); ++define_pd_global(intx, PrefetchScanIntervalInBytes, -1); ++define_pd_global(intx, PrefetchFieldsAhead, -1); ++ ++define_pd_global(intx, StackYellowPages, 2); ++define_pd_global(intx, StackRedPages, 1); ++define_pd_global(intx, StackShadowPages, 3 DEBUG_ONLY(+1)); ++ ++define_pd_global(bool, RewriteBytecodes, true); ++define_pd_global(bool, RewriteFrequentPairs, true); ++define_pd_global(bool, UseMembar, true); ++// GC Ergo Flags ++define_pd_global(intx, CMSYoungGenPerWorker, 64*M); // default max size of CMS young gen, per GC worker thread ++ ++define_pd_global(uintx, TypeProfileLevel, 111); ++ ++define_pd_global(bool, PreserveFramePointer, false); ++// Only c2 cares about this at the moment ++define_pd_global(intx, AllocatePrefetchStyle, 2); ++define_pd_global(intx, AllocatePrefetchDistance, -1); ++ ++#define ARCH_FLAGS(develop, product, diagnostic, experimental, notproduct) \ ++ \ ++ product(bool, UseLEXT1, false, \ ++ "Use LoongISA general EXTensions 1") \ ++ \ ++ product(bool, UseLEXT2, false, \ ++ "Use LoongISA general EXTensions 2") \ ++ \ ++ product(bool, UseLEXT3, false, \ ++ "Use LoongISA general EXTensions 3") \ ++ \ ++ product(bool, UseCodeCacheAllocOpt, true, \ ++ "Allocate code cache within 32-bit memory address space") \ ++ \ ++ product(intx, UseSyncLevel, 10000, \ ++ "The sync level on Loongson CPUs" \ ++ "UseSyncLevel == 10000, 111, for all Loongson CPUs, " \ ++ "UseSyncLevel == 4000, 101, maybe for GS464V" \ ++ "UseSyncLevel == 3000, 001, maybe for GS464V" \ ++ "UseSyncLevel == 2000, 011, maybe for GS464E/GS264" \ ++ "UseSyncLevel == 1000, 110, maybe for GS464") \ ++ \ ++ develop(bool, UseBoundCheckInstruction, false, \ ++ "Use bound check instruction") \ ++ \ ++ product(intx, SetFSFOFN, 999, \ ++ "Set the FS/FO/FN bits in FCSR" \ ++ "999 means FS/FO/FN will not be changed" \ ++ "=XYZ, with X:FS, Y:FO, Z:FN, X, Y and Z in 0=off, 1=on") \ ++ \ ++ /* assembler */ \ ++ product(bool, UseCountLeadingZerosInstructionMIPS64, true, \ ++ "Use count leading zeros instruction") \ ++ \ ++ product(bool, UseCountTrailingZerosInstructionMIPS64, false, \ ++ "Use count trailing zeros instruction") \ ++ \ ++ product(bool, UseActiveCoresMP, false, \ ++ "Eliminate barriers for single active cpu") ++ ++#endif // CPU_MIPS_VM_GLOBALS_MIPS_HPP +diff --git a/hotspot/src/cpu/mips/vm/icBuffer_mips.cpp b/hotspot/src/cpu/mips/vm/icBuffer_mips.cpp +new file mode 100644 +index 0000000000..96ea345360 +--- /dev/null ++++ b/hotspot/src/cpu/mips/vm/icBuffer_mips.cpp +@@ -0,0 +1,97 @@ ++/* ++ * Copyright (c) 1997, 2012, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2015, 2022, Loongson Technology. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ * ++ */ ++ ++#include "precompiled.hpp" ++#include "asm/macroAssembler.hpp" ++#include "asm/macroAssembler.inline.hpp" ++#include "code/icBuffer.hpp" ++#include "gc_interface/collectedHeap.inline.hpp" ++#include "interpreter/bytecodes.hpp" ++#include "memory/resourceArea.hpp" ++#include "nativeInst_mips.hpp" ++#include "oops/oop.inline.hpp" ++#include "oops/oop.inline2.hpp" ++ ++#define A0 RA0 ++#define A1 RA1 ++#define A2 RA2 ++#define A3 RA3 ++#define A4 RA4 ++#define A5 RA5 ++#define A6 RA6 ++#define A7 RA7 ++#define T0 RT0 ++#define T1 RT1 ++#define T2 RT2 ++#define T3 RT3 ++#define T8 RT8 ++#define T9 RT9 ++ ++int InlineCacheBuffer::ic_stub_code_size() { ++ return NativeMovConstReg::instruction_size + ++ NativeGeneralJump::instruction_size + ++ 1; ++ // so that code_end can be set in CodeBuffer ++ // 64bit 15 = 6 + 8 bytes + 1 byte ++ // 32bit 7 = 2 + 4 bytes + 1 byte ++} ++ ++ ++// we use T1 as cached oop(klass) now. this is the target of virtual call, ++// when reach here, the receiver in T0 ++// refer to shareRuntime_mips.cpp,gen_i2c2i_adapters ++void InlineCacheBuffer::assemble_ic_buffer_code(address code_begin, void* cached_value, address entry_point) { ++ ResourceMark rm; ++ CodeBuffer code(code_begin, ic_stub_code_size()); ++ MacroAssembler* masm = new MacroAssembler(&code); ++ // note: even though the code contains an embedded oop, we do not need reloc info ++ // because ++ // (1) the oop is old (i.e., doesn't matter for scavenges) ++ // (2) these ICStubs are removed *before* a GC happens, so the roots disappear ++// assert(cached_oop == NULL || cached_oop->is_perm(), "must be perm oop"); ++#define __ masm-> ++ __ patchable_set48(T1, (long)cached_value); ++ ++ __ patchable_jump(entry_point); ++ __ flush(); ++#undef __ ++} ++ ++ ++address InlineCacheBuffer::ic_buffer_entry_point(address code_begin) { ++ NativeMovConstReg* move = nativeMovConstReg_at(code_begin); // creation also verifies the object ++ NativeGeneralJump* jump = nativeGeneralJump_at(move->next_instruction_address()); ++ return jump->jump_destination(); ++} ++ ++ ++void* InlineCacheBuffer::ic_buffer_cached_value(address code_begin) { ++ // creation also verifies the object ++ NativeMovConstReg* move = nativeMovConstReg_at(code_begin); ++ // Verifies the jump ++ NativeGeneralJump* jump = nativeGeneralJump_at(move->next_instruction_address()); ++ void* o= (void*)move->data(); ++ return o; ++} +diff --git a/hotspot/src/cpu/mips/vm/icache_mips.cpp b/hotspot/src/cpu/mips/vm/icache_mips.cpp +new file mode 100644 +index 0000000000..848964b63f +--- /dev/null ++++ b/hotspot/src/cpu/mips/vm/icache_mips.cpp +@@ -0,0 +1,41 @@ ++/* ++ * Copyright (c) 1997, 2010, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2015, 2021, Loongson Technology. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ * ++ */ ++ ++#include "precompiled.hpp" ++#include "asm/macroAssembler.hpp" ++#include "runtime/icache.hpp" ++ ++void ICacheStubGenerator::generate_icache_flush(ICache::flush_icache_stub_t* flush_icache_stub) ++{ ++#define __ _masm-> ++ StubCodeMark mark(this, "ICache", "flush_icache_stub"); ++ address start = __ pc(); ++ ++ __ jr_hb(RA); ++ __ delayed()->ori(V0, RA2, 0); ++ ++ *flush_icache_stub = (ICache::flush_icache_stub_t)start; ++#undef __ ++} +diff --git a/hotspot/src/cpu/mips/vm/icache_mips.hpp b/hotspot/src/cpu/mips/vm/icache_mips.hpp +new file mode 100644 +index 0000000000..78ee11cc73 +--- /dev/null ++++ b/hotspot/src/cpu/mips/vm/icache_mips.hpp +@@ -0,0 +1,41 @@ ++/* ++ * Copyright (c) 1997, 2010, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2015, 2021, Loongson Technology. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ * ++ */ ++ ++#ifndef CPU_MIPS_VM_ICACHE_MIPS_HPP ++#define CPU_MIPS_VM_ICACHE_MIPS_HPP ++ ++// Interface for updating the instruction cache. Whenever the VM modifies ++// code, part of the processor instruction cache potentially has to be flushed. ++ ++class ICache : public AbstractICache { ++ public: ++ enum { ++ stub_size = 2 * BytesPerInstWord, // Size of the icache flush stub in bytes ++ line_size = 32, // flush instruction affects a dword ++ log2_line_size = 5 // log2(line_size) ++ }; ++}; ++ ++#endif // CPU_MIPS_VM_ICACHE_MIPS_HPP +diff --git a/hotspot/src/cpu/mips/vm/interp_masm_mips_64.cpp b/hotspot/src/cpu/mips/vm/interp_masm_mips_64.cpp +new file mode 100644 +index 0000000000..ed2d931e94 +--- /dev/null ++++ b/hotspot/src/cpu/mips/vm/interp_masm_mips_64.cpp +@@ -0,0 +1,2084 @@ ++/* ++ * Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2015, 2022, Loongson Technology. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ * ++ */ ++ ++#include "precompiled.hpp" ++#include "interp_masm_mips_64.hpp" ++#include "interpreter/interpreter.hpp" ++#include "interpreter/interpreterRuntime.hpp" ++#include "oops/arrayOop.hpp" ++#include "oops/markOop.hpp" ++#include "oops/methodData.hpp" ++#include "oops/method.hpp" ++#include "prims/jvmtiExport.hpp" ++#include "prims/jvmtiRedefineClassesTrace.hpp" ++#include "prims/jvmtiThreadState.hpp" ++#include "runtime/basicLock.hpp" ++#include "runtime/biasedLocking.hpp" ++#include "runtime/sharedRuntime.hpp" ++#include "runtime/thread.inline.hpp" ++ ++#define A0 RA0 ++#define A1 RA1 ++#define A2 RA2 ++#define A3 RA3 ++#define A4 RA4 ++#define A5 RA5 ++#define A6 RA6 ++#define A7 RA7 ++#define T0 RT0 ++#define T1 RT1 ++#define T2 RT2 ++#define T3 RT3 ++#define T8 RT8 ++#define T9 RT9 ++ ++// Implementation of InterpreterMacroAssembler ++ ++#ifdef CC_INTERP ++void InterpreterMacroAssembler::get_method(Register reg) { ++} ++#endif // CC_INTERP ++ ++void InterpreterMacroAssembler::get_2_byte_integer_at_bcp(Register reg, Register tmp, int offset) { ++ // The runtime address of BCP may be unaligned. ++ // Refer to the SPARC implementation. ++ lbu(reg, BCP, offset+1); ++ lbu(tmp, BCP, offset); ++ dsll(reg, reg, 8); ++ daddu(reg, tmp, reg); ++} ++ ++void InterpreterMacroAssembler::get_4_byte_integer_at_bcp(Register reg, Register tmp, int offset) { ++ assert(reg != tmp, "need separate temp register"); ++ if (offset & 3) { // Offset unaligned? ++ lbu(reg, BCP, offset+3); ++ lbu(tmp, BCP, offset+2); ++ dsll(reg, reg, 8); ++ daddu(reg, tmp, reg); ++ lbu(tmp, BCP, offset+1); ++ dsll(reg, reg, 8); ++ daddu(reg, tmp, reg); ++ lbu(tmp, BCP, offset); ++ dsll(reg, reg, 8); ++ daddu(reg, tmp, reg); ++ } else { ++ lwu(reg, BCP, offset); ++ } ++} ++ ++#ifndef CC_INTERP ++ ++void InterpreterMacroAssembler::call_VM_leaf_base(address entry_point, ++ int number_of_arguments) { ++ // interpreter specific ++ // ++ // Note: No need to save/restore bcp & locals (r13 & r14) pointer ++ // since these are callee saved registers and no blocking/ ++ // GC can happen in leaf calls. ++ // Further Note: DO NOT save/restore bcp/locals. If a caller has ++ // already saved them so that it can use BCP/LVP as temporaries ++ // then a save/restore here will DESTROY the copy the caller ++ // saved! There used to be a save_bcp() that only happened in ++ // the ASSERT path (no restore_bcp). Which caused bizarre failures ++ // when jvm built with ASSERTs. ++#ifdef ASSERT ++ save_bcp(); ++ { ++ Label L; ++ ld(AT,FP,frame::interpreter_frame_last_sp_offset * wordSize); ++ beq(AT,R0,L); ++ delayed()->nop(); ++ stop("InterpreterMacroAssembler::call_VM_leaf_base: last_sp != NULL"); ++ bind(L); ++ } ++#endif ++ // super call ++ MacroAssembler::call_VM_leaf_base(entry_point, number_of_arguments); ++ // interpreter specific ++ // Used to ASSERT that BCP/LVP were equal to frame's bcp/locals ++ // but since they may not have been saved (and we don't want to ++ // save them here (see note above) the assert is invalid. ++} ++ ++void InterpreterMacroAssembler::call_VM_base(Register oop_result, ++ Register java_thread, ++ Register last_java_sp, ++ address entry_point, ++ int number_of_arguments, ++ bool check_exceptions) { ++ // interpreter specific ++ // ++ // Note: Could avoid restoring locals ptr (callee saved) - however doesn't ++ // really make a difference for these runtime calls, since they are ++ // slow anyway. Btw., bcp must be saved/restored since it may change ++ // due to GC. ++ assert(java_thread == noreg , "not expecting a precomputed java thread"); ++ save_bcp(); ++#ifdef ASSERT ++ { ++ Label L; ++ ld(AT, FP, frame::interpreter_frame_last_sp_offset * wordSize); ++ beq(AT, R0, L); ++ delayed()->nop(); ++ stop("InterpreterMacroAssembler::call_VM_base: last_sp != NULL"); ++ bind(L); ++ } ++#endif /* ASSERT */ ++ // super call ++ MacroAssembler::call_VM_base(oop_result, java_thread, last_java_sp, ++ entry_point, number_of_arguments, ++ check_exceptions); ++ // interpreter specific ++ restore_bcp(); ++ restore_locals(); ++} ++ ++ ++void InterpreterMacroAssembler::check_and_handle_popframe(Register java_thread) { ++ if (JvmtiExport::can_pop_frame()) { ++ Label L; ++ // Initiate popframe handling only if it is not already being ++ // processed. If the flag has the popframe_processing bit set, it ++ // means that this code is called *during* popframe handling - we ++ // don't want to reenter. ++ // This method is only called just after the call into the vm in ++ // call_VM_base, so the arg registers are available. ++ // Not clear if any other register is available, so load AT twice ++ assert(AT != java_thread, "check"); ++ lw(AT, java_thread, in_bytes(JavaThread::popframe_condition_offset())); ++ andi(AT, AT, JavaThread::popframe_pending_bit); ++ beq(AT, R0, L); ++ delayed()->nop(); ++ ++ lw(AT, java_thread, in_bytes(JavaThread::popframe_condition_offset())); ++ andi(AT, AT, JavaThread::popframe_processing_bit); ++ bne(AT, R0, L); ++ delayed()->nop(); ++ call_VM_leaf(CAST_FROM_FN_PTR(address, Interpreter::remove_activation_preserving_args_entry)); ++ jr(V0); ++ delayed()->nop(); ++ bind(L); ++ } ++} ++ ++ ++void InterpreterMacroAssembler::load_earlyret_value(TosState state) { ++ Register thread = T8; ++#ifndef OPT_THREAD ++ get_thread(thread); ++#else ++ move(T8, TREG); ++#endif ++ ld_ptr(thread, thread, in_bytes(JavaThread::jvmti_thread_state_offset())); ++ const Address tos_addr (thread, in_bytes(JvmtiThreadState::earlyret_tos_offset())); ++ const Address oop_addr (thread, in_bytes(JvmtiThreadState::earlyret_oop_offset())); ++ const Address val_addr (thread, in_bytes(JvmtiThreadState::earlyret_value_offset())); ++ //V0, oop_addr,V1,val_addr ++ switch (state) { ++ case atos: ++ ld_ptr(V0, oop_addr); ++ st_ptr(R0, oop_addr); ++ verify_oop(V0, state); ++ break; ++ case ltos: ++ ld_ptr(V0, val_addr); // fall through ++ break; ++ case btos: // fall through ++ case ztos: // fall through ++ case ctos: // fall through ++ case stos: // fall through ++ case itos: ++ lw(V0, val_addr); ++ break; ++ case ftos: ++ lwc1(F0, thread, in_bytes(JvmtiThreadState::earlyret_value_offset())); ++ break; ++ case dtos: ++ ldc1(F0, thread, in_bytes(JvmtiThreadState::earlyret_value_offset())); ++ break; ++ case vtos: /* nothing to do */ break; ++ default : ShouldNotReachHere(); ++ } ++ // Clean up tos value in the thread object ++ move(AT, (int)ilgl); ++ sw(AT, tos_addr); ++ sw(R0, thread, in_bytes(JvmtiThreadState::earlyret_value_offset())); ++} ++ ++ ++void InterpreterMacroAssembler::check_and_handle_earlyret(Register java_thread) { ++ if (JvmtiExport::can_force_early_return()) { ++ Label L; ++ Register tmp = T9; ++ ++ assert(java_thread != AT, "check"); ++ assert(java_thread != tmp, "check"); ++ ld_ptr(AT, java_thread, in_bytes(JavaThread::jvmti_thread_state_offset())); ++ beq(AT, R0, L); ++ delayed()->nop(); ++ ++ // Initiate earlyret handling only if it is not already being processed. ++ // If the flag has the earlyret_processing bit set, it means that this code ++ // is called *during* earlyret handling - we don't want to reenter. ++ lw(AT, AT, in_bytes(JvmtiThreadState::earlyret_state_offset())); ++ move(tmp, JvmtiThreadState::earlyret_pending); ++ bne(tmp, AT, L); ++ delayed()->nop(); ++ ++ // Call Interpreter::remove_activation_early_entry() to get the address of the ++ // same-named entrypoint in the generated interpreter code. ++ ld_ptr(tmp, java_thread, in_bytes(JavaThread::jvmti_thread_state_offset())); ++ lw(AT, tmp, in_bytes(JvmtiThreadState::earlyret_tos_offset())); ++ move(A0, AT); ++ call_VM_leaf(CAST_FROM_FN_PTR(address, Interpreter::remove_activation_early_entry), A0); ++ jr(V0); ++ delayed()->nop(); ++ bind(L); ++ } ++} ++ ++ ++void InterpreterMacroAssembler::get_unsigned_2_byte_index_at_bcp(Register reg, ++ int bcp_offset) { ++ assert(bcp_offset >= 0, "bcp is still pointing to start of bytecode"); ++ lbu(AT, BCP, bcp_offset); ++ lbu(reg, BCP, bcp_offset + 1); ++ ins(reg, AT, 8, 8); ++} ++ ++ ++void InterpreterMacroAssembler::get_cache_index_at_bcp(Register index, ++ int bcp_offset, ++ size_t index_size) { ++ assert(bcp_offset > 0, "bcp is still pointing to start of bytecode"); ++ if (index_size == sizeof(u2)) { ++ get_2_byte_integer_at_bcp(index, AT, bcp_offset); ++ } else if (index_size == sizeof(u4)) { ++ assert(EnableInvokeDynamic, "giant index used only for JSR 292"); ++ get_4_byte_integer_at_bcp(index, AT, bcp_offset); ++ // Check if the secondary index definition is still ~x, otherwise ++ // we have to change the following assembler code to calculate the ++ // plain index. ++ assert(ConstantPool::decode_invokedynamic_index(~123) == 123, "else change next line"); ++ nor(index, index, R0); ++ sll(index, index, 0); ++ } else if (index_size == sizeof(u1)) { ++ lbu(index, BCP, bcp_offset); ++ } else { ++ ShouldNotReachHere(); ++ } ++} ++ ++ ++void InterpreterMacroAssembler::get_cache_and_index_at_bcp(Register cache, ++ Register index, ++ int bcp_offset, ++ size_t index_size) { ++ assert_different_registers(cache, index); ++ get_cache_index_at_bcp(index, bcp_offset, index_size); ++ ld(cache, FP, frame::interpreter_frame_cache_offset * wordSize); ++ assert(sizeof(ConstantPoolCacheEntry) == 4 * wordSize, "adjust code below"); ++ assert(exact_log2(in_words(ConstantPoolCacheEntry::size())) == 2, "else change next line"); ++ shl(index, 2); ++} ++ ++ ++void InterpreterMacroAssembler::get_cache_and_index_and_bytecode_at_bcp(Register cache, ++ Register index, ++ Register bytecode, ++ int byte_no, ++ int bcp_offset, ++ size_t index_size) { ++ get_cache_and_index_at_bcp(cache, index, bcp_offset, index_size); ++ // We use a 32-bit load here since the layout of 64-bit words on ++ // little-endian machines allow us that. ++ dsll(AT, index, Address::times_ptr); ++ daddu(AT, cache, AT); ++ lw(bytecode, AT, in_bytes(ConstantPoolCache::base_offset() + ConstantPoolCacheEntry::indices_offset())); ++ if(os::is_MP()) { ++ sync(); // load acquire ++ } ++ ++ const int shift_count = (1 + byte_no) * BitsPerByte; ++ assert((byte_no == TemplateTable::f1_byte && shift_count == ConstantPoolCacheEntry::bytecode_1_shift) || ++ (byte_no == TemplateTable::f2_byte && shift_count == ConstantPoolCacheEntry::bytecode_2_shift), ++ "correct shift count"); ++ dsrl(bytecode, bytecode, shift_count); ++ assert(ConstantPoolCacheEntry::bytecode_1_mask == ConstantPoolCacheEntry::bytecode_2_mask, "common mask"); ++ move(AT, ConstantPoolCacheEntry::bytecode_1_mask); ++ andr(bytecode, bytecode, AT); ++} ++ ++void InterpreterMacroAssembler::get_cache_entry_pointer_at_bcp(Register cache, ++ Register tmp, ++ int bcp_offset, ++ size_t index_size) { ++ assert(bcp_offset > 0, "bcp is still pointing to start of bytecode"); ++ assert(cache != tmp, "must use different register"); ++ get_cache_index_at_bcp(tmp, bcp_offset, index_size); ++ assert(sizeof(ConstantPoolCacheEntry) == 4 * wordSize, "adjust code below"); ++ // convert from field index to ConstantPoolCacheEntry index ++ // and from word offset to byte offset ++ assert(exact_log2(in_bytes(ConstantPoolCacheEntry::size_in_bytes())) == 2 + LogBytesPerWord, "else change next line"); ++ shl(tmp, 2 + LogBytesPerWord); ++ ld(cache, FP, frame::interpreter_frame_cache_offset * wordSize); ++ // skip past the header ++ daddiu(cache, cache, in_bytes(ConstantPoolCache::base_offset())); ++ daddu(cache, cache, tmp); ++} ++ ++void InterpreterMacroAssembler::get_method_counters(Register method, ++ Register mcs, Label& skip) { ++ Label has_counters; ++ ld(mcs, method, in_bytes(Method::method_counters_offset())); ++ bne(mcs, R0, has_counters); ++ delayed()->nop(); ++ call_VM(noreg, CAST_FROM_FN_PTR(address, ++ InterpreterRuntime::build_method_counters), method); ++ ld(mcs, method, in_bytes(Method::method_counters_offset())); ++ beq(mcs, R0, skip); // No MethodCounters allocated, OutOfMemory ++ delayed()->nop(); ++ bind(has_counters); ++} ++ ++// Load object from cpool->resolved_references(index) ++void InterpreterMacroAssembler::load_resolved_reference_at_index( ++ Register result, Register index) { ++ assert_different_registers(result, index); ++ // convert from field index to resolved_references() index and from ++ // word index to byte offset. Since this is a java object, it can be compressed ++ Register tmp = index; // reuse ++ shl(tmp, LogBytesPerHeapOop); ++ ++ get_constant_pool(result); ++ // load pointer for resolved_references[] objArray ++ ld(result, result, ConstantPool::resolved_references_offset_in_bytes()); ++ // JNIHandles::resolve(obj); ++ ld(result, result, 0); //? is needed? ++ // Add in the index ++ daddu(result, result, tmp); ++ load_heap_oop(result, Address(result, arrayOopDesc::base_offset_in_bytes(T_OBJECT))); ++} ++ ++// Resets LVP to locals. Register sub_klass cannot be any of the above. ++void InterpreterMacroAssembler::gen_subtype_check( Register Rsup_klass, Register Rsub_klass, Label &ok_is_subtype ) { ++ assert( Rsub_klass != Rsup_klass, "Rsup_klass holds superklass" ); ++ assert( Rsub_klass != T1, "T1 holds 2ndary super array length" ); ++ assert( Rsub_klass != T0, "T0 holds 2ndary super array scan ptr" ); ++ // Profile the not-null value's klass. ++ // Here T9 and T1 are used as temporary registers. ++ profile_typecheck(T9, Rsub_klass, T1); // blows T9, reloads T1 ++ ++ // Do the check. ++ check_klass_subtype(Rsub_klass, Rsup_klass, T1, ok_is_subtype); // blows T1 ++ ++ // Profile the failure of the check. ++ profile_typecheck_failed(T9); // blows T9 ++} ++ ++ ++ ++// Java Expression Stack ++ ++void InterpreterMacroAssembler::pop_ptr(Register r) { ++ ld(r, SP, 0); ++ daddiu(SP, SP, Interpreter::stackElementSize); ++} ++ ++void InterpreterMacroAssembler::pop_i(Register r) { ++ lw(r, SP, 0); ++ daddiu(SP, SP, Interpreter::stackElementSize); ++} ++ ++void InterpreterMacroAssembler::pop_l(Register r) { ++ ld(r, SP, 0); ++ daddiu(SP, SP, 2 * Interpreter::stackElementSize); ++} ++ ++void InterpreterMacroAssembler::pop_f(FloatRegister r) { ++ lwc1(r, SP, 0); ++ daddiu(SP, SP, Interpreter::stackElementSize); ++} ++ ++void InterpreterMacroAssembler::pop_d(FloatRegister r) { ++ ldc1(r, SP, 0); ++ daddiu(SP, SP, 2 * Interpreter::stackElementSize); ++} ++ ++void InterpreterMacroAssembler::push_ptr(Register r) { ++ daddiu(SP, SP, - Interpreter::stackElementSize); ++ sd(r, SP, 0); ++} ++ ++void InterpreterMacroAssembler::push_i(Register r) { ++ // For compatibility reason, don't change to sw. ++ daddiu(SP, SP, - Interpreter::stackElementSize); ++ sd(r, SP, 0); ++} ++ ++void InterpreterMacroAssembler::push_l(Register r) { ++ daddiu(SP, SP, -2 * Interpreter::stackElementSize); ++ sd(r, SP, 0); ++ sd(R0, SP, Interpreter::stackElementSize); ++} ++ ++void InterpreterMacroAssembler::push_f(FloatRegister r) { ++ daddiu(SP, SP, - Interpreter::stackElementSize); ++ swc1(r, SP, 0); ++} ++ ++void InterpreterMacroAssembler::push_d(FloatRegister r) { ++ daddiu(SP, SP, -2 * Interpreter::stackElementSize); ++ sdc1(r, SP, 0); ++ sd(R0, SP, Interpreter::stackElementSize); ++} ++ ++void InterpreterMacroAssembler::pop(TosState state) { ++ switch (state) { ++ case atos: pop_ptr(); break; ++ case btos: ++ case ztos: ++ case ctos: ++ case stos: ++ case itos: pop_i(); break; ++ case ltos: pop_l(); break; ++ case ftos: pop_f(); break; ++ case dtos: pop_d(); break; ++ case vtos: /* nothing to do */ break; ++ default: ShouldNotReachHere(); ++ } ++ verify_oop(FSR, state); ++} ++ ++//FSR=V0,SSR=V1 ++void InterpreterMacroAssembler::push(TosState state) { ++ verify_oop(FSR, state); ++ switch (state) { ++ case atos: push_ptr(); break; ++ case btos: ++ case ztos: ++ case ctos: ++ case stos: ++ case itos: push_i(); break; ++ case ltos: push_l(); break; ++ case ftos: push_f(); break; ++ case dtos: push_d(); break; ++ case vtos: /* nothing to do */ break; ++ default : ShouldNotReachHere(); ++ } ++} ++ ++ ++ ++void InterpreterMacroAssembler::load_ptr(int n, Register val) { ++ ld(val, SP, Interpreter::expr_offset_in_bytes(n)); ++} ++ ++void InterpreterMacroAssembler::store_ptr(int n, Register val) { ++ sd(val, SP, Interpreter::expr_offset_in_bytes(n)); ++} ++ ++// Jump to from_interpreted entry of a call unless single stepping is possible ++// in this thread in which case we must call the i2i entry ++void InterpreterMacroAssembler::jump_from_interpreted(Register method, Register temp) { ++ // record last_sp ++ move(Rsender, SP); ++ sd(SP, FP, frame::interpreter_frame_last_sp_offset * wordSize); ++ ++ if (JvmtiExport::can_post_interpreter_events()) { ++ Label run_compiled_code; ++ // JVMTI events, such as single-stepping, are implemented partly by avoiding running ++ // compiled code in threads for which the event is enabled. Check here for ++ // interp_only_mode if these events CAN be enabled. ++#ifndef OPT_THREAD ++ get_thread(temp); ++#else ++ move(temp, TREG); ++#endif ++ // interp_only is an int, on little endian it is sufficient to test the byte only ++ // Is a cmpl faster? ++ lw(AT, temp, in_bytes(JavaThread::interp_only_mode_offset())); ++ beq(AT, R0, run_compiled_code); ++ delayed()->nop(); ++ ld(AT, method, in_bytes(Method::interpreter_entry_offset())); ++ jr(AT); ++ delayed()->nop(); ++ bind(run_compiled_code); ++ } ++ ++ ld(AT, method, in_bytes(Method::from_interpreted_offset())); ++ jr(AT); ++ delayed()->nop(); ++} ++ ++ ++// The following two routines provide a hook so that an implementation ++// can schedule the dispatch in two parts. mips64 does not do this. ++void InterpreterMacroAssembler::dispatch_prolog(TosState state, int step) { ++ // Nothing mips64 specific to be done here ++} ++ ++void InterpreterMacroAssembler::dispatch_epilog(TosState state, int step) { ++ dispatch_next(state, step); ++} ++ ++// assume the next bytecode in T8. ++void InterpreterMacroAssembler::dispatch_base(TosState state, ++ address* table, ++ bool verifyoop) { ++ if (VerifyActivationFrameSize) { ++ Label L; ++ ++ dsubu(T2, FP, SP); ++ int min_frame_size = (frame::link_offset - ++ frame::interpreter_frame_initial_sp_offset) * wordSize; ++ daddiu(T2, T2,- min_frame_size); ++ bgez(T2, L); ++ delayed()->nop(); ++ stop("broken stack frame"); ++ bind(L); ++ } ++ // FIXME: I do not know which register should pass to verify_oop ++ if (verifyoop) verify_oop(FSR, state); ++ dsll(T2, Rnext, LogBytesPerWord); ++ ++ if((long)table >= (long)Interpreter::dispatch_table(btos) && ++ (long)table <= (long)Interpreter::dispatch_table(vtos) ++ ) { ++ int table_size = (long)Interpreter::dispatch_table(itos) - (long)Interpreter::dispatch_table(stos); ++ int table_offset = ((int)state - (int)itos) * table_size; ++ ++ // GP points to the starting address of Interpreter::dispatch_table(itos). ++ // See StubGenerator::generate_call_stub(address& return_address) for the initialization of GP. ++ if(table_offset != 0) { ++ daddiu(T3, GP, table_offset); ++ if (UseLEXT1) { ++ gsldx(T3, T2, T3, 0); ++ } else { ++ daddu(T3, T2, T3); ++ ld(T3, T3, 0); ++ } ++ } else { ++ if (UseLEXT1) { ++ gsldx(T3, T2, GP, 0); ++ } else { ++ daddu(T3, T2, GP); ++ ld(T3, T3, 0); ++ } ++ } ++ } else { ++ li(T3, (long)table); ++ if (UseLEXT1) { ++ gsldx(T3, T2, T3, 0); ++ } else { ++ daddu(T3, T2, T3); ++ ld(T3, T3, 0); ++ } ++ } ++ jr(T3); ++ delayed()->nop(); ++} ++ ++void InterpreterMacroAssembler::dispatch_only(TosState state) { ++ dispatch_base(state, Interpreter::dispatch_table(state)); ++} ++ ++void InterpreterMacroAssembler::dispatch_only_normal(TosState state) { ++ dispatch_base(state, Interpreter::normal_table(state)); ++} ++ ++void InterpreterMacroAssembler::dispatch_only_noverify(TosState state) { ++ dispatch_base(state, Interpreter::normal_table(state), false); ++} ++ ++ ++void InterpreterMacroAssembler::dispatch_next(TosState state, int step) { ++ // load next bytecode (load before advancing r13 to prevent AGI) ++ lbu(Rnext, BCP, step); ++ increment(BCP, step); ++ dispatch_base(state, Interpreter::dispatch_table(state)); ++} ++ ++void InterpreterMacroAssembler::dispatch_via(TosState state, address* table) { ++ // load current bytecode ++ lbu(Rnext, BCP, 0); ++ dispatch_base(state, table); ++} ++ ++// remove activation ++// ++// Unlock the receiver if this is a synchronized method. ++// Unlock any Java monitors from syncronized blocks. ++// Remove the activation from the stack. ++// ++// If there are locked Java monitors ++// If throw_monitor_exception ++// throws IllegalMonitorStateException ++// Else if install_monitor_exception ++// installs IllegalMonitorStateException ++// Else ++// no error processing ++// used registers : T1, T2, T3, T8 ++// T1 : thread, method access flags ++// T2 : monitor entry pointer ++// T3 : method, monitor top ++// T8 : unlock flag ++void InterpreterMacroAssembler::remove_activation( ++ TosState state, ++ Register ret_addr, ++ bool throw_monitor_exception, ++ bool install_monitor_exception, ++ bool notify_jvmdi) { ++ // Note: Registers V0, V1 and F0, F1 may be in use for the result ++ // check if synchronized method ++ Label unlocked, unlock, no_unlock; ++ ++ // get the value of _do_not_unlock_if_synchronized into T8 ++#ifndef OPT_THREAD ++ Register thread = T1; ++ get_thread(thread); ++#else ++ Register thread = TREG; ++#endif ++ lb(T8, thread, in_bytes(JavaThread::do_not_unlock_if_synchronized_offset())); ++ // reset the flag ++ sb(R0, thread, in_bytes(JavaThread::do_not_unlock_if_synchronized_offset())); ++ // get method access flags ++ ld(T3, FP, frame::interpreter_frame_method_offset * wordSize); ++ lw(T1, T3, in_bytes(Method::access_flags_offset())); ++ andi(T1, T1, JVM_ACC_SYNCHRONIZED); ++ beq(T1, R0, unlocked); ++ delayed()->nop(); ++ ++ // Don't unlock anything if the _do_not_unlock_if_synchronized flag is set. ++ bne(T8, R0, no_unlock); ++ delayed()->nop(); ++ // unlock monitor ++ push(state); // save result ++ ++ // BasicObjectLock will be first in list, since this is a ++ // synchronized method. However, need to check that the object has ++ // not been unlocked by an explicit monitorexit bytecode. ++ daddiu(c_rarg0, FP, frame::interpreter_frame_initial_sp_offset * wordSize ++ - (int)sizeof(BasicObjectLock)); ++ // address of first monitor ++ ld(T1, c_rarg0, BasicObjectLock::obj_offset_in_bytes()); ++ bne(T1, R0, unlock); ++ delayed()->nop(); ++ pop(state); ++ if (throw_monitor_exception) { ++ // Entry already unlocked, need to throw exception ++ // I think mips do not need empty_FPU_stack ++ // remove possible return value from FPU-stack, otherwise stack could overflow ++ empty_FPU_stack(); ++ call_VM(NOREG, CAST_FROM_FN_PTR(address, ++ InterpreterRuntime::throw_illegal_monitor_state_exception)); ++ should_not_reach_here(); ++ } else { ++ // Monitor already unlocked during a stack unroll. If requested, ++ // install an illegal_monitor_state_exception. Continue with ++ // stack unrolling. ++ if (install_monitor_exception) { ++ // remove possible return value from FPU-stack, ++ // otherwise stack could overflow ++ empty_FPU_stack(); ++ call_VM(NOREG, CAST_FROM_FN_PTR(address, ++ InterpreterRuntime::new_illegal_monitor_state_exception)); ++ ++ } ++ ++ b(unlocked); ++ delayed()->nop(); ++ } ++ ++ bind(unlock); ++ unlock_object(c_rarg0); ++ pop(state); ++ ++ // Check that for block-structured locking (i.e., that all locked ++ // objects has been unlocked) ++ bind(unlocked); ++ ++ // V0, V1: Might contain return value ++ ++ // Check that all monitors are unlocked ++ { ++ Label loop, exception, entry, restart; ++ const int entry_size = frame::interpreter_frame_monitor_size() * wordSize; ++ const Address monitor_block_top(FP, ++ frame::interpreter_frame_monitor_block_top_offset * wordSize); ++ ++ bind(restart); ++ // points to current entry, starting with top-most entry ++ ld(c_rarg0, monitor_block_top); ++ // points to word before bottom of monitor block ++ daddiu(T3, FP, frame::interpreter_frame_initial_sp_offset * wordSize); ++ b(entry); ++ delayed()->nop(); ++ ++ // Entry already locked, need to throw exception ++ bind(exception); ++ ++ if (throw_monitor_exception) { ++ // Throw exception ++ // remove possible return value from FPU-stack, ++ // otherwise stack could overflow ++ empty_FPU_stack(); ++ MacroAssembler::call_VM(NOREG, CAST_FROM_FN_PTR(address, ++ InterpreterRuntime::throw_illegal_monitor_state_exception)); ++ should_not_reach_here(); ++ } else { ++ // Stack unrolling. Unlock object and install illegal_monitor_exception ++ // Unlock does not block, so don't have to worry about the frame ++ // We don't have to preserve c_rarg0, since we are going to ++ // throw an exception ++ ++ push(state); ++ unlock_object(c_rarg0); ++ pop(state); ++ ++ if (install_monitor_exception) { ++ empty_FPU_stack(); ++ call_VM(NOREG, CAST_FROM_FN_PTR(address, ++ InterpreterRuntime::new_illegal_monitor_state_exception)); ++ } ++ ++ b(restart); ++ delayed()->nop(); ++ } ++ ++ bind(loop); ++ ld(T1, c_rarg0, BasicObjectLock::obj_offset_in_bytes()); ++ bne(T1, R0, exception);// check if current entry is used ++ delayed()->nop(); ++ ++ daddiu(c_rarg0, c_rarg0, entry_size);// otherwise advance to next entry ++ bind(entry); ++ bne(c_rarg0, T3, loop); // check if bottom reached ++ delayed()->nop(); // if not at bottom then check this entry ++ } ++ ++ bind(no_unlock); ++ ++ // jvmpi support (jvmdi does not generate MethodExit on exception / popFrame) ++ if (notify_jvmdi) { ++ notify_method_exit(state, NotifyJVMTI); // preserve TOSCA ++ } else { ++ notify_method_exit(state, SkipNotifyJVMTI); // preserve TOSCA ++ } ++ ++ // remove activation ++ ld(SP, FP, frame::interpreter_frame_sender_sp_offset * wordSize); ++ ld(ret_addr, FP, frame::interpreter_frame_return_addr_offset * wordSize); ++ ld(FP, FP, frame::interpreter_frame_sender_fp_offset * wordSize); ++} ++ ++#endif // C_INTERP ++ ++// Lock object ++// ++// Args: ++// c_rarg1: BasicObjectLock to be used for locking ++// ++// Kills: ++// c_rarg0, c_rarg1, c_rarg2, c_rarg3, .. (param regs) ++// rscratch1, rscratch2 (scratch regs) ++void InterpreterMacroAssembler::lock_object(Register lock_reg) { ++ assert(lock_reg == c_rarg0, "The argument is only for looks. It must be c_rarg0"); ++ ++ if (UseHeavyMonitors) { ++ call_VM(NOREG, CAST_FROM_FN_PTR(address, InterpreterRuntime::monitorenter), ++ lock_reg); ++ } else { ++ Label done; ++ ++ const Register swap_reg = T2; // Must use T2 for cmpxchg instruction ++ const Register obj_reg = T1; // Will contain the oop ++ ++ const int obj_offset = BasicObjectLock::obj_offset_in_bytes(); ++ const int lock_offset = BasicObjectLock::lock_offset_in_bytes (); ++ const int mark_offset = lock_offset + ++ BasicLock::displaced_header_offset_in_bytes(); ++ ++ Label slow_case; ++ ++ // Load object pointer into obj_reg %T1 ++ ld(obj_reg, lock_reg, obj_offset); ++ ++ if (UseBiasedLocking) { ++ // Note: we use noreg for the temporary register since it's hard ++ // to come up with a free register on all incoming code paths ++ biased_locking_enter(lock_reg, obj_reg, swap_reg, noreg, false, done, &slow_case); ++ } ++ ++ ++ // Load (object->mark() | 1) into swap_reg %T2 ++ ld(AT, obj_reg, 0); ++ ori(swap_reg, AT, 1); ++ ++ ++ // Save (object->mark() | 1) into BasicLock's displaced header ++ sd(swap_reg, lock_reg, mark_offset); ++ ++ assert(lock_offset == 0, "displached header must be first word in BasicObjectLock"); ++ //if (os::is_MP()) { ++ // lock(); ++ //} ++ cmpxchg(lock_reg, Address(obj_reg, 0), swap_reg); ++ ++ if (PrintBiasedLockingStatistics) { ++ Label L; ++ beq(AT, R0, L); ++ delayed()->nop(); ++ push(T0); ++ push(T1); ++ atomic_inc32((address)BiasedLocking::fast_path_entry_count_addr(), 1, T0, T1); ++ pop(T1); ++ pop(T0); ++ bind(L); ++ } ++ ++ bne(AT, R0, done); ++ delayed()->nop(); ++ ++ // Test if the oopMark is an obvious stack pointer, i.e., ++ // 1) (mark & 3) == 0, and ++ // 2) SP <= mark < SP + os::pagesize() ++ // ++ // These 3 tests can be done by evaluating the following ++ // expression: ((mark - sp) & (3 - os::vm_page_size())), ++ // assuming both stack pointer and pagesize have their ++ // least significant 2 bits clear. ++ // NOTE: the oopMark is in swap_reg %T2 as the result of cmpxchg ++ ++ dsubu(swap_reg, swap_reg, SP); ++ move(AT, 3 - os::vm_page_size()); ++ andr(swap_reg, swap_reg, AT); ++ // Save the test result, for recursive case, the result is zero ++ sd(swap_reg, lock_reg, mark_offset); ++ if (PrintBiasedLockingStatistics) { ++ Label L; ++ bne(swap_reg, R0, L); ++ delayed()->nop(); ++ push(T0); ++ push(T1); ++ atomic_inc32((address)BiasedLocking::fast_path_entry_count_addr(), 1, T0, T1); ++ pop(T1); ++ pop(T0); ++ bind(L); ++ } ++ ++ beq(swap_reg, R0, done); ++ delayed()->nop(); ++ bind(slow_case); ++ // Call the runtime routine for slow case ++ call_VM(NOREG, CAST_FROM_FN_PTR(address, InterpreterRuntime::monitorenter), lock_reg); ++ ++ bind(done); ++ } ++} ++ ++ ++// Unlocks an object. Used in monitorexit bytecode and ++// remove_activation. Throws an IllegalMonitorException if object is ++// not locked by current thread. ++// ++// Args: ++// c_rarg1: BasicObjectLock for lock ++// ++// Kills: ++// c_rarg0, c_rarg1, c_rarg2, c_rarg3, ... (param regs) ++// rscratch1, rscratch2 (scratch regs) ++// Argument: T6 : Points to BasicObjectLock structure for lock ++// Argument: c_rarg0 : Points to BasicObjectLock structure for lock ++// Throw an IllegalMonitorException if object is not locked by current thread ++void InterpreterMacroAssembler::unlock_object(Register lock_reg) { ++ assert(lock_reg == c_rarg0, "The argument is only for looks. It must be c_rarg0"); ++ ++ if (UseHeavyMonitors) { ++ call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::monitorexit), lock_reg); ++ } else { ++ Label done; ++ ++ const Register swap_reg = T2; // Must use T2 for cmpxchg instruction ++ const Register header_reg = T3; // Will contain the old oopMark ++ const Register obj_reg = T1; // Will contain the oop ++ ++ save_bcp(); // Save in case of exception ++ ++ // Convert from BasicObjectLock structure to object and BasicLock structure ++ // Store the BasicLock address into %T2 ++ daddiu(swap_reg, lock_reg, BasicObjectLock::lock_offset_in_bytes()); ++ ++ // Load oop into obj_reg(%T1) ++ ld(obj_reg, lock_reg, BasicObjectLock::obj_offset_in_bytes ()); ++ //free entry ++ sd(R0, lock_reg, BasicObjectLock::obj_offset_in_bytes()); ++ if (UseBiasedLocking) { ++ biased_locking_exit(obj_reg, header_reg, done); ++ } ++ ++ // Load the old header from BasicLock structure ++ ld(header_reg, swap_reg, BasicLock::displaced_header_offset_in_bytes()); ++ // zero for recursive case ++ beq(header_reg, R0, done); ++ delayed()->nop(); ++ ++ // Atomic swap back the old header ++ if (os::is_MP()); //lock(); ++ cmpxchg(header_reg, Address(obj_reg, 0), swap_reg); ++ ++ // zero for recursive case ++ bne(AT, R0, done); ++ delayed()->nop(); ++ ++ // Call the runtime routine for slow case. ++ sd(obj_reg, lock_reg, BasicObjectLock::obj_offset_in_bytes()); // restore obj ++ call_VM(NOREG, ++ CAST_FROM_FN_PTR(address, InterpreterRuntime::monitorexit), ++ lock_reg); ++ ++ bind(done); ++ ++ restore_bcp(); ++ } ++} ++ ++#ifndef CC_INTERP ++ ++void InterpreterMacroAssembler::test_method_data_pointer(Register mdp, ++ Label& zero_continue) { ++ assert(ProfileInterpreter, "must be profiling interpreter"); ++ ld(mdp, Address(FP, frame::interpreter_frame_mdx_offset * wordSize)); ++ beq(mdp, R0, zero_continue); ++ delayed()->nop(); ++} ++ ++ ++// Set the method data pointer for the current bcp. ++void InterpreterMacroAssembler::set_method_data_pointer_for_bcp() { ++ assert(ProfileInterpreter, "must be profiling interpreter"); ++ Label set_mdp; ++ ++ // V0 and T0 will be used as two temporary registers. ++ push2(V0, T0); ++ ++ get_method(T0); ++ // Test MDO to avoid the call if it is NULL. ++ ld(V0, T0, in_bytes(Method::method_data_offset())); ++ beq(V0, R0, set_mdp); ++ delayed()->nop(); ++ ++ // method: T0 ++ // bcp: BCP --> S0 ++ call_VM_leaf(CAST_FROM_FN_PTR(address, InterpreterRuntime::bcp_to_di), T0, BCP); ++ // mdi: V0 ++ // mdo is guaranteed to be non-zero here, we checked for it before the call. ++ get_method(T0); ++ ld(T0, T0, in_bytes(Method::method_data_offset())); ++ daddiu(T0, T0, in_bytes(MethodData::data_offset())); ++ daddu(V0, T0, V0); ++ bind(set_mdp); ++ sd(V0, FP, frame::interpreter_frame_mdx_offset * wordSize); ++ pop2(V0, T0); ++} ++ ++void InterpreterMacroAssembler::verify_method_data_pointer() { ++ assert(ProfileInterpreter, "must be profiling interpreter"); ++#ifdef ASSERT ++ Label verify_continue; ++ Register method = V0; ++ Register mdp = V1; ++ Register tmp = A0; ++ push(method); ++ push(mdp); ++ push(tmp); ++ test_method_data_pointer(mdp, verify_continue); // If mdp is zero, continue ++ get_method(method); ++ ++ // If the mdp is valid, it will point to a DataLayout header which is ++ // consistent with the bcp. The converse is highly probable also. ++ lhu(tmp, mdp, in_bytes(DataLayout::bci_offset())); ++ ld(AT, method, in_bytes(Method::const_offset())); ++ daddu(tmp, tmp, AT); ++ daddiu(tmp, tmp, in_bytes(ConstMethod::codes_offset())); ++ beq(tmp, BCP, verify_continue); ++ delayed()->nop(); ++ call_VM_leaf(CAST_FROM_FN_PTR(address, InterpreterRuntime::verify_mdp), method, BCP, mdp); ++ bind(verify_continue); ++ pop(tmp); ++ pop(mdp); ++ pop(method); ++#endif // ASSERT ++} ++ ++ ++void InterpreterMacroAssembler::set_mdp_data_at(Register mdp_in, ++ int constant, ++ Register value) { ++ assert(ProfileInterpreter, "must be profiling interpreter"); ++ Address data(mdp_in, constant); ++ sd(value, data); ++} ++ ++ ++void InterpreterMacroAssembler::increment_mdp_data_at(Register mdp_in, ++ int constant, ++ bool decrement) { ++ // Counter address ++ Address data(mdp_in, constant); ++ ++ increment_mdp_data_at(data, decrement); ++} ++ ++void InterpreterMacroAssembler::increment_mdp_data_at(Address data, ++ bool decrement) { ++ assert(ProfileInterpreter, "must be profiling interpreter"); ++ // %%% this does 64bit counters at best it is wasting space ++ // at worst it is a rare bug when counters overflow ++ Register tmp = S0; ++ push(tmp); ++ if (decrement) { ++ // Decrement the register. ++ ld(AT, data); ++ daddiu(tmp, AT, (int32_t) -DataLayout::counter_increment); ++ // If the decrement causes the counter to overflow, stay negative ++ Label L; ++ slt(AT, tmp, R0); ++ bne(AT, R0, L); ++ delayed()->nop(); ++ daddiu(tmp, tmp, (int32_t) DataLayout::counter_increment); ++ bind(L); ++ sd(tmp, data); ++ } else { ++ assert(DataLayout::counter_increment == 1, ++ "flow-free idiom only works with 1"); ++ ld(AT, data); ++ // Increment the register. ++ daddiu(tmp, AT, DataLayout::counter_increment); ++ // If the increment causes the counter to overflow, pull back by 1. ++ slt(AT, tmp, R0); ++ dsubu(tmp, tmp, AT); ++ sd(tmp, data); ++ } ++ pop(tmp); ++} ++ ++ ++void InterpreterMacroAssembler::increment_mdp_data_at(Register mdp_in, ++ Register reg, ++ int constant, ++ bool decrement) { ++ Register tmp = S0; ++ push(S0); ++ if (decrement) { ++ // Decrement the register. ++ daddu(AT, mdp_in, reg); ++ assert(Assembler::is_simm16(constant), "constant is not a simm16 !"); ++ ld(AT, AT, constant); ++ ++ daddiu(tmp, AT, (int32_t) -DataLayout::counter_increment); ++ // If the decrement causes the counter to overflow, stay negative ++ Label L; ++ slt(AT, tmp, R0); ++ bne(AT, R0, L); ++ delayed()->nop(); ++ daddiu(tmp, tmp, (int32_t) DataLayout::counter_increment); ++ bind(L); ++ ++ daddu(AT, mdp_in, reg); ++ sd(tmp, AT, constant); ++ } else { ++ daddu(AT, mdp_in, reg); ++ assert(Assembler::is_simm16(constant), "constant is not a simm16 !"); ++ ld(AT, AT, constant); ++ ++ // Increment the register. ++ daddiu(tmp, AT, DataLayout::counter_increment); ++ // If the increment causes the counter to overflow, pull back by 1. ++ slt(AT, tmp, R0); ++ dsubu(tmp, tmp, AT); ++ ++ daddu(AT, mdp_in, reg); ++ sd(tmp, AT, constant); ++ } ++ pop(S0); ++} ++ ++void InterpreterMacroAssembler::set_mdp_flag_at(Register mdp_in, ++ int flag_byte_constant) { ++ assert(ProfileInterpreter, "must be profiling interpreter"); ++ int header_offset = in_bytes(DataLayout::header_offset()); ++ int header_bits = DataLayout::flag_mask_to_header_mask(flag_byte_constant); ++ // Set the flag ++ lw(AT, Address(mdp_in, header_offset)); ++ if(Assembler::is_simm16(header_bits)) { ++ ori(AT, AT, header_bits); ++ } else { ++ push(T8); ++ // T8 is used as a temporary register. ++ move(T8, header_bits); ++ orr(AT, AT, T8); ++ pop(T8); ++ } ++ sw(AT, Address(mdp_in, header_offset)); ++} ++ ++ ++ ++void InterpreterMacroAssembler::test_mdp_data_at(Register mdp_in, ++ int offset, ++ Register value, ++ Register test_value_out, ++ Label& not_equal_continue) { ++ assert(ProfileInterpreter, "must be profiling interpreter"); ++ if (test_value_out == noreg) { ++ ld(AT, Address(mdp_in, offset)); ++ bne(AT, value, not_equal_continue); ++ delayed()->nop(); ++ } else { ++ // Put the test value into a register, so caller can use it: ++ ld(test_value_out, Address(mdp_in, offset)); ++ bne(value, test_value_out, not_equal_continue); ++ delayed()->nop(); ++ } ++} ++ ++ ++void InterpreterMacroAssembler::update_mdp_by_offset(Register mdp_in, ++ int offset_of_disp) { ++ assert(ProfileInterpreter, "must be profiling interpreter"); ++ assert(Assembler::is_simm16(offset_of_disp), "offset is not an simm16"); ++ ld(AT, mdp_in, offset_of_disp); ++ daddu(mdp_in, mdp_in, AT); ++ sd(mdp_in, Address(FP, frame::interpreter_frame_mdx_offset * wordSize)); ++} ++ ++ ++void InterpreterMacroAssembler::update_mdp_by_offset(Register mdp_in, ++ Register reg, ++ int offset_of_disp) { ++ assert(ProfileInterpreter, "must be profiling interpreter"); ++ daddu(AT, reg, mdp_in); ++ assert(Assembler::is_simm16(offset_of_disp), "offset is not an simm16"); ++ ld(AT, AT, offset_of_disp); ++ daddu(mdp_in, mdp_in, AT); ++ sd(mdp_in, Address(FP, frame::interpreter_frame_mdx_offset * wordSize)); ++} ++ ++ ++void InterpreterMacroAssembler::update_mdp_by_constant(Register mdp_in, ++ int constant) { ++ assert(ProfileInterpreter, "must be profiling interpreter"); ++ if(Assembler::is_simm16(constant)) { ++ daddiu(mdp_in, mdp_in, constant); ++ } else { ++ move(AT, constant); ++ daddu(mdp_in, mdp_in, AT); ++ } ++ sd(mdp_in, Address(FP, frame::interpreter_frame_mdx_offset * wordSize)); ++} ++ ++ ++void InterpreterMacroAssembler::update_mdp_for_ret(Register return_bci) { ++ assert(ProfileInterpreter, "must be profiling interpreter"); ++ push(return_bci); // save/restore across call_VM ++ call_VM(noreg, ++ CAST_FROM_FN_PTR(address, InterpreterRuntime::update_mdp_for_ret), ++ return_bci); ++ pop(return_bci); ++} ++ ++ ++void InterpreterMacroAssembler::profile_taken_branch(Register mdp, ++ Register bumped_count) { ++ if (ProfileInterpreter) { ++ Label profile_continue; ++ ++ // If no method data exists, go to profile_continue. ++ // Otherwise, assign to mdp ++ test_method_data_pointer(mdp, profile_continue); ++ ++ // We are taking a branch. Increment the taken count. ++ // We inline increment_mdp_data_at to return bumped_count in a register ++ //increment_mdp_data_at(mdp, in_bytes(JumpData::taken_offset())); ++ ld(bumped_count, mdp, in_bytes(JumpData::taken_offset())); ++ assert(DataLayout::counter_increment == 1, ++ "flow-free idiom only works with 1"); ++ push(T8); ++ // T8 is used as a temporary register. ++ daddiu(T8, bumped_count, DataLayout::counter_increment); ++ slt(AT, T8, R0); ++ dsubu(bumped_count, T8, AT); ++ pop(T8); ++ sd(bumped_count, mdp, in_bytes(JumpData::taken_offset())); // Store back out ++ // The method data pointer needs to be updated to reflect the new target. ++ update_mdp_by_offset(mdp, in_bytes(JumpData::displacement_offset())); ++ bind(profile_continue); ++ } ++} ++ ++ ++void InterpreterMacroAssembler::profile_not_taken_branch(Register mdp) { ++ if (ProfileInterpreter) { ++ Label profile_continue; ++ ++ // If no method data exists, go to profile_continue. ++ test_method_data_pointer(mdp, profile_continue); ++ ++ // We are taking a branch. Increment the not taken count. ++ increment_mdp_data_at(mdp, in_bytes(BranchData::not_taken_offset())); ++ ++ // The method data pointer needs to be updated to correspond to ++ // the next bytecode ++ update_mdp_by_constant(mdp, in_bytes(BranchData::branch_data_size())); ++ bind(profile_continue); ++ } ++} ++ ++ ++void InterpreterMacroAssembler::profile_call(Register mdp) { ++ if (ProfileInterpreter) { ++ Label profile_continue; ++ ++ // If no method data exists, go to profile_continue. ++ test_method_data_pointer(mdp, profile_continue); ++ ++ // We are making a call. Increment the count. ++ increment_mdp_data_at(mdp, in_bytes(CounterData::count_offset())); ++ ++ // The method data pointer needs to be updated to reflect the new target. ++ update_mdp_by_constant(mdp, in_bytes(CounterData::counter_data_size())); ++ bind(profile_continue); ++ } ++} ++ ++ ++void InterpreterMacroAssembler::profile_final_call(Register mdp) { ++ if (ProfileInterpreter) { ++ Label profile_continue; ++ ++ // If no method data exists, go to profile_continue. ++ test_method_data_pointer(mdp, profile_continue); ++ ++ // We are making a call. Increment the count. ++ increment_mdp_data_at(mdp, in_bytes(CounterData::count_offset())); ++ ++ // The method data pointer needs to be updated to reflect the new target. ++ update_mdp_by_constant(mdp, ++ in_bytes(VirtualCallData:: ++ virtual_call_data_size())); ++ bind(profile_continue); ++ } ++} ++ ++ ++void InterpreterMacroAssembler::profile_virtual_call(Register receiver, ++ Register mdp, ++ Register reg2, ++ bool receiver_can_be_null) { ++ if (ProfileInterpreter) { ++ Label profile_continue; ++ ++ // If no method data exists, go to profile_continue. ++ test_method_data_pointer(mdp, profile_continue); ++ ++ Label skip_receiver_profile; ++ if (receiver_can_be_null) { ++ Label not_null; ++ bne(receiver, R0, not_null); ++ delayed()->nop(); ++ // We are making a call. Increment the count. ++ increment_mdp_data_at(mdp, in_bytes(CounterData::count_offset())); ++ beq(R0, R0, skip_receiver_profile); ++ delayed()->nop(); ++ bind(not_null); ++ } ++ ++ // Record the receiver type. ++ record_klass_in_profile(receiver, mdp, reg2, true); ++ bind(skip_receiver_profile); ++ ++ // The method data pointer needs to be updated to reflect the new target. ++ update_mdp_by_constant(mdp, ++ in_bytes(VirtualCallData:: ++ virtual_call_data_size())); ++ bind(profile_continue); ++ } ++} ++ ++// This routine creates a state machine for updating the multi-row ++// type profile at a virtual call site (or other type-sensitive bytecode). ++// The machine visits each row (of receiver/count) until the receiver type ++// is found, or until it runs out of rows. At the same time, it remembers ++// the location of the first empty row. (An empty row records null for its ++// receiver, and can be allocated for a newly-observed receiver type.) ++// Because there are two degrees of freedom in the state, a simple linear ++// search will not work; it must be a decision tree. Hence this helper ++// function is recursive, to generate the required tree structured code. ++// It's the interpreter, so we are trading off code space for speed. ++// See below for example code. ++void InterpreterMacroAssembler::record_klass_in_profile_helper( ++ Register receiver, Register mdp, ++ Register reg2, int start_row, ++ Label& done, bool is_virtual_call) { ++ if (TypeProfileWidth == 0) { ++ if (is_virtual_call) { ++ increment_mdp_data_at(mdp, in_bytes(CounterData::count_offset())); ++ } ++ return; ++ } ++ ++ int last_row = VirtualCallData::row_limit() - 1; ++ assert(start_row <= last_row, "must be work left to do"); ++ // Test this row for both the receiver and for null. ++ // Take any of three different outcomes: ++ // 1. found receiver => increment count and goto done ++ // 2. found null => keep looking for case 1, maybe allocate this cell ++ // 3. found something else => keep looking for cases 1 and 2 ++ // Case 3 is handled by a recursive call. ++ for (int row = start_row; row <= last_row; row++) { ++ Label next_test; ++ bool test_for_null_also = (row == start_row); ++ ++ // See if the receiver is receiver[n]. ++ int recvr_offset = in_bytes(VirtualCallData::receiver_offset(row)); ++ test_mdp_data_at(mdp, recvr_offset, receiver, ++ (test_for_null_also ? reg2 : noreg), ++ next_test); ++ // (Reg2 now contains the receiver from the CallData.) ++ ++ // The receiver is receiver[n]. Increment count[n]. ++ int count_offset = in_bytes(VirtualCallData::receiver_count_offset(row)); ++ increment_mdp_data_at(mdp, count_offset); ++ beq(R0, R0, done); ++ delayed()->nop(); ++ bind(next_test); ++ ++ if (test_for_null_also) { ++ Label found_null; ++ // Failed the equality check on receiver[n]... Test for null. ++ if (start_row == last_row) { ++ // The only thing left to do is handle the null case. ++ if (is_virtual_call) { ++ beq(reg2, R0, found_null); ++ delayed()->nop(); ++ // Receiver did not match any saved receiver and there is no empty row for it. ++ // Increment total counter to indicate polymorphic case. ++ increment_mdp_data_at(mdp, in_bytes(CounterData::count_offset())); ++ beq(R0, R0, done); ++ delayed()->nop(); ++ bind(found_null); ++ } else { ++ bne(reg2, R0, done); ++ delayed()->nop(); ++ } ++ break; ++ } ++ // Since null is rare, make it be the branch-taken case. ++ beq(reg2, R0, found_null); ++ delayed()->nop(); ++ ++ // Put all the "Case 3" tests here. ++ record_klass_in_profile_helper(receiver, mdp, reg2, start_row + 1, done, is_virtual_call); ++ ++ // Found a null. Keep searching for a matching receiver, ++ // but remember that this is an empty (unused) slot. ++ bind(found_null); ++ } ++ } ++ ++ // In the fall-through case, we found no matching receiver, but we ++ // observed the receiver[start_row] is NULL. ++ ++ // Fill in the receiver field and increment the count. ++ int recvr_offset = in_bytes(VirtualCallData::receiver_offset(start_row)); ++ set_mdp_data_at(mdp, recvr_offset, receiver); ++ int count_offset = in_bytes(VirtualCallData::receiver_count_offset(start_row)); ++ move(reg2, DataLayout::counter_increment); ++ set_mdp_data_at(mdp, count_offset, reg2); ++ if (start_row > 0) { ++ beq(R0, R0, done); ++ delayed()->nop(); ++ } ++} ++ ++// Example state machine code for three profile rows: ++// // main copy of decision tree, rooted at row[1] ++// if (row[0].rec == rec) { row[0].incr(); goto done; } ++// if (row[0].rec != NULL) { ++// // inner copy of decision tree, rooted at row[1] ++// if (row[1].rec == rec) { row[1].incr(); goto done; } ++// if (row[1].rec != NULL) { ++// // degenerate decision tree, rooted at row[2] ++// if (row[2].rec == rec) { row[2].incr(); goto done; } ++// if (row[2].rec != NULL) { goto done; } // overflow ++// row[2].init(rec); goto done; ++// } else { ++// // remember row[1] is empty ++// if (row[2].rec == rec) { row[2].incr(); goto done; } ++// row[1].init(rec); goto done; ++// } ++// } else { ++// // remember row[0] is empty ++// if (row[1].rec == rec) { row[1].incr(); goto done; } ++// if (row[2].rec == rec) { row[2].incr(); goto done; } ++// row[0].init(rec); goto done; ++// } ++// done: ++ ++void InterpreterMacroAssembler::record_klass_in_profile(Register receiver, ++ Register mdp, Register reg2, ++ bool is_virtual_call) { ++ assert(ProfileInterpreter, "must be profiling"); ++ Label done; ++ ++ record_klass_in_profile_helper(receiver, mdp, reg2, 0, done, is_virtual_call); ++ ++ bind (done); ++} ++ ++void InterpreterMacroAssembler::profile_ret(Register return_bci, ++ Register mdp) { ++ if (ProfileInterpreter) { ++ Label profile_continue; ++ uint row; ++ ++ // If no method data exists, go to profile_continue. ++ test_method_data_pointer(mdp, profile_continue); ++ ++ // Update the total ret count. ++ increment_mdp_data_at(mdp, in_bytes(CounterData::count_offset())); ++ ++ for (row = 0; row < RetData::row_limit(); row++) { ++ Label next_test; ++ ++ // See if return_bci is equal to bci[n]: ++ test_mdp_data_at(mdp, ++ in_bytes(RetData::bci_offset(row)), ++ return_bci, noreg, ++ next_test); ++ ++ // return_bci is equal to bci[n]. Increment the count. ++ increment_mdp_data_at(mdp, in_bytes(RetData::bci_count_offset(row))); ++ ++ // The method data pointer needs to be updated to reflect the new target. ++ update_mdp_by_offset(mdp, ++ in_bytes(RetData::bci_displacement_offset(row))); ++ beq(R0, R0, profile_continue); ++ delayed()->nop(); ++ bind(next_test); ++ } ++ ++ update_mdp_for_ret(return_bci); ++ ++ bind(profile_continue); ++ } ++} ++ ++ ++void InterpreterMacroAssembler::profile_null_seen(Register mdp) { ++ if (ProfileInterpreter) { ++ Label profile_continue; ++ ++ // If no method data exists, go to profile_continue. ++ test_method_data_pointer(mdp, profile_continue); ++ ++ set_mdp_flag_at(mdp, BitData::null_seen_byte_constant()); ++ ++ // The method data pointer needs to be updated. ++ int mdp_delta = in_bytes(BitData::bit_data_size()); ++ if (TypeProfileCasts) { ++ mdp_delta = in_bytes(VirtualCallData::virtual_call_data_size()); ++ } ++ update_mdp_by_constant(mdp, mdp_delta); ++ ++ bind(profile_continue); ++ } ++} ++ ++ ++void InterpreterMacroAssembler::profile_typecheck_failed(Register mdp) { ++ if (ProfileInterpreter && TypeProfileCasts) { ++ Label profile_continue; ++ ++ // If no method data exists, go to profile_continue. ++ test_method_data_pointer(mdp, profile_continue); ++ ++ int count_offset = in_bytes(CounterData::count_offset()); ++ // Back up the address, since we have already bumped the mdp. ++ count_offset -= in_bytes(VirtualCallData::virtual_call_data_size()); ++ ++ // *Decrement* the counter. We expect to see zero or small negatives. ++ increment_mdp_data_at(mdp, count_offset, true); ++ ++ bind (profile_continue); ++ } ++} ++ ++ ++void InterpreterMacroAssembler::profile_typecheck(Register mdp, Register klass, Register reg2) { ++ if (ProfileInterpreter) { ++ Label profile_continue; ++ ++ // If no method data exists, go to profile_continue. ++ test_method_data_pointer(mdp, profile_continue); ++ ++ // The method data pointer needs to be updated. ++ int mdp_delta = in_bytes(BitData::bit_data_size()); ++ if (TypeProfileCasts) { ++ mdp_delta = in_bytes(VirtualCallData::virtual_call_data_size()); ++ ++ // Record the object type. ++ record_klass_in_profile(klass, mdp, reg2, false); ++ } ++ update_mdp_by_constant(mdp, mdp_delta); ++ ++ bind(profile_continue); ++ } ++} ++ ++ ++void InterpreterMacroAssembler::profile_switch_default(Register mdp) { ++ if (ProfileInterpreter) { ++ Label profile_continue; ++ ++ // If no method data exists, go to profile_continue. ++ test_method_data_pointer(mdp, profile_continue); ++ ++ // Update the default case count ++ increment_mdp_data_at(mdp, ++ in_bytes(MultiBranchData::default_count_offset())); ++ ++ // The method data pointer needs to be updated. ++ update_mdp_by_offset(mdp, ++ in_bytes(MultiBranchData:: ++ default_displacement_offset())); ++ ++ bind(profile_continue); ++ } ++} ++ ++ ++void InterpreterMacroAssembler::profile_switch_case(Register index, ++ Register mdp, ++ Register reg2) { ++ if (ProfileInterpreter) { ++ Label profile_continue; ++ ++ // If no method data exists, go to profile_continue. ++ test_method_data_pointer(mdp, profile_continue); ++ ++ // Build the base (index * per_case_size_in_bytes()) + ++ // case_array_offset_in_bytes() ++ move(reg2, in_bytes(MultiBranchData::per_case_size())); ++ if (UseLEXT1) { ++ gsdmult(index, index, reg2); ++ } else { ++ dmult(index, reg2); ++ mflo(index); ++ } ++ daddiu(index, index, in_bytes(MultiBranchData::case_array_offset())); ++ ++ // Update the case count ++ increment_mdp_data_at(mdp, ++ index, ++ in_bytes(MultiBranchData::relative_count_offset())); ++ ++ // The method data pointer needs to be updated. ++ update_mdp_by_offset(mdp, ++ index, ++ in_bytes(MultiBranchData:: ++ relative_displacement_offset())); ++ ++ bind(profile_continue); ++ } ++} ++ ++ ++void InterpreterMacroAssembler::narrow(Register result) { ++ ++ // Get method->_constMethod->_result_type ++ ld(T9, FP, frame::interpreter_frame_method_offset * wordSize); ++ ld(T9, T9, in_bytes(Method::const_offset())); ++ lbu(T9, T9, in_bytes(ConstMethod::result_type_offset())); ++ ++ Label done, notBool, notByte, notChar; ++ ++ // common case first ++ addiu(AT, T9, -T_INT); ++ beq(AT, R0, done); ++ delayed()->nop(); ++ ++ // mask integer result to narrower return type. ++ addiu(AT, T9, -T_BOOLEAN); ++ bne(AT, R0, notBool); ++ delayed()->nop(); ++ andi(result, result, 0x1); ++ beq(R0, R0, done); ++ delayed()->nop(); ++ ++ bind(notBool); ++ addiu(AT, T9, -T_BYTE); ++ bne(AT, R0, notByte); ++ delayed()->nop(); ++ seb(result, result); ++ beq(R0, R0, done); ++ delayed()->nop(); ++ ++ bind(notByte); ++ addiu(AT, T9, -T_CHAR); ++ bne(AT, R0, notChar); ++ delayed()->nop(); ++ andi(result, result, 0xFFFF); ++ beq(R0, R0, done); ++ delayed()->nop(); ++ ++ bind(notChar); ++ seh(result, result); ++ ++ // Nothing to do for T_INT ++ bind(done); ++} ++ ++ ++void InterpreterMacroAssembler::profile_obj_type(Register obj, const Address& mdo_addr) { ++ Label update, next, none; ++ ++ verify_oop(obj); ++ ++ if (mdo_addr.index() != noreg) { ++ guarantee(T0 != mdo_addr.base(), "The base register will be corrupted !"); ++ guarantee(T0 != mdo_addr.index(), "The index register will be corrupted !"); ++ push(T0); ++ dsll(T0, mdo_addr.index(), mdo_addr.scale()); ++ daddu(T0, T0, mdo_addr.base()); ++ } ++ ++ bne(obj, R0, update); ++ delayed()->nop(); ++ ++ if (mdo_addr.index() == noreg) { ++ ld(AT, mdo_addr); ++ } else { ++ ld(AT, T0, mdo_addr.disp()); ++ } ++ ori(AT, AT, TypeEntries::null_seen); ++ if (mdo_addr.index() == noreg) { ++ sd(AT, mdo_addr); ++ } else { ++ sd(AT, T0, mdo_addr.disp()); ++ } ++ ++ beq(R0, R0, next); ++ delayed()->nop(); ++ ++ bind(update); ++ load_klass(obj, obj); ++ ++ if (mdo_addr.index() == noreg) { ++ ld(AT, mdo_addr); ++ } else { ++ ld(AT, T0, mdo_addr.disp()); ++ } ++ xorr(obj, obj, AT); ++ ++ assert(TypeEntries::type_klass_mask == -4, "must be"); ++ dextm(AT, obj, 2, 62); ++ beq(AT, R0, next); ++ delayed()->nop(); ++ ++ andi(AT, obj, TypeEntries::type_unknown); ++ bne(AT, R0, next); ++ delayed()->nop(); ++ ++ if (mdo_addr.index() == noreg) { ++ ld(AT, mdo_addr); ++ } else { ++ ld(AT, T0, mdo_addr.disp()); ++ } ++ beq(AT, R0, none); ++ delayed()->nop(); ++ ++ daddiu(AT, AT, -(TypeEntries::null_seen)); ++ beq(AT, R0, none); ++ delayed()->nop(); ++ ++ // There is a chance that the checks above (re-reading profiling ++ // data from memory) fail if another thread has just set the ++ // profiling to this obj's klass ++ if (mdo_addr.index() == noreg) { ++ ld(AT, mdo_addr); ++ } else { ++ ld(AT, T0, mdo_addr.disp()); ++ } ++ xorr(obj, obj, AT); ++ assert(TypeEntries::type_klass_mask == -4, "must be"); ++ dextm(AT, obj, 2, 62); ++ beq(AT, R0, next); ++ delayed()->nop(); ++ ++ // different than before. Cannot keep accurate profile. ++ if (mdo_addr.index() == noreg) { ++ ld(AT, mdo_addr); ++ } else { ++ ld(AT, T0, mdo_addr.disp()); ++ } ++ ori(AT, AT, TypeEntries::type_unknown); ++ if (mdo_addr.index() == noreg) { ++ sd(AT, mdo_addr); ++ } else { ++ sd(AT, T0, mdo_addr.disp()); ++ } ++ beq(R0, R0, next); ++ delayed()->nop(); ++ ++ bind(none); ++ // first time here. Set profile type. ++ if (mdo_addr.index() == noreg) { ++ sd(obj, mdo_addr); ++ } else { ++ sd(obj, T0, mdo_addr.disp()); ++ } ++ ++ bind(next); ++ if (mdo_addr.index() != noreg) { ++ pop(T0); ++ } ++} ++ ++void InterpreterMacroAssembler::profile_arguments_type(Register mdp, Register callee, Register tmp, bool is_virtual) { ++ if (!ProfileInterpreter) { ++ return; ++ } ++ ++ if (MethodData::profile_arguments() || MethodData::profile_return()) { ++ Label profile_continue; ++ ++ test_method_data_pointer(mdp, profile_continue); ++ ++ int off_to_start = is_virtual ? in_bytes(VirtualCallData::virtual_call_data_size()) : in_bytes(CounterData::counter_data_size()); ++ ++ lb(AT, mdp, in_bytes(DataLayout::tag_offset()) - off_to_start); ++ li(tmp, is_virtual ? DataLayout::virtual_call_type_data_tag : DataLayout::call_type_data_tag); ++ bne(tmp, AT, profile_continue); ++ delayed()->nop(); ++ ++ ++ if (MethodData::profile_arguments()) { ++ Label done; ++ int off_to_args = in_bytes(TypeEntriesAtCall::args_data_offset()); ++ if (Assembler::is_simm16(off_to_args)) { ++ daddiu(mdp, mdp, off_to_args); ++ } else { ++ move(AT, off_to_args); ++ daddu(mdp, mdp, AT); ++ } ++ ++ ++ for (int i = 0; i < TypeProfileArgsLimit; i++) { ++ if (i > 0 || MethodData::profile_return()) { ++ // If return value type is profiled we may have no argument to profile ++ ld(tmp, mdp, in_bytes(TypeEntriesAtCall::cell_count_offset())-off_to_args); ++ ++ if (Assembler::is_simm16(-1 * i * TypeStackSlotEntries::per_arg_count())) { ++ addiu32(tmp, tmp, -1 * i * TypeStackSlotEntries::per_arg_count()); ++ } else { ++ li(AT, i*TypeStackSlotEntries::per_arg_count()); ++ subu32(tmp, tmp, AT); ++ } ++ ++ li(AT, TypeStackSlotEntries::per_arg_count()); ++ slt(AT, tmp, AT); ++ bne(AT, R0, done); ++ delayed()->nop(); ++ } ++ ld(tmp, callee, in_bytes(Method::const_offset())); ++ ++ lhu(tmp, tmp, in_bytes(ConstMethod::size_of_parameters_offset())); ++ ++ // stack offset o (zero based) from the start of the argument ++ // list, for n arguments translates into offset n - o - 1 from ++ // the end of the argument list ++ ld(AT, mdp, in_bytes(TypeEntriesAtCall::stack_slot_offset(i))-off_to_args); ++ subu(tmp, tmp, AT); ++ ++ addiu32(tmp, tmp, -1); ++ ++ Address arg_addr = argument_address(tmp); ++ ld(tmp, arg_addr); ++ ++ Address mdo_arg_addr(mdp, in_bytes(TypeEntriesAtCall::argument_type_offset(i))-off_to_args); ++ profile_obj_type(tmp, mdo_arg_addr); ++ ++ int to_add = in_bytes(TypeStackSlotEntries::per_arg_size()); ++ if (Assembler::is_simm16(to_add)) { ++ daddiu(mdp, mdp, to_add); ++ } else { ++ move(AT, to_add); ++ daddu(mdp, mdp, AT); ++ } ++ ++ off_to_args += to_add; ++ } ++ ++ if (MethodData::profile_return()) { ++ ld(tmp, mdp, in_bytes(TypeEntriesAtCall::cell_count_offset())-off_to_args); ++ ++ int tmp_arg_counts = TypeProfileArgsLimit*TypeStackSlotEntries::per_arg_count(); ++ if (Assembler::is_simm16(-1 * tmp_arg_counts)) { ++ addiu32(tmp, tmp, -1 * tmp_arg_counts); ++ } else { ++ move(AT, tmp_arg_counts); ++ subu32(mdp, mdp, AT); ++ } ++ } ++ ++ bind(done); ++ ++ if (MethodData::profile_return()) { ++ // We're right after the type profile for the last ++ // argument. tmp is the number of cells left in the ++ // CallTypeData/VirtualCallTypeData to reach its end. Non null ++ // if there's a return to profile. ++ assert(ReturnTypeEntry::static_cell_count() < TypeStackSlotEntries::per_arg_count(), "can't move past ret type"); ++ sll(tmp, tmp, exact_log2(DataLayout::cell_size)); ++ daddu(mdp, mdp, tmp); ++ } ++ sd(mdp, FP, frame::interpreter_frame_mdx_offset * wordSize); ++ } else { ++ assert(MethodData::profile_return(), "either profile call args or call ret"); ++ update_mdp_by_constant(mdp, in_bytes(TypeEntriesAtCall::return_only_size())); ++ } ++ ++ // mdp points right after the end of the ++ // CallTypeData/VirtualCallTypeData, right after the cells for the ++ // return value type if there's one ++ ++ bind(profile_continue); ++ } ++} ++ ++void InterpreterMacroAssembler::profile_return_type(Register mdp, Register ret, Register tmp) { ++ assert_different_registers(mdp, ret, tmp, _bcp_register); ++ if (ProfileInterpreter && MethodData::profile_return()) { ++ Label profile_continue, done; ++ ++ test_method_data_pointer(mdp, profile_continue); ++ ++ if (MethodData::profile_return_jsr292_only()) { ++ // If we don't profile all invoke bytecodes we must make sure ++ // it's a bytecode we indeed profile. We can't go back to the ++ // begining of the ProfileData we intend to update to check its ++ // type because we're right after it and we don't known its ++ // length ++ Label do_profile; ++ lb(tmp, _bcp_register, 0); ++ daddiu(AT, tmp, -1 * Bytecodes::_invokedynamic); ++ beq(AT, R0, do_profile); ++ delayed()->daddiu(AT, tmp, -1 * Bytecodes::_invokehandle); ++ beq(AT, R0, do_profile); ++ delayed()->nop(); ++ ++ get_method(tmp); ++ lb(tmp, tmp, Method::intrinsic_id_offset_in_bytes()); ++ li(AT, vmIntrinsics::_compiledLambdaForm); ++ bne(tmp, AT, profile_continue); ++ delayed()->nop(); ++ ++ bind(do_profile); ++ } ++ ++ Address mdo_ret_addr(mdp, -in_bytes(ReturnTypeEntry::size())); ++ daddu(tmp, ret, R0); ++ profile_obj_type(tmp, mdo_ret_addr); ++ ++ bind(profile_continue); ++ } ++} ++ ++void InterpreterMacroAssembler::profile_parameters_type(Register mdp, Register tmp1, Register tmp2) { ++ guarantee(T9 == tmp1, "You are reqired to use T9 as the index register for MIPS !"); ++ ++ if (ProfileInterpreter && MethodData::profile_parameters()) { ++ Label profile_continue, done; ++ ++ test_method_data_pointer(mdp, profile_continue); ++ ++ // Load the offset of the area within the MDO used for ++ // parameters. If it's negative we're not profiling any parameters ++ lw(tmp1, mdp, in_bytes(MethodData::parameters_type_data_di_offset()) - in_bytes(MethodData::data_offset())); ++ bltz(tmp1, profile_continue); ++ delayed()->nop(); ++ ++ // Compute a pointer to the area for parameters from the offset ++ // and move the pointer to the slot for the last ++ // parameters. Collect profiling from last parameter down. ++ // mdo start + parameters offset + array length - 1 ++ daddu(mdp, mdp, tmp1); ++ ld(tmp1, mdp, in_bytes(ArrayData::array_len_offset())); ++ decrement(tmp1, TypeStackSlotEntries::per_arg_count()); ++ ++ ++ Label loop; ++ bind(loop); ++ ++ int off_base = in_bytes(ParametersTypeData::stack_slot_offset(0)); ++ int type_base = in_bytes(ParametersTypeData::type_offset(0)); ++ Address::ScaleFactor per_arg_scale = Address::times(DataLayout::cell_size); ++ Address arg_type(mdp, tmp1, per_arg_scale, type_base); ++ ++ // load offset on the stack from the slot for this parameter ++ dsll(AT, tmp1, per_arg_scale); ++ daddu(AT, AT, mdp); ++ ld(tmp2, AT, off_base); ++ ++ subu(tmp2, R0, tmp2); ++ ++ // read the parameter from the local area ++ dsll(AT, tmp2, Interpreter::stackElementScale()); ++ daddu(AT, AT, _locals_register); ++ ld(tmp2, AT, 0); ++ ++ // profile the parameter ++ profile_obj_type(tmp2, arg_type); ++ ++ // go to next parameter ++ decrement(tmp1, TypeStackSlotEntries::per_arg_count()); ++ bgtz(tmp1, loop); ++ delayed()->nop(); ++ ++ bind(profile_continue); ++ } ++} ++ ++void InterpreterMacroAssembler::verify_oop(Register reg, TosState state) { ++ if (state == atos) { ++ MacroAssembler::verify_oop(reg); ++ } ++} ++ ++void InterpreterMacroAssembler::verify_FPU(int stack_depth, TosState state) { ++} ++#endif // !CC_INTERP ++ ++ ++void InterpreterMacroAssembler::notify_method_entry() { ++ // Whenever JVMTI is interp_only_mode, method entry/exit events are sent to ++ // track stack depth. If it is possible to enter interp_only_mode we add ++ // the code to check if the event should be sent. ++ Register tempreg = T0; ++#ifndef OPT_THREAD ++ get_thread(T8); ++#else ++ move(T8, TREG); ++#endif ++ if (JvmtiExport::can_post_interpreter_events()) { ++ Label L; ++ lw(tempreg, T8, in_bytes(JavaThread::interp_only_mode_offset())); ++ beq(tempreg, R0, L); ++ delayed()->nop(); ++ call_VM(noreg, CAST_FROM_FN_PTR(address, ++ InterpreterRuntime::post_method_entry)); ++ bind(L); ++ } ++ ++ { ++ SkipIfEqual skip_if(this, &DTraceMethodProbes, 0); ++ get_method(S3); ++ call_VM_leaf(CAST_FROM_FN_PTR(address, SharedRuntime::dtrace_method_entry), ++ //Rthread, ++ T8, ++ //Rmethod); ++ S3); ++ } ++ ++} ++ ++void InterpreterMacroAssembler::notify_method_exit( ++ TosState state, NotifyMethodExitMode mode) { ++ Register tempreg = T0; ++#ifndef OPT_THREAD ++ get_thread(T8); ++#else ++ move(T8, TREG); ++#endif ++ // Whenever JVMTI is interp_only_mode, method entry/exit events are sent to ++ // track stack depth. If it is possible to enter interp_only_mode we add ++ // the code to check if the event should be sent. ++ if (mode == NotifyJVMTI && JvmtiExport::can_post_interpreter_events()) { ++ Label skip; ++ // Note: frame::interpreter_frame_result has a dependency on how the ++ // method result is saved across the call to post_method_exit. If this ++ // is changed then the interpreter_frame_result implementation will ++ // need to be updated too. ++ ++ // For c++ interpreter the result is always stored at a known location in the frame ++ // template interpreter will leave it on the top of the stack. ++ NOT_CC_INTERP(push(state);) ++ lw(tempreg, T8, in_bytes(JavaThread::interp_only_mode_offset())); ++ beq(tempreg, R0, skip); ++ delayed()->nop(); ++ call_VM(noreg, ++ CAST_FROM_FN_PTR(address, InterpreterRuntime::post_method_exit)); ++ bind(skip); ++ NOT_CC_INTERP(pop(state)); ++ } ++ ++ { ++ // Dtrace notification ++ SkipIfEqual skip_if(this, &DTraceMethodProbes, 0); ++ NOT_CC_INTERP(push(state);) ++ get_method(S3); ++ call_VM_leaf(CAST_FROM_FN_PTR(address, SharedRuntime::dtrace_method_exit), ++ //Rthread, Rmethod); ++ T8, S3); ++ NOT_CC_INTERP(pop(state)); ++ } ++} ++ ++// Jump if ((*counter_addr += increment) & mask) satisfies the condition. ++void InterpreterMacroAssembler::increment_mask_and_jump(Address counter_addr, ++ int increment, int mask, ++ Register scratch, bool preloaded, ++ Condition cond, Label* where) { ++ assert_different_registers(scratch, AT); ++ ++ if (!preloaded) { ++ lw(scratch, counter_addr); ++ } ++ addiu32(scratch, scratch, increment); ++ sw(scratch, counter_addr); ++ ++ move(AT, mask); ++ andr(scratch, scratch, AT); ++ ++ if (cond == Assembler::zero) { ++ beq(scratch, R0, *where); ++ delayed()->nop(); ++ } else { ++ unimplemented(); ++ } ++} +diff --git a/hotspot/src/cpu/mips/vm/interp_masm_mips_64.hpp b/hotspot/src/cpu/mips/vm/interp_masm_mips_64.hpp +new file mode 100644 +index 0000000000..a2ebdec3ad +--- /dev/null ++++ b/hotspot/src/cpu/mips/vm/interp_masm_mips_64.hpp +@@ -0,0 +1,269 @@ ++/* ++ * Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2015, 2019, Loongson Technology. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ * ++ */ ++ ++#ifndef CPU_MIPS_VM_INTERP_MASM_MIPS_64_HPP ++#define CPU_MIPS_VM_INTERP_MASM_MIPS_64_HPP ++ ++#include "asm/assembler.hpp" ++#include "asm/macroAssembler.hpp" ++#include "asm/macroAssembler.inline.hpp" ++#include "interpreter/invocationCounter.hpp" ++#include "runtime/frame.hpp" ++ ++// This file specializes the assember with interpreter-specific macros ++ ++ ++class InterpreterMacroAssembler: public MacroAssembler { ++#ifndef CC_INTERP ++ private: ++ ++ Register _locals_register; // register that contains the pointer to the locals ++ Register _bcp_register; // register that contains the bcp ++ ++ protected: ++ // Interpreter specific version of call_VM_base ++ virtual void call_VM_leaf_base(address entry_point, ++ int number_of_arguments); ++ ++ virtual void call_VM_base(Register oop_result, ++ Register java_thread, ++ Register last_java_sp, ++ address entry_point, ++ int number_of_arguments, ++ bool check_exceptions); ++ ++ virtual void check_and_handle_popframe(Register java_thread); ++ virtual void check_and_handle_earlyret(Register java_thread); ++ ++ // base routine for all dispatches ++ void dispatch_base(TosState state, address* table, bool verifyoop = true); ++#endif // CC_INTERP ++ ++ public: ++ // narrow int return value ++ void narrow(Register result); ++ ++ InterpreterMacroAssembler(CodeBuffer* code) : MacroAssembler(code), _locals_register(LVP), _bcp_register(BCP) {} ++ ++ void get_2_byte_integer_at_bcp(Register reg, Register tmp, int offset); ++ void get_4_byte_integer_at_bcp(Register reg, Register tmp, int offset); ++ ++ void load_earlyret_value(TosState state); ++ ++#ifdef CC_INTERP ++ void save_bcp() { /* not needed in c++ interpreter and harmless */ } ++ void restore_bcp() { /* not needed in c++ interpreter and harmless */ } ++ ++ // Helpers for runtime call arguments/results ++ void get_method(Register reg); ++ ++#else ++ ++ // Interpreter-specific registers ++ void save_bcp() { ++ sd(BCP, FP, frame::interpreter_frame_bcx_offset * wordSize); ++ } ++ ++ void restore_bcp() { ++ ld(BCP, FP, frame::interpreter_frame_bcx_offset * wordSize); ++ } ++ ++ void restore_locals() { ++ ld(LVP, FP, frame::interpreter_frame_locals_offset * wordSize); ++ } ++ ++ // Helpers for runtime call arguments/results ++ void get_method(Register reg) { ++ ld(reg, FP, frame::interpreter_frame_method_offset * wordSize); ++ } ++ ++ void get_const(Register reg){ ++ get_method(reg); ++ ld(reg, reg, in_bytes(Method::const_offset())); ++ } ++ ++ void get_constant_pool(Register reg) { ++ get_const(reg); ++ ld(reg, reg, in_bytes(ConstMethod::constants_offset())); ++ } ++ ++ void get_constant_pool_cache(Register reg) { ++ get_constant_pool(reg); ++ ld(reg, reg, ConstantPool::cache_offset_in_bytes()); ++ } ++ ++ void get_cpool_and_tags(Register cpool, Register tags) { ++ get_constant_pool(cpool); ++ ld(tags, cpool, ConstantPool::tags_offset_in_bytes()); ++ } ++ ++ void get_unsigned_2_byte_index_at_bcp(Register reg, int bcp_offset); ++ void get_cache_and_index_at_bcp(Register cache, Register index, int bcp_offset, size_t index_size = sizeof(u2)); ++ void get_cache_and_index_and_bytecode_at_bcp(Register cache, Register index, Register bytecode, int byte_no, int bcp_offset, size_t index_size = sizeof(u2)); ++ void get_cache_entry_pointer_at_bcp(Register cache, Register tmp, int bcp_offset, size_t index_size = sizeof(u2)); ++ void get_cache_index_at_bcp(Register index, int bcp_offset, size_t index_size = sizeof(u2)); ++ void get_method_counters(Register method, Register mcs, Label& skip); ++ ++ // load cpool->resolved_references(index); ++ void load_resolved_reference_at_index(Register result, Register index); ++ ++ void pop_ptr( Register r = FSR); ++ void pop_i( Register r = FSR); ++ void pop_l( Register r = FSR); ++ void pop_f(FloatRegister r = FSF); ++ void pop_d(FloatRegister r = FSF); ++ ++ void push_ptr( Register r = FSR); ++ void push_i( Register r = FSR); ++ void push_l( Register r = FSR); ++ void push_f(FloatRegister r = FSF); ++ void push_d(FloatRegister r = FSF); ++ ++ void pop(Register r ) { ((MacroAssembler*)this)->pop(r); } ++ ++ void push(Register r ) { ((MacroAssembler*)this)->push(r); } ++ ++ void pop(TosState state); // transition vtos -> state ++ void push(TosState state); // transition state -> vtos ++ ++ void empty_expression_stack() { ++ ld(SP, FP, frame::interpreter_frame_monitor_block_top_offset * wordSize); ++ // NULL last_sp until next java call ++ sd(R0, FP, frame::interpreter_frame_last_sp_offset * wordSize); ++ } ++ ++ // Super call_VM calls - correspond to MacroAssembler::call_VM(_leaf) calls ++ void load_ptr(int n, Register val); ++ void store_ptr(int n, Register val); ++ ++ // Generate a subtype check: branch to ok_is_subtype if sub_klass is ++ // a subtype of super_klass. ++ //void gen_subtype_check( Register sub_klass, Label &ok_is_subtype ); ++ void gen_subtype_check( Register Rsup_klass, Register sub_klass, Label &ok_is_subtype ); ++ ++ // Dispatching ++ void dispatch_prolog(TosState state, int step = 0); ++ void dispatch_epilog(TosState state, int step = 0); ++ void dispatch_only(TosState state); ++ void dispatch_only_normal(TosState state); ++ void dispatch_only_noverify(TosState state); ++ void dispatch_next(TosState state, int step = 0); ++ void dispatch_via (TosState state, address* table); ++ ++ // jump to an invoked target ++ void prepare_to_jump_from_interpreted(); ++ void jump_from_interpreted(Register method, Register temp); ++ ++ ++ // Returning from interpreted functions ++ // ++ // Removes the current activation (incl. unlocking of monitors) ++ // and sets up the return address. This code is also used for ++ // exception unwindwing. In that case, we do not want to throw ++ // IllegalMonitorStateExceptions, since that might get us into an ++ // infinite rethrow exception loop. ++ // Additionally this code is used for popFrame and earlyReturn. ++ // In popFrame case we want to skip throwing an exception, ++ // installing an exception, and notifying jvmdi. ++ // In earlyReturn case we only want to skip throwing an exception ++ // and installing an exception. ++ void remove_activation(TosState state, Register ret_addr, ++ bool throw_monitor_exception = true, ++ bool install_monitor_exception = true, ++ bool notify_jvmdi = true); ++#endif // CC_INTERP ++ ++ // Object locking ++ void lock_object (Register lock_reg); ++ void unlock_object(Register lock_reg); ++ ++#ifndef CC_INTERP ++ ++ // Interpreter profiling operations ++ void set_method_data_pointer_for_bcp(); ++ void test_method_data_pointer(Register mdp, Label& zero_continue); ++ void verify_method_data_pointer(); ++ ++ void set_mdp_data_at(Register mdp_in, int constant, Register value); ++ void increment_mdp_data_at(Address data, bool decrement = false); ++ void increment_mdp_data_at(Register mdp_in, int constant, ++ bool decrement = false); ++ void increment_mdp_data_at(Register mdp_in, Register reg, int constant, ++ bool decrement = false); ++ void increment_mask_and_jump(Address counter_addr, ++ int increment, int mask, ++ Register scratch, bool preloaded, ++ Condition cond, Label* where); ++ void set_mdp_flag_at(Register mdp_in, int flag_constant); ++ void test_mdp_data_at(Register mdp_in, int offset, Register value, ++ Register test_value_out, ++ Label& not_equal_continue); ++ ++ void record_klass_in_profile(Register receiver, Register mdp, ++ Register reg2, bool is_virtual_call); ++ void record_klass_in_profile_helper(Register receiver, Register mdp, ++ Register reg2, int start_row, ++ Label& done, bool is_virtual_call); ++ ++ void update_mdp_by_offset(Register mdp_in, int offset_of_offset); ++ void update_mdp_by_offset(Register mdp_in, Register reg, int offset_of_disp); ++ void update_mdp_by_constant(Register mdp_in, int constant); ++ void update_mdp_for_ret(Register return_bci); ++ ++ void profile_taken_branch(Register mdp, Register bumped_count); ++ void profile_not_taken_branch(Register mdp); ++ void profile_call(Register mdp); ++ void profile_final_call(Register mdp); ++ void profile_virtual_call(Register receiver, Register mdp, ++ Register scratch2, ++ bool receiver_can_be_null = false); ++ void profile_ret(Register return_bci, Register mdp); ++ void profile_null_seen(Register mdp); ++ void profile_typecheck(Register mdp, Register klass, Register scratch); ++ void profile_typecheck_failed(Register mdp); ++ void profile_switch_default(Register mdp); ++ void profile_switch_case(Register index_in_scratch, Register mdp, ++ Register scratch2); ++ ++ // Debugging ++ // only if +VerifyOops && state == atos ++ void verify_oop(Register reg, TosState state = atos); ++ // only if +VerifyFPU && (state == ftos || state == dtos) ++ void verify_FPU(int stack_depth, TosState state = ftos); ++ ++ void profile_obj_type(Register obj, const Address& mdo_addr); ++ void profile_arguments_type(Register mdp, Register callee, Register tmp, bool is_virtual); ++ void profile_return_type(Register mdp, Register ret, Register tmp); ++ void profile_parameters_type(Register mdp, Register tmp1, Register tmp2); ++#endif // !CC_INTERP ++ ++ typedef enum { NotifyJVMTI, SkipNotifyJVMTI } NotifyMethodExitMode; ++ ++ // support for jvmti/dtrace ++ void notify_method_entry(); ++ void notify_method_exit(TosState state, NotifyMethodExitMode mode); ++}; ++ ++#endif // CPU_MIPS_VM_INTERP_MASM_MIPS_64_HPP +diff --git a/hotspot/src/cpu/mips/vm/interpreterGenerator_mips.hpp b/hotspot/src/cpu/mips/vm/interpreterGenerator_mips.hpp +new file mode 100644 +index 0000000000..26fced492a +--- /dev/null ++++ b/hotspot/src/cpu/mips/vm/interpreterGenerator_mips.hpp +@@ -0,0 +1,49 @@ ++/* ++ * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2015, 2016, Loongson Technology. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ * ++ */ ++ ++#ifndef CPU_MIPS_VM_INTERPRETERGENERATOR_MIPS_HPP ++#define CPU_MIPS_VM_INTERPRETERGENERATOR_MIPS_HPP ++ ++ ++// Generation of Interpreter ++// ++ friend class AbstractInterpreterGenerator; ++ ++ private: ++ ++ address generate_normal_entry(bool synchronized); ++ address generate_native_entry(bool synchronized); ++ address generate_abstract_entry(void); ++ address generate_math_entry(AbstractInterpreter::MethodKind kind); ++ address generate_empty_entry(void); ++ address generate_accessor_entry(void); ++ address generate_Reference_get_entry(); ++ void lock_method(void); ++ void generate_stack_overflow_check(void); ++ ++ void generate_counter_incr(Label* overflow, Label* profile_method, Label* profile_method_continue); ++ void generate_counter_overflow(Label* do_continue); ++ ++#endif // CPU_MIPS_VM_INTERPRETERGENERATOR_MIPS_HPP +diff --git a/hotspot/src/cpu/mips/vm/interpreterRT_mips.hpp b/hotspot/src/cpu/mips/vm/interpreterRT_mips.hpp +new file mode 100644 +index 0000000000..8dec2007c6 +--- /dev/null ++++ b/hotspot/src/cpu/mips/vm/interpreterRT_mips.hpp +@@ -0,0 +1,61 @@ ++/* ++ * Copyright (c) 1998, 2010, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2015, 2022, Loongson Technology. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ * ++ */ ++ ++#ifndef CPU_MIPS_VM_INTERPRETERRT_MIPS_HPP ++#define CPU_MIPS_VM_INTERPRETERRT_MIPS_HPP ++ ++#include "memory/allocation.hpp" ++ ++// native method calls ++ ++class SignatureHandlerGenerator: public NativeSignatureIterator { ++ private: ++ MacroAssembler* _masm; ++ ++ void move(int from_offset, int to_offset); ++ ++ void box(int from_offset, int to_offset); ++ void pass_int(); ++ void pass_long(); ++ void pass_object(); ++ void pass_float(); ++ void pass_double(); ++ ++ public: ++ // Creation ++ SignatureHandlerGenerator(methodHandle method, CodeBuffer* buffer) : NativeSignatureIterator(method) { ++ _masm = new MacroAssembler(buffer); ++ } ++ ++ // Code generation ++ void generate(uint64_t fingerprint); ++ ++ // Code generation support ++ static Register from(); ++ static Register to(); ++ static Register temp(); ++}; ++ ++#endif // CPU_MIPS_VM_INTERPRETERRT_MIPS_HPP +diff --git a/hotspot/src/cpu/mips/vm/interpreterRT_mips_64.cpp b/hotspot/src/cpu/mips/vm/interpreterRT_mips_64.cpp +new file mode 100644 +index 0000000000..14b7e39af7 +--- /dev/null ++++ b/hotspot/src/cpu/mips/vm/interpreterRT_mips_64.cpp +@@ -0,0 +1,259 @@ ++/* ++ * Copyright (c) 2003, 2012, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2015, 2022, Loongson Technology. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ * ++ */ ++ ++#include "precompiled.hpp" ++#include "interpreter/interpreter.hpp" ++#include "interpreter/interpreterRuntime.hpp" ++#include "memory/allocation.inline.hpp" ++#include "memory/universe.inline.hpp" ++#include "oops/method.hpp" ++#include "oops/oop.inline.hpp" ++#include "runtime/handles.inline.hpp" ++#include "runtime/icache.hpp" ++#include "runtime/interfaceSupport.hpp" ++#include "runtime/signature.hpp" ++ ++#define __ _masm-> ++ ++#define A0 RA0 ++#define A1 RA1 ++#define A2 RA2 ++#define A3 RA3 ++#define A4 RA4 ++#define A5 RA5 ++#define A6 RA6 ++#define A7 RA7 ++#define T0 RT0 ++#define T1 RT1 ++#define T2 RT2 ++#define T3 RT3 ++#define T8 RT8 ++#define T9 RT9 ++ ++// Implementation of SignatureHandlerGenerator ++ ++void InterpreterRuntime::SignatureHandlerGenerator::move(int from_offset, int to_offset) { ++ __ ld(temp(), from(), Interpreter::local_offset_in_bytes(from_offset)); ++ __ sd(temp(), to(), to_offset * longSize); ++} ++ ++void InterpreterRuntime::SignatureHandlerGenerator::box(int from_offset, int to_offset) { ++ __ addiu(temp(), from(),Interpreter::local_offset_in_bytes(from_offset) ); ++ __ lw(AT, from(), Interpreter::local_offset_in_bytes(from_offset) ); ++ ++ Label L; ++ __ bne(AT, R0, L); ++ __ delayed()->nop(); ++ __ move(temp(), R0); ++ __ bind(L); ++ __ sw(temp(), to(), to_offset * wordSize); ++} ++ ++void InterpreterRuntime::SignatureHandlerGenerator::generate(uint64_t fingerprint) { ++ // generate code to handle arguments ++ iterate(fingerprint); ++ // return result handler ++ __ li(V0, AbstractInterpreter::result_handler(method()->result_type())); ++ // return ++ __ jr(RA); ++ __ delayed()->nop(); ++ ++ __ flush(); ++} ++ ++void InterpreterRuntime::SignatureHandlerGenerator::pass_int() { ++ Argument jni_arg(jni_offset()); ++ if(jni_arg.is_Register()) { ++ __ lw(jni_arg.as_Register(), from(), Interpreter::local_offset_in_bytes(offset())); ++ } else { ++ __ lw(temp(), from(), Interpreter::local_offset_in_bytes(offset())); ++ __ sw(temp(), jni_arg.as_caller_address()); ++ } ++} ++ ++// the jvm specifies that long type takes 2 stack spaces, so in do_long(), _offset += 2. ++void InterpreterRuntime::SignatureHandlerGenerator::pass_long() { ++ Argument jni_arg(jni_offset()); ++ if(jni_arg.is_Register()) { ++ __ ld(jni_arg.as_Register(), from(), Interpreter::local_offset_in_bytes(offset() + 1)); ++ } else { ++ __ ld(temp(), from(), Interpreter::local_offset_in_bytes(offset() + 1)); ++ __ sd(temp(), jni_arg.as_caller_address()); ++ } ++} ++ ++void InterpreterRuntime::SignatureHandlerGenerator::pass_object() { ++ Argument jni_arg(jni_offset()); ++ ++ // the handle for a receiver will never be null ++ bool do_NULL_check = offset() != 0 || is_static(); ++ if (do_NULL_check) { ++ __ ld(AT, from(), Interpreter::local_offset_in_bytes(offset())); ++ __ daddiu((jni_arg.is_Register() ? jni_arg.as_Register() : temp()), from(), Interpreter::local_offset_in_bytes(offset())); ++ __ movz((jni_arg.is_Register() ? jni_arg.as_Register() : temp()), R0, AT); ++ } else { ++ __ daddiu(jni_arg.as_Register(), from(), Interpreter::local_offset_in_bytes(offset())); ++ } ++ ++ if (!jni_arg.is_Register()) ++ __ sd(temp(), jni_arg.as_caller_address()); ++} ++ ++void InterpreterRuntime::SignatureHandlerGenerator::pass_float() { ++ Argument jni_arg(jni_offset()); ++ if(jni_arg.is_Register()) { ++ __ lwc1(jni_arg.as_FloatRegister(), from(), Interpreter::local_offset_in_bytes(offset())); ++ } else { ++ __ lw(temp(), from(), Interpreter::local_offset_in_bytes(offset())); ++ __ sw(temp(), jni_arg.as_caller_address()); ++ } ++} ++ ++// the jvm specifies that double type takes 2 stack spaces, so in do_double(), _offset += 2. ++void InterpreterRuntime::SignatureHandlerGenerator::pass_double() { ++ Argument jni_arg(jni_offset()); ++ if(jni_arg.is_Register()) { ++ __ ldc1(jni_arg.as_FloatRegister(), from(), Interpreter::local_offset_in_bytes(offset() + 1)); ++ } else { ++ __ ld(temp(), from(), Interpreter::local_offset_in_bytes(offset() + 1)); ++ __ sd(temp(), jni_arg.as_caller_address()); ++ } ++} ++ ++ ++Register InterpreterRuntime::SignatureHandlerGenerator::from() { return LVP; } ++Register InterpreterRuntime::SignatureHandlerGenerator::to() { return SP; } ++Register InterpreterRuntime::SignatureHandlerGenerator::temp() { return T8; } ++ ++// Implementation of SignatureHandlerLibrary ++ ++void SignatureHandlerLibrary::pd_set_handler(address handler) {} ++ ++ ++class SlowSignatureHandler ++ : public NativeSignatureIterator { ++ private: ++ address _from; ++ intptr_t* _to; ++ intptr_t* _reg_args; ++ intptr_t* _fp_identifiers; ++ unsigned int _num_args; ++ ++ virtual void pass_int() ++ { ++ jint from_obj = *(jint *)(_from+Interpreter::local_offset_in_bytes(0)); ++ _from -= Interpreter::stackElementSize; ++ ++ if (_num_args < Argument::n_register_parameters) { ++ *_reg_args++ = from_obj; ++ _num_args++; ++ } else { ++ *_to++ = from_obj; ++ } ++ } ++ ++ virtual void pass_long() ++ { ++ intptr_t from_obj = *(intptr_t*)(_from+Interpreter::local_offset_in_bytes(1)); ++ _from -= 2 * Interpreter::stackElementSize; ++ ++ if (_num_args < Argument::n_register_parameters) { ++ *_reg_args++ = from_obj; ++ _num_args++; ++ } else { ++ *_to++ = from_obj; ++ } ++ } ++ ++ virtual void pass_object() ++ { ++ intptr_t *from_addr = (intptr_t*)(_from + Interpreter::local_offset_in_bytes(0)); ++ _from -= Interpreter::stackElementSize; ++ if (_num_args < Argument::n_register_parameters) { ++ *_reg_args++ = (*from_addr == 0) ? NULL : (intptr_t) from_addr; ++ _num_args++; ++ } else { ++ *_to++ = (*from_addr == 0) ? NULL : (intptr_t) from_addr; ++ } ++ } ++ ++ virtual void pass_float() ++ { ++ jint from_obj = *(jint *)(_from+Interpreter::local_offset_in_bytes(0)); ++ _from -= Interpreter::stackElementSize; ++ ++ if (_num_args < Argument::n_float_register_parameters) { ++ *_reg_args++ = from_obj; ++ *_fp_identifiers |= (0x01 << (_num_args*2)); // mark as float ++ _num_args++; ++ } else { ++ *_to++ = from_obj; ++ } ++ } ++ ++ virtual void pass_double() ++ { ++ intptr_t from_obj = *(intptr_t*)(_from+Interpreter::local_offset_in_bytes(1)); ++ _from -= 2*Interpreter::stackElementSize; ++ ++ if (_num_args < Argument::n_float_register_parameters) { ++ *_reg_args++ = from_obj; ++ *_fp_identifiers |= (0x3 << (_num_args*2)); // mark as double ++ _num_args++; ++ } else { ++ *_to++ = from_obj; ++ } ++ } ++ ++ public: ++ SlowSignatureHandler(methodHandle method, address from, intptr_t* to) ++ : NativeSignatureIterator(method) ++ { ++ _from = from; ++ _to = to; ++ ++ // see TemplateInterpreterGenerator::generate_slow_signature_handler() ++ _reg_args = to - Argument::n_register_parameters + jni_offset() - 1; ++ _fp_identifiers = to - 1; ++ *(int*) _fp_identifiers = 0; ++ _num_args = jni_offset(); ++ } ++}; ++ ++ ++IRT_ENTRY(address, ++ InterpreterRuntime::slow_signature_handler(JavaThread* thread, ++ Method* method, ++ intptr_t* from, ++ intptr_t* to)) ++ methodHandle m(thread, (Method*)method); ++ assert(m->is_native(), "sanity check"); ++ ++ // handle arguments ++ SlowSignatureHandler(m, (address)from, to).iterate(UCONST64(-1)); ++ ++ // return result handler ++ return Interpreter::result_handler(m->result_type()); ++IRT_END +diff --git a/hotspot/src/cpu/mips/vm/interpreter_mips.hpp b/hotspot/src/cpu/mips/vm/interpreter_mips.hpp +new file mode 100644 +index 0000000000..9a21d704fa +--- /dev/null ++++ b/hotspot/src/cpu/mips/vm/interpreter_mips.hpp +@@ -0,0 +1,50 @@ ++/* ++ * Copyright (c) 1997, 2011, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2015, 2022, Loongson Technology. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ * ++ */ ++ ++#ifndef CPU_MIPS_VM_INTERPRETER_MIPS_HPP ++#define CPU_MIPS_VM_INTERPRETER_MIPS_HPP ++ ++ public: ++ ++ // Sentinel placed in the code for interpreter returns so ++ // that i2c adapters and osr code can recognize an interpreter ++ // return address and convert the return to a specialized ++ // block of code to handle compiedl return values and cleaning ++ // the fpu stack. ++ static const int return_sentinel; ++ ++ static Address::ScaleFactor stackElementScale() { ++ return Address::times_8; ++ } ++ ++ // Offset from sp (which points to the last stack element) ++ static int expr_offset_in_bytes(int i) { return stackElementSize * i; } ++ // Size of interpreter code. Increase if too small. Interpreter will ++ // fail with a guarantee ("not enough space for interpreter generation"); ++ // if too small. ++ // Run with +PrintInterpreterSize to get the VM to print out the size. ++ // Max size with JVMTI and TaggedStackInterpreter ++ const static int InterpreterCodeSize = 168 * 1024; ++#endif // CPU_MIPS_VM_INTERPRETER_MIPS_HPP +diff --git a/hotspot/src/cpu/mips/vm/interpreter_mips_64.cpp b/hotspot/src/cpu/mips/vm/interpreter_mips_64.cpp +new file mode 100644 +index 0000000000..014c812713 +--- /dev/null ++++ b/hotspot/src/cpu/mips/vm/interpreter_mips_64.cpp +@@ -0,0 +1,286 @@ ++/* ++ * Copyright (c) 2003, 2014, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2015, 2022, Loongson Technology. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ * ++ */ ++ ++#include "precompiled.hpp" ++#include "asm/macroAssembler.hpp" ++#include "interpreter/bytecodeHistogram.hpp" ++#include "interpreter/interpreter.hpp" ++#include "interpreter/interpreterGenerator.hpp" ++#include "interpreter/interpreterRuntime.hpp" ++#include "interpreter/templateTable.hpp" ++#include "oops/arrayOop.hpp" ++#include "oops/methodData.hpp" ++#include "oops/method.hpp" ++#include "oops/oop.inline.hpp" ++#include "prims/jvmtiExport.hpp" ++#include "prims/jvmtiThreadState.hpp" ++#include "prims/methodHandles.hpp" ++#include "runtime/arguments.hpp" ++#include "runtime/deoptimization.hpp" ++#include "runtime/frame.inline.hpp" ++#include "runtime/sharedRuntime.hpp" ++#include "runtime/stubRoutines.hpp" ++#include "runtime/synchronizer.hpp" ++#include "runtime/timer.hpp" ++#include "runtime/vframeArray.hpp" ++#include "utilities/debug.hpp" ++ ++#define __ _masm-> ++ ++#define A0 RA0 ++#define A1 RA1 ++#define A2 RA2 ++#define A3 RA3 ++#define A4 RA4 ++#define A5 RA5 ++#define A6 RA6 ++#define A7 RA7 ++#define T0 RT0 ++#define T1 RT1 ++#define T2 RT2 ++#define T3 RT3 ++#define T8 RT8 ++#define T9 RT9 ++ ++ ++address AbstractInterpreterGenerator::generate_slow_signature_handler() { ++ address entry = __ pc(); ++ ++ // Rmethod: method ++ // LVP: pointer to locals ++ // A3: first stack arg ++ __ move(A3, SP); ++ __ daddiu(SP, SP, -10 * wordSize); ++ __ sd(RA, SP, 0); ++ __ call_VM(noreg, ++ CAST_FROM_FN_PTR(address, ++ InterpreterRuntime::slow_signature_handler), ++ Rmethod, LVP, A3); ++ ++ // V0: result handler ++ ++ // Stack layout: ++ // ... ++ // 10 stack arg0 <--- old sp ++ // 9 float/double identifiers ++ // 8 register arg7 ++ // ... ++ // 2 register arg1 ++ // 1 aligned slot ++ // SP: 0 return address ++ ++ // Do FP first so we can use T3 as temp ++ __ ld(T3, Address(SP, 9 * wordSize)); // float/double identifiers ++ ++ // A0 is for env. ++ // If the mothed is not static, A1 will be corrected in generate_native_entry. ++ for ( int i = 1; i < Argument::n_register_parameters; i++ ) { ++ Register reg = as_Register(i + A0->encoding()); ++ FloatRegister floatreg = as_FloatRegister(i + F12->encoding()); ++ Label isfloatordouble, isdouble, next; ++ ++ __ andi(AT, T3, 1 << (i*2)); // Float or Double? ++ __ bne(AT, R0, isfloatordouble); ++ __ delayed()->nop(); ++ ++ // Do Int register here ++ __ ld(reg, SP, (1 + i) * wordSize); ++ __ b (next); ++ __ delayed()->nop(); ++ ++ __ bind(isfloatordouble); ++ __ andi(AT, T3, 1 << ((i*2)+1)); // Double? ++ __ bne(AT, R0, isdouble); ++ __ delayed()->nop(); ++ ++ // Do Float Here ++ __ lwc1(floatreg, SP, (1 + i) * wordSize); ++ __ b(next); ++ __ delayed()->nop(); ++ ++ // Do Double here ++ __ bind(isdouble); ++ __ ldc1(floatreg, SP, (1 + i) * wordSize); ++ ++ __ bind(next); ++ } ++ ++ __ ld(RA, SP, 0); ++ __ daddiu(SP, SP, 10 * wordSize); ++ __ jr(RA); ++ __ delayed()->nop(); ++ return entry; ++} ++ ++ ++// ++// Various method entries ++// ++ ++address InterpreterGenerator::generate_math_entry(AbstractInterpreter::MethodKind kind) { ++ ++ // Rmethod: methodOop ++ // V0: scratrch ++ // Rsender: send 's sp ++ ++ if (!InlineIntrinsics) return NULL; // Generate a vanilla entry ++ ++ address entry_point = __ pc(); ++ ++ // These don't need a safepoint check because they aren't virtually ++ // callable. We won't enter these intrinsics from compiled code. ++ // If in the future we added an intrinsic which was virtually callable ++ // we'd have to worry about how to safepoint so that this code is used. ++ ++ // mathematical functions inlined by compiler ++ // (interpreter must provide identical implementation ++ // in order to avoid monotonicity bugs when switching ++ // from interpreter to compiler in the middle of some ++ // computation) ++ // ++ // stack: [ lo(arg) ] <-- sp ++ // [ hi(arg) ] ++ { ++ // Note: For JDK 1.3 StrictMath exists and Math.sin/cos/sqrt are ++ // java methods. Interpreter::method_kind(...) will select ++ // this entry point for the corresponding methods in JDK 1.3. ++ __ ldc1(F12, SP, 0 * wordSize); ++ __ ldc1(F13, SP, 1 * wordSize); ++ __ push2(RA, FP); ++ __ daddiu(FP, SP, 2 * wordSize); ++ ++ // [ fp ] <-- sp ++ // [ ra ] ++ // [ lo ] <-- fp ++ // [ hi ] ++ //FIXME, need consider this ++ switch (kind) { ++ case Interpreter::java_lang_math_sin : ++ __ trigfunc('s'); ++ break; ++ case Interpreter::java_lang_math_cos : ++ __ trigfunc('c'); ++ break; ++ case Interpreter::java_lang_math_tan : ++ __ trigfunc('t'); ++ break; ++ case Interpreter::java_lang_math_sqrt: ++ __ sqrt_d(F0, F12); ++ break; ++ case Interpreter::java_lang_math_abs: ++ __ abs_d(F0, F12); ++ break; ++ case Interpreter::java_lang_math_log: ++ // Store to stack to convert 80bit precision back to 64bits ++ break; ++ case Interpreter::java_lang_math_log10: ++ // Store to stack to convert 80bit precision back to 64bits ++ break; ++ case Interpreter::java_lang_math_pow: ++ break; ++ case Interpreter::java_lang_math_exp: ++ break; ++ ++ default : ++ ShouldNotReachHere(); ++ } ++ ++ // must maintain return value in F0:F1 ++ __ ld(RA, FP, (-1) * wordSize); ++ //FIXME ++ __ ld(FP, FP, (-2) * wordSize); ++ __ move(SP, Rsender); ++ __ jr(RA); ++ __ delayed()->nop(); ++ } ++ return entry_point; ++} ++ ++ ++// Abstract method entry ++// Attempt to execute abstract method. Throw exception ++address InterpreterGenerator::generate_abstract_entry(void) { ++ ++ // Rmethod: methodOop ++ // V0: receiver (unused) ++ // Rsender : sender 's sp ++ address entry_point = __ pc(); ++ ++ // abstract method entry ++ // throw exception ++ // adjust stack to what a normal return would do ++ __ empty_expression_stack(); ++ __ restore_bcp(); ++ __ restore_locals(); ++ __ call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::throw_AbstractMethodError)); ++ // the call_VM checks for exception, so we should never return here. ++ __ should_not_reach_here(); ++ ++ return entry_point; ++} ++ ++ ++// Empty method, generate a very fast return. ++ ++address InterpreterGenerator::generate_empty_entry(void) { ++ ++ // Rmethod: methodOop ++ // V0: receiver (unused) ++ // Rsender: sender 's sp , must set sp to this value on return , on mips ,now use T0,as it right? ++ if (!UseFastEmptyMethods) return NULL; ++ ++ address entry_point = __ pc(); ++ ++ Label slow_path; ++ __ li(RT0, SafepointSynchronize::address_of_state()); ++ __ lw(AT, RT0, 0); ++ __ move(RT0, (SafepointSynchronize::_not_synchronized)); ++ __ bne(AT, RT0,slow_path); ++ __ delayed()->nop(); ++ __ move(SP, Rsender); ++ __ jr(RA); ++ __ delayed()->nop(); ++ __ bind(slow_path); ++ (void) generate_normal_entry(false); ++ ++ return entry_point; ++ ++} ++ ++void Deoptimization::unwind_callee_save_values(frame* f, vframeArray* vframe_array) { ++ ++ // This code is sort of the equivalent of C2IAdapter::setup_stack_frame back in ++ // the days we had adapter frames. When we deoptimize a situation where a ++ // compiled caller calls a compiled caller will have registers it expects ++ // to survive the call to the callee. If we deoptimize the callee the only ++ // way we can restore these registers is to have the oldest interpreter ++ // frame that we create restore these values. That is what this routine ++ // will accomplish. ++ ++ // At the moment we have modified c2 to not have any callee save registers ++ // so this problem does not exist and this routine is just a place holder. ++ ++ assert(f->is_interpreted_frame(), "must be interpreted"); ++} +diff --git a/hotspot/src/cpu/mips/vm/javaFrameAnchor_mips.hpp b/hotspot/src/cpu/mips/vm/javaFrameAnchor_mips.hpp +new file mode 100644 +index 0000000000..dccdf6a019 +--- /dev/null ++++ b/hotspot/src/cpu/mips/vm/javaFrameAnchor_mips.hpp +@@ -0,0 +1,87 @@ ++/* ++ * Copyright (c) 2002, 2010, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2015, 2016, Loongson Technology. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ * ++ */ ++ ++#ifndef CPU_MIPS_VM_JAVAFRAMEANCHOR_MIPS_HPP ++#define CPU_MIPS_VM_JAVAFRAMEANCHOR_MIPS_HPP ++ ++private: ++ ++ // FP value associated with _last_Java_sp: ++ intptr_t* volatile _last_Java_fp; // pointer is volatile not what it points to ++ ++public: ++ // Each arch must define reset, save, restore ++ // These are used by objects that only care about: ++ // 1 - initializing a new state (thread creation, javaCalls) ++ // 2 - saving a current state (javaCalls) ++ // 3 - restoring an old state (javaCalls) ++ ++ void clear(void) { ++ // clearing _last_Java_sp must be first ++ _last_Java_sp = NULL; ++ // fence? ++ _last_Java_fp = NULL; ++ _last_Java_pc = NULL; ++ } ++ ++ void copy(JavaFrameAnchor* src) { ++ // In order to make sure the transition state is valid for "this" ++ // We must clear _last_Java_sp before copying the rest of the new data ++ // ++ // Hack Alert: Temporary bugfix for 4717480/4721647 ++ // To act like previous version (pd_cache_state) don't NULL _last_Java_sp ++ // unless the value is changing ++ // ++ if (_last_Java_sp != src->_last_Java_sp) ++ _last_Java_sp = NULL; ++ ++ _last_Java_fp = src->_last_Java_fp; ++ _last_Java_pc = src->_last_Java_pc; ++ // Must be last so profiler will always see valid frame if has_last_frame() is true ++ _last_Java_sp = src->_last_Java_sp; ++ } ++ ++ // Always walkable ++ bool walkable(void) { return true; } ++ // Never any thing to do since we are always walkable and can find address of return addresses ++ void make_walkable(JavaThread* thread) { } ++ ++ intptr_t* last_Java_sp(void) const { return _last_Java_sp; } ++ ++ address last_Java_pc(void) { return _last_Java_pc; } ++ ++private: ++ ++ static ByteSize last_Java_fp_offset() { return byte_offset_of(JavaFrameAnchor, _last_Java_fp); } ++ ++public: ++ ++ void set_last_Java_sp(intptr_t* sp) { _last_Java_sp = sp; } ++ ++ intptr_t* last_Java_fp(void) { return _last_Java_fp; } ++ // Assert (last_Java_sp == NULL || fp == NULL) ++ void set_last_Java_fp(intptr_t* fp) { _last_Java_fp = fp; } ++ ++#endif // CPU_MIPS_VM_JAVAFRAMEANCHOR_MIPS_HPP +diff --git a/hotspot/src/cpu/mips/vm/jniFastGetField_mips_64.cpp b/hotspot/src/cpu/mips/vm/jniFastGetField_mips_64.cpp +new file mode 100644 +index 0000000000..da94d0318f +--- /dev/null ++++ b/hotspot/src/cpu/mips/vm/jniFastGetField_mips_64.cpp +@@ -0,0 +1,171 @@ ++/* ++ * Copyright (c) 2004, 2010, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2015, 2021, Loongson Technology. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ * ++ */ ++ ++#include "precompiled.hpp" ++#include "asm/macroAssembler.hpp" ++#include "memory/resourceArea.hpp" ++#include "prims/jniFastGetField.hpp" ++#include "prims/jvm_misc.hpp" ++#include "runtime/safepoint.hpp" ++ ++#define __ masm-> ++ ++#define A0 RA0 ++#define A1 RA1 ++#define A2 RA2 ++#define A3 RA3 ++#define A4 RA4 ++#define A5 RA5 ++#define A6 RA6 ++#define A7 RA7 ++#define T0 RT0 ++#define T1 RT1 ++#define T2 RT2 ++#define T3 RT3 ++#define T8 RT8 ++#define T9 RT9 ++ ++#define BUFFER_SIZE 30*wordSize ++ ++// Instead of issuing lfence for LoadLoad barrier, we create data dependency ++// between loads, which is more efficient than lfence. ++ ++address JNI_FastGetField::generate_fast_get_int_field0(BasicType type) { ++ const char *name = NULL; ++ switch (type) { ++ case T_BOOLEAN: name = "jni_fast_GetBooleanField"; break; ++ case T_BYTE: name = "jni_fast_GetByteField"; break; ++ case T_CHAR: name = "jni_fast_GetCharField"; break; ++ case T_SHORT: name = "jni_fast_GetShortField"; break; ++ case T_INT: name = "jni_fast_GetIntField"; break; ++ case T_LONG: name = "jni_fast_GetLongField"; break; ++ case T_FLOAT: name = "jni_fast_GetFloatField"; break; ++ case T_DOUBLE: name = "jni_fast_GetDoubleField"; break; ++ default: ShouldNotReachHere(); ++ } ++ ResourceMark rm; ++ BufferBlob* blob = BufferBlob::create(name, BUFFER_SIZE); ++ CodeBuffer cbuf(blob); ++ MacroAssembler* masm = new MacroAssembler(&cbuf); ++ address fast_entry = __ pc(); ++ ++ Label slow; ++ ++ // return pc RA ++ // jni env A0 ++ // obj A1 ++ // jfieldID A2 ++ ++ address counter_addr = SafepointSynchronize::safepoint_counter_addr(); ++ __ set64(AT, (long)counter_addr); ++ __ lw(T1, AT, 0); ++ ++ // Parameters(A0~A3) should not be modified, since they will be used in slow path ++ __ andi(AT, T1, 1); ++ __ bne(AT, R0, slow); ++ __ delayed()->nop(); ++ ++ __ move(T0, A1); ++ __ clear_jweak_tag(T0); ++ ++ __ ld(T0, T0, 0); // unbox, *obj ++ __ dsrl(T2, A2, 2); // offset ++ __ daddu(T0, T0, T2); ++ ++ assert(count < LIST_CAPACITY, "LIST_CAPACITY too small"); ++ speculative_load_pclist[count] = __ pc(); ++ switch (type) { ++ case T_BOOLEAN: __ lbu (V0, T0, 0); break; ++ case T_BYTE: __ lb (V0, T0, 0); break; ++ case T_CHAR: __ lhu (V0, T0, 0); break; ++ case T_SHORT: __ lh (V0, T0, 0); break; ++ case T_INT: __ lw (V0, T0, 0); break; ++ case T_LONG: __ ld (V0, T0, 0); break; ++ case T_FLOAT: __ lwc1(F0, T0, 0); break; ++ case T_DOUBLE: __ ldc1(F0, T0, 0); break; ++ default: ShouldNotReachHere(); ++ } ++ ++ __ set64(AT, (long)counter_addr); ++ __ lw(AT, AT, 0); ++ __ bne(T1, AT, slow); ++ __ delayed()->nop(); ++ ++ __ jr(RA); ++ __ delayed()->nop(); ++ ++ slowcase_entry_pclist[count++] = __ pc(); ++ __ bind (slow); ++ address slow_case_addr = NULL; ++ switch (type) { ++ case T_BOOLEAN: slow_case_addr = jni_GetBooleanField_addr(); break; ++ case T_BYTE: slow_case_addr = jni_GetByteField_addr(); break; ++ case T_CHAR: slow_case_addr = jni_GetCharField_addr(); break; ++ case T_SHORT: slow_case_addr = jni_GetShortField_addr(); break; ++ case T_INT: slow_case_addr = jni_GetIntField_addr(); break; ++ case T_LONG: slow_case_addr = jni_GetLongField_addr(); break; ++ case T_FLOAT: slow_case_addr = jni_GetFloatField_addr(); break; ++ case T_DOUBLE: slow_case_addr = jni_GetDoubleField_addr(); break; ++ default: ShouldNotReachHere(); ++ } ++ __ jmp(slow_case_addr); ++ __ delayed()->nop(); ++ ++ __ flush (); ++ ++ return fast_entry; ++} ++ ++address JNI_FastGetField::generate_fast_get_boolean_field() { ++ return generate_fast_get_int_field0(T_BOOLEAN); ++} ++ ++address JNI_FastGetField::generate_fast_get_byte_field() { ++ return generate_fast_get_int_field0(T_BYTE); ++} ++ ++address JNI_FastGetField::generate_fast_get_char_field() { ++ return generate_fast_get_int_field0(T_CHAR); ++} ++ ++address JNI_FastGetField::generate_fast_get_short_field() { ++ return generate_fast_get_int_field0(T_SHORT); ++} ++ ++address JNI_FastGetField::generate_fast_get_int_field() { ++ return generate_fast_get_int_field0(T_INT); ++} ++ ++address JNI_FastGetField::generate_fast_get_long_field() { ++ return generate_fast_get_int_field0(T_LONG); ++} ++ ++address JNI_FastGetField::generate_fast_get_float_field() { ++ return generate_fast_get_int_field0(T_FLOAT); ++} ++ ++address JNI_FastGetField::generate_fast_get_double_field() { ++ return generate_fast_get_int_field0(T_DOUBLE); ++} +diff --git a/hotspot/src/cpu/mips/vm/jniTypes_mips.hpp b/hotspot/src/cpu/mips/vm/jniTypes_mips.hpp +new file mode 100644 +index 0000000000..dfcd47b478 +--- /dev/null ++++ b/hotspot/src/cpu/mips/vm/jniTypes_mips.hpp +@@ -0,0 +1,144 @@ ++/* ++ * Copyright (c) 1998, 2010, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2015, 2022, Loongson Technology. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ * ++ */ ++ ++#ifndef CPU_MIPS_VM_JNITYPES_MIPS_HPP ++#define CPU_MIPS_VM_JNITYPES_MIPS_HPP ++ ++#include "memory/allocation.hpp" ++#include "oops/oop.hpp" ++#include "prims/jni.h" ++ ++// This file holds platform-dependent routines used to write primitive jni ++// types to the array of arguments passed into JavaCalls::call ++ ++class JNITypes : AllStatic { ++ // These functions write a java primitive type (in native format) ++ // to a java stack slot array to be passed as an argument to JavaCalls:calls. ++ // I.e., they are functionally 'push' operations if they have a 'pos' ++ // formal parameter. Note that jlong's and jdouble's are written ++ // _in reverse_ of the order in which they appear in the interpreter ++ // stack. This is because call stubs (see stubGenerator_sparc.cpp) ++ // reverse the argument list constructed by JavaCallArguments (see ++ // javaCalls.hpp). ++ ++private: ++ ++ // 32bit Helper routines. ++ static inline void put_int2r(jint *from, intptr_t *to) { *(jint *)(to++) = from[1]; ++ *(jint *)(to ) = from[0]; } ++ static inline void put_int2r(jint *from, intptr_t *to, int& pos) { put_int2r(from, to + pos); pos += 2; } ++ ++public: ++ // In MIPS64, the sizeof intptr_t is 8 bytes, and each unit in JavaCallArguments::_value_buffer[] ++ // is 8 bytes. ++ // If we only write the low 4 bytes with (jint *), the high 4-bits will be left with uncertain values. ++ // Then, in JavaCallArguments::parameters(), the whole 8 bytes of a T_INT parameter is loaded. ++ // This error occurs in ReflectInvoke.java ++ // The parameter of DD(int) should be 4 instead of 0x550000004. ++ // ++ // See: [runtime/javaCalls.hpp] ++ ++ static inline void put_int(jint from, intptr_t *to) { *(intptr_t *)(to + 0 ) = from; } ++ static inline void put_int(jint from, intptr_t *to, int& pos) { *(intptr_t *)(to + pos++) = from; } ++ static inline void put_int(jint *from, intptr_t *to, int& pos) { *(intptr_t *)(to + pos++) = *from; } ++ ++ // Longs are stored in native format in one JavaCallArgument slot at ++ // *(to). ++ // In theory, *(to + 1) is an empty slot. But, for several Java2D testing programs (TestBorderLayout, SwingTest), ++ // *(to + 1) must contains a copy of the long value. Otherwise it will corrupts. ++ static inline void put_long(jlong from, intptr_t *to) { ++ *(jlong*) (to + 1) = from; ++ *(jlong*) (to) = from; ++ } ++ ++ // A long parameter occupies two slot. ++ // It must fit the layout rule in methodHandle. ++ // ++ // See: [runtime/reflection.cpp] Reflection::invoke() ++ // assert(java_args.size_of_parameters() == method->size_of_parameters(), "just checking"); ++ ++ static inline void put_long(jlong from, intptr_t *to, int& pos) { ++ *(jlong*) (to + 1 + pos) = from; ++ *(jlong*) (to + pos) = from; ++ pos += 2; ++ } ++ ++ static inline void put_long(jlong *from, intptr_t *to, int& pos) { ++ *(jlong*) (to + 1 + pos) = *from; ++ *(jlong*) (to + pos) = *from; ++ pos += 2; ++ } ++ ++ // Oops are stored in native format in one JavaCallArgument slot at *to. ++ static inline void put_obj(oop from, intptr_t *to) { *(oop *)(to + 0 ) = from; } ++ static inline void put_obj(oop from, intptr_t *to, int& pos) { *(oop *)(to + pos++) = from; } ++ static inline void put_obj(oop *from, intptr_t *to, int& pos) { *(oop *)(to + pos++) = *from; } ++ ++ // Floats are stored in native format in one JavaCallArgument slot at *to. ++ static inline void put_float(jfloat from, intptr_t *to) { *(jfloat *)(to + 0 ) = from; } ++ static inline void put_float(jfloat from, intptr_t *to, int& pos) { *(jfloat *)(to + pos++) = from; } ++ static inline void put_float(jfloat *from, intptr_t *to, int& pos) { *(jfloat *)(to + pos++) = *from; } ++ ++#undef _JNI_SLOT_OFFSET ++#define _JNI_SLOT_OFFSET 0 ++ ++ // Longs are stored in native format in one JavaCallArgument slot at ++ // *(to). ++ // In theory, *(to + 1) is an empty slot. But, for several Java2D testing programs (TestBorderLayout, SwingTest), ++ // *(to + 1) must contains a copy of the long value. Otherwise it will corrupts. ++ static inline void put_double(jdouble from, intptr_t *to) { ++ *(jdouble*) (to + 1) = from; ++ *(jdouble*) (to) = from; ++ } ++ ++ // A long parameter occupies two slot. ++ // It must fit the layout rule in methodHandle. ++ // ++ // See: [runtime/reflection.cpp] Reflection::invoke() ++ // assert(java_args.size_of_parameters() == method->size_of_parameters(), "just checking"); ++ ++ static inline void put_double(jdouble from, intptr_t *to, int& pos) { ++ *(jdouble*) (to + 1 + pos) = from; ++ *(jdouble*) (to + pos) = from; ++ pos += 2; ++ } ++ ++ static inline void put_double(jdouble *from, intptr_t *to, int& pos) { ++ *(jdouble*) (to + 1 + pos) = *from; ++ *(jdouble*) (to + pos) = *from; ++ pos += 2; ++ } ++ ++ // The get_xxx routines, on the other hand, actually _do_ fetch ++ // java primitive types from the interpreter stack. ++ static inline jint get_int (intptr_t *from) { return *(jint *) from; } ++ static inline jlong get_long (intptr_t *from) { return *(jlong *) (from + _JNI_SLOT_OFFSET); } ++ static inline oop get_obj (intptr_t *from) { return *(oop *) from; } ++ static inline jfloat get_float (intptr_t *from) { return *(jfloat *) from; } ++ static inline jdouble get_double(intptr_t *from) { return *(jdouble *)(from + _JNI_SLOT_OFFSET); } ++#undef _JNI_SLOT_OFFSET ++}; ++ ++#endif // CPU_MIPS_VM_JNITYPES_MIPS_HPP +diff --git a/hotspot/src/cpu/mips/vm/jni_mips.h b/hotspot/src/cpu/mips/vm/jni_mips.h +new file mode 100644 +index 0000000000..6714f51d5d +--- /dev/null ++++ b/hotspot/src/cpu/mips/vm/jni_mips.h +@@ -0,0 +1,51 @@ ++/* ++ * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2015, 2022, Loongson Technology. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. Oracle designates this ++ * particular file as subject to the "Classpath" exception as provided ++ * by Oracle in the LICENSE file that accompanied this code. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ */ ++ ++#ifndef _JAVASOFT_JNI_MD_H_ ++#define _JAVASOFT_JNI_MD_H_ ++ ++// Note: please do not change these without also changing jni_md.h in the JDK ++// repository ++#ifndef __has_attribute ++ #define __has_attribute(x) 0 ++#endif ++#if (defined(__GNUC__) && ((__GNUC__ > 4) || (__GNUC__ == 4) && (__GNUC_MINOR__ > 2))) || __has_attribute(visibility) ++ #define JNIEXPORT __attribute__((visibility("default"))) ++ #define JNIIMPORT __attribute__((visibility("default"))) ++#else ++ #define JNIEXPORT ++ #define JNIIMPORT ++#endif ++ ++#define JNICALL ++ ++typedef int jint; ++ ++typedef long jlong; ++ ++typedef signed char jbyte; ++ ++#endif +diff --git a/hotspot/src/cpu/mips/vm/macroAssembler_mips.cpp b/hotspot/src/cpu/mips/vm/macroAssembler_mips.cpp +new file mode 100644 +index 0000000000..2b8840ae10 +--- /dev/null ++++ b/hotspot/src/cpu/mips/vm/macroAssembler_mips.cpp +@@ -0,0 +1,4332 @@ ++/* ++ * Copyright (c) 1997, 2014, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2017, 2022, Loongson Technology. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ * ++ */ ++ ++#include "precompiled.hpp" ++#include "asm/assembler.hpp" ++#include "asm/assembler.inline.hpp" ++#include "asm/macroAssembler.inline.hpp" ++#include "compiler/disassembler.hpp" ++#include "gc_interface/collectedHeap.inline.hpp" ++#include "interpreter/interpreter.hpp" ++#include "memory/cardTableModRefBS.hpp" ++#include "memory/resourceArea.hpp" ++#include "memory/universe.hpp" ++#include "prims/methodHandles.hpp" ++#include "runtime/biasedLocking.hpp" ++#include "runtime/interfaceSupport.hpp" ++#include "runtime/objectMonitor.hpp" ++#include "runtime/os.hpp" ++#include "runtime/sharedRuntime.hpp" ++#include "runtime/stubRoutines.hpp" ++#include "utilities/macros.hpp" ++#if INCLUDE_ALL_GCS ++#include "gc_implementation/g1/g1CollectedHeap.inline.hpp" ++#include "gc_implementation/g1/g1SATBCardTableModRefBS.hpp" ++#include "gc_implementation/g1/heapRegion.hpp" ++#endif // INCLUDE_ALL_GCS ++ ++#define A0 RA0 ++#define A1 RA1 ++#define A2 RA2 ++#define A3 RA3 ++#define A4 RA4 ++#define A5 RA5 ++#define A6 RA6 ++#define A7 RA7 ++#define T0 RT0 ++#define T1 RT1 ++#define T2 RT2 ++#define T3 RT3 ++#define T8 RT8 ++#define T9 RT9 ++ ++// Implementation of MacroAssembler ++ ++intptr_t MacroAssembler::i[32] = {0}; ++float MacroAssembler::f[32] = {0.0}; ++ ++void MacroAssembler::print(outputStream *s) { ++ unsigned int k; ++ for(k=0; kprint_cr("i%d = 0x%.16lx", k, i[k]); ++ } ++ s->cr(); ++ ++ for(k=0; kprint_cr("f%d = %f", k, f[k]); ++ } ++ s->cr(); ++} ++ ++int MacroAssembler::i_offset(unsigned int k) { return (intptr_t)&((MacroAssembler*)0)->i[k]; } ++int MacroAssembler::f_offset(unsigned int k) { return (intptr_t)&((MacroAssembler*)0)->f[k]; } ++ ++void MacroAssembler::save_registers(MacroAssembler *masm) { ++#define __ masm-> ++ for(int k=0; k<32; k++) { ++ __ sw (as_Register(k), A0, i_offset(k)); ++ } ++ ++ for(int k=0; k<32; k++) { ++ __ swc1 (as_FloatRegister(k), A0, f_offset(k)); ++ } ++#undef __ ++} ++ ++void MacroAssembler::restore_registers(MacroAssembler *masm) { ++#define __ masm-> ++ for(int k=0; k<32; k++) { ++ __ lw (as_Register(k), A0, i_offset(k)); ++ } ++ ++ for(int k=0; k<32; k++) { ++ __ lwc1 (as_FloatRegister(k), A0, f_offset(k)); ++ } ++#undef __ ++} ++ ++ ++void MacroAssembler::pd_patch_instruction(address branch, address target) { ++ jint& stub_inst = *(jint*) branch; ++ jint *pc = (jint *)branch; ++ ++ if((opcode(stub_inst) == special_op) && (special(stub_inst) == daddu_op)) { ++ //b_far: ++ // move(AT, RA); // daddu ++ // emit_long(insn_ORRI(regimm_op, 0, bgezal_op, 1)); ++ // nop(); ++ // lui(T9, 0); // to be patched ++ // ori(T9, 0); ++ // daddu(T9, T9, RA); ++ // move(RA, AT); ++ // jr(T9); ++ ++ assert(opcode(pc[3]) == lui_op ++ && opcode(pc[4]) == ori_op ++ && special(pc[5]) == daddu_op, "Not a branch label patch"); ++ if(!(opcode(pc[3]) == lui_op ++ && opcode(pc[4]) == ori_op ++ && special(pc[5]) == daddu_op)) { tty->print_cr("Not a branch label patch"); } ++ ++ int offset = target - branch; ++ if (!is_simm16(offset)) { ++ pc[3] = (pc[3] & 0xffff0000) | high16(offset - 12); ++ pc[4] = (pc[4] & 0xffff0000) | low16(offset - 12); ++ } else { ++ // revert to "beq + nop" ++ CodeBuffer cb(branch, 4 * 10); ++ MacroAssembler masm(&cb); ++#define __ masm. ++ __ b(target); ++ __ delayed()->nop(); ++ __ nop(); ++ __ nop(); ++ __ nop(); ++ __ nop(); ++ __ nop(); ++ __ nop(); ++ } ++ return; ++ } else if (special(pc[4]) == jr_op ++ && opcode(pc[4]) == special_op ++ && (((opcode(pc[0]) == lui_op) || opcode(pc[0]) == daddiu_op) || (opcode(pc[0]) == ori_op))) { ++ //jmp_far: ++ // patchable_set48(T9, target); ++ // jr(T9); ++ // nop(); ++ ++ CodeBuffer cb(branch, 4 * 4); ++ MacroAssembler masm(&cb); ++ masm.patchable_set48(T9, (long)(target)); ++ return; ++ } ++ ++#ifndef PRODUCT ++ if (!is_simm16((target - branch - 4) >> 2)) { ++ tty->print_cr("Illegal patching: branch = " INTPTR_FORMAT ", target = " INTPTR_FORMAT, p2i(branch), p2i(target)); ++ tty->print_cr("======= Start decoding at branch = " INTPTR_FORMAT " =======", p2i(branch)); ++ Disassembler::decode(branch - 4 * 16, branch + 4 * 16, tty); ++ tty->print_cr("======= End of decoding ======="); ++ } ++#endif ++ ++ stub_inst = patched_branch(target - branch, stub_inst, 0); ++} ++ ++static inline address first_cache_address() { ++ return CodeCache::low_bound() + sizeof(HeapBlock::Header); ++} ++ ++static inline address last_cache_address() { ++ return CodeCache::high_bound() - Assembler::InstructionSize; ++} ++ ++int MacroAssembler::call_size(address target, bool far, bool patchable) { ++ if (patchable) return 6 << Assembler::LogInstructionSize; ++ if (!far) return 2 << Assembler::LogInstructionSize; // jal + nop ++ return (insts_for_set64((jlong)target) + 2) << Assembler::LogInstructionSize; ++} ++ ++// Can we reach target using jal/j from anywhere ++// in the code cache (because code can be relocated)? ++bool MacroAssembler::reachable_from_cache(address target) { ++ address cl = first_cache_address(); ++ address ch = last_cache_address(); ++ ++ return (cl <= target) && (target <= ch) && fit_in_jal(cl, ch); ++} ++ ++bool MacroAssembler::reachable_from_cache() { ++ if (ForceUnreachable) { ++ return false; ++ } else { ++ address cl = first_cache_address(); ++ address ch = last_cache_address(); ++ ++ return fit_in_jal(cl, ch); ++ } ++} ++ ++void MacroAssembler::general_jump(address target) { ++ if (reachable_from_cache(target)) { ++ j(target); ++ delayed()->nop(); ++ } else { ++ set64(T9, (long)target); ++ jr(T9); ++ delayed()->nop(); ++ } ++} ++ ++int MacroAssembler::insts_for_general_jump(address target) { ++ if (reachable_from_cache(target)) { ++ //j(target); ++ //nop(); ++ return 2; ++ } else { ++ //set64(T9, (long)target); ++ //jr(T9); ++ //nop(); ++ return insts_for_set64((jlong)target) + 2; ++ } ++} ++ ++void MacroAssembler::patchable_jump(address target) { ++ if (reachable_from_cache(target)) { ++ nop(); ++ nop(); ++ nop(); ++ nop(); ++ j(target); ++ delayed()->nop(); ++ } else { ++ patchable_set48(T9, (long)target); ++ jr(T9); ++ delayed()->nop(); ++ } ++} ++ ++int MacroAssembler::insts_for_patchable_jump(address target) { ++ return 6; ++} ++ ++void MacroAssembler::general_call(address target) { ++ if (reachable_from_cache(target)) { ++ jal(target); ++ delayed()->nop(); ++ } else { ++ set64(T9, (long)target); ++ jalr(T9); ++ delayed()->nop(); ++ } ++} ++ ++int MacroAssembler::insts_for_general_call(address target) { ++ if (reachable_from_cache(target)) { ++ //jal(target); ++ //nop(); ++ return 2; ++ } else { ++ //set64(T9, (long)target); ++ //jalr(T9); ++ //nop(); ++ return insts_for_set64((jlong)target) + 2; ++ } ++} ++ ++void MacroAssembler::patchable_call(address target) { ++ if (reachable_from_cache(target)) { ++ nop(); ++ nop(); ++ nop(); ++ nop(); ++ jal(target); ++ delayed()->nop(); ++ } else { ++ patchable_set48(T9, (long)target); ++ jalr(T9); ++ delayed()->nop(); ++ } ++} ++ ++int MacroAssembler::insts_for_patchable_call(address target) { ++ return 6; ++} ++ ++// Maybe emit a call via a trampoline. If the code cache is small ++// trampolines won't be emitted. ++ ++address MacroAssembler::trampoline_call(AddressLiteral entry, CodeBuffer *cbuf) { ++ assert(JavaThread::current()->is_Compiler_thread(), "just checking"); ++ assert(entry.rspec().type() == relocInfo::runtime_call_type ++ || entry.rspec().type() == relocInfo::opt_virtual_call_type ++ || entry.rspec().type() == relocInfo::static_call_type ++ || entry.rspec().type() == relocInfo::virtual_call_type, "wrong reloc type"); ++ ++ address target = entry.target(); ++ if (!reachable_from_cache()) { ++ address stub = emit_trampoline_stub(offset(), target); ++ if (stub == NULL) { ++ return NULL; // CodeCache is full ++ } ++ } ++ ++ if (cbuf) cbuf->set_insts_mark(); ++ relocate(entry.rspec()); ++ ++ if (reachable_from_cache()) { ++ nop(); ++ nop(); ++ nop(); ++ nop(); ++ jal(target); ++ delayed()->nop(); ++ } else { ++ // load the call target from the trampoline stub ++ // branch ++ long dest = (long)pc(); ++ dest += (dest & 0x8000) << 1; ++ lui(T9, dest >> 32); ++ ori(T9, T9, split_low(dest >> 16)); ++ dsll(T9, T9, 16); ++ ld(T9, T9, simm16(split_low(dest))); ++ jalr(T9); ++ delayed()->nop(); ++ } ++ return pc(); ++} ++ ++// Emit a trampoline stub for a call to a target which is too far away. ++address MacroAssembler::emit_trampoline_stub(int insts_call_instruction_offset, ++ address dest) { ++ // Max stub size: alignment nop, TrampolineStub. ++ address stub = start_a_stub(NativeInstruction::nop_instruction_size ++ + NativeCallTrampolineStub::instruction_size); ++ if (stub == NULL) { ++ return NULL; // CodeBuffer::expand failed ++ } ++ ++ // Create a trampoline stub relocation which relates this trampoline stub ++ // with the call instruction at insts_call_instruction_offset in the ++ // instructions code-section. ++ align(wordSize); ++ relocate(trampoline_stub_Relocation::spec(code()->insts()->start() ++ + insts_call_instruction_offset)); ++ emit_int64((int64_t)dest); ++ end_a_stub(); ++ return stub; ++} ++ ++void MacroAssembler::beq_far(Register rs, Register rt, address entry) { ++ u_char * cur_pc = pc(); ++ ++ // Near/Far jump ++ if(is_simm16((entry - pc() - 4) / 4)) { ++ Assembler::beq(rs, rt, offset(entry)); ++ } else { ++ Label not_jump; ++ bne(rs, rt, not_jump); ++ delayed()->nop(); ++ ++ b_far(entry); ++ delayed()->nop(); ++ ++ bind(not_jump); ++ has_delay_slot(); ++ } ++} ++ ++void MacroAssembler::beq_far(Register rs, Register rt, Label& L) { ++ if (L.is_bound()) { ++ beq_far(rs, rt, target(L)); ++ } else { ++ u_char * cur_pc = pc(); ++ Label not_jump; ++ bne(rs, rt, not_jump); ++ delayed()->nop(); ++ ++ b_far(L); ++ delayed()->nop(); ++ ++ bind(not_jump); ++ has_delay_slot(); ++ } ++} ++ ++void MacroAssembler::bne_far(Register rs, Register rt, address entry) { ++ u_char * cur_pc = pc(); ++ ++ //Near/Far jump ++ if(is_simm16((entry - pc() - 4) / 4)) { ++ Assembler::bne(rs, rt, offset(entry)); ++ } else { ++ Label not_jump; ++ beq(rs, rt, not_jump); ++ delayed()->nop(); ++ ++ b_far(entry); ++ delayed()->nop(); ++ ++ bind(not_jump); ++ has_delay_slot(); ++ } ++} ++ ++void MacroAssembler::bne_far(Register rs, Register rt, Label& L) { ++ if (L.is_bound()) { ++ bne_far(rs, rt, target(L)); ++ } else { ++ u_char * cur_pc = pc(); ++ Label not_jump; ++ beq(rs, rt, not_jump); ++ delayed()->nop(); ++ ++ b_far(L); ++ delayed()->nop(); ++ ++ bind(not_jump); ++ has_delay_slot(); ++ } ++} ++ ++void MacroAssembler::beq_long(Register rs, Register rt, Label& L) { ++ Label not_taken; ++ ++ bne(rs, rt, not_taken); ++ delayed()->nop(); ++ ++ jmp_far(L); ++ ++ bind(not_taken); ++} ++ ++void MacroAssembler::bne_long(Register rs, Register rt, Label& L) { ++ Label not_taken; ++ ++ beq(rs, rt, not_taken); ++ delayed()->nop(); ++ ++ jmp_far(L); ++ ++ bind(not_taken); ++} ++ ++void MacroAssembler::bc1t_long(Label& L) { ++ Label not_taken; ++ ++ bc1f(not_taken); ++ delayed()->nop(); ++ ++ jmp_far(L); ++ ++ bind(not_taken); ++} ++ ++void MacroAssembler::bc1f_long(Label& L) { ++ Label not_taken; ++ ++ bc1t(not_taken); ++ delayed()->nop(); ++ ++ jmp_far(L); ++ ++ bind(not_taken); ++} ++ ++void MacroAssembler::b_far(Label& L) { ++ if (L.is_bound()) { ++ b_far(target(L)); ++ } else { ++ volatile address dest = target(L); ++// ++// MacroAssembler::pd_patch_instruction branch=55651ed514, target=55651ef6d8 ++// 0x00000055651ed514: daddu at, ra, zero ++// 0x00000055651ed518: [4110001]bgezal zero, 0x00000055651ed520 ++// ++// 0x00000055651ed51c: sll zero, zero, 0 ++// 0x00000055651ed520: lui t9, 0x0 ++// 0x00000055651ed524: ori t9, t9, 0x21b8 ++// 0x00000055651ed528: daddu t9, t9, ra ++// 0x00000055651ed52c: daddu ra, at, zero ++// 0x00000055651ed530: jr t9 ++// 0x00000055651ed534: sll zero, zero, 0 ++// ++ move(AT, RA); ++ emit_long(insn_ORRI(regimm_op, 0, bgezal_op, 1)); ++ nop(); ++ lui(T9, 0); // to be patched ++ ori(T9, T9, 0); ++ daddu(T9, T9, RA); ++ move(RA, AT); ++ jr(T9); ++ } ++} ++ ++void MacroAssembler::b_far(address entry) { ++ u_char * cur_pc = pc(); ++ ++ // Near/Far jump ++ if(is_simm16((entry - pc() - 4) / 4)) { ++ b(offset(entry)); ++ } else { ++ // address must be bounded ++ move(AT, RA); ++ emit_long(insn_ORRI(regimm_op, 0, bgezal_op, 1)); ++ nop(); ++ li32(T9, entry - pc()); ++ daddu(T9, T9, RA); ++ move(RA, AT); ++ jr(T9); ++ } ++} ++ ++void MacroAssembler::ld_ptr(Register rt, Register base, Register offset) { ++ addu_long(AT, base, offset); ++ ld_ptr(rt, AT, 0); ++} ++ ++void MacroAssembler::st_ptr(Register rt, Register base, Register offset) { ++ guarantee(AT != rt, "AT must not equal rt"); ++ addu_long(AT, base, offset); ++ st_ptr(rt, AT, 0); ++} ++ ++Address MacroAssembler::as_Address(AddressLiteral adr) { ++ return Address(adr.target(), adr.rspec()); ++} ++ ++Address MacroAssembler::as_Address(ArrayAddress adr) { ++ return Address::make_array(adr); ++} ++ ++// tmp_reg1 and tmp_reg2 should be saved outside of atomic_inc32 (caller saved). ++void MacroAssembler::atomic_inc32(address counter_addr, int inc, Register tmp_reg1, Register tmp_reg2) { ++ Label again; ++ ++ li(tmp_reg1, counter_addr); ++ bind(again); ++ if (UseSyncLevel >= 10000 || UseSyncLevel == 1000 || UseSyncLevel == 4000) sync(); ++ ll(tmp_reg2, tmp_reg1, 0); ++ addiu(tmp_reg2, tmp_reg2, inc); ++ sc(tmp_reg2, tmp_reg1, 0); ++ beq(tmp_reg2, R0, again); ++ delayed()->nop(); ++} ++ ++int MacroAssembler::biased_locking_enter(Register lock_reg, ++ Register obj_reg, ++ Register swap_reg, ++ Register tmp_reg, ++ bool swap_reg_contains_mark, ++ Label& done, ++ Label* slow_case, ++ BiasedLockingCounters* counters) { ++ assert(UseBiasedLocking, "why call this otherwise?"); ++ bool need_tmp_reg = false; ++ if (tmp_reg == noreg) { ++ need_tmp_reg = true; ++ tmp_reg = T9; ++ } ++ assert_different_registers(lock_reg, obj_reg, swap_reg, tmp_reg, AT); ++ assert(markOopDesc::age_shift == markOopDesc::lock_bits + markOopDesc::biased_lock_bits, "biased locking makes assumptions about bit layout"); ++ Address mark_addr (obj_reg, oopDesc::mark_offset_in_bytes()); ++ Address saved_mark_addr(lock_reg, 0); ++ ++ // Biased locking ++ // See whether the lock is currently biased toward our thread and ++ // whether the epoch is still valid ++ // Note that the runtime guarantees sufficient alignment of JavaThread ++ // pointers to allow age to be placed into low bits ++ // First check to see whether biasing is even enabled for this object ++ Label cas_label; ++ int null_check_offset = -1; ++ if (!swap_reg_contains_mark) { ++ null_check_offset = offset(); ++ ld_ptr(swap_reg, mark_addr); ++ } ++ ++ if (need_tmp_reg) { ++ push(tmp_reg); ++ } ++ move(tmp_reg, swap_reg); ++ andi(tmp_reg, tmp_reg, markOopDesc::biased_lock_mask_in_place); ++ daddiu(AT, R0, markOopDesc::biased_lock_pattern); ++ dsubu(AT, AT, tmp_reg); ++ if (need_tmp_reg) { ++ pop(tmp_reg); ++ } ++ ++ bne(AT, R0, cas_label); ++ delayed()->nop(); ++ ++ ++ // The bias pattern is present in the object's header. Need to check ++ // whether the bias owner and the epoch are both still current. ++ // Note that because there is no current thread register on MIPS we ++ // need to store off the mark word we read out of the object to ++ // avoid reloading it and needing to recheck invariants below. This ++ // store is unfortunate but it makes the overall code shorter and ++ // simpler. ++ st_ptr(swap_reg, saved_mark_addr); ++ if (need_tmp_reg) { ++ push(tmp_reg); ++ } ++ if (swap_reg_contains_mark) { ++ null_check_offset = offset(); ++ } ++ load_prototype_header(tmp_reg, obj_reg); ++ xorr(tmp_reg, tmp_reg, swap_reg); ++ get_thread(swap_reg); ++ xorr(swap_reg, swap_reg, tmp_reg); ++ ++ move(AT, ~((int) markOopDesc::age_mask_in_place)); ++ andr(swap_reg, swap_reg, AT); ++ ++ if (PrintBiasedLockingStatistics) { ++ Label L; ++ bne(swap_reg, R0, L); ++ delayed()->nop(); ++ push(tmp_reg); ++ push(A0); ++ atomic_inc32((address)BiasedLocking::biased_lock_entry_count_addr(), 1, A0, tmp_reg); ++ pop(A0); ++ pop(tmp_reg); ++ bind(L); ++ } ++ if (need_tmp_reg) { ++ pop(tmp_reg); ++ } ++ beq(swap_reg, R0, done); ++ delayed()->nop(); ++ Label try_revoke_bias; ++ Label try_rebias; ++ ++ // At this point we know that the header has the bias pattern and ++ // that we are not the bias owner in the current epoch. We need to ++ // figure out more details about the state of the header in order to ++ // know what operations can be legally performed on the object's ++ // header. ++ ++ // If the low three bits in the xor result aren't clear, that means ++ // the prototype header is no longer biased and we have to revoke ++ // the bias on this object. ++ ++ move(AT, markOopDesc::biased_lock_mask_in_place); ++ andr(AT, swap_reg, AT); ++ bne(AT, R0, try_revoke_bias); ++ delayed()->nop(); ++ // Biasing is still enabled for this data type. See whether the ++ // epoch of the current bias is still valid, meaning that the epoch ++ // bits of the mark word are equal to the epoch bits of the ++ // prototype header. (Note that the prototype header's epoch bits ++ // only change at a safepoint.) If not, attempt to rebias the object ++ // toward the current thread. Note that we must be absolutely sure ++ // that the current epoch is invalid in order to do this because ++ // otherwise the manipulations it performs on the mark word are ++ // illegal. ++ ++ move(AT, markOopDesc::epoch_mask_in_place); ++ andr(AT,swap_reg, AT); ++ bne(AT, R0, try_rebias); ++ delayed()->nop(); ++ // The epoch of the current bias is still valid but we know nothing ++ // about the owner; it might be set or it might be clear. Try to ++ // acquire the bias of the object using an atomic operation. If this ++ // fails we will go in to the runtime to revoke the object's bias. ++ // Note that we first construct the presumed unbiased header so we ++ // don't accidentally blow away another thread's valid bias. ++ ++ ld_ptr(swap_reg, saved_mark_addr); ++ ++ move(AT, markOopDesc::biased_lock_mask_in_place | markOopDesc::age_mask_in_place | markOopDesc::epoch_mask_in_place); ++ andr(swap_reg, swap_reg, AT); ++ ++ if (need_tmp_reg) { ++ push(tmp_reg); ++ } ++ get_thread(tmp_reg); ++ orr(tmp_reg, tmp_reg, swap_reg); ++ //if (os::is_MP()) { ++ // sync(); ++ //} ++ cmpxchg(tmp_reg, Address(obj_reg, 0), swap_reg); ++ if (need_tmp_reg) { ++ pop(tmp_reg); ++ } ++ // If the biasing toward our thread failed, this means that ++ // another thread succeeded in biasing it toward itself and we ++ // need to revoke that bias. The revocation will occur in the ++ // interpreter runtime in the slow case. ++ if (PrintBiasedLockingStatistics) { ++ Label L; ++ bne(AT, R0, L); ++ delayed()->nop(); ++ push(tmp_reg); ++ push(A0); ++ atomic_inc32((address)BiasedLocking::anonymously_biased_lock_entry_count_addr(), 1, A0, tmp_reg); ++ pop(A0); ++ pop(tmp_reg); ++ bind(L); ++ } ++ if (slow_case != NULL) { ++ beq_far(AT, R0, *slow_case); ++ delayed()->nop(); ++ } ++ b(done); ++ delayed()->nop(); ++ ++ bind(try_rebias); ++ // At this point we know the epoch has expired, meaning that the ++ // current "bias owner", if any, is actually invalid. Under these ++ // circumstances _only_, we are allowed to use the current header's ++ // value as the comparison value when doing the cas to acquire the ++ // bias in the current epoch. In other words, we allow transfer of ++ // the bias from one thread to another directly in this situation. ++ // ++ // FIXME: due to a lack of registers we currently blow away the age ++ // bits in this situation. Should attempt to preserve them. ++ if (need_tmp_reg) { ++ push(tmp_reg); ++ } ++ load_prototype_header(tmp_reg, obj_reg); ++ get_thread(swap_reg); ++ orr(tmp_reg, tmp_reg, swap_reg); ++ ld_ptr(swap_reg, saved_mark_addr); ++ ++ //if (os::is_MP()) { ++ // sync(); ++ //} ++ cmpxchg(tmp_reg, Address(obj_reg, 0), swap_reg); ++ if (need_tmp_reg) { ++ pop(tmp_reg); ++ } ++ // If the biasing toward our thread failed, then another thread ++ // succeeded in biasing it toward itself and we need to revoke that ++ // bias. The revocation will occur in the runtime in the slow case. ++ if (PrintBiasedLockingStatistics) { ++ Label L; ++ bne(AT, R0, L); ++ delayed()->nop(); ++ push(AT); ++ push(tmp_reg); ++ atomic_inc32((address)BiasedLocking::rebiased_lock_entry_count_addr(), 1, AT, tmp_reg); ++ pop(tmp_reg); ++ pop(AT); ++ bind(L); ++ } ++ if (slow_case != NULL) { ++ beq_far(AT, R0, *slow_case); ++ delayed()->nop(); ++ } ++ ++ b(done); ++ delayed()->nop(); ++ bind(try_revoke_bias); ++ // The prototype mark in the klass doesn't have the bias bit set any ++ // more, indicating that objects of this data type are not supposed ++ // to be biased any more. We are going to try to reset the mark of ++ // this object to the prototype value and fall through to the ++ // CAS-based locking scheme. Note that if our CAS fails, it means ++ // that another thread raced us for the privilege of revoking the ++ // bias of this particular object, so it's okay to continue in the ++ // normal locking code. ++ // ++ // FIXME: due to a lack of registers we currently blow away the age ++ // bits in this situation. Should attempt to preserve them. ++ ld_ptr(swap_reg, saved_mark_addr); ++ ++ if (need_tmp_reg) { ++ push(tmp_reg); ++ } ++ load_prototype_header(tmp_reg, obj_reg); ++ //if (os::is_MP()) { ++ // lock(); ++ //} ++ cmpxchg(tmp_reg, Address(obj_reg, 0), swap_reg); ++ if (need_tmp_reg) { ++ pop(tmp_reg); ++ } ++ // Fall through to the normal CAS-based lock, because no matter what ++ // the result of the above CAS, some thread must have succeeded in ++ // removing the bias bit from the object's header. ++ if (PrintBiasedLockingStatistics) { ++ Label L; ++ bne(AT, R0, L); ++ delayed()->nop(); ++ push(AT); ++ push(tmp_reg); ++ atomic_inc32((address)BiasedLocking::revoked_lock_entry_count_addr(), 1, AT, tmp_reg); ++ pop(tmp_reg); ++ pop(AT); ++ bind(L); ++ } ++ ++ bind(cas_label); ++ return null_check_offset; ++} ++ ++void MacroAssembler::biased_locking_exit(Register obj_reg, Register temp_reg, Label& done) { ++ assert(UseBiasedLocking, "why call this otherwise?"); ++ ++ // Check for biased locking unlock case, which is a no-op ++ // Note: we do not have to check the thread ID for two reasons. ++ // First, the interpreter checks for IllegalMonitorStateException at ++ // a higher level. Second, if the bias was revoked while we held the ++ // lock, the object could not be rebiased toward another thread, so ++ // the bias bit would be clear. ++ ld(temp_reg, Address(obj_reg, oopDesc::mark_offset_in_bytes())); ++ andi(temp_reg, temp_reg, markOopDesc::biased_lock_mask_in_place); ++ daddiu(AT, R0, markOopDesc::biased_lock_pattern); ++ ++ beq(AT, temp_reg, done); ++ delayed()->nop(); ++} ++ ++// the stack pointer adjustment is needed. see InterpreterMacroAssembler::super_call_VM_leaf ++// this method will handle the stack problem, you need not to preserve the stack space for the argument now ++void MacroAssembler::call_VM_leaf_base(address entry_point, int number_of_arguments) { ++ Label L, E; ++ ++ assert(number_of_arguments <= 4, "just check"); ++ ++ andi(AT, SP, 0xf); ++ beq(AT, R0, L); ++ delayed()->nop(); ++ daddiu(SP, SP, -8); ++ call(entry_point, relocInfo::runtime_call_type); ++ delayed()->nop(); ++ daddiu(SP, SP, 8); ++ b(E); ++ delayed()->nop(); ++ ++ bind(L); ++ call(entry_point, relocInfo::runtime_call_type); ++ delayed()->nop(); ++ bind(E); ++} ++ ++ ++void MacroAssembler::jmp(address entry) { ++ patchable_set48(T9, (long)entry); ++ jr(T9); ++} ++ ++void MacroAssembler::jmp(address entry, relocInfo::relocType rtype) { ++ switch (rtype) { ++ case relocInfo::runtime_call_type: ++ case relocInfo::none: ++ jmp(entry); ++ break; ++ default: ++ { ++ InstructionMark im(this); ++ relocate(rtype); ++ patchable_set48(T9, (long)entry); ++ jr(T9); ++ } ++ break; ++ } ++} ++ ++void MacroAssembler::jmp_far(Label& L) { ++ if (L.is_bound()) { ++ address entry = target(L); ++ assert(entry != NULL, "jmp most probably wrong"); ++ InstructionMark im(this); ++ ++ relocate(relocInfo::internal_word_type); ++ patchable_set48(T9, (long)entry); ++ } else { ++ InstructionMark im(this); ++ L.add_patch_at(code(), locator()); ++ ++ relocate(relocInfo::internal_word_type); ++ patchable_set48(T9, (long)pc()); ++ } ++ ++ jr(T9); ++ delayed()->nop(); ++} ++void MacroAssembler::mov_metadata(Address dst, Metadata* obj) { ++ int oop_index; ++ if (obj) { ++ oop_index = oop_recorder()->find_index(obj); ++ } else { ++ oop_index = oop_recorder()->allocate_metadata_index(obj); ++ } ++ relocate(metadata_Relocation::spec(oop_index)); ++ patchable_set48(AT, (long)obj); ++ sd(AT, dst); ++} ++ ++void MacroAssembler::mov_metadata(Register dst, Metadata* obj) { ++ int oop_index; ++ if (obj) { ++ oop_index = oop_recorder()->find_index(obj); ++ } else { ++ oop_index = oop_recorder()->allocate_metadata_index(obj); ++ } ++ relocate(metadata_Relocation::spec(oop_index)); ++ patchable_set48(dst, (long)obj); ++} ++ ++void MacroAssembler::call(address entry) { ++// c/c++ code assume T9 is entry point, so we just always move entry to t9 ++// maybe there is some more graceful method to handle this. FIXME ++// For more info, see class NativeCall. ++ patchable_set48(T9, (long)entry); ++ jalr(T9); ++} ++ ++void MacroAssembler::call(address entry, relocInfo::relocType rtype) { ++ switch (rtype) { ++ case relocInfo::runtime_call_type: ++ case relocInfo::none: ++ call(entry); ++ break; ++ default: ++ { ++ InstructionMark im(this); ++ relocate(rtype); ++ call(entry); ++ } ++ break; ++ } ++} ++ ++void MacroAssembler::call(address entry, RelocationHolder& rh) ++{ ++ switch (rh.type()) { ++ case relocInfo::runtime_call_type: ++ case relocInfo::none: ++ call(entry); ++ break; ++ default: ++ { ++ InstructionMark im(this); ++ relocate(rh); ++ call(entry); ++ } ++ break; ++ } ++} ++ ++void MacroAssembler::ic_call(address entry) { ++ RelocationHolder rh = virtual_call_Relocation::spec(pc()); ++ patchable_set48(IC_Klass, (long)Universe::non_oop_word()); ++ assert(entry != NULL, "call most probably wrong"); ++ InstructionMark im(this); ++ trampoline_call(AddressLiteral(entry, rh)); ++} ++ ++void MacroAssembler::c2bool(Register r) { ++ Label L; ++ Assembler::beq(r, R0, L); ++ delayed()->nop(); ++ move(r, 1); ++ bind(L); ++} ++ ++#ifndef PRODUCT ++extern "C" void findpc(intptr_t x); ++#endif ++ ++void MacroAssembler::debug(char* msg/*, RegistersForDebugging* regs*/) { ++ if ( ShowMessageBoxOnError ) { ++ JavaThreadState saved_state = JavaThread::current()->thread_state(); ++ JavaThread::current()->set_thread_state(_thread_in_vm); ++ { ++ // In order to get locks work, we need to fake a in_VM state ++ ttyLocker ttyl; ++ ::tty->print_cr("EXECUTION STOPPED: %s\n", msg); ++ if (CountBytecodes || TraceBytecodes || StopInterpreterAt) { ++ BytecodeCounter::print(); ++ } ++ ++ } ++ ThreadStateTransition::transition(JavaThread::current(), _thread_in_vm, saved_state); ++ } ++ else ++ ::tty->print_cr("=============== DEBUG MESSAGE: %s ================\n", msg); ++} ++ ++ ++void MacroAssembler::stop(const char* msg) { ++ li(A0, (long)msg); ++ call(CAST_FROM_FN_PTR(address, MacroAssembler::debug), relocInfo::runtime_call_type); ++ delayed()->nop(); ++ brk(17); ++} ++ ++void MacroAssembler::warn(const char* msg) { ++ pushad(); ++ li(A0, (long)msg); ++ push(S2); ++ move(AT, -(StackAlignmentInBytes)); ++ move(S2, SP); // use S2 as a sender SP holder ++ andr(SP, SP, AT); // align stack as required by ABI ++ call(CAST_FROM_FN_PTR(address, MacroAssembler::debug), relocInfo::runtime_call_type); ++ delayed()->nop(); ++ move(SP, S2); // use S2 as a sender SP holder ++ pop(S2); ++ popad(); ++} ++ ++void MacroAssembler::increment(Register reg, int imm) { ++ if (!imm) return; ++ if (is_simm16(imm)) { ++ daddiu(reg, reg, imm); ++ } else { ++ move(AT, imm); ++ daddu(reg, reg, AT); ++ } ++} ++ ++void MacroAssembler::decrement(Register reg, int imm) { ++ increment(reg, -imm); ++} ++ ++ ++void MacroAssembler::call_VM(Register oop_result, ++ address entry_point, ++ bool check_exceptions) { ++ call_VM_helper(oop_result, entry_point, 0, check_exceptions); ++} ++ ++void MacroAssembler::call_VM(Register oop_result, ++ address entry_point, ++ Register arg_1, ++ bool check_exceptions) { ++ if (arg_1!=A1) move(A1, arg_1); ++ call_VM_helper(oop_result, entry_point, 1, check_exceptions); ++} ++ ++void MacroAssembler::call_VM(Register oop_result, ++ address entry_point, ++ Register arg_1, ++ Register arg_2, ++ bool check_exceptions) { ++ if (arg_1!=A1) move(A1, arg_1); ++ if (arg_2!=A2) move(A2, arg_2); ++ assert(arg_2 != A1, "smashed argument"); ++ call_VM_helper(oop_result, entry_point, 2, check_exceptions); ++} ++ ++void MacroAssembler::call_VM(Register oop_result, ++ address entry_point, ++ Register arg_1, ++ Register arg_2, ++ Register arg_3, ++ bool check_exceptions) { ++ if (arg_1!=A1) move(A1, arg_1); ++ if (arg_2!=A2) move(A2, arg_2); assert(arg_2 != A1, "smashed argument"); ++ if (arg_3!=A3) move(A3, arg_3); assert(arg_3 != A1 && arg_3 != A2, "smashed argument"); ++ call_VM_helper(oop_result, entry_point, 3, check_exceptions); ++} ++ ++void MacroAssembler::call_VM(Register oop_result, ++ Register last_java_sp, ++ address entry_point, ++ int number_of_arguments, ++ bool check_exceptions) { ++ call_VM_base(oop_result, NOREG, last_java_sp, entry_point, number_of_arguments, check_exceptions); ++} ++ ++void MacroAssembler::call_VM(Register oop_result, ++ Register last_java_sp, ++ address entry_point, ++ Register arg_1, ++ bool check_exceptions) { ++ if (arg_1 != A1) move(A1, arg_1); ++ call_VM(oop_result, last_java_sp, entry_point, 1, check_exceptions); ++} ++ ++void MacroAssembler::call_VM(Register oop_result, ++ Register last_java_sp, ++ address entry_point, ++ Register arg_1, ++ Register arg_2, ++ bool check_exceptions) { ++ if (arg_1 != A1) move(A1, arg_1); ++ if (arg_2 != A2) move(A2, arg_2); assert(arg_2 != A1, "smashed argument"); ++ call_VM(oop_result, last_java_sp, entry_point, 2, check_exceptions); ++} ++ ++void MacroAssembler::call_VM(Register oop_result, ++ Register last_java_sp, ++ address entry_point, ++ Register arg_1, ++ Register arg_2, ++ Register arg_3, ++ bool check_exceptions) { ++ if (arg_1 != A1) move(A1, arg_1); ++ if (arg_2 != A2) move(A2, arg_2); assert(arg_2 != A1, "smashed argument"); ++ if (arg_3 != A3) move(A3, arg_3); assert(arg_3 != A1 && arg_3 != A2, "smashed argument"); ++ call_VM(oop_result, last_java_sp, entry_point, 3, check_exceptions); ++} ++ ++void MacroAssembler::call_VM_base(Register oop_result, ++ Register java_thread, ++ Register last_java_sp, ++ address entry_point, ++ int number_of_arguments, ++ bool check_exceptions) { ++ ++ address before_call_pc; ++ // determine java_thread register ++ if (!java_thread->is_valid()) { ++#ifndef OPT_THREAD ++ java_thread = T2; ++ get_thread(java_thread); ++#else ++ java_thread = TREG; ++#endif ++ } ++ // determine last_java_sp register ++ if (!last_java_sp->is_valid()) { ++ last_java_sp = SP; ++ } ++ // debugging support ++ assert(number_of_arguments >= 0 , "cannot have negative number of arguments"); ++ assert(number_of_arguments <= 4 , "cannot have negative number of arguments"); ++ assert(java_thread != oop_result , "cannot use the same register for java_thread & oop_result"); ++ assert(java_thread != last_java_sp, "cannot use the same register for java_thread & last_java_sp"); ++ ++ assert(last_java_sp != FP, "this code doesn't work for last_java_sp == fp, which currently can't portably work anyway since C2 doesn't save fp"); ++ ++ // set last Java frame before call ++ before_call_pc = (address)pc(); ++ set_last_Java_frame(java_thread, last_java_sp, FP, before_call_pc); ++ ++ // do the call ++ move(A0, java_thread); ++ call(entry_point, relocInfo::runtime_call_type); ++ delayed()->nop(); ++ ++ // restore the thread (cannot use the pushed argument since arguments ++ // may be overwritten by C code generated by an optimizing compiler); ++ // however can use the register value directly if it is callee saved. ++#ifndef OPT_THREAD ++ get_thread(java_thread); ++#else ++#ifdef ASSERT ++ { ++ Label L; ++ get_thread(AT); ++ beq(java_thread, AT, L); ++ delayed()->nop(); ++ stop("MacroAssembler::call_VM_base: TREG not callee saved?"); ++ bind(L); ++ } ++#endif ++#endif ++ ++ // discard thread and arguments ++ ld_ptr(SP, java_thread, in_bytes(JavaThread::last_Java_sp_offset())); ++ // reset last Java frame ++ reset_last_Java_frame(java_thread, false); ++ ++ check_and_handle_popframe(java_thread); ++ check_and_handle_earlyret(java_thread); ++ if (check_exceptions) { ++ // check for pending exceptions (java_thread is set upon return) ++ Label L; ++ ld(AT, java_thread, in_bytes(Thread::pending_exception_offset())); ++ beq(AT, R0, L); ++ delayed()->nop(); ++ li(AT, before_call_pc); ++ push(AT); ++ jmp(StubRoutines::forward_exception_entry(), relocInfo::runtime_call_type); ++ delayed()->nop(); ++ bind(L); ++ } ++ ++ // get oop result if there is one and reset the value in the thread ++ if (oop_result->is_valid()) { ++ ld(oop_result, java_thread, in_bytes(JavaThread::vm_result_offset())); ++ sd(R0, java_thread, in_bytes(JavaThread::vm_result_offset())); ++ verify_oop(oop_result); ++ } ++} ++ ++void MacroAssembler::call_VM_helper(Register oop_result, address entry_point, int number_of_arguments, bool check_exceptions) { ++ ++ move(V0, SP); ++ //we also reserve space for java_thread here ++ move(AT, -(StackAlignmentInBytes)); ++ andr(SP, SP, AT); ++ call_VM_base(oop_result, NOREG, V0, entry_point, number_of_arguments, check_exceptions); ++ ++} ++ ++void MacroAssembler::call_VM_leaf(address entry_point, int number_of_arguments) { ++ call_VM_leaf_base(entry_point, number_of_arguments); ++} ++ ++void MacroAssembler::call_VM_leaf(address entry_point, Register arg_0) { ++ if (arg_0 != A0) move(A0, arg_0); ++ call_VM_leaf(entry_point, 1); ++} ++ ++void MacroAssembler::call_VM_leaf(address entry_point, Register arg_0, Register arg_1) { ++ if (arg_0 != A0) move(A0, arg_0); ++ if (arg_1 != A1) move(A1, arg_1); assert(arg_1 != A0, "smashed argument"); ++ call_VM_leaf(entry_point, 2); ++} ++ ++void MacroAssembler::call_VM_leaf(address entry_point, Register arg_0, Register arg_1, Register arg_2) { ++ if (arg_0 != A0) move(A0, arg_0); ++ if (arg_1 != A1) move(A1, arg_1); assert(arg_1 != A0, "smashed argument"); ++ if (arg_2 != A2) move(A2, arg_2); assert(arg_2 != A0 && arg_2 != A1, "smashed argument"); ++ call_VM_leaf(entry_point, 3); ++} ++void MacroAssembler::super_call_VM_leaf(address entry_point) { ++ MacroAssembler::call_VM_leaf_base(entry_point, 0); ++} ++ ++ ++void MacroAssembler::super_call_VM_leaf(address entry_point, ++ Register arg_1) { ++ if (arg_1 != A0) move(A0, arg_1); ++ MacroAssembler::call_VM_leaf_base(entry_point, 1); ++} ++ ++ ++void MacroAssembler::super_call_VM_leaf(address entry_point, ++ Register arg_1, ++ Register arg_2) { ++ if (arg_1 != A0) move(A0, arg_1); ++ if (arg_2 != A1) move(A1, arg_2); assert(arg_2 != A0, "smashed argument"); ++ MacroAssembler::call_VM_leaf_base(entry_point, 2); ++} ++void MacroAssembler::super_call_VM_leaf(address entry_point, ++ Register arg_1, ++ Register arg_2, ++ Register arg_3) { ++ if (arg_1 != A0) move(A0, arg_1); ++ if (arg_2 != A1) move(A1, arg_2); assert(arg_2 != A0, "smashed argument"); ++ if (arg_3 != A2) move(A2, arg_3); assert(arg_3 != A0 && arg_3 != A1, "smashed argument"); ++ MacroAssembler::call_VM_leaf_base(entry_point, 3); ++} ++ ++void MacroAssembler::check_and_handle_earlyret(Register java_thread) { ++} ++ ++void MacroAssembler::check_and_handle_popframe(Register java_thread) { ++} ++ ++void MacroAssembler::null_check(Register reg, int offset) { ++ if (needs_explicit_null_check(offset)) { ++ // provoke OS NULL exception if reg = NULL by ++ // accessing M[reg] w/o changing any (non-CC) registers ++ // NOTE: cmpl is plenty here to provoke a segv ++ lw(AT, reg, 0); ++ } else { ++ // nothing to do, (later) access of M[reg + offset] ++ // will provoke OS NULL exception if reg = NULL ++ } ++} ++ ++void MacroAssembler::enter() { ++ push2(RA, FP); ++ move(FP, SP); ++} ++ ++void MacroAssembler::leave() { ++ move(SP, FP); ++ pop2(RA, FP); ++} ++ ++void MacroAssembler::reset_last_Java_frame(Register java_thread, bool clear_fp) { ++ // determine java_thread register ++ if (!java_thread->is_valid()) { ++#ifndef OPT_THREAD ++ java_thread = T1; ++ get_thread(java_thread); ++#else ++ java_thread = TREG; ++#endif ++ } ++ // we must set sp to zero to clear frame ++ st_ptr(R0, java_thread, in_bytes(JavaThread::last_Java_sp_offset())); ++ // must clear fp, so that compiled frames are not confused; it is possible ++ // that we need it only for debugging ++ if(clear_fp) { ++ st_ptr(R0, java_thread, in_bytes(JavaThread::last_Java_fp_offset())); ++ } ++ ++ // Always clear the pc because it could have been set by make_walkable() ++ st_ptr(R0, java_thread, in_bytes(JavaThread::last_Java_pc_offset())); ++} ++ ++void MacroAssembler::reset_last_Java_frame(bool clear_fp) { ++ Register thread = TREG; ++#ifndef OPT_THREAD ++ get_thread(thread); ++#endif ++ // we must set sp to zero to clear frame ++ sd(R0, Address(thread, JavaThread::last_Java_sp_offset())); ++ // must clear fp, so that compiled frames are not confused; it is ++ // possible that we need it only for debugging ++ if (clear_fp) { ++ sd(R0, Address(thread, JavaThread::last_Java_fp_offset())); ++ } ++ ++ // Always clear the pc because it could have been set by make_walkable() ++ sd(R0, Address(thread, JavaThread::last_Java_pc_offset())); ++} ++ ++// Write serialization page so VM thread can do a pseudo remote membar. ++// We use the current thread pointer to calculate a thread specific ++// offset to write to within the page. This minimizes bus traffic ++// due to cache line collision. ++void MacroAssembler::serialize_memory(Register thread, Register tmp) { ++ int mask = os::vm_page_size() - sizeof(int); ++ assert_different_registers(AT, tmp); ++ assert(is_uimm(mask, 16), "Not a unsigned 16-bit"); ++ srl(AT, thread, os::get_serialize_page_shift_count()); ++ andi(AT, AT, mask); ++ li(tmp, os::get_memory_serialize_page()); ++ addu(tmp, tmp, AT); ++ sw(R0, tmp, 0); ++} ++ ++// Calls to C land ++// ++// When entering C land, the fp, & sp of the last Java frame have to be recorded ++// in the (thread-local) JavaThread object. When leaving C land, the last Java fp ++// has to be reset to 0. This is required to allow proper stack traversal. ++void MacroAssembler::set_last_Java_frame(Register java_thread, ++ Register last_java_sp, ++ Register last_java_fp, ++ address last_java_pc) { ++ // determine java_thread register ++ if (!java_thread->is_valid()) { ++#ifndef OPT_THREAD ++ java_thread = T2; ++ get_thread(java_thread); ++#else ++ java_thread = TREG; ++#endif ++ } ++ // determine last_java_sp register ++ if (!last_java_sp->is_valid()) { ++ last_java_sp = SP; ++ } ++ ++ // last_java_fp is optional ++ if (last_java_fp->is_valid()) { ++ st_ptr(last_java_fp, java_thread, in_bytes(JavaThread::last_Java_fp_offset())); ++ } ++ ++ // last_java_pc is optional ++ if (last_java_pc != NULL) { ++ relocate(relocInfo::internal_word_type); ++ patchable_set48(AT, (long)last_java_pc); ++ st_ptr(AT, java_thread, in_bytes(JavaThread::frame_anchor_offset() + JavaFrameAnchor::last_Java_pc_offset())); ++ } ++ st_ptr(last_java_sp, java_thread, in_bytes(JavaThread::last_Java_sp_offset())); ++} ++ ++void MacroAssembler::set_last_Java_frame(Register last_java_sp, ++ Register last_java_fp, ++ address last_java_pc) { ++ // determine last_java_sp register ++ if (!last_java_sp->is_valid()) { ++ last_java_sp = SP; ++ } ++ ++ Register thread = TREG; ++#ifndef OPT_THREAD ++ get_thread(thread); ++#endif ++ // last_java_fp is optional ++ if (last_java_fp->is_valid()) { ++ sd(last_java_fp, Address(thread, JavaThread::last_Java_fp_offset())); ++ } ++ ++ // last_java_pc is optional ++ if (last_java_pc != NULL) { ++ relocate(relocInfo::internal_word_type); ++ patchable_set48(AT, (long)last_java_pc); ++ st_ptr(AT, thread, in_bytes(JavaThread::frame_anchor_offset() + JavaFrameAnchor::last_Java_pc_offset())); ++ } ++ ++ sd(last_java_sp, Address(thread, JavaThread::last_Java_sp_offset())); ++} ++ ++////////////////////////////////////////////////////////////////////////////////// ++#if INCLUDE_ALL_GCS ++ ++void MacroAssembler::g1_write_barrier_pre(Register obj, ++ Register pre_val, ++ Register thread, ++ Register tmp, ++ bool tosca_live, ++ bool expand_call) { ++ ++ // If expand_call is true then we expand the call_VM_leaf macro ++ // directly to skip generating the check by ++ // InterpreterMacroAssembler::call_VM_leaf_base that checks _last_sp. ++ ++ assert(thread == TREG, "must be"); ++ ++ Label done; ++ Label runtime; ++ ++ assert(pre_val != noreg, "check this code"); ++ ++ if (obj != noreg) { ++ assert_different_registers(obj, pre_val, tmp); ++ assert(pre_val != V0, "check this code"); ++ } ++ ++ Address in_progress(thread, in_bytes(JavaThread::satb_mark_queue_offset() + ++ PtrQueue::byte_offset_of_active())); ++ Address index(thread, in_bytes(JavaThread::satb_mark_queue_offset() + ++ PtrQueue::byte_offset_of_index())); ++ Address buffer(thread, in_bytes(JavaThread::satb_mark_queue_offset() + ++ PtrQueue::byte_offset_of_buf())); ++ ++ ++ // Is marking active? ++ if (in_bytes(PtrQueue::byte_width_of_active()) == 4) { ++ lw(AT, in_progress); ++ } else { ++ assert(in_bytes(PtrQueue::byte_width_of_active()) == 1, "Assumption"); ++ lb(AT, in_progress); ++ } ++ beq(AT, R0, done); ++ delayed()->nop(); ++ ++ // Do we need to load the previous value? ++ if (obj != noreg) { ++ load_heap_oop(pre_val, Address(obj, 0)); ++ } ++ ++ // Is the previous value null? ++ beq(pre_val, R0, done); ++ delayed()->nop(); ++ ++ // Can we store original value in the thread's buffer? ++ // Is index == 0? ++ // (The index field is typed as size_t.) ++ ++ ld(tmp, index); ++ beq(tmp, R0, runtime); ++ delayed()->nop(); ++ ++ daddiu(tmp, tmp, -1 * wordSize); ++ sd(tmp, index); ++ ld(AT, buffer); ++ daddu(tmp, tmp, AT); ++ ++ // Record the previous value ++ sd(pre_val, tmp, 0); ++ beq(R0, R0, done); ++ delayed()->nop(); ++ ++ bind(runtime); ++ // save the live input values ++ if (tosca_live) push(V0); ++ ++ if (obj != noreg && obj != V0) push(obj); ++ ++ if (pre_val != V0) push(pre_val); ++ ++ // Calling the runtime using the regular call_VM_leaf mechanism generates ++ // code (generated by InterpreterMacroAssember::call_VM_leaf_base) ++ // that checks that the *(fp+frame::interpreter_frame_last_sp) == NULL. ++ // ++ // If we care generating the pre-barrier without a frame (e.g. in the ++ // intrinsified Reference.get() routine) then fp might be pointing to ++ // the caller frame and so this check will most likely fail at runtime. ++ // ++ // Expanding the call directly bypasses the generation of the check. ++ // So when we do not have have a full interpreter frame on the stack ++ // expand_call should be passed true. ++ ++ ++ if (expand_call) { ++ assert(pre_val != A1, "smashed arg"); ++ if (thread != A1) move(A1, thread); ++ if (pre_val != A0) move(A0, pre_val); ++ MacroAssembler::call_VM_leaf_base(CAST_FROM_FN_PTR(address, SharedRuntime::g1_wb_pre), 2); ++ } else { ++ call_VM_leaf(CAST_FROM_FN_PTR(address, SharedRuntime::g1_wb_pre), pre_val, thread); ++ } ++ ++ // save the live input values ++ if (pre_val != V0) ++ pop(pre_val); ++ ++ if (obj != noreg && obj != V0) ++ pop(obj); ++ ++ if(tosca_live) pop(V0); ++ ++ bind(done); ++} ++ ++void MacroAssembler::g1_write_barrier_post(Register store_addr, ++ Register new_val, ++ Register thread, ++ Register tmp, ++ Register tmp2) { ++ assert(tmp != AT, "must be"); ++ assert(tmp2 != AT, "must be"); ++ assert(thread == TREG, "must be"); ++ ++ Address queue_index(thread, in_bytes(JavaThread::dirty_card_queue_offset() + ++ PtrQueue::byte_offset_of_index())); ++ Address buffer(thread, in_bytes(JavaThread::dirty_card_queue_offset() + ++ PtrQueue::byte_offset_of_buf())); ++ ++ BarrierSet* bs = Universe::heap()->barrier_set(); ++ CardTableModRefBS* ct = (CardTableModRefBS*)bs; ++ assert(sizeof(*ct->byte_map_base) == sizeof(jbyte), "adjust this code"); ++ ++ Label done; ++ Label runtime; ++ ++ // Does store cross heap regions? ++ xorr(AT, store_addr, new_val); ++ dsrl(AT, AT, HeapRegion::LogOfHRGrainBytes); ++ beq(AT, R0, done); ++ delayed()->nop(); ++ ++ ++ // crosses regions, storing NULL? ++ beq(new_val, R0, done); ++ delayed()->nop(); ++ ++ // storing region crossing non-NULL, is card already dirty? ++ const Register card_addr = tmp; ++ const Register cardtable = tmp2; ++ ++ move(card_addr, store_addr); ++ dsrl(card_addr, card_addr, CardTableModRefBS::card_shift); ++ // Do not use ExternalAddress to load 'byte_map_base', since 'byte_map_base' is NOT ++ // a valid address and therefore is not properly handled by the relocation code. ++ set64(cardtable, (intptr_t)ct->byte_map_base); ++ daddu(card_addr, card_addr, cardtable); ++ ++ lb(AT, card_addr, 0); ++ daddiu(AT, AT, -1 * (int)G1SATBCardTableModRefBS::g1_young_card_val()); ++ beq(AT, R0, done); ++ delayed()->nop(); ++ ++ sync(); ++ lb(AT, card_addr, 0); ++ daddiu(AT, AT, -1 * (int)(int)CardTableModRefBS::dirty_card_val()); ++ beq(AT, R0, done); ++ delayed()->nop(); ++ ++ ++ // storing a region crossing, non-NULL oop, card is clean. ++ // dirty card and log. ++ move(AT, (int)CardTableModRefBS::dirty_card_val()); ++ sb(AT, card_addr, 0); ++ ++ lw(AT, queue_index); ++ beq(AT, R0, runtime); ++ delayed()->nop(); ++ daddiu(AT, AT, -1 * wordSize); ++ sw(AT, queue_index); ++ ld(tmp2, buffer); ++ ld(AT, queue_index); ++ daddu(tmp2, tmp2, AT); ++ sd(card_addr, tmp2, 0); ++ beq(R0, R0, done); ++ delayed()->nop(); ++ ++ bind(runtime); ++ // save the live input values ++ push(store_addr); ++ push(new_val); ++ call_VM_leaf(CAST_FROM_FN_PTR(address, SharedRuntime::g1_wb_post), card_addr, TREG); ++ pop(new_val); ++ pop(store_addr); ++ ++ bind(done); ++} ++ ++#endif // INCLUDE_ALL_GCS ++////////////////////////////////////////////////////////////////////////////////// ++ ++ ++void MacroAssembler::store_check(Register obj) { ++ // Does a store check for the oop in register obj. The content of ++ // register obj is destroyed afterwards. ++ store_check_part_1(obj); ++ store_check_part_2(obj); ++} ++ ++void MacroAssembler::store_check(Register obj, Address dst) { ++ store_check(obj); ++} ++ ++ ++// split the store check operation so that other instructions can be scheduled inbetween ++void MacroAssembler::store_check_part_1(Register obj) { ++ BarrierSet* bs = Universe::heap()->barrier_set(); ++ assert(bs->kind() == BarrierSet::CardTableModRef, "Wrong barrier set kind"); ++ dsrl(obj, obj, CardTableModRefBS::card_shift); ++} ++ ++void MacroAssembler::store_check_part_2(Register obj) { ++ BarrierSet* bs = Universe::heap()->barrier_set(); ++ assert(bs->kind() == BarrierSet::CardTableModRef, "Wrong barrier set kind"); ++ CardTableModRefBS* ct = (CardTableModRefBS*)bs; ++ assert(sizeof(*ct->byte_map_base) == sizeof(jbyte), "adjust this code"); ++ ++ set64(AT, (long)ct->byte_map_base); ++ daddu(AT, AT, obj); ++ if (UseConcMarkSweepGC) sync(); ++ sb(R0, AT, 0); ++} ++ ++// Defines obj, preserves var_size_in_bytes, okay for t2 == var_size_in_bytes. ++void MacroAssembler::tlab_allocate(Register obj, Register var_size_in_bytes, int con_size_in_bytes, ++ Register t1, Register t2, Label& slow_case) { ++ assert_different_registers(obj, var_size_in_bytes, t1, t2, AT); ++ ++ Register end = t2; ++#ifndef OPT_THREAD ++ Register thread = t1; ++ get_thread(thread); ++#else ++ Register thread = TREG; ++#endif ++ verify_tlab(t1, t2);//blows t1&t2 ++ ++ ld_ptr(obj, thread, in_bytes(JavaThread::tlab_top_offset())); ++ ++ if (var_size_in_bytes == NOREG) { ++ set64(AT, con_size_in_bytes); ++ addu(end, obj, AT); ++ } else { ++ addu(end, obj, var_size_in_bytes); ++ } ++ ++ ld_ptr(AT, thread, in_bytes(JavaThread::tlab_end_offset())); ++ sltu(AT, AT, end); ++ bne_far(AT, R0, slow_case); ++ delayed()->nop(); ++ ++ ++ // update the tlab top pointer ++ st_ptr(end, thread, in_bytes(JavaThread::tlab_top_offset())); ++ ++ verify_tlab(t1, t2); ++} ++ ++// Defines obj, preserves var_size_in_bytes ++void MacroAssembler::eden_allocate(Register obj, Register var_size_in_bytes, int con_size_in_bytes, ++ Register t1, Register t2, Label& slow_case) { ++ assert_different_registers(obj, var_size_in_bytes, t1, AT); ++ if (CMSIncrementalMode || !Universe::heap()->supports_inline_contig_alloc()) { ++ // No allocation in the shared eden. ++ b_far(slow_case); ++ delayed()->nop(); ++ } else { ++ ++ Address heap_top(t1); ++ li(t1, (long)Universe::heap()->top_addr()); ++ ld_ptr(obj, heap_top); ++ ++ Register end = t2; ++ Label retry; ++ ++ bind(retry); ++ if (var_size_in_bytes == NOREG) { ++ set64(AT, con_size_in_bytes); ++ addu(end, obj, AT); ++ } else { ++ addu(end, obj, var_size_in_bytes); ++ } ++ // if end < obj then we wrapped around => object too long => slow case ++ sltu(AT, end, obj); ++ bne_far(AT, R0, slow_case); ++ delayed()->nop(); ++ ++ li(AT, (long)Universe::heap()->end_addr()); ++ ld_ptr(AT, AT, 0); ++ sltu(AT, AT, end); ++ bne_far(AT, R0, slow_case); ++ delayed()->nop(); ++ // Compare obj with the top addr, and if still equal, store the new top addr in ++ // end at the address of the top addr pointer. Sets ZF if was equal, and clears ++ // it otherwise. Use lock prefix for atomicity on MPs. ++ //if (os::is_MP()) { ++ // sync(); ++ //} ++ ++ // if someone beat us on the allocation, try again, otherwise continue ++ cmpxchg(end, heap_top, obj); ++ beq_far(AT, R0, retry); ++ delayed()->nop(); ++ } ++} ++ ++// C2 doesn't invoke this one. ++void MacroAssembler::tlab_refill(Label& retry, Label& try_eden, Label& slow_case) { ++ Register top = T0; ++ Register t1 = T1; ++ Register t2 = T9; ++ Register t3 = T3; ++ Register thread_reg = T8; ++ assert_different_registers(top, thread_reg, t1, t2, /* preserve: */ T2, A4); ++ Label do_refill, discard_tlab; ++ ++ if (CMSIncrementalMode || !Universe::heap()->supports_inline_contig_alloc()) { ++ // No allocation in the shared eden. ++ b(slow_case); ++ delayed()->nop(); ++ } ++ ++ get_thread(thread_reg); ++ ++ ld_ptr(top, thread_reg, in_bytes(JavaThread::tlab_top_offset())); ++ ld_ptr(t1, thread_reg, in_bytes(JavaThread::tlab_end_offset())); ++ ++ // calculate amount of free space ++ subu(t1, t1, top); ++ shr(t1, LogHeapWordSize); ++ ++ // Retain tlab and allocate object in shared space if ++ // the amount free in the tlab is too large to discard. ++ ld_ptr(t2, thread_reg, in_bytes(JavaThread::tlab_refill_waste_limit_offset())); ++ slt(AT, t2, t1); ++ beq(AT, R0, discard_tlab); ++ delayed()->nop(); ++ ++ // Retain ++ li(AT, ThreadLocalAllocBuffer::refill_waste_limit_increment()); ++ addu(t2, t2, AT); ++ st_ptr(t2, thread_reg, in_bytes(JavaThread::tlab_refill_waste_limit_offset())); ++ ++ if (TLABStats) { ++ // increment number of slow_allocations ++ lw(AT, thread_reg, in_bytes(JavaThread::tlab_slow_allocations_offset())); ++ addiu(AT, AT, 1); ++ sw(AT, thread_reg, in_bytes(JavaThread::tlab_slow_allocations_offset())); ++ } ++ b(try_eden); ++ delayed()->nop(); ++ ++ bind(discard_tlab); ++ if (TLABStats) { ++ // increment number of refills ++ lw(AT, thread_reg, in_bytes(JavaThread::tlab_number_of_refills_offset())); ++ addiu(AT, AT, 1); ++ sw(AT, thread_reg, in_bytes(JavaThread::tlab_number_of_refills_offset())); ++ // accumulate wastage -- t1 is amount free in tlab ++ lw(AT, thread_reg, in_bytes(JavaThread::tlab_fast_refill_waste_offset())); ++ addu(AT, AT, t1); ++ sw(AT, thread_reg, in_bytes(JavaThread::tlab_fast_refill_waste_offset())); ++ } ++ ++ // if tlab is currently allocated (top or end != null) then ++ // fill [top, end + alignment_reserve) with array object ++ beq(top, R0, do_refill); ++ delayed()->nop(); ++ ++ // set up the mark word ++ li(AT, (long)markOopDesc::prototype()->copy_set_hash(0x2)); ++ st_ptr(AT, top, oopDesc::mark_offset_in_bytes()); ++ ++ // set the length to the remaining space ++ addiu(t1, t1, - typeArrayOopDesc::header_size(T_INT)); ++ addiu(t1, t1, ThreadLocalAllocBuffer::alignment_reserve()); ++ shl(t1, log2_intptr(HeapWordSize/sizeof(jint))); ++ sw(t1, top, arrayOopDesc::length_offset_in_bytes()); ++ ++ // set klass to intArrayKlass ++ li(AT, (intptr_t)Universe::intArrayKlassObj_addr()); ++ ld_ptr(t1, AT, 0); ++ //st_ptr(t1, top, oopDesc::klass_offset_in_bytes()); ++ store_klass(top, t1); ++ ++ ld_ptr(t1, thread_reg, in_bytes(JavaThread::tlab_start_offset())); ++ subu(t1, top, t1); ++ incr_allocated_bytes(thread_reg, t1, 0); ++ ++ // refill the tlab with an eden allocation ++ bind(do_refill); ++ ld_ptr(t1, thread_reg, in_bytes(JavaThread::tlab_size_offset())); ++ shl(t1, LogHeapWordSize); ++ // add object_size ?? ++ eden_allocate(top, t1, 0, t2, t3, slow_case); ++ ++ // Check that t1 was preserved in eden_allocate. ++#ifdef ASSERT ++ if (UseTLAB) { ++ Label ok; ++ assert_different_registers(thread_reg, t1); ++ ld_ptr(AT, thread_reg, in_bytes(JavaThread::tlab_size_offset())); ++ shl(AT, LogHeapWordSize); ++ beq(AT, t1, ok); ++ delayed()->nop(); ++ stop("assert(t1 != tlab size)"); ++ should_not_reach_here(); ++ ++ bind(ok); ++ } ++#endif ++ st_ptr(top, thread_reg, in_bytes(JavaThread::tlab_start_offset())); ++ st_ptr(top, thread_reg, in_bytes(JavaThread::tlab_top_offset())); ++ addu(top, top, t1); ++ addiu(top, top, - ThreadLocalAllocBuffer::alignment_reserve_in_bytes()); ++ st_ptr(top, thread_reg, in_bytes(JavaThread::tlab_end_offset())); ++ verify_tlab(t1, t2); ++ b(retry); ++ delayed()->nop(); ++} ++ ++void MacroAssembler::incr_allocated_bytes(Register thread, ++ Register var_size_in_bytes, ++ int con_size_in_bytes, ++ Register t1) { ++ if (!thread->is_valid()) { ++#ifndef OPT_THREAD ++ assert(t1->is_valid(), "need temp reg"); ++ thread = t1; ++ get_thread(thread); ++#else ++ thread = TREG; ++#endif ++ } ++ ++ ld_ptr(AT, thread, in_bytes(JavaThread::allocated_bytes_offset())); ++ if (var_size_in_bytes->is_valid()) { ++ addu(AT, AT, var_size_in_bytes); ++ } else { ++ addiu(AT, AT, con_size_in_bytes); ++ } ++ st_ptr(AT, thread, in_bytes(JavaThread::allocated_bytes_offset())); ++} ++ ++static const double pi_4 = 0.7853981633974483; ++ ++// must get argument(a double) in F12/F13 ++//void MacroAssembler::trigfunc(char trig, bool preserve_cpu_regs, int num_fpu_regs_in_use) { ++//We need to preseve the register which maybe modified during the Call ++void MacroAssembler::trigfunc(char trig, int num_fpu_regs_in_use) { ++ // save all modified register here ++ // FIXME, in the disassembly of tirgfunc, only used V0, V1, T9, SP, RA, so we ony save V0, V1, T9 ++ pushad(); ++ // we should preserve the stack space before we call ++ addiu(SP, SP, -wordSize * 2); ++ switch (trig){ ++ case 's' : ++ call( CAST_FROM_FN_PTR(address, SharedRuntime::dsin), relocInfo::runtime_call_type ); ++ delayed()->nop(); ++ break; ++ case 'c': ++ call( CAST_FROM_FN_PTR(address, SharedRuntime::dcos), relocInfo::runtime_call_type ); ++ delayed()->nop(); ++ break; ++ case 't': ++ call( CAST_FROM_FN_PTR(address, SharedRuntime::dtan), relocInfo::runtime_call_type ); ++ delayed()->nop(); ++ break; ++ default:assert (false, "bad intrinsic"); ++ break; ++ ++ } ++ ++ addiu(SP, SP, wordSize * 2); ++ popad(); ++} ++ ++void MacroAssembler::li(Register rd, long imm) { ++ if (imm <= max_jint && imm >= min_jint) { ++ li32(rd, (int)imm); ++ } else if (julong(imm) <= 0xFFFFFFFF) { ++ assert_not_delayed(); ++ // lui sign-extends, so we can't use that. ++ ori(rd, R0, julong(imm) >> 16); ++ dsll(rd, rd, 16); ++ ori(rd, rd, split_low(imm)); ++ } else if ((imm > 0) && is_simm16(imm >> 32)) { ++ // A 48-bit address ++ li48(rd, imm); ++ } else { ++ li64(rd, imm); ++ } ++} ++ ++void MacroAssembler::li32(Register reg, int imm) { ++ if (is_simm16(imm)) { ++ addiu(reg, R0, imm); ++ } else { ++ lui(reg, split_low(imm >> 16)); ++ if (split_low(imm)) ++ ori(reg, reg, split_low(imm)); ++ } ++} ++ ++void MacroAssembler::set64(Register d, jlong value) { ++ assert_not_delayed(); ++ ++ int hi = (int)(value >> 32); ++ int lo = (int)(value & ~0); ++ ++ if (value == lo) { // 32-bit integer ++ if (is_simm16(value)) { ++ daddiu(d, R0, value); ++ } else { ++ lui(d, split_low(value >> 16)); ++ if (split_low(value)) { ++ ori(d, d, split_low(value)); ++ } ++ } ++ } else if (hi == 0) { // hardware zero-extends to upper 32 ++ ori(d, R0, julong(value) >> 16); ++ dsll(d, d, 16); ++ if (split_low(value)) { ++ ori(d, d, split_low(value)); ++ } ++ } else if ((value> 0) && is_simm16(value >> 32)) { // li48 ++ // 4 insts ++ li48(d, value); ++ } else { // li64 ++ // 6 insts ++ li64(d, value); ++ } ++} ++ ++ ++int MacroAssembler::insts_for_set64(jlong value) { ++ int hi = (int)(value >> 32); ++ int lo = (int)(value & ~0); ++ ++ int count = 0; ++ ++ if (value == lo) { // 32-bit integer ++ if (is_simm16(value)) { ++ //daddiu(d, R0, value); ++ count++; ++ } else { ++ //lui(d, split_low(value >> 16)); ++ count++; ++ if (split_low(value)) { ++ //ori(d, d, split_low(value)); ++ count++; ++ } ++ } ++ } else if (hi == 0) { // hardware zero-extends to upper 32 ++ //ori(d, R0, julong(value) >> 16); ++ //dsll(d, d, 16); ++ count += 2; ++ if (split_low(value)) { ++ //ori(d, d, split_low(value)); ++ count++; ++ } ++ } else if ((value> 0) && is_simm16(value >> 32)) { // li48 ++ // 4 insts ++ //li48(d, value); ++ count += 4; ++ } else { // li64 ++ // 6 insts ++ //li64(d, value); ++ count += 6; ++ } ++ ++ return count; ++} ++ ++void MacroAssembler::patchable_set48(Register d, jlong value) { ++ assert_not_delayed(); ++ ++ int hi = (int)(value >> 32); ++ int lo = (int)(value & ~0); ++ ++ int count = 0; ++ ++ if (value == lo) { // 32-bit integer ++ if (is_simm16(value)) { ++ daddiu(d, R0, value); ++ count += 1; ++ } else { ++ lui(d, split_low(value >> 16)); ++ count += 1; ++ if (split_low(value)) { ++ ori(d, d, split_low(value)); ++ count += 1; ++ } ++ } ++ } else if (hi == 0) { // hardware zero-extends to upper 32 ++ ori(d, R0, julong(value) >> 16); ++ dsll(d, d, 16); ++ count += 2; ++ if (split_low(value)) { ++ ori(d, d, split_low(value)); ++ count += 1; ++ } ++ } else if ((value> 0) && is_simm16(value >> 32)) { // li48 ++ // 4 insts ++ li48(d, value); ++ count += 4; ++ } else { // li64 ++ tty->print_cr("value = 0x%lx", value); ++ guarantee(false, "Not supported yet !"); ++ } ++ ++ while (count < 4) { ++ nop(); ++ count++; ++ } ++} ++ ++void MacroAssembler::patchable_set32(Register d, jlong value) { ++ assert_not_delayed(); ++ ++ int hi = (int)(value >> 32); ++ int lo = (int)(value & ~0); ++ ++ int count = 0; ++ ++ if (value == lo) { // 32-bit integer ++ if (is_simm16(value)) { ++ daddiu(d, R0, value); ++ count += 1; ++ } else { ++ lui(d, split_low(value >> 16)); ++ count += 1; ++ if (split_low(value)) { ++ ori(d, d, split_low(value)); ++ count += 1; ++ } ++ } ++ } else if (hi == 0) { // hardware zero-extends to upper 32 ++ ori(d, R0, julong(value) >> 16); ++ dsll(d, d, 16); ++ count += 2; ++ if (split_low(value)) { ++ ori(d, d, split_low(value)); ++ count += 1; ++ } ++ } else { ++ tty->print_cr("value = 0x%lx", value); ++ guarantee(false, "Not supported yet !"); ++ } ++ ++ while (count < 3) { ++ nop(); ++ count++; ++ } ++} ++ ++void MacroAssembler::patchable_call32(Register d, jlong value) { ++ assert_not_delayed(); ++ ++ int hi = (int)(value >> 32); ++ int lo = (int)(value & ~0); ++ ++ int count = 0; ++ ++ if (value == lo) { // 32-bit integer ++ if (is_simm16(value)) { ++ daddiu(d, R0, value); ++ count += 1; ++ } else { ++ lui(d, split_low(value >> 16)); ++ count += 1; ++ if (split_low(value)) { ++ ori(d, d, split_low(value)); ++ count += 1; ++ } ++ } ++ } else { ++ tty->print_cr("value = 0x%lx", value); ++ guarantee(false, "Not supported yet !"); ++ } ++ ++ while (count < 2) { ++ nop(); ++ count++; ++ } ++} ++ ++void MacroAssembler::set_narrow_klass(Register dst, Klass* k) { ++ assert(UseCompressedClassPointers, "should only be used for compressed header"); ++ assert(oop_recorder() != NULL, "this assembler needs an OopRecorder"); ++ ++ int klass_index = oop_recorder()->find_index(k); ++ RelocationHolder rspec = metadata_Relocation::spec(klass_index); ++ long narrowKlass = (long)Klass::encode_klass(k); ++ ++ relocate(rspec, Assembler::narrow_oop_operand); ++ patchable_set48(dst, narrowKlass); ++} ++ ++ ++void MacroAssembler::set_narrow_oop(Register dst, jobject obj) { ++ assert(UseCompressedOops, "should only be used for compressed header"); ++ assert(oop_recorder() != NULL, "this assembler needs an OopRecorder"); ++ ++ int oop_index = oop_recorder()->find_index(obj); ++ RelocationHolder rspec = oop_Relocation::spec(oop_index); ++ ++ relocate(rspec, Assembler::narrow_oop_operand); ++ patchable_set48(dst, oop_index); ++} ++ ++void MacroAssembler::li64(Register rd, long imm) { ++ assert_not_delayed(); ++ lui(rd, split_low(imm >> 48)); ++ ori(rd, rd, split_low(imm >> 32)); ++ dsll(rd, rd, 16); ++ ori(rd, rd, split_low(imm >> 16)); ++ dsll(rd, rd, 16); ++ ori(rd, rd, split_low(imm)); ++} ++ ++void MacroAssembler::li48(Register rd, long imm) { ++ assert_not_delayed(); ++ assert(is_simm16(imm >> 32), "Not a 48-bit address"); ++ lui(rd, imm >> 32); ++ ori(rd, rd, split_low(imm >> 16)); ++ dsll(rd, rd, 16); ++ ori(rd, rd, split_low(imm)); ++} ++ ++void MacroAssembler::verify_oop(Register reg, const char* s) { ++ if (!VerifyOops) return; ++ const char * b = NULL; ++ stringStream ss; ++ ss.print("verify_oop: %s: %s", reg->name(), s); ++ b = code_string(ss.as_string()); ++ pushad(); ++ move(A1, reg); ++ li(A0, (long)b); ++ li(AT, (long)StubRoutines::verify_oop_subroutine_entry_address()); ++ ld(T9, AT, 0); ++ jalr(T9); ++ delayed()->nop(); ++ popad(); ++} ++ ++ ++void MacroAssembler::verify_oop_addr(Address addr, const char* s) { ++ if (!VerifyOops) { ++ nop(); ++ return; ++ } ++ // Pass register number to verify_oop_subroutine ++ const char * b = NULL; ++ stringStream ss; ++ ss.print("verify_oop_addr: %s", s); ++ b = code_string(ss.as_string()); ++ ++ addiu(SP, SP, - 7 * wordSize); ++ st_ptr(T0, SP, 6 * wordSize); ++ st_ptr(T1, SP, 5 * wordSize); ++ st_ptr(RA, SP, 4 * wordSize); ++ st_ptr(A0, SP, 3 * wordSize); ++ st_ptr(A1, SP, 2 * wordSize); ++ st_ptr(AT, SP, 1 * wordSize); ++ st_ptr(T9, SP, 0); ++ ++ // addr may contain sp so we will have to adjust it based on the ++ // pushes that we just did. ++ if (addr.uses(SP)) { ++ lea(A1, addr); ++ ld_ptr(A1, Address(A1, 7 * wordSize)); ++ } else { ++ ld_ptr(A1, addr); ++ } ++ li(A0, (long)b); ++ // call indirectly to solve generation ordering problem ++ li(AT, (long)StubRoutines::verify_oop_subroutine_entry_address()); ++ ld_ptr(T9, AT, 0); ++ jalr(T9); ++ delayed()->nop(); ++ ld_ptr(T0, SP, 6* wordSize); ++ ld_ptr(T1, SP, 5* wordSize); ++ ld_ptr(RA, SP, 4* wordSize); ++ ld_ptr(A0, SP, 3* wordSize); ++ ld_ptr(A1, SP, 2* wordSize); ++ ld_ptr(AT, SP, 1* wordSize); ++ ld_ptr(T9, SP, 0* wordSize); ++ addiu(SP, SP, 7 * wordSize); ++} ++ ++// used registers : T0, T1 ++void MacroAssembler::verify_oop_subroutine() { ++ // RA: ra ++ // A0: char* error message ++ // A1: oop object to verify ++ ++ Label exit, error; ++ // increment counter ++ li(T0, (long)StubRoutines::verify_oop_count_addr()); ++ lw(AT, T0, 0); ++ daddiu(AT, AT, 1); ++ sw(AT, T0, 0); ++ ++ // make sure object is 'reasonable' ++ beq(A1, R0, exit); // if obj is NULL it is ok ++ delayed()->nop(); ++ ++ // Check if the oop is in the right area of memory ++ // const int oop_mask = Universe::verify_oop_mask(); ++ // const int oop_bits = Universe::verify_oop_bits(); ++ const uintptr_t oop_mask = Universe::verify_oop_mask(); ++ const uintptr_t oop_bits = Universe::verify_oop_bits(); ++ li(AT, oop_mask); ++ andr(T0, A1, AT); ++ li(AT, oop_bits); ++ bne(T0, AT, error); ++ delayed()->nop(); ++ ++ // make sure klass is 'reasonable' ++ // add for compressedoops ++ reinit_heapbase(); ++ // add for compressedoops ++ load_klass(T0, A1); ++ beq(T0, R0, error); // if klass is NULL it is broken ++ delayed()->nop(); ++ // return if everything seems ok ++ bind(exit); ++ ++ jr(RA); ++ delayed()->nop(); ++ ++ // handle errors ++ bind(error); ++ pushad(); ++ call(CAST_FROM_FN_PTR(address, MacroAssembler::debug), relocInfo::runtime_call_type); ++ delayed()->nop(); ++ popad(); ++ jr(RA); ++ delayed()->nop(); ++} ++ ++void MacroAssembler::verify_tlab(Register t1, Register t2) { ++#ifdef ASSERT ++ assert_different_registers(t1, t2, AT); ++ if (UseTLAB && VerifyOops) { ++ Label next, ok; ++ ++ get_thread(t1); ++ ++ ld_ptr(t2, t1, in_bytes(JavaThread::tlab_top_offset())); ++ ld_ptr(AT, t1, in_bytes(JavaThread::tlab_start_offset())); ++ sltu(AT, t2, AT); ++ beq(AT, R0, next); ++ delayed()->nop(); ++ ++ stop("assert(top >= start)"); ++ ++ bind(next); ++ ld_ptr(AT, t1, in_bytes(JavaThread::tlab_end_offset())); ++ sltu(AT, AT, t2); ++ beq(AT, R0, ok); ++ delayed()->nop(); ++ ++ stop("assert(top <= end)"); ++ ++ bind(ok); ++ ++ } ++#endif ++} ++ ++RegisterOrConstant MacroAssembler::delayed_value_impl(intptr_t* delayed_value_addr, ++ Register tmp, ++ int offset) { ++ intptr_t value = *delayed_value_addr; ++ if (value != 0) ++ return RegisterOrConstant(value + offset); ++ AddressLiteral a(delayed_value_addr); ++ // load indirectly to solve generation ordering problem ++ //movptr(tmp, ExternalAddress((address) delayed_value_addr)); ++ //ld(tmp, a); ++ if (offset != 0) ++ daddiu(tmp,tmp, offset); ++ ++ return RegisterOrConstant(tmp); ++} ++ ++void MacroAssembler::hswap(Register reg) { ++ //short ++ //andi(reg, reg, 0xffff); ++ srl(AT, reg, 8); ++ sll(reg, reg, 24); ++ sra(reg, reg, 16); ++ orr(reg, reg, AT); ++} ++ ++void MacroAssembler::huswap(Register reg) { ++ dsrl(AT, reg, 8); ++ dsll(reg, reg, 24); ++ dsrl(reg, reg, 16); ++ orr(reg, reg, AT); ++ andi(reg, reg, 0xffff); ++} ++ ++// something funny to do this will only one more register AT ++// 32 bits ++void MacroAssembler::swap(Register reg) { ++ srl(AT, reg, 8); ++ sll(reg, reg, 24); ++ orr(reg, reg, AT); ++ //reg : 4 1 2 3 ++ srl(AT, AT, 16); ++ xorr(AT, AT, reg); ++ andi(AT, AT, 0xff); ++ //AT : 0 0 0 1^3); ++ xorr(reg, reg, AT); ++ //reg : 4 1 2 1 ++ sll(AT, AT, 16); ++ xorr(reg, reg, AT); ++ //reg : 4 3 2 1 ++} ++ ++// do 32-bit CAS using MIPS64 lld/scd ++// ++// cas_int should only compare 32-bits of the memory value. ++// However, lld/scd will do 64-bit operation, which violates the intention of cas_int. ++// To simulate a 32-bit atomic operation, the value loaded with LLD should be split into ++// tow halves, and only the low-32 bits is compared. If equals, the low-32 bits of newval, ++// plus the high-32 bits or memory value, are stored togethor with SCD. ++// ++//Example: ++// ++// double d = 3.1415926; ++// System.err.println("hello" + d); ++// ++// sun.misc.FloatingDecimal$1.() ++// | ++// `- java.util.concurrent.atomic.AtomicInteger::compareAndSet() ++// ++// 38 cas_int [a7a7|J] [a0|I] [a6|I] ++// a0: 0xffffffffe8ea9f63 pc: 0x55647f3354 ++// a6: 0x4ab325aa ++// ++//again: ++// 0x00000055647f3c5c: lld at, 0x0(a7) ; 64-bit load, "0xe8ea9f63" ++// ++// 0x00000055647f3c60: sll t9, at, 0 ; t9: low-32 bits (sign extended) ++// 0x00000055647f3c64: dsrl32 t8, at, 0 ; t8: high-32 bits ++// 0x00000055647f3c68: dsll32 t8, t8, 0 ++// 0x00000055647f3c6c: bne t9, a0, 0x00000055647f3c9c ; goto nequal ++// 0x00000055647f3c70: sll zero, zero, 0 ++// ++// 0x00000055647f3c74: ori v1, zero, 0xffffffff ; v1: low-32 bits of newval (sign unextended) ++// 0x00000055647f3c78: dsll v1, v1, 16 ; v1 = a6 & 0xFFFFFFFF; ++// 0x00000055647f3c7c: ori v1, v1, 0xffffffff ++// 0x00000055647f3c80: and v1, a6, v1 ++// 0x00000055647f3c84: or at, t8, v1 ++// 0x00000055647f3c88: scd at, 0x0(a7) ++// 0x00000055647f3c8c: beq at, zero, 0x00000055647f3c5c ; goto again ++// 0x00000055647f3c90: sll zero, zero, 0 ++// 0x00000055647f3c94: beq zero, zero, 0x00000055647f45ac ; goto done ++// 0x00000055647f3c98: sll zero, zero, 0 ++//nequal: ++// 0x00000055647f45a4: daddu a0, t9, zero ++// 0x00000055647f45a8: daddu at, zero, zero ++//done: ++// ++ ++void MacroAssembler::cmpxchg32(Register x_reg, Address dest, Register c_reg) { ++ // MIPS64 can use ll/sc for 32-bit atomic memory access ++ Label done, again, nequal; ++ ++ bind(again); ++ ++ if (UseSyncLevel >= 10000 || UseSyncLevel == 1000 || UseSyncLevel == 4000) sync(); ++ ll(AT, dest); ++ bne(AT, c_reg, nequal); ++ delayed()->nop(); ++ ++ move(AT, x_reg); ++ sc(AT, dest); ++ beq(AT, R0, again); ++ delayed()->nop(); ++ b(done); ++ delayed()->nop(); ++ ++ // not xchged ++ bind(nequal); ++ sync(); ++ move(c_reg, AT); ++ move(AT, R0); ++ ++ bind(done); ++} ++ ++void MacroAssembler::cmpxchg(Register x_reg, Address dest, Register c_reg) { ++ Label done, again, nequal; ++ ++ bind(again); ++ if (UseSyncLevel >= 10000 || UseSyncLevel == 1000 || UseSyncLevel == 4000) sync(); ++ lld(AT, dest); ++ bne(AT, c_reg, nequal); ++ delayed()->nop(); ++ ++ move(AT, x_reg); ++ scd(AT, dest); ++ beq(AT, R0, again); ++ delayed()->nop(); ++ b(done); ++ delayed()->nop(); ++ ++ // not xchged ++ bind(nequal); ++ sync(); ++ move(c_reg, AT); ++ move(AT, R0); ++ ++ bind(done); ++} ++ ++void MacroAssembler::cmpxchg8(Register x_regLo, Register x_regHi, Address dest, Register c_regLo, Register c_regHi) { ++ Label done, again, nequal; ++ ++ Register x_reg = x_regLo; ++ dsll32(x_regHi, x_regHi, 0); ++ dsll32(x_regLo, x_regLo, 0); ++ dsrl32(x_regLo, x_regLo, 0); ++ orr(x_reg, x_regLo, x_regHi); ++ ++ Register c_reg = c_regLo; ++ dsll32(c_regHi, c_regHi, 0); ++ dsll32(c_regLo, c_regLo, 0); ++ dsrl32(c_regLo, c_regLo, 0); ++ orr(c_reg, c_regLo, c_regHi); ++ ++ bind(again); ++ ++ if (UseSyncLevel >= 10000 || UseSyncLevel == 1000 || UseSyncLevel == 4000) sync(); ++ lld(AT, dest); ++ bne(AT, c_reg, nequal); ++ delayed()->nop(); ++ ++ //move(AT, x_reg); ++ daddu(AT, x_reg, R0); ++ scd(AT, dest); ++ beq(AT, R0, again); ++ delayed()->nop(); ++ b(done); ++ delayed()->nop(); ++ ++ // not xchged ++ bind(nequal); ++ sync(); ++ //move(c_reg, AT); ++ //move(AT, R0); ++ daddu(c_reg, AT, R0); ++ daddu(AT, R0, R0); ++ bind(done); ++} ++ ++// be sure the three register is different ++void MacroAssembler::rem_s(FloatRegister fd, FloatRegister fs, FloatRegister ft, FloatRegister tmp) { ++ assert_different_registers(tmp, fs, ft); ++ div_s(tmp, fs, ft); ++ trunc_l_s(tmp, tmp); ++ cvt_s_l(tmp, tmp); ++ mul_s(tmp, tmp, ft); ++ sub_s(fd, fs, tmp); ++} ++ ++// be sure the three register is different ++void MacroAssembler::rem_d(FloatRegister fd, FloatRegister fs, FloatRegister ft, FloatRegister tmp) { ++ assert_different_registers(tmp, fs, ft); ++ div_d(tmp, fs, ft); ++ trunc_l_d(tmp, tmp); ++ cvt_d_l(tmp, tmp); ++ mul_d(tmp, tmp, ft); ++ sub_d(fd, fs, tmp); ++} ++ ++// Fast_Lock and Fast_Unlock used by C2 ++ ++// Because the transitions from emitted code to the runtime ++// monitorenter/exit helper stubs are so slow it's critical that ++// we inline both the stack-locking fast-path and the inflated fast path. ++// ++// See also: cmpFastLock and cmpFastUnlock. ++// ++// What follows is a specialized inline transliteration of the code ++// in slow_enter() and slow_exit(). If we're concerned about I$ bloat ++// another option would be to emit TrySlowEnter and TrySlowExit methods ++// at startup-time. These methods would accept arguments as ++// (Obj, Self, box, Scratch) and return success-failure ++// indications in the icc.ZFlag. Fast_Lock and Fast_Unlock would simply ++// marshal the arguments and emit calls to TrySlowEnter and TrySlowExit. ++// In practice, however, the # of lock sites is bounded and is usually small. ++// Besides the call overhead, TrySlowEnter and TrySlowExit might suffer ++// if the processor uses simple bimodal branch predictors keyed by EIP ++// Since the helper routines would be called from multiple synchronization ++// sites. ++// ++// An even better approach would be write "MonitorEnter()" and "MonitorExit()" ++// in java - using j.u.c and unsafe - and just bind the lock and unlock sites ++// to those specialized methods. That'd give us a mostly platform-independent ++// implementation that the JITs could optimize and inline at their pleasure. ++// Done correctly, the only time we'd need to cross to native could would be ++// to park() or unpark() threads. We'd also need a few more unsafe operators ++// to (a) prevent compiler-JIT reordering of non-volatile accesses, and ++// (b) explicit barriers or fence operations. ++// ++// TODO: ++// ++// * Arrange for C2 to pass "Self" into Fast_Lock and Fast_Unlock in one of the registers (scr). ++// This avoids manifesting the Self pointer in the Fast_Lock and Fast_Unlock terminals. ++// Given TLAB allocation, Self is usually manifested in a register, so passing it into ++// the lock operators would typically be faster than reifying Self. ++// ++// * Ideally I'd define the primitives as: ++// fast_lock (nax Obj, nax box, tmp, nax scr) where box, tmp and scr are KILLED. ++// fast_unlock (nax Obj, box, nax tmp) where box and tmp are KILLED ++// Unfortunately ADLC bugs prevent us from expressing the ideal form. ++// Instead, we're stuck with a rather awkward and brittle register assignments below. ++// Furthermore the register assignments are overconstrained, possibly resulting in ++// sub-optimal code near the synchronization site. ++// ++// * Eliminate the sp-proximity tests and just use "== Self" tests instead. ++// Alternately, use a better sp-proximity test. ++// ++// * Currently ObjectMonitor._Owner can hold either an sp value or a (THREAD *) value. ++// Either one is sufficient to uniquely identify a thread. ++// TODO: eliminate use of sp in _owner and use get_thread(tr) instead. ++// ++// * Intrinsify notify() and notifyAll() for the common cases where the ++// object is locked by the calling thread but the waitlist is empty. ++// avoid the expensive JNI call to JVM_Notify() and JVM_NotifyAll(). ++// ++// * use jccb and jmpb instead of jcc and jmp to improve code density. ++// But beware of excessive branch density on AMD Opterons. ++// ++// * Both Fast_Lock and Fast_Unlock set the ICC.ZF to indicate success ++// or failure of the fast-path. If the fast-path fails then we pass ++// control to the slow-path, typically in C. In Fast_Lock and ++// Fast_Unlock we often branch to DONE_LABEL, just to find that C2 ++// will emit a conditional branch immediately after the node. ++// So we have branches to branches and lots of ICC.ZF games. ++// Instead, it might be better to have C2 pass a "FailureLabel" ++// into Fast_Lock and Fast_Unlock. In the case of success, control ++// will drop through the node. ICC.ZF is undefined at exit. ++// In the case of failure, the node will branch directly to the ++// FailureLabel ++ ++ ++// obj: object to lock ++// box: on-stack box address (displaced header location) - KILLED ++// tmp: tmp -- KILLED ++// scr: tmp -- KILLED ++void MacroAssembler::fast_lock(Register objReg, Register boxReg, Register tmpReg, Register scrReg) { ++ ++ // Ensure the register assignents are disjoint ++ guarantee (objReg != boxReg, "") ; ++ guarantee (objReg != tmpReg, "") ; ++ guarantee (objReg != scrReg, "") ; ++ guarantee (boxReg != tmpReg, "") ; ++ guarantee (boxReg != scrReg, "") ; ++ ++ ++ block_comment("FastLock"); ++ if (PrintBiasedLockingStatistics) { ++ push(tmpReg); ++ atomic_inc32((address)BiasedLocking::total_entry_count_addr(), 1, AT, tmpReg); ++ pop(tmpReg); ++ } ++ ++ if (EmitSync & 1) { ++ move(AT, 0x0); ++ return; ++ } else ++ if (EmitSync & 2) { ++ Label DONE_LABEL ; ++ if (UseBiasedLocking) { ++ // Note: tmpReg maps to the swap_reg argument and scrReg to the tmp_reg argument. ++ biased_locking_enter(boxReg, objReg, tmpReg, scrReg, false, DONE_LABEL, NULL); ++ } ++ ++ ld(tmpReg, Address(objReg, 0)) ; // fetch markword ++ ori(tmpReg, tmpReg, 0x1); ++ sd(tmpReg, Address(boxReg, 0)); // Anticipate successful CAS ++ ++ cmpxchg(boxReg, Address(objReg, 0), tmpReg); // Updates tmpReg ++ bne(AT, R0, DONE_LABEL); ++ delayed()->nop(); ++ ++ // Recursive locking ++ dsubu(tmpReg, tmpReg, SP); ++ li(AT, (7 - os::vm_page_size() )); ++ andr(tmpReg, tmpReg, AT); ++ sd(tmpReg, Address(boxReg, 0)); ++ bind(DONE_LABEL) ; ++ } else { ++ // Possible cases that we'll encounter in fast_lock ++ // ------------------------------------------------ ++ // * Inflated ++ // -- unlocked ++ // -- Locked ++ // = by self ++ // = by other ++ // * biased ++ // -- by Self ++ // -- by other ++ // * neutral ++ // * stack-locked ++ // -- by self ++ // = sp-proximity test hits ++ // = sp-proximity test generates false-negative ++ // -- by other ++ // ++ ++ Label IsInflated, DONE_LABEL, PopDone ; ++ ++ // TODO: optimize away redundant LDs of obj->mark and improve the markword triage ++ // order to reduce the number of conditional branches in the most common cases. ++ // Beware -- there's a subtle invariant that fetch of the markword ++ // at [FETCH], below, will never observe a biased encoding (*101b). ++ // If this invariant is not held we risk exclusion (safety) failure. ++ if (UseBiasedLocking && !UseOptoBiasInlining) { ++ biased_locking_enter(boxReg, objReg, tmpReg, scrReg, false, DONE_LABEL, NULL); ++ } ++ ++ ld(tmpReg, Address(objReg, 0)) ; //Fetch the markword of the object. ++ andi(AT, tmpReg, markOopDesc::monitor_value); ++ bne(AT, R0, IsInflated); // inflated vs stack-locked|neutral|bias ++ delayed()->nop(); ++ ++ // Attempt stack-locking ... ++ ori (tmpReg, tmpReg, markOopDesc::unlocked_value); ++ sd(tmpReg, Address(boxReg, 0)); // Anticipate successful CAS ++ //if (os::is_MP()) { ++ // sync(); ++ //} ++ ++ cmpxchg(boxReg, Address(objReg, 0), tmpReg); // Updates tmpReg ++ //AT == 1: unlocked ++ ++ if (PrintBiasedLockingStatistics) { ++ Label L; ++ beq(AT, R0, L); ++ delayed()->nop(); ++ push(T0); ++ push(T1); ++ atomic_inc32((address)BiasedLocking::fast_path_entry_count_addr(), 1, T0, T1); ++ pop(T1); ++ pop(T0); ++ bind(L); ++ } ++ bne(AT, R0, DONE_LABEL); ++ delayed()->nop(); ++ ++ // Recursive locking ++ // The object is stack-locked: markword contains stack pointer to BasicLock. ++ // Locked by current thread if difference with current SP is less than one page. ++ dsubu(tmpReg, tmpReg, SP); ++ li(AT, 7 - os::vm_page_size() ); ++ andr(tmpReg, tmpReg, AT); ++ sd(tmpReg, Address(boxReg, 0)); ++ if (PrintBiasedLockingStatistics) { ++ Label L; ++ // tmpReg == 0 => BiasedLocking::_fast_path_entry_count++ ++ bne(tmpReg, R0, L); ++ delayed()->nop(); ++ push(T0); ++ push(T1); ++ atomic_inc32((address)BiasedLocking::fast_path_entry_count_addr(), 1, T0, T1); ++ pop(T1); ++ pop(T0); ++ bind(L); ++ } ++ sltiu(AT, tmpReg, 1); // AT = (tmpReg == 0) ? 1 : 0 ++ ++ b(DONE_LABEL) ; ++ delayed()->nop(); ++ ++ bind(IsInflated) ; ++ // The object's monitor m is unlocked iff m->owner == NULL, ++ // otherwise m->owner may contain a thread or a stack address. ++ ++ // TODO: someday avoid the ST-before-CAS penalty by ++ // relocating (deferring) the following ST. ++ // We should also think about trying a CAS without having ++ // fetched _owner. If the CAS is successful we may ++ // avoid an RTO->RTS upgrade on the $line. ++ // Without cast to int32_t a movptr will destroy r10 which is typically obj ++ li(AT, (int32_t)intptr_t(markOopDesc::unused_mark())); ++ sd(AT, Address(boxReg, 0)); ++ ++ move(boxReg, tmpReg) ; ++ ld(tmpReg, Address(tmpReg, ObjectMonitor::owner_offset_in_bytes()-2)) ; ++ // if (m->owner != 0) => AT = 0, goto slow path. ++ move(AT, R0); ++ bne(tmpReg, R0, DONE_LABEL); ++ delayed()->nop(); ++ ++#ifndef OPT_THREAD ++ get_thread (TREG) ; ++#endif ++ // It's inflated and appears unlocked ++ //if (os::is_MP()) { ++ // sync(); ++ //} ++ cmpxchg(TREG, Address(boxReg, ObjectMonitor::owner_offset_in_bytes()-2), tmpReg) ; ++ // Intentional fall-through into DONE_LABEL ... ++ ++ ++ // DONE_LABEL is a hot target - we'd really like to place it at the ++ // start of cache line by padding with NOPs. ++ // See the AMD and Intel software optimization manuals for the ++ // most efficient "long" NOP encodings. ++ // Unfortunately none of our alignment mechanisms suffice. ++ bind(DONE_LABEL); ++ ++ // At DONE_LABEL the AT is set as follows ... ++ // Fast_Unlock uses the same protocol. ++ // AT == 1 -> Success ++ // AT == 0 -> Failure - force control through the slow-path ++ ++ // Avoid branch-to-branch on AMD processors ++ // This appears to be superstition. ++ if (EmitSync & 32) nop() ; ++ ++ } ++} ++ ++// obj: object to unlock ++// box: box address (displaced header location), killed. ++// tmp: killed tmp; cannot be obj nor box. ++// ++// Some commentary on balanced locking: ++// ++// Fast_Lock and Fast_Unlock are emitted only for provably balanced lock sites. ++// Methods that don't have provably balanced locking are forced to run in the ++// interpreter - such methods won't be compiled to use fast_lock and fast_unlock. ++// The interpreter provides two properties: ++// I1: At return-time the interpreter automatically and quietly unlocks any ++// objects acquired the current activation (frame). Recall that the ++// interpreter maintains an on-stack list of locks currently held by ++// a frame. ++// I2: If a method attempts to unlock an object that is not held by the ++// the frame the interpreter throws IMSX. ++// ++// Lets say A(), which has provably balanced locking, acquires O and then calls B(). ++// B() doesn't have provably balanced locking so it runs in the interpreter. ++// Control returns to A() and A() unlocks O. By I1 and I2, above, we know that O ++// is still locked by A(). ++// ++// The only other source of unbalanced locking would be JNI. The "Java Native Interface: ++// Programmer's Guide and Specification" claims that an object locked by jni_monitorenter ++// should not be unlocked by "normal" java-level locking and vice-versa. The specification ++// doesn't specify what will occur if a program engages in such mixed-mode locking, however. ++ ++void MacroAssembler::fast_unlock(Register objReg, Register boxReg, Register tmpReg) { ++ ++ guarantee (objReg != boxReg, "") ; ++ guarantee (objReg != tmpReg, "") ; ++ guarantee (boxReg != tmpReg, "") ; ++ ++ block_comment("FastUnlock"); ++ ++ ++ if (EmitSync & 4) { ++ // Disable - inhibit all inlining. Force control through the slow-path ++ move(AT, 0x0); ++ return; ++ } else ++ if (EmitSync & 8) { ++ Label DONE_LABEL ; ++ if (UseBiasedLocking) { ++ biased_locking_exit(objReg, tmpReg, DONE_LABEL); ++ } ++ // classic stack-locking code ... ++ ld(tmpReg, Address(boxReg, 0)) ; ++ beq(tmpReg, R0, DONE_LABEL) ; ++ move(AT, 0x1); // delay slot ++ ++ cmpxchg(tmpReg, Address(objReg, 0), boxReg); ++ bind(DONE_LABEL); ++ } else { ++ Label DONE_LABEL, Stacked, CheckSucc, Inflated ; ++ ++ // Critically, the biased locking test must have precedence over ++ // and appear before the (box->dhw == 0) recursive stack-lock test. ++ if (UseBiasedLocking && !UseOptoBiasInlining) { ++ biased_locking_exit(objReg, tmpReg, DONE_LABEL); ++ } ++ ++ ld(AT, Address(boxReg, 0)) ; // Examine the displaced header ++ beq(AT, R0, DONE_LABEL) ; // 0 indicates recursive stack-lock ++ delayed()->daddiu(AT, R0, 0x1); ++ ++ ld(tmpReg, Address(objReg, 0)) ; // Examine the object's markword ++ andi(AT, tmpReg, markOopDesc::monitor_value) ; // Inflated? ++ beq(AT, R0, Stacked) ; // Inflated? ++ delayed()->nop(); ++ ++ bind(Inflated) ; ++ // It's inflated. ++ // Despite our balanced locking property we still check that m->_owner == Self ++ // as java routines or native JNI code called by this thread might ++ // have released the lock. ++ // Refer to the comments in synchronizer.cpp for how we might encode extra ++ // state in _succ so we can avoid fetching EntryList|cxq. ++ // ++ // I'd like to add more cases in fast_lock() and fast_unlock() -- ++ // such as recursive enter and exit -- but we have to be wary of ++ // I$ bloat, T$ effects and BP$ effects. ++ // ++ // If there's no contention try a 1-0 exit. That is, exit without ++ // a costly MEMBAR or CAS. See synchronizer.cpp for details on how ++ // we detect and recover from the race that the 1-0 exit admits. ++ // ++ // Conceptually Fast_Unlock() must execute a STST|LDST "release" barrier ++ // before it STs null into _owner, releasing the lock. Updates ++ // to data protected by the critical section must be visible before ++ // we drop the lock (and thus before any other thread could acquire ++ // the lock and observe the fields protected by the lock). ++#ifndef OPT_THREAD ++ get_thread (TREG) ; ++#endif ++ ++ // It's inflated ++ ld(boxReg, Address (tmpReg, ObjectMonitor::owner_offset_in_bytes()-2)) ; ++ xorr(boxReg, boxReg, TREG); ++ ++ ld(AT, Address (tmpReg, ObjectMonitor::recursions_offset_in_bytes()-2)) ; ++ orr(boxReg, boxReg, AT); ++ ++ move(AT, R0); ++ bne(boxReg, R0, DONE_LABEL); ++ delayed()->nop(); ++ ++ ld(boxReg, Address (tmpReg, ObjectMonitor::cxq_offset_in_bytes()-2)) ; ++ ld(AT, Address (tmpReg, ObjectMonitor::EntryList_offset_in_bytes()-2)) ; ++ orr(boxReg, boxReg, AT); ++ ++ move(AT, R0); ++ bne(boxReg, R0, DONE_LABEL); ++ delayed()->nop(); ++ ++ sync(); ++ sd(R0, Address (tmpReg, ObjectMonitor::owner_offset_in_bytes()-2)) ; ++ move(AT, 0x1); ++ b(DONE_LABEL); ++ delayed()->nop(); ++ ++ bind (Stacked); ++ ld(tmpReg, Address(boxReg, 0)) ; ++ //if (os::is_MP()) { sync(); } ++ cmpxchg(tmpReg, Address(objReg, 0), boxReg); ++ ++ if (EmitSync & 65536) { ++ bind (CheckSucc); ++ } ++ ++ bind(DONE_LABEL); ++ ++ // Avoid branch to branch on AMD processors ++ if (EmitSync & 32768) { nop() ; } ++ } ++} ++ ++void MacroAssembler::align(int modulus) { ++ while (offset() % modulus != 0) nop(); ++} ++ ++ ++void MacroAssembler::verify_FPU(int stack_depth, const char* s) { ++ //Unimplemented(); ++} ++ ++Register caller_saved_registers[] = {AT, V0, V1, A0, A1, A2, A3, A4, A5, A6, A7, T0, T1, T2, T3, T8, T9, GP, RA, FP}; ++Register caller_saved_registers_except_v0[] = {AT, V1, A0, A1, A2, A3, A4, A5, A6, A7, T0, T1, T2, T3, T8, T9, GP, RA, FP}; ++ ++//In MIPS64, F0~23 are all caller-saved registers ++FloatRegister caller_saved_fpu_registers[] = {F0, F12, F13}; ++ ++// We preserve all caller-saved register ++void MacroAssembler::pushad(){ ++ int i; ++ ++ // Fixed-point registers ++ int len = sizeof(caller_saved_registers) / sizeof(caller_saved_registers[0]); ++ daddiu(SP, SP, -1 * len * wordSize); ++ for (i = 0; i < len; i++) ++ { ++ sd(caller_saved_registers[i], SP, (len - i - 1) * wordSize); ++ } ++ ++ // Floating-point registers ++ len = sizeof(caller_saved_fpu_registers) / sizeof(caller_saved_fpu_registers[0]); ++ daddiu(SP, SP, -1 * len * wordSize); ++ for (i = 0; i < len; i++) ++ { ++ sdc1(caller_saved_fpu_registers[i], SP, (len - i - 1) * wordSize); ++ } ++}; ++ ++void MacroAssembler::popad(){ ++ int i; ++ ++ // Floating-point registers ++ int len = sizeof(caller_saved_fpu_registers) / sizeof(caller_saved_fpu_registers[0]); ++ for (i = 0; i < len; i++) ++ { ++ ldc1(caller_saved_fpu_registers[i], SP, (len - i - 1) * wordSize); ++ } ++ daddiu(SP, SP, len * wordSize); ++ ++ // Fixed-point registers ++ len = sizeof(caller_saved_registers) / sizeof(caller_saved_registers[0]); ++ for (i = 0; i < len; i++) ++ { ++ ld(caller_saved_registers[i], SP, (len - i - 1) * wordSize); ++ } ++ daddiu(SP, SP, len * wordSize); ++}; ++ ++// We preserve all caller-saved register except V0 ++void MacroAssembler::pushad_except_v0() { ++ int i; ++ ++ // Fixed-point registers ++ int len = sizeof(caller_saved_registers_except_v0) / sizeof(caller_saved_registers_except_v0[0]); ++ daddiu(SP, SP, -1 * len * wordSize); ++ for (i = 0; i < len; i++) { ++ sd(caller_saved_registers_except_v0[i], SP, (len - i - 1) * wordSize); ++ } ++ ++ // Floating-point registers ++ len = sizeof(caller_saved_fpu_registers) / sizeof(caller_saved_fpu_registers[0]); ++ daddiu(SP, SP, -1 * len * wordSize); ++ for (i = 0; i < len; i++) { ++ sdc1(caller_saved_fpu_registers[i], SP, (len - i - 1) * wordSize); ++ } ++} ++ ++void MacroAssembler::popad_except_v0() { ++ int i; ++ ++ // Floating-point registers ++ int len = sizeof(caller_saved_fpu_registers) / sizeof(caller_saved_fpu_registers[0]); ++ for (i = 0; i < len; i++) { ++ ldc1(caller_saved_fpu_registers[i], SP, (len - i - 1) * wordSize); ++ } ++ daddiu(SP, SP, len * wordSize); ++ ++ // Fixed-point registers ++ len = sizeof(caller_saved_registers_except_v0) / sizeof(caller_saved_registers_except_v0[0]); ++ for (i = 0; i < len; i++) { ++ ld(caller_saved_registers_except_v0[i], SP, (len - i - 1) * wordSize); ++ } ++ daddiu(SP, SP, len * wordSize); ++} ++ ++void MacroAssembler::push2(Register reg1, Register reg2) { ++ daddiu(SP, SP, -16); ++ sd(reg1, SP, 8); ++ sd(reg2, SP, 0); ++} ++ ++void MacroAssembler::pop2(Register reg1, Register reg2) { ++ ld(reg1, SP, 8); ++ ld(reg2, SP, 0); ++ daddiu(SP, SP, 16); ++} ++ ++// for UseCompressedOops Option ++void MacroAssembler::load_klass(Register dst, Register src) { ++ if(UseCompressedClassPointers){ ++ lwu(dst, Address(src, oopDesc::klass_offset_in_bytes())); ++ decode_klass_not_null(dst); ++ } else ++ ld(dst, src, oopDesc::klass_offset_in_bytes()); ++} ++ ++void MacroAssembler::store_klass(Register dst, Register src) { ++ if(UseCompressedClassPointers){ ++ encode_klass_not_null(src); ++ sw(src, dst, oopDesc::klass_offset_in_bytes()); ++ } else { ++ sd(src, dst, oopDesc::klass_offset_in_bytes()); ++ } ++} ++ ++void MacroAssembler::load_prototype_header(Register dst, Register src) { ++ load_klass(dst, src); ++ ld(dst, Address(dst, Klass::prototype_header_offset())); ++} ++ ++void MacroAssembler::store_klass_gap(Register dst, Register src) { ++ if (UseCompressedClassPointers) { ++ sw(src, dst, oopDesc::klass_gap_offset_in_bytes()); ++ } ++} ++ ++void MacroAssembler::load_heap_oop(Register dst, Address src) { ++ if(UseCompressedOops){ ++ lwu(dst, src); ++ decode_heap_oop(dst); ++ } else { ++ ld(dst, src); ++ } ++} ++ ++void MacroAssembler::store_heap_oop(Address dst, Register src){ ++ if(UseCompressedOops){ ++ assert(!dst.uses(src), "not enough registers"); ++ encode_heap_oop(src); ++ sw(src, dst); ++ } else { ++ sd(src, dst); ++ } ++} ++ ++void MacroAssembler::store_heap_oop_null(Address dst){ ++ if(UseCompressedOops){ ++ sw(R0, dst); ++ } else { ++ sd(R0, dst); ++ } ++} ++ ++#ifdef ASSERT ++void MacroAssembler::verify_heapbase(const char* msg) { ++ assert (UseCompressedOops || UseCompressedClassPointers, "should be compressed"); ++ assert (Universe::heap() != NULL, "java heap should be initialized"); ++} ++#endif ++ ++ ++// Algorithm must match oop.inline.hpp encode_heap_oop. ++void MacroAssembler::encode_heap_oop(Register r) { ++#ifdef ASSERT ++ verify_heapbase("MacroAssembler::encode_heap_oop:heap base corrupted?"); ++#endif ++ verify_oop(r, "broken oop in encode_heap_oop"); ++ if (Universe::narrow_oop_base() == NULL) { ++ if (Universe::narrow_oop_shift() != 0) { ++ assert (LogMinObjAlignmentInBytes == Universe::narrow_oop_shift(), "decode alg wrong"); ++ shr(r, LogMinObjAlignmentInBytes); ++ } ++ return; ++ } ++ ++ movz(r, S5_heapbase, r); ++ dsubu(r, r, S5_heapbase); ++ if (Universe::narrow_oop_shift() != 0) { ++ assert (LogMinObjAlignmentInBytes == Universe::narrow_oop_shift(), "decode alg wrong"); ++ shr(r, LogMinObjAlignmentInBytes); ++ } ++} ++ ++void MacroAssembler::encode_heap_oop(Register dst, Register src) { ++#ifdef ASSERT ++ verify_heapbase("MacroAssembler::encode_heap_oop:heap base corrupted?"); ++#endif ++ verify_oop(src, "broken oop in encode_heap_oop"); ++ if (Universe::narrow_oop_base() == NULL) { ++ if (Universe::narrow_oop_shift() != 0) { ++ assert (LogMinObjAlignmentInBytes == Universe::narrow_oop_shift(), "decode alg wrong"); ++ dsrl(dst, src, LogMinObjAlignmentInBytes); ++ } else { ++ if (dst != src) move(dst, src); ++ } ++ } else { ++ if (dst == src) { ++ movz(dst, S5_heapbase, dst); ++ dsubu(dst, dst, S5_heapbase); ++ if (Universe::narrow_oop_shift() != 0) { ++ assert (LogMinObjAlignmentInBytes == Universe::narrow_oop_shift(), "decode alg wrong"); ++ shr(dst, LogMinObjAlignmentInBytes); ++ } ++ } else { ++ dsubu(dst, src, S5_heapbase); ++ if (Universe::narrow_oop_shift() != 0) { ++ assert (LogMinObjAlignmentInBytes == Universe::narrow_oop_shift(), "decode alg wrong"); ++ shr(dst, LogMinObjAlignmentInBytes); ++ } ++ movz(dst, R0, src); ++ } ++ } ++} ++ ++void MacroAssembler::encode_heap_oop_not_null(Register r) { ++ assert (UseCompressedOops, "should be compressed"); ++#ifdef ASSERT ++ if (CheckCompressedOops) { ++ Label ok; ++ bne(r, R0, ok); ++ delayed()->nop(); ++ stop("null oop passed to encode_heap_oop_not_null"); ++ bind(ok); ++ } ++#endif ++ verify_oop(r, "broken oop in encode_heap_oop_not_null"); ++ if (Universe::narrow_oop_base() != NULL) { ++ dsubu(r, r, S5_heapbase); ++ } ++ if (Universe::narrow_oop_shift() != 0) { ++ assert (LogMinObjAlignmentInBytes == Universe::narrow_oop_shift(), "decode alg wrong"); ++ shr(r, LogMinObjAlignmentInBytes); ++ } ++ ++} ++ ++void MacroAssembler::encode_heap_oop_not_null(Register dst, Register src) { ++ assert (UseCompressedOops, "should be compressed"); ++#ifdef ASSERT ++ if (CheckCompressedOops) { ++ Label ok; ++ bne(src, R0, ok); ++ delayed()->nop(); ++ stop("null oop passed to encode_heap_oop_not_null2"); ++ bind(ok); ++ } ++#endif ++ verify_oop(src, "broken oop in encode_heap_oop_not_null2"); ++ ++ if (Universe::narrow_oop_base() != NULL) { ++ dsubu(dst, src, S5_heapbase); ++ if (Universe::narrow_oop_shift() != 0) { ++ assert (LogMinObjAlignmentInBytes == Universe::narrow_oop_shift(), "decode alg wrong"); ++ shr(dst, LogMinObjAlignmentInBytes); ++ } ++ } else { ++ if (Universe::narrow_oop_shift() != 0) { ++ assert (LogMinObjAlignmentInBytes == Universe::narrow_oop_shift(), "decode alg wrong"); ++ dsrl(dst, src, LogMinObjAlignmentInBytes); ++ } else { ++ if (dst != src) move(dst, src); ++ } ++ } ++} ++ ++void MacroAssembler::decode_heap_oop(Register r) { ++#ifdef ASSERT ++ verify_heapbase("MacroAssembler::decode_heap_oop corrupted?"); ++#endif ++ if (Universe::narrow_oop_base() == NULL) { ++ if (Universe::narrow_oop_shift() != 0) { ++ assert (LogMinObjAlignmentInBytes == Universe::narrow_oop_shift(), "decode alg wrong"); ++ shl(r, LogMinObjAlignmentInBytes); ++ } ++ } else { ++ move(AT, r); ++ if (Universe::narrow_oop_shift() != 0) { ++ assert (LogMinObjAlignmentInBytes == Universe::narrow_oop_shift(), "decode alg wrong"); ++ shl(r, LogMinObjAlignmentInBytes); ++ } ++ daddu(r, r, S5_heapbase); ++ movz(r, R0, AT); ++ } ++ verify_oop(r, "broken oop in decode_heap_oop"); ++} ++ ++void MacroAssembler::decode_heap_oop(Register dst, Register src) { ++#ifdef ASSERT ++ verify_heapbase("MacroAssembler::decode_heap_oop corrupted?"); ++#endif ++ if (Universe::narrow_oop_base() == NULL) { ++ if (Universe::narrow_oop_shift() != 0) { ++ assert (LogMinObjAlignmentInBytes == Universe::narrow_oop_shift(), "decode alg wrong"); ++ if (dst != src) nop(); // DON'T DELETE THIS GUY. ++ dsll(dst, src, LogMinObjAlignmentInBytes); ++ } else { ++ if (dst != src) move(dst, src); ++ } ++ } else { ++ if (dst == src) { ++ move(AT, dst); ++ if (Universe::narrow_oop_shift() != 0) { ++ assert (LogMinObjAlignmentInBytes == Universe::narrow_oop_shift(), "decode alg wrong"); ++ shl(dst, LogMinObjAlignmentInBytes); ++ } ++ daddu(dst, dst, S5_heapbase); ++ movz(dst, R0, AT); ++ } else { ++ if (Universe::narrow_oop_shift() != 0) { ++ assert (LogMinObjAlignmentInBytes == Universe::narrow_oop_shift(), "decode alg wrong"); ++ dsll(dst, src, LogMinObjAlignmentInBytes); ++ daddu(dst, dst, S5_heapbase); ++ } else { ++ daddu(dst, src, S5_heapbase); ++ } ++ movz(dst, R0, src); ++ } ++ } ++ verify_oop(dst, "broken oop in decode_heap_oop"); ++} ++ ++void MacroAssembler::decode_heap_oop_not_null(Register r) { ++ // Note: it will change flags ++ assert (UseCompressedOops, "should only be used for compressed headers"); ++ assert (Universe::heap() != NULL, "java heap should be initialized"); ++ // Cannot assert, unverified entry point counts instructions (see .ad file) ++ // vtableStubs also counts instructions in pd_code_size_limit. ++ // Also do not verify_oop as this is called by verify_oop. ++ if (Universe::narrow_oop_shift() != 0) { ++ assert(LogMinObjAlignmentInBytes == Universe::narrow_oop_shift(), "decode alg wrong"); ++ shl(r, LogMinObjAlignmentInBytes); ++ if (Universe::narrow_oop_base() != NULL) { ++ daddu(r, r, S5_heapbase); ++ } ++ } else { ++ assert (Universe::narrow_oop_base() == NULL, "sanity"); ++ } ++} ++ ++void MacroAssembler::decode_heap_oop_not_null(Register dst, Register src) { ++ assert (UseCompressedOops, "should only be used for compressed headers"); ++ assert (Universe::heap() != NULL, "java heap should be initialized"); ++ ++ // Cannot assert, unverified entry point counts instructions (see .ad file) ++ // vtableStubs also counts instructions in pd_code_size_limit. ++ // Also do not verify_oop as this is called by verify_oop. ++ //lea(dst, Address(S5_heapbase, src, Address::times_8, 0)); ++ if (Universe::narrow_oop_shift() != 0) { ++ assert(LogMinObjAlignmentInBytes == Universe::narrow_oop_shift(), "decode alg wrong"); ++ if (LogMinObjAlignmentInBytes == Address::times_8) { ++ dsll(dst, src, LogMinObjAlignmentInBytes); ++ daddu(dst, dst, S5_heapbase); ++ } else { ++ dsll(dst, src, LogMinObjAlignmentInBytes); ++ if (Universe::narrow_oop_base() != NULL) { ++ daddu(dst, dst, S5_heapbase); ++ } ++ } ++ } else { ++ assert (Universe::narrow_oop_base() == NULL, "sanity"); ++ if (dst != src) { ++ move(dst, src); ++ } ++ } ++} ++ ++void MacroAssembler::encode_klass_not_null(Register r) { ++ if (Universe::narrow_klass_base() != NULL) { ++ assert(r != AT, "Encoding a klass in AT"); ++ set64(AT, (int64_t)Universe::narrow_klass_base()); ++ dsubu(r, r, AT); ++ } ++ if (Universe::narrow_klass_shift() != 0) { ++ assert (LogKlassAlignmentInBytes == Universe::narrow_klass_shift(), "decode alg wrong"); ++ shr(r, LogKlassAlignmentInBytes); ++ } ++} ++ ++void MacroAssembler::encode_klass_not_null(Register dst, Register src) { ++ if (dst == src) { ++ encode_klass_not_null(src); ++ } else { ++ if (Universe::narrow_klass_base() != NULL) { ++ set64(dst, (int64_t)Universe::narrow_klass_base()); ++ dsubu(dst, src, dst); ++ if (Universe::narrow_klass_shift() != 0) { ++ assert (LogKlassAlignmentInBytes == Universe::narrow_klass_shift(), "decode alg wrong"); ++ shr(dst, LogKlassAlignmentInBytes); ++ } ++ } else { ++ if (Universe::narrow_klass_shift() != 0) { ++ assert (LogKlassAlignmentInBytes == Universe::narrow_klass_shift(), "decode alg wrong"); ++ dsrl(dst, src, LogKlassAlignmentInBytes); ++ } else { ++ move(dst, src); ++ } ++ } ++ } ++} ++ ++// Function instr_size_for_decode_klass_not_null() counts the instructions ++// generated by decode_klass_not_null(register r) and reinit_heapbase(), ++// when (Universe::heap() != NULL). Hence, if the instructions they ++// generate change, then this method needs to be updated. ++int MacroAssembler::instr_size_for_decode_klass_not_null() { ++ assert (UseCompressedClassPointers, "only for compressed klass ptrs"); ++ if (Universe::narrow_klass_base() != NULL) { ++ // mov64 + addq + shlq? + mov64 (for reinit_heapbase()). ++ return (Universe::narrow_klass_shift() == 0 ? 4 * 9 : 4 * 10); ++ } else { ++ // longest load decode klass function, mov64, leaq ++ return (Universe::narrow_klass_shift() == 0 ? 4 * 0 : 4 * 1); ++ } ++} ++ ++void MacroAssembler::decode_klass_not_null(Register r) { ++ assert (UseCompressedClassPointers, "should only be used for compressed headers"); ++ assert(r != AT, "Decoding a klass in AT"); ++ // Cannot assert, unverified entry point counts instructions (see .ad file) ++ // vtableStubs also counts instructions in pd_code_size_limit. ++ // Also do not verify_oop as this is called by verify_oop. ++ if (Universe::narrow_klass_shift() != 0) { ++ assert(LogKlassAlignmentInBytes == Universe::narrow_klass_shift(), "decode alg wrong"); ++ shl(r, LogKlassAlignmentInBytes); ++ } ++ if (Universe::narrow_klass_base() != NULL) { ++ set64(AT, (int64_t)Universe::narrow_klass_base()); ++ daddu(r, r, AT); ++ //Not neccessary for MIPS at all. ++ //reinit_heapbase(); ++ } ++} ++ ++void MacroAssembler::decode_klass_not_null(Register dst, Register src) { ++ assert (UseCompressedClassPointers, "should only be used for compressed headers"); ++ ++ if (dst == src) { ++ decode_klass_not_null(dst); ++ } else { ++ // Cannot assert, unverified entry point counts instructions (see .ad file) ++ // vtableStubs also counts instructions in pd_code_size_limit. ++ // Also do not verify_oop as this is called by verify_oop. ++ set64(dst, (int64_t)Universe::narrow_klass_base()); ++ if (Universe::narrow_klass_shift() != 0) { ++ assert(LogKlassAlignmentInBytes == Universe::narrow_klass_shift(), "decode alg wrong"); ++ assert(LogKlassAlignmentInBytes == Address::times_8, "klass not aligned on 64bits?"); ++ dsll(AT, src, Address::times_8); ++ daddu(dst, dst, AT); ++ } else { ++ daddu(dst, src, dst); ++ } ++ } ++} ++ ++void MacroAssembler::incrementl(Register reg, int value) { ++ if (value == min_jint) { ++ move(AT, value); ++ addu32(reg, reg, AT); ++ return; ++ } ++ if (value < 0) { decrementl(reg, -value); return; } ++ if (value == 0) { ; return; } ++ ++ move(AT, value); ++ addu32(reg, reg, AT); ++} ++ ++void MacroAssembler::decrementl(Register reg, int value) { ++ if (value == min_jint) { ++ move(AT, value); ++ subu32(reg, reg, AT); ++ return; ++ } ++ if (value < 0) { incrementl(reg, -value); return; } ++ if (value == 0) { ; return; } ++ ++ move(AT, value); ++ subu32(reg, reg, AT); ++} ++ ++void MacroAssembler::reinit_heapbase() { ++ if (UseCompressedOops || UseCompressedClassPointers) { ++ if (Universe::heap() != NULL) { ++ if (Universe::narrow_oop_base() == NULL) { ++ move(S5_heapbase, R0); ++ } else { ++ set64(S5_heapbase, (int64_t)Universe::narrow_ptrs_base()); ++ } ++ } else { ++ set64(S5_heapbase, (intptr_t)Universe::narrow_ptrs_base_addr()); ++ ld(S5_heapbase, S5_heapbase, 0); ++ } ++ } ++} ++ ++void MacroAssembler::check_klass_subtype(Register sub_klass, ++ Register super_klass, ++ Register temp_reg, ++ Label& L_success) { ++//implement ind gen_subtype_check ++ Label L_failure; ++ check_klass_subtype_fast_path(sub_klass, super_klass, temp_reg, &L_success, &L_failure, NULL); ++ check_klass_subtype_slow_path(sub_klass, super_klass, temp_reg, noreg, &L_success, NULL); ++ bind(L_failure); ++} ++ ++SkipIfEqual::SkipIfEqual( ++ MacroAssembler* masm, const bool* flag_addr, bool value) { ++ _masm = masm; ++ _masm->li(AT, (address)flag_addr); ++ _masm->lb(AT, AT, 0); ++ _masm->addiu(AT, AT, -value); ++ _masm->beq(AT, R0, _label); ++ _masm->delayed()->nop(); ++} ++void MacroAssembler::check_klass_subtype_fast_path(Register sub_klass, ++ Register super_klass, ++ Register temp_reg, ++ Label* L_success, ++ Label* L_failure, ++ Label* L_slow_path, ++ RegisterOrConstant super_check_offset) { ++ assert_different_registers(sub_klass, super_klass, temp_reg); ++ bool must_load_sco = (super_check_offset.constant_or_zero() == -1); ++ if (super_check_offset.is_register()) { ++ assert_different_registers(sub_klass, super_klass, ++ super_check_offset.as_register()); ++ } else if (must_load_sco) { ++ assert(temp_reg != noreg, "supply either a temp or a register offset"); ++ } ++ ++ Label L_fallthrough; ++ int label_nulls = 0; ++ if (L_success == NULL) { L_success = &L_fallthrough; label_nulls++; } ++ if (L_failure == NULL) { L_failure = &L_fallthrough; label_nulls++; } ++ if (L_slow_path == NULL) { L_slow_path = &L_fallthrough; label_nulls++; } ++ assert(label_nulls <= 1, "at most one NULL in the batch"); ++ ++ int sc_offset = in_bytes(Klass::secondary_super_cache_offset()); ++ int sco_offset = in_bytes(Klass::super_check_offset_offset()); ++ // If the pointers are equal, we are done (e.g., String[] elements). ++ // This self-check enables sharing of secondary supertype arrays among ++ // non-primary types such as array-of-interface. Otherwise, each such ++ // type would need its own customized SSA. ++ // We move this check to the front of the fast path because many ++ // type checks are in fact trivially successful in this manner, ++ // so we get a nicely predicted branch right at the start of the check. ++ beq(sub_klass, super_klass, *L_success); ++ delayed()->nop(); ++ // Check the supertype display: ++ if (must_load_sco) { ++ lwu(temp_reg, super_klass, sco_offset); ++ super_check_offset = RegisterOrConstant(temp_reg); ++ } ++ daddu(AT, sub_klass, super_check_offset.register_or_noreg()); ++ ld(AT, AT, super_check_offset.constant_or_zero()); ++ ++ // This check has worked decisively for primary supers. ++ // Secondary supers are sought in the super_cache ('super_cache_addr'). ++ // (Secondary supers are interfaces and very deeply nested subtypes.) ++ // This works in the same check above because of a tricky aliasing ++ // between the super_cache and the primary super display elements. ++ // (The 'super_check_addr' can address either, as the case requires.) ++ // Note that the cache is updated below if it does not help us find ++ // what we need immediately. ++ // So if it was a primary super, we can just fail immediately. ++ // Otherwise, it's the slow path for us (no success at this point). ++ ++ if (super_check_offset.is_register()) { ++ beq(super_klass, AT, *L_success); ++ delayed()->nop(); ++ addiu(AT, super_check_offset.as_register(), -sc_offset); ++ if (L_failure == &L_fallthrough) { ++ beq(AT, R0, *L_slow_path); ++ delayed()->nop(); ++ } else { ++ bne_far(AT, R0, *L_failure); ++ delayed()->nop(); ++ b(*L_slow_path); ++ delayed()->nop(); ++ } ++ } else if (super_check_offset.as_constant() == sc_offset) { ++ // Need a slow path; fast failure is impossible. ++ if (L_slow_path == &L_fallthrough) { ++ beq(super_klass, AT, *L_success); ++ delayed()->nop(); ++ } else { ++ bne(super_klass, AT, *L_slow_path); ++ delayed()->nop(); ++ b(*L_success); ++ delayed()->nop(); ++ } ++ } else { ++ // No slow path; it's a fast decision. ++ if (L_failure == &L_fallthrough) { ++ beq(super_klass, AT, *L_success); ++ delayed()->nop(); ++ } else { ++ bne_far(super_klass, AT, *L_failure); ++ delayed()->nop(); ++ b(*L_success); ++ delayed()->nop(); ++ } ++ } ++ ++ bind(L_fallthrough); ++ ++} ++ ++ ++void MacroAssembler::check_klass_subtype_slow_path(Register sub_klass, ++ Register super_klass, ++ Register temp_reg, ++ Register temp2_reg, ++ Label* L_success, ++ Label* L_failure, ++ bool set_cond_codes) { ++ if (temp2_reg == noreg) ++ temp2_reg = TSR; ++ assert_different_registers(sub_klass, super_klass, temp_reg, temp2_reg); ++#define IS_A_TEMP(reg) ((reg) == temp_reg || (reg) == temp2_reg) ++ ++ Label L_fallthrough; ++ int label_nulls = 0; ++ if (L_success == NULL) { L_success = &L_fallthrough; label_nulls++; } ++ if (L_failure == NULL) { L_failure = &L_fallthrough; label_nulls++; } ++ assert(label_nulls <= 1, "at most one NULL in the batch"); ++ ++ // a couple of useful fields in sub_klass: ++ int ss_offset = in_bytes(Klass::secondary_supers_offset()); ++ int sc_offset = in_bytes(Klass::secondary_super_cache_offset()); ++ Address secondary_supers_addr(sub_klass, ss_offset); ++ Address super_cache_addr( sub_klass, sc_offset); ++ ++ // Do a linear scan of the secondary super-klass chain. ++ // This code is rarely used, so simplicity is a virtue here. ++ // The repne_scan instruction uses fixed registers, which we must spill. ++ // Don't worry too much about pre-existing connections with the input regs. ++ ++#ifndef PRODUCT ++ int* pst_counter = &SharedRuntime::_partial_subtype_ctr; ++ ExternalAddress pst_counter_addr((address) pst_counter); ++#endif //PRODUCT ++ ++ // We will consult the secondary-super array. ++ ld(temp_reg, secondary_supers_addr); ++ // Load the array length. ++ lw(temp2_reg, Address(temp_reg, Array::length_offset_in_bytes())); ++ // Skip to start of data. ++ daddiu(temp_reg, temp_reg, Array::base_offset_in_bytes()); ++ ++ // OpenJDK8 never compresses klass pointers in secondary-super array. ++ Label Loop, subtype; ++ bind(Loop); ++ beq(temp2_reg, R0, *L_failure); ++ delayed()->nop(); ++ ld(AT, temp_reg, 0); ++ beq(AT, super_klass, subtype); ++ delayed()->daddiu(temp_reg, temp_reg, 1 * wordSize); ++ b(Loop); ++ delayed()->daddiu(temp2_reg, temp2_reg, -1); ++ ++ bind(subtype); ++ sd(super_klass, super_cache_addr); ++ if (L_success != &L_fallthrough) { ++ b(*L_success); ++ delayed()->nop(); ++ } ++ ++ // Success. Cache the super we found and proceed in triumph. ++#undef IS_A_TEMP ++ ++ bind(L_fallthrough); ++} ++ ++void MacroAssembler::get_vm_result(Register oop_result, Register java_thread) { ++ ld(oop_result, Address(java_thread, JavaThread::vm_result_offset())); ++ sd(R0, Address(java_thread, JavaThread::vm_result_offset())); ++ verify_oop(oop_result, "broken oop in call_VM_base"); ++} ++ ++void MacroAssembler::get_vm_result_2(Register metadata_result, Register java_thread) { ++ ld(metadata_result, Address(java_thread, JavaThread::vm_result_2_offset())); ++ sd(R0, Address(java_thread, JavaThread::vm_result_2_offset())); ++} ++ ++Address MacroAssembler::argument_address(RegisterOrConstant arg_slot, ++ int extra_slot_offset) { ++ // cf. TemplateTable::prepare_invoke(), if (load_receiver). ++ int stackElementSize = Interpreter::stackElementSize; ++ int offset = Interpreter::expr_offset_in_bytes(extra_slot_offset+0); ++#ifdef ASSERT ++ int offset1 = Interpreter::expr_offset_in_bytes(extra_slot_offset+1); ++ assert(offset1 - offset == stackElementSize, "correct arithmetic"); ++#endif ++ Register scale_reg = NOREG; ++ Address::ScaleFactor scale_factor = Address::no_scale; ++ if (arg_slot.is_constant()) { ++ offset += arg_slot.as_constant() * stackElementSize; ++ } else { ++ scale_reg = arg_slot.as_register(); ++ scale_factor = Address::times_8; ++ } ++ // We don't push RA on stack in prepare_invoke. ++ // offset += wordSize; // return PC is on stack ++ if(scale_reg==NOREG) return Address(SP, offset); ++ else { ++ dsll(scale_reg, scale_reg, scale_factor); ++ daddu(scale_reg, SP, scale_reg); ++ return Address(scale_reg, offset); ++ } ++} ++ ++SkipIfEqual::~SkipIfEqual() { ++ _masm->bind(_label); ++} ++ ++void MacroAssembler::load_sized_value(Register dst, Address src, size_t size_in_bytes, bool is_signed, Register dst2) { ++ switch (size_in_bytes) { ++ case 8: ld(dst, src); break; ++ case 4: lw(dst, src); break; ++ case 2: is_signed ? lh(dst, src) : lhu(dst, src); break; ++ case 1: is_signed ? lb( dst, src) : lbu( dst, src); break; ++ default: ShouldNotReachHere(); ++ } ++} ++ ++void MacroAssembler::store_sized_value(Address dst, Register src, size_t size_in_bytes, Register src2) { ++ switch (size_in_bytes) { ++ case 8: sd(src, dst); break; ++ case 4: sw(src, dst); break; ++ case 2: sh(src, dst); break; ++ case 1: sb(src, dst); break; ++ default: ShouldNotReachHere(); ++ } ++} ++ ++// Look up the method for a megamorphic invokeinterface call. ++// The target method is determined by . ++// The receiver klass is in recv_klass. ++// On success, the result will be in method_result, and execution falls through. ++// On failure, execution transfers to the given label. ++void MacroAssembler::lookup_interface_method(Register recv_klass, ++ Register intf_klass, ++ RegisterOrConstant itable_index, ++ Register method_result, ++ Register scan_temp, ++ Label& L_no_such_interface, ++ bool return_method) { ++ assert_different_registers(recv_klass, intf_klass, scan_temp, AT); ++ assert_different_registers(method_result, intf_klass, scan_temp, AT); ++ assert(recv_klass != method_result || !return_method, ++ "recv_klass can be destroyed when method isn't needed"); ++ ++ assert(itable_index.is_constant() || itable_index.as_register() == method_result, ++ "caller must use same register for non-constant itable index as for method"); ++ ++ // Compute start of first itableOffsetEntry (which is at the end of the vtable) ++ int vtable_base = InstanceKlass::vtable_start_offset() * wordSize; ++ int itentry_off = itableMethodEntry::method_offset_in_bytes(); ++ int scan_step = itableOffsetEntry::size() * wordSize; ++ int vte_size = vtableEntry::size() * wordSize; ++ Address::ScaleFactor times_vte_scale = Address::times_ptr; ++ assert(vte_size == wordSize, "else adjust times_vte_scale"); ++ ++ lw(scan_temp, Address(recv_klass, InstanceKlass::vtable_length_offset() * wordSize)); ++ ++ // %%% Could store the aligned, prescaled offset in the klassoop. ++ dsll(scan_temp, scan_temp, times_vte_scale); ++ daddu(scan_temp, recv_klass, scan_temp); ++ daddiu(scan_temp, scan_temp, vtable_base); ++ if (HeapWordsPerLong > 1) { ++ // Round up to align_object_offset boundary ++ // see code for InstanceKlass::start_of_itable! ++ round_to(scan_temp, BytesPerLong); ++ } ++ ++ if (return_method) { ++ // Adjust recv_klass by scaled itable_index, so we can free itable_index. ++ assert(itableMethodEntry::size() * wordSize == wordSize, "adjust the scaling in the code below"); ++ if (itable_index.is_constant()) { ++ set64(AT, (int)itable_index.is_constant()); ++ dsll(AT, AT, (int)Address::times_ptr); ++ } else { ++ dsll(AT, itable_index.as_register(), (int)Address::times_ptr); ++ } ++ daddu(AT, AT, recv_klass); ++ daddiu(recv_klass, AT, itentry_off); ++ } ++ ++ Label search, found_method; ++ ++ for (int peel = 1; peel >= 0; peel--) { ++ ld(method_result, Address(scan_temp, itableOffsetEntry::interface_offset_in_bytes())); ++ ++ if (peel) { ++ beq(intf_klass, method_result, found_method); ++ delayed()->nop(); ++ } else { ++ bne(intf_klass, method_result, search); ++ delayed()->nop(); ++ // (invert the test to fall through to found_method...) ++ } ++ ++ if (!peel) break; ++ ++ bind(search); ++ ++ // Check that the previous entry is non-null. A null entry means that ++ // the receiver class doesn't implement the interface, and wasn't the ++ // same as when the caller was compiled. ++ beq(method_result, R0, L_no_such_interface); ++ delayed()->nop(); ++ daddiu(scan_temp, scan_temp, scan_step); ++ } ++ ++ bind(found_method); ++ ++ if (return_method) { ++ // Got a hit. ++ lw(scan_temp, Address(scan_temp, itableOffsetEntry::offset_offset_in_bytes())); ++ if (UseLEXT1) { ++ gsldx(method_result, recv_klass, scan_temp, 0); ++ } else { ++ daddu(AT, recv_klass, scan_temp); ++ ld(method_result, AT, 0); ++ } ++ } ++} ++ ++// virtual method calling ++void MacroAssembler::lookup_virtual_method(Register recv_klass, ++ RegisterOrConstant vtable_index, ++ Register method_result) { ++ Register tmp = GP; ++ push(tmp); ++ ++ if (vtable_index.is_constant()) { ++ assert_different_registers(recv_klass, method_result, tmp); ++ } else { ++ assert_different_registers(recv_klass, method_result, vtable_index.as_register(), tmp); ++ } ++ const int base = InstanceKlass::vtable_start_offset() * wordSize; ++ assert(vtableEntry::size() * wordSize == wordSize, "else adjust the scaling in the code below"); ++ if (vtable_index.is_constant()) { ++ set64(AT, vtable_index.as_constant()); ++ dsll(AT, AT, (int)Address::times_ptr); ++ } else { ++ dsll(AT, vtable_index.as_register(), (int)Address::times_ptr); ++ } ++ set64(tmp, base + vtableEntry::method_offset_in_bytes()); ++ daddu(tmp, tmp, AT); ++ daddu(tmp, tmp, recv_klass); ++ ld(method_result, tmp, 0); ++ ++ pop(tmp); ++} ++ ++void MacroAssembler::store_for_type_by_register(Register src_reg, Register tmp_reg, int disp, BasicType type, bool wide) { ++ switch (type) { ++ case T_LONG: ++ st_ptr(src_reg, tmp_reg, disp); ++ break; ++ case T_ARRAY: ++ case T_OBJECT: ++ if (UseCompressedOops && !wide) { ++ sw(src_reg, tmp_reg, disp); ++ } else { ++ st_ptr(src_reg, tmp_reg, disp); ++ } ++ break; ++ case T_ADDRESS: ++ st_ptr(src_reg, tmp_reg, disp); ++ break; ++ case T_INT: ++ sw(src_reg, tmp_reg, disp); ++ break; ++ case T_CHAR: ++ case T_SHORT: ++ sh(src_reg, tmp_reg, disp); ++ break; ++ case T_BYTE: ++ case T_BOOLEAN: ++ sb(src_reg, tmp_reg, disp); ++ break; ++ default: ++ ShouldNotReachHere(); ++ } ++} ++ ++void MacroAssembler::store_for_type(Register src_reg, Address addr, BasicType type, bool wide) { ++ Register tmp_reg = T9; ++ Register index_reg = addr.index(); ++ if (index_reg == NOREG) { ++ tmp_reg = NOREG; ++ } ++ ++ int scale = addr.scale(); ++ if (tmp_reg != NOREG && scale >= 0) { ++ dsll(tmp_reg, index_reg, scale); ++ } ++ ++ int disp = addr.disp(); ++ bool disp_is_simm16 = true; ++ if (!Assembler::is_simm16(disp)) { ++ disp_is_simm16 = false; ++ } ++ ++ Register base_reg = addr.base(); ++ if (tmp_reg != NOREG) { ++ assert_different_registers(tmp_reg, base_reg, index_reg); ++ } ++ ++ if (tmp_reg != NOREG) { ++ daddu(tmp_reg, base_reg, tmp_reg); ++ if (!disp_is_simm16) { ++ move(tmp_reg, disp); ++ daddu(tmp_reg, base_reg, tmp_reg); ++ } ++ store_for_type_by_register(src_reg, tmp_reg, disp_is_simm16 ? disp : 0, type, wide); ++ } else { ++ if (!disp_is_simm16) { ++ tmp_reg = T9; ++ assert_different_registers(tmp_reg, base_reg); ++ move(tmp_reg, disp); ++ daddu(tmp_reg, base_reg, tmp_reg); ++ } ++ store_for_type_by_register(src_reg, disp_is_simm16 ? base_reg : tmp_reg, disp_is_simm16 ? disp : 0, type, wide); ++ } ++} ++ ++void MacroAssembler::store_for_type_by_register(FloatRegister src_reg, Register tmp_reg, int disp, BasicType type) { ++ switch (type) { ++ case T_DOUBLE: ++ sdc1(src_reg, tmp_reg, disp); ++ break; ++ case T_FLOAT: ++ swc1(src_reg, tmp_reg, disp); ++ break; ++ default: ++ ShouldNotReachHere(); ++ } ++} ++ ++void MacroAssembler::store_for_type(FloatRegister src_reg, Address addr, BasicType type) { ++ Register tmp_reg = T9; ++ Register index_reg = addr.index(); ++ if (index_reg == NOREG) { ++ tmp_reg = NOREG; ++ } ++ ++ int scale = addr.scale(); ++ if (tmp_reg != NOREG && scale >= 0) { ++ dsll(tmp_reg, index_reg, scale); ++ } ++ ++ int disp = addr.disp(); ++ bool disp_is_simm16 = true; ++ if (!Assembler::is_simm16(disp)) { ++ disp_is_simm16 = false; ++ } ++ ++ Register base_reg = addr.base(); ++ if (tmp_reg != NOREG) { ++ assert_different_registers(tmp_reg, base_reg, index_reg); ++ } ++ ++ if (tmp_reg != NOREG) { ++ daddu(tmp_reg, base_reg, tmp_reg); ++ if (!disp_is_simm16) { ++ move(tmp_reg, disp); ++ daddu(tmp_reg, base_reg, tmp_reg); ++ } ++ store_for_type_by_register(src_reg, tmp_reg, disp_is_simm16 ? disp : 0, type); ++ } else { ++ if (!disp_is_simm16) { ++ tmp_reg = T9; ++ assert_different_registers(tmp_reg, base_reg); ++ move(tmp_reg, disp); ++ daddu(tmp_reg, base_reg, tmp_reg); ++ } ++ store_for_type_by_register(src_reg, disp_is_simm16 ? base_reg : tmp_reg, disp_is_simm16 ? disp : 0, type); ++ } ++} ++ ++void MacroAssembler::load_for_type_by_register(Register dst_reg, Register tmp_reg, int disp, BasicType type, bool wide) { ++ switch (type) { ++ case T_LONG: ++ ld_ptr(dst_reg, tmp_reg, disp); ++ break; ++ case T_ARRAY: ++ case T_OBJECT: ++ if (UseCompressedOops && !wide) { ++ lwu(dst_reg, tmp_reg, disp); ++ } else { ++ ld_ptr(dst_reg, tmp_reg, disp); ++ } ++ break; ++ case T_ADDRESS: ++ if (UseCompressedClassPointers && disp == oopDesc::klass_offset_in_bytes()) { ++ lwu(dst_reg, tmp_reg, disp); ++ } else { ++ ld_ptr(dst_reg, tmp_reg, disp); ++ } ++ break; ++ case T_INT: ++ lw(dst_reg, tmp_reg, disp); ++ break; ++ case T_CHAR: ++ lhu(dst_reg, tmp_reg, disp); ++ break; ++ case T_SHORT: ++ lh(dst_reg, tmp_reg, disp); ++ break; ++ case T_BYTE: ++ case T_BOOLEAN: ++ lb(dst_reg, tmp_reg, disp); ++ break; ++ default: ++ ShouldNotReachHere(); ++ } ++} ++ ++int MacroAssembler::load_for_type(Register dst_reg, Address addr, BasicType type, bool wide) { ++ int code_offset = 0; ++ Register tmp_reg = T9; ++ Register index_reg = addr.index(); ++ if (index_reg == NOREG) { ++ tmp_reg = NOREG; ++ } ++ ++ int scale = addr.scale(); ++ if (tmp_reg != NOREG && scale >= 0) { ++ dsll(tmp_reg, index_reg, scale); ++ } ++ ++ int disp = addr.disp(); ++ bool disp_is_simm16 = true; ++ if (!Assembler::is_simm16(disp)) { ++ disp_is_simm16 = false; ++ } ++ ++ Register base_reg = addr.base(); ++ if (tmp_reg != NOREG) { ++ assert_different_registers(tmp_reg, base_reg, index_reg); ++ } ++ ++ if (tmp_reg != NOREG) { ++ daddu(tmp_reg, base_reg, tmp_reg); ++ if (!disp_is_simm16) { ++ move(tmp_reg, disp); ++ daddu(tmp_reg, base_reg, tmp_reg); ++ } ++ code_offset = offset(); ++ load_for_type_by_register(dst_reg, tmp_reg, disp_is_simm16 ? disp : 0, type, wide); ++ } else { ++ if (!disp_is_simm16) { ++ tmp_reg = T9; ++ assert_different_registers(tmp_reg, base_reg); ++ move(tmp_reg, disp); ++ daddu(tmp_reg, base_reg, tmp_reg); ++ } ++ code_offset = offset(); ++ load_for_type_by_register(dst_reg, disp_is_simm16 ? base_reg : tmp_reg, disp_is_simm16 ? disp : 0, type, wide); ++ } ++ ++ return code_offset; ++} ++ ++void MacroAssembler::load_for_type_by_register(FloatRegister dst_reg, Register tmp_reg, int disp, BasicType type) { ++ switch (type) { ++ case T_DOUBLE: ++ ldc1(dst_reg, tmp_reg, disp); ++ break; ++ case T_FLOAT: ++ lwc1(dst_reg, tmp_reg, disp); ++ break; ++ default: ++ ShouldNotReachHere(); ++ } ++} ++ ++int MacroAssembler::load_for_type(FloatRegister dst_reg, Address addr, BasicType type) { ++ int code_offset = 0; ++ Register tmp_reg = T9; ++ Register index_reg = addr.index(); ++ if (index_reg == NOREG) { ++ tmp_reg = NOREG; ++ } ++ ++ int scale = addr.scale(); ++ if (tmp_reg != NOREG && scale >= 0) { ++ dsll(tmp_reg, index_reg, scale); ++ } ++ ++ int disp = addr.disp(); ++ bool disp_is_simm16 = true; ++ if (!Assembler::is_simm16(disp)) { ++ disp_is_simm16 = false; ++ } ++ ++ Register base_reg = addr.base(); ++ if (tmp_reg != NOREG) { ++ assert_different_registers(tmp_reg, base_reg, index_reg); ++ } ++ ++ if (tmp_reg != NOREG) { ++ daddu(tmp_reg, base_reg, tmp_reg); ++ if (!disp_is_simm16) { ++ move(tmp_reg, disp); ++ daddu(tmp_reg, base_reg, tmp_reg); ++ } ++ code_offset = offset(); ++ load_for_type_by_register(dst_reg, tmp_reg, disp_is_simm16 ? disp : 0, type); ++ } else { ++ if (!disp_is_simm16) { ++ tmp_reg = T9; ++ assert_different_registers(tmp_reg, base_reg); ++ move(tmp_reg, disp); ++ daddu(tmp_reg, base_reg, tmp_reg); ++ } ++ code_offset = offset(); ++ load_for_type_by_register(dst_reg, disp_is_simm16 ? base_reg : tmp_reg, disp_is_simm16 ? disp : 0, type); ++ } ++ ++ return code_offset; ++} ++ ++void MacroAssembler::clear_jweak_tag(Register possibly_jweak) { ++ const int32_t inverted_jweak_mask = ~static_cast(JNIHandles::weak_tag_mask); ++ STATIC_ASSERT(inverted_jweak_mask == -2); // otherwise check this code ++ // The inverted mask is sign-extended ++ move(AT, inverted_jweak_mask); ++ andr(possibly_jweak, AT, possibly_jweak); ++} ++ ++void MacroAssembler::resolve_jobject(Register value, ++ Register thread, ++ Register tmp) { ++ assert_different_registers(value, thread, tmp); ++ Label done, not_weak; ++ beq(value, R0, done); // Use NULL as-is. ++ delayed()->nop(); ++ move(AT, JNIHandles::weak_tag_mask); // Test for jweak tag. ++ andr(AT, value, AT); ++ beq(AT, R0, not_weak); ++ delayed()->nop(); ++ // Resolve jweak. ++ ld(value, value, -JNIHandles::weak_tag_value); ++ verify_oop(value); ++ #if INCLUDE_ALL_GCS ++ if (UseG1GC) { ++ g1_write_barrier_pre(noreg /* obj */, ++ value /* pre_val */, ++ thread /* thread */, ++ tmp /* tmp */, ++ true /* tosca_live */, ++ true /* expand_call */); ++ } ++ #endif // INCLUDE_ALL_GCS ++ b(done); ++ delayed()->nop(); ++ bind(not_weak); ++ // Resolve (untagged) jobject. ++ ld(value, value, 0); ++ verify_oop(value); ++ bind(done); ++} ++ ++void MacroAssembler::cmp_cmov(Register op1, ++ Register op2, ++ Register dst, ++ Register src, ++ CMCompare cmp, ++ bool is_signed) { ++ switch (cmp) { ++ case EQ: ++ subu(AT, op1, op2); ++ movz(dst, src, AT); ++ break; ++ ++ case NE: ++ subu(AT, op1, op2); ++ movn(dst, src, AT); ++ break; ++ ++ case GT: ++ if (is_signed) { ++ slt(AT, op2, op1); ++ } else { ++ sltu(AT, op2, op1); ++ } ++ movn(dst, src, AT); ++ break; ++ ++ case GE: ++ if (is_signed) { ++ slt(AT, op1, op2); ++ } else { ++ sltu(AT, op1, op2); ++ } ++ movz(dst, src, AT); ++ break; ++ ++ case LT: ++ if (is_signed) { ++ slt(AT, op1, op2); ++ } else { ++ sltu(AT, op1, op2); ++ } ++ movn(dst, src, AT); ++ break; ++ ++ case LE: ++ if (is_signed) { ++ slt(AT, op2, op1); ++ } else { ++ sltu(AT, op2, op1); ++ } ++ movz(dst, src, AT); ++ break; ++ ++ default: ++ Unimplemented(); ++ } ++} ++ ++void MacroAssembler::cmp_cmov(FloatRegister op1, ++ FloatRegister op2, ++ Register dst, ++ Register src, ++ CMCompare cmp, ++ bool is_float) { ++ switch(cmp) { ++ case EQ: ++ if (is_float) { ++ c_eq_s(op1, op2); ++ } else { ++ c_eq_d(op1, op2); ++ } ++ movt(dst, src); ++ break; ++ ++ case NE: ++ if (is_float) { ++ c_eq_s(op1, op2); ++ } else { ++ c_eq_d(op1, op2); ++ } ++ movf(dst, src); ++ break; ++ ++ case GT: ++ if (is_float) { ++ c_ule_s(op1, op2); ++ } else { ++ c_ule_d(op1, op2); ++ } ++ movf(dst, src); ++ break; ++ ++ case GE: ++ if (is_float) { ++ c_ult_s(op1, op2); ++ } else { ++ c_ult_d(op1, op2); ++ } ++ movf(dst, src); ++ break; ++ ++ case LT: ++ if (is_float) { ++ c_ult_s(op1, op2); ++ } else { ++ c_ult_d(op1, op2); ++ } ++ movt(dst, src); ++ break; ++ ++ case LE: ++ if (is_float) { ++ c_ule_s(op1, op2); ++ } else { ++ c_ule_d(op1, op2); ++ } ++ movt(dst, src); ++ break; ++ ++ default: ++ Unimplemented(); ++ } ++} ++ ++void MacroAssembler::cmp_cmov(FloatRegister op1, ++ FloatRegister op2, ++ FloatRegister dst, ++ FloatRegister src, ++ CMCompare cmp, ++ bool is_float) { ++ switch(cmp) { ++ case EQ: ++ if (!is_float) { ++ c_eq_d(op1, op2); ++ movt_d(dst, src); ++ } else { ++ c_eq_s(op1, op2); ++ movt_s(dst, src); ++ } ++ break; ++ ++ case NE: ++ if (!is_float) { ++ c_eq_d(op1, op2); ++ movf_d(dst, src); ++ } else { ++ c_eq_s(op1, op2); ++ movf_s(dst, src); ++ } ++ break; ++ ++ case GT: ++ if (!is_float) { ++ c_ule_d(op1, op2); ++ movf_d(dst, src); ++ } else { ++ c_ule_s(op1, op2); ++ movf_s(dst, src); ++ } ++ break; ++ ++ case GE: ++ if (!is_float) { ++ c_ult_d(op1, op2); ++ movf_d(dst, src); ++ } else { ++ c_ult_s(op1, op2); ++ movf_s(dst, src); ++ } ++ break; ++ ++ case LT: ++ if (!is_float) { ++ c_ult_d(op1, op2); ++ movt_d(dst, src); ++ } else { ++ c_ult_s(op1, op2); ++ movt_s(dst, src); ++ } ++ break; ++ ++ case LE: ++ if (!is_float) { ++ c_ule_d(op1, op2); ++ movt_d(dst, src); ++ } else { ++ c_ule_s(op1, op2); ++ movt_s(dst, src); ++ } ++ break; ++ ++ default: ++ Unimplemented(); ++ } ++} ++ ++void MacroAssembler::cmp_cmov(Register op1, ++ Register op2, ++ FloatRegister dst, ++ FloatRegister src, ++ CMCompare cmp, ++ bool is_float) { ++ Label L; ++ ++ switch(cmp) { ++ case EQ: ++ bne(op1, op2, L); ++ delayed()->nop(); ++ if (is_float) { ++ mov_s(dst, src); ++ } else { ++ mov_d(dst, src); ++ } ++ bind(L); ++ break; ++ ++ case NE: ++ beq(op1, op2, L); ++ delayed()->nop(); ++ if (is_float) { ++ mov_s(dst, src); ++ } else { ++ mov_d(dst, src); ++ } ++ bind(L); ++ break; ++ ++ case GT: ++ slt(AT, op2, op1); ++ beq(AT, R0, L); ++ delayed()->nop(); ++ if (is_float) { ++ mov_s(dst, src); ++ } else { ++ mov_d(dst, src); ++ } ++ bind(L); ++ break; ++ ++ case GE: ++ slt(AT, op1, op2); ++ bne(AT, R0, L); ++ delayed()->nop(); ++ if (is_float) { ++ mov_s(dst, src); ++ } else { ++ mov_d(dst, src); ++ } ++ bind(L); ++ break; ++ ++ case LT: ++ slt(AT, op1, op2); ++ beq(AT, R0, L); ++ delayed()->nop(); ++ if (is_float) { ++ mov_s(dst, src); ++ } else { ++ mov_d(dst, src); ++ } ++ bind(L); ++ break; ++ ++ case LE: ++ slt(AT, op2, op1); ++ bne(AT, R0, L); ++ delayed()->nop(); ++ if (is_float) { ++ mov_s(dst, src); ++ } else { ++ mov_d(dst, src); ++ } ++ bind(L); ++ break; ++ ++ default: ++ Unimplemented(); ++ } ++} +diff --git a/hotspot/src/cpu/mips/vm/macroAssembler_mips.hpp b/hotspot/src/cpu/mips/vm/macroAssembler_mips.hpp +new file mode 100644 +index 0000000000..ab9727793f +--- /dev/null ++++ b/hotspot/src/cpu/mips/vm/macroAssembler_mips.hpp +@@ -0,0 +1,701 @@ ++/* ++ * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2015, 2022, Loongson Technology. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ * ++ */ ++ ++#ifndef CPU_MIPS_VM_MACROASSEMBLER_MIPS_HPP ++#define CPU_MIPS_VM_MACROASSEMBLER_MIPS_HPP ++ ++#include "asm/assembler.hpp" ++#include "utilities/macros.hpp" ++#include "runtime/rtmLocking.hpp" ++ ++// MacroAssembler extends Assembler by frequently used macros. ++// ++// Instructions for which a 'better' code sequence exists depending ++// on arguments should also go in here. ++ ++class MacroAssembler: public Assembler { ++ friend class LIR_Assembler; ++ friend class Runtime1; // as_Address() ++ ++ public: ++ // Compare code ++ typedef enum { ++ EQ = 0x01, ++ NE = 0x02, ++ GT = 0x03, ++ GE = 0x04, ++ LT = 0x05, ++ LE = 0x06 ++ } CMCompare; ++ ++ protected: ++ ++ Address as_Address(AddressLiteral adr); ++ Address as_Address(ArrayAddress adr); ++ ++ // Support for VM calls ++ // ++ // This is the base routine called by the different versions of call_VM_leaf. The interpreter ++ // may customize this version by overriding it for its purposes (e.g., to save/restore ++ // additional registers when doing a VM call). ++#ifdef CC_INTERP ++ // c++ interpreter never wants to use interp_masm version of call_VM ++ #define VIRTUAL ++#else ++ #define VIRTUAL virtual ++#endif ++ ++ VIRTUAL void call_VM_leaf_base( ++ address entry_point, // the entry point ++ int number_of_arguments // the number of arguments to pop after the call ++ ); ++ ++ // This is the base routine called by the different versions of call_VM. The interpreter ++ // may customize this version by overriding it for its purposes (e.g., to save/restore ++ // additional registers when doing a VM call). ++ // ++ // If no java_thread register is specified (noreg) than TREG will be used instead. call_VM_base ++ // returns the register which contains the thread upon return. If a thread register has been ++ // specified, the return value will correspond to that register. If no last_java_sp is specified ++ // (noreg) than sp will be used instead. ++ VIRTUAL void call_VM_base( // returns the register containing the thread upon return ++ Register oop_result, // where an oop-result ends up if any; use noreg otherwise ++ Register java_thread, // the thread if computed before ; use noreg otherwise ++ Register last_java_sp, // to set up last_Java_frame in stubs; use noreg otherwise ++ address entry_point, // the entry point ++ int number_of_arguments, // the number of arguments (w/o thread) to pop after the call ++ bool check_exceptions // whether to check for pending exceptions after return ++ ); ++ ++ // These routines should emit JVMTI PopFrame and ForceEarlyReturn handling code. ++ // The implementation is only non-empty for the InterpreterMacroAssembler, ++ // as only the interpreter handles PopFrame and ForceEarlyReturn requests. ++ virtual void check_and_handle_popframe(Register java_thread); ++ virtual void check_and_handle_earlyret(Register java_thread); ++ ++ void call_VM_helper(Register oop_result, address entry_point, int number_of_arguments, bool check_exceptions = true); ++ ++ // helpers for FPU flag access ++ // tmp is a temporary register, if none is available use noreg ++ ++ public: ++ static intptr_t i[32]; ++ static float f[32]; ++ static void print(outputStream *s); ++ ++ static int i_offset(unsigned int k); ++ static int f_offset(unsigned int k); ++ ++ static void save_registers(MacroAssembler *masm); ++ static void restore_registers(MacroAssembler *masm); ++ ++ MacroAssembler(CodeBuffer* code) : Assembler(code) {} ++ ++ // Support for NULL-checks ++ // ++ // Generates code that causes a NULL OS exception if the content of reg is NULL. ++ // If the accessed location is M[reg + offset] and the offset is known, provide the ++ // offset. No explicit code generation is needed if the offset is within a certain ++ // range (0 <= offset <= page_size). ++ ++ void null_check(Register reg, int offset = -1); ++ static bool needs_explicit_null_check(intptr_t offset); ++ ++ // Required platform-specific helpers for Label::patch_instructions. ++ // They _shadow_ the declarations in AbstractAssembler, which are undefined. ++ void pd_patch_instruction(address branch, address target); ++ ++ address emit_trampoline_stub(int insts_call_instruction_offset, address target); ++ ++ // Support for inc/dec with optimal instruction selection depending on value ++ void incrementl(Register reg, int value = 1); ++ void decrementl(Register reg, int value = 1); ++ ++ ++ // Alignment ++ void align(int modulus); ++ ++ ++ // Stack frame creation/removal ++ void enter(); ++ void leave(); ++ ++ // Support for getting the JavaThread pointer (i.e.; a reference to thread-local information) ++ // The pointer will be loaded into the thread register. ++ void get_thread(Register thread); ++ ++ ++ // Support for VM calls ++ // ++ // It is imperative that all calls into the VM are handled via the call_VM macros. ++ // They make sure that the stack linkage is setup correctly. call_VM's correspond ++ // to ENTRY/ENTRY_X entry points while call_VM_leaf's correspond to LEAF entry points. ++ ++ ++ void call_VM(Register oop_result, ++ address entry_point, ++ bool check_exceptions = true); ++ void call_VM(Register oop_result, ++ address entry_point, ++ Register arg_1, ++ bool check_exceptions = true); ++ void call_VM(Register oop_result, ++ address entry_point, ++ Register arg_1, Register arg_2, ++ bool check_exceptions = true); ++ void call_VM(Register oop_result, ++ address entry_point, ++ Register arg_1, Register arg_2, Register arg_3, ++ bool check_exceptions = true); ++ ++ // Overloadings with last_Java_sp ++ void call_VM(Register oop_result, ++ Register last_java_sp, ++ address entry_point, ++ int number_of_arguments = 0, ++ bool check_exceptions = true); ++ void call_VM(Register oop_result, ++ Register last_java_sp, ++ address entry_point, ++ Register arg_1, bool ++ check_exceptions = true); ++ void call_VM(Register oop_result, ++ Register last_java_sp, ++ address entry_point, ++ Register arg_1, Register arg_2, ++ bool check_exceptions = true); ++ void call_VM(Register oop_result, ++ Register last_java_sp, ++ address entry_point, ++ Register arg_1, Register arg_2, Register arg_3, ++ bool check_exceptions = true); ++ ++ void get_vm_result (Register oop_result, Register thread); ++ void get_vm_result_2(Register metadata_result, Register thread); ++ void call_VM_leaf(address entry_point, ++ int number_of_arguments = 0); ++ void call_VM_leaf(address entry_point, ++ Register arg_1); ++ void call_VM_leaf(address entry_point, ++ Register arg_1, Register arg_2); ++ void call_VM_leaf(address entry_point, ++ Register arg_1, Register arg_2, Register arg_3); ++ ++ // Super call_VM calls - correspond to MacroAssembler::call_VM(_leaf) calls ++ void super_call_VM_leaf(address entry_point); ++ void super_call_VM_leaf(address entry_point, Register arg_1); ++ void super_call_VM_leaf(address entry_point, Register arg_1, Register arg_2); ++ void super_call_VM_leaf(address entry_point, Register arg_1, Register arg_2, Register arg_3); ++ ++ // last Java Frame (fills frame anchor) ++ void set_last_Java_frame(Register thread, ++ Register last_java_sp, ++ Register last_java_fp, ++ address last_java_pc); ++ ++ // thread in the default location (S6) ++ void set_last_Java_frame(Register last_java_sp, ++ Register last_java_fp, ++ address last_java_pc); ++ ++ void reset_last_Java_frame(Register thread, bool clear_fp); ++ ++ // thread in the default location (S6) ++ void reset_last_Java_frame(bool clear_fp); ++ ++ // Stores ++ void store_check(Register obj); // store check for obj - register is destroyed afterwards ++ void store_check(Register obj, Address dst); // same as above, dst is exact store location (reg. is destroyed) ++ ++ void resolve_jobject(Register value, Register thread, Register tmp); ++ void clear_jweak_tag(Register possibly_jweak); ++ ++#if INCLUDE_ALL_GCS ++ ++ void g1_write_barrier_pre(Register obj, ++ Register pre_val, ++ Register thread, ++ Register tmp, ++ bool tosca_live, ++ bool expand_call); ++ ++ void g1_write_barrier_post(Register store_addr, ++ Register new_val, ++ Register thread, ++ Register tmp, ++ Register tmp2); ++ ++#endif // INCLUDE_ALL_GCS ++ ++ // split store_check(Register obj) to enhance instruction interleaving ++ void store_check_part_1(Register obj); ++ void store_check_part_2(Register obj); ++ ++ // C 'boolean' to Java boolean: x == 0 ? 0 : 1 ++ void c2bool(Register x); ++ //add for compressedoops ++ void load_klass(Register dst, Register src); ++ void store_klass(Register dst, Register src); ++ void load_prototype_header(Register dst, Register src); ++ ++ void store_klass_gap(Register dst, Register src); ++ ++ void load_heap_oop(Register dst, Address src); ++ void store_heap_oop(Address dst, Register src); ++ void store_heap_oop_null(Address dst); ++ void encode_heap_oop(Register r); ++ void encode_heap_oop(Register dst, Register src); ++ void decode_heap_oop(Register r); ++ void decode_heap_oop(Register dst, Register src); ++ void encode_heap_oop_not_null(Register r); ++ void decode_heap_oop_not_null(Register r); ++ void encode_heap_oop_not_null(Register dst, Register src); ++ void decode_heap_oop_not_null(Register dst, Register src); ++ ++ void encode_klass_not_null(Register r); ++ void decode_klass_not_null(Register r); ++ void encode_klass_not_null(Register dst, Register src); ++ void decode_klass_not_null(Register dst, Register src); ++ ++ // Returns the byte size of the instructions generated by decode_klass_not_null() ++ // when compressed klass pointers are being used. ++ static int instr_size_for_decode_klass_not_null(); ++ ++ // if heap base register is used - reinit it with the correct value ++ void reinit_heapbase(); ++ ++ DEBUG_ONLY(void verify_heapbase(const char* msg);) ++ ++ void set_narrow_klass(Register dst, Klass* k); ++ void set_narrow_oop(Register dst, jobject obj); ++ ++ ++ ++ ++ // Sign extension ++ void sign_extend_short(Register reg) { /*dsll32(reg, reg, 16); dsra32(reg, reg, 16);*/ seh(reg, reg); } ++ void sign_extend_byte(Register reg) { /*dsll32(reg, reg, 24); dsra32(reg, reg, 24);*/ seb(reg, reg); } ++ void rem_s(FloatRegister fd, FloatRegister fs, FloatRegister ft, FloatRegister tmp); ++ void rem_d(FloatRegister fd, FloatRegister fs, FloatRegister ft, FloatRegister tmp); ++ ++ void trigfunc(char trig, int num_fpu_regs_in_use = 1); ++ // allocation ++ void eden_allocate( ++ Register obj, // result: pointer to object after successful allocation ++ Register var_size_in_bytes, // object size in bytes if unknown at compile time; invalid otherwise ++ int con_size_in_bytes, // object size in bytes if known at compile time ++ Register t1, // temp register ++ Register t2, ++ Label& slow_case // continuation point if fast allocation fails ++ ); ++ void tlab_allocate( ++ Register obj, // result: pointer to object after successful allocation ++ Register var_size_in_bytes, // object size in bytes if unknown at compile time; invalid otherwise ++ int con_size_in_bytes, // object size in bytes if known at compile time ++ Register t1, // temp register ++ Register t2, // temp register ++ Label& slow_case // continuation point if fast allocation fails ++ ); ++ void tlab_refill(Label& retry_tlab, Label& try_eden, Label& slow_case); ++ void incr_allocated_bytes(Register thread, ++ Register var_size_in_bytes, int con_size_in_bytes, ++ Register t1 = noreg); ++ // interface method calling ++ void lookup_interface_method(Register recv_klass, ++ Register intf_klass, ++ RegisterOrConstant itable_index, ++ Register method_result, ++ Register scan_temp, ++ Label& no_such_interface, ++ bool return_method = true); ++ ++ // virtual method calling ++ void lookup_virtual_method(Register recv_klass, ++ RegisterOrConstant vtable_index, ++ Register method_result); ++ ++ // Test sub_klass against super_klass, with fast and slow paths. ++ ++ // The fast path produces a tri-state answer: yes / no / maybe-slow. ++ // One of the three labels can be NULL, meaning take the fall-through. ++ // If super_check_offset is -1, the value is loaded up from super_klass. ++ // No registers are killed, except temp_reg. ++ void check_klass_subtype_fast_path(Register sub_klass, ++ Register super_klass, ++ Register temp_reg, ++ Label* L_success, ++ Label* L_failure, ++ Label* L_slow_path, ++ RegisterOrConstant super_check_offset = RegisterOrConstant(-1)); ++ ++ // The rest of the type check; must be wired to a corresponding fast path. ++ // It does not repeat the fast path logic, so don't use it standalone. ++ // The temp_reg and temp2_reg can be noreg, if no temps are available. ++ // Updates the sub's secondary super cache as necessary. ++ // If set_cond_codes, condition codes will be Z on success, NZ on failure. ++ void check_klass_subtype_slow_path(Register sub_klass, ++ Register super_klass, ++ Register temp_reg, ++ Register temp2_reg, ++ Label* L_success, ++ Label* L_failure, ++ bool set_cond_codes = false); ++ ++ // Simplified, combined version, good for typical uses. ++ // Falls through on failure. ++ void check_klass_subtype(Register sub_klass, ++ Register super_klass, ++ Register temp_reg, ++ Label& L_success); ++ ++ ++ // Debugging ++ ++ // only if +VerifyOops ++ void verify_oop(Register reg, const char* s = "broken oop"); ++ void verify_oop_addr(Address addr, const char * s = "broken oop addr"); ++ void verify_oop_subroutine(); ++ // TODO: verify method and klass metadata (compare against vptr?) ++ void _verify_method_ptr(Register reg, const char * msg, const char * file, int line) {} ++ void _verify_klass_ptr(Register reg, const char * msg, const char * file, int line){} ++ ++ #define verify_method_ptr(reg) _verify_method_ptr(reg, "broken method " #reg, __FILE__, __LINE__) ++ #define verify_klass_ptr(reg) _verify_klass_ptr(reg, "broken klass " #reg, __FILE__, __LINE__) ++ ++ // only if +VerifyFPU ++ void verify_FPU(int stack_depth, const char* s = "illegal FPU state"); ++ ++ // prints msg, dumps registers and stops execution ++ void stop(const char* msg); ++ ++ // prints msg and continues ++ void warn(const char* msg); ++ ++ static void debug(char* msg/*, RegistersForDebugging* regs*/); ++ static void debug64(char* msg, int64_t pc, int64_t regs[]); ++ ++ void print_reg(Register reg); ++ void print_reg(FloatRegister reg); ++ ++ void untested() { stop("untested"); } ++ ++ void unimplemented(const char* what = "") { char* b = new char[1024]; jio_snprintf(b, sizeof(b), "unimplemented: %s", what); stop(b); } ++ ++ void should_not_reach_here() { stop("should not reach here"); } ++ ++ void print_CPU_state(); ++ ++ // Stack overflow checking ++ void bang_stack_with_offset(int offset) { ++ // stack grows down, caller passes positive offset ++ assert(offset > 0, "must bang with negative offset"); ++ if (offset <= 32768) { ++ sw(RA0, SP, -offset); ++ } else { ++ li(AT, offset); ++ dsubu(AT, SP, AT); ++ sw(RA0, AT, 0); ++ } ++ } ++ ++ // Writes to stack successive pages until offset reached to check for ++ // stack overflow + shadow pages. Also, clobbers tmp ++ void bang_stack_size(Register size, Register tmp); ++ ++ virtual RegisterOrConstant delayed_value_impl(intptr_t* delayed_value_addr, ++ Register tmp, ++ int offset); ++ ++ // Support for serializing memory accesses between threads ++ void serialize_memory(Register thread, Register tmp); ++ ++ //void verify_tlab(); ++ void verify_tlab(Register t1, Register t2); ++ ++ // Biased locking support ++ // lock_reg and obj_reg must be loaded up with the appropriate values. ++ // tmp_reg is optional. If it is supplied (i.e., != noreg) it will ++ // be killed; if not supplied, push/pop will be used internally to ++ // allocate a temporary (inefficient, avoid if possible). ++ // Optional slow case is for implementations (interpreter and C1) which branch to ++ // slow case directly. Leaves condition codes set for C2's Fast_Lock node. ++ // Returns offset of first potentially-faulting instruction for null ++ // check info (currently consumed only by C1). If ++ // swap_reg_contains_mark is true then returns -1 as it is assumed ++ // the calling code has already passed any potential faults. ++ int biased_locking_enter(Register lock_reg, Register obj_reg, ++ Register swap_reg, Register tmp_reg, ++ bool swap_reg_contains_mark, ++ Label& done, Label* slow_case = NULL, ++ BiasedLockingCounters* counters = NULL); ++ void biased_locking_exit (Register obj_reg, Register temp_reg, Label& done); ++#ifdef COMPILER2 ++ void fast_lock(Register obj, Register box, Register tmp, Register scr); ++ void fast_unlock(Register obj, Register box, Register tmp); ++#endif ++ ++ ++ // Arithmetics ++ // Regular vs. d* versions ++ inline void addu_long(Register rd, Register rs, Register rt) { ++ daddu(rd, rs, rt); ++ } ++ inline void addu_long(Register rd, Register rs, long imm32_64) { ++ daddiu(rd, rs, imm32_64); ++ } ++ ++ void round_to(Register reg, int modulus) { ++ assert_different_registers(reg, AT); ++ increment(reg, modulus - 1); ++ move(AT, - modulus); ++ andr(reg, reg, AT); ++ } ++ ++ // the follow two might use AT register, be sure you have no meanful data in AT before you call them ++ void increment(Register reg, int imm); ++ void decrement(Register reg, int imm); ++ ++ void shl(Register reg, int sa) { dsll(reg, reg, sa); } ++ void shr(Register reg, int sa) { dsrl(reg, reg, sa); } ++ void sar(Register reg, int sa) { dsra(reg, reg, sa); } ++ ++ // Helper functions for statistics gathering. ++ void atomic_inc32(address counter_addr, int inc, Register tmp_reg1, Register tmp_reg2); ++ ++ // Calls ++ void call(address entry); ++ void call(address entry, relocInfo::relocType rtype); ++ void call(address entry, RelocationHolder& rh); ++ ++ address trampoline_call(AddressLiteral entry, CodeBuffer *cbuf = NULL); ++ ++ // Emit the CompiledIC call idiom ++ void ic_call(address entry); ++ ++ // Jumps ++ void jmp(address entry); ++ void jmp(address entry, relocInfo::relocType rtype); ++ void jmp_far(Label& L); // always long jumps ++ ++ /* branches may exceed 16-bit offset */ ++ void b_far(address entry); ++ void b_far(Label& L); ++ ++ void bne_far (Register rs, Register rt, address entry); ++ void bne_far (Register rs, Register rt, Label& L); ++ ++ void beq_far (Register rs, Register rt, address entry); ++ void beq_far (Register rs, Register rt, Label& L); ++ ++ // For C2 to support long branches ++ void beq_long (Register rs, Register rt, Label& L); ++ void bne_long (Register rs, Register rt, Label& L); ++ void bc1t_long (Label& L); ++ void bc1f_long (Label& L); ++ ++ void patchable_call(address target); ++ void general_call(address target); ++ ++ void patchable_jump(address target); ++ void general_jump(address target); ++ ++ static int insts_for_patchable_call(address target); ++ static int insts_for_general_call(address target); ++ ++ static int insts_for_patchable_jump(address target); ++ static int insts_for_general_jump(address target); ++ ++ // Floating ++ // Data ++ ++ // Load and store values by size and signed-ness ++ void load_sized_value(Register dst, Address src, size_t size_in_bytes, bool is_signed, Register dst2 = noreg); ++ void store_sized_value(Address dst, Register src, size_t size_in_bytes, Register src2 = noreg); ++ ++ // ld_ptr will perform lw for 32 bit VMs and ld for 64 bit VMs ++ inline void ld_ptr(Register rt, Address a) { ++ ld(rt, a); ++ } ++ ++ inline void ld_ptr(Register rt, Register base, int offset16) { ++ ld(rt, base, offset16); ++ } ++ ++ // st_ptr will perform sw for 32 bit VMs and sd for 64 bit VMs ++ inline void st_ptr(Register rt, Address a) { ++ sd(rt, a); ++ } ++ ++ inline void st_ptr(Register rt, Register base, int offset16) { ++ sd(rt, base, offset16); ++ } ++ ++ void ld_ptr(Register rt, Register base, Register offset); ++ void st_ptr(Register rt, Register base, Register offset); ++ ++ // swap the two byte of the low 16-bit halfword ++ // this directive will use AT, be sure the high 16-bit of reg is zero ++ void hswap(Register reg); ++ void huswap(Register reg); ++ ++ // convert big endian integer to little endian integer ++ void swap(Register reg); ++ ++ // implement the x86 instruction semantic ++ // if c_reg == *dest then *dest <= x_reg ++ // else c_reg <= *dest ++ // the AT indicate if xchg occurred, 1 for xchged, else 0 ++ void cmpxchg(Register x_reg, Address dest, Register c_reg); ++ void cmpxchg32(Register x_reg, Address dest, Register c_reg); ++ void cmpxchg8(Register x_regLo, Register x_regHi, Address dest, Register c_regLo, Register c_regHi); ++ ++ //pop & push ++ void extend_sign(Register rh, Register rl) { stop("extend_sign"); } ++ void neg(Register reg) { dsubu(reg, R0, reg); } ++ void push (Register reg) { daddiu(SP, SP, -8); sd (reg, SP, 0); } ++ void push (FloatRegister reg) { daddiu(SP, SP, -8); sdc1(reg, SP, 0); } ++ void pop (Register reg) { ld (reg, SP, 0); daddiu(SP, SP, 8); } ++ void pop (FloatRegister reg) { ldc1(reg, SP, 0); daddiu(SP, SP, 8); } ++ void pop () { daddiu(SP, SP, 8); } ++ void pop2 () { daddiu(SP, SP, 16); } ++ void push2(Register reg1, Register reg2); ++ void pop2 (Register reg1, Register reg2); ++ void dpush (Register reg) { daddiu(SP, SP, -8); sd (reg, SP, 0); } ++ void dpop (Register reg) { ld (reg, SP, 0); daddiu(SP, SP, 8); } ++ //we need 2 fun to save and resotre general register ++ void pushad(); ++ void popad(); ++ void pushad_except_v0(); ++ void popad_except_v0(); ++ ++ //move an 32-bit immediate to Register ++ void move(Register reg, int imm32) { li32(reg, imm32); } ++ void li (Register rd, long imm); ++ void li (Register rd, address addr) { li(rd, (long)addr); } ++ //replace move(Register reg, int imm) ++ void li32(Register rd, int imm32); // sign-extends to 64 bits on mips64 ++ void set64(Register d, jlong value); ++ static int insts_for_set64(jlong value); ++ ++ void patchable_set48(Register d, jlong value); ++ void patchable_set32(Register d, jlong value); ++ ++ void patchable_call32(Register d, jlong value); ++ ++ static int call_size(address target, bool far, bool patchable); ++ ++ static bool reachable_from_cache(address target); ++ static bool reachable_from_cache(); ++ ++ ++ void dli(Register rd, long imm) { li(rd, imm); } ++ void li64(Register rd, long imm); ++ void li48(Register rd, long imm); ++ ++ void move(Register rd, Register rs) { daddu(rd, rs, R0); } ++ void move_u32(Register rd, Register rs) { addu32(rd, rs, R0); } ++ void dmove(Register rd, Register rs) { daddu(rd, rs, R0); } ++ void mov_metadata(Register dst, Metadata* obj); ++ void mov_metadata(Address dst, Metadata* obj); ++ ++ void store_for_type_by_register(Register src_reg, Register tmp_reg, int disp, BasicType type, bool wide); ++ void store_for_type_by_register(FloatRegister src_reg, Register tmp_reg, int disp, BasicType type); ++ void store_for_type(Register src_reg, Address addr, BasicType type = T_INT, bool wide = false); ++ void store_for_type(FloatRegister src_reg, Address addr, BasicType type = T_INT); ++ void load_for_type_by_register(Register dst_reg, Register tmp_reg, int disp, BasicType type, bool wide); ++ void load_for_type_by_register(FloatRegister dst_reg, Register tmp_reg, int disp, BasicType type); ++ int load_for_type(Register dst_reg, Address addr, BasicType type = T_INT, bool wide = false); ++ int load_for_type(FloatRegister dst_reg, Address addr, BasicType type = T_INT); ++ ++#ifndef PRODUCT ++ static void pd_print_patched_instruction(address branch) { ++ jint stub_inst = *(jint*) branch; ++ print_instruction(stub_inst); ++ ::tty->print("%s", " (unresolved)"); ++ ++ } ++#endif ++ ++ //FIXME ++ void empty_FPU_stack(){/*need implemented*/}; ++ ++ ++ // method handles (JSR 292) ++ Address argument_address(RegisterOrConstant arg_slot, int extra_slot_offset = 0); ++ ++ // Conditional move ++ void cmp_cmov(Register op1, ++ Register op2, ++ Register dst, ++ Register src, ++ CMCompare cmp = EQ, ++ bool is_signed = true); ++ void cmp_cmov(FloatRegister op1, ++ FloatRegister op2, ++ Register dst, ++ Register src, ++ CMCompare cmp = EQ, ++ bool is_float = true); ++ void cmp_cmov(FloatRegister op1, ++ FloatRegister op2, ++ FloatRegister dst, ++ FloatRegister src, ++ CMCompare cmp = EQ, ++ bool is_float = true); ++ void cmp_cmov(Register op1, ++ Register op2, ++ FloatRegister dst, ++ FloatRegister src, ++ CMCompare cmp = EQ, ++ bool is_float = true); ++ ++#undef VIRTUAL ++ ++}; ++ ++/** ++ * class SkipIfEqual: ++ * ++ * Instantiating this class will result in assembly code being output that will ++ * jump around any code emitted between the creation of the instance and it's ++ * automatic destruction at the end of a scope block, depending on the value of ++ * the flag passed to the constructor, which will be checked at run-time. ++ */ ++class SkipIfEqual { ++ private: ++ MacroAssembler* _masm; ++ Label _label; ++ ++ public: ++ SkipIfEqual(MacroAssembler*, const bool* flag_addr, bool value); ++ ~SkipIfEqual(); ++}; ++ ++#ifdef ASSERT ++inline bool AbstractAssembler::pd_check_instruction_mark() { return true; } ++#endif ++ ++ ++#endif // CPU_MIPS_VM_MACROASSEMBLER_MIPS_HPP +diff --git a/hotspot/src/cpu/mips/vm/macroAssembler_mips.inline.hpp b/hotspot/src/cpu/mips/vm/macroAssembler_mips.inline.hpp +new file mode 100644 +index 0000000000..92c05fb726 +--- /dev/null ++++ b/hotspot/src/cpu/mips/vm/macroAssembler_mips.inline.hpp +@@ -0,0 +1,34 @@ ++/* ++ * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2017, Loongson Technology. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ * ++ */ ++ ++#ifndef CPU_MIPS_VM_MACROASSEMBLER_MIPS_INLINE_HPP ++#define CPU_MIPS_VM_MACROASSEMBLER_MIPS_INLINE_HPP ++ ++#include "asm/assembler.inline.hpp" ++#include "asm/macroAssembler.hpp" ++#include "asm/codeBuffer.hpp" ++#include "code/codeCache.hpp" ++ ++#endif // CPU_MIPS_VM_MACROASSEMBLER_MIPS_INLINE_HPP +diff --git a/hotspot/src/cpu/mips/vm/metaspaceShared_mips_64.cpp b/hotspot/src/cpu/mips/vm/metaspaceShared_mips_64.cpp +new file mode 100644 +index 0000000000..0c467df2f3 +--- /dev/null ++++ b/hotspot/src/cpu/mips/vm/metaspaceShared_mips_64.cpp +@@ -0,0 +1,123 @@ ++/* ++ * Copyright (c) 2004, 2012, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2015, 2021, Loongson Technology. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ * ++ */ ++ ++#include "precompiled.hpp" ++#include "asm/macroAssembler.hpp" ++#include "asm/codeBuffer.hpp" ++#include "memory/metaspaceShared.hpp" ++ ++// Generate the self-patching vtable method: ++// ++// This method will be called (as any other Klass virtual method) with ++// the Klass itself as the first argument. Example: ++// ++// oop obj; ++// int size = obj->klass()->klass_part()->oop_size(this); ++// ++// for which the virtual method call is Klass::oop_size(); ++// ++// The dummy method is called with the Klass object as the first ++// operand, and an object as the second argument. ++// ++ ++//===================================================================== ++ ++// All of the dummy methods in the vtable are essentially identical, ++// differing only by an ordinal constant, and they bear no releationship ++// to the original method which the caller intended. Also, there needs ++// to be 'vtbl_list_size' instances of the vtable in order to ++// differentiate between the 'vtable_list_size' original Klass objects. ++ ++#define __ masm-> ++ ++#define A0 RA0 ++#define A1 RA1 ++#define A2 RA2 ++#define A3 RA3 ++#define A4 RA4 ++#define A5 RA5 ++#define A6 RA6 ++#define A7 RA7 ++#define T0 RT0 ++#define T1 RT1 ++#define T2 RT2 ++#define T3 RT3 ++#define T8 RT8 ++#define T9 RT9 ++ ++void MetaspaceShared::generate_vtable_methods(void** vtbl_list, ++ void** vtable, ++ char** md_top, ++ char* md_end, ++ char** mc_top, ++ char* mc_end) { ++ ++ intptr_t vtable_bytes = (num_virtuals * vtbl_list_size) * sizeof(void*); ++ *(intptr_t *)(*md_top) = vtable_bytes; ++ *md_top += sizeof(intptr_t); ++ void** dummy_vtable = (void**)*md_top; ++ *vtable = dummy_vtable; ++ *md_top += vtable_bytes; ++ ++ // Get ready to generate dummy methods. ++ ++ CodeBuffer cb((unsigned char*)*mc_top, mc_end - *mc_top); ++ MacroAssembler* masm = new MacroAssembler(&cb); ++ ++ Label common_code; ++ for (int i = 0; i < vtbl_list_size; ++i) { ++ for (int j = 0; j < num_virtuals; ++j) { ++ dummy_vtable[num_virtuals * i + j] = (void*)masm->pc(); ++ ++ // Load V0 with a value indicating vtable/offset pair. ++ // -- bits[ 7..0] (8 bits) which virtual method in table? ++ // -- bits[12..8] (5 bits) which virtual method table? ++ // -- must fit in 13-bit instruction immediate field. ++ __ move(V0, (i << 8) + j); ++ __ b(common_code); ++ __ delayed()->nop(); ++ } ++ } ++ ++ __ bind(common_code); ++ ++ __ srl(T9, V0, 8); // isolate vtable identifier. ++ __ shl(T9, LogBytesPerWord); ++ __ li(AT, (long)vtbl_list); ++ __ addu(T9, AT, T9); ++ __ ld(T9, T9, 0); // get correct vtable address. ++ __ sd(T9, A0, 0); // update vtable pointer. ++ ++ __ andi(V0, V0, 0x00ff); // isolate vtable method index ++ __ shl(V0, LogBytesPerWord); ++ __ addu(T9, T9, V0); ++ __ ld(T9, T9, 0); // address of real method pointer. ++ __ jr(T9); // get real method pointer. ++ __ delayed()->nop(); ++ ++ __ flush(); ++ ++ *mc_top = (char*)__ pc(); ++} +diff --git a/hotspot/src/cpu/mips/vm/methodHandles_mips.cpp b/hotspot/src/cpu/mips/vm/methodHandles_mips.cpp +new file mode 100644 +index 0000000000..428c271362 +--- /dev/null ++++ b/hotspot/src/cpu/mips/vm/methodHandles_mips.cpp +@@ -0,0 +1,576 @@ ++/* ++ * Copyright (c) 1997, 2014, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2015, 2022, Loongson Technology. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ * ++ */ ++ ++#include "precompiled.hpp" ++#include "asm/macroAssembler.hpp" ++#include "interpreter/interpreter.hpp" ++#include "interpreter/interpreterRuntime.hpp" ++#include "memory/allocation.inline.hpp" ++#include "prims/methodHandles.hpp" ++ ++#define __ _masm-> ++ ++#define A0 RA0 ++#define A1 RA1 ++#define A2 RA2 ++#define A3 RA3 ++#define A4 RA4 ++#define A5 RA5 ++#define A6 RA6 ++#define A7 RA7 ++#define T0 RT0 ++#define T1 RT1 ++#define T2 RT2 ++#define T3 RT3 ++#define T8 RT8 ++#define T9 RT9 ++ ++#ifdef PRODUCT ++#define BLOCK_COMMENT(str) /* nothing */ ++#define STOP(error) stop(error) ++#else ++#define BLOCK_COMMENT(str) __ block_comment(str) ++#define STOP(error) block_comment(error); __ stop(error) ++#endif ++ ++#define BIND(label) bind(label); BLOCK_COMMENT(#label ":") ++ ++void MethodHandles::load_klass_from_Class(MacroAssembler* _masm, Register klass_reg) { ++ if (VerifyMethodHandles) ++ verify_klass(_masm, klass_reg, SystemDictionary::WK_KLASS_ENUM_NAME(java_lang_Class), ++ "MH argument is a Class"); ++ __ ld(klass_reg, Address(klass_reg, java_lang_Class::klass_offset_in_bytes())); ++} ++ ++#ifdef ASSERT ++static int check_nonzero(const char* xname, int x) { ++ assert(x != 0, err_msg("%s should be nonzero", xname)); ++ return x; ++} ++#define NONZERO(x) check_nonzero(#x, x) ++#else //ASSERT ++#define NONZERO(x) (x) ++#endif //ASSERT ++ ++#ifdef ASSERT ++void MethodHandles::verify_klass(MacroAssembler* _masm, ++ Register obj, SystemDictionary::WKID klass_id, ++ const char* error_message) { ++} ++ ++void MethodHandles::verify_ref_kind(MacroAssembler* _masm, int ref_kind, Register member_reg, Register temp) { ++ Label L; ++ BLOCK_COMMENT("verify_ref_kind {"); ++ __ lw(temp, Address(member_reg, NONZERO(java_lang_invoke_MemberName::flags_offset_in_bytes()))); ++ __ sra(temp, temp, java_lang_invoke_MemberName::MN_REFERENCE_KIND_SHIFT); ++ __ move(AT, java_lang_invoke_MemberName::MN_REFERENCE_KIND_MASK); ++ __ andr(temp, temp, AT); ++ __ move(AT, ref_kind); ++ __ beq(temp, AT, L); ++ __ delayed()->nop(); ++ { char* buf = NEW_C_HEAP_ARRAY(char, 100, mtInternal); ++ jio_snprintf(buf, 100, "verify_ref_kind expected %x", ref_kind); ++ if (ref_kind == JVM_REF_invokeVirtual || ++ ref_kind == JVM_REF_invokeSpecial) ++ // could do this for all ref_kinds, but would explode assembly code size ++ trace_method_handle(_masm, buf); ++ __ STOP(buf); ++ } ++ BLOCK_COMMENT("} verify_ref_kind"); ++ __ bind(L); ++} ++ ++#endif //ASSERT ++ ++void MethodHandles::jump_from_method_handle(MacroAssembler* _masm, Register method, Register temp, ++ bool for_compiler_entry) { ++ assert(method == Rmethod, "interpreter calling convention"); ++ ++ Label L_no_such_method; ++ __ beq(method, R0, L_no_such_method); ++ __ delayed()->nop(); ++ ++ __ verify_method_ptr(method); ++ ++ if (!for_compiler_entry && JvmtiExport::can_post_interpreter_events()) { ++ Label run_compiled_code; ++ // JVMTI events, such as single-stepping, are implemented partly by avoiding running ++ // compiled code in threads for which the event is enabled. Check here for ++ // interp_only_mode if these events CAN be enabled. ++ Register rthread = TREG; ++ // interp_only is an int, on little endian it is sufficient to test the byte only ++ // Is a cmpl faster? ++ __ lbu(AT, rthread, in_bytes(JavaThread::interp_only_mode_offset())); ++ __ beq(AT, R0, run_compiled_code); ++ __ delayed()->nop(); ++ __ ld(T9, method, in_bytes(Method::interpreter_entry_offset())); ++ __ jr(T9); ++ __ delayed()->nop(); ++ __ BIND(run_compiled_code); ++ } ++ ++ const ByteSize entry_offset = for_compiler_entry ? Method::from_compiled_offset() : ++ Method::from_interpreted_offset(); ++ __ ld(T9, method, in_bytes(entry_offset)); ++ __ jr(T9); ++ __ delayed()->nop(); ++ ++ __ bind(L_no_such_method); ++ address wrong_method = StubRoutines::throw_AbstractMethodError_entry(); ++ __ jmp(wrong_method, relocInfo::runtime_call_type); ++ __ delayed()->nop(); ++} ++ ++void MethodHandles::jump_to_lambda_form(MacroAssembler* _masm, ++ Register recv, Register method_temp, ++ Register temp2, ++ bool for_compiler_entry) { ++ BLOCK_COMMENT("jump_to_lambda_form {"); ++ // This is the initial entry point of a lazy method handle. ++ // After type checking, it picks up the invoker from the LambdaForm. ++ assert_different_registers(recv, method_temp, temp2); ++ assert(recv != noreg, "required register"); ++ assert(method_temp == Rmethod, "required register for loading method"); ++ ++ //NOT_PRODUCT({ FlagSetting fs(TraceMethodHandles, true); trace_method_handle(_masm, "LZMH"); }); ++ ++ // Load the invoker, as MH -> MH.form -> LF.vmentry ++ __ verify_oop(recv); ++ __ load_heap_oop(method_temp, Address(recv, NONZERO(java_lang_invoke_MethodHandle::form_offset_in_bytes()))); ++ __ verify_oop(method_temp); ++ __ load_heap_oop(method_temp, Address(method_temp, NONZERO(java_lang_invoke_LambdaForm::vmentry_offset_in_bytes()))); ++ __ verify_oop(method_temp); ++ // the following assumes that a Method* is normally compressed in the vmtarget field: ++ __ ld(method_temp, Address(method_temp, NONZERO(java_lang_invoke_MemberName::vmtarget_offset_in_bytes()))); ++ ++ if (VerifyMethodHandles && !for_compiler_entry) { ++ // make sure recv is already on stack ++ __ ld(temp2, Address(method_temp, Method::const_offset())); ++ __ load_sized_value(temp2, ++ Address(temp2, ConstMethod::size_of_parameters_offset()), ++ sizeof(u2), false); ++ // assert(sizeof(u2) == sizeof(Method::_size_of_parameters), ""); ++ Label L; ++ Address recv_addr = __ argument_address(temp2, -1); ++ __ ld(AT, recv_addr); ++ __ beq(recv, AT, L); ++ __ delayed()->nop(); ++ ++ recv_addr = __ argument_address(temp2, -1); ++ __ ld(V0, recv_addr); ++ __ STOP("receiver not on stack"); ++ __ BIND(L); ++ } ++ ++ jump_from_method_handle(_masm, method_temp, temp2, for_compiler_entry); ++ BLOCK_COMMENT("} jump_to_lambda_form"); ++} ++ ++ ++// Code generation ++address MethodHandles::generate_method_handle_interpreter_entry(MacroAssembler* _masm, ++ vmIntrinsics::ID iid) { ++ const bool not_for_compiler_entry = false; // this is the interpreter entry ++ assert(is_signature_polymorphic(iid), "expected invoke iid"); ++ if (iid == vmIntrinsics::_invokeGeneric || ++ iid == vmIntrinsics::_compiledLambdaForm) { ++ // Perhaps surprisingly, the symbolic references visible to Java are not directly used. ++ // They are linked to Java-generated adapters via MethodHandleNatives.linkMethod. ++ // They all allow an appendix argument. ++ __ stop("empty stubs make SG sick"); ++ return NULL; ++ } ++ ++ // Rmethod: Method* ++ // T9: argument locator (parameter slot count, added to sp) ++ // S7: used as temp to hold mh or receiver ++ Register t9_argp = T9; // argument list ptr, live on error paths ++ Register s7_mh = S7; // MH receiver; dies quickly and is recycled ++ Register rm_method = Rmethod; // eventual target of this invocation ++ ++ // here's where control starts out: ++ __ align(CodeEntryAlignment); ++ address entry_point = __ pc(); ++ ++ if (VerifyMethodHandles) { ++ Label L; ++ BLOCK_COMMENT("verify_intrinsic_id {"); ++ __ lbu(AT, rm_method, Method::intrinsic_id_offset_in_bytes()); ++ guarantee(Assembler::is_simm16(iid), "Oops, iid is not simm16! Change the instructions."); ++ __ addiu(AT, AT, -1 * (int) iid); ++ __ beq(AT, R0, L); ++ __ delayed()->nop(); ++ if (iid == vmIntrinsics::_linkToVirtual || ++ iid == vmIntrinsics::_linkToSpecial) { ++ // could do this for all kinds, but would explode assembly code size ++ trace_method_handle(_masm, "bad Method*::intrinsic_id"); ++ } ++ __ STOP("bad Method*::intrinsic_id"); ++ __ bind(L); ++ BLOCK_COMMENT("} verify_intrinsic_id"); ++ } ++ ++ // First task: Find out how big the argument list is. ++ Address t9_first_arg_addr; ++ int ref_kind = signature_polymorphic_intrinsic_ref_kind(iid); ++ assert(ref_kind != 0 || iid == vmIntrinsics::_invokeBasic, "must be _invokeBasic or a linkTo intrinsic"); ++ if (ref_kind == 0 || MethodHandles::ref_kind_has_receiver(ref_kind)) { ++ __ ld(t9_argp, Address(rm_method, Method::const_offset())); ++ __ load_sized_value(t9_argp, ++ Address(t9_argp, ConstMethod::size_of_parameters_offset()), ++ sizeof(u2), false); ++ // assert(sizeof(u2) == sizeof(Method::_size_of_parameters), ""); ++ t9_first_arg_addr = __ argument_address(t9_argp, -1); ++ } else { ++ DEBUG_ONLY(t9_argp = noreg); ++ } ++ ++ if (!is_signature_polymorphic_static(iid)) { ++ __ ld(s7_mh, t9_first_arg_addr); ++ DEBUG_ONLY(t9_argp = noreg); ++ } ++ ++ // t9_first_arg_addr is live! ++ ++ trace_method_handle_interpreter_entry(_masm, iid); ++ ++ if (iid == vmIntrinsics::_invokeBasic) { ++ generate_method_handle_dispatch(_masm, iid, s7_mh, noreg, not_for_compiler_entry); ++ ++ } else { ++ // Adjust argument list by popping the trailing MemberName argument. ++ Register r_recv = noreg; ++ if (MethodHandles::ref_kind_has_receiver(ref_kind)) { ++ // Load the receiver (not the MH; the actual MemberName's receiver) up from the interpreter stack. ++ __ ld(r_recv = T2, t9_first_arg_addr); ++ } ++ DEBUG_ONLY(t9_argp = noreg); ++ Register rm_member = rm_method; // MemberName ptr; incoming method ptr is dead now ++ __ pop(rm_member); // extract last argument ++ generate_method_handle_dispatch(_masm, iid, r_recv, rm_member, not_for_compiler_entry); ++ } ++ ++ return entry_point; ++} ++ ++void MethodHandles::generate_method_handle_dispatch(MacroAssembler* _masm, ++ vmIntrinsics::ID iid, ++ Register receiver_reg, ++ Register member_reg, ++ bool for_compiler_entry) { ++ assert(is_signature_polymorphic(iid), "expected invoke iid"); ++ Register rm_method = Rmethod; // eventual target of this invocation ++ // temps used in this code are not used in *either* compiled or interpreted calling sequences ++ Register j_rarg0 = T0; ++ Register j_rarg1 = A0; ++ Register j_rarg2 = A1; ++ Register j_rarg3 = A2; ++ Register j_rarg4 = A3; ++ Register j_rarg5 = A4; ++ ++ Register temp1 = T8; ++ Register temp2 = T9; ++ Register temp3 = V0; ++ if (for_compiler_entry) { ++ assert(receiver_reg == (iid == vmIntrinsics::_linkToStatic ? noreg : j_rarg0), "only valid assignment"); ++ assert_different_registers(temp1, j_rarg0, j_rarg1, j_rarg2, j_rarg3, j_rarg4, j_rarg5); ++ assert_different_registers(temp2, j_rarg0, j_rarg1, j_rarg2, j_rarg3, j_rarg4, j_rarg5); ++ assert_different_registers(temp3, j_rarg0, j_rarg1, j_rarg2, j_rarg3, j_rarg4, j_rarg5); ++ } ++ else { ++ assert_different_registers(temp1, temp2, temp3, saved_last_sp_register()); // don't trash lastSP ++ } ++ assert_different_registers(temp1, temp2, temp3, receiver_reg); ++ assert_different_registers(temp1, temp2, temp3, member_reg); ++ ++ if (iid == vmIntrinsics::_invokeBasic) { ++ // indirect through MH.form.vmentry.vmtarget ++ jump_to_lambda_form(_masm, receiver_reg, rm_method, temp1, for_compiler_entry); ++ ++ } else { ++ // The method is a member invoker used by direct method handles. ++ if (VerifyMethodHandles) { ++ // make sure the trailing argument really is a MemberName (caller responsibility) ++ verify_klass(_masm, member_reg, SystemDictionary::WK_KLASS_ENUM_NAME(java_lang_invoke_MemberName), ++ "MemberName required for invokeVirtual etc."); ++ } ++ ++ Address member_clazz( member_reg, NONZERO(java_lang_invoke_MemberName::clazz_offset_in_bytes())); ++ Address member_vmindex( member_reg, NONZERO(java_lang_invoke_MemberName::vmindex_offset_in_bytes())); ++ Address member_vmtarget( member_reg, NONZERO(java_lang_invoke_MemberName::vmtarget_offset_in_bytes())); ++ ++ Register temp1_recv_klass = temp1; ++ if (iid != vmIntrinsics::_linkToStatic) { ++ __ verify_oop(receiver_reg); ++ if (iid == vmIntrinsics::_linkToSpecial) { ++ // Don't actually load the klass; just null-check the receiver. ++ __ null_check(receiver_reg); ++ } else { ++ // load receiver klass itself ++ __ null_check(receiver_reg, oopDesc::klass_offset_in_bytes()); ++ __ load_klass(temp1_recv_klass, receiver_reg); ++ __ verify_klass_ptr(temp1_recv_klass); ++ } ++ BLOCK_COMMENT("check_receiver {"); ++ // The receiver for the MemberName must be in receiver_reg. ++ // Check the receiver against the MemberName.clazz ++ if (VerifyMethodHandles && iid == vmIntrinsics::_linkToSpecial) { ++ // Did not load it above... ++ __ load_klass(temp1_recv_klass, receiver_reg); ++ __ verify_klass_ptr(temp1_recv_klass); ++ } ++ if (VerifyMethodHandles && iid != vmIntrinsics::_linkToInterface) { ++ Label L_ok; ++ Register temp2_defc = temp2; ++ __ load_heap_oop(temp2_defc, member_clazz); ++ load_klass_from_Class(_masm, temp2_defc); ++ __ verify_klass_ptr(temp2_defc); ++ __ check_klass_subtype(temp1_recv_klass, temp2_defc, temp3, L_ok); ++ // If we get here, the type check failed! ++ __ STOP("receiver class disagrees with MemberName.clazz"); ++ __ bind(L_ok); ++ } ++ BLOCK_COMMENT("} check_receiver"); ++ } ++ if (iid == vmIntrinsics::_linkToSpecial || ++ iid == vmIntrinsics::_linkToStatic) { ++ DEBUG_ONLY(temp1_recv_klass = noreg); // these guys didn't load the recv_klass ++ } ++ ++ // Live registers at this point: ++ // member_reg - MemberName that was the trailing argument ++ // temp1_recv_klass - klass of stacked receiver, if needed ++ ++ Label L_incompatible_class_change_error; ++ switch (iid) { ++ case vmIntrinsics::_linkToSpecial: ++ if (VerifyMethodHandles) { ++ verify_ref_kind(_masm, JVM_REF_invokeSpecial, member_reg, temp3); ++ } ++ __ ld(rm_method, member_vmtarget); ++ break; ++ ++ case vmIntrinsics::_linkToStatic: ++ if (VerifyMethodHandles) { ++ verify_ref_kind(_masm, JVM_REF_invokeStatic, member_reg, temp3); ++ } ++ __ ld(rm_method, member_vmtarget); ++ break; ++ ++ case vmIntrinsics::_linkToVirtual: ++ { ++ // same as TemplateTable::invokevirtual, ++ // minus the CP setup and profiling: ++ ++ if (VerifyMethodHandles) { ++ verify_ref_kind(_masm, JVM_REF_invokeVirtual, member_reg, temp3); ++ } ++ ++ // pick out the vtable index from the MemberName, and then we can discard it: ++ Register temp2_index = temp2; ++ __ ld(temp2_index, member_vmindex); ++ ++ if (VerifyMethodHandles) { ++ Label L_index_ok; ++ __ slt(AT, R0, temp2_index); ++ __ bne(AT, R0, L_index_ok); ++ __ delayed()->nop(); ++ __ STOP("no virtual index"); ++ __ BIND(L_index_ok); ++ } ++ ++ // Note: The verifier invariants allow us to ignore MemberName.clazz and vmtarget ++ // at this point. And VerifyMethodHandles has already checked clazz, if needed. ++ ++ // get target Method* & entry point ++ __ lookup_virtual_method(temp1_recv_klass, temp2_index, rm_method); ++ break; ++ } ++ ++ case vmIntrinsics::_linkToInterface: ++ { ++ // same as TemplateTable::invokeinterface ++ // (minus the CP setup and profiling, with different argument motion) ++ if (VerifyMethodHandles) { ++ verify_ref_kind(_masm, JVM_REF_invokeInterface, member_reg, temp3); ++ } ++ ++ Register temp3_intf = temp3; ++ __ load_heap_oop(temp3_intf, member_clazz); ++ load_klass_from_Class(_masm, temp3_intf); ++ __ verify_klass_ptr(temp3_intf); ++ ++ Register rm_index = rm_method; ++ __ ld(rm_index, member_vmindex); ++ if (VerifyMethodHandles) { ++ Label L; ++ __ slt(AT, rm_index, R0); ++ __ beq(AT, R0, L); ++ __ delayed()->nop(); ++ __ STOP("invalid vtable index for MH.invokeInterface"); ++ __ bind(L); ++ } ++ ++ // given intf, index, and recv klass, dispatch to the implementation method ++ __ lookup_interface_method(temp1_recv_klass, temp3_intf, ++ // note: next two args must be the same: ++ rm_index, rm_method, ++ temp2, ++ L_incompatible_class_change_error); ++ break; ++ } ++ ++ default: ++ fatal(err_msg_res("unexpected intrinsic %d: %s", iid, vmIntrinsics::name_at(iid))); ++ break; ++ } ++ ++ // Live at this point: ++ // rm_method ++ ++ // After figuring out which concrete method to call, jump into it. ++ // Note that this works in the interpreter with no data motion. ++ // But the compiled version will require that r_recv be shifted out. ++ __ verify_method_ptr(rm_method); ++ jump_from_method_handle(_masm, rm_method, temp1, for_compiler_entry); ++ ++ if (iid == vmIntrinsics::_linkToInterface) { ++ __ bind(L_incompatible_class_change_error); ++ address icce_entry= StubRoutines::throw_IncompatibleClassChangeError_entry(); ++ __ jmp(icce_entry, relocInfo::runtime_call_type); ++ __ delayed()->nop(); ++ } ++ } ++} ++ ++#ifndef PRODUCT ++void trace_method_handle_stub(const char* adaptername, ++ oop mh, ++ intptr_t* saved_regs, ++ intptr_t* entry_sp) { ++ // called as a leaf from native code: do not block the JVM! ++ bool has_mh = (strstr(adaptername, "/static") == NULL && ++ strstr(adaptername, "linkTo") == NULL); // static linkers don't have MH ++ const char* mh_reg_name = has_mh ? "s7_mh" : "s7"; ++ tty->print_cr("MH %s %s=" PTR_FORMAT " sp=" PTR_FORMAT, ++ adaptername, mh_reg_name, ++ p2i(mh), p2i(entry_sp)); ++ ++ if (Verbose) { ++ tty->print_cr("Registers:"); ++ const int saved_regs_count = RegisterImpl::number_of_registers; ++ for (int i = 0; i < saved_regs_count; i++) { ++ Register r = as_Register(i); ++ // The registers are stored in reverse order on the stack (by pusha). ++ tty->print("%3s=" PTR_FORMAT, r->name(), saved_regs[((saved_regs_count - 1) - i)]); ++ if ((i + 1) % 4 == 0) { ++ tty->cr(); ++ } else { ++ tty->print(", "); ++ } ++ } ++ tty->cr(); ++ ++ { ++ // dumping last frame with frame::describe ++ ++ JavaThread* p = JavaThread::active(); ++ ++ ResourceMark rm; ++ PRESERVE_EXCEPTION_MARK; // may not be needed by safer and unexpensive here ++ FrameValues values; ++ ++ // Note: We want to allow trace_method_handle from any call site. ++ // While trace_method_handle creates a frame, it may be entered ++ // without a PC on the stack top (e.g. not just after a call). ++ // Walking that frame could lead to failures due to that invalid PC. ++ // => carefully detect that frame when doing the stack walking ++ ++ // Current C frame ++ frame cur_frame = os::current_frame(); ++ ++ // Robust search of trace_calling_frame (independant of inlining). ++ // Assumes saved_regs comes from a pusha in the trace_calling_frame. ++ assert(cur_frame.sp() < saved_regs, "registers not saved on stack ?"); ++ frame trace_calling_frame = os::get_sender_for_C_frame(&cur_frame); ++ while (trace_calling_frame.fp() < saved_regs) { ++ trace_calling_frame = os::get_sender_for_C_frame(&trace_calling_frame); ++ } ++ ++ // safely create a frame and call frame::describe ++ intptr_t *dump_sp = trace_calling_frame.sender_sp(); ++ intptr_t *dump_fp = trace_calling_frame.link(); ++ ++ bool walkable = has_mh; // whether the traced frame shoud be walkable ++ ++ if (walkable) { ++ // The previous definition of walkable may have to be refined ++ // if new call sites cause the next frame constructor to start ++ // failing. Alternatively, frame constructors could be ++ // modified to support the current or future non walkable ++ // frames (but this is more intrusive and is not considered as ++ // part of this RFE, which will instead use a simpler output). ++ frame dump_frame = frame(dump_sp, dump_fp); ++ dump_frame.describe(values, 1); ++ } else { ++ // Stack may not be walkable (invalid PC above FP): ++ // Add descriptions without building a Java frame to avoid issues ++ values.describe(-1, dump_fp, "fp for #1 "); ++ values.describe(-1, dump_sp, "sp for #1"); ++ } ++ values.describe(-1, entry_sp, "raw top of stack"); ++ ++ tty->print_cr("Stack layout:"); ++ values.print(p); ++ } ++ if (has_mh && mh->is_oop()) { ++ mh->print(); ++ if (java_lang_invoke_MethodHandle::is_instance(mh)) { ++ if (java_lang_invoke_MethodHandle::form_offset_in_bytes() != 0) ++ java_lang_invoke_MethodHandle::form(mh)->print(); ++ } ++ } ++ } ++} ++ ++// The stub wraps the arguments in a struct on the stack to avoid ++// dealing with the different calling conventions for passing 6 ++// arguments. ++struct MethodHandleStubArguments { ++ const char* adaptername; ++ oopDesc* mh; ++ intptr_t* saved_regs; ++ intptr_t* entry_sp; ++}; ++void trace_method_handle_stub_wrapper(MethodHandleStubArguments* args) { ++ trace_method_handle_stub(args->adaptername, ++ args->mh, ++ args->saved_regs, ++ args->entry_sp); ++} ++ ++void MethodHandles::trace_method_handle(MacroAssembler* _masm, const char* adaptername) { ++} ++#endif //PRODUCT +diff --git a/hotspot/src/cpu/mips/vm/methodHandles_mips.hpp b/hotspot/src/cpu/mips/vm/methodHandles_mips.hpp +new file mode 100644 +index 0000000000..03b65fc8ef +--- /dev/null ++++ b/hotspot/src/cpu/mips/vm/methodHandles_mips.hpp +@@ -0,0 +1,62 @@ ++/* ++ * Copyright (c) 2010, 2012, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2015, 2022, Loongson Technology. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ * ++ */ ++ ++// Platform-specific definitions for method handles. ++// These definitions are inlined into class MethodHandles. ++ ++// Adapters ++enum /* platform_dependent_constants */ { ++ adapter_code_size = 32000 DEBUG_ONLY(+ 150000) ++}; ++ ++// Additional helper methods for MethodHandles code generation: ++public: ++ static void load_klass_from_Class(MacroAssembler* _masm, Register klass_reg); ++ ++ static void verify_klass(MacroAssembler* _masm, ++ Register obj, SystemDictionary::WKID klass_id, ++ const char* error_message = "wrong klass") NOT_DEBUG_RETURN; ++ ++ static void verify_method_handle(MacroAssembler* _masm, Register mh_reg) { ++ verify_klass(_masm, mh_reg, SystemDictionary::WK_KLASS_ENUM_NAME(java_lang_invoke_MethodHandle), ++ "reference is a MH"); ++ } ++ ++ static void verify_ref_kind(MacroAssembler* _masm, int ref_kind, Register member_reg, Register temp) NOT_DEBUG_RETURN; ++ ++ // Similar to InterpreterMacroAssembler::jump_from_interpreted. ++ // Takes care of special dispatch from single stepping too. ++ static void jump_from_method_handle(MacroAssembler* _masm, Register method, Register temp, ++ bool for_compiler_entry); ++ ++ static void jump_to_lambda_form(MacroAssembler* _masm, ++ Register recv, Register method_temp, ++ Register temp2, ++ bool for_compiler_entry); ++ ++ static Register saved_last_sp_register() { ++ // Should be in sharedRuntime, not here. ++ return I29; ++ } +diff --git a/hotspot/src/cpu/mips/vm/mips.ad b/hotspot/src/cpu/mips/vm/mips.ad +new file mode 100644 +index 0000000000..3563bbe0e5 +--- /dev/null ++++ b/hotspot/src/cpu/mips/vm/mips.ad +@@ -0,0 +1,25 @@ ++// ++// Copyright (c) 2011, 2012, Oracle and/or its affiliates. All rights reserved. ++// Copyright (c) 2015, 2016, Loongson Technology. All rights reserved. ++// DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++// ++// This code is free software; you can redistribute it and/or modify it ++// under the terms of the GNU General Public License version 2 only, as ++// published by the Free Software Foundation. ++// ++// This code is distributed in the hope that it will be useful, but WITHOUT ++// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++// version 2 for more details (a copy is included in the LICENSE file that ++// accompanied this code). ++// ++// You should have received a copy of the GNU General Public License version ++// 2 along with this work; if not, write to the Free Software Foundation, ++// Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++// ++// Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++// or visit www.oracle.com if you need additional information or have any ++// questions. ++// ++// ++ +diff --git a/hotspot/src/cpu/mips/vm/mips_64.ad b/hotspot/src/cpu/mips/vm/mips_64.ad +new file mode 100644 +index 0000000000..2d714c8be1 +--- /dev/null ++++ b/hotspot/src/cpu/mips/vm/mips_64.ad +@@ -0,0 +1,14035 @@ ++// ++// Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved. ++// Copyright (c) 2015, 2022, Loongson Technology. All rights reserved. ++// DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++// ++// This code is free software; you can redistribute it and/or modify it ++// under the terms of the GNU General Public License version 2 only, as ++// published by the Free Software Foundation. ++// ++// This code is distributed in the hope that it will be useful, but WITHOUT ++// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++// version 2 for more details (a copy is included in the LICENSE file that ++// accompanied this code). ++// ++// You should have received a copy of the GNU General Public License version ++// 2 along with this work; if not, write to the Free Software Foundation, ++// Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++// ++// Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++// or visit www.oracle.com if you need additional information or have any ++// questions. ++// ++// ++ ++// GodSon3 Architecture Description File ++ ++//----------REGISTER DEFINITION BLOCK------------------------------------------ ++// This information is used by the matcher and the register allocator to ++// describe individual registers and classes of registers within the target ++// archtecture. ++ ++// format: ++// reg_def name (call convention, c-call convention, ideal type, encoding); ++// call convention : ++// NS = No-Save ++// SOC = Save-On-Call ++// SOE = Save-On-Entry ++// AS = Always-Save ++// ideal type : ++// see opto/opcodes.hpp for more info ++// reg_class name (reg, ...); ++// alloc_class name (reg, ...); ++register %{ ++ ++// General Registers ++// Integer Registers ++ reg_def R0 ( NS, NS, Op_RegI, 0, VMRegImpl::Bad()); ++ reg_def AT ( NS, NS, Op_RegI, 1, AT->as_VMReg()); ++ reg_def AT_H ( NS, NS, Op_RegI, 1, AT->as_VMReg()->next()); ++ reg_def V0 (SOC, SOC, Op_RegI, 2, V0->as_VMReg()); ++ reg_def V0_H (SOC, SOC, Op_RegI, 2, V0->as_VMReg()->next()); ++ reg_def V1 (SOC, SOC, Op_RegI, 3, V1->as_VMReg()); ++ reg_def V1_H (SOC, SOC, Op_RegI, 3, V1->as_VMReg()->next()); ++ reg_def A0 (SOC, SOC, Op_RegI, 4, A0->as_VMReg()); ++ reg_def A0_H (SOC, SOC, Op_RegI, 4, A0->as_VMReg()->next()); ++ reg_def A1 (SOC, SOC, Op_RegI, 5, A1->as_VMReg()); ++ reg_def A1_H (SOC, SOC, Op_RegI, 5, A1->as_VMReg()->next()); ++ reg_def A2 (SOC, SOC, Op_RegI, 6, A2->as_VMReg()); ++ reg_def A2_H (SOC, SOC, Op_RegI, 6, A2->as_VMReg()->next()); ++ reg_def A3 (SOC, SOC, Op_RegI, 7, A3->as_VMReg()); ++ reg_def A3_H (SOC, SOC, Op_RegI, 7, A3->as_VMReg()->next()); ++ reg_def A4 (SOC, SOC, Op_RegI, 8, A4->as_VMReg()); ++ reg_def A4_H (SOC, SOC, Op_RegI, 8, A4->as_VMReg()->next()); ++ reg_def A5 (SOC, SOC, Op_RegI, 9, A5->as_VMReg()); ++ reg_def A5_H (SOC, SOC, Op_RegI, 9, A5->as_VMReg()->next()); ++ reg_def A6 (SOC, SOC, Op_RegI, 10, A6->as_VMReg()); ++ reg_def A6_H (SOC, SOC, Op_RegI, 10, A6->as_VMReg()->next()); ++ reg_def A7 (SOC, SOC, Op_RegI, 11, A7->as_VMReg()); ++ reg_def A7_H (SOC, SOC, Op_RegI, 11, A7->as_VMReg()->next()); ++ reg_def T0 (SOC, SOC, Op_RegI, 12, T0->as_VMReg()); ++ reg_def T0_H (SOC, SOC, Op_RegI, 12, T0->as_VMReg()->next()); ++ reg_def T1 (SOC, SOC, Op_RegI, 13, T1->as_VMReg()); ++ reg_def T1_H (SOC, SOC, Op_RegI, 13, T1->as_VMReg()->next()); ++ reg_def T2 (SOC, SOC, Op_RegI, 14, T2->as_VMReg()); ++ reg_def T2_H (SOC, SOC, Op_RegI, 14, T2->as_VMReg()->next()); ++ reg_def T3 (SOC, SOC, Op_RegI, 15, T3->as_VMReg()); ++ reg_def T3_H (SOC, SOC, Op_RegI, 15, T3->as_VMReg()->next()); ++ reg_def S0 (SOC, SOE, Op_RegI, 16, S0->as_VMReg()); ++ reg_def S0_H (SOC, SOE, Op_RegI, 16, S0->as_VMReg()->next()); ++ reg_def S1 (SOC, SOE, Op_RegI, 17, S1->as_VMReg()); ++ reg_def S1_H (SOC, SOE, Op_RegI, 17, S1->as_VMReg()->next()); ++ reg_def S2 (SOC, SOE, Op_RegI, 18, S2->as_VMReg()); ++ reg_def S2_H (SOC, SOE, Op_RegI, 18, S2->as_VMReg()->next()); ++ reg_def S3 (SOC, SOE, Op_RegI, 19, S3->as_VMReg()); ++ reg_def S3_H (SOC, SOE, Op_RegI, 19, S3->as_VMReg()->next()); ++ reg_def S4 (SOC, SOE, Op_RegI, 20, S4->as_VMReg()); ++ reg_def S4_H (SOC, SOE, Op_RegI, 20, S4->as_VMReg()->next()); ++ reg_def S5 (SOC, SOE, Op_RegI, 21, S5->as_VMReg()); ++ reg_def S5_H (SOC, SOE, Op_RegI, 21, S5->as_VMReg()->next()); ++ reg_def S6 (SOC, SOE, Op_RegI, 22, S6->as_VMReg()); ++ reg_def S6_H (SOC, SOE, Op_RegI, 22, S6->as_VMReg()->next()); ++ reg_def S7 (SOC, SOE, Op_RegI, 23, S7->as_VMReg()); ++ reg_def S7_H (SOC, SOE, Op_RegI, 23, S7->as_VMReg()->next()); ++ reg_def T8 (SOC, SOC, Op_RegI, 24, T8->as_VMReg()); ++ reg_def T8_H (SOC, SOC, Op_RegI, 24, T8->as_VMReg()->next()); ++ reg_def T9 (SOC, SOC, Op_RegI, 25, T9->as_VMReg()); ++ reg_def T9_H (SOC, SOC, Op_RegI, 25, T9->as_VMReg()->next()); ++ ++// Special Registers ++ reg_def K0 ( NS, NS, Op_RegI, 26, K0->as_VMReg()); ++ reg_def K1 ( NS, NS, Op_RegI, 27, K1->as_VMReg()); ++ reg_def GP ( NS, NS, Op_RegI, 28, GP->as_VMReg()); ++ reg_def GP_H ( NS, NS, Op_RegI, 28, GP->as_VMReg()->next()); ++ reg_def SP ( NS, NS, Op_RegI, 29, SP->as_VMReg()); ++ reg_def SP_H ( NS, NS, Op_RegI, 29, SP->as_VMReg()->next()); ++ reg_def FP ( NS, NS, Op_RegI, 30, FP->as_VMReg()); ++ reg_def FP_H ( NS, NS, Op_RegI, 30, FP->as_VMReg()->next()); ++ reg_def RA ( NS, NS, Op_RegI, 31, RA->as_VMReg()); ++ reg_def RA_H ( NS, NS, Op_RegI, 31, RA->as_VMReg()->next()); ++ ++// Floating registers. ++reg_def F0 ( SOC, SOC, Op_RegF, 0, F0->as_VMReg()); ++reg_def F0_H ( SOC, SOC, Op_RegF, 0, F0->as_VMReg()->next()); ++reg_def F1 ( SOC, SOC, Op_RegF, 1, F1->as_VMReg()); ++reg_def F1_H ( SOC, SOC, Op_RegF, 1, F1->as_VMReg()->next()); ++reg_def F2 ( SOC, SOC, Op_RegF, 2, F2->as_VMReg()); ++reg_def F2_H ( SOC, SOC, Op_RegF, 2, F2->as_VMReg()->next()); ++reg_def F3 ( SOC, SOC, Op_RegF, 3, F3->as_VMReg()); ++reg_def F3_H ( SOC, SOC, Op_RegF, 3, F3->as_VMReg()->next()); ++reg_def F4 ( SOC, SOC, Op_RegF, 4, F4->as_VMReg()); ++reg_def F4_H ( SOC, SOC, Op_RegF, 4, F4->as_VMReg()->next()); ++reg_def F5 ( SOC, SOC, Op_RegF, 5, F5->as_VMReg()); ++reg_def F5_H ( SOC, SOC, Op_RegF, 5, F5->as_VMReg()->next()); ++reg_def F6 ( SOC, SOC, Op_RegF, 6, F6->as_VMReg()); ++reg_def F6_H ( SOC, SOC, Op_RegF, 6, F6->as_VMReg()->next()); ++reg_def F7 ( SOC, SOC, Op_RegF, 7, F7->as_VMReg()); ++reg_def F7_H ( SOC, SOC, Op_RegF, 7, F7->as_VMReg()->next()); ++reg_def F8 ( SOC, SOC, Op_RegF, 8, F8->as_VMReg()); ++reg_def F8_H ( SOC, SOC, Op_RegF, 8, F8->as_VMReg()->next()); ++reg_def F9 ( SOC, SOC, Op_RegF, 9, F9->as_VMReg()); ++reg_def F9_H ( SOC, SOC, Op_RegF, 9, F9->as_VMReg()->next()); ++reg_def F10 ( SOC, SOC, Op_RegF, 10, F10->as_VMReg()); ++reg_def F10_H ( SOC, SOC, Op_RegF, 10, F10->as_VMReg()->next()); ++reg_def F11 ( SOC, SOC, Op_RegF, 11, F11->as_VMReg()); ++reg_def F11_H ( SOC, SOC, Op_RegF, 11, F11->as_VMReg()->next()); ++reg_def F12 ( SOC, SOC, Op_RegF, 12, F12->as_VMReg()); ++reg_def F12_H ( SOC, SOC, Op_RegF, 12, F12->as_VMReg()->next()); ++reg_def F13 ( SOC, SOC, Op_RegF, 13, F13->as_VMReg()); ++reg_def F13_H ( SOC, SOC, Op_RegF, 13, F13->as_VMReg()->next()); ++reg_def F14 ( SOC, SOC, Op_RegF, 14, F14->as_VMReg()); ++reg_def F14_H ( SOC, SOC, Op_RegF, 14, F14->as_VMReg()->next()); ++reg_def F15 ( SOC, SOC, Op_RegF, 15, F15->as_VMReg()); ++reg_def F15_H ( SOC, SOC, Op_RegF, 15, F15->as_VMReg()->next()); ++reg_def F16 ( SOC, SOC, Op_RegF, 16, F16->as_VMReg()); ++reg_def F16_H ( SOC, SOC, Op_RegF, 16, F16->as_VMReg()->next()); ++reg_def F17 ( SOC, SOC, Op_RegF, 17, F17->as_VMReg()); ++reg_def F17_H ( SOC, SOC, Op_RegF, 17, F17->as_VMReg()->next()); ++reg_def F18 ( SOC, SOC, Op_RegF, 18, F18->as_VMReg()); ++reg_def F18_H ( SOC, SOC, Op_RegF, 18, F18->as_VMReg()->next()); ++reg_def F19 ( SOC, SOC, Op_RegF, 19, F19->as_VMReg()); ++reg_def F19_H ( SOC, SOC, Op_RegF, 19, F19->as_VMReg()->next()); ++reg_def F20 ( SOC, SOC, Op_RegF, 20, F20->as_VMReg()); ++reg_def F20_H ( SOC, SOC, Op_RegF, 20, F20->as_VMReg()->next()); ++reg_def F21 ( SOC, SOC, Op_RegF, 21, F21->as_VMReg()); ++reg_def F21_H ( SOC, SOC, Op_RegF, 21, F21->as_VMReg()->next()); ++reg_def F22 ( SOC, SOC, Op_RegF, 22, F22->as_VMReg()); ++reg_def F22_H ( SOC, SOC, Op_RegF, 22, F22->as_VMReg()->next()); ++reg_def F23 ( SOC, SOC, Op_RegF, 23, F23->as_VMReg()); ++reg_def F23_H ( SOC, SOC, Op_RegF, 23, F23->as_VMReg()->next()); ++reg_def F24 ( SOC, SOC, Op_RegF, 24, F24->as_VMReg()); ++reg_def F24_H ( SOC, SOC, Op_RegF, 24, F24->as_VMReg()->next()); ++reg_def F25 ( SOC, SOC, Op_RegF, 25, F25->as_VMReg()); ++reg_def F25_H ( SOC, SOC, Op_RegF, 25, F25->as_VMReg()->next()); ++reg_def F26 ( SOC, SOC, Op_RegF, 26, F26->as_VMReg()); ++reg_def F26_H ( SOC, SOC, Op_RegF, 26, F26->as_VMReg()->next()); ++reg_def F27 ( SOC, SOC, Op_RegF, 27, F27->as_VMReg()); ++reg_def F27_H ( SOC, SOC, Op_RegF, 27, F27->as_VMReg()->next()); ++reg_def F28 ( SOC, SOC, Op_RegF, 28, F28->as_VMReg()); ++reg_def F28_H ( SOC, SOC, Op_RegF, 28, F28->as_VMReg()->next()); ++reg_def F29 ( SOC, SOC, Op_RegF, 29, F29->as_VMReg()); ++reg_def F29_H ( SOC, SOC, Op_RegF, 29, F29->as_VMReg()->next()); ++reg_def F30 ( SOC, SOC, Op_RegF, 30, F30->as_VMReg()); ++reg_def F30_H ( SOC, SOC, Op_RegF, 30, F30->as_VMReg()->next()); ++reg_def F31 ( SOC, SOC, Op_RegF, 31, F31->as_VMReg()); ++reg_def F31_H ( SOC, SOC, Op_RegF, 31, F31->as_VMReg()->next()); ++ ++ ++// ---------------------------- ++// Special Registers ++//S6 is used for get_thread(S6) ++//S5 is uesd for heapbase of compressed oop ++alloc_class chunk0( ++ S7, S7_H, ++ S0, S0_H, ++ S1, S1_H, ++ S2, S2_H, ++ S4, S4_H, ++ S5, S5_H, ++ S6, S6_H, ++ S3, S3_H, ++ T2, T2_H, ++ T3, T3_H, ++ T8, T8_H, ++ T9, T9_H, ++ T1, T1_H, // inline_cache_reg ++ V1, V1_H, ++ A7, A7_H, ++ A6, A6_H, ++ A5, A5_H, ++ A4, A4_H, ++ V0, V0_H, ++ A3, A3_H, ++ A2, A2_H, ++ A1, A1_H, ++ A0, A0_H, ++ T0, T0_H, ++ GP, GP_H ++ RA, RA_H, ++ SP, SP_H, // stack_pointer ++ FP, FP_H // frame_pointer ++ ); ++ ++alloc_class chunk1( F0, F0_H, ++ F1, F1_H, ++ F2, F2_H, ++ F3, F3_H, ++ F4, F4_H, ++ F5, F5_H, ++ F6, F6_H, ++ F7, F7_H, ++ F8, F8_H, ++ F9, F9_H, ++ F10, F10_H, ++ F11, F11_H, ++ F20, F20_H, ++ F21, F21_H, ++ F22, F22_H, ++ F23, F23_H, ++ F24, F24_H, ++ F25, F25_H, ++ F26, F26_H, ++ F27, F27_H, ++ F28, F28_H, ++ F19, F19_H, ++ F18, F18_H, ++ F17, F17_H, ++ F16, F16_H, ++ F15, F15_H, ++ F14, F14_H, ++ F13, F13_H, ++ F12, F12_H, ++ F29, F29_H, ++ F30, F30_H, ++ F31, F31_H); ++ ++reg_class s_reg( S0, S1, S2, S3, S4, S5, S6, S7 ); ++reg_class s0_reg( S0 ); ++reg_class s1_reg( S1 ); ++reg_class s2_reg( S2 ); ++reg_class s3_reg( S3 ); ++reg_class s4_reg( S4 ); ++reg_class s5_reg( S5 ); ++reg_class s6_reg( S6 ); ++reg_class s7_reg( S7 ); ++ ++reg_class t_reg( T0, T1, T2, T3, T8, T9 ); ++reg_class t0_reg( T0 ); ++reg_class t1_reg( T1 ); ++reg_class t2_reg( T2 ); ++reg_class t3_reg( T3 ); ++reg_class t8_reg( T8 ); ++reg_class t9_reg( T9 ); ++ ++reg_class a_reg( A0, A1, A2, A3, A4, A5, A6, A7 ); ++reg_class a0_reg( A0 ); ++reg_class a1_reg( A1 ); ++reg_class a2_reg( A2 ); ++reg_class a3_reg( A3 ); ++reg_class a4_reg( A4 ); ++reg_class a5_reg( A5 ); ++reg_class a6_reg( A6 ); ++reg_class a7_reg( A7 ); ++ ++reg_class v0_reg( V0 ); ++reg_class v1_reg( V1 ); ++ ++reg_class sp_reg( SP, SP_H ); ++reg_class fp_reg( FP, FP_H ); ++ ++reg_class v0_long_reg( V0, V0_H ); ++reg_class v1_long_reg( V1, V1_H ); ++reg_class a0_long_reg( A0, A0_H ); ++reg_class a1_long_reg( A1, A1_H ); ++reg_class a2_long_reg( A2, A2_H ); ++reg_class a3_long_reg( A3, A3_H ); ++reg_class a4_long_reg( A4, A4_H ); ++reg_class a5_long_reg( A5, A5_H ); ++reg_class a6_long_reg( A6, A6_H ); ++reg_class a7_long_reg( A7, A7_H ); ++reg_class t0_long_reg( T0, T0_H ); ++reg_class t1_long_reg( T1, T1_H ); ++reg_class t2_long_reg( T2, T2_H ); ++reg_class t3_long_reg( T3, T3_H ); ++reg_class t8_long_reg( T8, T8_H ); ++reg_class t9_long_reg( T9, T9_H ); ++reg_class s0_long_reg( S0, S0_H ); ++reg_class s1_long_reg( S1, S1_H ); ++reg_class s2_long_reg( S2, S2_H ); ++reg_class s3_long_reg( S3, S3_H ); ++reg_class s4_long_reg( S4, S4_H ); ++reg_class s5_long_reg( S5, S5_H ); ++reg_class s6_long_reg( S6, S6_H ); ++reg_class s7_long_reg( S7, S7_H ); ++ ++reg_class int_reg( S7, S0, S1, S2, S4, S3, T8, T2, T3, T1, V1, A7, A6, A5, A4, V0, A3, A2, A1, A0, T0 ); ++ ++reg_class no_Ax_int_reg( S7, S0, S1, S2, S4, S3, T8, T2, T3, T1, V1, V0, T0 ); ++ ++reg_class p_reg( ++ S7, S7_H, ++ S0, S0_H, ++ S1, S1_H, ++ S2, S2_H, ++ S4, S4_H, ++ S3, S3_H, ++ T8, T8_H, ++ T2, T2_H, ++ T3, T3_H, ++ T1, T1_H, ++ A7, A7_H, ++ A6, A6_H, ++ A5, A5_H, ++ A4, A4_H, ++ A3, A3_H, ++ A2, A2_H, ++ A1, A1_H, ++ A0, A0_H, ++ T0, T0_H ++ ); ++ ++reg_class no_T8_p_reg( ++ S7, S7_H, ++ S0, S0_H, ++ S1, S1_H, ++ S2, S2_H, ++ S4, S4_H, ++ S3, S3_H, ++ T2, T2_H, ++ T3, T3_H, ++ T1, T1_H, ++ A7, A7_H, ++ A6, A6_H, ++ A5, A5_H, ++ A4, A4_H, ++ A3, A3_H, ++ A2, A2_H, ++ A1, A1_H, ++ A0, A0_H, ++ T0, T0_H ++ ); ++ ++reg_class long_reg( ++ S7, S7_H, ++ S0, S0_H, ++ S1, S1_H, ++ S2, S2_H, ++ S4, S4_H, ++ S3, S3_H, ++ T8, T8_H, ++ T2, T2_H, ++ T3, T3_H, ++ T1, T1_H, ++ A7, A7_H, ++ A6, A6_H, ++ A5, A5_H, ++ A4, A4_H, ++ A3, A3_H, ++ A2, A2_H, ++ A1, A1_H, ++ A0, A0_H, ++ T0, T0_H ++ ); ++ ++ ++// Floating point registers. ++// F31 are not used as temporary registers in D2I ++reg_class flt_reg( F0, F1, F2, F3, F4, F5, F6, F7, F8, F9, F10, F11, F12, F13, F14, F15, F16, F17, F18, F19, F20, F21, F22, F23, F24, F25, F26, F27, F28, F29, F31); ++reg_class dbl_reg( F0, F0_H, ++ F1, F1_H, ++ F2, F2_H, ++ F3, F3_H, ++ F4, F4_H, ++ F5, F5_H, ++ F6, F6_H, ++ F7, F7_H, ++ F8, F8_H, ++ F9, F9_H, ++ F10, F10_H, ++ F11, F11_H, ++ F12, F12_H, ++ F13, F13_H, ++ F14, F14_H, ++ F15, F15_H, ++ F16, F16_H, ++ F17, F17_H, ++ F18, F18_H, ++ F19, F19_H, ++ F20, F20_H, ++ F21, F21_H, ++ F22, F22_H, ++ F23, F23_H, ++ F24, F24_H, ++ F25, F25_H, ++ F26, F26_H, ++ F27, F27_H, ++ F28, F28_H, ++ F29, F29_H, ++ F31, F31_H); ++ ++reg_class flt_arg0( F12 ); ++reg_class dbl_arg0( F12, F12_H ); ++reg_class dbl_arg1( F14, F14_H ); ++ ++%} ++ ++//----------DEFINITION BLOCK--------------------------------------------------- ++// Define name --> value mappings to inform the ADLC of an integer valued name ++// Current support includes integer values in the range [0, 0x7FFFFFFF] ++// Format: ++// int_def ( , ); ++// Generated Code in ad_.hpp ++// #define () ++// // value == ++// Generated code in ad_.cpp adlc_verification() ++// assert( == , "Expect () to equal "); ++// ++definitions %{ ++ int_def DEFAULT_COST ( 100, 100); ++ int_def HUGE_COST (1000000, 1000000); ++ ++ // Memory refs are twice as expensive as run-of-the-mill. ++ int_def MEMORY_REF_COST ( 200, DEFAULT_COST * 2); ++ ++ // Branches are even more expensive. ++ int_def BRANCH_COST ( 300, DEFAULT_COST * 3); ++ // we use jr instruction to construct call, so more expensive ++ int_def CALL_COST ( 500, DEFAULT_COST * 5); ++/* ++ int_def EQUAL ( 1, 1 ); ++ int_def NOT_EQUAL ( 2, 2 ); ++ int_def GREATER ( 3, 3 ); ++ int_def GREATER_EQUAL ( 4, 4 ); ++ int_def LESS ( 5, 5 ); ++ int_def LESS_EQUAL ( 6, 6 ); ++*/ ++%} ++ ++ ++ ++//----------SOURCE BLOCK------------------------------------------------------- ++// This is a block of C++ code which provides values, functions, and ++// definitions necessary in the rest of the architecture description ++ ++source_hpp %{ ++// Header information of the source block. ++// Method declarations/definitions which are used outside ++// the ad-scope can conveniently be defined here. ++// ++// To keep related declarations/definitions/uses close together, ++// we switch between source %{ }% and source_hpp %{ }% freely as needed. ++ ++class CallStubImpl { ++ ++ //-------------------------------------------------------------- ++ //---< Used for optimization in Compile::shorten_branches >--- ++ //-------------------------------------------------------------- ++ ++ public: ++ // Size of call trampoline stub. ++ static uint size_call_trampoline() { ++ return 0; // no call trampolines on this platform ++ } ++ ++ // number of relocations needed by a call trampoline stub ++ static uint reloc_call_trampoline() { ++ return 0; // no call trampolines on this platform ++ } ++}; ++ ++class HandlerImpl { ++ ++ public: ++ ++ static int emit_exception_handler(CodeBuffer &cbuf); ++ static int emit_deopt_handler(CodeBuffer& cbuf); ++ ++ static uint size_exception_handler() { ++ // NativeCall instruction size is the same as NativeJump. ++ // exception handler starts out as jump and can be patched to ++ // a call be deoptimization. (4932387) ++ // Note that this value is also credited (in output.cpp) to ++ // the size of the code section. ++ int size = NativeCall::instruction_size; ++ return round_to(size, 16); ++ } ++ ++ static uint size_deopt_handler() { ++ int size = NativeCall::instruction_size; ++ return round_to(size, 16); ++ } ++}; ++ ++%} // end source_hpp ++ ++source %{ ++ ++#define NO_INDEX 0 ++#define RELOC_IMM64 Assembler::imm_operand ++#define RELOC_DISP32 Assembler::disp32_operand ++ ++ ++#define __ _masm. ++ ++#define A0 RA0 ++#define A1 RA1 ++#define A2 RA2 ++#define A3 RA3 ++#define A4 RA4 ++#define A5 RA5 ++#define A6 RA6 ++#define A7 RA7 ++#define T0 RT0 ++#define T1 RT1 ++#define T2 RT2 ++#define T3 RT3 ++#define T8 RT8 ++#define T9 RT9 ++ ++ ++// Emit exception handler code. ++// Stuff framesize into a register and call a VM stub routine. ++int HandlerImpl::emit_exception_handler(CodeBuffer& cbuf) { ++ // Note that the code buffer's insts_mark is always relative to insts. ++ // That's why we must use the macroassembler to generate a handler. ++ MacroAssembler _masm(&cbuf); ++ address base = __ start_a_stub(size_exception_handler()); ++ if (base == NULL) { ++ ciEnv::current()->record_failure("CodeCache is full"); ++ return 0; // CodeBuffer::expand failed ++ } ++ ++ int offset = __ offset(); ++ ++ __ block_comment("; emit_exception_handler"); ++ ++ cbuf.set_insts_mark(); ++ __ relocate(relocInfo::runtime_call_type); ++ __ patchable_jump((address)OptoRuntime::exception_blob()->entry_point()); ++ __ align(16); ++ assert(__ offset() - offset <= (int) size_exception_handler(), "overflow"); ++ __ end_a_stub(); ++ return offset; ++} ++ ++// Emit deopt handler code. ++int HandlerImpl::emit_deopt_handler(CodeBuffer& cbuf) { ++ // Note that the code buffer's insts_mark is always relative to insts. ++ // That's why we must use the macroassembler to generate a handler. ++ MacroAssembler _masm(&cbuf); ++ address base = __ start_a_stub(size_deopt_handler()); ++ if (base == NULL) { ++ ciEnv::current()->record_failure("CodeCache is full"); ++ return 0; // CodeBuffer::expand failed ++ } ++ ++ int offset = __ offset(); ++ ++ __ block_comment("; emit_deopt_handler"); ++ ++ cbuf.set_insts_mark(); ++ __ relocate(relocInfo::runtime_call_type); ++ __ patchable_call(SharedRuntime::deopt_blob()->unpack()); ++ __ align(16); ++ assert(__ offset() - offset <= (int) size_deopt_handler(), "overflow"); ++ __ end_a_stub(); ++ return offset; ++} ++ ++ ++const bool Matcher::match_rule_supported(int opcode) { ++ if (!has_match_rule(opcode)) ++ return false; ++ ++ switch (opcode) { ++ //Op_CountLeadingZerosI Op_CountLeadingZerosL can be deleted, all MIPS CPUs support clz & dclz. ++ case Op_CountLeadingZerosI: ++ case Op_CountLeadingZerosL: ++ if (!UseCountLeadingZerosInstructionMIPS64) ++ return false; ++ break; ++ case Op_CountTrailingZerosI: ++ case Op_CountTrailingZerosL: ++ if (!UseCountTrailingZerosInstructionMIPS64) ++ return false; ++ break; ++ } ++ ++ return true; // Per default match rules are supported. ++} ++ ++bool Matcher::is_short_branch_offset(int rule, int br_size, int offset) { ++ int offs = offset - br_size + 4; ++ // To be conservative on MIPS ++ // branch node should be end with: ++ // branch inst ++ // delay slot ++ const int safety_zone = 3 * BytesPerInstWord; ++ return Assembler::is_simm16((offs<0 ? offs-safety_zone : offs+safety_zone) >> 2); ++} ++ ++ ++// No additional cost for CMOVL. ++const int Matcher::long_cmove_cost() { return 0; } ++ ++// No CMOVF/CMOVD with SSE2 ++const int Matcher::float_cmove_cost() { return ConditionalMoveLimit; } ++ ++// Does the CPU require late expand (see block.cpp for description of late expand)? ++const bool Matcher::require_postalloc_expand = false; ++ ++// Should the Matcher clone shifts on addressing modes, expecting them ++// to be subsumed into complex addressing expressions or compute them ++// into registers? True for Intel but false for most RISCs ++const bool Matcher::clone_shift_expressions = false; ++ ++// Do we need to mask the count passed to shift instructions or does ++// the cpu only look at the lower 5/6 bits anyway? ++const bool Matcher::need_masked_shift_count = false; ++ ++bool Matcher::narrow_oop_use_complex_address() { ++ assert(UseCompressedOops, "only for compressed oops code"); ++ return false; ++} ++ ++bool Matcher::narrow_klass_use_complex_address() { ++ assert(UseCompressedClassPointers, "only for compressed klass code"); ++ return false; ++} ++ ++// This is UltraSparc specific, true just means we have fast l2f conversion ++const bool Matcher::convL2FSupported(void) { ++ return true; ++} ++ ++// Max vector size in bytes. 0 if not supported. ++const int Matcher::vector_width_in_bytes(BasicType bt) { ++ if (MaxVectorSize == 0) ++ return 0; ++ assert(MaxVectorSize == 8, ""); ++ return 8; ++} ++ ++// Vector ideal reg ++const uint Matcher::vector_ideal_reg(int size) { ++ assert(MaxVectorSize == 8, ""); ++ switch(size) { ++ case 8: return Op_VecD; ++ } ++ ShouldNotReachHere(); ++ return 0; ++} ++ ++// Only lowest bits of xmm reg are used for vector shift count. ++const uint Matcher::vector_shift_count_ideal_reg(int size) { ++ fatal("vector shift is not supported"); ++ return Node::NotAMachineReg; ++} ++ ++// Limits on vector size (number of elements) loaded into vector. ++const int Matcher::max_vector_size(const BasicType bt) { ++ assert(is_java_primitive(bt), "only primitive type vectors"); ++ return vector_width_in_bytes(bt)/type2aelembytes(bt); ++} ++ ++const int Matcher::min_vector_size(const BasicType bt) { ++ return max_vector_size(bt); // Same as max. ++} ++ ++// MIPS supports misaligned vectors store/load? FIXME ++const bool Matcher::misaligned_vectors_ok() { ++ return false; ++ //return !AlignVector; // can be changed by flag ++} ++ ++// Register for DIVI projection of divmodI ++RegMask Matcher::divI_proj_mask() { ++ ShouldNotReachHere(); ++ return RegMask(); ++} ++ ++// Register for MODI projection of divmodI ++RegMask Matcher::modI_proj_mask() { ++ ShouldNotReachHere(); ++ return RegMask(); ++} ++ ++// Register for DIVL projection of divmodL ++RegMask Matcher::divL_proj_mask() { ++ ShouldNotReachHere(); ++ return RegMask(); ++} ++ ++int Matcher::regnum_to_fpu_offset(int regnum) { ++ return regnum - 32; // The FP registers are in the second chunk ++} ++ ++ ++const bool Matcher::isSimpleConstant64(jlong value) { ++ // Will one (StoreL ConL) be cheaper than two (StoreI ConI)?. ++ return true; ++} ++ ++ ++// Return whether or not this register is ever used as an argument. This ++// function is used on startup to build the trampoline stubs in generateOptoStub. ++// Registers not mentioned will be killed by the VM call in the trampoline, and ++// arguments in those registers not be available to the callee. ++bool Matcher::can_be_java_arg( int reg ) { ++ // Refer to: [sharedRuntime_mips_64.cpp] SharedRuntime::java_calling_convention() ++ if ( reg == T0_num || reg == T0_H_num ++ || reg == A0_num || reg == A0_H_num ++ || reg == A1_num || reg == A1_H_num ++ || reg == A2_num || reg == A2_H_num ++ || reg == A3_num || reg == A3_H_num ++ || reg == A4_num || reg == A4_H_num ++ || reg == A5_num || reg == A5_H_num ++ || reg == A6_num || reg == A6_H_num ++ || reg == A7_num || reg == A7_H_num ) ++ return true; ++ ++ if ( reg == F12_num || reg == F12_H_num ++ || reg == F13_num || reg == F13_H_num ++ || reg == F14_num || reg == F14_H_num ++ || reg == F15_num || reg == F15_H_num ++ || reg == F16_num || reg == F16_H_num ++ || reg == F17_num || reg == F17_H_num ++ || reg == F18_num || reg == F18_H_num ++ || reg == F19_num || reg == F19_H_num ) ++ return true; ++ ++ return false; ++} ++ ++bool Matcher::is_spillable_arg( int reg ) { ++ return can_be_java_arg(reg); ++} ++ ++bool Matcher::use_asm_for_ldiv_by_con( jlong divisor ) { ++ return false; ++} ++ ++// Register for MODL projection of divmodL ++RegMask Matcher::modL_proj_mask() { ++ ShouldNotReachHere(); ++ return RegMask(); ++} ++ ++const RegMask Matcher::method_handle_invoke_SP_save_mask() { ++ return FP_REG_mask(); ++} ++ ++// MIPS doesn't support AES intrinsics ++const bool Matcher::pass_original_key_for_aes() { ++ return false; ++} ++ ++int CallStaticJavaDirectNode::compute_padding(int current_offset) const { ++ return round_to(current_offset, alignment_required()) - current_offset; ++} ++ ++int CallDynamicJavaDirectNode::compute_padding(int current_offset) const { ++ return round_to(current_offset, alignment_required()) - current_offset; ++} ++ ++int CallLeafNoFPDirectNode::compute_padding(int current_offset) const { ++ return round_to(current_offset, alignment_required()) - current_offset; ++} ++ ++int CallLeafDirectNode::compute_padding(int current_offset) const { ++ return round_to(current_offset, alignment_required()) - current_offset; ++} ++ ++int CallRuntimeDirectNode::compute_padding(int current_offset) const { ++ return round_to(current_offset, alignment_required()) - current_offset; ++} ++ ++// If CPU can load and store mis-aligned doubles directly then no fixup is ++// needed. Else we split the double into 2 integer pieces and move it ++// piece-by-piece. Only happens when passing doubles into C code as the ++// Java calling convention forces doubles to be aligned. ++const bool Matcher::misaligned_doubles_ok = false; ++// Do floats take an entire double register or just half? ++//const bool Matcher::float_in_double = true; ++bool Matcher::float_in_double() { return false; } ++// Threshold size for cleararray. ++const int Matcher::init_array_short_size = 8 * BytesPerLong; ++// Do ints take an entire long register or just half? ++const bool Matcher::int_in_long = true; ++// Is it better to copy float constants, or load them directly from memory? ++// Intel can load a float constant from a direct address, requiring no ++// extra registers. Most RISCs will have to materialize an address into a ++// register first, so they would do better to copy the constant from stack. ++const bool Matcher::rematerialize_float_constants = false; ++// Advertise here if the CPU requires explicit rounding operations ++// to implement the UseStrictFP mode. ++const bool Matcher::strict_fp_requires_explicit_rounding = false; ++// false => size gets scaled to BytesPerLong, ok. ++const bool Matcher::init_array_count_is_in_bytes = false; ++ ++// Indicate if the safepoint node needs the polling page as an input. ++// Since MIPS doesn't have absolute addressing, it needs. ++bool SafePointNode::needs_polling_address_input() { ++ return false; ++} ++ ++// !!!!! Special hack to get all type of calls to specify the byte offset ++// from the start of the call to the point where the return address ++// will point. ++int MachCallStaticJavaNode::ret_addr_offset() { ++ //lui ++ //ori ++ //nop ++ //nop ++ //jalr ++ //nop ++ return 24; ++} ++ ++int MachCallDynamicJavaNode::ret_addr_offset() { ++ //lui IC_Klass, ++ //ori IC_Klass, ++ //dsll IC_Klass ++ //ori IC_Klass ++ ++ //lui T9 ++ //ori T9 ++ //nop ++ //nop ++ //jalr T9 ++ //nop ++ return 4 * 4 + 4 * 6; ++} ++ ++//============================================================================= ++ ++// Figure out which register class each belongs in: rc_int, rc_float, rc_stack ++enum RC { rc_bad, rc_int, rc_float, rc_stack }; ++static enum RC rc_class( OptoReg::Name reg ) { ++ if( !OptoReg::is_valid(reg) ) return rc_bad; ++ if (OptoReg::is_stack(reg)) return rc_stack; ++ VMReg r = OptoReg::as_VMReg(reg); ++ if (r->is_Register()) return rc_int; ++ assert(r->is_FloatRegister(), "must be"); ++ return rc_float; ++} ++ ++uint MachSpillCopyNode::implementation( CodeBuffer *cbuf, PhaseRegAlloc *ra_, bool do_size, outputStream* st ) const { ++ // Get registers to move ++ OptoReg::Name src_second = ra_->get_reg_second(in(1)); ++ OptoReg::Name src_first = ra_->get_reg_first(in(1)); ++ OptoReg::Name dst_second = ra_->get_reg_second(this ); ++ OptoReg::Name dst_first = ra_->get_reg_first(this ); ++ ++ enum RC src_second_rc = rc_class(src_second); ++ enum RC src_first_rc = rc_class(src_first); ++ enum RC dst_second_rc = rc_class(dst_second); ++ enum RC dst_first_rc = rc_class(dst_first); ++ ++ assert(OptoReg::is_valid(src_first) && OptoReg::is_valid(dst_first), "must move at least 1 register" ); ++ ++ // Generate spill code! ++ int size = 0; ++ ++ if( src_first == dst_first && src_second == dst_second ) ++ return 0; // Self copy, no move ++ ++ if (src_first_rc == rc_stack) { ++ // mem -> ++ if (dst_first_rc == rc_stack) { ++ // mem -> mem ++ assert(src_second != dst_first, "overlap"); ++ if ((src_first & 1) == 0 && src_first + 1 == src_second && ++ (dst_first & 1) == 0 && dst_first + 1 == dst_second) { ++ // 64-bit ++ int src_offset = ra_->reg2offset(src_first); ++ int dst_offset = ra_->reg2offset(dst_first); ++ if (cbuf) { ++ MacroAssembler _masm(cbuf); ++ __ ld(AT, Address(SP, src_offset)); ++ __ sd(AT, Address(SP, dst_offset)); ++#ifndef PRODUCT ++ } else { ++ if(!do_size){ ++ if (size != 0) st->print("\n\t"); ++ st->print("ld AT, [SP + #%d]\t# 64-bit mem-mem spill 1\n\t" ++ "sd AT, [SP + #%d]", ++ src_offset, dst_offset); ++ } ++#endif ++ } ++ size += 8; ++ } else { ++ // 32-bit ++ assert(!((src_first & 1) == 0 && src_first + 1 == src_second), "no transform"); ++ assert(!((dst_first & 1) == 0 && dst_first + 1 == dst_second), "no transform"); ++ // No pushl/popl, so: ++ int src_offset = ra_->reg2offset(src_first); ++ int dst_offset = ra_->reg2offset(dst_first); ++ if (cbuf) { ++ MacroAssembler _masm(cbuf); ++ __ lw(AT, Address(SP, src_offset)); ++ __ sw(AT, Address(SP, dst_offset)); ++#ifndef PRODUCT ++ } else { ++ if(!do_size){ ++ if (size != 0) st->print("\n\t"); ++ st->print("lw AT, [SP + #%d] spill 2\n\t" ++ "sw AT, [SP + #%d]\n\t", ++ src_offset, dst_offset); ++ } ++#endif ++ } ++ size += 8; ++ } ++ return size; ++ } else if (dst_first_rc == rc_int) { ++ // mem -> gpr ++ if ((src_first & 1) == 0 && src_first + 1 == src_second && ++ (dst_first & 1) == 0 && dst_first + 1 == dst_second) { ++ // 64-bit ++ int offset = ra_->reg2offset(src_first); ++ if (cbuf) { ++ MacroAssembler _masm(cbuf); ++ __ ld(as_Register(Matcher::_regEncode[dst_first]), Address(SP, offset)); ++#ifndef PRODUCT ++ } else { ++ if(!do_size){ ++ if (size != 0) st->print("\n\t"); ++ st->print("ld %s, [SP + #%d]\t# spill 3", ++ Matcher::regName[dst_first], ++ offset); ++ } ++#endif ++ } ++ size += 4; ++ } else { ++ // 32-bit ++ assert(!((src_first & 1) == 0 && src_first + 1 == src_second), "no transform"); ++ assert(!((dst_first & 1) == 0 && dst_first + 1 == dst_second), "no transform"); ++ int offset = ra_->reg2offset(src_first); ++ if (cbuf) { ++ MacroAssembler _masm(cbuf); ++ if (this->ideal_reg() == Op_RegI) ++ __ lw(as_Register(Matcher::_regEncode[dst_first]), Address(SP, offset)); ++ else ++ __ lwu(as_Register(Matcher::_regEncode[dst_first]), Address(SP, offset)); ++#ifndef PRODUCT ++ } else { ++ if(!do_size){ ++ if (size != 0) st->print("\n\t"); ++ if (this->ideal_reg() == Op_RegI) ++ st->print("lw %s, [SP + #%d]\t# spill 4", ++ Matcher::regName[dst_first], ++ offset); ++ else ++ st->print("lwu %s, [SP + #%d]\t# spill 5", ++ Matcher::regName[dst_first], ++ offset); ++ } ++#endif ++ } ++ size += 4; ++ } ++ return size; ++ } else if (dst_first_rc == rc_float) { ++ // mem-> xmm ++ if ((src_first & 1) == 0 && src_first + 1 == src_second && ++ (dst_first & 1) == 0 && dst_first + 1 == dst_second) { ++ // 64-bit ++ int offset = ra_->reg2offset(src_first); ++ if (cbuf) { ++ MacroAssembler _masm(cbuf); ++ __ ldc1( as_FloatRegister(Matcher::_regEncode[dst_first]), Address(SP, offset)); ++#ifndef PRODUCT ++ } else { ++ if (!do_size) { ++ if (size != 0) st->print("\n\t"); ++ st->print("ldc1 %s, [SP + #%d]\t# spill 6", ++ Matcher::regName[dst_first], ++ offset); ++ } ++#endif ++ } ++ size += 4; ++ } else { ++ // 32-bit ++ assert(!((src_first & 1) == 0 && src_first + 1 == src_second), "no transform"); ++ assert(!((dst_first & 1) == 0 && dst_first + 1 == dst_second), "no transform"); ++ int offset = ra_->reg2offset(src_first); ++ if (cbuf) { ++ MacroAssembler _masm(cbuf); ++ __ lwc1( as_FloatRegister(Matcher::_regEncode[dst_first]), Address(SP, offset)); ++#ifndef PRODUCT ++ } else { ++ if(!do_size){ ++ if (size != 0) st->print("\n\t"); ++ st->print("lwc1 %s, [SP + #%d]\t# spill 7", ++ Matcher::regName[dst_first], ++ offset); ++ } ++#endif ++ } ++ size += 4; ++ } ++ return size; ++ } ++ } else if (src_first_rc == rc_int) { ++ // gpr -> ++ if (dst_first_rc == rc_stack) { ++ // gpr -> mem ++ if ((src_first & 1) == 0 && src_first + 1 == src_second && ++ (dst_first & 1) == 0 && dst_first + 1 == dst_second) { ++ // 64-bit ++ int offset = ra_->reg2offset(dst_first); ++ if (cbuf) { ++ MacroAssembler _masm(cbuf); ++ __ sd(as_Register(Matcher::_regEncode[src_first]), Address(SP, offset)); ++#ifndef PRODUCT ++ } else { ++ if(!do_size){ ++ if (size != 0) st->print("\n\t"); ++ st->print("sd %s, [SP + #%d] # spill 8", ++ Matcher::regName[src_first], ++ offset); ++ } ++#endif ++ } ++ size += 4; ++ } else { ++ // 32-bit ++ assert(!((src_first & 1) == 0 && src_first + 1 == src_second), "no transform"); ++ assert(!((dst_first & 1) == 0 && dst_first + 1 == dst_second), "no transform"); ++ int offset = ra_->reg2offset(dst_first); ++ if (cbuf) { ++ MacroAssembler _masm(cbuf); ++ __ sw(as_Register(Matcher::_regEncode[src_first]), Address(SP, offset)); ++#ifndef PRODUCT ++ } else { ++ if (!do_size) { ++ if (size != 0) st->print("\n\t"); ++ st->print("sw %s, [SP + #%d]\t# spill 9", ++ Matcher::regName[src_first], offset); ++ } ++#endif ++ } ++ size += 4; ++ } ++ return size; ++ } else if (dst_first_rc == rc_int) { ++ // gpr -> gpr ++ if ((src_first & 1) == 0 && src_first + 1 == src_second && ++ (dst_first & 1) == 0 && dst_first + 1 == dst_second) { ++ // 64-bit ++ if (cbuf) { ++ MacroAssembler _masm(cbuf); ++ __ move(as_Register(Matcher::_regEncode[dst_first]), ++ as_Register(Matcher::_regEncode[src_first])); ++#ifndef PRODUCT ++ } else { ++ if(!do_size){ ++ if (size != 0) st->print("\n\t"); ++ st->print("move(64bit) %s <-- %s\t# spill 10", ++ Matcher::regName[dst_first], ++ Matcher::regName[src_first]); ++ } ++#endif ++ } ++ size += 4; ++ return size; ++ } else { ++ // 32-bit ++ assert(!((src_first & 1) == 0 && src_first + 1 == src_second), "no transform"); ++ assert(!((dst_first & 1) == 0 && dst_first + 1 == dst_second), "no transform"); ++ if (cbuf) { ++ MacroAssembler _masm(cbuf); ++ if (this->ideal_reg() == Op_RegI) ++ __ move_u32(as_Register(Matcher::_regEncode[dst_first]), as_Register(Matcher::_regEncode[src_first])); ++ else ++ __ daddu(as_Register(Matcher::_regEncode[dst_first]), as_Register(Matcher::_regEncode[src_first]), R0); ++#ifndef PRODUCT ++ } else { ++ if (!do_size) { ++ if (size != 0) st->print("\n\t"); ++ st->print("move(32-bit) %s <-- %s\t# spill 11", ++ Matcher::regName[dst_first], ++ Matcher::regName[src_first]); ++ } ++#endif ++ } ++ size += 4; ++ return size; ++ } ++ } else if (dst_first_rc == rc_float) { ++ // gpr -> xmm ++ if ((src_first & 1) == 0 && src_first + 1 == src_second && ++ (dst_first & 1) == 0 && dst_first + 1 == dst_second) { ++ // 64-bit ++ if (cbuf) { ++ MacroAssembler _masm(cbuf); ++ __ dmtc1(as_Register(Matcher::_regEncode[src_first]), as_FloatRegister(Matcher::_regEncode[dst_first])); ++#ifndef PRODUCT ++ } else { ++ if(!do_size){ ++ if (size != 0) st->print("\n\t"); ++ st->print("dmtc1 %s, %s\t# spill 12", ++ Matcher::regName[dst_first], ++ Matcher::regName[src_first]); ++ } ++#endif ++ } ++ size += 4; ++ } else { ++ // 32-bit ++ assert(!((src_first & 1) == 0 && src_first + 1 == src_second), "no transform"); ++ assert(!((dst_first & 1) == 0 && dst_first + 1 == dst_second), "no transform"); ++ if (cbuf) { ++ MacroAssembler _masm(cbuf); ++ __ mtc1( as_Register(Matcher::_regEncode[src_first]), as_FloatRegister(Matcher::_regEncode[dst_first]) ); ++#ifndef PRODUCT ++ } else { ++ if(!do_size){ ++ if (size != 0) st->print("\n\t"); ++ st->print("mtc1 %s, %s\t# spill 13", ++ Matcher::regName[dst_first], ++ Matcher::regName[src_first]); ++ } ++#endif ++ } ++ size += 4; ++ } ++ return size; ++ } ++ } else if (src_first_rc == rc_float) { ++ // xmm -> ++ if (dst_first_rc == rc_stack) { ++ // xmm -> mem ++ if ((src_first & 1) == 0 && src_first + 1 == src_second && ++ (dst_first & 1) == 0 && dst_first + 1 == dst_second) { ++ // 64-bit ++ int offset = ra_->reg2offset(dst_first); ++ if (cbuf) { ++ MacroAssembler _masm(cbuf); ++ __ sdc1( as_FloatRegister(Matcher::_regEncode[src_first]), Address(SP, offset) ); ++#ifndef PRODUCT ++ } else { ++ if(!do_size){ ++ if (size != 0) st->print("\n\t"); ++ st->print("sdc1 %s, [SP + #%d]\t# spill 14", ++ Matcher::regName[src_first], ++ offset); ++ } ++#endif ++ } ++ size += 4; ++ } else { ++ // 32-bit ++ assert(!((src_first & 1) == 0 && src_first + 1 == src_second), "no transform"); ++ assert(!((dst_first & 1) == 0 && dst_first + 1 == dst_second), "no transform"); ++ int offset = ra_->reg2offset(dst_first); ++ if (cbuf) { ++ MacroAssembler _masm(cbuf); ++ __ swc1(as_FloatRegister(Matcher::_regEncode[src_first]), Address(SP, offset)); ++#ifndef PRODUCT ++ } else { ++ if(!do_size){ ++ if (size != 0) st->print("\n\t"); ++ st->print("swc1 %s, [SP + #%d]\t# spill 15", ++ Matcher::regName[src_first], ++ offset); ++ } ++#endif ++ } ++ size += 4; ++ } ++ return size; ++ } else if (dst_first_rc == rc_int) { ++ // xmm -> gpr ++ if ((src_first & 1) == 0 && src_first + 1 == src_second && ++ (dst_first & 1) == 0 && dst_first + 1 == dst_second) { ++ // 64-bit ++ if (cbuf) { ++ MacroAssembler _masm(cbuf); ++ __ dmfc1( as_Register(Matcher::_regEncode[dst_first]), as_FloatRegister(Matcher::_regEncode[src_first])); ++#ifndef PRODUCT ++ } else { ++ if(!do_size){ ++ if (size != 0) st->print("\n\t"); ++ st->print("dmfc1 %s, %s\t# spill 16", ++ Matcher::regName[dst_first], ++ Matcher::regName[src_first]); ++ } ++#endif ++ } ++ size += 4; ++ } else { ++ // 32-bit ++ assert(!((src_first & 1) == 0 && src_first + 1 == src_second), "no transform"); ++ assert(!((dst_first & 1) == 0 && dst_first + 1 == dst_second), "no transform"); ++ if (cbuf) { ++ MacroAssembler _masm(cbuf); ++ __ mfc1( as_Register(Matcher::_regEncode[dst_first]), as_FloatRegister(Matcher::_regEncode[src_first])); ++#ifndef PRODUCT ++ } else { ++ if(!do_size){ ++ if (size != 0) st->print("\n\t"); ++ st->print("mfc1 %s, %s\t# spill 17", ++ Matcher::regName[dst_first], ++ Matcher::regName[src_first]); ++ } ++#endif ++ } ++ size += 4; ++ } ++ return size; ++ } else if (dst_first_rc == rc_float) { ++ // xmm -> xmm ++ if ((src_first & 1) == 0 && src_first + 1 == src_second && ++ (dst_first & 1) == 0 && dst_first + 1 == dst_second) { ++ // 64-bit ++ if (cbuf) { ++ MacroAssembler _masm(cbuf); ++ __ mov_d( as_FloatRegister(Matcher::_regEncode[dst_first]), as_FloatRegister(Matcher::_regEncode[src_first])); ++#ifndef PRODUCT ++ } else { ++ if(!do_size){ ++ if (size != 0) st->print("\n\t"); ++ st->print("mov_d %s <-- %s\t# spill 18", ++ Matcher::regName[dst_first], ++ Matcher::regName[src_first]); ++ } ++#endif ++ } ++ size += 4; ++ } else { ++ // 32-bit ++ assert(!((src_first & 1) == 0 && src_first + 1 == src_second), "no transform"); ++ assert(!((dst_first & 1) == 0 && dst_first + 1 == dst_second), "no transform"); ++ if (cbuf) { ++ MacroAssembler _masm(cbuf); ++ __ mov_s( as_FloatRegister(Matcher::_regEncode[dst_first]), as_FloatRegister(Matcher::_regEncode[src_first])); ++#ifndef PRODUCT ++ } else { ++ if(!do_size){ ++ if (size != 0) st->print("\n\t"); ++ st->print("mov_s %s <-- %s\t# spill 19", ++ Matcher::regName[dst_first], ++ Matcher::regName[src_first]); ++ } ++#endif ++ } ++ size += 4; ++ } ++ return size; ++ } ++ } ++ ++ assert(0," foo "); ++ Unimplemented(); ++ return size; ++ ++} ++ ++#ifndef PRODUCT ++void MachSpillCopyNode::format( PhaseRegAlloc *ra_, outputStream* st ) const { ++ implementation( NULL, ra_, false, st ); ++} ++#endif ++ ++void MachSpillCopyNode::emit(CodeBuffer &cbuf, PhaseRegAlloc *ra_) const { ++ implementation( &cbuf, ra_, false, NULL ); ++} ++ ++uint MachSpillCopyNode::size(PhaseRegAlloc *ra_) const { ++ return MachNode::size(ra_); ++} ++ ++//============================================================================= ++# ++ ++#ifndef PRODUCT ++void MachBreakpointNode::format( PhaseRegAlloc *, outputStream* st ) const { ++ st->print("BRK"); ++} ++#endif ++ ++void MachBreakpointNode::emit(CodeBuffer &cbuf, PhaseRegAlloc* ra_) const { ++ MacroAssembler _masm(&cbuf); ++ __ brk(5); ++} ++ ++uint MachBreakpointNode::size(PhaseRegAlloc* ra_) const { ++ return MachNode::size(ra_); ++} ++ ++ ++//============================================================================= ++#ifndef PRODUCT ++void MachEpilogNode::format( PhaseRegAlloc *ra_, outputStream* st ) const { ++ Compile *C = ra_->C; ++ int framesize = C->frame_size_in_bytes(); ++ ++ assert((framesize & (StackAlignmentInBytes-1)) == 0, "frame size not aligned"); ++ ++ st->print_cr("daddiu SP, SP, %d # Rlease stack @ MachEpilogNode", framesize); ++ st->print("\t"); ++ if (UseLEXT1) { ++ st->print_cr("gslq RA, FP, SP, %d # Restore FP & RA @ MachEpilogNode", -wordSize*2); ++ } else { ++ st->print_cr("ld RA, SP, %d # Restore RA @ MachEpilogNode", -wordSize); ++ st->print("\t"); ++ st->print_cr("ld FP, SP, %d # Restore FP @ MachEpilogNode", -wordSize*2); ++ } ++ ++ if( do_polling() && C->is_method_compilation() ) { ++ st->print("\t"); ++ st->print_cr("Poll Safepoint # MachEpilogNode"); ++ } ++} ++#endif ++ ++void MachEpilogNode::emit(CodeBuffer &cbuf, PhaseRegAlloc *ra_) const { ++ Compile *C = ra_->C; ++ MacroAssembler _masm(&cbuf); ++ int framesize = C->frame_size_in_bytes(); ++ ++ assert((framesize & (StackAlignmentInBytes-1)) == 0, "frame size not aligned"); ++ ++ if (UseLEXT1) { ++ __ gslq(RA, FP, SP, framesize - wordSize * 2); ++ } else { ++ __ ld(RA, SP, framesize - wordSize ); ++ __ ld(FP, SP, framesize - wordSize * 2); ++ } ++ __ daddiu(SP, SP, framesize); ++ ++ if( do_polling() && C->is_method_compilation() ) { ++ __ set64(AT, (long)os::get_polling_page()); ++ __ relocate(relocInfo::poll_return_type); ++ __ lw(AT, AT, 0); ++ } ++} ++ ++uint MachEpilogNode::size(PhaseRegAlloc *ra_) const { ++ return MachNode::size(ra_); // too many variables; just compute it the hard way fujie debug ++} ++ ++int MachEpilogNode::reloc() const { ++ return 0; // a large enough number ++} ++ ++const Pipeline * MachEpilogNode::pipeline() const { ++ return MachNode::pipeline_class(); ++} ++ ++int MachEpilogNode::safepoint_offset() const { return 0; } ++ ++//============================================================================= ++ ++#ifndef PRODUCT ++void BoxLockNode::format( PhaseRegAlloc *ra_, outputStream* st ) const { ++ int offset = ra_->reg2offset(in_RegMask(0).find_first_elem()); ++ int reg = ra_->get_reg_first(this); ++ st->print("ADDI %s, SP, %d @BoxLockNode",Matcher::regName[reg],offset); ++} ++#endif ++ ++ ++uint BoxLockNode::size(PhaseRegAlloc *ra_) const { ++ return 4; ++} ++ ++void BoxLockNode::emit(CodeBuffer &cbuf, PhaseRegAlloc *ra_) const { ++ MacroAssembler _masm(&cbuf); ++ int offset = ra_->reg2offset(in_RegMask(0).find_first_elem()); ++ int reg = ra_->get_encode(this); ++ ++ __ addiu(as_Register(reg), SP, offset); ++} ++ ++ ++//static int sizeof_FFree_Float_Stack_All = -1; ++ ++int MachCallRuntimeNode::ret_addr_offset() { ++ //lui ++ //ori ++ //dsll ++ //ori ++ //jalr ++ //nop ++ assert(NativeCall::instruction_size == 24, "in MachCallRuntimeNode::ret_addr_offset()"); ++ return NativeCall::instruction_size; ++} ++ ++ ++//============================================================================= ++#ifndef PRODUCT ++void MachNopNode::format( PhaseRegAlloc *, outputStream* st ) const { ++ st->print("NOP \t# %d bytes pad for loops and calls", 4 * _count); ++} ++#endif ++ ++void MachNopNode::emit(CodeBuffer &cbuf, PhaseRegAlloc * ) const { ++ MacroAssembler _masm(&cbuf); ++ int i = 0; ++ for(i = 0; i < _count; i++) ++ __ nop(); ++} ++ ++uint MachNopNode::size(PhaseRegAlloc *) const { ++ return 4 * _count; ++} ++const Pipeline* MachNopNode::pipeline() const { ++ return MachNode::pipeline_class(); ++} ++ ++//============================================================================= ++ ++//============================================================================= ++#ifndef PRODUCT ++void MachUEPNode::format( PhaseRegAlloc *ra_, outputStream* st ) const { ++ st->print_cr("load_klass(T9, T0)"); ++ st->print_cr("\tbeq(T9, iCache, L)"); ++ st->print_cr("\tnop"); ++ st->print_cr("\tjmp(SharedRuntime::get_ic_miss_stub(), relocInfo::runtime_call_type)"); ++ st->print_cr("\tnop"); ++ st->print_cr("\tnop"); ++ st->print_cr(" L:"); ++} ++#endif ++ ++ ++void MachUEPNode::emit(CodeBuffer &cbuf, PhaseRegAlloc *ra_) const { ++ MacroAssembler _masm(&cbuf); ++ int ic_reg = Matcher::inline_cache_reg_encode(); ++ Label L; ++ Register receiver = T0; ++ Register iCache = as_Register(ic_reg); ++ ++ __ load_klass(T9, receiver); ++ __ beq(T9, iCache, L); ++ __ delayed()->nop(); ++ __ jmp((address)SharedRuntime::get_ic_miss_stub(), relocInfo::runtime_call_type); ++ __ delayed()->nop(); ++ __ bind(L); ++} ++ ++uint MachUEPNode::size(PhaseRegAlloc *ra_) const { ++ return MachNode::size(ra_); ++} ++ ++ ++ ++//============================================================================= ++ ++const RegMask& MachConstantBaseNode::_out_RegMask = P_REG_mask(); ++ ++int Compile::ConstantTable::calculate_table_base_offset() const { ++ return 0; // absolute addressing, no offset ++} ++ ++bool MachConstantBaseNode::requires_postalloc_expand() const { return false; } ++void MachConstantBaseNode::postalloc_expand(GrowableArray *nodes, PhaseRegAlloc *ra_) { ++ ShouldNotReachHere(); ++} ++ ++void MachConstantBaseNode::emit(CodeBuffer& cbuf, PhaseRegAlloc* ra_) const { ++ Compile* C = ra_->C; ++ Compile::ConstantTable& constant_table = C->constant_table(); ++ MacroAssembler _masm(&cbuf); ++ ++ Register Rtoc = as_Register(ra_->get_encode(this)); ++ CodeSection* consts_section = __ code()->consts(); ++ int consts_size = consts_section->align_at_start(consts_section->size()); ++ assert(constant_table.size() == consts_size, "must be equal"); ++ ++ if (consts_section->size()) { ++ // Materialize the constant table base. ++ address baseaddr = consts_section->start() + -(constant_table.table_base_offset()); ++ // RelocationHolder rspec = internal_word_Relocation::spec(baseaddr); ++ __ relocate(relocInfo::internal_word_type); ++ __ patchable_set48(Rtoc, (long)baseaddr); ++ } ++} ++ ++uint MachConstantBaseNode::size(PhaseRegAlloc* ra_) const { ++ // patchable_set48 (4 insts) ++ return 4 * 4; ++} ++ ++#ifndef PRODUCT ++void MachConstantBaseNode::format(PhaseRegAlloc* ra_, outputStream* st) const { ++ Register r = as_Register(ra_->get_encode(this)); ++ st->print("patchable_set48 %s, &constanttable (constant table base) @ MachConstantBaseNode", r->name()); ++} ++#endif ++ ++ ++//============================================================================= ++#ifndef PRODUCT ++void MachPrologNode::format( PhaseRegAlloc *ra_, outputStream* st ) const { ++ Compile* C = ra_->C; ++ ++ int framesize = C->frame_size_in_bytes(); ++ int bangsize = C->bang_size_in_bytes(); ++ assert((framesize & (StackAlignmentInBytes-1)) == 0, "frame size not aligned"); ++ ++ // Calls to C2R adapters often do not accept exceptional returns. ++ // We require that their callers must bang for them. But be careful, because ++ // some VM calls (such as call site linkage) can use several kilobytes of ++ // stack. But the stack safety zone should account for that. ++ // See bugs 4446381, 4468289, 4497237. ++ if (C->need_stack_bang(bangsize)) { ++ st->print_cr("# stack bang"); st->print("\t"); ++ } ++ if (UseLEXT1) { ++ st->print("gssq RA, FP, %d(SP) @ MachPrologNode\n\t", -wordSize*2); ++ } else { ++ st->print("sd RA, %d(SP) @ MachPrologNode\n\t", -wordSize); ++ st->print("sd FP, %d(SP) @ MachPrologNode\n\t", -wordSize*2); ++ } ++ st->print("daddiu FP, SP, -%d \n\t", wordSize*2); ++ st->print("daddiu SP, SP, -%d \t",framesize); ++} ++#endif ++ ++ ++void MachPrologNode::emit(CodeBuffer &cbuf, PhaseRegAlloc *ra_) const { ++ Compile* C = ra_->C; ++ MacroAssembler _masm(&cbuf); ++ ++ int framesize = C->frame_size_in_bytes(); ++ int bangsize = C->bang_size_in_bytes(); ++ ++ assert((framesize & (StackAlignmentInBytes-1)) == 0, "frame size not aligned"); ++ ++ // Make enough room for patch_verified_entry ++ __ nop(); ++ __ nop(); ++ ++ if (C->need_stack_bang(bangsize)) { ++ __ generate_stack_overflow_check(bangsize); ++ } ++ ++ __ daddiu(SP, SP, -framesize); ++ if (UseLEXT1) { ++ __ gssq(RA, FP, SP, framesize - wordSize * 2); ++ } else { ++ __ sd(RA, SP, framesize - wordSize); ++ __ sd(FP, SP, framesize - wordSize * 2); ++ } ++ __ daddiu(FP, SP, framesize - wordSize * 2); ++ ++ C->set_frame_complete(cbuf.insts_size()); ++ if (C->has_mach_constant_base_node()) { ++ // NOTE: We set the table base offset here because users might be ++ // emitted before MachConstantBaseNode. ++ Compile::ConstantTable& constant_table = C->constant_table(); ++ constant_table.set_table_base_offset(constant_table.calculate_table_base_offset()); ++ } ++} ++ ++ ++uint MachPrologNode::size(PhaseRegAlloc *ra_) const { ++ return MachNode::size(ra_); // too many variables; just compute it the hard way ++} ++ ++int MachPrologNode::reloc() const { ++ return 0; // a large enough number ++} ++ ++%} ++ ++//----------ENCODING BLOCK----------------------------------------------------- ++// This block specifies the encoding classes used by the compiler to output ++// byte streams. Encoding classes generate functions which are called by ++// Machine Instruction Nodes in order to generate the bit encoding of the ++// instruction. Operands specify their base encoding interface with the ++// interface keyword. There are currently supported four interfaces, ++// REG_INTER, CONST_INTER, MEMORY_INTER, & COND_INTER. REG_INTER causes an ++// operand to generate a function which returns its register number when ++// queried. CONST_INTER causes an operand to generate a function which ++// returns the value of the constant when queried. MEMORY_INTER causes an ++// operand to generate four functions which return the Base Register, the ++// Index Register, the Scale Value, and the Offset Value of the operand when ++// queried. COND_INTER causes an operand to generate six functions which ++// return the encoding code (ie - encoding bits for the instruction) ++// associated with each basic boolean condition for a conditional instruction. ++// Instructions specify two basic values for encoding. They use the ++// ins_encode keyword to specify their encoding class (which must be one of ++// the class names specified in the encoding block), and they use the ++// opcode keyword to specify, in order, their primary, secondary, and ++// tertiary opcode. Only the opcode sections which a particular instruction ++// needs for encoding need to be specified. ++encode %{ ++ ++ //Load byte signed ++ enc_class load_B_enc (mRegI dst, memory mem) %{ ++ MacroAssembler _masm(&cbuf); ++ int dst = $dst$$reg; ++ int base = $mem$$base; ++ int index = $mem$$index; ++ int scale = $mem$$scale; ++ int disp = $mem$$disp; ++ ++ if( index != 0 ) { ++ if( Assembler::is_simm16(disp) ) { ++ if (UseLEXT1) { ++ if (scale == 0) { ++ __ gslbx(as_Register(dst), as_Register(base), as_Register(index), disp); ++ } else { ++ __ dsll(AT, as_Register(index), scale); ++ __ gslbx(as_Register(dst), as_Register(base), AT, disp); ++ } ++ } else { ++ if (scale == 0) { ++ __ addu(AT, as_Register(base), as_Register(index)); ++ } else { ++ __ dsll(AT, as_Register(index), scale); ++ __ addu(AT, as_Register(base), AT); ++ } ++ __ lb(as_Register(dst), AT, disp); ++ } ++ } else { ++ if (scale == 0) { ++ __ addu(AT, as_Register(base), as_Register(index)); ++ } else { ++ __ dsll(AT, as_Register(index), scale); ++ __ addu(AT, as_Register(base), AT); ++ } ++ __ move(T9, disp); ++ if (UseLEXT1) { ++ __ gslbx(as_Register(dst), AT, T9, 0); ++ } else { ++ __ addu(AT, AT, T9); ++ __ lb(as_Register(dst), AT, 0); ++ } ++ } ++ } else { ++ if( Assembler::is_simm16(disp) ) { ++ __ lb(as_Register(dst), as_Register(base), disp); ++ } else { ++ __ move(T9, disp); ++ if (UseLEXT1) { ++ __ gslbx(as_Register(dst), as_Register(base), T9, 0); ++ } else { ++ __ addu(AT, as_Register(base), T9); ++ __ lb(as_Register(dst), AT, 0); ++ } ++ } ++ } ++ %} ++ ++ //Load byte unsigned ++ enc_class load_UB_enc (mRegI dst, memory mem) %{ ++ MacroAssembler _masm(&cbuf); ++ int dst = $dst$$reg; ++ int base = $mem$$base; ++ int index = $mem$$index; ++ int scale = $mem$$scale; ++ int disp = $mem$$disp; ++ ++ if( index != 0 ) { ++ if (scale == 0) { ++ __ daddu(AT, as_Register(base), as_Register(index)); ++ } else { ++ __ dsll(AT, as_Register(index), scale); ++ __ daddu(AT, as_Register(base), AT); ++ } ++ if( Assembler::is_simm16(disp) ) { ++ __ lbu(as_Register(dst), AT, disp); ++ } else { ++ __ move(T9, disp); ++ __ daddu(AT, AT, T9); ++ __ lbu(as_Register(dst), AT, 0); ++ } ++ } else { ++ if( Assembler::is_simm16(disp) ) { ++ __ lbu(as_Register(dst), as_Register(base), disp); ++ } else { ++ __ move(T9, disp); ++ __ daddu(AT, as_Register(base), T9); ++ __ lbu(as_Register(dst), AT, 0); ++ } ++ } ++ %} ++ ++ enc_class store_B_reg_enc (memory mem, mRegI src) %{ ++ MacroAssembler _masm(&cbuf); ++ int src = $src$$reg; ++ int base = $mem$$base; ++ int index = $mem$$index; ++ int scale = $mem$$scale; ++ int disp = $mem$$disp; ++ ++ if( index != 0 ) { ++ if (scale == 0) { ++ if( Assembler::is_simm(disp, 8) ) { ++ if (UseLEXT1) { ++ __ gssbx(as_Register(src), as_Register(base), as_Register(index), disp); ++ } else { ++ __ addu(AT, as_Register(base), as_Register(index)); ++ __ sb(as_Register(src), AT, disp); ++ } ++ } else if( Assembler::is_simm16(disp) ) { ++ __ addu(AT, as_Register(base), as_Register(index)); ++ __ sb(as_Register(src), AT, disp); ++ } else { ++ __ addu(AT, as_Register(base), as_Register(index)); ++ __ move(T9, disp); ++ if (UseLEXT1) { ++ __ gssbx(as_Register(src), AT, T9, 0); ++ } else { ++ __ addu(AT, AT, T9); ++ __ sb(as_Register(src), AT, 0); ++ } ++ } ++ } else { ++ __ dsll(AT, as_Register(index), scale); ++ if( Assembler::is_simm(disp, 8) ) { ++ if (UseLEXT1) { ++ __ gssbx(as_Register(src), AT, as_Register(base), disp); ++ } else { ++ __ addu(AT, as_Register(base), AT); ++ __ sb(as_Register(src), AT, disp); ++ } ++ } else if( Assembler::is_simm16(disp) ) { ++ __ addu(AT, as_Register(base), AT); ++ __ sb(as_Register(src), AT, disp); ++ } else { ++ __ addu(AT, as_Register(base), AT); ++ __ move(T9, disp); ++ if (UseLEXT1) { ++ __ gssbx(as_Register(src), AT, T9, 0); ++ } else { ++ __ addu(AT, AT, T9); ++ __ sb(as_Register(src), AT, 0); ++ } ++ } ++ } ++ } else { ++ if( Assembler::is_simm16(disp) ) { ++ __ sb(as_Register(src), as_Register(base), disp); ++ } else { ++ __ move(T9, disp); ++ if (UseLEXT1) { ++ __ gssbx(as_Register(src), as_Register(base), T9, 0); ++ } else { ++ __ addu(AT, as_Register(base), T9); ++ __ sb(as_Register(src), AT, 0); ++ } ++ } ++ } ++ %} ++ ++ enc_class store_B_immI_enc (memory mem, immI8 src) %{ ++ MacroAssembler _masm(&cbuf); ++ int base = $mem$$base; ++ int index = $mem$$index; ++ int scale = $mem$$scale; ++ int disp = $mem$$disp; ++ int value = $src$$constant; ++ ++ if( index != 0 ) { ++ if (!UseLEXT1) { ++ if (scale == 0) { ++ __ daddu(AT, as_Register(base), as_Register(index)); ++ } else { ++ __ dsll(AT, as_Register(index), scale); ++ __ daddu(AT, as_Register(base), AT); ++ } ++ if( Assembler::is_simm16(disp) ) { ++ if (value == 0) { ++ __ sb(R0, AT, disp); ++ } else { ++ __ move(T9, value); ++ __ sb(T9, AT, disp); ++ } ++ } else { ++ if (value == 0) { ++ __ move(T9, disp); ++ __ daddu(AT, AT, T9); ++ __ sb(R0, AT, 0); ++ } else { ++ __ move(T9, disp); ++ __ daddu(AT, AT, T9); ++ __ move(T9, value); ++ __ sb(T9, AT, 0); ++ } ++ } ++ } else { ++ ++ if (scale == 0) { ++ if( Assembler::is_simm(disp, 8) ) { ++ if (value == 0) { ++ __ gssbx(R0, as_Register(base), as_Register(index), disp); ++ } else { ++ __ move(T9, value); ++ __ gssbx(T9, as_Register(base), as_Register(index), disp); ++ } ++ } else if( Assembler::is_simm16(disp) ) { ++ __ daddu(AT, as_Register(base), as_Register(index)); ++ if (value == 0) { ++ __ sb(R0, AT, disp); ++ } else { ++ __ move(T9, value); ++ __ sb(T9, AT, disp); ++ } ++ } else { ++ if (value == 0) { ++ __ daddu(AT, as_Register(base), as_Register(index)); ++ __ move(T9, disp); ++ __ gssbx(R0, AT, T9, 0); ++ } else { ++ __ move(AT, disp); ++ __ move(T9, value); ++ __ daddu(AT, as_Register(base), AT); ++ __ gssbx(T9, AT, as_Register(index), 0); ++ } ++ } ++ ++ } else { ++ ++ if( Assembler::is_simm(disp, 8) ) { ++ __ dsll(AT, as_Register(index), scale); ++ if (value == 0) { ++ __ gssbx(R0, as_Register(base), AT, disp); ++ } else { ++ __ move(T9, value); ++ __ gssbx(T9, as_Register(base), AT, disp); ++ } ++ } else if( Assembler::is_simm16(disp) ) { ++ __ dsll(AT, as_Register(index), scale); ++ __ daddu(AT, as_Register(base), AT); ++ if (value == 0) { ++ __ sb(R0, AT, disp); ++ } else { ++ __ move(T9, value); ++ __ sb(T9, AT, disp); ++ } ++ } else { ++ __ dsll(AT, as_Register(index), scale); ++ if (value == 0) { ++ __ daddu(AT, as_Register(base), AT); ++ __ move(T9, disp); ++ __ gssbx(R0, AT, T9, 0); ++ } else { ++ __ move(T9, disp); ++ __ daddu(AT, AT, T9); ++ __ move(T9, value); ++ __ gssbx(T9, as_Register(base), AT, 0); ++ } ++ } ++ } ++ } ++ } else { ++ if( Assembler::is_simm16(disp) ) { ++ if (value == 0) { ++ __ sb(R0, as_Register(base), disp); ++ } else { ++ __ move(AT, value); ++ __ sb(AT, as_Register(base), disp); ++ } ++ } else { ++ if (value == 0) { ++ __ move(T9, disp); ++ if (UseLEXT1) { ++ __ gssbx(R0, as_Register(base), T9, 0); ++ } else { ++ __ daddu(AT, as_Register(base), T9); ++ __ sb(R0, AT, 0); ++ } ++ } else { ++ __ move(T9, disp); ++ if (UseLEXT1) { ++ __ move(AT, value); ++ __ gssbx(AT, as_Register(base), T9, 0); ++ } else { ++ __ daddu(AT, as_Register(base), T9); ++ __ move(T9, value); ++ __ sb(T9, AT, 0); ++ } ++ } ++ } ++ } ++ %} ++ ++ ++ enc_class store_B_immI_enc_sync (memory mem, immI8 src) %{ ++ MacroAssembler _masm(&cbuf); ++ int base = $mem$$base; ++ int index = $mem$$index; ++ int scale = $mem$$scale; ++ int disp = $mem$$disp; ++ int value = $src$$constant; ++ ++ if( index != 0 ) { ++ if (UseLEXT1) { ++ if ( Assembler::is_simm(disp,8) ) { ++ if ( scale == 0 ) { ++ if ( value == 0 ) { ++ __ gssbx(R0, as_Register(base), as_Register(index), disp); ++ } else { ++ __ move(AT, value); ++ __ gssbx(AT, as_Register(base), as_Register(index), disp); ++ } ++ } else { ++ __ dsll(AT, as_Register(index), scale); ++ if ( value == 0 ) { ++ __ gssbx(R0, as_Register(base), AT, disp); ++ } else { ++ __ move(T9, value); ++ __ gssbx(T9, as_Register(base), AT, disp); ++ } ++ } ++ } else if ( Assembler::is_simm16(disp) ) { ++ if ( scale == 0 ) { ++ __ daddu(AT, as_Register(base), as_Register(index)); ++ if ( value == 0 ){ ++ __ sb(R0, AT, disp); ++ } else { ++ __ move(T9, value); ++ __ sb(T9, AT, disp); ++ } ++ } else { ++ __ dsll(AT, as_Register(index), scale); ++ __ daddu(AT, as_Register(base), AT); ++ if ( value == 0 ) { ++ __ sb(R0, AT, disp); ++ } else { ++ __ move(T9, value); ++ __ sb(T9, AT, disp); ++ } ++ } ++ } else { ++ if ( scale == 0 ) { ++ __ move(AT, disp); ++ __ daddu(AT, as_Register(index), AT); ++ if ( value == 0 ) { ++ __ gssbx(R0, as_Register(base), AT, 0); ++ } else { ++ __ move(T9, value); ++ __ gssbx(T9, as_Register(base), AT, 0); ++ } ++ } else { ++ __ dsll(AT, as_Register(index), scale); ++ __ move(T9, disp); ++ __ daddu(AT, AT, T9); ++ if ( value == 0 ) { ++ __ gssbx(R0, as_Register(base), AT, 0); ++ } else { ++ __ move(T9, value); ++ __ gssbx(T9, as_Register(base), AT, 0); ++ } ++ } ++ } ++ } else { //not use loongson isa ++ if (scale == 0) { ++ __ daddu(AT, as_Register(base), as_Register(index)); ++ } else { ++ __ dsll(AT, as_Register(index), scale); ++ __ daddu(AT, as_Register(base), AT); ++ } ++ if( Assembler::is_simm16(disp) ) { ++ if (value == 0) { ++ __ sb(R0, AT, disp); ++ } else { ++ __ move(T9, value); ++ __ sb(T9, AT, disp); ++ } ++ } else { ++ if (value == 0) { ++ __ move(T9, disp); ++ __ daddu(AT, AT, T9); ++ __ sb(R0, AT, 0); ++ } else { ++ __ move(T9, disp); ++ __ daddu(AT, AT, T9); ++ __ move(T9, value); ++ __ sb(T9, AT, 0); ++ } ++ } ++ } ++ } else { ++ if (UseLEXT1){ ++ if ( Assembler::is_simm16(disp) ){ ++ if ( value == 0 ) { ++ __ sb(R0, as_Register(base), disp); ++ } else { ++ __ move(AT, value); ++ __ sb(AT, as_Register(base), disp); ++ } ++ } else { ++ __ move(AT, disp); ++ if ( value == 0 ) { ++ __ gssbx(R0, as_Register(base), AT, 0); ++ } else { ++ __ move(T9, value); ++ __ gssbx(T9, as_Register(base), AT, 0); ++ } ++ } ++ } else { ++ if( Assembler::is_simm16(disp) ) { ++ if (value == 0) { ++ __ sb(R0, as_Register(base), disp); ++ } else { ++ __ move(AT, value); ++ __ sb(AT, as_Register(base), disp); ++ } ++ } else { ++ if (value == 0) { ++ __ move(T9, disp); ++ __ daddu(AT, as_Register(base), T9); ++ __ sb(R0, AT, 0); ++ } else { ++ __ move(T9, disp); ++ __ daddu(AT, as_Register(base), T9); ++ __ move(T9, value); ++ __ sb(T9, AT, 0); ++ } ++ } ++ } ++ } ++ ++ __ sync(); ++ %} ++ ++ // Load Short (16bit signed) ++ enc_class load_S_enc (mRegI dst, memory mem) %{ ++ MacroAssembler _masm(&cbuf); ++ int dst = $dst$$reg; ++ int base = $mem$$base; ++ int index = $mem$$index; ++ int scale = $mem$$scale; ++ int disp = $mem$$disp; ++ ++ if( index != 0 ) { ++ if (UseLEXT1) { ++ if ( Assembler::is_simm(disp, 8) ) { ++ if (scale == 0) { ++ __ gslhx(as_Register(dst), as_Register(base), as_Register(index), disp); ++ } else { ++ __ dsll(AT, as_Register(index), scale); ++ __ gslhx(as_Register(dst), as_Register(base), AT, disp); ++ } ++ } else if ( Assembler::is_simm16(disp) ) { ++ if (scale == 0) { ++ __ daddu(AT, as_Register(base), as_Register(index)); ++ __ lh(as_Register(dst), AT, disp); ++ } else { ++ __ dsll(AT, as_Register(index), scale); ++ __ daddu(AT, as_Register(base), AT); ++ __ lh(as_Register(dst), AT, disp); ++ } ++ } else { ++ if (scale == 0) { ++ __ move(AT, disp); ++ __ daddu(AT, as_Register(index), AT); ++ __ gslhx(as_Register(dst), as_Register(base), AT, 0); ++ } else { ++ __ dsll(AT, as_Register(index), scale); ++ __ move(T9, disp); ++ __ daddu(AT, AT, T9); ++ __ gslhx(as_Register(dst), as_Register(base), AT, 0); ++ } ++ } ++ } else { // not use loongson isa ++ if (scale == 0) { ++ __ daddu(AT, as_Register(base), as_Register(index)); ++ } else { ++ __ dsll(AT, as_Register(index), scale); ++ __ daddu(AT, as_Register(base), AT); ++ } ++ if( Assembler::is_simm16(disp) ) { ++ __ lh(as_Register(dst), AT, disp); ++ } else { ++ __ move(T9, disp); ++ __ daddu(AT, AT, T9); ++ __ lh(as_Register(dst), AT, 0); ++ } ++ } ++ } else { // index is 0 ++ if (UseLEXT1) { ++ if ( Assembler::is_simm16(disp) ) { ++ __ lh(as_Register(dst), as_Register(base), disp); ++ } else { ++ __ move(T9, disp); ++ __ gslhx(as_Register(dst), as_Register(base), T9, 0); ++ } ++ } else { //not use loongson isa ++ if( Assembler::is_simm16(disp) ) { ++ __ lh(as_Register(dst), as_Register(base), disp); ++ } else { ++ __ move(T9, disp); ++ __ daddu(AT, as_Register(base), T9); ++ __ lh(as_Register(dst), AT, 0); ++ } ++ } ++ } ++ %} ++ ++ // Load Char (16bit unsigned) ++ enc_class load_C_enc (mRegI dst, memory mem) %{ ++ MacroAssembler _masm(&cbuf); ++ int dst = $dst$$reg; ++ int base = $mem$$base; ++ int index = $mem$$index; ++ int scale = $mem$$scale; ++ int disp = $mem$$disp; ++ ++ if( index != 0 ) { ++ if (scale == 0) { ++ __ daddu(AT, as_Register(base), as_Register(index)); ++ } else { ++ __ dsll(AT, as_Register(index), scale); ++ __ daddu(AT, as_Register(base), AT); ++ } ++ if( Assembler::is_simm16(disp) ) { ++ __ lhu(as_Register(dst), AT, disp); ++ } else { ++ __ move(T9, disp); ++ __ addu(AT, AT, T9); ++ __ lhu(as_Register(dst), AT, 0); ++ } ++ } else { ++ if( Assembler::is_simm16(disp) ) { ++ __ lhu(as_Register(dst), as_Register(base), disp); ++ } else { ++ __ move(T9, disp); ++ __ daddu(AT, as_Register(base), T9); ++ __ lhu(as_Register(dst), AT, 0); ++ } ++ } ++ %} ++ ++ // Store Char (16bit unsigned) ++ enc_class store_C_reg_enc (memory mem, mRegI src) %{ ++ MacroAssembler _masm(&cbuf); ++ int src = $src$$reg; ++ int base = $mem$$base; ++ int index = $mem$$index; ++ int scale = $mem$$scale; ++ int disp = $mem$$disp; ++ ++ if( index != 0 ) { ++ if( Assembler::is_simm16(disp) ) { ++ if ( UseLEXT1 && Assembler::is_simm(disp, 8) ) { ++ if (scale == 0) { ++ __ gsshx(as_Register(src), as_Register(base), as_Register(index), disp); ++ } else { ++ __ dsll(AT, as_Register(index), scale); ++ __ gsshx(as_Register(src), as_Register(base), AT, disp); ++ } ++ } else { ++ if (scale == 0) { ++ __ addu(AT, as_Register(base), as_Register(index)); ++ } else { ++ __ dsll(AT, as_Register(index), scale); ++ __ addu(AT, as_Register(base), AT); ++ } ++ __ sh(as_Register(src), AT, disp); ++ } ++ } else { ++ if (scale == 0) { ++ __ addu(AT, as_Register(base), as_Register(index)); ++ } else { ++ __ dsll(AT, as_Register(index), scale); ++ __ addu(AT, as_Register(base), AT); ++ } ++ __ move(T9, disp); ++ if (UseLEXT1) { ++ __ gsshx(as_Register(src), AT, T9, 0); ++ } else { ++ __ addu(AT, AT, T9); ++ __ sh(as_Register(src), AT, 0); ++ } ++ } ++ } else { ++ if( Assembler::is_simm16(disp) ) { ++ __ sh(as_Register(src), as_Register(base), disp); ++ } else { ++ __ move(T9, disp); ++ if (UseLEXT1) { ++ __ gsshx(as_Register(src), as_Register(base), T9, 0); ++ } else { ++ __ addu(AT, as_Register(base), T9); ++ __ sh(as_Register(src), AT, 0); ++ } ++ } ++ } ++ %} ++ ++ enc_class store_C0_enc (memory mem) %{ ++ MacroAssembler _masm(&cbuf); ++ int base = $mem$$base; ++ int index = $mem$$index; ++ int scale = $mem$$scale; ++ int disp = $mem$$disp; ++ ++ if( index != 0 ) { ++ if ( Assembler::is_simm16(disp) ) { ++ if ( UseLEXT1 && Assembler::is_simm(disp, 8) ) { ++ if (scale == 0) { ++ __ gsshx(R0, as_Register(base), as_Register(index), disp); ++ } else { ++ __ dsll(AT, as_Register(index), scale); ++ __ gsshx(R0, as_Register(base), AT, disp); ++ } ++ } else { ++ if (scale == 0) { ++ __ addu(AT, as_Register(base), as_Register(index)); ++ } else { ++ __ dsll(AT, as_Register(index), scale); ++ __ addu(AT, as_Register(base), AT); ++ } ++ __ sh(R0, AT, disp); ++ } ++ } else { ++ if (scale == 0) { ++ __ addu(AT, as_Register(base), as_Register(index)); ++ } else { ++ __ dsll(AT, as_Register(index), scale); ++ __ addu(AT, as_Register(base), AT); ++ } ++ __ move(T9, disp); ++ if (UseLEXT1) { ++ __ gsshx(R0, AT, T9, 0); ++ } else { ++ __ addu(AT, AT, T9); ++ __ sh(R0, AT, 0); ++ } ++ } ++ } else { ++ if( Assembler::is_simm16(disp) ) { ++ __ sh(R0, as_Register(base), disp); ++ } else { ++ __ move(T9, disp); ++ if (UseLEXT1) { ++ __ gsshx(R0, as_Register(base), T9, 0); ++ } else { ++ __ addu(AT, as_Register(base), T9); ++ __ sh(R0, AT, 0); ++ } ++ } ++ } ++ %} ++ ++ enc_class load_I_enc (mRegI dst, memory mem) %{ ++ MacroAssembler _masm(&cbuf); ++ int dst = $dst$$reg; ++ int base = $mem$$base; ++ int index = $mem$$index; ++ int scale = $mem$$scale; ++ int disp = $mem$$disp; ++ ++ if( index != 0 ) { ++ if( Assembler::is_simm16(disp) ) { ++ if ( UseLEXT1 && Assembler::is_simm(disp, 8) ) { ++ if (scale == 0) { ++ __ gslwx(as_Register(dst), as_Register(base), as_Register(index), disp); ++ } else { ++ __ dsll(AT, as_Register(index), scale); ++ __ gslwx(as_Register(dst), as_Register(base), AT, disp); ++ } ++ } else { ++ if (scale == 0) { ++ __ addu(AT, as_Register(base), as_Register(index)); ++ } else { ++ __ dsll(AT, as_Register(index), scale); ++ __ addu(AT, as_Register(base), AT); ++ } ++ __ lw(as_Register(dst), AT, disp); ++ } ++ } else { ++ if (scale == 0) { ++ __ addu(AT, as_Register(base), as_Register(index)); ++ } else { ++ __ dsll(AT, as_Register(index), scale); ++ __ addu(AT, as_Register(base), AT); ++ } ++ __ move(T9, disp); ++ if (UseLEXT1) { ++ __ gslwx(as_Register(dst), AT, T9, 0); ++ } else { ++ __ addu(AT, AT, T9); ++ __ lw(as_Register(dst), AT, 0); ++ } ++ } ++ } else { ++ if( Assembler::is_simm16(disp) ) { ++ __ lw(as_Register(dst), as_Register(base), disp); ++ } else { ++ __ move(T9, disp); ++ if (UseLEXT1) { ++ __ gslwx(as_Register(dst), as_Register(base), T9, 0); ++ } else { ++ __ addu(AT, as_Register(base), T9); ++ __ lw(as_Register(dst), AT, 0); ++ } ++ } ++ } ++ %} ++ ++ enc_class store_I_reg_enc (memory mem, mRegI src) %{ ++ MacroAssembler _masm(&cbuf); ++ int src = $src$$reg; ++ int base = $mem$$base; ++ int index = $mem$$index; ++ int scale = $mem$$scale; ++ int disp = $mem$$disp; ++ ++ if( index != 0 ) { ++ if( Assembler::is_simm16(disp) ) { ++ if ( UseLEXT1 && Assembler::is_simm(disp, 8) ) { ++ if (scale == 0) { ++ __ gsswx(as_Register(src), as_Register(base), as_Register(index), disp); ++ } else { ++ __ dsll(AT, as_Register(index), scale); ++ __ gsswx(as_Register(src), as_Register(base), AT, disp); ++ } ++ } else { ++ if (scale == 0) { ++ __ addu(AT, as_Register(base), as_Register(index)); ++ } else { ++ __ dsll(AT, as_Register(index), scale); ++ __ addu(AT, as_Register(base), AT); ++ } ++ __ sw(as_Register(src), AT, disp); ++ } ++ } else { ++ if (scale == 0) { ++ __ addu(AT, as_Register(base), as_Register(index)); ++ } else { ++ __ dsll(AT, as_Register(index), scale); ++ __ addu(AT, as_Register(base), AT); ++ } ++ __ move(T9, disp); ++ if (UseLEXT1) { ++ __ gsswx(as_Register(src), AT, T9, 0); ++ } else { ++ __ addu(AT, AT, T9); ++ __ sw(as_Register(src), AT, 0); ++ } ++ } ++ } else { ++ if( Assembler::is_simm16(disp) ) { ++ __ sw(as_Register(src), as_Register(base), disp); ++ } else { ++ __ move(T9, disp); ++ if (UseLEXT1) { ++ __ gsswx(as_Register(src), as_Register(base), T9, 0); ++ } else { ++ __ addu(AT, as_Register(base), T9); ++ __ sw(as_Register(src), AT, 0); ++ } ++ } ++ } ++ %} ++ ++ enc_class store_I_immI_enc (memory mem, immI src) %{ ++ MacroAssembler _masm(&cbuf); ++ int base = $mem$$base; ++ int index = $mem$$index; ++ int scale = $mem$$scale; ++ int disp = $mem$$disp; ++ int value = $src$$constant; ++ ++ if( index != 0 ) { ++ if (UseLEXT1) { ++ if ( Assembler::is_simm(disp, 8) ) { ++ if ( scale == 0 ) { ++ if ( value == 0 ) { ++ __ gsswx(R0, as_Register(base), as_Register(index), disp); ++ } else { ++ __ move(T9, value); ++ __ gsswx(T9, as_Register(base), as_Register(index), disp); ++ } ++ } else { ++ __ dsll(AT, as_Register(index), scale); ++ if ( value == 0 ) { ++ __ gsswx(R0, as_Register(base), AT, disp); ++ } else { ++ __ move(T9, value); ++ __ gsswx(T9, as_Register(base), AT, disp); ++ } ++ } ++ } else if ( Assembler::is_simm16(disp) ) { ++ if ( scale == 0 ) { ++ __ daddu(AT, as_Register(base), as_Register(index)); ++ if ( value == 0 ) { ++ __ sw(R0, AT, disp); ++ } else { ++ __ move(T9, value); ++ __ sw(T9, AT, disp); ++ } ++ } else { ++ __ dsll(AT, as_Register(index), scale); ++ __ daddu(AT, as_Register(base), AT); ++ if ( value == 0 ) { ++ __ sw(R0, AT, disp); ++ } else { ++ __ move(T9, value); ++ __ sw(T9, AT, disp); ++ } ++ } ++ } else { ++ if ( scale == 0 ) { ++ __ move(T9, disp); ++ __ daddu(AT, as_Register(index), T9); ++ if ( value ==0 ) { ++ __ gsswx(R0, as_Register(base), AT, 0); ++ } else { ++ __ move(T9, value); ++ __ gsswx(T9, as_Register(base), AT, 0); ++ } ++ } else { ++ __ dsll(AT, as_Register(index), scale); ++ __ move(T9, disp); ++ __ daddu(AT, AT, T9); ++ if ( value == 0 ) { ++ __ gsswx(R0, as_Register(base), AT, 0); ++ } else { ++ __ move(T9, value); ++ __ gsswx(T9, as_Register(base), AT, 0); ++ } ++ } ++ } ++ } else { //not use loongson isa ++ if (scale == 0) { ++ __ daddu(AT, as_Register(base), as_Register(index)); ++ } else { ++ __ dsll(AT, as_Register(index), scale); ++ __ daddu(AT, as_Register(base), AT); ++ } ++ if( Assembler::is_simm16(disp) ) { ++ if (value == 0) { ++ __ sw(R0, AT, disp); ++ } else { ++ __ move(T9, value); ++ __ sw(T9, AT, disp); ++ } ++ } else { ++ if (value == 0) { ++ __ move(T9, disp); ++ __ daddu(AT, AT, T9); ++ __ sw(R0, AT, 0); ++ } else { ++ __ move(T9, disp); ++ __ daddu(AT, AT, T9); ++ __ move(T9, value); ++ __ sw(T9, AT, 0); ++ } ++ } ++ } ++ } else { ++ if (UseLEXT1) { ++ if ( Assembler::is_simm16(disp) ) { ++ if ( value == 0 ) { ++ __ sw(R0, as_Register(base), disp); ++ } else { ++ __ move(AT, value); ++ __ sw(AT, as_Register(base), disp); ++ } ++ } else { ++ __ move(T9, disp); ++ if ( value == 0 ) { ++ __ gsswx(R0, as_Register(base), T9, 0); ++ } else { ++ __ move(AT, value); ++ __ gsswx(AT, as_Register(base), T9, 0); ++ } ++ } ++ } else { ++ if( Assembler::is_simm16(disp) ) { ++ if (value == 0) { ++ __ sw(R0, as_Register(base), disp); ++ } else { ++ __ move(AT, value); ++ __ sw(AT, as_Register(base), disp); ++ } ++ } else { ++ if (value == 0) { ++ __ move(T9, disp); ++ __ daddu(AT, as_Register(base), T9); ++ __ sw(R0, AT, 0); ++ } else { ++ __ move(T9, disp); ++ __ daddu(AT, as_Register(base), T9); ++ __ move(T9, value); ++ __ sw(T9, AT, 0); ++ } ++ } ++ } ++ } ++ %} ++ ++ enc_class load_N_enc (mRegN dst, memory mem) %{ ++ MacroAssembler _masm(&cbuf); ++ int dst = $dst$$reg; ++ int base = $mem$$base; ++ int index = $mem$$index; ++ int scale = $mem$$scale; ++ int disp = $mem$$disp; ++ relocInfo::relocType disp_reloc = $mem->disp_reloc(); ++ assert(disp_reloc == relocInfo::none, "cannot have disp"); ++ ++ if( index != 0 ) { ++ if (scale == 0) { ++ __ daddu(AT, as_Register(base), as_Register(index)); ++ } else { ++ __ dsll(AT, as_Register(index), scale); ++ __ daddu(AT, as_Register(base), AT); ++ } ++ if( Assembler::is_simm16(disp) ) { ++ __ lwu(as_Register(dst), AT, disp); ++ } else { ++ __ set64(T9, disp); ++ __ daddu(AT, AT, T9); ++ __ lwu(as_Register(dst), AT, 0); ++ } ++ } else { ++ if( Assembler::is_simm16(disp) ) { ++ __ lwu(as_Register(dst), as_Register(base), disp); ++ } else { ++ __ set64(T9, disp); ++ __ daddu(AT, as_Register(base), T9); ++ __ lwu(as_Register(dst), AT, 0); ++ } ++ } ++ %} ++ ++ ++ enc_class load_P_enc (mRegP dst, memory mem) %{ ++ MacroAssembler _masm(&cbuf); ++ int dst = $dst$$reg; ++ int base = $mem$$base; ++ int index = $mem$$index; ++ int scale = $mem$$scale; ++ int disp = $mem$$disp; ++ relocInfo::relocType disp_reloc = $mem->disp_reloc(); ++ assert(disp_reloc == relocInfo::none, "cannot have disp"); ++ ++ if( index != 0 ) { ++ if (UseLEXT1) { ++ if ( Assembler::is_simm(disp, 8) ) { ++ if ( scale != 0 ) { ++ __ dsll(AT, as_Register(index), scale); ++ __ gsldx(as_Register(dst), as_Register(base), AT, disp); ++ } else { ++ __ gsldx(as_Register(dst), as_Register(base), as_Register(index), disp); ++ } ++ } else if ( Assembler::is_simm16(disp) ){ ++ if ( scale != 0 ) { ++ __ dsll(AT, as_Register(index), scale); ++ __ daddu(AT, AT, as_Register(base)); ++ } else { ++ __ daddu(AT, as_Register(index), as_Register(base)); ++ } ++ __ ld(as_Register(dst), AT, disp); ++ } else { ++ if ( scale != 0 ) { ++ __ dsll(AT, as_Register(index), scale); ++ __ move(T9, disp); ++ __ daddu(AT, AT, T9); ++ } else { ++ __ move(T9, disp); ++ __ daddu(AT, as_Register(index), T9); ++ } ++ __ gsldx(as_Register(dst), as_Register(base), AT, 0); ++ } ++ } else { //not use loongson isa ++ if (scale == 0) { ++ __ daddu(AT, as_Register(base), as_Register(index)); ++ } else { ++ __ dsll(AT, as_Register(index), scale); ++ __ daddu(AT, as_Register(base), AT); ++ } ++ if( Assembler::is_simm16(disp) ) { ++ __ ld(as_Register(dst), AT, disp); ++ } else { ++ __ set64(T9, disp); ++ __ daddu(AT, AT, T9); ++ __ ld(as_Register(dst), AT, 0); ++ } ++ } ++ } else { ++ if (UseLEXT1) { ++ if ( Assembler::is_simm16(disp) ){ ++ __ ld(as_Register(dst), as_Register(base), disp); ++ } else { ++ __ set64(T9, disp); ++ __ gsldx(as_Register(dst), as_Register(base), T9, 0); ++ } ++ } else { //not use loongson isa ++ if( Assembler::is_simm16(disp) ) { ++ __ ld(as_Register(dst), as_Register(base), disp); ++ } else { ++ __ set64(T9, disp); ++ __ daddu(AT, as_Register(base), T9); ++ __ ld(as_Register(dst), AT, 0); ++ } ++ } ++ } ++ %} ++ ++ // Load acquire. ++ // load_P_enc + sync ++ enc_class load_P_enc_ac (mRegP dst, memory mem) %{ ++ MacroAssembler _masm(&cbuf); ++ int dst = $dst$$reg; ++ int base = $mem$$base; ++ int index = $mem$$index; ++ int scale = $mem$$scale; ++ int disp = $mem$$disp; ++ relocInfo::relocType disp_reloc = $mem->disp_reloc(); ++ assert(disp_reloc == relocInfo::none, "cannot have disp"); ++ ++ if( index != 0 ) { ++ if (UseLEXT1) { ++ if ( Assembler::is_simm(disp, 8) ) { ++ if ( scale != 0 ) { ++ __ dsll(AT, as_Register(index), scale); ++ __ gsldx(as_Register(dst), as_Register(base), AT, disp); ++ } else { ++ __ gsldx(as_Register(dst), as_Register(base), as_Register(index), disp); ++ } ++ } else if ( Assembler::is_simm16(disp) ){ ++ if ( scale != 0 ) { ++ __ dsll(AT, as_Register(index), scale); ++ __ daddu(AT, AT, as_Register(base)); ++ } else { ++ __ daddu(AT, as_Register(index), as_Register(base)); ++ } ++ __ ld(as_Register(dst), AT, disp); ++ } else { ++ if ( scale != 0 ) { ++ __ dsll(AT, as_Register(index), scale); ++ __ move(T9, disp); ++ __ daddu(AT, AT, T9); ++ } else { ++ __ move(T9, disp); ++ __ daddu(AT, as_Register(index), T9); ++ } ++ __ gsldx(as_Register(dst), as_Register(base), AT, 0); ++ } ++ } else { //not use loongson isa ++ if (scale == 0) { ++ __ daddu(AT, as_Register(base), as_Register(index)); ++ } else { ++ __ dsll(AT, as_Register(index), scale); ++ __ daddu(AT, as_Register(base), AT); ++ } ++ if( Assembler::is_simm16(disp) ) { ++ __ ld(as_Register(dst), AT, disp); ++ } else { ++ __ set64(T9, disp); ++ __ daddu(AT, AT, T9); ++ __ ld(as_Register(dst), AT, 0); ++ } ++ } ++ } else { ++ if (UseLEXT1) { ++ if ( Assembler::is_simm16(disp) ){ ++ __ ld(as_Register(dst), as_Register(base), disp); ++ } else { ++ __ set64(T9, disp); ++ __ gsldx(as_Register(dst), as_Register(base), T9, 0); ++ } ++ } else { //not use loongson isa ++ if( Assembler::is_simm16(disp) ) { ++ __ ld(as_Register(dst), as_Register(base), disp); ++ } else { ++ __ set64(T9, disp); ++ __ daddu(AT, as_Register(base), T9); ++ __ ld(as_Register(dst), AT, 0); ++ } ++ } ++ } ++ __ sync(); ++ %} ++ ++ enc_class store_P_reg_enc (memory mem, mRegP src) %{ ++ MacroAssembler _masm(&cbuf); ++ int src = $src$$reg; ++ int base = $mem$$base; ++ int index = $mem$$index; ++ int scale = $mem$$scale; ++ int disp = $mem$$disp; ++ ++ if( index != 0 ) { ++ if (UseLEXT1){ ++ if ( Assembler::is_simm(disp, 8) ) { ++ if ( scale == 0 ) { ++ __ gssdx(as_Register(src), as_Register(base), as_Register(index), disp); ++ } else { ++ __ dsll(AT, as_Register(index), scale); ++ __ gssdx(as_Register(src), as_Register(base), AT, disp); ++ } ++ } else if ( Assembler::is_simm16(disp) ) { ++ if ( scale == 0 ) { ++ __ daddu(AT, as_Register(base), as_Register(index)); ++ } else { ++ __ dsll(AT, as_Register(index), scale); ++ __ daddu(AT, as_Register(base), AT); ++ } ++ __ sd(as_Register(src), AT, disp); ++ } else { ++ if ( scale == 0 ) { ++ __ move(T9, disp); ++ __ daddu(AT, as_Register(index), T9); ++ } else { ++ __ dsll(AT, as_Register(index), scale); ++ __ move(T9, disp); ++ __ daddu(AT, AT, T9); ++ } ++ __ gssdx(as_Register(src), as_Register(base), AT, 0); ++ } ++ } else { //not use loongson isa ++ if (scale == 0) { ++ __ daddu(AT, as_Register(base), as_Register(index)); ++ } else { ++ __ dsll(AT, as_Register(index), scale); ++ __ daddu(AT, as_Register(base), AT); ++ } ++ if( Assembler::is_simm16(disp) ) { ++ __ sd(as_Register(src), AT, disp); ++ } else { ++ __ move(T9, disp); ++ __ daddu(AT, AT, T9); ++ __ sd(as_Register(src), AT, 0); ++ } ++ } ++ } else { ++ if (UseLEXT1) { ++ if ( Assembler::is_simm16(disp) ) { ++ __ sd(as_Register(src), as_Register(base), disp); ++ } else { ++ __ move(T9, disp); ++ __ gssdx(as_Register(src), as_Register(base), T9, 0); ++ } ++ } else { ++ if( Assembler::is_simm16(disp) ) { ++ __ sd(as_Register(src), as_Register(base), disp); ++ } else { ++ __ move(T9, disp); ++ __ daddu(AT, as_Register(base), T9); ++ __ sd(as_Register(src), AT, 0); ++ } ++ } ++ } ++ %} ++ ++ enc_class store_N_reg_enc (memory mem, mRegN src) %{ ++ MacroAssembler _masm(&cbuf); ++ int src = $src$$reg; ++ int base = $mem$$base; ++ int index = $mem$$index; ++ int scale = $mem$$scale; ++ int disp = $mem$$disp; ++ ++ if( index != 0 ) { ++ if (UseLEXT1){ ++ if ( Assembler::is_simm(disp, 8) ) { ++ if ( scale == 0 ) { ++ __ gsswx(as_Register(src), as_Register(base), as_Register(index), disp); ++ } else { ++ __ dsll(AT, as_Register(index), scale); ++ __ gsswx(as_Register(src), as_Register(base), AT, disp); ++ } ++ } else if ( Assembler::is_simm16(disp) ) { ++ if ( scale == 0 ) { ++ __ daddu(AT, as_Register(base), as_Register(index)); ++ } else { ++ __ dsll(AT, as_Register(index), scale); ++ __ daddu(AT, as_Register(base), AT); ++ } ++ __ sw(as_Register(src), AT, disp); ++ } else { ++ if ( scale == 0 ) { ++ __ move(T9, disp); ++ __ daddu(AT, as_Register(index), T9); ++ } else { ++ __ dsll(AT, as_Register(index), scale); ++ __ move(T9, disp); ++ __ daddu(AT, AT, T9); ++ } ++ __ gsswx(as_Register(src), as_Register(base), AT, 0); ++ } ++ } else { //not use loongson isa ++ if (scale == 0) { ++ __ daddu(AT, as_Register(base), as_Register(index)); ++ } else { ++ __ dsll(AT, as_Register(index), scale); ++ __ daddu(AT, as_Register(base), AT); ++ } ++ if( Assembler::is_simm16(disp) ) { ++ __ sw(as_Register(src), AT, disp); ++ } else { ++ __ move(T9, disp); ++ __ daddu(AT, AT, T9); ++ __ sw(as_Register(src), AT, 0); ++ } ++ } ++ } else { ++ if (UseLEXT1) { ++ if ( Assembler::is_simm16(disp) ) { ++ __ sw(as_Register(src), as_Register(base), disp); ++ } else { ++ __ move(T9, disp); ++ __ gsswx(as_Register(src), as_Register(base), T9, 0); ++ } ++ } else { ++ if( Assembler::is_simm16(disp) ) { ++ __ sw(as_Register(src), as_Register(base), disp); ++ } else { ++ __ move(T9, disp); ++ __ daddu(AT, as_Register(base), T9); ++ __ sw(as_Register(src), AT, 0); ++ } ++ } ++ } ++ %} ++ ++ enc_class store_P_immP0_enc (memory mem) %{ ++ MacroAssembler _masm(&cbuf); ++ int base = $mem$$base; ++ int index = $mem$$index; ++ int scale = $mem$$scale; ++ int disp = $mem$$disp; ++ ++ if( index != 0 ) { ++ if (scale == 0) { ++ if ( Assembler::is_simm16(disp) ) { ++ if (UseLEXT1 && Assembler::is_simm(disp, 8)) { ++ __ gssdx(R0, as_Register(base), as_Register(index), disp); ++ } else { ++ __ daddu(AT, as_Register(base), as_Register(index)); ++ __ sd(R0, AT, disp); ++ } ++ } else { ++ __ daddu(AT, as_Register(base), as_Register(index)); ++ __ move(T9, disp); ++ if (UseLEXT1) { ++ __ gssdx(R0, AT, T9, 0); ++ } else { ++ __ daddu(AT, AT, T9); ++ __ sd(R0, AT, 0); ++ } ++ } ++ } else { ++ __ dsll(AT, as_Register(index), scale); ++ if( Assembler::is_simm16(disp) ) { ++ if (UseLEXT1 && Assembler::is_simm(disp, 8)) { ++ __ gssdx(R0, as_Register(base), AT, disp); ++ } else { ++ __ daddu(AT, as_Register(base), AT); ++ __ sd(R0, AT, disp); ++ } ++ } else { ++ __ daddu(AT, as_Register(base), AT); ++ __ move(T9, disp); ++ if (UseLEXT1) { ++ __ gssdx(R0, AT, T9, 0); ++ } else { ++ __ daddu(AT, AT, T9); ++ __ sd(R0, AT, 0); ++ } ++ } ++ } ++ } else { ++ if( Assembler::is_simm16(disp) ) { ++ __ sd(R0, as_Register(base), disp); ++ } else { ++ __ move(T9, disp); ++ if (UseLEXT1) { ++ __ gssdx(R0, as_Register(base), T9, 0); ++ } else { ++ __ daddu(AT, as_Register(base), T9); ++ __ sd(R0, AT, 0); ++ } ++ } ++ } ++ %} ++ ++ enc_class storeImmN0_enc(memory mem, ImmN0 src) %{ ++ MacroAssembler _masm(&cbuf); ++ int base = $mem$$base; ++ int index = $mem$$index; ++ int scale = $mem$$scale; ++ int disp = $mem$$disp; ++ ++ if(index!=0){ ++ if (scale == 0) { ++ __ daddu(AT, as_Register(base), as_Register(index)); ++ } else { ++ __ dsll(AT, as_Register(index), scale); ++ __ daddu(AT, as_Register(base), AT); ++ } ++ ++ if( Assembler::is_simm16(disp) ) { ++ __ sw(R0, AT, disp); ++ } else { ++ __ move(T9, disp); ++ __ daddu(AT, AT, T9); ++ __ sw(R0, AT, 0); ++ } ++ } else { ++ if( Assembler::is_simm16(disp) ) { ++ __ sw(R0, as_Register(base), disp); ++ } else { ++ __ move(T9, disp); ++ __ daddu(AT, as_Register(base), T9); ++ __ sw(R0, AT, 0); ++ } ++ } ++ %} ++ ++ enc_class load_L_enc (mRegL dst, memory mem) %{ ++ MacroAssembler _masm(&cbuf); ++ int base = $mem$$base; ++ int index = $mem$$index; ++ int scale = $mem$$scale; ++ int disp = $mem$$disp; ++ Register dst_reg = as_Register($dst$$reg); ++ ++ if( index != 0 ) { ++ if (scale == 0) { ++ __ daddu(AT, as_Register(base), as_Register(index)); ++ } else { ++ __ dsll(AT, as_Register(index), scale); ++ __ daddu(AT, as_Register(base), AT); ++ } ++ if( Assembler::is_simm16(disp) ) { ++ __ ld(dst_reg, AT, disp); ++ } else { ++ __ move(T9, disp); ++ __ daddu(AT, AT, T9); ++ __ ld(dst_reg, AT, 0); ++ } ++ } else { ++ if( Assembler::is_simm16(disp) ) { ++ __ ld(dst_reg, as_Register(base), disp); ++ } else { ++ __ move(T9, disp); ++ __ daddu(AT, as_Register(base), T9); ++ __ ld(dst_reg, AT, 0); ++ } ++ } ++ %} ++ ++ enc_class store_L_reg_enc (memory mem, mRegL src) %{ ++ MacroAssembler _masm(&cbuf); ++ int base = $mem$$base; ++ int index = $mem$$index; ++ int scale = $mem$$scale; ++ int disp = $mem$$disp; ++ Register src_reg = as_Register($src$$reg); ++ ++ if( index != 0 ) { ++ if (scale == 0) { ++ __ daddu(AT, as_Register(base), as_Register(index)); ++ } else { ++ __ dsll(AT, as_Register(index), scale); ++ __ daddu(AT, as_Register(base), AT); ++ } ++ if( Assembler::is_simm16(disp) ) { ++ __ sd(src_reg, AT, disp); ++ } else { ++ __ move(T9, disp); ++ __ daddu(AT, AT, T9); ++ __ sd(src_reg, AT, 0); ++ } ++ } else { ++ if( Assembler::is_simm16(disp) ) { ++ __ sd(src_reg, as_Register(base), disp); ++ } else { ++ __ move(T9, disp); ++ __ daddu(AT, as_Register(base), T9); ++ __ sd(src_reg, AT, 0); ++ } ++ } ++ %} ++ ++ enc_class store_L_immL_0_enc (memory mem, immL_0 src) %{ ++ MacroAssembler _masm(&cbuf); ++ int base = $mem$$base; ++ int index = $mem$$index; ++ int scale = $mem$$scale; ++ int disp = $mem$$disp; ++ ++ if( index != 0 ) { ++ if (scale == 0) { ++ __ daddu(AT, as_Register(base), as_Register(index)); ++ } else { ++ __ dsll(AT, as_Register(index), scale); ++ __ daddu(AT, as_Register(base), AT); ++ } ++ if( Assembler::is_simm16(disp) ) { ++ __ sd(R0, AT, disp); ++ } else { ++ __ move(T9, disp); ++ __ addu(AT, AT, T9); ++ __ sd(R0, AT, 0); ++ } ++ } else { ++ if( Assembler::is_simm16(disp) ) { ++ __ sd(R0, as_Register(base), disp); ++ } else { ++ __ move(T9, disp); ++ __ addu(AT, as_Register(base), T9); ++ __ sd(R0, AT, 0); ++ } ++ } ++ %} ++ ++ enc_class store_L_immL_enc (memory mem, immL src) %{ ++ MacroAssembler _masm(&cbuf); ++ int base = $mem$$base; ++ int index = $mem$$index; ++ int scale = $mem$$scale; ++ int disp = $mem$$disp; ++ long imm = $src$$constant; ++ ++ if( index != 0 ) { ++ if (scale == 0) { ++ __ daddu(AT, as_Register(base), as_Register(index)); ++ } else { ++ __ dsll(AT, as_Register(index), scale); ++ __ daddu(AT, as_Register(base), AT); ++ } ++ if( Assembler::is_simm16(disp) ) { ++ __ set64(T9, imm); ++ __ sd(T9, AT, disp); ++ } else { ++ __ move(T9, disp); ++ __ addu(AT, AT, T9); ++ __ set64(T9, imm); ++ __ sd(T9, AT, 0); ++ } ++ } else { ++ if( Assembler::is_simm16(disp) ) { ++ __ move(AT, as_Register(base)); ++ __ set64(T9, imm); ++ __ sd(T9, AT, disp); ++ } else { ++ __ move(T9, disp); ++ __ addu(AT, as_Register(base), T9); ++ __ set64(T9, imm); ++ __ sd(T9, AT, 0); ++ } ++ } ++ %} ++ ++ enc_class load_F_enc (regF dst, memory mem) %{ ++ MacroAssembler _masm(&cbuf); ++ int base = $mem$$base; ++ int index = $mem$$index; ++ int scale = $mem$$scale; ++ int disp = $mem$$disp; ++ FloatRegister dst = $dst$$FloatRegister; ++ ++ if( index != 0 ) { ++ if( Assembler::is_simm16(disp) ) { ++ if ( UseLEXT1 && Assembler::is_simm(disp, 8) ) { ++ if (scale == 0) { ++ __ gslwxc1(dst, as_Register(base), as_Register(index), disp); ++ } else { ++ __ dsll(AT, as_Register(index), scale); ++ __ gslwxc1(dst, as_Register(base), AT, disp); ++ } ++ } else { ++ if (scale == 0) { ++ __ daddu(AT, as_Register(base), as_Register(index)); ++ } else { ++ __ dsll(AT, as_Register(index), scale); ++ __ daddu(AT, as_Register(base), AT); ++ } ++ __ lwc1(dst, AT, disp); ++ } ++ } else { ++ if (scale == 0) { ++ __ daddu(AT, as_Register(base), as_Register(index)); ++ } else { ++ __ dsll(AT, as_Register(index), scale); ++ __ daddu(AT, as_Register(base), AT); ++ } ++ __ move(T9, disp); ++ if (UseLEXT1) { ++ __ gslwxc1(dst, AT, T9, 0); ++ } else { ++ __ daddu(AT, AT, T9); ++ __ lwc1(dst, AT, 0); ++ } ++ } ++ } else { ++ if( Assembler::is_simm16(disp) ) { ++ __ lwc1(dst, as_Register(base), disp); ++ } else { ++ __ move(T9, disp); ++ if (UseLEXT1) { ++ __ gslwxc1(dst, as_Register(base), T9, 0); ++ } else { ++ __ daddu(AT, as_Register(base), T9); ++ __ lwc1(dst, AT, 0); ++ } ++ } ++ } ++ %} ++ ++ enc_class store_F_reg_enc (memory mem, regF src) %{ ++ MacroAssembler _masm(&cbuf); ++ int base = $mem$$base; ++ int index = $mem$$index; ++ int scale = $mem$$scale; ++ int disp = $mem$$disp; ++ FloatRegister src = $src$$FloatRegister; ++ ++ if( index != 0 ) { ++ if ( Assembler::is_simm16(disp) ) { ++ if ( UseLEXT1 && Assembler::is_simm(disp, 8) ) { ++ if (scale == 0) { ++ __ gsswxc1(src, as_Register(base), as_Register(index), disp); ++ } else { ++ __ dsll(AT, as_Register(index), scale); ++ __ gsswxc1(src, as_Register(base), AT, disp); ++ } ++ } else { ++ if (scale == 0) { ++ __ daddu(AT, as_Register(base), as_Register(index)); ++ } else { ++ __ dsll(AT, as_Register(index), scale); ++ __ daddu(AT, as_Register(base), AT); ++ } ++ __ swc1(src, AT, disp); ++ } ++ } else { ++ if (scale == 0) { ++ __ daddu(AT, as_Register(base), as_Register(index)); ++ } else { ++ __ dsll(AT, as_Register(index), scale); ++ __ daddu(AT, as_Register(base), AT); ++ } ++ __ move(T9, disp); ++ if (UseLEXT1) { ++ __ gsswxc1(src, AT, T9, 0); ++ } else { ++ __ daddu(AT, AT, T9); ++ __ swc1(src, AT, 0); ++ } ++ } ++ } else { ++ if( Assembler::is_simm16(disp) ) { ++ __ swc1(src, as_Register(base), disp); ++ } else { ++ __ move(T9, disp); ++ if (UseLEXT1) { ++ __ gsswxc1(src, as_Register(base), T9, 0); ++ } else { ++ __ daddu(AT, as_Register(base), T9); ++ __ swc1(src, AT, 0); ++ } ++ } ++ } ++ %} ++ ++ enc_class load_D_enc (regD dst, memory mem) %{ ++ MacroAssembler _masm(&cbuf); ++ int base = $mem$$base; ++ int index = $mem$$index; ++ int scale = $mem$$scale; ++ int disp = $mem$$disp; ++ FloatRegister dst_reg = as_FloatRegister($dst$$reg); ++ ++ if ( index != 0 ) { ++ if ( Assembler::is_simm16(disp) ) { ++ if ( UseLEXT1 && Assembler::is_simm(disp, 8) ) { ++ if (scale == 0) { ++ __ gsldxc1(dst_reg, as_Register(base), as_Register(index), disp); ++ } else { ++ __ dsll(AT, as_Register(index), scale); ++ __ gsldxc1(dst_reg, as_Register(base), AT, disp); ++ } ++ } else { ++ if (scale == 0) { ++ __ daddu(AT, as_Register(base), as_Register(index)); ++ } else { ++ __ dsll(AT, as_Register(index), scale); ++ __ daddu(AT, as_Register(base), AT); ++ } ++ __ ldc1(dst_reg, AT, disp); ++ } ++ } else { ++ if (scale == 0) { ++ __ daddu(AT, as_Register(base), as_Register(index)); ++ } else { ++ __ dsll(AT, as_Register(index), scale); ++ __ daddu(AT, as_Register(base), AT); ++ } ++ __ move(T9, disp); ++ if (UseLEXT1) { ++ __ gsldxc1(dst_reg, AT, T9, 0); ++ } else { ++ __ addu(AT, AT, T9); ++ __ ldc1(dst_reg, AT, 0); ++ } ++ } ++ } else { ++ if( Assembler::is_simm16(disp) ) { ++ __ ldc1(dst_reg, as_Register(base), disp); ++ } else { ++ __ move(T9, disp); ++ if (UseLEXT1) { ++ __ gsldxc1(dst_reg, as_Register(base), T9, 0); ++ } else { ++ __ addu(AT, as_Register(base), T9); ++ __ ldc1(dst_reg, AT, 0); ++ } ++ } ++ } ++ %} ++ ++ enc_class store_D_reg_enc (memory mem, regD src) %{ ++ MacroAssembler _masm(&cbuf); ++ int base = $mem$$base; ++ int index = $mem$$index; ++ int scale = $mem$$scale; ++ int disp = $mem$$disp; ++ FloatRegister src_reg = as_FloatRegister($src$$reg); ++ ++ if ( index != 0 ) { ++ if ( Assembler::is_simm16(disp) ) { ++ if ( UseLEXT1 && Assembler::is_simm(disp, 8) ) { ++ if (scale == 0) { ++ __ gssdxc1(src_reg, as_Register(base), as_Register(index), disp); ++ } else { ++ __ dsll(AT, as_Register(index), scale); ++ __ gssdxc1(src_reg, as_Register(base), AT, disp); ++ } ++ } else { ++ if (scale == 0) { ++ __ daddu(AT, as_Register(base), as_Register(index)); ++ } else { ++ __ dsll(AT, as_Register(index), scale); ++ __ daddu(AT, as_Register(base), AT); ++ } ++ __ sdc1(src_reg, AT, disp); ++ } ++ } else { ++ if (scale == 0) { ++ __ daddu(AT, as_Register(base), as_Register(index)); ++ } else { ++ __ dsll(AT, as_Register(index), scale); ++ __ daddu(AT, as_Register(base), AT); ++ } ++ __ move(T9, disp); ++ if (UseLEXT1) { ++ __ gssdxc1(src_reg, AT, T9, 0); ++ } else { ++ __ addu(AT, AT, T9); ++ __ sdc1(src_reg, AT, 0); ++ } ++ } ++ } else { ++ if ( Assembler::is_simm16(disp) ) { ++ __ sdc1(src_reg, as_Register(base), disp); ++ } else { ++ __ move(T9, disp); ++ if (UseLEXT1) { ++ __ gssdxc1(src_reg, as_Register(base), T9, 0); ++ } else { ++ __ addu(AT, as_Register(base), T9); ++ __ sdc1(src_reg, AT, 0); ++ } ++ } ++ } ++ %} ++ ++ enc_class Java_To_Runtime (method meth) %{ // CALL Java_To_Runtime, Java_To_Runtime_Leaf ++ MacroAssembler _masm(&cbuf); ++ // This is the instruction starting address for relocation info. ++ __ block_comment("Java_To_Runtime"); ++ cbuf.set_insts_mark(); ++ __ relocate(relocInfo::runtime_call_type); ++ __ patchable_call((address)$meth$$method); ++ %} ++ ++ enc_class Java_Static_Call (method meth) %{ // JAVA STATIC CALL ++ // CALL to fixup routine. Fixup routine uses ScopeDesc info to determine ++ // who we intended to call. ++ MacroAssembler _masm(&cbuf); ++ address addr = (address)$meth$$method; ++ address call; ++ __ block_comment("Java_Static_Call"); ++ ++ if ( !_method ) { ++ // A call to a runtime wrapper, e.g. new, new_typeArray_Java, uncommon_trap. ++ call = __ trampoline_call(AddressLiteral(addr, relocInfo::runtime_call_type), &cbuf); ++ } else if(_optimized_virtual) { ++ call = __ trampoline_call(AddressLiteral(addr, relocInfo::opt_virtual_call_type), &cbuf); ++ } else { ++ call = __ trampoline_call(AddressLiteral(addr, relocInfo::static_call_type), &cbuf); ++ } ++ ++ if (call == NULL) { ++ ciEnv::current()->record_failure("CodeCache is full"); ++ return; ++ } ++ ++ if( _method ) { // Emit stub for static call ++ address stub = CompiledStaticCall::emit_to_interp_stub(cbuf); ++ if (stub == NULL) { ++ ciEnv::current()->record_failure("CodeCache is full"); ++ return; ++ } ++ } ++ %} ++ ++ ++ // ++ // [Ref: LIR_Assembler::ic_call() ] ++ // ++ enc_class Java_Dynamic_Call (method meth) %{ // JAVA DYNAMIC CALL ++ MacroAssembler _masm(&cbuf); ++ __ block_comment("Java_Dynamic_Call"); ++ __ ic_call((address)$meth$$method); ++ %} ++ ++ ++ enc_class enc_PartialSubtypeCheck(mRegP result, mRegP sub, mRegP super, mRegI tmp) %{ ++ Register result = $result$$Register; ++ Register sub = $sub$$Register; ++ Register super = $super$$Register; ++ Register length = $tmp$$Register; ++ Register tmp = T9; ++ Label miss; ++ ++ // result may be the same as sub ++ // 47c B40: # B21 B41 <- B20 Freq: 0.155379 ++ // 47c partialSubtypeCheck result=S1, sub=S1, super=S3, length=S0 ++ // 4bc mov S2, NULL #@loadConP ++ // 4c0 beq S1, S2, B21 #@branchConP P=0.999999 C=-1.000000 ++ // ++ MacroAssembler _masm(&cbuf); ++ Label done; ++ __ check_klass_subtype_slow_path(sub, super, length, tmp, ++ NULL, &miss, ++ /*set_cond_codes:*/ true); ++ // Refer to X86_64's RDI ++ __ move(result, 0); ++ __ b(done); ++ __ delayed()->nop(); ++ ++ __ bind(miss); ++ __ move(result, 1); ++ __ bind(done); ++ %} ++ ++%} ++ ++ ++//---------MIPS FRAME-------------------------------------------------------------- ++// Definition of frame structure and management information. ++// ++// S T A C K L A Y O U T Allocators stack-slot number ++// | (to get allocators register number ++// G Owned by | | v add SharedInfo::stack0) ++// r CALLER | | ++// o | +--------+ pad to even-align allocators stack-slot ++// w V | pad0 | numbers; owned by CALLER ++// t -----------+--------+----> Matcher::_in_arg_limit, unaligned ++// h ^ | in | 5 ++// | | args | 4 Holes in incoming args owned by SELF ++// | | old | | 3 ++// | | SP-+--------+----> Matcher::_old_SP, even aligned ++// v | | ret | 3 return address ++// Owned by +--------+ ++// Self | pad2 | 2 pad to align old SP ++// | +--------+ 1 ++// | | locks | 0 ++// | +--------+----> SharedInfo::stack0, even aligned ++// | | pad1 | 11 pad to align new SP ++// | +--------+ ++// | | | 10 ++// | | spills | 9 spills ++// V | | 8 (pad0 slot for callee) ++// -----------+--------+----> Matcher::_out_arg_limit, unaligned ++// ^ | out | 7 ++// | | args | 6 Holes in outgoing args owned by CALLEE ++// Owned by new | | ++// Callee SP-+--------+----> Matcher::_new_SP, even aligned ++// | | ++// ++// Note 1: Only region 8-11 is determined by the allocator. Region 0-5 is ++// known from SELF's arguments and the Java calling convention. ++// Region 6-7 is determined per call site. ++// Note 2: If the calling convention leaves holes in the incoming argument ++// area, those holes are owned by SELF. Holes in the outgoing area ++// are owned by the CALLEE. Holes should not be nessecary in the ++// incoming area, as the Java calling convention is completely under ++// the control of the AD file. Doubles can be sorted and packed to ++// avoid holes. Holes in the outgoing arguments may be nessecary for ++// varargs C calling conventions. ++// Note 3: Region 0-3 is even aligned, with pad2 as needed. Region 3-5 is ++// even aligned with pad0 as needed. ++// Region 6 is even aligned. Region 6-7 is NOT even aligned; ++// region 6-11 is even aligned; it may be padded out more so that ++// the region from SP to FP meets the minimum stack alignment. ++// Note 4: For I2C adapters, the incoming FP may not meet the minimum stack ++// alignment. Region 11, pad1, may be dynamically extended so that ++// SP meets the minimum alignment. ++ ++ ++frame %{ ++ ++ stack_direction(TOWARDS_LOW); ++ ++ // These two registers define part of the calling convention ++ // between compiled code and the interpreter. ++ // SEE StartI2CNode::calling_convention & StartC2INode::calling_convention & StartOSRNode::calling_convention ++ // for more information. ++ ++ inline_cache_reg(T1); // Inline Cache Register ++ interpreter_method_oop_reg(S3); // Method Oop Register when calling interpreter ++ ++ // Optional: name the operand used by cisc-spilling to access [stack_pointer + offset] ++ cisc_spilling_operand_name(indOffset32); ++ ++ // Number of stack slots consumed by locking an object ++ // generate Compile::sync_stack_slots ++ sync_stack_slots(2); ++ ++ frame_pointer(SP); ++ ++ // Interpreter stores its frame pointer in a register which is ++ // stored to the stack by I2CAdaptors. ++ // I2CAdaptors convert from interpreted java to compiled java. ++ ++ interpreter_frame_pointer(FP); ++ ++ // generate Matcher::stack_alignment ++ stack_alignment(StackAlignmentInBytes); //wordSize = sizeof(char*); ++ ++ // Number of stack slots between incoming argument block and the start of ++ // a new frame. The PROLOG must add this many slots to the stack. The ++ // EPILOG must remove this many slots. ++ in_preserve_stack_slots(4); //Now VerifyStackAtCalls is defined as false ! Leave two stack slots for ra and fp ++ ++ // Number of outgoing stack slots killed above the out_preserve_stack_slots ++ // for calls to C. Supports the var-args backing area for register parms. ++ varargs_C_out_slots_killed(0); ++ ++ // The after-PROLOG location of the return address. Location of ++ // return address specifies a type (REG or STACK) and a number ++ // representing the register number (i.e. - use a register name) or ++ // stack slot. ++ // Ret Addr is on stack in slot 0 if no locks or verification or alignment. ++ // Otherwise, it is above the locks and verification slot and alignment word ++ //return_addr(STACK -1+ round_to(1+VerifyStackAtCalls+Compile::current()->sync()*Compile::current()->sync_stack_slots(),WordsPerLong)); ++ return_addr(REG RA); ++ ++ // Body of function which returns an integer array locating ++ // arguments either in registers or in stack slots. Passed an array ++ // of ideal registers called "sig" and a "length" count. Stack-slot ++ // offsets are based on outgoing arguments, i.e. a CALLER setting up ++ // arguments for a CALLEE. Incoming stack arguments are ++ // automatically biased by the preserve_stack_slots field above. ++ ++ ++ // will generated to Matcher::calling_convention(OptoRegPair *sig, uint length, bool is_outgoing) ++ // StartNode::calling_convention call this. ++ calling_convention %{ ++ SharedRuntime::java_calling_convention(sig_bt, regs, length, false); ++ %} ++ ++ ++ ++ ++ // Body of function which returns an integer array locating ++ // arguments either in registers or in stack slots. Passed an array ++ // of ideal registers called "sig" and a "length" count. Stack-slot ++ // offsets are based on outgoing arguments, i.e. a CALLER setting up ++ // arguments for a CALLEE. Incoming stack arguments are ++ // automatically biased by the preserve_stack_slots field above. ++ ++ ++ // SEE CallRuntimeNode::calling_convention for more information. ++ c_calling_convention %{ ++ (void) SharedRuntime::c_calling_convention(sig_bt, regs, /*regs2=*/NULL, length); ++ %} ++ ++ ++ // Location of C & interpreter return values ++ // register(s) contain(s) return value for Op_StartI2C and Op_StartOSR. ++ // SEE Matcher::match. ++ c_return_value %{ ++ assert( ideal_reg >= Op_RegI && ideal_reg <= Op_RegL, "only return normal values" ); ++ /* -- , -- , Op_RegN, Op_RegI, Op_RegP, Op_RegF, Op_RegD, Op_RegL */ ++ static int lo[Op_RegL+1] = { 0, 0, V0_num, V0_num, V0_num, F0_num, F0_num, V0_num }; ++ static int hi[Op_RegL+1] = { 0, 0, OptoReg::Bad, OptoReg::Bad, V0_H_num, OptoReg::Bad, F0_H_num, V0_H_num }; ++ return OptoRegPair(hi[ideal_reg],lo[ideal_reg]); ++ %} ++ ++ // Location of return values ++ // register(s) contain(s) return value for Op_StartC2I and Op_Start. ++ // SEE Matcher::match. ++ ++ return_value %{ ++ assert( ideal_reg >= Op_RegI && ideal_reg <= Op_RegL, "only return normal values" ); ++ /* -- , -- , Op_RegN, Op_RegI, Op_RegP, Op_RegF, Op_RegD, Op_RegL */ ++ static int lo[Op_RegL+1] = { 0, 0, V0_num, V0_num, V0_num, F0_num, F0_num, V0_num }; ++ static int hi[Op_RegL+1] = { 0, 0, OptoReg::Bad, OptoReg::Bad, V0_H_num, OptoReg::Bad, F0_H_num, V0_H_num}; ++ return OptoRegPair(hi[ideal_reg],lo[ideal_reg]); ++ %} ++ ++%} ++ ++//----------ATTRIBUTES--------------------------------------------------------- ++//----------Operand Attributes------------------------------------------------- ++op_attrib op_cost(0); // Required cost attribute ++ ++//----------Instruction Attributes--------------------------------------------- ++ins_attrib ins_cost(100); // Required cost attribute ++ins_attrib ins_size(32); // Required size attribute (in bits) ++ins_attrib ins_pc_relative(0); // Required PC Relative flag ++ins_attrib ins_short_branch(0); // Required flag: is this instruction a ++ // non-matching short branch variant of some ++ // long branch? ++ins_attrib ins_alignment(4); // Required alignment attribute (must be a power of 2) ++ // specifies the alignment that some part of the instruction (not ++ // necessarily the start) requires. If > 1, a compute_padding() ++ // function must be provided for the instruction ++ ++//----------OPERANDS----------------------------------------------------------- ++// Operand definitions must precede instruction definitions for correct parsing ++// in the ADLC because operands constitute user defined types which are used in ++// instruction definitions. ++ ++// Vectors ++operand vecD() %{ ++ constraint(ALLOC_IN_RC(dbl_reg)); ++ match(VecD); ++ ++ format %{ %} ++ interface(REG_INTER); ++%} ++ ++// Flags register, used as output of compare instructions ++operand FlagsReg() %{ ++ constraint(ALLOC_IN_RC(t0_reg)); ++ match(RegFlags); ++ ++ format %{ "T0" %} ++ interface(REG_INTER); ++%} ++ ++//----------Simple Operands---------------------------------------------------- ++// TODO: Should we need to define some more special immediate number ? ++// Immediate Operands ++// Integer Immediate ++operand immI() %{ ++ match(ConI); ++ // TODO: should not match immI8 here LEE ++ match(immI8); ++ ++ op_cost(20); ++ format %{ %} ++ interface(CONST_INTER); ++%} ++ ++operand immI8() %{ ++ predicate((-128 <= n->get_int()) && (n->get_int() <= 127)); ++ match(ConI); ++ ++ op_cost(5); ++ format %{ %} ++ interface(CONST_INTER); ++%} ++ ++operand immI16() %{ ++ predicate((-32768 <= n->get_int()) && (n->get_int() <= 32767)); ++ match(ConI); ++ ++ op_cost(10); ++ format %{ %} ++ interface(CONST_INTER); ++%} ++ ++operand immI_M65536() %{ ++ predicate(n->get_int() == -65536); ++ match(ConI); ++ ++ op_cost(5); ++ format %{ %} ++ interface(CONST_INTER); ++%} ++ ++// Constant for decrement ++operand immI_M1() %{ ++ predicate(n->get_int() == -1); ++ match(ConI); ++ ++ op_cost(0); ++ format %{ %} ++ interface(CONST_INTER); ++%} ++ ++// Constant for test vs zero ++operand immI_0() %{ ++ predicate(n->get_int() == 0); ++ match(ConI); ++ ++ op_cost(0); ++ format %{ %} ++ interface(CONST_INTER); ++%} ++ ++// Constant for increment ++operand immI_1() %{ ++ predicate(n->get_int() == 1); ++ match(ConI); ++ ++ op_cost(0); ++ format %{ %} ++ interface(CONST_INTER); ++%} ++ ++// Constants for increment ++operand immI_16() %{ ++ predicate(n->get_int() == 16); ++ match(ConI); ++ ++ format %{ %} ++ interface(CONST_INTER); ++%} ++ ++operand immI_24() %{ ++ predicate(n->get_int() == 24); ++ match(ConI); ++ ++ format %{ %} ++ interface(CONST_INTER); ++%} ++ ++// Constant for long shifts ++operand immI_32() %{ ++ predicate(n->get_int() == 32); ++ match(ConI); ++ ++ op_cost(0); ++ format %{ %} ++ interface(CONST_INTER); ++%} ++ ++// Constant for byte-wide masking ++operand immI_255() %{ ++ predicate(n->get_int() == 255); ++ match(ConI); ++ ++ op_cost(0); ++ format %{ %} ++ interface(CONST_INTER); ++%} ++ ++operand immI_65535() %{ ++ predicate(n->get_int() == 65535); ++ match(ConI); ++ ++ op_cost(5); ++ format %{ %} ++ interface(CONST_INTER); ++%} ++ ++operand immI_MaxI() %{ ++ predicate(n->get_int() == 2147483647); ++ match(ConI); ++ ++ op_cost(0); ++ format %{ %} ++ interface(CONST_INTER); ++%} ++ ++operand immI_M32767_32768() %{ ++ predicate((-32767 <= n->get_int()) && (n->get_int() <= 32768)); ++ match(ConI); ++ ++ op_cost(10); ++ format %{ %} ++ interface(CONST_INTER); ++%} ++ ++// Valid scale values for addressing modes ++operand immI_0_3() %{ ++ predicate(0 <= n->get_int() && (n->get_int() <= 3)); ++ match(ConI); ++ ++ format %{ %} ++ interface(CONST_INTER); ++%} ++ ++operand immI_0_31() %{ ++ predicate(n->get_int() >= 0 && n->get_int() <= 31); ++ match(ConI); ++ ++ op_cost(0); ++ format %{ %} ++ interface(CONST_INTER); ++%} ++ ++operand immI_0_32767() %{ ++ predicate(n->get_int() >= 0 && n->get_int() <= 32767); ++ match(ConI); ++ op_cost(0); ++ ++ format %{ %} ++ interface(CONST_INTER); ++%} ++ ++operand immI_0_65535() %{ ++ predicate(n->get_int() >= 0 && n->get_int() <= 65535); ++ match(ConI); ++ op_cost(0); ++ ++ format %{ %} ++ interface(CONST_INTER); ++%} ++ ++operand immI_32_63() %{ ++ predicate(n->get_int() >= 32 && n->get_int() <= 63); ++ match(ConI); ++ op_cost(0); ++ ++ format %{ %} ++ interface(CONST_INTER); ++%} ++ ++// Operand for non-negtive integer mask ++operand immI_nonneg_mask() %{ ++ predicate((n->get_int() >= 0) && (Assembler::is_int_mask(n->get_int()) != -1)); ++ match(ConI); ++ ++ op_cost(0); ++ format %{ %} ++ interface(CONST_INTER); ++%} ++ ++// Long Immediate ++operand immL() %{ ++ match(ConL); ++ ++ op_cost(20); ++ format %{ %} ++ interface(CONST_INTER); ++%} ++ ++// Long Immediate 8-bit ++operand immL8() %{ ++ predicate(-0x80L <= n->get_long() && n->get_long() < 0x80L); ++ match(ConL); ++ ++ op_cost(5); ++ format %{ %} ++ interface(CONST_INTER); ++%} ++ ++operand immL16() %{ ++ predicate((-32768 <= n->get_long()) && (n->get_long() <= 32767)); ++ match(ConL); ++ ++ op_cost(10); ++ format %{ %} ++ interface(CONST_INTER); ++%} ++ ++// Long Immediate 32-bit signed ++operand immL32() %{ ++ predicate(n->get_long() == (int)(n->get_long())); ++ match(ConL); ++ ++ op_cost(15); ++ format %{ %} ++ interface(CONST_INTER); ++%} ++ ++// bit 3..6 zero ++operand immL_M121() %{ ++ predicate(n->get_long() == -121L); ++ match(ConL); ++ op_cost(0); ++ ++ format %{ %} ++ interface(CONST_INTER); ++%} ++ ++// bit 0..2 zero ++operand immL_M8() %{ ++ predicate(n->get_long() == -8L); ++ match(ConL); ++ op_cost(0); ++ ++ format %{ %} ++ interface(CONST_INTER); ++%} ++ ++// bit 1..2 zero ++operand immL_M7() %{ ++ predicate(n->get_long() == -7L); ++ match(ConL); ++ op_cost(0); ++ ++ format %{ %} ++ interface(CONST_INTER); ++%} ++ ++// bit 2 zero ++operand immL_M5() %{ ++ predicate(n->get_long() == -5L); ++ match(ConL); ++ op_cost(0); ++ ++ format %{ %} ++ interface(CONST_INTER); ++%} ++ ++// bit 0..1 zero ++operand immL_M4() %{ ++ predicate(n->get_long() == -4L); ++ match(ConL); ++ op_cost(0); ++ ++ format %{ %} ++ interface(CONST_INTER); ++%} ++ ++operand immL_M1() %{ ++ predicate(n->get_long() == -1L); ++ match(ConL); ++ op_cost(0); ++ ++ format %{ %} ++ interface(CONST_INTER); ++%} ++ ++// Long Immediate zero ++operand immL_0() %{ ++ predicate(n->get_long() == 0L); ++ match(ConL); ++ op_cost(0); ++ ++ format %{ %} ++ interface(CONST_INTER); ++%} ++ ++operand immL_7() %{ ++ predicate(n->get_long() == 7L); ++ match(ConL); ++ op_cost(0); ++ ++ format %{ %} ++ interface(CONST_INTER); ++%} ++ ++// Long Immediate: low 32-bit mask ++operand immL_MaxUI() %{ ++ predicate(n->get_long() == 0xFFFFFFFFL); ++ match(ConL); ++ op_cost(20); ++ ++ format %{ %} ++ interface(CONST_INTER); ++%} ++ ++operand immL_M32767_32768() %{ ++ predicate((-32767 <= n->get_long()) && (n->get_long() <= 32768)); ++ match(ConL); ++ ++ op_cost(10); ++ format %{ %} ++ interface(CONST_INTER); ++%} ++ ++operand immL_0_65535() %{ ++ predicate(n->get_long() >= 0 && n->get_long() <= 65535); ++ match(ConL); ++ op_cost(0); ++ ++ format %{ %} ++ interface(CONST_INTER); ++%} ++ ++// Operand for non-negtive long mask ++operand immL_nonneg_mask() %{ ++ predicate((n->get_long() >= 0) && (Assembler::is_jlong_mask(n->get_long()) != -1)); ++ match(ConL); ++ ++ op_cost(0); ++ format %{ %} ++ interface(CONST_INTER); ++%} ++ ++// Pointer Immediate ++operand immP() %{ ++ match(ConP); ++ ++ op_cost(10); ++ format %{ %} ++ interface(CONST_INTER); ++%} ++ ++// NULL Pointer Immediate ++operand immP_0() %{ ++ predicate(n->get_ptr() == 0); ++ match(ConP); ++ op_cost(0); ++ ++ format %{ %} ++ interface(CONST_INTER); ++%} ++ ++// Pointer Immediate: 64-bit ++operand immP_no_oop_cheap() %{ ++ predicate(!n->bottom_type()->isa_oop_ptr() && (MacroAssembler::insts_for_set64(n->get_ptr()) <= 3)); ++ match(ConP); ++ ++ op_cost(5); ++ // formats are generated automatically for constants and base registers ++ format %{ %} ++ interface(CONST_INTER); ++%} ++ ++// Pointer for polling page ++operand immP_poll() %{ ++ predicate(n->get_ptr() != 0 && n->get_ptr() == (intptr_t)os::get_polling_page()); ++ match(ConP); ++ op_cost(5); ++ ++ format %{ %} ++ interface(CONST_INTER); ++%} ++ ++// Pointer Immediate ++operand immN() %{ ++ match(ConN); ++ ++ op_cost(10); ++ format %{ %} ++ interface(CONST_INTER); ++%} ++ ++operand immNKlass() %{ ++ match(ConNKlass); ++ ++ op_cost(10); ++ format %{ %} ++ interface(CONST_INTER); ++%} ++ ++// NULL Pointer Immediate ++operand immN_0() %{ ++ predicate(n->get_narrowcon() == 0); ++ match(ConN); ++ ++ op_cost(5); ++ format %{ %} ++ interface(CONST_INTER); ++%} ++ ++// Single-precision floating-point immediate ++operand immF() %{ ++ match(ConF); ++ ++ op_cost(20); ++ format %{ %} ++ interface(CONST_INTER); ++%} ++ ++// Single-precision floating-point zero ++operand immF_0() %{ ++ predicate(jint_cast(n->getf()) == 0); ++ match(ConF); ++ ++ op_cost(5); ++ format %{ %} ++ interface(CONST_INTER); ++%} ++ ++// Double-precision floating-point immediate ++operand immD() %{ ++ match(ConD); ++ ++ op_cost(20); ++ format %{ %} ++ interface(CONST_INTER); ++%} ++ ++// Double-precision floating-point zero ++operand immD_0() %{ ++ predicate(jlong_cast(n->getd()) == 0); ++ match(ConD); ++ ++ op_cost(5); ++ format %{ %} ++ interface(CONST_INTER); ++%} ++ ++// Register Operands ++// Integer Register ++operand mRegI() %{ ++ constraint(ALLOC_IN_RC(int_reg)); ++ match(RegI); ++ ++ format %{ %} ++ interface(REG_INTER); ++%} ++ ++operand no_Ax_mRegI() %{ ++ constraint(ALLOC_IN_RC(no_Ax_int_reg)); ++ match(RegI); ++ match(mRegI); ++ ++ format %{ %} ++ interface(REG_INTER); ++%} ++ ++operand mS0RegI() %{ ++ constraint(ALLOC_IN_RC(s0_reg)); ++ match(RegI); ++ match(mRegI); ++ ++ format %{ "S0" %} ++ interface(REG_INTER); ++%} ++ ++operand mS1RegI() %{ ++ constraint(ALLOC_IN_RC(s1_reg)); ++ match(RegI); ++ match(mRegI); ++ ++ format %{ "S1" %} ++ interface(REG_INTER); ++%} ++ ++operand mS2RegI() %{ ++ constraint(ALLOC_IN_RC(s2_reg)); ++ match(RegI); ++ match(mRegI); ++ ++ format %{ "S2" %} ++ interface(REG_INTER); ++%} ++ ++operand mS3RegI() %{ ++ constraint(ALLOC_IN_RC(s3_reg)); ++ match(RegI); ++ match(mRegI); ++ ++ format %{ "S3" %} ++ interface(REG_INTER); ++%} ++ ++operand mS4RegI() %{ ++ constraint(ALLOC_IN_RC(s4_reg)); ++ match(RegI); ++ match(mRegI); ++ ++ format %{ "S4" %} ++ interface(REG_INTER); ++%} ++ ++operand mS5RegI() %{ ++ constraint(ALLOC_IN_RC(s5_reg)); ++ match(RegI); ++ match(mRegI); ++ ++ format %{ "S5" %} ++ interface(REG_INTER); ++%} ++ ++operand mS6RegI() %{ ++ constraint(ALLOC_IN_RC(s6_reg)); ++ match(RegI); ++ match(mRegI); ++ ++ format %{ "S6" %} ++ interface(REG_INTER); ++%} ++ ++operand mS7RegI() %{ ++ constraint(ALLOC_IN_RC(s7_reg)); ++ match(RegI); ++ match(mRegI); ++ ++ format %{ "S7" %} ++ interface(REG_INTER); ++%} ++ ++ ++operand mT0RegI() %{ ++ constraint(ALLOC_IN_RC(t0_reg)); ++ match(RegI); ++ match(mRegI); ++ ++ format %{ "T0" %} ++ interface(REG_INTER); ++%} ++ ++operand mT1RegI() %{ ++ constraint(ALLOC_IN_RC(t1_reg)); ++ match(RegI); ++ match(mRegI); ++ ++ format %{ "T1" %} ++ interface(REG_INTER); ++%} ++ ++operand mT2RegI() %{ ++ constraint(ALLOC_IN_RC(t2_reg)); ++ match(RegI); ++ match(mRegI); ++ ++ format %{ "T2" %} ++ interface(REG_INTER); ++%} ++ ++operand mT3RegI() %{ ++ constraint(ALLOC_IN_RC(t3_reg)); ++ match(RegI); ++ match(mRegI); ++ ++ format %{ "T3" %} ++ interface(REG_INTER); ++%} ++ ++operand mT8RegI() %{ ++ constraint(ALLOC_IN_RC(t8_reg)); ++ match(RegI); ++ match(mRegI); ++ ++ format %{ "T8" %} ++ interface(REG_INTER); ++%} ++ ++operand mT9RegI() %{ ++ constraint(ALLOC_IN_RC(t9_reg)); ++ match(RegI); ++ match(mRegI); ++ ++ format %{ "T9" %} ++ interface(REG_INTER); ++%} ++ ++operand mA0RegI() %{ ++ constraint(ALLOC_IN_RC(a0_reg)); ++ match(RegI); ++ match(mRegI); ++ ++ format %{ "A0" %} ++ interface(REG_INTER); ++%} ++ ++operand mA1RegI() %{ ++ constraint(ALLOC_IN_RC(a1_reg)); ++ match(RegI); ++ match(mRegI); ++ ++ format %{ "A1" %} ++ interface(REG_INTER); ++%} ++ ++operand mA2RegI() %{ ++ constraint(ALLOC_IN_RC(a2_reg)); ++ match(RegI); ++ match(mRegI); ++ ++ format %{ "A2" %} ++ interface(REG_INTER); ++%} ++ ++operand mA3RegI() %{ ++ constraint(ALLOC_IN_RC(a3_reg)); ++ match(RegI); ++ match(mRegI); ++ ++ format %{ "A3" %} ++ interface(REG_INTER); ++%} ++ ++operand mA4RegI() %{ ++ constraint(ALLOC_IN_RC(a4_reg)); ++ match(RegI); ++ match(mRegI); ++ ++ format %{ "A4" %} ++ interface(REG_INTER); ++%} ++ ++operand mA5RegI() %{ ++ constraint(ALLOC_IN_RC(a5_reg)); ++ match(RegI); ++ match(mRegI); ++ ++ format %{ "A5" %} ++ interface(REG_INTER); ++%} ++ ++operand mA6RegI() %{ ++ constraint(ALLOC_IN_RC(a6_reg)); ++ match(RegI); ++ match(mRegI); ++ ++ format %{ "A6" %} ++ interface(REG_INTER); ++%} ++ ++operand mA7RegI() %{ ++ constraint(ALLOC_IN_RC(a7_reg)); ++ match(RegI); ++ match(mRegI); ++ ++ format %{ "A7" %} ++ interface(REG_INTER); ++%} ++ ++operand mV0RegI() %{ ++ constraint(ALLOC_IN_RC(v0_reg)); ++ match(RegI); ++ match(mRegI); ++ ++ format %{ "V0" %} ++ interface(REG_INTER); ++%} ++ ++operand mV1RegI() %{ ++ constraint(ALLOC_IN_RC(v1_reg)); ++ match(RegI); ++ match(mRegI); ++ ++ format %{ "V1" %} ++ interface(REG_INTER); ++%} ++ ++operand mRegN() %{ ++ constraint(ALLOC_IN_RC(int_reg)); ++ match(RegN); ++ ++ format %{ %} ++ interface(REG_INTER); ++%} ++ ++operand t0_RegN() %{ ++ constraint(ALLOC_IN_RC(t0_reg)); ++ match(RegN); ++ match(mRegN); ++ ++ format %{ %} ++ interface(REG_INTER); ++%} ++ ++operand t1_RegN() %{ ++ constraint(ALLOC_IN_RC(t1_reg)); ++ match(RegN); ++ match(mRegN); ++ ++ format %{ %} ++ interface(REG_INTER); ++%} ++ ++operand t2_RegN() %{ ++ constraint(ALLOC_IN_RC(t2_reg)); ++ match(RegN); ++ match(mRegN); ++ ++ format %{ %} ++ interface(REG_INTER); ++%} ++ ++operand t3_RegN() %{ ++ constraint(ALLOC_IN_RC(t3_reg)); ++ match(RegN); ++ match(mRegN); ++ ++ format %{ %} ++ interface(REG_INTER); ++%} ++ ++operand t8_RegN() %{ ++ constraint(ALLOC_IN_RC(t8_reg)); ++ match(RegN); ++ match(mRegN); ++ ++ format %{ %} ++ interface(REG_INTER); ++%} ++ ++operand t9_RegN() %{ ++ constraint(ALLOC_IN_RC(t9_reg)); ++ match(RegN); ++ match(mRegN); ++ ++ format %{ %} ++ interface(REG_INTER); ++%} ++ ++operand a0_RegN() %{ ++ constraint(ALLOC_IN_RC(a0_reg)); ++ match(RegN); ++ match(mRegN); ++ ++ format %{ %} ++ interface(REG_INTER); ++%} ++ ++operand a1_RegN() %{ ++ constraint(ALLOC_IN_RC(a1_reg)); ++ match(RegN); ++ match(mRegN); ++ ++ format %{ %} ++ interface(REG_INTER); ++%} ++ ++operand a2_RegN() %{ ++ constraint(ALLOC_IN_RC(a2_reg)); ++ match(RegN); ++ match(mRegN); ++ ++ format %{ %} ++ interface(REG_INTER); ++%} ++ ++operand a3_RegN() %{ ++ constraint(ALLOC_IN_RC(a3_reg)); ++ match(RegN); ++ match(mRegN); ++ ++ format %{ %} ++ interface(REG_INTER); ++%} ++ ++operand a4_RegN() %{ ++ constraint(ALLOC_IN_RC(a4_reg)); ++ match(RegN); ++ match(mRegN); ++ ++ format %{ %} ++ interface(REG_INTER); ++%} ++ ++operand a5_RegN() %{ ++ constraint(ALLOC_IN_RC(a5_reg)); ++ match(RegN); ++ match(mRegN); ++ ++ format %{ %} ++ interface(REG_INTER); ++%} ++ ++operand a6_RegN() %{ ++ constraint(ALLOC_IN_RC(a6_reg)); ++ match(RegN); ++ match(mRegN); ++ ++ format %{ %} ++ interface(REG_INTER); ++%} ++ ++operand a7_RegN() %{ ++ constraint(ALLOC_IN_RC(a7_reg)); ++ match(RegN); ++ match(mRegN); ++ ++ format %{ %} ++ interface(REG_INTER); ++%} ++ ++operand s0_RegN() %{ ++ constraint(ALLOC_IN_RC(s0_reg)); ++ match(RegN); ++ match(mRegN); ++ ++ format %{ %} ++ interface(REG_INTER); ++%} ++ ++operand s1_RegN() %{ ++ constraint(ALLOC_IN_RC(s1_reg)); ++ match(RegN); ++ match(mRegN); ++ ++ format %{ %} ++ interface(REG_INTER); ++%} ++ ++operand s2_RegN() %{ ++ constraint(ALLOC_IN_RC(s2_reg)); ++ match(RegN); ++ match(mRegN); ++ ++ format %{ %} ++ interface(REG_INTER); ++%} ++ ++operand s3_RegN() %{ ++ constraint(ALLOC_IN_RC(s3_reg)); ++ match(RegN); ++ match(mRegN); ++ ++ format %{ %} ++ interface(REG_INTER); ++%} ++ ++operand s4_RegN() %{ ++ constraint(ALLOC_IN_RC(s4_reg)); ++ match(RegN); ++ match(mRegN); ++ ++ format %{ %} ++ interface(REG_INTER); ++%} ++ ++operand s5_RegN() %{ ++ constraint(ALLOC_IN_RC(s5_reg)); ++ match(RegN); ++ match(mRegN); ++ ++ format %{ %} ++ interface(REG_INTER); ++%} ++ ++operand s6_RegN() %{ ++ constraint(ALLOC_IN_RC(s6_reg)); ++ match(RegN); ++ match(mRegN); ++ ++ format %{ %} ++ interface(REG_INTER); ++%} ++ ++operand s7_RegN() %{ ++ constraint(ALLOC_IN_RC(s7_reg)); ++ match(RegN); ++ match(mRegN); ++ ++ format %{ %} ++ interface(REG_INTER); ++%} ++ ++operand v0_RegN() %{ ++ constraint(ALLOC_IN_RC(v0_reg)); ++ match(RegN); ++ match(mRegN); ++ ++ format %{ %} ++ interface(REG_INTER); ++%} ++ ++operand v1_RegN() %{ ++ constraint(ALLOC_IN_RC(v1_reg)); ++ match(RegN); ++ match(mRegN); ++ ++ format %{ %} ++ interface(REG_INTER); ++%} ++ ++// Pointer Register ++operand mRegP() %{ ++ constraint(ALLOC_IN_RC(p_reg)); ++ match(RegP); ++ match(a0_RegP); ++ ++ format %{ %} ++ interface(REG_INTER); ++%} ++ ++operand no_T8_mRegP() %{ ++ constraint(ALLOC_IN_RC(no_T8_p_reg)); ++ match(RegP); ++ match(mRegP); ++ ++ format %{ %} ++ interface(REG_INTER); ++%} ++ ++operand s0_RegP() ++%{ ++ constraint(ALLOC_IN_RC(s0_long_reg)); ++ match(RegP); ++ match(mRegP); ++ match(no_T8_mRegP); ++ ++ format %{ %} ++ interface(REG_INTER); ++%} ++ ++operand s1_RegP() ++%{ ++ constraint(ALLOC_IN_RC(s1_long_reg)); ++ match(RegP); ++ match(mRegP); ++ match(no_T8_mRegP); ++ ++ format %{ %} ++ interface(REG_INTER); ++%} ++ ++operand s2_RegP() ++%{ ++ constraint(ALLOC_IN_RC(s2_long_reg)); ++ match(RegP); ++ match(mRegP); ++ match(no_T8_mRegP); ++ ++ format %{ %} ++ interface(REG_INTER); ++%} ++ ++operand s3_RegP() ++%{ ++ constraint(ALLOC_IN_RC(s3_long_reg)); ++ match(RegP); ++ match(mRegP); ++ match(no_T8_mRegP); ++ ++ format %{ %} ++ interface(REG_INTER); ++%} ++ ++operand s4_RegP() ++%{ ++ constraint(ALLOC_IN_RC(s4_long_reg)); ++ match(RegP); ++ match(mRegP); ++ match(no_T8_mRegP); ++ ++ format %{ %} ++ interface(REG_INTER); ++%} ++ ++operand s5_RegP() ++%{ ++ constraint(ALLOC_IN_RC(s5_long_reg)); ++ match(RegP); ++ match(mRegP); ++ match(no_T8_mRegP); ++ ++ format %{ %} ++ interface(REG_INTER); ++%} ++ ++operand s6_RegP() ++%{ ++ constraint(ALLOC_IN_RC(s6_long_reg)); ++ match(RegP); ++ match(mRegP); ++ match(no_T8_mRegP); ++ ++ format %{ %} ++ interface(REG_INTER); ++%} ++ ++operand s7_RegP() ++%{ ++ constraint(ALLOC_IN_RC(s7_long_reg)); ++ match(RegP); ++ match(mRegP); ++ match(no_T8_mRegP); ++ ++ format %{ %} ++ interface(REG_INTER); ++%} ++ ++operand t0_RegP() ++%{ ++ constraint(ALLOC_IN_RC(t0_long_reg)); ++ match(RegP); ++ match(mRegP); ++ match(no_T8_mRegP); ++ ++ format %{ %} ++ interface(REG_INTER); ++%} ++ ++operand t1_RegP() ++%{ ++ constraint(ALLOC_IN_RC(t1_long_reg)); ++ match(RegP); ++ match(mRegP); ++ match(no_T8_mRegP); ++ ++ format %{ %} ++ interface(REG_INTER); ++%} ++ ++operand t2_RegP() ++%{ ++ constraint(ALLOC_IN_RC(t2_long_reg)); ++ match(RegP); ++ match(mRegP); ++ match(no_T8_mRegP); ++ ++ format %{ %} ++ interface(REG_INTER); ++%} ++ ++operand t3_RegP() ++%{ ++ constraint(ALLOC_IN_RC(t3_long_reg)); ++ match(RegP); ++ match(mRegP); ++ match(no_T8_mRegP); ++ ++ format %{ %} ++ interface(REG_INTER); ++%} ++ ++operand t8_RegP() ++%{ ++ constraint(ALLOC_IN_RC(t8_long_reg)); ++ match(RegP); ++ match(mRegP); ++ ++ format %{ %} ++ interface(REG_INTER); ++%} ++ ++operand t9_RegP() ++%{ ++ constraint(ALLOC_IN_RC(t9_long_reg)); ++ match(RegP); ++ match(mRegP); ++ match(no_T8_mRegP); ++ ++ format %{ %} ++ interface(REG_INTER); ++%} ++ ++operand a0_RegP() ++%{ ++ constraint(ALLOC_IN_RC(a0_long_reg)); ++ match(RegP); ++ match(mRegP); ++ match(no_T8_mRegP); ++ ++ format %{ %} ++ interface(REG_INTER); ++%} ++ ++operand a1_RegP() ++%{ ++ constraint(ALLOC_IN_RC(a1_long_reg)); ++ match(RegP); ++ match(mRegP); ++ match(no_T8_mRegP); ++ ++ format %{ %} ++ interface(REG_INTER); ++%} ++ ++operand a2_RegP() ++%{ ++ constraint(ALLOC_IN_RC(a2_long_reg)); ++ match(RegP); ++ match(mRegP); ++ match(no_T8_mRegP); ++ ++ format %{ %} ++ interface(REG_INTER); ++%} ++ ++operand a3_RegP() ++%{ ++ constraint(ALLOC_IN_RC(a3_long_reg)); ++ match(RegP); ++ match(mRegP); ++ match(no_T8_mRegP); ++ ++ format %{ %} ++ interface(REG_INTER); ++%} ++ ++operand a4_RegP() ++%{ ++ constraint(ALLOC_IN_RC(a4_long_reg)); ++ match(RegP); ++ match(mRegP); ++ match(no_T8_mRegP); ++ ++ format %{ %} ++ interface(REG_INTER); ++%} ++ ++ ++operand a5_RegP() ++%{ ++ constraint(ALLOC_IN_RC(a5_long_reg)); ++ match(RegP); ++ match(mRegP); ++ match(no_T8_mRegP); ++ ++ format %{ %} ++ interface(REG_INTER); ++%} ++ ++operand a6_RegP() ++%{ ++ constraint(ALLOC_IN_RC(a6_long_reg)); ++ match(RegP); ++ match(mRegP); ++ match(no_T8_mRegP); ++ ++ format %{ %} ++ interface(REG_INTER); ++%} ++ ++operand a7_RegP() ++%{ ++ constraint(ALLOC_IN_RC(a7_long_reg)); ++ match(RegP); ++ match(mRegP); ++ match(no_T8_mRegP); ++ ++ format %{ %} ++ interface(REG_INTER); ++%} ++ ++operand v0_RegP() ++%{ ++ constraint(ALLOC_IN_RC(v0_long_reg)); ++ match(RegP); ++ match(mRegP); ++ match(no_T8_mRegP); ++ ++ format %{ %} ++ interface(REG_INTER); ++%} ++ ++operand v1_RegP() ++%{ ++ constraint(ALLOC_IN_RC(v1_long_reg)); ++ match(RegP); ++ match(mRegP); ++ match(no_T8_mRegP); ++ ++ format %{ %} ++ interface(REG_INTER); ++%} ++ ++/* ++operand mSPRegP(mRegP reg) %{ ++ constraint(ALLOC_IN_RC(sp_reg)); ++ match(reg); ++ ++ format %{ "SP" %} ++ interface(REG_INTER); ++%} ++ ++operand mFPRegP(mRegP reg) %{ ++ constraint(ALLOC_IN_RC(fp_reg)); ++ match(reg); ++ ++ format %{ "FP" %} ++ interface(REG_INTER); ++%} ++*/ ++ ++operand mRegL() %{ ++ constraint(ALLOC_IN_RC(long_reg)); ++ match(RegL); ++ ++ format %{ %} ++ interface(REG_INTER); ++%} ++ ++operand v0RegL() %{ ++ constraint(ALLOC_IN_RC(v0_long_reg)); ++ match(RegL); ++ match(mRegL); ++ ++ format %{ %} ++ interface(REG_INTER); ++%} ++ ++operand v1RegL() %{ ++ constraint(ALLOC_IN_RC(v1_long_reg)); ++ match(RegL); ++ match(mRegL); ++ ++ format %{ %} ++ interface(REG_INTER); ++%} ++ ++operand a0RegL() %{ ++ constraint(ALLOC_IN_RC(a0_long_reg)); ++ match(RegL); ++ match(mRegL); ++ ++ format %{ "A0" %} ++ interface(REG_INTER); ++%} ++ ++operand a1RegL() %{ ++ constraint(ALLOC_IN_RC(a1_long_reg)); ++ match(RegL); ++ match(mRegL); ++ ++ format %{ %} ++ interface(REG_INTER); ++%} ++ ++operand a2RegL() %{ ++ constraint(ALLOC_IN_RC(a2_long_reg)); ++ match(RegL); ++ match(mRegL); ++ ++ format %{ %} ++ interface(REG_INTER); ++%} ++ ++operand a3RegL() %{ ++ constraint(ALLOC_IN_RC(a3_long_reg)); ++ match(RegL); ++ match(mRegL); ++ ++ format %{ %} ++ interface(REG_INTER); ++%} ++ ++operand t0RegL() %{ ++ constraint(ALLOC_IN_RC(t0_long_reg)); ++ match(RegL); ++ match(mRegL); ++ ++ format %{ %} ++ interface(REG_INTER); ++%} ++ ++operand t1RegL() %{ ++ constraint(ALLOC_IN_RC(t1_long_reg)); ++ match(RegL); ++ match(mRegL); ++ ++ format %{ %} ++ interface(REG_INTER); ++%} ++ ++operand t2RegL() %{ ++ constraint(ALLOC_IN_RC(t2_long_reg)); ++ match(RegL); ++ match(mRegL); ++ ++ format %{ %} ++ interface(REG_INTER); ++%} ++ ++operand t3RegL() %{ ++ constraint(ALLOC_IN_RC(t3_long_reg)); ++ match(RegL); ++ match(mRegL); ++ ++ format %{ %} ++ interface(REG_INTER); ++%} ++ ++operand t8RegL() %{ ++ constraint(ALLOC_IN_RC(t8_long_reg)); ++ match(RegL); ++ match(mRegL); ++ ++ format %{ %} ++ interface(REG_INTER); ++%} ++ ++operand a4RegL() %{ ++ constraint(ALLOC_IN_RC(a4_long_reg)); ++ match(RegL); ++ match(mRegL); ++ ++ format %{ %} ++ interface(REG_INTER); ++%} ++ ++operand a5RegL() %{ ++ constraint(ALLOC_IN_RC(a5_long_reg)); ++ match(RegL); ++ match(mRegL); ++ ++ format %{ %} ++ interface(REG_INTER); ++%} ++ ++operand a6RegL() %{ ++ constraint(ALLOC_IN_RC(a6_long_reg)); ++ match(RegL); ++ match(mRegL); ++ ++ format %{ %} ++ interface(REG_INTER); ++%} ++ ++operand a7RegL() %{ ++ constraint(ALLOC_IN_RC(a7_long_reg)); ++ match(RegL); ++ match(mRegL); ++ ++ format %{ %} ++ interface(REG_INTER); ++%} ++ ++operand s0RegL() %{ ++ constraint(ALLOC_IN_RC(s0_long_reg)); ++ match(RegL); ++ match(mRegL); ++ ++ format %{ %} ++ interface(REG_INTER); ++%} ++ ++operand s1RegL() %{ ++ constraint(ALLOC_IN_RC(s1_long_reg)); ++ match(RegL); ++ match(mRegL); ++ ++ format %{ %} ++ interface(REG_INTER); ++%} ++ ++operand s2RegL() %{ ++ constraint(ALLOC_IN_RC(s2_long_reg)); ++ match(RegL); ++ match(mRegL); ++ ++ format %{ %} ++ interface(REG_INTER); ++%} ++ ++operand s3RegL() %{ ++ constraint(ALLOC_IN_RC(s3_long_reg)); ++ match(RegL); ++ match(mRegL); ++ ++ format %{ %} ++ interface(REG_INTER); ++%} ++ ++operand s4RegL() %{ ++ constraint(ALLOC_IN_RC(s4_long_reg)); ++ match(RegL); ++ match(mRegL); ++ ++ format %{ %} ++ interface(REG_INTER); ++%} ++ ++operand s7RegL() %{ ++ constraint(ALLOC_IN_RC(s7_long_reg)); ++ match(RegL); ++ match(mRegL); ++ ++ format %{ %} ++ interface(REG_INTER); ++%} ++ ++// Floating register operands ++operand regF() %{ ++ constraint(ALLOC_IN_RC(flt_reg)); ++ match(RegF); ++ ++ format %{ %} ++ interface(REG_INTER); ++%} ++ ++//Double Precision Floating register operands ++operand regD() %{ ++ constraint(ALLOC_IN_RC(dbl_reg)); ++ match(RegD); ++ ++ format %{ %} ++ interface(REG_INTER); ++%} ++ ++//----------Memory Operands---------------------------------------------------- ++// Indirect Memory Operand ++operand indirect(mRegP reg) %{ ++ constraint(ALLOC_IN_RC(p_reg)); ++ match(reg); ++ ++ format %{ "[$reg] @ indirect" %} ++ interface(MEMORY_INTER) %{ ++ base($reg); ++ index(0x0); /* NO_INDEX */ ++ scale(0x0); ++ disp(0x0); ++ %} ++%} ++ ++// Indirect Memory Plus Short Offset Operand ++operand indOffset8(mRegP reg, immL8 off) ++%{ ++ constraint(ALLOC_IN_RC(p_reg)); ++ match(AddP reg off); ++ ++ op_cost(10); ++ format %{ "[$reg + $off (8-bit)] @ indOffset8" %} ++ interface(MEMORY_INTER) %{ ++ base($reg); ++ index(0x0); /* NO_INDEX */ ++ scale(0x0); ++ disp($off); ++ %} ++%} ++ ++// Indirect Memory Times Scale Plus Index Register ++operand indIndexScale(mRegP reg, mRegL lreg, immI_0_3 scale) ++%{ ++ predicate(UseLEXT1); ++ constraint(ALLOC_IN_RC(p_reg)); ++ match(AddP reg (LShiftL lreg scale)); ++ ++ op_cost(10); ++ format %{"[$reg + $lreg << $scale] @ indIndexScale" %} ++ interface(MEMORY_INTER) %{ ++ base($reg); ++ index($lreg); ++ scale($scale); ++ disp(0x0); ++ %} ++%} ++ ++ ++// [base + index + offset] ++operand baseIndexOffset8(mRegP base, mRegL index, immL8 off) ++%{ ++ predicate(UseLEXT1); ++ constraint(ALLOC_IN_RC(p_reg)); ++ op_cost(5); ++ match(AddP (AddP base index) off); ++ ++ format %{ "[$base + $index + $off (8-bit)] @ baseIndexOffset8" %} ++ interface(MEMORY_INTER) %{ ++ base($base); ++ index($index); ++ scale(0x0); ++ disp($off); ++ %} ++%} ++ ++// [base + index + offset] ++operand baseIndexOffset8_convI2L(mRegP base, mRegI index, immL8 off) ++%{ ++ predicate(UseLEXT1); ++ constraint(ALLOC_IN_RC(p_reg)); ++ op_cost(5); ++ match(AddP (AddP base (ConvI2L index)) off); ++ ++ format %{ "[$base + $index + $off (8-bit)] @ baseIndexOffset8_convI2L" %} ++ interface(MEMORY_INTER) %{ ++ base($base); ++ index($index); ++ scale(0x0); ++ disp($off); ++ %} ++%} ++ ++// [base + index<in(2)->in(3)->in(1)->as_Type()->type()->is_long()->_lo >= 0); ++ op_cost(10); ++ match(AddP (AddP base (LShiftL (ConvI2L index) scale)) off); ++ ++ format %{ "[$base + $index << $scale + $off (8-bit)] @ basePosIndexScaleOffset8" %} ++ interface(MEMORY_INTER) %{ ++ base($base); ++ index($index); ++ scale($scale); ++ disp($off); ++ %} ++%} ++ ++//FIXME: I think it's better to limit the immI to be 16-bit at most! ++// Indirect Memory Plus Long Offset Operand ++operand indOffset32(mRegP reg, immL32 off) %{ ++ constraint(ALLOC_IN_RC(p_reg)); ++ op_cost(20); ++ match(AddP reg off); ++ ++ format %{ "[$reg + $off (32-bit)] @ indOffset32" %} ++ interface(MEMORY_INTER) %{ ++ base($reg); ++ index(0x0); /* NO_INDEX */ ++ scale(0x0); ++ disp($off); ++ %} ++%} ++ ++// Indirect Memory Plus Index Register ++operand indIndex(mRegP addr, mRegL index) %{ ++ constraint(ALLOC_IN_RC(p_reg)); ++ match(AddP addr index); ++ ++ op_cost(20); ++ format %{"[$addr + $index] @ indIndex" %} ++ interface(MEMORY_INTER) %{ ++ base($addr); ++ index($index); ++ scale(0x0); ++ disp(0x0); ++ %} ++%} ++ ++operand indirectNarrowKlass(mRegN reg) ++%{ ++ predicate(Universe::narrow_klass_shift() == 0); ++ constraint(ALLOC_IN_RC(p_reg)); ++ op_cost(10); ++ match(DecodeNKlass reg); ++ ++ format %{ "[$reg] @ indirectNarrowKlass" %} ++ interface(MEMORY_INTER) %{ ++ base($reg); ++ index(0x0); ++ scale(0x0); ++ disp(0x0); ++ %} ++%} ++ ++operand indOffset8NarrowKlass(mRegN reg, immL8 off) ++%{ ++ predicate(Universe::narrow_klass_shift() == 0); ++ constraint(ALLOC_IN_RC(p_reg)); ++ op_cost(10); ++ match(AddP (DecodeNKlass reg) off); ++ ++ format %{ "[$reg + $off (8-bit)] @ indOffset8NarrowKlass" %} ++ interface(MEMORY_INTER) %{ ++ base($reg); ++ index(0x0); ++ scale(0x0); ++ disp($off); ++ %} ++%} ++ ++operand indOffset32NarrowKlass(mRegN reg, immL32 off) ++%{ ++ predicate(Universe::narrow_klass_shift() == 0); ++ constraint(ALLOC_IN_RC(p_reg)); ++ op_cost(10); ++ match(AddP (DecodeNKlass reg) off); ++ ++ format %{ "[$reg + $off (32-bit)] @ indOffset32NarrowKlass" %} ++ interface(MEMORY_INTER) %{ ++ base($reg); ++ index(0x0); ++ scale(0x0); ++ disp($off); ++ %} ++%} ++ ++operand indIndexOffsetNarrowKlass(mRegN reg, mRegL lreg, immL32 off) ++%{ ++ predicate(UseLEXT1); ++ predicate(Universe::narrow_klass_shift() == 0); ++ constraint(ALLOC_IN_RC(p_reg)); ++ match(AddP (AddP (DecodeNKlass reg) lreg) off); ++ ++ op_cost(10); ++ format %{"[$reg + $off + $lreg] @ indIndexOffsetNarrowKlass" %} ++ interface(MEMORY_INTER) %{ ++ base($reg); ++ index($lreg); ++ scale(0x0); ++ disp($off); ++ %} ++%} ++ ++operand indIndexNarrowKlass(mRegN reg, mRegL lreg) ++%{ ++ predicate(Universe::narrow_klass_shift() == 0); ++ constraint(ALLOC_IN_RC(p_reg)); ++ match(AddP (DecodeNKlass reg) lreg); ++ ++ op_cost(10); ++ format %{"[$reg + $lreg] @ indIndexNarrowKlass" %} ++ interface(MEMORY_INTER) %{ ++ base($reg); ++ index($lreg); ++ scale(0x0); ++ disp(0x0); ++ %} ++%} ++ ++// Indirect Memory Operand ++operand indirectNarrow(mRegN reg) ++%{ ++ predicate(Universe::narrow_oop_shift() == 0); ++ constraint(ALLOC_IN_RC(p_reg)); ++ op_cost(10); ++ match(DecodeN reg); ++ ++ format %{ "[$reg] @ indirectNarrow" %} ++ interface(MEMORY_INTER) %{ ++ base($reg); ++ index(0x0); ++ scale(0x0); ++ disp(0x0); ++ %} ++%} ++ ++// Indirect Memory Plus Short Offset Operand ++operand indOffset8Narrow(mRegN reg, immL8 off) ++%{ ++ predicate(Universe::narrow_oop_shift() == 0); ++ constraint(ALLOC_IN_RC(p_reg)); ++ op_cost(10); ++ match(AddP (DecodeN reg) off); ++ ++ format %{ "[$reg + $off (8-bit)] @ indOffset8Narrow" %} ++ interface(MEMORY_INTER) %{ ++ base($reg); ++ index(0x0); ++ scale(0x0); ++ disp($off); ++ %} ++%} ++ ++// Indirect Memory Plus Index Register Plus Offset Operand ++operand indIndexOffset8Narrow(mRegN reg, mRegL lreg, immL8 off) ++%{ ++ predicate((Universe::narrow_oop_shift() == 0) && UseLEXT1); ++ constraint(ALLOC_IN_RC(p_reg)); ++ match(AddP (AddP (DecodeN reg) lreg) off); ++ ++ op_cost(10); ++ format %{"[$reg + $off + $lreg] @ indIndexOffset8Narrow" %} ++ interface(MEMORY_INTER) %{ ++ base($reg); ++ index($lreg); ++ scale(0x0); ++ disp($off); ++ %} ++%} ++ ++//----------Conditional Branch Operands---------------------------------------- ++// Comparison Op - This is the operation of the comparison, and is limited to ++// the following set of codes: ++// L (<), LE (<=), G (>), GE (>=), E (==), NE (!=) ++// ++// Other attributes of the comparison, such as unsignedness, are specified ++// by the comparison instruction that sets a condition code flags register. ++// That result is represented by a flags operand whose subtype is appropriate ++// to the unsignedness (etc.) of the comparison. ++// ++// Later, the instruction which matches both the Comparison Op (a Bool) and ++// the flags (produced by the Cmp) specifies the coding of the comparison op ++// by matching a specific subtype of Bool operand below, such as cmpOpU. ++ ++// Comparision Code ++operand cmpOp() %{ ++ match(Bool); ++ ++ format %{ "" %} ++ interface(COND_INTER) %{ ++ equal(0x01); ++ not_equal(0x02); ++ greater(0x03); ++ greater_equal(0x04); ++ less(0x05); ++ less_equal(0x06); ++ overflow(0x7); ++ no_overflow(0x8); ++ %} ++%} ++ ++ ++// Comparision Code ++// Comparison Code, unsigned compare. Used by FP also, with ++// C2 (unordered) turned into GT or LT already. The other bits ++// C0 and C3 are turned into Carry & Zero flags. ++operand cmpOpU() %{ ++ match(Bool); ++ ++ format %{ "" %} ++ interface(COND_INTER) %{ ++ equal(0x01); ++ not_equal(0x02); ++ greater(0x03); ++ greater_equal(0x04); ++ less(0x05); ++ less_equal(0x06); ++ overflow(0x7); ++ no_overflow(0x8); ++ %} ++%} ++ ++ ++//----------Special Memory Operands-------------------------------------------- ++// Stack Slot Operand - This operand is used for loading and storing temporary ++// values on the stack where a match requires a value to ++// flow through memory. ++operand stackSlotP(sRegP reg) %{ ++ constraint(ALLOC_IN_RC(stack_slots)); ++ // No match rule because this operand is only generated in matching ++ op_cost(50); ++ format %{ "[$reg]" %} ++ interface(MEMORY_INTER) %{ ++ base(0x1d); // SP ++ index(0x0); // No Index ++ scale(0x0); // No Scale ++ disp($reg); // Stack Offset ++ %} ++%} ++ ++operand stackSlotI(sRegI reg) %{ ++ constraint(ALLOC_IN_RC(stack_slots)); ++ // No match rule because this operand is only generated in matching ++ op_cost(50); ++ format %{ "[$reg]" %} ++ interface(MEMORY_INTER) %{ ++ base(0x1d); // SP ++ index(0x0); // No Index ++ scale(0x0); // No Scale ++ disp($reg); // Stack Offset ++ %} ++%} ++ ++operand stackSlotF(sRegF reg) %{ ++ constraint(ALLOC_IN_RC(stack_slots)); ++ // No match rule because this operand is only generated in matching ++ op_cost(50); ++ format %{ "[$reg]" %} ++ interface(MEMORY_INTER) %{ ++ base(0x1d); // SP ++ index(0x0); // No Index ++ scale(0x0); // No Scale ++ disp($reg); // Stack Offset ++ %} ++%} ++ ++operand stackSlotD(sRegD reg) %{ ++ constraint(ALLOC_IN_RC(stack_slots)); ++ // No match rule because this operand is only generated in matching ++ op_cost(50); ++ format %{ "[$reg]" %} ++ interface(MEMORY_INTER) %{ ++ base(0x1d); // SP ++ index(0x0); // No Index ++ scale(0x0); // No Scale ++ disp($reg); // Stack Offset ++ %} ++%} ++ ++operand stackSlotL(sRegL reg) %{ ++ constraint(ALLOC_IN_RC(stack_slots)); ++ // No match rule because this operand is only generated in matching ++ op_cost(50); ++ format %{ "[$reg]" %} ++ interface(MEMORY_INTER) %{ ++ base(0x1d); // SP ++ index(0x0); // No Index ++ scale(0x0); // No Scale ++ disp($reg); // Stack Offset ++ %} ++%} ++ ++ ++//------------------------OPERAND CLASSES-------------------------------------- ++//opclass memory( direct, indirect, indOffset16, indOffset32, indOffset32X, indIndexOffset ); ++opclass memory( indirect, indirectNarrow, indOffset8, indOffset32, indIndex, indIndexScale, baseIndexOffset8, baseIndexOffset8_convI2L, indOffset8Narrow, indIndexOffset8Narrow); ++ ++ ++//----------PIPELINE----------------------------------------------------------- ++// Rules which define the behavior of the target architectures pipeline. ++ ++pipeline %{ ++ ++ //----------ATTRIBUTES--------------------------------------------------------- ++ attributes %{ ++ fixed_size_instructions; // Fixed size instructions ++ branch_has_delay_slot; // branch have delay slot in gs2 ++ max_instructions_per_bundle = 1; // 1 instruction per bundle ++ max_bundles_per_cycle = 4; // Up to 4 bundles per cycle ++ bundle_unit_size=4; ++ instruction_unit_size = 4; // An instruction is 4 bytes long ++ instruction_fetch_unit_size = 16; // The processor fetches one line ++ instruction_fetch_units = 1; // of 16 bytes ++ ++ // List of nop instructions ++ nops( MachNop ); ++ %} ++ ++ //----------RESOURCES---------------------------------------------------------- ++ // Resources are the functional units available to the machine ++ ++ resources(D1, D2, D3, D4, DECODE = D1 | D2 | D3| D4, ALU1, ALU2, ALU = ALU1 | ALU2, FPU1, FPU2, FPU = FPU1 | FPU2, MEM, BR); ++ ++ //----------PIPELINE DESCRIPTION----------------------------------------------- ++ // Pipeline Description specifies the stages in the machine's pipeline ++ ++ // IF: fetch ++ // ID: decode ++ // RD: read ++ // CA: caculate ++ // WB: write back ++ // CM: commit ++ ++ pipe_desc(IF, ID, RD, CA, WB, CM); ++ ++ ++ //----------PIPELINE CLASSES--------------------------------------------------- ++ // Pipeline Classes describe the stages in which input and output are ++ // referenced by the hardware pipeline. ++ ++ //No.1 Integer ALU reg-reg operation : dst <-- reg1 op reg2 ++ pipe_class ialu_regI_regI(mRegI dst, mRegI src1, mRegI src2) %{ ++ single_instruction; ++ src1 : RD(read); ++ src2 : RD(read); ++ dst : WB(write)+1; ++ DECODE : ID; ++ ALU : CA; ++ %} ++ ++ //No.19 Integer mult operation : dst <-- reg1 mult reg2 ++ pipe_class ialu_mult(mRegI dst, mRegI src1, mRegI src2) %{ ++ src1 : RD(read); ++ src2 : RD(read); ++ dst : WB(write)+5; ++ DECODE : ID; ++ ALU2 : CA; ++ %} ++ ++ pipe_class mulL_reg_reg(mRegL dst, mRegL src1, mRegL src2) %{ ++ src1 : RD(read); ++ src2 : RD(read); ++ dst : WB(write)+10; ++ DECODE : ID; ++ ALU2 : CA; ++ %} ++ ++ //No.19 Integer div operation : dst <-- reg1 div reg2 ++ pipe_class ialu_div(mRegI dst, mRegI src1, mRegI src2) %{ ++ src1 : RD(read); ++ src2 : RD(read); ++ dst : WB(write)+10; ++ DECODE : ID; ++ ALU2 : CA; ++ %} ++ ++ //No.19 Integer mod operation : dst <-- reg1 mod reg2 ++ pipe_class ialu_mod(mRegI dst, mRegI src1, mRegI src2) %{ ++ instruction_count(2); ++ src1 : RD(read); ++ src2 : RD(read); ++ dst : WB(write)+10; ++ DECODE : ID; ++ ALU2 : CA; ++ %} ++ ++ //No.15 Long ALU reg-reg operation : dst <-- reg1 op reg2 ++ pipe_class ialu_regL_regL(mRegL dst, mRegL src1, mRegL src2) %{ ++ instruction_count(2); ++ src1 : RD(read); ++ src2 : RD(read); ++ dst : WB(write); ++ DECODE : ID; ++ ALU : CA; ++ %} ++ ++ //No.18 Long ALU reg-imm16 operation : dst <-- reg1 op imm16 ++ pipe_class ialu_regL_imm16(mRegL dst, mRegL src) %{ ++ instruction_count(2); ++ src : RD(read); ++ dst : WB(write); ++ DECODE : ID; ++ ALU : CA; ++ %} ++ ++ //no.16 load Long from memory : ++ pipe_class ialu_loadL(mRegL dst, memory mem) %{ ++ instruction_count(2); ++ mem : RD(read); ++ dst : WB(write)+5; ++ DECODE : ID; ++ MEM : RD; ++ %} ++ ++ //No.17 Store Long to Memory : ++ pipe_class ialu_storeL(mRegL src, memory mem) %{ ++ instruction_count(2); ++ mem : RD(read); ++ src : RD(read); ++ DECODE : ID; ++ MEM : RD; ++ %} ++ ++ //No.2 Integer ALU reg-imm16 operation : dst <-- reg1 op imm16 ++ pipe_class ialu_regI_imm16(mRegI dst, mRegI src) %{ ++ single_instruction; ++ src : RD(read); ++ dst : WB(write); ++ DECODE : ID; ++ ALU : CA; ++ %} ++ ++ //No.3 Integer move operation : dst <-- reg ++ pipe_class ialu_regI_mov(mRegI dst, mRegI src) %{ ++ src : RD(read); ++ dst : WB(write); ++ DECODE : ID; ++ ALU : CA; ++ %} ++ ++ //No.4 No instructions : do nothing ++ pipe_class empty( ) %{ ++ instruction_count(0); ++ %} ++ ++ //No.5 UnConditional branch : ++ pipe_class pipe_jump( label labl ) %{ ++ multiple_bundles; ++ DECODE : ID; ++ BR : RD; ++ %} ++ ++ //No.6 ALU Conditional branch : ++ pipe_class pipe_alu_branch(mRegI src1, mRegI src2, label labl ) %{ ++ multiple_bundles; ++ src1 : RD(read); ++ src2 : RD(read); ++ DECODE : ID; ++ BR : RD; ++ %} ++ ++ //no.7 load integer from memory : ++ pipe_class ialu_loadI(mRegI dst, memory mem) %{ ++ mem : RD(read); ++ dst : WB(write)+3; ++ DECODE : ID; ++ MEM : RD; ++ %} ++ ++ //No.8 Store Integer to Memory : ++ pipe_class ialu_storeI(mRegI src, memory mem) %{ ++ mem : RD(read); ++ src : RD(read); ++ DECODE : ID; ++ MEM : RD; ++ %} ++ ++ ++ //No.10 Floating FPU reg-reg operation : dst <-- reg1 op reg2 ++ pipe_class fpu_regF_regF(regF dst, regF src1, regF src2) %{ ++ src1 : RD(read); ++ src2 : RD(read); ++ dst : WB(write); ++ DECODE : ID; ++ FPU : CA; ++ %} ++ ++ //No.22 Floating div operation : dst <-- reg1 div reg2 ++ pipe_class fpu_div(regF dst, regF src1, regF src2) %{ ++ src1 : RD(read); ++ src2 : RD(read); ++ dst : WB(write); ++ DECODE : ID; ++ FPU2 : CA; ++ %} ++ ++ pipe_class fcvt_I2D(regD dst, mRegI src) %{ ++ src : RD(read); ++ dst : WB(write); ++ DECODE : ID; ++ FPU1 : CA; ++ %} ++ ++ pipe_class fcvt_D2I(mRegI dst, regD src) %{ ++ src : RD(read); ++ dst : WB(write); ++ DECODE : ID; ++ FPU1 : CA; ++ %} ++ ++ pipe_class pipe_mfc1(mRegI dst, regD src) %{ ++ src : RD(read); ++ dst : WB(write); ++ DECODE : ID; ++ MEM : RD; ++ %} ++ ++ pipe_class pipe_mtc1(regD dst, mRegI src) %{ ++ src : RD(read); ++ dst : WB(write); ++ DECODE : ID; ++ MEM : RD(5); ++ %} ++ ++ //No.23 Floating sqrt operation : dst <-- reg1 sqrt reg2 ++ pipe_class fpu_sqrt(regF dst, regF src1, regF src2) %{ ++ multiple_bundles; ++ src1 : RD(read); ++ src2 : RD(read); ++ dst : WB(write); ++ DECODE : ID; ++ FPU2 : CA; ++ %} ++ ++ //No.11 Load Floating from Memory : ++ pipe_class fpu_loadF(regF dst, memory mem) %{ ++ instruction_count(1); ++ mem : RD(read); ++ dst : WB(write)+3; ++ DECODE : ID; ++ MEM : RD; ++ %} ++ ++ //No.12 Store Floating to Memory : ++ pipe_class fpu_storeF(regF src, memory mem) %{ ++ instruction_count(1); ++ mem : RD(read); ++ src : RD(read); ++ DECODE : ID; ++ MEM : RD; ++ %} ++ ++ //No.13 FPU Conditional branch : ++ pipe_class pipe_fpu_branch(regF src1, regF src2, label labl ) %{ ++ multiple_bundles; ++ src1 : RD(read); ++ src2 : RD(read); ++ DECODE : ID; ++ BR : RD; ++ %} ++ ++//No.14 Floating FPU reg operation : dst <-- op reg ++ pipe_class fpu1_regF(regF dst, regF src) %{ ++ src : RD(read); ++ dst : WB(write); ++ DECODE : ID; ++ FPU : CA; ++ %} ++ ++ pipe_class long_memory_op() %{ ++ instruction_count(10); multiple_bundles; force_serialization; ++ fixed_latency(30); ++ %} ++ ++ pipe_class simple_call() %{ ++ instruction_count(10); multiple_bundles; force_serialization; ++ fixed_latency(200); ++ BR : RD; ++ %} ++ ++ pipe_class call() %{ ++ instruction_count(10); multiple_bundles; force_serialization; ++ fixed_latency(200); ++ %} ++ ++ //FIXME: ++ //No.9 Piple slow : for multi-instructions ++ pipe_class pipe_slow( ) %{ ++ instruction_count(20); ++ force_serialization; ++ multiple_bundles; ++ fixed_latency(50); ++ %} ++ ++%} ++ ++ ++ ++//----------INSTRUCTIONS------------------------------------------------------- ++// ++// match -- States which machine-independent subtree may be replaced ++// by this instruction. ++// ins_cost -- The estimated cost of this instruction is used by instruction ++// selection to identify a minimum cost tree of machine ++// instructions that matches a tree of machine-independent ++// instructions. ++// format -- A string providing the disassembly for this instruction. ++// The value of an instruction's operand may be inserted ++// by referring to it with a '$' prefix. ++// opcode -- Three instruction opcodes may be provided. These are referred ++// to within an encode class as $primary, $secondary, and $tertiary ++// respectively. The primary opcode is commonly used to ++// indicate the type of machine instruction, while secondary ++// and tertiary are often used for prefix options or addressing ++// modes. ++// ins_encode -- A list of encode classes with parameters. The encode class ++// name must have been defined in an 'enc_class' specification ++// in the encode section of the architecture description. ++ ++ ++// Load Integer ++instruct loadI(mRegI dst, memory mem) %{ ++ match(Set dst (LoadI mem)); ++ ++ ins_cost(125); ++ format %{ "lw $dst, $mem #@loadI" %} ++ ins_encode (load_I_enc(dst, mem)); ++ ins_pipe( ialu_loadI ); ++%} ++ ++instruct loadI_convI2L(mRegL dst, memory mem) %{ ++ match(Set dst (ConvI2L (LoadI mem))); ++ ++ ins_cost(125); ++ format %{ "lw $dst, $mem #@loadI_convI2L" %} ++ ins_encode (load_I_enc(dst, mem)); ++ ins_pipe( ialu_loadI ); ++%} ++ ++// Load Integer (32 bit signed) to Byte (8 bit signed) ++instruct loadI2B(mRegI dst, memory mem, immI_24 twentyfour) %{ ++ match(Set dst (RShiftI (LShiftI (LoadI mem) twentyfour) twentyfour)); ++ ++ ins_cost(125); ++ format %{ "lb $dst, $mem\t# int -> byte #@loadI2B" %} ++ ins_encode(load_B_enc(dst, mem)); ++ ins_pipe(ialu_loadI); ++%} ++ ++// Load Integer (32 bit signed) to Unsigned Byte (8 bit UNsigned) ++instruct loadI2UB(mRegI dst, memory mem, immI_255 mask) %{ ++ match(Set dst (AndI (LoadI mem) mask)); ++ ++ ins_cost(125); ++ format %{ "lbu $dst, $mem\t# int -> ubyte #@loadI2UB" %} ++ ins_encode(load_UB_enc(dst, mem)); ++ ins_pipe(ialu_loadI); ++%} ++ ++// Load Integer (32 bit signed) to Short (16 bit signed) ++instruct loadI2S(mRegI dst, memory mem, immI_16 sixteen) %{ ++ match(Set dst (RShiftI (LShiftI (LoadI mem) sixteen) sixteen)); ++ ++ ins_cost(125); ++ format %{ "lh $dst, $mem\t# int -> short #@loadI2S" %} ++ ins_encode(load_S_enc(dst, mem)); ++ ins_pipe(ialu_loadI); ++%} ++ ++// Load Integer (32 bit signed) to Unsigned Short/Char (16 bit UNsigned) ++instruct loadI2US(mRegI dst, memory mem, immI_65535 mask) %{ ++ match(Set dst (AndI (LoadI mem) mask)); ++ ++ ins_cost(125); ++ format %{ "lhu $dst, $mem\t# int -> ushort/char #@loadI2US" %} ++ ins_encode(load_C_enc(dst, mem)); ++ ins_pipe(ialu_loadI); ++%} ++ ++// Load Long. ++instruct loadL(mRegL dst, memory mem) %{ ++// predicate(!((LoadLNode*)n)->require_atomic_access()); ++ match(Set dst (LoadL mem)); ++ ++ ins_cost(250); ++ format %{ "ld $dst, $mem #@loadL" %} ++ ins_encode(load_L_enc(dst, mem)); ++ ins_pipe( ialu_loadL ); ++%} ++ ++// Load Long - UNaligned ++instruct loadL_unaligned(mRegL dst, memory mem) %{ ++ match(Set dst (LoadL_unaligned mem)); ++ ++ // FIXME: Need more effective ldl/ldr ++ ins_cost(450); ++ format %{ "ld $dst, $mem #@loadL_unaligned\n\t" %} ++ ins_encode(load_L_enc(dst, mem)); ++ ins_pipe( ialu_loadL ); ++%} ++ ++// Store Long ++instruct storeL_reg(memory mem, mRegL src) %{ ++ match(Set mem (StoreL mem src)); ++ ++ ins_cost(200); ++ format %{ "sd $mem, $src #@storeL_reg\n" %} ++ ins_encode(store_L_reg_enc(mem, src)); ++ ins_pipe( ialu_storeL ); ++%} ++ ++instruct storeL_immL_0(memory mem, immL_0 zero) %{ ++ match(Set mem (StoreL mem zero)); ++ ++ ins_cost(180); ++ format %{ "sd zero, $mem #@storeL_immL_0" %} ++ ins_encode(store_L_immL_0_enc(mem, zero)); ++ ins_pipe( ialu_storeL ); ++%} ++ ++instruct storeL_imm(memory mem, immL src) %{ ++ match(Set mem (StoreL mem src)); ++ ++ ins_cost(200); ++ format %{ "sd $src, $mem #@storeL_imm" %} ++ ins_encode(store_L_immL_enc(mem, src)); ++ ins_pipe( ialu_storeL ); ++%} ++ ++// Load Compressed Pointer ++instruct loadN(mRegN dst, memory mem) ++%{ ++ match(Set dst (LoadN mem)); ++ ++ ins_cost(125); // XXX ++ format %{ "lwu $dst, $mem\t# compressed ptr @ loadN" %} ++ ins_encode (load_N_enc(dst, mem)); ++ ins_pipe( ialu_loadI ); // XXX ++%} ++ ++instruct loadN2P(mRegP dst, memory mem) ++%{ ++ match(Set dst (DecodeN (LoadN mem))); ++ predicate(Universe::narrow_oop_base() == NULL && Universe::narrow_oop_shift() == 0); ++ ++ ins_cost(125); // XXX ++ format %{ "lwu $dst, $mem\t# @ loadN2P" %} ++ ins_encode (load_N_enc(dst, mem)); ++ ins_pipe( ialu_loadI ); // XXX ++%} ++ ++// Load Pointer ++instruct loadP(mRegP dst, memory mem) %{ ++ match(Set dst (LoadP mem)); ++ ++ ins_cost(125); ++ format %{ "ld $dst, $mem #@loadP" %} ++ ins_encode (load_P_enc(dst, mem)); ++ ins_pipe( ialu_loadI ); ++%} ++ ++// Load Klass Pointer ++instruct loadKlass(mRegP dst, memory mem) %{ ++ match(Set dst (LoadKlass mem)); ++ ++ ins_cost(125); ++ format %{ "MOV $dst,$mem @ loadKlass" %} ++ ins_encode (load_P_enc(dst, mem)); ++ ins_pipe( ialu_loadI ); ++%} ++ ++// Load narrow Klass Pointer ++instruct loadNKlass(mRegN dst, memory mem) ++%{ ++ match(Set dst (LoadNKlass mem)); ++ ++ ins_cost(125); // XXX ++ format %{ "lwu $dst, $mem\t# compressed klass ptr @ loadNKlass" %} ++ ins_encode (load_N_enc(dst, mem)); ++ ins_pipe( ialu_loadI ); // XXX ++%} ++ ++instruct loadN2PKlass(mRegP dst, memory mem) ++%{ ++ match(Set dst (DecodeNKlass (LoadNKlass mem))); ++ predicate(Universe::narrow_klass_base() == NULL && Universe::narrow_klass_shift() == 0); ++ ++ ins_cost(125); // XXX ++ format %{ "lwu $dst, $mem\t# compressed klass ptr @ loadN2PKlass" %} ++ ins_encode (load_N_enc(dst, mem)); ++ ins_pipe( ialu_loadI ); // XXX ++%} ++ ++// Load Constant ++instruct loadConI(mRegI dst, immI src) %{ ++ match(Set dst src); ++ ++ ins_cost(150); ++ format %{ "mov $dst, $src #@loadConI" %} ++ ins_encode %{ ++ Register dst = $dst$$Register; ++ int value = $src$$constant; ++ __ move(dst, value); ++ %} ++ ins_pipe( ialu_regI_regI ); ++%} ++ ++ ++instruct loadConL_set64(mRegL dst, immL src) %{ ++ match(Set dst src); ++ ins_cost(120); ++ format %{ "li $dst, $src @ loadConL_set64" %} ++ ins_encode %{ ++ __ set64($dst$$Register, $src$$constant); ++ %} ++ ins_pipe(ialu_regL_regL); ++%} ++ ++instruct loadConL16(mRegL dst, immL16 src) %{ ++ match(Set dst src); ++ ins_cost(105); ++ format %{ "mov $dst, $src #@loadConL16" %} ++ ins_encode %{ ++ Register dst_reg = as_Register($dst$$reg); ++ int value = $src$$constant; ++ __ daddiu(dst_reg, R0, value); ++ %} ++ ins_pipe( ialu_regL_regL ); ++%} ++ ++ ++instruct loadConL_immL_0(mRegL dst, immL_0 src) %{ ++ match(Set dst src); ++ ins_cost(100); ++ format %{ "mov $dst, zero #@loadConL_immL_0" %} ++ ins_encode %{ ++ Register dst_reg = as_Register($dst$$reg); ++ __ daddu(dst_reg, R0, R0); ++ %} ++ ins_pipe( ialu_regL_regL ); ++%} ++ ++// Load Range ++instruct loadRange(mRegI dst, memory mem) %{ ++ match(Set dst (LoadRange mem)); ++ ++ ins_cost(125); ++ format %{ "MOV $dst,$mem @ loadRange" %} ++ ins_encode(load_I_enc(dst, mem)); ++ ins_pipe( ialu_loadI ); ++%} ++ ++ ++instruct storeP(memory mem, mRegP src ) %{ ++ match(Set mem (StoreP mem src)); ++ ++ ins_cost(125); ++ format %{ "sd $src, $mem #@storeP" %} ++ ins_encode(store_P_reg_enc(mem, src)); ++ ins_pipe( ialu_storeI ); ++%} ++ ++// Store NULL Pointer, mark word, or other simple pointer constant. ++instruct storeImmP_immP_0(memory mem, immP_0 zero) %{ ++ match(Set mem (StoreP mem zero)); ++ ++ ins_cost(125); ++ format %{ "mov $mem, $zero #@storeImmP_immP_0" %} ++ ins_encode(store_P_immP0_enc(mem)); ++ ins_pipe( ialu_storeI ); ++%} ++ ++// Store Byte Immediate ++instruct storeImmB(memory mem, immI8 src) %{ ++ match(Set mem (StoreB mem src)); ++ ++ ins_cost(150); ++ format %{ "movb $mem, $src #@storeImmB" %} ++ ins_encode(store_B_immI_enc(mem, src)); ++ ins_pipe( ialu_storeI ); ++%} ++ ++// Store Compressed Pointer ++instruct storeN(memory mem, mRegN src) ++%{ ++ match(Set mem (StoreN mem src)); ++ ++ ins_cost(125); // XXX ++ format %{ "sw $mem, $src\t# compressed ptr @ storeN" %} ++ ins_encode(store_N_reg_enc(mem, src)); ++ ins_pipe( ialu_storeI ); ++%} ++ ++instruct storeP2N(memory mem, mRegP src) ++%{ ++ match(Set mem (StoreN mem (EncodeP src))); ++ predicate(Universe::narrow_oop_base() == NULL && Universe::narrow_oop_shift() == 0); ++ ++ ins_cost(125); // XXX ++ format %{ "sw $mem, $src\t# @ storeP2N" %} ++ ins_encode(store_N_reg_enc(mem, src)); ++ ins_pipe( ialu_storeI ); ++%} ++ ++instruct storeNKlass(memory mem, mRegN src) ++%{ ++ match(Set mem (StoreNKlass mem src)); ++ ++ ins_cost(125); // XXX ++ format %{ "sw $mem, $src\t# compressed klass ptr @ storeNKlass" %} ++ ins_encode(store_N_reg_enc(mem, src)); ++ ins_pipe( ialu_storeI ); ++%} ++ ++instruct storeP2NKlass(memory mem, mRegP src) ++%{ ++ match(Set mem (StoreNKlass mem (EncodePKlass src))); ++ predicate(Universe::narrow_klass_base() == NULL && Universe::narrow_klass_shift() == 0); ++ ++ ins_cost(125); // XXX ++ format %{ "sw $mem, $src\t# @ storeP2NKlass" %} ++ ins_encode(store_N_reg_enc(mem, src)); ++ ins_pipe( ialu_storeI ); ++%} ++ ++instruct storeImmN_immN_0(memory mem, immN_0 zero) ++%{ ++ match(Set mem (StoreN mem zero)); ++ ++ ins_cost(125); // XXX ++ format %{ "storeN0 zero, $mem\t# compressed ptr" %} ++ ins_encode(storeImmN0_enc(mem, zero)); ++ ins_pipe( ialu_storeI ); ++%} ++ ++// Store Byte ++instruct storeB(memory mem, mRegI src) %{ ++ match(Set mem (StoreB mem src)); ++ ++ ins_cost(125); ++ format %{ "sb $src, $mem #@storeB" %} ++ ins_encode(store_B_reg_enc(mem, src)); ++ ins_pipe( ialu_storeI ); ++%} ++ ++instruct storeB_convL2I(memory mem, mRegL src) %{ ++ match(Set mem (StoreB mem (ConvL2I src))); ++ ++ ins_cost(125); ++ format %{ "sb $src, $mem #@storeB_convL2I" %} ++ ins_encode(store_B_reg_enc(mem, src)); ++ ins_pipe( ialu_storeI ); ++%} ++ ++// Load Byte (8bit signed) ++instruct loadB(mRegI dst, memory mem) %{ ++ match(Set dst (LoadB mem)); ++ ++ ins_cost(125); ++ format %{ "lb $dst, $mem #@loadB" %} ++ ins_encode(load_B_enc(dst, mem)); ++ ins_pipe( ialu_loadI ); ++%} ++ ++instruct loadB_convI2L(mRegL dst, memory mem) %{ ++ match(Set dst (ConvI2L (LoadB mem))); ++ ++ ins_cost(125); ++ format %{ "lb $dst, $mem #@loadB_convI2L" %} ++ ins_encode(load_B_enc(dst, mem)); ++ ins_pipe( ialu_loadI ); ++%} ++ ++// Load Byte (8bit UNsigned) ++instruct loadUB(mRegI dst, memory mem) %{ ++ match(Set dst (LoadUB mem)); ++ ++ ins_cost(125); ++ format %{ "lbu $dst, $mem #@loadUB" %} ++ ins_encode(load_UB_enc(dst, mem)); ++ ins_pipe( ialu_loadI ); ++%} ++ ++instruct loadUB_convI2L(mRegL dst, memory mem) %{ ++ match(Set dst (ConvI2L (LoadUB mem))); ++ ++ ins_cost(125); ++ format %{ "lbu $dst, $mem #@loadUB_convI2L" %} ++ ins_encode(load_UB_enc(dst, mem)); ++ ins_pipe( ialu_loadI ); ++%} ++ ++// Load Short (16bit signed) ++instruct loadS(mRegI dst, memory mem) %{ ++ match(Set dst (LoadS mem)); ++ ++ ins_cost(125); ++ format %{ "lh $dst, $mem #@loadS" %} ++ ins_encode(load_S_enc(dst, mem)); ++ ins_pipe( ialu_loadI ); ++%} ++ ++// Load Short (16 bit signed) to Byte (8 bit signed) ++instruct loadS2B(mRegI dst, memory mem, immI_24 twentyfour) %{ ++ match(Set dst (RShiftI (LShiftI (LoadS mem) twentyfour) twentyfour)); ++ ++ ins_cost(125); ++ format %{ "lb $dst, $mem\t# short -> byte #@loadS2B" %} ++ ins_encode(load_B_enc(dst, mem)); ++ ins_pipe(ialu_loadI); ++%} ++ ++instruct loadS_convI2L(mRegL dst, memory mem) %{ ++ match(Set dst (ConvI2L (LoadS mem))); ++ ++ ins_cost(125); ++ format %{ "lh $dst, $mem #@loadS_convI2L" %} ++ ins_encode(load_S_enc(dst, mem)); ++ ins_pipe( ialu_loadI ); ++%} ++ ++// Store Integer Immediate ++instruct storeImmI(memory mem, immI src) %{ ++ match(Set mem (StoreI mem src)); ++ ++ ins_cost(150); ++ format %{ "mov $mem, $src #@storeImmI" %} ++ ins_encode(store_I_immI_enc(mem, src)); ++ ins_pipe( ialu_storeI ); ++%} ++ ++// Store Integer ++instruct storeI(memory mem, mRegI src) %{ ++ match(Set mem (StoreI mem src)); ++ ++ ins_cost(125); ++ format %{ "sw $mem, $src #@storeI" %} ++ ins_encode(store_I_reg_enc(mem, src)); ++ ins_pipe( ialu_storeI ); ++%} ++ ++instruct storeI_convL2I(memory mem, mRegL src) %{ ++ match(Set mem (StoreI mem (ConvL2I src))); ++ ++ ins_cost(125); ++ format %{ "sw $mem, $src #@storeI_convL2I" %} ++ ins_encode(store_I_reg_enc(mem, src)); ++ ins_pipe( ialu_storeI ); ++%} ++ ++// Load Float ++instruct loadF(regF dst, memory mem) %{ ++ match(Set dst (LoadF mem)); ++ ++ ins_cost(150); ++ format %{ "loadF $dst, $mem #@loadF" %} ++ ins_encode(load_F_enc(dst, mem)); ++ ins_pipe( ialu_loadI ); ++%} ++ ++instruct loadConP_general(mRegP dst, immP src) %{ ++ match(Set dst src); ++ ++ ins_cost(120); ++ format %{ "li $dst, $src #@loadConP_general" %} ++ ++ ins_encode %{ ++ Register dst = $dst$$Register; ++ long* value = (long*)$src$$constant; ++ ++ if($src->constant_reloc() == relocInfo::metadata_type){ ++ int klass_index = __ oop_recorder()->find_index((Klass*)value); ++ RelocationHolder rspec = metadata_Relocation::spec(klass_index); ++ ++ __ relocate(rspec); ++ __ patchable_set48(dst, (long)value); ++ } else if($src->constant_reloc() == relocInfo::oop_type){ ++ int oop_index = __ oop_recorder()->find_index((jobject)value); ++ RelocationHolder rspec = oop_Relocation::spec(oop_index); ++ ++ __ relocate(rspec); ++ __ patchable_set48(dst, (long)value); ++ } else if ($src->constant_reloc() == relocInfo::none) { ++ __ set64(dst, (long)value); ++ } ++ %} ++ ++ ins_pipe( ialu_regI_regI ); ++%} ++ ++instruct loadConP_no_oop_cheap(mRegP dst, immP_no_oop_cheap src) %{ ++ match(Set dst src); ++ ++ ins_cost(80); ++ format %{ "li $dst, $src @ loadConP_no_oop_cheap" %} ++ ++ ins_encode %{ ++ __ set64($dst$$Register, $src$$constant); ++ %} ++ ++ ins_pipe(ialu_regI_regI); ++%} ++ ++ ++instruct loadConP_poll(mRegP dst, immP_poll src) %{ ++ match(Set dst src); ++ ++ ins_cost(50); ++ format %{ "li $dst, $src #@loadConP_poll" %} ++ ++ ins_encode %{ ++ Register dst = $dst$$Register; ++ intptr_t value = (intptr_t)$src$$constant; ++ ++ __ set64(dst, (jlong)value); ++ %} ++ ++ ins_pipe( ialu_regI_regI ); ++%} ++ ++instruct loadConP_immP_0(mRegP dst, immP_0 src) ++%{ ++ match(Set dst src); ++ ++ ins_cost(50); ++ format %{ "mov $dst, R0\t# ptr" %} ++ ins_encode %{ ++ Register dst_reg = $dst$$Register; ++ __ daddu(dst_reg, R0, R0); ++ %} ++ ins_pipe( ialu_regI_regI ); ++%} ++ ++instruct loadConN_immN_0(mRegN dst, immN_0 src) %{ ++ match(Set dst src); ++ format %{ "move $dst, R0\t# compressed NULL ptr" %} ++ ins_encode %{ ++ __ move($dst$$Register, R0); ++ %} ++ ins_pipe( ialu_regI_regI ); ++%} ++ ++instruct loadConN(mRegN dst, immN src) %{ ++ match(Set dst src); ++ ++ ins_cost(125); ++ format %{ "li $dst, $src\t# compressed ptr @ loadConN" %} ++ ins_encode %{ ++ Register dst = $dst$$Register; ++ __ set_narrow_oop(dst, (jobject)$src$$constant); ++ %} ++ ins_pipe( ialu_regI_regI ); // XXX ++%} ++ ++instruct loadConNKlass(mRegN dst, immNKlass src) %{ ++ match(Set dst src); ++ ++ ins_cost(125); ++ format %{ "li $dst, $src\t# compressed klass ptr @ loadConNKlass" %} ++ ins_encode %{ ++ Register dst = $dst$$Register; ++ __ set_narrow_klass(dst, (Klass*)$src$$constant); ++ %} ++ ins_pipe( ialu_regI_regI ); // XXX ++%} ++ ++//FIXME ++// Tail Call; Jump from runtime stub to Java code. ++// Also known as an 'interprocedural jump'. ++// Target of jump will eventually return to caller. ++// TailJump below removes the return address. ++instruct TailCalljmpInd(mRegP jump_target, mRegP method_oop) %{ ++ match(TailCall jump_target method_oop ); ++ ins_cost(300); ++ format %{ "JMP $jump_target \t# @TailCalljmpInd" %} ++ ++ ins_encode %{ ++ Register target = $jump_target$$Register; ++ Register oop = $method_oop$$Register; ++ ++ // RA will be used in generate_forward_exception() ++ __ push(RA); ++ ++ __ move(S3, oop); ++ __ jr(target); ++ __ delayed()->nop(); ++ %} ++ ++ ins_pipe( pipe_jump ); ++%} ++ ++// Create exception oop: created by stack-crawling runtime code. ++// Created exception is now available to this handler, and is setup ++// just prior to jumping to this handler. No code emitted. ++instruct CreateException( a0_RegP ex_oop ) ++%{ ++ match(Set ex_oop (CreateEx)); ++ ++ // use the following format syntax ++ format %{ "# exception oop is in A0; no code emitted @CreateException" %} ++ ins_encode %{ ++ // X86 leaves this function empty ++ __ block_comment("CreateException is empty in MIPS"); ++ %} ++ ins_pipe( empty ); ++// ins_pipe( pipe_jump ); ++%} ++ ++ ++/* The mechanism of exception handling is clear now. ++ ++- Common try/catch: ++ [stubGenerator_mips.cpp] generate_forward_exception() ++ |- V0, V1 are created ++ |- T9 <= SharedRuntime::exception_handler_for_return_address ++ `- jr T9 ++ `- the caller's exception_handler ++ `- jr OptoRuntime::exception_blob ++ `- here ++- Rethrow(e.g. 'unwind'): ++ * The callee: ++ |- an exception is triggered during execution ++ `- exits the callee method through RethrowException node ++ |- The callee pushes exception_oop(T0) and exception_pc(RA) ++ `- The callee jumps to OptoRuntime::rethrow_stub() ++ * In OptoRuntime::rethrow_stub: ++ |- The VM calls _rethrow_Java to determine the return address in the caller method ++ `- exits the stub with tailjmpInd ++ |- pops exception_oop(V0) and exception_pc(V1) ++ `- jumps to the return address(usually an exception_handler) ++ * The caller: ++ `- continues processing the exception_blob with V0/V1 ++*/ ++ ++// Rethrow exception: ++// The exception oop will come in the first argument position. ++// Then JUMP (not call) to the rethrow stub code. ++instruct RethrowException() ++%{ ++ match(Rethrow); ++ ++ // use the following format syntax ++ format %{ "JMP rethrow_stub #@RethrowException" %} ++ ins_encode %{ ++ __ block_comment("@ RethrowException"); ++ ++ cbuf.set_insts_mark(); ++ cbuf.relocate(cbuf.insts_mark(), runtime_call_Relocation::spec()); ++ ++ // call OptoRuntime::rethrow_stub to get the exception handler in parent method ++ __ patchable_jump((address)OptoRuntime::rethrow_stub()); ++ %} ++ ins_pipe( pipe_jump ); ++%} ++ ++// ============================================================================ ++// Branch Instructions --- long offset versions ++ ++// Jump Direct ++instruct jmpDir_long(label labl) %{ ++ match(Goto); ++ effect(USE labl); ++ ++ ins_cost(300); ++ format %{ "JMP $labl #@jmpDir_long" %} ++ ++ ins_encode %{ ++ Label* L = $labl$$label; ++ __ jmp_far(*L); ++ %} ++ ++ ins_pipe( pipe_jump ); ++ //ins_pc_relative(1); ++%} ++ ++// Jump Direct Conditional - Label defines a relative address from Jcc+1 ++instruct jmpLoopEnd_long(cmpOp cop, mRegI src1, mRegI src2, label labl) %{ ++ match(CountedLoopEnd cop (CmpI src1 src2)); ++ effect(USE labl); ++ ++ ins_cost(300); ++ format %{ "J$cop $src1, $src2, $labl\t# Loop end @ jmpLoopEnd_long" %} ++ ins_encode %{ ++ Register op1 = $src1$$Register; ++ Register op2 = $src2$$Register; ++ Label* L = $labl$$label; ++ int flag = $cop$$cmpcode; ++ ++ switch(flag) { ++ case 0x01: //equal ++ __ beq_long(op1, op2, *L); ++ break; ++ case 0x02: //not_equal ++ __ bne_long(op1, op2, *L); ++ break; ++ case 0x03: //above ++ __ slt(AT, op2, op1); ++ __ bne_long(AT, R0, *L); ++ break; ++ case 0x04: //above_equal ++ __ slt(AT, op1, op2); ++ __ beq_long(AT, R0, *L); ++ break; ++ case 0x05: //below ++ __ slt(AT, op1, op2); ++ __ bne_long(AT, R0, *L); ++ break; ++ case 0x06: //below_equal ++ __ slt(AT, op2, op1); ++ __ beq_long(AT, R0, *L); ++ break; ++ default: ++ Unimplemented(); ++ } ++ %} ++ ins_pipe( pipe_jump ); ++ ins_pc_relative(1); ++%} ++ ++instruct jmpLoopEnd_reg_immI_long(cmpOp cop, mRegI src1, immI src2, label labl) %{ ++ match(CountedLoopEnd cop (CmpI src1 src2)); ++ effect(USE labl); ++ ++ ins_cost(300); ++ format %{ "J$cop $src1, $src2, $labl\t# Loop end @ jmpLoopEnd_reg_immI_long" %} ++ ins_encode %{ ++ Register op1 = $src1$$Register; ++ Register op2 = AT; ++ Label* L = $labl$$label; ++ int flag = $cop$$cmpcode; ++ ++ __ move(op2, $src2$$constant); ++ ++ switch(flag) { ++ case 0x01: //equal ++ __ beq_long(op1, op2, *L); ++ break; ++ case 0x02: //not_equal ++ __ bne_long(op1, op2, *L); ++ break; ++ case 0x03: //above ++ __ slt(AT, op2, op1); ++ __ bne_long(AT, R0, *L); ++ break; ++ case 0x04: //above_equal ++ __ slt(AT, op1, op2); ++ __ beq_long(AT, R0, *L); ++ break; ++ case 0x05: //below ++ __ slt(AT, op1, op2); ++ __ bne_long(AT, R0, *L); ++ break; ++ case 0x06: //below_equal ++ __ slt(AT, op2, op1); ++ __ beq_long(AT, R0, *L); ++ break; ++ default: ++ Unimplemented(); ++ } ++ %} ++ ins_pipe( pipe_jump ); ++ ins_pc_relative(1); ++%} ++ ++ ++// This match pattern is created for StoreIConditional since I cannot match IfNode without a RegFlags! ++instruct jmpCon_flags_long(cmpOp cop, FlagsReg cr, label labl) %{ ++ match(If cop cr); ++ effect(USE labl); ++ ++ ins_cost(300); ++ format %{ "J$cop $labl #mips uses T0 as equivalent to eflag @jmpCon_flags_long" %} ++ ++ ins_encode %{ ++ Label* L = $labl$$label; ++ switch($cop$$cmpcode) { ++ case 0x01: //equal ++ __ bne_long($cr$$Register, R0, *L); ++ break; ++ case 0x02: //not equal ++ __ beq_long($cr$$Register, R0, *L); ++ break; ++ default: ++ Unimplemented(); ++ } ++ %} ++ ++ ins_pipe( pipe_jump ); ++ ins_pc_relative(1); ++%} ++ ++// Conditional jumps ++instruct branchConP_zero_long(cmpOpU cmp, mRegP op1, immP_0 zero, label labl) %{ ++ match(If cmp (CmpP op1 zero)); ++ effect(USE labl); ++ ++ ins_cost(180); ++ format %{ "b$cmp $op1, R0, $labl #@branchConP_zero_long" %} ++ ++ ins_encode %{ ++ Register op1 = $op1$$Register; ++ Register op2 = R0; ++ Label* L = $labl$$label; ++ int flag = $cmp$$cmpcode; ++ ++ switch(flag) { ++ case 0x01: //equal ++ __ beq_long(op1, op2, *L); ++ break; ++ case 0x02: //not_equal ++ __ bne_long(op1, op2, *L); ++ break; ++ default: ++ Unimplemented(); ++ } ++ %} ++ ++ ins_pc_relative(1); ++ ins_pipe( pipe_alu_branch ); ++%} ++ ++instruct branchConN2P_zero_long(cmpOpU cmp, mRegN op1, immP_0 zero, label labl) %{ ++ match(If cmp (CmpP (DecodeN op1) zero)); ++ predicate(Universe::narrow_oop_base() == NULL && Universe::narrow_oop_shift() == 0); ++ effect(USE labl); ++ ++ ins_cost(180); ++ format %{ "b$cmp $op1, R0, $labl #@branchConN2P_zero_long" %} ++ ++ ins_encode %{ ++ Register op1 = $op1$$Register; ++ Register op2 = R0; ++ Label* L = $labl$$label; ++ int flag = $cmp$$cmpcode; ++ ++ switch(flag) ++ { ++ case 0x01: //equal ++ __ beq_long(op1, op2, *L); ++ break; ++ case 0x02: //not_equal ++ __ bne_long(op1, op2, *L); ++ break; ++ default: ++ Unimplemented(); ++ } ++ %} ++ ++ ins_pc_relative(1); ++ ins_pipe( pipe_alu_branch ); ++%} ++ ++ ++instruct branchConP_long(cmpOpU cmp, mRegP op1, mRegP op2, label labl) %{ ++ match(If cmp (CmpP op1 op2)); ++// predicate(can_branch_register(_kids[0]->_leaf, _kids[1]->_leaf)); ++ effect(USE labl); ++ ++ ins_cost(200); ++ format %{ "b$cmp $op1, $op2, $labl #@branchConP_long" %} ++ ++ ins_encode %{ ++ Register op1 = $op1$$Register; ++ Register op2 = $op2$$Register; ++ Label* L = $labl$$label; ++ int flag = $cmp$$cmpcode; ++ ++ switch(flag) { ++ case 0x01: //equal ++ __ beq_long(op1, op2, *L); ++ break; ++ case 0x02: //not_equal ++ __ bne_long(op1, op2, *L); ++ break; ++ case 0x03: //above ++ __ sltu(AT, op2, op1); ++ __ bne_long(R0, AT, *L); ++ break; ++ case 0x04: //above_equal ++ __ sltu(AT, op1, op2); ++ __ beq_long(AT, R0, *L); ++ break; ++ case 0x05: //below ++ __ sltu(AT, op1, op2); ++ __ bne_long(R0, AT, *L); ++ break; ++ case 0x06: //below_equal ++ __ sltu(AT, op2, op1); ++ __ beq_long(AT, R0, *L); ++ break; ++ default: ++ Unimplemented(); ++ } ++ %} ++ ++ ins_pc_relative(1); ++ ins_pipe( pipe_alu_branch ); ++%} ++ ++instruct cmpN_null_branch_long(cmpOp cmp, mRegN op1, immN_0 null, label labl) %{ ++ match(If cmp (CmpN op1 null)); ++ effect(USE labl); ++ ++ ins_cost(180); ++ format %{ "CMP $op1,0\t! compressed ptr\n\t" ++ "BP$cmp $labl @ cmpN_null_branch_long" %} ++ ins_encode %{ ++ Register op1 = $op1$$Register; ++ Register op2 = R0; ++ Label* L = $labl$$label; ++ int flag = $cmp$$cmpcode; ++ ++ switch(flag) { ++ case 0x01: //equal ++ __ beq_long(op1, op2, *L); ++ break; ++ case 0x02: //not_equal ++ __ bne_long(op1, op2, *L); ++ break; ++ default: ++ Unimplemented(); ++ } ++ %} ++//TODO: pipe_branchP or create pipe_branchN LEE ++ ins_pc_relative(1); ++ ins_pipe( pipe_alu_branch ); ++%} ++ ++instruct cmpN_reg_branch_long(cmpOp cmp, mRegN op1, mRegN op2, label labl) %{ ++ match(If cmp (CmpN op1 op2)); ++ effect(USE labl); ++ ++ ins_cost(180); ++ format %{ "CMP $op1,$op2\t! compressed ptr\n\t" ++ "BP$cmp $labl @ cmpN_reg_branch_long" %} ++ ins_encode %{ ++ Register op1_reg = $op1$$Register; ++ Register op2_reg = $op2$$Register; ++ Label* L = $labl$$label; ++ int flag = $cmp$$cmpcode; ++ ++ switch(flag) { ++ case 0x01: //equal ++ __ beq_long(op1_reg, op2_reg, *L); ++ break; ++ case 0x02: //not_equal ++ __ bne_long(op1_reg, op2_reg, *L); ++ break; ++ case 0x03: //above ++ __ sltu(AT, op2_reg, op1_reg); ++ __ bne_long(R0, AT, *L); ++ break; ++ case 0x04: //above_equal ++ __ sltu(AT, op1_reg, op2_reg); ++ __ beq_long(AT, R0, *L); ++ break; ++ case 0x05: //below ++ __ sltu(AT, op1_reg, op2_reg); ++ __ bne_long(R0, AT, *L); ++ break; ++ case 0x06: //below_equal ++ __ sltu(AT, op2_reg, op1_reg); ++ __ beq_long(AT, R0, *L); ++ break; ++ default: ++ Unimplemented(); ++ } ++ %} ++ ins_pc_relative(1); ++ ins_pipe( pipe_alu_branch ); ++%} ++ ++instruct branchConIU_reg_reg_long(cmpOpU cmp, mRegI src1, mRegI src2, label labl) %{ ++ match( If cmp (CmpU src1 src2) ); ++ effect(USE labl); ++ format %{ "BR$cmp $src1, $src2, $labl #@branchConIU_reg_reg_long" %} ++ ++ ins_encode %{ ++ Register op1 = $src1$$Register; ++ Register op2 = $src2$$Register; ++ Label* L = $labl$$label; ++ int flag = $cmp$$cmpcode; ++ ++ switch(flag) { ++ case 0x01: //equal ++ __ beq_long(op1, op2, *L); ++ break; ++ case 0x02: //not_equal ++ __ bne_long(op1, op2, *L); ++ break; ++ case 0x03: //above ++ __ sltu(AT, op2, op1); ++ __ bne_long(AT, R0, *L); ++ break; ++ case 0x04: //above_equal ++ __ sltu(AT, op1, op2); ++ __ beq_long(AT, R0, *L); ++ break; ++ case 0x05: //below ++ __ sltu(AT, op1, op2); ++ __ bne_long(AT, R0, *L); ++ break; ++ case 0x06: //below_equal ++ __ sltu(AT, op2, op1); ++ __ beq_long(AT, R0, *L); ++ break; ++ default: ++ Unimplemented(); ++ } ++ %} ++ ++ ins_pc_relative(1); ++ ins_pipe( pipe_alu_branch ); ++%} ++ ++ ++instruct branchConIU_reg_imm_long(cmpOpU cmp, mRegI src1, immI src2, label labl) %{ ++ match( If cmp (CmpU src1 src2) ); ++ effect(USE labl); ++ format %{ "BR$cmp $src1, $src2, $labl #@branchConIU_reg_imm_long" %} ++ ++ ins_encode %{ ++ Register op1 = $src1$$Register; ++ int val = $src2$$constant; ++ Label* L = $labl$$label; ++ int flag = $cmp$$cmpcode; ++ ++ __ move(AT, val); ++ switch(flag) { ++ case 0x01: //equal ++ __ beq_long(op1, AT, *L); ++ break; ++ case 0x02: //not_equal ++ __ bne_long(op1, AT, *L); ++ break; ++ case 0x03: //above ++ __ sltu(AT, AT, op1); ++ __ bne_long(R0, AT, *L); ++ break; ++ case 0x04: //above_equal ++ __ sltu(AT, op1, AT); ++ __ beq_long(AT, R0, *L); ++ break; ++ case 0x05: //below ++ __ sltu(AT, op1, AT); ++ __ bne_long(R0, AT, *L); ++ break; ++ case 0x06: //below_equal ++ __ sltu(AT, AT, op1); ++ __ beq_long(AT, R0, *L); ++ break; ++ default: ++ Unimplemented(); ++ } ++ %} ++ ++ ins_pc_relative(1); ++ ins_pipe( pipe_alu_branch ); ++%} ++ ++instruct branchConI_reg_reg_long(cmpOp cmp, mRegI src1, mRegI src2, label labl) %{ ++ match( If cmp (CmpI src1 src2) ); ++ effect(USE labl); ++ format %{ "BR$cmp $src1, $src2, $labl #@branchConI_reg_reg_long" %} ++ ++ ins_encode %{ ++ Register op1 = $src1$$Register; ++ Register op2 = $src2$$Register; ++ Label* L = $labl$$label; ++ int flag = $cmp$$cmpcode; ++ ++ switch(flag) { ++ case 0x01: //equal ++ __ beq_long(op1, op2, *L); ++ break; ++ case 0x02: //not_equal ++ __ bne_long(op1, op2, *L); ++ break; ++ case 0x03: //above ++ __ slt(AT, op2, op1); ++ __ bne_long(R0, AT, *L); ++ break; ++ case 0x04: //above_equal ++ __ slt(AT, op1, op2); ++ __ beq_long(AT, R0, *L); ++ break; ++ case 0x05: //below ++ __ slt(AT, op1, op2); ++ __ bne_long(R0, AT, *L); ++ break; ++ case 0x06: //below_equal ++ __ slt(AT, op2, op1); ++ __ beq_long(AT, R0, *L); ++ break; ++ default: ++ Unimplemented(); ++ } ++ %} ++ ++ ins_pc_relative(1); ++ ins_pipe( pipe_alu_branch ); ++%} ++ ++instruct branchConI_reg_immI_0_long(cmpOp cmp, mRegI src1, immI_0 src2, label labl) %{ ++ match( If cmp (CmpI src1 src2) ); ++ effect(USE labl); ++ ins_cost(170); ++ format %{ "BR$cmp $src1, $src2, $labl #@branchConI_reg_immI_0_long" %} ++ ++ ins_encode %{ ++ Register op1 = $src1$$Register; ++ Label* L = $labl$$label; ++ int flag = $cmp$$cmpcode; ++ ++ switch(flag) { ++ case 0x01: //equal ++ __ beq_long(op1, R0, *L); ++ break; ++ case 0x02: //not_equal ++ __ bne_long(op1, R0, *L); ++ break; ++ case 0x03: //greater ++ __ slt(AT, R0, op1); ++ __ bne_long(R0, AT, *L); ++ break; ++ case 0x04: //greater_equal ++ __ slt(AT, op1, R0); ++ __ beq_long(AT, R0, *L); ++ break; ++ case 0x05: //less ++ __ slt(AT, op1, R0); ++ __ bne_long(R0, AT, *L); ++ break; ++ case 0x06: //less_equal ++ __ slt(AT, R0, op1); ++ __ beq_long(AT, R0, *L); ++ break; ++ default: ++ Unimplemented(); ++ } ++ %} ++ ++ ins_pc_relative(1); ++ ins_pipe( pipe_alu_branch ); ++%} ++ ++instruct branchConI_reg_imm_long(cmpOp cmp, mRegI src1, immI src2, label labl) %{ ++ match( If cmp (CmpI src1 src2) ); ++ effect(USE labl); ++ ins_cost(200); ++ format %{ "BR$cmp $src1, $src2, $labl #@branchConI_reg_imm_long" %} ++ ++ ins_encode %{ ++ Register op1 = $src1$$Register; ++ int val = $src2$$constant; ++ Label* L = $labl$$label; ++ int flag = $cmp$$cmpcode; ++ ++ __ move(AT, val); ++ switch(flag) { ++ case 0x01: //equal ++ __ beq_long(op1, AT, *L); ++ break; ++ case 0x02: //not_equal ++ __ bne_long(op1, AT, *L); ++ break; ++ case 0x03: //greater ++ __ slt(AT, AT, op1); ++ __ bne_long(R0, AT, *L); ++ break; ++ case 0x04: //greater_equal ++ __ slt(AT, op1, AT); ++ __ beq_long(AT, R0, *L); ++ break; ++ case 0x05: //less ++ __ slt(AT, op1, AT); ++ __ bne_long(R0, AT, *L); ++ break; ++ case 0x06: //less_equal ++ __ slt(AT, AT, op1); ++ __ beq_long(AT, R0, *L); ++ break; ++ default: ++ Unimplemented(); ++ } ++ %} ++ ++ ins_pc_relative(1); ++ ins_pipe( pipe_alu_branch ); ++%} ++ ++instruct branchConIU_reg_immI_0_long(cmpOpU cmp, mRegI src1, immI_0 zero, label labl) %{ ++ match( If cmp (CmpU src1 zero) ); ++ effect(USE labl); ++ format %{ "BR$cmp $src1, zero, $labl #@branchConIU_reg_immI_0_long" %} ++ ++ ins_encode %{ ++ Register op1 = $src1$$Register; ++ Label* L = $labl$$label; ++ int flag = $cmp$$cmpcode; ++ ++ switch(flag) { ++ case 0x01: //equal ++ __ beq_long(op1, R0, *L); ++ break; ++ case 0x02: //not_equal ++ __ bne_long(op1, R0, *L); ++ break; ++ case 0x03: //above ++ __ bne_long(R0, op1, *L); ++ break; ++ case 0x04: //above_equal ++ __ beq_long(R0, R0, *L); ++ break; ++ case 0x05: //below ++ return; ++ break; ++ case 0x06: //below_equal ++ __ beq_long(op1, R0, *L); ++ break; ++ default: ++ Unimplemented(); ++ } ++ %} ++ ++ ins_pc_relative(1); ++ ins_pipe( pipe_alu_branch ); ++%} ++ ++ ++instruct branchConIU_reg_immI16_long(cmpOpU cmp, mRegI src1, immI16 src2, label labl) %{ ++ match( If cmp (CmpU src1 src2) ); ++ effect(USE labl); ++ ins_cost(180); ++ format %{ "BR$cmp $src1, $src2, $labl #@branchConIU_reg_immI16_long" %} ++ ++ ins_encode %{ ++ Register op1 = $src1$$Register; ++ int val = $src2$$constant; ++ Label* L = $labl$$label; ++ int flag = $cmp$$cmpcode; ++ ++ switch(flag) { ++ case 0x01: //equal ++ __ move(AT, val); ++ __ beq_long(op1, AT, *L); ++ break; ++ case 0x02: //not_equal ++ __ move(AT, val); ++ __ bne_long(op1, AT, *L); ++ break; ++ case 0x03: //above ++ __ move(AT, val); ++ __ sltu(AT, AT, op1); ++ __ bne_long(R0, AT, *L); ++ break; ++ case 0x04: //above_equal ++ __ sltiu(AT, op1, val); ++ __ beq_long(AT, R0, *L); ++ break; ++ case 0x05: //below ++ __ sltiu(AT, op1, val); ++ __ bne_long(R0, AT, *L); ++ break; ++ case 0x06: //below_equal ++ __ move(AT, val); ++ __ sltu(AT, AT, op1); ++ __ beq_long(AT, R0, *L); ++ break; ++ default: ++ Unimplemented(); ++ } ++ %} ++ ++ ins_pc_relative(1); ++ ins_pipe( pipe_alu_branch ); ++%} ++ ++ ++instruct branchConL_regL_regL_long(cmpOp cmp, mRegL src1, mRegL src2, label labl) %{ ++ match( If cmp (CmpL src1 src2) ); ++ effect(USE labl); ++ format %{ "BR$cmp $src1, $src2, $labl #@branchConL_regL_regL_long" %} ++ ins_cost(250); ++ ++ ins_encode %{ ++ Register opr1_reg = as_Register($src1$$reg); ++ Register opr2_reg = as_Register($src2$$reg); ++ ++ Label* target = $labl$$label; ++ int flag = $cmp$$cmpcode; ++ ++ switch(flag) { ++ case 0x01: //equal ++ __ beq_long(opr1_reg, opr2_reg, *target); ++ break; ++ ++ case 0x02: //not_equal ++ __ bne_long(opr1_reg, opr2_reg, *target); ++ break; ++ ++ case 0x03: //greater ++ __ slt(AT, opr2_reg, opr1_reg); ++ __ bne_long(AT, R0, *target); ++ break; ++ ++ case 0x04: //greater_equal ++ __ slt(AT, opr1_reg, opr2_reg); ++ __ beq_long(AT, R0, *target); ++ break; ++ ++ case 0x05: //less ++ __ slt(AT, opr1_reg, opr2_reg); ++ __ bne_long(AT, R0, *target); ++ break; ++ ++ case 0x06: //less_equal ++ __ slt(AT, opr2_reg, opr1_reg); ++ __ beq_long(AT, R0, *target); ++ break; ++ ++ default: ++ Unimplemented(); ++ } ++ %} ++ ++ ++ ins_pc_relative(1); ++ ins_pipe( pipe_alu_branch ); ++%} ++ ++instruct branchConUL_regL_regL_long(cmpOp cmp, mRegL src1, mRegL src2, label labl) %{ ++ match(If cmp (CmpUL src1 src2)); ++ effect(USE labl); ++ format %{ "BR$cmp $src1, $src2, $labl #@branchConUL_regL_regL_long" %} ++ ins_cost(250); ++ ++ ins_encode %{ ++ Register opr1_reg = as_Register($src1$$reg); ++ Register opr2_reg = as_Register($src2$$reg); ++ Label* target = $labl$$label; ++ int flag = $cmp$$cmpcode; ++ ++ switch(flag) { ++ case 0x01: // equal ++ __ beq_long(opr1_reg, opr2_reg, *target); ++ break; ++ ++ case 0x02: // not_equal ++ __ bne_long(opr1_reg, opr2_reg, *target); ++ break; ++ ++ case 0x03: // greater ++ __ sltu(AT, opr2_reg, opr1_reg); ++ __ bne_long(AT, R0, *target); ++ break; ++ ++ case 0x04: // greater_equal ++ __ sltu(AT, opr1_reg, opr2_reg); ++ __ beq_long(AT, R0, *target); ++ break; ++ ++ case 0x05: // less ++ __ sltu(AT, opr1_reg, opr2_reg); ++ __ bne_long(AT, R0, *target); ++ break; ++ ++ case 0x06: // less_equal ++ __ sltu(AT, opr2_reg, opr1_reg); ++ __ beq_long(AT, R0, *target); ++ break; ++ ++ default: ++ Unimplemented(); ++ } ++ %} ++ ++ ins_pc_relative(1); ++ ins_pipe(pipe_alu_branch); ++%} ++ ++instruct branchConL_regL_immL_0_long(cmpOp cmp, mRegL src1, immL_0 zero, label labl) %{ ++ match( If cmp (CmpL src1 zero) ); ++ effect(USE labl); ++ format %{ "BR$cmp $src1, zero, $labl #@branchConL_regL_immL_0_long" %} ++ ins_cost(150); ++ ++ ins_encode %{ ++ Register opr1_reg = as_Register($src1$$reg); ++ Register opr2_reg = R0; ++ ++ Label* target = $labl$$label; ++ int flag = $cmp$$cmpcode; ++ ++ switch(flag) { ++ case 0x01: //equal ++ __ beq_long(opr1_reg, opr2_reg, *target); ++ break; ++ ++ case 0x02: //not_equal ++ __ bne_long(opr1_reg, opr2_reg, *target); ++ break; ++ ++ case 0x03: //greater ++ __ slt(AT, opr2_reg, opr1_reg); ++ __ bne_long(AT, R0, *target); ++ break; ++ ++ case 0x04: //greater_equal ++ __ slt(AT, opr1_reg, opr2_reg); ++ __ beq_long(AT, R0, *target); ++ break; ++ ++ case 0x05: //less ++ __ slt(AT, opr1_reg, opr2_reg); ++ __ bne_long(AT, R0, *target); ++ break; ++ ++ case 0x06: //less_equal ++ __ slt(AT, opr2_reg, opr1_reg); ++ __ beq_long(AT, R0, *target); ++ break; ++ ++ default: ++ Unimplemented(); ++ } ++ %} ++ ++ ++ ins_pc_relative(1); ++ ins_pipe( pipe_alu_branch ); ++%} ++ ++instruct branchConUL_regL_immL_0_long(cmpOp cmp, mRegL src1, immL_0 zero, label labl) %{ ++ match(If cmp (CmpUL src1 zero)); ++ effect(USE labl); ++ format %{ "BR$cmp $src1, zero, $labl #@branchConUL_regL_immL_0_long" %} ++ ins_cost(150); ++ ++ ins_encode %{ ++ Register opr1_reg = as_Register($src1$$reg); ++ Register opr2_reg = R0; ++ Label* target = $labl$$label; ++ int flag = $cmp$$cmpcode; ++ ++ switch(flag) { ++ case 0x01: // equal ++ case 0x04: // greater_equal ++ case 0x06: // less_equal ++ __ beq_long(opr1_reg, opr2_reg, *target); ++ break; ++ ++ case 0x02: // not_equal ++ case 0x03: // greater ++ __ bne_long(opr1_reg, opr2_reg, *target); ++ break; ++ ++ case 0x05: // less ++ __ beq_long(R0, R0, *target); ++ break; ++ ++ default: ++ Unimplemented(); ++ } ++ %} ++ ++ ins_pc_relative(1); ++ ins_pipe(pipe_alu_branch); ++%} ++ ++instruct branchConL_regL_immL_long(cmpOp cmp, mRegL src1, immL src2, label labl) %{ ++ match( If cmp (CmpL src1 src2) ); ++ effect(USE labl); ++ format %{ "BR$cmp $src1, $src2, $labl #@branchConL_regL_immL_long" %} ++ ins_cost(180); ++ ++ ins_encode %{ ++ Register opr1_reg = as_Register($src1$$reg); ++ Register opr2_reg = AT; ++ ++ Label* target = $labl$$label; ++ int flag = $cmp$$cmpcode; ++ ++ __ set64(opr2_reg, $src2$$constant); ++ ++ switch(flag) { ++ case 0x01: //equal ++ __ beq_long(opr1_reg, opr2_reg, *target); ++ break; ++ ++ case 0x02: //not_equal ++ __ bne_long(opr1_reg, opr2_reg, *target); ++ break; ++ ++ case 0x03: //greater ++ __ slt(AT, opr2_reg, opr1_reg); ++ __ bne_long(AT, R0, *target); ++ break; ++ ++ case 0x04: //greater_equal ++ __ slt(AT, opr1_reg, opr2_reg); ++ __ beq_long(AT, R0, *target); ++ break; ++ ++ case 0x05: //less ++ __ slt(AT, opr1_reg, opr2_reg); ++ __ bne_long(AT, R0, *target); ++ break; ++ ++ case 0x06: //less_equal ++ __ slt(AT, opr2_reg, opr1_reg); ++ __ beq_long(AT, R0, *target); ++ break; ++ ++ default: ++ Unimplemented(); ++ } ++ %} ++ ++ ++ ins_pc_relative(1); ++ ins_pipe( pipe_alu_branch ); ++%} ++ ++instruct branchConUL_regL_immL_long(cmpOp cmp, mRegL src1, immL src2, label labl) %{ ++ match(If cmp (CmpUL src1 src2)); ++ effect(USE labl); ++ format %{ "BR$cmp $src1, $src2, $labl #@branchConUL_regL_immL_long" %} ++ ins_cost(180); ++ ++ ins_encode %{ ++ Register opr1_reg = as_Register($src1$$reg); ++ Register opr2_reg = AT; ++ Label* target = $labl$$label; ++ int flag = $cmp$$cmpcode; ++ ++ __ set64(opr2_reg, $src2$$constant); ++ ++ switch(flag) { ++ case 0x01: // equal ++ __ beq_long(opr1_reg, opr2_reg, *target); ++ break; ++ ++ case 0x02: // not_equal ++ __ bne_long(opr1_reg, opr2_reg, *target); ++ break; ++ ++ case 0x03: // greater ++ __ sltu(AT, opr2_reg, opr1_reg); ++ __ bne_long(AT, R0, *target); ++ break; ++ ++ case 0x04: // greater_equal ++ __ sltu(AT, opr1_reg, opr2_reg); ++ __ beq_long(AT, R0, *target); ++ break; ++ ++ case 0x05: // less ++ __ sltu(AT, opr1_reg, opr2_reg); ++ __ bne_long(AT, R0, *target); ++ break; ++ ++ case 0x06: // less_equal ++ __ sltu(AT, opr2_reg, opr1_reg); ++ __ beq_long(AT, R0, *target); ++ break; ++ ++ default: ++ Unimplemented(); ++ } ++ %} ++ ++ ins_pc_relative(1); ++ ins_pipe(pipe_alu_branch); ++%} ++ ++//FIXME ++instruct branchConF_reg_reg_long(cmpOp cmp, regF src1, regF src2, label labl) %{ ++ match( If cmp (CmpF src1 src2) ); ++ effect(USE labl); ++ format %{ "BR$cmp $src1, $src2, $labl #@branchConF_reg_reg_long" %} ++ ++ ins_encode %{ ++ FloatRegister reg_op1 = $src1$$FloatRegister; ++ FloatRegister reg_op2 = $src2$$FloatRegister; ++ Label* L = $labl$$label; ++ int flag = $cmp$$cmpcode; ++ ++ switch(flag) { ++ case 0x01: // equal ++ __ c_eq_s(reg_op1, reg_op2); ++ __ bc1t_long(*L); ++ break; ++ case 0x02: // not_equal ++ __ c_eq_s(reg_op1, reg_op2); ++ __ bc1f_long(*L); ++ break; ++ case 0x03: // greater ++ __ c_ule_s(reg_op1, reg_op2); ++ __ bc1f_long(*L); ++ break; ++ case 0x04: // greater_equal ++ __ c_ult_s(reg_op1, reg_op2); ++ __ bc1f_long(*L); ++ break; ++ case 0x05: // less ++ __ c_ult_s(reg_op1, reg_op2); ++ __ bc1t_long(*L); ++ break; ++ case 0x06: // less_equal ++ __ c_ule_s(reg_op1, reg_op2); ++ __ bc1t_long(*L); ++ break; ++ default: ++ Unimplemented(); ++ } ++ %} ++ ++ ins_pc_relative(1); ++ ins_pipe(pipe_slow); ++%} ++ ++instruct branchConD_reg_reg_long(cmpOp cmp, regD src1, regD src2, label labl) %{ ++ match( If cmp (CmpD src1 src2) ); ++ effect(USE labl); ++ format %{ "BR$cmp $src1, $src2, $labl #@branchConD_reg_reg_long" %} ++ ++ ins_encode %{ ++ FloatRegister reg_op1 = $src1$$FloatRegister; ++ FloatRegister reg_op2 = $src2$$FloatRegister; ++ Label* L = $labl$$label; ++ int flag = $cmp$$cmpcode; ++ ++ switch(flag) { ++ case 0x01: // equal ++ __ c_eq_d(reg_op1, reg_op2); ++ __ bc1t_long(*L); ++ break; ++ case 0x02: // not_equal ++ // c_ueq_d cannot distinguish NaN from equal. Double.isNaN(Double) is implemented by 'f != f', so the use of c_ueq_d causes bugs. ++ __ c_eq_d(reg_op1, reg_op2); ++ __ bc1f_long(*L); ++ break; ++ case 0x03: // greater ++ __ c_ule_d(reg_op1, reg_op2); ++ __ bc1f_long(*L); ++ break; ++ case 0x04: // greater_equal ++ __ c_ult_d(reg_op1, reg_op2); ++ __ bc1f_long(*L); ++ break; ++ case 0x05: // less ++ __ c_ult_d(reg_op1, reg_op2); ++ __ bc1t_long(*L); ++ break; ++ case 0x06: // less_equal ++ __ c_ule_d(reg_op1, reg_op2); ++ __ bc1t_long(*L); ++ break; ++ default: ++ Unimplemented(); ++ } ++ %} ++ ++ ins_pc_relative(1); ++ ins_pipe(pipe_slow); ++%} ++ ++ ++// ============================================================================ ++// Branch Instructions -- short offset versions ++ ++// Jump Direct ++instruct jmpDir_short(label labl) %{ ++ match(Goto); ++ effect(USE labl); ++ ++ ins_cost(300); ++ format %{ "JMP $labl #@jmpDir_short" %} ++ ++ ins_encode %{ ++ Label &L = *($labl$$label); ++ if(&L) ++ __ b(L); ++ else ++ __ b(int(0)); ++ __ delayed()->nop(); ++ %} ++ ++ ins_pipe( pipe_jump ); ++ ins_pc_relative(1); ++ ins_short_branch(1); ++%} ++ ++// Jump Direct Conditional - Label defines a relative address from Jcc+1 ++instruct jmpLoopEnd_short(cmpOp cop, mRegI src1, mRegI src2, label labl) %{ ++ match(CountedLoopEnd cop (CmpI src1 src2)); ++ effect(USE labl); ++ ++ ins_cost(300); ++ format %{ "J$cop $src1, $src2, $labl\t# Loop end @ jmpLoopEnd_short" %} ++ ins_encode %{ ++ Register op1 = $src1$$Register; ++ Register op2 = $src2$$Register; ++ Label &L = *($labl$$label); ++ int flag = $cop$$cmpcode; ++ ++ switch(flag) { ++ case 0x01: //equal ++ if (&L) ++ __ beq(op1, op2, L); ++ else ++ __ beq(op1, op2, (int)0); ++ break; ++ case 0x02: //not_equal ++ if (&L) ++ __ bne(op1, op2, L); ++ else ++ __ bne(op1, op2, (int)0); ++ break; ++ case 0x03: //above ++ __ slt(AT, op2, op1); ++ if(&L) ++ __ bne(AT, R0, L); ++ else ++ __ bne(AT, R0, (int)0); ++ break; ++ case 0x04: //above_equal ++ __ slt(AT, op1, op2); ++ if(&L) ++ __ beq(AT, R0, L); ++ else ++ __ beq(AT, R0, (int)0); ++ break; ++ case 0x05: //below ++ __ slt(AT, op1, op2); ++ if(&L) ++ __ bne(AT, R0, L); ++ else ++ __ bne(AT, R0, (int)0); ++ break; ++ case 0x06: //below_equal ++ __ slt(AT, op2, op1); ++ if(&L) ++ __ beq(AT, R0, L); ++ else ++ __ beq(AT, R0, (int)0); ++ break; ++ default: ++ Unimplemented(); ++ } ++ __ delayed()->nop(); ++ %} ++ ins_pipe( pipe_jump ); ++ ins_pc_relative(1); ++ ins_short_branch(1); ++%} ++ ++instruct jmpLoopEnd_reg_immI_short(cmpOp cop, mRegI src1, immI src2, label labl) %{ ++ match(CountedLoopEnd cop (CmpI src1 src2)); ++ effect(USE labl); ++ ++ ins_cost(300); ++ format %{ "J$cop $src1, $src2, $labl\t# Loop end @ jmpLoopEnd_reg_immI_short" %} ++ ins_encode %{ ++ Register op1 = $src1$$Register; ++ Register op2 = AT; ++ Label &L = *($labl$$label); ++ int flag = $cop$$cmpcode; ++ ++ __ move(op2, $src2$$constant); ++ ++ switch(flag) { ++ case 0x01: //equal ++ if (&L) ++ __ beq(op1, op2, L); ++ else ++ __ beq(op1, op2, (int)0); ++ break; ++ case 0x02: //not_equal ++ if (&L) ++ __ bne(op1, op2, L); ++ else ++ __ bne(op1, op2, (int)0); ++ break; ++ case 0x03: //above ++ __ slt(AT, op2, op1); ++ if(&L) ++ __ bne(AT, R0, L); ++ else ++ __ bne(AT, R0, (int)0); ++ break; ++ case 0x04: //above_equal ++ __ slt(AT, op1, op2); ++ if(&L) ++ __ beq(AT, R0, L); ++ else ++ __ beq(AT, R0, (int)0); ++ break; ++ case 0x05: //below ++ __ slt(AT, op1, op2); ++ if(&L) ++ __ bne(AT, R0, L); ++ else ++ __ bne(AT, R0, (int)0); ++ break; ++ case 0x06: //below_equal ++ __ slt(AT, op2, op1); ++ if(&L) ++ __ beq(AT, R0, L); ++ else ++ __ beq(AT, R0, (int)0); ++ break; ++ default: ++ Unimplemented(); ++ } ++ __ delayed()->nop(); ++ %} ++ ins_pipe( pipe_jump ); ++ ins_pc_relative(1); ++ ins_short_branch(1); ++%} ++ ++ ++// This match pattern is created for StoreIConditional since I cannot match IfNode without a RegFlags! ++instruct jmpCon_flags_short(cmpOp cop, FlagsReg cr, label labl) %{ ++ match(If cop cr); ++ effect(USE labl); ++ ++ ins_cost(300); ++ format %{ "J$cop $labl #mips uses T0 as equivalent to eflag @jmpCon_flags_short" %} ++ ++ ins_encode %{ ++ Label &L = *($labl$$label); ++ switch($cop$$cmpcode) { ++ case 0x01: //equal ++ if (&L) ++ __ bne($cr$$Register, R0, L); ++ else ++ __ bne($cr$$Register, R0, (int)0); ++ break; ++ case 0x02: //not equal ++ if (&L) ++ __ beq($cr$$Register, R0, L); ++ else ++ __ beq($cr$$Register, R0, (int)0); ++ break; ++ default: ++ Unimplemented(); ++ } ++ __ delayed()->nop(); ++ %} ++ ++ ins_pipe( pipe_jump ); ++ ins_pc_relative(1); ++ ins_short_branch(1); ++%} ++ ++// Conditional jumps ++instruct branchConP_zero_short(cmpOpU cmp, mRegP op1, immP_0 zero, label labl) %{ ++ match(If cmp (CmpP op1 zero)); ++ effect(USE labl); ++ ++ ins_cost(180); ++ format %{ "b$cmp $op1, R0, $labl #@branchConP_zero_short" %} ++ ++ ins_encode %{ ++ Register op1 = $op1$$Register; ++ Register op2 = R0; ++ Label &L = *($labl$$label); ++ int flag = $cmp$$cmpcode; ++ ++ switch(flag) { ++ case 0x01: //equal ++ if (&L) ++ __ beq(op1, op2, L); ++ else ++ __ beq(op1, op2, (int)0); ++ break; ++ case 0x02: //not_equal ++ if (&L) ++ __ bne(op1, op2, L); ++ else ++ __ bne(op1, op2, (int)0); ++ break; ++ default: ++ Unimplemented(); ++ } ++ __ delayed()->nop(); ++ %} ++ ++ ins_pc_relative(1); ++ ins_pipe( pipe_alu_branch ); ++ ins_short_branch(1); ++%} ++ ++instruct branchConN2P_zero_short(cmpOpU cmp, mRegN op1, immP_0 zero, label labl) %{ ++ match(If cmp (CmpP (DecodeN op1) zero)); ++ predicate(Universe::narrow_oop_base() == NULL && Universe::narrow_oop_shift() == 0); ++ effect(USE labl); ++ ++ ins_cost(180); ++ format %{ "b$cmp $op1, R0, $labl #@branchConN2P_zero_short" %} ++ ++ ins_encode %{ ++ Register op1 = $op1$$Register; ++ Register op2 = R0; ++ Label &L = *($labl$$label); ++ int flag = $cmp$$cmpcode; ++ ++ switch(flag) ++ { ++ case 0x01: //equal ++ if (&L) ++ __ beq(op1, op2, L); ++ else ++ __ beq(op1, op2, (int)0); ++ break; ++ case 0x02: //not_equal ++ if (&L) ++ __ bne(op1, op2, L); ++ else ++ __ bne(op1, op2, (int)0); ++ break; ++ default: ++ Unimplemented(); ++ } ++ __ delayed()->nop(); ++ %} ++ ++ ins_pc_relative(1); ++ ins_pipe( pipe_alu_branch ); ++ ins_short_branch(1); ++%} ++ ++ ++instruct branchConP_short(cmpOpU cmp, mRegP op1, mRegP op2, label labl) %{ ++ match(If cmp (CmpP op1 op2)); ++// predicate(can_branch_register(_kids[0]->_leaf, _kids[1]->_leaf)); ++ effect(USE labl); ++ ++ ins_cost(200); ++ format %{ "b$cmp $op1, $op2, $labl #@branchConP_short" %} ++ ++ ins_encode %{ ++ Register op1 = $op1$$Register; ++ Register op2 = $op2$$Register; ++ Label &L = *($labl$$label); ++ int flag = $cmp$$cmpcode; ++ ++ switch(flag) { ++ case 0x01: //equal ++ if (&L) ++ __ beq(op1, op2, L); ++ else ++ __ beq(op1, op2, (int)0); ++ break; ++ case 0x02: //not_equal ++ if (&L) ++ __ bne(op1, op2, L); ++ else ++ __ bne(op1, op2, (int)0); ++ break; ++ case 0x03: //above ++ __ sltu(AT, op2, op1); ++ if(&L) ++ __ bne(R0, AT, L); ++ else ++ __ bne(R0, AT, (int)0); ++ break; ++ case 0x04: //above_equal ++ __ sltu(AT, op1, op2); ++ if(&L) ++ __ beq(AT, R0, L); ++ else ++ __ beq(AT, R0, (int)0); ++ break; ++ case 0x05: //below ++ __ sltu(AT, op1, op2); ++ if(&L) ++ __ bne(R0, AT, L); ++ else ++ __ bne(R0, AT, (int)0); ++ break; ++ case 0x06: //below_equal ++ __ sltu(AT, op2, op1); ++ if(&L) ++ __ beq(AT, R0, L); ++ else ++ __ beq(AT, R0, (int)0); ++ break; ++ default: ++ Unimplemented(); ++ } ++ __ delayed()->nop(); ++ %} ++ ++ ins_pc_relative(1); ++ ins_pipe( pipe_alu_branch ); ++ ins_short_branch(1); ++%} ++ ++instruct cmpN_null_branch_short(cmpOp cmp, mRegN op1, immN_0 null, label labl) %{ ++ match(If cmp (CmpN op1 null)); ++ effect(USE labl); ++ ++ ins_cost(180); ++ format %{ "CMP $op1,0\t! compressed ptr\n\t" ++ "BP$cmp $labl @ cmpN_null_branch_short" %} ++ ins_encode %{ ++ Register op1 = $op1$$Register; ++ Register op2 = R0; ++ Label &L = *($labl$$label); ++ int flag = $cmp$$cmpcode; ++ ++ switch(flag) { ++ case 0x01: //equal ++ if (&L) ++ __ beq(op1, op2, L); ++ else ++ __ beq(op1, op2, (int)0); ++ break; ++ case 0x02: //not_equal ++ if (&L) ++ __ bne(op1, op2, L); ++ else ++ __ bne(op1, op2, (int)0); ++ break; ++ default: ++ Unimplemented(); ++ } ++ __ delayed()->nop(); ++ %} ++//TODO: pipe_branchP or create pipe_branchN LEE ++ ins_pc_relative(1); ++ ins_pipe( pipe_alu_branch ); ++ ins_short_branch(1); ++%} ++ ++instruct cmpN_reg_branch_short(cmpOp cmp, mRegN op1, mRegN op2, label labl) %{ ++ match(If cmp (CmpN op1 op2)); ++ effect(USE labl); ++ ++ ins_cost(180); ++ format %{ "CMP $op1,$op2\t! compressed ptr\n\t" ++ "BP$cmp $labl @ cmpN_reg_branch_short" %} ++ ins_encode %{ ++ Register op1_reg = $op1$$Register; ++ Register op2_reg = $op2$$Register; ++ Label &L = *($labl$$label); ++ int flag = $cmp$$cmpcode; ++ ++ switch(flag) { ++ case 0x01: //equal ++ if (&L) ++ __ beq(op1_reg, op2_reg, L); ++ else ++ __ beq(op1_reg, op2_reg, (int)0); ++ break; ++ case 0x02: //not_equal ++ if (&L) ++ __ bne(op1_reg, op2_reg, L); ++ else ++ __ bne(op1_reg, op2_reg, (int)0); ++ break; ++ case 0x03: //above ++ __ sltu(AT, op2_reg, op1_reg); ++ if(&L) ++ __ bne(R0, AT, L); ++ else ++ __ bne(R0, AT, (int)0); ++ break; ++ case 0x04: //above_equal ++ __ sltu(AT, op1_reg, op2_reg); ++ if(&L) ++ __ beq(AT, R0, L); ++ else ++ __ beq(AT, R0, (int)0); ++ break; ++ case 0x05: //below ++ __ sltu(AT, op1_reg, op2_reg); ++ if(&L) ++ __ bne(R0, AT, L); ++ else ++ __ bne(R0, AT, (int)0); ++ break; ++ case 0x06: //below_equal ++ __ sltu(AT, op2_reg, op1_reg); ++ if(&L) ++ __ beq(AT, R0, L); ++ else ++ __ beq(AT, R0, (int)0); ++ break; ++ default: ++ Unimplemented(); ++ } ++ __ delayed()->nop(); ++ %} ++ ins_pc_relative(1); ++ ins_pipe( pipe_alu_branch ); ++ ins_short_branch(1); ++%} ++ ++instruct branchConIU_reg_reg_short(cmpOpU cmp, mRegI src1, mRegI src2, label labl) %{ ++ match( If cmp (CmpU src1 src2) ); ++ effect(USE labl); ++ format %{ "BR$cmp $src1, $src2, $labl #@branchConIU_reg_reg_short" %} ++ ++ ins_encode %{ ++ Register op1 = $src1$$Register; ++ Register op2 = $src2$$Register; ++ Label &L = *($labl$$label); ++ int flag = $cmp$$cmpcode; ++ ++ switch(flag) { ++ case 0x01: //equal ++ if (&L) ++ __ beq(op1, op2, L); ++ else ++ __ beq(op1, op2, (int)0); ++ break; ++ case 0x02: //not_equal ++ if (&L) ++ __ bne(op1, op2, L); ++ else ++ __ bne(op1, op2, (int)0); ++ break; ++ case 0x03: //above ++ __ sltu(AT, op2, op1); ++ if(&L) ++ __ bne(AT, R0, L); ++ else ++ __ bne(AT, R0, (int)0); ++ break; ++ case 0x04: //above_equal ++ __ sltu(AT, op1, op2); ++ if(&L) ++ __ beq(AT, R0, L); ++ else ++ __ beq(AT, R0, (int)0); ++ break; ++ case 0x05: //below ++ __ sltu(AT, op1, op2); ++ if(&L) ++ __ bne(AT, R0, L); ++ else ++ __ bne(AT, R0, (int)0); ++ break; ++ case 0x06: //below_equal ++ __ sltu(AT, op2, op1); ++ if(&L) ++ __ beq(AT, R0, L); ++ else ++ __ beq(AT, R0, (int)0); ++ break; ++ default: ++ Unimplemented(); ++ } ++ __ delayed()->nop(); ++ %} ++ ++ ins_pc_relative(1); ++ ins_pipe( pipe_alu_branch ); ++ ins_short_branch(1); ++%} ++ ++ ++instruct branchConIU_reg_imm_short(cmpOpU cmp, mRegI src1, immI src2, label labl) %{ ++ match( If cmp (CmpU src1 src2) ); ++ effect(USE labl); ++ format %{ "BR$cmp $src1, $src2, $labl #@branchConIU_reg_imm_short" %} ++ ++ ins_encode %{ ++ Register op1 = $src1$$Register; ++ int val = $src2$$constant; ++ Label &L = *($labl$$label); ++ int flag = $cmp$$cmpcode; ++ ++ __ move(AT, val); ++ switch(flag) { ++ case 0x01: //equal ++ if (&L) ++ __ beq(op1, AT, L); ++ else ++ __ beq(op1, AT, (int)0); ++ break; ++ case 0x02: //not_equal ++ if (&L) ++ __ bne(op1, AT, L); ++ else ++ __ bne(op1, AT, (int)0); ++ break; ++ case 0x03: //above ++ __ sltu(AT, AT, op1); ++ if(&L) ++ __ bne(R0, AT, L); ++ else ++ __ bne(R0, AT, (int)0); ++ break; ++ case 0x04: //above_equal ++ __ sltu(AT, op1, AT); ++ if(&L) ++ __ beq(AT, R0, L); ++ else ++ __ beq(AT, R0, (int)0); ++ break; ++ case 0x05: //below ++ __ sltu(AT, op1, AT); ++ if(&L) ++ __ bne(R0, AT, L); ++ else ++ __ bne(R0, AT, (int)0); ++ break; ++ case 0x06: //below_equal ++ __ sltu(AT, AT, op1); ++ if(&L) ++ __ beq(AT, R0, L); ++ else ++ __ beq(AT, R0, (int)0); ++ break; ++ default: ++ Unimplemented(); ++ } ++ __ delayed()->nop(); ++ %} ++ ++ ins_pc_relative(1); ++ ins_pipe( pipe_alu_branch ); ++ ins_short_branch(1); ++%} ++ ++instruct branchConI_reg_reg_short(cmpOp cmp, mRegI src1, mRegI src2, label labl) %{ ++ match( If cmp (CmpI src1 src2) ); ++ effect(USE labl); ++ format %{ "BR$cmp $src1, $src2, $labl #@branchConI_reg_reg_short" %} ++ ++ ins_encode %{ ++ Register op1 = $src1$$Register; ++ Register op2 = $src2$$Register; ++ Label &L = *($labl$$label); ++ int flag = $cmp$$cmpcode; ++ ++ switch(flag) { ++ case 0x01: //equal ++ if (&L) ++ __ beq(op1, op2, L); ++ else ++ __ beq(op1, op2, (int)0); ++ break; ++ case 0x02: //not_equal ++ if (&L) ++ __ bne(op1, op2, L); ++ else ++ __ bne(op1, op2, (int)0); ++ break; ++ case 0x03: //above ++ __ slt(AT, op2, op1); ++ if(&L) ++ __ bne(R0, AT, L); ++ else ++ __ bne(R0, AT, (int)0); ++ break; ++ case 0x04: //above_equal ++ __ slt(AT, op1, op2); ++ if(&L) ++ __ beq(AT, R0, L); ++ else ++ __ beq(AT, R0, (int)0); ++ break; ++ case 0x05: //below ++ __ slt(AT, op1, op2); ++ if(&L) ++ __ bne(R0, AT, L); ++ else ++ __ bne(R0, AT, (int)0); ++ break; ++ case 0x06: //below_equal ++ __ slt(AT, op2, op1); ++ if(&L) ++ __ beq(AT, R0, L); ++ else ++ __ beq(AT, R0, (int)0); ++ break; ++ default: ++ Unimplemented(); ++ } ++ __ delayed()->nop(); ++ %} ++ ++ ins_pc_relative(1); ++ ins_pipe( pipe_alu_branch ); ++ ins_short_branch(1); ++%} ++ ++instruct branchConI_reg_immI_0_short(cmpOp cmp, mRegI src1, immI_0 src2, label labl) %{ ++ match( If cmp (CmpI src1 src2) ); ++ effect(USE labl); ++ ins_cost(170); ++ format %{ "BR$cmp $src1, $src2, $labl #@branchConI_reg_immI_0_short" %} ++ ++ ins_encode %{ ++ Register op1 = $src1$$Register; ++ Label &L = *($labl$$label); ++ int flag = $cmp$$cmpcode; ++ ++ switch(flag) { ++ case 0x01: //equal ++ if (&L) ++ __ beq(op1, R0, L); ++ else ++ __ beq(op1, R0, (int)0); ++ break; ++ case 0x02: //not_equal ++ if (&L) ++ __ bne(op1, R0, L); ++ else ++ __ bne(op1, R0, (int)0); ++ break; ++ case 0x03: //greater ++ if(&L) ++ __ bgtz(op1, L); ++ else ++ __ bgtz(op1, (int)0); ++ break; ++ case 0x04: //greater_equal ++ if(&L) ++ __ bgez(op1, L); ++ else ++ __ bgez(op1, (int)0); ++ break; ++ case 0x05: //less ++ if(&L) ++ __ bltz(op1, L); ++ else ++ __ bltz(op1, (int)0); ++ break; ++ case 0x06: //less_equal ++ if(&L) ++ __ blez(op1, L); ++ else ++ __ blez(op1, (int)0); ++ break; ++ default: ++ Unimplemented(); ++ } ++ __ delayed()->nop(); ++ %} ++ ++ ins_pc_relative(1); ++ ins_pipe( pipe_alu_branch ); ++ ins_short_branch(1); ++%} ++ ++ ++instruct branchConI_reg_imm_short(cmpOp cmp, mRegI src1, immI src2, label labl) %{ ++ match( If cmp (CmpI src1 src2) ); ++ effect(USE labl); ++ ins_cost(200); ++ format %{ "BR$cmp $src1, $src2, $labl #@branchConI_reg_imm_short" %} ++ ++ ins_encode %{ ++ Register op1 = $src1$$Register; ++ int val = $src2$$constant; ++ Label &L = *($labl$$label); ++ int flag = $cmp$$cmpcode; ++ ++ __ move(AT, val); ++ switch(flag) { ++ case 0x01: //equal ++ if (&L) ++ __ beq(op1, AT, L); ++ else ++ __ beq(op1, AT, (int)0); ++ break; ++ case 0x02: //not_equal ++ if (&L) ++ __ bne(op1, AT, L); ++ else ++ __ bne(op1, AT, (int)0); ++ break; ++ case 0x03: //greater ++ __ slt(AT, AT, op1); ++ if(&L) ++ __ bne(R0, AT, L); ++ else ++ __ bne(R0, AT, (int)0); ++ break; ++ case 0x04: //greater_equal ++ __ slt(AT, op1, AT); ++ if(&L) ++ __ beq(AT, R0, L); ++ else ++ __ beq(AT, R0, (int)0); ++ break; ++ case 0x05: //less ++ __ slt(AT, op1, AT); ++ if(&L) ++ __ bne(R0, AT, L); ++ else ++ __ bne(R0, AT, (int)0); ++ break; ++ case 0x06: //less_equal ++ __ slt(AT, AT, op1); ++ if(&L) ++ __ beq(AT, R0, L); ++ else ++ __ beq(AT, R0, (int)0); ++ break; ++ default: ++ Unimplemented(); ++ } ++ __ delayed()->nop(); ++ %} ++ ++ ins_pc_relative(1); ++ ins_pipe( pipe_alu_branch ); ++ ins_short_branch(1); ++%} ++ ++instruct branchConIU_reg_immI_0_short(cmpOpU cmp, mRegI src1, immI_0 zero, label labl) %{ ++ match( If cmp (CmpU src1 zero) ); ++ effect(USE labl); ++ format %{ "BR$cmp $src1, zero, $labl #@branchConIU_reg_immI_0_short" %} ++ ++ ins_encode %{ ++ Register op1 = $src1$$Register; ++ Label &L = *($labl$$label); ++ int flag = $cmp$$cmpcode; ++ ++ switch(flag) { ++ case 0x01: //equal ++ if (&L) ++ __ beq(op1, R0, L); ++ else ++ __ beq(op1, R0, (int)0); ++ break; ++ case 0x02: //not_equal ++ if (&L) ++ __ bne(op1, R0, L); ++ else ++ __ bne(op1, R0, (int)0); ++ break; ++ case 0x03: //above ++ if(&L) ++ __ bne(R0, op1, L); ++ else ++ __ bne(R0, op1, (int)0); ++ break; ++ case 0x04: //above_equal ++ if(&L) ++ __ beq(R0, R0, L); ++ else ++ __ beq(R0, R0, (int)0); ++ break; ++ case 0x05: //below ++ return; ++ break; ++ case 0x06: //below_equal ++ if(&L) ++ __ beq(op1, R0, L); ++ else ++ __ beq(op1, R0, (int)0); ++ break; ++ default: ++ Unimplemented(); ++ } ++ __ delayed()->nop(); ++ %} ++ ++ ins_pc_relative(1); ++ ins_pipe( pipe_alu_branch ); ++ ins_short_branch(1); ++%} ++ ++ ++instruct branchConIU_reg_immI16_short(cmpOpU cmp, mRegI src1, immI16 src2, label labl) %{ ++ match( If cmp (CmpU src1 src2) ); ++ effect(USE labl); ++ ins_cost(180); ++ format %{ "BR$cmp $src1, $src2, $labl #@branchConIU_reg_immI16_short" %} ++ ++ ins_encode %{ ++ Register op1 = $src1$$Register; ++ int val = $src2$$constant; ++ Label &L = *($labl$$label); ++ int flag = $cmp$$cmpcode; ++ ++ switch(flag) { ++ case 0x01: //equal ++ __ move(AT, val); ++ if (&L) ++ __ beq(op1, AT, L); ++ else ++ __ beq(op1, AT, (int)0); ++ break; ++ case 0x02: //not_equal ++ __ move(AT, val); ++ if (&L) ++ __ bne(op1, AT, L); ++ else ++ __ bne(op1, AT, (int)0); ++ break; ++ case 0x03: //above ++ __ move(AT, val); ++ __ sltu(AT, AT, op1); ++ if(&L) ++ __ bne(R0, AT, L); ++ else ++ __ bne(R0, AT, (int)0); ++ break; ++ case 0x04: //above_equal ++ __ sltiu(AT, op1, val); ++ if(&L) ++ __ beq(AT, R0, L); ++ else ++ __ beq(AT, R0, (int)0); ++ break; ++ case 0x05: //below ++ __ sltiu(AT, op1, val); ++ if(&L) ++ __ bne(R0, AT, L); ++ else ++ __ bne(R0, AT, (int)0); ++ break; ++ case 0x06: //below_equal ++ __ move(AT, val); ++ __ sltu(AT, AT, op1); ++ if(&L) ++ __ beq(AT, R0, L); ++ else ++ __ beq(AT, R0, (int)0); ++ break; ++ default: ++ Unimplemented(); ++ } ++ __ delayed()->nop(); ++ %} ++ ++ ins_pc_relative(1); ++ ins_pipe( pipe_alu_branch ); ++ ins_short_branch(1); ++%} ++ ++ ++instruct branchConL_regL_regL_short(cmpOp cmp, mRegL src1, mRegL src2, label labl) %{ ++ match( If cmp (CmpL src1 src2) ); ++ effect(USE labl); ++ format %{ "BR$cmp $src1, $src2, $labl #@branchConL_regL_regL_short" %} ++ ins_cost(250); ++ ++ ins_encode %{ ++ Register opr1_reg = as_Register($src1$$reg); ++ Register opr2_reg = as_Register($src2$$reg); ++ ++ Label &target = *($labl$$label); ++ int flag = $cmp$$cmpcode; ++ ++ switch(flag) { ++ case 0x01: //equal ++ if (&target) ++ __ beq(opr1_reg, opr2_reg, target); ++ else ++ __ beq(opr1_reg, opr2_reg, (int)0); ++ __ delayed()->nop(); ++ break; ++ ++ case 0x02: //not_equal ++ if(&target) ++ __ bne(opr1_reg, opr2_reg, target); ++ else ++ __ bne(opr1_reg, opr2_reg, (int)0); ++ __ delayed()->nop(); ++ break; ++ ++ case 0x03: //greater ++ __ slt(AT, opr2_reg, opr1_reg); ++ if(&target) ++ __ bne(AT, R0, target); ++ else ++ __ bne(AT, R0, (int)0); ++ __ delayed()->nop(); ++ break; ++ ++ case 0x04: //greater_equal ++ __ slt(AT, opr1_reg, opr2_reg); ++ if(&target) ++ __ beq(AT, R0, target); ++ else ++ __ beq(AT, R0, (int)0); ++ __ delayed()->nop(); ++ ++ break; ++ ++ case 0x05: //less ++ __ slt(AT, opr1_reg, opr2_reg); ++ if(&target) ++ __ bne(AT, R0, target); ++ else ++ __ bne(AT, R0, (int)0); ++ __ delayed()->nop(); ++ ++ break; ++ ++ case 0x06: //less_equal ++ __ slt(AT, opr2_reg, opr1_reg); ++ ++ if(&target) ++ __ beq(AT, R0, target); ++ else ++ __ beq(AT, R0, (int)0); ++ __ delayed()->nop(); ++ ++ break; ++ ++ default: ++ Unimplemented(); ++ } ++ %} ++ ++ ++ ins_pc_relative(1); ++ ins_pipe( pipe_alu_branch ); ++ ins_short_branch(1); ++%} ++ ++instruct branchConUL_regL_regL_short(cmpOp cmp, mRegL src1, mRegL src2, label labl) %{ ++ match( If cmp (CmpUL src1 src2) ); ++ effect(USE labl); ++ format %{ "BR$cmp $src1, $src2, $labl #@branchConUL_regL_regL_short" %} ++ ins_cost(250); ++ ++ ins_encode %{ ++ Register opr1_reg = as_Register($src1$$reg); ++ Register opr2_reg = as_Register($src2$$reg); ++ Label &target = *($labl$$label); ++ int flag = $cmp$$cmpcode; ++ ++ switch(flag) { ++ case 0x01: // equal ++ if (&target) ++ __ beq(opr1_reg, opr2_reg, target); ++ else ++ __ beq(opr1_reg, opr2_reg, (int)0); ++ __ delayed()->nop(); ++ break; ++ ++ case 0x02: // not_equal ++ if(&target) ++ __ bne(opr1_reg, opr2_reg, target); ++ else ++ __ bne(opr1_reg, opr2_reg, (int)0); ++ __ delayed()->nop(); ++ break; ++ ++ case 0x03: // greater ++ __ sltu(AT, opr2_reg, opr1_reg); ++ if(&target) ++ __ bne(AT, R0, target); ++ else ++ __ bne(AT, R0, (int)0); ++ __ delayed()->nop(); ++ break; ++ ++ case 0x04: // greater_equal ++ __ sltu(AT, opr1_reg, opr2_reg); ++ if(&target) ++ __ beq(AT, R0, target); ++ else ++ __ beq(AT, R0, (int)0); ++ __ delayed()->nop(); ++ break; ++ ++ case 0x05: // less ++ __ sltu(AT, opr1_reg, opr2_reg); ++ if(&target) ++ __ bne(AT, R0, target); ++ else ++ __ bne(AT, R0, (int)0); ++ __ delayed()->nop(); ++ break; ++ ++ case 0x06: // less_equal ++ __ sltu(AT, opr2_reg, opr1_reg); ++ if(&target) ++ __ beq(AT, R0, target); ++ else ++ __ beq(AT, R0, (int)0); ++ __ delayed()->nop(); ++ break; ++ ++ default: ++ Unimplemented(); ++ } ++ %} ++ ++ ins_pc_relative(1); ++ ins_pipe(pipe_alu_branch); ++ ins_short_branch(1); ++%} ++ ++instruct branchConL_regL_immL_0_short(cmpOp cmp, mRegL src1, immL_0 zero, label labl) %{ ++ match( If cmp (CmpL src1 zero) ); ++ effect(USE labl); ++ format %{ "BR$cmp $src1, zero, $labl #@branchConL_regL_immL_0_short" %} ++ ins_cost(150); ++ ++ ins_encode %{ ++ Register opr1_reg = as_Register($src1$$reg); ++ Label &target = *($labl$$label); ++ int flag = $cmp$$cmpcode; ++ ++ switch(flag) { ++ case 0x01: //equal ++ if (&target) ++ __ beq(opr1_reg, R0, target); ++ else ++ __ beq(opr1_reg, R0, int(0)); ++ break; ++ ++ case 0x02: //not_equal ++ if(&target) ++ __ bne(opr1_reg, R0, target); ++ else ++ __ bne(opr1_reg, R0, (int)0); ++ break; ++ ++ case 0x03: //greater ++ if(&target) ++ __ bgtz(opr1_reg, target); ++ else ++ __ bgtz(opr1_reg, (int)0); ++ break; ++ ++ case 0x04: //greater_equal ++ if(&target) ++ __ bgez(opr1_reg, target); ++ else ++ __ bgez(opr1_reg, (int)0); ++ break; ++ ++ case 0x05: //less ++ __ slt(AT, opr1_reg, R0); ++ if(&target) ++ __ bne(AT, R0, target); ++ else ++ __ bne(AT, R0, (int)0); ++ break; ++ ++ case 0x06: //less_equal ++ if (&target) ++ __ blez(opr1_reg, target); ++ else ++ __ blez(opr1_reg, int(0)); ++ break; ++ ++ default: ++ Unimplemented(); ++ } ++ __ delayed()->nop(); ++ %} ++ ++ ++ ins_pc_relative(1); ++ ins_pipe( pipe_alu_branch ); ++ ins_short_branch(1); ++%} ++ ++instruct branchConUL_regL_immL_0_short(cmpOp cmp, mRegL src1, immL_0 zero, label labl) %{ ++ match(If cmp (CmpUL src1 zero)); ++ effect(USE labl); ++ format %{ "BR$cmp $src1, zero, $labl #@branchConUL_regL_immL_0_short" %} ++ ins_cost(150); ++ ++ ins_encode %{ ++ Register opr1_reg = as_Register($src1$$reg); ++ Label &target = *($labl$$label); ++ int flag = $cmp$$cmpcode; ++ ++ switch(flag) { ++ case 0x01: // equal ++ case 0x04: // greater_equal ++ case 0x06: // less_equal ++ if (&target) ++ __ beq(opr1_reg, R0, target); ++ else ++ __ beq(opr1_reg, R0, int(0)); ++ break; ++ ++ case 0x02: // not_equal ++ case 0x03: // greater ++ if(&target) ++ __ bne(opr1_reg, R0, target); ++ else ++ __ bne(opr1_reg, R0, (int)0); ++ break; ++ ++ case 0x05: // less ++ if(&target) ++ __ beq(R0, R0, target); ++ else ++ __ beq(R0, R0, (int)0); ++ break; ++ ++ default: ++ Unimplemented(); ++ } ++ __ delayed()->nop(); ++ %} ++ ++ ins_pc_relative(1); ++ ins_pipe(pipe_alu_branch); ++ ins_short_branch(1); ++%} ++ ++instruct branchConL_regL_immL_short(cmpOp cmp, mRegL src1, immL src2, label labl) %{ ++ match( If cmp (CmpL src1 src2) ); ++ effect(USE labl); ++ format %{ "BR$cmp $src1, $src2, $labl #@branchConL_regL_immL_short" %} ++ ins_cost(180); ++ ++ ins_encode %{ ++ Register opr1_reg = as_Register($src1$$reg); ++ Register opr2_reg = AT; ++ ++ Label &target = *($labl$$label); ++ int flag = $cmp$$cmpcode; ++ ++ __ set64(opr2_reg, $src2$$constant); ++ ++ switch(flag) { ++ case 0x01: //equal ++ if (&target) ++ __ beq(opr1_reg, opr2_reg, target); ++ else ++ __ beq(opr1_reg, opr2_reg, (int)0); ++ break; ++ ++ case 0x02: //not_equal ++ if(&target) ++ __ bne(opr1_reg, opr2_reg, target); ++ else ++ __ bne(opr1_reg, opr2_reg, (int)0); ++ break; ++ ++ case 0x03: //greater ++ __ slt(AT, opr2_reg, opr1_reg); ++ if(&target) ++ __ bne(AT, R0, target); ++ else ++ __ bne(AT, R0, (int)0); ++ break; ++ ++ case 0x04: //greater_equal ++ __ slt(AT, opr1_reg, opr2_reg); ++ if(&target) ++ __ beq(AT, R0, target); ++ else ++ __ beq(AT, R0, (int)0); ++ break; ++ ++ case 0x05: //less ++ __ slt(AT, opr1_reg, opr2_reg); ++ if(&target) ++ __ bne(AT, R0, target); ++ else ++ __ bne(AT, R0, (int)0); ++ break; ++ ++ case 0x06: //less_equal ++ __ slt(AT, opr2_reg, opr1_reg); ++ if(&target) ++ __ beq(AT, R0, target); ++ else ++ __ beq(AT, R0, (int)0); ++ break; ++ ++ default: ++ Unimplemented(); ++ } ++ __ delayed()->nop(); ++ %} ++ ++ ++ ins_pc_relative(1); ++ ins_pipe( pipe_alu_branch ); ++ ins_short_branch(1); ++%} ++ ++instruct branchConUL_regL_immL_short(cmpOp cmp, mRegL src1, immL src2, label labl) %{ ++ match(If cmp (CmpUL src1 src2)); ++ effect(USE labl); ++ format %{ "BR$cmp $src1, $src2, $labl #@branchConUL_regL_immL_short" %} ++ ins_cost(180); ++ ++ ins_encode %{ ++ Register opr1_reg = as_Register($src1$$reg); ++ Register opr2_reg = AT; ++ Label &target = *($labl$$label); ++ int flag = $cmp$$cmpcode; ++ ++ __ set64(opr2_reg, $src2$$constant); ++ ++ switch(flag) { ++ case 0x01: // equal ++ if (&target) ++ __ beq(opr1_reg, opr2_reg, target); ++ else ++ __ beq(opr1_reg, opr2_reg, (int)0); ++ break; ++ ++ case 0x02: // not_equal ++ if(&target) ++ __ bne(opr1_reg, opr2_reg, target); ++ else ++ __ bne(opr1_reg, opr2_reg, (int)0); ++ break; ++ ++ case 0x03: // greater ++ __ sltu(AT, opr2_reg, opr1_reg); ++ if(&target) ++ __ bne(AT, R0, target); ++ else ++ __ bne(AT, R0, (int)0); ++ break; ++ ++ case 0x04: // greater_equal ++ __ sltu(AT, opr1_reg, opr2_reg); ++ if(&target) ++ __ beq(AT, R0, target); ++ else ++ __ beq(AT, R0, (int)0); ++ break; ++ ++ case 0x05: // less ++ __ sltu(AT, opr1_reg, opr2_reg); ++ if(&target) ++ __ bne(AT, R0, target); ++ else ++ __ bne(AT, R0, (int)0); ++ break; ++ ++ case 0x06: // less_equal ++ __ sltu(AT, opr2_reg, opr1_reg); ++ if(&target) ++ __ beq(AT, R0, target); ++ else ++ __ beq(AT, R0, (int)0); ++ break; ++ ++ default: ++ Unimplemented(); ++ } ++ __ delayed()->nop(); ++ %} ++ ++ ins_pc_relative(1); ++ ins_pipe(pipe_alu_branch); ++ ins_short_branch(1); ++%} ++ ++//FIXME ++instruct branchConF_reg_reg_short(cmpOp cmp, regF src1, regF src2, label labl) %{ ++ match( If cmp (CmpF src1 src2) ); ++ effect(USE labl); ++ format %{ "BR$cmp $src1, $src2, $labl #@branchConF_reg_reg_short" %} ++ ++ ins_encode %{ ++ FloatRegister reg_op1 = $src1$$FloatRegister; ++ FloatRegister reg_op2 = $src2$$FloatRegister; ++ Label& L = *($labl$$label); ++ int flag = $cmp$$cmpcode; ++ ++ switch(flag) { ++ case 0x01: // equal ++ __ c_eq_s(reg_op1, reg_op2); ++ if (&L) ++ __ bc1t(L); ++ else ++ __ bc1t((int)0); ++ break; ++ case 0x02: // not_equal ++ __ c_eq_s(reg_op1, reg_op2); ++ if (&L) ++ __ bc1f(L); ++ else ++ __ bc1f((int)0); ++ break; ++ case 0x03: // greater ++ __ c_ule_s(reg_op1, reg_op2); ++ if(&L) ++ __ bc1f(L); ++ else ++ __ bc1f((int)0); ++ break; ++ case 0x04: // greater_equal ++ __ c_ult_s(reg_op1, reg_op2); ++ if(&L) ++ __ bc1f(L); ++ else ++ __ bc1f((int)0); ++ break; ++ case 0x05: // less ++ __ c_ult_s(reg_op1, reg_op2); ++ if(&L) ++ __ bc1t(L); ++ else ++ __ bc1t((int)0); ++ break; ++ case 0x06: // less_equal ++ __ c_ule_s(reg_op1, reg_op2); ++ if(&L) ++ __ bc1t(L); ++ else ++ __ bc1t((int)0); ++ break; ++ default: ++ Unimplemented(); ++ } ++ __ delayed()->nop(); ++ %} ++ ++ ins_pc_relative(1); ++ ins_pipe(pipe_fpu_branch); ++ ins_short_branch(1); ++%} ++ ++instruct branchConD_reg_reg_short(cmpOp cmp, regD src1, regD src2, label labl) %{ ++ match( If cmp (CmpD src1 src2) ); ++ effect(USE labl); ++ format %{ "BR$cmp $src1, $src2, $labl #@branchConD_reg_reg_short" %} ++ ++ ins_encode %{ ++ FloatRegister reg_op1 = $src1$$FloatRegister; ++ FloatRegister reg_op2 = $src2$$FloatRegister; ++ Label& L = *($labl$$label); ++ int flag = $cmp$$cmpcode; ++ ++ switch(flag) { ++ case 0x01: // equal ++ __ c_eq_d(reg_op1, reg_op2); ++ if (&L) ++ __ bc1t(L); ++ else ++ __ bc1t((int)0); ++ break; ++ case 0x02: // not_equal ++ // c_ueq_d cannot distinguish NaN from equal. Double.isNaN(Double) is implemented by 'f != f', so the use of c_ueq_d causes bugs. ++ __ c_eq_d(reg_op1, reg_op2); ++ if (&L) ++ __ bc1f(L); ++ else ++ __ bc1f((int)0); ++ break; ++ case 0x03: // greater ++ __ c_ule_d(reg_op1, reg_op2); ++ if(&L) ++ __ bc1f(L); ++ else ++ __ bc1f((int)0); ++ break; ++ case 0x04: // greater_equal ++ __ c_ult_d(reg_op1, reg_op2); ++ if(&L) ++ __ bc1f(L); ++ else ++ __ bc1f((int)0); ++ break; ++ case 0x05: // less ++ __ c_ult_d(reg_op1, reg_op2); ++ if(&L) ++ __ bc1t(L); ++ else ++ __ bc1t((int)0); ++ break; ++ case 0x06: // less_equal ++ __ c_ule_d(reg_op1, reg_op2); ++ if(&L) ++ __ bc1t(L); ++ else ++ __ bc1t((int)0); ++ break; ++ default: ++ Unimplemented(); ++ } ++ __ delayed()->nop(); ++ %} ++ ++ ins_pc_relative(1); ++ ins_pipe(pipe_fpu_branch); ++ ins_short_branch(1); ++%} ++ ++// =================== End of branch instructions ========================== ++ ++// Call Runtime Instruction ++instruct CallRuntimeDirect(method meth) %{ ++ match(CallRuntime ); ++ effect(USE meth); ++ ++ ins_cost(300); ++ format %{ "CALL,runtime #@CallRuntimeDirect" %} ++ ins_encode( Java_To_Runtime( meth ) ); ++ ins_pipe( pipe_slow ); ++ ins_alignment(16); ++%} ++ ++ ++ ++//------------------------MemBar Instructions------------------------------- ++//Memory barrier flavors ++ ++instruct membar_acquire() %{ ++ match(MemBarAcquire); ++ ins_cost(400); ++ ++ format %{ "MEMBAR-acquire @ membar_acquire" %} ++ ins_encode %{ ++ __ sync(); ++ %} ++ ins_pipe(empty); ++%} ++ ++instruct load_fence() %{ ++ match(LoadFence); ++ ins_cost(400); ++ ++ format %{ "MEMBAR @ load_fence" %} ++ ins_encode %{ ++ __ sync(); ++ %} ++ ins_pipe(pipe_slow); ++%} ++ ++instruct membar_acquire_lock() ++%{ ++ match(MemBarAcquireLock); ++ ins_cost(0); ++ ++ size(0); ++ format %{ "MEMBAR-acquire (acquire as part of CAS in prior FastLock so empty encoding) @ membar_acquire_lock" %} ++ ins_encode(); ++ ins_pipe(empty); ++%} ++ ++instruct membar_release() %{ ++ match(MemBarRelease); ++ ins_cost(400); ++ ++ format %{ "MEMBAR-release @ membar_release" %} ++ ++ ins_encode %{ ++ // Attention: DO NOT DELETE THIS GUY! ++ __ sync(); ++ %} ++ ++ ins_pipe(pipe_slow); ++%} ++ ++instruct store_fence() %{ ++ match(StoreFence); ++ ins_cost(400); ++ ++ format %{ "MEMBAR @ store_fence" %} ++ ++ ins_encode %{ ++ __ sync(); ++ %} ++ ++ ins_pipe(pipe_slow); ++%} ++ ++instruct membar_release_lock() ++%{ ++ match(MemBarReleaseLock); ++ ins_cost(0); ++ ++ size(0); ++ format %{ "MEMBAR-release-lock (release in FastUnlock so empty) @ membar_release_lock" %} ++ ins_encode(); ++ ins_pipe(empty); ++%} ++ ++ ++instruct membar_volatile() %{ ++ match(MemBarVolatile); ++ ins_cost(400); ++ ++ format %{ "MEMBAR-volatile" %} ++ ins_encode %{ ++ if( !os::is_MP() ) return; // Not needed on single CPU ++ __ sync(); ++ ++ %} ++ ins_pipe(pipe_slow); ++%} ++ ++instruct unnecessary_membar_volatile() %{ ++ match(MemBarVolatile); ++ predicate(Matcher::post_store_load_barrier(n)); ++ ins_cost(0); ++ ++ size(0); ++ format %{ "MEMBAR-volatile (unnecessary so empty encoding) @ unnecessary_membar_volatile" %} ++ ins_encode( ); ++ ins_pipe(empty); ++%} ++ ++instruct membar_storestore() %{ ++ match(MemBarStoreStore); ++ ++ ins_cost(400); ++ format %{ "MEMBAR-storestore @ membar_storestore" %} ++ ins_encode %{ ++ __ sync(); ++ %} ++ ins_pipe(empty); ++%} ++ ++//----------Move Instructions-------------------------------------------------- ++instruct castX2P(mRegP dst, mRegL src) %{ ++ match(Set dst (CastX2P src)); ++ format %{ "castX2P $dst, $src @ castX2P" %} ++ ins_encode %{ ++ Register src = $src$$Register; ++ Register dst = $dst$$Register; ++ ++ if(src != dst) ++ __ move(dst, src); ++ %} ++ ins_cost(10); ++ ins_pipe( ialu_regI_mov ); ++%} ++ ++instruct castP2X(mRegL dst, mRegP src ) %{ ++ match(Set dst (CastP2X src)); ++ ++ format %{ "mov $dst, $src\t #@castP2X" %} ++ ins_encode %{ ++ Register src = $src$$Register; ++ Register dst = $dst$$Register; ++ ++ if(src != dst) ++ __ move(dst, src); ++ %} ++ ins_pipe( ialu_regI_mov ); ++%} ++ ++instruct MoveF2I_reg_reg(mRegI dst, regF src) %{ ++ match(Set dst (MoveF2I src)); ++ effect(DEF dst, USE src); ++ ins_cost(85); ++ format %{ "MoveF2I $dst, $src @ MoveF2I_reg_reg" %} ++ ins_encode %{ ++ Register dst = as_Register($dst$$reg); ++ FloatRegister src = as_FloatRegister($src$$reg); ++ ++ __ mfc1(dst, src); ++ %} ++ ins_pipe( pipe_slow ); ++%} ++ ++instruct MoveI2F_reg_reg(regF dst, mRegI src) %{ ++ match(Set dst (MoveI2F src)); ++ effect(DEF dst, USE src); ++ ins_cost(85); ++ format %{ "MoveI2F $dst, $src @ MoveI2F_reg_reg" %} ++ ins_encode %{ ++ Register src = as_Register($src$$reg); ++ FloatRegister dst = as_FloatRegister($dst$$reg); ++ ++ __ mtc1(src, dst); ++ %} ++ ins_pipe( pipe_slow ); ++%} ++ ++instruct MoveD2L_reg_reg(mRegL dst, regD src) %{ ++ match(Set dst (MoveD2L src)); ++ effect(DEF dst, USE src); ++ ins_cost(85); ++ format %{ "MoveD2L $dst, $src @ MoveD2L_reg_reg" %} ++ ins_encode %{ ++ Register dst = as_Register($dst$$reg); ++ FloatRegister src = as_FloatRegister($src$$reg); ++ ++ __ dmfc1(dst, src); ++ %} ++ ins_pipe( pipe_slow ); ++%} ++ ++instruct MoveL2D_reg_reg(regD dst, mRegL src) %{ ++ match(Set dst (MoveL2D src)); ++ effect(DEF dst, USE src); ++ ins_cost(85); ++ format %{ "MoveL2D $dst, $src @ MoveL2D_reg_reg" %} ++ ins_encode %{ ++ FloatRegister dst = as_FloatRegister($dst$$reg); ++ Register src = as_Register($src$$reg); ++ ++ __ dmtc1(src, dst); ++ %} ++ ins_pipe( pipe_slow ); ++%} ++ ++//----------Conditional Move--------------------------------------------------- ++// Conditional move ++instruct cmovI_cmpI_reg_reg(mRegI dst, mRegI src, mRegI tmp1, mRegI tmp2, cmpOp cop ) %{ ++ match(Set dst (CMoveI (Binary cop (CmpI tmp1 tmp2)) (Binary dst src))); ++ ins_cost(80); ++ format %{ ++ "CMP$cop $tmp1, $tmp2\t @cmovI_cmpI_reg_reg\n" ++ "\tCMOV $dst,$src \t @cmovI_cmpI_reg_reg" ++ %} ++ ++ ins_encode %{ ++ Register op1 = $tmp1$$Register; ++ Register op2 = $tmp2$$Register; ++ Register dst = $dst$$Register; ++ Register src = $src$$Register; ++ int flag = $cop$$cmpcode; ++ ++ __ cmp_cmov(op1, op2, dst, src, (MacroAssembler::CMCompare) flag, true /* is_signed */); ++ %} ++ ++ ins_pipe( pipe_slow ); ++%} ++ ++instruct cmovI_cmpP_reg_reg(mRegI dst, mRegI src, mRegP tmp1, mRegP tmp2, cmpOpU cop ) %{ ++ match(Set dst (CMoveI (Binary cop (CmpP tmp1 tmp2)) (Binary dst src))); ++ ins_cost(80); ++ format %{ ++ "CMPU$cop $tmp1,$tmp2\t @cmovI_cmpP_reg_reg\n\t" ++ "CMOV $dst,$src\t @cmovI_cmpP_reg_reg" ++ %} ++ ins_encode %{ ++ Register op1 = $tmp1$$Register; ++ Register op2 = $tmp2$$Register; ++ Register dst = $dst$$Register; ++ Register src = $src$$Register; ++ int flag = $cop$$cmpcode; ++ ++ __ cmp_cmov(op1, op2, dst, src, (MacroAssembler::CMCompare) flag, false /* is_signed */); ++ %} ++ ++ ins_pipe( pipe_slow ); ++%} ++ ++instruct cmovI_cmpN_reg_reg(mRegI dst, mRegI src, mRegN tmp1, mRegN tmp2, cmpOpU cop ) %{ ++ match(Set dst (CMoveI (Binary cop (CmpN tmp1 tmp2)) (Binary dst src))); ++ ins_cost(80); ++ format %{ ++ "CMPU$cop $tmp1,$tmp2\t @cmovI_cmpN_reg_reg\n\t" ++ "CMOV $dst,$src\t @cmovI_cmpN_reg_reg" ++ %} ++ ins_encode %{ ++ Register op1 = $tmp1$$Register; ++ Register op2 = $tmp2$$Register; ++ Register dst = $dst$$Register; ++ Register src = $src$$Register; ++ int flag = $cop$$cmpcode; ++ ++ __ cmp_cmov(op1, op2, dst, src, (MacroAssembler::CMCompare) flag, false /* is_signed */); ++ %} ++ ++ ins_pipe( pipe_slow ); ++%} ++ ++instruct cmovP_cmpU_reg_reg(mRegP dst, mRegP src, mRegI tmp1, mRegI tmp2, cmpOpU cop ) %{ ++ match(Set dst (CMoveP (Binary cop (CmpU tmp1 tmp2)) (Binary dst src))); ++ ins_cost(80); ++ format %{ ++ "CMPU$cop $tmp1,$tmp2\t @cmovP_cmpU_reg_reg\n\t" ++ "CMOV $dst,$src\t @cmovP_cmpU_reg_reg" ++ %} ++ ins_encode %{ ++ Register op1 = $tmp1$$Register; ++ Register op2 = $tmp2$$Register; ++ Register dst = $dst$$Register; ++ Register src = $src$$Register; ++ int flag = $cop$$cmpcode; ++ ++ __ cmp_cmov(op1, op2, dst, src, (MacroAssembler::CMCompare) flag, false /* is_signed */); ++ %} ++ ++ ins_pipe( pipe_slow ); ++%} ++ ++instruct cmovP_cmpF_reg_reg(mRegP dst, mRegP src, regF tmp1, regF tmp2, cmpOp cop ) %{ ++ match(Set dst (CMoveP (Binary cop (CmpF tmp1 tmp2)) (Binary dst src))); ++ ins_cost(80); ++ format %{ ++ "CMP$cop $tmp1, $tmp2\t @cmovP_cmpF_reg_reg\n" ++ "\tCMOV $dst,$src \t @cmovP_cmpF_reg_reg" ++ %} ++ ++ ins_encode %{ ++ FloatRegister reg_op1 = $tmp1$$FloatRegister; ++ FloatRegister reg_op2 = $tmp2$$FloatRegister; ++ Register dst = $dst$$Register; ++ Register src = $src$$Register; ++ int flag = $cop$$cmpcode; ++ ++ __ cmp_cmov(reg_op1, reg_op2, dst, src, (MacroAssembler::CMCompare) flag, true /* is_float */); ++ %} ++ ins_pipe( pipe_slow ); ++%} ++ ++instruct cmovP_cmpN_reg_reg(mRegP dst, mRegP src, mRegN tmp1, mRegN tmp2, cmpOpU cop ) %{ ++ match(Set dst (CMoveP (Binary cop (CmpN tmp1 tmp2)) (Binary dst src))); ++ ins_cost(80); ++ format %{ ++ "CMPU$cop $tmp1,$tmp2\t @cmovP_cmpN_reg_reg\n\t" ++ "CMOV $dst,$src\t @cmovP_cmpN_reg_reg" ++ %} ++ ins_encode %{ ++ Register op1 = $tmp1$$Register; ++ Register op2 = $tmp2$$Register; ++ Register dst = $dst$$Register; ++ Register src = $src$$Register; ++ int flag = $cop$$cmpcode; ++ ++ __ cmp_cmov(op1, op2, dst, src, (MacroAssembler::CMCompare) flag, false /* is_signed */); ++ %} ++ ++ ins_pipe( pipe_slow ); ++%} ++ ++instruct cmovN_cmpP_reg_reg(mRegN dst, mRegN src, mRegP tmp1, mRegP tmp2, cmpOpU cop ) %{ ++ match(Set dst (CMoveN (Binary cop (CmpP tmp1 tmp2)) (Binary dst src))); ++ ins_cost(80); ++ format %{ ++ "CMPU$cop $tmp1,$tmp2\t @cmovN_cmpP_reg_reg\n\t" ++ "CMOV $dst,$src\t @cmovN_cmpP_reg_reg" ++ %} ++ ins_encode %{ ++ Register op1 = $tmp1$$Register; ++ Register op2 = $tmp2$$Register; ++ Register dst = $dst$$Register; ++ Register src = $src$$Register; ++ int flag = $cop$$cmpcode; ++ ++ __ cmp_cmov(op1, op2, dst, src, (MacroAssembler::CMCompare) flag, false /* is_signed */); ++ %} ++ ++ ins_pipe( pipe_slow ); ++%} ++ ++instruct cmovP_cmpD_reg_reg(mRegP dst, mRegP src, regD tmp1, regD tmp2, cmpOp cop ) %{ ++ match(Set dst (CMoveP (Binary cop (CmpD tmp1 tmp2)) (Binary dst src))); ++ ins_cost(80); ++ format %{ ++ "CMP$cop $tmp1, $tmp2\t @cmovP_cmpD_reg_reg\n" ++ "\tCMOV $dst,$src \t @cmovP_cmpD_reg_reg" ++ %} ++ ins_encode %{ ++ FloatRegister reg_op1 = as_FloatRegister($tmp1$$reg); ++ FloatRegister reg_op2 = as_FloatRegister($tmp2$$reg); ++ Register dst = as_Register($dst$$reg); ++ Register src = as_Register($src$$reg); ++ int flag = $cop$$cmpcode; ++ ++ __ cmp_cmov(reg_op1, reg_op2, dst, src, (MacroAssembler::CMCompare) flag, false /* is_float */); ++ %} ++ ++ ins_pipe( pipe_slow ); ++%} ++ ++ ++instruct cmovN_cmpN_reg_reg(mRegN dst, mRegN src, mRegN tmp1, mRegN tmp2, cmpOpU cop ) %{ ++ match(Set dst (CMoveN (Binary cop (CmpN tmp1 tmp2)) (Binary dst src))); ++ ins_cost(80); ++ format %{ ++ "CMPU$cop $tmp1,$tmp2\t @cmovN_cmpN_reg_reg\n\t" ++ "CMOV $dst,$src\t @cmovN_cmpN_reg_reg" ++ %} ++ ins_encode %{ ++ Register op1 = $tmp1$$Register; ++ Register op2 = $tmp2$$Register; ++ Register dst = $dst$$Register; ++ Register src = $src$$Register; ++ int flag = $cop$$cmpcode; ++ ++ __ cmp_cmov(op1, op2, dst, src, (MacroAssembler::CMCompare) flag, false /* is_signed */); ++ %} ++ ++ ins_pipe( pipe_slow ); ++%} ++ ++ ++instruct cmovI_cmpU_reg_reg(mRegI dst, mRegI src, mRegI tmp1, mRegI tmp2, cmpOpU cop ) %{ ++ match(Set dst (CMoveI (Binary cop (CmpU tmp1 tmp2)) (Binary dst src))); ++ ins_cost(80); ++ format %{ ++ "CMPU$cop $tmp1,$tmp2\t @cmovI_cmpU_reg_reg\n\t" ++ "CMOV $dst,$src\t @cmovI_cmpU_reg_reg" ++ %} ++ ins_encode %{ ++ Register op1 = $tmp1$$Register; ++ Register op2 = $tmp2$$Register; ++ Register dst = $dst$$Register; ++ Register src = $src$$Register; ++ int flag = $cop$$cmpcode; ++ ++ __ cmp_cmov(op1, op2, dst, src, (MacroAssembler::CMCompare) flag, false /* is_signed */); ++ %} ++ ++ ins_pipe( pipe_slow ); ++%} ++ ++instruct cmovI_cmpL_reg_reg(mRegI dst, mRegI src, mRegL tmp1, mRegL tmp2, cmpOp cop ) %{ ++ match(Set dst (CMoveI (Binary cop (CmpL tmp1 tmp2)) (Binary dst src))); ++ ins_cost(80); ++ format %{ ++ "CMP$cop $tmp1, $tmp2\t @cmovI_cmpL_reg_reg\n" ++ "\tCMOV $dst,$src \t @cmovI_cmpL_reg_reg" ++ %} ++ ins_encode %{ ++ Register opr1 = as_Register($tmp1$$reg); ++ Register opr2 = as_Register($tmp2$$reg); ++ Register dst = $dst$$Register; ++ Register src = $src$$Register; ++ int flag = $cop$$cmpcode; ++ ++ __ cmp_cmov(opr1, opr2, dst, src, (MacroAssembler::CMCompare) flag, true /* is_signed */); ++ %} ++ ++ ins_pipe( pipe_slow ); ++%} ++ ++instruct cmovI_cmpUL_reg_reg(mRegI dst, mRegI src, mRegL tmp1, mRegL tmp2, cmpOp cop) %{ ++ match(Set dst (CMoveI (Binary cop (CmpUL tmp1 tmp2)) (Binary dst src))); ++ ins_cost(80); ++ format %{ ++ "CMP$cop $tmp1, $tmp2\t @cmovI_cmpUL_reg_reg\n" ++ "\tCMOV $dst,$src \t @cmovI_cmpUL_reg_reg" ++ %} ++ ins_encode %{ ++ Register opr1 = as_Register($tmp1$$reg); ++ Register opr2 = as_Register($tmp2$$reg); ++ Register dst = $dst$$Register; ++ Register src = $src$$Register; ++ int flag = $cop$$cmpcode; ++ ++ __ cmp_cmov(opr1, opr2, dst, src, (MacroAssembler::CMCompare) flag, false /* is_signed */); ++ %} ++ ++ ins_pipe(pipe_slow); ++%} ++ ++instruct cmovP_cmpL_reg_reg(mRegP dst, mRegP src, mRegL tmp1, mRegL tmp2, cmpOp cop ) %{ ++ match(Set dst (CMoveP (Binary cop (CmpL tmp1 tmp2)) (Binary dst src))); ++ ins_cost(80); ++ format %{ ++ "CMP$cop $tmp1, $tmp2\t @cmovP_cmpL_reg_reg\n" ++ "\tCMOV $dst,$src \t @cmovP_cmpL_reg_reg" ++ %} ++ ins_encode %{ ++ Register opr1 = as_Register($tmp1$$reg); ++ Register opr2 = as_Register($tmp2$$reg); ++ Register dst = $dst$$Register; ++ Register src = $src$$Register; ++ int flag = $cop$$cmpcode; ++ ++ __ cmp_cmov(opr1, opr2, dst, src, (MacroAssembler::CMCompare) flag, true /* is_signed */); ++ %} ++ ++ ins_pipe( pipe_slow ); ++%} ++ ++instruct cmovP_cmpUL_reg_reg(mRegP dst, mRegP src, mRegL tmp1, mRegL tmp2, cmpOp cop) %{ ++ match(Set dst (CMoveP (Binary cop (CmpUL tmp1 tmp2)) (Binary dst src))); ++ ins_cost(80); ++ format %{ ++ "CMP$cop $tmp1, $tmp2\t @cmovP_cmpUL_reg_reg\n" ++ "\tCMOV $dst,$src \t @cmovP_cmpUL_reg_reg" ++ %} ++ ins_encode %{ ++ Register opr1 = as_Register($tmp1$$reg); ++ Register opr2 = as_Register($tmp2$$reg); ++ Register dst = $dst$$Register; ++ Register src = $src$$Register; ++ int flag = $cop$$cmpcode; ++ ++ __ cmp_cmov(opr1, opr2, dst, src, (MacroAssembler::CMCompare) flag, false /* is_signed */); ++ %} ++ ++ ins_pipe(pipe_slow); ++%} ++ ++instruct cmovI_cmpD_reg_reg(mRegI dst, mRegI src, regD tmp1, regD tmp2, cmpOp cop ) %{ ++ match(Set dst (CMoveI (Binary cop (CmpD tmp1 tmp2)) (Binary dst src))); ++ ins_cost(80); ++ format %{ ++ "CMP$cop $tmp1, $tmp2\t @cmovI_cmpD_reg_reg\n" ++ "\tCMOV $dst,$src \t @cmovI_cmpD_reg_reg" ++ %} ++ ins_encode %{ ++ FloatRegister reg_op1 = as_FloatRegister($tmp1$$reg); ++ FloatRegister reg_op2 = as_FloatRegister($tmp2$$reg); ++ Register dst = as_Register($dst$$reg); ++ Register src = as_Register($src$$reg); ++ int flag = $cop$$cmpcode; ++ ++ __ cmp_cmov(reg_op1, reg_op2, dst, src, (MacroAssembler::CMCompare) flag, false /* is_float */); ++ %} ++ ++ ins_pipe( pipe_slow ); ++%} ++ ++ ++instruct cmovP_cmpP_reg_reg(mRegP dst, mRegP src, mRegP tmp1, mRegP tmp2, cmpOpU cop ) %{ ++ match(Set dst (CMoveP (Binary cop (CmpP tmp1 tmp2)) (Binary dst src))); ++ ins_cost(80); ++ format %{ ++ "CMPU$cop $tmp1,$tmp2\t @cmovP_cmpP_reg_reg\n\t" ++ "CMOV $dst,$src\t @cmovP_cmpP_reg_reg" ++ %} ++ ins_encode %{ ++ Register op1 = $tmp1$$Register; ++ Register op2 = $tmp2$$Register; ++ Register dst = $dst$$Register; ++ Register src = $src$$Register; ++ int flag = $cop$$cmpcode; ++ ++ __ cmp_cmov(op1, op2, dst, src, (MacroAssembler::CMCompare) flag, false /* is_signed */); ++ %} ++ ++ ins_pipe( pipe_slow ); ++%} ++ ++instruct cmovP_cmpI_reg_reg(mRegP dst, mRegP src, mRegI tmp1, mRegI tmp2, cmpOp cop ) %{ ++ match(Set dst (CMoveP (Binary cop (CmpI tmp1 tmp2)) (Binary dst src))); ++ ins_cost(80); ++ format %{ ++ "CMP$cop $tmp1,$tmp2\t @cmovP_cmpI_reg_reg\n\t" ++ "CMOV $dst,$src\t @cmovP_cmpI_reg_reg" ++ %} ++ ins_encode %{ ++ Register op1 = $tmp1$$Register; ++ Register op2 = $tmp2$$Register; ++ Register dst = $dst$$Register; ++ Register src = $src$$Register; ++ int flag = $cop$$cmpcode; ++ ++ __ cmp_cmov(op1, op2, dst, src, (MacroAssembler::CMCompare) flag, true /* is_signed */); ++ %} ++ ++ ins_pipe( pipe_slow ); ++%} ++ ++instruct cmovL_cmpP_reg_reg(mRegL dst, mRegL src, mRegP tmp1, mRegP tmp2, cmpOpU cop ) %{ ++ match(Set dst (CMoveL (Binary cop (CmpP tmp1 tmp2)) (Binary dst src))); ++ ins_cost(80); ++ format %{ ++ "CMPU$cop $tmp1,$tmp2\t @cmovL_cmpP_reg_reg\n\t" ++ "CMOV $dst,$src\t @cmovL_cmpP_reg_reg" ++ %} ++ ins_encode %{ ++ Register op1 = $tmp1$$Register; ++ Register op2 = $tmp2$$Register; ++ Register dst = $dst$$Register; ++ Register src = $src$$Register; ++ int flag = $cop$$cmpcode; ++ ++ __ cmp_cmov(op1, op2, dst, src, (MacroAssembler::CMCompare) flag, false /* is_signed */); ++ %} ++ ++ ins_pipe( pipe_slow ); ++%} ++ ++instruct cmovN_cmpU_reg_reg(mRegN dst, mRegN src, mRegI tmp1, mRegI tmp2, cmpOpU cop ) %{ ++ match(Set dst (CMoveN (Binary cop (CmpU tmp1 tmp2)) (Binary dst src))); ++ ins_cost(80); ++ format %{ ++ "CMPU$cop $tmp1,$tmp2\t @cmovN_cmpU_reg_reg\n\t" ++ "CMOV $dst,$src\t @cmovN_cmpU_reg_reg" ++ %} ++ ins_encode %{ ++ Register op1 = $tmp1$$Register; ++ Register op2 = $tmp2$$Register; ++ Register dst = $dst$$Register; ++ Register src = $src$$Register; ++ int flag = $cop$$cmpcode; ++ ++ __ cmp_cmov(op1, op2, dst, src, (MacroAssembler::CMCompare) flag, false /* is_signed */); ++ %} ++ ++ ins_pipe( pipe_slow ); ++%} ++ ++instruct cmovN_cmpL_reg_reg(mRegN dst, mRegN src, mRegL tmp1, mRegL tmp2, cmpOp cop) %{ ++ match(Set dst (CMoveN (Binary cop (CmpL tmp1 tmp2)) (Binary dst src))); ++ ins_cost(80); ++ format %{ ++ "CMP$cop $tmp1, $tmp2\t @cmovN_cmpL_reg_reg\n" ++ "\tCMOV $dst,$src \t @cmovN_cmpL_reg_reg" ++ %} ++ ins_encode %{ ++ Register opr1 = as_Register($tmp1$$reg); ++ Register opr2 = as_Register($tmp2$$reg); ++ Register dst = $dst$$Register; ++ Register src = $src$$Register; ++ int flag = $cop$$cmpcode; ++ ++ __ cmp_cmov(opr1, opr2, dst, src, (MacroAssembler::CMCompare) flag, true /* is_signed */); ++ %} ++ ++ ins_pipe( pipe_slow ); ++%} ++ ++instruct cmovN_cmpUL_reg_reg(mRegN dst, mRegN src, mRegL tmp1, mRegL tmp2, cmpOp cop) %{ ++ match(Set dst (CMoveN (Binary cop (CmpUL tmp1 tmp2)) (Binary dst src))); ++ ins_cost(80); ++ format %{ ++ "CMP$cop $tmp1, $tmp2\t @cmovN_cmpUL_reg_reg\n" ++ "\tCMOV $dst,$src \t @cmovN_cmpUL_reg_reg" ++ %} ++ ins_encode %{ ++ Register opr1 = as_Register($tmp1$$reg); ++ Register opr2 = as_Register($tmp2$$reg); ++ Register dst = $dst$$Register; ++ Register src = $src$$Register; ++ int flag = $cop$$cmpcode; ++ ++ __ cmp_cmov(opr1, opr2, dst, src, (MacroAssembler::CMCompare) flag, false /* is_signed */); ++ %} ++ ++ ins_pipe(pipe_slow); ++%} ++ ++instruct cmovN_cmpI_reg_reg(mRegN dst, mRegN src, mRegI tmp1, mRegI tmp2, cmpOp cop ) %{ ++ match(Set dst (CMoveN (Binary cop (CmpI tmp1 tmp2)) (Binary dst src))); ++ ins_cost(80); ++ format %{ ++ "CMP$cop $tmp1,$tmp2\t @cmovN_cmpI_reg_reg\n\t" ++ "CMOV $dst,$src\t @cmovN_cmpI_reg_reg" ++ %} ++ ins_encode %{ ++ Register op1 = $tmp1$$Register; ++ Register op2 = $tmp2$$Register; ++ Register dst = $dst$$Register; ++ Register src = $src$$Register; ++ int flag = $cop$$cmpcode; ++ ++ __ cmp_cmov(op1, op2, dst, src, (MacroAssembler::CMCompare) flag, true /* is_signed */); ++ %} ++ ++ ins_pipe( pipe_slow ); ++%} ++ ++instruct cmovL_cmpU_reg_reg(mRegL dst, mRegL src, mRegI tmp1, mRegI tmp2, cmpOpU cop ) %{ ++ match(Set dst (CMoveL (Binary cop (CmpU tmp1 tmp2)) (Binary dst src))); ++ ins_cost(80); ++ format %{ ++ "CMPU$cop $tmp1,$tmp2\t @cmovL_cmpU_reg_reg\n\t" ++ "CMOV $dst,$src\t @cmovL_cmpU_reg_reg" ++ %} ++ ins_encode %{ ++ Register op1 = $tmp1$$Register; ++ Register op2 = $tmp2$$Register; ++ Register dst = $dst$$Register; ++ Register src = $src$$Register; ++ int flag = $cop$$cmpcode; ++ ++ __ cmp_cmov(op1, op2, dst, src, (MacroAssembler::CMCompare) flag, false /* is_signed */); ++ %} ++ ++ ins_pipe( pipe_slow ); ++%} ++ ++instruct cmovL_cmpF_reg_reg(mRegL dst, mRegL src, regF tmp1, regF tmp2, cmpOp cop ) %{ ++ match(Set dst (CMoveL (Binary cop (CmpF tmp1 tmp2)) (Binary dst src))); ++ ins_cost(80); ++ format %{ ++ "CMP$cop $tmp1, $tmp2\t @cmovL_cmpF_reg_reg\n" ++ "\tCMOV $dst,$src \t @cmovL_cmpF_reg_reg" ++ %} ++ ++ ins_encode %{ ++ FloatRegister reg_op1 = $tmp1$$FloatRegister; ++ FloatRegister reg_op2 = $tmp2$$FloatRegister; ++ Register dst = $dst$$Register; ++ Register src = $src$$Register; ++ int flag = $cop$$cmpcode; ++ ++ __ cmp_cmov(reg_op1, reg_op2, dst, src, (MacroAssembler::CMCompare) flag, true /* is_float */); ++ %} ++ ins_pipe( pipe_slow ); ++%} ++ ++instruct cmovL_cmpI_reg_reg(mRegL dst, mRegL src, mRegI tmp1, mRegI tmp2, cmpOp cop ) %{ ++ match(Set dst (CMoveL (Binary cop (CmpI tmp1 tmp2)) (Binary dst src))); ++ ins_cost(80); ++ format %{ ++ "CMP$cop $tmp1, $tmp2\t @cmovL_cmpI_reg_reg\n" ++ "\tCMOV $dst,$src \t @cmovL_cmpI_reg_reg" ++ %} ++ ++ ins_encode %{ ++ Register op1 = $tmp1$$Register; ++ Register op2 = $tmp2$$Register; ++ Register dst = as_Register($dst$$reg); ++ Register src = as_Register($src$$reg); ++ int flag = $cop$$cmpcode; ++ ++ __ cmp_cmov(op1, op2, dst, src, (MacroAssembler::CMCompare) flag, true /* is_signed */); ++ %} ++ ++ ins_pipe( pipe_slow ); ++%} ++ ++instruct cmovL_cmpL_reg_reg(mRegL dst, mRegL src, mRegL tmp1, mRegL tmp2, cmpOp cop ) %{ ++ match(Set dst (CMoveL (Binary cop (CmpL tmp1 tmp2)) (Binary dst src))); ++ ins_cost(80); ++ format %{ ++ "CMP$cop $tmp1, $tmp2\t @cmovL_cmpL_reg_reg\n" ++ "\tCMOV $dst,$src \t @cmovL_cmpL_reg_reg" ++ %} ++ ins_encode %{ ++ Register opr1 = as_Register($tmp1$$reg); ++ Register opr2 = as_Register($tmp2$$reg); ++ Register dst = as_Register($dst$$reg); ++ Register src = as_Register($src$$reg); ++ int flag = $cop$$cmpcode; ++ ++ __ cmp_cmov(opr1, opr2, dst, src, (MacroAssembler::CMCompare) flag, true /* is_signed */); ++ %} ++ ++ ins_pipe( pipe_slow ); ++%} ++ ++instruct cmovL_cmpUL_reg_reg(mRegL dst, mRegL src, mRegL tmp1, mRegL tmp2, cmpOp cop) %{ ++ match(Set dst (CMoveL (Binary cop (CmpUL tmp1 tmp2)) (Binary dst src))); ++ ins_cost(80); ++ format %{ ++ "CMP$cop $tmp1, $tmp2\t @cmovL_cmpUL_reg_reg\n" ++ "\tCMOV $dst,$src \t @cmovL_cmpUL_reg_reg" ++ %} ++ ins_encode %{ ++ Register opr1 = as_Register($tmp1$$reg); ++ Register opr2 = as_Register($tmp2$$reg); ++ Register dst = as_Register($dst$$reg); ++ Register src = as_Register($src$$reg); ++ int flag = $cop$$cmpcode; ++ ++ __ cmp_cmov(opr1, opr2, dst, src, (MacroAssembler::CMCompare) flag, false /* is_signed */); ++ %} ++ ++ ins_pipe(pipe_slow); ++%} ++ ++instruct cmovL_cmpN_reg_reg(mRegL dst, mRegL src, mRegN tmp1, mRegN tmp2, cmpOpU cop ) %{ ++ match(Set dst (CMoveL (Binary cop (CmpN tmp1 tmp2)) (Binary dst src))); ++ ins_cost(80); ++ format %{ ++ "CMPU$cop $tmp1,$tmp2\t @cmovL_cmpN_reg_reg\n\t" ++ "CMOV $dst,$src\t @cmovL_cmpN_reg_reg" ++ %} ++ ins_encode %{ ++ Register op1 = $tmp1$$Register; ++ Register op2 = $tmp2$$Register; ++ Register dst = $dst$$Register; ++ Register src = $src$$Register; ++ int flag = $cop$$cmpcode; ++ ++ __ cmp_cmov(op1, op2, dst, src, (MacroAssembler::CMCompare) flag, false /* is_signed */); ++ %} ++ ++ ins_pipe( pipe_slow ); ++%} ++ ++ ++instruct cmovL_cmpD_reg_reg(mRegL dst, mRegL src, regD tmp1, regD tmp2, cmpOp cop ) %{ ++ match(Set dst (CMoveL (Binary cop (CmpD tmp1 tmp2)) (Binary dst src))); ++ ins_cost(80); ++ format %{ ++ "CMP$cop $tmp1, $tmp2\t @cmovL_cmpD_reg_reg\n" ++ "\tCMOV $dst,$src \t @cmovL_cmpD_reg_reg" ++ %} ++ ins_encode %{ ++ FloatRegister reg_op1 = as_FloatRegister($tmp1$$reg); ++ FloatRegister reg_op2 = as_FloatRegister($tmp2$$reg); ++ Register dst = as_Register($dst$$reg); ++ Register src = as_Register($src$$reg); ++ int flag = $cop$$cmpcode; ++ ++ __ cmp_cmov(reg_op1, reg_op2, dst, src, (MacroAssembler::CMCompare) flag, false /* is_float */); ++ %} ++ ++ ins_pipe( pipe_slow ); ++%} ++ ++instruct cmovD_cmpD_reg_reg(regD dst, regD src, regD tmp1, regD tmp2, cmpOp cop ) %{ ++ match(Set dst (CMoveD (Binary cop (CmpD tmp1 tmp2)) (Binary dst src))); ++ ins_cost(200); ++ format %{ ++ "CMP$cop $tmp1, $tmp2\t @cmovD_cmpD_reg_reg\n" ++ "\tCMOV $dst,$src \t @cmovD_cmpD_reg_reg" ++ %} ++ ins_encode %{ ++ FloatRegister reg_op1 = as_FloatRegister($tmp1$$reg); ++ FloatRegister reg_op2 = as_FloatRegister($tmp2$$reg); ++ FloatRegister dst = as_FloatRegister($dst$$reg); ++ FloatRegister src = as_FloatRegister($src$$reg); ++ int flag = $cop$$cmpcode; ++ ++ __ cmp_cmov(reg_op1, reg_op2, dst, src, (MacroAssembler::CMCompare) flag, false /* is_float */); ++ %} ++ ++ ins_pipe( pipe_slow ); ++%} ++ ++instruct cmovF_cmpI_reg_reg(regF dst, regF src, mRegI tmp1, mRegI tmp2, cmpOp cop ) %{ ++ match(Set dst (CMoveF (Binary cop (CmpI tmp1 tmp2)) (Binary dst src))); ++ ins_cost(200); ++ format %{ ++ "CMP$cop $tmp1, $tmp2\t @cmovF_cmpI_reg_reg\n" ++ "\tCMOV $dst, $src \t @cmovF_cmpI_reg_reg" ++ %} ++ ++ ins_encode %{ ++ Register op1 = $tmp1$$Register; ++ Register op2 = $tmp2$$Register; ++ FloatRegister dst = as_FloatRegister($dst$$reg); ++ FloatRegister src = as_FloatRegister($src$$reg); ++ int flag = $cop$$cmpcode; ++ ++ __ cmp_cmov(op1, op2, dst, src, (MacroAssembler::CMCompare) flag, true /* is_float */); ++ %} ++ ++ ins_pipe( pipe_slow ); ++%} ++ ++instruct cmovD_cmpI_reg_reg(regD dst, regD src, mRegI tmp1, mRegI tmp2, cmpOp cop ) %{ ++ match(Set dst (CMoveD (Binary cop (CmpI tmp1 tmp2)) (Binary dst src))); ++ ins_cost(200); ++ format %{ ++ "CMP$cop $tmp1, $tmp2\t @cmovD_cmpI_reg_reg\n" ++ "\tCMOV $dst, $src \t @cmovD_cmpI_reg_reg" ++ %} ++ ++ ins_encode %{ ++ Register op1 = $tmp1$$Register; ++ Register op2 = $tmp2$$Register; ++ FloatRegister dst = as_FloatRegister($dst$$reg); ++ FloatRegister src = as_FloatRegister($src$$reg); ++ int flag = $cop$$cmpcode; ++ ++ __ cmp_cmov(op1, op2, dst, src, (MacroAssembler::CMCompare) flag, false /* is_float */); ++ %} ++ ++ ins_pipe( pipe_slow ); ++%} ++ ++instruct cmovD_cmpP_reg_reg(regD dst, regD src, mRegP tmp1, mRegP tmp2, cmpOp cop ) %{ ++ match(Set dst (CMoveD (Binary cop (CmpP tmp1 tmp2)) (Binary dst src))); ++ ins_cost(200); ++ format %{ ++ "CMP$cop $tmp1, $tmp2\t @cmovD_cmpP_reg_reg\n" ++ "\tCMOV $dst, $src \t @cmovD_cmpP_reg_reg" ++ %} ++ ++ ins_encode %{ ++ Register op1 = $tmp1$$Register; ++ Register op2 = $tmp2$$Register; ++ FloatRegister dst = as_FloatRegister($dst$$reg); ++ FloatRegister src = as_FloatRegister($src$$reg); ++ int flag = $cop$$cmpcode; ++ ++ __ cmp_cmov(op1, op2, dst, src, (MacroAssembler::CMCompare) flag, false /* is_float */); ++ %} ++ ++ ins_pipe( pipe_slow ); ++%} ++ ++//FIXME ++instruct cmovI_cmpF_reg_reg(mRegI dst, mRegI src, regF tmp1, regF tmp2, cmpOp cop ) %{ ++ match(Set dst (CMoveI (Binary cop (CmpF tmp1 tmp2)) (Binary dst src))); ++ ins_cost(80); ++ format %{ ++ "CMP$cop $tmp1, $tmp2\t @cmovI_cmpF_reg_reg\n" ++ "\tCMOV $dst,$src \t @cmovI_cmpF_reg_reg" ++ %} ++ ++ ins_encode %{ ++ FloatRegister reg_op1 = $tmp1$$FloatRegister; ++ FloatRegister reg_op2 = $tmp2$$FloatRegister; ++ Register dst = $dst$$Register; ++ Register src = $src$$Register; ++ int flag = $cop$$cmpcode; ++ ++ __ cmp_cmov(reg_op1, reg_op2, dst, src, (MacroAssembler::CMCompare) flag, true /* is_float */); ++ %} ++ ins_pipe( pipe_slow ); ++%} ++ ++instruct cmovF_cmpF_reg_reg(regF dst, regF src, regF tmp1, regF tmp2, cmpOp cop ) %{ ++ match(Set dst (CMoveF (Binary cop (CmpF tmp1 tmp2)) (Binary dst src))); ++ ins_cost(200); ++ format %{ ++ "CMP$cop $tmp1, $tmp2\t @cmovF_cmpF_reg_reg\n" ++ "\tCMOV $dst,$src \t @cmovF_cmpF_reg_reg" ++ %} ++ ++ ins_encode %{ ++ FloatRegister reg_op1 = $tmp1$$FloatRegister; ++ FloatRegister reg_op2 = $tmp2$$FloatRegister; ++ FloatRegister dst = $dst$$FloatRegister; ++ FloatRegister src = $src$$FloatRegister; ++ int flag = $cop$$cmpcode; ++ ++ __ cmp_cmov(reg_op1, reg_op2, dst, src, (MacroAssembler::CMCompare) flag, true /* is_float */); ++ %} ++ ins_pipe( pipe_slow ); ++%} ++ ++// Manifest a CmpL result in an integer register. Very painful. ++// This is the test to avoid. ++instruct cmpL3_reg_reg(mRegI dst, mRegL src1, mRegL src2) %{ ++ match(Set dst (CmpL3 src1 src2)); ++ ins_cost(1000); ++ format %{ "cmpL3 $dst, $src1, $src2 @ cmpL3_reg_reg" %} ++ ins_encode %{ ++ Register opr1 = as_Register($src1$$reg); ++ Register opr2 = as_Register($src2$$reg); ++ Register dst = as_Register($dst$$reg); ++ ++ Label Done; ++ ++ __ subu(AT, opr1, opr2); ++ __ bltz(AT, Done); ++ __ delayed()->daddiu(dst, R0, -1); ++ ++ __ move(dst, 1); ++ __ movz(dst, R0, AT); ++ ++ __ bind(Done); ++ %} ++ ins_pipe( pipe_slow ); ++%} ++ ++// ++// less_rsult = -1 ++// greater_result = 1 ++// equal_result = 0 ++// nan_result = -1 ++// ++instruct cmpF3_reg_reg(mRegI dst, regF src1, regF src2) %{ ++ match(Set dst (CmpF3 src1 src2)); ++ ins_cost(1000); ++ format %{ "cmpF3 $dst, $src1, $src2 @ cmpF3_reg_reg" %} ++ ins_encode %{ ++ FloatRegister src1 = as_FloatRegister($src1$$reg); ++ FloatRegister src2 = as_FloatRegister($src2$$reg); ++ Register dst = as_Register($dst$$reg); ++ ++ Label Done; ++ ++ __ c_ult_s(src1, src2); ++ __ bc1t(Done); ++ __ delayed()->daddiu(dst, R0, -1); ++ ++ __ c_eq_s(src1, src2); ++ __ move(dst, 1); ++ __ movt(dst, R0); ++ ++ __ bind(Done); ++ %} ++ ins_pipe( pipe_slow ); ++%} ++ ++instruct cmpD3_reg_reg(mRegI dst, regD src1, regD src2) %{ ++ match(Set dst (CmpD3 src1 src2)); ++ ins_cost(1000); ++ format %{ "cmpD3 $dst, $src1, $src2 @ cmpD3_reg_reg" %} ++ ins_encode %{ ++ FloatRegister src1 = as_FloatRegister($src1$$reg); ++ FloatRegister src2 = as_FloatRegister($src2$$reg); ++ Register dst = as_Register($dst$$reg); ++ ++ Label Done; ++ ++ __ c_ult_d(src1, src2); ++ __ bc1t(Done); ++ __ delayed()->daddiu(dst, R0, -1); ++ ++ __ c_eq_d(src1, src2); ++ __ move(dst, 1); ++ __ movt(dst, R0); ++ ++ __ bind(Done); ++ %} ++ ins_pipe( pipe_slow ); ++%} ++ ++instruct clear_array(mRegL cnt, mRegP base, Universe dummy) %{ ++ match(Set dummy (ClearArray cnt base)); ++ format %{ "CLEAR_ARRAY base = $base, cnt = $cnt # Clear doublewords" %} ++ ins_encode %{ ++ //Assume cnt is the number of bytes in an array to be cleared, ++ //and base points to the starting address of the array. ++ Register base = $base$$Register; ++ Register num = $cnt$$Register; ++ Label Loop, done; ++ ++ __ beq(num, R0, done); ++ __ delayed()->daddu(AT, base, R0); ++ ++ __ move(T9, num); /* T9 = words */ ++ ++ __ bind(Loop); ++ __ sd(R0, AT, 0); ++ __ daddiu(T9, T9, -1); ++ __ bne(T9, R0, Loop); ++ __ delayed()->daddiu(AT, AT, wordSize); ++ ++ __ bind(done); ++ %} ++ ins_pipe( pipe_slow ); ++%} ++ ++instruct string_compare(a4_RegP str1, mA5RegI cnt1, a6_RegP str2, mA7RegI cnt2, no_Ax_mRegI result) %{ ++ match(Set result (StrComp (Binary str1 cnt1) (Binary str2 cnt2))); ++ effect(USE_KILL str1, USE_KILL str2, USE_KILL cnt1, USE_KILL cnt2); ++ ++ format %{ "String Compare $str1[len: $cnt1], $str2[len: $cnt2] -> $result @ string_compare" %} ++ ins_encode %{ ++ // Get the first character position in both strings ++ // [8] char array, [12] offset, [16] count ++ Register str1 = $str1$$Register; ++ Register str2 = $str2$$Register; ++ Register cnt1 = $cnt1$$Register; ++ Register cnt2 = $cnt2$$Register; ++ Register result = $result$$Register; ++ ++ Label L, Loop, haveResult, done; ++ ++ // compute the and difference of lengths (in result) ++ __ subu(result, cnt1, cnt2); // result holds the difference of two lengths ++ ++ // compute the shorter length (in cnt1) ++ __ slt(AT, cnt2, cnt1); ++ __ movn(cnt1, cnt2, AT); ++ ++ // Now the shorter length is in cnt1 and cnt2 can be used as a tmp register ++ __ bind(Loop); // Loop begin ++ __ beq(cnt1, R0, done); ++ __ delayed()->lhu(AT, str1, 0);; ++ ++ // compare current character ++ __ lhu(cnt2, str2, 0); ++ __ bne(AT, cnt2, haveResult); ++ __ delayed()->addiu(str1, str1, 2); ++ __ addiu(str2, str2, 2); ++ __ b(Loop); ++ __ delayed()->addiu(cnt1, cnt1, -1); // Loop end ++ ++ __ bind(haveResult); ++ __ subu(result, AT, cnt2); ++ ++ __ bind(done); ++ %} ++ ++ ins_pipe( pipe_slow ); ++%} ++ ++// intrinsic optimization ++instruct string_equals(a4_RegP str1, a5_RegP str2, mA6RegI cnt, mA7RegI temp, no_Ax_mRegI result) %{ ++ match(Set result (StrEquals (Binary str1 str2) cnt)); ++ effect(USE_KILL str1, USE_KILL str2, USE_KILL cnt, KILL temp); ++ ++ format %{ "String Equal $str1, $str2, len:$cnt tmp:$temp -> $result @ string_equals" %} ++ ins_encode %{ ++ // Get the first character position in both strings ++ // [8] char array, [12] offset, [16] count ++ Register str1 = $str1$$Register; ++ Register str2 = $str2$$Register; ++ Register cnt = $cnt$$Register; ++ Register tmp = $temp$$Register; ++ Register result = $result$$Register; ++ ++ Label Loop, True, False; ++ ++ __ beq(str1, str2, True); // same char[] ? ++ __ delayed()->daddiu(result, R0, 1); ++ ++ __ beq(cnt, R0, True); ++ __ delayed()->nop(); // count == 0 ++ ++ __ bind(Loop); ++ ++ // compare current character ++ __ lhu(AT, str1, 0); ++ __ lhu(tmp, str2, 0); ++ __ bne(AT, tmp, False); ++ __ delayed()->addiu(str1, str1, 2); ++ __ addiu(cnt, cnt, -1); ++ __ bne(cnt, R0, Loop); ++ __ delayed()->addiu(str2, str2, 2); ++ ++ __ b(True); ++ __ delayed()->nop(); ++ ++ __ bind(False); ++ __ daddiu(result, R0, 0); ++ ++ __ bind(True); ++ %} ++ ++ ins_pipe( pipe_slow ); ++%} ++ ++//----------Arithmetic Instructions------------------------------------------- ++//----------Addition Instructions--------------------------------------------- ++instruct addI_Reg_Reg(mRegI dst, mRegI src1, mRegI src2) %{ ++ match(Set dst (AddI src1 src2)); ++ ++ format %{ "addu $dst, $src1, $src2 #@addI_Reg_Reg" %} ++ ins_encode %{ ++ Register dst = $dst$$Register; ++ Register src1 = $src1$$Register; ++ Register src2 = $src2$$Register; ++ __ addu32(dst, src1, src2); ++ %} ++ ins_pipe( ialu_regI_regI ); ++%} ++ ++instruct addI_Reg_imm(mRegI dst, mRegI src1, immI src2) %{ ++ match(Set dst (AddI src1 src2)); ++ ++ format %{ "addu $dst, $src1, $src2 #@addI_Reg_imm" %} ++ ins_encode %{ ++ Register dst = $dst$$Register; ++ Register src1 = $src1$$Register; ++ int imm = $src2$$constant; ++ ++ if(Assembler::is_simm16(imm)) { ++ __ addiu32(dst, src1, imm); ++ } else { ++ __ move(AT, imm); ++ __ addu32(dst, src1, AT); ++ } ++ %} ++ ins_pipe( ialu_regI_regI ); ++%} ++ ++instruct addP_reg_reg(mRegP dst, mRegP src1, mRegL src2) %{ ++ match(Set dst (AddP src1 src2)); ++ ++ format %{ "daddu $dst, $src1, $src2 #@addP_reg_reg" %} ++ ++ ins_encode %{ ++ Register dst = $dst$$Register; ++ Register src1 = $src1$$Register; ++ Register src2 = $src2$$Register; ++ __ daddu(dst, src1, src2); ++ %} ++ ++ ins_pipe( ialu_regI_regI ); ++%} ++ ++instruct addP_reg_reg_convI2L(mRegP dst, mRegP src1, mRegI src2) %{ ++ match(Set dst (AddP src1 (ConvI2L src2))); ++ ++ format %{ "daddu $dst, $src1, $src2 #@addP_reg_reg_convI2L" %} ++ ++ ins_encode %{ ++ Register dst = $dst$$Register; ++ Register src1 = $src1$$Register; ++ Register src2 = $src2$$Register; ++ __ daddu(dst, src1, src2); ++ %} ++ ++ ins_pipe( ialu_regI_regI ); ++%} ++ ++instruct addP_reg_imm(mRegP dst, mRegP src1, immL src2) %{ ++ match(Set dst (AddP src1 src2)); ++ ++ format %{ "daddiu $dst, $src1, $src2 #@addP_reg_imm" %} ++ ins_encode %{ ++ Register src1 = $src1$$Register; ++ long src2 = $src2$$constant; ++ Register dst = $dst$$Register; ++ ++ if(Assembler::is_simm16(src2)) { ++ __ daddiu(dst, src1, src2); ++ } else { ++ __ set64(AT, src2); ++ __ daddu(dst, src1, AT); ++ } ++ %} ++ ins_pipe( ialu_regI_imm16 ); ++%} ++ ++// Add Long Register with Register ++instruct addL_Reg_Reg(mRegL dst, mRegL src1, mRegL src2) %{ ++ match(Set dst (AddL src1 src2)); ++ ins_cost(200); ++ format %{ "ADD $dst, $src1, $src2 #@addL_Reg_Reg\t" %} ++ ++ ins_encode %{ ++ Register dst_reg = as_Register($dst$$reg); ++ Register src1_reg = as_Register($src1$$reg); ++ Register src2_reg = as_Register($src2$$reg); ++ ++ __ daddu(dst_reg, src1_reg, src2_reg); ++ %} ++ ++ ins_pipe( ialu_regL_regL ); ++%} ++ ++instruct addL_Reg_imm(mRegL dst, mRegL src1, immL16 src2) ++%{ ++ match(Set dst (AddL src1 src2)); ++ ++ format %{ "ADD $dst, $src1, $src2 #@addL_Reg_imm " %} ++ ins_encode %{ ++ Register dst_reg = as_Register($dst$$reg); ++ Register src1_reg = as_Register($src1$$reg); ++ int src2_imm = $src2$$constant; ++ ++ __ daddiu(dst_reg, src1_reg, src2_imm); ++ %} ++ ++ ins_pipe( ialu_regL_regL ); ++%} ++ ++instruct addL_RegI2L_imm(mRegL dst, mRegI src1, immL16 src2) ++%{ ++ match(Set dst (AddL (ConvI2L src1) src2)); ++ ++ format %{ "ADD $dst, $src1, $src2 #@addL_RegI2L_imm " %} ++ ins_encode %{ ++ Register dst_reg = as_Register($dst$$reg); ++ Register src1_reg = as_Register($src1$$reg); ++ int src2_imm = $src2$$constant; ++ ++ __ daddiu(dst_reg, src1_reg, src2_imm); ++ %} ++ ++ ins_pipe( ialu_regL_regL ); ++%} ++ ++instruct addL_RegI2L_Reg(mRegL dst, mRegI src1, mRegL src2) %{ ++ match(Set dst (AddL (ConvI2L src1) src2)); ++ ins_cost(200); ++ format %{ "ADD $dst, $src1, $src2 #@addL_RegI2L_Reg\t" %} ++ ++ ins_encode %{ ++ Register dst_reg = as_Register($dst$$reg); ++ Register src1_reg = as_Register($src1$$reg); ++ Register src2_reg = as_Register($src2$$reg); ++ ++ __ daddu(dst_reg, src1_reg, src2_reg); ++ %} ++ ++ ins_pipe( ialu_regL_regL ); ++%} ++ ++instruct addL_RegI2L_RegI2L(mRegL dst, mRegI src1, mRegI src2) %{ ++ match(Set dst (AddL (ConvI2L src1) (ConvI2L src2))); ++ ins_cost(200); ++ format %{ "ADD $dst, $src1, $src2 #@addL_RegI2L_RegI2L\t" %} ++ ++ ins_encode %{ ++ Register dst_reg = as_Register($dst$$reg); ++ Register src1_reg = as_Register($src1$$reg); ++ Register src2_reg = as_Register($src2$$reg); ++ ++ __ daddu(dst_reg, src1_reg, src2_reg); ++ %} ++ ++ ins_pipe( ialu_regL_regL ); ++%} ++ ++instruct addL_Reg_RegI2L(mRegL dst, mRegL src1, mRegI src2) %{ ++ match(Set dst (AddL src1 (ConvI2L src2))); ++ ins_cost(200); ++ format %{ "ADD $dst, $src1, $src2 #@addL_Reg_RegI2L\t" %} ++ ++ ins_encode %{ ++ Register dst_reg = as_Register($dst$$reg); ++ Register src1_reg = as_Register($src1$$reg); ++ Register src2_reg = as_Register($src2$$reg); ++ ++ __ daddu(dst_reg, src1_reg, src2_reg); ++ %} ++ ++ ins_pipe( ialu_regL_regL ); ++%} ++ ++//----------Subtraction Instructions------------------------------------------- ++// Integer Subtraction Instructions ++instruct subI_Reg_Reg(mRegI dst, mRegI src1, mRegI src2) %{ ++ match(Set dst (SubI src1 src2)); ++ ins_cost(100); ++ ++ format %{ "subu $dst, $src1, $src2 #@subI_Reg_Reg" %} ++ ins_encode %{ ++ Register dst = $dst$$Register; ++ Register src1 = $src1$$Register; ++ Register src2 = $src2$$Register; ++ __ subu32(dst, src1, src2); ++ %} ++ ins_pipe( ialu_regI_regI ); ++%} ++ ++instruct subI_Reg_immI_M32767_32768(mRegI dst, mRegI src1, immI_M32767_32768 src2) %{ ++ match(Set dst (SubI src1 src2)); ++ ins_cost(80); ++ ++ format %{ "subu $dst, $src1, $src2 #@subI_Reg_immI_M32767_32768" %} ++ ins_encode %{ ++ Register dst = $dst$$Register; ++ Register src1 = $src1$$Register; ++ __ addiu32(dst, src1, -1 * $src2$$constant); ++ %} ++ ins_pipe( ialu_regI_regI ); ++%} ++ ++instruct negI_Reg(mRegI dst, immI_0 zero, mRegI src) %{ ++ match(Set dst (SubI zero src)); ++ ins_cost(80); ++ ++ format %{ "neg $dst, $src #@negI_Reg" %} ++ ins_encode %{ ++ Register dst = $dst$$Register; ++ Register src = $src$$Register; ++ __ subu32(dst, R0, src); ++ %} ++ ins_pipe( ialu_regI_regI ); ++%} ++ ++instruct negL_Reg(mRegL dst, immL_0 zero, mRegL src) %{ ++ match(Set dst (SubL zero src)); ++ ins_cost(80); ++ ++ format %{ "neg $dst, $src #@negL_Reg" %} ++ ins_encode %{ ++ Register dst = $dst$$Register; ++ Register src = $src$$Register; ++ __ subu(dst, R0, src); ++ %} ++ ins_pipe( ialu_regI_regI ); ++%} ++ ++instruct subL_Reg_immL_M32767_32768(mRegL dst, mRegL src1, immL_M32767_32768 src2) %{ ++ match(Set dst (SubL src1 src2)); ++ ins_cost(80); ++ ++ format %{ "subu $dst, $src1, $src2 #@subL_Reg_immL_M32767_32768" %} ++ ins_encode %{ ++ Register dst = $dst$$Register; ++ Register src1 = $src1$$Register; ++ __ daddiu(dst, src1, -1 * $src2$$constant); ++ %} ++ ins_pipe( ialu_regI_regI ); ++%} ++ ++// Subtract Long Register with Register. ++instruct subL_Reg_Reg(mRegL dst, mRegL src1, mRegL src2) %{ ++ match(Set dst (SubL src1 src2)); ++ ins_cost(100); ++ format %{ "SubL $dst, $src1, $src2 @ subL_Reg_Reg" %} ++ ins_encode %{ ++ Register dst = as_Register($dst$$reg); ++ Register src1 = as_Register($src1$$reg); ++ Register src2 = as_Register($src2$$reg); ++ ++ __ subu(dst, src1, src2); ++ %} ++ ins_pipe( ialu_regL_regL ); ++%} ++ ++instruct subL_Reg_RegI2L(mRegL dst, mRegL src1, mRegI src2) %{ ++ match(Set dst (SubL src1 (ConvI2L src2))); ++ ins_cost(100); ++ format %{ "SubL $dst, $src1, $src2 @ subL_Reg_RegI2L" %} ++ ins_encode %{ ++ Register dst = as_Register($dst$$reg); ++ Register src1 = as_Register($src1$$reg); ++ Register src2 = as_Register($src2$$reg); ++ ++ __ subu(dst, src1, src2); ++ %} ++ ins_pipe( ialu_regL_regL ); ++%} ++ ++instruct subL_RegI2L_Reg(mRegL dst, mRegI src1, mRegL src2) %{ ++ match(Set dst (SubL (ConvI2L src1) src2)); ++ ins_cost(200); ++ format %{ "SubL $dst, $src1, $src2 @ subL_RegI2L_Reg" %} ++ ins_encode %{ ++ Register dst = as_Register($dst$$reg); ++ Register src1 = as_Register($src1$$reg); ++ Register src2 = as_Register($src2$$reg); ++ ++ __ subu(dst, src1, src2); ++ %} ++ ins_pipe( ialu_regL_regL ); ++%} ++ ++instruct subL_RegI2L_RegI2L(mRegL dst, mRegI src1, mRegI src2) %{ ++ match(Set dst (SubL (ConvI2L src1) (ConvI2L src2))); ++ ins_cost(200); ++ format %{ "SubL $dst, $src1, $src2 @ subL_RegI2L_RegI2L" %} ++ ins_encode %{ ++ Register dst = as_Register($dst$$reg); ++ Register src1 = as_Register($src1$$reg); ++ Register src2 = as_Register($src2$$reg); ++ ++ __ subu(dst, src1, src2); ++ %} ++ ins_pipe( ialu_regL_regL ); ++%} ++ ++// Integer MOD with Register ++instruct modI_Reg_Reg(mRegI dst, mRegI src1, mRegI src2) %{ ++ match(Set dst (ModI src1 src2)); ++ ins_cost(300); ++ format %{ "modi $dst, $src1, $src2 @ modI_Reg_Reg" %} ++ ins_encode %{ ++ Register dst = $dst$$Register; ++ Register src1 = $src1$$Register; ++ Register src2 = $src2$$Register; ++ ++ //if (UseLEXT1) { ++ if (0) { ++ // Experiments show that gsmod is slower that div+mfhi. ++ // So I just disable it here. ++ __ gsmod(dst, src1, src2); ++ } else { ++ __ div(src1, src2); ++ __ mfhi(dst); ++ } ++ %} ++ ++ //ins_pipe( ialu_mod ); ++ ins_pipe( ialu_regI_regI ); ++%} ++ ++instruct modL_reg_reg(mRegL dst, mRegL src1, mRegL src2) %{ ++ match(Set dst (ModL src1 src2)); ++ format %{ "modL $dst, $src1, $src2 @modL_reg_reg" %} ++ ++ ins_encode %{ ++ Register dst = as_Register($dst$$reg); ++ Register op1 = as_Register($src1$$reg); ++ Register op2 = as_Register($src2$$reg); ++ ++ if (UseLEXT1) { ++ __ gsdmod(dst, op1, op2); ++ } else { ++ __ ddiv(op1, op2); ++ __ mfhi(dst); ++ } ++ %} ++ ins_pipe( pipe_slow ); ++%} ++ ++instruct mulI_Reg_Reg(mRegI dst, mRegI src1, mRegI src2) %{ ++ match(Set dst (MulI src1 src2)); ++ ++ ins_cost(300); ++ format %{ "mul $dst, $src1, $src2 @ mulI_Reg_Reg" %} ++ ins_encode %{ ++ Register src1 = $src1$$Register; ++ Register src2 = $src2$$Register; ++ Register dst = $dst$$Register; ++ ++ __ mul(dst, src1, src2); ++ %} ++ ins_pipe( ialu_mult ); ++%} ++ ++instruct maddI_Reg_Reg(mRegI dst, mRegI src1, mRegI src2, mRegI src3) %{ ++ match(Set dst (AddI (MulI src1 src2) src3)); ++ ++ ins_cost(999); ++ format %{ "madd $dst, $src1 * $src2 + $src3 #@maddI_Reg_Reg" %} ++ ins_encode %{ ++ Register src1 = $src1$$Register; ++ Register src2 = $src2$$Register; ++ Register src3 = $src3$$Register; ++ Register dst = $dst$$Register; ++ ++ __ mtlo(src3); ++ __ madd(src1, src2); ++ __ mflo(dst); ++ %} ++ ins_pipe( ialu_mult ); ++%} ++ ++instruct divI_Reg_Reg(mRegI dst, mRegI src1, mRegI src2) %{ ++ match(Set dst (DivI src1 src2)); ++ ++ ins_cost(300); ++ format %{ "div $dst, $src1, $src2 @ divI_Reg_Reg" %} ++ ins_encode %{ ++ Register src1 = $src1$$Register; ++ Register src2 = $src2$$Register; ++ Register dst = $dst$$Register; ++ ++ // In MIPS, div does not cause exception. ++ // We must trap an exception manually. ++ __ teq(R0, src2, 0x7); ++ ++ if (UseLEXT1) { ++ __ gsdiv(dst, src1, src2); ++ } else { ++ __ div(src1, src2); ++ ++ __ nop(); ++ __ nop(); ++ __ mflo(dst); ++ } ++ %} ++ ins_pipe( ialu_mod ); ++%} ++ ++instruct divF_Reg_Reg(regF dst, regF src1, regF src2) %{ ++ match(Set dst (DivF src1 src2)); ++ ++ ins_cost(300); ++ format %{ "divF $dst, $src1, $src2 @ divF_Reg_Reg" %} ++ ins_encode %{ ++ FloatRegister src1 = $src1$$FloatRegister; ++ FloatRegister src2 = $src2$$FloatRegister; ++ FloatRegister dst = $dst$$FloatRegister; ++ ++ /* Here do we need to trap an exception manually ? */ ++ __ div_s(dst, src1, src2); ++ %} ++ ins_pipe( pipe_slow ); ++%} ++ ++instruct divD_Reg_Reg(regD dst, regD src1, regD src2) %{ ++ match(Set dst (DivD src1 src2)); ++ ++ ins_cost(300); ++ format %{ "divD $dst, $src1, $src2 @ divD_Reg_Reg" %} ++ ins_encode %{ ++ FloatRegister src1 = $src1$$FloatRegister; ++ FloatRegister src2 = $src2$$FloatRegister; ++ FloatRegister dst = $dst$$FloatRegister; ++ ++ /* Here do we need to trap an exception manually ? */ ++ __ div_d(dst, src1, src2); ++ %} ++ ins_pipe( pipe_slow ); ++%} ++ ++instruct mulL_reg_reg(mRegL dst, mRegL src1, mRegL src2) %{ ++ match(Set dst (MulL src1 src2)); ++ format %{ "mulL $dst, $src1, $src2 @mulL_reg_reg" %} ++ ins_encode %{ ++ Register dst = as_Register($dst$$reg); ++ Register op1 = as_Register($src1$$reg); ++ Register op2 = as_Register($src2$$reg); ++ ++ if (UseLEXT1) { ++ __ gsdmult(dst, op1, op2); ++ } else { ++ __ dmult(op1, op2); ++ __ mflo(dst); ++ } ++ %} ++ ins_pipe( pipe_slow ); ++%} ++ ++instruct mulL_reg_regI2L(mRegL dst, mRegL src1, mRegI src2) %{ ++ match(Set dst (MulL src1 (ConvI2L src2))); ++ format %{ "mulL $dst, $src1, $src2 @mulL_reg_regI2L" %} ++ ins_encode %{ ++ Register dst = as_Register($dst$$reg); ++ Register op1 = as_Register($src1$$reg); ++ Register op2 = as_Register($src2$$reg); ++ ++ if (UseLEXT1) { ++ __ gsdmult(dst, op1, op2); ++ } else { ++ __ dmult(op1, op2); ++ __ mflo(dst); ++ } ++ %} ++ ins_pipe( pipe_slow ); ++%} ++ ++instruct divL_reg_reg(mRegL dst, mRegL src1, mRegL src2) %{ ++ match(Set dst (DivL src1 src2)); ++ format %{ "divL $dst, $src1, $src2 @divL_reg_reg" %} ++ ++ ins_encode %{ ++ Register dst = as_Register($dst$$reg); ++ Register op1 = as_Register($src1$$reg); ++ Register op2 = as_Register($src2$$reg); ++ ++ if (UseLEXT1) { ++ __ gsddiv(dst, op1, op2); ++ } else { ++ __ ddiv(op1, op2); ++ __ mflo(dst); ++ } ++ %} ++ ins_pipe( pipe_slow ); ++%} ++ ++instruct addF_reg_reg(regF dst, regF src1, regF src2) %{ ++ match(Set dst (AddF src1 src2)); ++ format %{ "AddF $dst, $src1, $src2 @addF_reg_reg" %} ++ ins_encode %{ ++ FloatRegister src1 = as_FloatRegister($src1$$reg); ++ FloatRegister src2 = as_FloatRegister($src2$$reg); ++ FloatRegister dst = as_FloatRegister($dst$$reg); ++ ++ __ add_s(dst, src1, src2); ++ %} ++ ins_pipe( fpu_regF_regF ); ++%} ++ ++instruct subF_reg_reg(regF dst, regF src1, regF src2) %{ ++ match(Set dst (SubF src1 src2)); ++ format %{ "SubF $dst, $src1, $src2 @subF_reg_reg" %} ++ ins_encode %{ ++ FloatRegister src1 = as_FloatRegister($src1$$reg); ++ FloatRegister src2 = as_FloatRegister($src2$$reg); ++ FloatRegister dst = as_FloatRegister($dst$$reg); ++ ++ __ sub_s(dst, src1, src2); ++ %} ++ ins_pipe( fpu_regF_regF ); ++%} ++instruct addD_reg_reg(regD dst, regD src1, regD src2) %{ ++ match(Set dst (AddD src1 src2)); ++ format %{ "AddD $dst, $src1, $src2 @addD_reg_reg" %} ++ ins_encode %{ ++ FloatRegister src1 = as_FloatRegister($src1$$reg); ++ FloatRegister src2 = as_FloatRegister($src2$$reg); ++ FloatRegister dst = as_FloatRegister($dst$$reg); ++ ++ __ add_d(dst, src1, src2); ++ %} ++ ins_pipe( fpu_regF_regF ); ++%} ++ ++instruct subD_reg_reg(regD dst, regD src1, regD src2) %{ ++ match(Set dst (SubD src1 src2)); ++ format %{ "SubD $dst, $src1, $src2 @subD_reg_reg" %} ++ ins_encode %{ ++ FloatRegister src1 = as_FloatRegister($src1$$reg); ++ FloatRegister src2 = as_FloatRegister($src2$$reg); ++ FloatRegister dst = as_FloatRegister($dst$$reg); ++ ++ __ sub_d(dst, src1, src2); ++ %} ++ ins_pipe( fpu_regF_regF ); ++%} ++ ++instruct negF_reg(regF dst, regF src) %{ ++ match(Set dst (NegF src)); ++ format %{ "negF $dst, $src @negF_reg" %} ++ ins_encode %{ ++ FloatRegister src = as_FloatRegister($src$$reg); ++ FloatRegister dst = as_FloatRegister($dst$$reg); ++ ++ __ neg_s(dst, src); ++ %} ++ ins_pipe( fpu_regF_regF ); ++%} ++ ++instruct negD_reg(regD dst, regD src) %{ ++ match(Set dst (NegD src)); ++ format %{ "negD $dst, $src @negD_reg" %} ++ ins_encode %{ ++ FloatRegister src = as_FloatRegister($src$$reg); ++ FloatRegister dst = as_FloatRegister($dst$$reg); ++ ++ __ neg_d(dst, src); ++ %} ++ ins_pipe( fpu_regF_regF ); ++%} ++ ++ ++instruct mulF_reg_reg(regF dst, regF src1, regF src2) %{ ++ match(Set dst (MulF src1 src2)); ++ format %{ "MULF $dst, $src1, $src2 @mulF_reg_reg" %} ++ ins_encode %{ ++ FloatRegister src1 = $src1$$FloatRegister; ++ FloatRegister src2 = $src2$$FloatRegister; ++ FloatRegister dst = $dst$$FloatRegister; ++ ++ __ mul_s(dst, src1, src2); ++ %} ++ ins_pipe( fpu_regF_regF ); ++%} ++ ++instruct maddF_reg_reg(regF dst, regF src1, regF src2, regF src3) %{ ++ match(Set dst (AddF (MulF src1 src2) src3)); ++ // For compatibility reason (e.g. on the Loongson platform), disable this guy. ++ ins_cost(44444); ++ format %{ "maddF $dst, $src1, $src2, $src3 @maddF_reg_reg" %} ++ ins_encode %{ ++ FloatRegister src1 = $src1$$FloatRegister; ++ FloatRegister src2 = $src2$$FloatRegister; ++ FloatRegister src3 = $src3$$FloatRegister; ++ FloatRegister dst = $dst$$FloatRegister; ++ ++ __ madd_s(dst, src1, src2, src3); ++ %} ++ ins_pipe( fpu_regF_regF ); ++%} ++ ++// Mul two double precision floating piont number ++instruct mulD_reg_reg(regD dst, regD src1, regD src2) %{ ++ match(Set dst (MulD src1 src2)); ++ format %{ "MULD $dst, $src1, $src2 @mulD_reg_reg" %} ++ ins_encode %{ ++ FloatRegister src1 = $src1$$FloatRegister; ++ FloatRegister src2 = $src2$$FloatRegister; ++ FloatRegister dst = $dst$$FloatRegister; ++ ++ __ mul_d(dst, src1, src2); ++ %} ++ ins_pipe( fpu_regF_regF ); ++%} ++ ++instruct maddD_reg_reg(regD dst, regD src1, regD src2, regD src3) %{ ++ match(Set dst (AddD (MulD src1 src2) src3)); ++ // For compatibility reason (e.g. on the Loongson platform), disable this guy. ++ ins_cost(44444); ++ format %{ "maddD $dst, $src1, $src2, $src3 @maddD_reg_reg" %} ++ ins_encode %{ ++ FloatRegister src1 = $src1$$FloatRegister; ++ FloatRegister src2 = $src2$$FloatRegister; ++ FloatRegister src3 = $src3$$FloatRegister; ++ FloatRegister dst = $dst$$FloatRegister; ++ ++ __ madd_d(dst, src1, src2, src3); ++ %} ++ ins_pipe( fpu_regF_regF ); ++%} ++ ++instruct absF_reg(regF dst, regF src) %{ ++ match(Set dst (AbsF src)); ++ ins_cost(100); ++ format %{ "absF $dst, $src @absF_reg" %} ++ ins_encode %{ ++ FloatRegister src = as_FloatRegister($src$$reg); ++ FloatRegister dst = as_FloatRegister($dst$$reg); ++ ++ __ abs_s(dst, src); ++ %} ++ ins_pipe( fpu_regF_regF ); ++%} ++ ++ ++// intrinsics for math_native. ++// AbsD SqrtD CosD SinD TanD LogD Log10D ++ ++instruct absD_reg(regD dst, regD src) %{ ++ match(Set dst (AbsD src)); ++ ins_cost(100); ++ format %{ "absD $dst, $src @absD_reg" %} ++ ins_encode %{ ++ FloatRegister src = as_FloatRegister($src$$reg); ++ FloatRegister dst = as_FloatRegister($dst$$reg); ++ ++ __ abs_d(dst, src); ++ %} ++ ins_pipe( fpu_regF_regF ); ++%} ++ ++instruct sqrtD_reg(regD dst, regD src) %{ ++ match(Set dst (SqrtD src)); ++ ins_cost(100); ++ format %{ "SqrtD $dst, $src @sqrtD_reg" %} ++ ins_encode %{ ++ FloatRegister src = as_FloatRegister($src$$reg); ++ FloatRegister dst = as_FloatRegister($dst$$reg); ++ ++ __ sqrt_d(dst, src); ++ %} ++ ins_pipe( fpu_regF_regF ); ++%} ++ ++instruct sqrtF_reg(regF dst, regF src) %{ ++ match(Set dst (ConvD2F (SqrtD (ConvF2D src)))); ++ ins_cost(100); ++ format %{ "SqrtF $dst, $src @sqrtF_reg" %} ++ ins_encode %{ ++ FloatRegister src = as_FloatRegister($src$$reg); ++ FloatRegister dst = as_FloatRegister($dst$$reg); ++ ++ __ sqrt_s(dst, src); ++ %} ++ ins_pipe( fpu_regF_regF ); ++%} ++//----------------------------------Logical Instructions---------------------- ++//__________________________________Integer Logical Instructions------------- ++ ++//And Instuctions ++// And Register with Immediate ++instruct andI_Reg_immI(mRegI dst, mRegI src1, immI src2) %{ ++ match(Set dst (AndI src1 src2)); ++ ++ format %{ "and $dst, $src1, $src2 #@andI_Reg_immI" %} ++ ins_encode %{ ++ Register dst = $dst$$Register; ++ Register src = $src1$$Register; ++ int val = $src2$$constant; ++ ++ __ move(AT, val); ++ __ andr(dst, src, AT); ++ %} ++ ins_pipe( ialu_regI_regI ); ++%} ++ ++instruct andI_Reg_imm_0_65535(mRegI dst, mRegI src1, immI_0_65535 src2) %{ ++ match(Set dst (AndI src1 src2)); ++ ins_cost(60); ++ ++ format %{ "and $dst, $src1, $src2 #@andI_Reg_imm_0_65535" %} ++ ins_encode %{ ++ Register dst = $dst$$Register; ++ Register src = $src1$$Register; ++ int val = $src2$$constant; ++ ++ __ andi(dst, src, val); ++ %} ++ ins_pipe( ialu_regI_regI ); ++%} ++ ++instruct andI_Reg_immI_nonneg_mask(mRegI dst, mRegI src1, immI_nonneg_mask mask) %{ ++ match(Set dst (AndI src1 mask)); ++ ins_cost(60); ++ ++ format %{ "and $dst, $src1, $mask #@andI_Reg_immI_nonneg_mask" %} ++ ins_encode %{ ++ Register dst = $dst$$Register; ++ Register src = $src1$$Register; ++ int size = Assembler::is_int_mask($mask$$constant); ++ ++ __ ext(dst, src, 0, size); ++ %} ++ ins_pipe( ialu_regI_regI ); ++%} ++ ++instruct andL_Reg_immL_nonneg_mask(mRegL dst, mRegL src1, immL_nonneg_mask mask) %{ ++ match(Set dst (AndL src1 mask)); ++ ins_cost(60); ++ ++ format %{ "and $dst, $src1, $mask #@andL_Reg_immL_nonneg_mask" %} ++ ins_encode %{ ++ Register dst = $dst$$Register; ++ Register src = $src1$$Register; ++ int size = Assembler::is_jlong_mask($mask$$constant); ++ ++ __ dext(dst, src, 0, size); ++ %} ++ ins_pipe( ialu_regI_regI ); ++%} ++ ++instruct xorI_Reg_imm_0_65535(mRegI dst, mRegI src1, immI_0_65535 src2) %{ ++ match(Set dst (XorI src1 src2)); ++ ins_cost(60); ++ ++ format %{ "xori $dst, $src1, $src2 #@xorI_Reg_imm_0_65535" %} ++ ins_encode %{ ++ Register dst = $dst$$Register; ++ Register src = $src1$$Register; ++ int val = $src2$$constant; ++ ++ __ xori(dst, src, val); ++ %} ++ ins_pipe( ialu_regI_regI ); ++%} ++ ++instruct xorI_Reg_immI_M1(mRegI dst, mRegI src1, immI_M1 M1) %{ ++ match(Set dst (XorI src1 M1)); ++ predicate(UseLEXT3); ++ ins_cost(60); ++ ++ format %{ "xor $dst, $src1, $M1 #@xorI_Reg_immI_M1" %} ++ ins_encode %{ ++ Register dst = $dst$$Register; ++ Register src = $src1$$Register; ++ ++ __ gsorn(dst, R0, src); ++ %} ++ ins_pipe( ialu_regI_regI ); ++%} ++ ++instruct xorL2I_Reg_immI_M1(mRegI dst, mRegL src1, immI_M1 M1) %{ ++ match(Set dst (XorI (ConvL2I src1) M1)); ++ predicate(UseLEXT3); ++ ins_cost(60); ++ ++ format %{ "xor $dst, $src1, $M1 #@xorL2I_Reg_immI_M1" %} ++ ins_encode %{ ++ Register dst = $dst$$Register; ++ Register src = $src1$$Register; ++ ++ __ gsorn(dst, R0, src); ++ %} ++ ins_pipe( ialu_regI_regI ); ++%} ++ ++instruct xorL_Reg_imm_0_65535(mRegL dst, mRegL src1, immL_0_65535 src2) %{ ++ match(Set dst (XorL src1 src2)); ++ ins_cost(60); ++ ++ format %{ "xori $dst, $src1, $src2 #@xorL_Reg_imm_0_65535" %} ++ ins_encode %{ ++ Register dst = $dst$$Register; ++ Register src = $src1$$Register; ++ int val = $src2$$constant; ++ ++ __ xori(dst, src, val); ++ %} ++ ins_pipe( ialu_regI_regI ); ++%} ++ ++/* ++instruct xorL_Reg_immL_M1(mRegL dst, mRegL src1, immL_M1 M1) %{ ++ match(Set dst (XorL src1 M1)); ++ predicate(UseLEXT3); ++ ins_cost(60); ++ ++ format %{ "xor $dst, $src1, $M1 #@xorL_Reg_immL_M1" %} ++ ins_encode %{ ++ Register dst = $dst$$Register; ++ Register src = $src1$$Register; ++ ++ __ gsorn(dst, R0, src); ++ %} ++ ins_pipe( ialu_regI_regI ); ++%} ++*/ ++ ++instruct lbu_and_lmask(mRegI dst, memory mem, immI_255 mask) %{ ++ match(Set dst (AndI mask (LoadB mem))); ++ ins_cost(60); ++ ++ format %{ "lhu $dst, $mem #@lbu_and_lmask" %} ++ ins_encode(load_UB_enc(dst, mem)); ++ ins_pipe( ialu_loadI ); ++%} ++ ++instruct lbu_and_rmask(mRegI dst, memory mem, immI_255 mask) %{ ++ match(Set dst (AndI (LoadB mem) mask)); ++ ins_cost(60); ++ ++ format %{ "lhu $dst, $mem #@lbu_and_rmask" %} ++ ins_encode(load_UB_enc(dst, mem)); ++ ins_pipe( ialu_loadI ); ++%} ++ ++instruct andI_Reg_Reg(mRegI dst, mRegI src1, mRegI src2) %{ ++ match(Set dst (AndI src1 src2)); ++ ++ format %{ "and $dst, $src1, $src2 #@andI_Reg_Reg" %} ++ ins_encode %{ ++ Register dst = $dst$$Register; ++ Register src1 = $src1$$Register; ++ Register src2 = $src2$$Register; ++ __ andr(dst, src1, src2); ++ %} ++ ins_pipe( ialu_regI_regI ); ++%} ++ ++instruct andnI_Reg_nReg(mRegI dst, mRegI src1, mRegI src2, immI_M1 M1) %{ ++ match(Set dst (AndI src1 (XorI src2 M1))); ++ predicate(UseLEXT3); ++ ++ format %{ "andn $dst, $src1, $src2 #@andnI_Reg_nReg" %} ++ ins_encode %{ ++ Register dst = $dst$$Register; ++ Register src1 = $src1$$Register; ++ Register src2 = $src2$$Register; ++ ++ __ gsandn(dst, src1, src2); ++ %} ++ ins_pipe( ialu_regI_regI ); ++%} ++ ++instruct ornI_Reg_nReg(mRegI dst, mRegI src1, mRegI src2, immI_M1 M1) %{ ++ match(Set dst (OrI src1 (XorI src2 M1))); ++ predicate(UseLEXT3); ++ ++ format %{ "orn $dst, $src1, $src2 #@ornI_Reg_nReg" %} ++ ins_encode %{ ++ Register dst = $dst$$Register; ++ Register src1 = $src1$$Register; ++ Register src2 = $src2$$Register; ++ ++ __ gsorn(dst, src1, src2); ++ %} ++ ins_pipe( ialu_regI_regI ); ++%} ++ ++instruct andnI_nReg_Reg(mRegI dst, mRegI src1, mRegI src2, immI_M1 M1) %{ ++ match(Set dst (AndI (XorI src1 M1) src2)); ++ predicate(UseLEXT3); ++ ++ format %{ "andn $dst, $src2, $src1 #@andnI_nReg_Reg" %} ++ ins_encode %{ ++ Register dst = $dst$$Register; ++ Register src1 = $src1$$Register; ++ Register src2 = $src2$$Register; ++ ++ __ gsandn(dst, src2, src1); ++ %} ++ ins_pipe( ialu_regI_regI ); ++%} ++ ++instruct ornI_nReg_Reg(mRegI dst, mRegI src1, mRegI src2, immI_M1 M1) %{ ++ match(Set dst (OrI (XorI src1 M1) src2)); ++ predicate(UseLEXT3); ++ ++ format %{ "orn $dst, $src2, $src1 #@ornI_nReg_Reg" %} ++ ins_encode %{ ++ Register dst = $dst$$Register; ++ Register src1 = $src1$$Register; ++ Register src2 = $src2$$Register; ++ ++ __ gsorn(dst, src2, src1); ++ %} ++ ins_pipe( ialu_regI_regI ); ++%} ++ ++// And Long Register with Register ++instruct andL_Reg_Reg(mRegL dst, mRegL src1, mRegL src2) %{ ++ match(Set dst (AndL src1 src2)); ++ format %{ "AND $dst, $src1, $src2 @ andL_Reg_Reg\n\t" %} ++ ins_encode %{ ++ Register dst_reg = as_Register($dst$$reg); ++ Register src1_reg = as_Register($src1$$reg); ++ Register src2_reg = as_Register($src2$$reg); ++ ++ __ andr(dst_reg, src1_reg, src2_reg); ++ %} ++ ins_pipe( ialu_regL_regL ); ++%} ++ ++instruct andL_Reg_Reg_convI2L(mRegL dst, mRegL src1, mRegI src2) %{ ++ match(Set dst (AndL src1 (ConvI2L src2))); ++ format %{ "AND $dst, $src1, $src2 @ andL_Reg_Reg_convI2L\n\t" %} ++ ins_encode %{ ++ Register dst_reg = as_Register($dst$$reg); ++ Register src1_reg = as_Register($src1$$reg); ++ Register src2_reg = as_Register($src2$$reg); ++ ++ __ andr(dst_reg, src1_reg, src2_reg); ++ %} ++ ins_pipe( ialu_regL_regL ); ++%} ++ ++instruct andL_Reg_imm_0_65535(mRegL dst, mRegL src1, immL_0_65535 src2) %{ ++ match(Set dst (AndL src1 src2)); ++ ins_cost(60); ++ ++ format %{ "and $dst, $src1, $src2 #@andL_Reg_imm_0_65535" %} ++ ins_encode %{ ++ Register dst = $dst$$Register; ++ Register src = $src1$$Register; ++ long val = $src2$$constant; ++ ++ __ andi(dst, src, val); ++ %} ++ ins_pipe( ialu_regI_regI ); ++%} ++ ++instruct andL2I_Reg_imm_0_65535(mRegI dst, mRegL src1, immL_0_65535 src2) %{ ++ match(Set dst (ConvL2I (AndL src1 src2))); ++ ins_cost(60); ++ ++ format %{ "and $dst, $src1, $src2 #@andL2I_Reg_imm_0_65535" %} ++ ins_encode %{ ++ Register dst = $dst$$Register; ++ Register src = $src1$$Register; ++ long val = $src2$$constant; ++ ++ __ andi(dst, src, val); ++ %} ++ ins_pipe( ialu_regI_regI ); ++%} ++ ++/* ++instruct andnL_Reg_nReg(mRegL dst, mRegL src1, mRegL src2, immL_M1 M1) %{ ++ match(Set dst (AndL src1 (XorL src2 M1))); ++ predicate(UseLEXT3); ++ ++ format %{ "andn $dst, $src1, $src2 #@andnL_Reg_nReg" %} ++ ins_encode %{ ++ Register dst = $dst$$Register; ++ Register src1 = $src1$$Register; ++ Register src2 = $src2$$Register; ++ ++ __ gsandn(dst, src1, src2); ++ %} ++ ins_pipe( ialu_regI_regI ); ++%} ++*/ ++ ++/* ++instruct ornL_Reg_nReg(mRegL dst, mRegL src1, mRegL src2, immL_M1 M1) %{ ++ match(Set dst (OrL src1 (XorL src2 M1))); ++ predicate(UseLEXT3); ++ ++ format %{ "orn $dst, $src1, $src2 #@ornL_Reg_nReg" %} ++ ins_encode %{ ++ Register dst = $dst$$Register; ++ Register src1 = $src1$$Register; ++ Register src2 = $src2$$Register; ++ ++ __ gsorn(dst, src1, src2); ++ %} ++ ins_pipe( ialu_regI_regI ); ++%} ++*/ ++ ++/* ++instruct andnL_nReg_Reg(mRegL dst, mRegL src1, mRegL src2, immL_M1 M1) %{ ++ match(Set dst (AndL (XorL src1 M1) src2)); ++ predicate(UseLEXT3); ++ ++ format %{ "andn $dst, $src2, $src1 #@andnL_nReg_Reg" %} ++ ins_encode %{ ++ Register dst = $dst$$Register; ++ Register src1 = $src1$$Register; ++ Register src2 = $src2$$Register; ++ ++ __ gsandn(dst, src2, src1); ++ %} ++ ins_pipe( ialu_regI_regI ); ++%} ++*/ ++ ++/* ++instruct ornL_nReg_Reg(mRegL dst, mRegL src1, mRegL src2, immL_M1 M1) %{ ++ match(Set dst (OrL (XorL src1 M1) src2)); ++ predicate(UseLEXT3); ++ ++ format %{ "orn $dst, $src2, $src1 #@ornL_nReg_Reg" %} ++ ins_encode %{ ++ Register dst = $dst$$Register; ++ Register src1 = $src1$$Register; ++ Register src2 = $src2$$Register; ++ ++ __ gsorn(dst, src2, src1); ++ %} ++ ins_pipe( ialu_regI_regI ); ++%} ++*/ ++ ++instruct andL_Reg_immL_M8(mRegL dst, immL_M8 M8) %{ ++ match(Set dst (AndL dst M8)); ++ ins_cost(60); ++ ++ format %{ "and $dst, $dst, $M8 #@andL_Reg_immL_M8" %} ++ ins_encode %{ ++ Register dst = $dst$$Register; ++ ++ __ dins(dst, R0, 0, 3); ++ %} ++ ins_pipe( ialu_regI_regI ); ++%} ++ ++instruct andL_Reg_immL_M5(mRegL dst, immL_M5 M5) %{ ++ match(Set dst (AndL dst M5)); ++ ins_cost(60); ++ ++ format %{ "and $dst, $dst, $M5 #@andL_Reg_immL_M5" %} ++ ins_encode %{ ++ Register dst = $dst$$Register; ++ ++ __ dins(dst, R0, 2, 1); ++ %} ++ ins_pipe( ialu_regI_regI ); ++%} ++ ++instruct andL_Reg_immL_M7(mRegL dst, immL_M7 M7) %{ ++ match(Set dst (AndL dst M7)); ++ ins_cost(60); ++ ++ format %{ "and $dst, $dst, $M7 #@andL_Reg_immL_M7" %} ++ ins_encode %{ ++ Register dst = $dst$$Register; ++ ++ __ dins(dst, R0, 1, 2); ++ %} ++ ins_pipe( ialu_regI_regI ); ++%} ++ ++instruct andL_Reg_immL_M4(mRegL dst, immL_M4 M4) %{ ++ match(Set dst (AndL dst M4)); ++ ins_cost(60); ++ ++ format %{ "and $dst, $dst, $M4 #@andL_Reg_immL_M4" %} ++ ins_encode %{ ++ Register dst = $dst$$Register; ++ ++ __ dins(dst, R0, 0, 2); ++ %} ++ ins_pipe( ialu_regI_regI ); ++%} ++ ++instruct andL_Reg_immL_M121(mRegL dst, immL_M121 M121) %{ ++ match(Set dst (AndL dst M121)); ++ ins_cost(60); ++ ++ format %{ "and $dst, $dst, $M121 #@andL_Reg_immL_M121" %} ++ ins_encode %{ ++ Register dst = $dst$$Register; ++ ++ __ dins(dst, R0, 3, 4); ++ %} ++ ins_pipe( ialu_regI_regI ); ++%} ++ ++// Or Long Register with Register ++instruct orL_Reg_Reg(mRegL dst, mRegL src1, mRegL src2) %{ ++ match(Set dst (OrL src1 src2)); ++ format %{ "OR $dst, $src1, $src2 @ orL_Reg_Reg\t" %} ++ ins_encode %{ ++ Register dst_reg = $dst$$Register; ++ Register src1_reg = $src1$$Register; ++ Register src2_reg = $src2$$Register; ++ ++ __ orr(dst_reg, src1_reg, src2_reg); ++ %} ++ ins_pipe( ialu_regL_regL ); ++%} ++ ++instruct orL_Reg_P2XReg(mRegL dst, mRegP src1, mRegL src2) %{ ++ match(Set dst (OrL (CastP2X src1) src2)); ++ format %{ "OR $dst, $src1, $src2 @ orL_Reg_P2XReg\t" %} ++ ins_encode %{ ++ Register dst_reg = $dst$$Register; ++ Register src1_reg = $src1$$Register; ++ Register src2_reg = $src2$$Register; ++ ++ __ orr(dst_reg, src1_reg, src2_reg); ++ %} ++ ins_pipe( ialu_regL_regL ); ++%} ++ ++// Xor Long Register with Register ++instruct xorL_Reg_Reg(mRegL dst, mRegL src1, mRegL src2) %{ ++ match(Set dst (XorL src1 src2)); ++ format %{ "XOR $dst, $src1, $src2 @ xorL_Reg_Reg\t" %} ++ ins_encode %{ ++ Register dst_reg = as_Register($dst$$reg); ++ Register src1_reg = as_Register($src1$$reg); ++ Register src2_reg = as_Register($src2$$reg); ++ ++ __ xorr(dst_reg, src1_reg, src2_reg); ++ %} ++ ins_pipe( ialu_regL_regL ); ++%} ++ ++// Shift Left by 8-bit immediate ++instruct salI_Reg_imm(mRegI dst, mRegI src, immI8 shift) %{ ++ match(Set dst (LShiftI src shift)); ++ ++ format %{ "SHL $dst, $src, $shift #@salI_Reg_imm" %} ++ ins_encode %{ ++ Register src = $src$$Register; ++ Register dst = $dst$$Register; ++ int shamt = $shift$$constant; ++ ++ __ sll(dst, src, shamt); ++ %} ++ ins_pipe( ialu_regI_regI ); ++%} ++ ++instruct salL2I_Reg_imm(mRegI dst, mRegL src, immI8 shift) %{ ++ match(Set dst (LShiftI (ConvL2I src) shift)); ++ ++ format %{ "SHL $dst, $src, $shift #@salL2I_Reg_imm" %} ++ ins_encode %{ ++ Register src = $src$$Register; ++ Register dst = $dst$$Register; ++ int shamt = $shift$$constant; ++ ++ __ sll(dst, src, shamt); ++ %} ++ ins_pipe( ialu_regI_regI ); ++%} ++ ++instruct salI_Reg_imm_and_M65536(mRegI dst, mRegI src, immI_16 shift, immI_M65536 mask) %{ ++ match(Set dst (AndI (LShiftI src shift) mask)); ++ ++ format %{ "SHL $dst, $src, $shift #@salI_Reg_imm_and_M65536" %} ++ ins_encode %{ ++ Register src = $src$$Register; ++ Register dst = $dst$$Register; ++ ++ __ sll(dst, src, 16); ++ %} ++ ins_pipe( ialu_regI_regI ); ++%} ++ ++instruct land7_2_s(mRegI dst, mRegL src, immL_7 seven, immI_16 sixteen) ++%{ ++ match(Set dst (RShiftI (LShiftI (ConvL2I (AndL src seven)) sixteen) sixteen)); ++ ++ format %{ "andi $dst, $src, 7\t# @land7_2_s" %} ++ ins_encode %{ ++ Register src = $src$$Register; ++ Register dst = $dst$$Register; ++ ++ __ andi(dst, src, 7); ++ %} ++ ins_pipe(ialu_regI_regI); ++%} ++ ++// Logical Shift Right by 16, followed by Arithmetic Shift Left by 16. ++// This idiom is used by the compiler the i2s bytecode. ++instruct i2s(mRegI dst, mRegI src, immI_16 sixteen) ++%{ ++ match(Set dst (RShiftI (LShiftI src sixteen) sixteen)); ++ ++ format %{ "i2s $dst, $src\t# @i2s" %} ++ ins_encode %{ ++ Register src = $src$$Register; ++ Register dst = $dst$$Register; ++ ++ __ seh(dst, src); ++ %} ++ ins_pipe(ialu_regI_regI); ++%} ++ ++// Logical Shift Right by 24, followed by Arithmetic Shift Left by 24. ++// This idiom is used by the compiler for the i2b bytecode. ++instruct i2b(mRegI dst, mRegI src, immI_24 twentyfour) ++%{ ++ match(Set dst (RShiftI (LShiftI src twentyfour) twentyfour)); ++ ++ format %{ "i2b $dst, $src\t# @i2b" %} ++ ins_encode %{ ++ Register src = $src$$Register; ++ Register dst = $dst$$Register; ++ ++ __ seb(dst, src); ++ %} ++ ins_pipe(ialu_regI_regI); ++%} ++ ++ ++instruct salI_RegL2I_imm(mRegI dst, mRegL src, immI8 shift) %{ ++ match(Set dst (LShiftI (ConvL2I src) shift)); ++ ++ format %{ "SHL $dst, $src, $shift #@salI_RegL2I_imm" %} ++ ins_encode %{ ++ Register src = $src$$Register; ++ Register dst = $dst$$Register; ++ int shamt = $shift$$constant; ++ ++ __ sll(dst, src, shamt); ++ %} ++ ins_pipe( ialu_regI_regI ); ++%} ++ ++// Shift Left by 8-bit immediate ++instruct salI_Reg_Reg(mRegI dst, mRegI src, mRegI shift) %{ ++ match(Set dst (LShiftI src shift)); ++ ++ format %{ "SHL $dst, $src, $shift #@salI_Reg_Reg" %} ++ ins_encode %{ ++ Register src = $src$$Register; ++ Register dst = $dst$$Register; ++ Register shamt = $shift$$Register; ++ __ sllv(dst, src, shamt); ++ %} ++ ins_pipe( ialu_regI_regI ); ++%} ++ ++ ++// Shift Left Long ++instruct salL_Reg_imm(mRegL dst, mRegL src, immI8 shift) %{ ++ match(Set dst (LShiftL src shift)); ++ ins_cost(100); ++ format %{ "salL $dst, $src, $shift @ salL_Reg_imm" %} ++ ins_encode %{ ++ Register src_reg = as_Register($src$$reg); ++ Register dst_reg = as_Register($dst$$reg); ++ int shamt = $shift$$constant; ++ ++ if (__ is_simm(shamt, 5)) ++ __ dsll(dst_reg, src_reg, shamt); ++ else { ++ int sa = Assembler::low(shamt, 6); ++ if (sa < 32) { ++ __ dsll(dst_reg, src_reg, sa); ++ } else { ++ __ dsll32(dst_reg, src_reg, sa - 32); ++ } ++ } ++ %} ++ ins_pipe( ialu_regL_regL ); ++%} ++ ++instruct salL_RegI2L_imm(mRegL dst, mRegI src, immI8 shift) %{ ++ match(Set dst (LShiftL (ConvI2L src) shift)); ++ ins_cost(100); ++ format %{ "salL $dst, $src, $shift @ salL_RegI2L_imm" %} ++ ins_encode %{ ++ Register src_reg = as_Register($src$$reg); ++ Register dst_reg = as_Register($dst$$reg); ++ int shamt = $shift$$constant; ++ ++ if (__ is_simm(shamt, 5)) ++ __ dsll(dst_reg, src_reg, shamt); ++ else { ++ int sa = Assembler::low(shamt, 6); ++ if (sa < 32) { ++ __ dsll(dst_reg, src_reg, sa); ++ } else { ++ __ dsll32(dst_reg, src_reg, sa - 32); ++ } ++ } ++ %} ++ ins_pipe( ialu_regL_regL ); ++%} ++ ++// Shift Left Long ++instruct salL_Reg_Reg(mRegL dst, mRegL src, mRegI shift) %{ ++ match(Set dst (LShiftL src shift)); ++ ins_cost(100); ++ format %{ "salL $dst, $src, $shift @ salL_Reg_Reg" %} ++ ins_encode %{ ++ Register src_reg = as_Register($src$$reg); ++ Register dst_reg = as_Register($dst$$reg); ++ ++ __ dsllv(dst_reg, src_reg, $shift$$Register); ++ %} ++ ins_pipe( ialu_regL_regL ); ++%} ++ ++// Shift Right Long ++instruct sarL_Reg_imm(mRegL dst, mRegL src, immI8 shift) %{ ++ match(Set dst (RShiftL src shift)); ++ ins_cost(100); ++ format %{ "sarL $dst, $src, $shift @ sarL_Reg_imm" %} ++ ins_encode %{ ++ Register src_reg = as_Register($src$$reg); ++ Register dst_reg = as_Register($dst$$reg); ++ int shamt = ($shift$$constant & 0x3f); ++ if (__ is_simm(shamt, 5)) ++ __ dsra(dst_reg, src_reg, shamt); ++ else { ++ int sa = Assembler::low(shamt, 6); ++ if (sa < 32) { ++ __ dsra(dst_reg, src_reg, sa); ++ } else { ++ __ dsra32(dst_reg, src_reg, sa - 32); ++ } ++ } ++ %} ++ ins_pipe( ialu_regL_regL ); ++%} ++ ++instruct sarL2I_Reg_immI_32_63(mRegI dst, mRegL src, immI_32_63 shift) %{ ++ match(Set dst (ConvL2I (RShiftL src shift))); ++ ins_cost(100); ++ format %{ "sarL $dst, $src, $shift @ sarL2I_Reg_immI_32_63" %} ++ ins_encode %{ ++ Register src_reg = as_Register($src$$reg); ++ Register dst_reg = as_Register($dst$$reg); ++ int shamt = $shift$$constant; ++ ++ __ dsra32(dst_reg, src_reg, shamt - 32); ++ %} ++ ins_pipe( ialu_regL_regL ); ++%} ++ ++// Shift Right Long arithmetically ++instruct sarL_Reg_Reg(mRegL dst, mRegL src, mRegI shift) %{ ++ match(Set dst (RShiftL src shift)); ++ ins_cost(100); ++ format %{ "sarL $dst, $src, $shift @ sarL_Reg_Reg" %} ++ ins_encode %{ ++ Register src_reg = as_Register($src$$reg); ++ Register dst_reg = as_Register($dst$$reg); ++ ++ __ dsrav(dst_reg, src_reg, $shift$$Register); ++ %} ++ ins_pipe( ialu_regL_regL ); ++%} ++ ++// Shift Right Long logically ++instruct slrL_Reg_Reg(mRegL dst, mRegL src, mRegI shift) %{ ++ match(Set dst (URShiftL src shift)); ++ ins_cost(100); ++ format %{ "slrL $dst, $src, $shift @ slrL_Reg_Reg" %} ++ ins_encode %{ ++ Register src_reg = as_Register($src$$reg); ++ Register dst_reg = as_Register($dst$$reg); ++ ++ __ dsrlv(dst_reg, src_reg, $shift$$Register); ++ %} ++ ins_pipe( ialu_regL_regL ); ++%} ++ ++instruct slrL_Reg_immI_0_31(mRegL dst, mRegL src, immI_0_31 shift) %{ ++ match(Set dst (URShiftL src shift)); ++ ins_cost(80); ++ format %{ "slrL $dst, $src, $shift @ slrL_Reg_immI_0_31" %} ++ ins_encode %{ ++ Register src_reg = as_Register($src$$reg); ++ Register dst_reg = as_Register($dst$$reg); ++ int shamt = $shift$$constant; ++ ++ __ dsrl(dst_reg, src_reg, shamt); ++ %} ++ ins_pipe( ialu_regL_regL ); ++%} ++ ++instruct slrL_Reg_immI_0_31_and_max_int(mRegI dst, mRegL src, immI_0_31 shift, immI_MaxI max_int) %{ ++ match(Set dst (AndI (ConvL2I (URShiftL src shift)) max_int)); ++ ins_cost(80); ++ format %{ "dext $dst, $src, $shift, 31 @ slrL_Reg_immI_0_31_and_max_int" %} ++ ins_encode %{ ++ Register src_reg = as_Register($src$$reg); ++ Register dst_reg = as_Register($dst$$reg); ++ int shamt = $shift$$constant; ++ ++ __ dext(dst_reg, src_reg, shamt, 31); ++ %} ++ ins_pipe( ialu_regL_regL ); ++%} ++ ++instruct slrL_P2XReg_immI_0_31(mRegL dst, mRegP src, immI_0_31 shift) %{ ++ match(Set dst (URShiftL (CastP2X src) shift)); ++ ins_cost(80); ++ format %{ "slrL $dst, $src, $shift @ slrL_P2XReg_immI_0_31" %} ++ ins_encode %{ ++ Register src_reg = as_Register($src$$reg); ++ Register dst_reg = as_Register($dst$$reg); ++ int shamt = $shift$$constant; ++ ++ __ dsrl(dst_reg, src_reg, shamt); ++ %} ++ ins_pipe( ialu_regL_regL ); ++%} ++ ++instruct slrL_Reg_immI_32_63(mRegL dst, mRegL src, immI_32_63 shift) %{ ++ match(Set dst (URShiftL src shift)); ++ ins_cost(80); ++ format %{ "slrL $dst, $src, $shift @ slrL_Reg_immI_32_63" %} ++ ins_encode %{ ++ Register src_reg = as_Register($src$$reg); ++ Register dst_reg = as_Register($dst$$reg); ++ int shamt = $shift$$constant; ++ ++ __ dsrl32(dst_reg, src_reg, shamt - 32); ++ %} ++ ins_pipe( ialu_regL_regL ); ++%} ++ ++instruct slrL_Reg_immI_convL2I(mRegI dst, mRegL src, immI_32_63 shift) %{ ++ match(Set dst (ConvL2I (URShiftL src shift))); ++ predicate(n->in(1)->in(2)->get_int() > 32); ++ ins_cost(80); ++ format %{ "slrL $dst, $src, $shift @ slrL_Reg_immI_convL2I" %} ++ ins_encode %{ ++ Register src_reg = as_Register($src$$reg); ++ Register dst_reg = as_Register($dst$$reg); ++ int shamt = $shift$$constant; ++ ++ __ dsrl32(dst_reg, src_reg, shamt - 32); ++ %} ++ ins_pipe( ialu_regL_regL ); ++%} ++ ++instruct slrL_P2XReg_immI_32_63(mRegL dst, mRegP src, immI_32_63 shift) %{ ++ match(Set dst (URShiftL (CastP2X src) shift)); ++ ins_cost(80); ++ format %{ "slrL $dst, $src, $shift @ slrL_P2XReg_immI_32_63" %} ++ ins_encode %{ ++ Register src_reg = as_Register($src$$reg); ++ Register dst_reg = as_Register($dst$$reg); ++ int shamt = $shift$$constant; ++ ++ __ dsrl32(dst_reg, src_reg, shamt - 32); ++ %} ++ ins_pipe( ialu_regL_regL ); ++%} ++ ++// Xor Instructions ++// Xor Register with Register ++instruct xorI_Reg_Reg(mRegI dst, mRegI src1, mRegI src2) %{ ++ match(Set dst (XorI src1 src2)); ++ ++ format %{ "XOR $dst, $src1, $src2 #@xorI_Reg_Reg" %} ++ ++ ins_encode %{ ++ Register dst = $dst$$Register; ++ Register src1 = $src1$$Register; ++ Register src2 = $src2$$Register; ++ __ xorr(dst, src1, src2); ++ %} ++ ++ ins_pipe( ialu_regI_regI ); ++%} ++ ++// Or Instructions ++instruct orI_Reg_imm(mRegI dst, mRegI src1, immI_0_32767 src2) %{ ++ match(Set dst (OrI src1 src2)); ++ ++ format %{ "OR $dst, $src1, $src2 #@orI_Reg_imm" %} ++ ins_encode %{ ++ __ ori($dst$$Register, $src1$$Register, $src2$$constant); ++ %} ++ ++ ins_pipe( ialu_regI_regI ); ++%} ++// Or Register with Register ++instruct orI_Reg_Reg(mRegI dst, mRegI src1, mRegI src2) %{ ++ match(Set dst (OrI src1 src2)); ++ ++ format %{ "OR $dst, $src1, $src2 #@orI_Reg_Reg" %} ++ ins_encode %{ ++ Register dst = $dst$$Register; ++ Register src1 = $src1$$Register; ++ Register src2 = $src2$$Register; ++ __ orr(dst, src1, src2); ++ %} ++ ++ ins_pipe( ialu_regI_regI ); ++%} ++ ++instruct rotI_shr_logical_Reg(mRegI dst, mRegI src, immI_0_31 rshift, immI_0_31 lshift, immI_1 one) %{ ++ match(Set dst (OrI (URShiftI src rshift) (LShiftI (AndI src one) lshift))); ++ predicate(32 == ((n->in(1)->in(2)->get_int() + n->in(2)->in(2)->get_int()))); ++ ++ format %{ "rotr $dst, $src, 1 ...\n\t" ++ "srl $dst, $dst, ($rshift-1) @ rotI_shr_logical_Reg" %} ++ ins_encode %{ ++ Register dst = $dst$$Register; ++ Register src = $src$$Register; ++ int rshift = $rshift$$constant; ++ ++ __ rotr(dst, src, 1); ++ if (rshift - 1) { ++ __ srl(dst, dst, rshift - 1); ++ } ++ %} ++ ++ ins_pipe( ialu_regI_regI ); ++%} ++ ++instruct orI_Reg_castP2X(mRegL dst, mRegL src1, mRegP src2) %{ ++ match(Set dst (OrI src1 (CastP2X src2))); ++ ++ format %{ "OR $dst, $src1, $src2 #@orI_Reg_castP2X" %} ++ ins_encode %{ ++ Register dst = $dst$$Register; ++ Register src1 = $src1$$Register; ++ Register src2 = $src2$$Register; ++ __ orr(dst, src1, src2); ++ %} ++ ++ ins_pipe( ialu_regI_regI ); ++%} ++ ++// Logical Shift Right by 8-bit immediate ++instruct shr_logical_Reg_imm(mRegI dst, mRegI src, immI8 shift) %{ ++ match(Set dst (URShiftI src shift)); ++ //effect(KILL cr); ++ ++ format %{ "SRL $dst, $src, $shift #@shr_logical_Reg_imm" %} ++ ins_encode %{ ++ Register src = $src$$Register; ++ Register dst = $dst$$Register; ++ int shift = $shift$$constant; ++ ++ __ srl(dst, src, shift); ++ %} ++ ins_pipe( ialu_regI_regI ); ++%} ++ ++instruct shr_logical_Reg_imm_nonneg_mask(mRegI dst, mRegI src, immI_0_31 shift, immI_nonneg_mask mask) %{ ++ match(Set dst (AndI (URShiftI src shift) mask)); ++ ++ format %{ "ext $dst, $src, $shift, one-bits($mask) #@shr_logical_Reg_imm_nonneg_mask" %} ++ ins_encode %{ ++ Register src = $src$$Register; ++ Register dst = $dst$$Register; ++ int pos = $shift$$constant; ++ int size = Assembler::is_int_mask($mask$$constant); ++ ++ __ ext(dst, src, pos, size); ++ %} ++ ins_pipe( ialu_regI_regI ); ++%} ++ ++instruct rolI_Reg_immI_0_31(mRegI dst, immI_0_31 lshift, immI_0_31 rshift) ++%{ ++ predicate(0 == ((n->in(1)->in(2)->get_int() + n->in(2)->in(2)->get_int()) & 0x1f)); ++ match(Set dst (OrI (LShiftI dst lshift) (URShiftI dst rshift))); ++ ++ ins_cost(100); ++ format %{ "rotr $dst, $dst, $rshift #@rolI_Reg_immI_0_31" %} ++ ins_encode %{ ++ Register dst = $dst$$Register; ++ int sa = $rshift$$constant; ++ ++ __ rotr(dst, dst, sa); ++ %} ++ ins_pipe( ialu_regI_regI ); ++%} ++ ++instruct rolL_Reg_immI_0_31(mRegL dst, mRegL src, immI_32_63 lshift, immI_0_31 rshift) ++%{ ++ predicate(0 == ((n->in(1)->in(2)->get_int() + n->in(2)->in(2)->get_int()) & 0x3f)); ++ match(Set dst (OrL (LShiftL src lshift) (URShiftL src rshift))); ++ ++ ins_cost(100); ++ format %{ "rotr $dst, $src, $rshift #@rolL_Reg_immI_0_31" %} ++ ins_encode %{ ++ Register dst = $dst$$Register; ++ Register src = $src$$Register; ++ int sa = $rshift$$constant; ++ ++ __ drotr(dst, src, sa); ++ %} ++ ins_pipe( ialu_regI_regI ); ++%} ++ ++instruct rolL_Reg_immI_32_63(mRegL dst, mRegL src, immI_0_31 lshift, immI_32_63 rshift) ++%{ ++ predicate(0 == ((n->in(1)->in(2)->get_int() + n->in(2)->in(2)->get_int()) & 0x3f)); ++ match(Set dst (OrL (LShiftL src lshift) (URShiftL src rshift))); ++ ++ ins_cost(100); ++ format %{ "rotr $dst, $src, $rshift #@rolL_Reg_immI_32_63" %} ++ ins_encode %{ ++ Register dst = $dst$$Register; ++ Register src = $src$$Register; ++ int sa = $rshift$$constant; ++ ++ __ drotr32(dst, src, sa - 32); ++ %} ++ ins_pipe( ialu_regI_regI ); ++%} ++ ++instruct rorI_Reg_immI_0_31(mRegI dst, mRegI src, immI_0_31 rshift, immI_0_31 lshift) ++%{ ++ predicate(0 == ((n->in(1)->in(2)->get_int() + n->in(2)->in(2)->get_int()) & 0x1f)); ++ match(Set dst (OrI (URShiftI src rshift) (LShiftI src lshift))); ++ ++ ins_cost(100); ++ format %{ "rotr $dst, $src, $rshift #@rorI_Reg_immI_0_31" %} ++ ins_encode %{ ++ Register dst = $dst$$Register; ++ Register src = $src$$Register; ++ int sa = $rshift$$constant; ++ ++ __ rotr(dst, src, sa); ++ %} ++ ins_pipe( ialu_regI_regI ); ++%} ++ ++instruct rorL_Reg_immI_0_31(mRegL dst, mRegL src, immI_0_31 rshift, immI_32_63 lshift) ++%{ ++ predicate(0 == ((n->in(1)->in(2)->get_int() + n->in(2)->in(2)->get_int()) & 0x3f)); ++ match(Set dst (OrL (URShiftL src rshift) (LShiftL src lshift))); ++ ++ ins_cost(100); ++ format %{ "rotr $dst, $src, $rshift #@rorL_Reg_immI_0_31" %} ++ ins_encode %{ ++ Register dst = $dst$$Register; ++ Register src = $src$$Register; ++ int sa = $rshift$$constant; ++ ++ __ drotr(dst, src, sa); ++ %} ++ ins_pipe( ialu_regI_regI ); ++%} ++ ++instruct rorL_Reg_immI_32_63(mRegL dst, mRegL src, immI_32_63 rshift, immI_0_31 lshift) ++%{ ++ predicate(0 == ((n->in(1)->in(2)->get_int() + n->in(2)->in(2)->get_int()) & 0x3f)); ++ match(Set dst (OrL (URShiftL src rshift) (LShiftL src lshift))); ++ ++ ins_cost(100); ++ format %{ "rotr $dst, $src, $rshift #@rorL_Reg_immI_32_63" %} ++ ins_encode %{ ++ Register dst = $dst$$Register; ++ Register src = $src$$Register; ++ int sa = $rshift$$constant; ++ ++ __ drotr32(dst, src, sa - 32); ++ %} ++ ins_pipe( ialu_regI_regI ); ++%} ++ ++// Logical Shift Right ++instruct shr_logical_Reg_Reg(mRegI dst, mRegI src, mRegI shift) %{ ++ match(Set dst (URShiftI src shift)); ++ ++ format %{ "SRL $dst, $src, $shift #@shr_logical_Reg_Reg" %} ++ ins_encode %{ ++ Register src = $src$$Register; ++ Register dst = $dst$$Register; ++ Register shift = $shift$$Register; ++ __ srlv(dst, src, shift); ++ %} ++ ins_pipe( ialu_regI_regI ); ++%} ++ ++ ++instruct shr_arith_Reg_imm(mRegI dst, mRegI src, immI8 shift) %{ ++ match(Set dst (RShiftI src shift)); ++ // effect(KILL cr); ++ ++ format %{ "SRA $dst, $src, $shift #@shr_arith_Reg_imm" %} ++ ins_encode %{ ++ Register src = $src$$Register; ++ Register dst = $dst$$Register; ++ int shift = $shift$$constant; ++ __ sra(dst, src, shift); ++ %} ++ ins_pipe( ialu_regI_regI ); ++%} ++ ++instruct shr_arith_Reg_Reg(mRegI dst, mRegI src, mRegI shift) %{ ++ match(Set dst (RShiftI src shift)); ++ // effect(KILL cr); ++ ++ format %{ "SRA $dst, $src, $shift #@shr_arith_Reg_Reg" %} ++ ins_encode %{ ++ Register src = $src$$Register; ++ Register dst = $dst$$Register; ++ Register shift = $shift$$Register; ++ __ srav(dst, src, shift); ++ %} ++ ins_pipe( ialu_regI_regI ); ++%} ++ ++//----------Convert Int to Boolean--------------------------------------------- ++ ++instruct convI2B(mRegI dst, mRegI src) %{ ++ match(Set dst (Conv2B src)); ++ ++ ins_cost(100); ++ format %{ "convI2B $dst, $src @ convI2B" %} ++ ins_encode %{ ++ Register dst = as_Register($dst$$reg); ++ Register src = as_Register($src$$reg); ++ ++ if (dst != src) { ++ __ daddiu(dst, R0, 1); ++ __ movz(dst, R0, src); ++ } else { ++ __ move(AT, src); ++ __ daddiu(dst, R0, 1); ++ __ movz(dst, R0, AT); ++ } ++ %} ++ ++ ins_pipe( ialu_regL_regL ); ++%} ++ ++instruct convI2L_reg( mRegL dst, mRegI src) %{ ++ match(Set dst (ConvI2L src)); ++ ++ ins_cost(100); ++ format %{ "SLL $dst, $src @ convI2L_reg\t" %} ++ ins_encode %{ ++ Register dst = as_Register($dst$$reg); ++ Register src = as_Register($src$$reg); ++ ++ if(dst != src) __ sll(dst, src, 0); ++ %} ++ ins_pipe( ialu_regL_regL ); ++%} ++ ++ ++instruct convL2I_reg( mRegI dst, mRegL src ) %{ ++ match(Set dst (ConvL2I src)); ++ ++ format %{ "MOV $dst, $src @ convL2I_reg" %} ++ ins_encode %{ ++ Register dst = as_Register($dst$$reg); ++ Register src = as_Register($src$$reg); ++ ++ __ sll(dst, src, 0); ++ %} ++ ++ ins_pipe( ialu_regI_regI ); ++%} ++ ++instruct convL2I2L_reg( mRegL dst, mRegL src ) %{ ++ match(Set dst (ConvI2L (ConvL2I src))); ++ ++ format %{ "sll $dst, $src, 0 @ convL2I2L_reg" %} ++ ins_encode %{ ++ Register dst = as_Register($dst$$reg); ++ Register src = as_Register($src$$reg); ++ ++ __ sll(dst, src, 0); ++ %} ++ ++ ins_pipe( ialu_regI_regI ); ++%} ++ ++instruct convL2D_reg( regD dst, mRegL src ) %{ ++ match(Set dst (ConvL2D src)); ++ format %{ "convL2D $dst, $src @ convL2D_reg" %} ++ ins_encode %{ ++ Register src = as_Register($src$$reg); ++ FloatRegister dst = as_FloatRegister($dst$$reg); ++ ++ __ dmtc1(src, dst); ++ __ cvt_d_l(dst, dst); ++ %} ++ ++ ins_pipe( pipe_slow ); ++%} ++ ++ ++instruct convD2L_reg_fast( mRegL dst, regD src ) %{ ++ match(Set dst (ConvD2L src)); ++ ins_cost(150); ++ format %{ "convD2L $dst, $src @ convD2L_reg_fast" %} ++ ins_encode %{ ++ Register dst = as_Register($dst$$reg); ++ FloatRegister src = as_FloatRegister($src$$reg); ++ ++ Label Done; ++ ++ __ trunc_l_d(F30, src); ++ // max_long: 0x7fffffffffffffff ++ // __ set64(AT, 0x7fffffffffffffff); ++ __ daddiu(AT, R0, -1); ++ __ dsrl(AT, AT, 1); ++ __ dmfc1(dst, F30); ++ ++ __ bne(dst, AT, Done); ++ __ delayed()->mtc1(R0, F30); ++ ++ __ cvt_d_w(F30, F30); ++ __ c_ult_d(src, F30); ++ __ bc1f(Done); ++ __ delayed()->daddiu(T9, R0, -1); ++ ++ __ c_un_d(src, src); //NaN? ++ __ subu(dst, T9, AT); ++ __ movt(dst, R0); ++ ++ __ bind(Done); ++ %} ++ ++ ins_pipe( pipe_slow ); ++%} ++ ++ ++instruct convD2L_reg_slow( mRegL dst, regD src ) %{ ++ match(Set dst (ConvD2L src)); ++ ins_cost(250); ++ format %{ "convD2L $dst, $src @ convD2L_reg_slow" %} ++ ins_encode %{ ++ Register dst = as_Register($dst$$reg); ++ FloatRegister src = as_FloatRegister($src$$reg); ++ ++ Label L; ++ ++ __ c_un_d(src, src); //NaN? ++ __ bc1t(L); ++ __ delayed(); ++ __ move(dst, R0); ++ ++ __ trunc_l_d(F30, src); ++ __ cfc1(AT, 31); ++ __ li(T9, 0x10000); ++ __ andr(AT, AT, T9); ++ __ beq(AT, R0, L); ++ __ delayed()->dmfc1(dst, F30); ++ ++ __ mov_d(F12, src); ++ __ call_VM_leaf(CAST_FROM_FN_PTR(address, SharedRuntime::d2l), 1); ++ __ move(dst, V0); ++ __ bind(L); ++ %} ++ ++ ins_pipe( pipe_slow ); ++%} ++ ++ ++instruct convF2I_reg_fast( mRegI dst, regF src ) %{ ++ match(Set dst (ConvF2I src)); ++ ins_cost(150); ++ format %{ "convf2i $dst, $src @ convF2I_reg_fast" %} ++ ins_encode %{ ++ Register dreg = $dst$$Register; ++ FloatRegister fval = $src$$FloatRegister; ++ Label L; ++ ++ __ trunc_w_s(F30, fval); ++ __ move(AT, 0x7fffffff); ++ __ mfc1(dreg, F30); ++ __ c_un_s(fval, fval); //NaN? ++ __ movt(dreg, R0); ++ ++ __ bne(AT, dreg, L); ++ __ delayed()->lui(T9, 0x8000); ++ ++ __ mfc1(AT, fval); ++ __ andr(AT, AT, T9); ++ ++ __ movn(dreg, T9, AT); ++ ++ __ bind(L); ++ ++ %} ++ ++ ins_pipe( pipe_slow ); ++%} ++ ++ ++ ++instruct convF2I_reg_slow( mRegI dst, regF src ) %{ ++ match(Set dst (ConvF2I src)); ++ ins_cost(250); ++ format %{ "convf2i $dst, $src @ convF2I_reg_slow" %} ++ ins_encode %{ ++ Register dreg = $dst$$Register; ++ FloatRegister fval = $src$$FloatRegister; ++ Label L; ++ ++ __ c_un_s(fval, fval); //NaN? ++ __ bc1t(L); ++ __ delayed(); ++ __ move(dreg, R0); ++ ++ __ trunc_w_s(F30, fval); ++ ++ /* Call SharedRuntime:f2i() to do valid convention */ ++ __ cfc1(AT, 31); ++ __ li(T9, 0x10000); ++ __ andr(AT, AT, T9); ++ __ beq(AT, R0, L); ++ __ delayed()->mfc1(dreg, F30); ++ ++ __ mov_s(F12, fval); ++ ++ //This bug was found when running ezDS's control-panel. ++ // J 982 C2 javax.swing.text.BoxView.layoutMajorAxis(II[I[I)V (283 bytes) @ 0x000000555c46aa74 ++ // ++ // An interger array index has been assigned to V0, and then changed from 1 to Integer.MAX_VALUE. ++ // V0 is corrupted during call_VM_leaf(), and should be preserved. ++ // ++ __ push(fval); ++ if(dreg != V0) { ++ __ push(V0); ++ } ++ __ call_VM_leaf(CAST_FROM_FN_PTR(address, SharedRuntime::f2i), 1); ++ if(dreg != V0) { ++ __ move(dreg, V0); ++ __ pop(V0); ++ } ++ __ pop(fval); ++ __ bind(L); ++ %} ++ ++ ins_pipe( pipe_slow ); ++%} ++ ++ ++instruct convF2L_reg_fast( mRegL dst, regF src ) %{ ++ match(Set dst (ConvF2L src)); ++ ins_cost(150); ++ format %{ "convf2l $dst, $src @ convF2L_reg_fast" %} ++ ins_encode %{ ++ Register dreg = $dst$$Register; ++ FloatRegister fval = $src$$FloatRegister; ++ Label L; ++ ++ __ trunc_l_s(F30, fval); ++ __ daddiu(AT, R0, -1); ++ __ dsrl(AT, AT, 1); ++ __ dmfc1(dreg, F30); ++ __ c_un_s(fval, fval); //NaN? ++ __ movt(dreg, R0); ++ ++ __ bne(AT, dreg, L); ++ __ delayed()->lui(T9, 0x8000); ++ ++ __ mfc1(AT, fval); ++ __ andr(AT, AT, T9); ++ ++ __ dsll32(T9, T9, 0); ++ __ movn(dreg, T9, AT); ++ ++ __ bind(L); ++ %} ++ ++ ins_pipe( pipe_slow ); ++%} ++ ++ ++instruct convF2L_reg_slow( mRegL dst, regF src ) %{ ++ match(Set dst (ConvF2L src)); ++ ins_cost(250); ++ format %{ "convf2l $dst, $src @ convF2L_reg_slow" %} ++ ins_encode %{ ++ Register dst = as_Register($dst$$reg); ++ FloatRegister fval = $src$$FloatRegister; ++ Label L; ++ ++ __ c_un_s(fval, fval); //NaN? ++ __ bc1t(L); ++ __ delayed(); ++ __ move(dst, R0); ++ ++ __ trunc_l_s(F30, fval); ++ __ cfc1(AT, 31); ++ __ li(T9, 0x10000); ++ __ andr(AT, AT, T9); ++ __ beq(AT, R0, L); ++ __ delayed()->dmfc1(dst, F30); ++ ++ __ mov_s(F12, fval); ++ __ call_VM_leaf(CAST_FROM_FN_PTR(address, SharedRuntime::f2l), 1); ++ __ move(dst, V0); ++ __ bind(L); ++ %} ++ ++ ins_pipe( pipe_slow ); ++%} ++ ++instruct convL2F_reg( regF dst, mRegL src ) %{ ++ match(Set dst (ConvL2F src)); ++ format %{ "convl2f $dst, $src @ convL2F_reg" %} ++ ins_encode %{ ++ FloatRegister dst = $dst$$FloatRegister; ++ Register src = as_Register($src$$reg); ++ Label L; ++ ++ __ dmtc1(src, dst); ++ __ cvt_s_l(dst, dst); ++ %} ++ ++ ins_pipe( pipe_slow ); ++%} ++ ++instruct convI2F_reg( regF dst, mRegI src ) %{ ++ match(Set dst (ConvI2F src)); ++ format %{ "convi2f $dst, $src @ convI2F_reg" %} ++ ins_encode %{ ++ Register src = $src$$Register; ++ FloatRegister dst = $dst$$FloatRegister; ++ ++ __ mtc1(src, dst); ++ __ cvt_s_w(dst, dst); ++ %} ++ ++ ins_pipe( fpu_regF_regF ); ++%} ++ ++instruct cmpLTMask_immI_0( mRegI dst, mRegI p, immI_0 zero ) %{ ++ match(Set dst (CmpLTMask p zero)); ++ ins_cost(100); ++ ++ format %{ "sra $dst, $p, 31 @ cmpLTMask_immI_0" %} ++ ins_encode %{ ++ Register src = $p$$Register; ++ Register dst = $dst$$Register; ++ ++ __ sra(dst, src, 31); ++ %} ++ ins_pipe( pipe_slow ); ++%} ++ ++ ++instruct cmpLTMask( mRegI dst, mRegI p, mRegI q ) %{ ++ match(Set dst (CmpLTMask p q)); ++ ins_cost(400); ++ ++ format %{ "cmpLTMask $dst, $p, $q @ cmpLTMask" %} ++ ins_encode %{ ++ Register p = $p$$Register; ++ Register q = $q$$Register; ++ Register dst = $dst$$Register; ++ ++ __ slt(dst, p, q); ++ __ subu(dst, R0, dst); ++ %} ++ ins_pipe( pipe_slow ); ++%} ++ ++instruct convP2B(mRegI dst, mRegP src) %{ ++ match(Set dst (Conv2B src)); ++ ++ ins_cost(100); ++ format %{ "convP2B $dst, $src @ convP2B" %} ++ ins_encode %{ ++ Register dst = as_Register($dst$$reg); ++ Register src = as_Register($src$$reg); ++ ++ if (dst != src) { ++ __ daddiu(dst, R0, 1); ++ __ movz(dst, R0, src); ++ } else { ++ __ move(AT, src); ++ __ daddiu(dst, R0, 1); ++ __ movz(dst, R0, AT); ++ } ++ %} ++ ++ ins_pipe( ialu_regL_regL ); ++%} ++ ++ ++instruct convI2D_reg_reg(regD dst, mRegI src) %{ ++ match(Set dst (ConvI2D src)); ++ format %{ "conI2D $dst, $src @convI2D_reg" %} ++ ins_encode %{ ++ Register src = $src$$Register; ++ FloatRegister dst = $dst$$FloatRegister; ++ __ mtc1(src, dst); ++ __ cvt_d_w(dst, dst); ++ %} ++ ins_pipe( fpu_regF_regF ); ++%} ++ ++instruct convF2D_reg_reg(regD dst, regF src) %{ ++ match(Set dst (ConvF2D src)); ++ format %{ "convF2D $dst, $src\t# @convF2D_reg_reg" %} ++ ins_encode %{ ++ FloatRegister dst = $dst$$FloatRegister; ++ FloatRegister src = $src$$FloatRegister; ++ ++ __ cvt_d_s(dst, src); ++ %} ++ ins_pipe( fpu_regF_regF ); ++%} ++ ++instruct convD2F_reg_reg(regF dst, regD src) %{ ++ match(Set dst (ConvD2F src)); ++ format %{ "convD2F $dst, $src\t# @convD2F_reg_reg" %} ++ ins_encode %{ ++ FloatRegister dst = $dst$$FloatRegister; ++ FloatRegister src = $src$$FloatRegister; ++ ++ __ cvt_s_d(dst, src); ++ %} ++ ins_pipe( fpu_regF_regF ); ++%} ++ ++ ++// Convert a double to an int. If the double is a NAN, stuff a zero in instead. ++instruct convD2I_reg_reg_fast( mRegI dst, regD src ) %{ ++ match(Set dst (ConvD2I src)); ++ ++ ins_cost(150); ++ format %{ "convD2I $dst, $src\t# @ convD2I_reg_reg_fast" %} ++ ++ ins_encode %{ ++ FloatRegister src = $src$$FloatRegister; ++ Register dst = $dst$$Register; ++ ++ Label Done; ++ ++ __ trunc_w_d(F30, src); ++ // max_int: 2147483647 ++ __ move(AT, 0x7fffffff); ++ __ mfc1(dst, F30); ++ ++ __ bne(dst, AT, Done); ++ __ delayed()->mtc1(R0, F30); ++ ++ __ cvt_d_w(F30, F30); ++ __ c_ult_d(src, F30); ++ __ bc1f(Done); ++ __ delayed()->addiu(T9, R0, -1); ++ ++ __ c_un_d(src, src); //NaN? ++ __ subu32(dst, T9, AT); ++ __ movt(dst, R0); ++ ++ __ bind(Done); ++ %} ++ ins_pipe( pipe_slow ); ++%} ++ ++ ++instruct convD2I_reg_reg_slow( mRegI dst, regD src ) %{ ++ match(Set dst (ConvD2I src)); ++ ++ ins_cost(250); ++ format %{ "convD2I $dst, $src\t# @ convD2I_reg_reg_slow" %} ++ ++ ins_encode %{ ++ FloatRegister src = $src$$FloatRegister; ++ Register dst = $dst$$Register; ++ Label L; ++ ++ __ trunc_w_d(F30, src); ++ __ cfc1(AT, 31); ++ __ li(T9, 0x10000); ++ __ andr(AT, AT, T9); ++ __ beq(AT, R0, L); ++ __ delayed()->mfc1(dst, F30); ++ ++ __ mov_d(F12, src); ++ __ call_VM_leaf(CAST_FROM_FN_PTR(address, SharedRuntime::d2i), 1); ++ __ move(dst, V0); ++ __ bind(L); ++ ++ %} ++ ins_pipe( pipe_slow ); ++%} ++ ++// Convert oop pointer into compressed form ++instruct encodeHeapOop(mRegN dst, mRegP src) %{ ++ predicate(n->bottom_type()->make_ptr()->ptr() != TypePtr::NotNull); ++ match(Set dst (EncodeP src)); ++ format %{ "encode_heap_oop $dst,$src" %} ++ ins_encode %{ ++ Register src = $src$$Register; ++ Register dst = $dst$$Register; ++ ++ __ encode_heap_oop(dst, src); ++ %} ++ ins_pipe( ialu_regL_regL ); ++%} ++ ++instruct encodeHeapOop_not_null(mRegN dst, mRegP src) %{ ++ predicate(n->bottom_type()->make_ptr()->ptr() == TypePtr::NotNull); ++ match(Set dst (EncodeP src)); ++ format %{ "encode_heap_oop_not_null $dst,$src @ encodeHeapOop_not_null" %} ++ ins_encode %{ ++ __ encode_heap_oop_not_null($dst$$Register, $src$$Register); ++ %} ++ ins_pipe( ialu_regL_regL ); ++%} ++ ++instruct decodeHeapOop(mRegP dst, mRegN src) %{ ++ predicate(n->bottom_type()->is_ptr()->ptr() != TypePtr::NotNull && ++ n->bottom_type()->is_ptr()->ptr() != TypePtr::Constant); ++ match(Set dst (DecodeN src)); ++ format %{ "decode_heap_oop $dst,$src @ decodeHeapOop" %} ++ ins_encode %{ ++ Register s = $src$$Register; ++ Register d = $dst$$Register; ++ ++ __ decode_heap_oop(d, s); ++ %} ++ ins_pipe( ialu_regL_regL ); ++%} ++ ++instruct decodeHeapOop_not_null(mRegP dst, mRegN src) %{ ++ predicate(n->bottom_type()->is_ptr()->ptr() == TypePtr::NotNull || ++ n->bottom_type()->is_ptr()->ptr() == TypePtr::Constant); ++ match(Set dst (DecodeN src)); ++ format %{ "decode_heap_oop_not_null $dst,$src @ decodeHeapOop_not_null" %} ++ ins_encode %{ ++ Register s = $src$$Register; ++ Register d = $dst$$Register; ++ if (s != d) { ++ __ decode_heap_oop_not_null(d, s); ++ } else { ++ __ decode_heap_oop_not_null(d); ++ } ++ %} ++ ins_pipe( ialu_regL_regL ); ++%} ++ ++instruct encodeKlass_not_null(mRegN dst, mRegP src) %{ ++ match(Set dst (EncodePKlass src)); ++ format %{ "encode_heap_oop_not_null $dst,$src @ encodeKlass_not_null" %} ++ ins_encode %{ ++ __ encode_klass_not_null($dst$$Register, $src$$Register); ++ %} ++ ins_pipe( ialu_regL_regL ); ++%} ++ ++instruct decodeKlass_not_null(mRegP dst, mRegN src) %{ ++ match(Set dst (DecodeNKlass src)); ++ format %{ "decode_heap_klass_not_null $dst,$src" %} ++ ins_encode %{ ++ Register s = $src$$Register; ++ Register d = $dst$$Register; ++ if (s != d) { ++ __ decode_klass_not_null(d, s); ++ } else { ++ __ decode_klass_not_null(d); ++ } ++ %} ++ ins_pipe( ialu_regL_regL ); ++%} ++ ++//FIXME ++instruct tlsLoadP(mRegP dst) %{ ++ match(Set dst (ThreadLocal)); ++ ++ ins_cost(0); ++ format %{ " get_thread in $dst #@tlsLoadP" %} ++ ins_encode %{ ++ Register dst = $dst$$Register; ++#ifdef OPT_THREAD ++ __ move(dst, TREG); ++#else ++ __ get_thread(dst); ++#endif ++ %} ++ ++ ins_pipe( ialu_loadI ); ++%} ++ ++ ++instruct checkCastPP( mRegP dst ) %{ ++ match(Set dst (CheckCastPP dst)); ++ ++ format %{ "#checkcastPP of $dst (empty encoding) #@chekCastPP" %} ++ ins_encode( /*empty encoding*/ ); ++ ins_pipe( empty ); ++%} ++ ++instruct castPP(mRegP dst) ++%{ ++ match(Set dst (CastPP dst)); ++ ++ size(0); ++ format %{ "# castPP of $dst" %} ++ ins_encode(/* empty encoding */); ++ ins_pipe(empty); ++%} ++ ++instruct castII( mRegI dst ) %{ ++ match(Set dst (CastII dst)); ++ format %{ "#castII of $dst empty encoding" %} ++ ins_encode( /*empty encoding*/ ); ++ ins_cost(0); ++ ins_pipe( empty ); ++%} ++ ++// Return Instruction ++// Remove the return address & jump to it. ++instruct Ret() %{ ++ match(Return); ++ format %{ "RET #@Ret" %} ++ ++ ins_encode %{ ++ __ jr(RA); ++ __ delayed()->nop(); ++ %} ++ ++ ins_pipe( pipe_jump ); ++%} ++ ++/* ++// For Loongson CPUs, jr seems too slow, so this rule shouldn't be imported. ++instruct jumpXtnd(mRegL switch_val) %{ ++ match(Jump switch_val); ++ ++ ins_cost(350); ++ ++ format %{ "load T9 <-- [$constanttablebase, $switch_val, $constantoffset] @ jumpXtnd\n\t" ++ "jr T9\n\t" ++ "nop" %} ++ ins_encode %{ ++ Register table_base = $constanttablebase; ++ int con_offset = $constantoffset; ++ Register switch_reg = $switch_val$$Register; ++ ++ if (UseLEXT1) { ++ if (Assembler::is_simm(con_offset, 8)) { ++ __ gsldx(T9, table_base, switch_reg, con_offset); ++ } else if (Assembler::is_simm16(con_offset)) { ++ __ daddu(T9, table_base, switch_reg); ++ __ ld(T9, T9, con_offset); ++ } else { ++ __ move(T9, con_offset); ++ __ daddu(AT, table_base, switch_reg); ++ __ gsldx(T9, AT, T9, 0); ++ } ++ } else { ++ if (Assembler::is_simm16(con_offset)) { ++ __ daddu(T9, table_base, switch_reg); ++ __ ld(T9, T9, con_offset); ++ } else { ++ __ move(T9, con_offset); ++ __ daddu(AT, table_base, switch_reg); ++ __ daddu(AT, T9, AT); ++ __ ld(T9, AT, 0); ++ } ++ } ++ ++ __ jr(T9); ++ __ delayed()->nop(); ++ ++ %} ++ ins_pipe(pipe_jump); ++%} ++*/ ++ ++ ++// Tail Jump; remove the return address; jump to target. ++// TailCall above leaves the return address around. ++// TailJump is used in only one place, the rethrow_Java stub (fancy_jump=2). ++// ex_oop (Exception Oop) is needed in %o0 at the jump. As there would be a ++// "restore" before this instruction (in Epilogue), we need to materialize it ++// in %i0. ++//FIXME ++instruct tailjmpInd(mRegP jump_target,mRegP ex_oop) %{ ++ match( TailJump jump_target ex_oop ); ++ ins_cost(200); ++ format %{ "Jmp $jump_target ; ex_oop = $ex_oop #@tailjmpInd" %} ++ ins_encode %{ ++ Register target = $jump_target$$Register; ++ ++ // V0, V1 are indicated in: ++ // [stubGenerator_mips.cpp] generate_forward_exception() ++ // [runtime_mips.cpp] OptoRuntime::generate_exception_blob() ++ // ++ Register oop = $ex_oop$$Register; ++ Register exception_oop = V0; ++ Register exception_pc = V1; ++ ++ __ move(exception_pc, RA); ++ __ move(exception_oop, oop); ++ ++ __ jr(target); ++ __ delayed()->nop(); ++ %} ++ ins_pipe( pipe_jump ); ++%} ++ ++// ============================================================================ ++// Procedure Call/Return Instructions ++// Call Java Static Instruction ++// Note: If this code changes, the corresponding ret_addr_offset() and ++// compute_padding() functions will have to be adjusted. ++instruct CallStaticJavaDirect(method meth) %{ ++ match(CallStaticJava); ++ effect(USE meth); ++ ++ ins_cost(300); ++ format %{ "CALL,static #@CallStaticJavaDirect " %} ++ ins_encode( Java_Static_Call( meth ) ); ++ ins_pipe( pipe_slow ); ++ ins_pc_relative(1); ++ ins_alignment(16); ++%} ++ ++// Call Java Dynamic Instruction ++// Note: If this code changes, the corresponding ret_addr_offset() and ++// compute_padding() functions will have to be adjusted. ++instruct CallDynamicJavaDirect(method meth) %{ ++ match(CallDynamicJava); ++ effect(USE meth); ++ ++ ins_cost(300); ++ format %{"MOV IC_Klass, #Universe::non_oop_word()\n\t" ++ "CallDynamic @ CallDynamicJavaDirect" %} ++ ins_encode( Java_Dynamic_Call( meth ) ); ++ ins_pipe( pipe_slow ); ++ ins_pc_relative(1); ++ ins_alignment(16); ++%} ++ ++instruct CallLeafNoFPDirect(method meth) %{ ++ match(CallLeafNoFP); ++ effect(USE meth); ++ ++ ins_cost(300); ++ format %{ "CALL_LEAF_NOFP,runtime " %} ++ ins_encode(Java_To_Runtime(meth)); ++ ins_pipe( pipe_slow ); ++ ins_pc_relative(1); ++ ins_alignment(16); ++%} ++ ++// Prefetch instructions. ++ ++instruct prefetchrNTA( memory mem ) %{ ++ match(PrefetchRead mem); ++ ins_cost(125); ++ ++ format %{ "pref $mem\t# Prefetch into non-temporal cache for read @ prefetchrNTA" %} ++ ins_encode %{ ++ int base = $mem$$base; ++ int index = $mem$$index; ++ int scale = $mem$$scale; ++ int disp = $mem$$disp; ++ ++ if( index != 0 ) { ++ if (scale == 0) { ++ __ daddu(AT, as_Register(base), as_Register(index)); ++ } else { ++ __ dsll(AT, as_Register(index), scale); ++ __ daddu(AT, as_Register(base), AT); ++ } ++ } else { ++ __ move(AT, as_Register(base)); ++ } ++ if( Assembler::is_simm16(disp) ) { ++ __ daddiu(AT, AT, disp); ++ } else { ++ __ move(T9, disp); ++ __ daddu(AT, AT, T9); ++ } ++ __ pref(0, AT, 0); //hint: 0:load ++ %} ++ ins_pipe(pipe_slow); ++%} ++ ++instruct prefetchwNTA( memory mem ) %{ ++ match(PrefetchWrite mem); ++ ins_cost(125); ++ format %{ "pref $mem\t# Prefetch to non-temporal cache for write @ prefetchwNTA" %} ++ ins_encode %{ ++ int base = $mem$$base; ++ int index = $mem$$index; ++ int scale = $mem$$scale; ++ int disp = $mem$$disp; ++ ++ if( index != 0 ) { ++ if (scale == 0) { ++ __ daddu(AT, as_Register(base), as_Register(index)); ++ } else { ++ __ dsll(AT, as_Register(index), scale); ++ __ daddu(AT, as_Register(base), AT); ++ } ++ } else { ++ __ move(AT, as_Register(base)); ++ } ++ if( Assembler::is_simm16(disp) ) { ++ __ daddiu(AT, AT, disp); ++ } else { ++ __ move(T9, disp); ++ __ daddu(AT, AT, T9); ++ } ++ __ pref(1, AT, 0); //hint: 1:store ++ %} ++ ins_pipe(pipe_slow); ++%} ++ ++// Prefetch instructions for allocation. ++ ++instruct prefetchAllocNTA( memory mem ) %{ ++ match(PrefetchAllocation mem); ++ ins_cost(125); ++ format %{ "pref $mem\t# Prefetch allocation @ prefetchAllocNTA" %} ++ ins_encode %{ ++ int base = $mem$$base; ++ int index = $mem$$index; ++ int scale = $mem$$scale; ++ int disp = $mem$$disp; ++ ++ Register dst = R0; ++ ++ if ( index != 0 ) { ++ if ( Assembler::is_simm16(disp) ) { ++ if (UseLEXT1) { ++ if (scale == 0) { ++ __ gslbx(dst, as_Register(base), as_Register(index), disp); ++ } else { ++ __ dsll(AT, as_Register(index), scale); ++ __ gslbx(dst, as_Register(base), AT, disp); ++ } ++ } else { ++ if (scale == 0) { ++ __ addu(AT, as_Register(base), as_Register(index)); ++ } else { ++ __ dsll(AT, as_Register(index), scale); ++ __ addu(AT, as_Register(base), AT); ++ } ++ __ lb(dst, AT, disp); ++ } ++ } else { ++ if (scale == 0) { ++ __ addu(AT, as_Register(base), as_Register(index)); ++ } else { ++ __ dsll(AT, as_Register(index), scale); ++ __ addu(AT, as_Register(base), AT); ++ } ++ __ move(T9, disp); ++ if (UseLEXT1) { ++ __ gslbx(dst, AT, T9, 0); ++ } else { ++ __ addu(AT, AT, T9); ++ __ lb(dst, AT, 0); ++ } ++ } ++ } else { ++ if ( Assembler::is_simm16(disp) ) { ++ __ lb(dst, as_Register(base), disp); ++ } else { ++ __ move(T9, disp); ++ if (UseLEXT1) { ++ __ gslbx(dst, as_Register(base), T9, 0); ++ } else { ++ __ addu(AT, as_Register(base), T9); ++ __ lb(dst, AT, 0); ++ } ++ } ++ } ++ %} ++ ins_pipe(pipe_slow); ++%} ++ ++ ++// Call runtime without safepoint ++instruct CallLeafDirect(method meth) %{ ++ match(CallLeaf); ++ effect(USE meth); ++ ++ ins_cost(300); ++ format %{ "CALL_LEAF,runtime #@CallLeafDirect " %} ++ ins_encode(Java_To_Runtime(meth)); ++ ins_pipe( pipe_slow ); ++ ins_pc_relative(1); ++ ins_alignment(16); ++%} ++ ++// Load Char (16bit unsigned) ++instruct loadUS(mRegI dst, memory mem) %{ ++ match(Set dst (LoadUS mem)); ++ ++ ins_cost(125); ++ format %{ "loadUS $dst,$mem @ loadC" %} ++ ins_encode(load_C_enc(dst, mem)); ++ ins_pipe( ialu_loadI ); ++%} ++ ++instruct loadUS_convI2L(mRegL dst, memory mem) %{ ++ match(Set dst (ConvI2L (LoadUS mem))); ++ ++ ins_cost(125); ++ format %{ "loadUS $dst,$mem @ loadUS_convI2L" %} ++ ins_encode(load_C_enc(dst, mem)); ++ ins_pipe( ialu_loadI ); ++%} ++ ++// Store Char (16bit unsigned) ++instruct storeC(memory mem, mRegI src) %{ ++ match(Set mem (StoreC mem src)); ++ ++ ins_cost(125); ++ format %{ "storeC $src, $mem @ storeC" %} ++ ins_encode(store_C_reg_enc(mem, src)); ++ ins_pipe( ialu_loadI ); ++%} ++ ++instruct storeC_0(memory mem, immI_0 zero) %{ ++ match(Set mem (StoreC mem zero)); ++ ++ ins_cost(125); ++ format %{ "storeC $zero, $mem @ storeC_0" %} ++ ins_encode(store_C0_enc(mem)); ++ ins_pipe( ialu_loadI ); ++%} ++ ++ ++instruct loadConF_immF_0(regF dst, immF_0 zero) %{ ++ match(Set dst zero); ++ ins_cost(100); ++ ++ format %{ "mov $dst, zero @ loadConF_immF_0\n"%} ++ ins_encode %{ ++ FloatRegister dst = $dst$$FloatRegister; ++ ++ __ mtc1(R0, dst); ++ %} ++ ins_pipe( fpu_loadF ); ++%} ++ ++ ++instruct loadConF(regF dst, immF src) %{ ++ match(Set dst src); ++ ins_cost(125); ++ ++ format %{ "lwc1 $dst, $constantoffset[$constanttablebase] # load FLOAT $src from table @ loadConF" %} ++ ins_encode %{ ++ int con_offset = $constantoffset($src); ++ ++ if (Assembler::is_simm16(con_offset)) { ++ __ lwc1($dst$$FloatRegister, $constanttablebase, con_offset); ++ } else { ++ __ set64(AT, con_offset); ++ if (UseLEXT1) { ++ __ gslwxc1($dst$$FloatRegister, $constanttablebase, AT, 0); ++ } else { ++ __ daddu(AT, $constanttablebase, AT); ++ __ lwc1($dst$$FloatRegister, AT, 0); ++ } ++ } ++ %} ++ ins_pipe( fpu_loadF ); ++%} ++ ++ ++instruct loadConD_immD_0(regD dst, immD_0 zero) %{ ++ match(Set dst zero); ++ ins_cost(100); ++ ++ format %{ "mov $dst, zero @ loadConD_immD_0"%} ++ ins_encode %{ ++ FloatRegister dst = as_FloatRegister($dst$$reg); ++ ++ __ dmtc1(R0, dst); ++ %} ++ ins_pipe( fpu_loadF ); ++%} ++ ++instruct loadConD(regD dst, immD src) %{ ++ match(Set dst src); ++ ins_cost(125); ++ ++ format %{ "ldc1 $dst, $constantoffset[$constanttablebase] # load DOUBLE $src from table @ loadConD" %} ++ ins_encode %{ ++ int con_offset = $constantoffset($src); ++ ++ if (Assembler::is_simm16(con_offset)) { ++ __ ldc1($dst$$FloatRegister, $constanttablebase, con_offset); ++ } else { ++ __ set64(AT, con_offset); ++ if (UseLEXT1) { ++ __ gsldxc1($dst$$FloatRegister, $constanttablebase, AT, 0); ++ } else { ++ __ daddu(AT, $constanttablebase, AT); ++ __ ldc1($dst$$FloatRegister, AT, 0); ++ } ++ } ++ %} ++ ins_pipe( fpu_loadF ); ++%} ++ ++// Store register Float value (it is faster than store from FPU register) ++instruct storeF_reg( memory mem, regF src) %{ ++ match(Set mem (StoreF mem src)); ++ ++ ins_cost(50); ++ format %{ "store $mem, $src\t# store float @ storeF_reg" %} ++ ins_encode(store_F_reg_enc(mem, src)); ++ ins_pipe( fpu_storeF ); ++%} ++ ++instruct storeF_immF_0( memory mem, immF_0 zero) %{ ++ match(Set mem (StoreF mem zero)); ++ ++ ins_cost(40); ++ format %{ "store $mem, zero\t# store float @ storeF_immF_0" %} ++ ins_encode %{ ++ int base = $mem$$base; ++ int index = $mem$$index; ++ int scale = $mem$$scale; ++ int disp = $mem$$disp; ++ ++ if( index != 0 ) { ++ if (UseLEXT1) { ++ if ( Assembler::is_simm(disp, 8) ) { ++ if ( scale == 0 ) { ++ __ gsswx(R0, as_Register(base), as_Register(index), disp); ++ } else { ++ __ dsll(T9, as_Register(index), scale); ++ __ gsswx(R0, as_Register(base), T9, disp); ++ } ++ } else if ( Assembler::is_simm16(disp) ) { ++ if ( scale == 0 ) { ++ __ daddu(AT, as_Register(base), as_Register(index)); ++ } else { ++ __ dsll(T9, as_Register(index), scale); ++ __ daddu(AT, as_Register(base), T9); ++ } ++ __ sw(R0, AT, disp); ++ } else { ++ if ( scale == 0 ) { ++ __ move(T9, disp); ++ __ daddu(AT, as_Register(index), T9); ++ __ gsswx(R0, as_Register(base), AT, 0); ++ } else { ++ __ dsll(T9, as_Register(index), scale); ++ __ move(AT, disp); ++ __ daddu(AT, AT, T9); ++ __ gsswx(R0, as_Register(base), AT, 0); ++ } ++ } ++ } else { //not use loongson isa ++ if(scale != 0) { ++ __ dsll(T9, as_Register(index), scale); ++ __ daddu(AT, as_Register(base), T9); ++ } else { ++ __ daddu(AT, as_Register(base), as_Register(index)); ++ } ++ if( Assembler::is_simm16(disp) ) { ++ __ sw(R0, AT, disp); ++ } else { ++ __ move(T9, disp); ++ __ daddu(AT, AT, T9); ++ __ sw(R0, AT, 0); ++ } ++ } ++ } else { //index is 0 ++ if (UseLEXT1) { ++ if ( Assembler::is_simm16(disp) ) { ++ __ sw(R0, as_Register(base), disp); ++ } else { ++ __ move(T9, disp); ++ __ gsswx(R0, as_Register(base), T9, 0); ++ } ++ } else { ++ if( Assembler::is_simm16(disp) ) { ++ __ sw(R0, as_Register(base), disp); ++ } else { ++ __ move(T9, disp); ++ __ daddu(AT, as_Register(base), T9); ++ __ sw(R0, AT, 0); ++ } ++ } ++ } ++ %} ++ ins_pipe( ialu_storeI ); ++%} ++ ++// Load Double ++instruct loadD(regD dst, memory mem) %{ ++ match(Set dst (LoadD mem)); ++ ++ ins_cost(150); ++ format %{ "loadD $dst, $mem #@loadD" %} ++ ins_encode(load_D_enc(dst, mem)); ++ ins_pipe( ialu_loadI ); ++%} ++ ++// Load Double - UNaligned ++instruct loadD_unaligned(regD dst, memory mem ) %{ ++ match(Set dst (LoadD_unaligned mem)); ++ ins_cost(250); ++ // FIXME: Need more effective ldl/ldr ++ format %{ "loadD_unaligned $dst, $mem #@loadD_unaligned" %} ++ ins_encode(load_D_enc(dst, mem)); ++ ins_pipe( ialu_loadI ); ++%} ++ ++instruct storeD_reg( memory mem, regD src) %{ ++ match(Set mem (StoreD mem src)); ++ ++ ins_cost(50); ++ format %{ "store $mem, $src\t# store float @ storeD_reg" %} ++ ins_encode(store_D_reg_enc(mem, src)); ++ ins_pipe( fpu_storeF ); ++%} ++ ++instruct storeD_immD_0( memory mem, immD_0 zero) %{ ++ match(Set mem (StoreD mem zero)); ++ ++ ins_cost(40); ++ format %{ "store $mem, zero\t# store float @ storeD_immD_0" %} ++ ins_encode %{ ++ int base = $mem$$base; ++ int index = $mem$$index; ++ int scale = $mem$$scale; ++ int disp = $mem$$disp; ++ ++ __ mtc1(R0, F30); ++ __ cvt_d_w(F30, F30); ++ ++ if( index != 0 ) { ++ if (UseLEXT1) { ++ if ( Assembler::is_simm(disp, 8) ) { ++ if (scale == 0) { ++ __ gssdxc1(F30, as_Register(base), as_Register(index), disp); ++ } else { ++ __ dsll(T9, as_Register(index), scale); ++ __ gssdxc1(F30, as_Register(base), T9, disp); ++ } ++ } else if ( Assembler::is_simm16(disp) ) { ++ if (scale == 0) { ++ __ daddu(AT, as_Register(base), as_Register(index)); ++ __ sdc1(F30, AT, disp); ++ } else { ++ __ dsll(T9, as_Register(index), scale); ++ __ daddu(AT, as_Register(base), T9); ++ __ sdc1(F30, AT, disp); ++ } ++ } else { ++ if (scale == 0) { ++ __ move(T9, disp); ++ __ daddu(AT, as_Register(index), T9); ++ __ gssdxc1(F30, as_Register(base), AT, 0); ++ } else { ++ __ move(T9, disp); ++ __ dsll(AT, as_Register(index), scale); ++ __ daddu(AT, AT, T9); ++ __ gssdxc1(F30, as_Register(base), AT, 0); ++ } ++ } ++ } else { // not use loongson isa ++ if(scale != 0) { ++ __ dsll(T9, as_Register(index), scale); ++ __ daddu(AT, as_Register(base), T9); ++ } else { ++ __ daddu(AT, as_Register(base), as_Register(index)); ++ } ++ if( Assembler::is_simm16(disp) ) { ++ __ sdc1(F30, AT, disp); ++ } else { ++ __ move(T9, disp); ++ __ daddu(AT, AT, T9); ++ __ sdc1(F30, AT, 0); ++ } ++ } ++ } else {// index is 0 ++ if (UseLEXT1) { ++ if ( Assembler::is_simm16(disp) ) { ++ __ sdc1(F30, as_Register(base), disp); ++ } else { ++ __ move(T9, disp); ++ __ gssdxc1(F30, as_Register(base), T9, 0); ++ } ++ } else { ++ if( Assembler::is_simm16(disp) ) { ++ __ sdc1(F30, as_Register(base), disp); ++ } else { ++ __ move(T9, disp); ++ __ daddu(AT, as_Register(base), T9); ++ __ sdc1(F30, AT, 0); ++ } ++ } ++ } ++ %} ++ ins_pipe( ialu_storeI ); ++%} ++ ++instruct loadSSI(mRegI dst, stackSlotI src) ++%{ ++ match(Set dst src); ++ ++ ins_cost(125); ++ format %{ "lw $dst, $src\t# int stk @ loadSSI" %} ++ ins_encode %{ ++ guarantee( Assembler::is_simm16($src$$disp), "disp too long (loadSSI) !"); ++ __ lw($dst$$Register, SP, $src$$disp); ++ %} ++ ins_pipe(ialu_loadI); ++%} ++ ++instruct storeSSI(stackSlotI dst, mRegI src) ++%{ ++ match(Set dst src); ++ ++ ins_cost(100); ++ format %{ "sw $dst, $src\t# int stk @ storeSSI" %} ++ ins_encode %{ ++ guarantee( Assembler::is_simm16($dst$$disp), "disp too long (storeSSI) !"); ++ __ sw($src$$Register, SP, $dst$$disp); ++ %} ++ ins_pipe(ialu_storeI); ++%} ++ ++instruct loadSSL(mRegL dst, stackSlotL src) ++%{ ++ match(Set dst src); ++ ++ ins_cost(125); ++ format %{ "ld $dst, $src\t# long stk @ loadSSL" %} ++ ins_encode %{ ++ guarantee( Assembler::is_simm16($src$$disp), "disp too long (loadSSL) !"); ++ __ ld($dst$$Register, SP, $src$$disp); ++ %} ++ ins_pipe(ialu_loadI); ++%} ++ ++instruct storeSSL(stackSlotL dst, mRegL src) ++%{ ++ match(Set dst src); ++ ++ ins_cost(100); ++ format %{ "sd $dst, $src\t# long stk @ storeSSL" %} ++ ins_encode %{ ++ guarantee( Assembler::is_simm16($dst$$disp), "disp too long (storeSSL) !"); ++ __ sd($src$$Register, SP, $dst$$disp); ++ %} ++ ins_pipe(ialu_storeI); ++%} ++ ++instruct loadSSP(mRegP dst, stackSlotP src) ++%{ ++ match(Set dst src); ++ ++ ins_cost(125); ++ format %{ "ld $dst, $src\t# ptr stk @ loadSSP" %} ++ ins_encode %{ ++ guarantee( Assembler::is_simm16($src$$disp), "disp too long (loadSSP) !"); ++ __ ld($dst$$Register, SP, $src$$disp); ++ %} ++ ins_pipe(ialu_loadI); ++%} ++ ++instruct storeSSP(stackSlotP dst, mRegP src) ++%{ ++ match(Set dst src); ++ ++ ins_cost(100); ++ format %{ "sd $dst, $src\t# ptr stk @ storeSSP" %} ++ ins_encode %{ ++ guarantee( Assembler::is_simm16($dst$$disp), "disp too long (storeSSP) !"); ++ __ sd($src$$Register, SP, $dst$$disp); ++ %} ++ ins_pipe(ialu_storeI); ++%} ++ ++instruct loadSSF(regF dst, stackSlotF src) ++%{ ++ match(Set dst src); ++ ++ ins_cost(125); ++ format %{ "lwc1 $dst, $src\t# float stk @ loadSSF" %} ++ ins_encode %{ ++ guarantee( Assembler::is_simm16($src$$disp), "disp too long (loadSSF) !"); ++ __ lwc1($dst$$FloatRegister, SP, $src$$disp); ++ %} ++ ins_pipe(ialu_loadI); ++%} ++ ++instruct storeSSF(stackSlotF dst, regF src) ++%{ ++ match(Set dst src); ++ ++ ins_cost(100); ++ format %{ "swc1 $dst, $src\t# float stk @ storeSSF" %} ++ ins_encode %{ ++ guarantee( Assembler::is_simm16($dst$$disp), "disp too long (storeSSF) !"); ++ __ swc1($src$$FloatRegister, SP, $dst$$disp); ++ %} ++ ins_pipe(fpu_storeF); ++%} ++ ++// Use the same format since predicate() can not be used here. ++instruct loadSSD(regD dst, stackSlotD src) ++%{ ++ match(Set dst src); ++ ++ ins_cost(125); ++ format %{ "ldc1 $dst, $src\t# double stk @ loadSSD" %} ++ ins_encode %{ ++ guarantee( Assembler::is_simm16($src$$disp), "disp too long (loadSSD) !"); ++ __ ldc1($dst$$FloatRegister, SP, $src$$disp); ++ %} ++ ins_pipe(ialu_loadI); ++%} ++ ++instruct storeSSD(stackSlotD dst, regD src) ++%{ ++ match(Set dst src); ++ ++ ins_cost(100); ++ format %{ "sdc1 $dst, $src\t# double stk @ storeSSD" %} ++ ins_encode %{ ++ guarantee( Assembler::is_simm16($dst$$disp), "disp too long (storeSSD) !"); ++ __ sdc1($src$$FloatRegister, SP, $dst$$disp); ++ %} ++ ins_pipe(fpu_storeF); ++%} ++ ++instruct cmpFastLock( FlagsReg cr, mRegP object, s0_RegP box, mRegI tmp, mRegP scr) %{ ++ match( Set cr (FastLock object box) ); ++ effect( TEMP tmp, TEMP scr, USE_KILL box ); ++ ins_cost(300); ++ format %{ "FASTLOCK $cr <-- $object, $box, $tmp, $scr #@ cmpFastLock" %} ++ ins_encode %{ ++ __ fast_lock($object$$Register, $box$$Register, $tmp$$Register, $scr$$Register); ++ __ move($cr$$Register, AT); ++ %} ++ ++ ins_pipe( pipe_slow ); ++ ins_pc_relative(1); ++%} ++ ++instruct cmpFastUnlock( FlagsReg cr, mRegP object, s0_RegP box, mRegP tmp ) %{ ++ match( Set cr (FastUnlock object box) ); ++ effect( TEMP tmp, USE_KILL box ); ++ ins_cost(300); ++ format %{ "FASTUNLOCK $cr <-- $object, $box, $tmp #@cmpFastUnlock" %} ++ ins_encode %{ ++ __ fast_unlock($object$$Register, $box$$Register, $tmp$$Register); ++ __ move($cr$$Register, AT); ++ %} ++ ++ ins_pipe( pipe_slow ); ++ ins_pc_relative(1); ++%} ++ ++// Store CMS card-mark Immediate ++instruct storeImmCM(memory mem, immI8 src) %{ ++ match(Set mem (StoreCM mem src)); ++ ++ ins_cost(150); ++ format %{ "MOV8 $mem,$src\t! CMS card-mark imm0" %} ++// opcode(0xC6); ++ ins_encode(store_B_immI_enc_sync(mem, src)); ++ ins_pipe( ialu_storeI ); ++%} ++ ++// Die now ++instruct ShouldNotReachHere( ) ++%{ ++ match(Halt); ++ ins_cost(300); ++ ++ // Use the following format syntax ++ format %{ "ILLTRAP ;#@ShouldNotReachHere" %} ++ ins_encode %{ ++ // Here we should emit illtrap ! ++ ++ __ stop("in ShoudNotReachHere"); ++ ++ %} ++ ins_pipe( pipe_jump ); ++%} ++ ++instruct leaP8Narrow(mRegP dst, indOffset8Narrow mem) ++%{ ++ predicate(Universe::narrow_oop_shift() == 0); ++ match(Set dst mem); ++ ++ ins_cost(110); ++ format %{ "leaq $dst, $mem\t# ptr off8narrow @ leaP8Narrow" %} ++ ins_encode %{ ++ Register dst = $dst$$Register; ++ Register base = as_Register($mem$$base); ++ int disp = $mem$$disp; ++ ++ __ daddiu(dst, base, disp); ++ %} ++ ins_pipe( ialu_regI_imm16 ); ++%} ++ ++instruct leaPPosIdxScaleOff8(mRegP dst, basePosIndexScaleOffset8 mem) ++%{ ++ match(Set dst mem); ++ ++ ins_cost(110); ++ format %{ "leaq $dst, $mem\t# @ PosIdxScaleOff8" %} ++ ins_encode %{ ++ Register dst = $dst$$Register; ++ Register base = as_Register($mem$$base); ++ Register index = as_Register($mem$$index); ++ int scale = $mem$$scale; ++ int disp = $mem$$disp; ++ ++ if (scale == 0) { ++ __ daddu(AT, base, index); ++ __ daddiu(dst, AT, disp); ++ } else { ++ __ dsll(AT, index, scale); ++ __ daddu(AT, base, AT); ++ __ daddiu(dst, AT, disp); ++ } ++ %} ++ ++ ins_pipe( ialu_regI_imm16 ); ++%} ++ ++instruct leaPIdxScale(mRegP dst, indIndexScale mem) ++%{ ++ match(Set dst mem); ++ ++ ins_cost(110); ++ format %{ "leaq $dst, $mem\t# @ leaPIdxScale" %} ++ ins_encode %{ ++ Register dst = $dst$$Register; ++ Register base = as_Register($mem$$base); ++ Register index = as_Register($mem$$index); ++ int scale = $mem$$scale; ++ ++ if (scale == 0) { ++ __ daddu(dst, base, index); ++ } else { ++ __ dsll(AT, index, scale); ++ __ daddu(dst, base, AT); ++ } ++ %} ++ ++ ins_pipe( ialu_regI_imm16 ); ++%} ++ ++ ++// ============================================================================ ++// The 2nd slow-half of a subtype check. Scan the subklass's 2ndary superklass ++// array for an instance of the superklass. Set a hidden internal cache on a ++// hit (cache is checked with exposed code in gen_subtype_check()). Return ++// NZ for a miss or zero for a hit. The encoding ALSO sets flags. ++instruct partialSubtypeCheck( mRegP result, no_T8_mRegP sub, no_T8_mRegP super, mT8RegI tmp ) %{ ++ match(Set result (PartialSubtypeCheck sub super)); ++ effect(KILL tmp); ++ ins_cost(1100); // slightly larger than the next version ++ format %{ "partialSubtypeCheck result=$result, sub=$sub, super=$super, tmp=$tmp " %} ++ ++ ins_encode( enc_PartialSubtypeCheck(result, sub, super, tmp) ); ++ ins_pipe( pipe_slow ); ++%} ++ ++// Conditional-store of the updated heap-top. ++// Used during allocation of the shared heap. ++ ++instruct storePConditional( memory heap_top_ptr, mRegP oldval, mRegP newval, FlagsReg cr ) %{ ++ match(Set cr (StorePConditional heap_top_ptr (Binary oldval newval))); ++ ++ format %{ "CMPXCHG $heap_top_ptr, $newval\t# (ptr) @storePConditional " ++ "If $oldval == $heap_top_ptr then store $newval into $heap_top_ptr" %} ++ ins_encode%{ ++ Register oldval = $oldval$$Register; ++ Register newval = $newval$$Register; ++ Address addr(as_Register($heap_top_ptr$$base), $heap_top_ptr$$disp); ++ ++ int index = $heap_top_ptr$$index; ++ int scale = $heap_top_ptr$$scale; ++ int disp = $heap_top_ptr$$disp; ++ ++ guarantee(Assembler::is_simm16(disp), ""); ++ ++ if( index != 0 ) { ++ __ stop("in storePConditional: index != 0"); ++ } else { ++ __ cmpxchg(newval, addr, oldval); ++ __ move($cr$$Register, AT); ++ } ++ %} ++ ins_pipe( long_memory_op ); ++%} ++ ++// Conditional-store of an int value. ++// AT flag is set on success, reset otherwise. ++instruct storeIConditional( memory mem, mRegI oldval, mRegI newval, FlagsReg cr ) %{ ++ match(Set cr (StoreIConditional mem (Binary oldval newval))); ++// effect(KILL oldval); ++ format %{ "CMPXCHG $newval, $mem, $oldval \t# @storeIConditional" %} ++ ++ ins_encode %{ ++ Register oldval = $oldval$$Register; ++ Register newval = $newval$$Register; ++ Address addr(as_Register($mem$$base), $mem$$disp); ++ Label again, failure; ++ ++ int index = $mem$$index; ++ int scale = $mem$$scale; ++ int disp = $mem$$disp; ++ ++ guarantee(Assembler::is_simm16(disp), ""); ++ ++ if( index != 0 ) { ++ __ stop("in storeIConditional: index != 0"); ++ } else { ++ __ bind(again); ++ if (UseSyncLevel >= 10000 || UseSyncLevel == 1000 || UseSyncLevel == 4000) __ sync(); ++ __ ll(AT, addr); ++ __ bne(AT, oldval, failure); ++ __ delayed()->addu(AT, R0, R0); ++ ++ __ addu(AT, newval, R0); ++ __ sc(AT, addr); ++ __ beq(AT, R0, again); ++ __ delayed()->addiu(AT, R0, 0xFF); ++ __ bind(failure); ++ __ sync(); ++ ++ __ move($cr$$Register, AT); ++ } ++%} ++ ++ ins_pipe( long_memory_op ); ++%} ++ ++// Conditional-store of a long value. ++// ZF flag is set on success, reset otherwise. Implemented with a CMPXCHG. ++instruct storeLConditional(memory mem, t2RegL oldval, mRegL newval, FlagsReg cr ) ++%{ ++ match(Set cr (StoreLConditional mem (Binary oldval newval))); ++ effect(KILL oldval); ++ ++ format %{ "cmpxchg $mem, $newval\t# If $oldval == $mem then store $newval into $mem" %} ++ ins_encode%{ ++ Register oldval = $oldval$$Register; ++ Register newval = $newval$$Register; ++ Address addr(as_Register($mem$$base), $mem$$disp); ++ ++ int index = $mem$$index; ++ int scale = $mem$$scale; ++ int disp = $mem$$disp; ++ ++ guarantee(Assembler::is_simm16(disp), ""); ++ ++ if( index != 0 ) { ++ __ stop("in storeIConditional: index != 0"); ++ } else { ++ __ cmpxchg(newval, addr, oldval); ++ __ move($cr$$Register, AT); ++ } ++ %} ++ ins_pipe( long_memory_op ); ++%} ++ ++// Implement LoadPLocked. Must be ordered against changes of the memory location ++// by storePConditional. ++instruct loadPLocked(mRegP dst, memory mem) %{ ++ match(Set dst (LoadPLocked mem)); ++ ins_cost(MEMORY_REF_COST); ++ ++ format %{ "ld $dst, $mem #@loadPLocked\n\t" ++ "sync" %} ++ size(12); ++ ins_encode (load_P_enc_ac(dst, mem)); ++ ins_pipe( ialu_loadI ); ++%} ++ ++ ++instruct compareAndSwapI( mRegI res, mRegP mem_ptr, mS2RegI oldval, mRegI newval) %{ ++ match(Set res (CompareAndSwapI mem_ptr (Binary oldval newval))); ++ effect(KILL oldval); ++// match(CompareAndSwapI mem_ptr (Binary oldval newval)); ++ format %{ "CMPXCHG $newval, [$mem_ptr], $oldval @ compareAndSwapL\n\t" ++ "MOV $res, 1 @ compareAndSwapI\n\t" ++ "BNE AT, R0 @ compareAndSwapI\n\t" ++ "MOV $res, 0 @ compareAndSwapI\n" ++ "L:" %} ++ ins_encode %{ ++ Register newval = $newval$$Register; ++ Register oldval = $oldval$$Register; ++ Register res = $res$$Register; ++ Address addr($mem_ptr$$Register, 0); ++ Label L; ++ ++ __ cmpxchg32(newval, addr, oldval); ++ __ move(res, AT); ++ %} ++ ins_pipe( long_memory_op ); ++%} ++ ++instruct compareAndSwapL( mRegI res, mRegP mem_ptr, s2RegL oldval, mRegL newval) %{ ++ predicate(VM_Version::supports_cx8()); ++ match(Set res (CompareAndSwapL mem_ptr (Binary oldval newval))); ++ effect(KILL oldval); ++ format %{ "CMPXCHG $newval, [$mem_ptr], $oldval @ compareAndSwapI\n\t" ++ "MOV $res, 1 @ compareAndSwapI\n\t" ++ "BNE AT, R0 @ compareAndSwapI\n\t" ++ "MOV $res, 0 @ compareAndSwapI\n" ++ "L:" %} ++ ins_encode %{ ++ Register newval = $newval$$Register; ++ Register oldval = $oldval$$Register; ++ Register res = $res$$Register; ++ Address addr($mem_ptr$$Register, 0); ++ Label L; ++ ++ __ cmpxchg(newval, addr, oldval); ++ __ move(res, AT); ++ %} ++ ins_pipe( long_memory_op ); ++%} ++ ++//FIXME: ++instruct compareAndSwapP( mRegI res, mRegP mem_ptr, s2_RegP oldval, mRegP newval) %{ ++ match(Set res (CompareAndSwapP mem_ptr (Binary oldval newval))); ++ effect(KILL oldval); ++ format %{ "CMPXCHG $newval, [$mem_ptr], $oldval @ compareAndSwapP\n\t" ++ "MOV $res, AT @ compareAndSwapP\n\t" ++ "L:" %} ++ ins_encode %{ ++ Register newval = $newval$$Register; ++ Register oldval = $oldval$$Register; ++ Register res = $res$$Register; ++ Address addr($mem_ptr$$Register, 0); ++ Label L; ++ ++ __ cmpxchg(newval, addr, oldval); ++ __ move(res, AT); ++ %} ++ ins_pipe( long_memory_op ); ++%} ++ ++instruct compareAndSwapN( mRegI res, mRegP mem_ptr, t2_RegN oldval, mRegN newval) %{ ++ match(Set res (CompareAndSwapN mem_ptr (Binary oldval newval))); ++ effect(KILL oldval); ++ format %{ "CMPXCHG $newval, [$mem_ptr], $oldval @ compareAndSwapN\n\t" ++ "MOV $res, AT @ compareAndSwapN\n\t" ++ "L:" %} ++ ins_encode %{ ++ Register newval = $newval$$Register; ++ Register oldval = $oldval$$Register; ++ Register res = $res$$Register; ++ Address addr($mem_ptr$$Register, 0); ++ Label L; ++ ++ // cmpxchg32 is implemented with ll/sc, which will do sign extension. ++ // Thus, we should extend oldval's sign for correct comparision. ++ // ++ __ sll(oldval, oldval, 0); ++ ++ __ cmpxchg32(newval, addr, oldval); ++ __ move(res, AT); ++ %} ++ ins_pipe( long_memory_op ); ++%} ++ ++//----------Max and Min-------------------------------------------------------- ++// Min Instructions ++//// ++// *** Min and Max using the conditional move are slower than the ++// *** branch version on a Pentium III. ++// // Conditional move for min ++//instruct cmovI_reg_lt( eRegI op2, eRegI op1, eFlagsReg cr ) %{ ++// effect( USE_DEF op2, USE op1, USE cr ); ++// format %{ "CMOVlt $op2,$op1\t! min" %} ++// opcode(0x4C,0x0F); ++// ins_encode( OpcS, OpcP, RegReg( op2, op1 ) ); ++// ins_pipe( pipe_cmov_reg ); ++//%} ++// ++//// Min Register with Register (P6 version) ++//instruct minI_eReg_p6( eRegI op1, eRegI op2 ) %{ ++// predicate(VM_Version::supports_cmov() ); ++// match(Set op2 (MinI op1 op2)); ++// ins_cost(200); ++// expand %{ ++// eFlagsReg cr; ++// compI_eReg(cr,op1,op2); ++// cmovI_reg_lt(op2,op1,cr); ++// %} ++//%} ++ ++// Min Register with Register (generic version) ++instruct minI_Reg_Reg(mRegI dst, mRegI src) %{ ++ match(Set dst (MinI dst src)); ++ //effect(KILL flags); ++ ins_cost(80); ++ ++ format %{ "MIN $dst, $src @minI_Reg_Reg" %} ++ ins_encode %{ ++ Register dst = $dst$$Register; ++ Register src = $src$$Register; ++ ++ __ slt(AT, src, dst); ++ __ movn(dst, src, AT); ++ ++ %} ++ ++ ins_pipe( pipe_slow ); ++%} ++ ++// Max Register with Register ++// *** Min and Max using the conditional move are slower than the ++// *** branch version on a Pentium III. ++// // Conditional move for max ++//instruct cmovI_reg_gt( eRegI op2, eRegI op1, eFlagsReg cr ) %{ ++// effect( USE_DEF op2, USE op1, USE cr ); ++// format %{ "CMOVgt $op2,$op1\t! max" %} ++// opcode(0x4F,0x0F); ++// ins_encode( OpcS, OpcP, RegReg( op2, op1 ) ); ++// ins_pipe( pipe_cmov_reg ); ++//%} ++// ++// // Max Register with Register (P6 version) ++//instruct maxI_eReg_p6( eRegI op1, eRegI op2 ) %{ ++// predicate(VM_Version::supports_cmov() ); ++// match(Set op2 (MaxI op1 op2)); ++// ins_cost(200); ++// expand %{ ++// eFlagsReg cr; ++// compI_eReg(cr,op1,op2); ++// cmovI_reg_gt(op2,op1,cr); ++// %} ++//%} ++ ++// Max Register with Register (generic version) ++instruct maxI_Reg_Reg(mRegI dst, mRegI src) %{ ++ match(Set dst (MaxI dst src)); ++ ins_cost(80); ++ ++ format %{ "MAX $dst, $src @maxI_Reg_Reg" %} ++ ++ ins_encode %{ ++ Register dst = $dst$$Register; ++ Register src = $src$$Register; ++ ++ __ slt(AT, dst, src); ++ __ movn(dst, src, AT); ++ ++ %} ++ ++ ins_pipe( pipe_slow ); ++%} ++ ++instruct maxI_Reg_zero(mRegI dst, immI_0 zero) %{ ++ match(Set dst (MaxI dst zero)); ++ ins_cost(50); ++ ++ format %{ "MAX $dst, 0 @maxI_Reg_zero" %} ++ ++ ins_encode %{ ++ Register dst = $dst$$Register; ++ ++ __ slt(AT, dst, R0); ++ __ movn(dst, R0, AT); ++ ++ %} ++ ++ ins_pipe( pipe_slow ); ++%} ++ ++instruct zerox_long_reg_reg(mRegL dst, mRegL src, immL_MaxUI mask) ++%{ ++ match(Set dst (AndL src mask)); ++ ++ format %{ "movl $dst, $src\t# zero-extend long @ zerox_long_reg_reg" %} ++ ins_encode %{ ++ Register dst = $dst$$Register; ++ Register src = $src$$Register; ++ ++ __ dext(dst, src, 0, 32); ++ %} ++ ins_pipe(ialu_regI_regI); ++%} ++ ++instruct combine_i2l(mRegL dst, mRegI src1, immL_MaxUI mask, mRegI src2, immI_32 shift32) ++%{ ++ match(Set dst (OrL (AndL (ConvI2L src1) mask) (LShiftL (ConvI2L src2) shift32))); ++ ++ format %{ "combine_i2l $dst, $src2(H), $src1(L) @ combine_i2l" %} ++ ins_encode %{ ++ Register dst = $dst$$Register; ++ Register src1 = $src1$$Register; ++ Register src2 = $src2$$Register; ++ ++ if (src1 == dst) { ++ __ dinsu(dst, src2, 32, 32); ++ } else if (src2 == dst) { ++ __ dsll32(dst, dst, 0); ++ __ dins(dst, src1, 0, 32); ++ } else { ++ __ dext(dst, src1, 0, 32); ++ __ dinsu(dst, src2, 32, 32); ++ } ++ %} ++ ins_pipe(ialu_regI_regI); ++%} ++ ++// Zero-extend convert int to long ++instruct convI2L_reg_reg_zex(mRegL dst, mRegI src, immL_MaxUI mask) ++%{ ++ match(Set dst (AndL (ConvI2L src) mask)); ++ ++ format %{ "movl $dst, $src\t# i2l zero-extend @ convI2L_reg_reg_zex" %} ++ ins_encode %{ ++ Register dst = $dst$$Register; ++ Register src = $src$$Register; ++ ++ __ dext(dst, src, 0, 32); ++ %} ++ ins_pipe(ialu_regI_regI); ++%} ++ ++instruct convL2I2L_reg_reg_zex(mRegL dst, mRegL src, immL_MaxUI mask) ++%{ ++ match(Set dst (AndL (ConvI2L (ConvL2I src)) mask)); ++ ++ format %{ "movl $dst, $src\t# i2l zero-extend @ convL2I2L_reg_reg_zex" %} ++ ins_encode %{ ++ Register dst = $dst$$Register; ++ Register src = $src$$Register; ++ ++ __ dext(dst, src, 0, 32); ++ %} ++ ins_pipe(ialu_regI_regI); ++%} ++ ++// Match loading integer and casting it to unsigned int in long register. ++// LoadI + ConvI2L + AndL 0xffffffff. ++instruct loadUI2L_rmask(mRegL dst, memory mem, immL_MaxUI mask) %{ ++ match(Set dst (AndL (ConvI2L (LoadI mem)) mask)); ++ ++ format %{ "lwu $dst, $mem \t// zero-extend to long @ loadUI2L_rmask" %} ++ ins_encode (load_N_enc(dst, mem)); ++ ins_pipe(ialu_loadI); ++%} ++ ++instruct loadUI2L_lmask(mRegL dst, memory mem, immL_MaxUI mask) %{ ++ match(Set dst (AndL mask (ConvI2L (LoadI mem)))); ++ ++ format %{ "lwu $dst, $mem \t// zero-extend to long @ loadUI2L_lmask" %} ++ ins_encode (load_N_enc(dst, mem)); ++ ins_pipe(ialu_loadI); ++%} ++ ++ ++// ============================================================================ ++// Safepoint Instruction ++instruct safePoint_poll_reg(mRegP poll) %{ ++ match(SafePoint poll); ++ predicate(false); ++ effect(USE poll); ++ ++ ins_cost(125); ++ format %{ "Safepoint @ [$poll] : poll for GC @ safePoint_poll_reg" %} ++ ++ ins_encode %{ ++ Register poll_reg = $poll$$Register; ++ ++ __ block_comment("Safepoint:"); ++ __ relocate(relocInfo::poll_type); ++ __ lw(AT, poll_reg, 0); ++ %} ++ ++ ins_pipe( ialu_storeI ); ++%} ++ ++instruct safePoint_poll() %{ ++ match(SafePoint); ++ ++ ins_cost(105); ++ format %{ "poll for GC @ safePoint_poll" %} ++ ++ ins_encode %{ ++ __ block_comment("Safepoint:"); ++ __ set64(T9, (long)os::get_polling_page()); ++ __ relocate(relocInfo::poll_type); ++ __ lw(AT, T9, 0); ++ %} ++ ++ ins_pipe( ialu_storeI ); ++%} ++ ++//----------Arithmetic Conversion Instructions--------------------------------- ++ ++instruct roundFloat_nop(regF dst) ++%{ ++ match(Set dst (RoundFloat dst)); ++ ++ ins_cost(0); ++ ins_encode(); ++ ins_pipe(empty); ++%} ++ ++instruct roundDouble_nop(regD dst) ++%{ ++ match(Set dst (RoundDouble dst)); ++ ++ ins_cost(0); ++ ins_encode(); ++ ins_pipe(empty); ++%} ++ ++//---------- Zeros Count Instructions ------------------------------------------ ++// CountLeadingZerosINode CountTrailingZerosINode ++instruct countLeadingZerosI(mRegI dst, mRegI src) %{ ++ predicate(UseCountLeadingZerosInstructionMIPS64); ++ match(Set dst (CountLeadingZerosI src)); ++ ++ format %{ "clz $dst, $src\t# count leading zeros (int)" %} ++ ins_encode %{ ++ __ clz($dst$$Register, $src$$Register); ++ %} ++ ins_pipe( ialu_regL_regL ); ++%} ++ ++instruct countLeadingZerosL(mRegI dst, mRegL src) %{ ++ predicate(UseCountLeadingZerosInstructionMIPS64); ++ match(Set dst (CountLeadingZerosL src)); ++ ++ format %{ "dclz $dst, $src\t# count leading zeros (long)" %} ++ ins_encode %{ ++ __ dclz($dst$$Register, $src$$Register); ++ %} ++ ins_pipe( ialu_regL_regL ); ++%} ++ ++instruct countTrailingZerosI(mRegI dst, mRegI src) %{ ++ predicate(UseCountTrailingZerosInstructionMIPS64); ++ match(Set dst (CountTrailingZerosI src)); ++ ++ format %{ "ctz $dst, $src\t# count trailing zeros (int)" %} ++ ins_encode %{ ++ // ctz and dctz is gs instructions. ++ __ ctz($dst$$Register, $src$$Register); ++ %} ++ ins_pipe( ialu_regL_regL ); ++%} ++ ++instruct countTrailingZerosL(mRegI dst, mRegL src) %{ ++ predicate(UseCountTrailingZerosInstructionMIPS64); ++ match(Set dst (CountTrailingZerosL src)); ++ ++ format %{ "dcto $dst, $src\t# count trailing zeros (long)" %} ++ ins_encode %{ ++ __ dctz($dst$$Register, $src$$Register); ++ %} ++ ins_pipe( ialu_regL_regL ); ++%} ++ ++// ====================VECTOR INSTRUCTIONS===================================== ++ ++// Load vectors (8 bytes long) ++instruct loadV8(vecD dst, memory mem) %{ ++ predicate(n->as_LoadVector()->memory_size() == 8); ++ match(Set dst (LoadVector mem)); ++ ins_cost(125); ++ format %{ "load $dst, $mem\t! load vector (8 bytes)" %} ++ ins_encode(load_D_enc(dst, mem)); ++ ins_pipe( fpu_loadF ); ++%} ++ ++// Store vectors (8 bytes long) ++instruct storeV8(memory mem, vecD src) %{ ++ predicate(n->as_StoreVector()->memory_size() == 8); ++ match(Set mem (StoreVector mem src)); ++ ins_cost(145); ++ format %{ "store $mem, $src\t! store vector (8 bytes)" %} ++ ins_encode(store_D_reg_enc(mem, src)); ++ ins_pipe( fpu_storeF ); ++%} ++ ++instruct Repl8B_DSP(vecD dst, mRegI src) %{ ++ predicate(n->as_Vector()->length() == 8 && UseLEXT3); ++ match(Set dst (ReplicateB src)); ++ ins_cost(100); ++ format %{ "replv_ob AT, $src\n\t" ++ "dmtc1 AT, $dst\t! replicate8B" %} ++ ins_encode %{ ++ __ replv_ob(AT, $src$$Register); ++ __ dmtc1(AT, $dst$$FloatRegister); ++ %} ++ ins_pipe( pipe_mtc1 ); ++%} ++ ++instruct Repl8B(vecD dst, mRegI src) %{ ++ predicate(n->as_Vector()->length() == 8); ++ match(Set dst (ReplicateB src)); ++ ins_cost(140); ++ format %{ "move AT, $src\n\t" ++ "dins AT, AT, 8, 8\n\t" ++ "dins AT, AT, 16, 16\n\t" ++ "dinsu AT, AT, 32, 32\n\t" ++ "dmtc1 AT, $dst\t! replicate8B" %} ++ ins_encode %{ ++ __ move(AT, $src$$Register); ++ __ dins(AT, AT, 8, 8); ++ __ dins(AT, AT, 16, 16); ++ __ dinsu(AT, AT, 32, 32); ++ __ dmtc1(AT, $dst$$FloatRegister); ++ %} ++ ins_pipe( pipe_mtc1 ); ++%} ++ ++instruct Repl8B_imm_DSP(vecD dst, immI con) %{ ++ predicate(n->as_Vector()->length() == 8 && UseLEXT3); ++ match(Set dst (ReplicateB con)); ++ ins_cost(110); ++ format %{ "repl_ob AT, [$con]\n\t" ++ "dmtc1 AT, $dst,0x00\t! replicate8B($con)" %} ++ ins_encode %{ ++ int val = $con$$constant; ++ __ repl_ob(AT, val); ++ __ dmtc1(AT, $dst$$FloatRegister); ++ %} ++ ins_pipe( pipe_mtc1 ); ++%} ++ ++instruct Repl8B_imm(vecD dst, immI con) %{ ++ predicate(n->as_Vector()->length() == 8); ++ match(Set dst (ReplicateB con)); ++ ins_cost(150); ++ format %{ "move AT, [$con]\n\t" ++ "dins AT, AT, 8, 8\n\t" ++ "dins AT, AT, 16, 16\n\t" ++ "dinsu AT, AT, 32, 32\n\t" ++ "dmtc1 AT, $dst,0x00\t! replicate8B($con)" %} ++ ins_encode %{ ++ __ move(AT, $con$$constant); ++ __ dins(AT, AT, 8, 8); ++ __ dins(AT, AT, 16, 16); ++ __ dinsu(AT, AT, 32, 32); ++ __ dmtc1(AT, $dst$$FloatRegister); ++ %} ++ ins_pipe( pipe_mtc1 ); ++%} ++ ++instruct Repl8B_zero(vecD dst, immI_0 zero) %{ ++ predicate(n->as_Vector()->length() == 8); ++ match(Set dst (ReplicateB zero)); ++ ins_cost(90); ++ format %{ "dmtc1 R0, $dst\t! replicate8B zero" %} ++ ins_encode %{ ++ __ dmtc1(R0, $dst$$FloatRegister); ++ %} ++ ins_pipe( pipe_mtc1 ); ++%} ++ ++instruct Repl8B_M1(vecD dst, immI_M1 M1) %{ ++ predicate(n->as_Vector()->length() == 8); ++ match(Set dst (ReplicateB M1)); ++ ins_cost(80); ++ format %{ "dmtc1 -1, $dst\t! replicate8B -1" %} ++ ins_encode %{ ++ __ nor(AT, R0, R0); ++ __ dmtc1(AT, $dst$$FloatRegister); ++ %} ++ ins_pipe( pipe_mtc1 ); ++%} ++ ++instruct Repl4S_DSP(vecD dst, mRegI src) %{ ++ predicate(n->as_Vector()->length() == 4 && UseLEXT3); ++ match(Set dst (ReplicateS src)); ++ ins_cost(100); ++ format %{ "replv_qh AT, $src\n\t" ++ "dmtc1 AT, $dst\t! replicate4S" %} ++ ins_encode %{ ++ __ replv_qh(AT, $src$$Register); ++ __ dmtc1(AT, $dst$$FloatRegister); ++ %} ++ ins_pipe( pipe_mtc1 ); ++%} ++ ++instruct Repl4S(vecD dst, mRegI src) %{ ++ predicate(n->as_Vector()->length() == 4); ++ match(Set dst (ReplicateS src)); ++ ins_cost(120); ++ format %{ "move AT, $src \n\t" ++ "dins AT, AT, 16, 16\n\t" ++ "dinsu AT, AT, 32, 32\n\t" ++ "dmtc1 AT, $dst\t! replicate4S" %} ++ ins_encode %{ ++ __ move(AT, $src$$Register); ++ __ dins(AT, AT, 16, 16); ++ __ dinsu(AT, AT, 32, 32); ++ __ dmtc1(AT, $dst$$FloatRegister); ++ %} ++ ins_pipe( pipe_mtc1 ); ++%} ++ ++instruct Repl4S_imm_DSP(vecD dst, immI con) %{ ++ predicate(n->as_Vector()->length() == 4 && UseLEXT3); ++ match(Set dst (ReplicateS con)); ++ ins_cost(100); ++ format %{ "repl_qh AT, [$con]\n\t" ++ "dmtc1 AT, $dst\t! replicate4S($con)" %} ++ ins_encode %{ ++ int val = $con$$constant; ++ if ( Assembler::is_simm(val, 10)) { ++ //repl_qh supports 10 bits immediate ++ __ repl_qh(AT, val); ++ } else { ++ __ li32(AT, val); ++ __ replv_qh(AT, AT); ++ } ++ __ dmtc1(AT, $dst$$FloatRegister); ++ %} ++ ins_pipe( pipe_mtc1 ); ++%} ++ ++instruct Repl4S_imm(vecD dst, immI con) %{ ++ predicate(n->as_Vector()->length() == 4); ++ match(Set dst (ReplicateS con)); ++ ins_cost(110); ++ format %{ "move AT, [$con]\n\t" ++ "dins AT, AT, 16, 16\n\t" ++ "dinsu AT, AT, 32, 32\n\t" ++ "dmtc1 AT, $dst\t! replicate4S($con)" %} ++ ins_encode %{ ++ __ move(AT, $con$$constant); ++ __ dins(AT, AT, 16, 16); ++ __ dinsu(AT, AT, 32, 32); ++ __ dmtc1(AT, $dst$$FloatRegister); ++ %} ++ ins_pipe( pipe_mtc1 ); ++%} ++ ++instruct Repl4S_zero(vecD dst, immI_0 zero) %{ ++ predicate(n->as_Vector()->length() == 4); ++ match(Set dst (ReplicateS zero)); ++ format %{ "dmtc1 R0, $dst\t! replicate4S zero" %} ++ ins_encode %{ ++ __ dmtc1(R0, $dst$$FloatRegister); ++ %} ++ ins_pipe( pipe_mtc1 ); ++%} ++ ++instruct Repl4S_M1(vecD dst, immI_M1 M1) %{ ++ predicate(n->as_Vector()->length() == 4); ++ match(Set dst (ReplicateS M1)); ++ format %{ "dmtc1 -1, $dst\t! replicate4S -1" %} ++ ins_encode %{ ++ __ nor(AT, R0, R0); ++ __ dmtc1(AT, $dst$$FloatRegister); ++ %} ++ ins_pipe( pipe_mtc1 ); ++%} ++ ++// Replicate integer (4 byte) scalar to be vector ++instruct Repl2I(vecD dst, mRegI src) %{ ++ predicate(n->as_Vector()->length() == 2); ++ match(Set dst (ReplicateI src)); ++ format %{ "dins AT, $src, 0, 32\n\t" ++ "dinsu AT, $src, 32, 32\n\t" ++ "dmtc1 AT, $dst\t! replicate2I" %} ++ ins_encode %{ ++ __ dins(AT, $src$$Register, 0, 32); ++ __ dinsu(AT, $src$$Register, 32, 32); ++ __ dmtc1(AT, $dst$$FloatRegister); ++ %} ++ ins_pipe( pipe_mtc1 ); ++%} ++ ++// Replicate integer (4 byte) scalar immediate to be vector by loading from const table. ++instruct Repl2I_imm(vecD dst, immI con, mA7RegI tmp) %{ ++ predicate(n->as_Vector()->length() == 2); ++ match(Set dst (ReplicateI con)); ++ effect(KILL tmp); ++ format %{ "li32 AT, [$con], 32\n\t" ++ "dinsu AT, AT\n\t" ++ "dmtc1 AT, $dst\t! replicate2I($con)" %} ++ ins_encode %{ ++ int val = $con$$constant; ++ __ li32(AT, val); ++ __ dinsu(AT, AT, 32, 32); ++ __ dmtc1(AT, $dst$$FloatRegister); ++ %} ++ ins_pipe( pipe_mtc1 ); ++%} ++ ++// Replicate integer (4 byte) scalar zero to be vector ++instruct Repl2I_zero(vecD dst, immI_0 zero) %{ ++ predicate(n->as_Vector()->length() == 2); ++ match(Set dst (ReplicateI zero)); ++ format %{ "dmtc1 R0, $dst\t! replicate2I zero" %} ++ ins_encode %{ ++ __ dmtc1(R0, $dst$$FloatRegister); ++ %} ++ ins_pipe( pipe_mtc1 ); ++%} ++ ++// Replicate integer (4 byte) scalar -1 to be vector ++instruct Repl2I_M1(vecD dst, immI_M1 M1) %{ ++ predicate(n->as_Vector()->length() == 2); ++ match(Set dst (ReplicateI M1)); ++ format %{ "dmtc1 -1, $dst\t! replicate2I -1, use AT" %} ++ ins_encode %{ ++ __ nor(AT, R0, R0); ++ __ dmtc1(AT, $dst$$FloatRegister); ++ %} ++ ins_pipe( pipe_mtc1 ); ++%} ++ ++// Replicate float (4 byte) scalar to be vector ++instruct Repl2F(vecD dst, regF src) %{ ++ predicate(n->as_Vector()->length() == 2); ++ match(Set dst (ReplicateF src)); ++ format %{ "cvt.ps $dst, $src, $src\t! replicate2F" %} ++ ins_encode %{ ++ __ cvt_ps_s($dst$$FloatRegister, $src$$FloatRegister, $src$$FloatRegister); ++ %} ++ ins_pipe( pipe_slow ); ++%} ++ ++// Replicate float (4 byte) scalar zero to be vector ++instruct Repl2F_zero(vecD dst, immF_0 zero) %{ ++ predicate(n->as_Vector()->length() == 2); ++ match(Set dst (ReplicateF zero)); ++ format %{ "dmtc1 R0, $dst\t! replicate2F zero" %} ++ ins_encode %{ ++ __ dmtc1(R0, $dst$$FloatRegister); ++ %} ++ ins_pipe( pipe_mtc1 ); ++%} ++ ++ ++// ====================VECTOR ARITHMETIC======================================= ++ ++// --------------------------------- ADD -------------------------------------- ++ ++// Floats vector add ++// kernel does not have emulation of PS instructions yet, so PS instructions is disabled. ++instruct vadd2F(vecD dst, vecD src) %{ ++ predicate(n->as_Vector()->length() == 2); ++ match(Set dst (AddVF dst src)); ++ format %{ "add.ps $dst,$src\t! add packed2F" %} ++ ins_encode %{ ++ __ add_ps($dst$$FloatRegister, $dst$$FloatRegister, $src$$FloatRegister); ++ %} ++ ins_pipe( pipe_slow ); ++%} ++ ++instruct vadd2F3(vecD dst, vecD src1, vecD src2) %{ ++ predicate(n->as_Vector()->length() == 2); ++ match(Set dst (AddVF src1 src2)); ++ format %{ "add.ps $dst,$src1,$src2\t! add packed2F" %} ++ ins_encode %{ ++ __ add_ps($dst$$FloatRegister, $src1$$FloatRegister, $src2$$FloatRegister); ++ %} ++ ins_pipe( fpu_regF_regF ); ++%} ++ ++// --------------------------------- SUB -------------------------------------- ++ ++// Floats vector sub ++instruct vsub2F(vecD dst, vecD src) %{ ++ predicate(n->as_Vector()->length() == 2); ++ match(Set dst (SubVF dst src)); ++ format %{ "sub.ps $dst,$src\t! sub packed2F" %} ++ ins_encode %{ ++ __ sub_ps($dst$$FloatRegister, $dst$$FloatRegister, $src$$FloatRegister); ++ %} ++ ins_pipe( fpu_regF_regF ); ++%} ++ ++// --------------------------------- MUL -------------------------------------- ++ ++// Floats vector mul ++instruct vmul2F(vecD dst, vecD src) %{ ++ predicate(n->as_Vector()->length() == 2); ++ match(Set dst (MulVF dst src)); ++ format %{ "mul.ps $dst, $src\t! mul packed2F" %} ++ ins_encode %{ ++ __ mul_ps($dst$$FloatRegister, $dst$$FloatRegister, $src$$FloatRegister); ++ %} ++ ins_pipe( fpu_regF_regF ); ++%} ++ ++instruct vmul2F3(vecD dst, vecD src1, vecD src2) %{ ++ predicate(n->as_Vector()->length() == 2); ++ match(Set dst (MulVF src1 src2)); ++ format %{ "mul.ps $dst, $src1, $src2\t! mul packed2F" %} ++ ins_encode %{ ++ __ mul_ps($dst$$FloatRegister, $src1$$FloatRegister, $src2$$FloatRegister); ++ %} ++ ins_pipe( fpu_regF_regF ); ++%} ++ ++// --------------------------------- DIV -------------------------------------- ++// MIPS do not have div.ps ++ ++// --------------------------------- MADD -------------------------------------- ++// Floats vector madd ++//instruct vmadd2F(vecD dst, vecD src1, vecD src2, vecD src3) %{ ++// predicate(n->as_Vector()->length() == 2); ++// match(Set dst (AddVF (MulVF src1 src2) src3)); ++// ins_cost(50); ++// format %{ "madd.ps $dst, $src3, $src1, $src2\t! madd packed2F" %} ++// ins_encode %{ ++// __ madd_ps($dst$$FloatRegister, $src3$$FloatRegister, $src1$$FloatRegister, $src2$$FloatRegister); ++// %} ++// ins_pipe( fpu_regF_regF ); ++//%} ++ ++ ++//----------PEEPHOLE RULES----------------------------------------------------- ++// These must follow all instruction definitions as they use the names ++// defined in the instructions definitions. ++// ++// peepmatch ( root_instr_name [preceeding_instruction]* ); ++// ++// peepconstraint %{ ++// (instruction_number.operand_name relational_op instruction_number.operand_name ++// [, ...] ); ++// // instruction numbers are zero-based using left to right order in peepmatch ++// ++// peepreplace ( instr_name ( [instruction_number.operand_name]* ) ); ++// // provide an instruction_number.operand_name for each operand that appears ++// // in the replacement instruction's match rule ++// ++// ---------VM FLAGS--------------------------------------------------------- ++// ++// All peephole optimizations can be turned off using -XX:-OptoPeephole ++// ++// Each peephole rule is given an identifying number starting with zero and ++// increasing by one in the order seen by the parser. An individual peephole ++// can be enabled, and all others disabled, by using -XX:OptoPeepholeAt=# ++// on the command-line. ++// ++// ---------CURRENT LIMITATIONS---------------------------------------------- ++// ++// Only match adjacent instructions in same basic block ++// Only equality constraints ++// Only constraints between operands, not (0.dest_reg == EAX_enc) ++// Only one replacement instruction ++// ++// ---------EXAMPLE---------------------------------------------------------- ++// ++// // pertinent parts of existing instructions in architecture description ++// instruct movI(eRegI dst, eRegI src) %{ ++// match(Set dst (CopyI src)); ++// %} ++// ++// instruct incI_eReg(eRegI dst, immI_1 src, eFlagsReg cr) %{ ++// match(Set dst (AddI dst src)); ++// effect(KILL cr); ++// %} ++// ++// // Change (inc mov) to lea ++// peephole %{ ++// // increment preceeded by register-register move ++// peepmatch ( incI_eReg movI ); ++// // require that the destination register of the increment ++// // match the destination register of the move ++// peepconstraint ( 0.dst == 1.dst ); ++// // construct a replacement instruction that sets ++// // the destination to ( move's source register + one ) ++// peepreplace ( leaI_eReg_immI( 0.dst 1.src 0.src ) ); ++// %} ++// ++// Implementation no longer uses movX instructions since ++// machine-independent system no longer uses CopyX nodes. ++// ++// peephole %{ ++// peepmatch ( incI_eReg movI ); ++// peepconstraint ( 0.dst == 1.dst ); ++// peepreplace ( leaI_eReg_immI( 0.dst 1.src 0.src ) ); ++// %} ++// ++// peephole %{ ++// peepmatch ( decI_eReg movI ); ++// peepconstraint ( 0.dst == 1.dst ); ++// peepreplace ( leaI_eReg_immI( 0.dst 1.src 0.src ) ); ++// %} ++// ++// peephole %{ ++// peepmatch ( addI_eReg_imm movI ); ++// peepconstraint ( 0.dst == 1.dst ); ++// peepreplace ( leaI_eReg_immI( 0.dst 1.src 0.src ) ); ++// %} ++// ++// peephole %{ ++// peepmatch ( addP_eReg_imm movP ); ++// peepconstraint ( 0.dst == 1.dst ); ++// peepreplace ( leaP_eReg_immI( 0.dst 1.src 0.src ) ); ++// %} ++ ++// // Change load of spilled value to only a spill ++// instruct storeI(memory mem, eRegI src) %{ ++// match(Set mem (StoreI mem src)); ++// %} ++// ++// instruct loadI(eRegI dst, memory mem) %{ ++// match(Set dst (LoadI mem)); ++// %} ++// ++//peephole %{ ++// peepmatch ( loadI storeI ); ++// peepconstraint ( 1.src == 0.dst, 1.mem == 0.mem ); ++// peepreplace ( storeI( 1.mem 1.mem 1.src ) ); ++//%} ++ ++//----------SMARTSPILL RULES--------------------------------------------------- ++// These must follow all instruction definitions as they use the names ++// defined in the instructions definitions. ++ +diff --git a/hotspot/src/cpu/mips/vm/nativeInst_mips.cpp b/hotspot/src/cpu/mips/vm/nativeInst_mips.cpp +new file mode 100644 +index 0000000000..b5cf1841f5 +--- /dev/null ++++ b/hotspot/src/cpu/mips/vm/nativeInst_mips.cpp +@@ -0,0 +1,1827 @@ ++/* ++ * Copyright (c) 1997, 2014, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2015, 2022, Loongson Technology. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ * ++ */ ++ ++#include "precompiled.hpp" ++#include "asm/macroAssembler.hpp" ++#include "compiler/disassembler.hpp" ++#include "memory/resourceArea.hpp" ++#include "nativeInst_mips.hpp" ++#include "oops/oop.inline.hpp" ++#include "runtime/handles.hpp" ++#include "runtime/sharedRuntime.hpp" ++#include "runtime/stubRoutines.hpp" ++#include "utilities/ostream.hpp" ++ ++#include ++ ++#define A0 RA0 ++#define A1 RA1 ++#define A2 RA2 ++#define A3 RA3 ++#define A4 RA4 ++#define A5 RA5 ++#define A6 RA6 ++#define A7 RA7 ++#define T0 RT0 ++#define T1 RT1 ++#define T2 RT2 ++#define T3 RT3 ++#define T8 RT8 ++#define T9 RT9 ++ ++void NativeInstruction::wrote(int offset) { ++ ICache::invalidate_word(addr_at(offset)); ++} ++ ++void NativeInstruction::set_long_at(int offset, long i) { ++ address addr = addr_at(offset); ++ *(long*)addr = i; ++ ICache::invalidate_range(addr, 8); ++} ++ ++static int illegal_instruction_bits = 0; ++ ++int NativeInstruction::illegal_instruction() { ++ if (illegal_instruction_bits == 0) { ++ ResourceMark rm; ++ char buf[40]; ++ CodeBuffer cbuf((address)&buf[0], 20); ++ MacroAssembler* a = new MacroAssembler(&cbuf); ++ address ia = a->pc(); ++ a->brk(11); ++ int bits = *(int*)ia; ++ illegal_instruction_bits = bits; ++ } ++ return illegal_instruction_bits; ++} ++ ++bool NativeInstruction::is_int_branch() { ++ switch(Assembler::opcode(insn_word())) { ++ case Assembler::beq_op: ++ case Assembler::beql_op: ++ case Assembler::bgtz_op: ++ case Assembler::bgtzl_op: ++ case Assembler::blez_op: ++ case Assembler::blezl_op: ++ case Assembler::bne_op: ++ case Assembler::bnel_op: ++ return true; ++ case Assembler::regimm_op: ++ switch(Assembler::rt(insn_word())) { ++ case Assembler::bgez_op: ++ case Assembler::bgezal_op: ++ case Assembler::bgezall_op: ++ case Assembler::bgezl_op: ++ case Assembler::bltz_op: ++ case Assembler::bltzal_op: ++ case Assembler::bltzall_op: ++ case Assembler::bltzl_op: ++ return true; ++ } ++ } ++ ++ return false; ++} ++ ++bool NativeInstruction::is_float_branch() { ++ if (!is_op(Assembler::cop1_op) || ++ !is_rs((Register)Assembler::bc1f_op)) return false; ++ ++ switch(Assembler::rt(insn_word())) { ++ case Assembler::bcf_op: ++ case Assembler::bcfl_op: ++ case Assembler::bct_op: ++ case Assembler::bctl_op: ++ return true; ++ } ++ ++ return false; ++} ++ ++ ++void NativeCall::verify() { ++ // make sure code pattern is actually a call instruction ++ ++ // nop ++ // nop ++ // nop ++ // nop ++ // jal target ++ // nop ++ if ( is_nop() && ++ nativeInstruction_at(addr_at(4))->is_nop() && ++ nativeInstruction_at(addr_at(8))->is_nop() && ++ nativeInstruction_at(addr_at(12))->is_nop() && ++ is_op(int_at(16), Assembler::jal_op) && ++ nativeInstruction_at(addr_at(20))->is_nop() ) { ++ return; ++ } ++ ++ // jal targe ++ // nop ++ if ( is_op(int_at(0), Assembler::jal_op) && ++ nativeInstruction_at(addr_at(4))->is_nop() ) { ++ return; ++ } ++ ++ // li64 ++ if ( is_op(Assembler::lui_op) && ++ is_op(int_at(4), Assembler::ori_op) && ++ is_special_op(int_at(8), Assembler::dsll_op) && ++ is_op(int_at(12), Assembler::ori_op) && ++ is_special_op(int_at(16), Assembler::dsll_op) && ++ is_op(int_at(20), Assembler::ori_op) && ++ is_special_op(int_at(24), Assembler::jalr_op) ) { ++ return; ++ } ++ ++ //lui dst, imm16 ++ //ori dst, dst, imm16 ++ //dsll dst, dst, 16 ++ //ori dst, dst, imm16 ++ if ( is_op(Assembler::lui_op) && ++ is_op (int_at(4), Assembler::ori_op) && ++ is_special_op(int_at(8), Assembler::dsll_op) && ++ is_op (int_at(12), Assembler::ori_op) && ++ is_special_op(int_at(16), Assembler::jalr_op) ) { ++ return; ++ } ++ ++ //ori dst, R0, imm16 ++ //dsll dst, dst, 16 ++ //ori dst, dst, imm16 ++ //nop ++ if ( is_op(Assembler::ori_op) && ++ is_special_op(int_at(4), Assembler::dsll_op) && ++ is_op (int_at(8), Assembler::ori_op) && ++ nativeInstruction_at(addr_at(12))->is_nop() && ++ is_special_op(int_at(16), Assembler::jalr_op) ) { ++ return; ++ } ++ ++ //ori dst, R0, imm16 ++ //dsll dst, dst, 16 ++ //nop ++ //nop ++ if ( is_op(Assembler::ori_op) && ++ is_special_op(int_at(4), Assembler::dsll_op) && ++ nativeInstruction_at(addr_at(8))->is_nop() && ++ nativeInstruction_at(addr_at(12))->is_nop() && ++ is_special_op(int_at(16), Assembler::jalr_op) ) { ++ return; ++ } ++ ++ //daddiu dst, R0, imm16 ++ //nop ++ //nop ++ //nop ++ if ( is_op(Assembler::daddiu_op) && ++ nativeInstruction_at(addr_at(4))->is_nop() && ++ nativeInstruction_at(addr_at(8))->is_nop() && ++ nativeInstruction_at(addr_at(12))->is_nop() && ++ is_special_op(int_at(16), Assembler::jalr_op) ) { ++ return; ++ } ++ ++ // FIXME: why add jr_op here? ++ //daddiu dst, R0, imm16 ++ //nop ++ //nop ++ //nop ++ if ( is_op(Assembler::daddiu_op) && ++ nativeInstruction_at(addr_at(4))->is_nop() && ++ nativeInstruction_at(addr_at(8))->is_nop() && ++ nativeInstruction_at(addr_at(12))->is_nop() && ++ is_special_op(int_at(16), Assembler::jr_op) ) { ++ return; ++ } ++ ++ //lui dst, imm16 ++ //ori dst, dst, imm16 ++ //nop ++ //nop ++ if ( is_op(Assembler::lui_op) && ++ is_op (int_at(4), Assembler::ori_op) && ++ nativeInstruction_at(addr_at(8))->is_nop() && ++ nativeInstruction_at(addr_at(12))->is_nop() && ++ is_special_op(int_at(16), Assembler::jalr_op) ) { ++ return; ++ } ++ ++ //lui dst, imm16 ++ //nop ++ //nop ++ //nop ++ if ( is_op(Assembler::lui_op) && ++ nativeInstruction_at(addr_at(4))->is_nop() && ++ nativeInstruction_at(addr_at(8))->is_nop() && ++ nativeInstruction_at(addr_at(12))->is_nop() && ++ is_special_op(int_at(16), Assembler::jalr_op) ) { ++ return; ++ } ++ ++ //daddiu dst, R0, imm16 ++ //nop ++ if ( is_op(Assembler::daddiu_op) && ++ nativeInstruction_at(addr_at(4))->is_nop() && ++ is_special_op(int_at(8), Assembler::jalr_op) ) { ++ return; ++ } ++ ++ //lui dst, imm16 ++ //ori dst, dst, imm16 ++ if ( is_op(Assembler::lui_op) && ++ is_op (int_at(4), Assembler::ori_op) && ++ is_special_op(int_at(8), Assembler::jalr_op) ) { ++ return; ++ } ++ ++ //lui dst, imm16 ++ //nop ++ if ( is_op(Assembler::lui_op) && ++ nativeInstruction_at(addr_at(4))->is_nop() && ++ is_special_op(int_at(8), Assembler::jalr_op) ) { ++ return; ++ } ++ ++ if (nativeInstruction_at(addr_at(0))->is_trampoline_call()) ++ return; ++ ++ fatal("not a call"); ++} ++ ++address NativeCall::target_addr_for_insn() const { ++ // jal target ++ // nop ++ if ( is_op(int_at(0), Assembler::jal_op) && ++ nativeInstruction_at(addr_at(4))->is_nop()) { ++ int instr_index = int_at(0) & 0x3ffffff; ++ intptr_t target_high = ((intptr_t)addr_at(4)) & 0xfffffffff0000000; ++ intptr_t target = target_high | (instr_index << 2); ++ return (address)target; ++ } ++ ++ // nop ++ // nop ++ // nop ++ // nop ++ // jal target ++ // nop ++ if ( nativeInstruction_at(addr_at(0))->is_nop() && ++ nativeInstruction_at(addr_at(4))->is_nop() && ++ nativeInstruction_at(addr_at(8))->is_nop() && ++ nativeInstruction_at(addr_at(12))->is_nop() && ++ is_op(int_at(16), Assembler::jal_op) && ++ nativeInstruction_at(addr_at(20))->is_nop()) { ++ int instr_index = int_at(16) & 0x3ffffff; ++ intptr_t target_high = ((intptr_t)addr_at(20)) & 0xfffffffff0000000; ++ intptr_t target = target_high | (instr_index << 2); ++ return (address)target; ++ } ++ ++ // li64 ++ if ( is_op(Assembler::lui_op) && ++ is_op(int_at(4), Assembler::ori_op) && ++ is_special_op(int_at(8), Assembler::dsll_op) && ++ is_op(int_at(12), Assembler::ori_op) && ++ is_special_op(int_at(16), Assembler::dsll_op) && ++ is_op(int_at(20), Assembler::ori_op) ) { ++ ++ return (address)Assembler::merge( (intptr_t)(int_at(20) & 0xffff), ++ (intptr_t)(int_at(12) & 0xffff), ++ (intptr_t)(int_at(4) & 0xffff), ++ (intptr_t)(int_at(0) & 0xffff)); ++ } ++ ++ //lui dst, imm16 ++ //ori dst, dst, imm16 ++ //dsll dst, dst, 16 ++ //ori dst, dst, imm16 ++ if ( is_op(Assembler::lui_op) && ++ is_op (int_at(4), Assembler::ori_op) && ++ is_special_op(int_at(8), Assembler::dsll_op) && ++ is_op (int_at(12), Assembler::ori_op) ) { ++ ++ return (address)Assembler::merge( (intptr_t)(int_at(12) & 0xffff), ++ (intptr_t)(int_at(4) & 0xffff), ++ (intptr_t)(int_at(0) & 0xffff), ++ (intptr_t)0); ++ } ++ ++ //lui dst, imm16 ++ //ori dst, dst, imm16 ++ //dsll dst, dst, 16 ++ //ld dst, dst, imm16 ++ if ( is_op(Assembler::lui_op) && ++ is_op (int_at(4), Assembler::ori_op) && ++ is_special_op(int_at(8), Assembler::dsll_op) && ++ is_op (int_at(12), Assembler::ld_op) ) { ++ ++ address dest = (address)Assembler::merge( (intptr_t)0, ++ (intptr_t)(int_at(4) & 0xffff), ++ (intptr_t)(int_at(0) & 0xffff), ++ (intptr_t)0); ++ return dest + Assembler::simm16((intptr_t)int_at(12) & 0xffff); ++ } ++ ++ //ori dst, R0, imm16 ++ //dsll dst, dst, 16 ++ //ori dst, dst, imm16 ++ //nop ++ if ( is_op(Assembler::ori_op) && ++ is_special_op(int_at(4), Assembler::dsll_op) && ++ is_op (int_at(8), Assembler::ori_op) && ++ nativeInstruction_at(addr_at(12))->is_nop()) { ++ ++ return (address)Assembler::merge( (intptr_t)(int_at(8) & 0xffff), ++ (intptr_t)(int_at(0) & 0xffff), ++ (intptr_t)0, ++ (intptr_t)0); ++ } ++ ++ //ori dst, R0, imm16 ++ //dsll dst, dst, 16 ++ //nop ++ //nop ++ if ( is_op(Assembler::ori_op) && ++ is_special_op(int_at(4), Assembler::dsll_op) && ++ nativeInstruction_at(addr_at(8))->is_nop() && ++ nativeInstruction_at(addr_at(12))->is_nop()) { ++ ++ return (address)Assembler::merge( (intptr_t)(0), ++ (intptr_t)(int_at(0) & 0xffff), ++ (intptr_t)0, ++ (intptr_t)0); ++ } ++ ++ //daddiu dst, R0, imm16 ++ //nop ++ //nop <-- optional ++ //nop <-- optional ++ if ( is_op(Assembler::daddiu_op) && ++ nativeInstruction_at(addr_at(4))->is_nop() ) { ++ ++ int sign = int_at(0) & 0x8000; ++ if (sign == 0) { ++ return (address)Assembler::merge( (intptr_t)(int_at(0) & 0xffff), ++ (intptr_t)0, ++ (intptr_t)0, ++ (intptr_t)0); ++ } else { ++ return (address)Assembler::merge( (intptr_t)(int_at(0) & 0xffff), ++ (intptr_t)(0xffff), ++ (intptr_t)(0xffff), ++ (intptr_t)(0xffff)); ++ } ++ } ++ ++ //lui dst, imm16 ++ //ori dst, dst, imm16 ++ //nop <-- optional ++ //nop <-- optional ++ if ( is_op(Assembler::lui_op) && ++ is_op (int_at(4), Assembler::ori_op) ) { ++ ++ int sign = int_at(0) & 0x8000; ++ if (sign == 0) { ++ return (address)Assembler::merge( (intptr_t)(int_at(4) & 0xffff), ++ (intptr_t)(int_at(0) & 0xffff), ++ (intptr_t)0, ++ (intptr_t)0); ++ } else { ++ return (address)Assembler::merge( (intptr_t)(int_at(4) & 0xffff), ++ (intptr_t)(int_at(0) & 0xffff), ++ (intptr_t)(0xffff), ++ (intptr_t)(0xffff)); ++ } ++ } ++ ++ //lui dst, imm16 ++ //nop ++ //nop <-- optional ++ //nop <-- optional ++ if ( is_op(Assembler::lui_op) && ++ nativeInstruction_at(addr_at(4))->is_nop() ) { ++ ++ int sign = int_at(0) & 0x8000; ++ if (sign == 0) { ++ return (address)Assembler::merge( (intptr_t)0, ++ (intptr_t)(int_at(0) & 0xffff), ++ (intptr_t)0, ++ (intptr_t)0); ++ } else { ++ return (address)Assembler::merge( (intptr_t)0, ++ (intptr_t)(int_at(0) & 0xffff), ++ (intptr_t)(0xffff), ++ (intptr_t)(0xffff)); ++ } ++ } ++ ++ tty->print_cr("not a call: addr = " INTPTR_FORMAT , p2i(addr_at(0))); ++ tty->print_cr("======= Start decoding at addr = " INTPTR_FORMAT " =======", p2i(addr_at(0))); ++ Disassembler::decode(addr_at(0) - 2 * 4, addr_at(0) + 8 * 4, tty); ++ tty->print_cr("======= End of decoding ======="); ++ fatal("not a call"); ++ return NULL; ++} ++ ++// Extract call destination from a NativeCall. The call might use a trampoline stub. ++address NativeCall::destination() const { ++ address addr = (address)this; ++ address destination = target_addr_for_insn(); ++ // Do we use a trampoline stub for this call? ++ // Trampoline stubs are located behind the main code. ++ if (destination > addr) { ++ // Filter out recursive method invocation (call to verified/unverified entry point). ++ CodeBlob* cb = CodeCache::find_blob_unsafe(addr); // Else we get assertion if nmethod is zombie. ++ assert(cb && cb->is_nmethod(), "sanity"); ++ nmethod *nm = (nmethod *)cb; ++ NativeInstruction* ni = nativeInstruction_at(addr); ++ if (nm->stub_contains(destination) && ni->is_trampoline_call()) { ++ // Yes we do, so get the destination from the trampoline stub. ++ const address trampoline_stub_addr = destination; ++ destination = nativeCallTrampolineStub_at(trampoline_stub_addr)->destination(); ++ } ++ } ++ return destination; ++} ++ ++// Similar to replace_mt_safe, but just changes the destination. The ++// important thing is that free-running threads are able to execute this ++// call instruction at all times. ++// ++// Used in the runtime linkage of calls; see class CompiledIC. ++// ++// Add parameter assert_lock to switch off assertion ++// during code generation, where no patching lock is needed. ++void NativeCall::set_destination_mt_safe(address dest, bool assert_lock) { ++ assert(!assert_lock || ++ (Patching_lock->is_locked() || SafepointSynchronize::is_at_safepoint()), ++ "concurrent code patching"); ++ ++ ResourceMark rm; ++ address addr_call = addr_at(0); ++ assert(NativeCall::is_call_at(addr_call), "unexpected code at call site"); ++ // Patch the constant in the call's trampoline stub. ++ if (MacroAssembler::reachable_from_cache()) { ++ set_destination(dest); ++ } else { ++ address trampoline_stub_addr = nativeCall_at(addr_call)->target_addr_for_insn(); ++ assert (get_trampoline() != NULL && trampoline_stub_addr == get_trampoline(), "we need a trampoline"); ++ nativeCallTrampolineStub_at(trampoline_stub_addr)->set_destination(dest); ++ } ++} ++ ++ ++address NativeCall::get_trampoline() { ++ address call_addr = addr_at(0); ++ ++ CodeBlob *code = CodeCache::find_blob(call_addr); ++ assert(code != NULL, "Could not find the containing code blob"); ++ ++ // If the codeBlob is not a nmethod, this is because we get here from the ++ // CodeBlob constructor, which is called within the nmethod constructor. ++ return trampoline_stub_Relocation::get_trampoline_for(call_addr, (nmethod*)code); ++} ++ ++// manual implementation of GSSQ ++// ++// 00000001200009c0 : ++// 1200009c0: 0085202d daddu a0, a0, a1 ++// 1200009c4: e8860027 gssq a2, a3, 0(a0) ++// 1200009c8: 03e00008 jr ra ++// 1200009cc: 00000000 nop ++// ++typedef void (* atomic_store128_ptr)(long *addr, int offset, long low64, long hi64); ++ ++static int *buf; ++ ++static atomic_store128_ptr get_atomic_store128_func() { ++ assert(UseLEXT1, "UseLEXT1 must be true"); ++ static atomic_store128_ptr p = NULL; ++ if (p != NULL) ++ return p; ++ ++ buf = (int *)mmap(NULL, 1024, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_PRIVATE | MAP_ANONYMOUS, ++ -1, 0); ++ buf[0] = 0x0085202d; ++ buf[1] = (0x3a << 26) | (4 << 21) | (6 << 16) | 0x27; /* gssq $a2, $a3, 0($a0) */ ++ buf[2] = 0x03e00008; ++ buf[3] = 0; ++ ++ asm("sync"); ++ p = (atomic_store128_ptr)buf; ++ return p; ++} ++ ++void NativeCall::patch_on_jal_only(address dst) { ++ long dest = ((long)dst - (((long)addr_at(4)) & 0xfffffffff0000000))>>2; ++ if ((dest >= 0) && (dest < (1<<26))) { ++ jint jal_inst = (Assembler::jal_op << 26) | dest; ++ set_int_at(0, jal_inst); ++ ICache::invalidate_range(addr_at(0), 4); ++ } else { ++ ShouldNotReachHere(); ++ } ++} ++ ++void NativeCall::patch_on_trampoline(address dest) { ++ assert(nativeInstruction_at(addr_at(0))->is_trampoline_call(), "unexpected code at call site"); ++ jlong dst = (jlong) dest; ++ //lui dst, imm16 ++ //ori dst, dst, imm16 ++ //dsll dst, dst, 16 ++ //ld dst, dst, imm16 ++ if ((dst> 0) && Assembler::is_simm16(dst >> 32)) { ++ dst += (dst & 0x8000) << 1; ++ set_int_at(0, (int_at(0) & 0xffff0000) | (Assembler::split_low(dst >> 32) & 0xffff)); ++ set_int_at(4, (int_at(4) & 0xffff0000) | (Assembler::split_low(dst >> 16) & 0xffff)); ++ set_int_at(12, (int_at(12) & 0xffff0000) | (Assembler::split_low(dst) & 0xffff)); ++ ++ ICache::invalidate_range(addr_at(0), 24); ++ } else { ++ ShouldNotReachHere(); ++ } ++} ++ ++void NativeCall::patch_on_jal_gs(address dst) { ++ long dest = ((long)dst - (((long)addr_at(20)) & 0xfffffffff0000000))>>2; ++ if ((dest >= 0) && (dest < (1<<26))) { ++ jint jal_inst = (Assembler::jal_op << 26) | dest; ++ set_int_at(16, jal_inst); ++ ICache::invalidate_range(addr_at(16), 4); ++ } else { ++ ShouldNotReachHere(); ++ } ++} ++ ++void NativeCall::patch_on_jal(address dst) { ++ patch_on_jal_gs(dst); ++} ++ ++void NativeCall::patch_on_jalr_gs(address dst) { ++ patch_set48_gs(dst); ++} ++ ++void NativeCall::patch_on_jalr(address dst) { ++ patch_set48(dst); ++} ++ ++void NativeCall::patch_set48_gs(address dest) { ++ jlong value = (jlong) dest; ++ int rt_reg = (int_at(0) & (0x1f << 16)); ++ ++ if (rt_reg == 0) rt_reg = 25 << 16; // r25 is T9 ++ ++ int rs_reg = rt_reg << 5; ++ int rd_reg = rt_reg >> 5; ++ ++ int hi = (int)(value >> 32); ++ int lo = (int)(value & ~0); ++ int count = 0; ++ int insts[4] = {0, 0, 0, 0}; ++ ++ if (value == lo) { // 32-bit integer ++ if (Assembler::is_simm16(value)) { ++ insts[count] = (Assembler::daddiu_op << 26) | rt_reg | Assembler::split_low(value); ++ count += 1; ++ } else { ++ insts[count] = (Assembler::lui_op << 26) | rt_reg | Assembler::split_low(value >> 16); ++ count += 1; ++ if (Assembler::split_low(value)) { ++ insts[count] = (Assembler::ori_op << 26) | rs_reg | rt_reg | Assembler::split_low(value); ++ count += 1; ++ } ++ } ++ } else if (hi == 0) { // hardware zero-extends to upper 32 ++ insts[count] = (Assembler::ori_op << 26) | rt_reg | Assembler::split_low(julong(value) >> 16); ++ count += 1; ++ insts[count] = (Assembler::dsll_op) | rt_reg | rd_reg | (16 << 6); ++ count += 1; ++ if (Assembler::split_low(value)) { ++ insts[count] = (Assembler::ori_op << 26) | rs_reg | rt_reg | Assembler::split_low(value); ++ count += 1; ++ } ++ } else if ((value> 0) && Assembler::is_simm16(value >> 32)) { ++ insts[count] = (Assembler::lui_op << 26) | rt_reg | Assembler::split_low(value >> 32); ++ count += 1; ++ insts[count] = (Assembler::ori_op << 26) | rs_reg | rt_reg | Assembler::split_low(value >> 16); ++ count += 1; ++ insts[count] = (Assembler::dsll_op) | rt_reg | rd_reg | (16 << 6); ++ count += 1; ++ insts[count] = (Assembler::ori_op << 26) | rs_reg | rt_reg | Assembler::split_low(value); ++ count += 1; ++ } else { ++ tty->print_cr("dest = 0x%lx", value); ++ guarantee(false, "Not supported yet !"); ++ } ++ ++ while (count < 4) { ++ insts[count] = 0; ++ count++; ++ } ++ ++ guarantee(((long)addr_at(0) % (BytesPerWord * 2)) == 0, "must be aligned"); ++ atomic_store128_ptr func = get_atomic_store128_func(); ++ (*func)((long *)addr_at(0), 0, *(long *)&insts[0], *(long *)&insts[2]); ++ ++ ICache::invalidate_range(addr_at(0), 16); ++} ++ ++void NativeCall::patch_set32_gs(address dest) { ++ jlong value = (jlong) dest; ++ int rt_reg = (int_at(0) & (0x1f << 16)); ++ ++ if (rt_reg == 0) rt_reg = 25 << 16; // r25 is T9 ++ ++ int rs_reg = rt_reg << 5; ++ int rd_reg = rt_reg >> 5; ++ ++ int hi = (int)(value >> 32); ++ int lo = (int)(value & ~0); ++ ++ int count = 0; ++ ++ int insts[2] = {0, 0}; ++ ++ if (value == lo) { // 32-bit integer ++ if (Assembler::is_simm16(value)) { ++ //daddiu(d, R0, value); ++ //set_int_at(count << 2, (Assembler::daddiu_op << 26) | rt_reg | Assembler::split_low(value)); ++ insts[count] = (Assembler::daddiu_op << 26) | rt_reg | Assembler::split_low(value); ++ count += 1; ++ } else { ++ //lui(d, split_low(value >> 16)); ++ //set_int_at(count << 2, (Assembler::lui_op << 26) | rt_reg | Assembler::split_low(value >> 16)); ++ insts[count] = (Assembler::lui_op << 26) | rt_reg | Assembler::split_low(value >> 16); ++ count += 1; ++ if (Assembler::split_low(value)) { ++ //ori(d, d, split_low(value)); ++ //set_int_at(count << 2, (Assembler::ori_op << 26) | rs_reg | rt_reg | Assembler::split_low(value)); ++ insts[count] = (Assembler::ori_op << 26) | rs_reg | rt_reg | Assembler::split_low(value); ++ count += 1; ++ } ++ } ++ } else { ++ tty->print_cr("dest = 0x%lx", value); ++ guarantee(false, "Not supported yet !"); ++ } ++ ++ while (count < 2) { ++ //nop(); ++ //set_int_at(count << 2, 0); ++ insts[count] = 0; ++ count++; ++ } ++ ++ long inst = insts[1]; ++ inst = inst << 32; ++ inst = inst + insts[0]; ++ ++ set_long_at(0, inst); ++} ++ ++void NativeCall::patch_set48(address dest) { ++ jlong value = (jlong) dest; ++ int rt_reg = (int_at(0) & (0x1f << 16)); ++ ++ if (rt_reg == 0) rt_reg = 25 << 16; // r25 is T9 ++ ++ int rs_reg = rt_reg << 5; ++ int rd_reg = rt_reg >> 5; ++ ++ int hi = (int)(value >> 32); ++ int lo = (int)(value & ~0); ++ ++ int count = 0; ++ ++ if (value == lo) { // 32-bit integer ++ if (Assembler::is_simm16(value)) { ++ //daddiu(d, R0, value); ++ set_int_at(count << 2, (Assembler::daddiu_op << 26) | rt_reg | Assembler::split_low(value)); ++ count += 1; ++ } else { ++ //lui(d, split_low(value >> 16)); ++ set_int_at(count << 2, (Assembler::lui_op << 26) | rt_reg | Assembler::split_low(value >> 16)); ++ count += 1; ++ if (Assembler::split_low(value)) { ++ //ori(d, d, split_low(value)); ++ set_int_at(count << 2, (Assembler::ori_op << 26) | rs_reg | rt_reg | Assembler::split_low(value)); ++ count += 1; ++ } ++ } ++ } else if (hi == 0) { // hardware zero-extends to upper 32 ++ //ori(d, R0, julong(value) >> 16); ++ set_int_at(count << 2, (Assembler::ori_op << 26) | rt_reg | Assembler::split_low(julong(value) >> 16)); ++ count += 1; ++ //dsll(d, d, 16); ++ set_int_at(count << 2, (Assembler::dsll_op) | rt_reg | rd_reg | (16 << 6)); ++ count += 1; ++ if (Assembler::split_low(value)) { ++ //ori(d, d, split_low(value)); ++ set_int_at(count << 2, (Assembler::ori_op << 26) | rs_reg | rt_reg | Assembler::split_low(value)); ++ count += 1; ++ } ++ } else if ((value> 0) && Assembler::is_simm16(value >> 32)) { ++ //lui(d, value >> 32); ++ set_int_at(count << 2, (Assembler::lui_op << 26) | rt_reg | Assembler::split_low(value >> 32)); ++ count += 1; ++ //ori(d, d, split_low(value >> 16)); ++ set_int_at(count << 2, (Assembler::ori_op << 26) | rs_reg | rt_reg | Assembler::split_low(value >> 16)); ++ count += 1; ++ //dsll(d, d, 16); ++ set_int_at(count << 2, (Assembler::dsll_op) | rt_reg | rd_reg | (16 << 6)); ++ count += 1; ++ //ori(d, d, split_low(value)); ++ set_int_at(count << 2, (Assembler::ori_op << 26) | rs_reg | rt_reg | Assembler::split_low(value)); ++ count += 1; ++ } else { ++ tty->print_cr("dest = 0x%lx", value); ++ guarantee(false, "Not supported yet !"); ++ } ++ ++ while (count < 4) { ++ //nop(); ++ set_int_at(count << 2, 0); ++ count++; ++ } ++ ++ ICache::invalidate_range(addr_at(0), 16); ++} ++ ++void NativeCall::patch_set32(address dest) { ++ patch_set32_gs(dest); ++} ++ ++void NativeCall::set_destination(address dest) { ++ OrderAccess::fence(); ++ ++ // li64 ++ if (is_special_op(int_at(16), Assembler::dsll_op)) { ++ int first_word = int_at(0); ++ set_int_at(0, 0x1000ffff); /* .1: b .1 */ ++ set_int_at(4, (int_at(4) & 0xffff0000) | (Assembler::split_low((intptr_t)dest >> 32) & 0xffff)); ++ set_int_at(12, (int_at(12) & 0xffff0000) | (Assembler::split_low((intptr_t)dest >> 16) & 0xffff)); ++ set_int_at(20, (int_at(20) & 0xffff0000) | (Assembler::split_low((intptr_t)dest) & 0xffff)); ++ set_int_at(0, (first_word & 0xffff0000) | (Assembler::split_low((intptr_t)dest >> 48) & 0xffff)); ++ ICache::invalidate_range(addr_at(0), 24); ++ } else if (is_op(int_at(16), Assembler::jal_op)) { ++ if (UseLEXT1) { ++ patch_on_jal_gs(dest); ++ } else { ++ patch_on_jal(dest); ++ } ++ } else if (is_op(int_at(0), Assembler::jal_op)) { ++ patch_on_jal_only(dest); ++ } else if (is_special_op(int_at(16), Assembler::jalr_op)) { ++ if (UseLEXT1) { ++ patch_on_jalr_gs(dest); ++ } else { ++ patch_on_jalr(dest); ++ } ++ } else if (is_special_op(int_at(8), Assembler::jalr_op)) { ++ guarantee(!os::is_MP() || (((long)addr_at(0) % 8) == 0), "destination must be aligned by 8"); ++ if (UseLEXT1) { ++ patch_set32_gs(dest); ++ } else { ++ patch_set32(dest); ++ } ++ ICache::invalidate_range(addr_at(0), 8); ++ } else { ++ fatal("not a call"); ++ } ++} ++ ++void NativeCall::print() { ++ tty->print_cr(PTR_FORMAT ": call " PTR_FORMAT, ++ p2i(instruction_address()), p2i(destination())); ++} ++ ++// Inserts a native call instruction at a given pc ++void NativeCall::insert(address code_pos, address entry) { ++ NativeCall *call = nativeCall_at(code_pos); ++ CodeBuffer cb(call->addr_at(0), instruction_size); ++ MacroAssembler masm(&cb); ++#define __ masm. ++ __ li48(T9, (long)entry); ++ __ jalr (); ++ __ delayed()->nop(); ++#undef __ ++ ++ ICache::invalidate_range(call->addr_at(0), instruction_size); ++} ++ ++// MT-safe patching of a call instruction. ++// First patches first word of instruction to two jmp's that jmps to them ++// selfs (spinlock). Then patches the last byte, and then atomicly replaces ++// the jmp's with the first 4 byte of the new instruction. ++void NativeCall::replace_mt_safe(address instr_addr, address code_buffer) { ++ Unimplemented(); ++} ++ ++//------------------------------------------------------------------- ++ ++void NativeMovConstReg::verify() { ++ // li64 ++ if ( is_op(Assembler::lui_op) && ++ is_op(int_at(4), Assembler::ori_op) && ++ is_special_op(int_at(8), Assembler::dsll_op) && ++ is_op(int_at(12), Assembler::ori_op) && ++ is_special_op(int_at(16), Assembler::dsll_op) && ++ is_op(int_at(20), Assembler::ori_op) ) { ++ return; ++ } ++ ++ //lui dst, imm16 ++ //ori dst, dst, imm16 ++ //dsll dst, dst, 16 ++ //ori dst, dst, imm16 ++ if ( is_op(Assembler::lui_op) && ++ is_op (int_at(4), Assembler::ori_op) && ++ is_special_op(int_at(8), Assembler::dsll_op) && ++ is_op (int_at(12), Assembler::ori_op) ) { ++ return; ++ } ++ ++ //ori dst, R0, imm16 ++ //dsll dst, dst, 16 ++ //ori dst, dst, imm16 ++ //nop ++ if ( is_op(Assembler::ori_op) && ++ is_special_op(int_at(4), Assembler::dsll_op) && ++ is_op (int_at(8), Assembler::ori_op) && ++ nativeInstruction_at(addr_at(12))->is_nop()) { ++ return; ++ } ++ ++ //ori dst, R0, imm16 ++ //dsll dst, dst, 16 ++ //nop ++ //nop ++ if ( is_op(Assembler::ori_op) && ++ is_special_op(int_at(4), Assembler::dsll_op) && ++ nativeInstruction_at(addr_at(8))->is_nop() && ++ nativeInstruction_at(addr_at(12))->is_nop()) { ++ return; ++ } ++ ++ //daddiu dst, R0, imm16 ++ //nop ++ //nop ++ //nop ++ if ( is_op(Assembler::daddiu_op) && ++ nativeInstruction_at(addr_at(4))->is_nop() && ++ nativeInstruction_at(addr_at(8))->is_nop() && ++ nativeInstruction_at(addr_at(12))->is_nop() ) { ++ return; ++ } ++ ++ //lui dst, imm16 ++ //ori dst, dst, imm16 ++ //nop ++ //nop ++ if ( is_op(Assembler::lui_op) && ++ is_op (int_at(4), Assembler::ori_op) && ++ nativeInstruction_at(addr_at(8))->is_nop() && ++ nativeInstruction_at(addr_at(12))->is_nop() ) { ++ return; ++ } ++ ++ //lui dst, imm16 ++ //nop ++ //nop ++ //nop ++ if ( is_op(Assembler::lui_op) && ++ nativeInstruction_at(addr_at(4))->is_nop() && ++ nativeInstruction_at(addr_at(8))->is_nop() && ++ nativeInstruction_at(addr_at(12))->is_nop() ) { ++ return; ++ } ++ ++ fatal("not a mov reg, imm64/imm48"); ++} ++ ++void NativeMovConstReg::print() { ++ tty->print_cr(PTR_FORMAT ": mov reg, " INTPTR_FORMAT, ++ p2i(instruction_address()), data()); ++} ++ ++intptr_t NativeMovConstReg::data() const { ++ // li64 ++ if ( is_op(Assembler::lui_op) && ++ is_op(int_at(4), Assembler::ori_op) && ++ is_special_op(int_at(8), Assembler::dsll_op) && ++ is_op(int_at(12), Assembler::ori_op) && ++ is_special_op(int_at(16), Assembler::dsll_op) && ++ is_op(int_at(20), Assembler::ori_op) ) { ++ ++ return Assembler::merge( (intptr_t)(int_at(20) & 0xffff), ++ (intptr_t)(int_at(12) & 0xffff), ++ (intptr_t)(int_at(4) & 0xffff), ++ (intptr_t)(int_at(0) & 0xffff)); ++ } ++ ++ //lui dst, imm16 ++ //ori dst, dst, imm16 ++ //dsll dst, dst, 16 ++ //ori dst, dst, imm16 ++ if ( is_op(Assembler::lui_op) && ++ is_op (int_at(4), Assembler::ori_op) && ++ is_special_op(int_at(8), Assembler::dsll_op) && ++ is_op (int_at(12), Assembler::ori_op) ) { ++ ++ return Assembler::merge( (intptr_t)(int_at(12) & 0xffff), ++ (intptr_t)(int_at(4) & 0xffff), ++ (intptr_t)(int_at(0) & 0xffff), ++ (intptr_t)0); ++ } ++ ++ //ori dst, R0, imm16 ++ //dsll dst, dst, 16 ++ //ori dst, dst, imm16 ++ //nop ++ if ( is_op(Assembler::ori_op) && ++ is_special_op(int_at(4), Assembler::dsll_op) && ++ is_op (int_at(8), Assembler::ori_op) && ++ nativeInstruction_at(addr_at(12))->is_nop()) { ++ ++ return Assembler::merge( (intptr_t)(int_at(8) & 0xffff), ++ (intptr_t)(int_at(0) & 0xffff), ++ (intptr_t)0, ++ (intptr_t)0); ++ } ++ ++ //ori dst, R0, imm16 ++ //dsll dst, dst, 16 ++ //nop ++ //nop ++ if ( is_op(Assembler::ori_op) && ++ is_special_op(int_at(4), Assembler::dsll_op) && ++ nativeInstruction_at(addr_at(8))->is_nop() && ++ nativeInstruction_at(addr_at(12))->is_nop()) { ++ ++ return Assembler::merge( (intptr_t)(0), ++ (intptr_t)(int_at(0) & 0xffff), ++ (intptr_t)0, ++ (intptr_t)0); ++ } ++ ++ //daddiu dst, R0, imm16 ++ //nop ++ //nop ++ //nop ++ if ( is_op(Assembler::daddiu_op) && ++ nativeInstruction_at(addr_at(4))->is_nop() && ++ nativeInstruction_at(addr_at(8))->is_nop() && ++ nativeInstruction_at(addr_at(12))->is_nop() ) { ++ ++ int sign = int_at(0) & 0x8000; ++ if (sign == 0) { ++ return Assembler::merge( (intptr_t)(int_at(0) & 0xffff), ++ (intptr_t)0, ++ (intptr_t)0, ++ (intptr_t)0); ++ } else { ++ return Assembler::merge( (intptr_t)(int_at(0) & 0xffff), ++ (intptr_t)(0xffff), ++ (intptr_t)(0xffff), ++ (intptr_t)(0xffff)); ++ } ++ } ++ ++ //lui dst, imm16 ++ //ori dst, dst, imm16 ++ //nop ++ //nop ++ if ( is_op(Assembler::lui_op) && ++ is_op (int_at(4), Assembler::ori_op) && ++ nativeInstruction_at(addr_at(8))->is_nop() && ++ nativeInstruction_at(addr_at(12))->is_nop() ) { ++ ++ int sign = int_at(0) & 0x8000; ++ if (sign == 0) { ++ return Assembler::merge( (intptr_t)(int_at(4) & 0xffff), ++ (intptr_t)(int_at(0) & 0xffff), ++ (intptr_t)0, ++ (intptr_t)0); ++ } else { ++ return Assembler::merge( (intptr_t)(int_at(4) & 0xffff), ++ (intptr_t)(int_at(0) & 0xffff), ++ (intptr_t)(0xffff), ++ (intptr_t)(0xffff)); ++ } ++ } ++ ++ //lui dst, imm16 ++ //nop ++ //nop ++ //nop ++ if ( is_op(Assembler::lui_op) && ++ nativeInstruction_at(addr_at(4))->is_nop() && ++ nativeInstruction_at(addr_at(8))->is_nop() && ++ nativeInstruction_at(addr_at(12))->is_nop() ) { ++ ++ int sign = int_at(0) & 0x8000; ++ if (sign == 0) { ++ return Assembler::merge( (intptr_t)0, ++ (intptr_t)(int_at(0) & 0xffff), ++ (intptr_t)0, ++ (intptr_t)0); ++ } else { ++ return Assembler::merge( (intptr_t)0, ++ (intptr_t)(int_at(0) & 0xffff), ++ (intptr_t)(0xffff), ++ (intptr_t)(0xffff)); ++ } ++ } ++ ++ fatal("not a mov reg, imm64/imm48"); ++ return 0; // unreachable ++} ++ ++void NativeMovConstReg::patch_set48(intptr_t x) { ++ jlong value = (jlong) x; ++ int rt_reg = (int_at(0) & (0x1f << 16)); ++ int rs_reg = rt_reg << 5; ++ int rd_reg = rt_reg >> 5; ++ ++ int hi = (int)(value >> 32); ++ int lo = (int)(value & ~0); ++ ++ int count = 0; ++ ++ if (value == lo) { // 32-bit integer ++ if (Assembler::is_simm16(value)) { ++ //daddiu(d, R0, value); ++ set_int_at(count << 2, (Assembler::daddiu_op << 26) | rt_reg | Assembler::split_low(value)); ++ count += 1; ++ } else { ++ //lui(d, split_low(value >> 16)); ++ set_int_at(count << 2, (Assembler::lui_op << 26) | rt_reg | Assembler::split_low(value >> 16)); ++ count += 1; ++ if (Assembler::split_low(value)) { ++ //ori(d, d, split_low(value)); ++ set_int_at(count << 2, (Assembler::ori_op << 26) | rs_reg | rt_reg | Assembler::split_low(value)); ++ count += 1; ++ } ++ } ++ } else if (hi == 0) { // hardware zero-extends to upper 32 ++ set_int_at(count << 2, (Assembler::ori_op << 26) | rt_reg | Assembler::split_low(julong(value) >> 16)); ++ count += 1; ++ set_int_at(count << 2, (Assembler::dsll_op) | rt_reg | rd_reg | (16 << 6)); ++ count += 1; ++ if (Assembler::split_low(value)) { ++ set_int_at(count << 2, (Assembler::ori_op << 26) | rs_reg | rt_reg | Assembler::split_low(value)); ++ count += 1; ++ } ++ } else if ((value> 0) && Assembler::is_simm16(value >> 32)) { ++ set_int_at(count << 2, (Assembler::lui_op << 26) | rt_reg | Assembler::split_low(value >> 32)); ++ count += 1; ++ set_int_at(count << 2, (Assembler::ori_op << 26) | rs_reg | rt_reg | Assembler::split_low(value >> 16)); ++ count += 1; ++ set_int_at(count << 2, (Assembler::dsll_op) | rt_reg | rd_reg | (16 << 6)); ++ count += 1; ++ set_int_at(count << 2, (Assembler::ori_op << 26) | rs_reg | rt_reg | Assembler::split_low(value)); ++ count += 1; ++ } else { ++ tty->print_cr("value = 0x%lx", value); ++ guarantee(false, "Not supported yet !"); ++ } ++ ++ while (count < 4) { ++ set_int_at(count << 2, 0); ++ count++; ++ } ++} ++ ++void NativeMovConstReg::set_data(intptr_t x, intptr_t o) { ++ // li64 or li48 ++ if ((!nativeInstruction_at(addr_at(12))->is_nop()) && is_special_op(int_at(16), Assembler::dsll_op) && is_op(long_at(20), Assembler::ori_op)) { ++ set_int_at(0, (int_at(0) & 0xffff0000) | (Assembler::split_low((intptr_t)x >> 48) & 0xffff)); ++ set_int_at(4, (int_at(4) & 0xffff0000) | (Assembler::split_low((intptr_t)x >> 32) & 0xffff)); ++ set_int_at(12, (int_at(12) & 0xffff0000) | (Assembler::split_low((intptr_t)x >> 16) & 0xffff)); ++ set_int_at(20, (int_at(20) & 0xffff0000) | (Assembler::split_low((intptr_t)x) & 0xffff)); ++ } else { ++ patch_set48(x); ++ } ++ ++ ICache::invalidate_range(addr_at(0), 24); ++ ++ // Find and replace the oop/metadata corresponding to this ++ // instruction in oops section. ++ CodeBlob* blob = CodeCache::find_blob_unsafe(instruction_address()); ++ nmethod* nm = blob->as_nmethod_or_null(); ++ if (nm != NULL) { ++ o = o ? o : x; ++ RelocIterator iter(nm, instruction_address(), next_instruction_address()); ++ while (iter.next()) { ++ if (iter.type() == relocInfo::oop_type) { ++ oop* oop_addr = iter.oop_reloc()->oop_addr(); ++ *oop_addr = cast_to_oop(o); ++ break; ++ } else if (iter.type() == relocInfo::metadata_type) { ++ Metadata** metadata_addr = iter.metadata_reloc()->metadata_addr(); ++ *metadata_addr = (Metadata*)o; ++ break; ++ } ++ } ++ } ++} ++ ++//------------------------------------------------------------------- ++ ++int NativeMovRegMem::offset() const{ ++ if (is_immediate()) ++ return (short)(int_at(instruction_offset)&0xffff); ++ else ++ return Assembler::merge(int_at(hiword_offset)&0xffff, long_at(instruction_offset)&0xffff); ++} ++ ++void NativeMovRegMem::set_offset(int x) { ++ if (is_immediate()) { ++ assert(Assembler::is_simm16(x), "just check"); ++ set_int_at(0, (int_at(0)&0xffff0000) | (x&0xffff) ); ++ if (is_64ldst()) { ++ assert(Assembler::is_simm16(x+4), "just check"); ++ set_int_at(4, (int_at(4)&0xffff0000) | ((x+4)&0xffff) ); ++ } ++ } else { ++ set_int_at(0, (int_at(0) & 0xffff0000) | (Assembler::split_high(x) & 0xffff)); ++ set_int_at(4, (int_at(4) & 0xffff0000) | (Assembler::split_low(x) & 0xffff)); ++ } ++ ICache::invalidate_range(addr_at(0), 8); ++} ++ ++void NativeMovRegMem::verify() { ++ int offset = 0; ++ ++ if ( Assembler::opcode(int_at(0)) == Assembler::lui_op ) { ++ ++ if ( Assembler::opcode(int_at(4)) != Assembler::ori_op ) { ++ fatal ("not a mov [reg+offs], reg instruction"); ++ } ++ ++ offset += 12; ++ } ++ ++ switch(Assembler::opcode(int_at(offset))) { ++ case Assembler::lb_op: ++ case Assembler::lbu_op: ++ case Assembler::lh_op: ++ case Assembler::lhu_op: ++ case Assembler::lw_op: ++ case Assembler::lwu_op: ++ case Assembler::ld_op: ++ case Assembler::lwc1_op: ++ case Assembler::ldc1_op: ++ case Assembler::sb_op: ++ case Assembler::sh_op: ++ case Assembler::sw_op: ++ case Assembler::sd_op: ++ case Assembler::swc1_op: ++ case Assembler::sdc1_op: ++ break; ++ default: ++ fatal ("not a mov [reg+offs], reg instruction"); ++ } ++} ++ ++ ++void NativeMovRegMem::print() { ++ tty->print_cr(PTR_FORMAT ": mov reg, [reg + %x]", p2i(instruction_address()), offset()); ++} ++ ++bool NativeInstruction::is_sigill_zombie_not_entrant() { ++ return uint_at(0) == NativeIllegalInstruction::instruction_code; ++} ++ ++void NativeIllegalInstruction::insert(address code_pos) { ++ *(juint*)code_pos = instruction_code; ++ ICache::invalidate_range(code_pos, instruction_size); ++} ++ ++void NativeJump::verify() { ++ assert(((NativeInstruction *)this)->is_jump() || ++ ((NativeInstruction *)this)->is_cond_jump(), "not a general jump instruction"); ++} ++ ++void NativeJump::patch_set48_gs(address dest) { ++ jlong value = (jlong) dest; ++ int rt_reg = (int_at(0) & (0x1f << 16)); ++ ++ if (rt_reg == 0) rt_reg = 25 << 16; // r25 is T9 ++ ++ int rs_reg = rt_reg << 5; ++ int rd_reg = rt_reg >> 5; ++ ++ int hi = (int)(value >> 32); ++ int lo = (int)(value & ~0); ++ ++ int count = 0; ++ ++ int insts[4] = {0, 0, 0, 0}; ++ ++ if (value == lo) { // 32-bit integer ++ if (Assembler::is_simm16(value)) { ++ insts[count] = (Assembler::daddiu_op << 26) | rt_reg | Assembler::split_low(value); ++ count += 1; ++ } else { ++ insts[count] = (Assembler::lui_op << 26) | rt_reg | Assembler::split_low(value >> 16); ++ count += 1; ++ if (Assembler::split_low(value)) { ++ insts[count] = (Assembler::ori_op << 26) | rs_reg | rt_reg | Assembler::split_low(value); ++ count += 1; ++ } ++ } ++ } else if (hi == 0) { // hardware zero-extends to upper 32 ++ insts[count] = (Assembler::ori_op << 26) | rt_reg | Assembler::split_low(julong(value) >> 16); ++ count += 1; ++ insts[count] = (Assembler::dsll_op) | rt_reg | rd_reg | (16 << 6); ++ count += 1; ++ if (Assembler::split_low(value)) { ++ insts[count] = (Assembler::ori_op << 26) | rs_reg | rt_reg | Assembler::split_low(value); ++ count += 1; ++ } ++ } else if ((value> 0) && Assembler::is_simm16(value >> 32)) { ++ insts[count] = (Assembler::lui_op << 26) | rt_reg | Assembler::split_low(value >> 32); ++ count += 1; ++ insts[count] = (Assembler::ori_op << 26) | rs_reg | rt_reg | Assembler::split_low(value >> 16); ++ count += 1; ++ insts[count] = (Assembler::dsll_op) | rt_reg | rd_reg | (16 << 6); ++ count += 1; ++ insts[count] = (Assembler::ori_op << 26) | rs_reg | rt_reg | Assembler::split_low(value); ++ count += 1; ++ } else { ++ tty->print_cr("dest = 0x%lx", value); ++ guarantee(false, "Not supported yet !"); ++ } ++ ++ while (count < 4) { ++ insts[count] = 0; ++ count++; ++ } ++ ++ guarantee(((long)addr_at(0) % (BytesPerWord * 2)) == 0, "must be aligned"); ++ atomic_store128_ptr func = get_atomic_store128_func(); ++ (*func)((long *)addr_at(0), 0, *(long *)&insts[0], *(long *)&insts[2]); ++ ++ ICache::invalidate_range(addr_at(0), 16); ++} ++ ++void NativeJump::patch_set48(address dest) { ++ jlong value = (jlong) dest; ++ int rt_reg = (int_at(0) & (0x1f << 16)); ++ int rs_reg = rt_reg << 5; ++ int rd_reg = rt_reg >> 5; ++ ++ int hi = (int)(value >> 32); ++ int lo = (int)(value & ~0); ++ ++ int count = 0; ++ ++ if (value == lo) { // 32-bit integer ++ if (Assembler::is_simm16(value)) { ++ set_int_at(count << 2, (Assembler::daddiu_op << 26) | rt_reg | Assembler::split_low(value)); ++ count += 1; ++ } else { ++ set_int_at(count << 2, (Assembler::lui_op << 26) | rt_reg | Assembler::split_low(value >> 16)); ++ count += 1; ++ if (Assembler::split_low(value)) { ++ set_int_at(count << 2, (Assembler::ori_op << 26) | rs_reg | rt_reg | Assembler::split_low(value)); ++ count += 1; ++ } ++ } ++ } else if (hi == 0) { // hardware zero-extends to upper 32 ++ set_int_at(count << 2, (Assembler::ori_op << 26) | rt_reg | Assembler::split_low(julong(value) >> 16)); ++ count += 1; ++ set_int_at(count << 2, (Assembler::dsll_op) | rt_reg | rd_reg | (16 << 6)); ++ count += 1; ++ if (Assembler::split_low(value)) { ++ set_int_at(count << 2, (Assembler::ori_op << 26) | rs_reg | rt_reg | Assembler::split_low(value)); ++ count += 1; ++ } ++ } else if ((value> 0) && Assembler::is_simm16(value >> 32)) { ++ set_int_at(count << 2, (Assembler::lui_op << 26) | rt_reg | Assembler::split_low(value >> 32)); ++ count += 1; ++ set_int_at(count << 2, (Assembler::ori_op << 26) | rs_reg | rt_reg | Assembler::split_low(value >> 16)); ++ count += 1; ++ set_int_at(count << 2, (Assembler::dsll_op) | rt_reg | rd_reg | (16 << 6)); ++ count += 1; ++ set_int_at(count << 2, (Assembler::ori_op << 26) | rs_reg | rt_reg | Assembler::split_low(value)); ++ count += 1; ++ } else { ++ tty->print_cr("dest = 0x%lx", value); ++ guarantee(false, "Not supported yet !"); ++ } ++ ++ while (count < 4) { ++ set_int_at(count << 2, 0); ++ count++; ++ } ++ ++ ICache::invalidate_range(addr_at(0), 16); ++} ++ ++void NativeJump::patch_on_j_only(address dst) { ++ long dest = ((long)dst - (((long)addr_at(4)) & 0xfffffffff0000000))>>2; ++ if ((dest >= 0) && (dest < (1<<26))) { ++ jint j_inst = (Assembler::j_op << 26) | dest; ++ set_int_at(0, j_inst); ++ ICache::invalidate_range(addr_at(0), 4); ++ } else { ++ ShouldNotReachHere(); ++ } ++} ++ ++ ++void NativeJump::patch_on_j_gs(address dst) { ++ long dest = ((long)dst - (((long)addr_at(20)) & 0xfffffffff0000000))>>2; ++ if ((dest >= 0) && (dest < (1<<26))) { ++ jint j_inst = (Assembler::j_op << 26) | dest; ++ set_int_at(16, j_inst); ++ ICache::invalidate_range(addr_at(16), 4); ++ } else { ++ ShouldNotReachHere(); ++ } ++} ++ ++void NativeJump::patch_on_j(address dst) { ++ patch_on_j_gs(dst); ++} ++ ++void NativeJump::patch_on_jr_gs(address dst) { ++ patch_set48_gs(dst); ++ ICache::invalidate_range(addr_at(0), 16); ++} ++ ++void NativeJump::patch_on_jr(address dst) { ++ patch_set48(dst); ++ ICache::invalidate_range(addr_at(0), 16); ++} ++ ++ ++void NativeJump::set_jump_destination(address dest) { ++ OrderAccess::fence(); ++ ++ if (is_short()) { ++ assert(Assembler::is_simm16(dest-addr_at(4)), "change this code"); ++ set_int_at(0, (int_at(0) & 0xffff0000) | (dest - addr_at(4)) & 0xffff ); ++ ICache::invalidate_range(addr_at(0), 4); ++ } else if (is_b_far()) { ++ int offset = dest - addr_at(12); ++ set_int_at(12, (int_at(12) & 0xffff0000) | (offset >> 16)); ++ set_int_at(16, (int_at(16) & 0xffff0000) | (offset & 0xffff)); ++ } else { ++ if (is_op(int_at(16), Assembler::j_op)) { ++ if (UseLEXT1) { ++ patch_on_j_gs(dest); ++ } else { ++ patch_on_j(dest); ++ } ++ } else if (is_op(int_at(0), Assembler::j_op)) { ++ patch_on_j_only(dest); ++ } else if (is_special_op(int_at(16), Assembler::jr_op)) { ++ if (UseLEXT1) { ++ //guarantee(!os::is_MP() || (((long)addr_at(0) % 16) == 0), "destination must be aligned for GSSD"); ++ //patch_on_jr_gs(dest); ++ patch_on_jr(dest); ++ } else { ++ patch_on_jr(dest); ++ } ++ } else { ++ fatal("not a jump"); ++ } ++ } ++} ++ ++void NativeGeneralJump::insert_unconditional(address code_pos, address entry) { ++ CodeBuffer cb(code_pos, instruction_size); ++ MacroAssembler masm(&cb); ++#define __ masm. ++ if (Assembler::is_simm16((entry - code_pos - 4) / 4)) { ++ __ b(entry); ++ __ delayed()->nop(); ++ } else { ++ // Attention: We have to use a relative jump here since PC reloc-operation isn't allowed here. ++ int offset = entry - code_pos; ++ ++ Label L; ++ __ bgezal(R0, L); ++ __ delayed()->lui(T9, (offset - 8) >> 16); ++ __ bind(L); ++ __ ori(T9, T9, (offset - 8) & 0xffff); ++ __ daddu(T9, T9, RA); ++ __ jr(T9); ++ __ delayed()->nop(); ++ } ++ ++#undef __ ++ ++ ICache::invalidate_range(code_pos, instruction_size); ++} ++ ++bool NativeJump::is_b_far() { ++// ++// 0x000000556809f198: daddu at, ra, zero ++// 0x000000556809f19c: [4110001]bgezal zero, 0x000000556809f1a4 ++// ++// 0x000000556809f1a0: nop ++// 0x000000556809f1a4: lui t9, 0xfffffffd ++// 0x000000556809f1a8: ori t9, t9, 0x14dc ++// 0x000000556809f1ac: daddu t9, t9, ra ++// 0x000000556809f1b0: daddu ra, at, zero ++// 0x000000556809f1b4: jr t9 ++// 0x000000556809f1b8: nop ++// ;; ImplicitNullCheckStub slow case ++// 0x000000556809f1bc: lui t9, 0x55 ++// ++ return is_op(int_at(12), Assembler::lui_op); ++} ++ ++address NativeJump::jump_destination() { ++ if ( is_short() ) { ++ return addr_at(4) + Assembler::imm_off(int_at(instruction_offset)) * 4; ++ } ++ // Assembler::merge() is not correct in MIPS_64! ++ // ++ // Example: ++ // hi16 = 0xfffd, ++ // lo16 = f7a4, ++ // ++ // offset=0xfffdf7a4 (Right) ++ // Assembler::merge = 0xfffcf7a4 (Wrong) ++ // ++ if ( is_b_far() ) { ++ int hi16 = int_at(12)&0xffff; ++ int low16 = int_at(16)&0xffff; ++ address target = addr_at(12) + (hi16 << 16) + low16; ++ return target; ++ } ++ ++ // nop ++ // nop ++ // nop ++ // nop ++ // j target ++ // nop ++ if ( nativeInstruction_at(addr_at(0))->is_nop() && ++ nativeInstruction_at(addr_at(4))->is_nop() && ++ nativeInstruction_at(addr_at(8))->is_nop() && ++ nativeInstruction_at(addr_at(12))->is_nop() && ++ is_op(int_at(16), Assembler::j_op) && ++ nativeInstruction_at(addr_at(20))->is_nop()) { ++ int instr_index = int_at(16) & 0x3ffffff; ++ intptr_t target_high = ((intptr_t)addr_at(20)) & 0xfffffffff0000000; ++ intptr_t target = target_high | (instr_index << 2); ++ return (address)target; ++ } ++ ++ // j target ++ // nop ++ if ( is_op(int_at(0), Assembler::j_op) && ++ nativeInstruction_at(addr_at(4))->is_nop()) { ++ int instr_index = int_at(0) & 0x3ffffff; ++ intptr_t target_high = ((intptr_t)addr_at(4)) & 0xfffffffff0000000; ++ intptr_t target = target_high | (instr_index << 2); ++ return (address)target; ++ } ++ ++ // li64 ++ if ( is_op(Assembler::lui_op) && ++ is_op(int_at(4), Assembler::ori_op) && ++ is_special_op(int_at(8), Assembler::dsll_op) && ++ is_op(int_at(12), Assembler::ori_op) && ++ is_special_op(int_at(16), Assembler::dsll_op) && ++ is_op(int_at(20), Assembler::ori_op) ) { ++ ++ return (address)Assembler::merge( (intptr_t)(int_at(20) & 0xffff), ++ (intptr_t)(int_at(12) & 0xffff), ++ (intptr_t)(int_at(4) & 0xffff), ++ (intptr_t)(int_at(0) & 0xffff)); ++ } ++ ++ //lui dst, imm16 ++ //ori dst, dst, imm16 ++ //dsll dst, dst, 16 ++ //ori dst, dst, imm16 ++ if ( is_op(Assembler::lui_op) && ++ is_op (int_at(4), Assembler::ori_op) && ++ is_special_op(int_at(8), Assembler::dsll_op) && ++ is_op (int_at(12), Assembler::ori_op) ) { ++ ++ return (address)Assembler::merge( (intptr_t)(int_at(12) & 0xffff), ++ (intptr_t)(int_at(4) & 0xffff), ++ (intptr_t)(int_at(0) & 0xffff), ++ (intptr_t)0); ++ } ++ ++ //ori dst, R0, imm16 ++ //dsll dst, dst, 16 ++ //ori dst, dst, imm16 ++ //nop ++ if ( is_op(Assembler::ori_op) && ++ is_special_op(int_at(4), Assembler::dsll_op) && ++ is_op (int_at(8), Assembler::ori_op) && ++ nativeInstruction_at(addr_at(12))->is_nop()) { ++ ++ return (address)Assembler::merge( (intptr_t)(int_at(8) & 0xffff), ++ (intptr_t)(int_at(0) & 0xffff), ++ (intptr_t)0, ++ (intptr_t)0); ++ } ++ ++ //ori dst, R0, imm16 ++ //dsll dst, dst, 16 ++ //nop ++ //nop ++ if ( is_op(Assembler::ori_op) && ++ is_special_op(int_at(4), Assembler::dsll_op) && ++ nativeInstruction_at(addr_at(8))->is_nop() && ++ nativeInstruction_at(addr_at(12))->is_nop()) { ++ ++ return (address)Assembler::merge( (intptr_t)(0), ++ (intptr_t)(int_at(0) & 0xffff), ++ (intptr_t)0, ++ (intptr_t)0); ++ } ++ ++ //daddiu dst, R0, imm16 ++ //nop ++ //nop ++ //nop ++ if ( is_op(Assembler::daddiu_op) && ++ nativeInstruction_at(addr_at(4))->is_nop() && ++ nativeInstruction_at(addr_at(8))->is_nop() && ++ nativeInstruction_at(addr_at(12))->is_nop() ) { ++ ++ int sign = int_at(0) & 0x8000; ++ if (sign == 0) { ++ return (address)Assembler::merge( (intptr_t)(int_at(0) & 0xffff), ++ (intptr_t)0, ++ (intptr_t)0, ++ (intptr_t)0); ++ } else { ++ return (address)Assembler::merge( (intptr_t)(int_at(0) & 0xffff), ++ (intptr_t)(0xffff), ++ (intptr_t)(0xffff), ++ (intptr_t)(0xffff)); ++ } ++ } ++ ++ //lui dst, imm16 ++ //ori dst, dst, imm16 ++ //nop ++ //nop ++ if ( is_op(Assembler::lui_op) && ++ is_op (int_at(4), Assembler::ori_op) && ++ nativeInstruction_at(addr_at(8))->is_nop() && ++ nativeInstruction_at(addr_at(12))->is_nop() ) { ++ ++ int sign = int_at(0) & 0x8000; ++ if (sign == 0) { ++ return (address)Assembler::merge( (intptr_t)(int_at(4) & 0xffff), ++ (intptr_t)(int_at(0) & 0xffff), ++ (intptr_t)0, ++ (intptr_t)0); ++ } else { ++ return (address)Assembler::merge( (intptr_t)(int_at(4) & 0xffff), ++ (intptr_t)(int_at(0) & 0xffff), ++ (intptr_t)(0xffff), ++ (intptr_t)(0xffff)); ++ } ++ } ++ ++ //lui dst, imm16 ++ //nop ++ //nop ++ //nop ++ if ( is_op(Assembler::lui_op) && ++ nativeInstruction_at(addr_at(4))->is_nop() && ++ nativeInstruction_at(addr_at(8))->is_nop() && ++ nativeInstruction_at(addr_at(12))->is_nop() ) { ++ ++ int sign = int_at(0) & 0x8000; ++ if (sign == 0) { ++ return (address)Assembler::merge( (intptr_t)0, ++ (intptr_t)(int_at(0) & 0xffff), ++ (intptr_t)0, ++ (intptr_t)0); ++ } else { ++ return (address)Assembler::merge( (intptr_t)0, ++ (intptr_t)(int_at(0) & 0xffff), ++ (intptr_t)(0xffff), ++ (intptr_t)(0xffff)); ++ } ++ } ++ ++ fatal("not a jump"); ++ return NULL; // unreachable ++} ++ ++// MT-safe patching of a long jump instruction. ++// First patches first word of instruction to two jmp's that jmps to them ++// selfs (spinlock). Then patches the last byte, and then atomicly replaces ++// the jmp's with the first 4 byte of the new instruction. ++void NativeGeneralJump::replace_mt_safe(address instr_addr, address code_buffer) { ++ NativeGeneralJump* h_jump = nativeGeneralJump_at (instr_addr); ++ assert((int)instruction_size == (int)NativeCall::instruction_size, ++ "note::Runtime1::patch_code uses NativeCall::instruction_size"); ++ ++ // ensure 100% atomicity ++ guarantee(!os::is_MP() || (((long)instr_addr % BytesPerWord) == 0), "destination must be aligned for SD"); ++ ++ int *p = (int *)instr_addr; ++ int jr_word = p[4]; ++ ++ p[4] = 0x1000fffb; /* .1: --; --; --; --; b .1; nop */ ++ memcpy(instr_addr, code_buffer, NativeCall::instruction_size - 8); ++ *(long *)(instr_addr + 16) = *(long *)(code_buffer + 16); ++} ++ ++// Must ensure atomicity ++void NativeJump::patch_verified_entry(address entry, address verified_entry, address dest) { ++ assert(dest == SharedRuntime::get_handle_wrong_method_stub(), "expected fixed destination of patch"); ++ assert(nativeInstruction_at(verified_entry + BytesPerInstWord)->is_nop(), "mips64 cannot replace non-nop with jump"); ++ ++ if (MacroAssembler::reachable_from_cache(dest)) { ++ CodeBuffer cb(verified_entry, 1 * BytesPerInstWord); ++ MacroAssembler masm(&cb); ++ masm.j(dest); ++ } else { ++ // We use an illegal instruction for marking a method as ++ // not_entrant or zombie ++ NativeIllegalInstruction::insert(verified_entry); ++ } ++ ++ ICache::invalidate_range(verified_entry, 1 * BytesPerInstWord); ++} ++ ++bool NativeInstruction::is_jump() ++{ ++ if ((int_at(0) & NativeGeneralJump::b_mask) == NativeGeneralJump::beq_opcode) ++ return true; ++ if (is_op(int_at(4), Assembler::lui_op)) // simplified b_far ++ return true; ++ if (is_op(int_at(12), Assembler::lui_op)) // original b_far ++ return true; ++ ++ // nop ++ // nop ++ // nop ++ // nop ++ // j target ++ // nop ++ if ( is_nop() && ++ nativeInstruction_at(addr_at(4))->is_nop() && ++ nativeInstruction_at(addr_at(8))->is_nop() && ++ nativeInstruction_at(addr_at(12))->is_nop() && ++ nativeInstruction_at(addr_at(16))->is_op(Assembler::j_op) && ++ nativeInstruction_at(addr_at(20))->is_nop() ) { ++ return true; ++ } ++ ++ if ( nativeInstruction_at(addr_at(0))->is_op(Assembler::j_op) && ++ nativeInstruction_at(addr_at(4))->is_nop() ) { ++ return true; ++ } ++ ++ // lui rd, imm(63...48); ++ // ori rd, rd, imm(47...32); ++ // dsll rd, rd, 16; ++ // ori rd, rd, imm(31...16); ++ // dsll rd, rd, 16; ++ // ori rd, rd, imm(15...0); ++ // jr rd ++ // nop ++ if (is_op(int_at(0), Assembler::lui_op) && ++ is_op(int_at(4), Assembler::ori_op) && ++ is_special_op(int_at(8), Assembler::dsll_op) && ++ is_op(int_at(12), Assembler::ori_op) && ++ is_special_op(int_at(16), Assembler::dsll_op) && ++ is_op(int_at(20), Assembler::ori_op) && ++ is_special_op(int_at(24), Assembler::jr_op)) { ++ return true; ++ } ++ ++ //lui dst, imm16 ++ //ori dst, dst, imm16 ++ //dsll dst, dst, 16 ++ //ori dst, dst, imm16 ++ if (is_op(int_at(0), Assembler::lui_op) && ++ is_op(int_at(4), Assembler::ori_op) && ++ is_special_op(int_at(8), Assembler::dsll_op) && ++ is_op(int_at(12), Assembler::ori_op) && ++ is_special_op(int_at(16), Assembler::jr_op)) { ++ return true; ++ } ++ ++ //ori dst, R0, imm16 ++ //dsll dst, dst, 16 ++ //ori dst, dst, imm16 ++ //nop ++ if ( is_op(Assembler::ori_op) && ++ is_special_op(int_at(4), Assembler::dsll_op) && ++ is_op (int_at(8), Assembler::ori_op) && ++ nativeInstruction_at(addr_at(12))->is_nop() && ++ is_special_op(int_at(16), Assembler::jr_op)) { ++ return true; ++ } ++ ++ //ori dst, R0, imm16 ++ //dsll dst, dst, 16 ++ //nop ++ //nop ++ if ( is_op(Assembler::ori_op) && ++ is_special_op(int_at(4), Assembler::dsll_op) && ++ nativeInstruction_at(addr_at(8))->is_nop() && ++ nativeInstruction_at(addr_at(12))->is_nop() && ++ is_special_op(int_at(16), Assembler::jr_op)) { ++ return true; ++ } ++ ++ //daddiu dst, R0, imm16 ++ //nop ++ //nop ++ //nop ++ if ( is_op(Assembler::daddiu_op) && ++ nativeInstruction_at(addr_at(4))->is_nop() && ++ nativeInstruction_at(addr_at(8))->is_nop() && ++ nativeInstruction_at(addr_at(12))->is_nop() && ++ is_special_op(int_at(16), Assembler::jr_op)) { ++ return true; ++ } ++ ++ //lui dst, imm16 ++ //ori dst, dst, imm16 ++ //nop ++ //nop ++ if ( is_op(Assembler::lui_op) && ++ is_op (int_at(4), Assembler::ori_op) && ++ nativeInstruction_at(addr_at(8))->is_nop() && ++ nativeInstruction_at(addr_at(12))->is_nop() && ++ is_special_op(int_at(16), Assembler::jr_op)) { ++ return true; ++ } ++ ++ //lui dst, imm16 ++ //nop ++ //nop ++ //nop ++ if ( is_op(Assembler::lui_op) && ++ nativeInstruction_at(addr_at(4))->is_nop() && ++ nativeInstruction_at(addr_at(8))->is_nop() && ++ nativeInstruction_at(addr_at(12))->is_nop() && ++ is_special_op(int_at(16), Assembler::jr_op)) { ++ return true; ++ } ++ ++ return false; ++} ++ ++bool NativeInstruction::is_dtrace_trap() { ++ //return (*(int32_t*)this & 0xff) == 0xcc; ++ Unimplemented(); ++ return false; ++} ++ ++bool NativeInstruction::is_safepoint_poll() { ++ // ++ // 390 li T2, 0x0000000000400000 #@loadConP ++ // 394 sw [SP + #12], V1 # spill 9 ++ // 398 Safepoint @ [T2] : poll for GC @ safePoint_poll # spec.benchmarks.compress.Decompressor::decompress @ bci:224 L[0]=A6 L[1]=_ L[2]=sp + #28 L[3]=_ L[4]=V1 ++ // ++ // 0x000000ffe5815130: lui t2, 0x40 ++ // 0x000000ffe5815134: sw v1, 0xc(sp) ; OopMap{a6=Oop off=920} ++ // ;*goto ++ // ; - spec.benchmarks.compress.Decompressor::decompress@224 (line 584) ++ // ++ // 0x000000ffe5815138: lw at, 0x0(t2) ;*goto <--- PC ++ // ; - spec.benchmarks.compress.Decompressor::decompress@224 (line 584) ++ // ++ ++ // Since there may be some spill instructions between the safePoint_poll and loadConP, ++ // we check the safepoint instruction like the this. ++ return is_op(Assembler::lw_op) && is_rt(AT); ++} +diff --git a/hotspot/src/cpu/mips/vm/nativeInst_mips.hpp b/hotspot/src/cpu/mips/vm/nativeInst_mips.hpp +new file mode 100644 +index 0000000000..13a4cb4ef1 +--- /dev/null ++++ b/hotspot/src/cpu/mips/vm/nativeInst_mips.hpp +@@ -0,0 +1,735 @@ ++/* ++ * Copyright (c) 1997, 2011, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2015, 2022, Loongson Technology. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ * ++ */ ++ ++#ifndef CPU_MIPS_VM_NATIVEINST_MIPS_HPP ++#define CPU_MIPS_VM_NATIVEINST_MIPS_HPP ++ ++#include "asm/assembler.hpp" ++#include "memory/allocation.hpp" ++#include "runtime/icache.hpp" ++#include "runtime/os.hpp" ++#include "utilities/top.hpp" ++ ++// We have interfaces for the following instructions: ++// - NativeInstruction ++// - - NativeCall ++// - - NativeMovConstReg ++// - - NativeMovConstRegPatching ++// - - NativeMovRegMem ++// - - NativeMovRegMemPatching ++// - - NativeJump ++// - - NativeIllegalOpCode ++// - - NativeGeneralJump ++// - - NativeReturn ++// - - NativeReturnX (return with argument) ++// - - NativePushConst ++// - - NativeTstRegMem ++ ++// The base class for different kinds of native instruction abstractions. ++// Provides the primitive operations to manipulate code relative to this. ++ ++class NativeInstruction VALUE_OBJ_CLASS_SPEC { ++ friend class Relocation; ++ ++ public: ++ enum mips_specific_constants { ++ nop_instruction_code = 0, ++ nop_instruction_size = 4, ++ sync_instruction_code = 0xf ++ }; ++ ++ bool is_nop() { return long_at(0) == nop_instruction_code; } ++ bool is_sync() { return long_at(0) == sync_instruction_code; } ++ bool is_dtrace_trap(); ++ inline bool is_call(); ++ inline bool is_illegal(); ++ inline bool is_return(); ++ bool is_jump(); ++ inline bool is_cond_jump(); ++ bool is_safepoint_poll(); ++ ++ //mips has no instruction to generate a illegal instrucion exception ++ //we define ours: break 11 ++ static int illegal_instruction(); ++ ++ bool is_int_branch(); ++ bool is_float_branch(); ++ ++ inline bool is_trampoline_call(); ++ ++ //We use an illegal instruction for marking a method as not_entrant or zombie. ++ bool is_sigill_zombie_not_entrant(); ++ ++ protected: ++ address addr_at(int offset) const { return address(this) + offset; } ++ address instruction_address() const { return addr_at(0); } ++ address next_instruction_address() const { return addr_at(BytesPerInstWord); } ++ address prev_instruction_address() const { return addr_at(-BytesPerInstWord); } ++ ++ s_char sbyte_at(int offset) const { return *(s_char*) addr_at(offset); } ++ u_char ubyte_at(int offset) const { return *(u_char*) addr_at(offset); } ++ ++ jint int_at(int offset) const { return *(jint*) addr_at(offset); } ++ juint uint_at(int offset) const { return *(juint*) addr_at(offset); } ++ ++ intptr_t ptr_at(int offset) const { return *(intptr_t*) addr_at(offset); } ++ ++ oop oop_at (int offset) const { return *(oop*) addr_at(offset); } ++ int long_at(int offset) const { return *(jint*)addr_at(offset); } ++ ++ ++ void set_char_at(int offset, char c) { *addr_at(offset) = (u_char)c; wrote(offset); } ++ void set_int_at(int offset, jint i) { *(jint*)addr_at(offset) = i; wrote(offset); } ++ void set_ptr_at (int offset, intptr_t ptr) { *(intptr_t*) addr_at(offset) = ptr; wrote(offset); } ++ void set_oop_at (int offset, oop o) { *(oop*) addr_at(offset) = o; wrote(offset); } ++ void set_long_at(int offset, long i); ++ ++ int insn_word() const { return long_at(0); } ++ static bool is_op (int insn, Assembler::ops op) { return Assembler::opcode(insn) == (int)op; } ++ bool is_op (Assembler::ops op) const { return is_op(insn_word(), op); } ++ bool is_rs (int insn, Register rs) const { return Assembler::rs(insn) == (int)rs->encoding(); } ++ bool is_rs (Register rs) const { return is_rs(insn_word(), rs); } ++ bool is_rt (int insn, Register rt) const { return Assembler::rt(insn) == (int)rt->encoding(); } ++ bool is_rt (Register rt) const { return is_rt(insn_word(), rt); } ++ ++ static bool is_special_op (int insn, Assembler::special_ops op) { ++ return is_op(insn, Assembler::special_op) && Assembler::special(insn)==(int)op; ++ } ++ bool is_special_op (Assembler::special_ops op) const { return is_special_op(insn_word(), op); } ++ ++ void wrote(int offset); ++ ++ public: ++ ++ // unit test stuff ++ static void test() {} // override for testing ++ ++ inline friend NativeInstruction* nativeInstruction_at(address address); ++}; ++ ++inline NativeInstruction* nativeInstruction_at(address address) { ++ NativeInstruction* inst = (NativeInstruction*)address; ++#ifdef ASSERT ++ //inst->verify(); ++#endif ++ return inst; ++} ++ ++inline NativeCall* nativeCall_at(address address); ++// The NativeCall is an abstraction for accessing/manipulating native call imm32/imm64 ++// instructions (used to manipulate inline caches, primitive & dll calls, etc.). ++// MIPS has no call instruction with imm32/imm64. Usually, a call was done like this: ++// 32 bits: ++// lui rt, imm16 ++// addiu rt, rt, imm16 ++// jalr rt ++// nop ++// ++// 64 bits: ++// lui rd, imm(63...48); ++// ori rd, rd, imm(47...32); ++// dsll rd, rd, 16; ++// ori rd, rd, imm(31...16); ++// dsll rd, rd, 16; ++// ori rd, rd, imm(15...0); ++// jalr rd ++// nop ++// ++ ++// we just consider the above for instruction as one call instruction ++class NativeCall: public NativeInstruction { ++ public: ++ enum mips_specific_constants { ++ instruction_offset = 0, ++ instruction_size = 6 * BytesPerInstWord, ++ return_address_offset_short = 4 * BytesPerInstWord, ++ return_address_offset_long = 6 * BytesPerInstWord, ++ displacement_offset = 0 ++ }; ++ ++ address instruction_address() const { return addr_at(instruction_offset); } ++ ++ address next_instruction_address() const { ++ if (is_special_op(int_at(8), Assembler::jalr_op)) { ++ return addr_at(return_address_offset_short); ++ } else { ++ return addr_at(return_address_offset_long); ++ } ++ } ++ ++ address return_address() const { ++ return next_instruction_address(); ++ } ++ ++ address target_addr_for_insn() const; ++ address destination() const; ++ void set_destination(address dest); ++ ++ void patch_set48_gs(address dest); ++ void patch_set48(address dest); ++ ++ void patch_on_jalr_gs(address dest); ++ void patch_on_jalr(address dest); ++ ++ void patch_on_jal_gs(address dest); ++ void patch_on_jal(address dest); ++ ++ void patch_on_trampoline(address dest); ++ ++ void patch_on_jal_only(address dest); ++ ++ void patch_set32_gs(address dest); ++ void patch_set32(address dest); ++ ++ void verify_alignment() { } ++ void verify(); ++ void print(); ++ ++ // Creation ++ inline friend NativeCall* nativeCall_at(address address); ++ inline friend NativeCall* nativeCall_before(address return_address); ++ ++ static bool is_call_at(address instr) { ++ return nativeInstruction_at(instr)->is_call(); ++ } ++ ++ static bool is_call_before(address return_address) { ++ return is_call_at(return_address - return_address_offset_short) | is_call_at(return_address - return_address_offset_long); ++ } ++ ++ static bool is_call_to(address instr, address target) { ++ return nativeInstruction_at(instr)->is_call() && ++nativeCall_at(instr)->destination() == target; ++ } ++ ++ // MT-safe patching of a call instruction. ++ static void insert(address code_pos, address entry); ++ ++ static void replace_mt_safe(address instr_addr, address code_buffer); ++ ++ // Similar to replace_mt_safe, but just changes the destination. The ++ // important thing is that free-running threads are able to execute ++ // this call instruction at all times. If the call is an immediate jal ++ // instruction we can simply rely on atomicity of 32-bit writes to ++ // make sure other threads will see no intermediate states. ++ ++ // We cannot rely on locks here, since the free-running threads must run at ++ // full speed. ++ // ++ // Used in the runtime linkage of calls; see class CompiledIC. ++ ++ // The parameter assert_lock disables the assertion during code generation. ++ void set_destination_mt_safe(address dest, bool assert_lock = true); ++ ++ address get_trampoline(); ++}; ++ ++inline NativeCall* nativeCall_at(address address) { ++ NativeCall* call = (NativeCall*)(address - NativeCall::instruction_offset); ++#ifdef ASSERT ++ call->verify(); ++#endif ++ return call; ++} ++ ++inline NativeCall* nativeCall_before(address return_address) { ++ NativeCall* call = NULL; ++ if (NativeCall::is_call_at(return_address - NativeCall::return_address_offset_long)) { ++ call = (NativeCall*)(return_address - NativeCall::return_address_offset_long); ++ } else { ++ call = (NativeCall*)(return_address - NativeCall::return_address_offset_short); ++ } ++#ifdef ASSERT ++ call->verify(); ++#endif ++ return call; ++} ++ ++class NativeMovConstReg: public NativeInstruction { ++ public: ++ enum mips_specific_constants { ++ instruction_offset = 0, ++ instruction_size = 4 * BytesPerInstWord, ++ next_instruction_offset = 4 * BytesPerInstWord, ++ }; ++ ++ int insn_word() const { return long_at(instruction_offset); } ++ address instruction_address() const { return addr_at(0); } ++ address next_instruction_address() const { return addr_at(next_instruction_offset); } ++ intptr_t data() const; ++ void set_data(intptr_t x, intptr_t o = 0); ++ ++ void patch_set48(intptr_t x); ++ ++ void verify(); ++ void print(); ++ ++ // unit test stuff ++ static void test() {} ++ ++ // Creation ++ inline friend NativeMovConstReg* nativeMovConstReg_at(address address); ++ inline friend NativeMovConstReg* nativeMovConstReg_before(address address); ++}; ++ ++inline NativeMovConstReg* nativeMovConstReg_at(address address) { ++ NativeMovConstReg* test = (NativeMovConstReg*)(address - NativeMovConstReg::instruction_offset); ++#ifdef ASSERT ++ test->verify(); ++#endif ++ return test; ++} ++ ++inline NativeMovConstReg* nativeMovConstReg_before(address address) { ++ NativeMovConstReg* test = (NativeMovConstReg*)(address - NativeMovConstReg::instruction_size - NativeMovConstReg::instruction_offset); ++#ifdef ASSERT ++ test->verify(); ++#endif ++ return test; ++} ++ ++class NativeMovConstRegPatching: public NativeMovConstReg { ++ private: ++ friend NativeMovConstRegPatching* nativeMovConstRegPatching_at(address address) { ++ NativeMovConstRegPatching* test = (NativeMovConstRegPatching*)(address - instruction_offset); ++ #ifdef ASSERT ++ test->verify(); ++ #endif ++ return test; ++ } ++}; ++ ++// An interface for accessing/manipulating native moves of the form: ++// lui AT, split_high(offset) ++// addiu AT, split_low(offset) ++// addu reg, reg, AT ++// lb/lbu/sb/lh/lhu/sh/lw/sw/lwc1/swc1 dest, reg, 0 ++// [lw/sw/lwc1/swc1 dest, reg, 4] ++// or ++// lb/lbu/sb/lh/lhu/sh/lw/sw/lwc1/swc1 dest, reg, offset ++// [lw/sw/lwc1/swc1 dest, reg, offset+4] ++// ++// Warning: These routines must be able to handle any instruction sequences ++// that are generated as a result of the load/store byte,word,long ++// macros. ++ ++class NativeMovRegMem: public NativeInstruction { ++ public: ++ enum mips_specific_constants { ++ instruction_offset = 0, ++ hiword_offset = 4, ++ ldst_offset = 12, ++ immediate_size = 4, ++ ldst_size = 16 ++ }; ++ ++ //offset is less than 16 bits. ++ bool is_immediate() const { return !is_op(long_at(instruction_offset), Assembler::lui_op); } ++ bool is_64ldst() const { ++ if (is_immediate()) { ++ return (Assembler::opcode(long_at(hiword_offset)) == Assembler::opcode(long_at(instruction_offset))) && ++ (Assembler::imm_off(long_at(hiword_offset)) == Assembler::imm_off(long_at(instruction_offset)) + wordSize); ++ } else { ++ return (Assembler::opcode(long_at(ldst_offset+hiword_offset)) == Assembler::opcode(long_at(ldst_offset))) && ++ (Assembler::imm_off(long_at(ldst_offset+hiword_offset)) == Assembler::imm_off(long_at(ldst_offset)) + wordSize); ++ } ++ } ++ ++ address instruction_address() const { return addr_at(instruction_offset); } ++ address next_instruction_address() const { ++ return addr_at( (is_immediate()? immediate_size : ldst_size) + (is_64ldst()? 4 : 0)); ++ } ++ ++ int offset() const; ++ ++ void set_offset(int x); ++ ++ void add_offset_in_bytes(int add_offset) { set_offset ( ( offset() + add_offset ) ); } ++ ++ void verify(); ++ void print (); ++ ++ // unit test stuff ++ static void test() {} ++ ++ private: ++ inline friend NativeMovRegMem* nativeMovRegMem_at (address address); ++}; ++ ++inline NativeMovRegMem* nativeMovRegMem_at (address address) { ++ NativeMovRegMem* test = (NativeMovRegMem*)(address - NativeMovRegMem::instruction_offset); ++#ifdef ASSERT ++ test->verify(); ++#endif ++ return test; ++} ++ ++class NativeMovRegMemPatching: public NativeMovRegMem { ++ private: ++ friend NativeMovRegMemPatching* nativeMovRegMemPatching_at (address address) { ++ NativeMovRegMemPatching* test = (NativeMovRegMemPatching*)(address - instruction_offset); ++ #ifdef ASSERT ++ test->verify(); ++ #endif ++ return test; ++ } ++}; ++ ++ ++// Handles all kinds of jump on Loongson. Long/far, conditional/unconditional ++// 32 bits: ++// far jump: ++// lui reg, split_high(addr) ++// addiu reg, split_low(addr) ++// jr reg ++// nop ++// or ++// beq ZERO, ZERO, offset ++// nop ++// ++ ++//64 bits: ++// far jump: ++// lui rd, imm(63...48); ++// ori rd, rd, imm(47...32); ++// dsll rd, rd, 16; ++// ori rd, rd, imm(31...16); ++// dsll rd, rd, 16; ++// ori rd, rd, imm(15...0); ++// jalr rd ++// nop ++// ++class NativeJump: public NativeInstruction { ++ public: ++ enum mips_specific_constants { ++ instruction_offset = 0, ++ beq_opcode = 0x10000000,//000100|00000|00000|offset ++ b_mask = 0xffff0000, ++ short_size = 8, ++ instruction_size = 6 * BytesPerInstWord ++ }; ++ ++ bool is_short() const { return (long_at(instruction_offset) & b_mask) == beq_opcode; } ++ bool is_b_far(); ++ address instruction_address() const { return addr_at(instruction_offset); } ++ address jump_destination(); ++ ++ void patch_set48_gs(address dest); ++ void patch_set48(address dest); ++ ++ void patch_on_jr_gs(address dest); ++ void patch_on_jr(address dest); ++ ++ void patch_on_j_gs(address dest); ++ void patch_on_j(address dest); ++ ++ void patch_on_j_only(address dest); ++ ++ void set_jump_destination(address dest); ++ ++ // Creation ++ inline friend NativeJump* nativeJump_at(address address); ++ ++ // Insertion of native jump instruction ++ static void insert(address code_pos, address entry) { Unimplemented(); } ++ // MT-safe insertion of native jump at verified method entry ++ static void check_verified_entry_alignment(address entry, address verified_entry) {} ++ static void patch_verified_entry(address entry, address verified_entry, address dest); ++ ++ void verify(); ++}; ++ ++inline NativeJump* nativeJump_at(address address) { ++ NativeJump* jump = (NativeJump*)(address - NativeJump::instruction_offset); ++ debug_only(jump->verify();) ++ return jump; ++} ++ ++class NativeGeneralJump: public NativeJump { ++ public: ++ // Creation ++ inline friend NativeGeneralJump* nativeGeneralJump_at(address address); ++ ++ // Insertion of native general jump instruction ++ static void insert_unconditional(address code_pos, address entry); ++ static void replace_mt_safe(address instr_addr, address code_buffer); ++}; ++ ++inline NativeGeneralJump* nativeGeneralJump_at(address address) { ++ NativeGeneralJump* jump = (NativeGeneralJump*)(address); ++ debug_only(jump->verify();) ++ return jump; ++} ++ ++class NativeIllegalInstruction: public NativeInstruction { ++public: ++ enum mips_specific_constants { ++ instruction_code = 0x42000029, // mips reserved instruction ++ instruction_size = 4, ++ instruction_offset = 0, ++ next_instruction_offset = 4 ++ }; ++ ++ // Insert illegal opcode as specific address ++ static void insert(address code_pos); ++}; ++ ++// return instruction that does not pop values of the stack ++// jr RA ++// delay slot ++class NativeReturn: public NativeInstruction { ++ public: ++ enum mips_specific_constants { ++ instruction_size = 8, ++ instruction_offset = 0, ++ next_instruction_offset = 8 ++ }; ++}; ++ ++ ++ ++ ++class NativeCondJump; ++inline NativeCondJump* nativeCondJump_at(address address); ++class NativeCondJump: public NativeInstruction { ++ public: ++ enum mips_specific_constants { ++ instruction_size = 16, ++ instruction_offset = 12, ++ next_instruction_offset = 20 ++ }; ++ ++ ++ int insn_word() const { return long_at(instruction_offset); } ++ address instruction_address() const { return addr_at(0); } ++ address next_instruction_address() const { return addr_at(next_instruction_offset); } ++ ++ // Creation ++ inline friend NativeCondJump* nativeCondJump_at(address address); ++ ++ address jump_destination() const { ++ return ::nativeCondJump_at(addr_at(12))->jump_destination(); ++ } ++ ++ void set_jump_destination(address dest) { ++ ::nativeCondJump_at(addr_at(12))->set_jump_destination(dest); ++ } ++ ++}; ++ ++inline NativeCondJump* nativeCondJump_at(address address) { ++ NativeCondJump* jump = (NativeCondJump*)(address); ++ return jump; ++} ++ ++ ++ ++inline bool NativeInstruction::is_illegal() { return insn_word() == illegal_instruction(); } ++ ++inline bool NativeInstruction::is_call() { ++ // jal target ++ // nop ++ if ( nativeInstruction_at(addr_at(0))->is_op(Assembler::jal_op) && ++ nativeInstruction_at(addr_at(4))->is_nop() ) { ++ return true; ++ } ++ ++ // nop ++ // nop ++ // nop ++ // nop ++ // jal target ++ // nop ++ if ( is_nop() && ++ nativeInstruction_at(addr_at(4))->is_nop() && ++ nativeInstruction_at(addr_at(8))->is_nop() && ++ nativeInstruction_at(addr_at(12))->is_nop() && ++ nativeInstruction_at(addr_at(16))->is_op(Assembler::jal_op) && ++ nativeInstruction_at(addr_at(20))->is_nop() ) { ++ return true; ++ } ++ ++ // li64 ++ if ( is_op(Assembler::lui_op) && ++ is_op(int_at(4), Assembler::ori_op) && ++ is_special_op(int_at(8), Assembler::dsll_op) && ++ is_op(int_at(12), Assembler::ori_op) && ++ is_special_op(int_at(16), Assembler::dsll_op) && ++ is_op(int_at(20), Assembler::ori_op) && ++ is_special_op(int_at(24), Assembler::jalr_op) ) { ++ return true; ++ } ++ ++ //lui dst, imm16 ++ //ori dst, dst, imm16 ++ //dsll dst, dst, 16 ++ //ori dst, dst, imm16 ++ if ( is_op(Assembler::lui_op) && ++ is_op (int_at(4), Assembler::ori_op) && ++ is_special_op(int_at(8), Assembler::dsll_op) && ++ is_op (int_at(12), Assembler::ori_op) && ++ is_special_op(int_at(16), Assembler::jalr_op) ) { ++ return true; ++ } ++ ++ //ori dst, R0, imm16 ++ //dsll dst, dst, 16 ++ //ori dst, dst, imm16 ++ //nop ++ if ( is_op(Assembler::ori_op) && ++ is_special_op(int_at(4), Assembler::dsll_op) && ++ is_op (int_at(8), Assembler::ori_op) && ++ nativeInstruction_at(addr_at(12))->is_nop() && ++ is_special_op(int_at(16), Assembler::jalr_op) ) { ++ return true; ++ } ++ ++ //ori dst, R0, imm16 ++ //dsll dst, dst, 16 ++ //nop ++ //nop ++ if ( is_op(Assembler::ori_op) && ++ is_special_op(int_at(4), Assembler::dsll_op) && ++ nativeInstruction_at(addr_at(8))->is_nop() && ++ nativeInstruction_at(addr_at(12))->is_nop() && ++ is_special_op(int_at(16), Assembler::jalr_op) ) { ++ return true; ++ } ++ ++ //daddiu dst, R0, imm16 ++ //nop ++ //nop ++ //nop ++ if ( is_op(Assembler::daddiu_op) && ++ nativeInstruction_at(addr_at(4))->is_nop() && ++ nativeInstruction_at(addr_at(8))->is_nop() && ++ nativeInstruction_at(addr_at(12))->is_nop() && ++ is_special_op(int_at(16), Assembler::jalr_op) ) { ++ return true; ++ } ++ ++ //lui dst, imm16 ++ //ori dst, dst, imm16 ++ //nop ++ //nop ++ if ( is_op(Assembler::lui_op) && ++ is_op (int_at(4), Assembler::ori_op) && ++ nativeInstruction_at(addr_at(8))->is_nop() && ++ nativeInstruction_at(addr_at(12))->is_nop() && ++ is_special_op(int_at(16), Assembler::jalr_op) ) { ++ return true; ++ } ++ ++ //lui dst, imm16 ++ //nop ++ //nop ++ //nop ++ if ( is_op(Assembler::lui_op) && ++ nativeInstruction_at(addr_at(4))->is_nop() && ++ nativeInstruction_at(addr_at(8))->is_nop() && ++ nativeInstruction_at(addr_at(12))->is_nop() && ++ is_special_op(int_at(16), Assembler::jalr_op) ) { ++ return true; ++ } ++ ++ ++ //daddiu dst, R0, imm16 ++ //nop ++ if ( is_op(Assembler::daddiu_op) && ++ nativeInstruction_at(addr_at(4))->is_nop() && ++ is_special_op(int_at(8), Assembler::jalr_op) ) { ++ return true; ++ } ++ ++ //lui dst, imm16 ++ //ori dst, dst, imm16 ++ if ( is_op(Assembler::lui_op) && ++ is_op (int_at(4), Assembler::ori_op) && ++ is_special_op(int_at(8), Assembler::jalr_op) ) { ++ return true; ++ } ++ ++ //lui dst, imm16 ++ //nop ++ if ( is_op(Assembler::lui_op) && ++ nativeInstruction_at(addr_at(4))->is_nop() && ++ is_special_op(int_at(8), Assembler::jalr_op) ) { ++ return true; ++ } ++ ++ if(is_trampoline_call()) ++ return true; ++ ++ return false; ++ ++} ++ ++inline bool NativeInstruction::is_return() { return is_special_op(Assembler::jr_op) && is_rs(RA);} ++ ++inline bool NativeInstruction::is_cond_jump() { return is_int_branch() || is_float_branch(); } ++ ++// Call trampoline stubs. ++class NativeCallTrampolineStub : public NativeInstruction { ++ public: ++ ++ enum mips_specific_constants { ++ instruction_size = 2 * BytesPerInstWord, ++ instruction_offset = 0, ++ next_instruction_offset = 2 * BytesPerInstWord ++ }; ++ ++ address destination() const { ++ return (address)ptr_at(0); ++ } ++ ++ void set_destination(address new_destination) { ++ set_ptr_at(0, (intptr_t)new_destination); ++ } ++}; ++ ++inline bool NativeInstruction::is_trampoline_call() { ++ // lui dst, imm16 ++ // ori dst, dst, imm16 ++ // dsll dst, dst, 16 ++ // ld target, dst, imm16 ++ // jalr target ++ // nop ++ if ( is_op(Assembler::lui_op) && ++ is_op(int_at(4), Assembler::ori_op) && ++ is_special_op(int_at(8), Assembler::dsll_op) && ++ is_op(int_at(12), Assembler::ld_op) && ++ is_special_op(int_at(16), Assembler::jalr_op) && ++ nativeInstruction_at(addr_at(20))->is_nop() ) { ++ return true; ++ } ++ ++ return false; ++} ++ ++inline NativeCallTrampolineStub* nativeCallTrampolineStub_at(address addr) { ++ return (NativeCallTrampolineStub*)addr; ++} ++ ++#endif // CPU_MIPS_VM_NATIVEINST_MIPS_HPP +diff --git a/hotspot/src/cpu/mips/vm/registerMap_mips.hpp b/hotspot/src/cpu/mips/vm/registerMap_mips.hpp +new file mode 100644 +index 0000000000..7f800eb107 +--- /dev/null ++++ b/hotspot/src/cpu/mips/vm/registerMap_mips.hpp +@@ -0,0 +1,47 @@ ++/* ++ * Copyright (c) 1998, 2010, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2015, 2016, Loongson Technology. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ * ++ */ ++ ++#ifndef CPU_MIPS_VM_REGISTERMAP_MIPS_HPP ++#define CPU_MIPS_VM_REGISTERMAP_MIPS_HPP ++ ++// machine-dependent implemention for register maps ++ friend class frame; ++ ++ private: ++#ifndef CORE ++ // This is the hook for finding a register in an "well-known" location, ++ // such as a register block of a predetermined format. ++ // Since there is none, we just return NULL. ++ // See registerMap_sparc.hpp for an example of grabbing registers ++ // from register save areas of a standard layout. ++ address pd_location(VMReg reg) const {return NULL;} ++#endif ++ ++ // no PD state to clear or copy: ++ void pd_clear() {} ++ void pd_initialize() {} ++ void pd_initialize_from(const RegisterMap* map) {} ++ ++#endif // CPU_MIPS_VM_REGISTERMAP_MIPS_HPP +diff --git a/hotspot/src/cpu/mips/vm/register_definitions_mips.cpp b/hotspot/src/cpu/mips/vm/register_definitions_mips.cpp +new file mode 100644 +index 0000000000..4af2531834 +--- /dev/null ++++ b/hotspot/src/cpu/mips/vm/register_definitions_mips.cpp +@@ -0,0 +1,103 @@ ++/* ++ * Copyright (c) 2002, 2013, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2015, 2016, Loongson Technology. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ * ++ */ ++ ++#include "precompiled.hpp" ++#include "asm/assembler.hpp" ++#include "asm/register.hpp" ++#include "register_mips.hpp" ++#ifdef TARGET_ARCH_MODEL_mips_32 ++# include "interp_masm_mips_32.hpp" ++#endif ++#ifdef TARGET_ARCH_MODEL_mips_64 ++# include "interp_masm_mips_64.hpp" ++#endif ++ ++REGISTER_DEFINITION(Register, noreg); ++REGISTER_DEFINITION(Register, i0); ++REGISTER_DEFINITION(Register, i1); ++REGISTER_DEFINITION(Register, i2); ++REGISTER_DEFINITION(Register, i3); ++REGISTER_DEFINITION(Register, i4); ++REGISTER_DEFINITION(Register, i5); ++REGISTER_DEFINITION(Register, i6); ++REGISTER_DEFINITION(Register, i7); ++REGISTER_DEFINITION(Register, i8); ++REGISTER_DEFINITION(Register, i9); ++REGISTER_DEFINITION(Register, i10); ++REGISTER_DEFINITION(Register, i11); ++REGISTER_DEFINITION(Register, i12); ++REGISTER_DEFINITION(Register, i13); ++REGISTER_DEFINITION(Register, i14); ++REGISTER_DEFINITION(Register, i15); ++REGISTER_DEFINITION(Register, i16); ++REGISTER_DEFINITION(Register, i17); ++REGISTER_DEFINITION(Register, i18); ++REGISTER_DEFINITION(Register, i19); ++REGISTER_DEFINITION(Register, i20); ++REGISTER_DEFINITION(Register, i21); ++REGISTER_DEFINITION(Register, i22); ++REGISTER_DEFINITION(Register, i23); ++REGISTER_DEFINITION(Register, i24); ++REGISTER_DEFINITION(Register, i25); ++REGISTER_DEFINITION(Register, i26); ++REGISTER_DEFINITION(Register, i27); ++REGISTER_DEFINITION(Register, i28); ++REGISTER_DEFINITION(Register, i29); ++REGISTER_DEFINITION(Register, i30); ++REGISTER_DEFINITION(Register, i31); ++ ++REGISTER_DEFINITION(FloatRegister, fnoreg); ++REGISTER_DEFINITION(FloatRegister, f0); ++REGISTER_DEFINITION(FloatRegister, f1); ++REGISTER_DEFINITION(FloatRegister, f2); ++REGISTER_DEFINITION(FloatRegister, f3); ++REGISTER_DEFINITION(FloatRegister, f4); ++REGISTER_DEFINITION(FloatRegister, f5); ++REGISTER_DEFINITION(FloatRegister, f6); ++REGISTER_DEFINITION(FloatRegister, f7); ++REGISTER_DEFINITION(FloatRegister, f8); ++REGISTER_DEFINITION(FloatRegister, f9); ++REGISTER_DEFINITION(FloatRegister, f10); ++REGISTER_DEFINITION(FloatRegister, f11); ++REGISTER_DEFINITION(FloatRegister, f12); ++REGISTER_DEFINITION(FloatRegister, f13); ++REGISTER_DEFINITION(FloatRegister, f14); ++REGISTER_DEFINITION(FloatRegister, f15); ++REGISTER_DEFINITION(FloatRegister, f16); ++REGISTER_DEFINITION(FloatRegister, f17); ++REGISTER_DEFINITION(FloatRegister, f18); ++REGISTER_DEFINITION(FloatRegister, f19); ++REGISTER_DEFINITION(FloatRegister, f20); ++REGISTER_DEFINITION(FloatRegister, f21); ++REGISTER_DEFINITION(FloatRegister, f22); ++REGISTER_DEFINITION(FloatRegister, f23); ++REGISTER_DEFINITION(FloatRegister, f24); ++REGISTER_DEFINITION(FloatRegister, f25); ++REGISTER_DEFINITION(FloatRegister, f26); ++REGISTER_DEFINITION(FloatRegister, f27); ++REGISTER_DEFINITION(FloatRegister, f28); ++REGISTER_DEFINITION(FloatRegister, f29); ++REGISTER_DEFINITION(FloatRegister, f30); ++REGISTER_DEFINITION(FloatRegister, f31); +diff --git a/hotspot/src/cpu/mips/vm/register_mips.cpp b/hotspot/src/cpu/mips/vm/register_mips.cpp +new file mode 100644 +index 0000000000..4a9b22bfef +--- /dev/null ++++ b/hotspot/src/cpu/mips/vm/register_mips.cpp +@@ -0,0 +1,52 @@ ++/* ++ * Copyright (c) 2000, 2012, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2015, 2022, Loongson Technology. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ * ++ */ ++ ++#include "precompiled.hpp" ++#include "register_mips.hpp" ++ ++const int ConcreteRegisterImpl::max_gpr = RegisterImpl::number_of_registers << 1; ++const int ConcreteRegisterImpl::max_fpr = ConcreteRegisterImpl::max_gpr + ++ 2 * FloatRegisterImpl::number_of_registers; ++ ++const char* RegisterImpl::name() const { ++ const char* names[number_of_registers] = { ++ "zero", "at", "v0", "v1", "a0", "a1", "a2", "a3", ++ "a4", "a5", "a6", "a7", "t0", "t1", "t2", "t3", ++ "s0", "s1", "s2", "s3", "s4", "s5", "s6", "s7", ++ "t8", "t9", "k0", "k1", "gp", "sp", "fp", "ra" ++ }; ++ return is_valid() ? names[encoding()] : "noreg"; ++} ++ ++const char* FloatRegisterImpl::name() const { ++ const char* names[number_of_registers] = { ++ "f0", "f1", "f2", "f3", "f4", "f5", "f6", "f7", ++ "f8", "f9", "f10", "f11", "f12", "f13", "f14", "f15", ++ "f16", "f17", "f18", "f19", "f20", "f21", "f22", "f23", ++ "f24", "f25", "f26", "f27", "f28", "f29", "f30", "f31", ++ }; ++ return is_valid() ? names[encoding()] : "fnoreg"; ++} ++ +diff --git a/hotspot/src/cpu/mips/vm/register_mips.hpp b/hotspot/src/cpu/mips/vm/register_mips.hpp +new file mode 100644 +index 0000000000..88bf2d68cc +--- /dev/null ++++ b/hotspot/src/cpu/mips/vm/register_mips.hpp +@@ -0,0 +1,346 @@ ++/* ++ * Copyright (c) 2000, 2012, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2015, 2022, Loongson Technology. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ * ++ */ ++ ++#ifndef CPU_MIPS_VM_REGISTER_MIPS_HPP ++#define CPU_MIPS_VM_REGISTER_MIPS_HPP ++ ++#include "asm/register.hpp" ++#include "vm_version_mips.hpp" ++ ++class VMRegImpl; ++typedef VMRegImpl* VMReg; ++ ++// Use Register as shortcut ++class RegisterImpl; ++typedef RegisterImpl* Register; ++ ++ ++// The implementation of integer registers for the mips architecture ++inline Register as_Register(int encoding) { ++ return (Register)(intptr_t) encoding; ++} ++ ++class RegisterImpl: public AbstractRegisterImpl { ++ public: ++ enum { ++ number_of_registers = 32 ++ }; ++ ++ // derived registers, offsets, and addresses ++ Register successor() const { return as_Register(encoding() + 1); } ++ ++ // construction ++ inline friend Register as_Register(int encoding); ++ ++ VMReg as_VMReg(); ++ ++ // accessors ++ int encoding() const { assert(is_valid(),err_msg( "invalid register (%d)", (int)(intptr_t)this)); return (intptr_t)this; } ++ bool is_valid() const { return 0 <= (intptr_t)this && (intptr_t)this < number_of_registers; } ++ const char* name() const; ++}; ++ ++ ++// The integer registers of the MIPS32 architecture ++CONSTANT_REGISTER_DECLARATION(Register, noreg, (-1)); ++ ++ ++CONSTANT_REGISTER_DECLARATION(Register, i0, (0)); ++CONSTANT_REGISTER_DECLARATION(Register, i1, (1)); ++CONSTANT_REGISTER_DECLARATION(Register, i2, (2)); ++CONSTANT_REGISTER_DECLARATION(Register, i3, (3)); ++CONSTANT_REGISTER_DECLARATION(Register, i4, (4)); ++CONSTANT_REGISTER_DECLARATION(Register, i5, (5)); ++CONSTANT_REGISTER_DECLARATION(Register, i6, (6)); ++CONSTANT_REGISTER_DECLARATION(Register, i7, (7)); ++CONSTANT_REGISTER_DECLARATION(Register, i8, (8)); ++CONSTANT_REGISTER_DECLARATION(Register, i9, (9)); ++CONSTANT_REGISTER_DECLARATION(Register, i10, (10)); ++CONSTANT_REGISTER_DECLARATION(Register, i11, (11)); ++CONSTANT_REGISTER_DECLARATION(Register, i12, (12)); ++CONSTANT_REGISTER_DECLARATION(Register, i13, (13)); ++CONSTANT_REGISTER_DECLARATION(Register, i14, (14)); ++CONSTANT_REGISTER_DECLARATION(Register, i15, (15)); ++CONSTANT_REGISTER_DECLARATION(Register, i16, (16)); ++CONSTANT_REGISTER_DECLARATION(Register, i17, (17)); ++CONSTANT_REGISTER_DECLARATION(Register, i18, (18)); ++CONSTANT_REGISTER_DECLARATION(Register, i19, (19)); ++CONSTANT_REGISTER_DECLARATION(Register, i20, (20)); ++CONSTANT_REGISTER_DECLARATION(Register, i21, (21)); ++CONSTANT_REGISTER_DECLARATION(Register, i22, (22)); ++CONSTANT_REGISTER_DECLARATION(Register, i23, (23)); ++CONSTANT_REGISTER_DECLARATION(Register, i24, (24)); ++CONSTANT_REGISTER_DECLARATION(Register, i25, (25)); ++CONSTANT_REGISTER_DECLARATION(Register, i26, (26)); ++CONSTANT_REGISTER_DECLARATION(Register, i27, (27)); ++CONSTANT_REGISTER_DECLARATION(Register, i28, (28)); ++CONSTANT_REGISTER_DECLARATION(Register, i29, (29)); ++CONSTANT_REGISTER_DECLARATION(Register, i30, (30)); ++CONSTANT_REGISTER_DECLARATION(Register, i31, (31)); ++ ++#ifndef DONT_USE_REGISTER_DEFINES ++#define NOREG ((Register)(noreg_RegisterEnumValue)) ++ ++#define I0 ((Register)(i0_RegisterEnumValue)) ++#define I1 ((Register)(i1_RegisterEnumValue)) ++#define I2 ((Register)(i2_RegisterEnumValue)) ++#define I3 ((Register)(i3_RegisterEnumValue)) ++#define I4 ((Register)(i4_RegisterEnumValue)) ++#define I5 ((Register)(i5_RegisterEnumValue)) ++#define I6 ((Register)(i6_RegisterEnumValue)) ++#define I7 ((Register)(i7_RegisterEnumValue)) ++#define I8 ((Register)(i8_RegisterEnumValue)) ++#define I9 ((Register)(i9_RegisterEnumValue)) ++#define I10 ((Register)(i10_RegisterEnumValue)) ++#define I11 ((Register)(i11_RegisterEnumValue)) ++#define I12 ((Register)(i12_RegisterEnumValue)) ++#define I13 ((Register)(i13_RegisterEnumValue)) ++#define I14 ((Register)(i14_RegisterEnumValue)) ++#define I15 ((Register)(i15_RegisterEnumValue)) ++#define I16 ((Register)(i16_RegisterEnumValue)) ++#define I17 ((Register)(i17_RegisterEnumValue)) ++#define I18 ((Register)(i18_RegisterEnumValue)) ++#define I19 ((Register)(i19_RegisterEnumValue)) ++#define I20 ((Register)(i20_RegisterEnumValue)) ++#define I21 ((Register)(i21_RegisterEnumValue)) ++#define I22 ((Register)(i22_RegisterEnumValue)) ++#define I23 ((Register)(i23_RegisterEnumValue)) ++#define I24 ((Register)(i24_RegisterEnumValue)) ++#define I25 ((Register)(i25_RegisterEnumValue)) ++#define I26 ((Register)(i26_RegisterEnumValue)) ++#define I27 ((Register)(i27_RegisterEnumValue)) ++#define I28 ((Register)(i28_RegisterEnumValue)) ++#define I29 ((Register)(i29_RegisterEnumValue)) ++#define I30 ((Register)(i30_RegisterEnumValue)) ++#define I31 ((Register)(i31_RegisterEnumValue)) ++ ++#define R0 ((Register)(i0_RegisterEnumValue)) ++#define AT ((Register)(i1_RegisterEnumValue)) ++#define V0 ((Register)(i2_RegisterEnumValue)) ++#define V1 ((Register)(i3_RegisterEnumValue)) ++#define RA0 ((Register)(i4_RegisterEnumValue)) ++#define RA1 ((Register)(i5_RegisterEnumValue)) ++#define RA2 ((Register)(i6_RegisterEnumValue)) ++#define RA3 ((Register)(i7_RegisterEnumValue)) ++#define RA4 ((Register)(i8_RegisterEnumValue)) ++#define RA5 ((Register)(i9_RegisterEnumValue)) ++#define RA6 ((Register)(i10_RegisterEnumValue)) ++#define RA7 ((Register)(i11_RegisterEnumValue)) ++#define RT0 ((Register)(i12_RegisterEnumValue)) ++#define RT1 ((Register)(i13_RegisterEnumValue)) ++#define RT2 ((Register)(i14_RegisterEnumValue)) ++#define RT3 ((Register)(i15_RegisterEnumValue)) ++#define S0 ((Register)(i16_RegisterEnumValue)) ++#define S1 ((Register)(i17_RegisterEnumValue)) ++#define S2 ((Register)(i18_RegisterEnumValue)) ++#define S3 ((Register)(i19_RegisterEnumValue)) ++#define S4 ((Register)(i20_RegisterEnumValue)) ++#define S5 ((Register)(i21_RegisterEnumValue)) ++#define S6 ((Register)(i22_RegisterEnumValue)) ++#define S7 ((Register)(i23_RegisterEnumValue)) ++#define RT8 ((Register)(i24_RegisterEnumValue)) ++#define RT9 ((Register)(i25_RegisterEnumValue)) ++#define K0 ((Register)(i26_RegisterEnumValue)) ++#define K1 ((Register)(i27_RegisterEnumValue)) ++#define GP ((Register)(i28_RegisterEnumValue)) ++#define SP ((Register)(i29_RegisterEnumValue)) ++#define FP ((Register)(i30_RegisterEnumValue)) ++#define S8 ((Register)(i30_RegisterEnumValue)) ++#define RA ((Register)(i31_RegisterEnumValue)) ++ ++#define c_rarg0 RT0 ++#define c_rarg1 RT1 ++#define Rmethod S3 ++#define Rsender S4 ++#define Rnext S1 ++ ++/* ++#define RT0 T0 ++#define RT1 T1 ++#define RT2 T2 ++#define RT3 T3 ++#define RT4 T8 ++#define RT5 T9 ++*/ ++ ++ ++//for interpreter frame ++// bytecode pointer register ++#define BCP S0 ++// local variable pointer register ++#define LVP S7 ++// temperary callee saved register, we use this register to save the register maybe blowed cross call_VM ++// be sure to save and restore its value in call_stub ++#define TSR S2 ++ ++//OPT_SAFEPOINT not supported yet ++#define OPT_SAFEPOINT 1 ++ ++#define OPT_THREAD 1 ++ ++#define TREG S6 ++ ++#define S5_heapbase S5 ++ ++#define mh_SP_save SP ++ ++#define FSR V0 ++#define SSR V1 ++#define FSF F0 ++#define SSF F1 ++#define FTF F14 ++#define STF F15 ++ ++#define AFT F30 ++ ++#define RECEIVER T0 ++#define IC_Klass T1 ++ ++#define SHIFT_count T3 ++ ++#endif // DONT_USE_REGISTER_DEFINES ++ ++// Use FloatRegister as shortcut ++class FloatRegisterImpl; ++typedef FloatRegisterImpl* FloatRegister; ++ ++inline FloatRegister as_FloatRegister(int encoding) { ++ return (FloatRegister)(intptr_t) encoding; ++} ++ ++// The implementation of floating point registers for the mips architecture ++class FloatRegisterImpl: public AbstractRegisterImpl { ++ public: ++ enum { ++ float_arg_base = 12, ++ number_of_registers = 32 ++ }; ++ ++ // construction ++ inline friend FloatRegister as_FloatRegister(int encoding); ++ ++ VMReg as_VMReg(); ++ ++ // derived registers, offsets, and addresses ++ FloatRegister successor() const { return as_FloatRegister(encoding() + 1); } ++ ++ // accessors ++ int encoding() const { assert(is_valid(), "invalid register"); return (intptr_t)this; } ++ bool is_valid() const { return 0 <= (intptr_t)this && (intptr_t)this < number_of_registers; } ++ const char* name() const; ++ ++}; ++ ++CONSTANT_REGISTER_DECLARATION(FloatRegister, fnoreg , (-1)); ++ ++CONSTANT_REGISTER_DECLARATION(FloatRegister, f0 , ( 0)); ++CONSTANT_REGISTER_DECLARATION(FloatRegister, f1 , ( 1)); ++CONSTANT_REGISTER_DECLARATION(FloatRegister, f2 , ( 2)); ++CONSTANT_REGISTER_DECLARATION(FloatRegister, f3 , ( 3)); ++CONSTANT_REGISTER_DECLARATION(FloatRegister, f4 , ( 4)); ++CONSTANT_REGISTER_DECLARATION(FloatRegister, f5 , ( 5)); ++CONSTANT_REGISTER_DECLARATION(FloatRegister, f6 , ( 6)); ++CONSTANT_REGISTER_DECLARATION(FloatRegister, f7 , ( 7)); ++CONSTANT_REGISTER_DECLARATION(FloatRegister, f8 , ( 8)); ++CONSTANT_REGISTER_DECLARATION(FloatRegister, f9 , ( 9)); ++CONSTANT_REGISTER_DECLARATION(FloatRegister, f10 , (10)); ++CONSTANT_REGISTER_DECLARATION(FloatRegister, f11 , (11)); ++CONSTANT_REGISTER_DECLARATION(FloatRegister, f12 , (12)); ++CONSTANT_REGISTER_DECLARATION(FloatRegister, f13 , (13)); ++CONSTANT_REGISTER_DECLARATION(FloatRegister, f14 , (14)); ++CONSTANT_REGISTER_DECLARATION(FloatRegister, f15 , (15)); ++CONSTANT_REGISTER_DECLARATION(FloatRegister, f16 , (16)); ++CONSTANT_REGISTER_DECLARATION(FloatRegister, f17 , (17)); ++CONSTANT_REGISTER_DECLARATION(FloatRegister, f18 , (18)); ++CONSTANT_REGISTER_DECLARATION(FloatRegister, f19 , (19)); ++CONSTANT_REGISTER_DECLARATION(FloatRegister, f20 , (20)); ++CONSTANT_REGISTER_DECLARATION(FloatRegister, f21 , (21)); ++CONSTANT_REGISTER_DECLARATION(FloatRegister, f22 , (22)); ++CONSTANT_REGISTER_DECLARATION(FloatRegister, f23 , (23)); ++CONSTANT_REGISTER_DECLARATION(FloatRegister, f24 , (24)); ++CONSTANT_REGISTER_DECLARATION(FloatRegister, f25 , (25)); ++CONSTANT_REGISTER_DECLARATION(FloatRegister, f26 , (26)); ++CONSTANT_REGISTER_DECLARATION(FloatRegister, f27 , (27)); ++CONSTANT_REGISTER_DECLARATION(FloatRegister, f28 , (28)); ++CONSTANT_REGISTER_DECLARATION(FloatRegister, f29 , (29)); ++CONSTANT_REGISTER_DECLARATION(FloatRegister, f30 , (30)); ++CONSTANT_REGISTER_DECLARATION(FloatRegister, f31 , (31)); ++ ++#ifndef DONT_USE_REGISTER_DEFINES ++#define FNOREG ((FloatRegister)(fnoreg_FloatRegisterEnumValue)) ++#define F0 ((FloatRegister)( f0_FloatRegisterEnumValue)) ++#define F1 ((FloatRegister)( f1_FloatRegisterEnumValue)) ++#define F2 ((FloatRegister)( f2_FloatRegisterEnumValue)) ++#define F3 ((FloatRegister)( f3_FloatRegisterEnumValue)) ++#define F4 ((FloatRegister)( f4_FloatRegisterEnumValue)) ++#define F5 ((FloatRegister)( f5_FloatRegisterEnumValue)) ++#define F6 ((FloatRegister)( f6_FloatRegisterEnumValue)) ++#define F7 ((FloatRegister)( f7_FloatRegisterEnumValue)) ++#define F8 ((FloatRegister)( f8_FloatRegisterEnumValue)) ++#define F9 ((FloatRegister)( f9_FloatRegisterEnumValue)) ++#define F10 ((FloatRegister)( f10_FloatRegisterEnumValue)) ++#define F11 ((FloatRegister)( f11_FloatRegisterEnumValue)) ++#define F12 ((FloatRegister)( f12_FloatRegisterEnumValue)) ++#define F13 ((FloatRegister)( f13_FloatRegisterEnumValue)) ++#define F14 ((FloatRegister)( f14_FloatRegisterEnumValue)) ++#define F15 ((FloatRegister)( f15_FloatRegisterEnumValue)) ++#define F16 ((FloatRegister)( f16_FloatRegisterEnumValue)) ++#define F17 ((FloatRegister)( f17_FloatRegisterEnumValue)) ++#define F18 ((FloatRegister)( f18_FloatRegisterEnumValue)) ++#define F19 ((FloatRegister)( f19_FloatRegisterEnumValue)) ++#define F20 ((FloatRegister)( f20_FloatRegisterEnumValue)) ++#define F21 ((FloatRegister)( f21_FloatRegisterEnumValue)) ++#define F22 ((FloatRegister)( f22_FloatRegisterEnumValue)) ++#define F23 ((FloatRegister)( f23_FloatRegisterEnumValue)) ++#define F24 ((FloatRegister)( f24_FloatRegisterEnumValue)) ++#define F25 ((FloatRegister)( f25_FloatRegisterEnumValue)) ++#define F26 ((FloatRegister)( f26_FloatRegisterEnumValue)) ++#define F27 ((FloatRegister)( f27_FloatRegisterEnumValue)) ++#define F28 ((FloatRegister)( f28_FloatRegisterEnumValue)) ++#define F29 ((FloatRegister)( f29_FloatRegisterEnumValue)) ++#define F30 ((FloatRegister)( f30_FloatRegisterEnumValue)) ++#define F31 ((FloatRegister)( f31_FloatRegisterEnumValue)) ++#endif // DONT_USE_REGISTER_DEFINES ++ ++ ++const int MIPS_ARGS_IN_REGS_NUM = 4; ++ ++// Need to know the total number of registers of all sorts for SharedInfo. ++// Define a class that exports it. ++class ConcreteRegisterImpl : public AbstractRegisterImpl { ++ public: ++ enum { ++ // A big enough number for C2: all the registers plus flags ++ // This number must be large enough to cover REG_COUNT (defined by c2) registers. ++ // There is no requirement that any ordering here matches any ordering c2 gives ++ // it's optoregs. ++ number_of_registers = (RegisterImpl::number_of_registers + FloatRegisterImpl::number_of_registers) * 2 ++ }; ++ ++ static const int max_gpr; ++ static const int max_fpr; ++}; ++ ++#endif //CPU_MIPS_VM_REGISTER_MIPS_HPP +diff --git a/hotspot/src/cpu/mips/vm/relocInfo_mips.cpp b/hotspot/src/cpu/mips/vm/relocInfo_mips.cpp +new file mode 100644 +index 0000000000..9c21222fee +--- /dev/null ++++ b/hotspot/src/cpu/mips/vm/relocInfo_mips.cpp +@@ -0,0 +1,155 @@ ++/* ++ * Copyright (c) 1998, 2013, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2015, 2022, Loongson Technology. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ * ++ */ ++ ++#include "precompiled.hpp" ++#include "asm/macroAssembler.hpp" ++#include "code/relocInfo.hpp" ++#include "nativeInst_mips.hpp" ++#include "oops/oop.inline.hpp" ++#include "runtime/safepoint.hpp" ++ ++ ++void Relocation::pd_set_data_value(address x, intptr_t o, bool verify_only) { ++ x += o; ++ typedef Assembler::WhichOperand WhichOperand; ++ WhichOperand which = (WhichOperand) format(); // that is, disp32 or imm, call32, narrow oop ++ assert(which == Assembler::disp32_operand || ++ which == Assembler::narrow_oop_operand || ++ which == Assembler::imm_operand, "format unpacks ok"); ++ if (which == Assembler::imm_operand) { ++ if (verify_only) { ++ assert(nativeMovConstReg_at(addr())->data() == (long)x, "instructions must match"); ++ } else { ++ nativeMovConstReg_at(addr())->set_data((intptr_t)(x)); ++ } ++ } else if (which == Assembler::narrow_oop_operand) { ++ // both compressed oops and compressed classes look the same ++ if (Universe::heap()->is_in_reserved((oop)x)) { ++ if (verify_only) { ++ assert(nativeMovConstReg_at(addr())->data() == (long)oopDesc::encode_heap_oop((oop)x), "instructions must match"); ++ } else { ++ nativeMovConstReg_at(addr())->set_data((intptr_t)(oopDesc::encode_heap_oop((oop)x)), (intptr_t)(x)); ++ } ++ } else { ++ if (verify_only) { ++ assert(nativeMovConstReg_at(addr())->data() == (long)Klass::encode_klass((Klass*)x), "instructions must match"); ++ } else { ++ nativeMovConstReg_at(addr())->set_data((intptr_t)(Klass::encode_klass((Klass*)x)), (intptr_t)(x)); ++ } ++ } ++ } else { ++ // Note: Use runtime_call_type relocations for call32_operand. ++ assert(0, "call32_operand not supported in MIPS64"); ++ } ++} ++ ++ ++//NOTICE HERE, this relocate is not need for MIPS, since MIPS USE abosolutly target, ++//Maybe We should FORGET CALL RELOCATION ++address Relocation::pd_call_destination(address orig_addr) { ++ intptr_t adj = 0; ++ NativeInstruction* ni = nativeInstruction_at(addr()); ++ if (ni->is_call()) { ++ if (!ni->is_trampoline_call()) { ++ return nativeCall_at(addr())->target_addr_for_insn(); ++ } else { ++ address trampoline = nativeCall_at(addr())->get_trampoline(); ++ if (trampoline) { ++ return nativeCallTrampolineStub_at(trampoline)->destination(); ++ } else { ++ return (address) -1; ++ } ++ } ++ } else if (ni->is_jump()) { ++ return nativeGeneralJump_at(addr())->jump_destination() + adj; ++ } else if (ni->is_cond_jump()) { ++ return nativeCondJump_at(addr())->jump_destination() +adj; ++ } else { ++ tty->print_cr("\nError!\ncall destination: 0x%lx", p2i(addr())); ++ Disassembler::decode(addr() - 10 * 4, addr() + 10 * 4, tty); ++ ShouldNotReachHere(); ++ return NULL; ++ } ++} ++ ++ ++void Relocation::pd_set_call_destination(address x) { ++ NativeInstruction* ni = nativeInstruction_at(addr()); ++ if (ni->is_call()) { ++ NativeCall* call = nativeCall_at(addr()); ++ if (!ni->is_trampoline_call()) { ++ call->set_destination(x); ++ } else { ++ address trampoline_stub_addr = call->get_trampoline(); ++ if (trampoline_stub_addr != NULL) { ++ address orig = call->target_addr_for_insn(); ++ if (orig != trampoline_stub_addr) { ++ call->patch_on_trampoline(trampoline_stub_addr); ++ } ++ call->set_destination_mt_safe(x, false); ++ } ++ } ++ } else if (ni->is_jump()) ++ nativeGeneralJump_at(addr())->set_jump_destination(x); ++ else if (ni->is_cond_jump()) ++ nativeCondJump_at(addr())->set_jump_destination(x); ++ else ++ { ShouldNotReachHere(); } ++ ++ // Unresolved jumps are recognized by a destination of -1 ++ // However 64bit can't actually produce such an address ++ // and encodes a jump to self but jump_destination will ++ // return a -1 as the signal. We must not relocate this ++ // jmp or the ic code will not see it as unresolved. ++} ++ ++ ++address* Relocation::pd_address_in_code() { ++ return (address*)addr(); ++} ++ ++ ++address Relocation::pd_get_address_from_code() { ++ NativeMovConstReg* ni = nativeMovConstReg_at(addr()); ++ return (address)ni->data(); ++} ++ ++ ++ ++void poll_Relocation::fix_relocation_after_move(const CodeBuffer* src, CodeBuffer* dest) { ++} ++ ++void poll_return_Relocation::fix_relocation_after_move(const CodeBuffer* src, CodeBuffer* dest) { ++} ++ ++void internal_pc_Relocation::fix_relocation_after_move(const CodeBuffer* src, CodeBuffer* dest) { ++ address target =0; ++ NativeMovConstReg* ni = nativeMovConstReg_at(addr()); ++ target = new_addr_for((address)ni->data(), src, dest); ++ ni->set_data((intptr_t)target); ++} ++ ++void metadata_Relocation::pd_fix_value(address x) { ++} +diff --git a/hotspot/src/cpu/mips/vm/relocInfo_mips.hpp b/hotspot/src/cpu/mips/vm/relocInfo_mips.hpp +new file mode 100644 +index 0000000000..04ad5dac96 +--- /dev/null ++++ b/hotspot/src/cpu/mips/vm/relocInfo_mips.hpp +@@ -0,0 +1,40 @@ ++/* ++ * Copyright (c) 1997, 2010, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2015, 2022, Loongson Technology. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ * ++ */ ++ ++#ifndef CPU_MIPS_VM_RELOCINFO_MIPS_HPP ++#define CPU_MIPS_VM_RELOCINFO_MIPS_HPP ++ ++ // machine-dependent parts of class relocInfo ++ private: ++ enum { ++ // Since MIPS instructions are whole words, ++ // the two low-order offset bits can always be discarded. ++ offset_unit = 4, ++ ++ // imm_oop_operand vs. narrow_oop_operand ++ format_width = 2 ++ }; ++ ++#endif // CPU_MIPS_VM_RELOCINFO_MIPS_HPP +diff --git a/hotspot/src/cpu/mips/vm/runtime_mips_64.cpp b/hotspot/src/cpu/mips/vm/runtime_mips_64.cpp +new file mode 100644 +index 0000000000..bb9269b423 +--- /dev/null ++++ b/hotspot/src/cpu/mips/vm/runtime_mips_64.cpp +@@ -0,0 +1,206 @@ ++/* ++ * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2015, 2020, Loongson Technology. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ * ++ */ ++ ++#include "precompiled.hpp" ++#ifdef COMPILER2 ++#include "asm/macroAssembler.hpp" ++#include "asm/macroAssembler.inline.hpp" ++#include "classfile/systemDictionary.hpp" ++#include "code/vmreg.hpp" ++#include "interpreter/interpreter.hpp" ++#include "opto/runtime.hpp" ++#include "runtime/interfaceSupport.hpp" ++#include "runtime/sharedRuntime.hpp" ++#include "runtime/stubRoutines.hpp" ++#include "runtime/vframeArray.hpp" ++#include "utilities/globalDefinitions.hpp" ++#include "vmreg_mips.inline.hpp" ++#endif ++ ++#define __ masm-> ++ ++#define A0 RA0 ++#define A1 RA1 ++#define A2 RA2 ++#define A3 RA3 ++#define A4 RA4 ++#define A5 RA5 ++#define A6 RA6 ++#define A7 RA7 ++#define T0 RT0 ++#define T1 RT1 ++#define T2 RT2 ++#define T3 RT3 ++#define T8 RT8 ++#define T9 RT9 ++ ++//-------------- generate_exception_blob ----------- ++// creates _exception_blob. ++// The exception blob is jumped to from a compiled method. ++// (see emit_exception_handler in sparc.ad file) ++// ++// Given an exception pc at a call we call into the runtime for the ++// handler in this method. This handler might merely restore state ++// (i.e. callee save registers) unwind the frame and jump to the ++// exception handler for the nmethod if there is no Java level handler ++// for the nmethod. ++// ++// This code is entered with a jump, and left with a jump. ++// ++// Arguments: ++// V0: exception oop ++// V1: exception pc ++// ++// Results: ++// A0: exception oop ++// A1: exception pc in caller or ??? ++// jumps to: exception handler of caller ++// ++// Note: the exception pc MUST be at a call (precise debug information) ++// ++// [stubGenerator_mips.cpp] generate_forward_exception() ++// |- V0, V1 are created ++// |- T9 <= SharedRuntime::exception_handler_for_return_address ++// `- jr T9 ++// `- the caller's exception_handler ++// `- jr OptoRuntime::exception_blob ++// `- here ++// ++void OptoRuntime::generate_exception_blob() { ++ // Capture info about frame layout ++ enum layout { ++ fp_off, ++ return_off, // slot for return address ++ framesize ++ }; ++ ++ // allocate space for the code ++ ResourceMark rm; ++ // setup code generation tools ++ CodeBuffer buffer("exception_blob", 5120, 5120); ++ MacroAssembler* masm = new MacroAssembler(&buffer); ++ ++ ++ address start = __ pc(); ++ ++ __ daddiu(SP, SP, -1 * framesize * wordSize); // Prolog! ++ ++ // this frame will be treated as the original caller method. ++ // So, the return pc should be filled with the original exception pc. ++ // ref: X86's implementation ++ __ sd(V1, SP, return_off *wordSize); // return address ++ __ sd(FP, SP, fp_off *wordSize); ++ ++ // Save callee saved registers. None for UseSSE=0, ++ // floats-only for UseSSE=1, and doubles for UseSSE=2. ++ ++ __ daddiu(FP, SP, fp_off * wordSize); ++ ++ // Store exception in Thread object. We cannot pass any arguments to the ++ // handle_exception call, since we do not want to make any assumption ++ // about the size of the frame where the exception happened in. ++ Register thread = TREG; ++ ++#ifndef OPT_THREAD ++ __ get_thread(thread); ++#endif ++ ++ __ sd(V0, Address(thread, JavaThread::exception_oop_offset())); ++ __ sd(V1, Address(thread, JavaThread::exception_pc_offset())); ++ ++ // This call does all the hard work. It checks if an exception handler ++ // exists in the method. ++ // If so, it returns the handler address. ++ // If not, it prepares for stack-unwinding, restoring the callee-save ++ // registers of the frame being removed. ++ __ set_last_Java_frame(thread, NOREG, NOREG, NULL); ++ ++ __ move(AT, -(StackAlignmentInBytes)); ++ __ andr(SP, SP, AT); // Fix stack alignment as required by ABI ++ ++ __ relocate(relocInfo::internal_pc_type); ++ ++ { ++ long save_pc = (long)__ pc() + 48; ++ __ patchable_set48(AT, save_pc); ++ } ++ __ sd(AT, thread, in_bytes(JavaThread::last_Java_pc_offset())); ++ ++ __ move(A0, thread); ++ __ patchable_set48(T9, (long)OptoRuntime::handle_exception_C); ++ __ jalr(T9); ++ __ delayed()->nop(); ++ ++ // Set an oopmap for the call site ++ OopMapSet *oop_maps = new OopMapSet(); ++ OopMap* map = new OopMap( framesize, 0 ); ++ ++ oop_maps->add_gc_map( __ offset(), map); ++ ++#ifndef OPT_THREAD ++ __ get_thread(thread); ++#endif ++ __ reset_last_Java_frame(thread, true); ++ ++ // Pop self-frame. ++ __ leave(); // Epilog! ++ ++ // V0: exception handler ++ ++ // We have a handler in V0, (could be deopt blob) ++ __ move(T9, V0); ++ ++#ifndef OPT_THREAD ++ __ get_thread(thread); ++#endif ++ // Get the exception ++ __ ld(A0, Address(thread, JavaThread::exception_oop_offset())); ++ // Get the exception pc in case we are deoptimized ++ __ ld(A1, Address(thread, JavaThread::exception_pc_offset())); ++#ifdef ASSERT ++ __ sd(R0, Address(thread, JavaThread::exception_handler_pc_offset())); ++ __ sd(R0, Address(thread, JavaThread::exception_pc_offset())); ++#endif ++ // Clear the exception oop so GC no longer processes it as a root. ++ __ sd(R0, Address(thread, JavaThread::exception_oop_offset())); ++ ++ // Fix seg fault when running: ++ // Eclipse + Plugin + Debug As ++ // This is the only condition where C2 calls SharedRuntime::generate_deopt_blob() ++ // ++ __ move(V0, A0); ++ __ move(V1, A1); ++ ++ // V0: exception oop ++ // T9: exception handler ++ // A1: exception pc ++ __ jr(T9); ++ __ delayed()->nop(); ++ ++ // make sure all code is generated ++ masm->flush(); ++ ++ _exception_blob = ExceptionBlob::create(&buffer, oop_maps, framesize); ++} +diff --git a/hotspot/src/cpu/mips/vm/sharedRuntime_mips_64.cpp b/hotspot/src/cpu/mips/vm/sharedRuntime_mips_64.cpp +new file mode 100644 +index 0000000000..35821c810d +--- /dev/null ++++ b/hotspot/src/cpu/mips/vm/sharedRuntime_mips_64.cpp +@@ -0,0 +1,3816 @@ ++/* ++ * Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2015, 2022, Loongson Technology. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ * ++ */ ++ ++#include "precompiled.hpp" ++#include "asm/macroAssembler.hpp" ++#include "asm/macroAssembler.inline.hpp" ++#include "code/debugInfoRec.hpp" ++#include "code/icBuffer.hpp" ++#include "code/vtableStubs.hpp" ++#include "interpreter/interpreter.hpp" ++#include "oops/compiledICHolder.hpp" ++#include "prims/jvmtiRedefineClassesTrace.hpp" ++#include "runtime/sharedRuntime.hpp" ++#include "runtime/vframeArray.hpp" ++#include "vmreg_mips.inline.hpp" ++#ifdef COMPILER2 ++#include "opto/runtime.hpp" ++#endif ++ ++#include ++ ++#define __ masm-> ++ ++#define A0 RA0 ++#define A1 RA1 ++#define A2 RA2 ++#define A3 RA3 ++#define A4 RA4 ++#define A5 RA5 ++#define A6 RA6 ++#define A7 RA7 ++#define T0 RT0 ++#define T1 RT1 ++#define T2 RT2 ++#define T3 RT3 ++#define T8 RT8 ++#define T9 RT9 ++ ++const int StackAlignmentInSlots = StackAlignmentInBytes / VMRegImpl::stack_slot_size; ++ ++class RegisterSaver { ++ enum { FPU_regs_live = 32 }; ++ // Capture info about frame layout ++ enum layout { ++#define DEF_LAYOUT_OFFS(regname) regname ## _off, regname ## H_off, ++ DEF_LAYOUT_OFFS(for_16_bytes_aligned) ++ DEF_LAYOUT_OFFS(fpr0) ++ DEF_LAYOUT_OFFS(fpr1) ++ DEF_LAYOUT_OFFS(fpr2) ++ DEF_LAYOUT_OFFS(fpr3) ++ DEF_LAYOUT_OFFS(fpr4) ++ DEF_LAYOUT_OFFS(fpr5) ++ DEF_LAYOUT_OFFS(fpr6) ++ DEF_LAYOUT_OFFS(fpr7) ++ DEF_LAYOUT_OFFS(fpr8) ++ DEF_LAYOUT_OFFS(fpr9) ++ DEF_LAYOUT_OFFS(fpr10) ++ DEF_LAYOUT_OFFS(fpr11) ++ DEF_LAYOUT_OFFS(fpr12) ++ DEF_LAYOUT_OFFS(fpr13) ++ DEF_LAYOUT_OFFS(fpr14) ++ DEF_LAYOUT_OFFS(fpr15) ++ DEF_LAYOUT_OFFS(fpr16) ++ DEF_LAYOUT_OFFS(fpr17) ++ DEF_LAYOUT_OFFS(fpr18) ++ DEF_LAYOUT_OFFS(fpr19) ++ DEF_LAYOUT_OFFS(fpr20) ++ DEF_LAYOUT_OFFS(fpr21) ++ DEF_LAYOUT_OFFS(fpr22) ++ DEF_LAYOUT_OFFS(fpr23) ++ DEF_LAYOUT_OFFS(fpr24) ++ DEF_LAYOUT_OFFS(fpr25) ++ DEF_LAYOUT_OFFS(fpr26) ++ DEF_LAYOUT_OFFS(fpr27) ++ DEF_LAYOUT_OFFS(fpr28) ++ DEF_LAYOUT_OFFS(fpr29) ++ DEF_LAYOUT_OFFS(fpr30) ++ DEF_LAYOUT_OFFS(fpr31) ++ ++ DEF_LAYOUT_OFFS(v0) ++ DEF_LAYOUT_OFFS(v1) ++ DEF_LAYOUT_OFFS(a0) ++ DEF_LAYOUT_OFFS(a1) ++ DEF_LAYOUT_OFFS(a2) ++ DEF_LAYOUT_OFFS(a3) ++ DEF_LAYOUT_OFFS(a4) ++ DEF_LAYOUT_OFFS(a5) ++ DEF_LAYOUT_OFFS(a6) ++ DEF_LAYOUT_OFFS(a7) ++ DEF_LAYOUT_OFFS(t0) ++ DEF_LAYOUT_OFFS(t1) ++ DEF_LAYOUT_OFFS(t2) ++ DEF_LAYOUT_OFFS(t3) ++ DEF_LAYOUT_OFFS(s0) ++ DEF_LAYOUT_OFFS(s1) ++ DEF_LAYOUT_OFFS(s2) ++ DEF_LAYOUT_OFFS(s3) ++ DEF_LAYOUT_OFFS(s4) ++ DEF_LAYOUT_OFFS(s5) ++ DEF_LAYOUT_OFFS(s6) ++ DEF_LAYOUT_OFFS(s7) ++ DEF_LAYOUT_OFFS(t8) ++ DEF_LAYOUT_OFFS(t9) ++ ++ DEF_LAYOUT_OFFS(gp) ++ DEF_LAYOUT_OFFS(fp) ++ DEF_LAYOUT_OFFS(return) ++ reg_save_size ++ }; ++ ++ public: ++ ++ static OopMap* save_live_registers(MacroAssembler* masm, int additional_frame_words, int* total_frame_words, bool save_vectors =false ); ++ static void restore_live_registers(MacroAssembler* masm, bool restore_vectors = false); ++ static int raOffset(void) { return return_off / 2; } ++ //Rmethod ++ static int methodOffset(void) { return s3_off / 2; } ++ ++ static int v0Offset(void) { return v0_off / 2; } ++ static int v1Offset(void) { return v1_off / 2; } ++ ++ static int fpResultOffset(void) { return fpr0_off / 2; } ++ ++ // During deoptimization only the result register need to be restored ++ // all the other values have already been extracted. ++ static void restore_result_registers(MacroAssembler* masm); ++}; ++ ++OopMap* RegisterSaver::save_live_registers(MacroAssembler* masm, int additional_frame_words, int* total_frame_words, bool save_vectors ) { ++ ++ // Always make the frame size 16-byte aligned ++ int frame_size_in_bytes = round_to(additional_frame_words*wordSize + ++ reg_save_size*BytesPerInt, 16); ++ // OopMap frame size is in compiler stack slots (jint's) not bytes or words ++ int frame_size_in_slots = frame_size_in_bytes / BytesPerInt; ++ // The caller will allocate additional_frame_words ++ int additional_frame_slots = additional_frame_words*wordSize / BytesPerInt; ++ // CodeBlob frame size is in words. ++ int frame_size_in_words = frame_size_in_bytes / wordSize; ++ *total_frame_words = frame_size_in_words; ++ ++ // save registers ++ ++ __ daddiu(SP, SP, - reg_save_size * jintSize); ++ ++ __ sdc1(F0, SP, fpr0_off * jintSize); __ sdc1(F1, SP, fpr1_off * jintSize); ++ __ sdc1(F2, SP, fpr2_off * jintSize); __ sdc1(F3, SP, fpr3_off * jintSize); ++ __ sdc1(F4, SP, fpr4_off * jintSize); __ sdc1(F5, SP, fpr5_off * jintSize); ++ __ sdc1(F6, SP, fpr6_off * jintSize); __ sdc1(F7, SP, fpr7_off * jintSize); ++ __ sdc1(F8, SP, fpr8_off * jintSize); __ sdc1(F9, SP, fpr9_off * jintSize); ++ __ sdc1(F10, SP, fpr10_off * jintSize); __ sdc1(F11, SP, fpr11_off * jintSize); ++ __ sdc1(F12, SP, fpr12_off * jintSize); __ sdc1(F13, SP, fpr13_off * jintSize); ++ __ sdc1(F14, SP, fpr14_off * jintSize); __ sdc1(F15, SP, fpr15_off * jintSize); ++ __ sdc1(F16, SP, fpr16_off * jintSize); __ sdc1(F17, SP, fpr17_off * jintSize); ++ __ sdc1(F18, SP, fpr18_off * jintSize); __ sdc1(F19, SP, fpr19_off * jintSize); ++ __ sdc1(F20, SP, fpr20_off * jintSize); __ sdc1(F21, SP, fpr21_off * jintSize); ++ __ sdc1(F22, SP, fpr22_off * jintSize); __ sdc1(F23, SP, fpr23_off * jintSize); ++ __ sdc1(F24, SP, fpr24_off * jintSize); __ sdc1(F25, SP, fpr25_off * jintSize); ++ __ sdc1(F26, SP, fpr26_off * jintSize); __ sdc1(F27, SP, fpr27_off * jintSize); ++ __ sdc1(F28, SP, fpr28_off * jintSize); __ sdc1(F29, SP, fpr29_off * jintSize); ++ __ sdc1(F30, SP, fpr30_off * jintSize); __ sdc1(F31, SP, fpr31_off * jintSize); ++ __ sd(V0, SP, v0_off * jintSize); __ sd(V1, SP, v1_off * jintSize); ++ __ sd(A0, SP, a0_off * jintSize); __ sd(A1, SP, a1_off * jintSize); ++ __ sd(A2, SP, a2_off * jintSize); __ sd(A3, SP, a3_off * jintSize); ++ __ sd(A4, SP, a4_off * jintSize); __ sd(A5, SP, a5_off * jintSize); ++ __ sd(A6, SP, a6_off * jintSize); __ sd(A7, SP, a7_off * jintSize); ++ __ sd(T0, SP, t0_off * jintSize); ++ __ sd(T1, SP, t1_off * jintSize); ++ __ sd(T2, SP, t2_off * jintSize); ++ __ sd(T3, SP, t3_off * jintSize); ++ __ sd(S0, SP, s0_off * jintSize); ++ __ sd(S1, SP, s1_off * jintSize); ++ __ sd(S2, SP, s2_off * jintSize); ++ __ sd(S3, SP, s3_off * jintSize); ++ __ sd(S4, SP, s4_off * jintSize); ++ __ sd(S5, SP, s5_off * jintSize); ++ __ sd(S6, SP, s6_off * jintSize); ++ __ sd(S7, SP, s7_off * jintSize); ++ ++ __ sd(T8, SP, t8_off * jintSize); ++ __ sd(T9, SP, t9_off * jintSize); ++ ++ __ sd(GP, SP, gp_off * jintSize); ++ __ sd(FP, SP, fp_off * jintSize); ++ __ sd(RA, SP, return_off * jintSize); ++ __ daddiu(FP, SP, fp_off * jintSize); ++ ++ OopMapSet *oop_maps = new OopMapSet(); ++ //OopMap* map = new OopMap( frame_words, 0 ); ++ OopMap* map = new OopMap( frame_size_in_slots, 0 ); ++ ++ ++//#define STACK_OFFSET(x) VMRegImpl::stack2reg((x) + additional_frame_words) ++#define STACK_OFFSET(x) VMRegImpl::stack2reg((x) + additional_frame_slots) ++ map->set_callee_saved(STACK_OFFSET( v0_off), V0->as_VMReg()); ++ map->set_callee_saved(STACK_OFFSET( v1_off), V1->as_VMReg()); ++ map->set_callee_saved(STACK_OFFSET( a0_off), A0->as_VMReg()); ++ map->set_callee_saved(STACK_OFFSET( a1_off), A1->as_VMReg()); ++ map->set_callee_saved(STACK_OFFSET( a2_off), A2->as_VMReg()); ++ map->set_callee_saved(STACK_OFFSET( a3_off), A3->as_VMReg()); ++ map->set_callee_saved(STACK_OFFSET( a4_off), A4->as_VMReg()); ++ map->set_callee_saved(STACK_OFFSET( a5_off), A5->as_VMReg()); ++ map->set_callee_saved(STACK_OFFSET( a6_off), A6->as_VMReg()); ++ map->set_callee_saved(STACK_OFFSET( a7_off), A7->as_VMReg()); ++ map->set_callee_saved(STACK_OFFSET( t0_off), T0->as_VMReg()); ++ map->set_callee_saved(STACK_OFFSET( t1_off), T1->as_VMReg()); ++ map->set_callee_saved(STACK_OFFSET( t2_off), T2->as_VMReg()); ++ map->set_callee_saved(STACK_OFFSET( t3_off), T3->as_VMReg()); ++ map->set_callee_saved(STACK_OFFSET( s0_off), S0->as_VMReg()); ++ map->set_callee_saved(STACK_OFFSET( s1_off), S1->as_VMReg()); ++ map->set_callee_saved(STACK_OFFSET( s2_off), S2->as_VMReg()); ++ map->set_callee_saved(STACK_OFFSET( s3_off), S3->as_VMReg()); ++ map->set_callee_saved(STACK_OFFSET( s4_off), S4->as_VMReg()); ++ map->set_callee_saved(STACK_OFFSET( s5_off), S5->as_VMReg()); ++ map->set_callee_saved(STACK_OFFSET( s6_off), S6->as_VMReg()); ++ map->set_callee_saved(STACK_OFFSET( s7_off), S7->as_VMReg()); ++ map->set_callee_saved(STACK_OFFSET( t8_off), T8->as_VMReg()); ++ map->set_callee_saved(STACK_OFFSET( t9_off), T9->as_VMReg()); ++ map->set_callee_saved(STACK_OFFSET( gp_off), GP->as_VMReg()); ++ map->set_callee_saved(STACK_OFFSET( fp_off), FP->as_VMReg()); ++ map->set_callee_saved(STACK_OFFSET( return_off), RA->as_VMReg()); ++ ++ map->set_callee_saved(STACK_OFFSET( fpr0_off), F0->as_VMReg()); ++ map->set_callee_saved(STACK_OFFSET( fpr1_off), F1->as_VMReg()); ++ map->set_callee_saved(STACK_OFFSET( fpr2_off), F2->as_VMReg()); ++ map->set_callee_saved(STACK_OFFSET( fpr3_off), F3->as_VMReg()); ++ map->set_callee_saved(STACK_OFFSET( fpr4_off), F4->as_VMReg()); ++ map->set_callee_saved(STACK_OFFSET( fpr5_off), F5->as_VMReg()); ++ map->set_callee_saved(STACK_OFFSET( fpr6_off), F6->as_VMReg()); ++ map->set_callee_saved(STACK_OFFSET( fpr7_off), F7->as_VMReg()); ++ map->set_callee_saved(STACK_OFFSET( fpr8_off), F8->as_VMReg()); ++ map->set_callee_saved(STACK_OFFSET( fpr9_off), F9->as_VMReg()); ++ map->set_callee_saved(STACK_OFFSET( fpr10_off), F10->as_VMReg()); ++ map->set_callee_saved(STACK_OFFSET( fpr11_off), F11->as_VMReg()); ++ map->set_callee_saved(STACK_OFFSET( fpr12_off), F12->as_VMReg()); ++ map->set_callee_saved(STACK_OFFSET( fpr13_off), F13->as_VMReg()); ++ map->set_callee_saved(STACK_OFFSET( fpr14_off), F14->as_VMReg()); ++ map->set_callee_saved(STACK_OFFSET( fpr15_off), F15->as_VMReg()); ++ map->set_callee_saved(STACK_OFFSET( fpr16_off), F16->as_VMReg()); ++ map->set_callee_saved(STACK_OFFSET( fpr17_off), F17->as_VMReg()); ++ map->set_callee_saved(STACK_OFFSET( fpr18_off), F18->as_VMReg()); ++ map->set_callee_saved(STACK_OFFSET( fpr19_off), F19->as_VMReg()); ++ map->set_callee_saved(STACK_OFFSET( fpr20_off), F20->as_VMReg()); ++ map->set_callee_saved(STACK_OFFSET( fpr21_off), F21->as_VMReg()); ++ map->set_callee_saved(STACK_OFFSET( fpr22_off), F22->as_VMReg()); ++ map->set_callee_saved(STACK_OFFSET( fpr23_off), F23->as_VMReg()); ++ map->set_callee_saved(STACK_OFFSET( fpr24_off), F24->as_VMReg()); ++ map->set_callee_saved(STACK_OFFSET( fpr25_off), F25->as_VMReg()); ++ map->set_callee_saved(STACK_OFFSET( fpr26_off), F26->as_VMReg()); ++ map->set_callee_saved(STACK_OFFSET( fpr27_off), F27->as_VMReg()); ++ map->set_callee_saved(STACK_OFFSET( fpr28_off), F28->as_VMReg()); ++ map->set_callee_saved(STACK_OFFSET( fpr29_off), F29->as_VMReg()); ++ map->set_callee_saved(STACK_OFFSET( fpr30_off), F30->as_VMReg()); ++ map->set_callee_saved(STACK_OFFSET( fpr31_off), F31->as_VMReg()); ++ ++#undef STACK_OFFSET ++ return map; ++} ++ ++ ++// Pop the current frame and restore all the registers that we ++// saved. ++void RegisterSaver::restore_live_registers(MacroAssembler* masm, bool restore_vectors) { ++ __ ldc1(F0, SP, fpr0_off * jintSize); __ ldc1(F1, SP, fpr1_off * jintSize); ++ __ ldc1(F2, SP, fpr2_off * jintSize); __ ldc1(F3, SP, fpr3_off * jintSize); ++ __ ldc1(F4, SP, fpr4_off * jintSize); __ ldc1(F5, SP, fpr5_off * jintSize); ++ __ ldc1(F6, SP, fpr6_off * jintSize); __ ldc1(F7, SP, fpr7_off * jintSize); ++ __ ldc1(F8, SP, fpr8_off * jintSize); __ ldc1(F9, SP, fpr9_off * jintSize); ++ __ ldc1(F10, SP, fpr10_off * jintSize); __ ldc1(F11, SP, fpr11_off * jintSize); ++ __ ldc1(F12, SP, fpr12_off * jintSize); __ ldc1(F13, SP, fpr13_off * jintSize); ++ __ ldc1(F14, SP, fpr14_off * jintSize); __ ldc1(F15, SP, fpr15_off * jintSize); ++ __ ldc1(F16, SP, fpr16_off * jintSize); __ ldc1(F17, SP, fpr17_off * jintSize); ++ __ ldc1(F18, SP, fpr18_off * jintSize); __ ldc1(F19, SP, fpr19_off * jintSize); ++ __ ldc1(F20, SP, fpr20_off * jintSize); __ ldc1(F21, SP, fpr21_off * jintSize); ++ __ ldc1(F22, SP, fpr22_off * jintSize); __ ldc1(F23, SP, fpr23_off * jintSize); ++ __ ldc1(F24, SP, fpr24_off * jintSize); __ ldc1(F25, SP, fpr25_off * jintSize); ++ __ ldc1(F26, SP, fpr26_off * jintSize); __ ldc1(F27, SP, fpr27_off * jintSize); ++ __ ldc1(F28, SP, fpr28_off * jintSize); __ ldc1(F29, SP, fpr29_off * jintSize); ++ __ ldc1(F30, SP, fpr30_off * jintSize); __ ldc1(F31, SP, fpr31_off * jintSize); ++ ++ __ ld(V0, SP, v0_off * jintSize); __ ld(V1, SP, v1_off * jintSize); ++ __ ld(A0, SP, a0_off * jintSize); __ ld(A1, SP, a1_off * jintSize); ++ __ ld(A2, SP, a2_off * jintSize); __ ld(A3, SP, a3_off * jintSize); ++ __ ld(A4, SP, a4_off * jintSize); __ ld(A5, SP, a5_off * jintSize); ++ __ ld(A6, SP, a6_off * jintSize); __ ld(A7, SP, a7_off * jintSize); ++ __ ld(T0, SP, t0_off * jintSize); ++ __ ld(T1, SP, t1_off * jintSize); ++ __ ld(T2, SP, t2_off * jintSize); ++ __ ld(T3, SP, t3_off * jintSize); ++ __ ld(S0, SP, s0_off * jintSize); ++ __ ld(S1, SP, s1_off * jintSize); ++ __ ld(S2, SP, s2_off * jintSize); ++ __ ld(S3, SP, s3_off * jintSize); ++ __ ld(S4, SP, s4_off * jintSize); ++ __ ld(S5, SP, s5_off * jintSize); ++ __ ld(S6, SP, s6_off * jintSize); ++ __ ld(S7, SP, s7_off * jintSize); ++ ++ __ ld(T8, SP, t8_off * jintSize); ++ __ ld(T9, SP, t9_off * jintSize); ++ ++ __ ld(GP, SP, gp_off * jintSize); ++ __ ld(FP, SP, fp_off * jintSize); ++ __ ld(RA, SP, return_off * jintSize); ++ ++ __ addiu(SP, SP, reg_save_size * jintSize); ++} ++ ++// Pop the current frame and restore the registers that might be holding ++// a result. ++void RegisterSaver::restore_result_registers(MacroAssembler* masm) { ++ ++ // Just restore result register. Only used by deoptimization. By ++ // now any callee save register that needs to be restore to a c2 ++ // caller of the deoptee has been extracted into the vframeArray ++ // and will be stuffed into the c2i adapter we create for later ++ // restoration so only result registers need to be restored here. ++ ++ __ ld(V0, SP, v0_off * jintSize); ++ __ ld(V1, SP, v1_off * jintSize); ++ __ ldc1(F0, SP, fpr0_off * jintSize); ++ __ ldc1(F1, SP, fpr1_off * jintSize); ++ __ addiu(SP, SP, return_off * jintSize); ++} ++ ++// Is vector's size (in bytes) bigger than a size saved by default? ++// 16 bytes XMM registers are saved by default using fxsave/fxrstor instructions. ++bool SharedRuntime::is_wide_vector(int size) { ++ return size > 16; ++} ++ ++// The java_calling_convention describes stack locations as ideal slots on ++// a frame with no abi restrictions. Since we must observe abi restrictions ++// (like the placement of the register window) the slots must be biased by ++// the following value. ++ ++static int reg2offset_in(VMReg r) { ++ // Account for saved fp and return address ++ // This should really be in_preserve_stack_slots ++ return (r->reg2stack() + 2 * VMRegImpl::slots_per_word) * VMRegImpl::stack_slot_size; // + 2 * VMRegImpl::stack_slot_size); ++} ++ ++static int reg2offset_out(VMReg r) { ++ return (r->reg2stack() + SharedRuntime::out_preserve_stack_slots()) * VMRegImpl::stack_slot_size; ++} ++ ++// --------------------------------------------------------------------------- ++// Read the array of BasicTypes from a signature, and compute where the ++// arguments should go. Values in the VMRegPair regs array refer to 4-byte ++// quantities. Values less than SharedInfo::stack0 are registers, those above ++// refer to 4-byte stack slots. All stack slots are based off of the stack pointer ++// as framesizes are fixed. ++// VMRegImpl::stack0 refers to the first slot 0(sp). ++// and VMRegImpl::stack0+1 refers to the memory word 4-byes higher. Register ++// up to RegisterImpl::number_of_registers) are the 32-bit ++// integer registers. ++ ++// Pass first five oop/int args in registers T0, A0 - A3. ++// Pass float/double/long args in stack. ++// Doubles have precedence, so if you pass a mix of floats and doubles ++// the doubles will grab the registers before the floats will. ++ ++// Note: the INPUTS in sig_bt are in units of Java argument words, which are ++// either 32-bit or 64-bit depending on the build. The OUTPUTS are in 32-bit ++// units regardless of build. ++ ++ ++// --------------------------------------------------------------------------- ++// The compiled Java calling convention. ++// Pass first five oop/int args in registers T0, A0 - A3. ++// Pass float/double/long args in stack. ++// Doubles have precedence, so if you pass a mix of floats and doubles ++// the doubles will grab the registers before the floats will. ++ ++int SharedRuntime::java_calling_convention(const BasicType *sig_bt, ++ VMRegPair *regs, ++ int total_args_passed, ++ int is_outgoing) { ++ ++ // Create the mapping between argument positions and registers. ++ static const Register INT_ArgReg[Argument::n_register_parameters] = { ++ T0, A0, A1, A2, A3, A4, A5, A6 ++ }; ++ static const FloatRegister FP_ArgReg[Argument::n_float_register_parameters] = { ++ F12, F13, F14, F15, F16, F17, F18, F19 ++ }; ++ ++ uint args = 0; ++ uint stk_args = 0; // inc by 2 each time ++ ++ for (int i = 0; i < total_args_passed; i++) { ++ switch (sig_bt[i]) { ++ case T_VOID: ++ // halves of T_LONG or T_DOUBLE ++ assert(i != 0 && (sig_bt[i - 1] == T_LONG || sig_bt[i - 1] == T_DOUBLE), "expecting half"); ++ regs[i].set_bad(); ++ break; ++ case T_BOOLEAN: ++ case T_CHAR: ++ case T_BYTE: ++ case T_SHORT: ++ case T_INT: ++ if (args < Argument::n_register_parameters) { ++ regs[i].set1(INT_ArgReg[args++]->as_VMReg()); ++ } else { ++ regs[i].set1(VMRegImpl::stack2reg(stk_args)); ++ stk_args += 2; ++ } ++ break; ++ case T_LONG: ++ assert(sig_bt[i + 1] == T_VOID, "expecting half"); ++ // fall through ++ case T_OBJECT: ++ case T_ARRAY: ++ case T_ADDRESS: ++ if (args < Argument::n_register_parameters) { ++ regs[i].set2(INT_ArgReg[args++]->as_VMReg()); ++ } else { ++ regs[i].set2(VMRegImpl::stack2reg(stk_args)); ++ stk_args += 2; ++ } ++ break; ++ case T_FLOAT: ++ if (args < Argument::n_float_register_parameters) { ++ regs[i].set1(FP_ArgReg[args++]->as_VMReg()); ++ } else { ++ regs[i].set1(VMRegImpl::stack2reg(stk_args)); ++ stk_args += 2; ++ } ++ break; ++ case T_DOUBLE: ++ assert(sig_bt[i + 1] == T_VOID, "expecting half"); ++ if (args < Argument::n_float_register_parameters) { ++ regs[i].set2(FP_ArgReg[args++]->as_VMReg()); ++ } else { ++ regs[i].set2(VMRegImpl::stack2reg(stk_args)); ++ stk_args += 2; ++ } ++ break; ++ default: ++ ShouldNotReachHere(); ++ break; ++ } ++ } ++ ++ return round_to(stk_args, 2); ++} ++ ++// Patch the callers callsite with entry to compiled code if it exists. ++static void patch_callers_callsite(MacroAssembler *masm) { ++ Label L; ++ __ verify_oop(Rmethod); ++ __ ld_ptr(AT, Rmethod, in_bytes(Method::code_offset())); ++ __ beq(AT, R0, L); ++ __ delayed()->nop(); ++ // Schedule the branch target address early. ++ // Call into the VM to patch the caller, then jump to compiled callee ++ // V0 isn't live so capture return address while we easily can ++ __ move(V0, RA); ++ ++ __ pushad(); ++#ifdef COMPILER2 ++ // C2 may leave the stack dirty if not in SSE2+ mode ++ __ empty_FPU_stack(); ++#endif ++ ++ // VM needs caller's callsite ++ // VM needs target method ++ ++ __ move(A0, Rmethod); ++ __ move(A1, V0); ++ // we should preserve the return address ++ __ verify_oop(Rmethod); ++ __ move(S0, SP); ++ __ move(AT, -(StackAlignmentInBytes)); // align the stack ++ __ andr(SP, SP, AT); ++ __ call(CAST_FROM_FN_PTR(address, SharedRuntime::fixup_callers_callsite), ++ relocInfo::runtime_call_type); ++ ++ __ delayed()->nop(); ++ __ move(SP, S0); ++ __ popad(); ++ __ bind(L); ++} ++ ++static void gen_c2i_adapter(MacroAssembler *masm, ++ int total_args_passed, ++ int comp_args_on_stack, ++ const BasicType *sig_bt, ++ const VMRegPair *regs, ++ Label& skip_fixup) { ++ ++ // Before we get into the guts of the C2I adapter, see if we should be here ++ // at all. We've come from compiled code and are attempting to jump to the ++ // interpreter, which means the caller made a static call to get here ++ // (vcalls always get a compiled target if there is one). Check for a ++ // compiled target. If there is one, we need to patch the caller's call. ++ // However we will run interpreted if we come thru here. The next pass ++ // thru the call site will run compiled. If we ran compiled here then ++ // we can (theorectically) do endless i2c->c2i->i2c transitions during ++ // deopt/uncommon trap cycles. If we always go interpreted here then ++ // we can have at most one and don't need to play any tricks to keep ++ // from endlessly growing the stack. ++ // ++ // Actually if we detected that we had an i2c->c2i transition here we ++ // ought to be able to reset the world back to the state of the interpreted ++ // call and not bother building another interpreter arg area. We don't ++ // do that at this point. ++ ++ patch_callers_callsite(masm); ++ __ bind(skip_fixup); ++ ++#ifdef COMPILER2 ++ __ empty_FPU_stack(); ++#endif ++ //this is for native ? ++ // Since all args are passed on the stack, total_args_passed * interpreter_ ++ // stack_element_size is the ++ // space we need. ++ int extraspace = total_args_passed * Interpreter::stackElementSize; ++ ++ // stack is aligned, keep it that way ++ extraspace = round_to(extraspace, 2*wordSize); ++ ++ // Get return address ++ __ move(V0, RA); ++ // set senderSP value ++ //refer to interpreter_mips.cpp:generate_asm_entry ++ __ move(Rsender, SP); ++ __ addiu(SP, SP, -extraspace); ++ ++ // Now write the args into the outgoing interpreter space ++ for (int i = 0; i < total_args_passed; i++) { ++ if (sig_bt[i] == T_VOID) { ++ assert(i > 0 && (sig_bt[i-1] == T_LONG || sig_bt[i-1] == T_DOUBLE), "missing half"); ++ continue; ++ } ++ ++ // st_off points to lowest address on stack. ++ int st_off = ((total_args_passed - 1) - i) * Interpreter::stackElementSize; ++ // Say 4 args: ++ // i st_off ++ // 0 12 T_LONG ++ // 1 8 T_VOID ++ // 2 4 T_OBJECT ++ // 3 0 T_BOOL ++ VMReg r_1 = regs[i].first(); ++ VMReg r_2 = regs[i].second(); ++ if (!r_1->is_valid()) { ++ assert(!r_2->is_valid(), ""); ++ continue; ++ } ++ if (r_1->is_stack()) { ++ // memory to memory use fpu stack top ++ int ld_off = r_1->reg2stack() * VMRegImpl::stack_slot_size + extraspace; ++ if (!r_2->is_valid()) { ++ __ ld_ptr(AT, SP, ld_off); ++ __ st_ptr(AT, SP, st_off); ++ ++ } else { ++ ++ ++ int next_off = st_off - Interpreter::stackElementSize; ++ __ ld_ptr(AT, SP, ld_off); ++ __ st_ptr(AT, SP, st_off); ++ ++ // Ref to is_Register condition ++ if(sig_bt[i] == T_LONG || sig_bt[i] == T_DOUBLE) ++ __ st_ptr(AT, SP, st_off - 8); ++ } ++ } else if (r_1->is_Register()) { ++ Register r = r_1->as_Register(); ++ if (!r_2->is_valid()) { ++ __ sd(r, SP, st_off); ++ } else { ++ //FIXME, mips will not enter here ++ // long/double in gpr ++ __ sd(r, SP, st_off); ++ // In [java/util/zip/ZipFile.java] ++ // ++ // private static native long open(String name, int mode, long lastModified); ++ // private static native int getTotal(long jzfile); ++ // ++ // We need to transfer T_LONG paramenters from a compiled method to a native method. ++ // It's a complex process: ++ // ++ // Caller -> lir_static_call -> gen_resolve_stub ++ // -> -- resolve_static_call_C ++ // `- gen_c2i_adapter() [*] ++ // | ++ // `- AdapterHandlerLibrary::get_create_apapter_index ++ // -> generate_native_entry ++ // -> InterpreterRuntime::SignatureHandlerGenerator::pass_long [**] ++ // ++ // In [**], T_Long parameter is stored in stack as: ++ // ++ // (high) ++ // | | ++ // ----------- ++ // | 8 bytes | ++ // | (void) | ++ // ----------- ++ // | 8 bytes | ++ // | (long) | ++ // ----------- ++ // | | ++ // (low) ++ // ++ // However, the sequence is reversed here: ++ // ++ // (high) ++ // | | ++ // ----------- ++ // | 8 bytes | ++ // | (long) | ++ // ----------- ++ // | 8 bytes | ++ // | (void) | ++ // ----------- ++ // | | ++ // (low) ++ // ++ // So I stored another 8 bytes in the T_VOID slot. It then can be accessed from generate_native_entry(). ++ // ++ if (sig_bt[i] == T_LONG) ++ __ sd(r, SP, st_off - 8); ++ } ++ } else if (r_1->is_FloatRegister()) { ++ assert(sig_bt[i] == T_FLOAT || sig_bt[i] == T_DOUBLE, "Must be a float register"); ++ ++ FloatRegister fr = r_1->as_FloatRegister(); ++ if (sig_bt[i] == T_FLOAT) ++ __ swc1(fr, SP, st_off); ++ else { ++ __ sdc1(fr, SP, st_off); ++ __ sdc1(fr, SP, st_off - 8); // T_DOUBLE needs two slots ++ } ++ } ++ } ++ ++ // Schedule the branch target address early. ++ __ ld_ptr(AT, Rmethod, in_bytes(Method::interpreter_entry_offset()) ); ++ // And repush original return address ++ __ move(RA, V0); ++ __ jr (AT); ++ __ delayed()->nop(); ++} ++ ++static void gen_i2c_adapter(MacroAssembler *masm, ++ int total_args_passed, ++ int comp_args_on_stack, ++ const BasicType *sig_bt, ++ const VMRegPair *regs) { ++ ++ // Generate an I2C adapter: adjust the I-frame to make space for the C-frame ++ // layout. Lesp was saved by the calling I-frame and will be restored on ++ // return. Meanwhile, outgoing arg space is all owned by the callee ++ // C-frame, so we can mangle it at will. After adjusting the frame size, ++ // hoist register arguments and repack other args according to the compiled ++ // code convention. Finally, end in a jump to the compiled code. The entry ++ // point address is the start of the buffer. ++ ++ // We will only enter here from an interpreted frame and never from after ++ // passing thru a c2i. Azul allowed this but we do not. If we lose the ++ // race and use a c2i we will remain interpreted for the race loser(s). ++ // This removes all sorts of headaches on the mips side and also eliminates ++ // the possibility of having c2i -> i2c -> c2i -> ... endless transitions. ++ ++ ++ __ move(T9, SP); ++ ++ // Cut-out for having no stack args. Since up to 2 int/oop args are passed ++ // in registers, we will occasionally have no stack args. ++ int comp_words_on_stack = 0; ++ if (comp_args_on_stack) { ++ // Sig words on the stack are greater-than VMRegImpl::stack0. Those in ++ // registers are below. By subtracting stack0, we either get a negative ++ // number (all values in registers) or the maximum stack slot accessed. ++ // int comp_args_on_stack = VMRegImpl::reg2stack(max_arg); ++ // Convert 4-byte stack slots to words. ++ comp_words_on_stack = round_to(comp_args_on_stack*4, wordSize)>>LogBytesPerWord; ++ // Round up to miminum stack alignment, in wordSize ++ comp_words_on_stack = round_to(comp_words_on_stack, 2); ++ __ daddiu(SP, SP, -comp_words_on_stack * wordSize); ++ } ++ ++ // Align the outgoing SP ++ __ move(AT, -(StackAlignmentInBytes)); ++ __ andr(SP, SP, AT); ++ // push the return address on the stack (note that pushing, rather ++ // than storing it, yields the correct frame alignment for the callee) ++ // Put saved SP in another register ++ const Register saved_sp = V0; ++ __ move(saved_sp, T9); ++ ++ ++ // Will jump to the compiled code just as if compiled code was doing it. ++ // Pre-load the register-jump target early, to schedule it better. ++ __ ld(T9, Rmethod, in_bytes(Method::from_compiled_offset())); ++ ++ // Now generate the shuffle code. Pick up all register args and move the ++ // rest through the floating point stack top. ++ for (int i = 0; i < total_args_passed; i++) { ++ if (sig_bt[i] == T_VOID) { ++ // Longs and doubles are passed in native word order, but misaligned ++ // in the 32-bit build. ++ assert(i > 0 && (sig_bt[i-1] == T_LONG || sig_bt[i-1] == T_DOUBLE), "missing half"); ++ continue; ++ } ++ ++ // Pick up 0, 1 or 2 words from SP+offset. ++ ++ //assert(!regs[i].second()->is_valid() || regs[i].first()->next() == regs[i].second(), "scrambled load targets?"); ++ // Load in argument order going down. ++ int ld_off = (total_args_passed -1 - i)*Interpreter::stackElementSize; ++ // Point to interpreter value (vs. tag) ++ int next_off = ld_off - Interpreter::stackElementSize; ++ VMReg r_1 = regs[i].first(); ++ VMReg r_2 = regs[i].second(); ++ if (!r_1->is_valid()) { ++ assert(!r_2->is_valid(), ""); ++ continue; ++ } ++ if (r_1->is_stack()) { ++ // Convert stack slot to an SP offset (+ wordSize to ++ // account for return address ) ++ // NOTICE HERE!!!! I sub a wordSize here ++ int st_off = regs[i].first()->reg2stack()*VMRegImpl::stack_slot_size; ++ //+ wordSize; ++ ++ if (!r_2->is_valid()) { ++ __ ld(AT, saved_sp, ld_off); ++ __ sd(AT, SP, st_off); ++ } else { ++ // Interpreter local[n] == MSW, local[n+1] == LSW however locals ++ // are accessed as negative so LSW is at LOW address ++ ++ // ld_off is MSW so get LSW ++ // st_off is LSW (i.e. reg.first()) ++ ++ // [./org/eclipse/swt/graphics/GC.java] ++ // void drawImageXRender(Image srcImage, int srcX, int srcY, int srcWidth, int srcHeight, ++ // int destX, int destY, int destWidth, int destHeight, ++ // boolean simple, ++ // int imgWidth, int imgHeight, ++ // long maskPixmap, <-- Pass T_LONG in stack ++ // int maskType); ++ // Before this modification, Eclipse displays icons with solid black background. ++ // ++ __ ld(AT, saved_sp, ld_off); ++ if (sig_bt[i] == T_LONG || sig_bt[i] == T_DOUBLE) ++ __ ld(AT, saved_sp, ld_off - 8); ++ __ sd(AT, SP, st_off); ++ } ++ } else if (r_1->is_Register()) { // Register argument ++ Register r = r_1->as_Register(); ++ if (r_2->is_valid()) { ++ // Remember r_1 is low address (and LSB on mips) ++ // So r_2 gets loaded from high address regardless of the platform ++ assert(r_2->as_Register() == r_1->as_Register(), ""); ++ __ ld(r, saved_sp, ld_off); ++ ++ // ++ // For T_LONG type, the real layout is as below: ++ // ++ // (high) ++ // | | ++ // ----------- ++ // | 8 bytes | ++ // | (void) | ++ // ----------- ++ // | 8 bytes | ++ // | (long) | ++ // ----------- ++ // | | ++ // (low) ++ // ++ // We should load the low-8 bytes. ++ // ++ if (sig_bt[i] == T_LONG) ++ __ ld(r, saved_sp, ld_off - 8); ++ } else { ++ __ lw(r, saved_sp, ld_off); ++ } ++ } else if (r_1->is_FloatRegister()) { // Float Register ++ assert(sig_bt[i] == T_FLOAT || sig_bt[i] == T_DOUBLE, "Must be a float register"); ++ ++ FloatRegister fr = r_1->as_FloatRegister(); ++ if (sig_bt[i] == T_FLOAT) ++ __ lwc1(fr, saved_sp, ld_off); ++ else { ++ __ ldc1(fr, saved_sp, ld_off); ++ __ ldc1(fr, saved_sp, ld_off - 8); ++ } ++ } ++ } ++ ++ // 6243940 We might end up in handle_wrong_method if ++ // the callee is deoptimized as we race thru here. If that ++ // happens we don't want to take a safepoint because the ++ // caller frame will look interpreted and arguments are now ++ // "compiled" so it is much better to make this transition ++ // invisible to the stack walking code. Unfortunately if ++ // we try and find the callee by normal means a safepoint ++ // is possible. So we stash the desired callee in the thread ++ // and the vm will find there should this case occur. ++ __ get_thread(T8); ++ __ sd(Rmethod, T8, in_bytes(JavaThread::callee_target_offset())); ++ ++ // move methodOop to V0 in case we end up in an c2i adapter. ++ // the c2i adapters expect methodOop in V0 (c2) because c2's ++ // resolve stubs return the result (the method) in V0. ++ // I'd love to fix this. ++ __ move(V0, Rmethod); ++ __ jr(T9); ++ __ delayed()->nop(); ++} ++ ++// --------------------------------------------------------------- ++AdapterHandlerEntry* SharedRuntime::generate_i2c2i_adapters(MacroAssembler *masm, ++ int total_args_passed, ++ int comp_args_on_stack, ++ const BasicType *sig_bt, ++ const VMRegPair *regs, ++ AdapterFingerPrint* fingerprint) { ++ address i2c_entry = __ pc(); ++ ++ gen_i2c_adapter(masm, total_args_passed, comp_args_on_stack, sig_bt, regs); ++ ++ // ------------------------------------------------------------------------- ++ // Generate a C2I adapter. On entry we know G5 holds the methodOop. The ++ // args start out packed in the compiled layout. They need to be unpacked ++ // into the interpreter layout. This will almost always require some stack ++ // space. We grow the current (compiled) stack, then repack the args. We ++ // finally end in a jump to the generic interpreter entry point. On exit ++ // from the interpreter, the interpreter will restore our SP (lest the ++ // compiled code, which relys solely on SP and not FP, get sick). ++ ++ address c2i_unverified_entry = __ pc(); ++ Label skip_fixup; ++ { ++ Register holder = T1; ++ Register receiver = T0; ++ Register temp = T8; ++ address ic_miss = SharedRuntime::get_ic_miss_stub(); ++ ++ Label missed; ++ ++ __ verify_oop(holder); ++ //add for compressedoops ++ __ load_klass(temp, receiver); ++ __ verify_oop(temp); ++ ++ __ ld_ptr(AT, holder, CompiledICHolder::holder_klass_offset()); ++ __ ld_ptr(Rmethod, holder, CompiledICHolder::holder_metadata_offset()); ++ __ bne(AT, temp, missed); ++ __ delayed()->nop(); ++ // Method might have been compiled since the call site was patched to ++ // interpreted if that is the case treat it as a miss so we can get ++ // the call site corrected. ++ __ ld_ptr(AT, Rmethod, in_bytes(Method::code_offset())); ++ __ beq(AT, R0, skip_fixup); ++ __ delayed()->nop(); ++ __ bind(missed); ++ ++ __ jmp(ic_miss, relocInfo::runtime_call_type); ++ __ delayed()->nop(); ++ } ++ ++ address c2i_entry = __ pc(); ++ ++ gen_c2i_adapter(masm, total_args_passed, comp_args_on_stack, sig_bt, regs, skip_fixup); ++ ++ __ flush(); ++ return AdapterHandlerLibrary::new_entry(fingerprint, i2c_entry, c2i_entry, c2i_unverified_entry); ++} ++ ++int SharedRuntime::c_calling_convention(const BasicType *sig_bt, ++ VMRegPair *regs, ++ VMRegPair *regs2, ++ int total_args_passed) { ++ assert(regs2 == NULL, "not needed on MIPS"); ++ // Return the number of VMReg stack_slots needed for the args. ++ // This value does not include an abi space (like register window ++ // save area). ++ ++ // We return the amount of VMReg stack slots we need to reserve for all ++ // the arguments NOT counting out_preserve_stack_slots. Since we always ++ // have space for storing at least 6 registers to memory we start with that. ++ // See int_stk_helper for a further discussion. ++ // We return the amount of VMRegImpl stack slots we need to reserve for all ++ // the arguments NOT counting out_preserve_stack_slots. ++ static const Register INT_ArgReg[Argument::n_register_parameters] = { ++ A0, A1, A2, A3, A4, A5, A6, A7 ++ }; ++ static const FloatRegister FP_ArgReg[Argument::n_float_register_parameters] = { ++ F12, F13, F14, F15, F16, F17, F18, F19 ++ }; ++ uint args = 0; ++ uint stk_args = 0; // inc by 2 each time ++ ++// Example: ++// n java.lang.UNIXProcess::forkAndExec ++// private native int forkAndExec(byte[] prog, ++// byte[] argBlock, int argc, ++// byte[] envBlock, int envc, ++// byte[] dir, ++// boolean redirectErrorStream, ++// FileDescriptor stdin_fd, ++// FileDescriptor stdout_fd, ++// FileDescriptor stderr_fd) ++// JNIEXPORT jint JNICALL ++// Java_java_lang_UNIXProcess_forkAndExec(JNIEnv *env, ++// jobject process, ++// jbyteArray prog, ++// jbyteArray argBlock, jint argc, ++// jbyteArray envBlock, jint envc, ++// jbyteArray dir, ++// jboolean redirectErrorStream, ++// jobject stdin_fd, ++// jobject stdout_fd, ++// jobject stderr_fd) ++// ++// ::c_calling_convention ++// 0: // env <-- a0 ++// 1: L // klass/obj <-- t0 => a1 ++// 2: [ // prog[] <-- a0 => a2 ++// 3: [ // argBlock[] <-- a1 => a3 ++// 4: I // argc <-- a2 => a4 ++// 5: [ // envBlock[] <-- a3 => a5 ++// 6: I // envc <-- a4 => a5 ++// 7: [ // dir[] <-- a5 => a7 ++// 8: Z // redirectErrorStream <-- a6 => sp[0] ++// 9: L // stdin fp[16] => sp[8] ++// 10: L // stdout fp[24] => sp[16] ++// 11: L // stderr fp[32] => sp[24] ++// ++ for (int i = 0; i < total_args_passed; i++) { ++ switch (sig_bt[i]) { ++ case T_VOID: // Halves of longs and doubles ++ assert(i != 0 && (sig_bt[i - 1] == T_LONG || sig_bt[i - 1] == T_DOUBLE), "expecting half"); ++ regs[i].set_bad(); ++ break; ++ case T_BOOLEAN: ++ case T_CHAR: ++ case T_BYTE: ++ case T_SHORT: ++ case T_INT: ++ if (args < Argument::n_register_parameters) { ++ regs[i].set1(INT_ArgReg[args++]->as_VMReg()); ++ } else { ++ regs[i].set1(VMRegImpl::stack2reg(stk_args)); ++ stk_args += 2; ++ } ++ break; ++ case T_LONG: ++ assert(sig_bt[i + 1] == T_VOID, "expecting half"); ++ // fall through ++ case T_OBJECT: ++ case T_ARRAY: ++ case T_ADDRESS: ++ case T_METADATA: ++ if (args < Argument::n_register_parameters) { ++ regs[i].set2(INT_ArgReg[args++]->as_VMReg()); ++ } else { ++ regs[i].set2(VMRegImpl::stack2reg(stk_args)); ++ stk_args += 2; ++ } ++ break; ++ case T_FLOAT: ++ if (args < Argument::n_float_register_parameters) { ++ regs[i].set1(FP_ArgReg[args++]->as_VMReg()); ++ } else { ++ regs[i].set1(VMRegImpl::stack2reg(stk_args)); ++ stk_args += 2; ++ } ++ break; ++ case T_DOUBLE: ++ assert(sig_bt[i + 1] == T_VOID, "expecting half"); ++ if (args < Argument::n_float_register_parameters) { ++ regs[i].set2(FP_ArgReg[args++]->as_VMReg()); ++ } else { ++ regs[i].set2(VMRegImpl::stack2reg(stk_args)); ++ stk_args += 2; ++ } ++ break; ++ default: ++ ShouldNotReachHere(); ++ break; ++ } ++ } ++ ++ return round_to(stk_args, 2); ++} ++ ++// --------------------------------------------------------------------------- ++void SharedRuntime::save_native_result(MacroAssembler *masm, BasicType ret_type, int frame_slots) { ++ // We always ignore the frame_slots arg and just use the space just below frame pointer ++ // which by this time is free to use ++ switch (ret_type) { ++ case T_FLOAT: ++ __ swc1(FSF, FP, -wordSize); ++ break; ++ case T_DOUBLE: ++ __ sdc1(FSF, FP, -wordSize ); ++ break; ++ case T_VOID: break; ++ case T_LONG: ++ __ sd(V0, FP, -wordSize); ++ break; ++ case T_OBJECT: ++ case T_ARRAY: ++ __ sd(V0, FP, -wordSize); ++ break; ++ default: { ++ __ sw(V0, FP, -wordSize); ++ } ++ } ++} ++ ++void SharedRuntime::restore_native_result(MacroAssembler *masm, BasicType ret_type, int frame_slots) { ++ // We always ignore the frame_slots arg and just use the space just below frame pointer ++ // which by this time is free to use ++ switch (ret_type) { ++ case T_FLOAT: ++ __ lwc1(FSF, FP, -wordSize); ++ break; ++ case T_DOUBLE: ++ __ ldc1(FSF, FP, -wordSize ); ++ break; ++ case T_LONG: ++ __ ld(V0, FP, -wordSize); ++ break; ++ case T_VOID: break; ++ case T_OBJECT: ++ case T_ARRAY: ++ __ ld(V0, FP, -wordSize); ++ break; ++ default: { ++ __ lw(V0, FP, -wordSize); ++ } ++ } ++} ++ ++static void save_args(MacroAssembler *masm, int arg_count, int first_arg, VMRegPair *args) { ++ for ( int i = first_arg ; i < arg_count ; i++ ) { ++ if (args[i].first()->is_Register()) { ++ __ push(args[i].first()->as_Register()); ++ } else if (args[i].first()->is_FloatRegister()) { ++ __ push(args[i].first()->as_FloatRegister()); ++ } ++ } ++} ++ ++static void restore_args(MacroAssembler *masm, int arg_count, int first_arg, VMRegPair *args) { ++ for ( int i = arg_count - 1 ; i >= first_arg ; i-- ) { ++ if (args[i].first()->is_Register()) { ++ __ pop(args[i].first()->as_Register()); ++ } else if (args[i].first()->is_FloatRegister()) { ++ __ pop(args[i].first()->as_FloatRegister()); ++ } ++ } ++} ++ ++// A simple move of integer like type ++static void simple_move32(MacroAssembler* masm, VMRegPair src, VMRegPair dst) { ++ if (src.first()->is_stack()) { ++ if (dst.first()->is_stack()) { ++ // stack to stack ++ __ lw(AT, FP, reg2offset_in(src.first())); ++ __ sd(AT, SP, reg2offset_out(dst.first())); ++ } else { ++ // stack to reg ++ __ lw(dst.first()->as_Register(), FP, reg2offset_in(src.first())); ++ } ++ } else if (dst.first()->is_stack()) { ++ // reg to stack ++ __ sd(src.first()->as_Register(), SP, reg2offset_out(dst.first())); ++ } else { ++ if (dst.first() != src.first()){ ++ __ move(dst.first()->as_Register(), src.first()->as_Register()); // fujie error:dst.first() ++ } ++ } ++} ++ ++// An oop arg. Must pass a handle not the oop itself ++static void object_move(MacroAssembler* masm, ++ OopMap* map, ++ int oop_handle_offset, ++ int framesize_in_slots, ++ VMRegPair src, ++ VMRegPair dst, ++ bool is_receiver, ++ int* receiver_offset) { ++ ++ // must pass a handle. First figure out the location we use as a handle ++ ++ //FIXME, for mips, dst can be register ++ if (src.first()->is_stack()) { ++ // Oop is already on the stack as an argument ++ Register rHandle = V0; ++ Label nil; ++ __ xorr(rHandle, rHandle, rHandle); ++ __ ld(AT, FP, reg2offset_in(src.first())); ++ __ beq(AT, R0, nil); ++ __ delayed()->nop(); ++ __ lea(rHandle, Address(FP, reg2offset_in(src.first()))); ++ __ bind(nil); ++ if(dst.first()->is_stack())__ sd( rHandle, SP, reg2offset_out(dst.first())); ++ else __ move( (dst.first())->as_Register(), rHandle); ++ //if dst is register ++ //FIXME, do mips need out preserve stack slots? ++ int offset_in_older_frame = src.first()->reg2stack() ++ + SharedRuntime::out_preserve_stack_slots(); ++ map->set_oop(VMRegImpl::stack2reg(offset_in_older_frame + framesize_in_slots)); ++ if (is_receiver) { ++ *receiver_offset = (offset_in_older_frame ++ + framesize_in_slots) * VMRegImpl::stack_slot_size; ++ } ++ } else { ++ // Oop is in an a register we must store it to the space we reserve ++ // on the stack for oop_handles ++ const Register rOop = src.first()->as_Register(); ++ assert( (rOop->encoding() >= A0->encoding()) && (rOop->encoding() <= T0->encoding()),"wrong register"); ++ const Register rHandle = V0; ++ //Important: refer to java_calling_convertion ++ int oop_slot = (rOop->encoding() - A0->encoding()) * VMRegImpl::slots_per_word + oop_handle_offset; ++ int offset = oop_slot*VMRegImpl::stack_slot_size; ++ Label skip; ++ __ sd( rOop , SP, offset ); ++ map->set_oop(VMRegImpl::stack2reg(oop_slot)); ++ __ xorr( rHandle, rHandle, rHandle); ++ __ beq(rOop, R0, skip); ++ __ delayed()->nop(); ++ __ lea(rHandle, Address(SP, offset)); ++ __ bind(skip); ++ // Store the handle parameter ++ if(dst.first()->is_stack())__ sd( rHandle, SP, reg2offset_out(dst.first())); ++ else __ move((dst.first())->as_Register(), rHandle); ++ //if dst is register ++ ++ if (is_receiver) { ++ *receiver_offset = offset; ++ } ++ } ++} ++ ++// A float arg may have to do float reg int reg conversion ++static void float_move(MacroAssembler* masm, VMRegPair src, VMRegPair dst) { ++ assert(!src.second()->is_valid() && !dst.second()->is_valid(), "bad float_move"); ++ ++ if (src.first()->is_stack()) { ++ if (dst.first()->is_stack()) { ++ __ lw(AT, FP, reg2offset_in(src.first())); ++ __ sw(AT, SP, reg2offset_out(dst.first())); ++ } ++ else ++ __ lwc1(dst.first()->as_FloatRegister(), FP, reg2offset_in(src.first())); ++ } else { ++ // reg to stack ++ if(dst.first()->is_stack()) ++ __ swc1(src.first()->as_FloatRegister(), SP, reg2offset_out(dst.first())); ++ else ++ __ mov_s(dst.first()->as_FloatRegister(), src.first()->as_FloatRegister()); ++ } ++} ++ ++// A long move ++static void long_move(MacroAssembler* masm, VMRegPair src, VMRegPair dst) { ++ ++ // The only legal possibility for a long_move VMRegPair is: ++ // 1: two stack slots (possibly unaligned) ++ // as neither the java or C calling convention will use registers ++ // for longs. ++ ++ if (src.first()->is_stack()) { ++ assert(src.second()->is_stack() && dst.second()->is_stack(), "must be all stack"); ++ if( dst.first()->is_stack()){ ++ __ ld(AT, FP, reg2offset_in(src.first())); ++ __ sd(AT, SP, reg2offset_out(dst.first())); ++ } else { ++ __ ld( (dst.first())->as_Register() , FP, reg2offset_in(src.first())); ++ } ++ } else { ++ if( dst.first()->is_stack()){ ++ __ sd( (src.first())->as_Register(), SP, reg2offset_out(dst.first())); ++ } else { ++ __ move( (dst.first())->as_Register() , (src.first())->as_Register()); ++ } ++ } ++} ++ ++// A double move ++static void double_move(MacroAssembler* masm, VMRegPair src, VMRegPair dst) { ++ ++ // The only legal possibilities for a double_move VMRegPair are: ++ // The painful thing here is that like long_move a VMRegPair might be ++ ++ // Because of the calling convention we know that src is either ++ // 1: a single physical register (xmm registers only) ++ // 2: two stack slots (possibly unaligned) ++ // dst can only be a pair of stack slots. ++ ++ ++ if (src.first()->is_stack()) { ++ // source is all stack ++ if( dst.first()->is_stack()){ ++ __ ld(AT, FP, reg2offset_in(src.first())); ++ __ sd(AT, SP, reg2offset_out(dst.first())); ++ } else { ++ __ ldc1( (dst.first())->as_FloatRegister(), FP, reg2offset_in(src.first())); ++ } ++ ++ } else { ++ // reg to stack ++ // No worries about stack alignment ++ if( dst.first()->is_stack()){ ++ __ sdc1(src.first()->as_FloatRegister(), SP, reg2offset_out(dst.first())); ++ } ++ else ++ __ mov_d( dst.first()->as_FloatRegister(), src.first()->as_FloatRegister()); ++ ++ } ++} ++ ++static void verify_oop_args(MacroAssembler* masm, ++ methodHandle method, ++ const BasicType* sig_bt, ++ const VMRegPair* regs) { ++ Register temp_reg = T9; // not part of any compiled calling seq ++ if (VerifyOops) { ++ for (int i = 0; i < method->size_of_parameters(); i++) { ++ if (sig_bt[i] == T_OBJECT || ++ sig_bt[i] == T_ARRAY) { ++ VMReg r = regs[i].first(); ++ assert(r->is_valid(), "bad oop arg"); ++ if (r->is_stack()) { ++ __ ld(temp_reg, Address(SP, r->reg2stack() * VMRegImpl::stack_slot_size + wordSize)); ++ __ verify_oop(temp_reg); ++ } else { ++ __ verify_oop(r->as_Register()); ++ } ++ } ++ } ++ } ++} ++ ++static void gen_special_dispatch(MacroAssembler* masm, ++ methodHandle method, ++ const BasicType* sig_bt, ++ const VMRegPair* regs) { ++ verify_oop_args(masm, method, sig_bt, regs); ++ vmIntrinsics::ID iid = method->intrinsic_id(); ++ ++ // Now write the args into the outgoing interpreter space ++ bool has_receiver = false; ++ Register receiver_reg = noreg; ++ int member_arg_pos = -1; ++ Register member_reg = noreg; ++ int ref_kind = MethodHandles::signature_polymorphic_intrinsic_ref_kind(iid); ++ if (ref_kind != 0) { ++ member_arg_pos = method->size_of_parameters() - 1; // trailing MemberName argument ++ member_reg = S3; // known to be free at this point ++ has_receiver = MethodHandles::ref_kind_has_receiver(ref_kind); ++ } else if (iid == vmIntrinsics::_invokeBasic) { ++ has_receiver = true; ++ } else { ++ fatal(err_msg_res("unexpected intrinsic id %d", iid)); ++ } ++ ++ if (member_reg != noreg) { ++ // Load the member_arg into register, if necessary. ++ SharedRuntime::check_member_name_argument_is_last_argument(method, sig_bt, regs); ++ VMReg r = regs[member_arg_pos].first(); ++ if (r->is_stack()) { ++ __ ld(member_reg, Address(SP, r->reg2stack() * VMRegImpl::stack_slot_size)); ++ } else { ++ // no data motion is needed ++ member_reg = r->as_Register(); ++ } ++ } ++ ++ if (has_receiver) { ++ // Make sure the receiver is loaded into a register. ++ assert(method->size_of_parameters() > 0, "oob"); ++ assert(sig_bt[0] == T_OBJECT, "receiver argument must be an object"); ++ VMReg r = regs[0].first(); ++ assert(r->is_valid(), "bad receiver arg"); ++ if (r->is_stack()) { ++ // Porting note: This assumes that compiled calling conventions always ++ // pass the receiver oop in a register. If this is not true on some ++ // platform, pick a temp and load the receiver from stack. ++ fatal("receiver always in a register"); ++ receiver_reg = SSR; // known to be free at this point ++ __ ld(receiver_reg, Address(SP, r->reg2stack() * VMRegImpl::stack_slot_size)); ++ } else { ++ // no data motion is needed ++ receiver_reg = r->as_Register(); ++ } ++ } ++ ++ // Figure out which address we are really jumping to: ++ MethodHandles::generate_method_handle_dispatch(masm, iid, ++ receiver_reg, member_reg, /*for_compiler_entry:*/ true); ++} ++ ++// --------------------------------------------------------------------------- ++// Generate a native wrapper for a given method. The method takes arguments ++// in the Java compiled code convention, marshals them to the native ++// convention (handlizes oops, etc), transitions to native, makes the call, ++// returns to java state (possibly blocking), unhandlizes any result and ++// returns. ++nmethod *SharedRuntime::generate_native_wrapper(MacroAssembler* masm, ++ methodHandle method, ++ int compile_id, ++ BasicType* in_sig_bt, ++ VMRegPair* in_regs, ++ BasicType ret_type) { ++ if (method->is_method_handle_intrinsic()) { ++ vmIntrinsics::ID iid = method->intrinsic_id(); ++ intptr_t start = (intptr_t)__ pc(); ++ int vep_offset = ((intptr_t)__ pc()) - start; ++ // Make enough room for patch_verified_entry ++ __ nop(); ++ __ nop(); ++ gen_special_dispatch(masm, ++ method, ++ in_sig_bt, ++ in_regs); ++ int frame_complete = ((intptr_t)__ pc()) - start; // not complete, period ++ __ flush(); ++ int stack_slots = SharedRuntime::out_preserve_stack_slots(); // no out slots at all, actually ++ return nmethod::new_native_nmethod(method, ++ compile_id, ++ masm->code(), ++ vep_offset, ++ frame_complete, ++ stack_slots / VMRegImpl::slots_per_word, ++ in_ByteSize(-1), ++ in_ByteSize(-1), ++ (OopMapSet*)NULL); ++ } ++ bool is_critical_native = true; ++ address native_func = method->critical_native_function(); ++ if (native_func == NULL) { ++ native_func = method->native_function(); ++ is_critical_native = false; ++ } ++ assert(native_func != NULL, "must have function"); ++ ++ // Native nmethod wrappers never take possesion of the oop arguments. ++ // So the caller will gc the arguments. The only thing we need an ++ // oopMap for is if the call is static ++ // ++ // An OopMap for lock (and class if static), and one for the VM call itself ++ OopMapSet *oop_maps = new OopMapSet(); ++ ++ // We have received a description of where all the java arg are located ++ // on entry to the wrapper. We need to convert these args to where ++ // the jni function will expect them. To figure out where they go ++ // we convert the java signature to a C signature by inserting ++ // the hidden arguments as arg[0] and possibly arg[1] (static method) ++ ++ const int total_in_args = method->size_of_parameters(); ++ int total_c_args = total_in_args; ++ if (!is_critical_native) { ++ total_c_args += 1; ++ if (method->is_static()) { ++ total_c_args++; ++ } ++ } else { ++ for (int i = 0; i < total_in_args; i++) { ++ if (in_sig_bt[i] == T_ARRAY) { ++ total_c_args++; ++ } ++ } ++ } ++ ++ BasicType* out_sig_bt = NEW_RESOURCE_ARRAY(BasicType, total_c_args); ++ VMRegPair* out_regs = NEW_RESOURCE_ARRAY(VMRegPair, total_c_args); ++ BasicType* in_elem_bt = NULL; ++ ++ int argc = 0; ++ if (!is_critical_native) { ++ out_sig_bt[argc++] = T_ADDRESS; ++ if (method->is_static()) { ++ out_sig_bt[argc++] = T_OBJECT; ++ } ++ ++ for (int i = 0; i < total_in_args ; i++ ) { ++ out_sig_bt[argc++] = in_sig_bt[i]; ++ } ++ } else { ++ Thread* THREAD = Thread::current(); ++ in_elem_bt = NEW_RESOURCE_ARRAY(BasicType, total_in_args); ++ SignatureStream ss(method->signature()); ++ for (int i = 0; i < total_in_args ; i++ ) { ++ if (in_sig_bt[i] == T_ARRAY) { ++ // Arrays are passed as int, elem* pair ++ out_sig_bt[argc++] = T_INT; ++ out_sig_bt[argc++] = T_ADDRESS; ++ Symbol* atype = ss.as_symbol(CHECK_NULL); ++ const char* at = atype->as_C_string(); ++ if (strlen(at) == 2) { ++ assert(at[0] == '[', "must be"); ++ switch (at[1]) { ++ case 'B': in_elem_bt[i] = T_BYTE; break; ++ case 'C': in_elem_bt[i] = T_CHAR; break; ++ case 'D': in_elem_bt[i] = T_DOUBLE; break; ++ case 'F': in_elem_bt[i] = T_FLOAT; break; ++ case 'I': in_elem_bt[i] = T_INT; break; ++ case 'J': in_elem_bt[i] = T_LONG; break; ++ case 'S': in_elem_bt[i] = T_SHORT; break; ++ case 'Z': in_elem_bt[i] = T_BOOLEAN; break; ++ default: ShouldNotReachHere(); ++ } ++ } ++ } else { ++ out_sig_bt[argc++] = in_sig_bt[i]; ++ in_elem_bt[i] = T_VOID; ++ } ++ if (in_sig_bt[i] != T_VOID) { ++ assert(in_sig_bt[i] == ss.type(), "must match"); ++ ss.next(); ++ } ++ } ++ } ++ ++ // Now figure out where the args must be stored and how much stack space ++ // they require (neglecting out_preserve_stack_slots but space for storing ++ // the 1st six register arguments). It's weird see int_stk_helper. ++ // ++ int out_arg_slots; ++ out_arg_slots = c_calling_convention(out_sig_bt, out_regs, NULL, total_c_args); ++ ++ // Compute framesize for the wrapper. We need to handlize all oops in ++ // registers. We must create space for them here that is disjoint from ++ // the windowed save area because we have no control over when we might ++ // flush the window again and overwrite values that gc has since modified. ++ // (The live window race) ++ // ++ // We always just allocate 6 word for storing down these object. This allow ++ // us to simply record the base and use the Ireg number to decide which ++ // slot to use. (Note that the reg number is the inbound number not the ++ // outbound number). ++ // We must shuffle args to match the native convention, and include var-args space. ++ ++ // Calculate the total number of stack slots we will need. ++ ++ // First count the abi requirement plus all of the outgoing args ++ int stack_slots = SharedRuntime::out_preserve_stack_slots() + out_arg_slots; ++ ++ // Now the space for the inbound oop handle area ++ int total_save_slots = 9 * VMRegImpl::slots_per_word; // 9 arguments passed in registers ++ if (is_critical_native) { ++ // Critical natives may have to call out so they need a save area ++ // for register arguments. ++ int double_slots = 0; ++ int single_slots = 0; ++ for ( int i = 0; i < total_in_args; i++) { ++ if (in_regs[i].first()->is_Register()) { ++ const Register reg = in_regs[i].first()->as_Register(); ++ switch (in_sig_bt[i]) { ++ case T_BOOLEAN: ++ case T_BYTE: ++ case T_SHORT: ++ case T_CHAR: ++ case T_INT: single_slots++; break; ++ case T_ARRAY: ++ case T_LONG: double_slots++; break; ++ default: ShouldNotReachHere(); ++ } ++ } else if (in_regs[i].first()->is_FloatRegister()) { ++ switch (in_sig_bt[i]) { ++ case T_FLOAT: single_slots++; break; ++ case T_DOUBLE: double_slots++; break; ++ default: ShouldNotReachHere(); ++ } ++ } ++ } ++ total_save_slots = double_slots * 2 + single_slots; ++ // align the save area ++ if (double_slots != 0) { ++ stack_slots = round_to(stack_slots, 2); ++ } ++ } ++ ++ int oop_handle_offset = stack_slots; ++ stack_slots += total_save_slots; ++ ++ // Now any space we need for handlizing a klass if static method ++ ++ int klass_slot_offset = 0; ++ int klass_offset = -1; ++ int lock_slot_offset = 0; ++ bool is_static = false; ++ ++ if (method->is_static()) { ++ klass_slot_offset = stack_slots; ++ stack_slots += VMRegImpl::slots_per_word; ++ klass_offset = klass_slot_offset * VMRegImpl::stack_slot_size; ++ is_static = true; ++ } ++ ++ // Plus a lock if needed ++ ++ if (method->is_synchronized()) { ++ lock_slot_offset = stack_slots; ++ stack_slots += VMRegImpl::slots_per_word; ++ } ++ ++ // Now a place to save return value or as a temporary for any gpr -> fpr moves ++ // + 2 for return address (which we own) and saved fp ++ stack_slots += 2 + 9 * VMRegImpl::slots_per_word; // (T0, A0, A1, A2, A3, A4, A5, A6, A7) ++ ++ // Ok The space we have allocated will look like: ++ // ++ // ++ // FP-> | | ++ // |---------------------| ++ // | 2 slots for moves | ++ // |---------------------| ++ // | lock box (if sync) | ++ // |---------------------| <- lock_slot_offset ++ // | klass (if static) | ++ // |---------------------| <- klass_slot_offset ++ // | oopHandle area | ++ // |---------------------| <- oop_handle_offset ++ // | outbound memory | ++ // | based arguments | ++ // | | ++ // |---------------------| ++ // | vararg area | ++ // |---------------------| ++ // | | ++ // SP-> | out_preserved_slots | ++ // ++ // ++ ++ ++ // Now compute actual number of stack words we need rounding to make ++ // stack properly aligned. ++ stack_slots = round_to(stack_slots, StackAlignmentInSlots); ++ ++ int stack_size = stack_slots * VMRegImpl::stack_slot_size; ++ ++ intptr_t start = (intptr_t)__ pc(); ++ ++ ++ ++ // First thing make an ic check to see if we should even be here ++ address ic_miss = SharedRuntime::get_ic_miss_stub(); ++ ++ // We are free to use all registers as temps without saving them and ++ // restoring them except fp. fp is the only callee save register ++ // as far as the interpreter and the compiler(s) are concerned. ++ ++ //refer to register_mips.hpp:IC_Klass ++ const Register ic_reg = T1; ++ const Register receiver = T0; ++ ++ Label hit; ++ Label exception_pending; ++ ++ __ verify_oop(receiver); ++ //add for compressedoops ++ __ load_klass(T9, receiver); ++ __ beq(T9, ic_reg, hit); ++ __ delayed()->nop(); ++ __ jmp(ic_miss, relocInfo::runtime_call_type); ++ __ delayed()->nop(); ++ __ bind(hit); ++ ++ int vep_offset = ((intptr_t)__ pc()) - start; ++ ++ // Make enough room for patch_verified_entry ++ __ nop(); ++ __ nop(); ++ ++ // Generate stack overflow check ++ if (UseStackBanging) { ++ __ bang_stack_with_offset(StackShadowPages*os::vm_page_size()); ++ } ++ ++ // Generate a new frame for the wrapper. ++ // do mips need this ? ++#ifndef OPT_THREAD ++ __ get_thread(TREG); ++#endif ++ __ st_ptr(SP, TREG, in_bytes(JavaThread::last_Java_sp_offset())); ++ __ move(AT, -(StackAlignmentInBytes)); ++ __ andr(SP, SP, AT); ++ ++ __ enter(); ++ // -2 because return address is already present and so is saved fp ++ __ addiu(SP, SP, -1 * (stack_size - 2*wordSize)); ++ ++ // Frame is now completed as far a size and linkage. ++ ++ int frame_complete = ((intptr_t)__ pc()) - start; ++ ++ // Calculate the difference between sp and fp. We need to know it ++ // after the native call because on windows Java Natives will pop ++ // the arguments and it is painful to do sp relative addressing ++ // in a platform independent way. So after the call we switch to ++ // fp relative addressing. ++ //FIXME actually , the fp_adjustment may not be the right, because andr(sp, sp, at) may change ++ //the SP ++ int fp_adjustment = stack_size - 2*wordSize; ++ ++#ifdef COMPILER2 ++ // C2 may leave the stack dirty if not in SSE2+ mode ++ __ empty_FPU_stack(); ++#endif ++ ++ // Compute the fp offset for any slots used after the jni call ++ ++ int lock_slot_fp_offset = (lock_slot_offset*VMRegImpl::stack_slot_size) - fp_adjustment; ++ // We use TREG as a thread pointer because it is callee save and ++ // if we load it once it is usable thru the entire wrapper ++ const Register thread = TREG; ++ ++ // We use S4 as the oop handle for the receiver/klass ++ // It is callee save so it survives the call to native ++ ++ const Register oop_handle_reg = S4; ++ if (is_critical_native) { ++ __ stop("generate_native_wrapper in sharedRuntime <2>"); ++ // check_needs_gc_for_critical_native(masm, stack_slots, total_c_args, total_in_args, ++ // oop_handle_offset, oop_maps, in_regs, in_sig_bt); ++ } ++ ++#ifndef OPT_THREAD ++ __ get_thread(thread); ++#endif ++ ++ // ++ // We immediately shuffle the arguments so that any vm call we have to ++ // make from here on out (sync slow path, jvmpi, etc.) we will have ++ // captured the oops from our caller and have a valid oopMap for ++ // them. ++ ++ // ----------------- ++ // The Grand Shuffle ++ // ++ // Natives require 1 or 2 extra arguments over the normal ones: the JNIEnv* ++ // and, if static, the class mirror instead of a receiver. This pretty much ++ // guarantees that register layout will not match (and mips doesn't use reg ++ // parms though amd does). Since the native abi doesn't use register args ++ // and the java conventions does we don't have to worry about collisions. ++ // All of our moved are reg->stack or stack->stack. ++ // We ignore the extra arguments during the shuffle and handle them at the ++ // last moment. The shuffle is described by the two calling convention ++ // vectors we have in our possession. We simply walk the java vector to ++ // get the source locations and the c vector to get the destinations. ++ ++ int c_arg = method->is_static() ? 2 : 1 ; ++ ++ // Record sp-based slot for receiver on stack for non-static methods ++ int receiver_offset = -1; ++ ++ // This is a trick. We double the stack slots so we can claim ++ // the oops in the caller's frame. Since we are sure to have ++ // more args than the caller doubling is enough to make ++ // sure we can capture all the incoming oop args from the ++ // caller. ++ // ++ OopMap* map = new OopMap(stack_slots * 2, 0 /* arg_slots*/); ++ ++ // Mark location of fp (someday) ++ // map->set_callee_saved(VMRegImpl::stack2reg( stack_slots - 2), stack_slots * 2, 0, vmreg(fp)); ++ ++#ifdef ASSERT ++ bool reg_destroyed[RegisterImpl::number_of_registers]; ++ bool freg_destroyed[FloatRegisterImpl::number_of_registers]; ++ for ( int r = 0 ; r < RegisterImpl::number_of_registers ; r++ ) { ++ reg_destroyed[r] = false; ++ } ++ for ( int f = 0 ; f < FloatRegisterImpl::number_of_registers ; f++ ) { ++ freg_destroyed[f] = false; ++ } ++ ++#endif /* ASSERT */ ++ ++ // This may iterate in two different directions depending on the ++ // kind of native it is. The reason is that for regular JNI natives ++ // the incoming and outgoing registers are offset upwards and for ++ // critical natives they are offset down. ++ GrowableArray arg_order(2 * total_in_args); ++ VMRegPair tmp_vmreg; ++ tmp_vmreg.set1(T8->as_VMReg()); ++ ++ if (!is_critical_native) { ++ for (int i = total_in_args - 1, c_arg = total_c_args - 1; i >= 0; i--, c_arg--) { ++ arg_order.push(i); ++ arg_order.push(c_arg); ++ } ++ } else { ++ // Compute a valid move order, using tmp_vmreg to break any cycles ++ __ stop("generate_native_wrapper in sharedRuntime <2>"); ++ // ComputeMoveOrder cmo(total_in_args, in_regs, total_c_args, out_regs, in_sig_bt, arg_order, tmp_vmreg); ++ } ++ ++ int temploc = -1; ++ for (int ai = 0; ai < arg_order.length(); ai += 2) { ++ int i = arg_order.at(ai); ++ int c_arg = arg_order.at(ai + 1); ++ __ block_comment(err_msg("move %d -> %d", i, c_arg)); ++ if (c_arg == -1) { ++ assert(is_critical_native, "should only be required for critical natives"); ++ // This arg needs to be moved to a temporary ++ __ move(tmp_vmreg.first()->as_Register(), in_regs[i].first()->as_Register()); ++ in_regs[i] = tmp_vmreg; ++ temploc = i; ++ continue; ++ } else if (i == -1) { ++ assert(is_critical_native, "should only be required for critical natives"); ++ // Read from the temporary location ++ assert(temploc != -1, "must be valid"); ++ i = temploc; ++ temploc = -1; ++ } ++#ifdef ASSERT ++ if (in_regs[i].first()->is_Register()) { ++ assert(!reg_destroyed[in_regs[i].first()->as_Register()->encoding()], "destroyed reg!"); ++ } else if (in_regs[i].first()->is_FloatRegister()) { ++ assert(!freg_destroyed[in_regs[i].first()->as_FloatRegister()->encoding()], "destroyed reg!"); ++ } ++ if (out_regs[c_arg].first()->is_Register()) { ++ reg_destroyed[out_regs[c_arg].first()->as_Register()->encoding()] = true; ++ } else if (out_regs[c_arg].first()->is_FloatRegister()) { ++ freg_destroyed[out_regs[c_arg].first()->as_FloatRegister()->encoding()] = true; ++ } ++#endif /* ASSERT */ ++ switch (in_sig_bt[i]) { ++ case T_ARRAY: ++ if (is_critical_native) { ++ __ stop("generate_native_wrapper in sharedRuntime <2>"); ++ // unpack_array_argument(masm, in_regs[i], in_elem_bt[i], out_regs[c_arg + 1], out_regs[c_arg]); ++ c_arg++; ++#ifdef ASSERT ++ if (out_regs[c_arg].first()->is_Register()) { ++ reg_destroyed[out_regs[c_arg].first()->as_Register()->encoding()] = true; ++ } else if (out_regs[c_arg].first()->is_FloatRegister()) { ++ freg_destroyed[out_regs[c_arg].first()->as_FloatRegister()->encoding()] = true; ++ } ++#endif ++ break; ++ } ++ case T_OBJECT: ++ assert(!is_critical_native, "no oop arguments"); ++ object_move(masm, map, oop_handle_offset, stack_slots, in_regs[i], out_regs[c_arg], ++ ((i == 0) && (!is_static)), ++ &receiver_offset); ++ break; ++ case T_VOID: ++ break; ++ ++ case T_FLOAT: ++ float_move(masm, in_regs[i], out_regs[c_arg]); ++ break; ++ ++ case T_DOUBLE: ++ assert( i + 1 < total_in_args && ++ in_sig_bt[i + 1] == T_VOID && ++ out_sig_bt[c_arg+1] == T_VOID, "bad arg list"); ++ double_move(masm, in_regs[i], out_regs[c_arg]); ++ break; ++ ++ case T_LONG : ++ long_move(masm, in_regs[i], out_regs[c_arg]); ++ break; ++ ++ case T_ADDRESS: assert(false, "found T_ADDRESS in java args"); ++ ++ default: ++ simple_move32(masm, in_regs[i], out_regs[c_arg]); ++ } ++ } ++ ++ // point c_arg at the first arg that is already loaded in case we ++ // need to spill before we call out ++ c_arg = total_c_args - total_in_args; ++ // Pre-load a static method's oop. Used both by locking code and ++ // the normal JNI call code. ++ ++ __ move(oop_handle_reg, A1); ++ ++ if (method->is_static() && !is_critical_native) { ++ ++ // load opp into a register ++ int oop_index = __ oop_recorder()->find_index(JNIHandles::make_local( ++ (method->method_holder())->java_mirror())); ++ ++ ++ RelocationHolder rspec = oop_Relocation::spec(oop_index); ++ __ relocate(rspec); ++ __ patchable_set48(oop_handle_reg, (long)JNIHandles::make_local((method->method_holder())->java_mirror())); ++ // Now handlize the static class mirror it's known not-null. ++ __ sd( oop_handle_reg, SP, klass_offset); ++ map->set_oop(VMRegImpl::stack2reg(klass_slot_offset)); ++ ++ // Now get the handle ++ __ lea(oop_handle_reg, Address(SP, klass_offset)); ++ // store the klass handle as second argument ++ __ move(A1, oop_handle_reg); ++ // and protect the arg if we must spill ++ c_arg--; ++ } ++ ++ // Change state to native (we save the return address in the thread, since it might not ++ // be pushed on the stack when we do a a stack traversal). It is enough that the pc() ++ // points into the right code segment. It does not have to be the correct return pc. ++ // We use the same pc/oopMap repeatedly when we call out ++ ++ intptr_t the_pc = (intptr_t) __ pc(); ++ oop_maps->add_gc_map(the_pc - start, map); ++ ++ __ set_last_Java_frame(SP, noreg, NULL); ++ __ relocate(relocInfo::internal_pc_type); ++ { ++ intptr_t save_pc = (intptr_t)the_pc ; ++ __ patchable_set48(AT, save_pc); ++ } ++ __ sd(AT, thread, in_bytes(JavaThread::frame_anchor_offset() + JavaFrameAnchor::last_Java_pc_offset())); ++ ++ ++ // We have all of the arguments setup at this point. We must not touch any register ++ // argument registers at this point (what if we save/restore them there are no oop? ++ { ++ SkipIfEqual skip_if(masm, &DTraceMethodProbes, 0); ++ save_args(masm, total_c_args, c_arg, out_regs); ++ int metadata_index = __ oop_recorder()->find_index(method()); ++ RelocationHolder rspec = metadata_Relocation::spec(metadata_index); ++ __ relocate(rspec); ++ __ patchable_set48(AT, (long)(method())); ++ ++ __ call_VM_leaf( ++ CAST_FROM_FN_PTR(address, SharedRuntime::dtrace_method_entry), ++ thread, AT); ++ ++ restore_args(masm, total_c_args, c_arg, out_regs); ++ } ++ ++ // These are register definitions we need for locking/unlocking ++ const Register swap_reg = T8; // Must use T8 for cmpxchg instruction ++ const Register obj_reg = T9; // Will contain the oop ++ //const Register lock_reg = T6; // Address of compiler lock object (BasicLock) ++ const Register lock_reg = c_rarg0; // Address of compiler lock object (BasicLock) ++ ++ ++ ++ Label slow_path_lock; ++ Label lock_done; ++ ++ // Lock a synchronized method ++ if (method->is_synchronized()) { ++ assert(!is_critical_native, "unhandled"); ++ ++ const int mark_word_offset = BasicLock::displaced_header_offset_in_bytes(); ++ ++ // Get the handle (the 2nd argument) ++ __ move(oop_handle_reg, A1); ++ ++ // Get address of the box ++ __ lea(lock_reg, Address(FP, lock_slot_fp_offset)); ++ ++ // Load the oop from the handle ++ __ ld(obj_reg, oop_handle_reg, 0); ++ ++ if (UseBiasedLocking) { ++ // Note that oop_handle_reg is trashed during this call ++ __ biased_locking_enter(lock_reg, obj_reg, swap_reg, A1, false, lock_done, &slow_path_lock); ++ } ++ ++ // Load immediate 1 into swap_reg %T8 ++ __ move(swap_reg, 1); ++ ++ __ ld(AT, obj_reg, 0); ++ __ orr(swap_reg, swap_reg, AT); ++ ++ __ sd( swap_reg, lock_reg, mark_word_offset); ++ __ cmpxchg(lock_reg, Address(obj_reg, 0), swap_reg); ++ __ bne(AT, R0, lock_done); ++ __ delayed()->nop(); ++ // Test if the oopMark is an obvious stack pointer, i.e., ++ // 1) (mark & 3) == 0, and ++ // 2) sp <= mark < mark + os::pagesize() ++ // These 3 tests can be done by evaluating the following ++ // expression: ((mark - sp) & (3 - os::vm_page_size())), ++ // assuming both stack pointer and pagesize have their ++ // least significant 2 bits clear. ++ // NOTE: the oopMark is in swap_reg %T8 as the result of cmpxchg ++ ++ __ dsubu(swap_reg, swap_reg, SP); ++ __ move(AT, 3 - os::vm_page_size()); ++ __ andr(swap_reg , swap_reg, AT); ++ // Save the test result, for recursive case, the result is zero ++ __ sd(swap_reg, lock_reg, mark_word_offset); ++ __ bne(swap_reg, R0, slow_path_lock); ++ __ delayed()->nop(); ++ // Slow path will re-enter here ++ __ bind(lock_done); ++ ++ if (UseBiasedLocking) { ++ // Re-fetch oop_handle_reg as we trashed it above ++ __ move(A1, oop_handle_reg); ++ } ++ } ++ ++ ++ // Finally just about ready to make the JNI call ++ ++ ++ // get JNIEnv* which is first argument to native ++ if (!is_critical_native) { ++ __ addiu(A0, thread, in_bytes(JavaThread::jni_environment_offset())); ++ } ++ ++ // Example: Java_java_lang_ref_Finalizer_invokeFinalizeMethod(JNIEnv *env, jclass clazz, jobject ob) ++ // Load the second arguments into A1 ++ //__ ld(A1, SP , wordSize ); // klass ++ ++ // Now set thread in native ++ __ addiu(AT, R0, _thread_in_native); ++ if(os::is_MP()) { ++ __ sync(); // store release ++ } ++ __ sw(AT, thread, in_bytes(JavaThread::thread_state_offset())); ++ // do the call ++ __ call(method->native_function(), relocInfo::runtime_call_type); ++ __ delayed()->nop(); ++ // WARNING - on Windows Java Natives use pascal calling convention and pop the ++ // arguments off of the stack. We could just re-adjust the stack pointer here ++ // and continue to do SP relative addressing but we instead switch to FP ++ // relative addressing. ++ ++ // Unpack native results. ++ switch (ret_type) { ++ case T_BOOLEAN: __ c2bool(V0); break; ++ case T_CHAR : __ andi(V0, V0, 0xFFFF); break; ++ case T_BYTE : __ sign_extend_byte (V0); break; ++ case T_SHORT : __ sign_extend_short(V0); break; ++ case T_INT : // nothing to do break; ++ case T_DOUBLE : ++ case T_FLOAT : ++ // Result is in st0 we'll save as needed ++ break; ++ case T_ARRAY: // Really a handle ++ case T_OBJECT: // Really a handle ++ break; // can't de-handlize until after safepoint check ++ case T_VOID: break; ++ case T_LONG: break; ++ default : ShouldNotReachHere(); ++ } ++ // Switch thread to "native transition" state before reading the synchronization state. ++ // This additional state is necessary because reading and testing the synchronization ++ // state is not atomic w.r.t. GC, as this scenario demonstrates: ++ // Java thread A, in _thread_in_native state, loads _not_synchronized and is preempted. ++ // VM thread changes sync state to synchronizing and suspends threads for GC. ++ // Thread A is resumed to finish this native method, but doesn't block here since it ++ // didn't see any synchronization is progress, and escapes. ++ __ addiu(AT, R0, _thread_in_native_trans); ++ if(os::is_MP()) { ++ __ sync(); // store release ++ } ++ __ sw(AT, thread, in_bytes(JavaThread::thread_state_offset())); ++ ++ if(os::is_MP()) { ++ if (UseMembar) { ++ // Force this write out before the read below ++ __ sync(); ++ } else { ++ // Write serialization page so VM thread can do a pseudo remote membar. ++ // We use the current thread pointer to calculate a thread specific ++ // offset to write to within the page. This minimizes bus traffic ++ // due to cache line collision. ++ __ serialize_memory(thread, A0); ++ } ++ } ++ ++ Label after_transition; ++ ++ // check for safepoint operation in progress and/or pending suspend requests ++ { ++ Label Continue; ++ __ li(AT, SafepointSynchronize::address_of_state()); ++ __ lw(A0, AT, 0); ++ __ addiu(AT, A0, -SafepointSynchronize::_not_synchronized); ++ Label L; ++ __ bne(AT, R0, L); ++ __ delayed()->nop(); ++ __ lw(AT, thread, in_bytes(JavaThread::suspend_flags_offset())); ++ __ beq(AT, R0, Continue); ++ __ delayed()->nop(); ++ __ bind(L); ++ ++ // Don't use call_VM as it will see a possible pending exception and forward it ++ // and never return here preventing us from clearing _last_native_pc down below. ++ // ++ save_native_result(masm, ret_type, stack_slots); ++ __ move(A0, thread); ++ __ addiu(SP, SP, -wordSize); ++ __ push(S2); ++ __ move(AT, -(StackAlignmentInBytes)); ++ __ move(S2, SP); // use S2 as a sender SP holder ++ __ andr(SP, SP, AT); // align stack as required by ABI ++ if (!is_critical_native) { ++ __ call(CAST_FROM_FN_PTR(address, JavaThread::check_special_condition_for_native_trans), relocInfo::runtime_call_type); ++ __ delayed()->nop(); ++ } else { ++ __ call(CAST_FROM_FN_PTR(address, JavaThread::check_special_condition_for_native_trans_and_transition), relocInfo::runtime_call_type); ++ __ delayed()->nop(); ++ } ++ __ move(SP, S2); // use S2 as a sender SP holder ++ __ pop(S2); ++ __ addiu(SP, SP, wordSize); ++ //add for compressedoops ++ __ reinit_heapbase(); ++ // Restore any method result value ++ restore_native_result(masm, ret_type, stack_slots); ++ ++ if (is_critical_native) { ++ // The call above performed the transition to thread_in_Java so ++ // skip the transition logic below. ++ __ beq(R0, R0, after_transition); ++ __ delayed()->nop(); ++ } ++ ++ __ bind(Continue); ++ } ++ ++ // change thread state ++ __ addiu(AT, R0, _thread_in_Java); ++ if(os::is_MP()) { ++ __ sync(); // store release ++ } ++ __ sw(AT, thread, in_bytes(JavaThread::thread_state_offset())); ++ __ bind(after_transition); ++ Label reguard; ++ Label reguard_done; ++ __ lw(AT, thread, in_bytes(JavaThread::stack_guard_state_offset())); ++ __ addiu(AT, AT, -JavaThread::stack_guard_yellow_disabled); ++ __ beq(AT, R0, reguard); ++ __ delayed()->nop(); ++ // slow path reguard re-enters here ++ __ bind(reguard_done); ++ ++ // Handle possible exception (will unlock if necessary) ++ ++ // native result if any is live ++ ++ // Unlock ++ Label slow_path_unlock; ++ Label unlock_done; ++ if (method->is_synchronized()) { ++ ++ Label done; ++ ++ // Get locked oop from the handle we passed to jni ++ __ ld( obj_reg, oop_handle_reg, 0); ++ if (UseBiasedLocking) { ++ __ biased_locking_exit(obj_reg, T8, done); ++ ++ } ++ ++ // Simple recursive lock? ++ ++ __ ld(AT, FP, lock_slot_fp_offset); ++ __ beq(AT, R0, done); ++ __ delayed()->nop(); ++ // Must save FSF if if it is live now because cmpxchg must use it ++ if (ret_type != T_FLOAT && ret_type != T_DOUBLE && ret_type != T_VOID) { ++ save_native_result(masm, ret_type, stack_slots); ++ } ++ ++ // get old displaced header ++ __ ld (T8, FP, lock_slot_fp_offset); ++ // get address of the stack lock ++ __ addiu(c_rarg0, FP, lock_slot_fp_offset); ++ // Atomic swap old header if oop still contains the stack lock ++ __ cmpxchg(T8, Address(obj_reg, 0), c_rarg0); ++ ++ __ beq(AT, R0, slow_path_unlock); ++ __ delayed()->nop(); ++ // slow path re-enters here ++ __ bind(unlock_done); ++ if (ret_type != T_FLOAT && ret_type != T_DOUBLE && ret_type != T_VOID) { ++ restore_native_result(masm, ret_type, stack_slots); ++ } ++ ++ __ bind(done); ++ ++ } ++ { ++ SkipIfEqual skip_if(masm, &DTraceMethodProbes, 0); ++ // Tell dtrace about this method exit ++ save_native_result(masm, ret_type, stack_slots); ++ int metadata_index = __ oop_recorder()->find_index( (method())); ++ RelocationHolder rspec = metadata_Relocation::spec(metadata_index); ++ __ relocate(rspec); ++ __ patchable_set48(AT, (long)(method())); ++ ++ __ call_VM_leaf( ++ CAST_FROM_FN_PTR(address, SharedRuntime::dtrace_method_exit), ++ thread, AT); ++ restore_native_result(masm, ret_type, stack_slots); ++ } ++ ++ // We can finally stop using that last_Java_frame we setup ages ago ++ ++ __ reset_last_Java_frame(false); ++ ++ // Unpack oop result, e.g. JNIHandles::resolve value. ++ if (ret_type == T_OBJECT || ret_type == T_ARRAY) { ++ __ resolve_jobject(V0, thread, T9); ++ } ++ ++ if (!is_critical_native) { ++ // reset handle block ++ __ ld(AT, thread, in_bytes(JavaThread::active_handles_offset())); ++ __ sw(R0, AT, JNIHandleBlock::top_offset_in_bytes()); ++ } ++ ++ if (!is_critical_native) { ++ // Any exception pending? ++ __ ld(AT, thread, in_bytes(Thread::pending_exception_offset())); ++ __ bne(AT, R0, exception_pending); ++ __ delayed()->nop(); ++ } ++ // no exception, we're almost done ++ ++ // check that only result value is on FPU stack ++ __ verify_FPU(ret_type == T_FLOAT || ret_type == T_DOUBLE ? 1 : 0, "native_wrapper normal exit"); ++ ++ // Return ++#ifndef OPT_THREAD ++ __ get_thread(TREG); ++#endif ++ //__ ld_ptr(SP, TREG, in_bytes(JavaThread::last_Java_sp_offset())); ++ __ leave(); ++ ++ __ jr(RA); ++ __ delayed()->nop(); ++ // Unexpected paths are out of line and go here ++ // Slow path locking & unlocking ++ if (method->is_synchronized()) { ++ ++ // BEGIN Slow path lock ++ __ bind(slow_path_lock); ++ ++ // protect the args we've loaded ++ save_args(masm, total_c_args, c_arg, out_regs); ++ ++ // has last_Java_frame setup. No exceptions so do vanilla call not call_VM ++ // args are (oop obj, BasicLock* lock, JavaThread* thread) ++ ++ __ move(A0, obj_reg); ++ __ move(A1, lock_reg); ++ __ move(A2, thread); ++ __ addiu(SP, SP, - 3*wordSize); ++ ++ __ move(AT, -(StackAlignmentInBytes)); ++ __ move(S2, SP); // use S2 as a sender SP holder ++ __ andr(SP, SP, AT); // align stack as required by ABI ++ ++ __ call(CAST_FROM_FN_PTR(address, SharedRuntime::complete_monitor_locking_C), relocInfo::runtime_call_type); ++ __ delayed()->nop(); ++ __ move(SP, S2); ++ __ addiu(SP, SP, 3*wordSize); ++ ++ restore_args(masm, total_c_args, c_arg, out_regs); ++ ++#ifdef ASSERT ++ { Label L; ++ __ ld(AT, thread, in_bytes(Thread::pending_exception_offset())); ++ __ beq(AT, R0, L); ++ __ delayed()->nop(); ++ __ stop("no pending exception allowed on exit from monitorenter"); ++ __ bind(L); ++ } ++#endif ++ __ b(lock_done); ++ __ delayed()->nop(); ++ // END Slow path lock ++ ++ // BEGIN Slow path unlock ++ __ bind(slow_path_unlock); ++ ++ // Slow path unlock ++ ++ if (ret_type == T_FLOAT || ret_type == T_DOUBLE ) { ++ save_native_result(masm, ret_type, stack_slots); ++ } ++ // Save pending exception around call to VM (which contains an EXCEPTION_MARK) ++ ++ __ ld(AT, thread, in_bytes(Thread::pending_exception_offset())); ++ __ push(AT); ++ __ sd(R0, thread, in_bytes(Thread::pending_exception_offset())); ++ ++ __ move(AT, -(StackAlignmentInBytes)); ++ __ move(S2, SP); // use S2 as a sender SP holder ++ __ andr(SP, SP, AT); // align stack as required by ABI ++ ++ // should be a peal ++ // +wordSize because of the push above ++ __ addiu(A1, FP, lock_slot_fp_offset); ++ ++ __ move(A0, obj_reg); ++ __ addiu(SP,SP, -2*wordSize); ++ __ call(CAST_FROM_FN_PTR(address, SharedRuntime::complete_monitor_unlocking_C), ++ relocInfo::runtime_call_type); ++ __ delayed()->nop(); ++ __ addiu(SP, SP, 2*wordSize); ++ __ move(SP, S2); ++ //add for compressedoops ++ __ reinit_heapbase(); ++#ifdef ASSERT ++ { ++ Label L; ++ __ ld( AT, thread, in_bytes(Thread::pending_exception_offset())); ++ __ beq(AT, R0, L); ++ __ delayed()->nop(); ++ __ stop("no pending exception allowed on exit complete_monitor_unlocking_C"); ++ __ bind(L); ++ } ++#endif /* ASSERT */ ++ ++ __ pop(AT); ++ __ sd(AT, thread, in_bytes(Thread::pending_exception_offset())); ++ if (ret_type == T_FLOAT || ret_type == T_DOUBLE ) { ++ restore_native_result(masm, ret_type, stack_slots); ++ } ++ __ b(unlock_done); ++ __ delayed()->nop(); ++ // END Slow path unlock ++ ++ } ++ ++ // SLOW PATH Reguard the stack if needed ++ ++ __ bind(reguard); ++ save_native_result(masm, ret_type, stack_slots); ++ __ call(CAST_FROM_FN_PTR(address, SharedRuntime::reguard_yellow_pages), ++ relocInfo::runtime_call_type); ++ __ delayed()->nop(); ++ //add for compressedoops ++ __ reinit_heapbase(); ++ restore_native_result(masm, ret_type, stack_slots); ++ __ b(reguard_done); ++ __ delayed()->nop(); ++ ++ // BEGIN EXCEPTION PROCESSING ++ if (!is_critical_native) { ++ // Forward the exception ++ __ bind(exception_pending); ++ ++ // remove possible return value from FPU register stack ++ __ empty_FPU_stack(); ++ ++ // pop our frame ++ //forward_exception_entry need return address on stack ++ __ move(SP, FP); ++ __ pop(FP); ++ ++ // and forward the exception ++ __ jmp(StubRoutines::forward_exception_entry(), relocInfo::runtime_call_type); ++ __ delayed()->nop(); ++ } ++ __ flush(); ++ ++ nmethod *nm = nmethod::new_native_nmethod(method, ++ compile_id, ++ masm->code(), ++ vep_offset, ++ frame_complete, ++ stack_slots / VMRegImpl::slots_per_word, ++ (is_static ? in_ByteSize(klass_offset) : in_ByteSize(receiver_offset)), ++ in_ByteSize(lock_slot_offset*VMRegImpl::stack_slot_size), ++ oop_maps); ++ ++ if (is_critical_native) { ++ nm->set_lazy_critical_native(true); ++ } ++ ++ return nm; ++ ++} ++ ++#ifdef HAVE_DTRACE_H ++// --------------------------------------------------------------------------- ++// Generate a dtrace nmethod for a given signature. The method takes arguments ++// in the Java compiled code convention, marshals them to the native ++// abi and then leaves nops at the position you would expect to call a native ++// function. When the probe is enabled the nops are replaced with a trap ++// instruction that dtrace inserts and the trace will cause a notification ++// to dtrace. ++// ++// The probes are only able to take primitive types and java/lang/String as ++// arguments. No other java types are allowed. Strings are converted to utf8 ++// strings so that from dtrace point of view java strings are converted to C ++// strings. There is an arbitrary fixed limit on the total space that a method ++// can use for converting the strings. (256 chars per string in the signature). ++// So any java string larger then this is truncated. ++ ++static int fp_offset[ConcreteRegisterImpl::number_of_registers] = { 0 }; ++static bool offsets_initialized = false; ++ ++static VMRegPair reg64_to_VMRegPair(Register r) { ++ VMRegPair ret; ++ if (wordSize == 8) { ++ ret.set2(r->as_VMReg()); ++ } else { ++ ret.set_pair(r->successor()->as_VMReg(), r->as_VMReg()); ++ } ++ return ret; ++} ++ ++ ++nmethod *SharedRuntime::generate_dtrace_nmethod(MacroAssembler *masm, ++ methodHandle method) { ++ ++ ++ // generate_dtrace_nmethod is guarded by a mutex so we are sure to ++ // be single threaded in this method. ++ assert(AdapterHandlerLibrary_lock->owned_by_self(), "must be"); ++ ++ // Fill in the signature array, for the calling-convention call. ++ int total_args_passed = method->size_of_parameters(); ++ ++ BasicType* in_sig_bt = NEW_RESOURCE_ARRAY(BasicType, total_args_passed); ++ VMRegPair *in_regs = NEW_RESOURCE_ARRAY(VMRegPair, total_args_passed); ++ ++ // The signature we are going to use for the trap that dtrace will see ++ // java/lang/String is converted. We drop "this" and any other object ++ // is converted to NULL. (A one-slot java/lang/Long object reference ++ // is converted to a two-slot long, which is why we double the allocation). ++ BasicType* out_sig_bt = NEW_RESOURCE_ARRAY(BasicType, total_args_passed * 2); ++ VMRegPair* out_regs = NEW_RESOURCE_ARRAY(VMRegPair, total_args_passed * 2); ++ ++ int i=0; ++ int total_strings = 0; ++ int first_arg_to_pass = 0; ++ int total_c_args = 0; ++ ++ // Skip the receiver as dtrace doesn't want to see it ++ if( !method->is_static() ) { ++ in_sig_bt[i++] = T_OBJECT; ++ first_arg_to_pass = 1; ++ } ++ ++ SignatureStream ss(method->signature()); ++ for ( ; !ss.at_return_type(); ss.next()) { ++ BasicType bt = ss.type(); ++ in_sig_bt[i++] = bt; // Collect remaining bits of signature ++ out_sig_bt[total_c_args++] = bt; ++ if( bt == T_OBJECT) { ++ symbolOop s = ss.as_symbol_or_null(); ++ if (s == vmSymbols::java_lang_String()) { ++ total_strings++; ++ out_sig_bt[total_c_args-1] = T_ADDRESS; ++ } else if (s == vmSymbols::java_lang_Boolean() || ++ s == vmSymbols::java_lang_Byte()) { ++ out_sig_bt[total_c_args-1] = T_BYTE; ++ } else if (s == vmSymbols::java_lang_Character() || ++ s == vmSymbols::java_lang_Short()) { ++ out_sig_bt[total_c_args-1] = T_SHORT; ++ } else if (s == vmSymbols::java_lang_Integer() || ++ s == vmSymbols::java_lang_Float()) { ++ out_sig_bt[total_c_args-1] = T_INT; ++ } else if (s == vmSymbols::java_lang_Long() || ++ s == vmSymbols::java_lang_Double()) { ++ out_sig_bt[total_c_args-1] = T_LONG; ++ out_sig_bt[total_c_args++] = T_VOID; ++ } ++ } else if ( bt == T_LONG || bt == T_DOUBLE ) { ++ in_sig_bt[i++] = T_VOID; // Longs & doubles take 2 Java slots ++ // We convert double to long ++ out_sig_bt[total_c_args-1] = T_LONG; ++ out_sig_bt[total_c_args++] = T_VOID; ++ } else if ( bt == T_FLOAT) { ++ // We convert float to int ++ out_sig_bt[total_c_args-1] = T_INT; ++ } ++ } ++ ++ assert(i==total_args_passed, "validly parsed signature"); ++ ++ // Now get the compiled-Java layout as input arguments ++ int comp_args_on_stack; ++ comp_args_on_stack = SharedRuntime::java_calling_convention( ++ in_sig_bt, in_regs, total_args_passed, false); ++ ++ // We have received a description of where all the java arg are located ++ // on entry to the wrapper. We need to convert these args to where ++ // the a native (non-jni) function would expect them. To figure out ++ // where they go we convert the java signature to a C signature and remove ++ // T_VOID for any long/double we might have received. ++ ++ ++ // Now figure out where the args must be stored and how much stack space ++ // they require (neglecting out_preserve_stack_slots but space for storing ++ // the 1st six register arguments). It's weird see int_stk_helper. ++ ++ int out_arg_slots; ++ out_arg_slots = c_calling_convention(out_sig_bt, out_regs, NULL, total_c_args); ++ ++ // Calculate the total number of stack slots we will need. ++ ++ // First count the abi requirement plus all of the outgoing args ++ int stack_slots = SharedRuntime::out_preserve_stack_slots() + out_arg_slots; ++ ++ // Plus a temp for possible converion of float/double/long register args ++ ++ int conversion_temp = stack_slots; ++ stack_slots += 2; ++ ++ ++ // Now space for the string(s) we must convert ++ ++ int string_locs = stack_slots; ++ stack_slots += total_strings * ++ (max_dtrace_string_size / VMRegImpl::stack_slot_size); ++ ++ // Ok The space we have allocated will look like: ++ // ++ // ++ // FP-> | | ++ // |---------------------| ++ // | string[n] | ++ // |---------------------| <- string_locs[n] ++ // | string[n-1] | ++ // |---------------------| <- string_locs[n-1] ++ // | ... | ++ // | ... | ++ // |---------------------| <- string_locs[1] ++ // | string[0] | ++ // |---------------------| <- string_locs[0] ++ // | temp | ++ // |---------------------| <- conversion_temp ++ // | outbound memory | ++ // | based arguments | ++ // | | ++ // |---------------------| ++ // | | ++ // SP-> | out_preserved_slots | ++ // ++ // ++ ++ // Now compute actual number of stack words we need rounding to make ++ // stack properly aligned. ++ stack_slots = round_to(stack_slots, 4 * VMRegImpl::slots_per_word); ++ ++ int stack_size = stack_slots * VMRegImpl::stack_slot_size; ++ ++ intptr_t start = (intptr_t)__ pc(); ++ ++ // First thing make an ic check to see if we should even be here ++ ++ { ++ Label L; ++ const Register temp_reg = G3_scratch; ++ Address ic_miss(temp_reg, SharedRuntime::get_ic_miss_stub()); ++ __ verify_oop(O0); ++ __ ld_ptr(O0, oopDesc::klass_offset_in_bytes(), temp_reg); ++ __ cmp(temp_reg, G5_inline_cache_reg); ++ __ brx(Assembler::equal, true, Assembler::pt, L); ++ __ delayed()->nop(); ++ ++ __ jump_to(ic_miss, 0); ++ __ delayed()->nop(); ++ __ align(CodeEntryAlignment); ++ __ bind(L); ++ } ++ ++ int vep_offset = ((intptr_t)__ pc()) - start; ++ ++ // Make enough room for patch_verified_entry ++ __ nop(); ++ __ nop(); ++ ++ // Generate stack overflow check before creating frame ++ __ generate_stack_overflow_check(stack_size); ++ ++ // Generate a new frame for the wrapper. ++ __ save(SP, -stack_size, SP); ++ ++ // Frame is now completed as far a size and linkage. ++ ++ int frame_complete = ((intptr_t)__ pc()) - start; ++ ++#ifdef ASSERT ++ bool reg_destroyed[RegisterImpl::number_of_registers]; ++ bool freg_destroyed[FloatRegisterImpl::number_of_registers]; ++ for ( int r = 0 ; r < RegisterImpl::number_of_registers ; r++ ) { ++ reg_destroyed[r] = false; ++ } ++ for ( int f = 0 ; f < FloatRegisterImpl::number_of_registers ; f++ ) { ++ freg_destroyed[f] = false; ++ } ++ ++#endif /* ASSERT */ ++ ++ VMRegPair zero; ++ const Register g0 = G0; // without this we get a compiler warning (why??) ++ zero.set2(g0->as_VMReg()); ++ ++ int c_arg, j_arg; ++ ++ Register conversion_off = noreg; ++ ++ for (j_arg = first_arg_to_pass, c_arg = 0 ; ++ j_arg < total_args_passed ; j_arg++, c_arg++ ) { ++ ++ VMRegPair src = in_regs[j_arg]; ++ VMRegPair dst = out_regs[c_arg]; ++ ++#ifdef ASSERT ++ if (src.first()->is_Register()) { ++ assert(!reg_destroyed[src.first()->as_Register()->encoding()], "ack!"); ++ } else if (src.first()->is_FloatRegister()) { ++ assert(!freg_destroyed[src.first()->as_FloatRegister()->encoding( ++ FloatRegisterImpl::S)], "ack!"); ++ } ++ if (dst.first()->is_Register()) { ++ reg_destroyed[dst.first()->as_Register()->encoding()] = true; ++ } else if (dst.first()->is_FloatRegister()) { ++ freg_destroyed[dst.first()->as_FloatRegister()->encoding( ++ FloatRegisterImpl::S)] = true; ++ } ++#endif /* ASSERT */ ++ ++ switch (in_sig_bt[j_arg]) { ++ case T_ARRAY: ++ case T_OBJECT: ++ { ++ if (out_sig_bt[c_arg] == T_BYTE || out_sig_bt[c_arg] == T_SHORT || ++ out_sig_bt[c_arg] == T_INT || out_sig_bt[c_arg] == T_LONG) { ++ // need to unbox a one-slot value ++ Register in_reg = L0; ++ Register tmp = L2; ++ if ( src.first()->is_reg() ) { ++ in_reg = src.first()->as_Register(); ++ } else { ++ assert(Assembler::is_simm13(reg2offset(src.first()) + STACK_BIAS), ++ "must be"); ++ __ ld_ptr(FP, reg2offset(src.first()) + STACK_BIAS, in_reg); ++ } ++ // If the final destination is an acceptable register ++ if ( dst.first()->is_reg() ) { ++ if ( dst.is_single_phys_reg() || out_sig_bt[c_arg] != T_LONG ) { ++ tmp = dst.first()->as_Register(); ++ } ++ } ++ ++ Label skipUnbox; ++ if ( wordSize == 4 && out_sig_bt[c_arg] == T_LONG ) { ++ __ mov(G0, tmp->successor()); ++ } ++ __ br_null(in_reg, true, Assembler::pn, skipUnbox); ++ __ delayed()->mov(G0, tmp); ++ ++ BasicType bt = out_sig_bt[c_arg]; ++ int box_offset = java_lang_boxing_object::value_offset_in_bytes(bt); ++ switch (bt) { ++ case T_BYTE: ++ __ ldub(in_reg, box_offset, tmp); break; ++ case T_SHORT: ++ __ lduh(in_reg, box_offset, tmp); break; ++ case T_INT: ++ __ ld(in_reg, box_offset, tmp); break; ++ case T_LONG: ++ __ ld_long(in_reg, box_offset, tmp); break; ++ default: ShouldNotReachHere(); ++ } ++ ++ __ bind(skipUnbox); ++ // If tmp wasn't final destination copy to final destination ++ if (tmp == L2) { ++ VMRegPair tmp_as_VM = reg64_to_VMRegPair(L2); ++ if (out_sig_bt[c_arg] == T_LONG) { ++ long_move(masm, tmp_as_VM, dst); ++ } else { ++ move32_64(masm, tmp_as_VM, out_regs[c_arg]); ++ } ++ } ++ if (out_sig_bt[c_arg] == T_LONG) { ++ assert(out_sig_bt[c_arg+1] == T_VOID, "must be"); ++ ++c_arg; // move over the T_VOID to keep the loop indices in sync ++ } ++ } else if (out_sig_bt[c_arg] == T_ADDRESS) { ++ Register s = ++ src.first()->is_reg() ? src.first()->as_Register() : L2; ++ Register d = ++ dst.first()->is_reg() ? dst.first()->as_Register() : L2; ++ ++ // We store the oop now so that the conversion pass can reach ++ // while in the inner frame. This will be the only store if ++ // the oop is NULL. ++ if (s != L2) { ++ // src is register ++ if (d != L2) { ++ // dst is register ++ __ mov(s, d); ++ } else { ++ assert(Assembler::is_simm13(reg2offset(dst.first()) + ++ STACK_BIAS), "must be"); ++ __ st_ptr(s, SP, reg2offset(dst.first()) + STACK_BIAS); ++ } ++ } else { ++ // src not a register ++ assert(Assembler::is_simm13(reg2offset(src.first()) + ++ STACK_BIAS), "must be"); ++ __ ld_ptr(FP, reg2offset(src.first()) + STACK_BIAS, d); ++ if (d == L2) { ++ assert(Assembler::is_simm13(reg2offset(dst.first()) + ++ STACK_BIAS), "must be"); ++ __ st_ptr(d, SP, reg2offset(dst.first()) + STACK_BIAS); ++ } ++ } ++ } else if (out_sig_bt[c_arg] != T_VOID) { ++ // Convert the arg to NULL ++ if (dst.first()->is_reg()) { ++ __ mov(G0, dst.first()->as_Register()); ++ } else { ++ assert(Assembler::is_simm13(reg2offset(dst.first()) + ++ STACK_BIAS), "must be"); ++ __ st_ptr(G0, SP, reg2offset(dst.first()) + STACK_BIAS); ++ } ++ } ++ } ++ break; ++ case T_VOID: ++ break; ++ ++ case T_FLOAT: ++ if (src.first()->is_stack()) { ++ // Stack to stack/reg is simple ++ move32_64(masm, src, dst); ++ } else { ++ if (dst.first()->is_reg()) { ++ // freg -> reg ++ int off = ++ STACK_BIAS + conversion_temp * VMRegImpl::stack_slot_size; ++ Register d = dst.first()->as_Register(); ++ if (Assembler::is_simm13(off)) { ++ __ stf(FloatRegisterImpl::S, src.first()->as_FloatRegister(), ++ SP, off); ++ __ ld(SP, off, d); ++ } else { ++ if (conversion_off == noreg) { ++ __ set(off, L6); ++ conversion_off = L6; ++ } ++ __ stf(FloatRegisterImpl::S, src.first()->as_FloatRegister(), ++ SP, conversion_off); ++ __ ld(SP, conversion_off , d); ++ } ++ } else { ++ // freg -> mem ++ int off = STACK_BIAS + reg2offset(dst.first()); ++ if (Assembler::is_simm13(off)) { ++ __ stf(FloatRegisterImpl::S, src.first()->as_FloatRegister(), ++ SP, off); ++ } else { ++ if (conversion_off == noreg) { ++ __ set(off, L6); ++ conversion_off = L6; ++ } ++ __ stf(FloatRegisterImpl::S, src.first()->as_FloatRegister(), ++ SP, conversion_off); ++ } ++ } ++ } ++ break; ++ ++ case T_DOUBLE: ++ assert( j_arg + 1 < total_args_passed && ++ in_sig_bt[j_arg + 1] == T_VOID && ++ out_sig_bt[c_arg+1] == T_VOID, "bad arg list"); ++ if (src.first()->is_stack()) { ++ // Stack to stack/reg is simple ++ long_move(masm, src, dst); ++ } else { ++ Register d = dst.first()->is_reg() ? dst.first()->as_Register() : L2; ++ ++ // Destination could be an odd reg on 32bit in which case ++ // we can't load direct to the destination. ++ ++ if (!d->is_even() && wordSize == 4) { ++ d = L2; ++ } ++ int off = STACK_BIAS + conversion_temp * VMRegImpl::stack_slot_size; ++ if (Assembler::is_simm13(off)) { ++ __ stf(FloatRegisterImpl::D, src.first()->as_FloatRegister(), ++ SP, off); ++ __ ld_long(SP, off, d); ++ } else { ++ if (conversion_off == noreg) { ++ __ set(off, L6); ++ conversion_off = L6; ++ } ++ __ stf(FloatRegisterImpl::D, src.first()->as_FloatRegister(), ++ SP, conversion_off); ++ __ ld_long(SP, conversion_off, d); ++ } ++ if (d == L2) { ++ long_move(masm, reg64_to_VMRegPair(L2), dst); ++ } ++ } ++ break; ++ ++ case T_LONG : ++ // 32bit can't do a split move of something like g1 -> O0, O1 ++ // so use a memory temp ++ if (src.is_single_phys_reg() && wordSize == 4) { ++ Register tmp = L2; ++ if (dst.first()->is_reg() && ++ (wordSize == 8 || dst.first()->as_Register()->is_even())) { ++ tmp = dst.first()->as_Register(); ++ } ++ ++ int off = STACK_BIAS + conversion_temp * VMRegImpl::stack_slot_size; ++ if (Assembler::is_simm13(off)) { ++ __ stx(src.first()->as_Register(), SP, off); ++ __ ld_long(SP, off, tmp); ++ } else { ++ if (conversion_off == noreg) { ++ __ set(off, L6); ++ conversion_off = L6; ++ } ++ __ stx(src.first()->as_Register(), SP, conversion_off); ++ __ ld_long(SP, conversion_off, tmp); ++ } ++ ++ if (tmp == L2) { ++ long_move(masm, reg64_to_VMRegPair(L2), dst); ++ } ++ } else { ++ long_move(masm, src, dst); ++ } ++ break; ++ ++ case T_ADDRESS: assert(false, "found T_ADDRESS in java args"); ++ ++ default: ++ move32_64(masm, src, dst); ++ } ++ } ++ ++ ++ // If we have any strings we must store any register based arg to the stack ++ // This includes any still live xmm registers too. ++ ++ if (total_strings > 0 ) { ++ ++ // protect all the arg registers ++ __ save_frame(0); ++ __ mov(G2_thread, L7_thread_cache); ++ const Register L2_string_off = L2; ++ ++ // Get first string offset ++ __ set(string_locs * VMRegImpl::stack_slot_size, L2_string_off); ++ ++ for (c_arg = 0 ; c_arg < total_c_args ; c_arg++ ) { ++ if (out_sig_bt[c_arg] == T_ADDRESS) { ++ ++ VMRegPair dst = out_regs[c_arg]; ++ const Register d = dst.first()->is_reg() ? ++ dst.first()->as_Register()->after_save() : noreg; ++ ++ // It's a string the oop and it was already copied to the out arg ++ // position ++ if (d != noreg) { ++ __ mov(d, O0); ++ } else { ++ assert(Assembler::is_simm13(reg2offset(dst.first()) + STACK_BIAS), ++ "must be"); ++ __ ld_ptr(FP, reg2offset(dst.first()) + STACK_BIAS, O0); ++ } ++ Label skip; ++ ++ __ br_null(O0, false, Assembler::pn, skip); ++ __ delayed()->addu(FP, L2_string_off, O1); ++ ++ if (d != noreg) { ++ __ mov(O1, d); ++ } else { ++ assert(Assembler::is_simm13(reg2offset(dst.first()) + STACK_BIAS), ++ "must be"); ++ __ st_ptr(O1, FP, reg2offset(dst.first()) + STACK_BIAS); ++ } ++ ++ __ call(CAST_FROM_FN_PTR(address, SharedRuntime::get_utf), ++ relocInfo::runtime_call_type); ++ __ delayed()->addu(L2_string_off, max_dtrace_string_size, L2_string_off); ++ ++ __ bind(skip); ++ ++ } ++ ++ } ++ __ mov(L7_thread_cache, G2_thread); ++ __ restore(); ++ ++ } ++ ++ ++ // Ok now we are done. Need to place the nop that dtrace wants in order to ++ // patch in the trap ++ ++ int patch_offset = ((intptr_t)__ pc()) - start; ++ ++ __ nop(); ++ ++ ++ // Return ++ ++ __ ret(); ++ __ delayed()->restore(); ++ ++ __ flush(); ++ ++ nmethod *nm = nmethod::new_dtrace_nmethod( ++ method, masm->code(), vep_offset, patch_offset, frame_complete, ++ stack_slots / VMRegImpl::slots_per_word); ++ return nm; ++ ++} ++ ++#endif // HAVE_DTRACE_H ++ ++// this function returns the adjust size (in number of words) to a c2i adapter ++// activation for use during deoptimization ++int Deoptimization::last_frame_adjust(int callee_parameters, int callee_locals) { ++ return (callee_locals - callee_parameters) * Interpreter::stackElementWords; ++} ++ ++// "Top of Stack" slots that may be unused by the calling convention but must ++// otherwise be preserved. ++// On Intel these are not necessary and the value can be zero. ++// On Sparc this describes the words reserved for storing a register window ++// when an interrupt occurs. ++uint SharedRuntime::out_preserve_stack_slots() { ++ return 0; ++} ++ ++//------------------------------generate_deopt_blob---------------------------- ++// Ought to generate an ideal graph & compile, but here's some SPARC ASM ++// instead. ++void SharedRuntime::generate_deopt_blob() { ++ // allocate space for the code ++ ResourceMark rm; ++ // setup code generation tools ++ //CodeBuffer buffer ("deopt_blob", 4000, 2048); ++ CodeBuffer buffer ("deopt_blob", 8000, 2048); ++ MacroAssembler* masm = new MacroAssembler( & buffer); ++ int frame_size_in_words; ++ OopMap* map = NULL; ++ // Account for the extra args we place on the stack ++ // by the time we call fetch_unroll_info ++ const int additional_words = 2; // deopt kind, thread ++ ++ OopMapSet *oop_maps = new OopMapSet(); ++ ++ address start = __ pc(); ++ Label cont; ++ // we use S3 for DeOpt reason register ++ Register reason = S3; ++ // use S6 for thread register ++ Register thread = TREG; ++ // use S7 for fetch_unroll_info returned UnrollBlock ++ Register unroll = S7; ++ // Prolog for non exception case! ++ // Correct the return address we were given. ++ //FIXME, return address is on the tos or Ra? ++ __ addiu(RA, RA, - (NativeCall::return_address_offset_long)); ++ // Save everything in sight. ++ map = RegisterSaver::save_live_registers(masm, additional_words, &frame_size_in_words); ++ // Normal deoptimization ++ __ move(reason, Deoptimization::Unpack_deopt); ++ __ b(cont); ++ __ delayed()->nop(); ++ ++ int reexecute_offset = __ pc() - start; ++ ++ // Reexecute case ++ // return address is the pc describes what bci to do re-execute at ++ ++ // No need to update map as each call to save_live_registers will produce identical oopmap ++ (void) RegisterSaver::save_live_registers(masm, additional_words, &frame_size_in_words); ++ __ move(reason, Deoptimization::Unpack_reexecute); ++ __ b(cont); ++ __ delayed()->nop(); ++ ++ int exception_offset = __ pc() - start; ++ // Prolog for exception case ++ ++ // all registers are dead at this entry point, except for V0 and ++ // V1 which contain the exception oop and exception pc ++ // respectively. Set them in TLS and fall thru to the ++ // unpack_with_exception_in_tls entry point. ++ ++ __ get_thread(thread); ++ __ st_ptr(V1, thread, in_bytes(JavaThread::exception_pc_offset())); ++ __ st_ptr(V0, thread, in_bytes(JavaThread::exception_oop_offset())); ++ int exception_in_tls_offset = __ pc() - start; ++ // new implementation because exception oop is now passed in JavaThread ++ ++ // Prolog for exception case ++ // All registers must be preserved because they might be used by LinearScan ++ // Exceptiop oop and throwing PC are passed in JavaThread ++ // tos: stack at point of call to method that threw the exception (i.e. only ++ // args are on the stack, no return address) ++ ++ // Return address will be patched later with the throwing pc. The correct value is not ++ // available now because loading it from memory would destroy registers. ++ // Save everything in sight. ++ // No need to update map as each call to save_live_registers will produce identical oopmap ++ __ addiu(RA, RA, - (NativeCall::return_address_offset_long)); ++ (void) RegisterSaver::save_live_registers(masm, additional_words, &frame_size_in_words); ++ ++ // Now it is safe to overwrite any register ++ // store the correct deoptimization type ++ __ move(reason, Deoptimization::Unpack_exception); ++ // load throwing pc from JavaThread and patch it as the return address ++ // of the current frame. Then clear the field in JavaThread ++ __ get_thread(thread); ++ __ ld_ptr(V1, thread, in_bytes(JavaThread::exception_pc_offset())); ++ __ st_ptr(V1, SP, RegisterSaver::raOffset() * wordSize); //save ra ++ __ st_ptr(R0, thread, in_bytes(JavaThread::exception_pc_offset())); ++ ++ ++#ifdef ASSERT ++ // verify that there is really an exception oop in JavaThread ++ __ ld_ptr(AT, thread, in_bytes(JavaThread::exception_oop_offset())); ++ __ verify_oop(AT); ++ // verify that there is no pending exception ++ Label no_pending_exception; ++ __ ld_ptr(AT, thread, in_bytes(Thread::pending_exception_offset())); ++ __ beq(AT, R0, no_pending_exception); ++ __ delayed()->nop(); ++ __ stop("must not have pending exception here"); ++ __ bind(no_pending_exception); ++#endif ++ __ bind(cont); ++ // Compiled code leaves the floating point stack dirty, empty it. ++ __ empty_FPU_stack(); ++ ++ ++ // Call C code. Need thread and this frame, but NOT official VM entry ++ // crud. We cannot block on this call, no GC can happen. ++#ifndef OPT_THREAD ++ __ get_thread(thread); ++#endif ++ ++ __ move(A0, thread); ++ __ addiu(SP, SP, -additional_words * wordSize); ++ ++ __ set_last_Java_frame(NOREG, NOREG, NULL); ++ ++ // Call fetch_unroll_info(). Need thread and this frame, but NOT official VM entry - cannot block on ++ // this call, no GC can happen. Call should capture return values. ++ ++ __ relocate(relocInfo::internal_pc_type); ++ { ++ intptr_t save_pc = (intptr_t)__ pc() + NativeMovConstReg::instruction_size + 28; ++ __ patchable_set48(AT, save_pc); ++ } ++ __ sd(AT, thread, in_bytes(JavaThread::frame_anchor_offset() + JavaFrameAnchor::last_Java_pc_offset())); ++ ++ __ call((address)Deoptimization::fetch_unroll_info); ++ //__ call(CAST_FROM_FN_PTR(address, Deoptimization::fetch_unroll_info), relocInfo::runtime_call_type); ++ __ delayed()->nop(); ++ oop_maps->add_gc_map(__ pc() - start, map); ++ __ addiu(SP, SP, additional_words * wordSize); ++ __ get_thread(thread); ++ __ reset_last_Java_frame(false); ++ ++ // Load UnrollBlock into S7 ++ __ move(unroll, V0); ++ ++ ++ // Move the unpack kind to a safe place in the UnrollBlock because ++ // we are very short of registers ++ ++ Address unpack_kind(unroll, Deoptimization::UnrollBlock::unpack_kind_offset_in_bytes()); ++ __ sw(reason, unpack_kind); ++ // save the unpack_kind value ++ // Retrieve the possible live values (return values) ++ // All callee save registers representing jvm state ++ // are now in the vframeArray. ++ ++ Label noException; ++ __ move(AT, Deoptimization::Unpack_exception); ++ __ bne(AT, reason, noException);// Was exception pending? ++ __ delayed()->nop(); ++ __ ld_ptr(V0, thread, in_bytes(JavaThread::exception_oop_offset())); ++ __ ld_ptr(V1, thread, in_bytes(JavaThread::exception_pc_offset())); ++ __ st_ptr(R0, thread, in_bytes(JavaThread::exception_pc_offset())); ++ __ st_ptr(R0, thread, in_bytes(JavaThread::exception_oop_offset())); ++ ++ __ verify_oop(V0); ++ ++ // Overwrite the result registers with the exception results. ++ __ st_ptr(V0, SP, RegisterSaver::v0Offset()*wordSize); ++ __ st_ptr(V1, SP, RegisterSaver::v1Offset()*wordSize); ++ ++ __ bind(noException); ++ ++ ++ // Stack is back to only having register save data on the stack. ++ // Now restore the result registers. Everything else is either dead or captured ++ // in the vframeArray. ++ ++ RegisterSaver::restore_result_registers(masm); ++ // All of the register save area has been popped of the stack. Only the ++ // return address remains. ++ // Pop all the frames we must move/replace. ++ // Frame picture (youngest to oldest) ++ // 1: self-frame (no frame link) ++ // 2: deopting frame (no frame link) ++ // 3: caller of deopting frame (could be compiled/interpreted). ++ // ++ // Note: by leaving the return address of self-frame on the stack ++ // and using the size of frame 2 to adjust the stack ++ // when we are done the return to frame 3 will still be on the stack. ++ ++ // register for the sender's sp ++ Register sender_sp = Rsender; ++ // register for frame pcs ++ Register pcs = T0; ++ // register for frame sizes ++ Register sizes = T1; ++ // register for frame count ++ Register count = T3; ++ ++ // Pop deoptimized frame ++ __ lw(AT, unroll, Deoptimization::UnrollBlock::size_of_deoptimized_frame_offset_in_bytes()); ++ __ addu(SP, SP, AT); ++ // sp should be pointing at the return address to the caller (3) ++ ++ // Load array of frame pcs into pcs ++ __ ld_ptr(pcs, unroll, Deoptimization::UnrollBlock::frame_pcs_offset_in_bytes()); ++ __ addiu(SP, SP, wordSize); // trash the old pc ++ // Load array of frame sizes into T6 ++ __ ld_ptr(sizes, unroll, Deoptimization::UnrollBlock::frame_sizes_offset_in_bytes()); ++ ++ ++ ++ // Load count of frams into T3 ++ __ lw(count, unroll, Deoptimization::UnrollBlock::number_of_frames_offset_in_bytes()); ++ // Pick up the initial fp we should save ++ __ ld(FP, unroll, Deoptimization::UnrollBlock::initial_info_offset_in_bytes()); ++ // Now adjust the caller's stack to make up for the extra locals ++ // but record the original sp so that we can save it in the skeletal interpreter ++ // frame and the stack walking of interpreter_sender will get the unextended sp ++ // value and not the "real" sp value. ++ __ move(sender_sp, SP); ++ __ lw(AT, unroll, Deoptimization::UnrollBlock::caller_adjustment_offset_in_bytes()); ++ __ subu(SP, SP, AT); ++ ++ // Push interpreter frames in a loop ++ // ++ //Loop: ++ // 0x000000555bd82d18: lw t2, 0x0(t1) ; lw sizes[i] <--- error lw->ld ++ // 0x000000555bd82d1c: ld at, 0x0(t0) ; ld pcs[i] ++ // 0x000000555bd82d20: daddiu t2, t2, 0xfffffff0 ; t2 -= 16 ++ // 0x000000555bd82d24: daddiu sp, sp, 0xfffffff0 ++ // 0x000000555bd82d28: sd fp, 0x0(sp) ; push fp ++ // 0x000000555bd82d2c: sd at, 0x8(sp) ; push at ++ // 0x000000555bd82d30: daddu fp, sp, zero ; fp <- sp ++ // 0x000000555bd82d34: dsubu sp, sp, t2 ; sp -= t2 ++ // 0x000000555bd82d38: sd zero, 0xfffffff0(fp) ; __ sd(R0, FP, frame::interpreter_frame_last_sp_offset * wordSize); ++ // 0x000000555bd82d3c: sd s4, 0xfffffff8(fp) ; __ sd(sender_sp, FP, frame::interpreter_frame_sender_sp_offset * wordSize); ++ // 0x000000555bd82d40: daddu s4, sp, zero ; move(sender_sp, SP); ++ // 0x000000555bd82d44: daddiu t3, t3, 0xffffffff ; count -- ++ // 0x000000555bd82d48: daddiu t1, t1, 0x4 ; sizes += 4 ++ // 0x000000555bd82d4c: bne t3, zero, 0x000000555bd82d18 ++ // 0x000000555bd82d50: daddiu t0, t0, 0x4 ; <--- error t0 += 8 ++ // ++ // pcs[0] = frame_pcs[0] = deopt_sender.raw_pc(); regex.split ++ Label loop; ++ __ bind(loop); ++ __ ld(T2, sizes, 0); // Load frame size ++ __ ld_ptr(AT, pcs, 0); // save return address ++ __ addiu(T2, T2, -2*wordSize); // we'll push pc and fp, by hand ++ __ push2(AT, FP); ++ __ move(FP, SP); ++ __ subu(SP, SP, T2); // Prolog! ++ // This value is corrected by layout_activation_impl ++ __ sd(R0, FP, frame::interpreter_frame_last_sp_offset * wordSize); ++ __ sd(sender_sp, FP, frame::interpreter_frame_sender_sp_offset * wordSize);// Make it walkable ++ __ move(sender_sp, SP); // pass to next frame ++ __ addiu(count, count, -1); // decrement counter ++ __ addiu(sizes, sizes, wordSize); // Bump array pointer (sizes) ++ __ bne(count, R0, loop); ++ __ delayed()->addiu(pcs, pcs, wordSize); // Bump array pointer (pcs) ++ __ ld(AT, pcs, 0); // frame_pcs[number_of_frames] = Interpreter::deopt_entry(vtos, 0); ++ // Re-push self-frame ++ __ push2(AT, FP); ++ __ move(FP, SP); ++ __ sd(R0, FP, frame::interpreter_frame_last_sp_offset * wordSize); ++ __ sd(sender_sp, FP, frame::interpreter_frame_sender_sp_offset * wordSize); ++ __ addiu(SP, SP, -(frame_size_in_words - 2 - additional_words) * wordSize); ++ ++ // Restore frame locals after moving the frame ++ __ sd(V0, SP, RegisterSaver::v0Offset() * wordSize); ++ __ sd(V1, SP, RegisterSaver::v1Offset() * wordSize); ++ __ sdc1(F0, SP, RegisterSaver::fpResultOffset()* wordSize);// Pop float stack and store in local ++ __ sdc1(F1, SP, (RegisterSaver::fpResultOffset() + 1) * wordSize); ++ ++ ++ // Call unpack_frames(). Need thread and this frame, but NOT official VM entry - cannot block on ++ // this call, no GC can happen. ++ __ move(A1, reason); // exec_mode ++ __ get_thread(thread); ++ __ move(A0, thread); // thread ++ __ addiu(SP, SP, (-additional_words) *wordSize); ++ ++ // set last_Java_sp, last_Java_fp ++ __ set_last_Java_frame(NOREG, FP, NULL); ++ ++ __ move(AT, -(StackAlignmentInBytes)); ++ __ andr(SP, SP, AT); // Fix stack alignment as required by ABI ++ ++ __ relocate(relocInfo::internal_pc_type); ++ { ++ intptr_t save_pc = (intptr_t)__ pc() + NativeMovConstReg::instruction_size + 28; ++ __ patchable_set48(AT, save_pc); ++ } ++ __ sd(AT, thread, in_bytes(JavaThread::frame_anchor_offset() + JavaFrameAnchor::last_Java_pc_offset())); ++ ++ __ call(CAST_FROM_FN_PTR(address, Deoptimization::unpack_frames), relocInfo::runtime_call_type); ++ __ delayed()->nop(); ++ // Revert SP alignment after call since we're going to do some SP relative addressing below ++ __ ld(SP, thread, in_bytes(JavaThread::last_Java_sp_offset())); ++ // Set an oopmap for the call site ++ oop_maps->add_gc_map(__ offset(), new OopMap( frame_size_in_words , 0)); ++ ++ __ push(V0); ++ ++ __ get_thread(thread); ++ __ reset_last_Java_frame(true); ++ ++ // Collect return values ++ __ ld(V0, SP, (RegisterSaver::v0Offset() + additional_words + 1) * wordSize); ++ __ ld(V1, SP, (RegisterSaver::v1Offset() + additional_words + 1) * wordSize); ++ __ ldc1(F0, SP, (RegisterSaver::fpResultOffset() + additional_words + 1) * wordSize);// Pop float stack and store in local ++ __ ldc1(F1, SP, (RegisterSaver::fpResultOffset() + additional_words + 2) * wordSize); ++ //FIXME, ++ // Clear floating point stack before returning to interpreter ++ __ empty_FPU_stack(); ++ //FIXME, we should consider about float and double ++ // Push a float or double return value if necessary. ++ __ leave(); ++ ++ // Jump to interpreter ++ __ jr(RA); ++ __ delayed()->nop(); ++ ++ masm->flush(); ++ _deopt_blob = DeoptimizationBlob::create(&buffer, oop_maps, 0, exception_offset, reexecute_offset, frame_size_in_words); ++ _deopt_blob->set_unpack_with_exception_in_tls_offset(exception_in_tls_offset); ++} ++ ++#ifdef COMPILER2 ++ ++//------------------------------generate_uncommon_trap_blob-------------------- ++// Ought to generate an ideal graph & compile, but here's some SPARC ASM ++// instead. ++void SharedRuntime::generate_uncommon_trap_blob() { ++ // allocate space for the code ++ ResourceMark rm; ++ // setup code generation tools ++ CodeBuffer buffer ("uncommon_trap_blob", 512*80 , 512*40 ); ++ MacroAssembler* masm = new MacroAssembler(&buffer); ++ ++ enum frame_layout { ++ fp_off, fp_off2, ++ return_off, return_off2, ++ framesize ++ }; ++ assert(framesize % 4 == 0, "sp not 16-byte aligned"); ++ ++ address start = __ pc(); ++ ++ // Push self-frame. ++ __ daddiu(SP, SP, -framesize * BytesPerInt); ++ ++ __ sd(RA, SP, return_off * BytesPerInt); ++ __ sd(FP, SP, fp_off * BytesPerInt); ++ ++ __ daddiu(FP, SP, fp_off * BytesPerInt); ++ ++ // Clear the floating point exception stack ++ __ empty_FPU_stack(); ++ ++ Register thread = TREG; ++ ++#ifndef OPT_THREAD ++ __ get_thread(thread); ++#endif ++ // set last_Java_sp ++ __ set_last_Java_frame(NOREG, FP, NULL); ++ __ relocate(relocInfo::internal_pc_type); ++ { ++ long save_pc = (long)__ pc() + 52; ++ __ patchable_set48(AT, (long)save_pc); ++ __ sd(AT, thread, in_bytes(JavaThread::frame_anchor_offset() + JavaFrameAnchor::last_Java_pc_offset())); ++ } ++ // Call C code. Need thread but NOT official VM entry ++ // crud. We cannot block on this call, no GC can happen. Call should ++ // capture callee-saved registers as well as return values. ++ __ move(A0, thread); ++ // argument already in T0 ++ __ move(A1, T0); ++ __ patchable_call((address)Deoptimization::uncommon_trap); ++ ++ // Set an oopmap for the call site ++ OopMapSet *oop_maps = new OopMapSet(); ++ OopMap* map = new OopMap( framesize, 0 ); ++ ++ //oop_maps->add_gc_map( __ offset(), true, map); ++ oop_maps->add_gc_map( __ offset(), map); ++ ++#ifndef OPT_THREAD ++ __ get_thread(thread); ++#endif ++ __ reset_last_Java_frame(false); ++ ++ // Load UnrollBlock into S7 ++ Register unroll = S7; ++ __ move(unroll, V0); ++ ++ // Pop all the frames we must move/replace. ++ // ++ // Frame picture (youngest to oldest) ++ // 1: self-frame (no frame link) ++ // 2: deopting frame (no frame link) ++ // 3: possible-i2c-adapter-frame ++ // 4: caller of deopting frame (could be compiled/interpreted. If interpreted we will create an ++ // and c2i here) ++ ++ __ daddiu(SP, SP, framesize * BytesPerInt); ++ ++ // Pop deoptimized frame ++ __ lw(AT, unroll, Deoptimization::UnrollBlock::size_of_deoptimized_frame_offset_in_bytes()); ++ __ daddu(SP, SP, AT); ++ ++ // register for frame pcs ++ Register pcs = T8; ++ // register for frame sizes ++ Register sizes = T9; ++ // register for frame count ++ Register count = T3; ++ // register for the sender's sp ++ Register sender_sp = T1; ++ ++ // sp should be pointing at the return address to the caller (4) ++ // Load array of frame pcs ++ __ ld(pcs, unroll, Deoptimization::UnrollBlock::frame_pcs_offset_in_bytes()); ++ ++ // Load array of frame sizes ++ __ ld(sizes, unroll, Deoptimization::UnrollBlock::frame_sizes_offset_in_bytes()); ++ __ lwu(count, unroll, Deoptimization::UnrollBlock::number_of_frames_offset_in_bytes()); ++ ++ // Pick up the initial fp we should save ++ __ ld(FP, unroll, Deoptimization::UnrollBlock::initial_info_offset_in_bytes()); ++ // Now adjust the caller's stack to make up for the extra locals ++ // but record the original sp so that we can save it in the skeletal interpreter ++ // frame and the stack walking of interpreter_sender will get the unextended sp ++ // value and not the "real" sp value. ++ ++ __ move(sender_sp, SP); ++ __ lw(AT, unroll, Deoptimization::UnrollBlock::caller_adjustment_offset_in_bytes()); ++ __ dsubu(SP, SP, AT); ++ // Push interpreter frames in a loop ++ Label loop; ++ __ bind(loop); ++ __ ld(T2, sizes, 0); // Load frame size ++ __ ld(AT, pcs, 0); // save return address ++ __ daddiu(T2, T2, -2*wordSize); // we'll push pc and fp, by hand ++ __ push2(AT, FP); ++ __ move(FP, SP); ++ __ dsubu(SP, SP, T2); // Prolog! ++ // This value is corrected by layout_activation_impl ++ __ sd(R0, FP, frame::interpreter_frame_last_sp_offset * wordSize); ++ __ sd(sender_sp, FP, frame::interpreter_frame_sender_sp_offset * wordSize);// Make it walkable ++ __ move(sender_sp, SP); // pass to next frame ++ __ daddiu(count, count, -1); // decrement counter ++ __ daddiu(sizes, sizes, wordSize); // Bump array pointer (sizes) ++ __ addiu(pcs, pcs, wordSize); // Bump array pointer (pcs) ++ __ bne(count, R0, loop); ++ __ delayed()->nop(); // Bump array pointer (pcs) ++ ++ __ ld(RA, pcs, 0); ++ ++ // Re-push self-frame ++ // save old & set new FP ++ // save final return address ++ __ enter(); ++ ++ // Use FP because the frames look interpreted now ++ // Save "the_pc" since it cannot easily be retrieved using the last_java_SP after we aligned SP. ++ // Don't need the precise return PC here, just precise enough to point into this code blob. ++ address the_pc = __ pc(); ++ __ set_last_Java_frame(NOREG, FP, the_pc); ++ ++ __ move(AT, -(StackAlignmentInBytes)); ++ __ andr(SP, SP, AT); // Fix stack alignment as required by ABI ++ ++ // Call C code. Need thread but NOT official VM entry ++ // crud. We cannot block on this call, no GC can happen. Call should ++ // restore return values to their stack-slots with the new SP. ++ __ move(A0, thread); ++ __ move(A1, Deoptimization::Unpack_uncommon_trap); ++ __ patchable_call((address)Deoptimization::unpack_frames); ++ // Set an oopmap for the call site ++ oop_maps->add_gc_map( __ offset(), new OopMap( framesize, 0 ) ); ++ ++ __ reset_last_Java_frame(true); ++ ++ // Pop self-frame. ++ __ leave(); // Epilog! ++ ++ // Jump to interpreter ++ __ jr(RA); ++ __ delayed()->nop(); ++ // ------------- ++ // make sure all code is generated ++ masm->flush(); ++ ++ _uncommon_trap_blob = UncommonTrapBlob::create(&buffer, oop_maps, framesize / 2); ++} ++ ++#endif // COMPILER2 ++ ++//------------------------------generate_handler_blob------------------- ++// ++// Generate a special Compile2Runtime blob that saves all registers, and sets ++// up an OopMap and calls safepoint code to stop the compiled code for ++// a safepoint. ++// ++// This blob is jumped to (via a breakpoint and the signal handler) from a ++// safepoint in compiled code. ++ ++SafepointBlob* SharedRuntime::generate_handler_blob(address call_ptr, int pool_type) { ++ ++ // Account for thread arg in our frame ++ const int additional_words = 0; ++ int frame_size_in_words; ++ ++ assert (StubRoutines::forward_exception_entry() != NULL, "must be generated before"); ++ ++ ResourceMark rm; ++ OopMapSet *oop_maps = new OopMapSet(); ++ OopMap* map; ++ ++ // allocate space for the code ++ // setup code generation tools ++ CodeBuffer buffer ("handler_blob", 2048, 512); ++ MacroAssembler* masm = new MacroAssembler( &buffer); ++ ++ const Register thread = TREG; ++ address start = __ pc(); ++ address call_pc = NULL; ++ bool cause_return = (pool_type == POLL_AT_RETURN); ++ bool save_vectors = (pool_type == POLL_AT_VECTOR_LOOP); ++ ++ // If cause_return is true we are at a poll_return and there is ++ // the return address in RA to the caller on the nmethod ++ // that is safepoint. We can leave this return in RA and ++ // effectively complete the return and safepoint in the caller. ++ // Otherwise we load exception pc to RA. ++ __ push(thread); ++#ifndef OPT_THREAD ++ __ get_thread(thread); ++#endif ++ ++ if(!cause_return) { ++ __ ld_ptr(RA, Address(thread, JavaThread::saved_exception_pc_offset())); ++ } ++ ++ __ pop(thread); ++ map = RegisterSaver::save_live_registers(masm, additional_words, &frame_size_in_words, save_vectors); ++ ++#ifndef OPT_THREAD ++ __ get_thread(thread); ++#endif ++ ++ // The following is basically a call_VM. However, we need the precise ++ // address of the call in order to generate an oopmap. Hence, we do all the ++ // work outselvs. ++ ++ __ move(A0, thread); ++ __ set_last_Java_frame(NOREG, NOREG, NULL); ++ ++ ++ // Do the call ++ __ call(call_ptr); ++ __ delayed()->nop(); ++ ++ // Set an oopmap for the call site. This oopmap will map all ++ // oop-registers and debug-info registers as callee-saved. This ++ // will allow deoptimization at this safepoint to find all possible ++ // debug-info recordings, as well as let GC find all oops. ++ oop_maps->add_gc_map(__ offset(), map); ++ ++ Label noException; ++ ++ // Clear last_Java_sp again ++ __ reset_last_Java_frame(false); ++ ++ __ ld_ptr(AT, thread, in_bytes(Thread::pending_exception_offset())); ++ __ beq(AT, R0, noException); ++ __ delayed()->nop(); ++ ++ // Exception pending ++ ++ RegisterSaver::restore_live_registers(masm, save_vectors); ++ //forward_exception_entry need return address on the stack ++ __ push(RA); ++ __ patchable_jump((address)StubRoutines::forward_exception_entry()); ++ ++ // No exception case ++ __ bind(noException); ++ // Normal exit, register restoring and exit ++ RegisterSaver::restore_live_registers(masm, save_vectors); ++ __ jr(RA); ++ __ delayed()->nop(); ++ ++ masm->flush(); ++ ++ // Fill-out other meta info ++ return SafepointBlob::create(&buffer, oop_maps, frame_size_in_words); ++} ++ ++// ++// generate_resolve_blob - call resolution (static/virtual/opt-virtual/ic-miss ++// ++// Generate a stub that calls into vm to find out the proper destination ++// of a java call. All the argument registers are live at this point ++// but since this is generic code we don't know what they are and the caller ++// must do any gc of the args. ++// ++RuntimeStub* SharedRuntime::generate_resolve_blob(address destination, const char* name) { ++ assert (StubRoutines::forward_exception_entry() != NULL, "must be generated before"); ++ ++ // allocate space for the code ++ ResourceMark rm; ++ ++ //CodeBuffer buffer(name, 1000, 512); ++ CodeBuffer buffer(name, 2000, 2048); ++ MacroAssembler* masm = new MacroAssembler(&buffer); ++ ++ int frame_size_words; ++ //we put the thread in A0 ++ ++ OopMapSet *oop_maps = new OopMapSet(); ++ OopMap* map = NULL; ++ ++ int start = __ offset(); ++ map = RegisterSaver::save_live_registers(masm, 0, &frame_size_words); ++ ++ ++ int frame_complete = __ offset(); ++ ++ const Register thread = T8; ++ __ get_thread(thread); ++ ++ __ move(A0, thread); ++ __ set_last_Java_frame(noreg, FP, NULL); ++ //align the stack before invoke native ++ __ move(AT, -(StackAlignmentInBytes)); ++ __ andr(SP, SP, AT); ++ __ relocate(relocInfo::internal_pc_type); ++ { ++ intptr_t save_pc = (intptr_t)__ pc() + NativeMovConstReg::instruction_size + 24 + 1 * BytesPerInstWord; ++ __ patchable_set48(AT, save_pc); ++ } ++ __ sd(AT, thread, in_bytes(JavaThread::last_Java_pc_offset())); ++ ++ __ call(destination); ++ __ delayed()->nop(); ++ ++ // Set an oopmap for the call site. ++ // We need this not only for callee-saved registers, but also for volatile ++ // registers that the compiler might be keeping live across a safepoint. ++ oop_maps->add_gc_map( __ offset() - start, map); ++ // V0 contains the address we are going to jump to assuming no exception got installed ++ __ get_thread(thread); ++ __ ld_ptr(SP, thread, in_bytes(JavaThread::last_Java_sp_offset())); ++ // clear last_Java_sp ++ __ reset_last_Java_frame(true); ++ // check for pending exceptions ++ Label pending; ++ __ ld_ptr(AT, thread, in_bytes(Thread::pending_exception_offset())); ++ __ bne(AT, R0, pending); ++ __ delayed()->nop(); ++ // get the returned Method* ++ //FIXME, do mips need this ? ++ __ get_vm_result_2(Rmethod, thread); // Refer to OpenJDK8 ++ __ st_ptr(Rmethod, SP, RegisterSaver::methodOffset() * wordSize); ++ __ st_ptr(V0, SP, RegisterSaver::v0Offset() * wordSize); ++ RegisterSaver::restore_live_registers(masm); ++ ++ // We are back the the original state on entry and ready to go the callee method. ++ __ jr(V0); ++ __ delayed()->nop(); ++ // Pending exception after the safepoint ++ ++ __ bind(pending); ++ ++ RegisterSaver::restore_live_registers(masm); ++ ++ // exception pending => remove activation and forward to exception handler ++ //forward_exception_entry need return address on the stack ++ __ push(RA); ++ __ get_thread(thread); ++ __ st_ptr(R0, thread, in_bytes(JavaThread::vm_result_offset())); ++ __ ld_ptr(V0, thread, in_bytes(Thread::pending_exception_offset())); ++ __ jmp(StubRoutines::forward_exception_entry(), relocInfo::runtime_call_type); ++ __ delayed()->nop(); ++ // ++ // make sure all code is generated ++ masm->flush(); ++ ++ RuntimeStub* tmp= RuntimeStub::new_runtime_stub(name, &buffer, frame_complete, frame_size_words, oop_maps, true); ++ return tmp; ++} ++ ++extern "C" int SpinPause() {return 0;} ++ ++ ++//------------------------------Montgomery multiplication------------------------ ++// ++ ++// Subtract 0:b from carry:a. Return carry. ++static unsigned long ++sub(unsigned long a[], unsigned long b[], unsigned long carry, long len) { ++ long borrow = 0, t = 0; ++ unsigned long tmp0, tmp1; ++ __asm__ __volatile__ ( ++ "0: \n" ++ "ld %[tmp0], 0(%[a]) \n" ++ "ld %[tmp1], 0(%[b]) \n" ++ "sltu %[t], %[tmp0], %[borrow] \n" ++ "dsubu %[tmp0], %[tmp0], %[borrow] \n" ++ "sltu %[borrow], %[tmp0], %[tmp1] \n" ++ "or %[borrow], %[borrow], %[t] \n" ++ "dsubu %[tmp0], %[tmp0], %[tmp1] \n" ++ "sd %[tmp0], 0(%[a]) \n" ++ "daddiu %[a], %[a], 8 \n" ++ "daddiu %[b], %[b], 8 \n" ++ "daddiu %[len], %[len], -1 \n" ++ "bgtz %[len], 0b \n" ++ "dsubu %[tmp0], %[carry], %[borrow] \n" ++ : [len]"+r"(len), [tmp0]"=&r"(tmp0), [tmp1]"=&r"(tmp1), [borrow]"+r"(borrow), [a]"+r"(a), [b]"+r"(b), [t]"+r"(t) ++ : [carry]"r"(carry) ++ : "memory" ++ ); ++ return tmp0; ++} ++ ++// Multiply (unsigned) Long A by Long B, accumulating the double- ++// length result into the accumulator formed of t0, t1, and t2. ++inline void MACC(unsigned long A, unsigned long B, unsigned long &t0, unsigned long &t1, unsigned long &t2) { ++ unsigned long hi, lo, carry = 0, t = 0; ++ __asm__ __volatile__( ++ "dmultu %[A], %[B] \n" ++ "mfhi %[hi] \n" ++ "mflo %[lo] \n" ++ "daddu %[t0], %[t0], %[lo] \n" ++ "sltu %[carry], %[t0], %[lo] \n" ++ "daddu %[t1], %[t1], %[carry] \n" ++ "sltu %[t], %[t1], %[carry] \n" ++ "daddu %[t1], %[t1], %[hi] \n" ++ "sltu %[carry], %[t1], %[hi] \n" ++ "or %[carry], %[carry], %[t] \n" ++ "daddu %[t2], %[t2], %[carry] \n" ++ : [hi]"=&r"(hi), [lo]"=&r"(lo), [t0]"+r"(t0), [t1]"+r"(t1), [t2]"+r"(t2), [carry]"+r"(carry), [t]"+r"(t) ++ : [A]"r"(A), [B]"r"(B) ++ : ++ ); ++} ++ ++// As above, but add twice the double-length result into the ++// accumulator. ++inline void MACC2(unsigned long A, unsigned long B, unsigned long &t0, unsigned long &t1, unsigned long &t2) { ++ unsigned long hi, lo, carry = 0, t = 0; ++ __asm__ __volatile__( ++ "dmultu %[A], %[B] \n" ++ "mfhi %[hi] \n" ++ "mflo %[lo] \n" ++ "daddu %[t0], %[t0], %[lo] \n" ++ "sltu %[carry], %[t0], %[lo] \n" ++ "daddu %[t1], %[t1], %[carry] \n" ++ "sltu %[t], %[t1], %[carry] \n" ++ "daddu %[t1], %[t1], %[hi] \n" ++ "sltu %[carry], %[t1], %[hi] \n" ++ "or %[carry], %[carry], %[t] \n" ++ "daddu %[t2], %[t2], %[carry] \n" ++ "daddu %[t0], %[t0], %[lo] \n" ++ "sltu %[carry], %[t0], %[lo] \n" ++ "daddu %[t1], %[t1], %[carry] \n" ++ "sltu %[t], %[t1], %[carry] \n" ++ "daddu %[t1], %[t1], %[hi] \n" ++ "sltu %[carry], %[t1], %[hi] \n" ++ "or %[carry], %[carry], %[t] \n" ++ "daddu %[t2], %[t2], %[carry] \n" ++ : [hi]"=&r"(hi), [lo]"=&r"(lo), [t0]"+r"(t0), [t1]"+r"(t1), [t2]"+r"(t2), [carry]"+r"(carry), [t]"+r"(t) ++ : [A]"r"(A), [B]"r"(B) ++ : ++ ); ++} ++ ++// Fast Montgomery multiplication. The derivation of the algorithm is ++// in A Cryptographic Library for the Motorola DSP56000, ++// Dusse and Kaliski, Proc. EUROCRYPT 90, pp. 230-237. ++ ++static void __attribute__((noinline)) ++montgomery_multiply(unsigned long a[], unsigned long b[], unsigned long n[], ++ unsigned long m[], unsigned long inv, int len) { ++ unsigned long t0 = 0, t1 = 0, t2 = 0; // Triple-precision accumulator ++ int i; ++ ++ assert(inv * n[0] == -1UL, "broken inverse in Montgomery multiply"); ++ ++ for (i = 0; i < len; i++) { ++ int j; ++ for (j = 0; j < i; j++) { ++ MACC(a[j], b[i-j], t0, t1, t2); ++ MACC(m[j], n[i-j], t0, t1, t2); ++ } ++ MACC(a[i], b[0], t0, t1, t2); ++ m[i] = t0 * inv; ++ MACC(m[i], n[0], t0, t1, t2); ++ ++ assert(t0 == 0, "broken Montgomery multiply"); ++ ++ t0 = t1; t1 = t2; t2 = 0; ++ } ++ ++ for (i = len; i < 2*len; i++) { ++ int j; ++ for (j = i-len+1; j < len; j++) { ++ MACC(a[j], b[i-j], t0, t1, t2); ++ MACC(m[j], n[i-j], t0, t1, t2); ++ } ++ m[i-len] = t0; ++ t0 = t1; t1 = t2; t2 = 0; ++ } ++ ++ while (t0) ++ t0 = sub(m, n, t0, len); ++} ++ ++// Fast Montgomery squaring. This uses asymptotically 25% fewer ++// multiplies so it should be up to 25% faster than Montgomery ++// multiplication. However, its loop control is more complex and it ++// may actually run slower on some machines. ++ ++static void __attribute__((noinline)) ++montgomery_square(unsigned long a[], unsigned long n[], ++ unsigned long m[], unsigned long inv, int len) { ++ unsigned long t0 = 0, t1 = 0, t2 = 0; // Triple-precision accumulator ++ int i; ++ ++ assert(inv * n[0] == -1UL, "broken inverse in Montgomery multiply"); ++ ++ for (i = 0; i < len; i++) { ++ int j; ++ int end = (i+1)/2; ++ for (j = 0; j < end; j++) { ++ MACC2(a[j], a[i-j], t0, t1, t2); ++ MACC(m[j], n[i-j], t0, t1, t2); ++ } ++ if ((i & 1) == 0) { ++ MACC(a[j], a[j], t0, t1, t2); ++ } ++ for (; j < i; j++) { ++ MACC(m[j], n[i-j], t0, t1, t2); ++ } ++ m[i] = t0 * inv; ++ MACC(m[i], n[0], t0, t1, t2); ++ ++ assert(t0 == 0, "broken Montgomery square"); ++ ++ t0 = t1; t1 = t2; t2 = 0; ++ } ++ ++ for (i = len; i < 2*len; i++) { ++ int start = i-len+1; ++ int end = start + (len - start)/2; ++ int j; ++ for (j = start; j < end; j++) { ++ MACC2(a[j], a[i-j], t0, t1, t2); ++ MACC(m[j], n[i-j], t0, t1, t2); ++ } ++ if ((i & 1) == 0) { ++ MACC(a[j], a[j], t0, t1, t2); ++ } ++ for (; j < len; j++) { ++ MACC(m[j], n[i-j], t0, t1, t2); ++ } ++ m[i-len] = t0; ++ t0 = t1; t1 = t2; t2 = 0; ++ } ++ ++ while (t0) ++ t0 = sub(m, n, t0, len); ++} ++ ++// Swap words in a longword. ++static unsigned long swap(unsigned long x) { ++ return (x << 32) | (x >> 32); ++} ++ ++// Copy len longwords from s to d, word-swapping as we go. The ++// destination array is reversed. ++static void reverse_words(unsigned long *s, unsigned long *d, int len) { ++ d += len; ++ while(len-- > 0) { ++ d--; ++ *d = swap(*s); ++ s++; ++ } ++} ++ ++// The threshold at which squaring is advantageous was determined ++// experimentally on an i7-3930K (Ivy Bridge) CPU @ 3.5GHz. ++// Doesn't seem to be relevant for MIPS64 so we use the same value. ++#define MONTGOMERY_SQUARING_THRESHOLD 64 ++ ++void SharedRuntime::montgomery_multiply(jint *a_ints, jint *b_ints, jint *n_ints, ++ jint len, jlong inv, ++ jint *m_ints) { ++ assert(len % 2 == 0, "array length in montgomery_multiply must be even"); ++ int longwords = len/2; ++ ++ // Make very sure we don't use so much space that the stack might ++ // overflow. 512 jints corresponds to an 16384-bit integer and ++ // will use here a total of 8k bytes of stack space. ++ int total_allocation = longwords * sizeof (unsigned long) * 4; ++ guarantee(total_allocation <= 8192, "must be"); ++ unsigned long *scratch = (unsigned long *)alloca(total_allocation); ++ ++ // Local scratch arrays ++ unsigned long ++ *a = scratch + 0 * longwords, ++ *b = scratch + 1 * longwords, ++ *n = scratch + 2 * longwords, ++ *m = scratch + 3 * longwords; ++ ++ reverse_words((unsigned long *)a_ints, a, longwords); ++ reverse_words((unsigned long *)b_ints, b, longwords); ++ reverse_words((unsigned long *)n_ints, n, longwords); ++ ++ ::montgomery_multiply(a, b, n, m, (unsigned long)inv, longwords); ++ ++ reverse_words(m, (unsigned long *)m_ints, longwords); ++} ++ ++void SharedRuntime::montgomery_square(jint *a_ints, jint *n_ints, ++ jint len, jlong inv, ++ jint *m_ints) { ++ assert(len % 2 == 0, "array length in montgomery_square must be even"); ++ int longwords = len/2; ++ ++ // Make very sure we don't use so much space that the stack might ++ // overflow. 512 jints corresponds to an 16384-bit integer and ++ // will use here a total of 6k bytes of stack space. ++ int total_allocation = longwords * sizeof (unsigned long) * 3; ++ guarantee(total_allocation <= 8192, "must be"); ++ unsigned long *scratch = (unsigned long *)alloca(total_allocation); ++ ++ // Local scratch arrays ++ unsigned long ++ *a = scratch + 0 * longwords, ++ *n = scratch + 1 * longwords, ++ *m = scratch + 2 * longwords; ++ ++ reverse_words((unsigned long *)a_ints, a, longwords); ++ reverse_words((unsigned long *)n_ints, n, longwords); ++ ++ if (len >= MONTGOMERY_SQUARING_THRESHOLD) { ++ ::montgomery_square(a, n, m, (unsigned long)inv, longwords); ++ } else { ++ ::montgomery_multiply(a, a, n, m, (unsigned long)inv, longwords); ++ } ++ ++ reverse_words(m, (unsigned long *)m_ints, longwords); ++} +diff --git a/hotspot/src/cpu/mips/vm/stubGenerator_mips_64.cpp b/hotspot/src/cpu/mips/vm/stubGenerator_mips_64.cpp +new file mode 100644 +index 0000000000..aeb797faf9 +--- /dev/null ++++ b/hotspot/src/cpu/mips/vm/stubGenerator_mips_64.cpp +@@ -0,0 +1,2147 @@ ++/* ++ * Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2015, 2022, Loongson Technology. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ * ++ */ ++ ++#include "precompiled.hpp" ++#include "asm/macroAssembler.hpp" ++#include "asm/macroAssembler.inline.hpp" ++#include "interpreter/interpreter.hpp" ++#include "nativeInst_mips.hpp" ++#include "oops/instanceOop.hpp" ++#include "oops/method.hpp" ++#include "oops/objArrayKlass.hpp" ++#include "oops/oop.inline.hpp" ++#include "prims/methodHandles.hpp" ++#include "runtime/frame.inline.hpp" ++#include "runtime/handles.inline.hpp" ++#include "runtime/sharedRuntime.hpp" ++#include "runtime/stubCodeGenerator.hpp" ++#include "runtime/stubRoutines.hpp" ++#include "runtime/thread.inline.hpp" ++#include "utilities/top.hpp" ++#ifdef COMPILER2 ++#include "opto/runtime.hpp" ++#endif ++ ++// Declaration and definition of StubGenerator (no .hpp file). ++// For a more detailed description of the stub routine structure ++// see the comment in stubRoutines.hpp ++ ++#define __ _masm-> ++ ++#define A0 RA0 ++#define A1 RA1 ++#define A2 RA2 ++#define A3 RA3 ++#define A4 RA4 ++#define A5 RA5 ++#define A6 RA6 ++#define A7 RA7 ++#define T0 RT0 ++#define T1 RT1 ++#define T2 RT2 ++#define T3 RT3 ++#define T8 RT8 ++#define T9 RT9 ++ ++#define TIMES_OOP (UseCompressedOops ? Address::times_4 : Address::times_8) ++//#define a__ ((Assembler*)_masm)-> ++ ++//#ifdef PRODUCT ++//#define BLOCK_COMMENT(str) /* nothing */ ++//#else ++//#define BLOCK_COMMENT(str) __ block_comment(str) ++//#endif ++ ++//#define BIND(label) bind(label); BLOCK_COMMENT(#label ":") ++const int MXCSR_MASK = 0xFFC0; // Mask out any pending exceptions ++ ++// Stub Code definitions ++ ++static address handle_unsafe_access() { ++ JavaThread* thread = JavaThread::current(); ++ address pc = thread->saved_exception_pc(); ++ // pc is the instruction which we must emulate ++ // doing a no-op is fine: return garbage from the load ++ // therefore, compute npc ++ address npc = (address)((unsigned long)pc + sizeof(unsigned int)); ++ ++ // request an async exception ++ thread->set_pending_unsafe_access_error(); ++ ++ // return address of next instruction to execute ++ return npc; ++} ++ ++class StubGenerator: public StubCodeGenerator { ++ private: ++ ++ // ABI mips n64 ++ // This fig is not MIPS ABI. It is call Java from C ABI. ++ // Call stubs are used to call Java from C ++ // ++ // [ return_from_Java ] ++ // [ argument word n-1 ] <--- sp ++ // ... ++ // [ argument word 0 ] ++ // ... ++ // -8 [ S6 ] ++ // -7 [ S5 ] ++ // -6 [ S4 ] ++ // -5 [ S3 ] ++ // -4 [ S1 ] ++ // -3 [ TSR(S2) ] ++ // -2 [ LVP(S7) ] ++ // -1 [ BCP(S1) ] ++ // 0 [ saved fp ] <--- fp_after_call ++ // 1 [ return address ] ++ // 2 [ ptr. to call wrapper ] <--- a0 (old sp -->)fp ++ // 3 [ result ] <--- a1 ++ // 4 [ result_type ] <--- a2 ++ // 5 [ method ] <--- a3 ++ // 6 [ entry_point ] <--- a4 ++ // 7 [ parameters ] <--- a5 ++ // 8 [ parameter_size ] <--- a6 ++ // 9 [ thread ] <--- a7 ++ ++ // ++ // n64 does not save paras in sp. ++ // ++ // [ return_from_Java ] ++ // [ argument word n-1 ] <--- sp ++ // ... ++ // [ argument word 0 ] ++ // ... ++ //-13 [ thread ] ++ //-12 [ result_type ] <--- a2 ++ //-11 [ result ] <--- a1 ++ //-10 [ ] ++ // -9 [ ptr. to call wrapper ] <--- a0 ++ // -8 [ S6 ] ++ // -7 [ S5 ] ++ // -6 [ S4 ] ++ // -5 [ S3 ] ++ // -4 [ S1 ] ++ // -3 [ TSR(S2) ] ++ // -2 [ LVP(S7) ] ++ // -1 [ BCP(S1) ] ++ // 0 [ saved fp ] <--- fp_after_call ++ // 1 [ return address ] ++ // 2 [ ] <--- old sp ++ // ++ // Find a right place in the call_stub for GP. ++ // GP will point to the starting point of Interpreter::dispatch_table(itos). ++ // It should be saved/restored before/after Java calls. ++ // ++ enum call_stub_layout { ++ RA_off = 1, ++ FP_off = 0, ++ BCP_off = -1, ++ LVP_off = -2, ++ TSR_off = -3, ++ S1_off = -4, ++ S3_off = -5, ++ S4_off = -6, ++ S5_off = -7, ++ S6_off = -8, ++ call_wrapper_off = -9, ++ result_off = -11, ++ result_type_off = -12, ++ thread_off = -13, ++ total_off = thread_off - 1, ++ GP_off = -14, ++ }; ++ ++ address generate_call_stub(address& return_address) { ++ ++ assert((int)frame::entry_frame_call_wrapper_offset == (int)call_wrapper_off, "adjust this code"); ++ StubCodeMark mark(this, "StubRoutines", "call_stub"); ++ address start = __ pc(); ++ ++ // same as in generate_catch_exception()! ++ ++ // stub code ++ // save ra and fp ++ __ enter(); ++ // I think 14 is the max gap between argument and callee saved register ++ __ daddiu(SP, SP, total_off * wordSize); ++ __ sd(BCP, FP, BCP_off * wordSize); ++ __ sd(LVP, FP, LVP_off * wordSize); ++ __ sd(TSR, FP, TSR_off * wordSize); ++ __ sd(S1, FP, S1_off * wordSize); ++ __ sd(S3, FP, S3_off * wordSize); ++ __ sd(S4, FP, S4_off * wordSize); ++ __ sd(S5, FP, S5_off * wordSize); ++ __ sd(S6, FP, S6_off * wordSize); ++ __ sd(A0, FP, call_wrapper_off * wordSize); ++ __ sd(A1, FP, result_off * wordSize); ++ __ sd(A2, FP, result_type_off * wordSize); ++ __ sd(A7, FP, thread_off * wordSize); ++ __ sd(GP, FP, GP_off * wordSize); ++ ++ __ set64(GP, (long)Interpreter::dispatch_table(itos)); ++ ++#ifdef OPT_THREAD ++ __ move(TREG, A7); ++#endif ++ //add for compressedoops ++ __ reinit_heapbase(); ++ ++#ifdef ASSERT ++ // make sure we have no pending exceptions ++ { ++ Label L; ++ __ ld(AT, A7, in_bytes(Thread::pending_exception_offset())); ++ __ beq(AT, R0, L); ++ __ delayed()->nop(); ++ /* FIXME: I do not know how to realize stop in mips arch, do it in the future */ ++ __ stop("StubRoutines::call_stub: entered with pending exception"); ++ __ bind(L); ++ } ++#endif ++ ++ // pass parameters if any ++ // A5: parameter ++ // A6: parameter_size ++ // T0: parameter_size_tmp(--) ++ // T2: offset(++) ++ // T3: tmp ++ Label parameters_done; ++ // judge if the parameter_size equals 0 ++ __ beq(A6, R0, parameters_done); ++ __ delayed()->nop(); ++ __ dsll(AT, A6, Interpreter::logStackElementSize); ++ __ dsubu(SP, SP, AT); ++ __ move(AT, -StackAlignmentInBytes); ++ __ andr(SP, SP , AT); ++ // Copy Java parameters in reverse order (receiver last) ++ // Note that the argument order is inverted in the process ++ Label loop; ++ __ move(T0, A6); ++ __ move(T2, R0); ++ __ bind(loop); ++ ++ // get parameter ++ __ dsll(T3, T0, LogBytesPerWord); ++ __ daddu(T3, T3, A5); ++ __ ld(AT, T3, -wordSize); ++ __ dsll(T3, T2, LogBytesPerWord); ++ __ daddu(T3, T3, SP); ++ __ sd(AT, T3, Interpreter::expr_offset_in_bytes(0)); ++ __ daddiu(T2, T2, 1); ++ __ daddiu(T0, T0, -1); ++ __ bne(T0, R0, loop); ++ __ delayed()->nop(); ++ // advance to next parameter ++ ++ // call Java function ++ __ bind(parameters_done); ++ ++ // receiver in V0, methodOop in Rmethod ++ ++ __ move(Rmethod, A3); ++ __ move(Rsender, SP); //set sender sp ++ __ jalr(A4); ++ __ delayed()->nop(); ++ return_address = __ pc(); ++ ++ Label common_return; ++ __ bind(common_return); ++ ++ // store result depending on type ++ // (everything that is not T_LONG, T_FLOAT or T_DOUBLE is treated as T_INT) ++ __ ld(T0, FP, result_off * wordSize); // result --> T0 ++ Label is_long, is_float, is_double, exit; ++ __ ld(T2, FP, result_type_off * wordSize); // result_type --> T2 ++ __ daddiu(T3, T2, (-1) * T_LONG); ++ __ beq(T3, R0, is_long); ++ __ delayed()->daddiu(T3, T2, (-1) * T_FLOAT); ++ __ beq(T3, R0, is_float); ++ __ delayed()->daddiu(T3, T2, (-1) * T_DOUBLE); ++ __ beq(T3, R0, is_double); ++ __ delayed()->nop(); ++ ++ // handle T_INT case ++ __ sd(V0, T0, 0 * wordSize); ++ __ bind(exit); ++ ++ // restore ++ __ ld(BCP, FP, BCP_off * wordSize); ++ __ ld(LVP, FP, LVP_off * wordSize); ++ __ ld(GP, FP, GP_off * wordSize); ++ __ ld(TSR, FP, TSR_off * wordSize); ++ ++ __ ld(S1, FP, S1_off * wordSize); ++ __ ld(S3, FP, S3_off * wordSize); ++ __ ld(S4, FP, S4_off * wordSize); ++ __ ld(S5, FP, S5_off * wordSize); ++ __ ld(S6, FP, S6_off * wordSize); ++ ++ __ leave(); ++ ++ // return ++ __ jr(RA); ++ __ delayed()->nop(); ++ ++ // handle return types different from T_INT ++ __ bind(is_long); ++ __ sd(V0, T0, 0 * wordSize); ++ __ b(exit); ++ __ delayed()->nop(); ++ ++ __ bind(is_float); ++ __ swc1(F0, T0, 0 * wordSize); ++ __ b(exit); ++ __ delayed()->nop(); ++ ++ __ bind(is_double); ++ __ sdc1(F0, T0, 0 * wordSize); ++ __ b(exit); ++ __ delayed()->nop(); ++ //FIXME, 1.6 mips version add operation of fpu here ++ StubRoutines::gs2::set_call_stub_compiled_return(__ pc()); ++ __ b(common_return); ++ __ delayed()->nop(); ++ return start; ++ } ++ ++ // Return point for a Java call if there's an exception thrown in ++ // Java code. The exception is caught and transformed into a ++ // pending exception stored in JavaThread that can be tested from ++ // within the VM. ++ // ++ // Note: Usually the parameters are removed by the callee. In case ++ // of an exception crossing an activation frame boundary, that is ++ // not the case if the callee is compiled code => need to setup the ++ // sp. ++ // ++ // V0: exception oop ++ ++ address generate_catch_exception() { ++ StubCodeMark mark(this, "StubRoutines", "catch_exception"); ++ address start = __ pc(); ++ ++ Register thread = TREG; ++ ++ // get thread directly ++#ifndef OPT_THREAD ++ __ ld(thread, FP, thread_off * wordSize); ++#endif ++ ++#ifdef ASSERT ++ // verify that threads correspond ++ { Label L; ++ __ get_thread(T8); ++ __ beq(T8, thread, L); ++ __ delayed()->nop(); ++ __ stop("StubRoutines::catch_exception: threads must correspond"); ++ __ bind(L); ++ } ++#endif ++ // set pending exception ++ __ verify_oop(V0); ++ __ sd(V0, thread, in_bytes(Thread::pending_exception_offset())); ++ __ li(AT, (long)__FILE__); ++ __ sd(AT, thread, in_bytes(Thread::exception_file_offset ())); ++ __ li(AT, (long)__LINE__); ++ __ sd(AT, thread, in_bytes(Thread::exception_line_offset ())); ++ ++ // complete return to VM ++ assert(StubRoutines::_call_stub_return_address != NULL, "_call_stub_return_address must have been generated before"); ++ __ jmp(StubRoutines::_call_stub_return_address, relocInfo::none); ++ __ delayed()->nop(); ++ ++ return start; ++ } ++ ++ // Continuation point for runtime calls returning with a pending ++ // exception. The pending exception check happened in the runtime ++ // or native call stub. The pending exception in Thread is ++ // converted into a Java-level exception. ++ // ++ // Contract with Java-level exception handlers: ++ // V0: exception ++ // V1: throwing pc ++ // ++ // NOTE: At entry of this stub, exception-pc must be on stack !! ++ ++ address generate_forward_exception() { ++ StubCodeMark mark(this, "StubRoutines", "forward exception"); ++ //Register thread = TREG; ++ Register thread = TREG; ++ address start = __ pc(); ++ ++ // Upon entry, the sp points to the return address returning into ++ // Java (interpreted or compiled) code; i.e., the return address ++ // throwing pc. ++ // ++ // Arguments pushed before the runtime call are still on the stack ++ // but the exception handler will reset the stack pointer -> ++ // ignore them. A potential result in registers can be ignored as ++ // well. ++ ++#ifndef OPT_THREAD ++ __ get_thread(thread); ++#endif ++#ifdef ASSERT ++ // make sure this code is only executed if there is a pending exception ++ { ++ Label L; ++ __ ld(AT, thread, in_bytes(Thread::pending_exception_offset())); ++ __ bne(AT, R0, L); ++ __ delayed()->nop(); ++ __ stop("StubRoutines::forward exception: no pending exception (1)"); ++ __ bind(L); ++ } ++#endif ++ ++ // compute exception handler into T9 ++ __ ld(A1, SP, 0); ++ __ call_VM_leaf(CAST_FROM_FN_PTR(address, SharedRuntime::exception_handler_for_return_address), thread, A1); ++ __ move(T9, V0); ++ __ pop(V1); ++ ++#ifndef OPT_THREAD ++ __ get_thread(thread); ++#endif ++ __ ld(V0, thread, in_bytes(Thread::pending_exception_offset())); ++ __ sd(R0, thread, in_bytes(Thread::pending_exception_offset())); ++ ++#ifdef ASSERT ++ // make sure exception is set ++ { ++ Label L; ++ __ bne(V0, R0, L); ++ __ delayed()->nop(); ++ __ stop("StubRoutines::forward exception: no pending exception (2)"); ++ __ bind(L); ++ } ++#endif ++ ++ // continue at exception handler (return address removed) ++ // V0: exception ++ // T9: exception handler ++ // V1: throwing pc ++ __ verify_oop(V0); ++ __ jr(T9); ++ __ delayed()->nop(); ++ ++ return start; ++ } ++ ++ // The following routine generates a subroutine to throw an ++ // asynchronous UnknownError when an unsafe access gets a fault that ++ // could not be reasonably prevented by the programmer. (Example: ++ // SIGBUS/OBJERR.) ++ address generate_handler_for_unsafe_access() { ++ StubCodeMark mark(this, "StubRoutines", "handler_for_unsafe_access"); ++ address start = __ pc(); ++ __ push(V0); ++ __ pushad_except_v0(); // push registers ++ __ call(CAST_FROM_FN_PTR(address, handle_unsafe_access), relocInfo::runtime_call_type); ++ __ delayed()->nop(); ++ __ popad_except_v0(); ++ __ move(RA, V0); ++ __ pop(V0); ++ __ jr(RA); ++ __ delayed()->nop(); ++ return start; ++ } ++ ++ // Non-destructive plausibility checks for oops ++ // ++ address generate_verify_oop() { ++ StubCodeMark mark(this, "StubRoutines", "verify_oop"); ++ address start = __ pc(); ++ __ reinit_heapbase(); ++ __ verify_oop_subroutine(); ++ address end = __ pc(); ++ return start; ++ } ++ ++ // ++ // Generate overlap test for array copy stubs ++ // ++ // Input: ++ // A0 - array1 ++ // A1 - array2 ++ // A2 - element count ++ // ++ ++ // use T9 as temp ++ void array_overlap_test(address no_overlap_target, int log2_elem_size) { ++ int elem_size = 1 << log2_elem_size; ++ Address::ScaleFactor sf = Address::times_1; ++ ++ switch (log2_elem_size) { ++ case 0: sf = Address::times_1; break; ++ case 1: sf = Address::times_2; break; ++ case 2: sf = Address::times_4; break; ++ case 3: sf = Address::times_8; break; ++ } ++ ++ __ dsll(AT, A2, sf); ++ __ daddu(AT, AT, A0); ++ __ daddiu(T9, AT, -elem_size); ++ __ dsubu(AT, A1, A0); ++ __ blez(AT, no_overlap_target); ++ __ delayed()->nop(); ++ __ dsubu(AT, A1, T9); ++ __ bgtz(AT, no_overlap_target); ++ __ delayed()->nop(); ++ ++ // If A0 = 0xf... and A1 = 0x0..., than goto no_overlap_target ++ Label L; ++ __ bgez(A0, L); ++ __ delayed()->nop(); ++ __ bgtz(A1, no_overlap_target); ++ __ delayed()->nop(); ++ __ bind(L); ++ ++ } ++ ++ // ++ // Generate store check for array ++ // ++ // Input: ++ // T0 - starting address ++ // T1 - element count ++ // ++ // The 2 input registers are overwritten ++ // ++ ++ ++ void array_store_check(Register tmp) { ++ assert_different_registers(tmp, AT, T0, T1); ++ BarrierSet* bs = Universe::heap()->barrier_set(); ++ assert(bs->kind() == BarrierSet::CardTableModRef, "Wrong barrier set kind"); ++ CardTableModRefBS* ct = (CardTableModRefBS*)bs; ++ assert(sizeof(*ct->byte_map_base) == sizeof(jbyte), "adjust this code"); ++ Label l_0; ++ ++ if (UseConcMarkSweepGC) __ sync(); ++ ++ __ set64(tmp, (long)ct->byte_map_base); ++ ++ __ dsll(AT, T1, TIMES_OOP); ++ __ daddu(AT, T0, AT); ++ __ daddiu(T1, AT, - BytesPerHeapOop); ++ ++ __ shr(T0, CardTableModRefBS::card_shift); ++ __ shr(T1, CardTableModRefBS::card_shift); ++ ++ __ dsubu(T1, T1, T0); // end --> cards count ++ __ bind(l_0); ++ ++ __ daddu(AT, tmp, T0); ++ if (UseLEXT1) { ++ __ gssbx(R0, AT, T1, 0); ++ } else { ++ __ daddu(AT, AT, T1); ++ __ sb(R0, AT, 0); ++ } ++ ++ __ bgtz(T1, l_0); ++ __ delayed()->daddiu(T1, T1, - 1); ++ } ++ ++ // Generate code for an array write pre barrier ++ // ++ // addr - starting address ++ // count - element count ++ // tmp - scratch register ++ // ++ // Destroy no registers! ++ // ++ void gen_write_ref_array_pre_barrier(Register addr, Register count, bool dest_uninitialized) { ++ BarrierSet* bs = Universe::heap()->barrier_set(); ++ switch (bs->kind()) { ++ case BarrierSet::G1SATBCT: ++ case BarrierSet::G1SATBCTLogging: ++ // With G1, don't generate the call if we statically know that the target in uninitialized ++ if (!dest_uninitialized) { ++ __ pushad(); // push registers ++ if (count == A0) { ++ if (addr == A1) { ++ // exactly backwards!! ++ //__ xchgptr(c_rarg1, c_rarg0); ++ __ move(AT, A0); ++ __ move(A0, A1); ++ __ move(A1, AT); ++ } else { ++ __ move(A1, count); ++ __ move(A0, addr); ++ } ++ } else { ++ __ move(A0, addr); ++ __ move(A1, count); ++ } ++ __ call_VM_leaf(CAST_FROM_FN_PTR(address, BarrierSet::static_write_ref_array_pre), 2); ++ __ popad(); ++ } ++ break; ++ case BarrierSet::CardTableModRef: ++ case BarrierSet::CardTableExtension: ++ case BarrierSet::ModRef: ++ break; ++ default: ++ ShouldNotReachHere(); ++ ++ } ++ } ++ ++ // ++ // Generate code for an array write post barrier ++ // ++ // Input: ++ // start - register containing starting address of destination array ++ // count - elements count ++ // scratch - scratch register ++ // ++ // The input registers are overwritten. ++ // ++ void gen_write_ref_array_post_barrier(Register start, Register count, Register scratch) { ++ assert_different_registers(start, count, scratch, AT); ++ BarrierSet* bs = Universe::heap()->barrier_set(); ++ switch (bs->kind()) { ++ case BarrierSet::G1SATBCT: ++ case BarrierSet::G1SATBCTLogging: ++ { ++ __ pushad(); // push registers (overkill) ++ if (count == A0) { ++ if (start == A1) { ++ // exactly backwards!! ++ //__ xchgptr(c_rarg1, c_rarg0); ++ __ move(AT, A0); ++ __ move(A0, A1); ++ __ move(A1, AT); ++ } else { ++ __ move(A1, count); ++ __ move(A0, start); ++ } ++ } else { ++ __ move(A0, start); ++ __ move(A1, count); ++ } ++ __ call_VM_leaf(CAST_FROM_FN_PTR(address, BarrierSet::static_write_ref_array_post), 2); ++ __ popad(); ++ } ++ break; ++ case BarrierSet::CardTableModRef: ++ case BarrierSet::CardTableExtension: ++ { ++ CardTableModRefBS* ct = (CardTableModRefBS*)bs; ++ assert(sizeof(*ct->byte_map_base) == sizeof(jbyte), "adjust this code"); ++ ++ Label L_loop; ++ const Register end = count; ++ ++ if (UseConcMarkSweepGC) __ sync(); ++ ++ int64_t disp = (int64_t) ct->byte_map_base; ++ __ set64(scratch, disp); ++ ++ __ lea(end, Address(start, count, TIMES_OOP, 0)); // end == start+count*oop_size ++ __ daddiu(end, end, -BytesPerHeapOop); // end - 1 to make inclusive ++ __ shr(start, CardTableModRefBS::card_shift); ++ __ shr(end, CardTableModRefBS::card_shift); ++ __ dsubu(end, end, start); // end --> cards count ++ ++ __ daddu(start, start, scratch); ++ ++ __ bind(L_loop); ++ if (UseLEXT1) { ++ __ gssbx(R0, start, count, 0); ++ } else { ++ __ daddu(AT, start, count); ++ __ sb(R0, AT, 0); ++ } ++ __ daddiu(count, count, -1); ++ __ slt(AT, count, R0); ++ __ beq(AT, R0, L_loop); ++ __ delayed()->nop(); ++ } ++ break; ++ default: ++ ShouldNotReachHere(); ++ } ++ } ++ ++ // Arguments: ++ // aligned - true => Input and output aligned on a HeapWord == 8-byte boundary ++ // ignored ++ // name - stub name string ++ // ++ // Inputs: ++ // c_rarg0 - source array address ++ // c_rarg1 - destination array address ++ // c_rarg2 - element count, treated as ssize_t, can be zero ++ // ++ // If 'from' and/or 'to' are aligned on 4-, 2-, or 1-byte boundaries, ++ // we let the hardware handle it. The one to eight bytes within words, ++ // dwords or qwords that span cache line boundaries will still be loaded ++ // and stored atomically. ++ // ++ // Side Effects: ++ // disjoint_byte_copy_entry is set to the no-overlap entry point ++ // used by generate_conjoint_byte_copy(). ++ // ++ address generate_disjoint_byte_copy(bool aligned, const char * name) { ++ StubCodeMark mark(this, "StubRoutines", name); ++ __ align(CodeEntryAlignment); ++ ++ ++ Register tmp1 = T0; ++ Register tmp2 = T1; ++ Register tmp3 = T3; ++ ++ address start = __ pc(); ++ ++ __ push(tmp1); ++ __ push(tmp2); ++ __ push(tmp3); ++ __ move(tmp1, A0); ++ __ move(tmp2, A1); ++ __ move(tmp3, A2); ++ ++ ++ Label l_1, l_2, l_3, l_4, l_5, l_6, l_7, l_8, l_9, l_10, l_11; ++ Label l_debug; ++ ++ __ daddiu(AT, tmp3, -9); //why the number is 9 ? ++ __ blez(AT, l_9); ++ __ delayed()->nop(); ++ ++ if (!aligned) { ++ __ xorr(AT, tmp1, tmp2); ++ __ andi(AT, AT, 1); ++ __ bne(AT, R0, l_9); // if arrays don't have the same alignment mod 2, do 1 element copy ++ __ delayed()->nop(); ++ ++ __ andi(AT, tmp1, 1); ++ __ beq(AT, R0, l_10); //copy 1 enlement if necessary to aligh to 2 bytes ++ __ delayed()->nop(); ++ ++ __ lb(AT, tmp1, 0); ++ __ daddiu(tmp1, tmp1, 1); ++ __ sb(AT, tmp2, 0); ++ __ daddiu(tmp2, tmp2, 1); ++ __ daddiu(tmp3, tmp3, -1); ++ __ bind(l_10); ++ ++ __ xorr(AT, tmp1, tmp2); ++ __ andi(AT, AT, 3); ++ __ bne(AT, R0, l_1); // if arrays don't have the same alignment mod 4, do 2 elements copy ++ __ delayed()->nop(); ++ ++ // At this point it is guaranteed that both, from and to have the same alignment mod 4. ++ ++ // Copy 2 elements if necessary to align to 4 bytes. ++ __ andi(AT, tmp1, 3); ++ __ beq(AT, R0, l_2); ++ __ delayed()->nop(); ++ ++ __ lhu(AT, tmp1, 0); ++ __ daddiu(tmp1, tmp1, 2); ++ __ sh(AT, tmp2, 0); ++ __ daddiu(tmp2, tmp2, 2); ++ __ daddiu(tmp3, tmp3, -2); ++ __ bind(l_2); ++ ++ // At this point the positions of both, from and to, are at least 4 byte aligned. ++ ++ // Copy 4 elements at a time. ++ // Align to 8 bytes, but only if both, from and to, have same alignment mod 8. ++ __ xorr(AT, tmp1, tmp2); ++ __ andi(AT, AT, 7); ++ __ bne(AT, R0, l_6); // not same alignment mod 8 -> copy 2, either from or to will be unaligned ++ __ delayed()->nop(); ++ ++ // Copy a 4 elements if necessary to align to 8 bytes. ++ __ andi(AT, tmp1, 7); ++ __ beq(AT, R0, l_7); ++ __ delayed()->nop(); ++ ++ __ lw(AT, tmp1, 0); ++ __ daddiu(tmp3, tmp3, -4); ++ __ sw(AT, tmp2, 0); ++ { // FasterArrayCopy ++ __ daddiu(tmp1, tmp1, 4); ++ __ daddiu(tmp2, tmp2, 4); ++ } ++ } ++ ++ __ bind(l_7); ++ ++ // Copy 4 elements at a time; either the loads or the stores can ++ // be unaligned if aligned == false. ++ ++ { // FasterArrayCopy ++ __ daddiu(AT, tmp3, -7); ++ __ blez(AT, l_6); // copy 4 at a time if less than 4 elements remain ++ __ delayed()->nop(); ++ ++ __ bind(l_8); ++ // For Loongson, there is 128-bit memory access. TODO ++ __ ld(AT, tmp1, 0); ++ __ sd(AT, tmp2, 0); ++ __ daddiu(tmp1, tmp1, 8); ++ __ daddiu(tmp2, tmp2, 8); ++ __ daddiu(tmp3, tmp3, -8); ++ __ daddiu(AT, tmp3, -8); ++ __ bgez(AT, l_8); ++ __ delayed()->nop(); ++ } ++ __ bind(l_6); ++ ++ // copy 4 bytes at a time ++ { // FasterArrayCopy ++ __ daddiu(AT, tmp3, -3); ++ __ blez(AT, l_1); ++ __ delayed()->nop(); ++ ++ __ bind(l_3); ++ __ lw(AT, tmp1, 0); ++ __ sw(AT, tmp2, 0); ++ __ daddiu(tmp1, tmp1, 4); ++ __ daddiu(tmp2, tmp2, 4); ++ __ daddiu(tmp3, tmp3, -4); ++ __ daddiu(AT, tmp3, -4); ++ __ bgez(AT, l_3); ++ __ delayed()->nop(); ++ ++ } ++ ++ // do 2 bytes copy ++ __ bind(l_1); ++ { ++ __ daddiu(AT, tmp3, -1); ++ __ blez(AT, l_9); ++ __ delayed()->nop(); ++ ++ __ bind(l_5); ++ __ lhu(AT, tmp1, 0); ++ __ daddiu(tmp3, tmp3, -2); ++ __ sh(AT, tmp2, 0); ++ __ daddiu(tmp1, tmp1, 2); ++ __ daddiu(tmp2, tmp2, 2); ++ __ daddiu(AT, tmp3, -2); ++ __ bgez(AT, l_5); ++ __ delayed()->nop(); ++ } ++ ++ //do 1 element copy--byte ++ __ bind(l_9); ++ __ beq(R0, tmp3, l_4); ++ __ delayed()->nop(); ++ ++ { ++ __ bind(l_11); ++ __ lb(AT, tmp1, 0); ++ __ daddiu(tmp3, tmp3, -1); ++ __ sb(AT, tmp2, 0); ++ __ daddiu(tmp1, tmp1, 1); ++ __ daddiu(tmp2, tmp2, 1); ++ __ daddiu(AT, tmp3, -1); ++ __ bgez(AT, l_11); ++ __ delayed()->nop(); ++ } ++ ++ __ bind(l_4); ++ __ pop(tmp3); ++ __ pop(tmp2); ++ __ pop(tmp1); ++ ++ __ jr(RA); ++ __ delayed()->nop(); ++ ++ return start; ++ } ++ ++ // Arguments: ++ // aligned - true => Input and output aligned on a HeapWord == 8-byte boundary ++ // ignored ++ // name - stub name string ++ // ++ // Inputs: ++ // A0 - source array address ++ // A1 - destination array address ++ // A2 - element count, treated as ssize_t, can be zero ++ // ++ // If 'from' and/or 'to' are aligned on 4-, 2-, or 1-byte boundaries, ++ // we let the hardware handle it. The one to eight bytes within words, ++ // dwords or qwords that span cache line boundaries will still be loaded ++ // and stored atomically. ++ // ++ address generate_conjoint_byte_copy(bool aligned, const char *name) { ++ __ align(CodeEntryAlignment); ++ StubCodeMark mark(this, "StubRoutines", name); ++ address start = __ pc(); ++ ++ Label l_copy_4_bytes_loop, l_copy_suffix, l_copy_suffix_loop, l_exit; ++ Label l_copy_byte, l_from_unaligned, l_unaligned, l_4_bytes_aligned; ++ ++ address nooverlap_target = aligned ? ++ StubRoutines::arrayof_jbyte_disjoint_arraycopy() : ++ StubRoutines::jbyte_disjoint_arraycopy(); ++ ++ array_overlap_test(nooverlap_target, 0); ++ ++ const Register from = A0; // source array address ++ const Register to = A1; // destination array address ++ const Register count = A2; // elements count ++ const Register end_from = T3; // source array end address ++ const Register end_to = T0; // destination array end address ++ const Register end_count = T1; // destination array end address ++ ++ __ push(end_from); ++ __ push(end_to); ++ __ push(end_count); ++ __ push(T8); ++ ++ // copy from high to low ++ __ move(end_count, count); ++ __ daddu(end_from, from, end_count); ++ __ daddu(end_to, to, end_count); ++ ++ // If end_from and end_to has differante alignment, unaligned copy is performed. ++ __ andi(AT, end_from, 3); ++ __ andi(T8, end_to, 3); ++ __ bne(AT, T8, l_copy_byte); ++ __ delayed()->nop(); ++ ++ // First deal with the unaligned data at the top. ++ __ bind(l_unaligned); ++ __ beq(end_count, R0, l_exit); ++ __ delayed()->nop(); ++ ++ __ andi(AT, end_from, 3); ++ __ bne(AT, R0, l_from_unaligned); ++ __ delayed()->nop(); ++ ++ __ andi(AT, end_to, 3); ++ __ beq(AT, R0, l_4_bytes_aligned); ++ __ delayed()->nop(); ++ ++ __ bind(l_from_unaligned); ++ __ lb(AT, end_from, -1); ++ __ sb(AT, end_to, -1); ++ __ daddiu(end_from, end_from, -1); ++ __ daddiu(end_to, end_to, -1); ++ __ daddiu(end_count, end_count, -1); ++ __ b(l_unaligned); ++ __ delayed()->nop(); ++ ++ // now end_to, end_from point to 4-byte aligned high-ends ++ // end_count contains byte count that is not copied. ++ // copy 4 bytes at a time ++ __ bind(l_4_bytes_aligned); ++ ++ __ move(T8, end_count); ++ __ daddiu(AT, end_count, -3); ++ __ blez(AT, l_copy_suffix); ++ __ delayed()->nop(); ++ ++ //__ andi(T8, T8, 3); ++ __ lea(end_from, Address(end_from, -4)); ++ __ lea(end_to, Address(end_to, -4)); ++ ++ __ dsrl(end_count, end_count, 2); ++ __ align(16); ++ __ bind(l_copy_4_bytes_loop); //l_copy_4_bytes ++ __ lw(AT, end_from, 0); ++ __ sw(AT, end_to, 0); ++ __ addiu(end_from, end_from, -4); ++ __ addiu(end_to, end_to, -4); ++ __ addiu(end_count, end_count, -1); ++ __ bne(end_count, R0, l_copy_4_bytes_loop); ++ __ delayed()->nop(); ++ ++ __ b(l_copy_suffix); ++ __ delayed()->nop(); ++ // copy dwords aligned or not with repeat move ++ // l_copy_suffix ++ // copy suffix (0-3 bytes) ++ __ bind(l_copy_suffix); ++ __ andi(T8, T8, 3); ++ __ beq(T8, R0, l_exit); ++ __ delayed()->nop(); ++ __ addiu(end_from, end_from, 3); ++ __ addiu(end_to, end_to, 3); ++ __ bind(l_copy_suffix_loop); ++ __ lb(AT, end_from, 0); ++ __ sb(AT, end_to, 0); ++ __ addiu(end_from, end_from, -1); ++ __ addiu(end_to, end_to, -1); ++ __ addiu(T8, T8, -1); ++ __ bne(T8, R0, l_copy_suffix_loop); ++ __ delayed()->nop(); ++ ++ __ bind(l_copy_byte); ++ __ beq(end_count, R0, l_exit); ++ __ delayed()->nop(); ++ __ lb(AT, end_from, -1); ++ __ sb(AT, end_to, -1); ++ __ daddiu(end_from, end_from, -1); ++ __ daddiu(end_to, end_to, -1); ++ __ daddiu(end_count, end_count, -1); ++ __ b(l_copy_byte); ++ __ delayed()->nop(); ++ ++ __ bind(l_exit); ++ __ pop(T8); ++ __ pop(end_count); ++ __ pop(end_to); ++ __ pop(end_from); ++ __ jr(RA); ++ __ delayed()->nop(); ++ return start; ++ } ++ ++ // Generate stub for disjoint short copy. If "aligned" is true, the ++ // "from" and "to" addresses are assumed to be heapword aligned. ++ // ++ // Arguments for generated stub: ++ // from: A0 ++ // to: A1 ++ // elm.count: A2 treated as signed ++ // one element: 2 bytes ++ // ++ // Strategy for aligned==true: ++ // ++ // If length <= 9: ++ // 1. copy 1 elements at a time (l_5) ++ // ++ // If length > 9: ++ // 1. copy 4 elements at a time until less than 4 elements are left (l_7) ++ // 2. copy 2 elements at a time until less than 2 elements are left (l_6) ++ // 3. copy last element if one was left in step 2. (l_1) ++ // ++ // ++ // Strategy for aligned==false: ++ // ++ // If length <= 9: same as aligned==true case ++ // ++ // If length > 9: ++ // 1. continue with step 7. if the alignment of from and to mod 4 ++ // is different. ++ // 2. align from and to to 4 bytes by copying 1 element if necessary ++ // 3. at l_2 from and to are 4 byte aligned; continue with ++ // 6. if they cannot be aligned to 8 bytes because they have ++ // got different alignment mod 8. ++ // 4. at this point we know that both, from and to, have the same ++ // alignment mod 8, now copy one element if necessary to get ++ // 8 byte alignment of from and to. ++ // 5. copy 4 elements at a time until less than 4 elements are ++ // left; depending on step 3. all load/stores are aligned. ++ // 6. copy 2 elements at a time until less than 2 elements are ++ // left. (l_6) ++ // 7. copy 1 element at a time. (l_5) ++ // 8. copy last element if one was left in step 6. (l_1) ++ ++ address generate_disjoint_short_copy(bool aligned, const char * name) { ++ StubCodeMark mark(this, "StubRoutines", name); ++ __ align(CodeEntryAlignment); ++ ++ Register tmp1 = T0; ++ Register tmp2 = T1; ++ Register tmp3 = T3; ++ Register tmp4 = T8; ++ Register tmp5 = T9; ++ Register tmp6 = T2; ++ ++ address start = __ pc(); ++ ++ __ push(tmp1); ++ __ push(tmp2); ++ __ push(tmp3); ++ __ move(tmp1, A0); ++ __ move(tmp2, A1); ++ __ move(tmp3, A2); ++ ++ Label l_1, l_2, l_3, l_4, l_5, l_6, l_7, l_8, l_9, l_10, l_11, l_12, l_13, l_14; ++ Label l_debug; ++ // don't try anything fancy if arrays don't have many elements ++ __ daddiu(AT, tmp3, -23); ++ __ blez(AT, l_14); ++ __ delayed()->nop(); ++ // move push here ++ __ push(tmp4); ++ __ push(tmp5); ++ __ push(tmp6); ++ ++ if (!aligned) { ++ __ xorr(AT, A0, A1); ++ __ andi(AT, AT, 1); ++ __ bne(AT, R0, l_debug); // if arrays don't have the same alignment mod 2, can this happen? ++ __ delayed()->nop(); ++ ++ __ xorr(AT, A0, A1); ++ __ andi(AT, AT, 3); ++ __ bne(AT, R0, l_1); // if arrays don't have the same alignment mod 4, do 1 element copy ++ __ delayed()->nop(); ++ ++ // At this point it is guaranteed that both, from and to have the same alignment mod 4. ++ ++ // Copy 1 element if necessary to align to 4 bytes. ++ __ andi(AT, A0, 3); ++ __ beq(AT, R0, l_2); ++ __ delayed()->nop(); ++ ++ __ lhu(AT, tmp1, 0); ++ __ daddiu(tmp1, tmp1, 2); ++ __ sh(AT, tmp2, 0); ++ __ daddiu(tmp2, tmp2, 2); ++ __ daddiu(tmp3, tmp3, -1); ++ __ bind(l_2); ++ ++ // At this point the positions of both, from and to, are at least 4 byte aligned. ++ ++ // Copy 4 elements at a time. ++ // Align to 8 bytes, but only if both, from and to, have same alignment mod 8. ++ __ xorr(AT, tmp1, tmp2); ++ __ andi(AT, AT, 7); ++ __ bne(AT, R0, l_6); // not same alignment mod 8 -> copy 2, either from or to will be unaligned ++ __ delayed()->nop(); ++ ++ // Copy a 2-element word if necessary to align to 8 bytes. ++ __ andi(AT, tmp1, 7); ++ __ beq(AT, R0, l_7); ++ __ delayed()->nop(); ++ ++ __ lw(AT, tmp1, 0); ++ __ daddiu(tmp3, tmp3, -2); ++ __ sw(AT, tmp2, 0); ++ __ daddiu(tmp1, tmp1, 4); ++ __ daddiu(tmp2, tmp2, 4); ++ }// end of if (!aligned) ++ ++ __ bind(l_7); ++ // At this time the position of both, from and to, are at least 8 byte aligned. ++ // Copy 8 elemnets at a time. ++ // Align to 16 bytes, but only if both from and to have same alignment mod 8. ++ __ xorr(AT, tmp1, tmp2); ++ __ andi(AT, AT, 15); ++ __ bne(AT, R0, l_9); ++ __ delayed()->nop(); ++ ++ // Copy 4-element word if necessary to align to 16 bytes, ++ __ andi(AT, tmp1, 15); ++ __ beq(AT, R0, l_10); ++ __ delayed()->nop(); ++ ++ __ ld(AT, tmp1, 0); ++ __ daddiu(tmp3, tmp3, -4); ++ __ sd(AT, tmp2, 0); ++ __ daddiu(tmp1, tmp1, 8); ++ __ daddiu(tmp2, tmp2, 8); ++ ++ __ bind(l_10); ++ ++ // Copy 8 elements at a time; either the loads or the stores can ++ // be unalligned if aligned == false ++ ++ { // FasterArrayCopy ++ __ bind(l_11); ++ // For loongson the 128-bit memory access instruction is gslq/gssq ++ if (UseLEXT1) { ++ __ gslq(AT, tmp4, tmp1, 0); ++ __ gslq(tmp5, tmp6, tmp1, 16); ++ __ daddiu(tmp1, tmp1, 32); ++ __ daddiu(tmp2, tmp2, 32); ++ __ gssq(AT, tmp4, tmp2, -32); ++ __ gssq(tmp5, tmp6, tmp2, -16); ++ } else { ++ __ ld(AT, tmp1, 0); ++ __ ld(tmp4, tmp1, 8); ++ __ ld(tmp5, tmp1, 16); ++ __ ld(tmp6, tmp1, 24); ++ __ daddiu(tmp1, tmp1, 32); ++ __ sd(AT, tmp2, 0); ++ __ sd(tmp4, tmp2, 8); ++ __ sd(tmp5, tmp2, 16); ++ __ sd(tmp6, tmp2, 24); ++ __ daddiu(tmp2, tmp2, 32); ++ } ++ __ daddiu(tmp3, tmp3, -16); ++ __ daddiu(AT, tmp3, -16); ++ __ bgez(AT, l_11); ++ __ delayed()->nop(); ++ } ++ __ bind(l_9); ++ ++ // Copy 4 elements at a time; either the loads or the stores can ++ // be unaligned if aligned == false. ++ { // FasterArrayCopy ++ __ daddiu(AT, tmp3, -15);// loop unrolling 4 times, so if the elements should not be less than 16 ++ __ blez(AT, l_4); // copy 2 at a time if less than 16 elements remain ++ __ delayed()->nop(); ++ ++ __ bind(l_8); ++ __ ld(AT, tmp1, 0); ++ __ ld(tmp4, tmp1, 8); ++ __ ld(tmp5, tmp1, 16); ++ __ ld(tmp6, tmp1, 24); ++ __ sd(AT, tmp2, 0); ++ __ sd(tmp4, tmp2, 8); ++ __ sd(tmp5, tmp2,16); ++ __ daddiu(tmp1, tmp1, 32); ++ __ daddiu(tmp2, tmp2, 32); ++ __ daddiu(tmp3, tmp3, -16); ++ __ daddiu(AT, tmp3, -16); ++ __ bgez(AT, l_8); ++ __ delayed()->sd(tmp6, tmp2, -8); ++ } ++ __ bind(l_6); ++ ++ // copy 2 element at a time ++ { // FasterArrayCopy ++ __ daddiu(AT, tmp3, -7); ++ __ blez(AT, l_4); ++ __ delayed()->nop(); ++ ++ __ bind(l_3); ++ __ lw(AT, tmp1, 0); ++ __ lw(tmp4, tmp1, 4); ++ __ lw(tmp5, tmp1, 8); ++ __ lw(tmp6, tmp1, 12); ++ __ sw(AT, tmp2, 0); ++ __ sw(tmp4, tmp2, 4); ++ __ sw(tmp5, tmp2, 8); ++ __ daddiu(tmp1, tmp1, 16); ++ __ daddiu(tmp2, tmp2, 16); ++ __ daddiu(tmp3, tmp3, -8); ++ __ daddiu(AT, tmp3, -8); ++ __ bgez(AT, l_3); ++ __ delayed()->sw(tmp6, tmp2, -4); ++ } ++ ++ __ bind(l_1); ++ // do single element copy (8 bit), can this happen? ++ { // FasterArrayCopy ++ __ daddiu(AT, tmp3, -3); ++ __ blez(AT, l_4); ++ __ delayed()->nop(); ++ ++ __ bind(l_5); ++ __ lhu(AT, tmp1, 0); ++ __ lhu(tmp4, tmp1, 2); ++ __ lhu(tmp5, tmp1, 4); ++ __ lhu(tmp6, tmp1, 6); ++ __ sh(AT, tmp2, 0); ++ __ sh(tmp4, tmp2, 2); ++ __ sh(tmp5, tmp2, 4); ++ __ daddiu(tmp1, tmp1, 8); ++ __ daddiu(tmp2, tmp2, 8); ++ __ daddiu(tmp3, tmp3, -4); ++ __ daddiu(AT, tmp3, -4); ++ __ bgez(AT, l_5); ++ __ delayed()->sh(tmp6, tmp2, -2); ++ } ++ // single element ++ __ bind(l_4); ++ ++ __ pop(tmp6); ++ __ pop(tmp5); ++ __ pop(tmp4); ++ ++ __ bind(l_14); ++ { // FasterArrayCopy ++ __ beq(R0, tmp3, l_13); ++ __ delayed()->nop(); ++ ++ __ bind(l_12); ++ __ lhu(AT, tmp1, 0); ++ __ sh(AT, tmp2, 0); ++ __ daddiu(tmp1, tmp1, 2); ++ __ daddiu(tmp2, tmp2, 2); ++ __ daddiu(tmp3, tmp3, -1); ++ __ daddiu(AT, tmp3, -1); ++ __ bgez(AT, l_12); ++ __ delayed()->nop(); ++ } ++ ++ __ bind(l_13); ++ __ pop(tmp3); ++ __ pop(tmp2); ++ __ pop(tmp1); ++ ++ __ jr(RA); ++ __ delayed()->nop(); ++ ++ __ bind(l_debug); ++ __ stop("generate_disjoint_short_copy should not reach here"); ++ return start; ++ } ++ ++ // Arguments: ++ // aligned - true => Input and output aligned on a HeapWord == 8-byte boundary ++ // ignored ++ // name - stub name string ++ // ++ // Inputs: ++ // c_rarg0 - source array address ++ // c_rarg1 - destination array address ++ // c_rarg2 - element count, treated as ssize_t, can be zero ++ // ++ // If 'from' and/or 'to' are aligned on 4- or 2-byte boundaries, we ++ // let the hardware handle it. The two or four words within dwords ++ // or qwords that span cache line boundaries will still be loaded ++ // and stored atomically. ++ // ++ address generate_conjoint_short_copy(bool aligned, const char *name) { ++ StubCodeMark mark(this, "StubRoutines", name); ++ __ align(CodeEntryAlignment); ++ address start = __ pc(); ++ ++ Label l_exit, l_copy_short, l_from_unaligned, l_unaligned, l_4_bytes_aligned; ++ ++ address nooverlap_target = aligned ? ++ StubRoutines::arrayof_jshort_disjoint_arraycopy() : ++ StubRoutines::jshort_disjoint_arraycopy(); ++ ++ array_overlap_test(nooverlap_target, 1); ++ ++ const Register from = A0; // source array address ++ const Register to = A1; // destination array address ++ const Register count = A2; // elements count ++ const Register end_from = T3; // source array end address ++ const Register end_to = T0; // destination array end address ++ const Register end_count = T1; // destination array end address ++ ++ __ push(end_from); ++ __ push(end_to); ++ __ push(end_count); ++ __ push(T8); ++ ++ // copy from high to low ++ __ move(end_count, count); ++ __ sll(AT, end_count, Address::times_2); ++ __ daddu(end_from, from, AT); ++ __ daddu(end_to, to, AT); ++ ++ // If end_from and end_to has differante alignment, unaligned copy is performed. ++ __ andi(AT, end_from, 3); ++ __ andi(T8, end_to, 3); ++ __ bne(AT, T8, l_copy_short); ++ __ delayed()->nop(); ++ ++ // First deal with the unaligned data at the top. ++ __ bind(l_unaligned); ++ __ beq(end_count, R0, l_exit); ++ __ delayed()->nop(); ++ ++ __ andi(AT, end_from, 3); ++ __ bne(AT, R0, l_from_unaligned); ++ __ delayed()->nop(); ++ ++ __ andi(AT, end_to, 3); ++ __ beq(AT, R0, l_4_bytes_aligned); ++ __ delayed()->nop(); ++ ++ // Copy 1 element if necessary to align to 4 bytes. ++ __ bind(l_from_unaligned); ++ __ lhu(AT, end_from, -2); ++ __ sh(AT, end_to, -2); ++ __ daddiu(end_from, end_from, -2); ++ __ daddiu(end_to, end_to, -2); ++ __ daddiu(end_count, end_count, -1); ++ __ b(l_unaligned); ++ __ delayed()->nop(); ++ ++ // now end_to, end_from point to 4-byte aligned high-ends ++ // end_count contains byte count that is not copied. ++ // copy 4 bytes at a time ++ __ bind(l_4_bytes_aligned); ++ ++ __ daddiu(AT, end_count, -1); ++ __ blez(AT, l_copy_short); ++ __ delayed()->nop(); ++ ++ __ lw(AT, end_from, -4); ++ __ sw(AT, end_to, -4); ++ __ addiu(end_from, end_from, -4); ++ __ addiu(end_to, end_to, -4); ++ __ addiu(end_count, end_count, -2); ++ __ b(l_4_bytes_aligned); ++ __ delayed()->nop(); ++ ++ // copy 1 element at a time ++ __ bind(l_copy_short); ++ __ beq(end_count, R0, l_exit); ++ __ delayed()->nop(); ++ __ lhu(AT, end_from, -2); ++ __ sh(AT, end_to, -2); ++ __ daddiu(end_from, end_from, -2); ++ __ daddiu(end_to, end_to, -2); ++ __ daddiu(end_count, end_count, -1); ++ __ b(l_copy_short); ++ __ delayed()->nop(); ++ ++ __ bind(l_exit); ++ __ pop(T8); ++ __ pop(end_count); ++ __ pop(end_to); ++ __ pop(end_from); ++ __ jr(RA); ++ __ delayed()->nop(); ++ return start; ++ } ++ ++ // Arguments: ++ // aligned - true => Input and output aligned on a HeapWord == 8-byte boundary ++ // ignored ++ // is_oop - true => oop array, so generate store check code ++ // name - stub name string ++ // ++ // Inputs: ++ // c_rarg0 - source array address ++ // c_rarg1 - destination array address ++ // c_rarg2 - element count, treated as ssize_t, can be zero ++ // ++ // If 'from' and/or 'to' are aligned on 4-byte boundaries, we let ++ // the hardware handle it. The two dwords within qwords that span ++ // cache line boundaries will still be loaded and stored atomicly. ++ // ++ // Side Effects: ++ // disjoint_int_copy_entry is set to the no-overlap entry point ++ // used by generate_conjoint_int_oop_copy(). ++ // ++ address generate_disjoint_int_oop_copy(bool aligned, bool is_oop, const char *name, bool dest_uninitialized = false) { ++ Label l_3, l_4, l_5, l_6, l_7; ++ StubCodeMark mark(this, "StubRoutines", name); ++ ++ __ align(CodeEntryAlignment); ++ address start = __ pc(); ++ __ push(T3); ++ __ push(T0); ++ __ push(T1); ++ __ push(T8); ++ __ push(T9); ++ __ move(T1, A2); ++ __ move(T3, A0); ++ __ move(T0, A1); ++ ++ if (is_oop) { ++ gen_write_ref_array_pre_barrier(A1, A2, dest_uninitialized); ++ } ++ ++ if(!aligned) { ++ __ xorr(AT, T3, T0); ++ __ andi(AT, AT, 7); ++ __ bne(AT, R0, l_5); // not same alignment mod 8 -> copy 1 element each time ++ __ delayed()->nop(); ++ ++ __ andi(AT, T3, 7); ++ __ beq(AT, R0, l_6); //copy 2 elements each time ++ __ delayed()->nop(); ++ ++ __ lw(AT, T3, 0); ++ __ daddiu(T1, T1, -1); ++ __ sw(AT, T0, 0); ++ __ daddiu(T3, T3, 4); ++ __ daddiu(T0, T0, 4); ++ } ++ ++ { ++ __ bind(l_6); ++ __ daddiu(AT, T1, -1); ++ __ blez(AT, l_5); ++ __ delayed()->nop(); ++ ++ __ bind(l_7); ++ __ ld(AT, T3, 0); ++ __ sd(AT, T0, 0); ++ __ daddiu(T3, T3, 8); ++ __ daddiu(T0, T0, 8); ++ __ daddiu(T1, T1, -2); ++ __ daddiu(AT, T1, -2); ++ __ bgez(AT, l_7); ++ __ delayed()->nop(); ++ } ++ ++ __ bind(l_5); ++ __ beq(T1, R0, l_4); ++ __ delayed()->nop(); ++ ++ __ align(16); ++ __ bind(l_3); ++ __ lw(AT, T3, 0); ++ __ sw(AT, T0, 0); ++ __ addiu(T3, T3, 4); ++ __ addiu(T0, T0, 4); ++ __ addiu(T1, T1, -1); ++ __ bne(T1, R0, l_3); ++ __ delayed()->nop(); ++ ++ // exit ++ __ bind(l_4); ++ if (is_oop) { ++ gen_write_ref_array_post_barrier(A1, A2, T1); ++ } ++ __ pop(T9); ++ __ pop(T8); ++ __ pop(T1); ++ __ pop(T0); ++ __ pop(T3); ++ __ jr(RA); ++ __ delayed()->nop(); ++ ++ return start; ++ } ++ ++ // Arguments: ++ // aligned - true => Input and output aligned on a HeapWord == 8-byte boundary ++ // ignored ++ // is_oop - true => oop array, so generate store check code ++ // name - stub name string ++ // ++ // Inputs: ++ // c_rarg0 - source array address ++ // c_rarg1 - destination array address ++ // c_rarg2 - element count, treated as ssize_t, can be zero ++ // ++ // If 'from' and/or 'to' are aligned on 4-byte boundaries, we let ++ // the hardware handle it. The two dwords within qwords that span ++ // cache line boundaries will still be loaded and stored atomicly. ++ // ++ address generate_conjoint_int_oop_copy(bool aligned, bool is_oop, const char *name, bool dest_uninitialized = false) { ++ Label l_2, l_4; ++ StubCodeMark mark(this, "StubRoutines", name); ++ __ align(CodeEntryAlignment); ++ address start = __ pc(); ++ address nooverlap_target; ++ ++ if (is_oop) { ++ nooverlap_target = aligned ? ++ StubRoutines::arrayof_oop_disjoint_arraycopy() : ++ StubRoutines::oop_disjoint_arraycopy(); ++ } else { ++ nooverlap_target = aligned ? ++ StubRoutines::arrayof_jint_disjoint_arraycopy() : ++ StubRoutines::jint_disjoint_arraycopy(); ++ } ++ ++ array_overlap_test(nooverlap_target, 2); ++ ++ if (is_oop) { ++ gen_write_ref_array_pre_barrier(A1, A2, dest_uninitialized); ++ } ++ ++ __ push(T3); ++ __ push(T0); ++ __ push(T1); ++ __ push(T8); ++ __ push(T9); ++ ++ __ move(T1, A2); ++ __ move(T3, A0); ++ __ move(T0, A1); ++ ++ // T3: source array address ++ // T0: destination array address ++ // T1: element count ++ ++ __ sll(AT, T1, Address::times_4); ++ __ addu(AT, T3, AT); ++ __ daddiu(T3, AT, -4); ++ __ sll(AT, T1, Address::times_4); ++ __ addu(AT, T0, AT); ++ __ daddiu(T0, AT, -4); ++ ++ __ beq(T1, R0, l_4); ++ __ delayed()->nop(); ++ ++ __ align(16); ++ __ bind(l_2); ++ __ lw(AT, T3, 0); ++ __ sw(AT, T0, 0); ++ __ addiu(T3, T3, -4); ++ __ addiu(T0, T0, -4); ++ __ addiu(T1, T1, -1); ++ __ bne(T1, R0, l_2); ++ __ delayed()->nop(); ++ ++ __ bind(l_4); ++ if (is_oop) { ++ gen_write_ref_array_post_barrier(A1, A2, T1); ++ } ++ __ pop(T9); ++ __ pop(T8); ++ __ pop(T1); ++ __ pop(T0); ++ __ pop(T3); ++ __ jr(RA); ++ __ delayed()->nop(); ++ ++ return start; ++ } ++ ++ // Arguments: ++ // aligned - true => Input and output aligned on a HeapWord == 8-byte boundary ++ // ignored ++ // is_oop - true => oop array, so generate store check code ++ // name - stub name string ++ // ++ // Inputs: ++ // c_rarg0 - source array address ++ // c_rarg1 - destination array address ++ // c_rarg2 - element count, treated as ssize_t, can be zero ++ // ++ // If 'from' and/or 'to' are aligned on 4-byte boundaries, we let ++ // the hardware handle it. The two dwords within qwords that span ++ // cache line boundaries will still be loaded and stored atomicly. ++ // ++ // Side Effects: ++ // disjoint_int_copy_entry is set to the no-overlap entry point ++ // used by generate_conjoint_int_oop_copy(). ++ // ++ address generate_disjoint_long_oop_copy(bool aligned, bool is_oop, const char *name, bool dest_uninitialized = false) { ++ Label l_3, l_4; ++ StubCodeMark mark(this, "StubRoutines", name); ++ __ align(CodeEntryAlignment); ++ address start = __ pc(); ++ ++ if (is_oop) { ++ gen_write_ref_array_pre_barrier(A1, A2, dest_uninitialized); ++ } ++ ++ __ push(T3); ++ __ push(T0); ++ __ push(T1); ++ __ push(T8); ++ __ push(T9); ++ ++ __ move(T1, A2); ++ __ move(T3, A0); ++ __ move(T0, A1); ++ ++ // T3: source array address ++ // T0: destination array address ++ // T1: element count ++ ++ __ beq(T1, R0, l_4); ++ __ delayed()->nop(); ++ ++ __ align(16); ++ __ bind(l_3); ++ __ ld(AT, T3, 0); ++ __ sd(AT, T0, 0); ++ __ addiu(T3, T3, 8); ++ __ addiu(T0, T0, 8); ++ __ addiu(T1, T1, -1); ++ __ bne(T1, R0, l_3); ++ __ delayed()->nop(); ++ ++ // exit ++ __ bind(l_4); ++ if (is_oop) { ++ gen_write_ref_array_post_barrier(A1, A2, T1); ++ } ++ __ pop(T9); ++ __ pop(T8); ++ __ pop(T1); ++ __ pop(T0); ++ __ pop(T3); ++ __ jr(RA); ++ __ delayed()->nop(); ++ return start; ++ } ++ ++ // Arguments: ++ // aligned - true => Input and output aligned on a HeapWord == 8-byte boundary ++ // ignored ++ // is_oop - true => oop array, so generate store check code ++ // name - stub name string ++ // ++ // Inputs: ++ // c_rarg0 - source array address ++ // c_rarg1 - destination array address ++ // c_rarg2 - element count, treated as ssize_t, can be zero ++ // ++ // If 'from' and/or 'to' are aligned on 4-byte boundaries, we let ++ // the hardware handle it. The two dwords within qwords that span ++ // cache line boundaries will still be loaded and stored atomicly. ++ // ++ address generate_conjoint_long_oop_copy(bool aligned, bool is_oop, const char *name, bool dest_uninitialized = false) { ++ Label l_2, l_4; ++ StubCodeMark mark(this, "StubRoutines", name); ++ __ align(CodeEntryAlignment); ++ address start = __ pc(); ++ address nooverlap_target; ++ ++ if (is_oop) { ++ nooverlap_target = aligned ? ++ StubRoutines::arrayof_oop_disjoint_arraycopy() : ++ StubRoutines::oop_disjoint_arraycopy(); ++ } else { ++ nooverlap_target = aligned ? ++ StubRoutines::arrayof_jlong_disjoint_arraycopy() : ++ StubRoutines::jlong_disjoint_arraycopy(); ++ } ++ ++ array_overlap_test(nooverlap_target, 3); ++ ++ if (is_oop) { ++ gen_write_ref_array_pre_barrier(A1, A2, dest_uninitialized); ++ } ++ ++ __ push(T3); ++ __ push(T0); ++ __ push(T1); ++ __ push(T8); ++ __ push(T9); ++ ++ __ move(T1, A2); ++ __ move(T3, A0); ++ __ move(T0, A1); ++ ++ __ sll(AT, T1, Address::times_8); ++ __ addu(AT, T3, AT); ++ __ daddiu(T3, AT, -8); ++ __ sll(AT, T1, Address::times_8); ++ __ addu(AT, T0, AT); ++ __ daddiu(T0, AT, -8); ++ ++ __ beq(T1, R0, l_4); ++ __ delayed()->nop(); ++ ++ __ align(16); ++ __ bind(l_2); ++ __ ld(AT, T3, 0); ++ __ sd(AT, T0, 0); ++ __ addiu(T3, T3, -8); ++ __ addiu(T0, T0, -8); ++ __ addiu(T1, T1, -1); ++ __ bne(T1, R0, l_2); ++ __ delayed()->nop(); ++ ++ // exit ++ __ bind(l_4); ++ if (is_oop) { ++ gen_write_ref_array_post_barrier(A1, A2, T1); ++ } ++ __ pop(T9); ++ __ pop(T8); ++ __ pop(T1); ++ __ pop(T0); ++ __ pop(T3); ++ __ jr(RA); ++ __ delayed()->nop(); ++ return start; ++ } ++ ++ //FIXME ++ address generate_disjoint_long_copy(bool aligned, const char *name) { ++ Label l_1, l_2; ++ StubCodeMark mark(this, "StubRoutines", name); ++ __ align(CodeEntryAlignment); ++ address start = __ pc(); ++ ++ __ move(T1, A2); ++ __ move(T3, A0); ++ __ move(T0, A1); ++ __ push(T3); ++ __ push(T0); ++ __ push(T1); ++ __ b(l_2); ++ __ delayed()->nop(); ++ __ align(16); ++ __ bind(l_1); ++ __ ld(AT, T3, 0); ++ __ sd (AT, T0, 0); ++ __ addiu(T3, T3, 8); ++ __ addiu(T0, T0, 8); ++ __ bind(l_2); ++ __ addiu(T1, T1, -1); ++ __ bgez(T1, l_1); ++ __ delayed()->nop(); ++ __ pop(T1); ++ __ pop(T0); ++ __ pop(T3); ++ __ jr(RA); ++ __ delayed()->nop(); ++ return start; ++ } ++ ++ ++ address generate_conjoint_long_copy(bool aligned, const char *name) { ++ Label l_1, l_2; ++ StubCodeMark mark(this, "StubRoutines", name); ++ __ align(CodeEntryAlignment); ++ address start = __ pc(); ++ address nooverlap_target = aligned ? ++ StubRoutines::arrayof_jlong_disjoint_arraycopy() : ++ StubRoutines::jlong_disjoint_arraycopy(); ++ array_overlap_test(nooverlap_target, 3); ++ ++ __ push(T3); ++ __ push(T0); ++ __ push(T1); ++ ++ __ move(T1, A2); ++ __ move(T3, A0); ++ __ move(T0, A1); ++ __ sll(AT, T1, Address::times_8); ++ __ addu(AT, T3, AT); ++ __ daddiu(T3, AT, -8); ++ __ sll(AT, T1, Address::times_8); ++ __ addu(AT, T0, AT); ++ __ daddiu(T0, AT, -8); ++ ++ __ b(l_2); ++ __ delayed()->nop(); ++ __ align(16); ++ __ bind(l_1); ++ __ ld(AT, T3, 0); ++ __ sd (AT, T0, 0); ++ __ addiu(T3, T3, -8); ++ __ addiu(T0, T0,-8); ++ __ bind(l_2); ++ __ addiu(T1, T1, -1); ++ __ bgez(T1, l_1); ++ __ delayed()->nop(); ++ __ pop(T1); ++ __ pop(T0); ++ __ pop(T3); ++ __ jr(RA); ++ __ delayed()->nop(); ++ return start; ++ } ++ ++ void generate_arraycopy_stubs() { ++ if (UseCompressedOops) { ++ StubRoutines::_oop_disjoint_arraycopy = generate_disjoint_int_oop_copy(false, true, ++ "oop_disjoint_arraycopy"); ++ StubRoutines::_oop_arraycopy = generate_conjoint_int_oop_copy(false, true, ++ "oop_arraycopy"); ++ StubRoutines::_oop_disjoint_arraycopy_uninit = generate_disjoint_int_oop_copy(false, true, ++ "oop_disjoint_arraycopy_uninit", true); ++ StubRoutines::_oop_arraycopy_uninit = generate_conjoint_int_oop_copy(false, true, ++ "oop_arraycopy_uninit", true); ++ } else { ++ StubRoutines::_oop_disjoint_arraycopy = generate_disjoint_long_oop_copy(false, true, ++ "oop_disjoint_arraycopy"); ++ StubRoutines::_oop_arraycopy = generate_conjoint_long_oop_copy(false, true, ++ "oop_arraycopy"); ++ StubRoutines::_oop_disjoint_arraycopy_uninit = generate_disjoint_long_oop_copy(false, true, ++ "oop_disjoint_arraycopy_uninit", true); ++ StubRoutines::_oop_arraycopy_uninit = generate_conjoint_long_oop_copy(false, true, ++ "oop_arraycopy_uninit", true); ++ } ++ ++ StubRoutines::_jbyte_disjoint_arraycopy = generate_disjoint_byte_copy(false, "jbyte_disjoint_arraycopy"); ++ StubRoutines::_jshort_disjoint_arraycopy = generate_disjoint_short_copy(false, "jshort_disjoint_arraycopy"); ++ StubRoutines::_jint_disjoint_arraycopy = generate_disjoint_int_oop_copy(false, false, "jint_disjoint_arraycopy"); ++ StubRoutines::_jlong_disjoint_arraycopy = generate_disjoint_long_copy(false, "jlong_disjoint_arraycopy"); ++ ++ StubRoutines::_jbyte_arraycopy = generate_conjoint_byte_copy(false, "jbyte_arraycopy"); ++ StubRoutines::_jshort_arraycopy = generate_conjoint_short_copy(false, "jshort_arraycopy"); ++ StubRoutines::_jint_arraycopy = generate_conjoint_int_oop_copy(false, false, "jint_arraycopy"); ++ StubRoutines::_jlong_arraycopy = generate_conjoint_long_copy(false, "jlong_arraycopy"); ++ ++ // We don't generate specialized code for HeapWord-aligned source ++ // arrays, so just use the code we've already generated ++ StubRoutines::_arrayof_jbyte_disjoint_arraycopy = StubRoutines::_jbyte_disjoint_arraycopy; ++ StubRoutines::_arrayof_jbyte_arraycopy = StubRoutines::_jbyte_arraycopy; ++ ++ StubRoutines::_arrayof_jshort_disjoint_arraycopy = StubRoutines::_jshort_disjoint_arraycopy; ++ StubRoutines::_arrayof_jshort_arraycopy = StubRoutines::_jshort_arraycopy; ++ ++ StubRoutines::_arrayof_jint_disjoint_arraycopy = StubRoutines::_jint_disjoint_arraycopy; ++ StubRoutines::_arrayof_jint_arraycopy = StubRoutines::_jint_arraycopy; ++ ++ StubRoutines::_arrayof_jlong_disjoint_arraycopy = StubRoutines::_jlong_disjoint_arraycopy; ++ StubRoutines::_arrayof_jlong_arraycopy = StubRoutines::_jlong_arraycopy; ++ ++ StubRoutines::_arrayof_oop_disjoint_arraycopy = StubRoutines::_oop_disjoint_arraycopy; ++ StubRoutines::_arrayof_oop_arraycopy = StubRoutines::_oop_arraycopy; ++ ++ StubRoutines::_arrayof_oop_disjoint_arraycopy_uninit = StubRoutines::_oop_disjoint_arraycopy_uninit; ++ StubRoutines::_arrayof_oop_arraycopy_uninit = StubRoutines::_oop_arraycopy_uninit; ++ } ++ ++ // add a function to implement SafeFetch32 and SafeFetchN ++ void generate_safefetch(const char* name, int size, address* entry, ++ address* fault_pc, address* continuation_pc) { ++ // safefetch signatures: ++ // int SafeFetch32(int* adr, int errValue); ++ // intptr_t SafeFetchN (intptr_t* adr, intptr_t errValue); ++ // ++ // arguments: ++ // A0 = adr ++ // A1 = errValue ++ // ++ // result: ++ // PPC_RET = *adr or errValue ++ ++ StubCodeMark mark(this, "StubRoutines", name); ++ ++ // Entry point, pc or function descriptor. ++ *entry = __ pc(); ++ ++ // Load *adr into A1, may fault. ++ *fault_pc = __ pc(); ++ switch (size) { ++ case 4: ++ // int32_t ++ __ lw(A1, A0, 0); ++ break; ++ case 8: ++ // int64_t ++ __ ld(A1, A0, 0); ++ break; ++ default: ++ ShouldNotReachHere(); ++ } ++ ++ // return errValue or *adr ++ *continuation_pc = __ pc(); ++ __ addu(V0,A1,R0); ++ __ jr(RA); ++ __ delayed()->nop(); ++ } ++ ++ ++#undef __ ++#define __ masm-> ++ ++ // Continuation point for throwing of implicit exceptions that are ++ // not handled in the current activation. Fabricates an exception ++ // oop and initiates normal exception dispatching in this ++ // frame. Since we need to preserve callee-saved values (currently ++ // only for C2, but done for C1 as well) we need a callee-saved oop ++ // map and therefore have to make these stubs into RuntimeStubs ++ // rather than BufferBlobs. If the compiler needs all registers to ++ // be preserved between the fault point and the exception handler ++ // then it must assume responsibility for that in ++ // AbstractCompiler::continuation_for_implicit_null_exception or ++ // continuation_for_implicit_division_by_zero_exception. All other ++ // implicit exceptions (e.g., NullPointerException or ++ // AbstractMethodError on entry) are either at call sites or ++ // otherwise assume that stack unwinding will be initiated, so ++ // caller saved registers were assumed volatile in the compiler. ++ address generate_throw_exception(const char* name, ++ address runtime_entry, ++ bool restore_saved_exception_pc) { ++ // Information about frame layout at time of blocking runtime call. ++ // Note that we only have to preserve callee-saved registers since ++ // the compilers are responsible for supplying a continuation point ++ // if they expect all registers to be preserved. ++ enum layout { ++ thread_off, // last_java_sp ++ S7_off, // callee saved register sp + 1 ++ S6_off, // callee saved register sp + 2 ++ S5_off, // callee saved register sp + 3 ++ S4_off, // callee saved register sp + 4 ++ S3_off, // callee saved register sp + 5 ++ S2_off, // callee saved register sp + 6 ++ S1_off, // callee saved register sp + 7 ++ S0_off, // callee saved register sp + 8 ++ FP_off, ++ ret_address, ++ framesize ++ }; ++ ++ int insts_size = 2048; ++ int locs_size = 32; ++ ++ // CodeBuffer* code = new CodeBuffer(insts_size, locs_size, 0, 0, 0, false, ++ // NULL, NULL, NULL, false, NULL, name, false); ++ CodeBuffer code (name , insts_size, locs_size); ++ OopMapSet* oop_maps = new OopMapSet(); ++ MacroAssembler* masm = new MacroAssembler(&code); ++ ++ address start = __ pc(); ++ ++ // This is an inlined and slightly modified version of call_VM ++ // which has the ability to fetch the return PC out of ++ // thread-local storage and also sets up last_Java_sp slightly ++ // differently than the real call_VM ++#ifndef OPT_THREAD ++ Register java_thread = TREG; ++ __ get_thread(java_thread); ++#else ++ Register java_thread = TREG; ++#endif ++ if (restore_saved_exception_pc) { ++ __ ld(RA, java_thread, in_bytes(JavaThread::saved_exception_pc_offset())); ++ } ++ ++ __ enter(); // required for proper stackwalking of RuntimeStub frame ++ ++ __ addiu(SP, SP, (-1) * (framesize-2) * wordSize); // prolog ++ __ sd(S0, SP, S0_off * wordSize); ++ __ sd(S1, SP, S1_off * wordSize); ++ __ sd(S2, SP, S2_off * wordSize); ++ __ sd(S3, SP, S3_off * wordSize); ++ __ sd(S4, SP, S4_off * wordSize); ++ __ sd(S5, SP, S5_off * wordSize); ++ __ sd(S6, SP, S6_off * wordSize); ++ __ sd(S7, SP, S7_off * wordSize); ++ ++ int frame_complete = __ pc() - start; ++ // push java thread (becomes first argument of C function) ++ __ sd(java_thread, SP, thread_off * wordSize); ++ if (java_thread != A0) ++ __ move(A0, java_thread); ++ ++ // Set up last_Java_sp and last_Java_fp ++ __ set_last_Java_frame(java_thread, SP, FP, NULL); ++ // Align stack ++ __ set64(AT, -(StackAlignmentInBytes)); ++ __ andr(SP, SP, AT); ++ ++ __ relocate(relocInfo::internal_pc_type); ++ { ++ intptr_t save_pc = (intptr_t)__ pc() + NativeMovConstReg::instruction_size + 28; ++ __ patchable_set48(AT, save_pc); ++ } ++ __ sd(AT, java_thread, in_bytes(JavaThread::last_Java_pc_offset())); ++ ++ // Call runtime ++ __ call(runtime_entry); ++ __ delayed()->nop(); ++ // Generate oop map ++ OopMap* map = new OopMap(framesize, 0); ++ oop_maps->add_gc_map(__ offset(), map); ++ ++ // restore the thread (cannot use the pushed argument since arguments ++ // may be overwritten by C code generated by an optimizing compiler); ++ // however can use the register value directly if it is callee saved. ++#ifndef OPT_THREAD ++ __ get_thread(java_thread); ++#endif ++ ++ __ ld(SP, java_thread, in_bytes(JavaThread::last_Java_sp_offset())); ++ __ reset_last_Java_frame(java_thread, true); ++ ++ // Restore callee save registers. This must be done after resetting the Java frame ++ __ ld(S0, SP, S0_off * wordSize); ++ __ ld(S1, SP, S1_off * wordSize); ++ __ ld(S2, SP, S2_off * wordSize); ++ __ ld(S3, SP, S3_off * wordSize); ++ __ ld(S4, SP, S4_off * wordSize); ++ __ ld(S5, SP, S5_off * wordSize); ++ __ ld(S6, SP, S6_off * wordSize); ++ __ ld(S7, SP, S7_off * wordSize); ++ ++ // discard arguments ++ __ move(SP, FP); // epilog ++ __ pop(FP); ++ // check for pending exceptions ++#ifdef ASSERT ++ Label L; ++ __ ld(AT, java_thread, in_bytes(Thread::pending_exception_offset())); ++ __ bne(AT, R0, L); ++ __ delayed()->nop(); ++ __ should_not_reach_here(); ++ __ bind(L); ++#endif //ASSERT ++ __ jmp(StubRoutines::forward_exception_entry(), relocInfo::runtime_call_type); ++ __ delayed()->nop(); ++ RuntimeStub* stub = RuntimeStub::new_runtime_stub(name, ++ &code, ++ frame_complete, ++ framesize, ++ oop_maps, false); ++ return stub->entry_point(); ++ } ++ ++ // Initialization ++ void generate_initial() { ++ // Generates all stubs and initializes the entry points ++ ++ //------------------------------------------------------------- ++ //----------------------------------------------------------- ++ // entry points that exist in all platforms ++ // Note: This is code that could be shared among different platforms - however the benefit seems to be smaller ++ // than the disadvantage of having a much more complicated generator structure. ++ // See also comment in stubRoutines.hpp. ++ StubRoutines::_forward_exception_entry = generate_forward_exception(); ++ StubRoutines::_call_stub_entry = generate_call_stub(StubRoutines::_call_stub_return_address); ++ // is referenced by megamorphic call ++ StubRoutines::_catch_exception_entry = generate_catch_exception(); ++ ++ StubRoutines::_handler_for_unsafe_access_entry = generate_handler_for_unsafe_access(); ++ ++ StubRoutines::_throw_StackOverflowError_entry = generate_throw_exception("StackOverflowError throw_exception", ++ CAST_FROM_FN_PTR(address, SharedRuntime::throw_StackOverflowError), false); ++ } ++ ++ void generate_all() { ++ // Generates all stubs and initializes the entry points ++ ++ // These entry points require SharedInfo::stack0 to be set up in ++ // non-core builds and need to be relocatable, so they each ++ // fabricate a RuntimeStub internally. ++ StubRoutines::_throw_AbstractMethodError_entry = generate_throw_exception("AbstractMethodError throw_exception", ++ CAST_FROM_FN_PTR(address, SharedRuntime::throw_AbstractMethodError), false); ++ ++ StubRoutines::_throw_IncompatibleClassChangeError_entry = generate_throw_exception("IncompatibleClassChangeError throw_exception", ++ CAST_FROM_FN_PTR(address, SharedRuntime:: throw_IncompatibleClassChangeError), false); ++ ++ StubRoutines::_throw_NullPointerException_at_call_entry = generate_throw_exception("NullPointerException at call throw_exception", ++ CAST_FROM_FN_PTR(address, SharedRuntime::throw_NullPointerException_at_call), false); ++ ++ // entry points that are platform specific ++ ++ // support for verify_oop (must happen after universe_init) ++ StubRoutines::_verify_oop_subroutine_entry = generate_verify_oop(); ++#ifndef CORE ++ // arraycopy stubs used by compilers ++ generate_arraycopy_stubs(); ++#endif ++ ++ // Safefetch stubs. ++ generate_safefetch("SafeFetch32", sizeof(int), &StubRoutines::_safefetch32_entry, ++ &StubRoutines::_safefetch32_fault_pc, ++ &StubRoutines::_safefetch32_continuation_pc); ++ generate_safefetch("SafeFetchN", sizeof(intptr_t), &StubRoutines::_safefetchN_entry, ++ &StubRoutines::_safefetchN_fault_pc, ++ &StubRoutines::_safefetchN_continuation_pc); ++ ++ if (UseMontgomeryMultiplyIntrinsic) { ++ StubRoutines::_montgomeryMultiply ++ = CAST_FROM_FN_PTR(address, SharedRuntime::montgomery_multiply); ++ } ++ if (UseMontgomerySquareIntrinsic) { ++ StubRoutines::_montgomerySquare ++ = CAST_FROM_FN_PTR(address, SharedRuntime::montgomery_square); ++ } ++ } ++ ++ public: ++ StubGenerator(CodeBuffer* code, bool all) : StubCodeGenerator(code) { ++ if (all) { ++ generate_all(); ++ } else { ++ generate_initial(); ++ } ++ } ++}; // end class declaration ++ ++void StubGenerator_generate(CodeBuffer* code, bool all) { ++ StubGenerator g(code, all); ++} +diff --git a/hotspot/src/cpu/mips/vm/stubRoutines_mips_64.cpp b/hotspot/src/cpu/mips/vm/stubRoutines_mips_64.cpp +new file mode 100644 +index 0000000000..733a48b889 +--- /dev/null ++++ b/hotspot/src/cpu/mips/vm/stubRoutines_mips_64.cpp +@@ -0,0 +1,35 @@ ++/* ++ * Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2015, 2016, Loongson Technology. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ * ++ */ ++ ++#include "precompiled.hpp" ++#include "runtime/deoptimization.hpp" ++#include "runtime/frame.inline.hpp" ++#include "runtime/stubRoutines.hpp" ++#include "runtime/thread.inline.hpp" ++ ++// a description of how to extend it, see the stubRoutines.hpp file. ++ ++//find the last fp value ++address StubRoutines::gs2::_call_stub_compiled_return = NULL; +diff --git a/hotspot/src/cpu/mips/vm/stubRoutines_mips_64.hpp b/hotspot/src/cpu/mips/vm/stubRoutines_mips_64.hpp +new file mode 100644 +index 0000000000..920c08844e +--- /dev/null ++++ b/hotspot/src/cpu/mips/vm/stubRoutines_mips_64.hpp +@@ -0,0 +1,59 @@ ++/* ++ * Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2015, 2022, Loongson Technology. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ * ++ */ ++ ++#ifndef CPU_MIPS_VM_STUBROUTINES_MIPS_64_HPP ++#define CPU_MIPS_VM_STUBROUTINES_MIPS_64_HPP ++ ++// This file holds the platform specific parts of the StubRoutines ++// definition. See stubRoutines.hpp for a description on how to ++// extend it. ++ ++static bool returns_to_call_stub(address return_pc){ ++ return return_pc == _call_stub_return_address||return_pc == gs2::get_call_stub_compiled_return(); ++} ++ ++enum platform_dependent_constants { ++ code_size1 = 20000, // simply increase if too small (assembler will crash if too small) ++ code_size2 = 40000 // simply increase if too small (assembler will crash if too small) ++}; ++ ++class gs2 { ++ friend class StubGenerator; ++ friend class VMStructs; ++ private: ++ // If we call compiled code directly from the call stub we will ++ // need to adjust the return back to the call stub to a specialized ++ // piece of code that can handle compiled results and cleaning the fpu ++ // stack. The variable holds that location. ++ static address _call_stub_compiled_return; ++ ++public: ++ // Call back points for traps in compiled code ++ static address get_call_stub_compiled_return() { return _call_stub_compiled_return; } ++ static void set_call_stub_compiled_return(address ret){ _call_stub_compiled_return = ret; } ++ ++}; ++ ++#endif // CPU_MIPS_VM_STUBROUTINES_MIPS_64_HPP +diff --git a/hotspot/src/cpu/mips/vm/templateInterpreterGenerator_mips.hpp b/hotspot/src/cpu/mips/vm/templateInterpreterGenerator_mips.hpp +new file mode 100644 +index 0000000000..a83c3728f8 +--- /dev/null ++++ b/hotspot/src/cpu/mips/vm/templateInterpreterGenerator_mips.hpp +@@ -0,0 +1,35 @@ ++/* ++ * Copyright (c) 1997, 2010, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2015, 2016, Loongson Technology. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ * ++ */ ++ ++#ifndef CPU_MIPS_VM_TEMPLATEINTERPRETERGENERATOR_MIPS_HPP ++#define CPU_MIPS_VM_TEMPLATEINTERPRETERGENERATOR_MIPS_HPP ++ ++ protected: ++ ++ void generate_fixed_frame(bool native_call); ++ ++ // address generate_asm_interpreter_entry(bool synchronized); ++ ++#endif // CPU_MIPS_VM_TEMPLATEINTERPRETERGENERATOR_MIPS_HPP +diff --git a/hotspot/src/cpu/mips/vm/templateInterpreter_mips.hpp b/hotspot/src/cpu/mips/vm/templateInterpreter_mips.hpp +new file mode 100644 +index 0000000000..204f1b2f21 +--- /dev/null ++++ b/hotspot/src/cpu/mips/vm/templateInterpreter_mips.hpp +@@ -0,0 +1,41 @@ ++/* ++ * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2015, 2022, Loongson Technology. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ * ++ */ ++ ++#ifndef CPU_MIPS_VM_TEMPLATEINTERPRETER_MIPS_HPP ++#define CPU_MIPS_VM_TEMPLATEINTERPRETER_MIPS_HPP ++ ++ ++ protected: ++ ++ // Size of interpreter code. Increase if too small. Interpreter will ++ // fail with a guarantee ("not enough space for interpreter generation"); ++ // if too small. ++ // Run with +PrintInterpreter to get the VM to print out the size. ++ // Max size with JVMTI ++ // The sethi() instruction generates lots more instructions when shell ++ // stack limit is unlimited, so that's why this is much bigger. ++ const static int InterpreterCodeSize = 500 * K; ++ ++#endif // CPU_MIPS_VM_TEMPLATEINTERPRETER_MIPS_HPP +diff --git a/hotspot/src/cpu/mips/vm/templateInterpreter_mips_64.cpp b/hotspot/src/cpu/mips/vm/templateInterpreter_mips_64.cpp +new file mode 100644 +index 0000000000..0cc5d33070 +--- /dev/null ++++ b/hotspot/src/cpu/mips/vm/templateInterpreter_mips_64.cpp +@@ -0,0 +1,2306 @@ ++/* ++ * Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2015, 2022, Loongson Technology. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ * ++ */ ++ ++#include "precompiled.hpp" ++#include "asm/macroAssembler.hpp" ++#include "interpreter/bytecodeHistogram.hpp" ++#include "interpreter/interpreter.hpp" ++#include "interpreter/interpreterGenerator.hpp" ++#include "interpreter/interpreterRuntime.hpp" ++#include "interpreter/templateTable.hpp" ++#include "oops/arrayOop.hpp" ++#include "oops/methodData.hpp" ++#include "oops/method.hpp" ++#include "oops/oop.inline.hpp" ++#include "prims/jvmtiExport.hpp" ++#include "prims/jvmtiThreadState.hpp" ++#include "runtime/arguments.hpp" ++#include "runtime/deoptimization.hpp" ++#include "runtime/frame.inline.hpp" ++#include "runtime/sharedRuntime.hpp" ++#include "runtime/stubRoutines.hpp" ++#include "runtime/synchronizer.hpp" ++#include "runtime/timer.hpp" ++#include "runtime/vframeArray.hpp" ++#include "utilities/debug.hpp" ++ ++#define __ _masm-> ++ ++#define A0 RA0 ++#define A1 RA1 ++#define A2 RA2 ++#define A3 RA3 ++#define A4 RA4 ++#define A5 RA5 ++#define A6 RA6 ++#define A7 RA7 ++#define T0 RT0 ++#define T1 RT1 ++#define T2 RT2 ++#define T3 RT3 ++#define T8 RT8 ++#define T9 RT9 ++ ++#ifndef CC_INTERP ++ ++// asm based interpreter deoptimization helpers ++int AbstractInterpreter::size_activation(int max_stack, ++ int temps, ++ int extra_args, ++ int monitors, ++ int callee_params, ++ int callee_locals, ++ bool is_top_frame) { ++ // Note: This calculation must exactly parallel the frame setup ++ // in AbstractInterpreterGenerator::generate_method_entry. ++ ++ // fixed size of an interpreter frame: ++ int overhead = frame::sender_sp_offset - ++ frame::interpreter_frame_initial_sp_offset; ++ // Our locals were accounted for by the caller (or last_frame_adjust ++ // on the transistion) Since the callee parameters already account ++ // for the callee's params we only need to account for the extra ++ // locals. ++ int size = overhead + ++ (callee_locals - callee_params)*Interpreter::stackElementWords + ++ monitors * frame::interpreter_frame_monitor_size() + ++ temps* Interpreter::stackElementWords + extra_args; ++ ++ return size; ++} ++ ++ ++const int Interpreter::return_sentinel = 0xfeedbeed; ++const int method_offset = frame::interpreter_frame_method_offset * wordSize; ++const int bci_offset = frame::interpreter_frame_bcx_offset * wordSize; ++const int locals_offset = frame::interpreter_frame_locals_offset * wordSize; ++ ++//----------------------------------------------------------------------------- ++ ++address TemplateInterpreterGenerator::generate_StackOverflowError_handler() { ++ address entry = __ pc(); ++ ++#ifdef ASSERT ++ { ++ Label L; ++ __ addiu(T1, FP, frame::interpreter_frame_monitor_block_top_offset * wordSize); ++ __ subu(T1, T1, SP); // T1 = maximal sp for current fp ++ __ bgez(T1, L); // check if frame is complete ++ __ delayed()->nop(); ++ __ stop("interpreter frame not set up"); ++ __ bind(L); ++ } ++#endif // ASSERT ++ // Restore bcp under the assumption that the current frame is still ++ // interpreted ++ // FIXME: please change the func restore_bcp ++ // S0 is the conventional register for bcp ++ __ restore_bcp(); ++ ++ // expression stack must be empty before entering the VM if an ++ // exception happened ++ __ empty_expression_stack(); ++ // throw exception ++ // FIXME: why do not pass parameter thread ? ++ __ call_VM(NOREG, CAST_FROM_FN_PTR(address, InterpreterRuntime::throw_StackOverflowError)); ++ return entry; ++} ++ ++address TemplateInterpreterGenerator::generate_ArrayIndexOutOfBounds_handler( ++ const char* name) { ++ address entry = __ pc(); ++ // expression stack must be empty before entering the VM if an ++ // exception happened ++ __ empty_expression_stack(); ++ __ li(A1, (long)name); ++ __ call_VM(noreg, CAST_FROM_FN_PTR(address, ++ InterpreterRuntime::throw_ArrayIndexOutOfBoundsException), A1, A2); ++ return entry; ++} ++ ++address TemplateInterpreterGenerator::generate_ClassCastException_handler() { ++ address entry = __ pc(); ++ ++ // expression stack must be empty before entering the VM if an ++ // exception happened ++ __ empty_expression_stack(); ++ __ empty_FPU_stack(); ++ __ call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::throw_ClassCastException), FSR); ++ return entry; ++} ++ ++address TemplateInterpreterGenerator::generate_exception_handler_common( ++ const char* name, const char* message, bool pass_oop) { ++ assert(!pass_oop || message == NULL, "either oop or message but not both"); ++ address entry = __ pc(); ++ ++ // expression stack must be empty before entering the VM if an exception happened ++ __ empty_expression_stack(); ++ // setup parameters ++ __ li(A1, (long)name); ++ if (pass_oop) { ++ __ call_VM(V0, ++ CAST_FROM_FN_PTR(address, InterpreterRuntime::create_klass_exception), A1, FSR); ++ } else { ++ __ li(A2, (long)message); ++ __ call_VM(V0, ++ CAST_FROM_FN_PTR(address, InterpreterRuntime::create_exception), A1, A2); ++ } ++ // throw exception ++ __ jmp(Interpreter::throw_exception_entry(), relocInfo::none); ++ __ delayed()->nop(); ++ return entry; ++} ++ ++ ++address TemplateInterpreterGenerator::generate_continuation_for(TosState state) { ++ address entry = __ pc(); ++ // NULL last_sp until next java call ++ __ sd(R0,Address(FP, frame::interpreter_frame_last_sp_offset * wordSize)); ++ __ dispatch_next(state); ++ return entry; ++} ++ ++ ++address TemplateInterpreterGenerator::generate_return_entry_for(TosState state, int step, size_t index_size) { ++ ++ address entry = __ pc(); ++ ++ // Restore stack bottom in case i2c adjusted stack ++ __ ld(SP, Address(FP, frame::interpreter_frame_last_sp_offset * wordSize)); ++ // and NULL it as marker that sp is now tos until next java call ++ __ sd(R0, FP, frame::interpreter_frame_last_sp_offset * wordSize); ++ ++ __ restore_bcp(); ++ __ restore_locals(); ++ ++ // mdp: T8 ++ // ret: FSR ++ // tmp: T9 ++ if (state == atos) { ++ Register mdp = T8; ++ Register tmp = T9; ++ __ profile_return_type(mdp, FSR, tmp); ++ } ++ ++ ++ const Register cache = T9; ++ const Register index = T3; ++ __ get_cache_and_index_at_bcp(cache, index, 1, index_size); ++ ++ const Register flags = cache; ++ __ dsll(AT, index, Address::times_ptr); ++ __ daddu(AT, cache, AT); ++ __ lw(flags, AT, in_bytes(ConstantPoolCache::base_offset() + ConstantPoolCacheEntry::flags_offset())); ++ __ andi(flags, flags, ConstantPoolCacheEntry::parameter_size_mask); ++ __ dsll(AT, flags, Interpreter::stackElementScale()); ++ __ daddu(SP, SP, AT); ++ ++ __ dispatch_next(state, step); ++ ++ return entry; ++} ++ ++ ++address TemplateInterpreterGenerator::generate_deopt_entry_for(TosState state, ++ int step) { ++ address entry = __ pc(); ++ // NULL last_sp until next java call ++ __ sd(R0, FP, frame::interpreter_frame_last_sp_offset * wordSize); ++ __ restore_bcp(); ++ __ restore_locals(); ++ // handle exceptions ++ { ++ Label L; ++ const Register thread = TREG; ++#ifndef OPT_THREAD ++ __ get_thread(thread); ++#endif ++ __ ld(AT, thread, in_bytes(Thread::pending_exception_offset())); ++ __ beq(AT, R0, L); ++ __ delayed()->nop(); ++ __ call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::throw_pending_exception)); ++ __ should_not_reach_here(); ++ __ bind(L); ++ } ++ __ dispatch_next(state, step); ++ return entry; ++} ++ ++int AbstractInterpreter::BasicType_as_index(BasicType type) { ++ int i = 0; ++ switch (type) { ++ case T_BOOLEAN: i = 0; break; ++ case T_CHAR : i = 1; break; ++ case T_BYTE : i = 2; break; ++ case T_SHORT : i = 3; break; ++ case T_INT : // fall through ++ case T_LONG : // fall through ++ case T_VOID : i = 4; break; ++ case T_FLOAT : i = 5; break; ++ case T_DOUBLE : i = 6; break; ++ case T_OBJECT : // fall through ++ case T_ARRAY : i = 7; break; ++ default : ShouldNotReachHere(); ++ } ++ assert(0 <= i && i < AbstractInterpreter::number_of_result_handlers, ++ "index out of bounds"); ++ return i; ++} ++ ++ ++address TemplateInterpreterGenerator::generate_result_handler_for( ++ BasicType type) { ++ address entry = __ pc(); ++ switch (type) { ++ case T_BOOLEAN: __ c2bool(V0); break; ++ case T_CHAR : __ andi(V0, V0, 0xFFFF); break; ++ case T_BYTE : __ sign_extend_byte (V0); break; ++ case T_SHORT : __ sign_extend_short(V0); break; ++ case T_INT : /* nothing to do */ break; ++ case T_FLOAT : /* nothing to do */ break; ++ case T_DOUBLE : /* nothing to do */ break; ++ case T_OBJECT : ++ { ++ __ ld(V0, FP, frame::interpreter_frame_oop_temp_offset * wordSize); ++ __ verify_oop(V0); // and verify it ++ } ++ break; ++ default : ShouldNotReachHere(); ++ } ++ __ jr(RA); // return from result handler ++ __ delayed()->nop(); ++ return entry; ++} ++ ++address TemplateInterpreterGenerator::generate_safept_entry_for( ++ TosState state, ++ address runtime_entry) { ++ address entry = __ pc(); ++ __ push(state); ++ __ call_VM(noreg, runtime_entry); ++ __ dispatch_via(vtos, Interpreter::_normal_table.table_for(vtos)); ++ return entry; ++} ++ ++ ++ ++// Helpers for commoning out cases in the various type of method entries. ++// ++ ++ ++// increment invocation count & check for overflow ++// ++// Note: checking for negative value instead of overflow ++// so we have a 'sticky' overflow test ++// ++// Rmethod: method ++// T3 : invocation counter ++// ++void InterpreterGenerator::generate_counter_incr( ++ Label* overflow, ++ Label* profile_method, ++ Label* profile_method_continue) { ++ Label done; ++ if (TieredCompilation) { ++ int increment = InvocationCounter::count_increment; ++ int mask = ((1 << Tier0InvokeNotifyFreqLog) - 1) << InvocationCounter::count_shift; ++ Label no_mdo; ++ if (ProfileInterpreter) { ++ // Are we profiling? ++ __ ld(FSR, Address(Rmethod, Method::method_data_offset())); ++ __ beq(FSR, R0, no_mdo); ++ __ delayed()->nop(); ++ // Increment counter in the MDO ++ const Address mdo_invocation_counter(FSR, in_bytes(MethodData::invocation_counter_offset()) + ++ in_bytes(InvocationCounter::counter_offset())); ++ __ increment_mask_and_jump(mdo_invocation_counter, increment, mask, T3, false, Assembler::zero, overflow); ++ __ beq(R0, R0, done); ++ __ delayed()->nop(); ++ } ++ __ bind(no_mdo); ++ // Increment counter in MethodCounters ++ const Address invocation_counter(FSR, ++ MethodCounters::invocation_counter_offset() + ++ InvocationCounter::counter_offset()); ++ __ get_method_counters(Rmethod, FSR, done); ++ __ increment_mask_and_jump(invocation_counter, increment, mask, T3, false, Assembler::zero, overflow); ++ __ bind(done); ++ } else { ++ const Address invocation_counter(FSR, in_bytes(MethodCounters::invocation_counter_offset()) ++ + in_bytes(InvocationCounter::counter_offset())); ++ const Address backedge_counter (FSR, in_bytes(MethodCounters::backedge_counter_offset()) ++ + in_bytes(InvocationCounter::counter_offset())); ++ ++ __ get_method_counters(Rmethod, FSR, done); ++ ++ if (ProfileInterpreter) { // %%% Merge this into methodDataOop ++ __ lw(T9, FSR, in_bytes(MethodCounters::interpreter_invocation_counter_offset())); ++ __ incrementl(T9, 1); ++ __ sw(T9, FSR, in_bytes(MethodCounters::interpreter_invocation_counter_offset())); ++ } ++ // Update standard invocation counters ++ __ lw(T3, invocation_counter); ++ __ increment(T3, InvocationCounter::count_increment); ++ __ sw(T3, invocation_counter); // save invocation count ++ ++ __ lw(FSR, backedge_counter); // load backedge counter ++ __ li(AT, InvocationCounter::count_mask_value); // mask out the status bits ++ __ andr(FSR, FSR, AT); ++ ++ __ daddu(T3, T3, FSR); // add both counters ++ ++ if (ProfileInterpreter && profile_method != NULL) { ++ // Test to see if we should create a method data oop ++ if (Assembler::is_simm16(InvocationCounter::InterpreterProfileLimit)) { ++ __ slti(AT, T3, InvocationCounter::InterpreterProfileLimit); ++ } else { ++ __ li(AT, (long)&InvocationCounter::InterpreterProfileLimit); ++ __ lw(AT, AT, 0); ++ __ slt(AT, T3, AT); ++ } ++ ++ __ bne_far(AT, R0, *profile_method_continue); ++ __ delayed()->nop(); ++ ++ // if no method data exists, go to profile_method ++ __ test_method_data_pointer(FSR, *profile_method); ++ } ++ ++ if (Assembler::is_simm16(CompileThreshold)) { ++ __ srl(AT, T3, InvocationCounter::count_shift); ++ __ slti(AT, AT, CompileThreshold); ++ } else { ++ __ li(AT, (long)&InvocationCounter::InterpreterInvocationLimit); ++ __ lw(AT, AT, 0); ++ __ slt(AT, T3, AT); ++ } ++ ++ __ beq_far(AT, R0, *overflow); ++ __ delayed()->nop(); ++ __ bind(done); ++ } ++} ++ ++void InterpreterGenerator::generate_counter_overflow(Label* do_continue) { ++ ++ // Asm interpreter on entry ++ // S7 - locals ++ // S0 - bcp ++ // Rmethod - method ++ // FP - interpreter frame ++ ++ // On return (i.e. jump to entry_point) ++ // Rmethod - method ++ // RA - return address of interpreter caller ++ // tos - the last parameter to Java method ++ // SP - sender_sp ++ ++ ++ // the bcp is valid if and only if it's not null ++ __ call_VM(NOREG, CAST_FROM_FN_PTR(address, ++ InterpreterRuntime::frequency_counter_overflow), R0); ++ __ ld(Rmethod, FP, method_offset); ++ // Preserve invariant that S0/S7 contain bcp/locals of sender frame ++ __ b_far(*do_continue); ++ __ delayed()->nop(); ++} ++ ++// See if we've got enough room on the stack for locals plus overhead. ++// The expression stack grows down incrementally, so the normal guard ++// page mechanism will work for that. ++// ++// NOTE: Since the additional locals are also always pushed (wasn't ++// obvious in generate_method_entry) so the guard should work for them ++// too. ++// ++// Args: ++// T2: number of additional locals this frame needs (what we must check) ++// T0: Method* ++// ++void InterpreterGenerator::generate_stack_overflow_check(void) { ++ // see if we've got enough room on the stack for locals plus overhead. ++ // the expression stack grows down incrementally, so the normal guard ++ // page mechanism will work for that. ++ // ++ // Registers live on entry: ++ // ++ // T0: Method* ++ // T2: number of additional locals this frame needs (what we must check) ++ ++ // NOTE: since the additional locals are also always pushed (wasn't obvious in ++ // generate_method_entry) so the guard should work for them too. ++ // ++ ++ const int entry_size = frame::interpreter_frame_monitor_size() * wordSize; ++ ++ // total overhead size: entry_size + (saved fp thru expr stack bottom). ++ // be sure to change this if you add/subtract anything to/from the overhead area ++ const int overhead_size = -(frame::interpreter_frame_initial_sp_offset*wordSize) ++ + entry_size; ++ ++ const int page_size = os::vm_page_size(); ++ ++ Label after_frame_check; ++ ++ // see if the frame is greater than one page in size. If so, ++ // then we need to verify there is enough stack space remaining ++ // for the additional locals. ++ __ move(AT, (page_size - overhead_size) / Interpreter::stackElementSize); ++ __ slt(AT, AT, T2); ++ __ beq(AT, R0, after_frame_check); ++ __ delayed()->nop(); ++ ++ // compute sp as if this were going to be the last frame on ++ // the stack before the red zone ++#ifndef OPT_THREAD ++ Register thread = T1; ++ __ get_thread(thread); ++#else ++ Register thread = TREG; ++#endif ++ ++ // locals + overhead, in bytes ++ __ dsll(T3, T2, Interpreter::stackElementScale()); ++ __ daddiu(T3, T3, overhead_size); // locals * 4 + overhead_size --> T3 ++ ++#ifdef ASSERT ++ Label stack_base_okay, stack_size_okay; ++ // verify that thread stack base is non-zero ++ __ ld(AT, thread, in_bytes(Thread::stack_base_offset())); ++ __ bne(AT, R0, stack_base_okay); ++ __ delayed()->nop(); ++ __ stop("stack base is zero"); ++ __ bind(stack_base_okay); ++ // verify that thread stack size is non-zero ++ __ ld(AT, thread, in_bytes(Thread::stack_size_offset())); ++ __ bne(AT, R0, stack_size_okay); ++ __ delayed()->nop(); ++ __ stop("stack size is zero"); ++ __ bind(stack_size_okay); ++#endif ++ ++ // Add stack base to locals and subtract stack size ++ __ ld(AT, thread, in_bytes(Thread::stack_base_offset())); // stack_base --> AT ++ __ daddu(T3, T3, AT); // locals * 4 + overhead_size + stack_base--> T3 ++ __ ld(AT, thread, in_bytes(Thread::stack_size_offset())); // stack_size --> AT ++ __ dsubu(T3, T3, AT); // locals * 4 + overhead_size + stack_base - stack_size --> T3 ++ ++ ++ // add in the redzone and yellow size ++ __ move(AT, (StackRedPages+StackYellowPages) * page_size); ++ __ addu(T3, T3, AT); ++ ++ // check against the current stack bottom ++ __ slt(AT, T3, SP); ++ __ bne(AT, R0, after_frame_check); ++ __ delayed()->nop(); ++ ++ // Note: the restored frame is not necessarily interpreted. ++ // Use the shared runtime version of the StackOverflowError. ++ __ move(SP, Rsender); ++ assert(StubRoutines::throw_StackOverflowError_entry() != NULL, "stub not yet generated"); ++ __ jmp(StubRoutines::throw_StackOverflowError_entry(), relocInfo::runtime_call_type); ++ __ delayed()->nop(); ++ ++ // all done with frame size check ++ __ bind(after_frame_check); ++} ++ ++// Allocate monitor and lock method (asm interpreter) ++// Rmethod - Method* ++void InterpreterGenerator::lock_method(void) { ++ // synchronize method ++ const int entry_size = frame::interpreter_frame_monitor_size() * wordSize; ++ ++#ifdef ASSERT ++ { Label L; ++ __ lw(T0, Rmethod, in_bytes(Method::access_flags_offset())); ++ __ andi(T0, T0, JVM_ACC_SYNCHRONIZED); ++ __ bne(T0, R0, L); ++ __ delayed()->nop(); ++ __ stop("method doesn't need synchronization"); ++ __ bind(L); ++ } ++#endif // ASSERT ++ // get synchronization object ++ { ++ Label done; ++ const int mirror_offset = in_bytes(Klass::java_mirror_offset()); ++ __ lw(T0, Rmethod, in_bytes(Method::access_flags_offset())); ++ __ andi(T2, T0, JVM_ACC_STATIC); ++ __ ld(T0, LVP, Interpreter::local_offset_in_bytes(0)); ++ __ beq(T2, R0, done); ++ __ delayed()->nop(); ++ __ ld(T0, Rmethod, in_bytes(Method::const_offset())); ++ __ ld(T0, T0, in_bytes(ConstMethod::constants_offset())); ++ __ ld(T0, T0, ConstantPool::pool_holder_offset_in_bytes()); ++ __ ld(T0, T0, mirror_offset); ++ __ bind(done); ++ } ++ // add space for monitor & lock ++ __ daddiu(SP, SP, (-1) * entry_size); // add space for a monitor entry ++ __ sd(SP, FP, frame::interpreter_frame_monitor_block_top_offset * wordSize); ++ // set new monitor block top ++ __ sd(T0, SP, BasicObjectLock::obj_offset_in_bytes()); // store object ++ // FIXME: I do not know what lock_object will do and what it will need ++ __ move(c_rarg0, SP); // object address ++ __ lock_object(c_rarg0); ++} ++ ++// Generate a fixed interpreter frame. This is identical setup for ++// interpreted methods and for native methods hence the shared code. ++void TemplateInterpreterGenerator::generate_fixed_frame(bool native_call) { ++ ++ // [ local var m-1 ] <--- sp ++ // ... ++ // [ local var 0 ] ++ // [ argumnet word n-1 ] <--- T0(sender's sp) ++ // ... ++ // [ argument word 0 ] <--- S7 ++ ++ // initialize fixed part of activation frame ++ // sender's sp in Rsender ++ int i = 0; ++ int frame_size = 9; ++#ifndef CORE ++ ++frame_size; ++#endif ++ __ daddiu(SP, SP, (-frame_size) * wordSize); ++ __ sd(RA, SP, (frame_size - 1) * wordSize); // save return address ++ __ sd(FP, SP, (frame_size - 2) * wordSize); // save sender's fp ++ __ daddiu(FP, SP, (frame_size - 2) * wordSize); ++ __ sd(Rsender, FP, (-++i) * wordSize); // save sender's sp ++ __ sd(R0, FP,(-++i) * wordSize); //save last_sp as null ++ __ sd(LVP, FP, (-++i) * wordSize); // save locals offset ++ __ ld(BCP, Rmethod, in_bytes(Method::const_offset())); // get constMethodOop ++ __ daddiu(BCP, BCP, in_bytes(ConstMethod::codes_offset())); // get codebase ++ __ sd(Rmethod, FP, (-++i) * wordSize); // save Method* ++#ifndef CORE ++ if (ProfileInterpreter) { ++ Label method_data_continue; ++ __ ld(AT, Rmethod, in_bytes(Method::method_data_offset())); ++ __ beq(AT, R0, method_data_continue); ++ __ delayed()->nop(); ++ __ daddiu(AT, AT, in_bytes(MethodData::data_offset())); ++ __ bind(method_data_continue); ++ __ sd(AT, FP, (-++i) * wordSize); ++ } else { ++ __ sd(R0, FP, (-++i) * wordSize); ++ } ++#endif // !CORE ++ ++ __ ld(T2, Rmethod, in_bytes(Method::const_offset())); ++ __ ld(T2, T2, in_bytes(ConstMethod::constants_offset())); ++ __ ld(T2, T2, ConstantPool::cache_offset_in_bytes()); ++ __ sd(T2, FP, (-++i) * wordSize); // set constant pool cache ++ if (native_call) { ++ __ sd(R0, FP, (-++i) * wordSize); // no bcp ++ } else { ++ __ sd(BCP, FP, (-++i) * wordSize); // set bcp ++ } ++ __ sd(SP, FP, (-++i) * wordSize); // reserve word for pointer to expression stack bottom ++ assert(i + 2 == frame_size, "i + 2 should be equal to frame_size"); ++} ++ ++// End of helpers ++ ++// Various method entries ++//------------------------------------------------------------------------------------------------------------------------ ++// ++// ++ ++// Call an accessor method (assuming it is resolved, otherwise drop ++// into vanilla (slow path) entry ++address InterpreterGenerator::generate_accessor_entry(void) { ++ ++ // Rmethod: Method* ++ // V0: receiver (preserve for slow entry into asm interpreter) ++ // Rsender: senderSP must preserved for slow path, set SP to it on fast path ++ ++ address entry_point = __ pc(); ++ Label xreturn_path; ++ // do fastpath for resolved accessor methods ++ if (UseFastAccessorMethods) { ++ Label slow_path; ++ __ li(T2, SafepointSynchronize::address_of_state()); ++ __ lw(AT, T2, 0); ++ __ daddiu(AT, AT, -(SafepointSynchronize::_not_synchronized)); ++ __ bne(AT, R0, slow_path); ++ __ delayed()->nop(); ++ // Code: _aload_0, _(i|a)getfield, _(i|a)return or any rewrites thereof; ++ // parameter size = 1 ++ // Note: We can only use this code if the getfield has been resolved ++ // and if we don't have a null-pointer exception => check for ++ // these conditions first and use slow path if necessary. ++ // Rmethod: method ++ // V0: receiver ++ ++ // [ receiver ] <-- sp ++ __ ld(T0, SP, 0); ++ ++ // check if local 0 != NULL and read field ++ __ beq(T0, R0, slow_path); ++ __ delayed()->nop(); ++ __ ld(T2, Rmethod, in_bytes(Method::const_offset())); ++ __ ld(T2, T2, in_bytes(ConstMethod::constants_offset())); ++ // read first instruction word and extract bytecode @ 1 and index @ 2 ++ __ ld(T3, Rmethod, in_bytes(Method::const_offset())); ++ __ lw(T3, T3, in_bytes(ConstMethod::codes_offset())); ++ // Shift codes right to get the index on the right. ++ // The bytecode fetched looks like <0xb4><0x2a> ++ __ dsrl(T3, T3, 2 * BitsPerByte); ++ // FIXME: maybe it's wrong ++ __ dsll(T3, T3, exact_log2(in_words(ConstantPoolCacheEntry::size()))); ++ __ ld(T2, T2, ConstantPool::cache_offset_in_bytes()); ++ ++ // T0: local 0 ++ // Rmethod: method ++ // V0: receiver - do not destroy since it is needed for slow path! ++ // T1: scratch use which register instead ? ++ // T3: constant pool cache index ++ // T2: constant pool cache ++ // Rsender: send's sp ++ // check if getfield has been resolved and read constant pool cache entry ++ // check the validity of the cache entry by testing whether _indices field ++ // contains Bytecode::_getfield in b1 byte. ++ assert(in_words(ConstantPoolCacheEntry::size()) == 4, "adjust shift below"); ++ ++ __ dsll(T8, T3, Address::times_8); ++ __ move(T1, in_bytes(ConstantPoolCache::base_offset() ++ + ConstantPoolCacheEntry::indices_offset())); ++ __ daddu(T1, T8, T1); ++ __ daddu(T1, T1, T2); ++ __ lw(T1, T1, 0); ++ __ dsrl(T1, T1, 2 * BitsPerByte); ++ __ andi(T1, T1, 0xFF); ++ __ daddiu(T1, T1, (-1) * Bytecodes::_getfield); ++ __ bne(T1, R0, slow_path); ++ __ delayed()->nop(); ++ ++ // Note: constant pool entry is not valid before bytecode is resolved ++ ++ __ move(T1, in_bytes(ConstantPoolCache::base_offset() + ConstantPoolCacheEntry::f2_offset())); ++ __ daddu(T1, T1, T8); ++ __ daddu(T1, T1, T2); ++ __ lw(AT, T1, 0); ++ ++ __ move(T1, in_bytes(ConstantPoolCache::base_offset() + ConstantPoolCacheEntry::flags_offset())); ++ __ daddu(T1, T1, T8); ++ __ daddu(T1, T1, T2); ++ __ lw(T3, T1, 0); ++ ++ Label notByte, notBool, notShort, notChar, notObj; ++ ++ // Need to differentiate between igetfield, agetfield, bgetfield etc. ++ // because they are different sizes. ++ // Use the type from the constant pool cache ++ __ srl(T3, T3, ConstantPoolCacheEntry::tos_state_shift); ++ // Make sure we don't need to mask T3 for tosBits after the above shift ++ ConstantPoolCacheEntry::verify_tos_state_shift(); ++ // btos = 0 ++ __ bne(T3, R0, notByte); ++ __ delayed()->daddu(T0, T0, AT); ++ ++ __ lb(V0, T0, 0); ++ __ b(xreturn_path); ++ __ delayed()->nop(); ++ ++ //ztos ++ __ bind(notByte); ++ __ daddiu(T1, T3, (-1) * ztos); ++ __ bne(T1, R0, notBool); ++ __ delayed()->nop(); ++ __ lb(V0, T0, 0); ++ __ b(xreturn_path); ++ __ delayed()->nop(); ++ ++ //stos ++ __ bind(notBool); ++ __ daddiu(T1, T3, (-1) * stos); ++ __ bne(T1, R0, notShort); ++ __ delayed()->nop(); ++ __ lh(V0, T0, 0); ++ __ b(xreturn_path); ++ __ delayed()->nop(); ++ ++ //ctos ++ __ bind(notShort); ++ __ daddiu(T1, T3, (-1) * ctos); ++ __ bne(T1, R0, notChar); ++ __ delayed()->nop(); ++ __ lhu(V0, T0, 0); ++ __ b(xreturn_path); ++ __ delayed()->nop(); ++ ++ //atos ++ __ bind(notChar); ++ __ daddiu(T1, T3, (-1) * atos); ++ __ bne(T1, R0, notObj); ++ __ delayed()->nop(); ++ //add for compressedoops ++ __ load_heap_oop(V0, Address(T0, 0)); ++ __ b(xreturn_path); ++ __ delayed()->nop(); ++ ++ //itos ++ __ bind(notObj); ++#ifdef ASSERT ++ Label okay; ++ __ daddiu(T1, T3, (-1) * itos); ++ __ beq(T1, R0, okay); ++ __ delayed()->nop(); ++ __ stop("what type is this?"); ++ __ bind(okay); ++#endif // ASSERT ++ __ lw(V0, T0, 0); ++ ++ __ bind(xreturn_path); ++ ++ // _ireturn/_areturn ++ //FIXME ++ __ move(SP, Rsender);//FIXME, set sender's fp to SP ++ __ jr(RA); ++ __ delayed()->nop(); ++ ++ // generate a vanilla interpreter entry as the slow path ++ __ bind(slow_path); ++ (void) generate_normal_entry(false); ++ } else { ++ (void) generate_normal_entry(false); ++ } ++ ++ return entry_point; ++} ++ ++// Method entry for java.lang.ref.Reference.get. ++address InterpreterGenerator::generate_Reference_get_entry(void) { ++#if INCLUDE_ALL_GCS ++ // Code: _aload_0, _getfield, _areturn ++ // parameter size = 1 ++ // ++ // The code that gets generated by this routine is split into 2 parts: ++ // 1. The "intrinsified" code for G1 (or any SATB based GC), ++ // 2. The slow path - which is an expansion of the regular method entry. ++ // ++ // Notes:- ++ // * In the G1 code we do not check whether we need to block for ++ // a safepoint. If G1 is enabled then we must execute the specialized ++ // code for Reference.get (except when the Reference object is null) ++ // so that we can log the value in the referent field with an SATB ++ // update buffer. ++ // If the code for the getfield template is modified so that the ++ // G1 pre-barrier code is executed when the current method is ++ // Reference.get() then going through the normal method entry ++ // will be fine. ++ // * The G1 code can, however, check the receiver object (the instance ++ // of java.lang.Reference) and jump to the slow path if null. If the ++ // Reference object is null then we obviously cannot fetch the referent ++ // and so we don't need to call the G1 pre-barrier. Thus we can use the ++ // regular method entry code to generate the NPE. ++ // ++ // This code is based on generate_accessor_enty. ++ // ++ // Rmethod: Method* ++ ++ // Rsender: senderSP must preserve for slow path, set SP to it on fast path (Rsender) ++ ++ address entry = __ pc(); ++ ++ const int referent_offset = java_lang_ref_Reference::referent_offset; ++ guarantee(referent_offset > 0, "referent offset not initialized"); ++ ++ if (UseG1GC) { ++ Label slow_path; ++ ++ // Check if local 0 != NULL ++ // If the receiver is null then it is OK to jump to the slow path. ++ __ ld(V0, SP, 0); ++ ++ __ beq(V0, R0, slow_path); ++ __ delayed()->nop(); ++ ++ // Generate the G1 pre-barrier code to log the value of ++ // the referent field in an SATB buffer. ++ ++ // Load the value of the referent field. ++ const Address field_address(V0, referent_offset); ++ __ load_heap_oop(V0, field_address); ++ ++ __ push(RA); ++ // Generate the G1 pre-barrier code to log the value of ++ // the referent field in an SATB buffer. ++ __ g1_write_barrier_pre(noreg /* obj */, ++ V0 /* pre_val */, ++ TREG /* thread */, ++ Rmethod /* tmp */, ++ true /* tosca_live */, ++ true /* expand_call */); ++ __ pop(RA); ++ ++ __ jr(RA); ++ __ delayed()->daddu(SP, Rsender, R0); // set sp to sender sp ++ ++ // generate a vanilla interpreter entry as the slow path ++ __ bind(slow_path); ++ (void) generate_normal_entry(false); ++ ++ return entry; ++ } ++#endif // INCLUDE_ALL_GCS ++ ++ // If G1 is not enabled then attempt to go through the accessor entry point ++ // Reference.get is an accessor ++ return generate_accessor_entry(); ++} ++ ++// Interpreter stub for calling a native method. (asm interpreter) ++// This sets up a somewhat different looking stack for calling the ++// native method than the typical interpreter frame setup. ++address InterpreterGenerator::generate_native_entry(bool synchronized) { ++ // determine code generation flags ++ bool inc_counter = UseCompiler || CountCompiledCalls; ++ // Rsender: sender's sp ++ // Rmethod: Method* ++ address entry_point = __ pc(); ++ ++#ifndef CORE ++ const Address invocation_counter(Rmethod,in_bytes(MethodCounters::invocation_counter_offset() + ++ InvocationCounter::counter_offset())); ++#endif ++ ++ // get parameter size (always needed) ++ // the size in the java stack ++ __ ld(V0, Rmethod, in_bytes(Method::const_offset())); ++ __ lhu(V0, V0, in_bytes(ConstMethod::size_of_parameters_offset())); ++ ++ // native calls don't need the stack size check since they have no expression stack ++ // and the arguments are already on the stack and we only add a handful of words ++ // to the stack ++ ++ // Rmethod: Method* ++ // V0: size of parameters ++ // Layout of frame at this point ++ // ++ // [ argument word n-1 ] <--- sp ++ // ... ++ // [ argument word 0 ] ++ ++ // for natives the size of locals is zero ++ ++ // compute beginning of parameters (S7) ++ __ dsll(LVP, V0, Address::times_8); ++ __ daddiu(LVP, LVP, (-1) * wordSize); ++ __ daddu(LVP, LVP, SP); ++ ++ ++ // add 2 zero-initialized slots for native calls ++ // 1 slot for native oop temp offset (setup via runtime) ++ // 1 slot for static native result handler3 (setup via runtime) ++ __ push2(R0, R0); ++ ++ // Layout of frame at this point ++ // [ method holder mirror ] <--- sp ++ // [ result type info ] ++ // [ argument word n-1 ] <--- T0 ++ // ... ++ // [ argument word 0 ] <--- LVP ++ ++ ++#ifndef CORE ++ if (inc_counter) __ lw(T3, invocation_counter); // (pre-)fetch invocation count ++#endif ++ ++ // initialize fixed part of activation frame ++ generate_fixed_frame(true); ++ // after this function, the layout of frame is as following ++ // ++ // [ monitor block top ] <--- sp ( the top monitor entry ) ++ // [ byte code pointer (0) ] (if native, bcp = 0) ++ // [ constant pool cache ] ++ // [ Method* ] ++ // [ locals offset ] ++ // [ sender's sp ] ++ // [ sender's fp ] ++ // [ return address ] <--- fp ++ // [ method holder mirror ] ++ // [ result type info ] ++ // [ argumnet word n-1 ] <--- sender's sp ++ // ... ++ // [ argument word 0 ] <--- S7 ++ ++ ++ // make sure method is native & not abstract ++#ifdef ASSERT ++ __ lw(T0, Rmethod, in_bytes(Method::access_flags_offset())); ++ { ++ Label L; ++ __ andi(AT, T0, JVM_ACC_NATIVE); ++ __ bne(AT, R0, L); ++ __ delayed()->nop(); ++ __ stop("tried to execute native method as non-native"); ++ __ bind(L); ++ } ++ { ++ Label L; ++ __ andi(AT, T0, JVM_ACC_ABSTRACT); ++ __ beq(AT, R0, L); ++ __ delayed()->nop(); ++ __ stop("tried to execute abstract method in interpreter"); ++ __ bind(L); ++ } ++#endif ++ ++ // Since at this point in the method invocation the exception handler ++ // would try to exit the monitor of synchronized methods which hasn't ++ // been entered yet, we set the thread local variable ++ // _do_not_unlock_if_synchronized to true. The remove_activation will ++ // check this flag. ++ Register thread = TREG; ++#ifndef OPT_THREAD ++ __ get_thread(thread); ++#endif ++ __ move(AT, (int)true); ++ __ sb(AT, thread, in_bytes(JavaThread::do_not_unlock_if_synchronized_offset())); ++ ++#ifndef CORE ++ // increment invocation count & check for overflow ++ Label invocation_counter_overflow; ++ if (inc_counter) { ++ generate_counter_incr(&invocation_counter_overflow, NULL, NULL); ++ } ++ ++ Label continue_after_compile; ++ __ bind(continue_after_compile); ++#endif // CORE ++ ++ bang_stack_shadow_pages(true); ++ ++ // reset the _do_not_unlock_if_synchronized flag ++#ifndef OPT_THREAD ++ __ get_thread(thread); ++#endif ++ __ sb(R0, thread, in_bytes(JavaThread::do_not_unlock_if_synchronized_offset())); ++ ++ // check for synchronized methods ++ // Must happen AFTER invocation_counter check and stack overflow check, ++ // so method is not locked if overflows. ++ if (synchronized) { ++ lock_method(); ++ } else { ++ // no synchronization necessary ++#ifdef ASSERT ++ { ++ Label L; ++ __ lw(T0, Rmethod, in_bytes(Method::access_flags_offset())); ++ __ andi(AT, T0, JVM_ACC_SYNCHRONIZED); ++ __ beq(AT, R0, L); ++ __ delayed()->nop(); ++ __ stop("method needs synchronization"); ++ __ bind(L); ++ } ++#endif ++ } ++ ++ // after method_lock, the layout of frame is as following ++ // ++ // [ monitor entry ] <--- sp ++ // ... ++ // [ monitor entry ] ++ // [ monitor block top ] ( the top monitor entry ) ++ // [ byte code pointer (0) ] (if native, bcp = 0) ++ // [ constant pool cache ] ++ // [ Method* ] ++ // [ locals offset ] ++ // [ sender's sp ] ++ // [ sender's fp ] ++ // [ return address ] <--- fp ++ // [ method holder mirror ] ++ // [ result type info ] ++ // [ argumnet word n-1 ] <--- ( sender's sp ) ++ // ... ++ // [ argument word 0 ] <--- S7 ++ ++ // start execution ++#ifdef ASSERT ++ { ++ Label L; ++ __ ld(AT, FP, frame::interpreter_frame_monitor_block_top_offset * wordSize); ++ __ beq(AT, SP, L); ++ __ delayed()->nop(); ++ __ stop("broken stack frame setup in interpreter in asm"); ++ __ bind(L); ++ } ++#endif ++ ++ // jvmti/jvmpi support ++ __ notify_method_entry(); ++ ++ // work registers ++ const Register method = Rmethod; ++ //const Register thread = T2; ++ const Register t = T8; ++ ++ __ get_method(method); ++ __ verify_oop(method); ++ { ++ Label L, Lstatic; ++ __ ld(t,method,in_bytes(Method::const_offset())); ++ __ lhu(t, t, in_bytes(ConstMethod::size_of_parameters_offset())); ++ // MIPS n64 ABI: caller does not reserve space for the register auguments. ++ // A0 and A1(if needed) ++ __ lw(AT, Rmethod, in_bytes(Method::access_flags_offset())); ++ __ andi(AT, AT, JVM_ACC_STATIC); ++ __ beq(AT, R0, Lstatic); ++ __ delayed()->nop(); ++ __ daddiu(t, t, 1); ++ __ bind(Lstatic); ++ __ daddiu(t, t, -7); ++ __ blez(t, L); ++ __ delayed()->nop(); ++ __ dsll(t, t, Address::times_8); ++ __ dsubu(SP, SP, t); ++ __ bind(L); ++ } ++ __ move(AT, -(StackAlignmentInBytes)); ++ __ andr(SP, SP, AT); ++ __ move(AT, SP); ++ // [ ] <--- sp ++ // ... (size of parameters - 8 ) ++ // [ monitor entry ] ++ // ... ++ // [ monitor entry ] ++ // [ monitor block top ] ( the top monitor entry ) ++ // [ byte code pointer (0) ] (if native, bcp = 0) ++ // [ constant pool cache ] ++ // [ Method* ] ++ // [ locals offset ] ++ // [ sender's sp ] ++ // [ sender's fp ] ++ // [ return address ] <--- fp ++ // [ method holder mirror ] ++ // [ result type info ] ++ // [ argumnet word n-1 ] <--- ( sender's sp ) ++ // ... ++ // [ argument word 0 ] <--- LVP ++ ++ // get signature handler ++ { ++ Label L; ++ __ ld(T9, method, in_bytes(Method::signature_handler_offset())); ++ __ bne(T9, R0, L); ++ __ delayed()->nop(); ++ __ call_VM(NOREG, CAST_FROM_FN_PTR(address, ++ InterpreterRuntime::prepare_native_call), method); ++ __ get_method(method); ++ __ ld(T9, method, in_bytes(Method::signature_handler_offset())); ++ __ bind(L); ++ } ++ ++ // call signature handler ++ // FIXME: when change codes in InterpreterRuntime, note this point ++ // from: begin of parameters ++ assert(InterpreterRuntime::SignatureHandlerGenerator::from() == LVP, "adjust this code"); ++ // to: current sp ++ assert(InterpreterRuntime::SignatureHandlerGenerator::to () == SP, "adjust this code"); ++ // temp: T3 ++ assert(InterpreterRuntime::SignatureHandlerGenerator::temp() == t , "adjust this code"); ++ ++ __ jalr(T9); ++ __ delayed()->nop(); ++ __ get_method(method); ++ ++ // ++ // if native function is static, and its second parameter has type length of double word, ++ // and first parameter has type length of word, we have to reserve one word ++ // for the first parameter, according to mips o32 abi. ++ // if native function is not static, and its third parameter has type length of double word, ++ // and second parameter has type length of word, we have to reserve one word for the second ++ // parameter. ++ // ++ ++ ++ // result handler is in V0 ++ // set result handler ++ __ sd(V0, FP, (frame::interpreter_frame_result_handler_offset)*wordSize); ++ ++#define FIRSTPARA_SHIFT_COUNT 5 ++#define SECONDPARA_SHIFT_COUNT 9 ++#define THIRDPARA_SHIFT_COUNT 13 ++#define PARA_MASK 0xf ++ ++ // pass mirror handle if static call ++ { ++ Label L; ++ const int mirror_offset = in_bytes(Klass::java_mirror_offset()); ++ __ lw(t, method, in_bytes(Method::access_flags_offset())); ++ __ andi(AT, t, JVM_ACC_STATIC); ++ __ beq(AT, R0, L); ++ __ delayed()->nop(); ++ ++ // get mirror ++ __ ld(t, method, in_bytes(Method:: const_offset())); ++ __ ld(t, t, in_bytes(ConstMethod::constants_offset())); //?? ++ __ ld(t, t, ConstantPool::pool_holder_offset_in_bytes()); ++ __ ld(t, t, mirror_offset); ++ // copy mirror into activation frame ++ //__ sw(t, FP, frame::interpreter_frame_oop_temp_offset * wordSize); ++ // pass handle to mirror ++ __ sd(t, FP, frame::interpreter_frame_oop_temp_offset * wordSize); ++ __ daddiu(t, FP, frame::interpreter_frame_oop_temp_offset * wordSize); ++ __ move(A1, t); ++ __ bind(L); ++ } ++ ++ // [ mthd holder mirror ptr ] <--- sp --------------------| (only for static method) ++ // [ ] | ++ // ... size of parameters(or +1) | ++ // [ monitor entry ] | ++ // ... | ++ // [ monitor entry ] | ++ // [ monitor block top ] ( the top monitor entry ) | ++ // [ byte code pointer (0) ] (if native, bcp = 0) | ++ // [ constant pool cache ] | ++ // [ Method* ] | ++ // [ locals offset ] | ++ // [ sender's sp ] | ++ // [ sender's fp ] | ++ // [ return address ] <--- fp | ++ // [ method holder mirror ] <----------------------------| ++ // [ result type info ] ++ // [ argumnet word n-1 ] <--- ( sender's sp ) ++ // ... ++ // [ argument word 0 ] <--- S7 ++ ++ // get native function entry point ++ { Label L; ++ __ ld(T9, method, in_bytes(Method::native_function_offset())); ++ __ li(V1, SharedRuntime::native_method_throw_unsatisfied_link_error_entry()); ++ __ bne(V1, T9, L); ++ __ delayed()->nop(); ++ __ call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::prepare_native_call), method); ++ __ get_method(method); ++ __ verify_oop(method); ++ __ ld(T9, method, in_bytes(Method::native_function_offset())); ++ __ bind(L); ++ } ++ ++ // pass JNIEnv ++ // native function in T9 ++#ifndef OPT_THREAD ++ __ get_thread(thread); ++#endif ++ __ daddiu(t, thread, in_bytes(JavaThread::jni_environment_offset())); ++ __ move(A0, t); ++ // [ jni environment ] <--- sp ++ // [ mthd holder mirror ptr ] ---------------------------->| (only for static method) ++ // [ ] | ++ // ... size of parameters | ++ // [ monitor entry ] | ++ // ... | ++ // [ monitor entry ] | ++ // [ monitor block top ] ( the top monitor entry ) | ++ // [ byte code pointer (0) ] (if native, bcp = 0) | ++ // [ constant pool cache ] | ++ // [ Method* ] | ++ // [ locals offset ] | ++ // [ sender's sp ] | ++ // [ sender's fp ] | ++ // [ return address ] <--- fp | ++ // [ method holder mirror ] <----------------------------| ++ // [ result type info ] ++ // [ argumnet word n-1 ] <--- ( sender's sp ) ++ // ... ++ // [ argument word 0 ] <--- S7 ++ ++ // set_last_Java_frame_before_call ++ __ sd(FP, thread, in_bytes(JavaThread::last_Java_fp_offset())); ++ // Change state to native (we save the return address in the thread, since it might not ++ // be pushed on the stack when we do a a stack traversal). It is enough that the pc() ++ // points into the right code segment. It does not have to be the correct return pc. ++ __ li(t, __ pc()); ++ __ sd(t, thread, in_bytes(JavaThread::last_Java_pc_offset())); ++ __ sd(SP, thread, in_bytes(JavaThread::last_Java_sp_offset())); ++ ++ // change thread state ++#ifdef ASSERT ++ { ++ Label L; ++ __ lw(t, thread, in_bytes(JavaThread::thread_state_offset())); ++ __ daddiu(t, t, (-1) * _thread_in_Java); ++ __ beq(t, R0, L); ++ __ delayed()->nop(); ++ __ stop("Wrong thread state in native stub"); ++ __ bind(L); ++ } ++#endif ++ ++ __ move(t, _thread_in_native); ++ if(os::is_MP()) { ++ __ sync(); // store release ++ } ++ __ sw(t, thread, in_bytes(JavaThread::thread_state_offset())); ++ ++ // call native method ++ __ jalr(T9); ++ __ delayed()->nop(); ++ // result potentially in V0 or F0 ++ ++ ++ // via _last_native_pc and not via _last_jave_sp ++ // NOTE: the order of theses push(es) is known to frame::interpreter_frame_result. ++ // If the order changes or anything else is added to the stack the code in ++ // interpreter_frame_result will have to be changed. ++ //FIXME, should modify here ++ // save return value to keep the value from being destroyed by other calls ++ __ push(dtos); ++ __ push(ltos); ++ ++ // change thread state ++ __ get_thread(thread); ++ __ move(t, _thread_in_native_trans); ++ if(os::is_MP()) { ++ __ sync(); // store release ++ } ++ __ sw(t, thread, in_bytes(JavaThread::thread_state_offset())); ++ ++ if(os::is_MP()) { ++ if (UseMembar) { ++ // Force this write out before the read below ++ __ sync(); ++ } else { ++ // Write serialization page so VM thread can do a pseudo remote membar. ++ // We use the current thread pointer to calculate a thread specific ++ // offset to write to within the page. This minimizes bus traffic ++ // due to cache line collision. ++ __ serialize_memory(thread, A0); ++ } ++ } ++ ++ // check for safepoint operation in progress and/or pending suspend requests ++ { Label Continue; ++ ++ // Don't use call_VM as it will see a possible pending exception and forward it ++ // and never return here preventing us from clearing _last_native_pc down below. ++ // Also can't use call_VM_leaf either as it will check to see if BCP & LVP are ++ // preserved and correspond to the bcp/locals pointers. So we do a runtime call ++ // by hand. ++ // ++ Label L; ++ __ li(AT, SafepointSynchronize::address_of_state()); ++ __ lw(AT, AT, 0); ++ __ bne(AT, R0, L); ++ __ delayed()->nop(); ++ __ lw(AT, thread, in_bytes(JavaThread::suspend_flags_offset())); ++ __ beq(AT, R0, Continue); ++ __ delayed()->nop(); ++ __ bind(L); ++ __ move(A0, thread); ++ __ call(CAST_FROM_FN_PTR(address, JavaThread::check_special_condition_for_native_trans), ++ relocInfo::runtime_call_type); ++ __ delayed()->nop(); ++ ++#ifndef OPT_THREAD ++ __ get_thread(thread); ++#endif ++ //add for compressedoops ++ __ reinit_heapbase(); ++ __ bind(Continue); ++ } ++ ++ // change thread state ++ __ move(t, _thread_in_Java); ++ if(os::is_MP()) { ++ __ sync(); // store release ++ } ++ __ sw(t, thread, in_bytes(JavaThread::thread_state_offset())); ++ __ reset_last_Java_frame(thread, true); ++ ++ // reset handle block ++ __ ld(t, thread, in_bytes(JavaThread::active_handles_offset())); ++ __ sw(R0, t, JNIHandleBlock::top_offset_in_bytes()); ++ ++ // If result was an oop then unbox and save it in the frame ++ { ++ Label no_oop; ++ //FIXME, addiu only support 16-bit imeditate ++ __ ld(AT, FP, frame::interpreter_frame_result_handler_offset*wordSize); ++ __ li(T0, AbstractInterpreter::result_handler(T_OBJECT)); ++ __ bne(AT, T0, no_oop); ++ __ delayed()->nop(); ++ __ pop(ltos); ++ // Unbox oop result, e.g. JNIHandles::resolve value. ++ __ resolve_jobject(V0, thread, T9); ++ __ sd(V0, FP, (frame::interpreter_frame_oop_temp_offset)*wordSize); ++ // keep stack depth as expected by pushing oop which will eventually be discarded ++ __ push(ltos); ++ __ bind(no_oop); ++ } ++ { ++ Label no_reguard; ++ __ lw(t, thread, in_bytes(JavaThread::stack_guard_state_offset())); ++ __ move(AT,(int) JavaThread::stack_guard_yellow_disabled); ++ __ bne(t, AT, no_reguard); ++ __ delayed()->nop(); ++ __ pushad(); ++ __ move(S5_heapbase, SP); ++ __ move(AT, -StackAlignmentInBytes); ++ __ andr(SP, SP, AT); ++ __ call(CAST_FROM_FN_PTR(address, SharedRuntime::reguard_yellow_pages), relocInfo::runtime_call_type); ++ __ delayed()->nop(); ++ __ move(SP, S5_heapbase); ++ __ popad(); ++ //add for compressedoops ++ __ reinit_heapbase(); ++ __ bind(no_reguard); ++ } ++ // restore BCP to have legal interpreter frame, ++ // i.e., bci == 0 <=> BCP == code_base() ++ // Can't call_VM until bcp is within reasonable. ++ __ get_method(method); // method is junk from thread_in_native to now. ++ __ verify_oop(method); ++ __ ld(BCP, method, in_bytes(Method::const_offset())); ++ __ lea(BCP, Address(BCP, in_bytes(ConstMethod::codes_offset()))); ++ // handle exceptions (exception handling will handle unlocking!) ++ { ++ Label L; ++ __ ld(t, thread, in_bytes(Thread::pending_exception_offset())); ++ __ beq(t, R0, L); ++ __ delayed()->nop(); ++ // Note: At some point we may want to unify this with the code used in ++ // call_VM_base(); ++ // i.e., we should use the StubRoutines::forward_exception code. For now this ++ // doesn't work here because the sp is not correctly set at this point. ++ __ MacroAssembler::call_VM(noreg, ++ CAST_FROM_FN_PTR(address, ++ InterpreterRuntime::throw_pending_exception)); ++ __ should_not_reach_here(); ++ __ bind(L); ++ } ++ ++ // do unlocking if necessary ++ { ++ Label L; ++ __ lw(t, method, in_bytes(Method::access_flags_offset())); ++ __ andi(t, t, JVM_ACC_SYNCHRONIZED); ++ __ beq(t, R0, L); ++ // the code below should be shared with interpreter macro assembler implementation ++ { ++ Label unlock; ++ // BasicObjectLock will be first in list, ++ // since this is a synchronized method. However, need ++ // to check that the object has not been unlocked by ++ // an explicit monitorexit bytecode. ++ __ delayed()->daddiu(c_rarg0, FP, frame::interpreter_frame_initial_sp_offset * wordSize - (int)sizeof(BasicObjectLock)); ++ // address of first monitor ++ ++ __ ld(t, c_rarg0, BasicObjectLock::obj_offset_in_bytes()); ++ __ bne(t, R0, unlock); ++ __ delayed()->nop(); ++ ++ // Entry already unlocked, need to throw exception ++ __ MacroAssembler::call_VM(NOREG, CAST_FROM_FN_PTR(address, ++ InterpreterRuntime::throw_illegal_monitor_state_exception)); ++ __ should_not_reach_here(); ++ ++ __ bind(unlock); ++ __ unlock_object(c_rarg0); ++ } ++ __ bind(L); ++ } ++ ++ // jvmti/jvmpi support ++ // Note: This must happen _after_ handling/throwing any exceptions since ++ // the exception handler code notifies the runtime of method exits ++ // too. If this happens before, method entry/exit notifications are ++ // not properly paired (was bug - gri 11/22/99). ++ __ notify_method_exit(vtos, InterpreterMacroAssembler::NotifyJVMTI); ++ ++ // restore potential result in V0, ++ // call result handler to restore potential result in ST0 & handle result ++ ++ __ pop(ltos); ++ __ pop(dtos); ++ ++ __ ld(t, FP, (frame::interpreter_frame_result_handler_offset) * wordSize); ++ __ jalr(t); ++ __ delayed()->nop(); ++ ++ ++ // remove activation ++ __ ld(SP, FP, frame::interpreter_frame_sender_sp_offset * wordSize); // get sender sp ++ __ ld(RA, FP, frame::interpreter_frame_return_addr_offset * wordSize); // get return address ++ __ ld(FP, FP, frame::interpreter_frame_sender_fp_offset * wordSize); // restore sender's fp ++ __ jr(RA); ++ __ delayed()->nop(); ++ ++#ifndef CORE ++ if (inc_counter) { ++ // Handle overflow of counter and compile method ++ __ bind(invocation_counter_overflow); ++ generate_counter_overflow(&continue_after_compile); ++ // entry_point is the beginning of this ++ // function and checks again for compiled code ++ } ++#endif ++ return entry_point; ++} ++ ++// ++// Generic interpreted method entry to (asm) interpreter ++// ++// Layout of frame just at the entry ++// ++// [ argument word n-1 ] <--- sp ++// ... ++// [ argument word 0 ] ++// assume Method* in Rmethod before call this method. ++// prerequisites to the generated stub : the callee Method* in Rmethod ++// note you must save the caller bcp before call the generated stub ++// ++address InterpreterGenerator::generate_normal_entry(bool synchronized) { ++ // determine code generation flags ++ bool inc_counter = UseCompiler || CountCompiledCalls; ++ ++ // Rmethod: Method* ++ // Rsender: sender 's sp ++ address entry_point = __ pc(); ++ ++ const Address invocation_counter(Rmethod, ++ in_bytes(MethodCounters::invocation_counter_offset() + InvocationCounter::counter_offset())); ++ ++ // get parameter size (always needed) ++ __ ld(T3, Rmethod, in_bytes(Method::const_offset())); //T3 --> Rmethod._constMethod ++ __ lhu(V0, T3, in_bytes(ConstMethod::size_of_parameters_offset())); ++ ++ // Rmethod: Method* ++ // V0: size of parameters ++ // Rsender: sender 's sp ,could be different frome sp+ wordSize if we call via c2i ++ // get size of locals in words to T2 ++ __ lhu(T2, T3, in_bytes(ConstMethod::size_of_locals_offset())); ++ // T2 = no. of additional locals, locals include parameters ++ __ dsubu(T2, T2, V0); ++ ++ // see if we've got enough room on the stack for locals plus overhead. ++ // Layout of frame at this point ++ // ++ // [ argument word n-1 ] <--- sp ++ // ... ++ // [ argument word 0 ] ++ generate_stack_overflow_check(); ++ // after this function, the layout of frame does not change ++ ++ // compute beginning of parameters (LVP) ++ __ dsll(LVP, V0, LogBytesPerWord); ++ __ daddiu(LVP, LVP, (-1) * wordSize); ++ __ daddu(LVP, LVP, SP); ++ ++ // T2 - # of additional locals ++ // allocate space for locals ++ // explicitly initialize locals ++ { ++ Label exit, loop; ++ __ beq(T2, R0, exit); ++ __ delayed()->nop(); ++ ++ __ bind(loop); ++ __ daddiu(SP, SP, (-1) * wordSize); ++ __ daddiu(T2, T2, -1); // until everything initialized ++ __ bne(T2, R0, loop); ++ __ delayed()->sd(R0, SP, 0); // initialize local variables ++ ++ __ bind(exit); ++ } ++ ++ // ++ // [ local var m-1 ] <--- sp ++ // ... ++ // [ local var 0 ] ++ // [ argument word n-1 ] <--- T0? ++ // ... ++ // [ argument word 0 ] <--- LVP ++ ++ // initialize fixed part of activation frame ++ ++ generate_fixed_frame(false); ++ ++ ++ // after this function, the layout of frame is as following ++ // ++ // [ monitor block top ] <--- sp ( the top monitor entry ) ++ // [ byte code pointer ] (if native, bcp = 0) ++ // [ constant pool cache ] ++ // [ Method* ] ++ // [ locals offset ] ++ // [ sender's sp ] ++ // [ sender's fp ] <--- fp ++ // [ return address ] ++ // [ local var m-1 ] ++ // ... ++ // [ local var 0 ] ++ // [ argumnet word n-1 ] <--- ( sender's sp ) ++ // ... ++ // [ argument word 0 ] <--- LVP ++ ++ ++ // make sure method is not native & not abstract ++#ifdef ASSERT ++ __ ld(AT, Rmethod, in_bytes(Method::access_flags_offset())); ++ { ++ Label L; ++ __ andi(T2, AT, JVM_ACC_NATIVE); ++ __ beq(T2, R0, L); ++ __ delayed()->nop(); ++ __ stop("tried to execute native method as non-native"); ++ __ bind(L); ++ } ++ { ++ Label L; ++ __ andi(T2, AT, JVM_ACC_ABSTRACT); ++ __ beq(T2, R0, L); ++ __ delayed()->nop(); ++ __ stop("tried to execute abstract method in interpreter"); ++ __ bind(L); ++ } ++#endif ++ ++ // Since at this point in the method invocation the exception handler ++ // would try to exit the monitor of synchronized methods which hasn't ++ // been entered yet, we set the thread local variable ++ // _do_not_unlock_if_synchronized to true. The remove_activation will ++ // check this flag. ++ ++#ifndef OPT_THREAD ++ Register thread = T8; ++ __ get_thread(thread); ++#else ++ Register thread = TREG; ++#endif ++ __ move(AT, (int)true); ++ __ sb(AT, thread, in_bytes(JavaThread::do_not_unlock_if_synchronized_offset())); ++ ++#ifndef CORE ++ ++ // mdp : T8 ++ // tmp1: T9 ++ // tmp2: T2 ++ __ profile_parameters_type(T8, T9, T2); ++ ++ // increment invocation count & check for overflow ++ Label invocation_counter_overflow; ++ Label profile_method; ++ Label profile_method_continue; ++ if (inc_counter) { ++ generate_counter_incr(&invocation_counter_overflow, ++ &profile_method, ++ &profile_method_continue); ++ if (ProfileInterpreter) { ++ __ bind(profile_method_continue); ++ } ++ } ++ ++ Label continue_after_compile; ++ __ bind(continue_after_compile); ++ ++#endif // CORE ++ ++ bang_stack_shadow_pages(false); ++ ++ // reset the _do_not_unlock_if_synchronized flag ++#ifndef OPT_THREAD ++ __ get_thread(thread); ++#endif ++ __ sb(R0, thread, in_bytes(JavaThread::do_not_unlock_if_synchronized_offset())); ++ ++ // check for synchronized methods ++ // Must happen AFTER invocation_counter check and stack overflow check, ++ // so method is not locked if overflows. ++ // ++ if (synchronized) { ++ // Allocate monitor and lock method ++ lock_method(); ++ } else { ++ // no synchronization necessary ++#ifdef ASSERT ++ { Label L; ++ __ lw(AT, Rmethod, in_bytes(Method::access_flags_offset())); ++ __ andi(T2, AT, JVM_ACC_SYNCHRONIZED); ++ __ beq(T2, R0, L); ++ __ delayed()->nop(); ++ __ stop("method needs synchronization"); ++ __ bind(L); ++ } ++#endif ++ } ++ ++ // layout of frame after lock_method ++ // [ monitor entry ] <--- sp ++ // ... ++ // [ monitor entry ] ++ // [ monitor block top ] ( the top monitor entry ) ++ // [ byte code pointer ] (if native, bcp = 0) ++ // [ constant pool cache ] ++ // [ Method* ] ++ // [ locals offset ] ++ // [ sender's sp ] ++ // [ sender's fp ] ++ // [ return address ] <--- fp ++ // [ local var m-1 ] ++ // ... ++ // [ local var 0 ] ++ // [ argumnet word n-1 ] <--- ( sender's sp ) ++ // ... ++ // [ argument word 0 ] <--- LVP ++ ++ ++ // start execution ++#ifdef ASSERT ++ { ++ Label L; ++ __ ld(AT, FP, frame::interpreter_frame_monitor_block_top_offset * wordSize); ++ __ beq(AT, SP, L); ++ __ delayed()->nop(); ++ __ stop("broken stack frame setup in interpreter in native"); ++ __ bind(L); ++ } ++#endif ++ ++ // jvmti/jvmpi support ++ __ notify_method_entry(); ++ ++ __ dispatch_next(vtos); ++ ++ // invocation counter overflow ++ if (inc_counter) { ++ if (ProfileInterpreter) { ++ // We have decided to profile this method in the interpreter ++ __ bind(profile_method); ++ __ call_VM(noreg, CAST_FROM_FN_PTR(address, ++ InterpreterRuntime::profile_method)); ++ __ set_method_data_pointer_for_bcp(); ++ __ get_method(Rmethod); ++ __ b(profile_method_continue); ++ __ delayed()->nop(); ++ } ++ // Handle overflow of counter and compile method ++ __ bind(invocation_counter_overflow); ++ generate_counter_overflow(&continue_after_compile); ++ } ++ ++ return entry_point; ++} ++ ++// Entry points ++// ++// Here we generate the various kind of entries into the interpreter. ++// The two main entry type are generic bytecode methods and native ++// call method. These both come in synchronized and non-synchronized ++// versions but the frame layout they create is very similar. The ++// other method entry types are really just special purpose entries ++// that are really entry and interpretation all in one. These are for ++// trivial methods like accessor, empty, or special math methods. ++// ++// When control flow reaches any of the entry types for the interpreter ++// the following holds -> ++// ++// Arguments: ++// ++// Rmethod: Method* ++// V0: receiver ++// ++// ++// Stack layout immediately at entry ++// ++// [ parameter n-1 ] <--- sp ++// ... ++// [ parameter 0 ] ++// [ expression stack ] (caller's java expression stack) ++ ++// Assuming that we don't go to one of the trivial specialized entries ++// the stack will look like below when we are ready to execute the ++// first bytecode (or call the native routine). The register usage ++// will be as the template based interpreter expects (see ++// interpreter_mips_64.hpp). ++// ++// local variables follow incoming parameters immediately; i.e. ++// the return address is moved to the end of the locals). ++// ++// [ monitor entry ] <--- sp ++// ... ++// [ monitor entry ] ++// [ monitor block top ] ( the top monitor entry ) ++// [ byte code pointer ] (if native, bcp = 0) ++// [ constant pool cache ] ++// [ Method* ] ++// [ locals offset ] ++// [ sender's sp ] ++// [ sender's fp ] ++// [ return address ] <--- fp ++// [ local var m-1 ] ++// ... ++// [ local var 0 ] ++// [ argumnet word n-1 ] <--- ( sender's sp ) ++// ... ++// [ argument word 0 ] <--- S7 ++ ++address AbstractInterpreterGenerator::generate_method_entry( ++ AbstractInterpreter::MethodKind kind) { ++ // determine code generation flags ++ bool synchronized = false; ++ address entry_point = NULL; ++ switch (kind) { ++ case Interpreter::zerolocals : ++ break; ++ case Interpreter::zerolocals_synchronized: ++ synchronized = true; ++ break; ++ case Interpreter::native : ++ entry_point = ((InterpreterGenerator*)this)->generate_native_entry(false); ++ break; ++ case Interpreter::native_synchronized : ++ entry_point = ((InterpreterGenerator*)this)->generate_native_entry(true); ++ break; ++ case Interpreter::empty : ++ entry_point = ((InterpreterGenerator*)this)->generate_empty_entry(); ++ break; ++ case Interpreter::accessor : ++ entry_point = ((InterpreterGenerator*)this)->generate_accessor_entry(); ++ break; ++ case Interpreter::abstract : ++ entry_point = ((InterpreterGenerator*)this)->generate_abstract_entry(); ++ break; ++ ++ case Interpreter::java_lang_math_sin : // fall thru ++ case Interpreter::java_lang_math_cos : // fall thru ++ case Interpreter::java_lang_math_tan : // fall thru ++ case Interpreter::java_lang_math_log : // fall thru ++ case Interpreter::java_lang_math_log10 : // fall thru ++ case Interpreter::java_lang_math_pow : // fall thru ++ case Interpreter::java_lang_math_exp : break; ++ case Interpreter::java_lang_math_abs : // fall thru ++ case Interpreter::java_lang_math_sqrt : ++ entry_point = ((InterpreterGenerator*)this)->generate_math_entry(kind); break; ++ case Interpreter::java_lang_ref_reference_get: ++ entry_point = ((InterpreterGenerator*)this)->generate_Reference_get_entry(); break; ++ default: ++ fatal(err_msg("unexpected method kind: %d", kind)); ++ break; ++ } ++ if (entry_point) return entry_point; ++ ++ return ((InterpreterGenerator*)this)->generate_normal_entry(synchronized); ++} ++ ++// These should never be compiled since the interpreter will prefer ++// the compiled version to the intrinsic version. ++bool AbstractInterpreter::can_be_compiled(methodHandle m) { ++ switch (method_kind(m)) { ++ case Interpreter::java_lang_math_sin : // fall thru ++ case Interpreter::java_lang_math_cos : // fall thru ++ case Interpreter::java_lang_math_tan : // fall thru ++ case Interpreter::java_lang_math_abs : // fall thru ++ case Interpreter::java_lang_math_log : // fall thru ++ case Interpreter::java_lang_math_log10 : // fall thru ++ case Interpreter::java_lang_math_sqrt : // fall thru ++ case Interpreter::java_lang_math_pow : // fall thru ++ case Interpreter::java_lang_math_exp : ++ return false; ++ default: ++ return true; ++ } ++} ++ ++// How much stack a method activation needs in words. ++int AbstractInterpreter::size_top_interpreter_activation(Method* method) { ++ ++ const int entry_size = frame::interpreter_frame_monitor_size(); ++ ++ // total overhead size: entry_size + (saved fp thru expr stack bottom). ++ // be sure to change this if you add/subtract anything to/from the overhead area ++ const int overhead_size = -(frame::interpreter_frame_initial_sp_offset) + entry_size; ++ ++ const int stub_code = 6; // see generate_call_stub ++ // return overhead_size + method->max_locals() + method->max_stack() + stub_code; ++ const int method_stack = (method->max_locals() + method->max_stack()) * ++ Interpreter::stackElementWords; ++ return overhead_size + method_stack + stub_code; ++} ++ ++void AbstractInterpreter::layout_activation(Method* method, ++ int tempcount, ++ int popframe_extra_args, ++ int moncount, ++ int caller_actual_parameters, ++ int callee_param_count, ++ int callee_locals, ++ frame* caller, ++ frame* interpreter_frame, ++ bool is_top_frame, ++ bool is_bottom_frame) { ++ // Note: This calculation must exactly parallel the frame setup ++ // in AbstractInterpreterGenerator::generate_method_entry. ++ // If interpreter_frame!=NULL, set up the method, locals, and monitors. ++ // The frame interpreter_frame, if not NULL, is guaranteed to be the ++ // right size, as determined by a previous call to this method. ++ // It is also guaranteed to be walkable even though it is in a skeletal state ++ ++ // fixed size of an interpreter frame: ++ ++ int max_locals = method->max_locals() * Interpreter::stackElementWords; ++ int extra_locals = (method->max_locals() - method->size_of_parameters()) * Interpreter::stackElementWords; ++ ++#ifdef ASSERT ++ if (!EnableInvokeDynamic) { ++ // @@@ FIXME: Should we correct interpreter_frame_sender_sp in the calling sequences? ++ // Probably, since deoptimization doesn't work yet. ++ assert(caller->unextended_sp() == interpreter_frame->interpreter_frame_sender_sp(), "Frame not properly walkable"); ++ } ++ assert(caller->sp() == interpreter_frame->sender_sp(), "Frame not properly walkable(2)"); ++#endif ++ ++ interpreter_frame->interpreter_frame_set_method(method); ++ // NOTE the difference in using sender_sp and interpreter_frame_sender_sp ++ // interpreter_frame_sender_sp is the original sp of the caller (the unextended_sp) ++ // and sender_sp is fp+8 ++ intptr_t* locals = interpreter_frame->sender_sp() + max_locals - 1; ++ ++#ifdef ASSERT ++ if (caller->is_interpreted_frame()) { ++ assert(locals < caller->fp() + frame::interpreter_frame_initial_sp_offset, "bad placement"); ++ } ++#endif ++ ++ interpreter_frame->interpreter_frame_set_locals(locals); ++ BasicObjectLock* montop = interpreter_frame->interpreter_frame_monitor_begin(); ++ BasicObjectLock* monbot = montop - moncount; ++ interpreter_frame->interpreter_frame_set_monitor_end(montop - moncount); ++ ++ //set last sp; ++ intptr_t* sp = (intptr_t*) monbot - tempcount*Interpreter::stackElementWords - ++ popframe_extra_args; ++ interpreter_frame->interpreter_frame_set_last_sp(sp); ++ // All frames but the initial interpreter frame we fill in have a ++ // value for sender_sp that allows walking the stack but isn't ++ // truly correct. Correct the value here. ++ // ++ if (extra_locals != 0 && ++ interpreter_frame->sender_sp() == interpreter_frame->interpreter_frame_sender_sp() ) { ++ interpreter_frame->set_interpreter_frame_sender_sp(caller->sp() + extra_locals); ++ } ++ *interpreter_frame->interpreter_frame_cache_addr() = method->constants()->cache(); ++} ++ ++//----------------------------------------------------------------------------- ++// Exceptions ++ ++void TemplateInterpreterGenerator::generate_throw_exception() { ++ // Entry point in previous activation (i.e., if the caller was ++ // interpreted) ++ Interpreter::_rethrow_exception_entry = __ pc(); ++ // Restore sp to interpreter_frame_last_sp even though we are going ++ // to empty the expression stack for the exception processing. ++ __ sd(R0,FP, frame::interpreter_frame_last_sp_offset * wordSize); ++ ++ // V0: exception ++ // V1: return address/pc that threw exception ++ __ restore_bcp(); // BCP points to call/send ++ __ restore_locals(); ++ ++ //add for compressedoops ++ __ reinit_heapbase(); ++ // Entry point for exceptions thrown within interpreter code ++ Interpreter::_throw_exception_entry = __ pc(); ++ // expression stack is undefined here ++ // V0: exception ++ // BCP: exception bcp ++ __ verify_oop(V0); ++ ++ // expression stack must be empty before entering the VM in case of an exception ++ __ empty_expression_stack(); ++ // find exception handler address and preserve exception oop ++ __ move(A1, V0); ++ __ call_VM(V1, CAST_FROM_FN_PTR(address, InterpreterRuntime::exception_handler_for_exception), A1); ++ // V0: exception handler entry point ++ // V1: preserved exception oop ++ // S0: bcp for exception handler ++ __ push(V1); // push exception which is now the only value on the stack ++ __ jr(V0); // jump to exception handler (may be _remove_activation_entry!) ++ __ delayed()->nop(); ++ ++ // If the exception is not handled in the current frame the frame is removed and ++ // the exception is rethrown (i.e. exception continuation is _rethrow_exception). ++ // ++ // Note: At this point the bci is still the bxi for the instruction which caused ++ // the exception and the expression stack is empty. Thus, for any VM calls ++ // at this point, GC will find a legal oop map (with empty expression stack). ++ ++ // In current activation ++ // V0: exception ++ // BCP: exception bcp ++ ++ // ++ // JVMTI PopFrame support ++ // ++ ++ Interpreter::_remove_activation_preserving_args_entry = __ pc(); ++ __ empty_expression_stack(); ++ // Set the popframe_processing bit in pending_popframe_condition indicating that we are ++ // currently handling popframe, so that call_VMs that may happen later do not trigger new ++ // popframe handling cycles. ++#ifndef OPT_THREAD ++ Register thread = T2; ++ __ get_thread(T2); ++#else ++ Register thread = TREG; ++#endif ++ __ lw(T3, thread, in_bytes(JavaThread::popframe_condition_offset())); ++ __ ori(T3, T3, JavaThread::popframe_processing_bit); ++ __ sw(T3, thread, in_bytes(JavaThread::popframe_condition_offset())); ++ ++#ifndef CORE ++ { ++ // Check to see whether we are returning to a deoptimized frame. ++ // (The PopFrame call ensures that the caller of the popped frame is ++ // either interpreted or compiled and deoptimizes it if compiled.) ++ // In this case, we can't call dispatch_next() after the frame is ++ // popped, but instead must save the incoming arguments and restore ++ // them after deoptimization has occurred. ++ // ++ // Note that we don't compare the return PC against the ++ // deoptimization blob's unpack entry because of the presence of ++ // adapter frames in C2. ++ Label caller_not_deoptimized; ++ __ ld(A0, FP, frame::return_addr_offset * wordSize); ++ __ super_call_VM_leaf(CAST_FROM_FN_PTR(address, InterpreterRuntime::interpreter_contains), A0); ++ __ bne(V0, R0, caller_not_deoptimized); ++ __ delayed()->nop(); ++ ++ // Compute size of arguments for saving when returning to deoptimized caller ++ __ get_method(A1); ++ __ verify_oop(A1); ++ __ ld(A1, A1, in_bytes(Method::const_offset())); ++ __ lhu(A1, A1, in_bytes(ConstMethod::size_of_parameters_offset())); ++ __ shl(A1, Interpreter::logStackElementSize); ++ __ restore_locals(); ++ __ dsubu(A2, LVP, A1); ++ __ daddiu(A2, A2, wordSize); ++ // Save these arguments ++#ifndef OPT_THREAD ++ __ get_thread(A0); ++#else ++ __ move(A0, TREG); ++#endif ++ __ super_call_VM_leaf(CAST_FROM_FN_PTR(address, Deoptimization::popframe_preserve_args), A0, A1, A2); ++ ++ __ remove_activation(vtos, T9, false, false, false); ++ ++ // Inform deoptimization that it is responsible for restoring these arguments ++#ifndef OPT_THREAD ++ __ get_thread(thread); ++#endif ++ __ move(AT, JavaThread::popframe_force_deopt_reexecution_bit); ++ __ sw(AT, thread, in_bytes(JavaThread::popframe_condition_offset())); ++ // Continue in deoptimization handler ++ __ jr(T9); ++ __ delayed()->nop(); ++ ++ __ bind(caller_not_deoptimized); ++ } ++#endif /* !CORE */ ++ ++ __ remove_activation(vtos, T3, ++ /* throw_monitor_exception */ false, ++ /* install_monitor_exception */ false, ++ /* notify_jvmdi */ false); ++ ++ // Clear the popframe condition flag ++ // Finish with popframe handling ++ // A previous I2C followed by a deoptimization might have moved the ++ // outgoing arguments further up the stack. PopFrame expects the ++ // mutations to those outgoing arguments to be preserved and other ++ // constraints basically require this frame to look exactly as ++ // though it had previously invoked an interpreted activation with ++ // no space between the top of the expression stack (current ++ // last_sp) and the top of stack. Rather than force deopt to ++ // maintain this kind of invariant all the time we call a small ++ // fixup routine to move the mutated arguments onto the top of our ++ // expression stack if necessary. ++ __ move(T8, SP); ++ __ ld(A2, FP, frame::interpreter_frame_last_sp_offset * wordSize); ++#ifndef OPT_THREAD ++ __ get_thread(thread); ++#endif ++ // PC must point into interpreter here ++ __ set_last_Java_frame(thread, noreg, FP, __ pc()); ++ __ super_call_VM_leaf(CAST_FROM_FN_PTR(address, InterpreterRuntime::popframe_move_outgoing_args), thread, T8, A2); ++ __ reset_last_Java_frame(thread, true); ++ // Restore the last_sp and null it out ++ __ ld(SP, FP, frame::interpreter_frame_last_sp_offset * wordSize); ++ __ sd(R0, FP, frame::interpreter_frame_last_sp_offset * wordSize); ++ ++ ++ ++ __ move(AT, JavaThread::popframe_inactive); ++ __ sw(AT, thread, in_bytes(JavaThread::popframe_condition_offset())); ++ ++ // Finish with popframe handling ++ __ restore_bcp(); ++ __ restore_locals(); ++#ifndef CORE ++ // The method data pointer was incremented already during ++ // call profiling. We have to restore the mdp for the current bcp. ++ if (ProfileInterpreter) { ++ __ set_method_data_pointer_for_bcp(); ++ } ++#endif // !CORE ++ // Clear the popframe condition flag ++#ifndef OPT_THREAD ++ __ get_thread(thread); ++#endif ++ __ move(AT, JavaThread::popframe_inactive); ++ __ sw(AT, thread, in_bytes(JavaThread::popframe_condition_offset())); ++ ++#if INCLUDE_JVMTI ++ { ++ Label L_done; ++ ++ __ lbu(AT, BCP, 0); ++ __ daddiu(AT, AT, -1 * Bytecodes::_invokestatic); ++ __ bne(AT, R0, L_done); ++ __ delayed()->nop(); ++ ++ // The member name argument must be restored if _invokestatic is re-executed after a PopFrame call. ++ // Detect such a case in the InterpreterRuntime function and return the member name argument, or NULL. ++ ++ __ get_method(T9); ++ __ ld(T8, LVP, 0); ++ __ call_VM(T8, CAST_FROM_FN_PTR(address, InterpreterRuntime::member_name_arg_or_null), T8, T9, BCP); ++ ++ __ beq(T8, R0, L_done); ++ __ delayed()->nop(); ++ ++ __ sd(T8, SP, 0); ++ __ bind(L_done); ++ } ++#endif // INCLUDE_JVMTI ++ ++ __ dispatch_next(vtos); ++ // end of PopFrame support ++ ++ Interpreter::_remove_activation_entry = __ pc(); ++ ++ // preserve exception over this code sequence ++ __ pop(T0); ++#ifndef OPT_THREAD ++ __ get_thread(thread); ++#endif ++ __ sd(T0, thread, in_bytes(JavaThread::vm_result_offset())); ++ // remove the activation (without doing throws on illegalMonitorExceptions) ++ __ remove_activation(vtos, T3, false, true, false); ++ // restore exception ++ __ get_vm_result(T0, thread); ++ __ verify_oop(T0); ++ ++ // In between activations - previous activation type unknown yet ++ // compute continuation point - the continuation point expects ++ // the following registers set up: ++ // ++ // T0: exception ++ // T1: return address/pc that threw exception ++ // SP: expression stack of caller ++ // FP: fp of caller ++ __ push2(T0, T3); // save exception and return address ++ __ move(A1, T3); ++ __ super_call_VM_leaf(CAST_FROM_FN_PTR(address, SharedRuntime::exception_handler_for_return_address), thread, A1); ++ __ move(T9, V0); // save exception handler ++ __ pop2(V0, V1); // restore return address and exception ++ ++ // Note that an "issuing PC" is actually the next PC after the call ++ __ jr(T9); // jump to exception handler of caller ++ __ delayed()->nop(); ++} ++ ++ ++// ++// JVMTI ForceEarlyReturn support ++// ++address TemplateInterpreterGenerator::generate_earlyret_entry_for(TosState state) { ++ address entry = __ pc(); ++ __ restore_bcp(); ++ __ restore_locals(); ++ __ empty_expression_stack(); ++ __ empty_FPU_stack(); ++ __ load_earlyret_value(state); ++ ++#ifndef OPT_THREAD ++ __ get_thread(TREG); ++#endif ++ __ ld_ptr(T9, TREG, in_bytes(JavaThread::jvmti_thread_state_offset())); ++ const Address cond_addr(T9, in_bytes(JvmtiThreadState::earlyret_state_offset())); ++ // Clear the earlyret state ++ __ move(AT, JvmtiThreadState::earlyret_inactive); ++ __ sw(AT, cond_addr); ++ __ sync(); ++ ++ ++ __ remove_activation(state, T0, ++ false, /* throw_monitor_exception */ ++ false, /* install_monitor_exception */ ++ true); /* notify_jvmdi */ ++ __ sync(); ++ __ jr(T0); ++ __ delayed()->nop(); ++ return entry; ++} // end of ForceEarlyReturn support ++ ++ ++//----------------------------------------------------------------------------- ++// Helper for vtos entry point generation ++ ++void TemplateInterpreterGenerator::set_vtos_entry_points(Template* t, ++ address& bep, ++ address& cep, ++ address& sep, ++ address& aep, ++ address& iep, ++ address& lep, ++ address& fep, ++ address& dep, ++ address& vep) { ++ assert(t->is_valid() && t->tos_in() == vtos, "illegal template"); ++ Label L; ++ fep = __ pc(); __ push(ftos); __ b(L); __ delayed()->nop(); ++ dep = __ pc(); __ push(dtos); __ b(L); __ delayed()->nop(); ++ lep = __ pc(); __ push(ltos); __ b(L); __ delayed()->nop(); ++ aep =__ pc(); __ push(atos); __ b(L); __ delayed()->nop(); ++ bep = cep = sep = ++ iep = __ pc(); __ push(itos); ++ vep = __ pc(); ++ __ bind(L); ++ generate_and_dispatch(t); ++} ++ ++ ++//----------------------------------------------------------------------------- ++// Generation of individual instructions ++ ++// helpers for generate_and_dispatch ++ ++ ++InterpreterGenerator::InterpreterGenerator(StubQueue* code) ++ : TemplateInterpreterGenerator(code) { ++ generate_all(); // down here so it can be "virtual" ++} ++ ++//----------------------------------------------------------------------------- ++ ++// Non-product code ++#ifndef PRODUCT ++address TemplateInterpreterGenerator::generate_trace_code(TosState state) { ++ address entry = __ pc(); ++ ++ // prepare expression stack ++ __ push(state); // save tosca ++ ++ // tos & tos2 ++ // trace_bytecode need actually 4 args, the last two is tos&tos2 ++ // this work fine for x86. but mips o32 call convention will store A2-A3 ++ // to the stack position it think is the tos&tos2 ++ // when the expression stack have no more than 2 data, error occur. ++ __ ld(A2, SP, 0); ++ __ ld(A3, SP, 1 * wordSize); ++ ++ // pass arguments & call tracer ++ __ call_VM(noreg, CAST_FROM_FN_PTR(address, SharedRuntime::trace_bytecode), RA, A2, A3); ++ __ move(RA, V0); // make sure return address is not destroyed by pop(state) ++ ++ // restore expression stack ++ __ pop(state); // restore tosca ++ ++ // return ++ __ jr(RA); ++ __ delayed()->nop(); ++ ++ return entry; ++} ++ ++void TemplateInterpreterGenerator::count_bytecode() { ++ __ li(T8, (long)&BytecodeCounter::_counter_value); ++ __ lw(AT, T8, 0); ++ __ daddiu(AT, AT, 1); ++ __ sw(AT, T8, 0); ++} ++ ++void TemplateInterpreterGenerator::histogram_bytecode(Template* t) { ++ __ li(T8, (long)&BytecodeHistogram::_counters[t->bytecode()]); ++ __ lw(AT, T8, 0); ++ __ daddiu(AT, AT, 1); ++ __ sw(AT, T8, 0); ++} ++ ++void TemplateInterpreterGenerator::histogram_bytecode_pair(Template* t) { ++ __ li(T8, (long)&BytecodePairHistogram::_index); ++ __ lw(T9, T8, 0); ++ __ dsrl(T9, T9, BytecodePairHistogram::log2_number_of_codes); ++ __ li(T8, ((long)t->bytecode()) << BytecodePairHistogram::log2_number_of_codes); ++ __ orr(T9, T9, T8); ++ __ li(T8, (long)&BytecodePairHistogram::_index); ++ __ sw(T9, T8, 0); ++ __ dsll(T9, T9, 2); ++ __ li(T8, (long)BytecodePairHistogram::_counters); ++ __ daddu(T8, T8, T9); ++ __ lw(AT, T8, 0); ++ __ daddiu(AT, AT, 1); ++ __ sw(AT, T8, 0); ++} ++ ++ ++void TemplateInterpreterGenerator::trace_bytecode(Template* t) { ++ // Call a little run-time stub to avoid blow-up for each bytecode. ++ // The run-time runtime saves the right registers, depending on ++ // the tosca in-state for the given template. ++ ++ address entry = Interpreter::trace_code(t->tos_in()); ++ assert(entry != NULL, "entry must have been generated"); ++ __ call(entry, relocInfo::none); ++ __ delayed()->nop(); ++ //add for compressedoops ++ __ reinit_heapbase(); ++} ++ ++ ++void TemplateInterpreterGenerator::stop_interpreter_at() { ++ Label L; ++ __ li(T8, long(&BytecodeCounter::_counter_value)); ++ __ lw(T8, T8, 0); ++ __ move(AT, StopInterpreterAt); ++ __ bne(T8, AT, L); ++ __ delayed()->nop(); ++ __ brk(5); ++ __ delayed()->nop(); ++ __ bind(L); ++} ++#endif // !PRODUCT ++#endif // ! CC_INTERP +diff --git a/hotspot/src/cpu/mips/vm/templateTable_mips.hpp b/hotspot/src/cpu/mips/vm/templateTable_mips.hpp +new file mode 100644 +index 0000000000..d879e6dc92 +--- /dev/null ++++ b/hotspot/src/cpu/mips/vm/templateTable_mips.hpp +@@ -0,0 +1,34 @@ ++/* ++ * Copyright (c) 2003, 2012, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2015, 2016, Loongson Technology. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ * ++ */ ++ ++ static void prepare_invoke(Register method, Register index, int byte_no, ++ Bytecodes::Code code); ++ static void invokevirtual_helper(Register index, Register recv, ++ Register flags); ++ static void volatile_barrier(); ++ ++ // Helpers ++ static void index_check(Register array, Register index); ++ static void index_check_without_pop(Register array, Register index); +diff --git a/hotspot/src/cpu/mips/vm/templateTable_mips_64.cpp b/hotspot/src/cpu/mips/vm/templateTable_mips_64.cpp +new file mode 100644 +index 0000000000..7415511b99 +--- /dev/null ++++ b/hotspot/src/cpu/mips/vm/templateTable_mips_64.cpp +@@ -0,0 +1,4623 @@ ++/* ++ * Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2015, 2022, Loongson Technology. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ * ++ */ ++ ++#include "precompiled.hpp" ++#include "asm/macroAssembler.hpp" ++#include "interpreter/interpreter.hpp" ++#include "interpreter/interpreterRuntime.hpp" ++#include "interpreter/templateTable.hpp" ++#include "memory/universe.inline.hpp" ++#include "oops/methodData.hpp" ++#include "oops/objArrayKlass.hpp" ++#include "oops/oop.inline.hpp" ++#include "prims/methodHandles.hpp" ++#include "runtime/sharedRuntime.hpp" ++#include "runtime/stubRoutines.hpp" ++#include "runtime/synchronizer.hpp" ++#include "utilities/macros.hpp" ++ ++ ++#ifndef CC_INTERP ++ ++#define __ _masm-> ++ ++#define A0 RA0 ++#define A1 RA1 ++#define A2 RA2 ++#define A3 RA3 ++#define A4 RA4 ++#define A5 RA5 ++#define A6 RA6 ++#define A7 RA7 ++#define T0 RT0 ++#define T1 RT1 ++#define T2 RT2 ++#define T3 RT3 ++#define T8 RT8 ++#define T9 RT9 ++ ++// Platform-dependent initialization ++ ++void TemplateTable::pd_initialize() { ++ // No mips specific initialization ++} ++ ++// Address computation: local variables ++ ++static inline Address iaddress(int n) { ++ return Address(LVP, Interpreter::local_offset_in_bytes(n)); ++} ++ ++static inline Address laddress(int n) { ++ return iaddress(n + 1); ++} ++ ++static inline Address faddress(int n) { ++ return iaddress(n); ++} ++ ++static inline Address daddress(int n) { ++ return laddress(n); ++} ++ ++static inline Address aaddress(int n) { ++ return iaddress(n); ++} ++static inline Address haddress(int n) { return iaddress(n + 0); } ++ ++ ++static inline Address at_sp() { return Address(SP, 0); } ++static inline Address at_sp_p1() { return Address(SP, 1 * wordSize); } ++static inline Address at_sp_p2() { return Address(SP, 2 * wordSize); } ++ ++// At top of Java expression stack which may be different than sp(). It ++// isn't for category 1 objects. ++static inline Address at_tos () { ++ Address tos = Address(SP, Interpreter::expr_offset_in_bytes(0)); ++ return tos; ++} ++ ++static inline Address at_tos_p1() { ++ return Address(SP, Interpreter::expr_offset_in_bytes(1)); ++} ++ ++static inline Address at_tos_p2() { ++ return Address(SP, Interpreter::expr_offset_in_bytes(2)); ++} ++ ++static inline Address at_tos_p3() { ++ return Address(SP, Interpreter::expr_offset_in_bytes(3)); ++} ++ ++// we use S0 as bcp, be sure you have bcp in S0 before you call any of the Template generator ++Address TemplateTable::at_bcp(int offset) { ++ assert(_desc->uses_bcp(), "inconsistent uses_bcp information"); ++ return Address(BCP, offset); ++} ++ ++// Miscelaneous helper routines ++// Store an oop (or NULL) at the address described by obj. ++// If val == noreg this means store a NULL ++ ++static void do_oop_store(InterpreterMacroAssembler* _masm, ++ Address obj, ++ Register val, ++ BarrierSet::Name barrier, ++ bool precise) { ++ assert(val == noreg || val == V0, "parameter is just for looks"); ++ switch (barrier) { ++#if INCLUDE_ALL_GCS ++ case BarrierSet::G1SATBCT: ++ case BarrierSet::G1SATBCTLogging: ++ { ++ // flatten object address if needed ++ if (obj.index() == noreg && obj.disp() == 0) { ++ if (obj.base() != T3) { ++ __ move(T3, obj.base()); ++ } ++ } else { ++ __ lea(T3, obj); ++ } ++ __ g1_write_barrier_pre(T3 /* obj */, ++ T1 /* pre_val */, ++ TREG /* thread */, ++ T9 /* tmp */, ++ val != noreg /* tosca_live */, ++ false /* expand_call */); ++ if (val == noreg) { ++ __ store_heap_oop_null(Address(T3, 0)); ++ } else { ++ // G1 barrier needs uncompressed oop for region cross check. ++ Register new_val = val; ++ if (UseCompressedOops) { ++ new_val = T1; ++ __ move(new_val, val); ++ } ++ __ store_heap_oop(Address(T3, 0), val); ++ __ g1_write_barrier_post(T3 /* store_adr */, ++ new_val /* new_val */, ++ TREG /* thread */, ++ T9 /* tmp */, ++ T1 /* tmp2 */); ++ } ++ } ++ break; ++#endif // INCLUDE_ALL_GCS ++ case BarrierSet::CardTableModRef: ++ case BarrierSet::CardTableExtension: ++ { ++ if (val == noreg) { ++ __ store_heap_oop_null(obj); ++ } else { ++ __ store_heap_oop(obj, val); ++ // flatten object address if needed ++ if (!precise || (obj.index() == noreg && obj.disp() == 0)) { ++ __ store_check(obj.base()); ++ } else { ++ __ lea(T9, obj); ++ __ store_check(T9); ++ } ++ } ++ } ++ break; ++ case BarrierSet::ModRef: ++ case BarrierSet::Other: ++ if (val == noreg) { ++ __ store_heap_oop_null(obj); ++ } else { ++ __ store_heap_oop(obj, val); ++ } ++ break; ++ default : ++ ShouldNotReachHere(); ++ ++ } ++} ++ ++// bytecode folding ++void TemplateTable::patch_bytecode(Bytecodes::Code bc, Register bc_reg, ++ Register tmp_reg, bool load_bc_into_bc_reg/*=true*/, ++ int byte_no) { ++ if (!RewriteBytecodes) return; ++ Label L_patch_done; ++ ++ switch (bc) { ++ case Bytecodes::_fast_aputfield: ++ case Bytecodes::_fast_bputfield: ++ case Bytecodes::_fast_zputfield: ++ case Bytecodes::_fast_cputfield: ++ case Bytecodes::_fast_dputfield: ++ case Bytecodes::_fast_fputfield: ++ case Bytecodes::_fast_iputfield: ++ case Bytecodes::_fast_lputfield: ++ case Bytecodes::_fast_sputfield: ++ { ++ // We skip bytecode quickening for putfield instructions when ++ // the put_code written to the constant pool cache is zero. ++ // This is required so that every execution of this instruction ++ // calls out to InterpreterRuntime::resolve_get_put to do ++ // additional, required work. ++ assert(byte_no == f1_byte || byte_no == f2_byte, "byte_no out of range"); ++ assert(load_bc_into_bc_reg, "we use bc_reg as temp"); ++ __ get_cache_and_index_and_bytecode_at_bcp(tmp_reg, bc_reg, tmp_reg, byte_no, 1); ++ __ daddiu(bc_reg, R0, bc); ++ __ beq(tmp_reg, R0, L_patch_done); ++ __ delayed()->nop(); ++ } ++ break; ++ default: ++ assert(byte_no == -1, "sanity"); ++ // the pair bytecodes have already done the load. ++ if (load_bc_into_bc_reg) { ++ __ move(bc_reg, bc); ++ } ++ } ++ ++ if (JvmtiExport::can_post_breakpoint()) { ++ Label L_fast_patch; ++ // if a breakpoint is present we can't rewrite the stream directly ++ __ lbu(tmp_reg, at_bcp(0)); ++ __ move(AT, Bytecodes::_breakpoint); ++ __ bne(tmp_reg, AT, L_fast_patch); ++ __ delayed()->nop(); ++ ++ __ get_method(tmp_reg); ++ // Let breakpoint table handling rewrite to quicker bytecode ++ __ call_VM(NOREG, CAST_FROM_FN_PTR(address, ++ InterpreterRuntime::set_original_bytecode_at), tmp_reg, BCP, bc_reg); ++ ++ __ b(L_patch_done); ++ __ delayed()->nop(); ++ __ bind(L_fast_patch); ++ } ++ ++#ifdef ASSERT ++ Label L_okay; ++ __ lbu(tmp_reg, at_bcp(0)); ++ __ move(AT, (int)Bytecodes::java_code(bc)); ++ __ beq(tmp_reg, AT, L_okay); ++ __ delayed()->nop(); ++ __ beq(tmp_reg, bc_reg, L_patch_done); ++ __ delayed()->nop(); ++ __ stop("patching the wrong bytecode"); ++ __ bind(L_okay); ++#endif ++ ++ // patch bytecode ++ __ sb(bc_reg, at_bcp(0)); ++ __ bind(L_patch_done); ++} ++ ++ ++// Individual instructions ++ ++void TemplateTable::nop() { ++ transition(vtos, vtos); ++ // nothing to do ++} ++ ++void TemplateTable::shouldnotreachhere() { ++ transition(vtos, vtos); ++ __ stop("shouldnotreachhere bytecode"); ++} ++ ++void TemplateTable::aconst_null() { ++ transition(vtos, atos); ++ __ move(FSR, R0); ++} ++ ++void TemplateTable::iconst(int value) { ++ transition(vtos, itos); ++ if (value == 0) { ++ __ move(FSR, R0); ++ } else { ++ __ move(FSR, value); ++ } ++} ++ ++void TemplateTable::lconst(int value) { ++ transition(vtos, ltos); ++ if (value == 0) { ++ __ move(FSR, R0); ++ } else { ++ __ move(FSR, value); ++ } ++} ++ ++void TemplateTable::fconst(int value) { ++ transition(vtos, ftos); ++ switch( value ) { ++ case 0: __ mtc1(R0, FSF); return; ++ case 1: __ addiu(AT, R0, 1); break; ++ case 2: __ addiu(AT, R0, 2); break; ++ default: ShouldNotReachHere(); ++ } ++ __ mtc1(AT, FSF); ++ __ cvt_s_w(FSF, FSF); ++} ++ ++void TemplateTable::dconst(int value) { ++ transition(vtos, dtos); ++ switch( value ) { ++ case 0: __ dmtc1(R0, FSF); ++ return; ++ case 1: __ daddiu(AT, R0, 1); ++ __ dmtc1(AT, FSF); ++ __ cvt_d_w(FSF, FSF); ++ break; ++ default: ShouldNotReachHere(); ++ } ++} ++ ++void TemplateTable::bipush() { ++ transition(vtos, itos); ++ __ lb(FSR, at_bcp(1)); ++} ++ ++void TemplateTable::sipush() { ++ transition(vtos, itos); ++ __ lb(FSR, BCP, 1); ++ __ lbu(AT, BCP, 2); ++ __ dsll(FSR, FSR, 8); ++ __ orr(FSR, FSR, AT); ++} ++ ++// T1 : tags ++// T2 : index ++// T3 : cpool ++// T8 : tag ++void TemplateTable::ldc(bool wide) { ++ transition(vtos, vtos); ++ Label call_ldc, notFloat, notClass, Done; ++ // get index in cpool ++ if (wide) { ++ __ get_unsigned_2_byte_index_at_bcp(T2, 1); ++ } else { ++ __ lbu(T2, at_bcp(1)); ++ } ++ ++ __ get_cpool_and_tags(T3, T1); ++ ++ const int base_offset = ConstantPool::header_size() * wordSize; ++ const int tags_offset = Array::base_offset_in_bytes(); ++ ++ // get type ++ if (UseLEXT1 && Assembler::is_simm(sizeof(tags_offset), 8)) { ++ __ gslbx(T1, T1, T2, tags_offset); ++ } else { ++ __ daddu(AT, T1, T2); ++ __ lb(T1, AT, tags_offset); ++ } ++ if(os::is_MP()) { ++ __ sync(); // load acquire ++ } ++ //now T1 is the tag ++ ++ // unresolved class - get the resolved class ++ __ daddiu(AT, T1, - JVM_CONSTANT_UnresolvedClass); ++ __ beq(AT, R0, call_ldc); ++ __ delayed()->nop(); ++ ++ // unresolved class in error (resolution failed) - call into runtime ++ // so that the same error from first resolution attempt is thrown. ++ __ daddiu(AT, T1, -JVM_CONSTANT_UnresolvedClassInError); ++ __ beq(AT, R0, call_ldc); ++ __ delayed()->nop(); ++ ++ // resolved class - need to call vm to get java mirror of the class ++ __ daddiu(AT, T1, - JVM_CONSTANT_Class); ++ __ bne(AT, R0, notClass); ++ __ delayed()->dsll(T2, T2, Address::times_8); ++ ++ __ bind(call_ldc); ++ __ move(A1, wide); ++ call_VM(FSR, CAST_FROM_FN_PTR(address, InterpreterRuntime::ldc), A1); ++ //__ push(atos); ++ __ daddiu(SP, SP, - Interpreter::stackElementSize); ++ __ b(Done); ++ __ delayed()->sd(FSR, SP, 0); // added for performance issue ++ ++ __ bind(notClass); ++ __ daddiu(AT, T1, -JVM_CONSTANT_Float); ++ __ bne(AT, R0, notFloat); ++ __ delayed()->nop(); ++ // ftos ++ if (UseLEXT1 && Assembler::is_simm(sizeof(base_offset), 8)) { ++ __ gslwxc1(FSF, T3, T2, base_offset); ++ } else { ++ __ daddu(AT, T3, T2); ++ __ lwc1(FSF, AT, base_offset); ++ } ++ //__ push_f(); ++ __ daddiu(SP, SP, - Interpreter::stackElementSize); ++ __ b(Done); ++ __ delayed()->swc1(FSF, SP, 0); ++ ++ __ bind(notFloat); ++#ifdef ASSERT ++ { ++ Label L; ++ __ daddiu(AT, T1, -JVM_CONSTANT_Integer); ++ __ beq(AT, R0, L); ++ __ delayed()->nop(); ++ __ stop("unexpected tag type in ldc"); ++ __ bind(L); ++ } ++#endif ++ // itos JVM_CONSTANT_Integer only ++ if (UseLEXT1 && Assembler::is_simm(sizeof(base_offset), 8)) { ++ __ gslwx(FSR, T3, T2, base_offset); ++ } else { ++ __ daddu(T0, T3, T2); ++ __ lw(FSR, T0, base_offset); ++ } ++ __ push(itos); ++ __ bind(Done); ++} ++ ++// Fast path for caching oop constants. ++void TemplateTable::fast_aldc(bool wide) { ++ transition(vtos, atos); ++ ++ Register result = FSR; ++ Register tmp = SSR; ++ int index_size = wide ? sizeof(u2) : sizeof(u1); ++ ++ Label resolved; ++ ++ // We are resolved if the resolved reference cache entry contains a ++ // non-null object (String, MethodType, etc.) ++ assert_different_registers(result, tmp); ++ __ get_cache_index_at_bcp(tmp, 1, index_size); ++ __ load_resolved_reference_at_index(result, tmp); ++ __ bne(result, R0, resolved); ++ __ delayed()->nop(); ++ ++ address entry = CAST_FROM_FN_PTR(address, InterpreterRuntime::resolve_ldc); ++ // first time invocation - must resolve first ++ int i = (int)bytecode(); ++ __ move(tmp, i); ++ __ call_VM(result, entry, tmp); ++ ++ __ bind(resolved); ++ ++ if (VerifyOops) { ++ __ verify_oop(result); ++ } ++} ++ ++ ++// used register: T2, T3, T1 ++// T2 : index ++// T3 : cpool ++// T1 : tag ++void TemplateTable::ldc2_w() { ++ transition(vtos, vtos); ++ Label Long, Done; ++ ++ // get index in cpool ++ __ get_unsigned_2_byte_index_at_bcp(T2, 1); ++ ++ __ get_cpool_and_tags(T3, T1); ++ ++ const int base_offset = ConstantPool::header_size() * wordSize; ++ const int tags_offset = Array::base_offset_in_bytes(); ++ ++ // get type in T1 ++ if (UseLEXT1 && Assembler::is_simm(tags_offset, 8)) { ++ __ gslbx(T1, T1, T2, tags_offset); ++ } else { ++ __ daddu(AT, T1, T2); ++ __ lb(T1, AT, tags_offset); ++ } ++ ++ __ daddiu(AT, T1, - JVM_CONSTANT_Double); ++ __ bne(AT, R0, Long); ++ __ delayed()->dsll(T2, T2, Address::times_8); ++ ++ // dtos ++ if (UseLEXT1 && Assembler::is_simm(base_offset, 8)) { ++ __ gsldxc1(FSF, T3, T2, base_offset); ++ } else { ++ __ daddu(AT, T3, T2); ++ __ ldc1(FSF, AT, base_offset); ++ } ++ __ push(dtos); ++ __ b(Done); ++ __ delayed()->nop(); ++ ++ // ltos ++ __ bind(Long); ++ if (UseLEXT1 && Assembler::is_simm(base_offset, 8)) { ++ __ gsldx(FSR, T3, T2, base_offset); ++ } else { ++ __ daddu(AT, T3, T2); ++ __ ld(FSR, AT, base_offset); ++ } ++ __ push(ltos); ++ ++ __ bind(Done); ++} ++ ++// we compute the actual local variable address here ++// the x86 dont do so for it has scaled index memory access model, we dont have, so do here ++void TemplateTable::locals_index(Register reg, int offset) { ++ __ lbu(reg, at_bcp(offset)); ++ __ dsll(reg, reg, Address::times_8); ++ __ dsubu(reg, LVP, reg); ++} ++ ++// this method will do bytecode folding of the two form: ++// iload iload iload caload ++// used register : T2, T3 ++// T2 : bytecode ++// T3 : folded code ++void TemplateTable::iload() { ++ transition(vtos, itos); ++ if (RewriteFrequentPairs) { ++ Label rewrite, done; ++ // get the next bytecode in T2 ++ __ lbu(T2, at_bcp(Bytecodes::length_for(Bytecodes::_iload))); ++ // if _iload, wait to rewrite to iload2. We only want to rewrite the ++ // last two iloads in a pair. Comparing against fast_iload means that ++ // the next bytecode is neither an iload or a caload, and therefore ++ // an iload pair. ++ __ move(AT, Bytecodes::_iload); ++ __ beq(AT, T2, done); ++ __ delayed()->nop(); ++ ++ __ move(T3, Bytecodes::_fast_iload2); ++ __ move(AT, Bytecodes::_fast_iload); ++ __ beq(AT, T2, rewrite); ++ __ delayed()->nop(); ++ ++ // if _caload, rewrite to fast_icaload ++ __ move(T3, Bytecodes::_fast_icaload); ++ __ move(AT, Bytecodes::_caload); ++ __ beq(AT, T2, rewrite); ++ __ delayed()->nop(); ++ ++ // rewrite so iload doesn't check again. ++ __ move(T3, Bytecodes::_fast_iload); ++ ++ // rewrite ++ // T3 : fast bytecode ++ __ bind(rewrite); ++ patch_bytecode(Bytecodes::_iload, T3, T2, false); ++ __ bind(done); ++ } ++ ++ // Get the local value into tos ++ locals_index(T2); ++ __ lw(FSR, T2, 0); ++} ++ ++// used register T2 ++// T2 : index ++void TemplateTable::fast_iload2() { ++ transition(vtos, itos); ++ locals_index(T2); ++ __ lw(FSR, T2, 0); ++ __ push(itos); ++ locals_index(T2, 3); ++ __ lw(FSR, T2, 0); ++} ++ ++// used register T2 ++// T2 : index ++void TemplateTable::fast_iload() { ++ transition(vtos, itos); ++ locals_index(T2); ++ __ lw(FSR, T2, 0); ++} ++ ++// used register T2 ++// T2 : index ++void TemplateTable::lload() { ++ transition(vtos, ltos); ++ locals_index(T2); ++ __ ld(FSR, T2, -wordSize); ++} ++ ++// used register T2 ++// T2 : index ++void TemplateTable::fload() { ++ transition(vtos, ftos); ++ locals_index(T2); ++ __ lwc1(FSF, T2, 0); ++} ++ ++// used register T2 ++// T2 : index ++void TemplateTable::dload() { ++ transition(vtos, dtos); ++ locals_index(T2); ++ __ ldc1(FSF, T2, -wordSize); ++} ++ ++// used register T2 ++// T2 : index ++void TemplateTable::aload() { ++ transition(vtos, atos); ++ locals_index(T2); ++ __ ld(FSR, T2, 0); ++} ++ ++void TemplateTable::locals_index_wide(Register reg) { ++ __ get_unsigned_2_byte_index_at_bcp(reg, 2); ++ __ dsll(reg, reg, Address::times_8); ++ __ dsubu(reg, LVP, reg); ++} ++ ++// used register T2 ++// T2 : index ++void TemplateTable::wide_iload() { ++ transition(vtos, itos); ++ locals_index_wide(T2); ++ __ ld(FSR, T2, 0); ++} ++ ++// used register T2 ++// T2 : index ++void TemplateTable::wide_lload() { ++ transition(vtos, ltos); ++ locals_index_wide(T2); ++ __ ld(FSR, T2, -wordSize); ++} ++ ++// used register T2 ++// T2 : index ++void TemplateTable::wide_fload() { ++ transition(vtos, ftos); ++ locals_index_wide(T2); ++ __ lwc1(FSF, T2, 0); ++} ++ ++// used register T2 ++// T2 : index ++void TemplateTable::wide_dload() { ++ transition(vtos, dtos); ++ locals_index_wide(T2); ++ __ ldc1(FSF, T2, -wordSize); ++} ++ ++// used register T2 ++// T2 : index ++void TemplateTable::wide_aload() { ++ transition(vtos, atos); ++ locals_index_wide(T2); ++ __ ld(FSR, T2, 0); ++} ++ ++// we use A2 as the regiser for index, BE CAREFUL! ++// we dont use our tge 29 now, for later optimization ++void TemplateTable::index_check(Register array, Register index) { ++ // Pop ptr into array ++ __ pop_ptr(array); ++ index_check_without_pop(array, index); ++} ++ ++void TemplateTable::index_check_without_pop(Register array, Register index) { ++ // destroys A2 ++ // check array ++ __ null_check(array, arrayOopDesc::length_offset_in_bytes()); ++ ++ // sign extend since tos (index) might contain garbage in upper bits ++ __ sll(index, index, 0); ++ ++ // check index ++ Label ok; ++ __ lw(AT, array, arrayOopDesc::length_offset_in_bytes()); ++#ifndef OPT_RANGECHECK ++ __ sltu(AT, index, AT); ++ __ bne(AT, R0, ok); ++ __ delayed()->nop(); ++ ++ //throw_ArrayIndexOutOfBoundsException assume abberrant index in A2 ++ if (A2 != index) __ move(A2, index); ++ __ jmp(Interpreter::_throw_ArrayIndexOutOfBoundsException_entry); ++ __ delayed()->nop(); ++ __ bind(ok); ++#else ++ __ lw(AT, array, arrayOopDesc::length_offset_in_bytes()); ++ __ move(A2, index); ++ __ tgeu(A2, AT, 29); ++#endif ++} ++ ++void TemplateTable::iaload() { ++ transition(itos, itos); ++ if(UseBoundCheckInstruction) { ++ __ pop(SSR); //SSR:array FSR: index ++ __ dsll(FSR, FSR, 2); ++ __ daddu(FSR, SSR, FSR); ++ __ addiu(FSR, FSR, arrayOopDesc::base_offset_in_bytes(T_INT)); ++ ++ __ lw(AT, SSR, arrayOopDesc::length_offset_in_bytes()); //bound ++ __ dsll(AT, AT, 2); ++ __ daddu(AT, SSR, AT); ++ __ addiu(AT, AT, arrayOopDesc::base_offset_in_bytes(T_INT)); ++ ++ __ gslwle(FSR, FSR, AT); ++ } else { ++ index_check(SSR, FSR); ++ __ dsll(FSR, FSR, 2); ++ if (UseLEXT1 && Assembler::is_simm(arrayOopDesc::base_offset_in_bytes(T_INT), 8)) { ++ __ gslwx(FSR, FSR, SSR, arrayOopDesc::base_offset_in_bytes(T_INT)); ++ } else { ++ __ daddu(FSR, SSR, FSR); ++ __ lw(FSR, FSR, arrayOopDesc::base_offset_in_bytes(T_INT)); ++ } ++ } ++} ++ ++void TemplateTable::laload() { ++ transition(itos, ltos); ++ if(UseBoundCheckInstruction) { ++ __ pop(SSR); //SSR:array FSR: index ++ __ dsll(FSR, FSR, Address::times_8); ++ __ daddu(FSR, SSR, FSR); ++ __ addiu(FSR, FSR, arrayOopDesc::base_offset_in_bytes(T_LONG) + 0 * wordSize); ++ ++ __ lw(AT, SSR, arrayOopDesc::length_offset_in_bytes()); //bound ++ __ dsll(AT, AT, Address::times_8); ++ __ daddu(AT, SSR, AT); ++ __ addiu(AT, AT, arrayOopDesc::base_offset_in_bytes(T_LONG) + 0 * wordSize); ++ ++ __ gsldle(FSR, FSR, AT); ++ } else { ++ index_check(SSR, FSR); ++ __ dsll(AT, FSR, Address::times_8); ++ if (UseLEXT1 && Assembler::is_simm(arrayOopDesc::base_offset_in_bytes(T_LONG), 8)) { ++ __ gsldx(FSR, SSR, AT, arrayOopDesc::base_offset_in_bytes(T_LONG)); ++ } else { ++ __ daddu(AT, SSR, AT); ++ __ ld(FSR, AT, arrayOopDesc::base_offset_in_bytes(T_LONG)); ++ } ++ } ++} ++ ++void TemplateTable::faload() { ++ transition(itos, ftos); ++ if(UseBoundCheckInstruction) { ++ __ pop(SSR); //SSR:array FSR: index ++ __ shl(FSR, 2); ++ __ daddu(FSR, SSR, FSR); ++ __ addiu(FSR, FSR, arrayOopDesc::base_offset_in_bytes(T_FLOAT)); ++ ++ __ lw(AT, SSR, arrayOopDesc::length_offset_in_bytes()); //bound ++ __ shl(AT, 2); ++ __ daddu(AT, SSR, AT); ++ __ addiu(AT, AT, arrayOopDesc::base_offset_in_bytes(T_FLOAT)); ++ ++ __ gslwlec1(FSF, FSR, AT); ++ } else { ++ index_check(SSR, FSR); ++ __ shl(FSR, 2); ++ if (UseLEXT1 && Assembler::is_simm(arrayOopDesc::base_offset_in_bytes(T_FLOAT), 8)) { ++ __ gslwxc1(FSF, SSR, FSR, arrayOopDesc::base_offset_in_bytes(T_FLOAT)); ++ } else { ++ __ daddu(FSR, SSR, FSR); ++ __ lwc1(FSF, FSR, arrayOopDesc::base_offset_in_bytes(T_FLOAT)); ++ } ++ } ++} ++ ++void TemplateTable::daload() { ++ transition(itos, dtos); ++ if(UseBoundCheckInstruction) { ++ __ pop(SSR); //SSR:array FSR: index ++ __ dsll(FSR, FSR, 3); ++ __ daddu(FSR, SSR, FSR); ++ __ addiu(FSR, FSR, arrayOopDesc::base_offset_in_bytes(T_DOUBLE) + 0 * wordSize); ++ ++ __ lw(AT, SSR, arrayOopDesc::length_offset_in_bytes()); //bound ++ __ dsll(AT, AT, 3); ++ __ daddu(AT, SSR, AT); ++ __ addiu(AT, AT, arrayOopDesc::base_offset_in_bytes(T_DOUBLE) + 0 * wordSize); ++ ++ __ gsldlec1(FSF, FSR, AT); ++ } else { ++ index_check(SSR, FSR); ++ __ dsll(AT, FSR, 3); ++ if (UseLEXT1 && Assembler::is_simm(arrayOopDesc::base_offset_in_bytes(T_DOUBLE), 8)) { ++ __ gsldxc1(FSF, SSR, AT, arrayOopDesc::base_offset_in_bytes(T_DOUBLE)); ++ } else { ++ __ daddu(AT, SSR, AT); ++ __ ldc1(FSF, AT, arrayOopDesc::base_offset_in_bytes(T_DOUBLE)); ++ } ++ } ++} ++ ++void TemplateTable::aaload() { ++ transition(itos, atos); ++ index_check(SSR, FSR); ++ __ dsll(FSR, FSR, UseCompressedOops ? Address::times_4 : Address::times_8); ++ __ daddu(FSR, SSR, FSR); ++ //add for compressedoops ++ __ load_heap_oop(FSR, Address(FSR, arrayOopDesc::base_offset_in_bytes(T_OBJECT))); ++} ++ ++void TemplateTable::baload() { ++ transition(itos, itos); ++ if(UseBoundCheckInstruction) { ++ __ pop(SSR); //SSR:array FSR:index ++ __ daddu(FSR, SSR, FSR); ++ __ addiu(FSR, FSR, arrayOopDesc::base_offset_in_bytes(T_BYTE)); //base ++ ++ __ lw(AT, SSR, arrayOopDesc::length_offset_in_bytes()); ++ __ daddu(AT, SSR, AT); ++ __ addiu(AT, AT, arrayOopDesc::base_offset_in_bytes(T_BYTE)); //bound ++ ++ __ gslble(FSR, FSR, AT); ++ } else { ++ index_check(SSR, FSR); ++ if (UseLEXT1 && Assembler::is_simm(arrayOopDesc::base_offset_in_bytes(T_BYTE), 8)) { ++ __ gslbx(FSR, SSR, FSR, arrayOopDesc::base_offset_in_bytes(T_BYTE)); ++ } else { ++ __ daddu(FSR, SSR, FSR); ++ __ lb(FSR, FSR, arrayOopDesc::base_offset_in_bytes(T_BYTE)); ++ } ++ } ++} ++ ++void TemplateTable::caload() { ++ transition(itos, itos); ++ index_check(SSR, FSR); ++ __ dsll(FSR, FSR, Address::times_2); ++ __ daddu(FSR, SSR, FSR); ++ __ lhu(FSR, FSR, arrayOopDesc::base_offset_in_bytes(T_CHAR)); ++} ++ ++// iload followed by caload frequent pair ++// used register : T2 ++// T2 : index ++void TemplateTable::fast_icaload() { ++ transition(vtos, itos); ++ // load index out of locals ++ locals_index(T2); ++ __ lw(FSR, T2, 0); ++ index_check(SSR, FSR); ++ __ dsll(FSR, FSR, 1); ++ __ daddu(FSR, SSR, FSR); ++ __ lhu(FSR, FSR, arrayOopDesc::base_offset_in_bytes(T_CHAR)); ++} ++ ++void TemplateTable::saload() { ++ transition(itos, itos); ++ if(UseBoundCheckInstruction) { ++ __ pop(SSR); //SSR:array FSR: index ++ __ dsll(FSR, FSR, Address::times_2); ++ __ daddu(FSR, SSR, FSR); ++ __ addiu(FSR, FSR, arrayOopDesc::base_offset_in_bytes(T_SHORT)); ++ ++ __ lw(AT, SSR, arrayOopDesc::length_offset_in_bytes()); //bound ++ __ dsll(AT, AT, Address::times_2); ++ __ daddu(AT, SSR, AT); ++ __ addiu(AT, AT, arrayOopDesc::base_offset_in_bytes(T_SHORT)); ++ ++ __ gslhle(FSR, FSR, AT); ++ } else { ++ index_check(SSR, FSR); ++ __ dsll(FSR, FSR, Address::times_2); ++ if (UseLEXT1 && Assembler::is_simm(arrayOopDesc::base_offset_in_bytes(T_SHORT), 8)) { ++ __ gslhx(FSR, SSR, FSR, arrayOopDesc::base_offset_in_bytes(T_SHORT)); ++ } else { ++ __ daddu(FSR, SSR, FSR); ++ __ lh(FSR, FSR, arrayOopDesc::base_offset_in_bytes(T_SHORT)); ++ } ++ } ++} ++ ++void TemplateTable::iload(int n) { ++ transition(vtos, itos); ++ __ lw(FSR, iaddress(n)); ++} ++ ++void TemplateTable::lload(int n) { ++ transition(vtos, ltos); ++ __ ld(FSR, laddress(n)); ++} ++ ++void TemplateTable::fload(int n) { ++ transition(vtos, ftos); ++ __ lwc1(FSF, faddress(n)); ++} ++ ++void TemplateTable::dload(int n) { ++ transition(vtos, dtos); ++ __ ldc1(FSF, laddress(n)); ++} ++ ++void TemplateTable::aload(int n) { ++ transition(vtos, atos); ++ __ ld(FSR, aaddress(n)); ++} ++ ++// used register : T2, T3 ++// T2 : bytecode ++// T3 : folded code ++void TemplateTable::aload_0() { ++ transition(vtos, atos); ++ // According to bytecode histograms, the pairs: ++ // ++ // _aload_0, _fast_igetfield ++ // _aload_0, _fast_agetfield ++ // _aload_0, _fast_fgetfield ++ // ++ // occur frequently. If RewriteFrequentPairs is set, the (slow) ++ // _aload_0 bytecode checks if the next bytecode is either ++ // _fast_igetfield, _fast_agetfield or _fast_fgetfield and then ++ // rewrites the current bytecode into a pair bytecode; otherwise it ++ // rewrites the current bytecode into _fast_aload_0 that doesn't do ++ // the pair check anymore. ++ // ++ // Note: If the next bytecode is _getfield, the rewrite must be ++ // delayed, otherwise we may miss an opportunity for a pair. ++ // ++ // Also rewrite frequent pairs ++ // aload_0, aload_1 ++ // aload_0, iload_1 ++ // These bytecodes with a small amount of code are most profitable ++ // to rewrite ++ if (RewriteFrequentPairs) { ++ Label rewrite, done; ++ // get the next bytecode in T2 ++ __ lbu(T2, at_bcp(Bytecodes::length_for(Bytecodes::_aload_0))); ++ ++ // do actual aload_0 ++ aload(0); ++ ++ // if _getfield then wait with rewrite ++ __ move(AT, Bytecodes::_getfield); ++ __ beq(AT, T2, done); ++ __ delayed()->nop(); ++ ++ // if _igetfield then reqrite to _fast_iaccess_0 ++ assert(Bytecodes::java_code(Bytecodes::_fast_iaccess_0) == ++ Bytecodes::_aload_0, ++ "fix bytecode definition"); ++ __ move(T3, Bytecodes::_fast_iaccess_0); ++ __ move(AT, Bytecodes::_fast_igetfield); ++ __ beq(AT, T2, rewrite); ++ __ delayed()->nop(); ++ ++ // if _agetfield then reqrite to _fast_aaccess_0 ++ assert(Bytecodes::java_code(Bytecodes::_fast_aaccess_0) == ++ Bytecodes::_aload_0, ++ "fix bytecode definition"); ++ __ move(T3, Bytecodes::_fast_aaccess_0); ++ __ move(AT, Bytecodes::_fast_agetfield); ++ __ beq(AT, T2, rewrite); ++ __ delayed()->nop(); ++ ++ // if _fgetfield then reqrite to _fast_faccess_0 ++ assert(Bytecodes::java_code(Bytecodes::_fast_faccess_0) == ++ Bytecodes::_aload_0, ++ "fix bytecode definition"); ++ __ move(T3, Bytecodes::_fast_faccess_0); ++ __ move(AT, Bytecodes::_fast_fgetfield); ++ __ beq(AT, T2, rewrite); ++ __ delayed()->nop(); ++ ++ // else rewrite to _fast_aload0 ++ assert(Bytecodes::java_code(Bytecodes::_fast_aload_0) == ++ Bytecodes::_aload_0, ++ "fix bytecode definition"); ++ __ move(T3, Bytecodes::_fast_aload_0); ++ ++ // rewrite ++ __ bind(rewrite); ++ patch_bytecode(Bytecodes::_aload_0, T3, T2, false); ++ ++ __ bind(done); ++ } else { ++ aload(0); ++ } ++} ++ ++void TemplateTable::istore() { ++ transition(itos, vtos); ++ locals_index(T2); ++ __ sw(FSR, T2, 0); ++} ++ ++void TemplateTable::lstore() { ++ transition(ltos, vtos); ++ locals_index(T2); ++ __ sd(FSR, T2, -wordSize); ++} ++ ++void TemplateTable::fstore() { ++ transition(ftos, vtos); ++ locals_index(T2); ++ __ swc1(FSF, T2, 0); ++} ++ ++void TemplateTable::dstore() { ++ transition(dtos, vtos); ++ locals_index(T2); ++ __ sdc1(FSF, T2, -wordSize); ++} ++ ++void TemplateTable::astore() { ++ transition(vtos, vtos); ++ __ pop_ptr(FSR); ++ locals_index(T2); ++ __ sd(FSR, T2, 0); ++} ++ ++void TemplateTable::wide_istore() { ++ transition(vtos, vtos); ++ __ pop_i(FSR); ++ locals_index_wide(T2); ++ __ sd(FSR, T2, 0); ++} ++ ++void TemplateTable::wide_lstore() { ++ transition(vtos, vtos); ++ __ pop_l(FSR); ++ locals_index_wide(T2); ++ __ sd(FSR, T2, -wordSize); ++} ++ ++void TemplateTable::wide_fstore() { ++ wide_istore(); ++} ++ ++void TemplateTable::wide_dstore() { ++ wide_lstore(); ++} ++ ++void TemplateTable::wide_astore() { ++ transition(vtos, vtos); ++ __ pop_ptr(FSR); ++ locals_index_wide(T2); ++ __ sd(FSR, T2, 0); ++} ++ ++// used register : T2 ++void TemplateTable::iastore() { ++ transition(itos, vtos); ++ __ pop_i(SSR); // T2: array SSR: index ++ if(UseBoundCheckInstruction) { ++ __ pop_ptr(T2); ++ __ dsll(SSR, SSR, Address::times_4); ++ __ daddu(SSR, T2, SSR); ++ __ addiu(SSR, SSR, arrayOopDesc::base_offset_in_bytes(T_INT)); // base ++ ++ __ lw(AT, T2, arrayOopDesc::length_offset_in_bytes()); ++ __ dsll(AT, AT, Address::times_4); ++ __ daddu(AT, T2, AT); ++ __ addiu(AT, AT, arrayOopDesc::base_offset_in_bytes(T_INT)); //bound ++ ++ __ gsswle(FSR, SSR, AT); ++ } else { ++ index_check(T2, SSR); // prefer index in SSR ++ __ dsll(SSR, SSR, Address::times_4); ++ if (UseLEXT1 && Assembler::is_simm(arrayOopDesc::base_offset_in_bytes(T_INT), 8)) { ++ __ gsswx(FSR, T2, SSR, arrayOopDesc::base_offset_in_bytes(T_INT)); ++ } else { ++ __ daddu(T2, T2, SSR); ++ __ sw(FSR, T2, arrayOopDesc::base_offset_in_bytes(T_INT)); ++ } ++ } ++} ++ ++ ++ ++// used register T2, T3 ++void TemplateTable::lastore() { ++ transition(ltos, vtos); ++ __ pop_i (T2); ++ if(UseBoundCheckInstruction) { ++ __ pop_ptr(T3); ++ __ dsll(T2, T2, Address::times_8); ++ __ daddu(T2, T3, T2); ++ __ addiu(T2, T2, arrayOopDesc::base_offset_in_bytes(T_LONG) + 0 * wordSize); // base ++ ++ __ lw(AT, T3, arrayOopDesc::length_offset_in_bytes()); ++ __ dsll(AT, AT, Address::times_8); ++ __ daddu(AT, T3, AT); ++ __ addiu(AT, AT, arrayOopDesc::base_offset_in_bytes(T_LONG) + 0 * wordSize); //bound ++ ++ __ gssdle(FSR, T2, AT); ++ } else { ++ index_check(T3, T2); ++ __ dsll(T2, T2, Address::times_8); ++ if (UseLEXT1 && Assembler::is_simm(arrayOopDesc::base_offset_in_bytes(T_LONG), 8)) { ++ __ gssdx(FSR, T3, T2, arrayOopDesc::base_offset_in_bytes(T_LONG)); ++ } else { ++ __ daddu(T3, T3, T2); ++ __ sd(FSR, T3, arrayOopDesc::base_offset_in_bytes(T_LONG)); ++ } ++ } ++} ++ ++// used register T2 ++void TemplateTable::fastore() { ++ transition(ftos, vtos); ++ __ pop_i(SSR); ++ if(UseBoundCheckInstruction) { ++ __ pop_ptr(T2); ++ __ dsll(SSR, SSR, Address::times_4); ++ __ daddu(SSR, T2, SSR); ++ __ addiu(SSR, SSR, arrayOopDesc::base_offset_in_bytes(T_FLOAT)); // base ++ ++ __ lw(AT, T2, arrayOopDesc::length_offset_in_bytes()); ++ __ dsll(AT, AT, Address::times_4); ++ __ daddu(AT, T2, AT); ++ __ addiu(AT, AT, arrayOopDesc::base_offset_in_bytes(T_FLOAT)); //bound ++ ++ __ gsswlec1(FSF, SSR, AT); ++ } else { ++ index_check(T2, SSR); ++ __ dsll(SSR, SSR, Address::times_4); ++ if (UseLEXT1 && Assembler::is_simm(arrayOopDesc::base_offset_in_bytes(T_FLOAT), 8)) { ++ __ gsswxc1(FSF, T2, SSR, arrayOopDesc::base_offset_in_bytes(T_FLOAT)); ++ } else { ++ __ daddu(T2, T2, SSR); ++ __ swc1(FSF, T2, arrayOopDesc::base_offset_in_bytes(T_FLOAT)); ++ } ++ } ++} ++ ++// used register T2, T3 ++void TemplateTable::dastore() { ++ transition(dtos, vtos); ++ __ pop_i (T2); ++ if(UseBoundCheckInstruction) { ++ __ pop_ptr(T3); ++ __ dsll(T2, T2, Address::times_8); ++ __ daddu(T2, T3, T2); ++ __ addiu(T2, T2, arrayOopDesc::base_offset_in_bytes(T_DOUBLE) + 0 * wordSize); // base ++ ++ __ lw(AT, T3, arrayOopDesc::length_offset_in_bytes()); ++ __ dsll(AT, AT, Address::times_8); ++ __ daddu(AT, T3, AT); ++ __ addiu(AT, AT, arrayOopDesc::base_offset_in_bytes(T_DOUBLE) + 0 * wordSize); //bound ++ ++ __ gssdlec1(FSF, T2, AT); ++ } else { ++ index_check(T3, T2); ++ __ dsll(T2, T2, Address::times_8); ++ if (UseLEXT1 && Assembler::is_simm(arrayOopDesc::base_offset_in_bytes(T_DOUBLE), 8)) { ++ __ gssdxc1(FSF, T3, T2, arrayOopDesc::base_offset_in_bytes(T_DOUBLE)); ++ } else { ++ __ daddu(T3, T3, T2); ++ __ sdc1(FSF, T3, arrayOopDesc::base_offset_in_bytes(T_DOUBLE)); ++ } ++ } ++} ++ ++// used register : T2, T3, T8 ++// T2 : array ++// T3 : subklass ++// T8 : supklass ++void TemplateTable::aastore() { ++ Label is_null, ok_is_subtype, done; ++ transition(vtos, vtos); ++ // stack: ..., array, index, value ++ __ ld(FSR, at_tos()); // Value ++ __ lw(SSR, at_tos_p1()); // Index ++ __ ld(T2, at_tos_p2()); // Array ++ ++ // index_check(T2, SSR); ++ index_check_without_pop(T2, SSR); ++ // do array store check - check for NULL value first ++ __ beq(FSR, R0, is_null); ++ __ delayed()->nop(); ++ ++ // Move subklass into T3 ++ //add for compressedoops ++ __ load_klass(T3, FSR); ++ // Move superklass into T8 ++ //add for compressedoops ++ __ load_klass(T8, T2); ++ __ ld(T8, Address(T8, ObjArrayKlass::element_klass_offset())); ++ // Compress array+index*4+12 into a single register. T2 ++ __ dsll(AT, SSR, UseCompressedOops? Address::times_4 : Address::times_8); ++ __ daddu(T2, T2, AT); ++ __ daddiu(T2, T2, arrayOopDesc::base_offset_in_bytes(T_OBJECT)); ++ ++ // Generate subtype check. ++ // Superklass in T8. Subklass in T3. ++ __ gen_subtype_check(T8, T3, ok_is_subtype); ++ // Come here on failure ++ // object is at FSR ++ __ jmp(Interpreter::_throw_ArrayStoreException_entry); ++ __ delayed()->nop(); ++ // Come here on success ++ __ bind(ok_is_subtype); ++ do_oop_store(_masm, Address(T2, 0), FSR, _bs->kind(), true); ++ __ b(done); ++ __ delayed()->nop(); ++ ++ // Have a NULL in FSR, T2=array, SSR=index. Store NULL at ary[idx] ++ __ bind(is_null); ++ __ profile_null_seen(T9); ++ __ dsll(AT, SSR, UseCompressedOops? Address::times_4 : Address::times_8); ++ __ daddu(T2, T2, AT); ++ do_oop_store(_masm, Address(T2, arrayOopDesc::base_offset_in_bytes(T_OBJECT)), noreg, _bs->kind(), true); ++ ++ __ bind(done); ++ __ daddiu(SP, SP, 3 * Interpreter::stackElementSize); ++} ++ ++void TemplateTable::bastore() { ++ transition(itos, vtos); ++ __ pop_i(SSR); ++ if(UseBoundCheckInstruction) { ++ guarantee(false, "unimplemented yet!"); ++ __ pop_ptr(T2); ++ __ daddu(SSR, T2, SSR); ++ __ addiu(SSR, SSR, arrayOopDesc::base_offset_in_bytes(T_BYTE)); // base ++ ++ __ lw(AT, T2, arrayOopDesc::length_offset_in_bytes()); ++ __ daddu(AT, T2, AT); ++ __ addiu(AT, AT, arrayOopDesc::base_offset_in_bytes(T_BYTE)); //bound ++ ++ __ gssble(FSR, SSR, AT); ++ } else { ++ index_check(T2, SSR); ++ ++ // Need to check whether array is boolean or byte ++ // since both types share the bastore bytecode. ++ __ load_klass(T9, T2); ++ __ lw(T9, T9, in_bytes(Klass::layout_helper_offset())); ++ ++ int diffbit = Klass::layout_helper_boolean_diffbit(); ++ __ move(AT, diffbit); ++ ++ Label L_skip; ++ __ andr(AT, T9, AT); ++ __ beq(AT, R0, L_skip); ++ __ delayed()->nop(); ++ __ andi(FSR, FSR, 0x1); ++ __ bind(L_skip); ++ ++ if (UseLEXT1 && Assembler::is_simm(arrayOopDesc::base_offset_in_bytes(T_BYTE), 8)) { ++ __ gssbx(FSR, T2, SSR, arrayOopDesc::base_offset_in_bytes(T_BYTE)); ++ } else { ++ __ daddu(SSR, T2, SSR); ++ __ sb(FSR, SSR, arrayOopDesc::base_offset_in_bytes(T_BYTE)); ++ } ++ } ++} ++ ++void TemplateTable::castore() { ++ transition(itos, vtos); ++ __ pop_i(SSR); ++ if(UseBoundCheckInstruction) { ++ __ pop_ptr(T2); ++ __ dsll(SSR, SSR, Address::times_2); ++ __ daddu(SSR, T2, SSR); ++ __ addiu(SSR, SSR, arrayOopDesc::base_offset_in_bytes(T_CHAR)); // base ++ ++ __ lw(AT, T2, arrayOopDesc::length_offset_in_bytes()); ++ __ dsll(AT, AT, Address::times_2); ++ __ daddu(AT, T2, AT); ++ __ addiu(AT, AT, arrayOopDesc::base_offset_in_bytes(T_CHAR)); //bound ++ ++ __ gsshle(FSR, SSR, AT); ++ } else { ++ index_check(T2, SSR); ++ __ dsll(SSR, SSR, Address::times_2); ++ if (UseLEXT1 && Assembler::is_simm(arrayOopDesc::base_offset_in_bytes(T_CHAR), 8)) { ++ __ gsshx(FSR, T2, SSR, arrayOopDesc::base_offset_in_bytes(T_CHAR)); ++ } else { ++ __ daddu(SSR, T2, SSR); ++ __ sh(FSR, SSR, arrayOopDesc::base_offset_in_bytes(T_CHAR)); ++ } ++ } ++} ++ ++void TemplateTable::sastore() { ++ castore(); ++} ++ ++void TemplateTable::istore(int n) { ++ transition(itos, vtos); ++ __ sw(FSR, iaddress(n)); ++} ++ ++void TemplateTable::lstore(int n) { ++ transition(ltos, vtos); ++ __ sd(FSR, laddress(n)); ++} ++ ++void TemplateTable::fstore(int n) { ++ transition(ftos, vtos); ++ __ swc1(FSF, faddress(n)); ++} ++ ++void TemplateTable::dstore(int n) { ++ transition(dtos, vtos); ++ __ sdc1(FSF, laddress(n)); ++} ++ ++void TemplateTable::astore(int n) { ++ transition(vtos, vtos); ++ __ pop_ptr(FSR); ++ __ sd(FSR, aaddress(n)); ++} ++ ++void TemplateTable::pop() { ++ transition(vtos, vtos); ++ __ daddiu(SP, SP, Interpreter::stackElementSize); ++} ++ ++void TemplateTable::pop2() { ++ transition(vtos, vtos); ++ __ daddiu(SP, SP, 2 * Interpreter::stackElementSize); ++} ++ ++void TemplateTable::dup() { ++ transition(vtos, vtos); ++ // stack: ..., a ++ __ load_ptr(0, FSR); ++ __ push_ptr(FSR); ++ // stack: ..., a, a ++} ++ ++// blows FSR ++void TemplateTable::dup_x1() { ++ transition(vtos, vtos); ++ // stack: ..., a, b ++ __ load_ptr(0, FSR); // load b ++ __ load_ptr(1, A5); // load a ++ __ store_ptr(1, FSR); // store b ++ __ store_ptr(0, A5); // store a ++ __ push_ptr(FSR); // push b ++ // stack: ..., b, a, b ++} ++ ++// blows FSR ++void TemplateTable::dup_x2() { ++ transition(vtos, vtos); ++ // stack: ..., a, b, c ++ __ load_ptr(0, FSR); // load c ++ __ load_ptr(2, A5); // load a ++ __ store_ptr(2, FSR); // store c in a ++ __ push_ptr(FSR); // push c ++ // stack: ..., c, b, c, c ++ __ load_ptr(2, FSR); // load b ++ __ store_ptr(2, A5); // store a in b ++ // stack: ..., c, a, c, c ++ __ store_ptr(1, FSR); // store b in c ++ // stack: ..., c, a, b, c ++} ++ ++// blows FSR ++void TemplateTable::dup2() { ++ transition(vtos, vtos); ++ // stack: ..., a, b ++ __ load_ptr(1, FSR); // load a ++ __ push_ptr(FSR); // push a ++ __ load_ptr(1, FSR); // load b ++ __ push_ptr(FSR); // push b ++ // stack: ..., a, b, a, b ++} ++ ++// blows FSR ++void TemplateTable::dup2_x1() { ++ transition(vtos, vtos); ++ // stack: ..., a, b, c ++ __ load_ptr(0, T2); // load c ++ __ load_ptr(1, FSR); // load b ++ __ push_ptr(FSR); // push b ++ __ push_ptr(T2); // push c ++ // stack: ..., a, b, c, b, c ++ __ store_ptr(3, T2); // store c in b ++ // stack: ..., a, c, c, b, c ++ __ load_ptr(4, T2); // load a ++ __ store_ptr(2, T2); // store a in 2nd c ++ // stack: ..., a, c, a, b, c ++ __ store_ptr(4, FSR); // store b in a ++ // stack: ..., b, c, a, b, c ++ ++ // stack: ..., b, c, a, b, c ++} ++ ++// blows FSR, SSR ++void TemplateTable::dup2_x2() { ++ transition(vtos, vtos); ++ // stack: ..., a, b, c, d ++ // stack: ..., a, b, c, d ++ __ load_ptr(0, T2); // load d ++ __ load_ptr(1, FSR); // load c ++ __ push_ptr(FSR); // push c ++ __ push_ptr(T2); // push d ++ // stack: ..., a, b, c, d, c, d ++ __ load_ptr(4, FSR); // load b ++ __ store_ptr(2, FSR); // store b in d ++ __ store_ptr(4, T2); // store d in b ++ // stack: ..., a, d, c, b, c, d ++ __ load_ptr(5, T2); // load a ++ __ load_ptr(3, FSR); // load c ++ __ store_ptr(3, T2); // store a in c ++ __ store_ptr(5, FSR); // store c in a ++ // stack: ..., c, d, a, b, c, d ++ ++ // stack: ..., c, d, a, b, c, d ++} ++ ++// blows FSR ++void TemplateTable::swap() { ++ transition(vtos, vtos); ++ // stack: ..., a, b ++ ++ __ load_ptr(1, A5); // load a ++ __ load_ptr(0, FSR); // load b ++ __ store_ptr(0, A5); // store a in b ++ __ store_ptr(1, FSR); // store b in a ++ ++ // stack: ..., b, a ++} ++ ++void TemplateTable::iop2(Operation op) { ++ transition(itos, itos); ++ ++ __ pop_i(SSR); ++ switch (op) { ++ case add : __ addu32(FSR, SSR, FSR); break; ++ case sub : __ subu32(FSR, SSR, FSR); break; ++ case mul : __ mul(FSR, SSR, FSR); break; ++ case _and : __ andr(FSR, SSR, FSR); break; ++ case _or : __ orr(FSR, SSR, FSR); break; ++ case _xor : __ xorr(FSR, SSR, FSR); break; ++ case shl : __ sllv(FSR, SSR, FSR); break; ++ case shr : __ srav(FSR, SSR, FSR); break; ++ case ushr : __ srlv(FSR, SSR, FSR); break; ++ default : ShouldNotReachHere(); ++ } ++} ++ ++// the result stored in FSR, SSR, ++// used registers : T2, T3 ++void TemplateTable::lop2(Operation op) { ++ transition(ltos, ltos); ++ __ pop_l(T2); ++ ++ switch (op) { ++ case add : __ daddu(FSR, T2, FSR); break; ++ case sub : __ dsubu(FSR, T2, FSR); break; ++ case _and: __ andr(FSR, T2, FSR); break; ++ case _or : __ orr(FSR, T2, FSR); break; ++ case _xor: __ xorr(FSR, T2, FSR); break; ++ default : ShouldNotReachHere(); ++ } ++} ++ ++// java require this bytecode could handle 0x80000000/-1, dont cause a overflow exception, ++// the result is 0x80000000 ++// the godson2 cpu do the same, so we need not handle this specially like x86 ++void TemplateTable::idiv() { ++ transition(itos, itos); ++ Label not_zero; ++ ++ __ bne(FSR, R0, not_zero); ++ __ delayed()->nop(); ++ __ jmp(Interpreter::_throw_ArithmeticException_entry); ++ __ delayed()->nop(); ++ __ bind(not_zero); ++ ++ __ pop_i(SSR); ++ if (UseLEXT1) { ++ __ gsdiv(FSR, SSR, FSR); ++ } else { ++ __ div(SSR, FSR); ++ __ mflo(FSR); ++ } ++} ++ ++void TemplateTable::irem() { ++ transition(itos, itos); ++ Label not_zero; ++ __ pop_i(SSR); ++ __ div(SSR, FSR); ++ ++ __ bne(FSR, R0, not_zero); ++ __ delayed()->nop(); ++ //__ brk(7); ++ __ jmp(Interpreter::_throw_ArithmeticException_entry); ++ __ delayed()->nop(); ++ ++ __ bind(not_zero); ++ __ mfhi(FSR); ++} ++ ++void TemplateTable::lmul() { ++ transition(ltos, ltos); ++ __ pop_l(T2); ++ if (UseLEXT1) { ++ __ gsdmult(FSR, T2, FSR); ++ } else { ++ __ dmult(T2, FSR); ++ __ mflo(FSR); ++ } ++} ++ ++// NOTE: i DONT use the Interpreter::_throw_ArithmeticException_entry ++void TemplateTable::ldiv() { ++ transition(ltos, ltos); ++ Label normal; ++ ++ __ bne(FSR, R0, normal); ++ __ delayed()->nop(); ++ ++ //__ brk(7); //generate FPE ++ __ jmp(Interpreter::_throw_ArithmeticException_entry); ++ __ delayed()->nop(); ++ ++ __ bind(normal); ++ __ pop_l(A2); ++ if (UseLEXT1) { ++ __ gsddiv(FSR, A2, FSR); ++ } else { ++ __ ddiv(A2, FSR); ++ __ mflo(FSR); ++ } ++} ++ ++// NOTE: i DONT use the Interpreter::_throw_ArithmeticException_entry ++void TemplateTable::lrem() { ++ transition(ltos, ltos); ++ Label normal; ++ ++ __ bne(FSR, R0, normal); ++ __ delayed()->nop(); ++ ++ __ jmp(Interpreter::_throw_ArithmeticException_entry); ++ __ delayed()->nop(); ++ ++ __ bind(normal); ++ __ pop_l (A2); ++ ++ if (UseLEXT1) { ++ __ gsdmod(FSR, A2, FSR); ++ } else { ++ __ ddiv(A2, FSR); ++ __ mfhi(FSR); ++ } ++} ++ ++// result in FSR ++// used registers : T0 ++void TemplateTable::lshl() { ++ transition(itos, ltos); ++ __ pop_l(T0); ++ __ dsllv(FSR, T0, FSR); ++} ++ ++// used registers : T0 ++void TemplateTable::lshr() { ++ transition(itos, ltos); ++ __ pop_l(T0); ++ __ dsrav(FSR, T0, FSR); ++} ++ ++// used registers : T0 ++void TemplateTable::lushr() { ++ transition(itos, ltos); ++ __ pop_l(T0); ++ __ dsrlv(FSR, T0, FSR); ++} ++ ++// result in FSF ++void TemplateTable::fop2(Operation op) { ++ transition(ftos, ftos); ++ switch (op) { ++ case add: ++ __ lwc1(FTF, at_sp()); ++ __ add_s(FSF, FTF, FSF); ++ break; ++ case sub: ++ __ lwc1(FTF, at_sp()); ++ __ sub_s(FSF, FTF, FSF); ++ break; ++ case mul: ++ __ lwc1(FTF, at_sp()); ++ __ mul_s(FSF, FTF, FSF); ++ break; ++ case div: ++ __ lwc1(FTF, at_sp()); ++ __ div_s(FSF, FTF, FSF); ++ break; ++ case rem: ++ __ mov_s(F13, FSF); ++ __ lwc1(F12, at_sp()); ++ __ call_VM_leaf(CAST_FROM_FN_PTR(address, SharedRuntime::frem), 2); ++ break; ++ default : ShouldNotReachHere(); ++ } ++ ++ __ daddiu(SP, SP, 1 * wordSize); ++} ++ ++// result in SSF||FSF ++// i dont handle the strict flags ++void TemplateTable::dop2(Operation op) { ++ transition(dtos, dtos); ++ switch (op) { ++ case add: ++ __ ldc1(FTF, at_sp()); ++ __ add_d(FSF, FTF, FSF); ++ break; ++ case sub: ++ __ ldc1(FTF, at_sp()); ++ __ sub_d(FSF, FTF, FSF); ++ break; ++ case mul: ++ __ ldc1(FTF, at_sp()); ++ __ mul_d(FSF, FTF, FSF); ++ break; ++ case div: ++ __ ldc1(FTF, at_sp()); ++ __ div_d(FSF, FTF, FSF); ++ break; ++ case rem: ++ __ mov_d(F13, FSF); ++ __ ldc1(F12, at_sp()); ++ __ call_VM_leaf(CAST_FROM_FN_PTR(address, SharedRuntime::drem), 2); ++ break; ++ default : ShouldNotReachHere(); ++ } ++ ++ __ daddiu(SP, SP, 2 * wordSize); ++} ++ ++void TemplateTable::ineg() { ++ transition(itos, itos); ++ __ subu32(FSR, R0, FSR); ++} ++ ++void TemplateTable::lneg() { ++ transition(ltos, ltos); ++ __ dsubu(FSR, R0, FSR); ++} ++ ++void TemplateTable::fneg() { ++ transition(ftos, ftos); ++ __ neg_s(FSF, FSF); ++} ++ ++void TemplateTable::dneg() { ++ transition(dtos, dtos); ++ __ neg_d(FSF, FSF); ++} ++ ++// used registers : T2 ++void TemplateTable::iinc() { ++ transition(vtos, vtos); ++ locals_index(T2); ++ __ lw(FSR, T2, 0); ++ __ lb(AT, at_bcp(2)); // get constant ++ __ daddu(FSR, FSR, AT); ++ __ sw(FSR, T2, 0); ++} ++ ++// used register : T2 ++void TemplateTable::wide_iinc() { ++ transition(vtos, vtos); ++ locals_index_wide(T2); ++ __ get_2_byte_integer_at_bcp(FSR, AT, 4); ++ __ hswap(FSR); ++ __ lw(AT, T2, 0); ++ __ daddu(FSR, AT, FSR); ++ __ sw(FSR, T2, 0); ++} ++ ++void TemplateTable::convert() { ++ // Checking ++#ifdef ASSERT ++ { ++ TosState tos_in = ilgl; ++ TosState tos_out = ilgl; ++ switch (bytecode()) { ++ case Bytecodes::_i2l: // fall through ++ case Bytecodes::_i2f: // fall through ++ case Bytecodes::_i2d: // fall through ++ case Bytecodes::_i2b: // fall through ++ case Bytecodes::_i2c: // fall through ++ case Bytecodes::_i2s: tos_in = itos; break; ++ case Bytecodes::_l2i: // fall through ++ case Bytecodes::_l2f: // fall through ++ case Bytecodes::_l2d: tos_in = ltos; break; ++ case Bytecodes::_f2i: // fall through ++ case Bytecodes::_f2l: // fall through ++ case Bytecodes::_f2d: tos_in = ftos; break; ++ case Bytecodes::_d2i: // fall through ++ case Bytecodes::_d2l: // fall through ++ case Bytecodes::_d2f: tos_in = dtos; break; ++ default : ShouldNotReachHere(); ++ } ++ switch (bytecode()) { ++ case Bytecodes::_l2i: // fall through ++ case Bytecodes::_f2i: // fall through ++ case Bytecodes::_d2i: // fall through ++ case Bytecodes::_i2b: // fall through ++ case Bytecodes::_i2c: // fall through ++ case Bytecodes::_i2s: tos_out = itos; break; ++ case Bytecodes::_i2l: // fall through ++ case Bytecodes::_f2l: // fall through ++ case Bytecodes::_d2l: tos_out = ltos; break; ++ case Bytecodes::_i2f: // fall through ++ case Bytecodes::_l2f: // fall through ++ case Bytecodes::_d2f: tos_out = ftos; break; ++ case Bytecodes::_i2d: // fall through ++ case Bytecodes::_l2d: // fall through ++ case Bytecodes::_f2d: tos_out = dtos; break; ++ default : ShouldNotReachHere(); ++ } ++ transition(tos_in, tos_out); ++ } ++#endif // ASSERT ++ ++ // Conversion ++ switch (bytecode()) { ++ case Bytecodes::_i2l: ++ __ sll(FSR, FSR, 0); ++ break; ++ case Bytecodes::_i2f: ++ __ mtc1(FSR, FSF); ++ __ cvt_s_w(FSF, FSF); ++ break; ++ case Bytecodes::_i2d: ++ __ mtc1(FSR, FSF); ++ __ cvt_d_w(FSF, FSF); ++ break; ++ case Bytecodes::_i2b: ++ __ seb(FSR, FSR); ++ break; ++ case Bytecodes::_i2c: ++ __ andi(FSR, FSR, 0xFFFF); // truncate upper 56 bits ++ break; ++ case Bytecodes::_i2s: ++ __ seh(FSR, FSR); ++ break; ++ case Bytecodes::_l2i: ++ __ sll(FSR, FSR, 0); ++ break; ++ case Bytecodes::_l2f: ++ __ dmtc1(FSR, FSF); ++ __ cvt_s_l(FSF, FSF); ++ break; ++ case Bytecodes::_l2d: ++ __ dmtc1(FSR, FSF); ++ __ cvt_d_l(FSF, FSF); ++ break; ++ case Bytecodes::_f2i: ++ { ++ Label L; ++ ++ __ trunc_w_s(F12, FSF); ++ __ move(AT, 0x7fffffff); ++ __ mfc1(FSR, F12); ++ __ c_un_s(FSF, FSF); //NaN? ++ __ movt(FSR, R0); ++ ++ __ bne(AT, FSR, L); ++ __ delayed()->lui(T9, 0x8000); ++ ++ __ mfc1(AT, FSF); ++ __ andr(AT, AT, T9); ++ ++ __ movn(FSR, T9, AT); ++ ++ __ bind(L); ++ } ++ break; ++ case Bytecodes::_f2l: ++ { ++ Label L; ++ ++ __ trunc_l_s(F12, FSF); ++ __ daddiu(AT, R0, -1); ++ __ dsrl(AT, AT, 1); ++ __ dmfc1(FSR, F12); ++ __ c_un_s(FSF, FSF); //NaN? ++ __ movt(FSR, R0); ++ ++ __ bne(AT, FSR, L); ++ __ delayed()->lui(T9, 0x8000); ++ ++ __ mfc1(AT, FSF); ++ __ andr(AT, AT, T9); ++ ++ __ dsll32(T9, T9, 0); ++ __ movn(FSR, T9, AT); ++ ++ __ bind(L); ++ } ++ break; ++ case Bytecodes::_f2d: ++ __ cvt_d_s(FSF, FSF); ++ break; ++ case Bytecodes::_d2i: ++ { ++ Label L; ++ ++ __ trunc_w_d(F12, FSF); ++ __ move(AT, 0x7fffffff); ++ __ mfc1(FSR, F12); ++ ++ __ bne(FSR, AT, L); ++ __ delayed()->mtc1(R0, F12); ++ ++ __ cvt_d_w(F12, F12); ++ __ c_ult_d(FSF, F12); ++ __ bc1f(L); ++ __ delayed()->addiu(T9, R0, -1); ++ ++ __ c_un_d(FSF, FSF); //NaN? ++ __ subu32(FSR, T9, AT); ++ __ movt(FSR, R0); ++ ++ __ bind(L); ++ } ++ break; ++ case Bytecodes::_d2l: ++ { ++ Label L; ++ ++ __ trunc_l_d(F12, FSF); ++ __ daddiu(AT, R0, -1); ++ __ dsrl(AT, AT, 1); ++ __ dmfc1(FSR, F12); ++ ++ __ bne(FSR, AT, L); ++ __ delayed()->mtc1(R0, F12); ++ ++ __ cvt_d_w(F12, F12); ++ __ c_ult_d(FSF, F12); ++ __ bc1f(L); ++ __ delayed()->daddiu(T9, R0, -1); ++ ++ __ c_un_d(FSF, FSF); //NaN? ++ __ subu(FSR, T9, AT); ++ __ movt(FSR, R0); ++ ++ __ bind(L); ++ } ++ break; ++ case Bytecodes::_d2f: ++ __ cvt_s_d(FSF, FSF); ++ break; ++ default : ++ ShouldNotReachHere(); ++ } ++} ++ ++void TemplateTable::lcmp() { ++ transition(ltos, itos); ++ ++ Label low, high, done; ++ __ pop(T0); ++ __ pop(R0); ++ __ slt(AT, T0, FSR); ++ __ bne(AT, R0, low); ++ __ delayed()->nop(); ++ ++ __ bne(T0, FSR, high); ++ __ delayed()->nop(); ++ ++ __ li(FSR, (long)0); ++ __ b(done); ++ __ delayed()->nop(); ++ ++ __ bind(low); ++ __ li(FSR, (long)-1); ++ __ b(done); ++ __ delayed()->nop(); ++ ++ __ bind(high); ++ __ li(FSR, (long)1); ++ __ b(done); ++ __ delayed()->nop(); ++ ++ __ bind(done); ++} ++ ++void TemplateTable::float_cmp(bool is_float, int unordered_result) { ++ Label less, done; ++ ++ __ move(FSR, R0); ++ ++ if (is_float) { ++ __ lwc1(FTF, at_sp()); ++ __ c_eq_s(FTF, FSF); ++ __ bc1t(done); ++ __ delayed()->daddiu(SP, SP, 1 * wordSize); ++ ++ if (unordered_result<0) ++ __ c_ult_s(FTF, FSF); ++ else ++ __ c_olt_s(FTF, FSF); ++ } else { ++ __ ldc1(FTF, at_sp()); ++ __ c_eq_d(FTF, FSF); ++ __ bc1t(done); ++ __ delayed()->daddiu(SP, SP, 2 * wordSize); ++ ++ if (unordered_result<0) ++ __ c_ult_d(FTF, FSF); ++ else ++ __ c_olt_d(FTF, FSF); ++ } ++ __ bc1t(less); ++ __ delayed()->nop(); ++ __ move(FSR, 1); ++ __ b(done); ++ __ delayed()->nop(); ++ __ bind(less); ++ __ move(FSR, -1); ++ __ bind(done); ++} ++ ++ ++// used registers : T3, A7, Rnext ++// FSR : return bci, this is defined by the vm specification ++// T2 : MDO taken count ++// T3 : method ++// A7 : offset ++// Rnext : next bytecode, this is required by dispatch_base ++void TemplateTable::branch(bool is_jsr, bool is_wide) { ++ __ get_method(T3); ++ __ profile_taken_branch(A7, T2); // only C2 meaningful ++ ++ const ByteSize be_offset = MethodCounters::backedge_counter_offset() + ++ InvocationCounter::counter_offset(); ++ const ByteSize inv_offset = MethodCounters::invocation_counter_offset() + ++ InvocationCounter::counter_offset(); ++ ++ // Load up T4 with the branch displacement ++ if (!is_wide) { ++ __ lb(A7, BCP, 1); ++ __ lbu(AT, BCP, 2); ++ __ dsll(A7, A7, 8); ++ __ orr(A7, A7, AT); ++ } else { ++ __ get_4_byte_integer_at_bcp(A7, AT, 1); ++ __ swap(A7); ++ } ++ ++ // Handle all the JSR stuff here, then exit. ++ // It's much shorter and cleaner than intermingling with the non-JSR ++ // normal-branch stuff occuring below. ++ if (is_jsr) { ++ // Pre-load the next target bytecode into Rnext ++ __ daddu(AT, BCP, A7); ++ __ lbu(Rnext, AT, 0); ++ ++ // compute return address as bci in FSR ++ __ daddiu(FSR, BCP, (is_wide?5:3) - in_bytes(ConstMethod::codes_offset())); ++ __ ld(AT, T3, in_bytes(Method::const_offset())); ++ __ dsubu(FSR, FSR, AT); ++ // Adjust the bcp in BCP by the displacement in A7 ++ __ daddu(BCP, BCP, A7); ++ // jsr returns atos that is not an oop ++ // Push return address ++ __ push_i(FSR); ++ // jsr returns vtos ++ __ dispatch_only_noverify(vtos); ++ ++ return; ++ } ++ ++ // Normal (non-jsr) branch handling ++ ++ // Adjust the bcp in S0 by the displacement in T4 ++ __ daddu(BCP, BCP, A7); ++ ++ assert(UseLoopCounter || !UseOnStackReplacement, "on-stack-replacement requires loop counters"); ++ Label backedge_counter_overflow; ++ Label profile_method; ++ Label dispatch; ++ if (UseLoopCounter) { ++ // increment backedge counter for backward branches ++ // T3: method ++ // T4: target offset ++ // BCP: target bcp ++ // LVP: locals pointer ++ __ bgtz(A7, dispatch); // check if forward or backward branch ++ __ delayed()->nop(); ++ ++ // check if MethodCounters exists ++ Label has_counters; ++ __ ld(AT, T3, in_bytes(Method::method_counters_offset())); // use AT as MDO, TEMP ++ __ bne(AT, R0, has_counters); ++ __ delayed()->nop(); ++ __ push(T3); ++ __ call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::build_method_counters), ++ T3); ++ __ pop(T3); ++ __ ld(AT, T3, in_bytes(Method::method_counters_offset())); // use AT as MDO, TEMP ++ __ beq(AT, R0, dispatch); ++ __ delayed()->nop(); ++ __ bind(has_counters); ++ ++ if (TieredCompilation) { ++ Label no_mdo; ++ int increment = InvocationCounter::count_increment; ++ int mask = ((1 << Tier0BackedgeNotifyFreqLog) - 1) << InvocationCounter::count_shift; ++ if (ProfileInterpreter) { ++ // Are we profiling? ++ __ ld(T0, Address(T3, in_bytes(Method::method_data_offset()))); ++ __ beq(T0, R0, no_mdo); ++ __ delayed()->nop(); ++ // Increment the MDO backedge counter ++ const Address mdo_backedge_counter(T0, in_bytes(MethodData::backedge_counter_offset()) + ++ in_bytes(InvocationCounter::counter_offset())); ++ __ increment_mask_and_jump(mdo_backedge_counter, increment, mask, ++ T1, false, Assembler::zero, &backedge_counter_overflow); ++ __ beq(R0, R0, dispatch); ++ __ delayed()->nop(); ++ } ++ __ bind(no_mdo); ++ // Increment backedge counter in MethodCounters* ++ __ ld(T0, Address(T3, Method::method_counters_offset())); ++ __ increment_mask_and_jump(Address(T0, be_offset), increment, mask, ++ T1, false, Assembler::zero, &backedge_counter_overflow); ++ if (!UseOnStackReplacement) { ++ __ bind(backedge_counter_overflow); ++ } ++ } else { ++ // increment back edge counter ++ __ ld(T1, T3, in_bytes(Method::method_counters_offset())); ++ __ lw(T0, T1, in_bytes(be_offset)); ++ __ increment(T0, InvocationCounter::count_increment); ++ __ sw(T0, T1, in_bytes(be_offset)); ++ ++ // load invocation counter ++ __ lw(T1, T1, in_bytes(inv_offset)); ++ // buffer bit added, mask no needed ++ ++ // daddu backedge counter & invocation counter ++ __ daddu(T1, T1, T0); ++ ++ if (ProfileInterpreter) { ++ // Test to see if we should create a method data oop ++ // T1 : backedge counter & invocation counter ++ if (Assembler::is_simm16(InvocationCounter::InterpreterProfileLimit)) { ++ __ slti(AT, T1, InvocationCounter::InterpreterProfileLimit); ++ } else { ++ __ li(AT, (long)&InvocationCounter::InterpreterProfileLimit); ++ __ lw(AT, AT, 0); ++ __ slt(AT, T1, AT); ++ } ++ ++ __ bne(AT, R0, dispatch); ++ __ delayed()->nop(); ++ ++ // if no method data exists, go to profile method ++ __ test_method_data_pointer(T1, profile_method); ++ ++ if (UseOnStackReplacement) { ++ if (Assembler::is_simm16(InvocationCounter::InterpreterBackwardBranchLimit)) { ++ __ slti(AT, T2, InvocationCounter::InterpreterBackwardBranchLimit); ++ } else { ++ __ li(AT, (long)&InvocationCounter::InterpreterBackwardBranchLimit); ++ __ lw(AT, AT, 0); ++ __ slt(AT, T2, AT); ++ } ++ ++ __ bne(AT, R0, dispatch); ++ __ delayed()->nop(); ++ ++ // When ProfileInterpreter is on, the backedge_count comes ++ // from the methodDataOop, which value does not get reset on ++ // the call to frequency_counter_overflow(). ++ // To avoid excessive calls to the overflow routine while ++ // the method is being compiled, daddu a second test to make ++ // sure the overflow function is called only once every ++ // overflow_frequency. ++ const int overflow_frequency = 1024; ++ __ andi(AT, T2, overflow_frequency-1); ++ __ beq(AT, R0, backedge_counter_overflow); ++ __ delayed()->nop(); ++ } ++ } else { ++ if (UseOnStackReplacement) { ++ // check for overflow against AT, which is the sum of the counters ++ __ li(AT, (long)&InvocationCounter::InterpreterBackwardBranchLimit); ++ __ lw(AT, AT, 0); ++ __ slt(AT, T1, AT); ++ __ beq(AT, R0, backedge_counter_overflow); ++ __ delayed()->nop(); ++ } ++ } ++ } ++ __ bind(dispatch); ++ } ++ ++ // Pre-load the next target bytecode into Rnext ++ __ lbu(Rnext, BCP, 0); ++ ++ // continue with the bytecode @ target ++ // FSR: return bci for jsr's, unused otherwise ++ // Rnext: target bytecode ++ // BCP: target bcp ++ __ dispatch_only(vtos); ++ ++ if (UseLoopCounter) { ++ if (ProfileInterpreter) { ++ // Out-of-line code to allocate method data oop. ++ __ bind(profile_method); ++ __ call_VM(NOREG, CAST_FROM_FN_PTR(address, InterpreterRuntime::profile_method)); ++ __ lbu(Rnext, BCP, 0); ++ __ set_method_data_pointer_for_bcp(); ++ __ b(dispatch); ++ __ delayed()->nop(); ++ } ++ ++ if (UseOnStackReplacement) { ++ // invocation counter overflow ++ __ bind(backedge_counter_overflow); ++ __ subu(A7, BCP, A7); // branch bcp ++ call_VM(NOREG, CAST_FROM_FN_PTR(address, ++ InterpreterRuntime::frequency_counter_overflow), A7); ++ __ lbu(Rnext, BCP, 0); ++ ++ // V0: osr nmethod (osr ok) or NULL (osr not possible) ++ // V1: osr adapter frame return address ++ // Rnext: target bytecode ++ // LVP: locals pointer ++ // BCP: bcp ++ __ beq(V0, R0, dispatch); ++ __ delayed()->nop(); ++ // nmethod may have been invalidated (VM may block upon call_VM return) ++ __ lw(T3, V0, nmethod::entry_bci_offset()); ++ __ move(AT, InvalidOSREntryBci); ++ __ beq(AT, T3, dispatch); ++ __ delayed()->nop(); ++ // We need to prepare to execute the OSR method. First we must ++ // migrate the locals and monitors off of the stack. ++ //V0: osr nmethod (osr ok) or NULL (osr not possible) ++ //V1: osr adapter frame return address ++ //Rnext: target bytecode ++ //LVP: locals pointer ++ //BCP: bcp ++ __ move(BCP, V0); ++ const Register thread = TREG; ++#ifndef OPT_THREAD ++ __ get_thread(thread); ++#endif ++ call_VM(noreg, CAST_FROM_FN_PTR(address, SharedRuntime::OSR_migration_begin)); ++ ++ // V0 is OSR buffer, move it to expected parameter location ++ // refer to osrBufferPointer in c1_LIRAssembler_mips.cpp ++ __ move(T0, V0); ++ ++ // pop the interpreter frame ++ __ ld(A7, Address(FP, frame::interpreter_frame_sender_sp_offset * wordSize)); ++ //FIXME, shall we keep the return address on the stack? ++ __ leave(); // remove frame anchor ++ __ move(LVP, RA); ++ __ move(SP, A7); ++ ++ __ move(AT, -(StackAlignmentInBytes)); ++ __ andr(SP , SP , AT); ++ ++ // push the (possibly adjusted) return address ++ //refer to osr_entry in c1_LIRAssembler_mips.cpp ++ __ ld(AT, BCP, nmethod::osr_entry_point_offset()); ++ __ jr(AT); ++ __ delayed()->nop(); ++ } ++ } ++} ++ ++ ++void TemplateTable::if_0cmp(Condition cc) { ++ transition(itos, vtos); ++ // assume branch is more often taken than not (loops use backward branches) ++ Label not_taken; ++ switch(cc) { ++ case not_equal: ++ __ beq(FSR, R0, not_taken); ++ break; ++ case equal: ++ __ bne(FSR, R0, not_taken); ++ break; ++ case less: ++ __ bgez(FSR, not_taken); ++ break; ++ case less_equal: ++ __ bgtz(FSR, not_taken); ++ break; ++ case greater: ++ __ blez(FSR, not_taken); ++ break; ++ case greater_equal: ++ __ bltz(FSR, not_taken); ++ break; ++ } ++ __ delayed()->nop(); ++ ++ branch(false, false); ++ ++ __ bind(not_taken); ++ __ profile_not_taken_branch(FSR); ++} ++ ++void TemplateTable::if_icmp(Condition cc) { ++ transition(itos, vtos); ++ // assume branch is more often taken than not (loops use backward branches) ++ Label not_taken; ++ ++ __ pop_i(SSR); ++ switch(cc) { ++ case not_equal: ++ __ beq(SSR, FSR, not_taken); ++ break; ++ case equal: ++ __ bne(SSR, FSR, not_taken); ++ break; ++ case less: ++ __ slt(AT, SSR, FSR); ++ __ beq(AT, R0, not_taken); ++ break; ++ case less_equal: ++ __ slt(AT, FSR, SSR); ++ __ bne(AT, R0, not_taken); ++ break; ++ case greater: ++ __ slt(AT, FSR, SSR); ++ __ beq(AT, R0, not_taken); ++ break; ++ case greater_equal: ++ __ slt(AT, SSR, FSR); ++ __ bne(AT, R0, not_taken); ++ break; ++ } ++ __ delayed()->nop(); ++ ++ branch(false, false); ++ __ bind(not_taken); ++ __ profile_not_taken_branch(FSR); ++} ++ ++void TemplateTable::if_nullcmp(Condition cc) { ++ transition(atos, vtos); ++ // assume branch is more often taken than not (loops use backward branches) ++ Label not_taken; ++ switch(cc) { ++ case not_equal: ++ __ beq(FSR, R0, not_taken); ++ break; ++ case equal: ++ __ bne(FSR, R0, not_taken); ++ break; ++ default: ++ ShouldNotReachHere(); ++ } ++ __ delayed()->nop(); ++ ++ branch(false, false); ++ __ bind(not_taken); ++ __ profile_not_taken_branch(FSR); ++} ++ ++ ++void TemplateTable::if_acmp(Condition cc) { ++ transition(atos, vtos); ++ // assume branch is more often taken than not (loops use backward branches) ++ Label not_taken; ++ // __ lw(SSR, SP, 0); ++ __ pop_ptr(SSR); ++ switch(cc) { ++ case not_equal: ++ __ beq(SSR, FSR, not_taken); ++ break; ++ case equal: ++ __ bne(SSR, FSR, not_taken); ++ break; ++ default: ++ ShouldNotReachHere(); ++ } ++ __ delayed()->nop(); ++ ++ branch(false, false); ++ ++ __ bind(not_taken); ++ __ profile_not_taken_branch(FSR); ++} ++ ++// used registers : T1, T2, T3 ++// T1 : method ++// T2 : returb bci ++void TemplateTable::ret() { ++ transition(vtos, vtos); ++ ++ locals_index(T2); ++ __ ld(T2, T2, 0); ++ __ profile_ret(T2, T3); ++ ++ __ get_method(T1); ++ __ ld(BCP, T1, in_bytes(Method::const_offset())); ++ __ daddu(BCP, BCP, T2); ++ __ daddiu(BCP, BCP, in_bytes(ConstMethod::codes_offset())); ++ ++ __ dispatch_next(vtos); ++} ++ ++// used registers : T1, T2, T3 ++// T1 : method ++// T2 : returb bci ++void TemplateTable::wide_ret() { ++ transition(vtos, vtos); ++ ++ locals_index_wide(T2); ++ __ ld(T2, T2, 0); // get return bci, compute return bcp ++ __ profile_ret(T2, T3); ++ ++ __ get_method(T1); ++ __ ld(BCP, T1, in_bytes(Method::const_offset())); ++ __ daddu(BCP, BCP, T2); ++ __ daddiu(BCP, BCP, in_bytes(ConstMethod::codes_offset())); ++ ++ __ dispatch_next(vtos); ++} ++ ++// used register T2, T3, A7, Rnext ++// T2 : bytecode pointer ++// T3 : low ++// A7 : high ++// Rnext : dest bytecode, required by dispatch_base ++void TemplateTable::tableswitch() { ++ Label default_case, continue_execution; ++ transition(itos, vtos); ++ ++ // align BCP ++ __ daddiu(T2, BCP, BytesPerInt); ++ __ li(AT, -BytesPerInt); ++ __ andr(T2, T2, AT); ++ ++ // load lo & hi ++ __ lw(T3, T2, 1 * BytesPerInt); ++ __ swap(T3); ++ __ lw(A7, T2, 2 * BytesPerInt); ++ __ swap(A7); ++ ++ // check against lo & hi ++ __ slt(AT, FSR, T3); ++ __ bne(AT, R0, default_case); ++ __ delayed()->nop(); ++ ++ __ slt(AT, A7, FSR); ++ __ bne(AT, R0, default_case); ++ __ delayed()->nop(); ++ ++ // lookup dispatch offset, in A7 big endian ++ __ dsubu(FSR, FSR, T3); ++ __ dsll(AT, FSR, Address::times_4); ++ __ daddu(AT, T2, AT); ++ __ lw(A7, AT, 3 * BytesPerInt); ++ __ profile_switch_case(FSR, T9, T3); ++ ++ __ bind(continue_execution); ++ __ swap(A7); ++ __ daddu(BCP, BCP, A7); ++ __ lbu(Rnext, BCP, 0); ++ __ dispatch_only(vtos); ++ ++ // handle default ++ __ bind(default_case); ++ __ profile_switch_default(FSR); ++ __ lw(A7, T2, 0); ++ __ b(continue_execution); ++ __ delayed()->nop(); ++} ++ ++void TemplateTable::lookupswitch() { ++ transition(itos, itos); ++ __ stop("lookupswitch bytecode should have been rewritten"); ++} ++ ++// used registers : T2, T3, A7, Rnext ++// T2 : bytecode pointer ++// T3 : pair index ++// A7 : offset ++// Rnext : dest bytecode ++// the data after the opcode is the same as lookupswitch ++// see Rewriter::rewrite_method for more information ++void TemplateTable::fast_linearswitch() { ++ transition(itos, vtos); ++ Label loop_entry, loop, found, continue_execution; ++ ++ // swap FSR so we can avoid swapping the table entries ++ __ swap(FSR); ++ ++ // align BCP ++ __ daddiu(T2, BCP, BytesPerInt); ++ __ li(AT, -BytesPerInt); ++ __ andr(T2, T2, AT); ++ ++ // set counter ++ __ lw(T3, T2, BytesPerInt); ++ __ swap(T3); ++ __ b(loop_entry); ++ __ delayed()->nop(); ++ ++ // table search ++ __ bind(loop); ++ // get the entry value ++ __ dsll(AT, T3, Address::times_8); ++ __ daddu(AT, T2, AT); ++ __ lw(AT, AT, 2 * BytesPerInt); ++ ++ // found? ++ __ beq(FSR, AT, found); ++ __ delayed()->nop(); ++ ++ __ bind(loop_entry); ++ __ bgtz(T3, loop); ++ __ delayed()->daddiu(T3, T3, -1); ++ ++ // default case ++ __ profile_switch_default(FSR); ++ __ lw(A7, T2, 0); ++ __ b(continue_execution); ++ __ delayed()->nop(); ++ ++ // entry found -> get offset ++ __ bind(found); ++ __ dsll(AT, T3, Address::times_8); ++ __ daddu(AT, T2, AT); ++ __ lw(A7, AT, 3 * BytesPerInt); ++ __ profile_switch_case(T3, FSR, T2); ++ ++ // continue execution ++ __ bind(continue_execution); ++ __ swap(A7); ++ __ daddu(BCP, BCP, A7); ++ __ lbu(Rnext, BCP, 0); ++ __ dispatch_only(vtos); ++} ++ ++// used registers : T0, T1, T2, T3, A7, Rnext ++// T2 : pairs address(array) ++// Rnext : dest bytecode ++// the data after the opcode is the same as lookupswitch ++// see Rewriter::rewrite_method for more information ++void TemplateTable::fast_binaryswitch() { ++ transition(itos, vtos); ++ // Implementation using the following core algorithm: ++ // ++ // int binary_search(int key, LookupswitchPair* array, int n) { ++ // // Binary search according to "Methodik des Programmierens" by ++ // // Edsger W. Dijkstra and W.H.J. Feijen, Addison Wesley Germany 1985. ++ // int i = 0; ++ // int j = n; ++ // while (i+1 < j) { ++ // // invariant P: 0 <= i < j <= n and (a[i] <= key < a[j] or Q) ++ // // with Q: for all i: 0 <= i < n: key < a[i] ++ // // where a stands for the array and assuming that the (inexisting) ++ // // element a[n] is infinitely big. ++ // int h = (i + j) >> 1; ++ // // i < h < j ++ // if (key < array[h].fast_match()) { ++ // j = h; ++ // } else { ++ // i = h; ++ // } ++ // } ++ // // R: a[i] <= key < a[i+1] or Q ++ // // (i.e., if key is within array, i is the correct index) ++ // return i; ++ // } ++ ++ // register allocation ++ const Register array = T2; ++ const Register i = T3, j = A7; ++ const Register h = T1; ++ const Register temp = T0; ++ const Register key = FSR; ++ ++ // setup array ++ __ daddiu(array, BCP, 3*BytesPerInt); ++ __ li(AT, -BytesPerInt); ++ __ andr(array, array, AT); ++ ++ // initialize i & j ++ __ move(i, R0); ++ __ lw(j, array, - 1 * BytesPerInt); ++ // Convert j into native byteordering ++ __ swap(j); ++ ++ // and start ++ Label entry; ++ __ b(entry); ++ __ delayed()->nop(); ++ ++ // binary search loop ++ { ++ Label loop; ++ __ bind(loop); ++ // int h = (i + j) >> 1; ++ __ daddu(h, i, j); ++ __ dsrl(h, h, 1); ++ // if (key < array[h].fast_match()) { ++ // j = h; ++ // } else { ++ // i = h; ++ // } ++ // Convert array[h].match to native byte-ordering before compare ++ __ dsll(AT, h, Address::times_8); ++ __ daddu(AT, array, AT); ++ __ lw(temp, AT, 0 * BytesPerInt); ++ __ swap(temp); ++ ++ { ++ Label set_i, end_of_if; ++ __ slt(AT, key, temp); ++ __ beq(AT, R0, set_i); ++ __ delayed()->nop(); ++ ++ __ b(end_of_if); ++ __ delayed(); __ move(j, h); ++ ++ __ bind(set_i); ++ __ move(i, h); ++ ++ __ bind(end_of_if); ++ } ++ // while (i+1 < j) ++ __ bind(entry); ++ __ daddiu(h, i, 1); ++ __ slt(AT, h, j); ++ __ bne(AT, R0, loop); ++ __ delayed()->nop(); ++ } ++ ++ // end of binary search, result index is i (must check again!) ++ Label default_case; ++ // Convert array[i].match to native byte-ordering before compare ++ __ dsll(AT, i, Address::times_8); ++ __ daddu(AT, array, AT); ++ __ lw(temp, AT, 0 * BytesPerInt); ++ __ swap(temp); ++ __ bne(key, temp, default_case); ++ __ delayed()->nop(); ++ ++ // entry found -> j = offset ++ __ dsll(AT, i, Address::times_8); ++ __ daddu(AT, array, AT); ++ __ lw(j, AT, 1 * BytesPerInt); ++ __ profile_switch_case(i, key, array); ++ __ swap(j); ++ ++ __ daddu(BCP, BCP, j); ++ __ lbu(Rnext, BCP, 0); ++ __ dispatch_only(vtos); ++ ++ // default case -> j = default offset ++ __ bind(default_case); ++ __ profile_switch_default(i); ++ __ lw(j, array, - 2 * BytesPerInt); ++ __ swap(j); ++ __ daddu(BCP, BCP, j); ++ __ lbu(Rnext, BCP, 0); ++ __ dispatch_only(vtos); ++} ++ ++void TemplateTable::_return(TosState state) { ++ transition(state, state); ++ assert(_desc->calls_vm(), ++ "inconsistent calls_vm information"); // call in remove_activation ++ ++ if (_desc->bytecode() == Bytecodes::_return_register_finalizer) { ++ assert(state == vtos, "only valid state"); ++ __ ld(T1, aaddress(0)); ++ __ load_klass(LVP, T1); ++ __ lw(LVP, LVP, in_bytes(Klass::access_flags_offset())); ++ __ move(AT, JVM_ACC_HAS_FINALIZER); ++ __ andr(AT, AT, LVP); ++ Label skip_register_finalizer; ++ __ beq(AT, R0, skip_register_finalizer); ++ __ delayed()->nop(); ++ __ call_VM(noreg, CAST_FROM_FN_PTR(address, ++ InterpreterRuntime::register_finalizer), T1); ++ __ bind(skip_register_finalizer); ++ } ++ ++ // Narrow result if state is itos but result type is smaller. ++ // Need to narrow in the return bytecode rather than in generate_return_entry ++ // since compiled code callers expect the result to already be narrowed. ++ if (state == itos) { ++ __ narrow(FSR); ++ } ++ ++ __ remove_activation(state, T9); ++ __ sync(); ++ ++ __ jr(T9); ++ __ delayed()->nop(); ++} ++ ++// ---------------------------------------------------------------------------- ++// Volatile variables demand their effects be made known to all CPU's ++// in order. Store buffers on most chips allow reads & writes to ++// reorder; the JMM's ReadAfterWrite.java test fails in -Xint mode ++// without some kind of memory barrier (i.e., it's not sufficient that ++// the interpreter does not reorder volatile references, the hardware ++// also must not reorder them). ++// ++// According to the new Java Memory Model (JMM): ++// (1) All volatiles are serialized wrt to each other. ALSO reads & ++// writes act as aquire & release, so: ++// (2) A read cannot let unrelated NON-volatile memory refs that ++// happen after the read float up to before the read. It's OK for ++// non-volatile memory refs that happen before the volatile read to ++// float down below it. ++// (3) Similar a volatile write cannot let unrelated NON-volatile ++// memory refs that happen BEFORE the write float down to after the ++// write. It's OK for non-volatile memory refs that happen after the ++// volatile write to float up before it. ++// ++// We only put in barriers around volatile refs (they are expensive), ++// not _between_ memory refs (that would require us to track the ++// flavor of the previous memory refs). Requirements (2) and (3) ++// require some barriers before volatile stores and after volatile ++// loads. These nearly cover requirement (1) but miss the ++// volatile-store-volatile-load case. This final case is placed after ++// volatile-stores although it could just as well go before ++// volatile-loads. ++void TemplateTable::volatile_barrier() { ++ if(os::is_MP()) __ sync(); ++} ++ ++// we dont shift left 2 bits in get_cache_and_index_at_bcp ++// for we always need shift the index we use it. the ConstantPoolCacheEntry ++// is 16-byte long, index is the index in ++// ConstantPoolCache, so cache + base_offset() + index * 16 is ++// the corresponding ConstantPoolCacheEntry ++// used registers : T2 ++// NOTE : the returned index need also shift left 4 to get the address! ++void TemplateTable::resolve_cache_and_index(int byte_no, ++ Register Rcache, ++ Register index, ++ size_t index_size) { ++ assert(byte_no == f1_byte || byte_no == f2_byte, "byte_no out of range"); ++ const Register temp = A1; ++ assert_different_registers(Rcache, index); ++ ++ Label resolved; ++ __ get_cache_and_index_and_bytecode_at_bcp(Rcache, index, temp, byte_no, 1, index_size); ++ // is resolved? ++ int i = (int)bytecode(); ++ __ addiu(temp, temp, -i); ++ __ beq(temp, R0, resolved); ++ __ delayed()->nop(); ++ // resolve first time through ++ address entry; ++ switch (bytecode()) { ++ case Bytecodes::_getstatic : // fall through ++ case Bytecodes::_putstatic : // fall through ++ case Bytecodes::_getfield : // fall through ++ case Bytecodes::_putfield : ++ entry = CAST_FROM_FN_PTR(address, InterpreterRuntime::resolve_get_put); ++ break; ++ case Bytecodes::_invokevirtual : // fall through ++ case Bytecodes::_invokespecial : // fall through ++ case Bytecodes::_invokestatic : // fall through ++ case Bytecodes::_invokeinterface: ++ entry = CAST_FROM_FN_PTR(address, InterpreterRuntime::resolve_invoke); ++ break; ++ case Bytecodes::_invokehandle: ++ entry = CAST_FROM_FN_PTR(address, InterpreterRuntime::resolve_invokehandle); ++ break; ++ case Bytecodes::_invokedynamic: ++ entry = CAST_FROM_FN_PTR(address, InterpreterRuntime::resolve_invokedynamic); ++ break; ++ default : ++ fatal(err_msg("unexpected bytecode: %s", Bytecodes::name(bytecode()))); ++ break; ++ } ++ ++ __ move(temp, i); ++ __ call_VM(NOREG, entry, temp); ++ ++ // Update registers with resolved info ++ __ get_cache_and_index_at_bcp(Rcache, index, 1, index_size); ++ __ bind(resolved); ++} ++ ++// The Rcache and index registers must be set before call ++void TemplateTable::load_field_cp_cache_entry(Register obj, ++ Register cache, ++ Register index, ++ Register off, ++ Register flags, ++ bool is_static = false) { ++ assert_different_registers(cache, index, flags, off); ++ ++ ByteSize cp_base_offset = ConstantPoolCache::base_offset(); ++ // Field offset ++ __ dsll(AT, index, Address::times_ptr); ++ __ daddu(AT, cache, AT); ++ __ ld(off, AT, in_bytes(cp_base_offset + ConstantPoolCacheEntry::f2_offset())); ++ // Flags ++ __ ld(flags, AT, in_bytes(cp_base_offset + ConstantPoolCacheEntry::flags_offset())); ++ ++ // klass overwrite register ++ if (is_static) { ++ __ ld(obj, AT, in_bytes(cp_base_offset + ConstantPoolCacheEntry::f1_offset())); ++ const int mirror_offset = in_bytes(Klass::java_mirror_offset()); ++ __ ld(obj, Address(obj, mirror_offset)); ++ ++ __ verify_oop(obj); ++ } ++} ++ ++// get the method, itable_index and flags of the current invoke ++void TemplateTable::load_invoke_cp_cache_entry(int byte_no, ++ Register method, ++ Register itable_index, ++ Register flags, ++ bool is_invokevirtual, ++ bool is_invokevfinal, /*unused*/ ++ bool is_invokedynamic) { ++ // setup registers ++ const Register cache = T3; ++ const Register index = T1; ++ assert_different_registers(method, flags); ++ assert_different_registers(method, cache, index); ++ assert_different_registers(itable_index, flags); ++ assert_different_registers(itable_index, cache, index); ++ assert(is_invokevirtual == (byte_no == f2_byte), "is invokevirtual flag redundant"); ++ // determine constant pool cache field offsets ++ const int method_offset = in_bytes( ++ ConstantPoolCache::base_offset() + ++ ((byte_no == f2_byte) ++ ? ConstantPoolCacheEntry::f2_offset() ++ : ConstantPoolCacheEntry::f1_offset())); ++ const int flags_offset = in_bytes(ConstantPoolCache::base_offset() + ++ ConstantPoolCacheEntry::flags_offset()); ++ // access constant pool cache fields ++ const int index_offset = in_bytes(ConstantPoolCache::base_offset() + ++ ConstantPoolCacheEntry::f2_offset()); ++ ++ size_t index_size = (is_invokedynamic ? sizeof(u4): sizeof(u2)); ++ resolve_cache_and_index(byte_no, cache, index, index_size); ++ ++ //assert(wordSize == 8, "adjust code below"); ++ // note we shift 4 not 2, for we get is the true inde ++ // of ConstantPoolCacheEntry, not the shifted 2-bit index as x86 version ++ __ dsll(AT, index, Address::times_ptr); ++ __ daddu(AT, cache, AT); ++ __ ld(method, AT, method_offset); ++ ++ if (itable_index != NOREG) { ++ __ ld(itable_index, AT, index_offset); ++ } ++ __ ld(flags, AT, flags_offset); ++} ++ ++// The registers cache and index expected to be set before call. ++// Correct values of the cache and index registers are preserved. ++void TemplateTable::jvmti_post_field_access(Register cache, Register index, ++ bool is_static, bool has_tos) { ++ // do the JVMTI work here to avoid disturbing the register state below ++ // We use c_rarg registers here because we want to use the register used in ++ // the call to the VM ++ if (JvmtiExport::can_post_field_access()) { ++ // Check to see if a field access watch has been set before we ++ // take the time to call into the VM. ++ Label L1; ++ // kill FSR ++ Register tmp1 = T2; ++ Register tmp2 = T1; ++ Register tmp3 = T3; ++ assert_different_registers(cache, index, AT); ++ __ li(AT, (intptr_t)JvmtiExport::get_field_access_count_addr()); ++ __ lw(AT, AT, 0); ++ __ beq(AT, R0, L1); ++ __ delayed()->nop(); ++ ++ __ get_cache_and_index_at_bcp(tmp2, tmp3, 1); ++ ++ // cache entry pointer ++ __ daddiu(tmp2, tmp2, in_bytes(ConstantPoolCache::base_offset())); ++ __ shl(tmp3, LogBytesPerWord); ++ __ daddu(tmp2, tmp2, tmp3); ++ if (is_static) { ++ __ move(tmp1, R0); ++ } else { ++ __ ld(tmp1, SP, 0); ++ __ verify_oop(tmp1); ++ } ++ // tmp1: object pointer or NULL ++ // tmp2: cache entry pointer ++ // tmp3: jvalue object on the stack ++ __ call_VM(NOREG, CAST_FROM_FN_PTR(address, ++ InterpreterRuntime::post_field_access), ++ tmp1, tmp2, tmp3); ++ __ get_cache_and_index_at_bcp(cache, index, 1); ++ __ bind(L1); ++ } ++} ++ ++void TemplateTable::pop_and_check_object(Register r) { ++ __ pop_ptr(r); ++ __ null_check(r); // for field access must check obj. ++ __ verify_oop(r); ++} ++ ++// used registers : T1, T2, T3, T1 ++// T1 : flags ++// T2 : off ++// T3 : obj ++// T1 : field address ++// The flags 31, 30, 29, 28 together build a 4 bit number 0 to 8 with the ++// following mapping to the TosState states: ++// btos: 0 ++// ctos: 1 ++// stos: 2 ++// itos: 3 ++// ltos: 4 ++// ftos: 5 ++// dtos: 6 ++// atos: 7 ++// vtos: 8 ++// see ConstantPoolCacheEntry::set_field for more info ++void TemplateTable::getfield_or_static(int byte_no, bool is_static) { ++ transition(vtos, vtos); ++ ++ const Register cache = T3; ++ const Register index = T0; ++ ++ const Register obj = T3; ++ const Register off = T2; ++ const Register flags = T1; ++ ++ const Register scratch = T8; ++ ++ resolve_cache_and_index(byte_no, cache, index, sizeof(u2)); ++ jvmti_post_field_access(cache, index, is_static, false); ++ load_field_cp_cache_entry(obj, cache, index, off, flags, is_static); ++ ++ { ++ __ move(scratch, 1 << ConstantPoolCacheEntry::is_volatile_shift); ++ __ andr(scratch, scratch, flags); ++ ++ Label notVolatile; ++ __ beq(scratch, R0, notVolatile); ++ __ delayed()->nop(); ++ volatile_barrier(); ++ __ bind(notVolatile); ++ } ++ ++ if (!is_static) pop_and_check_object(obj); ++ __ daddu(index, obj, off); ++ ++ ++ Label Done, notByte, notBool, notInt, notShort, notChar, ++ notLong, notFloat, notObj, notDouble; ++ ++ assert(btos == 0, "change code, btos != 0"); ++ __ dsrl(flags, flags, ConstantPoolCacheEntry::tos_state_shift); ++ __ andi(flags, flags, ConstantPoolCacheEntry::tos_state_mask); ++ __ bne(flags, R0, notByte); ++ __ delayed()->nop(); ++ ++ // btos ++ __ lb(FSR, index, 0); ++ __ push(btos); ++ ++ // Rewrite bytecode to be faster ++ if (!is_static) { ++ patch_bytecode(Bytecodes::_fast_bgetfield, T3, T2); ++ } ++ __ b(Done); ++ __ delayed()->nop(); ++ ++ ++ __ bind(notByte); ++ __ move(AT, ztos); ++ __ bne(flags, AT, notBool); ++ __ delayed()->nop(); ++ ++ // ztos ++ __ lb(FSR, index, 0); ++ __ push(ztos); ++ ++ // Rewrite bytecode to be faster ++ if (!is_static) { ++ patch_bytecode(Bytecodes::_fast_bgetfield, T3, T2); ++ } ++ __ b(Done); ++ __ delayed()->nop(); ++ ++ ++ __ bind(notBool); ++ __ move(AT, itos); ++ __ bne(flags, AT, notInt); ++ __ delayed()->nop(); ++ ++ // itos ++ __ lw(FSR, index, 0); ++ __ push(itos); ++ ++ // Rewrite bytecode to be faster ++ if (!is_static) { ++ patch_bytecode(Bytecodes::_fast_igetfield, T3, T2); ++ } ++ __ b(Done); ++ __ delayed()->nop(); ++ ++ __ bind(notInt); ++ __ move(AT, atos); ++ __ bne(flags, AT, notObj); ++ __ delayed()->nop(); ++ ++ // atos ++ //add for compressedoops ++ __ load_heap_oop(FSR, Address(index, 0)); ++ __ push(atos); ++ ++ if (!is_static) { ++ patch_bytecode(Bytecodes::_fast_agetfield, T3, T2); ++ } ++ __ b(Done); ++ __ delayed()->nop(); ++ ++ __ bind(notObj); ++ __ move(AT, ctos); ++ __ bne(flags, AT, notChar); ++ __ delayed()->nop(); ++ ++ // ctos ++ __ lhu(FSR, index, 0); ++ __ push(ctos); ++ ++ if (!is_static) { ++ patch_bytecode(Bytecodes::_fast_cgetfield, T3, T2); ++ } ++ __ b(Done); ++ __ delayed()->nop(); ++ ++ __ bind(notChar); ++ __ move(AT, stos); ++ __ bne(flags, AT, notShort); ++ __ delayed()->nop(); ++ ++ // stos ++ __ lh(FSR, index, 0); ++ __ push(stos); ++ ++ if (!is_static) { ++ patch_bytecode(Bytecodes::_fast_sgetfield, T3, T2); ++ } ++ __ b(Done); ++ __ delayed()->nop(); ++ ++ __ bind(notShort); ++ __ move(AT, ltos); ++ __ bne(flags, AT, notLong); ++ __ delayed()->nop(); ++ ++ // FIXME : the load/store should be atomic, we have no simple method to do this in mips32 ++ // ltos ++ __ ld(FSR, index, 0 * wordSize); ++ __ push(ltos); ++ ++ // Don't rewrite to _fast_lgetfield for potential volatile case. ++ __ b(Done); ++ __ delayed()->nop(); ++ ++ __ bind(notLong); ++ __ move(AT, ftos); ++ __ bne(flags, AT, notFloat); ++ __ delayed()->nop(); ++ ++ // ftos ++ __ lwc1(FSF, index, 0); ++ __ push(ftos); ++ ++ if (!is_static) { ++ patch_bytecode(Bytecodes::_fast_fgetfield, T3, T2); ++ } ++ __ b(Done); ++ __ delayed()->nop(); ++ ++ __ bind(notFloat); ++ __ move(AT, dtos); ++#ifdef ASSERT ++ __ bne(flags, AT, notDouble); ++ __ delayed()->nop(); ++#endif ++ ++ // dtos ++ __ ldc1(FSF, index, 0 * wordSize); ++ __ push(dtos); ++ ++ if (!is_static) { ++ patch_bytecode(Bytecodes::_fast_dgetfield, T3, T2); ++ } ++ ++ ++#ifdef ASSERT ++ __ b(Done); ++ __ delayed()->nop(); ++ __ bind(notDouble); ++ __ stop("Bad state"); ++#endif ++ ++ __ bind(Done); ++ ++ { ++ Label notVolatile; ++ __ beq(scratch, R0, notVolatile); ++ __ delayed()->nop(); ++ volatile_barrier(); ++ __ bind(notVolatile); ++ } ++} ++ ++ ++void TemplateTable::getfield(int byte_no) { ++ getfield_or_static(byte_no, false); ++} ++ ++void TemplateTable::getstatic(int byte_no) { ++ getfield_or_static(byte_no, true); ++} ++ ++// The registers cache and index expected to be set before call. ++// The function may destroy various registers, just not the cache and index registers. ++void TemplateTable::jvmti_post_field_mod(Register cache, Register index, bool is_static) { ++ transition(vtos, vtos); ++ ++ ByteSize cp_base_offset = ConstantPoolCache::base_offset(); ++ ++ if (JvmtiExport::can_post_field_modification()) { ++ // Check to see if a field modification watch has been set before ++ // we take the time to call into the VM. ++ Label L1; ++ //kill AT, T1, T2, T3, T9 ++ Register tmp1 = T2; ++ Register tmp2 = T1; ++ Register tmp3 = T3; ++ Register tmp4 = T9; ++ assert_different_registers(cache, index, tmp4); ++ ++ __ li(AT, JvmtiExport::get_field_modification_count_addr()); ++ __ lw(AT, AT, 0); ++ __ beq(AT, R0, L1); ++ __ delayed()->nop(); ++ ++ __ get_cache_and_index_at_bcp(tmp2, tmp4, 1); ++ ++ if (is_static) { ++ __ move(tmp1, R0); ++ } else { ++ // Life is harder. The stack holds the value on top, followed by ++ // the object. We don't know the size of the value, though; it ++ // could be one or two words depending on its type. As a result, ++ // we must find the type to determine where the object is. ++ Label two_word, valsize_known; ++ __ dsll(AT, tmp4, Address::times_8); ++ __ daddu(AT, tmp2, AT); ++ __ ld(tmp3, AT, in_bytes(cp_base_offset + ++ ConstantPoolCacheEntry::flags_offset())); ++ __ shr(tmp3, ConstantPoolCacheEntry::tos_state_shift); ++ ++ ConstantPoolCacheEntry::verify_tos_state_shift(); ++ __ move(tmp1, SP); ++ __ move(AT, ltos); ++ __ beq(tmp3, AT, two_word); ++ __ delayed()->nop(); ++ __ move(AT, dtos); ++ __ beq(tmp3, AT, two_word); ++ __ delayed()->nop(); ++ __ b(valsize_known); ++ __ delayed()->daddiu(tmp1, tmp1, Interpreter::expr_offset_in_bytes(1) ); ++ ++ __ bind(two_word); ++ __ daddiu(tmp1, tmp1, Interpreter::expr_offset_in_bytes(2)); ++ ++ __ bind(valsize_known); ++ // setup object pointer ++ __ ld(tmp1, tmp1, 0*wordSize); ++ } ++ // cache entry pointer ++ __ daddiu(tmp2, tmp2, in_bytes(cp_base_offset)); ++ __ shl(tmp4, LogBytesPerWord); ++ __ daddu(tmp2, tmp2, tmp4); ++ // object (tos) ++ __ move(tmp3, SP); ++ // tmp1: object pointer set up above (NULL if static) ++ // tmp2: cache entry pointer ++ // tmp3: jvalue object on the stack ++ __ call_VM(NOREG, ++ CAST_FROM_FN_PTR(address, ++ InterpreterRuntime::post_field_modification), ++ tmp1, tmp2, tmp3); ++ __ get_cache_and_index_at_bcp(cache, index, 1); ++ __ bind(L1); ++ } ++} ++ ++// used registers : T0, T1, T2, T3, T8 ++// T1 : flags ++// T2 : off ++// T3 : obj ++// T8 : volatile bit ++// see ConstantPoolCacheEntry::set_field for more info ++void TemplateTable::putfield_or_static(int byte_no, bool is_static) { ++ transition(vtos, vtos); ++ ++ const Register cache = T3; ++ const Register index = T0; ++ const Register obj = T3; ++ const Register off = T2; ++ const Register flags = T1; ++ const Register bc = T3; ++ ++ const Register scratch = T8; ++ ++ resolve_cache_and_index(byte_no, cache, index, sizeof(u2)); ++ jvmti_post_field_mod(cache, index, is_static); ++ load_field_cp_cache_entry(obj, cache, index, off, flags, is_static); ++ ++ Label Done; ++ { ++ __ move(scratch, 1 << ConstantPoolCacheEntry::is_volatile_shift); ++ __ andr(scratch, scratch, flags); ++ ++ Label notVolatile; ++ __ beq(scratch, R0, notVolatile); ++ __ delayed()->nop(); ++ volatile_barrier(); ++ __ bind(notVolatile); ++ } ++ ++ ++ Label notByte, notBool, notInt, notShort, notChar, notLong, notFloat, notObj, notDouble; ++ ++ assert(btos == 0, "change code, btos != 0"); ++ ++ // btos ++ __ dsrl(flags, flags, ConstantPoolCacheEntry::tos_state_shift); ++ __ andi(flags, flags, ConstantPoolCacheEntry::tos_state_mask); ++ __ bne(flags, R0, notByte); ++ __ delayed()->nop(); ++ ++ __ pop(btos); ++ if (!is_static) { ++ pop_and_check_object(obj); ++ } ++ __ daddu(AT, obj, off); ++ __ sb(FSR, AT, 0); ++ ++ if (!is_static) { ++ patch_bytecode(Bytecodes::_fast_bputfield, bc, off, true, byte_no); ++ } ++ __ b(Done); ++ __ delayed()->nop(); ++ ++ // ztos ++ __ bind(notByte); ++ __ move(AT, ztos); ++ __ bne(flags, AT, notBool); ++ __ delayed()->nop(); ++ ++ __ pop(ztos); ++ if (!is_static) { ++ pop_and_check_object(obj); ++ } ++ __ daddu(AT, obj, off); ++ __ andi(FSR, FSR, 0x1); ++ __ sb(FSR, AT, 0); ++ ++ if (!is_static) { ++ patch_bytecode(Bytecodes::_fast_zputfield, bc, off, true, byte_no); ++ } ++ __ b(Done); ++ __ delayed()->nop(); ++ ++ // itos ++ __ bind(notBool); ++ __ move(AT, itos); ++ __ bne(flags, AT, notInt); ++ __ delayed()->nop(); ++ ++ __ pop(itos); ++ if (!is_static) { ++ pop_and_check_object(obj); ++ } ++ __ daddu(AT, obj, off); ++ __ sw(FSR, AT, 0); ++ ++ if (!is_static) { ++ patch_bytecode(Bytecodes::_fast_iputfield, bc, off, true, byte_no); ++ } ++ __ b(Done); ++ __ delayed()->nop(); ++ ++ // atos ++ __ bind(notInt); ++ __ move(AT, atos); ++ __ bne(flags, AT, notObj); ++ __ delayed()->nop(); ++ ++ __ pop(atos); ++ if (!is_static) { ++ pop_and_check_object(obj); ++ } ++ ++ do_oop_store(_masm, Address(obj, off, Address::times_1, 0), FSR, _bs->kind(), false); ++ ++ if (!is_static) { ++ patch_bytecode(Bytecodes::_fast_aputfield, bc, off, true, byte_no); ++ } ++ __ b(Done); ++ __ delayed()->nop(); ++ ++ // ctos ++ __ bind(notObj); ++ __ move(AT, ctos); ++ __ bne(flags, AT, notChar); ++ __ delayed()->nop(); ++ ++ __ pop(ctos); ++ if (!is_static) { ++ pop_and_check_object(obj); ++ } ++ __ daddu(AT, obj, off); ++ __ sh(FSR, AT, 0); ++ if (!is_static) { ++ patch_bytecode(Bytecodes::_fast_cputfield, bc, off, true, byte_no); ++ } ++ __ b(Done); ++ __ delayed()->nop(); ++ ++ // stos ++ __ bind(notChar); ++ __ move(AT, stos); ++ __ bne(flags, AT, notShort); ++ __ delayed()->nop(); ++ ++ __ pop(stos); ++ if (!is_static) { ++ pop_and_check_object(obj); ++ } ++ __ daddu(AT, obj, off); ++ __ sh(FSR, AT, 0); ++ if (!is_static) { ++ patch_bytecode(Bytecodes::_fast_sputfield, bc, off, true, byte_no); ++ } ++ __ b(Done); ++ __ delayed()->nop(); ++ ++ // ltos ++ __ bind(notShort); ++ __ move(AT, ltos); ++ __ bne(flags, AT, notLong); ++ __ delayed()->nop(); ++ ++ __ pop(ltos); ++ if (!is_static) { ++ pop_and_check_object(obj); ++ } ++ __ daddu(AT, obj, off); ++ __ sd(FSR, AT, 0); ++ if (!is_static) { ++ patch_bytecode(Bytecodes::_fast_lputfield, bc, off, true, byte_no); ++ } ++ __ b(Done); ++ __ delayed()->nop(); ++ ++ // ftos ++ __ bind(notLong); ++ __ move(AT, ftos); ++ __ bne(flags, AT, notFloat); ++ __ delayed()->nop(); ++ ++ __ pop(ftos); ++ if (!is_static) { ++ pop_and_check_object(obj); ++ } ++ __ daddu(AT, obj, off); ++ __ swc1(FSF, AT, 0); ++ if (!is_static) { ++ patch_bytecode(Bytecodes::_fast_fputfield, bc, off, true, byte_no); ++ } ++ __ b(Done); ++ __ delayed()->nop(); ++ ++ ++ // dtos ++ __ bind(notFloat); ++ __ move(AT, dtos); ++#ifdef ASSERT ++ __ bne(flags, AT, notDouble); ++ __ delayed()->nop(); ++#endif ++ ++ __ pop(dtos); ++ if (!is_static) { ++ pop_and_check_object(obj); ++ } ++ __ daddu(AT, obj, off); ++ __ sdc1(FSF, AT, 0); ++ if (!is_static) { ++ patch_bytecode(Bytecodes::_fast_dputfield, bc, off, true, byte_no); ++ } ++ ++#ifdef ASSERT ++ __ b(Done); ++ __ delayed()->nop(); ++ ++ __ bind(notDouble); ++ __ stop("Bad state"); ++#endif ++ ++ __ bind(Done); ++ ++ { ++ Label notVolatile; ++ __ beq(scratch, R0, notVolatile); ++ __ delayed()->nop(); ++ volatile_barrier(); ++ __ bind(notVolatile); ++ } ++} ++ ++void TemplateTable::putfield(int byte_no) { ++ putfield_or_static(byte_no, false); ++} ++ ++void TemplateTable::putstatic(int byte_no) { ++ putfield_or_static(byte_no, true); ++} ++ ++// used registers : T1, T2, T3 ++// T1 : cp_entry ++// T2 : obj ++// T3 : value pointer ++void TemplateTable::jvmti_post_fast_field_mod() { ++ if (JvmtiExport::can_post_field_modification()) { ++ // Check to see if a field modification watch has been set before ++ // we take the time to call into the VM. ++ Label L2; ++ //kill AT, T1, T2, T3, T9 ++ Register tmp1 = T2; ++ Register tmp2 = T1; ++ Register tmp3 = T3; ++ Register tmp4 = T9; ++ __ li(AT, JvmtiExport::get_field_modification_count_addr()); ++ __ lw(tmp3, AT, 0); ++ __ beq(tmp3, R0, L2); ++ __ delayed()->nop(); ++ __ pop_ptr(tmp1); ++ __ verify_oop(tmp1); ++ __ push_ptr(tmp1); ++ switch (bytecode()) { // load values into the jvalue object ++ case Bytecodes::_fast_aputfield: __ push_ptr(FSR); break; ++ case Bytecodes::_fast_bputfield: // fall through ++ case Bytecodes::_fast_zputfield: // fall through ++ case Bytecodes::_fast_sputfield: // fall through ++ case Bytecodes::_fast_cputfield: // fall through ++ case Bytecodes::_fast_iputfield: __ push_i(FSR); break; ++ case Bytecodes::_fast_dputfield: __ push_d(FSF); break; ++ case Bytecodes::_fast_fputfield: __ push_f(); break; ++ case Bytecodes::_fast_lputfield: __ push_l(FSR); break; ++ default: ShouldNotReachHere(); ++ } ++ __ move(tmp3, SP); ++ // access constant pool cache entry ++ __ get_cache_entry_pointer_at_bcp(tmp2, FSR, 1); ++ __ verify_oop(tmp1); ++ // tmp1: object pointer copied above ++ // tmp2: cache entry pointer ++ // tmp3: jvalue object on the stack ++ __ call_VM(NOREG, ++ CAST_FROM_FN_PTR(address, ++ InterpreterRuntime::post_field_modification), ++ tmp1, tmp2, tmp3); ++ ++ switch (bytecode()) { // restore tos values ++ case Bytecodes::_fast_aputfield: __ pop_ptr(FSR); break; ++ case Bytecodes::_fast_bputfield: // fall through ++ case Bytecodes::_fast_zputfield: // fall through ++ case Bytecodes::_fast_sputfield: // fall through ++ case Bytecodes::_fast_cputfield: // fall through ++ case Bytecodes::_fast_iputfield: __ pop_i(FSR); break; ++ case Bytecodes::_fast_dputfield: __ pop_d(); break; ++ case Bytecodes::_fast_fputfield: __ pop_f(); break; ++ case Bytecodes::_fast_lputfield: __ pop_l(FSR); break; ++ } ++ __ bind(L2); ++ } ++} ++ ++// used registers : T2, T3, T1 ++// T2 : index & off & field address ++// T3 : cache & obj ++// T1 : flags ++void TemplateTable::fast_storefield(TosState state) { ++ transition(state, vtos); ++ ++ const Register scratch = T8; ++ ++ ByteSize base = ConstantPoolCache::base_offset(); ++ ++ jvmti_post_fast_field_mod(); ++ ++ // access constant pool cache ++ __ get_cache_and_index_at_bcp(T3, T2, 1); ++ ++ // Must prevent reordering of the following cp cache loads with bytecode load ++ __ sync(); ++ ++ // test for volatile with T1 ++ __ dsll(AT, T2, Address::times_8); ++ __ daddu(AT, T3, AT); ++ __ ld(T1, AT, in_bytes(base + ConstantPoolCacheEntry::flags_offset())); ++ ++ // replace index with field offset from cache entry ++ __ ld(T2, AT, in_bytes(base + ConstantPoolCacheEntry::f2_offset())); ++ ++ Label Done; ++ { ++ __ move(scratch, 1 << ConstantPoolCacheEntry::is_volatile_shift); ++ __ andr(scratch, scratch, T1); ++ ++ Label notVolatile; ++ __ beq(scratch, R0, notVolatile); ++ __ delayed()->nop(); ++ volatile_barrier(); ++ __ bind(notVolatile); ++ } ++ ++ // Get object from stack ++ pop_and_check_object(T3); ++ ++ if (bytecode() != Bytecodes::_fast_aputfield) { ++ // field address ++ __ daddu(T2, T3, T2); ++ } ++ ++ // access field ++ switch (bytecode()) { ++ case Bytecodes::_fast_zputfield: ++ __ andi(FSR, FSR, 0x1); // boolean is true if LSB is 1 ++ // fall through to bputfield ++ case Bytecodes::_fast_bputfield: ++ __ sb(FSR, T2, 0); ++ break; ++ case Bytecodes::_fast_sputfield: // fall through ++ case Bytecodes::_fast_cputfield: ++ __ sh(FSR, T2, 0); ++ break; ++ case Bytecodes::_fast_iputfield: ++ __ sw(FSR, T2, 0); ++ break; ++ case Bytecodes::_fast_lputfield: ++ __ sd(FSR, T2, 0 * wordSize); ++ break; ++ case Bytecodes::_fast_fputfield: ++ __ swc1(FSF, T2, 0); ++ break; ++ case Bytecodes::_fast_dputfield: ++ __ sdc1(FSF, T2, 0 * wordSize); ++ break; ++ case Bytecodes::_fast_aputfield: ++ do_oop_store(_masm, Address(T3, T2, Address::times_1, 0), FSR, _bs->kind(), false); ++ break; ++ default: ++ ShouldNotReachHere(); ++ } ++ ++ { ++ Label notVolatile; ++ __ beq(scratch, R0, notVolatile); ++ __ delayed()->nop(); ++ volatile_barrier(); ++ __ bind(notVolatile); ++ } ++} ++ ++// used registers : T2, T3, T1 ++// T3 : cp_entry & cache ++// T2 : index & offset ++void TemplateTable::fast_accessfield(TosState state) { ++ transition(atos, state); ++ ++ const Register scratch = T8; ++ ++ // do the JVMTI work here to avoid disturbing the register state below ++ if (JvmtiExport::can_post_field_access()) { ++ // Check to see if a field access watch has been set before we take ++ // the time to call into the VM. ++ Label L1; ++ __ li(AT, (intptr_t)JvmtiExport::get_field_access_count_addr()); ++ __ lw(T3, AT, 0); ++ __ beq(T3, R0, L1); ++ __ delayed()->nop(); ++ // access constant pool cache entry ++ __ get_cache_entry_pointer_at_bcp(T3, T1, 1); ++ __ move(TSR, FSR); ++ __ verify_oop(FSR); ++ // FSR: object pointer copied above ++ // T3: cache entry pointer ++ __ call_VM(NOREG, ++ CAST_FROM_FN_PTR(address, InterpreterRuntime::post_field_access), ++ FSR, T3); ++ __ move(FSR, TSR); ++ __ bind(L1); ++ } ++ ++ // access constant pool cache ++ __ get_cache_and_index_at_bcp(T3, T2, 1); ++ ++ // Must prevent reordering of the following cp cache loads with bytecode load ++ __ sync(); ++ ++ // replace index with field offset from cache entry ++ __ dsll(AT, T2, Address::times_8); ++ __ daddu(AT, T3, AT); ++ __ ld(T2, AT, in_bytes(ConstantPoolCache::base_offset() + ConstantPoolCacheEntry::f2_offset())); ++ ++ { ++ __ ld(AT, AT, in_bytes(ConstantPoolCache::base_offset() + ConstantPoolCacheEntry::flags_offset())); ++ __ move(scratch, 1 << ConstantPoolCacheEntry::is_volatile_shift); ++ __ andr(scratch, scratch, AT); ++ ++ Label notVolatile; ++ __ beq(scratch, R0, notVolatile); ++ __ delayed()->nop(); ++ volatile_barrier(); ++ __ bind(notVolatile); ++ } ++ ++ // FSR: object ++ __ verify_oop(FSR); ++ __ null_check(FSR); ++ // field addresses ++ __ daddu(FSR, FSR, T2); ++ ++ // access field ++ switch (bytecode()) { ++ case Bytecodes::_fast_bgetfield: ++ __ lb(FSR, FSR, 0); ++ break; ++ case Bytecodes::_fast_sgetfield: ++ __ lh(FSR, FSR, 0); ++ break; ++ case Bytecodes::_fast_cgetfield: ++ __ lhu(FSR, FSR, 0); ++ break; ++ case Bytecodes::_fast_igetfield: ++ __ lw(FSR, FSR, 0); ++ break; ++ case Bytecodes::_fast_lgetfield: ++ __ stop("should not be rewritten"); ++ break; ++ case Bytecodes::_fast_fgetfield: ++ __ lwc1(FSF, FSR, 0); ++ break; ++ case Bytecodes::_fast_dgetfield: ++ __ ldc1(FSF, FSR, 0); ++ break; ++ case Bytecodes::_fast_agetfield: ++ //add for compressedoops ++ __ load_heap_oop(FSR, Address(FSR, 0)); ++ __ verify_oop(FSR); ++ break; ++ default: ++ ShouldNotReachHere(); ++ } ++ ++ { ++ Label notVolatile; ++ __ beq(scratch, R0, notVolatile); ++ __ delayed()->nop(); ++ volatile_barrier(); ++ __ bind(notVolatile); ++ } ++} ++ ++// generator for _fast_iaccess_0, _fast_aaccess_0, _fast_faccess_0 ++// used registers : T1, T2, T3, T1 ++// T1 : obj & field address ++// T2 : off ++// T3 : cache ++// T1 : index ++void TemplateTable::fast_xaccess(TosState state) { ++ transition(vtos, state); ++ ++ const Register scratch = T8; ++ ++ // get receiver ++ __ ld(T1, aaddress(0)); ++ // access constant pool cache ++ __ get_cache_and_index_at_bcp(T3, T2, 2); ++ __ dsll(AT, T2, Address::times_8); ++ __ daddu(AT, T3, AT); ++ __ ld(T2, AT, in_bytes(ConstantPoolCache::base_offset() + ConstantPoolCacheEntry::f2_offset())); ++ ++ { ++ __ ld(AT, AT, in_bytes(ConstantPoolCache::base_offset() + ConstantPoolCacheEntry::flags_offset())); ++ __ move(scratch, 1 << ConstantPoolCacheEntry::is_volatile_shift); ++ __ andr(scratch, scratch, AT); ++ ++ Label notVolatile; ++ __ beq(scratch, R0, notVolatile); ++ __ delayed()->nop(); ++ volatile_barrier(); ++ __ bind(notVolatile); ++ } ++ ++ // make sure exception is reported in correct bcp range (getfield is ++ // next instruction) ++ __ daddiu(BCP, BCP, 1); ++ __ null_check(T1); ++ __ daddu(T1, T1, T2); ++ ++ if (state == itos) { ++ __ lw(FSR, T1, 0); ++ } else if (state == atos) { ++ __ load_heap_oop(FSR, Address(T1, 0)); ++ __ verify_oop(FSR); ++ } else if (state == ftos) { ++ __ lwc1(FSF, T1, 0); ++ } else { ++ ShouldNotReachHere(); ++ } ++ __ daddiu(BCP, BCP, -1); ++ ++ { ++ Label notVolatile; ++ __ beq(scratch, R0, notVolatile); ++ __ delayed()->nop(); ++ volatile_barrier(); ++ __ bind(notVolatile); ++ } ++} ++ ++ ++ ++//----------------------------------------------------------------------------- ++// Calls ++ ++void TemplateTable::count_calls(Register method, Register temp) { ++ // implemented elsewhere ++ ShouldNotReachHere(); ++} ++ ++// method, index, recv, flags: T1, T2, T3, T1 ++// byte_no = 2 for _invokevirtual, 1 else ++// T0 : return address ++// get the method & index of the invoke, and push the return address of ++// the invoke(first word in the frame) ++// this address is where the return code jmp to. ++// NOTE : this method will set T3&T1 as recv&flags ++void TemplateTable::prepare_invoke(int byte_no, ++ Register method, // linked method (or i-klass) ++ Register index, // itable index, MethodType, etc. ++ Register recv, // if caller wants to see it ++ Register flags // if caller wants to test it ++ ) { ++ // determine flags ++ const Bytecodes::Code code = bytecode(); ++ const bool is_invokeinterface = code == Bytecodes::_invokeinterface; ++ const bool is_invokedynamic = code == Bytecodes::_invokedynamic; ++ const bool is_invokehandle = code == Bytecodes::_invokehandle; ++ const bool is_invokevirtual = code == Bytecodes::_invokevirtual; ++ const bool is_invokespecial = code == Bytecodes::_invokespecial; ++ const bool load_receiver = (recv != noreg); ++ const bool save_flags = (flags != noreg); ++ assert(load_receiver == (code != Bytecodes::_invokestatic && code != Bytecodes::_invokedynamic),""); ++ assert(save_flags == (is_invokeinterface || is_invokevirtual), "need flags for vfinal"); ++ assert(flags == noreg || flags == T1, "error flags reg."); ++ assert(recv == noreg || recv == T3, "error recv reg."); ++ ++ // setup registers & access constant pool cache ++ if(recv == noreg) recv = T3; ++ if(flags == noreg) flags = T1; ++ assert_different_registers(method, index, recv, flags); ++ ++ // save 'interpreter return address' ++ __ save_bcp(); ++ ++ load_invoke_cp_cache_entry(byte_no, method, index, flags, is_invokevirtual, false, is_invokedynamic); ++ ++ if (is_invokedynamic || is_invokehandle) { ++ Label L_no_push; ++ __ move(AT, (1 << ConstantPoolCacheEntry::has_appendix_shift)); ++ __ andr(AT, AT, flags); ++ __ beq(AT, R0, L_no_push); ++ __ delayed()->nop(); ++ // Push the appendix as a trailing parameter. ++ // This must be done before we get the receiver, ++ // since the parameter_size includes it. ++ Register tmp = SSR; ++ __ push(tmp); ++ __ move(tmp, index); ++ assert(ConstantPoolCacheEntry::_indy_resolved_references_appendix_offset == 0, "appendix expected at index+0"); ++ __ load_resolved_reference_at_index(index, tmp); ++ __ pop(tmp); ++ __ push(index); // push appendix (MethodType, CallSite, etc.) ++ __ bind(L_no_push); ++ } ++ ++ // load receiver if needed (after appendix is pushed so parameter size is correct) ++ // Note: no return address pushed yet ++ if (load_receiver) { ++ __ move(AT, ConstantPoolCacheEntry::parameter_size_mask); ++ __ andr(recv, flags, AT); ++ // Since we won't push RA on stack, no_return_pc_pushed_yet should be 0. ++ const int no_return_pc_pushed_yet = 0; // argument slot correction before we push return address ++ const int receiver_is_at_end = -1; // back off one slot to get receiver ++ Address recv_addr = __ argument_address(recv, no_return_pc_pushed_yet + receiver_is_at_end); ++ __ ld(recv, recv_addr); ++ __ verify_oop(recv); ++ } ++ if(save_flags) { ++ __ move(BCP, flags); ++ } ++ ++ // compute return type ++ __ dsrl(flags, flags, ConstantPoolCacheEntry::tos_state_shift); ++ __ andi(flags, flags, 0xf); ++ ++ // Make sure we don't need to mask flags for tos_state_shift after the above shift ++ ConstantPoolCacheEntry::verify_tos_state_shift(); ++ // load return address ++ { ++ const address table = (address) Interpreter::invoke_return_entry_table_for(code); ++ __ li(AT, (long)table); ++ __ dsll(flags, flags, LogBytesPerWord); ++ __ daddu(AT, AT, flags); ++ __ ld(RA, AT, 0); ++ } ++ ++ if (save_flags) { ++ __ move(flags, BCP); ++ __ restore_bcp(); ++ } ++} ++ ++// used registers : T0, T3, T1, T2 ++// T3 : recv, this two register using convention is by prepare_invoke ++// T1 : flags, klass ++// Rmethod : method, index must be Rmethod ++void TemplateTable::invokevirtual_helper(Register index, ++ Register recv, ++ Register flags) { ++ ++ assert_different_registers(index, recv, flags, T2); ++ ++ // Test for an invoke of a final method ++ Label notFinal; ++ __ move(AT, (1 << ConstantPoolCacheEntry::is_vfinal_shift)); ++ __ andr(AT, flags, AT); ++ __ beq(AT, R0, notFinal); ++ __ delayed()->nop(); ++ ++ Register method = index; // method must be Rmethod ++ assert(method == Rmethod, "methodOop must be Rmethod for interpreter calling convention"); ++ ++ // do the call - the index is actually the method to call ++ // the index is indeed methodOop, for this is vfinal, ++ // see ConstantPoolCacheEntry::set_method for more info ++ ++ __ verify_oop(method); ++ ++ // It's final, need a null check here! ++ __ null_check(recv); ++ ++ // profile this call ++ __ profile_final_call(T2); ++ ++ // T2: tmp, used for mdp ++ // method: callee ++ // T9: tmp ++ // is_virtual: true ++ __ profile_arguments_type(T2, method, T9, true); ++ ++ __ jump_from_interpreted(method, T2); ++ ++ __ bind(notFinal); ++ ++ // get receiver klass ++ __ null_check(recv, oopDesc::klass_offset_in_bytes()); ++ __ load_klass(T2, recv); ++ __ verify_oop(T2); ++ ++ // profile this call ++ __ profile_virtual_call(T2, T0, T1); ++ ++ // get target methodOop & entry point ++ const int base = InstanceKlass::vtable_start_offset() * wordSize; ++ assert(vtableEntry::size() * wordSize == wordSize, "adjust the scaling in the code below"); ++ __ dsll(AT, index, Address::times_ptr); ++ // T2: receiver ++ __ daddu(AT, T2, AT); ++ //this is a ualign read ++ __ ld(method, AT, base + vtableEntry::method_offset_in_bytes()); ++ __ profile_arguments_type(T2, method, T9, true); ++ __ jump_from_interpreted(method, T2); ++ ++} ++ ++void TemplateTable::invokevirtual(int byte_no) { ++ transition(vtos, vtos); ++ assert(byte_no == f2_byte, "use this argument"); ++ prepare_invoke(byte_no, Rmethod, NOREG, T3, T1); ++ // now recv & flags in T3, T1 ++ invokevirtual_helper(Rmethod, T3, T1); ++} ++ ++// T9 : entry ++// Rmethod : method ++void TemplateTable::invokespecial(int byte_no) { ++ transition(vtos, vtos); ++ assert(byte_no == f1_byte, "use this argument"); ++ prepare_invoke(byte_no, Rmethod, NOREG, T3); ++ // now recv & flags in T3, T1 ++ __ verify_oop(T3); ++ __ null_check(T3); ++ __ profile_call(T9); ++ ++ // T8: tmp, used for mdp ++ // Rmethod: callee ++ // T9: tmp ++ // is_virtual: false ++ __ profile_arguments_type(T8, Rmethod, T9, false); ++ ++ __ jump_from_interpreted(Rmethod, T9); ++ __ move(T0, T3); ++} ++ ++void TemplateTable::invokestatic(int byte_no) { ++ transition(vtos, vtos); ++ assert(byte_no == f1_byte, "use this argument"); ++ prepare_invoke(byte_no, Rmethod, NOREG); ++ __ verify_oop(Rmethod); ++ ++ __ profile_call(T9); ++ ++ // T8: tmp, used for mdp ++ // Rmethod: callee ++ // T9: tmp ++ // is_virtual: false ++ __ profile_arguments_type(T8, Rmethod, T9, false); ++ ++ __ jump_from_interpreted(Rmethod, T9); ++} ++ ++// i have no idea what to do here, now. for future change. FIXME. ++void TemplateTable::fast_invokevfinal(int byte_no) { ++ transition(vtos, vtos); ++ assert(byte_no == f2_byte, "use this argument"); ++ __ stop("fast_invokevfinal not used on mips64"); ++} ++ ++// used registers : T0, T1, T2, T3, T1, A7 ++// T0 : itable, vtable, entry ++// T1 : interface ++// T3 : receiver ++// T1 : flags, klass ++// Rmethod : index, method, this is required by interpreter_entry ++void TemplateTable::invokeinterface(int byte_no) { ++ transition(vtos, vtos); ++ //this method will use T1-T4 and T0 ++ assert(byte_no == f1_byte, "use this argument"); ++ prepare_invoke(byte_no, T2, Rmethod, T3, T1); ++ // T2: reference klass ++ // Rmethod: method ++ // T3: receiver ++ // T1: flags ++ ++ // Special case of invokeinterface called for virtual method of ++ // java.lang.Object. See cpCacheOop.cpp for details. ++ // This code isn't produced by javac, but could be produced by ++ // another compliant java compiler. ++ Label notMethod; ++ __ move(AT, (1 << ConstantPoolCacheEntry::is_forced_virtual_shift)); ++ __ andr(AT, T1, AT); ++ __ beq(AT, R0, notMethod); ++ __ delayed()->nop(); ++ ++ invokevirtual_helper(Rmethod, T3, T1); ++ __ bind(notMethod); ++ // Get receiver klass into T1 - also a null check ++ //add for compressedoops ++ __ load_klass(T1, T3); ++ __ verify_oop(T1); ++ ++ Label no_such_interface, no_such_method; ++ ++ // Receiver subtype check against REFC. ++ // Superklass in T2. Subklass in T1. ++ __ lookup_interface_method(// inputs: rec. class, interface, itable index ++ T1, T2, noreg, ++ // outputs: scan temp. reg, scan temp. reg ++ T0, FSR, ++ no_such_interface, ++ /*return_method=*/false); ++ ++ ++ // profile this call ++ __ profile_virtual_call(T1, T0, FSR); ++ ++ // Get declaring interface class from method, and itable index ++ __ ld_ptr(T2, Rmethod, in_bytes(Method::const_offset())); ++ __ ld_ptr(T2, T2, in_bytes(ConstMethod::constants_offset())); ++ __ ld_ptr(T2, T2, ConstantPool::pool_holder_offset_in_bytes()); ++ __ lw(Rmethod, Rmethod, in_bytes(Method::itable_index_offset())); ++ __ addiu(Rmethod, Rmethod, (-1) * Method::itable_index_max); ++ __ subu32(Rmethod, R0, Rmethod); ++ ++ __ lookup_interface_method(// inputs: rec. class, interface, itable index ++ T1, T2, Rmethod, ++ // outputs: method, scan temp. reg ++ Rmethod, T0, ++ no_such_interface); ++ ++ // Rmethod: Method* to call ++ // T3: receiver ++ // Check for abstract method error ++ // Note: This should be done more efficiently via a throw_abstract_method_error ++ // interpreter entry point and a conditional jump to it in case of a null ++ // method. ++ __ beq(Rmethod, R0, no_such_method); ++ __ delayed()->nop(); ++ ++ __ profile_arguments_type(T1, Rmethod, T0, true); ++ ++ // do the call ++ // T3: receiver ++ // Rmethod: Method* ++ __ jump_from_interpreted(Rmethod, T1); ++ __ should_not_reach_here(); ++ ++ // exception handling code follows... ++ // note: must restore interpreter registers to canonical ++ // state for exception handling to work correctly! ++ ++ __ bind(no_such_method); ++ // throw exception ++ __ call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::throw_AbstractMethodError)); ++ // the call_VM checks for exception, so we should never return here. ++ __ should_not_reach_here(); ++ ++ __ bind(no_such_interface); ++ // throw exception ++ __ call_VM(noreg, CAST_FROM_FN_PTR(address, ++ InterpreterRuntime::throw_IncompatibleClassChangeError)); ++ // the call_VM checks for exception, so we should never return here. ++ __ should_not_reach_here(); ++ ++} ++ ++ ++void TemplateTable::invokehandle(int byte_no) { ++ transition(vtos, vtos); ++ assert(byte_no == f1_byte, "use this argument"); ++ const Register T2_method = Rmethod; ++ const Register FSR_mtype = FSR; ++ const Register T3_recv = T3; ++ ++ if (!EnableInvokeDynamic) { ++ // rewriter does not generate this bytecode ++ __ should_not_reach_here(); ++ return; ++ } ++ ++ prepare_invoke(byte_no, T2_method, FSR_mtype, T3_recv); ++ //??__ verify_method_ptr(T2_method); ++ __ verify_oop(T3_recv); ++ __ null_check(T3_recv); ++ ++ // T9: MethodType object (from cpool->resolved_references[f1], if necessary) ++ // T2_method: MH.invokeExact_MT method (from f2) ++ ++ // Note: T9 is already pushed (if necessary) by prepare_invoke ++ ++ // FIXME: profile the LambdaForm also ++ __ profile_final_call(T9); ++ ++ // T8: tmp, used for mdp ++ // T2_method: callee ++ // T9: tmp ++ // is_virtual: true ++ __ profile_arguments_type(T8, T2_method, T9, true); ++ ++ __ jump_from_interpreted(T2_method, T9); ++} ++ ++ void TemplateTable::invokedynamic(int byte_no) { ++ transition(vtos, vtos); ++ assert(byte_no == f1_byte, "use this argument"); ++ ++ if (!EnableInvokeDynamic) { ++ // We should not encounter this bytecode if !EnableInvokeDynamic. ++ // The verifier will stop it. However, if we get past the verifier, ++ // this will stop the thread in a reasonable way, without crashing the JVM. ++ __ call_VM(noreg, CAST_FROM_FN_PTR(address, ++ InterpreterRuntime::throw_IncompatibleClassChangeError)); ++ // the call_VM checks for exception, so we should never return here. ++ __ should_not_reach_here(); ++ return; ++ } ++ ++ //const Register Rmethod = T2; ++ const Register T2_callsite = T2; ++ ++ prepare_invoke(byte_no, Rmethod, T2_callsite); ++ ++ // T2: CallSite object (from cpool->resolved_references[f1]) ++ // Rmethod: MH.linkToCallSite method (from f2) ++ ++ // Note: T2_callsite is already pushed by prepare_invoke ++ // %%% should make a type profile for any invokedynamic that takes a ref argument ++ // profile this call ++ __ profile_call(T9); ++ ++ // T8: tmp, used for mdp ++ // Rmethod: callee ++ // T9: tmp ++ // is_virtual: false ++ __ profile_arguments_type(T8, Rmethod, T9, false); ++ ++ __ verify_oop(T2_callsite); ++ ++ __ jump_from_interpreted(Rmethod, T9); ++ } ++ ++//----------------------------------------------------------------------------- ++// Allocation ++// T1 : tags & buffer end & thread ++// T2 : object end ++// T3 : klass ++// T1 : object size ++// A1 : cpool ++// A2 : cp index ++// return object in FSR ++void TemplateTable::_new() { ++ transition(vtos, atos); ++ __ get_unsigned_2_byte_index_at_bcp(A2, 1); ++ ++ Label slow_case; ++ Label done; ++ Label initialize_header; ++ Label initialize_object; // including clearing the fields ++ Label allocate_shared; ++ ++ // get InstanceKlass in T3 ++ __ get_cpool_and_tags(A1, T1); ++ ++ __ dsll(AT, A2, Address::times_8); ++ if (UseLEXT1 && Assembler::is_simm(sizeof(ConstantPool), 8)) { ++ __ gsldx(T3, A1, AT, sizeof(ConstantPool)); ++ } else { ++ __ daddu(AT, A1, AT); ++ __ ld(T3, AT, sizeof(ConstantPool)); ++ } ++ ++ // make sure the class we're about to instantiate has been resolved. ++ // Note: slow_case does a pop of stack, which is why we loaded class/pushed above ++ const int tags_offset = Array::base_offset_in_bytes(); ++ if (UseLEXT1 && Assembler::is_simm(tags_offset, 8)) { ++ __ gslbx(AT, T1, A2, tags_offset); ++ } else { ++ __ daddu(T1, T1, A2); ++ __ lb(AT, T1, tags_offset); ++ } ++ if(os::is_MP()) { ++ __ sync(); // load acquire ++ } ++ __ daddiu(AT, AT, - (int)JVM_CONSTANT_Class); ++ __ bne(AT, R0, slow_case); ++ __ delayed()->nop(); ++ ++ ++ // make sure klass is initialized & doesn't have finalizer ++ // make sure klass is fully initialized ++ __ lhu(T1, T3, in_bytes(InstanceKlass::init_state_offset())); ++ __ daddiu(AT, T1, - (int)InstanceKlass::fully_initialized); ++ __ bne(AT, R0, slow_case); ++ __ delayed()->nop(); ++ ++ // has_finalizer ++ __ lw(T0, T3, in_bytes(Klass::layout_helper_offset()) ); ++ __ andi(AT, T0, Klass::_lh_instance_slow_path_bit); ++ __ bne(AT, R0, slow_case); ++ __ delayed()->nop(); ++ ++ // Allocate the instance ++ // 1) Try to allocate in the TLAB ++ // 2) if fail and the object is large allocate in the shared Eden ++ // 3) if the above fails (or is not applicable), go to a slow case ++ // (creates a new TLAB, etc.) ++ ++ const bool allow_shared_alloc = ++ Universe::heap()->supports_inline_contig_alloc() && !CMSIncrementalMode; ++ ++#ifndef OPT_THREAD ++ const Register thread = T8; ++ if (UseTLAB || allow_shared_alloc) { ++ __ get_thread(thread); ++ } ++#else ++ const Register thread = TREG; ++#endif ++ ++ if (UseTLAB) { ++ // get tlab_top ++ __ ld(FSR, thread, in_bytes(JavaThread::tlab_top_offset())); ++ // get tlab_end ++ __ ld(AT, thread, in_bytes(JavaThread::tlab_end_offset())); ++ __ daddu(T2, FSR, T0); ++ __ slt(AT, AT, T2); ++ __ bne(AT, R0, allow_shared_alloc ? allocate_shared : slow_case); ++ __ delayed()->nop(); ++ __ sd(T2, thread, in_bytes(JavaThread::tlab_top_offset())); ++ ++ if (ZeroTLAB) { ++ // the fields have been already cleared ++ __ beq(R0, R0, initialize_header); ++ } else { ++ // initialize both the header and fields ++ __ beq(R0, R0, initialize_object); ++ } ++ __ delayed()->nop(); ++ } ++ ++ // Allocation in the shared Eden , if allowed ++ // T0 : instance size in words ++ if(allow_shared_alloc){ ++ __ bind(allocate_shared); ++ ++ Label retry; ++ Address heap_top(T1); ++ __ set64(T1, (long)Universe::heap()->top_addr()); ++ __ ld(FSR, heap_top); ++ ++ __ bind(retry); ++ __ set64(AT, (long)Universe::heap()->end_addr()); ++ __ ld(AT, AT, 0); ++ __ daddu(T2, FSR, T0); ++ __ slt(AT, AT, T2); ++ __ bne(AT, R0, slow_case); ++ __ delayed()->nop(); ++ ++ // Compare FSR with the top addr, and if still equal, store the new ++ // top addr in T2 at the address of the top addr pointer. Sets AT if was ++ // equal, and clears it otherwise. Use lock prefix for atomicity on MPs. ++ // ++ // FSR: object begin ++ // T2: object end ++ // T0: instance size in words ++ ++ // if someone beat us on the allocation, try again, otherwise continue ++ __ cmpxchg(T2, heap_top, FSR); ++ __ beq(AT, R0, retry); ++ __ delayed()->nop(); ++ ++ __ incr_allocated_bytes(thread, T0, 0); ++ } ++ ++ if (UseTLAB || Universe::heap()->supports_inline_contig_alloc()) { ++ // The object is initialized before the header. If the object size is ++ // zero, go directly to the header initialization. ++ __ bind(initialize_object); ++ __ set64(AT, - sizeof(oopDesc)); ++ __ daddu(T0, T0, AT); ++ __ beq(T0, R0, initialize_header); ++ __ delayed()->nop(); ++ ++ // initialize remaining object fields: T0 is a multiple of 2 ++ { ++ Label loop; ++ __ daddu(T1, FSR, T0); ++ __ daddiu(T1, T1, -oopSize); ++ ++ __ bind(loop); ++ __ sd(R0, T1, sizeof(oopDesc) + 0 * oopSize); ++ __ bne(T1, FSR, loop); //dont clear header ++ __ delayed()->daddiu(T1, T1, -oopSize); ++ } ++ ++ //klass in T3, ++ // initialize object header only. ++ __ bind(initialize_header); ++ if (UseBiasedLocking) { ++ __ ld(AT, T3, in_bytes(Klass::prototype_header_offset())); ++ __ sd(AT, FSR, oopDesc::mark_offset_in_bytes ()); ++ } else { ++ __ set64(AT, (long)markOopDesc::prototype()); ++ __ sd(AT, FSR, oopDesc::mark_offset_in_bytes()); ++ } ++ ++ __ store_klass_gap(FSR, R0); ++ __ store_klass(FSR, T3); ++ ++ { ++ SkipIfEqual skip_if(_masm, &DTraceAllocProbes, 0); ++ // Trigger dtrace event for fastpath ++ __ push(atos); ++ __ call_VM_leaf( ++ CAST_FROM_FN_PTR(address, SharedRuntime::dtrace_object_alloc), FSR); ++ __ pop(atos); ++ ++ } ++ __ b(done); ++ __ delayed()->nop(); ++ } ++ ++ // slow case ++ __ bind(slow_case); ++ call_VM(FSR, CAST_FROM_FN_PTR(address, InterpreterRuntime::_new), A1, A2); ++ ++ // continue ++ __ bind(done); ++ __ sync(); ++} ++ ++void TemplateTable::newarray() { ++ transition(itos, atos); ++ __ lbu(A1, at_bcp(1)); ++ //type, count ++ call_VM(FSR, CAST_FROM_FN_PTR(address, InterpreterRuntime::newarray), A1, FSR); ++ __ sync(); ++} ++ ++void TemplateTable::anewarray() { ++ transition(itos, atos); ++ __ get_2_byte_integer_at_bcp(A2, AT, 1); ++ __ huswap(A2); ++ __ get_constant_pool(A1); ++ // cp, index, count ++ call_VM(FSR, CAST_FROM_FN_PTR(address, InterpreterRuntime::anewarray), A1, A2, FSR); ++ __ sync(); ++} ++ ++void TemplateTable::arraylength() { ++ transition(atos, itos); ++ __ null_check(FSR, arrayOopDesc::length_offset_in_bytes()); ++ __ lw(FSR, FSR, arrayOopDesc::length_offset_in_bytes()); ++} ++ ++// when invoke gen_subtype_check, super in T3, sub in T2, object in FSR(it's always) ++// T2 : sub klass ++// T3 : cpool ++// T3 : super klass ++void TemplateTable::checkcast() { ++ transition(atos, atos); ++ Label done, is_null, ok_is_subtype, quicked, resolved; ++ __ beq(FSR, R0, is_null); ++ __ delayed()->nop(); ++ ++ // Get cpool & tags index ++ __ get_cpool_and_tags(T3, T1); ++ __ get_2_byte_integer_at_bcp(T2, AT, 1); ++ __ huswap(T2); ++ ++ // See if bytecode has already been quicked ++ __ daddu(AT, T1, T2); ++ __ lb(AT, AT, Array::base_offset_in_bytes()); ++ if(os::is_MP()) { ++ __ sync(); // load acquire ++ } ++ __ daddiu(AT, AT, - (int)JVM_CONSTANT_Class); ++ __ beq(AT, R0, quicked); ++ __ delayed()->nop(); ++ ++ // In InterpreterRuntime::quicken_io_cc, lots of new classes may be loaded. ++ // Then, GC will move the object in V0 to another places in heap. ++ // Therefore, We should never save such an object in register. ++ // Instead, we should save it in the stack. It can be modified automatically by the GC thread. ++ // After GC, the object address in FSR is changed to a new place. ++ // ++ __ push(atos); ++ const Register thread = TREG; ++#ifndef OPT_THREAD ++ __ get_thread(thread); ++#endif ++ call_VM(NOREG, CAST_FROM_FN_PTR(address, InterpreterRuntime::quicken_io_cc)); ++ __ get_vm_result_2(T3, thread); ++ __ pop_ptr(FSR); ++ __ b(resolved); ++ __ delayed()->nop(); ++ ++ // klass already in cp, get superklass in T3 ++ __ bind(quicked); ++ __ dsll(AT, T2, Address::times_8); ++ __ daddu(AT, T3, AT); ++ __ ld(T3, AT, sizeof(ConstantPool)); ++ ++ __ bind(resolved); ++ ++ // get subklass in T2 ++ //add for compressedoops ++ __ load_klass(T2, FSR); ++ // Superklass in T3. Subklass in T2. ++ __ gen_subtype_check(T3, T2, ok_is_subtype); ++ ++ // Come here on failure ++ // object is at FSR ++ __ jmp(Interpreter::_throw_ClassCastException_entry); ++ __ delayed()->nop(); ++ ++ // Come here on success ++ __ bind(ok_is_subtype); ++ ++ // Collect counts on whether this check-cast sees NULLs a lot or not. ++ if (ProfileInterpreter) { ++ __ b(done); ++ __ delayed()->nop(); ++ __ bind(is_null); ++ __ profile_null_seen(T3); ++ } else { ++ __ bind(is_null); ++ } ++ __ bind(done); ++} ++ ++// i use T3 as cpool, T1 as tags, T2 as index ++// object always in FSR, superklass in T3, subklass in T2 ++void TemplateTable::instanceof() { ++ transition(atos, itos); ++ Label done, is_null, ok_is_subtype, quicked, resolved; ++ ++ __ beq(FSR, R0, is_null); ++ __ delayed()->nop(); ++ ++ // Get cpool & tags index ++ __ get_cpool_and_tags(T3, T1); ++ // get index ++ __ get_2_byte_integer_at_bcp(T2, AT, 1); ++ __ huswap(T2); ++ ++ // See if bytecode has already been quicked ++ // quicked ++ __ daddu(AT, T1, T2); ++ __ lb(AT, AT, Array::base_offset_in_bytes()); ++ if(os::is_MP()) { ++ __ sync(); // load acquire ++ } ++ __ daddiu(AT, AT, - (int)JVM_CONSTANT_Class); ++ __ beq(AT, R0, quicked); ++ __ delayed()->nop(); ++ ++ __ push(atos); ++ const Register thread = TREG; ++#ifndef OPT_THREAD ++ __ get_thread(thread); ++#endif ++ call_VM(NOREG, CAST_FROM_FN_PTR(address, InterpreterRuntime::quicken_io_cc)); ++ __ get_vm_result_2(T3, thread); ++ __ pop_ptr(FSR); ++ __ b(resolved); ++ __ delayed()->nop(); ++ ++ // get superklass in T3, subklass in T2 ++ __ bind(quicked); ++ __ dsll(AT, T2, Address::times_8); ++ __ daddu(AT, T3, AT); ++ __ ld(T3, AT, sizeof(ConstantPool)); ++ ++ __ bind(resolved); ++ // get subklass in T2 ++ //add for compressedoops ++ __ load_klass(T2, FSR); ++ ++ // Superklass in T3. Subklass in T2. ++ __ gen_subtype_check(T3, T2, ok_is_subtype); ++ // Come here on failure ++ __ b(done); ++ __ delayed(); __ move(FSR, R0); ++ ++ // Come here on success ++ __ bind(ok_is_subtype); ++ __ move(FSR, 1); ++ ++ // Collect counts on whether this test sees NULLs a lot or not. ++ if (ProfileInterpreter) { ++ __ beq(R0, R0, done); ++ __ delayed()->nop(); ++ __ bind(is_null); ++ __ profile_null_seen(T3); ++ } else { ++ __ bind(is_null); // same as 'done' ++ } ++ __ bind(done); ++ // FSR = 0: obj == NULL or obj is not an instanceof the specified klass ++ // FSR = 1: obj != NULL and obj is an instanceof the specified klass ++} ++ ++//-------------------------------------------------------- ++//-------------------------------------------- ++// Breakpoints ++void TemplateTable::_breakpoint() { ++ // Note: We get here even if we are single stepping.. ++ // jbug inists on setting breakpoints at every bytecode ++ // even if we are in single step mode. ++ ++ transition(vtos, vtos); ++ ++ // get the unpatched byte code ++ __ get_method(A1); ++ __ call_VM(NOREG, ++ CAST_FROM_FN_PTR(address, ++ InterpreterRuntime::get_original_bytecode_at), ++ A1, BCP); ++ __ move(Rnext, V0); // Rnext will be used in dispatch_only_normal ++ ++ // post the breakpoint event ++ __ get_method(A1); ++ __ call_VM(NOREG, CAST_FROM_FN_PTR(address, InterpreterRuntime::_breakpoint), A1, BCP); ++ ++ // complete the execution of original bytecode ++ __ dispatch_only_normal(vtos); ++} ++ ++//----------------------------------------------------------------------------- ++// Exceptions ++ ++void TemplateTable::athrow() { ++ transition(atos, vtos); ++ __ null_check(FSR); ++ __ jmp(Interpreter::throw_exception_entry()); ++ __ delayed()->nop(); ++} ++ ++//----------------------------------------------------------------------------- ++// Synchronization ++// ++// Note: monitorenter & exit are symmetric routines; which is reflected ++// in the assembly code structure as well ++// ++// Stack layout: ++// ++// [expressions ] <--- SP = expression stack top ++// .. ++// [expressions ] ++// [monitor entry] <--- monitor block top = expression stack bot ++// .. ++// [monitor entry] ++// [frame data ] <--- monitor block bot ++// ... ++// [return addr ] <--- FP ++ ++// we use T2 as monitor entry pointer, T3 as monitor top pointer, c_rarg0 as free slot pointer ++// object always in FSR ++void TemplateTable::monitorenter() { ++ transition(atos, vtos); ++ ++ // check for NULL object ++ __ null_check(FSR); ++ ++ const Address monitor_block_top(FP, frame::interpreter_frame_monitor_block_top_offset ++ * wordSize); ++ const int entry_size = (frame::interpreter_frame_monitor_size()* wordSize); ++ Label allocated; ++ ++ // initialize entry pointer ++ __ move(c_rarg0, R0); ++ ++ // find a free slot in the monitor block (result in c_rarg0) ++ { ++ Label entry, loop, exit, next; ++ __ ld(T2, monitor_block_top); ++ __ b(entry); ++ __ delayed()->daddiu(T3, FP, frame::interpreter_frame_initial_sp_offset * wordSize); ++ ++ // free slot? ++ __ bind(loop); ++ __ ld(AT, T2, BasicObjectLock::obj_offset_in_bytes()); ++ __ bne(AT, R0, next); ++ __ delayed()->nop(); ++ __ move(c_rarg0, T2); ++ ++ __ bind(next); ++ __ beq(FSR, AT, exit); ++ __ delayed()->nop(); ++ __ daddiu(T2, T2, entry_size); ++ ++ __ bind(entry); ++ __ bne(T3, T2, loop); ++ __ delayed()->nop(); ++ __ bind(exit); ++ } ++ ++ __ bne(c_rarg0, R0, allocated); ++ __ delayed()->nop(); ++ ++ // allocate one if there's no free slot ++ { ++ Label entry, loop; ++ // 1. compute new pointers // SP: old expression stack top ++ __ ld(c_rarg0, monitor_block_top); ++ __ daddiu(SP, SP, - entry_size); ++ __ daddiu(c_rarg0, c_rarg0, - entry_size); ++ __ sd(c_rarg0, monitor_block_top); ++ __ b(entry); ++ __ delayed(); __ move(T3, SP); ++ ++ // 2. move expression stack contents ++ __ bind(loop); ++ __ ld(AT, T3, entry_size); ++ __ sd(AT, T3, 0); ++ __ daddiu(T3, T3, wordSize); ++ __ bind(entry); ++ __ bne(T3, c_rarg0, loop); ++ __ delayed()->nop(); ++ } ++ ++ __ bind(allocated); ++ // Increment bcp to point to the next bytecode, ++ // so exception handling for async. exceptions work correctly. ++ // The object has already been poped from the stack, so the ++ // expression stack looks correct. ++ __ daddiu(BCP, BCP, 1); ++ __ sd(FSR, c_rarg0, BasicObjectLock::obj_offset_in_bytes()); ++ __ lock_object(c_rarg0); ++ // check to make sure this monitor doesn't cause stack overflow after locking ++ __ save_bcp(); // in case of exception ++ __ generate_stack_overflow_check(0); ++ // The bcp has already been incremented. Just need to dispatch to next instruction. ++ ++ __ dispatch_next(vtos); ++} ++ ++// T2 : top ++// c_rarg0 : entry ++void TemplateTable::monitorexit() { ++ transition(atos, vtos); ++ ++ __ null_check(FSR); ++ ++ const int entry_size =(frame::interpreter_frame_monitor_size()* wordSize); ++ Label found; ++ ++ // find matching slot ++ { ++ Label entry, loop; ++ __ ld(c_rarg0, FP, frame::interpreter_frame_monitor_block_top_offset * wordSize); ++ __ b(entry); ++ __ delayed()->daddiu(T2, FP, frame::interpreter_frame_initial_sp_offset * wordSize); ++ ++ __ bind(loop); ++ __ ld(AT, c_rarg0, BasicObjectLock::obj_offset_in_bytes()); ++ __ beq(FSR, AT, found); ++ __ delayed()->nop(); ++ __ daddiu(c_rarg0, c_rarg0, entry_size); ++ __ bind(entry); ++ __ bne(T2, c_rarg0, loop); ++ __ delayed()->nop(); ++ } ++ ++ // error handling. Unlocking was not block-structured ++ Label end; ++ __ call_VM(NOREG, CAST_FROM_FN_PTR(address, ++ InterpreterRuntime::throw_illegal_monitor_state_exception)); ++ __ should_not_reach_here(); ++ ++ // call run-time routine ++ // c_rarg0: points to monitor entry ++ __ bind(found); ++ __ move(TSR, FSR); ++ __ unlock_object(c_rarg0); ++ __ move(FSR, TSR); ++ __ bind(end); ++} ++ ++ ++// Wide instructions ++void TemplateTable::wide() { ++ transition(vtos, vtos); ++ __ lbu(Rnext, at_bcp(1)); ++ __ dsll(T9, Rnext, Address::times_8); ++ __ li(AT, (long)Interpreter::_wentry_point); ++ __ daddu(AT, T9, AT); ++ __ ld(T9, AT, 0); ++ __ jr(T9); ++ __ delayed()->nop(); ++} ++ ++ ++void TemplateTable::multianewarray() { ++ transition(vtos, atos); ++ // last dim is on top of stack; we want address of first one: ++ // first_addr = last_addr + (ndims - 1) * wordSize ++ __ lbu(A1, at_bcp(3)); // dimension ++ __ daddiu(A1, A1, -1); ++ __ dsll(A1, A1, Address::times_8); ++ __ daddu(A1, SP, A1); // now A1 pointer to the count array on the stack ++ call_VM(FSR, CAST_FROM_FN_PTR(address, InterpreterRuntime::multianewarray), A1); ++ __ lbu(AT, at_bcp(3)); ++ __ dsll(AT, AT, Address::times_8); ++ __ daddu(SP, SP, AT); ++ __ sync(); ++} ++#endif // !CC_INTERP +diff --git a/hotspot/src/cpu/mips/vm/templateTable_mips_64.hpp b/hotspot/src/cpu/mips/vm/templateTable_mips_64.hpp +new file mode 100644 +index 0000000000..b63274a206 +--- /dev/null ++++ b/hotspot/src/cpu/mips/vm/templateTable_mips_64.hpp +@@ -0,0 +1,44 @@ ++/* ++ * Copyright (c) 2003, 2012, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2015, 2016, Loongson Technology. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ * ++ */ ++ ++#ifndef CPU_MIPS_VM_TEMPLATETABLE_MIPS_64_HPP ++#define CPU_MIPS_VM_TEMPLATETABLE_MIPS_64_HPP ++ ++ static void prepare_invoke(int byte_no, ++ Register method, ++ Register index = noreg, ++ Register recv = noreg, ++ Register flags = noreg ++ ); ++ static void invokevirtual_helper(Register index, Register recv, ++ Register flags); ++ //static void volatile_barrier(Assembler::Membar_mask_bits order_constraint); ++ static void volatile_barrier(); ++ ++ // Helpers ++ static void index_check(Register array, Register index); ++ static void index_check_without_pop(Register array, Register index); ++ ++#endif // CPU_MIPS_VM_TEMPLATETABLE_MIPS_64_HPP +diff --git a/hotspot/src/cpu/mips/vm/vmStructs_mips.hpp b/hotspot/src/cpu/mips/vm/vmStructs_mips.hpp +new file mode 100644 +index 0000000000..6939914356 +--- /dev/null ++++ b/hotspot/src/cpu/mips/vm/vmStructs_mips.hpp +@@ -0,0 +1,68 @@ ++/* ++ * Copyright (c) 2001, 2013, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2015, 2016, Loongson Technology. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ * ++ */ ++ ++#ifndef CPU_MIPS_VM_VMSTRUCTS_MIPS_HPP ++#define CPU_MIPS_VM_VMSTRUCTS_MIPS_HPP ++ ++// These are the CPU-specific fields, types and integer ++// constants required by the Serviceability Agent. This file is ++// referenced by vmStructs.cpp. ++ ++#define VM_STRUCTS_CPU(nonstatic_field, static_field, unchecked_nonstatic_field, volatile_nonstatic_field, nonproduct_nonstatic_field, c2_nonstatic_field, unchecked_c1_static_field, unchecked_c2_static_field) \ ++ \ ++ /******************************/ \ ++ /* JavaCallWrapper */ \ ++ /******************************/ \ ++ /******************************/ \ ++ /* JavaFrameAnchor */ \ ++ /******************************/ \ ++ volatile_nonstatic_field(JavaFrameAnchor, _last_Java_fp, intptr_t*) \ ++ \ ++ ++ /* NOTE that we do not use the last_entry() macro here; it is used */ ++ /* in vmStructs__.hpp's VM_STRUCTS_OS_CPU macro (and must */ ++ /* be present there) */ ++ ++ ++#define VM_TYPES_CPU(declare_type, declare_toplevel_type, declare_oop_type, declare_integer_type, declare_unsigned_integer_type, declare_c1_toplevel_type, declare_c2_type, declare_c2_toplevel_type) \ ++ ++ /* NOTE that we do not use the last_entry() macro here; it is used */ ++ /* in vmStructs__.hpp's VM_TYPES_OS_CPU macro (and must */ ++ /* be present there) */ ++ ++ ++#define VM_INT_CONSTANTS_CPU(declare_constant, declare_preprocessor_constant, declare_c1_constant, declare_c2_constant, declare_c2_preprocessor_constant) \ ++ ++ /* NOTE that we do not use the last_entry() macro here; it is used */ ++ /* in vmStructs__.hpp's VM_INT_CONSTANTS_OS_CPU macro (and must */ ++ /* be present there) */ ++ ++#define VM_LONG_CONSTANTS_CPU(declare_constant, declare_preprocessor_constant, declare_c1_constant, declare_c2_constant, declare_c2_preprocessor_constant) \ ++ ++ /* NOTE that we do not use the last_entry() macro here; it is used */ ++ /* in vmStructs__.hpp's VM_LONG_CONSTANTS_OS_CPU macro (and must */ ++ /* be present there) */ ++ ++#endif // CPU_MIPS_VM_VMSTRUCTS_MIPS_HPP +diff --git a/hotspot/src/cpu/mips/vm/vm_version_ext_mips.cpp b/hotspot/src/cpu/mips/vm/vm_version_ext_mips.cpp +new file mode 100644 +index 0000000000..a98f70d9ff +--- /dev/null ++++ b/hotspot/src/cpu/mips/vm/vm_version_ext_mips.cpp +@@ -0,0 +1,89 @@ ++/* ++ * Copyright (c) 2013, 2018, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2018, 2020, Loongson Technology. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ * ++ */ ++ ++#include "memory/allocation.inline.hpp" ++#include "vm_version_ext_mips.hpp" ++ ++// VM_Version_Ext statics ++int VM_Version_Ext::_no_of_threads = 0; ++int VM_Version_Ext::_no_of_cores = 0; ++int VM_Version_Ext::_no_of_sockets = 0; ++bool VM_Version_Ext::_initialized = false; ++char VM_Version_Ext::_cpu_name[CPU_TYPE_DESC_BUF_SIZE] = {0}; ++char VM_Version_Ext::_cpu_desc[CPU_DETAILED_DESC_BUF_SIZE] = {0}; ++ ++void VM_Version_Ext::initialize_cpu_information(void) { ++ // do nothing if cpu info has been initialized ++ if (_initialized) { ++ return; ++ } ++ ++ _no_of_cores = os::processor_count(); ++ _no_of_threads = _no_of_cores; ++ _no_of_sockets = _no_of_cores; ++ if (is_loongson()) { ++ snprintf(_cpu_name, CPU_TYPE_DESC_BUF_SIZE - 1, "Loongson MIPS"); ++ snprintf(_cpu_desc, CPU_DETAILED_DESC_BUF_SIZE, "Loongson MIPS %s", cpu_features()); ++ } else { ++ snprintf(_cpu_name, CPU_TYPE_DESC_BUF_SIZE - 1, "MIPS"); ++ snprintf(_cpu_desc, CPU_DETAILED_DESC_BUF_SIZE, "MIPS %s", cpu_features()); ++ } ++ _initialized = true; ++} ++ ++int VM_Version_Ext::number_of_threads(void) { ++ initialize_cpu_information(); ++ return _no_of_threads; ++} ++ ++int VM_Version_Ext::number_of_cores(void) { ++ initialize_cpu_information(); ++ return _no_of_cores; ++} ++ ++int VM_Version_Ext::number_of_sockets(void) { ++ initialize_cpu_information(); ++ return _no_of_sockets; ++} ++ ++const char* VM_Version_Ext::cpu_name(void) { ++ initialize_cpu_information(); ++ char* tmp = NEW_C_HEAP_ARRAY_RETURN_NULL(char, CPU_TYPE_DESC_BUF_SIZE, mtTracing); ++ if (NULL == tmp) { ++ return NULL; ++ } ++ strncpy(tmp, _cpu_name, CPU_TYPE_DESC_BUF_SIZE); ++ return tmp; ++} ++ ++const char* VM_Version_Ext::cpu_description(void) { ++ initialize_cpu_information(); ++ char* tmp = NEW_C_HEAP_ARRAY_RETURN_NULL(char, CPU_DETAILED_DESC_BUF_SIZE, mtTracing); ++ if (NULL == tmp) { ++ return NULL; ++ } ++ strncpy(tmp, _cpu_desc, CPU_DETAILED_DESC_BUF_SIZE); ++ return tmp; ++} +diff --git a/hotspot/src/cpu/mips/vm/vm_version_ext_mips.hpp b/hotspot/src/cpu/mips/vm/vm_version_ext_mips.hpp +new file mode 100644 +index 0000000000..a240fcc2e9 +--- /dev/null ++++ b/hotspot/src/cpu/mips/vm/vm_version_ext_mips.hpp +@@ -0,0 +1,54 @@ ++/* ++ * Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2018, 2020, Loongson Technology. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ * ++ */ ++ ++#ifndef CPU_MIPS_VM_VM_VERSION_EXT_MIPS_HPP ++#define CPU_MIPS_VM_VM_VERSION_EXT_MIPS_HPP ++ ++#include "runtime/vm_version.hpp" ++#include "utilities/macros.hpp" ++ ++class VM_Version_Ext : public VM_Version { ++ private: ++ static const size_t CPU_TYPE_DESC_BUF_SIZE = 256; ++ static const size_t CPU_DETAILED_DESC_BUF_SIZE = 4096; ++ ++ static int _no_of_threads; ++ static int _no_of_cores; ++ static int _no_of_sockets; ++ static bool _initialized; ++ static char _cpu_name[CPU_TYPE_DESC_BUF_SIZE]; ++ static char _cpu_desc[CPU_DETAILED_DESC_BUF_SIZE]; ++ ++ public: ++ static int number_of_threads(void); ++ static int number_of_cores(void); ++ static int number_of_sockets(void); ++ ++ static const char* cpu_name(void); ++ static const char* cpu_description(void); ++ static void initialize_cpu_information(void); ++}; ++ ++#endif // CPU_MIPS_VM_VM_VERSION_EXT_MIPS_HPP +diff --git a/hotspot/src/cpu/mips/vm/vm_version_mips.cpp b/hotspot/src/cpu/mips/vm/vm_version_mips.cpp +new file mode 100644 +index 0000000000..99e16dda9c +--- /dev/null ++++ b/hotspot/src/cpu/mips/vm/vm_version_mips.cpp +@@ -0,0 +1,504 @@ ++/* ++ * Copyright (c) 1997, 2014, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2015, 2021, Loongson Technology. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ * ++ */ ++ ++#include "precompiled.hpp" ++#include "asm/macroAssembler.hpp" ++#include "asm/macroAssembler.inline.hpp" ++#include "memory/resourceArea.hpp" ++#include "runtime/java.hpp" ++#include "runtime/stubCodeGenerator.hpp" ++#include "vm_version_mips.hpp" ++#ifdef TARGET_OS_FAMILY_linux ++# include "os_linux.inline.hpp" ++#endif ++ ++#define A0 RA0 ++ ++int VM_Version::_cpuFeatures; ++const char* VM_Version::_features_str = ""; ++VM_Version::CpuidInfo VM_Version::_cpuid_info = { 0, }; ++volatile bool VM_Version::_is_determine_cpucfg_supported_running = false; ++bool VM_Version::_is_cpucfg_instruction_supported = true; ++bool VM_Version::_cpu_info_is_initialized = false; ++ ++static BufferBlob* stub_blob; ++static const int stub_size = 600; ++ ++extern "C" { ++ typedef void (*get_cpu_info_stub_t)(void*); ++} ++static get_cpu_info_stub_t get_cpu_info_stub = NULL; ++ ++ ++class VM_Version_StubGenerator: public StubCodeGenerator { ++ public: ++ ++ VM_Version_StubGenerator(CodeBuffer *c) : StubCodeGenerator(c) {} ++ ++ address generate_get_cpu_info() { ++ assert(!VM_Version::cpu_info_is_initialized(), "VM_Version should not be initialized"); ++ StubCodeMark mark(this, "VM_Version", "get_cpu_info_stub"); ++# define __ _masm-> ++ ++ address start = __ pc(); ++ ++ __ enter(); ++ __ push(AT); ++ __ push(V0); ++ ++ __ li(AT, (long)0); ++ __ cpucfg(V0, AT); ++ __ lw(AT, A0, in_bytes(VM_Version::Loongson_Cpucfg_id0_offset())); ++ __ sw(V0, A0, in_bytes(VM_Version::Loongson_Cpucfg_id0_offset())); ++ ++ __ li(AT, 1); ++ __ cpucfg(V0, AT); ++ __ lw(AT, A0, in_bytes(VM_Version::Loongson_Cpucfg_id1_offset())); ++ __ sw(V0, A0, in_bytes(VM_Version::Loongson_Cpucfg_id1_offset())); ++ ++ __ li(AT, 2); ++ __ cpucfg(V0, AT); ++ __ lw(AT, A0, in_bytes(VM_Version::Loongson_Cpucfg_id2_offset())); ++ __ sw(V0, A0, in_bytes(VM_Version::Loongson_Cpucfg_id2_offset())); ++ ++ __ pop(V0); ++ __ pop(AT); ++ __ leave(); ++ __ jr(RA); ++ __ delayed()->nop(); ++# undef __ ++ ++ return start; ++ }; ++}; ++ ++uint32_t VM_Version::get_feature_flags_by_cpucfg() { ++ uint32_t result = 0; ++ if (_cpuid_info.cpucfg_info_id1.bits.MMI != 0) ++ result |= CPU_MMI; ++ if (_cpuid_info.cpucfg_info_id1.bits.MSA1 != 0) ++ result |= CPU_MSA1_0; ++ if (_cpuid_info.cpucfg_info_id1.bits.MSA2 != 0) ++ result |= CPU_MSA2_0; ++ if (_cpuid_info.cpucfg_info_id1.bits.CGP != 0) ++ result |= CPU_CGP; ++ if (_cpuid_info.cpucfg_info_id1.bits.LSX1 != 0) ++ result |= CPU_LSX1; ++ if (_cpuid_info.cpucfg_info_id1.bits.LSX2 != 0) ++ result |= CPU_LSX2; ++ if (_cpuid_info.cpucfg_info_id1.bits.LASX != 0) ++ result |= CPU_LASX; ++ if (_cpuid_info.cpucfg_info_id1.bits.LLSYNC != 0) ++ result |= CPU_LLSYNC; ++ if (_cpuid_info.cpucfg_info_id1.bits.TGTSYNC != 0) ++ result |= CPU_TGTSYNC; ++ if (_cpuid_info.cpucfg_info_id1.bits.MUALP != 0) ++ result |= CPU_MUALP; ++ if (_cpuid_info.cpucfg_info_id2.bits.LEXT1 != 0) ++ result |= CPU_LEXT1; ++ if (_cpuid_info.cpucfg_info_id2.bits.LEXT2 != 0) ++ result |= CPU_LEXT2; ++ if (_cpuid_info.cpucfg_info_id2.bits.LEXT3 != 0) ++ result |= CPU_LEXT3; ++ if (_cpuid_info.cpucfg_info_id2.bits.LAMO != 0) ++ result |= CPU_LAMO; ++ if (_cpuid_info.cpucfg_info_id2.bits.LPIXU != 0) ++ result |= CPU_LPIXU; ++ ++ result |= CPU_ULSYNC; ++ ++ return result; ++} ++ ++void read_cpu_info(const char *path, char *result) { ++ FILE *ptr; ++ char buf[1024]; ++ int i = 0; ++ if((ptr=fopen(path, "r")) != NULL) { ++ while(fgets(buf, 1024, ptr)!=NULL) { ++ strcat(result,buf); ++ i++; ++ if (i == 10) break; ++ } ++ fclose(ptr); ++ } else { ++ warning("Can't detect CPU info - cannot open %s", path); ++ } ++} ++ ++void strlwr(char *str) { ++ for (; *str!='\0'; str++) ++ *str = tolower(*str); ++} ++ ++int VM_Version::get_feature_flags_by_cpuinfo(int features) { ++ assert(!cpu_info_is_initialized(), "VM_Version should not be initialized"); ++ ++ char res[10240]; ++ int i; ++ memset(res, '\0', 10240 * sizeof(char)); ++ read_cpu_info("/proc/cpuinfo", res); ++ // res is converted to lower case ++ strlwr(res); ++ ++ if (strstr(res, "loongson")) { ++ // Loongson CPU ++ features |= CPU_LOONGSON; ++ ++ const struct Loongson_Cpuinfo loongson_cpuinfo[] = { ++ {L_3A1000, "3a1000"}, ++ {L_3B1500, "3b1500"}, ++ {L_3A2000, "3a2000"}, ++ {L_3B2000, "3b2000"}, ++ {L_3A3000, "3a3000"}, ++ {L_3B3000, "3b3000"}, ++ {L_2K1000, "2k1000"}, ++ {L_UNKNOWN, "unknown"} ++ }; ++ ++ // Loongson Family ++ int detected = 0; ++ for (i = 0; i <= L_UNKNOWN; i++) { ++ switch (i) { ++ // 3A1000 and 3B1500 may use an old kernel and further comparsion is needed ++ // test PRID REV in /proc/cpuinfo ++ // 3A1000: V0.5, model name: ICT Loongson-3A V0.5 FPU V0.1 ++ // 3B1500: V0.7, model name: ICT Loongson-3B V0.7 FPU V0.1 ++ case L_3A1000: ++ if (strstr(res, loongson_cpuinfo[i].match_str) || strstr(res, "loongson-3a v0.5")) { ++ features |= CPU_LOONGSON_GS464; ++ detected++; ++ //tty->print_cr("3A1000 platform"); ++ } ++ break; ++ case L_3B1500: ++ if (strstr(res, loongson_cpuinfo[i].match_str) || strstr(res, "loongson-3b v0.7")) { ++ features |= CPU_LOONGSON_GS464; ++ detected++; ++ //tty->print_cr("3B1500 platform"); ++ } ++ break; ++ case L_3A2000: ++ case L_3B2000: ++ case L_3A3000: ++ case L_3B3000: ++ if (strstr(res, loongson_cpuinfo[i].match_str)) { ++ features |= CPU_LOONGSON_GS464E; ++ detected++; ++ //tty->print_cr("3A2000/3A3000/3B2000/3B3000 platform"); ++ } ++ break; ++ case L_2K1000: ++ if (strstr(res, loongson_cpuinfo[i].match_str)) { ++ features |= CPU_LOONGSON_GS264; ++ detected++; ++ //tty->print_cr("2K1000 platform"); ++ } ++ break; ++ case L_UNKNOWN: ++ if (detected == 0) { ++ detected++; ++ //tty->print_cr("unknown Loongson platform"); ++ } ++ break; ++ default: ++ ShouldNotReachHere(); ++ } ++ } ++ assert (detected == 1, "one and only one of LOONGSON_CPU_FAMILY should be detected"); ++ } else { // not Loongson ++ // Not Loongson CPU ++ //tty->print_cr("MIPS platform"); ++ } ++ ++ if (features & CPU_LOONGSON_GS264) { ++ features |= CPU_LEXT1; ++ features |= CPU_LEXT2; ++ features |= CPU_TGTSYNC; ++ features |= CPU_ULSYNC; ++ features |= CPU_MSA1_0; ++ features |= CPU_LSX1; ++ } else if (features & CPU_LOONGSON_GS464) { ++ features |= CPU_LEXT1; ++ features |= CPU_LLSYNC; ++ features |= CPU_TGTSYNC; ++ } else if (features & CPU_LOONGSON_GS464E) { ++ features |= CPU_LEXT1; ++ features |= CPU_LEXT2; ++ features |= CPU_LEXT3; ++ features |= CPU_TGTSYNC; ++ features |= CPU_ULSYNC; ++ } else if (features & CPU_LOONGSON) { ++ // unknow loongson ++ features |= CPU_LLSYNC; ++ features |= CPU_TGTSYNC; ++ features |= CPU_ULSYNC; ++ } ++ VM_Version::_cpu_info_is_initialized = true; ++ ++ return features; ++} ++ ++void VM_Version::get_processor_features() { ++ ++ clean_cpuFeatures(); ++ ++ // test if cpucfg instruction is supported ++ VM_Version::_is_determine_cpucfg_supported_running = true; ++ __asm__ __volatile__( ++ ".insn \n\t" ++ ".word (0xc8080118)\n\t" // cpucfg zero, zero ++ : ++ : ++ : ++ ); ++ VM_Version::_is_determine_cpucfg_supported_running = false; ++ ++ if (supports_cpucfg()) { ++ get_cpu_info_stub(&_cpuid_info); ++ _cpuFeatures = get_feature_flags_by_cpucfg(); ++ // Only Loongson CPUs support cpucfg ++ _cpuFeatures |= CPU_LOONGSON; ++ } else { ++ _cpuFeatures = get_feature_flags_by_cpuinfo(0); ++ } ++ ++ _supports_cx8 = true; ++ ++ if (UseG1GC && FLAG_IS_DEFAULT(MaxGCPauseMillis)) { ++ FLAG_SET_CMDLINE(uintx, MaxGCPauseMillis, 650); ++ } ++ ++#ifdef COMPILER2 ++ if (MaxVectorSize > 0) { ++ if (!is_power_of_2(MaxVectorSize)) { ++ warning("MaxVectorSize must be a power of 2"); ++ MaxVectorSize = 8; ++ } ++ if (MaxVectorSize > 0 && supports_ps()) { ++ MaxVectorSize = 8; ++ } else { ++ MaxVectorSize = 0; ++ } ++ } ++ // ++ // Vector optimization of MIPS works in most cases, but cannot pass hotspot/test/compiler/6340864/TestFloatVect.java. ++ // Vector optimization was closed by default. ++ // The reasons: ++ // 1. The kernel does not have emulation of PS instructions yet, so the emulation of PS instructions must be done in JVM, see JVM_handle_linux_signal. ++ // 2. It seems the gcc4.4.7 had some bug related to ucontext_t, which is used in signal handler to emulate PS instructions. ++ // ++ if (FLAG_IS_DEFAULT(MaxVectorSize)) { ++ MaxVectorSize = 0; ++ } ++ ++#endif ++ ++ if (needs_llsync() && needs_tgtsync() && !needs_ulsync()) { ++ if (FLAG_IS_DEFAULT(UseSyncLevel)) { ++ FLAG_SET_DEFAULT(UseSyncLevel, 1000); ++ } ++ } else if (!needs_llsync() && needs_tgtsync() && needs_ulsync()) { ++ if (FLAG_IS_DEFAULT(UseSyncLevel)) { ++ FLAG_SET_DEFAULT(UseSyncLevel, 2000); ++ } ++ } else if (!needs_llsync() && !needs_tgtsync() && needs_ulsync()) { ++ if (FLAG_IS_DEFAULT(UseSyncLevel)) { ++ FLAG_SET_DEFAULT(UseSyncLevel, 3000); ++ } ++ } else if (needs_llsync() && !needs_tgtsync() && needs_ulsync()) { ++ if (FLAG_IS_DEFAULT(UseSyncLevel)) { ++ FLAG_SET_DEFAULT(UseSyncLevel, 4000); ++ } ++ } else if (needs_llsync() && needs_tgtsync() && needs_ulsync()) { ++ if (FLAG_IS_DEFAULT(UseSyncLevel)) { ++ FLAG_SET_DEFAULT(UseSyncLevel, 10000); ++ } ++ } else { ++ assert(false, "Should Not Reach Here, what is the cpu type?"); ++ if (FLAG_IS_DEFAULT(UseSyncLevel)) { ++ FLAG_SET_DEFAULT(UseSyncLevel, 10000); ++ } ++ } ++ ++ if (supports_lext1()) { ++ if (FLAG_IS_DEFAULT(UseLEXT1)) { ++ FLAG_SET_DEFAULT(UseLEXT1, true); ++ } ++ } else if (UseLEXT1) { ++ warning("LEXT1 instructions are not available on this CPU"); ++ FLAG_SET_DEFAULT(UseLEXT1, false); ++ } ++ ++ if (supports_lext2()) { ++ if (FLAG_IS_DEFAULT(UseLEXT2)) { ++ FLAG_SET_DEFAULT(UseLEXT2, true); ++ } ++ } else if (UseLEXT2) { ++ warning("LEXT2 instructions are not available on this CPU"); ++ FLAG_SET_DEFAULT(UseLEXT2, false); ++ } ++ ++ if (supports_lext3()) { ++ if (FLAG_IS_DEFAULT(UseLEXT3)) { ++ FLAG_SET_DEFAULT(UseLEXT3, true); ++ } ++ } else if (UseLEXT3) { ++ warning("LEXT3 instructions are not available on this CPU"); ++ FLAG_SET_DEFAULT(UseLEXT3, false); ++ } ++ ++ if (UseLEXT2) { ++ if (FLAG_IS_DEFAULT(UseCountTrailingZerosInstructionMIPS64)) { ++ FLAG_SET_DEFAULT(UseCountTrailingZerosInstructionMIPS64, 1); ++ } ++ } else if (UseCountTrailingZerosInstructionMIPS64) { ++ if (!FLAG_IS_DEFAULT(UseCountTrailingZerosInstructionMIPS64)) ++ warning("ctz/dctz instructions are not available on this CPU"); ++ FLAG_SET_DEFAULT(UseCountTrailingZerosInstructionMIPS64, 0); ++ } ++ ++ if (TieredCompilation) { ++ if (!FLAG_IS_DEFAULT(TieredCompilation)) ++ warning("TieredCompilation not supported"); ++ FLAG_SET_DEFAULT(TieredCompilation, false); ++ } ++ ++ char buf[256]; ++ bool is_unknown_loongson_cpu = is_loongson() && !is_gs464() && !is_gs464e() && !is_gs264() && !supports_cpucfg(); ++ ++ // A note on the _features_string format: ++ // There are jtreg tests checking the _features_string for various properties. ++ // For some strange reason, these tests require the string to contain ++ // only _lowercase_ characters. Keep that in mind when being surprised ++ // about the unusual notation of features - and when adding new ones. ++ // Features may have one comma at the end. ++ // Furthermore, use one, and only one, separator space between features. ++ // Multiple spaces are considered separate tokens, messing up everything. ++ jio_snprintf(buf, sizeof(buf), "%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s, usesynclevel:%d", ++ (is_loongson() ? "mips-compatible loongson cpu" : "mips cpu"), ++ (is_gs464() ? ", gs464 (3a1000/3b1500)" : ""), ++ (is_gs464e() ? ", gs464e (3a2000/3a3000/3b2000/3b3000)" : ""), ++ (is_gs264() ? ", gs264 (2k1000)" : ""), ++ (is_unknown_loongson_cpu ? ", unknown loongson cpu" : ""), ++ (supports_dsp() ? ", dsp" : ""), ++ (supports_ps() ? ", ps" : ""), ++ (supports_3d() ? ", 3d" : ""), ++ (supports_mmi() ? ", mmi" : ""), ++ (supports_msa1_0() ? ", msa1_0" : ""), ++ (supports_msa2_0() ? ", msa2_0" : ""), ++ (supports_lsx1() ? ", lsx1" : ""), ++ (supports_lsx2() ? ", lsx2" : ""), ++ (supports_lasx() ? ", lasx" : ""), ++ (supports_lext1() ? ", lext1" : ""), ++ (supports_lext2() ? ", lext2" : ""), ++ (supports_lext3() ? ", lext3" : ""), ++ (supports_cgp() ? ", aes, crc, sha1, sha256, sha512" : ""), ++ (supports_lamo() ? ", lamo" : ""), ++ (supports_lpixu() ? ", lpixu" : ""), ++ (needs_llsync() ? ", llsync" : ""), ++ (needs_tgtsync() ? ", tgtsync": ""), ++ (needs_ulsync() ? ", ulsync": ""), ++ (supports_mualp() ? ", mualp" : ""), ++ UseSyncLevel); ++ _features_str = strdup(buf); ++ ++ if (FLAG_IS_DEFAULT(AllocatePrefetchStyle)) { ++ FLAG_SET_DEFAULT(AllocatePrefetchStyle, 1); ++ } ++ ++ if (FLAG_IS_DEFAULT(AllocatePrefetchLines)) { ++ FLAG_SET_DEFAULT(AllocatePrefetchLines, 1); ++ } ++ ++ if (FLAG_IS_DEFAULT(AllocatePrefetchStepSize)) { ++ FLAG_SET_DEFAULT(AllocatePrefetchStepSize, 64); ++ } ++ ++ if (FLAG_IS_DEFAULT(AllocatePrefetchDistance)) { ++ FLAG_SET_DEFAULT(AllocatePrefetchDistance, 64); ++ } ++ ++ if (FLAG_IS_DEFAULT(AllocateInstancePrefetchLines)) { ++ FLAG_SET_DEFAULT(AllocateInstancePrefetchLines, 1); ++ } ++ ++ if (UseSHA) { ++ warning("SHA instructions are not available on this CPU"); ++ FLAG_SET_DEFAULT(UseSHA, false); ++ } ++ ++ if (UseSHA1Intrinsics || UseSHA256Intrinsics || UseSHA512Intrinsics) { ++ warning("SHA intrinsics are not available on this CPU"); ++ FLAG_SET_DEFAULT(UseSHA1Intrinsics, false); ++ FLAG_SET_DEFAULT(UseSHA256Intrinsics, false); ++ FLAG_SET_DEFAULT(UseSHA512Intrinsics, false); ++ } ++ ++ if (UseAES) { ++ if (!FLAG_IS_DEFAULT(UseAES)) { ++ warning("AES instructions are not available on this CPU"); ++ FLAG_SET_DEFAULT(UseAES, false); ++ } ++ } ++ ++ if (UseCRC32Intrinsics) { ++ if (!FLAG_IS_DEFAULT(UseCRC32Intrinsics)) { ++ warning("CRC32Intrinsics instructions are not available on this CPU"); ++ FLAG_SET_DEFAULT(UseCRC32Intrinsics, false); ++ } ++ } ++ ++ if (UseAESIntrinsics) { ++ if (!FLAG_IS_DEFAULT(UseAESIntrinsics)) { ++ warning("AES intrinsics are not available on this CPU"); ++ FLAG_SET_DEFAULT(UseAESIntrinsics, false); ++ } ++ } ++ ++ if (FLAG_IS_DEFAULT(UseMontgomeryMultiplyIntrinsic)) { ++ UseMontgomeryMultiplyIntrinsic = true; ++ } ++ if (FLAG_IS_DEFAULT(UseMontgomerySquareIntrinsic)) { ++ UseMontgomerySquareIntrinsic = true; ++ } ++ ++} ++ ++void VM_Version::initialize() { ++ ResourceMark rm; ++ // Making this stub must be FIRST use of assembler ++ ++ stub_blob = BufferBlob::create("get_cpu_info_stub", stub_size); ++ if (stub_blob == NULL) { ++ vm_exit_during_initialization("Unable to allocate get_cpu_info_stub"); ++ } ++ CodeBuffer c(stub_blob); ++ VM_Version_StubGenerator g(&c); ++ get_cpu_info_stub = CAST_TO_FN_PTR(get_cpu_info_stub_t, ++ g.generate_get_cpu_info()); ++ ++ get_processor_features(); ++} +diff --git a/hotspot/src/cpu/mips/vm/vm_version_mips.hpp b/hotspot/src/cpu/mips/vm/vm_version_mips.hpp +new file mode 100644 +index 0000000000..0de01e5f64 +--- /dev/null ++++ b/hotspot/src/cpu/mips/vm/vm_version_mips.hpp +@@ -0,0 +1,221 @@ ++/* ++ * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2015, 2019, Loongson Technology. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ * ++ */ ++ ++#ifndef CPU_MIPS_VM_VM_VERSION_MIPS_HPP ++#define CPU_MIPS_VM_VM_VERSION_MIPS_HPP ++ ++#include "runtime/globals_extension.hpp" ++#include "runtime/vm_version.hpp" ++ ++ ++class VM_Version: public Abstract_VM_Version { ++public: ++ ++ union Loongson_Cpucfg_Id1 { ++ uint32_t value; ++ struct { ++ uint32_t FP : 1, ++ FPREV : 3, ++ MMI : 1, ++ MSA1 : 1, ++ MSA2 : 1, ++ CGP : 1, ++ WRP : 1, ++ LSX1 : 1, ++ LSX2 : 1, ++ LASX : 1, ++ R6FXP : 1, ++ R6CRCP : 1, ++ R6FPP : 1, ++ CNT64 : 1, ++ LSLDR0 : 1, ++ LSPREF : 1, ++ LSPREFX : 1, ++ LSSYNCI : 1, ++ LSUCA : 1, ++ LLSYNC : 1, ++ TGTSYNC : 1, ++ LLEXC : 1, ++ SCRAND : 1, ++ MUALP : 1, ++ KMUALEn : 1, ++ ITLBT : 1, ++ LSUPERF : 1, ++ SFBP : 1, ++ CDMAP : 1, ++ : 1; ++ } bits; ++ }; ++ ++ union Loongson_Cpucfg_Id2 { ++ uint32_t value; ++ struct { ++ uint32_t LEXT1 : 1, ++ LEXT2 : 1, ++ LEXT3 : 1, ++ LSPW : 1, ++ LBT1 : 1, ++ LBT2 : 1, ++ LBT3 : 1, ++ LBTMMU : 1, ++ LPMP : 1, ++ LPMRev : 3, ++ LAMO : 1, ++ LPIXU : 1, ++ LPIXNU : 1, ++ LVZP : 1, ++ LVZRev : 3, ++ LGFTP : 1, ++ LGFTRev : 3, ++ LLFTP : 1, ++ LLFTRev : 3, ++ LCSRP : 1, ++ DISBLKLY : 1, ++ : 3; ++ } bits; ++ }; ++ ++protected: ++ ++ enum { ++ CPU_LOONGSON = (1 << 1), ++ CPU_LOONGSON_GS464 = (1 << 2), ++ CPU_LOONGSON_GS464E = (1 << 3), ++ CPU_LOONGSON_GS264 = (1 << 4), ++ CPU_MMI = (1 << 11), ++ CPU_MSA1_0 = (1 << 12), ++ CPU_MSA2_0 = (1 << 13), ++ CPU_CGP = (1 << 14), ++ CPU_LSX1 = (1 << 15), ++ CPU_LSX2 = (1 << 16), ++ CPU_LASX = (1 << 17), ++ CPU_LEXT1 = (1 << 18), ++ CPU_LEXT2 = (1 << 19), ++ CPU_LEXT3 = (1 << 20), ++ CPU_LAMO = (1 << 21), ++ CPU_LPIXU = (1 << 22), ++ CPU_LLSYNC = (1 << 23), ++ CPU_TGTSYNC = (1 << 24), ++ CPU_ULSYNC = (1 << 25), ++ CPU_MUALP = (1 << 26), ++ ++ //////////////////////add some other feature here////////////////// ++ } cpuFeatureFlags; ++ ++ enum Loongson_Family { ++ L_3A1000 = 0, ++ L_3B1500 = 1, ++ L_3A2000 = 2, ++ L_3B2000 = 3, ++ L_3A3000 = 4, ++ L_3B3000 = 5, ++ L_2K1000 = 6, ++ L_UNKNOWN = 7 ++ }; ++ ++ struct Loongson_Cpuinfo { ++ Loongson_Family id; ++ const char* const match_str; ++ }; ++ ++ static int _cpuFeatures; ++ static const char* _features_str; ++ static volatile bool _is_determine_cpucfg_supported_running; ++ static bool _is_cpucfg_instruction_supported; ++ static bool _cpu_info_is_initialized; ++ ++ struct CpuidInfo { ++ uint32_t cpucfg_info_id0; ++ Loongson_Cpucfg_Id1 cpucfg_info_id1; ++ Loongson_Cpucfg_Id2 cpucfg_info_id2; ++ uint32_t cpucfg_info_id3; ++ uint32_t cpucfg_info_id4; ++ uint32_t cpucfg_info_id5; ++ uint32_t cpucfg_info_id6; ++ uint32_t cpucfg_info_id8; ++ }; ++ ++ // The actual cpuid info block ++ static CpuidInfo _cpuid_info; ++ ++ static uint32_t get_feature_flags_by_cpucfg(); ++ static int get_feature_flags_by_cpuinfo(int features); ++ static void get_processor_features(); ++ ++public: ++ // Offsets for cpuid asm stub ++ static ByteSize Loongson_Cpucfg_id0_offset() { return byte_offset_of(CpuidInfo, cpucfg_info_id0); } ++ static ByteSize Loongson_Cpucfg_id1_offset() { return byte_offset_of(CpuidInfo, cpucfg_info_id1); } ++ static ByteSize Loongson_Cpucfg_id2_offset() { return byte_offset_of(CpuidInfo, cpucfg_info_id2); } ++ static ByteSize Loongson_Cpucfg_id3_offset() { return byte_offset_of(CpuidInfo, cpucfg_info_id3); } ++ static ByteSize Loongson_Cpucfg_id4_offset() { return byte_offset_of(CpuidInfo, cpucfg_info_id4); } ++ static ByteSize Loongson_Cpucfg_id5_offset() { return byte_offset_of(CpuidInfo, cpucfg_info_id5); } ++ static ByteSize Loongson_Cpucfg_id6_offset() { return byte_offset_of(CpuidInfo, cpucfg_info_id6); } ++ static ByteSize Loongson_Cpucfg_id8_offset() { return byte_offset_of(CpuidInfo, cpucfg_info_id8); } ++ ++ static bool is_determine_features_test_running() { return _is_determine_cpucfg_supported_running; } ++ ++ static void clean_cpuFeatures() { _cpuFeatures = 0; } ++ ++ // Initialization ++ static void initialize(); ++ ++ static bool cpu_info_is_initialized() { return _cpu_info_is_initialized; } ++ ++ static bool supports_cpucfg() { return _is_cpucfg_instruction_supported; } ++ static bool set_supports_cpucfg(bool value) { return _is_cpucfg_instruction_supported = value; } ++ ++ static bool is_loongson() { return _cpuFeatures & CPU_LOONGSON; } ++ static bool is_gs264() { return _cpuFeatures & CPU_LOONGSON_GS264; } ++ static bool is_gs464() { return _cpuFeatures & CPU_LOONGSON_GS464; } ++ static bool is_gs464e() { return _cpuFeatures & CPU_LOONGSON_GS464E; } ++ static bool supports_dsp() { return 0; /*not supported yet*/} ++ static bool supports_ps() { return 0; /*not supported yet*/} ++ static bool supports_3d() { return 0; /*not supported yet*/} ++ static bool supports_msa1_0() { return _cpuFeatures & CPU_MSA1_0; } ++ static bool supports_msa2_0() { return _cpuFeatures & CPU_MSA2_0; } ++ static bool supports_cgp() { return _cpuFeatures & CPU_CGP; } ++ static bool supports_mmi() { return _cpuFeatures & CPU_MMI; } ++ static bool supports_lsx1() { return _cpuFeatures & CPU_LSX1; } ++ static bool supports_lsx2() { return _cpuFeatures & CPU_LSX2; } ++ static bool supports_lasx() { return _cpuFeatures & CPU_LASX; } ++ static bool supports_lext1() { return _cpuFeatures & CPU_LEXT1; } ++ static bool supports_lext2() { return _cpuFeatures & CPU_LEXT2; } ++ static bool supports_lext3() { return _cpuFeatures & CPU_LEXT3; } ++ static bool supports_lamo() { return _cpuFeatures & CPU_LAMO; } ++ static bool supports_lpixu() { return _cpuFeatures & CPU_LPIXU; } ++ static bool needs_llsync() { return _cpuFeatures & CPU_LLSYNC; } ++ static bool needs_tgtsync() { return _cpuFeatures & CPU_TGTSYNC; } ++ static bool needs_ulsync() { return _cpuFeatures & CPU_ULSYNC; } ++ static bool supports_mualp() { return _cpuFeatures & CPU_MUALP; } ++ ++ //mips has no such instructions, use ll/sc instead ++ static bool supports_compare_and_exchange() { return false; } ++ ++ static const char* cpu_features() { return _features_str; } ++ ++}; ++ ++#endif // CPU_MIPS_VM_VM_VERSION_MIPS_HPP +diff --git a/hotspot/src/cpu/mips/vm/vmreg_mips.cpp b/hotspot/src/cpu/mips/vm/vmreg_mips.cpp +new file mode 100644 +index 0000000000..86bd74d430 +--- /dev/null ++++ b/hotspot/src/cpu/mips/vm/vmreg_mips.cpp +@@ -0,0 +1,51 @@ ++/* ++ * Copyright (c) 2006, 2012, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2015, 2022, Loongson Technology. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ * ++ */ ++ ++#include "precompiled.hpp" ++#include "asm/assembler.hpp" ++#include "code/vmreg.hpp" ++ ++ ++ ++void VMRegImpl::set_regName() { ++ Register reg = ::as_Register(0); ++ int i; ++ for (i = 0; i < ConcreteRegisterImpl::max_gpr ; ) { ++ regName[i++] = reg->name(); ++ regName[i++] = reg->name(); ++ reg = reg->successor(); ++ } ++ ++ FloatRegister freg = ::as_FloatRegister(0); ++ for ( ; i < ConcreteRegisterImpl::max_fpr ; ) { ++ regName[i++] = freg->name(); ++ regName[i++] = freg->name(); ++ freg = freg->successor(); ++ } ++ ++ for ( ; i < ConcreteRegisterImpl::number_of_registers ; i ++ ) { ++ regName[i] = "NON-GPR-FPR"; ++ } ++} +diff --git a/hotspot/src/cpu/mips/vm/vmreg_mips.hpp b/hotspot/src/cpu/mips/vm/vmreg_mips.hpp +new file mode 100644 +index 0000000000..6a970ea91a +--- /dev/null ++++ b/hotspot/src/cpu/mips/vm/vmreg_mips.hpp +@@ -0,0 +1,35 @@ ++/* ++ * Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2015, 2019, Loongson Technology. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ * ++ */ ++ ++#ifndef CPU_MIPS_VM_VMREG_MIPS_HPP ++#define CPU_MIPS_VM_VMREG_MIPS_HPP ++ ++bool is_Register(); ++Register as_Register(); ++ ++bool is_FloatRegister(); ++FloatRegister as_FloatRegister(); ++ ++#endif // CPU_MIPS_VM_VMREG_MIPS_HPP +diff --git a/hotspot/src/cpu/mips/vm/vmreg_mips.inline.hpp b/hotspot/src/cpu/mips/vm/vmreg_mips.inline.hpp +new file mode 100644 +index 0000000000..77e18ce57d +--- /dev/null ++++ b/hotspot/src/cpu/mips/vm/vmreg_mips.inline.hpp +@@ -0,0 +1,68 @@ ++/* ++ * Copyright (c) 2006, 2012, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2015, 2022, Loongson Technology. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ * ++ */ ++ ++#ifndef CPU_MIPS_VM_VMREG_MIPS_INLINE_HPP ++#define CPU_MIPS_VM_VMREG_MIPS_INLINE_HPP ++ ++inline VMReg RegisterImpl::as_VMReg() { ++ if( this==noreg ) return VMRegImpl::Bad(); ++ return VMRegImpl::as_VMReg(encoding() << 1 ); ++} ++ ++inline VMReg FloatRegisterImpl::as_VMReg() { ++ return VMRegImpl::as_VMReg((encoding() << 1) + ConcreteRegisterImpl::max_gpr); ++} ++ ++inline bool VMRegImpl::is_Register() { ++ return (unsigned int) value() < (unsigned int) ConcreteRegisterImpl::max_gpr; ++} ++ ++inline bool VMRegImpl::is_FloatRegister() { ++ return value() >= ConcreteRegisterImpl::max_gpr && value() < ConcreteRegisterImpl::max_fpr; ++} ++ ++inline Register VMRegImpl::as_Register() { ++ ++ assert( is_Register(), "must be"); ++ // Yuk ++ return ::as_Register(value() >> 1); ++} ++ ++inline FloatRegister VMRegImpl::as_FloatRegister() { ++ assert( is_FloatRegister(), "must be" ); ++ // Yuk ++ assert( is_even(value()), "must be" ); ++ return ::as_FloatRegister((value() - ConcreteRegisterImpl::max_gpr) >> 1); ++} ++ ++inline bool VMRegImpl::is_concrete() { ++ assert(is_reg(), "must be"); ++ if(is_Register()) return true; ++ if(is_FloatRegister()) return true; ++ assert(false, "what register?"); ++ return false; ++} ++ ++#endif // CPU_MIPS_VM_VMREG_MIPS_INLINE_HPP +diff --git a/hotspot/src/cpu/mips/vm/vtableStubs_mips_64.cpp b/hotspot/src/cpu/mips/vm/vtableStubs_mips_64.cpp +new file mode 100644 +index 0000000000..7779c58e0a +--- /dev/null ++++ b/hotspot/src/cpu/mips/vm/vtableStubs_mips_64.cpp +@@ -0,0 +1,301 @@ ++/* ++ * Copyright (c) 2003, 2014, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2015, 2021, Loongson Technology. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ * ++ */ ++ ++#include "precompiled.hpp" ++#include "asm/macroAssembler.hpp" ++#include "code/vtableStubs.hpp" ++#include "interp_masm_mips_64.hpp" ++#include "memory/resourceArea.hpp" ++#include "oops/compiledICHolder.hpp" ++#include "oops/klassVtable.hpp" ++#include "runtime/sharedRuntime.hpp" ++#include "vmreg_mips.inline.hpp" ++#ifdef COMPILER2 ++#include "opto/runtime.hpp" ++#endif ++ ++ ++// machine-dependent part of VtableStubs: create VtableStub of correct size and ++// initialize its code ++ ++#define __ masm-> ++ ++#define A0 RA0 ++#define A1 RA1 ++#define A2 RA2 ++#define A3 RA3 ++#define A4 RA4 ++#define A5 RA5 ++#define A6 RA6 ++#define A7 RA7 ++#define T0 RT0 ++#define T1 RT1 ++#define T2 RT2 ++#define T3 RT3 ++#define T8 RT8 ++#define T9 RT9 ++ ++#ifndef PRODUCT ++extern "C" void bad_compiled_vtable_index(JavaThread* thread, ++ oop receiver, ++ int index); ++#endif ++ ++// used by compiler only; reciever in T0. ++// used registers : ++// Rmethod : receiver klass & method ++// NOTE: If this code is used by the C1, the receiver_location is always 0. ++// when reach here, receiver in T0, klass in T8 ++VtableStub* VtableStubs::create_vtable_stub(int vtable_index) { ++ const int gs2_code_length = VtableStub::pd_code_size_limit(true); ++ VtableStub* s = new(gs2_code_length) VtableStub(true, vtable_index); ++ ResourceMark rm; ++ CodeBuffer cb(s->entry_point(), gs2_code_length); ++ MacroAssembler* masm = new MacroAssembler(&cb); ++ Register t1 = T8, t2 = Rmethod; ++#ifndef PRODUCT ++ if (CountCompiledCalls) { ++ __ li(AT, SharedRuntime::nof_megamorphic_calls_addr()); ++ __ lw(t1, AT , 0); ++ __ addiu(t1, t1, 1); ++ __ sw(t1, AT,0); ++ } ++#endif ++ ++ // get receiver (need to skip return address on top of stack) ++ //assert(receiver_location == T0->as_VMReg(), "receiver expected in T0"); ++ ++ // get receiver klass ++ address npe_addr = __ pc(); ++ //add for compressedoops ++ __ load_klass(t1, T0); ++ // compute entry offset (in words) ++ int entry_offset = InstanceKlass::vtable_start_offset() + vtable_index*vtableEntry::size(); ++#ifndef PRODUCT ++ if (DebugVtables) { ++ Label L; ++ // check offset vs vtable length ++ __ lw(t2, t1, InstanceKlass::vtable_length_offset()*wordSize); ++ assert(Assembler::is_simm16(vtable_index*vtableEntry::size()), "change this code"); ++ __ move(AT, vtable_index*vtableEntry::size()); ++ __ slt(AT, AT, t2); ++ __ bne(AT, R0, L); ++ __ delayed()->nop(); ++ __ move(A2, vtable_index); ++ __ move(A1, A0); ++ __ call_VM(noreg, CAST_FROM_FN_PTR(address, bad_compiled_vtable_index), A1, A2); ++ __ bind(L); ++ } ++#endif // PRODUCT ++ // load methodOop and target address ++ const Register method = Rmethod; ++ int offset = entry_offset*wordSize + vtableEntry::method_offset_in_bytes(); ++ guarantee(Assembler::is_simm16(offset), "not a signed 16-bit int"); ++ __ ld_ptr(method, t1, offset); ++ if (DebugVtables) { ++ Label L; ++ __ beq(method, R0, L); ++ __ delayed()->nop(); ++ __ ld(AT, method,in_bytes(Method::from_compiled_offset())); ++ __ bne(AT, R0, L); ++ __ delayed()->nop(); ++ __ stop("Vtable entry is NULL"); ++ __ bind(L); ++ } ++ // T8: receiver klass ++ // T0: receiver ++ // Rmethod: methodOop ++ // T9: entry ++ address ame_addr = __ pc(); ++ __ ld_ptr(T9, method,in_bytes(Method::from_compiled_offset())); ++ __ jr(T9); ++ __ delayed()->nop(); ++ masm->flush(); ++ s->set_exception_points(npe_addr, ame_addr); ++ return s; ++} ++ ++ ++// used registers : ++// T1 T2 ++// when reach here, the receiver in T0, klass in T1 ++VtableStub* VtableStubs::create_itable_stub(int itable_index) { ++ // Note well: pd_code_size_limit is the absolute minimum we can get ++ // away with. If you add code here, bump the code stub size ++ // returned by pd_code_size_limit! ++ const int gs2_code_length = VtableStub::pd_code_size_limit(false); ++ VtableStub* s = new(gs2_code_length) VtableStub(false, itable_index); ++ ResourceMark rm; ++ CodeBuffer cb(s->entry_point(), gs2_code_length); ++ MacroAssembler* masm = new MacroAssembler(&cb); ++ // we T8,T9 as temparary register, they are free from register allocator ++ Register t1 = T8, t2 = T2; ++ // Entry arguments: ++ // T1: Interface ++ // T0: Receiver ++ ++#ifndef PRODUCT ++ if (CountCompiledCalls) { ++ __ li(AT, SharedRuntime::nof_megamorphic_calls_addr()); ++ __ lw(T8, AT, 0); ++ __ addiu(T8, T8,1); ++ __ sw(T8, AT, 0); ++ } ++#endif /* PRODUCT */ ++ const Register holder_klass_reg = T1; // declaring interface klass (DECC) ++ const Register resolved_klass_reg = Rmethod; // resolved interface klass (REFC) ++ const Register icholder_reg = T1; ++ __ ld_ptr(resolved_klass_reg, icholder_reg, CompiledICHolder::holder_klass_offset()); ++ __ ld_ptr(holder_klass_reg, icholder_reg, CompiledICHolder::holder_metadata_offset()); ++ ++ // get receiver klass (also an implicit null-check) ++ address npe_addr = __ pc(); ++ __ load_klass(t1, T0); ++ { ++ // x86 use lookup_interface_method, but lookup_interface_method does not work on MIPS. ++ const int base = InstanceKlass::vtable_start_offset() * wordSize; ++ assert(vtableEntry::size() * wordSize == 8, "adjust the scaling in the code below"); ++ assert(Assembler::is_simm16(base), "change this code"); ++ __ daddiu(t2, t1, base); ++ assert(Assembler::is_simm16(InstanceKlass::vtable_length_offset() * wordSize), "change this code"); ++ __ lw(AT, t1, InstanceKlass::vtable_length_offset() * wordSize); ++ __ dsll(AT, AT, Address::times_8); ++ __ daddu(t2, t2, AT); ++ if (HeapWordsPerLong > 1) { ++ __ round_to(t2, BytesPerLong); ++ } ++ ++ Label hit, entry; ++ assert(Assembler::is_simm16(itableOffsetEntry::size() * wordSize), "change this code"); ++ __ bind(entry); ++ ++#ifdef ASSERT ++ // Check that the entry is non-null ++ if (DebugVtables) { ++ Label L; ++ assert(Assembler::is_simm16(itableOffsetEntry::interface_offset_in_bytes()), "change this code"); ++ __ lw(AT, t1, itableOffsetEntry::interface_offset_in_bytes()); ++ __ bne(AT, R0, L); ++ __ delayed()->nop(); ++ __ stop("null entry point found in itable's offset table"); ++ __ bind(L); ++ } ++#endif ++ assert(Assembler::is_simm16(itableOffsetEntry::interface_offset_in_bytes()), "change this code"); ++ __ ld_ptr(AT, t2, itableOffsetEntry::interface_offset_in_bytes()); ++ __ bne(AT, resolved_klass_reg, entry); ++ __ delayed()->addiu(t2, t2, itableOffsetEntry::size() * wordSize); ++ ++ } ++ ++ // add for compressedoops ++ __ load_klass(t1, T0); ++ // compute itable entry offset (in words) ++ const int base = InstanceKlass::vtable_start_offset() * wordSize; ++ assert(vtableEntry::size() * wordSize == 8, "adjust the scaling in the code below"); ++ assert(Assembler::is_simm16(base), "change this code"); ++ __ daddiu(t2, t1, base); ++ assert(Assembler::is_simm16(InstanceKlass::vtable_length_offset() * wordSize), "change this code"); ++ __ lw(AT, t1, InstanceKlass::vtable_length_offset() * wordSize); ++ __ dsll(AT, AT, Address::times_8); ++ __ daddu(t2, t2, AT); ++ if (HeapWordsPerLong > 1) { ++ __ round_to(t2, BytesPerLong); ++ } ++ ++ Label hit, entry; ++ assert(Assembler::is_simm16(itableOffsetEntry::size() * wordSize), "change this code"); ++ __ bind(entry); ++ ++#ifdef ASSERT ++ // Check that the entry is non-null ++ if (DebugVtables) { ++ Label L; ++ assert(Assembler::is_simm16(itableOffsetEntry::interface_offset_in_bytes()), "change this code"); ++ __ lw(AT, t1, itableOffsetEntry::interface_offset_in_bytes()); ++ __ bne(AT, R0, L); ++ __ delayed()->nop(); ++ __ stop("null entry point found in itable's offset table"); ++ __ bind(L); ++ } ++#endif ++ assert(Assembler::is_simm16(itableOffsetEntry::interface_offset_in_bytes()), "change this code"); ++ __ ld_ptr(AT, t2, itableOffsetEntry::interface_offset_in_bytes()); ++ __ bne(AT, holder_klass_reg, entry); ++ __ delayed()->addiu(t2, t2, itableOffsetEntry::size() * wordSize); ++ ++ // We found a hit, move offset into T9 ++ __ ld_ptr(t2, t2, itableOffsetEntry::offset_offset_in_bytes() - itableOffsetEntry::size() * wordSize); ++ ++ // Compute itableMethodEntry. ++ const int method_offset = (itableMethodEntry::size() * wordSize * itable_index) + ++ itableMethodEntry::method_offset_in_bytes(); ++ ++ // Get methodOop and entrypoint for compiler ++ const Register method = Rmethod; ++ __ dsll(AT, t2, Address::times_1); ++ __ addu(AT, AT, t1); ++ guarantee(Assembler::is_simm16(method_offset), "not a signed 16-bit int"); ++ __ ld_ptr(method, AT, method_offset); ++ ++#ifdef ASSERT ++ if (DebugVtables) { ++ Label L1; ++ __ beq(method, R0, L1); ++ __ delayed()->nop(); ++ __ ld(AT, method,in_bytes(Method::from_compiled_offset())); ++ __ bne(AT, R0, L1); ++ __ delayed()->nop(); ++ __ stop("methodOop is null"); ++ __ bind(L1); ++ } ++#endif // ASSERT ++ ++ // Rmethod: methodOop ++ // T0: receiver ++ // T9: entry point ++ address ame_addr = __ pc(); ++ __ ld_ptr(T9, method,in_bytes(Method::from_compiled_offset())); ++ __ jr(T9); ++ __ delayed()->nop(); ++ masm->flush(); ++ s->set_exception_points(npe_addr, ame_addr); ++ return s; ++} ++ ++// NOTE : whenever you change the code above, dont forget to change the const here ++int VtableStub::pd_code_size_limit(bool is_vtable_stub) { ++ if (is_vtable_stub) { ++ return ( DebugVtables ? 600 : 28) + (CountCompiledCalls ? 24 : 0)+ ++ (UseCompressedOops ? 16 : 0); ++ } else { ++ return ( DebugVtables ? 636 : 152) + (CountCompiledCalls ? 24 : 0)+ ++ (UseCompressedOops ? 32 : 0); ++ } ++} ++ ++int VtableStub::pd_code_alignment() { ++ return wordSize; ++} +diff --git a/hotspot/src/cpu/x86/vm/c1_LIRAssembler_x86.cpp b/hotspot/src/cpu/x86/vm/c1_LIRAssembler_x86.cpp +index c1c053e66c..5c90df1079 100644 +--- a/hotspot/src/cpu/x86/vm/c1_LIRAssembler_x86.cpp ++++ b/hotspot/src/cpu/x86/vm/c1_LIRAssembler_x86.cpp +@@ -1513,6 +1513,10 @@ void LIR_Assembler::emit_opBranch(LIR_OpBranch* op) { + } + } + ++void LIR_Assembler::emit_opCmpBranch(LIR_OpCmpBranch* op) { ++ ShouldNotReachHere(); ++} ++ + void LIR_Assembler::emit_opConvert(LIR_OpConvert* op) { + LIR_Opr src = op->in_opr(); + LIR_Opr dest = op->result_opr(); +@@ -2102,6 +2106,12 @@ void LIR_Assembler::cmove(LIR_Condition condition, LIR_Opr opr1, LIR_Opr opr2, L + } + + ++void LIR_Assembler::cmp_cmove(LIR_Condition condition, LIR_Opr left, LIR_Opr right, ++ LIR_Opr src1, LIR_Opr src2, LIR_Opr result, BasicType type) { ++ ShouldNotReachHere(); ++} ++ ++ + void LIR_Assembler::arith_op(LIR_Code code, LIR_Opr left, LIR_Opr right, LIR_Opr dest, CodeEmitInfo* info, bool pop_fpu_stack) { + assert(info == NULL, "should never be used, idiv/irem and ldiv/lrem not handled by this method"); + +diff --git a/hotspot/src/cpu/x86/vm/c1_LIRGenerator_x86.cpp b/hotspot/src/cpu/x86/vm/c1_LIRGenerator_x86.cpp +index 92b73e1c71..45da327efb 100644 +--- a/hotspot/src/cpu/x86/vm/c1_LIRGenerator_x86.cpp ++++ b/hotspot/src/cpu/x86/vm/c1_LIRGenerator_x86.cpp +@@ -242,20 +242,27 @@ void LIRGenerator::increment_counter(LIR_Address* addr, int step) { + __ add((LIR_Opr)addr, LIR_OprFact::intConst(step), (LIR_Opr)addr); + } + +-void LIRGenerator::cmp_mem_int(LIR_Condition condition, LIR_Opr base, int disp, int c, CodeEmitInfo* info) { ++template ++void LIRGenerator::cmp_mem_int_branch(LIR_Condition condition, LIR_Opr base, int disp, int c, T tgt, CodeEmitInfo* info) { + __ cmp_mem_int(condition, base, disp, c, info); ++ __ branch(condition, T_INT, tgt); + } + ++// Explicit instantiation for all supported types. ++template void LIRGenerator::cmp_mem_int_branch(LIR_Condition, LIR_Opr, int, int, Label*, CodeEmitInfo*); ++template void LIRGenerator::cmp_mem_int_branch(LIR_Condition, LIR_Opr, int, int, BlockBegin*, CodeEmitInfo*); ++template void LIRGenerator::cmp_mem_int_branch(LIR_Condition, LIR_Opr, int, int, CodeStub*, CodeEmitInfo*); + +-void LIRGenerator::cmp_reg_mem(LIR_Condition condition, LIR_Opr reg, LIR_Opr base, int disp, BasicType type, CodeEmitInfo* info) { +- __ cmp_reg_mem(condition, reg, new LIR_Address(base, disp, type), info); +-} +- +- +-void LIRGenerator::cmp_reg_mem(LIR_Condition condition, LIR_Opr reg, LIR_Opr base, LIR_Opr disp, BasicType type, CodeEmitInfo* info) { ++template ++void LIRGenerator::cmp_reg_mem_branch(LIR_Condition condition, LIR_Opr reg, LIR_Opr base, int disp, BasicType type, T tgt, CodeEmitInfo* info) { + __ cmp_reg_mem(condition, reg, new LIR_Address(base, disp, type), info); ++ __ branch(condition, type, tgt); + } + ++// Explicit instantiation for all supported types. ++template void LIRGenerator::cmp_reg_mem_branch(LIR_Condition, LIR_Opr, LIR_Opr, int, BasicType, Label*, CodeEmitInfo*); ++template void LIRGenerator::cmp_reg_mem_branch(LIR_Condition, LIR_Opr, LIR_Opr, int, BasicType, BlockBegin*, CodeEmitInfo*); ++template void LIRGenerator::cmp_reg_mem_branch(LIR_Condition, LIR_Opr, LIR_Opr, int, BasicType, CodeStub*, CodeEmitInfo*); + + bool LIRGenerator::strength_reduce_multiply(LIR_Opr left, jint c, LIR_Opr result, LIR_Opr tmp) { + if (tmp->is_valid() && c > 0 && c < max_jint) { +diff --git a/hotspot/src/os/linux/vm/os_linux.cpp b/hotspot/src/os/linux/vm/os_linux.cpp +index 2d3880b363..be40c43917 100644 +--- a/hotspot/src/os/linux/vm/os_linux.cpp ++++ b/hotspot/src/os/linux/vm/os_linux.cpp +@@ -22,6 +22,12 @@ + * + */ + ++/* ++ * This file has been modified by Loongson Technology in 2022. These ++ * modifications are Copyright (c) 2021, 2022, Loongson Technology, and are made ++ * available on the same license terms set forth above. ++ */ ++ + // no precompiled headers + #include "classfile/classLoader.hpp" + #include "classfile/systemDictionary.hpp" +@@ -1973,7 +1979,11 @@ void * os::dll_load(const char *filename, char *ebuf, int ebuflen) + {EM_ALPHA, EM_ALPHA, ELFCLASS64, ELFDATA2LSB, (char*)"Alpha"}, + {EM_MIPS_RS3_LE, EM_MIPS_RS3_LE, ELFCLASS32, ELFDATA2LSB, (char*)"MIPSel"}, + {EM_MIPS, EM_MIPS, ELFCLASS32, ELFDATA2MSB, (char*)"MIPS"}, ++ {EM_MIPS, EM_MIPS, ELFCLASS64, ELFDATA2LSB, (char*)"MIPS64 LE"}, + {EM_PARISC, EM_PARISC, ELFCLASS32, ELFDATA2MSB, (char*)"PARISC"}, ++#if defined (LOONGARCH64) ++ {EM_LOONGARCH, EM_LOONGARCH, ELFCLASS64, ELFDATA2LSB, (char*)"LOONGARCH64"}, ++#endif + {EM_68K, EM_68K, ELFCLASS32, ELFDATA2MSB, (char*)"M68k"}, + {EM_AARCH64, EM_AARCH64, ELFCLASS64, ELFDATA2LSB, (char*)"AARCH64"}, + }; +@@ -1988,6 +1998,8 @@ void * os::dll_load(const char *filename, char *ebuf, int ebuflen) + static Elf32_Half running_arch_code=EM_SPARCV9; + #elif (defined __sparc) && (!defined _LP64) + static Elf32_Half running_arch_code=EM_SPARC; ++ #elif (defined MIPS64) ++ static Elf32_Half running_arch_code=EM_MIPS; + #elif (defined __powerpc64__) + static Elf32_Half running_arch_code=EM_PPC64; + #elif (defined __powerpc__) +@@ -2008,9 +2020,11 @@ void * os::dll_load(const char *filename, char *ebuf, int ebuflen) + static Elf32_Half running_arch_code=EM_68K; + #elif (defined AARCH64) + static Elf32_Half running_arch_code=EM_AARCH64; ++ #elif (defined LOONGARCH64) ++ static Elf32_Half running_arch_code=EM_LOONGARCH; + #else + #error Method os::dll_load requires that one of following is defined:\ +- IA32, AMD64, IA64, __sparc, __powerpc__, ARM, S390, ALPHA, MIPS, MIPSEL, PARISC, M68K, AARCH64 ++ IA32, AMD64, IA64, __sparc, __powerpc__, ARM, S390, ALPHA, MIPS, MIPSEL, __mips64, PARISC, M68K, AARCH64 + #endif + + // Identify compatability class for VM's architecture and library's architecture +@@ -3517,7 +3531,7 @@ size_t os::Linux::find_large_page_size() { + + #ifndef ZERO + large_page_size = IA32_ONLY(4 * M) AMD64_ONLY(2 * M) IA64_ONLY(256 * M) SPARC_ONLY(4 * M) +- ARM_ONLY(2 * M) PPC_ONLY(4 * M) AARCH64_ONLY(2 * M); ++ ARM_ONLY(2 * M) PPC_ONLY(4 * M) AARCH64_ONLY(2 * M) MIPS64_ONLY(4 * M) LOONGARCH64_ONLY(4 * M); //In MIPS _large_page_size is seted 4*M. // TODO: LA + #endif // ZERO + + FILE *fp = fopen("/proc/meminfo", "r"); +@@ -5124,7 +5138,12 @@ jint os::init_2(void) + Linux::fast_thread_clock_init(); + + // Allocate a single page and mark it as readable for safepoint polling ++#ifdef OPT_SAFEPOINT ++ void * p = (void *)(0x10000); ++ address polling_page = (address) ::mmap(p, Linux::page_size(), PROT_READ, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0); ++#else + address polling_page = (address) ::mmap(NULL, Linux::page_size(), PROT_READ, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0); ++#endif + guarantee( polling_page != MAP_FAILED, "os::init_2: failed to allocate polling page" ); + + os::set_polling_page( polling_page ); +@@ -5159,13 +5178,20 @@ jint os::init_2(void) + // size. Add a page for compiler2 recursion in main thread. + // Add in 2*BytesPerWord times page size to account for VM stack during + // class initialization depending on 32 or 64 bit VM. ++ ++ /* ++ * 2014/1/2: JDK8 requires larger -Xss option. ++ * Some application cannot run with -Xss192K. ++ * We are not sure whether this causes errors, so simply print a warning. ++ */ ++ size_t min_stack_allowed_jdk6 = os::Linux::min_stack_allowed; + os::Linux::min_stack_allowed = MAX2(os::Linux::min_stack_allowed, + (size_t)(StackYellowPages+StackRedPages+StackShadowPages) * Linux::page_size() + + (2*BytesPerWord COMPILER2_PRESENT(+1)) * Linux::vm_default_page_size()); + + size_t threadStackSizeInBytes = ThreadStackSize * K; + if (threadStackSizeInBytes != 0 && +- threadStackSizeInBytes < os::Linux::min_stack_allowed) { ++ threadStackSizeInBytes < min_stack_allowed_jdk6) { + tty->print_cr("\nThe stack size specified is too small, " + "Specify at least %dk", + os::Linux::min_stack_allowed/ K); +diff --git a/hotspot/src/os/linux/vm/os_perf_linux.cpp b/hotspot/src/os/linux/vm/os_perf_linux.cpp +index 0d1f75810a..cbc6c0757c 100644 +--- a/hotspot/src/os/linux/vm/os_perf_linux.cpp ++++ b/hotspot/src/os/linux/vm/os_perf_linux.cpp +@@ -50,6 +50,12 @@ + #ifdef TARGET_ARCH_ppc + # include "vm_version_ext_ppc.hpp" + #endif ++#ifdef TARGET_ARCH_mips ++# include "vm_version_ext_mips.hpp" ++#endif ++#ifdef TARGET_ARCH_loongarch ++# include "vm_version_ext_loongarch.hpp" ++#endif + + #include + #include +diff --git a/hotspot/src/os_cpu/linux_loongarch/vm/assembler_linux_loongarch.cpp b/hotspot/src/os_cpu/linux_loongarch/vm/assembler_linux_loongarch.cpp +new file mode 100644 +index 0000000000..5ee0965f42 +--- /dev/null ++++ b/hotspot/src/os_cpu/linux_loongarch/vm/assembler_linux_loongarch.cpp +@@ -0,0 +1,92 @@ ++/* ++ * Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2015, 2022, Loongson Technology. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ * ++ */ ++ ++#include "precompiled.hpp" ++#include "asm/macroAssembler.hpp" ++#include "asm/macroAssembler.inline.hpp" ++#include "runtime/os.hpp" ++#include "runtime/threadLocalStorage.hpp" ++ ++#define A0 RA0 ++#define A1 RA1 ++#define A2 RA2 ++#define A3 RA3 ++#define A4 RA4 ++#define A5 RA5 ++#define A6 RA6 ++#define A7 RA7 ++#define T0 RT0 ++#define T1 RT1 ++#define T2 RT2 ++#define T3 RT3 ++#define T4 RT4 ++#define T5 RT5 ++#define T6 RT6 ++#define T7 RT7 ++#define T8 RT8 ++ ++void MacroAssembler::get_thread(Register thread) { ++#ifdef MINIMIZE_RAM_USAGE ++ Register tmp; ++ ++ if (thread == AT) ++ tmp = T9; ++ else ++ tmp = AT; ++ ++ move(thread, SP); ++ shr(thread, PAGE_SHIFT); ++ ++ push(tmp); ++ li(tmp, ((1UL << (SP_BITLENGTH - PAGE_SHIFT)) - 1)); ++ andr(thread, thread, tmp); ++ shl(thread, Address::times_ptr); // sizeof(Thread *) ++ li48(tmp, (long)ThreadLocalStorage::sp_map_addr()); ++ add_d(tmp, tmp, thread); ++ ld_ptr(thread, tmp, 0); ++ pop(tmp); ++#else ++ if (thread != V0) { ++ push(V0); ++ } ++ pushad_except_v0(); ++ ++ li(A0, ThreadLocalStorage::thread_index()); ++ push(S5); ++ move(S5, SP); ++ li(AT, -StackAlignmentInBytes); ++ andr(SP, SP, AT); ++ // TODO: confirm reloc ++ call(CAST_FROM_FN_PTR(address, pthread_getspecific), relocInfo::runtime_call_type); ++ move(SP, S5); ++ pop(S5); ++ ++ popad_except_v0(); ++ if (thread != V0) { ++ move(thread, V0); ++ pop(V0); ++ } ++#endif // MINIMIZE_RAM_USAGE ++} +diff --git a/hotspot/src/os_cpu/linux_loongarch/vm/atomic_linux_loongarch.inline.hpp b/hotspot/src/os_cpu/linux_loongarch/vm/atomic_linux_loongarch.inline.hpp +new file mode 100644 +index 0000000000..7944618037 +--- /dev/null ++++ b/hotspot/src/os_cpu/linux_loongarch/vm/atomic_linux_loongarch.inline.hpp +@@ -0,0 +1,206 @@ ++/* ++ * Copyright (c) 1999, 2013, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2015, 2020, Loongson Technology. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ * ++ */ ++ ++#ifndef OS_CPU_LINUX_LOONGARCH_VM_ATOMIC_LINUX_LOONGARCH_INLINE_HPP ++#define OS_CPU_LINUX_LOONGARCH_VM_ATOMIC_LINUX_LOONGARCH_INLINE_HPP ++ ++#include "orderAccess_linux_loongarch.inline.hpp" ++#include "runtime/atomic.hpp" ++#include "runtime/os.hpp" ++#include "vm_version_loongarch.hpp" ++ ++// Implementation of class atomic ++ ++inline void Atomic::store (jbyte store_value, jbyte* dest) { *dest = store_value; } ++inline void Atomic::store (jshort store_value, jshort* dest) { *dest = store_value; } ++inline void Atomic::store (jint store_value, jint* dest) { *dest = store_value; } ++inline void Atomic::store (jlong store_value, jlong* dest) { *dest = store_value; } ++inline void Atomic::store_ptr(intptr_t store_value, intptr_t* dest) { *dest = store_value; } ++inline void Atomic::store_ptr(void* store_value, void* dest) { *(void**)dest = store_value; } ++ ++inline void Atomic::store (jbyte store_value, volatile jbyte* dest) { *dest = store_value; } ++inline void Atomic::store (jshort store_value, volatile jshort* dest) { *dest = store_value; } ++inline void Atomic::store (jint store_value, volatile jint* dest) { *dest = store_value; } ++inline void Atomic::store (jlong store_value, volatile jlong* dest) { *dest = store_value; } ++inline void Atomic::store_ptr(intptr_t store_value, volatile intptr_t* dest) { *dest = store_value; } ++inline void Atomic::store_ptr(void* store_value, volatile void* dest) { *(void**)dest = store_value; } ++ ++inline jlong Atomic::load (volatile jlong* src) { return *src; } ++ ++///////////implementation of Atomic::add*///////////////// ++inline jint Atomic::add (jint add_value, volatile jint* dest) { ++ //TODO LA opt amadd ++ jint __ret, __tmp; ++ __asm__ __volatile__ ( ++ "1: ll.w %[__ret], %[__dest] \n\t" ++ " add.w %[__tmp], %[__val], %[__ret] \n\t" ++ " sc.w %[__tmp], %[__dest] \n\t" ++ " beqz %[__tmp], 1b \n\t" ++ ++ : [__ret] "=&r" (__ret), [__tmp] "=&r" (__tmp) ++ : [__dest] "ZC" (*(volatile jint*)dest), [__val] "r" (add_value) ++ : "memory" ++ ); ++ ++ return add_value + __ret; ++} ++ ++inline intptr_t Atomic::add_ptr (intptr_t add_value, volatile intptr_t* dest) { ++ //TODO LA opt amadd ++ jint __ret, __tmp; ++ __asm__ __volatile__ ( ++ "1: ll.d %[__ret], %[__dest] \n\t" ++ " add.d %[__tmp], %[__val], %[__ret] \n\t" ++ " sc.d %[__tmp], %[__dest] \n\t" ++ " beqz %[__tmp], 1b \n\t" ++ ++ : [__ret] "=&r" (__ret), [__tmp] "=&r" (__tmp) ++ : [__dest] "ZC" (*(volatile jint*)dest), [__val] "r" (add_value) ++ : "memory" ++ ); ++ ++ return add_value + __ret; ++} ++ ++inline void* Atomic::add_ptr (intptr_t add_value, volatile void* dest) { ++ return (void*)add_ptr((intptr_t)add_value, (volatile intptr_t*)dest); ++} ++ ++///////////implementation of Atomic::inc*///////////////// ++inline void Atomic::inc (volatile jint* dest) { (void)add(1, dest); } ++inline void Atomic::inc_ptr (volatile intptr_t* dest) { (void)add_ptr(1, dest); } ++inline void Atomic::inc_ptr (volatile void* dest) { (void)inc_ptr((volatile intptr_t*)dest); } ++ ++///////////implementation of Atomic::dec*///////////////// ++inline void Atomic::dec (volatile jint* dest) { (void)add(-1, dest); } ++inline void Atomic::dec_ptr (volatile intptr_t* dest) { (void)add_ptr(-1, dest); } ++inline void Atomic::dec_ptr (volatile void* dest) { (void)dec_ptr((volatile intptr_t*)dest); } ++ ++ ++///////////implementation of Atomic::xchg*///////////////// ++inline jint Atomic::xchg (jint exchange_value, volatile jint* dest) { ++ jint __ret, __tmp; ++ ++ __asm__ __volatile__ ( ++ "1: ll.w %[__ret], %[__dest] \n\t" ++ " move %[__tmp], %[__val] \n\t" ++ " sc.w %[__tmp], %[__dest] \n\t" ++ " beqz %[__tmp], 1b \n\t" ++ ++ : [__ret] "=&r" (__ret), [__tmp] "=&r" (__tmp) ++ : [__dest] "ZC" (*(volatile jint*)dest), [__val] "r" (exchange_value) ++ : "memory" ++ ); ++ ++ return __ret; ++} ++ ++inline intptr_t Atomic::xchg_ptr(intptr_t exchange_value, volatile intptr_t* dest) { ++ intptr_t __ret, __tmp; ++ __asm__ __volatile__ ( ++ "1: ll.d %[__ret], %[__dest] \n\t" ++ " move %[__tmp], %[__val] \n\t" ++ " sc.d %[__tmp], %[__dest] \n\t" ++ " beqz %[__tmp], 1b \n\t" ++ ++ : [__ret] "=&r" (__ret), [__tmp] "=&r" (__tmp) ++ : [__dest] "ZC" (*(volatile intptr_t*)dest), [__val] "r" (exchange_value) ++ : "memory" ++ ); ++ return __ret; ++ ++} ++ ++inline void* Atomic::xchg_ptr(void* exchange_value, volatile void* dest) { ++ return (void*)xchg_ptr((intptr_t)exchange_value, (volatile intptr_t*)dest); ++} ++ ++///////////implementation of Atomic::cmpxchg*///////////////// ++inline jint Atomic::cmpxchg (jint exchange_value, volatile jint* dest, jint compare_value) { ++ jint __prev, __cmp; ++ ++ __asm__ __volatile__ ( ++ "1: ll.w %[__prev], %[__dest] \n\t" ++ " bne %[__prev], %[__old], 2f \n\t" ++ " move %[__cmp], $r0 \n\t" ++ " move %[__cmp], %[__new] \n\t" ++ " sc.w %[__cmp], %[__dest] \n\t" ++ " beqz %[__cmp], 1b \n\t" ++ "2: \n\t" ++ " dbar 0 \n\t" ++ ++ : [__prev] "=&r" (__prev), [__cmp] "=&r" (__cmp) ++ : [__dest] "ZC" (*(volatile jint*)dest), [__old] "r" (compare_value), [__new] "r" (exchange_value) ++ : "memory" ++ ); ++ ++ return __prev; ++} ++ ++inline jlong Atomic::cmpxchg (jlong exchange_value, volatile jlong* dest, jlong compare_value) { ++ jlong __prev, __cmp; ++ ++ __asm__ __volatile__ ( ++ "1: ll.d %[__prev], %[__dest] \n\t" ++ " bne %[__prev], %[__old], 2f \n\t" ++ " move %[__cmp], $r0 \n\t" ++ " move %[__cmp], %[__new] \n\t" ++ " sc.d %[__cmp], %[__dest] \n\t" ++ " beqz %[__cmp], 1b \n\t" ++ "2: \n\t" ++ " dbar 0 \n\t" ++ ++ : [__prev] "=&r" (__prev), [__cmp] "=&r" (__cmp) ++ : [__dest] "ZC" (*(volatile jlong*)dest), [__old] "r" (compare_value), [__new] "r" (exchange_value) ++ : "memory" ++ ); ++ return __prev; ++} ++ ++inline intptr_t Atomic::cmpxchg_ptr(intptr_t exchange_value, volatile intptr_t* dest, intptr_t compare_value) { ++ intptr_t __prev, __cmp; ++ __asm__ __volatile__ ( ++ "1: ll.d %[__prev], %[__dest] \n\t" ++ " bne %[__prev], %[__old], 2f \n\t" ++ " move %[__cmp], $r0 \n\t" ++ " move %[__cmp], %[__new] \n\t" ++ " sc.d %[__cmp], %[__dest] \n\t" ++ " beqz %[__cmp], 1b \n\t" ++ "2: \n\t" ++ " dbar 0 \n\t" ++ ++ : [__prev] "=&r" (__prev), [__cmp] "=&r" (__cmp) ++ : [__dest] "ZC" (*(volatile intptr_t*)dest), [__old] "r" (compare_value), [__new] "r" (exchange_value) ++ : "memory" ++ ); ++ ++ return __prev; ++} ++ ++inline void* Atomic::cmpxchg_ptr(void* exchange_value, volatile void* dest, void* compare_value) { ++ return (void*)cmpxchg_ptr((intptr_t)exchange_value, (volatile intptr_t*)dest, (intptr_t)compare_value); ++} ++ ++#endif // OS_CPU_LINUX_LOONGARCH_VM_ATOMIC_LINUX_LOONGARCH_INLINE_HPP +diff --git a/hotspot/src/os_cpu/linux_loongarch/vm/bytes_linux_loongarch.inline.hpp b/hotspot/src/os_cpu/linux_loongarch/vm/bytes_linux_loongarch.inline.hpp +new file mode 100644 +index 0000000000..4e205c468e +--- /dev/null ++++ b/hotspot/src/os_cpu/linux_loongarch/vm/bytes_linux_loongarch.inline.hpp +@@ -0,0 +1,37 @@ ++/* ++ * Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2015, 2020, Loongson Technology. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ * ++ */ ++ ++#ifndef OS_CPU_LINUX_LOONGARCH_VM_BYTES_LINUX_LOONGARCH_INLINE_HPP ++#define OS_CPU_LINUX_LOONGARCH_VM_BYTES_LINUX_LOONGARCH_INLINE_HPP ++ ++#include ++ ++// Efficient swapping of data bytes from Java byte ++// ordering to native byte ordering and vice versa. ++inline u2 Bytes::swap_u2(u2 x) { return bswap_16(x); } ++inline u4 Bytes::swap_u4(u4 x) { return bswap_32(x); } ++inline u8 Bytes::swap_u8(u8 x) { return bswap_64(x); } ++ ++#endif // OS_CPU_LINUX_LOONGARCH_VM_BYTES_LINUX_LOONGARCH_INLINE_HPP +diff --git a/hotspot/src/os_cpu/linux_loongarch/vm/copy_linux_loongarch.inline.hpp b/hotspot/src/os_cpu/linux_loongarch/vm/copy_linux_loongarch.inline.hpp +new file mode 100644 +index 0000000000..7d6e11a935 +--- /dev/null ++++ b/hotspot/src/os_cpu/linux_loongarch/vm/copy_linux_loongarch.inline.hpp +@@ -0,0 +1,125 @@ ++/* ++ * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2015, 2020, Loongson Technology. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ * ++ */ ++ ++#ifndef OS_CPU_LINUX_LOONGARCH_VM_COPY_LINUX_LOONGARCH_INLINE_HPP ++#define OS_CPU_LINUX_LOONGARCH_VM_COPY_LINUX_LOONGARCH_INLINE_HPP ++ ++static void pd_conjoint_words(HeapWord* from, HeapWord* to, size_t count) { ++ (void)memmove(to, from, count * HeapWordSize); ++} ++ ++static void pd_disjoint_words(HeapWord* from, HeapWord* to, size_t count) { ++ switch (count) { ++ case 8: to[7] = from[7]; ++ case 7: to[6] = from[6]; ++ case 6: to[5] = from[5]; ++ case 5: to[4] = from[4]; ++ case 4: to[3] = from[3]; ++ case 3: to[2] = from[2]; ++ case 2: to[1] = from[1]; ++ case 1: to[0] = from[0]; ++ case 0: break; ++ default: ++ (void)memcpy(to, from, count * HeapWordSize); ++ break; ++ } ++} ++ ++static void pd_disjoint_words_atomic(HeapWord* from, HeapWord* to, size_t count) { ++ switch (count) { ++ case 8: to[7] = from[7]; ++ case 7: to[6] = from[6]; ++ case 6: to[5] = from[5]; ++ case 5: to[4] = from[4]; ++ case 4: to[3] = from[3]; ++ case 3: to[2] = from[2]; ++ case 2: to[1] = from[1]; ++ case 1: to[0] = from[0]; ++ case 0: break; ++ default: ++ while (count-- > 0) { ++ *to++ = *from++; ++ } ++ break; ++ } ++} ++ ++static void pd_aligned_conjoint_words(HeapWord* from, HeapWord* to, size_t count) { ++ pd_conjoint_words(from, to, count); ++} ++ ++static void pd_aligned_disjoint_words(HeapWord* from, HeapWord* to, size_t count) { ++ pd_disjoint_words(from, to, count); ++} ++ ++static void pd_conjoint_bytes(void* from, void* to, size_t count) { ++ (void)memmove(to, from, count); ++} ++ ++static void pd_conjoint_bytes_atomic(void* from, void* to, size_t count) { ++ pd_conjoint_bytes(from, to, count); ++} ++ ++static void pd_conjoint_jshorts_atomic(jshort* from, jshort* to, size_t count) { ++ copy_conjoint_atomic(from, to, count); ++} ++ ++static void pd_conjoint_jints_atomic(jint* from, jint* to, size_t count) { ++ copy_conjoint_atomic(from, to, count); ++} ++ ++static void pd_conjoint_jlongs_atomic(jlong* from, jlong* to, size_t count) { ++ copy_conjoint_atomic(from, to, count); ++} ++ ++static void pd_conjoint_oops_atomic(oop* from, oop* to, size_t count) { ++ //assert(!UseCompressedOops, "foo!"); ++ assert(HeapWordSize == BytesPerOop, "heapwords and oops must be the same size"); ++ copy_conjoint_atomic(from, to, count); ++} ++ ++static void pd_arrayof_conjoint_bytes(HeapWord* from, HeapWord* to, size_t count) { ++ pd_conjoint_bytes_atomic(from, to, count); ++} ++ ++static void pd_arrayof_conjoint_jshorts(HeapWord* from, HeapWord* to, size_t count) { ++ pd_conjoint_jshorts_atomic((jshort*)from, (jshort*)to, count); ++} ++ ++static void pd_arrayof_conjoint_jints(HeapWord* from, HeapWord* to, size_t count) { ++ pd_conjoint_jints_atomic((jint*)from, (jint*)to, count); ++} ++ ++static void pd_arrayof_conjoint_jlongs(HeapWord* from, HeapWord* to, size_t count) { ++ pd_conjoint_jlongs_atomic((jlong*)from, (jlong*)to, count); ++} ++ ++static void pd_arrayof_conjoint_oops(HeapWord* from, HeapWord* to, size_t count) { ++ //assert(!UseCompressedOops, "foo!"); ++ assert(BytesPerLong == BytesPerOop, "jlongs and oops must be the same size"); ++ pd_conjoint_oops_atomic((oop*)from, (oop*)to, count); ++} ++ ++#endif // OS_CPU_LINUX_LOONGARCH_VM_COPY_LINUX_LOONGARCH_INLINE_HPP +diff --git a/hotspot/src/os_cpu/linux_loongarch/vm/globals_linux_loongarch.hpp b/hotspot/src/os_cpu/linux_loongarch/vm/globals_linux_loongarch.hpp +new file mode 100644 +index 0000000000..8ec3fa8239 +--- /dev/null ++++ b/hotspot/src/os_cpu/linux_loongarch/vm/globals_linux_loongarch.hpp +@@ -0,0 +1,43 @@ ++/* ++ * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2015, 2022, Loongson Technology. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ * ++ */ ++ ++#ifndef OS_CPU_LINUX_LOONGARCH_VM_GLOBALS_LINUX_LOONGARCH_HPP ++#define OS_CPU_LINUX_LOONGARCH_VM_GLOBALS_LINUX_LOONGARCH_HPP ++ ++// Sets the default values for platform dependent flags used by the runtime system. ++// (see globals.hpp) ++ ++define_pd_global(bool, DontYieldALot, false); ++define_pd_global(intx, ThreadStackSize, 2048); ++define_pd_global(intx, VMThreadStackSize, 2048); ++ ++define_pd_global(intx, CompilerThreadStackSize, 0); // 0 => use system default ++ ++define_pd_global(uintx,JVMInvokeMethodSlack, 8192); ++ ++// Used on 64 bit platforms for UseCompressedOops base address ++define_pd_global(uintx,HeapBaseMinAddress, 2*G); ++ ++#endif // OS_CPU_LINUX_LOONGARCH_VM_GLOBALS_LINUX_LOONGARCH_HPP +diff --git a/hotspot/src/os_cpu/linux_loongarch/vm/orderAccess_linux_loongarch.inline.hpp b/hotspot/src/os_cpu/linux_loongarch/vm/orderAccess_linux_loongarch.inline.hpp +new file mode 100644 +index 0000000000..0e1331ac90 +--- /dev/null ++++ b/hotspot/src/os_cpu/linux_loongarch/vm/orderAccess_linux_loongarch.inline.hpp +@@ -0,0 +1,115 @@ ++/* ++ * Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2015, 2022, Loongson Technology. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ * ++ */ ++ ++#ifndef OS_CPU_LINUX_LOONGARCH_VM_ORDERACCESS_LINUX_LOONGARCH_INLINE_HPP ++#define OS_CPU_LINUX_LOONGARCH_VM_ORDERACCESS_LINUX_LOONGARCH_INLINE_HPP ++ ++#include "runtime/atomic.hpp" ++#include "runtime/orderAccess.hpp" ++#include "runtime/os.hpp" ++#include "vm_version_loongarch.hpp" ++ ++#define inlasm_sync() if (os::is_ActiveCoresMP()) \ ++ __asm__ __volatile__ ("nop" : : : "memory"); \ ++ else \ ++ __asm__ __volatile__ ("dbar 0" : : : "memory"); ++ ++inline void OrderAccess::loadload() { inlasm_sync(); } ++inline void OrderAccess::storestore() { inlasm_sync(); } ++inline void OrderAccess::loadstore() { inlasm_sync(); } ++inline void OrderAccess::storeload() { inlasm_sync(); } ++ ++inline void OrderAccess::acquire() { inlasm_sync(); } ++inline void OrderAccess::release() { inlasm_sync(); } ++inline void OrderAccess::fence() { inlasm_sync(); } ++ ++//implementation of load_acquire ++inline jbyte OrderAccess::load_acquire(volatile jbyte* p) { jbyte data = *p; acquire(); return data; } ++inline jshort OrderAccess::load_acquire(volatile jshort* p) { jshort data = *p; acquire(); return data; } ++inline jint OrderAccess::load_acquire(volatile jint* p) { jint data = *p; acquire(); return data; } ++inline jlong OrderAccess::load_acquire(volatile jlong* p) { jlong tmp = *p; acquire(); return tmp; } ++inline jubyte OrderAccess::load_acquire(volatile jubyte* p) { jubyte data = *p; acquire(); return data; } ++inline jushort OrderAccess::load_acquire(volatile jushort* p) { jushort data = *p; acquire(); return data; } ++inline juint OrderAccess::load_acquire(volatile juint* p) { juint data = *p; acquire(); return data; } ++inline julong OrderAccess::load_acquire(volatile julong* p) { julong tmp = *p; acquire(); return tmp; } ++inline jfloat OrderAccess::load_acquire(volatile jfloat* p) { jfloat data = *p; acquire(); return data; } ++inline jdouble OrderAccess::load_acquire(volatile jdouble* p) { jdouble tmp = *p; acquire(); return tmp; } ++ ++//implementation of load_ptr_acquire ++inline intptr_t OrderAccess::load_ptr_acquire(volatile intptr_t* p) { intptr_t data = *p; acquire(); return data; } ++inline void* OrderAccess::load_ptr_acquire(volatile void* p) { void *data = *(void* volatile *)p; acquire(); return data; } ++inline void* OrderAccess::load_ptr_acquire(const volatile void* p) { void *data = *(void* volatile *)p; acquire(); return data; } ++ ++//implementation of release_store ++inline void OrderAccess::release_store(volatile jbyte* p, jbyte v) { release(); *p = v; } ++inline void OrderAccess::release_store(volatile jshort* p, jshort v) { release(); *p = v; } ++inline void OrderAccess::release_store(volatile jint* p, jint v) { release(); *p = v; } ++inline void OrderAccess::release_store(volatile jlong* p, jlong v) { release(); *p = v; } ++inline void OrderAccess::release_store(volatile jubyte* p, jubyte v) { release(); *p = v; } ++inline void OrderAccess::release_store(volatile jushort* p, jushort v) { release(); *p = v; } ++inline void OrderAccess::release_store(volatile juint* p, juint v) { release(); *p = v; } ++inline void OrderAccess::release_store(volatile julong* p, julong v) { release(); *p = v; } ++inline void OrderAccess::release_store(volatile jfloat* p, jfloat v) { release(); *p = v; } ++inline void OrderAccess::release_store(volatile jdouble* p, jdouble v) { release(); *p = v; } ++ ++//implementation of release_store_ptr ++inline void OrderAccess::release_store_ptr(volatile intptr_t* p, intptr_t v) { release(); *p = v; } ++inline void OrderAccess::release_store_ptr(volatile void* p, void* v) { release(); *(void* volatile *)p = v; } ++ ++//implementation of store_fence ++inline void OrderAccess::store_fence(jbyte* p, jbyte v) { *p = v; fence(); } ++inline void OrderAccess::store_fence(jshort* p, jshort v) { *p = v; fence(); } ++inline void OrderAccess::store_fence(jint* p, jint v) { *p = v; fence(); } ++inline void OrderAccess::store_fence(jlong* p, jlong v) { *p = v; fence(); } ++inline void OrderAccess::store_fence(jubyte* p, jubyte v) { *p = v; fence(); } ++inline void OrderAccess::store_fence(jushort* p, jushort v) { *p = v; fence(); } ++inline void OrderAccess::store_fence(juint* p, juint v) { *p = v; fence(); } ++inline void OrderAccess::store_fence(julong* p, julong v) { *p = v; fence(); } ++inline void OrderAccess::store_fence(jfloat* p, jfloat v) { *p = v; fence(); } ++inline void OrderAccess::store_fence(jdouble* p, jdouble v) { *p = v; fence(); } ++ ++//implementation of store_ptr_fence ++inline void OrderAccess::store_ptr_fence(intptr_t* p, intptr_t v) { *p = v; fence(); } ++inline void OrderAccess::store_ptr_fence(void** p, void* v) { *p = v; fence(); } ++ ++//implementation of release_store_fence ++inline void OrderAccess::release_store_fence(volatile jbyte* p, jbyte v) { release_store(p, v); fence(); } ++inline void OrderAccess::release_store_fence(volatile jshort* p, jshort v) { release_store(p, v); fence(); } ++inline void OrderAccess::release_store_fence(volatile jint* p, jint v) { release_store(p, v); fence(); } ++inline void OrderAccess::release_store_fence(volatile jlong* p, jlong v) { release_store(p, v); fence(); } ++inline void OrderAccess::release_store_fence(volatile jubyte* p, jubyte v) { release_store(p, v); fence(); } ++inline void OrderAccess::release_store_fence(volatile jushort* p, jushort v) { release_store(p, v); fence(); } ++inline void OrderAccess::release_store_fence(volatile juint* p, juint v) { release_store(p, v); fence(); } ++inline void OrderAccess::release_store_fence(volatile julong* p, julong v) { release_store(p, v); fence(); } ++inline void OrderAccess::release_store_fence(volatile jfloat* p, jfloat v) { release_store(p, v); fence(); } ++inline void OrderAccess::release_store_fence(volatile jdouble* p, jdouble v) { release_store(p, v); fence(); } ++ ++//implementaion of release_store_ptr_fence ++inline void OrderAccess::release_store_ptr_fence(volatile intptr_t* p, intptr_t v) { release_store_ptr(p, v); fence(); } ++inline void OrderAccess::release_store_ptr_fence(volatile void* p, void* v) { release_store_ptr(p, v); fence(); } ++ ++#undef inlasm_sync ++ ++#endif // OS_CPU_LINUX_LOONGARCH_VM_ORDERACCESS_LINUX_LOONGARCH_INLINE_HPP +diff --git a/hotspot/src/os_cpu/linux_loongarch/vm/os_linux_loongarch.cpp b/hotspot/src/os_cpu/linux_loongarch/vm/os_linux_loongarch.cpp +new file mode 100644 +index 0000000000..2f4a089855 +--- /dev/null ++++ b/hotspot/src/os_cpu/linux_loongarch/vm/os_linux_loongarch.cpp +@@ -0,0 +1,750 @@ ++/* ++ * Copyright (c) 1999, 2014, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2015, 2021, Loongson Technology. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ * ++ */ ++ ++// no precompiled headers ++#include "asm/macroAssembler.hpp" ++#include "classfile/classLoader.hpp" ++#include "classfile/systemDictionary.hpp" ++#include "classfile/vmSymbols.hpp" ++#include "code/icBuffer.hpp" ++#include "code/vtableStubs.hpp" ++#include "interpreter/interpreter.hpp" ++#include "jvm_linux.h" ++#include "memory/allocation.inline.hpp" ++#include "mutex_linux.inline.hpp" ++#include "os_share_linux.hpp" ++#include "prims/jniFastGetField.hpp" ++#include "prims/jvm.h" ++#include "prims/jvm_misc.hpp" ++#include "runtime/arguments.hpp" ++#include "runtime/extendedPC.hpp" ++#include "runtime/frame.inline.hpp" ++#include "runtime/interfaceSupport.hpp" ++#include "runtime/java.hpp" ++#include "runtime/javaCalls.hpp" ++#include "runtime/mutexLocker.hpp" ++#include "runtime/osThread.hpp" ++#include "runtime/sharedRuntime.hpp" ++#include "runtime/stubRoutines.hpp" ++#include "runtime/thread.inline.hpp" ++#include "runtime/timer.hpp" ++#include "utilities/events.hpp" ++#include "utilities/vmError.hpp" ++#include "utilities/debug.hpp" ++#include "compiler/disassembler.hpp" ++// put OS-includes here ++# include ++# include ++# include ++# include ++# include ++# include ++# include ++# include ++# include ++# include ++# include ++# include ++# include ++# include ++# include ++# include ++# include ++# include ++# include ++# include ++ ++#define REG_SP 3 ++#define REG_FP 22 ++ ++address os::current_stack_pointer() { ++ register void *sp __asm__ ("$r3"); ++ return (address) sp; ++} ++ ++char* os::non_memory_address_word() { ++ // Must never look like an address returned by reserve_memory, ++ // even in its subfields (as defined by the CPU immediate fields, ++ // if the CPU splits constants across multiple instructions). ++ ++ return (char*) -1; ++} ++ ++void os::initialize_thread(Thread* thr) { ++// Nothing to do. ++} ++ ++address os::Linux::ucontext_get_pc(ucontext_t * uc) { ++ return (address)uc->uc_mcontext.__pc; ++} ++ ++intptr_t* os::Linux::ucontext_get_sp(ucontext_t * uc) { ++ return (intptr_t*)uc->uc_mcontext.__gregs[REG_SP]; ++} ++ ++intptr_t* os::Linux::ucontext_get_fp(ucontext_t * uc) { ++ return (intptr_t*)uc->uc_mcontext.__gregs[REG_FP]; ++} ++ ++// For Forte Analyzer AsyncGetCallTrace profiling support - thread ++// is currently interrupted by SIGPROF. ++// os::Solaris::fetch_frame_from_ucontext() tries to skip nested signal ++// frames. Currently we don't do that on Linux, so it's the same as ++// os::fetch_frame_from_context(). ++ExtendedPC os::Linux::fetch_frame_from_ucontext(Thread* thread, ++ ucontext_t* uc, intptr_t** ret_sp, intptr_t** ret_fp) { ++ ++ assert(thread != NULL, "just checking"); ++ assert(ret_sp != NULL, "just checking"); ++ assert(ret_fp != NULL, "just checking"); ++ ++ return os::fetch_frame_from_context(uc, ret_sp, ret_fp); ++} ++ ++ExtendedPC os::fetch_frame_from_context(void* ucVoid, ++ intptr_t** ret_sp, intptr_t** ret_fp) { ++ ++ ExtendedPC epc; ++ ucontext_t* uc = (ucontext_t*)ucVoid; ++ ++ if (uc != NULL) { ++ epc = ExtendedPC(os::Linux::ucontext_get_pc(uc)); ++ if (ret_sp) *ret_sp = os::Linux::ucontext_get_sp(uc); ++ if (ret_fp) *ret_fp = os::Linux::ucontext_get_fp(uc); ++ } else { ++ // construct empty ExtendedPC for return value checking ++ epc = ExtendedPC(NULL); ++ if (ret_sp) *ret_sp = (intptr_t *)NULL; ++ if (ret_fp) *ret_fp = (intptr_t *)NULL; ++ } ++ ++ return epc; ++} ++ ++frame os::fetch_frame_from_context(void* ucVoid) { ++ intptr_t* sp; ++ intptr_t* fp; ++ ExtendedPC epc = fetch_frame_from_context(ucVoid, &sp, &fp); ++ return frame(sp, fp, epc.pc()); ++} ++ ++// By default, gcc always save frame pointer on stack. It may get ++// turned off by -fomit-frame-pointer, ++frame os::get_sender_for_C_frame(frame* fr) { ++ return frame(fr->sender_sp(), fr->link(), fr->sender_pc()); ++} ++ ++//intptr_t* _get_previous_fp() { ++intptr_t* __attribute__((noinline)) os::get_previous_fp() { ++ return (intptr_t*)__builtin_frame_address(0); ++} ++ ++frame os::current_frame() { ++ intptr_t* fp = (intptr_t*)get_previous_fp(); ++ frame myframe((intptr_t*)os::current_stack_pointer(), ++ (intptr_t*)fp, ++ CAST_FROM_FN_PTR(address, os::current_frame)); ++ if (os::is_first_C_frame(&myframe)) { ++ // stack is not walkable ++ return frame(); ++ } else { ++ return os::get_sender_for_C_frame(&myframe); ++ } ++} ++ ++extern "C" JNIEXPORT int ++JVM_handle_linux_signal(int sig, ++ siginfo_t* info, ++ void* ucVoid, ++ int abort_if_unrecognized) { ++#ifdef PRINT_SIGNAL_HANDLE ++ tty->print_cr("Signal: signo=%d, sicode=%d, sierrno=%d, siaddr=%lx", ++ info->si_signo, ++ info->si_code, ++ info->si_errno, ++ info->si_addr); ++#endif ++ ++ ucontext_t* uc = (ucontext_t*) ucVoid; ++ ++ Thread* t = ThreadLocalStorage::get_thread_slow(); ++ ++ SignalHandlerMark shm(t); ++ ++ // Note: it's not uncommon that JNI code uses signal/sigset to install ++ // then restore certain signal handler (e.g. to temporarily block SIGPIPE, ++ // or have a SIGILL handler when detecting CPU type). When that happens, ++ // JVM_handle_linux_signal() might be invoked with junk info/ucVoid. To ++ // avoid unnecessary crash when libjsig is not preloaded, try handle signals ++ // that do not require siginfo/ucontext first. ++ ++ if (sig == SIGPIPE/* || sig == SIGXFSZ*/) { ++ // allow chained handler to go first ++ if (os::Linux::chained_handler(sig, info, ucVoid)) { ++ return true; ++ } else { ++ if (PrintMiscellaneous && (WizardMode || Verbose)) { ++ warning("Ignoring SIGPIPE - see bug 4229104"); ++ } ++ return true; ++ } ++ } ++ ++ JavaThread* thread = NULL; ++ VMThread* vmthread = NULL; ++ if (os::Linux::signal_handlers_are_installed) { ++ if (t != NULL ){ ++ if(t->is_Java_thread()) { ++#ifdef PRINT_SIGNAL_HANDLE ++ tty->print_cr("this thread is a java thread"); ++#endif ++ thread = (JavaThread*)t; ++ } ++ else if(t->is_VM_thread()){ ++#ifdef PRINT_SIGNAL_HANDLE ++ tty->print_cr("this thread is a VM thread\n"); ++#endif ++ vmthread = (VMThread *)t; ++ } ++ } ++ } ++ ++ // decide if this trap can be handled by a stub ++ address stub = NULL; ++ address pc = NULL; ++ ++ pc = (address) os::Linux::ucontext_get_pc(uc); ++#ifdef PRINT_SIGNAL_HANDLE ++ tty->print_cr("pc=%lx", pc); ++ os::print_context(tty, uc); ++#endif ++ //%note os_trap_1 ++ if (info != NULL && uc != NULL && thread != NULL) { ++ pc = (address) os::Linux::ucontext_get_pc(uc); ++ // Handle ALL stack overflow variations here ++ if (sig == SIGSEGV) { ++ address addr = (address) info->si_addr; ++#ifdef PRINT_SIGNAL_HANDLE ++ tty->print("handle all stack overflow variations: "); ++ /*tty->print("addr = %lx, stack base = %lx, stack top = %lx\n", ++ addr, ++ thread->stack_base(), ++ thread->stack_base() - thread->stack_size()); ++ */ ++#endif ++ ++ // check if fault address is within thread stack ++ if (addr < thread->stack_base() && ++ addr >= thread->stack_base() - thread->stack_size()) { ++ // stack overflow ++#ifdef PRINT_SIGNAL_HANDLE ++ tty->print("stack exception check \n"); ++#endif ++ if (thread->in_stack_yellow_zone(addr)) { ++#ifdef PRINT_SIGNAL_HANDLE ++ tty->print("exception addr is in yellow zone\n"); ++#endif ++ thread->disable_stack_yellow_zone(); ++ if (thread->thread_state() == _thread_in_Java) { ++ // Throw a stack overflow exception. Guard pages will be reenabled ++ // while unwinding the stack. ++#ifdef PRINT_SIGNAL_HANDLE ++ tty->print("this thread is in java\n"); ++#endif ++ stub = SharedRuntime::continuation_for_implicit_exception(thread, pc, SharedRuntime::STACK_OVERFLOW); ++ } else { ++ // Thread was in the vm or native code. Return and try to finish. ++#ifdef PRINT_SIGNAL_HANDLE ++ tty->print("this thread is in vm or native codes and return\n"); ++#endif ++ return 1; ++ } ++ } else if (thread->in_stack_red_zone(addr)) { ++ // Fatal red zone violation. Disable the guard pages and fall through ++ // to handle_unexpected_exception way down below. ++#ifdef PRINT_SIGNAL_HANDLE ++ tty->print("exception addr is in red zone\n"); ++#endif ++ thread->disable_stack_red_zone(); ++ tty->print_raw_cr("An irrecoverable stack overflow has occurred."); ++ ++ // This is a likely cause, but hard to verify. Let's just print ++ // it as a hint. ++ tty->print_raw_cr("Please check if any of your loaded .so files has " ++ "enabled executable stack (see man page execstack(8))"); ++ } else { ++ // Accessing stack address below sp may cause SEGV if current ++ // thread has MAP_GROWSDOWN stack. This should only happen when ++ // current thread was created by user code with MAP_GROWSDOWN flag ++ // and then attached to VM. See notes in os_linux.cpp. ++#ifdef PRINT_SIGNAL_HANDLE ++ tty->print("exception addr is neither in yellow zone nor in the red one\n"); ++#endif ++ if (thread->osthread()->expanding_stack() == 0) { ++ thread->osthread()->set_expanding_stack(); ++ if (os::Linux::manually_expand_stack(thread, addr)) { ++ thread->osthread()->clear_expanding_stack(); ++ return 1; ++ } ++ thread->osthread()->clear_expanding_stack(); ++ } else { ++ fatal("recursive segv. expanding stack."); ++ } ++ } ++ } ++ } // sig == SIGSEGV ++ ++ if (thread->thread_state() == _thread_in_Java) { ++ // Java thread running in Java code => find exception handler if any ++ // a fault inside compiled code, the interpreter, or a stub ++#ifdef PRINT_SIGNAL_HANDLE ++ tty->print("java thread running in java code\n"); ++#endif ++ ++ // Handle signal from NativeJump::patch_verified_entry(). ++ if (sig == SIGILL & nativeInstruction_at(pc)->is_sigill_zombie_not_entrant()) { ++#ifdef PRINT_SIGNAL_HANDLE ++ tty->print_cr("verified entry = %lx, sig=%d", nativeInstruction_at(pc), sig); ++#endif ++ stub = SharedRuntime::get_handle_wrong_method_stub(); ++ } else if (sig == SIGSEGV && os::is_poll_address((address)info->si_addr)) { ++#ifdef PRINT_SIGNAL_HANDLE ++ tty->print_cr("polling address = %lx, sig=%d", os::get_polling_page(), sig); ++#endif ++ stub = SharedRuntime::get_poll_stub(pc); ++ } else if (sig == SIGBUS /* && info->si_code == BUS_OBJERR */) { ++ // BugId 4454115: A read from a MappedByteBuffer can fault ++ // here if the underlying file has been truncated. ++ // Do not crash the VM in such a case. ++ CodeBlob* cb = CodeCache::find_blob_unsafe(pc); ++ nmethod* nm = cb->is_nmethod() ? (nmethod*)cb : NULL; ++#ifdef PRINT_SIGNAL_HANDLE ++ tty->print("cb = %lx, nm = %lx\n", cb, nm); ++#endif ++ if (nm != NULL && nm->has_unsafe_access()) { ++ stub = StubRoutines::handler_for_unsafe_access(); ++ } ++ } else if (sig == SIGFPE /* && info->si_code == FPE_INTDIV */) { ++ // HACK: si_code does not work on linux 2.2.12-20!!! ++ int op = pc[0] & 0x3f; ++ int op1 = pc[3] & 0x3f; ++ //FIXME, Must port to LA code!! ++ switch (op) { ++ case 0x1e: //ddiv ++ case 0x1f: //ddivu ++ case 0x1a: //div ++ case 0x1b: //divu ++ case 0x34: //trap ++ // In LA, div_by_zero exception can only be triggered by explicit 'trap'. ++ stub = SharedRuntime::continuation_for_implicit_exception(thread, ++ pc, ++ SharedRuntime::IMPLICIT_DIVIDE_BY_ZERO); ++ break; ++ default: ++ // TODO: handle more cases if we are using other x86 instructions ++ // that can generate SIGFPE signal on linux. ++ tty->print_cr("unknown opcode 0x%X -0x%X with SIGFPE.", op, op1); ++ //fatal("please update this code."); ++ } ++ } else if (sig == SIGSEGV && ++ !MacroAssembler::needs_explicit_null_check((intptr_t)info->si_addr)) { ++#ifdef PRINT_SIGNAL_HANDLE ++ tty->print("continuation for implicit exception\n"); ++#endif ++ // Determination of interpreter/vtable stub/compiled code null exception ++ stub = SharedRuntime::continuation_for_implicit_exception(thread, pc, SharedRuntime::IMPLICIT_NULL); ++#ifdef PRINT_SIGNAL_HANDLE ++ tty->print_cr("continuation_for_implicit_exception stub: %lx", stub); ++#endif ++ } ++ } else if (thread->thread_state() == _thread_in_vm && ++ sig == SIGBUS && /* info->si_code == BUS_OBJERR && */ ++ thread->doing_unsafe_access()) { ++#ifdef PRINT_SIGNAL_HANDLE ++ tty->print_cr("SIGBUS in vm thread \n"); ++#endif ++ stub = StubRoutines::handler_for_unsafe_access(); ++ } ++ ++ // jni_fast_GetField can trap at certain pc's if a GC kicks in ++ // and the heap gets shrunk before the field access. ++ if ((sig == SIGSEGV) || (sig == SIGBUS)) { ++#ifdef PRINT_SIGNAL_HANDLE ++ tty->print("jni fast get trap: "); ++#endif ++ address addr = JNI_FastGetField::find_slowcase_pc(pc); ++ if (addr != (address)-1) { ++ stub = addr; ++ } ++#ifdef PRINT_SIGNAL_HANDLE ++ tty->print_cr("addr = %d, stub = %lx", addr, stub); ++#endif ++ } ++ ++ // Check to see if we caught the safepoint code in the ++ // process of write protecting the memory serialization page. ++ // It write enables the page immediately after protecting it ++ // so we can just return to retry the write. ++ if ((sig == SIGSEGV) && ++ os::is_memory_serialize_page(thread, (address) info->si_addr)) { ++#ifdef PRINT_SIGNAL_HANDLE ++ tty->print("write protecting the memory serialiazation page\n"); ++#endif ++ // Block current thread until the memory serialize page permission restored. ++ os::block_on_serialize_page_trap(); ++ return true; ++ } ++ } ++ ++ if (stub != NULL) { ++#ifdef PRINT_SIGNAL_HANDLE ++ tty->print_cr("resolved stub=%lx\n",stub); ++#endif ++ // save all thread context in case we need to restore it ++ if (thread != NULL) thread->set_saved_exception_pc(pc); ++ ++ uc->uc_mcontext.__pc = (greg_t)stub; ++ return true; ++ } ++ ++ // signal-chaining ++ if (os::Linux::chained_handler(sig, info, ucVoid)) { ++#ifdef PRINT_SIGNAL_HANDLE ++ tty->print_cr("signal chaining\n"); ++#endif ++ return true; ++ } ++ ++ if (!abort_if_unrecognized) { ++#ifdef PRINT_SIGNAL_HANDLE ++ tty->print_cr("abort becauce of unrecognized\n"); ++#endif ++ // caller wants another chance, so give it to him ++ return false; ++ } ++ ++ if (pc == NULL && uc != NULL) { ++ pc = os::Linux::ucontext_get_pc(uc); ++ } ++ ++ // unmask current signal ++ sigset_t newset; ++ sigemptyset(&newset); ++ sigaddset(&newset, sig); ++ sigprocmask(SIG_UNBLOCK, &newset, NULL); ++#ifdef PRINT_SIGNAL_HANDLE ++ tty->print_cr("VMError in signal handler\n"); ++#endif ++ VMError err(t, sig, pc, info, ucVoid); ++ err.report_and_die(); ++ ++ ShouldNotReachHere(); ++ return true; // Mute compiler ++} ++ ++void os::Linux::init_thread_fpu_state(void) { ++} ++ ++int os::Linux::get_fpu_control_word(void) { ++ return 0; // mute compiler ++} ++ ++void os::Linux::set_fpu_control_word(int fpu_control) { ++} ++ ++bool os::is_allocatable(size_t bytes) { ++ ++ if (bytes < 2 * G) { ++ return true; ++ } ++ ++ char* addr = reserve_memory(bytes, NULL); ++ ++ if (addr != NULL) { ++ release_memory(addr, bytes); ++ } ++ ++ return addr != NULL; ++} ++ ++//////////////////////////////////////////////////////////////////////////////// ++// thread stack ++ ++size_t os::Linux::min_stack_allowed = 96 * K; ++ ++// Test if pthread library can support variable thread stack size. LinuxThreads ++// in fixed stack mode allocates 2M fixed slot for each thread. LinuxThreads ++// in floating stack mode and NPTL support variable stack size. ++bool os::Linux::supports_variable_stack_size() { ++ if (os::Linux::is_NPTL()) { ++ // NPTL, yes ++ return true; ++ ++ } else { ++ // Note: We can't control default stack size when creating a thread. ++ // If we use non-default stack size (pthread_attr_setstacksize), both ++ // floating stack and non-floating stack LinuxThreads will return the ++ // same value. This makes it impossible to implement this function by ++ // detecting thread stack size directly. ++ // ++ // An alternative approach is to check %gs. Fixed-stack LinuxThreads ++ // do not use %gs, so its value is 0. Floating-stack LinuxThreads use ++ // %gs (either as LDT selector or GDT selector, depending on kernel) ++ // to access thread specific data. ++ // ++ // Note that %gs is a reserved glibc register since early 2001, so ++ // applications are not allowed to change its value (Ulrich Drepper from ++ // Redhat confirmed that all known offenders have been modified to use ++ // either %fs or TSD). In the worst case scenario, when VM is embedded in ++ // a native application that plays with %gs, we might see non-zero %gs ++ // even LinuxThreads is running in fixed stack mode. As the result, we'll ++ // return true and skip _thread_safety_check(), so we may not be able to ++ // detect stack-heap collisions. But otherwise it's harmless. ++ // ++ return false; ++ } ++} ++ ++// return default stack size for thr_type ++size_t os::Linux::default_stack_size(os::ThreadType thr_type) { ++ // default stack size (compiler thread needs larger stack) ++ size_t s = (thr_type == os::compiler_thread ? 2 * M : 512 * K); ++ return s; ++} ++ ++size_t os::Linux::default_guard_size(os::ThreadType thr_type) { ++ // Creating guard page is very expensive. Java thread has HotSpot ++ // guard page, only enable glibc guard page for non-Java threads. ++ return (thr_type == java_thread ? 0 : page_size()); ++} ++ ++// Java thread: ++// ++// Low memory addresses ++// +------------------------+ ++// | |\ JavaThread created by VM does not have glibc ++// | glibc guard page | - guard, attached Java thread usually has ++// | |/ 1 page glibc guard. ++// P1 +------------------------+ Thread::stack_base() - Thread::stack_size() ++// | |\ ++// | HotSpot Guard Pages | - red and yellow pages ++// | |/ ++// +------------------------+ JavaThread::stack_yellow_zone_base() ++// | |\ ++// | Normal Stack | - ++// | |/ ++// P2 +------------------------+ Thread::stack_base() ++// ++// Non-Java thread: ++// ++// Low memory addresses ++// +------------------------+ ++// | |\ ++// | glibc guard page | - usually 1 page ++// | |/ ++// P1 +------------------------+ Thread::stack_base() - Thread::stack_size() ++// | |\ ++// | Normal Stack | - ++// | |/ ++// P2 +------------------------+ Thread::stack_base() ++// ++// ** P1 (aka bottom) and size ( P2 = P1 - size) are the address and stack size returned from ++// pthread_attr_getstack() ++ ++static void current_stack_region(address * bottom, size_t * size) { ++ if (os::is_primordial_thread()) { ++ // primordial thread needs special handling because pthread_getattr_np() ++ // may return bogus value. ++ *bottom = os::Linux::initial_thread_stack_bottom(); ++ *size = os::Linux::initial_thread_stack_size(); ++ } else { ++ pthread_attr_t attr; ++ ++ int rslt = pthread_getattr_np(pthread_self(), &attr); ++ ++ // JVM needs to know exact stack location, abort if it fails ++ if (rslt != 0) { ++ if (rslt == ENOMEM) { ++ vm_exit_out_of_memory(0, OOM_MMAP_ERROR, "pthread_getattr_np"); ++ } else { ++ fatal(err_msg("pthread_getattr_np failed with errno = %d", rslt)); ++ } ++ } ++ ++ if (pthread_attr_getstack(&attr, (void **)bottom, size) != 0) { ++ fatal("Can not locate current stack attributes!"); ++ } ++ ++ pthread_attr_destroy(&attr); ++ ++ } ++ assert(os::current_stack_pointer() >= *bottom && ++ os::current_stack_pointer() < *bottom + *size, "just checking"); ++} ++ ++address os::current_stack_base() { ++ address bottom; ++ size_t size; ++ current_stack_region(&bottom, &size); ++ return (bottom + size); ++} ++ ++size_t os::current_stack_size() { ++ // stack size includes normal stack and HotSpot guard pages ++ address bottom; ++ size_t size; ++ current_stack_region(&bottom, &size); ++ return size; ++} ++ ++///////////////////////////////////////////////////////////////////////////// ++// helper functions for fatal error handler ++void os::print_register_info(outputStream *st, void *context) { ++ ++ ucontext_t *uc = (ucontext_t*)context; ++ ++ st->print_cr("Register to memory mapping:"); ++ st->cr(); ++ // this is horrendously verbose but the layout of the registers in the ++ // // context does not match how we defined our abstract Register set, so ++ // // we can't just iterate through the gregs area ++ // ++ // // this is only for the "general purpose" registers ++ st->print("ZERO=" ); print_location(st, uc->uc_mcontext.__gregs[0]); ++ st->print("RA=" ); print_location(st, uc->uc_mcontext.__gregs[1]); ++ st->print("TP=" ); print_location(st, uc->uc_mcontext.__gregs[2]); ++ st->print("SP=" ); print_location(st, uc->uc_mcontext.__gregs[3]); ++ st->cr(); ++ st->print("A0=" ); print_location(st, uc->uc_mcontext.__gregs[4]); ++ st->print("A1=" ); print_location(st, uc->uc_mcontext.__gregs[5]); ++ st->print("A2=" ); print_location(st, uc->uc_mcontext.__gregs[6]); ++ st->print("A3=" ); print_location(st, uc->uc_mcontext.__gregs[7]); ++ st->cr(); ++ st->print("A4=" ); print_location(st, uc->uc_mcontext.__gregs[8]); ++ st->print("A5=" ); print_location(st, uc->uc_mcontext.__gregs[9]); ++ st->print("A6=" ); print_location(st, uc->uc_mcontext.__gregs[10]); ++ st->print("A7=" ); print_location(st, uc->uc_mcontext.__gregs[11]); ++ st->cr(); ++ st->print("T0=" ); print_location(st, uc->uc_mcontext.__gregs[12]); ++ st->print("T1=" ); print_location(st, uc->uc_mcontext.__gregs[13]); ++ st->print("T2=" ); print_location(st, uc->uc_mcontext.__gregs[14]); ++ st->print("T3=" ); print_location(st, uc->uc_mcontext.__gregs[15]); ++ st->cr(); ++ st->print("T4=" ); print_location(st, uc->uc_mcontext.__gregs[16]); ++ st->print("T5=" ); print_location(st, uc->uc_mcontext.__gregs[17]); ++ st->print("T6=" ); print_location(st, uc->uc_mcontext.__gregs[18]); ++ st->print("T7=" ); print_location(st, uc->uc_mcontext.__gregs[19]); ++ st->cr(); ++ st->print("T8=" ); print_location(st, uc->uc_mcontext.__gregs[20]); ++ st->print("RX=" ); print_location(st, uc->uc_mcontext.__gregs[21]); ++ st->print("FP=" ); print_location(st, uc->uc_mcontext.__gregs[22]); ++ st->print("S0=" ); print_location(st, uc->uc_mcontext.__gregs[23]); ++ st->cr(); ++ st->print("S1=" ); print_location(st, uc->uc_mcontext.__gregs[24]); ++ st->print("S2=" ); print_location(st, uc->uc_mcontext.__gregs[25]); ++ st->print("S3=" ); print_location(st, uc->uc_mcontext.__gregs[26]); ++ st->print("S4=" ); print_location(st, uc->uc_mcontext.__gregs[27]); ++ st->cr(); ++ st->print("S5=" ); print_location(st, uc->uc_mcontext.__gregs[28]); ++ st->print("S6=" ); print_location(st, uc->uc_mcontext.__gregs[29]); ++ st->print("S7=" ); print_location(st, uc->uc_mcontext.__gregs[30]); ++ st->print("S8=" ); print_location(st, uc->uc_mcontext.__gregs[31]); ++ st->cr(); ++ ++} ++void os::print_context(outputStream *st, void *context) { ++ ++ ucontext_t *uc = (ucontext_t*)context; ++ st->print_cr("Registers:"); ++ st->print( "ZERO=" INTPTR_FORMAT, (intptr_t)uc->uc_mcontext.__gregs[0]); ++ st->print(", RA=" INTPTR_FORMAT, (intptr_t)uc->uc_mcontext.__gregs[1]); ++ st->print(", TP=" INTPTR_FORMAT, (intptr_t)uc->uc_mcontext.__gregs[2]); ++ st->print(", SP=" INTPTR_FORMAT, (intptr_t)uc->uc_mcontext.__gregs[3]); ++ st->cr(); ++ st->print( "A0=" INTPTR_FORMAT, (intptr_t)uc->uc_mcontext.__gregs[4]); ++ st->print(", A1=" INTPTR_FORMAT, (intptr_t)uc->uc_mcontext.__gregs[5]); ++ st->print(", A2=" INTPTR_FORMAT, (intptr_t)uc->uc_mcontext.__gregs[6]); ++ st->print(", A3=" INTPTR_FORMAT, (intptr_t)uc->uc_mcontext.__gregs[7]); ++ st->cr(); ++ st->print( "A4=" INTPTR_FORMAT, (intptr_t)uc->uc_mcontext.__gregs[8]); ++ st->print(", A5=" INTPTR_FORMAT, (intptr_t)uc->uc_mcontext.__gregs[9]); ++ st->print(", A6=" INTPTR_FORMAT, (intptr_t)uc->uc_mcontext.__gregs[10]); ++ st->print(", A7=" INTPTR_FORMAT, (intptr_t)uc->uc_mcontext.__gregs[11]); ++ st->cr(); ++ st->print( "T0=" INTPTR_FORMAT, (intptr_t)uc->uc_mcontext.__gregs[12]); ++ st->print(", T1=" INTPTR_FORMAT, (intptr_t)uc->uc_mcontext.__gregs[13]); ++ st->print(", T2=" INTPTR_FORMAT, (intptr_t)uc->uc_mcontext.__gregs[14]); ++ st->print(", T3=" INTPTR_FORMAT, (intptr_t)uc->uc_mcontext.__gregs[15]); ++ st->cr(); ++ st->print( "T4=" INTPTR_FORMAT, (intptr_t)uc->uc_mcontext.__gregs[16]); ++ st->print(", T5=" INTPTR_FORMAT, (intptr_t)uc->uc_mcontext.__gregs[17]); ++ st->print(", T6=" INTPTR_FORMAT, (intptr_t)uc->uc_mcontext.__gregs[18]); ++ st->print(", T7=" INTPTR_FORMAT, (intptr_t)uc->uc_mcontext.__gregs[19]); ++ st->cr(); ++ st->print( "T8=" INTPTR_FORMAT, (intptr_t)uc->uc_mcontext.__gregs[20]); ++ st->print(", RX=" INTPTR_FORMAT, (intptr_t)uc->uc_mcontext.__gregs[21]); ++ st->print(", FP=" INTPTR_FORMAT, (intptr_t)uc->uc_mcontext.__gregs[22]); ++ st->print(", S0=" INTPTR_FORMAT, (intptr_t)uc->uc_mcontext.__gregs[23]); ++ st->cr(); ++ st->print( "S1=" INTPTR_FORMAT, (intptr_t)uc->uc_mcontext.__gregs[24]); ++ st->print(", S2=" INTPTR_FORMAT, (intptr_t)uc->uc_mcontext.__gregs[25]); ++ st->print(", S3=" INTPTR_FORMAT, (intptr_t)uc->uc_mcontext.__gregs[26]); ++ st->print(", S4=" INTPTR_FORMAT, (intptr_t)uc->uc_mcontext.__gregs[27]); ++ st->cr(); ++ st->print( "S5=" INTPTR_FORMAT, (intptr_t)uc->uc_mcontext.__gregs[28]); ++ st->print(", S6=" INTPTR_FORMAT, (intptr_t)uc->uc_mcontext.__gregs[29]); ++ st->print(", S7=" INTPTR_FORMAT, (intptr_t)uc->uc_mcontext.__gregs[30]); ++ st->print(", S8=" INTPTR_FORMAT, (intptr_t)uc->uc_mcontext.__gregs[31]); ++ st->cr(); ++ st->cr(); ++ ++ intptr_t *sp = (intptr_t *)os::Linux::ucontext_get_sp(uc); ++ st->print_cr("Top of Stack: (sp=" PTR_FORMAT ")", p2i(sp)); ++ //print_hex_dump(st, (address)sp, (address)(sp + 8*sizeof(intptr_t)), sizeof(intptr_t)); ++ print_hex_dump(st, (address)sp-32, (address)(sp + 32), sizeof(intptr_t)); ++ st->cr(); ++ ++ // Note: it may be unsafe to inspect memory near pc. For example, pc may ++ // point to garbage if entry point in an nmethod is corrupted. Leave ++ // this at the end, and hope for the best. ++ address pc = os::Linux::ucontext_get_pc(uc); ++ st->print_cr("Instructions: (pc=" PTR_FORMAT ")", p2i(pc)); ++ print_hex_dump(st, pc - 64, pc + 64, sizeof(char)); ++ Disassembler::decode(pc - 80, pc + 80, st); ++} ++ ++void os::setup_fpu() { ++ // no use for LA ++} ++ ++#ifndef PRODUCT ++void os::verify_stack_alignment() { ++ assert(((intptr_t)os::current_stack_pointer() & (StackAlignmentInBytes-1)) == 0, "incorrect stack alignment"); ++} ++#endif ++ ++bool os::is_ActiveCoresMP() { ++ return UseActiveCoresMP && _initial_active_processor_count == 1; ++} +diff --git a/hotspot/src/os_cpu/linux_loongarch/vm/os_linux_loongarch.hpp b/hotspot/src/os_cpu/linux_loongarch/vm/os_linux_loongarch.hpp +new file mode 100644 +index 0000000000..a7321ae025 +--- /dev/null ++++ b/hotspot/src/os_cpu/linux_loongarch/vm/os_linux_loongarch.hpp +@@ -0,0 +1,39 @@ ++/* ++ * Copyright (c) 1999, 2013, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2015, 2022, Loongson Technology. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ * ++ */ ++ ++#ifndef OS_CPU_LINUX_LOONGARCH_VM_OS_LINUX_LOONGARCH_HPP ++#define OS_CPU_LINUX_LOONGARCH_VM_OS_LINUX_LOONGARCH_HPP ++ ++ static void setup_fpu(); ++ static bool is_allocatable(size_t bytes); ++ static intptr_t *get_previous_fp(); ++ ++ // Used to register dynamic code cache area with the OS ++ // Note: Currently only used in 64 bit Windows implementations ++ static bool register_code_area(char *low, char *high) { return true; } ++ ++ static bool is_ActiveCoresMP(); ++ ++#endif // OS_CPU_LINUX_LOONGARCH_VM_OS_LINUX_LOONGARCH_HPP +diff --git a/hotspot/src/os_cpu/linux_loongarch/vm/prefetch_linux_loongarch.inline.hpp b/hotspot/src/os_cpu/linux_loongarch/vm/prefetch_linux_loongarch.inline.hpp +new file mode 100644 +index 0000000000..a1cedcd8cf +--- /dev/null ++++ b/hotspot/src/os_cpu/linux_loongarch/vm/prefetch_linux_loongarch.inline.hpp +@@ -0,0 +1,56 @@ ++/* ++ * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2015, 2022, Loongson Technology. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ * ++ */ ++ ++#ifndef OS_CPU_LINUX_LOONGARCH_VM_PREFETCH_LINUX_LOONGARCH_INLINE_HPP ++#define OS_CPU_LINUX_LOONGARCH_VM_PREFETCH_LINUX_LOONGARCH_INLINE_HPP ++ ++ ++inline void Prefetch::read (void *loc, intx interval) { ++// According to previous and present SPECjbb2015 score, ++// comment prefetch is better than if (interval >= 0) prefetch branch. ++// So choose comment prefetch as the base line. ++#if 0 ++ __asm__ __volatile__ ( ++ " preld 0, %[__loc] \n" ++ : ++ : [__loc] "m"( *((address)loc + interval) ) ++ : "memory" ++ ); ++#endif ++} ++ ++inline void Prefetch::write(void *loc, intx interval) { ++// Ditto ++#if 0 ++ __asm__ __volatile__ ( ++ " preld 8, %[__loc] \n" ++ : ++ : [__loc] "m"( *((address)loc + interval) ) ++ : "memory" ++ ); ++#endif ++} ++ ++#endif // OS_CPU_LINUX_LOONGARCH_VM_PREFETCH_LINUX_LOONGARCH_INLINE_HPP +diff --git a/hotspot/src/os_cpu/linux_loongarch/vm/threadLS_linux_loongarch.cpp b/hotspot/src/os_cpu/linux_loongarch/vm/threadLS_linux_loongarch.cpp +new file mode 100644 +index 0000000000..be28a562a1 +--- /dev/null ++++ b/hotspot/src/os_cpu/linux_loongarch/vm/threadLS_linux_loongarch.cpp +@@ -0,0 +1,84 @@ ++/* ++ * Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2015, 2022, Loongson Technology. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ * ++ */ ++ ++#include "precompiled.hpp" ++#include "runtime/thread.inline.hpp" ++#include "runtime/threadLocalStorage.hpp" ++ ++// Map stack pointer (%esp) to thread pointer for faster TLS access ++// ++// Here we use a flat table for better performance. Getting current thread ++// is down to one memory access (read _sp_map[%esp>>12]) in generated code ++// and two in runtime code (-fPIC code needs an extra load for _sp_map). ++// ++// This code assumes stack page is not shared by different threads. It works ++// in 32-bit VM when page size is 4K (or a multiple of 4K, if that matters). ++// ++// Notice that _sp_map is allocated in the bss segment, which is ZFOD ++// (zero-fill-on-demand). While it reserves 4M address space upfront, ++// actual memory pages are committed on demand. ++// ++// If an application creates and destroys a lot of threads, usually the ++// stack space freed by a thread will soon get reused by new thread ++// (this is especially true in NPTL or LinuxThreads in fixed-stack mode). ++// No memory page in _sp_map is wasted. ++// ++// However, it's still possible that we might end up populating & ++// committing a large fraction of the 4M table over time, but the actual ++// amount of live data in the table could be quite small. The max wastage ++// is less than 4M bytes. If it becomes an issue, we could use madvise() ++// with MADV_DONTNEED to reclaim unused (i.e. all-zero) pages in _sp_map. ++// MADV_DONTNEED on Linux keeps the virtual memory mapping, but zaps the ++// physical memory page (i.e. similar to MADV_FREE on Solaris). ++ ++#ifdef MINIMIZE_RAM_USAGE ++Thread* ThreadLocalStorage::_sp_map[1UL << (SP_BITLENGTH - PAGE_SHIFT)]; ++#endif // MINIMIZE_RAM_USAGE ++ ++void ThreadLocalStorage::generate_code_for_get_thread() { ++ // nothing we can do here for user-level thread ++} ++ ++void ThreadLocalStorage::pd_init() { ++#ifdef MINIMIZE_RAM_USAGE ++ assert(align_size_down(os::vm_page_size(), PAGE_SIZE) == os::vm_page_size(), ++ "page size must be multiple of PAGE_SIZE"); ++#endif // MINIMIZE_RAM_USAGE ++} ++ ++void ThreadLocalStorage::pd_set_thread(Thread* thread) { ++ os::thread_local_storage_at_put(ThreadLocalStorage::thread_index(), thread); ++#ifdef MINIMIZE_RAM_USAGE ++ address stack_top = os::current_stack_base(); ++ size_t stack_size = os::current_stack_size(); ++ ++ for (address p = stack_top - stack_size; p < stack_top; p += PAGE_SIZE) { ++ int index = ((uintptr_t)p >> PAGE_SHIFT) & ((1UL << (SP_BITLENGTH - PAGE_SHIFT)) - 1); ++ assert(thread == NULL || _sp_map[index] == NULL || thread == _sp_map[index], ++ "thread exited without detaching from VM??"); ++ _sp_map[index] = thread; ++ } ++#endif // MINIMIZE_RAM_USAGE ++} +diff --git a/hotspot/src/os_cpu/linux_loongarch/vm/threadLS_linux_loongarch.hpp b/hotspot/src/os_cpu/linux_loongarch/vm/threadLS_linux_loongarch.hpp +new file mode 100644 +index 0000000000..4fab788a75 +--- /dev/null ++++ b/hotspot/src/os_cpu/linux_loongarch/vm/threadLS_linux_loongarch.hpp +@@ -0,0 +1,61 @@ ++/* ++ * Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2015, 2022, Loongson Technology. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ * ++ */ ++ ++#ifndef OS_CPU_LINUX_LOONGARCH_VM_THREADLS_LINUX_LOONGARCH_HPP ++#define OS_CPU_LINUX_LOONGARCH_VM_THREADLS_LINUX_LOONGARCH_HPP ++ ++#ifdef MINIMIZE_RAM_USAGE ++ // Processor dependent parts of ThreadLocalStorage ++ //only the low 2G space for user program in Linux ++ ++ #define SP_BITLENGTH 34 ++ #define PAGE_SHIFT 14 ++ #define PAGE_SIZE (1UL << PAGE_SHIFT) ++ ++ static Thread* _sp_map[1UL << (SP_BITLENGTH - PAGE_SHIFT)]; ++ static int _sp_map_low; ++ static int _sp_map_high; ++#endif // MINIMIZE_RAM_USAGE ++ ++public: ++#ifdef MINIMIZE_RAM_USAGE ++ static Thread** sp_map_addr() { return _sp_map; } ++#endif // MINIMIZE_RAM_USAGE ++ ++ static Thread* thread() { ++#ifdef MINIMIZE_RAM_USAGE ++ /* Thread::thread() can also be optimized in the same way as __get_thread() */ ++ //return (Thread*) os::thread_local_storage_at(thread_index()); ++ uintptr_t sp; ++ uintptr_t mask = (1UL << (SP_BITLENGTH - PAGE_SHIFT)) - 1; ++ ++ __asm__ __volatile__ ("addi.d %0, $r29, 0 " : "=r" (sp)); ++ ++ return _sp_map[(sp >> PAGE_SHIFT) & mask]; ++#else ++ return (Thread*) os::thread_local_storage_at(thread_index()); ++#endif // MINIMIZE_RAM_USAGE ++ } ++#endif // OS_CPU_LINUX_LOONGARCH_VM_THREADLS_LINUX_LOONGARCH_HPP +diff --git a/hotspot/src/os_cpu/linux_loongarch/vm/thread_linux_loongarch.cpp b/hotspot/src/os_cpu/linux_loongarch/vm/thread_linux_loongarch.cpp +new file mode 100644 +index 0000000000..44f666d61f +--- /dev/null ++++ b/hotspot/src/os_cpu/linux_loongarch/vm/thread_linux_loongarch.cpp +@@ -0,0 +1,99 @@ ++/* ++ * Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2015, 2021, Loongson Technology. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ * ++ */ ++ ++#include "precompiled.hpp" ++#include "runtime/frame.inline.hpp" ++#include "runtime/thread.inline.hpp" ++#include "runtime/sharedRuntime.hpp" ++ ++void JavaThread::pd_initialize() ++{ ++ _anchor.clear(); ++} ++ ++// For Forte Analyzer AsyncGetCallTrace profiling support - thread is ++// currently interrupted by SIGPROF ++bool JavaThread::pd_get_top_frame_for_signal_handler(frame* fr_addr, ++ void* ucontext, bool isInJava) { ++ ++ assert(Thread::current() == this, "caller must be current thread"); ++ return pd_get_top_frame(fr_addr, ucontext, isInJava); ++} ++ ++bool JavaThread::pd_get_top_frame_for_profiling(frame* fr_addr, void* ucontext, bool isInJava) { ++ return pd_get_top_frame(fr_addr, ucontext, isInJava); ++} ++ ++bool JavaThread::pd_get_top_frame(frame* fr_addr, void* ucontext, bool isInJava) { ++ assert(this->is_Java_thread(), "must be JavaThread"); ++ JavaThread* jt = (JavaThread *)this; ++ ++ // If we have a last_Java_frame, then we should use it even if ++ // isInJava == true. It should be more reliable than ucontext info. ++ if (jt->has_last_Java_frame() && jt->frame_anchor()->walkable()) { ++ *fr_addr = jt->pd_last_frame(); ++ return true; ++ } ++ ++ // At this point, we don't have a last_Java_frame, so ++ // we try to glean some information out of the ucontext ++ // if we were running Java code when SIGPROF came in. ++ if (isInJava) { ++ ucontext_t* uc = (ucontext_t*) ucontext; ++ ++ intptr_t* ret_fp; ++ intptr_t* ret_sp; ++ ExtendedPC addr = os::Linux::fetch_frame_from_ucontext(this, uc, ++ &ret_sp, &ret_fp); ++ if (addr.pc() == NULL || ret_sp == NULL ) { ++ // ucontext wasn't useful ++ return false; ++ } ++ ++ frame ret_frame(ret_sp, ret_fp, addr.pc()); ++ if (!ret_frame.safe_for_sender(jt)) { ++#ifdef COMPILER2 ++ // C2 uses ebp as a general register see if NULL fp helps ++ frame ret_frame2(ret_sp, NULL, addr.pc()); ++ if (!ret_frame2.safe_for_sender(jt)) { ++ // nothing else to try if the frame isn't good ++ return false; ++ } ++ ret_frame = ret_frame2; ++#else ++ // nothing else to try if the frame isn't good ++ return false; ++#endif /* COMPILER2 */ ++ } ++ *fr_addr = ret_frame; ++ return true; ++ } ++ ++ // nothing else to try ++ return false; ++} ++ ++void JavaThread::cache_global_variables() { } ++ +diff --git a/hotspot/src/os_cpu/linux_loongarch/vm/thread_linux_loongarch.hpp b/hotspot/src/os_cpu/linux_loongarch/vm/thread_linux_loongarch.hpp +new file mode 100644 +index 0000000000..d6dd2521f4 +--- /dev/null ++++ b/hotspot/src/os_cpu/linux_loongarch/vm/thread_linux_loongarch.hpp +@@ -0,0 +1,75 @@ ++/* ++ * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2015, 2021, Loongson Technology. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ * ++ */ ++ ++#ifndef OS_CPU_LINUX_LOONGARCH_VM_THREAD_LINUX_LOONGARCH_HPP ++#define OS_CPU_LINUX_LOONGARCH_VM_THREAD_LINUX_LOONGARCH_HPP ++ ++ private: ++ void pd_initialize(); ++ ++ frame pd_last_frame() { ++ assert(has_last_Java_frame(), "must have last_Java_sp() when suspended"); ++ if (_anchor.last_Java_pc() != NULL) { ++ return frame(_anchor.last_Java_sp(), _anchor.last_Java_fp(), _anchor.last_Java_pc()); ++ } else { ++ // This will pick up pc from sp ++ return frame(_anchor.last_Java_sp(), _anchor.last_Java_fp()); ++ } ++ } ++ ++ ++ public: ++ // Mutators are highly dangerous.... ++ intptr_t* last_Java_fp() { return _anchor.last_Java_fp(); } ++ void set_last_Java_fp(intptr_t* fp) { _anchor.set_last_Java_fp(fp); } ++ ++ void set_base_of_stack_pointer(intptr_t* base_sp) { ++ } ++ ++ static ByteSize last_Java_fp_offset() { ++ return byte_offset_of(JavaThread, _anchor) + JavaFrameAnchor::last_Java_fp_offset(); ++ } ++ ++ intptr_t* base_of_stack_pointer() { ++ return NULL; ++ } ++ void record_base_of_stack_pointer() { ++ } ++ ++ bool pd_get_top_frame_for_signal_handler(frame* fr_addr, void* ucontext, ++ bool isInJava); ++ ++ bool pd_get_top_frame_for_profiling(frame* fr_addr, void* ucontext, bool isInJava); ++private: ++ bool pd_get_top_frame(frame* fr_addr, void* ucontext, bool isInJava); ++public: ++ ++ // These routines are only used on cpu architectures that ++ // have separate register stacks (Itanium). ++ static bool register_stack_overflow() { return false; } ++ static void enable_register_stack_guard() {} ++ static void disable_register_stack_guard() {} ++ ++#endif // OS_CPU_LINUX_LOONGARCH_VM_THREAD_LINUX_LOONGARCH_HPP +diff --git a/hotspot/src/os_cpu/linux_loongarch/vm/vmStructs_linux_loongarch.hpp b/hotspot/src/os_cpu/linux_loongarch/vm/vmStructs_linux_loongarch.hpp +new file mode 100644 +index 0000000000..0097cadcb7 +--- /dev/null ++++ b/hotspot/src/os_cpu/linux_loongarch/vm/vmStructs_linux_loongarch.hpp +@@ -0,0 +1,55 @@ ++/* ++ * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2015, 2020, Loongson Technology. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ * ++ */ ++ ++#ifndef OS_CPU_LINUX_LOONGARCH_VM_VMSTRUCTS_LINUX_LOONGARCH_HPP ++#define OS_CPU_LINUX_LOONGARCH_VM_VMSTRUCTS_LINUX_LOONGARCH_HPP ++ ++// These are the OS and CPU-specific fields, types and integer ++// constants required by the Serviceability Agent. This file is ++// referenced by vmStructs.cpp. ++ ++#define VM_STRUCTS_OS_CPU(nonstatic_field, static_field, unchecked_nonstatic_field, volatile_nonstatic_field, nonproduct_nonstatic_field, c2_nonstatic_field, unchecked_c1_static_field, unchecked_c2_static_field) \ ++ \ ++ /******************************/ \ ++ /* Threads (NOTE: incomplete) */ \ ++ /******************************/ \ ++ nonstatic_field(OSThread, _thread_id, pid_t) \ ++ nonstatic_field(OSThread, _pthread_id, pthread_t) ++ ++ ++#define VM_TYPES_OS_CPU(declare_type, declare_toplevel_type, declare_oop_type, declare_integer_type, declare_unsigned_integer_type, declare_c1_toplevel_type, declare_c2_type, declare_c2_toplevel_type) \ ++ \ ++ /**********************/ \ ++ /* Posix Thread IDs */ \ ++ /**********************/ \ ++ \ ++ declare_integer_type(pid_t) \ ++ declare_unsigned_integer_type(pthread_t) ++ ++#define VM_INT_CONSTANTS_OS_CPU(declare_constant, declare_preprocessor_constant, declare_c1_constant, declare_c2_constant, declare_c2_preprocessor_constant) ++ ++#define VM_LONG_CONSTANTS_OS_CPU(declare_constant, declare_preprocessor_constant, declare_c1_constant, declare_c2_constant, declare_c2_preprocessor_constant) ++ ++#endif // OS_CPU_LINUX_LOONGARCH_VM_VMSTRUCTS_LINUX_LOONGARCH_HPP +diff --git a/hotspot/src/os_cpu/linux_mips/vm/assembler_linux_mips.cpp b/hotspot/src/os_cpu/linux_mips/vm/assembler_linux_mips.cpp +new file mode 100644 +index 0000000000..4ba53d9341 +--- /dev/null ++++ b/hotspot/src/os_cpu/linux_mips/vm/assembler_linux_mips.cpp +@@ -0,0 +1,111 @@ ++/* ++ * Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2015, 2022, Loongson Technology. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ * ++ */ ++ ++#include "precompiled.hpp" ++#include "asm/macroAssembler.hpp" ++#include "asm/macroAssembler.inline.hpp" ++#include "runtime/os.hpp" ++#include "runtime/threadLocalStorage.hpp" ++ ++#define A0 RA0 ++#define A1 RA1 ++#define A2 RA2 ++#define A3 RA3 ++#define A4 RA4 ++#define A5 RA5 ++#define A6 RA6 ++#define A7 RA7 ++#define T0 RT0 ++#define T1 RT1 ++#define T2 RT2 ++#define T3 RT3 ++#define T8 RT8 ++#define T9 RT9 ++ ++void MacroAssembler::get_thread(Register thread) { ++#ifdef MINIMIZE_RAM_USAGE ++// ++// In MIPS64, we don't use full 64-bit address space. ++// Only a small range is actually used. ++// ++// Example: ++// $ cat /proc/13352/maps ++// 120000000-120010000 r-xp 00000000 08:01 41077 /mnt/openjdk6-mips-full/build/linux-mips64/j2sdk-image/bin/java ++// 12001c000-120020000 rw-p 0000c000 08:01 41077 /mnt/openjdk6-mips-full/build/linux-mips64/j2sdk-image/bin/java ++// 120020000-1208dc000 rwxp 00000000 00:00 0 [heap] ++// 555d574000-555d598000 r-xp 00000000 08:01 2073768 /lib/ld-2.12.so ++// 555d598000-555d59c000 rw-p 00000000 00:00 0 ++// ...... ++// 558b1f8000-558b23c000 rwxp 00000000 00:00 0 ++// 558b23c000-558b248000 ---p 00000000 00:00 0 ++// 558b248000-558b28c000 rwxp 00000000 00:00 0 ++// ffff914000-ffff94c000 rwxp 00000000 00:00 0 [stack] ++// ffffffc000-10000000000 r-xp 00000000 00:00 0 [vdso] ++// ++// All stacks are positioned at 0x55________. ++// Therefore, we can utilize the same algorithm used in 32-bit. ++ // int index = ((uintptr_t)p >> PAGE_SHIFT) & ((1UL << (SP_BITLENGTH - PAGE_SHIFT)) - 1); ++ // Thread* thread = _sp_map[index]; ++ Register tmp; ++ ++ if (thread == AT) ++ tmp = T9; ++ else ++ tmp = AT; ++ ++ move(thread, SP); ++ shr(thread, PAGE_SHIFT); ++ ++ push(tmp); ++ li(tmp, ((1UL << (SP_BITLENGTH - PAGE_SHIFT)) - 1)); ++ andr(thread, thread, tmp); ++ shl(thread, Address::times_ptr); // sizeof(Thread *) ++ li48(tmp, (long)ThreadLocalStorage::sp_map_addr()); ++ addu(tmp, tmp, thread); ++ ld_ptr(thread, tmp, 0); ++ pop(tmp); ++#else ++ if (thread != V0) { ++ push(V0); ++ } ++ pushad_except_v0(); ++ ++ move(A0, ThreadLocalStorage::thread_index()); ++ push(S5); ++ move(S5, SP); ++ move(AT, -StackAlignmentInBytes); ++ andr(SP, SP, AT); ++ call(CAST_FROM_FN_PTR(address, pthread_getspecific)); ++ delayed()->nop(); ++ move(SP, S5); ++ pop(S5); ++ ++ popad_except_v0(); ++ if (thread != V0) { ++ move(thread, V0); ++ pop(V0); ++ } ++#endif // MINIMIZE_RAM_USAGE ++} +diff --git a/hotspot/src/os_cpu/linux_mips/vm/atomic_linux_mips.inline.hpp b/hotspot/src/os_cpu/linux_mips/vm/atomic_linux_mips.inline.hpp +new file mode 100644 +index 0000000000..1c7ad605e9 +--- /dev/null ++++ b/hotspot/src/os_cpu/linux_mips/vm/atomic_linux_mips.inline.hpp +@@ -0,0 +1,258 @@ ++/* ++ * Copyright (c) 1999, 2013, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2015, 2022, Loongson Technology. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ * ++ */ ++ ++#ifndef OS_CPU_LINUX_MIPS_VM_ATOMIC_LINUX_MIPS_INLINE_HPP ++#define OS_CPU_LINUX_MIPS_VM_ATOMIC_LINUX_MIPS_INLINE_HPP ++ ++#include "orderAccess_linux_mips.inline.hpp" ++#include "runtime/atomic.hpp" ++#include "runtime/os.hpp" ++#include "vm_version_mips.hpp" ++ ++// Implementation of class atomic ++ ++inline void Atomic::store (jbyte store_value, jbyte* dest) { *dest = store_value; } ++inline void Atomic::store (jshort store_value, jshort* dest) { *dest = store_value; } ++inline void Atomic::store (jint store_value, jint* dest) { *dest = store_value; } ++inline void Atomic::store (jlong store_value, jlong* dest) { *dest = store_value; } ++inline void Atomic::store_ptr(intptr_t store_value, intptr_t* dest) { *dest = store_value; } ++inline void Atomic::store_ptr(void* store_value, void* dest) { *(void**)dest = store_value; } ++ ++inline void Atomic::store (jbyte store_value, volatile jbyte* dest) { *dest = store_value; } ++inline void Atomic::store (jshort store_value, volatile jshort* dest) { *dest = store_value; } ++inline void Atomic::store (jint store_value, volatile jint* dest) { *dest = store_value; } ++inline void Atomic::store (jlong store_value, volatile jlong* dest) { *dest = store_value; } ++inline void Atomic::store_ptr(intptr_t store_value, volatile intptr_t* dest) { *dest = store_value; } ++inline void Atomic::store_ptr(void* store_value, volatile void* dest) { *(void**)dest = store_value; } ++ ++inline jlong Atomic::load (volatile jlong* src) { return *src; } ++ ++///////////implementation of Atomic::add*///////////////// ++inline jint Atomic::add (jint add_value, volatile jint* dest) { ++ jint __ret, __tmp; ++ __asm__ __volatile__ ( ++ " .set push\n\t" ++ " .set mips64\n\t" ++ " .set noreorder\n\t" ++ ++ "1: sync \n\t" ++ " ll %[__ret], %[__dest] \n\t" ++ " addu %[__tmp], %[__val], %[__ret] \n\t" ++ " sc %[__tmp], %[__dest] \n\t" ++ " beqz %[__tmp], 1b \n\t" ++ " nop \n\t" ++ ++ " .set pop\n\t" ++ ++ : [__ret] "=&r" (__ret), [__tmp] "=&r" (__tmp) ++ : [__dest] "m" (*(volatile jint*)dest), [__val] "r" (add_value) ++ : "memory" ++ ); ++ ++ return add_value + __ret; ++} ++ ++inline intptr_t Atomic::add_ptr (intptr_t add_value, volatile intptr_t* dest) { ++ jint __ret, __tmp; ++ __asm__ __volatile__ ( ++ " .set push\n\t" ++ " .set mips64\n\t" ++ " .set noreorder\n\t" ++ ++ "1: sync \n\t" ++ " lld %[__ret], %[__dest] \n\t" ++ " daddu %[__tmp], %[__val], %[__ret] \n\t" ++ " scd %[__tmp], %[__dest] \n\t" ++ " beqz %[__tmp], 1b \n\t" ++ " nop \n\t" ++ ++ " .set pop\n\t" ++ ++ : [__ret] "=&r" (__ret), [__tmp] "=&r" (__tmp) ++ : [__dest] "m" (*(volatile jint*)dest), [__val] "r" (add_value) ++ : "memory" ++ ); ++ ++ return add_value + __ret; ++} ++ ++inline void* Atomic::add_ptr (intptr_t add_value, volatile void* dest) { ++ return (void*)add_ptr((intptr_t)add_value, (volatile intptr_t*)dest); ++} ++ ++///////////implementation of Atomic::inc*///////////////// ++inline void Atomic::inc (volatile jint* dest) { (void)add(1, dest); } ++inline void Atomic::inc_ptr (volatile intptr_t* dest) { (void)add_ptr(1, dest); } ++inline void Atomic::inc_ptr (volatile void* dest) { (void)inc_ptr((volatile intptr_t*)dest); } ++ ++///////////implementation of Atomic::dec*///////////////// ++inline void Atomic::dec (volatile jint* dest) { (void)add(-1, dest); } ++inline void Atomic::dec_ptr (volatile intptr_t* dest) { (void)add_ptr(-1, dest); } ++inline void Atomic::dec_ptr (volatile void* dest) { (void)dec_ptr((volatile intptr_t*)dest); } ++ ++ ++///////////implementation of Atomic::xchg*///////////////// ++inline jint Atomic::xchg (jint exchange_value, volatile jint* dest) { ++ jint __ret, __tmp; ++ ++ __asm__ __volatile__ ( ++ " .set push\n\t" ++ " .set mips64\n\t" ++ " .set noreorder\n\t" ++ ++ "1: sync\n\t" ++ " ll %[__ret], %[__dest] \n\t" ++ " move %[__tmp], %[__val] \n\t" ++ " sc %[__tmp], %[__dest] \n\t" ++ " beqz %[__tmp], 1b \n\t" ++ " nop \n\t" ++ ++ " .set pop\n\t" ++ ++ : [__ret] "=&r" (__ret), [__tmp] "=&r" (__tmp) ++ : [__dest] "m" (*(volatile jint*)dest), [__val] "r" (exchange_value) ++ : "memory" ++ ); ++ ++ return __ret; ++} ++ ++inline intptr_t Atomic::xchg_ptr(intptr_t exchange_value, volatile intptr_t* dest) { ++ intptr_t __ret, __tmp; ++ __asm__ __volatile__ ( ++ " .set push\n\t" ++ " .set mips64\n\t" ++ " .set noreorder\n\t" ++ ++ "1: sync\n\t" ++ " lld %[__ret], %[__dest] \n\t" ++ " move %[__tmp], %[__val] \n\t" ++ " scd %[__tmp], %[__dest] \n\t" ++ " beqz %[__tmp], 1b \n\t" ++ " nop \n\t" ++ ++ " .set pop\n\t" ++ ++ : [__ret] "=&r" (__ret), [__tmp] "=&r" (__tmp) ++ : [__dest] "m" (*(volatile intptr_t*)dest), [__val] "r" (exchange_value) ++ : "memory" ++ ); ++ return __ret; ++} ++ ++inline void* Atomic::xchg_ptr(void* exchange_value, volatile void* dest) { ++ return (void*)xchg_ptr((intptr_t)exchange_value, (volatile intptr_t*)dest); ++} ++ ++///////////implementation of Atomic::cmpxchg*///////////////// ++inline jint Atomic::cmpxchg (jint exchange_value, volatile jint* dest, jint compare_value) { ++ jint __prev, __cmp; ++ ++ __asm__ __volatile__ ( ++ " .set push\n\t" ++ " .set mips64\n\t" ++ " .set noreorder\n\t" ++ ++ "1:sync \n\t" ++ " ll %[__prev], %[__dest] \n\t" ++ " bne %[__prev], %[__old], 2f \n\t" ++ " move %[__cmp], $0 \n\t" ++ " move %[__cmp], %[__new] \n\t" ++ " sc %[__cmp], %[__dest] \n\t" ++ " beqz %[__cmp], 1b \n\t" ++ " nop \n\t" ++ "2: \n\t" ++ " sync \n\t" ++ ++ " .set pop\n\t" ++ ++ : [__prev] "=&r" (__prev), [__cmp] "=&r" (__cmp) ++ : [__dest] "m" (*(volatile jint*)dest), [__old] "r" (compare_value), [__new] "r" (exchange_value) ++ : "memory" ++ ); ++ ++ return __prev; ++} ++ ++inline jlong Atomic::cmpxchg (jlong exchange_value, volatile jlong* dest, jlong compare_value) { ++ jlong __prev, __cmp; ++ ++ __asm__ __volatile__ ( ++ " .set push\n\t" ++ " .set mips64\n\t" ++ " .set noreorder\n\t" ++ ++ "1:sync \n\t" ++ " lld %[__prev], %[__dest] \n\t" ++ " bne %[__prev], %[__old], 2f \n\t" ++ " move %[__cmp], $0 \n\t" ++ " move %[__cmp], %[__new] \n\t" ++ " scd %[__cmp], %[__dest] \n\t" ++ " beqz %[__cmp], 1b \n\t" ++ " nop \n\t" ++ "2: \n\t" ++ " sync \n\t" ++ ++ " .set pop\n\t" ++ ++ : [__prev] "=&r" (__prev), [__cmp] "=&r" (__cmp) ++ : [__dest] "m" (*(volatile jlong*)dest), [__old] "r" (compare_value), [__new] "r" (exchange_value) ++ : "memory" ++ ); ++ return __prev; ++} ++ ++inline intptr_t Atomic::cmpxchg_ptr(intptr_t exchange_value, volatile intptr_t* dest, intptr_t compare_value) { ++ intptr_t __prev, __cmp; ++ __asm__ __volatile__ ( ++ " .set push \n\t" ++ " .set mips64\n\t\t" ++ " .set noreorder\n\t" ++ ++ "1:sync \n\t" ++ " lld %[__prev], %[__dest] \n\t" ++ " bne %[__prev], %[__old], 2f \n\t" ++ " move %[__cmp], $0 \n\t" ++ " move %[__cmp], %[__new] \n\t" ++ " scd %[__cmp], %[__dest] \n\t" ++ " beqz %[__cmp], 1b \n\t" ++ " nop \n\t" ++ "2: \n\t" ++ " sync \n\t" ++ " .set pop \n\t" ++ ++ : [__prev] "=&r" (__prev), [__cmp] "=&r" (__cmp) ++ : [__dest] "m" (*(volatile intptr_t*)dest), [__old] "r" (compare_value), [__new] "r" (exchange_value) ++ : "memory" ++ ); ++ ++ return __prev; ++} ++ ++inline void* Atomic::cmpxchg_ptr(void* exchange_value, volatile void* dest, void* compare_value) { ++ return (void*)cmpxchg_ptr((intptr_t)exchange_value, (volatile intptr_t*)dest, (intptr_t)compare_value); ++} ++ ++#endif // OS_CPU_LINUX_MIPS_VM_ATOMIC_LINUX_MIPS_INLINE_HPP +diff --git a/hotspot/src/os_cpu/linux_mips/vm/bytes_linux_mips.inline.hpp b/hotspot/src/os_cpu/linux_mips/vm/bytes_linux_mips.inline.hpp +new file mode 100644 +index 0000000000..5b5cd10aa5 +--- /dev/null ++++ b/hotspot/src/os_cpu/linux_mips/vm/bytes_linux_mips.inline.hpp +@@ -0,0 +1,37 @@ ++/* ++ * Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2015, 2016, Loongson Technology. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ * ++ */ ++ ++#ifndef OS_CPU_LINUX_MIPS_VM_BYTES_LINUX_MIPS_INLINE_HPP ++#define OS_CPU_LINUX_MIPS_VM_BYTES_LINUX_MIPS_INLINE_HPP ++ ++#include ++ ++// Efficient swapping of data bytes from Java byte ++// ordering to native byte ordering and vice versa. ++inline u2 Bytes::swap_u2(u2 x) { return bswap_16(x); } ++inline u4 Bytes::swap_u4(u4 x) { return bswap_32(x); } ++inline u8 Bytes::swap_u8(u8 x) { return bswap_64(x); } ++ ++#endif // OS_CPU_LINUX_MIPS_VM_BYTES_LINUX_MIPS_INLINE_HPP +diff --git a/hotspot/src/os_cpu/linux_mips/vm/copy_linux_mips.inline.hpp b/hotspot/src/os_cpu/linux_mips/vm/copy_linux_mips.inline.hpp +new file mode 100644 +index 0000000000..73ac34501b +--- /dev/null ++++ b/hotspot/src/os_cpu/linux_mips/vm/copy_linux_mips.inline.hpp +@@ -0,0 +1,125 @@ ++/* ++ * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2015, 2016, Loongson Technology. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ * ++ */ ++ ++#ifndef OS_CPU_LINUX_MIPS_VM_COPY_LINUX_MIPS_INLINE_HPP ++#define OS_CPU_LINUX_MIPS_VM_COPY_LINUX_MIPS_INLINE_HPP ++ ++static void pd_conjoint_words(HeapWord* from, HeapWord* to, size_t count) { ++ (void)memmove(to, from, count * HeapWordSize); ++} ++ ++static void pd_disjoint_words(HeapWord* from, HeapWord* to, size_t count) { ++ switch (count) { ++ case 8: to[7] = from[7]; ++ case 7: to[6] = from[6]; ++ case 6: to[5] = from[5]; ++ case 5: to[4] = from[4]; ++ case 4: to[3] = from[3]; ++ case 3: to[2] = from[2]; ++ case 2: to[1] = from[1]; ++ case 1: to[0] = from[0]; ++ case 0: break; ++ default: ++ (void)memcpy(to, from, count * HeapWordSize); ++ break; ++ } ++} ++ ++static void pd_disjoint_words_atomic(HeapWord* from, HeapWord* to, size_t count) { ++ switch (count) { ++ case 8: to[7] = from[7]; ++ case 7: to[6] = from[6]; ++ case 6: to[5] = from[5]; ++ case 5: to[4] = from[4]; ++ case 4: to[3] = from[3]; ++ case 3: to[2] = from[2]; ++ case 2: to[1] = from[1]; ++ case 1: to[0] = from[0]; ++ case 0: break; ++ default: ++ while (count-- > 0) { ++ *to++ = *from++; ++ } ++ break; ++ } ++} ++ ++static void pd_aligned_conjoint_words(HeapWord* from, HeapWord* to, size_t count) { ++ pd_conjoint_words(from, to, count); ++} ++ ++static void pd_aligned_disjoint_words(HeapWord* from, HeapWord* to, size_t count) { ++ pd_disjoint_words(from, to, count); ++} ++ ++static void pd_conjoint_bytes(void* from, void* to, size_t count) { ++ (void)memmove(to, from, count); ++} ++ ++static void pd_conjoint_bytes_atomic(void* from, void* to, size_t count) { ++ pd_conjoint_bytes(from, to, count); ++} ++ ++static void pd_conjoint_jshorts_atomic(jshort* from, jshort* to, size_t count) { ++ copy_conjoint_atomic(from, to, count); ++} ++ ++static void pd_conjoint_jints_atomic(jint* from, jint* to, size_t count) { ++ copy_conjoint_atomic(from, to, count); ++} ++ ++static void pd_conjoint_jlongs_atomic(jlong* from, jlong* to, size_t count) { ++ copy_conjoint_atomic(from, to, count); ++} ++ ++static void pd_conjoint_oops_atomic(oop* from, oop* to, size_t count) { ++ //assert(!UseCompressedOops, "foo!"); ++ assert(HeapWordSize == BytesPerOop, "heapwords and oops must be the same size"); ++ copy_conjoint_atomic(from, to, count); ++} ++ ++static void pd_arrayof_conjoint_bytes(HeapWord* from, HeapWord* to, size_t count) { ++ pd_conjoint_bytes_atomic(from, to, count); ++} ++ ++static void pd_arrayof_conjoint_jshorts(HeapWord* from, HeapWord* to, size_t count) { ++ pd_conjoint_jshorts_atomic((jshort*)from, (jshort*)to, count); ++} ++ ++static void pd_arrayof_conjoint_jints(HeapWord* from, HeapWord* to, size_t count) { ++ pd_conjoint_jints_atomic((jint*)from, (jint*)to, count); ++} ++ ++static void pd_arrayof_conjoint_jlongs(HeapWord* from, HeapWord* to, size_t count) { ++ pd_conjoint_jlongs_atomic((jlong*)from, (jlong*)to, count); ++} ++ ++static void pd_arrayof_conjoint_oops(HeapWord* from, HeapWord* to, size_t count) { ++ //assert(!UseCompressedOops, "foo!"); ++ assert(BytesPerLong == BytesPerOop, "jlongs and oops must be the same size"); ++ pd_conjoint_oops_atomic((oop*)from, (oop*)to, count); ++} ++ ++#endif // OS_CPU_LINUX_MIPS_VM_COPY_LINUX_MIPS_INLINE_HPP +diff --git a/hotspot/src/os_cpu/linux_mips/vm/globals_linux_mips.hpp b/hotspot/src/os_cpu/linux_mips/vm/globals_linux_mips.hpp +new file mode 100644 +index 0000000000..f1599ac5f1 +--- /dev/null ++++ b/hotspot/src/os_cpu/linux_mips/vm/globals_linux_mips.hpp +@@ -0,0 +1,51 @@ ++/* ++ * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2015, 2018, Loongson Technology. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ * ++ */ ++ ++#ifndef OS_CPU_LINUX_MIPS_VM_GLOBALS_LINUX_MIPS_HPP ++#define OS_CPU_LINUX_MIPS_VM_GLOBALS_LINUX_MIPS_HPP ++ ++// Sets the default values for platform dependent flags used by the runtime system. ++// (see globals.hpp) ++ ++define_pd_global(bool, DontYieldALot, false); ++#ifdef MIPS64 ++define_pd_global(intx, ThreadStackSize, 1024); // 0 => use system default ++define_pd_global(intx, VMThreadStackSize, 1024); ++#else ++// ThreadStackSize 320 allows a couple of test cases to run while ++// keeping the number of threads that can be created high. System ++// default ThreadStackSize appears to be 512 which is too big. ++define_pd_global(intx, ThreadStackSize, 320); ++define_pd_global(intx, VMThreadStackSize, 512); ++#endif // MIPS64 ++ ++define_pd_global(intx, CompilerThreadStackSize, 0); ++ ++define_pd_global(uintx,JVMInvokeMethodSlack, 8192); ++ ++// Used on 64 bit platforms for UseCompressedOops base address ++define_pd_global(uintx,HeapBaseMinAddress, 2*G); ++ ++#endif // OS_CPU_LINUX_MIPS_VM_GLOBALS_LINUX_MIPS_HPP +diff --git a/hotspot/src/os_cpu/linux_mips/vm/linux_mips.ad b/hotspot/src/os_cpu/linux_mips/vm/linux_mips.ad +new file mode 100644 +index 0000000000..5e38996ffa +--- /dev/null ++++ b/hotspot/src/os_cpu/linux_mips/vm/linux_mips.ad +@@ -0,0 +1,153 @@ ++// ++// Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved. ++// Copyright (c) 2015, 2016, Loongson Technology. All rights reserved. ++// DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++// ++// This code is free software; you can redistribute it and/or modify it ++// under the terms of the GNU General Public License version 2 only, as ++// published by the Free Software Foundation. ++// ++// This code is distributed in the hope that it will be useful, but WITHOUT ++// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++// version 2 for more details (a copy is included in the LICENSE file that ++// accompanied this code). ++// ++// You should have received a copy of the GNU General Public License version ++// 2 along with this work; if not, write to the Free Software Foundation, ++// Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++// ++// Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++// or visit www.oracle.com if you need additional information or have any ++// questions. ++// ++// ++ ++// mips32/godson2 Linux Architecture Description File ++ ++//----------OS-DEPENDENT ENCODING BLOCK---------------------------------------- ++// This block specifies the encoding classes used by the compiler to ++// output byte streams. Encoding classes generate functions which are ++// called by Machine Instruction Nodes in order to generate the bit ++// encoding of the instruction. Operands specify their base encoding ++// interface with the interface keyword. There are currently ++// supported four interfaces, REG_INTER, CONST_INTER, MEMORY_INTER, & ++// COND_INTER. REG_INTER causes an operand to generate a function ++// which returns its register number when queried. CONST_INTER causes ++// an operand to generate a function which returns the value of the ++// constant when queried. MEMORY_INTER causes an operand to generate ++// four functions which return the Base Register, the Index Register, ++// the Scale Value, and the Offset Value of the operand when queried. ++// COND_INTER causes an operand to generate six functions which return ++// the encoding code (ie - encoding bits for the instruction) ++// associated with each basic boolean condition for a conditional ++// instruction. Instructions specify two basic values for encoding. ++// They use the ins_encode keyword to specify their encoding class ++// (which must be one of the class names specified in the encoding ++// block), and they use the opcode keyword to specify, in order, their ++// primary, secondary, and tertiary opcode. Only the opcode sections ++// which a particular instruction needs for encoding need to be ++// specified. ++encode %{ ++ // Build emit functions for each basic byte or larger field in the intel ++ // encoding scheme (opcode, rm, sib, immediate), and call them from C++ ++ // code in the enc_class source block. Emit functions will live in the ++ // main source block for now. In future, we can generalize this by ++ // adding a syntax that specifies the sizes of fields in an order, ++ // so that the adlc can build the emit functions automagically ++ ++ enc_class linux_breakpoint ++ %{ ++ MacroAssembler* masm = new MacroAssembler(&cbuf); ++ masm->call(CAST_FROM_FN_PTR(address, os::breakpoint), relocInfo::runtime_call_type); ++ %} ++ ++ enc_class call_epilog ++ %{ ++ if (VerifyStackAtCalls) { ++ // Check that stack depth is unchanged: find majik cookie on stack ++ int framesize = ra_->reg2offset_unchecked(OptoReg::add(ra_->_matcher._old_SP,-2)); ++ if(framesize >= 128) { ++ emit_opcode(cbuf, 0x81); // cmp [esp+0],0xbadb1ood ++ emit_d8(cbuf,0xBC); ++ emit_d8(cbuf,0x24); ++ emit_d32(cbuf,framesize); // Find majik cookie from ESP ++ emit_d32(cbuf, 0xbadb100d); ++ } ++ else { ++ emit_opcode(cbuf, 0x81); // cmp [esp+0],0xbadb1ood ++ emit_d8(cbuf,0x7C); ++ emit_d8(cbuf,0x24); ++ emit_d8(cbuf,framesize); // Find majik cookie from ESP ++ emit_d32(cbuf, 0xbadb100d); ++ } ++ // jmp EQ around INT3 ++ // QQQ TODO ++ const int jump_around = 5; // size of call to breakpoint, 1 for CC ++ emit_opcode(cbuf, 0x74); ++ emit_d8(cbuf, jump_around); ++ // QQQ temporary ++ emit_break(cbuf); ++ // Die if stack mismatch ++ // emit_opcode(cbuf,0xCC); ++ } ++ %} ++ ++%} ++ ++// INSTRUCTIONS -- Platform dependent ++ ++//----------OS and Locking Instructions---------------------------------------- ++ ++// This name is KNOWN by the ADLC and cannot be changed. ++// The ADLC forces a 'TypeRawPtr::BOTTOM' output type ++// for this guy. ++instruct tlsLoadP(eAXRegP dst, eFlagsReg cr) %{ ++%{ ++ match(Set dst (ThreadLocal)); ++ effect(DEF dst, KILL cr); ++ ++ format %{ "MOV EAX, Thread::current()" %} ++ ins_encode( linux_tlsencode(dst) ); ++ ins_pipe( ialu_reg_fat ); ++%} ++ ++// Die now ++instruct ShouldNotReachHere() ++%{ ++ match(Halt); ++ ++ // Use the following format syntax ++ format %{ "int3\t# ShouldNotReachHere" %} ++ // QQQ TODO for now call breakpoint ++ // opcode(0xCC); ++ // ins_encode(Opc); ++ ins_encode(linux_breakpoint); ++ ins_pipe(pipe_slow); ++%} ++ ++ ++// Platform dependent source ++ ++source ++%{ ++// emit an interrupt that is caught by the debugger ++void emit_break(CodeBuffer& cbuf) { ++ // Debugger doesn't really catch this but best we can do so far QQQ ++#define __ masm. ++ __ lui(T9, Assembler::split_high((int)os::breakpoint)); ++ __ addiu(T9, T9, Assembler::split_low((int)os::breakpoint)); ++ __ jalr(T9); ++ __ delayed()->nop(); ++} ++ ++void MachBreakpointNode::emit(CodeBuffer& cbuf, PhaseRegAlloc* ra_) const { ++ emit_break(cbuf); ++} ++ ++uint MachBreakpointNode::size(PhaseRegAlloc* ra_) const { ++ //return 5; ++ return 16; ++} ++ ++%} +diff --git a/hotspot/src/os_cpu/linux_mips/vm/linux_mips.s b/hotspot/src/os_cpu/linux_mips/vm/linux_mips.s +new file mode 100644 +index 0000000000..f87fbf265d +--- /dev/null ++++ b/hotspot/src/os_cpu/linux_mips/vm/linux_mips.s +@@ -0,0 +1,25 @@ ++# ++# Copyright (c) 2004, 2013, Oracle and/or its affiliates. All rights reserved. ++# Copyright (c) 2015, 2017, Loongson Technology. All rights reserved. ++# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++# ++# This code is free software; you can redistribute it and/or modify it ++# under the terms of the GNU General Public License version 2 only, as ++# published by the Free Software Foundation. ++# ++# This code is distributed in the hope that it will be useful, but WITHOUT ++# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++# version 2 for more details (a copy is included in the LICENSE file that ++# accompanied this code). ++# ++# You should have received a copy of the GNU General Public License version ++# 2 along with this work; if not, write to the Free Software Foundation, ++# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++# ++# Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++# or visit www.oracle.com if you need additional information or have any ++# questions. ++# ++ ++ +diff --git a/hotspot/src/os_cpu/linux_mips/vm/linux_mips_64.ad b/hotspot/src/os_cpu/linux_mips/vm/linux_mips_64.ad +new file mode 100644 +index 0000000000..ca4d094738 +--- /dev/null ++++ b/hotspot/src/os_cpu/linux_mips/vm/linux_mips_64.ad +@@ -0,0 +1,50 @@ ++// ++// Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved. ++// Copyright (c) 2015, 2016, Loongson Technology. All rights reserved. ++// DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++// ++// This code is free software; you can redistribute it and/or modify it ++// under the terms of the GNU General Public License version 2 only, as ++// published by the Free Software Foundation. ++// ++// This code is distributed in the hope that it will be useful, but WITHOUT ++// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++// version 2 for more details (a copy is included in the LICENSE file that ++// accompanied this code). ++// ++// You should have received a copy of the GNU General Public License version ++// 2 along with this work; if not, write to the Free Software Foundation, ++// Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++// ++// Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++// or visit www.oracle.com if you need additional information or have any ++// questions. ++// ++// ++ ++// AMD64 Linux Architecture Description File ++ ++//----------OS-DEPENDENT ENCODING BLOCK---------------------------------------- ++// This block specifies the encoding classes used by the compiler to ++// output byte streams. Encoding classes generate functions which are ++// called by Machine Instruction Nodes in order to generate the bit ++// encoding of the instruction. Operands specify their base encoding ++// interface with the interface keyword. There are currently ++// supported four interfaces, REG_INTER, CONST_INTER, MEMORY_INTER, & ++// COND_INTER. REG_INTER causes an operand to generate a function ++// which returns its register number when queried. CONST_INTER causes ++// an operand to generate a function which returns the value of the ++// constant when queried. MEMORY_INTER causes an operand to generate ++// four functions which return the Base Register, the Index Register, ++// the Scale Value, and the Offset Value of the operand when queried. ++// COND_INTER causes an operand to generate six functions which return ++// the encoding code (ie - encoding bits for the instruction) ++// associated with each basic boolean condition for a conditional ++// instruction. Instructions specify two basic values for encoding. ++// They use the ins_encode keyword to specify their encoding class ++// (which must be one of the class names specified in the encoding ++// block), and they use the opcode keyword to specify, in order, their ++// primary, secondary, and tertiary opcode. Only the opcode sections ++// which a particular instruction needs for encoding need to be ++// specified. +diff --git a/hotspot/src/os_cpu/linux_mips/vm/orderAccess_linux_mips.inline.hpp b/hotspot/src/os_cpu/linux_mips/vm/orderAccess_linux_mips.inline.hpp +new file mode 100644 +index 0000000000..c9bc169aa5 +--- /dev/null ++++ b/hotspot/src/os_cpu/linux_mips/vm/orderAccess_linux_mips.inline.hpp +@@ -0,0 +1,115 @@ ++/* ++ * Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2015, 2022, Loongson Technology. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ * ++ */ ++ ++#ifndef OS_CPU_LINUX_MIPS_VM_ORDERACCESS_LINUX_MIPS_INLINE_HPP ++#define OS_CPU_LINUX_MIPS_VM_ORDERACCESS_LINUX_MIPS_INLINE_HPP ++ ++#include "runtime/atomic.hpp" ++#include "runtime/orderAccess.hpp" ++#include "runtime/os.hpp" ++#include "vm_version_mips.hpp" ++ ++#define inlasm_sync() if (os::is_ActiveCoresMP()) \ ++ __asm__ __volatile__ ("nop" : : : "memory"); \ ++ else \ ++ __asm__ __volatile__ ("sync" : : : "memory"); ++ ++inline void OrderAccess::loadload() { inlasm_sync(); } ++inline void OrderAccess::storestore() { inlasm_sync(); } ++inline void OrderAccess::loadstore() { inlasm_sync(); } ++inline void OrderAccess::storeload() { inlasm_sync(); } ++ ++inline void OrderAccess::acquire() { inlasm_sync(); } ++inline void OrderAccess::release() { inlasm_sync(); } ++inline void OrderAccess::fence() { inlasm_sync(); } ++ ++//implementation of load_acquire ++inline jbyte OrderAccess::load_acquire(volatile jbyte* p) { jbyte data = *p; acquire(); return data; } ++inline jshort OrderAccess::load_acquire(volatile jshort* p) { jshort data = *p; acquire(); return data; } ++inline jint OrderAccess::load_acquire(volatile jint* p) { jint data = *p; acquire(); return data; } ++inline jlong OrderAccess::load_acquire(volatile jlong* p) { jlong tmp = *p; acquire(); return tmp; } ++inline jubyte OrderAccess::load_acquire(volatile jubyte* p) { jubyte data = *p; acquire(); return data; } ++inline jushort OrderAccess::load_acquire(volatile jushort* p) { jushort data = *p; acquire(); return data; } ++inline juint OrderAccess::load_acquire(volatile juint* p) { juint data = *p; acquire(); return data; } ++inline julong OrderAccess::load_acquire(volatile julong* p) { julong tmp = *p; acquire(); return tmp; } ++inline jfloat OrderAccess::load_acquire(volatile jfloat* p) { jfloat data = *p; acquire(); return data; } ++inline jdouble OrderAccess::load_acquire(volatile jdouble* p) { jdouble tmp = *p; acquire(); return tmp; } ++ ++//implementation of load_ptr_acquire ++inline intptr_t OrderAccess::load_ptr_acquire(volatile intptr_t* p) { intptr_t data = *p; acquire(); return data; } ++inline void* OrderAccess::load_ptr_acquire(volatile void* p) { void *data = *(void* volatile *)p; acquire(); return data; } ++inline void* OrderAccess::load_ptr_acquire(const volatile void* p) { void *data = *(void* volatile *)p; acquire(); return data; } ++ ++//implementation of release_store ++inline void OrderAccess::release_store(volatile jbyte* p, jbyte v) { release(); *p = v; } ++inline void OrderAccess::release_store(volatile jshort* p, jshort v) { release(); *p = v; } ++inline void OrderAccess::release_store(volatile jint* p, jint v) { release(); *p = v; } ++inline void OrderAccess::release_store(volatile jlong* p, jlong v) { release(); *p = v; } ++inline void OrderAccess::release_store(volatile jubyte* p, jubyte v) { release(); *p = v; } ++inline void OrderAccess::release_store(volatile jushort* p, jushort v) { release(); *p = v; } ++inline void OrderAccess::release_store(volatile juint* p, juint v) { release(); *p = v; } ++inline void OrderAccess::release_store(volatile julong* p, julong v) { release(); *p = v; } ++inline void OrderAccess::release_store(volatile jfloat* p, jfloat v) { release(); *p = v; } ++inline void OrderAccess::release_store(volatile jdouble* p, jdouble v) { release(); *p = v; } ++ ++//implementation of release_store_ptr ++inline void OrderAccess::release_store_ptr(volatile intptr_t* p, intptr_t v) { release(); *p = v; } ++inline void OrderAccess::release_store_ptr(volatile void* p, void* v) { release(); *(void* volatile *)p = v; } ++ ++//implementation of store_fence ++inline void OrderAccess::store_fence(jbyte* p, jbyte v) { *p = v; fence(); } ++inline void OrderAccess::store_fence(jshort* p, jshort v) { *p = v; fence(); } ++inline void OrderAccess::store_fence(jint* p, jint v) { *p = v; fence(); } ++inline void OrderAccess::store_fence(jlong* p, jlong v) { *p = v; fence(); } ++inline void OrderAccess::store_fence(jubyte* p, jubyte v) { *p = v; fence(); } ++inline void OrderAccess::store_fence(jushort* p, jushort v) { *p = v; fence(); } ++inline void OrderAccess::store_fence(juint* p, juint v) { *p = v; fence(); } ++inline void OrderAccess::store_fence(julong* p, julong v) { *p = v; fence(); } ++inline void OrderAccess::store_fence(jfloat* p, jfloat v) { *p = v; fence(); } ++inline void OrderAccess::store_fence(jdouble* p, jdouble v) { *p = v; fence(); } ++ ++//implementation of store_ptr_fence ++inline void OrderAccess::store_ptr_fence(intptr_t* p, intptr_t v) { *p = v; fence(); } ++inline void OrderAccess::store_ptr_fence(void** p, void* v) { *p = v; fence(); } ++ ++//implementation of release_store_fence ++inline void OrderAccess::release_store_fence(volatile jbyte* p, jbyte v) { release_store(p, v); fence(); } ++inline void OrderAccess::release_store_fence(volatile jshort* p, jshort v) { release_store(p, v); fence(); } ++inline void OrderAccess::release_store_fence(volatile jint* p, jint v) { release_store(p, v); fence(); } ++inline void OrderAccess::release_store_fence(volatile jlong* p, jlong v) { release_store(p, v); fence(); } ++inline void OrderAccess::release_store_fence(volatile jubyte* p, jubyte v) { release_store(p, v); fence(); } ++inline void OrderAccess::release_store_fence(volatile jushort* p, jushort v) { release_store(p, v); fence(); } ++inline void OrderAccess::release_store_fence(volatile juint* p, juint v) { release_store(p, v); fence(); } ++inline void OrderAccess::release_store_fence(volatile julong* p, julong v) { release_store(p, v); fence(); } ++inline void OrderAccess::release_store_fence(volatile jfloat* p, jfloat v) { release_store(p, v); fence(); } ++inline void OrderAccess::release_store_fence(volatile jdouble* p, jdouble v) { release_store(p, v); fence(); } ++ ++//implementaion of release_store_ptr_fence ++inline void OrderAccess::release_store_ptr_fence(volatile intptr_t* p, intptr_t v) { release_store_ptr(p, v); fence(); } ++inline void OrderAccess::release_store_ptr_fence(volatile void* p, void* v) { release_store_ptr(p, v); fence(); } ++ ++#undef inlasm_sync ++ ++#endif // OS_CPU_LINUX_MIPS_VM_ORDERACCESS_LINUX_MIPS_INLINE_HPP +diff --git a/hotspot/src/os_cpu/linux_mips/vm/os_linux_mips.cpp b/hotspot/src/os_cpu/linux_mips/vm/os_linux_mips.cpp +new file mode 100644 +index 0000000000..a23c302c93 +--- /dev/null ++++ b/hotspot/src/os_cpu/linux_mips/vm/os_linux_mips.cpp +@@ -0,0 +1,1015 @@ ++/* ++ * Copyright (c) 1999, 2014, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2015, 2022, Loongson Technology. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ * ++ */ ++ ++// no precompiled headers ++#include "asm/macroAssembler.hpp" ++#include "classfile/classLoader.hpp" ++#include "classfile/systemDictionary.hpp" ++#include "classfile/vmSymbols.hpp" ++#include "code/icBuffer.hpp" ++#include "code/vtableStubs.hpp" ++#include "interpreter/interpreter.hpp" ++#include "jvm_linux.h" ++#include "memory/allocation.inline.hpp" ++#include "mutex_linux.inline.hpp" ++#include "os_share_linux.hpp" ++#include "prims/jniFastGetField.hpp" ++#include "prims/jvm.h" ++#include "prims/jvm_misc.hpp" ++#include "runtime/arguments.hpp" ++#include "runtime/extendedPC.hpp" ++#include "runtime/frame.inline.hpp" ++#include "runtime/interfaceSupport.hpp" ++#include "runtime/java.hpp" ++#include "runtime/javaCalls.hpp" ++#include "runtime/mutexLocker.hpp" ++#include "runtime/osThread.hpp" ++#include "runtime/sharedRuntime.hpp" ++#include "runtime/stubRoutines.hpp" ++#include "runtime/thread.inline.hpp" ++#include "runtime/timer.hpp" ++#include "utilities/events.hpp" ++#include "utilities/vmError.hpp" ++#include "utilities/debug.hpp" ++#include "compiler/disassembler.hpp" ++// put OS-includes here ++# include ++# include ++# include ++# include ++# include ++# include ++# include ++# include ++# include ++# include ++# include ++# include ++# include ++# include ++# include ++# include ++# include ++# include ++# include ++# include ++ ++#define REG_SP 29 ++#define REG_FP 30 ++ ++address os::current_stack_pointer() { ++ register void *sp __asm__ ("$29"); ++ return (address) sp; ++} ++ ++char* os::non_memory_address_word() { ++ // Must never look like an address returned by reserve_memory, ++ // even in its subfields (as defined by the CPU immediate fields, ++ // if the CPU splits constants across multiple instructions). ++ ++ return (char*) -1; ++} ++ ++void os::initialize_thread(Thread* thr) { ++// Nothing to do. ++} ++ ++address os::Linux::ucontext_get_pc(ucontext_t * uc) { ++ //return (address)uc->uc_mcontext.gregs[REG_PC]; ++ return (address)uc->uc_mcontext.pc; ++} ++ ++intptr_t* os::Linux::ucontext_get_sp(ucontext_t * uc) { ++ return (intptr_t*)uc->uc_mcontext.gregs[REG_SP]; ++} ++ ++intptr_t* os::Linux::ucontext_get_fp(ucontext_t * uc) { ++ return (intptr_t*)uc->uc_mcontext.gregs[REG_FP]; ++} ++ ++// For Forte Analyzer AsyncGetCallTrace profiling support - thread ++// is currently interrupted by SIGPROF. ++// os::Solaris::fetch_frame_from_ucontext() tries to skip nested signal ++// frames. Currently we don't do that on Linux, so it's the same as ++// os::fetch_frame_from_context(). ++ExtendedPC os::Linux::fetch_frame_from_ucontext(Thread* thread, ++ ucontext_t* uc, intptr_t** ret_sp, intptr_t** ret_fp) { ++ ++ assert(thread != NULL, "just checking"); ++ assert(ret_sp != NULL, "just checking"); ++ assert(ret_fp != NULL, "just checking"); ++ ++ return os::fetch_frame_from_context(uc, ret_sp, ret_fp); ++} ++ ++ExtendedPC os::fetch_frame_from_context(void* ucVoid, ++ intptr_t** ret_sp, intptr_t** ret_fp) { ++ ++ ExtendedPC epc; ++ ucontext_t* uc = (ucontext_t*)ucVoid; ++ ++ if (uc != NULL) { ++ epc = ExtendedPC(os::Linux::ucontext_get_pc(uc)); ++ if (ret_sp) *ret_sp = os::Linux::ucontext_get_sp(uc); ++ if (ret_fp) *ret_fp = os::Linux::ucontext_get_fp(uc); ++ } else { ++ // construct empty ExtendedPC for return value checking ++ epc = ExtendedPC(NULL); ++ if (ret_sp) *ret_sp = (intptr_t *)NULL; ++ if (ret_fp) *ret_fp = (intptr_t *)NULL; ++ } ++ ++ return epc; ++} ++ ++frame os::fetch_frame_from_context(void* ucVoid) { ++ intptr_t* sp; ++ intptr_t* fp; ++ ExtendedPC epc = fetch_frame_from_context(ucVoid, &sp, &fp); ++ return frame(sp, fp, epc.pc()); ++} ++ ++// By default, gcc always save frame pointer (%ebp/%rbp) on stack. It may get ++// turned off by -fomit-frame-pointer, ++frame os::get_sender_for_C_frame(frame* fr) { ++ return frame(fr->sender_sp(), fr->link(), fr->sender_pc()); ++} ++ ++//intptr_t* _get_previous_fp() { ++intptr_t* __attribute__((noinline)) os::get_previous_fp() { ++ int *pc; ++ intptr_t sp; ++ int *pc_limit = (int*)(void*)&os::get_previous_fp; ++ int insn; ++ ++ { ++ l_pc:; ++ pc = (int*)&&l_pc; ++ __asm__ __volatile__ ("move %0, $sp" : "=r" (sp)); ++ } ++ ++ do { ++ insn = *pc; ++ switch(bitfield(insn, 16, 16)) { ++ case 0x27bd: /* addiu $sp,$sp,-i */ ++ case 0x67bd: /* daddiu $sp,$sp,-i */ ++ assert ((short)bitfield(insn, 0, 16)<0, "bad frame"); ++ sp -= (short)bitfield(insn, 0, 16); ++ return (intptr_t*)sp; ++ } ++ --pc; ++ } while (pc>=pc_limit); // The initial value of pc may be equal to pc_limit, because of GCC optimization. ++ ++ ShouldNotReachHere(); ++ return NULL; // mute compiler ++} ++ ++ ++frame os::current_frame() { ++ intptr_t* fp = (intptr_t*)get_previous_fp(); ++ frame myframe((intptr_t*)os::current_stack_pointer(), ++ (intptr_t*)fp, ++ CAST_FROM_FN_PTR(address, os::current_frame)); ++ if (os::is_first_C_frame(&myframe)) { ++ // stack is not walkable ++ return frame(); ++ } else { ++ return os::get_sender_for_C_frame(&myframe); ++ } ++} ++ ++//x86 add 2 new assemble function here! ++extern "C" JNIEXPORT int ++JVM_handle_linux_signal(int sig, ++ siginfo_t* info, ++ void* ucVoid, ++ int abort_if_unrecognized) { ++#ifdef PRINT_SIGNAL_HANDLE ++ tty->print_cr("Signal: signo=%d, sicode=%d, sierrno=%d, siaddr=%lx", ++ info->si_signo, ++ info->si_code, ++ info->si_errno, ++ info->si_addr); ++#endif ++ ++ ucontext_t* uc = (ucontext_t*) ucVoid; ++ ++ Thread* t = ThreadLocalStorage::get_thread_slow(); ++ ++ SignalHandlerMark shm(t); ++ ++ // Note: it's not uncommon that JNI code uses signal/sigset to install ++ // then restore certain signal handler (e.g. to temporarily block SIGPIPE, ++ // or have a SIGILL handler when detecting CPU type). When that happens, ++ // JVM_handle_linux_signal() might be invoked with junk info/ucVoid. To ++ // avoid unnecessary crash when libjsig is not preloaded, try handle signals ++ // that do not require siginfo/ucontext first. ++ ++ if (sig == SIGPIPE/* || sig == SIGXFSZ*/) { ++ // allow chained handler to go first ++ if (os::Linux::chained_handler(sig, info, ucVoid)) { ++ return true; ++ } else { ++ if (PrintMiscellaneous && (WizardMode || Verbose)) { ++ warning("Ignoring SIGPIPE - see bug 4229104"); ++ } ++ return true; ++ } ++ } ++ ++ JavaThread* thread = NULL; ++ VMThread* vmthread = NULL; ++ if (os::Linux::signal_handlers_are_installed) { ++ if (t != NULL ){ ++ if(t->is_Java_thread()) { ++#ifdef PRINT_SIGNAL_HANDLE ++ tty->print_cr("this thread is a java thread"); ++#endif ++ thread = (JavaThread*)t; ++ } ++ else if(t->is_VM_thread()){ ++#ifdef PRINT_SIGNAL_HANDLE ++ tty->print_cr("this thread is a VM thread\n"); ++#endif ++ vmthread = (VMThread *)t; ++ } ++ } ++ } ++ ++ // decide if this trap can be handled by a stub ++ address stub = NULL; ++ address pc = NULL; ++ ++ pc = (address) os::Linux::ucontext_get_pc(uc); ++#ifdef PRINT_SIGNAL_HANDLE ++ tty->print_cr("pc=%lx", pc); ++ os::print_context(tty, uc); ++#endif ++ //%note os_trap_1 ++ if (info != NULL && uc != NULL && thread != NULL) { ++ pc = (address) os::Linux::ucontext_get_pc(uc); ++ // Handle ALL stack overflow variations here ++ if (sig == SIGSEGV) { ++ address addr = (address) info->si_addr; ++#ifdef PRINT_SIGNAL_HANDLE ++ tty->print("handle all stack overflow variations: "); ++ /*tty->print("addr = %lx, stack base = %lx, stack top = %lx\n", ++ addr, ++ thread->stack_base(), ++ thread->stack_base() - thread->stack_size()); ++ */ ++#endif ++ ++ // check if fault address is within thread stack ++ if (addr < thread->stack_base() && ++ addr >= thread->stack_base() - thread->stack_size()) { ++ // stack overflow ++#ifdef PRINT_SIGNAL_HANDLE ++ tty->print("stack exception check \n"); ++#endif ++ if (thread->in_stack_yellow_zone(addr)) { ++#ifdef PRINT_SIGNAL_HANDLE ++ tty->print("exception addr is in yellow zone\n"); ++#endif ++ thread->disable_stack_yellow_zone(); ++ if (thread->thread_state() == _thread_in_Java) { ++ // Throw a stack overflow exception. Guard pages will be reenabled ++ // while unwinding the stack. ++#ifdef PRINT_SIGNAL_HANDLE ++ tty->print("this thread is in java\n"); ++#endif ++ stub = SharedRuntime::continuation_for_implicit_exception(thread, pc, SharedRuntime::STACK_OVERFLOW); ++ } else { ++ // Thread was in the vm or native code. Return and try to finish. ++#ifdef PRINT_SIGNAL_HANDLE ++ tty->print("this thread is in vm or native codes and return\n"); ++#endif ++ return 1; ++ } ++ } else if (thread->in_stack_red_zone(addr)) { ++ // Fatal red zone violation. Disable the guard pages and fall through ++ // to handle_unexpected_exception way down below. ++#ifdef PRINT_SIGNAL_HANDLE ++ tty->print("exception addr is in red zone\n"); ++#endif ++ thread->disable_stack_red_zone(); ++ tty->print_raw_cr("An irrecoverable stack overflow has occurred."); ++ ++ // This is a likely cause, but hard to verify. Let's just print ++ // it as a hint. ++ tty->print_raw_cr("Please check if any of your loaded .so files has " ++ "enabled executable stack (see man page execstack(8))"); ++ } else { ++ // Accessing stack address below sp may cause SEGV if current ++ // thread has MAP_GROWSDOWN stack. This should only happen when ++ // current thread was created by user code with MAP_GROWSDOWN flag ++ // and then attached to VM. See notes in os_linux.cpp. ++#ifdef PRINT_SIGNAL_HANDLE ++ tty->print("exception addr is neither in yellow zone nor in the red one\n"); ++#endif ++ if (thread->osthread()->expanding_stack() == 0) { ++ thread->osthread()->set_expanding_stack(); ++ if (os::Linux::manually_expand_stack(thread, addr)) { ++ thread->osthread()->clear_expanding_stack(); ++ return 1; ++ } ++ thread->osthread()->clear_expanding_stack(); ++ } else { ++ fatal("recursive segv. expanding stack."); ++ } ++ } ++ } //addr < ++ } //sig == SIGSEGV ++ ++ if (thread->thread_state() == _thread_in_Java) { ++ // Java thread running in Java code => find exception handler if any ++ // a fault inside compiled code, the interpreter, or a stub ++#ifdef PRINT_SIGNAL_HANDLE ++ tty->print("java thread running in java code\n"); ++#endif ++ ++ // Handle signal from NativeJump::patch_verified_entry(). ++ if (sig == SIGILL & nativeInstruction_at(pc)->is_sigill_zombie_not_entrant()) { ++#ifdef PRINT_SIGNAL_HANDLE ++ tty->print_cr("verified entry = %lx, sig=%d", nativeInstruction_at(pc), sig); ++#endif ++ stub = SharedRuntime::get_handle_wrong_method_stub(); ++ } else if (sig == SIGSEGV && os::is_poll_address((address)info->si_addr)) { ++#ifdef PRINT_SIGNAL_HANDLE ++ tty->print_cr("polling address = %lx, sig=%d", os::get_polling_page(), sig); ++#endif ++ stub = SharedRuntime::get_poll_stub(pc); ++ } else if (sig == SIGBUS /* && info->si_code == BUS_OBJERR */) { ++ // BugId 4454115: A read from a MappedByteBuffer can fault ++ // here if the underlying file has been truncated. ++ // Do not crash the VM in such a case. ++ CodeBlob* cb = CodeCache::find_blob_unsafe(pc); ++ nmethod* nm = cb->is_nmethod() ? (nmethod*)cb : NULL; ++#ifdef PRINT_SIGNAL_HANDLE ++ tty->print("cb = %lx, nm = %lx\n", cb, nm); ++#endif ++ if (nm != NULL && nm->has_unsafe_access()) { ++ stub = StubRoutines::handler_for_unsafe_access(); ++ } ++ } else if (sig == SIGFPE /* && info->si_code == FPE_INTDIV */) { ++ // HACK: si_code does not work on linux 2.2.12-20!!! ++ int op = pc[0] & 0x3f; ++ int op1 = pc[3] & 0x3f; ++ //FIXME, Must port to mips code!! ++ switch (op) { ++ case 0x1e: //ddiv ++ case 0x1f: //ddivu ++ case 0x1a: //div ++ case 0x1b: //divu ++ case 0x34: //trap ++ /* In MIPS, div_by_zero exception can only be triggered by explicit 'trap'. ++ * Ref: [c1_LIRAssembler_mips.cpp] arithmetic_idiv() ++ */ ++ stub = SharedRuntime::continuation_for_implicit_exception(thread, ++ pc, ++ SharedRuntime::IMPLICIT_DIVIDE_BY_ZERO); ++ break; ++ default: ++ // TODO: handle more cases if we are using other x86 instructions ++ // that can generate SIGFPE signal on linux. ++ tty->print_cr("unknown opcode 0x%X -0x%X with SIGFPE.", op, op1); ++ //fatal("please update this code."); ++ } ++ } else if (sig == SIGSEGV && ++ !MacroAssembler::needs_explicit_null_check((intptr_t)info->si_addr)) { ++#ifdef PRINT_SIGNAL_HANDLE ++ tty->print("continuation for implicit exception\n"); ++#endif ++ // Determination of interpreter/vtable stub/compiled code null exception ++ stub = SharedRuntime::continuation_for_implicit_exception(thread, pc, SharedRuntime::IMPLICIT_NULL); ++#ifdef PRINT_SIGNAL_HANDLE ++ tty->print_cr("continuation_for_implicit_exception stub: %lx", stub); ++#endif ++ } else if (/*thread->thread_state() == _thread_in_Java && */sig == SIGILL) { ++ //Since kernel does not have emulation of PS instructions yet, the emulation must be handled here. ++ //The method is to trigger kernel emulation of float emulation. ++ int inst = *(int*)pc; ++ int ops = (inst >> 26) & 0x3f; ++ int ops_fmt = (inst >> 21) & 0x1f; ++ int op = inst & 0x3f; ++ if (ops == Assembler::cop1_op && ops_fmt == Assembler::ps_fmt) { ++ int ft, fs, fd; ++ ft = (inst >> 16) & 0x1f; ++ fs = (inst >> 11) & 0x1f; ++ fd = (inst >> 6) & 0x1f; ++ float ft_upper, ft_lower, fs_upper, fs_lower, fd_upper, fd_lower; ++ double ft_value, fs_value, fd_value; ++ ft_value = uc->uc_mcontext.fpregs.fp_r.fp_dregs[ft]; ++ fs_value = uc->uc_mcontext.fpregs.fp_r.fp_dregs[fs]; ++ __asm__ __volatile__ ( ++ "cvt.s.pl %0, %4\n\t" ++ "cvt.s.pu %1, %4\n\t" ++ "cvt.s.pl %2, %5\n\t" ++ "cvt.s.pu %3, %5\n\t" ++ : "=f" (fs_lower), "=f" (fs_upper), "=f" (ft_lower), "=f" (ft_upper) ++ : "f" (fs_value), "f" (ft_value) ++ ); ++ ++ switch (op) { ++ case Assembler::fadd_op: ++ __asm__ __volatile__ ( ++ "add.s %1, %3, %5\n\t" ++ "add.s %2, %4, %6\n\t" ++ "pll.ps %0, %1, %2\n\t" ++ : "=f" (fd_value), "=f" (fd_upper), "=f" (fd_lower) ++ : "f" (fs_upper), "f" (fs_lower), "f" (ft_upper), "f" (ft_lower) ++ ); ++ uc->uc_mcontext.fpregs.fp_r.fp_dregs[fd] = fd_value; ++ stub = pc + 4; ++ break; ++ case Assembler::fsub_op: ++ //fd = fs - ft ++ __asm__ __volatile__ ( ++ "sub.s %1, %3, %5\n\t" ++ "sub.s %2, %4, %6\n\t" ++ "pll.ps %0, %1, %2\n\t" ++ : "=f" (fd_value), "=f" (fd_upper), "=f" (fd_lower) ++ : "f" (fs_upper), "f" (fs_lower), "f" (ft_upper), "f" (ft_lower) ++ ); ++ uc->uc_mcontext.fpregs.fp_r.fp_dregs[fd] = fd_value; ++ stub = pc + 4; ++ break; ++ case Assembler::fmul_op: ++ __asm__ __volatile__ ( ++ "mul.s %1, %3, %5\n\t" ++ "mul.s %2, %4, %6\n\t" ++ "pll.ps %0, %1, %2\n\t" ++ : "=f" (fd_value), "=f" (fd_upper), "=f" (fd_lower) ++ : "f" (fs_upper), "f" (fs_lower), "f" (ft_upper), "f" (ft_lower) ++ ); ++ uc->uc_mcontext.fpregs.fp_r.fp_dregs[fd] = fd_value; ++ stub = pc + 4; ++ break; ++ default: ++ tty->print_cr("unknown cop1 opcode 0x%x with SIGILL.", op); ++ } ++ } else if (ops == Assembler::cop1x_op /*&& op == Assembler::nmadd_ps_op*/) { ++ // madd.ps is not used, the code below were not tested ++ int fr, ft, fs, fd; ++ float fr_upper, fr_lower, fs_upper, fs_lower, ft_upper, ft_lower, fd_upper, fd_lower; ++ double fr_value, ft_value, fs_value, fd_value; ++ switch (op) { ++ case Assembler::madd_ps_op: ++ // fd = (fs * ft) + fr ++ fr = (inst >> 21) & 0x1f; ++ ft = (inst >> 16) & 0x1f; ++ fs = (inst >> 11) & 0x1f; ++ fd = (inst >> 6) & 0x1f; ++ fr_value = uc->uc_mcontext.fpregs.fp_r.fp_dregs[fr]; ++ ft_value = uc->uc_mcontext.fpregs.fp_r.fp_dregs[ft]; ++ fs_value = uc->uc_mcontext.fpregs.fp_r.fp_dregs[fs]; ++ __asm__ __volatile__ ( ++ "cvt.s.pu %3, %9\n\t" ++ "cvt.s.pl %4, %9\n\t" ++ "cvt.s.pu %5, %10\n\t" ++ "cvt.s.pl %6, %10\n\t" ++ "cvt.s.pu %7, %11\n\t" ++ "cvt.s.pl %8, %11\n\t" ++ "madd.s %1, %3, %5, %7\n\t" ++ "madd.s %2, %4, %6, %8\n\t" ++ "pll.ps %0, %1, %2\n\t" ++ : "=f" (fd_value), "=f" (fd_upper), "=f" (fd_lower), "=f" (fr_upper), "=f" (fr_lower), "=f" (fs_upper), "=f" (fs_lower), "=f" (ft_upper), "=f" (ft_lower) ++ : "f" (fr_value)/*9*/, "f" (fs_value)/*10*/, "f" (ft_value)/*11*/ ++ ); ++ uc->uc_mcontext.fpregs.fp_r.fp_dregs[fd] = fd_value; ++ stub = pc + 4; ++ break; ++ default: ++ tty->print_cr("unknown cop1x opcode 0x%x with SIGILL.", op); ++ } ++ } ++ } //SIGILL ++ } else if (sig == SIGILL && VM_Version::is_determine_features_test_running()) { ++ // thread->thread_state() != _thread_in_Java ++ // SIGILL must be caused by VM_Version::determine_features(). ++ VM_Version::set_supports_cpucfg(false); ++ stub = pc + 4; // continue with next instruction. ++ } else if (thread->thread_state() == _thread_in_vm && ++ sig == SIGBUS && /* info->si_code == BUS_OBJERR && */ ++ thread->doing_unsafe_access()) { ++#ifdef PRINT_SIGNAL_HANDLE ++ tty->print_cr("SIGBUS in vm thread \n"); ++#endif ++ stub = StubRoutines::handler_for_unsafe_access(); ++ } ++ ++ // jni_fast_GetField can trap at certain pc's if a GC kicks in ++ // and the heap gets shrunk before the field access. ++ if ((sig == SIGSEGV) || (sig == SIGBUS)) { ++#ifdef PRINT_SIGNAL_HANDLE ++ tty->print("jni fast get trap: "); ++#endif ++ address addr = JNI_FastGetField::find_slowcase_pc(pc); ++ if (addr != (address)-1) { ++ stub = addr; ++ } ++#ifdef PRINT_SIGNAL_HANDLE ++ tty->print_cr("addr = %d, stub = %lx", addr, stub); ++#endif ++ } ++ ++ // Check to see if we caught the safepoint code in the ++ // process of write protecting the memory serialization page. ++ // It write enables the page immediately after protecting it ++ // so we can just return to retry the write. ++ if ((sig == SIGSEGV) && ++ os::is_memory_serialize_page(thread, (address) info->si_addr)) { ++#ifdef PRINT_SIGNAL_HANDLE ++ tty->print("write protecting the memory serialiazation page\n"); ++#endif ++ // Block current thread until the memory serialize page permission restored. ++ os::block_on_serialize_page_trap(); ++ return true; ++ } ++ } ++ ++ // Execution protection violation ++ // ++ // This should be kept as the last step in the triage. We don't ++ // have a dedicated trap number for a no-execute fault, so be ++ // conservative and allow other handlers the first shot. ++ // ++ // Note: We don't test that info->si_code == SEGV_ACCERR here. ++ // this si_code is so generic that it is almost meaningless; and ++ // the si_code for this condition may change in the future. ++ // Furthermore, a false-positive should be harmless. ++ if (UnguardOnExecutionViolation > 0 && ++ //(sig == SIGSEGV || sig == SIGBUS) && ++ //uc->uc_mcontext.gregs[REG_TRAPNO] == trap_page_fault) { ++ (sig == SIGSEGV || sig == SIGBUS ++#ifdef OPT_RANGECHECK ++ || sig == SIGSYS ++#endif ++ ) && ++ //(uc->uc_mcontext.cause == 2 || uc->uc_mcontext.cause == 3)) { ++ (uc->uc_mcontext.hi1 == 2 || uc->uc_mcontext.hi1 == 3)) { ++#ifdef PRINT_SIGNAL_HANDLE ++ tty->print_cr("execution protection violation\n"); ++#endif ++ ++ int page_size = os::vm_page_size(); ++ address addr = (address) info->si_addr; ++ address pc = os::Linux::ucontext_get_pc(uc); ++ // Make sure the pc and the faulting address are sane. ++ // ++ // If an instruction spans a page boundary, and the page containing ++ // the beginning of the instruction is executable but the following ++ // page is not, the pc and the faulting address might be slightly ++ // different - we still want to unguard the 2nd page in this case. ++ // ++ // 15 bytes seems to be a (very) safe value for max instruction size. ++ bool pc_is_near_addr = ++ (pointer_delta((void*) addr, (void*) pc, sizeof(char)) < 15); ++ bool instr_spans_page_boundary = ++ (align_size_down((intptr_t) pc ^ (intptr_t) addr, ++ (intptr_t) page_size) > 0); ++ ++ if (pc == addr || (pc_is_near_addr && instr_spans_page_boundary)) { ++ static volatile address last_addr = ++ (address) os::non_memory_address_word(); ++ ++ // In conservative mode, don't unguard unless the address is in the VM ++ if (addr != last_addr && ++ (UnguardOnExecutionViolation > 1 || os::address_is_in_vm(addr))) { ++ ++ // Set memory to RWX and retry ++ address page_start = ++ (address) align_size_down((intptr_t) addr, (intptr_t) page_size); ++ bool res = os::protect_memory((char*) page_start, page_size, ++ os::MEM_PROT_RWX); ++ ++ if (PrintMiscellaneous && Verbose) { ++ char buf[256]; ++ jio_snprintf(buf, sizeof(buf), "Execution protection violation " ++ "at " INTPTR_FORMAT ++ ", unguarding " INTPTR_FORMAT ": %s, errno=%d", addr, ++ page_start, (res ? "success" : "failed"), errno); ++ tty->print_raw_cr(buf); ++ } ++ stub = pc; ++ ++ // Set last_addr so if we fault again at the same address, we don't end ++ // up in an endless loop. ++ // ++ // There are two potential complications here. Two threads trapping at ++ // the same address at the same time could cause one of the threads to ++ // think it already unguarded, and abort the VM. Likely very rare. ++ // ++ // The other race involves two threads alternately trapping at ++ // different addresses and failing to unguard the page, resulting in ++ // an endless loop. This condition is probably even more unlikely than ++ // the first. ++ // ++ // Although both cases could be avoided by using locks or thread local ++ // last_addr, these solutions are unnecessary complication: this ++ // handler is a best-effort safety net, not a complete solution. It is ++ // disabled by default and should only be used as a workaround in case ++ // we missed any no-execute-unsafe VM code. ++ ++ last_addr = addr; ++ } ++ } ++ } ++ ++ if (stub != NULL) { ++#ifdef PRINT_SIGNAL_HANDLE ++ tty->print_cr("resolved stub=%lx\n",stub); ++#endif ++ // save all thread context in case we need to restore it ++ if (thread != NULL) thread->set_saved_exception_pc(pc); ++ ++ uc->uc_mcontext.pc = (greg_t)stub; ++ return true; ++ } ++ ++ // signal-chaining ++ if (os::Linux::chained_handler(sig, info, ucVoid)) { ++#ifdef PRINT_SIGNAL_HANDLE ++ tty->print_cr("signal chaining\n"); ++#endif ++ return true; ++ } ++ ++ if (!abort_if_unrecognized) { ++#ifdef PRINT_SIGNAL_HANDLE ++ tty->print_cr("abort becauce of unrecognized\n"); ++#endif ++ // caller wants another chance, so give it to him ++ return false; ++ } ++ ++ if (pc == NULL && uc != NULL) { ++ pc = os::Linux::ucontext_get_pc(uc); ++ } ++ ++ // unmask current signal ++ sigset_t newset; ++ sigemptyset(&newset); ++ sigaddset(&newset, sig); ++ sigprocmask(SIG_UNBLOCK, &newset, NULL); ++#ifdef PRINT_SIGNAL_HANDLE ++ tty->print_cr("VMError in signal handler\n"); ++#endif ++ VMError err(t, sig, pc, info, ucVoid); ++ err.report_and_die(); ++ ++ ShouldNotReachHere(); ++ return true; // Mute compiler ++} ++ ++// FCSR:...|24| 23 |22|21|... ++// ...|FS|FCC0|FO|FN|... ++void os::Linux::init_thread_fpu_state(void) { ++ if (SetFSFOFN == 999) ++ return; ++ int fs = (SetFSFOFN / 100)? 1:0; ++ int fo = ((SetFSFOFN % 100) / 10)? 1:0; ++ int fn = (SetFSFOFN % 10)? 1:0; ++ int mask = fs << 24 | fo << 22 | fn << 21; ++ ++ int fcsr = get_fpu_control_word(); ++ fcsr = fcsr | mask; ++ set_fpu_control_word(fcsr); ++ /* ++ if (fcsr != get_fpu_control_word()) ++ tty->print_cr(" fail to set to %lx, get_fpu_control_word:%lx", fcsr, get_fpu_control_word()); ++ */ ++} ++ ++int os::Linux::get_fpu_control_word(void) { ++ int fcsr; ++ __asm__ __volatile__ ( ++ ".set noat;" ++ "daddiu %0, $0, 0;" ++ "cfc1 %0, $31;" ++ : "=r" (fcsr) ++ ); ++ return fcsr; ++} ++ ++void os::Linux::set_fpu_control_word(int fpu_control) { ++ __asm__ __volatile__ ( ++ ".set noat;" ++ "ctc1 %0, $31;" ++ : ++ : "r" (fpu_control) ++ ); ++} ++ ++bool os::is_allocatable(size_t bytes) { ++ ++ if (bytes < 2 * G) { ++ return true; ++ } ++ ++ char* addr = reserve_memory(bytes, NULL); ++ ++ if (addr != NULL) { ++ release_memory(addr, bytes); ++ } ++ ++ return addr != NULL; ++} ++ ++//////////////////////////////////////////////////////////////////////////////// ++// thread stack ++ ++size_t os::Linux::min_stack_allowed = 96 * K; ++ ++ ++// Test if pthread library can support variable thread stack size. LinuxThreads ++// in fixed stack mode allocates 2M fixed slot for each thread. LinuxThreads ++// in floating stack mode and NPTL support variable stack size. ++bool os::Linux::supports_variable_stack_size() { ++ if (os::Linux::is_NPTL()) { ++ // NPTL, yes ++ return true; ++ ++ } else { ++ // Note: We can't control default stack size when creating a thread. ++ // If we use non-default stack size (pthread_attr_setstacksize), both ++ // floating stack and non-floating stack LinuxThreads will return the ++ // same value. This makes it impossible to implement this function by ++ // detecting thread stack size directly. ++ // ++ // An alternative approach is to check %gs. Fixed-stack LinuxThreads ++ // do not use %gs, so its value is 0. Floating-stack LinuxThreads use ++ // %gs (either as LDT selector or GDT selector, depending on kernel) ++ // to access thread specific data. ++ // ++ // Note that %gs is a reserved glibc register since early 2001, so ++ // applications are not allowed to change its value (Ulrich Drepper from ++ // Redhat confirmed that all known offenders have been modified to use ++ // either %fs or TSD). In the worst case scenario, when VM is embedded in ++ // a native application that plays with %gs, we might see non-zero %gs ++ // even LinuxThreads is running in fixed stack mode. As the result, we'll ++ // return true and skip _thread_safety_check(), so we may not be able to ++ // detect stack-heap collisions. But otherwise it's harmless. ++ // ++ return false; ++ } ++} ++ ++// return default stack size for thr_type ++size_t os::Linux::default_stack_size(os::ThreadType thr_type) { ++ // default stack size (compiler thread needs larger stack) ++ size_t s = (thr_type == os::compiler_thread ? 2 * M : 512 * K); ++ return s; ++} ++ ++size_t os::Linux::default_guard_size(os::ThreadType thr_type) { ++ // Creating guard page is very expensive. Java thread has HotSpot ++ // guard page, only enable glibc guard page for non-Java threads. ++ return (thr_type == java_thread ? 0 : page_size()); ++} ++ ++// Java thread: ++// ++// Low memory addresses ++// +------------------------+ ++// | |\ JavaThread created by VM does not have glibc ++// | glibc guard page | - guard, attached Java thread usually has ++// | |/ 1 page glibc guard. ++// P1 +------------------------+ Thread::stack_base() - Thread::stack_size() ++// | |\ ++// | HotSpot Guard Pages | - red and yellow pages ++// | |/ ++// +------------------------+ JavaThread::stack_yellow_zone_base() ++// | |\ ++// | Normal Stack | - ++// | |/ ++// P2 +------------------------+ Thread::stack_base() ++// ++// Non-Java thread: ++// ++// Low memory addresses ++// +------------------------+ ++// | |\ ++// | glibc guard page | - usually 1 page ++// | |/ ++// P1 +------------------------+ Thread::stack_base() - Thread::stack_size() ++// | |\ ++// | Normal Stack | - ++// | |/ ++// P2 +------------------------+ Thread::stack_base() ++// ++// ** P1 (aka bottom) and size ( P2 = P1 - size) are the address and stack size returned from ++// pthread_attr_getstack() ++ ++static void current_stack_region(address * bottom, size_t * size) { ++ if (os::is_primordial_thread()) { ++ // primordial thread needs special handling because pthread_getattr_np() ++ // may return bogus value. ++ *bottom = os::Linux::initial_thread_stack_bottom(); ++ *size = os::Linux::initial_thread_stack_size(); ++ } else { ++ pthread_attr_t attr; ++ ++ int rslt = pthread_getattr_np(pthread_self(), &attr); ++ ++ // JVM needs to know exact stack location, abort if it fails ++ if (rslt != 0) { ++ if (rslt == ENOMEM) { ++ vm_exit_out_of_memory(0, OOM_MMAP_ERROR, "pthread_getattr_np"); ++ } else { ++ fatal(err_msg("pthread_getattr_np failed with errno = %d", rslt)); ++ } ++ } ++ ++ if (pthread_attr_getstack(&attr, (void **)bottom, size) != 0) { ++ fatal("Can not locate current stack attributes!"); ++ } ++ ++ pthread_attr_destroy(&attr); ++ ++ } ++ assert(os::current_stack_pointer() >= *bottom && ++ os::current_stack_pointer() < *bottom + *size, "just checking"); ++} ++ ++address os::current_stack_base() { ++ address bottom; ++ size_t size; ++ current_stack_region(&bottom, &size); ++ return (bottom + size); ++} ++ ++size_t os::current_stack_size() { ++ // stack size includes normal stack and HotSpot guard pages ++ address bottom; ++ size_t size; ++ current_stack_region(&bottom, &size); ++ return size; ++} ++ ++///////////////////////////////////////////////////////////////////////////// ++// helper functions for fatal error handler ++void os::print_register_info(outputStream *st, void *context) { ++ if (context == NULL) return; ++ ++ ucontext_t *uc = (ucontext_t*)context; ++ ++ st->print_cr("Register to memory mapping:"); ++ st->cr(); ++ // this is horrendously verbose but the layout of the registers in the ++ // // context does not match how we defined our abstract Register set, so ++ // // we can't just iterate through the gregs area ++ // ++ // // this is only for the "general purpose" registers ++ st->print("R0=" ); print_location(st, uc->uc_mcontext.gregs[0]); ++ st->print("AT=" ); print_location(st, uc->uc_mcontext.gregs[1]); ++ st->print("V0=" ); print_location(st, uc->uc_mcontext.gregs[2]); ++ st->print("V1=" ); print_location(st, uc->uc_mcontext.gregs[3]); ++ st->cr(); ++ st->print("A0=" ); print_location(st, uc->uc_mcontext.gregs[4]); ++ st->print("A1=" ); print_location(st, uc->uc_mcontext.gregs[5]); ++ st->print("A2=" ); print_location(st, uc->uc_mcontext.gregs[6]); ++ st->print("A3=" ); print_location(st, uc->uc_mcontext.gregs[7]); ++ st->cr(); ++ st->print("A4=" ); print_location(st, uc->uc_mcontext.gregs[8]); ++ st->print("A5=" ); print_location(st, uc->uc_mcontext.gregs[9]); ++ st->print("A6=" ); print_location(st, uc->uc_mcontext.gregs[10]); ++ st->print("A7=" ); print_location(st, uc->uc_mcontext.gregs[11]); ++ st->cr(); ++ st->print("T0=" ); print_location(st, uc->uc_mcontext.gregs[12]); ++ st->print("T1=" ); print_location(st, uc->uc_mcontext.gregs[13]); ++ st->print("T2=" ); print_location(st, uc->uc_mcontext.gregs[14]); ++ st->print("T3=" ); print_location(st, uc->uc_mcontext.gregs[15]); ++ st->cr(); ++ st->print("S0=" ); print_location(st, uc->uc_mcontext.gregs[16]); ++ st->print("S1=" ); print_location(st, uc->uc_mcontext.gregs[17]); ++ st->print("S2=" ); print_location(st, uc->uc_mcontext.gregs[18]); ++ st->print("S3=" ); print_location(st, uc->uc_mcontext.gregs[19]); ++ st->cr(); ++ st->print("S4=" ); print_location(st, uc->uc_mcontext.gregs[20]); ++ st->print("S5=" ); print_location(st, uc->uc_mcontext.gregs[21]); ++ st->print("S6=" ); print_location(st, uc->uc_mcontext.gregs[22]); ++ st->print("S7=" ); print_location(st, uc->uc_mcontext.gregs[23]); ++ st->cr(); ++ st->print("T8=" ); print_location(st, uc->uc_mcontext.gregs[24]); ++ st->print("T9=" ); print_location(st, uc->uc_mcontext.gregs[25]); ++ st->print("K0=" ); print_location(st, uc->uc_mcontext.gregs[26]); ++ st->print("K1=" ); print_location(st, uc->uc_mcontext.gregs[27]); ++ st->cr(); ++ st->print("GP=" ); print_location(st, uc->uc_mcontext.gregs[28]); ++ st->print("SP=" ); print_location(st, uc->uc_mcontext.gregs[29]); ++ st->print("FP=" ); print_location(st, uc->uc_mcontext.gregs[30]); ++ st->print("RA=" ); print_location(st, uc->uc_mcontext.gregs[31]); ++ st->cr(); ++ ++} ++void os::print_context(outputStream *st, void *context) { ++ if (context == NULL) return; ++ ++ ucontext_t *uc = (ucontext_t*)context; ++ st->print_cr("Registers:"); ++ st->print( "R0=" INTPTR_FORMAT, (intptr_t)uc->uc_mcontext.gregs[0]); ++ st->print(", AT=" INTPTR_FORMAT, (intptr_t)uc->uc_mcontext.gregs[1]); ++ st->print(", V0=" INTPTR_FORMAT, (intptr_t)uc->uc_mcontext.gregs[2]); ++ st->print(", V1=" INTPTR_FORMAT, (intptr_t)uc->uc_mcontext.gregs[3]); ++ st->cr(); ++ st->print( "A0=" INTPTR_FORMAT, (intptr_t)uc->uc_mcontext.gregs[4]); ++ st->print(", A1=" INTPTR_FORMAT, (intptr_t)uc->uc_mcontext.gregs[5]); ++ st->print(", A2=" INTPTR_FORMAT, (intptr_t)uc->uc_mcontext.gregs[6]); ++ st->print(", A3=" INTPTR_FORMAT, (intptr_t)uc->uc_mcontext.gregs[7]); ++ st->cr(); ++ st->print( "A4=" INTPTR_FORMAT, (intptr_t)uc->uc_mcontext.gregs[8]); ++ st->print(", A5=" INTPTR_FORMAT, (intptr_t)uc->uc_mcontext.gregs[9]); ++ st->print(", A6=" INTPTR_FORMAT, (intptr_t)uc->uc_mcontext.gregs[10]); ++ st->print(", A7=" INTPTR_FORMAT, (intptr_t)uc->uc_mcontext.gregs[11]); ++ st->cr(); ++ st->print( "T0=" INTPTR_FORMAT, (intptr_t)uc->uc_mcontext.gregs[12]); ++ st->print(", T1=" INTPTR_FORMAT, (intptr_t)uc->uc_mcontext.gregs[13]); ++ st->print(", T2=" INTPTR_FORMAT, (intptr_t)uc->uc_mcontext.gregs[14]); ++ st->print(", T3=" INTPTR_FORMAT, (intptr_t)uc->uc_mcontext.gregs[15]); ++ st->cr(); ++ st->print( "S0=" INTPTR_FORMAT, (intptr_t)uc->uc_mcontext.gregs[16]); ++ st->print(", S1=" INTPTR_FORMAT, (intptr_t)uc->uc_mcontext.gregs[17]); ++ st->print(", S2=" INTPTR_FORMAT, (intptr_t)uc->uc_mcontext.gregs[18]); ++ st->print(", S3=" INTPTR_FORMAT, (intptr_t)uc->uc_mcontext.gregs[19]); ++ st->cr(); ++ st->print( "S4=" INTPTR_FORMAT, (intptr_t)uc->uc_mcontext.gregs[20]); ++ st->print(", S5=" INTPTR_FORMAT, (intptr_t)uc->uc_mcontext.gregs[21]); ++ st->print(", S6=" INTPTR_FORMAT, (intptr_t)uc->uc_mcontext.gregs[22]); ++ st->print(", S7=" INTPTR_FORMAT, (intptr_t)uc->uc_mcontext.gregs[23]); ++ st->cr(); ++ st->print( "T8=" INTPTR_FORMAT, (intptr_t)uc->uc_mcontext.gregs[24]); ++ st->print(", T9=" INTPTR_FORMAT, (intptr_t)uc->uc_mcontext.gregs[25]); ++ st->print(", K0=" INTPTR_FORMAT, (intptr_t)uc->uc_mcontext.gregs[26]); ++ st->print(", K1=" INTPTR_FORMAT, (intptr_t)uc->uc_mcontext.gregs[27]); ++ st->cr(); ++ st->print( "GP=" INTPTR_FORMAT, (intptr_t)uc->uc_mcontext.gregs[28]); ++ st->print(", SP=" INTPTR_FORMAT, (intptr_t)uc->uc_mcontext.gregs[29]); ++ st->print(", FP=" INTPTR_FORMAT, (intptr_t)uc->uc_mcontext.gregs[30]); ++ st->print(", RA=" INTPTR_FORMAT, (intptr_t)uc->uc_mcontext.gregs[31]); ++ st->cr(); ++ st->cr(); ++ ++ intptr_t *sp = (intptr_t *)os::Linux::ucontext_get_sp(uc); ++ st->print_cr("Top of Stack: (sp=" PTR_FORMAT ")", p2i(sp)); ++ //print_hex_dump(st, (address)sp, (address)(sp + 8*sizeof(intptr_t)), sizeof(intptr_t)); ++ print_hex_dump(st, (address)sp-32, (address)(sp + 32), sizeof(intptr_t)); ++ st->cr(); ++ ++ // Note: it may be unsafe to inspect memory near pc. For example, pc may ++ // point to garbage if entry point in an nmethod is corrupted. Leave ++ // this at the end, and hope for the best. ++ address pc = os::Linux::ucontext_get_pc(uc); ++ st->print_cr("Instructions: (pc=" PTR_FORMAT ")", p2i(pc)); ++ print_hex_dump(st, pc - 64, pc + 64, sizeof(char)); ++ Disassembler::decode(pc - 80, pc + 80, st); ++} ++ ++void os::setup_fpu() { ++ /* ++ //no use for MIPS ++ int fcsr; ++ address fpu_cntrl = StubRoutines::addr_fpu_cntrl_wrd_std(); ++ __asm__ __volatile__ ( ++ ".set noat;" ++ "cfc1 %0, $31;" ++ "sw %0, 0(%1);" ++ : "=r" (fcsr) ++ : "r" (fpu_cntrl) ++ : "memory" ++ ); ++ printf("fpu_cntrl: %lx\n", fpu_cntrl); ++ */ ++} ++ ++#ifndef PRODUCT ++void os::verify_stack_alignment() { ++ assert(((intptr_t)os::current_stack_pointer() & (StackAlignmentInBytes-1)) == 0, "incorrect stack alignment"); ++} ++#endif ++ ++bool os::is_ActiveCoresMP() { ++ return UseActiveCoresMP && _initial_active_processor_count == 1; ++} +diff --git a/hotspot/src/os_cpu/linux_mips/vm/os_linux_mips.hpp b/hotspot/src/os_cpu/linux_mips/vm/os_linux_mips.hpp +new file mode 100644 +index 0000000000..c07d08156f +--- /dev/null ++++ b/hotspot/src/os_cpu/linux_mips/vm/os_linux_mips.hpp +@@ -0,0 +1,39 @@ ++/* ++ * Copyright (c) 1999, 2013, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2015, 2022, Loongson Technology. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ * ++ */ ++ ++#ifndef OS_CPU_LINUX_MIPS_VM_OS_LINUX_MIPS_HPP ++#define OS_CPU_LINUX_MIPS_VM_OS_LINUX_MIPS_HPP ++ ++ static void setup_fpu(); ++ static bool is_allocatable(size_t bytes); ++ static intptr_t *get_previous_fp(); ++ ++ // Used to register dynamic code cache area with the OS ++ // Note: Currently only used in 64 bit Windows implementations ++ static bool register_code_area(char *low, char *high) { return true; } ++ ++ static bool is_ActiveCoresMP(); ++ ++#endif // OS_CPU_LINUX_MIPS_VM_OS_LINUX_MIPS_HPP +diff --git a/hotspot/src/os_cpu/linux_mips/vm/prefetch_linux_mips.inline.hpp b/hotspot/src/os_cpu/linux_mips/vm/prefetch_linux_mips.inline.hpp +new file mode 100644 +index 0000000000..93490345f0 +--- /dev/null ++++ b/hotspot/src/os_cpu/linux_mips/vm/prefetch_linux_mips.inline.hpp +@@ -0,0 +1,58 @@ ++/* ++ * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2015, 2021, Loongson Technology. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ * ++ */ ++ ++#ifndef OS_CPU_LINUX_MIPS_VM_PREFETCH_LINUX_MIPS_INLINE_HPP ++#define OS_CPU_LINUX_MIPS_VM_PREFETCH_LINUX_MIPS_INLINE_HPP ++ ++ ++inline void Prefetch::read (void *loc, intx interval) { ++ // 'pref' is implemented as NOP in Loongson 3A ++ __asm__ __volatile__ ( ++ " .set push\n" ++ " .set mips32\n" ++ " .set noreorder\n" ++ " pref 0, 0(%[__loc]) \n" ++ " .set pop\n" ++ : [__loc] "=&r"(loc) ++ : ++ : "memory" ++ ); ++} ++ ++inline void Prefetch::write(void *loc, intx interval) { ++ __asm__ __volatile__ ( ++ " .set push\n" ++ " .set mips32\n" ++ " .set noreorder\n" ++ " pref 1, 0(%[__loc]) \n" ++ " .set pop\n" ++ : [__loc] "=&r"(loc) ++ : ++ : "memory" ++ ); ++ ++} ++ ++#endif // OS_CPU_LINUX_MIPS_VM_PREFETCH_LINUX_MIPS_INLINE_HPP +diff --git a/hotspot/src/os_cpu/linux_mips/vm/threadLS_linux_mips.cpp b/hotspot/src/os_cpu/linux_mips/vm/threadLS_linux_mips.cpp +new file mode 100644 +index 0000000000..be28a562a1 +--- /dev/null ++++ b/hotspot/src/os_cpu/linux_mips/vm/threadLS_linux_mips.cpp +@@ -0,0 +1,84 @@ ++/* ++ * Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2015, 2022, Loongson Technology. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ * ++ */ ++ ++#include "precompiled.hpp" ++#include "runtime/thread.inline.hpp" ++#include "runtime/threadLocalStorage.hpp" ++ ++// Map stack pointer (%esp) to thread pointer for faster TLS access ++// ++// Here we use a flat table for better performance. Getting current thread ++// is down to one memory access (read _sp_map[%esp>>12]) in generated code ++// and two in runtime code (-fPIC code needs an extra load for _sp_map). ++// ++// This code assumes stack page is not shared by different threads. It works ++// in 32-bit VM when page size is 4K (or a multiple of 4K, if that matters). ++// ++// Notice that _sp_map is allocated in the bss segment, which is ZFOD ++// (zero-fill-on-demand). While it reserves 4M address space upfront, ++// actual memory pages are committed on demand. ++// ++// If an application creates and destroys a lot of threads, usually the ++// stack space freed by a thread will soon get reused by new thread ++// (this is especially true in NPTL or LinuxThreads in fixed-stack mode). ++// No memory page in _sp_map is wasted. ++// ++// However, it's still possible that we might end up populating & ++// committing a large fraction of the 4M table over time, but the actual ++// amount of live data in the table could be quite small. The max wastage ++// is less than 4M bytes. If it becomes an issue, we could use madvise() ++// with MADV_DONTNEED to reclaim unused (i.e. all-zero) pages in _sp_map. ++// MADV_DONTNEED on Linux keeps the virtual memory mapping, but zaps the ++// physical memory page (i.e. similar to MADV_FREE on Solaris). ++ ++#ifdef MINIMIZE_RAM_USAGE ++Thread* ThreadLocalStorage::_sp_map[1UL << (SP_BITLENGTH - PAGE_SHIFT)]; ++#endif // MINIMIZE_RAM_USAGE ++ ++void ThreadLocalStorage::generate_code_for_get_thread() { ++ // nothing we can do here for user-level thread ++} ++ ++void ThreadLocalStorage::pd_init() { ++#ifdef MINIMIZE_RAM_USAGE ++ assert(align_size_down(os::vm_page_size(), PAGE_SIZE) == os::vm_page_size(), ++ "page size must be multiple of PAGE_SIZE"); ++#endif // MINIMIZE_RAM_USAGE ++} ++ ++void ThreadLocalStorage::pd_set_thread(Thread* thread) { ++ os::thread_local_storage_at_put(ThreadLocalStorage::thread_index(), thread); ++#ifdef MINIMIZE_RAM_USAGE ++ address stack_top = os::current_stack_base(); ++ size_t stack_size = os::current_stack_size(); ++ ++ for (address p = stack_top - stack_size; p < stack_top; p += PAGE_SIZE) { ++ int index = ((uintptr_t)p >> PAGE_SHIFT) & ((1UL << (SP_BITLENGTH - PAGE_SHIFT)) - 1); ++ assert(thread == NULL || _sp_map[index] == NULL || thread == _sp_map[index], ++ "thread exited without detaching from VM??"); ++ _sp_map[index] = thread; ++ } ++#endif // MINIMIZE_RAM_USAGE ++} +diff --git a/hotspot/src/os_cpu/linux_mips/vm/threadLS_linux_mips.hpp b/hotspot/src/os_cpu/linux_mips/vm/threadLS_linux_mips.hpp +new file mode 100644 +index 0000000000..e595195e21 +--- /dev/null ++++ b/hotspot/src/os_cpu/linux_mips/vm/threadLS_linux_mips.hpp +@@ -0,0 +1,61 @@ ++/* ++ * Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2015, 2022, Loongson Technology. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ * ++ */ ++ ++#ifndef OS_CPU_LINUX_MIPS_VM_THREADLS_LINUX_MIPS_HPP ++#define OS_CPU_LINUX_MIPS_VM_THREADLS_LINUX_MIPS_HPP ++ ++#ifdef MINIMIZE_RAM_USAGE ++ // Processor dependent parts of ThreadLocalStorage ++ //only the low 2G space for user program in Linux ++ ++ #define SP_BITLENGTH 34 ++ #define PAGE_SHIFT 14 ++ #define PAGE_SIZE (1UL << PAGE_SHIFT) ++ ++ static Thread* _sp_map[1UL << (SP_BITLENGTH - PAGE_SHIFT)]; ++ static int _sp_map_low; ++ static int _sp_map_high; ++#endif // MINIMIZE_RAM_USAGE ++ ++public: ++#ifdef MINIMIZE_RAM_USAGE ++ static Thread** sp_map_addr() { return _sp_map; } ++#endif // MINIMIZE_RAM_USAGE ++ ++ static Thread* thread() { ++#ifdef MINIMIZE_RAM_USAGE ++ /* Thread::thread() can also be optimized in the same way as __get_thread() */ ++ //return (Thread*) os::thread_local_storage_at(thread_index()); ++ uintptr_t sp; ++ uintptr_t mask = (1UL << (SP_BITLENGTH - PAGE_SHIFT)) - 1; ++ ++ __asm__ __volatile__ ("daddiu %0, $29, 0 " : "=r" (sp)); ++ ++ return _sp_map[(sp >> PAGE_SHIFT) & mask]; ++#else ++ return (Thread*) os::thread_local_storage_at(thread_index()); ++#endif // MINIMIZE_RAM_USAGE ++ } ++#endif // OS_CPU_LINUX_MIPS_VM_THREADLS_LINUX_MIPS_HPP +diff --git a/hotspot/src/os_cpu/linux_mips/vm/thread_linux_mips.cpp b/hotspot/src/os_cpu/linux_mips/vm/thread_linux_mips.cpp +new file mode 100644 +index 0000000000..44f666d61f +--- /dev/null ++++ b/hotspot/src/os_cpu/linux_mips/vm/thread_linux_mips.cpp +@@ -0,0 +1,99 @@ ++/* ++ * Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2015, 2021, Loongson Technology. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ * ++ */ ++ ++#include "precompiled.hpp" ++#include "runtime/frame.inline.hpp" ++#include "runtime/thread.inline.hpp" ++#include "runtime/sharedRuntime.hpp" ++ ++void JavaThread::pd_initialize() ++{ ++ _anchor.clear(); ++} ++ ++// For Forte Analyzer AsyncGetCallTrace profiling support - thread is ++// currently interrupted by SIGPROF ++bool JavaThread::pd_get_top_frame_for_signal_handler(frame* fr_addr, ++ void* ucontext, bool isInJava) { ++ ++ assert(Thread::current() == this, "caller must be current thread"); ++ return pd_get_top_frame(fr_addr, ucontext, isInJava); ++} ++ ++bool JavaThread::pd_get_top_frame_for_profiling(frame* fr_addr, void* ucontext, bool isInJava) { ++ return pd_get_top_frame(fr_addr, ucontext, isInJava); ++} ++ ++bool JavaThread::pd_get_top_frame(frame* fr_addr, void* ucontext, bool isInJava) { ++ assert(this->is_Java_thread(), "must be JavaThread"); ++ JavaThread* jt = (JavaThread *)this; ++ ++ // If we have a last_Java_frame, then we should use it even if ++ // isInJava == true. It should be more reliable than ucontext info. ++ if (jt->has_last_Java_frame() && jt->frame_anchor()->walkable()) { ++ *fr_addr = jt->pd_last_frame(); ++ return true; ++ } ++ ++ // At this point, we don't have a last_Java_frame, so ++ // we try to glean some information out of the ucontext ++ // if we were running Java code when SIGPROF came in. ++ if (isInJava) { ++ ucontext_t* uc = (ucontext_t*) ucontext; ++ ++ intptr_t* ret_fp; ++ intptr_t* ret_sp; ++ ExtendedPC addr = os::Linux::fetch_frame_from_ucontext(this, uc, ++ &ret_sp, &ret_fp); ++ if (addr.pc() == NULL || ret_sp == NULL ) { ++ // ucontext wasn't useful ++ return false; ++ } ++ ++ frame ret_frame(ret_sp, ret_fp, addr.pc()); ++ if (!ret_frame.safe_for_sender(jt)) { ++#ifdef COMPILER2 ++ // C2 uses ebp as a general register see if NULL fp helps ++ frame ret_frame2(ret_sp, NULL, addr.pc()); ++ if (!ret_frame2.safe_for_sender(jt)) { ++ // nothing else to try if the frame isn't good ++ return false; ++ } ++ ret_frame = ret_frame2; ++#else ++ // nothing else to try if the frame isn't good ++ return false; ++#endif /* COMPILER2 */ ++ } ++ *fr_addr = ret_frame; ++ return true; ++ } ++ ++ // nothing else to try ++ return false; ++} ++ ++void JavaThread::cache_global_variables() { } ++ +diff --git a/hotspot/src/os_cpu/linux_mips/vm/thread_linux_mips.hpp b/hotspot/src/os_cpu/linux_mips/vm/thread_linux_mips.hpp +new file mode 100644 +index 0000000000..cb11c36ae5 +--- /dev/null ++++ b/hotspot/src/os_cpu/linux_mips/vm/thread_linux_mips.hpp +@@ -0,0 +1,75 @@ ++/* ++ * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2015, 2021, Loongson Technology. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ * ++ */ ++ ++#ifndef OS_CPU_LINUX_MIPS_VM_THREAD_LINUX_MIPS_HPP ++#define OS_CPU_LINUX_MIPS_VM_THREAD_LINUX_MIPS_HPP ++ ++ private: ++ void pd_initialize(); ++ ++ frame pd_last_frame() { ++ assert(has_last_Java_frame(), "must have last_Java_sp() when suspended"); ++ if (_anchor.last_Java_pc() != NULL) { ++ return frame(_anchor.last_Java_sp(), _anchor.last_Java_fp(), _anchor.last_Java_pc()); ++ } else { ++ // This will pick up pc from sp ++ return frame(_anchor.last_Java_sp(), _anchor.last_Java_fp()); ++ } ++ } ++ ++ ++ public: ++ // Mutators are highly dangerous.... ++ intptr_t* last_Java_fp() { return _anchor.last_Java_fp(); } ++ void set_last_Java_fp(intptr_t* fp) { _anchor.set_last_Java_fp(fp); } ++ ++ void set_base_of_stack_pointer(intptr_t* base_sp) { ++ } ++ ++ static ByteSize last_Java_fp_offset() { ++ return byte_offset_of(JavaThread, _anchor) + JavaFrameAnchor::last_Java_fp_offset(); ++ } ++ ++ intptr_t* base_of_stack_pointer() { ++ return NULL; ++ } ++ void record_base_of_stack_pointer() { ++ } ++ ++ bool pd_get_top_frame_for_signal_handler(frame* fr_addr, void* ucontext, ++ bool isInJava); ++ ++ bool pd_get_top_frame_for_profiling(frame* fr_addr, void* ucontext, bool isInJava); ++private: ++ bool pd_get_top_frame(frame* fr_addr, void* ucontext, bool isInJava); ++public: ++ ++ // These routines are only used on cpu architectures that ++ // have separate register stacks (Itanium). ++ static bool register_stack_overflow() { return false; } ++ static void enable_register_stack_guard() {} ++ static void disable_register_stack_guard() {} ++ ++#endif // OS_CPU_LINUX_MIPS_VM_THREAD_LINUX_MIPS_HPP +diff --git a/hotspot/src/os_cpu/linux_mips/vm/vmStructs_linux_mips.hpp b/hotspot/src/os_cpu/linux_mips/vm/vmStructs_linux_mips.hpp +new file mode 100644 +index 0000000000..b7454bf045 +--- /dev/null ++++ b/hotspot/src/os_cpu/linux_mips/vm/vmStructs_linux_mips.hpp +@@ -0,0 +1,55 @@ ++/* ++ * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2015, 2016, Loongson Technology. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ * ++ */ ++ ++#ifndef OS_CPU_LINUX_MIPS_VM_VMSTRUCTS_LINUX_MIPS_HPP ++#define OS_CPU_LINUX_MIPS_VM_VMSTRUCTS_LINUX_MIPS_HPP ++ ++// These are the OS and CPU-specific fields, types and integer ++// constants required by the Serviceability Agent. This file is ++// referenced by vmStructs.cpp. ++ ++#define VM_STRUCTS_OS_CPU(nonstatic_field, static_field, unchecked_nonstatic_field, volatile_nonstatic_field, nonproduct_nonstatic_field, c2_nonstatic_field, unchecked_c1_static_field, unchecked_c2_static_field) \ ++ \ ++ /******************************/ \ ++ /* Threads (NOTE: incomplete) */ \ ++ /******************************/ \ ++ nonstatic_field(OSThread, _thread_id, pid_t) \ ++ nonstatic_field(OSThread, _pthread_id, pthread_t) ++ ++ ++#define VM_TYPES_OS_CPU(declare_type, declare_toplevel_type, declare_oop_type, declare_integer_type, declare_unsigned_integer_type, declare_c1_toplevel_type, declare_c2_type, declare_c2_toplevel_type) \ ++ \ ++ /**********************/ \ ++ /* Posix Thread IDs */ \ ++ /**********************/ \ ++ \ ++ declare_integer_type(pid_t) \ ++ declare_unsigned_integer_type(pthread_t) ++ ++#define VM_INT_CONSTANTS_OS_CPU(declare_constant, declare_preprocessor_constant, declare_c1_constant, declare_c2_constant, declare_c2_preprocessor_constant) ++ ++#define VM_LONG_CONSTANTS_OS_CPU(declare_constant, declare_preprocessor_constant, declare_c1_constant, declare_c2_constant, declare_c2_preprocessor_constant) ++ ++#endif // OS_CPU_LINUX_MIPS_VM_VMSTRUCTS_LINUX_MIPS_HPP +diff --git a/hotspot/src/os_cpu/linux_mips/vm/vm_version_linux_mips.cpp b/hotspot/src/os_cpu/linux_mips/vm/vm_version_linux_mips.cpp +new file mode 100644 +index 0000000000..ce697823b9 +--- /dev/null ++++ b/hotspot/src/os_cpu/linux_mips/vm/vm_version_linux_mips.cpp +@@ -0,0 +1,28 @@ ++/* ++ * Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2015, 2019, Loongson Technology. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ * ++ */ ++ ++#include "precompiled.hpp" ++#include "runtime/os.hpp" ++#include "vm_version_mips.hpp" +diff --git a/hotspot/src/share/tools/hsdis/Makefile b/hotspot/src/share/tools/hsdis/Makefile +index 0d1b608944..a9754ce2ac 100644 +--- a/hotspot/src/share/tools/hsdis/Makefile ++++ b/hotspot/src/share/tools/hsdis/Makefile +@@ -105,12 +105,25 @@ CFLAGS/sparc += -m32 + endif + CFLAGS += $(CFLAGS/$(ARCH)) + CFLAGS += -fPIC ++ifeq ($(ARCH), mips64) ++CPUINFO = $(shell cat /proc/cpuinfo) ++ifneq ($(findstring Loongson,$(CPUINFO)),) ++CFLAGS += -DLOONGSON ++endif ++endif + OS = linux + LIB_EXT = .so + CC = gcc + endif + CFLAGS += -O + DLDFLAGS += -shared ++ifeq ($(ARCH), mips64) ++DLDFLAGS += -Wl,-z,noexecstack ++endif ++ifeq ($(ARCH), loongarch64) ++DLDFLAGS += -Wl,-z,noexecstack ++CONFIGURE_ARGS += --disable-werror ++endif + LDFLAGS += -ldl + OUTFLAGS += -o $@ + else +diff --git a/hotspot/src/share/tools/hsdis/hsdis.c b/hotspot/src/share/tools/hsdis/hsdis.c +index 4fb4964870..f6ef5bea15 100644 +--- a/hotspot/src/share/tools/hsdis/hsdis.c ++++ b/hotspot/src/share/tools/hsdis/hsdis.c +@@ -493,6 +493,16 @@ static const char* native_arch_name() { + #if defined(LIBARCH_ppc64) || defined(LIBARCH_ppc64le) + res = "powerpc:common64"; + #endif ++#ifdef LIBARCH_mips64 ++#ifdef LOONGSON ++ res = "mips:loongson_3a"; ++#else ++ res = "mips:isa64"; ++#endif ++#endif ++#ifdef LIBARCH_loongarch64 ++ res = "loongarch"; ++#endif + #ifdef LIBARCH_aarch64 + res = "aarch64"; + #endif +diff --git a/hotspot/src/share/vm/adlc/main.cpp b/hotspot/src/share/vm/adlc/main.cpp +index 52044f12d4..50c585872e 100644 +--- a/hotspot/src/share/vm/adlc/main.cpp ++++ b/hotspot/src/share/vm/adlc/main.cpp +@@ -22,6 +22,12 @@ + * + */ + ++/* ++ * This file has been modified by Loongson Technology in 2020. These ++ * modifications are Copyright (c) 2015, 2020, Loongson Technology, and are made ++ * available on the same license terms set forth above. ++ */ ++ + // MAIN.CPP - Entry point for the Architecture Description Language Compiler + #include "adlc.hpp" + +@@ -234,6 +240,14 @@ int main(int argc, char *argv[]) + AD.addInclude(AD._CPP_file, "nativeInst_x86.hpp"); + AD.addInclude(AD._CPP_file, "vmreg_x86.inline.hpp"); + #endif ++#ifdef TARGET_ARCH_mips ++ AD.addInclude(AD._CPP_file, "nativeInst_mips.hpp"); ++ AD.addInclude(AD._CPP_file, "vmreg_mips.inline.hpp"); ++#endif ++#ifdef TARGET_ARCH_loongarch ++ AD.addInclude(AD._CPP_file, "nativeInst_loongarch.hpp"); ++ AD.addInclude(AD._CPP_file, "vmreg_loongarch.inline.hpp"); ++#endif + #ifdef TARGET_ARCH_aarch64 + AD.addInclude(AD._CPP_file, "assembler_aarch64.inline.hpp"); + AD.addInclude(AD._CPP_file, "nativeInst_aarch64.hpp"); +diff --git a/hotspot/src/share/vm/asm/assembler.hpp b/hotspot/src/share/vm/asm/assembler.hpp +index f7f1ae1d36..572aa997ca 100644 +--- a/hotspot/src/share/vm/asm/assembler.hpp ++++ b/hotspot/src/share/vm/asm/assembler.hpp +@@ -22,6 +22,12 @@ + * + */ + ++/* ++ * This file has been modified by Loongson Technology in 2020. These ++ * modifications are Copyright (c) 2015, 2020, Loongson Technology, and are made ++ * available on the same license terms set forth above. ++ */ ++ + #ifndef SHARE_VM_ASM_ASSEMBLER_HPP + #define SHARE_VM_ASM_ASSEMBLER_HPP + +@@ -53,6 +59,14 @@ + # include "register_ppc.hpp" + # include "vm_version_ppc.hpp" + #endif ++#ifdef TARGET_ARCH_mips ++# include "register_mips.hpp" ++# include "vm_version_mips.hpp" ++#endif ++#ifdef TARGET_ARCH_loongarch ++# include "register_loongarch.hpp" ++# include "vm_version_loongarch.hpp" ++#endif + #ifdef TARGET_ARCH_aarch64 + # include "register_aarch64.hpp" + # include "vm_version_aarch64.hpp" +@@ -468,6 +482,12 @@ class AbstractAssembler : public ResourceObj { + #ifdef TARGET_ARCH_ppc + # include "assembler_ppc.hpp" + #endif ++#ifdef TARGET_ARCH_mips ++# include "assembler_mips.hpp" ++#endif ++#ifdef TARGET_ARCH_loongarch ++# include "assembler_loongarch.hpp" ++#endif + + + #endif // SHARE_VM_ASM_ASSEMBLER_HPP +diff --git a/hotspot/src/share/vm/asm/assembler.inline.hpp b/hotspot/src/share/vm/asm/assembler.inline.hpp +index 1a48cb3171..8ac90e1474 100644 +--- a/hotspot/src/share/vm/asm/assembler.inline.hpp ++++ b/hotspot/src/share/vm/asm/assembler.inline.hpp +@@ -22,6 +22,12 @@ + * + */ + ++/* ++ * This file has been modified by Loongson Technology in 2020. These ++ * modifications are Copyright (c) 2015, 2020, Loongson Technology, and are made ++ * available on the same license terms set forth above. ++ */ ++ + #ifndef SHARE_VM_ASM_ASSEMBLER_INLINE_HPP + #define SHARE_VM_ASM_ASSEMBLER_INLINE_HPP + +@@ -42,6 +48,12 @@ + #ifdef TARGET_ARCH_ppc + # include "assembler_ppc.inline.hpp" + #endif ++#ifdef TARGET_ARCH_mips ++# include "assembler_mips.inline.hpp" ++#endif ++#ifdef TARGET_ARCH_loongarch ++# include "assembler_loongarch.inline.hpp" ++#endif + #ifdef TARGET_ARCH_aarch64 + # include "assembler_aarch64.inline.hpp" + #endif +diff --git a/hotspot/src/share/vm/asm/codeBuffer.cpp b/hotspot/src/share/vm/asm/codeBuffer.cpp +index d94ac40655..6172d32aac 100644 +--- a/hotspot/src/share/vm/asm/codeBuffer.cpp ++++ b/hotspot/src/share/vm/asm/codeBuffer.cpp +@@ -22,6 +22,12 @@ + * + */ + ++/* ++ * This file has been modified by Loongson Technology in 2021. These ++ * modifications are Copyright (c) 2015, 2021, Loongson Technology, and are made ++ * available on the same license terms set forth above. ++ */ ++ + #include "precompiled.hpp" + #include "asm/codeBuffer.hpp" + #include "compiler/disassembler.hpp" +@@ -323,6 +329,9 @@ void CodeSection::relocate(address at, RelocationHolder const& spec, int format) + assert(rtype == relocInfo::none || + rtype == relocInfo::runtime_call_type || + rtype == relocInfo::internal_word_type|| ++#if defined MIPS && !defined ZERO ++ rtype == relocInfo::internal_pc_type || ++#endif + rtype == relocInfo::section_word_type || + rtype == relocInfo::external_word_type, + "code needs relocation information"); +diff --git a/hotspot/src/share/vm/asm/codeBuffer.hpp b/hotspot/src/share/vm/asm/codeBuffer.hpp +index 02b619ad77..c04560a0bc 100644 +--- a/hotspot/src/share/vm/asm/codeBuffer.hpp ++++ b/hotspot/src/share/vm/asm/codeBuffer.hpp +@@ -22,6 +22,12 @@ + * + */ + ++/* ++ * This file has been modified by Loongson Technology in 2020. These ++ * modifications are Copyright (c) 2015, 2020, Loongson Technology, and are made ++ * available on the same license terms set forth above. ++ */ ++ + #ifndef SHARE_VM_ASM_CODEBUFFER_HPP + #define SHARE_VM_ASM_CODEBUFFER_HPP + +@@ -635,6 +641,12 @@ class CodeBuffer: public StackObj { + #ifdef TARGET_ARCH_ppc + # include "codeBuffer_ppc.hpp" + #endif ++#ifdef TARGET_ARCH_mips ++# include "codeBuffer_mips.hpp" ++#endif ++#ifdef TARGET_ARCH_loongarch ++# include "codeBuffer_loongarch.hpp" ++#endif + + }; + +diff --git a/hotspot/src/share/vm/asm/macroAssembler.hpp b/hotspot/src/share/vm/asm/macroAssembler.hpp +index 1482eb630b..0be415b6c5 100644 +--- a/hotspot/src/share/vm/asm/macroAssembler.hpp ++++ b/hotspot/src/share/vm/asm/macroAssembler.hpp +@@ -22,6 +22,12 @@ + * + */ + ++/* ++ * This file has been modified by Loongson Technology in 2020. These ++ * modifications are Copyright (c) 2015, 2020, Loongson Technology, and are made ++ * available on the same license terms set forth above. ++ */ ++ + #ifndef SHARE_VM_ASM_MACROASSEMBLER_HPP + #define SHARE_VM_ASM_MACROASSEMBLER_HPP + +@@ -45,5 +51,10 @@ + #ifdef TARGET_ARCH_aarch64 + # include "macroAssembler_aarch64.hpp" + #endif +- ++#ifdef TARGET_ARCH_mips ++# include "macroAssembler_mips.hpp" ++#endif ++#ifdef TARGET_ARCH_loongarch ++# include "macroAssembler_loongarch.hpp" ++#endif + #endif // SHARE_VM_ASM_MACROASSEMBLER_HPP +diff --git a/hotspot/src/share/vm/asm/macroAssembler.inline.hpp b/hotspot/src/share/vm/asm/macroAssembler.inline.hpp +index db3daa52e9..6f4e523c59 100644 +--- a/hotspot/src/share/vm/asm/macroAssembler.inline.hpp ++++ b/hotspot/src/share/vm/asm/macroAssembler.inline.hpp +@@ -22,6 +22,12 @@ + * + */ + ++/* ++ * This file has been modified by Loongson Technology in 2020. These ++ * modifications are Copyright (c) 2015, 2020, Loongson Technology, and are made ++ * available on the same license terms set forth above. ++ */ ++ + #ifndef SHARE_VM_ASM_MACROASSEMBLER_INLINE_HPP + #define SHARE_VM_ASM_MACROASSEMBLER_INLINE_HPP + +@@ -42,6 +48,12 @@ + #ifdef TARGET_ARCH_ppc + # include "macroAssembler_ppc.inline.hpp" + #endif ++#ifdef TARGET_ARCH_mips ++# include "macroAssembler_mips.inline.hpp" ++#endif ++#ifdef TARGET_ARCH_loongarch ++# include "macroAssembler_loongarch.inline.hpp" ++#endif + #ifdef TARGET_ARCH_aarch64 + # include "macroAssembler_aarch64.inline.hpp" + #endif +diff --git a/hotspot/src/share/vm/asm/register.hpp b/hotspot/src/share/vm/asm/register.hpp +index c500890181..6a20929e59 100644 +--- a/hotspot/src/share/vm/asm/register.hpp ++++ b/hotspot/src/share/vm/asm/register.hpp +@@ -22,6 +22,12 @@ + * + */ + ++/* ++ * This file has been modified by Loongson Technology in 2020. These ++ * modifications are Copyright (c) 2015, 2020, Loongson Technology, and are made ++ * available on the same license terms set forth above. ++ */ ++ + #ifndef SHARE_VM_ASM_REGISTER_HPP + #define SHARE_VM_ASM_REGISTER_HPP + +@@ -108,6 +114,12 @@ const type name = ((type)name##_##type##EnumValue) + #ifdef TARGET_ARCH_ppc + # include "register_ppc.hpp" + #endif ++#ifdef TARGET_ARCH_mips ++# include "register_mips.hpp" ++#endif ++#ifdef TARGET_ARCH_loongarch ++# include "register_loongarch.hpp" ++#endif + #ifdef TARGET_ARCH_aarch64 + # include "register_aarch64.hpp" + #endif +diff --git a/hotspot/src/share/vm/c1/c1_Defs.hpp b/hotspot/src/share/vm/c1/c1_Defs.hpp +index b0cd763739..b42b9de1b5 100644 +--- a/hotspot/src/share/vm/c1/c1_Defs.hpp ++++ b/hotspot/src/share/vm/c1/c1_Defs.hpp +@@ -22,6 +22,12 @@ + * + */ + ++/* ++ * This file has been modified by Loongson Technology in 2022. These ++ * modifications are Copyright (c) 2015, 2022, Loongson Technology, and are made ++ * available on the same license terms set forth above. ++ */ ++ + #ifndef SHARE_VM_C1_C1_DEFS_HPP + #define SHARE_VM_C1_C1_DEFS_HPP + +@@ -29,6 +35,9 @@ + #ifdef TARGET_ARCH_x86 + # include "register_x86.hpp" + #endif ++#ifdef TARGET_ARCH_loongarch ++# include "register_loongarch.hpp" ++#endif + #ifdef TARGET_ARCH_aarch64 + # include "register_aarch64.hpp" + #endif +@@ -56,6 +65,9 @@ enum { + #ifdef TARGET_ARCH_x86 + # include "c1_Defs_x86.hpp" + #endif ++#ifdef TARGET_ARCH_loongarch ++# include "c1_Defs_loongarch.hpp" ++#endif + #ifdef TARGET_ARCH_aarch64 + # include "c1_Defs_aarch64.hpp" + #endif +diff --git a/hotspot/src/share/vm/c1/c1_FpuStackSim.hpp b/hotspot/src/share/vm/c1/c1_FpuStackSim.hpp +index f07e97a4d3..6bc367a897 100644 +--- a/hotspot/src/share/vm/c1/c1_FpuStackSim.hpp ++++ b/hotspot/src/share/vm/c1/c1_FpuStackSim.hpp +@@ -22,6 +22,12 @@ + * + */ + ++/* ++ * This file has been modified by Loongson Technology in 2022. These ++ * modifications are Copyright (c) 2015, 2022, Loongson Technology, and are made ++ * available on the same license terms set forth above. ++ */ ++ + #ifndef SHARE_VM_C1_C1_FPUSTACKSIM_HPP + #define SHARE_VM_C1_C1_FPUSTACKSIM_HPP + +@@ -35,6 +41,9 @@ class FpuStackSim; + #ifdef TARGET_ARCH_x86 + # include "c1_FpuStackSim_x86.hpp" + #endif ++#ifdef TARGET_ARCH_loongarch ++# include "c1_FpuStackSim_loongarch.hpp" ++#endif + #ifdef TARGET_ARCH_aarch64 + # include "c1_FpuStackSim_aarch64.hpp" + #endif +diff --git a/hotspot/src/share/vm/c1/c1_FrameMap.cpp b/hotspot/src/share/vm/c1/c1_FrameMap.cpp +index 1dac94d58c..b1e37ec41c 100644 +--- a/hotspot/src/share/vm/c1/c1_FrameMap.cpp ++++ b/hotspot/src/share/vm/c1/c1_FrameMap.cpp +@@ -22,6 +22,12 @@ + * + */ + ++/* ++ * This file has been modified by Loongson Technology in 2022. These ++ * modifications are Copyright (c) 2015, 2022, Loongson Technology, and are made ++ * available on the same license terms set forth above. ++ */ ++ + #include "precompiled.hpp" + #include "c1/c1_FrameMap.hpp" + #include "c1/c1_LIR.hpp" +@@ -29,6 +35,9 @@ + #ifdef TARGET_ARCH_x86 + # include "vmreg_x86.inline.hpp" + #endif ++#ifdef TARGET_ARCH_loongarch ++# include "vmreg_loongarch.inline.hpp" ++#endif + #ifdef TARGET_ARCH_aarch64 + # include "vmreg_aarch64.inline.hpp" + #endif +diff --git a/hotspot/src/share/vm/c1/c1_FrameMap.hpp b/hotspot/src/share/vm/c1/c1_FrameMap.hpp +index 41571e3d16..c0e7b28ea4 100644 +--- a/hotspot/src/share/vm/c1/c1_FrameMap.hpp ++++ b/hotspot/src/share/vm/c1/c1_FrameMap.hpp +@@ -22,6 +22,12 @@ + * + */ + ++/* ++ * This file has been modified by Loongson Technology in 2022. These ++ * modifications are Copyright (c) 2015, 2022, Loongson Technology, and are made ++ * available on the same license terms set forth above. ++ */ ++ + #ifndef SHARE_VM_C1_C1_FRAMEMAP_HPP + #define SHARE_VM_C1_C1_FRAMEMAP_HPP + +@@ -85,6 +91,9 @@ class FrameMap : public CompilationResourceObj { + #ifdef TARGET_ARCH_x86 + # include "c1_FrameMap_x86.hpp" + #endif ++#ifdef TARGET_ARCH_loongarch ++# include "c1_FrameMap_loongarch.hpp" ++#endif + #ifdef TARGET_ARCH_aarch64 + # include "c1_FrameMap_aarch64.hpp" + #endif +diff --git a/hotspot/src/share/vm/c1/c1_LIR.cpp b/hotspot/src/share/vm/c1/c1_LIR.cpp +index fa37e7a046..67f86cd094 100644 +--- a/hotspot/src/share/vm/c1/c1_LIR.cpp ++++ b/hotspot/src/share/vm/c1/c1_LIR.cpp +@@ -22,6 +22,12 @@ + * + */ + ++/* ++ * This file has been modified by Loongson Technology in 2022. These ++ * modifications are Copyright (c) 2015, 2022, Loongson Technology, and are made ++ * available on the same license terms set forth above. ++ */ ++ + #include "precompiled.hpp" + #include "c1/c1_InstructionPrinter.hpp" + #include "c1/c1_LIR.hpp" +@@ -79,6 +85,17 @@ FloatRegister LIR_OprDesc::as_double_reg() const { + + #endif + ++#if defined(LOONGARCH64) ++ ++FloatRegister LIR_OprDesc::as_float_reg() const { ++ return as_FloatRegister(fpu_regnr()); ++} ++ ++FloatRegister LIR_OprDesc::as_double_reg() const { ++ return as_FloatRegister(fpu_regnrLo()); ++} ++ ++#endif + + LIR_Opr LIR_OprFact::illegalOpr = LIR_OprFact::illegal(); + +@@ -149,13 +166,19 @@ void LIR_Address::verify0() const { + #endif + #ifdef _LP64 + assert(base()->is_cpu_register(), "wrong base operand"); +-#ifndef AARCH64 ++#if !defined(AARCH64) && !defined(LOONGARCH64) + assert(index()->is_illegal() || index()->is_double_cpu(), "wrong index operand"); + #else + assert(index()->is_illegal() || index()->is_double_cpu() || index()->is_single_cpu(), "wrong index operand"); + #endif ++#ifdef LOONGARCH64 ++ assert(base()->type() == T_ADDRESS || base()->type() == T_OBJECT || ++ base()->type() == T_LONG || base()->type() == T_METADATA, ++ "wrong type for addresses"); ++#else + assert(base()->type() == T_OBJECT || base()->type() == T_LONG || base()->type() == T_METADATA, + "wrong type for addresses"); ++#endif + #else + assert(base()->is_single_cpu(), "wrong base operand"); + assert(index()->is_illegal() || index()->is_single_cpu(), "wrong index operand"); +@@ -258,8 +281,6 @@ bool LIR_OprDesc::is_oop() const { + } + } + +- +- + void LIR_Op2::verify() const { + #ifdef ASSERT + switch (code()) { +@@ -301,6 +322,18 @@ void LIR_Op2::verify() const { + #endif + } + ++void LIR_Op4::verify() const { ++#ifdef ASSERT ++ switch (code()) { ++ case lir_cmp_cmove: ++ break; ++ ++ default: ++ assert(!result_opr()->is_register() || !result_opr()->is_oop_register(), ++ "can't produce oops from arith"); ++ } ++#endif ++} + + LIR_OpBranch::LIR_OpBranch(LIR_Condition cond, BasicType type, BlockBegin* block) + : LIR_Op(lir_branch, LIR_OprFact::illegalOpr, (CodeEmitInfo*)NULL) +@@ -358,6 +391,55 @@ void LIR_OpBranch::negate_cond() { + } + } + ++LIR_OpCmpBranch::LIR_OpCmpBranch(LIR_Condition cond, LIR_Opr left, LIR_Opr right, CodeStub* stub, CodeEmitInfo* info) ++ : LIR_Op2(lir_cmp_branch, cond, left, right, info) ++ , _label(stub->entry()) ++ , _block(NULL) ++ , _ublock(NULL) ++ , _stub(stub) { ++} ++ ++LIR_OpCmpBranch::LIR_OpCmpBranch(LIR_Condition cond, LIR_Opr left, LIR_Opr right, BlockBegin* block, CodeEmitInfo* info) ++ : LIR_Op2(lir_cmp_branch, cond, left, right, info) ++ , _label(block->label()) ++ , _block(block) ++ , _ublock(NULL) ++ , _stub(NULL) { ++} ++ ++LIR_OpCmpBranch::LIR_OpCmpBranch(LIR_Condition cond, LIR_Opr left, LIR_Opr right, BlockBegin* block, BlockBegin* ublock, CodeEmitInfo* info) ++ : LIR_Op2(lir_cmp_float_branch, cond, left, right, info) ++ , _label(block->label()) ++ , _block(block) ++ , _ublock(ublock) ++ , _stub(NULL) { ++} ++ ++void LIR_OpCmpBranch::change_block(BlockBegin* b) { ++ assert(_block != NULL, "must have old block"); ++ assert(_block->label() == label(), "must be equal"); ++ ++ _block = b; ++ _label = b->label(); ++} ++ ++void LIR_OpCmpBranch::change_ublock(BlockBegin* b) { ++ assert(_ublock != NULL, "must have old block"); ++ ++ _ublock = b; ++} ++ ++void LIR_OpCmpBranch::negate_cond() { ++ switch (condition()) { ++ case lir_cond_equal: set_condition(lir_cond_notEqual); break; ++ case lir_cond_notEqual: set_condition(lir_cond_equal); break; ++ case lir_cond_less: set_condition(lir_cond_greaterEqual); break; ++ case lir_cond_lessEqual: set_condition(lir_cond_greater); break; ++ case lir_cond_greaterEqual: set_condition(lir_cond_less); break; ++ case lir_cond_greater: set_condition(lir_cond_lessEqual); break; ++ default: ShouldNotReachHere(); ++ } ++} + + LIR_OpTypeCheck::LIR_OpTypeCheck(LIR_Code code, LIR_Opr result, LIR_Opr object, ciKlass* klass, + LIR_Opr tmp1, LIR_Opr tmp2, LIR_Opr tmp3, +@@ -560,10 +642,7 @@ void LIR_OpVisitState::visit(LIR_Op* op) { + assert(opConvert->_info == NULL, "must be"); + if (opConvert->_opr->is_valid()) do_input(opConvert->_opr); + if (opConvert->_result->is_valid()) do_output(opConvert->_result); +-#if defined(PPC) || defined(AARCH64) +- if (opConvert->_tmp1->is_valid()) do_temp(opConvert->_tmp1); +- if (opConvert->_tmp2->is_valid()) do_temp(opConvert->_tmp2); +-#endif ++ if (opConvert->_tmp->is_valid()) do_temp(opConvert->_tmp); + do_stub(opConvert->_stub); + + break; +@@ -661,6 +740,25 @@ void LIR_OpVisitState::visit(LIR_Op* op) { + break; + } + ++// LIR_OpCmpBranch; ++ case lir_cmp_branch: // may have info, input and result register always invalid ++ case lir_cmp_float_branch: // may have info, input and result register always invalid ++ { ++ assert(op->as_OpCmpBranch() != NULL, "must be"); ++ LIR_OpCmpBranch* opCmpBranch = (LIR_OpCmpBranch*)op; ++ assert(opCmpBranch->_tmp2->is_illegal() && opCmpBranch->_tmp3->is_illegal() && ++ opCmpBranch->_tmp4->is_illegal() && opCmpBranch->_tmp5->is_illegal(), "not used"); ++ ++ if (opCmpBranch->_info) do_info(opCmpBranch->_info); ++ if (opCmpBranch->_opr1->is_valid()) do_input(opCmpBranch->_opr1); ++ if (opCmpBranch->_opr2->is_valid()) do_input(opCmpBranch->_opr2); ++ if (opCmpBranch->_tmp1->is_valid()) do_temp(opCmpBranch->_tmp1); ++ if (opCmpBranch->_stub != NULL) opCmpBranch->stub()->visit(this); ++ assert(opCmpBranch->_result->is_illegal(), "not used"); ++ ++ break; ++ } ++ + // special handling for cmove: right input operand must not be equal + // to the result operand, otherwise the backend fails + case lir_cmove: +@@ -806,6 +904,29 @@ void LIR_OpVisitState::visit(LIR_Op* op) { + break; + } + ++// LIR_Op4 ++ // special handling for cmp cmove: src2(opr4) operand must not be equal ++ // to the result operand, otherwise the backend fails ++ case lir_cmp_cmove: ++ { ++ assert(op->as_Op4() != NULL, "must be"); ++ LIR_Op4* op4 = (LIR_Op4*)op; ++ ++ assert(op4->_info == NULL, "not used"); ++ assert(op4->_opr1->is_valid() && op4->_opr2->is_valid() && ++ op4->_opr3->is_valid() && op4->_opr4->is_valid() && ++ op4->_result->is_valid(), "used"); ++ ++ do_input(op4->_opr1); ++ do_input(op4->_opr2); ++ do_input(op4->_opr3); ++ do_input(op4->_opr4); ++ do_temp(op4->_opr4); ++ do_output(op4->_result); ++ ++ break; ++ } ++ + + // LIR_OpJavaCall + case lir_static_call: +@@ -1121,6 +1242,13 @@ void LIR_Op2::emit_code(LIR_Assembler* masm) { + masm->emit_op2(this); + } + ++void LIR_OpCmpBranch::emit_code(LIR_Assembler* masm) { ++ masm->emit_opCmpBranch(this); ++ if (stub()) { ++ masm->append_code_stub(stub()); ++ } ++} ++ + void LIR_OpAllocArray::emit_code(LIR_Assembler* masm) { + masm->emit_alloc_array(this); + masm->append_code_stub(stub()); +@@ -1141,6 +1269,10 @@ void LIR_Op3::emit_code(LIR_Assembler* masm) { + masm->emit_op3(this); + } + ++void LIR_Op4::emit_code(LIR_Assembler* masm) { ++ masm->emit_op4(this); ++} ++ + void LIR_OpLock::emit_code(LIR_Assembler* masm) { + masm->emit_lock(this); + if (stub()) { +@@ -1381,7 +1513,6 @@ void LIR_List::cmp_mem_int(LIR_Condition condition, LIR_Opr base, int disp, int + info)); + } + +- + void LIR_List::cmp_reg_mem(LIR_Condition condition, LIR_Opr reg, LIR_Address* addr, CodeEmitInfo* info) { + append(new LIR_Op2( + lir_cmp, +@@ -1391,6 +1522,17 @@ void LIR_List::cmp_reg_mem(LIR_Condition condition, LIR_Opr reg, LIR_Address* ad + info)); + } + ++void LIR_List::null_check(LIR_Opr opr, CodeEmitInfo* info, bool deoptimize_on_null) { ++ if (deoptimize_on_null) { ++ // Emit an explicit null check and deoptimize if opr is null ++ CodeStub* deopt = new DeoptimizeStub(info); ++ cmp_branch(lir_cond_equal, opr, LIR_OprFact::oopConst(NULL), T_OBJECT, deopt); ++ } else { ++ // Emit an implicit null check ++ append(new LIR_Op1(lir_null_check, opr, info)); ++ } ++} ++ + void LIR_List::allocate_object(LIR_Opr dst, LIR_Opr t1, LIR_Opr t2, LIR_Opr t3, LIR_Opr t4, + int header_size, int object_size, LIR_Opr klass, bool init_check, CodeStub* stub) { + append(new LIR_OpAllocObj( +@@ -1520,18 +1662,6 @@ void LIR_List::store_check(LIR_Opr object, LIR_Opr array, LIR_Opr tmp1, LIR_Opr + append(c); + } + +-void LIR_List::null_check(LIR_Opr opr, CodeEmitInfo* info, bool deoptimize_on_null) { +- if (deoptimize_on_null) { +- // Emit an explicit null check and deoptimize if opr is null +- CodeStub* deopt = new DeoptimizeStub(info); +- cmp(lir_cond_equal, opr, LIR_OprFact::oopConst(NULL)); +- branch(lir_cond_equal, T_OBJECT, deopt); +- } else { +- // Emit an implicit null check +- append(new LIR_Op1(lir_null_check, opr, info)); +- } +-} +- + void LIR_List::cas_long(LIR_Opr addr, LIR_Opr cmp_value, LIR_Opr new_value, + LIR_Opr t1, LIR_Opr t2, LIR_Opr result) { + append(new LIR_OpCompareAndSwap(lir_cas_long, addr, cmp_value, new_value, t1, t2, result)); +@@ -1780,6 +1910,8 @@ const char * LIR_Op::name() const { + case lir_cmp_l2i: s = "cmp_l2i"; break; + case lir_ucmp_fd2i: s = "ucomp_fd2i"; break; + case lir_cmp_fd2i: s = "comp_fd2i"; break; ++ case lir_cmp_branch: s = "cmp_branch"; break; ++ case lir_cmp_float_branch: s = "cmp_fbranch"; break; + case lir_cmove: s = "cmove"; break; + case lir_add: s = "add"; break; + case lir_sub: s = "sub"; break; +@@ -1809,6 +1941,8 @@ const char * LIR_Op::name() const { + // LIR_Op3 + case lir_idiv: s = "idiv"; break; + case lir_irem: s = "irem"; break; ++ // LIR_Op4 ++ case lir_cmp_cmove: s = "cmp_cmove"; break; + // LIR_OpJavaCall + case lir_static_call: s = "static"; break; + case lir_optvirtual_call: s = "optvirtual"; break; +@@ -1960,6 +2094,26 @@ void LIR_OpBranch::print_instr(outputStream* out) const { + } + } + ++// LIR_OpCmpBranch ++void LIR_OpCmpBranch::print_instr(outputStream* out) const { ++ print_condition(out, condition()); out->print(" "); ++ in_opr1()->print(out); out->print(" "); ++ in_opr2()->print(out); out->print(" "); ++ if (block() != NULL) { ++ out->print("[B%d] ", block()->block_id()); ++ } else if (stub() != NULL) { ++ out->print("["); ++ stub()->print_name(out); ++ out->print(": " INTPTR_FORMAT "]", p2i(stub())); ++ if (stub()->info() != NULL) out->print(" [bci:%d]", stub()->info()->stack()->bci()); ++ } else { ++ out->print("[label:" INTPTR_FORMAT "] ", p2i(label())); ++ } ++ if (ublock() != NULL) { ++ out->print("unordered: [B%d] ", ublock()->block_id()); ++ } ++} ++ + void LIR_Op::print_condition(outputStream* out, LIR_Condition cond) { + switch(cond) { + case lir_cond_equal: out->print("[EQ]"); break; +@@ -1980,12 +2134,7 @@ void LIR_OpConvert::print_instr(outputStream* out) const { + print_bytecode(out, bytecode()); + in_opr()->print(out); out->print(" "); + result_opr()->print(out); out->print(" "); +-#if defined(PPC) || defined(AARCH64) +- if(tmp1()->is_valid()) { +- tmp1()->print(out); out->print(" "); +- tmp2()->print(out); out->print(" "); +- } +-#endif ++ if(tmp()->is_valid()) tmp()->print(out); out->print(" "); + } + + void LIR_OpConvert::print_bytecode(outputStream* out, Bytecodes::Code code) { +@@ -2031,9 +2180,6 @@ void LIR_OpRoundFP::print_instr(outputStream* out) const { + + // LIR_Op2 + void LIR_Op2::print_instr(outputStream* out) const { +- if (code() == lir_cmove) { +- print_condition(out, condition()); out->print(" "); +- } + in_opr1()->print(out); out->print(" "); + in_opr2()->print(out); out->print(" "); + if (tmp1_opr()->is_valid()) { tmp1_opr()->print(out); out->print(" "); } +@@ -2082,6 +2228,18 @@ void LIR_Op3::print_instr(outputStream* out) const { + result_opr()->print(out); + } + ++// LIR_Op4 ++void LIR_Op4::print_instr(outputStream* out) const { ++ if (code() == lir_cmp_cmove) { ++ print_condition(out, condition()); out->print(" "); ++ } ++ in_opr1()->print(out); out->print(" "); ++ in_opr2()->print(out); out->print(" "); ++ in_opr3()->print(out); out->print(" "); ++ in_opr4()->print(out); out->print(" "); ++ result_opr()->print(out); ++} ++ + + void LIR_OpLock::print_instr(outputStream* out) const { + hdr_opr()->print(out); out->print(" "); +@@ -2095,10 +2253,14 @@ void LIR_OpLock::print_instr(outputStream* out) const { + + #ifdef ASSERT + void LIR_OpAssert::print_instr(outputStream* out) const { ++ tty->print_cr("function LIR_OpAssert::print_instr unimplemented yet! "); ++ Unimplemented(); ++ /* + print_condition(out, condition()); out->print(" "); + in_opr1()->print(out); out->print(" "); + in_opr2()->print(out); out->print(", \""); + out->print("%s", msg()); out->print("\""); ++ */ + } + #endif + +diff --git a/hotspot/src/share/vm/c1/c1_LIR.hpp b/hotspot/src/share/vm/c1/c1_LIR.hpp +index 24b8620211..aec77afe1f 100644 +--- a/hotspot/src/share/vm/c1/c1_LIR.hpp ++++ b/hotspot/src/share/vm/c1/c1_LIR.hpp +@@ -22,6 +22,11 @@ + * + */ + ++/* ++ * This file has been modified by Loongson Technology in 2022. These ++ * modifications are Copyright (c) 2018, 2022, Loongson Technology, and are made ++ * available on the same license terms set forth above. ++ */ + #ifndef SHARE_VM_C1_C1_LIR_HPP + #define SHARE_VM_C1_C1_LIR_HPP + +@@ -452,7 +457,7 @@ class LIR_OprDesc: public CompilationResourceObj { + // for compatibility with RInfo + int fpu () const { return lo_reg_half(); } + #endif +-#if defined(SPARC) || defined(ARM) || defined(PPC) || defined(AARCH64) ++#if defined(SPARC) || defined(ARM) || defined(PPC) || defined(AARCH64) || defined(LOONGARCH) + FloatRegister as_float_reg () const; + FloatRegister as_double_reg () const; + #endif +@@ -542,7 +547,7 @@ class LIR_Address: public LIR_OprPtr { + , _type(type) + , _disp(0) { verify(); } + +-#if defined(X86) || defined(ARM) || defined(AARCH64) ++#if defined(X86) || defined(ARM) || defined(AARCH64) || defined(LOONGARCH) + LIR_Address(LIR_Opr base, LIR_Opr index, Scale scale, intx disp, BasicType type): + _base(base) + , _index(index) +@@ -658,7 +663,13 @@ class LIR_OprFact: public AllStatic { + LIR_OprDesc::double_type | + LIR_OprDesc::cpu_register | + LIR_OprDesc::double_size); } +-#endif // PPC ++#elif defined(LOONGARCH) ++ static LIR_Opr double_fpu(int reg) { return (LIR_Opr)(intptr_t)((reg << LIR_OprDesc::reg1_shift) | ++ (reg << LIR_OprDesc::reg2_shift) | ++ LIR_OprDesc::double_type | ++ LIR_OprDesc::fpu_register | ++ LIR_OprDesc::double_size); } ++#endif // LOONGARCH + + static LIR_Opr virtual_register(int index, BasicType type) { + LIR_Opr res; +@@ -872,9 +883,11 @@ class LIR_OpConvert; + class LIR_OpAllocObj; + class LIR_OpRoundFP; + class LIR_Op2; ++class LIR_OpCmpBranch; + class LIR_OpDelay; + class LIR_Op3; + class LIR_OpAllocArray; ++class LIR_Op4; + class LIR_OpCall; + class LIR_OpJavaCall; + class LIR_OpRTCall; +@@ -943,6 +956,8 @@ enum LIR_Code { + , lir_cmp_l2i + , lir_ucmp_fd2i + , lir_cmp_fd2i ++ , lir_cmp_branch ++ , lir_cmp_float_branch + , lir_cmove + , lir_add + , lir_sub +@@ -976,6 +991,9 @@ enum LIR_Code { + , lir_idiv + , lir_irem + , end_op3 ++ , begin_op4 ++ , lir_cmp_cmove ++ , end_op4 + , begin_opJavaCall + , lir_static_call + , lir_optvirtual_call +@@ -1139,12 +1157,14 @@ class LIR_Op: public CompilationResourceObj { + virtual LIR_OpAllocObj* as_OpAllocObj() { return NULL; } + virtual LIR_OpRoundFP* as_OpRoundFP() { return NULL; } + virtual LIR_OpBranch* as_OpBranch() { return NULL; } ++ virtual LIR_OpCmpBranch* as_OpCmpBranch() { return NULL; } + virtual LIR_OpRTCall* as_OpRTCall() { return NULL; } + virtual LIR_OpConvert* as_OpConvert() { return NULL; } + virtual LIR_Op0* as_Op0() { return NULL; } + virtual LIR_Op1* as_Op1() { return NULL; } + virtual LIR_Op2* as_Op2() { return NULL; } + virtual LIR_Op3* as_Op3() { return NULL; } ++ virtual LIR_Op4* as_Op4() { return NULL; } + virtual LIR_OpArrayCopy* as_OpArrayCopy() { return NULL; } + virtual LIR_OpUpdateCRC32* as_OpUpdateCRC32() { return NULL; } + virtual LIR_OpTypeCheck* as_OpTypeCheck() { return NULL; } +@@ -1474,37 +1494,18 @@ class LIR_OpConvert: public LIR_Op1 { + private: + Bytecodes::Code _bytecode; + ConversionStub* _stub; +-#if defined(PPC) || defined(AARCH64) +- LIR_Opr _tmp1; +- LIR_Opr _tmp2; +-#endif ++ LIR_Opr _tmp; + + public: +- LIR_OpConvert(Bytecodes::Code code, LIR_Opr opr, LIR_Opr result, ConversionStub* stub) ++ LIR_OpConvert(Bytecodes::Code code, LIR_Opr opr, LIR_Opr result, ConversionStub* stub, LIR_Opr tmp) + : LIR_Op1(lir_convert, opr, result) + , _stub(stub) +-#ifdef PPC +- , _tmp1(LIR_OprDesc::illegalOpr()) +- , _tmp2(LIR_OprDesc::illegalOpr()) +-#endif ++ , _tmp(tmp) + , _bytecode(code) {} + +-#if defined(PPC) || defined(AARCH64) +- LIR_OpConvert(Bytecodes::Code code, LIR_Opr opr, LIR_Opr result, ConversionStub* stub +- ,LIR_Opr tmp1, LIR_Opr tmp2) +- : LIR_Op1(lir_convert, opr, result) +- , _stub(stub) +- , _tmp1(tmp1) +- , _tmp2(tmp2) +- , _bytecode(code) {} +-#endif +- + Bytecodes::Code bytecode() const { return _bytecode; } + ConversionStub* stub() const { return _stub; } +-#if defined(PPC) || defined(AARCH64) +- LIR_Opr tmp1() const { return _tmp1; } +- LIR_Opr tmp2() const { return _tmp2; } +-#endif ++ LIR_Opr tmp() const { return _tmp; } + + virtual void emit_code(LIR_Assembler* masm); + virtual LIR_OpConvert* as_OpConvert() { return this; } +@@ -1659,7 +1660,7 @@ class LIR_Op2: public LIR_Op { + , _tmp3(LIR_OprFact::illegalOpr) + , _tmp4(LIR_OprFact::illegalOpr) + , _tmp5(LIR_OprFact::illegalOpr) { +- assert(code == lir_cmp || code == lir_assert, "code check"); ++ assert(code == lir_cmp || code == lir_cmp_branch || code == lir_cmp_float_branch || code == lir_assert, "code check"); + } + + LIR_Op2(LIR_Code code, LIR_Condition condition, LIR_Opr opr1, LIR_Opr opr2, LIR_Opr result, BasicType type) +@@ -1691,7 +1692,7 @@ class LIR_Op2: public LIR_Op { + , _tmp3(LIR_OprFact::illegalOpr) + , _tmp4(LIR_OprFact::illegalOpr) + , _tmp5(LIR_OprFact::illegalOpr) { +- assert(code != lir_cmp && is_in_range(code, begin_op2, end_op2), "code check"); ++ assert((code != lir_cmp && code != lir_cmp_branch && code != lir_cmp_float_branch) && is_in_range(code, begin_op2, end_op2), "code check"); + } + + LIR_Op2(LIR_Code code, LIR_Opr opr1, LIR_Opr opr2, LIR_Opr result, LIR_Opr tmp1, LIR_Opr tmp2 = LIR_OprFact::illegalOpr, +@@ -1707,7 +1708,7 @@ class LIR_Op2: public LIR_Op { + , _tmp3(tmp3) + , _tmp4(tmp4) + , _tmp5(tmp5) { +- assert(code != lir_cmp && is_in_range(code, begin_op2, end_op2), "code check"); ++ assert((code != lir_cmp && code != lir_cmp_branch && code != lir_cmp_float_branch) && is_in_range(code, begin_op2, end_op2), "code check"); + } + + LIR_Opr in_opr1() const { return _opr1; } +@@ -1719,10 +1720,12 @@ class LIR_Op2: public LIR_Op { + LIR_Opr tmp4_opr() const { return _tmp4; } + LIR_Opr tmp5_opr() const { return _tmp5; } + LIR_Condition condition() const { +- assert(code() == lir_cmp || code() == lir_cmove || code() == lir_assert, "only valid for cmp and cmove and assert"); return _condition; ++ assert(code() == lir_cmp || code() == lir_cmp_branch || code() == lir_cmp_float_branch || code() == lir_cmove || code() == lir_assert, "only valid for cmp and cmove and assert"); ++ return _condition; + } + void set_condition(LIR_Condition condition) { +- assert(code() == lir_cmp || code() == lir_cmove, "only valid for cmp and cmove"); _condition = condition; ++ assert(code() == lir_cmp || code() == lir_cmp_branch || code() == lir_cmp_float_branch || code() == lir_cmove, "only valid for cmp and cmove"); ++ _condition = condition; + } + + void set_fpu_stack_size(int size) { _fpu_stack_size = size; } +@@ -1736,6 +1739,43 @@ class LIR_Op2: public LIR_Op { + virtual void print_instr(outputStream* out) const PRODUCT_RETURN; + }; + ++class LIR_OpCmpBranch: public LIR_Op2 { ++ friend class LIR_OpVisitState; ++ ++ private: ++ Label* _label; ++ BlockBegin* _block; // if this is a branch to a block, this is the block ++ BlockBegin* _ublock; // if this is a float-branch, this is the unorderd block ++ CodeStub* _stub; // if this is a branch to a stub, this is the stub ++ ++ public: ++ LIR_OpCmpBranch(LIR_Condition cond, LIR_Opr left, LIR_Opr right, Label* lbl, CodeEmitInfo* info = NULL) ++ : LIR_Op2(lir_cmp_branch, cond, left, right, info) ++ , _label(lbl) ++ , _block(NULL) ++ , _ublock(NULL) ++ , _stub(NULL) { } ++ ++ LIR_OpCmpBranch(LIR_Condition cond, LIR_Opr left, LIR_Opr right, CodeStub* stub, CodeEmitInfo* info = NULL); ++ LIR_OpCmpBranch(LIR_Condition cond, LIR_Opr left, LIR_Opr right, BlockBegin* block, CodeEmitInfo* info = NULL); ++ ++ // for unordered comparisons ++ LIR_OpCmpBranch(LIR_Condition cond, LIR_Opr left, LIR_Opr right, BlockBegin* block, BlockBegin* ublock, CodeEmitInfo* info = NULL); ++ ++ Label* label() const { return _label; } ++ BlockBegin* block() const { return _block; } ++ BlockBegin* ublock() const { return _ublock; } ++ CodeStub* stub() const { return _stub; } ++ ++ void change_block(BlockBegin* b); ++ void change_ublock(BlockBegin* b); ++ void negate_cond(); ++ ++ virtual void emit_code(LIR_Assembler* masm); ++ virtual LIR_OpCmpBranch* as_OpCmpBranch() { return this; } ++ virtual void print_instr(outputStream* out) const PRODUCT_RETURN; ++}; ++ + class LIR_OpAllocArray : public LIR_Op { + friend class LIR_OpVisitState; + +@@ -1776,7 +1816,6 @@ class LIR_OpAllocArray : public LIR_Op { + virtual void print_instr(outputStream* out) const PRODUCT_RETURN; + }; + +- + class LIR_Op3: public LIR_Op { + friend class LIR_OpVisitState; + +@@ -1800,6 +1839,48 @@ class LIR_Op3: public LIR_Op { + }; + + ++class LIR_Op4: public LIR_Op { ++ friend class LIR_OpVisitState; ++ ++ private: ++ LIR_Opr _opr1; ++ LIR_Opr _opr2; ++ LIR_Opr _opr3; ++ LIR_Opr _opr4; ++ BasicType _type; ++ LIR_Condition _condition; ++ ++ void verify() const; ++ ++ public: ++ LIR_Op4(LIR_Code code, LIR_Condition condition, LIR_Opr opr1, LIR_Opr opr2, LIR_Opr opr3, LIR_Opr opr4, LIR_Opr result, BasicType type) ++ : LIR_Op(code, result, NULL) ++ , _opr1(opr1) ++ , _opr2(opr2) ++ , _opr3(opr3) ++ , _opr4(opr4) ++ , _type(type) ++ , _condition(condition) { ++ assert(is_in_range(code, begin_op4, end_op4), "code check"); ++ assert(type != T_ILLEGAL, "cmove should have type"); ++ } ++ LIR_Opr in_opr1() const { return _opr1; } ++ LIR_Opr in_opr2() const { return _opr2; } ++ LIR_Opr in_opr3() const { return _opr3; } ++ LIR_Opr in_opr4() const { return _opr4; } ++ BasicType type() const { return _type; } ++ LIR_Condition condition() const { ++ assert(code() == lir_cmp_cmove, "only valid for cmp cmove"); return _condition; ++ } ++ void set_condition(LIR_Condition condition) { ++ assert(code() == lir_cmp_cmove, "only valid for cmp cmove"); _condition = condition; ++ } ++ ++ virtual void emit_code(LIR_Assembler* masm); ++ virtual LIR_Op4* as_Op4() { return this; } ++ virtual void print_instr(outputStream* out) const PRODUCT_RETURN; ++}; ++ + //-------------------------------- + class LabelObj: public CompilationResourceObj { + private: +@@ -2141,17 +2222,9 @@ class LIR_List: public CompilationResourceObj { + + void safepoint(LIR_Opr tmp, CodeEmitInfo* info) { append(new LIR_Op1(lir_safepoint, tmp, info)); } + +-#ifdef PPC +- void convert(Bytecodes::Code code, LIR_Opr left, LIR_Opr dst, LIR_Opr tmp1, LIR_Opr tmp2) { append(new LIR_OpConvert(code, left, dst, NULL, tmp1, tmp2)); } +-#endif +-#if defined(AARCH64) +- void convert(Bytecodes::Code code, LIR_Opr left, LIR_Opr dst, +- ConversionStub* stub = NULL, LIR_Opr tmp1 = LIR_OprDesc::illegalOpr()) { +- append(new LIR_OpConvert(code, left, dst, stub, tmp1, LIR_OprDesc::illegalOpr())); ++ void convert(Bytecodes::Code code, LIR_Opr left, LIR_Opr dst, ConversionStub* stub = NULL, LIR_Opr tmp = LIR_OprFact::illegalOpr) { ++ append(new LIR_OpConvert(code, left, dst, stub, tmp)); + } +-#else +- void convert(Bytecodes::Code code, LIR_Opr left, LIR_Opr dst, ConversionStub* stub = NULL/*, bool is_32bit = false*/) { append(new LIR_OpConvert(code, left, dst, stub)); } +-#endif + + void logical_and (LIR_Opr left, LIR_Opr right, LIR_Opr dst) { append(new LIR_Op2(lir_logic_and, left, right, dst)); } + void logical_or (LIR_Opr left, LIR_Opr right, LIR_Opr dst) { append(new LIR_Op2(lir_logic_or, left, right, dst)); } +@@ -2256,6 +2329,48 @@ class LIR_List: public CompilationResourceObj { + append(new LIR_OpBranch(cond, type, block, unordered)); + } + ++#if defined(X86) || defined(AARCH64) ++ ++ template ++ void cmp_branch(LIR_Condition condition, LIR_Opr left, LIR_Opr right, BasicType type, T tgt, CodeEmitInfo* info = NULL) { ++ cmp(condition, left, right, info); ++ branch(condition, type, tgt); ++ } ++ ++ void cmp_branch(LIR_Condition condition, LIR_Opr left, LIR_Opr right, BasicType type, BlockBegin* block, BlockBegin* unordered) { ++ cmp(condition, left, right); ++ branch(condition, type, block, unordered); ++ } ++ ++ void cmp_cmove(LIR_Condition condition, LIR_Opr left, LIR_Opr right, LIR_Opr src1, LIR_Opr src2, LIR_Opr dst, BasicType type) { ++ cmp(condition, left, right); ++ cmove(condition, src1, src2, dst, type); ++ } ++ ++#endif ++ ++#ifdef LOONGARCH ++ ++ template ++ void cmp_branch(LIR_Condition condition, LIR_Opr left, LIR_Opr right, BasicType type, T tgt, CodeEmitInfo* info = NULL) { ++ append(new LIR_OpCmpBranch(condition, left, right, tgt, info)); ++ } ++ ++ void cmp_branch(LIR_Condition condition, LIR_Opr left, LIR_Opr right, BasicType type, BlockBegin* block, BlockBegin* unordered) { ++ append(new LIR_OpCmpBranch(condition, left, right, block, unordered)); ++ } ++ ++ void cmp_cmove(LIR_Condition condition, LIR_Opr left, LIR_Opr right, LIR_Opr src1, LIR_Opr src2, LIR_Opr dst, BasicType type) { ++ append(new LIR_Op4(lir_cmp_cmove, condition, left, right, src1, src2, dst, type)); ++ } ++ ++#endif ++ ++ template ++ void cmp_branch(LIR_Condition condition, LIR_Opr left, int right, BasicType type, T tgt, CodeEmitInfo* info = NULL) { ++ cmp_branch(condition, left, LIR_OprFact::intConst(right), type, tgt, info); ++ } ++ + void shift_left(LIR_Opr value, LIR_Opr count, LIR_Opr dst, LIR_Opr tmp); + void shift_right(LIR_Opr value, LIR_Opr count, LIR_Opr dst, LIR_Opr tmp); + void unsigned_shift_right(LIR_Opr value, LIR_Opr count, LIR_Opr dst, LIR_Opr tmp); +diff --git a/hotspot/src/share/vm/c1/c1_LIRAssembler.cpp b/hotspot/src/share/vm/c1/c1_LIRAssembler.cpp +index e5cd19f17a..a18c53008b 100644 +--- a/hotspot/src/share/vm/c1/c1_LIRAssembler.cpp ++++ b/hotspot/src/share/vm/c1/c1_LIRAssembler.cpp +@@ -22,6 +22,12 @@ + * + */ + ++/* ++ * This file has been modified by Loongson Technology in 2022. These ++ * modifications are Copyright (c) 2015, 2022, Loongson Technology, and are made ++ * available on the same license terms set forth above. ++ */ ++ + #include "precompiled.hpp" + #include "c1/c1_Compilation.hpp" + #include "c1/c1_Instruction.hpp" +@@ -34,6 +40,10 @@ + # include "nativeInst_x86.hpp" + # include "vmreg_x86.inline.hpp" + #endif ++#ifdef TARGET_ARCH_loongarch ++# include "nativeInst_loongarch.hpp" ++# include "vmreg_loongarch.inline.hpp" ++#endif + #ifdef TARGET_ARCH_aarch64 + # include "nativeInst_aarch64.hpp" + # include "vmreg_aarch64.inline.hpp" +@@ -811,6 +821,18 @@ void LIR_Assembler::emit_op2(LIR_Op2* op) { + } + + ++void LIR_Assembler::emit_op4(LIR_Op4* op) { ++ switch (op->code()) { ++ case lir_cmp_cmove: ++ cmp_cmove(op->condition(), op->in_opr1(), op->in_opr2(), op->in_opr3(), op->in_opr4(), op->result_opr(), op->type()); ++ break; ++ ++ default: ++ Unimplemented(); ++ break; ++ } ++} ++ + void LIR_Assembler::build_frame() { + _masm->build_frame(initial_frame_size_in_bytes(), bang_size_in_bytes()); + } +diff --git a/hotspot/src/share/vm/c1/c1_LIRAssembler.hpp b/hotspot/src/share/vm/c1/c1_LIRAssembler.hpp +index 1a68d458d2..ac0f4e7a46 100644 +--- a/hotspot/src/share/vm/c1/c1_LIRAssembler.hpp ++++ b/hotspot/src/share/vm/c1/c1_LIRAssembler.hpp +@@ -22,6 +22,12 @@ + * + */ + ++/* ++ * This file has been modified by Loongson Technology in 2022. These ++ * modifications are Copyright (c) 2015, 2022, Loongson Technology, and are made ++ * available on the same license terms set forth above. ++ */ ++ + #ifndef SHARE_VM_C1_C1_LIRASSEMBLER_HPP + #define SHARE_VM_C1_C1_LIRASSEMBLER_HPP + +@@ -195,7 +201,9 @@ class LIR_Assembler: public CompilationResourceObj { + void emit_op1(LIR_Op1* op); + void emit_op2(LIR_Op2* op); + void emit_op3(LIR_Op3* op); ++ void emit_op4(LIR_Op4* op); + void emit_opBranch(LIR_OpBranch* op); ++ void emit_opCmpBranch(LIR_OpCmpBranch* op); + void emit_opLabel(LIR_OpLabel* op); + void emit_arraycopy(LIR_OpArrayCopy* op); + void emit_updatecrc32(LIR_OpUpdateCRC32* op); +@@ -227,6 +235,7 @@ class LIR_Assembler: public CompilationResourceObj { + void volatile_move_op(LIR_Opr src, LIR_Opr result, BasicType type, CodeEmitInfo* info); + void comp_mem_op(LIR_Opr src, LIR_Opr result, BasicType type, CodeEmitInfo* info); // info set for null exceptions + void comp_fl2i(LIR_Code code, LIR_Opr left, LIR_Opr right, LIR_Opr result, LIR_Op2* op); ++ void cmp_cmove(LIR_Condition code, LIR_Opr left, LIR_Opr right, LIR_Opr src1, LIR_Opr src2, LIR_Opr result, BasicType type); + void cmove(LIR_Condition code, LIR_Opr left, LIR_Opr right, LIR_Opr result, BasicType type); + + void call( LIR_OpJavaCall* op, relocInfo::relocType rtype); +@@ -265,6 +274,9 @@ class LIR_Assembler: public CompilationResourceObj { + #ifdef TARGET_ARCH_x86 + # include "c1_LIRAssembler_x86.hpp" + #endif ++#ifdef TARGET_ARCH_loongarch ++# include "c1_LIRAssembler_loongarch.hpp" ++#endif + #ifdef TARGET_ARCH_aarch64 + # include "c1_LIRAssembler_aarch64.hpp" + #endif +diff --git a/hotspot/src/share/vm/c1/c1_LIRGenerator.cpp b/hotspot/src/share/vm/c1/c1_LIRGenerator.cpp +index 837553ddb6..c66f3102b9 100644 +--- a/hotspot/src/share/vm/c1/c1_LIRGenerator.cpp ++++ b/hotspot/src/share/vm/c1/c1_LIRGenerator.cpp +@@ -22,6 +22,12 @@ + * + */ + ++/* ++ * This file has been modified by Loongson Technology in 2022. These ++ * modifications are Copyright (c) 2015, 2022, Loongson Technology, and are made ++ * available on the same license terms set forth above. ++ */ ++ + #include "precompiled.hpp" + #include "c1/c1_Defs.hpp" + #include "c1/c1_Compilation.hpp" +@@ -482,13 +488,11 @@ void LIRGenerator::array_range_check(LIR_Opr array, LIR_Opr index, + CodeEmitInfo* null_check_info, CodeEmitInfo* range_check_info) { + CodeStub* stub = new RangeCheckStub(range_check_info, index); + if (index->is_constant()) { +- cmp_mem_int(lir_cond_belowEqual, array, arrayOopDesc::length_offset_in_bytes(), +- index->as_jint(), null_check_info); +- __ branch(lir_cond_belowEqual, T_INT, stub); // forward branch ++ cmp_mem_int_branch(lir_cond_belowEqual, array, arrayOopDesc::length_offset_in_bytes(), ++ index->as_jint(), stub, null_check_info); // forward branch + } else { +- cmp_reg_mem(lir_cond_aboveEqual, index, array, +- arrayOopDesc::length_offset_in_bytes(), T_INT, null_check_info); +- __ branch(lir_cond_aboveEqual, T_INT, stub); // forward branch ++ cmp_reg_mem_branch(lir_cond_aboveEqual, index, array, arrayOopDesc::length_offset_in_bytes(), ++ T_INT, stub, null_check_info); // forward branch + } + } + +@@ -496,12 +500,10 @@ void LIRGenerator::array_range_check(LIR_Opr array, LIR_Opr index, + void LIRGenerator::nio_range_check(LIR_Opr buffer, LIR_Opr index, LIR_Opr result, CodeEmitInfo* info) { + CodeStub* stub = new RangeCheckStub(info, index, true); + if (index->is_constant()) { +- cmp_mem_int(lir_cond_belowEqual, buffer, java_nio_Buffer::limit_offset(), index->as_jint(), info); +- __ branch(lir_cond_belowEqual, T_INT, stub); // forward branch ++ cmp_mem_int_branch(lir_cond_belowEqual, buffer, java_nio_Buffer::limit_offset(), index->as_jint(), stub, info); // forward branch + } else { +- cmp_reg_mem(lir_cond_aboveEqual, index, buffer, +- java_nio_Buffer::limit_offset(), T_INT, info); +- __ branch(lir_cond_aboveEqual, T_INT, stub); // forward branch ++ cmp_reg_mem_branch(lir_cond_aboveEqual, index, buffer, ++ java_nio_Buffer::limit_offset(), T_INT, stub, info); // forward branch + } + __ move(index, result); + } +@@ -934,7 +936,7 @@ LIR_Opr LIRGenerator::force_to_spill(LIR_Opr value, BasicType t) { + return tmp; + } + +-void LIRGenerator::profile_branch(If* if_instr, If::Condition cond) { ++void LIRGenerator::profile_branch(If* if_instr, If::Condition cond, LIR_Opr left, LIR_Opr right) { + if (if_instr->should_profile()) { + ciMethod* method = if_instr->profiled_method(); + assert(method != NULL, "method should be set if branch is profiled"); +@@ -955,10 +957,17 @@ void LIRGenerator::profile_branch(If* if_instr, If::Condition cond) { + __ metadata2reg(md->constant_encoding(), md_reg); + + LIR_Opr data_offset_reg = new_pointer_register(); +- __ cmove(lir_cond(cond), +- LIR_OprFact::intptrConst(taken_count_offset), +- LIR_OprFact::intptrConst(not_taken_count_offset), +- data_offset_reg, as_BasicType(if_instr->x()->type())); ++ if (left == LIR_OprFact::illegalOpr && right == LIR_OprFact::illegalOpr) { ++ __ cmove(lir_cond(cond), ++ LIR_OprFact::intptrConst(taken_count_offset), ++ LIR_OprFact::intptrConst(not_taken_count_offset), ++ data_offset_reg, as_BasicType(if_instr->x()->type())); ++ } else { ++ __ cmp_cmove(lir_cond(cond), left, right, ++ LIR_OprFact::intptrConst(taken_count_offset), ++ LIR_OprFact::intptrConst(not_taken_count_offset), ++ data_offset_reg, as_BasicType(if_instr->x()->type())); ++ } + + // MDO cells are intptr_t, so the data_reg width is arch-dependent. + LIR_Opr data_reg = new_pointer_register(); +@@ -1305,8 +1314,8 @@ void LIRGenerator::do_isPrimitive(Intrinsic* x) { + } + + __ move(new LIR_Address(rcvr.result(), java_lang_Class::klass_offset_in_bytes(), T_ADDRESS), temp, info); +- __ cmp(lir_cond_notEqual, temp, LIR_OprFact::metadataConst(0)); +- __ cmove(lir_cond_notEqual, LIR_OprFact::intConst(0), LIR_OprFact::intConst(1), result, T_BOOLEAN); ++ __ cmp_cmove(lir_cond_notEqual, temp, LIR_OprFact::metadataConst(0), ++ LIR_OprFact::intConst(0), LIR_OprFact::intConst(1), result, T_BOOLEAN); + } + + // Example: Thread.currentThread() +@@ -1499,7 +1508,6 @@ void LIRGenerator::G1SATBCardTableModRef_pre_barrier(LIR_Opr addr_opr, LIR_Opr p + // Read the marking-in-progress flag. + LIR_Opr flag_val = new_register(T_INT); + __ load(mark_active_flag_addr, flag_val); +- __ cmp(lir_cond_notEqual, flag_val, LIR_OprFact::intConst(0)); + + LIR_PatchCode pre_val_patch_code = lir_patch_none; + +@@ -1528,7 +1536,7 @@ void LIRGenerator::G1SATBCardTableModRef_pre_barrier(LIR_Opr addr_opr, LIR_Opr p + slow = new G1PreBarrierStub(pre_val); + } + +- __ branch(lir_cond_notEqual, T_INT, slow); ++ __ cmp_branch(lir_cond_notEqual, flag_val, LIR_OprFact::intConst(0), T_INT, slow); + __ branch_destination(slow->continuation()); + } + +@@ -1586,10 +1594,8 @@ void LIRGenerator::G1SATBCardTableModRef_post_barrier(LIR_OprDesc* addr, LIR_Opr + } + assert(new_val->is_register(), "must be a register at this point"); + +- __ cmp(lir_cond_notEqual, xor_shift_res, LIR_OprFact::intptrConst(NULL_WORD)); +- + CodeStub* slow = new G1PostBarrierStub(addr, new_val); +- __ branch(lir_cond_notEqual, LP64_ONLY(T_LONG) NOT_LP64(T_INT), slow); ++ __ cmp_branch(lir_cond_notEqual, xor_shift_res, LIR_OprFact::intptrConst(NULL_WORD), T_INT, slow); + __ branch_destination(slow->continuation()); + } + +@@ -1859,12 +1865,10 @@ void LIRGenerator::do_NIOCheckIndex(Intrinsic* x) { + CodeEmitInfo* info = state_for(x); + CodeStub* stub = new RangeCheckStub(info, index.result(), true); + if (index.result()->is_constant()) { +- cmp_mem_int(lir_cond_belowEqual, buf.result(), java_nio_Buffer::limit_offset(), index.result()->as_jint(), info); +- __ branch(lir_cond_belowEqual, T_INT, stub); ++ cmp_mem_int_branch(lir_cond_belowEqual, buf.result(), java_nio_Buffer::limit_offset(), index.result()->as_jint(), stub, info); + } else { +- cmp_reg_mem(lir_cond_aboveEqual, index.result(), buf.result(), +- java_nio_Buffer::limit_offset(), T_INT, info); +- __ branch(lir_cond_aboveEqual, T_INT, stub); ++ cmp_reg_mem_branch(lir_cond_aboveEqual, index.result(), buf.result(), ++ java_nio_Buffer::limit_offset(), T_INT, stub, info); + } + __ move(index.result(), result); + } else { +@@ -1945,8 +1949,8 @@ void LIRGenerator::do_LoadIndexed(LoadIndexed* x) { + } else if (use_length) { + // TODO: use a (modified) version of array_range_check that does not require a + // constant length to be loaded to a register +- __ cmp(lir_cond_belowEqual, length.result(), index.result()); +- __ branch(lir_cond_belowEqual, T_INT, new RangeCheckStub(range_check_info, index.result())); ++ CodeStub* stub = new RangeCheckStub(range_check_info, index.result()); ++ __ cmp_branch(lir_cond_belowEqual, length.result(), index.result(), T_INT, stub); + } else { + array_range_check(array.result(), index.result(), null_check_info, range_check_info); + // The range check performs the null check, so clear it out for the load +@@ -2128,7 +2132,7 @@ void LIRGenerator::do_UnsafeGetRaw(UnsafeGetRaw* x) { + assert(index_op->type() == T_INT, "only int constants supported"); + addr = new LIR_Address(base_op, index_op->as_jint(), dst_type); + } else { +-#if defined(X86) || defined(AARCH64) ++#if defined(X86) || defined(AARCH64) || defined(LOONGARCH) + addr = new LIR_Address(base_op, index_op, LIR_Address::Scale(log2_scale), 0, dst_type); + #elif defined(GENERATE_ADDRESS_IS_PREFERRED) + addr = generate_address(base_op, index_op, log2_scale, 0, dst_type); +@@ -2343,19 +2347,18 @@ void LIRGenerator::do_UnsafeGetObject(UnsafeGetObject* x) { + + if (off.type()->is_int()) { + referent_off = LIR_OprFact::intConst(java_lang_ref_Reference::referent_offset); ++ __ cmp_branch(lir_cond_notEqual, off.result(), referent_off, T_INT, Lcont->label()); + } else { + assert(off.type()->is_long(), "what else?"); + referent_off = new_register(T_LONG); + __ move(LIR_OprFact::longConst(java_lang_ref_Reference::referent_offset), referent_off); ++ __ cmp_branch(lir_cond_notEqual, off.result(), referent_off, T_LONG, Lcont->label()); + } +- __ cmp(lir_cond_notEqual, off.result(), referent_off); +- __ branch(lir_cond_notEqual, as_BasicType(off.type()), Lcont->label()); + } + if (gen_source_check) { + // offset is a const and equals referent offset + // if (source == null) -> continue +- __ cmp(lir_cond_equal, src_reg, LIR_OprFact::oopConst(NULL)); +- __ branch(lir_cond_equal, T_OBJECT, Lcont->label()); ++ __ cmp_branch(lir_cond_equal, src_reg, LIR_OprFact::oopConst(NULL), T_OBJECT, Lcont->label()); + } + LIR_Opr src_klass = new_register(T_METADATA); + if (gen_type_check) { +@@ -2365,8 +2368,7 @@ void LIRGenerator::do_UnsafeGetObject(UnsafeGetObject* x) { + LIR_Address* reference_type_addr = new LIR_Address(src_klass, in_bytes(InstanceKlass::reference_type_offset()), T_BYTE); + LIR_Opr reference_type = new_register(T_INT); + __ move(reference_type_addr, reference_type); +- __ cmp(lir_cond_equal, reference_type, LIR_OprFact::intConst(REF_NONE)); +- __ branch(lir_cond_equal, T_INT, Lcont->label()); ++ __ cmp_branch(lir_cond_equal, reference_type, LIR_OprFact::intConst(REF_NONE), T_INT, Lcont->label()); + } + { + // We have determined that src->_klass->_reference_type != REF_NONE +@@ -2446,19 +2448,14 @@ void LIRGenerator::do_SwitchRanges(SwitchRangeArray* x, LIR_Opr value, BlockBegi + int high_key = one_range->high_key(); + BlockBegin* dest = one_range->sux(); + if (low_key == high_key) { +- __ cmp(lir_cond_equal, value, low_key); +- __ branch(lir_cond_equal, T_INT, dest); ++ __ cmp_branch(lir_cond_equal, value, low_key, T_INT, dest); + } else if (high_key - low_key == 1) { +- __ cmp(lir_cond_equal, value, low_key); +- __ branch(lir_cond_equal, T_INT, dest); +- __ cmp(lir_cond_equal, value, high_key); +- __ branch(lir_cond_equal, T_INT, dest); ++ __ cmp_branch(lir_cond_equal, value, low_key, T_INT, dest); ++ __ cmp_branch(lir_cond_equal, value, high_key, T_INT, dest); + } else { + LabelObj* L = new LabelObj(); +- __ cmp(lir_cond_less, value, low_key); +- __ branch(lir_cond_less, T_INT, L->label()); +- __ cmp(lir_cond_lessEqual, value, high_key); +- __ branch(lir_cond_lessEqual, T_INT, dest); ++ __ cmp_branch(lir_cond_less, value, low_key, T_INT, L->label()); ++ __ cmp_branch(lir_cond_lessEqual, value, high_key, T_INT, dest); + __ branch_destination(L->label()); + } + } +@@ -2545,8 +2542,7 @@ void LIRGenerator::do_TableSwitch(TableSwitch* x) { + do_SwitchRanges(create_lookup_ranges(x), value, x->default_sux()); + } else { + for (int i = 0; i < len; i++) { +- __ cmp(lir_cond_equal, value, i + lo_key); +- __ branch(lir_cond_equal, T_INT, x->sux_at(i)); ++ __ cmp_branch(lir_cond_equal, value, i + lo_key, T_INT, x->sux_at(i)); + } + __ jump(x->default_sux()); + } +@@ -2571,8 +2567,7 @@ void LIRGenerator::do_LookupSwitch(LookupSwitch* x) { + } else { + int len = x->length(); + for (int i = 0; i < len; i++) { +- __ cmp(lir_cond_equal, value, x->key_at(i)); +- __ branch(lir_cond_equal, T_INT, x->sux_at(i)); ++ __ cmp_branch(lir_cond_equal, value, x->key_at(i), T_INT, x->sux_at(i)); + } + __ jump(x->default_sux()); + } +@@ -2624,7 +2619,6 @@ void LIRGenerator::do_Goto(Goto* x) { + } + LIR_Opr md_reg = new_register(T_METADATA); + __ metadata2reg(md->constant_encoding(), md_reg); +- + increment_counter(new LIR_Address(md_reg, offset, + NOT_LP64(T_INT) LP64_ONLY(T_LONG)), DataLayout::counter_increment); + } +@@ -3078,8 +3072,8 @@ void LIRGenerator::do_IfOp(IfOp* x) { + f_val.dont_load_item(); + LIR_Opr reg = rlock_result(x); + +- __ cmp(lir_cond(x->cond()), left.result(), right.result()); +- __ cmove(lir_cond(x->cond()), t_val.result(), f_val.result(), reg, as_BasicType(x->x()->type())); ++ __ cmp_cmove(lir_cond(x->cond()), left.result(), right.result(), ++ t_val.result(), f_val.result(), reg, as_BasicType(x->x()->type())); + } + + #ifdef JFR_HAVE_INTRINSICS +@@ -3119,8 +3113,7 @@ void LIRGenerator::do_getEventWriter(Intrinsic* x) { + T_OBJECT); + LIR_Opr result = rlock_result(x); + __ move_wide(jobj_addr, result); +- __ cmp(lir_cond_equal, result, LIR_OprFact::oopConst(NULL)); +- __ branch(lir_cond_equal, T_OBJECT, L_end->label()); ++ __ cmp_branch(lir_cond_equal, result, LIR_OprFact::oopConst(0), T_OBJECT, L_end->label()); + __ move_wide(new LIR_Address(result, T_OBJECT), result); + + __ branch_destination(L_end->label()); +@@ -3484,10 +3477,9 @@ void LIRGenerator::increment_event_counter_impl(CodeEmitInfo* info, + LIR_Opr meth = new_register(T_METADATA); + __ metadata2reg(method->constant_encoding(), meth); + __ logical_and(result, mask, result); +- __ cmp(lir_cond_equal, result, LIR_OprFact::intConst(0)); + // The bci for info can point to cmp for if's we want the if bci + CodeStub* overflow = new CounterOverflowStub(info, bci, meth); +- __ branch(lir_cond_equal, T_INT, overflow); ++ __ cmp_branch(lir_cond_equal, result, LIR_OprFact::intConst(0), T_INT, overflow); + __ branch_destination(overflow->continuation()); + } + } +@@ -3599,8 +3591,7 @@ void LIRGenerator::do_RangeCheckPredicate(RangeCheckPredicate *x) { + CodeEmitInfo *info = state_for(x, x->state()); + CodeStub* stub = new PredicateFailedStub(info); + +- __ cmp(lir_cond(cond), left, right); +- __ branch(lir_cond(cond), right->type(), stub); ++ __ cmp_branch(lir_cond(cond), left, right, right->type(), stub); + } + } + +@@ -3748,8 +3739,7 @@ LIR_Opr LIRGenerator::maybe_mask_boolean(StoreIndexed* x, LIR_Opr array, LIR_Opr + __ move(new LIR_Address(klass, in_bytes(Klass::layout_helper_offset()), T_INT), layout); + int diffbit = Klass::layout_helper_boolean_diffbit(); + __ logical_and(layout, LIR_OprFact::intConst(diffbit), layout); +- __ cmp(lir_cond_notEqual, layout, LIR_OprFact::intConst(0)); +- __ cmove(lir_cond_notEqual, value_fixed, value, value_fixed, T_BYTE); ++ __ cmp_cmove(lir_cond_notEqual, layout, LIR_OprFact::intConst(0), value_fixed, value, value_fixed, T_BYTE); + value = value_fixed; + } + return value; +diff --git a/hotspot/src/share/vm/c1/c1_LIRGenerator.hpp b/hotspot/src/share/vm/c1/c1_LIRGenerator.hpp +index 27be79fee1..57c253db69 100644 +--- a/hotspot/src/share/vm/c1/c1_LIRGenerator.hpp ++++ b/hotspot/src/share/vm/c1/c1_LIRGenerator.hpp +@@ -22,6 +22,12 @@ + * + */ + ++/* ++ * This file has been modified by Loongson Technology in 2015. These ++ * modifications are Copyright (c) 2015 Loongson Technology, and are made ++ * available on the same license terms set forth above. ++ */ ++ + #ifndef SHARE_VM_C1_C1_LIRGENERATOR_HPP + #define SHARE_VM_C1_C1_LIRGENERATOR_HPP + +@@ -246,6 +252,9 @@ class LIRGenerator: public InstructionVisitor, public BlockClosure { + void do_getClass(Intrinsic* x); + void do_currentThread(Intrinsic* x); + void do_MathIntrinsic(Intrinsic* x); ++#if defined(LOONGARCH64) ++ void do_LibmIntrinsic(Intrinsic* x); ++#endif + void do_ArrayCopy(Intrinsic* x); + void do_CompareAndSwap(Intrinsic* x, ValueType* type); + void do_NIOCheckIndex(Intrinsic* x); +@@ -335,8 +344,10 @@ class LIRGenerator: public InstructionVisitor, public BlockClosure { + void new_instance (LIR_Opr dst, ciInstanceKlass* klass, bool is_unresolved, LIR_Opr scratch1, LIR_Opr scratch2, LIR_Opr scratch3, LIR_Opr scratch4, LIR_Opr klass_reg, CodeEmitInfo* info); + + // machine dependent +- void cmp_mem_int(LIR_Condition condition, LIR_Opr base, int disp, int c, CodeEmitInfo* info); +- void cmp_reg_mem(LIR_Condition condition, LIR_Opr reg, LIR_Opr base, int disp, BasicType type, CodeEmitInfo* info); ++ template ++ void cmp_mem_int_branch(LIR_Condition condition, LIR_Opr base, int disp, int c, T tgt, CodeEmitInfo* info); ++ template ++ void cmp_reg_mem_branch(LIR_Condition condition, LIR_Opr reg, LIR_Opr base, int disp, BasicType type, T tgt, CodeEmitInfo* info); + void cmp_reg_mem(LIR_Condition condition, LIR_Opr reg, LIR_Opr base, LIR_Opr disp, BasicType type, CodeEmitInfo* info); + + void arraycopy_helper(Intrinsic* x, int* flags, ciArrayKlass** expected_type); +@@ -364,7 +375,7 @@ class LIRGenerator: public InstructionVisitor, public BlockClosure { + + LIR_Opr safepoint_poll_register(); + +- void profile_branch(If* if_instr, If::Condition cond); ++ void profile_branch(If* if_instr, If::Condition cond, LIR_Opr left = LIR_OprFact::illegalOpr, LIR_Opr right = LIR_OprFact::illegalOpr); + void increment_event_counter_impl(CodeEmitInfo* info, + ciMethod *method, int frequency, + int bci, bool backedge, bool notify); +diff --git a/hotspot/src/share/vm/c1/c1_LinearScan.cpp b/hotspot/src/share/vm/c1/c1_LinearScan.cpp +index 1f6281bf25..4549ff0928 100644 +--- a/hotspot/src/share/vm/c1/c1_LinearScan.cpp ++++ b/hotspot/src/share/vm/c1/c1_LinearScan.cpp +@@ -22,6 +22,12 @@ + * + */ + ++/* ++ * This file has been modified by Loongson Technology in 2022. These ++ * modifications are Copyright (c) 2015, 2022, Loongson Technology, and are made ++ * available on the same license terms set forth above. ++ */ ++ + #include "precompiled.hpp" + #include "c1/c1_CFGPrinter.hpp" + #include "c1/c1_CodeStubs.hpp" +@@ -35,6 +41,9 @@ + #ifdef TARGET_ARCH_x86 + # include "vmreg_x86.inline.hpp" + #endif ++#ifdef TARGET_ARCH_loongarch ++# include "vmreg_loongarch.inline.hpp" ++#endif + #ifdef TARGET_ARCH_aarch64 + # include "vmreg_aarch64.inline.hpp" + #endif +@@ -1256,6 +1265,23 @@ void LinearScan::add_register_hints(LIR_Op* op) { + LIR_Opr move_from = cmove->in_opr1(); + LIR_Opr move_to = cmove->result_opr(); + ++ if (move_to->is_register() && move_from->is_register()) { ++ Interval* from = interval_at(reg_num(move_from)); ++ Interval* to = interval_at(reg_num(move_to)); ++ if (from != NULL && to != NULL) { ++ to->set_register_hint(from); ++ TRACE_LINEAR_SCAN(4, tty->print_cr("operation at op_id %d: added hint from interval %d to %d", cmove->id(), from->reg_num(), to->reg_num())); ++ } ++ } ++ break; ++ } ++ case lir_cmp_cmove: { ++ assert(op->as_Op4() != NULL, "lir_cmp_cmove must be LIR_Op4"); ++ LIR_Op4* cmove = (LIR_Op4*)op; ++ ++ LIR_Opr move_from = cmove->in_opr3(); ++ LIR_Opr move_to = cmove->result_opr(); ++ + if (move_to->is_register() && move_from->is_register()) { + Interval* from = interval_at(reg_num(move_from)); + Interval* to = interval_at(reg_num(move_to)); +@@ -2104,7 +2130,7 @@ LIR_Opr LinearScan::calc_operand_for_interval(const Interval* interval) { + #ifdef _LP64 + return LIR_OprFact::double_cpu(assigned_reg, assigned_reg); + #else +-#if defined(SPARC) || defined(PPC) ++#if defined(SPARC) || defined(PPC) || defined(LOONGARCH) + return LIR_OprFact::double_cpu(assigned_regHi, assigned_reg); + #else + return LIR_OprFact::double_cpu(assigned_reg, assigned_regHi); +@@ -3285,7 +3311,9 @@ void LinearScan::verify_no_oops_in_fixed_intervals() { + check_live = (move->patch_code() == lir_patch_none); + } + LIR_OpBranch* branch = op->as_OpBranch(); +- if (branch != NULL && branch->stub() != NULL && branch->stub()->is_exception_throw_stub()) { ++ LIR_OpCmpBranch* cmp_branch = op->as_OpCmpBranch(); ++ if ((branch != NULL && branch->stub() != NULL && branch->stub()->is_exception_throw_stub()) || ++ (cmp_branch != NULL && cmp_branch->stub() != NULL && cmp_branch->stub()->is_exception_throw_stub())) { + // Don't bother checking the stub in this case since the + // exception stub will never return to normal control flow. + check_live = false; +@@ -6142,6 +6170,16 @@ void ControlFlowOptimizer::substitute_branch_target(BlockBegin* block, BlockBegi + assert(op->as_OpBranch() != NULL, "branch must be of type LIR_OpBranch"); + LIR_OpBranch* branch = (LIR_OpBranch*)op; + ++ if (branch->block() == target_from) { ++ branch->change_block(target_to); ++ } ++ if (branch->ublock() == target_from) { ++ branch->change_ublock(target_to); ++ } ++ } else if (op->code() == lir_cmp_branch || op->code() == lir_cmp_float_branch) { ++ assert(op->as_OpCmpBranch() != NULL, "branch must be of type LIR_OpCmpBranch"); ++ LIR_OpCmpBranch* branch = (LIR_OpCmpBranch*)op; ++ + if (branch->block() == target_from) { + branch->change_block(target_to); + } +@@ -6252,6 +6290,20 @@ void ControlFlowOptimizer::delete_unnecessary_jumps(BlockList* code) { + instructions->truncate(instructions->length() - 1); + } + } ++ } else if (prev_op->code() == lir_cmp_branch || prev_op->code() == lir_cmp_float_branch) { ++ assert(prev_op->as_OpCmpBranch() != NULL, "branch must be of type LIR_OpCmpBranch"); ++ LIR_OpCmpBranch* prev_branch = (LIR_OpCmpBranch*)prev_op; ++ ++ if (prev_branch->stub() == NULL) { ++ if (prev_branch->block() == code->at(i + 1) && prev_branch->info() == NULL) { ++ TRACE_LINEAR_SCAN(3, tty->print_cr("Negating conditional branch and deleting unconditional branch at end of block B%d", block->block_id())); ++ ++ // eliminate a conditional branch to the immediate successor ++ prev_branch->change_block(last_branch->block()); ++ prev_branch->negate_cond(); ++ instructions->trunc_to(instructions->length() - 1); ++ } ++ } + } + } + } +@@ -6328,6 +6380,13 @@ void ControlFlowOptimizer::verify(BlockList* code) { + assert(op_branch->block() == NULL || code->index_of(op_branch->block()) != -1, "branch target not valid"); + assert(op_branch->ublock() == NULL || code->index_of(op_branch->ublock()) != -1, "branch target not valid"); + } ++ ++ LIR_OpCmpBranch* op_cmp_branch = instructions->at(j)->as_OpCmpBranch(); ++ ++ if (op_cmp_branch != NULL) { ++ assert(op_cmp_branch->block() == NULL || code->find(op_cmp_branch->block()) != -1, "branch target not valid"); ++ assert(op_cmp_branch->ublock() == NULL || code->find(op_cmp_branch->ublock()) != -1, "branch target not valid"); ++ } + } + + for (j = 0; j < block->number_of_sux() - 1; j++) { +@@ -6571,6 +6630,24 @@ void LinearScanStatistic::collect(LinearScan* allocator) { + break; + } + ++ case lir_cmp_branch: ++ case lir_cmp_float_branch: { ++ LIR_OpCmpBranch* branch = op->as_OpCmpBranch(); ++ if (branch->block() == NULL) { ++ inc_counter(counter_stub_branch); ++ } else { ++ inc_counter(counter_cond_branch); ++ } ++ inc_counter(counter_cmp); ++ break; ++ } ++ ++ case lir_cmp_cmove: { ++ inc_counter(counter_misc_inst); ++ inc_counter(counter_cmp); ++ break; ++ } ++ + case lir_neg: + case lir_add: + case lir_sub: +diff --git a/hotspot/src/share/vm/c1/c1_LinearScan.hpp b/hotspot/src/share/vm/c1/c1_LinearScan.hpp +index 96e6b3babf..576a07d73d 100644 +--- a/hotspot/src/share/vm/c1/c1_LinearScan.hpp ++++ b/hotspot/src/share/vm/c1/c1_LinearScan.hpp +@@ -22,6 +22,12 @@ + * + */ + ++/* ++ * This file has been modified by Loongson Technology in 2022. These ++ * modifications are Copyright (c) 2015, 2022, Loongson Technology, and are made ++ * available on the same license terms set forth above. ++ */ ++ + #ifndef SHARE_VM_C1_C1_LINEARSCAN_HPP + #define SHARE_VM_C1_C1_LINEARSCAN_HPP + +@@ -976,6 +982,9 @@ class LinearScanTimers : public StackObj { + #ifdef TARGET_ARCH_x86 + # include "c1_LinearScan_x86.hpp" + #endif ++#ifdef TARGET_ARCH_loongarch ++# include "c1_LinearScan_loongarch.hpp" ++#endif + #ifdef TARGET_ARCH_aarch64 + # include "c1_LinearScan_aarch64.hpp" + #endif +diff --git a/hotspot/src/share/vm/c1/c1_MacroAssembler.hpp b/hotspot/src/share/vm/c1/c1_MacroAssembler.hpp +index 7e22bbaa27..12aca7bf50 100644 +--- a/hotspot/src/share/vm/c1/c1_MacroAssembler.hpp ++++ b/hotspot/src/share/vm/c1/c1_MacroAssembler.hpp +@@ -22,6 +22,12 @@ + * + */ + ++/* ++ * This file has been modified by Loongson Technology in 2022. These ++ * modifications are Copyright (c) 2015, 2022, Loongson Technology, and are made ++ * available on the same license terms set forth above. ++ */ ++ + #ifndef SHARE_VM_C1_C1_MACROASSEMBLER_HPP + #define SHARE_VM_C1_C1_MACROASSEMBLER_HPP + +@@ -50,6 +56,9 @@ class C1_MacroAssembler: public MacroAssembler { + #ifdef TARGET_ARCH_x86 + # include "c1_MacroAssembler_x86.hpp" + #endif ++#ifdef TARGET_ARCH_loongarch ++# include "c1_MacroAssembler_loongarch.hpp" ++#endif + #ifdef TARGET_ARCH_aarch64 + # include "c1_MacroAssembler_aarch64.hpp" + #endif +diff --git a/hotspot/src/share/vm/c1/c1_Runtime1.cpp b/hotspot/src/share/vm/c1/c1_Runtime1.cpp +index aebc377527..f1253506f6 100644 +--- a/hotspot/src/share/vm/c1/c1_Runtime1.cpp ++++ b/hotspot/src/share/vm/c1/c1_Runtime1.cpp +@@ -22,6 +22,12 @@ + * + */ + ++/* ++ * This file has been modified by Loongson Technology in 2022. These ++ * modifications are Copyright (c) 2015, 2022, Loongson Technology, and are made ++ * available on the same license terms set forth above. ++ */ ++ + #include "precompiled.hpp" + #include "asm/codeBuffer.hpp" + #include "c1/c1_CodeStubs.hpp" +@@ -710,6 +716,7 @@ JRT_ENTRY(void, Runtime1::deoptimize(JavaThread* thread)) + // Return to the now deoptimized frame. + JRT_END + ++#ifndef LOONGARCH + + static Klass* resolve_field_return_klass(methodHandle caller, int bci, TRAPS) { + Bytecode_field field_access(caller, bci); +@@ -1186,6 +1193,47 @@ JRT_ENTRY(void, Runtime1::patch_code(JavaThread* thread, Runtime1::StubID stub_i + } + JRT_END + ++#else ++ ++JRT_ENTRY(void, Runtime1::patch_code(JavaThread* thread, Runtime1::StubID stub_id )) ++{ ++ RegisterMap reg_map(thread, false); ++ ++ NOT_PRODUCT(_patch_code_slowcase_cnt++;) ++ // According to the LoongArch, "Concurrent modification and ++ // execution of instructions can lead to the resulting instruction ++ // performing any behavior that can be achieved by executing any ++ // sequence of instructions that can be executed from the same ++ // Exception level, except where the instruction before ++ // modification and the instruction after modification is a B, BL, ++ // NOP, BRK instruction." ++ // ++ // This effectively makes the games we play when patching ++ // impossible, so when we come across an access that needs ++ // patching we must deoptimize. ++ ++ if (TracePatching) { ++ tty->print_cr("Deoptimizing because patch is needed"); ++ } ++ ++ frame runtime_frame = thread->last_frame(); ++ frame caller_frame = runtime_frame.sender(®_map); ++ ++ // It's possible the nmethod was invalidated in the last ++ // safepoint, but if it's still alive then make it not_entrant. ++ nmethod* nm = CodeCache::find_nmethod(caller_frame.pc()); ++ if (nm != NULL) { ++ nm->make_not_entrant(); ++ } ++ ++ Deoptimization::deoptimize_frame(thread, caller_frame.id()); ++ ++ // Return to the now deoptimized frame. ++} ++JRT_END ++ ++#endif ++ + // + // Entry point for compiled code. We want to patch a nmethod. + // We don't do a normal VM transition here because we want to +diff --git a/hotspot/src/share/vm/c1/c1_globals.hpp b/hotspot/src/share/vm/c1/c1_globals.hpp +index 8f7f9f61c9..0e2d926bdf 100644 +--- a/hotspot/src/share/vm/c1/c1_globals.hpp ++++ b/hotspot/src/share/vm/c1/c1_globals.hpp +@@ -22,6 +22,12 @@ + * + */ + ++/* ++ * This file has been modified by Loongson Technology in 2022. These ++ * modifications are Copyright (c) 2015, 2022, Loongson Technology, and are made ++ * available on the same license terms set forth above. ++ */ ++ + #ifndef SHARE_VM_C1_C1_GLOBALS_HPP + #define SHARE_VM_C1_C1_GLOBALS_HPP + +@@ -29,6 +35,9 @@ + #ifdef TARGET_ARCH_x86 + # include "c1_globals_x86.hpp" + #endif ++#ifdef TARGET_ARCH_loongarch ++# include "c1_globals_loongarch.hpp" ++#endif + #ifdef TARGET_ARCH_aarch64 + # include "c1_globals_aarch64.hpp" + #endif +diff --git a/hotspot/src/share/vm/classfile/bytecodeAssembler.cpp b/hotspot/src/share/vm/classfile/bytecodeAssembler.cpp +index f067419ffc..5aa19dc84f 100644 +--- a/hotspot/src/share/vm/classfile/bytecodeAssembler.cpp ++++ b/hotspot/src/share/vm/classfile/bytecodeAssembler.cpp +@@ -22,6 +22,12 @@ + * + */ + ++/* ++ * This file has been modified by Loongson Technology in 2020. These ++ * modifications are Copyright (c) 2015, 2020, Loongson Technology, and are made ++ * available on the same license terms set forth above. ++ */ ++ + #include "precompiled.hpp" + + #include "classfile/bytecodeAssembler.hpp" +@@ -32,6 +38,12 @@ + #ifdef TARGET_ARCH_x86 + # include "bytes_x86.hpp" + #endif ++#ifdef TARGET_ARCH_mips ++# include "bytes_mips.hpp" ++#endif ++#ifdef TARGET_ARCH_loongarch ++# include "bytes_loongarch.hpp" ++#endif + #ifdef TARGET_ARCH_sparc + # include "bytes_sparc.hpp" + #endif +diff --git a/hotspot/src/share/vm/classfile/classFileStream.hpp b/hotspot/src/share/vm/classfile/classFileStream.hpp +index 9632c8c8c2..fad25c44fc 100644 +--- a/hotspot/src/share/vm/classfile/classFileStream.hpp ++++ b/hotspot/src/share/vm/classfile/classFileStream.hpp +@@ -22,6 +22,12 @@ + * + */ + ++/* ++ * This file has been modified by Loongson Technology in 2020. These ++ * modifications are Copyright (c) 2015, 2020, Loongson Technology, and are made ++ * available on the same license terms set forth above. ++ */ ++ + #ifndef SHARE_VM_CLASSFILE_CLASSFILESTREAM_HPP + #define SHARE_VM_CLASSFILE_CLASSFILESTREAM_HPP + +@@ -29,6 +35,12 @@ + #ifdef TARGET_ARCH_x86 + # include "bytes_x86.hpp" + #endif ++#ifdef TARGET_ARCH_mips ++# include "bytes_mips.hpp" ++#endif ++#ifdef TARGET_ARCH_loongarch ++# include "bytes_loongarch.hpp" ++#endif + #ifdef TARGET_ARCH_aarch64 + # include "bytes_aarch64.hpp" + #endif +diff --git a/hotspot/src/share/vm/classfile/stackMapTable.hpp b/hotspot/src/share/vm/classfile/stackMapTable.hpp +index a36a7ba3cf..d7c1f08644 100644 +--- a/hotspot/src/share/vm/classfile/stackMapTable.hpp ++++ b/hotspot/src/share/vm/classfile/stackMapTable.hpp +@@ -22,6 +22,12 @@ + * + */ + ++/* ++ * This file has been modified by Loongson Technology in 2020. These ++ * modifications are Copyright (c) 2015, 2020, Loongson Technology, and are made ++ * available on the same license terms set forth above. ++ */ ++ + #ifndef SHARE_VM_CLASSFILE_STACKMAPTABLE_HPP + #define SHARE_VM_CLASSFILE_STACKMAPTABLE_HPP + +@@ -34,6 +40,12 @@ + #ifdef TARGET_ARCH_x86 + # include "bytes_x86.hpp" + #endif ++#ifdef TARGET_ARCH_mips ++# include "bytes_mips.hpp" ++#endif ++#ifdef TARGET_ARCH_loongarch ++# include "bytes_loongarch.hpp" ++#endif + #ifdef TARGET_ARCH_aarch64 + # include "bytes_aarch64.hpp" + #endif +diff --git a/hotspot/src/share/vm/classfile/verifier.cpp b/hotspot/src/share/vm/classfile/verifier.cpp +index c653e2b5a9..1a6b7e8b1a 100644 +--- a/hotspot/src/share/vm/classfile/verifier.cpp ++++ b/hotspot/src/share/vm/classfile/verifier.cpp +@@ -22,6 +22,12 @@ + * + */ + ++/* ++ * This file has been modified by Loongson Technology in 2020. These ++ * modifications are Copyright (c) 2015, 2020, Loongson Technology, and are made ++ * available on the same license terms set forth above. ++ */ ++ + #include "precompiled.hpp" + #include "classfile/classFileStream.hpp" + #include "classfile/javaClasses.hpp" +@@ -48,6 +54,12 @@ + #ifdef TARGET_ARCH_x86 + # include "bytes_x86.hpp" + #endif ++#ifdef TARGET_ARCH_mips ++# include "bytes_mips.hpp" ++#endif ++#ifdef TARGET_ARCH_loongarch ++# include "bytes_loongarch.hpp" ++#endif + #ifdef TARGET_ARCH_aarch64 + # include "bytes_aarch64.hpp" + #endif +diff --git a/hotspot/src/share/vm/code/codeBlob.cpp b/hotspot/src/share/vm/code/codeBlob.cpp +index aff2aaf0ca..9ba76007cd 100644 +--- a/hotspot/src/share/vm/code/codeBlob.cpp ++++ b/hotspot/src/share/vm/code/codeBlob.cpp +@@ -22,6 +22,12 @@ + * + */ + ++/* ++ * This file has been modified by Loongson Technology in 2020. These ++ * modifications are Copyright (c) 2015, 2020, Loongson Technology, and are made ++ * available on the same license terms set forth above. ++ */ ++ + #include "precompiled.hpp" + #include "code/codeBlob.hpp" + #include "code/codeCache.hpp" +@@ -57,6 +63,12 @@ + #ifdef TARGET_ARCH_ppc + # include "nativeInst_ppc.hpp" + #endif ++#ifdef TARGET_ARCH_mips ++# include "nativeInst_mips.hpp" ++#endif ++#ifdef TARGET_ARCH_loongarch ++# include "nativeInst_loongarch.hpp" ++#endif + #ifdef COMPILER1 + #include "c1/c1_Runtime1.hpp" + #endif +diff --git a/hotspot/src/share/vm/code/compiledIC.hpp b/hotspot/src/share/vm/code/compiledIC.hpp +index f910f11886..e282a3f3af 100644 +--- a/hotspot/src/share/vm/code/compiledIC.hpp ++++ b/hotspot/src/share/vm/code/compiledIC.hpp +@@ -22,6 +22,12 @@ + * + */ + ++/* ++ * This file has been modified by Loongson Technology in 2020. These ++ * modifications are Copyright (c) 2015, 2020, Loongson Technology, and are made ++ * available on the same license terms set forth above. ++ */ ++ + #ifndef SHARE_VM_CODE_COMPILEDIC_HPP + #define SHARE_VM_CODE_COMPILEDIC_HPP + +@@ -45,6 +51,12 @@ + #ifdef TARGET_ARCH_ppc + # include "nativeInst_ppc.hpp" + #endif ++#ifdef TARGET_ARCH_mips ++# include "nativeInst_mips.hpp" ++#endif ++#ifdef TARGET_ARCH_loongarch ++# include "nativeInst_loongarch.hpp" ++#endif + + //----------------------------------------------------------------------------- + // The CompiledIC represents a compiled inline cache. +diff --git a/hotspot/src/share/vm/code/relocInfo.hpp b/hotspot/src/share/vm/code/relocInfo.hpp +index ad55a2fd93..813504821d 100644 +--- a/hotspot/src/share/vm/code/relocInfo.hpp ++++ b/hotspot/src/share/vm/code/relocInfo.hpp +@@ -22,6 +22,12 @@ + * + */ + ++/* ++ * This file has been modified by Loongson Technology in 2021. These ++ * modifications are Copyright (c) 2015, 2021, Loongson Technology, and are made ++ * available on the same license terms set forth above. ++ */ ++ + #ifndef SHARE_VM_CODE_RELOCINFO_HPP + #define SHARE_VM_CODE_RELOCINFO_HPP + +@@ -261,7 +267,11 @@ class relocInfo VALUE_OBJ_CLASS_SPEC { + poll_return_type = 11, // polling instruction for safepoints at return + metadata_type = 12, // metadata that used to be oops + trampoline_stub_type = 13, // stub-entry for trampoline ++#if !defined MIPS64 + yet_unused_type_1 = 14, // Still unused ++#else ++ internal_pc_type = 14, // tag for internal data,?? ++#endif + data_prefix_tag = 15, // tag for a prefix (carries data arguments) + type_mask = 15 // A mask which selects only the above values + }; +@@ -288,6 +298,7 @@ class relocInfo VALUE_OBJ_CLASS_SPEC { + ; + #endif + ++#if defined MIPS64 && !defined ZERO + #define APPLY_TO_RELOCATIONS(visitor) \ + visitor(oop) \ + visitor(metadata) \ +@@ -300,9 +311,26 @@ class relocInfo VALUE_OBJ_CLASS_SPEC { + visitor(internal_word) \ + visitor(poll) \ + visitor(poll_return) \ +- visitor(section_word) \ + visitor(trampoline_stub) \ ++ visitor(internal_pc) \ + ++#else ++ #define APPLY_TO_RELOCATIONS(visitor) \ ++ visitor(oop) \ ++ visitor(metadata) \ ++ visitor(virtual_call) \ ++ visitor(opt_virtual_call) \ ++ visitor(static_call) \ ++ visitor(static_stub) \ ++ visitor(runtime_call) \ ++ visitor(external_word) \ ++ visitor(internal_word) \ ++ visitor(poll) \ ++ visitor(poll_return) \ ++ visitor(trampoline_stub) \ ++ visitor(section_word) \ ++ ++#endif + + public: + enum { +@@ -432,6 +460,12 @@ class relocInfo VALUE_OBJ_CLASS_SPEC { + #endif + #ifdef TARGET_ARCH_ppc + # include "relocInfo_ppc.hpp" ++#endif ++#ifdef TARGET_ARCH_mips ++# include "relocInfo_mips.hpp" ++#endif ++#ifdef TARGET_ARCH_loongarch ++# include "relocInfo_loongarch.hpp" + #endif + + +@@ -1024,6 +1058,15 @@ class metadata_Relocation : public DataRelocation { + // Note: metadata_value transparently converts Universe::non_metadata_word to NULL. + }; + ++#if defined MIPS64 ++// to handle the set_last_java_frame pc ++class internal_pc_Relocation : public Relocation { ++ relocInfo::relocType type() { return relocInfo::internal_pc_type; } ++ public: ++ address pc() { return pd_get_address_from_code(); } ++ void fix_relocation_after_move(const CodeBuffer* src, CodeBuffer* dest); ++}; ++#endif + + class virtual_call_Relocation : public CallRelocation { + relocInfo::relocType type() { return relocInfo::virtual_call_type; } +diff --git a/hotspot/src/share/vm/code/vmreg.hpp b/hotspot/src/share/vm/code/vmreg.hpp +index 07b595b60a..5bc7131a8a 100644 +--- a/hotspot/src/share/vm/code/vmreg.hpp ++++ b/hotspot/src/share/vm/code/vmreg.hpp +@@ -22,6 +22,12 @@ + * + */ + ++/* ++ * This file has been modified by Loongson Technology in 2020. These ++ * modifications are Copyright (c) 2015, 2020, Loongson Technology, and are made ++ * available on the same license terms set forth above. ++ */ ++ + #ifndef SHARE_VM_CODE_VMREG_HPP + #define SHARE_VM_CODE_VMREG_HPP + +@@ -47,6 +53,12 @@ + #elif defined TARGET_ARCH_MODEL_ppc_64 + # include "adfiles/adGlobals_ppc_64.hpp" + #endif ++#ifdef TARGET_ARCH_MODEL_mips_64 ++# include "adfiles/adGlobals_mips_64.hpp" ++#endif ++#ifdef TARGET_ARCH_MODEL_loongarch_64 ++# include "adfiles/adGlobals_loongarch_64.hpp" ++#endif + #endif + + //------------------------------VMReg------------------------------------------ +@@ -158,6 +170,12 @@ public: + #ifdef TARGET_ARCH_x86 + # include "vmreg_x86.hpp" + #endif ++#ifdef TARGET_ARCH_mips ++# include "vmreg_mips.hpp" ++#endif ++#ifdef TARGET_ARCH_loongarch ++# include "vmreg_loongarch.hpp" ++#endif + #ifdef TARGET_ARCH_aarch64 + # include "vmreg_aarch64.hpp" + #endif +diff --git a/hotspot/src/share/vm/compiler/disassembler.cpp b/hotspot/src/share/vm/compiler/disassembler.cpp +index dfdd5f77e7..2dd0ff69ac 100644 +--- a/hotspot/src/share/vm/compiler/disassembler.cpp ++++ b/hotspot/src/share/vm/compiler/disassembler.cpp +@@ -22,6 +22,12 @@ + * + */ + ++/* ++ * This file has been modified by Loongson Technology in 2020. These ++ * modifications are Copyright (c) 2015, 2020, Loongson Technology, and are made ++ * available on the same license terms set forth above. ++ */ ++ + #include "precompiled.hpp" + #include "classfile/javaClasses.hpp" + #include "code/codeCache.hpp" +@@ -50,6 +56,12 @@ + #ifdef TARGET_ARCH_ppc + # include "depChecker_ppc.hpp" + #endif ++#ifdef TARGET_ARCH_mips ++# include "depChecker_mips.hpp" ++#endif ++#ifdef TARGET_ARCH_loongarch ++# include "depChecker_loongarch.hpp" ++#endif + #ifdef SHARK + #include "shark/sharkEntry.hpp" + #endif +diff --git a/hotspot/src/share/vm/compiler/disassembler.hpp b/hotspot/src/share/vm/compiler/disassembler.hpp +index 168851cc26..8b632748f2 100644 +--- a/hotspot/src/share/vm/compiler/disassembler.hpp ++++ b/hotspot/src/share/vm/compiler/disassembler.hpp +@@ -22,6 +22,12 @@ + * + */ + ++/* ++ * This file has been modified by Loongson Technology in 2020. These ++ * modifications are Copyright (c) 2015, 2020, Loongson Technology, and are made ++ * available on the same license terms set forth above. ++ */ ++ + #ifndef SHARE_VM_COMPILER_DISASSEMBLER_HPP + #define SHARE_VM_COMPILER_DISASSEMBLER_HPP + +@@ -95,6 +101,12 @@ class Disassembler { + #endif + #ifdef TARGET_ARCH_ppc + # include "disassembler_ppc.hpp" ++#endif ++#ifdef TARGET_ARCH_mips ++# include "disassembler_mips.hpp" ++#endif ++#ifdef TARGET_ARCH_loongarch ++# include "disassembler_loongarch.hpp" + #endif + + +diff --git a/hotspot/src/share/vm/gc_implementation/parallelScavenge/cardTableExtension.hpp b/hotspot/src/share/vm/gc_implementation/parallelScavenge/cardTableExtension.hpp +index 733b5c91ad..678a1ee836 100644 +--- a/hotspot/src/share/vm/gc_implementation/parallelScavenge/cardTableExtension.hpp ++++ b/hotspot/src/share/vm/gc_implementation/parallelScavenge/cardTableExtension.hpp +@@ -86,6 +86,9 @@ class CardTableExtension : public CardTableModRefBS { + void inline_write_ref_field_gc(void* field, oop new_val) { + jbyte* byte = byte_for(field); + *byte = youngergen_card; ++#if (defined MIPS || defined LOONGARCH) && !defined ZERO ++ if (UseSyncLevel >= 2000) OrderAccess::fence(); ++#endif + } + + // Adaptive size policy support +diff --git a/hotspot/src/share/vm/gc_implementation/parallelScavenge/parMarkBitMap.cpp b/hotspot/src/share/vm/gc_implementation/parallelScavenge/parMarkBitMap.cpp +index 1dde10746d..8b800b31c5 100644 +--- a/hotspot/src/share/vm/gc_implementation/parallelScavenge/parMarkBitMap.cpp ++++ b/hotspot/src/share/vm/gc_implementation/parallelScavenge/parMarkBitMap.cpp +@@ -105,6 +105,9 @@ ParMarkBitMap::mark_obj(HeapWord* addr, size_t size) + assert(end_bit_ok, "concurrency problem"); + DEBUG_ONLY(Atomic::inc_ptr(&mark_bitmap_count)); + DEBUG_ONLY(Atomic::add_ptr(size, &mark_bitmap_size)); ++#if (defined MIPS || defined LOONGARCH) && !defined ZERO ++ if (UseSyncLevel >= 2000) OrderAccess::fence(); ++#endif + return true; + } + return false; +diff --git a/hotspot/src/share/vm/gc_implementation/parallelScavenge/psCompactionManager.inline.hpp b/hotspot/src/share/vm/gc_implementation/parallelScavenge/psCompactionManager.inline.hpp +index 6cf76353d9..4d34bc209b 100644 +--- a/hotspot/src/share/vm/gc_implementation/parallelScavenge/psCompactionManager.inline.hpp ++++ b/hotspot/src/share/vm/gc_implementation/parallelScavenge/psCompactionManager.inline.hpp +@@ -33,6 +33,9 @@ void ParCompactionManager::push_objarray(oop obj, size_t index) + ObjArrayTask task(obj, index); + assert(task.is_valid(), "bad ObjArrayTask"); + _objarray_stack.push(task); ++#if (defined MIPS || defined LOONGARCH) && !defined ZERO ++ if (UseSyncLevel >= 2000) OrderAccess::fence(); ++#endif + } + + void ParCompactionManager::push_region(size_t index) +@@ -44,6 +47,9 @@ void ParCompactionManager::push_region(size_t index) + assert(region_ptr->_pushed++ == 0, "should only be pushed once"); + #endif + region_stack()->push(index); ++#if (defined MIPS || defined LOONGARCH) && !defined ZERO ++ if (UseSyncLevel >= 2000) OrderAccess::fence(); ++#endif + } + + #endif // SHARE_VM_GC_IMPLEMENTATION_PARALLELSCAVENGE_PSCOMPACTIONMANAGER_INLINE_HPP +diff --git a/hotspot/src/share/vm/gc_implementation/parallelScavenge/psParallelCompact.cpp b/hotspot/src/share/vm/gc_implementation/parallelScavenge/psParallelCompact.cpp +index 0fa980ef83..2f66493e0a 100644 +--- a/hotspot/src/share/vm/gc_implementation/parallelScavenge/psParallelCompact.cpp ++++ b/hotspot/src/share/vm/gc_implementation/parallelScavenge/psParallelCompact.cpp +@@ -499,6 +499,9 @@ void ParallelCompactData::add_obj(HeapWord* addr, size_t len) + if (beg_region == end_region) { + // All in one region. + _region_data[beg_region].add_live_obj(len); ++#if (defined MIPS || defined LOONGARCH) && !defined ZERO ++ if (UseSyncLevel >= 2000) OrderAccess::fence(); ++#endif + return; + } + +@@ -517,6 +520,9 @@ void ParallelCompactData::add_obj(HeapWord* addr, size_t len) + const size_t end_ofs = region_offset(addr + len - 1); + _region_data[end_region].set_partial_obj_size(end_ofs + 1); + _region_data[end_region].set_partial_obj_addr(addr); ++#if (defined MIPS || defined LOONGARCH) && !defined ZERO ++ if (UseSyncLevel >= 2000) OrderAccess::fence(); ++#endif + } + + void +@@ -3229,6 +3235,9 @@ void PSParallelCompact::fill_blocks(size_t region_idx) + if (new_block != cur_block) { + cur_block = new_block; + sd.block(cur_block)->set_offset(bitmap->bits_to_words(live_bits)); ++#if (defined MIPS || defined LOONGARCH) && !defined ZERO ++ if (UseSyncLevel >= 2000) OrderAccess::fence(); ++#endif + } + + const size_t end_bit = bitmap->find_obj_end(beg_bit, range_end); +diff --git a/hotspot/src/share/vm/gc_implementation/parallelScavenge/psParallelCompact.hpp b/hotspot/src/share/vm/gc_implementation/parallelScavenge/psParallelCompact.hpp +index 881f380cea..461b83930f 100644 +--- a/hotspot/src/share/vm/gc_implementation/parallelScavenge/psParallelCompact.hpp ++++ b/hotspot/src/share/vm/gc_implementation/parallelScavenge/psParallelCompact.hpp +@@ -1329,6 +1329,9 @@ inline bool PSParallelCompact::mark_obj(oop obj) { + const int obj_size = obj->size(); + if (mark_bitmap()->mark_obj(obj, obj_size)) { + _summary_data.add_obj(obj, obj_size); ++#if (defined MIPS || defined LOONGARCH) && !defined ZERO ++ if (UseSyncLevel >= 2000) OrderAccess::fence(); ++#endif + return true; + } else { + return false; +@@ -1363,6 +1366,9 @@ inline void PSParallelCompact::mark_and_push(ParCompactionManager* cm, T* p) { + oop obj = oopDesc::decode_heap_oop_not_null(heap_oop); + if (mark_bitmap()->is_unmarked(obj) && mark_obj(obj)) { + cm->push(obj); ++#if (defined MIPS || defined LOONGARCH) && !defined ZERO ++ if (UseSyncLevel >= 2000) OrderAccess::fence(); ++#endif + } + } + } +diff --git a/hotspot/src/share/vm/gc_implementation/parallelScavenge/psPromotionManager.inline.hpp b/hotspot/src/share/vm/gc_implementation/parallelScavenge/psPromotionManager.inline.hpp +index a33132009c..291019660a 100644 +--- a/hotspot/src/share/vm/gc_implementation/parallelScavenge/psPromotionManager.inline.hpp ++++ b/hotspot/src/share/vm/gc_implementation/parallelScavenge/psPromotionManager.inline.hpp +@@ -41,8 +41,9 @@ template + inline void PSPromotionManager::claim_or_forward_internal_depth(T* p) { + if (p != NULL) { // XXX: error if p != NULL here + oop o = oopDesc::load_decode_heap_oop_not_null(p); +- if (o->is_forwarded()) { +- o = o->forwardee(); ++ markOop m = o->mark(); ++ if (m->is_marked()) { ++ o = (oop) m->decode_pointer(); + // Card mark + if (PSScavenge::is_obj_in_young(o)) { + PSScavenge::card_table()->inline_write_ref_field_gc(p, o); +@@ -102,11 +103,19 @@ oop PSPromotionManager::copy_to_survivor_space(oop o) { + + oop new_obj = NULL; + ++#if (defined MIPS || defined LOONGARCH) && !defined ZERO ++ if (UseSyncLevel >= 2000) OrderAccess::fence(); ++#endif ++ + // NOTE! We must be very careful with any methods that access the mark + // in o. There may be multiple threads racing on it, and it may be forwarded + // at any time. Do not use oop methods for accessing the mark! + markOop test_mark = o->mark(); + ++#if (defined MIPS || defined LOONGARCH) && !defined ZERO ++ if (UseSyncLevel >= 2000) OrderAccess::fence(); ++#endif ++ + // The same test as "o->is_forwarded()" + if (!test_mark->is_marked()) { + bool new_obj_is_tenured = false; +@@ -141,6 +150,10 @@ oop PSPromotionManager::copy_to_survivor_space(oop o) { + } + } + } ++ ++#if (defined MIPS || defined LOONGARCH) && !defined ZERO ++ if (UseSyncLevel >= 2000) OrderAccess::fence(); ++#endif + } + } + +@@ -200,6 +213,9 @@ oop PSPromotionManager::copy_to_survivor_space(oop o) { + + // Copy obj + Copy::aligned_disjoint_words((HeapWord*)o, (HeapWord*)new_obj, new_obj_size); ++#if (defined MIPS || defined LOONGARCH) && !defined ZERO ++ if (UseSyncLevel >= 2000) OrderAccess::fence(); ++#endif + + // Now we have to CAS in the header. + if (o->cas_forward_to(new_obj, test_mark)) { +@@ -247,6 +263,10 @@ oop PSPromotionManager::copy_to_survivor_space(oop o) { + // don't update this before the unallocation! + new_obj = o->forwardee(); + } ++ ++#if (defined MIPS || defined LOONGARCH) && !defined ZERO ++ if (UseSyncLevel >= 2000) OrderAccess::fence(); ++#endif + } else { + assert(o->is_forwarded(), "Sanity"); + new_obj = o->forwardee(); +diff --git a/hotspot/src/share/vm/gc_implementation/parallelScavenge/psScavenge.inline.hpp b/hotspot/src/share/vm/gc_implementation/parallelScavenge/psScavenge.inline.hpp +index 1a722a7ca7..4980be3946 100644 +--- a/hotspot/src/share/vm/gc_implementation/parallelScavenge/psScavenge.inline.hpp ++++ b/hotspot/src/share/vm/gc_implementation/parallelScavenge/psScavenge.inline.hpp +@@ -71,14 +71,22 @@ inline void PSScavenge::copy_and_push_safe_barrier(PSPromotionManager* pm, + assert(should_scavenge(p, true), "revisiting object?"); + + oop o = oopDesc::load_decode_heap_oop_not_null(p); +- oop new_obj = o->is_forwarded() +- ? o->forwardee() +- : pm->copy_to_survivor_space(o); ++#if defined MIPS || defined LOONGARCH ++ if (oopDesc::is_null(o)) return; ++#endif ++ ++ oop new_obj; ++ markOop m = o->mark(); ++ if (m->is_marked()) { ++ new_obj = (oop) m->decode_pointer(); ++ } else { ++ new_obj = pm->copy_to_survivor_space(o); ++ } + + #ifndef PRODUCT + // This code must come after the CAS test, or it will print incorrect + // information. +- if (TraceScavenge && o->is_forwarded()) { ++ if (TraceScavenge && m->is_marked()) { + gclog_or_tty->print_cr("{%s %s " PTR_FORMAT " -> " PTR_FORMAT " (%d)}", + "forwarding", + new_obj->klass()->internal_name(), p2i((void *)o), p2i((void *)new_obj), new_obj->size()); +@@ -138,8 +146,9 @@ class PSScavengeFromKlassClosure: public OopClosure { + + oop o = *p; + oop new_obj; +- if (o->is_forwarded()) { +- new_obj = o->forwardee(); ++ markOop m = o->mark(); ++ if (m->is_marked()) { ++ new_obj = (oop) m->decode_pointer(); + } else { + new_obj = _pm->copy_to_survivor_space(o); + } +diff --git a/hotspot/src/share/vm/interpreter/abstractInterpreter.hpp b/hotspot/src/share/vm/interpreter/abstractInterpreter.hpp +index e14c50bf01..8b3860070c 100644 +--- a/hotspot/src/share/vm/interpreter/abstractInterpreter.hpp ++++ b/hotspot/src/share/vm/interpreter/abstractInterpreter.hpp +@@ -22,6 +22,12 @@ + * + */ + ++/* ++ * This file has been modified by Loongson Technology in 2020. These ++ * modifications are Copyright (c) 2015, 2020, Loongson Technology, and are made ++ * available on the same license terms set forth above. ++ */ ++ + #ifndef SHARE_VM_INTERPRETER_ABSTRACTINTERPRETER_HPP + #define SHARE_VM_INTERPRETER_ABSTRACTINTERPRETER_HPP + +@@ -42,6 +48,10 @@ + # include "interp_masm_zero.hpp" + #elif defined TARGET_ARCH_MODEL_ppc_64 + # include "interp_masm_ppc_64.hpp" ++#elif defined TARGET_ARCH_MODEL_mips_64 ++# include "interp_masm_mips_64.hpp" ++#elif defined TARGET_ARCH_MODEL_loongarch_64 ++# include "interp_masm_loongarch_64.hpp" + #endif + + // This file contains the platform-independent parts +diff --git a/hotspot/src/share/vm/interpreter/bytecode.hpp b/hotspot/src/share/vm/interpreter/bytecode.hpp +index 7e55fd009a..a06dcd58bc 100644 +--- a/hotspot/src/share/vm/interpreter/bytecode.hpp ++++ b/hotspot/src/share/vm/interpreter/bytecode.hpp +@@ -22,6 +22,12 @@ + * + */ + ++/* ++ * This file has been modified by Loongson Technology in 2020. These ++ * modifications are Copyright (c) 2015, 2020, Loongson Technology, and are made ++ * available on the same license terms set forth above. ++ */ ++ + #ifndef SHARE_VM_INTERPRETER_BYTECODE_HPP + #define SHARE_VM_INTERPRETER_BYTECODE_HPP + +@@ -31,6 +37,12 @@ + #ifdef TARGET_ARCH_x86 + # include "bytes_x86.hpp" + #endif ++#ifdef TARGET_ARCH_mips ++# include "bytes_mips.hpp" ++#endif ++#ifdef TARGET_ARCH_loongarch ++# include "bytes_loongarch.hpp" ++#endif + #ifdef TARGET_ARCH_aarch64 + # include "bytes_aarch64.hpp" + #endif +diff --git a/hotspot/src/share/vm/interpreter/bytecodeInterpreter.hpp b/hotspot/src/share/vm/interpreter/bytecodeInterpreter.hpp +index 28843715c7..c17fe8d7e0 100644 +--- a/hotspot/src/share/vm/interpreter/bytecodeInterpreter.hpp ++++ b/hotspot/src/share/vm/interpreter/bytecodeInterpreter.hpp +@@ -22,6 +22,12 @@ + * + */ + ++/* ++ * This file has been modified by Loongson Technology in 2020. These ++ * modifications are Copyright (c) 2015, 2020, Loongson Technology, and are made ++ * available on the same license terms set forth above. ++ */ ++ + #ifndef SHARE_VM_INTERPRETER_BYTECODEINTERPRETER_HPP + #define SHARE_VM_INTERPRETER_BYTECODEINTERPRETER_HPP + +@@ -35,6 +41,9 @@ + #ifdef TARGET_ARCH_x86 + # include "bytes_x86.hpp" + #endif ++#ifdef TARGET_ARCH_mips ++# include "bytes_mips.hpp" ++#endif + #ifdef TARGET_ARCH_aarch64 + # include "bytes_aarch64.hpp" + #endif +@@ -592,6 +601,12 @@ void print(); + #ifdef TARGET_ARCH_x86 + # include "bytecodeInterpreter_x86.hpp" + #endif ++#ifdef TARGET_ARCH_mips ++# include "bytecodeInterpreter_mips.hpp" ++#endif ++#ifdef TARGET_ARCH_loongarch ++# include "bytecodeInterpreter_loongarch.hpp" ++#endif + #ifdef TARGET_ARCH_aarch64 + # include "bytecodeInterpreter_aarch64.hpp" + #endif +diff --git a/hotspot/src/share/vm/interpreter/bytecodeInterpreter.inline.hpp b/hotspot/src/share/vm/interpreter/bytecodeInterpreter.inline.hpp +index f5db0b4d9d..8adbf95acb 100644 +--- a/hotspot/src/share/vm/interpreter/bytecodeInterpreter.inline.hpp ++++ b/hotspot/src/share/vm/interpreter/bytecodeInterpreter.inline.hpp +@@ -22,6 +22,12 @@ + * + */ + ++/* ++ * This file has been modified by Loongson Technology in 2020. These ++ * modifications are Copyright (c) 2015, 2020, Loongson Technology, and are made ++ * available on the same license terms set forth above. ++ */ ++ + #ifndef SHARE_VM_INTERPRETER_BYTECODEINTERPRETER_INLINE_HPP + #define SHARE_VM_INTERPRETER_BYTECODEINTERPRETER_INLINE_HPP + +@@ -46,6 +52,12 @@ + #ifdef TARGET_ARCH_x86 + # include "bytecodeInterpreter_x86.inline.hpp" + #endif ++#ifdef TARGET_ARCH_mips ++# include "bytecodeInterpreter_mips.inline.hpp" ++#endif ++#ifdef TARGET_ARCH_loongarch ++# include "bytecodeInterpreter_loongarch.inline.hpp" ++#endif + #ifdef TARGET_ARCH_aarch64 + # include "bytecodeInterpreter_aarch64.inline.hpp" + #endif +diff --git a/hotspot/src/share/vm/interpreter/bytecodeStream.hpp b/hotspot/src/share/vm/interpreter/bytecodeStream.hpp +index b814b88d5d..e1f2421600 100644 +--- a/hotspot/src/share/vm/interpreter/bytecodeStream.hpp ++++ b/hotspot/src/share/vm/interpreter/bytecodeStream.hpp +@@ -22,6 +22,12 @@ + * + */ + ++/* ++ * This file has been modified by Loongson Technology in 2020. These ++ * modifications are Copyright (c) 2015, 2020, Loongson Technology, and are made ++ * available on the same license terms set forth above. ++ */ ++ + #ifndef SHARE_VM_INTERPRETER_BYTECODESTREAM_HPP + #define SHARE_VM_INTERPRETER_BYTECODESTREAM_HPP + +@@ -32,6 +38,12 @@ + #ifdef TARGET_ARCH_x86 + # include "bytes_x86.hpp" + #endif ++#ifdef TARGET_ARCH_mips ++# include "bytes_mips.hpp" ++#endif ++#ifdef TARGET_ARCH_loongarch ++# include "bytes_loongarch.hpp" ++#endif + #ifdef TARGET_ARCH_aarch64 + # include "bytes_aarch64.hpp" + #endif +diff --git a/hotspot/src/share/vm/interpreter/bytecodes.cpp b/hotspot/src/share/vm/interpreter/bytecodes.cpp +index fdb880a3b3..4f5111074f 100644 +--- a/hotspot/src/share/vm/interpreter/bytecodes.cpp ++++ b/hotspot/src/share/vm/interpreter/bytecodes.cpp +@@ -22,6 +22,12 @@ + * + */ + ++/* ++ * This file has been modified by Loongson Technology in 2020. These ++ * modifications are Copyright (c) 2015, 2020, Loongson Technology, and are made ++ * available on the same license terms set forth above. ++ */ ++ + #include "precompiled.hpp" + #include "interpreter/bytecodes.hpp" + #include "memory/resourceArea.hpp" +@@ -29,6 +35,12 @@ + #ifdef TARGET_ARCH_x86 + # include "bytes_x86.hpp" + #endif ++#ifdef TARGET_ARCH_mips ++# include "bytes_mips.hpp" ++#endif ++#ifdef TARGET_ARCH_loongarch ++# include "bytes_loongarch.hpp" ++#endif + #ifdef TARGET_ARCH_aarch64 + # include "bytes_aarch64.hpp" + #endif +diff --git a/hotspot/src/share/vm/interpreter/bytecodes.hpp b/hotspot/src/share/vm/interpreter/bytecodes.hpp +index c3463cd76d..bdf4c487f0 100644 +--- a/hotspot/src/share/vm/interpreter/bytecodes.hpp ++++ b/hotspot/src/share/vm/interpreter/bytecodes.hpp +@@ -22,6 +22,12 @@ + * + */ + ++/* ++ * This file has been modified by Loongson Technology in 2020. These ++ * modifications are Copyright (c) 2015, 2020, Loongson Technology, and are made ++ * available on the same license terms set forth above. ++ */ ++ + #ifndef SHARE_VM_INTERPRETER_BYTECODES_HPP + #define SHARE_VM_INTERPRETER_BYTECODES_HPP + +@@ -292,6 +298,12 @@ class Bytecodes: AllStatic { + #ifdef TARGET_ARCH_x86 + # include "bytecodes_x86.hpp" + #endif ++#ifdef TARGET_ARCH_mips ++# include "bytecodes_mips.hpp" ++#endif ++#ifdef TARGET_ARCH_loongarch ++# include "bytecodes_loongarch.hpp" ++#endif + #ifdef TARGET_ARCH_aarch64 + # include "bytecodes_aarch64.hpp" + #endif +diff --git a/hotspot/src/share/vm/interpreter/cppInterpreter.hpp b/hotspot/src/share/vm/interpreter/cppInterpreter.hpp +index 6a6447503c..f9c540fb4a 100644 +--- a/hotspot/src/share/vm/interpreter/cppInterpreter.hpp ++++ b/hotspot/src/share/vm/interpreter/cppInterpreter.hpp +@@ -22,6 +22,12 @@ + * + */ + ++/* ++ * This file has been modified by Loongson Technology in 2020. These ++ * modifications are Copyright (c) 2015, 2020, Loongson Technology, and are made ++ * available on the same license terms set forth above. ++ */ ++ + #ifndef SHARE_VM_INTERPRETER_CPPINTERPRETER_HPP + #define SHARE_VM_INTERPRETER_CPPINTERPRETER_HPP + +@@ -84,6 +90,12 @@ class CppInterpreter: public AbstractInterpreter { + #ifdef TARGET_ARCH_x86 + # include "cppInterpreter_x86.hpp" + #endif ++#ifdef TARGET_ARCH_mips ++# include "cppInterpreter_mips.hpp" ++#endif ++#ifdef TARGET_ARCH_loongarch ++# include "cppInterpreter_loongarch.hpp" ++#endif + #ifdef TARGET_ARCH_aarch64 + # include "cppInterpreter_aarch64.hpp" + #endif +diff --git a/hotspot/src/share/vm/interpreter/cppInterpreterGenerator.hpp b/hotspot/src/share/vm/interpreter/cppInterpreterGenerator.hpp +index 6a08a3f43f..1fd19994d7 100644 +--- a/hotspot/src/share/vm/interpreter/cppInterpreterGenerator.hpp ++++ b/hotspot/src/share/vm/interpreter/cppInterpreterGenerator.hpp +@@ -22,6 +22,12 @@ + * + */ + ++/* ++ * This file has been modified by Loongson Technology in 2020. These ++ * modifications are Copyright (c) 2015, 2020, Loongson Technology, and are made ++ * available on the same license terms set forth above. ++ */ ++ + #ifndef SHARE_VM_INTERPRETER_CPPINTERPRETERGENERATOR_HPP + #define SHARE_VM_INTERPRETER_CPPINTERPRETERGENERATOR_HPP + +@@ -50,6 +56,12 @@ class CppInterpreterGenerator: public AbstractInterpreterGenerator { + #ifdef TARGET_ARCH_x86 + # include "cppInterpreterGenerator_x86.hpp" + #endif ++#ifdef TARGET_ARCH_mips ++# include "cppInterpreterGenerator_mips.hpp" ++#endif ++#ifdef TARGET_ARCH_loongarch ++# include "cppInterpreterGenerator_loongarch.hpp" ++#endif + #ifdef TARGET_ARCH_aarch64 + # include "cppInterpreterGenerator_aarch64.hpp" + #endif +diff --git a/hotspot/src/share/vm/interpreter/interpreter.hpp b/hotspot/src/share/vm/interpreter/interpreter.hpp +index ebfb68d36b..610949f3f7 100644 +--- a/hotspot/src/share/vm/interpreter/interpreter.hpp ++++ b/hotspot/src/share/vm/interpreter/interpreter.hpp +@@ -22,6 +22,12 @@ + * + */ + ++/* ++ * This file has been modified by Loongson Technology in 2020. These ++ * modifications are Copyright (c) 2015, 2020, Loongson Technology, and are made ++ * available on the same license terms set forth above. ++ */ ++ + #ifndef SHARE_VM_INTERPRETER_INTERPRETER_HPP + #define SHARE_VM_INTERPRETER_INTERPRETER_HPP + +@@ -148,6 +154,12 @@ class Interpreter: public CC_INTERP_ONLY(CppInterpreter) NOT_CC_INTERP(TemplateI + #ifdef TARGET_ARCH_x86 + # include "interpreter_x86.hpp" + #endif ++#ifdef TARGET_ARCH_mips ++# include "interpreter_mips.hpp" ++#endif ++#ifdef TARGET_ARCH_loongarch ++# include "interpreter_loongarch.hpp" ++#endif + #ifdef TARGET_ARCH_aarch64 + # include "interpreter_aarch64.hpp" + #endif +diff --git a/hotspot/src/share/vm/interpreter/interpreterGenerator.hpp b/hotspot/src/share/vm/interpreter/interpreterGenerator.hpp +index 1dc7cb2983..92bbe6b440 100644 +--- a/hotspot/src/share/vm/interpreter/interpreterGenerator.hpp ++++ b/hotspot/src/share/vm/interpreter/interpreterGenerator.hpp +@@ -22,6 +22,12 @@ + * + */ + ++/* ++ * This file has been modified by Loongson Technology in 2020. These ++ * modifications are Copyright (c) 2015, 2020, Loongson Technology, and are made ++ * available on the same license terms set forth above. ++ */ ++ + #ifndef SHARE_VM_INTERPRETER_INTERPRETERGENERATOR_HPP + #define SHARE_VM_INTERPRETER_INTERPRETERGENERATOR_HPP + +@@ -44,6 +50,12 @@ InterpreterGenerator(StubQueue* _code); + #ifdef TARGET_ARCH_x86 + # include "interpreterGenerator_x86.hpp" + #endif ++#ifdef TARGET_ARCH_mips ++# include "interpreterGenerator_mips.hpp" ++#endif ++#ifdef TARGET_ARCH_loongarch ++# include "interpreterGenerator_loongarch.hpp" ++#endif + #ifdef TARGET_ARCH_aarch64 + # include "interpreterGenerator_aarch64.hpp" + #endif +diff --git a/hotspot/src/share/vm/interpreter/interpreterRuntime.cpp b/hotspot/src/share/vm/interpreter/interpreterRuntime.cpp +index 5d2845383c..f48622f67e 100644 +--- a/hotspot/src/share/vm/interpreter/interpreterRuntime.cpp ++++ b/hotspot/src/share/vm/interpreter/interpreterRuntime.cpp +@@ -22,6 +22,12 @@ + * + */ + ++/* ++ * This file has been modified by Loongson Technology in 2020. These ++ * modifications are Copyright (c) 2015, 2020, Loongson Technology, and are made ++ * available on the same license terms set forth above. ++ */ ++ + #include "precompiled.hpp" + #include "classfile/systemDictionary.hpp" + #include "classfile/vmSymbols.hpp" +@@ -59,6 +65,12 @@ + #ifdef TARGET_ARCH_x86 + # include "vm_version_x86.hpp" + #endif ++#ifdef TARGET_ARCH_mips ++# include "vm_version_mips.hpp" ++#endif ++#ifdef TARGET_ARCH_loongarch ++# include "vm_version_loongarch.hpp" ++#endif + #ifdef TARGET_ARCH_aarch64 + # include "vm_version_aarch64.hpp" + #endif +@@ -1290,7 +1302,7 @@ IRT_ENTRY(void, InterpreterRuntime::prepare_native_call(JavaThread* thread, Meth + // preparing the same method will be sure to see non-null entry & mirror. + IRT_END + +-#if defined(IA32) || defined(AMD64) || defined(ARM) || defined(AARCH64) ++#if defined(IA32) || defined(AMD64) || defined(ARM) || defined(AARCH64) || defined(MIPS) || defined(LOONGARCH) + IRT_LEAF(void, InterpreterRuntime::popframe_move_outgoing_args(JavaThread* thread, void* src_address, void* dest_address)) + if (src_address == dest_address) { + return; +diff --git a/hotspot/src/share/vm/interpreter/interpreterRuntime.hpp b/hotspot/src/share/vm/interpreter/interpreterRuntime.hpp +index 472bf4d94c..9a98d5559c 100644 +--- a/hotspot/src/share/vm/interpreter/interpreterRuntime.hpp ++++ b/hotspot/src/share/vm/interpreter/interpreterRuntime.hpp +@@ -22,6 +22,12 @@ + * + */ + ++/* ++ * This file has been modified by Loongson Technology in 2020. These ++ * modifications are Copyright (c) 2015, 2020, Loongson Technology, and are made ++ * available on the same license terms set forth above. ++ */ ++ + #ifndef SHARE_VM_INTERPRETER_INTERPRETERRUNTIME_HPP + #define SHARE_VM_INTERPRETER_INTERPRETERRUNTIME_HPP + +@@ -156,7 +162,7 @@ class InterpreterRuntime: AllStatic { + Method* method, + intptr_t* from, intptr_t* to); + +-#if defined(IA32) || defined(AMD64) || defined(ARM) || defined(AARCH64) ++#if defined(IA32) || defined(AMD64) || defined(ARM) || defined(AARCH64) || defined(MIPS) || defined(LOONGARCH) + // Popframe support (only needed on x86, AMD64 and ARM) + static void popframe_move_outgoing_args(JavaThread* thread, void* src_address, void* dest_address); + #endif +@@ -165,6 +171,12 @@ class InterpreterRuntime: AllStatic { + #ifdef TARGET_ARCH_x86 + # include "interpreterRT_x86.hpp" + #endif ++#ifdef TARGET_ARCH_mips ++# include "interpreterRT_mips.hpp" ++#endif ++#ifdef TARGET_ARCH_loongarch ++# include "interpreterRT_loongarch.hpp" ++#endif + #ifdef TARGET_ARCH_aarch64 + # include "interpreterRT_aarch64.hpp" + #endif +diff --git a/hotspot/src/share/vm/interpreter/templateInterpreter.hpp b/hotspot/src/share/vm/interpreter/templateInterpreter.hpp +index 5f76dca8a6..757860f43c 100644 +--- a/hotspot/src/share/vm/interpreter/templateInterpreter.hpp ++++ b/hotspot/src/share/vm/interpreter/templateInterpreter.hpp +@@ -22,6 +22,12 @@ + * + */ + ++/* ++ * This file has been modified by Loongson Technology in 2020. These ++ * modifications are Copyright (c) 2015, 2020, Loongson Technology, and are made ++ * available on the same license terms set forth above. ++ */ ++ + #ifndef SHARE_VM_INTERPRETER_TEMPLATEINTERPRETER_HPP + #define SHARE_VM_INTERPRETER_TEMPLATEINTERPRETER_HPP + +@@ -190,6 +196,12 @@ class TemplateInterpreter: public AbstractInterpreter { + #ifdef TARGET_ARCH_x86 + # include "templateInterpreter_x86.hpp" + #endif ++#ifdef TARGET_ARCH_mips ++# include "templateInterpreter_mips.hpp" ++#endif ++#ifdef TARGET_ARCH_loongarch ++# include "templateInterpreter_loongarch.hpp" ++#endif + #ifdef TARGET_ARCH_aarch64 + # include "templateInterpreter_aarch64.hpp" + #endif +diff --git a/hotspot/src/share/vm/interpreter/templateInterpreterGenerator.hpp b/hotspot/src/share/vm/interpreter/templateInterpreterGenerator.hpp +index bd94bd02bc..28ca437eb2 100644 +--- a/hotspot/src/share/vm/interpreter/templateInterpreterGenerator.hpp ++++ b/hotspot/src/share/vm/interpreter/templateInterpreterGenerator.hpp +@@ -22,6 +22,12 @@ + * + */ + ++/* ++ * This file has been modified by Loongson Technology in 2020. These ++ * modifications are Copyright (c) 2015, 2020, Loongson Technology, and are made ++ * available on the same license terms set forth above. ++ */ ++ + #ifndef SHARE_VM_INTERPRETER_TEMPLATEINTERPRETERGENERATOR_HPP + #define SHARE_VM_INTERPRETER_TEMPLATEINTERPRETERGENERATOR_HPP + +@@ -89,6 +95,12 @@ class TemplateInterpreterGenerator: public AbstractInterpreterGenerator { + #ifdef TARGET_ARCH_x86 + # include "templateInterpreterGenerator_x86.hpp" + #endif ++#ifdef TARGET_ARCH_mips ++# include "templateInterpreterGenerator_mips.hpp" ++#endif ++#ifdef TARGET_ARCH_loongarch ++# include "templateInterpreterGenerator_loongarch.hpp" ++#endif + #ifdef TARGET_ARCH_aarch64 + # include "templateInterpreterGenerator_aarch64.hpp" + #endif +diff --git a/hotspot/src/share/vm/interpreter/templateTable.hpp b/hotspot/src/share/vm/interpreter/templateTable.hpp +index 60d243c16a..1b73822abd 100644 +--- a/hotspot/src/share/vm/interpreter/templateTable.hpp ++++ b/hotspot/src/share/vm/interpreter/templateTable.hpp +@@ -22,6 +22,12 @@ + * + */ + ++/* ++ * This file has been modified by Loongson Technology in 2020. These ++ * modifications are Copyright (c) 2015, 2020, Loongson Technology, and are made ++ * available on the same license terms set forth above. ++ */ ++ + #ifndef SHARE_VM_INTERPRETER_TEMPLATETABLE_HPP + #define SHARE_VM_INTERPRETER_TEMPLATETABLE_HPP + +@@ -40,6 +46,10 @@ + # include "interp_masm_zero.hpp" + #elif defined TARGET_ARCH_MODEL_ppc_64 + # include "interp_masm_ppc_64.hpp" ++#elif defined TARGET_ARCH_MODEL_mips_64 ++# include "interp_masm_mips_64.hpp" ++#elif defined TARGET_ARCH_MODEL_loongarch_64 ++# include "interp_masm_loongarch_64.hpp" + #endif + + #ifndef CC_INTERP +@@ -367,6 +377,10 @@ class TemplateTable: AllStatic { + # include "templateTable_zero.hpp" + #elif defined TARGET_ARCH_MODEL_ppc_64 + # include "templateTable_ppc_64.hpp" ++#elif defined TARGET_ARCH_MODEL_mips_64 ++# include "templateTable_mips_64.hpp" ++#elif defined TARGET_ARCH_MODEL_loongarch_64 ++# include "templateTable_loongarch_64.hpp" + #endif + + }; +diff --git a/hotspot/src/share/vm/jfr/utilities/jfrBigEndian.hpp b/hotspot/src/share/vm/jfr/utilities/jfrBigEndian.hpp +index 6d9ab39fdd..f4e9a4ca69 100644 +--- a/hotspot/src/share/vm/jfr/utilities/jfrBigEndian.hpp ++++ b/hotspot/src/share/vm/jfr/utilities/jfrBigEndian.hpp +@@ -116,7 +116,7 @@ inline T JfrBigEndian::read_unaligned(const address location) { + inline bool JfrBigEndian::platform_supports_unaligned_reads(void) { + #if defined(IA32) || defined(AMD64) || defined(PPC) || defined(S390) + return true; +-#elif defined(SPARC) || defined(ARM) || defined(AARCH64) ++#elif defined(SPARC) || defined(ARM) || defined(AARCH64) || defined(MIPS) || defined(LOONGARCH) + return false; + #else + #warning "Unconfigured platform" +diff --git a/hotspot/src/share/vm/memory/barrierSet.hpp b/hotspot/src/share/vm/memory/barrierSet.hpp +index 13ff9b2738..081b70744d 100644 +--- a/hotspot/src/share/vm/memory/barrierSet.hpp ++++ b/hotspot/src/share/vm/memory/barrierSet.hpp +@@ -27,6 +27,7 @@ + + #include "memory/memRegion.hpp" + #include "oops/oopsHierarchy.hpp" ++#include "runtime/orderAccess.hpp" + + // This class provides the interface between a barrier implementation and + // the rest of the system. +@@ -95,8 +96,16 @@ private: + // Keep this private so as to catch violations at build time. + virtual void write_ref_field_pre_work( void* field, oop new_val) { guarantee(false, "Not needed"); }; + protected: +- virtual void write_ref_field_pre_work( oop* field, oop new_val) {}; +- virtual void write_ref_field_pre_work(narrowOop* field, oop new_val) {}; ++ virtual void write_ref_field_pre_work( oop* field, oop new_val) { ++#if (defined MIPS || defined LOONGARCH) && !defined ZERO ++ if (UseSyncLevel >= 2000) OrderAccess::fence(); ++#endif ++ }; ++ virtual void write_ref_field_pre_work(narrowOop* field, oop new_val) { ++#if (defined MIPS || defined LOONGARCH) && !defined ZERO ++ if (UseSyncLevel >= 2000) OrderAccess::fence(); ++#endif ++ }; + public: + + // ...then the post-write version. +@@ -132,9 +141,17 @@ public: + + // Below length is the # array elements being written + virtual void write_ref_array_pre(oop* dst, int length, +- bool dest_uninitialized = false) {} ++ bool dest_uninitialized = false) { ++#if (defined MIPS || defined LOONGARCH) && !defined ZERO ++ if (UseSyncLevel >= 2000) OrderAccess::fence(); ++#endif ++ } + virtual void write_ref_array_pre(narrowOop* dst, int length, +- bool dest_uninitialized = false) {} ++ bool dest_uninitialized = false) { ++#if (defined MIPS || defined LOONGARCH) && !defined ZERO ++ if (UseSyncLevel >= 2000) OrderAccess::fence(); ++#endif ++} + // Below count is the # array elements being written, starting + // at the address "start", which may not necessarily be HeapWord-aligned + inline void write_ref_array(HeapWord* start, size_t count); +diff --git a/hotspot/src/share/vm/memory/cardTableModRefBS.hpp b/hotspot/src/share/vm/memory/cardTableModRefBS.hpp +index 01e4688836..80bd151873 100644 +--- a/hotspot/src/share/vm/memory/cardTableModRefBS.hpp ++++ b/hotspot/src/share/vm/memory/cardTableModRefBS.hpp +@@ -316,6 +316,9 @@ public: + + inline void inline_write_ref_array(MemRegion mr) { + dirty_MemRegion(mr); ++#if (defined MIPS || defined LOONGARCH) && !defined ZERO ++ if (UseSyncLevel >= 2000) OrderAccess::fence(); ++#endif + } + protected: + void write_ref_array_work(MemRegion mr) { +@@ -329,7 +332,11 @@ public: + + // *** Card-table-barrier-specific things. + +- template inline void inline_write_ref_field_pre(T* field, oop newVal) {} ++ template inline void inline_write_ref_field_pre(T* field, oop newVal) { ++#if (defined MIPS || defined LOONGARCH) && !defined ZERO ++ if (UseSyncLevel >= 2000) OrderAccess::fence(); ++#endif ++ } + + template inline void inline_write_ref_field(T* field, oop newVal, bool release) { + jbyte* byte = byte_for((void*)field); +@@ -339,6 +346,9 @@ public: + } else { + *byte = dirty_card; + } ++#if (defined MIPS || defined LOONGARCH) && !defined ZERO ++ if (UseSyncLevel >= 2000) OrderAccess::fence(); ++#endif + } + + // These are used by G1, when it uses the card table as a temporary data +diff --git a/hotspot/src/share/vm/memory/cardTableRS.cpp b/hotspot/src/share/vm/memory/cardTableRS.cpp +index fb33a708ae..da22acba47 100644 +--- a/hotspot/src/share/vm/memory/cardTableRS.cpp ++++ b/hotspot/src/share/vm/memory/cardTableRS.cpp +@@ -252,6 +252,9 @@ void ClearNoncleanCardWrapper::do_MemRegion(MemRegion mr) { + // cur_youngergen_and_prev_nonclean_card ==> no change. + void CardTableRS::write_ref_field_gc_par(void* field, oop new_val) { + jbyte* entry = ct_bs()->byte_for(field); ++#if (defined MIPS || defined LOONGARCH) && !defined ZERO ++ if (UseSyncLevel >= 2000) OrderAccess::fence(); ++#endif + do { + jbyte entry_val = *entry; + // We put this first because it's probably the most common case. +@@ -266,7 +269,12 @@ void CardTableRS::write_ref_field_gc_par(void* field, oop new_val) { + jbyte new_val = cur_youngergen_and_prev_nonclean_card; + jbyte res = Atomic::cmpxchg(new_val, entry, entry_val); + // Did the CAS succeed? +- if (res == entry_val) return; ++ if (res == entry_val) { ++#if (defined MIPS || defined LOONGARCH) && !defined ZERO ++ if (UseSyncLevel >= 2000) OrderAccess::fence(); ++#endif ++ return; ++ } + // Otherwise, retry, to see the new value. + continue; + } else { +diff --git a/hotspot/src/share/vm/memory/cardTableRS.hpp b/hotspot/src/share/vm/memory/cardTableRS.hpp +index 25884feac8..5d4e77f269 100644 +--- a/hotspot/src/share/vm/memory/cardTableRS.hpp ++++ b/hotspot/src/share/vm/memory/cardTableRS.hpp +@@ -121,7 +121,14 @@ public: + + void inline_write_ref_field_gc(void* field, oop new_val) { + jbyte* byte = _ct_bs->byte_for(field); +- *byte = youngergen_card; ++#if (defined MIPS || defined LOONGARCH) && !defined ZERO ++ if (UseSyncLevel >= 2000) OrderAccess::fence(); ++#endif ++ *byte = youngergen_card; ++#if (defined MIPS || defined LOONGARCH) && !defined ZERO ++ if (UseSyncLevel >= 2000) OrderAccess::fence(); ++#endif ++ + } + void write_ref_field_gc_work(void* field, oop new_val) { + inline_write_ref_field_gc(field, new_val); +diff --git a/hotspot/src/share/vm/memory/metaspace.cpp b/hotspot/src/share/vm/memory/metaspace.cpp +index 600bcfd125..f326dad76a 100644 +--- a/hotspot/src/share/vm/memory/metaspace.cpp ++++ b/hotspot/src/share/vm/memory/metaspace.cpp +@@ -21,6 +21,13 @@ + * questions. + * + */ ++ ++/* ++ * This file has been modified by Loongson Technology in 2021. These ++ * modifications are Copyright (c) 2021 Loongson Technology, and are made ++ * available on the same license terms set forth above. ++ */ ++ + #include "precompiled.hpp" + #include "gc_interface/collectedHeap.hpp" + #include "memory/allocation.hpp" +@@ -3065,12 +3072,12 @@ void Metaspace::allocate_metaspace_compressed_klass_ptrs(char* requested_addr, a + // Don't use large pages for the class space. + bool large_pages = false; + +-#ifndef AARCH64 ++#if !defined(AARCH64) && !defined(MIPS64) && !defined(LOONGARCH) + ReservedSpace metaspace_rs = ReservedSpace(compressed_class_space_size(), + _reserve_alignment, + large_pages, + requested_addr, 0); +-#else // AARCH64 ++#else // defined(AARCH64) || defined(MIPS64) || defined(LOONGARCH) + ReservedSpace metaspace_rs; + + // Our compressed klass pointers may fit nicely into the lower 32 +@@ -3107,7 +3114,7 @@ void Metaspace::allocate_metaspace_compressed_klass_ptrs(char* requested_addr, a + } + } + +-#endif // AARCH64 ++#endif // defined(AARCH64) || defined(MIPS64) || defined(LOONGARCH) + + if (!metaspace_rs.is_reserved()) { + #if INCLUDE_CDS +@@ -3937,11 +3944,13 @@ class TestVirtualSpaceNodeTest { + assert(cm.sum_free_chunks() == 2*MediumChunk, "sizes should add up"); + } + +- { // 4 pages of VSN is committed, some is used by chunks ++ const size_t page_chunks = 4 * (size_t)os::vm_page_size() / BytesPerWord; ++ // This doesn't work for systems with vm_page_size >= 16K. ++ if (page_chunks < MediumChunk) { ++ // 4 pages of VSN is committed, some is used by chunks + ChunkManager cm(SpecializedChunk, SmallChunk, MediumChunk); + VirtualSpaceNode vsn(vsn_test_size_bytes); +- const size_t page_chunks = 4 * (size_t)os::vm_page_size() / BytesPerWord; +- assert(page_chunks < MediumChunk, "Test expects medium chunks to be at least 4*page_size"); ++ + vsn.initialize(); + vsn.expand_by(page_chunks, page_chunks); + vsn.get_chunk_vs(SmallChunk); +diff --git a/hotspot/src/share/vm/oops/constantPool.hpp b/hotspot/src/share/vm/oops/constantPool.hpp +index ec111df04e..6c0607105c 100644 +--- a/hotspot/src/share/vm/oops/constantPool.hpp ++++ b/hotspot/src/share/vm/oops/constantPool.hpp +@@ -22,6 +22,12 @@ + * + */ + ++/* ++ * This file has been modified by Loongson Technology in 2020. These ++ * modifications are Copyright (c) 2015, 2020, Loongson Technology, and are made ++ * available on the same license terms set forth above. ++ */ ++ + #ifndef SHARE_VM_OOPS_CONSTANTPOOLOOP_HPP + #define SHARE_VM_OOPS_CONSTANTPOOLOOP_HPP + +@@ -50,6 +56,13 @@ + #ifdef TARGET_ARCH_ppc + # include "bytes_ppc.hpp" + #endif ++#ifdef TARGET_ARCH_mips ++# include "bytes_mips.hpp" ++#endif ++#ifdef TARGET_ARCH_loongarch ++# include "bytes_loongarch.hpp" ++#endif ++ + + // A constantPool is an array containing class constants as described in the + // class file. +diff --git a/hotspot/src/share/vm/oops/klass.hpp b/hotspot/src/share/vm/oops/klass.hpp +index acef334849..23fc0b9988 100644 +--- a/hotspot/src/share/vm/oops/klass.hpp ++++ b/hotspot/src/share/vm/oops/klass.hpp +@@ -32,6 +32,9 @@ + #include "oops/klassPS.hpp" + #include "oops/metadata.hpp" + #include "oops/oop.hpp" ++#if defined MIPS || defined LOONGARCH ++#include "runtime/orderAccess.hpp" ++#endif + #include "utilities/accessFlags.hpp" + #include "utilities/macros.hpp" + #if INCLUDE_ALL_GCS +@@ -289,8 +292,18 @@ protected: + // The Klasses are not placed in the Heap, so the Card Table or + // the Mod Union Table can't be used to mark when klasses have modified oops. + // The CT and MUT bits saves this information for the individual Klasses. +- void record_modified_oops() { _modified_oops = 1; } +- void clear_modified_oops() { _modified_oops = 0; } ++ void record_modified_oops() { ++ _modified_oops = 1; ++#if (defined MIPS || defined LOONGARCH) && !defined ZERO ++ if (UseSyncLevel >= 2000) OrderAccess::fence(); ++#endif ++ } ++ void clear_modified_oops() { ++ _modified_oops = 0; ++#if (defined MIPS || defined LOONGARCH) && !defined ZERO ++ if (UseSyncLevel >= 2000) OrderAccess::fence(); ++#endif ++ } + bool has_modified_oops() { return _modified_oops == 1; } + + void accumulate_modified_oops() { if (has_modified_oops()) _accumulated_modified_oops = 1; } +diff --git a/hotspot/src/share/vm/oops/oop.hpp b/hotspot/src/share/vm/oops/oop.hpp +index 0678c6b3fb..1cb20e351f 100644 +--- a/hotspot/src/share/vm/oops/oop.hpp ++++ b/hotspot/src/share/vm/oops/oop.hpp +@@ -72,7 +72,13 @@ class oopDesc { + markOop mark() const { return _mark; } + markOop* mark_addr() const { return (markOop*) &_mark; } + +- void set_mark(volatile markOop m) { _mark = m; } ++ void set_mark(volatile markOop m) { ++#if (defined MIPS || defined LOONGARCH) && !defined ZERO ++ if (UseSyncLevel >= 2000) release_set_mark(m); ++ else ++#endif ++ _mark = m; ++ } + + void release_set_mark(markOop m); + markOop cas_set_mark(markOop new_mark, markOop old_mark); +diff --git a/hotspot/src/share/vm/oops/oop.inline.hpp b/hotspot/src/share/vm/oops/oop.inline.hpp +index beec739d38..8660c1e331 100644 +--- a/hotspot/src/share/vm/oops/oop.inline.hpp ++++ b/hotspot/src/share/vm/oops/oop.inline.hpp +@@ -22,6 +22,12 @@ + * + */ + ++/* ++ * This file has been modified by Loongson Technology in 2020. These ++ * modifications are Copyright (c) 2015, 2020, Loongson Technology, and are made ++ * available on the same license terms set forth above. ++ */ ++ + #ifndef SHARE_VM_OOPS_OOP_INLINE_HPP + #define SHARE_VM_OOPS_OOP_INLINE_HPP + +@@ -60,6 +66,12 @@ + #ifdef TARGET_ARCH_ppc + # include "bytes_ppc.hpp" + #endif ++#ifdef TARGET_ARCH_mips ++# include "bytes_mips.hpp" ++#endif ++#ifdef TARGET_ARCH_loongarch ++# include "bytes_loongarch.hpp" ++#endif + + // Implementation of all inlined member functions defined in oop.hpp + // We need a separate file to avoid circular references +diff --git a/hotspot/src/share/vm/opto/buildOopMap.cpp b/hotspot/src/share/vm/opto/buildOopMap.cpp +index 91642f1d7d..5df185df04 100644 +--- a/hotspot/src/share/vm/opto/buildOopMap.cpp ++++ b/hotspot/src/share/vm/opto/buildOopMap.cpp +@@ -22,6 +22,12 @@ + * + */ + ++/* ++ * This file has been modified by Loongson Technology in 2020. These ++ * modifications are Copyright (c) 2015, 2020, Loongson Technology, and are made ++ * available on the same license terms set forth above. ++ */ ++ + #include "precompiled.hpp" + #include "compiler/oopMap.hpp" + #include "opto/addnode.hpp" +@@ -50,6 +56,12 @@ + #ifdef TARGET_ARCH_ppc + # include "vmreg_ppc.inline.hpp" + #endif ++#ifdef TARGET_ARCH_mips ++# include "vmreg_mips.inline.hpp" ++#endif ++#ifdef TARGET_ARCH_loongarch ++# include "vmreg_loongarch.inline.hpp" ++#endif + + // The functions in this file builds OopMaps after all scheduling is done. + // +diff --git a/hotspot/src/share/vm/opto/bytecodeInfo.cpp b/hotspot/src/share/vm/opto/bytecodeInfo.cpp +index 7fd615d35f..ad472e8722 100644 +--- a/hotspot/src/share/vm/opto/bytecodeInfo.cpp ++++ b/hotspot/src/share/vm/opto/bytecodeInfo.cpp +@@ -361,9 +361,20 @@ bool InlineTree::try_to_inline(ciMethod* callee_method, ciMethod* caller_method, + } else if (forced_inline()) { + // Inlining was forced by CompilerOracle, ciReplay or annotation + } else if (profile.count() == 0) { ++#ifndef MIPS + // don't inline unreached call sites + set_msg("call site not reached"); + return false; ++#else ++ ciMethodBlocks* blocks = caller_method->get_method_blocks(); ++ // Check if the call site belongs to a start block: ++ // call sites in a start block must be reached before. ++ if (blocks->block_containing(0) != blocks->block_containing(jvms->bci())) { ++ // don't inline unreached call sites ++ set_msg("call site not reached"); ++ return false; ++ } ++#endif + } + } + +diff --git a/hotspot/src/share/vm/opto/c2_globals.hpp b/hotspot/src/share/vm/opto/c2_globals.hpp +index 82d2efef92..d373b20456 100644 +--- a/hotspot/src/share/vm/opto/c2_globals.hpp ++++ b/hotspot/src/share/vm/opto/c2_globals.hpp +@@ -22,6 +22,12 @@ + * + */ + ++/* ++ * This file has been modified by Loongson Technology in 2020. These ++ * modifications are Copyright (c) 2015, 2020, Loongson Technology, and are made ++ * available on the same license terms set forth above. ++ */ ++ + #ifndef SHARE_VM_OPTO_C2_GLOBALS_HPP + #define SHARE_VM_OPTO_C2_GLOBALS_HPP + +@@ -35,6 +41,12 @@ + #ifdef TARGET_ARCH_sparc + # include "c2_globals_sparc.hpp" + #endif ++#ifdef TARGET_ARCH_mips ++# include "c2_globals_mips.hpp" ++#endif ++#ifdef TARGET_ARCH_loongarch ++# include "c2_globals_loongarch.hpp" ++#endif + #ifdef TARGET_ARCH_arm + # include "c2_globals_arm.hpp" + #endif +diff --git a/hotspot/src/share/vm/opto/c2compiler.cpp b/hotspot/src/share/vm/opto/c2compiler.cpp +index 137f49600d..f689d64a38 100644 +--- a/hotspot/src/share/vm/opto/c2compiler.cpp ++++ b/hotspot/src/share/vm/opto/c2compiler.cpp +@@ -22,6 +22,12 @@ + * + */ + ++/* ++ * This file has been modified by Loongson Technology in 2020. These ++ * modifications are Copyright (c) 2015, 2020, Loongson Technology, and are made ++ * available on the same license terms set forth above. ++ */ ++ + #include "precompiled.hpp" + #include "opto/c2compiler.hpp" + #include "opto/runtime.hpp" +@@ -39,6 +45,10 @@ + # include "adfiles/ad_zero.hpp" + #elif defined TARGET_ARCH_MODEL_ppc_64 + # include "adfiles/ad_ppc_64.hpp" ++#elif defined TARGET_ARCH_MODEL_mips_64 ++# include "adfiles/ad_mips_64.hpp" ++#elif defined TARGET_ARCH_MODEL_loongarch_64 ++# include "adfiles/ad_loongarch_64.hpp" + #endif + + // register information defined by ADLC +diff --git a/hotspot/src/share/vm/opto/chaitin.hpp b/hotspot/src/share/vm/opto/chaitin.hpp +index de6d443cd3..0b27dc9335 100644 +--- a/hotspot/src/share/vm/opto/chaitin.hpp ++++ b/hotspot/src/share/vm/opto/chaitin.hpp +@@ -22,6 +22,12 @@ + * + */ + ++/* ++ * This file has been modified by Loongson Technology in 2022, These ++ * modifications are Copyright (c) 2022, Loongson Technology, and are made ++ * available on the same license terms set forth above. ++ */ ++ + #ifndef SHARE_VM_OPTO_CHAITIN_HPP + #define SHARE_VM_OPTO_CHAITIN_HPP + +@@ -136,8 +142,12 @@ public: + + // Number of registers this live range uses when it colors + private: ++#ifdef LOONGARCH64 ++ uint16_t _num_regs; ++#else + uint8 _num_regs; // 2 for Longs and Doubles, 1 for all else + // except _num_regs is kill count for fat_proj ++#endif + public: + int num_regs() const { return _num_regs; } + void set_num_regs( int reg ) { assert( _num_regs == reg || !_num_regs, "" ); _num_regs = reg; } +@@ -145,7 +155,11 @@ public: + private: + // Number of physical registers this live range uses when it colors + // Architecture and register-set dependent ++#ifdef LOONGARCH64 ++ uint16_t _reg_pressure; ++#else + uint8 _reg_pressure; ++#endif + public: + void set_reg_pressure(int i) { _reg_pressure = i; } + int reg_pressure() const { return _reg_pressure; } +diff --git a/hotspot/src/share/vm/opto/compile.cpp b/hotspot/src/share/vm/opto/compile.cpp +index 5bee20b5a0..c4d893422b 100644 +--- a/hotspot/src/share/vm/opto/compile.cpp ++++ b/hotspot/src/share/vm/opto/compile.cpp +@@ -22,6 +22,12 @@ + * + */ + ++/* ++ * This file has been modified by Loongson Technology in 2020. These ++ * modifications are Copyright (c) 2015, 2020, Loongson Technology, and are made ++ * available on the same license terms set forth above. ++ */ ++ + #include "precompiled.hpp" + #include "asm/macroAssembler.hpp" + #include "asm/macroAssembler.inline.hpp" +@@ -81,6 +87,10 @@ + # include "adfiles/ad_zero.hpp" + #elif defined TARGET_ARCH_MODEL_ppc_64 + # include "adfiles/ad_ppc_64.hpp" ++#elif defined TARGET_ARCH_MODEL_mips_64 ++# include "adfiles/ad_mips_64.hpp" ++#elif defined TARGET_ARCH_MODEL_loongarch_64 ++# include "adfiles/ad_loongarch_64.hpp" + #endif + + // -------------------- Compile::mach_constant_base_node ----------------------- +diff --git a/hotspot/src/share/vm/opto/compile.hpp b/hotspot/src/share/vm/opto/compile.hpp +index c92e7d2dbd..777b5120c1 100644 +--- a/hotspot/src/share/vm/opto/compile.hpp ++++ b/hotspot/src/share/vm/opto/compile.hpp +@@ -1029,7 +1029,7 @@ class Compile : public Phase { + bool in_scratch_emit_size() const { return _in_scratch_emit_size; } + + enum ScratchBufferBlob { +- MAX_inst_size = 1024, ++ MAX_inst_size = 1024 MIPS64_ONLY(* 2) LOONGARCH64_ONLY(*2), + MAX_locs_size = 128, // number of relocInfo elements + MAX_const_size = 128, + MAX_stubs_size = 128 +diff --git a/hotspot/src/share/vm/opto/gcm.cpp b/hotspot/src/share/vm/opto/gcm.cpp +index f51484efb0..12457b7c34 100644 +--- a/hotspot/src/share/vm/opto/gcm.cpp ++++ b/hotspot/src/share/vm/opto/gcm.cpp +@@ -22,6 +22,12 @@ + * + */ + ++/* ++ * This file has been modified by Loongson Technology in 2020. These ++ * modifications are Copyright (c) 2015, 2020, Loongson Technology, and are made ++ * available on the same license terms set forth above. ++ */ ++ + #include "precompiled.hpp" + #include "libadt/vectset.hpp" + #include "memory/allocation.inline.hpp" +@@ -49,6 +55,10 @@ + # include "adfiles/ad_zero.hpp" + #elif defined TARGET_ARCH_MODEL_ppc_64 + # include "adfiles/ad_ppc_64.hpp" ++#elif defined TARGET_ARCH_MODEL_mips_64 ++# include "adfiles/ad_mips_64.hpp" ++#elif defined TARGET_ARCH_MODEL_loongarch_64 ++# include "adfiles/ad_loongarch_64.hpp" + #endif + + +diff --git a/hotspot/src/share/vm/opto/lcm.cpp b/hotspot/src/share/vm/opto/lcm.cpp +index c6178a715b..2d492568d9 100644 +--- a/hotspot/src/share/vm/opto/lcm.cpp ++++ b/hotspot/src/share/vm/opto/lcm.cpp +@@ -22,6 +22,12 @@ + * + */ + ++/* ++ * This file has been modified by Loongson Technology in 2020. These ++ * modifications are Copyright (c) 2015, 2020, Loongson Technology, and are made ++ * available on the same license terms set forth above. ++ */ ++ + #include "precompiled.hpp" + #include "memory/allocation.inline.hpp" + #include "opto/block.hpp" +@@ -44,6 +50,10 @@ + # include "adfiles/ad_zero.hpp" + #elif defined TARGET_ARCH_MODEL_ppc_64 + # include "adfiles/ad_ppc_64.hpp" ++#elif defined TARGET_ARCH_MODEL_mips_64 ++# include "adfiles/ad_mips_64.hpp" ++#elif defined TARGET_ARCH_MODEL_loongarch_64 ++# include "adfiles/ad_loongarch_64.hpp" + #endif + + // Optimization - Graph Style +diff --git a/hotspot/src/share/vm/opto/locknode.hpp b/hotspot/src/share/vm/opto/locknode.hpp +index b320f6bfb2..4bfb0ff072 100644 +--- a/hotspot/src/share/vm/opto/locknode.hpp ++++ b/hotspot/src/share/vm/opto/locknode.hpp +@@ -22,6 +22,12 @@ + * + */ + ++/* ++ * This file has been modified by Loongson Technology in 2020. These ++ * modifications are Copyright (c) 2015, 2020, Loongson Technology, and are made ++ * available on the same license terms set forth above. ++ */ ++ + #ifndef SHARE_VM_OPTO_LOCKNODE_HPP + #define SHARE_VM_OPTO_LOCKNODE_HPP + +@@ -42,6 +48,10 @@ + # include "adfiles/ad_zero.hpp" + #elif defined TARGET_ARCH_MODEL_ppc_64 + # include "adfiles/ad_ppc_64.hpp" ++#elif defined TARGET_ARCH_MODEL_mips_64 ++# include "adfiles/ad_mips_64.hpp" ++#elif defined TARGET_ARCH_MODEL_loongarch_64 ++# include "adfiles/ad_loongarch_64.hpp" + #endif + + //------------------------------BoxLockNode------------------------------------ +diff --git a/hotspot/src/share/vm/opto/matcher.cpp b/hotspot/src/share/vm/opto/matcher.cpp +index 0c9a8d431b..f768524c99 100644 +--- a/hotspot/src/share/vm/opto/matcher.cpp ++++ b/hotspot/src/share/vm/opto/matcher.cpp +@@ -22,6 +22,12 @@ + * + */ + ++/* ++ * This file has been modified by Loongson Technology in 2020. These ++ * modifications are Copyright (c) 2015, 2020, Loongson Technology, and are made ++ * available on the same license terms set forth above. ++ */ ++ + #include "precompiled.hpp" + #include "memory/allocation.inline.hpp" + #include "opto/addnode.hpp" +@@ -52,6 +58,10 @@ + # include "adfiles/ad_zero.hpp" + #elif defined TARGET_ARCH_MODEL_ppc_64 + # include "adfiles/ad_ppc_64.hpp" ++#elif defined TARGET_ARCH_MODEL_mips_64 ++# include "adfiles/ad_mips_64.hpp" ++#elif defined TARGET_ARCH_MODEL_loongarch_64 ++# include "adfiles/ad_loongarch_64.hpp" + #endif + + OptoReg::Name OptoReg::c_frame_pointer; +diff --git a/hotspot/src/share/vm/opto/output.cpp b/hotspot/src/share/vm/opto/output.cpp +index 5c9566e1ea..6579d81d35 100644 +--- a/hotspot/src/share/vm/opto/output.cpp ++++ b/hotspot/src/share/vm/opto/output.cpp +@@ -22,6 +22,12 @@ + * + */ + ++/* ++ * This file has been modified by Loongson Technology in 2021. These ++ * modifications are Copyright (c) 2018, 2021, Loongson Technology, and are made ++ * available on the same license terms set forth above. ++ */ ++ + #include "precompiled.hpp" + #include "asm/assembler.inline.hpp" + #include "code/compiledIC.hpp" +@@ -844,6 +850,27 @@ void Compile::Process_OopMap_Node(MachNode *mach, int current_offset) { + // Add the safepoint in the DebugInfoRecorder + if( !mach->is_MachCall() ) { + mcall = NULL; ++#if defined(MIPS) || defined(LOONGARCH) ++ // safepoint_pc_offset should point to tha last instruction in safePoint. ++ // In X86 and sparc, their safePoints only contain one instruction. ++ // However, we should add current_offset with the size of safePoint in MIPS. ++ // 0x2d6ff22c: lw s2, 0x14(s2) ++ // last_pd->pc_offset()=308, pc_offset=304, bci=64 ++ // last_pd->pc_offset()=312, pc_offset=312, bci=64 ++ // src/hotspot/share/code/debugInfoRec.cpp:295, assert(last_pd->pc_offset() == pc_offset, "must be last pc") ++ // ++ // ;; Safepoint: ++ // ---> pc_offset=304 ++ // 0x2d6ff230: lui at, 0x2b7a ; OopMap{s2=Oop s5=Oop t4=Oop off=308} ++ // ;*goto ++ // ; - java.util.Hashtable::get@64 (line 353) ++ // ---> last_pd(308) ++ // 0x2d6ff234: lw at, 0xffffc100(at) ;*goto ++ // ; - java.util.Hashtable::get@64 (line 353) ++ // ; {poll} ++ // 0x2d6ff238: addiu s0, zero, 0x0 ++ safepoint_pc_offset += sfn->size(_regalloc) - 4; ++#endif + debug_info()->add_safepoint(safepoint_pc_offset, sfn->_oop_map); + } else { + mcall = mach->as_MachCall(); +@@ -1502,6 +1529,22 @@ void Compile::fill_buffer(CodeBuffer* cb, uint* blk_starts) { + DEBUG_ONLY( uint instr_offset = cb->insts_size(); ) + n->emit(*cb, _regalloc); + current_offset = cb->insts_size(); ++#if defined(MIPS) || defined(LOONGARCH) ++ if (!n->is_Proj() && (cb->insts()->end() != badAddress)) { ++ // For MIPS, the first instruction of the previous node (usually a instruction sequence) sometime ++ // is not the instruction which access memory. adjust is needed. previous_offset points to the ++ // instruction which access memory. Instruction size is 4. cb->insts_size() and ++ // cb->insts()->end() are the location of current instruction. ++ int adjust = 4; ++ NativeInstruction* inst = (NativeInstruction*) (cb->insts()->end() - 4); ++ if (inst->is_sync()) { ++ // a sync may be the last instruction, see store_B_immI_enc_sync ++ adjust += 4; ++ inst = (NativeInstruction*) (cb->insts()->end() - 8); ++ } ++ previous_offset = current_offset - adjust; ++ } ++#endif + + // Above we only verified that there is enough space in the instruction section. + // However, the instruction may emit stubs that cause code buffer expansion. +diff --git a/hotspot/src/share/vm/opto/output.hpp b/hotspot/src/share/vm/opto/output.hpp +index ba72841363..37f954de9b 100644 +--- a/hotspot/src/share/vm/opto/output.hpp ++++ b/hotspot/src/share/vm/opto/output.hpp +@@ -22,6 +22,12 @@ + * + */ + ++/* ++ * This file has been modified by Loongson Technology in 2020. These ++ * modifications are Copyright (c) 2015, 2020, Loongson Technology, and are made ++ * available on the same license terms set forth above. ++ */ ++ + #ifndef SHARE_VM_OPTO_OUTPUT_HPP + #define SHARE_VM_OPTO_OUTPUT_HPP + +@@ -41,6 +47,10 @@ + # include "adfiles/ad_zero.hpp" + #elif defined TARGET_ARCH_MODEL_ppc_64 + # include "adfiles/ad_ppc_64.hpp" ++#elif defined TARGET_ARCH_MODEL_mips_64 ++# include "adfiles/ad_mips_64.hpp" ++#elif defined TARGET_ARCH_MODEL_loongarch_64 ++# include "adfiles/ad_loongarch_64.hpp" + #endif + + class Arena; +diff --git a/hotspot/src/share/vm/opto/regmask.cpp b/hotspot/src/share/vm/opto/regmask.cpp +index 352ccfb9d9..9a656d03ee 100644 +--- a/hotspot/src/share/vm/opto/regmask.cpp ++++ b/hotspot/src/share/vm/opto/regmask.cpp +@@ -22,6 +22,12 @@ + * + */ + ++/* ++ * This file has been modified by Loongson Technology in 2020. These ++ * modifications are Copyright (c) 2015, 2020, Loongson Technology, and are made ++ * available on the same license terms set forth above. ++ */ ++ + #include "precompiled.hpp" + #include "opto/compile.hpp" + #include "opto/regmask.hpp" +@@ -39,6 +45,10 @@ + # include "adfiles/ad_zero.hpp" + #elif defined TARGET_ARCH_MODEL_ppc_64 + # include "adfiles/ad_ppc_64.hpp" ++#elif defined TARGET_ARCH_MODEL_mips_64 ++# include "adfiles/ad_mips_64.hpp" ++#elif defined TARGET_ARCH_MODEL_loongarch_64 ++# include "adfiles/ad_loongarch_64.hpp" + #endif + + #define RM_SIZE _RM_SIZE /* a constant private to the class RegMask */ +diff --git a/hotspot/src/share/vm/opto/regmask.hpp b/hotspot/src/share/vm/opto/regmask.hpp +index 5ceebb3fb8..6d08b68731 100644 +--- a/hotspot/src/share/vm/opto/regmask.hpp ++++ b/hotspot/src/share/vm/opto/regmask.hpp +@@ -22,6 +22,12 @@ + * + */ + ++/* ++ * This file has been modified by Loongson Technology in 2020. These ++ * modifications are Copyright (c) 2015, 2020, Loongson Technology, and are made ++ * available on the same license terms set forth above. ++ */ ++ + #ifndef SHARE_VM_OPTO_REGMASK_HPP + #define SHARE_VM_OPTO_REGMASK_HPP + +@@ -42,6 +48,10 @@ + # include "adfiles/adGlobals_zero.hpp" + #elif defined TARGET_ARCH_MODEL_ppc_64 + # include "adfiles/adGlobals_ppc_64.hpp" ++#elif defined TARGET_ARCH_MODEL_mips_64 ++# include "adfiles/adGlobals_mips_64.hpp" ++#elif defined TARGET_ARCH_MODEL_loongarch_64 ++# include "adfiles/adGlobals_loongarch_64.hpp" + #endif + + // Some fun naming (textual) substitutions: +diff --git a/hotspot/src/share/vm/opto/runtime.cpp b/hotspot/src/share/vm/opto/runtime.cpp +index a43b37f2c5..f2bcafa2c5 100644 +--- a/hotspot/src/share/vm/opto/runtime.cpp ++++ b/hotspot/src/share/vm/opto/runtime.cpp +@@ -22,6 +22,12 @@ + * + */ + ++/* ++ * This file has been modified by Loongson Technology in 2020. These ++ * modifications are Copyright (c) 2015, 2020, Loongson Technology, and are made ++ * available on the same license terms set forth above. ++ */ ++ + #include "precompiled.hpp" + #include "classfile/systemDictionary.hpp" + #include "classfile/vmSymbols.hpp" +@@ -82,6 +88,10 @@ + # include "adfiles/ad_zero.hpp" + #elif defined TARGET_ARCH_MODEL_ppc_64 + # include "adfiles/ad_ppc_64.hpp" ++#elif defined TARGET_ARCH_MODEL_mips_64 ++# include "adfiles/ad_mips_64.hpp" ++#elif defined TARGET_ARCH_MODEL_loongarch_64 ++# include "adfiles/ad_loongarch_64.hpp" + #endif + + +diff --git a/hotspot/src/share/vm/opto/type.cpp b/hotspot/src/share/vm/opto/type.cpp +index 58572f137d..299d48b12a 100644 +--- a/hotspot/src/share/vm/opto/type.cpp ++++ b/hotspot/src/share/vm/opto/type.cpp +@@ -22,6 +22,12 @@ + * + */ + ++/* ++ * This file has been modified by Loongson Technology in 2022, These ++ * modifications are Copyright (c) 2022, Loongson Technology, and are made ++ * available on the same license terms set forth above. ++ */ ++ + #include "precompiled.hpp" + #include "ci/ciMethodData.hpp" + #include "ci/ciTypeFlow.hpp" +@@ -68,6 +74,16 @@ const Type::TypeInfo Type::_type_info[Type::lastype] = { + { Bad, T_ILLEGAL, "vectord:", false, Op_RegD, relocInfo::none }, // VectorD + { Bad, T_ILLEGAL, "vectorx:", false, 0, relocInfo::none }, // VectorX + { Bad, T_ILLEGAL, "vectory:", false, 0, relocInfo::none }, // VectorY ++#elif defined(MIPS64) ++ { Bad, T_ILLEGAL, "vectors:", false, 0, relocInfo::none }, // VectorS ++ { Bad, T_ILLEGAL, "vectord:", false, Op_VecD, relocInfo::none }, // VectorD ++ { Bad, T_ILLEGAL, "vectorx:", false, 0, relocInfo::none }, // VectorX ++ { Bad, T_ILLEGAL, "vectory:", false, 0, relocInfo::none }, // VectorY ++#elif defined(LOONGARCH64) ++ { Bad, T_ILLEGAL, "vectors:", false, 0, relocInfo::none }, // VectorS ++ { Bad, T_ILLEGAL, "vectord:", false, 0, relocInfo::none }, // VectorD ++ { Bad, T_ILLEGAL, "vectorx:", false, Op_VecX, relocInfo::none }, // VectorX ++ { Bad, T_ILLEGAL, "vectory:", false, Op_VecY, relocInfo::none }, // VectorY + #elif defined(PPC64) + { Bad, T_ILLEGAL, "vectors:", false, 0, relocInfo::none }, // VectorS + { Bad, T_ILLEGAL, "vectord:", false, Op_RegL, relocInfo::none }, // VectorD +diff --git a/hotspot/src/share/vm/prims/jniCheck.cpp b/hotspot/src/share/vm/prims/jniCheck.cpp +index 593ca8a1e3..82813b71fe 100644 +--- a/hotspot/src/share/vm/prims/jniCheck.cpp ++++ b/hotspot/src/share/vm/prims/jniCheck.cpp +@@ -22,6 +22,12 @@ + * + */ + ++/* ++ * This file has been modified by Loongson Technology in 2020. These ++ * modifications are Copyright (c) 2015, 2020, Loongson Technology, and are made ++ * available on the same license terms set forth above. ++ */ ++ + #include "precompiled.hpp" + #include "classfile/systemDictionary.hpp" + #include "classfile/vmSymbols.hpp" +@@ -55,6 +61,12 @@ + #ifdef TARGET_ARCH_ppc + # include "jniTypes_ppc.hpp" + #endif ++#ifdef TARGET_ARCH_mips ++# include "jniTypes_mips.hpp" ++#endif ++#ifdef TARGET_ARCH_loongarch ++# include "jniTypes_loongarch.hpp" ++#endif + + // Complain every extra number of unplanned local refs + #define CHECK_JNI_LOCAL_REF_CAP_WARN_THRESHOLD 32 +diff --git a/hotspot/src/share/vm/prims/jni_md.h b/hotspot/src/share/vm/prims/jni_md.h +index 6209a66449..271715d4a2 100644 +--- a/hotspot/src/share/vm/prims/jni_md.h ++++ b/hotspot/src/share/vm/prims/jni_md.h +@@ -22,6 +22,12 @@ + * or visit www.oracle.com if you need additional information or have any + * questions. + */ ++/* ++ * This file has been modified by Loongson Technology in 2020. These ++ * modifications are Copyright (c) 2015, 2020, Loongson Technology, and are made ++ * available on the same license terms set forth above. ++ */ ++ + + /* Switch to the correct jni_md.h file without reliance on -I options. */ + #ifdef TARGET_ARCH_x86 +@@ -42,6 +48,12 @@ + #ifdef TARGET_ARCH_ppc + # include "jni_ppc.h" + #endif ++#ifdef TARGET_ARCH_mips ++# include "jni_mips.h" ++#endif ++#ifdef TARGET_ARCH_loongarch ++# include "jni_loongarch.h" ++#endif + + + /* +diff --git a/hotspot/src/share/vm/prims/jvmtiClassFileReconstituter.cpp b/hotspot/src/share/vm/prims/jvmtiClassFileReconstituter.cpp +index ab31d0d91e..0d8570b764 100644 +--- a/hotspot/src/share/vm/prims/jvmtiClassFileReconstituter.cpp ++++ b/hotspot/src/share/vm/prims/jvmtiClassFileReconstituter.cpp +@@ -22,6 +22,12 @@ + * + */ + ++/* ++ * This file has been modified by Loongson Technology in 2020. These ++ * modifications are Copyright (c) 2015, 2020, Loongson Technology, and are made ++ * available on the same license terms set forth above. ++ */ ++ + #include "precompiled.hpp" + #include "classfile/symbolTable.hpp" + #include "interpreter/bytecodeStream.hpp" +@@ -46,6 +52,12 @@ + #ifdef TARGET_ARCH_ppc + # include "bytes_ppc.hpp" + #endif ++#ifdef TARGET_ARCH_mips ++# include "bytes_mips.hpp" ++#endif ++#ifdef TARGET_ARCH_loongarch ++# include "bytes_loongarch.hpp" ++#endif + // FIXME: add Deprecated attribute + // FIXME: fix Synthetic attribute + // FIXME: per Serguei, add error return handling for ConstantPool::copy_cpool_bytes() +diff --git a/hotspot/src/share/vm/prims/methodHandles.hpp b/hotspot/src/share/vm/prims/methodHandles.hpp +index db6e06180d..841082859a 100644 +--- a/hotspot/src/share/vm/prims/methodHandles.hpp ++++ b/hotspot/src/share/vm/prims/methodHandles.hpp +@@ -22,6 +22,12 @@ + * + */ + ++/* ++ * This file has been modified by Loongson Technology in 2020. These ++ * modifications are Copyright (c) 2015, 2020, Loongson Technology, and are made ++ * available on the same license terms set forth above. ++ */ ++ + #ifndef SHARE_VM_PRIMS_METHODHANDLES_HPP + #define SHARE_VM_PRIMS_METHODHANDLES_HPP + +@@ -198,6 +204,13 @@ public: + #ifdef TARGET_ARCH_ppc + # include "methodHandles_ppc.hpp" + #endif ++#ifdef TARGET_ARCH_mips ++# include "methodHandles_mips.hpp" ++#endif ++#ifdef TARGET_ARCH_loongarch ++# include "methodHandles_loongarch.hpp" ++#endif ++ + + // Tracing + static void trace_method_handle(MacroAssembler* _masm, const char* adaptername) PRODUCT_RETURN; +diff --git a/hotspot/src/share/vm/runtime/atomic.inline.hpp b/hotspot/src/share/vm/runtime/atomic.inline.hpp +index 222f29cbf4..7c7c6edb27 100644 +--- a/hotspot/src/share/vm/runtime/atomic.inline.hpp ++++ b/hotspot/src/share/vm/runtime/atomic.inline.hpp +@@ -22,6 +22,12 @@ + * + */ + ++/* ++ * This file has been modified by Loongson Technology in 2020. These ++ * modifications are Copyright (c) 2015, 2020, Loongson Technology, and are made ++ * available on the same license terms set forth above. ++ */ ++ + #ifndef SHARE_VM_RUNTIME_ATOMIC_INLINE_HPP + #define SHARE_VM_RUNTIME_ATOMIC_INLINE_HPP + +@@ -31,6 +37,12 @@ + #ifdef TARGET_OS_ARCH_linux_x86 + # include "atomic_linux_x86.inline.hpp" + #endif ++#ifdef TARGET_OS_ARCH_linux_mips ++# include "atomic_linux_mips.inline.hpp" ++#endif ++#ifdef TARGET_OS_ARCH_linux_loongarch ++# include "atomic_linux_loongarch.inline.hpp" ++#endif + #ifdef TARGET_OS_ARCH_linux_sparc + # include "atomic_linux_sparc.inline.hpp" + #endif +diff --git a/hotspot/src/share/vm/runtime/deoptimization.cpp b/hotspot/src/share/vm/runtime/deoptimization.cpp +index f91afdc416..36a924fd4f 100644 +--- a/hotspot/src/share/vm/runtime/deoptimization.cpp ++++ b/hotspot/src/share/vm/runtime/deoptimization.cpp +@@ -22,6 +22,12 @@ + * + */ + ++/* ++ * This file has been modified by Loongson Technology in 2020. These ++ * modifications are Copyright (c) 2015, 2020, Loongson Technology, and are made ++ * available on the same license terms set forth above. ++ */ ++ + #include "precompiled.hpp" + #include "classfile/systemDictionary.hpp" + #include "code/debugInfoRec.hpp" +@@ -68,6 +74,12 @@ + #ifdef TARGET_ARCH_ppc + # include "vmreg_ppc.inline.hpp" + #endif ++#ifdef TARGET_ARCH_mips ++# include "vmreg_mips.inline.hpp" ++#endif ++#ifdef TARGET_ARCH_loongarch ++# include "vmreg_loongarch.inline.hpp" ++#endif + #ifdef COMPILER2 + #if defined AD_MD_HPP + # include AD_MD_HPP +@@ -84,6 +96,12 @@ + #elif defined TARGET_ARCH_MODEL_ppc_64 + # include "adfiles/ad_ppc_64.hpp" + #endif ++#ifdef TARGET_ARCH_MODEL_mips_64 ++# include "adfiles/ad_mips_64.hpp" ++#endif ++#ifdef TARGET_ARCH_MODEL_loongarch_64 ++# include "adfiles/ad_loongarch_64.hpp" ++#endif + #endif // COMPILER2 + + PRAGMA_FORMAT_MUTE_WARNINGS_FOR_GCC +diff --git a/hotspot/src/share/vm/runtime/dtraceJSDT.hpp b/hotspot/src/share/vm/runtime/dtraceJSDT.hpp +index db568def34..490c5f5a4e 100644 +--- a/hotspot/src/share/vm/runtime/dtraceJSDT.hpp ++++ b/hotspot/src/share/vm/runtime/dtraceJSDT.hpp +@@ -22,6 +22,12 @@ + * + */ + ++/* ++ * This file has been modified by Loongson Technology in 2020. These ++ * modifications are Copyright (c) 2015, 2020, Loongson Technology, and are made ++ * available on the same license terms set forth above. ++ */ ++ + #ifndef SHARE_VM_RUNTIME_DTRACEJSDT_HPP + #define SHARE_VM_RUNTIME_DTRACEJSDT_HPP + +@@ -44,6 +50,12 @@ + #ifdef TARGET_ARCH_ppc + # include "nativeInst_ppc.hpp" + #endif ++#ifdef TARGET_ARCH_mips ++# include "nativeInst_mips.hpp" ++#endif ++#ifdef TARGET_ARCH_loongarch ++# include "nativeInst_loongarch.hpp" ++#endif + + class RegisteredProbes; + typedef jlong OpaqueProbes; +diff --git a/hotspot/src/share/vm/runtime/frame.cpp b/hotspot/src/share/vm/runtime/frame.cpp +index 338b7ad3a7..5a161133ba 100644 +--- a/hotspot/src/share/vm/runtime/frame.cpp ++++ b/hotspot/src/share/vm/runtime/frame.cpp +@@ -22,6 +22,12 @@ + * + */ + ++/* ++ * This file has been modified by Loongson Technology in 2020. These ++ * modifications are Copyright (c) 2015, 2020, Loongson Technology, and are made ++ * available on the same license terms set forth above. ++ */ ++ + #include "precompiled.hpp" + #include "compiler/abstractCompiler.hpp" + #include "compiler/disassembler.hpp" +@@ -64,6 +70,13 @@ + #ifdef TARGET_ARCH_ppc + # include "nativeInst_ppc.hpp" + #endif ++#ifdef TARGET_ARCH_mips ++# include "nativeInst_mips.hpp" ++#endif ++#ifdef TARGET_ARCH_loongarch ++# include "nativeInst_loongarch.hpp" ++#endif ++ + + PRAGMA_FORMAT_MUTE_WARNINGS_FOR_GCC + +diff --git a/hotspot/src/share/vm/runtime/frame.hpp b/hotspot/src/share/vm/runtime/frame.hpp +index 2d80ecc208..4a9e6edb54 100644 +--- a/hotspot/src/share/vm/runtime/frame.hpp ++++ b/hotspot/src/share/vm/runtime/frame.hpp +@@ -22,6 +22,12 @@ + * + */ + ++/* ++ * This file has been modified by Loongson Technology in 2020. These ++ * modifications are Copyright (c) 2015, 2020, Loongson Technology, and are made ++ * available on the same license terms set forth above. ++ */ ++ + #ifndef SHARE_VM_RUNTIME_FRAME_HPP + #define SHARE_VM_RUNTIME_FRAME_HPP + +@@ -45,6 +51,10 @@ + # include "adfiles/adGlobals_zero.hpp" + #elif defined TARGET_ARCH_MODEL_ppc_64 + # include "adfiles/adGlobals_ppc_64.hpp" ++#elif defined TARGET_ARCH_MODEL_mips_64 ++# include "adfiles/adGlobals_mips_64.hpp" ++#elif defined TARGET_ARCH_MODEL_loongarch_64 ++# include "adfiles/adGlobals_loongarch_64.hpp" + #endif + #endif // COMPILER2 + #ifdef TARGET_ARCH_zero +@@ -489,6 +499,12 @@ class frame VALUE_OBJ_CLASS_SPEC { + #ifdef TARGET_ARCH_x86 + # include "frame_x86.hpp" + #endif ++#ifdef TARGET_ARCH_mips ++# include "frame_mips.hpp" ++#endif ++#ifdef TARGET_ARCH_loongarch ++# include "frame_loongarch.hpp" ++#endif + #ifdef TARGET_ARCH_aarch64 + # include "frame_aarch64.hpp" + #endif +diff --git a/hotspot/src/share/vm/runtime/frame.inline.hpp b/hotspot/src/share/vm/runtime/frame.inline.hpp +index 710b82306a..704cc8df8f 100644 +--- a/hotspot/src/share/vm/runtime/frame.inline.hpp ++++ b/hotspot/src/share/vm/runtime/frame.inline.hpp +@@ -22,6 +22,12 @@ + * + */ + ++/* ++ * This file has been modified by Loongson Technology in 2020. These ++ * modifications are Copyright (c) 2015, 2020, Loongson Technology, and are made ++ * available on the same license terms set forth above. ++ */ ++ + #ifndef SHARE_VM_RUNTIME_FRAME_INLINE_HPP + #define SHARE_VM_RUNTIME_FRAME_INLINE_HPP + +@@ -49,6 +55,12 @@ + #ifdef TARGET_ARCH_ppc + # include "jniTypes_ppc.hpp" + #endif ++#ifdef TARGET_ARCH_mips ++# include "jniTypes_mips.hpp" ++#endif ++#ifdef TARGET_ARCH_loongarch ++# include "jniTypes_loongarch.hpp" ++#endif + #ifdef TARGET_ARCH_zero + # include "entryFrame_zero.hpp" + # include "fakeStubFrame_zero.hpp" +@@ -115,6 +127,12 @@ inline oop* frame::interpreter_frame_temp_oop_addr() const { + #ifdef TARGET_ARCH_ppc + # include "frame_ppc.inline.hpp" + #endif ++#ifdef TARGET_ARCH_mips ++# include "frame_mips.inline.hpp" ++#endif ++#ifdef TARGET_ARCH_loongarch ++# include "frame_loongarch.inline.hpp" ++#endif + + + #endif // SHARE_VM_RUNTIME_FRAME_INLINE_HPP +diff --git a/hotspot/src/share/vm/runtime/globals.hpp b/hotspot/src/share/vm/runtime/globals.hpp +index 23ce8af569..f36137aabf 100644 +--- a/hotspot/src/share/vm/runtime/globals.hpp ++++ b/hotspot/src/share/vm/runtime/globals.hpp +@@ -55,6 +55,12 @@ + #ifdef TARGET_ARCH_ppc + # include "globals_ppc.hpp" + #endif ++#ifdef TARGET_ARCH_mips ++# include "globals_mips.hpp" ++#endif ++#ifdef TARGET_ARCH_loongarch ++# include "globals_loongarch.hpp" ++#endif + #ifdef TARGET_OS_FAMILY_linux + # include "globals_linux.hpp" + #endif +@@ -79,6 +85,12 @@ + #ifdef TARGET_OS_ARCH_linux_sparc + # include "globals_linux_sparc.hpp" + #endif ++#ifdef TARGET_OS_ARCH_linux_mips ++# include "globals_linux_mips.hpp" ++#endif ++#ifdef TARGET_OS_ARCH_linux_loongarch ++# include "globals_linux_loongarch.hpp" ++#endif + #ifdef TARGET_OS_ARCH_linux_zero + # include "globals_linux_zero.hpp" + #endif +@@ -116,6 +128,12 @@ + #ifdef TARGET_ARCH_sparc + # include "c1_globals_sparc.hpp" + #endif ++#ifdef TARGET_ARCH_mips ++# include "c1_globals_mips.hpp" ++#endif ++#ifdef TARGET_ARCH_loongarch ++# include "c1_globals_loongarch.hpp" ++#endif + #ifdef TARGET_ARCH_arm + # include "c1_globals_arm.hpp" + #endif +@@ -148,6 +166,12 @@ + #ifdef TARGET_ARCH_sparc + # include "c2_globals_sparc.hpp" + #endif ++#ifdef TARGET_ARCH_mips ++# include "c2_globals_mips.hpp" ++#endif ++#ifdef TARGET_ARCH_loongarch ++# include "c2_globals_loongarch.hpp" ++#endif + #ifdef TARGET_ARCH_arm + # include "c2_globals_arm.hpp" + #endif +@@ -3209,7 +3233,7 @@ class CommandLineFlags { + product(uintx, InitialHeapSize, 0, \ + "Initial heap size (in bytes); zero means use ergonomics") \ + \ +- product(uintx, MaxHeapSize, ScaleForWordSize(96*M), \ ++ product(uintx, MaxHeapSize, ScaleForWordSize(MIPS64_ONLY(1500) NOT_MIPS64(96) *M), \ + "Maximum heap size (in bytes)") \ + \ + product(uintx, OldSize, ScaleForWordSize(4*M), \ +diff --git a/hotspot/src/share/vm/runtime/icache.hpp b/hotspot/src/share/vm/runtime/icache.hpp +index ba81a06ff5..9c0cfdb7d7 100644 +--- a/hotspot/src/share/vm/runtime/icache.hpp ++++ b/hotspot/src/share/vm/runtime/icache.hpp +@@ -22,6 +22,12 @@ + * + */ + ++/* ++ * This file has been modified by Loongson Technology in 2020. These ++ * modifications are Copyright (c) 2015, 2020, Loongson Technology, and are made ++ * available on the same license terms set forth above. ++ */ ++ + #ifndef SHARE_VM_RUNTIME_ICACHE_HPP + #define SHARE_VM_RUNTIME_ICACHE_HPP + +@@ -86,7 +92,12 @@ class AbstractICache : AllStatic { + #ifdef TARGET_ARCH_ppc + # include "icache_ppc.hpp" + #endif +- ++#ifdef TARGET_ARCH_mips ++# include "icache_mips.hpp" ++#endif ++#ifdef TARGET_ARCH_loongarch ++# include "icache_loongarch.hpp" ++#endif + + + class ICacheStubGenerator : public StubCodeGenerator { +diff --git a/hotspot/src/share/vm/runtime/java.cpp b/hotspot/src/share/vm/runtime/java.cpp +index 0a263b017c..9ba0decaae 100644 +--- a/hotspot/src/share/vm/runtime/java.cpp ++++ b/hotspot/src/share/vm/runtime/java.cpp +@@ -22,6 +22,12 @@ + * + */ + ++/* ++ * This file has been modified by Loongson Technology in 2020. These ++ * modifications are Copyright (c) 2015, 2020, Loongson Technology, and are made ++ * available on the same license terms set forth above. ++ */ ++ + #include "precompiled.hpp" + #include "classfile/classLoader.hpp" + #include "classfile/symbolTable.hpp" +@@ -84,6 +90,12 @@ + #ifdef TARGET_ARCH_ppc + # include "vm_version_ppc.hpp" + #endif ++#ifdef TARGET_ARCH_mips ++# include "vm_version_mips.hpp" ++#endif ++#ifdef TARGET_ARCH_loongarch ++# include "vm_version_loongarch.hpp" ++#endif + #if INCLUDE_ALL_GCS + #include "gc_implementation/concurrentMarkSweep/concurrentMarkSweepThread.hpp" + #include "gc_implementation/parallelScavenge/psScavenge.hpp" +diff --git a/hotspot/src/share/vm/runtime/javaCalls.hpp b/hotspot/src/share/vm/runtime/javaCalls.hpp +index 6126bbe75e..1747e2b2ee 100644 +--- a/hotspot/src/share/vm/runtime/javaCalls.hpp ++++ b/hotspot/src/share/vm/runtime/javaCalls.hpp +@@ -22,6 +22,12 @@ + * + */ + ++/* ++ * This file has been modified by Loongson Technology in 2020. These ++ * modifications are Copyright (c) 2015, 2020, Loongson Technology, and are made ++ * available on the same license terms set forth above. ++ */ ++ + #ifndef SHARE_VM_RUNTIME_JAVACALLS_HPP + #define SHARE_VM_RUNTIME_JAVACALLS_HPP + +@@ -49,6 +55,12 @@ + #ifdef TARGET_ARCH_ppc + # include "jniTypes_ppc.hpp" + #endif ++#ifdef TARGET_ARCH_mips ++# include "jniTypes_mips.hpp" ++#endif ++#ifdef TARGET_ARCH_loongarch ++# include "jniTypes_loongarch.hpp" ++#endif + + // A JavaCallWrapper is constructed before each JavaCall and destructed after the call. + // Its purpose is to allocate/deallocate a new handle block and to save/restore the last +diff --git a/hotspot/src/share/vm/runtime/javaFrameAnchor.hpp b/hotspot/src/share/vm/runtime/javaFrameAnchor.hpp +index 129a01e293..c2b1b2e6c3 100644 +--- a/hotspot/src/share/vm/runtime/javaFrameAnchor.hpp ++++ b/hotspot/src/share/vm/runtime/javaFrameAnchor.hpp +@@ -22,6 +22,12 @@ + * + */ + ++/* ++ * This file has been modified by Loongson Technology in 2020. These ++ * modifications are Copyright (c) 2015, 2020, Loongson Technology, and are made ++ * available on the same license terms set forth above. ++ */ ++ + #ifndef SHARE_VM_RUNTIME_JAVAFRAMEANCHOR_HPP + #define SHARE_VM_RUNTIME_JAVAFRAMEANCHOR_HPP + +@@ -80,6 +86,12 @@ friend class JavaCallWrapper; + #ifdef TARGET_ARCH_x86 + # include "javaFrameAnchor_x86.hpp" + #endif ++#ifdef TARGET_ARCH_mips ++# include "javaFrameAnchor_mips.hpp" ++#endif ++#ifdef TARGET_ARCH_loongarch ++# include "javaFrameAnchor_loongarch.hpp" ++#endif + #ifdef TARGET_ARCH_aarch64 + # include "javaFrameAnchor_aarch64.hpp" + #endif +diff --git a/hotspot/src/share/vm/runtime/os.cpp b/hotspot/src/share/vm/runtime/os.cpp +index 96eed03670..28c78409e7 100644 +--- a/hotspot/src/share/vm/runtime/os.cpp ++++ b/hotspot/src/share/vm/runtime/os.cpp +@@ -1122,7 +1122,8 @@ bool os::is_first_C_frame(frame* fr) { + + uintptr_t old_fp = (uintptr_t)fr->link(); + if ((old_fp & fp_align_mask) != 0) return true; +- if (old_fp == 0 || old_fp == (uintptr_t)-1 || old_fp == ufp) return true; ++ // The check for old_fp and ufp is harmful on MIPS due to its special ABI. ++ if (old_fp == 0 || old_fp == (uintptr_t)-1 NOT_MIPS64(|| old_fp == ufp)) return true; + + // stack grows downwards; if old_fp is below current fp or if the stack + // frame is too large, either the stack is corrupted or fp is not saved +diff --git a/hotspot/src/share/vm/runtime/os.hpp b/hotspot/src/share/vm/runtime/os.hpp +index 836c231b03..0ca6e64598 100644 +--- a/hotspot/src/share/vm/runtime/os.hpp ++++ b/hotspot/src/share/vm/runtime/os.hpp +@@ -22,6 +22,12 @@ + * + */ + ++/* ++ * This file has been modified by Loongson Technology in 2020. These ++ * modifications are Copyright (c) 2015, 2020, Loongson Technology, and are made ++ * available on the same license terms set forth above. ++ */ ++ + #ifndef SHARE_VM_RUNTIME_OS_HPP + #define SHARE_VM_RUNTIME_OS_HPP + +@@ -857,6 +863,12 @@ class os: AllStatic { + #ifdef TARGET_OS_ARCH_linux_x86 + # include "os_linux_x86.hpp" + #endif ++#ifdef TARGET_OS_ARCH_linux_mips ++# include "os_linux_mips.hpp" ++#endif ++#ifdef TARGET_OS_ARCH_linux_loongarch ++# include "os_linux_loongarch.hpp" ++#endif + #ifdef TARGET_OS_ARCH_linux_aarch64 + # include "os_linux_aarch64.hpp" + #endif +diff --git a/hotspot/src/share/vm/runtime/prefetch.inline.hpp b/hotspot/src/share/vm/runtime/prefetch.inline.hpp +index f4e30de34d..fec16f842c 100644 +--- a/hotspot/src/share/vm/runtime/prefetch.inline.hpp ++++ b/hotspot/src/share/vm/runtime/prefetch.inline.hpp +@@ -46,6 +46,12 @@ + #ifdef TARGET_OS_ARCH_linux_ppc + # include "prefetch_linux_ppc.inline.hpp" + #endif ++#ifdef TARGET_OS_ARCH_linux_mips ++# include "prefetch_linux_mips.inline.hpp" ++#endif ++#ifdef TARGET_OS_ARCH_linux_loongarch ++# include "prefetch_linux_loongarch.inline.hpp" ++#endif + + // Solaris + #ifdef TARGET_OS_ARCH_solaris_x86 +diff --git a/hotspot/src/share/vm/runtime/registerMap.hpp b/hotspot/src/share/vm/runtime/registerMap.hpp +index 67ef212d65..1e26dfcba4 100644 +--- a/hotspot/src/share/vm/runtime/registerMap.hpp ++++ b/hotspot/src/share/vm/runtime/registerMap.hpp +@@ -22,6 +22,12 @@ + * + */ + ++/* ++ * This file has been modified by Loongson Technology in 2020. These ++ * modifications are Copyright (c) 2015, 2020, Loongson Technology, and are made ++ * available on the same license terms set forth above. ++ */ ++ + #ifndef SHARE_VM_RUNTIME_REGISTERMAP_HPP + #define SHARE_VM_RUNTIME_REGISTERMAP_HPP + +@@ -45,6 +51,12 @@ + #ifdef TARGET_ARCH_ppc + # include "register_ppc.hpp" + #endif ++#ifdef TARGET_ARCH_mips ++# include "register_mips.hpp" ++#endif ++#ifdef TARGET_ARCH_loongarch ++# include "register_loongarch.hpp" ++#endif + + class JavaThread; + +@@ -156,6 +168,12 @@ class RegisterMap : public StackObj { + #ifdef TARGET_ARCH_ppc + # include "registerMap_ppc.hpp" + #endif ++#ifdef TARGET_ARCH_mips ++# include "registerMap_mips.hpp" ++#endif ++#ifdef TARGET_ARCH_loongarch ++# include "registerMap_loongarch.hpp" ++#endif + + }; + +diff --git a/hotspot/src/share/vm/runtime/relocator.hpp b/hotspot/src/share/vm/runtime/relocator.hpp +index bb19c75fe6..53f3c9f6bd 100644 +--- a/hotspot/src/share/vm/runtime/relocator.hpp ++++ b/hotspot/src/share/vm/runtime/relocator.hpp +@@ -22,6 +22,12 @@ + * + */ + ++/* ++ * This file has been modified by Loongson Technology in 2020. These ++ * modifications are Copyright (c) 2015, 2020, Loongson Technology, and are made ++ * available on the same license terms set forth above. ++ */ ++ + #ifndef SHARE_VM_RUNTIME_RELOCATOR_HPP + #define SHARE_VM_RUNTIME_RELOCATOR_HPP + +@@ -45,6 +51,12 @@ + #ifdef TARGET_ARCH_ppc + # include "bytes_ppc.hpp" + #endif ++#ifdef TARGET_ARCH_mips ++# include "bytes_mips.hpp" ++#endif ++#ifdef TARGET_ARCH_loongarch ++# include "bytes_loongarch.hpp" ++#endif + + // This code has been converted from the 1.1E java virtual machine + // Thanks to the JavaTopics group for using the code +diff --git a/hotspot/src/share/vm/runtime/safepoint.cpp b/hotspot/src/share/vm/runtime/safepoint.cpp +index 440617c802..be0e4dd13c 100644 +--- a/hotspot/src/share/vm/runtime/safepoint.cpp ++++ b/hotspot/src/share/vm/runtime/safepoint.cpp +@@ -22,6 +22,12 @@ + * + */ + ++/* ++ * This file has been modified by Loongson Technology in 2020. These ++ * modifications are Copyright (c) 2015, 2020, Loongson Technology, and are made ++ * available on the same license terms set forth above. ++ */ ++ + #include "precompiled.hpp" + #include "classfile/symbolTable.hpp" + #include "classfile/systemDictionary.hpp" +@@ -78,6 +84,14 @@ + # include "nativeInst_ppc.hpp" + # include "vmreg_ppc.inline.hpp" + #endif ++#ifdef TARGET_ARCH_mips ++# include "nativeInst_mips.hpp" ++# include "vmreg_mips.inline.hpp" ++#endif ++#ifdef TARGET_ARCH_loongarch ++# include "nativeInst_loongarch.hpp" ++# include "vmreg_loongarch.inline.hpp" ++#endif + #if INCLUDE_ALL_GCS + #include "gc_implementation/concurrentMarkSweep/concurrentMarkSweepThread.hpp" + #include "gc_implementation/shared/suspendibleThreadSet.hpp" +diff --git a/hotspot/src/share/vm/runtime/sharedRuntime.cpp b/hotspot/src/share/vm/runtime/sharedRuntime.cpp +index 5f540247f9..abcd6066b9 100644 +--- a/hotspot/src/share/vm/runtime/sharedRuntime.cpp ++++ b/hotspot/src/share/vm/runtime/sharedRuntime.cpp +@@ -22,6 +22,12 @@ + * + */ + ++/* ++ * This file has been modified by Loongson Technology in 2020. These ++ * modifications are Copyright (c) 2015, 2020, Loongson Technology, and are made ++ * available on the same license terms set forth above. ++ */ ++ + #include "precompiled.hpp" + #include "classfile/systemDictionary.hpp" + #include "classfile/vmSymbols.hpp" +@@ -82,6 +88,15 @@ + # include "nativeInst_ppc.hpp" + # include "vmreg_ppc.inline.hpp" + #endif ++#ifdef TARGET_ARCH_mips ++# include "nativeInst_mips.hpp" ++# include "vmreg_mips.inline.hpp" ++#endif ++#ifdef TARGET_ARCH_loongarch ++# include "nativeInst_loongarch.hpp" ++# include "vmreg_loongarch.inline.hpp" ++#endif ++ + #ifdef COMPILER1 + #include "c1/c1_Runtime1.hpp" + #endif +@@ -220,7 +235,6 @@ void SharedRuntime::print_ic_miss_histogram() { + } + } + #endif // PRODUCT +- + #if INCLUDE_ALL_GCS + + // G1 write-barrier pre: executed before a pointer store. +diff --git a/hotspot/src/share/vm/runtime/sharedRuntimeTrig.cpp b/hotspot/src/share/vm/runtime/sharedRuntimeTrig.cpp +index 37880d8a5c..3987880b16 100644 +--- a/hotspot/src/share/vm/runtime/sharedRuntimeTrig.cpp ++++ b/hotspot/src/share/vm/runtime/sharedRuntimeTrig.cpp +@@ -22,6 +22,12 @@ + * + */ + ++/* ++ * This file has been modified by Loongson Technology in 2020, These ++ * modifications are Copyright (c) 2015, 2020, Loongson Technology, and are made ++ * available on the same license terms set forth above. ++ */ ++ + #include "precompiled.hpp" + #include "prims/jni.h" + #include "runtime/interfaceSupport.hpp" +@@ -534,6 +540,15 @@ static SAFEBUF int __ieee754_rem_pio2(double x, double *y) { + * then 3 2 + * sin(x) = x + (S1*x + (x *(r-y/2)+y)) + */ ++#if defined(MIPS) || defined(LOONGARCH) ++// TODO: LA ++#undef S1 ++#undef S2 ++#undef S3 ++#undef S4 ++#undef S5 ++#undef S6 ++#endif + + static const double + S1 = -1.66666666666666324348e-01, /* 0xBFC55555, 0x55555549 */ +diff --git a/hotspot/src/share/vm/runtime/stackValueCollection.cpp b/hotspot/src/share/vm/runtime/stackValueCollection.cpp +index 8774768311..fe81c1bfd8 100644 +--- a/hotspot/src/share/vm/runtime/stackValueCollection.cpp ++++ b/hotspot/src/share/vm/runtime/stackValueCollection.cpp +@@ -22,6 +22,12 @@ + * + */ + ++/* ++ * This file has been modified by Loongson Technology in 2020. These ++ * modifications are Copyright (c) 2015, 2020, Loongson Technology, and are made ++ * available on the same license terms set forth above. ++ */ ++ + #include "precompiled.hpp" + #include "runtime/stackValueCollection.hpp" + #ifdef TARGET_ARCH_x86 +@@ -42,6 +48,12 @@ + #ifdef TARGET_ARCH_ppc + # include "jniTypes_ppc.hpp" + #endif ++#ifdef TARGET_ARCH_mips ++# include "jniTypes_mips.hpp" ++#endif ++#ifdef TARGET_ARCH_loongarch ++# include "jniTypes_loongarch.hpp" ++#endif + + PRAGMA_FORMAT_MUTE_WARNINGS_FOR_GCC + +diff --git a/hotspot/src/share/vm/runtime/statSampler.cpp b/hotspot/src/share/vm/runtime/statSampler.cpp +index 41f469622f..3b43089062 100644 +--- a/hotspot/src/share/vm/runtime/statSampler.cpp ++++ b/hotspot/src/share/vm/runtime/statSampler.cpp +@@ -22,6 +22,12 @@ + * + */ + ++/* ++ * This file has been modified by Loongson Technology in 2020. These ++ * modifications are Copyright (c) 2015, 2020 Loongson Technology, and are made ++ * available on the same license terms set forth above. ++ */ ++ + #include "precompiled.hpp" + #include "classfile/systemDictionary.hpp" + #include "classfile/vmSymbols.hpp" +@@ -51,6 +57,12 @@ + #ifdef TARGET_ARCH_ppc + # include "vm_version_ppc.hpp" + #endif ++#ifdef TARGET_ARCH_mips ++# include "vm_version_mips.hpp" ++#endif ++#ifdef TARGET_ARCH_loongarch ++# include "vm_version_loongarch.hpp" ++#endif + + // -------------------------------------------------------- + // StatSamplerTask +diff --git a/hotspot/src/share/vm/runtime/stubRoutines.hpp b/hotspot/src/share/vm/runtime/stubRoutines.hpp +index e18b9127df..9bf933762a 100644 +--- a/hotspot/src/share/vm/runtime/stubRoutines.hpp ++++ b/hotspot/src/share/vm/runtime/stubRoutines.hpp +@@ -22,6 +22,12 @@ + * + */ + ++/* ++ * This file has been modified by Loongson Technology in 2020. These ++ * modifications are Copyright (c) 2015, 2020, Loongson Technology, and are made ++ * available on the same license terms set forth above. ++ */ ++ + #ifndef SHARE_VM_RUNTIME_STUBROUTINES_HPP + #define SHARE_VM_RUNTIME_STUBROUTINES_HPP + +@@ -49,6 +55,12 @@ + #ifdef TARGET_ARCH_ppc + # include "nativeInst_ppc.hpp" + #endif ++#ifdef TARGET_ARCH_mips ++# include "nativeInst_mips.hpp" ++#endif ++#ifdef TARGET_ARCH_loongarch ++# include "nativeInst_loongarch.hpp" ++#endif + + // StubRoutines provides entry points to assembly routines used by + // compiled code and the run-time system. Platform-specific entry +@@ -116,6 +128,10 @@ class StubRoutines: AllStatic { + # include "stubRoutines_zero.hpp" + #elif defined TARGET_ARCH_MODEL_ppc_64 + # include "stubRoutines_ppc_64.hpp" ++#elif defined TARGET_ARCH_MODEL_mips_64 ++# include "stubRoutines_mips_64.hpp" ++#elif defined TARGET_ARCH_MODEL_loongarch_64 ++# include "stubRoutines_loongarch_64.hpp" + #endif + + static jint _verify_oop_count; +diff --git a/hotspot/src/share/vm/runtime/thread.cpp b/hotspot/src/share/vm/runtime/thread.cpp +index 3df082fd06..ff726866c7 100644 +--- a/hotspot/src/share/vm/runtime/thread.cpp ++++ b/hotspot/src/share/vm/runtime/thread.cpp +@@ -22,6 +22,12 @@ + * + */ + ++/* ++ * This file has been modified by Loongson Technology in 2020. These ++ * modifications are Copyright (c) 2015, 2020, Loongson Technology, and are made ++ * available on the same license terms set forth above. ++ */ ++ + #include "precompiled.hpp" + #include "classfile/classLoader.hpp" + #include "classfile/javaClasses.hpp" +diff --git a/hotspot/src/share/vm/runtime/thread.hpp b/hotspot/src/share/vm/runtime/thread.hpp +index fcd4814ffd..b49e03f629 100644 +--- a/hotspot/src/share/vm/runtime/thread.hpp ++++ b/hotspot/src/share/vm/runtime/thread.hpp +@@ -22,6 +22,12 @@ + * + */ + ++/* ++ * This file has been modified by Loongson Technology in 2020. These ++ * modifications are Copyright (c) 2015, 2020, Loongson Technology, and are made ++ * available on the same license terms set forth above. ++ */ ++ + #ifndef SHARE_VM_RUNTIME_THREAD_HPP + #define SHARE_VM_RUNTIME_THREAD_HPP + +@@ -1711,6 +1717,12 @@ public: + #ifdef TARGET_OS_ARCH_linux_x86 + # include "thread_linux_x86.hpp" + #endif ++#ifdef TARGET_OS_ARCH_linux_mips ++# include "thread_linux_mips.hpp" ++#endif ++#ifdef TARGET_OS_ARCH_linux_loongarch ++# include "thread_linux_loongarch.hpp" ++#endif + #ifdef TARGET_OS_ARCH_linux_aarch64 + # include "thread_linux_aarch64.hpp" + #endif +diff --git a/hotspot/src/share/vm/runtime/threadLocalStorage.hpp b/hotspot/src/share/vm/runtime/threadLocalStorage.hpp +index 58c1afc810..0938b2edda 100644 +--- a/hotspot/src/share/vm/runtime/threadLocalStorage.hpp ++++ b/hotspot/src/share/vm/runtime/threadLocalStorage.hpp +@@ -22,6 +22,12 @@ + * + */ + ++/* ++ * This file has been modified by Loongson Technology in 2020. These ++ * modifications are Copyright (c) 2015, 2020, Loongson Technology, and are made ++ * available on the same license terms set forth above. ++ */ ++ + #ifndef SHARE_VM_RUNTIME_THREADLOCALSTORAGE_HPP + #define SHARE_VM_RUNTIME_THREADLOCALSTORAGE_HPP + +@@ -51,6 +57,12 @@ class ThreadLocalStorage : AllStatic { + #ifdef TARGET_OS_ARCH_linux_x86 + # include "threadLS_linux_x86.hpp" + #endif ++#ifdef TARGET_OS_ARCH_linux_mips ++# include "threadLS_linux_mips.hpp" ++#endif ++#ifdef TARGET_OS_ARCH_linux_loongarch ++# include "threadLS_linux_loongarch.hpp" ++#endif + #ifdef TARGET_OS_ARCH_linux_aarch64 + # include "threadLS_linux_aarch64.hpp" + #endif +diff --git a/hotspot/src/share/vm/runtime/virtualspace.cpp b/hotspot/src/share/vm/runtime/virtualspace.cpp +index 66392b75f1..5ced38d838 100644 +--- a/hotspot/src/share/vm/runtime/virtualspace.cpp ++++ b/hotspot/src/share/vm/runtime/virtualspace.cpp +@@ -1,5 +1,6 @@ + /* + * Copyright (c) 1997, 2014, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2015, 2021, Loongson Technology. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it +@@ -147,6 +148,15 @@ void ReservedSpace::initialize(size_t size, size_t alignment, bool large, + bool special = large && !os::can_commit_large_page_memory(); + char* base = NULL; + ++#if defined MIPS && !defined ZERO ++ size_t opt_reg_addr = 5 * os::Linux::page_size(); ++ static int code_cache_init_flag = 1; ++ if (UseCodeCacheAllocOpt && code_cache_init_flag && executable) { ++ code_cache_init_flag = 0; ++ requested_address = (char*) opt_reg_addr; ++ } ++#endif ++ + if (requested_address != 0) { + requested_address -= noaccess_prefix; // adjust requested address + assert(requested_address != NULL, "huge noaccess prefix?"); +@@ -193,6 +203,12 @@ void ReservedSpace::initialize(size_t size, size_t alignment, bool large, + if (failed_to_reserve_as_requested(base, requested_address, size, false)) { + // OS ignored requested address. Try different address. + base = NULL; ++#if defined MIPS && !defined ZERO ++ if (UseCodeCacheAllocOpt && requested_address == (char*) opt_reg_addr) { ++ requested_address = NULL; ++ base = os::reserve_memory(size, NULL, alignment); ++ } ++#endif + } + } else { + base = os::reserve_memory(size, NULL, alignment); +diff --git a/hotspot/src/share/vm/runtime/vmStructs.cpp b/hotspot/src/share/vm/runtime/vmStructs.cpp +index 32e3921b2b..c6cc4c4329 100644 +--- a/hotspot/src/share/vm/runtime/vmStructs.cpp ++++ b/hotspot/src/share/vm/runtime/vmStructs.cpp +@@ -22,6 +22,12 @@ + * + */ + ++/* ++ * This file has been modified by Loongson Technology in 2020. These ++ * modifications are Copyright (c) 2015, 2020, Loongson Technology, and are made ++ * available on the same license terms set forth above. ++ */ ++ + #include "precompiled.hpp" + #include "classfile/dictionary.hpp" + #include "classfile/javaClasses.hpp" +@@ -122,6 +128,12 @@ + #ifdef TARGET_ARCH_ppc + # include "vmStructs_ppc.hpp" + #endif ++#ifdef TARGET_ARCH_mips ++# include "vmStructs_mips.hpp" ++#endif ++#ifdef TARGET_ARCH_loongarch ++# include "vmStructs_loongarch.hpp" ++#endif + #ifdef TARGET_OS_ARCH_linux_x86 + # include "vmStructs_linux_x86.hpp" + #endif +@@ -149,6 +161,12 @@ + #ifdef TARGET_OS_ARCH_linux_ppc + # include "vmStructs_linux_ppc.hpp" + #endif ++#ifdef TARGET_OS_ARCH_linux_mips ++# include "vmStructs_linux_mips.hpp" ++#endif ++#ifdef TARGET_OS_ARCH_linux_loongarch ++# include "vmStructs_linux_loongarch.hpp" ++#endif + #ifdef TARGET_OS_ARCH_aix_ppc + # include "vmStructs_aix_ppc.hpp" + #endif +@@ -208,6 +226,10 @@ + # include "adfiles/adGlobals_zero.hpp" + #elif defined TARGET_ARCH_MODEL_ppc_64 + # include "adfiles/adGlobals_ppc_64.hpp" ++#elif defined TARGET_ARCH_MODEL_mips_64 ++# include "adfiles/adGlobals_mips_64.hpp" ++#elif defined TARGET_ARCH_MODEL_loongarch_64 ++# include "adfiles/adGlobals_loongarch_64.hpp" + #endif + #endif // COMPILER2 + +diff --git a/hotspot/src/share/vm/runtime/vm_version.cpp b/hotspot/src/share/vm/runtime/vm_version.cpp +index 4c61570ee8..56613e7620 100644 +--- a/hotspot/src/share/vm/runtime/vm_version.cpp ++++ b/hotspot/src/share/vm/runtime/vm_version.cpp +@@ -22,6 +22,12 @@ + * + */ + ++/* ++ * This file has been modified by Loongson Technology in 2020. These ++ * modifications are Copyright (c) 2015, 2020, Loongson Technology, and are made ++ * available on the same license terms set forth above. ++ */ ++ + #include "precompiled.hpp" + #include "memory/universe.hpp" + #include "oops/oop.inline.hpp" +@@ -44,6 +50,12 @@ + #ifdef TARGET_ARCH_ppc + # include "vm_version_ppc.hpp" + #endif ++#ifdef TARGET_ARCH_mips ++# include "vm_version_mips.hpp" ++#endif ++#ifdef TARGET_ARCH_loongarch ++# include "vm_version_loongarch.hpp" ++#endif + + const char* Abstract_VM_Version::_s_vm_release = Abstract_VM_Version::vm_release(); + const char* Abstract_VM_Version::_s_internal_vm_info_string = Abstract_VM_Version::internal_vm_info_string(); +@@ -193,6 +205,14 @@ const char* Abstract_VM_Version::jre_release_version() { + #else + #define CPU "ppc64" + #endif ++#elif defined(MIPS64) ++#if defined(VM_LITTLE_ENDIAN) ++#define CPU "mips64el" ++#else ++#define CPU "mips64" ++#endif ++#elif defined(LOONGARCH64) ++#define CPU "loongarch64" + #else + #define CPU IA32_ONLY("x86") \ + IA64_ONLY("ia64") \ +diff --git a/hotspot/src/share/vm/utilities/copy.hpp b/hotspot/src/share/vm/utilities/copy.hpp +index c1d82c7083..1279319a17 100644 +--- a/hotspot/src/share/vm/utilities/copy.hpp ++++ b/hotspot/src/share/vm/utilities/copy.hpp +@@ -22,6 +22,12 @@ + * + */ + ++/* ++ * This file has been modified by Loongson Technology in 2020. These ++ * modifications are Copyright (c) 2015, 2020, Loongson Technology, and are made ++ * available on the same license terms set forth above. ++ */ ++ + #ifndef SHARE_VM_UTILITIES_COPY_HPP + #define SHARE_VM_UTILITIES_COPY_HPP + +@@ -331,6 +337,27 @@ class Copy : AllStatic { + #endif + } + ++ ++ // SAPJVM AS 2011-09-20. Template for atomic copy. ++ template static void copy_conjoint_atomic(T* from, T* to, size_t count) ++ { ++ if (from > to) { ++ while (count-- > 0) { ++ // Copy forwards ++ *to++ = *from++; ++ } ++ } else { ++ from += count - 1; ++ to += count - 1; ++ while (count-- > 0) { ++ // Copy backwards ++ *to-- = *from--; ++ } ++ } ++ } ++ ++ ++ + // Platform dependent implementations of the above methods. + #ifdef TARGET_ARCH_x86 + # include "copy_x86.hpp" +@@ -350,6 +377,13 @@ class Copy : AllStatic { + #ifdef TARGET_ARCH_ppc + # include "copy_ppc.hpp" + #endif ++#ifdef TARGET_ARCH_mips ++# include "copy_mips.hpp" ++#endif ++#ifdef TARGET_ARCH_loongarch ++# include "copy_loongarch.hpp" ++#endif ++ + + }; + +diff --git a/hotspot/src/share/vm/utilities/debug.cpp b/hotspot/src/share/vm/utilities/debug.cpp +index 58a32a2b83..1026585f84 100644 +--- a/hotspot/src/share/vm/utilities/debug.cpp ++++ b/hotspot/src/share/vm/utilities/debug.cpp +@@ -690,6 +690,7 @@ void help() { + tty->print_cr(" pns($sp, $ebp, $pc) on Linux/x86 or"); + tty->print_cr(" pns($sp, $fp, $pc) on Linux/AArch64 or"); + tty->print_cr(" pns($sp, 0, $pc) on Linux/ppc64 or"); ++ tty->print_cr(" pns($sp, $s8, $pc) on Linux/mips or"); + tty->print_cr(" pns($sp + 0x7ff, 0, $pc) on Solaris/SPARC"); + tty->print_cr(" - in gdb do 'set overload-resolution off' before calling pns()"); + tty->print_cr(" - in dbx do 'frame 1' before calling pns()"); +diff --git a/hotspot/src/share/vm/utilities/globalDefinitions.hpp b/hotspot/src/share/vm/utilities/globalDefinitions.hpp +index 81866b8409..61fc0c48a2 100644 +--- a/hotspot/src/share/vm/utilities/globalDefinitions.hpp ++++ b/hotspot/src/share/vm/utilities/globalDefinitions.hpp +@@ -22,6 +22,12 @@ + * + */ + ++/* ++ * This file has been modified by Loongson Technology in 2020. These ++ * modifications are Copyright (c) 2015, 2020, Loongson Technology, and are made ++ * available on the same license terms set forth above. ++ */ ++ + #ifndef SHARE_VM_UTILITIES_GLOBALDEFINITIONS_HPP + #define SHARE_VM_UTILITIES_GLOBALDEFINITIONS_HPP + +@@ -455,6 +461,12 @@ enum RTMState { + #ifdef TARGET_ARCH_ppc + # include "globalDefinitions_ppc.hpp" + #endif ++#ifdef TARGET_ARCH_mips ++# include "globalDefinitions_mips.hpp" ++#endif ++#ifdef TARGET_ARCH_loongarch ++# include "globalDefinitions_loongarch.hpp" ++#endif + + /* + * If a platform does not support native stack walking +diff --git a/hotspot/src/share/vm/utilities/macros.hpp b/hotspot/src/share/vm/utilities/macros.hpp +index 53cc46bee7..d9d147c474 100644 +--- a/hotspot/src/share/vm/utilities/macros.hpp ++++ b/hotspot/src/share/vm/utilities/macros.hpp +@@ -22,6 +22,12 @@ + * + */ + ++/* ++ * This file has been modified by Loongson Technology in 2020. These ++ * modifications are Copyright (c) 2015, 2020, Loongson Technology, and are made ++ * available on the same license terms set forth above. ++ */ ++ + #ifndef SHARE_VM_UTILITIES_MACROS_HPP + #define SHARE_VM_UTILITIES_MACROS_HPP + +@@ -370,6 +376,30 @@ + #define NOT_SPARC(code) code + #endif + ++#ifdef MIPS64 ++#ifndef MIPS ++#define MIPS ++#endif ++#define MIPS64_ONLY(code) code ++#define NOT_MIPS64(code) ++#else ++#undef MIPS ++#define MIPS64_ONLY(code) ++#define NOT_MIPS64(code) code ++#endif ++ ++#ifdef LOONGARCH64 ++#ifndef LOONGARCH ++#define LOONGARCH ++#endif ++#define LOONGARCH64_ONLY(code) code ++#define NOT_LOONGARCH64(code) ++#else ++#undef LOONGARCH ++#define LOONGARCH64_ONLY(code) ++#define NOT_LOONGARCH64(code) code ++#endif ++ + #if defined(PPC32) || defined(PPC64) + #ifndef PPC + #define PPC +diff --git a/hotspot/src/share/vm/utilities/taskqueue.hpp b/hotspot/src/share/vm/utilities/taskqueue.hpp +index bc06caccb4..46be35a325 100644 +--- a/hotspot/src/share/vm/utilities/taskqueue.hpp ++++ b/hotspot/src/share/vm/utilities/taskqueue.hpp +@@ -121,11 +121,22 @@ protected: + Age(const Age& age) { _data = age._data; } + Age(idx_t top, idx_t tag) { _fields._top = top; _fields._tag = tag; } + ++#if !defined MIPS && !defined LOONGARCH + Age get() const volatile { return _data; } + void set(Age age) volatile { _data = age._data; } + + idx_t top() const volatile { return _fields._top; } + idx_t tag() const volatile { return _fields._tag; } ++#else ++ Age get() const volatile { ++ size_t res = OrderAccess::load_ptr_acquire((volatile intptr_t*) &_data); ++ return *(Age*)(&res); ++ } ++ void set(Age age) volatile { OrderAccess::release_store_ptr((volatile intptr_t*) &_data, *(size_t*)(&age._data)); } ++ ++ idx_t top() const volatile { return OrderAccess::load_acquire((volatile idx_t*) &(_fields._top)); } ++ idx_t tag() const volatile { return OrderAccess::load_acquire((volatile idx_t*) &(_fields._tag)); } ++#endif + + // Increment top; if it wraps, increment tag also. + void increment() { +@@ -195,23 +206,50 @@ protected: + public: + TaskQueueSuper() : _bottom(0), _age() {} + ++#if defined MIPS || defined LOONGARCH ++ inline uint get_bottom() const { ++ return OrderAccess::load_acquire((volatile juint*)&_bottom); ++ } ++ ++ inline void set_bottom(uint new_bottom) { ++ OrderAccess::release_store(&_bottom, new_bottom); ++ } ++#endif + // Return true if the TaskQueue contains/does not contain any tasks. +- bool peek() const { return _bottom != _age.top(); } ++ bool peek() const { ++#if defined MIPS || defined LOONGARCH ++ return get_bottom() != _age.top(); ++#else ++ return _bottom != _age.top(); ++#endif ++ } + bool is_empty() const { return size() == 0; } + + // Return an estimate of the number of elements in the queue. + // The "careful" version admits the possibility of pop_local/pop_global + // races. + uint size() const { ++#if defined MIPS || defined LOONGARCH ++ return size(get_bottom(), _age.top()); ++#else + return size(_bottom, _age.top()); ++#endif + } + + uint dirty_size() const { ++#if defined MIPS || defined LOONGARCH ++ return dirty_size(get_bottom(), _age.top()); ++#else + return dirty_size(_bottom, _age.top()); ++#endif + } + + void set_empty() { ++#if defined MIPS || defined LOONGARCH ++ set_bottom(0); ++#else + _bottom = 0; ++#endif + _age.set(0); + } + +@@ -263,7 +301,9 @@ protected: + typedef typename TaskQueueSuper::Age Age; + typedef typename TaskQueueSuper::idx_t idx_t; + ++#if !defined MIPS && !defined LOONGARCH + using TaskQueueSuper::_bottom; ++#endif + using TaskQueueSuper::_age; + using TaskQueueSuper::increment_index; + using TaskQueueSuper::decrement_index; +@@ -327,7 +367,11 @@ template + void GenericTaskQueue::oops_do(OopClosure* f) { + // tty->print_cr("START OopTaskQueue::oops_do"); + uint iters = size(); ++#if defined MIPS || defined LOONGARCH ++ uint index = this->get_bottom(); ++#else + uint index = _bottom; ++#endif + for (uint i = 0; i < iters; ++i) { + index = decrement_index(index); + // tty->print_cr(" doing entry %d," INTPTR_T " -> " INTPTR_T, +@@ -345,14 +389,22 @@ template + bool GenericTaskQueue::push_slow(E t, uint dirty_n_elems) { + if (dirty_n_elems == N - 1) { + // Actually means 0, so do the push. ++#if defined MIPS || defined LOONGARCH ++ uint localBot = this->get_bottom(); ++#else + uint localBot = _bottom; ++#endif + // g++ complains if the volatile result of the assignment is + // unused, so we cast the volatile away. We cannot cast directly + // to void, because gcc treats that as not using the result of the + // assignment. However, casting to E& means that we trigger an + // unused-value warning. So, we cast the E& to void. + (void)const_cast(_elems[localBot] = t); ++#if defined MIPS || defined LOONGARCH ++ this->set_bottom(increment_index(localBot)); ++#else + OrderAccess::release_store(&_bottom, increment_index(localBot)); ++#endif + TASKQUEUE_STATS_ONLY(stats.record_push()); + return true; + } +@@ -407,7 +459,11 @@ bool GenericTaskQueue::pop_global(volatile E& t) { + #if !(defined SPARC || defined IA32 || defined AMD64) + OrderAccess::fence(); + #endif ++#if defined MIPS || defined LOONGARCH ++ uint localBot = this->get_bottom(); ++#else + uint localBot = OrderAccess::load_acquire((volatile juint*)&_bottom); ++#endif + uint n_elems = size(localBot, oldAge.top()); + if (n_elems == 0) { + return false; +@@ -662,7 +718,11 @@ public: + + template inline bool + GenericTaskQueue::push(E t) { ++#if defined MIPS || defined LOONGARCH ++ uint localBot = this->get_bottom(); ++#else + uint localBot = _bottom; ++#endif + assert(localBot < N, "_bottom out of range."); + idx_t top = _age.top(); + uint dirty_n_elems = dirty_size(localBot, top); +@@ -674,7 +734,11 @@ GenericTaskQueue::push(E t) { + // assignment. However, casting to E& means that we trigger an + // unused-value warning. So, we cast the E& to void. + (void) const_cast(_elems[localBot] = t); ++#if defined MIPS || defined LOONGARCH ++ this->set_bottom(increment_index(localBot)); ++#else + OrderAccess::release_store(&_bottom, increment_index(localBot)); ++#endif + TASKQUEUE_STATS_ONLY(stats.record_push()); + return true; + } else { +@@ -684,7 +748,11 @@ GenericTaskQueue::push(E t) { + + template inline bool + GenericTaskQueue::pop_local(volatile E& t) { ++#if defined MIPS || defined LOONGARCH ++ uint localBot = this->get_bottom(); ++#else + uint localBot = _bottom; ++#endif + // This value cannot be N-1. That can only occur as a result of + // the assignment to bottom in this method. If it does, this method + // resets the size to 0 before the next call (which is sequential, +@@ -693,7 +761,11 @@ GenericTaskQueue::pop_local(volatile E& t) { + assert(dirty_n_elems != N - 1, "Shouldn't be possible..."); + if (dirty_n_elems == 0) return false; + localBot = decrement_index(localBot); ++#if defined MIPS || defined LOONGARCH ++ this->set_bottom(localBot); ++#else + _bottom = localBot; ++#endif + // This is necessary to prevent any read below from being reordered + // before the store just above. + OrderAccess::fence(); +diff --git a/hotspot/src/share/vm/utilities/vmError.cpp b/hotspot/src/share/vm/utilities/vmError.cpp +index fa7a32508e..7098a98a9f 100644 +--- a/hotspot/src/share/vm/utilities/vmError.cpp ++++ b/hotspot/src/share/vm/utilities/vmError.cpp +@@ -22,6 +22,13 @@ + * + */ + ++/* ++ * This file has been modified by Loongson Technology in 2018. These ++ * modifications are Copyright (c) 2018 Loongson Technology, and are made ++ * available on the same license terms set forth above. ++ * ++*/ ++ + #include + #include "precompiled.hpp" + #include "compiler/compileBroker.hpp" +@@ -488,7 +495,12 @@ void VMError::report(outputStream* st) { + JDK_Version::runtime_name() : ""; + const char* runtime_version = JDK_Version::runtime_version() != NULL ? + JDK_Version::runtime_version() : ""; +- st->print_cr("# JRE version: %s (%s) (build %s)", runtime_name, buf, runtime_version); ++#ifdef LOONGSON_RUNTIME_NAME ++ const char* loongson_runtime_name_and_version = LOONGSON_RUNTIME_NAME; ++#else ++ const char* loongson_runtime_name_and_version = ""; ++#endif ++ st->print_cr("# JRE version: %s (%s) (build %s) (%s)", runtime_name, buf, runtime_version, loongson_runtime_name_and_version); + st->print_cr("# Java VM: %s (%s %s %s %s)", + Abstract_VM_Version::vm_name(), + Abstract_VM_Version::vm_release(), +diff --git a/hotspot/test/compiler/6865265/StackOverflowBug.java b/hotspot/test/compiler/6865265/StackOverflowBug.java +index 295a6b4177..b8fe082fd9 100644 +--- a/hotspot/test/compiler/6865265/StackOverflowBug.java ++++ b/hotspot/test/compiler/6865265/StackOverflowBug.java +@@ -28,7 +28,7 @@ + * @summary JVM crashes with "missing exception handler" error + * @author volker.simonis@sap.com + * +- * @run main/othervm -XX:CompileThreshold=100 -Xbatch -Xss248k StackOverflowBug ++ * @run main/othervm -XX:CompileThreshold=100 -Xbatch -Xss392k StackOverflowBug + */ + + +diff --git a/hotspot/test/compiler/8009761/Test8009761.java b/hotspot/test/compiler/8009761/Test8009761.java +index 401458b6b9..035847895a 100644 +--- a/hotspot/test/compiler/8009761/Test8009761.java ++++ b/hotspot/test/compiler/8009761/Test8009761.java +@@ -25,7 +25,7 @@ + * @test + * @bug 8009761 + * @summary Deoptimization on sparc doesn't set Llast_SP correctly in the interpreter frames it creates +- * @run main/othervm -XX:CompileCommand=exclude,Test8009761::m2 -XX:-UseOnStackReplacement -XX:-BackgroundCompilation -Xss256K Test8009761 ++ * @run main/othervm -XX:CompileCommand=exclude,Test8009761::m2 -XX:-UseOnStackReplacement -XX:-BackgroundCompilation -Xss392K Test8009761 + * + */ + +diff --git a/hotspot/test/compiler/exceptions/TestRecursiveReplacedException.java b/hotspot/test/compiler/exceptions/TestRecursiveReplacedException.java +index 996d82a0fe..d3cace0534 100644 +--- a/hotspot/test/compiler/exceptions/TestRecursiveReplacedException.java ++++ b/hotspot/test/compiler/exceptions/TestRecursiveReplacedException.java +@@ -25,7 +25,7 @@ + * @test + * @bug 8054224 + * @summary Recursive method compiled by C1 is unable to catch StackOverflowError +- * @run main/othervm -Xcomp -XX:CompileOnly=Test.run -XX:+TieredCompilation -XX:TieredStopAtLevel=2 -Xss256K TestRecursiveReplacedException ++ * @run main/othervm -Xcomp -XX:CompileOnly=Test.run -XX:+TieredCompilation -XX:TieredStopAtLevel=2 -Xss392K TestRecursiveReplacedException + * + */ + +diff --git a/hotspot/test/compiler/intrinsics/sha/cli/testcases/GenericTestCaseForOtherCPU.java b/hotspot/test/compiler/intrinsics/sha/cli/testcases/GenericTestCaseForOtherCPU.java +index fa9a6f208b..885957cf1c 100644 +--- a/hotspot/test/compiler/intrinsics/sha/cli/testcases/GenericTestCaseForOtherCPU.java ++++ b/hotspot/test/compiler/intrinsics/sha/cli/testcases/GenericTestCaseForOtherCPU.java +@@ -34,11 +34,12 @@ import com.oracle.java.testlibrary.cli.predicate.OrPredicate; + public class GenericTestCaseForOtherCPU extends + SHAOptionsBase.TestCase { + public GenericTestCaseForOtherCPU(String optionName) { +- // Execute the test case on any CPU except SPARC and X86 ++ // Execute the test case on any CPU except SPARC, LoongArch64 and X86 + super(optionName, new NotPredicate(new OrPredicate(Platform::isSparc, + new OrPredicate(Platform::isAArch64, + new OrPredicate(Platform::isPPC, +- new OrPredicate(Platform::isX64, Platform::isX86)))))); ++ new OrPredicate(Platform::isLoongArch64, ++ new OrPredicate(Platform::isX64, Platform::isX86))))))); + } + + @Override +diff --git a/hotspot/test/compiler/testlibrary/sha/predicate/IntrinsicPredicates.java b/hotspot/test/compiler/testlibrary/sha/predicate/IntrinsicPredicates.java +index dc8c398408..2427b2bf7b 100644 +--- a/hotspot/test/compiler/testlibrary/sha/predicate/IntrinsicPredicates.java ++++ b/hotspot/test/compiler/testlibrary/sha/predicate/IntrinsicPredicates.java +@@ -62,18 +62,24 @@ public class IntrinsicPredicates { + = new OrPredicate( + new CPUSpecificPredicate("sparc.*", new String[] { "sha1" }, + null), ++ // Basic instructions are used to implement SHA1 Intrinsics on LA, so "sha1" feature is not needed. ++ new OrPredicate(new CPUSpecificPredicate("loongarch64.*", null, ++ null), + new CPUSpecificPredicate("aarch64", new String[] { "sha1" }, +- null)); ++ null))); + + public static final BooleanSupplier SHA256_INSTRUCTION_AVAILABLE + = new OrPredicate(new CPUSpecificPredicate("aarch64", new String[] { "sha256" }, + null), + new OrPredicate(new CPUSpecificPredicate("sparc.*", new String[] { "sha256" }, + null), ++ // Basic instructions are used to implement SHA256 Intrinsics on LA, so "sha256" feature is not needed. ++ new OrPredicate(new CPUSpecificPredicate("loongarch64.*", null, ++ null), + new OrPredicate(new CPUSpecificPredicate("ppc64.*", new String[] { "sha" }, + null), + new CPUSpecificPredicate("ppc64le.*", new String[] { "sha" }, +- null)))); ++ null))))); + + public static final BooleanSupplier SHA512_INSTRUCTION_AVAILABLE + = new OrPredicate( +diff --git a/hotspot/test/compiler/uncommontrap/StackOverflowGuardPagesOff.java b/hotspot/test/compiler/uncommontrap/StackOverflowGuardPagesOff.java +index 4ad409bb1e..ba3b879553 100644 +--- a/hotspot/test/compiler/uncommontrap/StackOverflowGuardPagesOff.java ++++ b/hotspot/test/compiler/uncommontrap/StackOverflowGuardPagesOff.java +@@ -25,7 +25,7 @@ + * @test + * @bug 8029383 + * @summary stack overflow if callee is marked for deoptimization causes crash +- * @run main/othervm -XX:TieredStopAtLevel=1 -XX:-BackgroundCompilation -XX:CompileCommand=dontinline,StackOverflowGuardPagesOff::m1 -XX:CompileCommand=exclude,StackOverflowGuardPagesOff::m2 -Xss256K -XX:-UseOnStackReplacement StackOverflowGuardPagesOff ++ * @run main/othervm -XX:TieredStopAtLevel=1 -XX:-BackgroundCompilation -XX:CompileCommand=dontinline,StackOverflowGuardPagesOff::m1 -XX:CompileCommand=exclude,StackOverflowGuardPagesOff::m2 -Xss392K -XX:-UseOnStackReplacement StackOverflowGuardPagesOff + * + */ + +diff --git a/hotspot/test/compiler/uncommontrap/TestStackBangMonitorOwned.java b/hotspot/test/compiler/uncommontrap/TestStackBangMonitorOwned.java +index 3d93d7d5a5..83345642f7 100644 +--- a/hotspot/test/compiler/uncommontrap/TestStackBangMonitorOwned.java ++++ b/hotspot/test/compiler/uncommontrap/TestStackBangMonitorOwned.java +@@ -25,7 +25,7 @@ + * @test + * @bug 8032410 + * @summary Stack overflow at deoptimization doesn't release owned monitors +- * @run main/othervm -XX:-BackgroundCompilation -XX:CompileCommand=dontinline,TestStackBangMonitorOwned::m1 -XX:CompileCommand=exclude,TestStackBangMonitorOwned::m2 -Xss256K -XX:-UseOnStackReplacement TestStackBangMonitorOwned ++ * @run main/othervm -XX:-BackgroundCompilation -XX:CompileCommand=dontinline,TestStackBangMonitorOwned::m1 -XX:CompileCommand=exclude,TestStackBangMonitorOwned::m2 -Xss392K -XX:-UseOnStackReplacement TestStackBangMonitorOwned + * + */ + public class TestStackBangMonitorOwned { +diff --git a/hotspot/test/compiler/uncommontrap/TestStackBangRbp.java b/hotspot/test/compiler/uncommontrap/TestStackBangRbp.java +index 38d4e206e0..f242e6edd6 100644 +--- a/hotspot/test/compiler/uncommontrap/TestStackBangRbp.java ++++ b/hotspot/test/compiler/uncommontrap/TestStackBangRbp.java +@@ -25,7 +25,7 @@ + * @test + * @bug 8028308 + * @summary rbp not restored when stack overflow is thrown from deopt/uncommon trap blobs +- * @run main/othervm -XX:-BackgroundCompilation -XX:CompileCommand=dontinline,TestStackBangRbp::m1 -XX:CompileCommand=exclude,TestStackBangRbp::m2 -Xss256K -XX:-UseOnStackReplacement TestStackBangRbp ++ * @run main/othervm -XX:-BackgroundCompilation -XX:CompileCommand=dontinline,TestStackBangRbp::m1 -XX:CompileCommand=exclude,TestStackBangRbp::m2 -Xss392K -XX:-UseOnStackReplacement TestStackBangRbp + * + */ + public class TestStackBangRbp { +diff --git a/hotspot/test/gc/arguments/TestMaxHeapSizeTools.java b/hotspot/test/gc/arguments/TestMaxHeapSizeTools.java +index b5859b5cf4..99ed508d67 100644 +--- a/hotspot/test/gc/arguments/TestMaxHeapSizeTools.java ++++ b/hotspot/test/gc/arguments/TestMaxHeapSizeTools.java +@@ -112,7 +112,7 @@ class TestMaxHeapSizeTools { + } + + private static void checkInvalidMinInitialHeapCombinations(String gcflag) throws Exception { +- expectError(new String[] { gcflag, "-Xms8M", "-XX:InitialHeapSize=4M", "-version" }); ++ expectError(new String[] { gcflag, "-Xms64M", "-XX:InitialHeapSize=32M", "-version" }); + } + + private static void checkValidMinInitialHeapCombinations(String gcflag) throws Exception { +diff --git a/hotspot/test/gc/g1/TestHumongousAllocInitialMark.java b/hotspot/test/gc/g1/TestHumongousAllocInitialMark.java +index 473ce666e9..b6e5c3d66d 100644 +--- a/hotspot/test/gc/g1/TestHumongousAllocInitialMark.java ++++ b/hotspot/test/gc/g1/TestHumongousAllocInitialMark.java +@@ -31,7 +31,9 @@ + import com.oracle.java.testlibrary.*; + + public class TestHumongousAllocInitialMark { +- private static final int heapSize = 200; // MB ++ // Heap sizes < 224 MB are increased to 224 MB if vm_page_size == 64K to ++ // fulfill alignment constraints. ++ private static final int heapSize = 224; // MB + private static final int heapRegionSize = 1; // MB + private static final int initiatingHeapOccupancyPercent = 50; // % + +diff --git a/hotspot/test/runtime/6929067/Test6929067.sh b/hotspot/test/runtime/6929067/Test6929067.sh +index 90b96d5e9d..7137237b71 100644 +--- a/hotspot/test/runtime/6929067/Test6929067.sh ++++ b/hotspot/test/runtime/6929067/Test6929067.sh +@@ -97,6 +97,10 @@ case "$ARCH" in + i686) + ARCH=i386 + ;; ++ loongarch64) ++ COMP_FLAG="" ++ ARCH=loongarch64 ++ ;; + # Assuming other ARCH values need no translation + esac + +diff --git a/hotspot/test/runtime/StackGap/testme.sh b/hotspot/test/runtime/StackGap/testme.sh +index 2471be4a58..bcb4b035bd 100644 +--- a/hotspot/test/runtime/StackGap/testme.sh ++++ b/hotspot/test/runtime/StackGap/testme.sh +@@ -54,6 +54,19 @@ then + CFLAGS="-m${VM_BITS}" + fi + ++PLATFORM=$(echo ${VM_CPU}) ++case "$PLATFORM" in ++ mips64el ) ++ CFLAGS="-mabi=${VM_BITS}" ++ ;; ++ loongarch64 ) ++ CFLAGS="-mabi=lp${VM_BITS}" ++ ;; ++ * ) ++ CFLAGS="-m${VM_BITS}" ++ ;; ++esac ++ + LD_LIBRARY_PATH=.:${COMPILEJAVA}/jre/lib/${VM_CPU}/${VM_TYPE}:/usr/lib:$LD_LIBRARY_PATH + export LD_LIBRARY_PATH + +diff --git a/hotspot/test/test_env.sh b/hotspot/test/test_env.sh +index e76796e715..75133e402a 100644 +--- a/hotspot/test/test_env.sh ++++ b/hotspot/test/test_env.sh +@@ -205,6 +205,29 @@ if [ $? = 0 ] + then + VM_CPU="aarch64" + fi ++grep "mips" vm_version.out > ${NULL} ++if [ $? = 0 ] ++then ++ VM_CPU="mips" ++ if [ $VM_BITS = "64" ] ++ then ++ VM_CPU="mips64" ++ grep "mips64el" vm_version.out > ${NULL} ++ if [ $? = 0 ] ++ then ++ VM_CPU="mips64el" ++ fi ++ fi ++fi ++grep "loongarch" vm_version.out > ${NULL} ++if [ $? = 0 ] ++then ++ VM_CPU="loongarch" ++ if [ $VM_BITS = "64" ] ++ then ++ VM_CPU="loongarch64" ++ fi ++fi + export VM_TYPE VM_BITS VM_OS VM_CPU + echo "VM_TYPE=${VM_TYPE}" + echo "VM_BITS=${VM_BITS}" +diff --git a/hotspot/test/testlibrary/com/oracle/java/testlibrary/Platform.java b/hotspot/test/testlibrary/com/oracle/java/testlibrary/Platform.java +index 6a14079347..56a6375b5f 100644 +--- a/hotspot/test/testlibrary/com/oracle/java/testlibrary/Platform.java ++++ b/hotspot/test/testlibrary/com/oracle/java/testlibrary/Platform.java +@@ -126,6 +126,10 @@ public class Platform { + return isArch("aarch64"); + } + ++ public static boolean isLoongArch64() { ++ return isArch("loongarch64"); ++ } ++ + private static boolean isArch(String archnameRE) { + return Pattern.compile(archnameRE, Pattern.CASE_INSENSITIVE) + .matcher(osArch) +@@ -136,6 +140,10 @@ public class Platform { + return osArch; + } + ++ public static boolean isMIPS() { ++ return isArch("mips.*"); ++ } ++ + /** + * Return a boolean for whether we expect to be able to attach + * the SA to our own processes on this system. +diff --git a/hotspot/test/testlibrary_tests/TestMutuallyExclusivePlatformPredicates.java b/hotspot/test/testlibrary_tests/TestMutuallyExclusivePlatformPredicates.java +index 7d56a4a3bc..41825e18b3 100644 +--- a/hotspot/test/testlibrary_tests/TestMutuallyExclusivePlatformPredicates.java ++++ b/hotspot/test/testlibrary_tests/TestMutuallyExclusivePlatformPredicates.java +@@ -43,7 +43,7 @@ import java.util.Set; + */ + public class TestMutuallyExclusivePlatformPredicates { + private static enum MethodGroup { +- ARCH("isARM", "isPPC", "isSparc", "isX86", "isX64", "isAArch64"), ++ ARCH("isARM", "isPPC", "isSparc", "isX86", "isX64", "isAArch64", "isMIPS", "isLoongArch64"), + BITNESS("is32bit", "is64bit"), + OS("isAix", "isLinux", "isSolaris", "isWindows", "isOSX"), + VM_TYPE("isClient", "isServer", "isGraal", "isMinimal"), +diff --git a/jdk/make/Images.gmk b/jdk/make/Images.gmk +index ac39ad3304..05a01340e1 100644 +--- a/jdk/make/Images.gmk ++++ b/jdk/make/Images.gmk +@@ -23,6 +23,12 @@ + # questions. + # + ++# ++# This file has been modified by Loongson Technology in 2022. These ++# modifications are Copyright (c) 2018, 2022, Loongson Technology, and are made ++# available on the same license terms set forth above. ++# ++ + include $(SPEC) + include MakeBase.gmk + include JavaCompilation.gmk +@@ -649,6 +655,11 @@ $(JDK_IMAGE_DIR)/src.zip: $(IMAGES_OUTPUTDIR)/src.zip + $(ECHO) $(LOG_INFO) Copying $(patsubst $(OUTPUT_ROOT)/%,%,$@) + $(install-file) + ++# create link "mips64el -> mips64" for deploy ++$(JDK_IMAGE_DIR)/jre/lib/mips64: $(JDK_IMAGE_DIR)/jre/lib/mips64el ++ $(ECHO) $(LOG_INFO) Create link from mips64 to mips64 ++ $(CD) $(JDK_IMAGE_DIR)/jre/lib && $(RM) mips64 && $(LN) -s mips64el mips64 ++ + ################################################################################ + # Post processing (strip etc) + +@@ -727,6 +738,14 @@ ifneq ($(POST_STRIP_CMD), ) + + endif + ++################################################################################ ++# Loongson added list, architecture dependent files ++ifeq ($(OPENJDK_TARGET_CPU), mips64) ++ ifeq ($(OPENJDK_TARGET_CPU_ENDIAN), little) ++ JDK_IMAGE_LOONGSON_LIST := $(JDK_IMAGE_DIR)/jre/lib/mips64el ++ endif ++endif ++ + ################################################################################ + + # Include the custom makefile right here, after all variables have been defined +@@ -752,6 +771,7 @@ jdk-image: $(JDK_BIN_TARGETS) $(JDKJRE_BIN_TARGETS) \ + $(JDKJRE_DOC_TARGETS) $(JDK_DOC_TARGETS) \ + $(JDK_INFO_FILE) $(JDKJRE_STRIP_LIST) $(JDK_BIN_STRIP_LIST) \ + $(JDK_IMAGE_DIR)/src.zip \ ++ $(JDK_IMAGE_LOONGSON_LIST) \ + $(JDK_BIN_ISADIR_LINK_TARGETS) $(JDKJRE_BIN_ISADIR_LINK_TARGETS) + + jre-overlay-image: $(JRE_OVERLAY_BIN_TARGETS) $(JRE_OVERLAY_LIB_TARGETS) \ +diff --git a/jdk/make/gensrc/GensrcMisc.gmk b/jdk/make/gensrc/GensrcMisc.gmk +index 0e3dee5ca3..66f19f4d25 100644 +--- a/jdk/make/gensrc/GensrcMisc.gmk ++++ b/jdk/make/gensrc/GensrcMisc.gmk +@@ -23,6 +23,12 @@ + # questions. + # + ++# ++# This file has been modified by Loongson Technology in 2018. These ++# modifications are Copyright (c) 2018, Loongson Technology, and are made ++# available on the same license terms set forth above. ++# ++ + include ProfileNames.gmk + + ################################################################################ +@@ -39,6 +45,7 @@ $(PROFILE_VERSION_JAVA_TARGETS): \ + $(SED) -e 's/@@launcher_name@@/$(LAUNCHER_NAME)/g' \ + -e 's/@@java_version@@/$(RELEASE)/g' \ + -e 's/@@java_runtime_version@@/$(FULL_VERSION)/g' \ ++ -e 's/@@loongson_runtime_name@@/$(LOONGSON_RUNTIME_NAME)/g' \ + -e 's/@@java_runtime_name@@/$(RUNTIME_NAME)/g' \ + -e 's/@@java_profile_name@@/$(call profile_version_name, $@)/g' \ + $< > $@.tmp +diff --git a/jdk/make/lib/SoundLibraries.gmk b/jdk/make/lib/SoundLibraries.gmk +index b59a9462ec..8ce97dc854 100644 +--- a/jdk/make/lib/SoundLibraries.gmk ++++ b/jdk/make/lib/SoundLibraries.gmk +@@ -23,6 +23,12 @@ + # questions. + # + ++# ++# This file has been modified by Loongson Technology in 2021. These ++# modifications are Copyright (c) 2015, 2021, Loongson Technology, and are made ++# available on the same license terms set forth above. ++# ++ + LIBJSOUND_SRC_DIRS := \ + $(JDK_TOPDIR)/src/share/native/com/sun/media/sound \ + $(JDK_TOPDIR)/src/$(OPENJDK_TARGET_OS_API_DIR)/native/com/sun/media/sound +@@ -136,6 +142,14 @@ else + LIBJSOUND_CFLAGS += -DX_ARCH=X_PPC + endif + ++ ifeq ($(OPENJDK_TARGET_CPU), mips64) ++ LIBJSOUND_CFLAGS += -DX_ARCH=X_MIPS64 ++ endif ++ ++ ifeq ($(OPENJDK_TARGET_CPU), loongarch64) ++ LIBJSOUND_CFLAGS += -DX_ARCH=X_LOONGARCH64 ++ endif ++ + ifeq ($(OPENJDK_TARGET_CPU), ppc64) + LIBJSOUND_CFLAGS += -DX_ARCH=X_PPC64 + endif +diff --git a/jdk/src/share/classes/sun/misc/Version.java.template b/jdk/src/share/classes/sun/misc/Version.java.template +index 32e2586e79..e38541a9f7 100644 +--- a/jdk/src/share/classes/sun/misc/Version.java.template ++++ b/jdk/src/share/classes/sun/misc/Version.java.template +@@ -23,6 +23,13 @@ + * questions. + */ + ++/* ++ * This file has been modified by Loongson Technology in 2018. These ++ * modifications are Copyright (c) 2018 Loongson Technology, and are made ++ * available on the same license terms set forth above. ++ */ ++ ++ + package sun.misc; + import java.io.PrintStream; + +@@ -44,6 +51,9 @@ public class Version { + private static final String java_runtime_version = + "@@java_runtime_version@@"; + ++ private static final String loongson_runtime_name = ++ "@@loongson_runtime_name@@"; ++ + static { + init(); + } +@@ -103,7 +113,11 @@ public class Version { + + /* Second line: runtime version (ie, libraries). */ + +- ps.print(java_runtime_name + " (build " + java_runtime_version); ++ ps.print(java_runtime_name); ++ if (loongson_runtime_name.length() > 0) { ++ ps.print(" ("+ loongson_runtime_name +")"); ++ } ++ ps.print(" (build " + java_runtime_version); + + if (java_profile_name.length() > 0) { + // profile name +diff --git a/jdk/src/solaris/bin/loongarch64/jvm.cfg b/jdk/src/solaris/bin/loongarch64/jvm.cfg +new file mode 100644 +index 0000000000..42a06755da +--- /dev/null ++++ b/jdk/src/solaris/bin/loongarch64/jvm.cfg +@@ -0,0 +1,36 @@ ++# Copyright (c) 2003, Oracle and/or its affiliates. All rights reserved. ++# Copyright (c) 2015, 2022, Loongson Technology. All rights reserved. ++# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++# ++# This code is free software; you can redistribute it and/or modify it ++# under the terms of the GNU General Public License version 2 only, as ++# published by the Free Software Foundation. Oracle designates this ++# particular file as subject to the "Classpath" exception as provided ++# by Oracle in the LICENSE file that accompanied this code. ++# ++# This code is distributed in the hope that it will be useful, but WITHOUT ++# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++# version 2 for more details (a copy is included in the LICENSE file that ++# accompanied this code). ++# ++# You should have received a copy of the GNU General Public License version ++# 2 along with this work; if not, write to the Free Software Foundation, ++# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++# ++# Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++# or visit www.oracle.com if you need additional information or have any ++# questions. ++# ++# ++# List of JVMs that can be used as an option to java, javac, etc. ++# Order is important -- first in this list is the default JVM. ++# NOTE that this both this file and its format are UNSUPPORTED and ++# WILL GO AWAY in a future release. ++# ++# You may also select a JVM in an arbitrary location with the ++# "-XXaltjvm=" option, but that too is unsupported ++# and may not be available in a future release. ++# ++-server KNOWN ++-client IGNORE +diff --git a/jdk/src/solaris/bin/mips64/jvm.cfg b/jdk/src/solaris/bin/mips64/jvm.cfg +new file mode 100644 +index 0000000000..42a06755da +--- /dev/null ++++ b/jdk/src/solaris/bin/mips64/jvm.cfg +@@ -0,0 +1,36 @@ ++# Copyright (c) 2003, Oracle and/or its affiliates. All rights reserved. ++# Copyright (c) 2015, 2022, Loongson Technology. All rights reserved. ++# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++# ++# This code is free software; you can redistribute it and/or modify it ++# under the terms of the GNU General Public License version 2 only, as ++# published by the Free Software Foundation. Oracle designates this ++# particular file as subject to the "Classpath" exception as provided ++# by Oracle in the LICENSE file that accompanied this code. ++# ++# This code is distributed in the hope that it will be useful, but WITHOUT ++# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++# version 2 for more details (a copy is included in the LICENSE file that ++# accompanied this code). ++# ++# You should have received a copy of the GNU General Public License version ++# 2 along with this work; if not, write to the Free Software Foundation, ++# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++# ++# Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++# or visit www.oracle.com if you need additional information or have any ++# questions. ++# ++# ++# List of JVMs that can be used as an option to java, javac, etc. ++# Order is important -- first in this list is the default JVM. ++# NOTE that this both this file and its format are UNSUPPORTED and ++# WILL GO AWAY in a future release. ++# ++# You may also select a JVM in an arbitrary location with the ++# "-XXaltjvm=" option, but that too is unsupported ++# and may not be available in a future release. ++# ++-server KNOWN ++-client IGNORE +diff --git a/jdk/test/jdk/jfr/event/os/TestCPUInformation.java b/jdk/test/jdk/jfr/event/os/TestCPUInformation.java +index d6a026b2cc..b65486023f 100644 +--- a/jdk/test/jdk/jfr/event/os/TestCPUInformation.java ++++ b/jdk/test/jdk/jfr/event/os/TestCPUInformation.java +@@ -54,8 +54,8 @@ public class TestCPUInformation { + Events.assertField(event, "hwThreads").atLeast(1); + Events.assertField(event, "cores").atLeast(1); + Events.assertField(event, "sockets").atLeast(1); +- Events.assertField(event, "cpu").containsAny("Intel", "AMD", "Unknown x86", "sparc", "ARM", "PPC", "PowerPC", "AArch64", "s390"); +- Events.assertField(event, "description").containsAny("Intel", "AMD", "Unknown x86", "SPARC", "ARM", "PPC", "PowerPC", "AArch64", "s390"); ++ Events.assertField(event, "cpu").containsAny("Intel", "AMD", "Unknown x86", "sparc", "ARM", "PPC", "PowerPC", "AArch64", "s390", "MIPS", "LoongArch"); ++ Events.assertField(event, "description").containsAny("Intel", "AMD", "Unknown x86", "SPARC", "ARM", "PPC", "PowerPC", "AArch64", "s390", "MIPS", "LoongArch"); + } + } + } +diff --git a/jdk/test/sun/management/jmxremote/bootstrap/linux-loongarch64/launcher b/jdk/test/sun/management/jmxremote/bootstrap/linux-loongarch64/launcher +new file mode 100755 +index 0000000000..66291c7522 +Binary files /dev/null and b/jdk/test/sun/management/jmxremote/bootstrap/linux-loongarch64/launcher differ +diff --git a/jdk/test/sun/management/jmxremote/bootstrap/linux-mips64el/launcher b/jdk/test/sun/management/jmxremote/bootstrap/linux-mips64el/launcher +new file mode 100644 +index 0000000000..5c8385ca12 +Binary files /dev/null and b/jdk/test/sun/management/jmxremote/bootstrap/linux-mips64el/launcher differ +diff --git a/jdk/test/sun/security/pkcs11/PKCS11Test.java b/jdk/test/sun/security/pkcs11/PKCS11Test.java +index 5fc9c605de..9db6a17d66 100644 +--- a/jdk/test/sun/security/pkcs11/PKCS11Test.java ++++ b/jdk/test/sun/security/pkcs11/PKCS11Test.java +@@ -21,6 +21,11 @@ + * questions. + */ + ++ /* ++ * This file has been modified by Loongson Technology in 2022, These ++ * modifications are Copyright (c) 2022, Loongson Technology, and are made ++ * available on the same license terms set forth above. ++ */ + + // common infrastructure for SunPKCS11 tests + +@@ -589,6 +594,9 @@ public abstract class PKCS11Test { + "/usr/lib64/"}); + osMap.put("Linux-ppc64-64", new String[]{"/usr/lib64/"}); + osMap.put("Linux-ppc64le-64", new String[]{"/usr/lib64/"}); ++ osMap.put("Linux-mips64el-64", new String[]{"/usr/lib64/"}); ++ osMap.put("Linux-loongarch64-64", new String[]{"/usr/lib/loongarch64-linux-gnu/", ++ "/usr/lib64/" }); + osMap.put("Windows-x86-32", new String[]{ + PKCS11_BASE + "/nss/lib/windows-i586/".replace('/', SEP)}); + osMap.put("Windows-amd64-64", new String[]{ diff --git a/Print-class-loading-details-when-enable-TraceClassLo.patch b/Print-class-loading-details-when-enable-TraceClassLo.patch new file mode 100644 index 0000000000000000000000000000000000000000..594034d8f184ffa3c93634e77370aa395c2b3c67 --- /dev/null +++ b/Print-class-loading-details-when-enable-TraceClassLo.patch @@ -0,0 +1,67 @@ +From 3b427b4702ac1ccdfa47fc46522fc06884abf394 Mon Sep 17 00:00:00 2001 +From: eapen +Date: Fri, 16 Dec 2022 09:23:41 +0800 +Subject: [PATCH 24/33] I68TO2: Print class loading details when enable + TraceClassLoading +--- + hotspot/src/share/vm/classfile/classFileParser.cpp | 25 +++++++++++++++++++--- + hotspot/src/share/vm/runtime/globals.hpp | 8 +++++++ + 2 files changed, 30 insertions(+), 3 deletions(-) + +diff --git a/hotspot/src/share/vm/classfile/classFileParser.cpp b/hotspot/src/share/vm/classfile/classFileParser.cpp +index ae91995..3ec6aec 100644 +--- a/hotspot/src/share/vm/classfile/classFileParser.cpp ++++ b/hotspot/src/share/vm/classfile/classFileParser.cpp +@@ -4323,9 +4323,28 @@ instanceKlassHandle ClassFileParser::parseClassFile(Symbol* name, + if (TraceClassLoading) { + ResourceMark rm; + // print in a single call to reduce interleaving of output +- if (cfs->source() != NULL) { +- tty->print("[Loaded %s from %s]\n", this_klass->external_name(), +- cfs->source()); ++ const char* source = cfs->source(); ++ if (source != NULL && PrintClassLoadingDetails) { ++ tty->date_stamp(true); ++ OSThread* osThread = THREAD->osthread(); ++ if (osThread != NULL) { ++ tty->print("%d ", osThread->thread_id()); ++ } ++ const char* loader_name = class_loader.is_null() ++ ? "bootstrap" ++ : InstanceKlass::cast(class_loader->klass())->external_name(); ++ const char* klass_name = this_klass->external_name(); ++ tty->print(" [Loaded %s from %s by classloader %s]\n", klass_name, ++ source, loader_name); ++ if (PrintThreadStackOnLoadingClass != NULL && klass_name != NULL && ++ strstr(klass_name, PrintThreadStackOnLoadingClass) && THREAD->is_Java_thread()) { ++ JavaThread* javaThread = ((JavaThread*) THREAD); ++ javaThread->print_on(tty); ++ javaThread->print_stack_on(tty); ++ } ++ } else if (source != NULL) { ++ tty->print("[Loaded %s from %s]\n", this_klass->external_name(), ++ source); + } else if (class_loader.is_null()) { + Klass* caller = + THREAD->is_Java_thread() +diff --git a/hotspot/src/share/vm/runtime/globals.hpp b/hotspot/src/share/vm/runtime/globals.hpp +index d1e3cda..14c3c89 100644 +--- a/hotspot/src/share/vm/runtime/globals.hpp ++++ b/hotspot/src/share/vm/runtime/globals.hpp +@@ -951,6 +951,14 @@ class CommandLineFlags { + product(ccstrlist, OnOutOfMemoryError, "", \ + "Run user-defined commands on first java.lang.OutOfMemoryError") \ + \ ++ manageable(bool, PrintClassLoadingDetails, false, \ ++ "Print class loading details (including date stamps, thread id " \ ++ "and effective class loaders) when enable TraceClassLoading") \ ++ \ ++ manageable(ccstr, PrintThreadStackOnLoadingClass, NULL, \ ++ "Print thread stack when the specified class is loaded when " \ ++ "enable PrintClassLoadingDetails") \ ++ \ + manageable(bool, HeapDumpBeforeFullGC, false, \ + "Dump heap to file before any major stop-the-world GC") \ + \ +-- +1.8.3.1 diff --git a/The-code-style-is-fixed-and-test-cases-are-added.patch b/The-code-style-is-fixed-and-test-cases-are-added.patch new file mode 100644 index 0000000000000000000000000000000000000000..9219f3dddf289dd403c4ed554b9269d2494aa417 --- /dev/null +++ b/The-code-style-is-fixed-and-test-cases-are-added.patch @@ -0,0 +1,529 @@ +From 0a877e963eeb55b98dcd0194ac44b4f010d382eb Mon Sep 17 00:00:00 2001 +Date: Wed, 21 Sep 2022 09:54:56 +0800 +Subject: The code style is fixed and test cases are added + +--- + hotspot/src/share/vm/cds/archiveBuilder.hpp | 1 - + hotspot/src/share/vm/cds/archiveUtils.hpp | 1 - + hotspot/src/share/vm/cds/dynamicArchive.hpp | 1 - + .../share/vm/classfile/systemDictionary.cpp | 2 +- + .../vm/classfile/systemDictionaryShared.hpp | 1 + + .../shared/parGCAllocBuffer.cpp | 6 +- + hotspot/src/share/vm/memory/filemap.cpp | 7 +- + hotspot/src/share/vm/memory/filemap.hpp | 2 +- + .../src/share/vm/memory/metaspaceClosure.cpp | 25 + + .../src/share/vm/memory/metaspaceClosure.hpp | 25 +- + hotspot/src/share/vm/oops/cpCache.cpp | 1 - + hotspot/src/share/vm/oops/instanceKlass.cpp | 4 - + hotspot/test/runtime/6929067/Test6929067.sh | 2 +- + .../runtime/InitialThreadOverflow/testme.sh | 2 +- + hotspot/test/runtime/Thread/StopAtExit.java | 119 ++++ + jdk/make/profile-rtjar-includes.txt | 7 +- + .../classes/java/io/ObjectInputStream.java | 4 +- + .../classes/java/io/ObjectOutputStream.java | 10 +- + .../classes/sun/awt/FontConfiguration.java | 7 +- + .../security/openssl/kae_cipher_rsa.c | 3 +- + .../security/openssl/kae_keyagreement_dh.c | 4 +- + .../openssl/RSAKeyPairGeneratorBenchmark.java | 2 +- + 23 files changed, 194 insertions(+), 668 deletions(-) + create mode 100644 hotspot/test/runtime/Thread/StopAtExit.java + +diff --git a/hotspot/src/share/vm/cds/archiveBuilder.hpp b/hotspot/src/share/vm/cds/archiveBuilder.hpp +index f7a5c107..93c0e245 100644 +--- a/hotspot/src/share/vm/cds/archiveBuilder.hpp ++++ b/hotspot/src/share/vm/cds/archiveBuilder.hpp +@@ -29,7 +29,6 @@ + #include "cds/archiveUtils.hpp" + #include "cds/dumpAllocStats.hpp" + #include "memory/metaspaceClosure.hpp" +-//#include "oops/array.hpp" + #include "oops/klass.hpp" + #include "runtime/os.hpp" + #include "utilities/align.hpp" +diff --git a/hotspot/src/share/vm/cds/archiveUtils.hpp b/hotspot/src/share/vm/cds/archiveUtils.hpp +index 55c2431a..44f03c8e 100644 +--- a/hotspot/src/share/vm/cds/archiveUtils.hpp ++++ b/hotspot/src/share/vm/cds/archiveUtils.hpp +@@ -133,7 +133,6 @@ public: + _dump_region->append_intptr_t((intptr_t)tag); + } + +- //void do_oop(oop* o); + void do_region(u_char* start, size_t size); + bool reading() const { return false; } + }; +diff --git a/hotspot/src/share/vm/cds/dynamicArchive.hpp b/hotspot/src/share/vm/cds/dynamicArchive.hpp +index 1d5b7122..0e068e65 100644 +--- a/hotspot/src/share/vm/cds/dynamicArchive.hpp ++++ b/hotspot/src/share/vm/cds/dynamicArchive.hpp +@@ -26,7 +26,6 @@ + #ifndef SHARE_VM_CDS_DYNAMICARCHIVE_HPP + #define SHARE_VM_CDS_DYNAMICARCHIVE_HPP + +-//#include "classfile/compactHashtable.hpp" + #include "memory/allocation.hpp" + #include "memory/filemap.hpp" + #include "memory/memRegion.hpp" +diff --git a/hotspot/src/share/vm/classfile/systemDictionary.cpp b/hotspot/src/share/vm/classfile/systemDictionary.cpp +index 2eebdbac..0ea2d9b7 100644 +--- a/hotspot/src/share/vm/classfile/systemDictionary.cpp ++++ b/hotspot/src/share/vm/classfile/systemDictionary.cpp +@@ -1344,7 +1344,7 @@ instanceKlassHandle SystemDictionary::load_shared_class( + Handle klass_name = java_lang_String::create_from_str(name, CHECK_0); + JavaValue result(T_OBJECT); + +- // load_shared_class need protected domain to handle non-bootstrap loaded class, ++ // load_shared_class need protected domain to handle non-bootstrap loaded class, + // so here call_virtual to call getProtectionDomainInternal function of URLClassLoader.java, + // to get protected domain and save into result. + JavaCalls::call_virtual(&result, +diff --git a/hotspot/src/share/vm/classfile/systemDictionaryShared.hpp b/hotspot/src/share/vm/classfile/systemDictionaryShared.hpp +index 36423bee..fb9583d4 100644 +--- a/hotspot/src/share/vm/classfile/systemDictionaryShared.hpp ++++ b/hotspot/src/share/vm/classfile/systemDictionaryShared.hpp +@@ -194,6 +194,7 @@ public: + } + return true; + } ++ + static size_t estimate_size_for_archive(); + static void write_to_archive(); + static void write_dictionary(RunTimeSharedDictionary* dictionary, bool is_builtin); +diff --git a/hotspot/src/share/vm/gc_implementation/shared/parGCAllocBuffer.cpp b/hotspot/src/share/vm/gc_implementation/shared/parGCAllocBuffer.cpp +index bddf14b6..0244bf84 100644 +--- a/hotspot/src/share/vm/gc_implementation/shared/parGCAllocBuffer.cpp ++++ b/hotspot/src/share/vm/gc_implementation/shared/parGCAllocBuffer.cpp +@@ -98,9 +98,9 @@ void PLABStats::adjust_desired_plab_sz(uint no_of_gc_workers) { + if (_allocated == 0) { + assert(_unused == 0, + err_msg("Inconsistency in PLAB stats: " +- "_allocated: "SIZE_FORMAT", " +- "_wasted: "SIZE_FORMAT", " +- "_unused: "SIZE_FORMAT", " ++ "_allocated: " SIZE_FORMAT ", " ++ "_wasted: " SIZE_FORMAT ", " ++ "_unused: " SIZE_FORMAT ", " + "_used : "SIZE_FORMAT, + _allocated, _wasted, _unused, _used)); + +diff --git a/hotspot/src/share/vm/memory/filemap.cpp b/hotspot/src/share/vm/memory/filemap.cpp +index 1891fc80..0682cd67 100644 +--- a/hotspot/src/share/vm/memory/filemap.cpp ++++ b/hotspot/src/share/vm/memory/filemap.cpp +@@ -240,12 +240,7 @@ void FileMapInfo::FileMapHeader::populate(FileMapInfo* mapinfo, size_t alignment + _version = current_version(); + _alignment = alignment; + _obj_alignment = ObjectAlignmentInBytes; +- /* TODO +- _compressed_oops = UseCompressedOops; +- _compressed_class_ptrs = UseCompressedClassPointers; +- _max_heap_size = MaxHeapSize; +- _narrow_klass_shift = CompressedKlassPointers::shift(); +- */ ++ + if (!DynamicDumpSharedSpaces) { + _classpath_entry_table_size = mapinfo->_classpath_entry_table_size; + _classpath_entry_table = mapinfo->_classpath_entry_table; +diff --git a/hotspot/src/share/vm/memory/filemap.hpp b/hotspot/src/share/vm/memory/filemap.hpp +index 36b27f13..27fff35e 100644 +--- a/hotspot/src/share/vm/memory/filemap.hpp ++++ b/hotspot/src/share/vm/memory/filemap.hpp +@@ -232,7 +232,7 @@ public: + char* region_end(int i) { return region_base(i) + used_aligned(i); } + struct FileMapHeader* header() { return _header; } + struct DynamicArchiveHeader* dynamic_header() { +- // assert(!is_static(), "must be"); ++ + return (struct DynamicArchiveHeader*)header(); + } + +diff --git a/hotspot/src/share/vm/memory/metaspaceClosure.cpp b/hotspot/src/share/vm/memory/metaspaceClosure.cpp +index 00ec8fce..e19402cb 100644 +--- a/hotspot/src/share/vm/memory/metaspaceClosure.cpp ++++ b/hotspot/src/share/vm/memory/metaspaceClosure.cpp +@@ -1,3 +1,28 @@ ++/* ++ * Copyright (c) 2019, 2021, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2022, Huawei Technologies Co., Ltd. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ * ++ */ ++ + #include "precompiled.hpp" + #include "memory/metaspaceClosure.hpp" + +diff --git a/hotspot/src/share/vm/memory/metaspaceClosure.hpp b/hotspot/src/share/vm/memory/metaspaceClosure.hpp +index f67d8d6f..5422e2a0 100644 +--- a/hotspot/src/share/vm/memory/metaspaceClosure.hpp ++++ b/hotspot/src/share/vm/memory/metaspaceClosure.hpp +@@ -1,4 +1,27 @@ +- ++/* ++ * Copyright (c) 2019, 2021, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2022, Huawei Technologies Co., Ltd. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ * ++ */ + + #ifndef SHARE_VM_MEMORY_METASPACECLOSURE_HPP + #define SHARE_VM_MEMORY_METASPACECLOSURE_HPP +diff --git a/hotspot/src/share/vm/oops/cpCache.cpp b/hotspot/src/share/vm/oops/cpCache.cpp +index 51f5397b..874cef41 100644 +--- a/hotspot/src/share/vm/oops/cpCache.cpp ++++ b/hotspot/src/share/vm/oops/cpCache.cpp +@@ -610,7 +610,6 @@ void ConstantPoolCache::metaspace_pointers_do(MetaspaceClosure* it) { + dynamic_cds_log->print_cr("Iter(ConstantPoolCache): %p", this); + } + it->push(&_constant_pool); +- // it->push(&_reference_map); + } + + void ConstantPoolCache::remove_unshareable_info() { +diff --git a/hotspot/src/share/vm/oops/instanceKlass.cpp b/hotspot/src/share/vm/oops/instanceKlass.cpp +index 9276b895..2a9cd92d 100644 +--- a/hotspot/src/share/vm/oops/instanceKlass.cpp ++++ b/hotspot/src/share/vm/oops/instanceKlass.cpp +@@ -526,10 +526,6 @@ void InstanceKlass::metaspace_pointers_do(MetaspaceClosure* it) { + } + } + } +- +- // it->push(&_nest_members); +- // it->push(&_permitted_subclasses); +- // it->push(&_record_components); + } + + klassVtable* InstanceKlass::vtable() const { +diff --git a/hotspot/test/runtime/6929067/Test6929067.sh b/hotspot/test/runtime/6929067/Test6929067.sh +index 438a287c..c78e1787 100644 +--- a/hotspot/test/runtime/6929067/Test6929067.sh ++++ b/hotspot/test/runtime/6929067/Test6929067.sh +@@ -102,7 +102,7 @@ esac + + + if [ "${VM_CPU}" == "aarch64" ]; then +- COMP_FLAG="-mabi=lp64" ++ COMP_FLAG="" + fi + + # VM type: need to know server or client +diff --git a/hotspot/test/runtime/InitialThreadOverflow/testme.sh b/hotspot/test/runtime/InitialThreadOverflow/testme.sh +index ffd7d6e3..cf48c2fe 100644 +--- a/hotspot/test/runtime/InitialThreadOverflow/testme.sh ++++ b/hotspot/test/runtime/InitialThreadOverflow/testme.sh +@@ -52,7 +52,7 @@ fi + CFLAGS="-m${VM_BITS}" + + if [ "${VM_CPU}" == "aarch64" ]; then +- CFLAGS="-mabi=lp64" ++ CFLAGS="" + fi + + LD_LIBRARY_PATH=.:${COMPILEJAVA}/jre/lib/${VM_CPU}/${VM_TYPE}:/usr/lib:$LD_LIBRARY_PATH +diff --git a/hotspot/test/runtime/Thread/StopAtExit.java b/hotspot/test/runtime/Thread/StopAtExit.java +new file mode 100644 +index 00000000..8d6344a6 +--- /dev/null ++++ b/hotspot/test/runtime/Thread/StopAtExit.java +@@ -0,0 +1,119 @@ ++/* ++ * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ */ ++ ++/** ++ * @test ++ * @bug 8167108 ++ * @summary Stress test java.lang.Thread.stop() at thread exit. ++ * @run main/othervm StopAtExit ++ */ ++ ++import java.util.concurrent.CountDownLatch; ++import java.util.concurrent.TimeUnit; ++ ++public class StopAtExit extends Thread { ++ final static int N_THREADS = 32; ++ final static int N_LATE_CALLS = 1000; ++ ++ public CountDownLatch exitSyncObj = new CountDownLatch(1); ++ public CountDownLatch startSyncObj = new CountDownLatch(1); ++ ++ @Override ++ public void run() { ++ try { ++ // Tell main thread we have started. ++ startSyncObj.countDown(); ++ try { ++ // Wait for main thread to interrupt us so we ++ // can race to exit. ++ exitSyncObj.await(); ++ } catch (InterruptedException e) { ++ // ignore because we expect one ++ } ++ } catch (ThreadDeath td) { ++ // ignore because we're testing Thread.stop() which throws it ++ } catch (NoClassDefFoundError ncdfe) { ++ // ignore because we're testing Thread.stop() which can cause it ++ } ++ } ++ ++ public static void main(String[] args) { ++ StopAtExit threads[] = new StopAtExit[N_THREADS]; ++ ++ for (int i = 0; i < N_THREADS; i++ ) { ++ threads[i] = new StopAtExit(); ++ int late_count = 1; ++ threads[i].start(); ++ try { ++ // Wait for the worker thread to get going. ++ threads[i].startSyncObj.await(); ++ ++ // This interrupt() call will break the worker out ++ // of the exitSyncObj.await() call and the stop() ++ // calls will come in during thread exit. ++ threads[i].interrupt(); ++ for (; late_count <= N_LATE_CALLS; late_count++) { ++ threads[i].stop(); ++ ++ if (!threads[i].isAlive()) { ++ // Done with Thread.stop() calls since ++ // thread is not alive. ++ break; ++ } ++ } ++ } catch (InterruptedException e) { ++ throw new Error("Unexpected: " + e); ++ } catch (NoClassDefFoundError ncdfe) { ++ // Ignore because we're testing Thread.stop() which can ++ // cause it. Yes, a NoClassDefFoundError that happens ++ // in a worker thread can subsequently be seen in the ++ // main thread. ++ } ++ ++ System.out.println("INFO: thread #" + i + ": made " + late_count + ++ " late calls to java.lang.Thread.stop()"); ++ System.out.println("INFO: thread #" + i + ": N_LATE_CALLS==" + ++ N_LATE_CALLS + " value is " + ++ ((late_count >= N_LATE_CALLS) ? "NOT " : "") + ++ "large enough to cause a Thread.stop() " + ++ "call after thread exit."); ++ ++ try { ++ threads[i].join(); ++ } catch (InterruptedException e) { ++ throw new Error("Unexpected: " + e); ++ } ++ threads[i].stop(); ++ if (threads[i].isAlive()) { ++ throw new Error("Expected !Thread.isAlive() after thread #" + ++ i + " has been join()'ed"); ++ } ++ } ++ ++ String cmd = System.getProperty("sun.java.command"); ++ if (cmd != null && !cmd.startsWith("com.sun.javatest.regtest.agent.MainWrapper")) { ++ // Exit with success in a non-JavaTest environment: ++ System.exit(0); ++ } ++ } ++} +diff --git a/jdk/make/profile-rtjar-includes.txt b/jdk/make/profile-rtjar-includes.txt +index f36d1d5c..dd275590 100644 +--- a/jdk/make/profile-rtjar-includes.txt ++++ b/jdk/make/profile-rtjar-includes.txt +@@ -73,8 +73,8 @@ PROFILE_1_RTJAR_INCLUDE_PACKAGES := \ + + PROFILE_1_RTJAR_INCLUDE_TYPES := + +-PROFILE_1_RTJAR_EXCLUDE_TYPES := +- ++PROFILE_1_RTJAR_EXCLUDE_TYPES := \ ++ com/huawei + PROFILE_1_INCLUDE_METAINF_SERVICES := + + +@@ -99,7 +99,8 @@ PROFILE_2_RTJAR_INCLUDE_PACKAGES := \ + + PROFILE_2_RTJAR_INCLUDE_TYPES := + +-PROFILE_2_RTJAR_EXCLUDE_TYPES := ++PROFILE_2_RTJAR_EXCLUDE_TYPES := \ ++ com/huawei + + PROFILE_2_INCLUDE_METAINF_SERVICES := \ + META-INF/services/sun.util.spi.XmlPropertiesProvider +diff --git a/jdk/src/share/classes/java/io/ObjectInputStream.java b/jdk/src/share/classes/java/io/ObjectInputStream.java +index af6c5dd6..85e3958b 100644 +--- a/jdk/src/share/classes/java/io/ObjectInputStream.java ++++ b/jdk/src/share/classes/java/io/ObjectInputStream.java +@@ -768,7 +768,7 @@ public class ObjectInputStream + * Cache the class meta during serialization. + * Only used in FastSerilizer. + */ +- protected static ConcurrentHashMap> nameToClass = new ConcurrentHashMap<>(); ++ private static ConcurrentHashMap> nameToClass = new ConcurrentHashMap<>(); + + /** + * Load the local class equivalent of the specified stream class +@@ -1013,7 +1013,7 @@ public class ObjectInputStream + + if (s0 != STREAM_MAGIC) { + throw new StreamCorruptedException( +- String.format("invalid stream header: %04X%04X, and FastSerializer is activated", s0, s1)); ++ String.format("invalid stream header: %04X%04X", s0, s1)); + } + + if (!fastSerializerEscapeMode) { +diff --git a/jdk/src/share/classes/java/io/ObjectOutputStream.java b/jdk/src/share/classes/java/io/ObjectOutputStream.java +index 840f7fdc..23c1fff5 100644 +--- a/jdk/src/share/classes/java/io/ObjectOutputStream.java ++++ b/jdk/src/share/classes/java/io/ObjectOutputStream.java +@@ -234,11 +234,6 @@ public class ObjectOutputStream + new sun.security.action.GetBooleanAction( + "sun.io.serialization.extendedDebugInfo")).booleanValue(); + +- /** +- * Magic number that is written to the stream header when using fastserilizer. +- */ +- private static final short STREAM_MAGIC_FAST = (short)0xdeca; +- + private static final Unsafe UNSAFE = Unsafe.getUnsafe(); + + /** +@@ -255,6 +250,11 @@ public class ObjectOutputStream + new sun.security.action.GetBooleanAction( + "printFastSerializer")).booleanValue(); + ++ /** ++ * Magic number that is written to the stream header when using fastserilizer. ++ */ ++ private static final short STREAM_MAGIC_FAST = (short)0xdeca; ++ + /** + * Creates an ObjectOutputStream that writes to the specified OutputStream. + * This constructor writes the serialization stream header to the +diff --git a/jdk/src/share/classes/sun/awt/FontConfiguration.java b/jdk/src/share/classes/sun/awt/FontConfiguration.java +index 93e38e06..c2e94d15 100644 +--- a/jdk/src/share/classes/sun/awt/FontConfiguration.java ++++ b/jdk/src/share/classes/sun/awt/FontConfiguration.java +@@ -300,12 +300,7 @@ public abstract class FontConfiguration { + } + } + foundOsSpecificFile = false; +- +- configFile = findImpl(baseName); +- if (configFile != null) { +- return configFile; +- } +- return null; ++ return (configFile = findImpl(baseName)); + } + + /* Initialize the internal data tables from binary format font +diff --git a/jdk/src/solaris/native/org/openeuler/security/openssl/kae_cipher_rsa.c b/jdk/src/solaris/native/org/openeuler/security/openssl/kae_cipher_rsa.c +index 73b94cbe..d9b16ab9 100644 +--- a/jdk/src/solaris/native/org/openeuler/security/openssl/kae_cipher_rsa.c ++++ b/jdk/src/solaris/native/org/openeuler/security/openssl/kae_cipher_rsa.c +@@ -174,6 +174,7 @@ static int RSACryptOAEPPadding(JNIEnv* env, jlong keyAddress, jint inLen, jbyteA + jbyte* inBytes = NULL; + // outLen type should be size_t + // EVP_PKEY_encrypt takes the outLen address as a parameter, and the parameter type is size_t* ++ // You can refer to the issue #2774 to see more content + size_t outLen = 0; + ENGINE* kaeEngine = GetEngineByAlgorithmIndex(RSA_INDEX); + KAE_TRACE("RSACryptOAEPPadding: kaeEngine => %p", kaeEngine); +@@ -366,7 +367,7 @@ JNIEXPORT jlong JNICALL Java_org_openeuler_security_openssl_KAERSACipher_nativeC + } + + // set rsa public key params n and e +- if(RSA_set0_key(rsa, bnN, bnE, NULL) <= 0) { ++ if (RSA_set0_key(rsa, bnN, bnE, NULL) <= 0) { + KAE_ThrowFromOpenssl(env, "RSA_set0_key", KAE_ThrowRuntimeException); + goto cleanup; + } +diff --git a/jdk/src/solaris/native/org/openeuler/security/openssl/kae_keyagreement_dh.c b/jdk/src/solaris/native/org/openeuler/security/openssl/kae_keyagreement_dh.c +index 90b33045..d8d2ee7c 100644 +--- a/jdk/src/solaris/native/org/openeuler/security/openssl/kae_keyagreement_dh.c ++++ b/jdk/src/solaris/native/org/openeuler/security/openssl/kae_keyagreement_dh.c +@@ -131,9 +131,9 @@ cleanup: + if (g_bn != NULL) + KAE_ReleaseBigNumFromByteArray(g_bn); + if (secret != NULL) +- free(secret); ++ free(secret); + if (computeKeyRetBn != NULL) +- BN_free(computeKeyRetBn); ++ BN_free(computeKeyRetBn); + + return retByteArray; + } +diff --git a/jdk/test/micro/org/openeuler/bench/security/openssl/RSAKeyPairGeneratorBenchmark.java b/jdk/test/micro/org/openeuler/bench/security/openssl/RSAKeyPairGeneratorBenchmark.java +index b1739222..13d3e8cf 100644 +--- a/jdk/test/micro/org/openeuler/bench/security/openssl/RSAKeyPairGeneratorBenchmark.java ++++ b/jdk/test/micro/org/openeuler/bench/security/openssl/RSAKeyPairGeneratorBenchmark.java +@@ -54,7 +54,7 @@ public class RSAKeyPairGeneratorBenchmark extends BenchmarkBase { + public KeyPair generateKeyPair() throws Exception { + return keyPairGenerator.generateKeyPair(); + } +- ++ + private KeyPairGenerator createKeyPairGenerator() throws Exception { + if (prov != null) { + return KeyPairGenerator.getInstance(algorithm, prov); +-- +2.22.0 + diff --git a/add-DumpSharedSpace-guarantee-when-create-anonymous-classes.patch b/add-DumpSharedSpace-guarantee-when-create-anonymous-classes.patch index 85ff56193248923a9450520c194454c86287486c..1a8df78fabbe1177bdc7d95f012ae212dce49756 100644 --- a/add-DumpSharedSpace-guarantee-when-create-anonymous-classes.patch +++ b/add-DumpSharedSpace-guarantee-when-create-anonymous-classes.patch @@ -17,7 +17,7 @@ index f20bf3d2b..3ab82c5c4 100644 + if (DumpSharedSpaces) { + tty->print_cr("failed: must not create anonymous classes when dumping."); -+ JVM_Exit(0); ++ JVM_Halt(0); + } + if (UsePerfData) { diff --git a/add-appcds-file-lock.patch b/add-appcds-file-lock.patch index f422f5469d2bfc53b87b2260b3cbb049f4b3e6f1..b34121e7c043dc09fc893d87ab4fbffb9e2928a5 100644 --- a/add-appcds-file-lock.patch +++ b/add-appcds-file-lock.patch @@ -224,7 +224,7 @@ index 17447587..d2095e63 100644 + tty->print_cr("The lock path is: %s", _appcds_file_lock_path); + tty->print_cr("Failed to create jsa file !\n Please check: \n 1. The directory exists.\n " + "2. You have the permission.\n 3. Make sure no other process using the same lock file.\n"); -+ JVM_Exit(0); ++ JVM_Halt(0); + } + tty->print_cr("You are using file lock %s in concurrent mode", AppCDSLockFile); + } diff --git a/add-configuration-option-of-huawei-internal-version-shown-in-release-file.patch b/add-configuration-option-of-huawei-internal-version-shown-in-release-file.patch new file mode 100644 index 0000000000000000000000000000000000000000..cb2c2625ba5fd3ddb226ba447323b69d9431a2aa --- /dev/null +++ b/add-configuration-option-of-huawei-internal-version-shown-in-release-file.patch @@ -0,0 +1,111 @@ +From 59040a3951dfdf21ba646cc9510739f175751469 Mon Sep 17 00:00:00 2001 +Date: Wed, 21 Sep 2022 09:54:04 +0800 +Subject: [PATCH 2/5] add configuration option of huawei internal version shown in release file + +--- + common/autoconf/generated-configure.sh | 17 +++++++++++++++++ + common/autoconf/jdk-options.m4 | 11 +++++++++++ + common/autoconf/spec.gmk.in | 3 +++ + jdk/make/Images.gmk | 1 + + 4 files changed, 32 insertions(+) + +diff --git a/common/autoconf/generated-configure.sh b/common/autoconf/generated-configure.sh +index 53e6cf18..a6ba1ac9 100644 +--- a/common/autoconf/generated-configure.sh ++++ b/common/autoconf/generated-configure.sh +@@ -831,6 +831,7 @@ COPYRIGHT_YEAR + VENDOR_URL_VM_BUG + VENDOR_URL_BUG + VENDOR_URL ++INTERNAL_VERSION + COMPANY_NAME + MACOSX_BUNDLE_ID_BASE + MACOSX_BUNDLE_NAME_BASE +@@ -1077,6 +1078,7 @@ with_vendor_url + with_vendor_bug_url + with_vendor_vm_bug_url + with_copyright_year ++with_internal_version + with_boot_jdk + with_boot_jdk_jvmargs + with_add_source_root +@@ -1937,6 +1939,9 @@ Optional Packages: + --with-vendor-vm-bug-url + Sets the bug URL which will be displayed when the VM + crashes [not specified] ++ --with-internal-version ++ Sets the internal version which will be ++ displayed in the release file [not specified] + --with-copyright-year Set copyright year value for build [current year] + --with-boot-jdk path to Boot JDK (used to bootstrap build) [probed] + --with-boot-jdk-jvmargs specify JVM arguments to be passed to all +@@ -20301,6 +20306,18 @@ fi + COPYRIGHT_YEAR=`date +'%Y'` + fi + ++# Check whether --with-internal-version was given. ++if test "${with_internal_version+set}" = set; then : ++ withval=$with_internal_version; ++fi ++ ++ if test "x$with_internal_version" = xyes; then ++ as_fn_error $? "--with-internal-version must have a value" "$LINENO" 5 ++ elif ! [[ $with_internal_version =~ ^[[:print:]]*$ ]] ; then ++ as_fn_error $? "--with-internal-version contains non-printing characters: $with_internal_version" "$LINENO" 5 ++ else ++ INTERNAL_VERSION="$with_internal_version" ++ fi + + if test "x$JDK_UPDATE_VERSION" != x; then + JDK_VERSION="${JDK_MAJOR_VERSION}.${JDK_MINOR_VERSION}.${JDK_MICRO_VERSION}_${JDK_UPDATE_VERSION}" +diff --git a/common/autoconf/jdk-options.m4 b/common/autoconf/jdk-options.m4 +index c506086d..b9f25175 100644 +--- a/common/autoconf/jdk-options.m4 ++++ b/common/autoconf/jdk-options.m4 +@@ -627,6 +627,17 @@ AC_DEFUN_ONCE([JDKOPT_SETUP_JDK_VERSION_NUMBERS], + fi + AC_SUBST(COPYRIGHT_YEAR) + ++ AC_ARG_WITH(internal-version, [AS_HELP_STRING([--with-internal-version], ++ [Sets the internal version which will be displayed in the release file @<:@not specified@:>@])]) ++ if test "x$with_internal_version" = xyes; then ++ AC_MSG_ERROR([--with-internal-version must have a value]) ++ elif [ ! [[ $with_internal_version =~ ^[[:print:]]*$ ]] ]; then ++ AC_MSG_ERROR([--with-internal-version contains non-printing characters: $with_internal_version]) ++ else ++ INTERNAL_VERSION="$with_internal_version" ++ fi ++ AC_SUBST(INTERNAL_VERSION) ++ + if test "x$JDK_UPDATE_VERSION" != x; then + JDK_VERSION="${JDK_MAJOR_VERSION}.${JDK_MINOR_VERSION}.${JDK_MICRO_VERSION}_${JDK_UPDATE_VERSION}" + else +diff --git a/common/autoconf/spec.gmk.in b/common/autoconf/spec.gmk.in +index 79248cbf..ca5e2d74 100644 +--- a/common/autoconf/spec.gmk.in ++++ b/common/autoconf/spec.gmk.in +@@ -162,6 +162,9 @@ VENDOR_URL:=@VENDOR_URL@ + VENDOR_URL_BUG:=@VENDOR_URL_BUG@ + VENDOR_URL_VM_BUG:=@VENDOR_URL_VM_BUG@ + ++# Huawei internal version for use in release file. ++INTERNAL_VERSION:=@INTERNAL_VERSION@ ++ + # Location where build customization files may be found + CUSTOM_MAKE_DIR:=@CUSTOM_MAKE_DIR@ + +diff --git a/jdk/make/Images.gmk b/jdk/make/Images.gmk +index ac39ad33..233ce703 100644 +--- a/jdk/make/Images.gmk ++++ b/jdk/make/Images.gmk +@@ -618,6 +618,7 @@ define create-info-file + $(call info-file-item, "OS_ARCH", "$(OPENJDK_TARGET_CPU_LEGACY)") + if [ -n "$(JDK_ARCH_ABI_PROP_NAME)" ]; then $(call info-file-item, "SUN_ARCH_ABI", "$(JDK_ARCH_ABI_PROP_NAME)"); fi + $(call info-file-item, "SOURCE", "$(strip $(SOURCE_REVISION))") ++ if [ -n "$(INTERNAL_VERSION)" ]; then $(call info-file-item, "INTERNAL_VERSION", "$(INTERNAL_VERSION)"); fi + endef + + SOURCE_REVISION = $(shell \ +-- +2.22.0 + diff --git a/add-header-file-for-LoongArch64.patch b/add-header-file-for-LoongArch64.patch new file mode 100644 index 0000000000000000000000000000000000000000..34cdcca6adedc8423bd716790768f6fb9cee62b0 --- /dev/null +++ b/add-header-file-for-LoongArch64.patch @@ -0,0 +1,14 @@ +diff --git a/hotspot/src/share/vm/jfr/writers/jfrEncoders.hpp b/hotspot/src/share/vm/jfr/writers/jfrEncoders.hpp +index 42a8b719..b69f3b2c 100644 +--- a/hotspot/src/share/vm/jfr/writers/jfrEncoders.hpp ++++ b/hotspot/src/share/vm/jfr/writers/jfrEncoders.hpp +@@ -46,6 +46,9 @@ + #ifdef TARGET_ARCH_aarch64 + # include "bytes_aarch64.hpp" + #endif ++#ifdef TARGET_ARCH_loongarch ++# include "bytes_loongarch.hpp" ++#endif + + // + // The Encoding policy prescribes a template diff --git a/add-missing-test-case.patch b/add-missing-test-case.patch index e432247c8161497406d216d9d8cf56f375419caf..ef14a15d5ccea62fb9bef0a245a41ce851f6d82e 100644 --- a/add-missing-test-case.patch +++ b/add-missing-test-case.patch @@ -125,7 +125,7 @@ index 00000000..9b614024 --- /dev/null +++ b/version.txt @@ -0,0 +1 @@ -+8.292.8.0.13 ++8.352.8.0.13 -- 2.23.0 diff --git a/change-sa-jdi.jar-make-file-for-BEP.PATCH b/change-sa-jdi.jar-make-file-for-BEP.PATCH new file mode 100644 index 0000000000000000000000000000000000000000..21db586a7f5c20349f465230df7a43b40cd33bf0 --- /dev/null +++ b/change-sa-jdi.jar-make-file-for-BEP.PATCH @@ -0,0 +1,38 @@ +From 980b919fde4e1353a9ff989fb78031a48d395ec0 Mon Sep 17 00:00:00 2001 +From: zhangyipeng +Date: Fri, 6 May 2022 15:23:26 +0800 +Subject: [PATCH 02/10] change sa-jdi.jar make file for BEP + +--- + hotspot/make/linux/makefiles/sa.make | 8 ++++++-- + 1 file changed, 6 insertions(+), 2 deletions(-) + +diff --git a/hotspot/make/linux/makefiles/sa.make b/hotspot/make/linux/makefiles/sa.make +index cdcb16a1a..6388d95c9 100644 +--- a/hotspot/make/linux/makefiles/sa.make ++++ b/hotspot/make/linux/makefiles/sa.make +@@ -50,6 +50,7 @@ SA_CLASSPATH = $(BOOT_JAVA_HOME)/lib/tools.jar + MODULELIB_PATH= $(BOOT_JAVA_HOME)/lib/modules + + AGENT_FILES_LIST := $(GENERATED)/agent.classes.list ++SA_CLASSDIR_JAR_CONTENTS := $(GENERATED)/sa.jar_contents + + SA_CLASSDIR = $(GENERATED)/saclasses + +@@ -104,8 +105,11 @@ $(GENERATED)/sa-jdi.jar:: $(AGENT_FILES) + $(QUIETLY) rm -f $(SA_CLASSDIR)/sun/jvm/hotspot/ui/resources/* + $(QUIETLY) cp $(AGENT_SRC_DIR)/sun/jvm/hotspot/ui/resources/*.png $(SA_CLASSDIR)/sun/jvm/hotspot/ui/resources/ + $(QUIETLY) cp -r $(AGENT_SRC_DIR)/images/* $(SA_CLASSDIR)/ +- $(QUIETLY) $(REMOTE) $(RUN.JAR) cf $@ -C $(SA_CLASSDIR)/ . +- $(QUIETLY) $(REMOTE) $(RUN.JAR) uf $@ -C $(AGENT_SRC_DIR) META-INF/services/com.sun.jdi.connect.Connector ++ $(QUIETLY) rm -f $(SA_CLASSDIR_JAR_CONTENTS) && touch $(SA_CLASSDIR_JAR_CONTENTS) ++ $(QUIETLY) find $(SA_CLASSDIR) -type f | sed 's|$(SA_CLASSDIR)/||g' >> $(SA_CLASSDIR_JAR_CONTENTS) ++ $(QUIETLY) cd $(AGENT_SRC_DIR) && $(REMOTE) $(RUN.JAR) cf $@ META-INF/services/com.sun.jdi.connect.Connector ++ $(QUIETLY) cd $(SA_CLASSDIR) && $(REMOTE) $(RUN.JAR) uf $@ @$(SA_CLASSDIR_JAR_CONTENTS) ++ $(QUIETLY) cd $(TOPDIR) + $(QUIETLY) $(REMOTE) $(RUN.JAVAH) -classpath $(SA_CLASSDIR) -d $(GENERATED) -jni sun.jvm.hotspot.debugger.x86.X86ThreadContext + $(QUIETLY) $(REMOTE) $(RUN.JAVAH) -classpath $(SA_CLASSDIR) -d $(GENERATED) -jni sun.jvm.hotspot.debugger.amd64.AMD64ThreadContext + $(QUIETLY) $(REMOTE) $(RUN.JAVAH) -classpath $(SA_CLASSDIR) -d $(GENERATED) -jni sun.jvm.hotspot.debugger.aarch64.AARCH64ThreadContext +-- +2.22.0 + diff --git a/cve-2022-37434-Fix-a-bug-when-getting-a-gzip-header-extra-field-with-inflate.patch b/cve-2022-37434-Fix-a-bug-when-getting-a-gzip-header-extra-field-with-inflate.patch new file mode 100644 index 0000000000000000000000000000000000000000..f02cbb3dc8f9ba437e6677f522b5922032eb5212 --- /dev/null +++ b/cve-2022-37434-Fix-a-bug-when-getting-a-gzip-header-extra-field-with-inflate.patch @@ -0,0 +1,30 @@ +From fa03b567552ecc1a2a91850c959220ab28f178dd Mon Sep 17 00:00:00 2001 +From: yangyudong +Date: Fri, 21 Oct 2022 12:02:55 +0800 +Subject: cve-2022-37434: Fix a bug when getting a gzip header extra + field with inflate(). + +Bug url: https://bugzilla.redhat.com/show_bug.cgi?id=CVE-2022-37434 +--- + jdk/src/share/native/java/util/zip/zlib/inflate.c | 5 +++-- + 1 file changed, 3 insertions(+), 2 deletions(-) + +diff --git a/jdk/src/share/native/java/util/zip/zlib/inflate.c b/jdk/src/share/native/java/util/zip/zlib/inflate.c +index ca904e744..63decdb19 100644 +--- a/jdk/src/share/native/java/util/zip/zlib/inflate.c ++++ b/jdk/src/share/native/java/util/zip/zlib/inflate.c +@@ -783,8 +783,9 @@ int flush; + if (copy > have) copy = have; + if (copy) { + if (state->head != Z_NULL && +- state->head->extra != Z_NULL) { +- len = state->head->extra_len - state->length; ++ state->head->extra != Z_NULL && ++ (len = state->head->extra_len - state->length) < ++ state->head->extra_max) { + zmemcpy(state->head->extra + len, next, + len + copy > state->head->extra_max ? + state->head->extra_max - len : copy); +-- +2.22.0 + diff --git a/debuginfo.diz-should-not-contain-the-path-after-unzip.patch b/debuginfo.diz-should-not-contain-the-path-after-unzip.patch deleted file mode 100755 index 1f49d5f7761b0de3ef03d69f94a126e675408603..0000000000000000000000000000000000000000 --- a/debuginfo.diz-should-not-contain-the-path-after-unzip.patch +++ /dev/null @@ -1,16 +0,0 @@ -diff --git a/make/common/NativeCompilation.gmk b/make/common/NativeCompilation.gmk -index 0e0346374..2d9bdbeed 100644 ---- a/make/common/NativeCompilation.gmk -+++ b/make/common/NativeCompilation.gmk -@@ -537,7 +537,7 @@ define SetupNativeCompilation - # to be rebuilt properly. - $$($1_DEBUGINFO_ZIP): $$($1_DEBUGINFO_FILES) $$($1_TARGET) - $(CD) $$($1_OBJECT_DIR) \ -- && $(ZIP) -q $$@ $$($1_DEBUGINFO_FILES) -+ && $(ZIP) -q $$@ $$(subst $$($1_OBJECT_DIR)/,,$$($1_DEBUGINFO_FILES)) - endif - else - ifneq ($$($1_STRIP_POLICY), no_strip) --- -2.22.0 - diff --git a/dynamic-cds-_header-and-_fd-handles-are-not-free.patch b/dynamic-cds-_header-and-_fd-handles-are-not-free.patch new file mode 100644 index 0000000000000000000000000000000000000000..e11210e9af5b4197ea704838d66ba9685d264177 --- /dev/null +++ b/dynamic-cds-_header-and-_fd-handles-are-not-free.patch @@ -0,0 +1,34 @@ +From cf12a2fae11baf41773308a48d9cfad9031f5344 Mon Sep 17 00:00:00 2001 +Date: Fri, 9 Sep 2022 11:26:22 +0800 +Subject: dynamic cds _header and _fd handles are not free. + +--- + hotspot/src/share/vm/memory/filemap.cpp | 12 ++++++++++++ + 1 file changed, 12 insertions(+) + +diff --git a/hotspot/src/share/vm/memory/filemap.cpp b/hotspot/src/share/vm/memory/filemap.cpp +index adb043f77..6549828e3 100644 +--- a/hotspot/src/share/vm/memory/filemap.cpp ++++ b/hotspot/src/share/vm/memory/filemap.cpp +@@ -169,6 +169,18 @@ FileMapInfo::~FileMapInfo() { + assert(_dynamic_archive_info == this, "must be singleton"); // not thread safe + _dynamic_archive_info = NULL; + } ++ ++ if (_header != NULL) { ++ delete _header; ++ } ++ ++ if (_file_open) { ++ if (::close(_fd) < 0) { ++ fail_stop("Unable to close the shared archive file."); ++ } ++ _file_open = false; ++ _fd = -1; ++ } + } + + void FileMapInfo::populate_header(size_t alignment) { +-- +2.22.0 + diff --git a/fix-appcds-s-option-AppCDSLockFile.patch b/fix-appcds-s-option-AppCDSLockFile.patch index 37eae2257c45f2bcc66a120258c935f344c009cd..88b6f4df2562877f78cb56b6e0b43672a8a3db71 100755 --- a/fix-appcds-s-option-AppCDSLockFile.patch +++ b/fix-appcds-s-option-AppCDSLockFile.patch @@ -33,7 +33,7 @@ index 5858c9355..99b1f58d0 100644 - tty->print_cr("The lock path is: %s", _appcds_file_lock_path); tty->print_cr("Failed to create jsa file !\n Please check: \n 1. The directory exists.\n " "2. You have the permission.\n 3. Make sure no other process using the same lock file.\n"); -- JVM_Exit(0); +- JVM_Halt(0); + fail_stop("Failed to create appcds lock file, the lock path is: %s.", _appcds_file_lock_path); } tty->print_cr("You are using file lock %s in concurrent mode", AppCDSLockFile); diff --git a/fix-dumped-heap-using-jhat-parsing-to-appear-failed-to-resolve-object-id-warning-message.patch b/fix-dumped-heap-using-jhat-parsing-to-appear-failed-to-resolve-object-id-warning-message.patch new file mode 100644 index 0000000000000000000000000000000000000000..f26dd692779fa3a48d12f78555f4ec509acb49d9 --- /dev/null +++ b/fix-dumped-heap-using-jhat-parsing-to-appear-failed-to-resolve-object-id-warning-message.patch @@ -0,0 +1,113 @@ +From 68293d50de005b5982a3cce437fc7af807d7264e Mon Sep 17 00:00:00 2001 +Date: Wed, 14 Sep 2022 14:57:25 +0800 +Subject: fix dumped heap using jhat parsing to appear failed to + resolve object id warning message + +--- + hotspot/src/share/vm/services/heapDumper.cpp | 5 -- + .../serviceability/dcmd/gc/HeapDumpTest.java | 77 +++++++++++++++++++ + 2 files changed, 77 insertions(+), 5 deletions(-) + create mode 100644 jdk/test/serviceability/dcmd/gc/HeapDumpTest.java + +diff --git a/hotspot/src/share/vm/services/heapDumper.cpp b/hotspot/src/share/vm/services/heapDumper.cpp +index f7aba2a84..b5915c412 100644 +--- a/hotspot/src/share/vm/services/heapDumper.cpp ++++ b/hotspot/src/share/vm/services/heapDumper.cpp +@@ -984,11 +984,6 @@ void DumperSupport::dump_class_and_array_classes(DumpWriter* writer, Klass* k) { + return; + } + +- // Ignore the class if it hasn't been initialized yet +- if (!ik->is_linked()) { +- return; +- } +- + writer->write_u1(HPROF_GC_CLASS_DUMP); + + // class ID +diff --git a/jdk/test/serviceability/dcmd/gc/HeapDumpTest.java b/jdk/test/serviceability/dcmd/gc/HeapDumpTest.java +new file mode 100644 +index 000000000..7204c2c37 +--- /dev/null ++++ b/jdk/test/serviceability/dcmd/gc/HeapDumpTest.java +@@ -0,0 +1,77 @@ ++/* ++ * Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ */ ++import org.testng.annotations.Test; ++import org.testng.Assert; ++import java.io.File; ++import java.nio.file.Files; ++import java.io.IOException; ++import java.util.List; ++import jdk.test.lib.hprof.HprofParser; ++import jdk.test.lib.hprof.model.Snapshot; ++import jdk.test.lib.JDKToolFinder; ++import jdk.test.lib.process.OutputAnalyzer; ++import jdk.test.lib.dcmd.CommandExecutor; ++import jdk.test.lib.dcmd.PidJcmdExecutor; ++/* ++ * @test ++ * @summary Test of diagnostic command GC.heap_dump ++ * @library /lib ++ * @run testng HeapDumpTest ++ */ ++public class HeapDumpTest { ++ protected String heapDumpArgs = ""; ++ ++ public void run(CommandExecutor executor) throws IOException { ++ File dump = new File("jcmd.gc.heap_dump." + System.currentTimeMillis() + ".hprof"); ++ if (dump.exists()) { ++ dump.delete(); ++ } ++ ++ String cmd = "GC.heap_dump " + heapDumpArgs + " " + dump.getAbsolutePath(); ++ executor.execute(cmd); ++ ++ verifyHeapDump(dump); ++ dump.delete(); ++ } ++ private void verifyHeapDump(File dump) { ++ Assert.assertTrue(dump.exists() && dump.isFile(), "Could not create dump file " + dump.getAbsolutePath()); ++ try { ++ File out = HprofParser.parse(dump); ++ Assert.assertTrue(out != null && out.exists() && out.isFile(), "Could not find hprof parser output file"); ++ List lines = Files.readAllLines(out.toPath()); ++ Assert.assertTrue(lines.size() > 0, "hprof parser output file is empty"); ++ for (String line : lines) { ++ Assert.assertFalse(line.matches(".*WARNING(?!.*Failed to resolve object.*constantPoolOop.*).*")); ++ } ++ out.delete(); ++ } catch (Exception e) { ++ e.printStackTrace(); ++ Assert.fail("Could not parse dump file " + dump.getAbsolutePath()); ++ } ++ } ++ /* GC.heap_dump is not available over JMX, running jcmd pid executor instead */ ++ @Test ++ public void pid() throws IOException { ++ run(new PidJcmdExecutor()); ++ } ++} +-- +2.22.0 + diff --git a/fix-log-bug-enhance-aes-hmac-performance.patch b/fix-log-bug-enhance-aes-hmac-performance.patch index 48da9a117e036e931e20ab491019986206550437..6f42559b23943815edc84ecea5f6e3e1b93d3310 100644 --- a/fix-log-bug-enhance-aes-hmac-performance.patch +++ b/fix-log-bug-enhance-aes-hmac-performance.patch @@ -17,38 +17,10 @@ Signed-off-by: He Dongbo create mode 100644 jdk/test/micro/org/openeuler/bench/security/openssl/HMacBenchmark.java diff --git a/common/autoconf/generated-configure.sh b/common/autoconf/generated-configure.sh -index 60298422f..bdfdd207b 100644 +index 27cff542..d19c772e 100644 --- a/common/autoconf/generated-configure.sh +++ b/common/autoconf/generated-configure.sh -@@ -4288,7 +4288,7 @@ TOOLCHAIN_DESCRIPTION_xlc="IBM XL C/C++" - - ################################################################################ - # The order of these defines the priority by which we try to find them. --VALID_VS_VERSIONS="2010 2012 2013 2015 2017" -+VALID_VS_VERSIONS="2010 2012 2013 2015 2017 2019" - - VS_DESCRIPTION_2010="Microsoft Visual Studio 2010" - VS_VERSION_INTERNAL_2010=100 -@@ -4346,6 +4346,18 @@ VS_SDK_INSTALLDIR_2017= - VS_VS_PLATFORM_NAME_2017="v141" - VS_SDK_PLATFORM_NAME_2017= - -+VS_DESCRIPTION_2019="Microsoft Visual Studio 2019 - CURRENTLY NOT WORKING" -+VS_VERSION_INTERNAL_2019=141 -+VS_MSVCR_2019=vcruntime140.dll -+VS_MSVCP_2019=msvcp140.dll -+VS_ENVVAR_2019="VS150COMNTOOLS" -+VS_USE_UCRT_2019="true" -+VS_VS_INSTALLDIR_2019="Microsoft Visual Studio/2019" -+VS_EDITIONS_2019="Community Professional Enterprise" -+VS_SDK_INSTALLDIR_2019= -+VS_VS_PLATFORM_NAME_2019="v141" -+VS_SDK_PLATFORM_NAME_2019= -+ - ################################################################################ - - -@@ -25694,10 +25706,10 @@ $as_echo "$as_me: Valid Visual Studio versions: $VALID_VS_VERSIONS." >&6;} +@@ -25937,10 +25937,10 @@ $as_echo "$as_me: Valid Visual Studio versions: $VALID_VS_VERSIONS." >&6;} { $as_echo "$as_me:${as_lineno-$LINENO}: Found Visual Studio installation at $VS_BASE using $METHOD" >&5 $as_echo "$as_me: Found Visual Studio installation at $VS_BASE using $METHOD" >&6;} if test "x$OPENJDK_TARGET_CPU_BITS" = x32; then @@ -61,7 +33,7 @@ index 60298422f..bdfdd207b 100644 fi for VCVARSFILE in $VCVARSFILES; do -@@ -25751,10 +25763,10 @@ $as_echo "$as_me: Warning: None of $VCVARSFILES were found, Visual Studio instal +@@ -25994,10 +25994,10 @@ $as_echo "$as_me: Warning: None of $VCVARSFILES were found, Visual Studio instal { $as_echo "$as_me:${as_lineno-$LINENO}: Found Visual Studio installation at $VS_BASE using $METHOD" >&5 $as_echo "$as_me: Found Visual Studio installation at $VS_BASE using $METHOD" >&6;} if test "x$OPENJDK_TARGET_CPU_BITS" = x32; then @@ -74,7 +46,7 @@ index 60298422f..bdfdd207b 100644 fi for VCVARSFILE in $VCVARSFILES; do -@@ -25790,8 +25802,6 @@ $as_echo "$as_me: directory within the Visual Studio installation" >&6;} +@@ -26033,8 +26033,6 @@ $as_echo "$as_me: directory within the Visual Studio installation" >&6;} fi fi @@ -83,7 +55,7 @@ index 60298422f..bdfdd207b 100644 if test "x$VS_COMNTOOLS" != x; then if test "x$VS_ENV_CMD" = x; then -@@ -25824,10 +25834,10 @@ $as_echo "$as_me: directory within the Visual Studio installation" >&6;} +@@ -26067,10 +26065,10 @@ $as_echo "$as_me: directory within the Visual Studio installation" >&6;} { $as_echo "$as_me:${as_lineno-$LINENO}: Found Visual Studio installation at $VS_BASE using $METHOD" >&5 $as_echo "$as_me: Found Visual Studio installation at $VS_BASE using $METHOD" >&6;} if test "x$OPENJDK_TARGET_CPU_BITS" = x32; then @@ -96,7 +68,7 @@ index 60298422f..bdfdd207b 100644 fi for VCVARSFILE in $VCVARSFILES; do -@@ -25883,10 +25893,10 @@ $as_echo "$as_me: Warning: None of $VCVARSFILES were found, Visual Studio instal +@@ -26126,10 +26124,10 @@ $as_echo "$as_me: Warning: None of $VCVARSFILES were found, Visual Studio instal { $as_echo "$as_me:${as_lineno-$LINENO}: Found Visual Studio installation at $VS_BASE using $METHOD" >&5 $as_echo "$as_me: Found Visual Studio installation at $VS_BASE using $METHOD" >&6;} if test "x$OPENJDK_TARGET_CPU_BITS" = x32; then @@ -109,7 +81,7 @@ index 60298422f..bdfdd207b 100644 fi for VCVARSFILE in $VCVARSFILES; do -@@ -25944,10 +25954,10 @@ $as_echo "$as_me: Warning: None of $VCVARSFILES were found, Visual Studio instal +@@ -26187,10 +26185,10 @@ $as_echo "$as_me: Warning: None of $VCVARSFILES were found, Visual Studio instal { $as_echo "$as_me:${as_lineno-$LINENO}: Found Visual Studio installation at $VS_BASE using $METHOD" >&5 $as_echo "$as_me: Found Visual Studio installation at $VS_BASE using $METHOD" >&6;} if test "x$OPENJDK_TARGET_CPU_BITS" = x32; then @@ -122,7 +94,7 @@ index 60298422f..bdfdd207b 100644 fi for VCVARSFILE in $VCVARSFILES; do -@@ -26002,10 +26012,10 @@ $as_echo "$as_me: Warning: None of $VCVARSFILES were found, Visual Studio instal +@@ -26245,10 +26243,10 @@ $as_echo "$as_me: Warning: None of $VCVARSFILES were found, Visual Studio instal { $as_echo "$as_me:${as_lineno-$LINENO}: Found Visual Studio installation at $VS_BASE using $METHOD" >&5 $as_echo "$as_me: Found Visual Studio installation at $VS_BASE using $METHOD" >&6;} if test "x$OPENJDK_TARGET_CPU_BITS" = x32; then @@ -135,7 +107,7 @@ index 60298422f..bdfdd207b 100644 fi for VCVARSFILE in $VCVARSFILES; do -@@ -26059,10 +26069,10 @@ $as_echo "$as_me: Warning: None of $VCVARSFILES were found, Visual Studio instal +@@ -26302,10 +26300,10 @@ $as_echo "$as_me: Warning: None of $VCVARSFILES were found, Visual Studio instal { $as_echo "$as_me:${as_lineno-$LINENO}: Found Visual Studio installation at $VS_BASE using $METHOD" >&5 $as_echo "$as_me: Found Visual Studio installation at $VS_BASE using $METHOD" >&6;} if test "x$OPENJDK_TARGET_CPU_BITS" = x32; then @@ -149,38 +121,10 @@ index 60298422f..bdfdd207b 100644 for VCVARSFILE in $VCVARSFILES; do diff --git a/common/autoconf/toolchain_windows.m4 b/common/autoconf/toolchain_windows.m4 -index a78f9ac66..0b5efdad2 100644 +index 9e617c33..f024da1e 100644 --- a/common/autoconf/toolchain_windows.m4 +++ b/common/autoconf/toolchain_windows.m4 -@@ -25,7 +25,7 @@ - - ################################################################################ - # The order of these defines the priority by which we try to find them. --VALID_VS_VERSIONS="2010 2012 2013 2015 2017" -+VALID_VS_VERSIONS="2010 2012 2013 2015 2017 2019" - - VS_DESCRIPTION_2010="Microsoft Visual Studio 2010" - VS_VERSION_INTERNAL_2010=100 -@@ -83,6 +83,18 @@ VS_SDK_INSTALLDIR_2017= - VS_VS_PLATFORM_NAME_2017="v141" - VS_SDK_PLATFORM_NAME_2017= - -+VS_DESCRIPTION_2019="Microsoft Visual Studio 2019 - CURRENTLY NOT WORKING" -+VS_VERSION_INTERNAL_2019=141 -+VS_MSVCR_2019=vcruntime140.dll -+VS_MSVCP_2019=msvcp140.dll -+VS_ENVVAR_2019="VS150COMNTOOLS" -+VS_USE_UCRT_2019="true" -+VS_VS_INSTALLDIR_2019="Microsoft Visual Studio/2019" -+VS_EDITIONS_2019="Community Professional Enterprise" -+VS_SDK_INSTALLDIR_2019= -+VS_VS_PLATFORM_NAME_2019="v141" -+VS_SDK_PLATFORM_NAME_2019= -+ - ################################################################################ - - AC_DEFUN([TOOLCHAIN_CHECK_POSSIBLE_VISUAL_STUDIO_ROOT], -@@ -107,10 +119,10 @@ AC_DEFUN([TOOLCHAIN_CHECK_POSSIBLE_VISUAL_STUDIO_ROOT], +@@ -121,10 +121,10 @@ AC_DEFUN([TOOLCHAIN_CHECK_POSSIBLE_VISUAL_STUDIO_ROOT], if test -d "$VS_BASE"; then AC_MSG_NOTICE([Found Visual Studio installation at $VS_BASE using $METHOD]) if test "x$OPENJDK_TARGET_CPU_BITS" = x32; then @@ -193,7 +137,7 @@ index a78f9ac66..0b5efdad2 100644 fi for VCVARSFILE in $VCVARSFILES; do -@@ -198,8 +210,6 @@ AC_DEFUN([TOOLCHAIN_FIND_VISUAL_STUDIO_BAT_FILE], +@@ -212,8 +212,6 @@ AC_DEFUN([TOOLCHAIN_FIND_VISUAL_STUDIO_BAT_FILE], fi fi diff --git a/fix-the-length-value-of-ciBlock-in-ciMethodBlocks.cp.patch b/fix-the-length-value-of-ciBlock-in-ciMethodBlocks.cp.patch new file mode 100644 index 0000000000000000000000000000000000000000..d1490858b052c1c8b8b231d470b42651663622f5 --- /dev/null +++ b/fix-the-length-value-of-ciBlock-in-ciMethodBlocks.cp.patch @@ -0,0 +1,23 @@ +From 102b398cc59e95cb4f5327b9c8fc9a3c5594acce Mon Sep 17 00:00:00 2001 +From: eapen +Date: Tue, 29 Nov 2022 09:23:01 +0800 +Subject: [PATCH 29/33] I68TO2: fix the length value of ciBlock in ciMethodBlocks.cpp +--- + hotspot/src/share/vm/ci/ciMethodBlocks.cpp | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/hotspot/src/share/vm/ci/ciMethodBlocks.cpp b/hotspot/src/share/vm/ci/ciMethodBlocks.cpp +index 614e75d..3ce828e 100644 +--- a/hotspot/src/share/vm/ci/ciMethodBlocks.cpp ++++ b/hotspot/src/share/vm/ci/ciMethodBlocks.cpp +@@ -372,7 +372,7 @@ static const char *flagnames[] = { + + void ciBlock::dump() { + tty->print(" [%d .. %d), {", _start_bci, _limit_bci); +- for (int i = 0; i < 8; i++) { ++ for (int i = 0; i < 7; i++) { + if ((_flags & (1 << i)) != 0) { + tty->print(" %s", flagnames[i]); + } +-- +1.8.3.1 diff --git a/fix-windows-compile-fail.patch b/fix-windows-compile-fail.patch index 196913dee9811bcbe2476244f2780047a6f7afc1..fcc90b7f7151a44431393a9839d168e16d1137e6 100644 --- a/fix-windows-compile-fail.patch +++ b/fix-windows-compile-fail.patch @@ -38,7 +38,7 @@ index 9cfa0451..170f1fd9 100644 if (match_option(option, "-XX:+UseAppCDS", &tail)) { +#ifndef __linux__ + tty->print_cr("failed: must not use AppCDS on non-linux system."); -+ JVM_Exit(0); ++ JVM_Halt(0); +#endif if (!process_argument("+UseAppCDS", args->ignoreUnrecognized, origin)) { return JNI_EINVAL; diff --git a/fix_X509TrustManagerImpl_symantec_distrust.patch b/fix_X509TrustManagerImpl_symantec_distrust.patch new file mode 100644 index 0000000000000000000000000000000000000000..5ff273d601590b1792daf6e352851a4fb7718929 --- /dev/null +++ b/fix_X509TrustManagerImpl_symantec_distrust.patch @@ -0,0 +1,77 @@ +diff --git a/jdk/make/data/cacerts/geotrustglobalca b/jdk/make/data/cacerts/geotrustglobalca +new file mode 100644 +index 000000000..7f8bf9a66 +--- /dev/null ++++ b/jdk/make/data/cacerts/geotrustglobalca +@@ -0,0 +1,27 @@ ++Owner: CN=GeoTrust Global CA, O=GeoTrust Inc., C=US ++Issuer: CN=GeoTrust Global CA, O=GeoTrust Inc., C=US ++Serial number: 23456 ++Valid from: Tue May 21 04:00:00 GMT 2002 until: Sat May 21 04:00:00 GMT 2022 ++Signature algorithm name: SHA1withRSA ++Subject Public Key Algorithm: 2048-bit RSA key ++Version: 3 ++-----BEGIN CERTIFICATE----- ++MIIDVDCCAjygAwIBAgIDAjRWMA0GCSqGSIb3DQEBBQUAMEIxCzAJBgNVBAYTAlVT ++MRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMRswGQYDVQQDExJHZW9UcnVzdCBHbG9i ++YWwgQ0EwHhcNMDIwNTIxMDQwMDAwWhcNMjIwNTIxMDQwMDAwWjBCMQswCQYDVQQG ++EwJVUzEWMBQGA1UEChMNR2VvVHJ1c3QgSW5jLjEbMBkGA1UEAxMSR2VvVHJ1c3Qg ++R2xvYmFsIENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA2swYYzD9 ++9BcjGlZ+W988bDjkcbd4kdS8odhM+KhDtgPpTSEHCIjaWC9mOSm9BXiLnTjoBbdq ++fnGk5sRgprDvgOSJKA+eJdbtg/OtppHHmMlCGDUUna2YRpIuT8rxh0PBFpVXLVDv ++iS2Aelet8u5fa9IAjbkU+BQVNdnARqN7csiRv8lVK83Qlz6cJmTM386DGXHKTubU ++1XupGc1V3sjs0l44U+VcT4wt/lAjNvxm5suOpDkZALeVAjmRCw7+OC7RHQWa9k0+ ++bw8HHa8sHo9gOeL6NlMTOdReJivbPagUvTLrGAMoUgRx5aszPeE4uwc2hGKceeoW ++MPRfwCvocWvk+QIDAQABo1MwUTAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBTA ++ephojYn7qwVkDBF9qn1luMrMTjAfBgNVHSMEGDAWgBTAephojYn7qwVkDBF9qn1l ++uMrMTjANBgkqhkiG9w0BAQUFAAOCAQEANeMpauUvXVSOKVCUn5kaFOSPeCpilKIn ++Z57QzxpeR+nBsqTP3UEaBU6bS+5Kb1VSsyShNwrrZHYqLizz/Tt1kL/6cdjHPTfS ++tQWVYrmm3ok9Nns4d0iXrKYgjy6myQzCsplFAMfOEVEiIuCl6rYVSAlk6l5PdPcF ++PseKUgzbFbS9bZvlxrFUaKnjaZC2mqUPuLk/IH2uSrW4nOQdtqvmlKXBx4Ot2/Un ++hw4EbNX/3aBd7YdStysVAq45pmp06drE57xNNB6pXE0zX5IJL4hmXXeXxx12E6nV ++5fEWCRE11azbJHFwLJhWC9kXtNHjUStedejV0NxPNO3CBWaAocvmMw== ++-----END CERTIFICATE----- +diff --git a/jdk/test/sun/security/lib/cacerts/VerifyCACerts.java b/jdk/test/sun/security/lib/cacerts/VerifyCACerts.java +index 54e1bfa0d..c1423dc5b 100644 +--- a/jdk/test/sun/security/lib/cacerts/VerifyCACerts.java ++++ b/jdk/test/sun/security/lib/cacerts/VerifyCACerts.java +@@ -53,12 +53,12 @@ public class VerifyCACerts { + + File.separator + "security" + File.separator + "cacerts"; + + // The numbers of certs now. +- private static final int COUNT = 83; ++ private static final int COUNT = 84; + + // SHA-256 of cacerts, can be generated with + // shasum -a 256 cacerts | sed -e 's/../&:/g' | tr '[:lower:]' '[:upper:]' | cut -c1-95 + private static final String CHECKSUM +- = "2D:04:88:6C:52:53:54:EB:38:2D:BC:E0:AF:B7:82:F4:9E:32:A8:1A:1B:A3:AE:CF:25:CB:C2:F6:0F:4E:E1:20"; ++ = "D3:05:21:64:FA:D7:CD:29:E8:CB:57:E7:47:ED:79:9B:47:D8:0E:75:2D:CA:83:BB:86:AF:D9:43:FD:3E:17:85"; + + // map of cert alias to SHA-256 fingerprint + @SuppressWarnings("serial") +@@ -111,7 +111,9 @@ public class VerifyCACerts { + "7E:37:CB:8B:4C:47:09:0C:AB:36:55:1B:A6:F4:5D:B8:40:68:0F:BA:16:6A:95:2D:B1:00:71:7F:43:05:3F:C2"); + put("digicerthighassuranceevrootca [jdk]", + "74:31:E5:F4:C3:C1:CE:46:90:77:4F:0B:61:E0:54:40:88:3B:A9:A0:1E:D0:0B:A6:AB:D7:80:6E:D3:B1:18:CF"); +- put("geotrustprimaryca [jdk]", ++ put("geotrustglobalca [jdk]", ++ "FF:85:6A:2D:25:1D:CD:88:D3:66:56:F4:50:12:67:98:CF:AB:AA:DE:40:79:9C:72:2D:E4:D2:B5:DB:36:A7:3A"); ++ put("geotrustprimaryca [jdk]", + "37:D5:10:06:C5:12:EA:AB:62:64:21:F1:EC:8C:92:01:3F:C5:F8:2A:E9:8E:E5:33:EB:46:19:B8:DE:B4:D0:6C"); + put("geotrustprimarycag2 [jdk]", + "5E:DB:7A:C4:3B:82:A0:6A:87:61:E8:D7:BE:49:79:EB:F2:61:1F:7D:D7:9B:F9:1C:1C:6B:56:6A:21:9E:D7:66"); +@@ -237,7 +239,12 @@ public class VerifyCACerts { + // Exception list to 90 days expiry policy + // No error will be reported if certificate in this list expires + @SuppressWarnings("serial") +- private static final HashSet EXPIRY_EXC_ENTRIES = new HashSet(); ++ private static final HashSet EXPIRY_EXC_ENTRIES = new HashSet() { ++ { ++ // Valid until: Sat May 21 04:00:00 GMT 2022 ++ add("geotrustglobalca [jdk]"); ++ } ++ }; + + // Ninety days in milliseconds + private static final long NINETY_DAYS = 7776000000L; diff --git a/fix_wrap_memcpy_undefined_gcc10_3.patch b/fix_wrap_memcpy_undefined_gcc10_3.patch new file mode 100644 index 0000000000000000000000000000000000000000..9da21e6117853b5d3a5a8c59bc30eafeb337e9aa --- /dev/null +++ b/fix_wrap_memcpy_undefined_gcc10_3.patch @@ -0,0 +1,291 @@ +diff --git a/jdk/make/CompileDemos.gmk b/jdk/make/CompileDemos.gmk +index 763c968e..6c5eb432 100644 +--- a/jdk/make/CompileDemos.gmk ++++ b/jdk/make/CompileDemos.gmk +@@ -250,7 +250,6 @@ define SetupJVMTIDemo + SRC := $(JDK_TOPDIR)/src/share/demo/jvmti/$1 $$(BUILD_DEMO_JVMTI_$1_EXTRA_SRC), \ + LANG := $$(BUILD_DEMO_JVMTI_$1_LANG), \ + OPTIMIZATION := LOW, \ +- EXTRA_FILES := $(HOTSPOT_TOPDIR)/src/os_cpu/linux_x86/vm/memcpy.cpp, \ + CXXFLAGS := $$($1_CXXFLAGS), \ + LDFLAGS := $(filter-out -incremental:no -opt:ref, $$(LDFLAGS_JDKLIB)), \ + LDFLAGS_macosx := $$(call SET_EXECUTABLE_ORIGIN), \ +diff --git a/jdk/make/CompileLaunchers.gmk b/jdk/make/CompileLaunchers.gmk +index 29211f83..2ac718fc 100644 +--- a/jdk/make/CompileLaunchers.gmk ++++ b/jdk/make/CompileLaunchers.gmk +@@ -512,7 +512,6 @@ $(eval $(call SetupNativeCompilation,BUILD_UNPACKEXE, \ + EXCLUDE_FILES := jni.cpp, \ + LANG := $(UNPACKEXE_LANG), \ + OPTIMIZATION := LOW, \ +- EXTRA_FILES := $(HOTSPOT_TOPDIR)/src/os_cpu/linux_x86/vm/memcpy.cpp, \ + CFLAGS := $(UNPACKEXE_CFLAGS) $(CXXFLAGS_JDKEXE) \ + -DFULL, \ + CFLAGS_release := -DPRODUCT, \ +diff --git a/jdk/make/lib/Awt2dLibraries.gmk b/jdk/make/lib/Awt2dLibraries.gmk +index 71d87c37..9368a9d5 100644 +--- a/jdk/make/lib/Awt2dLibraries.gmk ++++ b/jdk/make/lib/Awt2dLibraries.gmk +@@ -52,7 +52,6 @@ $(eval $(call SetupNativeCompilation,BUILD_LIBMLIB_IMAGE, \ + EXCLUDE_FILES := awt_ImagingLib.c mlib_c_ImageBlendTable.c, \ + LANG := C, \ + OPTIMIZATION := HIGHEST, \ +- EXTRA_FILES := $(HOTSPOT_TOPDIR)/src/os_cpu/linux_x86/vm/memcpy.cpp, \ + CFLAGS := $(CFLAGS_JDKLIB) \ + $(BUILD_LIBMLIB_CFLAGS), \ + MAPFILE := $(BUILD_LIBMLIB_IMAGE_MAPFILE), \ +@@ -471,7 +470,6 @@ $(eval $(call SetupNativeCompilation,BUILD_LIBAWT, \ + INCLUDE_FILES := $(LIBAWT_FILES), \ + LANG := $(LIBAWT_LANG), \ + OPTIMIZATION := LOW, \ +- EXTRA_FILES := $(HOTSPOT_TOPDIR)/src/os_cpu/linux_x86/vm/memcpy.cpp, \ + CFLAGS := $(CFLAGS_JDKLIB) $(LIBAWT_CFLAGS), \ + ASFLAGS := $(LIBAWT_ASFLAGS), \ + MAPFILE := $(LIBAWT_MAPFILE), \ +@@ -633,7 +631,6 @@ ifeq ($(findstring $(OPENJDK_TARGET_OS),windows macosx),) + INCLUDE_FILES := $(LIBAWT_XAWT_FILES), \ + LANG := C, \ + OPTIMIZATION := LOW, \ +- EXTRA_FILES := $(HOTSPOT_TOPDIR)/src/os_cpu/linux_x86/vm/memcpy.cpp, \ + CFLAGS := $(CFLAGS_JDKLIB) $(LIBAWT_XAWT_CFLAGS) \ + $(X_CFLAGS), \ + MAPFILE := $(JDK_TOPDIR)/make/mapfiles/libawt_xawt/mapfile-vers, \ +@@ -675,7 +672,6 @@ $(eval $(call SetupNativeCompilation,BUILD_LIBLCMS, \ + SRC := $(JDK_TOPDIR)/src/share/native/sun/java2d/cmm/lcms, \ + LANG := C, \ + OPTIMIZATION := HIGHEST, \ +- EXTRA_FILES := $(HOTSPOT_TOPDIR)/src/os_cpu/linux_x86/vm/memcpy.cpp, \ + CFLAGS := $(filter-out -xc99=%none, $(CFLAGS_JDKLIB)) \ + -DCMS_DONT_USE_FAST_FLOOR \ + $(SHARED_LIBRARY_FLAGS) \ +@@ -743,7 +739,6 @@ $(eval $(call SetupNativeCompilation,BUILD_LIBJPEG, \ + $(JDK_TOPDIR)/src/share/native/sun/awt/image/jpeg, \ + LANG := C, \ + OPTIMIZATION := HIGHEST, \ +- EXTRA_FILES := $(HOTSPOT_TOPDIR)/src/os_cpu/linux_x86/vm/memcpy.cpp, \ + CFLAGS := $(CFLAGS_JDKLIB) \ + $(BUILD_LIBJPEG_CLOSED_INCLUDES) \ + -I$(JDK_TOPDIR)/src/share/native/sun/awt/image/jpeg, \ +@@ -919,7 +914,6 @@ $(eval $(call SetupNativeCompilation,BUILD_LIBFONTMANAGER, \ + EXCLUDE_FILES := $(LIBFONTMANAGER_EXCLUDE_FILES) \ + AccelGlyphCache.c, \ + LANG := C++, \ +- EXTRA_FILES := $(HOTSPOT_TOPDIR)/src/os_cpu/linux_x86/vm/memcpy.cpp, \ + CFLAGS := $(CFLAGS_JDKLIB) $(BUILD_LIBFONTMANAGER_CFLAGS_COMMON), \ + CXXFLAGS := $(CXXFLAGS_JDKLIB) $(BUILD_LIBFONTMANAGER_CFLAGS_COMMON), \ + OPTIMIZATION := $(LIBFONTMANAGER_OPTIMIZATION), \ +@@ -1211,7 +1205,6 @@ ifndef BUILD_HEADLESS_ONLY + EXCLUDE_FILES := imageioJPEG.c jpegdecoder.c pngtest.c, \ + LANG := C, \ + OPTIMIZATION := LOW, \ +- EXTRA_FILES := $(HOTSPOT_TOPDIR)/src/os_cpu/linux_x86/vm/memcpy.cpp, \ + CFLAGS := $(LIBSPLASHSCREEN_CFLAGS) $(CFLAGS_JDKLIB) $(GIFLIB_CFLAGS), \ + MAPFILE := $(JDK_TOPDIR)/make/mapfiles/libsplashscreen/mapfile-vers, \ + LDFLAGS := $(LDFLAGS_JDKLIB) \ +diff --git a/jdk/make/lib/CoreLibraries.gmk b/jdk/make/lib/CoreLibraries.gmk +index b444abf9..e43fc2ed 100644 +--- a/jdk/make/lib/CoreLibraries.gmk ++++ b/jdk/make/lib/CoreLibraries.gmk +@@ -113,7 +113,6 @@ $(eval $(call SetupNativeCompilation,BUILD_LIBVERIFY, \ + INCLUDE_FILES := $(BUILD_LIBVERIFY_SRC), \ + LANG := C, \ + OPTIMIZATION := $(LIBVERIFY_OPTIMIZATION), \ +- EXTRA_FILES := $(HOTSPOT_TOPDIR)/src/os_cpu/linux_x86/vm/memcpy.cpp, \ + CFLAGS := $(CFLAGS_JDKLIB), \ + MAPFILE := $(JDK_TOPDIR)/make/mapfiles/libverify/mapfile-vers, \ + LDFLAGS := $(LDFLAGS_JDKLIB) \ +@@ -225,7 +224,6 @@ $(eval $(call SetupNativeCompilation,BUILD_LIBJAVA, \ + EXCLUDE_FILES := $(LIBJAVA_EXCLUDE_FILES), \ + LANG := C, \ + OPTIMIZATION := HIGH, \ +- EXTRA_FILES := $(HOTSPOT_TOPDIR)/src/os_cpu/linux_x86/vm/memcpy.cpp, \ + CFLAGS := $(CFLAGS_JDKLIB) \ + $(LIBJAVA_CFLAGS), \ + MAPFILE := $(LIBJAVA_MAPFILE), \ +@@ -287,7 +285,6 @@ $(eval $(call SetupNativeCompilation,BUILD_LIBZIP, \ + OUTPUT_DIR := $(INSTALL_LIBRARIES_HERE), \ + LANG := C, \ + OPTIMIZATION := LOW, \ +- EXTRA_FILES := $(HOTSPOT_TOPDIR)/src/os_cpu/linux_x86/vm/memcpy.cpp, \ + SRC := $(JDK_TOPDIR)/src/share/native/java/util/zip, \ + EXCLUDES := $(LIBZIP_EXCLUDES), \ + CFLAGS := $(CFLAGS_JDKLIB) \ +@@ -329,7 +326,6 @@ $(eval $(call SetupNativeCompilation,BUILD_LIBUNPACK, \ + EXCLUDE_FILES := main.cpp, \ + LANG := C++, \ + OPTIMIZATION := LOW, \ +- EXTRA_FILES := $(HOTSPOT_TOPDIR)/src/os_cpu/linux_x86/vm/memcpy.cpp, \ + CFLAGS := $(CXXFLAGS_JDKLIB) \ + -DNO_ZLIB -DUNPACK_JNI -DFULL, \ + CFLAGS_release := -DPRODUCT, \ +@@ -442,7 +438,6 @@ $(eval $(call SetupNativeCompilation,BUILD_LIBJLI, \ + INCLUDE_FILES := $(BUILD_LIBJLI_FILES), \ + LANG := C, \ + OPTIMIZATION := HIGH, \ +- EXTRA_FILES := $(HOTSPOT_TOPDIR)/src/os_cpu/linux_x86/vm/memcpy.cpp, \ + CFLAGS := $(LIBJLI_CFLAGS), \ + MAPFILE := $(JDK_TOPDIR)/make/mapfiles/libjli/mapfile-vers, \ + LDFLAGS := $(LDFLAGS_JDKLIB) \ +@@ -544,7 +539,6 @@ $(eval $(call SetupNativeCompilation,BUILD_LIBNPT, \ + SRC := $(JDK_TOPDIR)/src/share/npt $(JDK_TOPDIR)/src/$(OPENJDK_TARGET_OS_API_DIR)/npt, \ + LANG := C, \ + OPTIMIZATION := LOW, \ +- EXTRA_FILES := $(HOTSPOT_TOPDIR)/src/os_cpu/linux_x86/vm/memcpy.cpp, \ + CFLAGS := $(CFLAGS_JDKLIB) \ + -I$(JDK_TOPDIR)/src/share/npt \ + -I$(JDK_TOPDIR)/src/$(OPENJDK_TARGET_OS_API_DIR)/npt, \ +diff --git a/jdk/make/lib/NetworkingLibraries.gmk b/jdk/make/lib/NetworkingLibraries.gmk +index f826c66d..347c3237 100644 +--- a/jdk/make/lib/NetworkingLibraries.gmk ++++ b/jdk/make/lib/NetworkingLibraries.gmk +@@ -65,7 +65,6 @@ $(eval $(call SetupNativeCompilation,BUILD_LIBNET, \ + EXCLUDE_FILES := $(LIBNET_EXCLUDE_FILES), \ + LANG := C, \ + OPTIMIZATION := LOW, \ +- EXTRA_FILES := $(HOTSPOT_TOPDIR)/src/os_cpu/linux_x86/vm/memcpy.cpp, \ + CFLAGS := $(CFLAGS_JDKLIB) \ + $(LIBNET_CFLAGS), \ + MAPFILE := $(JDK_TOPDIR)/make/mapfiles/libnet/mapfile-vers, \ +diff --git a/jdk/make/lib/NioLibraries.gmk b/jdk/make/lib/NioLibraries.gmk +index 54c9c29e..6c9c46a3 100644 +--- a/jdk/make/lib/NioLibraries.gmk ++++ b/jdk/make/lib/NioLibraries.gmk +@@ -181,7 +181,6 @@ ifeq ($(OPENJDK_TARGET_OS_API), posix) + SRC := $(JDK_TOPDIR)/src/$(OPENJDK_TARGET_OS_API_DIR)/native/sun/nio/ch/sctp, \ + LANG := C, \ + OPTIMIZATION := LOW, \ +- EXTRA_FILES := $(HOTSPOT_TOPDIR)/src/os_cpu/linux_x86/vm/memcpy.cpp, \ + CFLAGS := $(CFLAGS_JDKLIB) \ + -I$(JDK_TOPDIR)/src/share/native/sun/nio/ch \ + -I$(JDK_TOPDIR)/src/share/native/sun/nio/ch/sctp \ +diff --git a/jdk/make/lib/SecurityLibraries.gmk b/jdk/make/lib/SecurityLibraries.gmk +index 10ab8043..5b9ec17f 100644 +--- a/jdk/make/lib/SecurityLibraries.gmk ++++ b/jdk/make/lib/SecurityLibraries.gmk +@@ -196,7 +196,6 @@ $(eval $(call SetupNativeCompilation,BUILD_LIBJ2PKCS11, \ + $(JDK_TOPDIR)/src/$(OPENJDK_TARGET_OS_API_DIR)/native/sun/security/pkcs11/wrapper, \ + LANG := C, \ + OPTIMIZATION := LOW, \ +- EXTRA_FILES := $(HOTSPOT_TOPDIR)/src/os_cpu/linux_x86/vm/memcpy.cpp, \ + CFLAGS := $(CFLAGS_JDKLIB) \ + -I$(JDK_TOPDIR)/src/share/native/sun/security/pkcs11 \ + -I$(JDK_TOPDIR)/src/$(OPENJDK_TARGET_OS_API_DIR)/native/sun/security/pkcs11 \ +@@ -242,7 +241,6 @@ ifeq ($(ENABLE_INTREE_EC), yes) + $(JDK_TOPDIR)/src/share/native/sun/security/ec/impl, \ + LANG := C++, \ + OPTIMIZATION := LOW, \ +- EXTRA_FILES := $(HOTSPOT_TOPDIR)/src/os_cpu/linux_x86/vm/memcpy.cpp, \ + CFLAGS := $(filter-out $(ECC_JNI_SOLSPARC_FILTER), $(CFLAGS_JDKLIB)) \ + $(BUILD_LIBSUNEC_FLAGS) \ + -DMP_API_COMPATIBLE -DNSS_ECC_MORE_THAN_SUITE_B, \ +@@ -300,7 +298,6 @@ ifeq ($(ENABLE_KAE), true) + SRC := $(JDK_TOPDIR)/src/$(OPENJDK_TARGET_OS_API_DIR)/native/org/openeuler/security/openssl, \ + LANG := C, \ + OPTIMIZATION := LOW, \ +- EXTRA_FILES := $(HOTSPOT_TOPDIR)/src/os_cpu/linux_x86/vm/memcpy.cpp, \ + CFLAGS := $(CFLAGS_JDKLIB) \ + -I$(JDK_TOPDIR)/src/$(OPENJDK_TARGET_OS_API_DIR)/native/org/openeuler/security/openssl, \ + MAPFILE := $(JDK_TOPDIR)/make/mapfiles/libj2kae/mapfile-vers, \ +diff --git a/jdk/make/lib/ServiceabilityLibraries.gmk b/jdk/make/lib/ServiceabilityLibraries.gmk +index 2c80ffc0..19c8601d 100644 +--- a/jdk/make/lib/ServiceabilityLibraries.gmk ++++ b/jdk/make/lib/ServiceabilityLibraries.gmk +@@ -83,7 +83,6 @@ $(eval $(call SetupNativeCompilation,BUILD_LIBDT_SOCKET, \ + $(JDK_TOPDIR)/src/$(OPENJDK_TARGET_OS_API_DIR)/transport/socket, \ + LANG := C, \ + OPTIMIZATION := LOW, \ +- EXTRA_FILES := $(HOTSPOT_TOPDIR)/src/os_cpu/linux_x86/vm/memcpy.cpp, \ + CFLAGS := $(CFLAGS_JDKLIB) $(CFLAGS_WARNINGS_ARE_ERRORS) -DUSE_MMAP \ + -I$(INCLUDEDIR) -I$(JDK_OUTPUTDIR)/include/$(OPENJDK_TARGET_OS) \ + -I$(JDK_TOPDIR)/src/share/transport/socket \ +@@ -149,7 +148,6 @@ $(eval $(call SetupNativeCompilation,BUILD_LIBJDWP, \ + SRC := $(JDK_TOPDIR)/src/share/back $(JDK_TOPDIR)/src/$(OPENJDK_TARGET_OS_API_DIR)/back, \ + LANG := C, \ + OPTIMIZATION := LOW, \ +- EXTRA_FILES := $(HOTSPOT_TOPDIR)/src/os_cpu/linux_x86/vm/memcpy.cpp, \ + CFLAGS := $(CFLAGS_JDKLIB) $(CFLAGS_WARNINGS_ARE_ERRORS) -DJDWP_LOGGING \ + -I$(JDK_TOPDIR)/src/share/transport/export \ + -I$(JDK_TOPDIR)/src/share/back/export \ +@@ -255,7 +253,6 @@ $(eval $(call SetupNativeCompilation,BUILD_LIBINSTRUMENT, \ + INCLUDE_FILES := $(LIBINSTRUMENT_FILES), \ + LANG := C, \ + OPTIMIZATION := LOW, \ +- EXTRA_FILES := $(HOTSPOT_TOPDIR)/src/os_cpu/linux_x86/vm/memcpy.cpp, \ + CFLAGS := $(LIBINSTRUMENT_CFLAGS) $(CFLAGS_WARNINGS_ARE_ERRORS), \ + CFLAGS_debug := -DJPLIS_LOGGING, \ + CFLAGS_release := -DNO_JPLIS_LOGGING, \ +@@ -379,7 +376,6 @@ $(eval $(call SetupNativeCompilation,BUILD_LIBHPROF, \ + SRC := $(BUILD_LIBHPROF_SRC), \ + LANG := C, \ + OPTIMIZATION := $(LIBHPROF_OPTIMIZATION), \ +- EXTRA_FILES := $(HOTSPOT_TOPDIR)/src/os_cpu/linux_x86/vm/memcpy.cpp, \ + CFLAGS := $(CFLAGS_JDKLIB) $(CFLAGS_WARNINGS_ARE_ERRORS) \ + $(BUILD_LIBHPROF_CFLAGS), \ + CFLAGS_debug := -DHPROF_LOGGING, \ +@@ -408,7 +404,6 @@ $(eval $(call SetupNativeCompilation,BUILD_LIBJAVA_CRW_DEMO, \ + SRC := $(JDK_TOPDIR)/src/share/demo/jvmti/java_crw_demo, \ + LANG := C, \ + OPTIMIZATION := LOW, \ +- EXTRA_FILES := $(HOTSPOT_TOPDIR)/src/os_cpu/linux_x86/vm/memcpy.cpp, \ + CFLAGS := $(CFLAGS_JDKLIB) $(CFLAGS_WARNINGS_ARE_ERRORS) \ + -I$(JDK_TOPDIR)/src/share/demo/jvmti/java_crw_demo, \ + MAPFILE := $(JDK_TOPDIR)/make/mapfiles/libjava_crw_demo/mapfile-vers, \ +diff --git a/jdk/make/lib/SoundLibraries.gmk b/jdk/make/lib/SoundLibraries.gmk +index 0ea9ba84..b59a9462 100644 +--- a/jdk/make/lib/SoundLibraries.gmk ++++ b/jdk/make/lib/SoundLibraries.gmk +@@ -201,7 +201,6 @@ ifneq ($(filter jsoundalsa, $(EXTRA_SOUND_JNI_LIBS)), ) + PLATFORM_API_LinuxOS_ALSA_Ports.c, \ + LANG := C, \ + OPTIMIZATION := LOW, \ +- EXTRA_FILES := $(HOTSPOT_TOPDIR)/src/os_cpu/linux_x86/vm/memcpy.cpp, \ + CFLAGS := $(CFLAGS_JDKLIB) $(ALSA_CFLAGS) \ + $(LIBJSOUND_CFLAGS) \ + -DUSE_DAUDIO=TRUE \ +diff --git a/make/common/NativeCompilation.gmk b/make/common/NativeCompilation.gmk +index 2d9bdbee..9586d20e 100644 +--- a/make/common/NativeCompilation.gmk ++++ b/make/common/NativeCompilation.gmk +@@ -271,6 +271,7 @@ define SetupNativeCompilation + + # Find all files in the source trees. Sort to remove duplicates. + $1_ALL_SRCS := $$(sort $$(call CacheFind,$$($1_SRC))) ++ + # Extract the C/C++ files. + $1_EXCLUDE_FILES:=$$(foreach i,$$($1_SRC),$$(addprefix $$i/,$$($1_EXCLUDE_FILES))) + $1_INCLUDE_FILES:=$$(foreach i,$$($1_SRC),$$(addprefix $$i/,$$($1_INCLUDE_FILES))) +@@ -281,13 +282,20 @@ define SetupNativeCompilation + ifneq (,$$(strip $$($1_INCLUDE_FILES))) + $1_SRCS := $$(filter $$($1_INCLUDE_FILES),$$($1_SRCS)) + endif ++ ++ # Pickup extra OPENJDK_TARGET_OS_API and/or OPENJDK_TARGET_OS dependent variables ++ # for LDFLAGS and LDFLAGS_SUFFIX ++ $1_EXTRA_LDFLAGS:=$$($1_LDFLAGS_$(OPENJDK_TARGET_OS_API)) $$($1_LDFLAGS_$(OPENJDK_TARGET_OS)) ++ $1_EXTRA_LDFLAGS_SUFFIX:=$$($1_LDFLAGS_SUFFIX_$(OPENJDK_TARGET_OS_API)) $$($1_LDFLAGS_SUFFIX_$(OPENJDK_TARGET_OS)) ++ + ifeq ($(OPENJDK_TARGET_OS), linux) # only on linux +- ifneq ($(OPENJDK_TARGET_CPU_ARCH), aarch64) # not need on the arm arch +- ifneq (,$$(strip $$($1_EXTRA_FILES))) +- $1_SRCS += $$($1_EXTRA_FILES) ++ ifneq ($$(findstring wrap=memcpy, $$($1_LDFLAGS)$$($1_EXTRA_LDFLAGS))$$($1_EXTRA_LDFLAGS_SUFFIX),) ++ ifeq ($$(findstring memcpy.cpp, $$($1_SRCS)),) ++ $1_SRCS += $(HOTSPOT_TOPDIR)/src/os_cpu/linux_x86/vm/memcpy.cpp + endif + endif + endif ++ + ifeq (,$$($1_SRCS)) + $$(error No sources found for $1 when looking inside the dirs $$($1_SRC)) + endif +@@ -432,10 +440,6 @@ define SetupNativeCompilation + endif + endif + +- # Pickup extra OPENJDK_TARGET_OS_API and/or OPENJDK_TARGET_OS dependent variables +- # for LDFLAGS and LDFLAGS_SUFFIX +- $1_EXTRA_LDFLAGS:=$$($1_LDFLAGS_$(OPENJDK_TARGET_OS_API)) $$($1_LDFLAGS_$(OPENJDK_TARGET_OS)) +- $1_EXTRA_LDFLAGS_SUFFIX:=$$($1_LDFLAGS_SUFFIX_$(OPENJDK_TARGET_OS_API)) $$($1_LDFLAGS_SUFFIX_$(OPENJDK_TARGET_OS)) + ifneq (,$$($1_REAL_MAPFILE)) + $1_EXTRA_LDFLAGS += $(call SET_SHARED_LIBRARY_MAPFILE,$$($1_REAL_MAPFILE)) + endif diff --git a/implementation_of_Blas_hotspot_function_in_Intrinsics.patch b/implementation_of_Blas_hotspot_function_in_Intrinsics.patch index e731e53bcf5c923cd8c9e7a42c9a1c904bca3dd4..50f26b4fb6e3b488a7d2f8f07451517614c048cd 100755 --- a/implementation_of_Blas_hotspot_function_in_Intrinsics.patch +++ b/implementation_of_Blas_hotspot_function_in_Intrinsics.patch @@ -289,8 +289,8 @@ index c5ec637a1..125983179 100644 + // Search path: /jre/lib///libopenblas.so + if (jvm_offset >= 0) { + if (jvm_offset + strlen(library_name) + strlen(os::dll_file_extension()) < JVM_MAXPATHLEN) { -+ strncpy(&path[jvm_offset], library_name, strlen(library_name)); -+ strncat(&path[jvm_offset], os::dll_file_extension(), strlen(os::dll_file_extension())); ++ strncpy(&path[jvm_offset], library_name, JVM_MAXPATHLEN - jvm_offset); ++ strncat(path, os::dll_file_extension(), strlen(os::dll_file_extension())); + library = (address)os::dll_load(path, err_buf, sizeof(err_buf)); + } + } diff --git a/jdk8u-jdk8u322-b06.tar.xz b/jdk8u-jdk8u352-b08.tar.xz similarity index 82% rename from jdk8u-jdk8u322-b06.tar.xz rename to jdk8u-jdk8u352-b08.tar.xz index 03b30c0cf15b36aef487601269528822ff5cba98..19d13e4a3511e06ec5acd8548c55b253065cffb0 100644 Binary files a/jdk8u-jdk8u322-b06.tar.xz and b/jdk8u-jdk8u352-b08.tar.xz differ diff --git a/kae-usability-enhancement.patch b/kae-usability-enhancement.patch new file mode 100644 index 0000000000000000000000000000000000000000..ec1207a0fd7758557930ae10228489caeccb3f89 --- /dev/null +++ b/kae-usability-enhancement.patch @@ -0,0 +1,3640 @@ +From 8545f560d406db592303b09fc576c13ba9a8caa0 Mon Sep 17 00:00:00 2001 +From: kuenking111 +Date: Sat, 3 Sep 2022 14:18:42 +0000 +Subject: [PATCH 2/6] kae-usability-enhancement + +--- + jdk/make/CopyFiles.gmk | 2 +- + jdk/make/mapfiles/libj2kae/mapfile-vers | 1 + + jdk/src/share/lib/security/kaeprovider.conf | 65 ++- + .../openeuler/security/openssl/KAEConfig.java | 386 ++++++++++++++++++ + .../openeuler/security/openssl/KAELog.java | 183 +++++++++ + .../security/openssl/KAEProvider.java | 151 ++++--- + .../security/openssl/KAESM4Cipher.java | 181 ++++++++ + .../security/openssl/kae_cipher_rsa.c | 13 +- + .../openeuler/security/openssl/kae_digest.c | 9 +- + .../org/openeuler/security/openssl/kae_hmac.c | 9 +- + .../security/openssl/kae_keyagreement_dh.c | 4 +- + .../openssl/kae_keypairgenerator_dh.c | 4 +- + .../openssl/kae_keypairgenerator_rsa.c | 6 +- + .../openeuler/security/openssl/kae_provider.c | 54 ++- + .../security/openssl/kae_signature_rsa.c | 21 +- + .../security/openssl/kae_symmetric_cipher.c | 9 +- + .../org/openeuler/security/openssl/kae_util.c | 138 ++++++- + .../org/openeuler/security/openssl/kae_util.h | 51 ++- + .../openeuler/security/openssl/AESTest.java | 114 ++++++ + .../openeuler/security/openssl/DHTest.java | 9 +- + .../security/openssl/DigestTest.java | 60 +++ + .../openeuler/security/openssl/ECDHTest.java | 1 + + .../openeuler/security/openssl/HmacTest.java | 88 ++++ + .../security/openssl/KAEConfTest.java | 121 ++++++ + .../openssl/KAEDisabledAlgorithmsTest.java | 164 ++++++++ + .../security/openssl/KAEEngineIdTest.java | 76 ++++ + .../security/openssl/KAELogTest.java | 126 ++++++ + .../security/openssl/KAETestHelper.java | 209 ++++++++++ + .../security/openssl/KAEUseEngineTest.java | 262 ++++++++++++ + .../security/openssl/KaeDebugLogTest.java | 88 ++++ + .../security/openssl/KaeProviderTest.java | 170 ++++++++ + .../openeuler/security/openssl/RSATest.java | 137 +++++++ + .../openeuler/security/openssl/SM3Test.java | 54 --- + .../openeuler/security/openssl/SM4Test.java | 62 ++- + 34 files changed, 2844 insertions(+), 184 deletions(-) + create mode 100644 jdk/src/solaris/classes/org/openeuler/security/openssl/KAEConfig.java + create mode 100644 jdk/src/solaris/classes/org/openeuler/security/openssl/KAELog.java + create mode 100644 jdk/test/org/openeuler/security/openssl/AESTest.java + create mode 100644 jdk/test/org/openeuler/security/openssl/DigestTest.java + create mode 100644 jdk/test/org/openeuler/security/openssl/HmacTest.java + create mode 100644 jdk/test/org/openeuler/security/openssl/KAEConfTest.java + create mode 100644 jdk/test/org/openeuler/security/openssl/KAEDisabledAlgorithmsTest.java + create mode 100644 jdk/test/org/openeuler/security/openssl/KAEEngineIdTest.java + create mode 100644 jdk/test/org/openeuler/security/openssl/KAELogTest.java + create mode 100644 jdk/test/org/openeuler/security/openssl/KAETestHelper.java + create mode 100644 jdk/test/org/openeuler/security/openssl/KAEUseEngineTest.java + create mode 100644 jdk/test/org/openeuler/security/openssl/KaeDebugLogTest.java + create mode 100644 jdk/test/org/openeuler/security/openssl/KaeProviderTest.java + create mode 100644 jdk/test/org/openeuler/security/openssl/RSATest.java + delete mode 100644 jdk/test/org/openeuler/security/openssl/SM3Test.java + +diff --git a/jdk/make/CopyFiles.gmk b/jdk/make/CopyFiles.gmk +index 2a6fc0932..806d7bec1 100644 +--- a/jdk/make/CopyFiles.gmk ++++ b/jdk/make/CopyFiles.gmk +@@ -634,7 +634,7 @@ endif + ifeq ($(ENABLE_KAE), true) + ifeq ($(OPENJDK_TARGET_CPU_ARCH), aarch64) + +- KAE_CONF_PATH= $(JDK_OUTPUTDIR)/lib/ext ++ KAE_CONF_PATH= $(JDK_OUTPUTDIR)/lib + $(KAE_CONF_PATH)/kaeprovider.conf: $(JDK_TOPDIR)/src/share/lib/security/kaeprovider.conf + $(call install-file) + +diff --git a/jdk/make/mapfiles/libj2kae/mapfile-vers b/jdk/make/mapfiles/libj2kae/mapfile-vers +index 128d1e322..a1bdb830b 100644 +--- a/jdk/make/mapfiles/libj2kae/mapfile-vers ++++ b/jdk/make/mapfiles/libj2kae/mapfile-vers +@@ -27,6 +27,7 @@ SUNWprivate_1.1 { + global: + JNI_OnLoad; + Java_org_openeuler_security_openssl_KAEProvider_initOpenssl; ++ Java_org_openeuler_security_openssl_KAEProvider_getEngineFlags; + Java_org_openeuler_security_openssl_KAEDigest_nativeInit; + Java_org_openeuler_security_openssl_KAEDigest_nativeUpdate; + Java_org_openeuler_security_openssl_KAEDigest_nativeDigest; +diff --git a/jdk/src/share/lib/security/kaeprovider.conf b/jdk/src/share/lib/security/kaeprovider.conf +index a48969669..cc50611d1 100644 +--- a/jdk/src/share/lib/security/kaeprovider.conf ++++ b/jdk/src/share/lib/security/kaeprovider.conf +@@ -1,9 +1,13 @@ + # +-# This is the config file for KAEProvider ++# This is the config file for KAEProvider. ++# These configuration properties support the use of jdk system properties, ++# and jdk system properties take precedence over file configuration properties. ++# For detailed usage, please refer to the user manual: ++# https://gitee.com/openeuler/bishengjdk-8/wikis/%E4%B8%AD%E6%96%87%E6%96%87%E6%A1%A3/KAE%20Provider%E7%94%A8%E6%88%B7%E4%BD%BF%E7%94%A8%E6%89%8B%E5%86%8C + # +-# Algorithms are enabled by default if KAEProvider is used. +-# Delete # if you want to disable certain algorithm. + ++# Algorithms are enabled by default if KAEProvider is used. ++# Delete # if you want to disable certain algorithm. + # kae.md5=false + # kae.sha256=false + # kae.sha384=false +@@ -15,5 +19,58 @@ + # kae.dh=false + # kae.ec=false + +-# enable KAEProvider log setting ++# Configure engine id, the default value is kae. ++# kae.engine.id=kae ++ ++# Configure whether libcrypto.so uses GLOBAL mode, uses LOCAL mode by default. ++# If you use uadk_engine, you need to enable this option. ++# kae.libcrypto.useGlobalMode=false ++ ++# The following configuration will only take effect when using KAEProvider. ++# Configure whether to enable KAE hardware acceleration for each category of algorithm. ++# The configurable value are as follows: ++# true : enable KAE hardware acceleration by default ++# false: use openssl soft calculation by default ++# The digest/sm4/rsa/dh category algorithm enable KAE hardware acceleration by default. ++# The aes/hmac/ec category algorithm use openssl soft calculation by default. ++# The ec category algorithm configuration does not take effect temporarily. and it ++# currently does not support KAE hardware acceleration, temporarily use openssl soft calculation. ++# kae.digest.useKaeEngine=true ++# kae.aes.useKaeEngine=false ++# kae.sm4.useKaeEngine=true ++# kae.hmac.useKaeEngine=false ++# kae.rsa.useKaeEngine=true ++# kae.dh.useKaeEngine=true ++# kae.ec.useKaeEngine=false ++# ++# Some engines do not fully support certain categories of algorithms, for example, the digest ++# algorithm implemented by kae engine only supports md5 and sm3.For more information, please refer to: ++# KAE : https://github.com/kunpengcompute/KAE#:~:text=Digest%20algorithm%3A%20SM3/MD5 ++# UADK: https://gitee.com/openeuler/uadk/wikis/%E4%BD%BF%E7%94%A8%E6%96%87%E6%A1%A3/UADK%20quick%20start#11-uadk ++# ++# Users can disable unsupported algorithms through the following property configuration. ++# Disable algorithm to enable KAE hardware acceleration, use openssl soft algorithm instead. ++# The sha256, sha384 algorithms are disabled by default. ++# digest : md5,sha256,sha384,sm3 ++# aes : aes-128-ecb,aes-128-cbc,aes-128-ctr,aes-128-gcm, ++# aes-192-ecb,aes-192-cbc,aes-192-ctr,aes-192-gcm, ++# aes-256-ecb,aes-256-cbc,aes-256-ctr,aes-256-gcm ++# sm4 : sm4-ecb,sm4-cbc,sm4-ctr,sm4-ofb ++# hmac : hmac-md5,hmac-sha1,hmac-sha224,hmac-sha256,hmac-sha384,hmac-sha512 ++# rsa : rsa ++# dh : dh ++# ec : ec ++# kae.engine.disabledAlgorithms=sha256,sha384 ++ ++# SM4 max chunk size of each encryption or decryption. ++# when input data does not have an accessible byte[]. ++# The default value is 4096, when configuring a non-positive Integer type, use the default value of 4096. ++# kae.sm4.maxChunkSize=4096 ++ ++# Enable engine load log. + # kae.log=true ++# ++# It only takes effect when the property kae.log value is true. ++# Configure log file path, default value is System.getProperty("user.dir") + "/ + "kae.log". ++# kae.log.file=/home/user/kae.log ++ +diff --git a/jdk/src/solaris/classes/org/openeuler/security/openssl/KAEConfig.java b/jdk/src/solaris/classes/org/openeuler/security/openssl/KAEConfig.java +new file mode 100644 +index 000000000..07294dbd6 +--- /dev/null ++++ b/jdk/src/solaris/classes/org/openeuler/security/openssl/KAEConfig.java +@@ -0,0 +1,386 @@ ++/* ++ * Copyright (c) 2022, Huawei Technologies Co., Ltd. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ */ ++ ++package org.openeuler.security.openssl; ++ ++import sun.security.util.Debug; ++ ++import java.io.BufferedInputStream; ++import java.io.File; ++import java.io.FileInputStream; ++import java.io.IOException; ++import java.io.InputStream; ++import java.security.AccessController; ++import java.security.PrivilegedAction; ++import java.util.Arrays; ++import java.util.HashMap; ++import java.util.Map; ++import java.util.Properties; ++ ++public class KAEConfig { ++ private static final Debug kaeDebug = Debug.getInstance("kae"); ++ ++ // these property names indicates whether each algorithm uses KAEProvider ++ private static final String[] useKaeProviderPropertyNames = new String[]{ ++ "kae.md5", ++ "kae.sha256", ++ "kae.sha384", ++ "kae.sm3", ++ "kae.aes", ++ "kae.sm4", ++ "kae.hmac", ++ "kae.rsa", ++ "kae.dh", ++ "kae.ec" ++ }; ++ ++ // these property names indicate whether KAE hardware acceleration is enabled for each algorithm ++ private static final String[] useKaeEnginePropertyNames = new String[]{ ++ "kae.digest.useKaeEngine", ++ "kae.aes.useKaeEngine", ++ "kae.sm4.useKaeEngine", ++ "kae.hmac.useKaeEngine", ++ "kae.rsa.useKaeEngine", ++ "kae.dh.useKaeEngine", ++ "kae.ec.useKaeEngine" ++ }; ++ ++ // algorithm names ++ private static final String[] algorithmNames = new String[]{ ++ "md5", ++ "sha256", ++ "sha384", ++ "sm3", ++ "aes-128-ecb", ++ "aes-128-cbc", ++ "aes-128-ctr", ++ "aes-128-gcm", ++ "aes-192-ecb", ++ "aes-192-cbc", ++ "aes-192-ctr", ++ "aes-192-gcm", ++ "aes-256-ecb", ++ "aes-256-cbc", ++ "aes-256-ctr", ++ "aes-256-gcm", ++ "sm4-ecb", ++ "sm4-cbc", ++ "sm4-ctr", ++ "sm4-ofb", ++ "hmac-md5", ++ "hmac-sha1", ++ "hmac-sha224", ++ "hmac-sha256", ++ "hmac-sha384", ++ "hmac-sha512", ++ "rsa", ++ "dh", ++ "ec" ++ }; ++ ++ // algorithm name and algorithm index mapping ++ private static final Map algorithmNameIndexMap = new HashMap<>(); ++ ++ // algorithm name and algorithm category index mapping ++ private static final Map algorithmNameCategoryMap = new HashMap<>(); ++ ++ // whether use KAEProvider for each algorithm ++ private static final boolean[] useKaeProviderFlags = new boolean[algorithmNames.length]; ++ ++ // whether use KAEProvider for each category algorithm ++ private static final Map useKaeProviderCategoryMap = new HashMap<>(); ++ ++ // whether enable the Kunpeng acceleration engine for each algorithm ++ private static final boolean[] useKaeEngineFlags = new boolean[algorithmNames.length]; ++ ++ // The kaeprovider.cnf properties ++ private static Properties props; ++ ++ private KAEConfig() { ++ ++ } ++ ++ static { ++ AccessController.doPrivileged(new PrivilegedAction() { ++ public Void run() { ++ initialize(); ++ return null; ++ } ++ }); ++ } ++ ++ private static File kaePropFile(String filename) { ++ String sep = File.separator; ++ String defaultKaeConf = System.getProperty("java.home") + sep + "lib" + sep + filename; ++ String kaeConf = System.getProperty("kae.conf", defaultKaeConf); ++ return new File(kaeConf); ++ } ++ ++ private static void initialize() { ++ initProperties(); ++ initAlgorithmNameMap(); ++ initUseKaeProviderFlags(); ++ initUseKaeEngineFlags(); ++ } ++ ++ private static void initProperties() { ++ props = new Properties(); ++ File propFile = kaePropFile("kaeprovider.conf"); ++ if (propFile.exists()) { ++ InputStream is = null; ++ try { ++ FileInputStream fis = new FileInputStream(propFile); ++ is = new BufferedInputStream(fis); ++ props.load(is); ++ ++ if (kaeDebug != null) { ++ kaeDebug.println("reading kae properties file: " + ++ propFile); ++ } ++ } catch (IOException e) { ++ if (kaeDebug != null) { ++ kaeDebug.println("unable to load kae properties from " + ++ propFile); ++ e.printStackTrace(); ++ } ++ } finally { ++ if (is != null) { ++ try { ++ is.close(); ++ } catch (IOException ioe) { ++ if (kaeDebug != null) { ++ kaeDebug.println("unable to close input stream"); ++ } ++ } ++ } ++ } ++ } else { ++ if (kaeDebug != null) { ++ kaeDebug.println("not found kae properties file: " + ++ propFile); ++ } ++ } ++ } ++ ++ public static Boolean useKaeProvider(String key) { ++ return useKaeProviderCategoryMap.getOrDefault(key, Boolean.TRUE); ++ } ++ ++ private static void initUseKaeProviderFlags() { ++ boolean[] categoryFlagsForProvider = new boolean[useKaeProviderPropertyNames.length]; ++ Arrays.fill(categoryFlagsForProvider, true); ++ for (int i = 0; i < useKaeProviderPropertyNames.length; i++) { ++ String configValue = privilegedGetOverridable(useKaeProviderPropertyNames[i]); ++ if (configValue != null) { ++ categoryFlagsForProvider[i] = Boolean.parseBoolean(configValue); ++ } ++ useKaeProviderCategoryMap.put(useKaeProviderPropertyNames[i], categoryFlagsForProvider[i]); ++ } ++ int offset = useKaeProviderPropertyNames.length - useKaeEnginePropertyNames.length; ++ int digestAlgorithmLen = offset + 1; ++ // digest ++ System.arraycopy(categoryFlagsForProvider, 0, useKaeProviderFlags, 0, digestAlgorithmLen); ++ ++ // non-digest ++ for (int i = digestAlgorithmLen; i < useKaeProviderFlags.length; i++) { ++ Integer algorithmCategoryIndex = algorithmNameCategoryMap.get(algorithmNames[i]); ++ if (categoryFlagsForProvider[algorithmCategoryIndex + offset]) { ++ useKaeProviderFlags[i] = true; ++ } ++ } ++ ++ if (kaeDebug != null) { ++ kaeDebug.println("useKaeProviderPropertyNames: "); ++ for (int i = 0; i < categoryFlagsForProvider.length; i++) { ++ kaeDebug.println(useKaeProviderPropertyNames[i] + "=" + categoryFlagsForProvider[i]); ++ } ++ ++ kaeDebug.println("useKaeProviderFlags: "); ++ for (int i = 0; i < useKaeProviderFlags.length; i++) { ++ kaeDebug.println(algorithmNames[i] + "=" + useKaeProviderFlags[i]); ++ } ++ } ++ } ++ ++ public static boolean[] getUseKaeProviderFlags() { ++ return useKaeProviderFlags; ++ } ++ ++ private static void initUseKaeEngineFlags() { ++ boolean[] categoryFlagsForEngine = new boolean[]{ ++ true, // digest ++ false, // aes ++ true, // sm4 ++ false, // hmac ++ true, // rsa ++ true, // dh ++ false // ec ++ }; ++ for (int i = 0; i < useKaeEnginePropertyNames.length; i++) { ++ String configValue = privilegedGetOverridable(useKaeEnginePropertyNames[i]); ++ if (configValue != null) { ++ categoryFlagsForEngine[i] = Boolean.parseBoolean(configValue); ++ } ++ } ++ ++ // EC algorithm currently does not support KAE hardware acceleration, temporarily use openssl soft calculation. ++ categoryFlagsForEngine[useKaeEnginePropertyNames.length - 1] = false; ++ ++ for (int i = 0; i < useKaeEngineFlags.length; i++) { ++ Integer algorithmCategoryIndex = algorithmNameCategoryMap.get(algorithmNames[i]); ++ if (categoryFlagsForEngine[algorithmCategoryIndex]) { ++ useKaeEngineFlags[i] = true; ++ } ++ } ++ ++ String[] disabledAlgorithms = getDisabledAlgorithms(); ++ for (String disabledAlgorithm : disabledAlgorithms) { ++ Integer algorithmIndex = algorithmNameIndexMap.get(disabledAlgorithm); ++ if (algorithmIndex != null) { ++ useKaeEngineFlags[algorithmIndex] = false; ++ } ++ } ++ if (kaeDebug != null) { ++ kaeDebug.println("useKaeEnginePropertyNames: "); ++ for (int i = 0; i < categoryFlagsForEngine.length; i++) { ++ kaeDebug.println(useKaeEnginePropertyNames[i] + "=" + categoryFlagsForEngine[i]); ++ } ++ ++ kaeDebug.println("disabledAlgorithms: "); ++ for (int i = 0; i < disabledAlgorithms.length; i++) { ++ kaeDebug.println(disabledAlgorithms[i]); ++ } ++ ++ kaeDebug.println("useKaeEngineFlags: "); ++ for (int i = 0; i < useKaeEngineFlags.length; i++) { ++ kaeDebug.println(algorithmNames[i] + "=" + useKaeEngineFlags[i]); ++ } ++ } ++ } ++ ++ public static boolean[] getUseKaeEngineFlags() { ++ return useKaeEngineFlags; ++ } ++ ++ private static void initAlgorithmNameIndexMap() { ++ for (int i = 0; i < algorithmNames.length; i++) { ++ algorithmNameIndexMap.put(algorithmNames[i], i); ++ } ++ } ++ ++ /* ++ * 0 : digest ++ * 1 : aes ++ * 2 : sm4 ++ * 3 : hmac ++ * 4 : rsa ++ * 5 : dh ++ * 6 : ec ++ */ ++ private static void initAlgorithmNameCategoryMap() { ++ algorithmNameCategoryMap.put("md5", 0); ++ algorithmNameCategoryMap.put("sha256", 0); ++ algorithmNameCategoryMap.put("sha384", 0); ++ algorithmNameCategoryMap.put("sm3", 0); ++ algorithmNameCategoryMap.put("aes-128-ecb", 1); ++ algorithmNameCategoryMap.put("aes-128-cbc", 1); ++ algorithmNameCategoryMap.put("aes-128-ctr", 1); ++ algorithmNameCategoryMap.put("aes-128-gcm", 1); ++ algorithmNameCategoryMap.put("aes-192-ecb", 1); ++ algorithmNameCategoryMap.put("aes-192-cbc", 1); ++ algorithmNameCategoryMap.put("aes-192-ctr", 1); ++ algorithmNameCategoryMap.put("aes-192-gcm", 1); ++ algorithmNameCategoryMap.put("aes-256-ecb", 1); ++ algorithmNameCategoryMap.put("aes-256-cbc", 1); ++ algorithmNameCategoryMap.put("aes-256-ctr", 1); ++ algorithmNameCategoryMap.put("aes-256-gcm", 1); ++ algorithmNameCategoryMap.put("sm4-ecb", 2); ++ algorithmNameCategoryMap.put("sm4-cbc", 2); ++ algorithmNameCategoryMap.put("sm4-ctr", 2); ++ algorithmNameCategoryMap.put("sm4-ofb", 2); ++ algorithmNameCategoryMap.put("hmac-md5", 3); ++ algorithmNameCategoryMap.put("hmac-sha1", 3); ++ algorithmNameCategoryMap.put("hmac-sha224", 3); ++ algorithmNameCategoryMap.put("hmac-sha256", 3); ++ algorithmNameCategoryMap.put("hmac-sha384", 3); ++ algorithmNameCategoryMap.put("hmac-sha512", 3); ++ algorithmNameCategoryMap.put("rsa", 4); ++ algorithmNameCategoryMap.put("dh", 5); ++ algorithmNameCategoryMap.put("ec", 6); ++ } ++ ++ private static void initAlgorithmNameMap() { ++ initAlgorithmNameIndexMap(); ++ initAlgorithmNameCategoryMap(); ++ } ++ ++ private static String[] getDisabledAlgorithms() { ++ String disabledAlgorithms = privilegedGetOverridable("kae.engine.disabledAlgorithms", ++ "sha256,sha384"); ++ return disabledAlgorithms.replaceAll(" ", "").split("\\,"); ++ } ++ ++ public static String privilegedGetProperty(String key) { ++ if (System.getSecurityManager() == null) { ++ return getProperty(key); ++ } else { ++ return AccessController.doPrivileged((PrivilegedAction) () -> getOverridableProperty(key)); ++ } ++ } ++ ++ public static String privilegedGetOverridable(String key) { ++ if (System.getSecurityManager() == null) { ++ return getOverridableProperty(key); ++ } else { ++ return AccessController.doPrivileged((PrivilegedAction) () -> getOverridableProperty(key)); ++ } ++ } ++ ++ public static String privilegedGetOverridable(String key, String defaultValue) { ++ String val = privilegedGetOverridable(key); ++ return (val == null) ? defaultValue : val; ++ } ++ ++ private static String getProperty(String key) { ++ String val = props.getProperty(key); ++ if (val != null) ++ val = val.trim(); ++ return val; ++ } ++ ++ private static String getOverridableProperty(String key) { ++ String val = System.getProperty(key); ++ if (val == null) { ++ return getProperty(key); ++ } else { ++ return val; ++ } ++ } ++ ++ public static String getAlgorithmName(int index) { ++ if (index < 0 || index >= algorithmNames.length) { ++ throw new IndexOutOfBoundsException(); ++ } ++ return algorithmNames[index]; ++ } ++} +diff --git a/jdk/src/solaris/classes/org/openeuler/security/openssl/KAELog.java b/jdk/src/solaris/classes/org/openeuler/security/openssl/KAELog.java +new file mode 100644 +index 000000000..434f773a1 +--- /dev/null ++++ b/jdk/src/solaris/classes/org/openeuler/security/openssl/KAELog.java +@@ -0,0 +1,183 @@ ++/* ++ * Copyright (c) 2022, Huawei Technologies Co., Ltd. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ */ ++ ++package org.openeuler.security.openssl; ++ ++import sun.security.util.Debug; ++ ++import java.io.BufferedWriter; ++import java.io.File; ++import java.io.IOException; ++import java.nio.file.Files; ++import java.nio.file.Path; ++import java.nio.file.StandardOpenOption; ++import java.security.AccessController; ++import java.security.PrivilegedAction; ++import java.text.SimpleDateFormat; ++import java.util.Arrays; ++import java.util.Date; ++ ++public class KAELog { ++ private static final Debug kaeDebug = Debug.getInstance("kae"); ++ private static File logFile; ++ private static boolean exist; ++ ++ private KAELog() { ++ ++ } ++ ++ static { ++ AccessController.doPrivileged(new PrivilegedAction() { ++ public Void run() { ++ initialize(); ++ return null; ++ } ++ }); ++ } ++ ++ private static void initialize() { ++ if (!enableKaeLog()) { ++ if (kaeDebug != null) { ++ kaeDebug.println("kae logging is not enabled"); ++ } ++ return; ++ } ++ ++ logFile = kaeLogFile("kae.log"); ++ File parentFile = logFile.getParentFile(); ++ if (!parentFile.exists()) { ++ try { ++ Files.createDirectories(parentFile.toPath()); ++ } catch (IOException e) { ++ if (kaeDebug != null) { ++ kaeDebug.println("failed to create directory :" + parentFile); ++ e.printStackTrace(); ++ } ++ return; ++ } ++ } ++ ++ if (logFile.exists()) { ++ if (kaeDebug != null) { ++ kaeDebug.println("found kae log file :" + logFile); ++ } ++ exist = true; ++ } else { ++ if (kaeDebug != null) { ++ kaeDebug.println("not found kae log file :" + logFile); ++ } ++ try { ++ Path path = Files.createFile(logFile.toPath()); ++ if (path != null) { ++ exist = true; ++ } ++ } catch (IOException e) { ++ if (kaeDebug != null) { ++ kaeDebug.println("unable to create new kae log file :" + logFile); ++ e.printStackTrace(); ++ } ++ } ++ ++ if (exist) { ++ if (kaeDebug != null) { ++ kaeDebug.println("create new kae log file :" + logFile); ++ } ++ } ++ } ++ } ++ ++ public static boolean enableKaeLog() { ++ String debug = KAEConfig.privilegedGetOverridable("kae.log"); ++ return Boolean.parseBoolean(debug); ++ } ++ ++ private static File kaeLogFile(String filename) { ++ String sep = File.separator; ++ String defaultKaeLog = System.getProperty("user.dir") + sep + filename; ++ String kaeLog = KAEConfig.privilegedGetOverridable("kae.log.file", defaultKaeLog); ++ return new File(kaeLog); ++ } ++ ++ private static String getLogTime() { ++ SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); ++ return simpleDateFormat.format(new Date()); ++ } ++ ++ public static void log(String engineId, Throwable throwable, boolean[] engineFlags, boolean[] kaeProviderFlags) { ++ if (engineFlags.length != kaeProviderFlags.length) { ++ if (kaeDebug != null) { ++ kaeDebug.println("The length of engineFlags is not equal to the length of kaeProviderFlags."); ++ kaeDebug.println(String.format("engineFlags : %s", Arrays.toString(engineFlags))); ++ kaeDebug.println(String.format("kaeProviderFlags : %s", Arrays.toString(kaeProviderFlags))); ++ } ++ return; ++ } ++ if (!exist) { ++ return; ++ } ++ ++ try (BufferedWriter writer = Files.newBufferedWriter(logFile.toPath(), ++ StandardOpenOption.APPEND)) { ++ logEngine(writer, engineId, throwable); ++ writer.newLine(); ++ logAlgorithmStrategy(writer, engineFlags, kaeProviderFlags); ++ writer.newLine(); ++ } catch (IOException e) { ++ if (kaeDebug != null) { ++ kaeDebug.println("write kae log failed"); ++ e.printStackTrace(); ++ } ++ } ++ } ++ ++ // log engine ++ private static void logEngine(BufferedWriter writer, String engineId, Throwable throwable) throws IOException { ++ writer.write(String.format("[%s] ", getLogTime())); ++ if (throwable == null) { ++ writer.write(String.format("%s engine was found.", engineId)); ++ } else if (throwable instanceof RuntimeException) { ++ writer.write(String.format("%s engine was not found. %s", engineId, throwable.getMessage())); ++ } else { ++ writer.write(throwable.getMessage()); ++ } ++ } ++ ++ // log algorithm strategy ++ private static void logAlgorithmStrategy(BufferedWriter writer, boolean[] engineFlags, boolean[] kaeProviderFlags) ++ throws IOException { ++ writer.write(String.format("[%s] ", getLogTime())); ++ writer.write("The implementation strategy of each algorithm is as follows : "); ++ for (int i = 0; i < engineFlags.length; i++) { ++ writer.newLine(); ++ String algorithmName = KAEConfig.getAlgorithmName(i); ++ String message; ++ if (kaeProviderFlags[i]) { ++ String detail = engineFlags[i] ? "enable KAE hardware acceleration" : "Use openssl soft calculation"; ++ message = String.format(" %-11s => %s: %s", algorithmName, "KAEProvider", detail); ++ } else { ++ message = String.format(" %-11s => %s", algorithmName, "Non-KAEProvider"); ++ } ++ writer.write(message); ++ } ++ } ++} +diff --git a/jdk/src/solaris/classes/org/openeuler/security/openssl/KAEProvider.java b/jdk/src/solaris/classes/org/openeuler/security/openssl/KAEProvider.java +index 83ed8649c..3e7f54638 100644 +--- a/jdk/src/solaris/classes/org/openeuler/security/openssl/KAEProvider.java ++++ b/jdk/src/solaris/classes/org/openeuler/security/openssl/KAEProvider.java +@@ -24,116 +24,103 @@ + + package org.openeuler.security.openssl; + +-import java.io.BufferedWriter; +-import java.io.BufferedInputStream; +-import java.io.File; +-import java.io.FileInputStream; +-import java.io.InputStream; +-import java.io.IOException; +-import java.nio.file.Files; +-import java.nio.file.Path; +-import java.nio.file.Paths; +-import java.nio.file.StandardOpenOption; +-import java.util.Date; +-import java.util.Properties; ++import sun.security.util.Debug; ++ ++import java.security.AccessController; ++import java.security.PrivilegedAction; + import java.security.Provider; + + /** + * KAE Provider + */ + public class KAEProvider extends Provider { +- private static Throwable excp; +- private static boolean needLog = true; ++ private static final Debug kaeDebug = Debug.getInstance("kae"); ++ ++ // default engine id ++ private static final String DEFAULT_ENGINE_ID = "kae"; + + static { +- Throwable status = null; +- try { +- System.loadLibrary("j2kae"); +- initOpenssl(); +- } catch (UnsatisfiedLinkError t) { +- status = t; +- } catch (RuntimeException e) { +- status = e; +- } +- excp = status; ++ initialize(); + } + +- private void logStart(Throwable excp) { +- File file = new File(System.getProperty("user.dir"), "kae.log"); +- Path fpath = file.toPath(); +- if (!Files.exists(fpath)) { +- try { +- file.createNewFile(); +- } catch (IOException e) { +- e.printStackTrace(); +- } +- } ++ private static void initialize() { ++ loadLibrary(); ++ initOpenssl(); ++ } + +- try (BufferedWriter writer = Files.newBufferedWriter(fpath, StandardOpenOption.APPEND)) { +- if (excp != null) { +- writer.write(excp.getMessage()); +- } else { +- writer.write("KAE Engine was found"); ++ // load kae.so ++ private static void loadLibrary() { ++ AccessController.doPrivileged(new PrivilegedAction() { ++ @Override ++ public Object run() { ++ System.loadLibrary("j2kae"); ++ return null; + } +- writer.write(" " + new Date()); +- writer.newLine(); +- } catch (IOException e) { +- e.initCause(excp).printStackTrace(); +- } +- KAEProvider.excp = null; // Exception already logged, clean it. ++ }); + } + +- private Properties getProp() { +- Properties props = new Properties(); +- String sep = File.separator; +- File propFile = new File(System.getProperty("java.home") + sep + "lib" + sep + +- "ext" + sep + "kaeprovider.conf"); +- if (propFile.exists()) { +- try (InputStream is = new BufferedInputStream(new FileInputStream(propFile))) { +- props.load(is); +- } catch (IOException e) { +- e.printStackTrace(); ++ // init openssl ++ private static void initOpenssl() { ++ boolean useGlobalMode = useGlobalMode(); ++ String engineId = getEngineId(); ++ boolean[] algorithmKaeFlags = KAEConfig.getUseKaeEngineFlags(); ++ Throwable throwable = null; ++ try { ++ initOpenssl(useGlobalMode, engineId, algorithmKaeFlags); ++ } catch (Throwable t) { ++ throwable = t; ++ if (kaeDebug != null) { ++ kaeDebug.println("initOpenssl failed : " + throwable.getMessage()); + } + } +- return props; ++ boolean[] engineFlags = getEngineFlags(); ++ boolean[] kaeProviderFlags = KAEConfig.getUseKaeProviderFlags(); ++ KAELog.log(engineId, throwable, engineFlags, kaeProviderFlags); ++ } ++ ++ // get engine id ++ private static String getEngineId() { ++ return KAEConfig.privilegedGetOverridable("kae.engine.id", DEFAULT_ENGINE_ID); ++ } ++ ++ // whether to set libcrypto.so to GLOBAL mode, by default libcrypto.so is LOCAL mode ++ private static boolean useGlobalMode() { ++ String explicitLoad = KAEConfig.privilegedGetOverridable( ++ "kae.libcrypto.useGlobalMode", "false"); ++ return Boolean.parseBoolean(explicitLoad); + } + + public KAEProvider() { + super("KAEProvider", 1.8d, "KAE provider"); +- Properties props = getProp(); +- if (needLog && "true".equalsIgnoreCase(props.getProperty("kae.log"))) { +- logStart(excp); +- needLog = false; // Log only once +- } +- if (!"false".equalsIgnoreCase(props.getProperty("kae.md5"))) { ++ if (KAEConfig.useKaeProvider("kae.md5")) { + putMD5(); + } +- if (!"false".equalsIgnoreCase(props.getProperty("kae.sha256"))) { ++ if (KAEConfig.useKaeProvider("kae.sha256")) { + putSHA256(); + } +- if (!"false".equalsIgnoreCase(props.getProperty("kae.sha384"))) { ++ if (KAEConfig.useKaeProvider("kae.sha384")) { + putSHA384(); + } +- if (!"false".equalsIgnoreCase(props.getProperty("kae.sm3"))) { ++ if (KAEConfig.useKaeProvider("kae.sm3")) { + putSM3(); + } +- if (!"false".equalsIgnoreCase(props.getProperty("kae.aes"))) { ++ if (KAEConfig.useKaeProvider("kae.aes")) { + putAES(); + } +- if (!"false".equalsIgnoreCase(props.getProperty("kae.sm4"))) { ++ if (KAEConfig.useKaeProvider("kae.sm4")) { + putSM4(); + } +- if (!"false".equalsIgnoreCase(props.getProperty("kae.hmac"))) { ++ if (KAEConfig.useKaeProvider("kae.hmac")) { + putHMAC(); + } +- if (!"false".equalsIgnoreCase(props.getProperty("kae.rsa"))) { ++ if (KAEConfig.useKaeProvider("kae.rsa")) { + putRSA(); + putSignatureRSA(); + } +- if (!"false".equalsIgnoreCase(props.getProperty("kae.dh"))) { ++ if (KAEConfig.useKaeProvider("kae.dh")) { + putDH(); + } +- if (!"false".equalsIgnoreCase(props.getProperty("kae.ec"))) { ++ if (KAEConfig.useKaeProvider("kae.ec")) { + putEC(); + } + } +@@ -285,28 +272,28 @@ public class KAEProvider extends Provider { + "org.openeuler.security.openssl.KAERSASignature$SHA512withRSA"); + + // alias +- put("Alg.Alias.Signature.1.2.840.113549.1.1.4", "MD5withRSA"); ++ put("Alg.Alias.Signature.1.2.840.113549.1.1.4", "MD5withRSA"); + put("Alg.Alias.Signature.OID.1.2.840.113549.1.1.4", "MD5withRSA"); + +- put("Alg.Alias.Signature.1.2.840.113549.1.1.5", "SHA1withRSA"); ++ put("Alg.Alias.Signature.1.2.840.113549.1.1.5", "SHA1withRSA"); + put("Alg.Alias.Signature.OID.1.2.840.113549.1.1.5", "SHA1withRSA"); +- put("Alg.Alias.Signature.1.3.14.3.2.29", "SHA1withRSA"); ++ put("Alg.Alias.Signature.1.3.14.3.2.29", "SHA1withRSA"); + +- put("Alg.Alias.Signature.1.2.840.113549.1.1.14", "SHA224withRSA"); ++ put("Alg.Alias.Signature.1.2.840.113549.1.1.14", "SHA224withRSA"); + put("Alg.Alias.Signature.OID.1.2.840.113549.1.1.14", "SHA224withRSA"); + +- put("Alg.Alias.Signature.1.2.840.113549.1.1.11", "SHA256withRSA"); ++ put("Alg.Alias.Signature.1.2.840.113549.1.1.11", "SHA256withRSA"); + put("Alg.Alias.Signature.OID.1.2.840.113549.1.1.11", "SHA256withRSA"); + +- put("Alg.Alias.Signature.1.2.840.113549.1.1.12", "SHA384withRSA"); ++ put("Alg.Alias.Signature.1.2.840.113549.1.1.12", "SHA384withRSA"); + put("Alg.Alias.Signature.OID.1.2.840.113549.1.1.12", "SHA384withRSA"); + +- put("Alg.Alias.Signature.1.2.840.113549.1.1.13", "SHA512withRSA"); ++ put("Alg.Alias.Signature.1.2.840.113549.1.1.13", "SHA512withRSA"); + put("Alg.Alias.Signature.OID.1.2.840.113549.1.1.13", "SHA512withRSA"); + + put("Signature.RSASSA-PSS", "org.openeuler.security.openssl.KAERSAPSSSignature"); + +- put("Alg.Alias.Signature.1.2.840.113549.1.1.10", "RSASSA-PSS"); ++ put("Alg.Alias.Signature.1.2.840.113549.1.1.10", "RSASSA-PSS"); + put("Alg.Alias.Signature.OID.1.2.840.113549.1.1.10", "RSASSA-PSS"); + + // attributes for supported key classes +@@ -326,6 +313,10 @@ public class KAEProvider extends Provider { + put("Alg.Alias.KeyPairGenerator.EllipticCurve", "EC"); + put("KeyAgreement.ECDH", "org.openeuler.security.openssl.KAEECDHKeyAgreement"); + } ++ + // init openssl +- static native void initOpenssl() throws RuntimeException; ++ static native void initOpenssl(boolean useGlobalMode, String engineId, boolean[] algorithmKaeFlags) ++ throws RuntimeException; ++ ++ static native boolean[] getEngineFlags(); + } +diff --git a/jdk/src/solaris/classes/org/openeuler/security/openssl/KAESM4Cipher.java b/jdk/src/solaris/classes/org/openeuler/security/openssl/KAESM4Cipher.java +index b189bea3a..cca619e1a 100644 +--- a/jdk/src/solaris/classes/org/openeuler/security/openssl/KAESM4Cipher.java ++++ b/jdk/src/solaris/classes/org/openeuler/security/openssl/KAESM4Cipher.java +@@ -26,13 +26,20 @@ + + package org.openeuler.security.openssl; + ++import sun.security.util.Debug; ++ ++import java.nio.ByteBuffer; + import java.security.InvalidAlgorithmParameterException; + import java.security.InvalidKeyException; + import java.security.NoSuchAlgorithmException; + import java.security.Key; ++import java.security.ProviderException; + import java.util.Locale; + ++import javax.crypto.BadPaddingException; ++import javax.crypto.IllegalBlockSizeException; + import javax.crypto.NoSuchPaddingException; ++import javax.crypto.ShortBufferException; + + /* + * This class currently supports: +@@ -46,6 +53,55 @@ import javax.crypto.NoSuchPaddingException; + */ + abstract class KAESM4Cipher extends KAESymmetricCipherBase { + ++ private static final Debug debug = Debug.getInstance("kae"); ++ ++ /* ++ * SM4 max chunk size of each encryption or decryption ++ * when input data does not have an accessible byte[] ++ */ ++ private static final int DEFAULT_KAE_SM4_MAX_CHUNK_SIZE = 4096; ++ private static int KAE_SM4_MAX_CHUNK_SIZE; ++ static { ++ initSM4MaxChunkSize(); ++ } ++ ++ private static void initSM4MaxChunkSize() { ++ String maxChunkSize = KAEConfig.privilegedGetOverridable("kae.sm4.maxChunkSize", ++ DEFAULT_KAE_SM4_MAX_CHUNK_SIZE + ""); ++ try { ++ KAE_SM4_MAX_CHUNK_SIZE = Integer.parseInt(maxChunkSize); ++ } catch (NumberFormatException e) { ++ // When parsing string argument to signed decimal integer fails, uses the default chunk size (4096) ++ KAE_SM4_MAX_CHUNK_SIZE = DEFAULT_KAE_SM4_MAX_CHUNK_SIZE; ++ if (debug != null) { ++ debug.println("The configured block size (" + maxChunkSize + ") cannot be converted to an integer, " + ++ "uses the default chunk size (" + DEFAULT_KAE_SM4_MAX_CHUNK_SIZE + ")"); ++ e.printStackTrace(); ++ } ++ return; ++ } ++ // when the configured chunk size is less than or equal to 0, uses the default chunk size (4096) ++ if (KAE_SM4_MAX_CHUNK_SIZE <= 0) { ++ KAE_SM4_MAX_CHUNK_SIZE = DEFAULT_KAE_SM4_MAX_CHUNK_SIZE; ++ if (debug != null) { ++ debug.println("The configured chunk size (" + KAE_SM4_MAX_CHUNK_SIZE + ") is less than " + ++ "or equal to 0, uses the default chunk size (" + DEFAULT_KAE_SM4_MAX_CHUNK_SIZE + ")"); ++ } ++ return; ++ } ++ if (debug != null) { ++ debug.println("The configured chunk size is " + KAE_SM4_MAX_CHUNK_SIZE); ++ } ++ } ++ ++ /** ++ * Used by the engineUpdate(ByteBuffer, ByteBuffer) and ++ * engineDoFinal(ByteBuffer, ByteBuffer) methods. ++ */ ++ private static int getSM4MaxChunkSize(int totalSize) { ++ return Math.min(KAE_SM4_MAX_CHUNK_SIZE, totalSize); ++ } ++ + public static class Sm4 extends KAESM4Cipher { + public Sm4(Mode mode, Padding padding) { + super(mode, padding, 16); +@@ -170,6 +226,131 @@ abstract class KAESM4Cipher extends KAESymmetricCipherBase { + } + } + ++ @Override ++ protected int engineUpdate(ByteBuffer input, ByteBuffer output) throws ShortBufferException { ++ try { ++ return bufferCrypt(input, output, true); ++ } catch (IllegalBlockSizeException e) { ++ // never thrown for engineUpdate() ++ throw new ProviderException("Internal error in update()"); ++ } catch (BadPaddingException e) { ++ // never thrown for engineUpdate() ++ throw new ProviderException("Internal error in update()"); ++ } ++ } ++ ++ @Override ++ protected int engineDoFinal(ByteBuffer input, ByteBuffer output) ++ throws ShortBufferException, IllegalBlockSizeException, BadPaddingException { ++ return bufferCrypt(input, output, false); ++ } ++ ++ /** ++ * Implementation for encryption using ByteBuffers. Used for both ++ * engineUpdate() and engineDoFinal(). ++ */ ++ private int bufferCrypt(ByteBuffer input, ByteBuffer output, ++ boolean isUpdate) throws ShortBufferException, ++ IllegalBlockSizeException, BadPaddingException { ++ if ((input == null) || (output == null)) { ++ throw new NullPointerException ++ ("Input and output buffers must not be null"); ++ } ++ int inPos = input.position(); ++ int inLimit = input.limit(); ++ int inLen = inLimit - inPos; ++ if (isUpdate && (inLen == 0)) { ++ return 0; ++ } ++ int outLenNeeded = engineGetOutputSize(inLen); ++ ++ if (output.remaining() < outLenNeeded) { ++ throw new ShortBufferException("Need at least " + outLenNeeded ++ + " bytes of space in output buffer"); ++ } ++ ++ // detecting input and output buffer overlap may be tricky ++ // we can only write directly into output buffer when we ++ // are 100% sure it's safe to do so ++ ++ boolean a1 = input.hasArray(); ++ boolean a2 = output.hasArray(); ++ int total = 0; ++ ++ if (a1) { // input has an accessible byte[] ++ byte[] inArray = input.array(); ++ int inOfs = input.arrayOffset() + inPos; ++ ++ byte[] outArray; ++ if (a2) { // output has an accessible byte[] ++ outArray = output.array(); ++ int outPos = output.position(); ++ int outOfs = output.arrayOffset() + outPos; ++ ++ // check array address and offsets and use temp output buffer ++ // if output offset is larger than input offset and ++ // falls within the range of input data ++ boolean useTempOut = false; ++ if (inArray == outArray && ++ ((inOfs < outOfs) && (outOfs < inOfs + inLen))) { ++ useTempOut = true; ++ outArray = new byte[outLenNeeded]; ++ outOfs = 0; ++ } ++ if (isUpdate) { ++ total = engineUpdate(inArray, inOfs, inLen, outArray, outOfs); ++ } else { ++ total = engineDoFinal(inArray, inOfs, inLen, outArray, outOfs); ++ } ++ if (useTempOut) { ++ output.put(outArray, outOfs, total); ++ } else { ++ // adjust output position manually ++ output.position(outPos + total); ++ } ++ } else { // output does not have an accessible byte[] ++ if (isUpdate) { ++ outArray = engineUpdate(inArray, inOfs, inLen); ++ } else { ++ outArray = engineDoFinal(inArray, inOfs, inLen); ++ } ++ if (outArray != null && outArray.length != 0) { ++ output.put(outArray); ++ total = outArray.length; ++ } ++ } ++ // adjust input position manually ++ input.position(inLimit); ++ } else { // input does not have an accessible byte[] ++ // have to assume the worst, since we have no way of determine ++ // if input and output overlaps or not ++ byte[] tempOut = new byte[outLenNeeded]; ++ int outOfs = 0; ++ ++ byte[] tempIn = new byte[getSM4MaxChunkSize(inLen)]; ++ do { ++ int chunk = Math.min(inLen, tempIn.length); ++ if (chunk > 0) { ++ input.get(tempIn, 0, chunk); ++ } ++ int n; ++ if (isUpdate || (inLen > chunk)) { ++ n = engineUpdate(tempIn, 0, chunk, tempOut, outOfs); ++ } else { ++ n = engineDoFinal(tempIn, 0, chunk, tempOut, outOfs); ++ } ++ outOfs += n; ++ total += n; ++ inLen -= chunk; ++ } while (inLen > 0); ++ if (total > 0) { ++ output.put(tempOut, 0, total); ++ } ++ } ++ ++ return total; ++ } ++ + protected void checkIvBytes(byte[] ivBytes) throws InvalidAlgorithmParameterException { + if (ivBytes == null) { + throw new InvalidAlgorithmParameterException("Wrong IV length: iv is null "); +diff --git a/jdk/src/solaris/native/org/openeuler/security/openssl/kae_cipher_rsa.c b/jdk/src/solaris/native/org/openeuler/security/openssl/kae_cipher_rsa.c +index 80a0e58b9..d9b16ab9d 100644 +--- a/jdk/src/solaris/native/org/openeuler/security/openssl/kae_cipher_rsa.c ++++ b/jdk/src/solaris/native/org/openeuler/security/openssl/kae_cipher_rsa.c +@@ -24,12 +24,11 @@ + #include + #include + #include ++#include "kae_log.h" + #include "kae_util.h" + #include "kae_exception.h" + #include "org_openeuler_security_openssl_KAERSACipher.h" + +-static ENGINE* kaeEngine = NULL; +- + typedef int RSACryptOperation(int, const unsigned char*, unsigned char*, RSA*, int); + + typedef int EvpPkeyCryptOperation(EVP_PKEY_CTX*, unsigned char*, size_t*, const unsigned char*, size_t); +@@ -176,7 +175,9 @@ static int RSACryptOAEPPadding(JNIEnv* env, jlong keyAddress, jint inLen, jbyteA + // outLen type should be size_t + // EVP_PKEY_encrypt takes the outLen address as a parameter, and the parameter type is size_t* + size_t outLen = 0; +- kaeEngine = (kaeEngine == NULL) ? GetKaeEngine() : kaeEngine; ++ ENGINE* kaeEngine = GetEngineByAlgorithmIndex(RSA_INDEX); ++ KAE_TRACE("RSACryptOAEPPadding: kaeEngine => %p", kaeEngine); ++ + + EVP_PKEY* pkey = (EVP_PKEY*) keyAddress; + +@@ -272,7 +273,8 @@ JNIEXPORT jlong JNICALL Java_org_openeuler_security_openssl_KAERSACipher_nativeC + BIGNUM* bnIQMP = NULL; + RSA* rsa = NULL; + EVP_PKEY* pkey = NULL; +- kaeEngine = (kaeEngine == NULL) ? GetKaeEngine() : kaeEngine; ++ ENGINE* kaeEngine = GetEngineByAlgorithmIndex(RSA_INDEX); ++ KAE_TRACE("KAERSACipher_nativeCreateRSAPrivateCrtKey: kaeEngine => %p", kaeEngine); + + // convert to big num + if ((bnN = KAE_GetBigNumFromByteArray(env, n)) == NULL || +@@ -334,7 +336,8 @@ JNIEXPORT jlong JNICALL Java_org_openeuler_security_openssl_KAERSACipher_nativeC + BIGNUM* bnE = NULL; + RSA* rsa = NULL; + EVP_PKEY* pkey = NULL; +- kaeEngine = (kaeEngine == NULL) ? GetKaeEngine() : kaeEngine; ++ ENGINE* kaeEngine = GetEngineByAlgorithmIndex(RSA_INDEX); ++ KAE_TRACE("KAERSACipher_nativeCreateRSAPublicKey: kaeEngine => %p", kaeEngine); + + // get public key param n + bnN = KAE_GetBigNumFromByteArray(env, n); +diff --git a/jdk/src/solaris/native/org/openeuler/security/openssl/kae_digest.c b/jdk/src/solaris/native/org/openeuler/security/openssl/kae_digest.c +index f0e7b0be4..23b178978 100644 +--- a/jdk/src/solaris/native/org/openeuler/security/openssl/kae_digest.c ++++ b/jdk/src/solaris/native/org/openeuler/security/openssl/kae_digest.c +@@ -42,7 +42,7 @@ JNIEXPORT jlong JNICALL + Java_org_openeuler_security_openssl_KAEDigest_nativeInit(JNIEnv *env, jclass cls, jstring algorithmName) + { + EVP_MD_CTX* ctx = NULL; +- static ENGINE* kaeEngine = NULL; ++ ENGINE* kaeEngine = NULL; + + if (algorithmName == NULL) { + KAE_ThrowNullPointerException(env, "algorithm is null"); +@@ -51,11 +51,8 @@ Java_org_openeuler_security_openssl_KAEDigest_nativeInit(JNIEnv *env, jclass cls + + // EVP_get_digestbyname + const char* algo_utf = (*env)->GetStringUTFChars(env, algorithmName, 0); +- if ((strcasecmp(algo_utf, "md5") == 0) || (strcasecmp(algo_utf, "sm3") == 0)) { +- kaeEngine = (kaeEngine == NULL) ? GetKaeEngine() : kaeEngine; +- } else { +- kaeEngine = NULL; +- } ++ kaeEngine = GetDigestEngineByAlgorithmName(algo_utf); ++ KAE_TRACE("KAEDigest_nativeInit: kaeEngine => %p", kaeEngine); + EVP_MD* md = (EVP_MD*) EVP_get_digestbyname(algo_utf); + (*env)->ReleaseStringUTFChars(env, algorithmName, algo_utf); + if (md == NULL) { +diff --git a/jdk/src/solaris/native/org/openeuler/security/openssl/kae_hmac.c b/jdk/src/solaris/native/org/openeuler/security/openssl/kae_hmac.c +index 554a9750c..1efacbb5b 100644 +--- a/jdk/src/solaris/native/org/openeuler/security/openssl/kae_hmac.c ++++ b/jdk/src/solaris/native/org/openeuler/security/openssl/kae_hmac.c +@@ -73,9 +73,14 @@ JNIEXPORT jlong JNICALL Java_org_openeuler_security_openssl_KAEHMac_nativeInit + HMAC_CTX* ctx = NULL; + jbyte* key_buffer = NULL; + const EVP_MD* md = NULL; ++ ENGINE* kaeEngine = NULL; + + const char* algo = (*env)->GetStringUTFChars(env, algoStr, 0); +- md = EVPGetDigestByName(env, algo); ++ md = EVPGetDigestByName(env, algo); ++ ++ kaeEngine = GetHmacEngineByAlgorithmName(algo); ++ KAE_TRACE("KAEHMac_nativeInit: kaeEngine => %p", kaeEngine); ++ + (*env)->ReleaseStringUTFChars(env, algoStr, algo); + if (md == NULL) { + KAE_ThrowRuntimeException(env, "algorithm unsupport"); +@@ -98,7 +103,7 @@ JNIEXPORT jlong JNICALL Java_org_openeuler_security_openssl_KAEHMac_nativeInit + } + + // init hmac context with sc_key and evp_md +- int result_code = HMAC_Init_ex(ctx, key_buffer, key_len, md, NULL); ++ int result_code = HMAC_Init_ex(ctx, key_buffer, key_len, md, kaeEngine); + if (result_code == 0) { + KAE_ThrowRuntimeException(env, "Hmac_Init_ex invoked failed"); + goto cleanup; +diff --git a/jdk/src/solaris/native/org/openeuler/security/openssl/kae_keyagreement_dh.c b/jdk/src/solaris/native/org/openeuler/security/openssl/kae_keyagreement_dh.c +index 7cdf790cb..d8d2ee7cb 100644 +--- a/jdk/src/solaris/native/org/openeuler/security/openssl/kae_keyagreement_dh.c ++++ b/jdk/src/solaris/native/org/openeuler/security/openssl/kae_keyagreement_dh.c +@@ -51,8 +51,8 @@ JNIEXPORT jbyteArray JNICALL Java_org_openeuler_security_openssl_KAEDHKeyAgreeme + int computekeyLength = 0; + unsigned char* secret = NULL; + jbyteArray retByteArray = NULL; +- static ENGINE* kaeEngine = NULL; +- kaeEngine = (kaeEngine == NULL) ? GetKaeEngine() : kaeEngine; ++ ENGINE* kaeEngine = GetEngineByAlgorithmIndex(DH_INDEX); ++ KAE_TRACE("KAEDHKeyAgreement_nativeComputeKey: kaeEngine => %p", kaeEngine); + + // bits to Bytes + int pSizeInByte = (pSize +7) >> 3; +diff --git a/jdk/src/solaris/native/org/openeuler/security/openssl/kae_keypairgenerator_dh.c b/jdk/src/solaris/native/org/openeuler/security/openssl/kae_keypairgenerator_dh.c +index 54dc07edd..d16b42b41 100644 +--- a/jdk/src/solaris/native/org/openeuler/security/openssl/kae_keypairgenerator_dh.c ++++ b/jdk/src/solaris/native/org/openeuler/security/openssl/kae_keypairgenerator_dh.c +@@ -50,8 +50,8 @@ JNIEXPORT jobjectArray JNICALL Java_org_openeuler_security_openssl_KAEDHKeyPairG + jobjectArray keys = NULL; + jbyteArray pri_key = NULL; + jbyteArray pub_key = NULL; +- static ENGINE* kaeEngine = NULL; +- kaeEngine = (kaeEngine == NULL) ? GetKaeEngine() : kaeEngine; ++ ENGINE* kaeEngine = GetEngineByAlgorithmIndex(DH_INDEX); ++ KAE_TRACE("KAEDHKeyPairGenerator_nativeGenerateKeyPair: kaeEngine => %p", kaeEngine); + + KAE_TRACE("Java_org_openeuler_security_openssl_KAEDHKeyPairGenerator_nativeGenerateKeyPair start !"); + +diff --git a/jdk/src/solaris/native/org/openeuler/security/openssl/kae_keypairgenerator_rsa.c b/jdk/src/solaris/native/org/openeuler/security/openssl/kae_keypairgenerator_rsa.c +index 2ca978bbe..9251b56c4 100644 +--- a/jdk/src/solaris/native/org/openeuler/security/openssl/kae_keypairgenerator_rsa.c ++++ b/jdk/src/solaris/native/org/openeuler/security/openssl/kae_keypairgenerator_rsa.c +@@ -23,6 +23,7 @@ + + #include + #include ++#include "kae_log.h" + #include "kae_util.h" + #include "kae_exception.h" + #include "org_openeuler_security_openssl_KAERSAKeyPairGenerator.h" +@@ -62,8 +63,9 @@ static const BIGNUM* (* GetRSAParamFunctionList[])(const RSA*) = { + * step 3.Generate rsa key, and all key information is stored in RSA + */ + static RSA* NewRSA(JNIEnv* env, jint keySize, jbyteArray publicExponent) { +- static ENGINE* kaeEngine = NULL; +- kaeEngine = (kaeEngine == NULL) ? GetKaeEngine() : kaeEngine; ++ ENGINE* kaeEngine = GetEngineByAlgorithmIndex(RSA_INDEX); ++ KAE_TRACE("NewRSA: kaeEngine => %p", kaeEngine); ++ + // new rsa + RSA* rsa = RSA_new_method(kaeEngine); + if (rsa == NULL) { +diff --git a/jdk/src/solaris/native/org/openeuler/security/openssl/kae_provider.c b/jdk/src/solaris/native/org/openeuler/security/openssl/kae_provider.c +index aa46e737e..fca035b04 100644 +--- a/jdk/src/solaris/native/org/openeuler/security/openssl/kae_provider.c ++++ b/jdk/src/solaris/native/org/openeuler/security/openssl/kae_provider.c +@@ -24,21 +24,53 @@ + #include + #include + #include ++#include + #include "kae_exception.h" + #include "kae_util.h" + #include "org_openeuler_security_openssl_KAEProvider.h" + ++#define KAE_OPENSSL_LIBRARY "libcrypto.so" ++ + /* + * Class: Java_org_openeuler_security_openssl_KAEProvider + * Method: initOpenssl + * Signature: ()V + */ + JNIEXPORT void JNICALL Java_org_openeuler_security_openssl_KAEProvider_initOpenssl +- (JNIEnv *env, jclass cls) { ++ (JNIEnv *env, jclass cls, jboolean useGlobalMode, jstring engineId, jbooleanArray algorithmKaeFlags) { + SSL_load_error_strings(); + ERR_load_BIO_strings(); + OpenSSL_add_all_algorithms(); + ++ /* ++ * If the same shared object is opened again with dlopen(), the same object handle is returned. ++ * The dynamic linker maintains reference counts for object handles. ++ * An object that was previously opened with RTLD_LOCAL can be promoted to RTLD_GLOBAL in a subsequent dlopen(). ++ * ++ * RTLD_GLOBAL ++ * The symbols defined by this shared object will be made ++ * available for symbol resolution of subsequently loaded ++ * shared objects. ++ * RTLD_LOCAL ++ * This is the converse of RTLD_GLOBAL, and the default if ++ * neither flag is specified. Symbols defined in this shared ++ * object are not made available to resolve references in ++ * subsequently loaded shared objects. ++ * For more information see https://man7.org/linux/man-pages/man3/dlopen.3.html. ++ */ ++ if (useGlobalMode) { ++ char msg[1024]; ++ void *handle = NULL; ++ // Promote the flags of the loaded libcrypto.so library from RTLD_LOCAL to RTLD_GLOBAL ++ handle = dlopen(KAE_OPENSSL_LIBRARY, RTLD_LAZY | RTLD_GLOBAL); ++ if (handle == NULL) { ++ snprintf(msg, sizeof(msg), "Cannot load %s (%s)!", KAE_OPENSSL_LIBRARY, dlerror()); ++ KAE_ThrowByName(env, "java/lang/UnsatisfiedLinkError", msg); ++ return; ++ } ++ dlclose(handle); ++ } ++ + // check if KaeEngine holder is already set + ENGINE* e = GetKaeEngine(); + if (e != NULL) { +@@ -47,11 +79,25 @@ JNIEXPORT void JNICALL Java_org_openeuler_security_openssl_KAEProvider_initOpens + } + + // determine whether KAE is loaded successfully +- e = ENGINE_by_id("kae"); ++ const char* id = (*env)->GetStringUTFChars(env, engineId, 0); ++ e = ENGINE_by_id(id); ++ (*env)->ReleaseStringUTFChars(env, engineId, id); + if (e == NULL) { +- ERR_clear_error(); +- KAE_ThrowRuntimeException(env, "kae engine not found"); ++ KAE_ThrowFromOpenssl(env, "ENGINE_by_id", KAE_ThrowRuntimeException); + return; + } + SetKaeEngine(e); ++ ++ // initialize the engine for each algorithm ++ initEngines(env, algorithmKaeFlags); + } ++ ++/* ++ * Class: Java_org_openeuler_security_openssl_KAEProvider ++ * Method: getEngineFlags ++ * Signature: ()V ++ */ ++JNIEXPORT jbooleanArray JNICALL Java_org_openeuler_security_openssl_KAEProvider_getEngineFlags ++ (JNIEnv *env, jclass cls) { ++ return getEngineFlags(env); ++} +\ No newline at end of file +diff --git a/jdk/src/solaris/native/org/openeuler/security/openssl/kae_signature_rsa.c b/jdk/src/solaris/native/org/openeuler/security/openssl/kae_signature_rsa.c +index e81dc1406..6c401356d 100644 +--- a/jdk/src/solaris/native/org/openeuler/security/openssl/kae_signature_rsa.c ++++ b/jdk/src/solaris/native/org/openeuler/security/openssl/kae_signature_rsa.c +@@ -24,6 +24,7 @@ + #include + #include + #include ++#include "kae_log.h" + #include "kae_util.h" + #include "kae_exception.h" + +@@ -99,8 +100,9 @@ JNIEXPORT jbyteArray JNICALL Java_org_openeuler_security_openssl_KAERSASignature + jbyte* digestBytes = NULL; + jbyte* sigBytes = NULL; + jbyteArray sigByteArray = NULL; +- static ENGINE* kaeEngine = NULL; +- kaeEngine = (kaeEngine == NULL) ? GetKaeEngine() : kaeEngine; ++ ENGINE* kaeEngine = GetEngineByAlgorithmIndex(RSA_INDEX); ++ KAE_TRACE("KAERSASignatureNative_rsaSign: kaeEngine => %p", kaeEngine); ++ + // new EVP_PKEY_CTX + if ((pkeyCtx = EVP_PKEY_CTX_new(pkey, kaeEngine)) == NULL) { + KAE_ThrowFromOpenssl(env, "EVP_PKEY_new", KAE_ThrowSignatureException); +@@ -163,8 +165,9 @@ JNIEXPORT jboolean JNICALL Java_org_openeuler_security_openssl_KAERSASignatureNa + jbyte* digestBytes = NULL; + jbyte* sigBytes = NULL; + jboolean isSuccess = JNI_FALSE; +- static ENGINE* kaeEngine = NULL; +- kaeEngine = (kaeEngine == NULL) ? GetKaeEngine() : kaeEngine; ++ ENGINE* kaeEngine = GetEngineByAlgorithmIndex(RSA_INDEX); ++ KAE_TRACE("KAERSASignatureNative_rsaVerify: kaeEngine => %p", kaeEngine); ++ + // new EVP_PKEY_CTX + if ((pkeyCtx = EVP_PKEY_CTX_new(pkey, kaeEngine)) == NULL) { + KAE_ThrowFromOpenssl(env, "EVP_PKEY_new", KAE_ThrowSignatureException); +@@ -255,8 +258,9 @@ JNIEXPORT jbyteArray JNICALL Java_org_openeuler_security_openssl_KAERSASignature + jbyte* digestBytes = NULL; + jbyte* sigBytes = NULL; + jbyteArray sigByteArray = NULL; +- static ENGINE* kaeEngine = NULL; +- kaeEngine = (kaeEngine == NULL) ? GetKaeEngine() : kaeEngine; ++ ENGINE* kaeEngine = GetEngineByAlgorithmIndex(RSA_INDEX); ++ KAE_TRACE("KAERSASignatureNative_pssSign: kaeEngine => %p", kaeEngine); ++ + // new EVP_PKEY_CTX + if ((pkeyCtx = EVP_PKEY_CTX_new(pkey, kaeEngine)) == NULL) { + KAE_ThrowFromOpenssl(env, "EVP_PKEY_new", KAE_ThrowSignatureException); +@@ -320,8 +324,9 @@ JNIEXPORT jboolean JNICALL Java_org_openeuler_security_openssl_KAERSASignatureNa + jbyte* digestBytes = NULL; + jbyte* sigBytes = NULL; + jboolean isSuccess = JNI_FALSE; +- static ENGINE* kaeEngine = NULL; +- kaeEngine = (kaeEngine == NULL) ? GetKaeEngine() : kaeEngine; ++ ENGINE* kaeEngine = GetEngineByAlgorithmIndex(RSA_INDEX); ++ KAE_TRACE("KAERSASignatureNative_pssVerify: kaeEngine => %p", kaeEngine); ++ + // new EVP_PKEY_CTX + if ((pkeyCtx = EVP_PKEY_CTX_new(pkey, kaeEngine)) == NULL) { + KAE_ThrowFromOpenssl(env, "EVP_PKEY_new", KAE_ThrowSignatureException); +diff --git a/jdk/src/solaris/native/org/openeuler/security/openssl/kae_symmetric_cipher.c b/jdk/src/solaris/native/org/openeuler/security/openssl/kae_symmetric_cipher.c +index 71c28bdea..43f6326b2 100644 +--- a/jdk/src/solaris/native/org/openeuler/security/openssl/kae_symmetric_cipher.c ++++ b/jdk/src/solaris/native/org/openeuler/security/openssl/kae_symmetric_cipher.c +@@ -142,16 +142,19 @@ Java_org_openeuler_security_openssl_KAESymmetricCipherBase_nativeInit(JNIEnv* en + jbyte* keyBytes = NULL; + jbyte* ivBytes = NULL; + const EVP_CIPHER* cipher = NULL; +- static ENGINE* kaeEngine = NULL; ++ ENGINE* kaeEngine = NULL; + + const char* algo = (*env)->GetStringUTFChars(env, cipherType, 0); + if (StartsWith("aes", algo)) { + cipher = EVPGetAesCipherByName(env, algo); +- kaeEngine = NULL; ++ kaeEngine = GetAesEngineByAlgorithmName(algo); + } else { + cipher = EVPGetSm4CipherByName(env, algo); +- kaeEngine = (kaeEngine == NULL) ? GetKaeEngine() : kaeEngine; ++ kaeEngine = GetSm4EngineByAlgorithmName(algo); + } ++ ++ KAE_TRACE("KAESymmetricCipherBase_nativeInit: kaeEngine => %p", kaeEngine); ++ + (*env)->ReleaseStringUTFChars(env, cipherType, algo); + if (cipher == NULL) { + KAE_ThrowOOMException(env, "create EVP_CIPHER fail"); +diff --git a/jdk/src/solaris/native/org/openeuler/security/openssl/kae_util.c b/jdk/src/solaris/native/org/openeuler/security/openssl/kae_util.c +index 0e656a834..a16d944c4 100644 +--- a/jdk/src/solaris/native/org/openeuler/security/openssl/kae_util.c ++++ b/jdk/src/solaris/native/org/openeuler/security/openssl/kae_util.c +@@ -22,6 +22,7 @@ + */ + + #include ++#include + #include "kae_util.h" + #include "kae_exception.h" + +@@ -55,7 +56,7 @@ BIGNUM* KAE_GetBigNumFromByteArray(JNIEnv* env, jbyteArray byteArray) { + + jbyte* bytes = (*env)->GetByteArrayElements(env, byteArray, NULL); + if (bytes == NULL) { +- KAE_ThrowNullPointerException(env,"GetByteArrayElements failed"); ++ KAE_ThrowNullPointerException(env, "GetByteArrayElements failed"); + goto cleanup; + } + BIGNUM* result = BN_bin2bn((const unsigned char*) bytes, len, bn); +@@ -109,3 +110,138 @@ cleanup: + (*env)->ReleaseByteArrayElements(env, javaBytes, bytes, 0); + return javaBytes; + } ++ ++#define ENGINE_LENGTH (EC_INDEX + 1) ++static ENGINE* engines[ENGINE_LENGTH] = {NULL}; ++static jboolean engineFlags[ENGINE_LENGTH] = {JNI_FALSE}; ++static KAEAlgorithm kaeAlgorithms[ENGINE_LENGTH] = { ++ {MD5_INDEX, "md5"}, ++ {SHA256_INDEX, "sha256"}, ++ {SHA384_INDEX, "sha384"}, ++ {SM3_INDEX, "sm3"}, ++ {AES_128_ECB_INDEX, "aes-128-ecb"}, ++ {AES_128_CBC_INDEX, "aes-128-cbc"}, ++ {AES_128_CTR_INDEX, "aes-128-ctr"}, ++ {AES_128_GCM_INDEX, "aes-128-gcm"}, ++ {AES_192_ECB_INDEX, "aes-192-ecb"}, ++ {AES_192_CBC_INDEX, "aes-192-cbc"}, ++ {AES_192_CTR_INDEX, "aes-192-ctr"}, ++ {AES_192_GCM_INDEX, "aes-192-gcm"}, ++ {AES_256_ECB_INDEX, "aes-256-ecb"}, ++ {AES_256_CBC_INDEX, "aes-256-cbc"}, ++ {AES_256_CTR_INDEX, "aes-256-ctr"}, ++ {AES_256_GCM_INDEX, "aes-256-gcm"}, ++ {SM4_ECB_INDEX, "sm4-ecb"}, ++ {SM4_CBC_INDEX, "sm4-cbc"}, ++ {SM4_CTR_INDEX, "sm4-ctr"}, ++ {SM4_OFB_INDEX, "sm4-ofb"}, ++ {HMAC_MD5_INDEX, "hmac-md5"}, ++ {HMAC_SHA1_INDEX, "hmac-sha1"}, ++ {HMAC_SHA224_INDEX, "hmac-sha224"}, ++ {HMAC_SHA256_INDEX, "hmac-sha256"}, ++ {HMAC_SHA384_INDEX, "hmac-sha384"}, ++ {HMAC_SHA512_INDEX, "hmac-sha512"}, ++ {RSA_INDEX, "rsa"}, ++ {DH_INDEX, "dh"}, ++ {EC_INDEX, "ec"} ++}; ++ ++void initEngines(JNIEnv* env, jbooleanArray algorithmKaeFlags) { ++ if (algorithmKaeFlags == NULL) { ++ return; ++ } ++ ++ // get jTemp ++ jboolean* jTemp = NULL; ++ int length = (*env)->GetArrayLength(env, algorithmKaeFlags); ++ jTemp = (jboolean*) malloc(length); ++ if (jTemp == NULL) { ++ KAE_ThrowOOMException(env, "initEngines GetArrayLength error"); ++ return; ++ } ++ (*env)->GetBooleanArrayRegion(env, algorithmKaeFlags, 0, length, jTemp); ++ ++ // assign engines ++ int minLen = length < ENGINE_LENGTH ? length : ENGINE_LENGTH; ++ int i; ++ for (i = 0; i < minLen; i++) { ++ if (jTemp[i]) { ++ engines[i] = kaeEngine; ++ engineFlags[i] = JNI_TRUE; ++ } ++ } ++ if (length < ENGINE_LENGTH) { ++ for (i = minLen; i < ENGINE_LENGTH; i++) { ++ engines[i] = kaeEngine; ++ engineFlags[i] = JNI_TRUE; ++ } ++ } ++ ++ // free jTemp ++ if (jTemp != NULL) { ++ free(jTemp); ++ } ++} ++ ++jbooleanArray getEngineFlags(JNIEnv* env) { ++ jbooleanArray array = (*env)->NewBooleanArray(env, ENGINE_LENGTH); ++ (*env)->SetBooleanArrayRegion(env, array, 0, ENGINE_LENGTH, engineFlags); ++ return array; ++} ++ ++ENGINE* GetEngineByAlgorithmIndex(AlgorithmIndex algorithmIndex) { ++ return engines[algorithmIndex]; ++} ++ ++/* ++ * Get the engine used by the specified algorithm. ++ * @param beginIndex the beginning index, inclusive. ++ * @param endIndex the ending index, exclusive. ++ * @param algorithmName algorithm name ++ * @return engine ++ */ ++ENGINE* GetEngineByBeginIndexAndEndIndex(int beginIndex, int endIndex, ++ const char* algorithmName) { ++ if (beginIndex < 0 || endIndex > ENGINE_LENGTH) { ++ return NULL; ++ } ++ ++ int i; ++ for (i = beginIndex; i < endIndex; i++) { ++ if (strcasecmp(kaeAlgorithms[i].algorithmName, algorithmName) == 0) { ++ return engines[kaeAlgorithms[i].algorithmIndex]; ++ } ++ } ++ return NULL; ++} ++ ++ENGINE* GetHmacEngineByAlgorithmName(const char* algorithmName) { ++ char prefix[] = {"hmac-"}; ++ int len = strlen(algorithmName); ++ int newLen = strlen(algorithmName) + strlen(prefix) + 1; ++ char* newAlgorithmName = NULL; ++ newAlgorithmName = malloc(newLen); ++ if (newAlgorithmName == NULL) { ++ return NULL; ++ } ++ strcpy(newAlgorithmName, prefix); ++ strcat(newAlgorithmName, algorithmName); ++ ENGINE* engine = GetEngineByBeginIndexAndEndIndex(HMAC_MD5_INDEX, HMAC_SHA512_INDEX + 1, newAlgorithmName); ++ if (newAlgorithmName != NULL) { ++ free(newAlgorithmName); ++ } ++ return engine; ++} ++ ++ENGINE* GetDigestEngineByAlgorithmName(const char* algorithmName) { ++ return GetEngineByBeginIndexAndEndIndex(MD5_INDEX, SM3_INDEX + 1, algorithmName); ++} ++ ++ENGINE* GetAesEngineByAlgorithmName(const char* algorithmName) { ++ return GetEngineByBeginIndexAndEndIndex(AES_128_ECB_INDEX, AES_256_GCM_INDEX + 1, algorithmName); ++} ++ ++ENGINE* GetSm4EngineByAlgorithmName(const char* algorithmName) { ++ return GetEngineByBeginIndexAndEndIndex(SM4_ECB_INDEX, SM4_OFB_INDEX + 1, algorithmName); ++} ++ +diff --git a/jdk/src/solaris/native/org/openeuler/security/openssl/kae_util.h b/jdk/src/solaris/native/org/openeuler/security/openssl/kae_util.h +index 13bd5976d..347337509 100644 +--- a/jdk/src/solaris/native/org/openeuler/security/openssl/kae_util.h ++++ b/jdk/src/solaris/native/org/openeuler/security/openssl/kae_util.h +@@ -27,6 +27,43 @@ + #include + #include + ++typedef enum { ++ MD5_INDEX, ++ SHA256_INDEX, ++ SHA384_INDEX, ++ SM3_INDEX, ++ AES_128_ECB_INDEX, ++ AES_128_CBC_INDEX, ++ AES_128_CTR_INDEX, ++ AES_128_GCM_INDEX, ++ AES_192_ECB_INDEX, ++ AES_192_CBC_INDEX, ++ AES_192_CTR_INDEX, ++ AES_192_GCM_INDEX, ++ AES_256_ECB_INDEX, ++ AES_256_CBC_INDEX, ++ AES_256_CTR_INDEX, ++ AES_256_GCM_INDEX, ++ SM4_ECB_INDEX, ++ SM4_CBC_INDEX, ++ SM4_CTR_INDEX, ++ SM4_OFB_INDEX, ++ HMAC_MD5_INDEX, ++ HMAC_SHA1_INDEX, ++ HMAC_SHA224_INDEX, ++ HMAC_SHA256_INDEX, ++ HMAC_SHA384_INDEX, ++ HMAC_SHA512_INDEX, ++ RSA_INDEX, ++ DH_INDEX, ++ EC_INDEX ++} AlgorithmIndex; ++ ++typedef struct { ++ AlgorithmIndex algorithmIndex; ++ const char* algorithmName; ++} KAEAlgorithm; ++ + /* jbyteArray convert to BIGNUM */ + BIGNUM* KAE_GetBigNumFromByteArray(JNIEnv* env, jbyteArray byteArray); + +@@ -40,8 +77,18 @@ void SetKaeEngine(ENGINE* engine); + + ENGINE* GetKaeEngine(); + +-void SetKaeEngine(ENGINE* engine); ++void initEngines(JNIEnv* env, jbooleanArray algorithmKaeFlags); + +-ENGINE* GetKaeEngine(); ++jbooleanArray getEngineFlags(JNIEnv* env); ++ ++ENGINE* GetEngineByAlgorithmIndex(AlgorithmIndex algorithmIndex); ++ ++ENGINE* GetHmacEngineByAlgorithmName(const char* algorithmName); ++ ++ENGINE* GetDigestEngineByAlgorithmName(const char* algorithmName); ++ ++ENGINE* GetAesEngineByAlgorithmName(const char* algorithmName); ++ ++ENGINE* GetSm4EngineByAlgorithmName(const char* algorithmName); + + #endif +diff --git a/jdk/test/org/openeuler/security/openssl/AESTest.java b/jdk/test/org/openeuler/security/openssl/AESTest.java +new file mode 100644 +index 000000000..77da5ecc2 +--- /dev/null ++++ b/jdk/test/org/openeuler/security/openssl/AESTest.java +@@ -0,0 +1,114 @@ ++/* ++ * Copyright (c) 2022, Huawei Technologies Co., Ltd. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ */ ++ ++import org.openeuler.security.openssl.KAEProvider; ++ ++import java.nio.charset.StandardCharsets; ++import java.security.Security; ++import java.security.spec.AlgorithmParameterSpec; ++import javax.crypto.Cipher; ++import javax.crypto.KeyGenerator; ++import javax.crypto.SecretKey; ++import javax.crypto.spec.IvParameterSpec; ++ ++/** ++ * @test ++ * @summary Basic test for AES ++ * @requires os.arch=="aarch64" ++ * @run main AESTest ++ */ ++ ++public class AESTest { ++ private static final String[] ALGORITHM = {"AES", "AES_128", "AES_192", "AES_256"}; ++ private static final String[] MODES = {"ECB", "CBC", "CTR", "GCM"}; ++ private static final String[] PADDING = {"NoPadding", "PKCS5Padding"}; ++ private static final int AES_128_KEY_LENGTH = 128; ++ private static final int AES_192_KEY_LENGTH = 192; ++ private static final int AES_256_KEY_LENGTH = 256; ++ private static String plainText = "helloworldhellow"; // 16bytes for NoPadding ++ private static String shortPlainText = "helloworld"; // 5 bytes for padding ++ ++ public static void main(String[] args) throws Exception { ++ Security.insertProviderAt(new KAEProvider(), 1); ++ for (String algo : ALGORITHM) { ++ for (String mode : MODES) { ++ int padKinds = 2; ++ if (mode.equalsIgnoreCase("CTR")) { ++ padKinds = 1; ++ } ++ for (int k = 0; k < padKinds; k++) { ++ test(algo, mode, PADDING[k]); ++ } ++ } ++ } ++ } ++ ++ public static void test(String algo, String mo, String pad) throws Exception { ++ AlgorithmParameterSpec aps = null; ++ ++ Cipher cipher = Cipher.getInstance(algo + "/" + mo + "/" + pad); ++ ++ KeyGenerator kg = KeyGenerator.getInstance("AES"); ++ if (algo.equalsIgnoreCase("AES_192")) { ++ kg.init(AES_192_KEY_LENGTH); ++ } else if (algo.equalsIgnoreCase("AES_256")) { ++ kg.init(AES_256_KEY_LENGTH); ++ } else { ++ kg.init(AES_128_KEY_LENGTH); ++ } ++ ++ SecretKey key = kg.generateKey(); ++ ++ // encrypt ++ if (!mo.equalsIgnoreCase("GCM")) { ++ cipher.init(Cipher.ENCRYPT_MODE, key, aps); ++ } else { ++ cipher.init(Cipher.ENCRYPT_MODE, key); ++ } ++ ++ String cipherString = null; ++ if (!pad.equalsIgnoreCase("NoPadding")) { ++ cipherString = shortPlainText; ++ } else { ++ cipherString = plainText; ++ } ++ byte[] cipherText = cipher.doFinal(cipherString.getBytes(StandardCharsets.UTF_8)); ++ if (!mo.equalsIgnoreCase("ECB")) { ++ aps = new IvParameterSpec(cipher.getIV()); ++ } else { ++ aps = null; ++ } ++ ++ if (!mo.equalsIgnoreCase("GCM")) { ++ cipher.init(Cipher.DECRYPT_MODE, key, aps); ++ } else { ++ cipher.init(Cipher.DECRYPT_MODE, key, cipher.getParameters()); ++ } ++ ++ String decryptPlainText = new String(cipher.doFinal(cipherText)); ++ ++ if (!cipherString.equals(decryptPlainText)) { ++ throw new RuntimeException("aes decryption failed, algo = " + algo + ", mo = " + mo + ", pad = " + pad); ++ } ++ } ++} +diff --git a/jdk/test/org/openeuler/security/openssl/DHTest.java b/jdk/test/org/openeuler/security/openssl/DHTest.java +index 6eb5e7c96..ee5d63684 100644 +--- a/jdk/test/org/openeuler/security/openssl/DHTest.java ++++ b/jdk/test/org/openeuler/security/openssl/DHTest.java +@@ -28,7 +28,6 @@ import java.io.Serializable; + import java.math.BigInteger; + import java.security.*; + import java.util.Arrays; +-import java.util.Date; + import javax.crypto.KeyAgreement; + import javax.crypto.spec.*; + import org.openeuler.security.openssl.KAEProvider; +@@ -75,18 +74,16 @@ import org.openeuler.security.openssl.KAEProvider; + /** + * @test + * @summary Basic test for DH ++ * @requires os.arch=="aarch64" + * @run main DHTest + */ + +-final class DHTest implements Serializable { +- private static int bitLength = 8192; ++public class DHTest implements Serializable { + private static BigInteger g512; + private static BigInteger p512; +- Throwable t = null; + + private static volatile Provider sunJceProvider; + private static volatile Provider kaeProvider; +- Date d = new Date(); + + public static void main(String[] args) throws Exception { + Security.addProvider(new KAEProvider()); +@@ -97,8 +94,6 @@ final class DHTest implements Serializable { + + p512 = new BigInteger("27672987386729926592037876826877634387173876890702920770064392919138769821035856568775311919542560094764667151024449425954917954337048895981297730855891532066350935045229294626339548842381843985759061682551900379979643117695834175891578650111093016914264824311693147701566019122696621248493126219217339690346346921463135605151471303957324058301097079967414639146647429422884520134312590056632178576758580657240245655739869017244657144448267757255018625514803292549109401806336918448001843022629625467069714240279603204909633404992842479161100500474744098408277938070656334892106100534117209709263785505019003765693651"); + +- DHTest.bitLength = 0; +- + DHParameterSpec dhParams = new DHParameterSpec(p512, g512); + KeyPairGenerator SunJCEkeyGen = KeyPairGenerator.getInstance("DH", sunJceProvider); + KeyPairGenerator KAEkeyGen = KeyPairGenerator.getInstance("DH", kaeProvider); +diff --git a/jdk/test/org/openeuler/security/openssl/DigestTest.java b/jdk/test/org/openeuler/security/openssl/DigestTest.java +new file mode 100644 +index 000000000..a293f7268 +--- /dev/null ++++ b/jdk/test/org/openeuler/security/openssl/DigestTest.java +@@ -0,0 +1,60 @@ ++import org.openeuler.security.openssl.KAEProvider; ++ ++import java.nio.charset.StandardCharsets; ++import java.security.MessageDigest; ++import java.security.Security; ++import java.util.Arrays; ++import java.util.HashMap; ++import java.util.Map; ++ ++/** ++ * @test ++ * @summary Basic test for MD5 SHA256 SHA384 ++ * @requires os.arch=="aarch64" ++ * @run main/othervm DigestTest ++ */ ++public class DigestTest { ++ private static String PLAIN_TEXT = "hello world"; ++ ++ private static Map alg = new HashMap(); ++ ++ static { ++ alg.put("MD5", new byte[] {94, -74, 59, -69, -32, 30, -18, -48, -109, -53, 34, -69, -113, 90, -51, -61}); ++ alg.put( ++ "SHA-256", ++ new byte[] { ++ -71, 77, 39, -71, -109, 77, 62, 8, -91, 46, 82, -41, -38, 125, -85, -6, ++ -60, -124, -17, -29, 122, 83, -128, -18, -112, -120, -9, -84, -30, -17, -51, -23 ++ }); ++ alg.put( ++ "SHA-384", ++ new byte[] { ++ -3, -67, -114, 117, -90, 127, 41, -9, 1, -92, -32, 64, 56, 94, 46, 35, ++ -104, 99, 3, -22, 16, 35, -110, 17, -81, -112, 127, -53, -72, 53, 120, -77, ++ -28, 23, -53, 113, -50, 100, 110, -3, 8, 25, -35, -116, 8, -115, -31, -67 ++ }); ++ alg.put( ++ "SM3", ++ new byte[] { ++ 68, -16, 6, 30, 105, -6, 111, -33, -62, -112, -60, -108, 101, 74, 5, ++ -36, 12, 5, 61, -89, -27, -59, 43, -124, -17, -109, -87, -42, 125, 63, ++ -1, -120 ++ }); ++ } ++ ++ public static void main(String[] args) throws Exception { ++ Security.insertProviderAt(new KAEProvider(), 1); ++ for (String key : alg.keySet()) { ++ test(PLAIN_TEXT, key, alg.get(key)); ++ } ++ } ++ ++ public static void test(String plainText, String algo, byte[] expectRes) throws Exception { ++ MessageDigest md = MessageDigest.getInstance(algo); ++ md.update(plainText.getBytes(StandardCharsets.UTF_8)); ++ byte[] res = md.digest(); ++ if (!Arrays.equals(res, expectRes)) { ++ throw new RuntimeException(algo + " failed"); ++ } ++ } ++} +diff --git a/jdk/test/org/openeuler/security/openssl/ECDHTest.java b/jdk/test/org/openeuler/security/openssl/ECDHTest.java +index 590c31154..069c32295 100644 +--- a/jdk/test/org/openeuler/security/openssl/ECDHTest.java ++++ b/jdk/test/org/openeuler/security/openssl/ECDHTest.java +@@ -41,6 +41,7 @@ import java.nio.charset.StandardCharsets; + /** + * @test + * @summary Basic test for ECDH ++ * @requires os.arch=="aarch64" + * @run main ECDHTest + */ + +diff --git a/jdk/test/org/openeuler/security/openssl/HmacTest.java b/jdk/test/org/openeuler/security/openssl/HmacTest.java +new file mode 100644 +index 000000000..9ff328629 +--- /dev/null ++++ b/jdk/test/org/openeuler/security/openssl/HmacTest.java +@@ -0,0 +1,88 @@ ++/* ++ * Copyright (c) 2022, Huawei Technologies Co., Ltd. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ */ ++ ++import org.openeuler.security.openssl.KAEProvider; ++ ++import javax.crypto.Mac; ++import javax.crypto.spec.SecretKeySpec; ++import java.security.Key; ++import java.security.Security; ++import java.util.Arrays; ++ ++/** ++ * @test ++ * @summary test for Hmac ++ * @requires os.arch=="aarch64" ++ * @run main/othervm HmacTest ++ */ ++public class HmacTest { ++ private static final byte[] PLAIN_BYTES = "hello world".getBytes(); ++ private static final String[] ALGORITHMS = new String[]{ ++ "HmacMD5", ++ "HmacSHA1", ++ "HmacSHA224", ++ "HmacSHA256", ++ "HmacSHA384", ++ "HmacSHA512", ++ }; ++ private static final byte[][] EXPECTED_BYTES = { ++ {-40, 63, -96, 13, 107, -33, -1, -53, -116, 117, 75, -6, 85, -88, -112, -90}, ++ {-68, 104, 112, -36, 123, 123, -92, 104, 89, -90, 63, 56, 84, 45, 12, -7, 41, 103, -105, -27}, ++ {-31, 0, 103, 51, -119, -61, 2, -76, -83, -113, 95, 86, 8, 46, 91, 20, ++ -15, -23, -71, 62, -50, 86, -54, 71, -94, -47, -103, 43}, ++ {-69, -83, -3, 7, 61, 38, -122, -59, 7, -53, 106, 114, 58, 102, 65, -118, ++ 54, -50, 116, -56, 110, 54, -71, 36, 60, 84, 14, 97, 78, 18, -119, -24}, ++ {100, -58, 106, 64, -96, 91, 99, -33, 36, -78, -53, -50, -78, 116, -110, 85, ++ 84, -5, -63, 17, 51, -69, -39, -122, 65, 8, -122, -43, 39, 13, -41, -52, ++ 45, -38, -59, 70, 17, -87, -63, -126, 4, 120, -77, 71, 119, 96, -2, -68}, ++ {-89, 47, -98, -12, 110, -88, 23, 2, 28, 26, -71, 53, -108, 54, -52, 1, ++ -121, -121, 87, 6, -78, 123, -14, -86, 127, 114, 124, -73, -98, 79, -122, 69, ++ -32, 50, 48, -79, -110, 66, 38, 70, -3, -76, 95, 55, 74, 48, 57, -121, ++ 22, 60, -83, -109, 59, 79, 0, -49, 107, 88, -82, -35, 87, -36, 49, -54} ++ }; ++ private static final Key key = new SecretKeySpec("mac".getBytes(), ""); ++ ++ public static void main(String[] args) throws Exception { ++ Security.insertProviderAt(new KAEProvider(), 1); ++ for (int i = 0; i < ALGORITHMS.length; i++) { ++ test(ALGORITHMS[i], key, PLAIN_BYTES, EXPECTED_BYTES[i]); ++ } ++ } ++ ++ private static void test(String algorithm, Key key, byte[] inputBytes, byte[] expectedBytes) throws Exception { ++ Mac mac = Mac.getInstance(algorithm); ++ mac.init(key); ++ mac.update(inputBytes); ++ byte[] bytes = mac.doFinal(); ++ if (!(mac.getProvider() instanceof KAEProvider)) { ++ throw new RuntimeException(algorithm + " failed," + ++ "provider=" + mac.getProvider().getClass() + "," + ++ "expectedProvider=" + KAEProvider.class); ++ } ++ if (!Arrays.equals(bytes, expectedBytes)) { ++ throw new RuntimeException(algorithm + " failed," + ++ "bytes=" + Arrays.toString(bytes) + "," + ++ "expectedBytes=" + Arrays.toString(expectedBytes)); ++ } ++ } ++} +diff --git a/jdk/test/org/openeuler/security/openssl/KAEConfTest.java b/jdk/test/org/openeuler/security/openssl/KAEConfTest.java +new file mode 100644 +index 000000000..9028d28b5 +--- /dev/null ++++ b/jdk/test/org/openeuler/security/openssl/KAEConfTest.java +@@ -0,0 +1,121 @@ ++/* ++ * Copyright (c) 2022, Huawei Technologies Co., Ltd. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ */ ++ ++import org.openeuler.security.openssl.KAEConfig; ++import org.openeuler.security.openssl.KAEProvider; ++ ++import java.io.File; ++import java.io.FileWriter; ++import java.io.IOException; ++import java.nio.file.Files; ++import java.util.ArrayList; ++import java.util.List; ++ ++/* ++ * @test ++ * @summary Test KAE Conf ++ * @requires os.arch=="aarch64" ++ * @run main/othervm KAEConfTest DEFAULT ++ * @run main/othervm KAEConfTest SPECIFY ++ */ ++public class KAEConfTest { ++ private static final String DEFAULT_CONF = System.getProperty("java.home") + ++ File.separator + "lib" + File.separator + "kaeprovider.conf"; ++ ++ private static final String SPECIFY_CONF = System.getProperty("user.dir") + ++ File.separator + "kaeprovider.conf"; ++ ++ private static final String SPECIFY_LOG_PATH = System.getProperty("user.dir") + File.separator + "kae.log"; ++ private static final List files = new ArrayList<>(); ++ ++ enum Mode { ++ DEFAULT, ++ SPECIFY ++ } ++ ++ public static void main(String[] args) throws IOException { ++ Mode mode = getMode(args); ++ try { ++ init(mode); ++ new KAEProvider(); ++ test(mode); ++ } finally { ++ KAETestHelper.cleanUp(files); ++ } ++ } ++ ++ private static Mode getMode(String[] args) { ++ if (args.length <= 0) { ++ return Mode.DEFAULT; ++ } ++ return Mode.valueOf(args[0]); ++ } ++ ++ private static void init(Mode mode) throws IOException { ++ if (Mode.SPECIFY.equals(mode)) { ++ System.setProperty("kae.conf", SPECIFY_CONF); ++ File file = new File(SPECIFY_CONF); ++ if (!file.exists()) { ++ Files.createFile(file.toPath()); ++ } ++ files.add(file); ++ try (FileWriter fileWriter = new FileWriter(file)) { ++ fileWriter.write("kae.log=true"); ++ fileWriter.flush(); ++ } ++ } ++ } ++ ++ private static void testDefault() { ++ File file = new File(DEFAULT_CONF); ++ if (!file.exists()) { ++ throw new RuntimeException("test failed"); ++ } ++ } ++ ++ private static void testSpecify() { ++ String value = KAEConfig.privilegedGetOverridable("kae.log"); ++ if (!"true".equals(value)) { ++ throw new RuntimeException("test failed : kae.log=" + value); ++ } ++ File file = new File(SPECIFY_LOG_PATH); ++ if (!file.exists()) { ++ throw new RuntimeException(SPECIFY_LOG_PATH + "does not exist"); ++ } ++ // kae log file ++ files.add(file); ++ } ++ ++ private static void test(Mode mode) { ++ switch (mode) { ++ case DEFAULT: ++ testDefault(); ++ break; ++ case SPECIFY: ++ testSpecify(); ++ break; ++ default: ++ throw new IllegalArgumentException("invalid mode"); ++ } ++ } ++} +diff --git a/jdk/test/org/openeuler/security/openssl/KAEDisabledAlgorithmsTest.java b/jdk/test/org/openeuler/security/openssl/KAEDisabledAlgorithmsTest.java +new file mode 100644 +index 000000000..6301b6d76 +--- /dev/null ++++ b/jdk/test/org/openeuler/security/openssl/KAEDisabledAlgorithmsTest.java +@@ -0,0 +1,164 @@ ++/* ++ * Copyright (c) 2022, Huawei Technologies Co., Ltd. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ */ ++ ++import org.openeuler.security.openssl.KAEConfig; ++import org.openeuler.security.openssl.KAEProvider; ++ ++import java.util.HashSet; ++import java.util.Set; ++ ++/* ++ * @test ++ * @summary Test property kae.engine.disableAlgorithms ++ * @requires os.arch=="aarch64" ++ * @run main/othervm -Dkae.engine.disabledAlgorithms=md5 KAEDisabledAlgorithmsTest ++ * @run main/othervm -Dkae.engine.disabledAlgorithms=sha256 KAEDisabledAlgorithmsTest ++ * @run main/othervm -Dkae.engine.disabledAlgorithms=sha384 KAEDisabledAlgorithmsTest ++ * @run main/othervm -Dkae.engine.disabledAlgorithms=sm3 KAEDisabledAlgorithmsTest ++ * @run main/othervm -Dkae.engine.disabledAlgorithms=aes-128-ecb KAEDisabledAlgorithmsTest ++ * @run main/othervm -Dkae.engine.disabledAlgorithms=aes-128-cbc KAEDisabledAlgorithmsTest ++ * @run main/othervm -Dkae.engine.disabledAlgorithms=aes-128-ctr KAEDisabledAlgorithmsTest ++ * @run main/othervm -Dkae.engine.disabledAlgorithms=aes-128-gcm KAEDisabledAlgorithmsTest ++ * @run main/othervm -Dkae.engine.disabledAlgorithms=aes-192-ecb KAEDisabledAlgorithmsTest ++ * @run main/othervm -Dkae.engine.disabledAlgorithms=aes-192-cbc KAEDisabledAlgorithmsTest ++ * @run main/othervm -Dkae.engine.disabledAlgorithms=aes-192-ctr KAEDisabledAlgorithmsTest ++ * @run main/othervm -Dkae.engine.disabledAlgorithms=aes-192-gcm KAEDisabledAlgorithmsTest ++ * @run main/othervm -Dkae.engine.disabledAlgorithms=aes-256-ecb KAEDisabledAlgorithmsTest ++ * @run main/othervm -Dkae.engine.disabledAlgorithms=aes-256-cbc KAEDisabledAlgorithmsTest ++ * @run main/othervm -Dkae.engine.disabledAlgorithms=aes-256-ctr KAEDisabledAlgorithmsTest ++ * @run main/othervm -Dkae.engine.disabledAlgorithms=aes-256-gcm KAEDisabledAlgorithmsTest ++ * @run main/othervm -Dkae.engine.disabledAlgorithms=sm4-ecb KAEDisabledAlgorithmsTest ++ * @run main/othervm -Dkae.engine.disabledAlgorithms=sm4-cbc KAEDisabledAlgorithmsTest ++ * @run main/othervm -Dkae.engine.disabledAlgorithms=sm4-ctr KAEDisabledAlgorithmsTest ++ * @run main/othervm -Dkae.engine.disabledAlgorithms=sm4-ofb KAEDisabledAlgorithmsTest ++ * @run main/othervm -Dkae.engine.disabledAlgorithms=hmac-md5 KAEDisabledAlgorithmsTest ++ * @run main/othervm -Dkae.engine.disabledAlgorithms=hmac-sha1 KAEDisabledAlgorithmsTest ++ * @run main/othervm -Dkae.engine.disabledAlgorithms=hmac-sha224 KAEDisabledAlgorithmsTest ++ * @run main/othervm -Dkae.engine.disabledAlgorithms=hmac-sha256 KAEDisabledAlgorithmsTest ++ * @run main/othervm -Dkae.engine.disabledAlgorithms=hmac-sha384 KAEDisabledAlgorithmsTest ++ * @run main/othervm -Dkae.engine.disabledAlgorithms=hmac-sha512 KAEDisabledAlgorithmsTest ++ * @run main/othervm -Dkae.engine.disabledAlgorithms=rsa KAEDisabledAlgorithmsTest ++ * @run main/othervm -Dkae.engine.disabledAlgorithms=dh KAEDisabledAlgorithmsTest ++ * @run main/othervm -Dkae.engine.disabledAlgorithms=ec KAEDisabledAlgorithmsTest ++ * @run main/othervm -Dkae.engine.disabledAlgorithms=aes-128-gcm,aes-192-gcm,aes-256-gcm KAEDisabledAlgorithmsTest ++ * @run main/othervm -Dkae.engine.disabledAlgorithms=md5,aes-128-ecb,sm4-ecb,hmac-sha1,rsa,dh,ec KAEDisabledAlgorithmsTest ++ * @run main/othervm -Dkae.engine.id=uadk_engine -Dkae.libcrypto.useGlobalMode=true -Dkae.engine.disabledAlgorithms=md5 KAEDisabledAlgorithmsTest ++ * @run main/othervm -Dkae.engine.id=uadk_engine -Dkae.libcrypto.useGlobalMode=true -Dkae.engine.disabledAlgorithms=sha256 KAEDisabledAlgorithmsTest ++ * @run main/othervm -Dkae.engine.id=uadk_engine -Dkae.libcrypto.useGlobalMode=true -Dkae.engine.disabledAlgorithms=sha384 KAEDisabledAlgorithmsTest ++ * @run main/othervm -Dkae.engine.id=uadk_engine -Dkae.libcrypto.useGlobalMode=true -Dkae.engine.disabledAlgorithms=sm3 KAEDisabledAlgorithmsTest ++ * @run main/othervm -Dkae.engine.id=uadk_engine -Dkae.libcrypto.useGlobalMode=true -Dkae.engine.disabledAlgorithms=aes-128-ecb KAEDisabledAlgorithmsTest ++ * @run main/othervm -Dkae.engine.id=uadk_engine -Dkae.libcrypto.useGlobalMode=true -Dkae.engine.disabledAlgorithms=aes-128-cbc KAEDisabledAlgorithmsTest ++ * @run main/othervm -Dkae.engine.id=uadk_engine -Dkae.libcrypto.useGlobalMode=true -Dkae.engine.disabledAlgorithms=aes-128-ctr KAEDisabledAlgorithmsTest ++ * @run main/othervm -Dkae.engine.id=uadk_engine -Dkae.libcrypto.useGlobalMode=true -Dkae.engine.disabledAlgorithms=aes-128-gcm KAEDisabledAlgorithmsTest ++ * @run main/othervm -Dkae.engine.id=uadk_engine -Dkae.libcrypto.useGlobalMode=true -Dkae.engine.disabledAlgorithms=aes-192-ecb KAEDisabledAlgorithmsTest ++ * @run main/othervm -Dkae.engine.id=uadk_engine -Dkae.libcrypto.useGlobalMode=true -Dkae.engine.disabledAlgorithms=aes-192-cbc KAEDisabledAlgorithmsTest ++ * @run main/othervm -Dkae.engine.id=uadk_engine -Dkae.libcrypto.useGlobalMode=true -Dkae.engine.disabledAlgorithms=aes-192-ctr KAEDisabledAlgorithmsTest ++ * @run main/othervm -Dkae.engine.id=uadk_engine -Dkae.libcrypto.useGlobalMode=true -Dkae.engine.disabledAlgorithms=aes-192-gcm KAEDisabledAlgorithmsTest ++ * @run main/othervm -Dkae.engine.id=uadk_engine -Dkae.libcrypto.useGlobalMode=true -Dkae.engine.disabledAlgorithms=aes-256-ecb KAEDisabledAlgorithmsTest ++ * @run main/othervm -Dkae.engine.id=uadk_engine -Dkae.libcrypto.useGlobalMode=true -Dkae.engine.disabledAlgorithms=aes-256-cbc KAEDisabledAlgorithmsTest ++ * @run main/othervm -Dkae.engine.id=uadk_engine -Dkae.libcrypto.useGlobalMode=true -Dkae.engine.disabledAlgorithms=aes-256-ctr KAEDisabledAlgorithmsTest ++ * @run main/othervm -Dkae.engine.id=uadk_engine -Dkae.libcrypto.useGlobalMode=true -Dkae.engine.disabledAlgorithms=aes-256-gcm KAEDisabledAlgorithmsTest ++ * @run main/othervm -Dkae.engine.id=uadk_engine -Dkae.libcrypto.useGlobalMode=true -Dkae.engine.disabledAlgorithms=sm4-ecb KAEDisabledAlgorithmsTest ++ * @run main/othervm -Dkae.engine.id=uadk_engine -Dkae.libcrypto.useGlobalMode=true -Dkae.engine.disabledAlgorithms=sm4-cbc KAEDisabledAlgorithmsTest ++ * @run main/othervm -Dkae.engine.id=uadk_engine -Dkae.libcrypto.useGlobalMode=true -Dkae.engine.disabledAlgorithms=sm4-ctr KAEDisabledAlgorithmsTest ++ * @run main/othervm -Dkae.engine.id=uadk_engine -Dkae.libcrypto.useGlobalMode=true -Dkae.engine.disabledAlgorithms=sm4-ofb KAEDisabledAlgorithmsTest ++ * @run main/othervm -Dkae.engine.id=uadk_engine -Dkae.libcrypto.useGlobalMode=true -Dkae.engine.disabledAlgorithms=hmac-md5 KAEDisabledAlgorithmsTest ++ * @run main/othervm -Dkae.engine.id=uadk_engine -Dkae.libcrypto.useGlobalMode=true -Dkae.engine.disabledAlgorithms=hmac-sha1 KAEDisabledAlgorithmsTest ++ * @run main/othervm -Dkae.engine.id=uadk_engine -Dkae.libcrypto.useGlobalMode=true -Dkae.engine.disabledAlgorithms=hmac-sha224 KAEDisabledAlgorithmsTest ++ * @run main/othervm -Dkae.engine.id=uadk_engine -Dkae.libcrypto.useGlobalMode=true -Dkae.engine.disabledAlgorithms=hmac-sha256 KAEDisabledAlgorithmsTest ++ * @run main/othervm -Dkae.engine.id=uadk_engine -Dkae.libcrypto.useGlobalMode=true -Dkae.engine.disabledAlgorithms=hmac-sha384 KAEDisabledAlgorithmsTest ++ * @run main/othervm -Dkae.engine.id=uadk_engine -Dkae.libcrypto.useGlobalMode=true -Dkae.engine.disabledAlgorithms=hmac-sha512 KAEDisabledAlgorithmsTest ++ * @run main/othervm -Dkae.engine.id=uadk_engine -Dkae.libcrypto.useGlobalMode=true -Dkae.engine.disabledAlgorithms=rsa KAEDisabledAlgorithmsTest ++ * @run main/othervm -Dkae.engine.id=uadk_engine -Dkae.libcrypto.useGlobalMode=true -Dkae.engine.disabledAlgorithms=dh KAEDisabledAlgorithmsTest ++ * @run main/othervm -Dkae.engine.id=uadk_engine -Dkae.libcrypto.useGlobalMode=true -Dkae.engine.disabledAlgorithms=ec KAEDisabledAlgorithmsTest ++ * @run main/othervm -Dkae.engine.id=uadk_engine -Dkae.libcrypto.useGlobalMode=true -Dkae.engine.disabledAlgorithms=aes-128-gcm,aes-192-gcm,aes-256-gcm KAEDisabledAlgorithmsTest ++ * @run main/othervm -Dkae.engine.id=uadk_engine -Dkae.libcrypto.useGlobalMode=true -Dkae.engine.disabledAlgorithms=md5,aes-128-ecb,sm4-ecb,hmac-sha1,rsa,dh,ec KAEDisabledAlgorithmsTest ++ */ ++public class KAEDisabledAlgorithmsTest { ++ ++ public static void main(String[] args) { ++ KAETestHelper.Engine engine = KAETestHelper.getEngine(); ++ if (!engine.isValid()) { ++ System.out.println("Skip test, engine " + engine.getEngineId() + " does not exist."); ++ return; ++ } ++ String[] disabledAlgorithms = getDisabledAlgorithms(); ++ init(); ++ new KAEProvider(); ++ test(disabledAlgorithms); ++ } ++ ++ private static final String[] PROPERTY_NAMES = new String[]{ ++ "kae.digest.useKaeEngine", ++ "kae.aes.useKaeEngine", ++ "kae.sm4.useKaeEngine", ++ "kae.hmac.useKaeEngine", ++ "kae.rsa.useKaeEngine", ++ "kae.dh.useKaeEngine", ++ "kae.ec.useKaeEngine" ++ }; ++ ++ private static String[] getDisabledAlgorithms() { ++ String value = System.getProperty("kae.engine.disabledAlgorithms"); ++ if (value == null) { ++ return new String[0]; ++ } ++ return value.split(","); ++ } ++ ++ private static void init() { ++ for (String propertyName : PROPERTY_NAMES) { ++ System.setProperty(propertyName, "true"); ++ } ++ } ++ ++ private static void test(String[] disabledAlgorithms) { ++ boolean[] useKaeEngineFlags = KAEConfig.getUseKaeEngineFlags(); ++ Set disabledAlgorithmIndexSet = new HashSet<>(); ++ ++ // test disabled algorithms ++ for (String disabledAlgorithm : disabledAlgorithms) { ++ Integer index = KAETestHelper.getAlgorithmIndex(disabledAlgorithm); ++ if (index == null || index < 0 || index >= useKaeEngineFlags.length) { ++ continue; ++ } ++ if (useKaeEngineFlags[index]) { ++ throw new RuntimeException("test failed"); ++ } ++ disabledAlgorithmIndexSet.add(index); ++ } ++ ++ // test other algorithms that are not disabled (except ec) ++ for (int i = 0; i < useKaeEngineFlags.length - 1; i++) { ++ if (!disabledAlgorithmIndexSet.contains(i) && !useKaeEngineFlags[i]) { ++ throw new RuntimeException(KAETestHelper.getAlgorithmName(i) + " algorithm is not disabled"); ++ } ++ } ++ ++ // test whether the ec algorithm is disabled by default ++ if (useKaeEngineFlags[useKaeEngineFlags.length - 1]) { ++ throw new RuntimeException(KAETestHelper.getAlgorithmName(useKaeEngineFlags.length - 1) ++ + " algorithm is disabled by default"); ++ } ++ } ++} +diff --git a/jdk/test/org/openeuler/security/openssl/KAEEngineIdTest.java b/jdk/test/org/openeuler/security/openssl/KAEEngineIdTest.java +new file mode 100644 +index 000000000..2ddaf6712 +--- /dev/null ++++ b/jdk/test/org/openeuler/security/openssl/KAEEngineIdTest.java +@@ -0,0 +1,76 @@ ++/* ++ * Copyright (c) 2022, Huawei Technologies Co., Ltd. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ */ ++ ++import org.openeuler.security.openssl.KAEProvider; ++ ++import java.io.BufferedReader; ++import java.io.File; ++import java.io.FileReader; ++import java.io.IOException; ++import java.util.ArrayList; ++import java.util.List; ++ ++/* ++ * @test ++ * @summary Test KAE property kae.engine.id and kae.libcrypto.useGlobalMode ++ * @requires os.arch=="aarch64" ++ * @run main/othervm -Dkae.log=true KAEEngineIdTest ++ * @run main/othervm -Dkae.log=true -Dkae.engine.id=kae KAEEngineIdTest ++ * @run main/othervm -Dkae.log=true -Dkae.engine.id=uadk_engine -Dkae.libcrypto.useGlobalMode=true KAEEngineIdTest ++ */ ++public class KAEEngineIdTest { ++ ++ private static final String LOG_PATH = System.getProperty("user.dir") + ++ File.separator + "kae.log"; ++ ++ private static final List files = new ArrayList<>(); ++ ++ public static void main(String[] args) throws IOException { ++ KAETestHelper.Engine engine = KAETestHelper.getEngine(); ++ if (!engine.isValid()) { ++ System.out.println("Skip test, engine " + engine.getEngineId() + " does not exist."); ++ return; ++ } ++ ++ try { ++ new KAEProvider(); ++ test(engine); ++ } finally { ++ KAETestHelper.cleanUp(files); ++ } ++ } ++ ++ private static void test(KAETestHelper.Engine engine) throws IOException { ++ File file = new File(LOG_PATH); ++ if (!file.exists()) { ++ throw new RuntimeException(LOG_PATH + " does not exist"); ++ } ++ files.add(file); ++ try (BufferedReader bufferedReader = new BufferedReader(new FileReader(file))) { ++ String s = bufferedReader.readLine(); ++ if (!s.contains(engine.getEngineId() + " engine was found")) { ++ throw new RuntimeException("test failed"); ++ } ++ } ++ } ++} +diff --git a/jdk/test/org/openeuler/security/openssl/KAELogTest.java b/jdk/test/org/openeuler/security/openssl/KAELogTest.java +new file mode 100644 +index 000000000..31c8f5d99 +--- /dev/null ++++ b/jdk/test/org/openeuler/security/openssl/KAELogTest.java +@@ -0,0 +1,126 @@ ++/* ++ * Copyright (c) 2022, Huawei Technologies Co., Ltd. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ */ ++ ++import org.openeuler.security.openssl.KAEProvider; ++ ++import java.io.File; ++import java.util.ArrayList; ++import java.util.List; ++ ++/* ++ * @test ++ * @summary Test KAE log ++ * @requires os.arch=="aarch64" ++ * @run main/othervm KAELogTest ++ * @run main/othervm -Dkae.log=false KAELogTest ++ * @run main/othervm -Dkae.log=true KAELogTest ++ * @run main/othervm -Dkae.log=true -Dkae.log.file=./KAELogTest/kae.log KAELogTest ++ */ ++public class KAELogTest { ++ private static final String DEFAULT_LOG_PATH = System.getProperty("user.dir") + ++ File.separator + "kae.log"; ++ ++ private static final String SPECIFY_LOG_PATH = System.getProperty("user.dir") + ++ File.separator + "KAELogTest" + File.separator + "kae.log"; ++ ++ private static final List files = new ArrayList<>(); ++ ++ enum Mode { ++ DEFAULT, ++ DISABLE, ++ ENABLE, ++ SPECIFY ++ } ++ ++ public static void main(String[] args) { ++ Mode mode = getMode(); ++ try { ++ new KAEProvider(); ++ test(mode); ++ } finally { ++ KAETestHelper.cleanUp(files); ++ } ++ } ++ ++ private static Mode getMode() { ++ String enableKaeLog = System.getProperty("kae.log"); ++ if (enableKaeLog == null) { ++ return Mode.DEFAULT; ++ } else if ("false".equals(enableKaeLog)) { ++ return Mode.DISABLE; ++ } else { ++ String logPath = System.getProperty("kae.log.file"); ++ if (logPath == null) { ++ return Mode.ENABLE; ++ } ++ return Mode.SPECIFY; ++ } ++ } ++ ++ private static void testDefault() { ++ testDisable(); ++ } ++ ++ private static void testDisable() { ++ File file = new File(DEFAULT_LOG_PATH); ++ if (file.exists()) { ++ throw new RuntimeException("test failed"); ++ } ++ } ++ ++ private static void testEnable() { ++ File file = new File(DEFAULT_LOG_PATH); ++ if (!file.exists()) { ++ throw new RuntimeException("test failed"); ++ } ++ files.add(file); ++ } ++ ++ private static void testSpecify() { ++ File file = new File(KAELogTest.SPECIFY_LOG_PATH); ++ if (!file.exists()) { ++ throw new RuntimeException("test failed"); ++ } ++ files.add(file); ++ files.add(file.getParentFile()); ++ } ++ ++ private static void test(Mode mode) { ++ switch (mode) { ++ case DEFAULT: ++ testDefault(); ++ break; ++ case DISABLE: ++ testDisable(); ++ break; ++ case ENABLE: ++ testEnable(); ++ break; ++ case SPECIFY: ++ testSpecify(); ++ break; ++ default: ++ throw new IllegalArgumentException("invalid mode"); ++ } ++ } ++} +diff --git a/jdk/test/org/openeuler/security/openssl/KAETestHelper.java b/jdk/test/org/openeuler/security/openssl/KAETestHelper.java +new file mode 100644 +index 000000000..31e22493a +--- /dev/null ++++ b/jdk/test/org/openeuler/security/openssl/KAETestHelper.java +@@ -0,0 +1,209 @@ ++/* ++ * Copyright (c) 2022, Huawei Technologies Co., Ltd. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ */ ++ ++import java.io.BufferedReader; ++import java.io.File; ++import java.io.FileReader; ++import java.io.IOException; ++import java.util.HashMap; ++import java.util.List; ++import java.util.Map; ++ ++class KAETestHelper { ++ private static final String KAE_ENGINE_ID = "kae"; ++ private static final String UADK_ENGINE_ID = "uadk_engine"; ++ private static boolean hasKaeEngine; ++ private static boolean hasUadkEngine; ++ ++ private static String engineRootPath; ++ ++ // algorithm names ++ private static final String[] ALGORITHM_NAMES = new String[]{ ++ "md5", ++ "sha256", ++ "sha384", ++ "sm3", ++ "aes-128-ecb", ++ "aes-128-cbc", ++ "aes-128-ctr", ++ "aes-128-gcm", ++ "aes-192-ecb", ++ "aes-192-cbc", ++ "aes-192-ctr", ++ "aes-192-gcm", ++ "aes-256-ecb", ++ "aes-256-cbc", ++ "aes-256-ctr", ++ "aes-256-gcm", ++ "sm4-ecb", ++ "sm4-cbc", ++ "sm4-ctr", ++ "sm4-ofb", ++ "hmac-md5", ++ "hmac-sha1", ++ "hmac-sha224", ++ "hmac-sha256", ++ "hmac-sha384", ++ "hmac-sha512", ++ "rsa", ++ "dh", ++ "ec" ++ }; ++ private static final Map ALGORITHM_NAME_MAP = new HashMap<>(); ++ ++ private static final String PROVIDER_NAME = "KAEProvider"; ++ private static final String USE_OPENSSL_MSG = "Use openssl soft calculation"; ++ private static final String USE_KAE_HARDWARE_MSG = "enable KAE hardware acceleration"; ++ private static final Map ALGORITHM_MSG_MAP = new HashMap<>(); ++ ++ static { ++ init(); ++ } ++ ++ enum Engine { ++ default_engine(hasKaeEngine, KAE_ENGINE_ID), ++ kae(hasKaeEngine, KAE_ENGINE_ID), ++ uadk_engine(hasUadkEngine, UADK_ENGINE_ID); ++ private final boolean isValid; ++ private final String engineId; ++ ++ Engine(boolean isValid, String engineId) { ++ this.isValid = isValid; ++ this.engineId = engineId; ++ } ++ ++ public boolean isValid() { ++ return isValid; ++ } ++ ++ public String getEngineId() { ++ return engineId; ++ } ++ } ++ ++ private static void init() { ++ engineRootPath = System.getenv("OPENSSL_ENGINES"); ++ if (engineRootPath == null || engineRootPath.equals("")) { ++ System.out.println("Environment variable OPENSSL_ENGINES is not configured"); ++ } ++ hasKaeEngine = hasEngine(KAE_ENGINE_ID); ++ hasUadkEngine = hasEngine(UADK_ENGINE_ID); ++ ++ for (int i = 0; i < ALGORITHM_NAMES.length; i++) { ++ ALGORITHM_NAME_MAP.put(ALGORITHM_NAMES[i], i); ++ } ++ ++ ALGORITHM_MSG_MAP.put(USE_OPENSSL_MSG, false); ++ ALGORITHM_MSG_MAP.put(USE_KAE_HARDWARE_MSG, true); ++ } ++ ++ static Integer getAlgorithmIndex(String algorithmName) { ++ return ALGORITHM_NAME_MAP.get(algorithmName); ++ } ++ ++ static String getAlgorithmName(Integer algorithmIndex) { ++ return ALGORITHM_NAMES[algorithmIndex]; ++ } ++ ++ private static boolean hasEngine(String engineId) { ++ String filePath = engineRootPath + File.separator + engineId + ".so"; ++ File file = new File(filePath); ++ return file.exists(); ++ } ++ ++ static boolean hasKaeEngine() { ++ return hasKaeEngine; ++ } ++ ++ static boolean hasUadkEngine() { ++ return hasUadkEngine; ++ } ++ ++ static void cleanUp(List files) { ++ for (File file : files) { ++ System.out.println("delete file : " + file); ++ file.delete(); ++ } ++ } ++ ++ static boolean[] parseLog(Engine engine, File file) throws IOException { ++ boolean[] kaeUseEngineFlags; ++ String expectedEngineMsg = engine.getEngineId() + " engine was found"; ++ try (BufferedReader reader = new BufferedReader(new FileReader(file))) { ++ // load engine message ++ String engineMsg = reader.readLine(); ++ if (engineMsg == null || !engineMsg.contains(expectedEngineMsg)) { ++ throw new RuntimeException("test failed : actual message :" + engineMsg); ++ } ++ ++ // summary message ++ String summaryMessage = reader.readLine(); ++ if (summaryMessage == null) { ++ throw new RuntimeException("test failed : summary message is null"); ++ } ++ ++ kaeUseEngineFlags = new boolean[ALGORITHM_NAMES.length]; ++ // strategy of each algorithm ++ String strategy; ++ while ((strategy = reader.readLine()) != null) { ++ String[] splitArray = strategy.split("=>"); ++ if (splitArray.length < 2) { ++ throw new RuntimeException("test failed : strategy = " + strategy); ++ } ++ ++ // algorithm Index ++ String algorithm = splitArray[0].replace(" ", ""); ++ Integer algorithmIndex = ALGORITHM_NAME_MAP.get(algorithm); ++ if (algorithmIndex == null) { ++ throw new RuntimeException("test failed : illegal algorithm " + algorithm); ++ } ++ ++ // provider and algorithm value ++ String detail = splitArray[1]; ++ String[] detailArray = detail.split(":"); ++ if (detailArray.length < 2) { ++ throw new RuntimeException("test failed : detail=" + strategy); ++ } ++ String provider = detailArray[0].replace(" ", ""); ++ if (!PROVIDER_NAME.equals(provider)) { ++ throw new RuntimeException("test failed : provider= " + provider); ++ } ++ String algorithmMsg = detailArray[1].trim(); ++ Boolean algorithmValue = ALGORITHM_MSG_MAP.get(algorithmMsg); ++ if (algorithmValue == null) { ++ throw new RuntimeException("test failed : algorithmMsg= " + algorithmMsg); ++ } ++ kaeUseEngineFlags[algorithmIndex] = algorithmValue; ++ } ++ } ++ return kaeUseEngineFlags; ++ } ++ ++ static KAETestHelper.Engine getEngine() { ++ String engineId = System.getProperty("kae.engine.id"); ++ if (engineId == null) { ++ return KAETestHelper.Engine.default_engine; ++ } ++ return KAETestHelper.Engine.valueOf(engineId); ++ } ++} +diff --git a/jdk/test/org/openeuler/security/openssl/KAEUseEngineTest.java b/jdk/test/org/openeuler/security/openssl/KAEUseEngineTest.java +new file mode 100644 +index 000000000..4e57f775e +--- /dev/null ++++ b/jdk/test/org/openeuler/security/openssl/KAEUseEngineTest.java +@@ -0,0 +1,262 @@ ++/* ++ * Copyright (c) 2022, Huawei Technologies Co., Ltd. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ */ ++ ++import org.openeuler.security.openssl.KAEProvider; ++ ++import java.io.File; ++import java.io.IOException; ++import java.util.ArrayList; ++import java.util.Arrays; ++import java.util.HashMap; ++import java.util.List; ++import java.util.Map; ++ ++/* ++ * @test ++ * @summary Test KAE property kae..useKaeEngine ++ * @requires os.arch=="aarch64" ++ * @run main/othervm -Dkae.log=true -Dall.test=default KAEUseEngineTest ++ * @run main/othervm -Dkae.log=true -Dkae.digest.useKaeEngine=true KAEUseEngineTest ++ * @run main/othervm -Dkae.log=true -Dkae.aes.useKaeEngine=true KAEUseEngineTest ++ * @run main/othervm -Dkae.log=true -Dkae.sm4.useKaeEngine=true KAEUseEngineTest ++ * @run main/othervm -Dkae.log=true -Dkae.hmac.useKaeEngine=true KAEUseEngineTest ++ * @run main/othervm -Dkae.log=true -Dkae.rsa.useKaeEngine=true KAEUseEngineTest ++ * @run main/othervm -Dkae.log=true -Dkae.dh.useKaeEngine=true KAEUseEngineTest ++ * @run main/othervm -Dkae.log=true -Dkae.ec.useKaeEngine=true KAEUseEngineTest ++ * @run main/othervm -Dkae.log=true -Dall.test=enable -Dkae.digest.useKaeEngine=true -Dkae.aes.useKaeEngine=true -Dkae.sm4.useKaeEngine=true -Dkae.hmac.useKaeEngine=true -Dkae.rsa.useKaeEngine=true -Dkae.dh.useKaeEngine=true -Dkae.ec.useKaeEngine=true KAEUseEngineTest ++ * @run main/othervm -Dkae.log=true -Dkae.digest.useKaeEngine=false KAEUseEngineTest ++ * @run main/othervm -Dkae.log=true -Dkae.aes.useKaeEngine=true KAEUseEngineTest ++ * @run main/othervm -Dkae.log=true -Dkae.sm4.useKaeEngine=true KAEUseEngineTest ++ * @run main/othervm -Dkae.log=true -Dkae.hmac.useKaeEngine=true KAEUseEngineTest ++ * @run main/othervm -Dkae.log=true -Dkae.rsa.useKaeEngine=true KAEUseEngineTest ++ * @run main/othervm -Dkae.log=true -Dkae.dh.useKaeEngine=true KAEUseEngineTest ++ * @run main/othervm -Dkae.log=true -Dkae.ec.useKaeEngine=true KAEUseEngineTest ++ * @run main/othervm -Dkae.log=true -Dall.test=disable -Dkae.digest.useKaeEngine=false -Dkae.aes.useKaeEngine=false -Dkae.sm4.useKaeEngine=false -Dkae.hmac.useKaeEngine=false -Dkae.rsa.useKaeEngine=false -Dkae.dh.useKaeEngine=false -Dkae.ec.useKaeEngine=false KAEUseEngineTest ++ * @run main/othervm -Dkae.log=true -Dall.test=default -Dkae.engine.id=uadk_engine -Dkae.libcrypto.useGlobalMode=true KAEUseEngineTest ++ * @run main/othervm -Dkae.log=true -Dkae.engine.id=uadk_engine -Dkae.libcrypto.useGlobalMode=true -Dkae.digest.useKaeEngine=true KAEUseEngineTest ++ * @run main/othervm -Dkae.log=true -Dkae.engine.id=uadk_engine -Dkae.libcrypto.useGlobalMode=true -Dkae.aes.useKaeEngine=true KAEUseEngineTest ++ * @run main/othervm -Dkae.log=true -Dkae.engine.id=uadk_engine -Dkae.libcrypto.useGlobalMode=true -Dkae.sm4.useKaeEngine=true KAEUseEngineTest ++ * @run main/othervm -Dkae.log=true -Dkae.engine.id=uadk_engine -Dkae.libcrypto.useGlobalMode=true -Dkae.hmac.useKaeEngine=true KAEUseEngineTest ++ * @run main/othervm -Dkae.log=true -Dkae.engine.id=uadk_engine -Dkae.libcrypto.useGlobalMode=true -Dkae.rsa.useKaeEngine=true KAEUseEngineTest ++ * @run main/othervm -Dkae.log=true -Dkae.engine.id=uadk_engine -Dkae.libcrypto.useGlobalMode=true -Dkae.dh.useKaeEngine=true KAEUseEngineTest ++ * @run main/othervm -Dkae.log=true -Dkae.engine.id=uadk_engine -Dkae.libcrypto.useGlobalMode=true -Dkae.ec.useKaeEngine=true KAEUseEngineTest ++ * @run main/othervm -Dkae.log=true -Dall.test=enable -Dkae.engine.id=uadk_engine -Dkae.libcrypto.useGlobalMode=true -Dkae.digest.useKaeEngine=true -Dkae.aes.useKaeEngine=true -Dkae.sm4.useKaeEngine=true -Dkae.hmac.useKaeEngine=true -Dkae.rsa.useKaeEngine=true -Dkae.dh.useKaeEngine=true -Dkae.ec.useKaeEngine=true KAEUseEngineTest ++ * @run main/othervm -Dkae.log=true -Dkae.engine.id=uadk_engine -Dkae.libcrypto.useGlobalMode=true -Dkae.digest.useKaeEngine=false KAEUseEngineTest ++ * @run main/othervm -Dkae.log=true -Dkae.engine.id=uadk_engine -Dkae.libcrypto.useGlobalMode=true -Dkae.aes.useKaeEngine=true KAEUseEngineTest ++ * @run main/othervm -Dkae.log=true -Dkae.engine.id=uadk_engine -Dkae.libcrypto.useGlobalMode=true -Dkae.sm4.useKaeEngine=true KAEUseEngineTest ++ * @run main/othervm -Dkae.log=true -Dkae.engine.id=uadk_engine -Dkae.libcrypto.useGlobalMode=true -Dkae.hmac.useKaeEngine=true KAEUseEngineTest ++ * @run main/othervm -Dkae.log=true -Dkae.engine.id=uadk_engine -Dkae.libcrypto.useGlobalMode=true -Dkae.rsa.useKaeEngine=true KAEUseEngineTest ++ * @run main/othervm -Dkae.log=true -Dkae.engine.id=uadk_engine -Dkae.libcrypto.useGlobalMode=true -Dkae.dh.useKaeEngine=true KAEUseEngineTest ++ * @run main/othervm -Dkae.log=true -Dkae.engine.id=uadk_engine -Dkae.libcrypto.useGlobalMode=true -Dkae.ec.useKaeEngine=true KAEUseEngineTest ++ * @run main/othervm -Dkae.log=true -Dall.test=disable -Dkae.engine.id=uadk_engine -Dkae.libcrypto.useGlobalMode=true -Dkae.digest.useKaeEngine=false -Dkae.aes.useKaeEngine=false -Dkae.sm4.useKaeEngine=false -Dkae.hmac.useKaeEngine=false -Dkae.rsa.useKaeEngine=false -Dkae.dh.useKaeEngine=false -Dkae.ec.useKaeEngine=false KAEUseEngineTest ++ */ ++public class KAEUseEngineTest { ++ enum Mode { ++ DEFAULT(new boolean[]{ ++ true, false, false, true, false, false, false, false, false, false, ++ false, false, false, false, false, false, true, true, true, true, ++ false, false, false, false, false, false, true, true, false ++ }), ++ DIGEST_ENABLE(new boolean[]{ ++ true, false, false, true, false, false, false, false, false, false, ++ false, false, false, false, false, false, true, true, true, true, ++ false, false, false, false, false, false, true, true, false ++ }, 0, true), ++ AES_ENABLE(new boolean[]{ ++ true, false, false, true, true, true, true, true, true, true, ++ true, true, true, true, true, true, true, true, true, true, ++ false, false, false, false, false, false, true, true, false ++ }, 1, true), ++ SM4_ENABLE(new boolean[]{ ++ true, false, false, true, false, false, false, false, false, false, ++ false, false, false, false, false, false, true, true, true, true, ++ false, false, false, false, false, false, true, true, false ++ }, 2, true), ++ HMAC_ENABLE(new boolean[]{ ++ true, false, false, true, false, false, false, false, false, false, ++ false, false, false, false, false, false, true, true, true, true, ++ true, true, true, true, true, true, true, true, false ++ }, 3, true), ++ RSA_ENABLE(new boolean[]{ ++ true, false, false, true, false, false, false, false, false, false, ++ false, false, false, false, false, false, true, true, true, true, ++ false, false, false, false, false, false, true, true, false ++ }, 4, true), ++ DH_ENABLE(new boolean[]{ ++ true, false, false, true, false, false, false, false, false, false, ++ false, false, false, false, false, false, true, true, true, true, ++ false, false, false, false, false, false, true, true, false ++ }, 5, true), ++ EC_ENABLE(new boolean[]{ ++ true, false, false, true, false, false, false, false, false, false, ++ false, false, false, false, false, false, true, true, true, true, ++ false, false, false, false, false, false, true, true, false ++ }, 6, true), ++ ALL_ENABLE(new boolean[]{ ++ true, false, false, true, true, true, true, true, true, true, ++ true, true, true, true, true, true, true, true, true, true, ++ true, true, true, true, true, true, true, true, false ++ }, true), ++ DIGEST_DISABLE(new boolean[]{ ++ false, false, false, false, false, false, false, false, false, false, ++ false, false, false, false, false, false, true, true, true, true, ++ false, false, false, false, false, false, true, true, false ++ }, 0, false), ++ AES_DISABLE(new boolean[]{ ++ true, false, false, true, false, false, false, false, false, false, ++ false, false, false, false, false, false, true, true, true, true, ++ false, false, false, false, false, false, true, true, false ++ }, 1, false), ++ SM4_DISABLE(new boolean[]{ ++ true, false, false, true, false, false, false, false, false, false, ++ false, false, false, false, false, false, false, false, false, false, ++ false, false, false, false, false, false, true, true, false ++ }, 2, false), ++ HMAC_DISABLE(new boolean[]{ ++ true, false, false, true, false, false, false, false, false, false, ++ false, false, false, false, false, false, true, true, true, true, ++ false, false, false, false, false, false, true, true, false ++ }, 3, false), ++ RSA_DISABLE(new boolean[]{ ++ true, false, false, true, false, false, false, false, false, false, ++ false, false, false, false, false, false, true, true, true, true, ++ false, false, false, false, false, false, false, true, false ++ }, 4, false), ++ DH_DISABLE(new boolean[]{ ++ true, false, false, true, false, false, false, false, false, false, ++ false, false, false, false, false, false, true, true, true, true, ++ false, false, false, false, false, false, true, false, false ++ }, 5, false), ++ EC_DISABLE(new boolean[]{ ++ true, false, false, true, false, false, false, false, false, false, ++ false, false, false, false, false, false, true, true, true, true, ++ false, false, false, false, false, false, true, true, false ++ }, 6, false), ++ ALL_DISABLE(new boolean[]{ ++ false, false, false, false, false, false, false, false, false, false, ++ false, false, false, false, false, false, false, false, false, false, ++ false, false, false, false, false, false, false, false, false ++ }, false); ++ private final boolean[] expectedResult; ++ private final Integer propertyNameIndex; ++ private final boolean enable; ++ private static final Map modeMap = new HashMap<>(); ++ ++ static { ++ Mode[] modes = Mode.values(); ++ for (Mode mode : modes) { ++ if (mode.propertyNameIndex != null) { ++ modeMap.put(PROPERTY_NAMES[mode.propertyNameIndex] + ":" + mode.enable, mode); ++ } ++ } ++ modeMap.put("default", DEFAULT); ++ modeMap.put("disable", ALL_DISABLE); ++ modeMap.put("enable", ALL_ENABLE); ++ } ++ ++ Mode(boolean[] expectedResult) { ++ this(expectedResult, false); ++ } ++ ++ Mode(boolean[] expectedResult, boolean enable) { ++ this(expectedResult, null, enable); ++ } ++ ++ Mode(boolean[] expectedResult, Integer propertyNameIndex, boolean enable) { ++ this.expectedResult = expectedResult; ++ this.propertyNameIndex = propertyNameIndex; ++ this.enable = enable; ++ } ++ ++ static Mode getMode(String name, Boolean enable) { ++ return modeMap.get(name + ":" + enable); ++ } ++ ++ static Mode getMode(String key) { ++ return modeMap.get(key); ++ } ++ } ++ ++ private static final String KAE_LOG_PATH = System.getProperty("user.dir") + ++ File.separator + "kae.log"; ++ ++ private static final String[] PROPERTY_NAMES = new String[]{ ++ "kae.digest.useKaeEngine", ++ "kae.aes.useKaeEngine", ++ "kae.sm4.useKaeEngine", ++ "kae.hmac.useKaeEngine", ++ "kae.rsa.useKaeEngine", ++ "kae.dh.useKaeEngine", ++ "kae.ec.useKaeEngine" ++ }; ++ ++ private static final List files = new ArrayList<>(); ++ ++ public static void main(String[] args) throws IOException { ++ KAETestHelper.Engine engine = KAETestHelper.getEngine(); ++ if (!engine.isValid()) { ++ System.out.println("Skip test, engine " + engine.getEngineId() + " does not exist."); ++ return; ++ } ++ Mode mode = getMode(); ++ if (mode == null) { ++ throw new RuntimeException("test failed: mode is null"); ++ } ++ ++ try { ++ new KAEProvider(); ++ test(mode, engine); ++ } finally { ++ KAETestHelper.cleanUp(files); ++ } ++ } ++ ++ private static Mode getMode() { ++ String value = System.getProperty("all.test"); ++ if (value != null) { ++ return Mode.getMode(value); ++ } ++ for (String propertyName : PROPERTY_NAMES) { ++ String property = System.getProperty(propertyName); ++ Boolean enable = null; ++ if (property != null) { ++ enable = Boolean.valueOf(property); ++ } ++ Mode mode = Mode.getMode(propertyName, enable); ++ if (mode != null) { ++ return mode; ++ } ++ } ++ return null; ++ } ++ ++ private static void test(Mode mode, KAETestHelper.Engine engine) throws IOException { ++ File file = new File(KAE_LOG_PATH); ++ files.add(file); ++ boolean[] kaeUseEngineFlags = KAETestHelper.parseLog(engine, file); ++ if (!Arrays.equals(mode.expectedResult, kaeUseEngineFlags)) { ++ throw new RuntimeException("test failed : expected : " + Arrays.toString(mode.expectedResult) + "," + ++ "actual:" + Arrays.toString(kaeUseEngineFlags)); ++ } ++ } ++} +diff --git a/jdk/test/org/openeuler/security/openssl/KaeDebugLogTest.java b/jdk/test/org/openeuler/security/openssl/KaeDebugLogTest.java +new file mode 100644 +index 000000000..bcce9cb8b +--- /dev/null ++++ b/jdk/test/org/openeuler/security/openssl/KaeDebugLogTest.java +@@ -0,0 +1,88 @@ ++/* ++ * Copyright (c) 2021, Huawei Technologies Co., Ltd. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ */ ++ ++import org.openeuler.security.openssl.KAEProvider; ++ ++import javax.crypto.Cipher; ++import javax.crypto.spec.SecretKeySpec; ++import java.io.PrintStream; ++import java.nio.charset.StandardCharsets; ++import java.nio.file.Files; ++import java.nio.file.Paths; ++import java.security.Security; ++import java.util.Objects; ++import java.util.stream.Collectors; ++import java.util.stream.Stream; ++ ++/** ++ * @test ++ * @summary test for KaeDebugLogTest ++ * @requires os.arch=="aarch64" ++ * @run main/othervm -Djava.security.debug=kae -Dkae.sm4.maxChunkSize=65536 KaeDebugLogTest ++ * @run main/othervm -Djava.security.debug=kae KaeDebugLogTest ++ * @run main/othervm -Djava.security.auth.debug=kae KaeDebugLogTest ++ * @run main/othervm KaeDebugLogTest ++ */ ++ ++public class KaeDebugLogTest { ++ ++ private static final PrintStream err = System.err; ++ ++ public static void main(String[] args) throws Exception { ++ PrintStream printStream = new PrintStream("kaetest.out"); ++ System.setErr(printStream); ++ testDebugLog(); ++ System.setErr(printStream); ++ testSm4ChunkSize(); ++ } ++ ++ public static void testDebugLog() throws Exception { ++ new KAEProvider(); ++ Stream lines = Files.lines(Paths.get("kaetest.out")); ++ System.setErr(err); ++ String content = lines.collect(Collectors.joining(System.lineSeparator())); ++ if(("kae".equals(System.getProperty("java.security.debug")) ++ || "kae".equals(System.getProperty("java.security..auth.debug"))) ++ && !content.contains("reading kae properties file:")){ ++ throw new RuntimeException("KaeDebugLogTest Failed! Failed to set the debug log."); ++ } ++ lines.close(); ++ } ++ ++ public static void testSm4ChunkSize() throws Exception { ++ Security.insertProviderAt(new KAEProvider(), 1); ++ Cipher cipher = Cipher.getInstance("SM4"); ++ cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec("sm4EncryptionKey".getBytes(StandardCharsets.UTF_8), "SM4")); ++ Stream lines = Files.lines(Paths.get("kaetest.out")); ++ System.setErr(err); ++ String content = lines.collect(Collectors.joining(System.lineSeparator())); ++ String log = "The configured chunk size is " + System.getProperty("kae.sm4.maxChunkSize"); ++ if(("kae".equals(System.getProperty("java.security.debug")) ++ || "kae".equals(System.getProperty("java.security..auth.debug"))) ++ && Objects.nonNull(System.getProperty("kae.sm4.maxChunkSize")) &&!content.contains(log)){ ++ throw new RuntimeException("KaeDebugLogTest Failed! Failed to set the kae.sm4.maxChunkSize = " + System.getProperty("kae.sm4.maxChunkSize")); ++ } ++ lines.close(); ++ } ++ ++} +diff --git a/jdk/test/org/openeuler/security/openssl/KaeProviderTest.java b/jdk/test/org/openeuler/security/openssl/KaeProviderTest.java +new file mode 100644 +index 000000000..d8587891b +--- /dev/null ++++ b/jdk/test/org/openeuler/security/openssl/KaeProviderTest.java +@@ -0,0 +1,170 @@ ++/* ++ * Copyright (c) 2021, Huawei Technologies Co., Ltd. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ */ ++ ++import org.openeuler.security.openssl.KAEProvider; ++ ++import javax.crypto.Cipher; ++import javax.crypto.Mac; ++import javax.crypto.NoSuchPaddingException; ++import java.security.KeyPairGenerator; ++import java.security.MessageDigest; ++import java.security.NoSuchAlgorithmException; ++import java.security.Security; ++ ++/** ++ * @test ++ * @requires os.arch=="aarch64" ++ * @summary test for KaeProviderTest ++ * @run main/othervm KaeProviderTest ++ * @run main/othervm KaeProviderTest true ++ * @run main/othervm KaeProviderTest false ++ * @run main/othervm KaeProviderTest wrong ++ */ ++ ++public class KaeProviderTest { ++ ++ private static final String[] algorithmKaeProviderPropertyNames = new String[]{ ++ "kae.md5", ++ "kae.sha256", ++ "kae.sha384", ++ "kae.sm3", ++ "kae.aes", ++ "kae.sm4", ++ "kae.hmac", ++ "kae.rsa", ++ "kae.dh", ++ "kae.ec" ++ }; ++ ++ private static final String KAE = "KAEProvider"; ++ ++ public static void main(String[] args) throws Exception { ++ initProperty(args); ++ Security.insertProviderAt(new KAEProvider(), 1); ++ testALL(); ++ } ++ ++ private static void initProperty(String[] args) { ++ if (args.length <= 0) { ++ return; ++ } ++ String value = args[0]; ++ for (String name : algorithmKaeProviderPropertyNames){ ++ System.setProperty(name,value); ++ } ++ } ++ ++ public static void testALL() throws Exception { ++ testMd5(); ++ testSha256(); ++ testSha384(); ++ testSm3(); ++ testAes(); ++ testSm4(); ++ testHmac(); ++ testRsa(); ++ testDh(); ++ testEc(); ++ } ++ ++ public static void testMd5() throws NoSuchAlgorithmException { ++ MessageDigest messageDigest = MessageDigest.getInstance("MD5"); ++ judge("kae.md5",messageDigest.getProvider().getName()); ++ ++ } ++ ++ public static void testSha256() throws NoSuchAlgorithmException { ++ MessageDigest messageDigest = MessageDigest.getInstance("SHA-256"); ++ judge("kae.sha256",messageDigest.getProvider().getName()); ++ } ++ ++ public static void testSha384() throws NoSuchAlgorithmException { ++ MessageDigest messageDigest = MessageDigest.getInstance("SHA-384"); ++ judge("kae.sha384",messageDigest.getProvider().getName()); ++ } ++ ++ public static void testSm3() throws NoSuchAlgorithmException { ++ try{ ++ MessageDigest messageDigest = MessageDigest.getInstance("SM3"); ++ judge("kae.sm3",messageDigest.getProvider().getName()); ++ }catch (NoSuchAlgorithmException e){ ++ if(Boolean.parseBoolean(System.getProperty("kae.sm3"))){ ++ throw e; ++ } ++ } ++ } ++ ++ public static void testAes() throws NoSuchAlgorithmException, NoSuchPaddingException { ++ Cipher cipher = Cipher.getInstance("AES"); ++ judge("kae.aes",cipher.getProvider().getName()); ++ } ++ ++ public static void testSm4() throws NoSuchAlgorithmException, NoSuchPaddingException { ++ try{ ++ Cipher cipher = Cipher.getInstance("SM4"); ++ judge("kae.sm4",cipher.getProvider().getName()); ++ }catch (NoSuchAlgorithmException e){ ++ if(Boolean.parseBoolean(System.getProperty("kae.sm4"))){ ++ throw e; ++ } ++ } ++ } ++ ++ public static void testHmac() throws NoSuchAlgorithmException { ++ Mac mac = Mac.getInstance("HmacMD5"); ++ judge("kae.hmac",mac.getProvider().getName()); ++ } ++ ++ public static void testRsa() throws NoSuchAlgorithmException, NoSuchPaddingException { ++ Cipher cipher = Cipher.getInstance("RSA"); ++ judge("kae.rsa",cipher.getProvider().getName()); ++ } ++ ++ public static void testDh() throws NoSuchAlgorithmException { ++ KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("DH"); ++ judge("kae.dh",keyPairGenerator.getProvider().getName()); ++ } ++ ++ public static void testEc() throws NoSuchAlgorithmException { ++ KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("EC"); ++ judge("kae.ec",keyPairGenerator.getProvider().getName()); ++ } ++ ++ private static void judge(String algorithm , String providerName){ ++ String value = System.getProperty(algorithm); ++ if (value == null) { ++ if (!KAE.equals(providerName)) { ++ throw new RuntimeException("KaeProviderTest Failed! default Provider.name is not right!"); ++ } ++ } else { ++ if (Boolean.parseBoolean(value) && !KAE.equals(providerName)) { ++ throw new RuntimeException("KaeProviderTest Failed! " + algorithm + " is " + value + "," + ++ " Provider.name is not right!"); ++ } ++ if (!Boolean.parseBoolean(value) && KAE.equals(providerName)) { ++ throw new RuntimeException("KaeProviderTest Failed! " + algorithm + " is " + value + ", " + ++ " Provider.name is not right!"); ++ } ++ } ++ } ++} +diff --git a/jdk/test/org/openeuler/security/openssl/RSATest.java b/jdk/test/org/openeuler/security/openssl/RSATest.java +new file mode 100644 +index 000000000..1f740af0b +--- /dev/null ++++ b/jdk/test/org/openeuler/security/openssl/RSATest.java +@@ -0,0 +1,137 @@ ++/* ++ * Copyright (c) 2022, Huawei Technologies Co., Ltd. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ */ ++ ++import org.openeuler.security.openssl.KAEProvider; ++ ++import java.nio.charset.StandardCharsets; ++import java.security.*; ++import java.security.spec.*; ++import javax.crypto.Cipher; ++ ++/** ++ * @test ++ * @summary Basic test for RSA ++ * @run main RSATest ++ */ ++ ++public class RSATest { ++ private static final String algorithm = "RSA"; ++ private static KeyPairGenerator keyPairGenerator; ++ private static byte[] privateKey; ++ private static byte[] publicKey; ++ private static String plainText = "helloworld"; ++ // 512, 768, ++ private static int[] keySizes = {1024, 2048, 4096, 5120, 6144}; ++ private static String[] signAlgorithms = { ++ "MD2withRSA", "MD5withRSA", "SHA1withRSA", "SHA224withRSA", "SHA256withRSA", "SHA384withRSA", "SHA512withRSA" ++ }; ++ private static String[] signAlgorithmsPSS = {"SHA-1", "SHA-224", "SHA-256", "SHA-384", "SHA-512"}; ++ ++ public static void main(String[] args) throws Exception { ++ Security.insertProviderAt(new KAEProvider(), 1); ++ ++ for (int keySize : keySizes) { ++ testKeyPairByKeySize(keySize); ++ testRSACipher(keySize); ++ testSignature(); ++ testPSSSignature(keySize); ++ } ++ } ++ ++ public static void testKeyPairByKeySize(int keySize) throws Exception { ++ keyPairGenerator = KeyPairGenerator.getInstance(algorithm); ++ keyPairGenerator.initialize(keySize); ++ KeyPair keyPair = keyPairGenerator.generateKeyPair(); ++ ++ PrivateKey pairPrivate = keyPair.getPrivate(); ++ PublicKey pairPublic = keyPair.getPublic(); ++ ++ privateKey = pairPrivate.getEncoded(); ++ publicKey = pairPublic.getEncoded(); ++ } ++ ++ public static void testRSACipher(int keySize) throws Exception { ++ PublicKey pubKey = KeyFactory.getInstance("RSA").generatePublic(new X509EncodedKeySpec(publicKey)); ++ Cipher cipher = Cipher.getInstance("RSA"); ++ cipher.init(Cipher.ENCRYPT_MODE, pubKey); ++ ++ byte[] cipherText = cipher.doFinal(plainText.getBytes(StandardCharsets.UTF_8)); ++ ++ PrivateKey priKey = KeyFactory.getInstance("RSA").generatePrivate(new PKCS8EncodedKeySpec(privateKey)); ++ ++ cipher.init(Cipher.DECRYPT_MODE, priKey); ++ ++ String decryptText = new String(cipher.doFinal(cipherText)); ++ ++ if (!plainText.equals(decryptText)) { ++ throw new RuntimeException("rsa decryption failed. keySize = " + keySize); ++ } ++ } ++ ++ public static void testSignature() throws Exception { ++ PrivateKey priKey = KeyFactory.getInstance("RSA").generatePrivate(new PKCS8EncodedKeySpec(privateKey)); ++ PublicKey pubKey = KeyFactory.getInstance("RSA").generatePublic(new X509EncodedKeySpec(publicKey)); ++ ++ for (String algorithm : signAlgorithms) { ++ Signature sign = Signature.getInstance(algorithm); ++ sign.initSign(priKey); ++ sign.update(plainText.getBytes()); ++ byte[] signInfo = sign.sign(); ++ ++ sign.initVerify(pubKey); ++ sign.update(plainText.getBytes()); ++ if (!sign.verify(signInfo)) { ++ throw new RuntimeException("rsa testSignature failed. digest algorithm = " + algorithm); ++ } ++ } ++ } ++ ++ public static void testPSSSignature(int keySize) throws Exception { ++ PrivateKey priKey = KeyFactory.getInstance("RSA").generatePrivate(new PKCS8EncodedKeySpec(privateKey)); ++ PublicKey pubKey = KeyFactory.getInstance("RSA").generatePublic(new X509EncodedKeySpec(publicKey)); ++ ++ Signature sign = Signature.getInstance("RSASSA-PSS"); ++ ++ for (String algorithm : signAlgorithmsPSS) { ++ if (algorithm.equals(signAlgorithmsPSS[4]) && keySize <= 1024) { ++ continue; ++ } ++ sign.initSign(priKey); ++ ++ MessageDigest digest = MessageDigest.getInstance(algorithm); ++ byte[] digestByte = digest.digest(plainText.getBytes()); ++ sign.setParameter( ++ new PSSParameterSpec(algorithm, "MGF1", new MGF1ParameterSpec(algorithm), digestByte.length, 1)); ++ ++ sign.update(plainText.getBytes()); ++ byte[] signInfo = sign.sign(); ++ ++ sign.initVerify(pubKey); ++ ++ sign.update(plainText.getBytes()); ++ if (!sign.verify(signInfo)) { ++ throw new RuntimeException("rsa testPSSSignature failed. digest algorithm = " + algorithm); ++ } ++ } ++ } ++} +diff --git a/jdk/test/org/openeuler/security/openssl/SM3Test.java b/jdk/test/org/openeuler/security/openssl/SM3Test.java +deleted file mode 100644 +index 181f708ff..000000000 +--- a/jdk/test/org/openeuler/security/openssl/SM3Test.java ++++ /dev/null +@@ -1,54 +0,0 @@ +-/* +- * Copyright (c) 2021, Huawei Technologies Co., Ltd. All rights reserved. +- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +- * +- * This code is free software; you can redistribute it and/or modify it +- * under the terms of the GNU General Public License version 2 only, as +- * published by the Free Software Foundation. +- * +- * This code is distributed in the hope that it will be useful, but WITHOUT +- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +- * version 2 for more details (a copy is included in the LICENSE file that +- * accompanied this code). +- * +- * You should have received a copy of the GNU General Public License version +- * 2 along with this work; if not, write to the Free Software Foundation, +- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +- * +- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA +- * or visit www.oracle.com if you need additional information or have any +- * questions. +- */ +- +-import org.openeuler.security.openssl.KAEProvider; +-import java.nio.charset.StandardCharsets; +-import java.util.Arrays; +-import java.security.MessageDigest; +-import java.security.Security; +- +-/** +- * @test +- * @summary Basic test for sm3 +- * @run main SM3Test +- */ +- +-public class SM3Test { +- +- private static String plainText = "helloworldhellow"; +- +- public static void main(String[] args) throws Exception { +- Security.insertProviderAt(new KAEProvider(), 1); +- test(plainText, "SM3", new byte[]{40, -103, -71, 4, -80, -49, 94, 112, 11, -75, -66, 121, 63, 80, 62, -14, -45, -75, -34, 66, -77, -34, -26, 26, 33, -23, 45, 52, -74, 67, -18, 118}); +- } +- +- public static void test(String plainText, String algo, byte[] expectRes) throws Exception { +- MessageDigest md = MessageDigest.getInstance(algo); +- md.update(plainText.getBytes(StandardCharsets.UTF_8)); +- byte[] res = md.digest(); +- if (!Arrays.equals(res, expectRes)) { +- throw new RuntimeException("sm3 failed"); +- } +- } +- +-} +diff --git a/jdk/test/org/openeuler/security/openssl/SM4Test.java b/jdk/test/org/openeuler/security/openssl/SM4Test.java +index 4c28dc5b6..1029fe897 100644 +--- a/jdk/test/org/openeuler/security/openssl/SM4Test.java ++++ b/jdk/test/org/openeuler/security/openssl/SM4Test.java +@@ -22,9 +22,10 @@ + */ + + import org.openeuler.security.openssl.KAEProvider; ++ ++import java.nio.ByteBuffer; + import java.nio.charset.StandardCharsets; + import java.util.Arrays; +-import java.security.NoSuchAlgorithmException; + import java.security.Security; + import javax.crypto.Cipher; + import javax.crypto.spec.IvParameterSpec; +@@ -55,6 +56,25 @@ public class SM4Test { + test(shortPlainText, "SM4/OFB/PKCS5Padding", new byte[]{32, 108, 35, 108, -16, 119, -111, 114, 94, 110}); + + testCtrShortIv(plainText, "SM4/CTR/NOPADDING", new byte[]{-13, 73, 40, -36, -64, -67, 75, -72, 90, 58, 73, -4, -36, 115, 126, -48}); ++ ++ testByteBuffer(plainText, "SM4/CBC/NOPADDING", new byte[]{86, 69, 47, -115, -63, 54, 35, 24, -2, 114, 113, 102, 82, 20, 69, 59}); ++ testByteBuffer(shortPlainText, "SM4/CBC/PKCS5Padding", new byte[]{10, 105, 75, -80, -85, -68, 13, -53, 42, 91, -64, 99, 104, 35, -85, 8}); ++ testByteBuffer(plainText, "SM4/ECB/NOPADDING", new byte[]{103, 36, -31, -53, -109, -12, -71, -79, -54, 106, 10, -3, -35, -22, -122, -67}); ++ testByteBuffer(shortPlainText, "SM4/ECB/PKCS5Padding", new byte[]{-10, 99, -9, 90, 58, -36, -109, 54, -55, -52, 7, -49, 110, -88, 72, 40}); ++ testByteBuffer(plainText, "SM4/CTR/NOPADDING", new byte[]{32, 108, 35, 108, -16, 119, -111, 114, 94, 110, -100, -113, -46, -29, -11, 71}); ++ testByteBuffer(plainText, "SM4/OFB/NOPADDING", new byte[]{32, 108, 35, 108, -16, 119, -111, 114, 94, 110, -100, -113, -46, -29, -11, 71}); ++ testByteBuffer(shortPlainText, "SM4/OFB/PKCS5Padding", new byte[]{32, 108, 35, 108, -16, 119, -111, 114, 94, 110}); ++ ++ System.setProperty("kae.sm4.maxChunkSize", "65536"); ++ testByteBuffer(plainText, "SM4/CBC/NOPADDING", new byte[]{86, 69, 47, -115, -63, 54, 35, 24, -2, 114, 113, 102, 82, 20, 69, 59}); ++ testByteBuffer(shortPlainText, "SM4/CBC/PKCS5Padding", new byte[]{10, 105, 75, -80, -85, -68, 13, -53, 42, 91, -64, 99, 104, 35, -85, 8}); ++ testByteBuffer(plainText, "SM4/ECB/NOPADDING", new byte[]{103, 36, -31, -53, -109, -12, -71, -79, -54, 106, 10, -3, -35, -22, -122, -67}); ++ testByteBuffer(shortPlainText, "SM4/ECB/PKCS5Padding", new byte[]{-10, 99, -9, 90, 58, -36, -109, 54, -55, -52, 7, -49, 110, -88, 72, 40}); ++ testByteBuffer(plainText, "SM4/CTR/NOPADDING", new byte[]{32, 108, 35, 108, -16, 119, -111, 114, 94, 110, -100, -113, -46, -29, -11, 71}); ++ testByteBuffer(plainText, "SM4/OFB/NOPADDING", new byte[]{32, 108, 35, 108, -16, 119, -111, 114, 94, 110, -100, -113, -46, -29, -11, 71}); ++ testByteBuffer(shortPlainText, "SM4/OFB/PKCS5Padding", new byte[]{32, 108, 35, 108, -16, 119, -111, 114, 94, 110}); ++ ++ + } + + public static void test(String plainText, String algo, byte[] expectRes) throws Exception { +@@ -92,4 +112,44 @@ public class SM4Test { + throw new RuntimeException("sm4 decryption failed, algo = " + algo); + } + } ++ ++ public static void testByteBuffer(String plainText, String algo, byte[] expectRes) throws Exception { ++ // encrypt ++ Cipher encryptCipher = Cipher.getInstance(algo); ++ if (algo.contains("ECB")) { ++ encryptCipher.init(Cipher.ENCRYPT_MODE, ks); ++ } else { ++ encryptCipher.init(Cipher.ENCRYPT_MODE, ks, iv); ++ } ++ int inputLen = plainText.length(); ++ ByteBuffer sourceByteBuffer = ByteBuffer.allocateDirect(inputLen); ++ sourceByteBuffer.put(plainText.getBytes()); ++ sourceByteBuffer.flip(); ++ int outputLen = encryptCipher.getOutputSize(inputLen); ++ ByteBuffer encryptedByteBuffer = ByteBuffer.allocate(outputLen); ++ encryptCipher.doFinal(sourceByteBuffer,encryptedByteBuffer); ++ encryptedByteBuffer.flip(); ++ byte[] encryptedBytes = new byte[encryptedByteBuffer.limit()]; ++ encryptedByteBuffer.get(encryptedBytes); ++ if (!Arrays.equals(encryptedBytes, expectRes)) { ++ throw new RuntimeException("sm4 encryption failed, algo = " + algo); ++ } ++ sourceByteBuffer.clear(); ++ encryptedByteBuffer.flip(); ++ ++ // decrypt ++ Cipher decryptCipher = Cipher.getInstance(algo); ++ decryptCipher.init(Cipher.DECRYPT_MODE, ks, encryptCipher.getParameters()); ++ outputLen = decryptCipher.getOutputSize(encryptedBytes.length); ++ ByteBuffer decryptedByteBuffer = ByteBuffer.allocate(outputLen); ++ decryptCipher.doFinal(encryptedByteBuffer, decryptedByteBuffer); ++ decryptedByteBuffer.flip(); ++ byte[] decryptedBytes = new byte[decryptedByteBuffer.limit()]; ++ decryptedByteBuffer.get(decryptedBytes); ++ if (!Arrays.equals(plainText.getBytes(), decryptedBytes)) { ++ throw new RuntimeException("sm4 decryption failed, algo = " + algo); ++ } ++ encryptedByteBuffer.clear(); ++ decryptedByteBuffer.clear(); ++ } + } +-- +2.17.1 + diff --git a/modify_coding_style_and_describe_error.patch b/modify_coding_style_and_describe_error.patch new file mode 100644 index 0000000000000000000000000000000000000000..9464ab21dde76df0e439597cd55f1c8f7caa22cf --- /dev/null +++ b/modify_coding_style_and_describe_error.patch @@ -0,0 +1,56 @@ +From 9d32c786ff6886bcd4b76e0a80eb19ce602dbe42 Mon Sep 17 00:00:00 2001 +From: wangkun +Date: Thu, 28 Jul 2022 17:24:52 +0800 +Subject: [PATCH 3/3] fix xx + +--- + .../classes/org/openeuler/security/openssl/KAEDigest.java | 6 +++--- + .../classes/org/openeuler/security/openssl/KAEProvider.java | 2 -- + jdk/src/solaris/native/java/io/path_util.c | 1 - + 3 files changed, 3 insertions(+), 6 deletions(-) + +diff --git a/jdk/src/solaris/classes/org/openeuler/security/openssl/KAEDigest.java b/jdk/src/solaris/classes/org/openeuler/security/openssl/KAEDigest.java +index bb5c8681..6ff03241 100644 +--- a/jdk/src/solaris/classes/org/openeuler/security/openssl/KAEDigest.java ++++ b/jdk/src/solaris/classes/org/openeuler/security/openssl/KAEDigest.java +@@ -88,9 +88,9 @@ abstract class KAEDigest extends MessageDigestSpi implements Cloneable { + private static class DigestContextRef extends PhantomReference + implements Comparable { + +- private static ReferenceQueue referenceQueue = new ReferenceQueue<>(); +- private static Set referenceList = new ConcurrentSkipListSet<>(); +- private static boolean disableKaeDispose = Boolean.getBoolean("kae.disableKaeDispose"); ++ private static final ReferenceQueue referenceQueue = new ReferenceQueue<>(); ++ private static final Set referenceList = new ConcurrentSkipListSet<>(); ++ private static final boolean disableKaeDispose = Boolean.getBoolean("kae.disableKaeDispose"); + + private final long ctxAddress; + +diff --git a/jdk/src/solaris/classes/org/openeuler/security/openssl/KAEProvider.java b/jdk/src/solaris/classes/org/openeuler/security/openssl/KAEProvider.java +index 8ba70200..83ed8649 100644 +--- a/jdk/src/solaris/classes/org/openeuler/security/openssl/KAEProvider.java ++++ b/jdk/src/solaris/classes/org/openeuler/security/openssl/KAEProvider.java +@@ -104,8 +104,6 @@ public class KAEProvider extends Provider { + if (needLog && "true".equalsIgnoreCase(props.getProperty("kae.log"))) { + logStart(excp); + needLog = false; // Log only once +- } else { +- KAEProvider.excp = null; // Ignore exception. + } + if (!"false".equalsIgnoreCase(props.getProperty("kae.md5"))) { + putMD5(); +diff --git a/jdk/src/solaris/native/java/io/path_util.c b/jdk/src/solaris/native/java/io/path_util.c +index 8a533f81..4b978206 100644 +--- a/jdk/src/solaris/native/java/io/path_util.c ++++ b/jdk/src/solaris/native/java/io/path_util.c +@@ -116,7 +116,6 @@ collapse(char *path) + int nc; + char **ix; + int i, j; +- char *p, *q; + + nc = collapsible(names); + if (nc < 2) return; /* Nothing to do */ +-- +2.22.0 + diff --git a/openjdk-1.8.0.spec b/openjdk-1.8.0.spec index 6571b1a8bf966fa415faea07576e142d0d23b6eb..6baa17c8e94008477a6bf82d37206151c94ee5fb 100644 --- a/openjdk-1.8.0.spec +++ b/openjdk-1.8.0.spec @@ -52,9 +52,9 @@ %endif %global aarch64 aarch64 -%global jit_arches x86_64 %{aarch64} -%global sa_arches x86_64 %{aarch64} -%global jfr_arches x86_64 %{aarch64} +%global jit_arches x86_64 %{aarch64} loongarch64 +%global sa_arches x86_64 %{aarch64} loongarch64 +%global jfr_arches x86_64 %{aarch64} loongarch64 # By default, we build a debug build during main build on JIT architectures %global include_debug_build 1 @@ -115,6 +115,16 @@ %global archinstall aarch64 %global stapinstall arm64 %endif +%ifarch loongarch64 +%global archinstall loongarch64 +%global stapinstall loongarch64 +%endif + +# Need to support noarch for srpm build +%ifarch noarch +%global archinstall %{nil} +%global stapinstall %{nil} +%endif %ifarch %{jit_arches} %global with_systemtap 1 @@ -146,13 +156,13 @@ %global origin_nice OpenJDK %global top_level_dir_name %{origin} %global repo jdk8u -%global revision jdk8u322-b06 +%global revision jdk8u352-b08 %global full_revision %{repo}-%{revision} # Define IcedTea version used for SystemTap tapsets and desktop files %global icedteaver 3.15.0 -%global updatever 322 -%global buildver b06 +%global updatever 352 +%global buildver b08 # priority must be 7 digits in total. The expression is workarounding tip %global priority 1800%{updatever} @@ -583,7 +593,7 @@ exit 0 %{_jvmdir}/%{jredir -- %{?1}}/lib/%{archinstall}/libnpt.so %ifarch %{aarch64} %{_jvmdir}/%{jredir -- %{?1}}/lib/%{archinstall}/libj2kae.so -%{_jvmdir}/%{jredir -- %{?1}}/lib/ext/kaeprovider.conf +%{_jvmdir}/%{jredir -- %{?1}}/lib/kaeprovider.conf %endif %ifarch %{sa_arches} %{_jvmdir}/%{jredir -- %{?1}}/lib/%{archinstall}/libsaproc.so @@ -820,7 +830,7 @@ Requires: nss-softokn%{?_isa} %{NSSSOFTOKN_BUILDTIME_VERSION} # tool to copy jdk's configs - should be Recommends only, but then only dnf/yum enforce it, # not rpm transaction and so no configs are persisted when pure rpm -u is run. It may be # considered as regression -Requires: copy-jdk-configs >= 3.3 +Requires: copy-jdk-configs >= 3.9 OrderWithRequires: copy-jdk-configs # for printing support Requires: cups-libs @@ -916,7 +926,7 @@ Provides: java-%{javaver}-%{origin}-accessibility%{?1} = %{epoch}:%{version}-%{r Name: java-%{javaver}-%{origin} Version: %{javaver}.%{updatever}.%{buildver} -Release: 4 +Release: 6 # java-1.5.0-ibm from jpackage.org set Epoch to 1 for unknown reasons # and this change was brought into RHEL-4. java-1.5.0-ibm packages # also included the epoch in their virtual provides. This created a @@ -993,7 +1003,6 @@ Patch58: Reduce-the-probability-of-the-crash-related-to-ciObj.patch Patch62: 8165857.patch Patch63: 8033552.patch Patch67: 8165860.patch -Patch68: 8194154.patch Patch70: 8164948.patch # 8u242 @@ -1030,8 +1039,6 @@ Patch115: 8243670.patch Patch118: Fix-LineBuffer-vappend-when-buffer-too-small.patch Patch121: Remove-unused-GenericTaskQueueSet-T-F-tasks.patch Patch122: optimize-jmap-F-dump-xxx.patch -Patch123: recreate-.java_pid-file-when-deleted-for-attach-mechanism.patch -Patch124: Support-Git-commit-ID-in-the-SOURCE-field-of-the-release.patch Patch125: Extend-CDS-to-support-app-class-metadata-sharing.patch Patch127: add-DumpSharedSpace-guarantee-when-create-anonymous-classes.patch @@ -1046,7 +1053,6 @@ Patch142: 8207160.patch Patch144: add-appcds-test-case.patch # 8u282 -Patch146: 8168926.patch Patch147: 8215047.patch Patch148: 8237894.patch Patch149: Remove-the-parentheses-around-company-name.patch @@ -1077,15 +1083,12 @@ Patch177: downgrade-symver-of-memcpy-GLIBC.patch Patch178: fix-log-bug-enhance-aes-hmac-performance.patch Patch179: keep-the-binary-equal.patch Patch180: link-option-use-rpath-instead-of-runpath.patch -Patch181: remove-gnu-debuglink-when-using-enable-debug-.patch Patch183: revert-windows-bugfix.patch Patch184: set-vm.vendor-by-configure.patch Patch185: update-cacerts-and-VerifyCACerts.java-test.patch Patch186: update-to-keep-same-with-master.patch Patch188: 8247691_incorrect_handling_of_VM_exceptions_in_C1_deopt_stub.patch -Patch189: 8266187_Memory_leak_in_appendBootClassPath.patch Patch192: add_kae_implementation_add_default_conf_file.patch -Patch193: improve_algorithmConstraints_checkAlgorithm_performance.patch Patch194: modify_the_default_iteration_time_and_forks_in_the_JMH_of_KAEProvider.patch Patch195: support_CMS_parallel_inspection.patch Patch196: g1gc-numa-aware-Implementation.patch @@ -1099,17 +1102,14 @@ Patch202: Fix-RSACipher-memory-usage.patch Patch203: fix-lock-ordering-issue-when-calling-JVMTI-GetLoaded.patch Patch204: 8069191.patch Patch205: fix_g1uncommit_ygc_expand_crash.patch -Patch206: 8167014-jdeps-failed-with-Missing-message-warn-skippen-entry.patch Patch207: fix_bug_in_keypairgenerator.patch Patch208: C1-assert-is_virtual-failed-type-check.patch Patch209: 8197387-Run-the-jcmd-tool-as-the-root-user-to-access.patch Patch210: create-jfr-dump-file-with-pid-or-timestamp-if-specif.patch Patch212: enhance-the-TimeZone-s-path-solution-on-Euler.patch -Patch213: fix-wrong-commitID-in-release-file.patch Patch214: fix-appcds-s-option-AppCDSLockFile.patch Patch215: PS-introduce-UsePSRelaxedForwardee-to-enable-using-r.patch Patch216: Parallel-Full-GC-for-G1.patch -Patch217: 8202142-jfr-event-io-TestInstrumentation-is-unstable.patch Patch218: 8143251-Thread-suspend-on-VM_G1IncCollectionPause-do.patch Patch219: G1Uncommit-Introduce-G1PeriodGCNotRetry-control-whet.patch Patch220: JDK-debug-version-crash-when-using-AppCDS.patch @@ -1125,8 +1125,6 @@ Patch229: downgrade-the-symver-of-fcntl64.patch # 8u322 Patch230: add-system-property-swing.JComboBox.useLegacyMode.patch -Patch231: debuginfo.diz-should-not-contain-the-path-after-unzip.patch -Patch232: 8173361-various-crashes-in-JvmtiExport-post_compiled.patch Patch233: fix-TestUseCompressedOopsErgo-run-failed.patch Patch235: fix-testme-Test6929067-run-faild.patch Patch236: penetration_testing_vulnerability_fix.patch @@ -1136,6 +1134,65 @@ Patch239: print-fd-and-file-path-when-a-zip-invalid-loc-header.patch Patch240: 8207011-Remove-uses-of-the-register-storage-class-specifier.patch Patch241: 8268819-SA-Remove-libthread_db-dependency-on-Linux.patch +# 8u332 +Patch243: Fix-compile-and-runtime-failures-for-minimal1-versio.patch +Patch244: fix_X509TrustManagerImpl_symantec_distrust.patch +Patch245: change-sa-jdi.jar-make-file-for-BEP.PATCH +Patch246: 7092821-java.security.Provider.getService-is-synchro.patch +Patch248: 8067941-TESTBUG-Fix-tests-for-OS-with-64K-page-size.patch + +# 8u342 +Patch249: Improve_AlgorithmConstraints_checkAlgorithm_performance.patch +Patch250: modify_coding_style_and_describe_error.patch +Patch251: fix_wrap_memcpy_undefined_gcc10_3.patch +Patch252: 8290705_fix_StringConcat_validate_mem_flow_asserts_with_unexpected_userStoreI.patch +Patch253: 8143925-enhancing-CounterMode.crypt-for-AESCrypt.patch +Patch254: kae-usability-enhancement.patch +Patch255: Dynamic-CDS-Archive.patch +Patch256: 8202951-Support-default-jsa.patch +Patch257: 8200332-Improve-GCM-counting.patch +Patch258: dynamic-cds-_header-and-_fd-handles-are-not-free.patch +Patch259: fix-dumped-heap-using-jhat-parsing-to-appear-failed-to-resolve-object-id-warning-message.patch +Patch260: 8159720-Failure-of-C2-compilation-with-tiered-preven.patch +Patch261: revert-fPIC-and-security-compilation-flag-on.patch +Patch262: add-configuration-option-of-huawei-internal-version-shown-in-release-file.patch +Patch263: The-code-style-is-fixed-and-test-cases-are-added.patch +Patch264: 8287109-Distrust-failed-with-CertificateExpired.patch + +# 8u352 +Patch265: cve-2022-37434-Fix-a-bug-when-getting-a-gzip-header-extra-field-with-inflate.patch +Patch266: 8065895-Synchronous-signals-during-error-reporting-may-terminate-or-hang-vm-process.patch +Patch267: Huawei-fix-windows-build-Dynamic-CDS-failure.patch +Patch268: 8296480-Fix-the-problem-that-the-TestPolicy.java-cas.patch +Patch269: 8296485-BuildEEBasicConstraints.java-test-fails-with.patch +Patch270: 8294357-tz-Update-Timezone-Data-to-2022d.patch +Patch271: 8296241-tz-Update-Timezone-Data-to-2022e.patch +Patch272: 8296108-tz-Update-Timezone-Data-to-2022f.patch +Patch273: 8257695-linux-Add-process-memory-information-to-hs-e.patch +Patch274: 8261167-print_process_memory_info-add-a-close-call-a.patch +Patch275: 8268893-jcmd-to-trim-the-glibc-heap.patch +Patch276: 8263185-Mallinfo-deprecated-in-glibc-2.33.patch +Patch277: 8293114-GC-should-trim-the-native-heap-and-bug-fix.patch +Patch278: 8275775-Add-jcmd-VM.classes-to-print-details-of-all-.patch +Patch279: Fix-compactibleFreeListSpace-block_size_no_stall-cra.patch +Patch280: 8203682-Add-jcmd-VM.classloaders-command-to-print-ou.patch +Patch281: 8229517-Support-for-optional-asynchronous-buffered-l.patch +Patch282: 8189688-NMT-Report-per-class-load-metadata-informati.patch +Patch283: 8219584-Try-to-dump-error-file-by-thread-which-cause.patch +Patch284: 8204595-add-more-thread-related-system-settings-info.patch +Patch285: 8198553-jcmd-separate-Metaspace-statistics-from-NMT.patch +Patch286: 8242181-Show-source-information-when-printing-native.patch +Patch287: Print-class-loading-details-when-enable-TraceClassLo.patch +Patch288: 8200720-Print-additional-information-in-thread-dump-.patch +Patch289: support-numactl-for-hadoop-yarn.patch +Patch290: 8232069-enable-shutdown-UseCompressedClassPointers-U.patch +Patch291: 8065402-G1-does-not-expand-marking-stack-when-mark-s.patch +Patch292: fix-the-length-value-of-ciBlock-in-ciMethodBlocks.cp.patch +Patch293: 8140594-Various-minor-code-improvements-compiler.patch +Patch294: Fix-the-crash-that-occurs-when-the-process-exits-due.patch +Patch295: Fix-AsyncGCLog-s-content-consistent-bug.patch + + ############################################# # # Upstreamable patches @@ -1182,6 +1239,10 @@ Patch539: pr2888-openjdk_should_check_for_system_cacerts_database_eg_etc_pki_jav ############################################# Patch1000: rh1648249-add_commented_out_nss_cfg_provider_to_java_security.patch +# LoongArch64 support +Patch2000: LoongArch64-support-jdk8u%{updatever}%{buildver}.patch +Patch2001: add-header-file-for-LoongArch64.patch + ############################################# # # Dependencies @@ -1196,6 +1257,7 @@ BuildRequires: cups-devel BuildRequires: desktop-file-utils # elfutils only are OK for build without AOT BuildRequires: elfutils-devel +BuildRequires: elfutils-extra BuildRequires: fontconfig-devel BuildRequires: freetype-devel BuildRequires: giflib-devel @@ -1470,6 +1532,7 @@ ln -s %{top_level_dir_name} jdk8 pushd %{top_level_dir_name} # OpenJDK patches +%ifnarch loongarch64 %patch8 -p1 %patch10 -p1 %patch18 -p1 @@ -1493,7 +1556,6 @@ pushd %{top_level_dir_name} %patch62 -p1 %patch63 -p1 %patch67 -p1 -%patch68 -p1 %patch70 -p1 %patch75 -p1 %patch83 -p1 @@ -1522,8 +1584,6 @@ pushd %{top_level_dir_name} %patch118 -p1 %patch121 -p1 %patch122 -p1 -%patch123 -p1 -%patch124 -p1 %patch125 -p1 %patch127 -p1 %patch133 -p1 @@ -1534,7 +1594,6 @@ pushd %{top_level_dir_name} %patch141 -p1 %patch142 -p1 %patch144 -p1 -%patch146 -p1 %patch147 -p1 %patch148 -p1 %patch149 -p1 @@ -1563,13 +1622,11 @@ pushd %{top_level_dir_name} %patch178 -p1 %patch179 -p1 %patch180 -p1 -%patch181 -p1 %patch183 -p1 %patch184 -p1 %patch185 -p1 %patch186 -p1 %patch188 -p1 -%patch189 -p1 %patch192 -p1 %patch194 -p1 %patch195 -p1 @@ -1582,17 +1639,14 @@ pushd %{top_level_dir_name} %patch203 -p1 %patch204 -p1 %patch205 -p1 -%patch206 -p1 %patch207 -p1 %patch208 -p1 %patch209 -p1 %patch210 -p1 %patch212 -p1 -%patch213 -p1 %patch214 -p1 %patch215 -p1 %patch216 -p1 -%patch217 -p1 %patch218 -p1 %patch219 -p1 %patch220 -p1 @@ -1604,8 +1658,6 @@ pushd %{top_level_dir_name} %patch228 -p1 %patch229 -p1 %patch230 -p1 -%patch231 -p1 -%patch232 -p1 %patch233 -p1 %patch235 -p1 %patch236 -p1 @@ -1614,6 +1666,63 @@ pushd %{top_level_dir_name} %patch239 -p1 %patch240 -p1 %patch241 -p1 +%patch243 -p1 +%patch244 -p1 +%patch245 -p1 +%patch246 -p1 +%patch248 -p1 +%patch249 -p1 +%patch250 -p1 +%patch251 -p1 +%patch252 -p1 +%patch253 -p1 +%patch254 -p1 +%patch255 -p1 +%patch256 -p1 +%patch257 -p1 +%patch258 -p1 +%patch259 -p1 +%patch260 -p1 +%patch261 -p1 +%patch262 -p1 +%patch263 -p1 +%patch264 -p1 +%patch265 -p1 +%patch266 -p1 +%patch267 -p1 +%patch268 -p1 +%patch269 -p1 +%patch270 -p1 +%patch271 -p1 +%patch272 -p1 +%patch273 -p1 +%patch274 -p1 +%patch275 -p1 +%patch276 -p1 +%patch277 -p1 +%patch278 -p1 +%patch279 -p1 +%patch280 -p1 +%patch281 -p1 +%patch282 -p1 +%patch283 -p1 +%patch284 -p1 +%patch285 -p1 +%patch286 -p1 +%patch287 -p1 +%patch288 -p1 +%patch289 -p1 +%patch290 -p1 +%patch291 -p1 +%patch292 -p1 +%patch293 -p1 +%patch294 -p1 +%patch295 -p1 +%endif +%ifarch loongarch64 +%patch2000 -p1 +%patch2001 -p1 +%endif popd # System library fixes @@ -1715,15 +1824,19 @@ bash ${top_srcdir_abs_path}/configure \ --with-milestone="fcs" \ --with-update-version=%{updatever} \ --with-build-number=%{buildver} \ +%ifnarch loongarch64 --with-company-name="Bisheng" \ --with-vendor-name="Bisheng" \ +%endif --with-vendor-url="https://openeuler.org/" \ --with-vendor-bug-url="https://gitee.com/src-openeuler/openjdk-1.8.0/issues/" \ --with-vendor-vm-bug-url="https://gitee.com/src-openeuler/openjdk-1.8.0/issues/" \ --with-debug-level=$debugbuild \ --enable-unlimited-crypto \ --with-zlib=system \ +%ifnarch loongarch64 --enable-kae=yes \ +%endif --with-stdc++lib=dynamic \ --with-extra-cflags="$EXTRA_CFLAGS" \ --with-extra-cxxflags="$EXTRA_CPP_FLAGS" \ @@ -1842,6 +1955,7 @@ done # javaCalls.cpp:58 should map to: # http://hg.openjdk.java.net/jdk8u/jdk8u/hotspot/file/ff3b27e6bcc2/src/share/vm/runtime/javaCalls.cpp#l58 # Using line number 1 might cause build problems. +%ifnarch loongarch64 gdb -q "$JAVA_HOME/bin/java" < - 1:1.8.0.352-b08.6 +- add 8296480-Fix-the-problem-that-the-TestPolicy.java-cas.patch +- add 8296485-BuildEEBasicConstraints.java-test-fails-with.patch +- add 8294357-tz-Update-Timezone-Data-to-2022d.patch +- add 8296241-tz-Update-Timezone-Data-to-2022e.patch +- add 8296108-tz-Update-Timezone-Data-to-2022f.patch +- add 8257695-linux-Add-process-memory-information-to-hs-e.patch +- add 8261167-print_process_memory_info-add-a-close-call-a.patch +- add 8268893-jcmd-to-trim-the-glibc-heap.patch +- add 8263185-Mallinfo-deprecated-in-glibc-2.33.patch +- add 8293114-GC-should-trim-the-native-heap-and-bug-fix.patch +- add 8275775-Add-jcmd-VM.classes-to-print-details-of-all-.patch +- add Fix-compactibleFreeListSpace-block_size_no_stall-cra.patch +- add 8203682-Add-jcmd-VM.classloaders-command-to-print-ou.patch +- add 8229517-Support-for-optional-asynchronous-buffered-l.patch +- add 8189688-NMT-Report-per-class-load-metadata-informati.patch +- add 8219584-Try-to-dump-error-file-by-thread-which-cause.patch +- add 8204595-add-more-thread-related-system-settings-info.patch +- add 8198553-jcmd-separate-Metaspace-statistics-from-NMT.patch +- add 8242181-Show-source-information-when-printing-native.patch +- add Print-class-loading-details-when-enable-TraceClassLo.patch +- add 8200720-Print-additional-information-in-thread-dump-.patch +- add support-numactl-for-hadoop-yarn.patch +- add 8232069-enable-shutdown-UseCompressedClassPointers-U.patch +- add 8065402-G1-does-not-expand-marking-stack-when-mark-s.patch +- add fix-the-length-value-of-ciBlock-in-ciMethodBlocks.cp.patch +- add 8140594-Various-minor-code-improvements-compiler.patch +- add Fix-the-crash-that-occurs-when-the-process-exits-due.patch +- add Fix-AsyncGCLog-s-content-consistent-bug.patch + +* Fri Nov 25 2022 aoqi - 1:1.8.0.352-b08.5 +- init support of LoongArch64 + +* Tue Oct 25 2022 kuenking111 - 1:1.8.0.352-b08.4 +- add Huawei-fix-windows-build-Dynamic-CDS-failure.patch + +* Mon Oct 24 2022 kuenking111 - 1:1.8.0.352-b08.3 +- add 8065895-Synchronous-signals-during-error-reporting-may-terminate-or-hang-vm-process.patch + +* Mon Oct 24 2022 kuenking111 - 1:1.8.0.352-b08.2 +- add cve-2022-37434-Fix-a-bug-when-getting-a-gzip-header-extra-field-with-inflate.patch + +* Mon Oct 24 2022 kuenking111 - 1:1.8.0.352-b08.1 +- remove gitattributes gitignore jcheck files + +* Wed Oct 19 2022 kuenking111 - 1:1.8.0.352-b08.0 +- modified add-missing-test-case.patch +- upgrade to jdk8u352-b08 + +* Thu Sep 29 2022 DXwangg - 1:1.8.0.352-b07.0 +- upgrade to jdk8u352-b07 +- deleted Improve_AlgorithmConstraints_checkAlgorithm_performance.patch +- deleted 8173361-various-crashes-in-JvmtiExport-post_compiled.patch +- modified Fix-compile-and-runtime-failures-for-minimal1-versio.patch +- deleted 8173339-AArch64-Fix-minimum-stack-size-computations.patch +- modified add-appcds-file-lock.patch +- modified add-DumpSharedSpace-guarantee-when-create-anonymous-classes.patch +- modified fix-appcds-s-option-AppCDSLockFile.patch +- modified fix-windows-compile-fail.patch + +* Sat Sep 24 2022 kuenking111 - 1:1.8.0.342-b07.15 +- add 8287109-Distrust-failed-with-CertificateExpired.patch + +* Fri Sep 23 2022 kuenking111 - 1:1.8.0.342-b07.14 +- add The-code-style-is-fixed-and-test-cases-are-added.patch + +* Thu Sep 22 2022 kuenking111 - 1:1.8.0.342-b07.13 +- add add-configuration-option-of-huawei-internal-version-shown-in-release-file.patch + +* Wed Sep 21 2022 kuenking111 - 1:1.8.0.342-b07.12 +- add revert-fPIC-and-security-compilation-flag-on.patch + +* Mon Sep 19 2022 zhoulei - 1:1.8.0.342-b07.11 +- add 8159720-Failure-of-C2-compilation-with-tiered-preven.patch + +* Fri Sep 16 2022 kuenking111 - 1:1.8.0.342-b07.10 +- add fix-dumped-heap-using-jhat-parsing-to-appear-failed-to-resolve-object-id-warning-message.patch + +* Fri Sep 16 2022 kuenking111 - 1:1.8.0.342-b07.9 +- add dynamic-cds-_header-and-_fd-handles-are-not-free.patch + +* Fri Sep 16 2022 kuenking111 - 1:1.8.0.342-b07.8 +- add 8200332-Improve-GCM-counting.patch + +* Fri Sep 16 2022 kuenking111 - 1:1.8.0.342-b07.7 +- add 8202951-Support-default-jsa.patch + +* Thu Sep 15 2022 kuenking111 - 1:1.8.0.342-b07.6 +- add Dynamic-CDS-Archive.patch + +* Thu Sep 15 2022 kuenking111 - 1:1.8.0.342-b07.5 +- add kae-usability-enhancement.patch + +* Thu Sep 15 2022 kuenking111 - 1:1.8.0.342-b07.4 +- add 8143925-enhancing-CounterMode.crypt-for-AESCrypt.patch + +* Fri Aug 5 2022 kuenking111 - 1:1.8.0.342-b07.3 +- add 8290705_fix_StringConcat_validate_mem_flow_asserts_with_unexpected_userStoreI.patch +- modified version.txt + +* Thu Jul 28 2022 kuenking111 - 1:1.8.0.342-b07.2 +- add modify_coding_style_and_describe_error.patch +- add Improve_AlgorithmConstraints_checkAlgorithm_performance.patch +- add fix_wrap_memcpy_undefined_gcc10_3.patch +- modified implementation_of_Blas_hotspot_function_in_Intrinsics.patch + +* Thu Jul 28 2022 kuenking111 - 1:1.8.0.342-b07.1 +- del hg git files + +* Fri Jul 22 2022 kuenking111 - 1:1.8.0.342-b07.0 +- del 8168926.patch +- del 8194154.patch +- del 8202142-jfr-event-io-TestInstrumentation-is-unstable.patch +- del 8266187_Memory_leak_in_appendBootClassPath.patch +- del debuginfo.diz-should-not-contain-the-path-after-unzip.patch +- del fix-make-bugs-when-git-and-hg-not-exist.patch +- modified 7092821-java.security.Provider.getService-is-synchro.patch +- modified 8268819-SA-Remove-libthread_db-dependency-on-Linux.patch +- modified fix-log-bug-enhance-aes-hmac-performance.patch + +* Fri Jul 15 2022 kuenking111 - 1:1.8.0.332-b09.7 +- del remove-gnu-debuglink-when-using-enable-debug-.patch + +* Mon Jul 4 2022 kuenking111 - 1:1.8.0.332-b09.6 +- add 8067941-TESTBUG-Fix-tests-for-OS-with-64K-page-size.patch + +* Mon Jul 4 2022 kuenking111 - 1:1.8.0.332-b09.5 +- add 8173339-AArch64-Fix-minimum-stack-size-computations.patch + +* Mon Jul 4 2022 kuenking111 - 1:1.8.0.332-b09.4 +- add 7092821-java.security.Provider.getService-is-synchro.patch + +* Thu Jun 30 2022 kuenking111 - 1:1.8.0.332-b09.3 +- add change-sa-jdi.jar-make-file-for-BEP.PATCH + +* Thu Apr 28 2022 kuenking111 - 1:1.8.0.332-b09.2 +- add fix_X509TrustManagerImpl_symantec_distrust.patch + +* Wed Apr 27 2022 kuenking111 - 1:1.8.0.332-b09.1 +- add Fix-compile-and-runtime-failures-for-minimal1-versio.patch + +* Wed Apr 27 2022 kuenking111 - 1:1.8.0.332-b09.0 +- deleted Support-Git-commit-ID-in-the-SOURCE-field-of-the-release.patch +- deleted 8167014-jdeps-failed-with-Missing-message-warn-skippen-entry.patch +- deleted fix-wrong-commitID-in-release-file.patch +- deleted recreate-.java_pid-file-when-deleted-for-attach-mechanism.patch +- modified update-cacerts-and-VerifyCACerts.java-test.patch +- modified 8194154.patch +- modified add-missing-test-case.patch +- add fix-make-bugs-when-git-and-hg-not-exist.patch + * Wed Mar 2 2022 kuenking111 - 1:1.8.0.322-b06.4 - add 8268819-SA-Remove-libthread_db-dependency-on-Linux.patch -* Thu Mar 1 2022 kuenking111 - 1:1.8.0.322-b06.3 +* Tue Mar 1 2022 kuenking111 - 1:1.8.0.322-b06.3 - modified 8233280-Remove-GCLockerInvokesConcurrent-relative-logic-for-G1.patch * Wed Feb 16 2022 kuenking111 - 1:1.8.0.322-b06.2 @@ -2262,6 +2535,8 @@ require "copy_jdk_configs.lua" - deleted Delete-expired-certificate-globalsignr2ca.patch - deleted inline-optimize-for-aarch64.patch +* Tue Jan 05 2021 noah - 1:1.8.0.312-b07.11 +- adapted to newst cjc to fix issue with rpm 4.17 * Tue Dec 21 2021 kuenking111 - 1:1.8.0.312-b07.10 - delete stack protection @@ -2293,7 +2568,7 @@ require "copy_jdk_configs.lua" * Tue Nov 23 2021 lijingwei - 1:1.8.0.312-b07.1 - correct spec file release number typo -* Mon Nov 11 2021 kuenking111 - 1:1.8.0.312-b07.0 +* Mon Nov 1 2021 kuenking111 - 1:1.8.0.312-b07.0 - update to 8u312-b07(ga) - delete 8194246.patch - delete 8214418-half-closed-SSLEngine-status-may-cause-appli.patch @@ -2349,10 +2624,10 @@ require "copy_jdk_configs.lua" - delete fix-crash-in-JVMTI-debug.patch - other adaptations to jdk8u302 -* Thu Jul 12 2021 noah - 1:1.8.0.292-b10.19 +* Mon Jul 12 2021 noah - 1:1.8.0.292-b10.19 - add Fix-RSACipher-memory-usage.patch -* Thu Jul 12 2021 kuenking111 - 1:1.8.0.292-b10.18 +* Mon Jul 12 2021 kuenking111 - 1:1.8.0.292-b10.18 - fix run SPECjvm2008 failed on 32 bit system * Thu Jul 8 2021 noah - 1:1.8.0.292-b10.17 @@ -2376,7 +2651,7 @@ require "copy_jdk_configs.lua" * Sat Jun 12 2021 kuenking111 - 1:1.8.0.292-b10.11 - add g1gc-numa-aware-Implementation.patch -* Wed Jun 10 2021 hu_bo_dao - 1:1.8.0.292-b10.10 +* Fri Jun 11 2021 hu_bo_dao - 1:1.8.0.292-b10.10 - add support_CMS_parallel_inspection.patch * Wed Jun 9 2021 noah - 1:1.8.0.292-b10.9 @@ -2397,13 +2672,13 @@ require "copy_jdk_configs.lua" * Thu May 27 2021 kuenking111 - 1:1.8.0.292-b10.4 - add 8264640.patch -* Fri May 20 2021 kuenking111 - 1:1.8.0.292-b10.3 +* Fri May 21 2021 kuenking111 - 1:1.8.0.292-b10.3 - add 8266929_huawei_add_oid_mapping_common_sig_types.patch -* Fri May 20 2021 kuenking111 - 1:1.8.0.292-b10.2 +* Fri May 21 2021 kuenking111 - 1:1.8.0.292-b10.2 - add 8266187_Memory_leak_in_appendBootClassPath.patch -* Fri May 20 2021 kuenking111 - 1:1.8.0.292-b10.1 +* Fri May 21 2021 kuenking111 - 1:1.8.0.292-b10.1 - add 8247691_incorrect_handling_of_VM_exceptions_in_C1_deopt_stub.patch * Tue May 18 2021 eapen - 1:1.8.0.292-b10.0 diff --git a/policytool.desktop.in b/policytool.desktop.in index 5f4cb4a86df11309657200a234083c29361a1a20..e3ab2f1dd878985e55af4c2566ce49e635cdcb6b 100644 --- a/policytool.desktop.in +++ b/policytool.desktop.in @@ -1,6 +1,8 @@ [Desktop Entry] Name=OpenJDK @JAVA_VER@ for @target_cpu@ Policy Tool (@OPENJDK_VER@) +Name[zh_CN]=OpenJDK策略工具 Comment=Manage OpenJDK policy files +Comment[zh_CN]=管理OpenJDK策略文件 Exec=_JREBINDIR_/policytool Icon=java-@JAVA_VER@-@JAVA_VENDOR@ Terminal=false diff --git a/remove-gnu-debuglink-when-using-enable-debug-.patch b/remove-gnu-debuglink-when-using-enable-debug-.patch deleted file mode 100644 index 8d7ff6eb74a3d74bffdb2c935cb3df117a0f1d75..0000000000000000000000000000000000000000 --- a/remove-gnu-debuglink-when-using-enable-debug-.patch +++ /dev/null @@ -1,68 +0,0 @@ -From 39774b66e6b962a89a02504f08c20b309f9eef1f Mon Sep 17 00:00:00 2001 -From: zhangyipeng -Date: Thu, 4 Mar 2021 10:10:30 +0800 -Subject: [PATCH] [Huawei]remove gnu debuglink when using enable debug - symbols - - - -Signed-off-by: Sun Jianye ---- - hotspot/make/linux/makefiles/jsig.make | 1 - - hotspot/make/linux/makefiles/saproc.make | 1 - - hotspot/make/linux/makefiles/vm.make | 1 - - make/common/NativeCompilation.gmk | 2 +- - 4 files changed, 1 insertion(+), 4 deletions(-) - -diff --git a/hotspot/make/linux/makefiles/jsig.make b/hotspot/make/linux/makefiles/jsig.make -index 6290db5af..9838a50aa 100644 ---- a/hotspot/make/linux/makefiles/jsig.make -+++ b/hotspot/make/linux/makefiles/jsig.make -@@ -63,7 +63,6 @@ $(LIBJSIG): $(JSIGSRCDIR)/jsig.c $(LIBJSIG_MAPFILE) - ifeq ($(ENABLE_FULL_DEBUG_SYMBOLS),1) - ifneq ($(STRIP_POLICY),no_strip) - $(QUIETLY) $(OBJCOPY) --only-keep-debug $@ $(LIBJSIG_DEBUGINFO) -- $(QUIETLY) $(OBJCOPY) --add-gnu-debuglink=$(LIBJSIG_DEBUGINFO) $@ - endif - ifeq ($(STRIP_POLICY),all_strip) - $(QUIETLY) $(STRIP) $@ -diff --git a/hotspot/make/linux/makefiles/saproc.make b/hotspot/make/linux/makefiles/saproc.make -index ffc0ec5ce..dfeb254da 100644 ---- a/hotspot/make/linux/makefiles/saproc.make -+++ b/hotspot/make/linux/makefiles/saproc.make -@@ -107,7 +107,6 @@ $(LIBSAPROC): $(SASRCFILES) $(SAMAPFILE) - ifeq ($(ENABLE_FULL_DEBUG_SYMBOLS),1) - ifneq ($(STRIP_POLICY),no_strip) - $(QUIETLY) $(OBJCOPY) --only-keep-debug $@ $(LIBSAPROC_DEBUGINFO) -- $(QUIETLY) $(OBJCOPY) --add-gnu-debuglink=$(LIBSAPROC_DEBUGINFO) $@ - endif - ifeq ($(STRIP_POLICY),all_strip) - $(QUIETLY) $(STRIP) $@ -diff --git a/hotspot/make/linux/makefiles/vm.make b/hotspot/make/linux/makefiles/vm.make -index 1985db071..408b0cc9d 100644 ---- a/hotspot/make/linux/makefiles/vm.make -+++ b/hotspot/make/linux/makefiles/vm.make -@@ -359,7 +359,6 @@ $(LIBJVM): $(LIBJVM.o) $(LIBJVM_MAPFILE) $(LD_SCRIPT) - ifeq ($(ENABLE_FULL_DEBUG_SYMBOLS),1) - ifneq ($(STRIP_POLICY),no_strip) - $(QUIETLY) $(OBJCOPY) --only-keep-debug $@ $(LIBJVM_DEBUGINFO) -- $(QUIETLY) $(OBJCOPY) --add-gnu-debuglink=$(LIBJVM_DEBUGINFO) $@ - endif - ifeq ($(STRIP_POLICY),all_strip) - $(QUIETLY) $(STRIP) $@ -diff --git a/make/common/NativeCompilation.gmk b/make/common/NativeCompilation.gmk -index 9980e8ab9..4fa9f14cf 100644 ---- a/make/common/NativeCompilation.gmk -+++ b/make/common/NativeCompilation.gmk -@@ -487,7 +487,7 @@ define SetupNativeCompilation - $$($1_DEBUGINFO_FILES): $$($1_TARGET) - $(RM) $$@ - $(OBJCOPY) --only-keep-debug $$< $$@ -- $(CD) $$(@D) && $(OBJCOPY) --add-gnu-debuglink=$$(@F) $$< -+ $(CD) $$(@D) - $(TOUCH) $$@ - endif - else ifeq ($(OPENJDK_TARGET_OS), aix) --- -2.19.0 - diff --git a/revert-fPIC-and-security-compilation-flag-on.patch b/revert-fPIC-and-security-compilation-flag-on.patch new file mode 100644 index 0000000000000000000000000000000000000000..a785c84154e7daba6a202a27a2e6f57dae0cc68f --- /dev/null +++ b/revert-fPIC-and-security-compilation-flag-on.patch @@ -0,0 +1,60 @@ +From 9ffc530e0d34086e68c87306ca0410e5847812d6 Mon Sep 17 00:00:00 2001 +Date: Wed, 21 Sep 2022 09:53:14 +0800 +Subject: revert fPIC and security compilation flag on + +--- + common/autoconf/flags.m4 | 6 +----- + common/autoconf/generated-configure.sh | 6 +----- + hotspot/make/pic.make | 2 +- + 3 files changed, 3 insertions(+), 11 deletions(-) + +diff --git a/common/autoconf/flags.m4 b/common/autoconf/flags.m4 +index 69bea78d..71703a15 100644 +--- a/common/autoconf/flags.m4 ++++ b/common/autoconf/flags.m4 +@@ -807,11 +807,7 @@ AC_DEFUN_ONCE([FLAGS_SETUP_COMPILER_FLAGS_FOR_JDK], + LDFLAGS_JDKEXE="$LDFLAGS_JDKEXE -Xlinker --allow-shlib-undefined" + fi + if test "x$TOOLCHAIN_TYPE" = xgcc; then +- # Enabling pie on 32 bit builds prevents the JVM from allocating a continuous +- # java heap. +- if test "x$OPENJDK_TARGET_CPU_BITS" != "x32"; then +- LDFLAGS_JDKEXE="$LDFLAGS_JDKEXE -pie" +- fi ++ LDFLAGS_JDKEXE="$LDFLAGS_JDKEXE -pie" + fi + fi + AC_SUBST(LDFLAGS_JDKLIB) +diff --git a/common/autoconf/generated-configure.sh b/common/autoconf/generated-configure.sh +index f0e49f50..53e6cf18 100644 +--- a/common/autoconf/generated-configure.sh ++++ b/common/autoconf/generated-configure.sh +@@ -43068,11 +43068,7 @@ $as_echo "$supports" >&6; } + LDFLAGS_JDKEXE="$LDFLAGS_JDKEXE -Xlinker --allow-shlib-undefined" + fi + if test "x$TOOLCHAIN_TYPE" = xgcc; then +- # Enabling pie on 32 bit builds prevents the JVM from allocating a continuous +- # java heap. +- if test "x$OPENJDK_TARGET_CPU_BITS" != "x32"; then +- LDFLAGS_JDKEXE="$LDFLAGS_JDKEXE -pie" +- fi ++ LDFLAGS_JDKEXE="$LDFLAGS_JDKEXE -pie" + fi + fi + +diff --git a/hotspot/make/pic.make b/hotspot/make/pic.make +index 0e61ad93..3d85546c 100644 +--- a/hotspot/make/pic.make ++++ b/hotspot/make/pic.make +@@ -30,7 +30,7 @@ include $(GAMMADIR)/make/scm.make + + ifneq ($(OSNAME), windows) + ifndef LP64 +- PARTIAL_NONPIC=1 ++ PARTIAL_NONPIC=0 + endif + PIC_ARCH = ppc arm + ifneq ("$(filter $(PIC_ARCH),$(BUILDARCH))","") +-- +2.22.0 + diff --git a/support-numactl-for-hadoop-yarn.patch b/support-numactl-for-hadoop-yarn.patch new file mode 100644 index 0000000000000000000000000000000000000000..80c7d61fa13f5eab38bce6fb487f306596605014 --- /dev/null +++ b/support-numactl-for-hadoop-yarn.patch @@ -0,0 +1,599 @@ +From db8bc872fb7a132cb3c2363dcb3a7aa8b0a5827e Mon Sep 17 00:00:00 2001 +From: eapen +Date: Mon, 25 Jul 2022 16:41:24 +0800 +Subject: [PATCH 26/33] I68TO2: support numactl for hadoop yarn +--- + hotspot/make/aix/makefiles/mapfile-vers-debug | 1 + + hotspot/make/aix/makefiles/mapfile-vers-product | 1 + + hotspot/make/linux/makefiles/mapfile-vers-debug | 1 + + hotspot/make/linux/makefiles/mapfile-vers-product | 1 + + hotspot/make/solaris/makefiles/mapfile-vers | 1 + + hotspot/make/windows/jvmexp.lcf | 1 + + hotspot/make/windows/jvmexp_g.lcf | 1 + + hotspot/make/windows/makefiles/vm.make | 1 + + hotspot/src/os/linux/vm/os_linux.cpp | 91 +++++++++++++++++++ + hotspot/src/os/linux/vm/os_linux.hpp | 51 +++++++++++ + hotspot/src/share/vm/prims/jni.cpp | 6 ++ + hotspot/src/share/vm/prims/jni.h | 3 + + hotspot/src/share/vm/runtime/globals.hpp | 13 +++ + .../runtime/containers/docker/CPUSetsReader.java | 9 ++ + .../runtime/containers/docker/TestNUMANodes.java | 102 +++++++++++++++++++++ + jdk/src/share/bin/java.c | 2 + + jdk/src/share/bin/java.h | 3 + + jdk/src/share/javavm/export/jni.h | 3 + + jdk/src/solaris/bin/java_md_solinux.c | 7 ++ + 19 files changed, 298 insertions(+) + create mode 100644 hotspot/test/runtime/containers/docker/TestNUMANodes.java + +diff --git a/hotspot/make/aix/makefiles/mapfile-vers-debug b/hotspot/make/aix/makefiles/mapfile-vers-debug +index 760f955..127794c 100644 +--- a/hotspot/make/aix/makefiles/mapfile-vers-debug ++++ b/hotspot/make/aix/makefiles/mapfile-vers-debug +@@ -28,6 +28,7 @@ SUNWprivate_1.1 { + global: + # JNI + JNI_CreateJavaVM; ++ JNI_SetCParam; + JNI_GetCreatedJavaVMs; + JNI_GetDefaultJavaVMInitArgs; + +diff --git a/hotspot/make/aix/makefiles/mapfile-vers-product b/hotspot/make/aix/makefiles/mapfile-vers-product +index e84b671..2bbfb32 100644 +--- a/hotspot/make/aix/makefiles/mapfile-vers-product ++++ b/hotspot/make/aix/makefiles/mapfile-vers-product +@@ -28,6 +28,7 @@ SUNWprivate_1.1 { + global: + # JNI + JNI_CreateJavaVM; ++ JNI_SetCParam; + JNI_GetCreatedJavaVMs; + JNI_GetDefaultJavaVMInitArgs; + +diff --git a/hotspot/make/linux/makefiles/mapfile-vers-debug b/hotspot/make/linux/makefiles/mapfile-vers-debug +index 48b4f9d..1ebe436 100644 +--- a/hotspot/make/linux/makefiles/mapfile-vers-debug ++++ b/hotspot/make/linux/makefiles/mapfile-vers-debug +@@ -28,6 +28,7 @@ SUNWprivate_1.1 { + global: + # JNI + JNI_CreateJavaVM; ++ JNI_SetCParam; + JNI_GetCreatedJavaVMs; + JNI_GetDefaultJavaVMInitArgs; + +diff --git a/hotspot/make/linux/makefiles/mapfile-vers-product b/hotspot/make/linux/makefiles/mapfile-vers-product +index d4100d7..75e5278 100644 +--- a/hotspot/make/linux/makefiles/mapfile-vers-product ++++ b/hotspot/make/linux/makefiles/mapfile-vers-product +@@ -28,6 +28,7 @@ SUNWprivate_1.1 { + global: + # JNI + JNI_CreateJavaVM; ++ JNI_SetCParam; + JNI_GetCreatedJavaVMs; + JNI_GetDefaultJavaVMInitArgs; + +diff --git a/hotspot/make/solaris/makefiles/mapfile-vers b/hotspot/make/solaris/makefiles/mapfile-vers +index 26288b7..41045dd 100644 +--- a/hotspot/make/solaris/makefiles/mapfile-vers ++++ b/hotspot/make/solaris/makefiles/mapfile-vers +@@ -28,6 +28,7 @@ SUNWprivate_1.1 { + global: + # JNI + JNI_CreateJavaVM; ++ JNI_SetCParam; + JNI_GetCreatedJavaVMs; + JNI_GetDefaultJavaVMInitArgs; + +diff --git a/hotspot/make/windows/jvmexp.lcf b/hotspot/make/windows/jvmexp.lcf +index 6489d02..4bba798 100644 +--- a/hotspot/make/windows/jvmexp.lcf ++++ b/hotspot/make/windows/jvmexp.lcf +@@ -1,5 +1,6 @@ + -export:JNI_GetDefaultJavaVMInitArgs + -export:JNI_CreateJavaVM ++-export:JNI_SetCParam; + -export:JNI_GetCreatedJavaVMs + + -export:jio_snprintf +diff --git a/hotspot/make/windows/jvmexp_g.lcf b/hotspot/make/windows/jvmexp_g.lcf +index 6489d02..4bba798 100644 +--- a/hotspot/make/windows/jvmexp_g.lcf ++++ b/hotspot/make/windows/jvmexp_g.lcf +@@ -1,5 +1,6 @@ + -export:JNI_GetDefaultJavaVMInitArgs + -export:JNI_CreateJavaVM ++-export:JNI_SetCParam; + -export:JNI_GetCreatedJavaVMs + + -export:jio_snprintf +diff --git a/hotspot/make/windows/makefiles/vm.make b/hotspot/make/windows/makefiles/vm.make +index 5322a4b..fd5e5d2 100644 +--- a/hotspot/make/windows/makefiles/vm.make ++++ b/hotspot/make/windows/makefiles/vm.make +@@ -86,6 +86,7 @@ AGCT_EXPORT=/export:AsyncGetCallTrace + LD_FLAGS=$(LD_FLAGS) $(STACK_SIZE) /subsystem:windows /dll /base:0x8000000 \ + /export:JNI_GetDefaultJavaVMInitArgs \ + /export:JNI_CreateJavaVM \ ++ /export:JNI_SetCParam \ + /export:JVM_FindClassFromBootLoader \ + /export:JNI_GetCreatedJavaVMs \ + /export:jio_snprintf \ +diff --git a/hotspot/src/os/linux/vm/os_linux.cpp b/hotspot/src/os/linux/vm/os_linux.cpp +index 1ec68ab..ab28ee3 100644 +--- a/hotspot/src/os/linux/vm/os_linux.cpp ++++ b/hotspot/src/os/linux/vm/os_linux.cpp +@@ -133,6 +133,8 @@ PRAGMA_FORMAT_MUTE_WARNINGS_FOR_GCC + #define LARGEPAGES_BIT (1 << 6) + //////////////////////////////////////////////////////////////////////////////// + // global variables ++extern char** argv_for_execvp; ++ + julong os::Linux::_physical_memory = 0; + + address os::Linux::_initial_thread_stack_bottom = NULL; +@@ -3129,6 +3131,77 @@ void* os::Linux::libnuma_v2_dlsym(void* handle, const char* name) { + return dlvsym(handle, name, "libnuma_1.2"); + } + ++void os::Linux::parse_numa_nodes() { ++ if (NUMANodes == NULL && NUMANodesRandom == 0) { ++ return; ++ } ++ const char* numa_nodes = NUMANodes; ++ // Max length for "%d-%d" is 24 ++ char buf[24] = {0}; ++ if (NUMANodesRandom != 0) { ++ int nodes_to_bind = NUMANodesRandom; ++ int nodes_num = Linux::numa_max_node() + 1; ++ const int MAX_NUMA = 1000000; ++ if (nodes_num > 0 && ++ nodes_num < MAX_NUMA && ++ nodes_to_bind > 0 && ++ nodes_to_bind < nodes_num) { ++ int bound = 1; ++ while (bound < nodes_to_bind) { ++ bound *= 2; ++ } ++ struct timeval tv; ++ gettimeofday(&tv,NULL); ++ srand(tv.tv_usec); ++ int first = 0; ++ if (nodes_num > bound) { ++ first = rand() % (nodes_num / bound) * bound; ++ } ++ if (bound != nodes_to_bind) { ++ first += rand() % (1 + bound - nodes_to_bind); ++ } ++ sprintf(buf, "%d-%d", first, first + nodes_to_bind - 1); ++ numa_nodes = buf; ++ if (LogNUMANodes) { ++ warning("NUMANodes is converted to %s, with total %d nodes!", buf, nodes_num); ++ } ++ } else { ++ if (LogNUMANodes) { ++ warning("The count of nodes to bind should be less that the count of all nodes, Skip!"); ++ } ++ return; ++ } ++ } ++ bitmask* mask = os::Linux::numa_parse_nodestring_all(numa_nodes); ++ if (!mask) { ++ if (LogNUMANodes) { ++ warning("<%s> is invalid", numa_nodes); ++ } ++ return; ++ } ++ if (os::Linux::numa_bitmask_equal(mask, os::Linux::_numa_membind_bitmask)) { ++ os::Linux::numa_bitmask_free(mask); ++ if (LogNUMANodes) { ++ warning("Mempolicy is not changed, param: %s", numa_nodes); ++ } ++ return; ++ } ++ errno = 0; ++ os::Linux::numa_run_on_node_mask(mask); ++ if (errno) { ++ perror("sched_setaffinity"); ++ } ++ errno = 0; ++ os::Linux::numa_set_membind(mask); ++ int errtmp = errno; ++ os::Linux::numa_bitmask_free(mask); ++ if (errtmp) { ++ perror("numa_set_membind"); ++ } else { ++ execvp(*argv_for_execvp, argv_for_execvp); ++ } ++} ++ + bool os::Linux::libnuma_init() { + // sched_getcpu() should be in libc. + set_sched_getcpu(CAST_TO_FN_PTR(sched_getcpu_func_t, +@@ -3169,6 +3242,16 @@ bool os::Linux::libnuma_init() { + libnuma_dlsym(handle, "numa_move_pages"))); + set_numa_run_on_node(CAST_TO_FN_PTR(numa_run_on_node_func_t, + libnuma_dlsym(handle, "numa_run_on_node"))); ++ set_numa_parse_nodestring_all(CAST_TO_FN_PTR(numa_parse_nodestring_all_func_t, ++ libnuma_dlsym(handle, "numa_parse_nodestring_all"))); ++ set_numa_run_on_node_mask(CAST_TO_FN_PTR(numa_run_on_node_mask_func_t, ++ libnuma_v2_dlsym(handle, "numa_run_on_node_mask"))); ++ set_numa_bitmask_equal(CAST_TO_FN_PTR(numa_bitmask_equal_func_t, ++ libnuma_v2_dlsym(handle, "numa_bitmask_equal"))); ++ set_numa_set_membind(CAST_TO_FN_PTR(numa_set_membind_func_t, ++ libnuma_v2_dlsym(handle, "numa_set_membind"))); ++ set_numa_bitmask_free(CAST_TO_FN_PTR(numa_bitmask_free_func_t, ++ libnuma_dlsym(handle, "numa_bitmask_free"))); + + if (numa_available() != -1) { + set_numa_all_nodes((unsigned long*)libnuma_dlsym(handle, "numa_all_nodes")); +@@ -3176,6 +3259,9 @@ bool os::Linux::libnuma_init() { + set_numa_nodes_ptr((struct bitmask **)libnuma_dlsym(handle, "numa_nodes_ptr")); + set_numa_interleave_bitmask(_numa_get_interleave_mask()); + set_numa_membind_bitmask(_numa_get_membind()); ++ if (isbound_to_all_node()) { ++ parse_numa_nodes(); ++ } + // Create an index -> node mapping, since nodes are not always consecutive + _nindex_to_node = new (ResourceObj::C_HEAP, mtInternal) GrowableArray(0, true); + rebuild_nindex_to_node_map(); +@@ -3295,6 +3381,11 @@ os::Linux::numa_get_membind_func_t os::Linux::_numa_get_membind; + os::Linux::numa_get_interleave_mask_func_t os::Linux::_numa_get_interleave_mask; + os::Linux::numa_move_pages_func_t os::Linux::_numa_move_pages; + os::Linux::numa_run_on_node_func_t os::Linux::_numa_run_on_node; ++os::Linux::numa_parse_nodestring_all_func_t os::Linux::_numa_parse_nodestring_all; ++os::Linux::numa_run_on_node_mask_func_t os::Linux::_numa_run_on_node_mask; ++os::Linux::numa_bitmask_equal_func_t os::Linux::_numa_bitmask_equal; ++os::Linux::numa_set_membind_func_t os::Linux::_numa_set_membind; ++os::Linux::numa_bitmask_free_func_t os::Linux::_numa_bitmask_free; + os::Linux::NumaAllocationPolicy os::Linux::_current_numa_policy; + unsigned long* os::Linux::_numa_all_nodes; + struct bitmask* os::Linux::_numa_all_nodes_ptr; +diff --git a/hotspot/src/os/linux/vm/os_linux.hpp b/hotspot/src/os/linux/vm/os_linux.hpp +index 4ee2c9b..18ac68f 100644 +--- a/hotspot/src/os/linux/vm/os_linux.hpp ++++ b/hotspot/src/os/linux/vm/os_linux.hpp +@@ -195,6 +195,7 @@ class Linux { + static bool is_floating_stack() { return _is_floating_stack; } + + static void libpthread_init(); ++ static void parse_numa_nodes(); + static bool libnuma_init(); + static void* libnuma_dlsym(void* handle, const char* name); + // libnuma v2 (libnuma_1.2) symbols +@@ -283,6 +284,11 @@ private: + typedef struct bitmask* (*numa_get_interleave_mask_func_t)(void); + typedef long (*numa_move_pages_func_t)(int pid, unsigned long count, void **pages, const int *nodes, int *status, int flags); + typedef int (*numa_run_on_node_func_t)(int node); ++ typedef struct bitmask* (*numa_parse_nodestring_all_func_t)(const char*); ++ typedef int (*numa_run_on_node_mask_func_t)(struct bitmask* mask); ++ typedef void (*numa_set_membind_func_t)(struct bitmask* mask); ++ typedef int (*numa_bitmask_equal_func_t)(struct bitmask* mask, struct bitmask* mask1); ++ typedef void (*numa_bitmask_free_func_t)(struct bitmask* mask); + + typedef void (*numa_set_bind_policy_func_t)(int policy); + typedef int (*numa_bitmask_isbitset_func_t)(struct bitmask *bmp, unsigned int n); +@@ -303,6 +309,11 @@ private: + static numa_get_interleave_mask_func_t _numa_get_interleave_mask; + static numa_move_pages_func_t _numa_move_pages; + static numa_run_on_node_func_t _numa_run_on_node; ++ static numa_parse_nodestring_all_func_t _numa_parse_nodestring_all; ++ static numa_run_on_node_mask_func_t _numa_run_on_node_mask; ++ static numa_bitmask_equal_func_t _numa_bitmask_equal; ++ static numa_set_membind_func_t _numa_set_membind; ++ static numa_bitmask_free_func_t _numa_bitmask_free; + + static unsigned long* _numa_all_nodes; + static struct bitmask* _numa_all_nodes_ptr; +@@ -325,6 +336,11 @@ private: + static void set_numa_get_interleave_mask(numa_get_interleave_mask_func_t func) { _numa_get_interleave_mask = func; } + static void set_numa_move_pages(numa_move_pages_func_t func) { _numa_move_pages = func; } + static void set_numa_run_on_node(numa_run_on_node_func_t func) { _numa_run_on_node = func; } ++ static void set_numa_parse_nodestring_all(numa_parse_nodestring_all_func_t func) { _numa_parse_nodestring_all = func; } ++ static void set_numa_run_on_node_mask(numa_run_on_node_mask_func_t func) { _numa_run_on_node_mask = func; } ++ static void set_numa_bitmask_equal(numa_bitmask_equal_func_t func) { _numa_bitmask_equal = func; } ++ static void set_numa_set_membind(numa_set_membind_func_t func) { _numa_set_membind = func; } ++ static void set_numa_bitmask_free(numa_bitmask_free_func_t func) { _numa_bitmask_free = func; } + static void set_numa_all_nodes(unsigned long* ptr) { _numa_all_nodes = ptr; } + static void set_numa_all_nodes_ptr(struct bitmask **ptr) { _numa_all_nodes_ptr = (ptr == NULL ? NULL : *ptr); } + static void set_numa_nodes_ptr(struct bitmask **ptr) { _numa_nodes_ptr = (ptr == NULL ? NULL : *ptr); } +@@ -472,6 +488,41 @@ public: + static mallinfo_retval_t get_mallinfo(glibc_mallinfo2* out); + #endif + ++ static bool isbound_to_all_node() { ++ if (_numa_membind_bitmask != NULL && _numa_max_node != NULL && _numa_bitmask_isbitset != NULL) { ++ unsigned int highest_node_number = _numa_max_node(); ++ for (unsigned int node = 0; node <= highest_node_number; node++) { ++ if (!_numa_bitmask_isbitset(_numa_membind_bitmask, node)) { ++ return false; ++ } ++ } ++ } ++ return true; ++ } ++ ++ static bitmask* numa_parse_nodestring_all(const char* s) { ++ return _numa_parse_nodestring_all != NULL ? _numa_parse_nodestring_all(s) : NULL; ++ } ++ ++ static int numa_run_on_node_mask(bitmask* bitmask) { ++ return _numa_run_on_node_mask != NULL ? _numa_run_on_node_mask(bitmask) : -1; ++ } ++ ++ static int numa_bitmask_equal(bitmask* bitmask, struct bitmask* bitmask1) { ++ return _numa_bitmask_equal != NULL ? _numa_bitmask_equal(bitmask, bitmask1) : 1; ++ } ++ ++ static void numa_set_membind(bitmask* bitmask) { ++ if (_numa_set_membind != NULL) { ++ _numa_set_membind(bitmask); ++ } ++ } ++ ++ static void numa_bitmask_free(bitmask* bitmask) { ++ if (_numa_bitmask_free != NULL) { ++ _numa_bitmask_free(bitmask); ++ } ++ } + }; + + +diff --git a/hotspot/src/share/vm/prims/jni.cpp b/hotspot/src/share/vm/prims/jni.cpp +index dde3975..dd40c2c 100644 +--- a/hotspot/src/share/vm/prims/jni.cpp ++++ b/hotspot/src/share/vm/prims/jni.cpp +@@ -5188,6 +5188,12 @@ DT_RETURN_MARK_DECL(CreateJavaVM, jint + , HOTSPOT_JNI_CREATEJAVAVM_RETURN(_ret_ref)); + #endif /* USDT2 */ + ++const char** argv_for_execvp; ++ ++_JNI_IMPORT_OR_EXPORT_ void JNICALL JNI_SetCParam(char** raw_argv) { ++ argv_for_execvp = (const char**)raw_argv; ++} ++ + _JNI_IMPORT_OR_EXPORT_ jint JNICALL JNI_CreateJavaVM(JavaVM **vm, void **penv, void *args) { + #ifndef USDT2 + HS_DTRACE_PROBE3(hotspot_jni, CreateJavaVM__entry, vm, penv, args); +diff --git a/hotspot/src/share/vm/prims/jni.h b/hotspot/src/share/vm/prims/jni.h +index 582f2c9..1c910e8 100644 +--- a/hotspot/src/share/vm/prims/jni.h ++++ b/hotspot/src/share/vm/prims/jni.h +@@ -1937,6 +1937,9 @@ JNI_GetDefaultJavaVMInitArgs(void *args); + _JNI_IMPORT_OR_EXPORT_ jint JNICALL + JNI_CreateJavaVM(JavaVM **pvm, void **penv, void *args); + ++_JNI_IMPORT_OR_EXPORT_ void JNICALL ++JNI_SetCParam(char**raw_argv); ++ + _JNI_IMPORT_OR_EXPORT_ jint JNICALL + JNI_GetCreatedJavaVMs(JavaVM **, jsize, jsize *); + +diff --git a/hotspot/src/share/vm/runtime/globals.hpp b/hotspot/src/share/vm/runtime/globals.hpp +index 2631971..3dd4c51 100644 +--- a/hotspot/src/share/vm/runtime/globals.hpp ++++ b/hotspot/src/share/vm/runtime/globals.hpp +@@ -613,6 +613,19 @@ class CommandLineFlags { + product(uintx, NUMAPageScanRate, 256, \ + "Maximum number of pages to include in the page scan procedure") \ + \ ++ product(bool, LogNUMANodes, false, \ ++ "Print NUMANodes") \ ++ \ ++ product(ccstr, NUMANodes, NULL, \ ++ "This parameter provides the same functionality as" \ ++ "'numactl --all -N -m '." \ ++ " can be '0-2', '0,1,2', 'all' and so on.") \ ++ \ ++ product(uintx, NUMANodesRandom, 0, \ ++ "Number of continuous nodes to bind" \ ++ "with the first node randomly chosen." \ ++ "NUMANodesRandom has higher priority than NUMANodes") \ ++ \ + product_pd(bool, NeedsDeoptSuspend, \ + "True for register window machines (sparc/ia64)") \ + \ +diff --git a/hotspot/test/runtime/containers/docker/CPUSetsReader.java b/hotspot/test/runtime/containers/docker/CPUSetsReader.java +index f6fa93e..cb8ced2 100644 +--- a/hotspot/test/runtime/containers/docker/CPUSetsReader.java ++++ b/hotspot/test/runtime/containers/docker/CPUSetsReader.java +@@ -51,6 +51,15 @@ public class CPUSetsReader { + Asserts.assertEquals(listToString(parseCpuSet(cpuSet)), expectedResult); + } + ++ public static int getNumCpus() { ++ String path = "/proc/cpuinfo"; ++ try { ++ Stream stream = Files.lines(Paths.get(path)); ++ return (int) stream.filter(line -> line.startsWith("processor")).count(); ++ } catch (IOException e) { ++ return 0; ++ } ++ } + + public static String readFromProcStatus(String setType) { + String path = PROC_SELF_STATUS_PATH; +diff --git a/hotspot/test/runtime/containers/docker/TestNUMANodes.java b/hotspot/test/runtime/containers/docker/TestNUMANodes.java +new file mode 100644 +index 0000000..b781484 +--- /dev/null ++++ b/hotspot/test/runtime/containers/docker/TestNUMANodes.java +@@ -0,0 +1,102 @@ ++/** ++ * @test TestNUMANodes.java ++ * @library /testlibrary ++ * @build CPUSetsReader TestNUMANodes ++ * @run main/othervm TestNUMANodes 1 -XX:+UseNUMA -XX:NUMANodes=0 -XX:-LogNUMANodes ++ * @run main/othervm TestNUMANodes 2 -XX:+UseNUMA -XX:NUMANodes=all -XX:+LogNUMANodes ++ * @run main/othervm TestNUMANodes 3 -XX:+UseNUMA -XX:NUMANodesRandom=1 -XX:+LogNUMANodes ++ * @run main/othervm TestNUMANodes 4 -XX:+UseNUMA -XX:NUMANodesRandom=4 -XX:+LogNUMANodes ++ * @run main/othervm TestNUMANodes 5 -XX:+UseNUMA -XX:NUMANodes=100-200 -XX:+LogNUMANodes ++ * @summary test numanodes ++ * @author zhoulei ++ */ ++ ++import java.io.IOException; ++import java.nio.file.Files; ++import java.nio.file.Paths; ++import java.util.ArrayList; ++import java.util.Arrays; ++import java.util.List; ++import java.util.Optional; ++import java.util.stream.Collectors; ++import java.util.stream.IntStream; ++import java.util.stream.Stream; ++import com.oracle.java.testlibrary.Asserts; ++import com.oracle.java.testlibrary.OutputAnalyzer; ++import com.oracle.java.testlibrary.ProcessTools; ++ ++public class TestNUMANodes { ++ ++ private static int getNUMAs() throws Exception { ++ final String[] arguments = {"numactl", "-H"}; ++ OutputAnalyzer output = ProcessTools.executeProcess(new ProcessBuilder(arguments)); ++ String[] numainfo = output.getStdout().split("\n"); ++ Optional o = Arrays.asList(numainfo).stream() ++ .filter(line -> line.contains("available")) ++ .findFirst(); ++ String numas = o.get(); ++ return Integer.valueOf(numas.substring(11, 12)); ++ } ++ ++ private static class ExeTest { ++ public static void main(String[] str) throws Exception { ++ int numCpus = CPUSetsReader.getNumCpus(); ++ String cpuSetStr = CPUSetsReader.readFromProcStatus("Cpus_allowed_list"); ++ String[] cpus = cpuSetStr.split(","); ++ int total = 0; ++ for (String cpu : cpus) { ++ String[] c = cpu.split("-"); ++ int start = Integer.valueOf(c[0]); ++ int end = Integer.valueOf(c[1]); ++ total += end - start + 1; ++ } ++ System.err.print(total); ++ } ++ } ++ ++ private static OutputAnalyzer forkProcess(String[] args) throws Exception { ++ final String[] arguments = { ++ args[1], ++ args[2], ++ args[3], ++ ExeTest.class.getName(), ++ args[0] ++ }; ++ ++ ProcessBuilder pb = ProcessTools.createJavaProcessBuilder(arguments); ++ OutputAnalyzer output = new OutputAnalyzer(pb.start()); ++ output.shouldHaveExitValue(0); ++ return output; ++ } ++ ++ public static void main(String[] args)throws Exception { ++ OutputAnalyzer output = forkProcess(args); ++ String err = output.getStderr(); ++ String out = output.getStdout(); ++ int c = Integer.parseInt(args[0]); ++ int numas = TestNUMANodes.getNUMAs(); ++ int numCpus = CPUSetsReader.getNumCpus(); ++ switch(c) { ++ case 1: ++ int cpuUsed = Integer.valueOf(err); ++ Asserts.assertTrue(cpuUsed * numas == numCpus); ++ break; ++ case 2: ++ Asserts.assertTrue(err.contains("Mempolicy is not changed")); ++ break; ++ case 3: ++ if (numas > 1) { ++ Asserts.assertTrue(err.contains("NUMANodes is converted to")); ++ } ++ break; ++ case 4: ++ Asserts.assertTrue(err.contains("The count of nodes to bind should be")); ++ break; ++ case 5: ++ Asserts.assertTrue(err.contains("is invalid")); ++ break; ++ default: ++ break; ++ } ++ } ++} +diff --git a/jdk/src/share/bin/java.c b/jdk/src/share/bin/java.c +index d74b185..c3d3b1b 100644 +--- a/jdk/src/share/bin/java.c ++++ b/jdk/src/share/bin/java.c +@@ -245,6 +245,7 @@ JLI_Launch(int argc, char ** argv, /* main argc, argc */ + + ifn.CreateJavaVM = 0; + ifn.GetDefaultJavaVMInitArgs = 0; ++ ifn.raw_argv = argv; + + if (JLI_IsTraceLauncher()) { + start = CounterGet(); +@@ -1237,6 +1238,7 @@ InitializeJVM(JavaVM **pvm, JNIEnv **penv, InvocationFunctions *ifn) + i, args.options[i].optionString); + } + ++ ifn->SetCParam(ifn->raw_argv); + r = ifn->CreateJavaVM(pvm, (void **)penv, &args); + JLI_MemFree(options); + return r == JNI_OK; +diff --git a/jdk/src/share/bin/java.h b/jdk/src/share/bin/java.h +index 9dc0e16..9a8b839 100644 +--- a/jdk/src/share/bin/java.h ++++ b/jdk/src/share/bin/java.h +@@ -78,13 +78,16 @@ + * Pointers to the needed JNI invocation API, initialized by LoadJavaVM. + */ + typedef jint (JNICALL *CreateJavaVM_t)(JavaVM **pvm, void **env, void *args); ++typedef void (JNICALL *SetCParam_t)(char** raw_argv); + typedef jint (JNICALL *GetDefaultJavaVMInitArgs_t)(void *args); + typedef jint (JNICALL *GetCreatedJavaVMs_t)(JavaVM **vmBuf, jsize bufLen, jsize *nVMs); + + typedef struct { + CreateJavaVM_t CreateJavaVM; ++ SetCParam_t SetCParam; + GetDefaultJavaVMInitArgs_t GetDefaultJavaVMInitArgs; + GetCreatedJavaVMs_t GetCreatedJavaVMs; ++ char** raw_argv; + } InvocationFunctions; + + int +diff --git a/jdk/src/share/javavm/export/jni.h b/jdk/src/share/javavm/export/jni.h +index 2e83cb7..8567766 100644 +--- a/jdk/src/share/javavm/export/jni.h ++++ b/jdk/src/share/javavm/export/jni.h +@@ -1937,6 +1937,9 @@ JNI_GetDefaultJavaVMInitArgs(void *args); + _JNI_IMPORT_OR_EXPORT_ jint JNICALL + JNI_CreateJavaVM(JavaVM **pvm, void **penv, void *args); + ++_JNI_IMPORT_OR_EXPORT_ void JNICALL ++JNI_SetCParam(char** raw_argv); ++ + _JNI_IMPORT_OR_EXPORT_ jint JNICALL + JNI_GetCreatedJavaVMs(JavaVM **, jsize, jsize *); + +diff --git a/jdk/src/solaris/bin/java_md_solinux.c b/jdk/src/solaris/bin/java_md_solinux.c +index a967137..9865f9d 100644 +--- a/jdk/src/solaris/bin/java_md_solinux.c ++++ b/jdk/src/solaris/bin/java_md_solinux.c +@@ -903,6 +903,13 @@ LoadJavaVM(const char *jvmpath, InvocationFunctions *ifn) + return JNI_FALSE; + } + ++ ifn->SetCParam = (SetCParam_t) ++ dlsym(libjvm, "JNI_SetCParam"); ++ if (ifn->SetCParam == NULL) { ++ JLI_ReportErrorMessage(DLL_ERROR2, jvmpath, dlerror()); ++ return JNI_FALSE; ++ } ++ + ifn->GetDefaultJavaVMInitArgs = (GetDefaultJavaVMInitArgs_t) + dlsym(libjvm, "JNI_GetDefaultJavaVMInitArgs"); + if (ifn->GetDefaultJavaVMInitArgs == NULL) { +-- +1.8.3.1 diff --git a/update-cacerts-and-VerifyCACerts.java-test.patch b/update-cacerts-and-VerifyCACerts.java-test.patch index e98895573d7f24ac1232bb22c368420624372274..424d2f552982a1c075673ec11a2466a51f457c0e 100644 --- a/update-cacerts-and-VerifyCACerts.java-test.patch +++ b/update-cacerts-and-VerifyCACerts.java-test.patch @@ -3,6 +3,8 @@ From: zhangyipeng Date: Tue, 20 Apr 2021 10:40:35 +0800 Subject: [PATCH] [Huawei]update cacerts and VerifyCACerts.java test +Offering: Cloud Compiler JDK + Signed-off-by: Wang Kun --- jdk/make/data/cacerts/addtrustexternalca | 32 ----------------- @@ -10,6 +12,7 @@ Signed-off-by: Wang Kun jdk/make/data/cacerts/luxtrustglobalrootca | 28 --------------- jdk/make/data/cacerts/quovadisrootca | 41 ---------------------- jdk/make/data/cacerts/utnuserfirstobjectca | 33 ----------------- + jdk/make/data/cacerts/geotrustglobalca | 27 ------------------- .../sun/security/lib/cacerts/VerifyCACerts.java | 29 ++------------------- 8 files changed, 3 insertions(+), 192 deletions(-) delete mode 100644 jdk/make/data/cacerts/addtrustexternalca @@ -19,6 +22,7 @@ Signed-off-by: Wang Kun delete mode 100644 jdk/make/data/cacerts/thawtepremiumserverca delete mode 100644 jdk/make/data/cacerts/utnuserfirstobjectca delete mode 100644 jdk/make/data/cacerts/verisigntsaca + delete mode 100644 jdk/make/data/cacerts/geotrustglobalca diff --git a/jdk/make/data/cacerts/addtrustexternalca b/jdk/make/data/cacerts/addtrustexternalca deleted file mode 100644 @@ -216,6 +220,39 @@ index 80a0b5c..0000000 -81OtbLUrohKqGU8J2l7nk8aOFAj+8DCAGKCGhU3IfdeLA/5u1fedFqySLKAj5ZyR -Uh+U3xeUc8OzwcFxBSAAeL0TUh2oPs0AH8g= ------END CERTIFICATE----- +diff --git a/jdk/make/data/cacerts/geotrustglobalca b/jdk/make/data/cacerts/geotrustglobalca +deleted file mode 100644 +index 7f8bf9a6..00000000 +--- a/jdk/make/data/cacerts/geotrustglobalca ++++ /dev/null +@@ -1,27 +0,0 @@ +-Owner: CN=GeoTrust Global CA, O=GeoTrust Inc., C=US +-Issuer: CN=GeoTrust Global CA, O=GeoTrust Inc., C=US +-Serial number: 23456 +-Valid from: Tue May 21 04:00:00 GMT 2002 until: Sat May 21 04:00:00 GMT 2022 +-Signature algorithm name: SHA1withRSA +-Subject Public Key Algorithm: 2048-bit RSA key +-Version: 3 +------BEGIN CERTIFICATE----- +-MIIDVDCCAjygAwIBAgIDAjRWMA0GCSqGSIb3DQEBBQUAMEIxCzAJBgNVBAYTAlVT +-MRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMRswGQYDVQQDExJHZW9UcnVzdCBHbG9i +-YWwgQ0EwHhcNMDIwNTIxMDQwMDAwWhcNMjIwNTIxMDQwMDAwWjBCMQswCQYDVQQG +-EwJVUzEWMBQGA1UEChMNR2VvVHJ1c3QgSW5jLjEbMBkGA1UEAxMSR2VvVHJ1c3Qg +-R2xvYmFsIENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA2swYYzD9 +-9BcjGlZ+W988bDjkcbd4kdS8odhM+KhDtgPpTSEHCIjaWC9mOSm9BXiLnTjoBbdq +-fnGk5sRgprDvgOSJKA+eJdbtg/OtppHHmMlCGDUUna2YRpIuT8rxh0PBFpVXLVDv +-iS2Aelet8u5fa9IAjbkU+BQVNdnARqN7csiRv8lVK83Qlz6cJmTM386DGXHKTubU +-1XupGc1V3sjs0l44U+VcT4wt/lAjNvxm5suOpDkZALeVAjmRCw7+OC7RHQWa9k0+ +-bw8HHa8sHo9gOeL6NlMTOdReJivbPagUvTLrGAMoUgRx5aszPeE4uwc2hGKceeoW +-MPRfwCvocWvk+QIDAQABo1MwUTAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBTA +-ephojYn7qwVkDBF9qn1luMrMTjAfBgNVHSMEGDAWgBTAephojYn7qwVkDBF9qn1l +-uMrMTjANBgkqhkiG9w0BAQUFAAOCAQEANeMpauUvXVSOKVCUn5kaFOSPeCpilKIn +-Z57QzxpeR+nBsqTP3UEaBU6bS+5Kb1VSsyShNwrrZHYqLizz/Tt1kL/6cdjHPTfS +-tQWVYrmm3ok9Nns4d0iXrKYgjy6myQzCsplFAMfOEVEiIuCl6rYVSAlk6l5PdPcF +-PseKUgzbFbS9bZvlxrFUaKnjaZC2mqUPuLk/IH2uSrW4nOQdtqvmlKXBx4Ot2/Un +-hw4EbNX/3aBd7YdStysVAq45pmp06drE57xNNB6pXE0zX5IJL4hmXXeXxx12E6nV +-5fEWCRE11azbJHFwLJhWC9kXtNHjUStedejV0NxPNO3CBWaAocvmMw== +------END CERTIFICATE----- diff --git a/jdk/test/sun/security/lib/cacerts/VerifyCACerts.java b/jdk/test/sun/security/lib/cacerts/VerifyCACerts.java index dd107fc..791ddb6 100644 --- a/jdk/test/sun/security/lib/cacerts/VerifyCACerts.java @@ -225,13 +262,13 @@ index dd107fc..791ddb6 100644 // The numbers of certs now. - private static final int COUNT = 89; -+ private static final int COUNT = 84; ++ private static final int COUNT = 83; // SHA-256 of cacerts, can be generated with // shasum -a 256 cacerts | sed -e 's/../&:/g' | tr '[:lower:]' '[:upper:]' | cut -c1-95 private static final String CHECKSUM - = "CC:AD:BB:49:70:97:3F:42:AD:73:91:A0:A2:C4:B8:AA:D1:95:59:F3:B3:22:09:2A:1F:2C:AB:04:47:08:EF:AA"; -+ = "D3:05:21:64:FA:D7:CD:29:E8:CB:57:E7:47:ED:79:9B:47:D8:0E:75:2D:CA:83:BB:86:AF:D9:43:FD:3E:17:85"; ++ = "2D:04:88:6C:52:53:54:EB:38:2D:BC:E0:AF:B7:82:F4:9E:32:A8:1A:1B:A3:AE:CF:25:CB:C2:F6:0F:4E:E1:20"; // map of cert alias to SHA-256 fingerprint @SuppressWarnings("serial") @@ -248,6 +285,15 @@ index dd107fc..791ddb6 100644 put("baltimorecybertrustca [jdk]", "16:AF:57:A9:F6:76:B0:AB:12:60:95:AA:5E:BA:DE:F2:2A:B3:11:19:D6:44:AC:95:CD:4B:93:DB:F3:F2:6A:EB"); put("digicertglobalrootca [jdk]", +@@ -111,8 +111,6 @@ public class VerifyCACerts { + "7E:37:CB:8B:4C:47:09:0C:AB:36:55:1B:A6:F4:5D:B8:40:68:0F:BA:16:6A:95:2D:B1:00:71:7F:43:05:3F:C2"); + put("digicerthighassuranceevrootca [jdk]", + "74:31:E5:F4:C3:C1:CE:46:90:77:4F:0B:61:E0:54:40:88:3B:A9:A0:1E:D0:0B:A6:AB:D7:80:6E:D3:B1:18:CF"); +- put("geotrustglobalca [jdk]", +- "FF:85:6A:2D:25:1D:CD:88:D3:66:56:F4:50:12:67:98:CF:AB:AA:DE:40:79:9C:72:2D:E4:D2:B5:DB:36:A7:3A"); + put("geotrustprimaryca [jdk]", + "37:D5:10:06:C5:12:EA:AB:62:64:21:F1:EC:8C:92:01:3F:C5:F8:2A:E9:8E:E5:33:EB:46:19:B8:DE:B4:D0:6C"); + put("geotrustprimarycag2 [jdk]", @@ -163,10 +147,6 @@ public class VerifyCACerts { "5D:56:49:9B:E4:D2:E0:8B:CF:CA:D0:8A:3E:38:72:3D:50:50:3B:DE:70:69:48:E4:2F:55:60:30:19:E5:28:AE"); put("letsencryptisrgx1 [jdk]", @@ -259,7 +305,7 @@ index dd107fc..791ddb6 100644 put("quovadisrootca1g3 [jdk]", "8A:86:6F:D1:B2:76:B5:7E:57:8E:92:1C:65:82:8A:2B:ED:58:E9:F2:F2:88:05:41:34:B7:F1:F4:BF:C9:CC:74"); put("quovadisrootca2 [jdk]", -@@ -267,20 +247,7 @@ public class VerifyCACerts { +@@ -267,22 +247,7 @@ public class VerifyCACerts { // Exception list to 90 days expiry policy // No error will be reported if certificate in this list expires @SuppressWarnings("serial") @@ -275,6 +321,8 @@ index dd107fc..791ddb6 100644 - add("luxtrustglobalrootca [jdk]"); - // Valid until: Wed Mar 17 11:33:33 PDT 2021 - add("quovadisrootca [jdk]"); +- // Valid until: Sat May 21 04:00:00 GMT 2022 +- add("geotrustglobalca [jdk]"); - } - }; + private static final HashSet EXPIRY_EXC_ENTRIES = new HashSet(); @@ -282,5 +330,4 @@ index dd107fc..791ddb6 100644 // Ninety days in milliseconds private static final long NINETY_DAYS = 7776000000L; -- -1.8.3.1 - +2.19.0