diff --git a/common.json b/common.json index 021b263c8277..07a251a455ff 100644 --- a/common.json +++ b/common.json @@ -11,12 +11,12 @@ "oraclejdk11": {"name": "jpg-jdk", "version": "11.0.11", "build_id": "9", "release": true, "platformspecific": true, "extrabundles": ["static-libs"] }, "oraclejdk17": {"name": "jpg-jdk", "version": "17.0.1", "build_id": "12", "release": true, "platformspecific": true, "extrabundles": ["static-libs"]}, - "labsjdk-ce-17": {"name": "labsjdk", "version": "ce-17.0.8+7-jvmci-23.0-b15", "platformspecific": true }, - "labsjdk-ce-17Debug": {"name": "labsjdk", "version": "ce-17.0.8+7-jvmci-23.0-b15-debug", "platformspecific": true }, - "labsjdk-ce-17-llvm": {"name": "labsjdk", "version": "ce-17.0.8+7-jvmci-23.0-b15-sulong", "platformspecific": true }, - "labsjdk-ee-17": {"name": "labsjdk", "version": "ee-17.0.8+9-jvmci-23.0-b14", "platformspecific": true }, - "labsjdk-ee-17Debug": {"name": "labsjdk", "version": "ee-17.0.8+9-jvmci-23.0-b14-debug", "platformspecific": true }, - "labsjdk-ee-17-llvm": {"name": "labsjdk", "version": "ee-17.0.8+9-jvmci-23.0-b14-sulong", "platformspecific": true }, + "labsjdk-ce-17": {"name": "labsjdk", "version": "ce-17.0.9+9-jvmci-23.0-b22", "platformspecific": true }, + "labsjdk-ce-17Debug": {"name": "labsjdk", "version": "ce-17.0.9+9-jvmci-23.0-b22-debug", "platformspecific": true }, + "labsjdk-ce-17-llvm": {"name": "labsjdk", "version": "ce-17.0.9+9-jvmci-23.0-b22-sulong", "platformspecific": true }, + "labsjdk-ee-17": {"name": "labsjdk", "version": "ee-17.0.9+11-jvmci-23.0-b21", "platformspecific": true }, + "labsjdk-ee-17Debug": {"name": "labsjdk", "version": "ee-17.0.9+11-jvmci-23.0-b21-debug", "platformspecific": true }, + "labsjdk-ee-17-llvm": {"name": "labsjdk", "version": "ee-17.0.9+11-jvmci-23.0-b21-sulong", "platformspecific": true }, "oraclejdk19": {"name": "jpg-jdk", "version": "19", "build_id": "26", "release": true, "platformspecific": true, "extrabundles": ["static-libs"]}, "labsjdk-ce-19": {"name": "labsjdk", "version": "ce-19.0.1+10-jvmci-23.0-b04", "platformspecific": true }, diff --git a/compiler/mx.compiler/suite.py b/compiler/mx.compiler/suite.py index b5784858d6b5..179a73ae7bab 100644 --- a/compiler/mx.compiler/suite.py +++ b/compiler/mx.compiler/suite.py @@ -4,8 +4,8 @@ "sourceinprojectwhitelist" : [], "groupId" : "org.graalvm.compiler", - "version" : "23.0.2.0", - "release" : True, + "version" : "23.0.2.1", + "release" : False, "url" : "http://www.graalvm.org/", "developer" : { "name" : "GraalVM Development", @@ -1526,6 +1526,11 @@ "requires" : [ "jdk.unsupported", ], + "requiresConcealed" : { + "java.base" : [ + "jdk.internal.vm.annotation", + ] + }, "checkstyle" : "org.graalvm.compiler.graph", "javaCompliance" : "17+", "workingSets" : "Graal,Test", diff --git a/compiler/src/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/meta/UnimplementedGraalIntrinsics.java b/compiler/src/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/meta/UnimplementedGraalIntrinsics.java index a3b3afe58fec..0c4b6fe2a47c 100644 --- a/compiler/src/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/meta/UnimplementedGraalIntrinsics.java +++ b/compiler/src/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/meta/UnimplementedGraalIntrinsics.java @@ -399,7 +399,7 @@ public UnimplementedGraalIntrinsics(GraalHotSpotVMConfig config, Architecture ar "jdk/internal/vm/vector/VectorSupport.unaryOp(ILjava/lang/Class;Ljava/lang/Class;ILjava/lang/Object;Ljava/util/function/Function;)Ljava/lang/Object;" // @formatter:on ); - add(ignore, + add(toBeInvestigated, "jdk/internal/misc/Unsafe.storeStoreFence()V"); } diff --git a/compiler/src/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/replacements/HotSpotAllocationSnippets.java b/compiler/src/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/replacements/HotSpotAllocationSnippets.java index a353a20ccfcd..d0329a14f3d3 100644 --- a/compiler/src/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/replacements/HotSpotAllocationSnippets.java +++ b/compiler/src/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/replacements/HotSpotAllocationSnippets.java @@ -146,10 +146,11 @@ public HotSpotAllocationSnippets(GraalHotSpotVMConfig config, HotSpotRegistersPr @Snippet protected Object allocateInstance(KlassPointer hub, @ConstantParameter long size, + @ConstantParameter boolean forceSlowPath, @ConstantParameter FillContent fillContents, @ConstantParameter boolean emitMemoryBarrier, @ConstantParameter HotSpotAllocationProfilingData profilingData) { - Object result = allocateInstanceImpl(hub.asWord(), WordFactory.unsigned(size), fillContents, emitMemoryBarrier, true, profilingData); + Object result = allocateInstanceImpl(hub.asWord(), WordFactory.unsigned(size), forceSlowPath, fillContents, emitMemoryBarrier, true, profilingData); return piCastToSnippetReplaceeStamp(result); } @@ -194,7 +195,7 @@ public Object allocateInstanceDynamic(@NonNullParameter Class type, * binding of parameters is not yet supported by the GraphBuilderPlugin system. */ UnsignedWord size = WordFactory.unsigned(layoutHelper); - return allocateInstanceImpl(nonNullHub.asWord(), size, fillContents, emitMemoryBarrier, false, profilingData); + return allocateInstanceImpl(nonNullHub.asWord(), size, false, fillContents, emitMemoryBarrier, false, profilingData); } } else { DeoptimizeNode.deopt(None, RuntimeConstraint); @@ -657,12 +658,14 @@ public void lower(NewInstanceNode node, LoweringTool tool) { HotSpotResolvedObjectType type = (HotSpotResolvedObjectType) node.instanceClass(); assert !type.isArray(); ConstantNode hub = ConstantNode.forConstant(KlassPointerStamp.klassNonNull(), type.klass(), tool.getMetaAccess(), graph); - long size = instanceSize(type); + long size = type.instanceSize(); OptionValues localOptions = graph.getOptions(); Arguments args = new Arguments(allocateInstance, graph.getGuardsStage(), tool.getLoweringStage()); args.add("hub", hub); - args.addConst("size", size); + // instanceSize returns a negative number for types which should be slow path allocated + args.addConst("size", Math.abs(size)); + args.addConst("forceSlowPath", size < 0); args.addConst("fillContents", FillContent.fromBoolean(node.fillContents())); args.addConst("emitMemoryBarrier", node.emitMemoryBarrier()); args.addConst("profilingData", getProfilingData(localOptions, "instance", type)); @@ -794,11 +797,6 @@ private static HotSpotResolvedObjectType lookupArrayClass(LoweringTool tool, Jav return HotSpotAllocationSnippets.lookupArrayClass(tool.getMetaAccess(), kind); } - private static long instanceSize(HotSpotResolvedObjectType type) { - long size = type.instanceSize(); - assert size >= 0; - return size; - } } private static class HotSpotAllocationProfilingData extends AllocationProfilingData { diff --git a/compiler/src/org.graalvm.compiler.jtt/src/org/graalvm/compiler/hotspot/test/HumongousReferenceObjectTest.java b/compiler/src/org.graalvm.compiler.jtt/src/org/graalvm/compiler/hotspot/test/HumongousReferenceObjectTest.java new file mode 100644 index 000000000000..57aee8cd52b6 --- /dev/null +++ b/compiler/src/org.graalvm.compiler.jtt/src/org/graalvm/compiler/hotspot/test/HumongousReferenceObjectTest.java @@ -0,0 +1,386 @@ +/* + * Copyright (c) 2016, 2023, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.graalvm.compiler.hotspot.test; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import org.graalvm.compiler.core.test.SubprocessTest; +import org.junit.Test; + +import jdk.internal.vm.annotation.Contended; + +@SuppressWarnings("unused") +public class HumongousReferenceObjectTest extends SubprocessTest { + /* + * Due to 300 fields with 8K @Contended padding around each field, it takes 2.4M bytes per + * instance. With small G1 regions, it is bound to cross regions. G1 should properly (card) mark + * the object nevertheless. With 128M heap, it is enough to allocate ~100 of these objects to + * provoke at least one GC. + */ + + static volatile Object instance; + + public static void testSnippet() { + for (int c = 0; c < 100; c++) { + instance = new HumongousReferenceObjectTest(); + } + } + + public void runSubprocessTest(String... args) throws IOException, InterruptedException { + List newArgs = new ArrayList<>(); + Collections.addAll(newArgs, args); + // Filter out any explicitly selected GC + newArgs.remove("-XX:+UseZGC"); + newArgs.remove("-XX:+UseG1GC"); + newArgs.remove("-XX:+UseParallelGC"); + + launchSubprocess(() -> { + test("testSnippet"); + }, newArgs.toArray(new String[0])); + + // Test without assertions as well + newArgs.add("-da"); + launchSubprocess(() -> { + test("testSnippet"); + }, newArgs.toArray(new String[0])); + } + + @Test + public void testG1() throws IOException, InterruptedException { + String[] sizes = {"-XX:G1HeapRegionSize=1M", "-XX:G1HeapRegionSize=2M", "-XX:G1HeapRegionSize=4M", "-XX:G1HeapRegionSize=8M"}; + for (String size : sizes) { + runSubprocessTest("-XX:+UseG1GC", "-XX:+EnableContended", "-XX:-RestrictContended", "-Xmx128m", "-XX:ContendedPaddingWidth=8192", size); + } + } + + @Test + public void testParallel() throws IOException, InterruptedException { + runSubprocessTest("-XX:+UseParallelGC", "-XX:+EnableContended", "-XX:-RestrictContended", "-Xmx128m", "-XX:ContendedPaddingWidth=8192"); + } + + @Contended Integer int1 = 1; + @Contended Integer int2 = 2; + @Contended Integer int3 = 3; + @Contended Integer int4 = 4; + @Contended Integer int5 = 5; + @Contended Integer int6 = 6; + @Contended Integer int7 = 7; + @Contended Integer int8 = 8; + @Contended Integer int9 = 9; + @Contended Integer int10 = 10; + @Contended Integer int11 = 11; + @Contended Integer int12 = 12; + @Contended Integer int13 = 13; + @Contended Integer int14 = 14; + @Contended Integer int15 = 15; + @Contended Integer int16 = 16; + @Contended Integer int17 = 17; + @Contended Integer int18 = 18; + @Contended Integer int19 = 19; + @Contended Integer int20 = 20; + @Contended Integer int21 = 21; + @Contended Integer int22 = 22; + @Contended Integer int23 = 23; + @Contended Integer int24 = 24; + @Contended Integer int25 = 25; + @Contended Integer int26 = 26; + @Contended Integer int27 = 27; + @Contended Integer int28 = 28; + @Contended Integer int29 = 29; + @Contended Integer int30 = 30; + @Contended Integer int31 = 31; + @Contended Integer int32 = 32; + @Contended Integer int33 = 33; + @Contended Integer int34 = 34; + @Contended Integer int35 = 35; + @Contended Integer int36 = 36; + @Contended Integer int37 = 37; + @Contended Integer int38 = 38; + @Contended Integer int39 = 39; + @Contended Integer int40 = 40; + @Contended Integer int41 = 41; + @Contended Integer int42 = 42; + @Contended Integer int43 = 43; + @Contended Integer int44 = 44; + @Contended Integer int45 = 45; + @Contended Integer int46 = 46; + @Contended Integer int47 = 47; + @Contended Integer int48 = 48; + @Contended Integer int49 = 49; + @Contended Integer int50 = 50; + @Contended Integer int51 = 51; + @Contended Integer int52 = 52; + @Contended Integer int53 = 53; + @Contended Integer int54 = 54; + @Contended Integer int55 = 55; + @Contended Integer int56 = 56; + @Contended Integer int57 = 57; + @Contended Integer int58 = 58; + @Contended Integer int59 = 59; + @Contended Integer int60 = 60; + @Contended Integer int61 = 61; + @Contended Integer int62 = 62; + @Contended Integer int63 = 63; + @Contended Integer int64 = 64; + @Contended Integer int65 = 65; + @Contended Integer int66 = 66; + @Contended Integer int67 = 67; + @Contended Integer int68 = 68; + @Contended Integer int69 = 69; + @Contended Integer int70 = 70; + @Contended Integer int71 = 71; + @Contended Integer int72 = 72; + @Contended Integer int73 = 73; + @Contended Integer int74 = 74; + @Contended Integer int75 = 75; + @Contended Integer int76 = 76; + @Contended Integer int77 = 77; + @Contended Integer int78 = 78; + @Contended Integer int79 = 79; + @Contended Integer int80 = 80; + @Contended Integer int81 = 81; + @Contended Integer int82 = 82; + @Contended Integer int83 = 83; + @Contended Integer int84 = 84; + @Contended Integer int85 = 85; + @Contended Integer int86 = 86; + @Contended Integer int87 = 87; + @Contended Integer int88 = 88; + @Contended Integer int89 = 89; + @Contended Integer int90 = 90; + @Contended Integer int91 = 91; + @Contended Integer int92 = 92; + @Contended Integer int93 = 93; + @Contended Integer int94 = 94; + @Contended Integer int95 = 95; + @Contended Integer int96 = 96; + @Contended Integer int97 = 97; + @Contended Integer int98 = 98; + @Contended Integer int99 = 99; + @Contended Integer int100 = 100; + @Contended Integer int101 = 101; + @Contended Integer int102 = 102; + @Contended Integer int103 = 103; + @Contended Integer int104 = 104; + @Contended Integer int105 = 105; + @Contended Integer int106 = 106; + @Contended Integer int107 = 107; + @Contended Integer int108 = 108; + @Contended Integer int109 = 109; + @Contended Integer int110 = 110; + @Contended Integer int111 = 111; + @Contended Integer int112 = 112; + @Contended Integer int113 = 113; + @Contended Integer int114 = 114; + @Contended Integer int115 = 115; + @Contended Integer int116 = 116; + @Contended Integer int117 = 117; + @Contended Integer int118 = 118; + @Contended Integer int119 = 119; + @Contended Integer int120 = 120; + @Contended Integer int121 = 121; + @Contended Integer int122 = 122; + @Contended Integer int123 = 123; + @Contended Integer int124 = 124; + @Contended Integer int125 = 125; + @Contended Integer int126 = 126; + @Contended Integer int127 = 127; + @Contended Integer int128 = 128; + @Contended Integer int129 = 129; + @Contended Integer int130 = 130; + @Contended Integer int131 = 131; + @Contended Integer int132 = 132; + @Contended Integer int133 = 133; + @Contended Integer int134 = 134; + @Contended Integer int135 = 135; + @Contended Integer int136 = 136; + @Contended Integer int137 = 137; + @Contended Integer int138 = 138; + @Contended Integer int139 = 139; + @Contended Integer int140 = 140; + @Contended Integer int141 = 141; + @Contended Integer int142 = 142; + @Contended Integer int143 = 143; + @Contended Integer int144 = 144; + @Contended Integer int145 = 145; + @Contended Integer int146 = 146; + @Contended Integer int147 = 147; + @Contended Integer int148 = 148; + @Contended Integer int149 = 149; + @Contended Integer int150 = 150; + @Contended Integer int151 = 151; + @Contended Integer int152 = 152; + @Contended Integer int153 = 153; + @Contended Integer int154 = 154; + @Contended Integer int155 = 155; + @Contended Integer int156 = 156; + @Contended Integer int157 = 157; + @Contended Integer int158 = 158; + @Contended Integer int159 = 159; + @Contended Integer int160 = 160; + @Contended Integer int161 = 161; + @Contended Integer int162 = 162; + @Contended Integer int163 = 163; + @Contended Integer int164 = 164; + @Contended Integer int165 = 165; + @Contended Integer int166 = 166; + @Contended Integer int167 = 167; + @Contended Integer int168 = 168; + @Contended Integer int169 = 169; + @Contended Integer int170 = 170; + @Contended Integer int171 = 171; + @Contended Integer int172 = 172; + @Contended Integer int173 = 173; + @Contended Integer int174 = 174; + @Contended Integer int175 = 175; + @Contended Integer int176 = 176; + @Contended Integer int177 = 177; + @Contended Integer int178 = 178; + @Contended Integer int179 = 179; + @Contended Integer int180 = 180; + @Contended Integer int181 = 181; + @Contended Integer int182 = 182; + @Contended Integer int183 = 183; + @Contended Integer int184 = 184; + @Contended Integer int185 = 185; + @Contended Integer int186 = 186; + @Contended Integer int187 = 187; + @Contended Integer int188 = 188; + @Contended Integer int189 = 189; + @Contended Integer int190 = 190; + @Contended Integer int191 = 191; + @Contended Integer int192 = 192; + @Contended Integer int193 = 193; + @Contended Integer int194 = 194; + @Contended Integer int195 = 195; + @Contended Integer int196 = 196; + @Contended Integer int197 = 197; + @Contended Integer int198 = 198; + @Contended Integer int199 = 199; + @Contended Integer int200 = 200; + @Contended Integer int201 = 201; + @Contended Integer int202 = 202; + @Contended Integer int203 = 203; + @Contended Integer int204 = 204; + @Contended Integer int205 = 205; + @Contended Integer int206 = 206; + @Contended Integer int207 = 207; + @Contended Integer int208 = 208; + @Contended Integer int209 = 209; + @Contended Integer int210 = 210; + @Contended Integer int211 = 211; + @Contended Integer int212 = 212; + @Contended Integer int213 = 213; + @Contended Integer int214 = 214; + @Contended Integer int215 = 215; + @Contended Integer int216 = 216; + @Contended Integer int217 = 217; + @Contended Integer int218 = 218; + @Contended Integer int219 = 219; + @Contended Integer int220 = 220; + @Contended Integer int221 = 221; + @Contended Integer int222 = 222; + @Contended Integer int223 = 223; + @Contended Integer int224 = 224; + @Contended Integer int225 = 225; + @Contended Integer int226 = 226; + @Contended Integer int227 = 227; + @Contended Integer int228 = 228; + @Contended Integer int229 = 229; + @Contended Integer int230 = 230; + @Contended Integer int231 = 231; + @Contended Integer int232 = 232; + @Contended Integer int233 = 233; + @Contended Integer int234 = 234; + @Contended Integer int235 = 235; + @Contended Integer int236 = 236; + @Contended Integer int237 = 237; + @Contended Integer int238 = 238; + @Contended Integer int239 = 239; + @Contended Integer int240 = 240; + @Contended Integer int241 = 241; + @Contended Integer int242 = 242; + @Contended Integer int243 = 243; + @Contended Integer int244 = 244; + @Contended Integer int245 = 245; + @Contended Integer int246 = 246; + @Contended Integer int247 = 247; + @Contended Integer int248 = 248; + @Contended Integer int249 = 249; + @Contended Integer int250 = 250; + @Contended Integer int251 = 251; + @Contended Integer int252 = 252; + @Contended Integer int253 = 253; + @Contended Integer int254 = 254; + @Contended Integer int255 = 255; + @Contended Integer int256 = 256; + @Contended Integer int257 = 257; + @Contended Integer int258 = 258; + @Contended Integer int259 = 259; + @Contended Integer int260 = 260; + @Contended Integer int261 = 261; + @Contended Integer int262 = 262; + @Contended Integer int263 = 263; + @Contended Integer int264 = 264; + @Contended Integer int265 = 265; + @Contended Integer int266 = 266; + @Contended Integer int267 = 267; + @Contended Integer int268 = 268; + @Contended Integer int269 = 269; + @Contended Integer int270 = 270; + @Contended Integer int271 = 271; + @Contended Integer int272 = 272; + @Contended Integer int273 = 273; + @Contended Integer int274 = 274; + @Contended Integer int275 = 275; + @Contended Integer int276 = 276; + @Contended Integer int277 = 277; + @Contended Integer int278 = 278; + @Contended Integer int279 = 279; + @Contended Integer int280 = 280; + @Contended Integer int281 = 281; + @Contended Integer int282 = 282; + @Contended Integer int283 = 283; + @Contended Integer int284 = 284; + @Contended Integer int285 = 285; + @Contended Integer int286 = 286; + @Contended Integer int287 = 287; + @Contended Integer int288 = 288; + @Contended Integer int289 = 289; + @Contended Integer int290 = 290; + @Contended Integer int291 = 291; + @Contended Integer int292 = 292; + @Contended Integer int293 = 293; + @Contended Integer int294 = 294; + @Contended Integer int295 = 295; + @Contended Integer int296 = 296; + @Contended Integer int297 = 297; + @Contended Integer int298 = 298; + @Contended Integer int299 = 299; + @Contended Integer int300 = 300; +} diff --git a/compiler/src/org.graalvm.compiler.lir.aarch64/src/org/graalvm/compiler/lir/aarch64/AArch64EncodeArrayOp.java b/compiler/src/org.graalvm.compiler.lir.aarch64/src/org/graalvm/compiler/lir/aarch64/AArch64EncodeArrayOp.java index 417b78f7b57b..632b7a6128ee 100644 --- a/compiler/src/org.graalvm.compiler.lir.aarch64/src/org/graalvm/compiler/lir/aarch64/AArch64EncodeArrayOp.java +++ b/compiler/src/org.graalvm.compiler.lir.aarch64/src/org/graalvm/compiler/lir/aarch64/AArch64EncodeArrayOp.java @@ -24,20 +24,20 @@ */ package org.graalvm.compiler.lir.aarch64; -import static jdk.vm.ci.aarch64.AArch64.SIMD; import static jdk.vm.ci.code.ValueUtil.asRegister; +import static org.graalvm.compiler.asm.aarch64.AArch64ASIMDAssembler.ASIMDSize.FullReg; +import static org.graalvm.compiler.asm.aarch64.AArch64Address.createBaseRegisterOnlyAddress; +import static org.graalvm.compiler.asm.aarch64.AArch64Address.createImmediateAddress; import static org.graalvm.compiler.lir.LIRInstruction.OperandFlag.REG; import org.graalvm.compiler.asm.Label; import org.graalvm.compiler.asm.aarch64.AArch64ASIMDAssembler.ASIMDInstruction; -import org.graalvm.compiler.asm.aarch64.AArch64ASIMDAssembler.ASIMDSize; import org.graalvm.compiler.asm.aarch64.AArch64ASIMDAssembler.ElementSize; import org.graalvm.compiler.asm.aarch64.AArch64Address; import org.graalvm.compiler.asm.aarch64.AArch64Address.AddressingMode; import org.graalvm.compiler.asm.aarch64.AArch64Assembler.ConditionFlag; import org.graalvm.compiler.asm.aarch64.AArch64Assembler.PrefetchMode; import org.graalvm.compiler.asm.aarch64.AArch64MacroAssembler; -import org.graalvm.compiler.core.common.LIRKind; import org.graalvm.compiler.lir.LIRInstructionClass; import org.graalvm.compiler.lir.Opcode; import org.graalvm.compiler.lir.StubPort; @@ -45,7 +45,6 @@ import org.graalvm.compiler.lir.gen.LIRGeneratorTool; import org.graalvm.compiler.lir.gen.LIRGeneratorTool.CharsetName; -import jdk.vm.ci.aarch64.AArch64; import jdk.vm.ci.aarch64.AArch64Kind; import jdk.vm.ci.code.Register; import jdk.vm.ci.meta.AllocatableValue; @@ -59,7 +58,7 @@ sha1 = "cc047926c7a2f0dda07e6480ba61e23b3f460638") // @formatter:on @Opcode("AArch64_ENCODE_ARRAY") -public final class AArch64EncodeArrayOp extends AArch64LIRInstruction { +public final class AArch64EncodeArrayOp extends AArch64ComplexVectorOp { public static final LIRInstructionClass TYPE = LIRInstructionClass.create(AArch64EncodeArrayOp.class); @Def({REG}) private Value resultValue; @@ -70,12 +69,7 @@ public final class AArch64EncodeArrayOp extends AArch64LIRInstruction { @Temp({REG}) private AllocatableValue srcValue; @Temp({REG}) private AllocatableValue dstValue; - @Temp({REG}) private Value vectorTempValue0; - @Temp({REG}) private Value vectorTempValue1; - @Temp({REG}) private Value vectorTempValue2; - @Temp({REG}) private Value vectorTempValue3; - @Temp({REG}) private Value vectorTempValue4; - @Temp({REG}) private Value vectorTempValue5; + @Temp({REG}) private Value[] vectorTempValue; private final CharsetName charset; @@ -90,13 +84,7 @@ public AArch64EncodeArrayOp(LIRGeneratorTool tool, Value result, Value src, Valu this.srcValue = tool.newVariable(src.getValueKind()); this.dstValue = tool.newVariable(dst.getValueKind()); - LIRKind vectorKind = LIRKind.value(tool.target().arch.getLargestStorableKind(SIMD)); - this.vectorTempValue0 = AArch64.v0.asValue(vectorKind); - this.vectorTempValue1 = AArch64.v1.asValue(vectorKind); - this.vectorTempValue2 = AArch64.v2.asValue(vectorKind); - this.vectorTempValue3 = AArch64.v3.asValue(vectorKind); - this.vectorTempValue4 = AArch64.v4.asValue(vectorKind); - this.vectorTempValue5 = AArch64.v5.asValue(vectorKind); + this.vectorTempValue = allocateConsecutiveVectorRegisters(tool, charset == CharsetName.ASCII ? 7 : 6); this.charset = charset; assert charset == CharsetName.ASCII || charset == CharsetName.ISO_8859_1; @@ -114,99 +102,89 @@ protected void emitCode(CompilationResultBuilder crb, AArch64MacroAssembler masm Register len = asRegister(lenValue); Register res = asRegister(resultValue); - Register vtmp0 = asRegister(vectorTempValue0); - Register vtmp1 = asRegister(vectorTempValue1); - Register vtmp2 = asRegister(vectorTempValue2); - Register vtmp3 = asRegister(vectorTempValue3); - - try (AArch64MacroAssembler.ScratchRegister sc1 = masm.getScratchRegister(); - AArch64MacroAssembler.ScratchRegister sc2 = masm.getScratchRegister()) { - Register cnt = res; - Register max = sc1.getRegister(); - Register chk = sc2.getRegister(); - - masm.prfm(AArch64Address.createBaseRegisterOnlyAddress(64, src), PrefetchMode.PLDL1STRM); - masm.mov(32, cnt, len); - - Label labelLoop32 = new Label(); - Label labelDone32 = new Label(); - Label labelFail32 = new Label(); - - masm.bind(labelLoop32); - masm.compare(32, cnt, 32); - masm.branchConditionally(ConditionFlag.LT, labelDone32); - masm.neon.ld1MultipleVVVV(ASIMDSize.FullReg, ElementSize.HalfWord, vtmp0, vtmp1, vtmp2, vtmp3, - AArch64Address.createStructureImmediatePostIndexAddress(ASIMDInstruction.LD1_MULTIPLE_4R, ASIMDSize.FullReg, ElementSize.HalfWord, src, 64)); - // Extract lower bytes. - Register vlo0 = asRegister(vectorTempValue4); - Register vlo1 = asRegister(vectorTempValue5); - masm.neon.uzp1VVV(ASIMDSize.FullReg, ElementSize.Byte, vlo0, vtmp0, vtmp1); - masm.neon.uzp1VVV(ASIMDSize.FullReg, ElementSize.Byte, vlo1, vtmp2, vtmp3); - // Merge bits... - masm.neon.orrVVV(ASIMDSize.FullReg, vtmp0, vtmp0, vtmp1); - masm.neon.orrVVV(ASIMDSize.FullReg, vtmp2, vtmp2, vtmp3); - // Extract merged upper bytes. - Register vhix = vtmp0; - masm.neon.uzp2VVV(ASIMDSize.FullReg, ElementSize.Byte, vhix, vtmp0, vtmp2); - // ISO-check on hi-parts (all zero). - if (ascii) { - // + ASCII-check on lo-parts (no sign). - Register vlox = vtmp1; // Merge lower bytes. - masm.neon.orrVVV(ASIMDSize.FullReg, vlox, vlo0, vlo1); - masm.neon.umovGX(ElementSize.DoubleWord, chk, vhix, 1); - masm.neon.cmltZeroVV(ASIMDSize.FullReg, ElementSize.Byte, vlox, vlox); - masm.fmov(64, max, vhix); - masm.neon.umaxvSV(ASIMDSize.FullReg, ElementSize.Byte, vlox, vlox); - masm.orr(64, chk, chk, max); - masm.neon.umovGX(ElementSize.Byte, max, vlo0, 0); - masm.orr(64, chk, chk, max); - } else { - masm.neon.umovGX(ElementSize.DoubleWord, chk, vhix, 1); - masm.fmov(64, max, vhix); - masm.orr(64, chk, chk, max); - } - - masm.cbnz(64, chk, labelFail32); - masm.sub(32, cnt, cnt, 32); - masm.neon.st1MultipleVV(ASIMDSize.FullReg, ElementSize.Byte, vlo0, vlo1, - AArch64Address.createStructureImmediatePostIndexAddress(ASIMDInstruction.ST1_MULTIPLE_2R, ASIMDSize.FullReg, ElementSize.Byte, dst, 32)); - masm.jmp(labelLoop32); - - masm.bind(labelFail32); - masm.sub(64, src, src, 64); - masm.bind(labelDone32); - - Label labelLoop8 = new Label(); - Label labelSkip8 = new Label(); - - masm.bind(labelLoop8); - masm.compare(32, cnt, 8); - masm.branchConditionally(ConditionFlag.LT, labelSkip8); - - Register vhi = vtmp0; - Register vlo = vtmp1; - masm.neon.ld1MultipleV(ASIMDSize.FullReg, ElementSize.HalfWord, vtmp3, AArch64Address.createBaseRegisterOnlyAddress(64, src)); - masm.neon.uzp1VVV(ASIMDSize.FullReg, ElementSize.Byte, vlo, vtmp3, vtmp3); - masm.neon.uzp2VVV(ASIMDSize.FullReg, ElementSize.Byte, vhi, vtmp3, vtmp3); - // ISO-check on hi-parts (all zero). - if (ascii) { - // + ASCII-check on lo-parts (no sign). - masm.neon.cmltZeroVV(ASIMDSize.FullReg, ElementSize.Byte, vtmp2, vlo); - masm.fmov(64, chk, vhi); - masm.neon.umaxvSV(ASIMDSize.FullReg, ElementSize.Byte, vtmp2, vtmp2); - masm.neon.umovGX(ElementSize.Byte, max, vtmp2, 0); - masm.orr(64, chk, chk, max); - } else { - masm.fmov(64, chk, vhi); - } - - masm.cbnz(64, chk, labelSkip8); - masm.fstr(64, vlo, AArch64Address.createImmediateAddress(64, AddressingMode.IMMEDIATE_POST_INDEXED, dst, 8)); - masm.sub(32, cnt, cnt, 8); - masm.add(64, src, src, 16); - masm.jmp(labelLoop8); - - masm.bind(labelSkip8); + Register vtmp0 = asRegister(vectorTempValue[0]); + Register vtmp1 = asRegister(vectorTempValue[1]); + Register vtmp2 = asRegister(vectorTempValue[2]); + Register vtmp3 = asRegister(vectorTempValue[3]); + Register vlo0 = asRegister(vectorTempValue[4]); + Register vlo1 = asRegister(vectorTempValue[5]); + Register vmask = ascii ? asRegister(vectorTempValue[6]) : null; + + Register cnt = res; + + masm.prfm(createBaseRegisterOnlyAddress(64, src), PrefetchMode.PLDL1STRM); + masm.mov(32, cnt, len); + + if (ascii) { + masm.neon.moveVI(FullReg, ElementSize.HalfWord, vmask, (short) 0xff80); + } + + Label labelLoop32 = new Label(); + Label labelDone32 = new Label(); + Label labelFail32 = new Label(); + + masm.bind(labelLoop32); + masm.compare(32, cnt, 32); + masm.branchConditionally(ConditionFlag.LT, labelDone32); + masm.neon.ld1MultipleVVVV(FullReg, ElementSize.HalfWord, vtmp0, vtmp1, vtmp2, vtmp3, + AArch64Address.createStructureImmediatePostIndexAddress(ASIMDInstruction.LD1_MULTIPLE_4R, FullReg, ElementSize.HalfWord, src, 64)); + // Extract lower bytes. + masm.neon.uzp1VVV(FullReg, ElementSize.Byte, vlo0, vtmp0, vtmp1); + masm.neon.uzp1VVV(FullReg, ElementSize.Byte, vlo1, vtmp2, vtmp3); + // Merge bits... + masm.neon.orrVVV(FullReg, vtmp0, vtmp0, vtmp1); + masm.neon.orrVVV(FullReg, vtmp2, vtmp2, vtmp3); + masm.neon.orrVVV(FullReg, vtmp0, vtmp0, vtmp2); + if (ascii) { + // Check if all merged chars are <= 0x7f + masm.neon.cmtstVVV(FullReg, ElementSize.HalfWord, vtmp0, vtmp0, vmask); + // Narrow result to bytes for fcmpZero + masm.neon.xtnVV(ElementSize.HalfWord.narrow(), vtmp0, vtmp0); + } else { + // Extract merged upper bytes for ISO check (all zero). + masm.neon.uzp2VVV(FullReg, ElementSize.Byte, vtmp0, vtmp0, vtmp0); + } + + masm.fcmpZero(64, vtmp0); + masm.branchConditionally(ConditionFlag.NE, labelFail32); + masm.sub(32, cnt, cnt, 32); + masm.fstp(128, vlo0, vlo1, createImmediateAddress(FullReg.bits(), AddressingMode.IMMEDIATE_PAIR_POST_INDEXED, dst, 32)); + masm.jmp(labelLoop32); + + masm.bind(labelFail32); + masm.sub(64, src, src, 64); + masm.bind(labelDone32); + + Label labelLoop8 = new Label(); + Label labelSkip8 = new Label(); + + masm.bind(labelLoop8); + masm.compare(32, cnt, 8); + masm.branchConditionally(ConditionFlag.LT, labelSkip8); + + masm.fldr(128, vtmp0, createBaseRegisterOnlyAddress(128, src)); + // Extract lower bytes. + masm.neon.uzp1VVV(FullReg, ElementSize.Byte, vlo0, vtmp0, vtmp0); + if (ascii) { + // Check if all merged chars are <= 0x7f + masm.neon.cmtstVVV(FullReg, ElementSize.HalfWord, vtmp0, vtmp0, vmask); + // Narrow result to bytes for fcmpZero + masm.neon.xtnVV(ElementSize.HalfWord.narrow(), vtmp0, vtmp0); + } else { + // Extract upper bytes for ISO check (all zero). + masm.neon.uzp2VVV(FullReg, ElementSize.Byte, vtmp0, vtmp0, vtmp0); + } + + masm.fcmpZero(64, vtmp0); + masm.branchConditionally(ConditionFlag.NE, labelSkip8); + masm.fstr(64, vlo0, createImmediateAddress(64, AddressingMode.IMMEDIATE_POST_INDEXED, dst, 8)); + masm.sub(32, cnt, cnt, 8); + masm.add(64, src, src, 16); + masm.jmp(labelLoop8); + + masm.bind(labelSkip8); + + try (AArch64MacroAssembler.ScratchRegister sc1 = masm.getScratchRegister()) { Label labelLoop = new Label(); Label labelDone = new Label(); @@ -214,10 +192,10 @@ protected void emitCode(CompilationResultBuilder crb, AArch64MacroAssembler masm masm.cbz(32, cnt, labelDone); masm.bind(labelLoop); Register chr = sc1.getRegister(); - masm.ldr(16, chr, AArch64Address.createImmediateAddress(16, AddressingMode.IMMEDIATE_POST_INDEXED, src, 2)); + masm.ldr(16, chr, createImmediateAddress(16, AddressingMode.IMMEDIATE_POST_INDEXED, src, 2)); masm.tst(32, chr, ascii ? 0xFF80 : 0xFF00); masm.branchConditionally(ConditionFlag.NE, labelDone); - masm.str(8, chr, AArch64Address.createImmediateAddress(8, AddressingMode.IMMEDIATE_POST_INDEXED, dst, 1)); + masm.str(8, chr, createImmediateAddress(8, AddressingMode.IMMEDIATE_POST_INDEXED, dst, 1)); masm.subs(32, cnt, cnt, 1); masm.branchConditionally(ConditionFlag.GT, labelLoop); diff --git a/compiler/src/org.graalvm.compiler.lir.aarch64/src/org/graalvm/compiler/lir/aarch64/AArch64Move.java b/compiler/src/org.graalvm.compiler.lir.aarch64/src/org/graalvm/compiler/lir/aarch64/AArch64Move.java index fd7dbb39d4ef..3c544f763cc7 100644 --- a/compiler/src/org.graalvm.compiler.lir.aarch64/src/org/graalvm/compiler/lir/aarch64/AArch64Move.java +++ b/compiler/src/org.graalvm.compiler.lir.aarch64/src/org/graalvm/compiler/lir/aarch64/AArch64Move.java @@ -829,13 +829,9 @@ public static class CompressPointerOp extends PointerCompressionOp { public static final LIRInstructionClass TYPE = LIRInstructionClass.create(CompressPointerOp.class); public CompressPointerOp(AllocatableValue result, Value input, AllocatableValue baseRegister, CompressEncoding encoding, boolean nonNull, LIRKindTool lirKindTool) { - this(TYPE, result, input, baseRegister, encoding, nonNull, lirKindTool); - } - - private CompressPointerOp(LIRInstructionClass type, AllocatableValue result, Value input, - AllocatableValue baseRegister, CompressEncoding encoding, boolean nonNull, LIRKindTool lirKindTool) { + super(TYPE, result, input, baseRegister, encoding, nonNull, lirKindTool); - super(type, result, input, baseRegister, encoding, nonNull, lirKindTool); + assert input.getPlatformKind().equals(AArch64Kind.QWORD); } @Override @@ -870,13 +866,22 @@ protected void emitCode(CompilationResultBuilder crb, AArch64MacroAssembler masm public static class UncompressPointerOp extends PointerCompressionOp { public static final LIRInstructionClass TYPE = LIRInstructionClass.create(UncompressPointerOp.class); + /** + * Tracks whether this uncompress expands a 32-bit value to 64-bits. When this is occurs, + * care must be taken to ensure the upper 32-bits of the input value are ignored. + */ + private final boolean uncompress32To64Bits; + public UncompressPointerOp(AllocatableValue result, Value input, AllocatableValue baseRegister, CompressEncoding encoding, boolean nonNull, LIRKindTool lirKindTool) { - this(TYPE, result, input, baseRegister, encoding, nonNull, lirKindTool); - } + super(TYPE, result, input, baseRegister, encoding, nonNull, lirKindTool); - private UncompressPointerOp(LIRInstructionClass type, AllocatableValue result, Value input, - AllocatableValue baseRegister, CompressEncoding encoding, boolean nonNull, LIRKindTool lirKindTool) { - super(type, result, input, baseRegister, encoding, nonNull, lirKindTool); + uncompress32To64Bits = input.getPlatformKind().getSizeInBytes() != result.getPlatformKind().getSizeInBytes(); + if (uncompress32To64Bits) { + assert input.getPlatformKind().equals(AArch64Kind.DWORD); + } else { + assert input.getPlatformKind().equals(AArch64Kind.QWORD); + } + assert result.getPlatformKind().equals(AArch64Kind.QWORD); } @Override @@ -887,14 +892,19 @@ protected void emitCode(CompilationResultBuilder crb, AArch64MacroAssembler masm // result = base + (ptr << shift) if (nonNull || base == null) { - masm.add(64, resultRegister, base == null ? zr : base, inputRegister, AArch64Assembler.ShiftType.LSL, encoding.getShift()); + Register src; + if (uncompress32To64Bits) { + masm.mov(32, resultRegister, inputRegister); + src = resultRegister; + } else { + src = inputRegister; + } + masm.add(64, resultRegister, base == null ? zr : base, src, AArch64Assembler.ShiftType.LSL, encoding.getShift()); } else { // if ptr is null it has to be null after decompression Label done = new Label(); - if (!resultRegister.equals(inputRegister)) { - masm.mov(32, resultRegister, inputRegister); - } - masm.cbz(32, resultRegister, done); + masm.mov(uncompress32To64Bits ? 32 : 64, resultRegister, inputRegister); + masm.cbz(64, resultRegister, done); masm.add(64, resultRegister, base, resultRegister, AArch64Assembler.ShiftType.LSL, encoding.getShift()); masm.bind(done); } @@ -909,13 +919,20 @@ protected ZeroNullConversionOp(LIRInstructionClass getInductionVariables() { } /** - * Collect all the basic induction variables for the loop and the find any induction variables - * which are derived from the basic ones. + * Collect all the {@link BasicInductionVariable} variables for the loop and find any induction + * variables which are {@link DerivedInductionVariable} from the basic ones. An + * induction variable is a + * variable that gets increased/decreased by a fixed != 0 amount every iteration of a loop. The + * {@code !=0} portion is guaranteed by {@link BasicInductionVariable#direction()}. More + * importantly an induction variable, by definition, guarantees that the value of the IV is + * strictly monotonically increasing or decreasing, as long as they don't overflow. A counted + * loop's counter will never overflow (guaranteed by {@link CountedLoopInfo#maxTripCountNode()}) + * other IVs on the same loop might overflow. The extremum computations are correct even in the + * presence of overflow + * + * Typical examples are: + * + *
+     * int basicIV = 0;
+     * while (true) {
+     *     if (basivIV > limit) {
+     *         break;
+     *     }
+     *     int derivedOffsetIV = basicIV + 3;
+     *     int derivedScaledIV = basicIV * 17;
+     *     long derivedConvertedIV = (long) basicIV;
+     *     basicIV = basicIV + 1;
+     *     // and many more
+     * }
+     * 
* * @return a map from node to induction variable */ private EconomicMap findInductionVariables() { EconomicMap currentIvs = EconomicMap.create(Equivalence.IDENTITY); + // first find basic induction variables Queue scanQueue = new LinkedList<>(); LoopBeginNode loopBegin = this.loopBegin(); AbstractEndNode forwardEnd = loopBegin.forwardEnd(); @@ -452,7 +479,7 @@ private EconomicMap findInductionVariables() { if (backValue == phi) { continue; } - ValueNode stride = addSub(this, backValue, phi); + ValueNode stride = calcOffsetTo(this, backValue, phi, false); if (stride != null) { BasicInductionVariable biv = new BasicInductionVariable(this, (ValuePhiNode) phi, phi.valueAt(forwardEnd), stride, (BinaryArithmeticNode) backValue); currentIvs.put(phi, biv); @@ -460,6 +487,7 @@ private EconomicMap findInductionVariables() { } } + // now compute derived ones while (!scanQueue.isEmpty()) { InductionVariable baseIv = scanQueue.remove(); ValueNode baseIvNode = baseIv.valueNode(); @@ -475,13 +503,13 @@ private EconomicMap findInductionVariables() { continue; } InductionVariable iv = null; - ValueNode offset = addSub(this, op, baseIvNode); + ValueNode offset = calcOffsetTo(this, op, baseIvNode, true); ValueNode scale; if (offset != null) { iv = new DerivedOffsetInductionVariable(this, baseIv, offset, (BinaryArithmeticNode) op); } else if (op instanceof NegateNode) { iv = new DerivedScaledInductionVariable(this, baseIv, (NegateNode) op); - } else if ((scale = mul(this, op, baseIvNode)) != null) { + } else if ((scale = calcScaleTo(this, op, baseIvNode)) != null) { iv = new DerivedScaledInductionVariable(this, baseIv, scale, op); } else { boolean isValidConvert = op instanceof PiNode || op instanceof SignExtendNode; @@ -508,19 +536,164 @@ private EconomicMap findInductionVariables() { return currentIvs; } - private static ValueNode addSub(LoopEx loop, ValueNode op, ValueNode base) { - if (op.stamp(NodeView.DEFAULT) instanceof IntegerStamp && (op instanceof AddNode || op instanceof SubNode)) { - BinaryArithmeticNode aritOp = (BinaryArithmeticNode) op; - if (aritOp.getX() == base && loop.isOutsideLoop(aritOp.getY())) { - return aritOp.getY(); - } else if (aritOp.getY() == base && loop.isOutsideLoop(aritOp.getX())) { - return aritOp.getX(); + /** + * Determines if {@code op} is using {@code base} as an input. If so, determines if the other + * input of {@code op} is loop invariant with respect to {@code loop}. This marks one + * fundamental requirement for an offset based induction variable of a loop. + * + *
+     * int basicIV = 0;
+     * while (true) {
+     *     if (basivIV > limit) {
+     *         break;
+     *     }
+     *     basicIV = basicIV + stride;
+     * }
+     * 
+ * + * In the example above the {@code PhiNode} basicIV would be {@code base}, the add operation + * would be {@code op} and the loop invariant stride would be the other input to {@code op} that + * is {@code != base}. + * + * Note that this method is also used to compute {@code DerivedOffsetInductionVariable} which + * are offset of a {@code BasicInductionVariable}. + * + *
+     * int basicIV = 0;
+     * while (true) {
+     *     if (basicIV > limit) {
+     *         break;
+     *     }
+     *     int offsetIV = basicIV + 123;
+     *     basicIV = basicIV + stride;
+     * }
+     * 
+ * + * Such an example can be seen in {@code offsetIV} where {@code base} is the {@code basicIV}, + * i.e., the {@link PhiNode}. + * + * An offset can be positive or negative as well and involve addition or subtraction. This can + * cause some interesting patterns. In general we need to handle the following cases: + * + *
+     * int basicIV = 0;
+     *
+     * while (true) {
+     *     if (compare(basicIV, limit)) {
+     *         break;
+     *     }
+     *     // case 1 - addition with positive stride == addition
+     *     basicIV = basicIV + stride;
+     *     // case 2 - addition with negative stride == subtraction
+     *     basicIV = basicIV + (-stride);
+     *     // case 3 - subtraction with positive stride == subtraction
+     *     basicIV = basicIV - stride;
+     *     // case 4 - subtraction with negative stride == addition
+     *     basicIV = basicIV - (-stride);
+     * }
+     * 
+ * + * While one might assume that these patterns would be transformed into their canonical form by + * {@code CanonicalizerPhase} it is never guaranteed that a full canonicalizer has been run + * before loop detection is done. Thus, we have to handle all patterns here. + * + * Note that while addition is commutative and thus can handle both inputs mirrored, the same is + * not true for subtraction. + * + * For addition the following patterns are all valid IVs + * + *
+     * int basicIV = 0;
+     * while (true) {
+     *     if (compare(basicIV, limit)) {
+     *         break;
+     *     }
+     *     // case 1 - loop invariant input is y input of add
+     *     basicIV = basicIV + stride;
+     *     // case 2 - loop invariant input is x input of add
+     *     basicIV = stride + basicIV;
+     * }
+     * 
+ * + * because addition is commutative. + * + * For subtraction, this is not correct when detecting the basic induction variable. + * + * Here is an example of a regular down-counted loop, with a subtraction basic IV operation: + * + *
+     * int basicIV = init;
+     * while (true) {
+     *     if (basicIV >= 0) {
+     *         break;
+     *     }
+     *     basicIV = basicIV - 2;
+     * }
+     * 
+ * + * As can be seen, the IV above has the values [init, init - 2, init - 4, ...]. + * + * In contrast, here is an example of an invalid induction variable: + * + *
+     * int basicIV = 0;
+     * while (true) {
+     *     if (basicIV <= limit) {
+     *         break;
+     *     }
+     *     basicIV = 2 - basicIV;
+     * }
+     * 
+ * + * because the IV value is [0,2-0=2,2-2=0,2,0,2,0, etc]. This alternating pattern is by + * definition not an induction variable. The reason for this difference is that while addition + * is commutative, subtraction is not. Thus we only allow subtraction of the form + * {@code base - stride/offset}. + * + * Derived induction variables with mirrored inputs however are perfectly fine because the base + * IV is a regular (non-alternating) one. So the loop + * + *
+     * int basicIV = 0;
+     * while (true) {
+     *     if (basicIV >= limit) {
+     *         break;
+     *     }
+     *     int otherIV = 124555 - basicIV;
+     *     basicIV = basicIV + 1;
+     * }
+     * 
+ * + * contains the correct IV {@code otherIV} which is an induction variable as per definition: it + * increases/decreases its value by a fixed amount every iteration (that amount being the stride + * of base IV). + */ + private static ValueNode calcOffsetTo(LoopEx loop, ValueNode opNode, ValueNode base, boolean forDerivedIV) { + if (isNumericInteger(opNode) && (opNode instanceof AddNode || opNode instanceof SubNode)) { + BinaryArithmeticNode arithOp = (BinaryArithmeticNode) opNode; + BinaryOp op = arithOp.getArithmeticOp(); + if (arithOp.getX() == base && loop.isOutsideLoop(arithOp.getY())) { + return arithOp.getY(); + } else if ((op.isCommutative() || forDerivedIV) && arithOp.getY() == base && loop.isOutsideLoop(arithOp.getX())) { + return arithOp.getX(); } } return null; } - private static ValueNode mul(LoopEx loop, ValueNode op, ValueNode base) { + private static boolean isNumericInteger(ValueNode v) { + Stamp s = v.stamp(NodeView.DEFAULT); + return s instanceof IntegerStamp; + } + + /** + * Determine if the given {@code op} represents a {@code DerivedScaledInductionVariable} + * variable with respect to {@code base}. + * + * See {@link LoopEx#calcOffsetTo(LoopEx, ValueNode, ValueNode, boolean)}. Multiplication is + * commutative so the logic of addition applies here. + */ + private static ValueNode calcScaleTo(LoopEx loop, ValueNode op, ValueNode base) { if (op instanceof MulNode) { MulNode mul = (MulNode) op; if (mul.getX() == base && loop.isOutsideLoop(mul.getY())) { diff --git a/compiler/src/org.graalvm.compiler.phases.common/src/org/graalvm/compiler/phases/common/util/LoopUtility.java b/compiler/src/org.graalvm.compiler.phases.common/src/org/graalvm/compiler/phases/common/util/LoopUtility.java index e46ea9dce075..59434d6166c3 100644 --- a/compiler/src/org.graalvm.compiler.phases.common/src/org/graalvm/compiler/phases/common/util/LoopUtility.java +++ b/compiler/src/org.graalvm.compiler.phases.common/src/org/graalvm/compiler/phases/common/util/LoopUtility.java @@ -26,6 +26,8 @@ import java.util.EnumSet; +import org.graalvm.compiler.core.common.type.IntegerStamp; +import org.graalvm.compiler.core.common.type.Stamp; import org.graalvm.compiler.graph.Graph.NodeEvent; import org.graalvm.compiler.graph.Graph.NodeEventScope; import org.graalvm.compiler.nodes.LoopExitNode; @@ -46,6 +48,27 @@ public class LoopUtility { + public static boolean isNumericInteger(ValueNode v) { + Stamp s = v.stamp(NodeView.DEFAULT); + return s instanceof IntegerStamp; + } + + /** + * Determine if the given node has a 64-bit integer stamp. + */ + public static boolean isLong(ValueNode v) { + Stamp s = v.stamp(NodeView.DEFAULT); + return s instanceof IntegerStamp && IntegerStamp.getBits(s) == 64; + } + + /** + * Determine if the given node has a 32-bit integer stamp. + */ + public static boolean isInt(ValueNode v) { + Stamp s = v.stamp(NodeView.DEFAULT); + return s instanceof IntegerStamp && IntegerStamp.getBits(s) == 32; + } + /** * Remove loop proxies that became obsolete over time, i.e., they proxy a value that already * flowed out of a loop and dominates the loop now. diff --git a/compiler/src/org.graalvm.compiler.replacements.test/src/org/graalvm/compiler/replacements/test/EncodeArrayTest.java b/compiler/src/org.graalvm.compiler.replacements.test/src/org/graalvm/compiler/replacements/test/EncodeArrayTest.java index 1f4e7250d3a6..088696366f4f 100644 --- a/compiler/src/org.graalvm.compiler.replacements.test/src/org/graalvm/compiler/replacements/test/EncodeArrayTest.java +++ b/compiler/src/org.graalvm.compiler.replacements.test/src/org/graalvm/compiler/replacements/test/EncodeArrayTest.java @@ -52,7 +52,8 @@ public class EncodeArrayTest extends GraalCompilerTest { "XMM-XMM-XMM-XMM+YMM-YMM-YMM-YMM-ZMM-ZMM-ZMM-ZMM-ZMM-ZMM-ZMM-ZMM-", "XMM-XMM-XMM-XMM-YMM-YMM-YMM-YMM+ZMM-ZMM-ZMM-ZMM-ZMM-ZMM-ZMM-ZMM-", "XMM-XMM-XMM-XMM-YMM-YMM-YMM-YMM-ZMM-ZMM-ZMM-ZMM-ZMM-ZMM-ZMM-ZMM+", - "" + "", + "\u0000" + "0".repeat(15) + "1".repeat(15) + "\u00ffsome-string", }; private static Result executeCompiledMethod(InstalledCode compiledMethod, Object... args) { @@ -105,23 +106,25 @@ public void testStringCodingAscii() throws ClassNotFoundException { // Caller of the tested method should guarantee the indexes are within the range -- // there is no need for boundary-value testing. - for (String input : testData) { - char[] value = input.toCharArray(); - int len = value.length; - byte[] daExpected = new byte[len]; - byte[] daActual = new byte[len]; - int sp = 0; - int dp = 0; - while (sp < value.length) { - Result expected = executeExpected(method, null, value, sp, daExpected, dp, len); - Result actual = executeCompiledMethod(compiledMethod, value, sp, daActual, dp, len); - assertEquals(expected, actual); - assertDeepEquals(daExpected, daActual); - int ret = (int) actual.returnValue; - sp += ret; - dp += ret; - while (sp < value.length && value[sp++] > 0x7f) { - dp++; + for (String inputOrig : testData) { + for (String input : new String[]{inputOrig, "_".repeat(31) + inputOrig, "_".repeat(63) + inputOrig}) { + char[] value = input.toCharArray(); + int len = value.length; + byte[] daExpected = new byte[len]; + byte[] daActual = new byte[len]; + int sp = 0; + int dp = 0; + while (sp < value.length) { + Result expected = executeExpected(method, null, value, sp, daExpected, dp, len - sp); + Result actual = executeCompiledMethod(compiledMethod, value, sp, daActual, dp, len - sp); + assertEquals(expected, actual); + assertDeepEquals(daExpected, daActual); + int ret = (int) actual.returnValue; + sp += ret; + dp += ret; + while (sp < value.length && value[sp++] > 0x7f) { + dp++; + } } } } diff --git a/compiler/src/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/AllocationSnippets.java b/compiler/src/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/AllocationSnippets.java index 805f9178d74c..8e6b9a9125bf 100644 --- a/compiler/src/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/AllocationSnippets.java +++ b/compiler/src/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/AllocationSnippets.java @@ -49,6 +49,7 @@ public abstract class AllocationSnippets implements Snippets { protected Object allocateInstanceImpl(Word hub, UnsignedWord size, + boolean forceSlowPath, FillContent fillContents, boolean emitMemoryBarrier, boolean constantSize, @@ -58,7 +59,7 @@ protected Object allocateInstanceImpl(Word hub, Word top = readTlabTop(tlabInfo); Word end = readTlabEnd(tlabInfo); Word newTop = top.add(size); - if (useTLAB() && probability(FAST_PATH_PROBABILITY, shouldAllocateInTLAB(size, false)) && probability(FAST_PATH_PROBABILITY, newTop.belowOrEqual(end))) { + if (!forceSlowPath && useTLAB() && probability(FAST_PATH_PROBABILITY, shouldAllocateInTLAB(size, false)) && probability(FAST_PATH_PROBABILITY, newTop.belowOrEqual(end))) { writeTlabTop(tlabInfo, newTop); emitPrefetchAllocate(newTop, false); result = formatObject(hub, size, top, fillContents, emitMemoryBarrier, constantSize, profilingData.snippetCounters); @@ -123,7 +124,7 @@ protected UnsignedWord arrayAllocationSize(int length, int arrayBaseOffset, int * We do an unsigned multiplication so that a negative array length will result in an array size * greater than Integer.MAX_VALUE. */ - public static long arrayAllocationSize(int length, int arrayBaseOffset, int log2ElementSize, int alignment) { + public static long arrayAllocationSize(long length, int arrayBaseOffset, int log2ElementSize, int alignment) { long size = ((length & 0xFFFFFFFFL) << log2ElementSize) + arrayBaseOffset + (alignment - 1); long mask = ~(alignment - 1); return size & mask; diff --git a/compiler/src/org.graalvm.compiler.truffle.runtime.hotspot/src/org/graalvm/compiler/truffle/runtime/hotspot/AbstractHotSpotTruffleRuntime.java b/compiler/src/org.graalvm.compiler.truffle.runtime.hotspot/src/org/graalvm/compiler/truffle/runtime/hotspot/AbstractHotSpotTruffleRuntime.java index b5b5ffe08dd7..d2dfc2435aec 100644 --- a/compiler/src/org.graalvm.compiler.truffle.runtime.hotspot/src/org/graalvm/compiler/truffle/runtime/hotspot/AbstractHotSpotTruffleRuntime.java +++ b/compiler/src/org.graalvm.compiler.truffle.runtime.hotspot/src/org/graalvm/compiler/truffle/runtime/hotspot/AbstractHotSpotTruffleRuntime.java @@ -593,7 +593,7 @@ protected int getBaseInstanceSize(Class type) { HotSpotMetaAccessProvider meta = (HotSpotMetaAccessProvider) getMetaAccess(); HotSpotResolvedObjectType resolvedType = (HotSpotResolvedObjectType) meta.lookupJavaType(type); - return resolvedType.instanceSize(); + return Math.abs(resolvedType.instanceSize()); } private static boolean fieldIsNotEligible(Class clazz, ResolvedJavaField f) { diff --git a/compiler/src/org.graalvm.compiler.truffle.runtime/src/org/graalvm/compiler/truffle/runtime/GraalTruffleRuntime.java b/compiler/src/org.graalvm.compiler.truffle.runtime/src/org/graalvm/compiler/truffle/runtime/GraalTruffleRuntime.java index 8ee041fcad09..489ca9c9f53f 100644 --- a/compiler/src/org.graalvm.compiler.truffle.runtime/src/org/graalvm/compiler/truffle/runtime/GraalTruffleRuntime.java +++ b/compiler/src/org.graalvm.compiler.truffle.runtime/src/org/graalvm/compiler/truffle/runtime/GraalTruffleRuntime.java @@ -486,7 +486,12 @@ protected void installDefaultListeners() { TraceASTCompilationListener.install(this); JFRListener.install(this); TruffleSplittingStrategy.installListener(this); - Runtime.getRuntime().addShutdownHook(new Thread(this::shutdown)); + try { + Runtime.getRuntime().addShutdownHook(new Thread(this::shutdown)); + } catch (IllegalStateException e) { + // shutdown already in progress + // catching the exception is the only way to detect this. + } } public final void initializeKnownMethods(MetaAccessProvider metaAccess) { diff --git a/compiler/src/org.graalvm.compiler.truffle.test/src/org/graalvm/compiler/truffle/test/TruffleShutdownHookTest.java b/compiler/src/org.graalvm.compiler.truffle.test/src/org/graalvm/compiler/truffle/test/TruffleShutdownHookTest.java new file mode 100644 index 000000000000..e61fcee16742 --- /dev/null +++ b/compiler/src/org.graalvm.compiler.truffle.test/src/org/graalvm/compiler/truffle/test/TruffleShutdownHookTest.java @@ -0,0 +1,97 @@ +/* + * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.graalvm.compiler.truffle.test; + +import java.io.IOException; + +import org.graalvm.polyglot.Engine; +import org.junit.Test; + +import com.oracle.truffle.api.Truffle; +import com.oracle.truffle.api.frame.FrameInstance; +import com.oracle.truffle.api.frame.FrameInstanceVisitor; +import com.oracle.truffle.api.test.SubprocessTestUtils; + +/** + * Test for GR-47083. + */ +public class TruffleShutdownHookTest { + + @Test + public void testRuntimeInit() throws IOException, InterruptedException { + SubprocessTestUtils.newBuilder(getClass(), TruffleShutdownHookTest::inProcess).failOnNonZeroExit(true).// + postfixVmOption("-XX:+UseJVMCICompiler").// force Graal host compilation + onExit((p) -> { + String out = p.toString(System.lineSeparator()); + if (out.contains("java.lang.IllegalStateException: Shutdown in progress")) { + throw new AssertionError(out); + } + }).run(); + } + + private static void inProcess() { + Runtime.getRuntime().addShutdownHook(new Thread() { + @Override + public void run() { + try { + Truffle.getRuntime().iterateFrames(new FrameInstanceVisitor() { + @Override + public Void visitFrame(FrameInstance frameInstance) { + return null; + } + }); + } catch (Throwable t) { + t.printStackTrace(); + } + } + }); + } + + @Test + public void testEngineInit() throws IOException, InterruptedException { + SubprocessTestUtils.newBuilder(getClass(), TruffleShutdownHookTest::inProcessEngineInit).failOnNonZeroExit(true).// + postfixVmOption("-XX:+UseJVMCICompiler").// force Graal host compilation + onExit((p) -> { + String out = p.toString(System.lineSeparator()); + if (out.contains("java.lang.IllegalStateException: Shutdown in progress")) { + throw new AssertionError(out); + } + }).run(); + } + + private static void inProcessEngineInit() { + Runtime.getRuntime().addShutdownHook(new Thread() { + @Override + public void run() { + try { + Engine.create().close(); + } catch (Throwable t) { + t.printStackTrace(); + } + } + }); + } + +} diff --git a/compiler/src/org.graalvm.compiler.word/src/org/graalvm/compiler/word/WordCastNode.java b/compiler/src/org.graalvm.compiler.word/src/org/graalvm/compiler/word/WordCastNode.java index b4597554262a..d86e4c1b318f 100644 --- a/compiler/src/org.graalvm.compiler.word/src/org/graalvm/compiler/word/WordCastNode.java +++ b/compiler/src/org.graalvm.compiler.word/src/org/graalvm/compiler/word/WordCastNode.java @@ -35,14 +35,14 @@ import org.graalvm.compiler.core.common.type.StampFactory; import org.graalvm.compiler.graph.Node; import org.graalvm.compiler.graph.NodeClass; -import org.graalvm.compiler.nodes.spi.Canonicalizable; -import org.graalvm.compiler.nodes.spi.CanonicalizerTool; import org.graalvm.compiler.lir.ConstantValue; import org.graalvm.compiler.nodeinfo.NodeInfo; import org.graalvm.compiler.nodes.ConstantNode; import org.graalvm.compiler.nodes.FixedWithNextNode; import org.graalvm.compiler.nodes.NodeView; import org.graalvm.compiler.nodes.ValueNode; +import org.graalvm.compiler.nodes.spi.Canonicalizable; +import org.graalvm.compiler.nodes.spi.CanonicalizerTool; import org.graalvm.compiler.nodes.spi.LIRLowerable; import org.graalvm.compiler.nodes.spi.NodeLIRBuilderTool; import org.graalvm.compiler.nodes.type.NarrowOopStamp; @@ -185,21 +185,21 @@ public Node canonical(CanonicalizerTool tool) { @Override public void generate(NodeLIRBuilderTool generator) { Value value = generator.operand(input); - ValueKind kind = generator.getLIRGeneratorTool().getLIRKind(stamp(NodeView.DEFAULT)); - assert kind.getPlatformKind().getSizeInBytes() == value.getPlatformKind().getSizeInBytes(); + ValueKind resultKind = generator.getLIRGeneratorTool().getLIRKind(stamp(NodeView.DEFAULT)); + assert resultKind.getPlatformKind().getSizeInBytes() == value.getPlatformKind().getSizeInBytes(); - if (trackedPointer && LIRKind.isValue(kind) && !LIRKind.isValue(value)) { + if (trackedPointer && LIRKind.isValue(resultKind) && !LIRKind.isValue(value)) { // just change the PlatformKind, but don't drop reference information - kind = value.getValueKind().changeType(kind.getPlatformKind()); + resultKind = value.getValueKind().changeType(resultKind.getPlatformKind()); } - if (kind.equals(value.getValueKind()) && !(value instanceof ConstantValue)) { + if (resultKind.equals(value.getValueKind()) && !(value instanceof ConstantValue)) { generator.setResult(this, value); } else { - AllocatableValue result = generator.getLIRGeneratorTool().newVariable(kind); + AllocatableValue result = generator.getLIRGeneratorTool().newVariable(resultKind); if (stamp.equals(StampFactory.object())) { generator.getLIRGeneratorTool().emitConvertZeroToNull(result, value); - } else if (!trackedPointer && !((AbstractPointerStamp) input.stamp(NodeView.DEFAULT)).nonNull()) { + } else if (!trackedPointer && !((AbstractPointerStamp) input.stamp(NodeView.DEFAULT)).nonNull() && !(input.stamp(NodeView.DEFAULT) instanceof NarrowOopStamp)) { generator.getLIRGeneratorTool().emitConvertNullToZero(result, (AllocatableValue) value); } else { generator.getLIRGeneratorTool().emitMove(result, value); diff --git a/compiler/src/org.graalvm.jniutils/src/org/graalvm/jniutils/JNI.java b/compiler/src/org.graalvm.jniutils/src/org/graalvm/jniutils/JNI.java index c6bd26b0fd8d..e80b4406a9e3 100644 --- a/compiler/src/org.graalvm.jniutils/src/org/graalvm/jniutils/JNI.java +++ b/compiler/src/org.graalvm.jniutils/src/org/graalvm/jniutils/JNI.java @@ -364,6 +364,9 @@ public interface JNINativeInterface extends PointerBase { @CField("NewWeakGlobalRef") NewWeakGlobalRef getNewWeakGlobalRef(); + @CField("NewLocalRef") + NewLocalRef getNewLocalRef(); + @CField("DeleteWeakGlobalRef") DeleteWeakGlobalRef getDeleteWeakGlobalRef(); @@ -821,6 +824,11 @@ public interface NewWeakGlobalRef extends CFunctionPointer { JWeak call(JNIEnv env, JObject lobj); } + public interface NewLocalRef extends CFunctionPointer { + @InvokeCFunctionPointer + JObject call(JNIEnv env, JObject obj); + } + public interface NewObjectA extends CFunctionPointer { @InvokeCFunctionPointer JObject call(JNIEnv env, JClass clazz, JMethodID methodID, JValue args); diff --git a/compiler/src/org.graalvm.jniutils/src/org/graalvm/jniutils/JNIUtil.java b/compiler/src/org.graalvm.jniutils/src/org/graalvm/jniutils/JNIUtil.java index c41c064a05a2..1ebe5c566b3f 100644 --- a/compiler/src/org.graalvm.jniutils/src/org/graalvm/jniutils/JNIUtil.java +++ b/compiler/src/org.graalvm.jniutils/src/org/graalvm/jniutils/JNIUtil.java @@ -489,6 +489,11 @@ public static JWeak NewWeakGlobalRef(JNIEnv env, JObject ref, String type) { return res; } + public static JObject NewLocalRef(JNIEnv env, JObject ref) { + traceJNI("NewLocalRef"); + return env.getFunctions().getNewLocalRef().call(env, ref); + } + public static void DeleteWeakGlobalRef(JNIEnv env, JWeak ref) { traceJNI("DeleteWeakGlobalRef"); if (tracingAt(3)) { diff --git a/docs/enterprise-overview/support.md b/docs/enterprise-overview/support.md index 2cc125bb692e..98a0c33e502b 100644 --- a/docs/enterprise-overview/support.md +++ b/docs/enterprise-overview/support.md @@ -7,14 +7,14 @@ permalink: /support/ ## Available Distributions -Oracle GraalVM distributions are based on Oracle JDK 17 and Oracle JDK 20. -Oracle GraalVM releases include all Oracle Java critical patch updates (CPUs), which are released on a regular schedule to remedy defects and known vulnerabilities. +Oracle GraalVM for JDK 17 is based on Oracle JDK 17. +Each release of Oracle GraalVM for JDK 17 includes all Oracle Java critical patch updates (CPUs), which are provided on a regular schedule to remedy defects and known vulnerabilities. -Oracle GraalVM is available for Linux, macOS, and Windows platforms on x64 systems, for Linux and macOS on AArch64 architecture. +Oracle GraalVM for JDK 17 is available for Linux, macOS, and Windows on the x64 architecture, and for Linux and macOS on the AArch64 architecture. ## Certified Platforms -The following platforms are certified for Oracle GraalVM: +Oracle GraalVM for JDK 17 is certified on the following platforms: | Operating System | Version | Architecture | Installation Guide | |------------------------------------ |-------------- |-------------- |------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | @@ -23,39 +23,32 @@ The following platforms are certified for Oracle GraalVM: | macOS | 11 (Big Sur), 12.4 (Monterey), 13.3 (Ventura) | x64, AArch64 | [Installation Guide for macOS](../getting-started/graalvm-enterprise/macos.md) | | Microsoft Windows | Server 2016, 2019, 2022 | x64 | [Installation Guide for Windows](../getting-started/graalvm-enterprise/windows.md) | -## Features Support +## Experimental Components -Oracle GraalVM features are distributed as _fully supported_ or _experimental_. +Oracle GraalVM for JDK 17 includes some components that are considered experimental. +These components are not meant for use in production and are not supported by Oracle. +Some components are considered experimental on specific platforms. +The GraalVM team welcomes feedback on these components, but users should be aware that the components may not be included in a future release or may change significantly before being considered production-ready: +* Java on Truffle (Espresso) is _supported_ on Linux x64 only and is experimental on other certified platforms. +* The GraalVM Python runtime (GraalPy) is _experimental_ on all certified platforms. -_Experimental_ features are being considered for future versions of Oracle GraalVM. -They are not meant for use in production and are **not** supported by Oracle. -The development team welcomes feedback on experimental features, but users should be aware that experimental features may not be included in a final version, or may change significantly before being considered production-ready. +## Deprecated Components -For more information, check the [GraalVM Free Terms and Conditions including License for Early Adopter Versions](https://www.oracle.com/downloads/licenses/graal-free-license.html). +The following components are deprecated and will be removed in Oracle GraalVM for JDK 23: +* LLVM Runtime +* LLVM Toolchain +* Node.js +## Related Technologies -The following table lists supported and experimental features in Oracle GraalVM by platform. - -| Feature | Linux x64 | Linux AArch64 | macOS x64 | macOS AArch64 | Windows x64 | -|-----------------|---------------|---------------|---------------|---------------|---------------| -| Native Image | supported | supported | supported | supported | supported | -| LLVM runtime | supported | supported | supported | supported | experimental | -| LLVM toolchain | supported | supported | supported | supported | experimental | -| JavaScript | supported | supported | supported | supported | supported | -| Node.js | supported | supported | supported | supported | supported | -| Java on Truffle | supported | experimental | experimental | experimental | experimental | -| Python | experimental | experimental | experimental | experimental | not available | -| Ruby | experimental | experimental | experimental | experimental | not available | -| WebAssembly | experimental | experimental | experimental | experimental | experimental | +Additional open source language runtimes designed for use with Oracle GraalVM for JDK 17 are available on [graalvm.org](https://www.graalvm.org/jdk17/reference-manual/languages/). ## Licensing and Support -Oracle GraalVM is licensed under [GraalVM Free Terms and Conditions (GFTC) including License for Early Adopter Versions](https://www.oracle.com/downloads/licenses/graal-free-license.html). -Subject to the conditions in the license, including the License for Early Adopter Versions, the GFTC is intended to permit use by any user including commercial and production use. Redistribution is permitted as long as it is not for a fee. -Oracle GraalVM is also free to use on Oracle Cloud Infrastructure. -For more information about Oracle GraalVM licensing, see the [Oracle Java SE Licensing FAQ](https://www.oracle.com/java/technologies/javase/jdk-faqs.html#GraalVM-licensing). - -Oracle GraalVM is available as part of the [Oracle Java SE Subscription](https://www.oracle.com/java/java-se-subscription/) which includes 24x7x365 [Oracle premier support](https://www.oracle.com/support/premier/) and access to [My Oracle Support (MOS)](https://www.oracle.com/support/). +Oracle GraalVM is licensed under [GraalVM Free Terms and Conditions (GFTC) including License for Early Adopter Versions](https://www.oracle.com/downloads/licenses/graal-free-license.html). +Subject to the conditions in the license, including the License for Early Adopter Versions, the GFTC is intended to permit use by any user including commercial and production use. +Redistribution is permitted as long as it is not for a fee. +Oracle GraalVM is also free to use on Oracle Cloud Infrastructure. +For more information about Oracle GraalVM licensing, see the [Oracle Java SE Licensing FAQ](https://www.oracle.com/java/technologies/javase/jdk-faqs.html#GraalVM-licensing). -Oracle GraalVM focuses on support for Java LTS releases for production deployments. -See the [release calendar](../../release-notes/enterprise/oracle-graalvm-release-calendar.md) for more information. +Oracle GraalVM is available as part of the [Oracle Java SE Subscription](https://www.oracle.com/java/java-se-subscription/) which includes 24x7x365 [Oracle premier support](https://www.oracle.com/support/premier/) and access to [My Oracle Support (MOS)](https://www.oracle.com/support/). \ No newline at end of file diff --git a/docs/getting-started/graalvm-community/container-images/graalvm-ce-container-images.md b/docs/getting-started/graalvm-community/container-images/graalvm-ce-container-images.md index edda4c6963b0..ad5e782af68a 100644 --- a/docs/getting-started/graalvm-community/container-images/graalvm-ce-container-images.md +++ b/docs/getting-started/graalvm-community/container-images/graalvm-ce-container-images.md @@ -8,54 +8,98 @@ permalink: /docs/getting-started/container-images/ ## GraalVM Community Edition Container Images To support container-based development, GraalVM Community Edition container images are published in the [GitHub Container Registry](https://github.com/orgs/graalvm/packages). -Learn here how to start using GraalVM Community Edition images for Docker containers. -You can pull a package by name or by name and version tag. To install GraalVM JDK from the command line, use: -```shell -docker pull ghcr.io/graalvm/jdk-community:20.0.1-ol9 +## Repositories + +There are different GraalVM Community Edition container images provided depending on the architecture and the Java version. +The container image repositories for the latest GraalVM versions (GraalVM for JDK 17 and GraalVM for JDK 20) have a `-community` suffix. +These are: **native-image-community**, **jdk-community**, **truffleruby-community**, **nodejs-community**, and **graalpy-community**. +The images are multi-arch, for AMD64 and AArch64 processor architectures, with a choice of Oracle Linux versions 7, 8, or 9. + +GraalVM is installed in `/usr/lib64/graalvm/graalvm-java<$FeatureVersion>` where `<$FeatureVersion>` is `17`, `20`, etc. +For instance, GraalVM for JDK 17 is installed in `/usr/lib64/graalvm/graalvm-java17`. +All binaries, including `java`, `javac`, `native-image`, and other binaries are available as global commands via the `alternatives` command. + +See a full list of GraalVM Community Edition container images [here](https://github.com/graalvm/container). + +## Tags + +Each repository provides multiple tags that let you choose the level of stability you need including the Java version, build number, and the Oracle Linux version. +Image tags use the following naming convention: + +```bash +$version[-muslib(for native image only)][-$platform][-$buildnumber] ``` -Alternatively, use GraalVM JDK as a base image in [Dockerfile](https://docs.docker.com/engine/reference/builder/): -```shell -FROM ghcr.io/graalvm/jdk-community:20.0.1-ol9 +The following tags are listed from the most-specific tag (at the top) to the least-specific tag (at the bottom). +The most-specific tag is unique and always points to the same image, while the less-specific tags point to newer image variants over time. + +``` +17.0.8-ol9-20230725 +17.0.8-ol9 +17.0.8 +17-ol9 +17 ``` -There are different GraalVM Community Edition container images provided depending on the architecture and the Java version. -The images are multi-arch (`aarch64` or `amd64` depending on the host architecture), and tagged with the format `ghcr.io/graalvm/$IMAGE_NAME[:][$java_version][-$os_version][-$date]`. -The version tag defines the level of specificity. It is recommended that the most specific tag be used, for example, `20.0.1` or `20.0.1-ol9[-$date]`, where the `-ol9-[-$date]` means the image required a patch and this specific build will never change. +## Pulling Images + +1. To pull the container image for GraalVM JDK for a specific JDK feature version, e.g, _17_, run: + ```bash + docker pull ghcr.io/graalvm/jdk-community:17 + ``` + + Alternatively, to use the container image as the base image in your Dockerfile, use: + ```bash + FROM ghcr.io/graalvm/jdk-community:17 + ``` -See what types of container images are available [here](https://github.com/graalvm/container). + You have pulled a size compact GraalVM Community Edition container image with the GraalVM JDK and the Graal compiler pre-installed. -## Get Started +2. To pull the container image with the `native-image` utility for a specific JDK feature version, e.g, _17_, run: + ```bash + docker pull ghcr.io/graalvm/native-image-community:17 + ``` -1. Start a container and enter the `bash` session with the following run command: - ```shell - docker run -it --rm ghcr.io/graalvm/jdk-community:20.0.1-ol9 bash + Alternatively, to pull the container image with the `native-image` utility with the `musl libc` toolchain to create fully statically linked executables, use: + ```bash + docker pull ghcr.io/graalvm/native-image-community:17-muslib ``` -2. Check the `java` version: - ```shell - →docker run -it --rm ghcr.io/graalvm/jdk-community:20.0.1-ol9 bash - bash-4.4# java -version + + Alternatively, to use the container image as the base image in your Dockerfile, use: + ```bash + FROM ghcr.io/graalvm/native-image-community:17-muslib ``` -You have pulled a size compact GraalVM Community Edition container image with the GraalVM JDK pre-installed and the Graal compiler. +3. To verify, start the container and enter the Bash session: + ```bash + docker run -it --rm ghcr.io/graalvm/native-image-community:17 bash + ``` -RPM-based GraalVM Community container images are based on GraalVM components RPMs that are available for Oracle Linux 7, Oracle Linux 8, and Oracle Linux 9. -Similar to any other available packages, you can install these components using `yum` on Oracle Linux 7 or `microdnf` on the Oracle Linux + To check the version of GraalVM and its installed location, run the `env` command from the Bash prompt: + ```bash + env + ``` -To pull a GraalVM Community Edition container image containing the [`gu` utility](../../../reference-manual/graalvm-updater.md) for installing additional components, run this command: -``` -docker pull ghcr.io/graalvm/graalvm-community:20.0.1-ol9 -``` + The output shows the environment variable `JAVA_HOME` pointing to the installed GraalVM version and location. -Here is a sample command that maps the `/absolute/path/to/directory/no/trailing/slash` directory from the host system to the `/path/inside/container` inside the container. + To check the Java version, run: + ```bash + java -version + ``` + + To check the `native-image` version, run: + ```bash + native-image --version + ``` -```shell -docker run -it --rm -v /absolute/path/to/directory/no/trailing/slash:/path/inside/container ghcr.io/graalvm/graalvm-community:20.0.1-ol9 bash -``` +4. Calling `docker pull` without specifying a processor architecture pulls container images for the processor architecture that matches your Docker client. To pull container images for a different platform architecture, specify the desired platform architecture with the `--platform` option and either `linux/amd64` or `linux/aarch64` as follows: + ```bash + docker pull --platform linux/aarch64 ghcr.io/graalvm/native-image-community:17 + ``` -Using `ghcr.io/graalvm/native-image-community` you will always get the latest update available for GraalVM Community Native Image, the latest OS which is for now Oracle Linux 9 and Oracle Linux 9 slim, and the latest Java version. +If you are looking for Oracle GraalVM container images, they are published in the [Oracle Container Registry](https://container-registry.oracle.com). -Check what other configuration types of container images are available [here](https://github.com/orgs/graalvm/packages). +### Learn More -If you are looking for Oracle GraalVM container images, they are published in the [Oracle Container Registry](https://container-registry.oracle.com/ords/f?p=113:10::::::). \ No newline at end of file +- [GraalVM Native Image, Spring and Containerisation](https://luna.oracle.com/lab/fdfd090d-e52c-4481-a8de-dccecdca7d68): Learn how GraalVM Native Image can generate native executables ideal for containerization. diff --git a/docs/getting-started/graalvm-enterprise/container-images/graalvm-ee-container-images.md b/docs/getting-started/graalvm-enterprise/container-images/graalvm-ee-container-images.md index 3adab953b54c..50c510385678 100644 --- a/docs/getting-started/graalvm-enterprise/container-images/graalvm-ee-container-images.md +++ b/docs/getting-started/graalvm-enterprise/container-images/graalvm-ee-container-images.md @@ -1,132 +1,120 @@ --- layout: docs toc_group: container-images -link_title: Get Started with Oracle GraalVM Container Images +link_title: Oracle GraalVM Container Images permalink: /getting-started/container-images/ --- -## Get Started with Oracle GraalVM Container Images +## Oracle GraalVM Container Images -Oracle GraalVM container images are published in the [Oracle Container Registry](https://container-registry.oracle.com). +Oracle GraalVM container images are available in [Oracle Container Registry (OCR)](https://container-registry.oracle.com) under the [GraalVM Free Terms and Conditions (GFTC) license](https://www.oracle.com/downloads/licenses/graal-free-license.html). -The following images are available: +## Repositories -| Image Name | Description -------------------------------------------------------------------------------------------------------------------------------------------------------------|-----------| -| **jdk-ee** | A compact image containing the Oracle GraalVM JDK | -| **native-image-ee** | A compact image containing the Oracle GraalVM `native-image` utility and JDK | -| **enterprise** | Provides the Oracle GraalVM JDK along with the `gu` (Graal Updater) utility to enable installation of additional features | -| **nodejs-ee** | Includes the Node.js runtime and the Oracle GraalVM JDK | +Oracle GraalVM container images are published in two OCR repositories: **jdk** and **native-image**. -## Images Tagging Structure and Availability +| Repository | Description | +|------------------|-------------| +| **jdk** | Provides container images with Oracle GraalVM JDK which can be used to both compile and deploy Java applications. Image tags let you select the Java version and Oracle Linux version. | +| **native-image** | Provides Oracle GraalVM container images with the `native-image` utility along with all tools required to compile applications into native Linux executables. These images are commonly used in multi-stage builds to compile applications into executables that are then packaged in a lightweight container image. Image tags let you select the Java version and Oracle Linux version as well as variants that include the musl toolchain for the creation of fully statically linked executables. | -Images are multi-arch (`x64`, `aarch64` for Java 11 or later, depending on container host architecture), and tagged with the format: +Both repositories provide container images for AMD64 and AArch64 processor architectures, with a choice of Oracle Linux versions 7, 8, or 9. -``` -container-registry.oracle.com/graalvm/{IMAGE_NAME}:{IMAGE_TAG} -``` +Oracle GraalVM is installed in `/usr/lib64/graalvm/graalvm-java<$FeatureVersion>` where `<$FeatureVersion>` is `17`, `20`, etc. +For instance, Oracle GraalVM for JDK 17 is installed in `/usr/lib64/graalvm/graalvm-java17`. +All binaries, including `java`, `javac`, `native-image`, and other binaries are available as global commands via the `alternatives` command. -The structure of {IMAGE_TAG} is: +## Tags -``` -{OS_VERSION}-java{JAVA_VERSION}-{MAJOR_RELEASE}.{MINOR_RELEASE}.{PATCH_RELEASE}-b{BUILD_NUMBER} +Each repository provides multiple tags that let you choose the level of stability you need including the Java version, build number, and the Oracle Linux version. +Oracle GraalVM image tags use the following naming convention: + +```bash +$version[-muslib(for native image only)][-$platform][-$buildnumber] ``` -The structure is designed to allow references with different levels of specificity. -The minimum valid image tag is `java{JAVA_VERSION}-{MAJOR_RELEASE}`. For example, the following are all valid image tags: +The following tags are listed from the most-specific tag (at the top) to the least-specific tag (at the bottom). +The most-specific tag is unique and always points to the same image, while the less-specific tags point to newer image variants over time. ``` -java17-22 -java17-22.3 -java17-22.3.0 -java17-22.3.0-b1 -ol8-java17-22.3.1-b2 +17.0.8-ol9-20230904 +17.0.8-ol9 +17.0.8 +17-ol9 +17 ``` -It is recommended you use the most specific tag, for example, `ol8-java17-22.3.1` or `ol8-java17-22.3.1-b1`, where `-b1` indicates that the image required a patch and this specific build will never change. - -Image tags that are not fully specified, for example, `java17-22`, are not stable and will change over time to refer to the latest available Oracle GraalVM 22.x release. Using `latest` (or no tag) will always get the latest release available for a given image, the latest OS, the latest Java version, and the latest GraalVM version. - -## Get Started - -To pull an Oracle GraalVM image from the Oracle Container Registry, you are required to accept the license agreement. +## Pulling Images -1. Go to [Oracle Container Registry](https://container-registry.oracle.com/) and click the “GraalVM” tile. You are redirected to a page describing the GraalVM repositories. +1. To pull the container image for Oracle GraalVM JDK for a specific JDK feature version, e.g., _17_, run: -2. Click on an image repository. For example, if you need a compact container image with the JDK, click the **jdk-ee** link. - -3. Click **Sign In**. This takes you to the Oracle Single Sign-on page. - -4. Sign in with an Oracle account. If you do not have an existing Oracle account, you can create one. - -5. Once you have signed in you are presented with a license to review. Click **Continue** and then click **Accept** to accept the license and proceed. - -6. Check and accept the license. - -7. Open a terminal window and login to Oracle Container Registry using your Oracle account, as follows: - - ```shell - docker login container-registry.oracle.com - Username: - Password: - Login successful. + ```bash + docker pull container-registry.oracle.com/graalvm/jdk:17 ``` - -8. Pull the image with the `docker pull` command. Use the latest tag or a specific tag from the list of tags displayed on the page. For example: - - ```shell - docker pull container-registry.oracle.com/graalvm/jdk-ee:latest - latest: Pulling from graalvm/jdk-ee - 58c4eaffce77: Pull complete - 8800a93aa49d: Pull complete - da2734fc865b: Pull complete - Digest: sha256:ccde822a1119da5f95e97b331632e6219b0ae29f81f516d1c0b9787 - Status: Downloaded newer image for container-registry.oracle.com/graalvm/jdk-ee:latest - container-registry.oracle.com/graalvm/jdk-ee:latest + + Alternatively, to use the container image as the base image in your Dockerfile, use: + + ```bash + FROM container-registry.oracle.com/graalvm/jdk:17 ``` -9. Start a container from the `jdk-ee` image and enter the bash session with the following `run` command: - - ```shell - docker run -it --rm container-registry.oracle.com/graalvm/jdk-ee:latest bash +2. To pull the container image for Oracle GraalVM `native-image` utility for a specific JDK feature version, e.g., _17_, run: + + ```bash + docker pull container-registry.oracle.com/graalvm/native-image:17 ``` -10. Check the version of Oracle GraalVM and its storage location by running the `env` command: - - ```shell - bash-5.1# env + Alternatively, to pull the container image for Oracle GraalVM `native-image` utility with the `musl libc` toolchain to create fully statically linked executables, run: + + ```bash + docker pull container-registry.oracle.com/graalvm/native-image:17-muslib ``` - The output message includes the value of `JAVA_HOME` showing the version of installed Oracle GraalVM and the location where it is installed. - -11. Check the contents of the Oracle GraalVM _bin_ directory: - - ```shell - bash-5.1# ls /usr/lib64/graalvm/graalvm22-ee-java17/bin + + Alternatively, to use the container image as the base image in your Dockerfile, use: + + ```bash + FROM container-registry.oracle.com/graalvm/native-image:17-muslib ``` + +3. To verify, start the container and enter the Bash session: -12. Check the Java version by running the following command: - - ```shell - bash-5.1# java -version + ```bash + docker run -it --rm --entrypoint /bin/bash container-registry.oracle.com/graalvm/native-image:17 ``` - The output printed contains the information about the runtime environment and its version number. - -If you pull a `native-image-ee` image, you can start a container and enter the session from the `native-image-ee` image immediately: - -```shell -docker run -it --rm container-registry.oracle.com/graalvm/native-image-ee:latest bash -``` -Follow steps 10-12 above to check the installed version and the location of Oracle GraalVM. The _bin_ directory, in this case, includes `java` and `native-image` launchers. Check the versions: + To check the version of Oracle GraalVM and its installed location, run the `env` command from the Bash prompt: -```shell -native-image --version -``` + ```bash + env + ``` + + The output shows the environment variable `JAVA_HOME` pointing to the installed Oracle GraalVM version and location. -```shell -java -version -``` + To check the Java version, run the following command from the Bash prompt: + + ```bash + java -version + ``` + + The output shows the installed Oracle GraalVM Java runtime environment and version information. + + To check the `native-image` version, run the following command from the Bash prompt: + + ```bash + native-image --version + ``` + + The output shows the installed Oracle GraalVM `native-image` utility version information. + +4. Calling `docker pull` without specifying a processor architecture pulls container images for the processor architecture that matches your Docker client. +To pull container images for a different platform architecture, specify the desired platform architecture with the `--platform` option and either `linux/amd64` or `linux/aarch64` as follows: + + ```bash + docker pull --platform linux/aarch64 container-registry.oracle.com/graalvm/native-image:17 + ``` ### Learn More -- [GraalVM Native Image, Spring and Containerisation](https://luna.oracle.com/lab/fdfd090d-e52c-4481-a8de-dccecdca7d68): learn how GraalVM Native Image can generate native executables ideal for containerization. \ No newline at end of file +- [GraalVM Native Image, Spring and Containerisation](https://luna.oracle.com/lab/fdfd090d-e52c-4481-a8de-dccecdca7d68): Learn how GraalVM Native Image can generate native executables ideal for containerization. +- [Announcement Blog: New Oracle GraalVM Container Images](https://blogs.oracle.com/java/post/new-oracle-graalvm-container-images) + diff --git a/docs/reference-manual/native-image/Compatibility.md b/docs/reference-manual/native-image/Compatibility.md index 23f11e1e74d1..5584002170e8 100644 --- a/docs/reference-manual/native-image/Compatibility.md +++ b/docs/reference-manual/native-image/Compatibility.md @@ -3,7 +3,9 @@ layout: docs toc_group: metadata link_title: Compatibility Guide permalink: /reference-manual/native-image/metadata/Compatibility/ -redirect_from: /reference-manual/native-image/Limitations/ +redirect_from: +- /reference-manual/native-image/Limitations/ +- /reference-manual/native-image/ARM64/ --- # Native Image Compatibility Guide diff --git a/docs/reference-manual/native-image/JCASecurityServices.md b/docs/reference-manual/native-image/JCASecurityServices.md index 5d3357bf70fe..97ad6428e423 100644 --- a/docs/reference-manual/native-image/JCASecurityServices.md +++ b/docs/reference-manual/native-image/JCASecurityServices.md @@ -3,7 +3,9 @@ layout: docs toc_group: dynamic-features link_title: JCA Security Services permalink: /reference-manual/native-image/dynamic-features/JCASecurityServices/ -redirect_from: /reference-manual/native-image/featuresJCASecurityServices/ +redirect_from: +- /reference-manual/native-image/features/JCASecurityServices/ +- /reference-manual/native-image/JCASecurityServices/ --- # JCA Security Services in Native Image diff --git a/docs/reference-manual/native-image/contribute/CodeStyle.md b/docs/reference-manual/native-image/contribute/CodeStyle.md index e1187787d7dc..30669250735a 100644 --- a/docs/reference-manual/native-image/contribute/CodeStyle.md +++ b/docs/reference-manual/native-image/contribute/CodeStyle.md @@ -56,13 +56,9 @@ This is resolved by running `mx ideinit` and cleaning the affected projects. IDE plugins can be helpful in adhering to style rules. Some examples are: -* Eclipse Checkstyle Plugin: reports Checkstyle violations in Eclipse, making it unnecessary to run `mx checkstyle` manually. - https://checkstyle.github.io/eclipse-cs/ -* IntelliJ Eclipse Code Formatter: formats source files in IntelliJ according to Eclipse IntelliJ rules. -This plugin is automatically configured by `mx ideinit`. - https://github.com/krasa/EclipseCodeFormatter -* IntelliJ Save Actions to automatically format files before saving them. - https://github.com/dubreuia/intellij-plugin-save-actions +* [Eclipse Checkstyle Plugin](https://checkstyle.github.io/eclipse-cs/): reports Checkstyle violations in Eclipse, making it unnecessary to run `mx checkstyle` manually. +* [IntelliJ Eclipse Code Formatter](https://github.com/krasa/EclipseCodeFormatter): formats source files in IntelliJ according to Eclipse IntelliJ rules. This plugin is automatically configured by `mx ideinit`. +* [IntelliJ Save Actions](https://github.com/dubreuia/intellij-plugin-save-actions) to automatically format files before saving them. See the [documentation on IDE integration](https://github.com/graalvm/mx/blob/master/docs/IDE.md) for further suggestions. diff --git a/docs/reference-manual/native-image/guides/access-environment-variables.md b/docs/reference-manual/native-image/guides/access-environment-variables.md index b6104a7d2232..ae2256a06769 100644 --- a/docs/reference-manual/native-image/guides/access-environment-variables.md +++ b/docs/reference-manual/native-image/guides/access-environment-variables.md @@ -3,6 +3,7 @@ layout: ni-docs toc_group: how-to-guides link_title: Access Environment Variables permalink: /reference-manual/native-image/guides/access-environment-variables/ +redirect_from: /reference-manual/native-image/Properties/ --- # Access Environment Variables in a Native Executable at Run Time diff --git a/docs/reference-manual/native-image/guides/add-logging-to-native-executable.md b/docs/reference-manual/native-image/guides/add-logging-to-native-executable.md index a045c12cd5ed..e69965c18971 100644 --- a/docs/reference-manual/native-image/guides/add-logging-to-native-executable.md +++ b/docs/reference-manual/native-image/guides/add-logging-to-native-executable.md @@ -3,6 +3,7 @@ layout: ni-docs toc_group: how-to-guides link_title: Add Logging to a Native Executable permalink: /reference-manual/native-image/guides/add-logging-to-native-executable/ +redirect_from: /reference-manual/native-image/Logging/ --- # Add Logging to a Native Executable diff --git a/docs/reference-manual/native-image/guides/build-static-and-mostly-static-executable.md b/docs/reference-manual/native-image/guides/build-static-and-mostly-static-executable.md index 1bef6c569906..805baee2020e 100644 --- a/docs/reference-manual/native-image/guides/build-static-and-mostly-static-executable.md +++ b/docs/reference-manual/native-image/guides/build-static-and-mostly-static-executable.md @@ -3,6 +3,7 @@ layout: ni-docs toc_group: how-to-guides link_title: Build a Statically Linked or Mostly-Statically Linked Native Executable permalink: /reference-manual/native-image/guides/build-static-executables/ +redirect_from: /reference-manual/native-image/StaticImages/ --- # Build a Statically Linked or Mostly-Statically Linked Native Executable diff --git a/docs/reference-manual/native-image/guides/containerise-native-executable-with-docker.md b/docs/reference-manual/native-image/guides/containerise-native-executable-with-docker.md index 7d1b16a8e2a4..0c87c7a89653 100644 --- a/docs/reference-manual/native-image/guides/containerise-native-executable-with-docker.md +++ b/docs/reference-manual/native-image/guides/containerise-native-executable-with-docker.md @@ -7,114 +7,128 @@ permalink: /reference-manual/native-image/guides/containerise-native-executable- # Containerise a Native Executable and Run in a Docker Container -Docker containers provide the flexibility of development environments to match a production environment, to help isolate your application, and to minimize overhead. For self-contained executables, generated with GraalVM Native Image, containers are an obvious deployment scenario. +Docker containers provide the flexibility of development environments to match a production environment, to help isolate your application, and to minimize overhead. +For self-contained executables, generated with GraalVM Native Image, containers are an obvious deployment choice. To support container-based development, there are several GraalVM container images available, depending on the platform, the architecture, the Java version, and the edition: -- Oracle GraalVM container images can be found in the [Oracle Container Registry](https://container-registry.oracle.com/ords/f?p=113:10::::::) -- GraalVM Community Edition container images can be found in the [GitHub Container Registry](https://github.com/orgs/graalvm/packages) +- Oracle GraalVM container images, available in [Oracle Container Registry (OCR)](https://container-registry.oracle.com) under the [GraalVM Free Terms and Conditions (GFTC) license](https://www.oracle.com/downloads/licenses/graal-free-license.html). +- GraalVM Community Edition container images published in the [GitHub Container Registry](https://github.com/orgs/graalvm/packages). -This guide shows how to containerise a Java application with Docker on macOS. -You will use `ghcr.io/graalvm/jdk:ol8-java17` which is a size compact GraalVM Community container image with the GraalVM JDK pre-installed. -The Dockerfile will be provided. +This guide shows how to containerise a native executable for your Java application. +You will use a GraalVM container image with Native Image to compile a Java application ahead-of-time into a native executable. -### Prerequisites +### Sample Application -- Docker-API compatible container runtime like [Rancher Desktop](https://docs.rancherdesktop.io/getting-started/installation/) or [Docker](https://www.docker.io/gettingstarted/) installed to run MySQL and to run tests using [Testcontainers](https://www.testcontainers.org). +This guide uses the [Spring Boot 3 Native Image Microservice example](https://github.com/graalvm/graalvm-demos/blob/master/spring-native-image/README.md). +The example is a minimal REST-based API application, built on top of Spring Boot 3. +If you call the HTTP endpoint `/jibber`, it will return some nonsense verse generated in the style of the Jabberwocky poem, by Lewis Carroll. -## Note on a Sample Application +## Prerequisites -For the demo you will use the [Spring Boot 3 Native Image Microservice example](https://github.com/graalvm/graalvm-demos/blob/master/spring-native-image/README.md). +1. Download and install the latest Oracle GraalVM from [Downloads](https://www.graalvm.org/downloads/). +The easiest option is to use [SDKMAN!](https://sdkman.io). Run the following command to install Oracle GraalVM for JDK 17: -1. Make sure you have installed a GraalVM JDK. -The easiest way to get started is with [SDKMAN!](https://sdkman.io/jdks#graal). -For other installation options, visit the [Downloads section](https://www.graalvm.org/downloads/). + ```bash + sdk install java 17.0.8-graal + ``` + +2. Install and run a Docker-API compatible container runtime such as [Rancher Desktop](https://docs.rancherdesktop.io/getting-started/installation/), [Docker](https://www.docker.io/gettingstarted/), or [Podman](https://podman.io/docs/installation). -2. Clone the [GraalVM Demos repository](https://github.com/graalvm/graalvm-demos) and enter the application directory: +3. Clone the GraalVM Demos repository: ```shell git clone https://github.com/graalvm/graalvm-demos ``` + +4. Change directory to the demo directory: + ```shell cd spring-native-image ``` -3. Build a native executable and run the application: +## Build and Run as a Native Executable + +With the built-in support for GraalVM Native Image in Spring Boot 3, it has become much easier to compile a Spring Boot 3 application into a native executable. + +1. Build a native executable: ```shell - mvn -Pnative native:compile + ./mvnw native:compile -Pnative ``` - The `-Pnative` profile is used to turn on building a native executable with Maven. - - This will create a binary executable `target/benchmark-jibber`. Start it to see the application running: + + The `-Pnative` profile is used to generate a native executable for your platform. + This will generate a native executable called _benchmark-jibber_ in the _target_ directory. + +2. Run the native executable and put it into the background by appending `&`: ```shell ./target/benchmark-jibber & + ``` + +3. Call the endpoint using `curl`: + + ```shell curl http://localhost:8080/jibber - fg ``` -Now that you have a native executable version of the sample application (`target/jibber`) and seen it working, you can proceed to the next steps. + You should get a random nonsense verse. + +4. Bring the application to the foreground using `fg`, and then enter `` to terminate the application. + +## Containerise the Native Executable -## Containerise a Native Executable +The generated native executable is platform-dependent. -The output of a native executable is platform-dependent. -If you use a Mac or Windows, to build a Docker image containing your native executable, you build a native executable **within** a Docker container - so you need a container with a JDK distribution. -If you are a Linux user, you can just pass a native executable to Docker and use the simplest slim or distroless container, depending on static libraries your application is linked against. -For example: +1. Containerise the native executable using the following command: -``` -FROM gcr.io/distroless/base -ARG APP_FILE -EXPOSE 8080 -COPY target/${APP_FILE} app -ENTRYPOINT ["/jibber"] -``` + - On Linux, containerise the native executable generated in the previous step using the following command: + + ```shell + docker build -f Dockerfiles/Dockerfile.native --build-arg APP_FILE=benchmark-jibber -t jibber-benchmark:native.0.0.1-SNAPSHOT . + ``` -For user's convenience, Dockerfiles are provided with the sample application. + - On MacOS, Windows, or Linux, use multistage Docker builds to build a native executable inside a container, and package the native executable in a lightweight container image: + + ```shell + docker build -f Dockerfiles/Dockerfile -t jibber-benchmark:native.0.0.1-SNAPSHOT . + ``` + +2. Run the application: -1. From application root folder, run this command to create a native executable within a container and then build a Docker image containing that native executable: ```shell - docker build -f Dockerfiles/Dockerfile \ - --build-arg APP_FILE=./target/jibber \ - -t localhost/jibber:native.01 . + docker run --rm --name native -p 8080:8080 jibber-benchmark:native.0.0.1-SNAPSHOT ``` - It will take several minutes to set up Maven in the container and do rest of the job. -2. Query Docker to look at your newly built image: +3. From a new terminal window, call the endpoint using `curl`: + ```shell - docker images | head -n2 + curl http://localhost:8080/jibber ``` - You should see a new image listed. -3. Run the image as follows: + It should generate a random nonsense verse. + +4. To stop the application, first get the container id using `docker ps`, and then run: + ```shell - docker run --rm --name native -d -p 8080:8080 localhost/jibber:native.01 + docker rm -f ``` - -4. Then call the endpoint using the `curl` command in the same console window: + +5. To delete the container images, first get the image id using `docker images`, and then run: + ```shell - curl http://localhost:8080/jibber + docker rmi -f ``` - You should receive a nonsense verse in the style of the poem Jabberwocky. - - -You can take a look at how long the application took to startup by looking at the logs: - -```shell -docker logs -``` -You can also query Docker to get the size of the produced container: -``` -docker images localhost/jibber:native.01 -``` -The difference will be more visible if you build a Docker image of the same Spring Boot application containing a JAR file instead of a native executable, and compare images startup times and file sizes. - -On Linux, you can shrink your container size even more. -With GraalVM Native Image you have the ability to build a statically linked native executable by packaging the native executable directly into an empty Docker image, also known as a scratch container. Continue to [Build a Static or Mostly-Static Native Executable guide](build-static-and-mostly-static-executable.md) to learn more. + +## Summary + +In this guide, you saw how to use GraalVM container images to containerize a native executable for your Java application. + +With GraalVM Native Image you can build a statically linked native executable by packaging the native executable directly into tiny containers such as scratch or distroless images. +Continue to [Build a Static or Mostly-Static Native Executable guide](build-static-and-mostly-static-executable.md) to learn more. ### Related Documentation -* [GraalVM Native Image, Spring and Containerisation](https://luna.oracle.com/lab/fdfd090d-e52c-4481-a8de-dccecdca7d68) -* [GraalVM Community Images](https://github.com/graalvm/container/) -* [Build a Static or Mostly-Static Native Executable](build-static-and-mostly-static-executable.md) \ No newline at end of file +* [Build a Static or Mostly-Static Native Executable](build-static-and-mostly-static-executable.md) +*
Oracle GraalVM Container Images +* Hands-on Lab: GraalVM Native Image, Spring and Containerisation diff --git a/docs/reference-manual/native-image/guides/create-heap-dump-from-native-executable.md b/docs/reference-manual/native-image/guides/create-heap-dump-from-native-executable.md index 97be87ecafbc..3d24fc5339a8 100644 --- a/docs/reference-manual/native-image/guides/create-heap-dump-from-native-executable.md +++ b/docs/reference-manual/native-image/guides/create-heap-dump-from-native-executable.md @@ -3,6 +3,7 @@ layout: ni-docs toc_group: how-to-guides link_title: Create a Heap Dump permalink: /reference-manual/native-image/guides/create-heap-dump/ +redirect_from: /reference-manual/native-image/NativeImageHeapdump/ --- # Create a Heap Dump from a Native Executable diff --git a/docs/reference-manual/native-image/guides/optimize-native-executable-with-pgo.md b/docs/reference-manual/native-image/guides/optimize-native-executable-with-pgo.md index 2383dce1da5e..506930c7885f 100644 --- a/docs/reference-manual/native-image/guides/optimize-native-executable-with-pgo.md +++ b/docs/reference-manual/native-image/guides/optimize-native-executable-with-pgo.md @@ -3,6 +3,7 @@ layout: ni-docs toc_group: how-to-guides link_title: Optimize a Native Executable with PGO permalink: /reference-manual/native-image/guides/optimize-native-executable-with-pgo/ +redirect_from: /reference-manual/native-image/PGO/ --- # Optimize a Native Executable with Profile-Guided Optimizations diff --git a/docs/reference-manual/native-image/guides/use-system-properties.md b/docs/reference-manual/native-image/guides/use-system-properties.md index 1bbc528649f0..e7f1c667c832 100644 --- a/docs/reference-manual/native-image/guides/use-system-properties.md +++ b/docs/reference-manual/native-image/guides/use-system-properties.md @@ -3,6 +3,7 @@ layout: ni-docs toc_group: how-to-guides link_title: Use System Properties permalink: /reference-manual/native-image/guides/use-system-properties/ +redirect_from: /reference-manual/native-image/Properties/ --- # Use System Properties in a Native Executable diff --git a/espresso/mx.espresso/suite.py b/espresso/mx.espresso/suite.py index bb3834fdc4bc..e8b7eec8d74f 100644 --- a/espresso/mx.espresso/suite.py +++ b/espresso/mx.espresso/suite.py @@ -23,8 +23,8 @@ suite = { "mxversion": "6.17.0", "name": "espresso", - "version" : "23.0.2.0", - "release" : True, + "version" : "23.0.2.1", + "release" : False, "groupId" : "org.graalvm.espresso", "url" : "https://www.graalvm.org/reference-manual/java-on-truffle/", "developer" : { diff --git a/regex/mx.regex/suite.py b/regex/mx.regex/suite.py index 5769bf0d612d..3abb0ef28ea8 100644 --- a/regex/mx.regex/suite.py +++ b/regex/mx.regex/suite.py @@ -43,8 +43,8 @@ "name" : "regex", - "version" : "23.0.2.0", - "release" : True, + "version" : "23.0.2.1", + "release" : False, "groupId" : "org.graalvm.regex", "url" : "http://www.graalvm.org/", "developer" : { diff --git a/sdk/mx.sdk/mx_sdk_vm_impl.py b/sdk/mx.sdk/mx_sdk_vm_impl.py index a2f42b52eba7..763b45df424c 100644 --- a/sdk/mx.sdk/mx_sdk_vm_impl.py +++ b/sdk/mx.sdk/mx_sdk_vm_impl.py @@ -2957,7 +2957,7 @@ def cflags(self): # launcher classpath for launching via jvm _cp = NativePropertiesBuildTask.get_launcher_classpath(_dist, _graalvm_home, self.language_library_config, self.component, exclude_implicit=True) _cp = [join(_dist.path_substitutions.substitute(''), x) for x in _cp] - # path from langauge launcher to jars + # path from language launcher to jars _cp = [relpath(x, start=_exe_dir) for x in _cp] if mx.is_windows(): _cp = [x.replace('\\', '\\\\') for x in _cp] diff --git a/sdk/mx.sdk/suite.py b/sdk/mx.sdk/suite.py index 1b4dd54daead..e762d763825f 100644 --- a/sdk/mx.sdk/suite.py +++ b/sdk/mx.sdk/suite.py @@ -41,8 +41,8 @@ suite = { "mxversion": "6.17.0", "name" : "sdk", - "version" : "23.0.2.0", - "release" : True, + "version" : "23.0.2.1", + "release" : False, "sourceinprojectwhitelist" : [], "url" : "https://github.com/oracle/graal", "groupId" : "org.graalvm.sdk", diff --git a/sdk/src/org.graalvm.polyglot/src/org/graalvm/polyglot/Engine.java b/sdk/src/org.graalvm.polyglot/src/org/graalvm/polyglot/Engine.java index e410e207afed..91a033dc1379 100644 --- a/sdk/src/org.graalvm.polyglot/src/org/graalvm/polyglot/Engine.java +++ b/sdk/src/org.graalvm.polyglot/src/org/graalvm/polyglot/Engine.java @@ -796,7 +796,12 @@ public Engine newEngine(AbstractEngineDispatch dispatch, Object receiver, boolea synchronized (ENGINES) { if (!shutdownHookInitialized) { shutdownHookInitialized = true; - Runtime.getRuntime().addShutdownHook(new Thread(new EngineShutDownHook())); + try { + Runtime.getRuntime().addShutdownHook(new Thread(new EngineShutDownHook())); + } catch (IllegalStateException e) { + // shutdown already in progress + // catching the exception is the only way to detect this. + } } } } diff --git a/substratevm/mx.substratevm/suite.py b/substratevm/mx.substratevm/suite.py index 5ad65404e5a1..b743c923e9d5 100644 --- a/substratevm/mx.substratevm/suite.py +++ b/substratevm/mx.substratevm/suite.py @@ -2,8 +2,8 @@ suite = { "mxversion": "6.17.0", "name": "substratevm", - "version" : "23.0.2.0", - "release" : True, + "version" : "23.0.2.1", + "release" : False, "url" : "https://github.com/oracle/graal/tree/master/substratevm", "groupId" : "org.graalvm.nativeimage", diff --git a/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/AbstractCollectionPolicy.java b/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/AbstractCollectionPolicy.java index bee7b2871e1b..191ae8bdf87b 100644 --- a/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/AbstractCollectionPolicy.java +++ b/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/AbstractCollectionPolicy.java @@ -144,8 +144,6 @@ protected void guaranteeSizeParametersInitialized() { @Override public void updateSizeParameters() { - PhysicalMemory.tryInitialize(); - SizeParameters params = computeSizeParameters(sizes); SizeParameters previous = sizes; if (previous != null && params.equal(previous)) { diff --git a/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/BasicCollectionPolicies.java b/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/BasicCollectionPolicies.java index ae89d3740679..c568c7b1be65 100644 --- a/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/BasicCollectionPolicies.java +++ b/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/BasicCollectionPolicies.java @@ -78,12 +78,6 @@ public void ensureSizeParametersInitialized() { @Override public void updateSizeParameters() { - // Sample the physical memory size, before the first GC but after some allocation. - UnsignedWord allocationBeforeUpdate = WordFactory.unsigned(SerialAndEpsilonGCOptions.AllocationBeforePhysicalMemorySize.getValue()); - if (GCImpl.getGCImpl().getCollectionEpoch().equal(WordFactory.zero()) && - HeapImpl.getHeapImpl().getAccounting().getYoungUsedBytes().aboveOrEqual(allocationBeforeUpdate)) { - PhysicalMemory.tryInitialize(); - } // Size parameters are recomputed from current values whenever they are queried } diff --git a/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/SerialAndEpsilonGCOptions.java b/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/SerialAndEpsilonGCOptions.java index 4dc96f62894c..b52a5f405702 100644 --- a/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/SerialAndEpsilonGCOptions.java +++ b/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/SerialAndEpsilonGCOptions.java @@ -69,9 +69,6 @@ protected void onValueUpdate(EconomicMap, Object> values, Long oldV @Option(help = "After use, Fill memory chunks with a sentinel value. Serial and epsilon GC only.", type = OptionType.Debug) // public static final HostedOptionKey ZapConsumedHeapChunks = new HostedOptionKey<>(false, SerialAndEpsilonGCOptions::serialOrEpsilonGCOnly); - @Option(help = "Bytes that can be allocated before (re-)querying the physical memory size. Serial and epsilon GC only.", type = OptionType.Debug) // - public static final HostedOptionKey AllocationBeforePhysicalMemorySize = new HostedOptionKey<>(1L * 1024L * 1024L, SerialAndEpsilonGCOptions::serialOrEpsilonGCOnly); - @Option(help = "Number of bytes at the beginning of each heap chunk that are not used for payload data, i.e., can be freely used as metadata by the heap chunk provider. Serial and epsilon GC only.", type = OptionType.Debug) // public static final HostedOptionKey HeapChunkHeaderPadding = new HostedOptionKey<>(0, SerialAndEpsilonGCOptions::serialOrEpsilonGCOnly); diff --git a/substratevm/src/com.oracle.svm.core.graal.aarch64/src/com/oracle/svm/core/graal/aarch64/SubstrateAArch64Backend.java b/substratevm/src/com.oracle.svm.core.graal.aarch64/src/com/oracle/svm/core/graal/aarch64/SubstrateAArch64Backend.java index e8acc824eb4f..9ef88c2646d0 100755 --- a/substratevm/src/com.oracle.svm.core.graal.aarch64/src/com/oracle/svm/core/graal/aarch64/SubstrateAArch64Backend.java +++ b/substratevm/src/com.oracle.svm.core.graal.aarch64/src/com/oracle/svm/core/graal/aarch64/SubstrateAArch64Backend.java @@ -539,15 +539,31 @@ protected Value emitIndirectForeignCallAddress(ForeignCallLinkage linkage) { protected void emitForeignCallOp(ForeignCallLinkage linkage, Value targetAddress, Value result, Value[] arguments, Value[] temps, LIRFrameState info) { SubstrateForeignCallLinkage callTarget = (SubstrateForeignCallLinkage) linkage; SharedMethod targetMethod = (SharedMethod) callTarget.getMethod(); + Value exceptionTemp = getExceptionTemp(info != null && info.exceptionEdge != null); + if (shouldEmitOnlyIndirectCalls()) { RegisterValue targetRegister = AArch64.lr.asValue(FrameAccess.getWordStamp().getLIRKind(getLIRKindTool())); emitMove(targetRegister, targetAddress); append(new SubstrateAArch64IndirectCallOp(targetMethod, result, arguments, temps, targetRegister, info, Value.ILLEGAL, StatusSupport.STATUS_ILLEGAL, - getDestroysCallerSavedRegisters(targetMethod), Value.ILLEGAL)); + getDestroysCallerSavedRegisters(targetMethod), exceptionTemp)); } else { assert targetAddress == null; append(new SubstrateAArch64DirectCallOp(targetMethod, result, arguments, temps, info, Value.ILLEGAL, StatusSupport.STATUS_ILLEGAL, - getDestroysCallerSavedRegisters(targetMethod), Value.ILLEGAL)); + getDestroysCallerSavedRegisters(targetMethod), exceptionTemp)); + } + } + + /** + * For invokes that have an exception handler, the register used for the incoming exception + * is destroyed at the call site even when registers are caller saved. The normal object + * return register is used in {@link NodeLIRBuilder#emitReadExceptionObject} also for the + * exception. + */ + private Value getExceptionTemp(boolean hasExceptionEdge) { + if (hasExceptionEdge) { + return getRegisterConfig().getReturnRegister(JavaKind.Object).asValue(); + } else { + return Value.ILLEGAL; } } @@ -709,11 +725,7 @@ protected void prologSetParameterNodes(StructuredGraph graph, Value[] params) { * exception. */ private Value getExceptionTemp(CallTargetNode callTarget) { - if (callTarget.invoke() instanceof InvokeWithExceptionNode) { - return gen.getRegisterConfig().getReturnRegister(JavaKind.Object).asValue(); - } else { - return Value.ILLEGAL; - } + return ((SubstrateAArch64LIRGenerator) gen).getExceptionTemp(callTarget.invoke() instanceof InvokeWithExceptionNode); } @Override diff --git a/substratevm/src/com.oracle.svm.core.graal.amd64/src/com/oracle/svm/core/graal/amd64/SubstrateAMD64Backend.java b/substratevm/src/com.oracle.svm.core.graal.amd64/src/com/oracle/svm/core/graal/amd64/SubstrateAMD64Backend.java index 9855953543c8..d791b3a9fe33 100644 --- a/substratevm/src/com.oracle.svm.core.graal.amd64/src/com/oracle/svm/core/graal/amd64/SubstrateAMD64Backend.java +++ b/substratevm/src/com.oracle.svm.core.graal.amd64/src/com/oracle/svm/core/graal/amd64/SubstrateAMD64Backend.java @@ -629,17 +629,32 @@ protected Value emitIndirectForeignCallAddress(ForeignCallLinkage linkage) { protected void emitForeignCallOp(ForeignCallLinkage linkage, Value targetAddress, Value result, Value[] arguments, Value[] temps, LIRFrameState info) { SubstrateForeignCallLinkage callTarget = (SubstrateForeignCallLinkage) linkage; SharedMethod targetMethod = (SharedMethod) callTarget.getMethod(); - vzeroupperBeforeCall(this, arguments, info, targetMethod); + Value exceptionTemp = getExceptionTemp(info != null && info.exceptionEdge != null); + vzeroupperBeforeCall(this, arguments, info, targetMethod); if (shouldEmitOnlyIndirectCalls()) { AllocatableValue targetRegister = AMD64.rax.asValue(FrameAccess.getWordStamp().getLIRKind(getLIRKindTool())); emitMove(targetRegister, targetAddress); append(new SubstrateAMD64IndirectCallOp(targetMethod, result, arguments, temps, targetRegister, info, - Value.ILLEGAL, Value.ILLEGAL, StatusSupport.STATUS_ILLEGAL, getDestroysCallerSavedRegisters(targetMethod), Value.ILLEGAL)); + Value.ILLEGAL, Value.ILLEGAL, StatusSupport.STATUS_ILLEGAL, getDestroysCallerSavedRegisters(targetMethod), exceptionTemp)); } else { assert targetAddress == null; append(new SubstrateAMD64DirectCallOp(targetMethod, result, arguments, temps, info, Value.ILLEGAL, - Value.ILLEGAL, StatusSupport.STATUS_ILLEGAL, getDestroysCallerSavedRegisters(targetMethod), Value.ILLEGAL)); + Value.ILLEGAL, StatusSupport.STATUS_ILLEGAL, getDestroysCallerSavedRegisters(targetMethod), exceptionTemp)); + } + } + + /** + * For invokes that have an exception handler, the register used for the incoming exception + * is destroyed at the call site even when registers are caller saved. The normal object + * return register is used in {@link NodeLIRBuilder#emitReadExceptionObject} also for the + * exception. + */ + private Value getExceptionTemp(boolean hasExceptionEdge) { + if (hasExceptionEdge) { + return getRegisterConfig().getReturnRegister(JavaKind.Object).asValue(); + } else { + return Value.ILLEGAL; } } @@ -903,11 +918,7 @@ private boolean getDestroysCallerSavedRegisters(ResolvedJavaMethod targetMethod) * exception. */ private Value getExceptionTemp(CallTargetNode callTarget) { - if (callTarget.invoke() instanceof InvokeWithExceptionNode) { - return gen.getRegisterConfig().getReturnRegister(JavaKind.Object).asValue(); - } else { - return Value.ILLEGAL; - } + return ((SubstrateAMD64LIRGenerator) gen).getExceptionTemp(callTarget.invoke() instanceof InvokeWithExceptionNode); } @Override diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/SubstrateOptions.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/SubstrateOptions.java index 5ebd3f39c542..b07164e6d704 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/SubstrateOptions.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/SubstrateOptions.java @@ -310,6 +310,8 @@ protected void onValueUpdate(EconomicMap, Object> values, Boolean o } } }; + @Option(help = "Physical memory size (in bytes). By default, the value is queried from the OS/container during VM startup.", type = OptionType.Expert)// + public static final RuntimeOptionKey MaxRAM = new RuntimeOptionKey<>(0L, Immutable); @Option(help = "The size of each thread stack at run-time, in bytes.", type = OptionType.User)// public static final RuntimeOptionKey StackSize = new RuntimeOptionKey<>(0L); diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/graal/snippets/CEntryPointSnippets.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/graal/snippets/CEntryPointSnippets.java index 1a818b24a79f..4101185e6007 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/graal/snippets/CEntryPointSnippets.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/graal/snippets/CEntryPointSnippets.java @@ -89,6 +89,7 @@ import com.oracle.svm.core.graal.nodes.CEntryPointLeaveNode; import com.oracle.svm.core.graal.nodes.CEntryPointUtilityNode; import com.oracle.svm.core.heap.Heap; +import com.oracle.svm.core.heap.PhysicalMemory; import com.oracle.svm.core.heap.ReferenceHandler; import com.oracle.svm.core.heap.ReferenceHandlerThread; import com.oracle.svm.core.heap.RestrictHeapAccess; @@ -357,6 +358,9 @@ private static int initializeIsolate(CEntryPointCreateIsolateParameters paramete /* Adjust stack overflow boundary of main thread. */ StackOverflowCheck.singleton().updateStackOverflowBoundary(); + /* Initialize the physical memory size. */ + PhysicalMemory.size(); + assert !isolateInitialized; isolateInitialized = true; diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/graal/snippets/SubstrateAllocationSnippets.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/graal/snippets/SubstrateAllocationSnippets.java index 470a82b65434..3e862eff4793 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/graal/snippets/SubstrateAllocationSnippets.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/graal/snippets/SubstrateAllocationSnippets.java @@ -131,7 +131,7 @@ protected Object allocateInstance(@NonNullParameter DynamicHub hub, @ConstantParameter FillContent fillContents, @ConstantParameter boolean emitMemoryBarrier, @ConstantParameter AllocationProfilingData profilingData) { - Object result = allocateInstanceImpl(encodeAsTLABObjectHeader(hub), WordFactory.unsigned(size), fillContents, emitMemoryBarrier, true, profilingData); + Object result = allocateInstanceImpl(encodeAsTLABObjectHeader(hub), WordFactory.unsigned(size), false, fillContents, emitMemoryBarrier, true, profilingData); return piCastToSnippetReplaceeStamp(result); } @@ -230,7 +230,7 @@ protected Object allocateInstanceDynamicImpl(DynamicHub hub, FillContent fillCon @SuppressWarnings("unused") boolean supportsOptimizedFilling, AllocationProfilingData profilingData) { // The hub was already verified by a ValidateNewInstanceClassNode. UnsignedWord size = LayoutEncoding.getPureInstanceAllocationSize(hub.getLayoutEncoding()); - Object result = allocateInstanceImpl(encodeAsTLABObjectHeader(hub), size, fillContents, emitMemoryBarrier, false, profilingData); + Object result = allocateInstanceImpl(encodeAsTLABObjectHeader(hub), size, false, fillContents, emitMemoryBarrier, false, profilingData); return piCastToSnippetReplaceeStamp(result); } diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/heap/PhysicalMemory.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/heap/PhysicalMemory.java index ec613423bf85..d9516fe2b2d3 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/heap/PhysicalMemory.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/heap/PhysicalMemory.java @@ -29,7 +29,7 @@ import org.graalvm.word.WordFactory; import com.oracle.svm.core.Containers; -import com.oracle.svm.core.graal.snippets.CEntryPointSnippets; +import com.oracle.svm.core.SubstrateOptions; import com.oracle.svm.core.jdk.UninterruptibleUtils.AtomicInteger; import com.oracle.svm.core.stack.StackOverflowCheck; import com.oracle.svm.core.thread.PlatformThreads; @@ -45,6 +45,7 @@ public class PhysicalMemory { /** Implemented by operating-system specific code. */ public interface PhysicalMemorySupport { + /* Will be removed in GR-48001. */ default boolean hasSize() { throw VMError.shouldNotReachHere("Unused, will be removed"); } @@ -53,15 +54,14 @@ default boolean hasSize() { UnsignedWord size(); } - /** A sentinel unset value. */ + private static final AtomicInteger INITIALIZING = new AtomicInteger(0); private static final UnsignedWord UNSET_SENTINEL = UnsignedUtils.MAX_VALUE; - - /** Prevent recursive initialization in {@link #tryInitialize}. */ - static AtomicInteger initializing = new AtomicInteger(0); - - /** The cached size of physical memory, or an unset value. */ private static UnsignedWord cachedSize = UNSET_SENTINEL; + public static boolean isInitialized() { + return cachedSize != UNSET_SENTINEL; + } + /** * Returns the size of physical memory in bytes, querying it from the OS if it has not been * initialized yet. @@ -75,57 +75,33 @@ public static UnsignedWord size() { * Note that we want to have this safety check even when the cache is already * initialized, so that we always detect wrong usages that could lead to problems. */ - throw VMError.shouldNotReachHere("Accessing the physical memory size requires allocation and synchronization"); + throw VMError.shouldNotReachHere("Accessing the physical memory size may require allocation and synchronization"); } if (!isInitialized()) { - initializing.incrementAndGet(); + /* + * Multiple threads can race to initialize the cache. This is OK because all of them + * will (most-likely) compute the same value. + */ + INITIALIZING.incrementAndGet(); try { - /* - * Multiple threads can race to initialize the cache. This is OK because all of them - * will compute the same value. - */ - doInitialize(); + long memoryLimit = SubstrateOptions.MaxRAM.getValue(); + if (memoryLimit > 0) { + cachedSize = WordFactory.unsigned(memoryLimit); + } else { + memoryLimit = Containers.memoryLimitInBytes(); + cachedSize = memoryLimit == Containers.UNKNOWN + ? ImageSingletons.lookup(PhysicalMemorySupport.class).size() + : WordFactory.unsigned(memoryLimit); + } } finally { - initializing.decrementAndGet(); + INITIALIZING.decrementAndGet(); } } return cachedSize; } - /** - * Tries to initialize the cached memory size. If the initialization is not possible, e.g., - * because the call is from within a VMOperation, the method does nothing. - */ - public static void tryInitialize() { - if (isInitialized() || isInitializationDisallowed()) { - return; - } - - /* - * We need to prevent recursive calls of the initialization. We also want only one thread to - * try the initialization. Since this is an optional initialization, we also do not need to - * wait until the other thread has finished the initialization. Initialization can be quite - * heavyweight and involve reading configuration files. - */ - if (initializing.compareAndSet(0, 1)) { - try { - doInitialize(); - } finally { - initializing.decrementAndGet(); - } - } - } - - /** - * Returns true if the memory size has been queried from the OS, i.e., if - * {@link #getCachedSize()} can be called. - */ - public static boolean isInitialized() { - return cachedSize != UNSET_SENTINEL; - } - /** * Returns the size of physical memory in bytes that has been previously cached. This method * must not be called if {@link #isInitialized()} is still false. @@ -136,15 +112,6 @@ public static UnsignedWord getCachedSize() { } private static boolean isInitializationDisallowed() { - return Heap.getHeap().isAllocationDisallowed() || VMOperation.isInProgress() || !PlatformThreads.isCurrentAssigned() || - !CEntryPointSnippets.isIsolateInitialized() || StackOverflowCheck.singleton().isYellowZoneAvailable(); - } - - @RestrictHeapAccess(access = RestrictHeapAccess.Access.UNRESTRICTED, reason = "Only called if allocation is allowed.") - private static void doInitialize() { - long memoryLimit = Containers.memoryLimitInBytes(); - cachedSize = memoryLimit == Containers.UNKNOWN - ? ImageSingletons.lookup(PhysicalMemorySupport.class).size() - : WordFactory.unsigned(memoryLimit); + return Heap.getHeap().isAllocationDisallowed() || VMOperation.isInProgress() || !PlatformThreads.isCurrentAssigned() || StackOverflowCheck.singleton().isYellowZoneAvailable(); } } diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/VarHandleFeature.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/VarHandleFeature.java index 02341237e8e3..db1aa691525a 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/VarHandleFeature.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/VarHandleFeature.java @@ -183,10 +183,17 @@ public void afterAnalysis(AfterAnalysisAccess access) { private Object processVarHandle(Object obj) { VarHandleInfo info = infos.get(obj.getClass()); if (info != null && processedVarHandles.putIfAbsent(obj, true) == null) { - VMError.guarantee(markAsUnsafeAccessed != null, "New VarHandle found after static analysis"); - Field field = findVarHandleField(obj); - markAsUnsafeAccessed.accept(field); + /* + * It is OK if we see a new VarHandle after analysis, as long as the field itself was + * already registered as Unsafe accessed by another VarHandle during analysis. This can + * happen when the late class initializer analysis determines that a class is safe for + * initialization at build time after the analysis. + */ + if (processedVarHandles.putIfAbsent(field, true) == null) { + VMError.guarantee(markAsUnsafeAccessed != null, "New VarHandle found after static analysis"); + markAsUnsafeAccessed.accept(field); + } } return obj; } diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/JfrThreadRepository.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/JfrThreadRepository.java index 47e103ebedfa..adf53c0dbf66 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/JfrThreadRepository.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/JfrThreadRepository.java @@ -145,7 +145,8 @@ private long registerThreadGroup(Thread thread, boolean isVirtual) { /* For virtual threads, a fixed thread group id is reserved. */ return VIRTUAL_THREAD_GROUP_ID; } - return registerThreadGroup0(thread.getThreadGroup()); + ThreadGroup group = JavaThreads.getRawThreadGroup(thread); + return registerThreadGroup0(group); } @Uninterruptible(reason = "Epoch must not change while in this method.") diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/sampler/AbstractJfrExecutionSampler.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/sampler/AbstractJfrExecutionSampler.java index f05a381f45f8..94f3ac0bfe5f 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/sampler/AbstractJfrExecutionSampler.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/sampler/AbstractJfrExecutionSampler.java @@ -261,8 +261,8 @@ protected void operate() { boolean shouldSample = shouldSample(); if (sampler.isSampling != shouldSample) { if (shouldSample) { - sampler.startSampling(); sampler.isSampling = true; + sampler.startSampling(); } else { sampler.stopSampling(); sampler.isSampling = false; diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/thread/JavaThreads.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/thread/JavaThreads.java index e96ce08d9ee1..82aa1ac06088 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/thread/JavaThreads.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/thread/JavaThreads.java @@ -106,6 +106,25 @@ public static long getThreadId(Thread thread) { } } + /** + * Similar to {@link Thread#getThreadGroup()} but without any of the extra checks that the JDK + * code does (e.g., the method below does not check for virtual threads or + * {@code Thread.isTerminated()}). + */ + @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) + public static ThreadGroup getRawThreadGroup(Thread thread) { + Target_java_lang_Thread t = SubstrateUtil.cast(thread, Target_java_lang_Thread.class); + if (JavaVersionUtil.JAVA_SPEC >= 19) { + Target_java_lang_Thread_FieldHolder holder = t.holder; + if (holder != null) { + return holder.group; + } + return null; + } else { + return t.group; + } + } + /** * Safe method to check whether a thread has been interrupted. * diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/thread/Target_java_lang_Thread.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/thread/Target_java_lang_Thread.java index 8fd02487833b..cce5b52685eb 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/thread/Target_java_lang_Thread.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/thread/Target_java_lang_Thread.java @@ -271,7 +271,6 @@ private static int nextThreadNum() { contextClassLoader = ClassLoader.getSystemClassLoader(); } - @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) @Substitute @Platforms(InternalPlatform.NATIVE_ONLY.class) public long getId() { @@ -282,10 +281,6 @@ public long getId() { @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) public native String getName(); - @AnnotateOriginal - @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) - public native ThreadGroup getThreadGroup(); - @AnnotateOriginal @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) @TargetElement(onlyWith = JDK19OrLater.class) @@ -771,7 +766,6 @@ static void blockedOn(Target_sun_nio_ch_Interruptible b) { */ @Substitute @TargetElement(onlyWith = JDK19OrLater.class) - @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) boolean isTerminated() { return (holder.threadStatus & JVMTI_THREAD_STATE_TERMINATED) != 0; } diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/FeatureHandler.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/FeatureHandler.java index 5292a0bfea9f..fa88770748bd 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/FeatureHandler.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/FeatureHandler.java @@ -54,7 +54,9 @@ import com.oracle.svm.core.option.LocatableMultiOptionValue; import com.oracle.svm.core.option.SubstrateOptionsParser; import com.oracle.svm.core.util.UserError; +import com.oracle.svm.core.util.UserError.UserException; import com.oracle.svm.core.util.VMError; +import com.oracle.svm.core.util.VMError.HostedError; import com.oracle.svm.hosted.FeatureImpl.IsInConfigurationAccessImpl; import com.oracle.svm.util.ReflectionUtil; import com.oracle.svm.util.ReflectionUtil.ReflectionUtilError; @@ -83,7 +85,11 @@ private static List userEnabledFeatures() { public void forEachFeature(Consumer consumer) { for (Feature feature : featureInstances) { - consumer.accept(feature); + try { + consumer.accept(feature); + } catch (Throwable t) { + throw handleFeatureError(feature, t); + } } } @@ -215,8 +221,12 @@ private void registerFeature(Class baseFeatureClass, Function, Class throw UserError.abort(ex.getCause(), "Error instantiating Feature class %s. Ensure the class is not abstract and has a no-argument constructor.", featureClass.getTypeName()); } - if (!feature.isInConfiguration(access)) { - return; + try { + if (!feature.isInConfiguration(access)) { + return; + } + } catch (Throwable t) { + throw handleFeatureError(feature, t); } /* @@ -228,7 +238,13 @@ private void registerFeature(Class baseFeatureClass, Function, Class /* * First add dependent features so that initializers are executed in order of dependencies. */ - for (Class requiredFeatureClass : feature.getRequiredFeatures()) { + List> requiredFeatures; + try { + requiredFeatures = feature.getRequiredFeatures(); + } catch (Throwable t) { + throw handleFeatureError(feature, t); + } + for (Class requiredFeatureClass : requiredFeatures) { registerFeature(requiredFeatureClass, specificClassProvider, access); } @@ -254,4 +270,22 @@ public void dumpAllFeatures(PrintWriter out) { out.println(requiredFeaturesString); }); } + + private static UserException handleFeatureError(Feature feature, Throwable throwable) { + /* Avoid wrapping UserErrors and VMErrors. */ + if (throwable instanceof UserException userError) { + throw userError; + } + if (throwable instanceof HostedError vmError) { + throw vmError; + } + + String featureClassName = feature.getClass().getName(); + String throwableClassName = throwable.getClass().getName(); + if (InternalFeature.class.isAssignableFrom(feature.getClass())) { + throw VMError.shouldNotReachHere("InternalFeature defined by %s unexpectedly failed with a(n) %s".formatted(featureClassName, throwableClassName), throwable); + } + throw UserError.abort(throwable, "Feature defined by %s unexpectedly failed with a(n) %s. Please report this problem to the authors of %s.", featureClassName, throwableClassName, + featureClassName); + } } diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/reflect/ReflectionDataBuilder.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/reflect/ReflectionDataBuilder.java index f8090202bce0..52cd6679b95a 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/reflect/ReflectionDataBuilder.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/reflect/ReflectionDataBuilder.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 2023, 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 @@ -743,7 +743,10 @@ private void registerTypesForGenericSignature(Type type, int dimension) { } private void registerTypesForRecordComponent(RecordComponent recordComponent) { - register(ConfigurationCondition.alwaysTrue(), true, recordComponent.getAccessor()); + Method accessorOrNull = recordComponent.getAccessor(); + if (accessorOrNull != null) { + register(ConfigurationCondition.alwaysTrue(), true, accessorOrNull); + } registerTypesForAnnotations(recordComponent); registerTypesForTypeAnnotations(recordComponent); } diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/substitute/AnnotationSubstitutionProcessor.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/substitute/AnnotationSubstitutionProcessor.java index 6d083eb4adfa..636639eb9005 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/substitute/AnnotationSubstitutionProcessor.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/substitute/AnnotationSubstitutionProcessor.java @@ -55,6 +55,7 @@ import com.oracle.graal.pointsto.meta.AnalysisField; import com.oracle.svm.core.SubstrateOptions; import com.oracle.svm.core.SubstrateUtil; +import com.oracle.svm.core.Uninterruptible; import com.oracle.svm.core.annotate.Alias; import com.oracle.svm.core.annotate.AnnotateOriginal; import com.oracle.svm.core.annotate.Delete; @@ -460,10 +461,12 @@ private void handleMethodInAliasClass(Executable annotatedMethod, Class origi ResolvedJavaMethod annotated = metaAccess.lookupJavaMethod(annotatedMethod); ResolvedJavaMethod original = findOriginalMethod(annotatedMethod, originalClass); - if (original == null) { /* Optional target that is not present, so nothing to do. */ - } else if (deleteAnnotation != null) { + return; + } + + if (deleteAnnotation != null) { if (SubstrateOptions.VerifyNamingConventions.getValue()) { int modifiers = original.getModifiers(); if (Modifier.isProtected(modifiers) || Modifier.isPublic(modifiers)) { @@ -476,12 +479,20 @@ private void handleMethodInAliasClass(Executable annotatedMethod, Class origi } registerAsDeleted(annotated, original, deleteAnnotation); } else if (substituteAnnotation != null) { + if (AnnotationAccess.isAnnotationPresent(annotated, Uninterruptible.class) && !isEffectivelyFinal(original)) { + throw UserError.abort("@Uninterruptible may only be combined with @Substitute if the original method is effectively final: %s", annotatedMethod); + } + SubstitutionMethod substitution = new SubstitutionMethod(original, annotated, false, true); if (substituteAnnotation.polymorphicSignature()) { register(polymorphicMethodSubstitutions, annotated, original, substitution); } register(methodSubstitutions, annotated, original, substitution); } else if (annotateOriginalAnnotation != null) { + if (AnnotationAccess.isAnnotationPresent(annotated, Uninterruptible.class) && !isEffectivelyFinal(original)) { + throw UserError.abort("@Uninterruptible may only be combined with @AnnotateOriginal if the original method is effectively final: %s", annotatedMethod); + } + AnnotatedMethod substitution = new AnnotatedMethod(original, annotated); register(methodSubstitutions, annotated, original, substitution); } else if (aliasAnnotation != null) { @@ -489,6 +500,10 @@ private void handleMethodInAliasClass(Executable annotatedMethod, Class origi } } + private static boolean isEffectivelyFinal(ResolvedJavaMethod original) { + return original.isPrivate() || original.isStatic() || original.isFinalFlagSet() || original.getDeclaringClass().isFinalFlagSet(); + } + private boolean skipExcludedPlatform(AnnotatedElement annotatedMethod) { return !NativeImageGenerator.includedIn(ImageSingletons.lookup(Platform.class), lookupAnnotation(annotatedMethod, Platforms.class)); } diff --git a/sulong/tests/toolchain-launchers-tests/CMakeLists.txt b/sulong/tests/toolchain-launchers-tests/CMakeLists.txt index 44ca1d55a252..3a8b2652c73f 100644 --- a/sulong/tests/toolchain-launchers-tests/CMakeLists.txt +++ b/sulong/tests/toolchain-launchers-tests/CMakeLists.txt @@ -1,5 +1,5 @@ # -# Copyright (c) 2022, Oracle and/or its affiliates. +# Copyright (c) 2023, Oracle and/or its affiliates. # # All rights reserved. # @@ -37,7 +37,7 @@ set(CHECK_WRAPPER_SCRIPT | grep.exe "GraalVM wrapper script" ${QUIETLY_REDIR}) if (APPLE AND DEFINED SULONG_NATIVE_BUILD) set(BC_MAGIC 0b17c0de) set(BC_SECTION __bitcode\|__bundle) - set(SHARED_FLAGS -dynamic -dylib -arch x86_64 -macosx_version_min 10.14.0 -lSystem) + set(SHARED_FLAGS -dylib -arch ${CMAKE_SYSTEM_PROCESSOR} -macosx_version_min 10.14.0 -undefined dynamic_lookup) else() set(BC_MAGIC dec04342) set(BC_SECTION \.llvmbc) diff --git a/tools/mx.tools/suite.py b/tools/mx.tools/suite.py index a979b1709ca5..76f15a0b7c20 100644 --- a/tools/mx.tools/suite.py +++ b/tools/mx.tools/suite.py @@ -26,8 +26,8 @@ "defaultLicense" : "GPLv2-CPE", "groupId" : "org.graalvm.tools", - "version" : "23.0.2.0", - "release" : True, + "version" : "23.0.2.1", + "release" : False, "url" : "http://openjdk.java.net/projects/graal", "developer" : { "name" : "GraalVM Development", diff --git a/tools/src/com.oracle.truffle.tools.dap.test/src/com/oracle/truffle/tools/dap/test/RelativeSourceDAPTest.java b/tools/src/com.oracle.truffle.tools.dap.test/src/com/oracle/truffle/tools/dap/test/RelativeSourceDAPTest.java index 7ce2d007fa31..b53abf87e8b2 100644 --- a/tools/src/com.oracle.truffle.tools.dap.test/src/com/oracle/truffle/tools/dap/test/RelativeSourceDAPTest.java +++ b/tools/src/com.oracle.truffle.tools.dap.test/src/com/oracle/truffle/tools/dap/test/RelativeSourceDAPTest.java @@ -42,6 +42,7 @@ import org.graalvm.polyglot.Source; import static org.junit.Assert.assertTrue; +import org.junit.Ignore; import org.junit.Test; /** @@ -50,6 +51,7 @@ public class RelativeSourceDAPTest { @Test + @Ignore public void testSourcePath() throws Exception { // Using 3 source path elements and 3 sources to verify that // the correct final source path is built. @@ -155,6 +157,7 @@ public void testSourcePath() throws Exception { } @Test + @Ignore public void testNonExistingSourcePath() throws Exception { TestDebugNoContentLanguage language = new TestDebugNoContentLanguage("relative/path", true, true); ProxyLanguage.setDelegate(language); @@ -200,6 +203,7 @@ public void testNonExistingSourcePath() throws Exception { } @Test + @Ignore public void testBreakpoints() throws Exception { testBreakpoints(1, 1, 2); testBreakpoints(10, 4, 11); diff --git a/truffle/mx.truffle/suite.py b/truffle/mx.truffle/suite.py index 12360cc6f442..c22695cbd8c1 100644 --- a/truffle/mx.truffle/suite.py +++ b/truffle/mx.truffle/suite.py @@ -41,8 +41,8 @@ suite = { "mxversion": "6.17.0", "name" : "truffle", - "version" : "23.0.2.0", - "release" : True, + "version" : "23.0.2.1", + "release" : False, "groupId" : "org.graalvm.truffle", "sourceinprojectwhitelist" : [], "url" : "http://openjdk.java.net/projects/graal", diff --git a/truffle/src/com.oracle.truffle.api.strings.test/src/com/oracle/truffle/api/strings/test/TStringCornerCaseTests.java b/truffle/src/com.oracle.truffle.api.strings.test/src/com/oracle/truffle/api/strings/test/TStringCornerCaseTests.java index efcf28abf88c..2c2859140a4a 100644 --- a/truffle/src/com.oracle.truffle.api.strings.test/src/com/oracle/truffle/api/strings/test/TStringCornerCaseTests.java +++ b/truffle/src/com.oracle.truffle.api.strings.test/src/com/oracle/truffle/api/strings/test/TStringCornerCaseTests.java @@ -44,6 +44,7 @@ import static com.oracle.truffle.api.strings.test.TStringTestUtil.byteArray; import java.nio.charset.StandardCharsets; +import java.util.Arrays; import org.junit.Assert; import org.junit.Test; @@ -117,4 +118,14 @@ public void testMutableImpreciseCodeRange() { Assert.assertTrue(a.isCompatibleToUncached(TruffleString.Encoding.BYTES)); Assert.assertEquals(TruffleString.CodeRange.VALID, a.getCodeRangeImpreciseUncached(TruffleString.Encoding.BYTES)); } + + @Test + public void testSafePointPollInObjectEquals() { + char[] chars = new char[2000000]; + Arrays.fill(chars, 'a'); + String s = new String(chars); + TruffleString t1 = TruffleString.fromConstant(s, TruffleString.Encoding.UTF_16); + TruffleString t2 = TruffleString.fromConstant(s, TruffleString.Encoding.UTF_16); + Assert.assertEquals(t1, t2); + } } diff --git a/truffle/src/com.oracle.truffle.api.strings/src/com/oracle/truffle/api/strings/AbstractTruffleString.java b/truffle/src/com.oracle.truffle.api.strings/src/com/oracle/truffle/api/strings/AbstractTruffleString.java index 69c324e87b9a..77ff7d196853 100644 --- a/truffle/src/com.oracle.truffle.api.strings/src/com/oracle/truffle/api/strings/AbstractTruffleString.java +++ b/truffle/src/com.oracle.truffle.api.strings/src/com/oracle/truffle/api/strings/AbstractTruffleString.java @@ -1262,7 +1262,7 @@ public final boolean equals(Object obj) { return false; } } - return TruffleString.EqualNode.checkContentEquals(null, this, b, + return TruffleString.EqualNode.checkContentEquals(TruffleString.EqualNode.getUncached(), this, b, ToIndexableNodeGen.getUncached(), ToIndexableNodeGen.getUncached(), InlinedConditionProfile.getUncached(), @@ -1392,7 +1392,7 @@ private static void flatten(Node location, TruffleString src, int srcBegin, int @TruffleBoundary private static void copy(Node location, TruffleString src, byte[] dst, int dstFrom, int dstStride) { - Object arrayA = ToIndexableNodeGen.getUncached().execute(null, src, src.data()); + Object arrayA = ToIndexableNodeGen.getUncached().execute(location, src, src.data()); TStringOps.arraycopyWithStride(location, arrayA, src.offset(), src.stride(), 0, dst, 0, dstStride, dstFrom, src.length()); diff --git a/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/ReflectionUtils.java b/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/ReflectionUtils.java index 42f785b1c11b..2f89b755ce37 100644 --- a/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/ReflectionUtils.java +++ b/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/ReflectionUtils.java @@ -64,7 +64,19 @@ public static T newInstance(Class clazz, Object... args) { public static Object getField(Object value, String name) { try { - Field f = value.getClass().getDeclaredField(name); + Class c = value.getClass(); + Field f = null; + while (c != null) { + try { + f = c.getDeclaredField(name); + break; + } catch (NoSuchFieldException e) { + c = c.getSuperclass(); + if (c == null) { + throw e; + } + } + } setAccessible(f, true); return f.get(value); } catch (Exception e) { diff --git a/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/SubprocessTestUtils.java b/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/SubprocessTestUtils.java new file mode 100644 index 000000000000..3ed82b2cac5f --- /dev/null +++ b/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/SubprocessTestUtils.java @@ -0,0 +1,607 @@ +/* + * Copyright (c) 2020, 2021, 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 + * + * Subject to the condition set forth below, permission is hereby granted to any + * person obtaining a copy of this software, associated documentation and/or + * data (collectively the "Software"), free of charge and under any and all + * copyright rights in the Software, and any and all patent rights owned or + * freely licensable by each licensor hereunder covering either (i) the + * unmodified Software as contributed to or provided by such licensor, or (ii) + * the Larger Works (as defined below), to deal in both + * + * (a) the Software, and + * + * (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if + * one is included with the Software each a "Larger Work" to which the Software + * is contributed by such licensors), + * + * without restriction, including without limitation the rights to copy, create + * derivative works of, display, perform, and distribute the Software and make, + * use, sell, offer for sale, import, export, have made, and have sold the + * Software and the Larger Work(s), and to sublicense the foregoing rights on + * either these or other terms. + * + * This license is subject to the following condition: + * + * The above copyright notice and either this complete permission notice or at a + * minimum a reference to the UPL must be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package com.oracle.truffle.api.test; + +import java.io.BufferedReader; +import java.io.File; +import java.io.IOException; +import java.io.InputStreamReader; +import java.lang.reflect.Method; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.time.Duration; +import java.util.ArrayList; +import java.util.Formatter; +import java.util.List; +import java.util.Map; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicReference; +import java.util.function.Consumer; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.util.stream.Collectors; + +import org.junit.Assert; +import org.junit.Assume; +import org.junit.Test; + +/** + * Support for executing Truffle tests in a sub-process with filtered compilation failure options. + * This support is useful for tests that explicitly set + * {@code PolyglotCompilerOptions#CompilationFailureAction}, and can be affected by junit arguments. + * It's also useful for tests expecting compiler failure, VM crash or {@link OutOfMemoryError}. + * + * Usage example: + * + *
+ * @Test
+ * public void testCompilationFailure() throws Exception {
+ *     Runnable testToExecuteInSubprocess = () -> {
+ *         setupContext(Context.newBuilder().allowExperimentalOptions(true).option("engine.CompileImmediately", "true").option("engine.BackgroundCompilation", "false").option(
+ *                         "engine.CompilationFailureAction", "Throw"));
+ *         testCallTarget.call();
+ *     };
+ *     SubprocessTestUtils.newBuilder(getClass(), testToExecuteInSubprocess).run();
+ * }
+ * 
+ */ +public final class SubprocessTestUtils { + + private static final String CONFIGURED_PROPERTY = SubprocessTestUtils.class.getSimpleName() + ".configured"; + + private static final String TO_REMOVE_PREFIX = "~~"; + + private SubprocessTestUtils() { + } + + /** + * Executes action in a sub-process with filtered compilation failure options. + * + * @param testClass the test enclosing class. + * @param action the test to execute. + * @param additionalVmOptions additional vm option added to java arguments. Prepend + * {@link #TO_REMOVE_PREFIX} to remove item from existing vm options. + * @return {@link Subprocess} if it's called by a test that is not executing in a sub-process. + * Returns {@code null} for a caller run in a sub-process. + * @see SubprocessTestUtils + */ + public static Subprocess executeInSubprocess(Class testClass, Runnable action, String... additionalVmOptions) throws IOException, InterruptedException { + return executeInSubprocess(testClass, action, true, additionalVmOptions); + } + + /** + * Executes action in a sub-process with filtered compilation failure options. + * + * @param testClass the test enclosing class. + * @param action the test to execute. + * @param failOnNonZeroExitCode if {@code true}, the test fails if the sub-process ends with a + * non-zero return value. + * @param additionalVmOptions additional vm option added to java arguments. Prepend + * {@link #TO_REMOVE_PREFIX} to remove item from existing vm options. + * @return {@link Subprocess} if it's called by a test that is not executing in a sub-process. + * Returns {@code null} for a caller run in a sub-process. + * @see SubprocessTestUtils + */ + public static Subprocess executeInSubprocess(Class testClass, Runnable action, boolean failOnNonZeroExitCode, String... additionalVmOptions) throws IOException, InterruptedException { + AtomicReference process = new AtomicReference<>(); + newBuilder(testClass, action).failOnNonZeroExit(failOnNonZeroExitCode).prefixVmOption(additionalVmOptions).onExit((p) -> process.set(p)).run(); + return process.get(); + } + + /** + * Returns {@code true} if it's called by a test that is already executing in a sub-process. + */ + public static boolean isSubprocess() { + return Boolean.getBoolean(CONFIGURED_PROPERTY); + } + + private static Method findTestMethod(Class testClass) { + StackTraceElement[] stack = Thread.currentThread().getStackTrace(); + if (stack != null) { + for (int i = stack.length - 1; i >= 0; i--) { + StackTraceElement element = stack[i]; + if (testClass.getName().equals(element.getClassName())) { + try { + Method method = testClass.getDeclaredMethod(element.getMethodName()); + if (method.getAnnotation(Test.class) != null) { + return method; + } + } catch (NoSuchMethodException noSuchMethodException) { + // skip methods with arguments. + } + } + } + } + throw new IllegalStateException("Failed to find current test method in class " + testClass); + } + + private static Subprocess execute(Method testMethod, boolean failOnNonZeroExitCode, List prefixVMOptions, List postfixVmOptions) throws IOException, InterruptedException { + String enclosingElement = testMethod.getDeclaringClass().getName(); + String testName = testMethod.getName(); + Subprocess subprocess = javaHelper( + configure(getVmArgs(), prefixVMOptions, postfixVmOptions), + null, null, + List.of("com.oracle.mxtool.junit.MxJUnitWrapper", String.format("%s#%s", enclosingElement, testName)), + null); + if (failOnNonZeroExitCode && subprocess.exitCode != 0) { + Assert.fail(String.join("\n", subprocess.output)); + } + return subprocess; + } + + private static List configure(List vmArgs, List prefixVMOptions, List postfixVmOptions) { + List newVmArgs = new ArrayList<>(); + newVmArgs.addAll(vmArgs.stream().filter(vmArg -> { + for (String toRemove : getForbiddenVmOptions()) { + if (vmArg.startsWith(toRemove)) { + return false; + } + } + for (String additionalVmOption : prefixVMOptions) { + if (additionalVmOption.startsWith(TO_REMOVE_PREFIX) && vmArg.startsWith(additionalVmOption.substring(2))) { + return false; + } + } + for (String additionalVmOption : postfixVmOptions) { + if (additionalVmOption.startsWith(TO_REMOVE_PREFIX) && vmArg.startsWith(additionalVmOption.substring(2))) { + return false; + } + } + return true; + }).collect(Collectors.toList())); + for (String additionalVmOption : prefixVMOptions) { + if (!additionalVmOption.startsWith(TO_REMOVE_PREFIX)) { + newVmArgs.add(1, additionalVmOption); + } + } + for (String additionalVmOption : postfixVmOptions) { + if (!additionalVmOption.startsWith(TO_REMOVE_PREFIX)) { + newVmArgs.add(additionalVmOption); + } + } + newVmArgs.add(1, String.format("-D%s=%s", CONFIGURED_PROPERTY, "true")); + return newVmArgs; + } + + private static List getVmArgs() { + List vmArgs = getVMCommandLine(true); + vmArgs.add(PACKAGE_OPENING_OPTIONS); + return vmArgs; + } + + private static String[] getForbiddenVmOptions() { + return new String[]{ + graalOption("CompilationFailureAction"), + graalOption("CompilationBailoutAsFailure"), + graalOption("CrashAt"), + graalOption("DumpOnError"), + // Filter out the LogFile option to prevent overriding of the unit tests log + // file by a sub-process. + graalOption("LogFile"), // HotSpotTTYStreamProvider.Options#LogFile + "-Dpolyglot.log.file", + engineOption("CompilationFailureAction"), + engineOption("TraceCompilation"), + engineOption("TraceCompilationDetails") + }; + } + + private static String graalOption(String optionName) { + return "-Dgraal." + optionName; + } + + private static String engineOption(String optionName) { + return "-Dpolyglot.engine." + optionName; + } + + public static Builder newBuilder(Class testClass, Runnable inProcess) { + return new Builder(testClass, inProcess); + } + + public static final class Subprocess { + + /** + * The command line of the subprocess. + */ + public final List command; + + /** + * Exit code of the subprocess. + */ + public final int exitCode; + + /** + * Output from the subprocess broken into lines. + */ + public final List output; + + /** + * Whether the subprocess execution exceeded the supplied timeout. + */ + public final boolean timedOut; + + /** + * Explicit environment variables. + */ + private Map env; + + private Subprocess(List command, Map env, int exitCode, List output, boolean timedOut) { + this.command = command; + this.env = env; + this.exitCode = exitCode; + this.output = output; + this.timedOut = timedOut; + } + + private static final String DASHES_DELIMITER = "-------------------------------------------------------"; + + /** + * Returns the command followed by the output as a string. + * + * @param delimiter if non-null, the returned string has this value as a prefix and suffix + */ + public String toString(String delimiter) { + Formatter msg = new Formatter(); + if (delimiter != null) { + msg.format("%s%n", delimiter); + } + if (env != null && !env.isEmpty()) { + msg.format("env"); + for (Map.Entry e : env.entrySet()) { + msg.format(" %s=%s", e.getKey(), quoteShellArg(e.getValue())); + } + msg.format("\\%n"); + } + msg.format("%s%n", command.stream().map(e -> quoteShellArg(String.valueOf(e))).collect(Collectors.joining(" "))); + for (String line : output) { + msg.format("%s%n", line); + } + msg.format("exit code: %s%n", exitCode); + if (delimiter != null) { + msg.format("%s%n", delimiter); + } + return msg.toString(); + } + + /** + * Returns the command followed by the output as a string delimited by + * {@value #DASHES_DELIMITER}. + */ + @Override + public String toString() { + return toString(DASHES_DELIMITER); + } + } + + public static final class Builder { + + private final Class testClass; + private final Runnable runnable; + + private final List prefixVmArgs = new ArrayList<>(); + private final List postfixVmArgs = new ArrayList<>(); + private boolean failOnNonZeroExit = true; + private Consumer onExit; + + private Builder(Class testClass, Runnable run) { + this.testClass = testClass; + this.runnable = run; + } + + public Builder prefixVmOption(String... options) { + prefixVmArgs.addAll(List.of(options)); + return this; + } + + public Builder postfixVmOption(String... options) { + postfixVmArgs.addAll(List.of(options)); + return this; + } + + public Builder failOnNonZeroExit(boolean b) { + failOnNonZeroExit = b; + return this; + } + + public Builder onExit(Consumer exit) { + this.onExit = exit; + return this; + } + + public void run() throws IOException, InterruptedException { + if (isSubprocess()) { + runnable.run(); + } else { + Subprocess process = execute(findTestMethod(testClass), failOnNonZeroExit, prefixVmArgs, postfixVmArgs); + if (onExit != null) { + onExit.accept(process); + } + } + } + + } + + // Methods and constants copied from org.graalvm.compiler.test.SubprocessUtil + + /** + * The name of the boolean system property that can be set to preserve temporary files created + * as arguments files passed to the java launcher. + */ + private static final String KEEP_TEMPORARY_ARGUMENT_FILES_PROPERTY_NAME = "test.SubprocessTestUtil.keepTempArgumentFiles"; + + /** + * A sentinel value which when present in the {@code vmArgs} parameter for any of the + * {@code java(...)} methods in this class is replaced with a temporary argument file containing + * the contents of {@link #getPackageOpeningOptions}. The argument file is preserved if the + * {@link #KEEP_TEMPORARY_ARGUMENT_FILES_PROPERTY_NAME} system property is true. + */ + private static final String PACKAGE_OPENING_OPTIONS = ";:PACKAGE_OPENING_OPTIONS_IN_TEMPORARY_ARGUMENTS_FILE:;"; + + /** + * Pattern for a single shell command argument that does not need to quoted. + */ + private static final Pattern SAFE_SHELL_ARG = Pattern.compile("[A-Za-z0-9@%_\\-\\+=:,\\./]+"); + + /** + * Reliably quote a string as a single shell command argument. + */ + private static String quoteShellArg(String arg) { + if (arg.isEmpty()) { + return "\"\""; + } + Matcher m = SAFE_SHELL_ARG.matcher(arg); + if (m.matches()) { + return arg; + } + // See http://stackoverflow.com/a/1250279 + return "'" + arg.replace("'", "'\"'\"'") + "'"; + } + + /** + * Returns a new copy {@code args} with debugger arguments removed. + */ + private static List withoutDebuggerArguments(List args) { + List result = new ArrayList<>(args.size()); + for (String arg : args) { + if (!(arg.equals("-Xdebug") || arg.startsWith("-Xrunjdwp:") || arg.startsWith("-agentlib:jdwp"))) { + result.add(arg); + } + } + return result; + } + + /** + * Gets the command line for the current process. + * + * @return the command line arguments for the current process or {@code null} if they are not + * available + */ + private static List getProcessCommandLine() { + String processArgsFile = System.getenv().get("MX_SUBPROCESS_COMMAND_FILE"); + if (processArgsFile != null) { + try { + return Files.readAllLines(new File(processArgsFile).toPath()); + } catch (IOException e) { + } + } else { + Assume.assumeTrue("Process command line unavailable", false); + } + return null; + } + + /** + * Gets the command line used to start the current Java VM, including all VM arguments, but not + * including the main class or any Java arguments. This can be used to spawn an identical VM, + * but running different Java code. + * + * @param removeDebuggerArguments if {@code true}, pre-process the returned list with + * {@link #withoutDebuggerArguments(List)} + * @return {@code null} if the command line for the current process cannot be accessed + */ + private static List getVMCommandLine(boolean removeDebuggerArguments) { + List args = getProcessCommandLine(); + if (args == null) { + return null; + } else { + int index = findMainClassIndex(args); + args = args.subList(0, index); + if (removeDebuggerArguments) { + args = withoutDebuggerArguments(args); + } + return args; + } + } + + /** + * Executes a Java subprocess. + * + * @param vmArgs the VM arguments. If {@code vmArgs} contains {@link #PACKAGE_OPENING_OPTIONS}, + * the argument is replaced with arguments to do package opening (see + * {@link #getPackageOpeningOptions()} + * @param env the environment variables + * @param workingDir the working directory of the subprocess. If null, the working directory of + * the current process is used. + * @param mainClassAndArgs the main class and its arguments + * @param timeout the duration to wait for the process to finish. If null, the calling thread + * waits for the process indefinitely. + */ + private static Subprocess javaHelper(List vmArgs, Map env, File workingDir, List mainClassAndArgs, Duration timeout) throws IOException, InterruptedException { + List command = new ArrayList<>(vmArgs.size()); + Path packageOpeningOptionsArgumentsFile = null; + for (String vmArg : vmArgs) { + if (vmArg == PACKAGE_OPENING_OPTIONS) { + if (packageOpeningOptionsArgumentsFile == null) { + List packageOpeningOptions = getPackageOpeningOptions(); + if (!packageOpeningOptions.isEmpty()) { + packageOpeningOptionsArgumentsFile = Files.createTempFile(Paths.get("."), "package-opening-options-arguments-file", ".txt").toAbsolutePath(); + Files.write(packageOpeningOptionsArgumentsFile, packageOpeningOptions); + command.add("@" + packageOpeningOptionsArgumentsFile); + } + } + } else { + command.add(vmArg); + } + } + command.addAll(mainClassAndArgs); + try { + return process(command, env, workingDir, timeout); + } finally { + if (packageOpeningOptionsArgumentsFile != null) { + if (!Boolean.getBoolean(KEEP_TEMPORARY_ARGUMENT_FILES_PROPERTY_NAME)) { + Files.delete(packageOpeningOptionsArgumentsFile); + } + } + } + } + + /** + * Executes a command in a subprocess. + * + * @param command the command to be executed in a separate process. + * @param env environment variables of the subprocess. If null, no environment variables are + * passed. + * @param workingDir the working directory of the subprocess. If null, the working directory of + * the current process is used. + * @param timeout the duration to wait for the process to finish. When the timeout is reached, + * the subprocess is terminated forcefully. If the timeout is null, the calling + * thread waits for the process indefinitely. + */ + private static Subprocess process(List command, Map env, File workingDir, Duration timeout) throws IOException, InterruptedException { + ProcessBuilder processBuilder = new ProcessBuilder(command); + if (workingDir != null) { + processBuilder.directory(workingDir); + } + if (env != null) { + Map processBuilderEnv = processBuilder.environment(); + processBuilderEnv.putAll(env); + } + processBuilder.redirectErrorStream(true); + Process process = processBuilder.start(); + BufferedReader stdout = new BufferedReader(new InputStreamReader(process.getInputStream())); + List output = new ArrayList<>(); + if (timeout == null) { + String line; + while ((line = stdout.readLine()) != null) { + output.add(line); + } + return new Subprocess(command, env, process.waitFor(), output, false); + } else { + // The subprocess might produce output forever. We need to grab the output in a + // separate thread, so we can terminate the process after the timeout if necessary. + Thread outputReader = new Thread(() -> { + try { + String line; + while ((line = stdout.readLine()) != null) { + output.add(line); + } + } catch (IOException e) { + // happens when the process ends + } + }); + outputReader.start(); + boolean finishedOnTime = process.waitFor(timeout.getSeconds(), TimeUnit.SECONDS); + if (!finishedOnTime) { + process.destroyForcibly().waitFor(); + } + outputReader.join(); + return new Subprocess(command, env, process.exitValue(), output, !finishedOnTime); + } + } + + private static boolean hasArg(String optionName) { + if (optionName.equals("-cp") || optionName.equals("-classpath") || optionName.equals("-p")) { + return true; + } + if (optionName.equals("--version") || + optionName.equals("--show-version") || + optionName.equals("--dry-run") || + optionName.equals("--disable-@files") || + optionName.equals("--dry-run") || + optionName.equals("--help") || + optionName.equals("--help-extra")) { + return false; + } + if (optionName.startsWith("--")) { + return optionName.indexOf('=') == -1; + } + return false; + } + + /** + * Gets the command line options to do the same package opening and exporting specified by the + * {@code --open-packages} option to the {@code mx unittest} command. + * + * Properties defined in {@code com.oracle.mxtool.junit.MxJUnitWrapper}. + */ + private static List getPackageOpeningOptions() { + List result = new ArrayList<>(); + String[] actions = {"opens", "exports"}; + for (String action : actions) { + String opens = System.getProperty("com.oracle.mxtool.junit." + action); + if (opens != null && !opens.isEmpty()) { + for (String value : opens.split(System.lineSeparator())) { + result.add("--add-" + action + "=" + value); + } + } + } + return result; + } + + private static int findMainClassIndex(List commandLine) { + int i = 1; // Skip the java executable + + while (i < commandLine.size()) { + String s = commandLine.get(i); + if (s.charAt(0) != '-') { + // https://bugs.openjdk.java.net/browse/JDK-8027634 + if (s.charAt(0) != '@') { + return i; + } + i++; + } else if (hasArg(s)) { + i += 2; + } else { + i++; + } + } + throw new InternalError(); + } +} diff --git a/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/polyglot/GR47134.java b/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/polyglot/GR47134.java new file mode 100644 index 000000000000..37581d4b10db --- /dev/null +++ b/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/polyglot/GR47134.java @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2023, 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 + * + * Subject to the condition set forth below, permission is hereby granted to any + * person obtaining a copy of this software, associated documentation and/or + * data (collectively the "Software"), free of charge and under any and all + * copyright rights in the Software, and any and all patent rights owned or + * freely licensable by each licensor hereunder covering either (i) the + * unmodified Software as contributed to or provided by such licensor, or (ii) + * the Larger Works (as defined below), to deal in both + * + * (a) the Software, and + * + * (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if + * one is included with the Software each a "Larger Work" to which the Software + * is contributed by such licensors), + * + * without restriction, including without limitation the rights to copy, create + * derivative works of, display, perform, and distribute the Software and make, + * use, sell, offer for sale, import, export, have made, and have sold the + * Software and the Larger Work(s), and to sublicense the foregoing rights on + * either these or other terms. + * + * This license is subject to the following condition: + * + * The above copyright notice and either this complete permission notice or at a + * minimum a reference to the UPL must be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package com.oracle.truffle.api.test.polyglot; + +import static com.oracle.truffle.api.test.ReflectionUtils.getField; + +import java.util.Map; + +import org.graalvm.polyglot.Context; +import org.graalvm.polyglot.Value; +import org.graalvm.polyglot.proxy.ProxyExecutable; +import org.graalvm.polyglot.proxy.ProxyHashMap; +import org.junit.Assert; +import org.junit.Test; + +public class GR47134 { + @Test + @SuppressWarnings("unused") + public void test() { + try (Context ctx = Context.create()) { + // bug triggers only with hash maps + final Value member = ctx.asValue((ProxyExecutable) (v) -> ProxyHashMap.from(Map.of("a", "b"))); + for (int i = 0; i < 5000; i++) { + final Value execute = member.execute(); + for (var entries : execute.as(Map.class).entrySet()) { + // we need to iterate the map to trigger the bug + } + } + Map m = (Map) getField(getField(getField(getField(member, "context"), "lazy"), "languageInstance"), "hostToGuestCodeCache"); + Assert.assertTrue("Cache size is growing unbounded " + m.size(), m.size() < 100); + } + } +} diff --git a/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/polyglot/ValueAPITest.java b/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/polyglot/ValueAPITest.java index 597193225885..7b23f58e2623 100644 --- a/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/polyglot/ValueAPITest.java +++ b/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/polyglot/ValueAPITest.java @@ -2601,4 +2601,40 @@ public String toString() { } } + @Test + public void testDisconnectedBigInteger() { + Value val = Value.asValue(BigInteger.ONE); + assertTrue(val.isNumber()); + assertTrue(val.fitsInByte()); + assertTrue(val.fitsInShort()); + assertTrue(val.fitsInInt()); + assertTrue(val.fitsInInt()); + assertTrue(val.fitsInBigInteger()); + assertTrue(val.fitsInFloat()); + assertTrue(val.fitsInDouble()); + assertEquals(1, val.asByte()); + assertEquals(1, val.asShort()); + assertEquals(1, val.asInt()); + assertEquals(1, val.asLong()); + assertEquals(BigInteger.ONE, val.asBigInteger()); + assertEquals(1.0f, val.asFloat(), 0.0000001f); + assertEquals(1.0d, val.asDouble(), 0.000000000d); + assertEquals((Byte) (byte) 1, val.as(byte.class)); + assertEquals((Byte) (byte) 1, val.as(Byte.class)); + assertEquals((Short) (short) 1, val.as(short.class)); + assertEquals((Short) (short) 1, val.as(Short.class)); + assertEquals((Integer) 1, val.as(int.class)); + assertEquals((Integer) 1, val.as(Integer.class)); + assertEquals((Long) 1L, val.as(long.class)); + assertEquals((Long) 1L, val.as(Long.class)); + assertEquals((Float) 1.0f, val.as(float.class)); + assertEquals((Float) 1.0f, val.as(Float.class)); + assertEquals((Double) 1.0d, val.as(double.class)); + assertEquals((Double) 1.0d, val.as(Double.class)); + assertEquals(BigInteger.ONE, val.as(BigInteger.class)); + assertEquals(BigInteger.ONE, val.as(Number.class)); + assertEquals((Character) (char) 1, val.as(char.class)); + assertEquals((Character) (char) 1, val.as(Character.class)); + } + } diff --git a/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/polyglot/reflection.json b/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/polyglot/reflection.json index ac04707d40fc..ad1528e57da1 100644 --- a/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/polyglot/reflection.json +++ b/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/polyglot/reflection.json @@ -176,6 +176,8 @@ "name": "java.lang.Class", "methods": [ { "name": "getName", "parameterTypes": [] }, + { "name": "getSuperclass", "parameterTypes": [] }, + { "name": "getDeclaredField", "parameterTypes": ["java.lang.String"] }, { "name": "isInstance", "parameterTypes": ["java.lang.Object"] } ] }, @@ -194,5 +196,25 @@ "methods": [ { "name": "addOpens", "parameterTypes": ["java.lang.Module", "java.lang.String", "java.lang.Module"] } ] + }, + { + "name": "org.graalvm.polyglot.Value", + "allDeclaredFields": true, "allPublicFields": true + }, + { + "name": "org.graalvm.polyglot.AbstractValue", + "allDeclaredFields": true, "allPublicFields": true + }, + { + "name": "com.oracle.truffle.polyglot.PolyglotLanguageContext", + "allDeclaredFields": true, "allPublicFields": true + }, + { + "name": "com.oracle.truffle.polyglot.PolyglotLanguageContext$Lazy", + "allDeclaredFields": true, "allPublicFields": true + }, + { + "name": "com.oracle.truffle.polyglot.PolyglotLanguageInstance", + "allDeclaredFields": true, "allPublicFields": true } ] diff --git a/truffle/src/com.oracle.truffle.api/src/com/oracle/truffle/api/impl/Accessor.java b/truffle/src/com.oracle.truffle.api/src/com/oracle/truffle/api/impl/Accessor.java index 03eb42e35541..3edbadc8d28e 100644 --- a/truffle/src/com.oracle.truffle.api/src/com/oracle/truffle/api/impl/Accessor.java +++ b/truffle/src/com.oracle.truffle.api/src/com/oracle/truffle/api/impl/Accessor.java @@ -47,6 +47,7 @@ import java.io.OutputStream; import java.lang.invoke.MethodHandles.Lookup; import java.lang.reflect.Constructor; +import java.math.BigInteger; import java.net.URI; import java.net.URL; import java.nio.charset.Charset; @@ -308,6 +309,9 @@ protected HostSupport() { public abstract Node inlineToHostNode(Object target); + public abstract boolean bigIntegerFitsInFloat(BigInteger b); + + public abstract boolean bigIntegerFitsInDouble(BigInteger b); } public abstract static class EngineSupport extends Support { diff --git a/truffle/src/com.oracle.truffle.host/src/com/oracle/truffle/host/HostAccessor.java b/truffle/src/com.oracle.truffle.host/src/com/oracle/truffle/host/HostAccessor.java index 01608589c14e..84717d2305b6 100644 --- a/truffle/src/com.oracle.truffle.host/src/com/oracle/truffle/host/HostAccessor.java +++ b/truffle/src/com.oracle.truffle.host/src/com/oracle/truffle/host/HostAccessor.java @@ -40,6 +40,7 @@ */ package com.oracle.truffle.host; +import java.math.BigInteger; import java.util.function.Function; import java.util.function.Predicate; @@ -165,6 +166,16 @@ public boolean isHostLanguage(Class languageClass) { public Node inlineToHostNode(Object target) { return HostToTypeNodeGen.inline((InlineTarget) target); } + + @Override + public boolean bigIntegerFitsInFloat(BigInteger b) { + return HostObject.bigIntegerFitsInFloat(b); + } + + @Override + public boolean bigIntegerFitsInDouble(BigInteger b) { + return HostObject.bigIntegerFitsInDouble(b); + } } } diff --git a/truffle/src/com.oracle.truffle.host/src/com/oracle/truffle/host/HostObject.java b/truffle/src/com.oracle.truffle.host/src/com/oracle/truffle/host/HostObject.java index c9deea88c30d..bd12ed61f147 100644 --- a/truffle/src/com.oracle.truffle.host/src/com/oracle/truffle/host/HostObject.java +++ b/truffle/src/com.oracle.truffle.host/src/com/oracle/truffle/host/HostObject.java @@ -1838,6 +1838,10 @@ static boolean doOther(HostObject receiver, @TruffleBoundary boolean bigIntegerFitsInFloat() { BigInteger b = (BigInteger) obj; + return bigIntegerFitsInFloat(b); + } + + static boolean bigIntegerFitsInFloat(BigInteger b) { if (b.bitLength() <= 24) { // 24 = size of float mantissa + 1 return true; } else { @@ -1892,6 +1896,10 @@ static boolean doOther(HostObject receiver, @TruffleBoundary boolean bigIntegerFitsInDouble() { BigInteger b = (BigInteger) obj; + return bigIntegerFitsInDouble(b); + } + + static boolean bigIntegerFitsInDouble(BigInteger b) { if (b.bitLength() <= 53) { // 53 = size of double mantissa + 1 return true; } else { diff --git a/truffle/src/com.oracle.truffle.polyglot/src/com/oracle/truffle/polyglot/PolyglotImpl.java b/truffle/src/com.oracle.truffle.polyglot/src/com/oracle/truffle/polyglot/PolyglotImpl.java index e20a96a890d1..b228bd8f03ee 100644 --- a/truffle/src/com.oracle.truffle.polyglot/src/com/oracle/truffle/polyglot/PolyglotImpl.java +++ b/truffle/src/com.oracle.truffle.polyglot/src/com/oracle/truffle/polyglot/PolyglotImpl.java @@ -51,6 +51,7 @@ import java.io.OutputStream; import java.io.Reader; import java.lang.reflect.Method; +import java.math.BigInteger; import java.net.URI; import java.net.URL; import java.nio.charset.Charset; @@ -114,6 +115,7 @@ public final class PolyglotImpl extends AbstractPolyglotImpl { private final Map, PolyglotValueDispatch> primitiveValues = new HashMap<>(); Value hostNull; // effectively final private PolyglotValueDispatch disconnectedHostValue; + private PolyglotValueDispatch disconnectedBigIntegerHostValue; private volatile Object defaultFileSystemContext; private static volatile AbstractPolyglotImpl abstractImpl; @@ -163,6 +165,7 @@ PolyglotEngineImpl getPreinitializedEngine() { protected void initialize() { this.hostNull = getAPIAccess().newValue(PolyglotValueDispatch.createHostNull(this), null, EngineAccessor.HOST.getHostNull()); this.disconnectedHostValue = new PolyglotValueDispatch.HostValue(this); + this.disconnectedBigIntegerHostValue = new PolyglotValueDispatch.BigIntegerHostValue(this); PolyglotValueDispatch.createDefaultValues(this, null, primitiveValues); } @@ -452,7 +455,7 @@ Value asValue(PolyglotContextImpl currentContext, Object hostValue) { } else { guestValue = EngineAccessor.HOST.toDisconnectedHostObject(hostValue); } - return getAPIAccess().newValue(disconnectedHostValue, null, guestValue); + return getAPIAccess().newValue(hostValue instanceof BigInteger ? disconnectedBigIntegerHostValue : disconnectedHostValue, null, guestValue); } } diff --git a/truffle/src/com.oracle.truffle.polyglot/src/com/oracle/truffle/polyglot/PolyglotIterator.java b/truffle/src/com.oracle.truffle.polyglot/src/com/oracle/truffle/polyglot/PolyglotIterator.java index 9e3184807b2e..c100e519e973 100644 --- a/truffle/src/com.oracle.truffle.polyglot/src/com/oracle/truffle/polyglot/PolyglotIterator.java +++ b/truffle/src/com.oracle.truffle.polyglot/src/com/oracle/truffle/polyglot/PolyglotIterator.java @@ -185,7 +185,7 @@ static Cache lookup(PolyglotLanguageContext languageContext, Class receiverCl } assert cache.receiverClass == receiverClass; assert cache.valueClass == valueClass; - assert cache.valueType == valueType; + assert Objects.equals(cache.valueType, valueType); return cache; } diff --git a/truffle/src/com.oracle.truffle.polyglot/src/com/oracle/truffle/polyglot/PolyglotMap.java b/truffle/src/com.oracle.truffle.polyglot/src/com/oracle/truffle/polyglot/PolyglotMap.java index 96a9aecb0fb1..9e61e94820e6 100644 --- a/truffle/src/com.oracle.truffle.polyglot/src/com/oracle/truffle/polyglot/PolyglotMap.java +++ b/truffle/src/com.oracle.truffle.polyglot/src/com/oracle/truffle/polyglot/PolyglotMap.java @@ -46,6 +46,7 @@ import java.lang.reflect.Type; import java.util.AbstractMap; import java.util.AbstractSet; +import java.util.Arrays; import java.util.Collections; import java.util.Iterator; import java.util.List; @@ -960,5 +961,23 @@ public Type getRawType() { public Type getOwnerType() { return null; } + + @Override + public int hashCode() { + int res = rawType.hashCode(); + res = res * 31 + Arrays.hashCode(typeParameters); + return res; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } else if (obj == null || getClass() != obj.getClass()) { + return false; + } + ParameterizedTypeImpl other = (ParameterizedTypeImpl) obj; + return rawType == other.rawType && Arrays.equals(typeParameters, typeParameters); + } } } diff --git a/truffle/src/com.oracle.truffle.polyglot/src/com/oracle/truffle/polyglot/PolyglotStackFramesRetriever.java b/truffle/src/com.oracle.truffle.polyglot/src/com/oracle/truffle/polyglot/PolyglotStackFramesRetriever.java index 1e53a2f71675..2908a310ccb9 100644 --- a/truffle/src/com.oracle.truffle.polyglot/src/com/oracle/truffle/polyglot/PolyglotStackFramesRetriever.java +++ b/truffle/src/com.oracle.truffle.polyglot/src/com/oracle/truffle/polyglot/PolyglotStackFramesRetriever.java @@ -84,10 +84,12 @@ public Object visitFrame(FrameInstance frameInstance) { TruffleSafepoint.setBlockedThreadInterruptible(context.uncachedLocation, new TruffleSafepoint.Interruptible>() { @Override public void apply(Future arg) throws InterruptedException { - try { - arg.get(); - } catch (ExecutionException e) { - throw CompilerDirectives.shouldNotReachHere(e); + if (!context.state.isClosed()) { + try { + arg.get(); + } catch (ExecutionException e) { + throw CompilerDirectives.shouldNotReachHere(e); + } } } }, future); diff --git a/truffle/src/com.oracle.truffle.polyglot/src/com/oracle/truffle/polyglot/PolyglotValueDispatch.java b/truffle/src/com.oracle.truffle.polyglot/src/com/oracle/truffle/polyglot/PolyglotValueDispatch.java index e65309bdb36d..99a67afc9d76 100644 --- a/truffle/src/com.oracle.truffle.polyglot/src/com/oracle/truffle/polyglot/PolyglotValueDispatch.java +++ b/truffle/src/com.oracle.truffle.polyglot/src/com/oracle/truffle/polyglot/PolyglotValueDispatch.java @@ -1964,7 +1964,7 @@ public final String toString() { * Host value implementation used when a Value needs to be created but not context is available. * If a context is available the normal interop value implementation is used. */ - static final class HostValue extends PolyglotValueDispatch { + static class HostValue extends PolyglotValueDispatch { HostValue(PolyglotImpl polyglot) { super(polyglot, null); @@ -2015,6 +2015,166 @@ T asImpl(Object languageContext, Object receiver, Class targetType) { } + /** + * Must be kept in sync with the HostObject and the HostToTypeNode implementation. + */ + static final class BigIntegerHostValue extends HostValue { + BigIntegerHostValue(PolyglotImpl polyglot) { + super(polyglot); + } + + @Override + public boolean isNumber(Object context, Object receiver) { + assert asHostObject(context, receiver) instanceof BigInteger; + return true; + } + + @Override + public boolean fitsInByte(Object context, Object receiver) { + assert asHostObject(context, receiver) instanceof BigInteger; + return ((BigInteger) asHostObject(context, receiver)).bitLength() < Byte.SIZE; + } + + @Override + public boolean fitsInShort(Object context, Object receiver) { + assert asHostObject(context, receiver) instanceof BigInteger; + return ((BigInteger) asHostObject(context, receiver)).bitLength() < Short.SIZE; + } + + @Override + public boolean fitsInInt(Object context, Object receiver) { + assert asHostObject(context, receiver) instanceof BigInteger; + return ((BigInteger) asHostObject(context, receiver)).bitLength() < Integer.SIZE; + } + + @Override + public boolean fitsInLong(Object context, Object receiver) { + assert asHostObject(context, receiver) instanceof BigInteger; + return ((BigInteger) asHostObject(context, receiver)).bitLength() < Long.SIZE; + } + + @Override + public boolean fitsInBigInteger(Object context, Object receiver) { + assert asHostObject(context, receiver) instanceof BigInteger; + return true; + } + + @Override + public boolean fitsInFloat(Object context, Object receiver) { + assert asHostObject(context, receiver) instanceof BigInteger; + return EngineAccessor.HOST.bigIntegerFitsInFloat((BigInteger) asHostObject(context, receiver)); + } + + @Override + public boolean fitsInDouble(Object context, Object receiver) { + assert asHostObject(context, receiver) instanceof BigInteger; + return EngineAccessor.HOST.bigIntegerFitsInDouble((BigInteger) asHostObject(context, receiver)); + } + + @Override + public byte asByte(Object languageContext, Object receiver) { + assert asHostObject(languageContext, receiver) instanceof BigInteger; + try { + return ((BigInteger) asHostObject(languageContext, receiver)).byteValueExact(); + } catch (ArithmeticException e) { + // throws an unsupported error. + return super.asByte(languageContext, receiver); + } + } + + @Override + public short asShort(Object languageContext, Object receiver) { + assert asHostObject(languageContext, receiver) instanceof BigInteger; + try { + return ((BigInteger) asHostObject(languageContext, receiver)).shortValueExact(); + } catch (ArithmeticException e) { + // throws an unsupported error. + return super.asShort(languageContext, receiver); + } + } + + @Override + public int asInt(Object languageContext, Object receiver) { + assert asHostObject(languageContext, receiver) instanceof BigInteger; + try { + return ((BigInteger) asHostObject(languageContext, receiver)).intValueExact(); + } catch (ArithmeticException e) { + // throws an unsupported error. + return super.asInt(languageContext, receiver); + } + } + + @Override + public long asLong(Object languageContext, Object receiver) { + assert asHostObject(languageContext, receiver) instanceof BigInteger; + try { + return ((BigInteger) asHostObject(languageContext, receiver)).longValueExact(); + } catch (ArithmeticException e) { + // throws an unsupported error. + return super.asLong(languageContext, receiver); + } + } + + @Override + public BigInteger asBigInteger(Object languageContext, Object receiver) { + assert asHostObject(languageContext, receiver) instanceof BigInteger; + return ((BigInteger) asHostObject(languageContext, receiver)); + } + + @Override + public float asFloat(Object languageContext, Object receiver) { + assert asHostObject(languageContext, receiver) instanceof BigInteger; + if (fitsInFloat(languageContext, receiver)) { + return ((BigInteger) asHostObject(languageContext, receiver)).floatValue(); + } else { + // throws an unsupported error. + return super.asFloat(languageContext, receiver); + } + } + + @Override + public double asDouble(Object languageContext, Object receiver) { + assert asHostObject(languageContext, receiver) instanceof BigInteger; + if (fitsInFloat(languageContext, receiver)) { + return ((BigInteger) asHostObject(languageContext, receiver)).doubleValue(); + } else { + // throws an unsupported error. + return super.asDouble(languageContext, receiver); + } + } + + @SuppressWarnings("unchecked") + @Override + T asImpl(Object languageContext, Object receiver, Class targetType) { + assert asHostObject(languageContext, receiver) instanceof BigInteger; + + if (targetType == byte.class || targetType == Byte.class) { + return (T) (Byte) asByte(languageContext, receiver); + } else if (targetType == short.class || targetType == Short.class) { + return (T) (Short) asShort(languageContext, receiver); + } else if (targetType == int.class || targetType == Integer.class) { + return (T) (Integer) asInt(languageContext, receiver); + } else if (targetType == long.class || targetType == Long.class) { + return (T) (Long) asLong(languageContext, receiver); + } else if (targetType == float.class || targetType == Float.class) { + return (T) (Float) asFloat(languageContext, receiver); + } else if (targetType == double.class || targetType == Double.class) { + return (T) (Double) asDouble(languageContext, receiver); + } else if (targetType == BigInteger.class || targetType == Number.class) { + return (T) asBigInteger(languageContext, receiver); + } else if (targetType == char.class || targetType == Character.class) { + if (fitsInInt(languageContext, receiver)) { + int v = asInt(languageContext, receiver); + if (v >= 0 && v < 65536) { + return (T) (Character) (char) v; + } + } + } + + return super.asImpl(languageContext, receiver, targetType); + } + } + @SuppressWarnings("unused") static final class InteropValue extends PolyglotValueDispatch { diff --git a/vm/mx.vm/suite.py b/vm/mx.vm/suite.py index 89b07db2e367..f0506ca88f65 100644 --- a/vm/mx.vm/suite.py +++ b/vm/mx.vm/suite.py @@ -1,8 +1,8 @@ suite = { "name": "vm", - "version" : "23.0.2.0", + "version" : "23.0.2.1", "mxversion": "6.17.0", - "release" : True, + "release" : False, "groupId" : "org.graalvm", "url" : "http://www.graalvm.org/", @@ -39,7 +39,7 @@ "name": "graal-nodejs", "subdir": True, "dynamic": True, - "version": "60f4a03e64fd70f9941e6877a4b4bd8ce3368cdf", + "version": "e2b74f45ce0eb8ae3f9bd24b068f128e91d607e8", "urls" : [ {"url" : "https://github.com/graalvm/graaljs.git", "kind" : "git"}, {"url": "https://curio.ssw.jku.at/nexus/content/repositories/snapshots", "kind": "binary"}, @@ -49,7 +49,7 @@ "name": "graal-js", "subdir": True, "dynamic": True, - "version": "60f4a03e64fd70f9941e6877a4b4bd8ce3368cdf", + "version": "e2b74f45ce0eb8ae3f9bd24b068f128e91d607e8", "urls": [ {"url": "https://github.com/graalvm/graaljs.git", "kind" : "git"}, {"url": "https://curio.ssw.jku.at/nexus/content/repositories/snapshots", "kind": "binary"}, @@ -75,7 +75,7 @@ }, { "name": "graalpython", - "version": "99f6a03d18b16efdadcfde852d42ff774073e25a", + "version": "5ee94e7b30384a04d69c3c861aeb736fc095c195", "dynamic": True, "urls": [ {"url": "https://github.com/graalvm/graalpython.git", "kind": "git"}, diff --git a/vm/src/org.graalvm.component.installer/src/org/graalvm/component/installer/gds/rest/Bundle.properties b/vm/src/org.graalvm.component.installer/src/org/graalvm/component/installer/gds/rest/Bundle.properties index 4314404e4ddd..d10be14ded37 100644 --- a/vm/src/org.graalvm.component.installer/src/org/graalvm/component/installer/gds/rest/Bundle.properties +++ b/vm/src/org.graalvm.component.installer/src/org/graalvm/component/installer/gds/rest/Bundle.properties @@ -8,13 +8,8 @@ ERR_MalformedArtifactUrl=Malformed artifact download URL: {0} # {0} - the storage file # {1} - the error message -<<<<<<< HEAD -ERR_CouldNotLoadToken=Could not retrieve a download token from {0}: {1}. -ERR_CouldNotRemoveToken=Could not remove a download token from your configuration. -======= ERR_CouldNotLoadToken=Could not retrieve download token from {0}: {1}. ERR_CouldNotRemoveToken=Could not remove download token from your configuration. ->>>>>>> d72d4507f72 ([GR-43446] Revoke GDS token feature.) # {0} - the URL # {1} - the error message ERR_CouldNotLoadGDS=Could not retrieve information from {0}: {1}. diff --git a/wasm/mx.wasm/suite.py b/wasm/mx.wasm/suite.py index eb0efe778c2b..a720c6b3aeb3 100644 --- a/wasm/mx.wasm/suite.py +++ b/wasm/mx.wasm/suite.py @@ -42,7 +42,7 @@ "mxversion": "6.17.0", "name" : "wasm", "groupId" : "org.graalvm.wasm", - "version" : "23.0.2.0", + "version" : "23.0.2.1", "versionConflictResolution" : "latest", "url" : "http://graalvm.org/", "developer" : {