diff --git a/ci/ci_common/bench-common.libsonnet b/ci/ci_common/bench-common.libsonnet index f783783b03ff..36b24b8dd114 100644 --- a/ci/ci_common/bench-common.libsonnet +++ b/ci/ci_common/bench-common.libsonnet @@ -31,6 +31,17 @@ else true }, + # max number of threads to use for benchmarking in general + # the goal being to limit parallelism on very large servers which may not be respresentative of real-world scenarios + bench_max_threads:: { + restrict_threads:: 36 + }, + + bench_no_thread_cap:: { + restrict_threads:: null, + should_use_hwloc:: false + }, + bench_hw:: { _bench_machine:: { targets+: ["bench"], @@ -40,15 +51,16 @@ numa_nodes:: [], is_numa:: std.length(self.numa_nodes) > 0, num_threads:: error "num_threads must bet set!", + hyperthreading:: true, threads_per_node:: if self.is_numa then self.num_threads / std.length(self.numa_nodes) else self.num_threads, }, - x52:: common.linux_amd64 + self._bench_machine + { - machine_name:: "x52", - capabilities+: ["tmpfs25g"], + e3:: common.linux_amd64 + self._bench_machine + { + machine_name:: "e3", + capabilities: ["e3", "tmpfs25g", "linux", "amd64"], numa_nodes:: [0, 1], - default_numa_node:: 0, - num_threads:: 72 + default_numa_node:: 1, + num_threads:: 256 }, e4_8_64:: common.linux_amd64 + self._bench_machine + { machine_name:: "e4_8_64", @@ -67,31 +79,29 @@ xgene3:: common.linux_aarch64 + self._bench_machine + { machine_name:: "xgene3", capabilities+: [], - num_threads:: 32 + num_threads:: 32, + hyperthreading:: false }, a12c:: common.linux_aarch64 + self._bench_machine + { machine_name:: "a12c", capabilities+: ["tmpfs25g"], numa_nodes:: [0, 1], default_numa_node:: 0, - num_threads:: 160 + num_threads:: 160, + hyperthreading:: false } }, - hwlocIfNuma(numa, cmd, node=0):: - if numa then + hwloc_cmd(cmd, num_threads, node, hyperthreading, max_threads_per_node):: + if num_threads == null then ["hwloc-bind", "--cpubind", "node:"+node, "--membind", "node:"+node, "--"] + cmd else - cmd, - - parallelHwloc(cmd_node0, cmd_node1):: - // Returns a list of commands that will run cmd_nod0 on NUMA node 0 - // concurrently with cmd_node1 on NUMA node 1 and then wait for both to complete. - [ - $.hwlocIfNuma(true, cmd_node0, node=0) + ["&"], - $.hwlocIfNuma(true, cmd_node1, node=1) + ["&"], - ["wait"] - ], + local threads = if num_threads != null then num_threads else max_threads_per_node; + assert if hyperthreading then threads % 2 == 0 else true: "It is required to bind to an even number of threads on hyperthreaded machines. Got requested "+threads+" threads"; + assert threads <= max_threads_per_node: "Benchmarking must run on a single NUMA node for stability reasons. Got requested "+threads+" threads but the machine has only "+max_threads_per_node+" threads per node"; local cores = if hyperthreading then "0-"+((threads/2)-1)+".pu:0-1" else "0-"+(threads-1)+".pu:0"; + local cpu_bind = if hyperthreading then "node:"+node+".core:"+cores else "node:"+node+".core:"+cores+".pu:0"; + ["hwloc-bind", "--cpubind", cpu_bind, "--membind", "node:"+node, "--"] + cmd + , // building block used to generate fork builds many_forks_benchmarking:: common.build_base + { diff --git a/common.json b/common.json index 3f46910e350c..de8bf083e49b 100644 --- a/common.json +++ b/common.json @@ -4,7 +4,7 @@ "Jsonnet files should not include this file directly but use ci/common.jsonnet instead." ], - "mx_version": "7.4.1", + "mx_version": "7.4.1.1", "COMMENT.jdks": "When adding or removing JDKs keep in sync with JDKs in ci/common.jsonnet", "jdks": { @@ -44,13 +44,13 @@ "labsjdk-ee-21Debug": {"name": "labsjdk", "version": "ee-21.0.1+11-jvmci-23.1-b26-debug", "platformspecific": true }, "labsjdk-ee-21-llvm": {"name": "labsjdk", "version": "ee-21.0.1+11-jvmci-23.1-b26-sulong", "platformspecific": true }, - "oraclejdk-latest": {"name": "jpg-jdk", "version": "22", "build_id": "jdk-22.0.1+8", "platformspecific": true, "extrabundles": ["static-libs"]}, - "labsjdk-ce-latest": {"name": "labsjdk", "version": "ce-22.0.1+8-jvmci-b01", "platformspecific": true }, - "labsjdk-ce-latestDebug": {"name": "labsjdk", "version": "ce-22.0.1+8-jvmci-b01-debug", "platformspecific": true }, - "labsjdk-ce-latest-llvm": {"name": "labsjdk", "version": "ce-22.0.1+8-jvmci-b01-sulong", "platformspecific": true }, - "labsjdk-ee-latest": {"name": "labsjdk", "version": "ee-22.0.1+8-jvmci-b01", "platformspecific": true }, - "labsjdk-ee-latestDebug": {"name": "labsjdk", "version": "ee-22.0.1+8-jvmci-b01-debug", "platformspecific": true }, - "labsjdk-ee-latest-llvm": {"name": "labsjdk", "version": "ee-22.0.1+8-jvmci-b01-sulong", "platformspecific": true } + "oraclejdk-latest": {"name": "jpg-jdk", "version": "22", "build_id": "jdk-22.0.2+9", "platformspecific": true, "extrabundles": ["static-libs"]}, + "labsjdk-ce-latest": {"name": "labsjdk", "version": "ce-22.0.2+9-jvmci-b01", "platformspecific": true }, + "labsjdk-ce-latestDebug": {"name": "labsjdk", "version": "ce-22.0.2+9-jvmci-b01-debug", "platformspecific": true }, + "labsjdk-ce-latest-llvm": {"name": "labsjdk", "version": "ce-22.0.2+9-jvmci-b01-sulong", "platformspecific": true }, + "labsjdk-ee-latest": {"name": "labsjdk", "version": "ee-22.0.2+9-jvmci-b01", "platformspecific": true }, + "labsjdk-ee-latestDebug": {"name": "labsjdk", "version": "ee-22.0.2+9-jvmci-b01-debug", "platformspecific": true }, + "labsjdk-ee-latest-llvm": {"name": "labsjdk", "version": "ee-22.0.2+9-jvmci-b01-sulong", "platformspecific": true } }, "eclipse": { diff --git a/compiler/ci/ci_common/benchmark-builders.jsonnet b/compiler/ci/ci_common/benchmark-builders.jsonnet index 7a22ad0a2d7c..92ed044c76fa 100644 --- a/compiler/ci/ci_common/benchmark-builders.jsonnet +++ b/compiler/ci/ci_common/benchmark-builders.jsonnet @@ -11,41 +11,35 @@ local main_builds = std.flattenArrays([ [ - c.daily + c.opt_post_merge + hw.x52 + jdk + cc.libgraal + bench.dacapo + PR_bench_libgraal, - c.weekly + hw.x52 + jdk + cc.libgraal + bench.dacapo_size_variants, - c.daily + c.opt_post_merge + hw.x52 + jdk + cc.libgraal + bench.scala_dacapo + PR_bench_libgraal, - c.weekly + hw.x52 + jdk + cc.libgraal + bench.scala_dacapo_size_variants, - c.daily + c.opt_post_merge + hw.x52 + jdk + cc.libgraal + bench.renaissance + PR_bench_libgraal, - c.daily + c.opt_post_merge + hw.x52 + jdk + cc.libgraal + bench.specjvm2008 + PR_bench_libgraal, - c.weekly + hw.x52 + jdk + cc.libgraal + bench.specjbb2015, - c.daily + c.opt_post_merge + hw.x52 + jdk + cc.libgraal + bench.awfy + PR_bench_libgraal, - c.daily + hw.x52 + jdk + cc.libgraal + bench.microservice_benchmarks, - c.daily + hw.x52 + jdk + cc.libgraal + bench.micros_graal_whitebox, - c.daily + hw.x52 + jdk + cc.libgraal + bench.micros_graal_dist, - c.daily + hw.x52 + jdk + cc.libgraal + bench.micros_misc_graal_dist, - c.daily + hw.x52 + jdk + cc.libgraal + bench.micros_shootout_graal_dist, - c.daily + hw.e4_8_64 + jdk + cc.libgraal + bench.awfy + {job_prefix:: "bench-e4vm-compiler"}, - c.daily + hw.e4_8_64 + jdk + cc.libgraal + bench.dacapo + {job_prefix:: "bench-e4vm-compiler"}, - c.daily + hw.e4_8_64 + jdk + cc.libgraal + bench.scala_dacapo + {job_prefix:: "bench-e4vm-compiler"}, - c.daily + hw.e4_8_64 + jdk + cc.libgraal + bench.renaissance + {job_prefix:: "bench-e4vm-compiler"}, - c.daily + hw.e4_8_64 + jdk + cc.libgraal + bench.specjvm2008 + {job_prefix:: "bench-e4vm-compiler"}, - c.daily + hw.e4_8_64 + jdk + cc.libgraal + bench.microservice_benchmarks + {job_prefix:: "bench-e4vm-compiler"}, + c.daily + c.opt_post_merge + hw.e3 + jdk + cc.libgraal + bench.dacapo + PR_bench_libgraal, + c.daily + c.opt_post_merge + hw.e3 + jdk + cc.libgraal + bench.scala_dacapo + PR_bench_libgraal, + c.daily + c.opt_post_merge + hw.e3 + jdk + cc.libgraal + bench.renaissance + PR_bench_libgraal, + c.daily + c.opt_post_merge + hw.e3 + jdk + cc.libgraal + bench.specjvm2008 + PR_bench_libgraal, + c.on_demand + hw.e3 + jdk + cc.libgraal + bench.dacapo_size_variants, + c.on_demand + hw.e3 + jdk + cc.libgraal + bench.scala_dacapo_size_variants, + c.monthly + hw.e3 + jdk + cc.libgraal + bench.specjbb2015, + c.daily + c.opt_post_merge + hw.e3 + jdk + cc.libgraal + bench.awfy + PR_bench_libgraal, + c.daily + hw.e3 + jdk + cc.libgraal + bench.microservice_benchmarks, + c.weekly + hw.e3 + jdk + cc.libgraal + bench.micros_graal_whitebox, + c.weekly + hw.e3 + jdk + cc.libgraal + bench.micros_graal_dist, + c.weekly + hw.e3 + jdk + cc.libgraal + bench.micros_misc_graal_dist, + c.weekly + hw.e3 + jdk + cc.libgraal + bench.micros_shootout_graal_dist, ] for jdk in cc.product_jdks ]), local profiling_builds = std.flattenArrays([ [ - c.weekly + hw.x52 + jdk + cc.libgraal + suite + cc.enable_profiling + { job_prefix:: "bench-compiler-profiling" }, - c.weekly + hw.x52 + jdk + cc.libgraal + suite + cc.footprint_tracking + { job_prefix:: "bench-compiler-footprint" } + c.monthly + hw.e3 + jdk + cc.libgraal + suite + cc.enable_profiling + { job_prefix:: "bench-compiler-profiling" }, + c.monthly + hw.e3 + jdk + cc.libgraal + suite + cc.footprint_tracking + { job_prefix:: "bench-compiler-footprint" } ] for jdk in cc.product_jdks for suite in bench.groups.main_suites ]), local weekly_amd64_forks_builds = std.flattenArrays([ - bc.generate_fork_builds(c.weekly + hw.x52 + jdk + cc.libgraal + suite, subdir='compiler') + - bc.generate_fork_builds(c.monthly + hw.x52 + jdk + cc.jargraal + suite, subdir='compiler') + bc.generate_fork_builds(c.weekly + hw.e3 + jdk + cc.libgraal + suite, subdir='compiler') + + bc.generate_fork_builds(c.monthly + hw.e3 + jdk + cc.jargraal + suite, subdir='compiler') for jdk in cc.product_jdks for suite in bench.groups.weekly_forks_suites ]), @@ -70,7 +64,7 @@ ], local zgc_builds = [ - c.weekly + hw.x52 + jdk + cc.libgraal + cc.zgc_mode + suite, + c.weekly + hw.e3 + jdk + cc.libgraal + cc.zgc_mode + suite, for jdk in cc.product_jdks for suite in bench.groups.main_suites + [bench.specjbb2015] ], @@ -83,13 +77,13 @@ ], local no_tiered_builds = [ - c.weekly + hw.x52 + jdk + cc.libgraal + cc.no_tiered_comp + suite, + c.monthly + hw.e3 + jdk + cc.libgraal + cc.no_tiered_comp + suite, for jdk in cc.product_jdks for suite in bench.groups.main_suites ], local no_profile_info_builds = [ - c.weekly + hw.x52 + jdk + cc.libgraal + cc.no_profile_info + suite, + c.monthly + hw.e3 + jdk + cc.libgraal + cc.no_profile_info + suite, for jdk in cc.product_jdks for suite in bench.groups.main_suites ], diff --git a/compiler/ci/ci_common/benchmark-suites.libsonnet b/compiler/ci/ci_common/benchmark-suites.libsonnet index a364573cc859..2dbe4b7eed2f 100644 --- a/compiler/ci/ci_common/benchmark-suites.libsonnet +++ b/compiler/ci/ci_common/benchmark-suites.libsonnet @@ -24,7 +24,7 @@ // suite definitions // ***************** - awfy: cc.compiler_benchmark + c.heap.small + { + awfy: cc.compiler_benchmark + c.heap.small + bc.bench_max_threads + { suite:: "awfy", run+: [ self.benchmark_cmd + ["awfy:*", "--"] + self.extra_vm_args @@ -36,7 +36,7 @@ max_jdk_version:: null }, - dacapo: cc.compiler_benchmark + c.heap.default + { + dacapo: cc.compiler_benchmark + c.heap.default + bc.bench_max_threads + { suite:: "dacapo", run+: [ self.benchmark_cmd + ["dacapo:*", "--"] + self.extra_vm_args @@ -48,7 +48,7 @@ max_jdk_version:: null }, - dacapo_size_variants: cc.compiler_benchmark + c.heap.default + { + dacapo_size_variants: cc.compiler_benchmark + c.heap.default + bc.bench_max_threads + { suite:: "dacapo-size-variants", run+: [ self.benchmark_cmd + ["dacapo-small:*", "--"] + self.extra_vm_args, @@ -64,7 +64,7 @@ max_jdk_version:: null }, - scala_dacapo: cc.compiler_benchmark + c.heap.default + { + scala_dacapo: cc.compiler_benchmark + c.heap.default + bc.bench_max_threads + { suite:: "scala-dacapo", run+: [ self.benchmark_cmd + ["scala-dacapo:*", "--"] + self.extra_vm_args @@ -76,7 +76,7 @@ max_jdk_version:: null }, - scala_dacapo_size_variants: cc.compiler_benchmark + c.heap.default + { + scala_dacapo_size_variants: cc.compiler_benchmark + c.heap.default + bc.bench_max_threads + { suite:: "scala-dacapo-size-variants", run+: [ self.benchmark_cmd + ["scala-dacapo-tiny:*", "--"] + self.extra_vm_args, @@ -97,7 +97,7 @@ max_jdk_version:: null }, - renaissance_template(suite_version=null, suite_name="renaissance", max_jdk_version=null):: cc.compiler_benchmark + c.heap.default + { + renaissance_template(suite_version=null, suite_name="renaissance", max_jdk_version=null):: cc.compiler_benchmark + c.heap.default + bc.bench_max_threads + { suite:: suite_name, local suite_version_args = if suite_version != null then ["--bench-suite-version=" + suite_version] else [], run+: [ @@ -110,12 +110,9 @@ max_jdk_version:: max_jdk_version }, - renaissance: self.renaissance_template() + { - # [JDK-8303076] [GR-44499] requires extra stack size for C1 - extra_vm_args+:: if self.platform == "c1" then ["-Xss1090K"] else [] - }, + renaissance: self.renaissance_template(), - specjbb2015: cc.compiler_benchmark + c.heap.large_with_large_young_gen + { + specjbb2015: cc.compiler_benchmark + c.heap.large_with_large_young_gen + bc.bench_max_threads + { suite:: "specjbb2015", downloads+: { "SPECJBB2015": { name: "specjbb2015", version: "1.03" } @@ -130,7 +127,7 @@ max_jdk_version:: null }, - specjvm2008: cc.compiler_benchmark + c.heap.default + { + specjvm2008: cc.compiler_benchmark + c.heap.default + bc.bench_max_threads + { suite:: "specjvm2008", downloads+: { "SPECJVM2008": { name: "specjvm2008", version: "1.01" } @@ -146,7 +143,7 @@ }, // Microservice benchmarks - microservice_benchmarks: cc.compiler_benchmark + { + microservice_benchmarks: cc.compiler_benchmark + bc.bench_no_thread_cap + { # no thread cap here since hwloc is handled at the mx level for microservices suite:: "microservices", packages+: { "pip:psutil": "==5.8.0" @@ -159,28 +156,12 @@ local hwlocBind_16C_32T = ["--hwloc-bind=--cpubind node:0.core:0-15.pu:0-1 --membind node:0"], run+: [ # shopcart-wrk - self.benchmark_cmd + ["shopcart-wrk:mixed-tiny"] + hwlocBind_1C_1T + ["--"] + self.extra_vm_args + ["-Xms32m", "-Xmx112m", "-XX:ActiveProcessorCount=1", "-XX:MaxDirectMemorySize=256m"], - bench_upload, - self.benchmark_cmd + ["shopcart-wrk:mixed-small"] + hwlocBind_2C_2T + ["--"] + self.extra_vm_args + ["-Xms64m", "-Xmx224m", "-XX:ActiveProcessorCount=2", "-XX:MaxDirectMemorySize=512m"], - bench_upload, - self.benchmark_cmd + ["shopcart-wrk:mixed-medium"] + hwlocBind_4C_4T + ["--"] + self.extra_vm_args + ["-Xms128m", "-Xmx512m", "-XX:ActiveProcessorCount=4", "-XX:MaxDirectMemorySize=1024m"], - bench_upload, self.benchmark_cmd + ["shopcart-wrk:mixed-large"] + hwlocBind_16C_16T + ["--"] + self.extra_vm_args + ["-Xms512m", "-Xmx3072m", "-XX:ActiveProcessorCount=16", "-XX:MaxDirectMemorySize=4096m"], bench_upload, - self.benchmark_cmd + ["shopcart-wrk:mixed-huge"] + hwlocBind_16C_32T + ["--"] + self.extra_vm_args + ["-Xms1024m", "-Xmx8192m", "-XX:ActiveProcessorCount=32", "-XX:MaxDirectMemorySize=8192m"], - bench_upload, # petclinic-wrk - self.benchmark_cmd + ["petclinic-wrk:mixed-tiny"] + hwlocBind_1C_1T + ["--"] + self.extra_vm_args + ["-Xms32m", "-Xmx100m", "-XX:ActiveProcessorCount=1"], - bench_upload, - self.benchmark_cmd + ["petclinic-wrk:mixed-small"] + hwlocBind_2C_2T + ["--"] + self.extra_vm_args + ["-Xms40m", "-Xmx144m", "-XX:ActiveProcessorCount=2"], - bench_upload, - self.benchmark_cmd + ["petclinic-wrk:mixed-medium"] + hwlocBind_4C_4T + ["--"] + self.extra_vm_args + ["-Xms80m", "-Xmx256m", "-XX:ActiveProcessorCount=4"], - bench_upload, self.benchmark_cmd + ["petclinic-wrk:mixed-large"] + hwlocBind_16C_16T + ["--"] + self.extra_vm_args + ["-Xms128m", "-Xmx512m", "-XX:ActiveProcessorCount=16"], bench_upload, - self.benchmark_cmd + ["petclinic-wrk:mixed-huge"] + hwlocBind_16C_32T + ["--"] + self.extra_vm_args + ["-Xms640m", "-Xmx3072m", "-XX:ActiveProcessorCount=32"], - bench_upload, # helloworld-wrk self.benchmark_cmd + ["micronaut-helloworld-wrk:helloworld"] + hwlocBind_1C_1T + ["--"] + self.extra_vm_args + ["-Xms8m", "-Xmx64m", "-XX:ActiveProcessorCount=1", "-XX:MaxDirectMemorySize=256m"], @@ -188,13 +169,13 @@ self.benchmark_cmd + ["spring-helloworld-wrk:helloworld"] + hwlocBind_1C_1T + ["--"] + self.extra_vm_args + ["-Xms8m", "-Xmx64m", "-XX:ActiveProcessorCount=1", "-XX:MaxDirectMemorySize=256m"], bench_upload ], - timelimit: "7:00:00", + timelimit: "4:00:00", min_jdk_version:: 11, max_jdk_version:: null }, // JMH microbenchmarks - micros_graal_whitebox: cc.compiler_benchmark + c.heap.default + { + micros_graal_whitebox: cc.compiler_benchmark + c.heap.default + bc.bench_max_threads + { suite:: "micros-graal-whitebox", run+: [ self.benchmark_cmd + ["jmh-whitebox:*", "--"] + self.extra_vm_args + ["--", "jdk.graal.compiler"] @@ -204,7 +185,7 @@ max_jdk_version:: null }, - micros_graal_dist: cc.compiler_benchmark + c.heap.default + { + micros_graal_dist: cc.compiler_benchmark + c.heap.default + bc.bench_max_threads + { suite:: "micros-graal-dist", run+: [ self.benchmark_cmd + ["jmh-dist:GRAAL_COMPILER_MICRO_BENCHMARKS", "--"] + self.extra_vm_args @@ -214,7 +195,7 @@ max_jdk_version:: null }, - micros_misc_graal_dist: cc.compiler_benchmark + c.heap.default + { + micros_misc_graal_dist: cc.compiler_benchmark + c.heap.default + bc.bench_max_threads + { suite:: "micros-misc-graal-dist", run+: [ self.benchmark_cmd + ["jmh-dist:GRAAL_BENCH_MISC", "--"] + self.extra_vm_args @@ -224,7 +205,7 @@ max_jdk_version:: null }, - micros_shootout_graal_dist: cc.compiler_benchmark + c.heap.default { + micros_shootout_graal_dist: cc.compiler_benchmark + c.heap.default + bc.bench_max_threads + { suite:: "micros-shootout-graal-dist", run+: [ self.benchmark_cmd + ["jmh-dist:GRAAL_BENCH_SHOOTOUT", "--"] + self.extra_vm_args diff --git a/compiler/ci/ci_common/compiler-common.libsonnet b/compiler/ci/ci_common/compiler-common.libsonnet index f54a3f45559b..f6cd28133f63 100644 --- a/compiler/ci/ci_common/compiler-common.libsonnet +++ b/compiler/ci/ci_common/compiler-common.libsonnet @@ -71,7 +71,8 @@ "${BENCH_RESULTS_FILE_PATH}", "--machine-name=${MACHINE_NAME}"] + (if std.objectHasAll(self.environment, 'MX_TRACKER') then ["--tracker=" + self.environment['MX_TRACKER']] else ["--tracker=rss"]), - benchmark_cmd:: bench_common.hwlocIfNuma(self.should_use_hwloc, self.plain_benchmark_cmd, node=self.default_numa_node), + restrict_threads:: null, # can be overridden to restrict the benchmark to the given number of threads. If null, will use one full NUMA node + benchmark_cmd:: if self.should_use_hwloc then bench_common.hwloc_cmd(self.plain_benchmark_cmd, self.restrict_threads, self.default_numa_node, self.hyperthreading, self.threads_per_node) else self.plain_benchmark_cmd, min_heap_size:: if std.objectHasAll(self.environment, 'XMS') then ["-Xms${XMS}"] else [], max_heap_size:: if std.objectHasAll(self.environment, 'XMX') then ["-Xmx${XMX}"] else [], extra_vm_args:: diff --git a/compiler/ci/ci_includes/baseline-benchmarks.jsonnet b/compiler/ci/ci_includes/baseline-benchmarks.jsonnet index f558cd1ae1b5..2b77034fb408 100644 --- a/compiler/ci/ci_includes/baseline-benchmarks.jsonnet +++ b/compiler/ci/ci_includes/baseline-benchmarks.jsonnet @@ -7,7 +7,7 @@ local hw = bc.bench_hw, local hotspot_amd64_builds = [ - c.weekly + hw.x52 + jdk + cc.c2 + suite + c.weekly + hw.e3 + jdk + cc.c2 + suite for jdk in cc.product_jdks for suite in bench.groups.all_suites ], @@ -20,17 +20,17 @@ local hotspot_profiling_builds = std.flattenArrays([ [ - c.weekly + hw.x52 + jdk + cc.c2 + suite + cc.enable_profiling + { job_prefix:: "bench-compiler-profiling" }, - c.weekly + hw.a12c + jdk + cc.c2 + suite + cc.enable_profiling + { job_prefix:: "bench-compiler-profiling" }, - c.weekly + hw.x52 + jdk + cc.c2 + suite + cc.footprint_tracking + { job_prefix:: "bench-compiler-footprint" }, - c.weekly + hw.a12c + jdk + cc.c2 + suite + cc.footprint_tracking + { job_prefix:: "bench-compiler-footprint" } + c.monthly + hw.e3 + jdk + cc.c2 + suite + cc.enable_profiling + { job_prefix:: "bench-compiler-profiling" }, + c.monthly + hw.a12c + jdk + cc.c2 + suite + cc.enable_profiling + { job_prefix:: "bench-compiler-profiling" }, + c.monthly + hw.e3 + jdk + cc.c2 + suite + cc.footprint_tracking + { job_prefix:: "bench-compiler-footprint" }, + c.monthly + hw.a12c + jdk + cc.c2 + suite + cc.footprint_tracking + { job_prefix:: "bench-compiler-footprint" } ] for jdk in cc.product_jdks for suite in bench.groups.main_suites ]), local weekly_forks_amd64_builds = std.flattenArrays([ - bc.generate_fork_builds(c.weekly + hw.x52 + jdk + cc.c2 + suite) + bc.generate_fork_builds(c.weekly + hw.e3 + jdk + cc.c2 + suite) for jdk in cc.jdks_of_interest for suite in bench.groups.weekly_forks_suites ]), @@ -41,23 +41,16 @@ for suite in bench.groups.weekly_forks_suites ]), - local daily_economy_builds = [ - c.daily + hw.x52 + jdk + cc.libgraal + cc.economy_mode + suite + local economy_builds = [ + c.weekly + hw.e3 + jdk + cc.libgraal + cc.economy_mode + suite for jdk in cc.product_jdks for suite in bench.groups.main_suites ], - - local weekly_economy_builds = [ - c.weekly + hw.x52 + jdk + cc.libgraal + cc.economy_mode + suite - for jdk in cc.product_jdks - for suite in bench.groups.all_but_main_suites - ], - local no_tiered_builds = std.flattenArrays([ [ - c.weekly + hw.x52 + jdk + cc.c1 + suite, - c.weekly + hw.x52 + jdk + cc.c2 + cc.no_tiered_comp + suite, - c.weekly + hw.x52 + jdk + cc.libgraal + cc.economy_mode + cc.no_tiered_comp + suite + c.monthly + hw.e3 + jdk + cc.c1 + suite, + c.monthly + hw.e3 + jdk + cc.c2 + cc.no_tiered_comp + suite, + c.monthly + hw.e3 + jdk + cc.libgraal + cc.economy_mode + cc.no_tiered_comp + suite ] for jdk in cc.product_jdks for suite in bench.groups.main_suites @@ -65,21 +58,21 @@ local gc_variants_builds = std.flattenArrays([ [ - c.weekly + hw.x52 + jdk + cc.c2 + cc.zgc_mode + suite, + c.monthly + hw.e3 + jdk + cc.c2 + cc.zgc_mode + suite, ] for jdk in cc.product_jdks for suite in bench.groups.main_suites ]) + std.flattenArrays([ [ - c.weekly + hw.x52 + jdk + cc.c2 + cc.serialgc_mode + bench.microservice_benchmarks, - c.weekly + hw.x52 + jdk + cc.c2 + cc.pargc_mode + bench.microservice_benchmarks, - c.weekly + hw.x52 + jdk + cc.c2 + cc.zgc_mode + bench.microservice_benchmarks, - c.weekly + hw.x52 + jdk + cc.c2 + cc.gen_zgc_mode + bench.microservice_benchmarks, + c.monthly + hw.e3 + jdk + cc.c2 + cc.serialgc_mode + bench.microservice_benchmarks, + c.monthly + hw.e3 + jdk + cc.c2 + cc.pargc_mode + bench.microservice_benchmarks, + c.monthly + hw.e3 + jdk + cc.c2 + cc.zgc_mode + bench.microservice_benchmarks, + c.monthly + hw.e3 + jdk + cc.c2 + cc.gen_zgc_mode + bench.microservice_benchmarks, ] for jdk in cc.product_jdks ]), local all_builds = hotspot_amd64_builds + hotspot_aarch64_builds + hotspot_profiling_builds + - weekly_forks_amd64_builds + weekly_forks_aarch64_builds + daily_economy_builds + weekly_economy_builds + no_tiered_builds + gc_variants_builds, + weekly_forks_amd64_builds + weekly_forks_aarch64_builds + economy_builds + no_tiered_builds + gc_variants_builds, local filtered_builds = [b for b in all_builds if b.is_jdk_supported(b.jdk_version) && b.is_arch_supported(b.arch)], // adds a "defined_in" field to all builds mentioning the location of this current file diff --git a/compiler/mx.compiler/suite.py b/compiler/mx.compiler/suite.py index 987632aae1d5..0ded49b4dfc6 100644 --- a/compiler/mx.compiler/suite.py +++ b/compiler/mx.compiler/suite.py @@ -4,7 +4,7 @@ "sourceinprojectwhitelist" : [], "groupId" : "org.graalvm.compiler", - "version" : "24.0.1.1", + "version" : "24.0.2.0", "release" : False, "url" : "http://www.graalvm.org/", "developer" : { diff --git a/compiler/src/jdk.graal.compiler.test/src/jdk/graal/compiler/core/test/CountedLoopTest.java b/compiler/src/jdk.graal.compiler.test/src/jdk/graal/compiler/core/test/CountedLoopTest.java index 03d1209c5ac6..a19c3df543a4 100644 --- a/compiler/src/jdk.graal.compiler.test/src/jdk/graal/compiler/core/test/CountedLoopTest.java +++ b/compiler/src/jdk.graal.compiler.test/src/jdk/graal/compiler/core/test/CountedLoopTest.java @@ -47,6 +47,8 @@ import jdk.graal.compiler.nodes.util.GraphUtil; import jdk.graal.compiler.options.OptionValues; import jdk.graal.compiler.phases.OptimisticOptimizations; + +import org.junit.Ignore; import org.junit.Test; import jdk.vm.ci.code.InstalledCode; @@ -549,11 +551,13 @@ public void decrementUnsigned6() { } @Test + @Ignore("GR-54120: Loops requiring overflow to terminate must not be counted in graal, CountedLoopInfo API does not support that") public void decrementUnsigned7() { testCounted("decrementUnsignedSnippet", Integer.MAX_VALUE + 10, 0, 1); } @Test + @Ignore("GR-54120: Loops requiring overflow to terminate must not be counted in graal, CountedLoopInfo API does not support that") public void decrementUnsigned8() { testCounted("decrementUnsignedSnippet", Integer.MAX_VALUE + 11, 0, 2); } diff --git a/compiler/src/jdk.graal.compiler.test/src/jdk/graal/compiler/core/test/LoopFullUnrollTest.java b/compiler/src/jdk.graal.compiler.test/src/jdk/graal/compiler/core/test/LoopFullUnrollTest.java index 4e44c4941404..cfe838cfadcf 100644 --- a/compiler/src/jdk.graal.compiler.test/src/jdk/graal/compiler/core/test/LoopFullUnrollTest.java +++ b/compiler/src/jdk.graal.compiler.test/src/jdk/graal/compiler/core/test/LoopFullUnrollTest.java @@ -24,6 +24,7 @@ */ package jdk.graal.compiler.core.test; +import jdk.graal.compiler.api.directives.GraalDirectives; import jdk.graal.compiler.debug.DebugContext; import jdk.graal.compiler.debug.DebugDumpScope; import jdk.graal.compiler.loop.phases.LoopFullUnrollPhase; @@ -95,4 +96,38 @@ private void test(String snippet, int loopCount) { throw debug.handle(e); } } + + public static int snippetFlows() { + int init = Integer.MIN_VALUE; + int step = -1; + int limit = 1; + int phi = init; + while (Integer.MIN_VALUE - phi < limit) { + GraalDirectives.sideEffect(); + phi = phi + step; + } + return phi; + } + + @Test + public void testFlows() { + test("snippetFlows"); + } + + public static int snippetFlows2() { + int init = Integer.MAX_VALUE; + int step = -8; + int limit = 8184; + int phi = init; + while (Integer.MIN_VALUE - phi < limit) { + GraalDirectives.sideEffect(); + phi = phi + step; + } + return phi; + } + + @Test + public void testFlows2() { + test("snippetFlows2"); + } } diff --git a/compiler/src/jdk.graal.compiler.test/src/jdk/graal/compiler/core/test/LoopUnswitchTest.java b/compiler/src/jdk.graal.compiler.test/src/jdk/graal/compiler/core/test/LoopUnswitchTest.java index 49d1f31cea19..ce8f48979e11 100644 --- a/compiler/src/jdk.graal.compiler.test/src/jdk/graal/compiler/core/test/LoopUnswitchTest.java +++ b/compiler/src/jdk.graal.compiler.test/src/jdk/graal/compiler/core/test/LoopUnswitchTest.java @@ -27,6 +27,9 @@ import java.util.List; import org.graalvm.collections.EconomicMap; +import org.junit.Assert; +import org.junit.Test; + import jdk.graal.compiler.api.directives.GraalDirectives; import jdk.graal.compiler.debug.DebugContext; import jdk.graal.compiler.debug.DebugDumpScope; @@ -41,8 +44,6 @@ import jdk.graal.compiler.nodes.loop.LoopEx; import jdk.graal.compiler.nodes.loop.LoopPolicies; import jdk.graal.compiler.phases.common.CanonicalizerPhase; -import org.junit.Assert; -import org.junit.Test; public class LoopUnswitchTest extends GraalCompilerTest { @@ -476,4 +477,44 @@ public void test05() { CanonicalizerPhase canonicalizer = createCanonicalizerPhase(); new LoopUnswitchingPhase(new DefaultLoopPolicies(), canonicalizer).apply(graph, getDefaultHighTierContext()); } + + static final double ULP = Math.ulp(0.25); + + /** + * Simulate a profile that due to floating imprecision has branch probabilities summing to the + * next floating point number after 1. + */ + public static int testImpreciseProfileSnippet(int a) { + int sum = 0; + for (int i = 0; i < 1000; i++) { + switch (a) { + case 0: + GraalDirectives.injectSwitchCaseProbability(0.25); + sum += 1; + break; + case 1: + GraalDirectives.injectSwitchCaseProbability(0.25); + sum += 2; + break; + case 2: + GraalDirectives.injectSwitchCaseProbability(0.25); + sum += 3; + break; + default: + GraalDirectives.injectSwitchCaseProbability(0.25 + ULP); + sum += a; + break; + } + } + return sum; + } + + @Test + public void testImpreciseProfile() { + final StructuredGraph graph = parseEager("testImpreciseProfileSnippet", AllowAssumptions.NO); + CanonicalizerPhase canonicalizer = createCanonicalizerPhase(); + // Apply canonicalizer to inject switch probabilities + canonicalizer.apply(graph, getDefaultHighTierContext()); + new LoopUnswitchingPhase(new DefaultLoopPolicies(), canonicalizer).apply(graph, getDefaultHighTierContext()); + } } diff --git a/compiler/src/jdk.graal.compiler.test/src/jdk/graal/compiler/hotspot/test/GraalOSRTestBase.java b/compiler/src/jdk.graal.compiler.test/src/jdk/graal/compiler/hotspot/test/GraalOSRTestBase.java index 462d7c65cc49..e55d09a3fc5f 100644 --- a/compiler/src/jdk.graal.compiler.test/src/jdk/graal/compiler/hotspot/test/GraalOSRTestBase.java +++ b/compiler/src/jdk.graal.compiler.test/src/jdk/graal/compiler/hotspot/test/GraalOSRTestBase.java @@ -95,7 +95,7 @@ protected static void compile(DebugContext debug, ResolvedJavaMethod method, int /** * Returns the target BCIs of all bytecode backedges. */ - public int[] getBackedgeBCIs(DebugContext debug, ResolvedJavaMethod method) { + public static int[] getBackedgeBCIs(DebugContext debug, ResolvedJavaMethod method) { Bytecode code = new ResolvedJavaMethodBytecode(method); BytecodeStream stream = new BytecodeStream(code.getCode()); OptionValues options = debug.getOptions(); @@ -132,7 +132,15 @@ protected void compileOSR(OptionValues options, ResolvedJavaMethod method) { compileOSR(options, method, true); } + public interface Parser { + StructuredGraph parse(ResolvedJavaMethod m, AllowAssumptions a, OptionValues o); + } + protected void compileOSR(OptionValues options, ResolvedJavaMethod method, boolean expectBackedge) { + compileOSR(options, method, expectBackedge, (x, y, z) -> parseEager(x, y, z)); + } + + public static void compileOSR(OptionValues options, ResolvedJavaMethod method, boolean expectBackedge, Parser p) { OptionValues goptions = options; // Silence diagnostics for permanent bailout errors as they // are expected for some OSR tests. @@ -140,7 +148,7 @@ protected void compileOSR(OptionValues options, ResolvedJavaMethod method, boole goptions = new OptionValues(options, GraalCompilerOptions.CompilationBailoutAsFailure, false); } // ensure eager resolving - StructuredGraph graph = parseEager(method, AllowAssumptions.YES, goptions); + StructuredGraph graph = p.parse(method, AllowAssumptions.YES, goptions); DebugContext debug = graph.getDebug(); int[] backedgeBCIs = getBackedgeBCIs(debug, method); if (expectBackedge && backedgeBCIs.length == 0) { diff --git a/compiler/src/jdk.graal.compiler.test/src/jdk/graal/compiler/nodes/test/OpaqueNodeTest.java b/compiler/src/jdk.graal.compiler.test/src/jdk/graal/compiler/nodes/test/OpaqueNodeTest.java index 7587d7da8c7b..478e04aa9e23 100644 --- a/compiler/src/jdk.graal.compiler.test/src/jdk/graal/compiler/nodes/test/OpaqueNodeTest.java +++ b/compiler/src/jdk.graal.compiler.test/src/jdk/graal/compiler/nodes/test/OpaqueNodeTest.java @@ -82,4 +82,20 @@ public void testNegWrite() { test("writeNegSnippet"); assertTrue(arr[0] == (byte) -300); } + + public static void opaqueClassSnippet() { + /* + * GR-51558: This would cause an assertion failure in LIR constant load optimization if the + * opaque is not removed. + */ + Class c = GraalDirectives.opaque(Object.class); + if (c.getResource("resource.txt") == null) { + GraalDirectives.deoptimize(); + } + } + + @Test + public void testOpaqueClass() { + test("opaqueClassSnippet"); + } } diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/core/phases/EconomyLowTier.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/core/phases/EconomyLowTier.java index 8b3e52460920..351b6935a5e2 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/core/phases/EconomyLowTier.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/core/phases/EconomyLowTier.java @@ -31,6 +31,7 @@ import jdk.graal.compiler.phases.common.CanonicalizerPhase; import jdk.graal.compiler.phases.common.ExpandLogicPhase; import jdk.graal.compiler.phases.common.LowTierLoweringPhase; +import jdk.graal.compiler.phases.common.RemoveOpaqueValuePhase; import jdk.graal.compiler.phases.schedule.SchedulePhase; import jdk.graal.compiler.phases.tiers.LowTierContext; @@ -52,6 +53,7 @@ public EconomyLowTier() { * backend or the target specific suites provider. */ appendPhase(new PlaceholderPhase(AddressLoweringPhase.class)); + appendPhase(new RemoveOpaqueValuePhase()); appendPhase(new SchedulePhase(SchedulePhase.SchedulingStrategy.LATEST_OUT_OF_LOOPS)); } } diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/core/phases/LowTier.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/core/phases/LowTier.java index 61424ef9e681..a92c007f28b9 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/core/phases/LowTier.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/core/phases/LowTier.java @@ -42,6 +42,7 @@ import jdk.graal.compiler.phases.common.OptimizeExtendsPhase; import jdk.graal.compiler.phases.common.ProfileCompiledMethodsPhase; import jdk.graal.compiler.phases.common.PropagateDeoptimizeProbabilityPhase; +import jdk.graal.compiler.phases.common.RemoveOpaqueValuePhase; import jdk.graal.compiler.phases.schedule.SchedulePhase; import jdk.graal.compiler.phases.schedule.SchedulePhase.SchedulingStrategy; import jdk.graal.compiler.phases.tiers.LowTierContext; @@ -93,6 +94,8 @@ public LowTier(OptionValues options) { appendPhase(new OptimizeExtendsPhase()); + appendPhase(new RemoveOpaqueValuePhase()); + appendPhase(new SchedulePhase(SchedulePhase.SchedulingStrategy.LATEST_OUT_OF_LOOPS)); } diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/hotspot/JVMCIVersionCheck.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/hotspot/JVMCIVersionCheck.java index 767c699c24ba..875587bc9741 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/hotspot/JVMCIVersionCheck.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/hotspot/JVMCIVersionCheck.java @@ -54,8 +54,8 @@ public final class JVMCIVersionCheck { private static final Map> JVMCI_MIN_VERSIONS = Map.of( "21", Map.of(DEFAULT_VENDOR_ENTRY, new Version(23, 1, 26)), "22", Map.of( - "Oracle Corporation", new Version("22.0.1+8", 1), - DEFAULT_VENDOR_ENTRY, new Version("22.0.1+8", 1))); + "Oracle Corporation", new Version("22.0.2+9", 1), + DEFAULT_VENDOR_ENTRY, new Version("22.0.2+9", 1))); private static final int NA = 0; /** * Minimum Java release supported by Graal. diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/nodes/IfNode.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/nodes/IfNode.java index fbc0f26b3364..5aaa1b90de4c 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/nodes/IfNode.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/nodes/IfNode.java @@ -177,8 +177,8 @@ public AbstractBeginNode successor(boolean istrue) { public void setTrueSuccessorProbability(BranchProbabilityData profileData) { double prob = profileData.getDesignatedSuccessorProbability(); - assert prob >= -0.000000001 && prob <= 1.000000001 : "Probability out of bounds: " + prob; - double trueSuccessorProbability = Math.min(1.0, Math.max(0.0, prob)); + assert ProfileData.isApproximatelyInRange(prob, 0.0, 1.0) : "Probability out of bounds: " + prob; + double trueSuccessorProbability = Math.clamp(prob, 0.0, 1.0); this.profileData = profileData.copy(trueSuccessorProbability); } diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/nodes/ProfileData.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/nodes/ProfileData.java index 072124bba1a6..3ca71e8b0bb1 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/nodes/ProfileData.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/nodes/ProfileData.java @@ -37,7 +37,6 @@ * {@link LoopFrequencyData}, {@link SwitchProbabilityData} */ public abstract class ProfileData { - protected final ProfileSource profileSource; protected ProfileData(ProfileSource profileSource) { @@ -123,6 +122,32 @@ public ProfileSource getProfileSource() { return profileSource; } + /** + * The smallest possible difference between two numerical probabilities/frequencies. Should be + * used as a threshold for floating-point comparisons. + */ + public static final double EPSILON = 1e-9; + + /** + * Compares two probabilities for approximate equality with a threshold of {@link #EPSILON}. + * + * @return {@code true} iff the absolute difference between the two probabilities is smaller or + * equal to {@link #EPSILON}. + */ + public static boolean isApproximatelyEqual(double probability, double expected) { + return Math.abs(probability - expected) <= EPSILON; + } + + /** + * Determines whether a probability lies in a given allowed range, allowing for a threshold of + * {@link #EPSILON}. + * + * @return {@code true} iff {@code (min - EPSILON) <= probability <= (max + EPSILON)}. + */ + public static boolean isApproximatelyInRange(double probability, double min, double max) { + return min - EPSILON <= probability && probability <= max + EPSILON; + } + /** * Profile data for one successor of a node. Also used for two-way branches like {@link IfNode} * or {@link WithExceptionNode}. When used for a branch, the probability stored within is the diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/nodes/extended/OpaqueValueNode.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/nodes/extended/OpaqueValueNode.java index 0ad9643b04e5..39da4a377708 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/nodes/extended/OpaqueValueNode.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/nodes/extended/OpaqueValueNode.java @@ -24,6 +24,10 @@ */ package jdk.graal.compiler.nodes.extended; +import static jdk.graal.compiler.nodeinfo.NodeCycles.CYCLES_0; +import static jdk.graal.compiler.nodeinfo.NodeSize.SIZE_0; + +import jdk.graal.compiler.graph.IterableNodeType; import jdk.graal.compiler.graph.NodeClass; import jdk.graal.compiler.graph.spi.NodeWithIdentity; import jdk.graal.compiler.nodeinfo.InputType; @@ -31,13 +35,21 @@ import jdk.graal.compiler.nodes.NodeView; import jdk.graal.compiler.nodes.ValueNode; import jdk.graal.compiler.nodes.spi.LIRLowerable; -import jdk.graal.compiler.nodes.spi.NodeLIRBuilderTool; - -import static jdk.graal.compiler.nodeinfo.NodeCycles.CYCLES_0; -import static jdk.graal.compiler.nodeinfo.NodeSize.SIZE_0; +import jdk.graal.compiler.phases.common.RemoveOpaqueValuePhase; +/** + * This node type acts as an optimization barrier between its input node and its usages. For + * example, a MulNode with two ConstantNodes as input will be canonicalized to a ConstantNode. This + * optimization will be prevented if either of the two constants is wrapped by an OpaqueValueNode. + *

