diff --git a/ci.jsonnet b/ci.jsonnet index 4848ebf505..d7a1afacd7 100644 --- a/ci.jsonnet +++ b/ci.jsonnet @@ -97,6 +97,9 @@ local manual_interpreter_bench = manual_interpreter_env + task_spec({ name_suffix +:: ["manual-interpreter"], }), + local with_compiler = task_spec({ + dynamic_imports +:: ["/compiler"], + }), // ----------------------------------------------------------------------------------------------------------------- // @@ -174,13 +177,13 @@ "linux:aarch64:jdk21" : daily + t("01:30:00"), "darwin:aarch64:jdk21" : daily + t("01:30:00"), "windows:amd64:jdk21" : daily + t("01:00:00"), - "linux:amd64:jdk-latest" : tier3 + require(GRAAL_JDK_LATEST), - "linux:aarch64:jdk-latest" : tier3 + require(GRAAL_JDK_LATEST), - "darwin:aarch64:jdk-latest" : tier3 + require(GRAAL_JDK_LATEST), - "windows:amd64:jdk-latest" : tier3 + require(GRAAL_JDK_LATEST), + "linux:amd64:jdk-latest" : tier3 + require(GRAAL_JDK_LATEST) + with_compiler, + "linux:aarch64:jdk-latest" : tier3 + require(GRAAL_JDK_LATEST) + with_compiler, + "darwin:aarch64:jdk-latest" : tier3 + require(GRAAL_JDK_LATEST) + with_compiler, + "windows:amd64:jdk-latest" : tier3 + require(GRAAL_JDK_LATEST) + with_compiler, }), "python-junit-manual-interpreter": gpgate + platform_spec(no_jobs) + manual_interpreter_gate("python-junit") + platform_spec({ - "linux:amd64:jdk-latest" : tier3 + require(GRAAL_JDK_LATEST), + "linux:amd64:jdk-latest" : tier3 + require(GRAAL_JDK_LATEST) + with_compiler, }), "python-junit-maven": gpgate_maven + platform_spec(no_jobs) + platform_spec({ "linux:amd64:jdk21" : daily + t("00:30:00"), diff --git a/graalpython/com.oracle.graal.python.test/src/com/oracle/graal/python/test/advanced/CompilerFailureExitTest.java b/graalpython/com.oracle.graal.python.test/src/com/oracle/graal/python/test/advanced/CompilerFailureExitTest.java new file mode 100644 index 0000000000..3185a36e8f --- /dev/null +++ b/graalpython/com.oracle.graal.python.test/src/com/oracle/graal/python/test/advanced/CompilerFailureExitTest.java @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * The Universal Permissive License (UPL), Version 1.0 + * + * Subject to the condition set forth below, permission is hereby granted to any + * person obtaining a copy of this software, associated documentation and/or + * data (collectively the "Software"), free of charge and under any and all + * copyright rights in the Software, and any and all patent rights owned or + * freely licensable by each licensor hereunder covering either (i) the + * unmodified Software as contributed to or provided by such licensor, or (ii) + * the Larger Works (as defined below), to deal in both + * + * (a) the Software, and + * + * (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if + * one is included with the Software each a "Larger Work" to which the Software + * is contributed by such licensors), + * + * without restriction, including without limitation the rights to copy, create + * derivative works of, display, perform, and distribute the Software and make, + * use, sell, offer for sale, import, export, have made, and have sold the + * Software and the Larger Work(s), and to sublicense the foregoing rights on + * either these or other terms. + * + * This license is subject to the following condition: + * + * The above copyright notice and either this complete permission notice or at a + * minimum a reference to the UPL must be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package com.oracle.graal.python.test.advanced; + +import org.graalvm.polyglot.Context; +import org.graalvm.polyglot.Engine; +import org.junit.Assume; +import org.junit.Test; + +public class CompilerFailureExitTest { + private static final String ENABLE_FLAG_ENV_NAME = "GRAALPYTHON_JUNIT_COMPILER_FAILURE_EXIT_TEST"; + + @Test + public void compilerBailoutExitsVM() { + Assume.assumeTrue(ENABLE_FLAG_ENV_NAME + " is not set", "true".equals(System.getenv(ENABLE_FLAG_ENV_NAME))); + try (Engine engine = Engine.newBuilder("python").allowExperimentalOptions(true).// + option("engine.BackgroundCompilation", "false").// + option("engine.FirstTierCompilationThreshold", "1").// + option("engine.LastTierCompilationThreshold", "10").build(); + Context context = Context.newBuilder("python").engine(engine).allowExperimentalOptions(true).allowAllAccess(true).// + option("python.EnableDebuggingBuiltins", "true").build()) { + context.eval("python", """ + import __graalpython__ + + def trigger_compiler_bailout(): + for _ in range(100): + __graalpython__.compiler_bailout_for_tests() + + trigger_compiler_bailout() + """); + } + } +} diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/GraalPythonModuleBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/GraalPythonModuleBuiltins.java index 4429491715..3788d4723c 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/GraalPythonModuleBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/GraalPythonModuleBuiltins.java @@ -295,6 +295,7 @@ public void postInitialize(Python3Core core) { if (!context.getOption(PythonOptions.EnableDebuggingBuiltins)) { mod.setAttribute(tsLiteral("dump_truffle_ast"), PNone.NO_VALUE); + mod.setAttribute(tsLiteral("compiler_bailout_for_tests"), PNone.NO_VALUE); mod.setAttribute(tsLiteral("tdebug"), PNone.NO_VALUE); mod.setAttribute(tsLiteral("set_storage_strategy"), PNone.NO_VALUE); mod.setAttribute(tsLiteral("get_storage_strategy"), PNone.NO_VALUE); @@ -662,6 +663,16 @@ Object doIt(Object value) { } } + @Builtin(name = "compiler_bailout_for_tests", minNumOfPositionalArgs = 0) + @GenerateNodeFactory + public abstract static class CompilerBailoutForTests extends PythonBuiltinNode { + @Specialization + Object doIt() { + CompilerDirectives.bailout("test bailout requested by compiler_bailout_for_tests"); + return PNone.NONE; + } + } + @Builtin(name = "tdebug", takesVarArgs = true) @GenerateNodeFactory public abstract static class DebugNode extends PythonBuiltinNode { diff --git a/mx.graalpython/mx_graalpython.py b/mx.graalpython/mx_graalpython.py index 1001b153c1..96ed78477b 100644 --- a/mx.graalpython/mx_graalpython.py +++ b/mx.graalpython/mx_graalpython.py @@ -668,7 +668,10 @@ def __post_init__(self): if is_collecting_coverage(): skip_leak_tests = True - vm_args = ['-Dpolyglot.engine.WarnInterpreterOnly=false'] + bytecode_dsl_build_args() + vm_args = ['-Dpolyglot.engine.WarnInterpreterOnly=false'] + if mx.suite('compiler', fatalIfMissing=False): + vm_args.append('-Dpolyglot.engine.CompilationFailureAction=ExitVM') + vm_args += bytecode_dsl_build_args() # Note: we must use filters instead of --regex so that mx correctly processes the unit test configs, # but it is OK to apply --regex on top of the filters @@ -719,6 +722,30 @@ def __post_init__(self): "--forbidden-class", "com.oracle.graal.python.runtime.native_memory.NativePrimitiveReference"]) +def verify_junit_compilation_failure(): + compiler_failure_exit_test = 'com.oracle.graal.python.test.advanced.CompilerFailureExitTest' + enable_flag_env_name = 'GRAALPYTHON_JUNIT_COMPILER_FAILURE_EXIT_TEST' + if not mx.suite('compiler', fatalIfMissing=False): + mx.warn(f"Skipping {compiler_failure_exit_test}: the compiler suite is not imported.") + return + + args = [ + 'unittest', + '--suite', 'graalpython', + '--verbose', + '-Dpolyglot.engine.CompilationFailureAction=ExitVM', + ] + bytecode_dsl_build_args() + [ + compiler_failure_exit_test, + ] + output = mx.OutputCapture() + exit_code = run_mx(args, nonZeroIsFatal=False, out=mx.TeeOutputCapture(output), + err=mx.TeeOutputCapture(output), env={**os.environ, enable_flag_env_name: 'true'}) + if exit_code == 0: + mx.abort(f"Expected {compiler_failure_exit_test} to exit the VM with a compiler failure, but it passed.") + if 'Graal compilation failure' not in output.data: + mx.abort(f"{compiler_failure_exit_test} failed, but not with the expected compiler-failure exit.") + + PYTHON_ARCHIVES = ["GRAALPYTHON_GRAALVM_SUPPORT"] PYTHON_NATIVE_PROJECTS = ["python-libbz2", "python-liblzma", @@ -1447,6 +1474,7 @@ def graalpython_gate_runner(_, tasks): pass # Sometimes this fails on windows else: mx.command_function('tck')([]) + verify_junit_compilation_failure() # JUnit tests with Maven with Task('GraalPython integration JUnit with Maven', tasks, tags=[GraalPythonTags.junit_maven]) as task: