Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ jobs:
- os: ubuntu-24.04
env:
JDK_VERSION: "latest"
GATE_TAGS: "build,debuginfotest"
GATE_TAGS: "build,debuginfotest,scismoketest"
PRIMARY: "substratevm"
- env:
JDK_VERSION: "latest"
Expand Down
136 changes: 136 additions & 0 deletions substratevm/mx.substratevm/mx_substratevm.py
Original file line number Diff line number Diff line change
Expand Up @@ -262,6 +262,7 @@ def __getattr__(self, name):
'terminus',
'debuginfotest',
'standalone_pointsto_unittests',
'scismoketest',
'native_unittests',
'all_native_unittests',
'java_desktop_integration',
Expand Down Expand Up @@ -508,6 +509,11 @@ def svm_gate_body(args, tasks):
with native_image_context(IMAGE_ASSERTION_FLAGS) as native_image:
layereddebuginfotest(['--output-path', svmbuild_dir()] + args.extra_image_builder_arguments)

with Task('image scismoketest', tasks, tags=[GraalTags.scismoketest]) as t:
if t:
with native_image_context(IMAGE_ASSERTION_FLAGS) as native_image:
scismoketest(['--output-path', svmbuild_dir()] + args.extra_image_builder_arguments)

with Task('image debughelpertest', tasks, tags=[GraalTags.debuginfotest]) as t:
if t:
if mx.is_windows():
Expand Down Expand Up @@ -1482,6 +1488,117 @@ def testhello_ni_args(cincludepath, sourcepath):
'-H:DebugInfoSourceSearchPath=' + sourcepath,
])


def _run_scismoke_binary(binary_path):
expected_output = 'PASS' + os.linesep
actual_output = []

def _collector(x):
actual_output.append(x)
mx.log(x)

mx.run([binary_path], out=_collector)

# Basic correctness check.
if ''.join(actual_output) != expected_output:
mx.abort('Unexpected output: ' + str(actual_output) + ' != ' + str([expected_output]))


class _ScismokeHistogramTracker:
_HISTOGRAM_HEADER = 'Code Size; Nodes Parsing'
_HISTOGRAM_FOOTER = 'Size all methods'

def __init__(self):
self.in_histogram = False
self.has_single = False
self.has_multi = False
self.verified_histogram = False

def process_line(self, line):
# Only verify within method histogram bounds.
if line.startswith(self._HISTOGRAM_HEADER):
self.in_histogram = True
self.verified_histogram = True
elif self.in_histogram:
if line.startswith(self._HISTOGRAM_FOOTER):
self.in_histogram = False
else:
if 'singleCallsiteHelper' in line:
self.has_single = True
if 'multiCallsiteHelper' in line:
self.has_multi = True


def _find_methodhistogram_file(reports_dir):
matches = glob(join(reports_dir, 'methodhistogram_*.txt'))
if not matches:
return None
return max(matches, key=os.path.getmtime)


def _parse_methodhistogram_file(histogram_file):
tracker = _ScismokeHistogramTracker()
with open(histogram_file, 'r', encoding='utf-8') as histogram:
for line in histogram:
tracker.process_line(line.rstrip('\n'))
return tracker


def _build_scismoke_image(native_image, build_args, reports_dir):
def _log_output(line):
mx.log(line)

native_image(build_args, out=_log_output, err=_log_output)
histogram_file = _find_methodhistogram_file(reports_dir)
if histogram_file is None:
mx.abort('PrintMethodHistogram report not found in ' + reports_dir)
mx.log('Parsing method histogram from: ' + histogram_file)
tracker = _parse_methodhistogram_file(histogram_file)
if not tracker.verified_histogram:
mx.abort('PrintMethodHistogram output not found in ' + histogram_file)
return tracker


def _assert_scismoke_histogram(tracker, is_sci_enabled, variant_name):
if not tracker.has_multi:
mx.abort(variant_name + ': multiCallsiteHelper should always appear in histogram')
if is_sci_enabled:
if tracker.has_single:
mx.abort(variant_name + ': singleCallsiteHelper should not appear in histogram with SCI enabled')
elif not tracker.has_single:
mx.abort(variant_name + ': singleCallsiteHelper should appear in histogram with SCI disabled')


def _scismoketest(native_image, path, args):
mx_util.ensure_dir_exists(path)

base_args = [
'--native-image-info',
'-cp', classpath('com.oracle.svm.test.sci'),
'--initialize-at-build-time=com.oracle.svm.test.sci',
'com.oracle.svm.test.sci.SciSmoke',
] + args