+ *

+ * This node is not {@link LIRLowerable}, so it should be removed from the graph before LIR + * generation. + * + * @see RemoveOpaqueValuePhase + */ @NodeInfo(cycles = CYCLES_0, size = SIZE_0) -public final class OpaqueValueNode extends OpaqueNode implements NodeWithIdentity, LIRLowerable, GuardingNode { +public final class OpaqueValueNode extends OpaqueNode implements NodeWithIdentity, GuardingNode, IterableNodeType { public static final NodeClass TYPE = NodeClass.create(OpaqueValueNode.class); @Input(InputType.Value) private ValueNode value; @@ -57,9 +69,4 @@ public void setValue(ValueNode value) { this.updateUsages(this.value, value); this.value = value; } - - @Override - public void generate(NodeLIRBuilderTool gen) { - gen.setResult(this, gen.operand(getValue())); - } } diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/nodes/extended/SwitchNode.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/nodes/extended/SwitchNode.java index 8e0acf05ab3a..5de90e3a7c52 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/nodes/extended/SwitchNode.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/nodes/extended/SwitchNode.java @@ -106,7 +106,8 @@ private boolean assertProbabilities() { total += d; GraalError.guarantee(d >= 0.0, "Cannot have negative probabilities in switch node: %s", d); } - GraalError.guarantee(total > 0.999 && total < 1.001, "Total probability across branches not equal to one: %.4f", total); + GraalError.guarantee(ProfileData.isApproximatelyEqual(total, 1.0), + "Total probability across branches not equal to one: %.10f", total); return true; } @@ -130,7 +131,6 @@ public double probability(AbstractBeginNode successor) { public boolean setProbability(AbstractBeginNode successor, BranchProbabilityData successorProfileData) { double newProbability = successorProfileData.getDesignatedSuccessorProbability(); assert newProbability <= 1.0 && newProbability >= 0.0 : newProbability; - assert assertProbabilities(); double[] keyProbabilities = getKeyProbabilities().clone(); double sum = 0; diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/nodes/loop/DefaultLoopPolicies.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/nodes/loop/DefaultLoopPolicies.java index 76bbe4cad4b7..721eeb823156 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/nodes/loop/DefaultLoopPolicies.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/nodes/loop/DefaultLoopPolicies.java @@ -44,6 +44,15 @@ import java.util.List; +import org.graalvm.collections.EconomicMap; + +import jdk.graal.compiler.core.common.cfg.BasicBlock; +import jdk.graal.compiler.core.common.cfg.Loop; +import jdk.graal.compiler.core.common.util.UnsignedLong; +import jdk.graal.compiler.debug.DebugContext; +import jdk.graal.compiler.debug.GraalError; +import jdk.graal.compiler.graph.Node; +import jdk.graal.compiler.graph.NodeBitMap; import jdk.graal.compiler.nodes.AbstractBeginNode; import jdk.graal.compiler.nodes.ControlSplitNode; import jdk.graal.compiler.nodes.Invoke; @@ -54,17 +63,9 @@ import jdk.graal.compiler.nodes.calc.CompareNode; import jdk.graal.compiler.nodes.cfg.ControlFlowGraph; import jdk.graal.compiler.nodes.cfg.HIRBlock; +import jdk.graal.compiler.nodes.debug.ControlFlowAnchorNode; import jdk.graal.compiler.nodes.extended.ForeignCall; import jdk.graal.compiler.nodes.spi.CoreProviders; -import org.graalvm.collections.EconomicMap; -import jdk.graal.compiler.core.common.cfg.BasicBlock; -import jdk.graal.compiler.core.common.cfg.Loop; -import jdk.graal.compiler.core.common.util.UnsignedLong; -import jdk.graal.compiler.debug.DebugContext; -import jdk.graal.compiler.debug.GraalError; -import jdk.graal.compiler.graph.Node; -import jdk.graal.compiler.graph.NodeBitMap; -import jdk.graal.compiler.nodes.debug.ControlFlowAnchorNode; import jdk.graal.compiler.options.Option; import jdk.graal.compiler.options.OptionKey; import jdk.graal.compiler.options.OptionType; @@ -578,14 +579,14 @@ public UnswitchingDecision shouldUnswitch(LoopEx loop, EconomicMap + * int i = 0; + * while (i < limit) { + * int reversIv = off - i; + * i++; + * } + * + * + * here {@code reverseIv} stride node is actually {@code i} negated since the IV is not + * {@code i op off} but {@code off op i} where {@code op} is a subtraction. + */ + private boolean isMaskedNegateStride() { + return value instanceof SubNode && base.valueNode() == value.getY(); + } + @Override public boolean isConstantExtremum() { try { @@ -157,15 +189,18 @@ public ValueNode exitValueNode() { } private long opSafe(long b, long o) throws ArithmeticException { + // we can use offset bits in this method because all operands (init, scale, stride and + // extremum) have by construction equal bit sizes if (value instanceof AddNode) { - return Math.addExact(b, o); + return LoopUtility.addExact(IntegerStamp.getBits(offset.stamp(NodeView.DEFAULT)), b, o); } if (value instanceof SubNode) { if (base.valueNode() == value.getX()) { - return Math.subtractExact(b, o); + return LoopUtility.subtractExact(IntegerStamp.getBits(offset.stamp(NodeView.DEFAULT)), b, o); } else { - assert base.valueNode() == value.getY() : Assertions.errorMessage(base, base.valueNode(), value, value.getY()); - return Math.subtractExact(b, o); + assert base.valueNode() == value.getY() || base instanceof BasicInductionVariable basic && basic.getOp() instanceof IntegerExactArithmeticNode : Assertions.errorMessage(base, + base.valueNode(), value, value.getY()); + return LoopUtility.subtractExact(IntegerStamp.getBits(offset.stamp(NodeView.DEFAULT)), b, o); } } throw GraalError.shouldNotReachHereUnexpectedValue(value); // ExcludeFromJacocoGeneratedReport @@ -183,7 +218,8 @@ public ValueNode op(ValueNode b, ValueNode o, boolean gvn) { if (base.valueNode() == value.getX()) { return MathUtil.sub(graph(), b, o, gvn); } else { - assert base.valueNode() == value.getY() : Assertions.errorMessage(base, base.valueNode(), value, value.getY()); + assert base.valueNode() == value.getY() || base instanceof BasicInductionVariable basic && basic.getOp() instanceof IntegerExactArithmeticNode : Assertions.errorMessage(base, + base.valueNode(), value, value.getY()); return MathUtil.sub(graph(), o, b, gvn); } } diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/nodes/loop/DerivedScaledInductionVariable.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/nodes/loop/DerivedScaledInductionVariable.java index 2398aef9d872..cf43515ac973 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/nodes/loop/DerivedScaledInductionVariable.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/nodes/loop/DerivedScaledInductionVariable.java @@ -34,6 +34,7 @@ import jdk.graal.compiler.nodes.calc.IntegerConvertNode; import jdk.graal.compiler.nodes.calc.NegateNode; import jdk.graal.compiler.nodes.util.GraphUtil; +import jdk.graal.compiler.phases.common.util.LoopUtility; public class DerivedScaledInductionVariable extends DerivedInductionVariable { @@ -121,7 +122,7 @@ public long constantInit() { } private long constantInitSafe() throws ArithmeticException { - return Math.multiplyExact(base.constantInit(), scale.asJavaConstant().asLong()); + return opSafe(base.constantInit(), scale.asJavaConstant().asLong()); } @Override @@ -130,7 +131,13 @@ public long constantStride() { } private long constantStrideSafe() throws ArithmeticException { - return Math.multiplyExact(base.constantStride(), scale.asJavaConstant().asLong()); + return opSafe(base.constantStride(), scale.asJavaConstant().asLong()); + } + + private long opSafe(long a, long b) { + // we can use scale bits here because all operands (init, scale, stride and extremum) have + // by construction equal bit sizes + return LoopUtility.multiplyExact(IntegerStamp.getBits(scale.stamp(NodeView.DEFAULT)), a, b); } @Override @@ -152,7 +159,7 @@ public long constantExtremum() { } private long constantExtremumSafe() throws ArithmeticException { - return Math.multiplyExact(base.constantExtremum(), scale.asJavaConstant().asLong()); + return opSafe(base.constantExtremum(), scale.asJavaConstant().asLong()); } @Override diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/nodes/loop/LoopEx.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/nodes/loop/LoopEx.java index 7b349523b243..97d365263cec 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/nodes/loop/LoopEx.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/nodes/loop/LoopEx.java @@ -332,7 +332,7 @@ public boolean detectCounted() { if (iv.direction() == InductionVariable.Direction.Up) { if (limitStamp.asConstant() != null && limitStamp.asConstant().asLong() == counterStamp.upperBound()) { // signed: i < MAX_INT - } else if (limitStamp.asConstant() != null && limitStamp.asConstant().asLong() == counterStamp.unsignedUpperBound()) { + } else if (limitStamp.asConstant() != null && limitStamp.asConstant().asLong() == counterStamp.unsignedUpperBound() && IntegerStamp.sameSign(initStamp, limitStamp)) { unsigned = true; } else if (!iv.isConstantStride() || !absStrideIsOne(iv) || initStamp.upperBound() > limitStamp.lowerBound()) { return false; @@ -340,7 +340,7 @@ public boolean detectCounted() { } else if (iv.direction() == InductionVariable.Direction.Down) { if (limitStamp.asConstant() != null && limitStamp.asConstant().asLong() == counterStamp.lowerBound()) { // signed: MIN_INT > i - } else if (limitStamp.asConstant() != null && limitStamp.asConstant().asLong() == counterStamp.unsignedLowerBound()) { + } else if (limitStamp.asConstant() != null && limitStamp.asConstant().asLong() == counterStamp.unsignedLowerBound() && IntegerStamp.sameSign(initStamp, limitStamp)) { unsigned = true; } else if (!iv.isConstantStride() || !absStrideIsOne(iv) || initStamp.lowerBound() < limitStamp.upperBound()) { return false; diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/phases/common/RemoveOpaqueValuePhase.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/phases/common/RemoveOpaqueValuePhase.java new file mode 100644 index 000000000000..b472e1751e4c --- /dev/null +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/phases/common/RemoveOpaqueValuePhase.java @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2012, 2022, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. 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 jdk.graal.compiler.phases.common; + +import java.util.Optional; + +import jdk.graal.compiler.nodes.GraphState; +import jdk.graal.compiler.nodes.StructuredGraph; +import jdk.graal.compiler.nodes.extended.OpaqueValueNode; +import jdk.graal.compiler.nodes.spi.CoreProviders; +import jdk.graal.compiler.phases.BasePhase; + +/** + * Removes all {@link jdk.graal.compiler.nodes.extended.OpaqueValueNode}s from the graph. + */ +public class RemoveOpaqueValuePhase extends BasePhase { + @Override + public Optional notApplicableTo(GraphState graphState) { + return ALWAYS_APPLICABLE; + } + + public boolean shouldApply(StructuredGraph graph) { + return graph.hasNode(OpaqueValueNode.TYPE); + } + + @Override + protected void run(StructuredGraph graph, CoreProviders context) { + for (OpaqueValueNode opaque : graph.getNodes(OpaqueValueNode.TYPE)) { + opaque.replaceAtUsagesAndDelete(opaque.getValue()); + } + } +} diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/phases/common/util/LoopUtility.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/phases/common/util/LoopUtility.java index f025087619d8..ed884cd20a2a 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/phases/common/util/LoopUtility.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/phases/common/util/LoopUtility.java @@ -29,6 +29,7 @@ import jdk.graal.compiler.core.common.cfg.Loop; import jdk.graal.compiler.core.common.type.IntegerStamp; import jdk.graal.compiler.core.common.type.Stamp; +import jdk.graal.compiler.debug.Assertions; import jdk.graal.compiler.debug.GraalError; import jdk.graal.compiler.graph.Graph.NodeEvent; import jdk.graal.compiler.graph.Graph.NodeEventScope; @@ -55,6 +56,45 @@ public class LoopUtility { + public static long addExact(int bits, long a, long b) { + if (bits == 32) { + int ia = (int) a; + int ib = (int) b; + assert ia == a && ib == b : Assertions.errorMessage("Conversions must be lossless", bits, a, b, ia, ib); + return Math.addExact(ia, ib); + } else if (bits == 64) { + return Math.addExact(a, b); + } else { + throw GraalError.shouldNotReachHere("Must be one of java's core datatypes int/long but is " + bits); + } + } + + public static long subtractExact(int bits, long a, long b) { + if (bits == 32) { + int ia = (int) a; + int ib = (int) b; + assert ia == a && ib == b : Assertions.errorMessage("Conversions must be lossless", bits, a, b, ia, ib); + return Math.subtractExact(ia, ib); + } else if (bits == 64) { + return Math.subtractExact(a, b); + } else { + throw GraalError.shouldNotReachHere("Must be one of java's core datatypes int/long but is " + bits); + } + } + + public static long multiplyExact(int bits, long a, long b) { + if (bits == 32) { + int ia = (int) a; + int ib = (int) b; + assert ia == a && ib == b : Assertions.errorMessage("Conversions must be lossless", bits, a, b, ia, ib); + return Math.multiplyExact(ia, ib); + } else if (bits == 64) { + return Math.multiplyExact(a, b); + } else { + throw GraalError.shouldNotReachHere("Must be one of java's core datatypes int/long but is " + bits); + } + } + public static boolean canTakeAbs(long l, int bits) { try { abs(l, bits); diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/truffle/substitutions/TruffleGraphBuilderPlugins.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/truffle/substitutions/TruffleGraphBuilderPlugins.java index 6e149202138a..3a5ad53ca31d 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/truffle/substitutions/TruffleGraphBuilderPlugins.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/truffle/substitutions/TruffleGraphBuilderPlugins.java @@ -1004,8 +1004,6 @@ private static void registerMemorySegmentPlugins(InvocationPlugins plugins, Know if (memorySegmentImplType != null) { Registration r = new Registration(plugins, new ResolvedJavaSymbol(memorySegmentImplType)); r.register(new OptionalInvocationPlugin("sessionImpl", Receiver.class) { - private final SpeculationLog.SpeculationReason bufferSegmentNullSpeculationReason = BUFFER_SEGMENT_NULL_SPECULATION.createSpeculationReason(); - /** * ByteBuffer methods and VarHandles use the following code pattern to get any * memory session that needs to be checked: @@ -1037,6 +1035,7 @@ public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Rec Stamp stamp = segment.stamp(NodeView.DEFAULT); if (stamp instanceof ObjectStamp && !((ObjectStamp) stamp).nonNull() && !((ObjectStamp) stamp).alwaysNull()) { ValueNode load = GraphUtil.unproxify(segment); + SpeculationLog.SpeculationReason bufferSegmentNullSpeculationReason = BUFFER_SEGMENT_NULL_SPECULATION.createSpeculationReason(); if (load instanceof LoadFieldNode && types.Buffer_segment.equals(((LoadFieldNode) load).field()) && speculationLog.maySpeculate(bufferSegmentNullSpeculationReason)) { Speculation speculation = speculationLog.speculate(bufferSegmentNullSpeculationReason); diff --git a/espresso/ci/ci_common/common.jsonnet b/espresso/ci/ci_common/common.jsonnet index 59db9238612e..0882f3736ce9 100644 --- a/espresso/ci/ci_common/common.jsonnet +++ b/espresso/ci/ci_common/common.jsonnet @@ -33,8 +33,8 @@ local benchmark_suites = ['dacapo', 'renaissance', 'scala-dacapo']; linux_amd64: self.common + self.linux + graal_common.linux_amd64, linux_aarch64: self.common + self.linux + graal_common.linux_aarch64, - x52: { - capabilities+: ['no_frequency_scaling', 'tmpfs25g', 'x52'], + e3: { + capabilities+: ['no_frequency_scaling', 'tmpfs25g', 'e3'], }, darwin_amd64: self.common + graal_common.darwin_amd64 + { @@ -88,7 +88,7 @@ local benchmark_suites = ['dacapo', 'renaissance', 'scala-dacapo']; dailyBench: {targets+: ['bench', 'daily'], notify_groups:: ['espresso']}, daily: {targets+: ['daily'], notify_groups:: ['espresso']}, weekly: {targets+: ['weekly'], notify_groups:: ['espresso']}, - monthly: {targets+: ['monthly'], notify_groups:: ['espresso']}, + monthly: {targets+: ['monthly'], notify_groups:: ['espresso']}, weeklyBench: {targets+: ['bench', 'weekly'], notify_groups:: ['espresso']}, onDemand: {targets+: ['on-demand']}, onDemandBench: {targets+: ['bench', 'on-demand']}, @@ -105,7 +105,7 @@ local benchmark_suites = ['dacapo', 'renaissance', 'scala-dacapo']; jdk21_gate_darwin_amd64 : self.gate + self.darwin_amd64_21, jdk21_gate_darwin_aarch64 : self.gate + self.darwin_aarch64_21, jdk21_gate_windows_amd64 : self.gate + self.windows_21, - jdk21_bench_linux : self.bench + self.linux_amd64_21 + self.x52, + jdk21_bench_linux : self.bench + self.linux_amd64_21 + self.e3, jdk21_bench_darwin : self.bench + self.darwin_amd64_21, jdk21_bench_windows : self.bench + self.windows_21, jdk21_daily_linux_amd64 : self.daily + self.linux_amd64_21, @@ -113,7 +113,7 @@ local benchmark_suites = ['dacapo', 'renaissance', 'scala-dacapo']; jdk21_daily_darwin_amd64 : self.daily + self.darwin_amd64_21, jdk21_daily_darwin_aarch64 : self.daily + self.darwin_aarch64_21, jdk21_daily_windows_amd64 : self.daily + self.windows_21, - jdk21_daily_bench_linux : self.dailyBench + self.linux_amd64_21 + self.x52, + jdk21_daily_bench_linux : self.dailyBench + self.linux_amd64_21 + self.e3, jdk21_daily_bench_darwin : self.dailyBench + self.darwin_amd64_21, jdk21_daily_bench_windows : self.dailyBench + self.windows_21, jdk21_weekly_linux_amd64 : self.weekly + self.linux_amd64_21, @@ -126,13 +126,13 @@ local benchmark_suites = ['dacapo', 'renaissance', 'scala-dacapo']; jdk21_monthly_darwin_amd64 : self.monthly + self.darwin_amd64_21, jdk21_monthly_darwin_aarch64 : self.monthly + self.darwin_aarch64_21, jdk21_monthly_windows_amd64 : self.monthly + self.windows_21, - jdk21_weekly_bench_linux : self.weeklyBench + self.linux_amd64_21 + self.x52, + jdk21_weekly_bench_linux : self.weeklyBench + self.linux_amd64_21 + self.e3, jdk21_weekly_bench_darwin : self.weeklyBench + self.darwin_amd64_21, jdk21_weekly_bench_windows : self.weeklyBench + self.windows_21, jdk21_on_demand_linux : self.onDemand + self.linux_amd64_21, jdk21_on_demand_darwin : self.onDemand + self.darwin_amd64_21, jdk21_on_demand_windows : self.onDemand + self.windows_21, - jdk21_on_demand_bench_linux : self.onDemandBench + self.linux_amd64_21 + self.x52, + jdk21_on_demand_bench_linux : self.onDemandBench + self.linux_amd64_21 + self.e3, jdk21_on_demand_bench_darwin : self.onDemandBench + self.darwin_amd64_21, jdk21_on_demand_bench_windows : self.onDemandBench + self.windows_21, diff --git a/espresso/mx.espresso/espresso_runtime_resource.template b/espresso/mx.espresso/espresso_runtime_resource.template new file mode 100644 index 000000000000..0d79ece54150 --- /dev/null +++ b/espresso/mx.espresso/espresso_runtime_resource.template @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please 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 ; + +import com.oracle.truffle.api.CompilerDirectives; +import com.oracle.truffle.api.InternalResource; + +import java.io.IOException; +import java.nio.file.Path; + +@InternalResource.Id(value = "", componentId = "java", optional = true) +public final class EspressoRuntimeResource implements InternalResource { + + private static Path basePath(Env env) { + return Path.of("META-INF", "resources", "java", "", env.getOS().toString(), env.getCPUArchitecture().toString()); + } + + @Override + public void unpackFiles(Env env, Path targetDirectory) throws IOException { + Path base = basePath(env); + env.unpackResourceFiles(base.resolve("files"), targetDirectory, base); + } + + @Override + public String versionHash(Env env) { + try { + Path hashResource = basePath(env).resolve("sha256"); + return env.readResourceLines(hashResource).get(0); + } catch (IOException ioe) { + throw CompilerDirectives.shouldNotReachHere(ioe); + } + } +} diff --git a/espresso/mx.espresso/mx_espresso.py b/espresso/mx.espresso/mx_espresso.py index fbcf6e3fbf69..9e2f4d515f42 100644 --- a/espresso/mx.espresso/mx_espresso.py +++ b/espresso/mx.espresso/mx_espresso.py @@ -22,16 +22,20 @@ # import os +import shutil import signal import subprocess import mx +import mx_jardistribution +import mx_pomdistribution +import mx_subst import mx_espresso_benchmarks # pylint: disable=unused-import import mx_sdk_vm import mx_sdk_vm_impl from mx_gate import Task, add_gate_runner from mx_jackpot import jackpot -from os.path import join, isabs +from os.path import join, isabs, dirname, relpath, basename _suite = mx.suite('espresso') @@ -179,7 +183,7 @@ def _espresso_gate_runner(args, tasks): To fix the issue, run this gate locally: {instructions} And adapt the code to the modified headers in '{committed}'. -""".format(committed=os.path.relpath(mokapot_dir, _suite.vc_dir), generated=os.path.relpath(libjavavm_dir, _suite.vc_dir), instructions=run_instructions)) +""".format(committed=relpath(mokapot_dir, _suite.vc_dir), generated=relpath(libjavavm_dir, _suite.vc_dir), instructions=run_instructions)) # REGISTER MX GATE RUNNER @@ -262,40 +266,111 @@ def _espresso_gate_runner(args, tasks): )) -def _jdk_license(home): - if mx_sdk_vm.ee_implementor(home): - return "Oracle Proprietary" +def _resource_license(ee_implementor): + if ee_implementor: + return "GFTC" else: return "GPLv2-CPE" +_java_home_dep = None +_llvm_java_home_dep = None + + +def get_java_home_dep(): + global _java_home_dep + if _java_home_dep is None: + espresso_java_home = mx.get_env('ESPRESSO_JAVA_HOME') or mx_sdk_vm.base_jdk().home + _java_home_dep = JavaHomeDependency(_suite, "JAVA_HOME", espresso_java_home) + return _java_home_dep + + +def get_llvm_java_home_dep(): + global _llvm_java_home_dep + if _llvm_java_home_dep is None and espresso_llvm_java_home: + _llvm_java_home_dep = JavaHomeDependency(_suite, "LLVM_JAVA_HOME", espresso_llvm_java_home) + return _llvm_java_home_dep + + def mx_register_dynamic_suite_constituents(register_project, register_distribution): """ :type register_project: (mx.Project) -> None :type register_distribution: (mx.Distribution) -> None """ + java_home_dep = get_java_home_dep() + register_distribution(java_home_dep) # a "library" registered as a distribution is not ideal - if espresso_llvm_java_home: + llvm_java_home_dep = get_llvm_java_home_dep() + if llvm_java_home_dep: # Conditionally creates the ESPRESSO_LLVM_SUPPORT distribution if a Java home with LLVM bitcode is provided. lib_prefix = mx.add_lib_prefix('') lib_suffix = mx.add_lib_suffix('') + jdk_lib_dir = 'bin' if mx.is_windows() else 'lib' + register_distribution(llvm_java_home_dep) register_distribution(mx.LayoutTARDistribution(_suite, 'ESPRESSO_LLVM_SUPPORT', [], { "lib/llvm/default/": [ - f"dependency:LLVM_JAVA_HOME/lib/{lib_prefix}*{lib_suffix}", + f"dependency:LLVM_JAVA_HOME/{jdk_lib_dir}/{lib_prefix}*{lib_suffix}", "dependency:LLVM_JAVA_HOME/release" ], - }, None, True, _jdk_license(espresso_llvm_java_home))) + }, None, True, None)) + + if not java_home_dep.is_ee_implementor: + register_espresso_runtime_resources(register_project, register_distribution, _suite, java_home_dep, llvm_java_home_dep) + + register_distribution(mx_pomdistribution.POMDistribution( + _suite, "JAVA_COMMUNITY", [], + [ + "ESPRESSO", + "ESPRESSO_LIBS_RESOURCES", + "truffle:TRUFFLE_NFI_LIBFFI", + "truffle:TRUFFLE_RUNTIME", + # sulong is not strictly required, but it'll work out of the box in more cases if it's there + "sulong:LLVM_NATIVE_COMMUNITY", + ] + ([] if java_home_dep.is_ee_implementor else ["ESPRESSO_RUNTIME_RESOURCES"]), + None, + description="Java on Truffle (aka Espresso): a Java bytecode interpreter", + maven={ + "groupId" : "org.graalvm.polyglot", + "artifactId": "java-community", + "tag": ["default", "public"], + }, + )) + + +def register_espresso_runtime_resources(register_project, register_distribution, suite, java_home_dep, llvm_java_home_dep): + if llvm_java_home_dep: + lib_prefix = mx.add_lib_prefix('') + lib_suffix = mx.add_lib_suffix('') + jdk_lib_dir = 'bin' if mx.is_windows() else 'lib' + + if mx.get_env("SKIP_ESPRESSO_LLVM_CHECK", 'false').lower() in ('false', '0', 'no'): + libjava = join(llvm_java_home_dep.java_home, jdk_lib_dir, f'{lib_prefix}java{lib_suffix}') + if mx.is_linux(): + objdump = shutil.which('objdump') + if objdump: + objdump_out = subprocess.check_output(['objdump', '-h', libjava]).decode('utf-8') + if 'llvmbc' not in objdump_out: + raise mx.abort(f"Cannot find LLVM bitcode in provided Espresso LLVM JAVA_HOME ({libjava})") + elif mx.is_continuous_integration(): + raise mx.abort("objdump not found on the PATH. It is required to verify the Espresso LLVM JAVA_HOME") + elif mx.is_darwin(): + otool = shutil.which('otool') + if otool: + otool_out = subprocess.check_output(['otool', '-l', libjava]).decode('utf-8') + if '__LLVM' not in otool_out: + raise mx.abort(f"Cannot find LLVM bitcode in provided Espresso LLVM JAVA_HOME ({libjava})") + elif mx.is_continuous_integration(): + raise mx.abort("otool not found on the PATH. It is required to verify the Espresso LLVM JAVA_HOME") + if java_home_dep.is_ee_implementor != llvm_java_home_dep.is_ee_implementor: + raise mx.abort("The implementors for ESPRESSO's JAVA_HOME and LLVM JAVA_HOME don't match") llvm_runtime_dir = { "source_type": "dependency", - "dependency": "LLVM_JAVA_HOME", - "path": "lib/", + "dependency": "espresso:LLVM_JAVA_HOME", + "path": f"{jdk_lib_dir}/", } - register_project(JavaHomeDependency(_suite, "LLVM_JAVA_HOME", espresso_llvm_java_home)) else: llvm_runtime_dir = [] - espresso_java_home = mx.get_env('ESPRESSO_JAVA_HOME') or mx_sdk_vm.base_jdk().home - register_project(JavaHomeDependency(_suite, "JAVA_HOME", espresso_java_home)) if mx.is_windows(): platform_specific_excludes = [ "bin/", @@ -308,54 +383,191 @@ def mx_register_dynamic_suite_constituents(register_project, register_distributi "lib/", "man", ] - register_distribution(mx.LayoutDirDistribution(_suite, "ESPRESSO_RUNTIME_DIR", - deps=[], - layout={ - "META-INF/resources/java/espresso-runtime///": { - "source_type": "dependency", - "dependency": "JAVA_HOME", - "path": "*", - "exclude": [ - "include", - "jmods", - "lib/ct.sym", - "lib/jfr", - "lib/jvm.cfg", - "lib/src.zip", - "lib/static", - ] + platform_specific_excludes, - }, - "META-INF/resources/java/espresso-runtime///lib/llvm/": llvm_runtime_dir, - }, - path=None, - platformDependent=True, - platforms=[ - "linux-amd64", - "linux-aarch64", - "darwin-amd64", - "darwin-aarch64", - "windows-amd64", - ], - theLicense=None, - hashEntry="META-INF/resources/java/espresso-runtime///sha256", - fileListEntry="META-INF/resources/java/espresso-runtime///files", - maven=False)) - - -class JavaHomeDependency(mx.ArchivableProject): + register_distribution(mx.LayoutDirDistribution( + suite, "ESPRESSO_RUNTIME_DIR", + deps=[], + layout={ + "META-INF/resources/java/espresso-runtime///": { + "source_type": "dependency", + "dependency": "espresso:JAVA_HOME", + "path": "*", + "exclude": [ + "include", + "jmods", + "lib/ct.sym", + "lib/jfr", + "lib/jvm.cfg", + "lib/src.zip", + "lib/static", + ] + platform_specific_excludes, + }, + "META-INF/resources/java/espresso-runtime///lib/llvm/": llvm_runtime_dir, + }, + path=None, + platformDependent=True, + platforms=[ + "linux-amd64", + "linux-aarch64", + "darwin-amd64", + "darwin-aarch64", + "windows-amd64", + ], + theLicense=_resource_license(java_home_dep.is_ee_implementor), + hashEntry="META-INF/resources/java/espresso-runtime///sha256", + fileListEntry="META-INF/resources/java/espresso-runtime///files", + maven=False)) + + if register_project: + # com.oracle.truffle.espresso.resources.runtime + register_project(EspressoRuntimeResourceProject(suite, 'src', '?', suite.defaultLicense)) + + register_distribution(mx_jardistribution.JARDistribution( + suite, "ESPRESSO_RUNTIME_RESOURCES", None, None, None, + moduleInfo={ + "name": "org.graalvm.espresso.resources.runtime", + }, + deps=[ + "com.oracle.truffle.espresso.resources.runtime", + "ESPRESSO_RUNTIME_DIR", + ], + mainClass=None, + excludedLibs=[], + distDependencies=["truffle:TRUFFLE_API"], + javaCompliance=None, + platformDependent=True, + theLicense=_resource_license(java_home_dep.is_ee_implementor), + compress=True, + useModulePath=True, + description="Runtime environment used by the Java on Truffle (aka Espresso) implementation", + maven={ + "groupId": "org.graalvm.espresso", + "tag": ["default", "public"], + })) + + +class JavaHomeDependency(mx.BaseLibrary): def __init__(self, suite, name, java_home): - super().__init__(suite, name, deps=[], workingSets=[], theLicense=_jdk_license(java_home)) assert isabs(java_home) self.java_home = java_home + self.is_ee_implementor = mx_sdk_vm.ee_implementor(java_home) + if self.is_ee_implementor: + the_license = "Oracle Proprietary" + else: + the_license = "GPLv2-CPE" + super().__init__(suite, name, optional=False, theLicense=the_license) + self.deps = [] - def output_dir(self): - return self.java_home + def is_available(self): + return True - def archive_prefix(self): - return "" + def getBuildTask(self, args): + return mx.ArchivableBuildTask(self, args, 1) def getResults(self): - return JavaHomeDependency.walk(self.java_home) + for root, _, files in os.walk(self.java_home): + for name in files: + yield join(root, name) + + def getArchivableResults(self, use_relpath=True, single=False): + if single: + raise ValueError("single not supported") + for path in self.getResults(): + if use_relpath: + arcname = relpath(path, self.java_home) + else: + arcname = basename(path) + yield path, arcname + + def post_init(self): + pass # help act like a distribution since this is registered as a distribution + + def archived_deps(self): + return [] # help act like a distribution since this is registered as a distribution + + +class EspressoRuntimeResourceProject(mx.JavaProject): + def __init__(self, suite, subDir, runtime_type, theLicense): + name = f'com.oracle.truffle.espresso.resources.runtime' + project_dir = join(suite.dir, subDir, name) + deps = ['truffle:TRUFFLE_API'] + super().__init__(suite, name, subDir=subDir, srcDirs=[], deps=deps, + javaCompliance='17+', workingSets='Truffle', d=project_dir, + theLicense=theLicense) + self.declaredAnnotationProcessors = ['truffle:TRUFFLE_DSL_PROCESSOR'] + self.resource_id = "espresso-runtime" + self.checkstyleProj = name + self.checkPackagePrefix = False + + def getBuildTask(self, args): + jdk = mx.get_jdk(self.javaCompliance, tag=mx.DEFAULT_JDK_TAG, purpose='building ' + self.name) + return EspressoRuntimeResourceBuildTask(args, self, jdk) + + +class EspressoRuntimeResourceBuildTask(mx.JavaBuildTask): + def __str__(self): + return f'Generating {self.subject.name} internal resource and compiling it with {self._getCompiler().name()}' + + @staticmethod + def _template_file(): + return join(_suite.mxDir, 'espresso_runtime_resource.template') + + def needsBuild(self, newestInput): + is_needed, reason = mx.ProjectBuildTask.needsBuild(self, newestInput) + if is_needed: + return True, reason + proj = self.subject + for outDir in [proj.output_dir(), proj.source_gen_dir()]: + if not os.path.exists(outDir): + return True, f"{outDir} does not exist" + template_ts = mx.TimeStampFile.newest([ + EspressoRuntimeResourceBuildTask._template_file(), + __file__ + ]) + if newestInput is None or newestInput.isOlderThan(template_ts): + newestInput = template_ts + return super().needsBuild(newestInput) + + @staticmethod + def _target_file(root, pkg_name): + target_folder = join(root, pkg_name.replace('.', os.sep)) + target_file = join(target_folder, 'EspressoRuntimeResource.java') + return target_file + + + def _collect_files(self): + if self._javafiles is not None: + # already collected + return self + # collect project files first, then extend with generated resource + super(EspressoRuntimeResourceBuildTask, self)._collect_files() + javafiles = self._javafiles + prj = self.subject + gen_src_dir = prj.source_gen_dir() + pkg_name = prj.name + target_file = EspressoRuntimeResourceBuildTask._target_file(gen_src_dir, pkg_name) + if not target_file in javafiles: + bin_dir = prj.output_dir() + target_class = join(bin_dir, relpath(target_file, gen_src_dir)[:-len('.java')] + '.class') + javafiles[target_file] = target_class + # Remove annotation processor generated files. + javafiles = {k: v for k, v in javafiles.items() if k == target_file} + self._javafiles = javafiles + return self + + def build(self): + prj = self.subject + pkg_name = prj.name + with open(EspressoRuntimeResourceBuildTask._template_file(), 'r', encoding='utf-8') as f: + file_content = f.read() + subst_eng = mx_subst.SubstitutionEngine() + subst_eng.register_no_arg('package', pkg_name) + subst_eng.register_no_arg('resourceId', prj.resource_id) + file_content = subst_eng.substitute(file_content) + target_file = EspressoRuntimeResourceBuildTask._target_file(prj.source_gen_dir(), pkg_name) + mx.ensure_dir_exists(dirname(target_file)) + with mx.SafeFileCreation(target_file) as sfc, open(sfc.tmpPath, 'w', encoding='utf-8') as f: + f.write(file_content) + super(EspressoRuntimeResourceBuildTask, self).build() mx_sdk_vm.register_graalvm_component(mx_sdk_vm.GraalVmJreComponent( diff --git a/espresso/mx.espresso/suite.py b/espresso/mx.espresso/suite.py index b4fbe63d2b7e..af864eac0731 100644 --- a/espresso/mx.espresso/suite.py +++ b/espresso/mx.espresso/suite.py @@ -24,7 +24,7 @@ suite = { "mxversion": "6.44.0", "name": "espresso", - "version" : "24.0.1.1", + "version" : "24.0.2.0", "release" : False, "groupId" : "org.graalvm.espresso", "url" : "https://www.graalvm.org/reference-manual/java-on-truffle/", @@ -140,17 +140,6 @@ "checkstyle": "com.oracle.truffle.espresso", }, - "com.oracle.truffle.espresso.resources.runtime": { - "subDir": "src", - "sourceDirs": ["src"], - "dependencies": [ - "truffle:TRUFFLE_API", - ], - "annotationProcessors": ["truffle:TRUFFLE_DSL_PROCESSOR"], - "javaCompliance": "17+", - "checkstyle": "com.oracle.truffle.espresso", - }, - "com.oracle.truffle.espresso.processor": { "subDir": "src", "sourceDirs": ["src"], @@ -350,26 +339,6 @@ "noMavenJavadoc": True, }, - "JAVA_COMMUNITY": { - "type": "pom", - "runtimeDependencies": [ - "ESPRESSO", - "ESPRESSO_LIBS_RESOURCES", - "ESPRESSO_RUNTIME_RESOURCES", - "truffle:TRUFFLE_NFI_LIBFFI", - "truffle:TRUFFLE_RUNTIME", - # sulong is not strictly required but it'll work out of the box in more cases if it's there - "sulong:SULONG_NFI", - "sulong:SULONG_NATIVE", - ], - "description": "Java on Truffle (aka Espresso): a Java bytecode interpreter", - "maven": { - "groupId" : "org.graalvm.polyglot", - "artifactId": "java-community", - "tag": ["default", "public"], - }, - }, - "ESPRESSO_LAUNCHER": { "subDir": "src", "dependencies": [ @@ -485,26 +454,6 @@ "maven": False, }, - "ESPRESSO_RUNTIME_RESOURCES": { - "platformDependent": True, - "moduleInfo": { - "name": "org.graalvm.espresso.resources.runtime", - }, - "distDependencies": [ - "truffle:TRUFFLE_API", - ], - "dependencies": [ - "com.oracle.truffle.espresso.resources.runtime", - "ESPRESSO_RUNTIME_DIR", - ], - "compress": True, - "useModulePath": True, - "description": "Runtime environment used by the Java on Truffle (aka Espresso) implementation", - "maven" : { - "tag": ["default", "public"], - }, - }, - "ESPRESSO_SUPPORT": { "native": True, "description": "Espresso support distribution for the GraalVM (in espresso home)", diff --git a/espresso/src/com.oracle.truffle.espresso.jdwp/src/com/oracle/truffle/espresso/jdwp/impl/DebuggerController.java b/espresso/src/com.oracle.truffle.espresso.jdwp/src/com/oracle/truffle/espresso/jdwp/impl/DebuggerController.java index 00768b728761..dca41d3e3c23 100644 --- a/espresso/src/com.oracle.truffle.espresso.jdwp/src/com/oracle/truffle/espresso/jdwp/impl/DebuggerController.java +++ b/espresso/src/com.oracle.truffle.espresso.jdwp/src/com/oracle/truffle/espresso/jdwp/impl/DebuggerController.java @@ -362,33 +362,23 @@ public Object[] getVisibleGuestThreads() { return visibleThreads.toArray(new Object[visibleThreads.size()]); } - public void resumeAll(boolean sessionClosed) { - Object eventThread = null; - - // The order of which to resume threads is not specified, however when RESUME_ALL command is - // sent while performing a stepping request, some debuggers (IntelliJ is a known case) will - // expect all other threads but the current stepping thread to be resumed first. + void forceResumeAll() { for (Object thread : getVisibleGuestThreads()) { boolean resumed = false; SimpleLock suspendLock = getSuspendLock(thread); synchronized (suspendLock) { while (!resumed) { - if (isStepping(thread)) { - eventThread = thread; - break; - } else { - resumed = resume(thread, sessionClosed); - } + resumed = resume(thread, true); } } } - if (eventThread != null) { - boolean resumed = false; - SimpleLock suspendLock = getSuspendLock(eventThread); + } + + public void resumeAll() { + for (Object thread : getVisibleGuestThreads()) { + SimpleLock suspendLock = getSuspendLock(thread); synchronized (suspendLock) { - while (!resumed) { - resumed = resume(eventThread, sessionClosed); - } + resume(thread, false); } } } @@ -475,10 +465,6 @@ private String getThreadName(Object thread) { return getContext().getThreadName(thread); } - private boolean isStepping(Object thread) { - return commandRequestIds.get(thread) != null; - } - public void disposeDebugger(boolean prepareReconnect) { if (!prepareReconnect) { // OK, we're closing down the context which is equivalent @@ -672,7 +658,7 @@ private void checkThreadJobsAndRun(Object thread, boolean forceSuspend) { // suspend all other threads after the invocation for (Object activeThread : allThreads) { if (activeThread != thread) { - suspend(thread); + suspend(activeThread); } } } else { diff --git a/espresso/src/com.oracle.truffle.espresso.jdwp/src/com/oracle/truffle/espresso/jdwp/impl/JDWP.java b/espresso/src/com.oracle.truffle.espresso.jdwp/src/com/oracle/truffle/espresso/jdwp/impl/JDWP.java index 01083bd660e6..2f8c70cbbbdc 100644 --- a/espresso/src/com.oracle.truffle.espresso.jdwp/src/com/oracle/truffle/espresso/jdwp/impl/JDWP.java +++ b/espresso/src/com.oracle.truffle.espresso.jdwp/src/com/oracle/truffle/espresso/jdwp/impl/JDWP.java @@ -213,7 +213,7 @@ static CommandResult createReply(Packet packet, DebuggerController controller) { controller.fine(() -> "Resume all packet"); PacketStream reply = new PacketStream().replyPacket().id(packet.id); - controller.resumeAll(false); + controller.resumeAll(); return new CommandResult(reply); } } diff --git a/espresso/src/com.oracle.truffle.espresso.jdwp/src/com/oracle/truffle/espresso/jdwp/impl/JDWPInstrument.java b/espresso/src/com.oracle.truffle.espresso.jdwp/src/com/oracle/truffle/espresso/jdwp/impl/JDWPInstrument.java index 5226dec0fc54..5a73610c9fa6 100644 --- a/espresso/src/com.oracle.truffle.espresso.jdwp/src/com/oracle/truffle/espresso/jdwp/impl/JDWPInstrument.java +++ b/espresso/src/com.oracle.truffle.espresso.jdwp/src/com/oracle/truffle/espresso/jdwp/impl/JDWPInstrument.java @@ -104,7 +104,7 @@ public void reset(boolean prepareForReconnect) { controller.endSession(); // resume all threads - controller.resumeAll(true); + controller.forceResumeAll(); if (prepareForReconnect) { // replace the controller instance diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/EspressoLanguage.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/EspressoLanguage.java index b8b49d2b7552..aec442163a39 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/EspressoLanguage.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/EspressoLanguage.java @@ -27,6 +27,7 @@ import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; +import java.nio.file.Paths; import java.util.Objects; import java.util.concurrent.TimeUnit; import java.util.logging.Level; @@ -44,6 +45,7 @@ import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; import com.oracle.truffle.api.ContextThreadLocal; import com.oracle.truffle.api.TruffleContext; +import com.oracle.truffle.api.TruffleFile; import com.oracle.truffle.api.TruffleLanguage; import com.oracle.truffle.api.TruffleLanguage.Registration; import com.oracle.truffle.api.TruffleSafepoint; @@ -77,6 +79,7 @@ import com.oracle.truffle.espresso.runtime.EspressoThreadLocalState; import com.oracle.truffle.espresso.runtime.GuestAllocator; import com.oracle.truffle.espresso.runtime.JavaVersion; +import com.oracle.truffle.espresso.runtime.OS; import com.oracle.truffle.espresso.runtime.staticobject.StaticObject; import com.oracle.truffle.espresso.runtime.staticobject.StaticObject.StaticObjectFactory; import com.oracle.truffle.espresso.substitutions.Substitutions; @@ -590,9 +593,26 @@ public static Path getEspressoRuntime(TruffleLanguage.Env env) { } } try { - Path resources = Path.of(env.getInternalResource("espresso-runtime").getAbsoluteFile().toString()); - assert Files.isDirectory(resources); - return resources; + if (env.getOptions().hasBeenSet(EspressoOptions.JavaHome)) { + // This option's value will be used, no need to guess + return null; + } + TruffleFile resource = env.getInternalResource("espresso-runtime"); + if (resource != null) { + Path resources = Path.of(resource.getAbsoluteFile().toString()); + assert Files.isDirectory(resources); + return resources; + } + if (OS.getCurrent() == OS.Linux && JavaVersion.HOST_VERSION.compareTo(JavaVersion.latestSupported()) <= 0) { + if (!EspressoOptions.RUNNING_ON_SVM || (boolean) env.getConfig().getOrDefault("preinit", false)) { + // we might be able to use the host runtime libraries + env.getLogger(EspressoContext.class).info("Trying to use the host's runtime libraries"); + return Paths.get(System.getProperty("java.home")); + } + } + throw EspressoError.fatal("Couldn't find suitable runtime libraries for espresso. You can try to\n" + + "add a jar with the necessary resources such as org.graalvm.espresso:espresso-runtime-resources-*,\n" + + "or set java.JavaHome explicitly."); } catch (IOException e) { throw EspressoError.shouldNotReachHere(e); } diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/Klass.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/Klass.java index 81d9984113b6..c476c7321b04 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/Klass.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/Klass.java @@ -57,7 +57,7 @@ import com.oracle.truffle.api.library.ExportMessage; import com.oracle.truffle.api.nodes.ExplodeLoop; import com.oracle.truffle.api.nodes.Node; -import com.oracle.truffle.api.profiles.BranchProfile; +import com.oracle.truffle.api.profiles.InlinedBranchProfile; import com.oracle.truffle.api.utilities.TriState; import com.oracle.truffle.espresso.EspressoLanguage; import com.oracle.truffle.espresso.classfile.ConstantPool; @@ -81,14 +81,10 @@ import com.oracle.truffle.espresso.meta.Meta; import com.oracle.truffle.espresso.meta.MetaUtil; import com.oracle.truffle.espresso.meta.ModifiersProvider; -import com.oracle.truffle.espresso.nodes.interop.InvokeEspressoNode; -import com.oracle.truffle.espresso.nodes.interop.InvokeEspressoNodeGen; import com.oracle.truffle.espresso.nodes.interop.LookupDeclaredMethod; import com.oracle.truffle.espresso.nodes.interop.LookupDeclaredMethodNodeGen; import com.oracle.truffle.espresso.nodes.interop.LookupFieldNode; import com.oracle.truffle.espresso.nodes.interop.LookupFieldNodeGen; -import com.oracle.truffle.espresso.nodes.interop.OverLoadedMethodSelectorNode; -import com.oracle.truffle.espresso.nodes.interop.OverLoadedMethodSelectorNodeGen; import com.oracle.truffle.espresso.nodes.interop.ToEspressoNode; import com.oracle.truffle.espresso.nodes.interop.ToEspressoNodeFactory; import com.oracle.truffle.espresso.nodes.interop.ToPrimitive; @@ -102,6 +98,8 @@ import com.oracle.truffle.espresso.runtime.MethodHandleIntrinsics; import com.oracle.truffle.espresso.runtime.dispatch.staticobject.BaseInterop; import com.oracle.truffle.espresso.runtime.dispatch.staticobject.EspressoInterop; +import com.oracle.truffle.espresso.runtime.dispatch.staticobject.InteropLookupAndInvoke; +import com.oracle.truffle.espresso.runtime.dispatch.staticobject.InteropLookupAndInvokeFactory; import com.oracle.truffle.espresso.runtime.staticobject.StaticObject; import com.oracle.truffle.espresso.substitutions.JavaType; import com.oracle.truffle.espresso.vm.InterpreterToVM; @@ -179,15 +177,18 @@ abstract static class ReadMember { @Specialization(guards = "language.isShared()") static Object doShared(Klass receiver, String member, @CachedLibrary("receiver") InteropLibrary lib, + @Bind("$node") Node node, + @Cached @Shared InlinedBranchProfile error, @Bind("getLang(lib)") @SuppressWarnings("unused") EspressoLanguage language) throws UnknownIdentifierException { - return readMember(receiver, member, LookupFieldNodeGen.getUncached(), LookupDeclaredMethodNodeGen.getUncached(), BranchProfile.getUncached(), lib, language); + return readMember(receiver, member, LookupFieldNodeGen.getUncached(), LookupDeclaredMethodNodeGen.getUncached(), node, error, lib, language); } @Specialization static Object readMember(Klass receiver, String member, @Shared("lookupField") @Cached LookupFieldNode lookupFieldNode, @Shared("lookupMethod") @Cached LookupDeclaredMethod lookupMethod, - @Shared("error") @Cached BranchProfile error, + @Bind("$node") Node node, + @Cached @Shared InlinedBranchProfile error, @CachedLibrary("receiver") InteropLibrary lib, @Bind("getLang(lib)") @SuppressWarnings("unused") EspressoLanguage language) throws UnknownIdentifierException { EspressoContext ctx = EspressoContext.get(lib); @@ -229,7 +230,7 @@ static Object readMember(Klass receiver, String member, return receiver.getSuperKlass(); } - error.enter(); + error.enter(node); throw UnknownIdentifierException.create(member); } } @@ -263,24 +264,26 @@ abstract static class WriteMember { // Specialization prevents caching a node that would leak the context @Specialization(guards = "language.isShared()") static void doShared(Klass receiver, String member, Object value, - @Shared("error") @Cached BranchProfile error, + @Bind("$node") Node node, + @Cached @Shared InlinedBranchProfile error, @CachedLibrary("receiver") @SuppressWarnings("unused") InteropLibrary lib, @Bind("getLang(lib)") @SuppressWarnings("unused") EspressoLanguage language) throws UnknownIdentifierException, UnsupportedTypeException { - writeMember(receiver, member, value, LookupFieldNodeGen.getUncached(), ToEspressoNodeFactory.DynamicToEspressoNodeGen.getUncached(), error); + writeMember(receiver, member, value, LookupFieldNodeGen.getUncached(), ToEspressoNodeFactory.DynamicToEspressoNodeGen.getUncached(), node, error); } @Specialization static void writeMember(Klass receiver, String member, Object value, @Shared("lookupField") @Cached LookupFieldNode lookupFieldNode, @Exclusive @Cached ToEspressoNode.DynamicToEspresso toEspressoNode, - @Shared("error") @Cached BranchProfile error) throws UnknownIdentifierException, UnsupportedTypeException { + @Bind("$node") Node node, + @Cached @Shared InlinedBranchProfile error) throws UnknownIdentifierException, UnsupportedTypeException { Field field = lookupFieldNode.execute(receiver, member, true); // Can only write to non-final fields. if (field != null && !field.isFinalFlagSet()) { Object espressoValue = toEspressoNode.execute(value, field.resolveTypeKlass()); field.set(receiver.tryInitializeAndGetStatics(), espressoValue); } else { - error.enter(); + error.enter(node); throw UnknownIdentifierException.create(member); } } @@ -308,35 +311,27 @@ abstract static class InvokeMember { // Specialization prevents caching a node that would leak the context @Specialization(guards = "language.isShared()") static Object doShared(Klass receiver, String member, Object[] arguments, + @Bind("$node") Node node, + @Cached @Shared InlinedBranchProfile error, @CachedLibrary("receiver") @SuppressWarnings("unused") InteropLibrary receiverInterop, @Bind("getLang(receiverInterop)") @SuppressWarnings("unused") EspressoLanguage language) throws ArityException, UnknownIdentifierException, UnsupportedTypeException { - return invokeMember(receiver, member, arguments, receiverInterop, LookupDeclaredMethodNodeGen.getUncached(), OverLoadedMethodSelectorNodeGen.getUncached(), - InvokeEspressoNodeGen.getUncached(), - ToEspressoNodeFactory.DynamicToEspressoNodeGen.getUncached()); + return invokeMember(receiver, member, arguments, node, receiverInterop, error, InteropLookupAndInvokeFactory.NonVirtualNodeGen.getUncached()); } @Specialization static Object invokeMember(Klass receiver, String member, Object[] arguments, + @Bind("$node") Node node, @CachedLibrary("receiver") InteropLibrary receiverInterop, - @Cached LookupDeclaredMethod lookupMethod, - @Cached OverLoadedMethodSelectorNode overloadSelector, - @Exclusive @Cached InvokeEspressoNode invoke, - @Cached ToEspressoNode.DynamicToEspresso toEspressoNode) + @Cached InlinedBranchProfile error, + @Cached InteropLookupAndInvoke.NonVirtual lookupAndInvoke) throws ArityException, UnknownIdentifierException, UnsupportedTypeException { if (!receiverInterop.isMemberInvocable(receiver, member)) { // Not invocable, no matter the arity or argument types. + error.enter(node); throw UnknownIdentifierException.create(member); } - // The member (static method) may be invocable only for a certain arity and argument - // types. - Method[] candidates = lookupMethod.execute(receiver, member, true, true, arguments.length); - if (candidates != null) { - return EspressoInterop.invokeEspressoMethodHelper(null, member, arguments, overloadSelector, invoke, toEspressoNode, candidates); - } - // TODO(peterssen): The expected arity is not known, only that the given one is not - // correct. - throw ArityException.create(arguments.length + 1, -1, arguments.length); + return lookupAndInvoke.execute(null, receiver, arguments, member); } } @@ -415,6 +410,7 @@ abstract static class Instantiate { @Specialization(guards = "language.isShared()") static Object doShared(Klass receiver, Object[] args, @CachedLibrary("receiver") @SuppressWarnings("unused") InteropLibrary receiverInterop, + @Bind("$node") Node node, @Bind("getLang(receiverInterop)") @SuppressWarnings("unused") EspressoLanguage language) throws UnsupportedMessageException, UnsupportedTypeException, ArityException { if (receiver.isPrimitive()) { return doPrimitive(receiver, args); @@ -426,11 +422,10 @@ static Object doShared(Klass receiver, Object[] args, return doReferenceArray(receiver, args, ToPrimitiveFactory.ToIntNodeGen.getUncached()); } if (isMultidimensionalArray(receiver)) { - return doMultidimensionalArray(receiver, args, ToPrimitiveFactory.ToIntNodeGen.getUncached()); + return doMultidimensionalArray(receiver, args, node, InlinedBranchProfile.getUncached(), ToPrimitiveFactory.ToIntNodeGen.getUncached()); } if (isObjectKlass(receiver)) { - return doObject(receiver, args, receiverInterop, LookupDeclaredMethodNodeGen.getUncached(), OverLoadedMethodSelectorNodeGen.getUncached(), InvokeEspressoNodeGen.getUncached(), - ToEspressoNodeFactory.DynamicToEspressoNodeGen.getUncached()); + return doObject(receiver, args, node, InlinedBranchProfile.getUncached(), receiverInterop, InteropLookupAndInvokeFactory.NonVirtualNodeGen.getUncached()); } CompilerDirectives.transferToInterpreterAndInvalidate(); throw EspressoError.shouldNotReachHere(); @@ -498,10 +493,13 @@ static StaticObject doReferenceArray(Klass receiver, Object[] arguments, @Specialization(guards = "isMultidimensionalArray(receiver)") static StaticObject doMultidimensionalArray(Klass receiver, Object[] arguments, + @Bind("$node") Node node, + @Cached InlinedBranchProfile error, @Cached ToPrimitive.ToInt toInt) throws ArityException, UnsupportedTypeException { ArrayKlass arrayKlass = (ArrayKlass) receiver; assert arrayKlass.getElementalType().getJavaKind() != JavaKind.Void; if (arrayKlass.getDimension() != arguments.length) { + error.enter(node); throw ArityException.create(arrayKlass.getDimension(), arrayKlass.getDimension(), arguments.length); } EspressoContext context = EspressoContext.get(toInt); @@ -517,24 +515,18 @@ static StaticObject doMultidimensionalArray(Klass receiver, Object[] arguments, @Specialization(guards = "isObjectKlass(receiver)") static Object doObject(Klass receiver, Object[] arguments, + @Bind("$node") Node node, + @Cached InlinedBranchProfile error, @CachedLibrary("receiver") InteropLibrary receiverInterop, - @Shared("lookupMethod") @Cached LookupDeclaredMethod lookupMethod, - @Cached OverLoadedMethodSelectorNode overloadSelector, - @Exclusive @Cached InvokeEspressoNode invoke, - @Cached ToEspressoNode.DynamicToEspresso toEspressoNode) throws UnsupportedTypeException, ArityException, UnsupportedMessageException { + @Cached InteropLookupAndInvoke.NonVirtual lookupAndInvoke) throws UnsupportedTypeException, ArityException, UnsupportedMessageException { if (!receiverInterop.isInstantiable(receiver)) { + error.enter(node); throw UnsupportedMessageException.create(); } ObjectKlass objectKlass = (ObjectKlass) receiver; - Method[] initCandidates = lookupMethod.execute(receiver, INIT_NAME, true, false, arguments.length); - if (initCandidates != null) { - StaticObject instance = allocateNewInstance(EspressoContext.get(invoke), objectKlass); - EspressoInterop.invokeEspressoMethodHelper(instance, INIT_NAME, arguments, overloadSelector, invoke, toEspressoNode, initCandidates); - return instance; - } - // TODO(peterssen): We don't know the expected arity of this method, only that the given - // arity is incorrect. - throw ArityException.create(arguments.length + 1, -1, arguments.length); + StaticObject instance = allocateNewInstance(EspressoContext.get(lookupAndInvoke), objectKlass); + lookupAndInvoke.execute(instance, objectKlass, arguments, INIT_NAME); + return instance; } private static StaticObject allocateNewInstance(EspressoContext context, ObjectKlass objectKlass) { diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/meta/Meta.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/meta/Meta.java index 3c118483e58e..cc85d8e4d902 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/meta/Meta.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/meta/Meta.java @@ -2135,7 +2135,6 @@ public Klass resolveSymbolAndAccessCheck(Symbol type, Klass accessingKlass return klass; } - @TruffleBoundary public String toHostString(StaticObject str) { if (StaticObject.isNull(str)) { return null; @@ -2143,6 +2142,13 @@ public String toHostString(StaticObject str) { return stringConversion.toHost(str, getLanguage(), this); } + public StaticObject toGuestString(String hostString) { + if (hostString == null) { + return StaticObject.NULL; + } + return stringConversion.toGuest(hostString, this); + } + @TruffleBoundary public static String toHostStringStatic(StaticObject str) { if (StaticObject.isNull(str)) { @@ -2159,14 +2165,6 @@ public StaticObject toGuestString(Symbol hostString) { return toGuestString(hostString.toString()); } - @TruffleBoundary - public StaticObject toGuestString(String hostString) { - if (hostString == null) { - return StaticObject.NULL; - } - return stringConversion.toGuest(hostString, this); - } - public static boolean isString(Object string) { if (string instanceof StaticObject) { StaticObject staticObject = (StaticObject) string; diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/meta/StringConversion.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/meta/StringConversion.java index 04ea79e364ee..88f65c269411 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/meta/StringConversion.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/meta/StringConversion.java @@ -23,56 +23,104 @@ package com.oracle.truffle.espresso.meta; +import java.lang.reflect.Field; +import java.nio.charset.StandardCharsets; + +import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; import com.oracle.truffle.espresso.EspressoLanguage; import com.oracle.truffle.espresso.EspressoOptions; +import com.oracle.truffle.espresso.impl.SuppressFBWarnings; import com.oracle.truffle.espresso.runtime.EspressoContext; import com.oracle.truffle.espresso.runtime.staticobject.StaticObject; import com.oracle.truffle.espresso.vm.UnsafeAccess; import sun.misc.Unsafe; -public abstract class StringConversion { +public final class StringConversion { private static final Unsafe UNSAFE = UnsafeAccess.get(); - private static final class Offsets { + private static final class HostConstants { static final long hostValueOffset; static final long hostHashOffset; static final long hostCoderOffset; + // Static field COMPACT_STRINGS to check for host usage of compact strings. + static final boolean hostCompactStrings; + static final byte LATIN1; + static final byte UTF16; + @SuppressWarnings("deprecation") private static long getStringFieldOffset(String name) throws NoSuchFieldException { // TODO replace with TruffleString? return UNSAFE.objectFieldOffset(String.class.getDeclaredField(name)); } + @SuppressWarnings("deprecation") + private static boolean hostUsesCompact() { + try { + Field compactStringsField = String.class.getDeclaredField("COMPACT_STRINGS"); + return UNSAFE.getBoolean(UNSAFE.staticFieldBase(compactStringsField), UNSAFE.staticFieldOffset(compactStringsField)); + } catch (NoSuchFieldException e) { + throw EspressoError.shouldNotReachHere(e); + } + } + + @SuppressWarnings("deprecation") + private static byte hostCoderValue(String name) { + try { + Field compactStringsField = String.class.getDeclaredField(name); + return UNSAFE.getByte(UNSAFE.staticFieldBase(compactStringsField), UNSAFE.staticFieldOffset(compactStringsField)); + } catch (NoSuchFieldException e) { + throw EspressoError.shouldNotReachHere(e); + } + } + static { try { hostValueOffset = getStringFieldOffset("value"); hostHashOffset = getStringFieldOffset("hash"); hostCoderOffset = getStringFieldOffset("coder"); + + hostCompactStrings = hostUsesCompact(); + LATIN1 = hostCoderValue("LATIN1"); + UTF16 = hostCoderValue("UTF16"); } catch (NoSuchFieldException e) { throw EspressoError.shouldNotReachHere(e); } } } - public abstract String toHost(StaticObject str, EspressoLanguage language, Meta meta); + private final MaybeCopy maybeCopy; + private final FromGuest fromGuest; + private final ToHost toHost; + private final ToGuest toGuest; - public abstract StaticObject toGuest(String str, Meta meta); + public StringConversion(MaybeCopy maybeCopy, FromGuest fromGuest, ToGuest toGuest, ToHost toHost) { + this.maybeCopy = maybeCopy; + this.fromGuest = fromGuest; + this.toHost = toHost; + this.toGuest = toGuest; + } + + public String toHost(StaticObject str, EspressoLanguage language, Meta meta) { + return fromGuest.extract(str, language, meta, maybeCopy) /*- Unpacks guest string into (bytes, hash, coder), copying if necessary. */ + .toHost(toHost); /*- Repacks, taking care whether host has compact strings enabled. */ + } - private StringConversion() { + public StaticObject toGuest(String str, Meta meta) { + return toGuest.hostToGuest(str, meta, maybeCopy); } static StringConversion select(EspressoContext context) { - if (context.getJavaVersion().compactStringsEnabled()) { - if (context.getEnv().getOptions().get(EspressoOptions.StringSharing)) { - return CompactToCompact.INSTANCE; - } else { - return CopyingCompactToCompact.INSTANCE; - } - } else { - return CharGuestCompactHost.INSTANCE; - } + boolean sharing = context.getEnv().getOptions().get(EspressoOptions.StringSharing); + boolean compactGuest = context.getJavaVersion().compactStringsEnabled(); + boolean compactHost = HostConstants.hostCompactStrings; + + return new StringConversion( + sharing ? MaybeCopy.SHARING : MaybeCopy.NO_SHARING, + compactGuest ? FromGuest.FROM_COMPACT : FromGuest.FROM_NOT_COMPACT, + compactGuest ? ToGuest.TO_COMPACT : ToGuest.TO_NOT_COMPACT, + compactHost ? ToHost.COMPACT_ENABLED : ToHost.COMPACT_DISABLED); } private static String allocateHost() { @@ -100,15 +148,15 @@ private static byte extractGuestCoder(Meta meta, StaticObject str) { } private static byte[] extractHostBytes(String str) { - return (byte[]) UNSAFE.getObject(str, Offsets.hostValueOffset); + return (byte[]) UNSAFE.getObject(str, HostConstants.hostValueOffset); } private static int extractHostHash(String str) { - return UNSAFE.getInt(str, Offsets.hostHashOffset); + return UNSAFE.getInt(str, HostConstants.hostHashOffset); } private static byte extractHostCoder(String str) { - return UNSAFE.getByte(str, Offsets.hostCoderOffset); + return UNSAFE.getByte(str, HostConstants.hostCoderOffset); } private static StaticObject produceGuestString8(Meta meta, char[] value, int hash) { @@ -128,53 +176,88 @@ private static StaticObject produceGuestString11(Meta meta, byte[] value, int ha private static String produceHostString(byte[] value, int hash, byte coder) { String res = allocateHost(); - UNSAFE.putInt(res, Offsets.hostHashOffset, hash); - UNSAFE.putByte(res, Offsets.hostCoderOffset, coder); - UNSAFE.putObjectVolatile(res, Offsets.hostValueOffset, value); + UNSAFE.putInt(res, HostConstants.hostHashOffset, hash); + UNSAFE.putByte(res, HostConstants.hostCoderOffset, coder); + UNSAFE.putObjectVolatile(res, HostConstants.hostValueOffset, value); return res; } - private static final class CompactToCompact extends StringConversion { - - private static final StringConversion INSTANCE = new CompactToCompact(); - - @Override - public String toHost(StaticObject str, EspressoLanguage language, Meta meta) { - return produceHostString(extractGuestBytes11(language, meta, str), extractGuestHash(meta, str), extractGuestCoder(meta, str)); + // Helper classes. These help with not having to create 2^3 implementations of StringConversion. + + private interface FromGuest { + FromGuest FROM_COMPACT = (guest, language, meta, maybeCopy) -> new AlmostString(maybeCopy.maybeCopy( + extractGuestBytes11(language, meta, guest)), + extractGuestHash(meta, guest), + extractGuestCoder(meta, guest)); + + FromGuest FROM_NOT_COMPACT = (guest, language, meta, maybeCopy) -> { + /* + * Use host string building from char, then extract internals to obtain the intermediate + * structure. + * + * It will be used to create a new string again later, but PEA should be able to + * optimize that away, both for host and guest compilations. + */ + char[] chars = extractGuestChars8(language, meta, guest); + String host = newHostString(chars); + return new AlmostString(extractHostBytes(host), extractHostHash(host), extractHostCoder(host)); + }; + + @TruffleBoundary(allowInlining = true) + private static String newHostString(char[] chars) { + return new String(chars); } - @Override - public StaticObject toGuest(String str, Meta meta) { - return produceGuestString11(meta, extractHostBytes(str), extractHostHash(str), extractHostCoder(str)); - } + AlmostString extract(StaticObject guest, EspressoLanguage language, Meta meta, MaybeCopy maybeCopy); } - private static final class CopyingCompactToCompact extends StringConversion { + private interface MaybeCopy { + MaybeCopy NO_SHARING = byte[]::clone; + MaybeCopy SHARING = bytes -> bytes; - private static final StringConversion INSTANCE = new CopyingCompactToCompact(); + byte[] maybeCopy(byte[] bytes); + } - @Override - public String toHost(StaticObject str, EspressoLanguage language, Meta meta) { - return produceHostString(extractGuestBytes11(language, meta, str).clone(), extractGuestHash(meta, str), extractGuestCoder(meta, str)); - } + @SuppressFBWarnings(value = {"UCF"}, justification = "javac introduces a jump to next instruction in ") + private interface ToHost { + ToHost COMPACT_ENABLED = (almostString) -> produceHostString(almostString.bytes, almostString.hash, almostString.coder); + ToHost COMPACT_DISABLED = (almostString) -> { + if (almostString.coder == HostConstants.UTF16) { + // We already have an inflated string, we can re-use it as-is. + return produceHostString(almostString.bytes, almostString.hash, almostString.coder); + } else { + assert almostString.coder == HostConstants.LATIN1; + // Have to inflate from LATIN1. + return newStringFromLatin1(almostString.bytes); + } + }; - @Override - public StaticObject toGuest(String str, Meta meta) { - return produceGuestString11(meta, extractHostBytes(str).clone(), extractHostHash(str), extractHostCoder(str)); + @TruffleBoundary(allowInlining = true) + private static String newStringFromLatin1(byte[] bytes) { + return new String(bytes, StandardCharsets.ISO_8859_1 /*- LATIN1 */); } + + String toHost(AlmostString almostString); } - private static final class CharGuestCompactHost extends StringConversion { - private static final StringConversion INSTANCE = new CharGuestCompactHost(); + private interface ToGuest { + // Caveat here: if host is compact disabled, this may produce inflated guest string that + // could have been compacted. + ToGuest TO_COMPACT = (host, meta, maybeCopy) -> produceGuestString11(meta, maybeCopy.maybeCopy(extractHostBytes(host)), extractHostHash(host), extractHostCoder(host)); + + ToGuest TO_NOT_COMPACT = (host, meta, maybeCopy) -> produceGuestString8(meta, getCharArray(host), extractHostHash(host)); - @Override - public String toHost(StaticObject str, EspressoLanguage language, Meta meta) { - return new String(StringConversion.extractGuestChars8(language, meta, str)); + @TruffleBoundary(allowInlining = true) + private static char[] getCharArray(String host) { + return host.toCharArray(); } - @Override - public StaticObject toGuest(String str, Meta meta) { - return produceGuestString8(meta, str.toCharArray(), StringConversion.extractHostHash(str)); + StaticObject hostToGuest(String host, Meta meta, MaybeCopy maybeCopy); + } + + private record AlmostString(byte[] bytes, int hash, byte coder) { + public String toHost(ToHost toHost) { + return toHost.toHost(this); } } } diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/EspressoInstrumentableRootNodeImpl.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/EspressoInstrumentableRootNodeImpl.java index 2ee625ba3eff..42f2c821dbdb 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/EspressoInstrumentableRootNodeImpl.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/EspressoInstrumentableRootNodeImpl.java @@ -23,14 +23,12 @@ package com.oracle.truffle.espresso.nodes; import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; -import com.oracle.truffle.api.frame.Frame; import com.oracle.truffle.api.instrumentation.StandardTags; import com.oracle.truffle.api.instrumentation.Tag; import com.oracle.truffle.api.source.Source; import com.oracle.truffle.api.source.SourceSection; import com.oracle.truffle.espresso.impl.Method.MethodVersion; import com.oracle.truffle.espresso.meta.EspressoError; -import com.oracle.truffle.espresso.vm.VM; /** * Base node for all implementations of Java methods. @@ -98,9 +96,4 @@ public boolean hasTag(Class tag) { } return false; } - - @Override - public int getBci(Frame frame) { - return VM.EspressoStackElement.NATIVE_BCI; - } } diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/IntrinsicSubstitutorNode.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/IntrinsicSubstitutorNode.java index a5b131e5a338..6a29cb5685eb 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/IntrinsicSubstitutorNode.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/IntrinsicSubstitutorNode.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,14 +25,16 @@ import java.util.Arrays; +import com.oracle.truffle.api.frame.Frame; import com.oracle.truffle.api.frame.VirtualFrame; import com.oracle.truffle.api.nodes.Node; import com.oracle.truffle.espresso.impl.Method; import com.oracle.truffle.espresso.meta.EspressoError; import com.oracle.truffle.espresso.perf.DebugCounter; import com.oracle.truffle.espresso.substitutions.JavaSubstitution; +import com.oracle.truffle.espresso.vm.VM; -final class IntrinsicSubstitutorNode extends EspressoInstrumentableRootNodeImpl { +public final class IntrinsicSubstitutorNode extends EspressoInstrumentableRootNodeImpl { @Child private JavaSubstitution substitution; // Truffle does not want to report split on first call. Delay until the second. @@ -89,4 +91,13 @@ public Node copy() { boolean isTrivial() { return substitution.isTrivial(); } + + @Override + public int getBci(Frame frame) { + if (getMethodVersion().isMethodNative()) { + return VM.EspressoStackElement.NATIVE_BCI; + } else { + return 0; + } + } } diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/IntrinsifiedNativeMethodNode.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/IntrinsifiedNativeMethodNode.java index 7c9d3b0079fb..cffe2f61b772 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/IntrinsifiedNativeMethodNode.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/IntrinsifiedNativeMethodNode.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -23,9 +23,11 @@ package com.oracle.truffle.espresso.nodes; +import com.oracle.truffle.api.frame.Frame; import com.oracle.truffle.api.frame.VirtualFrame; import com.oracle.truffle.espresso.impl.Method; import com.oracle.truffle.espresso.substitutions.CallableFromNative; +import com.oracle.truffle.espresso.vm.VM; final class IntrinsifiedNativeMethodNode extends EspressoInstrumentableRootNodeImpl { @Child private CallableFromNative nativeMethod; @@ -56,4 +58,9 @@ Object execute(VirtualFrame frame) { } return nativeMethod.invokeDirect(env, args); } + + @Override + public int getBci(Frame frame) { + return VM.EspressoStackElement.NATIVE_BCI; + } } diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/NativeMethodNode.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/NativeMethodNode.java index 91f5b27a2a87..cf19859f33ab 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/NativeMethodNode.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/NativeMethodNode.java @@ -25,6 +25,7 @@ import com.oracle.truffle.api.CompilerDirectives; import com.oracle.truffle.api.CompilerDirectives.CompilationFinal; import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; +import com.oracle.truffle.api.frame.Frame; import com.oracle.truffle.api.frame.VirtualFrame; import com.oracle.truffle.api.interop.ArityException; import com.oracle.truffle.api.interop.InteropLibrary; @@ -43,6 +44,7 @@ import com.oracle.truffle.espresso.perf.DebugCounter; import com.oracle.truffle.espresso.runtime.EspressoException; import com.oracle.truffle.espresso.runtime.staticobject.StaticObject; +import com.oracle.truffle.espresso.vm.VM; /** * Represents a native Java method. @@ -149,4 +151,9 @@ private Object processResult(JniEnv env, Object result) { assert !(returnType == Type._void) || result == StaticObject.NULL; return result; } + + @Override + public int getBci(Frame frame) { + return VM.EspressoStackElement.NATIVE_BCI; + } } diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/interop/AbstractLookupNode.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/interop/AbstractLookupNode.java index 563df3e2050b..cd518b747827 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/interop/AbstractLookupNode.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/interop/AbstractLookupNode.java @@ -30,9 +30,13 @@ import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; import com.oracle.truffle.api.interop.ArityException; +import com.oracle.truffle.espresso.descriptors.Symbol; +import com.oracle.truffle.espresso.descriptors.Symbol.Name; +import com.oracle.truffle.espresso.descriptors.Symbol.Signature; import com.oracle.truffle.espresso.impl.Klass; import com.oracle.truffle.espresso.impl.Method; import com.oracle.truffle.espresso.nodes.EspressoNode; +import com.oracle.truffle.espresso.runtime.EspressoContext; public abstract class AbstractLookupNode extends EspressoNode { public static final char METHOD_SELECTION_SEPARATOR = '/'; @@ -41,6 +45,7 @@ public abstract class AbstractLookupNode extends EspressoNode { @TruffleBoundary Method[] doLookup(Klass klass, String key, boolean publicOnly, boolean isStatic, int arity) throws ArityException { + EspressoContext ctx = getContext(); ArrayList result = new ArrayList<>(); String methodName; String signature = null; @@ -51,12 +56,23 @@ Method[] doLookup(Klass klass, String key, boolean publicOnly, boolean isStatic, } else { methodName = key; } + Symbol name = ctx.getNames().lookup(methodName); + if (name == null) { + return null; + } + Symbol sig = null; + if (signature != null) { + sig = ctx.getSignatures().lookupValidSignature(signature); + if (sig == null) { + return null; + } + } int minOverallArity = Integer.MAX_VALUE; int maxOverallArity = -1; boolean skipArityCheck = arity == -1; for (Method.MethodVersion m : getMethodArray(klass)) { - if (matchMethod(m.getMethod(), methodName, signature, isStatic, publicOnly)) { + if (matchMethod(m.getMethod(), name, sig, isStatic, publicOnly)) { int matchArity = m.getMethod().getParameterCount(); minOverallArity = min(minOverallArity, matchArity); maxOverallArity = max(maxOverallArity, matchArity); @@ -71,13 +87,13 @@ Method[] doLookup(Klass klass, String key, boolean publicOnly, boolean isStatic, return result.isEmpty() ? null : result.toArray(new Method[0]); } - private static boolean matchMethod(Method m, String methodName, String signature, boolean isStatic, boolean publicOnly) { + private static boolean matchMethod(Method m, Symbol methodName, Symbol signature, boolean isStatic, boolean publicOnly) { return (!publicOnly || m.isPublic()) && m.isStatic() == isStatic && !m.isSignaturePolymorphicDeclared() && - m.getName().toString().equals(methodName) && + methodName == m.getName() && // If signature is specified, do the check. - (signature == null || m.getSignatureAsString().equals(signature)); + (signature == null || signature == m.getRawSignature()); } @TruffleBoundary @@ -91,8 +107,20 @@ protected boolean isInvocable(Klass klass, String key, boolean publicOnly, boole } else { methodName = key; } + EspressoContext ctx = getContext(); + Symbol name = ctx.getNames().lookup(methodName); + if (name == null) { + return false; + } + Symbol sig = null; + if (signature != null) { + sig = ctx.getSignatures().lookupValidSignature(signature); + if (sig == null) { + return false; + } + } for (Method.MethodVersion m : getMethodArray(klass)) { - if (matchMethod(m.getMethod(), methodName, signature, isStatic, publicOnly)) { + if (matchMethod(m.getMethod(), name, sig, isStatic, publicOnly)) { return true; } } diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/interop/MethodArgsUtils.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/interop/MethodArgsUtils.java index f3bbfe24bfb2..8ad648654dcc 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/interop/MethodArgsUtils.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/interop/MethodArgsUtils.java @@ -22,8 +22,6 @@ */ package com.oracle.truffle.espresso.nodes.interop; -import java.util.Arrays; - import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; import com.oracle.truffle.api.interop.InteropLibrary; import com.oracle.truffle.api.interop.InvalidArrayIndexException; @@ -40,64 +38,140 @@ public class MethodArgsUtils { - @TruffleBoundary + /** + * Tries to match a given candidate with from a given arguments array. A match happens when all + * given arguments can be {@link ToEspressoNode converted to an espresso object} of the klass of + * their corresponding parameter's type in the method signature. + *

+ * In case of a variable argument method, all parameters except the last (varargs argument) need + * to match, then, depending on what arguments are left to match: + *

    + *
  • If there is a single argument left, and it is an + * {@link InteropLibrary#hasArrayElements(Object)} interop array}, consider it to be containing + * the varargs, then append its elements into the returned + * {@link CandidateMethodWithArgs#getConvertedArgs()}, while trying to match each of its + * elements with the varargs component type..
  • + *
  • Else, every remaining parameter is matched with the component type of the varargs.
  • + *
+ * If at any point, matching an argument is not successful, this method returns {@code null}. + *

+ * Here are a couple examples: + *

+ * Trying to match method {@code int m(long, String)} with the argument types: + *

    + *
  • {@code [long l, String s]} -> {@code [l, s]}
  • + *
  • {@code [byte b, String s]} -> {@code [(long) b, s]}
  • + *
  • {@code [double d, String s]} -> fails
  • + *
  • {@code [long l, List s]} -> fails
  • + *
  • {@code [long l]} -> fails
  • + *
+ *

+ * Trying to match method {@code int m(long, String...)} with the interop argument types: + *

    + *
  • {@code [long l, String s]} -> {@code [l, s]}
  • + *
  • {@code [byte b, String s]} -> {@code [(long) b, s]}
  • + *
  • {@code [double d, String s]} -> fails
  • + *
  • {@code [long l, String[](s1, s2, s3)]} -> [l, s1, s2, s3]
  • + *
  • {@code [long l, List(s1, s2, s3)]} -> [l, s1, s2, s3]
  • + *
  • {@code [long l]} -> [l]
  • + *
  • {@code [long l, NULL]} -> [l, NULL]
  • + *
+ */ public static CandidateMethodWithArgs matchCandidate(Method candidate, Object[] arguments, Klass[] parameterKlasses, ToEspressoNode.DynamicToEspresso toEspressoNode) { - boolean canConvert = true; + Object[] convertedArgs = convertedArgs(candidate, arguments, parameterKlasses, toEspressoNode); + if (convertedArgs != null) { + return new CandidateMethodWithArgs(candidate, convertedArgs, parameterKlasses); + } + return null; + } + + @TruffleBoundary + private static Object[] convertedArgs(Method candidate, Object[] arguments, Klass[] parameterKlasses, ToEspressoNode.DynamicToEspresso toEspressoNode) { + assert arguments.length == parameterKlasses.length || (candidate.isVarargs() && arguments.length >= parameterKlasses.length - 1); int paramLength = parameterKlasses.length; - Object[] convertedArgs = new Object[arguments.length]; - - for (int j = 0; j < arguments.length; j++) { - Klass paramType = null; - Object argument = arguments[j]; - boolean checkNeeded = true; - try { - if (candidate.isVarargs() && j >= paramLength - 1) { - paramType = ((ArrayKlass) parameterKlasses[paramLength - 1]).getComponentType(); - InteropLibrary library = InteropLibrary.getUncached(); - if (arguments.length == paramLength && j == paramLength - 1) { - if (library.hasArrayElements(argument)) { - long arraySize = library.getArraySize(argument); - if (arraySize >= Integer.MAX_VALUE) { - canConvert = false; - break; - } - convertedArgs = Arrays.copyOf(convertedArgs, convertedArgs.length + (int) arraySize - 1); - for (int l = 0; l < arraySize; l++) { - if (library.isArrayElementReadable(argument, l)) { - Object element = library.readArrayElement(argument, l); - convertedArgs[j + l] = toEspressoNode.execute(element, paramType); - } else { - canConvert = false; - break; - } - } - checkNeeded = false; - } else { - if (library.isNull(argument)) { - // null varargs array - convertedArgs[j] = StaticObject.NULL; - checkNeeded = false; - } + try { + // Determine if we need to expand a given vararg array + long expansionLength = 0; + boolean needsExpansion = false; + boolean isNullVararg = false; + if (candidate.isVarargs() && arguments.length == parameterKlasses.length) { + Object varargArray = arguments[paramLength - 1]; + InteropLibrary lib = InteropLibrary.getUncached(); + if (lib.hasArrayElements(varargArray)) { + // Account for the additional array slot in the arguments array + expansionLength = lib.getArraySize(varargArray) - 1; + needsExpansion = true; + } + if (lib.isNull(varargArray)) { + isNullVararg = true; + } + } + + long arrayLen = arguments.length + expansionLength; + if (arrayLen > Integer.MAX_VALUE) { + return null; + } + + Object[] convertedArgs = new Object[Math.toIntExact(arrayLen)]; + + // First, convert args up until the vararg array if any. + int nonVarargsLen = parameterKlasses.length + (candidate.isVarargs() ? -1 : 0); + for (int pos = 0; pos < nonVarargsLen; pos++) { + Klass paramType = parameterKlasses[pos]; + convertedArgs[pos] = toEspressoNode.execute(arguments[pos], paramType); + } + + if (candidate.isVarargs()) { + // Then, expand the given vararg array or collect the leftover arguments as the + // component type of the vararg. + Klass varargType = ((ArrayKlass) parameterKlasses[paramLength - 1]).getComponentType(); + if (needsExpansion) { + Object varargArray = arguments[paramLength - 1]; + InteropLibrary lib = InteropLibrary.getUncached(); + assert lib.hasArrayElements(varargArray); + // Expand and convert given array. + for (int varargPos = 0; varargPos <= expansionLength; varargPos++) { + if (!lib.isArrayElementReadable(varargArray, varargPos)) { + return null; } + Object element = lib.readArrayElement(varargArray, varargPos); + convertedArgs[nonVarargsLen + varargPos] = toEspressoNode.execute(element, varargType); } - } - if (checkNeeded) { - if (paramType == null) { - paramType = parameterKlasses[j]; + } else if (isNullVararg) { + convertedArgs[nonVarargsLen] = StaticObject.NULL; + } else { + // Convert leftover arguments. + for (int pos = nonVarargsLen; pos < arguments.length; pos++) { + convertedArgs[pos] = toEspressoNode.execute(arguments[pos], varargType); } - convertedArgs[j] = toEspressoNode.execute(argument, paramType); } - } catch (UnsupportedTypeException e) { - canConvert = false; - } catch (UnsupportedMessageException | InvalidArrayIndexException e) { - throw EspressoError.shouldNotReachHere(); } + + return convertedArgs; + } catch (ArithmeticException // If expansion of the given vararg array overflows + | OutOfMemoryError // If converted args array creation fails. + | UnsupportedTypeException e) { + return null; + } catch (UnsupportedMessageException | InvalidArrayIndexException e) { + throw EspressoError.shouldNotReachHere(); } - return canConvert ? new CandidateMethodWithArgs(candidate, convertedArgs, parameterKlasses) : null; } - @TruffleBoundary + /** + * From a given varargs method and expanded arguments (see {@link #matchCandidate}), creates a + * new arguments array containing the original arguments until the varargs argument, then + * collects the trailing varargs arguments in an array and prepend it. If the trailing arguments + * is a single {@code null}, then it is prepended instead. + *

+ * For example, Trying to match method {@code int m(long, String...)} with the arguments: + *