diff --git a/sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/c/constant/CConstant.java b/sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/c/constant/CConstant.java
index 024289c25792..7c54c0645e85 100644
--- a/sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/c/constant/CConstant.java
+++ b/sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/c/constant/CConstant.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2013, 2019, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2013, 2024, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* The Universal Permissive License (UPL), Version 1.0
@@ -89,8 +89,7 @@ private ValueAccess() {
* This method is useful during native image generation, when the annotated method cannot be
* called.
*
- * @param declaringClass The class that contains the method annotated with {@link CConstant}
- * .
+ * @param declaringClass The class that contains the method.
* @param methodName The name of the method annotated with {@link CConstant}.
* @param returnType The desired type of the returned value. For integer-kind constants, the
* supported types are {@link Long}, {@link Integer}, and {@link Boolean}. For
diff --git a/sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/c/constant/CEnum.java b/sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/c/constant/CEnum.java
index a6d1a6e5488a..4c835148cabb 100644
--- a/sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/c/constant/CEnum.java
+++ b/sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/c/constant/CEnum.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2014, 2019, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2014, 2024, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* The Universal Permissive License (UPL), Version 1.0
@@ -63,9 +63,13 @@
* enumeration value. This annotation is optional and only needed if an attribute has a non-default
* value.
*
- * Note that C enumeration are merely a type-safe way to specify integer constants in C. Therefore,
- * C enumeration values can be imported to Java as a regular {@link CConstant}; and {@link CEnum}
- * can be used to import regular integer C constants as a Java enumeration.
+ * Note that in C, the keyword "enum" is merely a type-safe way to specify integer constants.
+ * Therefore, it is also possible to import C enumeration values as regular {@link CConstant}s into
+ * Java.
+ *
+ * {@link CEnum} can also be used to import multiple (potentially unrelated) regular integer C
+ * constants into a single Java enumeration. However, please note that each C constant will be
+ * converted to the C type that {@link CEnum} specifies.
*
* The annotated class, or an outer class that contains the class, must be annotated with
* {@link CContext}.
@@ -77,8 +81,8 @@
public @interface CEnum {
/**
- * Specifies the name of the imported C enum type. If no name is provided, int
is
- * used instead.
+ * Specifies the name of the imported C enum type. If no name is provided, the C data type
+ * int
is used instead.
*
* @since 19.0
*/
diff --git a/sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/c/struct/CBitfield.java b/sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/c/struct/CBitfield.java
index a056b803e12e..00fdf5eb553d 100644
--- a/sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/c/struct/CBitfield.java
+++ b/sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/c/struct/CBitfield.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2014, 2019, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2014, 2024, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* The Universal Permissive License (UPL), Version 1.0
@@ -65,7 +65,7 @@
* The receiver is the pointer to the struct that is accessed, i.e., the base address of the memory
* access.
*
- * The {@code FieldType} must be must be a primitive integer type or a {@link WordBase word type}.
+ * The {@code FieldType} must be a primitive integer type or a {@link WordBase word type}.
*
* The optional parameter {@code locationIdentity} specifies the {@link LocationIdentity} to be used
* for the memory access. Two memory accesses with two different location identities are guaranteed
diff --git a/sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/c/struct/CTypedefOfInfo.java b/sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/c/struct/CTypedefOfInfo.java
index e892358528fd..7de4ee628eec 100644
--- a/sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/c/struct/CTypedefOfInfo.java
+++ b/sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/c/struct/CTypedefOfInfo.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2014, 2019, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2014, 2024, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* The Universal Permissive License (UPL), Version 1.0
@@ -46,9 +46,9 @@
import java.lang.annotation.Target;
/**
- * Informational only. Used to provide information about the base type of a type mentioned in a
- * System Java annotation. Currently use with {@link CPointerTo} to inform about the base primitive
- * type of the type pointed to.
+ * Informational only. Used to document the base type of a type mentioned in a C interface
+ * annotation. Currently use with {@link CPointerTo} to inform about the base primitive type of the
+ * type pointed to.
*
* @since 19.0
*/
@@ -57,7 +57,7 @@
public @interface CTypedefOfInfo {
/**
- * Specifies the base type the C type of this annotated System Java class is a typedef of.
+ * Specifies the base type the C type of this annotated C interface class is a typedef of.
* (i.e., typedef a1 a0; typedef a2 a1; => base type of a2 is a0)
*
* @since 19.0
diff --git a/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/headers/Dlfcn.java b/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/headers/Dlfcn.java
index 8e9dc7e07ab8..05d564150cbb 100644
--- a/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/headers/Dlfcn.java
+++ b/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/headers/Dlfcn.java
@@ -24,25 +24,25 @@
*/
package com.oracle.svm.core.posix.headers;
-import com.oracle.svm.core.c.libc.GLibC;
-import com.oracle.svm.core.c.libc.LibCSpecific;
import org.graalvm.nativeimage.Platform;
import org.graalvm.nativeimage.Platforms;
import org.graalvm.nativeimage.c.CContext;
import org.graalvm.nativeimage.c.constant.CConstant;
import org.graalvm.nativeimage.c.function.CFunction;
-import org.graalvm.nativeimage.c.function.CLibrary;
import org.graalvm.nativeimage.c.function.CFunction.Transition;
+import org.graalvm.nativeimage.c.function.CLibrary;
import org.graalvm.nativeimage.c.struct.CField;
import org.graalvm.nativeimage.c.struct.CPointerTo;
import org.graalvm.nativeimage.c.struct.CStruct;
-import org.graalvm.nativeimage.c.struct.CTypedefOfInfo;
import org.graalvm.nativeimage.c.type.CCharPointer;
import org.graalvm.word.Pointer;
import org.graalvm.word.PointerBase;
import org.graalvm.word.SignedWord;
import org.graalvm.word.WordBase;
+import com.oracle.svm.core.c.libc.GLibC;
+import com.oracle.svm.core.c.libc.LibCSpecific;
+
// Checkstyle: stop
/**
@@ -106,7 +106,6 @@ public static class GNUExtensions {
public interface Lmid_t extends SignedWord {
}
- @CTypedefOfInfo("long int")
@CPointerTo(nameOfCType = "Lmid_t")
public interface Lmid_tPointer extends PointerBase {
Lmid_t read();
diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/c/enums/EnumArrayLookup.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/c/enums/CEnumArrayLookup.java
similarity index 81%
rename from substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/c/enums/EnumArrayLookup.java
rename to substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/c/enums/CEnumArrayLookup.java
index 16953838b8f1..37adfdd5b05f 100644
--- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/c/enums/EnumArrayLookup.java
+++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/c/enums/CEnumArrayLookup.java
@@ -26,20 +26,19 @@
import jdk.graal.compiler.core.common.calc.UnsignedMath;
-public class EnumArrayLookup extends EnumRuntimeData {
-
+public class CEnumArrayLookup extends CEnumRuntimeData {
private final long cOffset;
- private Enum>[] cToJava;
+ private final Enum>[] cToJava;
- public EnumArrayLookup(long[] javaToC, long cOffset, Enum>[] cToJava) {
- super(javaToC);
+ public CEnumArrayLookup(long[] javaToC, int bytesInC, boolean isCValueUnsigned, long cOffset, Enum>[] cToJava) {
+ super(javaToC, bytesInC, isCValueUnsigned);
this.cOffset = cOffset;
this.cToJava = cToJava;
}
@Override
- protected Enum> convertCToJava(long cValue) {
- long arrayIdx = cValue - cOffset;
+ protected Enum> lookupEnum(long value) {
+ long arrayIdx = value - cOffset;
if (UnsignedMath.aboveOrEqual(arrayIdx, cToJava.length)) {
return null;
}
diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/c/enums/EnumMapLookup.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/c/enums/CEnumMapLookup.java
similarity index 79%
rename from substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/c/enums/EnumMapLookup.java
rename to substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/c/enums/CEnumMapLookup.java
index d7e8d1039dec..fcdc987db266 100644
--- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/c/enums/EnumMapLookup.java
+++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/c/enums/CEnumMapLookup.java
@@ -26,17 +26,16 @@
import java.util.Map;
-public class EnumMapLookup extends EnumRuntimeData {
+public class CEnumMapLookup extends CEnumRuntimeData {
+ private final Map> cToJava;
- private Map> cToJava;
-
- public EnumMapLookup(long[] javaToC, Map> cToJava) {
- super(javaToC);
+ public CEnumMapLookup(long[] javaToC, int bytesInC, boolean isCValueUnsigned, Map> cToJava) {
+ super(javaToC, bytesInC, isCValueUnsigned);
this.cToJava = cToJava;
}
@Override
- protected Enum> convertCToJava(long cValue) {
- return cToJava.get(cValue);
+ protected Enum> lookupEnum(long value) {
+ return cToJava.get(value);
}
}
diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/c/enums/EnumNoLookup.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/c/enums/CEnumNoLookup.java
similarity index 84%
rename from substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/c/enums/EnumNoLookup.java
rename to substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/c/enums/CEnumNoLookup.java
index a4295688a96c..30fe0a15ad16 100644
--- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/c/enums/EnumNoLookup.java
+++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/c/enums/CEnumNoLookup.java
@@ -24,14 +24,13 @@
*/
package com.oracle.svm.core.c.enums;
-public class EnumNoLookup extends EnumRuntimeData {
-
- public EnumNoLookup(long[] javaToC) {
- super(javaToC);
+public class CEnumNoLookup extends CEnumRuntimeData {
+ public CEnumNoLookup(long[] javaToC, int bytesInC, boolean isCValueUnsigned) {
+ super(javaToC, bytesInC, isCValueUnsigned);
}
@Override
- protected Enum> convertCToJava(long cValue) {
+ protected Enum> lookupEnum(long value) {
return null;
}
}
diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/c/enums/CEnumRuntimeData.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/c/enums/CEnumRuntimeData.java
new file mode 100644
index 000000000000..f13120e72fe5
--- /dev/null
+++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/c/enums/CEnumRuntimeData.java
@@ -0,0 +1,109 @@
+/*
+ * Copyright (c) 2014, 2024, 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 com.oracle.svm.core.c.enums;
+
+import org.graalvm.word.SignedWord;
+import org.graalvm.word.UnsignedWord;
+import org.graalvm.word.WordFactory;
+
+import com.oracle.svm.core.Uninterruptible;
+import com.oracle.svm.core.jdk.UninterruptibleUtils.CodeUtil;
+
+public abstract class CEnumRuntimeData {
+ private static final NullPointerException CACHED_NULL_EXCEPTION = new NullPointerException(
+ "null enum object cannot be converted to C enum integer (typically for automatic conversions on return to C code)");
+
+ /** Stores the sign- or zero-extended C values (depending on the signedness of the C enum). */
+ private final long[] javaToC;
+ private final int bytesInC;
+ private final boolean isCEnumTypeUnsigned;
+
+ protected CEnumRuntimeData(long[] javaToC, int bytesInC, boolean isCEnumTypeUnsigned) {
+ this.javaToC = javaToC;
+ this.bytesInC = bytesInC;
+ this.isCEnumTypeUnsigned = isCEnumTypeUnsigned;
+ }
+
+ @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true)
+ public final boolean enumToBoolean(Enum> value) {
+ return enumToLong(value) != 0L;
+ }
+
+ @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true)
+ public final byte enumToByte(Enum> value) {
+ return (byte) enumToLong(value);
+ }
+
+ @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true)
+ public final int enumToShort(Enum> value) {
+ return (short) enumToLong(value);
+ }
+
+ @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true)
+ public final char enumToChar(Enum> value) {
+ return (char) CodeUtil.zeroExtend(enumToLong(value), bytesInC * Byte.SIZE);
+ }
+
+ @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true)
+ public final int enumToInt(Enum> value) {
+ return (int) enumToLong(value);
+ }
+
+ @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true)
+ public final long enumToLong(Enum> value) {
+ if (value == null) {
+ throw CACHED_NULL_EXCEPTION;
+ }
+ return javaToC[value.ordinal()];
+ }
+
+ @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true)
+ public final SignedWord enumToSignedWord(Enum> value) {
+ long result = enumToLong(value);
+ return WordFactory.signed(result);
+ }
+
+ @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true)
+ public final UnsignedWord enumToUnsignedWord(Enum> value) {
+ long result = CodeUtil.zeroExtend(enumToLong(value), bytesInC * Byte.SIZE);
+ return WordFactory.unsigned(result);
+ }
+
+ /**
+ * Reduce the C value to the bits that fit into the C enum type. Then, sign- or zero-extend that
+ * value to 64-bit so that it can be compared to the data in the image heap.
+ */
+ public final Enum> longToEnum(long rawValue) {
+ long lookupValue;
+ if (isCEnumTypeUnsigned) {
+ lookupValue = CodeUtil.zeroExtend(rawValue, bytesInC * Byte.SIZE);
+ } else {
+ lookupValue = CodeUtil.signExtend(rawValue, bytesInC * Byte.SIZE);
+ }
+ return lookupEnum(lookupValue);
+ }
+
+ protected abstract Enum> lookupEnum(long value);
+}
diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/c/enums/EnumRuntimeData.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/c/enums/EnumRuntimeData.java
deleted file mode 100644
index f8695b54609b..000000000000
--- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/c/enums/EnumRuntimeData.java
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
- * Copyright (c) 2014, 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. 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 com.oracle.svm.core.c.enums;
-
-import org.graalvm.nativeimage.c.constant.CEnumValue;
-
-import com.oracle.svm.core.Uninterruptible;
-
-public abstract class EnumRuntimeData {
- private static final NullPointerException CACHED_NULL_EXCEPTION = new NullPointerException(
- "null enum object cannot be converted to C enum integer (typically for automatic conversions on return to C code)");
-
- private final long[] javaToC;
-
- protected EnumRuntimeData(long[] javaToC) {
- this.javaToC = javaToC;
- }
-
- @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true)
- protected long convertJavaToCLong(Enum> javaValue) {
- if (javaValue == null) {
- throw CACHED_NULL_EXCEPTION;
- }
- return javaToC[javaValue.ordinal()];
- }
-
- /**
- * We need a separate method for single-slot return values. Since a call to this method replaces
- * a call to the {@link CEnumValue} annotated method, we must match the slot-count of the
- * original method otherwise frame state handling gets confused.
- */
- @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true)
- protected int convertJavaToCInt(Enum> javaValue) {
- return (int) convertJavaToCLong(javaValue);
- }
-
- protected abstract Enum> convertCToJava(long cValue);
-}
diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/UninterruptibleUtils.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/UninterruptibleUtils.java
index c1ec189f76b2..f7c432322b09 100644
--- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/UninterruptibleUtils.java
+++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/UninterruptibleUtils.java
@@ -713,4 +713,28 @@ public interface CharReplacer {
@Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true)
char replace(char val);
}
+
+ public static class CodeUtil {
+ @Uninterruptible(reason = CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true)
+ public static long signExtend(long value, int inputBits) {
+ if (inputBits < 64) {
+ if ((value >>> (inputBits - 1) & 1) == 1) {
+ return value | (-1L << inputBits);
+ } else {
+ return value & ~(-1L << inputBits);
+ }
+ } else {
+ return value;
+ }
+ }
+
+ @Uninterruptible(reason = CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true)
+ public static long zeroExtend(long value, int inputBits) {
+ if (inputBits < 64) {
+ return value & ~(-1L << inputBits);
+ } else {
+ return value;
+ }
+ }
+ }
}
diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jvmstat/MutablePerfDataEntry.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jvmstat/MutablePerfDataEntry.java
index 59f128a57401..ff9991e581c1 100644
--- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jvmstat/MutablePerfDataEntry.java
+++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jvmstat/MutablePerfDataEntry.java
@@ -32,8 +32,8 @@
* well). However, this is fairly tricky as the performance data memory is only reserved after the
* heap was already started up. So, it could happen that the GC (especially G1) tries to write to
* the performance data memory before it is reserved. This could probably be solved by porting the
- * code that maps the performance data memory to system Java so that the performance data memory
- * could be initialized after the image heap was mapped but before the GC is started up. Same
+ * code that maps the performance data memory to uninterruptible code so that the performance data
+ * memory could be initialized after the image heap was mapped but before the GC is started up. Same
* applies to the teardown. See GR-40601.
*/
public interface MutablePerfDataEntry extends PerfDataEntry {
diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/CPUFeatureAccessFeatureBase.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/CPUFeatureAccessFeatureBase.java
index 1e3afd80e1b4..a44772ee01ae 100644
--- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/CPUFeatureAccessFeatureBase.java
+++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/CPUFeatureAccessFeatureBase.java
@@ -31,7 +31,6 @@
import java.util.EnumSet;
import org.graalvm.collections.EconomicMap;
-import jdk.graal.compiler.debug.GraalError;
import org.graalvm.nativeimage.ImageSingletons;
import org.graalvm.nativeimage.Platform;
import org.graalvm.nativeimage.Platforms;
@@ -46,6 +45,8 @@
import com.oracle.svm.hosted.c.info.StructFieldInfo;
import com.oracle.svm.hosted.c.info.StructInfo;
+import jdk.graal.compiler.debug.GraalError;
+
public abstract class CPUFeatureAccessFeatureBase {
/**
* Initializes global data required at run time by {@link CPUFeatureAccess}. This method will
@@ -80,7 +81,7 @@ protected > void initializeCPUFeatureAccessData(Enum[] allC
var field = ((StructFieldInfo) entry);
var fieldName = field.getName();
int offset = field.getOffsetInfo().getProperty();
- int size = field.getSizeInfo().getProperty();
+ int size = field.getSizeInBytes();
GraalError.guarantee(size == Byte.BYTES, "Expected field %s to be byte sized, but was %s", field.getName(), size);
GraalError.guarantee(fieldName.startsWith("f"), "Unexpected field name in %s: %s", cpuFeatureStructClass.getName(), fieldName);
fieldToOffset.put(fieldName.substring(1), offset);
@@ -89,7 +90,7 @@ protected > void initializeCPUFeatureAccessData(Enum[] allC
// Initialize CPUFeatures struct.
// Over-allocate to a multiple of 64 bit.
- int structSize = ((cpuFeatureStructInfo.getSizeInfo().getProperty() + Long.BYTES - 1) / Long.BYTES) * Long.BYTES;
+ int structSize = ((cpuFeatureStructInfo.getSizeInBytes() + Long.BYTES - 1) / Long.BYTES) * Long.BYTES;
byte[] requiredFeaturesStruct = new byte[structSize];
// Data is stored in bitwise negated form, thus initialize to all 1s.
Arrays.fill(requiredFeaturesStruct, (byte) 0xff);
diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/NativeImageGenerator.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/NativeImageGenerator.java
index 1477e5374118..a27200774269 100644
--- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/NativeImageGenerator.java
+++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/NativeImageGenerator.java
@@ -976,8 +976,9 @@ protected void setupNativeImage(OptionValues options, Map feature.duringSetup(config));
@@ -1221,17 +1222,17 @@ private static Inflation createBigBang(DebugContext debug, OptionValues options,
}
@SuppressWarnings("try")
- protected NativeLibraries setupNativeLibraries(ConstantReflectionProvider aConstantReflection, MetaAccessProvider aMetaAccess,
- SnippetReflectionProvider aSnippetReflection, CEnumCallWrapperSubstitutionProcessor cEnumProcessor, ClassInitializationSupport classInitializationSupport, DebugContext debug) {
+ protected NativeLibraries setupNativeLibraries(HostedProviders providers, CEnumCallWrapperSubstitutionProcessor cEnumProcessor, ClassInitializationSupport classInitializationSupport,
+ DebugContext debug) {
try (StopTimer ignored = TimerCollection.createTimerAndStart("(cap)")) {
- NativeLibraries nativeLibs = new NativeLibraries(aConstantReflection, aMetaAccess, aSnippetReflection, ConfigurationValues.getTarget(), classInitializationSupport,
+ NativeLibraries nativeLibs = new NativeLibraries(providers, ConfigurationValues.getTarget(), classInitializationSupport,
ImageSingletons.lookup(TemporaryBuildDirectoryProvider.class).getTemporaryBuildDirectory(), debug);
cEnumProcessor.setNativeLibraries(nativeLibs);
- processNativeLibraryImports(nativeLibs, aMetaAccess, classInitializationSupport);
+ processNativeLibraryImports(nativeLibs, classInitializationSupport);
- ImageSingletons.add(SizeOfSupport.class, new SizeOfSupportImpl(nativeLibs, aMetaAccess));
- ImageSingletons.add(OffsetOf.Support.class, new OffsetOfSupportImpl(nativeLibs, aMetaAccess));
- ImageSingletons.add(CConstantValueSupport.class, new CConstantValueSupportImpl(nativeLibs, aMetaAccess));
+ ImageSingletons.add(SizeOfSupport.class, new SizeOfSupportImpl(nativeLibs));
+ ImageSingletons.add(OffsetOf.Support.class, new OffsetOfSupportImpl(nativeLibs));
+ ImageSingletons.add(CConstantValueSupport.class, new CConstantValueSupportImpl(nativeLibs));
if (CAnnotationProcessorCache.Options.ExitAfterQueryCodeGeneration.getValue()) {
throw new InterruptImageBuilding("Exiting image generation because of " + SubstrateOptionsParser.commandArgument(CAnnotationProcessorCache.Options.ExitAfterQueryCodeGeneration, "+"));
@@ -1725,8 +1726,8 @@ public static String checkName(String name) {
}
@SuppressWarnings("try")
- protected void processNativeLibraryImports(NativeLibraries nativeLibs, MetaAccessProvider metaAccess, ClassInitializationSupport classInitializationSupport) {
-
+ protected void processNativeLibraryImports(NativeLibraries nativeLibs, ClassInitializationSupport classInitializationSupport) {
+ MetaAccessProvider metaAccess = nativeLibs.getMetaAccess();
for (Method method : loader.findAnnotatedMethods(CConstant.class)) {
if (HostedLibCBase.isMethodProvidedInCurrentLibc(method)) {
initializeAtBuildTime(method.getDeclaringClass(), classInitializationSupport, CConstant.class);
diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/c/CConstantValueSupportImpl.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/c/CConstantValueSupportImpl.java
index e33555d5c232..cb9d180cc2ed 100644
--- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/c/CConstantValueSupportImpl.java
+++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/c/CConstantValueSupportImpl.java
@@ -24,70 +24,65 @@
*/
package com.oracle.svm.hosted.c;
+import org.graalvm.nativeimage.Platform;
+import org.graalvm.nativeimage.Platforms;
import org.graalvm.nativeimage.c.constant.CConstant;
import org.graalvm.nativeimage.impl.CConstantValueSupport;
import com.oracle.svm.core.util.VMError;
import com.oracle.svm.hosted.c.info.ConstantInfo;
+import com.oracle.svm.hosted.phases.CInterfaceInvocationPlugin;
+import com.oracle.svm.util.ReflectionUtil;
import jdk.vm.ci.meta.MetaAccessProvider;
import jdk.vm.ci.meta.ResolvedJavaMethod;
+@Platforms(Platform.HOSTED_ONLY.class)
public final class CConstantValueSupportImpl implements CConstantValueSupport {
private final NativeLibraries nativeLibraries;
private final MetaAccessProvider metaAccess;
- public CConstantValueSupportImpl(NativeLibraries nativeLibraries, MetaAccessProvider metaAccess) {
+ public CConstantValueSupportImpl(NativeLibraries nativeLibraries) {
this.nativeLibraries = nativeLibraries;
- this.metaAccess = metaAccess;
+ this.metaAccess = nativeLibraries.getMetaAccess();
}
@Override
+ @SuppressWarnings("unchecked")
public T getCConstantValue(Class> declaringClass, String methodName, Class returnType) {
+ ResolvedJavaMethod method = getAnnotatedMethod(declaringClass, methodName);
+ ConstantInfo constantInfo = (ConstantInfo) nativeLibraries.findElementInfo(method);
+ Object value = constantInfo.getValue();
+
+ switch (constantInfo.getKind()) {
+ case INTEGER, POINTER -> {
+ return (T) CInterfaceInvocationPlugin.convertCIntegerToMethodReturnType(nativeLibraries, returnType, (long) value, constantInfo.getSizeInBytes() * Byte.SIZE,
+ constantInfo.isUnsigned());
+ }
+ case FLOAT -> {
+ if (returnType == Float.class) {
+ return returnType.cast(((Double) value).floatValue());
+ }
+ return returnType.cast(value);
+ }
+ case STRING, BYTEARRAY -> {
+ return returnType.cast(value);
+ }
+ default -> throw VMError.shouldNotReachHere("Unexpected returnType: " + returnType.getName());
+ }
+ }
+
+ private ResolvedJavaMethod getAnnotatedMethod(Class> declaringClass, String methodName) {
ResolvedJavaMethod method;
try {
- method = metaAccess.lookupJavaMethod(declaringClass.getMethod(methodName));
- } catch (NoSuchMethodException | SecurityException e) {
+ method = metaAccess.lookupJavaMethod(ReflectionUtil.lookupMethod(declaringClass, methodName));
+ } catch (ReflectionUtil.ReflectionUtilError e) {
throw VMError.shouldNotReachHere("Method not found: " + declaringClass.getName() + "." + methodName);
}
+
if (method.getAnnotation(CConstant.class) == null) {
throw VMError.shouldNotReachHere("Method " + declaringClass.getName() + "." + methodName + " is not annotated with @" + CConstant.class.getSimpleName());
}
-
- ConstantInfo constantInfo = (ConstantInfo) nativeLibraries.findElementInfo(method);
- Object value = constantInfo.getValueInfo().getProperty();
- switch (constantInfo.getKind()) {
- case INTEGER:
- case POINTER:
- Long longValue = (Long) value;
- if (returnType == Boolean.class) {
- return returnType.cast(Boolean.valueOf(longValue.longValue() != 0));
- } else if (returnType == Integer.class) {
- return returnType.cast(Integer.valueOf((int) longValue.longValue()));
- } else if (returnType == Long.class) {
- return returnType.cast(value);
- }
- break;
-
- case FLOAT:
- if (returnType == Double.class) {
- return returnType.cast(value);
- }
- break;
-
- case STRING:
- if (returnType == String.class) {
- return returnType.cast(value);
- }
- break;
-
- case BYTEARRAY:
- if (returnType == byte[].class) {
- return returnType.cast(value);
- }
- break;
- }
-
- throw VMError.shouldNotReachHere("Unexpected returnType: " + returnType.getName());
+ return method;
}
}
diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/c/NativeLibraries.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/c/NativeLibraries.java
index 780c8ec3b54b..07a12c3fa930 100644
--- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/c/NativeLibraries.java
+++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/c/NativeLibraries.java
@@ -48,12 +48,6 @@
import java.util.stream.Collectors;
import java.util.stream.Stream;
-import jdk.graal.compiler.api.replacements.SnippetReflectionProvider;
-import jdk.graal.compiler.debug.DebugContext;
-import jdk.graal.compiler.hotspot.JVMCIVersionCheck;
-import jdk.graal.compiler.word.BarrieredAccess;
-import jdk.graal.compiler.word.ObjectAccess;
-import jdk.graal.compiler.word.Word;
import org.graalvm.nativeimage.ImageSingletons;
import org.graalvm.nativeimage.Platform;
import org.graalvm.nativeimage.c.CContext;
@@ -75,6 +69,7 @@
import com.oracle.graal.pointsto.infrastructure.WrappedElement;
import com.oracle.graal.pointsto.meta.AnalysisType;
+import com.oracle.graal.pointsto.meta.HostedProviders;
import com.oracle.svm.core.SubstrateOptions;
import com.oracle.svm.core.jdk.PlatformNativeLibrarySupport;
import com.oracle.svm.core.util.UserError;
@@ -86,6 +81,13 @@
import com.oracle.svm.util.ReflectionUtil;
import com.oracle.svm.util.ReflectionUtil.ReflectionUtilError;
+import jdk.graal.compiler.api.replacements.SnippetReflectionProvider;
+import jdk.graal.compiler.debug.DebugContext;
+import jdk.graal.compiler.hotspot.JVMCIVersionCheck;
+import jdk.graal.compiler.word.BarrieredAccess;
+import jdk.graal.compiler.word.ObjectAccess;
+import jdk.graal.compiler.word.Word;
+import jdk.graal.compiler.word.WordTypes;
import jdk.vm.ci.code.TargetDescription;
import jdk.vm.ci.meta.ConstantReflectionProvider;
import jdk.vm.ci.meta.MetaAccessProvider;
@@ -95,17 +97,18 @@
public final class NativeLibraries {
private final MetaAccessProvider metaAccess;
+ private final WordTypes wordTypes;
private final SnippetReflectionProvider snippetReflection;
private final TargetDescription target;
- private ClassInitializationSupport classInitializationSupport;
+ private final ClassInitializationSupport classInitializationSupport;
private final Map elementToInfo;
private final Map, NativeCodeContext> compilationUnitToContext;
private final ResolvedJavaType wordBaseType;
- private final ResolvedJavaType signedType;
- private final ResolvedJavaType unsignedType;
+ private final ResolvedJavaType signedWordType;
+ private final ResolvedJavaType unsignedWordType;
private final ResolvedJavaType pointerBaseType;
private final ResolvedJavaType stringType;
private final ResolvedJavaType byteArrayType;
@@ -219,11 +222,11 @@ public String toString() {
}
}
- public NativeLibraries(ConstantReflectionProvider constantReflection, MetaAccessProvider metaAccess, SnippetReflectionProvider snippetReflection, TargetDescription target,
- ClassInitializationSupport classInitializationSupport, Path tempDirectory, DebugContext debug) {
- this.metaAccess = metaAccess;
- this.constantReflection = constantReflection;
- this.snippetReflection = snippetReflection;
+ public NativeLibraries(HostedProviders providers, TargetDescription target, ClassInitializationSupport classInitializationSupport, Path tempDirectory, DebugContext debug) {
+ this.metaAccess = providers.getMetaAccess();
+ this.constantReflection = providers.getConstantReflection();
+ this.snippetReflection = providers.getSnippetReflection();
+ this.wordTypes = providers.getWordTypes();
this.target = target;
this.classInitializationSupport = classInitializationSupport;
this.tempDirectory = tempDirectory;
@@ -234,8 +237,8 @@ public NativeLibraries(ConstantReflectionProvider constantReflection, MetaAccess
compilationUnitToContext = new HashMap<>();
wordBaseType = lookupAndRegisterType(WordBase.class);
- signedType = lookupAndRegisterType(SignedWord.class);
- unsignedType = lookupAndRegisterType(UnsignedWord.class);
+ signedWordType = lookupAndRegisterType(SignedWord.class);
+ unsignedWordType = lookupAndRegisterType(UnsignedWord.class);
pointerBaseType = lookupAndRegisterType(PointerBase.class);
stringType = lookupAndRegisterType(String.class);
byteArrayType = lookupAndRegisterType(byte[].class);
@@ -266,6 +269,10 @@ public NativeLibraries(ConstantReflectionProvider constantReflection, MetaAccess
this.cache = new CAnnotationProcessorCache();
}
+ public static NativeLibraries singleton() {
+ return ImageSingletons.lookup(NativeLibraries.class);
+ }
+
private ResolvedJavaType lookupAndRegisterType(Class> clazz) {
AnalysisType type = (AnalysisType) metaAccess.lookupJavaType(clazz);
type.registerAsReachable("is native library type");
@@ -276,6 +283,10 @@ public MetaAccessProvider getMetaAccess() {
return metaAccess;
}
+ public WordTypes getWordTypes() {
+ return wordTypes;
+ }
+
public SnippetReflectionProvider getSnippetReflection() {
return snippetReflection;
}
@@ -571,19 +582,27 @@ public boolean isPointerBase(ResolvedJavaType type) {
}
public boolean isSigned(ResolvedJavaType type) {
+ if (type.isPrimitive()) {
+ return !type.getJavaKind().isUnsigned();
+ }
+
/*
- * No assignable check, we only go for exact match since Word (which implements Signed,
- * Unsigned, and Pointer) should not match here.
+ * Explicitly filter types that implement UnsignedWord or PointerBase (e.g., Word implements
+ * SignedWord, UnsignedWord, and Pointer).
*/
- return signedType.equals(type);
+ return signedWordType.isAssignableFrom(type) && !unsignedWordType.isAssignableFrom(type) && !pointerBaseType.isAssignableFrom(type);
}
- public boolean isUnsigned(ResolvedJavaType type) {
+ public boolean isIntegerType(ResolvedJavaType type) {
+ if (type.isPrimitive()) {
+ return type.getJavaKind().isNumericInteger();
+ }
+
/*
- * No assignable check, we only go for exact match since Word (which implements Signed,
- * Unsigned, and Pointer) should not match here.
+ * Explicitly filter types that implement PointerBase (e.g., Word implements SignedWord,
+ * UnsignedWord, and Pointer).
*/
- return unsignedType.equals(type);
+ return (signedWordType.isAssignableFrom(type) || unsignedWordType.isAssignableFrom(type)) && !pointerBaseType.isAssignableFrom(type);
}
public boolean isString(ResolvedJavaType type) {
diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/c/OffsetOfSupportImpl.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/c/OffsetOfSupportImpl.java
index 8bd115b1d842..3873f88e5493 100644
--- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/c/OffsetOfSupportImpl.java
+++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/c/OffsetOfSupportImpl.java
@@ -33,21 +33,18 @@
import com.oracle.svm.hosted.c.info.StructFieldInfo;
import com.oracle.svm.hosted.c.info.StructInfo;
-import jdk.vm.ci.meta.MetaAccessProvider;
import jdk.vm.ci.meta.ResolvedJavaType;
public final class OffsetOfSupportImpl implements OffsetOf.Support {
private final NativeLibraries nativeLibraries;
- private final MetaAccessProvider metaAccess;
- public OffsetOfSupportImpl(NativeLibraries nativeLibraries, MetaAccessProvider metaAccess) {
+ public OffsetOfSupportImpl(NativeLibraries nativeLibraries) {
this.nativeLibraries = nativeLibraries;
- this.metaAccess = metaAccess;
}
@Override
public int offsetOf(Class extends PointerBase> clazz, String fieldName) {
- ResolvedJavaType type = metaAccess.lookupJavaType(clazz);
+ ResolvedJavaType type = nativeLibraries.getMetaAccess().lookupJavaType(clazz);
ElementInfo typeInfo = nativeLibraries.findElementInfo(type);
VMError.guarantee(typeInfo instanceof StructInfo, "Class parameter %s of call to %s is not an annotated C struct", type, SizeOf.class.getSimpleName());
StructInfo structInfo = (StructInfo) typeInfo;
diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/c/SizeOfSupportImpl.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/c/SizeOfSupportImpl.java
index 6e3e4f3cf8ac..6444968630dd 100644
--- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/c/SizeOfSupportImpl.java
+++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/c/SizeOfSupportImpl.java
@@ -33,26 +33,23 @@
import com.oracle.svm.hosted.c.info.SizableInfo;
import com.oracle.svm.hosted.c.info.StructInfo;
-import jdk.vm.ci.meta.MetaAccessProvider;
import jdk.vm.ci.meta.ResolvedJavaType;
public final class SizeOfSupportImpl implements SizeOfSupport {
private final NativeLibraries nativeLibraries;
- private final MetaAccessProvider metaAccess;
- public SizeOfSupportImpl(NativeLibraries nativeLibraries, MetaAccessProvider metaAccess) {
+ public SizeOfSupportImpl(NativeLibraries nativeLibraries) {
this.nativeLibraries = nativeLibraries;
- this.metaAccess = metaAccess;
}
@Override
public int sizeof(Class extends PointerBase> clazz) {
- ResolvedJavaType type = metaAccess.lookupJavaType(clazz);
+ ResolvedJavaType type = nativeLibraries.getMetaAccess().lookupJavaType(clazz);
ElementInfo typeInfo = nativeLibraries.findElementInfo(type);
if (typeInfo instanceof StructInfo && ((StructInfo) typeInfo).isIncomplete()) {
throw UserError.abort("Class parameter %s of call to %s is an incomplete structure, so no size is available", type.toJavaName(true), SizeOf.class.getSimpleName());
} else if (typeInfo instanceof SizableInfo) {
- return ((SizableInfo) typeInfo).getSizeInfo().getProperty();
+ return ((SizableInfo) typeInfo).getSizeInBytes();
} else {
throw UserError.abort("Class parameter %s of call to %s is not an annotated C interface type", type.toJavaName(true), SizeOf.class.getSimpleName());
}
diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/c/codegen/QueryCodeWriter.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/c/codegen/QueryCodeWriter.java
index 710506b4ebd6..acb043129620 100644
--- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/c/codegen/QueryCodeWriter.java
+++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/c/codegen/QueryCodeWriter.java
@@ -39,6 +39,7 @@
import com.oracle.svm.hosted.c.info.ConstantInfo;
import com.oracle.svm.hosted.c.info.ElementInfo;
import com.oracle.svm.hosted.c.info.EnumConstantInfo;
+import com.oracle.svm.hosted.c.info.EnumInfo;
import com.oracle.svm.hosted.c.info.InfoTreeVisitor;
import com.oracle.svm.hosted.c.info.NativeCodeInfo;
import com.oracle.svm.hosted.c.info.PointerToInfo;
@@ -279,7 +280,7 @@ protected void visitStructBitfieldInfo(StructBitfieldInfo bitfieldInfo) {
@Override
protected void visitPointerToInfo(PointerToInfo pointerToInfo) {
- String sizeOfExpr = sizeOf(pointerToInfo);
+ String sizeOfExpr;
if (pointerToInfo.getKind() == ElementKind.POINTER && pointerToInfo.getName().startsWith("struct ")) {
/* Eliminate need for struct forward declarations */
sizeOfExpr = "sizeof(void *)";
@@ -307,6 +308,24 @@ protected void visitRawPointerToInfo(RawPointerToInfo pointerToInfo) {
/* Nothing to do, do not visit children. */
}
+ @Override
+ protected void visitEnumInfo(EnumInfo enumInfo) {
+ assert enumInfo.getKind() == ElementKind.INTEGER;
+ printUnsignedLong(enumInfo.getSizeInfo(), sizeOf(enumInfo));
+
+ registerElementForCurrentLine(enumInfo.getAnnotatedElement());
+ writer.indents().appendln("{");
+ writer.indent();
+ writer.indents().appendln(uInt64 + " all_bits_set = -1;");
+ writer.indents().appendln(enumInfo.getName() + " enumHolder = all_bits_set;");
+ writer.indents().appendln("int is_unsigned = enumHolder > 0;");
+ printIsUnsigned(enumInfo.getSignednessInfo(), "is_unsigned");
+ writer.outdent();
+ writer.indents().appendln("}");
+
+ super.visitEnumInfo(enumInfo);
+ }
+
@Override
protected void visitEnumConstantInfo(EnumConstantInfo constantInfo) {
assert constantInfo.getKind() == ElementKind.INTEGER;
diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/c/info/ConstantInfo.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/c/info/ConstantInfo.java
index 826ef5f311bb..b744abd84988 100644
--- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/c/info/ConstantInfo.java
+++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/c/info/ConstantInfo.java
@@ -38,6 +38,10 @@ public ConstantInfo(String name, ElementKind kind, ResolvedJavaMethod annotatedM
this.valueInfo = adoptChild(new PropertyInfo<>("value"));
}
+ public Object getValue() {
+ return valueInfo.getProperty();
+ }
+
public PropertyInfo getValueInfo() {
return valueInfo;
}
diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/c/info/EnumConstantInfo.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/c/info/EnumConstantInfo.java
index d49cabedb34a..4115d5357e60 100644
--- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/c/info/EnumConstantInfo.java
+++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/c/info/EnumConstantInfo.java
@@ -42,6 +42,10 @@ public EnumConstantInfo(String name, ResolvedJavaField annotatedField, boolean i
this.valueInfo = adoptChild(new PropertyInfo<>("value"));
}
+ public long getValue() {
+ return (long) valueInfo.getProperty();
+ }
+
public PropertyInfo getValueInfo() {
return valueInfo;
}
diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/c/info/EnumInfo.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/c/info/EnumInfo.java
index 940b142bdcf9..b748c4cfb96c 100644
--- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/c/info/EnumInfo.java
+++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/c/info/EnumInfo.java
@@ -24,43 +24,63 @@
*/
package com.oracle.svm.hosted.c.info;
-import com.oracle.svm.core.c.enums.EnumRuntimeData;
+import java.util.ArrayList;
+
+import com.oracle.graal.pointsto.meta.AnalysisMethod;
+import com.oracle.svm.core.c.enums.CEnumRuntimeData;
import jdk.vm.ci.meta.ResolvedJavaType;
-public class EnumInfo extends ElementInfo {
+public class EnumInfo extends SizableInfo {
private final ResolvedJavaType annotatedType;
+ private final ArrayList valueMethods = new ArrayList<>();
+ private final ArrayList lookupMethods = new ArrayList<>();
- protected boolean needsLookup;
- private EnumRuntimeData runtimeData;
+ private CEnumRuntimeData runtimeData;
public EnumInfo(String name, ResolvedJavaType annotatedType) {
- super(name);
+ super(name, ElementKind.INTEGER);
this.annotatedType = annotatedType;
}
@Override
- public ResolvedJavaType getAnnotatedElement() {
- return annotatedType;
+ public void accept(InfoTreeVisitor visitor) {
+ visitor.visitEnumInfo(this);
}
- public boolean getNeedsLookup() {
- return needsLookup;
+ @Override
+ public ResolvedJavaType getAnnotatedElement() {
+ return annotatedType;
}
- public EnumRuntimeData getRuntimeData() {
+ public CEnumRuntimeData getRuntimeData() {
assert runtimeData != null;
return runtimeData;
}
- public void setRuntimeData(EnumRuntimeData runtimeData) {
+ public void setRuntimeData(CEnumRuntimeData runtimeData) {
assert this.runtimeData == null;
this.runtimeData = runtimeData;
}
- @Override
- public void accept(InfoTreeVisitor visitor) {
- visitor.visitEnumInfo(this);
+ public Iterable getCEnumValueMethods() {
+ return valueMethods;
+ }
+
+ public Iterable getCEnumLookupMethods() {
+ return lookupMethods;
+ }
+
+ public void addCEnumValueMethod(AnalysisMethod method) {
+ valueMethods.add(method);
+ }
+
+ public void addCEnumLookupMethod(AnalysisMethod method) {
+ lookupMethods.add(method);
+ }
+
+ public boolean hasCEnumLookupMethods() {
+ return !lookupMethods.isEmpty();
}
}
diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/c/info/EnumLookupInfo.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/c/info/EnumLookupInfo.java
deleted file mode 100644
index 35d08109ae28..000000000000
--- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/c/info/EnumLookupInfo.java
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * Copyright (c) 2014, 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. 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 com.oracle.svm.hosted.c.info;
-
-import jdk.vm.ci.meta.ResolvedJavaMethod;
-
-public class EnumLookupInfo extends ElementInfo {
-
- private final ResolvedJavaMethod annotatedMethod;
-
- public EnumLookupInfo(ResolvedJavaMethod annotatedMethod) {
- super(annotatedMethod.getName());
- this.annotatedMethod = annotatedMethod;
- }
-
- @Override
- public ResolvedJavaMethod getAnnotatedElement() {
- return annotatedMethod;
- }
-
- @Override
- public void accept(InfoTreeVisitor visitor) {
- visitor.visitEnumLookupInfo(this);
- }
-}
diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/c/info/EnumValueInfo.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/c/info/EnumValueInfo.java
deleted file mode 100644
index 15ed824a2be7..000000000000
--- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/c/info/EnumValueInfo.java
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * Copyright (c) 2014, 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. 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 com.oracle.svm.hosted.c.info;
-
-import jdk.vm.ci.meta.ResolvedJavaMethod;
-
-public class EnumValueInfo extends ElementInfo {
-
- private final ResolvedJavaMethod annotatedMethod;
-
- public EnumValueInfo(ResolvedJavaMethod annotatedMethod) {
- super(annotatedMethod.getName());
- this.annotatedMethod = annotatedMethod;
- }
-
- @Override
- public ResolvedJavaMethod getAnnotatedElement() {
- return annotatedMethod;
- }
-
- @Override
- public void accept(InfoTreeVisitor visitor) {
- visitor.visitEnumValueInfo(this);
- }
-}
diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/c/info/InfoTreeBuilder.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/c/info/InfoTreeBuilder.java
index 27efaa2fc74c..74d77cfa74f5 100644
--- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/c/info/InfoTreeBuilder.java
+++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/c/info/InfoTreeBuilder.java
@@ -32,8 +32,6 @@
import java.util.Map;
import java.util.TreeMap;
-import jdk.graal.compiler.bytecode.BridgeMethodUtils;
-import jdk.graal.compiler.phases.util.Providers;
import org.graalvm.nativeimage.c.constant.CConstant;
import org.graalvm.nativeimage.c.constant.CEnum;
import org.graalvm.nativeimage.c.constant.CEnumConstant;
@@ -68,6 +66,8 @@
import com.oracle.svm.hosted.cenum.CEnumCallWrapperMethod;
import com.oracle.svm.util.ClassUtil;
+import jdk.graal.compiler.bytecode.BridgeMethodUtils;
+import jdk.graal.compiler.phases.util.Providers;
import jdk.vm.ci.meta.JavaConstant;
import jdk.vm.ci.meta.JavaKind;
import jdk.vm.ci.meta.JavaType;
@@ -139,7 +139,7 @@ protected void createConstantInfo(ResolvedJavaMethod method) {
ResolvedJavaType returnType = AccessorInfo.getReturnType(method);
if (returnType.getJavaKind() == JavaKind.Void ||
(returnType.getJavaKind() == JavaKind.Object && !nativeLibs.isString(returnType) && !nativeLibs.isByteArray(returnType) && !nativeLibs.isWordBase(returnType))) {
- nativeLibs.addError("Wrong return type: expected a primitive type, String, or a Word type; found " + returnType.toJavaName(true), method);
+ nativeLibs.addError("Wrong return type: expected a primitive type, String, byte[], or a Word type; found " + returnType.toJavaName(true), method);
return;
}
@@ -256,11 +256,7 @@ private void createStructInfo(ResolvedJavaType type) {
structAccessorInfos.add(accessorInfo);
} else {
Map> map = bitfieldAnnotation != null ? bitfieldAccessorInfos : fieldAccessorInfos;
- List accessorInfos = map.get(fieldName);
- if (accessorInfos == null) {
- accessorInfos = new ArrayList<>();
- map.put(fieldName, accessorInfos);
- }
+ List accessorInfos = map.computeIfAbsent(fieldName, k -> new ArrayList<>());
accessorInfos.add(accessorInfo);
}
@@ -326,12 +322,7 @@ private void createRawStructInfo(ResolvedJavaType type) {
if (fieldName == null) {
structAccessorInfos.add(accessorInfo);
} else {
- Map> map = fieldAccessorInfos;
- List accessorInfos = map.get(fieldName);
- if (accessorInfos == null) {
- accessorInfos = new ArrayList<>();
- map.put(fieldName, accessorInfos);
- }
+ List accessorInfos = fieldAccessorInfos.computeIfAbsent(fieldName, k -> new ArrayList<>());
accessorInfos.add(accessorInfo);
}
@@ -398,16 +389,13 @@ private ElementKind elementKind(Collection accessorInfos) {
AccessorInfo overallKindAccessor = null;
for (AccessorInfo accessorInfo : accessorInfos) {
- final ResolvedJavaType type;
+ ResolvedJavaType type;
switch (accessorInfo.getAccessorKind()) {
- case GETTER:
- type = accessorInfo.getReturnType();
- break;
- case SETTER:
- type = accessorInfo.getValueParameterType();
- break;
- default:
+ case GETTER -> type = accessorInfo.getReturnType();
+ case SETTER -> type = accessorInfo.getValueParameterType();
+ default -> {
continue;
+ }
}
ResolvedJavaMethod method = accessorInfo.getAnnotatedElement();
@@ -424,18 +412,14 @@ private ElementKind elementKind(Collection accessorInfos) {
private ElementKind elementKind(ResolvedJavaType type, boolean isPinnedObject) {
switch (type.getJavaKind()) {
- case Boolean:
- case Byte:
- case Char:
- case Short:
- case Int:
- case Long:
+ case Boolean, Byte, Char, Short, Int, Long -> {
return ElementKind.INTEGER;
- case Float:
- case Double:
+ }
+ case Float, Double -> {
return ElementKind.FLOAT;
- case Object:
- if (nativeLibs.isSigned(type) || nativeLibs.isUnsigned(type)) {
+ }
+ case Object -> {
+ if (nativeLibs.isIntegerType(type)) {
return ElementKind.INTEGER;
} else if (isPinnedObject) {
return ElementKind.OBJECT;
@@ -446,8 +430,10 @@ private ElementKind elementKind(ResolvedJavaType type, boolean isPinnedObject) {
} else {
return ElementKind.POINTER;
}
- default:
+ }
+ default -> {
return ElementKind.UNKNOWN;
+ }
}
}
@@ -467,8 +453,8 @@ private boolean accessorValid(AccessorInfo accessorInfo) {
if (accessorInfo.isIndexed()) {
ResolvedJavaType paramType = accessorInfo.getParameterType(accessorInfo.indexParameterNumber(false));
- if (paramType.getJavaKind() != JavaKind.Int && paramType.getJavaKind() != JavaKind.Long && !nativeLibs.isSigned(paramType)) {
- nativeLibs.addError("Wrong type of index parameter 0: expected int, long, or Signed; found " + paramType.toJavaName(true), method);
+ if (!nativeLibs.isIntegerType(paramType)) {
+ nativeLibs.addError("Wrong type of index parameter 0: expected an integer type; found " + paramType.toJavaName(true), method);
return false;
}
}
@@ -489,31 +475,39 @@ private boolean accessorValid(AccessorInfo accessorInfo) {
return false;
}
switch (accessorInfo.getAccessorKind()) {
- case ADDRESS:
- if (!nativeLibs.isPointerBase(returnType) || nativeLibs.isSigned(returnType) || nativeLibs.isUnsigned(returnType)) {
+ case ADDRESS -> {
+ if (!nativeLibs.isPointerBase(returnType) || nativeLibs.isIntegerType(returnType)) {
nativeLibs.addError("Wrong return type: expected a pointer type; found " + returnType.toJavaName(true), method);
return false;
}
- break;
- case OFFSET:
- if (!(returnType.getJavaKind().isNumericInteger() || nativeLibs.isUnsigned(returnType))) {
- nativeLibs.addError("Wrong return type: expected an integer numeric type or Unsigned; found " + returnType.toJavaName(true), method);
+ }
+ case OFFSET -> {
+ if (!nativeLibs.isIntegerType(returnType)) {
+ nativeLibs.addError("Wrong return type: expected an integer type; found " + returnType.toJavaName(true), method);
return false;
}
- break;
- case SETTER:
+ }
+ case SETTER -> {
if (!checkObjectType(accessorInfo.getValueParameterType(), method)) {
return false;
}
- break;
+ }
}
return true;
}
- private boolean checkObjectType(ResolvedJavaType returnType, ResolvedJavaMethod method) {
- if (returnType.getJavaKind() == JavaKind.Object && !nativeLibs.isWordBase(returnType) && !isPinnedObjectFieldAccessor(method)) {
- nativeLibs.addError("Wrong type: expected a primitive type or a Word type; found " + returnType.toJavaName(true) + ". Use the annotation @" + PinnedObjectField.class.getSimpleName() +
- " if you know what you are doing.", method);
+ private boolean checkObjectType(ResolvedJavaType type, ResolvedJavaMethod method) {
+ if (type.getJavaKind() == JavaKind.Void) {
+ return true;
+ } else if (type.getJavaKind() == JavaKind.Object && !nativeLibs.isWordBase(type)) {
+ if (!isPinnedObjectFieldAccessor(method)) {
+ nativeLibs.addError("Wrong type: expected a primitive type or a Word type; found " + type.toJavaName(true) + ". Use the annotation @" + PinnedObjectField.class.getSimpleName() +
+ " if you know what you are doing.", method);
+ return false;
+ }
+ } else if (isPinnedObjectFieldAccessor(method)) {
+ nativeLibs.addError("Wrong type: expected an object type; found " + type.toJavaName(true) + ". The annotation @" + PinnedObjectField.class.getSimpleName() +
+ " may only be used for object fields.", method);
return false;
}
return true;
@@ -664,13 +658,12 @@ private void createEnumInfo(ResolvedJavaType type) {
CEnum annotation = type.getAnnotation(CEnum.class);
String name = annotation.value();
- if (!name.isEmpty()) {
- if (annotation.addEnumKeyword()) {
- name = "enum " + name;
- }
- } else {
+ if (name.isEmpty()) {
name = "int";
+ } else if (annotation.addEnumKeyword()) {
+ name = "enum " + name;
}
+
EnumInfo enumInfo = new EnumInfo(name, type);
/* Use the wrapped type to avoid registering all CEnum annotated classes as reachable. */
@@ -681,14 +674,21 @@ private void createEnumInfo(ResolvedJavaType type) {
createEnumConstantInfo(enumInfo, field);
}
}
+
for (ResolvedJavaMethod method : type.getDeclaredMethods(false)) {
- if (getMethodAnnotation(method, CEnumValue.class) != null) {
- createEnumValueInfo(enumInfo, method);
+ AnalysisMethod analysisMethod = (AnalysisMethod) method;
+
+ CEnumValue cEnumValue = getMethodAnnotation(method, CEnumValue.class);
+ if (cEnumValue != null) {
+ addCEnumValueMethod(enumInfo, analysisMethod);
}
- if (getMethodAnnotation(method, CEnumLookup.class) != null) {
- createEnumLookupInfo(enumInfo, method);
+
+ CEnumLookup cEnumLookup = getMethodAnnotation(method, CEnumLookup.class);
+ if (cEnumLookup != null) {
+ addCEnumLookupMethod(enumInfo, analysisMethod);
}
}
+
nativeCodeInfo.adoptChild(enumInfo);
nativeLibs.registerElementInfo(type, enumInfo);
}
@@ -714,58 +714,44 @@ private void createEnumConstantInfo(EnumInfo enumInfo, ResolvedJavaField field)
enumInfo.adoptChild(constantInfo);
}
- private static ResolvedJavaMethod originalMethod(ResolvedJavaMethod method) {
- assert method instanceof AnalysisMethod;
- AnalysisMethod analysisMethod = (AnalysisMethod) method;
- assert analysisMethod.getWrapped() instanceof CEnumCallWrapperMethod;
- CEnumCallWrapperMethod wrapperMethod = (CEnumCallWrapperMethod) analysisMethod.getWrapped();
- return wrapperMethod.getOriginal();
- }
-
- private void createEnumValueInfo(EnumInfo enumInfo, ResolvedJavaMethod method) {
-
+ private void addCEnumValueMethod(EnumInfo enumInfo, AnalysisMethod method) {
/* Check the modifiers of the original method. The synthetic method is not native. */
ResolvedJavaMethod originalMethod = originalMethod(method);
if (!Modifier.isNative(originalMethod.getModifiers()) || Modifier.isStatic(originalMethod.getModifiers())) {
nativeLibs.addError("Method annotated with @" + CEnumValue.class.getSimpleName() + " must be a non-static native method", method);
return;
- }
- if (getParameterCount(method) != 0) {
+ } else if (getParameterCount(method) != 0) {
nativeLibs.addError("Method annotated with @" + CEnumValue.class.getSimpleName() + " cannot have parameters", method);
return;
- }
- ElementKind elementKind = elementKind(AccessorInfo.getReturnType(method), false);
- if (elementKind != ElementKind.INTEGER) {
- nativeLibs.addError("Method annotated with @" + CEnumValue.class.getSimpleName() + " must have an integer return type", method);
+ } else if (!nativeLibs.isIntegerType(AccessorInfo.getReturnType(method))) {
+ nativeLibs.addError("Method annotated with @" + CEnumValue.class.getSimpleName() + " must return an integer type", method);
return;
}
- EnumValueInfo valueInfo = new EnumValueInfo(method);
- enumInfo.adoptChild(valueInfo);
- nativeLibs.registerElementInfo(method, valueInfo);
+ enumInfo.addCEnumValueMethod(method);
}
- private void createEnumLookupInfo(EnumInfo enumInfo, ResolvedJavaMethod method) {
-
+ private void addCEnumLookupMethod(EnumInfo enumInfo, AnalysisMethod method) {
/* Check the modifiers of the original method. The synthetic method is not native. */
ResolvedJavaMethod originalMethod = originalMethod(method);
if (!Modifier.isNative(originalMethod.getModifiers()) || !Modifier.isStatic(originalMethod.getModifiers())) {
nativeLibs.addError("Method annotated with @" + CEnumLookup.class.getSimpleName() + " must be a static native method", method);
return;
- }
- if (getParameterCount(method) != 1 || elementKind(AccessorInfo.getParameterType(method, 0), false) != ElementKind.INTEGER) {
+ } else if (getParameterCount(method) != 1 || elementKind(AccessorInfo.getParameterType(method, 0), false) != ElementKind.INTEGER) {
nativeLibs.addError("Method annotated with @" + CEnumLookup.class.getSimpleName() + " must have exactly one integer parameter", method);
return;
- }
- if (!returnsDeclaringClass(method)) {
+ } else if (!returnsDeclaringClass(method)) {
nativeLibs.addError("Return type of method annotated with @" + CEnumLookup.class.getSimpleName() + " must be the annotation type", method);
return;
}
- enumInfo.needsLookup = true;
- EnumLookupInfo lookupInfo = new EnumLookupInfo(method);
- enumInfo.adoptChild(lookupInfo);
- nativeLibs.registerElementInfo(method, lookupInfo);
+ enumInfo.addCEnumLookupMethod(method);
+ }
+
+ private static ResolvedJavaMethod originalMethod(AnalysisMethod method) {
+ assert method.getWrapped() instanceof CEnumCallWrapperMethod;
+ CEnumCallWrapperMethod wrapperMethod = (CEnumCallWrapperMethod) method.getWrapped();
+ return wrapperMethod.getOriginal();
}
private static T getMethodAnnotation(ResolvedJavaMethod method, Class annotationClass) {
diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/c/info/InfoTreeVisitor.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/c/info/InfoTreeVisitor.java
index 68e15c2fa498..fa754d2be472 100644
--- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/c/info/InfoTreeVisitor.java
+++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/c/info/InfoTreeVisitor.java
@@ -75,7 +75,7 @@ protected final void processChildren(ElementInfo info) {
try {
child.accept(this);
} catch (NumberFormatException e) {
- throw UserError.abort("Missing CAP cache value for: %s", child.getUniqueID());
+ throw UserError.abort(e, "Missing CAP cache value for: %s", child.getUniqueID());
}
}
}
@@ -127,12 +127,4 @@ protected void visitEnumInfo(EnumInfo info) {
protected void visitEnumConstantInfo(EnumConstantInfo info) {
processChildren(info);
}
-
- protected void visitEnumValueInfo(EnumValueInfo info) {
- processChildren(info);
- }
-
- protected void visitEnumLookupInfo(EnumLookupInfo info) {
- processChildren(info);
- }
}
diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/c/info/SizableInfo.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/c/info/SizableInfo.java
index 50bb5086069f..5423585eb959 100644
--- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/c/info/SizableInfo.java
+++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/c/info/SizableInfo.java
@@ -81,9 +81,9 @@ public enum SignednessValue {
public SizableInfo(String name, ElementKind kind) {
super(name);
this.kind = kind;
- this.sizeInfo = adoptChild(new PropertyInfo("size"));
+ this.sizeInfo = adoptChild(new PropertyInfo<>("size"));
if (kind == ElementKind.INTEGER) {
- this.signednessInfo = adoptChild(new PropertyInfo("signedness"));
+ this.signednessInfo = adoptChild(new PropertyInfo<>("signedness"));
} else {
this.signednessInfo = null;
}
@@ -93,6 +93,11 @@ public final ElementKind getKind() {
return kind;
}
+ public final int getSizeInBytes() {
+ return getSizeInfo().getProperty();
+ }
+
+ /** Size in bytes. */
public final PropertyInfo getSizeInfo() {
return sizeInfo;
}
diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/c/info/StructBitfieldInfo.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/c/info/StructBitfieldInfo.java
index e67904c791b4..65412e4effff 100644
--- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/c/info/StructBitfieldInfo.java
+++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/c/info/StructBitfieldInfo.java
@@ -41,10 +41,10 @@ public class StructBitfieldInfo extends ElementInfo {
@SuppressWarnings("this-escape")
public StructBitfieldInfo(String name) {
super(name);
- this.byteOffset = adoptChild(new PropertyInfo("byteOffset"));
- this.startBit = adoptChild(new PropertyInfo("startBit"));
- this.endBit = adoptChild(new PropertyInfo("endBit"));
- this.signednessInfo = adoptChild(new PropertyInfo("signedness"));
+ this.byteOffset = adoptChild(new PropertyInfo<>("byteOffset"));
+ this.startBit = adoptChild(new PropertyInfo<>("startBit"));
+ this.endBit = adoptChild(new PropertyInfo<>("endBit"));
+ this.signednessInfo = adoptChild(new PropertyInfo<>("signedness"));
}
public PropertyInfo getByteOffsetInfo() {
diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/c/query/NativeInfoTreeVisitor.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/c/query/NativeInfoTreeVisitor.java
index 99339b4be089..2c3c6f40cd0c 100644
--- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/c/query/NativeInfoTreeVisitor.java
+++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/c/query/NativeInfoTreeVisitor.java
@@ -26,6 +26,7 @@
import com.oracle.svm.hosted.c.NativeLibraries;
import com.oracle.svm.hosted.c.info.InfoTreeVisitor;
+
import jdk.vm.ci.meta.JavaKind;
import jdk.vm.ci.meta.ResolvedJavaType;
@@ -47,8 +48,4 @@ protected final int getSizeInBytes(JavaKind kind) {
protected final int getSizeInBytes(ResolvedJavaType type) {
return getSizeInBytes(type.getJavaKind());
}
-
- protected final boolean isSigned(ResolvedJavaType type) {
- return nativeLibs.isSigned(type);
- }
}
diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/c/query/QueryParserUtil.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/c/query/QueryParserUtil.java
deleted file mode 100644
index e0b4ca7b95ae..000000000000
--- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/c/query/QueryParserUtil.java
+++ /dev/null
@@ -1,137 +0,0 @@
-/*
- * 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
- * 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 com.oracle.svm.hosted.c.query;
-
-import com.oracle.svm.hosted.c.info.PropertyInfo;
-
-public class QueryParserUtil {
-
- // The size of hex string the represents a long integer
- static final int SIZE_OF_LONG_HEX = 16;
-
- static void parseSigned(PropertyInfo info, String hex) {
- if (isNegative(hex)) {
- if (hex.length() < SIZE_OF_LONG_HEX) {
- String extended = signedExtend(hex);
- parseHexToLong(info, extended);
- } else {
- parseHexToLong(info, hex);
- }
- } else {
- parseHexToLong(info, hex);
- }
- }
-
- /**
- * Only checks the most significant digit in the hexadecimal representation of the integer
- * number. This method converts the most significant digit from String to int and check if its
- * value if greater or equals to 8, if it is the most significant bit is set. Therefore, the
- * integer number is negative.
- *
- * A unsigned integer should not use this method.
- *
- * @param hex hexadecimal string representation of the integer being checked
- * @return true if the hex represents a negative signed integer; no if it does not
- */
- static boolean isNegative(String hex) {
- int mostSignificantDigit = Integer.parseInt(hex.substring(0, 1), 16);
-
- if (mostSignificantDigit >= 8) {
- return true;
- }
-
- return false;
- }
-
- static String unsignedExtend(String hex) {
- int numOfMissingHexDigit = SIZE_OF_LONG_HEX - hex.length();
- StringBuilder sb = new StringBuilder();
-
- for (int i = 0; i < numOfMissingHexDigit; i++) {
- sb.append('0');
- }
-
- return sb.append(hex).toString();
- }
-
- static String unsignedExtendToSize(int sizeInByte, String hex) {
- int numOfMissingHexDigit = sizeInByte * 2 - hex.length();
- StringBuilder sb = new StringBuilder();
-
- for (int i = 0; i < numOfMissingHexDigit; i++) {
- sb.append('0');
- }
-
- return sb.append(hex).toString();
- }
-
- static String signedExtend(String hex) {
- int numOfMissingHexDigit = SIZE_OF_LONG_HEX - hex.length();
- StringBuilder sb = new StringBuilder();
-
- for (int i = 0; i < numOfMissingHexDigit; i++) {
- sb.append('F');
- }
-
- return sb.append(hex).toString();
- }
-
- /**
- * Extends short or int hex to long hex by assigning most significant bits to 0, and then parses
- * the hex string in chunks to avoid the problem that { Long.parseLong } does not deal with
- * two's complement.
- *
- * Signed negative hex should already be signed extended before calling { parseHexToLong }.
- *
- * @param info ValueInfo to be updated
- * @param hex Hex string to be parsed
- */
- static void parseHexToLong(PropertyInfo info, String hex) {
- String extended = unsignedExtend(hex);
-
- try {
- String msb = extended.substring(0, 8);
- String lsb = extended.substring(8);
- long msbValue = Long.parseLong(msb, 16);
- long lsbValue = Long.parseLong(lsb, 16);
- long value = msbValue << 32 | lsbValue;
- info.setProperty(value);
- } catch (NumberFormatException e) {
- parseHexToLongUnsafe(info, hex);
- }
- }
-
- static void parseHexToLongUnsafe(PropertyInfo info, String hex) {
- long value = 0;
-
- for (int i = 0; i < hex.length(); i++) {
- char digit = hex.charAt(i);
- value = 16 * value + Character.getNumericValue(digit);
- }
-
- info.setProperty(value);
- }
-
-}
diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/c/query/QueryResultParser.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/c/query/QueryResultParser.java
index 74a4f0a1b7c4..85529c30b19b 100644
--- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/c/query/QueryResultParser.java
+++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/c/query/QueryResultParser.java
@@ -25,21 +25,23 @@
package com.oracle.svm.hosted.c.query;
import static com.oracle.svm.core.util.VMError.shouldNotReachHereUnexpectedInput;
-import static com.oracle.svm.hosted.c.query.QueryParserUtil.parseHexToLong;
-import static com.oracle.svm.hosted.c.query.QueryParserUtil.parseSigned;
-import static com.oracle.svm.hosted.c.query.QueryParserUtil.unsignedExtendToSize;
import java.io.InputStream;
-import java.nio.charset.Charset;
+import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import org.graalvm.nativeimage.c.constant.CConstant;
+import org.graalvm.nativeimage.c.struct.AllowNarrowingCast;
+import org.graalvm.nativeimage.c.struct.AllowWideningCast;
+
import com.oracle.svm.hosted.c.NativeLibraries;
import com.oracle.svm.hosted.c.info.AccessorInfo;
import com.oracle.svm.hosted.c.info.ConstantInfo;
import com.oracle.svm.hosted.c.info.ElementInfo;
import com.oracle.svm.hosted.c.info.EnumConstantInfo;
+import com.oracle.svm.hosted.c.info.EnumInfo;
import com.oracle.svm.hosted.c.info.NativeCodeInfo;
import com.oracle.svm.hosted.c.info.PointerToInfo;
import com.oracle.svm.hosted.c.info.PropertyInfo;
@@ -53,7 +55,9 @@
import com.oracle.svm.hosted.c.info.StructInfo;
import com.oracle.svm.hosted.c.util.FileUtils;
+import jdk.vm.ci.code.CodeUtil;
import jdk.vm.ci.meta.JavaKind;
+import jdk.vm.ci.meta.ResolvedJavaType;
/**
* Parses query result described in {@link QueryResultFormat}.
@@ -83,47 +87,55 @@ public static List parse(NativeLibraries nativeLibs, NativeCodeInfo nati
@Override
protected void visitConstantInfo(ConstantInfo constantInfo) {
switch (constantInfo.getKind()) {
- case INTEGER:
+ case INTEGER -> {
parseIntegerProperty(constantInfo.getSizeInfo());
parseSignedness(constantInfo.getSignednessInfo());
parseIntegerConstantValue(constantInfo.getValueInfo());
-
- /*
- * From the point of view of the C compiler, plain #define constants have the type
- * int and therefore size 4. But sometimes we want to access such values as short or
- * byte to avoid casts. Check the actual value of the constant, and if it fits the
- * declared type of the constant, then change the actual size to the declared size.
- */
- JavaKind returnKind = AccessorInfo.getReturnType(constantInfo.getAnnotatedElement()).getJavaKind();
- if (returnKind == JavaKind.Object) {
- returnKind = nativeLibs.getTarget().wordJavaKind;
- }
- int declaredSize = getSizeInBytes(returnKind);
- int actualSize = constantInfo.getSizeInfo().getProperty();
- if (declaredSize != actualSize) {
- long value = (long) constantInfo.getValueInfo().getProperty();
- if (value >= returnKind.getMinValue() && value <= returnKind.getMaxValue()) {
- constantInfo.getSizeInfo().setProperty(declaredSize);
- }
- }
-
- break;
- case POINTER:
+ tryChangeSizeToDeclaredReturnType(constantInfo);
+ }
+ case POINTER -> {
parseIntegerProperty(constantInfo.getSizeInfo());
parseIntegerConstantValue(constantInfo.getValueInfo());
- break;
- case FLOAT:
+ }
+ case FLOAT -> {
parseIntegerProperty(constantInfo.getSizeInfo());
parseFloatValue(constantInfo.getValueInfo());
- break;
- case STRING:
- parseStringValue(constantInfo.getValueInfo());
- break;
- case BYTEARRAY:
- parseByteArrayValue(constantInfo.getValueInfo());
- break;
- default:
- throw shouldNotReachHereUnexpectedInput(constantInfo.getKind()); // ExcludeFromJacocoGeneratedReport
+ }
+ case STRING -> parseStringValue(constantInfo.getValueInfo());
+ case BYTEARRAY -> parseByteArrayValue(constantInfo.getValueInfo());
+ default -> throw shouldNotReachHereUnexpectedInput(constantInfo.getKind()); // ExcludeFromJacocoGeneratedReport
+ }
+ }
+
+ /**
+ * When a {@link CConstant} is accessed with a non-matching Java type (e.g., a C int is accessed
+ * as a Java short or Java long), then we try to implicitly cast the constant to the Java type.
+ * This implicit cast avoids the need for {@link AllowNarrowingCast}/{@link AllowWideningCast}.
+ */
+ private void tryChangeSizeToDeclaredReturnType(ConstantInfo constantInfo) {
+ ResolvedJavaType javaReturnType = AccessorInfo.getReturnType(constantInfo.getAnnotatedElement());
+ JavaKind javaReturnKind = nativeLibs.getWordTypes().asKind(javaReturnType);
+ int bytesInJava = javaReturnKind.getByteCount();
+
+ int bytesInC = constantInfo.getSizeInBytes();
+ long cValue = (long) constantInfo.getValue();
+ if (javaReturnKind == JavaKind.Boolean) {
+ /* Casts to boolean are always allowed because we convert the value to 0 or 1. */
+ long newValue = cValue != 0L ? 1L : 0L;
+ constantInfo.getValueInfo().setProperty(newValue);
+ constantInfo.getSizeInfo().setProperty(bytesInJava);
+ } else if (bytesInJava != bytesInC) {
+ /*
+ * Only allow casts if the constant can be represented accurately. Note that a simple
+ * range check is not enough because we store the value of the C constant as a Java
+ * long, which will overflow if we have a large unsigned 64-bit value.
+ */
+ boolean withinRange = cValue >= javaReturnKind.getMinValue() && cValue <= javaReturnKind.getMaxValue();
+ boolean negativeCValueAsUnsigned64BitValue = !constantInfo.isUnsigned() && cValue < 0 && !nativeLibs.isSigned(javaReturnType) && javaReturnKind.getBitCount() == Long.SIZE;
+ boolean largeUnsignedCValue = constantInfo.isUnsigned() && cValue < 0;
+ if (withinRange && !negativeCValueAsUnsigned64BitValue && !largeUnsignedCValue) {
+ constantInfo.getSizeInfo().setProperty(bytesInJava);
+ }
}
}
@@ -172,6 +184,15 @@ public void visitRawPointerToInfo(RawPointerToInfo pointerToInfo) {
/* Nothing to do, do not visit children. */
}
+ @Override
+ protected void visitEnumInfo(EnumInfo info) {
+ assert info.getKind() == ElementKind.INTEGER;
+ parseIntegerProperty(info.getSizeInfo());
+ parseSignedness(info.getSignednessInfo());
+
+ super.visitEnumInfo(info);
+ }
+
@Override
protected void visitEnumConstantInfo(EnumConstantInfo constantInfo) {
assert constantInfo.getKind() == ElementKind.INTEGER;
@@ -185,20 +206,29 @@ private void parseSignedness(PropertyInfo info) {
}
private void parseIntegerConstantValue(PropertyInfo info) {
- boolean isUnsigned = ((SizableInfo) info.getParent()).isUnsigned();
- int size = ((SizableInfo) info.getParent()).getSizeInfo().getProperty();
String hex = idToResult.get(info.getUniqueID());
- int hexSize = hex.length() / 2;
+ long value = parseHexToLong(hex);
- if (hexSize < size) {
- hex = unsignedExtendToSize(size, hex);
+ SizableInfo parent = (SizableInfo) info.getParent();
+ int bitsInC = parent.getSizeInBytes() * Byte.SIZE;
+ if (bitsInC < Long.SIZE) {
+ value = parent.isUnsigned() ? CodeUtil.zeroExtend(value, bitsInC) : CodeUtil.signExtend(value, bitsInC);
}
- if (isUnsigned) {
- parseHexToLong(info, hex);
- } else {
- parseSigned(info, hex);
+ info.setProperty(value);
+ }
+
+ private static long parseHexToLong(String hex) {
+ assert hex.length() <= 16;
+
+ if (hex.length() > 8) {
+ String msb = hex.substring(0, hex.length() - 8);
+ String lsb = hex.substring(hex.length() - 8);
+ long msbValue = Long.parseLong(msb, 16);
+ long lsbValue = Long.parseLong(lsb, 16);
+ return msbValue << 32 | lsbValue;
}
+ return Long.parseLong(hex, 16);
}
private void parseFloatValue(PropertyInfo info) {
@@ -228,7 +258,7 @@ private void parseByteArrayValue(PropertyInfo info) {
private byte[] byteArrayLiteral(ElementInfo info) {
String str = stringLiteral(info);
if (!str.isEmpty()) {
- return str.getBytes(Charset.forName("UTF-8"));
+ return str.getBytes(StandardCharsets.UTF_8);
} else {
return new byte[0];
}
diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/c/query/RawStructureLayoutPlanner.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/c/query/RawStructureLayoutPlanner.java
index 752cec68f8af..5d49627caff3 100644
--- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/c/query/RawStructureLayoutPlanner.java
+++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/c/query/RawStructureLayoutPlanner.java
@@ -86,11 +86,10 @@ protected void visitRawStructureInfo(RawStructureInfo info) {
}
ElementInfo einfo = nativeLibs.findElementInfo(t);
- if (!(einfo instanceof RawStructureInfo)) {
+ if (!(einfo instanceof RawStructureInfo rinfo)) {
throw UserError.abort(new CInterfaceError("Illegal super type " + t + " found", type).getMessage());
}
- RawStructureInfo rinfo = (RawStructureInfo) einfo;
rinfo.accept(this);
assert rinfo.isPlanned();
@@ -101,8 +100,7 @@ protected void visitRawStructureInfo(RawStructureInfo info) {
}
for (ElementInfo child : new ArrayList<>(info.getChildren())) {
- if (child instanceof StructFieldInfo) {
- StructFieldInfo fieldInfo = (StructFieldInfo) child;
+ if (child instanceof StructFieldInfo fieldInfo) {
StructFieldInfo parentFieldInfo = findParentFieldInfo(fieldInfo, info.getParentInfo());
if (parentFieldInfo != null) {
fieldInfo.mergeChildrenAndDelete(parentFieldInfo);
@@ -130,20 +128,16 @@ private void computeSize(StructFieldInfo info) {
* offsets are not calculated before visiting all StructFieldInfos and collecting all
* field types.
*/
- final ResolvedJavaType fieldType;
AccessorInfo accessor = info.getAccessorInfoWithSize();
- switch (accessor.getAccessorKind()) {
- case GETTER:
- fieldType = accessor.getReturnType();
- break;
- case SETTER:
- fieldType = accessor.getValueParameterType();
- break;
- default:
- throw shouldNotReachHere("Unexpected accessor kind " + accessor.getAccessorKind());
- }
+ ResolvedJavaType fieldType = switch (accessor.getAccessorKind()) {
+ case GETTER -> accessor.getReturnType();
+ case SETTER -> accessor.getValueParameterType();
+ default -> throw shouldNotReachHere("Unexpected accessor kind " + accessor.getAccessorKind());
+ };
+
if (info.getKind() == ElementKind.INTEGER) {
- info.getSignednessInfo().setProperty(isSigned(fieldType) ? SignednessValue.SIGNED : SignednessValue.UNSIGNED);
+ SignednessValue signedness = nativeLibs.isSigned(fieldType) ? SignednessValue.SIGNED : SignednessValue.UNSIGNED;
+ info.getSignednessInfo().setProperty(signedness);
}
declaredSize = getSizeInBytes(fieldType);
}
@@ -153,9 +147,9 @@ private void computeSize(StructFieldInfo info) {
/**
* Compute the offsets of each field.
*/
- private void planLayout(RawStructureInfo info) {
+ private static void planLayout(RawStructureInfo info) {
/* Inherit from the parent type. */
- int currentOffset = info.getParentInfo() != null ? info.getParentInfo().getSizeInfo().getProperty() : 0;
+ int currentOffset = info.getParentInfo() != null ? info.getParentInfo().getSizeInBytes() : 0;
List fields = new ArrayList<>();
for (ElementInfo child : info.getChildren()) {
@@ -170,11 +164,11 @@ private void planLayout(RawStructureInfo info) {
* Sort fields in field size descending order. Note that prior to this, the fields are
* already sorted in alphabetical order.
*/
- fields.sort((f1, f2) -> f2.getSizeInfo().getProperty() - f1.getSizeInfo().getProperty());
+ fields.sort((f1, f2) -> f2.getSizeInBytes() - f1.getSizeInBytes());
for (StructFieldInfo finfo : fields) {
assert findParentFieldInfo(finfo, info.getParentInfo()) == null;
- int fieldSize = finfo.getSizeInfo().getProperty();
+ int fieldSize = finfo.getSizeInBytes();
currentOffset = alignOffset(currentOffset, fieldSize);
assert currentOffset % fieldSize == 0;
finfo.getOffsetInfo().setProperty(currentOffset);
@@ -207,7 +201,7 @@ private void planLayout(RawStructureInfo info) {
info.setPlanned();
}
- private StructFieldInfo findParentFieldInfo(StructFieldInfo fieldInfo, RawStructureInfo parentInfo) {
+ private static StructFieldInfo findParentFieldInfo(StructFieldInfo fieldInfo, RawStructureInfo parentInfo) {
if (parentInfo == null) {
return null;
}
@@ -220,8 +214,7 @@ private StructFieldInfo findParentFieldInfo(StructFieldInfo fieldInfo, RawStruct
}
for (ElementInfo child : parentInfo.getChildren()) {
- if (child instanceof StructFieldInfo) {
- StructFieldInfo parentFieldInfo = (StructFieldInfo) child;
+ if (child instanceof StructFieldInfo parentFieldInfo) {
if (fieldInfo.getName().equals(parentFieldInfo.getName())) {
return parentFieldInfo;
}
diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/c/query/SizeAndSignednessVerifier.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/c/query/SizeAndSignednessVerifier.java
index 0dcb8a6cab7f..8c3b4db14d0d 100644
--- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/c/query/SizeAndSignednessVerifier.java
+++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/c/query/SizeAndSignednessVerifier.java
@@ -25,27 +25,30 @@
package com.oracle.svm.hosted.c.query;
import java.lang.annotation.Annotation;
-import java.lang.reflect.Array;
+import java.math.BigInteger;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import org.graalvm.nativeimage.AnnotationAccess;
+import org.graalvm.nativeimage.c.constant.CEnum;
+import org.graalvm.nativeimage.c.constant.CEnumConstant;
import org.graalvm.nativeimage.c.struct.AllowNarrowingCast;
import org.graalvm.nativeimage.c.struct.AllowWideningCast;
import org.graalvm.nativeimage.c.struct.UniqueLocationIdentity;
-import com.oracle.svm.core.c.enums.EnumArrayLookup;
-import com.oracle.svm.core.c.enums.EnumMapLookup;
-import com.oracle.svm.core.c.enums.EnumNoLookup;
-import com.oracle.svm.core.c.enums.EnumRuntimeData;
+import com.oracle.svm.core.c.enums.CEnumArrayLookup;
+import com.oracle.svm.core.c.enums.CEnumMapLookup;
+import com.oracle.svm.core.c.enums.CEnumNoLookup;
+import com.oracle.svm.core.c.enums.CEnumRuntimeData;
import com.oracle.svm.core.c.struct.CInterfaceLocationIdentity;
+import com.oracle.svm.core.option.HostedOptionKey;
import com.oracle.svm.hosted.c.NativeLibraries;
import com.oracle.svm.hosted.c.info.AccessorInfo;
import com.oracle.svm.hosted.c.info.ConstantInfo;
import com.oracle.svm.hosted.c.info.ElementInfo;
import com.oracle.svm.hosted.c.info.EnumConstantInfo;
import com.oracle.svm.hosted.c.info.EnumInfo;
-import com.oracle.svm.hosted.c.info.EnumValueInfo;
import com.oracle.svm.hosted.c.info.NativeCodeInfo;
import com.oracle.svm.hosted.c.info.SizableInfo;
import com.oracle.svm.hosted.c.info.SizableInfo.ElementKind;
@@ -53,6 +56,10 @@
import com.oracle.svm.hosted.c.info.StructFieldInfo;
import com.oracle.svm.util.ClassUtil;
+import jdk.graal.compiler.core.common.NumUtil;
+import jdk.graal.compiler.options.Option;
+import jdk.graal.compiler.options.OptionType;
+import jdk.vm.ci.code.CodeUtil;
import jdk.vm.ci.meta.JavaKind;
import jdk.vm.ci.meta.ResolvedJavaMethod;
import jdk.vm.ci.meta.ResolvedJavaType;
@@ -73,7 +80,7 @@ protected void visitConstantInfo(ConstantInfo constantInfo) {
if (constantInfo.getKind() != ElementKind.STRING && constantInfo.getKind() != ElementKind.BYTEARRAY) {
ResolvedJavaMethod method = constantInfo.getAnnotatedElement();
ResolvedJavaType returnType = AccessorInfo.getReturnType(method);
- checkSizeAndSignedness(constantInfo, returnType, method, true);
+ verifySize(constantInfo, returnType, method, true);
}
}
@@ -95,8 +102,7 @@ protected void visitStructBitfieldInfo(StructBitfieldInfo structBitfieldInfo) {
private void checkAccessorLocationIdentity(List children) {
AccessorInfo firstAccessorInfo = null;
for (ElementInfo child : children) {
- if (child instanceof AccessorInfo) {
- AccessorInfo accessorInfo = (AccessorInfo) child;
+ if (child instanceof AccessorInfo accessorInfo) {
if (accessorInfo.getAccessorKind() != AccessorInfo.AccessorKind.OFFSET) {
if (firstAccessorInfo == null) {
firstAccessorInfo = accessorInfo;
@@ -118,64 +124,60 @@ protected void visitAccessorInfo(AccessorInfo accessorInfo) {
ResolvedJavaType returnType = accessorInfo.getReturnType();
if (accessorInfo.getParent() instanceof StructBitfieldInfo) {
- StructBitfieldInfo bitfieldInfo = (StructBitfieldInfo) accessorInfo.getParent();
- switch (accessorInfo.getAccessorKind()) {
- case GETTER:
- case SETTER:
- checkSignedness(bitfieldInfo.isUnsigned(), returnType, method);
- break;
- default:
- assert false;
- }
+ assert accessorInfo.getAccessorKind() == AccessorInfo.AccessorKind.GETTER || accessorInfo.getAccessorKind() == AccessorInfo.AccessorKind.SETTER;
} else {
SizableInfo sizableInfo = (SizableInfo) accessorInfo.getParent();
switch (accessorInfo.getAccessorKind()) {
- case ADDRESS:
+ case ADDRESS -> {
assert nativeLibs.isPointerBase(returnType);
- break;
- case OFFSET:
- assert returnType.getJavaKind().isNumericInteger() || nativeLibs.isUnsigned(returnType);
- break;
- case GETTER:
- checkSizeAndSignedness(sizableInfo, returnType, method, true);
- break;
- case SETTER:
+ }
+ case OFFSET -> {
+ assert nativeLibs.isIntegerType(returnType);
+ }
+ case GETTER -> {
+ verifySize(sizableInfo, returnType, method, true);
+ verifySignedness(sizableInfo, returnType, method);
+ }
+ case SETTER -> {
assert returnType.getJavaKind() == JavaKind.Void;
ResolvedJavaType valueType = accessorInfo.getValueParameterType();
- checkSizeAndSignedness(sizableInfo, valueType, method, false);
- break;
+ verifySize(sizableInfo, valueType, method, false);
+ verifySignedness(sizableInfo, valueType, method);
+ }
}
}
}
@Override
protected void visitEnumInfo(EnumInfo enumInfo) {
+ verifyCEnumValueMethodReturnTypes(enumInfo);
+ verifyCEnumLookupMethodArguments(enumInfo);
+
+ CEnumRuntimeData runtimeData = createCEnumRuntimeData(enumInfo);
+ enumInfo.setRuntimeData(runtimeData);
+
super.visitEnumInfo(enumInfo);
+ }
+ private CEnumRuntimeData createCEnumRuntimeData(EnumInfo enumInfo) {
Map, Long> javaToC = new HashMap<>();
Map> cToJava = new HashMap<>();
- @SuppressWarnings("rawtypes")
- Class extends Enum> enumClass = null;
long minLookupValue = Long.MAX_VALUE;
long maxLookupValue = Long.MIN_VALUE;
+ /* Collect all the CEnumConstants. */
for (ElementInfo child : enumInfo.getChildren()) {
- if (child instanceof EnumConstantInfo) {
- EnumConstantInfo valueInfo = (EnumConstantInfo) child;
- long cValue = (Long) valueInfo.getValueInfo().getProperty();
+ if (child instanceof EnumConstantInfo valueInfo) {
+ long cValue = limitValueToEnumBytes(enumInfo, valueInfo);
Enum> javaValue = valueInfo.getEnumValue();
+ assert !javaToC.containsKey(javaValue);
+ javaToC.put(javaValue, cValue);
- assert enumClass == null || enumClass == javaValue.getClass();
- enumClass = javaValue.getClass();
-
- assert javaToC.get(javaValue) == null;
- javaToC.put(javaValue, Long.valueOf(cValue));
-
- if (enumInfo.getNeedsLookup() && valueInfo.getIncludeInLookup()) {
- if (cToJava.get(cValue) != null) {
- addError("C value is not unique, so reverse lookup from C to Java is not possible: " + cToJava.get(cValue) + " and " + javaValue + " hava same C value " + cValue,
- valueInfo.getAnnotatedElement());
+ if (enumInfo.hasCEnumLookupMethods() && valueInfo.getIncludeInLookup()) {
+ if (cToJava.containsKey(cValue)) {
+ addError("C value is not unique, so reverse lookup from C to Java is not possible: " + cToJava.get(cValue) + " and " + javaValue + " have the same C value " + cValue + ". " +
+ "Please exclude one of the values from the lookup (see @" + CEnumConstant.class.getSimpleName() + " for more details).", valueInfo.getAnnotatedElement());
}
cToJava.put(cValue, javaValue);
minLookupValue = Math.min(minLookupValue, cValue);
@@ -184,6 +186,7 @@ protected void visitEnumInfo(EnumInfo enumInfo) {
}
}
+ /* Create a long[] that contains the values for the C enum constants. */
long[] javaToCArray = new long[javaToC.size()];
for (Map.Entry, Long> entry : javaToC.entrySet()) {
int idx = entry.getKey().ordinal();
@@ -191,76 +194,126 @@ protected void visitEnumInfo(EnumInfo enumInfo) {
javaToCArray[idx] = entry.getValue();
}
- EnumRuntimeData runtimeData;
- if (cToJava.size() > 0) {
- assert minLookupValue <= maxLookupValue;
- long spread = maxLookupValue - minLookupValue;
- assert spread >= cToJava.size() - 1;
-
- /*
- * We have a choice between an array-based lookup and keeping the HashMap. Since HashMap
- * has a quite high memory footprint, an array is more compact even when most array
- * elements are null.
- */
- if (spread < cToJava.size() * 5L && spread >= 0 && spread < Integer.MAX_VALUE) {
- long offset = minLookupValue;
- Enum>[] cToJavaArray = (Enum[]) Array.newInstance(enumClass, (int) spread + 1);
-
- for (Map.Entry> entry : cToJava.entrySet()) {
- long idx = entry.getKey() - offset;
- assert idx >= 0 && idx < cToJavaArray.length;
- assert cToJavaArray[(int) idx] == null;
- cToJavaArray[(int) idx] = entry.getValue();
- }
+ int enumBytesInC = enumInfo.getSizeInBytes();
+ boolean isCEnumUnsigned = enumInfo.isUnsigned();
+ if (cToJava.isEmpty()) {
+ return new CEnumNoLookup(javaToCArray, enumBytesInC, isCEnumUnsigned);
+ }
- runtimeData = new EnumArrayLookup(javaToCArray, offset, cToJavaArray);
- } else {
- runtimeData = new EnumMapLookup(javaToCArray, cToJava);
+ /* Compute how spread out the C enum values are. */
+ assert minLookupValue <= maxLookupValue;
+ BigInteger bigIntSpread = BigInteger.valueOf(maxLookupValue).subtract(BigInteger.valueOf(minLookupValue));
+ assert bigIntSpread.compareTo(BigInteger.valueOf(cToJava.size() - 1)) >= 0;
+ long spread = bigIntSpread.compareTo(BigInteger.valueOf(Long.MAX_VALUE)) > 0 ? Long.MAX_VALUE : bigIntSpread.longValue();
+
+ /*
+ * Decide between array- and hash-map-based lookup. A hash map has a high memory footprint,
+ * so an array is more compact even if most elements are null.
+ */
+ if (spread < cToJava.size() * 5L && spread < Integer.MAX_VALUE - 10) {
+ int arraySize = NumUtil.safeToInt(spread + 1);
+ Enum>[] cToJavaArray = new Enum>[arraySize];
+ for (Map.Entry> entry : cToJava.entrySet()) {
+ int idx = NumUtil.safeToInt(entry.getKey() - minLookupValue);
+ assert idx >= 0 && idx < cToJavaArray.length;
+ assert cToJavaArray[idx] == null;
+ cToJavaArray[idx] = entry.getValue();
}
- } else {
- runtimeData = new EnumNoLookup(javaToCArray);
+ return new CEnumArrayLookup(javaToCArray, enumBytesInC, isCEnumUnsigned, minLookupValue, cToJavaArray);
}
- enumInfo.setRuntimeData(runtimeData);
+ return new CEnumMapLookup(javaToCArray, enumBytesInC, isCEnumUnsigned, Map.copyOf(cToJava));
}
- @Override
- protected void visitEnumValueInfo(EnumValueInfo valueInfo) {
- ResolvedJavaMethod method = valueInfo.getAnnotatedElement();
- ResolvedJavaType returnType = AccessorInfo.getReturnType(method);
-
- EnumInfo enumInfo = (EnumInfo) valueInfo.getParent();
- for (ElementInfo info : enumInfo.getChildren()) {
- if (info instanceof EnumConstantInfo) {
- EnumConstantInfo constantInfo = (EnumConstantInfo) info;
- checkSizeAndSignedness(constantInfo, returnType, method, true);
- }
+ private void verifyCEnumValueMethodReturnTypes(EnumInfo enumInfo) {
+ for (ResolvedJavaMethod method : enumInfo.getCEnumValueMethods()) {
+ ResolvedJavaType returnType = AccessorInfo.getReturnType(method);
+ verifySize(enumInfo, returnType, method, true);
}
}
- private void checkSizeAndSignedness(SizableInfo sizableInfo, ResolvedJavaType type, ResolvedJavaMethod method, boolean isReturn) {
+ private void verifyCEnumLookupMethodArguments(EnumInfo enumInfo) {
+ for (ResolvedJavaMethod method : enumInfo.getCEnumLookupMethods()) {
+ ResolvedJavaType arg0 = AccessorInfo.getParameterType(method, 0);
+ verifySize(enumInfo, arg0, method, false);
+ }
+ }
+
+ private void verifySize(SizableInfo sizableInfo, ResolvedJavaType type, ResolvedJavaMethod method, boolean isReturn) {
int declaredSize = getSizeInBytes(type);
- int actualSize = sizableInfo.isObject() ? getSizeInBytes(JavaKind.Object) : sizableInfo.getSizeInfo().getProperty();
+
+ boolean allowNarrowingCast = AnnotationAccess.isAnnotationPresent(method, AllowNarrowingCast.class);
+ if (allowNarrowingCast) {
+ if (sizableInfo.isObject()) {
+ addError(ClassUtil.getUnqualifiedName(AllowNarrowingCast.class) + " cannot be used on fields that have an object type.", method);
+ } else if (sizableInfo.getKind() == ElementKind.FLOAT) {
+ addError(ClassUtil.getUnqualifiedName(AllowNarrowingCast.class) + " cannot be used on fields of type float or double.", method);
+ }
+ }
+
+ boolean allowWideningCast = AnnotationAccess.isAnnotationPresent(method, AllowWideningCast.class);
+ if (allowWideningCast) {
+ if (sizableInfo.isObject()) {
+ addError(ClassUtil.getUnqualifiedName(AllowWideningCast.class) + " cannot be used on fields that have an object type.", method);
+ } else if (sizableInfo.getKind() == ElementKind.FLOAT) {
+ addError(ClassUtil.getUnqualifiedName(AllowWideningCast.class) + " cannot be used on fields of type float or double.", method);
+ }
+ }
+
+ int actualSize = sizableInfo.isObject() ? getSizeInBytes(JavaKind.Object) : sizableInfo.getSizeInBytes();
if (declaredSize != actualSize) {
- Class extends Annotation> supressionAnnotation = (declaredSize > actualSize) ^ isReturn ? AllowNarrowingCast.class : AllowWideningCast.class;
- if (method.getAnnotation(supressionAnnotation) == null) {
+ boolean narrow = declaredSize > actualSize;
+ if (isReturn) {
+ narrow = !narrow;
+ }
+
+ Class extends Annotation> suppressionAnnotation = narrow ? AllowNarrowingCast.class : AllowWideningCast.class;
+ if (method.getAnnotation(suppressionAnnotation) == null) {
addError("Type " + type.toJavaName(false) + " has a size of " + declaredSize + " bytes, but accessed C value has a size of " + actualSize +
- " bytes; to suppress this error, use the annotation @" + ClassUtil.getUnqualifiedName(supressionAnnotation), method);
+ " bytes; to suppress this error, use the annotation @" + ClassUtil.getUnqualifiedName(suppressionAnnotation), method);
}
}
-
- checkSignedness(sizableInfo.isUnsigned(), type, method);
}
- private void checkSignedness(boolean isUnsigned, ResolvedJavaType type, ResolvedJavaMethod method) {
- if (isSigned(type)) {
- if (isUnsigned) {
- addError("Type " + type.toJavaName(false) + " is signed, but accessed C value is unsigned", method);
- }
- } else if (nativeLibs.isWordBase(type)) {
- /* every Word type other than Signed is assumed to be unsigned. */
- if (!isUnsigned) {
- addError("Type " + type.toJavaName(false) + " is unsigned, but accessed C value is signed", method);
+ private void verifySignedness(SizableInfo sizableInfo, ResolvedJavaType type, ResolvedJavaMethod method) {
+ if (Options.VerifyCInterfaceSignedness.getValue() && sizableInfo.getKind() == ElementKind.INTEGER) {
+ boolean isJavaTypeSigned = nativeLibs.isSigned(type);
+ boolean isCTypeSigned = sizableInfo.getSignednessInfo().getProperty() == SizableInfo.SignednessValue.SIGNED;
+ if (isJavaTypeSigned != isCTypeSigned) {
+ addError("Java type " + type.toJavaName(false) + " is " + (isJavaTypeSigned ? "signed" : "unsigned") +
+ ", while the accessed C value is " + (isCTypeSigned ? "signed" : "unsigned"), method);
}
}
}
+
+ /**
+ * The value of the constant was already sign- or zero-extended to 64-bit when it was parsed
+ * (based on the size and signedness of the constant itself). However, it can happen that the
+ * data type of the C enum is smaller than the data type of the constant. Therefore, we
+ * explicitly limit the bits of the constant to the bits that fit into the type of the C enum.
+ */
+ private long limitValueToEnumBytes(EnumInfo enumInfo, EnumConstantInfo constantInfo) {
+ int enumBits = enumInfo.getSizeInBytes() * Byte.SIZE;
+
+ long result = constantInfo.getValue();
+ if (constantInfo.isUnsigned()) {
+ result = CodeUtil.zeroExtend(result, enumBits);
+ } else {
+ result = CodeUtil.signExtend(result, enumBits);
+ }
+
+ if (result != constantInfo.getValue()) {
+ addError("The value of a C constant does not fit into the C data type of the @" + CEnum.class.getSimpleName() + " ('" + enumInfo.getName() + "'). " +
+ "Please specify a larger C data type in @" + CEnum.class.getSimpleName() + "(value = \"...\").", enumInfo, constantInfo);
+ }
+ return result;
+ }
+
+ public static class Options {
+ /**
+ * Only verifies the signedness of accessors at the moment. The signedness of constants and
+ * enums in C is often just up to the compiler and therefore not particularly useful.
+ */
+ @Option(help = "Verify the signedness of Java and C types that are used in the C interface.", type = OptionType.Debug)//
+ public static final HostedOptionKey VerifyCInterfaceSignedness = new HostedOptionKey<>(false);
+ }
}
diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/c/query/package-info.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/c/query/package-info.java
deleted file mode 100644
index 8b38f07bf5a1..000000000000
--- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/c/query/package-info.java
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * Copyright (c) 2019, 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. 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.
- */
-
-@Platforms(Platform.HOSTED_ONLY.class)
-package com.oracle.svm.hosted.c.query;
-
-import org.graalvm.nativeimage.Platform;
-import org.graalvm.nativeimage.Platforms;
diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/c/util/package-info.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/c/util/package-info.java
deleted file mode 100644
index 61218bffdf0b..000000000000
--- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/c/util/package-info.java
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * Copyright (c) 2019, 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. 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.
- */
-
-@Platforms(Platform.HOSTED_ONLY.class)
-package com.oracle.svm.hosted.c.util;
-
-import org.graalvm.nativeimage.Platform;
-import org.graalvm.nativeimage.Platforms;
diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/cenum/CEnumCallWrapperMethod.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/cenum/CEnumCallWrapperMethod.java
index 2c41f1f186da..85625c6b5a86 100644
--- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/cenum/CEnumCallWrapperMethod.java
+++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/cenum/CEnumCallWrapperMethod.java
@@ -40,10 +40,7 @@
import com.oracle.svm.hosted.phases.CInterfaceInvocationPlugin;
import com.oracle.svm.hosted.phases.HostedGraphKit;
-import jdk.graal.compiler.core.common.type.Stamp;
-import jdk.graal.compiler.core.common.type.StampFactory;
import jdk.graal.compiler.debug.DebugContext;
-import jdk.graal.compiler.nodes.NodeView;
import jdk.graal.compiler.nodes.StructuredGraph;
import jdk.graal.compiler.nodes.ValueNode;
import jdk.vm.ci.meta.JavaKind;
@@ -51,8 +48,8 @@
import jdk.vm.ci.meta.ResolvedJavaType;
/**
- * Generated code for patching {@link CEnumLookup} annotated methods and calling
- * EnumRuntimeData.convertCToJava(long).
+ * Create a synthetic graph to substitute native methods that are annotated with {@link CEnumLookup}
+ * or {@link CEnumValue}.
*/
public class CEnumCallWrapperMethod extends CustomSubstitutionMethod {
@@ -76,33 +73,30 @@ public boolean isSynthetic() {
@Override
public StructuredGraph buildGraph(DebugContext debug, AnalysisMethod method, HostedProviders providers, Purpose purpose) {
HostedGraphKit kit = new HostedGraphKit(debug, providers, method);
-
AnalysisType returnType = method.getSignature().getReturnType();
ValueNode arg = kit.getInitialArguments().get(0);
- CInterfaceEnumTool tool = new CInterfaceEnumTool(kit.getMetaAccess());
+ ValueNode returnValue = createInvoke(method, kit, returnType, arg);
JavaKind pushKind = CInterfaceInvocationPlugin.pushKind(method);
- ValueNode returnValue;
+ kit.getFrameState().push(pushKind, returnValue);
+ kit.createReturn(returnValue, pushKind);
+
+ return kit.finalizeGraph();
+ }
+
+ private ValueNode createInvoke(AnalysisMethod method, HostedGraphKit kit, AnalysisType returnType, ValueNode arg) {
if (method.getAnnotation(CEnumLookup.class) != null) {
+ /* Call a method that converts the primitive value to a Java enum. */
EnumInfo enumInfo = (EnumInfo) nativeLibraries.findElementInfo(returnType);
- JavaKind parameterKind = JavaKind.Int;
- returnValue = tool.createEnumLookupInvoke(kit, returnType, enumInfo, parameterKind, arg);
+ return CInterfaceEnumTool.singleton().createInvokeLookupEnum(kit, returnType, enumInfo, arg);
} else if (method.getAnnotation(CEnumValue.class) != null) {
+ /* Call a method that converts a Java enum to a primitive value. */
ResolvedJavaType declaringType = method.getDeclaringClass();
EnumInfo enumInfo = (EnumInfo) nativeLibraries.findElementInfo(declaringType);
- ValueNode invoke = tool.createEnumValueInvoke(kit, enumInfo, returnType.getJavaKind(), arg);
-
- ValueNode adapted = CInterfaceInvocationPlugin.adaptPrimitiveType(kit.getGraph(), invoke, invoke.stamp(NodeView.DEFAULT).getStackKind(), returnType.getJavaKind(), false);
- Stamp originalStamp = StampFactory.forKind(returnType.getJavaKind());
- returnValue = CInterfaceInvocationPlugin.adaptPrimitiveType(kit.getGraph(), adapted, returnType.getJavaKind(), originalStamp.getStackKind(), false);
- } else {
- throw VMError.shouldNotReachHereUnexpectedInput(method); // ExcludeFromJacocoGeneratedReport
+ return CInterfaceEnumTool.singleton().createInvokeEnumToValue(kit, enumInfo, returnType, arg);
}
- kit.getFrameState().push(pushKind, returnValue);
- kit.createReturn(returnValue, pushKind);
-
- return kit.finalizeGraph();
+ throw VMError.shouldNotReachHereUnexpectedInput(method); // ExcludeFromJacocoGeneratedReport
}
}
diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/cenum/CEnumCallWrapperSubstitutionProcessor.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/cenum/CEnumCallWrapperSubstitutionProcessor.java
index 7a17b8abdcbd..68b423f16a96 100644
--- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/cenum/CEnumCallWrapperSubstitutionProcessor.java
+++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/cenum/CEnumCallWrapperSubstitutionProcessor.java
@@ -38,7 +38,7 @@
/**
* Substitutes methods declared as {@code native} with {@link CEnumLookup} annotation with a
- * synthetic graph that calls the appropriate EnumRuntimeData.convertCToJava(long) method.
+ * synthetic graph (see {@link CEnumCallWrapperMethod}).
*/
public class CEnumCallWrapperSubstitutionProcessor extends SubstitutionProcessor {
diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/cenum/package-info.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/cenum/package-info.java
deleted file mode 100644
index 6f2e01a41ca6..000000000000
--- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/cenum/package-info.java
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * Copyright (c) 2019, 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. 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.
- */
-
-@Platforms(Platform.HOSTED_ONLY.class)
-package com.oracle.svm.hosted.cenum;
-
-import org.graalvm.nativeimage.Platform;
-import org.graalvm.nativeimage.Platforms;
diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/code/CCallStubMethod.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/code/CCallStubMethod.java
index d8acd0017412..a3910fbfb645 100644
--- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/code/CCallStubMethod.java
+++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/code/CCallStubMethod.java
@@ -42,7 +42,6 @@
import com.oracle.svm.hosted.c.NativeLibraries;
import com.oracle.svm.hosted.c.info.ElementInfo;
import com.oracle.svm.hosted.c.info.EnumInfo;
-import com.oracle.svm.hosted.c.info.EnumLookupInfo;
import com.oracle.svm.hosted.phases.CInterfaceEnumTool;
import com.oracle.svm.hosted.phases.HostedGraphKit;
@@ -50,13 +49,9 @@
import jdk.graal.compiler.java.FrameStateBuilder;
import jdk.graal.compiler.nodes.StructuredGraph;
import jdk.graal.compiler.nodes.ValueNode;
-import jdk.vm.ci.meta.JavaKind;
-import jdk.vm.ci.meta.JavaType;
import jdk.vm.ci.meta.ResolvedJavaMethod;
public abstract class CCallStubMethod extends CustomSubstitutionMethod {
- private static final JavaKind cEnumKind = JavaKind.Int;
-
protected final int newThreadStatus;
CCallStubMethod(ResolvedJavaMethod original, int newThreadStatus) {
@@ -68,7 +63,7 @@ public abstract class CCallStubMethod extends CustomSubstitutionMethod {
@Override
public StructuredGraph buildGraph(DebugContext debug, AnalysisMethod method, HostedProviders providers, Purpose purpose) {
- NativeLibraries nativeLibraries = CEntryPointCallStubSupport.singleton().getNativeLibraries();
+ NativeLibraries nativeLibraries = NativeLibraries.singleton();
boolean deoptimizationTarget = MultiMethod.isDeoptTarget(method);
HostedGraphKit kit = new HostedGraphKit(debug, providers, method);
FrameStateBuilder state = kit.getFrameState();
@@ -96,57 +91,67 @@ public StructuredGraph buildGraph(DebugContext debug, AnalysisMethod method, Hos
protected abstract ValueNode createTargetAddressNode(HostedGraphKit kit, List arguments);
- protected static boolean isPrimitiveOrWord(HostedGraphKit kit, JavaType type) {
- return type.getJavaKind().isPrimitive() || kit.getWordTypes().isWord(type);
- }
-
+ /**
+ * The signature may contain Java object types. The only Java object types that we support at
+ * the moment are Java enums (annotated with @CEnum). This method replaces all Java enums with
+ * suitable primitive types.
+ */
protected ResolvedSignature adaptSignatureAndConvertArguments(NativeLibraries nativeLibraries,
HostedGraphKit kit, @SuppressWarnings("unused") AnalysisMethod method, AnalysisType returnType, AnalysisType[] parameterTypes, List arguments) {
-
for (int i = 0; i < parameterTypes.length; i++) {
- if (!isPrimitiveOrWord(kit, parameterTypes[i])) {
- ElementInfo typeInfo = nativeLibraries.findElementInfo(parameterTypes[i]);
- if (typeInfo instanceof EnumInfo) {
- ValueNode argumentValue = kit.maybeCreateExplicitNullCheck(arguments.get(i));
- CInterfaceEnumTool tool = new CInterfaceEnumTool(kit.getMetaAccess());
- argumentValue = tool.createEnumValueInvoke(kit, (EnumInfo) typeInfo, cEnumKind, argumentValue);
-
- arguments.set(i, argumentValue);
- parameterTypes[i] = kit.getMetaAccess().lookupJavaType(cEnumKind.toJavaClass());
- } else {
- throw UserError.abort("@%s parameter types are restricted to primitive types, word types and enumerations (@%s): %s",
- getCorrespondingAnnotationName(), CEnum.class.getSimpleName(), getOriginal());
- }
+ if (!CInterfaceEnumTool.isPrimitiveOrWord(parameterTypes[i])) {
+ /* Replace the CEnum with the corresponding primitive type. */
+ EnumInfo enumInfo = getEnumInfo(nativeLibraries, parameterTypes[i], false);
+ ValueNode argumentValue = kit.maybeCreateExplicitNullCheck(arguments.get(i));
+
+ AnalysisType cValueType = CInterfaceEnumTool.getCEnumValueType(enumInfo, kit.getMetaAccess());
+ argumentValue = CInterfaceEnumTool.singleton().createInvokeEnumToValue(kit, enumInfo, cValueType, argumentValue);
+
+ arguments.set(i, argumentValue);
+ parameterTypes[i] = cValueType;
}
}
- /* Actual checks and conversion are in adaptReturnValue() */
- AnalysisType actualReturnType = isPrimitiveOrWord(kit, returnType) ? returnType : (AnalysisType) kit.getWordTypes().getWordImplType();
- return ResolvedSignature.fromArray(parameterTypes, actualReturnType);
+
+ AnalysisType patchedReturnType = returnType;
+ if (!CInterfaceEnumTool.isPrimitiveOrWord(patchedReturnType)) {
+ /*
+ * The return type is a @CEnum. Change the return type to Word because the C function
+ * call will return some primitive value (and checks expect Word type replacements).
+ * adaptReturnValue() below does the actual conversion from the primitive value to the
+ * Java object.
+ */
+ assert getEnumInfo(nativeLibraries, patchedReturnType, true) != null;
+ patchedReturnType = (AnalysisType) kit.getWordTypes().getWordImplType();
+ }
+ return ResolvedSignature.fromArray(parameterTypes, patchedReturnType);
}
- private ValueNode adaptReturnValue(AnalysisMethod method, NativeLibraries nativeLibraries, HostedGraphKit kit, ValueNode invokeValue) {
- ValueNode returnValue = invokeValue;
+ private ValueNode adaptReturnValue(AnalysisMethod method, NativeLibraries nativeLibraries, HostedGraphKit kit, ValueNode value) {
AnalysisType declaredReturnType = method.getSignature().getReturnType();
- if (isPrimitiveOrWord(kit, declaredReturnType)) {
- return returnValue;
+ if (CInterfaceEnumTool.isPrimitiveOrWord(declaredReturnType)) {
+ return value;
}
- ElementInfo typeInfo = nativeLibraries.findElementInfo(declaredReturnType);
- if (typeInfo instanceof EnumInfo) {
- UserError.guarantee(typeInfo.getChildren().stream().anyMatch(EnumLookupInfo.class::isInstance),
- "Enum class %s needs a method that is annotated with @%s because it is used as the return type of a method annotated with @%s: %s",
- declaredReturnType,
- CEnumLookup.class.getSimpleName(),
- getCorrespondingAnnotationName(),
- getOriginal());
-
- // We take a word return type because checks expect word type replacements, but it is
- // narrowed to cEnumKind here.
- CInterfaceEnumTool tool = new CInterfaceEnumTool(kit.getMetaAccess());
- returnValue = tool.createEnumLookupInvoke(kit, declaredReturnType, (EnumInfo) typeInfo, cEnumKind, returnValue);
- } else {
+
+ /* The C function call returned a primitive value. Now, we convert it to a Java enum. */
+ EnumInfo enumInfo = getEnumInfo(nativeLibraries, declaredReturnType, true);
+ UserError.guarantee(enumInfo.hasCEnumLookupMethods(),
+ "Enum class %s needs a method that is annotated with @%s because it is used as the return type of a method annotated with @%s: %s.",
+ declaredReturnType, CEnumLookup.class.getSimpleName(), getCorrespondingAnnotationName(), getOriginal());
+
+ return CInterfaceEnumTool.singleton().createInvokeLookupEnum(kit, declaredReturnType, enumInfo, value);
+ }
+
+ private EnumInfo getEnumInfo(NativeLibraries nativeLibraries, AnalysisType type, boolean isReturnType) {
+ ElementInfo typeInfo = nativeLibraries.findElementInfo(type);
+ if (typeInfo instanceof EnumInfo enumInfo) {
+ return enumInfo;
+ }
+
+ if (isReturnType) {
throw UserError.abort("Return types of methods annotated with @%s are restricted to primitive types, word types and enumerations (@%s): %s",
getCorrespondingAnnotationName(), CEnum.class.getSimpleName(), getOriginal());
}
- return returnValue;
+ throw UserError.abort("@%s parameter types are restricted to primitive types, word types and enumerations (@%s): %s",
+ getCorrespondingAnnotationName(), CEnum.class.getSimpleName(), getOriginal());
}
}
diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/code/CEntryPointCallStubMethod.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/code/CEntryPointCallStubMethod.java
index 24027256ec0f..efa51ff354c2 100644
--- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/code/CEntryPointCallStubMethod.java
+++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/code/CEntryPointCallStubMethod.java
@@ -38,6 +38,7 @@
import org.graalvm.nativeimage.c.function.CEntryPoint.IsolateContext;
import org.graalvm.nativeimage.c.function.CEntryPoint.IsolateThreadContext;
+import com.oracle.graal.pointsto.BigBang;
import com.oracle.graal.pointsto.infrastructure.ResolvedSignature;
import com.oracle.graal.pointsto.meta.AnalysisMetaAccess;
import com.oracle.graal.pointsto.meta.AnalysisMethod;
@@ -63,7 +64,6 @@
import com.oracle.svm.hosted.c.NativeLibraries;
import com.oracle.svm.hosted.c.info.ElementInfo;
import com.oracle.svm.hosted.c.info.EnumInfo;
-import com.oracle.svm.hosted.c.info.EnumLookupInfo;
import com.oracle.svm.hosted.phases.CInterfaceEnumTool;
import com.oracle.svm.hosted.phases.HostedGraphKit;
@@ -93,46 +93,53 @@
import jdk.vm.ci.meta.ResolvedJavaType;
public final class CEntryPointCallStubMethod extends EntryPointCallStubMethod {
- static CEntryPointCallStubMethod create(AnalysisMethod targetMethod, CEntryPointData entryPointData, AnalysisMetaAccess aMetaAccess) {
+ static CEntryPointCallStubMethod create(BigBang bb, AnalysisMethod targetMethod, CEntryPointData entryPointData) {
MetaAccessProvider originalMetaAccess = GraalAccess.getOriginalProviders().getMetaAccess();
ResolvedJavaType declaringClass = originalMetaAccess.lookupJavaType(IsolateEnterStub.class);
ConstantPool constantPool = IsolateEnterStub.getConstantPool(originalMetaAccess);
- return new CEntryPointCallStubMethod(entryPointData, targetMethod, declaringClass, constantPool, aMetaAccess.getUniverse().getWordKind(), originalMetaAccess);
+ return new CEntryPointCallStubMethod(entryPointData, targetMethod, declaringClass, constantPool, bb.getMetaAccess());
}
- private static final JavaKind cEnumParameterKind = JavaKind.Int;
-
private final CEntryPointData entryPointData;
private final ResolvedJavaMethod targetMethod;
private final ResolvedSignature targetSignature;
- private CEntryPointCallStubMethod(CEntryPointData entryPointData, AnalysisMethod targetMethod, ResolvedJavaType holderClass, ConstantPool holderConstantPool, JavaKind wordKind,
- MetaAccessProvider metaAccess) {
- super(SubstrateUtil.uniqueStubName(targetMethod.getWrapped()), holderClass, createSignature(targetMethod, wordKind, metaAccess), holderConstantPool);
+ private CEntryPointCallStubMethod(CEntryPointData entryPointData, AnalysisMethod targetMethod, ResolvedJavaType holderClass, ConstantPool holderConstantPool, AnalysisMetaAccess metaAccess) {
+ super(SubstrateUtil.uniqueStubName(targetMethod.getWrapped()), holderClass, createSignature(targetMethod, metaAccess), holderConstantPool);
this.entryPointData = entryPointData;
this.targetMethod = targetMethod.getWrapped();
this.targetSignature = targetMethod.getSignature();
}
/**
- * This method creates a new signature for the stub in which all @CEnum values are converted
- * into their corresponding primitive type. In correspondence to how the @CEnum values are
- * actually handled, parameters are transformed to the type specified by cEnumParameterKind and
- * return type is transformed into the word type.
- *
- * @see CEnum
- * @see CEntryPointCallStubMethod#adaptParameterTypes
- * @see CEntryPointCallStubMethod#adaptReturnValue(HostedGraphKit, ValueNode)
+ * This method creates a new signature for the stub in which all {@link CEnum} values are
+ * converted to suitable primitive types.
*/
- private static ResolvedSignature createSignature(AnalysisMethod targetMethod, JavaKind wordKind, MetaAccessProvider metaAccess) {
- ResolvedJavaType[] paramTypes = targetMethod.toParameterList().stream()
- .map(type -> type.getAnnotation(CEnum.class) != null ? metaAccess.lookupJavaType(cEnumParameterKind.toJavaClass()) : type.getWrapped())
- .toArray(ResolvedJavaType[]::new);
- ResolvedJavaType returnType = targetMethod.getSignature().getReturnType().getWrapped();
- if (returnType.getAnnotation(CEnum.class) != null) {
- returnType = metaAccess.lookupJavaType(wordKind.toJavaClass());
+ private static ResolvedSignature createSignature(AnalysisMethod method, AnalysisMetaAccess metaAccess) {
+ NativeLibraries nativeLibraries = NativeLibraries.singleton();
+ AnalysisType[] args = method.toParameterList().toArray(AnalysisType[]::new);
+
+ ResolvedJavaType[] patchedArgs = new ResolvedJavaType[args.length];
+ for (int i = 0; i < args.length; i++) {
+ if (CInterfaceEnumTool.isPrimitiveOrWord(args[i])) {
+ patchedArgs[i] = args[i].getWrapped();
+ } else {
+ /* Replace the CEnum with the corresponding primitive type. */
+ EnumInfo enumInfo = getEnumInfo(nativeLibraries, method, args[i], false);
+ patchedArgs[i] = CInterfaceEnumTool.getCEnumValueType(enumInfo, metaAccess).getWrapped();
+ }
+ }
+
+ AnalysisType patchedReturnType = method.getSignature().getReturnType();
+ if (!CInterfaceEnumTool.isPrimitiveOrWord(patchedReturnType)) {
+ /*
+ * The return type is a @CEnum. Change the return type to Word because the C entry point
+ * will return some primitive value.
+ */
+ assert getEnumInfo(nativeLibraries, method, patchedReturnType, true) != null;
+ patchedReturnType = (AnalysisType) nativeLibraries.getWordTypes().getWordImplType();
}
- return ResolvedSignature.fromArray(paramTypes, returnType);
+ return ResolvedSignature.fromArray(patchedArgs, patchedReturnType.getWrapped());
}
@Override
@@ -151,14 +158,12 @@ public StructuredGraph buildGraph(DebugContext debug, AnalysisMethod method, Hos
}
HostedGraphKit kit = new HostedGraphKit(debug, providers, method);
- NativeLibraries nativeLibraries = CEntryPointCallStubSupport.singleton().getNativeLibraries();
+ NativeLibraries nativeLibraries = NativeLibraries.singleton();
List parameterTypes = new ArrayList<>(targetSignature.toParameterList(null));
List parameterLoadTypes = new ArrayList<>(parameterTypes);
- EnumInfo[] parameterEnumInfos;
-
- parameterEnumInfos = adaptParameterTypes(nativeLibraries, kit, parameterTypes, parameterLoadTypes);
+ EnumInfo[] parameterEnumInfos = adaptParameterTypes(nativeLibraries, kit, parameterTypes, parameterLoadTypes);
ValueNode[] args = kit.getInitialArguments().toArray(ValueNode.EMPTY_ARRAY);
if (ImageSingletons.contains(CInterfaceWrapper.class)) {
@@ -331,47 +336,60 @@ private StructuredGraph buildBuiltinGraph(DebugContext debug, AnalysisMethod met
return kit.finalizeGraph();
}
+ /**
+ * The signature may contain Java object types. The only Java object types that we support at
+ * the moment are Java enums (annotated with @CEnum). This method replaces all Java enums with
+ * suitable primitive types.
+ */
private EnumInfo[] adaptParameterTypes(NativeLibraries nativeLibraries, HostedGraphKit kit, List parameterTypes, List parameterLoadTypes) {
EnumInfo[] parameterEnumInfos = null;
for (int i = 0; i < parameterTypes.size(); i++) {
- if (!parameterTypes.get(i).getJavaKind().isPrimitive() && !kit.getWordTypes().isWord(parameterTypes.get(i))) {
- ElementInfo typeInfo = nativeLibraries.findElementInfo(parameterTypes.get(i));
- if (typeInfo instanceof EnumInfo) {
- UserError.guarantee(typeInfo.getChildren().stream().anyMatch(EnumLookupInfo.class::isInstance),
- "Enum class %s needs a method that is annotated with @%s because it is used as a parameter of an entry point method: %s",
- parameterTypes.get(i),
- CEnumLookup.class.getSimpleName(),
- targetMethod);
-
- if (parameterEnumInfos == null) {
- parameterEnumInfos = new EnumInfo[parameterTypes.size()];
- }
- parameterEnumInfos[i] = (EnumInfo) typeInfo;
-
- parameterLoadTypes.set(i, kit.getMetaAccess().lookupJavaType(cEnumParameterKind.toJavaClass()));
-
- final int parameterIndex = i;
- FrameState initialState = kit.getGraph().start().stateAfter();
- Iterator matchingNodes = initialState.values().filter(node -> ((ParameterNode) node).index() == parameterIndex).iterator();
- ValueNode parameterNode = matchingNodes.next();
- assert !matchingNodes.hasNext() && parameterNode.usages().filter(n -> n != initialState).isEmpty();
- parameterNode.setStamp(StampFactory.forKind(cEnumParameterKind));
- } else {
- throw UserError.abort("Entry point method parameter types are restricted to primitive types, word types and enumerations (@%s): %s, given type was %s",
- CEnum.class.getSimpleName(), targetMethod, parameterTypes.get(i));
+ if (!CInterfaceEnumTool.isPrimitiveOrWord(parameterTypes.get(i))) {
+ EnumInfo enumInfo = getEnumInfo(nativeLibraries, targetMethod, parameterTypes.get(i), false);
+ UserError.guarantee(enumInfo.hasCEnumLookupMethods(),
+ "Enum class %s needs a method that is annotated with @%s because it is used as a parameter of an entry point method: %s",
+ parameterTypes.get(i), CEnumLookup.class.getSimpleName(), targetMethod);
+
+ if (parameterEnumInfos == null) {
+ parameterEnumInfos = new EnumInfo[parameterTypes.size()];
}
+ parameterEnumInfos[i] = enumInfo;
+
+ /* The argument is a @CEnum, so change the type to a primitive type. */
+ AnalysisType paramType = CInterfaceEnumTool.getCEnumValueType(enumInfo, kit.getMetaAccess());
+ parameterLoadTypes.set(i, paramType);
+
+ final int parameterIndex = i;
+ FrameState initialState = kit.getGraph().start().stateAfter();
+ Iterator matchingNodes = initialState.values().filter(node -> ((ParameterNode) node).index() == parameterIndex).iterator();
+ ValueNode parameterNode = matchingNodes.next();
+ assert !matchingNodes.hasNext() && parameterNode.usages().filter(n -> n != initialState).isEmpty();
+ parameterNode.setStamp(StampFactory.forKind(paramType.getJavaKind()));
}
}
return parameterEnumInfos;
}
+ private static EnumInfo getEnumInfo(NativeLibraries nativeLibraries, ResolvedJavaMethod method, AnalysisType type, boolean isReturnType) {
+ ElementInfo typeInfo = nativeLibraries.findElementInfo(type);
+ if (typeInfo instanceof EnumInfo enumInfo) {
+ return enumInfo;
+ }
+
+ if (isReturnType) {
+ throw UserError.abort("Entry point method return types are restricted to primitive types, word types and enumerations (@%s): %s, given type was %s",
+ CEnum.class.getSimpleName(), method, type);
+ }
+ throw UserError.abort("Entry point method parameter types are restricted to primitive types, word types and enumerations (@%s): %s, given type was %s",
+ CEnum.class.getSimpleName(), method, type);
+ }
+
private static void adaptArgumentValues(HostedGraphKit kit, List parameterTypes, EnumInfo[] parameterEnumInfos, ValueNode[] args) {
if (parameterEnumInfos != null) {
// These methods must be called after the prologue established a safe context
for (int i = 0; i < parameterEnumInfos.length; i++) {
if (parameterEnumInfos[i] != null) {
- CInterfaceEnumTool tool = new CInterfaceEnumTool(kit.getMetaAccess());
- args[i] = tool.createEnumLookupInvoke(kit, parameterTypes.get(i), parameterEnumInfos[i], cEnumParameterKind, args[i]);
+ args[i] = CInterfaceEnumTool.singleton().createInvokeLookupEnum(kit, parameterTypes.get(i), parameterEnumInfos[i], args[i]);
}
}
}
@@ -566,31 +584,23 @@ private void generateExceptionHandler(ResolvedJavaMethod method, HostedGraphKit
}
private ValueNode adaptReturnValue(HostedGraphKit kit, ValueNode value) {
- ValueNode returnValue = value;
- if (returnValue.getStackKind().isPrimitive()) {
- return returnValue;
+ if (value.getStackKind().isPrimitive()) {
+ return value;
}
+
+ /* The method returns a Java enum, so we need to convert the enum to a primitive value. */
AnalysisType returnType = targetSignature.getReturnType();
- NativeLibraries nativeLibraries = CEntryPointCallStubSupport.singleton().getNativeLibraries();
- ElementInfo typeInfo = nativeLibraries.findElementInfo(returnType);
- if (typeInfo instanceof EnumInfo) {
- // Always return enum values as a signed word because it should never be a problem if
- // the caller expects a narrower integer type and the various checks already handle
- // replacements with word types.
- CInterfaceEnumTool tool = new CInterfaceEnumTool(kit.getMetaAccess());
- JavaKind cEnumReturnType = kit.getWordTypes().getWordKind();
- assert !cEnumReturnType.isUnsigned() : "requires correct representation of signed values";
- returnValue = tool.startEnumValueInvokeWithException(kit, (EnumInfo) typeInfo, cEnumReturnType, returnValue);
- kit.exceptionPart();
- kit.append(new CEntryPointLeaveNode(LeaveAction.ExceptionAbort, kit.exceptionObject()));
- kit.append(new LoweredDeadEndNode());
- kit.endInvokeWithException();
+ NativeLibraries nativeLibraries = NativeLibraries.singleton();
- } else {
- throw UserError.abort("Entry point method return types are restricted to primitive types, word types and enumerations (@%s): %s",
- CEnum.class.getSimpleName(), targetMethod);
- }
- return returnValue;
+ EnumInfo enumInfo = getEnumInfo(nativeLibraries, targetMethod, returnType, true);
+ ValueNode result = CInterfaceEnumTool.singleton().startInvokeWithExceptionEnumToValue(kit, enumInfo, CInterfaceEnumTool.getCEnumValueType(enumInfo, kit.getMetaAccess()), value);
+ result = kit.getGraph().unique(new ZeroExtendNode(result, kit.getWordTypes().getWordKind().getBitCount()));
+
+ kit.exceptionPart();
+ kit.append(new CEntryPointLeaveNode(LeaveAction.ExceptionAbort, kit.exceptionObject()));
+ kit.append(new LoweredDeadEndNode());
+ kit.endInvokeWithException();
+ return result;
}
private void generateEpilogue(HostedGraphKit kit) {
diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/code/CEntryPointCallStubSupport.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/code/CEntryPointCallStubSupport.java
index 0ff44e1ea8ae..2f5bbc51cb32 100644
--- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/code/CEntryPointCallStubSupport.java
+++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/code/CEntryPointCallStubSupport.java
@@ -40,18 +40,11 @@
import com.oracle.svm.core.feature.AutomaticallyRegisteredFeature;
import com.oracle.svm.core.feature.InternalFeature;
import com.oracle.svm.core.meta.MethodPointer;
-import com.oracle.svm.hosted.FeatureImpl.BeforeAnalysisAccessImpl;
import com.oracle.svm.hosted.FeatureImpl.DuringSetupAccessImpl;
-import com.oracle.svm.hosted.c.NativeLibraries;
import jdk.vm.ci.meta.ResolvedJavaType;
public final class CEntryPointCallStubSupport {
-
- static void initialize(BigBang bb) {
- ImageSingletons.add(CEntryPointCallStubSupport.class, new CEntryPointCallStubSupport(bb));
- }
-
public static CEntryPointCallStubSupport singleton() {
return ImageSingletons.lookup(CEntryPointCallStubSupport.class);
}
@@ -59,7 +52,6 @@ public static CEntryPointCallStubSupport singleton() {
private final BigBang bb;
private final Map methodToStub = new ConcurrentHashMap<>();
private final Map methodToJavaStub = new ConcurrentHashMap<>();
- private NativeLibraries nativeLibraries;
/**
* Cache the BoxedRelocatedPointer objects to ensure that the same constant is seen during
@@ -67,7 +59,7 @@ public static CEntryPointCallStubSupport singleton() {
*/
private final ConcurrentHashMap cFunctionPointerCache = new ConcurrentHashMap<>();
- private CEntryPointCallStubSupport(BigBang bb) {
+ CEntryPointCallStubSupport(BigBang bb) {
this.bb = bb;
}
@@ -95,7 +87,7 @@ public AnalysisMethod registerStubForMethod(AnalysisMethod method, Supplier new BoxedRelocatedPointer(t));
+ return cFunctionPointerCache.computeIfAbsent(cFunctionPointer, BoxedRelocatedPointer::new);
}
}
@@ -139,12 +122,6 @@ class CEntryPointCallStubFeature implements InternalFeature {
@Override
public void duringSetup(DuringSetupAccess arg) {
DuringSetupAccessImpl access = (DuringSetupAccessImpl) arg;
- CEntryPointCallStubSupport.initialize(access.getBigBang());
- }
-
- @Override
- public void beforeAnalysis(BeforeAnalysisAccess arg) {
- BeforeAnalysisAccessImpl access = (BeforeAnalysisAccessImpl) arg;
- CEntryPointCallStubSupport.singleton().setNativeLibraries(access.getNativeLibraries());
+ ImageSingletons.add(CEntryPointCallStubSupport.class, new CEntryPointCallStubSupport(access.getBigBang()));
}
}
diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/code/CEntryPointJavaCallStubMethod.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/code/CEntryPointJavaCallStubMethod.java
index 5db0aa2c2a72..2f7c9454e005 100644
--- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/code/CEntryPointJavaCallStubMethod.java
+++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/code/CEntryPointJavaCallStubMethod.java
@@ -74,7 +74,6 @@ protected String getCorrespondingAnnotationName() {
@Override
protected ValueNode createTargetAddressNode(HostedGraphKit kit, List arguments) {
-
/*
* We currently cannot handle {@link MethodPointer} as a constant in the code, so we use an
* indirection with a non-final field load from an object of BoxedRelocatedPointer.
diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeImageDebugInfoProvider.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeImageDebugInfoProvider.java
index 5ad266cdcde2..8eaa2d42c410 100644
--- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeImageDebugInfoProvider.java
+++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeImageDebugInfoProvider.java
@@ -770,7 +770,7 @@ private class NativeImageDebugForeignFieldInfo extends NativeImageDebugFileInfo
@Override
public int size() {
- return structFieldInfo.getSizeInfo().getProperty();
+ return structFieldInfo.getSizeInBytes();
}
@Override
diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeImageDebugInfoProviderBase.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeImageDebugInfoProviderBase.java
index f644131b417c..005ae7f518cb 100644
--- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeImageDebugInfoProviderBase.java
+++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeImageDebugInfoProviderBase.java
@@ -335,13 +335,10 @@ protected static boolean fieldTypeIsEmbedded(StructFieldInfo field) {
}
protected static int elementSize(ElementInfo elementInfo) {
- if (elementInfo == null || !(elementInfo instanceof SizableInfo)) {
+ if (!(elementInfo instanceof SizableInfo) || elementInfo instanceof StructInfo structInfo && structInfo.isIncomplete()) {
return 0;
}
- if (elementInfo instanceof StructInfo && ((StructInfo) elementInfo).isIncomplete()) {
- return 0;
- }
- Integer size = ((SizableInfo) elementInfo).getSizeInfo().getProperty();
+ Integer size = ((SizableInfo) elementInfo).getSizeInBytes();
assert size != null;
return size;
}
diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/jni/JNILibraryLoadFeature.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/jni/JNILibraryLoadFeature.java
index 88686b4d7cba..112b668273b9 100644
--- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/jni/JNILibraryLoadFeature.java
+++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/jni/JNILibraryLoadFeature.java
@@ -31,11 +31,10 @@
import com.oracle.svm.core.jdk.NativeLibrarySupport;
import com.oracle.svm.core.jni.JNILibraryInitializer;
import com.oracle.svm.hosted.c.NativeLibraries;
-import com.oracle.svm.hosted.code.CEntryPointCallStubSupport;
public class JNILibraryLoadFeature implements Feature {
- private JNILibraryInitializer jniLibraryInitializer = new JNILibraryInitializer();
+ private final JNILibraryInitializer jniLibraryInitializer = new JNILibraryInitializer();
@Override
public void duringSetup(DuringSetupAccess access) {
@@ -44,7 +43,7 @@ public void duringSetup(DuringSetupAccess access) {
@Override
public void duringAnalysis(DuringAnalysisAccess access) {
- NativeLibraries nativeLibraries = CEntryPointCallStubSupport.singleton().getNativeLibraries();
+ NativeLibraries nativeLibraries = NativeLibraries.singleton();
List staticLibNames = nativeLibraries.getJniStaticLibraries();
boolean isChanged = jniLibraryInitializer.fillCGlobalDataMap(staticLibNames);
if (isChanged) {
diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/phases/CInterfaceEnumTool.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/phases/CInterfaceEnumTool.java
index a34798c9e094..426cceb807a0 100644
--- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/phases/CInterfaceEnumTool.java
+++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/phases/CInterfaceEnumTool.java
@@ -26,12 +26,27 @@
import java.lang.reflect.Modifier;
+import org.graalvm.nativeimage.ImageSingletons;
+import org.graalvm.nativeimage.c.constant.CEnum;
+import org.graalvm.nativeimage.c.constant.CEnumLookup;
+import org.graalvm.nativeimage.c.constant.CEnumValue;
+import org.graalvm.nativeimage.c.function.CEntryPoint;
+import org.graalvm.word.PointerBase;
+import org.graalvm.word.SignedWord;
+import org.graalvm.word.UnsignedWord;
+
import com.oracle.graal.pointsto.meta.AnalysisMetaAccess;
import com.oracle.graal.pointsto.meta.AnalysisMethod;
import com.oracle.graal.pointsto.meta.AnalysisType;
-import com.oracle.svm.core.c.enums.EnumRuntimeData;
+import com.oracle.svm.core.c.enums.CEnumRuntimeData;
+import com.oracle.svm.core.feature.AutomaticallyRegisteredFeature;
+import com.oracle.svm.core.feature.InternalFeature;
import com.oracle.svm.core.util.VMError;
+import com.oracle.svm.hosted.FeatureImpl.BeforeAnalysisAccessImpl;
import com.oracle.svm.hosted.c.info.EnumInfo;
+import com.oracle.svm.hosted.cenum.CEnumCallWrapperMethod;
+import com.oracle.svm.hosted.code.CCallStubMethod;
+import com.oracle.svm.hosted.code.CEntryPointCallStubMethod;
import jdk.graal.compiler.core.common.type.ObjectStamp;
import jdk.graal.compiler.core.common.type.StampFactory;
@@ -43,14 +58,36 @@
import jdk.graal.compiler.nodes.InvokeWithExceptionNode;
import jdk.graal.compiler.nodes.LogicNode;
import jdk.graal.compiler.nodes.PiNode;
+import jdk.graal.compiler.nodes.StructuredGraph;
import jdk.graal.compiler.nodes.ValueNode;
+import jdk.graal.compiler.nodes.calc.ZeroExtendNode;
import jdk.graal.compiler.nodes.extended.BytecodeExceptionNode;
import jdk.graal.compiler.nodes.extended.GuardingNode;
import jdk.graal.compiler.nodes.graphbuilderconf.GraphBuilderTool;
import jdk.graal.compiler.nodes.java.InstanceOfNode;
import jdk.graal.compiler.nodes.java.MethodCallTargetNode;
+import jdk.graal.compiler.word.WordTypes;
import jdk.vm.ci.meta.JavaKind;
+/**
+ * Implements some shared parts for converting Java enums (see {@link CEnum}) to primitive values
+ * and vice versa. This class is used in the following scenarios:
+ *
+ *
+ * A {@link CEnum} is used in the signature of a {@link CEntryPoint}, see
+ * {@link CEntryPointCallStubMethod}.
+ * A {@link CEnum} is used in the signature of a C function call, see
+ * {@link CCallStubMethod}.
+ * A method is called that is annotated with {@link CEnumValue} or {@link CEnumLookup}, see
+ * {@link CEnumCallWrapperMethod}.
+ *
+ * {@link CEnumValue} and {@link CEnumLookup} methods may use arbitrary primitive Java data types
+ * that are completely unrelated to the actual type of the C enum. However, note that the specified
+ * Java types only have an effect if those methods are called explicitly. When implicitly converting
+ * between C values and Java enums (e.g., for C call arguments and return types), we do not use the
+ * annotated methods. So, for implicit conversion, only the type of the C enum is relevant and the
+ * semantics are similar to reading/writing a C value.
+ */
public class CInterfaceEnumTool {
interface CallTargetFactory {
MethodCallTargetNode createMethodCallTarget(InvokeKind invokeKind, AnalysisMethod targetMethod, ValueNode[] args, StampPair returnStamp, int bci);
@@ -64,51 +101,92 @@ static CallTargetFactory from(HostedGraphKit kit) {
}
}
- private final AnalysisMethod convertJavaToCLongMethod;
- private final AnalysisMethod convertJavaToCIntMethod;
- private final AnalysisMethod convertCToJavaMethod;
+ private final AnalysisMethod enumToBooleanMethod;
+ private final AnalysisMethod enumToByteMethod;
+ private final AnalysisMethod enumToShortMethod;
+ private final AnalysisMethod enumToCharMethod;
+ private final AnalysisMethod enumToIntMethod;
+ private final AnalysisMethod enumToLongMethod;
+ private final AnalysisMethod enumToUnsignedWordMethod;
+ private final AnalysisMethod enumToSignedWordMethod;
+
+ private final AnalysisMethod longToEnumMethod;
- public CInterfaceEnumTool(AnalysisMetaAccess metaAccess) {
+ CInterfaceEnumTool(AnalysisMetaAccess metaAccess) {
try {
- convertJavaToCLongMethod = metaAccess.lookupJavaMethod(EnumRuntimeData.class.getDeclaredMethod("convertJavaToCLong", Enum.class));
- convertJavaToCIntMethod = metaAccess.lookupJavaMethod(EnumRuntimeData.class.getDeclaredMethod("convertJavaToCInt", Enum.class));
- convertCToJavaMethod = metaAccess.lookupJavaMethod(EnumRuntimeData.class.getDeclaredMethod("convertCToJava", long.class));
+ enumToBooleanMethod = metaAccess.lookupJavaMethod(CEnumRuntimeData.class.getDeclaredMethod("enumToBoolean", Enum.class));
+ enumToByteMethod = metaAccess.lookupJavaMethod(CEnumRuntimeData.class.getDeclaredMethod("enumToByte", Enum.class));
+ enumToShortMethod = metaAccess.lookupJavaMethod(CEnumRuntimeData.class.getDeclaredMethod("enumToShort", Enum.class));
+ enumToCharMethod = metaAccess.lookupJavaMethod(CEnumRuntimeData.class.getDeclaredMethod("enumToChar", Enum.class));
+ enumToIntMethod = metaAccess.lookupJavaMethod(CEnumRuntimeData.class.getDeclaredMethod("enumToInt", Enum.class));
+ enumToLongMethod = metaAccess.lookupJavaMethod(CEnumRuntimeData.class.getDeclaredMethod("enumToLong", Enum.class));
+ enumToSignedWordMethod = metaAccess.lookupJavaMethod(CEnumRuntimeData.class.getDeclaredMethod("enumToSignedWord", Enum.class));
+ enumToUnsignedWordMethod = metaAccess.lookupJavaMethod(CEnumRuntimeData.class.getDeclaredMethod("enumToUnsignedWord", Enum.class));
+
+ longToEnumMethod = metaAccess.lookupJavaMethod(CEnumRuntimeData.class.getDeclaredMethod("longToEnum", long.class));
} catch (NoSuchMethodException e) {
throw VMError.shouldNotReachHere(e);
}
}
- private AnalysisMethod getValueMethodForKind(JavaKind kind) {
- return (kind == JavaKind.Long) ? convertJavaToCLongMethod : convertJavaToCIntMethod;
+ public static CInterfaceEnumTool singleton() {
+ return ImageSingletons.lookup(CInterfaceEnumTool.class);
}
- public ValueNode createEnumValueInvoke(HostedGraphKit kit, EnumInfo enumInfo, JavaKind resultKind, ValueNode arg) {
+ public ValueNode createInvokeEnumToValue(HostedGraphKit kit, EnumInfo enumInfo, AnalysisType returnType, ValueNode arg) {
int invokeBci = kit.bci();
- MethodCallTargetNode callTarget = invokeEnumValue(kit, CallTargetFactory.from(kit), invokeBci, enumInfo, resultKind, arg);
+ MethodCallTargetNode callTarget = createInvokeEnumToValue(kit, CallTargetFactory.from(kit), invokeBci, enumInfo, returnType, arg);
return kit.createInvokeWithExceptionAndUnwind(callTarget, kit.getFrameState(), invokeBci);
}
- public ValueNode startEnumValueInvokeWithException(HostedGraphKit kit, EnumInfo enumInfo, JavaKind resultKind, ValueNode arg) {
+ public ValueNode startInvokeWithExceptionEnumToValue(HostedGraphKit kit, EnumInfo enumInfo, AnalysisType returnType, ValueNode arg) {
int invokeBci = kit.bci();
- MethodCallTargetNode callTarget = invokeEnumValue(kit, CallTargetFactory.from(kit), invokeBci, enumInfo, resultKind, arg);
+ MethodCallTargetNode callTarget = createInvokeEnumToValue(kit, CallTargetFactory.from(kit), invokeBci, enumInfo, returnType, arg);
return kit.startInvokeWithException(callTarget, kit.getFrameState(), invokeBci);
}
- private MethodCallTargetNode invokeEnumValue(GraphBuilderTool b, CallTargetFactory callTargetFactory, int bci, EnumInfo enumInfo, JavaKind resultKind, ValueNode arg) {
- AnalysisMethod valueMethod = getValueMethodForKind(resultKind);
- AnalysisType returnType = valueMethod.getSignature().getReturnType();
+ private MethodCallTargetNode createInvokeEnumToValue(GraphBuilderTool b, CallTargetFactory callTargetFactory, int bci, EnumInfo enumInfo, AnalysisType returnType, ValueNode arg) {
+ AnalysisMethod method = getEnumToValueMethod(b, returnType);
+ assert !Modifier.isStatic(method.getModifiers()) && method.getSignature().getParameterCount(true) == 2;
+
ValueNode[] args = new ValueNode[2];
args[0] = ConstantNode.forConstant(b.getSnippetReflection().forObject(enumInfo.getRuntimeData()), b.getMetaAccess(), b.getGraph());
args[1] = arg;
- StampPair returnStamp = StampFactory.forDeclaredType(null, returnType, false);
- return b.append(callTargetFactory.createMethodCallTarget(InvokeKind.Virtual, valueMethod, args, returnStamp, bci));
+ WordTypes wordTypes = b.getWordTypes();
+ StampPair returnStamp = wordTypes.isWord(returnType) ? StampPair.createSingle(wordTypes.getWordStamp(returnType)) : StampFactory.forDeclaredType(null, returnType, false);
+ return b.append(callTargetFactory.createMethodCallTarget(InvokeKind.Special, method, args, returnStamp, bci));
+ }
+
+ private AnalysisMethod getEnumToValueMethod(GraphBuilderTool b, AnalysisType returnType) {
+ if (b.getWordTypes().isWord(returnType)) {
+ assert !b.getMetaAccess().lookupJavaType(PointerBase.class).isAssignableFrom(returnType) : "only integer types are allowed";
+
+ if (b.getMetaAccess().lookupJavaType(UnsignedWord.class).isAssignableFrom(returnType)) {
+ return enumToUnsignedWordMethod;
+ } else if (b.getMetaAccess().lookupJavaType(SignedWord.class).isAssignableFrom(returnType)) {
+ return enumToSignedWordMethod;
+ } else {
+ throw VMError.shouldNotReachHere("Unexpected return type: " + returnType);
+ }
+ }
+
+ JavaKind returnKind = returnType.getJavaKind();
+ return switch (returnKind) {
+ case Boolean -> enumToBooleanMethod;
+ case Byte -> enumToByteMethod;
+ case Short -> enumToShortMethod;
+ case Char -> enumToCharMethod;
+ case Int -> enumToIntMethod;
+ case Long -> enumToLongMethod;
+ default -> throw VMError.shouldNotReachHere("Unexpected return kind: " + returnKind);
+ };
}
- public ValueNode createEnumLookupInvoke(HostedGraphKit kit, AnalysisType enumType, EnumInfo enumInfo, JavaKind parameterKind, ValueNode arg) {
- // Create the invoke to the actual target method: EnumRuntimeData.convertCToJava
+ public ValueNode createInvokeLookupEnum(HostedGraphKit kit, AnalysisType enumType, EnumInfo enumInfo, ValueNode arg) {
+ // Create the invocation of the actual target method: EnumRuntimeData.convertCToJava
int invokeBci = kit.bci();
- MethodCallTargetNode callTarget = invokeEnumLookup(kit, CallTargetFactory.from(kit), invokeBci, enumInfo, parameterKind, arg);
+ MethodCallTargetNode callTarget = createInvokeLongToEnum(kit, CallTargetFactory.from(kit), invokeBci, enumInfo, arg);
InvokeWithExceptionNode invoke = kit.createInvokeWithExceptionAndUnwind(callTarget, kit.getFrameState(), invokeBci);
// Create the instanceof guard to narrow the return type for the analysis
@@ -121,15 +199,46 @@ public ValueNode createEnumLookupInvoke(HostedGraphKit kit, AnalysisType enumTyp
return kit.unique(new PiNode(invoke, resultStamp, guard.asNode()));
}
- private MethodCallTargetNode invokeEnumLookup(GraphBuilderTool b, CallTargetFactory callTargetFactory, int bci, EnumInfo enumInfo, JavaKind parameterKind, ValueNode arg) {
+ private MethodCallTargetNode createInvokeLongToEnum(GraphBuilderTool b, CallTargetFactory callTargetFactory, int bci, EnumInfo enumInfo, ValueNode arg) {
+ assert !Modifier.isStatic(longToEnumMethod.getModifiers()) && longToEnumMethod.getSignature().getParameterCount(false) == 1;
+
+ StructuredGraph graph = b.getGraph();
ValueNode[] args = new ValueNode[2];
- args[0] = ConstantNode.forConstant(b.getSnippetReflection().forObject(enumInfo.getRuntimeData()), b.getMetaAccess(), b.getGraph());
- assert !Modifier.isStatic(convertCToJavaMethod.getModifiers()) && convertCToJavaMethod.getSignature().getParameterCount(false) == 1;
- JavaKind expectedKind = convertCToJavaMethod.getSignature().getParameterType(0).getJavaKind();
- args[1] = CInterfaceInvocationPlugin.adaptPrimitiveType(b.getGraph(), arg, parameterKind, expectedKind, false);
+ args[0] = ConstantNode.forConstant(b.getSnippetReflection().forObject(enumInfo.getRuntimeData()), b.getMetaAccess(), graph);
+ /* The exact size and signedness only matters at a later point. */
+ args[1] = graph.unique(new ZeroExtendNode(arg, 64));
+
+ AnalysisType enumReturnType = longToEnumMethod.getSignature().getReturnType();
+ StampPair returnStamp = StampFactory.forDeclaredType(null, enumReturnType, false);
+ return b.append(callTargetFactory.createMethodCallTarget(InvokeKind.Special, longToEnumMethod, args, returnStamp, bci));
+ }
+
+ public static boolean isPrimitiveOrWord(AnalysisType type) {
+ return type.getStorageKind().isPrimitive();
+ }
+
+ /**
+ * For method signatures, only the size (but not the signedness) matters for {@link CEnum}
+ * values. So, when we convert {@link CEnum}s in the signature to primitive types, we always use
+ * signed primitive Java data types that have the same size as the enum data type in C.
+ */
+ public static AnalysisType getCEnumValueType(EnumInfo enumInfo, AnalysisMetaAccess metaAccess) {
+ int sizeInBytes = enumInfo.getSizeInBytes();
+ return switch (sizeInBytes) {
+ case 1 -> metaAccess.lookupJavaType(byte.class);
+ case 2 -> metaAccess.lookupJavaType(short.class);
+ case 4 -> metaAccess.lookupJavaType(int.class);
+ case 8 -> metaAccess.lookupJavaType(long.class);
+ default -> throw VMError.shouldNotReachHere("CEnum has unexpected size: " + sizeInBytes);
+ };
+ }
+}
- AnalysisType convertReturnType = convertCToJavaMethod.getSignature().getReturnType();
- StampPair returnStamp = StampFactory.forDeclaredType(null, convertReturnType, false);
- return b.append(callTargetFactory.createMethodCallTarget(InvokeKind.Virtual, convertCToJavaMethod, args, returnStamp, bci));
+@AutomaticallyRegisteredFeature
+class CInterfaceEnumToolFeature implements InternalFeature {
+ @Override
+ public void beforeAnalysis(BeforeAnalysisAccess a) {
+ BeforeAnalysisAccessImpl access = (BeforeAnalysisAccessImpl) a;
+ ImageSingletons.add(CInterfaceEnumTool.class, new CInterfaceEnumTool(access.getMetaAccess()));
}
}
diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/phases/CInterfaceInvocationPlugin.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/phases/CInterfaceInvocationPlugin.java
index 3e28179b9d8c..ac0ab43d48a7 100644
--- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/phases/CInterfaceInvocationPlugin.java
+++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/phases/CInterfaceInvocationPlugin.java
@@ -34,6 +34,8 @@
import org.graalvm.nativeimage.c.function.CFunctionPointer;
import org.graalvm.nativeimage.c.function.InvokeCFunctionPointer;
import org.graalvm.word.LocationIdentity;
+import org.graalvm.word.WordBase;
+import org.graalvm.word.impl.WordBoxFactory;
import com.oracle.graal.pointsto.meta.AnalysisMethod;
import com.oracle.graal.pointsto.meta.AnalysisType;
@@ -44,6 +46,7 @@
import com.oracle.svm.core.graal.nodes.CInterfaceReadNode;
import com.oracle.svm.core.graal.nodes.CInterfaceWriteNode;
import com.oracle.svm.core.util.UserError;
+import com.oracle.svm.core.util.VMError;
import com.oracle.svm.hosted.c.CInterfaceError;
import com.oracle.svm.hosted.c.CInterfaceWrapper;
import com.oracle.svm.hosted.c.NativeLibraries;
@@ -60,7 +63,6 @@
import com.oracle.svm.hosted.code.CEntryPointJavaCallStubMethod;
import com.oracle.svm.hosted.code.CFunctionPointerCallStubSupport;
-import jdk.graal.compiler.core.common.calc.FloatConvert;
import jdk.graal.compiler.core.common.memory.BarrierType;
import jdk.graal.compiler.core.common.memory.MemoryOrderMode;
import jdk.graal.compiler.core.common.type.IntegerStamp;
@@ -72,12 +74,12 @@
import jdk.graal.compiler.nodes.ConstantNode;
import jdk.graal.compiler.nodes.IndirectCallTargetNode;
import jdk.graal.compiler.nodes.LogicNode;
+import jdk.graal.compiler.nodes.NodeView;
import jdk.graal.compiler.nodes.StructuredGraph;
import jdk.graal.compiler.nodes.ValueNode;
import jdk.graal.compiler.nodes.calc.AddNode;
import jdk.graal.compiler.nodes.calc.AndNode;
import jdk.graal.compiler.nodes.calc.ConditionalNode;
-import jdk.graal.compiler.nodes.calc.FloatConvertNode;
import jdk.graal.compiler.nodes.calc.IntegerEqualsNode;
import jdk.graal.compiler.nodes.calc.LeftShiftNode;
import jdk.graal.compiler.nodes.calc.MulNode;
@@ -85,12 +87,14 @@
import jdk.graal.compiler.nodes.calc.OrNode;
import jdk.graal.compiler.nodes.calc.RightShiftNode;
import jdk.graal.compiler.nodes.calc.SignExtendNode;
+import jdk.graal.compiler.nodes.calc.UnsignedRightShiftNode;
import jdk.graal.compiler.nodes.calc.ZeroExtendNode;
import jdk.graal.compiler.nodes.extended.JavaReadNode;
import jdk.graal.compiler.nodes.extended.JavaWriteNode;
import jdk.graal.compiler.nodes.graphbuilderconf.GraphBuilderContext;
import jdk.graal.compiler.nodes.graphbuilderconf.NodePlugin;
import jdk.graal.compiler.nodes.memory.address.OffsetAddressNode;
+import jdk.vm.ci.code.CodeUtil;
import jdk.vm.ci.meta.JavaKind;
import jdk.vm.ci.meta.JavaType;
import jdk.vm.ci.meta.MetaAccessProvider;
@@ -156,77 +160,86 @@ private static boolean replaceOffsetOf(GraphBuilderContext b, AnalysisMethod met
return true;
}
- private static boolean replaceAccessor(GraphBuilderContext b, AnalysisMethod method, ValueNode[] args, AccessorInfo accessorInfo, int displacement) {
- StructuredGraph graph = b.getGraph();
- SizableInfo sizableInfo = (SizableInfo) accessorInfo.getParent();
- int elementSize = sizableInfo.getSizeInfo().getProperty();
- boolean isUnsigned = sizableInfo.isUnsigned();
- boolean isPinnedObject = sizableInfo.isObject();
-
+ private boolean replaceAccessor(GraphBuilderContext b, AnalysisMethod method, ValueNode[] args, AccessorInfo accessorInfo, int displacement) {
assert args.length == accessorInfo.parameterCount(true);
+ SizableInfo typeInC = (SizableInfo) accessorInfo.getParent();
ValueNode base = args[AccessorInfo.baseParameterNumber(true)];
assert base.getStackKind() == ConfigurationValues.getWordKind();
switch (accessorInfo.getAccessorKind()) {
- case ADDRESS: {
- ValueNode address = graph.addOrUniqueWithInputs(new AddNode(base, makeOffset(graph, args, accessorInfo, displacement, elementSize)));
- b.addPush(pushKind(method), address);
- return true;
- }
- case GETTER: {
- JavaKind resultKind = b.getWordTypes().asKind(b.getInvokeReturnType());
- JavaKind readKind = kindFromSize(elementSize, resultKind);
- if (readKind == JavaKind.Object) {
- assert resultKind == JavaKind.Object;
- } else if (readKind.getBitCount() > resultKind.getBitCount() && !readKind.isNumericFloat() && resultKind != JavaKind.Boolean) {
- readKind = resultKind;
- }
- OffsetAddressNode offsetAddress = makeOffsetAddress(graph, args, accessorInfo, base, displacement, elementSize);
- LocationIdentity locationIdentity = makeLocationIdentity(b, method, args, accessorInfo);
- final Stamp stamp;
- if (readKind == JavaKind.Object) {
- stamp = b.getInvokeReturnStamp(null).getTrustedStamp();
- } else if (readKind == JavaKind.Float || readKind == JavaKind.Double) {
- stamp = StampFactory.forKind(readKind);
- } else {
- stamp = IntegerStamp.create(readKind.getBitCount());
- }
- final ValueNode node;
- if (isPinnedObject) {
- node = b.add(new JavaReadNode(stamp, readKind, offsetAddress, locationIdentity, BarrierType.NONE, MemoryOrderMode.PLAIN, true));
- } else {
- ValueNode read = readPrimitive(b, offsetAddress, locationIdentity, stamp, accessorInfo, method);
- node = adaptPrimitiveType(graph, read, readKind, resultKind == JavaKind.Boolean ? resultKind : resultKind.getStackKind(), isUnsigned);
- }
- b.push(pushKind(method), node);
- return true;
- }
- case SETTER: {
- ValueNode value = args[accessorInfo.valueParameterNumber(true)];
- JavaKind valueKind = value.getStackKind();
- JavaKind writeKind = kindFromSize(elementSize, valueKind);
- OffsetAddressNode offsetAddress = makeOffsetAddress(graph, args, accessorInfo, base, displacement, elementSize);
- LocationIdentity locationIdentity = makeLocationIdentity(b, method, args, accessorInfo);
- if (isPinnedObject) {
- b.add(new JavaWriteNode(writeKind, offsetAddress, locationIdentity, value, BarrierType.NONE, true));
- } else {
- ValueNode adaptedValue = adaptPrimitiveType(graph, value, valueKind, writeKind, isUnsigned);
- writePrimitive(b, offsetAddress, locationIdentity, adaptedValue, accessorInfo, method);
- }
- return true;
- }
- default:
- throw shouldNotReachHereUnexpectedInput(accessorInfo.getAccessorKind()); // ExcludeFromJacocoGeneratedReport
+ case ADDRESS -> replaceWithAddress(b, method, base, args, accessorInfo, displacement, typeInC);
+ case GETTER -> replaceWithRead(b, method, base, args, accessorInfo, displacement, typeInC);
+ case SETTER -> replaceWithWrite(b, method, base, args, accessorInfo, displacement, typeInC);
+ default -> throw shouldNotReachHereUnexpectedInput(accessorInfo.getAccessorKind()); // ExcludeFromJacocoGeneratedReport
+ }
+ return true;
+ }
+
+ private static void replaceWithAddress(GraphBuilderContext b, AnalysisMethod method, ValueNode base, ValueNode[] args, AccessorInfo accessorInfo, int displacement, SizableInfo typeInC) {
+ StructuredGraph graph = b.getGraph();
+ ValueNode address = graph.addOrUniqueWithInputs(new AddNode(base, makeOffset(graph, args, accessorInfo, displacement, typeInC.getSizeInBytes())));
+ b.addPush(pushKind(method), address);
+ }
+
+ private void replaceWithRead(GraphBuilderContext b, AnalysisMethod method, ValueNode base, ValueNode[] args, AccessorInfo accessorInfo, int displacement, SizableInfo typeInC) {
+ StructuredGraph graph = b.getGraph();
+ OffsetAddressNode offsetAddress = makeOffsetAddress(graph, args, accessorInfo, base, displacement, typeInC.getSizeInBytes());
+ LocationIdentity locationIdentity = makeLocationIdentity(b, method, args, accessorInfo);
+
+ ValueNode node;
+ JavaKind returnKind = b.getWordTypes().asKind(b.getInvokeReturnType());
+ if (returnKind == JavaKind.Object) {
+ Stamp stamp = b.getInvokeReturnStamp(null).getTrustedStamp();
+ node = b.add(new JavaReadNode(stamp, returnKind, offsetAddress, locationIdentity, BarrierType.NONE, MemoryOrderMode.PLAIN, true));
+ } else if (returnKind == JavaKind.Float || returnKind == JavaKind.Double) {
+ Stamp stamp = StampFactory.forKind(returnKind);
+ node = readPrimitive(b, offsetAddress, locationIdentity, stamp, accessorInfo, method);
+ } else {
+ IntegerStamp stamp = IntegerStamp.create(typeInC.getSizeInBytes() * Byte.SIZE);
+ ValueNode read = readPrimitive(b, offsetAddress, locationIdentity, stamp, accessorInfo, method);
+ node = convertCIntegerToMethodReturnType(graph, method, read, typeInC.isUnsigned());
+ }
+ b.push(pushKind(method), node);
+ }
+
+ private static void replaceWithWrite(GraphBuilderContext b, AnalysisMethod method, ValueNode base, ValueNode[] args, AccessorInfo accessorInfo, int displacement, SizableInfo typeInC) {
+ StructuredGraph graph = b.getGraph();
+ OffsetAddressNode offsetAddress = makeOffsetAddress(graph, args, accessorInfo, base, displacement, typeInC.getSizeInBytes());
+ LocationIdentity locationIdentity = makeLocationIdentity(b, method, args, accessorInfo);
+
+ ValueNode value = args[accessorInfo.valueParameterNumber(true)];
+ JavaKind valueKind = value.getStackKind();
+ if (valueKind == JavaKind.Object) {
+ b.add(new JavaWriteNode(valueKind, offsetAddress, locationIdentity, value, BarrierType.NONE, true));
+ } else if (valueKind == JavaKind.Float || valueKind == JavaKind.Double) {
+ writePrimitive(b, offsetAddress, locationIdentity, value, accessorInfo, method);
+ } else {
+ ValueNode adaptedValue = adaptPrimitiveTypeForWrite(graph, value, typeInC.getSizeInBytes() * Byte.SIZE, typeInC.isUnsigned());
+ writePrimitive(b, offsetAddress, locationIdentity, adaptedValue, accessorInfo, method);
}
}
- private static boolean replaceBitfieldAccessor(GraphBuilderContext b, AnalysisMethod method, ValueNode[] args, StructBitfieldInfo bitfieldInfo, AccessorInfo accessorInfo) {
+ private static ValueNode adaptPrimitiveTypeForWrite(StructuredGraph graph, ValueNode value, int toBits, boolean isCValueUnsigned) {
+ JavaKind valueKind = value.getStackKind();
+ int valueBits = valueKind.getBitCount();
+ if (valueBits == toBits) {
+ return value;
+ } else if (valueBits > toBits) {
+ return graph.unique(new NarrowNode(value, toBits));
+ } else if (isCValueUnsigned) {
+ return graph.unique(new ZeroExtendNode(value, toBits));
+ } else {
+ return graph.unique(new SignExtendNode(value, toBits));
+ }
+ }
+
+ private boolean replaceBitfieldAccessor(GraphBuilderContext b, AnalysisMethod method, ValueNode[] args, StructBitfieldInfo bitfieldInfo, AccessorInfo accessorInfo) {
int byteOffset = bitfieldInfo.getByteOffsetInfo().getProperty();
int startBit = bitfieldInfo.getStartBitInfo().getProperty();
int endBit = bitfieldInfo.getEndBitInfo().getProperty();
- boolean isUnsigned = bitfieldInfo.isUnsigned();
- assert byteOffset >= 0 && byteOffset < ((SizableInfo) bitfieldInfo.getParent()).getSizeInfo().getProperty();
+ boolean isCValueUnsigned = bitfieldInfo.isUnsigned();
+ assert byteOffset >= 0 && byteOffset < ((SizableInfo) bitfieldInfo.getParent()).getSizeInBytes();
assert startBit >= 0 && startBit < 8;
assert endBit >= startBit && endBit < 64;
@@ -234,110 +247,121 @@ private static boolean replaceBitfieldAccessor(GraphBuilderContext b, AnalysisMe
* The startBit is always in the first byte. Therefore, the endBit tells us how many bytes
* we actually have to read and write.
*/
- JavaKind memoryKind;
+ JavaKind readKind;
if (endBit < 8) {
- memoryKind = JavaKind.Byte;
+ readKind = JavaKind.Byte;
} else if (endBit < 16) {
- memoryKind = JavaKind.Short;
+ readKind = JavaKind.Short;
} else if (endBit < 32) {
- memoryKind = JavaKind.Int;
+ readKind = JavaKind.Int;
} else {
- memoryKind = JavaKind.Long;
+ readKind = JavaKind.Long;
}
- int numBytes = memoryKind.getByteCount();
/*
- * Try to align the byteOffset to be a multiple of numBytes. That should always be possible,
- * but we don't trust the C compiler and memory layout enough to make it an assertion.
+ * Try to align the byteOffset to be a multiple of readBytes. That should always be
+ * possible, but we don't trust the C compiler and memory layout enough to make it an
+ * assertion.
*/
- int alignmentCorrection = byteOffset % numBytes;
- if (alignmentCorrection > 0 && endBit + alignmentCorrection * 8 < numBytes * 8) {
+ int readBytes = readKind.getByteCount();
+ int alignmentCorrection = byteOffset % readBytes;
+ if (alignmentCorrection > 0 && endBit + alignmentCorrection * Byte.SIZE < readKind.getBitCount()) {
byteOffset -= alignmentCorrection;
- startBit += alignmentCorrection * 8;
- endBit += alignmentCorrection * 8;
+ startBit += alignmentCorrection * Byte.SIZE;
+ endBit += alignmentCorrection * Byte.SIZE;
}
- assert byteOffset >= 0 && byteOffset < ((SizableInfo) bitfieldInfo.getParent()).getSizeInfo().getProperty();
- assert startBit >= 0 && startBit < numBytes * 8;
- assert endBit >= startBit && endBit < numBytes * 8;
-
- int numBits = endBit - startBit + 1;
- assert numBits > 0 && numBits <= numBytes * 8;
+ assert byteOffset >= 0 && byteOffset < ((SizableInfo) bitfieldInfo.getParent()).getSizeInBytes();
+ assert startBit >= 0 && startBit < readKind.getBitCount();
+ assert endBit >= startBit && endBit < readKind.getBitCount();
/*
- * The bit-operations on the value are either performed on Int or Long. We do not perform 8
- * or 16 bit arithmetic operations.
+ * Read the C memory. This is also necessary for writes, since we need to keep the bits
+ * around the written bitfield unchanged.
*/
- JavaKind computeKind = memoryKind.getStackKind();
- Stamp computeStamp = StampFactory.forKind(computeKind);
- int computeBits = computeKind.getBitCount();
- assert startBit >= 0 && startBit < computeBits;
- assert endBit >= startBit && endBit < computeBits;
- assert computeBits >= numBits;
-
assert args.length == accessorInfo.parameterCount(true);
ValueNode base = args[AccessorInfo.baseParameterNumber(true)];
StructuredGraph graph = b.getGraph();
- /*
- * Read the memory location. This is also necessary for writes, since we need to keep the
- * bits around the written bitfield unchanged.
- */
OffsetAddressNode address = makeOffsetAddress(graph, args, accessorInfo, base, byteOffset, -1);
LocationIdentity locationIdentity = makeLocationIdentity(b, method, args, accessorInfo);
- Stamp stamp = IntegerStamp.create(memoryKind.getBitCount());
- ValueNode cur = readPrimitive(b, address, locationIdentity, stamp, accessorInfo, method);
- cur = adaptPrimitiveType(graph, cur, memoryKind, computeKind, true);
+ Stamp stamp = IntegerStamp.create(readKind.getBitCount());
+ ValueNode cValue = readPrimitive(b, address, locationIdentity, stamp, accessorInfo, method);
+
+ /* Arithmetic operations are always performed on 32 or 64-bit. */
+ JavaKind computeKind = readKind.getStackKind();
+ int computeBits = computeKind.getBitCount();
+ assert startBit >= 0 && startBit < computeBits;
+ assert endBit >= startBit && endBit < computeBits;
+
+ int accessedBits = endBit - startBit + 1;
+ assert accessedBits > 0 && accessedBits <= readKind.getBitCount() && accessedBits <= 64;
+ assert computeBits >= accessedBits;
+
+ /* Zero-extend the C value if it is less than 32-bit so that we can do arithmetics. */
+ if (readKind.getBitCount() < 32) {
+ cValue = graph.unique(new ZeroExtendNode(cValue, computeBits));
+ }
switch (accessorInfo.getAccessorKind()) {
- case GETTER: {
- if (isUnsigned) {
- /*
- * Unsigned reads: shift the bitfield to the right and mask out the unnecessary
- * high-order bits.
- */
- cur = graph.unique(new RightShiftNode(cur, ConstantNode.forInt(startBit, graph)));
- cur = graph.unique(new AndNode(cur, ConstantNode.forIntegerStamp(computeStamp, (1L << numBits) - 1, graph)));
+ case GETTER -> {
+ /* Reduce the C value to the bits that we really wanted to read. */
+ cValue = graph.unique(new LeftShiftNode(cValue, ConstantNode.forInt(computeBits - endBit - 1, graph)));
+ if (isCValueUnsigned) {
+ cValue = graph.unique(new UnsignedRightShiftNode(cValue, ConstantNode.forInt(computeBits - accessedBits, graph)));
} else {
- /*
- * Signed reads: shift the bitfield to the right end to get the sign bit in
- * place, then do a signed left shift to have a proper sign extension.
- */
- cur = graph.unique(new LeftShiftNode(cur, ConstantNode.forInt(computeBits - endBit - 1, graph)));
- cur = graph.unique(new RightShiftNode(cur, ConstantNode.forInt(computeBits - numBits, graph)));
+ cValue = graph.unique(new RightShiftNode(cValue, ConstantNode.forInt(computeBits - accessedBits, graph)));
}
- JavaKind resultKind = b.getWordTypes().asKind(b.getInvokeReturnType());
- b.push(pushKind(method), adaptPrimitiveType(graph, cur, computeKind, resultKind == JavaKind.Boolean ? resultKind : resultKind.getStackKind(), isUnsigned));
- return true;
- }
- case SETTER: {
- /* Zero out the bits of our bitfields, i.e., the bits we are going to change. */
- long mask = ~(((1L << numBits) - 1) << startBit);
- cur = graph.unique(new AndNode(cur, ConstantNode.forIntegerStamp(computeStamp, mask, graph)));
+ /* Narrow the value so that its size matches the size of a C data type. */
+ int targetBits = 1 << CodeUtil.log2(accessedBits);
+ if (targetBits < accessedBits) {
+ targetBits = targetBits * 2;
+ }
+ if (targetBits < 8) {
+ targetBits = 8;
+ }
+
+ if (computeBits > targetBits) {
+ cValue = graph.unique(new NarrowNode(cValue, targetBits));
+ }
/*
- * Mask the unnecessary high-order bits of the value to be written, and shift it to
- * its place.
+ * Now, the value looks as if we actually read targetBits from C memory. So, we can
+ * finally convert this value to the method's declared return type.
*/
- ValueNode value = args[accessorInfo.valueParameterNumber(true)];
- value = adaptPrimitiveType(graph, value, value.getStackKind(), computeKind, isUnsigned);
- value = graph.unique(new AndNode(value, ConstantNode.forIntegerStamp(computeStamp, (1L << numBits) - 1, graph)));
- value = graph.unique(new LeftShiftNode(value, ConstantNode.forInt(startBit, graph)));
-
- /* Combine the leftover bits of the original memory word with the new value. */
- cur = graph.unique(new OrNode(cur, value));
-
- /* Narrow value to the number of bits we need to write. */
- cur = adaptPrimitiveType(graph, cur, computeKind, memoryKind, true);
- /* Perform the write (bitcount is taken from the stamp of the written value). */
- writePrimitive(b, address, locationIdentity, cur, accessorInfo, method);
+ ValueNode result = convertCIntegerToMethodReturnType(graph, method, cValue, isCValueUnsigned);
+ b.push(pushKind(method), result);
+ return true;
+ }
+ case SETTER -> {
+ /* Convert the new value to at least 32-bit so that we can do arithmetics. */
+ ValueNode newValue = args[accessorInfo.valueParameterNumber(true)];
+ newValue = adaptPrimitiveTypeForWrite(graph, newValue, computeBits, isCValueUnsigned);
+
+ if (accessedBits == 64) {
+ /* The new value replaces all 64 bits of the C memory. */
+ assert startBit == 0;
+ cValue = newValue;
+ } else {
+ /* Reduce the new value to the bits that we want and shift the bits in place. */
+ newValue = graph.unique(new LeftShiftNode(newValue, ConstantNode.forInt(computeBits - accessedBits, graph)));
+ newValue = graph.unique(new UnsignedRightShiftNode(newValue, ConstantNode.forInt(computeBits - accessedBits - startBit, graph)));
+
+ /* Replace the bits in the old value with the bits from the new value. */
+ long mask = ~(((1L << accessedBits) - 1) << startBit);
+ cValue = graph.unique(new AndNode(cValue, ConstantNode.forIntegerBits(computeBits, mask, graph)));
+ cValue = graph.unique(new OrNode(cValue, newValue));
+ }
+
+ /* Narrow the value to the bytes that we need and write those to C memory. */
+ cValue = adaptPrimitiveTypeForWrite(graph, cValue, readKind.getBitCount(), isCValueUnsigned);
+ writePrimitive(b, address, locationIdentity, cValue, accessorInfo, method);
return true;
}
- default:
- throw shouldNotReachHereUnexpectedInput(accessorInfo.getAccessorKind()); // ExcludeFromJacocoGeneratedReport
+ default -> throw shouldNotReachHereUnexpectedInput(accessorInfo.getAccessorKind()); // ExcludeFromJacocoGeneratedReport
}
}
- private static ValueNode readPrimitive(GraphBuilderContext b, OffsetAddressNode address, LocationIdentity locationIdentity, Stamp stamp, AccessorInfo accessorInfo, ResolvedJavaMethod method) {
+ private static ValueNode readPrimitive(GraphBuilderContext b, OffsetAddressNode address, LocationIdentity locationIdentity, Stamp stamp, AccessorInfo accessorInfo, AnalysisMethod method) {
if (ImageSingletons.contains(CInterfaceWrapper.class)) {
ValueNode replaced = ImageSingletons.lookup(CInterfaceWrapper.class).replacePrimitiveRead(b, address, stamp, method);
if (replaced != null) {
@@ -354,7 +378,7 @@ private static ValueNode readPrimitive(GraphBuilderContext b, OffsetAddressNode
return read;
}
- private static void writePrimitive(GraphBuilderContext b, OffsetAddressNode address, LocationIdentity locationIdentity, ValueNode value, AccessorInfo accessorInfo, ResolvedJavaMethod method) {
+ private static void writePrimitive(GraphBuilderContext b, OffsetAddressNode address, LocationIdentity locationIdentity, ValueNode value, AccessorInfo accessorInfo, AnalysisMethod method) {
if (ImageSingletons.contains(CInterfaceWrapper.class)) {
boolean replaced = ImageSingletons.lookup(CInterfaceWrapper.class).replacePrimitiveWrite(b, address, value, method);
if (replaced) {
@@ -379,7 +403,7 @@ private static ValueNode makeOffset(StructuredGraph graph, ValueNode[] args, Acc
if (accessorInfo.isIndexed()) {
ValueNode index = args[accessorInfo.indexParameterNumber(true)];
assert index.getStackKind().isPrimitive();
- ValueNode wordIndex = adaptPrimitiveType(graph, index, index.getStackKind(), ConfigurationValues.getWordKind(), false);
+ ValueNode wordIndex = extend(graph, index, ConfigurationValues.getWordKind().getBitCount(), false);
ValueNode scaledIndex = graph.unique(new MulNode(wordIndex, ConstantNode.forIntegerKind(ConfigurationValues.getWordKind(), indexScaling, graph)));
offset = graph.unique(new AddNode(scaledIndex, offset));
@@ -412,92 +436,243 @@ private static LocationIdentity makeLocationIdentity(GraphBuilderContext b, Anal
return locationIdentity;
}
- public static ValueNode adaptPrimitiveType(StructuredGraph graph, ValueNode value, JavaKind fromKind, JavaKind toKind, boolean isUnsigned) {
- if (fromKind == toKind) {
- return value;
- }
- assert fromKind.isNumericFloat() == toKind.isNumericFloat();
-
- int fromBits = fromKind.getBitCount();
- int toBits = toKind.getBitCount();
+ private ValueNode convertCIntegerToMethodReturnType(StructuredGraph graph, AnalysisMethod method, ValueNode cValue, boolean isCValueUnsigned) {
+ return convertCIntegerToMethodReturnType(graph, nativeLibs, method.getSignature().getReturnType(), cValue, isCValueUnsigned);
+ }
- if (fromBits == toBits) {
- return value;
- } else if (fromKind.isNumericFloat()) {
- FloatConvert op;
- if (fromKind == JavaKind.Float && toKind == JavaKind.Double) {
- op = FloatConvert.F2D;
- } else if (fromKind == JavaKind.Double && toKind == JavaKind.Float) {
- op = FloatConvert.D2F;
- } else {
- throw shouldNotReachHereUnexpectedInput(fromKind); // ExcludeFromJacocoGeneratedReport
+ /**
+ * Converts a C value to a value that can be returned by a Java method (i.e., a 32- or 64-bit
+ * value). Does zero- or sign-extend the value if necessary.
+ *
+ *
+ * -----------------------------------------------------------------------------------------
+ * | C type | declared return type | result |
+ * -----------------------------------------------------------------------------------------
+ * | signed 8-bit | boolean | 1 byte read, convert to 0 or 1 (32-bit) |
+ * | signed 8-bit | byte | 1 byte read, sign extend to 32-bit |
+ * | signed 8-bit | short | 1 byte read, sign extend to 32-bit |
+ * | signed 8-bit | char | 1 byte read, zero extend to 32-bit |
+ * | signed 8-bit | int | 1 byte read, sign extend to 32-bit |
+ * | signed 8-bit | long | 1 byte read, sign extend to 64-bit |
+ * | signed 8-bit | UnsignedWord | 1 byte read, zero extend to 64-bit |
+ * | |
+ * | unsigned 8-bit | boolean | 1 byte read, convert to 0 or 1 (32-bit) |
+ * | unsigned 8-bit | byte | 1 byte read, sign extend to 32-bit |
+ * | unsigned 8-bit | short | 1 byte read, zero extend to 32-bit |
+ * | unsigned 8-bit | char | 1 byte read, zero extend to 32-bit |
+ * | unsigned 8-bit | int | 1 byte read, zero extend to 32-bit |
+ * | unsigned 8-bit | long | 1 byte read, zero extend to 64-bit |
+ * | unsigned 8-bit | UnsignedWord | 1 byte read, zero extend to 64-bit |
+ * | |
+ * | signed 32-bit | boolean | 4 byte read, convert to 0 or 1 (32-bit) |
+ * | signed 32-bit | byte | 1 byte read, sign extend to 32-bit |
+ * | signed 32-bit | short | 2 byte read, sign extend to 32-bit |
+ * | signed 32-bit | char | 2 byte read, zero extend to 32-bit |
+ * | signed 32-bit | int | 4 byte read, use value directly |
+ * | signed 32-bit | long | 4 byte read, sign extend to 64-bit |
+ * | signed 32-bit | UnsignedWord | 4 byte read, sign extend to 64-bit |
+ * | |
+ * | unsigned 32-bit | boolean | 4 byte read, convert to 0 or 1 (32-bit) |
+ * | unsigned 32-bit | byte | 1 byte read, sign extend to 32-bit |
+ * | unsigned 32-bit | short | 2 byte read, sign extend to 32-bit |
+ * | unsigned 32-bit | char | 2 byte read, zero extend to 32-bit |
+ * | unsigned 32-bit | int | 4 byte read, use value directly |
+ * | unsigned 32-bit | long | 4 byte read, zero extend to 64-bit |
+ * | unsigned 32-bit | UnsignedWord | 4 byte read, zero extend to 64-bit |
+ * | |
+ * | signed 64-bit | boolean | 8 byte read, convert to 0 or 1 (32-bit) |
+ * | signed 64-bit | byte | 1 byte read, sign extend to 32-bit |
+ * | signed 64-bit | short | 2 byte read, sign extend to 32-bit |
+ * | signed 64-bit | char | 2 byte read, zero extend to 32-bit |
+ * | signed 64-bit | int | 4 byte read, use value directly |
+ * | signed 64-bit | long | 8 byte read, use value directly |
+ * | signed 64-bit | UnsignedWord | 8 byte read, use value directly |
+ * | |
+ * | unsigned 64-bit | boolean | 8 byte read, convert to 0 or 1 (32-bit) |
+ * | unsigned 64-bit | byte | 1 byte read, sign extend to 32-bit |
+ * | unsigned 64-bit | short | 2 byte read, sign extend to 32-bit |
+ * | unsigned 64-bit | char | 2 byte read, zero extend to 32-bit |
+ * | unsigned 64-bit | int | 4 byte read, use value directly |
+ * | unsigned 64-bit | long | 8 byte read, use value directly |
+ * | unsigned 64-bit | UnsignedWord | 8 byte read, use value directly |
+ * -----------------------------------------------------------------------------------------
+ *
+ */
+ public static ValueNode convertCIntegerToMethodReturnType(StructuredGraph graph, NativeLibraries nativeLibs, ResolvedJavaType declaredReturnType,
+ ValueNode cValue, boolean isCValueUnsigned) {
+ IntegerStamp cValueStamp = (IntegerStamp) cValue.stamp(NodeView.DEFAULT);
+ int bitsInC = cValueStamp.getBits();
+
+ JavaKind declaredReturnKind = nativeLibs.getWordTypes().asKind(declaredReturnType);
+ if (declaredReturnKind == JavaKind.Boolean) {
+ /* Convert the C value to a Java boolean (i.e., 32-bit zero or one). */
+ ValueNode comparisonValue = cValue;
+ int comparisonBits = bitsInC;
+ if (bitsInC < 32) {
+ /* For the comparison below, we need at least 32 bits. */
+ comparisonValue = graph.unique(new ZeroExtendNode(cValue, 32));
+ comparisonBits = 32;
}
- return graph.unique(new FloatConvertNode(op, value));
- } else if (toKind == JavaKind.Boolean) {
- JavaKind computeKind = fromKind == JavaKind.Long ? JavaKind.Long : JavaKind.Int;
- LogicNode comparison = graph.unique(new IntegerEqualsNode(adaptPrimitiveType(graph, value, fromKind, computeKind, true), ConstantNode.forIntegerKind(computeKind, 0, graph)));
+
+ LogicNode comparison = graph.unique(new IntegerEqualsNode(comparisonValue, ConstantNode.forIntegerBits(comparisonBits, 0, graph)));
return graph.unique(new ConditionalNode(comparison, ConstantNode.forBoolean(false, graph), ConstantNode.forBoolean(true, graph)));
- } else if (fromBits > toBits) {
- return graph.unique(new NarrowNode(value, toBits));
- } else if (isUnsigned) {
- return graph.unique(new ZeroExtendNode(value, toBits));
- } else {
- return graph.unique(new SignExtendNode(value, toBits));
}
+
+ /*
+ * Narrow the C value to the bits that are actually needed for the method's declared return
+ * type.
+ */
+ ValueNode result = cValue;
+ int resultBits = bitsInC;
+ if (bitsInC > declaredReturnKind.getBitCount()) {
+ result = graph.unique(new NarrowNode(result, declaredReturnKind.getBitCount()));
+ resultBits = declaredReturnKind.getBitCount();
+ }
+
+ /*
+ * Finally, sign- or zero-extend the value. Here, we need to be careful that the result is
+ * within the value range of the method's declared return type (e.g., a Java method with
+ * return type "byte" may only return values between -128 and +127). If we violated that
+ * invariant, we could end up with miscompiled code (e.g., branches could fold away
+ * incorrectly).
+ *
+ * So, we primarily use the signedness of the method's declared return type to decide if we
+ * should zero- or sign-extend. There is only one exception: if the C value is unsigned and
+ * uses fewer bits than the method's declared return type, then we can safely zero-extend
+ * the value (i.e., the unsigned C value will fit into the positive value range of the
+ * larger Java type).
+ *
+ * If the narrowed C value already has the correct number of bits, then the sign- or
+ * zero-extension is still necessary. However, it will only adjust the stamp of the value so
+ * that it is within the value range of the method's declared return type.
+ */
+ int actualReturnTypeBitCount = declaredReturnKind.getStackKind().getBitCount();
+ assert actualReturnTypeBitCount >= resultBits;
+
+ boolean zeroExtend = shouldZeroExtend(nativeLibs, declaredReturnType, isCValueUnsigned, bitsInC);
+ return extend(graph, result, actualReturnTypeBitCount, zeroExtend);
+ }
+
+ private static boolean shouldZeroExtend(NativeLibraries nativeLibs, ResolvedJavaType declaredReturnType, boolean isCValueUnsigned, int bitsInC) {
+ JavaKind declaredReturnKind = nativeLibs.getWordTypes().asKind(declaredReturnType);
+ boolean isDeclaredReturnTypeSigned = nativeLibs.isSigned(declaredReturnType);
+ return !isDeclaredReturnTypeSigned || isCValueUnsigned && bitsInC < declaredReturnKind.getBitCount();
}
- private static JavaKind kindFromSize(int sizeInBytes, JavaKind matchingKind) {
- if (matchingKind == JavaKind.Object || sizeInBytes * 8 == matchingKind.getBitCount()) {
- /* Out preferred matching kind fits, so we can use it. */
- return matchingKind;
+ private ValueNode convertCIntegerToMethodReturnType(StructuredGraph graph, AnalysisMethod method, long cValue, int bitsInC, boolean isCValueUnsigned) {
+ return convertCIntegerToMethodReturnType(graph, nativeLibs, method.getSignature().getReturnType(), cValue, bitsInC, isCValueUnsigned);
+ }
+
+ /**
+ * Creates a {@link ConstantNode} and ensures that the stamp matches the declared return type of
+ * the method. Similar to the other {@link #convertCIntegerToMethodReturnType} implementation.
+ */
+ public static ValueNode convertCIntegerToMethodReturnType(StructuredGraph graph, NativeLibraries nativeLibs, ResolvedJavaType declaredReturnType,
+ long cValue, int bitsInC, boolean isCValueUnsigned) {
+ JavaKind declaredReturnKind = nativeLibs.getWordTypes().asKind(declaredReturnType);
+ if (declaredReturnKind == JavaKind.Boolean) {
+ /* Convert the C value to a Java boolean (i.e., 32-bit zero or one). */
+ return ConstantNode.forBoolean(cValue != 0, graph);
}
- if (matchingKind == JavaKind.Float || matchingKind == JavaKind.Double) {
- switch (sizeInBytes) {
- case 4:
- return JavaKind.Float;
- case 8:
- return JavaKind.Double;
- }
+ long result = convertCIntegerToMethodReturnType(nativeLibs, declaredReturnType, cValue, bitsInC, isCValueUnsigned);
+ int resultBits = declaredReturnKind.getStackKind().getBitCount();
+
+ /*
+ * Finally, sign- or zero-extend the value. Here, we need to be careful that the stamp of
+ * the result is within the value range of the method's declared return type.
+ */
+ boolean zeroExtend = shouldZeroExtend(nativeLibs, declaredReturnType, isCValueUnsigned, bitsInC);
+ if (zeroExtend) {
+ IntegerStamp stamp = StampFactory.forUnsignedInteger(resultBits, result, result);
+ return ConstantNode.forIntegerStamp(stamp, result, graph);
+ }
+ return ConstantNode.forIntegerBits(resultBits, result, graph);
+ }
+
+ public static Object convertCIntegerToMethodReturnType(NativeLibraries nativeLibs, Class> objectReturnType, long cValue, int bitsInC, boolean isCValueUnsigned) {
+ ResolvedJavaType declaredReturnType = nativeLibs.getMetaAccess().lookupJavaType(getPrimitiveOrWordClass(nativeLibs, objectReturnType));
+ return createReturnObject(objectReturnType, convertCIntegerToMethodReturnType(nativeLibs, declaredReturnType, cValue, bitsInC, isCValueUnsigned));
+ }
+
+ private static long convertCIntegerToMethodReturnType(NativeLibraries nativeLibs, ResolvedJavaType declaredReturnType, long cValue, int bitsInC, boolean isCValueUnsigned) {
+ JavaKind declaredReturnKind = nativeLibs.getWordTypes().asKind(declaredReturnType);
+ if (declaredReturnKind == JavaKind.Boolean) {
+ /* Convert the C value to a Java boolean (i.e., zero or one). */
+ return cValue != 0L ? 1L : 0L;
+ }
+
+ /* Sign- or zero-extend the value. */
+ boolean zeroExtend = shouldZeroExtend(nativeLibs, declaredReturnType, isCValueUnsigned, bitsInC);
+ int inputBits = Math.min(bitsInC, declaredReturnKind.getBitCount());
+ if (zeroExtend) {
+ return CodeUtil.zeroExtend(cValue, inputBits);
+ }
+ return CodeUtil.signExtend(cValue, inputBits);
+ }
+
+ private static Class> getPrimitiveOrWordClass(NativeLibraries nativeLibs, Class> type) {
+ if (type == Boolean.class) {
+ return boolean.class;
+ } else if (type == Byte.class) {
+ return byte.class;
+ } else if (type == Short.class) {
+ return short.class;
+ } else if (type == Character.class) {
+ return char.class;
+ } else if (type == Integer.class) {
+ return int.class;
+ } else if (type == Long.class) {
+ return long.class;
+ } else if (type == Float.class) {
+ return float.class;
+ } else if (type == Double.class) {
+ return double.class;
+ } else if (nativeLibs.getWordTypes().isWord(type)) {
+ return type;
} else {
- switch (sizeInBytes) {
- case 1:
- return JavaKind.Byte;
- case 2:
- return JavaKind.Short;
- case 4:
- return JavaKind.Int;
- case 8:
- return JavaKind.Long;
- }
+ throw VMError.shouldNotReachHere("Unexpected type: " + type);
}
- throw shouldNotReachHere("Unsupported size: " + sizeInBytes);
}
- private static boolean replaceConstant(GraphBuilderContext b, AnalysisMethod method, ConstantInfo constantInfo) {
- Object value = constantInfo.getValueInfo().getProperty();
- JavaKind kind = b.getWordTypes().asKind(b.getInvokeReturnType());
+ private static Object createReturnObject(Class> returnType, long value) {
+ if (returnType == Boolean.class) {
+ return value != 0;
+ } else if (returnType == Byte.class) {
+ return (byte) value;
+ } else if (returnType == Short.class) {
+ return (short) value;
+ } else if (returnType == Character.class) {
+ return (char) value;
+ } else if (returnType == Integer.class) {
+ return (int) value;
+ } else if (returnType == Long.class) {
+ return value;
+ } else if (WordBase.class.isAssignableFrom(returnType)) {
+ return WordBoxFactory.box(value);
+ } else {
+ throw VMError.shouldNotReachHere("Unexpected returnType: " + returnType.getName());
+ }
+ }
- ConstantNode valueNode;
- switch (constantInfo.getKind()) {
- case INTEGER:
- case POINTER:
- if (method.getSignature().getReturnKind() == JavaKind.Boolean) {
- valueNode = ConstantNode.forBoolean((long) value != 0, b.getGraph());
- } else {
- valueNode = ConstantNode.forIntegerKind(kind, (long) value, b.getGraph());
- }
- break;
- case FLOAT:
- valueNode = ConstantNode.forFloatingKind(kind, (double) value, b.getGraph());
- break;
- case STRING:
- case BYTEARRAY:
- valueNode = ConstantNode.forConstant(b.getSnippetReflection().forObject(value), b.getMetaAccess(), b.getGraph());
- break;
- default:
- throw shouldNotReachHere("Unexpected constant kind " + constantInfo);
+ private static ValueNode extend(StructuredGraph graph, ValueNode value, int resultBits, boolean zeroExtend) {
+ if (zeroExtend) {
+ return graph.unique(new ZeroExtendNode(value, resultBits));
}
+ return graph.unique(new SignExtendNode(value, resultBits));
+ }
+
+ private boolean replaceConstant(GraphBuilderContext b, AnalysisMethod method, ConstantInfo constantInfo) {
+ Object value = constantInfo.getValue();
+ JavaKind declaredReturnKind = b.getWordTypes().asKind(b.getInvokeReturnType());
+ StructuredGraph graph = b.getGraph();
+
+ ValueNode valueNode = switch (constantInfo.getKind()) {
+ case INTEGER, POINTER -> convertCIntegerToMethodReturnType(graph, method, (long) value, constantInfo.getSizeInBytes() * Byte.SIZE, constantInfo.isUnsigned());
+ case FLOAT -> ConstantNode.forFloatingKind(declaredReturnKind, (double) value, graph);
+ case STRING, BYTEARRAY -> ConstantNode.forConstant(b.getSnippetReflection().forObject(value), b.getMetaAccess(), graph);
+ default -> throw shouldNotReachHere("Unexpected constant kind " + constantInfo);
+ };
b.push(pushKind(method), valueNode);
return true;
}