def build_and_run(variant_name, sci_flag, is_sci_enabled):
variant_path = join(path, variant_name)
mx_util.ensure_dir_exists(variant_path)
binary_path = join(variant_path, 'scismoke')
build_args = base_args + svm_experimental_options([
sci_flag,
'-H:+PrintMethodHistogram',
]) + [
'-o', binary_path,
]
reports_dir = join(variant_path, 'reports')
mx.log(f'native_image {build_args}')
tracker = _build_scismoke_image(native_image, build_args, reports_dir)
_assert_scismoke_histogram(tracker, is_sci_enabled, variant_name)
_run_scismoke_binary(binary_path)

build_and_run('sci_on', '-H:+AOTSingleCallsiteInline', True)
build_and_run('sci_off', '-H:-AOTSingleCallsiteInline', False)


def _gdbdebughelperstest(native_image, path, with_isolates_only, args):

# ====== check gdb version ======
Expand Down Expand Up @@ -2386,6 +2503,25 @@ def run_helloworld_command(args, config, command_name, native_image_wrapper=None
config=config,
)

@mx.command(suite_name=suite.name, command_name='scismoketest', usage_msg='[options]')
def scismoketest(args, config=None):
"""
Build and run a simple app with AOTSingleCallsiteInline enabled and disabled.
The output of the app is verified for correctness.
PrintMethodHistogram is parsed to verify the expected inlining happened.
"""
parser = ArgumentParser(prog='mx scismoketest')
all_args = ['--output-path', '--build-only']
masked_args = [_mask(arg, all_args) for arg in args]
parser.add_argument(all_args[0], metavar='<output-path>', nargs=1, help='Path of the generated image', default=[svmbuild_dir()])
parser.add_argument('image_args', nargs='*', default=[])
parsed = parser.parse_args(masked_args)
output_path = unmask(parsed.output_path)[0]
native_image_context_run(
lambda native_image, a:
_scismoketest(native_image, output_path, a), unmask(parsed.image_args),
config=config
)

@mx.command(suite_name=suite.name, command_name='debuginfotest', usage_msg='[options]')
def debuginfotest(args, config=None):
Expand Down
13 changes: 13 additions & 0 deletions substratevm/mx.substratevm/suite.py
Original file line number Diff line number Diff line change
Expand Up @@ -1274,6 +1274,19 @@
"testProject": True,
},

"com.oracle.svm.test.sci": {
"subDir": "src",
"sourceDirs": ["src"],
"dependencies": [
"SVM",
],
"checkstyle": "com.oracle.svm.test",
"workingSets": "SVM",
"javaCompliance": "22+",
"spotbugs": "false",
"jacoco": "exclude",
},

"com.oracle.svm.with.space.test": {
"subDir": "src",
"sourceDirs": ["src"],
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
/*
* Copyright (c) 2013, 2025, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2013, 2026, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2026, 2026, IBM Inc. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
Expand Down Expand Up @@ -891,6 +892,10 @@ public static long getTearDownFailureNanos() {
@Option(help = "Perform trivial method inlining in the AOT compiled native image")//
public static final HostedOptionKey<Boolean> AOTTrivialInline = new HostedOptionKey<>(true);

@LayerVerifiedOption(kind = Kind.Changed, severity = Severity.Error)//
@Option(help = "Perform single callsite method inlining in the AOT compiled native image", stability = OptionStability.EXPERIMENTAL)//
public static final HostedOptionKey<Boolean> AOTSingleCallsiteInline = new HostedOptionKey<>(false);

@LayerVerifiedOption(kind = Kind.Removed, severity = Severity.Warn, positional = false)//
@Option(help = "file:doc-files/NeverInlineHelp.txt", type = OptionType.Debug)//
public static final HostedOptionKey<AccumulatingLocatableMultiOptionValue.Strings> NeverInline = new HostedOptionKey<>(AccumulatingLocatableMultiOptionValue.Strings.build());
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
/*
* Copyright (c) 2013, 2022, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2013, 2026, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2026, 2026, IBM Inc. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
Expand All @@ -25,6 +26,7 @@
package com.oracle.svm.hosted.code;

import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;

import com.oracle.graal.pointsto.flow.AnalysisParsedGraph;
Expand All @@ -45,6 +47,9 @@
import jdk.graal.compiler.options.OptionValues;

public class CompilationInfo {
int sizeLastRound;

AtomicInteger callsites = new AtomicInteger();

protected final HostedMethod method;

Expand Down
Loading
Loading