From d6dd0dc3e06d42f108fe80920e1102d47a5aa583 Mon Sep 17 00:00:00 2001
From: Roger Riggs <rriggs@openjdk.org>
Date: Tue, 27 Jun 2023 17:49:09 +0000
Subject: [PATCH] 8308452: Extend internal Architecture enum with byte order
 and address size

Reviewed-by: mdoerr, jpai, mchung, amitkumar
---
 make/modules/java.base/gensrc/GensrcMisc.gmk  |   4 +-
 .../jdk/internal/util/Architecture.java       | 111 +++++++++++++-----
 .../internal/util/PlatformProps.java.template |   1 +
 .../jdk/tools/jlink/internal/Platform.java    |   1 -
 test/jdk/jdk/internal/util/ArchTest.java      |  98 ++++++++++------
 5 files changed, 145 insertions(+), 70 deletions(-)

diff --git a/make/modules/java.base/gensrc/GensrcMisc.gmk b/make/modules/java.base/gensrc/GensrcMisc.gmk
index e37aa50d41c..578adce4e9d 100644
--- a/make/modules/java.base/gensrc/GensrcMisc.gmk
+++ b/make/modules/java.base/gensrc/GensrcMisc.gmk
@@ -52,9 +52,7 @@ $(eval $(call SetupTextFileProcessing, BUILD_VERSION_JAVA, \
 
 
 # Normalize OPENJDK_TARGET_CPU name to match jdk.internal.util.Architecture enum
-ifneq ($(filter $(OPENJDK_TARGET_CPU), ppc64le), )
-  OPENJDK_TARGET_ARCH_CANONICAL = ppc64
-else ifneq ($(filter $(OPENJDK_TARGET_CPU), s390x), )
+ifneq ($(filter $(OPENJDK_TARGET_CPU), s390x), )
   OPENJDK_TARGET_ARCH_CANONICAL = s390
 else ifneq ($(filter $(OPENJDK_TARGET_CPU), x86_64 amd64), )
   OPENJDK_TARGET_ARCH_CANONICAL = x64
diff --git a/src/java.base/share/classes/jdk/internal/util/Architecture.java b/src/java.base/share/classes/jdk/internal/util/Architecture.java
index 3d68426ce3d..2c7d1f05f66 100644
--- a/src/java.base/share/classes/jdk/internal/util/Architecture.java
+++ b/src/java.base/share/classes/jdk/internal/util/Architecture.java
@@ -23,6 +23,8 @@
 package jdk.internal.util;
 
 import jdk.internal.vm.annotation.ForceInline;
+
+import java.nio.ByteOrder;
 import java.util.Locale;
 
 /**
@@ -33,19 +35,81 @@ import java.util.Locale;
  * architecture values.
  */
 public enum Architecture {
-    OTHER,      // An unknown architecture not specifically named
-    X64,        // Represents AMD64 and X86_64
-    X86,
-    AARCH64,
-    ARM,
-    RISCV64,
-    LOONGARCH64,
-    S390,
-    PPC64,
-    MIPSEL,
-    MIPS64EL
+    /*
+     * An unknown architecture not specifically named.
+     * The addrSize and ByteOrder values are those of the current architecture.
+     */
+    OTHER(is64bit() ? 64 : 32, ByteOrder.nativeOrder()),
+    X64(64, ByteOrder.LITTLE_ENDIAN),  // Represents AMD64 and X86_64
+    X86(32, ByteOrder.LITTLE_ENDIAN),
+    AARCH64(64, ByteOrder.LITTLE_ENDIAN),
+    ARM(32, ByteOrder.LITTLE_ENDIAN),
+    RISCV64(64, ByteOrder.LITTLE_ENDIAN),
+    LOONGARCH64(64, ByteOrder.LITTLE_ENDIAN),
+    S390(64, ByteOrder.BIG_ENDIAN),
+    PPC64(64, ByteOrder.BIG_ENDIAN),
+    PPC64LE(64, ByteOrder.LITTLE_ENDIAN),
+    MIPSEL(32, ByteOrder.LITTLE_ENDIAN),
+    MIPS64EL(64, ByteOrder.LITTLE_ENDIAN)
     ;
 
+    private final int addrSize;
+    private final ByteOrder byteOrder;
+
+    /**
+     * Construct an Architecture with number of address bits and byte order.
+     * @param addrSize number of address bits, typically 64 or 32
+     * @param byteOrder the byte order, big-endian or little-endian
+     */
+    Architecture(int addrSize, ByteOrder byteOrder) {
+        this.addrSize = addrSize;
+        this.byteOrder = byteOrder;
+    }
+
+    /**
+     * {@return the number of address bits, typically 64 or 32}
+     */
+    public int addressSize() {
+        return addrSize;
+    }
+
+    /**
+     * {@return the byte order, {@link ByteOrder#BIG_ENDIAN} or {@link ByteOrder#LITTLE_ENDIAN}}
+     */
+    public ByteOrder byteOrder() {
+        return byteOrder;
+    }
+
+    /**
+     * {@return the Architecture by name or an alias for the architecture}
+     * The names are mapped to upper case before mapping to an Architecture.
+     * @param archName an Architecture name or alias for the architecture.
+     * @throws IllegalArgumentException if the name is not an alias or an Architecture name
+     */
+    public static Architecture lookupByName(String archName) {
+        archName = archName.toUpperCase(Locale.ROOT); // normalize to uppercase
+        return switch (archName) {
+            case "X86_64", "AMD64" -> X64;
+            case "I386" -> X86;
+            case "S390X" -> S390;
+            default -> Architecture.valueOf(archName);
+        };
+    }
+
+    /**
+     * Returns the Architecture of the built architecture.
+     * Build time names are mapped to respective uppercase enum values.
+     * Names not recognized are mapped to Architecture.OTHER.
+     */
+    private static Architecture initArch(String archName) {
+        try {
+            return lookupByName(archName);
+        } catch (IllegalArgumentException ile) {
+            return Architecture.OTHER;
+        }
+    }
+
+    // Initialize the architecture by mapping aliases and names to the enum values.
     private static Architecture CURRENT_ARCH = initArch(PlatformProps.CURRENT_ARCH_STRING);
 
     /**
@@ -89,14 +153,21 @@ public enum Architecture {
     }
 
     /**
-     * {@return {@code true} if the current architecture is PPC64}
-     * Use {@link #isLittleEndian()} to determine big or little endian.
+     * {@return {@code true} if the current architecture is PPC64, big-endian}
      */
     @ForceInline
     public static boolean isPPC64() {
         return PlatformProps.TARGET_ARCH_IS_PPC64;
     }
 
+    /**
+     * {@return {@code true} if the current architecture is PPC64, little-endian}
+     */
+    @ForceInline
+    public static boolean isPPC64LE() {
+        return PlatformProps.TARGET_ARCH_IS_PPC64LE;
+    }
+
     /**
      * {@return {@code true} if the current architecture is ARM}
      */
@@ -151,18 +222,4 @@ public enum Architecture {
     public static boolean isLittleEndian() {
         return PlatformProps.TARGET_ARCH_LITTLE_ENDIAN;
     }
-
-
-    /**
-     * Returns the Architecture of the built architecture.
-     * Build time names are mapped to respective uppercase enum values.
-     * Names not recognized are mapped to Architecture.OTHER.
-     */
-    private static Architecture initArch(String archName) {
-        try {
-            return Architecture.valueOf(archName.toUpperCase(Locale.ROOT));
-        } catch (IllegalArgumentException ile) {
-            return Architecture.OTHER;
-        }
-    }
 }
diff --git a/src/java.base/share/classes/jdk/internal/util/PlatformProps.java.template b/src/java.base/share/classes/jdk/internal/util/PlatformProps.java.template
index cd615e600dd..fc3bccc6b28 100644
--- a/src/java.base/share/classes/jdk/internal/util/PlatformProps.java.template
+++ b/src/java.base/share/classes/jdk/internal/util/PlatformProps.java.template
@@ -58,6 +58,7 @@ class PlatformProps {
     static final boolean TARGET_ARCH_IS_LOONGARCH64 = "@@OPENJDK_TARGET_CPU@@" == "loongarch64";
     static final boolean TARGET_ARCH_IS_S390    = "@@OPENJDK_TARGET_CPU@@" == "s390";
     static final boolean TARGET_ARCH_IS_PPC64   = "@@OPENJDK_TARGET_CPU@@" == "ppc64";
+    static final boolean TARGET_ARCH_IS_PPC64LE = "@@OPENJDK_TARGET_CPU@@" == "ppc64le";
     static final boolean TARGET_ARCH_IS_MIPSEL  = "@@OPENJDK_TARGET_CPU@@" == "mipsel";
     static final boolean TARGET_ARCH_IS_MIPS64EL= "@@OPENJDK_TARGET_CPU@@" == "mips64el";
 }
diff --git a/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/Platform.java b/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/Platform.java
index cf9b9c324da..79744ba1621 100644
--- a/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/Platform.java
+++ b/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/Platform.java
@@ -52,7 +52,6 @@ public record Platform(OperatingSystem os, Architecture arch) {
         archName = platformString.substring(index + 1);
         // Alias architecture names, if needed
         archName = archName.replace("amd64", "X64");
-        archName = archName.replace("ppc64le", "PPC64");
         archName = archName.replace("s390x", "S390");
         Architecture arch = Architecture.valueOf(archName.toUpperCase(Locale.ROOT));
 
diff --git a/test/jdk/jdk/internal/util/ArchTest.java b/test/jdk/jdk/internal/util/ArchTest.java
index 1ffc91f77a5..4505a086027 100644
--- a/test/jdk/jdk/internal/util/ArchTest.java
+++ b/test/jdk/jdk/internal/util/ArchTest.java
@@ -20,16 +20,18 @@
  * or visit www.oracle.com if you need additional information or have any
  * questions.
  */
+import java.nio.ByteOrder;
+import java.util.List;
 import java.util.Locale;
 import java.util.stream.Stream;
 
 import jdk.internal.util.Architecture;
 import jdk.internal.misc.Unsafe;
 
-import static jdk.internal.util.Architecture.OTHER;
 import static jdk.internal.util.Architecture.AARCH64;
 import static jdk.internal.util.Architecture.ARM;
 import static jdk.internal.util.Architecture.PPC64;
+import static jdk.internal.util.Architecture.PPC64LE;
 import static jdk.internal.util.Architecture.RISCV64;
 import static jdk.internal.util.Architecture.LOONGARCH64;
 import static jdk.internal.util.Architecture.S390;
@@ -44,20 +46,51 @@ import org.junit.jupiter.params.provider.Arguments;
 import org.junit.jupiter.params.provider.MethodSource;
 
 import static org.junit.jupiter.api.Assertions.assertEquals;
-import static org.junit.jupiter.api.Assertions.fail;
 
 /**
  * @test
- * @bug 8304915
+ * @bug 8304915 8308452
  * @summary Verify Architecture enum maps to system property os.arch
  * @modules java.base/jdk.internal.util
  * @modules java.base/jdk.internal.misc
  * @run junit ArchTest
  */
 public class ArchTest {
-    private static boolean IS_BIG_ENDIAN = Unsafe.getUnsafe().isBigEndian();
+    private static final boolean IS_BIG_ENDIAN = Unsafe.getUnsafe().isBigEndian();
+
+    private static final boolean IS_64BIT_ADDRESS = Unsafe.getUnsafe().addressSize() == 8;
+
+    /**
+     * Test data for Architecture name vs Arch enums, address bits, endian-ness and boolean isXXX() methods..
+     * Each Argument contains:
+     *  - the common os.arch name,
+     *  - the Architecture Enum,
+     *  - address bits 32/64,
+     *  - the byte-order (little or big),
+     *  - the result of invoking the architecture specific static method
+     * @return a stream of arguments for parameterized tests
+     */
+    private static Stream<Arguments> archParams() {
+        // In alphabetical order
+        return Stream.of(
+                Arguments.of("aarch64", AARCH64, 64, ByteOrder.LITTLE_ENDIAN, Architecture.isAARCH64()),
+                Arguments.of("amd64", X64, 64, ByteOrder.LITTLE_ENDIAN, Architecture.isX64()),
+                Arguments.of("arm", ARM, 32, ByteOrder.LITTLE_ENDIAN, Architecture.isAARCH64()),
+                Arguments.of("i386", X86, 32, ByteOrder.LITTLE_ENDIAN, Architecture.isX86()),
+                Arguments.of("loongarch64", LOONGARCH64, 64, ByteOrder.LITTLE_ENDIAN, Architecture.isLOONGARCH64()),
+                Arguments.of("mips64el", MIPS64EL, 64, ByteOrder.LITTLE_ENDIAN, Architecture.isMIPS64EL()),
+                Arguments.of("mipsel", MIPSEL, 32, ByteOrder.LITTLE_ENDIAN, Architecture.isMIPSEL()),
+                Arguments.of("ppc64", PPC64, 64, ByteOrder.BIG_ENDIAN, Architecture.isPPC64()),
+                Arguments.of("ppc64le", PPC64LE, 64, ByteOrder.LITTLE_ENDIAN, Architecture.isPPC64LE()),
+                Arguments.of("riscv64", RISCV64, 64, ByteOrder.LITTLE_ENDIAN, Architecture.isRISCV64()),
+                Arguments.of("s390", S390, 64, ByteOrder.BIG_ENDIAN, Architecture.isS390()),
+                Arguments.of("s390x", S390, 64, ByteOrder.BIG_ENDIAN, Architecture.isS390()),
+                Arguments.of("x64", X64, 64, ByteOrder.LITTLE_ENDIAN, Architecture.isX64()),
+                Arguments.of("x86", X86, 32, ByteOrder.LITTLE_ENDIAN, Architecture.isX86()),
+                Arguments.of("x86_64", X64, 64, ByteOrder.LITTLE_ENDIAN, Architecture.isX64())
+        );
+    }
 
-    private static boolean IS_64BIT_ADDRESS = Unsafe.getUnsafe().addressSize() == 8;
 
     /**
      * Test consistency of System property "os.arch" with Architecture.current().
@@ -65,46 +98,33 @@ public class ArchTest {
     @Test
     public void nameVsCurrent() {
         String osArch = System.getProperty("os.arch").toLowerCase(Locale.ROOT);
-        System.out.printf("System property os.arch: \"%s\", Architecture.current(): \"%s\"%n",
+        System.err.printf("System property os.arch: \"%s\", Architecture.current(): \"%s\"%n",
                 osArch, Architecture.current());
-        Architecture arch = switch (osArch) {
-            case "x86_64", "amd64" -> X64;
-            case "x86", "i386" -> X86;
-            case "aarch64" -> AARCH64;
-            case "arm" -> ARM;
-            case "riscv64" -> RISCV64;
-            case "loongarch64" -> LOONGARCH64;
-            case "s390x", "s390" -> S390;
-            case "ppc64", "ppc64le" -> PPC64;
-            case "mipsel" -> MIPSEL;
-            case "mips64el" -> MIPS64EL;
-            default -> OTHER;
-        };
-        assertEquals(Architecture.current(), arch, "mismatch in Architecture.current vs " + osArch);
-    }
 
-    /**
-     * Test various Architecture enum values vs boolean isXXX() methods.
-     * @return a stream of arguments for parameterized test
-     */
-    private static Stream<Arguments> archParams() {
-        return Stream.of(
-                Arguments.of(X64, Architecture.isX64()),
-                Arguments.of(X86, Architecture.isX86()),
-                Arguments.of(AARCH64, Architecture.isAARCH64()),
-                Arguments.of(ARM, Architecture.isARM()),
-                Arguments.of(RISCV64, Architecture.isRISCV64()),
-                Arguments.of(LOONGARCH64, Architecture.isLOONGARCH64()),
-                Arguments.of(S390, Architecture.isS390()),
-                Arguments.of(MIPSEL, Architecture.isMIPSEL()),
-                Arguments.of(MIPS64EL, Architecture.isMIPS64EL()),
-                Arguments.of(PPC64, Architecture.isPPC64())
-        );
+        // Map os.arch system property to expected Architecture
+        List<Architecture> argList = archParams()
+                .filter(p -> p.get()[0].equals(osArch))
+                .map(a -> (Architecture)a.get()[1])
+                .toList();
+        assertEquals(1, argList.size(), osArch + " too few or too many matching system property os.arch cases: " + argList);
+        assertEquals(Architecture.current(), argList.get(0), "mismatch in Architecture.current vs " + osArch);
     }
 
     @ParameterizedTest
     @MethodSource("archParams")
-    public void isArch(Architecture arch, boolean isArch) {
+    public void checkParams(String archName, Architecture arch, int addrSize, ByteOrder byteOrder, boolean isArch) {
+        Architecture actual = Architecture.lookupByName(archName);
+        assertEquals(actual, arch, "Wrong Architecture from lookupByName");
+
+        actual = Architecture.lookupByName(archName.toUpperCase(Locale.ROOT));
+        assertEquals(actual, arch, "Wrong Architecture from lookupByName (upper-case)");
+
+        actual = Architecture.lookupByName(archName.toLowerCase(Locale.ROOT));
+        assertEquals(actual, arch, "Wrong Architecture from lookupByName (lower-case)");
+
+        assertEquals(addrSize, actual.addressSize(), "Wrong address size");
+        assertEquals(byteOrder, actual.byteOrder(), "Wrong byteOrder");
+
         Architecture current = Architecture.current();
         assertEquals(arch == current, isArch,
                 "Method is" + arch + "(): returned " + isArch + ", should be (" + arch + " == " + current + ")");