From 09e02b1d23c8afe5dcf41d583ce8524d777b1853 Mon Sep 17 00:00:00 2001 From: Matt D'Souza Date: Sat, 17 Aug 2024 14:09:44 -0400 Subject: [PATCH] Bytecode DSL interpreter migration --- ci.jsonnet | 2 +- .../python/harness.py | 3 + .../freeze_modules.py | 24 +- .../graal/python/pegparser/scope/Scope.java | 6 +- .../pegparser/scope/ScopeEnvironment.java | 10 + .../integration/generator/GeneratorTests.java | 117 +- .../test/integration/grammar/AsyncTests.java | 66 + .../test/integration/grammar/ClassTests.java | 17 + .../test/integration/grammar/TryTests.java | 45 + .../python/test/compiler/CompilerTests.java | 10 + .../src/tests/cpyext/test_shutdown.py | 2 + .../src/tests/test_ssl_java_integration.py | 7 +- .../oracle/graal/python/PythonLanguage.java | 51 +- .../builtins/modules/BuiltinFunctions.java | 13 +- .../modules/GraalPythonModuleBuiltins.java | 18 +- .../modules/MarshalModuleBuiltins.java | 299 +- .../modules/cext/PythonCextCodeBuiltins.java | 2 +- .../objects/asyncio/GetAwaitableNode.java | 8 +- .../builtins/objects/asyncio/PAsyncGen.java | 2 +- .../builtins/objects/code/CodeBuiltins.java | 149 +- .../builtins/objects/code/CodeNodes.java | 46 +- .../python/builtins/objects/code/PCode.java | 118 +- .../python/builtins/objects/frame/PFrame.java | 43 +- .../builtins/objects/function/PArguments.java | 8 + .../generator/CommonGeneratorBuiltins.java | 109 +- .../objects/generator/GeneratorBuiltins.java | 33 +- .../objects/generator/PGenerator.java | 195 +- .../objects/module/PythonFrozenModule.java | 11 +- .../python/builtins/objects/set/SetNodes.java | 2 + .../objects/superobject/SuperBuiltins.java | 43 +- .../objects/traceback/LazyTraceback.java | 4 +- .../objects/traceback/PTraceback.java | 15 +- .../objects/traceback/TracebackBuiltins.java | 6 +- .../python/compiler/BytecodeCodeUnit.java | 649 +++ .../graal/python/compiler/CodeUnit.java | 655 +-- .../python/compiler/CompilationScope.java | 2 +- .../python/compiler/CompilationUnit.java | 22 +- .../graal/python/compiler/Compiler.java | 74 +- .../bytecode_dsl/BaseBytecodeDSLVisitor.java | 493 ++ .../bytecode_dsl/BytecodeDSLCompiler.java | 124 + .../bytecode_dsl/RootNodeCompiler.java | 4568 +++++++++++++++++ .../lib/PyObjectStrAsTruffleStringNode.java | 2 +- .../oracle/graal/python/nodes/PGuards.java | 6 + .../oracle/graal/python/nodes/PRootNode.java | 8 +- .../graal/python/nodes/StringLiterals.java | 1 + .../python/nodes/builtins/ListNodes.java | 2 + .../nodes/bytecode/BytecodeFrameInfo.java | 60 + .../python/nodes/bytecode/FrameInfo.java | 43 +- .../python/nodes/bytecode/GetANextNode.java | 6 +- .../nodes/bytecode/GetSendValueNode.java | 2 + .../python/nodes/bytecode/GetTPFlagsNode.java | 2 + .../nodes/bytecode/GetYieldFromIterNode.java | 8 +- .../python/nodes/bytecode/ImportFromNode.java | 2 + .../python/nodes/bytecode/ImportNode.java | 1 + .../nodes/bytecode/MakeFunctionNode.java | 8 +- .../nodes/bytecode/PBytecodeRootNode.java | 49 +- .../python/nodes/bytecode/PrintExprNode.java | 2 + .../python/nodes/bytecode/RaiseNode.java | 2 + .../nodes/bytecode/SetupAnnotationsNode.java | 2 + .../InstrumentationSupport.java | 4 +- .../bytecode_dsl/BytecodeDSLCodeUnit.java | 93 + .../bytecode_dsl/BytecodeDSLFrameInfo.java | 44 + ...PBytecodeDSLGeneratorFunctionRootNode.java | 94 + .../bytecode_dsl/PBytecodeDSLRootNode.java | 4143 +++++++++++++++ .../graal/python/nodes/call/InvokeNode.java | 7 +- .../nodes/exception/ExceptMatchNode.java | 2 + .../exception/TopLevelExceptionHandler.java | 3 +- .../nodes/expression/BinaryArithmetic.java | 13 + .../python/nodes/expression/ContainsNode.java | 2 + .../expression/LookupAndCallInplaceNode.java | 1 + .../nodes/expression/UnaryArithmetic.java | 10 +- .../python/nodes/frame/DeleteGlobalNode.java | 4 +- .../nodes/frame/GetFrameLocalsNode.java | 86 +- .../nodes/frame/MaterializeFrameNode.java | 85 +- .../nodes/frame/ReadCallerFrameNode.java | 27 +- .../nodes/frame/ReadGlobalOrBuiltinNode.java | 4 +- .../python/nodes/frame/ReadNameNode.java | 4 +- .../python/nodes/frame/WriteGlobalNode.java | 4 +- .../python/nodes/frame/WriteNameNode.java | 2 +- .../graal/python/nodes/object/IsNode.java | 34 +- .../python/runtime/ExecutionContext.java | 52 +- .../graal/python/runtime/PythonContext.java | 65 +- .../graal/python/runtime/PythonOptions.java | 6 + .../runtime/exception/ExceptionUtils.java | 36 +- .../python/runtime/exception/PException.java | 96 +- .../runtime/object/PythonObjectFactory.java | 21 +- .../oracle/graal/python/util/PythonUtils.java | 8 +- .../lib-python/3/test/support/__init__.py | 11 + graalpython/lib-python/3/test/test_fstring.py | 1 - .../lib-python/3/test/test_sys_settrace.py | 1 + mx.graalpython/mx_graalpython.py | 127 +- mx.graalpython/mx_graalpython_benchmark.py | 4 + mx.graalpython/suite.py | 10 +- 93 files changed, 12262 insertions(+), 1119 deletions(-) create mode 100644 graalpython/com.oracle.graal.python.test.integration/src/com/oracle/graal/python/test/integration/grammar/AsyncTests.java create mode 100644 graalpython/com.oracle.graal.python/src/com/oracle/graal/python/compiler/BytecodeCodeUnit.java create mode 100644 graalpython/com.oracle.graal.python/src/com/oracle/graal/python/compiler/bytecode_dsl/BaseBytecodeDSLVisitor.java create mode 100644 graalpython/com.oracle.graal.python/src/com/oracle/graal/python/compiler/bytecode_dsl/BytecodeDSLCompiler.java create mode 100644 graalpython/com.oracle.graal.python/src/com/oracle/graal/python/compiler/bytecode_dsl/RootNodeCompiler.java create mode 100644 graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/bytecode/BytecodeFrameInfo.java create mode 100644 graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/bytecode_dsl/BytecodeDSLCodeUnit.java create mode 100644 graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/bytecode_dsl/BytecodeDSLFrameInfo.java create mode 100644 graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/bytecode_dsl/PBytecodeDSLGeneratorFunctionRootNode.java create mode 100644 graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/bytecode_dsl/PBytecodeDSLRootNode.java diff --git a/ci.jsonnet b/ci.jsonnet index 27d2901e7c..1decfbb9da 100644 --- a/ci.jsonnet +++ b/ci.jsonnet @@ -1 +1 @@ -{ "overlay": "2c97f9a352b2773941722f15612bd5c531f63489" } +{ "overlay": "410373502193976c276eb0c68eafd8d9d67fe54b" } diff --git a/graalpython/com.oracle.graal.python.benchmarks/python/harness.py b/graalpython/com.oracle.graal.python.benchmarks/python/harness.py index 5e5a6b6645..933d06230c 100644 --- a/graalpython/com.oracle.graal.python.benchmarks/python/harness.py +++ b/graalpython/com.oracle.graal.python.benchmarks/python/harness.py @@ -501,6 +501,9 @@ def run_benchmark(args): else: print("### no extra module search paths specified") + if GRAALPYTHON: + print(f"### using bytecode DSL interpreter: {__graalpython__.is_bytecode_dsl_interpreter}") + BenchRunner(bench_file, bench_args=bench_args, iterations=iterations, warmup=warmup, warmup_runs=warmup_runs, startup=startup, live_results=live_results).run() diff --git a/graalpython/com.oracle.graal.python.frozen/freeze_modules.py b/graalpython/com.oracle.graal.python.frozen/freeze_modules.py index 0eea044960..6ddf930c69 100644 --- a/graalpython/com.oracle.graal.python.frozen/freeze_modules.py +++ b/graalpython/com.oracle.graal.python.frozen/freeze_modules.py @@ -154,7 +154,7 @@ def relpath_for_posix_display(path, base): ####################################### # specs -def parse_frozen_specs(): +def parse_frozen_specs(suffix): seen = {} for section, specs in FROZEN: parsed = _parse_specs(specs, section, seen) @@ -163,7 +163,7 @@ def parse_frozen_specs(): try: source = seen[frozenid] except KeyError: - source = FrozenSource.from_id(frozenid, pyfile) + source = FrozenSource.from_id(frozenid, suffix, pyfile) seen[frozenid] = source else: assert not pyfile or pyfile == source.pyfile, item @@ -271,11 +271,11 @@ def iter_subs(): class FrozenSource(namedtuple('FrozenSource', 'id pyfile frozenfile deepfreezefile')): @classmethod - def from_id(cls, frozenid, pyfile=None): + def from_id(cls, frozenid, suffix, pyfile=None): if not pyfile: pyfile = os.path.join(STDLIB_DIR, *frozenid.split('.')) + '.py' #assert os.path.exists(pyfile), (frozenid, pyfile) - frozenfile = resolve_frozen_file(frozenid, FROZEN_MODULES_DIR) + frozenfile = resolve_frozen_file(frozenid, FROZEN_MODULES_DIR, suffix) return cls(frozenid, pyfile, frozenfile, STDLIB_DIR) @classmethod @@ -311,7 +311,7 @@ def isbootstrap(self): return self.id in BOOTSTRAP -def resolve_frozen_file(frozenid, destdir): +def resolve_frozen_file(frozenid, destdir, suffix): """Return the filename corresponding to the given frozen ID. For stdlib modules the ID will always be the full name @@ -324,7 +324,7 @@ def resolve_frozen_file(frozenid, destdir): raise ValueError(f'unsupported frozenid {frozenid!r}') # We use a consistent naming convention for all frozen modules. frozen_symbol = FrozenSource.resolve_symbol(frozenid) - frozenfile = f"Frozen{frozen_symbol}.bin" + frozenfile = f"Frozen{frozen_symbol}.{suffix}" if not destdir: return frozenfile @@ -634,11 +634,17 @@ def main(): STDLIB_DIR = os.path.abspath(parsed_args.python_lib) FROZEN_MODULES_DIR = os.path.abspath(parsed_args.binary_dir) + if __graalpython__.is_bytecode_dsl_interpreter: + suffix = "bin_dsl" + assert os.path.isdir(parsed_args.binary_dir), "Frozen modules for the DSL should be built after the manual bytecode interpreter." + else: + suffix = "bin" + shutil.rmtree(parsed_args.binary_dir, ignore_errors=True) + os.makedirs(parsed_args.binary_dir) + # create module specs - modules = list(parse_frozen_specs()) + modules = list(parse_frozen_specs(suffix)) - shutil.rmtree(parsed_args.binary_dir, ignore_errors=True) - os.makedirs(parsed_args.binary_dir) # write frozen module binary files containing the byte code and class files # used for importing the binary files for src in _iter_sources(modules): diff --git a/graalpython/com.oracle.graal.python.pegparser/src/com/oracle/graal/python/pegparser/scope/Scope.java b/graalpython/com.oracle.graal.python.pegparser/src/com/oracle/graal/python/pegparser/scope/Scope.java index d0c299cd24..b185bfb07e 100644 --- a/graalpython/com.oracle.graal.python.pegparser/src/com/oracle/graal/python/pegparser/scope/Scope.java +++ b/graalpython/com.oracle.graal.python.pegparser/src/com/oracle/graal/python/pegparser/scope/Scope.java @@ -229,11 +229,15 @@ public boolean isNested() { } public HashMap getSymbolsByType(EnumSet expectedFlags, int start) { + return getSymbolsByType(expectedFlags, EnumSet.noneOf(DefUse.class), start); + } + + public HashMap getSymbolsByType(EnumSet expectedFlags, EnumSet unexpectedFlags, int start) { int i = start; HashMap mapping = new HashMap<>(); for (String key : getSortedSymbols()) { EnumSet keyFlags = getUseOfName(key); - if (!Collections.disjoint(expectedFlags, keyFlags)) { + if (!Collections.disjoint(expectedFlags, keyFlags) && Collections.disjoint(unexpectedFlags, keyFlags)) { mapping.put(key, i++); } } diff --git a/graalpython/com.oracle.graal.python.pegparser/src/com/oracle/graal/python/pegparser/scope/ScopeEnvironment.java b/graalpython/com.oracle.graal.python.pegparser/src/com/oracle/graal/python/pegparser/scope/ScopeEnvironment.java index 2005dd65e2..37882b7e37 100644 --- a/graalpython/com.oracle.graal.python.pegparser/src/com/oracle/graal/python/pegparser/scope/ScopeEnvironment.java +++ b/graalpython/com.oracle.graal.python.pegparser/src/com/oracle/graal/python/pegparser/scope/ScopeEnvironment.java @@ -98,6 +98,7 @@ public class ScopeEnvironment { final HashMap blocks = new HashMap<>(); final ErrorCallback errorCallback; final EnumSet futureFeatures; + final HashMap parents = new HashMap<>(); public static ScopeEnvironment analyze(ModTy moduleNode, ErrorCallback errorCallback, EnumSet futureFeatures) { return new ScopeEnvironment(moduleNode, errorCallback, futureFeatures); @@ -128,6 +129,14 @@ public Scope lookupScope(SSTNode node) { return blocks.get(node); } + public Scope lookupParent(Scope scope) { + return parents.get(scope); + } + + public Scope getTopScope() { + return topScope; + } + private void analyzeBlock(Scope scope, HashSet bound, HashSet free, HashSet global) { HashSet local = new HashSet<>(); HashMap scopes = new HashMap<>(); @@ -328,6 +337,7 @@ private void enterBlock(String name, Scope.ScopeType type, SSTNode ast) { if (type == Scope.ScopeType.Annotation) { return; } + env.parents.put(scope, prev); if (prev != null) { prev.children.add(scope); } diff --git a/graalpython/com.oracle.graal.python.test.integration/src/com/oracle/graal/python/test/integration/generator/GeneratorTests.java b/graalpython/com.oracle.graal.python.test.integration/src/com/oracle/graal/python/test/integration/generator/GeneratorTests.java index 4eaf2fd873..6fcd800263 100644 --- a/graalpython/com.oracle.graal.python.test.integration/src/com/oracle/graal/python/test/integration/generator/GeneratorTests.java +++ b/graalpython/com.oracle.graal.python.test.integration/src/com/oracle/graal/python/test/integration/generator/GeneratorTests.java @@ -72,7 +72,7 @@ public void desugared() { } @Test - public void testYieldFrom() { + public void testYieldFromSimple() { String source = "def gen1():\n" + " yield 1\n" + " yield 2\n" + @@ -83,4 +83,119 @@ public void testYieldFrom() { "print(list(gen2()))\n"; assertPrints("[1, 2]\n", source); } + + @Test + public void testYieldFromIterable() { + // yield from should extract an iterator from a non-generator argument + String source = "class Foo:\n" + + " def __init__(self, wrapped):\n" + + " self.wrapped = wrapped\n" + + " def __iter__(self):\n" + + " return iter(self.wrapped)\n" + + "def gen():\n" + + " foo = Foo([1,2,3])\n" + + " yield from foo\n" + + "\n" + + "print(list(gen()))\n"; + assertPrints("[1, 2, 3]\n", source); + } + + @Test + public void testYieldFromReturn() { + String source = "def gen1():\n" + + " yield 1\n" + + " yield 2\n" + + " return 3\n" + + "\n" + + "def gen2():\n" + + " final = yield from gen1()\n" + + " yield final\n" + + "\n" + + "print(list(gen2()))\n"; + assertPrints("[1, 2, 3]\n", source); + } + + @Test + public void testYieldFromSend() { + String source = "def gen1(x):\n" + + " yield (yield (yield x))\n" + + "\n" + + "def gen2():\n" + + " yield from gen1(2)\n" + + " yield 8\n" + + "\n" + + "gen = gen2()\n" + + "print(gen.send(None))\n" + + "print(gen.send(4))\n" + + "print(gen.send(6))\n" + + "print(gen.send(42))\n"; + assertPrints("2\n4\n6\n8\n", source); + } + + @Test + public void testYieldFromThrowCaught() { + String source = "def gen1():\n" + + " try:\n" + + " x = 1\n" + + " while True:\n" + + " x = yield x\n" + + " except ValueError:\n" + + " yield 42\n" + + "\n" + + "def gen2():\n" + + " yield from gen1()\n" + + "\n" + + "gen = gen2()\n" + + "print(gen.send(None))\n" + + "print(gen.send(2))\n" + + "print(gen.send(3))\n" + + "print(gen.throw(ValueError))\n"; + assertPrints("1\n2\n3\n42\n", source); + } + + @Test + public void testYieldFromThrowUncaught() { + String source = "def gen1():\n" + + " x = 1\n" + + " while True:\n" + + " x = yield x\n" + + "\n" + + "def gen2():\n" + + " yield from gen1()\n" + + "\n" + + "gen = gen2()\n" + + "print(gen.send(None))\n" + + "print(gen.send(2))\n" + + "print(gen.send(3))\n" + + "try:\n" + + " gen.throw(ValueError)\n" + + " print('error')\n" + + "except ValueError:\n" + + " print('success')\n"; + assertPrints("1\n2\n3\nsuccess\n", source); + } + + @Test + public void testYieldFromClose() { + String source = "def gen1():\n" + + " x = 1\n" + + " try:\n" + + " while True:\n" + + " x = yield x\n" + + " except GeneratorExit:\n" + + " print('gen1 exit')\n" + + "\n" + + "def gen2():\n" + + " try:\n" + + " yield from gen1()\n" + + " except GeneratorExit:\n" + + " print('gen2 exit')\n" + + "\n" + + "gen = gen2()\n" + + "print(gen.send(None))\n" + + "print(gen.send(2))\n" + + "print(gen.send(3))\n" + + "gen.close()\n"; + assertPrints("1\n2\n3\ngen1 exit\ngen2 exit\n", source); + } } diff --git a/graalpython/com.oracle.graal.python.test.integration/src/com/oracle/graal/python/test/integration/grammar/AsyncTests.java b/graalpython/com.oracle.graal.python.test.integration/src/com/oracle/graal/python/test/integration/grammar/AsyncTests.java new file mode 100644 index 0000000000..25050feb7f --- /dev/null +++ b/graalpython/com.oracle.graal.python.test.integration/src/com/oracle/graal/python/test/integration/grammar/AsyncTests.java @@ -0,0 +1,66 @@ +package com.oracle.graal.python.test.integration.grammar; + +import static com.oracle.graal.python.test.integration.PythonTests.assertPrints; + +import org.junit.Test; + +public class AsyncTests { + @Test + public void nativeCoroutine() { + String source = "import asyncio\n" + + "async def foo():\n" + + " return 42\n" + + "async def main():\n" + + " print(await foo())\n" + + "asyncio.run(main())"; + assertPrints("42\n", source); + } + + @Test + public void asyncWith() { + String source = "import asyncio\n" + + "class AsyncContextManager:\n" + + " async def __aenter__(self):\n" + + " await asyncio.sleep(0.01)\n" + + " print(\"entered\")\n" + + " async def __aexit__(self, exc_type, exc_value, traceback):\n" + + " await asyncio.sleep(0.01)\n" + + " if exc_type:\n" + + " print(\"exited exceptionally\")\n" + + " else:\n" + + " print(\"exited normally\")\n" + + " return True\n" + + "async def main(shouldRaise):\n" + + " async with AsyncContextManager():\n" + + " print(\"inside\")\n" + + " if shouldRaise:\n" + + " raise ValueError\n" + + "asyncio.run(main(%s))"; + assertPrints("entered\ninside\nexited normally\n", String.format(source, "False")); + assertPrints("entered\ninside\nexited exceptionally\n", String.format(source, "True")); + } + + @Test + public void asyncWithExceptional() { + String source = "import asyncio\n" + + "class AsyncContextManager:\n" + + " async def __aenter__(self):\n" + + " await asyncio.sleep(0.01)\n" + + " print(\"entered\")\n" + + " async def __aexit__(self, exc_type, exc_value, traceback):\n" + + " await asyncio.sleep(0.01)\n" + + " print(\"exited\")\n" + + " return False\n" + // don't handle exception + "async def main(shouldRaise):\n" + + " async with AsyncContextManager():\n" + + " print(\"inside\")\n" + + " if shouldRaise:\n" + + " raise ValueError\n" + + "try:\n" + + " asyncio.run(main(%s))\n" + + "except ValueError:\n" + + " print(\"rethrew\")\n"; + assertPrints("entered\ninside\nexited\n", String.format(source, "False")); + assertPrints("entered\ninside\nexited\nrethrew\n", String.format(source, "True")); + } +} diff --git a/graalpython/com.oracle.graal.python.test.integration/src/com/oracle/graal/python/test/integration/grammar/ClassTests.java b/graalpython/com.oracle.graal.python.test.integration/src/com/oracle/graal/python/test/integration/grammar/ClassTests.java index 2e279ae577..785c598ee8 100644 --- a/graalpython/com.oracle.graal.python.test.integration/src/com/oracle/graal/python/test/integration/grammar/ClassTests.java +++ b/graalpython/com.oracle.graal.python.test.integration/src/com/oracle/graal/python/test/integration/grammar/ClassTests.java @@ -205,4 +205,21 @@ public void multipleInheritance() { assertPrints("common\n", source); } + @Test + public void classDecorator() { + String source = "def wrapper(cls):\n" + // + " orig_init = cls.__init__\n" + // + " def new_init(self):\n" + // + " print('wrapper')\n" + // + " orig_init(self)\n" + // + " cls.__init__ = new_init\n" + // + " return cls\n" + // + "@wrapper\n" + // + "class Foo:\n" + // + " def __init__(self):\n" + // + " print('Foo')\n" + // + "Foo()\n"; + assertPrints("wrapper\nFoo\n", source); + } + } diff --git a/graalpython/com.oracle.graal.python.test.integration/src/com/oracle/graal/python/test/integration/grammar/TryTests.java b/graalpython/com.oracle.graal.python.test.integration/src/com/oracle/graal/python/test/integration/grammar/TryTests.java index 697baf7341..bf030aebc2 100644 --- a/graalpython/com.oracle.graal.python.test.integration/src/com/oracle/graal/python/test/integration/grammar/TryTests.java +++ b/graalpython/com.oracle.graal.python.test.integration/src/com/oracle/graal/python/test/integration/grammar/TryTests.java @@ -290,4 +290,49 @@ public void testExceptionState6() { "print(repr(sys.exc_info()[1]))\n"; assertPrints("None\nNone\n", source); } + + @Test + public void testNamedExceptionDeleted() { + String source = "ex = 42\n" + + "try:\n" + + " raise NameError\n" + + "except BaseException as ex:\n" + + " pass\n" + + "try:\n" + + " print(ex)\n" + + " print(\"expected NameError\")\n" + + "except NameError:\n" + + " print(\"hit NameError\")\n"; + assertPrints("hit NameError\n", source); + } + + @Test + public void testNamedExceptionNotDeleted() { + String source = "ex = 42\n" + + "try:\n" + + " print(\"nothing thrown\")\n" + + "except BaseException as ex:\n" + + " pass\n" + + "try:\n" + + " print(ex)\n" + + "except NameError:\n" + + " print(\"hit unexpected NameError\")\n"; + assertPrints("nothing thrown\n42\n", source); + } + + @Test + public void testNamedExceptionDeletedByHandler() { + String source = "ex = 42\n" + + "try:\n" + + " raise NameError\n" + + "except BaseException as ex:\n" + + " print(\"deleting exception\")\n" + + " del ex\n" + + "try:\n" + + " print(ex)\n" + + " print(\"expected NameError\")\n" + + "except NameError:\n" + + " print(\"hit NameError\")\n"; + assertPrints("deleting exception\nhit NameError\n", source); + } } diff --git a/graalpython/com.oracle.graal.python.test/src/com/oracle/graal/python/test/compiler/CompilerTests.java b/graalpython/com.oracle.graal.python.test/src/com/oracle/graal/python/test/compiler/CompilerTests.java index a48efd2999..8759293c4a 100644 --- a/graalpython/com.oracle.graal.python.test/src/com/oracle/graal/python/test/compiler/CompilerTests.java +++ b/graalpython/com.oracle.graal.python.test/src/com/oracle/graal/python/test/compiler/CompilerTests.java @@ -51,6 +51,8 @@ import org.hamcrest.CoreMatchers; import org.hamcrest.MatcherAssert; import org.junit.Assert; +import org.junit.Assume; +import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.rules.TestName; @@ -64,6 +66,7 @@ import com.oracle.graal.python.pegparser.Parser; import com.oracle.graal.python.pegparser.sst.ModTy; import com.oracle.graal.python.pegparser.tokenizer.SourceRange; +import com.oracle.graal.python.runtime.PythonOptions; import com.oracle.graal.python.test.GraalPythonEnvVars; import com.oracle.graal.python.test.PythonTests; @@ -71,6 +74,13 @@ public class CompilerTests extends PythonTests { public CompilerTests() { } + @Before + public void beforeTest() { + // These tests are coupled to the manual bytecode interpreter. They shouldn't run if we're + // using the Bytecode DSL interpreter. + Assume.assumeFalse(PythonOptions.ENABLE_BYTECODE_DSL_INTERPRETER); + } + @Rule public TestName name = new TestName(); @Test diff --git a/graalpython/com.oracle.graal.python.test/src/tests/cpyext/test_shutdown.py b/graalpython/com.oracle.graal.python.test/src/tests/cpyext/test_shutdown.py index 88ba72a09c..7be19de1cf 100644 --- a/graalpython/com.oracle.graal.python.test/src/tests/cpyext/test_shutdown.py +++ b/graalpython/com.oracle.graal.python.test/src/tests/cpyext/test_shutdown.py @@ -51,6 +51,8 @@ ARGS = [] if sys.implementation.name == 'graalpy': ARGS = ['--python.EnableDebuggingBuiltins'] + if not __graalpython__.is_native and __graalpython__.is_bytecode_dsl_interpreter: + ARGS += ['--vm.Dpython.EnableBytecodeDSLInterpreter=true'] COMMAND = [sys.executable, *ARGS, str(MODULE_PATH)] diff --git a/graalpython/com.oracle.graal.python.test/src/tests/test_ssl_java_integration.py b/graalpython/com.oracle.graal.python.test/src/tests/test_ssl_java_integration.py index 6582d17f04..f21974b29c 100644 --- a/graalpython/com.oracle.graal.python.test/src/tests/test_ssl_java_integration.py +++ b/graalpython/com.oracle.graal.python.test/src/tests/test_ssl_java_integration.py @@ -63,4 +63,9 @@ def test_load_default_verify_keystore(): """) env = os.environ.copy() env['JAVA_TOOL_OPTIONS'] = f"-Djavax.net.ssl.trustStore={curdir}/ssldata/signing_keystore.jks" - subprocess.run([sys.executable, '-c', src], env=env, check=True) + + args = [] + if __graalpython__.is_bytecode_dsl_interpreter: + args += ['--vm.Dpython.EnableBytecodeDSLInterpreter=true'] + + subprocess.run([sys.executable, *args, '-c', src], env=env, check=True) diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/PythonLanguage.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/PythonLanguage.java index f8c06dba57..6fd62e47c2 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/PythonLanguage.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/PythonLanguage.java @@ -80,12 +80,16 @@ import com.oracle.graal.python.builtins.objects.type.PythonManagedClass; import com.oracle.graal.python.builtins.objects.type.TpSlots; import com.oracle.graal.python.builtins.objects.type.slots.TpSlot; +import com.oracle.graal.python.compiler.BytecodeCodeUnit; import com.oracle.graal.python.compiler.CodeUnit; import com.oracle.graal.python.compiler.CompilationUnit; import com.oracle.graal.python.compiler.Compiler; import com.oracle.graal.python.compiler.RaisePythonExceptionErrorCallback; +import com.oracle.graal.python.compiler.bytecode_dsl.BytecodeDSLCompiler; +import com.oracle.graal.python.compiler.bytecode_dsl.BytecodeDSLCompiler.BytecodeDSLCompilerResult; import com.oracle.graal.python.nodes.HiddenAttr; import com.oracle.graal.python.nodes.bytecode.PBytecodeRootNode; +import com.oracle.graal.python.nodes.bytecode_dsl.BytecodeDSLCodeUnit; import com.oracle.graal.python.nodes.call.CallNode; import com.oracle.graal.python.nodes.call.GenericInvokeNode; import com.oracle.graal.python.nodes.exception.TopLevelExceptionHandler; @@ -568,6 +572,7 @@ protected CallTarget parse(ParsingRequest request) { if (MIME_TYPE_BYTECODE.equals(source.getMimeType())) { byte[] bytes = source.getBytes().toByteArray(); CodeUnit code = MarshalModuleBuiltins.deserializeCodeUnit(bytes); + boolean internal = shouldMarkSourceInternal(context); // The original file path should be passed as the name String name = source.getName(); @@ -592,7 +597,18 @@ protected CallTarget parse(ParsingRequest request) { if (internal && !source.isInternal()) { source = Source.newBuilder(source).internal(true).build(); } - PBytecodeRootNode rootNode = PBytecodeRootNode.create(this, code, source); + RootNode rootNode = null; + + if (PythonOptions.ENABLE_BYTECODE_DSL_INTERPRETER) { + if (source.hasBytes()) { + // Force a character-based source so that source sections work as expected. + source = Source.newBuilder(source).content(Source.CONTENT_NONE).build(); + } + rootNode = ((BytecodeDSLCodeUnit) code).createRootNode(context, source); + } else { + rootNode = PBytecodeRootNode.create(this, (BytecodeCodeUnit) code, source); + } + return PythonUtils.getOrCreateCallTarget(rootNode); } @@ -634,7 +650,7 @@ public RootCallTarget parse(PythonContext context, Source source, InputType type Parser parser = Compiler.createParser(source.getCharacters().toString(), errorCb, type, interactiveTerminal); ModTy mod = (ModTy) parser.parse(); assert mod != null; - return compileForBytecodeInterpreter(context, mod, source, topLevel, optimize, argumentNames, errorCb, futureFeatures); + return compileModule(context, mod, source, topLevel, optimize, argumentNames, errorCb, futureFeatures); } catch (PException e) { if (topLevel) { PythonUtils.getOrCreateCallTarget(new TopLevelExceptionHandler(this, e)).call(); @@ -644,20 +660,19 @@ public RootCallTarget parse(PythonContext context, Source source, InputType type } @TruffleBoundary - public RootCallTarget compileForBytecodeInterpreter(PythonContext context, ModTy mod, Source source, boolean topLevel, int optimize, List argumentNames, + public RootCallTarget compileModule(PythonContext context, ModTy modIn, Source source, boolean topLevel, int optimize, List argumentNames, RaisePythonExceptionErrorCallback errorCallback, int flags) { - return compileForBytecodeInterpreter(context, mod, source, topLevel, optimize, argumentNames, errorCallback, FutureFeature.fromFlags(flags)); + return compileModule(context, modIn, source, topLevel, optimize, argumentNames, errorCallback, FutureFeature.fromFlags(flags)); } @TruffleBoundary - public RootCallTarget compileForBytecodeInterpreter(PythonContext context, ModTy modIn, Source source, boolean topLevel, int optimize, List argumentNames, + public RootCallTarget compileModule(PythonContext context, ModTy modIn, Source source, boolean topLevel, int optimize, List argumentNames, RaisePythonExceptionErrorCallback errorCallback, EnumSet futureFeatures) { RaisePythonExceptionErrorCallback errorCb = errorCallback; if (errorCb == null) { errorCb = new RaisePythonExceptionErrorCallback(source, PythonOptions.isPExceptionWithJavaStacktrace(this)); } try { - Compiler compiler = new Compiler(errorCb); boolean hasArguments = argumentNames != null && !argumentNames.isEmpty(); final ModTy mod; if (hasArguments) { @@ -665,9 +680,14 @@ public RootCallTarget compileForBytecodeInterpreter(PythonContext context, ModTy } else { mod = modIn; } - CompilationUnit cu = compiler.compile(mod, EnumSet.noneOf(Compiler.Flags.class), optimize, futureFeatures); - CodeUnit co = cu.assemble(); - RootNode rootNode = PBytecodeRootNode.create(this, co, source, errorCb); + + RootNode rootNode; + if (PythonOptions.ENABLE_BYTECODE_DSL_INTERPRETER) { + rootNode = compileForBytecodeDSLInterpreter(context, mod, source, optimize, errorCb, futureFeatures); + } else { + rootNode = compileForBytecodeInterpreter(mod, source, optimize, errorCb, futureFeatures); + } + if (topLevel) { GilNode gil = GilNode.getUncached(); boolean wasAcquired = gil.acquire(context, rootNode); @@ -692,6 +712,19 @@ public RootCallTarget compileForBytecodeInterpreter(PythonContext context, ModTy } } + private RootNode compileForBytecodeInterpreter(ModTy mod, Source source, int optimize, RaisePythonExceptionErrorCallback errorCallback, EnumSet futureFeatures) { + Compiler compiler = new Compiler(errorCallback); + CompilationUnit cu = compiler.compile(mod, EnumSet.noneOf(Compiler.Flags.class), optimize, futureFeatures); + BytecodeCodeUnit co = cu.assemble(); + return PBytecodeRootNode.create(this, co, source, errorCallback); + } + + private RootNode compileForBytecodeDSLInterpreter(PythonContext context, ModTy mod, Source source, int optimize, + RaisePythonExceptionErrorCallback errorCallback, EnumSet futureFeatures) { + BytecodeDSLCompilerResult result = BytecodeDSLCompiler.compile(this, context, mod, source, optimize, errorCallback, futureFeatures); + return result.rootNode(); + } + private static ModTy transformASTForExecutionWithArguments(List argumentNames, ModTy mod) { NodeFactory nodeFactory = new NodeFactory(); ArgTy[] astArgArray = new ArgTy[argumentNames.size()]; diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/BuiltinFunctions.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/BuiltinFunctions.java index 0879085729..630fa2ed0c 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/BuiltinFunctions.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/BuiltinFunctions.java @@ -203,6 +203,7 @@ import com.oracle.graal.python.nodes.builtins.ListNodes.ConstructListNode; import com.oracle.graal.python.nodes.bytecode.GetAIterNode; import com.oracle.graal.python.nodes.bytecode.PBytecodeRootNode; +import com.oracle.graal.python.nodes.bytecode_dsl.PBytecodeDSLRootNode; import com.oracle.graal.python.nodes.call.CallDispatchNode; import com.oracle.graal.python.nodes.call.CallNode; import com.oracle.graal.python.nodes.call.GenericInvokeNode; @@ -264,6 +265,7 @@ import com.oracle.truffle.api.HostCompilerDirectives.InliningCutoff; import com.oracle.truffle.api.RootCallTarget; import com.oracle.truffle.api.TruffleLanguage.Env; +import com.oracle.truffle.api.bytecode.OperationProxy; import com.oracle.truffle.api.debug.Debugger; import com.oracle.truffle.api.dsl.Bind; import com.oracle.truffle.api.dsl.Cached; @@ -1199,7 +1201,7 @@ Object generic(VirtualFrame frame, Object wSource, Object wFilename, TruffleStri if (AstModuleBuiltins.isAst(getContext(), wSource)) { ModTy mod = AstModuleBuiltins.obj2sst(getContext(), wSource, getParserInputType(mode, flags)); Source source = PythonUtils.createFakeSource(filename); - RootCallTarget rootCallTarget = getLanguage().compileForBytecodeInterpreter(getContext(), mod, source, false, optimize, null, null, flags); + RootCallTarget rootCallTarget = getLanguage().compileModule(getContext(), mod, source, false, optimize, null, null, flags); return wrapRootCallTarget(rootCallTarget, factory); } TruffleString source = sourceAsString(frame, inliningTarget, wSource, filename, interopLib, acquireLib, bufferLib, handleDecodingErrorNode, asStrNode, switchEncodingNode, factory, @@ -1210,8 +1212,12 @@ Object generic(VirtualFrame frame, Object wSource, Object wFilename, TruffleStri private static PCode wrapRootCallTarget(RootCallTarget rootCallTarget, PythonObjectFactory factory) { RootNode rootNode = rootCallTarget.getRootNode(); - if (rootNode instanceof PBytecodeRootNode) { - ((PBytecodeRootNode) rootNode).triggerDeferredDeprecationWarnings(); + if (PythonOptions.ENABLE_BYTECODE_DSL_INTERPRETER) { + if (rootNode instanceof PBytecodeDSLRootNode bytecodeDSLRootNode) { + bytecodeDSLRootNode.triggerDeferredDeprecationWarnings(); + } + } else if (rootNode instanceof PBytecodeRootNode bytecodeRootNode) { + bytecodeRootNode.triggerDeferredDeprecationWarnings(); } return factory.createCode(rootCallTarget); } @@ -1997,6 +2003,7 @@ static Object repr(VirtualFrame frame, Object obj, // format(object, [format_spec]) @Builtin(name = J_FORMAT, minNumOfPositionalArgs = 1, parameterNames = {"object", "format_spec"}) @GenerateNodeFactory + @OperationProxy.Proxyable @ImportStatic(PGuards.class) public abstract static class FormatNode extends PythonBinaryBuiltinNode { 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 f0f616ce35..1a7be4617e 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 @@ -130,6 +130,7 @@ import com.oracle.graal.python.nodes.PRaiseNode; import com.oracle.graal.python.nodes.builtins.FunctionNodes.GetCallTargetNode; import com.oracle.graal.python.nodes.bytecode.PBytecodeRootNode; +import com.oracle.graal.python.nodes.bytecode_dsl.PBytecodeDSLRootNode; import com.oracle.graal.python.nodes.call.CallNode; import com.oracle.graal.python.nodes.classes.IsSubtypeNode; import com.oracle.graal.python.nodes.function.BuiltinFunctionRootNode; @@ -204,6 +205,7 @@ protected List> getNodeFa public void initialize(Python3Core core) { super.initialize(core); addBuiltinConstant("is_native", ImageInfo.inImageCode()); + addBuiltinConstant("is_bytecode_dsl_interpreter", PythonOptions.ENABLE_BYTECODE_DSL_INTERPRETER); PythonContext ctx = core.getContext(); TruffleString encodingOpt = ctx.getLanguage().getEngineOption(PythonOptions.StandardStreamEncoding); TruffleString standardStreamEncoding = null; @@ -522,8 +524,12 @@ public Object doIt(VirtualFrame frame, PFunction func, @TruffleBoundary public synchronized PFunction convertToBuiltin(PFunction func) { RootNode rootNode = CodeNodes.GetCodeRootNode.executeUncached(func.getCode()); - if (rootNode instanceof PBytecodeRootNode) { - ((PBytecodeRootNode) rootNode).setPythonInternal(true); + if (PythonOptions.ENABLE_BYTECODE_DSL_INTERPRETER) { + if (rootNode instanceof PBytecodeDSLRootNode r) { + r.setPythonInternal(true); + } + } else if (rootNode instanceof PBytecodeRootNode r) { + r.setPythonInternal(true); } func.setBuiltin(true); return func; @@ -538,8 +544,12 @@ public Object doIt(PFunction func, @Bind("this") Node inliningTarget, @Cached CodeNodes.GetCodeRootNode getRootNode) { RootNode rootNode = getRootNode.execute(inliningTarget, func.getCode()); - if (rootNode instanceof PBytecodeRootNode) { - ((PBytecodeRootNode) rootNode).setPythonInternal(true); + if (PythonOptions.ENABLE_BYTECODE_DSL_INTERPRETER) { + if (rootNode instanceof PBytecodeDSLRootNode r) { + r.setPythonInternal(true); + } + } else if (rootNode instanceof PBytecodeRootNode r) { + r.setPythonInternal(true); } return func; } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/MarshalModuleBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/MarshalModuleBuiltins.java index 32ddad0ee3..230e61214d 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/MarshalModuleBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/MarshalModuleBuiltins.java @@ -35,6 +35,11 @@ import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; +import java.io.DataInput; +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.EOFException; +import java.io.DataOutput; import java.io.IOException; import java.io.InputStream; import java.math.BigInteger; @@ -43,7 +48,9 @@ import java.util.ArrayList; import java.util.HashMap; import java.util.List; +import java.util.function.Supplier; +import com.oracle.graal.python.PythonLanguage; import com.oracle.graal.python.annotations.ArgumentClinic; import com.oracle.graal.python.annotations.ArgumentClinic.ClinicConversion; import com.oracle.graal.python.builtins.Builtin; @@ -73,13 +80,16 @@ import com.oracle.graal.python.builtins.objects.common.SequenceStorageNodes.GetInternalObjectArrayNode; import com.oracle.graal.python.builtins.objects.complex.PComplex; import com.oracle.graal.python.builtins.objects.dict.PDict; +import com.oracle.graal.python.builtins.objects.ellipsis.PEllipsis; import com.oracle.graal.python.builtins.objects.floats.PFloat; +import com.oracle.graal.python.builtins.objects.function.PKeyword; import com.oracle.graal.python.builtins.objects.ints.PInt; import com.oracle.graal.python.builtins.objects.set.PBaseSet; import com.oracle.graal.python.builtins.objects.str.PString; import com.oracle.graal.python.builtins.objects.str.StringNodes; import com.oracle.graal.python.builtins.objects.str.StringNodes.IsInternedStringNode; import com.oracle.graal.python.builtins.objects.type.TypeNodes.IsSameTypeNode; +import com.oracle.graal.python.compiler.BytecodeCodeUnit; import com.oracle.graal.python.compiler.CodeUnit; import com.oracle.graal.python.compiler.Compiler; import com.oracle.graal.python.lib.PyComplexCheckExactNode; @@ -95,6 +105,9 @@ import com.oracle.graal.python.lib.PyUnicodeCheckExactNode; import com.oracle.graal.python.nodes.ErrorMessages; import com.oracle.graal.python.nodes.PRaiseNode; +import com.oracle.graal.python.nodes.bytecode_dsl.BytecodeDSLCodeUnit; +import com.oracle.graal.python.nodes.bytecode_dsl.PBytecodeDSLRootNode; +import com.oracle.graal.python.nodes.bytecode_dsl.PBytecodeDSLRootNodeGen; import com.oracle.graal.python.nodes.call.special.LookupAndCallBinaryNode; import com.oracle.graal.python.nodes.function.PythonBuiltinBaseNode; import com.oracle.graal.python.nodes.function.PythonBuiltinNode; @@ -105,6 +118,7 @@ import com.oracle.graal.python.runtime.ExecutionContext.IndirectCallContext; import com.oracle.graal.python.runtime.IndirectCallData; import com.oracle.graal.python.runtime.PythonContext; +import com.oracle.graal.python.runtime.PythonOptions; import com.oracle.graal.python.runtime.object.PythonObjectFactory; import com.oracle.graal.python.runtime.sequence.storage.ByteSequenceStorage; import com.oracle.graal.python.runtime.sequence.storage.SequenceStorage; @@ -113,6 +127,11 @@ import com.oracle.truffle.api.CompilerDirectives; import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; import com.oracle.truffle.api.dsl.Bind; +import com.oracle.truffle.api.bytecode.BytecodeConfig; +import com.oracle.truffle.api.bytecode.BytecodeRootNodes; +import com.oracle.truffle.api.bytecode.serialization.BytecodeDeserializer; +import com.oracle.truffle.api.bytecode.serialization.BytecodeSerializer; +import com.oracle.truffle.api.bytecode.serialization.SerializationUtils; import com.oracle.truffle.api.dsl.Cached; import com.oracle.truffle.api.dsl.GenerateNodeFactory; import com.oracle.truffle.api.dsl.NeverDefault; @@ -122,13 +141,14 @@ import com.oracle.truffle.api.library.CachedLibrary; import com.oracle.truffle.api.memory.ByteArraySupport; import com.oracle.truffle.api.nodes.Node; +import com.oracle.truffle.api.source.Source; import com.oracle.truffle.api.strings.InternalByteArray; import com.oracle.truffle.api.strings.TruffleString; import com.oracle.truffle.api.strings.TruffleString.Encoding; @CoreFunctions(defineModule = "marshal") public final class MarshalModuleBuiltins extends PythonBuiltins { - static final int CURRENT_VERSION = 4; + static final int CURRENT_VERSION = 5; @Override protected List> getNodeFactories() { @@ -298,6 +318,10 @@ static final class Marshal { private static final char TYPE_GRAALPYTHON_CODE_UNIT = 'U'; private static final char TYPE_BIG_INTEGER = 'B'; private static final char TYPE_ARRAY = ']'; + // These are constants that show up in the Bytecode DSL interpreter. + private static final char TYPE_GRAALPYTHON_DSL_CODE_UNIT = 'D'; + private static final char TYPE_DSL_SOURCE = '$'; + private static final char TYPE_DSL_EMPTY_KEYWORDS = 'k'; private static final char ARRAY_TYPE_OBJECT = 'o'; private static final char ARRAY_TYPE_INT = 'i'; @@ -349,7 +373,7 @@ public final Throwable fillInStackTrace() { static byte[] dump(Object value, int version, Python3Core core) throws IOException, MarshalError { Marshal outMarshal = new Marshal(version, core.getTrue(), core.getFalse()); outMarshal.writeObject(value); - return outMarshal.out.toByteArray(); + return outMarshal.outData.toByteArray(); } @TruffleBoundary @@ -421,8 +445,9 @@ public int read(byte[] b, int off, int len) { private static final PythonObjectFactory factory = PythonObjectFactory.getUncached(); final HashMap refMap; final ArrayList refList; - final ByteArrayOutputStream out; - final InputStream in; + final ByteArrayOutputStream outData; + final DataOutput out; + final DataInput in; final int version; final PInt pyTrue; final PInt pyFalse; @@ -430,50 +455,81 @@ public int read(byte[] b, int off, int len) { final ByteArraySupport baSupport = ByteArraySupport.littleEndian(); byte[] buffer = new byte[Long.BYTES]; int depth = 0; + /* + * A DSL node needs access to its Source during deserialization, but we do not wish to + * actually encode it in the serialized representation. Instead, we supply a Source to the + * Marshal object and return it when the source is needed. + */ + Source source = null; Marshal(int version, PInt pyTrue, PInt pyFalse) { this.version = version; this.pyTrue = pyTrue; this.pyFalse = pyFalse; - this.out = new ByteArrayOutputStream(); + this.outData = new ByteArrayOutputStream(); + this.out = new DataOutputStream(outData); + this.refMap = new HashMap<>(); + this.in = null; + this.refList = null; + } + + Marshal(int version, PInt pyTrue, PInt pyFalse, DataOutput out) { + this.version = version; + this.pyTrue = pyTrue; + this.pyFalse = pyFalse; + this.outData = null; + this.out = out; this.refMap = new HashMap<>(); this.in = null; this.refList = null; } Marshal(byte[] in, int length) { - this.in = new ByteArrayInputStream(in, 0, length); - this.refList = new ArrayList<>(); - this.version = -1; - this.pyTrue = null; - this.pyFalse = null; - this.out = null; - this.refMap = null; + this(new DataInputStream(new ByteArrayInputStream(in, 0, length)), null); } Marshal(Object in) { - this.in = new FileLikeInputStream(in); + this(new DataInputStream(new FileLikeInputStream(in)), null); + } + + Marshal(DataInput in, Source source) { + this.in = in; + this.source = source; this.refList = new ArrayList<>(); this.version = -1; this.pyTrue = null; this.pyFalse = null; + this.outData = null; this.out = null; this.refMap = null; } private void writeByte(int v) { - out.write(v); + try { + out.write(v); + } catch (IOException e) { + // The underlying output streams we use should not throw IOExceptions. + throw CompilerDirectives.shouldNotReachHere(); + } } - private int readByte() { - int nextByte; + private void writeBytes(byte[] b, int off, int len) { try { - nextByte = in.read(); + out.write(b, off, len); } catch (IOException e) { + // The underlying output streams we use should not throw IOExceptions. throw CompilerDirectives.shouldNotReachHere(); } - if (nextByte < 0) { + } + + private int readByte() { + int nextByte; + try { + nextByte = in.readUnsignedByte(); + } catch (EOFException e) { throw new MarshalError(PythonBuiltinClassType.EOFError, ErrorMessages.BAD_MARSHAL_DATA_EOF); + } catch (IOException e) { + throw CompilerDirectives.shouldNotReachHere(); } return nextByte; } @@ -517,15 +573,13 @@ private byte[] readNBytes(int sz, byte[] output) { if (sz == 0) { return output; } - int read; try { - read = in.read(output, 0, sz); + in.readFully(output, 0, sz); + } catch (EOFException e) { + throw new MarshalError(PythonBuiltinClassType.EOFError, ErrorMessages.BAD_MARSHAL_DATA_EOF); } catch (IOException e) { throw CompilerDirectives.shouldNotReachHere(); } - if (read < sz) { - throw new MarshalError(PythonBuiltinClassType.EOFError, ErrorMessages.BAD_MARSHAL_DATA_EOF); - } return output; } @@ -536,13 +590,13 @@ private byte[] readBytes() { private void writeInt(int v) { for (int i = 0; i < Integer.SIZE; i += Byte.SIZE) { - out.write((v >> i) & 0xff); + writeByte((v >> i) & 0xff); } } private void writeShort(short v) { for (int i = 0; i < Short.SIZE; i += Byte.SIZE) { - out.write((v >> i) & 0xff); + writeByte((v >> i) & 0xff); } } @@ -556,7 +610,7 @@ private short readShort() { private void writeLong(long v) { for (int i = 0; i < Long.SIZE; i += Byte.SIZE) { - out.write((int) ((v >>> i) & 0xff)); + writeByte((int) ((v >>> i) & 0xff)); } } @@ -582,7 +636,7 @@ private void writeBigInteger(BigInteger v) { } for (int digit : digits) { for (int i = 0; i < Short.SIZE; i += Byte.SIZE) { - out.write((digit >> i) & 0xff); + writeByte((digit >> i) & 0xff); } } } @@ -677,7 +731,7 @@ private void writeObject(Object v) throws IOException { writeByte(TYPE_NOVALUE); } else if (IsSameTypeNode.executeUncached(v, PythonBuiltinClassType.StopIteration)) { writeByte(TYPE_STOPITER); - } else if (IsSameTypeNode.executeUncached(v, PythonBuiltinClassType.PEllipsis)) { + } else if (v == PEllipsis.INSTANCE) { writeByte(TYPE_ELLIPSIS); } else if (v == Boolean.TRUE || v == pyTrue) { writeByte(TYPE_TRUE); @@ -826,6 +880,9 @@ private void writeComplexObject(Object v, int flag) { writeByte(TYPE_ARRAY | flag); writeByte(ARRAY_TYPE_STRING); writeStringArray((TruffleString[]) v); + } else if (v instanceof PKeyword[]) { + assert v == PKeyword.EMPTY_KEYWORDS; + writeByte(TYPE_DSL_EMPTY_KEYWORDS); } else if (v instanceof Object[]) { writeByte(TYPE_ARRAY | flag); writeByte(ARRAY_TYPE_OBJECT); @@ -845,8 +902,16 @@ private void writeComplexObject(Object v, int flag) { } writeBytes(lnotab); } else if (v instanceof CodeUnit) { - writeByte(TYPE_GRAALPYTHON_CODE_UNIT | flag); - writeCodeUnit((CodeUnit) v); + if (PythonOptions.ENABLE_BYTECODE_DSL_INTERPRETER) { + writeByte(TYPE_GRAALPYTHON_DSL_CODE_UNIT | flag); + writeBytecodeDSLCodeUnit((BytecodeDSLCodeUnit) v); + } else { + writeByte(TYPE_GRAALPYTHON_CODE_UNIT | flag); + writeBytecodeCodeUnit((BytecodeCodeUnit) v); + } + } else if (v instanceof Source s) { + writeByte(TYPE_DSL_SOURCE | flag); + setSource(s); } else { PythonBufferAcquireLibrary acquireLib = PythonBufferAcquireLibrary.getFactory().getUncached(v); if (acquireLib.hasBuffer(v)) { @@ -967,7 +1032,7 @@ private Object readObject(int type, AddRefAndReturn addRef) throws NumberFormatE case TYPE_STOPITER: return PythonBuiltinClassType.StopIteration; case TYPE_ELLIPSIS: - return PythonBuiltinClassType.PEllipsis; + return PEllipsis.INSTANCE; case TYPE_FALSE: return false; case TYPE_TRUE: @@ -1059,10 +1124,15 @@ private Object readObject(int type, AddRefAndReturn addRef) throws NumberFormatE case TYPE_GRAALPYTHON_CODE: return addRef.run(readCode()); case TYPE_GRAALPYTHON_CODE_UNIT: - return addRef.run(readCodeUnit()); - case TYPE_ARRAY: { + return addRef.run(readBytecodeCodeUnit()); + case TYPE_GRAALPYTHON_DSL_CODE_UNIT: + return addRef.run(readBytecodeDSLCodeUnit()); + case TYPE_DSL_SOURCE: + return getSource(); + case TYPE_DSL_EMPTY_KEYWORDS: + return PKeyword.EMPTY_KEYWORDS; + case TYPE_ARRAY: return addRef.run(readJavaArray()); - } default: throw new MarshalError(ValueError, ErrorMessages.BAD_MARSHAL_DATA); } @@ -1082,7 +1152,7 @@ private void writeString(TruffleString v) { } InternalByteArray ba = v.switchEncodingUncached(encoding).getInternalByteArrayUncached(encoding); writeSize(ba.getLength()); - out.write(ba.getArray(), ba.getOffset(), ba.getLength()); + writeBytes(ba.getArray(), ba.getOffset(), ba.getLength()); } private TruffleString readString() { @@ -1215,6 +1285,24 @@ private Object[] readObjectArray() { return a; } + private void setSource(Source s) { + if (source == null) { + source = s; + } else if (source != s) { + throw CompilerDirectives.shouldNotReachHere("attempted to serialize with multiple Source objects"); + } + } + + private Source getSource() { + if (source != null) { + return source; + } else { + // This should never happen when deserializing a bytecode DSL code unit, but could + // happen if the user tries to deserialize arbitrary bytes. + throw new MarshalError(ValueError, ErrorMessages.BAD_MARSHAL_DATA); + } + } + private void writeSparseTable(int[][] table) { writeInt(table.length); for (int i = 0; i < table.length; i++) { @@ -1239,6 +1327,21 @@ private int[][] readSparseTable() { } private CodeUnit readCodeUnit() { + int codeUnitType = readByte(); + return switch (codeUnitType) { + case TYPE_GRAALPYTHON_CODE_UNIT -> readBytecodeCodeUnit(); + case TYPE_GRAALPYTHON_DSL_CODE_UNIT -> readBytecodeDSLCodeUnit(); + default -> throw CompilerDirectives.shouldNotReachHere(); + }; + } + + private BytecodeCodeUnit readBytecodeCodeUnit() { + if (PythonOptions.ENABLE_BYTECODE_DSL_INTERPRETER) { + throw new MarshalError(ValueError, + PythonUtils.tsLiteral( + "Attempted to deserialize a code object from the manual bytecode interpreter, but the DSL interpreter is enabled. Consider clearing or setting a different pycache folder.")); + } + int fileVersion = readByte(); if (fileVersion != Compiler.BYTECODE_VERSION) { throw new MarshalError(ValueError, ErrorMessages.BYTECODE_VERSION_MISMATCH, Compiler.BYTECODE_VERSION, fileVersion); @@ -1272,13 +1375,57 @@ private CodeUnit readCodeUnit() { byte[] variableShouldUnbox = readBytes(); int[][] generalizeInputsMap = readSparseTable(); int[][] generalizeVarsMap = readSparseTable(); - return new CodeUnit(name, qualname, argCount, kwOnlyArgCount, positionalOnlyArgCount, stacksize, code, srcOffsetTable, - flags, names, varnames, cellvars, freevars, cell2arg, constants, primitiveConstants, exceptionHandlerRanges, conditionProfileCount, - startLine, startColumn, endLine, endColumn, + return new BytecodeCodeUnit(name, qualname, argCount, kwOnlyArgCount, positionalOnlyArgCount, flags, names, varnames, + cellvars, freevars, cell2arg, constants, startLine, startColumn, endLine, endColumn, code, srcOffsetTable, + primitiveConstants, exceptionHandlerRanges, stacksize, conditionProfileCount, outputCanQuicken, variableShouldUnbox, generalizeInputsMap, generalizeVarsMap); } + private BytecodeDSLCodeUnit readBytecodeDSLCodeUnit() { + if (!PythonOptions.ENABLE_BYTECODE_DSL_INTERPRETER) { + throw new MarshalError(ValueError, + PythonUtils.tsLiteral( + "Attempted to deserialize a code object from the Bytecode DSL interpreter, but the manual interpreter is enabled. Consider clearing or setting a different pycache folder.")); + } + + byte[] serialized = readBytes(); + TruffleString name = readString(); + TruffleString qualname = readString(); + int argCount = readInt(); + int kwOnlyArgCount = readInt(); + int positionalOnlyArgCount = readInt(); + int flags = readInt(); + TruffleString[] names = readStringArray(); + TruffleString[] varnames = readStringArray(); + TruffleString[] cellvars = readStringArray(); + TruffleString[] freevars = readStringArray(); + int[] cell2arg = readIntArray(); + if (cell2arg.length == 0) { + cell2arg = null; + } + Object[] constants = readObjectArray(); + int startLine = readInt(); + int startColumn = readInt(); + int endLine = readInt(); + int endColumn = readInt(); + int classcellIndex = readInt(); + int selfIndex = readInt(); + + return new BytecodeDSLCodeUnit(name, qualname, argCount, kwOnlyArgCount, positionalOnlyArgCount, flags, names, varnames, cellvars, freevars, cell2arg, constants, + startLine, startColumn, endLine, endColumn, classcellIndex, selfIndex, serialized, null); + } + private void writeCodeUnit(CodeUnit code) throws IOException { + if (PythonOptions.ENABLE_BYTECODE_DSL_INTERPRETER) { + writeByte(TYPE_GRAALPYTHON_DSL_CODE_UNIT); + writeBytecodeDSLCodeUnit((BytecodeDSLCodeUnit) code); + } else { + writeByte(TYPE_GRAALPYTHON_CODE_UNIT); + writeBytecodeCodeUnit((BytecodeCodeUnit) code); + } + } + + private void writeBytecodeCodeUnit(BytecodeCodeUnit code) throws IOException { writeByte(Compiler.BYTECODE_VERSION); writeString(code.name); writeString(code.qualname); @@ -1312,6 +1459,34 @@ private void writeCodeUnit(CodeUnit code) throws IOException { writeSparseTable(code.generalizeVarsMap); } + @SuppressWarnings("unchecked") + private void writeBytecodeDSLCodeUnit(BytecodeDSLCodeUnit code) throws IOException { + byte[] serialized = code.getSerialized(pyTrue, pyFalse); + writeBytes(serialized); + writeString(code.name); + writeString(code.qualname); + writeInt(code.argCount); + writeInt(code.kwOnlyArgCount); + writeInt(code.positionalOnlyArgCount); + writeInt(code.flags); + writeStringArray(code.names); + writeStringArray(code.varnames); + writeStringArray(code.cellvars); + writeStringArray(code.freevars); + if (code.cell2arg != null) { + writeIntArray(code.cell2arg); + } else { + writeIntArray(PythonUtils.EMPTY_INT_ARRAY); + } + writeObjectArray(code.constants); + writeInt(code.startLine); + writeInt(code.startColumn); + writeInt(code.endLine); + writeInt(code.endColumn); + writeInt(code.classcellIndex); + writeInt(code.selfIndex); + } + private PCode readCode() { TruffleString fileName = readString(); int flags = readInt(); @@ -1319,7 +1494,7 @@ private PCode readCode() { int codeLen = readSize(); byte[] codeString = new byte[codeLen + Long.BYTES]; try { - in.read(codeString, 0, codeLen); + in.readFully(codeString, 0, codeLen); } catch (IOException e) { throw CompilerDirectives.shouldNotReachHere(); } @@ -1339,7 +1514,7 @@ public static byte[] serializeCodeUnit(CodeUnit code) { try { Marshal marshal = new Marshal(CURRENT_VERSION, null, null); marshal.writeCodeUnit(code); - return marshal.out.toByteArray(); + return marshal.outData.toByteArray(); } catch (IOException e) { throw CompilerDirectives.shouldNotReachHere(e); } catch (Marshal.MarshalError me) { @@ -1358,4 +1533,48 @@ public static CodeUnit deserializeCodeUnit(byte[] bytes) { throw PRaiseNode.getUncached().raise(ValueError, ErrorMessages.BAD_MARSHAL_DATA_S, e.getMessage()); } } + + public static BytecodeRootNodes deserializeBytecodeNodes(PythonLanguage language, Source source, byte[] serialized) { + try { + Supplier supplier = () -> SerializationUtils.createDataInput(ByteBuffer.wrap(serialized)); + return PBytecodeDSLRootNodeGen.deserialize(language, BytecodeConfig.WITH_SOURCE, supplier, new MarshalModuleBuiltins.PBytecodeDSLDeserializer(source)); + } catch (IOException e) { + throw CompilerDirectives.shouldNotReachHere("Deserialization error."); + } + } + + public static class PBytecodeDSLSerializer implements BytecodeSerializer { + private final PInt pyTrue; + private final PInt pyFalse; + + public PBytecodeDSLSerializer(PInt pyTrue, PInt pyFalse) { + this.pyTrue = pyTrue; + this.pyFalse = pyFalse; + } + + public void serialize(SerializerContext context, DataOutput buffer, Object object) throws IOException { + /* + * NB: Since the deserializer uses a fresh Marshal instance for each object (see below) + * we must also do the same here. Otherwise, the encoding may be different (e.g., a + * reference for an already-emitted object). + */ + new Marshal(CURRENT_VERSION, pyTrue, pyFalse, buffer).writeObject(object); + } + } + + public static class PBytecodeDSLDeserializer implements BytecodeDeserializer { + final Source source; + + public PBytecodeDSLDeserializer(Source source) { + this.source = source; + } + + public Object deserialize(DeserializerContext context, DataInput buffer) throws IOException { + /* + * NB: Since a DSL node may reparse multiple times, we cannot reuse a common Marshal + * object across calls (each call may take a different buffer). + */ + return new Marshal(buffer, source).readObject(); + } + } } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cext/PythonCextCodeBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cext/PythonCextCodeBuiltins.java index 4c53354244..cd67388846 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cext/PythonCextCodeBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cext/PythonCextCodeBuiltins.java @@ -117,7 +117,7 @@ static int addr2line(PCode code, int lasti) { if (lasti < 0) { return code.co_firstlineno(); } - return code.bciToLine(code.lastiToBci(lasti)); + return code.lastiToLine(lasti); } } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/asyncio/GetAwaitableNode.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/asyncio/GetAwaitableNode.java index f48c021293..f729a18383 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/asyncio/GetAwaitableNode.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/asyncio/GetAwaitableNode.java @@ -51,20 +51,22 @@ import com.oracle.graal.python.nodes.call.special.CallUnaryMethodNode; import com.oracle.graal.python.nodes.call.special.LookupSpecialMethodSlotNode; import com.oracle.graal.python.nodes.object.GetClassNode; +import com.oracle.truffle.api.bytecode.OperationProxy; import com.oracle.truffle.api.dsl.Bind; import com.oracle.truffle.api.dsl.Cached; import com.oracle.truffle.api.dsl.Cached.Exclusive; import com.oracle.truffle.api.dsl.GenerateUncached; import com.oracle.truffle.api.dsl.ImportStatic; import com.oracle.truffle.api.dsl.Specialization; -import com.oracle.truffle.api.frame.Frame; +import com.oracle.truffle.api.frame.VirtualFrame; import com.oracle.truffle.api.nodes.Node; @GenerateUncached @ImportStatic(SpecialMethodSlot.class) +@OperationProxy.Proxyable @SuppressWarnings("truffle-inlining") public abstract class GetAwaitableNode extends Node { - public abstract Object execute(Frame frame, Object arg); + public abstract Object execute(VirtualFrame frame, Object arg); @Specialization public static Object doGenerator(PGenerator generator, @@ -83,7 +85,7 @@ public static Object doGenerator(PGenerator generator, } @Specialization - public static Object doGeneric(Frame frame, Object awaitable, + public static Object doGeneric(VirtualFrame frame, Object awaitable, @Bind("this") Node inliningTarget, @Exclusive @Cached PRaiseNode.Lazy raiseNoAwait, @Exclusive @Cached PRaiseNode.Lazy raiseNotIter, diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/asyncio/PAsyncGen.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/asyncio/PAsyncGen.java index bfeca4b659..e15483e01c 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/asyncio/PAsyncGen.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/asyncio/PAsyncGen.java @@ -58,7 +58,7 @@ public static PAsyncGen create(PythonLanguage lang, TruffleString name, TruffleS } private PAsyncGen(PythonLanguage lang, TruffleString name, TruffleString qualname, PBytecodeRootNode rootNode, RootCallTarget[] callTargets, Object[] arguments) { - super(lang, name, qualname, rootNode, callTargets, arguments, PythonBuiltinClassType.PAsyncGenerator, false); + super(lang, name, qualname, arguments, PythonBuiltinClassType.PAsyncGenerator, false, new BytecodeState(rootNode, callTargets)); } public boolean isClosed() { diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/code/CodeBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/code/CodeBuiltins.java index 013bdc4b46..beae2b7281 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/code/CodeBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/code/CodeBuiltins.java @@ -50,12 +50,14 @@ import com.oracle.graal.python.builtins.objects.str.StringNodes.InternStringNode; import com.oracle.graal.python.builtins.objects.str.StringUtils.SimpleTruffleStringFormatNode; import com.oracle.graal.python.builtins.objects.tuple.PTuple; +import com.oracle.graal.python.compiler.BytecodeCodeUnit; import com.oracle.graal.python.compiler.CodeUnit; import com.oracle.graal.python.compiler.OpCodes; import com.oracle.graal.python.compiler.SourceMap; import com.oracle.graal.python.lib.PyObjectGetIter; import com.oracle.graal.python.lib.PyObjectHashNode; import com.oracle.graal.python.nodes.PGuards; +import com.oracle.graal.python.nodes.bytecode_dsl.PBytecodeDSLRootNode; import com.oracle.graal.python.nodes.function.PythonBuiltinBaseNode; import com.oracle.graal.python.nodes.function.builtins.PythonBinaryBuiltinNode; import com.oracle.graal.python.nodes.function.builtins.PythonClinicBuiltinNode; @@ -64,9 +66,13 @@ import com.oracle.graal.python.nodes.util.CastToTruffleStringNode; import com.oracle.graal.python.runtime.IndirectCallData; import com.oracle.graal.python.runtime.PythonContext; +import com.oracle.graal.python.runtime.PythonOptions; import com.oracle.graal.python.runtime.object.PythonObjectFactory; import com.oracle.graal.python.util.PythonUtils; import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; +import com.oracle.truffle.api.bytecode.BytecodeNode; +import com.oracle.truffle.api.bytecode.Instruction; +import com.oracle.truffle.api.bytecode.SourceInformationTree; import com.oracle.truffle.api.dsl.Bind; import com.oracle.truffle.api.dsl.Cached; import com.oracle.truffle.api.dsl.Fallback; @@ -76,6 +82,7 @@ import com.oracle.truffle.api.frame.VirtualFrame; import com.oracle.truffle.api.library.CachedLibrary; import com.oracle.truffle.api.nodes.Node; +import com.oracle.truffle.api.source.SourceSection; import com.oracle.truffle.api.strings.TruffleString; @CoreFunctions(extendClasses = PythonBuiltinClassType.PCode) @@ -300,26 +307,107 @@ static Object lines(PCode self) { PTuple tuple; CodeUnit co = self.getCodeUnit(); if (co != null) { - SourceMap map = co.getSourceMap(); - List lines = new ArrayList<>(); - if (map != null && map.startLineMap.length > 0) { - IteratorData data = new IteratorData(); - data.line = map.startLineMap[0]; - co.iterateBytecode((int bci, OpCodes op, int oparg, byte[] followingArgs) -> { - int nextStart = bci + op.length(); - if (map.startLineMap[bci] != data.line || nextStart == co.code.length) { - lines.add(factory.createTuple(new int[]{data.start, nextStart, data.line})); - data.line = map.startLineMap[bci]; - data.start = nextStart; - } - }); + if (PythonOptions.ENABLE_BYTECODE_DSL_INTERPRETER) { + PBytecodeDSLRootNode rootNode = (PBytecodeDSLRootNode) self.getRootNodeForExtraction(); + List lines = computeLinesForBytecodeDSLInterpreter(rootNode, factory); + tuple = factory.createTuple(lines.toArray()); + } else { + BytecodeCodeUnit bytecodeCo = (BytecodeCodeUnit) co; + SourceMap map = bytecodeCo.getSourceMap(); + List lines = new ArrayList<>(); + if (map != null && map.startLineMap.length > 0) { + IteratorData data = new IteratorData(); + data.line = map.startLineMap[0]; + bytecodeCo.iterateBytecode((int bci, OpCodes op, int oparg, byte[] followingArgs) -> { + int nextStart = bci + op.length(); + if (map.startLineMap[bci] != data.line || nextStart == bytecodeCo.code.length) { + lines.add(factory.createTuple(new int[]{data.start, nextStart, data.line})); + data.line = map.startLineMap[bci]; + data.start = nextStart; + } + }); + } + tuple = factory.createTuple(lines.toArray()); } - tuple = factory.createTuple(lines.toArray()); } else { tuple = factory.createEmptyTuple(); } return PyObjectGetIter.executeUncached(tuple); } + + private static List computeLinesForBytecodeDSLInterpreter(PBytecodeDSLRootNode root, PythonObjectFactory factory) { + BytecodeNode bytecodeNode = root.getBytecodeNode(); + List triples = new ArrayList<>(); + SourceInformationTree sourceInformationTree = bytecodeNode.getSourceInformationTree(); + assert sourceInformationTree.getSourceSection() != null; + traverseSourceInformationTree(sourceInformationTree, triples); + return convertTripleBcisToInstructionIndices(bytecodeNode, factory, triples); + } + + /** + * This function traverses the source information tree recursively to compute a list of + * consecutive bytecode ranges with their corresponding line numbers. + *

+ * Each node in the tree covers a bytecode range. Each child covers some sub-range. The + * bytecodes covered by a particular node are the bytecodes within its range that are *not* + * covered by the node's children. + *

+ * For example, consider a node covering [0, 20] with children covering [4, 9] and [15, 18]. + * The node itself covers the ranges [0, 4], [9, 15], and [18, 20]. These ranges are + * assigned the line number of the node. + */ + private static void traverseSourceInformationTree(SourceInformationTree tree, List triples) { + int startIndex = tree.getStartBytecodeIndex(); + int startLine = tree.getSourceSection().getStartLine(); + for (SourceInformationTree child : tree.getChildren()) { + if (startIndex < child.getStartBytecodeIndex()) { + // range before child.start is uncovered + triples.add(new int[]{startIndex, child.getStartBytecodeIndex(), startLine}); + } + // recursively handle [child.start, child.end] + traverseSourceInformationTree(child, triples); + startIndex = child.getEndBytecodeIndex(); + } + + if (startIndex < tree.getEndBytecodeIndex()) { + // range after last_child.end is uncovered + triples.add(new int[]{startIndex, tree.getEndBytecodeIndex(), startLine}); + } + } + + /** + * The bci ranges in the triples are not stable and can change when the bytecode is + * instrumented. We create new triples with stable instruction indices by walking the + * instructions. + */ + private static List convertTripleBcisToInstructionIndices(BytecodeNode bytecodeNode, PythonObjectFactory factory, List triples) { + List result = new ArrayList<>(triples.size()); + int tripleIndex = 0; + int[] triple = triples.get(0); + assert triple[0] == 0 : "the first bytecode range should start from 0"; + + int startInstructionIndex = 0; + int instructionIndex = 0; + for (Instruction instruction : bytecodeNode.getInstructions()) { + if (instruction.getBytecodeIndex() == triple[1] /* end bci */) { + result.add(factory.createTuple(new int[]{startInstructionIndex, instructionIndex, triple[2]})); + startInstructionIndex = instructionIndex; + triple = triples.get(++tripleIndex); + assert triple[0] == instruction.getBytecodeIndex() : "bytecode ranges should be consecutive"; + } + + if (!instruction.isInstrumentation()) { + // Emulate CPython's fixed 2-word instructions. + instructionIndex += 2; + } + } + + result.add(factory.createTuple(new int[]{startInstructionIndex, instructionIndex, triple[2]})); + assert tripleIndex == triples.size() : "every bytecode range should have been converted to an instruction range"; + + return result; + } + } @Builtin(name = "co_positions", minNumOfPositionalArgs = 1) @@ -333,13 +421,34 @@ Object positions(PCode self) { PTuple tuple; CodeUnit co = self.getCodeUnit(); if (co != null) { - SourceMap map = co.getSourceMap(); List lines = new ArrayList<>(); - if (map != null && map.startLineMap.length > 0) { - byte[] bytecode = co.code; - for (int i = 0; i < bytecode.length;) { - lines.add(factory.createTuple(new int[]{map.startLineMap[i], map.endLineMap[i], map.startColumnMap[i], map.endColumnMap[i]})); - i += OpCodes.fromOpCode(bytecode[i]).length(); + if (PythonOptions.ENABLE_BYTECODE_DSL_INTERPRETER) { + PBytecodeDSLRootNode rootNode = (PBytecodeDSLRootNode) self.getRootNodeForExtraction(); + for (Instruction instruction : rootNode.getBytecodeNode().getInstructions()) { + if (instruction.isInstrumentation()) { + // Skip instrumented instructions. The co_positions array should agree + // with the logical instruction index. + continue; + } + SourceSection section = rootNode.getSourceSectionForLocation(instruction.getLocation()); + lines.add(factory.createTuple(new int[]{ + section.getStartLine(), + section.getEndLine(), + // 1-based inclusive to 0-based inclusive + section.getStartColumn() - 1, + // 1-based inclusive to 0-based exclusive (-1 + 1 = 0) + section.getEndColumn() + })); + } + } else { + BytecodeCodeUnit bytecodeCo = (BytecodeCodeUnit) co; + SourceMap map = bytecodeCo.getSourceMap(); + if (map != null && map.startLineMap.length > 0) { + byte[] bytecode = bytecodeCo.code; + for (int i = 0; i < bytecode.length;) { + lines.add(factory.createTuple(new int[]{map.startLineMap[i], map.endLineMap[i], map.startColumnMap[i], map.endColumnMap[i]})); + i += OpCodes.fromOpCode(bytecode[i]).length(); + } } } tuple = factory.createTuple(lines.toArray()); diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/code/CodeNodes.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/code/CodeNodes.java index e216da0c4e..f04ccf460a 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/code/CodeNodes.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/code/CodeNodes.java @@ -50,15 +50,20 @@ import com.oracle.graal.python.builtins.objects.code.CodeNodesFactory.GetCodeRootNodeGen; import com.oracle.graal.python.builtins.objects.function.PFunction; import com.oracle.graal.python.builtins.objects.function.Signature; +import com.oracle.graal.python.compiler.BytecodeCodeUnit; import com.oracle.graal.python.compiler.CodeUnit; import com.oracle.graal.python.nodes.PNodeWithContext; import com.oracle.graal.python.nodes.PRootNode; import com.oracle.graal.python.nodes.bytecode.PBytecodeGeneratorFunctionRootNode; import com.oracle.graal.python.nodes.bytecode.PBytecodeRootNode; +import com.oracle.graal.python.nodes.bytecode_dsl.BytecodeDSLCodeUnit; +import com.oracle.graal.python.nodes.bytecode_dsl.PBytecodeDSLGeneratorFunctionRootNode; +import com.oracle.graal.python.nodes.bytecode_dsl.PBytecodeDSLRootNode; import com.oracle.graal.python.nodes.util.BadOPCodeNode; import com.oracle.graal.python.runtime.ExecutionContext.IndirectCallContext; import com.oracle.graal.python.runtime.IndirectCallData; import com.oracle.graal.python.runtime.PythonContext; +import com.oracle.graal.python.runtime.PythonOptions; import com.oracle.graal.python.runtime.object.PythonObjectFactory; import com.oracle.graal.python.util.PythonUtils; import com.oracle.graal.python.util.Supplier; @@ -143,7 +148,7 @@ private static PCode createCode(PythonLanguage language, PythonContext context, parameterNames, kwOnlyNames); } else { - ct = create().deserializeForBytecodeInterpreter(language, codedata, cellvars, freevars); + ct = create().deserializeForBytecodeInterpreter(language, context, codedata, cellvars, freevars); signature = ((PRootNode) ct.getRootNode()).getSignature(); } if (filename != null) { @@ -154,20 +159,31 @@ private static PCode createCode(PythonLanguage language, PythonContext context, } @SuppressWarnings("static-method") - private RootCallTarget deserializeForBytecodeInterpreter(PythonLanguage language, byte[] data, TruffleString[] cellvars, TruffleString[] freevars) { - CodeUnit code = MarshalModuleBuiltins.deserializeCodeUnit(data); - if (cellvars != null && !Arrays.equals(code.cellvars, cellvars) || freevars != null && !Arrays.equals(code.freevars, freevars)) { - code = new CodeUnit(code.name, code.qualname, code.argCount, code.kwOnlyArgCount, code.positionalOnlyArgCount, code.stacksize, code.code, - code.srcOffsetTable, code.flags, code.names, code.varnames, - cellvars != null ? cellvars : code.cellvars, freevars != null ? freevars : code.freevars, - code.cell2arg, code.constants, code.primitiveConstants, code.exceptionHandlerRanges, code.conditionProfileCount, - code.startLine, code.startColumn, code.endLine, code.endColumn, - code.outputCanQuicken, code.variableShouldUnbox, - code.generalizeInputsMap, code.generalizeVarsMap); - } - RootNode rootNode = PBytecodeRootNode.create(language, code, PythonUtils.createFakeSource()); - if (code.isGeneratorOrCoroutine()) { - rootNode = new PBytecodeGeneratorFunctionRootNode(language, rootNode.getFrameDescriptor(), (PBytecodeRootNode) rootNode, code.name); + private RootCallTarget deserializeForBytecodeInterpreter(PythonLanguage language, PythonContext context, byte[] data, TruffleString[] cellvars, TruffleString[] freevars) { + CodeUnit codeUnit = MarshalModuleBuiltins.deserializeCodeUnit(data); + RootNode rootNode = null; + + if (PythonOptions.ENABLE_BYTECODE_DSL_INTERPRETER) { + BytecodeDSLCodeUnit code = (BytecodeDSLCodeUnit) codeUnit; + rootNode = code.createRootNode(context, PythonUtils.createFakeSource()); + if (code.isGeneratorOrCoroutine()) { + rootNode = new PBytecodeDSLGeneratorFunctionRootNode(language, rootNode.getFrameDescriptor(), (PBytecodeDSLRootNode) rootNode, code.name); + } + } else { + BytecodeCodeUnit code = (BytecodeCodeUnit) codeUnit; + if (cellvars != null && !Arrays.equals(code.cellvars, cellvars) || freevars != null && !Arrays.equals(code.freevars, freevars)) { + code = new BytecodeCodeUnit(code.name, code.qualname, code.argCount, code.kwOnlyArgCount, code.positionalOnlyArgCount, code.flags, code.names, + code.varnames, cellvars != null ? cellvars : code.cellvars, freevars != null ? freevars : code.freevars, code.cell2arg, + code.constants, code.startLine, + code.startColumn, code.endLine, code.endColumn, code.code, code.srcOffsetTable, + code.primitiveConstants, code.exceptionHandlerRanges, code.stacksize, code.conditionProfileCount, + code.outputCanQuicken, code.variableShouldUnbox, + code.generalizeInputsMap, code.generalizeVarsMap); + } + rootNode = PBytecodeRootNode.create(language, code, PythonUtils.createFakeSource()); + if (code.isGeneratorOrCoroutine()) { + rootNode = new PBytecodeGeneratorFunctionRootNode(language, rootNode.getFrameDescriptor(), (PBytecodeRootNode) rootNode, code.name); + } } return PythonUtils.getOrCreateCallTarget(rootNode); } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/code/PCode.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/code/PCode.java index 01c3431132..d9bc902235 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/code/PCode.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/code/PCode.java @@ -59,15 +59,20 @@ import com.oracle.graal.python.builtins.objects.function.Signature; import com.oracle.graal.python.builtins.objects.object.PythonBuiltinObject; import com.oracle.graal.python.builtins.objects.tuple.PTuple; +import com.oracle.graal.python.compiler.BytecodeCodeUnit; import com.oracle.graal.python.compiler.CodeUnit; import com.oracle.graal.python.compiler.OpCodes; import com.oracle.graal.python.nodes.PRootNode; import com.oracle.graal.python.nodes.bytecode.PBytecodeGeneratorFunctionRootNode; import com.oracle.graal.python.nodes.bytecode.PBytecodeGeneratorRootNode; import com.oracle.graal.python.nodes.bytecode.PBytecodeRootNode; +import com.oracle.graal.python.nodes.bytecode_dsl.BytecodeDSLCodeUnit; +import com.oracle.graal.python.nodes.bytecode_dsl.PBytecodeDSLGeneratorFunctionRootNode; +import com.oracle.graal.python.nodes.bytecode_dsl.PBytecodeDSLRootNode; import com.oracle.graal.python.nodes.object.IsForeignObjectNode; import com.oracle.graal.python.runtime.GilNode; import com.oracle.graal.python.runtime.PythonContext; +import com.oracle.graal.python.runtime.PythonOptions; import com.oracle.graal.python.runtime.object.PythonObjectFactory; import com.oracle.graal.python.runtime.sequence.storage.BoolSequenceStorage; import com.oracle.graal.python.runtime.sequence.storage.DoubleSequenceStorage; @@ -79,6 +84,9 @@ 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.bytecode.BytecodeLocation; +import com.oracle.truffle.api.bytecode.BytecodeNode; +import com.oracle.truffle.api.bytecode.ContinuationRootNode; import com.oracle.truffle.api.RootCallTarget; import com.oracle.truffle.api.dsl.Cached; import com.oracle.truffle.api.dsl.Cached.Exclusive; @@ -169,12 +177,18 @@ public PCode(Object cls, Shape instanceShape, Supplier callTargetSup this.filename = filename; } - public PCode(Object cls, Shape instanceShape, RootCallTarget callTarget, Signature signature, CodeUnit codeUnit) { + public PCode(Object cls, Shape instanceShape, RootCallTarget callTarget, Signature signature, BytecodeCodeUnit codeUnit) { this(cls, instanceShape, callTarget, signature, codeUnit.varnames.length, -1, -1, null, null, null, null, null, null, codeUnit.name, codeUnit.qualname, -1, codeUnit.srcOffsetTable); } + public PCode(Object cls, Shape instanceShape, RootCallTarget callTarget, Signature signature, BytecodeDSLCodeUnit codeUnit) { + this(cls, instanceShape, callTarget, signature, codeUnit.varnames.length, -1, -1, null, null, + null, null, null, null, + codeUnit.name, codeUnit.qualname, -1, null); + } + public PCode(Object cls, Shape instanceShape, RootCallTarget callTarget, Signature signature, int nlocals, int stacksize, int flags, Object[] constants, TruffleString[] names, TruffleString[] varnames, TruffleString[] freevars, TruffleString[] cellvars, @@ -266,13 +280,14 @@ private static String getSourceSectionFileName(SourceSection src) { @TruffleBoundary private static int extractFirstLineno(RootNode rootNode) { RootNode funcRootNode = rootNodeForExtraction(rootNode); - if (funcRootNode instanceof PBytecodeRootNode) { - CodeUnit co = ((PBytecodeRootNode) funcRootNode).getCodeUnit(); + CodeUnit co = getCodeUnit(funcRootNode); + if (co != null) { if ((co.flags & CO_GRAALPYHON_MODULE) != 0) { return 1; } return co.startLine; } + SourceSection sourceSection = funcRootNode.getSourceSection(); if (sourceSection != null) { return sourceSection.getStartLine(); @@ -288,10 +303,15 @@ private static TruffleString extractName(RootNode rootNode) { @TruffleBoundary private static int extractStackSize(RootNode rootNode) { RootNode funcRootNode = rootNodeForExtraction(rootNode); - if (funcRootNode instanceof PBytecodeRootNode) { - CodeUnit code = ((PBytecodeRootNode) funcRootNode).getCodeUnit(); + if (funcRootNode instanceof PBytecodeRootNode bytecodeRootNode) { + BytecodeCodeUnit code = bytecodeRootNode.getCodeUnit(); return code.stacksize + code.varnames.length + code.cellvars.length + code.freevars.length; } + /** + * NB: This fallback case includes PBytecodeDSLRootNode. The Bytecode DSL stack does not + * mirror a CPython stack (it's an operand stack for its own instruction set), so the frame + * size is our best estimate. + */ return funcRootNode.getFrameDescriptor().getNumberOfSlots(); } @@ -307,8 +327,18 @@ private static TruffleString[] extractVarnames(RootNode node) { @TruffleBoundary private static Object[] extractConstants(RootNode node) { RootNode rootNode = rootNodeForExtraction(node); - if (rootNode instanceof PBytecodeRootNode) { - CodeUnit co = ((PBytecodeRootNode) rootNode).getCodeUnit(); + if (PythonOptions.ENABLE_BYTECODE_DSL_INTERPRETER) { + if (rootNode instanceof PBytecodeDSLRootNode bytecodeDSLRootNode) { + BytecodeDSLCodeUnit co = bytecodeDSLRootNode.getCodeUnit(); + List constants = new ArrayList<>(); + for (int i = 0; i < co.constants.length; i++) { + Object constant = convertConstantToPythonSpace(rootNode, co.constants[i]); + constants.add(constant); + } + return constants.toArray(new Object[0]); + } + } else if (rootNode instanceof PBytecodeRootNode bytecodeRootNode) { + BytecodeCodeUnit co = bytecodeRootNode.getCodeUnit(); Set bytecodeConstants = new HashSet<>(); for (int bci = 0; bci < co.code.length;) { OpCodes op = OpCodes.fromOpCode(co.code[bci]); @@ -355,11 +385,20 @@ private static TruffleString[] extractNames(RootNode node) { } private static RootNode rootNodeForExtraction(RootNode rootNode) { - if (rootNode instanceof PBytecodeGeneratorFunctionRootNode) { - return ((PBytecodeGeneratorFunctionRootNode) rootNode).getBytecodeRootNode(); - } - if (rootNode instanceof PBytecodeGeneratorRootNode) { - return ((PBytecodeGeneratorRootNode) rootNode).getBytecodeRootNode(); + if (PythonOptions.ENABLE_BYTECODE_DSL_INTERPRETER) { + if (rootNode instanceof PBytecodeDSLGeneratorFunctionRootNode generatorFunctionRootNode) { + return generatorFunctionRootNode.getBytecodeRootNode(); + } + if (rootNode instanceof ContinuationRootNode generatorRootNode) { + return (RootNode) generatorRootNode.getSourceRootNode(); + } + } else { + if (rootNode instanceof PBytecodeGeneratorFunctionRootNode generatorFunctionRootNode) { + return generatorFunctionRootNode.getBytecodeRootNode(); + } + if (rootNode instanceof PBytecodeGeneratorRootNode generatorRootNode) { + return generatorRootNode.getBytecodeRootNode(); + } } return rootNode; } @@ -376,8 +415,12 @@ private static int extractFlags(RootNode node) { private static CodeUnit getCodeUnit(RootNode node) { RootNode rootNode = rootNodeForExtraction(node); - if (rootNode instanceof PBytecodeRootNode) { - return ((PBytecodeRootNode) rootNode).getCodeUnit(); + if (PythonOptions.ENABLE_BYTECODE_DSL_INTERPRETER) { + if (rootNode instanceof PBytecodeDSLRootNode bytecodeDSLRootNode) { + return bytecodeDSLRootNode.getCodeUnit(); + } + } else if (rootNode instanceof PBytecodeRootNode bytecodeRootNode) { + return bytecodeRootNode.getCodeUnit(); } return null; } @@ -386,6 +429,10 @@ RootNode getRootNode() { return getRootCallTarget().getRootNode(); } + RootNode getRootNodeForExtraction() { + return rootNodeForExtraction(getRootNode()); + } + public TruffleString[] getFreeVars() { if (freevars == null) { freevars = extractFreeVars(getRootNode()); @@ -433,23 +480,19 @@ public int getFirstLineNo() { } @TruffleBoundary - public int bciToLine(int bci) { + public int lastiToLine(int lasti) { RootNode funcRootNode = rootNodeForExtraction(getRootNode()); - if (funcRootNode instanceof PBytecodeRootNode bytecodeRootNode) { - return bytecodeRootNode.bciToLine(bci); + if (PythonOptions.ENABLE_BYTECODE_DSL_INTERPRETER) { + if (funcRootNode instanceof PBytecodeDSLRootNode bytecodeDSLRootNode) { + BytecodeNode bytecodeNode = bytecodeDSLRootNode.getBytecodeNode(); + return bytecodeDSLRootNode.bciToLine(PBytecodeDSLRootNode.lastiToBci(lasti, bytecodeNode), bytecodeNode); + } + } else if (funcRootNode instanceof PBytecodeRootNode bytecodeRootNode) { + return bytecodeRootNode.bciToLine(bytecodeRootNode.lastiToBci(lasti)); } return -1; } - @TruffleBoundary - public int lastiToBci(int lasti) { - RootNode funcRootNode = rootNodeForExtraction(getRootNode()); - if (funcRootNode instanceof PBytecodeRootNode bytecodeRootNode) { - return bytecodeRootNode.lastiToBci(lasti); - } - return lasti; - } - public TruffleString getName() { if (name == null) { name = extractName(getRootNode()); @@ -529,9 +572,15 @@ public Object[] getConstants() { private static Object convertConstantToPythonSpace(RootNode rootNode, Object o) { PythonObjectFactory factory = PythonObjectFactory.getUncached(); if (o instanceof CodeUnit) { - CodeUnit code = ((CodeUnit) o); - PBytecodeRootNode bytecodeRootNode = PBytecodeRootNode.create(PythonLanguage.get(rootNode), code, getSourceSection(rootNode).getSource()); - return factory.createCode(bytecodeRootNode.getCallTarget(), bytecodeRootNode.getSignature(), code); + if (PythonOptions.ENABLE_BYTECODE_DSL_INTERPRETER) { + BytecodeDSLCodeUnit code = (BytecodeDSLCodeUnit) o; + PBytecodeDSLRootNode root = code.createRootNode(PythonContext.get(rootNode), getSourceSection(rootNode).getSource()); + return factory.createCode(root.getCallTarget(), root.getSignature(), code); + } else { + BytecodeCodeUnit code = (BytecodeCodeUnit) o; + PBytecodeRootNode root = PBytecodeRootNode.create(PythonLanguage.get(rootNode), code, getSourceSection(rootNode).getSource()); + return factory.createCode(root.getCallTarget(), root.getSignature(), code); + } } else if (o instanceof BigInteger) { return factory.createInt((BigInteger) o); } else if (o instanceof int[]) { @@ -694,15 +743,20 @@ public String toString() { @TruffleBoundary public String toDisassembledString(boolean quickened) { RootNode rootNode = getRootCallTarget().getRootNode(); + if (PythonOptions.ENABLE_BYTECODE_DSL_INTERPRETER) { + return "dis not implemented for the Bytecode DSL interpreter"; + } + if (rootNode instanceof PBytecodeGeneratorRootNode r) { rootNode = r.getBytecodeRootNode(); } else if (rootNode instanceof PBytecodeGeneratorFunctionRootNode r) { rootNode = r.getBytecodeRootNode(); } - if (rootNode instanceof PBytecodeRootNode) { - CodeUnit code = ((PBytecodeRootNode) rootNode).getCodeUnit(); + + if (rootNode instanceof PBytecodeRootNode r) { + BytecodeCodeUnit code = r.getCodeUnit(); if (quickened) { - return code.toString(((PBytecodeRootNode) rootNode).getBytecode()); + return code.toString(r.getBytecode()); } return code.toString(); } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/frame/PFrame.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/frame/PFrame.java index 9dcfce8caf..633ea5a027 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/frame/PFrame.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/frame/PFrame.java @@ -50,22 +50,28 @@ import com.oracle.graal.python.builtins.objects.object.PythonObject; import com.oracle.graal.python.nodes.bytecode.PBytecodeGeneratorRootNode; import com.oracle.graal.python.nodes.bytecode.PBytecodeRootNode; +import com.oracle.graal.python.nodes.bytecode_dsl.PBytecodeDSLRootNode; import com.oracle.graal.python.nodes.frame.GetFrameLocalsNode; import com.oracle.graal.python.nodes.frame.MaterializeFrameNode; +import com.oracle.graal.python.runtime.PythonOptions; import com.oracle.graal.python.util.PythonUtils; import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; import com.oracle.truffle.api.RootCallTarget; +import com.oracle.truffle.api.bytecode.BytecodeNode; +import com.oracle.truffle.api.bytecode.Instruction; import com.oracle.truffle.api.frame.MaterializedFrame; import com.oracle.truffle.api.nodes.Node; public final class PFrame extends PythonBuiltinObject { + private static final int UNINITIALIZED_LINE = -2; + private Object[] arguments; private final MaterializedFrame locals; private Object localsDict; private final Reference virtualFrameInfo; private Node location; private RootCallTarget callTarget; - private int line = -2; + private int line = UNINITIALIZED_LINE; private int bci = -1; /* @@ -187,7 +193,7 @@ public PFrame(PythonLanguage lang, @SuppressWarnings("unused") Object threadStat this.virtualFrameInfo = curFrameInfo; curFrameInfo.setPyFrame(this); this.location = GetCodeRootNode.executeUncached(code); - this.line = this.location == null ? code.getFirstLineNo() : -2; + this.line = this.location == null ? code.getFirstLineNo() : UNINITIALIZED_LINE; this.arguments = frameArgs; this.locals = null; this.localsDict = localsDict; @@ -251,11 +257,16 @@ public boolean didJump() { @TruffleBoundary public int getLine() { - if (line == -2) { + if (line == UNINITIALIZED_LINE) { if (location == null) { line = -1; - } else if (location instanceof PBytecodeRootNode) { - return ((PBytecodeRootNode) location).bciToLine(bci); + } else if (PythonOptions.ENABLE_BYTECODE_DSL_INTERPRETER) { + if (location instanceof BytecodeNode bytecodeNode) { + PBytecodeDSLRootNode rootNode = (PBytecodeDSLRootNode) bytecodeNode.getRootNode(); + return rootNode.bciToLine(bci, bytecodeNode); + } + } else if (location instanceof PBytecodeRootNode bytecodeRootNode) { + return bytecodeRootNode.bciToLine(bci); } } return line; @@ -301,6 +312,10 @@ public Node getLocation() { return location; } + public BytecodeNode getBytecodeNode() { + return (location instanceof BytecodeNode bytecodeNode) ? bytecodeNode : null; + } + public int getBci() { return bci; } @@ -310,15 +325,21 @@ public void setBci(int bci) { } public int getLasti() { - return bciToLasti(bci); + return bciToLasti(bci, location); } @TruffleBoundary - public int bciToLasti(int bci) { - if (location instanceof PBytecodeRootNode bytecodeRootNode) { - return bytecodeRootNode.bciToLasti(bci); - } else if (location instanceof PBytecodeGeneratorRootNode generatorRootNode) { - return generatorRootNode.getBytecodeRootNode().bciToLasti(bci); + public static int bciToLasti(int bci, Node location) { + if (PythonOptions.ENABLE_BYTECODE_DSL_INTERPRETER) { + if (bci >= 0 && location instanceof BytecodeNode bytecodeNode) { + return PBytecodeDSLRootNode.bciToLasti(bci, bytecodeNode); + } + } else { + if (location instanceof PBytecodeRootNode bytecodeRootNode) { + return bytecodeRootNode.bciToLasti(bci); + } else if (location instanceof PBytecodeGeneratorRootNode generatorRootNode) { + return generatorRootNode.getBytecodeRootNode().bciToLasti(bci); + } } return -1; } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/function/PArguments.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/function/PArguments.java index 25baa2d572..b35996193d 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/function/PArguments.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/function/PArguments.java @@ -28,6 +28,8 @@ import com.oracle.graal.python.builtins.objects.cell.PCell; import com.oracle.graal.python.builtins.objects.frame.PFrame; import com.oracle.graal.python.builtins.objects.object.PythonObject; +import com.oracle.graal.python.runtime.PythonOptions; +import com.oracle.graal.python.runtime.exception.PException; import com.oracle.graal.python.util.PythonUtils; import com.oracle.truffle.api.CompilerAsserts; import com.oracle.truffle.api.exception.AbstractTruffleException; @@ -217,6 +219,11 @@ public static Object getExceptionUnchecked(Object[] arguments) { return arguments[INDEX_CURRENT_EXCEPTION]; } + public static boolean hasException(Object[] arguments) { + Object exception = getExceptionUnchecked(arguments); + return exception != null && exception != PException.NO_EXCEPTION; + } + public static void setException(Frame frame, AbstractTruffleException exc) { setException(frame.getArguments(), exc); } @@ -280,6 +287,7 @@ public static int getUserArgumentLength(Object[] arguments) { } public static MaterializedFrame getGeneratorFrame(Object[] arguments) { + assert !PythonOptions.ENABLE_BYTECODE_DSL_INTERPRETER; return (MaterializedFrame) arguments[INDEX_GENERATOR_FRAME]; } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/generator/CommonGeneratorBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/generator/CommonGeneratorBuiltins.java index 0a204506da..f58fcb3fdc 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/generator/CommonGeneratorBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/generator/CommonGeneratorBuiltins.java @@ -81,6 +81,8 @@ import com.oracle.graal.python.util.PythonUtils; import com.oracle.truffle.api.CallTarget; import com.oracle.truffle.api.RootCallTarget; +import com.oracle.truffle.api.Truffle; +import com.oracle.truffle.api.bytecode.ContinuationResult; import com.oracle.truffle.api.dsl.Bind; import com.oracle.truffle.api.dsl.Cached; import com.oracle.truffle.api.dsl.Cached.Exclusive; @@ -105,7 +107,7 @@ public final class CommonGeneratorBuiltins extends PythonBuiltins { * is invoked using {@code next(g)} outside of any {@code except} handler but the generator * requests the exception state, then the exception state will be written into the arguments. If * we now use the same arguments array every time, the next invocation would think that there is - * not excepion but in fact, the a subsequent call ot {@code next} may have a different + * not an exception but in fact, a subsequent call to {@code next} may have a different * exception state. * *
@@ -156,7 +158,7 @@ private static void checkResumable(Node inliningTarget, PGenerator self, PRaiseN
     abstract static class ResumeGeneratorNode extends Node {
         public abstract Object execute(VirtualFrame frame, Node inliningTarget, PGenerator self, Object sendValue);
 
-        @Specialization(guards = "sameCallTarget(self.getCurrentCallTarget(), call.getCallTarget())", limit = "getCallSiteInlineCacheMaxDepth()")
+        @Specialization(guards = {"!isBytecodeDSLInterpreter()", "sameCallTarget(self.getCurrentCallTarget(), call.getCallTarget())"}, limit = "getCallSiteInlineCacheMaxDepth()")
         static Object cached(VirtualFrame frame, Node inliningTarget, PGenerator self, Object sendValue,
                         @Cached(value = "createDirectCall(self.getCurrentCallTarget())", inline = false) CallTargetInvokeNode call,
                         @Exclusive @Cached InlinedBranchProfile returnProfile,
@@ -174,14 +176,49 @@ static Object cached(VirtualFrame frame, Node inliningTarget, PGenerator self, O
                 throw handleException(self, inliningTarget, errorProfile, raiseNode, e);
             } catch (GeneratorReturnException e) {
                 returnProfile.enter(inliningTarget);
-                throw handleReturn(self, e, raiseNode.get(inliningTarget));
+                throw handleReturn(self, e.value, raiseNode.get(inliningTarget));
             } finally {
                 self.setRunning(false);
             }
             return handleResult(inliningTarget, self, result);
         }
 
-        @Specialization(replaces = "cached")
+        @Specialization(guards = {"isBytecodeDSLInterpreter()", "sameCallTarget(currentCallTarget, call.getCallTarget())"}, limit = "getCallSiteInlineCacheMaxDepth()")
+        static Object cachedBytecodeDSL(VirtualFrame frame, Node inliningTarget, PGenerator self, Object sendValue,
+                        @Bind("self.getCurrentCallTarget()") @SuppressWarnings("unused") RootCallTarget currentCallTarget,
+                        @Cached(value = "createDirectCall(currentCallTarget)", inline = false) CallTargetInvokeNode call,
+                        @Cached("self.getContinuation() == null") boolean firstCall,
+                        @Exclusive @Cached InlinedBranchProfile returnProfile,
+                        @Exclusive @Cached IsBuiltinObjectProfile errorProfile,
+                        @Exclusive @Cached PRaiseNode.Lazy raiseNode) {
+            self.setRunning(true);
+            Object generatorResult;
+            try {
+                ContinuationResult continuation = self.getContinuation();
+                Object[] arguments;
+                if (firstCall) {
+                    // First invocation: call the regular root node.
+                    arguments = prepareArguments(self);
+                } else {
+                    // Subsequent invocations: call a continuation root node.
+                    arguments = new Object[]{continuation.getFrame(), sendValue};
+                }
+                generatorResult = call.execute(frame, null, null, null, arguments);
+            } catch (PException e) {
+                throw handleException(self, inliningTarget, errorProfile, raiseNode, e);
+            } finally {
+                self.setRunning(false);
+            }
+            if (generatorResult instanceof ContinuationResult continuation) {
+                return handleResult(inliningTarget, self, continuation);
+            } else {
+                returnProfile.enter(inliningTarget);
+                throw handleReturn(self, generatorResult, raiseNode.get(inliningTarget));
+            }
+
+        }
+
+        @Specialization(replaces = "cached", guards = "!isBytecodeDSLInterpreter()")
         @Megamorphic
         static Object generic(VirtualFrame frame, Node inliningTarget, PGenerator self, Object sendValue,
                         @Cached InlinedConditionProfile hasFrameProfile,
@@ -205,13 +242,53 @@ static Object generic(VirtualFrame frame, Node inliningTarget, PGenerator self,
                 throw handleException(self, inliningTarget, errorProfile, raiseNode, e);
             } catch (GeneratorReturnException e) {
                 returnProfile.enter(inliningTarget);
-                throw handleReturn(self, e, raiseNode.get(inliningTarget));
+                throw handleReturn(self, e.value, raiseNode.get(inliningTarget));
             } finally {
                 self.setRunning(false);
             }
             return handleResult(inliningTarget, self, result);
         }
 
+        @Specialization(replaces = "cachedBytecodeDSL", guards = "isBytecodeDSLInterpreter()")
+        @Megamorphic
+        static Object genericBytecodeDSL(VirtualFrame frame, Node inliningTarget, PGenerator self, Object sendValue,
+                        @Cached InlinedConditionProfile hasFrameProfile,
+                        @Cached(inline = false) GenericInvokeNode call,
+                        @Cached InlinedConditionProfile firstInvocationProfile,
+                        @Cached InlinedBranchProfile returnProfile,
+                        @Cached IsBuiltinObjectProfile errorProfile,
+                        @Cached PRaiseNode.Lazy raiseNode) {
+            self.setRunning(true);
+            Object generatorResult;
+            try {
+                ContinuationResult continuation = self.getContinuation();
+                Object[] arguments;
+                if (firstInvocationProfile.profile(inliningTarget, continuation == null)) {
+                    // First invocation: call the regular root node.
+                    arguments = prepareArguments(self);
+                } else {
+                    // Subsequent invocations: call a continuation root node.
+                    arguments = new Object[]{continuation.getFrame(), sendValue};
+                }
+
+                if (hasFrameProfile.profile(inliningTarget, frame != null)) {
+                    generatorResult = call.execute(frame, self.getCurrentCallTarget(), arguments);
+                } else {
+                    generatorResult = call.execute(self.getCurrentCallTarget(), arguments);
+                }
+            } catch (PException e) {
+                throw handleException(self, inliningTarget, errorProfile, raiseNode, e);
+            } finally {
+                self.setRunning(false);
+            }
+            if (generatorResult instanceof ContinuationResult continuation) {
+                return handleResult(inliningTarget, self, continuation);
+            } else {
+                returnProfile.enter(inliningTarget);
+                throw handleReturn(self, generatorResult, raiseNode.get(inliningTarget));
+            }
+        }
+
         private static PException handleException(PGenerator self, Node inliningTarget, IsBuiltinObjectProfile profile, PRaiseNode.Lazy raiseNode, PException e) {
             self.markAsFinished();
             if (self.isAsyncGen()) {
@@ -226,18 +303,17 @@ private static PException handleException(PGenerator self, Node inliningTarget,
             throw raiseNode.get(inliningTarget).raiseWithCause(RuntimeError, e.getEscapedException(), ErrorMessages.GENERATOR_RAISED_STOPITER);
         }
 
-        private static Object handleResult(Node node, PGenerator self, GeneratorYieldResult result) {
-            self.handleResult(PythonLanguage.get(node), result);
-            return result.yieldValue;
+        private static Object handleResult(Node node, PGenerator self, Object result) {
+            return self.handleResult(PythonLanguage.get(node), result);
         }
 
-        private static PException handleReturn(PGenerator self, GeneratorReturnException e, PRaiseNode raiseNode) {
+        private static PException handleReturn(PGenerator self, Object returnValue, PRaiseNode raiseNode) {
             self.markAsFinished();
             if (self.isAsyncGen()) {
                 throw raiseNode.raise(StopAsyncIteration);
             }
-            if (e.value != PNone.NONE) {
-                throw raiseNode.raise(StopIteration, new Object[]{e.value});
+            if (returnValue != PNone.NONE) {
+                throw raiseNode.raise(StopIteration, new Object[]{returnValue});
             } else {
                 throw raiseNode.raise(StopIteration);
             }
@@ -320,15 +396,20 @@ static Object sendThrow(VirtualFrame frame, PGenerator self, Object typ, Object
                 // its frame to the traceback manually.
                 self.markAsFinished();
                 Node location = self.getCurrentCallTarget().getRootNode();
-                MaterializedFrame generatorFrame = PArguments.getGeneratorFrame(self.getArguments());
+                MaterializedFrame generatorFrame;
+                if (PythonOptions.ENABLE_BYTECODE_DSL_INTERPRETER) {
+                    generatorFrame = Truffle.getRuntime().createMaterializedFrame(PArguments.create(), self.getRootNode().getFrameDescriptor());
+                } else {
+                    generatorFrame = PArguments.getGeneratorFrame(self.getArguments());
+                }
                 PFrame pFrame = MaterializeFrameNode.materializeGeneratorFrame(location, generatorFrame, PFrame.Reference.EMPTY, factory.get(inliningTarget));
                 FrameInfo info = (FrameInfo) generatorFrame.getFrameDescriptor().getInfo();
-                pFrame.setLine(info.getRootNode().getFirstLineno());
+                pFrame.setLine(info.getFirstLineNumber());
                 Object existingTracebackObj = getTracebackNode.execute(inliningTarget, instance);
                 PTraceback newTraceback = factory.get(inliningTarget).createTraceback(pFrame, pFrame.getLine(),
                                 (existingTracebackObj instanceof PTraceback existingTraceback) ? existingTraceback : null);
                 setTracebackNode.execute(inliningTarget, instance, newTraceback);
-                throw PException.fromObject(instance, location, PythonOptions.isPExceptionWithJavaStacktrace(language));
+                throw PException.fromObject(instance, inliningTarget, PythonOptions.isPExceptionWithJavaStacktrace(language));
             }
         }
     }
diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/generator/GeneratorBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/generator/GeneratorBuiltins.java
index 2aecfbec5d..1789295fe3 100644
--- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/generator/GeneratorBuiltins.java
+++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/generator/GeneratorBuiltins.java
@@ -50,12 +50,16 @@
 import com.oracle.graal.python.builtins.objects.str.StringUtils.SimpleTruffleStringFormatNode;
 import com.oracle.graal.python.nodes.ErrorMessages;
 import com.oracle.graal.python.nodes.PRaiseNode;
-import com.oracle.graal.python.nodes.bytecode.FrameInfo;
+import com.oracle.graal.python.nodes.bytecode.BytecodeFrameInfo;
+import com.oracle.graal.python.nodes.bytecode_dsl.BytecodeDSLFrameInfo;
 import com.oracle.graal.python.nodes.frame.MaterializeFrameNode;
 import com.oracle.graal.python.nodes.function.PythonBuiltinBaseNode;
 import com.oracle.graal.python.nodes.function.builtins.PythonBinaryBuiltinNode;
 import com.oracle.graal.python.nodes.function.builtins.PythonUnaryBuiltinNode;
+import com.oracle.graal.python.runtime.PythonOptions;
 import com.oracle.graal.python.runtime.object.PythonObjectFactory;
+import com.oracle.truffle.api.bytecode.BytecodeLocation;
+import com.oracle.truffle.api.bytecode.ContinuationResult;
 import com.oracle.truffle.api.dsl.Bind;
 import com.oracle.truffle.api.dsl.Cached;
 import com.oracle.truffle.api.dsl.GenerateNodeFactory;
@@ -187,14 +191,25 @@ static Object getFrame(PGenerator self,
             if (self.isFinished()) {
                 return PNone.NONE;
             } else {
-                MaterializedFrame generatorFrame = PArguments.getGeneratorFrame(self.getArguments());
-                Node location = ((FrameInfo) generatorFrame.getFrameDescriptor().getInfo()).getRootNode();
-                PFrame frame = MaterializeFrameNode.materializeGeneratorFrame(location, generatorFrame, PFrame.Reference.EMPTY, factory);
-                FrameInfo info = (FrameInfo) generatorFrame.getFrameDescriptor().getInfo();
-                int bci = self.getBci();
-                frame.setBci(bci);
-                frame.setLine(info.getRootNode().bciToLine(bci));
-                return frame;
+                if (PythonOptions.ENABLE_BYTECODE_DSL_INTERPRETER) {
+                    ContinuationResult continuation = self.getContinuation();
+                    BytecodeLocation location = continuation.getBytecodeLocation();
+                    MaterializedFrame generatorFrame = continuation.getFrame();
+                    BytecodeDSLFrameInfo info = (BytecodeDSLFrameInfo) generatorFrame.getFrameDescriptor().getInfo();
+                    PFrame frame = MaterializeFrameNode.materializeGeneratorFrame(location.getBytecodeNode(), generatorFrame, PFrame.Reference.EMPTY, factory);
+                    int bci = location.getBytecodeIndex();
+                    frame.setBci(bci);
+                    frame.setLine(info.getRootNode().bciToLine(bci, location.getBytecodeNode()));
+                    return frame;
+                } else {
+                    MaterializedFrame generatorFrame = PArguments.getGeneratorFrame(self.getArguments());
+                    BytecodeFrameInfo info = (BytecodeFrameInfo) generatorFrame.getFrameDescriptor().getInfo();
+                    PFrame frame = MaterializeFrameNode.materializeGeneratorFrame(info.getRootNode(), generatorFrame, PFrame.Reference.EMPTY, factory);
+                    int bci = self.getBci();
+                    frame.setBci(bci);
+                    frame.setLine(info.getRootNode().bciToLine(bci));
+                    return frame;
+                }
             }
         }
     }
diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/generator/PGenerator.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/generator/PGenerator.java
index 2325e7cfc9..511378dbdf 100644
--- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/generator/PGenerator.java
+++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/generator/PGenerator.java
@@ -31,15 +31,21 @@
 import com.oracle.graal.python.builtins.objects.code.PCode;
 import com.oracle.graal.python.builtins.objects.function.PArguments;
 import com.oracle.graal.python.builtins.objects.object.PythonBuiltinObject;
+import com.oracle.graal.python.nodes.bytecode.BytecodeFrameInfo;
 import com.oracle.graal.python.nodes.bytecode.FrameInfo;
 import com.oracle.graal.python.nodes.bytecode.GeneratorYieldResult;
 import com.oracle.graal.python.nodes.bytecode.PBytecodeGeneratorRootNode;
 import com.oracle.graal.python.nodes.bytecode.PBytecodeRootNode;
+import com.oracle.graal.python.nodes.bytecode_dsl.BytecodeDSLFrameInfo;
+import com.oracle.graal.python.nodes.bytecode_dsl.PBytecodeDSLRootNode;
+import com.oracle.graal.python.runtime.PythonOptions;
 import com.oracle.graal.python.runtime.object.PythonObjectFactory;
 import com.oracle.truffle.api.CompilerDirectives;
 import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
+import com.oracle.truffle.api.bytecode.ContinuationResult;
 import com.oracle.truffle.api.RootCallTarget;
 import com.oracle.truffle.api.nodes.Node;
+import com.oracle.truffle.api.nodes.RootNode;
 import com.oracle.truffle.api.profiles.InlinedConditionProfile;
 import com.oracle.truffle.api.strings.TruffleString;
 
@@ -47,32 +53,94 @@ public class PGenerator extends PythonBuiltinObject {
 
     private TruffleString name;
     private TruffleString qualname;
-    /**
-     * Call targets with copies of the generator's AST. Each call target corresponds to one possible
-     * entry point into the generator: the first call, and continuation for each yield. Each AST can
-     * then specialize towards which nodes are executed when starting from that particular entry
-     * point. When yielding, the next index to the next call target to continue from is updated via
-     * {@link #handleResult}.
-     */
-    @CompilationFinal(dimensions = 1) protected final RootCallTarget[] callTargets;
-    protected final Object[] arguments;
-    private boolean finished;
-    private PCode code;
-    private int currentCallTarget;
-    private final PBytecodeRootNode bytecodeRootNode;
     private final FrameInfo frameInfo;
+
+    private boolean finished;
     // running means it is currently on the stack, not just started
     private boolean running;
     private final boolean isCoroutine;
     private final boolean isAsyncGen;
 
+    private PCode code;
+    protected final Object[] arguments;
+
+    // TODO (GR-38700): remove BytecodeState after migrated to the Bytecode DSL interpreter.
+    protected static class BytecodeState {
+        private final PBytecodeRootNode rootNode;
+
+        /**
+         * Call targets with copies of the generator's AST. Each call target corresponds to one
+         * possible entry point into the generator: the first call, and continuation for each yield.
+         * Each AST can then specialize towards which nodes are executed when starting from that
+         * particular entry point. When yielding, the next index to the next call target to continue
+         * from is updated via {@link #handleResult}.
+         */
+        @CompilationFinal(dimensions = 1) private final RootCallTarget[] callTargets;
+        private int currentCallTarget;
+
+        public BytecodeState(PBytecodeRootNode rootNode, RootCallTarget[] callTargets) {
+            this.rootNode = rootNode;
+            this.callTargets = callTargets;
+            this.currentCallTarget = 0;
+        }
+
+        public RootCallTarget getCurrentCallTarget() {
+            return callTargets[currentCallTarget];
+        }
+
+        public PBytecodeGeneratorRootNode getCurrentRootNode() {
+            return (PBytecodeGeneratorRootNode) getCurrentCallTarget().getRootNode();
+        }
+
+        public Object handleResult(PythonLanguage language, GeneratorYieldResult result) {
+            currentCallTarget = result.resumeBci;
+            if (callTargets[currentCallTarget] == null) {
+                CompilerDirectives.transferToInterpreterAndInvalidate();
+                PBytecodeGeneratorRootNode generatorRootNode = new PBytecodeGeneratorRootNode(language, rootNode, result.resumeBci, result.resumeStackTop);
+                callTargets[currentCallTarget] = generatorRootNode.getCallTarget();
+            }
+            return result.yieldValue;
+        }
+    }
+
+    private static class BytecodeDSLState {
+        private final PBytecodeDSLRootNode rootNode;
+        private ContinuationResult yieldResult;
+
+        public BytecodeDSLState(PBytecodeDSLRootNode rootNode) {
+            this.rootNode = rootNode;
+            this.yieldResult = null;
+        }
+
+        public Object handleResult(ContinuationResult result) {
+            yieldResult = result;
+            return result.getResult();
+        }
+    }
+
+    // This is either BytecodeState or BytecodeDSLState.
+    private final Object state;
+
+    private BytecodeState getBytecodeState() {
+        return (BytecodeState) state;
+    }
+
+    private BytecodeDSLState getBytecodeDSLState() {
+        return (BytecodeDSLState) state;
+    }
+
     // An explicit isIterableCoroutine argument is needed for iterable coroutines (generally created
     // via types.coroutine)
     public static PGenerator create(PythonLanguage lang, TruffleString name, TruffleString qualname, PBytecodeRootNode rootNode, RootCallTarget[] callTargets, Object[] arguments,
                     PythonBuiltinClassType cls, boolean isIterableCoroutine) {
         // note: also done in PAsyncGen.create
         rootNode.createGeneratorFrame(arguments);
-        return new PGenerator(lang, name, qualname, rootNode, callTargets, arguments, cls, isIterableCoroutine);
+        return new PGenerator(lang, name, qualname, arguments, cls, isIterableCoroutine, new BytecodeState(rootNode, callTargets));
+    }
+
+    public static PGenerator create(PythonLanguage lang, TruffleString name, TruffleString qualname, PBytecodeDSLRootNode rootNode, Object[] arguments,
+                    PythonBuiltinClassType cls, boolean isIterableCoroutine) {
+        return new PGenerator(lang, name, qualname, arguments, cls, isIterableCoroutine, new BytecodeDSLState(rootNode));
     }
 
     public static PGenerator create(PythonLanguage lang, TruffleString name, TruffleString qualname, PBytecodeRootNode rootNode, RootCallTarget[] callTargets, Object[] arguments,
@@ -80,61 +148,79 @@ public static PGenerator create(PythonLanguage lang, TruffleString name, Truffle
         return create(lang, name, qualname, rootNode, callTargets, arguments, cls, false);
     }
 
-    protected PGenerator(PythonLanguage lang, TruffleString name, TruffleString qualname, PBytecodeRootNode rootNode, RootCallTarget[] callTargets, Object[] arguments, PythonBuiltinClassType cls,
-                    boolean isIterableCoroutine) {
+    public static PGenerator create(PythonLanguage lang, TruffleString name, TruffleString qualname, PBytecodeDSLRootNode rootNode, Object[] arguments,
+                    PythonBuiltinClassType cls) {
+        return create(lang, name, qualname, rootNode, arguments, cls, false);
+    }
+
+    protected PGenerator(PythonLanguage lang, TruffleString name, TruffleString qualname, Object[] arguments, PythonBuiltinClassType cls, boolean isIterableCoroutine, Object state) {
         super(cls, cls.getInstanceShape(lang));
         this.name = name;
         this.qualname = qualname;
-        this.callTargets = callTargets;
-        this.currentCallTarget = 0;
         this.arguments = arguments;
         this.finished = false;
-        this.bytecodeRootNode = rootNode;
-        this.frameInfo = (FrameInfo) rootNode.getFrameDescriptor().getInfo();
         this.isCoroutine = isIterableCoroutine || cls == PythonBuiltinClassType.PCoroutine;
         this.isAsyncGen = cls == PythonBuiltinClassType.PAsyncGenerator;
+        if (PythonOptions.ENABLE_BYTECODE_DSL_INTERPRETER) {
+            BytecodeDSLState bytecodeDSLState = (BytecodeDSLState) state;
+            this.state = state;
+            this.frameInfo = (BytecodeDSLFrameInfo) bytecodeDSLState.rootNode.getFrameDescriptor().getInfo();
+        } else {
+            BytecodeState bytecodeState = (BytecodeState) state;
+            this.state = state;
+            this.frameInfo = (BytecodeFrameInfo) bytecodeState.rootNode.getFrameDescriptor().getInfo();
+        }
     }
 
-    public final void handleResult(PythonLanguage language, GeneratorYieldResult result) {
-        currentCallTarget = result.resumeBci;
-        if (callTargets[currentCallTarget] == null) {
-            CompilerDirectives.transferToInterpreterAndInvalidate();
-            PBytecodeGeneratorRootNode rootNode = new PBytecodeGeneratorRootNode(language, bytecodeRootNode, result.resumeBci, result.resumeStackTop);
-            callTargets[currentCallTarget] = rootNode.getCallTarget();
+    public RootNode getRootNode() {
+        if (PythonOptions.ENABLE_BYTECODE_DSL_INTERPRETER) {
+            return getBytecodeDSLState().rootNode;
+        } else {
+            return getBytecodeState().rootNode;
         }
     }
 
     /**
-     * Returns the call target that should be used the next time the generator is called. Each time
-     * a generator call target returns through a yield, the generator should be updated with the
-     * next yield index to use via {@link #handleResult}
+     * Returns the call target that should be used the next time the generator is called.
      */
-    public final RootCallTarget getCurrentCallTarget() {
-        return callTargets[currentCallTarget];
+    public RootCallTarget getCurrentCallTarget() {
+        if (PythonOptions.ENABLE_BYTECODE_DSL_INTERPRETER) {
+            BytecodeDSLState bytecodeDSLState = getBytecodeDSLState();
+            if (bytecodeDSLState.yieldResult == null) {
+                return bytecodeDSLState.rootNode.getCallTarget();
+            }
+            return bytecodeDSLState.yieldResult.getContinuationCallTarget();
+        } else {
+            return getBytecodeState().getCurrentCallTarget();
+        }
     }
 
-    public final Object getYieldFrom() {
-        if (running || finished) {
+    public Object getYieldFrom() {
+        if (isRunning() || isFinished()) {
             return null;
         }
-        return frameInfo.getYieldFrom(PArguments.getGeneratorFrame(arguments), getBci(), getCurrentRootNode().getResumeStackTop());
-    }
 
-    private PBytecodeGeneratorRootNode getCurrentRootNode() {
-        return (PBytecodeGeneratorRootNode) getCurrentCallTarget().getRootNode();
+        if (PythonOptions.ENABLE_BYTECODE_DSL_INTERPRETER) {
+            throw new UnsupportedOperationException("not implemented"); // TODO: implement
+        } else {
+            return frameInfo.getYieldFrom(PArguments.getGeneratorFrame(arguments), getBci(), getBytecodeState().getCurrentRootNode().getResumeStackTop());
+        }
+
     }
 
-    public final boolean isStarted() {
-        return currentCallTarget != 0 && !running;
+    public boolean isStarted() {
+        if (PythonOptions.ENABLE_BYTECODE_DSL_INTERPRETER) {
+            return getBytecodeDSLState().yieldResult != null && !isRunning();
+        } else {
+            return getBytecodeState().currentCallTarget != 0 && !isRunning();
+        }
     }
 
-    public final int getBci() {
-        if (!isStarted()) {
-            return -1;
-        } else if (finished) {
-            return bytecodeRootNode.getCodeUnit().code.length;
+    public Object handleResult(PythonLanguage language, Object result) {
+        if (PythonOptions.ENABLE_BYTECODE_DSL_INTERPRETER) {
+            return getBytecodeDSLState().handleResult((ContinuationResult) result);
         } else {
-            return getCurrentRootNode().getResumeBci();
+            return getBytecodeState().handleResult(language, (GeneratorYieldResult) result);
         }
     }
 
@@ -157,8 +243,7 @@ public final String toString() {
 
     public final PCode getOrCreateCode(Node inliningTarget, InlinedConditionProfile hasCodeProfile, PythonObjectFactory.Lazy factory) {
         if (hasCodeProfile.profile(inliningTarget, code == null)) {
-            RootCallTarget callTarget;
-            callTarget = bytecodeRootNode.getCallTarget();
+            RootCallTarget callTarget = getRootNode().getCallTarget();
             code = factory.get(inliningTarget).createCode(callTarget);
         }
         return code;
@@ -196,4 +281,20 @@ public final boolean isCoroutine() {
     public final boolean isAsyncGen() {
         return isAsyncGen;
     }
+
+    public int getBci() {
+        assert !PythonOptions.ENABLE_BYTECODE_DSL_INTERPRETER;
+        if (!isStarted()) {
+            return -1;
+        } else if (isFinished()) {
+            return getBytecodeState().rootNode.getCodeUnit().code.length;
+        } else {
+            return getBytecodeState().getCurrentRootNode().getResumeBci();
+        }
+    }
+
+    public ContinuationResult getContinuation() {
+        assert PythonOptions.ENABLE_BYTECODE_DSL_INTERPRETER;
+        return getBytecodeDSLState().yieldResult;
+    }
 }
diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/module/PythonFrozenModule.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/module/PythonFrozenModule.java
index fe76a33414..b20fc5df77 100644
--- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/module/PythonFrozenModule.java
+++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/module/PythonFrozenModule.java
@@ -47,6 +47,7 @@
 import java.io.IOException;
 import java.io.InputStream;
 
+import com.oracle.graal.python.runtime.PythonOptions;
 import com.oracle.truffle.api.strings.TruffleString;
 
 public final class PythonFrozenModule {
@@ -56,7 +57,7 @@ public final class PythonFrozenModule {
 
     private static byte[] getByteCode(String symbol) {
         try {
-            InputStream resourceAsStream = PythonFrozenModule.class.getResourceAsStream("Frozen" + symbol + ".bin");
+            InputStream resourceAsStream = PythonFrozenModule.class.getResourceAsStream("Frozen" + symbol + "." + getSuffix());
             if (resourceAsStream != null) {
                 return resourceAsStream.readAllBytes();
             }
@@ -66,6 +67,14 @@ private static byte[] getByteCode(String symbol) {
         return null;
     }
 
+    private static String getSuffix() {
+        if (PythonOptions.ENABLE_BYTECODE_DSL_INTERPRETER) {
+            return "bin_dsl";
+        } else {
+            return "bin";
+        }
+    }
+
     public PythonFrozenModule(String symbol, String originalName, boolean isPackage) {
         this(toTruffleStringUncached(originalName), getByteCode(symbol), isPackage);
     }
diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/set/SetNodes.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/set/SetNodes.java
index 2886aea9b2..da6483c80c 100644
--- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/set/SetNodes.java
+++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/set/SetNodes.java
@@ -56,6 +56,7 @@
 import com.oracle.graal.python.nodes.object.BuiltinClassProfiles.IsBuiltinObjectProfile;
 import com.oracle.graal.python.runtime.exception.PException;
 import com.oracle.graal.python.runtime.object.PythonObjectFactory;
+import com.oracle.truffle.api.bytecode.OperationProxy;
 import com.oracle.truffle.api.dsl.Bind;
 import com.oracle.truffle.api.dsl.Cached;
 import com.oracle.truffle.api.dsl.Cached.Exclusive;
@@ -141,6 +142,7 @@ public static ConstructSetNode getUncached() {
     }
 
     @GenerateUncached
+    @OperationProxy.Proxyable
     @SuppressWarnings("truffle-inlining")       // footprint reduction 92 -> 73
     public abstract static class AddNode extends PNodeWithContext {
         public abstract void execute(Frame frame, PSet self, Object o);
diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/superobject/SuperBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/superobject/SuperBuiltins.java
index c691778620..acb4fbf4ff 100644
--- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/superobject/SuperBuiltins.java
+++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/superobject/SuperBuiltins.java
@@ -80,6 +80,7 @@
 import com.oracle.graal.python.builtins.objects.type.slots.TpSlotDescrGet.DescrGetBuiltinNode;
 import com.oracle.graal.python.builtins.objects.type.slots.TpSlotGetAttr.GetAttrBuiltinNode;
 import com.oracle.graal.python.lib.PyObjectLookupAttr;
+import com.oracle.graal.python.compiler.CodeUnit;
 import com.oracle.graal.python.nodes.ErrorMessages;
 import com.oracle.graal.python.nodes.PGuards;
 import com.oracle.graal.python.nodes.PNodeWithContext;
@@ -87,6 +88,9 @@
 import com.oracle.graal.python.nodes.attributes.ReadAttributeFromObjectNode;
 import com.oracle.graal.python.nodes.bytecode.FrameInfo;
 import com.oracle.graal.python.nodes.bytecode.PBytecodeRootNode;
+import com.oracle.graal.python.nodes.bytecode_dsl.PBytecodeDSLRootNode;
+import com.oracle.graal.python.nodes.call.special.CallTernaryMethodNode;
+import com.oracle.graal.python.nodes.call.special.LookupAndCallBinaryNode;
 import com.oracle.graal.python.nodes.classes.IsSubtypeNode;
 import com.oracle.graal.python.nodes.frame.ReadCallerFrameNode;
 import com.oracle.graal.python.nodes.frame.ReadCallerFrameNode.FrameSelector;
@@ -95,6 +99,7 @@
 import com.oracle.graal.python.nodes.function.builtins.PythonUnaryBuiltinNode;
 import com.oracle.graal.python.nodes.function.builtins.PythonVarargsBuiltinNode;
 import com.oracle.graal.python.nodes.object.GetClassNode;
+import com.oracle.graal.python.runtime.PythonOptions;
 import com.oracle.graal.python.runtime.exception.PException;
 import com.oracle.graal.python.runtime.exception.PythonErrorType;
 import com.oracle.graal.python.runtime.object.PythonObjectFactory;
@@ -256,12 +261,17 @@ protected boolean isInBuiltinFunctionRoot() {
         PNone initInPlace(VirtualFrame frame, SuperObject self, @SuppressWarnings("unused") PNone clsArg, @SuppressWarnings("unused") PNone objArg,
                         @Bind("this") Node inliningTarget,
                         @Shared @Cached CellBuiltins.GetRefNode getRefNode) {
-            PBytecodeRootNode rootNode = (PBytecodeRootNode) getRootNode();
-            Frame localFrame = frame;
-            if (rootNode.getCodeUnit().isGeneratorOrCoroutine()) {
-                localFrame = PArguments.getGeneratorFrame(frame);
+            if (PythonOptions.ENABLE_BYTECODE_DSL_INTERPRETER) {
+                PBytecodeDSLRootNode rootNode = (PBytecodeDSLRootNode) getRootNode();
+                return initFromLocalFrame(frame, inliningTarget, self, rootNode, frame, getRefNode);
+            } else {
+                PBytecodeRootNode rootNode = (PBytecodeRootNode) getRootNode();
+                Frame localFrame = frame;
+                if (rootNode.getCodeUnit().isGeneratorOrCoroutine()) {
+                    localFrame = PArguments.getGeneratorFrame(frame);
+                }
+                return initFromLocalFrame(frame, inliningTarget, self, rootNode, localFrame, getRefNode);
             }
-            return initFromLocalFrame(frame, inliningTarget, self, rootNode, localFrame, getRefNode);
         }
 
         /**
@@ -281,7 +291,11 @@ PNone init(VirtualFrame frame, SuperObject self, @SuppressWarnings("unused") PNo
                 throw raise(RuntimeError, ErrorMessages.SUPER_NO_CLASS);
             }
             FrameInfo frameInfo = (FrameInfo) locals.getFrameDescriptor().getInfo();
-            return initFromLocalFrame(frame, inliningTarget, self, frameInfo.getRootNode(), locals, getRefNode);
+            if (PythonOptions.ENABLE_BYTECODE_DSL_INTERPRETER) {
+                return initFromLocalFrame(frame, inliningTarget, self, (PBytecodeDSLRootNode) frameInfo.getRootNode(), locals, getRefNode);
+            } else {
+                return initFromLocalFrame(frame, inliningTarget, self, (PBytecodeRootNode) frameInfo.getRootNode(), locals, getRefNode);
+            }
         }
 
         private PNone initFromLocalFrame(VirtualFrame frame, Node inliningTarget, SuperObject self, PBytecodeRootNode rootNode, Frame localFrame, CellBuiltins.GetRefNode getRefNode) {
@@ -301,6 +315,23 @@ private PNone initFromLocalFrame(VirtualFrame frame, Node inliningTarget, SuperO
             return init(frame, self, cls, obj);
         }
 
+        private PNone initFromLocalFrame(VirtualFrame frame, Node inliningTarget, SuperObject self, PBytecodeDSLRootNode rootNode, Frame localFrame, CellBuiltins.GetRefNode getRefNode) {
+            PCell classCell = rootNode.readClassCell(localFrame);
+            if (classCell == null) {
+                throw raise(RuntimeError, ErrorMessages.SUPER_NO_CLASS);
+            }
+            Object cls = getRefNode.execute(inliningTarget, classCell);
+            if (cls == null) {
+                // the cell is empty
+                throw raise(RuntimeError, ErrorMessages.SUPER_EMPTY_CLASS);
+            }
+            Object obj = rootNode.readSelf(localFrame);
+            if (obj == null) {
+                throw raise(RuntimeError, ErrorMessages.NO_ARGS, "super()");
+            }
+            return init(frame, self, cls, obj);
+        }
+
         @SuppressWarnings("unused")
         @Fallback
         PNone initFallback(Object self, Object cls, Object obj) {
diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/traceback/LazyTraceback.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/traceback/LazyTraceback.java
index a63b7d14b6..a1125279a3 100644
--- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/traceback/LazyTraceback.java
+++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/traceback/LazyTraceback.java
@@ -154,8 +154,8 @@ public static boolean elementWantedForTraceback(TruffleStackTraceElement element
         if (frame != null) {
             // only include frames of non-builtin python functions
             Object info = frame.getFrameDescriptor().getInfo();
-            if (info instanceof FrameInfo) {
-                return ((FrameInfo) info).getRootNode().frameIsVisibleToPython();
+            if (info instanceof FrameInfo frameInfo) {
+                return frameInfo.includeInTraceback();
             }
         }
         return false;
diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/traceback/PTraceback.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/traceback/PTraceback.java
index 3d04a261ab..21011830be 100644
--- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/traceback/PTraceback.java
+++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/traceback/PTraceback.java
@@ -46,7 +46,10 @@
 import com.oracle.graal.python.builtins.PythonBuiltinClassType;
 import com.oracle.graal.python.builtins.objects.frame.PFrame;
 import com.oracle.graal.python.builtins.objects.object.PythonBuiltinObject;
+import com.oracle.graal.python.runtime.PythonOptions;
 import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
+import com.oracle.truffle.api.bytecode.BytecodeNode;
+import com.oracle.truffle.api.nodes.Node;
 import com.oracle.truffle.api.strings.TruffleString;
 
 public final class PTraceback extends PythonBuiltinObject {
@@ -56,6 +59,7 @@ public final class PTraceback extends PythonBuiltinObject {
     private PFrame.Reference frameInfo;
     private int lineno = UNKNOWN_LINE_NUMBER;
     private int bci = -1;
+    private BytecodeNode bytecodeNode = null;
     private int lasti = -1;
     private PTraceback next;
     private LazyTraceback lazyTraceback;
@@ -107,13 +111,20 @@ public int getLineno() {
 
     public int getLasti(PFrame pFrame) {
         if (lasti == -1 && bci >= 0) {
-            lasti = pFrame.bciToLasti(bci);
+            Node location;
+            if (PythonOptions.ENABLE_BYTECODE_DSL_INTERPRETER) {
+                location = bytecodeNode;
+            } else {
+                location = pFrame.getLocation();
+            }
+            lasti = PFrame.bciToLasti(bci, location);
         }
         return lasti;
     }
 
-    public void setBci(int bci) {
+    public void setLocation(int bci, BytecodeNode bytecodeNode) {
         this.bci = bci;
+        this.bytecodeNode = bytecodeNode; // nullable
         this.lasti = -1;
     }
 
diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/traceback/TracebackBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/traceback/TracebackBuiltins.java
index 228a0a26ed..ccb0b7660e 100644
--- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/traceback/TracebackBuiltins.java
+++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/traceback/TracebackBuiltins.java
@@ -137,14 +137,14 @@ static void doMaterialize(Node inliningTarget, PTraceback tb,
                     if (LazyTraceback.elementWantedForTraceback(element)) {
                         PFrame pFrame = materializeFrame(element, materializeFrameNode);
                         next = factory.createTraceback(pFrame, pFrame.getLine(), next);
-                        next.setBci(pFrame.getBci());
+                        next.setLocation(pFrame.getBci(), pFrame.getBytecodeNode());
                         pyIndex++;
                     }
                 }
             }
             if (lazyTraceback.catchingFrameWantedForTraceback()) {
-                tb.setBci(pException.getCatchBci());
-                tb.setLineno(pException.getCatchRootNode().bciToLine(pException.getCatchBci()));
+                tb.setLocation(pException.getCatchBci(), pException.getBytecodeNode());
+                tb.setLineno(pException.getCatchLine());
                 tb.setNext(next);
             } else {
                 assert next != null;
diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/compiler/BytecodeCodeUnit.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/compiler/BytecodeCodeUnit.java
new file mode 100644
index 0000000000..97ed3fc182
--- /dev/null
+++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/compiler/BytecodeCodeUnit.java
@@ -0,0 +1,649 @@
+package com.oracle.graal.python.compiler;
+
+import java.util.ArrayDeque;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Objects;
+
+import com.oracle.graal.python.builtins.PythonBuiltinClassType;
+import com.oracle.graal.python.builtins.objects.bytes.BytesUtils;
+import com.oracle.graal.python.builtins.objects.code.PCode;
+import com.oracle.graal.python.builtins.objects.str.StringNodes;
+import com.oracle.graal.python.compiler.OpCodes.CollectionBits;
+import com.oracle.graal.python.nodes.ErrorMessages;
+import com.oracle.graal.python.nodes.PRaiseNode;
+import com.oracle.graal.python.util.PythonUtils;
+import com.oracle.truffle.api.CompilerDirectives;
+import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
+import com.oracle.truffle.api.strings.TruffleString;
+
+public class BytecodeCodeUnit extends CodeUnit {
+    private static final int DISASSEMBLY_NUM_COLUMNS = 8;
+
+    @CompilationFinal(dimensions = 1) public final byte[] code;
+    @CompilationFinal(dimensions = 1) public final byte[] srcOffsetTable;
+    @CompilationFinal(dimensions = 1) public final long[] primitiveConstants;
+    @CompilationFinal(dimensions = 1) public final int[] exceptionHandlerRanges;
+    public final int stacksize;
+    public final int conditionProfileCount;
+
+    /* Quickening data. See docs in PBytecodeRootNode */
+    @CompilationFinal(dimensions = 1) public final byte[] outputCanQuicken;
+    @CompilationFinal(dimensions = 1) public final byte[] variableShouldUnbox;
+    @CompilationFinal(dimensions = 1) public final int[][] generalizeInputsMap;
+    @CompilationFinal(dimensions = 1) public final int[][] generalizeVarsMap;
+
+    /* Lazily initialized source map */
+    @CompilationFinal SourceMap sourceMap;
+
+    public BytecodeCodeUnit(TruffleString name, TruffleString qualname,
+                    int argCount, int kwOnlyArgCount, int positionalOnlyArgCount, int flags,
+                    TruffleString[] names, TruffleString[] varnames, TruffleString[] cellvars,
+                    TruffleString[] freevars, int[] cell2arg, Object[] constants, int startLine, int startColumn,
+                    int endLine, int endColumn,
+                    byte[] code, byte[] linetable,
+                    long[] primitiveConstants, int[] exceptionHandlerRanges, int stacksize, int conditionProfileCount,
+                    byte[] outputCanQuicken, byte[] variableShouldUnbox, int[][] generalizeInputsMap, int[][] generalizeVarsMap) {
+        super(name, qualname, argCount, kwOnlyArgCount, positionalOnlyArgCount, flags, names, varnames, cellvars, freevars, cell2arg, constants, startLine, startColumn, endLine, endColumn);
+        this.code = code;
+        this.srcOffsetTable = linetable;
+        this.primitiveConstants = primitiveConstants;
+        this.exceptionHandlerRanges = exceptionHandlerRanges;
+        this.stacksize = stacksize;
+        this.conditionProfileCount = conditionProfileCount;
+        this.outputCanQuicken = outputCanQuicken;
+        this.variableShouldUnbox = variableShouldUnbox;
+        this.generalizeInputsMap = generalizeInputsMap;
+        this.generalizeVarsMap = generalizeVarsMap;
+    }
+
+    public SourceMap getSourceMap() {
+        if (sourceMap == null) {
+            CompilerDirectives.transferToInterpreterAndInvalidate();
+            sourceMap = new SourceMap(code, srcOffsetTable, startLine, startColumn);
+        }
+        return sourceMap;
+    }
+
+    public int bciToLine(int bci) {
+        if (bci < 0 || bci >= code.length) {
+            return -1;
+        }
+        return getSourceMap().startLineMap[bci];
+    }
+
+    public int bciToColumn(int bci) {
+        if (bci < 0 || bci >= code.length) {
+            return -1;
+        }
+        return getSourceMap().startColumnMap[bci];
+    }
+
+    @Override
+    public String toString() {
+        return toString(code);
+    }
+
+    public String toString(byte[] bytecode) {
+        StringBuilder sb = new StringBuilder();
+
+        HashMap lines = new HashMap<>();
+
+        sb.append("Disassembly of ").append(qualname).append(":\n");
+
+        List flagNames = new ArrayList<>();
+        if (isGenerator()) {
+            flagNames.add("CO_GENERATOR");
+        }
+        if (isCoroutine()) {
+            flagNames.add("CO_COROUTINE");
+        }
+        if (isAsyncGenerator()) {
+            flagNames.add("CO_ASYNC_GENERATOR");
+        }
+        if (!flagNames.isEmpty()) {
+            sb.append("Flags: ").append(String.join(" | ", flagNames)).append("\n");
+        }
+
+        int bci = 0;
+        int oparg = 0;
+        SourceMap map = getSourceMap();
+        while (bci < bytecode.length) {
+            int bcBCI = bci;
+            OpCodes opcode = OpCodes.fromOpCode(bytecode[bci++]);
+
+            String[] line = lines.computeIfAbsent(bcBCI, k -> new String[DISASSEMBLY_NUM_COLUMNS]);
+            line[0] = String.format("%3d:%-3d - %3d:%-3d", map.startLineMap[bcBCI], map.startColumnMap[bcBCI], map.endLineMap[bcBCI], map.endColumnMap[bcBCI]);
+            if (line[1] == null) {
+                line[1] = "";
+            }
+            line[2] = String.valueOf(bcBCI);
+            line[3] = opcode.toString();
+            byte[] followingArgs = PythonUtils.EMPTY_BYTE_ARRAY;
+            if (!opcode.hasArg()) {
+                line[4] = "";
+            } else {
+                oparg |= Byte.toUnsignedInt(bytecode[bci++]);
+                if (opcode.argLength > 1) {
+                    followingArgs = new byte[opcode.argLength - 1];
+                    for (int i = 0; i < opcode.argLength - 1; i++) {
+                        followingArgs[i] = bytecode[bci++];
+                    }
+                }
+                line[4] = String.format("% 2d", oparg);
+            }
+
+            while (true) {
+                switch (opcode) {
+                    case EXTENDED_ARG:
+                        line[4] = "";
+                        break;
+                    case LOAD_BYTE:
+                        line[4] = String.format("% 2d", (byte) oparg);
+                        break;
+                    case LOAD_CONST:
+                    case LOAD_BIGINT:
+                    case LOAD_STRING:
+                    case LOAD_BYTES:
+                    case LOAD_CONST_COLLECTION:
+                    case MAKE_KEYWORD: {
+                        Object constant = constants[oparg];
+                        if (constant instanceof CodeUnit) {
+                            line[5] = ((CodeUnit) constant).qualname.toJavaStringUncached();
+                        } else {
+                            if (constant instanceof TruffleString) {
+                                line[5] = StringNodes.StringReprNode.getUncached().execute((TruffleString) constant).toJavaStringUncached();
+                            } else if (constant instanceof byte[]) {
+                                byte[] bytes = (byte[]) constant;
+                                line[5] = BytesUtils.bytesRepr(bytes, bytes.length);
+                            } else if (constant instanceof int[]) {
+                                line[5] = Arrays.toString((int[]) constant);
+                            } else if (constant instanceof long[]) {
+                                line[5] = Arrays.toString((long[]) constant);
+                            } else if (constant instanceof boolean[]) {
+                                line[5] = Arrays.toString((boolean[]) constant);
+                            } else if (constant instanceof double[]) {
+                                line[5] = Arrays.toString((double[]) constant);
+                            } else if (constant instanceof Object[]) {
+                                line[5] = Arrays.toString((Object[]) constant);
+                            } else {
+                                line[5] = Objects.toString(constant);
+                            }
+                        }
+                        if (opcode == OpCodes.LOAD_CONST_COLLECTION) {
+                            line[5] += " type " + collectionTypeToString(followingArgs[0]) + " into " + collectionKindToString(followingArgs[0]);
+                        }
+                        break;
+                    }
+                    case MAKE_FUNCTION: {
+                        line[4] = String.format("% 2d", followingArgs[0]);
+                        CodeUnit codeUnit = (CodeUnit) constants[oparg];
+                        line[5] = line[5] = codeUnit.qualname.toJavaStringUncached();
+                        break;
+                    }
+                    case LOAD_INT:
+                    case LOAD_LONG:
+                        line[5] = Objects.toString(primitiveConstants[oparg]);
+                        break;
+                    case LOAD_DOUBLE:
+                        line[5] = Objects.toString(Double.longBitsToDouble(primitiveConstants[oparg]));
+                        break;
+                    case LOAD_COMPLEX: {
+                        double[] num = (double[]) constants[oparg];
+                        if (num[0] == 0.0) {
+                            line[5] = String.format("%gj", num[1]);
+                        } else {
+                            line[5] = String.format("%g%+gj", num[0], num[1]);
+                        }
+                        break;
+                    }
+                    case LOAD_CLOSURE:
+                    case LOAD_DEREF:
+                    case STORE_DEREF:
+                    case DELETE_DEREF:
+                        if (oparg >= cellvars.length) {
+                            line[5] = freevars[oparg - cellvars.length].toJavaStringUncached();
+                        } else {
+                            line[5] = cellvars[oparg].toJavaStringUncached();
+                        }
+                        break;
+                    case LOAD_FAST:
+                    case STORE_FAST:
+                    case DELETE_FAST:
+                        line[5] = varnames[oparg].toJavaStringUncached();
+                        break;
+                    case LOAD_NAME:
+                    case LOAD_METHOD:
+                    case STORE_NAME:
+                    case DELETE_NAME:
+                    case IMPORT_NAME:
+                    case IMPORT_FROM:
+                    case LOAD_GLOBAL:
+                    case STORE_GLOBAL:
+                    case DELETE_GLOBAL:
+                    case LOAD_ATTR:
+                    case STORE_ATTR:
+                    case DELETE_ATTR:
+                        line[5] = names[oparg].toJavaStringUncached();
+                        break;
+                    case FORMAT_VALUE: {
+                        int type = oparg & FormatOptions.FVC_MASK;
+                        switch (type) {
+                            case FormatOptions.FVC_STR:
+                                line[5] = "STR";
+                                break;
+                            case FormatOptions.FVC_REPR:
+                                line[5] = "REPR";
+                                break;
+                            case FormatOptions.FVC_ASCII:
+                                line[5] = "ASCII";
+                                break;
+                            case FormatOptions.FVC_NONE:
+                                line[5] = "NONE";
+                                break;
+                        }
+                        if ((oparg & FormatOptions.FVS_MASK) == FormatOptions.FVS_HAVE_SPEC) {
+                            line[5] += " + SPEC";
+                        }
+                        break;
+                    }
+                    case CALL_METHOD: {
+                        line[4] = String.format("% 2d", oparg);
+                        break;
+                    }
+                    case UNARY_OP:
+                        line[5] = UnaryOps.values()[oparg].toString();
+                        break;
+                    case BINARY_OP:
+                        line[5] = BinaryOps.values()[oparg].toString();
+                        break;
+                    case COLLECTION_FROM_STACK:
+                    case COLLECTION_ADD_STACK:
+                    case COLLECTION_FROM_COLLECTION:
+                    case COLLECTION_ADD_COLLECTION:
+                    case ADD_TO_COLLECTION:
+                        line[4] = String.format("% 2d", CollectionBits.elementCount(oparg));
+                        line[5] = collectionKindToString(oparg);
+                        break;
+                    case UNPACK_EX:
+                        line[5] = String.format("%d, %d", oparg, Byte.toUnsignedInt(followingArgs[0]));
+                        break;
+                    case JUMP_BACKWARD:
+                        lines.computeIfAbsent(bcBCI - oparg, k -> new String[DISASSEMBLY_NUM_COLUMNS])[1] = ">>";
+                        line[5] = String.format("to %d", bcBCI - oparg);
+                        break;
+                    case FOR_ITER:
+                    case JUMP_FORWARD:
+                    case POP_AND_JUMP_IF_FALSE:
+                    case POP_AND_JUMP_IF_TRUE:
+                    case JUMP_IF_FALSE_OR_POP:
+                    case JUMP_IF_TRUE_OR_POP:
+                    case MATCH_EXC_OR_JUMP:
+                    case SEND:
+                    case THROW:
+                        lines.computeIfAbsent(bcBCI + oparg, k -> new String[DISASSEMBLY_NUM_COLUMNS])[1] = ">>";
+                        line[5] = String.format("to %d", bcBCI + oparg);
+                        break;
+                    default:
+                        if (opcode.quickens != null) {
+                            opcode = opcode.quickens;
+                            continue;
+                        }
+                }
+                if (opcode == OpCodes.EXTENDED_ARG) {
+                    oparg <<= 8;
+                } else {
+                    oparg = 0;
+                }
+                break;
+            }
+        }
+
+        for (int i = 0; i < exceptionHandlerRanges.length; i += 4) {
+            int start = exceptionHandlerRanges[i];
+            int stop = exceptionHandlerRanges[i + 1];
+            int handler = exceptionHandlerRanges[i + 2];
+            int stackAtHandler = exceptionHandlerRanges[i + 3];
+            String[] line = lines.get(handler);
+            assert line != null;
+            String handlerStr = String.format("exc handler %d - %d; stack: %d", start, stop, stackAtHandler);
+            if (line[6] == null) {
+                line[6] = handlerStr;
+            } else {
+                line[6] += " | " + handlerStr;
+            }
+        }
+
+        for (bci = 0; bci < bytecode.length; bci++) {
+            String[] line = lines.get(bci);
+            if (line != null) {
+                line[5] = line[5] == null ? "" : String.format("(%s)", line[5]);
+                line[6] = line[6] == null ? "" : String.format("(%s)", line[6]);
+                line[7] = "";
+                if (outputCanQuicken != null && (outputCanQuicken[bci] != 0 || generalizeInputsMap[bci] != null)) {
+                    StringBuilder quickenSb = new StringBuilder();
+                    if (outputCanQuicken[bci] != 0) {
+                        quickenSb.append("can quicken");
+                    }
+                    if (generalizeInputsMap[bci] != null) {
+                        if (quickenSb.length() > 0) {
+                            quickenSb.append(", ");
+                        }
+                        quickenSb.append("generalizes: ");
+                        for (int i = 0; i < generalizeInputsMap[bci].length; i++) {
+                            if (i > 0) {
+                                quickenSb.append(", ");
+                            }
+                            quickenSb.append(generalizeInputsMap[bci][i]);
+                        }
+                    }
+                    line[7] = quickenSb.toString();
+                }
+                String formatted = String.format("%-8s %2s %4s %-32s %-3s   %-32s %s %s", (Object[]) line);
+                sb.append(formatted.stripTrailing());
+                sb.append('\n');
+            }
+        }
+
+        for (Object c : constants) {
+            if (c instanceof CodeUnit) {
+                sb.append('\n');
+                sb.append(c);
+            }
+        }
+
+        return sb.toString();
+    }
+
+    private static String collectionKindToString(int oparg) {
+        switch (CollectionBits.collectionKind(oparg)) {
+            case CollectionBits.KIND_LIST:
+                return "list";
+            case CollectionBits.KIND_TUPLE:
+                return "tuple";
+            case CollectionBits.KIND_SET:
+                return "set";
+            case CollectionBits.KIND_DICT:
+                return "dict";
+            case CollectionBits.KIND_KWORDS:
+                return "PKeyword[]";
+            case CollectionBits.KIND_OBJECT:
+                return "Object[]";
+        }
+        throw new IllegalStateException("Unknown kind");
+    }
+
+    private static String collectionTypeToString(int oparg) {
+        switch (CollectionBits.elementType(oparg)) {
+            case CollectionBits.ELEMENT_BOOLEAN:
+                return "boolean";
+            case CollectionBits.ELEMENT_INT:
+                return "int";
+            case CollectionBits.ELEMENT_LONG:
+                return "long";
+            case CollectionBits.ELEMENT_DOUBLE:
+                return "double";
+            case CollectionBits.ELEMENT_OBJECT:
+                return "Object";
+        }
+        throw new IllegalStateException("Unknown type");
+    }
+
+    public static final int LINE_TO_BCI_LINE_AFTER_CODEBLOCK = -1;
+    public static final int LINE_TO_BCI_LINE_BEFORE_CODEBLOCK = -2;
+
+    // -1 for line after the code block, -2 for line before the code block, line number otherwise
+    public int lineToBci(int line) {
+        if (startLine == line) {
+            return 0;
+        }
+        if ((flags & PCode.CO_GRAALPYHON_MODULE) != 0 && line < startLine) {
+            // allow jump to the first line of a file, even if it is a comment
+            return 0;
+        }
+        int[] map = getSourceMap().startLineMap;
+        int bestBci = LINE_TO_BCI_LINE_AFTER_CODEBLOCK;
+        int lineDiff = Integer.MAX_VALUE;
+        boolean afterFirst = false;
+        for (int bci = 0; bci < map.length; ++bci) {
+            if (map[bci] >= line) {
+                int lineDiff2 = map[bci] - line;
+                // the first bci found is the start of the line
+                if (lineDiff2 < lineDiff) {
+                    bestBci = bci;
+                    lineDiff = lineDiff2;
+                }
+            }
+            if (map[bci] > 0 && map[bci] <= line) {
+                // the line is actually within the codeblock.
+                afterFirst = true;
+            }
+        }
+        // bestBci being -1 means the line is outside the code block
+        return afterFirst ? bestBci : LINE_TO_BCI_LINE_BEFORE_CODEBLOCK;
+    }
+
+    public enum StackItem {
+        With("the body of a with statement"),
+        Iterable("the body of a for loop"),
+        Except("an 'except' block as there's no exception"),
+        Object("Incompatible stack");
+
+        public final String error;
+
+        StackItem(String error) {
+            this.error = error;
+        }
+
+        ArrayList push(ArrayList v) {
+            ArrayList ret = v == null ? new ArrayList<>() : new ArrayList<>(v);
+            ret.add(this);
+            return ret;
+        }
+    }
+
+    private void setNextStack(ArrayDeque todo, List> stacks, int target, ArrayList value) {
+        ArrayList blocksAtTarget = stacks.get(target);
+        if (blocksAtTarget == null) {
+            stacks.set(target, value);
+            todo.addLast(target);
+        } else {
+            assert value.equals(blocksAtTarget) : "found conflicting stacks depending on code path: " + this.name + "\t at " + target;
+        }
+    }
+
+    private static ArrayList popStack(ArrayList blocks) {
+        assert blocks != null : "Pop from null stack";
+        assert blocks.size() >= 1 : "Pop from empty stack";
+        return new ArrayList<>(blocks.subList(0, blocks.size() - 1));
+    }
+
+    // returns null if the jump is fine
+    public String checkJump(List> stackElems, int from, int to) {
+        ArrayList blkFrom = stackElems.get(from);
+        if (blkFrom == null) {
+            // this should not happen
+            PRaiseNode.getUncached().raise(PythonBuiltinClassType.ValueError, ErrorMessages.LINE_D_COMES_BEFORE_THE_CURRENT_CODE_BLOCK, bciToLine(from));
+        }
+        ArrayList blkTo = stackElems.get(to);
+        if (blkTo == null) {
+            PRaiseNode.getUncached().raise(PythonBuiltinClassType.ValueError, ErrorMessages.LINE_D_COMES_AFTER_THE_CURRENT_CODE_BLOCK, bciToLine(from));
+        }
+        if (blkTo.size() > blkFrom.size()) {
+            return blkTo.get(blkTo.size() - 1).error;
+        }
+        for (int i = blkTo.size() - 1; i >= 0; --i) {
+            if (blkTo.get(i) != blkFrom.get(i)) {
+                return blkTo.get(i).error;
+            }
+        }
+        return null;
+    }
+
+    public List> computeStackElems() {
+        List> blocks = new ArrayList<>(Collections.nCopies(code.length + 1, null));
+        blocks.set(0, new ArrayList<>());
+        ArrayDeque todo = new ArrayDeque<>();
+        todo.addFirst(0);
+        while (!todo.isEmpty()) {
+            int firstBci = todo.removeLast();
+            assert blocks.get(firstBci) != null : "Reached block without determining its stack state";
+            opCodeAt(code, firstBci, (bci, op, oparg, followingArgs) -> {
+                // firstBci can be different from bci if EXTEND_ARG is used
+                // the stack is kept both at firstBci and bci
+                ArrayList next = blocks.get(firstBci);
+                if (firstBci != bci) {
+                    blocks.set(bci, next);
+                }
+                for (int j = 0; j < exceptionHandlerRanges.length; j += 4) {
+                    int start = exceptionHandlerRanges[j];
+                    int handler = exceptionHandlerRanges[j + 2];
+                    int stack = exceptionHandlerRanges[j + 3];
+                    if (start == bci) {
+                        ArrayList handlerStack = StackItem.Except.push(new ArrayList<>(blocks.get(bci).subList(0, stack)));
+                        // an exception handler is like a jump
+                        // the except block is added in the lines below
+                        setNextStack(todo, blocks, handler, handlerStack);
+                    }
+                }
+                switch (op) {
+                    case GET_ITER:
+                    case GET_AITER:
+                        next = StackItem.Iterable.push(popStack(blocks.get(bci)));
+                        setNextStack(todo, blocks, bci + 1, next);
+                        break;
+                    case FOR_ITER:
+                        setNextStack(todo, blocks, op.getNextBci(bci, oparg, false), StackItem.Object.push(next));
+                        setNextStack(todo, blocks, op.getNextBci(bci, oparg, true), popStack(next));
+                        break;
+                    case PUSH_EXC_INFO:
+                        next = StackItem.Except.push(StackItem.Object.push(popStack(blocks.get(bci))));
+                        setNextStack(todo, blocks, bci + 1, next);
+                        break;
+                    case MATCH_EXC_OR_JUMP:
+                        next = popStack(next);
+                        setNextStack(todo, blocks, op.getNextBci(bci, oparg, false), next);
+                        setNextStack(todo, blocks, op.getNextBci(bci, oparg, true), next);
+                        break;
+                    case SETUP_WITH:
+                    case SETUP_AWITH:
+                        next = StackItem.Object.push(StackItem.With.push(blocks.get(bci)));
+                        setNextStack(todo, blocks, op.getNextBci(bci, oparg, false), next);
+                        break;
+                    case GET_AEXIT_CORO:
+                        next = StackItem.Object.push(StackItem.Except.push(popStack(popStack(popStack(blocks.get(bci))))));
+                        setNextStack(todo, blocks, op.getNextBci(bci, oparg, false), next);
+                        break;
+                    case DUP_TOP:
+                        next = next.get(next.size() - 1).push(next);
+                        setNextStack(todo, blocks, op.getNextBci(bci, oparg, false), next);
+                        break;
+                    case ROT_TWO: {
+                        StackItem top = next.get(next.size() - 1);
+                        StackItem belowTop = next.get(next.size() - 2);
+                        next = belowTop.push(top.push(popStack(popStack(next))));
+                        setNextStack(todo, blocks, op.getNextBci(bci, oparg, false), next);
+                        break;
+                    }
+                    case ROT_THREE: {
+                        StackItem top = next.get(next.size() - 1);
+                        StackItem second = next.get(next.size() - 2);
+                        StackItem third = next.get(next.size() - 3);
+                        next = second.push(third.push(top.push(top.push(popStack(popStack(popStack(next)))))));
+                        setNextStack(todo, blocks, op.getNextBci(bci, oparg, false), next);
+                        break;
+                    }
+                    case LOAD_NONE:
+                        opCodeAt(code, op.getNextBci(bci, oparg, false), (ignored, nextOp, ignored2, ignored3) -> {
+                            // Usually, when compiling bytecode around exception handlers, the code
+                            // is generated twice, once for the path with no exception, and
+                            // once for the path with the exception. However, when generating code
+                            // for a with statement exit, the code is generated as follows (and in a
+                            // similar manner for async with).
+                            // ...
+                            // LOAD_NONE
+                            // EXIT_WITH (exception handler starts here)
+                            // ...
+                            // This means that setting the stack at EXIT_WITH to have Object on top,
+                            // as LOAD_NONE usually would, would cause a conflict with the exception
+                            // handler starting at that position, which has the stack top be an
+                            // Exception.
+                            if (nextOp != OpCodes.GET_AEXIT_CORO && nextOp != OpCodes.EXIT_WITH) {
+                                setNextStack(todo, blocks, op.getNextBci(bci, oparg, false), StackItem.Object.push(blocks.get(bci)));
+                            }
+                        });
+                        break;
+
+                    default: {
+                        int nextWJump = op.getNextBci(bci, oparg, true);
+                        int nextWOJump = op.getNextBci(bci, oparg, false);
+                        int stackLostWJump = op.getNumberOfConsumedStackItems(oparg, followingArgs, true);
+                        int stackLostWOJump = op.getNumberOfConsumedStackItems(oparg, followingArgs, false);
+                        int stackGainWJump = op.getNumberOfProducedStackItems(oparg, followingArgs, true);
+                        int stackGainWOJump = op.getNumberOfProducedStackItems(oparg, followingArgs, false);
+                        handleGeneralOp(blocks, todo, bci, nextWJump, stackLostWJump, stackGainWJump);
+                        if (nextWJump != nextWOJump) {
+                            handleGeneralOp(blocks, todo, bci, nextWOJump, stackLostWOJump, stackGainWOJump);
+                        }
+                        break;
+                    }
+                }
+            });
+        }
+        return blocks;
+    }
+
+    private void handleGeneralOp(List> blocks, ArrayDeque todo, int bci, int next, int stackLost, int stackGain) {
+        if (next >= 0) {
+            ArrayList blocksHere = new ArrayList<>(blocks.get(bci));
+            for (int k = 0; k < stackLost; ++k) {
+                blocksHere.remove(blocksHere.size() - 1);
+            }
+            for (int k = 0; k < stackGain; ++k) {
+                blocksHere.add(StackItem.Object);
+            }
+            setNextStack(todo, blocks, next, blocksHere);
+        }
+    }
+
+    @FunctionalInterface
+    public interface BytecodeAction {
+        void run(int bci, OpCodes op, int oparg, byte[] followingArgs);
+    }
+
+    // returns the following bci
+    private static int opCodeAt(byte[] bytecode, int bci, BytecodeAction action) {
+        int oparg = 0;
+        OpCodes op = OpCodes.fromOpCode(bytecode[bci]);
+        while (op == OpCodes.EXTENDED_ARG) {
+            oparg |= Byte.toUnsignedInt(bytecode[bci + 1]);
+            oparg <<= 8;
+            bci += 2;
+            op = OpCodes.fromOpCode(bytecode[bci]);
+        }
+        byte[] followingArgs = null;
+        if (op.argLength > 0) {
+            oparg |= Byte.toUnsignedInt(bytecode[bci + 1]);
+            if (op.argLength > 1) {
+                followingArgs = new byte[op.argLength - 1];
+                System.arraycopy(bytecode, bci + 2, followingArgs, 0, followingArgs.length);
+            }
+        }
+        action.run(bci, op, oparg, followingArgs);
+        return bci + op.length();
+    }
+
+    public static void iterateBytecode(byte[] bytecode, BytecodeAction action) {
+        for (int bci = 0; bci < bytecode.length;) {
+            bci = opCodeAt(bytecode, bci, action);
+        }
+    }
+
+    public void iterateBytecode(BytecodeAction action) {
+        iterateBytecode(code, action);
+    }
+
+}
diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/compiler/CodeUnit.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/compiler/CodeUnit.java
index a9f29ef5fb..d4e61dd2b2 100644
--- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/compiler/CodeUnit.java
+++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/compiler/CodeUnit.java
@@ -40,23 +40,10 @@
  */
 package com.oracle.graal.python.compiler;
 
-import java.util.ArrayDeque;
-import java.util.ArrayList;
 import java.util.Arrays;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Objects;
 
-import com.oracle.graal.python.builtins.PythonBuiltinClassType;
-import com.oracle.graal.python.builtins.objects.bytes.BytesUtils;
 import com.oracle.graal.python.builtins.objects.code.PCode;
-import com.oracle.graal.python.builtins.objects.str.StringNodes;
-import com.oracle.graal.python.compiler.OpCodes.CollectionBits;
-import com.oracle.graal.python.nodes.ErrorMessages;
-import com.oracle.graal.python.nodes.PRaiseNode;
-import com.oracle.graal.python.util.PythonUtils;
-import com.oracle.truffle.api.CompilerDirectives;
+import com.oracle.graal.python.builtins.objects.function.Signature;
 import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
 import com.oracle.truffle.api.source.Source;
 import com.oracle.truffle.api.source.SourceSection;
@@ -67,9 +54,7 @@
  * bytecode and all the related data, like constants or exception handler ranges. It doesn't contain
  * the filename to make it easier to keep in native images.
  */
-public final class CodeUnit {
-    private static final int DISASSEMBLY_NUM_COLUMNS = 8;
-
+public abstract class CodeUnit {
     public final TruffleString name;
     public final TruffleString qualname;
 
@@ -77,10 +62,6 @@ public final class CodeUnit {
     public final int kwOnlyArgCount;
     public final int positionalOnlyArgCount;
 
-    public final int stacksize;
-
-    @CompilationFinal(dimensions = 1) public final byte[] code;
-    @CompilationFinal(dimensions = 1) public final byte[] srcOffsetTable;
     public final int flags;
 
     @CompilationFinal(dimensions = 1) public final TruffleString[] names;
@@ -91,42 +72,22 @@ public final class CodeUnit {
     @CompilationFinal(dimensions = 1) public final int[] arg2cell;
 
     @CompilationFinal(dimensions = 1) public final Object[] constants;
-    @CompilationFinal(dimensions = 1) public final long[] primitiveConstants;
-
-    @CompilationFinal(dimensions = 1) public final int[] exceptionHandlerRanges;
-
-    public final int conditionProfileCount;
 
     public final int startLine;
     public final int startColumn;
     public final int endLine;
     public final int endColumn;
 
-    /* Lazily initialized source map */
-    @CompilationFinal SourceMap sourceMap;
-
-    /* Quickening data. See docs in PBytecodeRootNode */
-    @CompilationFinal(dimensions = 1) public final byte[] outputCanQuicken;
-    @CompilationFinal(dimensions = 1) public final byte[] variableShouldUnbox;
-    @CompilationFinal(dimensions = 1) public final int[][] generalizeInputsMap;
-    @CompilationFinal(dimensions = 1) public final int[][] generalizeVarsMap;
-
     public CodeUnit(TruffleString name, TruffleString qualname,
-                    int argCount, int kwOnlyArgCount, int positionalOnlyArgCount, int stacksize,
-                    byte[] code, byte[] linetable, int flags,
-                    TruffleString[] names, TruffleString[] varnames, TruffleString[] cellvars, TruffleString[] freevars, int[] cell2arg,
-                    Object[] constants, long[] primitiveConstants,
-                    int[] exceptionHandlerRanges, int conditionProfileCount,
-                    int startLine, int startColumn, int endLine, int endColumn,
-                    byte[] outputCanQuicken, byte[] variableShouldUnbox, int[][] generalizeInputsMap, int[][] generalizeVarsMap) {
+                    int argCount, int kwOnlyArgCount, int positionalOnlyArgCount, int flags,
+                    TruffleString[] names, TruffleString[] varnames, TruffleString[] cellvars,
+                    TruffleString[] freevars, int[] cell2arg, Object[] constants, int startLine, int startColumn,
+                    int endLine, int endColumn) {
         this.name = name;
         this.qualname = qualname != null ? qualname : name;
         this.argCount = argCount;
         this.kwOnlyArgCount = kwOnlyArgCount;
         this.positionalOnlyArgCount = positionalOnlyArgCount;
-        this.stacksize = stacksize;
-        this.code = code;
-        this.srcOffsetTable = linetable;
         this.flags = flags;
         this.names = names;
         this.varnames = varnames;
@@ -145,39 +106,11 @@ public CodeUnit(TruffleString name, TruffleString qualname,
         }
         this.arg2cell = arg2cellValue;
         this.constants = constants;
-        this.primitiveConstants = primitiveConstants;
-        this.exceptionHandlerRanges = exceptionHandlerRanges;
-        this.conditionProfileCount = conditionProfileCount;
+
         this.startLine = startLine;
         this.startColumn = startColumn;
         this.endLine = endLine;
         this.endColumn = endColumn;
-        this.outputCanQuicken = outputCanQuicken;
-        this.variableShouldUnbox = variableShouldUnbox;
-        this.generalizeInputsMap = generalizeInputsMap;
-        this.generalizeVarsMap = generalizeVarsMap;
-    }
-
-    public SourceMap getSourceMap() {
-        if (sourceMap == null) {
-            CompilerDirectives.transferToInterpreterAndInvalidate();
-            sourceMap = new SourceMap(code, srcOffsetTable, startLine, startColumn);
-        }
-        return sourceMap;
-    }
-
-    public int bciToLine(int bci) {
-        if (bci < 0 || bci >= code.length) {
-            return -1;
-        }
-        return getSourceMap().startLineMap[bci];
-    }
-
-    public int bciToColumn(int bci) {
-        if (bci < 0 || bci >= code.length) {
-            return -1;
-        }
-        return getSourceMap().startColumnMap[bci];
     }
 
     public SourceSection getSourceSection(Source source) {
@@ -223,568 +156,16 @@ public int getTotalArgCount() {
         return count;
     }
 
-    @SuppressWarnings("fallthrough")
-    @Override
-    public String toString() {
-        return toString(code);
-    }
-
-    public String toString(byte[] bytecode) {
-        StringBuilder sb = new StringBuilder();
-
-        HashMap lines = new HashMap<>();
-
-        sb.append("Disassembly of ").append(qualname).append(":\n");
-
-        List flagNames = new ArrayList<>();
-        if (isGenerator()) {
-            flagNames.add("CO_GENERATOR");
-        }
-        if (isCoroutine()) {
-            flagNames.add("CO_COROUTINE");
-        }
-        if (isAsyncGenerator()) {
-            flagNames.add("CO_ASYNC_GENERATOR");
-        }
-        if (!flagNames.isEmpty()) {
-            sb.append("Flags: ").append(String.join(" | ", flagNames)).append("\n");
-        }
-
-        int bci = 0;
-        int oparg = 0;
-        SourceMap map = getSourceMap();
-        while (bci < bytecode.length) {
-            int bcBCI = bci;
-            OpCodes opcode = OpCodes.fromOpCode(bytecode[bci++]);
-
-            String[] line = lines.computeIfAbsent(bcBCI, k -> new String[DISASSEMBLY_NUM_COLUMNS]);
-            line[0] = String.format("%3d:%-3d - %3d:%-3d", map.startLineMap[bcBCI], map.startColumnMap[bcBCI], map.endLineMap[bcBCI], map.endColumnMap[bcBCI]);
-            if (line[1] == null) {
-                line[1] = "";
-            }
-            line[2] = String.valueOf(bcBCI);
-            line[3] = opcode.toString();
-            byte[] followingArgs = PythonUtils.EMPTY_BYTE_ARRAY;
-            if (!opcode.hasArg()) {
-                line[4] = "";
-            } else {
-                oparg |= Byte.toUnsignedInt(bytecode[bci++]);
-                if (opcode.argLength > 1) {
-                    followingArgs = new byte[opcode.argLength - 1];
-                    for (int i = 0; i < opcode.argLength - 1; i++) {
-                        followingArgs[i] = bytecode[bci++];
-                    }
-                }
-                line[4] = String.format("% 2d", oparg);
-            }
-
-            while (true) {
-                switch (opcode) {
-                    case EXTENDED_ARG:
-                        line[4] = "";
-                        break;
-                    case LOAD_BYTE:
-                        line[4] = String.format("% 2d", (byte) oparg);
-                        break;
-                    case LOAD_CONST:
-                    case LOAD_BIGINT:
-                    case LOAD_STRING:
-                    case LOAD_BYTES:
-                    case LOAD_CONST_COLLECTION:
-                    case MAKE_KEYWORD: {
-                        Object constant = constants[oparg];
-                        if (constant instanceof CodeUnit) {
-                            line[5] = ((CodeUnit) constant).qualname.toJavaStringUncached();
-                        } else {
-                            if (constant instanceof TruffleString) {
-                                line[5] = StringNodes.StringReprNode.getUncached().execute((TruffleString) constant).toJavaStringUncached();
-                            } else if (constant instanceof byte[]) {
-                                byte[] bytes = (byte[]) constant;
-                                line[5] = BytesUtils.bytesRepr(bytes, bytes.length);
-                            } else if (constant instanceof int[]) {
-                                line[5] = Arrays.toString((int[]) constant);
-                            } else if (constant instanceof long[]) {
-                                line[5] = Arrays.toString((long[]) constant);
-                            } else if (constant instanceof boolean[]) {
-                                line[5] = Arrays.toString((boolean[]) constant);
-                            } else if (constant instanceof double[]) {
-                                line[5] = Arrays.toString((double[]) constant);
-                            } else if (constant instanceof Object[]) {
-                                line[5] = Arrays.toString((Object[]) constant);
-                            } else {
-                                line[5] = Objects.toString(constant);
-                            }
-                        }
-                        if (opcode == OpCodes.LOAD_CONST_COLLECTION) {
-                            line[5] += " type " + collectionTypeToString(followingArgs[0]) + " into " + collectionKindToString(followingArgs[0]);
-                        }
-                        break;
-                    }
-                    case MAKE_FUNCTION: {
-                        line[4] = String.format("% 2d", followingArgs[0]);
-                        CodeUnit codeUnit = (CodeUnit) constants[oparg];
-                        line[5] = line[5] = codeUnit.qualname.toJavaStringUncached();
-                        break;
-                    }
-                    case LOAD_INT:
-                    case LOAD_LONG:
-                        line[5] = Objects.toString(primitiveConstants[oparg]);
-                        break;
-                    case LOAD_DOUBLE:
-                        line[5] = Objects.toString(Double.longBitsToDouble(primitiveConstants[oparg]));
-                        break;
-                    case LOAD_COMPLEX: {
-                        double[] num = (double[]) constants[oparg];
-                        if (num[0] == 0.0) {
-                            line[5] = String.format("%gj", num[1]);
-                        } else {
-                            line[5] = String.format("%g%+gj", num[0], num[1]);
-                        }
-                        break;
-                    }
-                    case LOAD_CLOSURE:
-                    case LOAD_DEREF:
-                    case STORE_DEREF:
-                    case DELETE_DEREF:
-                        if (oparg >= cellvars.length) {
-                            line[5] = freevars[oparg - cellvars.length].toJavaStringUncached();
-                        } else {
-                            line[5] = cellvars[oparg].toJavaStringUncached();
-                        }
-                        break;
-                    case LOAD_FAST:
-                    case STORE_FAST:
-                    case DELETE_FAST:
-                        line[5] = varnames[oparg].toJavaStringUncached();
-                        break;
-                    case LOAD_NAME:
-                    case LOAD_METHOD:
-                    case STORE_NAME:
-                    case DELETE_NAME:
-                    case IMPORT_NAME:
-                    case IMPORT_FROM:
-                    case LOAD_GLOBAL:
-                    case STORE_GLOBAL:
-                    case DELETE_GLOBAL:
-                    case LOAD_ATTR:
-                    case STORE_ATTR:
-                    case DELETE_ATTR:
-                        line[5] = names[oparg].toJavaStringUncached();
-                        break;
-                    case FORMAT_VALUE: {
-                        int type = oparg & FormatOptions.FVC_MASK;
-                        switch (type) {
-                            case FormatOptions.FVC_STR:
-                                line[5] = "STR";
-                                break;
-                            case FormatOptions.FVC_REPR:
-                                line[5] = "REPR";
-                                break;
-                            case FormatOptions.FVC_ASCII:
-                                line[5] = "ASCII";
-                                break;
-                            case FormatOptions.FVC_NONE:
-                                line[5] = "NONE";
-                                break;
-                        }
-                        if ((oparg & FormatOptions.FVS_MASK) == FormatOptions.FVS_HAVE_SPEC) {
-                            line[5] += " + SPEC";
-                        }
-                        break;
-                    }
-                    case CALL_METHOD: {
-                        line[4] = String.format("% 2d", oparg);
-                        break;
-                    }
-                    case UNARY_OP:
-                        line[5] = UnaryOps.values()[oparg].toString();
-                        break;
-                    case BINARY_OP:
-                        line[5] = BinaryOps.values()[oparg].toString();
-                        break;
-                    case COLLECTION_FROM_STACK:
-                    case COLLECTION_ADD_STACK:
-                    case COLLECTION_FROM_COLLECTION:
-                    case COLLECTION_ADD_COLLECTION:
-                    case ADD_TO_COLLECTION:
-                        line[4] = String.format("% 2d", CollectionBits.elementCount(oparg));
-                        line[5] = collectionKindToString(oparg);
-                        break;
-                    case UNPACK_EX:
-                        line[5] = String.format("%d, %d", oparg, Byte.toUnsignedInt(followingArgs[0]));
-                        break;
-                    case JUMP_BACKWARD:
-                        lines.computeIfAbsent(bcBCI - oparg, k -> new String[DISASSEMBLY_NUM_COLUMNS])[1] = ">>";
-                        line[5] = String.format("to %d", bcBCI - oparg);
-                        break;
-                    case FOR_ITER:
-                    case JUMP_FORWARD:
-                    case POP_AND_JUMP_IF_FALSE:
-                    case POP_AND_JUMP_IF_TRUE:
-                    case JUMP_IF_FALSE_OR_POP:
-                    case JUMP_IF_TRUE_OR_POP:
-                    case MATCH_EXC_OR_JUMP:
-                    case SEND:
-                    case THROW:
-                        lines.computeIfAbsent(bcBCI + oparg, k -> new String[DISASSEMBLY_NUM_COLUMNS])[1] = ">>";
-                        line[5] = String.format("to %d", bcBCI + oparg);
-                        break;
-                    default:
-                        if (opcode.quickens != null) {
-                            opcode = opcode.quickens;
-                            continue;
-                        }
-                }
-                if (opcode == OpCodes.EXTENDED_ARG) {
-                    oparg <<= 8;
-                } else {
-                    oparg = 0;
-                }
-                break;
-            }
-        }
-
-        for (int i = 0; i < exceptionHandlerRanges.length; i += 4) {
-            int start = exceptionHandlerRanges[i];
-            int stop = exceptionHandlerRanges[i + 1];
-            int handler = exceptionHandlerRanges[i + 2];
-            int stackAtHandler = exceptionHandlerRanges[i + 3];
-            String[] line = lines.get(handler);
-            assert line != null;
-            String handlerStr = String.format("exc handler %d - %d; stack: %d", start, stop, stackAtHandler);
-            if (line[6] == null) {
-                line[6] = handlerStr;
-            } else {
-                line[6] += " | " + handlerStr;
-            }
-        }
-
-        for (bci = 0; bci < bytecode.length; bci++) {
-            String[] line = lines.get(bci);
-            if (line != null) {
-                line[5] = line[5] == null ? "" : String.format("(%s)", line[5]);
-                line[6] = line[6] == null ? "" : String.format("(%s)", line[6]);
-                line[7] = "";
-                if (outputCanQuicken != null && (outputCanQuicken[bci] != 0 || generalizeInputsMap[bci] != null)) {
-                    StringBuilder quickenSb = new StringBuilder();
-                    if (outputCanQuicken[bci] != 0) {
-                        quickenSb.append("can quicken");
-                    }
-                    if (generalizeInputsMap[bci] != null) {
-                        if (quickenSb.length() > 0) {
-                            quickenSb.append(", ");
-                        }
-                        quickenSb.append("generalizes: ");
-                        for (int i = 0; i < generalizeInputsMap[bci].length; i++) {
-                            if (i > 0) {
-                                quickenSb.append(", ");
-                            }
-                            quickenSb.append(generalizeInputsMap[bci][i]);
-                        }
-                    }
-                    line[7] = quickenSb.toString();
-                }
-                String formatted = String.format("%-8s %2s %4s %-32s %-3s   %-32s %s %s", (Object[]) line);
-                sb.append(formatted.stripTrailing());
-                sb.append('\n');
-            }
-        }
-
-        for (Object c : constants) {
-            if (c instanceof CodeUnit) {
-                sb.append('\n');
-                sb.append(c);
-            }
-        }
-
-        return sb.toString();
-    }
-
-    private static String collectionKindToString(int oparg) {
-        switch (CollectionBits.collectionKind(oparg)) {
-            case CollectionBits.KIND_LIST:
-                return "list";
-            case CollectionBits.KIND_TUPLE:
-                return "tuple";
-            case CollectionBits.KIND_SET:
-                return "set";
-            case CollectionBits.KIND_DICT:
-                return "dict";
-            case CollectionBits.KIND_KWORDS:
-                return "PKeyword[]";
-            case CollectionBits.KIND_OBJECT:
-                return "Object[]";
-        }
-        throw new IllegalStateException("Unknown kind");
-    }
-
-    private static String collectionTypeToString(int oparg) {
-        switch (CollectionBits.elementType(oparg)) {
-            case CollectionBits.ELEMENT_BOOLEAN:
-                return "boolean";
-            case CollectionBits.ELEMENT_INT:
-                return "int";
-            case CollectionBits.ELEMENT_LONG:
-                return "long";
-            case CollectionBits.ELEMENT_DOUBLE:
-                return "double";
-            case CollectionBits.ELEMENT_OBJECT:
-                return "Object";
-        }
-        throw new IllegalStateException("Unknown type");
-    }
-
-    public static final int LINE_TO_BCI_LINE_AFTER_CODEBLOCK = -1;
-    public static final int LINE_TO_BCI_LINE_BEFORE_CODEBLOCK = -2;
-
-    // -1 for line after the code block, -2 for line before the code block, line number otherwise
-    public int lineToBci(int line) {
-        if (startLine == line) {
-            return 0;
-        }
-        if ((flags & PCode.CO_GRAALPYHON_MODULE) != 0 && line < startLine) {
-            // allow jump to the first line of a file, even if it is a comment
-            return 0;
-        }
-        int[] map = getSourceMap().startLineMap;
-        int bestBci = LINE_TO_BCI_LINE_AFTER_CODEBLOCK;
-        int lineDiff = Integer.MAX_VALUE;
-        boolean afterFirst = false;
-        for (int bci = 0; bci < map.length; ++bci) {
-            if (map[bci] >= line) {
-                int lineDiff2 = map[bci] - line;
-                // the first bci found is the start of the line
-                if (lineDiff2 < lineDiff) {
-                    bestBci = bci;
-                    lineDiff = lineDiff2;
-                }
-            }
-            if (map[bci] > 0 && map[bci] <= line) {
-                // the line is actually within the codeblock.
-                afterFirst = true;
-            }
-        }
-        // bestBci being -1 means the line is outside the code block
-        return afterFirst ? bestBci : LINE_TO_BCI_LINE_BEFORE_CODEBLOCK;
-    }
-
-    public enum StackItem {
-        With("the body of a with statement"),
-        Iterable("the body of a for loop"),
-        Except("an 'except' block as there's no exception"),
-        Object("Incompatible stack");
-
-        public final String error;
-
-        StackItem(String error) {
-            this.error = error;
-        }
-
-        ArrayList push(ArrayList v) {
-            ArrayList ret = v == null ? new ArrayList<>() : new ArrayList<>(v);
-            ret.add(this);
-            return ret;
-        }
-    }
-
-    private void setNextStack(ArrayDeque todo, List> stacks, int target, ArrayList value) {
-        ArrayList blocksAtTarget = stacks.get(target);
-        if (blocksAtTarget == null) {
-            stacks.set(target, value);
-            todo.addLast(target);
-        } else {
-            assert value.equals(blocksAtTarget) : "found conflicting stacks depending on code path: " + this.name + "\t at " + target;
-        }
-    }
-
-    private static ArrayList popStack(ArrayList blocks) {
-        assert blocks != null : "Pop from null stack";
-        assert blocks.size() >= 1 : "Pop from empty stack";
-        return new ArrayList<>(blocks.subList(0, blocks.size() - 1));
-    }
-
-    // returns null if the jump is fine
-    public String checkJump(List> stackElems, int from, int to) {
-        ArrayList blkFrom = stackElems.get(from);
-        if (blkFrom == null) {
-            // this should not happen
-            PRaiseNode.getUncached().raise(PythonBuiltinClassType.ValueError, ErrorMessages.LINE_D_COMES_BEFORE_THE_CURRENT_CODE_BLOCK, bciToLine(from));
-        }
-        ArrayList blkTo = stackElems.get(to);
-        if (blkTo == null) {
-            PRaiseNode.getUncached().raise(PythonBuiltinClassType.ValueError, ErrorMessages.LINE_D_COMES_AFTER_THE_CURRENT_CODE_BLOCK, bciToLine(from));
-        }
-        if (blkTo.size() > blkFrom.size()) {
-            return blkTo.get(blkTo.size() - 1).error;
-        }
-        for (int i = blkTo.size() - 1; i >= 0; --i) {
-            if (blkTo.get(i) != blkFrom.get(i)) {
-                return blkTo.get(i).error;
-            }
-        }
-        return null;
-    }
-
-    public List> computeStackElems() {
-        List> blocks = new ArrayList<>(Collections.nCopies(code.length + 1, null));
-        blocks.set(0, new ArrayList<>());
-        ArrayDeque todo = new ArrayDeque<>();
-        todo.addFirst(0);
-        while (!todo.isEmpty()) {
-            int firstBci = todo.removeLast();
-            assert blocks.get(firstBci) != null : "Reached block without determining its stack state";
-            opCodeAt(code, firstBci, (bci, op, oparg, followingArgs) -> {
-                // firstBci can be different from bci if EXTEND_ARG is used
-                // the stack is kept both at firstBci and bci
-                ArrayList next = blocks.get(firstBci);
-                if (firstBci != bci) {
-                    blocks.set(bci, next);
-                }
-                for (int j = 0; j < exceptionHandlerRanges.length; j += 4) {
-                    int start = exceptionHandlerRanges[j];
-                    int handler = exceptionHandlerRanges[j + 2];
-                    int stack = exceptionHandlerRanges[j + 3];
-                    if (start == bci) {
-                        ArrayList handlerStack = StackItem.Except.push(new ArrayList<>(blocks.get(bci).subList(0, stack)));
-                        // an exception handler is like a jump
-                        // the except block is added in the lines below
-                        setNextStack(todo, blocks, handler, handlerStack);
-                    }
-                }
-                switch (op) {
-                    case GET_ITER:
-                    case GET_AITER:
-                        next = StackItem.Iterable.push(popStack(blocks.get(bci)));
-                        setNextStack(todo, blocks, bci + 1, next);
-                        break;
-                    case FOR_ITER:
-                        setNextStack(todo, blocks, op.getNextBci(bci, oparg, false), StackItem.Object.push(next));
-                        setNextStack(todo, blocks, op.getNextBci(bci, oparg, true), popStack(next));
-                        break;
-                    case PUSH_EXC_INFO:
-                        next = StackItem.Except.push(StackItem.Object.push(popStack(blocks.get(bci))));
-                        setNextStack(todo, blocks, bci + 1, next);
-                        break;
-                    case MATCH_EXC_OR_JUMP:
-                        next = popStack(next);
-                        setNextStack(todo, blocks, op.getNextBci(bci, oparg, false), next);
-                        setNextStack(todo, blocks, op.getNextBci(bci, oparg, true), next);
-                        break;
-                    case SETUP_WITH:
-                    case SETUP_AWITH:
-                        next = StackItem.Object.push(StackItem.With.push(blocks.get(bci)));
-                        setNextStack(todo, blocks, op.getNextBci(bci, oparg, false), next);
-                        break;
-                    case GET_AEXIT_CORO:
-                        next = StackItem.Object.push(StackItem.Except.push(popStack(popStack(popStack(blocks.get(bci))))));
-                        setNextStack(todo, blocks, op.getNextBci(bci, oparg, false), next);
-                        break;
-                    case DUP_TOP:
-                        next = next.get(next.size() - 1).push(next);
-                        setNextStack(todo, blocks, op.getNextBci(bci, oparg, false), next);
-                        break;
-                    case ROT_TWO: {
-                        StackItem top = next.get(next.size() - 1);
-                        StackItem belowTop = next.get(next.size() - 2);
-                        next = belowTop.push(top.push(popStack(popStack(next))));
-                        setNextStack(todo, blocks, op.getNextBci(bci, oparg, false), next);
-                        break;
-                    }
-                    case ROT_THREE: {
-                        StackItem top = next.get(next.size() - 1);
-                        StackItem second = next.get(next.size() - 2);
-                        StackItem third = next.get(next.size() - 3);
-                        next = second.push(third.push(top.push(top.push(popStack(popStack(popStack(next)))))));
-                        setNextStack(todo, blocks, op.getNextBci(bci, oparg, false), next);
-                        break;
-                    }
-                    case LOAD_NONE:
-                        opCodeAt(code, op.getNextBci(bci, oparg, false), (ignored, nextOp, ignored2, ignored3) -> {
-                            // Usually, when compiling bytecode around exception handlers, the code
-                            // is generated twice, once for the path with no exception, and
-                            // once for the path with the exception. However, when generating code
-                            // for a with statement exit, the code is generated as follows (and in a
-                            // similar manner for async with).
-                            // ...
-                            // LOAD_NONE
-                            // EXIT_WITH (exception handler starts here)
-                            // ...
-                            // This means that setting the stack at EXIT_WITH to have Object on top,
-                            // as LOAD_NONE usually would, would cause a conflict with the exception
-                            // handler starting at that position, which has the stack top be an
-                            // Exception.
-                            if (nextOp != OpCodes.GET_AEXIT_CORO && nextOp != OpCodes.EXIT_WITH) {
-                                setNextStack(todo, blocks, op.getNextBci(bci, oparg, false), StackItem.Object.push(blocks.get(bci)));
-                            }
-                        });
-                        break;
-
-                    default: {
-                        int nextWJump = op.getNextBci(bci, oparg, true);
-                        int nextWOJump = op.getNextBci(bci, oparg, false);
-                        int stackLostWJump = op.getNumberOfConsumedStackItems(oparg, followingArgs, true);
-                        int stackLostWOJump = op.getNumberOfConsumedStackItems(oparg, followingArgs, false);
-                        int stackGainWJump = op.getNumberOfProducedStackItems(oparg, followingArgs, true);
-                        int stackGainWOJump = op.getNumberOfProducedStackItems(oparg, followingArgs, false);
-                        handleGeneralOp(blocks, todo, bci, nextWJump, stackLostWJump, stackGainWJump);
-                        if (nextWJump != nextWOJump) {
-                            handleGeneralOp(blocks, todo, bci, nextWOJump, stackLostWOJump, stackGainWOJump);
-                        }
-                        break;
-                    }
-                }
-            });
-        }
-        return blocks;
-    }
-
-    private void handleGeneralOp(List> blocks, ArrayDeque todo, int bci, int next, int stackLost, int stackGain) {
-        if (next >= 0) {
-            ArrayList blocksHere = new ArrayList<>(blocks.get(bci));
-            for (int k = 0; k < stackLost; ++k) {
-                blocksHere.remove(blocksHere.size() - 1);
-            }
-            for (int k = 0; k < stackGain; ++k) {
-                blocksHere.add(StackItem.Object);
-            }
-            setNextStack(todo, blocks, next, blocksHere);
-        }
-    }
-
-    @FunctionalInterface
-    public interface BytecodeAction {
-        void run(int bci, OpCodes op, int oparg, byte[] followingArgs);
-    }
-
-    // returns the following bci
-    private static int opCodeAt(byte[] bytecode, int bci, BytecodeAction action) {
-        int oparg = 0;
-        OpCodes op = OpCodes.fromOpCode(bytecode[bci]);
-        while (op == OpCodes.EXTENDED_ARG) {
-            oparg |= Byte.toUnsignedInt(bytecode[bci + 1]);
-            oparg <<= 8;
-            bci += 2;
-            op = OpCodes.fromOpCode(bytecode[bci]);
-        }
-        byte[] followingArgs = null;
-        if (op.argLength > 0) {
-            oparg |= Byte.toUnsignedInt(bytecode[bci + 1]);
-            if (op.argLength > 1) {
-                followingArgs = new byte[op.argLength - 1];
-                System.arraycopy(bytecode, bci + 2, followingArgs, 0, followingArgs.length);
-            }
-        }
-        action.run(bci, op, oparg, followingArgs);
-        return bci + op.length();
-    }
-
-    public static void iterateBytecode(byte[] bytecode, BytecodeAction action) {
-        for (int bci = 0; bci < bytecode.length;) {
-            bci = opCodeAt(bytecode, bci, action);
-        }
-    }
-
-    public void iterateBytecode(BytecodeAction action) {
-        iterateBytecode(code, action);
+    public final Signature computeSignature() {
+        int posArgCount = argCount + positionalOnlyArgCount;
+        TruffleString[] parameterNames = Arrays.copyOf(varnames, posArgCount);
+        TruffleString[] kwOnlyNames = Arrays.copyOfRange(varnames, posArgCount, posArgCount + kwOnlyArgCount);
+        int varArgsIndex = takesVarArgs() ? posArgCount : -1;
+        return new Signature(positionalOnlyArgCount,
+                        takesVarKeywordArgs(),
+                        varArgsIndex,
+                        positionalOnlyArgCount > 0,
+                        parameterNames,
+                        kwOnlyNames);
     }
 }
diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/compiler/CompilationScope.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/compiler/CompilationScope.java
index dee34b0461..2bc97e288e 100644
--- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/compiler/CompilationScope.java
+++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/compiler/CompilationScope.java
@@ -40,7 +40,7 @@
  */
 package com.oracle.graal.python.compiler;
 
-enum CompilationScope {
+public enum CompilationScope {
     Module,
     Class,
     Function,
diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/compiler/CompilationUnit.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/compiler/CompilationUnit.java
index 1bb92cce55..43da9e23b2 100644
--- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/compiler/CompilationUnit.java
+++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/compiler/CompilationUnit.java
@@ -183,7 +183,7 @@ private void addImplicitReturn() {
         }
     }
 
-    public CodeUnit assemble() {
+    public BytecodeCodeUnit assemble() {
         addImplicitReturn();
         calculateJumpInstructionArguments();
 
@@ -321,19 +321,19 @@ public CodeUnit assemble() {
                 }
             }
         }
-        return new CodeUnit(toTruffleStringUncached(name), toTruffleStringUncached(qualName),
-                        argCount, kwOnlyArgCount, positionalOnlyArgCount, maxStackSize,
-                        buf.toByteArray(), sourceMapBuilder.build(), flags,
-                        orderedKeys(names, new TruffleString[0], PythonUtils::toTruffleStringUncached),
-                        orderedKeys(varnames, new TruffleString[0], PythonUtils::toTruffleStringUncached),
-                        orderedKeys(cellvars, new TruffleString[0], PythonUtils::toTruffleStringUncached),
+        return new BytecodeCodeUnit(toTruffleStringUncached(name), toTruffleStringUncached(qualName),
+                        argCount, kwOnlyArgCount, positionalOnlyArgCount, flags,
+                        orderedKeys(names, new TruffleString[0], PythonUtils::toTruffleStringUncached), orderedKeys(varnames, new TruffleString[0], PythonUtils::toTruffleStringUncached), orderedKeys(cellvars, new TruffleString[0], PythonUtils::toTruffleStringUncached),
                         orderedKeys(freevars, new TruffleString[0], cellvars.size(), PythonUtils::toTruffleStringUncached),
                         cell2arg,
                         orderedKeys(constants, new Object[0]),
-                        orderedLong(primitiveConstants),
-                        exceptionHandlerRanges,
-                        conditionProfileCount,
-                        startLocation.startLine, startLocation.startColumn, startLocation.endLine, startLocation.endColumn,
+                        startLocation.startLine,
+                        startLocation.startColumn,
+                        startLocation.endLine,
+                        startLocation.endColumn,
+                        buf.toByteArray(),
+                        sourceMapBuilder.build(),
+                        orderedLong(primitiveConstants), exceptionHandlerRanges, maxStackSize, conditionProfileCount,
                         finishedCanQuickenOutput, shouldUnboxVariable, finishedGeneralizeInputsMap, finishedGeneralizeVarsMap);
     }
 
diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/compiler/Compiler.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/compiler/Compiler.java
index f8cfc842fd..f5fb183b00 100644
--- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/compiler/Compiler.java
+++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/compiler/Compiler.java
@@ -238,9 +238,9 @@ public Compiler(ErrorCallback errorCallback) {
     public CompilationUnit compile(ModTy mod, EnumSet flags, int optimizationLevel, EnumSet futureFeatures) {
         this.flags = flags;
         if (mod instanceof ModTy.Module) {
-            parseFuture(((ModTy.Module) mod).body);
+            futureLineno = parseFuture(((ModTy.Module) mod).body, futureFeatures, errorCallback);
         } else if (mod instanceof ModTy.Interactive) {
-            parseFuture(((ModTy.Interactive) mod).body);
+            futureLineno = parseFuture(((ModTy.Interactive) mod).body, futureFeatures, errorCallback);
         }
         this.futureFeatures.addAll(futureFeatures);
         this.env = ScopeEnvironment.analyze(mod, errorCallback, this.futureFeatures);
@@ -252,21 +252,23 @@ public CompilationUnit compile(ModTy mod, EnumSet flags, int optimization
         return topUnit;
     }
 
-    private void parseFuture(StmtTy[] modBody) {
+    public static int parseFuture(StmtTy[] modBody, EnumSet futureFeatures, ErrorCallback errorCallback) {
+        int lastFutureLine = -1;
         if (modBody == null || modBody.length == 0) {
-            return;
+            return lastFutureLine;
         }
         boolean done = false;
         int prevLine = 0;
         int i = 0;
-        if (getDocstring(modBody) != null) {
+        if (findDocstring(modBody) != null) {
             i++;
         }
+
         for (; i < modBody.length; i++) {
             StmtTy s = modBody[i];
             int line = s.getSourceRange().startLine;
             if (done && line > prevLine) {
-                return;
+                return lastFutureLine;
             }
             prevLine = line;
             if (s instanceof StmtTy.ImportFrom) {
@@ -275,8 +277,8 @@ private void parseFuture(StmtTy[] modBody) {
                     if (done) {
                         errorCallback.onError(ErrorType.Syntax, s.getSourceRange(), "from __future__ imports must occur at the beginning of the file");
                     }
-                    parseFutureFeatures(importFrom, futureFeatures);
-                    futureLineno = line;
+                    parseFutureFeatures(importFrom, futureFeatures, errorCallback);
+                    lastFutureLine = line;
                 } else {
                     done = true;
                 }
@@ -284,9 +286,10 @@ private void parseFuture(StmtTy[] modBody) {
                 done = true;
             }
         }
+        return lastFutureLine;
     }
 
-    private void parseFutureFeatures(StmtTy.ImportFrom node, EnumSet features) {
+    private static void parseFutureFeatures(StmtTy.ImportFrom node, EnumSet features, ErrorCallback errorCallback) {
         for (AliasTy alias : node.names) {
             if (alias.name != null) {
                 switch (alias.name) {
@@ -446,7 +449,7 @@ private void exitScope() {
         }
     }
 
-    private void checkForbiddenName(String id, ExprContextTy context) {
+    protected final void checkForbiddenName(String id, ExprContextTy context) {
         if (context == ExprContextTy.Store) {
             if (id.equals("__debug__")) {
                 errorCallback.onError(ErrorType.Syntax, unit.currentLocation, "cannot assign to __debug__");
@@ -658,10 +661,7 @@ private static  int addObject(HashMap dict, T o) {
         return v;
     }
 
-    private TruffleString getDocstring(StmtTy[] body) {
-        if (optimizationLevel >= 2) {
-            return null;
-        }
+    private static TruffleString findDocstring(StmtTy[] body) {
         if (body != null && body.length > 0) {
             StmtTy stmt = body[0];
             if (stmt instanceof StmtTy.Expr) {
@@ -677,6 +677,13 @@ private TruffleString getDocstring(StmtTy[] body) {
         return null;
     }
 
+    private TruffleString getDocstring(StmtTy[] body) {
+        if (optimizationLevel >= 2) {
+            return null;
+        }
+        return findDocstring(body);
+    }
+
     private SourceRange setLocation(SourceRange location) {
         SourceRange savedLocation = unit.currentLocation;
         unit.currentLocation = location;
@@ -887,7 +894,7 @@ private void collectIntoDict(ExprTy[] keys, ExprTy[] values) {
         collector.finishCollection();
     }
 
-    private void validateKeywords(KeywordTy[] keywords) {
+    protected final void validateKeywords(KeywordTy[] keywords) {
         for (int i = 0; i < keywords.length; i++) {
             if (keywords[i].arg != null) {
                 checkForbiddenName(keywords[i].arg, ExprContextTy.Store);
@@ -1547,7 +1554,7 @@ public Void visit(ExprTy.List node) {
                 case Store:
                     return unpackInto(node.elements);
                 case Load:
-                    boolean emittedConstant = tryCollectConstantCollection(node.elements, CollectionBits.KIND_LIST);
+                    boolean emittedConstant = tryLoadConstantCollection(node.elements, CollectionBits.KIND_LIST);
                     if (emittedConstant) {
                         return null;
                     }
@@ -1830,7 +1837,7 @@ public Void visit(ExprTy.Tuple node) {
                             }
                         }
                     }
-                    boolean emittedConstant = tryCollectConstantCollection(node.elements, CollectionBits.KIND_TUPLE);
+                    boolean emittedConstant = tryLoadConstantCollection(node.elements, CollectionBits.KIND_TUPLE);
                     if (emittedConstant) {
                         return null;
                     }
@@ -1850,13 +1857,33 @@ public Void visit(ExprTy.Tuple node) {
         }
     }
 
-    private boolean tryCollectConstantCollection(ExprTy[] elements, int collectionKind) {
+    private boolean tryLoadConstantCollection(ExprTy[] elements, int collectionKind) {
+        ConstantCollection constantCollection = tryCollectConstantCollection(elements);
+        if (constantCollection == null) {
+            return false;
+        }
+
+        addOp(LOAD_CONST_COLLECTION, addObject(unit.constants, constantCollection.collection), new byte[]{(byte) (constantCollection.elementType | collectionKind)});
+        return true;
+    }
+
+    public static final class ConstantCollection {
+        public final Object collection;
+        public final int elementType;
+
+        ConstantCollection(Object collection, int elementType) {
+            this.collection = collection;
+            this.elementType = elementType;
+        }
+    }
+
+    public static ConstantCollection tryCollectConstantCollection(ExprTy[] elements) {
         /*
          * We try to store the whole tuple as a Java array constant when all the elements are
          * constant and context-independent.
          */
         if (elements == null || elements.length == 0) {
-            return false;
+            return null;
         }
 
         int constantType = -1;
@@ -1886,10 +1913,10 @@ private boolean tryCollectConstantCollection(ExprTy[] elements, int collectionKi
                     constantType = determineConstantType(constantType, CollectionBits.ELEMENT_OBJECT);
                     constants.add(PNone.NONE);
                 } else {
-                    return false;
+                    return null;
                 }
             } else {
-                return false;
+                return null;
             }
         }
         Object newConstant = null;
@@ -1930,11 +1957,10 @@ private boolean tryCollectConstantCollection(ExprTy[] elements, int collectionKi
                 break;
             }
         }
-        addOp(LOAD_CONST_COLLECTION, addObject(unit.constants, newConstant), new byte[]{(byte) (constantType | collectionKind)});
-        return true;
+        return new ConstantCollection(newConstant, constantType);
     }
 
-    int determineConstantType(int existing, int type) {
+    private static int determineConstantType(int existing, int type) {
         if (existing == -1 || existing == type) {
             return type;
         }
diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/compiler/bytecode_dsl/BaseBytecodeDSLVisitor.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/compiler/bytecode_dsl/BaseBytecodeDSLVisitor.java
new file mode 100644
index 0000000000..ae663b9c3c
--- /dev/null
+++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/compiler/bytecode_dsl/BaseBytecodeDSLVisitor.java
@@ -0,0 +1,493 @@
+/*
+ * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * The Universal Permissive License (UPL), Version 1.0
+ *
+ * Subject to the condition set forth below, permission is hereby granted to any
+ * person obtaining a copy of this software, associated documentation and/or
+ * data (collectively the "Software"), free of charge and under any and all
+ * copyright rights in the Software, and any and all patent rights owned or
+ * freely licensable by each licensor hereunder covering either (i) the
+ * unmodified Software as contributed to or provided by such licensor, or (ii)
+ * the Larger Works (as defined below), to deal in both
+ *
+ * (a) the Software, and
+ *
+ * (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if
+ * one is included with the Software each a "Larger Work" to which the Software
+ * is contributed by such licensors),
+ *
+ * without restriction, including without limitation the rights to copy, create
+ * derivative works of, display, perform, and distribute the Software and make,
+ * use, sell, offer for sale, import, export, have made, and have sold the
+ * Software and the Larger Work(s), and to sublicense the foregoing rights on
+ * either these or other terms.
+ *
+ * This license is subject to the following condition:
+ *
+ * The above copyright notice and either this complete permission notice or at a
+ * minimum a reference to the UPL must be included in all copies or substantial
+ * portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+package com.oracle.graal.python.compiler.bytecode_dsl;
+
+import com.oracle.graal.python.pegparser.sst.AliasTy;
+import com.oracle.graal.python.pegparser.sst.ArgTy;
+import com.oracle.graal.python.pegparser.sst.ArgumentsTy;
+import com.oracle.graal.python.pegparser.sst.ComprehensionTy;
+import com.oracle.graal.python.pegparser.sst.ExceptHandlerTy;
+import com.oracle.graal.python.pegparser.sst.ExprTy;
+import com.oracle.graal.python.pegparser.sst.KeywordTy;
+import com.oracle.graal.python.pegparser.sst.MatchCaseTy;
+import com.oracle.graal.python.pegparser.sst.ModTy;
+import com.oracle.graal.python.pegparser.sst.PatternTy;
+import com.oracle.graal.python.pegparser.sst.SSTNode;
+import com.oracle.graal.python.pegparser.sst.SSTreeVisitor;
+import com.oracle.graal.python.pegparser.sst.StmtTy;
+import com.oracle.graal.python.pegparser.sst.TypeIgnoreTy.TypeIgnore;
+import com.oracle.graal.python.pegparser.sst.WithItemTy;
+
+/**
+ * This interface provides default implementations of all {@code SSTreeVisitor} methods, which makes
+ * it easier to incrementally add support to the Bytecode DSL compiler. Once the compiler is stable,
+ * this interface should be removed.
+ */
+public interface BaseBytecodeDSLVisitor extends SSTreeVisitor {
+
+    default T defaultValue(SSTNode node) {
+        throw new UnsupportedOperationException(getClass().getSimpleName() + ": " + node.getClass().getSimpleName());
+    }
+
+    default void visitNode(SSTNode node) {
+        if (node != null) {
+            node.accept(this);
+        }
+    }
+
+    default T visit(AliasTy node) {
+        return defaultValue(node);
+    }
+
+    default T visit(ArgTy node) {
+        return defaultValue(node);
+    }
+
+    default T visit(ArgumentsTy node) {
+        visitSequence(node.defaults);
+        visitSequence(node.kwDefaults);
+        return defaultValue(node);
+    }
+
+    default T visit(ComprehensionTy node) {
+        visitNode(node.iter);
+        visitSequence(node.ifs);
+        visitNode(node.target);
+        return defaultValue(node);
+    }
+
+    default T visit(ExprTy.Attribute node) {
+        visitNode(node.value);
+        return defaultValue(node);
+    }
+
+    default T visit(ExprTy.Await node) {
+        visitNode(node.value);
+        return defaultValue(node);
+    }
+
+    default T visit(ExprTy.BinOp node) {
+        visitNode(node.left);
+        visitNode(node.right);
+        return defaultValue(node);
+    }
+
+    default T visit(ExprTy.BoolOp node) {
+        visitSequence(node.values);
+        return defaultValue(node);
+    }
+
+    default T visit(ExprTy.Call node) {
+        visitNode(node.func);
+        visitSequence(node.args);
+        visitSequence(node.keywords);
+        return defaultValue(node);
+    }
+
+    default T visit(ExprTy.Compare node) {
+        visitNode(node.left);
+        visitSequence(node.comparators);
+        return defaultValue(node);
+    }
+
+    default T visit(ExprTy.Constant node) {
+        return defaultValue(node);
+    }
+
+    default T visit(ExprTy.Dict node) {
+        visitSequence(node.keys);
+        visitSequence(node.values);
+        return defaultValue(node);
+    }
+
+    default T visit(ExprTy.DictComp node) {
+        visitSequence(node.generators);
+        visitNode(node.key);
+        visitNode(node.value);
+        return defaultValue(node);
+    }
+
+    default T visit(ExprTy.FormattedValue node) {
+        visitNode(node.formatSpec);
+        visitNode(node.value);
+        return defaultValue(node);
+    }
+
+    default T visit(ExprTy.GeneratorExp node) {
+        visitNode(node.element);
+        visitSequence(node.generators);
+        return defaultValue(node);
+    }
+
+    default T visit(ExprTy.IfExp node) {
+        visitNode(node.test);
+        visitNode(node.body);
+        visitNode(node.orElse);
+        return defaultValue(node);
+    }
+
+    default T visit(ExprTy.JoinedStr node) {
+        visitSequence(node.values);
+        return defaultValue(node);
+    }
+
+    default T visit(ExprTy.Lambda node) {
+        visitNode(node.body);
+        return defaultValue(node);
+    }
+
+    default T visit(ExprTy.List node) {
+        visitSequence(node.elements);
+        return defaultValue(node);
+    }
+
+    default T visit(ExprTy.ListComp node) {
+        visitSequence(node.generators);
+        visitNode(node.element);
+        return defaultValue(node);
+    }
+
+    default T visit(ExprTy.Name node) {
+        return defaultValue(node);
+    }
+
+    default T visit(ExprTy.NamedExpr node) {
+        visitNode(node.target);
+        visitNode(node.value);
+        return defaultValue(node);
+    }
+
+    default T visit(ExprTy.Set node) {
+        visitSequence(node.elements);
+        return defaultValue(node);
+    }
+
+    default T visit(ExprTy.SetComp node) {
+        visitSequence(node.generators);
+        visitNode(node.element);
+        return defaultValue(node);
+    }
+
+    default T visit(ExprTy.Slice node) {
+        visitNode(node.lower);
+        visitNode(node.upper);
+        visitNode(node.step);
+        return defaultValue(node);
+    }
+
+    default T visit(ExprTy.Starred node) {
+        visitNode(node.value);
+        return defaultValue(node);
+    }
+
+    default T visit(ExprTy.Subscript node) {
+        visitNode(node.value);
+        visitNode(node.slice);
+        return defaultValue(node);
+    }
+
+    default T visit(ExprTy.Tuple node) {
+        visitSequence(node.elements);
+        return defaultValue(node);
+    }
+
+    default T visit(ExprTy.UnaryOp node) {
+        visitNode(node.operand);
+        return defaultValue(node);
+    }
+
+    default T visit(ExprTy.Yield node) {
+        visitNode(node.value);
+        return defaultValue(node);
+    }
+
+    default T visit(ExprTy.YieldFrom node) {
+        visitNode(node.value);
+        return defaultValue(node);
+    }
+
+    default T visit(KeywordTy node) {
+        visitNode(node.value);
+        return defaultValue(node);
+    }
+
+    default T visit(ModTy.Expression node) {
+        visitNode(node.body);
+        return defaultValue(node);
+    }
+
+    default T visit(ModTy.FunctionType node) {
+        visitNode(node.returns);
+        return defaultValue(node);
+    }
+
+    default T visit(ModTy.Interactive node) {
+        visitSequence(node.body);
+        return defaultValue(node);
+    }
+
+    default T visit(ModTy.Module node) {
+        visitSequence(node.body);
+        return defaultValue(node);
+    }
+
+    default T visit(StmtTy.AnnAssign node) {
+        visitNode(node.target);
+        visitNode(node.annotation);
+        visitNode(node.value);
+        return defaultValue(node);
+    }
+
+    default T visit(StmtTy.Assert node) {
+        visitNode(node.test);
+        visitNode(node.msg);
+        return defaultValue(node);
+    }
+
+    default T visit(StmtTy.Assign node) {
+        visitNode(node.value);
+        visitSequence(node.targets);
+        return defaultValue(node);
+    }
+
+    default T visit(StmtTy.AsyncFor node) {
+        visitNode(node.target);
+        visitNode(node.iter);
+        visitSequence(node.body);
+        visitSequence(node.orElse);
+        return defaultValue(node);
+    }
+
+    default T visit(StmtTy.AsyncFunctionDef node) {
+        visitSequence(node.decoratorList);
+        visitNode(node.args);
+        visitNode(node.returns);
+        visitSequence(node.body);
+        return defaultValue(node);
+    }
+
+    default T visit(StmtTy.AsyncWith node) {
+        visitSequence(node.items);
+        visitSequence(node.body);
+        return defaultValue(node);
+    }
+
+    default T visit(StmtTy.AugAssign node) {
+        visitNode(node.target);
+        visitNode(node.value);
+        return defaultValue(node);
+    }
+
+    default T visit(StmtTy.ClassDef node) {
+        visitSequence(node.decoratorList);
+        visitSequence(node.bases);
+        visitSequence(node.keywords);
+        visitSequence(node.body);
+        return defaultValue(node);
+    }
+
+    default T visit(StmtTy.Delete node) {
+        visitSequence(node.targets);
+        return defaultValue(node);
+    }
+
+    default T visit(StmtTy.Expr node) {
+        visitNode(node.value);
+        return defaultValue(node);
+    }
+
+    default T visit(StmtTy.For node) {
+        visitNode(node.iter);
+        visitNode(node.target);
+        visitSequence(node.body);
+        visitSequence(node.orElse);
+        return defaultValue(node);
+    }
+
+    default T visit(StmtTy.FunctionDef node) {
+        visitSequence(node.decoratorList);
+        visitNode(node.args);
+        visitNode(node.returns);
+        visitSequence(node.body);
+        return defaultValue(node);
+    }
+
+    default T visit(StmtTy.Global node) {
+        return defaultValue(node);
+    }
+
+    default T visit(StmtTy.If node) {
+        visitNode(node.test);
+        visitSequence(node.body);
+        visitSequence(node.orElse);
+        return defaultValue(node);
+    }
+
+    default T visit(StmtTy.Import node) {
+        return defaultValue(node);
+    }
+
+    default T visit(StmtTy.ImportFrom node) {
+        return defaultValue(node);
+    }
+
+    default T visit(StmtTy.Match node) {
+        visitNode(node.subject);
+        visitSequence(node.cases);
+        return defaultValue(node);
+    }
+
+    default T visit(MatchCaseTy node) {
+        visitNode(node.pattern);
+        visitNode(node.guard);
+        visitSequence(node.body);
+        return defaultValue(node);
+    }
+
+    default T visit(PatternTy.MatchAs node) {
+        visitNode(node.pattern);
+        return defaultValue(node);
+    }
+
+    default T visit(PatternTy.MatchClass node) {
+        visitSequence(node.patterns);
+        visitSequence(node.kwdPatterns);
+        visitNode(node.cls);
+        return defaultValue(node);
+    }
+
+    default T visit(PatternTy.MatchMapping node) {
+        visitSequence(node.keys);
+        visitSequence(node.patterns);
+        return defaultValue(node);
+    }
+
+    default T visit(PatternTy.MatchOr node) {
+        visitSequence(node.patterns);
+        return defaultValue(node);
+    }
+
+    default T visit(PatternTy.MatchSequence node) {
+        visitSequence(node.patterns);
+        return defaultValue(node);
+    }
+
+    default T visit(PatternTy.MatchSingleton node) {
+        return defaultValue(node);
+    }
+
+    default T visit(PatternTy.MatchStar node) {
+        return defaultValue(node);
+    }
+
+    default T visit(PatternTy.MatchValue node) {
+        visitNode(node.value);
+        return defaultValue(node);
+    }
+
+    default T visit(StmtTy.Nonlocal node) {
+        return defaultValue(node);
+    }
+
+    default T visit(StmtTy.Raise node) {
+        visitNode(node.exc);
+        visitNode(node.cause);
+        return defaultValue(node);
+    }
+
+    default T visit(StmtTy.Return node) {
+        visitNode(node.value);
+        return defaultValue(node);
+    }
+
+    default T visit(StmtTy.Try node) {
+        visitSequence(node.body);
+        visitSequence(node.orElse);
+        visitSequence(node.finalBody);
+        visitSequence(node.handlers);
+        return defaultValue(node);
+    }
+
+    default T visit(StmtTy.TryStar node) {
+        return defaultValue(node);
+    }
+
+    default T visit(ExceptHandlerTy.ExceptHandler node) {
+        visitNode(node.type);
+        visitSequence(node.body);
+        return defaultValue(node);
+    }
+
+    default T visit(StmtTy.While node) {
+        visitNode(node.test);
+        visitSequence(node.body);
+        visitSequence(node.orElse);
+        return defaultValue(node);
+    }
+
+    default T visit(StmtTy.With node) {
+        visitSequence(node.items);
+        visitSequence(node.body);
+        return defaultValue(node);
+    }
+
+    default T visit(WithItemTy node) {
+        visitNode(node.contextExpr);
+        visitNode(node.optionalVars);
+        return defaultValue(node);
+    }
+
+    default T visit(StmtTy.Break node) {
+        return defaultValue(node);
+    }
+
+    default T visit(StmtTy.Continue node) {
+        return defaultValue(node);
+    }
+
+    default T visit(StmtTy.Pass node) {
+        return defaultValue(node);
+    }
+
+    default T visit(TypeIgnore node) {
+        return defaultValue(node);
+    }
+}
diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/compiler/bytecode_dsl/BytecodeDSLCompiler.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/compiler/bytecode_dsl/BytecodeDSLCompiler.java
new file mode 100644
index 0000000000..fd23a61002
--- /dev/null
+++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/compiler/bytecode_dsl/BytecodeDSLCompiler.java
@@ -0,0 +1,124 @@
+package com.oracle.graal.python.compiler.bytecode_dsl;
+
+import java.util.EnumSet;
+import java.util.HashMap;
+import java.util.Map;
+
+import com.oracle.graal.python.PythonLanguage;
+import com.oracle.graal.python.compiler.Compiler;
+import com.oracle.graal.python.compiler.RaisePythonExceptionErrorCallback;
+import com.oracle.graal.python.nodes.bytecode_dsl.BytecodeDSLCodeUnit;
+import com.oracle.graal.python.nodes.bytecode_dsl.PBytecodeDSLRootNode;
+import com.oracle.graal.python.pegparser.FutureFeature;
+import com.oracle.graal.python.pegparser.scope.Scope;
+import com.oracle.graal.python.pegparser.scope.ScopeEnvironment;
+import com.oracle.graal.python.pegparser.sst.ModTy;
+import com.oracle.graal.python.pegparser.sst.StmtTy;
+import com.oracle.graal.python.runtime.PythonContext;
+import com.oracle.graal.python.runtime.object.PythonObjectSlowPathFactory;
+import com.oracle.truffle.api.source.Source;
+
+public class BytecodeDSLCompiler {
+
+    public static final record BytecodeDSLCompilerResult(PBytecodeDSLRootNode rootNode, BytecodeDSLCodeUnit codeUnit) {
+    }
+
+    public static BytecodeDSLCompilerResult compile(PythonLanguage language, PythonContext context, ModTy mod, Source source, int optimize, RaisePythonExceptionErrorCallback errorCallback,
+                    EnumSet futureFeatures) {
+        /**
+         * Parse __future__ annotations before the analysis step. The analysis does extra validation
+         * when __future__.annotations is imported.
+         */
+        int futureLineNumber = parseFuture(mod, futureFeatures, errorCallback);
+        ScopeEnvironment scopeEnvironment = ScopeEnvironment.analyze(mod, errorCallback, futureFeatures);
+        BytecodeDSLCompilerContext ctx = new BytecodeDSLCompilerContext(language, context, mod, source, optimize, futureFeatures, futureLineNumber, errorCallback, scopeEnvironment);
+        RootNodeCompiler compiler = new RootNodeCompiler(ctx, mod, futureFeatures);
+        return compiler.compile();
+    }
+
+    private static int parseFuture(ModTy mod, EnumSet futureFeatures, RaisePythonExceptionErrorCallback errorCallback) {
+        StmtTy[] stmts = null;
+        if (mod instanceof ModTy.Module module) {
+            stmts = module.body;
+        } else if (mod instanceof ModTy.Interactive interactive) {
+            stmts = interactive.body;
+        } else {
+            return -1;
+        }
+        return Compiler.parseFuture(stmts, futureFeatures, errorCallback);
+    }
+
+    public static class BytecodeDSLCompilerContext {
+
+        public final PythonLanguage language;
+        public final PythonContext pythonContext;
+        public final PythonObjectSlowPathFactory factory;
+        public final ModTy mod;
+        public final Source source;
+        public final int optimizationLevel;
+        public final EnumSet futureFeatures;
+        public final int futureLineNumber;
+        public final RaisePythonExceptionErrorCallback errorCallback;
+        public final ScopeEnvironment scopeEnvironment;
+        public final Map qualifiedNames;
+
+        public BytecodeDSLCompilerContext(PythonLanguage language, PythonContext context, ModTy mod, Source source, int optimizationLevel,
+                        EnumSet futureFeatures, int futureLineNumber, RaisePythonExceptionErrorCallback errorCallback, ScopeEnvironment scopeEnvironment) {
+            this.language = language;
+            this.pythonContext = context;
+            this.factory = context.factory();
+            this.mod = mod;
+            this.source = source;
+            this.optimizationLevel = optimizationLevel;
+            this.futureFeatures = futureFeatures;
+            this.futureLineNumber = futureLineNumber;
+            this.errorCallback = errorCallback;
+            this.scopeEnvironment = scopeEnvironment;
+            this.qualifiedNames = new HashMap<>();
+        }
+
+        String mangle(Scope scope, String name) {
+            return ScopeEnvironment.mangle(getClassName(scope), name);
+        }
+
+        String getClassName(Scope s) {
+            Scope cur = s;
+            while (cur != null) {
+                if (cur.isClass()) {
+                    return cur.getName();
+                }
+                cur = scopeEnvironment.lookupParent(cur);
+            }
+            return null;
+        }
+
+        String getQualifiedName(Scope scope) {
+            if (qualifiedNames.containsKey(scope)) {
+                return qualifiedNames.get(scope);
+            } else {
+                String qualifiedName = computeQualifiedName(scope);
+                qualifiedNames.put(scope, qualifiedName);
+                return qualifiedName;
+            }
+        }
+
+        private String computeQualifiedName(Scope scope) {
+            String qualifiedName = scope.getName();
+            Scope parentScope = scopeEnvironment.lookupParent(scope);
+            if (parentScope != null && parentScope != scopeEnvironment.getTopScope()) {
+                if (!((scope.isFunction() || scope.isClass()) && parentScope.getUseOfName(mangle(scope, scope.getName())).contains(Scope.DefUse.GlobalExplicit))) {
+                    // Qualify the name, unless it's a function/class and the parent declared the
+                    // name as a global (in which case the function/class doesn't belong to the
+                    // parent).
+                    if (parentScope.isFunction()) {
+                        qualifiedName = getQualifiedName(parentScope) + ".." + scope.getName();
+                    } else {
+                        qualifiedName = getQualifiedName(parentScope) + "." + scope.getName();
+                    }
+                }
+            }
+
+            return qualifiedName;
+        }
+    }
+}
diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/compiler/bytecode_dsl/RootNodeCompiler.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/compiler/bytecode_dsl/RootNodeCompiler.java
new file mode 100644
index 0000000000..b8f330414e
--- /dev/null
+++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/compiler/bytecode_dsl/RootNodeCompiler.java
@@ -0,0 +1,4568 @@
+package com.oracle.graal.python.compiler.bytecode_dsl;
+
+import static com.oracle.graal.python.compiler.CompilationScope.Class;
+import static com.oracle.graal.python.util.PythonUtils.toTruffleStringUncached;
+import static com.oracle.graal.python.nodes.SpecialAttributeNames.J___CLASS__;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.EnumSet;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.function.BiConsumer;
+import java.util.function.Consumer;
+import java.util.function.Function;
+
+import com.oracle.graal.python.builtins.objects.PNone;
+import com.oracle.graal.python.builtins.objects.code.PCode;
+import com.oracle.graal.python.builtins.objects.ellipsis.PEllipsis;
+import com.oracle.graal.python.builtins.objects.function.PArguments;
+import com.oracle.graal.python.builtins.objects.function.PKeyword;
+import com.oracle.graal.python.compiler.CompilationScope;
+import com.oracle.graal.python.compiler.Compiler;
+import com.oracle.graal.python.compiler.Compiler.ConstantCollection;
+import com.oracle.graal.python.compiler.OpCodes.CollectionBits;
+import com.oracle.graal.python.compiler.bytecode_dsl.BytecodeDSLCompiler.BytecodeDSLCompilerContext;
+import com.oracle.graal.python.compiler.bytecode_dsl.BytecodeDSLCompiler.BytecodeDSLCompilerResult;
+import com.oracle.graal.python.compiler.Unparser;
+import com.oracle.graal.python.nodes.StringLiterals;
+import com.oracle.graal.python.nodes.bytecode_dsl.BytecodeDSLCodeUnit;
+import com.oracle.graal.python.nodes.bytecode_dsl.PBytecodeDSLRootNode;
+import com.oracle.graal.python.nodes.bytecode_dsl.PBytecodeDSLRootNodeGen;
+import com.oracle.graal.python.nodes.bytecode_dsl.PBytecodeDSLRootNodeGen.Builder;
+import com.oracle.graal.python.pegparser.FutureFeature;
+import com.oracle.graal.python.pegparser.ErrorCallback.ErrorType;
+import com.oracle.graal.python.pegparser.ErrorCallback.WarningType;
+import com.oracle.graal.python.pegparser.scope.Scope;
+import com.oracle.graal.python.pegparser.scope.Scope.DefUse;
+import com.oracle.graal.python.pegparser.sst.AliasTy;
+import com.oracle.graal.python.pegparser.sst.ArgTy;
+import com.oracle.graal.python.pegparser.sst.ArgumentsTy;
+import com.oracle.graal.python.pegparser.sst.BoolOpTy;
+import com.oracle.graal.python.pegparser.sst.CmpOpTy;
+import com.oracle.graal.python.pegparser.sst.ComprehensionTy;
+import com.oracle.graal.python.pegparser.sst.ConstantValue;
+import com.oracle.graal.python.pegparser.sst.ConstantValue.Kind;
+import com.oracle.graal.python.pegparser.sst.ExceptHandlerTy;
+import com.oracle.graal.python.pegparser.sst.ExprContextTy;
+import com.oracle.graal.python.pegparser.sst.ExprTy;
+import com.oracle.graal.python.pegparser.sst.KeywordTy;
+import com.oracle.graal.python.pegparser.sst.MatchCaseTy;
+import com.oracle.graal.python.pegparser.sst.ModTy;
+import com.oracle.graal.python.pegparser.sst.OperatorTy;
+import com.oracle.graal.python.pegparser.sst.PatternTy;
+import com.oracle.graal.python.pegparser.sst.SSTNode;
+import com.oracle.graal.python.pegparser.sst.StmtTy;
+import com.oracle.graal.python.pegparser.sst.UnaryOpTy;
+import com.oracle.graal.python.pegparser.sst.WithItemTy;
+import com.oracle.graal.python.pegparser.sst.ExprTy.Constant;
+import com.oracle.graal.python.pegparser.sst.ExprTy.DictComp;
+import com.oracle.graal.python.pegparser.sst.ExprTy.GeneratorExp;
+import com.oracle.graal.python.pegparser.sst.ExprTy.Lambda;
+import com.oracle.graal.python.pegparser.sst.ExprTy.ListComp;
+import com.oracle.graal.python.pegparser.sst.ExprTy.SetComp;
+import com.oracle.graal.python.pegparser.sst.StmtTy.AsyncFunctionDef;
+import com.oracle.graal.python.pegparser.tokenizer.SourceRange;
+import com.oracle.graal.python.util.PythonUtils;
+import com.oracle.truffle.api.CompilerDirectives;
+import com.oracle.truffle.api.bytecode.BytecodeConfig;
+import com.oracle.truffle.api.bytecode.BytecodeLabel;
+import com.oracle.truffle.api.bytecode.BytecodeLocal;
+import com.oracle.truffle.api.bytecode.BytecodeRootNodes;
+import com.oracle.truffle.api.instrumentation.StandardTags.StatementTag;
+import com.oracle.truffle.api.bytecode.BytecodeParser;
+import com.oracle.truffle.api.strings.TruffleString;
+
+/**
+ * Visitor that compiles a top-level AST (modules, functions, classes, etc.) to a root node.
+ * Produces a {@link BytecodeDSLCompilerResult}.
+ * 

+ * This visitor is a small wrapper that calls into another visitor, {@link StatementCompiler}, to + * produce bytecode for the various statements/expressions within the AST. + */ +public class RootNodeCompiler implements BaseBytecodeDSLVisitor { + /** + * Because a {@link RootNodeCompiler} instance gets reused on reparse, it should be idempotent. + * Consequently, most of its fields are final and immutable/not mutated after construction. For + * some tables updated during parsing (e.g., the constants map), we ensure these updates are + * idempotent. Any remaining fields must be {@link #reset()} at the beginning of the parse. + */ + // Immutable + private final BytecodeDSLCompilerContext ctx; + private final SSTNode startNode; + private final Scope scope; + private final CompilationScope scopeType; + private final boolean isInteractive; + private final EnumSet futureFeatures; + + // Immutable after construction + private final HashMap varnames; + private final HashMap cellvars; + private final HashMap freevars; + private final int[] cell2arg; + private final String selfCellName; + + // Updated idempotently + private final Map locals = new HashMap<>(); + private final Map cellLocals = new HashMap<>(); + private final Map freeLocals = new HashMap<>(); + private final HashMap constants = new HashMap<>(); + private final HashMap names = new HashMap<>(); + + // Mutable (must be reset) + private SourceRange currentLocation; + + public RootNodeCompiler(BytecodeDSLCompilerContext ctx, SSTNode rootNode, EnumSet futureFeatures) { + this.ctx = ctx; + this.startNode = rootNode; + this.scope = ctx.scopeEnvironment.lookupScope(rootNode); + this.scopeType = getScopeType(scope, rootNode); + this.isInteractive = rootNode instanceof ModTy.Interactive; + this.futureFeatures = futureFeatures; + + this.varnames = new HashMap<>(); + if (scope.isFunction()) { + /* + * scope.getVarnames only returns parameters. We use the scope to collect the rest of + * the regular variables. + */ + for (int i = 0; i < scope.getVarnames().size(); i++) { + varnames.put(scope.getVarnames().get(i), i); + } + varnames.putAll(scope.getSymbolsByType(EnumSet.of(DefUse.Local), EnumSet.of(DefUse.DefParam, DefUse.Cell, DefUse.Free), varnames.size())); + } + + this.cellvars = scope.getSymbolsByType(EnumSet.of(Scope.DefUse.Cell), 0); + if (scope.needsClassClosure()) { + assert scopeType == Class; + assert cellvars.isEmpty(); + cellvars.put("__class__", 0); + } + + this.freevars = scope.getSymbolsByType(EnumSet.of(Scope.DefUse.Free, Scope.DefUse.DefFreeClass), 0); + + int[] cell2argValue = new int[cellvars.size()]; + boolean hasArgCell = false; + Arrays.fill(cell2argValue, -1); + String selfCellNameValue = null; + for (String cellvar : cellvars.keySet()) { + if (varnames.containsKey(cellvar)) { + int argIndex = varnames.get(cellvar); + cell2argValue[cellvars.get(cellvar)] = argIndex; + hasArgCell = true; + if (argIndex == 0) { + assert selfCellNameValue == null; + selfCellNameValue = cellvar; + } + } + } + this.cell2arg = hasArgCell ? cell2argValue : null; + this.selfCellName = selfCellNameValue; + } + + private static CompilationScope getScopeType(Scope scope, SSTNode rootNode) { + if (scope.isModule()) { + return CompilationScope.Module; + } else if (scope.isClass()) { + return CompilationScope.Class; + } else if (scope.isFunction()) { + if (rootNode instanceof Lambda) { + return CompilationScope.Lambda; + } else if (rootNode instanceof AsyncFunctionDef) { + return CompilationScope.AsyncFunction; + } else { + return CompilationScope.Function; + } + } else { + assert rootNode instanceof DictComp || rootNode instanceof ListComp || rootNode instanceof SetComp || rootNode instanceof GeneratorExp; + return CompilationScope.Comprehension; + } + } + + private static U[] orderedKeys(HashMap map, U[] base, Function converter) { + U[] result = Arrays.copyOf(base, map.size()); + for (Map.Entry e : map.entrySet()) { + result[e.getValue()] = converter.apply(e.getKey()); + } + return result; + } + + private static T[] orderedKeys(HashMap map, T[] base) { + return orderedKeys(map, base, x -> x); + } + + private Object addConstant(Object c) { + Integer v = constants.get(c); + if (v == null) { + v = constants.size(); + constants.put(c, v); + } + return c; + } + + private static TruffleString[] orderedTruffleStringArray(HashMap map) { + return orderedKeys(map, new TruffleString[0], PythonUtils::toTruffleStringUncached); + } + + private BytecodeDSLCompilerResult compileRootNode(String name, ArgumentInfo argumentInfo, SourceRange sourceRange, BytecodeParser parser) { + BytecodeRootNodes nodes = PBytecodeDSLRootNodeGen.create(ctx.language, BytecodeConfig.WITH_SOURCE, parser); + List nodeList = nodes.getNodes(); + assert nodeList.size() == 1; + PBytecodeDSLRootNode rootNode = nodeList.get(0); + + int flags = PCode.CO_OPTIMIZED | PCode.CO_NEWLOCALS; + flags |= argumentInfo.takesVarArgs ? PCode.CO_VARARGS : 0; + flags |= argumentInfo.takesVarKeywordArgs ? PCode.CO_VARKEYWORDS : 0; + if (scope.isNested()) { + flags |= PCode.CO_NESTED; + } + if (scope.isModule()) { + flags |= PCode.CO_GRAALPYHON_MODULE; + } + if (scope.isGenerator() && scope.isCoroutine()) { + flags |= PCode.CO_ASYNC_GENERATOR; + } else if (scope.isGenerator()) { + flags |= PCode.CO_GENERATOR; + } else if (scope.isCoroutine()) { + flags |= PCode.CO_COROUTINE; + } + for (FutureFeature flag : futureFeatures) { + flags |= flag.flagValue; + } + + int classcellIndex = -1; + if (freeLocals.containsKey(J___CLASS__)) { + classcellIndex = freeLocals.get(J___CLASS__).getLocalOffset(); + } + + int selfIndex = -1; + if (argumentInfo.nonEmpty()) { + selfIndex = 0; + if (selfCellName != null) { + selfIndex = cellLocals.get(selfCellName).getLocalOffset(); + } + } + + BytecodeDSLCodeUnit codeUnit = new BytecodeDSLCodeUnit(toTruffleStringUncached(name), toTruffleStringUncached(ctx.getQualifiedName(scope)), + argumentInfo.argCount, argumentInfo.kwOnlyArgCount, argumentInfo.positionalOnlyArgCount, + flags, orderedTruffleStringArray(names), + orderedTruffleStringArray(varnames), + orderedTruffleStringArray(cellvars), + orderedTruffleStringArray(freevars), + cell2arg, + orderedKeys(constants, new Object[0]), + sourceRange.startLine, + sourceRange.startColumn, + sourceRange.endLine, + sourceRange.endColumn, + classcellIndex, + selfIndex, + null, + nodes); + rootNode.setMetadata(codeUnit, ctx.errorCallback); + return new BytecodeDSLCompilerResult(rootNode, codeUnit); + } + + private static class ArgumentInfo { + static final ArgumentInfo NO_ARGS = new ArgumentInfo(0, 0, 0, false, false); + + final int argCount; + final int positionalOnlyArgCount; + final int kwOnlyArgCount; + final boolean takesVarArgs; + final boolean takesVarKeywordArgs; + + ArgumentInfo(int argCount, int positionalOnlyArgCount, int kwOnlyArgCount, boolean takesVarArgs, boolean takesVarKeywordArgs) { + this.argCount = argCount; + this.positionalOnlyArgCount = positionalOnlyArgCount; + this.kwOnlyArgCount = kwOnlyArgCount; + this.takesVarArgs = takesVarArgs; + this.takesVarKeywordArgs = takesVarKeywordArgs; + } + + static ArgumentInfo fromArguments(ArgumentsTy args) { + int argc, pargc, kwargc; + boolean splat, kwSplat; + if (args == null) { + argc = pargc = kwargc = 0; + splat = kwSplat = false; + } else { + argc = args.args == null ? 0 : args.args.length; + pargc = args.posOnlyArgs == null ? 0 : args.posOnlyArgs.length; + kwargc = args.kwOnlyArgs == null ? 0 : args.kwOnlyArgs.length; + splat = args.varArg != null; + kwSplat = args.kwArg != null; + } + return new ArgumentInfo(argc, pargc, kwargc, splat, kwSplat); + } + + private boolean nonEmpty() { + return argCount + positionalOnlyArgCount + kwOnlyArgCount > 0 || takesVarArgs || takesVarKeywordArgs; + } + } + + protected final void checkForbiddenName(String id, NameOperation context) { + if (context == NameOperation.BeginWrite) { + if (id.equals("__debug__")) { + ctx.errorCallback.onError(ErrorType.Syntax, currentLocation, "cannot assign to __debug__"); + } + } + if (context == NameOperation.Delete) { + if (id.equals("__debug__")) { + ctx.errorCallback.onError(ErrorType.Syntax, currentLocation, "cannot delete __debug__"); + } + } + } + + private void checkForbiddenArgs(ArgumentsTy args) { + if (args != null) { + if (args.posOnlyArgs != null) { + for (ArgTy arg : args.posOnlyArgs) { + checkForbiddenName(arg.arg, NameOperation.BeginWrite); + } + } + if (args.args != null) { + for (ArgTy arg : args.args) { + checkForbiddenName(arg.arg, NameOperation.BeginWrite); + } + } + if (args.kwOnlyArgs != null) { + for (ArgTy arg : args.kwOnlyArgs) { + checkForbiddenName(arg.arg, NameOperation.BeginWrite); + } + } + if (args.varArg != null) { + checkForbiddenName(args.varArg.arg, NameOperation.BeginWrite); + } + if (args.kwArg != null) { + checkForbiddenName(args.kwArg.arg, NameOperation.BeginWrite); + } + } + } + + private boolean containsAnnotations(StmtTy[] stmts) { + if (stmts == null) { + return false; + } + for (StmtTy stmt : stmts) { + if (containsAnnotations(stmt)) { + return true; + } + } + return false; + } + + private boolean containsAnnotations(StmtTy stmt) { + if (stmt instanceof StmtTy.AnnAssign) { + return true; + } else if (stmt instanceof StmtTy.For) { + return containsAnnotations(((StmtTy.For) stmt).body) || containsAnnotations(((StmtTy.For) stmt).orElse); + } else if (stmt instanceof StmtTy.While) { + return containsAnnotations(((StmtTy.While) stmt).body) || containsAnnotations(((StmtTy.While) stmt).orElse); + } else if (stmt instanceof StmtTy.If) { + return containsAnnotations(((StmtTy.If) stmt).body) || containsAnnotations(((StmtTy.If) stmt).orElse); + } else if (stmt instanceof StmtTy.With) { + return containsAnnotations(((StmtTy.With) stmt).body); + } else if (stmt instanceof StmtTy.Try) { + StmtTy.Try tryStmt = (StmtTy.Try) stmt; + if (tryStmt.handlers != null) { + for (ExceptHandlerTy h : tryStmt.handlers) { + if (containsAnnotations(((ExceptHandlerTy.ExceptHandler) h).body)) { + return true; + } + } + } + return containsAnnotations(tryStmt.body) || containsAnnotations(tryStmt.finalBody) || containsAnnotations(tryStmt.orElse); + } + return false; + } + + private static final class ParamAnnotation { + final TruffleString name; + final ExprTy annotation; + + ParamAnnotation(TruffleString name, ExprTy annotation) { + this.name = name; + this.annotation = annotation; + } + } + + private List collectParamAnnotations(ArgumentsTy args, ExprTy returns) { + List result = new ArrayList<>(); + if (args != null) { + visitParamAnnotations(result, args.args); + visitParamAnnotations(result, args.posOnlyArgs); + if (args.varArg != null) { + visitParamAnnotation(result, args.varArg.arg, args.varArg.annotation); + } + visitParamAnnotations(result, args.kwOnlyArgs); + if (args.kwArg != null) { + visitParamAnnotation(result, args.kwArg.arg, args.kwArg.annotation); + } + } + visitParamAnnotation(result, "return", returns); + return result; + } + + private void visitParamAnnotations(List result, ArgTy[] args) { + for (int i = 0; i < args.length; i++) { + visitParamAnnotation(result, args[i].arg, args[i].annotation); + } + } + + private void visitParamAnnotation(List result, String name, ExprTy annotation) { + if (annotation != null) { + String mangled = mangle(name); + result.add(new ParamAnnotation(toTruffleStringUncached(mangled), annotation)); + } + } + + public BytecodeDSLCompilerResult compile() { + return startNode.accept(this); + } + + public void reset() { + this.currentLocation = null; + } + + // -------------- helpers -------------- + + void beginRootNode(SSTNode node, ArgumentsTy args, Builder b) { + reset(); + b.beginSource(ctx.source); + beginRootSourceSection(node, b); + + b.beginRoot(); + + checkForbiddenArgs(args); + setUpFrame(args, b); + + b.emitTraceOrProfileCall(); + } + + void endRootNode(Builder b) { + b.endRoot(); + endRootSourceSection(b); + b.endSource(); + } + + /** + * Opens a new SourceSection operation. Emits TraceLine and starts a new Tag(Statement) if this + * location has a different line from the previous location. + *

+ * Returns whether this call opened a new Tag(Statement). The result should be passed to the + * corresponding {@link #endSourceSection} call to ensure the Tag is closed. + */ + boolean beginSourceSection(SSTNode node, Builder b) { + SourceRange sourceRange = node.getSourceRange(); + SourceRange oldSourceRange = this.currentLocation; + this.currentLocation = sourceRange; + + if (ctx.source.hasCharacters()) { + int startOffset = getStartOffset(sourceRange); + int endOffset = getEndOffset(sourceRange); + int length = endOffset - startOffset; + if (length == 0) { + startOffset = 0; + } + b.beginSourceSection(startOffset, length); + + if (oldSourceRange == null || oldSourceRange.startLine != sourceRange.startLine) { + b.beginTag(StatementTag.class); + b.beginBlock(); + b.emitTraceLine(sourceRange.startLine); + return true; + } + } + return false; + } + + /** + * Same as {@link #beginSourceSection(SSTNode, Builder)}, but does not emit tags or trace events + * (since the root has not been started yet). Avoids setting {@link #currentLocation} so that + * {{@link #beginSourceSection(SSTNode, Builder)} will emit a TraceLine for a statement on the + * first line. + */ + void beginRootSourceSection(SSTNode node, Builder b) { + SourceRange sourceRange = node.getSourceRange(); + + if (ctx.source.hasCharacters()) { + int startOffset = getStartOffset(sourceRange); + int endOffset = getEndOffset(sourceRange); + int length = endOffset - startOffset; + if (length == 0) { + startOffset = 0; + } + b.beginSourceSection(startOffset, length); + } + } + + void endSourceSection(Builder b, boolean closeTag) { + if (ctx.source.hasCharacters()) { + if (closeTag) { + b.endBlock(); + b.endTag(StatementTag.class); + } + b.endSourceSection(); + } + } + + void endRootSourceSection(Builder b) { + if (ctx.source.hasCharacters()) { + b.endSourceSection(); + } + } + + int getStartOffset(SourceRange sourceRange) { + return ctx.source.getLineStartOffset(sourceRange.startLine) + sourceRange.startColumn; + } + + int getEndOffset(SourceRange sourceRange) { + return ctx.source.getLineStartOffset(sourceRange.endLine) + sourceRange.endColumn; + } + + void beginReturn(Builder b) { + b.beginReturn(); + b.beginTraceOrProfileReturn(); + } + + void endReturn(Builder b) { + b.endTraceOrProfileReturn(); + b.endReturn(); + } + + // --------------------- visitor --------------------------- + + @Override + public BytecodeDSLCompilerResult visit(ModTy.Module node) { + return compileRootNode("", ArgumentInfo.NO_ARGS, node.getSourceRange(), b -> { + beginRootNode(node, null, b); + visitModuleBody(node.body, b); + endRootNode(b); + }); + } + + @Override + public BytecodeDSLCompilerResult visit(ModTy.Expression node) { + return compileRootNode("", ArgumentInfo.NO_ARGS, node.getSourceRange(), b -> { + beginRootNode(node, null, b); + beginReturn(b); + new StatementCompiler(b).visitNode(node.body); + endReturn(b); + endRootNode(b); + }); + } + + @Override + public BytecodeDSLCompilerResult visit(ModTy.Interactive node) { + return compileRootNode("", ArgumentInfo.NO_ARGS, node.getSourceRange(), b -> { + beginRootNode(node, null, b); + visitModuleBody(node.body, b); + endRootNode(b); + }); + } + + private void visitModuleBody(StmtTy[] body, Builder b) { + if (body != null) { + if (containsAnnotations(body)) { + b.emitSetupAnnotations(); + } + + StatementCompiler statementCompiler = new StatementCompiler(b); + if (isInteractive) { + for (int i = 0; i < body.length; i++) { + StmtTy bodyNode = body[i]; + if (i == body.length - 1) { + bodyNode.accept(statementCompiler); + + // For interactive code, always return None. + beginReturn(b); + b.emitLoadConstant(PNone.NONE); + endReturn(b); + } else { + bodyNode.accept(statementCompiler); + } + } + } else { + int i = 0; + TruffleString docstring = getDocstring(body); + if (docstring != null) { + /* + * Skip over the docstring so it does not get evaluated (and registered as a + * constant) for higher optimization levels. We manually add it as a constant + * for lower levels. + */ + i++; + if (ctx.optimizationLevel < 2) { + beginStoreLocal("__doc__", b); + emitPythonConstant(docstring, b); + endStoreLocal("__doc__", b); + } + } + if (i == body.length) { + // Special case: module body just consists of a docstring. + beginReturn(b); + b.emitLoadConstant(PNone.NONE); + endReturn(b); + return; + } + + for (; i < body.length; i++) { + StmtTy bodyNode = body[i]; + if (i == body.length - 1) { + if (bodyNode instanceof StmtTy.Expr expr) { + // Return the value of the last statement for interop eval. + beginReturn(b); + expr.value.accept(statementCompiler); + endReturn(b); + } else { + bodyNode.accept(statementCompiler); + beginReturn(b); + b.emitLoadConstant(PNone.NONE); + endReturn(b); + } + } else { + bodyNode.accept(statementCompiler); + } + } + } + } else { + beginReturn(b); + b.emitLoadConstant(PNone.NONE); + endReturn(b); + } + } + + private static TruffleString getDocstring(StmtTy[] body) { + if (body != null && body.length > 0) { + StmtTy stmt = body[0]; + if (stmt instanceof StmtTy.Expr expr // + && expr.value instanceof ExprTy.Constant constant // + && constant.value.kind == ConstantValue.Kind.RAW) { + return constant.value.getRaw(TruffleString.class); + } + } + return null; + } + + @Override + public BytecodeDSLCompilerResult visit(StmtTy.FunctionDef node) { + return compileRootNode(node.name, ArgumentInfo.fromArguments(node.args), node.getSourceRange(), + b -> emitFunctionDef(node, node.args, node.body, b, getDocstring(node.body), false)); + } + + @Override + public BytecodeDSLCompilerResult visit(StmtTy.AsyncFunctionDef node) { + return compileRootNode(node.name, ArgumentInfo.fromArguments(node.args), node.getSourceRange(), + b -> emitFunctionDef(node, node.args, node.body, b, getDocstring(node.body), false)); + } + + @Override + public BytecodeDSLCompilerResult visit(ExprTy.Lambda node) { + return compileRootNode("", ArgumentInfo.fromArguments(node.args), node.getSourceRange(), + b -> emitFunctionDef(node, node.args, new SSTNode[]{node.body}, b, null, !scope.isGenerator())); + } + + private void emitFunctionDef(SSTNode node, ArgumentsTy args, SSTNode[] body, Builder b, Object docstring, boolean isRegularLambda) { + beginRootNode(node, args, b); + + int i = 0; + if (docstring != null) { + i++; + if (ctx.optimizationLevel < 2) { + addConstant(docstring); + } else { + addConstant(PNone.NONE); + } + } else { + addConstant(PNone.NONE); + } + + StatementCompiler statementCompiler = new StatementCompiler(b); + + if (isRegularLambda) { + assert i == 0; + assert body[0] instanceof ExprTy; + beginReturn(b); + body[0].accept(statementCompiler); + endReturn(b); + } else { + for (; i < body.length; i++) { + body[i].accept(statementCompiler); + } + beginReturn(b); + emitPythonConstant(PNone.NONE, b); + endReturn(b); + } + + endRootNode(b); + } + + @Override + public BytecodeDSLCompilerResult visit(StmtTy.ClassDef node) { + return compileRootNode(node.name, ArgumentInfo.NO_ARGS, node.getSourceRange(), b -> { + beginRootNode(node, null, b); + + beginStoreLocal("__module__", b); + emitReadLocal("__name__", b); + endStoreLocal("__module__", b); + + beginStoreLocal("__qualname__", b); + emitPythonConstant(toTruffleStringUncached(ctx.getQualifiedName(scope)), b); + endStoreLocal("__qualname__", b); + + if (containsAnnotations(node.body)) { + b.emitSetupAnnotations(); + } + + int i = 0; + TruffleString docstring = getDocstring(node.body); + if (docstring != null) { + i++; + if (ctx.optimizationLevel < 2) { + beginStoreLocal("__doc__", b); + emitPythonConstant(docstring, b); + endStoreLocal("__doc__", b); + } + } + + StatementCompiler statementCompiler = new StatementCompiler(b); + for (; i < node.body.length; i++) { + node.body[i].accept(statementCompiler); + } + + if (scope.needsClassClosure()) { + beginStoreLocal("__classcell__", b); + b.emitLoadLocal(cellLocals.get("__class__")); + endStoreLocal("__classcell__", b); + + beginReturn(b); + b.emitLoadLocal(cellLocals.get("__class__")); + endReturn(b); + } else { + beginReturn(b); + b.emitLoadConstant(PNone.NONE); + endReturn(b); + } + + endRootNode(b); + }); + } + + private boolean beginComprehension(ComprehensionTy comp, int index, Builder b) { + boolean newStatement = beginSourceSection(comp, b); + + BytecodeLocal localIter = b.createLocal(); + BytecodeLocal localValue = b.createLocal(); + StatementCompiler statementCompiler = new StatementCompiler(b); + + b.beginStoreLocal(localIter); + b.beginGetIter(); + if (index == 0) { + b.emitLoadArgument(PArguments.USER_ARGUMENTS_OFFSET); + } else { + comp.iter.accept(statementCompiler); + } + b.endGetIter(); + b.endStoreLocal(); + + b.beginWhile(); + + b.beginBlock(); + b.emitTraceLineAtLoopHeader(currentLocation.startLine); + b.beginForIterate(localValue); + b.emitLoadLocal(localIter); + b.endForIterate(); + b.endBlock(); + + b.beginBlock(); + + comp.target.accept(statementCompiler.new StoreVisitor(() -> b.emitLoadLocal(localValue))); + + if (comp.ifs != null) { + for (int i = 0; i < comp.ifs.length; i++) { + b.beginIfThen(); + statementCompiler.visitCondition(comp.ifs[i]); + b.beginBlock(); + } + } + + return newStatement; + } + + private void endComprehension(ComprehensionTy comp, Builder b, boolean newStatement) { + if (comp.ifs != null) { + for (int i = 0; i < len(comp.ifs); i++) { + b.endBlock(); + b.endIfThen(); + } + } + + b.endBlock(); + b.endWhile(); + + endSourceSection(b, newStatement); + } + + private BytecodeDSLCompilerResult buildComprehensionCodeUnit(SSTNode node, ComprehensionTy[] generators, String name, + Consumer emptyCollectionProducer, + BiConsumer accumulateProducer) { + return compileRootNode(name, new ArgumentInfo(1, 0, 0, false, false), node.getSourceRange(), b -> { + beginRootNode(node, null, b); + + StatementCompiler statementCompiler = new StatementCompiler(b); + boolean isGenerator = emptyCollectionProducer == null; + BytecodeLocal collectionLocal = null; + if (!isGenerator) { + collectionLocal = b.createLocal(); + b.beginStoreLocal(collectionLocal); + emptyCollectionProducer.accept(statementCompiler); + b.endStoreLocal(); + } + + boolean[] newStatement = new boolean[generators.length]; + for (int i = 0; i < generators.length; i++) { + newStatement[i] = beginComprehension(generators[i], i, b); + } + accumulateProducer.accept(statementCompiler, collectionLocal); + for (int i = generators.length - 1; i >= 0; i--) { + endComprehension(generators[i], b, newStatement[i]); + } + + beginReturn(b); + if (isGenerator) { + b.emitLoadConstant(PNone.NONE); + } else { + b.emitLoadLocal(collectionLocal); + } + endReturn(b); + + endRootNode(b); + }); + } + + @Override + public BytecodeDSLCompilerResult visit(ExprTy.ListComp node) { + return buildComprehensionCodeUnit(node, node.generators, "", + (statementCompiler) -> statementCompiler.b.emitMakeEmptyList(), + (statementCompiler, collection) -> { + statementCompiler.b.beginListAppend(); + statementCompiler.b.emitLoadLocal(collection); + node.element.accept(statementCompiler); + statementCompiler.b.endListAppend(); + }); + } + + @Override + public BytecodeDSLCompilerResult visit(ExprTy.DictComp node) { + return buildComprehensionCodeUnit(node, node.generators, "", + (statementCompiler) -> statementCompiler.b.emitMakeEmptyDict(), + (statementCompiler, collection) -> { + statementCompiler.b.beginSetDictItem(); + statementCompiler.b.emitLoadLocal(collection); + node.key.accept(statementCompiler); + node.value.accept(statementCompiler); + statementCompiler.b.endSetDictItem(); + }); + } + + @Override + public BytecodeDSLCompilerResult visit(ExprTy.SetComp node) { + return buildComprehensionCodeUnit(node, node.generators, "", + (statementCompiler) -> statementCompiler.b.emitMakeEmptySet(), + (statementCompiler, collection) -> { + statementCompiler.b.beginSetAdd(); + statementCompiler.b.emitLoadLocal(collection); + node.element.accept(statementCompiler); + statementCompiler.b.endSetAdd(); + }); + } + + @Override + public BytecodeDSLCompilerResult visit(ExprTy.GeneratorExp node) { + return buildComprehensionCodeUnit(node, node.generators, "", + null, + (statementCompiler, collection) -> emitYield((statementCompiler_) -> node.element.accept(statementCompiler_), statementCompiler)); + } + + enum NameOperation { + Read, + BeginWrite, + EndWrite, + Delete + } + + private String mangle(String name) { + return ctx.mangle(scope, name); + } + + private void emitNotImplemented(String what, Builder b) { + b.beginRaiseNotImplementedError(); + emitPythonConstant(toTruffleStringUncached(what), b); + b.endRaiseNotImplementedError(); + } + + /** + * Use this method for values that should show up in co_consts. + */ + private void emitPythonConstant(Object constant, Builder b) { + b.emitLoadConstant(addConstant(constant)); + } + + /** + * This helper encapsulates all of the logic needed to yield and resume. Yields should not be + * emitted directly. + */ + private static void emitYield(Consumer yieldValueProducer, StatementCompiler statementCompiler) { + statementCompiler.b.beginResumeYield(); + statementCompiler.b.beginYield(); + statementCompiler.b.beginPreYield(); + yieldValueProducer.accept(statementCompiler); + statementCompiler.b.endPreYield(); + statementCompiler.b.endYield(); + statementCompiler.b.endResumeYield(); + } + + private void emitNameCellOperation(String mangled, NameOperation op, Builder b) { + int index; + BytecodeLocal local; + if (freevars.containsKey(mangled)) { + index = freevars.get(mangled) + cellvars.size(); + local = freeLocals.get(mangled); + } else { + index = cellvars.get(mangled); + local = cellLocals.get(mangled); + } + + switch (op) { + case Read: + if (scope.isClass()) { + b.beginClassLoadCell(index); + b.emitLoadLocal(local); + b.endClassLoadCell(); + } else { + b.beginLoadCell(index); + b.emitLoadLocal(local); + b.endLoadCell(); + } + break; + case Delete: + b.beginClearCell(index); + b.emitLoadLocal(local); + b.endClearCell(); + break; + case BeginWrite: + b.beginStoreCell(); + b.emitLoadLocal(local); + break; + case EndWrite: + b.endStoreCell(); + break; + default: + throw new UnsupportedOperationException("unknown value: " + op); + } + } + + private void emitNameFastOperation(String mangled, NameOperation op, Builder b) { + BytecodeLocal local = locals.get(mangled); + switch (op) { + case Read: + b.beginCheckUnboundLocal(varnames.get(mangled)); + b.emitLoadLocal(local); + b.endCheckUnboundLocal(); + break; + case Delete: + b.beginBlock(); + b.beginCheckUnboundLocal(varnames.get(mangled)); + b.emitLoadLocal(local); + b.endCheckUnboundLocal(); + + b.beginStoreLocal(local); + b.emitLoadNull(); + b.endStoreLocal(); + b.endBlock(); + break; + case BeginWrite: + if (local == null) { + throw new NullPointerException("local " + mangled + " not defined"); + } + b.beginStoreLocal(local); + break; + case EndWrite: + b.endStoreLocal(); + break; + default: + throw new UnsupportedOperationException("unknown value: " + op); + } + } + + private void emitNameGlobalOperation(String name, NameOperation op, Builder b) { + assert locals.get(name) == null; + names.putIfAbsent(name, names.size()); + TruffleString tsName = toTruffleStringUncached(name); + switch (op) { + case Read: + b.emitReadGlobal(tsName); + break; + case Delete: + b.emitDeleteGlobal(tsName); + break; + case BeginWrite: + b.beginWriteGlobal(tsName); + break; + case EndWrite: + b.endWriteGlobal(); + break; + default: + throw new UnsupportedOperationException("unknown value: " + op); + } + } + + private void emitNameSlowOperation(String name, NameOperation op, Builder b) { + assert locals.get(name) == null; + names.putIfAbsent(name, names.size()); + TruffleString tsName = toTruffleStringUncached(name); + switch (op) { + case Read: + b.emitReadName(tsName); + break; + case Delete: + b.emitDeleteName(tsName); + break; + case BeginWrite: + b.beginWriteName(tsName); + break; + case EndWrite: + b.endWriteName(); + break; + default: + throw new UnsupportedOperationException("unknown value: " + op); + } + } + + private void emitNameOperation(String name, NameOperation op, Builder b) { + checkForbiddenName(name, op); + + String mangled = mangle(name); + EnumSet uses = scope.getUseOfName(mangled); + + if (uses != null) { + if (uses.contains(DefUse.Free)) { + assert freevars.containsKey(mangled) : String.format("scope analysis did not mark %s as a free variable", mangled); + emitNameCellOperation(mangled, op, b); + return; + } else if (uses.contains(DefUse.Cell)) { + assert cellvars.containsKey(mangled) : String.format("scope analysis did not mark %s as a cell variable", mangled); + emitNameCellOperation(mangled, op, b); + return; + } else if (uses.contains(DefUse.Local)) { + if (scope.isFunction()) { + assert varnames.containsKey(mangled) : String.format("scope analysis did not mark %s as a regular variable", mangled); + emitNameFastOperation(mangled, op, b); + return; + } + } else if (uses.contains(DefUse.GlobalImplicit)) { + if (scope.isFunction()) { + emitNameGlobalOperation(mangled, op, b); + return; + } + } else if (uses.contains(DefUse.GlobalExplicit)) { + emitNameGlobalOperation(mangled, op, b); + return; + } + } + emitNameSlowOperation(mangled, op, b); + } + + private void emitReadLocal(String name, Builder b) { + emitNameOperation(name, NameOperation.Read, b); + } + + private void emitDelLocal(String name, Builder b) { + emitNameOperation(name, NameOperation.Delete, b); + } + + private void beginStoreLocal(String name, Builder b) { + emitNameOperation(name, NameOperation.BeginWrite, b); + } + + private void endStoreLocal(String name, Builder b) { + emitNameOperation(name, NameOperation.EndWrite, b); + } + + private BytecodeLocal getLocal(String name) { + return locals.get(mangle(name)); + } + + public void setUpFrame(ArgumentsTy args, Builder b) { + /** + * This method does two things: + * + * 1. It allocates a contiguous region in the frame for Python variables. Some nodes in the + * GraalPy AST expect locals to be allocated contiguously starting at index 0. The resultant + * frame has the following layout: + * + * [var1, var2, ..., cell1, cell2, ..., free1, free2, ..., temp1, temp2, ..., stack] + * + * The temp variables are allocated elsewhere during compilation (e.g., to store an + * intermediate computation) and the stack space is automatically reserved by the DSL. + * + * 2. It emits code to copy arguments, initialize cells, and copy free variables. + */ + + // 1. Allocate space in the frame. + if (scope.isFunction()) { + String[] regularVariables = orderedKeys(varnames, new String[0]); + for (int i = 0; i < regularVariables.length; i++) { + locals.put(regularVariables[i], b.createLocal()); + } + } + + String[] cellVariables = orderedKeys(cellvars, new String[0]); + BytecodeLocal[] cellVariableLocals = new BytecodeLocal[cellVariables.length]; + for (int i = 0; i < cellVariables.length; i++) { + BytecodeLocal local = b.createLocal(); + cellLocals.put(cellVariables[i], local); + cellVariableLocals[i] = local; + } + + String[] freeVariables = orderedKeys(freevars, new String[0]); + BytecodeLocal[] freeVariableLocals = new BytecodeLocal[freeVariables.length]; + for (int i = 0; i < freeVariables.length; i++) { + BytecodeLocal local = b.createLocal(); + freeLocals.put(freeVariables[i], local); + freeVariableLocals[i] = local; + } + + // 2. Copy arguments, initialize cells, and copy free variables. + copyArguments(args, b); + + if (cellVariableLocals.length > 0) { + List toClear = new ArrayList<>(); + + b.beginStoreRange(cellVariableLocals); + b.beginMakeVariadic(); + for (int i = 0; i < cellVariableLocals.length; i++) { + b.beginCreateCell(); + if (scope.getUseOfName(cellVariables[i]).contains(DefUse.DefParam)) { + /* + * To simplify the argument copying performed above, we copy cell params into + * regular locals just like all other arguments. Then, here we move the value + * into a cell and clear the regular local. + */ + BytecodeLocal param = getLocal(cellVariables[i]); + b.emitLoadLocal(param); + toClear.add(param); + } else { + b.emitLoadNull(); + } + b.endCreateCell(); + } + b.endMakeVariadic(); + b.endStoreRange(); + + for (BytecodeLocal local : toClear) { + b.emitClearLocal(local); + } + } + + if (freeVariableLocals.length > 0) { + b.beginStoreRange(freeVariableLocals); + b.emitLoadClosure(); + b.endStoreRange(); + } + } + + private void copyArguments(ArgumentsTy args, Builder b) { + if (args == null) { + return; + } + + int argIdx = PArguments.USER_ARGUMENTS_OFFSET; + if (args.posOnlyArgs != null) { + for (int i = 0; i < args.posOnlyArgs.length; i++) { + BytecodeLocal local = getLocal(args.posOnlyArgs[i].arg); + assert local != null; + b.beginStoreLocal(local); + b.emitLoadArgument(argIdx++); + b.endStoreLocal(); + } + } + + if (args.args != null) { + for (int i = 0; i < args.args.length; i++) { + BytecodeLocal local = getLocal(args.args[i].arg); + assert local != null; + b.beginStoreLocal(local); + b.emitLoadArgument(argIdx++); + b.endStoreLocal(); + } + } + + if (args.kwOnlyArgs != null) { + for (int i = 0; i < args.kwOnlyArgs.length; i++) { + BytecodeLocal local = getLocal(args.kwOnlyArgs[i].arg); + assert local != null; + b.beginStoreLocal(local); + b.emitLoadArgument(argIdx++); + b.endStoreLocal(); + } + } + + if (args.varArg != null) { + BytecodeLocal local = getLocal(args.varArg.arg); + assert local != null; + b.beginStoreLocal(local); + b.emitLoadVariableArguments(); + b.endStoreLocal(); + } + + if (args.kwArg != null) { + BytecodeLocal local = getLocal(args.kwArg.arg); + assert local != null; + b.beginStoreLocal(local); + b.emitLoadKeywordArguments(); + b.endStoreLocal(); + } + } + + private static int len(T[] arr) { + return arr == null ? 0 : arr.length; + } + + /* ---------------- StatementCompiler -------------------- */ + + public class StatementCompiler implements BaseBytecodeDSLVisitor { + private final Builder b; + + private BytecodeLabel breakLabel; + private BytecodeLabel continueLabel; + + public StatementCompiler(Builder b) { + this.b = b; + } + + // --------------------- visitor --------------------------- + + @Override + public Void visit(AliasTy node) { + throw new UnsupportedOperationException("" + node.getClass()); + } + + @Override + public Void visit(ArgTy node) { + throw new UnsupportedOperationException("" + node.getClass()); + } + + @Override + public Void visit(ArgumentsTy node) { + throw new UnsupportedOperationException("" + node.getClass()); + } + + @Override + public Void visit(ComprehensionTy node) { + throw new UnsupportedOperationException("" + node.getClass()); + } + + @Override + public Void visit(ExprTy.Attribute node) { + boolean newStatement = beginSourceSection(node, b); + + b.beginGetAttribute(toTruffleStringUncached(mangle(node.attr))); + node.value.accept(this); + b.endGetAttribute(); + + endSourceSection(b, newStatement); + + return null; + } + + @Override + public Void visit(ExprTy.Await node) { + if (!scope.isFunction()) { + ctx.errorCallback.onError(ErrorType.Syntax, currentLocation, "'await' outside function"); + } + if (scopeType != CompilationScope.AsyncFunction && scopeType != CompilationScope.Comprehension) { + ctx.errorCallback.onError(ErrorType.Syntax, currentLocation, "'await' outside async function"); + } + boolean newStatement = beginSourceSection(node, b); + emitAwait(() -> node.value.accept(this)); + endSourceSection(b, newStatement); + return null; + } + + @Override + public Void visit(ExprTy.BinOp node) { + boolean newStatement = beginSourceSection(node, b); + switch (node.op) { + case Add: + b.beginAdd(); + node.left.accept(this); + node.right.accept(this); + b.endAdd(); + break; + case BitAnd: + b.beginBitAnd(); + node.left.accept(this); + node.right.accept(this); + b.endBitAnd(); + break; + case BitOr: + b.beginBitOr(); + node.left.accept(this); + node.right.accept(this); + b.endBitOr(); + break; + case BitXor: + b.beginBitXor(); + node.left.accept(this); + node.right.accept(this); + b.endBitXor(); + break; + case Div: + b.beginTrueDiv(); + node.left.accept(this); + node.right.accept(this); + b.endTrueDiv(); + break; + case FloorDiv: + b.beginFloorDiv(); + node.left.accept(this); + node.right.accept(this); + b.endFloorDiv(); + break; + case LShift: + b.beginLShift(); + node.left.accept(this); + node.right.accept(this); + b.endLShift(); + break; + case MatMult: + b.beginMatMul(); + node.left.accept(this); + node.right.accept(this); + b.endMatMul(); + break; + case Mod: + b.beginMod(); + node.left.accept(this); + node.right.accept(this); + b.endMod(); + break; + case Mult: + b.beginMul(); + node.left.accept(this); + node.right.accept(this); + b.endMul(); + break; + case Pow: + b.beginPow(); + node.left.accept(this); + node.right.accept(this); + b.endPow(); + break; + case RShift: + b.beginRShift(); + node.left.accept(this); + node.right.accept(this); + b.endRShift(); + break; + case Sub: + b.beginSub(); + node.left.accept(this); + node.right.accept(this); + b.endSub(); + break; + default: + throw new UnsupportedOperationException("" + node.getClass()); + } + + endSourceSection(b, newStatement); + return null; + } + + @Override + public Void visit(ExprTy.BoolOp node) { + boolean newStatement = beginSourceSection(node, b); + + if (node.op == BoolOpTy.And) { + b.beginBoolAnd(); + } else { + b.beginBoolOr(); + } + + visitSequence(node.values); + + if (node.op == BoolOpTy.And) { + b.endBoolAnd(); + } else { + b.endBoolOr(); + } + + endSourceSection(b, newStatement); + return null; + } + + private static boolean anyIsStarred(SSTNode[] nodes) { + for (int i = 0; i < nodes.length; i++) { + if (nodes[i] instanceof ExprTy.Starred) { + return true; + } + } + + return false; + } + + protected final void validateKeywords(KeywordTy[] keywords) { + for (int i = 0; i < keywords.length; i++) { + if (keywords[i].arg != null) { + checkForbiddenName(keywords[i].arg, NameOperation.BeginWrite); + for (int j = i + 1; j < keywords.length; j++) { + if (keywords[i].arg.equals(keywords[j].arg)) { + ctx.errorCallback.onError(ErrorType.Syntax, currentLocation, "keyword argument repeated: " + keywords[i].arg); + } + } + } + } + } + + private static boolean isAttributeLoad(ExprTy node) { + return node instanceof ExprTy.Attribute && ((ExprTy.Attribute) node).context == ExprContextTy.Load; + } + + private static final int NUM_ARGS_MAX_FIXED = 4; + + private void emitCall(ExprTy func, ExprTy[] args, KeywordTy[] keywords) { + validateKeywords(keywords); + + boolean isMethodCall = isAttributeLoad(func) && keywords.length == 0; + int numArgs = len(args) + (isMethodCall ? 1 : 0); + boolean useVariadic = anyIsStarred(args) || len(keywords) > 0 || numArgs > NUM_ARGS_MAX_FIXED; + + // @formatter:off + if (useVariadic) { + b.beginCallVarargsMethod(); + } else { + switch (numArgs) { + case 0: b.beginCallNilaryMethod(); break; + case 1: b.beginCallUnaryMethod(); break; + case 2: b.beginCallBinaryMethod(); break; + case 3: b.beginCallTernaryMethod(); break; + case 4: b.beginCallQuaternaryMethod(); break; + } + } + + // @formatter:on + + if (isMethodCall) { + // The receiver is needed for method lookup and for the first argument. + BytecodeLocal receiver = b.createLocal(); + + if (useVariadic) { + BytecodeLocal function = b.createLocal(); + b.beginBlock(); + b.beginStoreLocal(function); + emitGetMethod(func, receiver); + b.endStoreLocal(); + b.emitLoadLocal(function); + b.endBlock(); + + emitUnstar(() -> b.emitLoadLocal(receiver), args); + emitKeywords(keywords, function); + } else { + assert len(keywords) == 0; + + emitGetMethod(func, receiver); + b.emitLoadLocal(receiver); + visitSequence(args); + } + + } else { + if (useVariadic) { + BytecodeLocal function = b.createLocal(); + + b.beginBlock(); + b.beginStoreLocal(function); + func.accept(this); + b.endStoreLocal(); + b.emitLoadLocal(function); + b.endBlock(); + + emitUnstar(args); + emitKeywords(keywords, function); + } else { + assert len(keywords) == 0; + + func.accept(this); + visitSequence(args); + } + } + + // @formatter:off + if (useVariadic) { + b.endCallVarargsMethod(); + } else { + switch (numArgs) { + case 0: b.endCallNilaryMethod(); break; + case 1: b.endCallUnaryMethod(); break; + case 2: b.endCallBinaryMethod(); break; + case 3: b.endCallTernaryMethod(); break; + case 4: b.endCallQuaternaryMethod(); break; + } + } + // @formatter:on + } + + private void emitGetMethod(ExprTy func, BytecodeLocal receiver) { + assert isAttributeLoad(func); + ExprTy.Attribute attrAccess = (ExprTy.Attribute) func; + b.beginBlock(); + b.beginStoreLocal(receiver); + attrAccess.value.accept(this); + b.endStoreLocal(); + + String mangled = mangle(attrAccess.attr); + b.beginGetMethod(toTruffleStringUncached(mangled)); + b.emitLoadLocal(receiver); + b.endGetMethod(); + b.endBlock(); + } + + @Override + public Void visit(ExprTy.Call node) { + boolean newStatement = beginSourceSection(node, b); + emitCall(node.func, node.args, node.keywords); + endSourceSection(b, newStatement); + return null; + } + + private void beginComparison(CmpOpTy op) { + switch (op) { + case Eq: + b.beginEq(); + break; + case NotEq: + b.beginNe(); + break; + case Lt: + b.beginLt(); + break; + case LtE: + b.beginLe(); + break; + case Gt: + b.beginGt(); + break; + case GtE: + b.beginGe(); + break; + case Is: + b.beginIs(); + break; + case IsNot: + b.beginNot(); + b.beginIs(); + break; + case In: + b.beginContains(); + break; + case NotIn: + b.beginNot(); + b.beginContains(); + break; + default: + throw new UnsupportedOperationException("" + op); + } + } + + private void endComparison(CmpOpTy op) { + switch (op) { + case Eq: + b.endEq(); + break; + case NotEq: + b.endNe(); + break; + case Lt: + b.endLt(); + break; + case LtE: + b.endLe(); + break; + case Gt: + b.endGt(); + break; + case GtE: + b.endGe(); + break; + case Is: + b.endIs(); + break; + case IsNot: + b.endIs(); + b.endNot(); + break; + case In: + b.endContains(); + break; + case NotIn: + b.endContains(); + b.endNot(); + break; + default: + throw new UnsupportedOperationException("" + op); + } + } + + @Override + public Void visit(ExprTy.Compare node) { + boolean newStatement = beginSourceSection(node, b); + checkCompare(node); + + boolean multipleComparisons = node.comparators.length > 1; + + if (multipleComparisons) { + b.beginBoolAnd(); + } + + BytecodeLocal tmp = b.createLocal(); + + for (int i = 0; i < node.comparators.length; i++) { + beginComparison(node.ops[i]); + + if (i == 0) { + node.left.accept(this); + } else { + b.emitLoadLocal(tmp); + } + + if (i != node.comparators.length - 1) { + b.beginTeeLocal(tmp); + } + node.comparators[i].accept(this); + if (i != node.comparators.length - 1) { + b.endTeeLocal(); + } + + endComparison(node.ops[i]); + } + + if (multipleComparisons) { + b.endBoolAnd(); + } + + endSourceSection(b, newStatement); + return null; + } + + private void warn(SSTNode node, String message, Object... arguments) { + ctx.errorCallback.onWarning(WarningType.Syntax, node.getSourceRange(), message, arguments); + } + + private void checkCompare(ExprTy node) { + if (!(node instanceof ExprTy.Compare compare)) { + return; + } + boolean left = checkIsArg(compare.left); + int n = compare.ops == null ? 0 : compare.ops.length; + for (int i = 0; i < n; ++i) { + CmpOpTy op = compare.ops[i]; + boolean right = checkIsArg(compare.comparators[i]); + if (op == CmpOpTy.Is || op == CmpOpTy.IsNot) { + if (!right || !left) { + warn(compare, op == CmpOpTy.Is ? "\"is\" with a literal. Did you mean \"==\"?" : "\"is not\" with a literal. Did you mean \"!=\"?"); + } + } + left = right; + } + } + + private static boolean checkIsArg(ExprTy e) { + if (e instanceof ExprTy.Constant) { + ConstantValue.Kind kind = ((Constant) e).value.kind; + return kind == Kind.NONE || kind == Kind.BOOLEAN || kind == Kind.ELLIPSIS; + } + return true; + } + + private void createConstant(ConstantValue value) { + switch (value.kind) { + case NONE: + b.emitLoadConstant(PNone.NONE); + break; + case ELLIPSIS: + b.emitLoadConstant(PEllipsis.INSTANCE); + break; + case BOOLEAN: + emitPythonConstant(value.getBoolean(), b); + break; + case LONG: + emitPythonConstant(getConstantNumber(value.getLong()), b); + break; + case DOUBLE: + emitPythonConstant(value.getDouble(), b); + break; + case COMPLEX: { + double[] complex = value.getComplex(); + addConstant(complex); + b.emitLoadComplex(complex[0], complex[1]); + break; + } + case BIGINTEGER: + addConstant(value.getBigInteger()); + b.emitLoadBigInt(value.getBigInteger()); + break; + case RAW: + emitPythonConstant(value.getRaw(TruffleString.class), b); + break; + case BYTES: + addConstant(value.getBytes()); + b.emitLoadBytes(value.getBytes()); + break; + case TUPLE: + b.beginMakeTuple(); + b.beginMakeVariadic(); + for (ConstantValue cv : value.getTupleElements()) { + createConstant(cv); + } + b.endMakeVariadic(); + b.endMakeTuple(); + break; + case FROZENSET: + b.beginMakeFrozenSet(value.getFrozensetElements().length); + for (ConstantValue cv : value.getFrozensetElements()) { + createConstant(cv); + } + b.endMakeFrozenSet(); + break; + + default: + throw new UnsupportedOperationException("not supported: " + value.kind); + } + } + + /** + * Some AST nodes have type guards expecting ints rather than long. When the actual constant + * fits into something smaller, convert it accordingly. + */ + private Object getConstantNumber(long value) { + if (value == (int) value) { + return (int) value; + } else { + return value; + } + } + + @Override + public Void visit(ExprTy.Constant node) { + boolean newStatement = beginSourceSection(node, b); + createConstant(node.value); + endSourceSection(b, newStatement); + return null; + } + + @Override + public Void visit(ExprTy.Dict node) { + boolean newStatement = beginSourceSection(node, b); + + if (len(node.keys) == 0) { + b.emitMakeEmptyDict(); + } else { + b.beginMakeDict(node.keys.length); + for (int i = 0; i < node.keys.length; i++) { + if (node.keys[i] == null) { + b.emitLoadConstant(PNone.NO_VALUE); + } else { + node.keys[i].accept(this); + } + node.values[i].accept(this); + } + b.endMakeDict(); + } + + endSourceSection(b, newStatement); + return null; + } + + @Override + public Void visit(ExprTy.DictComp node) { + boolean newStatement = beginSourceSection(node, b); + + b.beginCallUnaryMethod(); + emitMakeFunction(node, "", COMPREHENSION_ARGS, null); + node.generators[0].iter.accept(this); + b.endCallUnaryMethod(); + + endSourceSection(b, newStatement); + return null; + } + + @Override + public Void visit(ExprTy.FormattedValue node) { + boolean newStatement = beginSourceSection(node, b); + b.beginFormat(); + + // @formatter:off + switch (node.conversion) { + case 's': b.beginFormatStr(); break; + case 'r': b.beginFormatRepr(); break; + case 'a': b.beginFormatAscii(); break; + case -1: break; + default: throw new UnsupportedOperationException("unknown conversion: " + node.conversion); + } + // @formatter:on + + node.value.accept(this); + + // @formatter:off + switch (node.conversion) { + case 's': b.endFormatStr(); break; + case 'r': b.endFormatRepr(); break; + case 'a': b.endFormatAscii(); break; + case -1: break; + default: throw new UnsupportedOperationException("unknown conversion: " + node.conversion); + } + // @formatter:on + + if (node.formatSpec != null) { + node.formatSpec.accept(this); + } else { + b.emitLoadConstant(StringLiterals.T_EMPTY_STRING); + } + + b.endFormat(); + endSourceSection(b, newStatement); + + return null; + } + + @Override + public Void visit(ExprTy.GeneratorExp node) { + boolean newStatement = beginSourceSection(node, b); + + b.beginCallUnaryMethod(); + emitMakeFunction(node, "", COMPREHENSION_ARGS, null); + node.generators[0].iter.accept(this); + b.endCallUnaryMethod(); + + endSourceSection(b, newStatement); + return null; + } + + @Override + public Void visit(ExprTy.IfExp node) { + boolean newStatement = beginSourceSection(node, b); + + b.beginConditional(); + visitCondition(node.test); + node.body.accept(this); + node.orElse.accept(this); + b.endConditional(); + + endSourceSection(b, newStatement); + return null; + } + + @Override + public Void visit(ExprTy.JoinedStr node) { + boolean newStatement = beginSourceSection(node, b); + + if (node.values.length == 1) { + node.values[0].accept(this); + } else { + b.beginBuildString(node.values.length); + visitSequence(node.values); + b.endBuildString(); + } + + endSourceSection(b, newStatement); + return null; + } + + @Override + public Void visit(ExprTy.Lambda node) { + boolean newStatement = beginSourceSection(node, b); + emitMakeFunction(node, "", node.args, null); + endSourceSection(b, newStatement); + return null; + } + + @Override + public Void visit(ExprTy.List node) { + boolean newStatement = beginSourceSection(node, b); + + ConstantCollection constantCollection = Compiler.tryCollectConstantCollection(node.elements); + if (constantCollection != null) { + emitConstantList(constantCollection); + } else { + b.beginMakeList(); + emitUnstar(node.elements); + b.endMakeList(); + } + + endSourceSection(b, newStatement); + return null; + } + + private static final String COMPREHENSION_ARGUMENT_NAME = ".0"; + private static final ArgumentsTy COMPREHENSION_ARGS = new ArgumentsTy(new ArgTy[]{new ArgTy(COMPREHENSION_ARGUMENT_NAME, null, null, null)}, null, null, null, null, null, null, null); + + @Override + public Void visit(ExprTy.ListComp node) { + boolean newStatement = beginSourceSection(node, b); + + b.beginCallUnaryMethod(); + emitMakeFunction(node, "", COMPREHENSION_ARGS, null); + node.generators[0].iter.accept(this); + b.endCallUnaryMethod(); + + endSourceSection(b, newStatement); + return null; + } + + @Override + public Void visit(ExprTy.Name node) { + boolean newStatement = beginSourceSection(node, b); + emitReadLocal(node.id, b); + endSourceSection(b, newStatement); + return null; + } + + @Override + public Void visit(ExprTy.NamedExpr node) { + boolean newStatement = beginSourceSection(node, b); + b.beginBlock(); + + // save expr result to "tmp" + BytecodeLocal tmp = b.createLocal(); + b.beginStoreLocal(tmp); + node.value.accept(this); + b.endStoreLocal(); + + node.target.accept(new StoreVisitor(() -> { + b.emitLoadLocal(tmp); + })); + + b.emitLoadLocal(tmp); + + b.endBlock(); + endSourceSection(b, newStatement); + return null; + } + + private void emitConstantList(ConstantCollection constantCollection) { + addConstant(constantCollection.collection); + switch (constantCollection.elementType) { + case CollectionBits.ELEMENT_INT: + b.emitMakeConstantIntList((int[]) constantCollection.collection); + break; + case CollectionBits.ELEMENT_LONG: + b.emitMakeConstantLongList((long[]) constantCollection.collection); + break; + case CollectionBits.ELEMENT_BOOLEAN: + b.emitMakeConstantBooleanList((boolean[]) constantCollection.collection); + break; + case CollectionBits.ELEMENT_DOUBLE: + b.emitMakeConstantDoubleList((double[]) constantCollection.collection); + break; + case CollectionBits.ELEMENT_OBJECT: + b.emitMakeConstantObjectList((Object[]) constantCollection.collection); + break; + default: + throw CompilerDirectives.shouldNotReachHere(); + } + } + + private void emitConstantTuple(ConstantCollection constantCollection) { + addConstant(constantCollection.collection); + switch (constantCollection.elementType) { + case CollectionBits.ELEMENT_INT: + b.emitMakeConstantIntTuple((int[]) constantCollection.collection); + break; + case CollectionBits.ELEMENT_LONG: + b.emitMakeConstantLongTuple((long[]) constantCollection.collection); + break; + case CollectionBits.ELEMENT_BOOLEAN: + b.emitMakeConstantBooleanTuple((boolean[]) constantCollection.collection); + break; + case CollectionBits.ELEMENT_DOUBLE: + b.emitMakeConstantDoubleTuple((double[]) constantCollection.collection); + break; + case CollectionBits.ELEMENT_OBJECT: + b.emitMakeConstantObjectTuple((Object[]) constantCollection.collection); + break; + default: + throw CompilerDirectives.shouldNotReachHere(); + } + } + + /** + * Converts a sequence of expressions of which some may be starred into just an Object[]. + * + * @param args the sequence of expressions + */ + private void emitUnstar(ExprTy[] args) { + emitUnstar(null, args); + } + + /** + * Same as above, but takes an optional Runnable to produce elements at the beginning of the + * sequence. + * + * @param initialElementsProducer a runnable to produce the first element(s) of the + * sequence. + * @param args the sequence of expressions to unstar + */ + private void emitUnstar(Runnable initialElementsProducer, ExprTy[] args) { + if (len(args) == 0 && initialElementsProducer == null) { + b.emitLoadConstant(PythonUtils.EMPTY_OBJECT_ARRAY); + } else if (anyIsStarred(args)) { + /** + * We emit one or more arrays and concatenate them using Unstar. Each array + * corresponds to a contiguous sequence of arguments or the result of unpacking a + * single starred argument. + * + * For example, for the argument list a, b, *c, d, e, *f, g we would emit: + * + * @formatter:off + * Unstar( + * MakeVariadic(a, b), + * UnpackStarred(c), + * MakeVariadic(d, e), + * UnpackStarred(f), + * MakeVariadic(g) + * ) + * @formatter:on + */ + b.beginUnstar(); + boolean inVariadic = false; + int numOperands = 0; + + if (initialElementsProducer != null) { + b.beginMakeVariadic(); + initialElementsProducer.run(); + inVariadic = true; + } + + for (int i = 0; i < args.length; i++) { + if (args[i] instanceof ExprTy.Starred) { + if (inVariadic) { + b.endMakeVariadic(); + inVariadic = false; + numOperands++; + } + + b.beginUnpackStarred(); + ((ExprTy.Starred) args[i]).value.accept(this); + b.endUnpackStarred(); + numOperands++; + } else { + if (!inVariadic) { + b.beginMakeVariadic(); + inVariadic = true; + } + + args[i].accept(this); + } + } + + if (inVariadic) { + b.endMakeVariadic(); + numOperands++; + } + + b.endUnstar(numOperands); + } else { + b.beginMakeVariadic(); + if (initialElementsProducer != null) { + initialElementsProducer.run(); + } + visitSequence(args); + b.endMakeVariadic(); + } + } + + @Override + public Void visit(ExprTy.Set node) { + boolean newStatement = beginSourceSection(node, b); + + if (len(node.elements) == 0) { + b.emitMakeEmptySet(); + } else { + b.beginMakeSet(); + emitUnstar(node.elements); + b.endMakeSet(); + } + + endSourceSection(b, newStatement); + return null; + } + + @Override + public Void visit(ExprTy.SetComp node) { + boolean newStatement = beginSourceSection(node, b); + + b.beginCallUnaryMethod(); + emitMakeFunction(node, "", COMPREHENSION_ARGS, null); + node.generators[0].iter.accept(this); + b.endCallUnaryMethod(); + + endSourceSection(b, newStatement); + return null; + } + + private void visitNoneable(ExprTy node) { + if (node == null) { + b.emitLoadConstant(PNone.NONE); + } else { + node.accept(this); + } + } + + @Override + public Void visit(ExprTy.Slice node) { + boolean newStatement = beginSourceSection(node, b); + + b.beginMakeSlice(); + + visitNoneable(node.lower); + visitNoneable(node.upper); + visitNoneable(node.step); + + b.endMakeSlice(); + + endSourceSection(b, newStatement); + return null; + } + + @Override + public Void visit(ExprTy.Starred node) { + throw new UnsupportedOperationException("" + node.getClass()); + } + + @Override + public Void visit(ExprTy.Subscript node) { + boolean newStatement = beginSourceSection(node, b); + + b.beginGetItem(); + node.value.accept(this); + node.slice.accept(this); + b.endGetItem(); + + endSourceSection(b, newStatement); + return null; + } + + @Override + public Void visit(ExprTy.Tuple node) { + boolean newStatement = beginSourceSection(node, b); + + ConstantCollection constantCollection = Compiler.tryCollectConstantCollection(node.elements); + if (constantCollection != null) { + emitConstantTuple(constantCollection); + } else { + b.beginMakeTuple(); + emitUnstar(node.elements); + b.endMakeTuple(); + } + + endSourceSection(b, newStatement); + return null; + } + + @Override + public Void visit(ExprTy.UnaryOp node) { + // Basic constant folding for unary negation + if (node.op == UnaryOpTy.USub && node.operand instanceof ExprTy.Constant c) { + if (c.value.kind == ConstantValue.Kind.BIGINTEGER || c.value.kind == ConstantValue.Kind.DOUBLE || c.value.kind == ConstantValue.Kind.LONG || + c.value.kind == ConstantValue.Kind.COMPLEX) { + ConstantValue cv = c.value.negate(); + boolean newStatement = beginSourceSection(node, b); + visit(new ExprTy.Constant(cv, null, c.getSourceRange())); + endSourceSection(b, newStatement); + return null; + } + } + boolean newStatement = beginSourceSection(node, b); + switch (node.op) { + case UAdd: + b.beginPos(); + node.operand.accept(this); + b.endPos(); + break; + case Invert: + b.beginInvert(); + node.operand.accept(this); + b.endInvert(); + break; + case USub: + b.beginNeg(); + node.operand.accept(this); + b.endNeg(); + break; + case Not: + b.beginNot(); + node.operand.accept(this); + b.endNot(); + break; + default: + throw new UnsupportedOperationException("" + node.getClass()); + } + + endSourceSection(b, newStatement); + return null; + } + + @Override + public Void visit(ExprTy.Yield node) { + if (!scope.isFunction()) { + ctx.errorCallback.onError(ErrorType.Syntax, currentLocation, "'yield' outside function"); + } + boolean newStatement = beginSourceSection(node, b); + emitYield((statementCompiler) -> { + if (node.value != null) { + node.value.accept(this); + } else { + statementCompiler.b.emitLoadConstant(PNone.NONE); + } + }, this); + + endSourceSection(b, newStatement); + return null; + } + + @Override + public Void visit(ExprTy.YieldFrom node) { + if (!scope.isFunction()) { + ctx.errorCallback.onError(ErrorType.Syntax, currentLocation, "'yield' outside function"); + } + if (scopeType == CompilationScope.AsyncFunction) { + ctx.errorCallback.onError(ErrorType.Syntax, currentLocation, "'yield from' inside async function"); + } + boolean newStatement = beginSourceSection(node, b); + emitYieldFrom(() -> { + b.beginGetYieldFromIter(); + node.value.accept(this); + b.endGetYieldFromIter(); + }); + endSourceSection(b, newStatement); + return null; + } + + public void emitYieldFrom(Runnable generatorOrCoroutineProducer) { + /** + * @formatter:off + * generator = + * returnValue = None + * sentValue = None + * + * # Step 1: prime the generator + * try: + * yieldValue = generator.send(sentValue) + * except StopIteration as e: + * returnValue = e.value + * goto end + * + * while True: + * # Step 2: yield yieldValue to the caller + * try: + * sentValue = yield yieldValue + * except Exception as e: + * # throw/close generator + * if generator returned a value: + * goto end + * else: + * continue (generator yielded a value) + * + * # Step 3: send sentValue into the generator + * try: + * yieldValue = generator.send(sentValue) + * except StopIteration as e: + * returnValue = e.value + * goto end + * + * end: + * # Step 4: return returnValue + * returnValue (result) + * @formatter:on + */ + b.beginBlock(); + + BytecodeLocal generator = b.createLocal(); + BytecodeLocal returnValue = b.createLocal(); + BytecodeLocal sentValue = b.createLocal(); + BytecodeLocal yieldValue = b.createLocal(); + BytecodeLabel end = b.createLabel(); + + b.beginStoreLocal(generator); + generatorOrCoroutineProducer.run(); + b.endStoreLocal(); + + b.beginStoreLocal(returnValue); + b.emitLoadConstant(PNone.NONE); + b.endStoreLocal(); + + b.beginStoreLocal(sentValue); + b.emitLoadConstant(PNone.NONE); + b.endStoreLocal(); + + // Step 1: prime the generator + emitSend(generator, sentValue, yieldValue, returnValue, end); + + b.beginWhile(); + b.emitLoadConstant(true); + + b.beginBlock(); + BytecodeLabel loopEnd = b.createLabel(); + // Step 2: yield yieldValue to the caller + b.beginTryCatch(); + + // try clause: yield + b.beginStoreLocal(sentValue); + emitYield((statementCompiler) -> statementCompiler.b.emitLoadLocal(yieldValue), this); + b.endStoreLocal(); + + // catch clause: handle throw/close exceptions. + b.beginIfThenElse(); + b.beginYieldFromThrow(yieldValue, returnValue); + b.emitLoadLocal(generator); + b.emitLoadException(); + b.endYieldFromThrow(); + + // StopIteration was raised; go to the end. + b.emitBranch(end); + + // The generator yielded a value; go to top of the loop. + b.emitBranch(loopEnd); + + b.endIfThenElse(); + + b.endTryCatch(); + + // Step 3: send sentValue into the generator + emitSend(generator, sentValue, yieldValue, returnValue, end); + + b.emitLabel(loopEnd); + b.endBlock(); + b.endWhile(); + + // Step 4: return returnValue + b.emitLabel(end); + b.emitLoadLocal(returnValue); + + b.endBlock(); + } + + private void emitSend(BytecodeLocal generator, BytecodeLocal sentValue, BytecodeLocal yieldValue, BytecodeLocal returnValue, BytecodeLabel end) { + b.beginIfThen(); + // When the generator raises StopIteration, send evaluates to true; branch to the end. + b.beginYieldFromSend(yieldValue, returnValue); + b.emitLoadLocal(generator); + b.emitLoadLocal(sentValue); + b.endYieldFromSend(); + + b.emitBranch(end); + + b.endIfThen(); + } + + private void emitAwait(Runnable producer) { + emitYieldFrom(() -> { + b.beginGetAwaitable(); + producer.run(); + b.endGetAwaitable(); + }); + } + + @Override + public Void visit(KeywordTy node) { + throw new UnsupportedOperationException("" + node.getClass()); + } + + @Override + public Void visit(StmtTy.AnnAssign node) { + boolean newStatement = beginSourceSection(node, b); + b.beginBlock(); + if (node.value != null) { + // Emit the assignment if there's an RHS. + emitAssignment(new ExprTy[]{node.target}, node.value); + } + if (node.target instanceof ExprTy.Name) { + String name = ((ExprTy.Name) node.target).id; + checkForbiddenName(name, NameOperation.BeginWrite); + /* If we have a simple name in a module or class, store annotation. */ + if (node.isSimple && + (scopeType == CompilationScope.Module || scopeType == CompilationScope.Class)) { + b.beginSetDictItem(); + emitNameOperation("__annotations__", NameOperation.Read, b); + + String mangled = mangle(name); + emitPythonConstant(toTruffleStringUncached(mangled), b); + + if (futureFeatures.contains(FutureFeature.ANNOTATIONS)) { + emitPythonConstant(Unparser.unparse(node.annotation), b); + } else { + node.annotation.accept(this); + } + + b.endSetDictItem(); + } + } else if (node.target instanceof ExprTy.Attribute) { + if (node.value == null) { + ExprTy.Attribute attr = (ExprTy.Attribute) node.target; + checkForbiddenName(attr.attr, NameOperation.BeginWrite); + if (attr.value != null) { + checkAnnExpr(attr.value); + } + } + } else if (node.target instanceof ExprTy.Subscript) { + if (node.value == null) { + ExprTy.Subscript subscript = (ExprTy.Subscript) node.target; + if (subscript.value != null) { + checkAnnExpr(subscript.value); + } + checkAnnSubscr(subscript.slice); + } + } else { + ctx.errorCallback.onError(ErrorType.Syntax, node.getSourceRange(), "invalid node type for annotated assignment"); + } + if (!node.isSimple) { + /* + * Annotations of complex targets does not produce anything under annotations + * future. Annotations are only evaluated in a module or class. + */ + if (!futureFeatures.contains(FutureFeature.ANNOTATIONS) && (scopeType == CompilationScope.Module || scopeType == CompilationScope.Class)) { + checkAnnExpr(node.annotation); + } + } + b.endBlock(); + endSourceSection(b, newStatement); + return null; + } + + private void checkAnnExpr(ExprTy expr) { + expr.accept(this); + } + + private void checkAnnSubscr(ExprTy expr) { + if (expr instanceof ExprTy.Slice) { + ExprTy.Slice slice = (ExprTy.Slice) expr; + if (slice.lower != null) { + checkAnnExpr(slice.lower); + } + if (slice.upper != null) { + checkAnnExpr(slice.upper); + } + if (slice.step != null) { + checkAnnExpr(slice.step); + } + } else if (expr instanceof ExprTy.Tuple) { + ExprTy.Tuple tuple = (ExprTy.Tuple) expr; + for (int i = 0; i < tuple.elements.length; i++) { + checkAnnSubscr(tuple.elements[i]); + } + } else { + checkAnnExpr(expr); + } + } + + @Override + public Void visit(StmtTy.Assert node) { + if (ctx.optimizationLevel <= 0) { + boolean newStatement = beginSourceSection(node, b); + b.beginIfThen(); + + b.beginNot(); + node.test.accept(this); + b.endNot(); + + b.beginAssertFailed(); + if (node.msg == null) { + b.emitLoadConstant(PNone.NO_VALUE); + } else { + node.msg.accept(this); + } + b.endAssertFailed(); + + b.endIfThen(); + endSourceSection(b, newStatement); + } + return null; + } + + // --------------------- assign ------------------------ + + /** + * Generates code to store the value produced by {@link #generateValue} into the visited + * expression. + */ + public class StoreVisitor implements BaseBytecodeDSLVisitor { + private final Builder b = StatementCompiler.this.b; + private final Runnable generateValue; + + StoreVisitor(Runnable generateValue) { + this.generateValue = generateValue; + } + + @Override + public Void visit(ExprTy.Name node) { + boolean newStatement = beginSourceSection(node, b); + beginStoreLocal(node.id, b); + generateValue.run(); + endStoreLocal(node.id, b); + endSourceSection(b, newStatement); + return null; + } + + @Override + public Void visit(ExprTy.Attribute node) { + boolean newStatement = beginSourceSection(node, b); + checkForbiddenName(node.attr, NameOperation.BeginWrite); + b.beginSetAttribute(toTruffleStringUncached(mangle(node.attr))); + generateValue.run(); + node.value.accept(StatementCompiler.this); + b.endSetAttribute(); + endSourceSection(b, newStatement); + return null; + } + + @Override + public Void visit(ExprTy.Subscript node) { + boolean newStatement = beginSourceSection(node, b); + b.beginSetItem(); + generateValue.run(); + node.value.accept(StatementCompiler.this); + node.slice.accept(StatementCompiler.this); + b.endSetItem(); + endSourceSection(b, newStatement); + return null; + } + + /** + * This method unpacks the rhs (a sequence/iterable) to the elements on the lhs + * (specified by {@code nodes}. + */ + private void visitIterableAssign(ExprTy[] nodes) { + b.beginBlock(); + + /** + * The rhs should be fully evaluated and unpacked into the expected number of + * elements before storing values into the lhs (e.g., if an lhs element is f().attr, + * but computing or unpacking rhs throws, f() is not computed). Thus, the unpacking + * step stores the unpacked values into intermediate variables, and then those + * variables are copied into the lhs elements afterward. + */ + BytecodeLocal[] targets = new BytecodeLocal[nodes.length]; + for (int i = 0; i < targets.length; i++) { + targets[i] = b.createLocal(); + } + + int indexOfStarred = -1; + for (int i = 0; i < nodes.length; i++) { + if (nodes[i] instanceof ExprTy.Starred) { + indexOfStarred = i; + break; + } + } + + if (indexOfStarred == -1) { + b.beginUnpackToLocals(targets); + } else { + b.beginUnpackStarredToLocals(indexOfStarred, targets); + } + + generateValue.run(); + + if (indexOfStarred == -1) { + b.endUnpackToLocals(); + } else { + b.endUnpackStarredToLocals(); + } + + for (int i = 0; i < nodes.length; i++) { + final int index = i; + + ExprTy target = nodes[i]; + if (nodes[i] instanceof ExprTy.Starred) { + target = ((ExprTy.Starred) target).value; + } + + target.accept(new StoreVisitor(() -> { + b.emitLoadLocal(targets[index]); + })); + } + + b.endBlock(); + } + + @Override + public Void visit(ExprTy.Tuple node) { + boolean newStatement = beginSourceSection(node, b); + visitIterableAssign(node.elements); + endSourceSection(b, newStatement); + return null; + } + + @Override + public Void visit(ExprTy.List node) { + boolean newStatement = beginSourceSection(node, b); + visitIterableAssign(node.elements); + endSourceSection(b, newStatement); + return null; + } + } + + private class AugStoreVisitor implements BaseBytecodeDSLVisitor { + private final Builder b = StatementCompiler.this.b; + private final ExprTy value; + private final OperatorTy op; + + AugStoreVisitor(OperatorTy op, ExprTy value) { + this.op = op; + this.value = value; + } + + private void beginAugAssign() { + switch (op) { + case Add: + b.beginIAdd(); + break; + case Sub: + b.beginISub(); + break; + case Mult: + b.beginIMult(); + break; + case FloorDiv: + b.beginIFloorDiv(); + break; + case BitAnd: + b.beginIAnd(); + break; + case BitOr: + b.beginIOr(); + break; + case BitXor: + b.beginIXor(); + break; + case RShift: + b.beginIRShift(); + break; + case LShift: + b.beginILShift(); + break; + case Div: + b.beginITrueDiv(); + break; + case Mod: + b.beginIMod(); + break; + case MatMult: + b.beginIMatMul(); + break; + case Pow: + b.beginIPow(); + break; + default: + throw new UnsupportedOperationException("aug ass: " + op); + } + } + + private void endAugAssign() { + switch (op) { + case Add: + b.endIAdd(); + break; + case Sub: + b.endISub(); + break; + case Mult: + b.endIMult(); + break; + case FloorDiv: + b.endIFloorDiv(); + break; + case BitAnd: + b.endIAnd(); + break; + case BitOr: + b.endIOr(); + break; + case BitXor: + b.endIXor(); + break; + case RShift: + b.endIRShift(); + break; + case LShift: + b.endILShift(); + break; + case Div: + b.endITrueDiv(); + break; + case Mod: + b.endIMod(); + break; + case MatMult: + b.endIMatMul(); + break; + case Pow: + b.endIPow(); + break; + default: + throw new UnsupportedOperationException("aug ass: " + op); + } + } + + @Override + public Void visit(ExprTy.Name node) { + boolean newStatement = beginSourceSection(node, b); + + beginStoreLocal(node.id, b); + beginAugAssign(); + emitReadLocal(node.id, b); + value.accept(StatementCompiler.this); + endAugAssign(); + endStoreLocal(node.id, b); + + endSourceSection(b, newStatement); + return null; + } + + @Override + public Void visit(ExprTy.Attribute node) { + boolean newStatement = beginSourceSection(node, b); + checkForbiddenName(node.attr, NameOperation.BeginWrite); + b.beginBlock(); + // { + BytecodeLocal target = b.createLocal(); + + b.beginStoreLocal(target); + node.value.accept(StatementCompiler.this); + b.endStoreLocal(); + + TruffleString attrName = toTruffleStringUncached(mangle(node.attr)); + b.beginSetAttribute(attrName); + beginAugAssign(); + + b.beginGetAttribute(attrName); + b.emitLoadLocal(target); + b.endGetAttribute(); + + value.accept(StatementCompiler.this); + + endAugAssign(); + + b.emitLoadLocal(target); + b.endSetAttribute(); + // } + b.endBlock(); + endSourceSection(b, newStatement); + return null; + } + + @Override + public Void visit(ExprTy.Subscript node) { + boolean newStatement = beginSourceSection(node, b); + b.beginBlock(); + // { + BytecodeLocal target = b.createLocal(); + BytecodeLocal slice = b.createLocal(); + + b.beginStoreLocal(target); + node.value.accept(StatementCompiler.this); + b.endStoreLocal(); + + b.beginStoreLocal(slice); + node.slice.accept(StatementCompiler.this); + b.endStoreLocal(); + + b.beginSetItem(); + beginAugAssign(); + + b.beginGetItem(); + b.emitLoadLocal(target); + b.emitLoadLocal(slice); + b.endGetItem(); + + value.accept(StatementCompiler.this); + + endAugAssign(); + + b.emitLoadLocal(target); + b.emitLoadLocal(slice); + b.endSetItem(); + // } + b.endBlock(); + endSourceSection(b, newStatement); + return null; + } + } + + @Override + public Void visit(StmtTy.Assign node) { + boolean newStatement = beginSourceSection(node, b); + b.beginBlock(); + emitAssignment(node.targets, node.value); + b.endBlock(); + endSourceSection(b, newStatement); + return null; + } + + private void emitAssignment(ExprTy[] targets, ExprTy value) { + if (targets.length == 1) { + targets[0].accept(new StoreVisitor(() -> { + value.accept(this); + })); + } else { + BytecodeLocal tmp = b.createLocal(); + b.beginStoreLocal(tmp); + value.accept(this); + b.endStoreLocal(); + + for (ExprTy target : targets) { + target.accept(new StoreVisitor(() -> { + b.emitLoadLocal(tmp); + })); + } + } + } + + @Override + public Void visit(StmtTy.AsyncFor node) { + emitNotImplemented("async for", b); + return null; + } + + @Override + public Void visit(StmtTy.AsyncWith node) { + if (!scope.isFunction()) { + ctx.errorCallback.onError(ErrorType.Syntax, currentLocation, "'async with' outside function"); + } + if (scopeType != CompilationScope.AsyncFunction && scopeType != CompilationScope.Comprehension) { + ctx.errorCallback.onError(ErrorType.Syntax, currentLocation, "'async with' outside async function"); + } + boolean newStatement = beginSourceSection(node, b); + visitWithRecurse(node.items, 0, node.body, true); + endSourceSection(b, newStatement); + return null; + } + + @Override + public Void visit(StmtTy.AugAssign node) { + boolean newStatement = beginSourceSection(node, b); + node.target.accept(new AugStoreVisitor(node.op, node.value)); + endSourceSection(b, newStatement); + return null; + } + + private abstract static sealed class KeywordGroup permits NamedKeywords, SplatKeywords { + } + + private static final class NamedKeywords extends KeywordGroup { + final ArrayList names; + final ArrayList values; + + NamedKeywords(ArrayList names, ArrayList values) { + this.names = names; + this.values = values; + } + } + + private static final class SplatKeywords extends KeywordGroup { + final ExprTy expr; + + SplatKeywords(ExprTy expr) { + this.expr = expr; + } + } + + private void emitKeywords(KeywordTy[] kws, BytecodeLocal function) { + if (len(kws) == 0) { + b.emitLoadConstant(PKeyword.EMPTY_KEYWORDS); + } else { + KeywordGroup[] groups = partitionKeywords(kws); + // The nodes that validate keyword arguments operate on PDicts, so we convert into + // a list of PKeywords after validation. + b.beginMappingToKeywords(); + emitKeywordsRecursive(groups, groups.length - 1, function); + b.endMappingToKeywords(); + } + } + + private KeywordGroup[] partitionKeywords(KeywordTy[] kws) { + ArrayList groups = new ArrayList<>(); + + int i = 0; + while (i < kws.length) { + if (kws[i].arg == null) { + // splat + groups.add(new SplatKeywords(kws[i].value)); + i++; + } else { + // named keyword + ArrayList kwNames = new ArrayList<>(); + ArrayList kwValues = new ArrayList<>(); + while (i < kws.length && kws[i].arg != null) { + kwNames.add(toTruffleStringUncached(kws[i].arg)); + kwValues.add(kws[i].value); + i++; + } + groups.add(new NamedKeywords(kwNames, kwValues)); + } + } + + return groups.toArray(KeywordGroup[]::new); + } + + private void emitKeywordsRecursive(KeywordGroup[] groups, int i, BytecodeLocal function) { + /* + * Keyword groups should be merged left-to-right. For example, for groups [A, B, C] we + * should emit KwArgsMerge(KwArgsMerge(A, B), C). + */ + if (i == 0) { + emitKeywordGroup(groups[i], true, function); + } else { + b.beginKwargsMerge(); + emitKeywordsRecursive(groups, i - 1, function); + emitKeywordGroup(groups[i], false, function); + b.emitLoadLocal(function); + b.endKwargsMerge(); + } + } + + private void emitKeywordGroup(KeywordGroup group, boolean copy, BytecodeLocal function) { + if (group instanceof NamedKeywords namedKeywords) { + b.beginMakeDict(namedKeywords.names.size()); + for (int i = 0; i < namedKeywords.names.size(); i++) { + emitPythonConstant(namedKeywords.names.get(i), b); + namedKeywords.values.get(i).accept(this); + } + b.endMakeDict(); + } else { + SplatKeywords splatKeywords = (SplatKeywords) group; + + if (copy) { + b.beginKwargsMerge(); + b.emitMakeEmptyDict(); + splatKeywords.expr.accept(this); + b.emitLoadLocal(function); + b.endKwargsMerge(); + } else { + splatKeywords.expr.accept(this); + } + } + } + + @Override + public Void visit(StmtTy.ClassDef node) { + boolean newStatement = beginSourceSection(node, b); + b.beginBlock(); + BytecodeLocal buildClassFunction = b.createLocal(); + + // compute __build_class__ (we need it in multiple places, so store it) + b.beginStoreLocal(buildClassFunction); + b.emitBuildClass(); + b.endStoreLocal(); + + // ClassName = __build_class__(, "ClassName", bases, keywords) + beginStoreLocal(node.name, b); + + int numDeco = len(node.decoratorList); + for (int i = 0; i < numDeco; i++) { + b.beginCallUnaryMethod(); + node.decoratorList[i].accept(this); + } + + b.beginCallVarargsMethod(); + + b.emitLoadLocal(buildClassFunction); + + // positional args + emitUnstar(() -> { + emitMakeFunction(node, node.name, null, null); + emitPythonConstant(toTruffleStringUncached(node.name), b); + }, node.bases); + + // keyword args + validateKeywords(node.keywords); + emitKeywords(node.keywords, buildClassFunction); + + b.endCallVarargsMethod(); + + for (int i = 0; i < numDeco; i++) { + b.endCallUnaryMethod(); + } + + endStoreLocal(node.name, b); + + b.endBlock(); + endSourceSection(b, newStatement); + + return null; + } + + private class DeleteVisitor implements BaseBytecodeDSLVisitor { + + @Override + public Void visit(ExprTy.Subscript node) { + boolean newStatement = beginSourceSection(node, b); + + b.beginDeleteItem(); + node.value.accept(StatementCompiler.this); + node.slice.accept(StatementCompiler.this); + b.endDeleteItem(); + + endSourceSection(b, newStatement); + return null; + } + + @Override + public Void visit(ExprTy.Attribute node) { + boolean newStatement = beginSourceSection(node, b); + checkForbiddenName(node.attr, NameOperation.BeginWrite); + b.beginDeleteAttribute(toTruffleStringUncached(node.attr)); + node.value.accept(StatementCompiler.this); + b.endDeleteAttribute(); + + endSourceSection(b, newStatement); + return null; + } + + @Override + public Void visit(ExprTy.Name node) { + boolean newStatement = beginSourceSection(node, b); + emitNameOperation(node.id, NameOperation.Delete, b); + endSourceSection(b, newStatement); + return null; + } + + @Override + public Void visit(ExprTy.Tuple node) { + boolean newStatement = beginSourceSection(node, b); + b.beginBlock(); + visitSequence(node.elements); + b.endBlock(); + endSourceSection(b, newStatement); + return null; + } + + @Override + public Void visit(ExprTy.List node) { + boolean newStatement = beginSourceSection(node, b); + b.beginBlock(); + visitSequence(node.elements); + b.endBlock(); + endSourceSection(b, newStatement); + return null; + } + } + + @Override + public Void visit(StmtTy.Delete node) { + new DeleteVisitor().visitSequence(node.targets); + return null; + } + + @Override + public Void visit(StmtTy.Expr node) { + boolean newStatement = beginSourceSection(node, b); + if (isInteractive) { + b.beginPrintExpr(); + node.value.accept(this); + b.endPrintExpr(); + } else { + node.value.accept(this); + } + endSourceSection(b, newStatement); + + return null; + } + + @Override + public Void visit(StmtTy.For node) { + // @formatter:off + // iter = GetIter(<>); value; + // while (ForIterate(iter, &value)) { + // store value + // <> + // continueLabel: + // } + // < + // breakLabel: + // @formatter:on + boolean newStatement = beginSourceSection(node, b); + b.beginBlock(); + + BytecodeLocal iter = b.createLocal(); + + b.beginStoreLocal(iter); + b.beginGetIter(); + node.iter.accept(this); + b.endGetIter(); + b.endStoreLocal(); + + BytecodeLabel oldBreakLabel = breakLabel; + BytecodeLabel oldContinueLabel = continueLabel; + + BytecodeLabel currentBreakLabel = b.createLabel(); + breakLabel = currentBreakLabel; + + b.beginWhile(); + BytecodeLocal value = b.createLocal(); + + // condition + b.beginBlock(); + b.emitTraceLineAtLoopHeader(currentLocation.startLine); + b.beginForIterate(value); + b.emitLoadLocal(iter); + b.endForIterate(); + b.endBlock(); + + // body + b.beginBlock(); + continueLabel = b.createLabel(); + node.target.accept(new StoreVisitor(() -> { + b.emitLoadLocal(value); + })); + + visitSequence(node.body); + b.emitLabel(continueLabel); + b.endBlock(); + + b.endWhile(); + + breakLabel = oldBreakLabel; + continueLabel = oldContinueLabel; + visitSequence(node.orElse); + b.emitLabel(currentBreakLabel); + + b.endBlock(); + endSourceSection(b, newStatement); + return null; + } + + @Override + public Void visit(StmtTy.FunctionDef node) { + boolean newStatement = beginSourceSection(node, b); + b.beginBlock(); + + beginStoreLocal(node.name, b); + + int numDeco = len(node.decoratorList); + for (int i = 0; i < numDeco; i++) { + b.beginCallUnaryMethod(); + node.decoratorList[i].accept(this); + } + + List annotations = collectParamAnnotations(node.args, node.returns); + emitMakeFunction(node, node.name, node.args, annotations); + + for (int i = 0; i < numDeco; i++) { + b.endCallUnaryMethod(); + } + + endStoreLocal(node.name, b); + + b.endBlock(); + endSourceSection(b, newStatement); + return null; + } + + @Override + public Void visit(StmtTy.AsyncFunctionDef node) { + boolean newStatement = beginSourceSection(node, b); + beginStoreLocal(node.name, b); + + int numDeco = len(node.decoratorList); + for (int i = 0; i < numDeco; i++) { + b.beginCallUnaryMethod(); + node.decoratorList[i].accept(this); + } + + List annotations = collectParamAnnotations(node.args, node.returns); + emitMakeFunction(node, node.name, node.args, annotations); + + for (int i = 0; i < numDeco; i++) { + b.endCallUnaryMethod(); + } + + endStoreLocal(node.name, b); + endSourceSection(b, newStatement); + return null; + } + + private void emitParamAnnotation(ParamAnnotation paramAnnotation) { + emitPythonConstant(paramAnnotation.name, b); + + if (futureFeatures.contains(FutureFeature.ANNOTATIONS)) { + emitPythonConstant(Unparser.unparse(paramAnnotation.annotation), b); + } else { + if (paramAnnotation.annotation instanceof ExprTy.Starred starred) { + // *args: *Ts (where Ts is a TypeVarTuple). + // Do [annotation_value] = [*Ts]. + b.beginBlock(); + BytecodeLocal local = b.createLocal(); + b.beginUnpackToLocals(new BytecodeLocal[]{local}); + starred.value.accept(this); + b.endUnpackToLocals(); + b.emitLoadLocal(local); + b.endBlock(); + } else { + paramAnnotation.annotation.accept(this); + } + } + } + + private void emitMakeFunction(SSTNode node, String name, ArgumentsTy args, List annotations) { + BytecodeDSLCompilerResult compilerResult = compileNode(node); + BytecodeDSLCodeUnit codeUnit = compilerResult.codeUnit(); + + TruffleString functionName = toTruffleStringUncached(name); + Scope targetScope = ctx.scopeEnvironment.lookupScope(node); + TruffleString qualifiedName = toTruffleStringUncached(ctx.getQualifiedName(targetScope)); + + // Register these in the Python constants list. + addConstant(qualifiedName); + addConstant(codeUnit); + + b.beginMakeFunction(functionName, qualifiedName, codeUnit); + + if (args == null || len(args.defaults) == 0) { + b.emitLoadConstant(PythonUtils.EMPTY_OBJECT_ARRAY); + } else { + b.beginMakeVariadic(); + for (int i = 0; i < args.defaults.length; i++) { + args.defaults[i].accept(this); + } + b.endMakeVariadic(); + } + + boolean hasKeywords = false; + if (args != null && len(args.kwDefaults) != 0) { + // We only emit keywords with default values. Check if any exist. + for (int i = 0; i < args.kwDefaults.length; i++) { + if (args.kwDefaults[i] != null) { + hasKeywords = true; + break; + } + } + } + + if (!hasKeywords) { + b.emitLoadConstant(PKeyword.EMPTY_KEYWORDS); + } else { + ArgTy[] kwOnlyArgs = args.kwOnlyArgs; + + List keys = new ArrayList<>(); + b.beginMakeKeywords(); + for (int i = 0; i < args.kwDefaults.length; i++) { + // Only emit keywords with default values. + if (args.kwDefaults[i] != null) { + keys.add(toTruffleStringUncached(mangle(kwOnlyArgs[i].arg))); + args.kwDefaults[i].accept(this); + } + } + b.endMakeKeywords(keys.toArray(new TruffleString[0])); + } + + if (codeUnit.freevars.length == 0) { + b.emitLoadNull(); + } else { + b.beginMakeCellArray(); + for (int i = 0; i < codeUnit.freevars.length; i++) { + String fv = codeUnit.freevars[i].toJavaStringUncached(); + BytecodeLocal local; + if (scopeType == CompilationScope.Class && "__class__".equals(fv) || scope.getUseOfName(fv).contains(Scope.DefUse.Cell)) { + local = cellLocals.get(fv); + } else { + local = freeLocals.get(fv); + } + b.emitLoadLocal(local); + } + b.endMakeCellArray(); + } + + // __annotations__ + if (annotations != null && annotations.size() > 0) { + b.beginMakeDict(annotations.size()); + for (ParamAnnotation annotation : annotations) { + emitParamAnnotation(annotation); + } + b.endMakeDict(); + } else { + b.emitLoadNull(); + } + + b.endMakeFunction(); + } + + private BytecodeDSLCompilerResult compileNode(SSTNode node) { + return (new RootNodeCompiler(ctx, node, futureFeatures)).compile(); + } + + @Override + public Void visit(StmtTy.Global node) { + return null; + } + + private void visitStatements(StmtTy[] stmts) { + b.beginBlock(); + if (stmts != null) { + for (StmtTy stmt : stmts) { + stmt.accept(this); + } + } + b.endBlock(); + } + + @Override + public Void visit(StmtTy.If node) { + boolean newStatement = beginSourceSection(node, b); + if (node.orElse == null || node.orElse.length == 0) { + b.beginIfThen(); + visitCondition(node.test); + visitStatements(node.body); + b.endIfThen(); + } else { + b.beginIfThenElse(); + visitCondition(node.test); + visitStatements(node.body); + visitStatements(node.orElse); + b.endIfThenElse(); + } + + endSourceSection(b, newStatement); + return null; + } + + private boolean producesBoolean(ExprTy node) { + // NB: Binary and/or operations evaluate to their operands, which are not necessarily + // booleans. + return node instanceof ExprTy.UnaryOp unOp && unOp.op == UnaryOpTy.Not || + node instanceof ExprTy.Constant c && c.value.kind == Kind.BOOLEAN; + } + + private void visitCondition(ExprTy node) { + boolean mayNeedCoercion = !producesBoolean(node); + if (mayNeedCoercion) { + b.beginYes(); + } + + node.accept(this); + + if (mayNeedCoercion) { + b.endYes(); + } + } + + @Override + public Void visit(StmtTy.Import node) { + boolean newStatement = beginSourceSection(node, b); + b.beginBlock(); + + for (AliasTy name : node.names) { + addConstant(PythonUtils.EMPTY_TRUFFLESTRING_ARRAY); + if (name.asName == null) { + // import a.b.c + // --> a = (Import "a.b.c" [] 0) + // import a + // --> a = (Import "a" [] 0) + String resName = name.name.contains(".") + ? name.name.substring(0, name.name.indexOf('.')) + : name.name; + + beginStoreLocal(resName, b); + b.emitImport(toTruffleStringUncached(name.name), PythonUtils.EMPTY_TRUFFLESTRING_ARRAY, 0); + endStoreLocal(resName, b); + } else { + // import a.b.c as x + // --> x = (ImportFrom (ImportFrom (Import "a.b.c" [] 0) "b") "c") + // import a as x + // --> x = (Import "a" [] 0) + String[] parts = name.name.split("\\."); + + beginStoreLocal(name.asName, b); + + for (int i = parts.length - 1; i >= 0; i--) { + if (i != 0) { + b.beginImportFrom(toTruffleStringUncached(parts[i])); + } else { + b.emitImport(toTruffleStringUncached(name.name), PythonUtils.EMPTY_TRUFFLESTRING_ARRAY, 0); + } + } + + for (int i = 1; i < parts.length; i++) { + b.endImportFrom(); + } + + endStoreLocal(name.asName, b); + } + } + + b.endBlock(); + endSourceSection(b, newStatement); + return null; + } + + @Override + public Void visit(StmtTy.ImportFrom node) { + boolean newStatement = beginSourceSection(node, b); + if (node.getSourceRange().startLine > ctx.futureLineNumber && "__future__".equals(node.module)) { + ctx.errorCallback.onError(ErrorType.Syntax, node.getSourceRange(), "from __future__ imports must occur at the beginning of the file"); + } + + TruffleString tsModuleName = toTruffleStringUncached(node.module == null ? "" : node.module); + + if (node.names[0].name.equals("*")) { + b.emitImportStar(tsModuleName, node.level); + } else { + b.beginBlock(); + + BytecodeLocal module = b.createLocal(); + + TruffleString[] fromList = new TruffleString[node.names.length]; + for (int i = 0; i < fromList.length; i++) { + fromList[i] = toTruffleStringUncached(node.names[i].name); + } + + b.beginStoreLocal(module); + b.emitImport(tsModuleName, fromList, node.level); + b.endStoreLocal(); + + TruffleString[] importedNames = new TruffleString[node.names.length]; + for (int i = 0; i < node.names.length; i++) { + AliasTy alias = node.names[i]; + String asName = alias.asName == null ? alias.name : alias.asName; + beginStoreLocal(asName, b); + + TruffleString name = toTruffleStringUncached(alias.name); + importedNames[i] = name; + b.beginImportFrom(name); + b.emitLoadLocal(module); + b.endImportFrom(); + + endStoreLocal(asName, b); + } + addConstant(importedNames); + + b.endBlock(); + } + + endSourceSection(b, newStatement); + return null; + } + + @Override + public Void visit(StmtTy.Match node) { + boolean newStatement = beginSourceSection(node, b); + b.beginBlock(); + // Compute and store the subject in a local. + BytecodeLocal subject = b.createLocal(); + b.beginStoreLocal(subject); + node.subject.accept(this); + b.endStoreLocal(); + + visitMatchCaseRecursively(node.cases, 0, new PatternContext(subject)); + + b.endBlock(); + endSourceSection(b, newStatement); + return null; + } + + private final class PatternContext { + private final Map bindVariables = new HashMap<>(); + private final BytecodeLocal subject; + private boolean allowIrrefutable = false; + + PatternContext(BytecodeLocal subject) { + this.subject = subject; + } + + public void copySubjectToTemporary(String name) { + BytecodeLocal temporary = allocateBindVariable(name); + b.beginStoreLocal(temporary); + b.emitLoadLocal(subject); + b.endStoreLocal(); + } + + private BytecodeLocal allocateBindVariable(String name) { + checkForbiddenName(name, NameOperation.BeginWrite); + if (bindVariables.containsKey(name)) { + duplicateStoreError(name); + } + BytecodeLocal result = b.createLocal(); + bindVariables.put(name, result); + return result; + } + + private void duplicateStoreError(String name) { + ctx.errorCallback.onError(ErrorType.Syntax, currentLocation, "multiple assignments to name '%s' in pattern", name); + } + + } + + private void visitMatchCaseRecursively(MatchCaseTy[] cases, int index, PatternContext pc) { + /** + * Cases are chained as a sequence of if-then-else clauses, as in: + * + * @formatter:off + * IfThenElse( + * , + * , + * IfThenElse( + * , + * , + * ... + * ) + * ) + * @formatter:on + */ + MatchCaseTy c = cases[index]; + boolean newStatement = beginSourceSection(c, b); + + if (index != cases.length - 1) { + b.beginIfThenElse(); + + // A case that isn't last can be irrefutable only if it is guarded. + pc.allowIrrefutable = c.guard != null; + + emitPatternCondition(c, pc); + visitStatements(c.body); + visitMatchCaseRecursively(cases, index + 1, pc); + b.endIfThenElse(); + } else { + /** + * For the last pattern: if it's an unguarded wildcard _, just emit the body. + * Otherwise, emit an IfThen (no else). + */ + if (wildcardCheck(c.pattern) && c.guard == null) { + visitStatements(c.body); + } else { + b.beginIfThen(); + + // The last case can be irrefutable. + pc.allowIrrefutable = true; + + emitPatternCondition(c, pc); + visitStatements(c.body); + b.endIfThen(); + } + } + + endSourceSection(b, newStatement); + } + + private void emitPatternCondition(MatchCaseTy currentCase, PatternContext pc) { + PatternTy pattern = currentCase.pattern; + ExprTy guard = currentCase.guard; + + /** + * We evaluate conditions using a sequence of boolean computations chained with + * short-circuiting ANDs. If a condition fails at any point, we abort and continue with + * the next pattern. + * + * Patterns can bind variables, but a variable is only bound if the full pattern + * matches. We accumulate the bound values into temporary variables and copy them all + * over only if the pattern matches. For example: + * + * @formatter:off + * IfThenElse( + * And( + * , + * Block( + * + * ... + * , + * true // continue unconditionally + * ), + * + * ), + * , + * ... + * ) + * @formatter:on + */ + b.beginPrimitiveBoolAnd(); + + visitPattern(pattern, pc); + + if (!pc.bindVariables.isEmpty()) { + b.beginBlock(); + + for (Map.Entry entry : pc.bindVariables.entrySet()) { + beginStoreLocal(entry.getKey(), b); + b.emitLoadLocal(entry.getValue()); + endStoreLocal(entry.getKey(), b); + } + + b.emitLoadConstant(true); + b.endBlock(); + } + if (guard != null) { + guard.accept(this); + } + b.endPrimitiveBoolAnd(); + } + + /** + * Generates code to test a {@code pattern} against the value stored in {@code subject}. + * + * Invariants: + *

    + *
  • The code for each pattern produces a boolean value. + *
  • When the pattern has a variable binding, the code will use the {@code pc} to allocate + * a new temporary variable to store the value of the binding. If the pattern match + * succeeds, only then will we copy the temporaries into Python-level variables. + *
  • The {@code pc.subject} variable always contains the value to match against a pattern. + * When performing structural recursion on a value, the original value will be overwritten + * unless saved in a new local. + *
+ */ + private void visitPattern(PatternTy pattern, PatternContext pc) { + boolean newStatement = beginSourceSection(pattern, b); + if (pattern instanceof PatternTy.MatchAs matchAs) { + doVisitPattern(matchAs, pc); + } else if (pattern instanceof PatternTy.MatchClass matchClass) { + doVisitPattern(matchClass); + } else if (pattern instanceof PatternTy.MatchMapping matchMapping) { + doVisitPattern(matchMapping); + } else if (pattern instanceof PatternTy.MatchOr matchOr) { + doVisitPattern(matchOr); + } else if (pattern instanceof PatternTy.MatchSequence matchSequence) { + doVisitPattern(matchSequence, pc); + } else if (pattern instanceof PatternTy.MatchSingleton matchSingleton) { + doVisitPattern(matchSingleton, pc); + } else if (pattern instanceof PatternTy.MatchStar matchStar) { + doVisitPattern(matchStar, pc); + } else if (pattern instanceof PatternTy.MatchValue matchValue) { + doVisitPattern(matchValue, pc); + } else { + throw CompilerDirectives.shouldNotReachHere(); + } + endSourceSection(b, newStatement); + } + + // In a subpattern, irrefutable patterns are OK. + private void visitSubpattern(PatternTy pattern, PatternContext pc) { + boolean allowIrrefutable = pc.allowIrrefutable; + pc.allowIrrefutable = true; + visitPattern(pattern, pc); + pc.allowIrrefutable = allowIrrefutable; + } + + private void doVisitPattern(PatternTy.MatchAs node, PatternContext pc) { + b.beginBlock(); + if (node.name != null) { + pc.copySubjectToTemporary(node.name); + } + + if (node.pattern == null) { + // If there's no pattern (e.g., _), it trivially matches. Ensure this is permitted. + if (!pc.allowIrrefutable) { + if (node.name != null) { + ctx.errorCallback.onError(ErrorType.Syntax, currentLocation, "name capture '%s' makes remaining patterns unreachable", node.name); + } + ctx.errorCallback.onError(ErrorType.Syntax, currentLocation, "wildcard makes remaining patterns unreachable"); + } + b.emitLoadConstant(true); + } else { + assert node.name != null : "name should only be null for the empty wildcard pattern '_'"; + visitPattern(node.pattern, pc); + } + + b.endBlock(); + } + + private void emitPatternNotImplemented(String kind) { + b.beginBlock(); + emitNotImplemented(kind + " pattern matching", b); + // we need a value producing operation + b.emitLoadConstant(false); + b.endBlock(); + } + + private void doVisitPattern(PatternTy.MatchClass node) { + emitPatternNotImplemented("class"); + } + + private void doVisitPattern(PatternTy.MatchMapping node) { + emitPatternNotImplemented("mapping"); + } + + private void doVisitPattern(PatternTy.MatchOr node) { + emitPatternNotImplemented("OR"); + } + + private void patternHelperSequenceUnpack(PatternTy[] patterns, PatternContext pc) { + int n = len(patterns); + + b.beginBlock(); + // We need to remember the unpacked array, since subject will be overwritten in + // recursive calls. + BytecodeLocal unpacked = b.createLocal(); + b.beginStoreLocal(unpacked); + patternUnpackHelper(patterns, pc); + b.endStoreLocal(); + + for (int i = 0; i < n; i++) { + b.beginStoreLocal(pc.subject); + b.beginArrayIndex(); + b.emitLoadLocal(unpacked); + b.emitLoadConstant(i); + b.endArrayIndex(); + b.endStoreLocal(); + + visitSubpattern(patterns[i], pc); + } + b.endBlock(); + } + + private void patternUnpackHelper(PatternTy[] patterns, PatternContext pc) { + int n = len(patterns); + + boolean seenStar = false; + for (int i = 0; i < n; i++) { + PatternTy pattern = patterns[i]; + if (pattern instanceof PatternTy.MatchStar) { + if (seenStar) { + ctx.errorCallback.onError(ErrorType.Syntax, currentLocation, "multiple starred expressions in sequence pattern"); + } + seenStar = true; + int countAfter = n - i - 1; + if (countAfter != (byte) countAfter) { + ctx.errorCallback.onError(ErrorType.Syntax, currentLocation, "too many expressions in star-unpacking sequence pattern"); + } + // If there's a star pattern, emit UnpackEx. + b.beginUnpackEx(i, countAfter); + b.emitLoadLocal(pc.subject); + b.endUnpackEx(); + // Continue in the loop to ensure there are no additional starred patterns. + } + } + // If there were no star patterns, emit UnpackSequence. + if (!seenStar) { + b.beginUnpackSequence(n); + b.emitLoadLocal(pc.subject); + b.endUnpackSequence(); + } + } + + /** + * Like patternHelperSequenceUnpack, but uses subscripting, which is (likely) more efficient + * for patterns with a starred wildcard like [first, *_], [first, *_, last], [*_, last], + * etc. + */ + private void patternHelperSequenceSubscr(PatternTy[] patterns, int star, PatternContext pc) { + int n = len(patterns); + + b.beginBlock(); + // We need to remember the sequence, since subject will be overwritten in recursive + // calls. + BytecodeLocal sequence = b.createLocal(); + b.beginStoreLocal(sequence); + b.emitLoadLocal(pc.subject); + b.endStoreLocal(); + + for (int i = 0; i < n; i++) { + PatternTy pattern = patterns[i]; + if (wildcardCheck(pattern)) { + // nothing to check + continue; + } else if (i == star) { + // nothing to check + assert wildcardStarCheck(pattern); + continue; + } + + b.beginStoreLocal(pc.subject); + b.beginBinarySubscript(); + b.emitLoadLocal(sequence); + if (i < star) { + b.emitLoadConstant(i); + } else { + // The subject may not support negative indexing! Compute a + // nonnegative index: + b.beginSub(); + + b.beginGetLen(); + b.emitLoadLocal(sequence); + b.endGetLen(); + + b.emitLoadConstant(n - i); + + b.endSub(); + } + b.endBinarySubscript(); + b.endStoreLocal(); + + visitSubpattern(pattern, pc); + } + b.endBlock(); + } + + private void doVisitPattern(PatternTy.MatchSequence node, PatternContext pc) { + int size = len(node.patterns); + int star = -1; + boolean onlyWildcard = true; + boolean starWildcard = false; + + // Find a starred name, if it exists. There may be at most one: + for (int i = 0; i < size; i++) { + PatternTy pattern = node.patterns[i]; + if (pattern instanceof PatternTy.MatchStar) { + if (star >= 0) { + ctx.errorCallback.onError(ErrorType.Syntax, node.getSourceRange(), "multiple starred names in sequence pattern"); + } + starWildcard = wildcardStarCheck(pattern); + onlyWildcard &= starWildcard; + star = i; + continue; + } + onlyWildcard &= wildcardCheck(pattern); + } + + b.beginPrimitiveBoolAnd(); + + b.beginIsSequence(); + b.emitLoadLocal(pc.subject); + b.endIsSequence(); + + if (star < 0) { + // No star: len(subject) == size + b.beginEq(); + b.beginGetLen(); + b.emitLoadLocal(pc.subject); + b.endGetLen(); + b.emitLoadConstant(size); + b.endEq(); + } else if (size > 1) { + // Star: len(subject) >= size - 1 + b.beginGe(); + b.beginGetLen(); + b.emitLoadLocal(pc.subject); + b.endGetLen(); + b.emitLoadConstant(size - 1); + b.endGe(); + } + + if (onlyWildcard) { + /** + * For patterns like: [] / [_] / [_, _] / [*_] / [_, *_] / [_, _, *_] / etc., there + * is nothing more to check. + */ + } else if (starWildcard) { + /** + * For sequences with a *_ pattern, it is (likely) more efficient to extract the + * bound elements with subscripting rather than iterating the entire collection. + */ + patternHelperSequenceSubscr(node.patterns, star, pc); + } else { + /** + * Otherwise, unpack the sequence element-by-element. If there's a named * pattern, + * collect the rest into it. + */ + patternHelperSequenceUnpack(node.patterns, pc); + } + + b.endPrimitiveBoolAnd(); + } + + private void doVisitPattern(PatternTy.MatchSingleton node, PatternContext pc) { + b.beginIs(); + b.emitLoadLocal(pc.subject); + + switch (node.value.kind) { + case BOOLEAN: + b.emitLoadConstant(node.value.getBoolean()); + break; + case NONE: + b.emitLoadConstant(PNone.NONE); + break; + default: + throw new IllegalStateException("wrong MatchSingleton value kind " + node.value.kind); + } + b.endIs(); + } + + private void doVisitPattern(PatternTy.MatchStar node, PatternContext pc) { + if (node.name != null) { + b.beginBlock(); + pc.copySubjectToTemporary(node.name); + b.emitLoadConstant(true); + b.endBlock(); + } + /** + * If there's no name, no need to emit anything. A MatchStar can only appear as a + * subpattern of a mapping/sequence pattern, at which point in code generation we will + * be in the middle of a short-circuiting AND (that already has at least one operand) + */ + } + + private void doVisitPattern(PatternTy.MatchValue node, PatternContext pc) { + b.beginEq(); + b.emitLoadLocal(pc.subject); + + if (node.value instanceof ExprTy.UnaryOp || node.value instanceof ExprTy.BinOp) { + createConstant(foldConstantOp(node.value)); + } else if (node.value instanceof ExprTy.Constant || node.value instanceof ExprTy.Attribute) { + node.value.accept(this); + } else { + ctx.errorCallback.onError(ErrorType.Syntax, currentLocation, "patterns may only match literals and attribute lookups"); + } + b.endEq(); + } + + private static boolean wildcardCheck(PatternTy pattern) { + return pattern instanceof PatternTy.MatchAs && ((PatternTy.MatchAs) pattern).name == null; + } + + private static boolean wildcardStarCheck(PatternTy pattern) { + return pattern instanceof PatternTy.MatchStar && ((PatternTy.MatchStar) pattern).name == null; + } + + /** + * handles only particular cases when a constant comes either as a unary or binary op + */ + private ConstantValue foldConstantOp(ExprTy value) { + if (value instanceof ExprTy.UnaryOp unaryOp) { + return foldUnaryOpConstant(unaryOp); + } else if (value instanceof ExprTy.BinOp binOp) { + return foldBinOpComplexConstant(binOp); + } + throw new IllegalStateException("should not reach here"); + } + + /** + * handles only unary sub and a numeric constant + */ + private ConstantValue foldUnaryOpConstant(ExprTy.UnaryOp unaryOp) { + assert unaryOp.op == UnaryOpTy.USub; + assert unaryOp.operand instanceof ExprTy.Constant : unaryOp.operand; + ExprTy.Constant c = (ExprTy.Constant) unaryOp.operand; + ConstantValue ret = c.value.negate(); + assert ret != null; + return ret; + } + + /** + * handles only complex which comes as a BinOp + */ + private ConstantValue foldBinOpComplexConstant(ExprTy.BinOp binOp) { + assert (binOp.left instanceof ExprTy.UnaryOp || binOp.left instanceof ExprTy.Constant) && binOp.right instanceof ExprTy.Constant : binOp.left + " " + binOp.right; + assert binOp.op == OperatorTy.Sub || binOp.op == OperatorTy.Add; + ConstantValue left; + if (binOp.left instanceof ExprTy.UnaryOp) { + left = foldUnaryOpConstant((ExprTy.UnaryOp) binOp.left); + } else { + left = ((ExprTy.Constant) binOp.left).value; + } + ExprTy.Constant right = (ExprTy.Constant) binOp.right; + switch (binOp.op) { + case Add: + return left.addComplex(right.value); + case Sub: + return left.subComplex(right.value); + default: + throw new IllegalStateException("wrong constant BinOp operator " + binOp.op); + } + } + + @Override + public Void visit(MatchCaseTy node) { + throw new UnsupportedOperationException("" + node.getClass()); + } + + @Override + public Void visit(PatternTy.MatchAs node) { + throw new UnsupportedOperationException("" + node.getClass()); + } + + @Override + public Void visit(PatternTy.MatchClass node) { + throw new UnsupportedOperationException("" + node.getClass()); + } + + @Override + public Void visit(PatternTy.MatchMapping node) { + throw new UnsupportedOperationException("" + node.getClass()); + } + + @Override + public Void visit(PatternTy.MatchOr node) { + throw new UnsupportedOperationException("" + node.getClass()); + } + + @Override + public Void visit(PatternTy.MatchSequence node) { + throw new UnsupportedOperationException("" + node.getClass()); + } + + @Override + public Void visit(PatternTy.MatchSingleton node) { + throw new UnsupportedOperationException("" + node.getClass()); + } + + @Override + public Void visit(PatternTy.MatchStar node) { + throw new UnsupportedOperationException("" + node.getClass()); + } + + @Override + public Void visit(PatternTy.MatchValue node) { + throw new UnsupportedOperationException("" + node.getClass()); + } + + @Override + public Void visit(StmtTy.Nonlocal node) { + return null; + } + + @Override + public Void visit(StmtTy.Raise node) { + boolean newStatement = beginSourceSection(node, b); + b.beginRaise(); + + if (node.exc != null) { + node.exc.accept(this); + } else { + b.emitLoadConstant(PNone.NO_VALUE); + } + + if (node.cause != null) { + node.cause.accept(this); + } else { + b.emitLoadConstant(PNone.NO_VALUE); + } + + b.endRaise(); + endSourceSection(b, newStatement); + return null; + } + + @Override + public Void visit(StmtTy.Return node) { + if (!scope.isFunction()) { + ctx.errorCallback.onError(ErrorType.Syntax, currentLocation, "'return' outside function"); + } + boolean newStatement = beginSourceSection(node, b); + beginReturn(b); + if (node.value != null) { + node.value.accept(this); + } else { + b.emitLoadConstant(PNone.NONE); + } + endReturn(b); + endSourceSection(b, newStatement); + return null; + } + + @Override + public Void visit(StmtTy.Try node) { + boolean newStatement = beginSourceSection(node, b); + if (node.finalBody != null && node.finalBody.length != 0) { + /** + * In Python, an uncaught exception becomes the "current" exception inside a finally + * block. The finally body can itself throw, in which case it replaces the exception + * being thrown. For such a scenario, we have to be careful to restore the "current" + * exception using a try-finally. + * + * In pseudocode, the implementation looks like: + * @formatter:off + * try { + * try_catch_else + * } finally { + * finally_body + * } catch uncaught_ex { + * save current exception + * set the current exception to uncaught_ex + * markCaught(uncaught_ex) + * try { + * finally_body + * } finally { + * restore current exception + * } catch handler_ex { + * restore current exception + * markCaught(handler_ex) + * reraise handler_ex + * } + * reraise uncaught_ex + * } + */ + b.beginTryFinallyCatch(() -> { + b.beginBlock(); // finally + visitSequence(node.finalBody); + b.endBlock(); + }); + + emitTryExceptElse(node); // try + + b.beginBlock(); // catch + BytecodeLocal savedException = b.createLocal(); + emitSaveCurrentException(savedException); + emitSetCurrentException(); + // Mark this location for the stack trace. + b.beginMarkExceptionAsCaught(); + b.emitLoadException(); + b.endMarkExceptionAsCaught(); + + b.beginTryFinallyCatch(() -> emitRestoreCurrentException(savedException)); + b.beginBlock(); // try + visitSequence(node.finalBody); + b.endBlock(); // try + + b.beginBlock(); // catch + emitRestoreCurrentException(savedException); + + b.beginMarkExceptionAsCaught(); + b.emitLoadException(); + b.endMarkExceptionAsCaught(); + + b.beginReraise(); + b.emitLoadException(); + b.endReraise(); + b.endBlock(); // catch + b.endTryFinallyCatch(); + + b.beginReraise(); + b.emitLoadException(); + b.endReraise(); + b.endBlock(); // catch + b.endTryFinallyCatch(); + // @formatter:on + } else { + emitTryExceptElse(node); + } + + endSourceSection(b, newStatement); + return null; + } + + /** + * Emit the "try-except-else" part of a Try node. The "finally" part, if it exists, should + * be handled by the caller of this method. + */ + private void emitTryExceptElse(StmtTy.Try node) { + if (node.handlers != null && node.handlers.length != 0) { + /** + * There are two orthogonal issues that complicate Python try-except clauses. + * + * First, when in an exception handler, the "current" exception (accessible via, e.g., + * sys.exc_info) gets set to the caught exception. After leaving the handler, this + * "current" exception must be restored to the one previously stored. Since except + * clauses can themselves raise exceptions, the restoring process must happen inside + * a finally block. + * + * Second, when an exception is bound to an identifier (e.g., except BaseException as + * ex), the identifier must be deleted after leaving the except clause. Again, since + * the except clause may raise an exception, the deletion must happen inside a finally + * block. Since the bound name is different in each clause, this block is specific to + * each handler. + * + * @formatter:off + * try { + * try_body + * # fall through to else_body + * } catch ex { + * save current exception + * set current exception to ex + * markCaught(ex) + * try { + * if (handler_1_matches(ex)) { + * assign ex to handler_1_name + * try { + * handler_1_body + * } finally { + * unbind handler_1_name + * } catch handler_1_ex { + * unbind handler_1_name + * // Freeze the bci before it gets rethrown. + * markCaught(handler_ex) + * throw handler_1_ex + * } + * goto afterElse + * } + * ... // more handlers + * + * // case 1: bare except + * bare_except_body + * goto afterElse + * } finally { + * // Exception handled. Restore the exception state. + * restore current exception + * } catch handler_ex { + * // A handler raised or no handler was found. Restore exception state and reraise. + * restore current exception + * markCaught(handler_ex) // (no-op if handler_ex is the original exception) + * reraise handler_ex + * } + * // case 2: no bare except (we only reach this point if no handler matched/threw) + * reraise ex + * } + * else_body + * afterElse: + */ + b.beginBlock(); // outermost block + + BytecodeLocal savedException = b.createLocal(); + BytecodeLabel afterElse = b.createLabel(); + + b.beginTryCatch(); + + b.beginBlock(); // try + visitSequence(node.body); + b.endBlock(); // try + + b.beginBlock(); // catch + emitSaveCurrentException(savedException); + emitSetCurrentException(); + // Mark this location for the stack trace. + b.beginMarkExceptionAsCaught(); + b.emitLoadException(); // ex + b.endMarkExceptionAsCaught(); + + b.beginTryFinallyCatch(() -> emitRestoreCurrentException(savedException)); + b.beginBlock(); // try + SourceRange bareExceptRange = null; + for (ExceptHandlerTy h : node.handlers) { +boolean newStatement = beginSourceSection(h, b); + if (bareExceptRange != null) { + ctx.errorCallback.onError(ErrorType.Syntax, currentLocation, "default 'except:' must be last"); + } + + ExceptHandlerTy.ExceptHandler handler = (ExceptHandlerTy.ExceptHandler) h; + if (handler.type != null) { + b.beginIfThen(); + b.beginExceptMatch(); + b.emitLoadException(); // ex + handler.type.accept(this); + b.endExceptMatch(); + } else { + bareExceptRange = handler.getSourceRange(); + } + + b.beginBlock(); // handler body + + if (handler.name != null) { + // Assign exception to handler name. + beginStoreLocal(handler.name, b); + b.beginUnwrapException(); + b.emitLoadException(); // ex + b.endUnwrapException(); + endStoreLocal(handler.name, b); + + b.beginTryFinallyCatch(() -> emitUnbindHandlerVariable(handler)); + b.beginBlock(); // try + visitSequence(handler.body); + b.endBlock(); // try + + b.beginBlock(); // catch + emitUnbindHandlerVariable(handler); + + b.beginMarkExceptionAsCaught(); + b.emitLoadException(); // handler_i_ex + b.endMarkExceptionAsCaught(); + + b.beginThrow(); + b.emitLoadException(); // handler_i_ex + b.endThrow(); + b.endBlock(); // catch + b.endTryFinallyCatch(); + } else { // bare except + b.beginBlock(); + visitSequence(handler.body); + b.endBlock(); + } + + b.emitBranch(afterElse); + + b.endBlock(); // handler body + + if (handler.type != null) { + b.endIfThen(); + } + + endSourceSection(b, newStatement); + } + b.endBlock(); // try + + b.beginBlock(); // catch + emitRestoreCurrentException(savedException); + + b.beginMarkExceptionAsCaught(); + b.emitLoadException(); // handler_ex + b.endMarkExceptionAsCaught(); + + b.beginReraise(); + b.emitLoadException(); // handler_ex + b.endReraise(); + b.endBlock(); // catch + b.endTryFinallyCatch(); + + /** + * Each handler branches to afterElse. If we reach this point and there was not a + * bare exception, none of the handlers matched, and we should reraise. + * Optimization: If there's a bare except clause, control will never fall through + * and we can omit the rethrow. + */ + if (bareExceptRange == null) { + b.beginReraise(); + b.emitLoadException(); // ex + b.endReraise(); + } + + b.endBlock(); // catch + + b.endTryCatch(); + + if (node.orElse != null) { + visitSequence(node.orElse); + } + b.emitLabel(afterElse); + + b.endBlock(); // outermost block + // @formatter:on + } else { + // Optimization: If there's no except clauses, there's no point in generating a + // TryCatch with a catch that just rethrows the caught exception. + b.beginBlock(); + visitSequence(node.body); + b.endBlock(); + } + } + + private void emitSaveCurrentException(BytecodeLocal savedException) { + b.beginStoreLocal(savedException); + b.emitGetCurrentException(); + b.endStoreLocal(); + } + + private void emitSetCurrentException() { + b.beginSetCurrentException(); + b.emitLoadException(); + b.endSetCurrentException(); + } + + private void emitRestoreCurrentException(BytecodeLocal savedException) { + b.beginSetCurrentException(); + b.emitLoadLocal(savedException); + b.endSetCurrentException(); + } + + private void emitUnbindHandlerVariable(ExceptHandlerTy.ExceptHandler handler) { + b.beginBlock(); + // Store None to the variable just in case the handler deleted it. + beginStoreLocal(handler.name, b); + b.emitLoadConstant(PNone.NONE); + endStoreLocal(handler.name, b); + emitDelLocal(handler.name, b); + b.endBlock(); + } + + @Override + public Void visit(StmtTy.TryStar node) { + emitNotImplemented("try star", b); + return null; + } + + @Override + public Void visit(ExceptHandlerTy.ExceptHandler node) { + throw new UnsupportedOperationException("" + node.getClass()); + } + + @Override + public Void visit(StmtTy.While node) { + boolean newStatement = beginSourceSection(node, b); + b.beginBlock(); + + BytecodeLabel oldBreakLabel = breakLabel; + BytecodeLabel oldContinueLabel = continueLabel; + + BytecodeLabel currentBreakLabel = b.createLabel(); + breakLabel = currentBreakLabel; + + b.beginWhile(); + + b.beginBlock(); + b.emitTraceLineAtLoopHeader(currentLocation.startLine); + visitCondition(node.test); + b.endBlock(); + + b.beginBlock(); + continueLabel = b.createLabel(); + visitStatements(node.body); + b.emitLabel(continueLabel); + b.endBlock(); + + b.endWhile(); + + breakLabel = oldBreakLabel; + continueLabel = oldContinueLabel; + visitStatements(node.orElse); + b.emitLabel(currentBreakLabel); + + b.endBlock(); + endSourceSection(b, newStatement); + return null; + } + + private void visitWithRecurse(WithItemTy[] items, int index, StmtTy[] body, boolean async) { + /** + * For a with-statement like + * + * with foo as x: + * bar + * + * we generate code that performs (roughly) + * + * @formatter:off + * contextManager = foo + * resolve __enter__ and __exit__ + * value = __enter__() + * try { + * x = value + * bar + * } finally { + * call __exit__(None, None, None) + * } catch ex { + * if not __exit__(...): + * raise + * } + * @formatter:on + * + * When there are multiple context managers, they are recursively generated (where "bar" + * is). Once we have entered all of the context managers, we emit the body. + */ + WithItemTy item = items[index]; + boolean newStatement = beginSourceSection(item, b); + b.beginBlock(); + + BytecodeLocal contextManager = b.createLocal(); + b.beginStoreLocal(contextManager); + item.contextExpr.accept(this); + b.endStoreLocal(); + + BytecodeLocal exit = b.createLocal(); + BytecodeLocal value = b.createLocal(); + if (async) { + // call __aenter__ + b.beginAsyncContextManagerEnter(exit, value); + b.emitLoadLocal(contextManager); + b.endAsyncContextManagerEnter(); + // await the result + emitAwait(() -> b.emitLoadLocal(value)); + } else { + // call __enter__ + b.beginContextManagerEnter(exit, value); + b.emitLoadLocal(contextManager); + b.endContextManagerEnter(); + } + + Runnable finallyHandler; + if (async) { + finallyHandler = () -> emitAwait(() -> { + b.beginAsyncContextManagerCallExit(); + b.emitLoadConstant(PNone.NONE); + b.emitLoadLocal(exit); + b.emitLoadLocal(contextManager); + b.endAsyncContextManagerCallExit(); + }); + } else { + finallyHandler = () -> { + // call __exit__ + b.beginContextManagerExit(); + b.emitLoadConstant(PNone.NONE); + b.emitLoadLocal(exit); + b.emitLoadLocal(contextManager); + b.endContextManagerExit(); + }; + } + b.beginTryFinallyCatch(finallyHandler); + b.beginBlock(); // try + if (item.optionalVars != null) { + item.optionalVars.accept(new StoreVisitor(() -> b.emitLoadLocal(value))); + } + if (index < items.length - 1) { + visitWithRecurse(items, index + 1, body, async); + } else { + visitSequence(body); + } + b.endBlock(); // try + + b.beginBlock(); // catch + + // Mark this location for the stack trace. + b.beginMarkExceptionAsCaught(); + b.emitLoadException(); + b.endMarkExceptionAsCaught(); + + // exceptional exit + if (async) { + // call, await, and handle result of __aexit__ + b.beginAsyncContextManagerExit(); + b.emitLoadException(); + emitAwait(() -> { + b.beginAsyncContextManagerCallExit(); + b.emitLoadException(); + b.emitLoadLocal(exit); + b.emitLoadLocal(contextManager); + b.endAsyncContextManagerCallExit(); + }); + b.endAsyncContextManagerExit(); + } else { + // call __exit__ + b.beginContextManagerExit(); + b.emitLoadException(); + b.emitLoadLocal(exit); + b.emitLoadLocal(contextManager); + b.endContextManagerExit(); + } + b.endBlock(); // catch + + b.endTryFinallyCatch(); + b.endBlock(); + endSourceSection(b, newStatement); + } + + @Override + public Void visit(StmtTy.With node) { + boolean newStatement = beginSourceSection(node, b); + visitWithRecurse(node.items, 0, node.body, false); + endSourceSection(b, newStatement); + return null; + } + + @Override + public Void visit(WithItemTy node) { + throw new UnsupportedOperationException("" + node.getClass()); + } + + @Override + public Void visit(StmtTy.Break aThis) { + if (breakLabel == null) { + ctx.errorCallback.onError(ErrorType.Syntax, currentLocation, "'break' outside loop"); + } + boolean newStatement = beginSourceSection(aThis, b); + b.emitBranch(breakLabel); + endSourceSection(b, newStatement); + return null; + } + + @Override + public Void visit(StmtTy.Continue aThis) { + if (continueLabel == null) { + ctx.errorCallback.onError(ErrorType.Syntax, currentLocation, "'continue' not properly in loop"); + } + boolean newStatement = beginSourceSection(aThis, b); + b.emitBranch(continueLabel); + endSourceSection(b, newStatement); + return null; + } + + @Override + public Void visit(StmtTy.Pass aThis) { + return null; + } + } +} diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyObjectStrAsTruffleStringNode.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyObjectStrAsTruffleStringNode.java index a3a06130c4..f1fdc37556 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyObjectStrAsTruffleStringNode.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyObjectStrAsTruffleStringNode.java @@ -73,7 +73,7 @@ public static TruffleString executeUncached(Object object) { public abstract TruffleString execute(Frame frame, Node inliningTarget, Object object); @Specialization - static TruffleString doString(TruffleString obj) { + static TruffleString doString(Node inliningTarget, TruffleString obj) { return obj; } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/PGuards.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/PGuards.java index c693b01b50..d7da59f41f 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/PGuards.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/PGuards.java @@ -105,6 +105,7 @@ import com.oracle.graal.python.lib.PyIndexCheckNode; import com.oracle.graal.python.nodes.attributes.LookupCallableSlotInMRONode; import com.oracle.graal.python.nodes.object.GetClassNode.GetPythonObjectClassNode; +import com.oracle.graal.python.runtime.PythonOptions; import com.oracle.graal.python.runtime.exception.PException; import com.oracle.graal.python.runtime.sequence.PSequence; import com.oracle.graal.python.runtime.sequence.storage.ByteSequenceStorage; @@ -712,4 +713,9 @@ public static boolean isNullOrZero(Object value, InteropLibrary lib) { public static boolean hasBuiltinDictIter(Node inliningTarget, PDict dict, GetPythonObjectClassNode getClassNode, LookupCallableSlotInMRONode lookupIter) { return isBuiltinDict(dict) || lookupIter.execute(getClassNode.execute(inliningTarget, dict)) == BuiltinMethodDescriptors.DICT_ITER; } + + @Idempotent + public static boolean isBytecodeDSLInterpreter() { + return PythonOptions.ENABLE_BYTECODE_DSL_INTERPRETER; + } } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/PRootNode.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/PRootNode.java index 1f56957fff..46b56a408e 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/PRootNode.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/PRootNode.java @@ -61,19 +61,19 @@ public abstract class PRootNode extends RootNode { private final ConditionProfile frameEscaped = ConditionProfile.create(); - @CompilationFinal private Assumption dontNeedCallerFrame = createCallerFrameAssumption(); + @CompilationFinal private transient Assumption dontNeedCallerFrame = createCallerFrameAssumption(); /** * Flag indicating if some child node of this root node (or a callee) eventually needs the * exception state. Hence, the caller of this root node should provide the exception state in * the arguments. */ - @CompilationFinal private Assumption dontNeedExceptionState = createExceptionStateAssumption(); + @CompilationFinal private transient Assumption dontNeedExceptionState = createExceptionStateAssumption(); - private int nodeCount = -1; + private transient int nodeCount = -1; // contains the code of this root node in marshaled/serialized form - private byte[] code; + private transient byte[] code; protected PRootNode(TruffleLanguage language) { super(language); diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/StringLiterals.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/StringLiterals.java index 3259a130f0..04583fa732 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/StringLiterals.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/StringLiterals.java @@ -144,6 +144,7 @@ public abstract class StringLiterals { public static final TruffleString T_EXEC = tsLiteral("exec"); public static final TruffleString T_EVAL = tsLiteral("eval"); public static final TruffleString T_FUNC_TYPE = tsLiteral("func_type"); + public static final TruffleString T_SUPER = tsLiteral("super"); public static final String J_OB_REFCNT = "ob_refcnt"; public static final String J_DEBUG = "debug"; public static final String J_TRACE = "trace"; diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/builtins/ListNodes.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/builtins/ListNodes.java index 48c7b8935c..b186d64f1a 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/builtins/ListNodes.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/builtins/ListNodes.java @@ -84,6 +84,7 @@ import com.oracle.graal.python.runtime.sequence.storage.SequenceStorage; import com.oracle.truffle.api.CompilerDirectives; import com.oracle.truffle.api.HostCompilerDirectives.InliningCutoff; +import com.oracle.truffle.api.bytecode.OperationProxy; import com.oracle.truffle.api.dsl.Bind; import com.oracle.truffle.api.dsl.Cached; import com.oracle.truffle.api.dsl.Cached.Shared; @@ -316,6 +317,7 @@ Object doGeneric(VirtualFrame frame, Object object) { */ @GenerateUncached @GenerateInline(false) // footprint reduction 36 -> 17 + @OperationProxy.Proxyable public abstract static class AppendNode extends PNodeWithContext { private static final BranchProfile[] DISABLED = new BranchProfile[]{BranchProfile.getUncached()}; diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/bytecode/BytecodeFrameInfo.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/bytecode/BytecodeFrameInfo.java new file mode 100644 index 0000000000..33f588abf1 --- /dev/null +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/bytecode/BytecodeFrameInfo.java @@ -0,0 +1,60 @@ +package com.oracle.graal.python.nodes.bytecode; + +import com.oracle.graal.python.compiler.CodeUnit; +import com.oracle.graal.python.compiler.OpCodesConstants; +import com.oracle.truffle.api.CompilerDirectives.CompilationFinal; +import com.oracle.truffle.api.frame.Frame; + +public class BytecodeFrameInfo implements FrameInfo { + @CompilationFinal PBytecodeRootNode rootNode; + + void setRootNode(PBytecodeRootNode rootNode) { + this.rootNode = rootNode; + } + + @Override + public PBytecodeRootNode getRootNode() { + return rootNode; + } + + public int getBci(Frame frame) { + if (frame.isInt(rootNode.bcioffset)) { + return frame.getInt(rootNode.bcioffset); + } else { + return -1; + } + } + + public int getLineForBci(int bci) { + return rootNode.bciToLine(bci); + } + + public int getLine(Frame frame) { + return getLineForBci(getBci(frame)); + } + + @Override + public int getFirstLineNumber() { + return rootNode.getFirstLineno(); + } + + @Override + public Object getYieldFrom(Frame generatorFrame, int bci, int stackTop) { + /* Match the `yield from` bytecode pattern and get the object from stack */ + if (bci > 3 && bci < rootNode.bytecode.length && rootNode.bytecode[bci - 3] == OpCodesConstants.SEND && rootNode.bytecode[bci - 1] == OpCodesConstants.YIELD_VALUE && + rootNode.bytecode[bci] == OpCodesConstants.RESUME_YIELD) { + return generatorFrame.getObject(stackTop); + } + return null; + } + + @Override + public CodeUnit getCodeUnit() { + return rootNode.getCodeUnit(); + } + + @Override + public boolean includeInTraceback() { + return rootNode.frameIsVisibleToPython(); + } +} diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/bytecode/FrameInfo.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/bytecode/FrameInfo.java index 97a4d69844..ddfdfbe8b0 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/bytecode/FrameInfo.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/bytecode/FrameInfo.java @@ -41,8 +41,7 @@ package com.oracle.graal.python.nodes.bytecode; import com.oracle.graal.python.compiler.CodeUnit; -import com.oracle.graal.python.compiler.OpCodesConstants; -import com.oracle.truffle.api.CompilerDirectives.CompilationFinal; +import com.oracle.graal.python.nodes.PRootNode; import com.oracle.truffle.api.dsl.Idempotent; import com.oracle.truffle.api.frame.Frame; import com.oracle.truffle.api.frame.FrameDescriptor; @@ -53,38 +52,30 @@ * returned by {@link FrameDescriptor#getInfo()} if and only if the frame is coming from the * bytecode interpreter. */ -public final class FrameInfo { - @CompilationFinal PBytecodeRootNode rootNode; +public interface FrameInfo { - public PBytecodeRootNode getRootNode() { - return rootNode; - } + public PRootNode getRootNode(); - public int getBci(Frame frame) { - if (frame.isInt(rootNode.bcioffset)) { - return frame.getInt(rootNode.bcioffset); - } else { - return -1; - } - } + public int getFirstLineNumber(); - public Object getYieldFrom(Frame generatorFrame, int bci, int stackTop) { - /* Match the `yield from` bytecode pattern and get the object from stack */ - if (bci > 3 && bci < rootNode.bytecode.length && rootNode.bytecode[bci - 3] == OpCodesConstants.SEND && rootNode.bytecode[bci - 1] == OpCodesConstants.YIELD_VALUE && - rootNode.bytecode[bci] == OpCodesConstants.RESUME_YIELD) { - return generatorFrame.getObject(stackTop); - } - return null; - } + public Object getYieldFrom(Frame generatorFrame, int bci, int stackTop); + + public boolean includeInTraceback(); + + public CodeUnit getCodeUnit(); @Idempotent - public int getVariableCount() { - CodeUnit code = rootNode.getCodeUnit(); + public default int getVariableCount() { + CodeUnit code = getCodeUnit(); return code.varnames.length + code.cellvars.length + code.freevars.length; } - public TruffleString getVariableName(int slot) { - CodeUnit code = rootNode.getCodeUnit(); + public default int getRegularVariableCount() { + return getCodeUnit().varnames.length; + } + + public default TruffleString getVariableName(int slot) { + CodeUnit code = getCodeUnit(); if (slot < code.varnames.length) { return code.varnames[slot]; } else if (slot < code.varnames.length + code.cellvars.length) { diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/bytecode/GetANextNode.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/bytecode/GetANextNode.java index deb1141529..efc67f3d54 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/bytecode/GetANextNode.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/bytecode/GetANextNode.java @@ -59,7 +59,7 @@ import com.oracle.truffle.api.dsl.GenerateUncached; import com.oracle.truffle.api.dsl.ImportStatic; import com.oracle.truffle.api.dsl.Specialization; -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.api.profiles.InlinedBranchProfile; @@ -67,7 +67,7 @@ @ImportStatic(SpecialMethodSlot.class) @GenerateInline(false) // used in bytecode root node public abstract class GetANextNode extends PNodeWithContext { - public abstract Object execute(Frame frame, Object receiver); + public abstract Object execute(VirtualFrame frame, Object receiver); public static GetANextNode getUncached() { return GetANextNodeGen.getUncached(); @@ -78,7 +78,7 @@ public static GetANextNode create() { } @Specialization - Object doGeneric(Frame frame, Object receiver, + Object doGeneric(VirtualFrame frame, Object receiver, @Bind("this") Node inliningTarget, @Cached(parameters = "ANext") LookupSpecialMethodSlotNode getANext, @Cached GetClassNode getAsyncIterType, diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/bytecode/GetSendValueNode.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/bytecode/GetSendValueNode.java index 45587123f8..02ca48c2e7 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/bytecode/GetSendValueNode.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/bytecode/GetSendValueNode.java @@ -47,6 +47,7 @@ import com.oracle.truffle.api.dsl.Fallback; import com.oracle.truffle.api.dsl.GenerateInline; import com.oracle.truffle.api.dsl.GenerateUncached; +import com.oracle.truffle.api.dsl.NeverDefault; import com.oracle.truffle.api.dsl.Specialization; @GenerateUncached @@ -69,6 +70,7 @@ Object doSend(Object obj) { return obj; } + @NeverDefault public static GetSendValueNode create() { return GetSendValueNodeGen.create(); } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/bytecode/GetTPFlagsNode.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/bytecode/GetTPFlagsNode.java index 73f593079c..4a65206df6 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/bytecode/GetTPFlagsNode.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/bytecode/GetTPFlagsNode.java @@ -47,6 +47,7 @@ import com.oracle.truffle.api.dsl.Cached; import com.oracle.truffle.api.dsl.GenerateInline; import com.oracle.truffle.api.dsl.GenerateUncached; +import com.oracle.truffle.api.dsl.NeverDefault; import com.oracle.truffle.api.dsl.Specialization; import com.oracle.truffle.api.nodes.Node; @@ -63,6 +64,7 @@ long get(Object object, return getTypeFlagsNode.execute(getClassNode.execute(inliningTarget, object)); } + @NeverDefault public static GetTPFlagsNode create() { return GetTPFlagsNodeGen.create(); } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/bytecode/GetYieldFromIterNode.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/bytecode/GetYieldFromIterNode.java index 274e748b5a..bd2375982b 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/bytecode/GetYieldFromIterNode.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/bytecode/GetYieldFromIterNode.java @@ -44,18 +44,20 @@ import com.oracle.graal.python.builtins.objects.generator.PGenerator; import com.oracle.graal.python.lib.PyObjectGetIter; import com.oracle.graal.python.nodes.object.BuiltinClassProfiles.IsBuiltinObjectExactProfile; +import com.oracle.truffle.api.bytecode.OperationProxy; import com.oracle.truffle.api.dsl.Bind; import com.oracle.truffle.api.dsl.Cached; import com.oracle.truffle.api.dsl.GenerateInline; import com.oracle.truffle.api.dsl.GenerateUncached; import com.oracle.truffle.api.dsl.Specialization; -import com.oracle.truffle.api.frame.Frame; +import com.oracle.truffle.api.frame.VirtualFrame; import com.oracle.truffle.api.nodes.Node; @GenerateUncached @GenerateInline(false) // used in BCI root node +@OperationProxy.Proxyable public abstract class GetYieldFromIterNode extends Node { - public abstract Object execute(Frame frame, Object receiver); + public abstract Object execute(VirtualFrame frame, Object receiver); @Specialization public static Object getGeneratorOrCoroutine(PGenerator arg) { @@ -64,7 +66,7 @@ public static Object getGeneratorOrCoroutine(PGenerator arg) { } @Specialization - public static Object getGeneric(Frame frame, Object arg, + public static Object getGeneric(VirtualFrame frame, Object arg, @Bind("this") Node inliningTarget, @Cached PyObjectGetIter getIter, @Cached IsBuiltinObjectExactProfile isCoro) { diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/bytecode/ImportFromNode.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/bytecode/ImportFromNode.java index 98e70f902c..c2941f22db 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/bytecode/ImportFromNode.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/bytecode/ImportFromNode.java @@ -62,6 +62,7 @@ import com.oracle.truffle.api.dsl.Cached; import com.oracle.truffle.api.dsl.GenerateInline; import com.oracle.truffle.api.dsl.GenerateUncached; +import com.oracle.truffle.api.dsl.NeverDefault; import com.oracle.truffle.api.dsl.Specialization; import com.oracle.truffle.api.frame.Frame; import com.oracle.truffle.api.frame.VirtualFrame; @@ -128,6 +129,7 @@ private Object tryResolveCircularImport(Object module, TruffleString name) { } } + @NeverDefault public static ImportFromNode create() { return ImportFromNodeGen.create(); } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/bytecode/ImportNode.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/bytecode/ImportNode.java index a55b537958..95441a9c8a 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/bytecode/ImportNode.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/bytecode/ImportNode.java @@ -63,6 +63,7 @@ Object doImport(VirtualFrame frame, TruffleString name, Object globals, @NeverDe return importModule(frame, name, globals, cachedFromList, level, importName); } + @NeverDefault public static ImportNode create() { return ImportNodeGen.create(); } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/bytecode/MakeFunctionNode.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/bytecode/MakeFunctionNode.java index b032f7da80..cb9e4ee2a3 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/bytecode/MakeFunctionNode.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/bytecode/MakeFunctionNode.java @@ -49,7 +49,7 @@ import com.oracle.graal.python.builtins.objects.function.PKeyword; import com.oracle.graal.python.builtins.objects.function.Signature; import com.oracle.graal.python.builtins.objects.object.PythonObject; -import com.oracle.graal.python.compiler.CodeUnit; +import com.oracle.graal.python.compiler.BytecodeCodeUnit; import com.oracle.graal.python.compiler.OpCodes; import com.oracle.graal.python.nodes.PNodeWithContext; import com.oracle.graal.python.nodes.attributes.WriteAttributeToPythonObjectNode; @@ -66,7 +66,7 @@ public abstract class MakeFunctionNode extends PNodeWithContext { private final RootCallTarget callTarget; - private final CodeUnit code; + private final BytecodeCodeUnit code; private final Signature signature; @CompilationFinal private PCode cachedCode; @@ -75,7 +75,7 @@ public abstract class MakeFunctionNode extends PNodeWithContext { public abstract int execute(VirtualFrame frame, Object globals, int initialStackTop, int flags); - public MakeFunctionNode(RootCallTarget callTarget, CodeUnit code, Signature signature) { + public MakeFunctionNode(RootCallTarget callTarget, BytecodeCodeUnit code, Signature signature) { this.callTarget = callTarget; this.code = code; this.signature = signature; @@ -143,7 +143,7 @@ int makeFunction(VirtualFrame frame, Object globals, int initialStackTop, int fl return stackTop; } - public static MakeFunctionNode create(PythonLanguage language, CodeUnit code, Source source) { + public static MakeFunctionNode create(PythonLanguage language, BytecodeCodeUnit code, Source source) { RootCallTarget callTarget; PBytecodeRootNode bytecodeRootNode = PBytecodeRootNode.create(language, code, source); if (code.isGeneratorOrCoroutine()) { diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/bytecode/PBytecodeRootNode.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/bytecode/PBytecodeRootNode.java index 8d5ed625b3..62842f5cc0 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/bytecode/PBytecodeRootNode.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/bytecode/PBytecodeRootNode.java @@ -52,7 +52,6 @@ import static com.oracle.graal.python.util.PythonUtils.toTruffleStringUncached; import java.math.BigInteger; -import java.util.Arrays; import java.util.Collections; import java.util.Set; import java.util.concurrent.locks.Lock; @@ -101,7 +100,7 @@ import com.oracle.graal.python.builtins.objects.slice.SliceNodes.CreateSliceNode; import com.oracle.graal.python.builtins.objects.slice.SliceNodesFactory.CreateSliceNodeGen; import com.oracle.graal.python.compiler.BinaryOpsConstants; -import com.oracle.graal.python.compiler.CodeUnit; +import com.oracle.graal.python.compiler.BytecodeCodeUnit; import com.oracle.graal.python.compiler.FormatOptions; import com.oracle.graal.python.compiler.OpCodes; import com.oracle.graal.python.compiler.OpCodes.CollectionBits; @@ -503,7 +502,7 @@ public final class PBytecodeRootNode extends PRootNode implements BytecodeOSRNod final int selfIndex; final int classcellIndex; - private final CodeUnit co; + private final BytecodeCodeUnit co; private final Source source; private SourceSection sourceSection; // For deferred deprecation warnings @@ -575,7 +574,7 @@ public final class PBytecodeRootNode extends PRootNode implements BytecodeOSRNod @Child private InstrumentationRoot instrumentationRoot = InstrumentationRoot.create(); - private static FrameDescriptor makeFrameDescriptor(CodeUnit co, FrameInfo info) { + private static FrameDescriptor makeFrameDescriptor(BytecodeCodeUnit co, FrameInfo info) { int capacity = co.varnames.length + co.cellvars.length + co.freevars.length + co.stacksize + 1; FrameDescriptor.Builder newBuilder = FrameDescriptor.newBuilder(capacity); newBuilder.info(info); @@ -613,40 +612,27 @@ private static FrameDescriptor makeFrameDescriptor(CodeUnit co, FrameInfo info) return newBuilder.build(); } - private static Signature makeSignature(CodeUnit co) { - int posArgCount = co.argCount + co.positionalOnlyArgCount; - TruffleString[] parameterNames = Arrays.copyOf(co.varnames, posArgCount); - TruffleString[] kwOnlyNames = Arrays.copyOfRange(co.varnames, posArgCount, posArgCount + co.kwOnlyArgCount); - int varArgsIndex = co.takesVarArgs() ? posArgCount : -1; - return new Signature(co.positionalOnlyArgCount, - co.takesVarKeywordArgs(), - varArgsIndex, - co.positionalOnlyArgCount > 0, - parameterNames, - kwOnlyNames); - } - @TruffleBoundary - public static PBytecodeRootNode create(PythonLanguage language, CodeUnit co, Source source) { + public static PBytecodeRootNode create(PythonLanguage language, BytecodeCodeUnit co, Source source) { return create(language, co, source, null); } @TruffleBoundary - public static PBytecodeRootNode create(PythonLanguage language, CodeUnit co, Source source, RaisePythonExceptionErrorCallback parserErrorCallback) { - FrameInfo frameInfo = new FrameInfo(); + public static PBytecodeRootNode create(PythonLanguage language, BytecodeCodeUnit co, Source source, RaisePythonExceptionErrorCallback parserErrorCallback) { + BytecodeFrameInfo frameInfo = new BytecodeFrameInfo(); FrameDescriptor fd = makeFrameDescriptor(co, frameInfo); - PBytecodeRootNode rootNode = new PBytecodeRootNode(language, fd, makeSignature(co), co, source, parserErrorCallback); + PBytecodeRootNode rootNode = new PBytecodeRootNode(language, fd, co.computeSignature(), co, source, parserErrorCallback); PythonContext context = PythonContext.get(rootNode); if (context != null && context.getOption(PythonOptions.EagerlyMaterializeInstrumentationNodes)) { rootNode.adoptChildren(); rootNode.instrumentationRoot.materializeInstrumentableNodes(Collections.singleton(StandardTags.StatementTag.class)); } - frameInfo.rootNode = rootNode; + frameInfo.setRootNode(rootNode); return rootNode; } @TruffleBoundary - private PBytecodeRootNode(PythonLanguage language, FrameDescriptor fd, Signature sign, CodeUnit co, Source source, RaisePythonExceptionErrorCallback parserErrorCallback) { + private PBytecodeRootNode(PythonLanguage language, FrameDescriptor fd, Signature sign, BytecodeCodeUnit co, Source source, RaisePythonExceptionErrorCallback parserErrorCallback) { super(language, fd); assert source != null; this.celloffset = co.varnames.length; @@ -734,7 +720,7 @@ public void setPythonInternal(boolean pythonInternal) { this.pythonInternal = pythonInternal; } - public CodeUnit getCodeUnit() { + public BytecodeCodeUnit getCodeUnit() { return co; } @@ -1073,7 +1059,6 @@ public Object execute(VirtualFrame virtualFrame) { if (!co.isGeneratorOrCoroutine()) { copyArgsAndCells(virtualFrame, virtualFrame.getArguments()); } - return executeFromBci(virtualFrame, virtualFrame, this, 0, getInitialStackTop()); } finally { calleeContext.exit(virtualFrame, this); @@ -2598,7 +2583,7 @@ private void bytecodeSetupAnnotations(VirtualFrame virtualFrame, boolean useCach @BytecodeInterpreterSwitch private int bytecodeMakeFunction(VirtualFrame virtualFrame, Object globals, int stackTop, Node[] localNodes, int beginBci, int flags, Object localConsts) { - CodeUnit codeUnit = (CodeUnit) localConsts; + BytecodeCodeUnit codeUnit = (BytecodeCodeUnit) localConsts; MakeFunctionNode makeFunctionNode = insertMakeFunctionNode(localNodes, beginBci, codeUnit); return makeFunctionNode.execute(virtualFrame, globals, stackTop, flags); } @@ -2763,15 +2748,15 @@ private Object notifyEnter(VirtualFrame virtualFrame, InstrumentationSupport ins return null; } - private MakeFunctionNode insertMakeFunctionNode(Node[] localNodes, int beginBci, CodeUnit codeUnit) { + private MakeFunctionNode insertMakeFunctionNode(Node[] localNodes, int beginBci, BytecodeCodeUnit codeUnit) { return insertChildNode(localNodes, beginBci, MakeFunctionNodeGen.class, () -> MakeFunctionNode.create(getLanguage(PythonLanguage.class), codeUnit, source)); } public void materializeContainedFunctionsForInstrumentation(Set> materializedTags) { usingCachedNodes = true; - CodeUnit.iterateBytecode(bytecode, (bci, op, oparg, followingArgs) -> { + BytecodeCodeUnit.iterateBytecode(bytecode, (bci, op, oparg, followingArgs) -> { if (op == OpCodes.MAKE_FUNCTION) { - CodeUnit codeUnit = (CodeUnit) consts[oparg]; + BytecodeCodeUnit codeUnit = (BytecodeCodeUnit) consts[oparg]; MakeFunctionNode makeFunctionNode = insertMakeFunctionNode(getChildNodes(), bci, codeUnit); RootNode rootNode = makeFunctionNode.getCallTarget().getRootNode(); if (rootNode instanceof PBytecodeGeneratorFunctionRootNode) { @@ -2939,10 +2924,10 @@ private int traceLine(VirtualFrame virtualFrame, MutableLoopData mutableData, by if (pyFrame.didJump()) { int newBci = lineToBci(pyFrame.getJumpDestLine()); mutableData.setPastBci(bci); - if (newBci == CodeUnit.LINE_TO_BCI_LINE_AFTER_CODEBLOCK) { + if (newBci == BytecodeCodeUnit.LINE_TO_BCI_LINE_AFTER_CODEBLOCK) { // line after the code block throw PRaiseNode.getUncached().raise(ValueError, ErrorMessages.LINE_D_COMES_AFTER_THE_CURRENT_CODE_BLOCK, pyFrame.getLine()); - } else if (newBci == CodeUnit.LINE_TO_BCI_LINE_BEFORE_CODEBLOCK) { + } else if (newBci == BytecodeCodeUnit.LINE_TO_BCI_LINE_BEFORE_CODEBLOCK) { // line before the code block throw PRaiseNode.getUncached().raise(ValueError, ErrorMessages.LINE_D_COMES_BEFORE_THE_CURRENT_CODE_BLOCK, pyFrame.getJumpDestLine()); } else { @@ -3148,7 +3133,7 @@ private void syncLocalsBackToFrame(VirtualFrame virtualFrame, PFrame pyFrame) { if (co.isGeneratorOrCoroutine()) { localFrame = PArguments.getGeneratorFrame(virtualFrame); } - GetFrameLocalsNode.syncLocalsBackToFrame(co, pyFrame, localFrame); + GetFrameLocalsNode.syncLocalsBackToFrame(co, this, pyFrame, localFrame); } private void profileCEvent(VirtualFrame virtualFrame, Object callable, PythonContext.ProfileEvent event, MutableLoopData mutableData, byte tracingOrProfilingEnabled) { diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/bytecode/PrintExprNode.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/bytecode/PrintExprNode.java index c8e0576ba8..b8838c57a0 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/bytecode/PrintExprNode.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/bytecode/PrintExprNode.java @@ -55,6 +55,7 @@ import com.oracle.truffle.api.dsl.Cached; import com.oracle.truffle.api.dsl.GenerateInline; import com.oracle.truffle.api.dsl.GenerateUncached; +import com.oracle.truffle.api.dsl.NeverDefault; import com.oracle.truffle.api.dsl.Specialization; import com.oracle.truffle.api.frame.Frame; import com.oracle.truffle.api.frame.VirtualFrame; @@ -79,6 +80,7 @@ void print(VirtualFrame frame, Object object, callNode.execute(frame, displayhook, object); } + @NeverDefault public static PrintExprNode create() { return PrintExprNodeGen.create(); } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/bytecode/RaiseNode.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/bytecode/RaiseNode.java index 4202c74e1e..6992f700f0 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/bytecode/RaiseNode.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/bytecode/RaiseNode.java @@ -51,6 +51,7 @@ import com.oracle.truffle.api.dsl.Fallback; import com.oracle.truffle.api.dsl.GenerateInline; import com.oracle.truffle.api.dsl.ImportStatic; +import com.oracle.truffle.api.dsl.NeverDefault; import com.oracle.truffle.api.dsl.Specialization; import com.oracle.truffle.api.exception.AbstractTruffleException; import com.oracle.truffle.api.frame.VirtualFrame; @@ -247,6 +248,7 @@ private static PException raiseNoException(PRaiseNode raise) { throw raise.raise(TypeError, ErrorMessages.EXCEPTIONS_MUST_DERIVE_FROM_BASE_EX); } + @NeverDefault public static RaiseNode create() { return RaiseNodeGen.create(); } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/bytecode/SetupAnnotationsNode.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/bytecode/SetupAnnotationsNode.java index ab7481b702..b548ea82e5 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/bytecode/SetupAnnotationsNode.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/bytecode/SetupAnnotationsNode.java @@ -58,6 +58,7 @@ import com.oracle.graal.python.nodes.object.BuiltinClassProfiles.IsBuiltinObjectProfile; import com.oracle.graal.python.runtime.exception.PException; import com.oracle.graal.python.runtime.object.PythonObjectFactory; +import com.oracle.truffle.api.bytecode.OperationProxy; import com.oracle.truffle.api.dsl.Bind; import com.oracle.truffle.api.dsl.Cached; import com.oracle.truffle.api.dsl.Cached.Shared; @@ -75,6 +76,7 @@ @GenerateUncached @ImportStatic(PArguments.class) @GenerateInline(false) // used in BCI root node +@OperationProxy.Proxyable public abstract class SetupAnnotationsNode extends PNodeWithContext { public abstract void execute(Frame frame); diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/bytecode/instrumentation/InstrumentationSupport.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/bytecode/instrumentation/InstrumentationSupport.java index ca1734135a..03198b5825 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/bytecode/instrumentation/InstrumentationSupport.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/bytecode/instrumentation/InstrumentationSupport.java @@ -40,7 +40,7 @@ */ package com.oracle.graal.python.nodes.bytecode.instrumentation; -import com.oracle.graal.python.compiler.CodeUnit; +import com.oracle.graal.python.compiler.BytecodeCodeUnit; import com.oracle.graal.python.compiler.OpCodes; import com.oracle.graal.python.nodes.BuiltinNames; import com.oracle.graal.python.nodes.bytecode.PBytecodeRootNode; @@ -62,7 +62,7 @@ * @see InstrumentationRoot */ public final class InstrumentationSupport extends Node { - final CodeUnit code; + final BytecodeCodeUnit code; @Children InstrumentedBytecodeStatement[] statements; /* * When instrumentation is active, this array is used instead of PBytecodeRootNode#adoptedNodes diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/bytecode_dsl/BytecodeDSLCodeUnit.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/bytecode_dsl/BytecodeDSLCodeUnit.java new file mode 100644 index 0000000000..cbe001de6a --- /dev/null +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/bytecode_dsl/BytecodeDSLCodeUnit.java @@ -0,0 +1,93 @@ +package com.oracle.graal.python.nodes.bytecode_dsl; + +import java.io.ByteArrayOutputStream; +import java.io.DataOutputStream; +import java.io.IOException; + +import com.oracle.graal.python.builtins.modules.MarshalModuleBuiltins; +import com.oracle.graal.python.builtins.modules.MarshalModuleBuiltins.PBytecodeDSLSerializer; +import com.oracle.graal.python.builtins.objects.ints.PInt; +import com.oracle.graal.python.compiler.CodeUnit; +import com.oracle.graal.python.runtime.PythonContext; +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.bytecode.BytecodeRootNodes; +import com.oracle.truffle.api.bytecode.serialization.BytecodeSerializer; +import com.oracle.truffle.api.source.Source; +import com.oracle.truffle.api.strings.TruffleString; + +public class BytecodeDSLCodeUnit extends CodeUnit { + /* + * A {@link BytecodeDSLCodeUnit} is a context-independent representation of a root node. It + * contains the bytes produced from Bytecode DSL serialization. + * + * Since it is expensive to serialize every root node, we perform serialization lazily using the + * {@link BytecodeNodes} produced during parsing. + * + * When this code unit is directly instantiated via unmarshaling, there is no {@link + * BytecodeNodes}; instead, we store the serialized bytes directly. + */ + @CompilationFinal(dimensions = 1) private byte[] serialized; + private final BytecodeRootNodes nodes; + public final int classcellIndex; + public final int selfIndex; + + public BytecodeDSLCodeUnit(TruffleString name, TruffleString qualname, int argCount, int kwOnlyArgCount, int positionalOnlyArgCount, int flags, TruffleString[] names, TruffleString[] varnames, + TruffleString[] cellvars, TruffleString[] freevars, int[] cell2arg, Object[] constants, int startLine, int startColumn, int endLine, int endColumn, + int classcellIndex, int selfIndex, byte[] serialized, BytecodeRootNodes nodes) { + super(name, qualname, argCount, kwOnlyArgCount, positionalOnlyArgCount, flags, names, varnames, cellvars, freevars, cell2arg, constants, startLine, startColumn, endLine, endColumn); + // Only one of these fields should be set. The other gets computed dynamically. + assert serialized == null ^ nodes == null; + this.serialized = serialized; + this.nodes = nodes; + this.classcellIndex = classcellIndex; + this.selfIndex = selfIndex; + } + + @TruffleBoundary + public PBytecodeDSLRootNode createRootNode(PythonContext context, Source source) { + byte[] toDeserialize = getSerialized(context.getTrue(), context.getFalse()); + BytecodeRootNodes deserialized = MarshalModuleBuiltins.deserializeBytecodeNodes(context.getLanguage(), source, toDeserialize); + assert deserialized.count() == 1; + PBytecodeDSLRootNode result = deserialized.getNode(0); + result.setMetadata(this, null); + return result; + } + + public byte[] getSerialized(PInt pyTrue, PInt pyFalse) { + byte[] result = serialized; + if (result == null) { + synchronized (this) { + result = serialized; + if (result == null) { + CompilerDirectives.transferToInterpreterAndInvalidate(); + result = serialized = computeSerialized(pyTrue, pyFalse); + } + } + } + return result; + } + + @SuppressWarnings("unchecked") + @TruffleBoundary + private byte[] computeSerialized(PInt pyTrue, PInt pyFalse) { + try { + BytecodeSerializer serializer = new PBytecodeDSLSerializer(pyTrue, pyFalse); + ByteArrayOutputStream bytes = new ByteArrayOutputStream(); + nodes.serialize(new DataOutputStream(bytes), serializer); + return bytes.toByteArray(); + } catch (IOException e) { + throw CompilerDirectives.shouldNotReachHere(e); + } + } + + public TruffleString getDocstring() { + // The first constant in the code unit is the docstring (if available) or PNone. + if (constants.length > 0 && constants[0] instanceof TruffleString docstring) { + return docstring; + } + return null; + } + +} diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/bytecode_dsl/BytecodeDSLFrameInfo.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/bytecode_dsl/BytecodeDSLFrameInfo.java new file mode 100644 index 0000000000..2229f81bef --- /dev/null +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/bytecode_dsl/BytecodeDSLFrameInfo.java @@ -0,0 +1,44 @@ +package com.oracle.graal.python.nodes.bytecode_dsl; + +import com.oracle.graal.python.compiler.CodeUnit; +import com.oracle.graal.python.nodes.bytecode.FrameInfo; +import com.oracle.truffle.api.CompilerDirectives.CompilationFinal; +import com.oracle.truffle.api.frame.Frame; + +public class BytecodeDSLFrameInfo implements FrameInfo { + @CompilationFinal PBytecodeDSLRootNode rootNode; + + /** + * The root node cannot be created without a frame descriptor, which cannot be created without + * the frame info, so we have to set the root node after the frame info is constructed. + */ + void setRootNode(PBytecodeDSLRootNode rootNode) { + this.rootNode = rootNode; + } + + @Override + public PBytecodeDSLRootNode getRootNode() { + return rootNode; + } + + @Override + public int getFirstLineNumber() { + return rootNode.getFirstLineno(); + } + + @Override + public Object getYieldFrom(Frame generatorFrame, int bci, int stackTop) { + // TODO implement + throw new UnsupportedOperationException("not implemented"); + } + + @Override + public CodeUnit getCodeUnit() { + return rootNode.getCodeUnit(); + } + + @Override + public boolean includeInTraceback() { + return !rootNode.isInternal(); + } +} diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/bytecode_dsl/PBytecodeDSLGeneratorFunctionRootNode.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/bytecode_dsl/PBytecodeDSLGeneratorFunctionRootNode.java new file mode 100644 index 0000000000..88a297b7b3 --- /dev/null +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/bytecode_dsl/PBytecodeDSLGeneratorFunctionRootNode.java @@ -0,0 +1,94 @@ +package com.oracle.graal.python.nodes.bytecode_dsl; + +import com.oracle.graal.python.PythonLanguage; +import com.oracle.graal.python.builtins.objects.PNone; +import com.oracle.graal.python.builtins.objects.function.PArguments; +import com.oracle.graal.python.builtins.objects.function.PFunction; +import com.oracle.graal.python.builtins.objects.function.Signature; +import com.oracle.graal.python.nodes.PRootNode; +import com.oracle.truffle.api.CompilerAsserts; +import com.oracle.truffle.api.CompilerDirectives; +import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; +import com.oracle.truffle.api.frame.FrameDescriptor; +import com.oracle.truffle.api.frame.VirtualFrame; +import com.oracle.truffle.api.profiles.ConditionProfile; +import com.oracle.truffle.api.source.SourceSection; +import com.oracle.truffle.api.strings.TruffleString; + +public final class PBytecodeDSLGeneratorFunctionRootNode extends PRootNode { + private final PBytecodeDSLRootNode rootNode; + private final TruffleString originalName; + private final ConditionProfile isIterableCoroutine = ConditionProfile.create(); + + @TruffleBoundary + public PBytecodeDSLGeneratorFunctionRootNode(PythonLanguage language, FrameDescriptor frameDescriptor, PBytecodeDSLRootNode rootNode, TruffleString originalName) { + super(language, frameDescriptor); + this.rootNode = rootNode; + this.originalName = originalName; + } + + @Override + public Object execute(VirtualFrame frame) { + Object[] arguments = frame.getArguments(); + + // This is passed from InvokeNode node + PFunction generatorFunction = PArguments.getGeneratorFunction(arguments); + assert generatorFunction != null; + if (rootNode.getCodeUnit().isGenerator()) { + // if CO_ITERABLE_COROUTINE was explicitly set (likely by types.coroutine), we have to + // pass the information to the generator + // .gi_code.co_flags will still be wrong, but at least await will work correctly + if (isIterableCoroutine.profile((generatorFunction.getCode().getFlags() & 0x100) != 0)) { + return rootNode.factory.createIterableCoroutine(generatorFunction.getName(), generatorFunction.getQualname(), rootNode, arguments); + } else { + return rootNode.factory.createGenerator(generatorFunction.getName(), generatorFunction.getQualname(), rootNode, arguments); + } + } else if (rootNode.getCodeUnit().isCoroutine()) { + return rootNode.factory.createCoroutine(generatorFunction.getName(), generatorFunction.getQualname(), rootNode, arguments); + } else if (rootNode.getCodeUnit().isAsyncGenerator()) { + /* + * TODO: Support async generators. + * + * We need to produce something instead of failing here because async generators are + * instantiated in frozen module code. + */ + return PNone.NONE; + } + throw CompilerDirectives.shouldNotReachHere("Unknown generator/coroutine type"); + } + + @Override + public String getName() { + return originalName.toJavaStringUncached(); + } + + @Override + public String toString() { + CompilerAsserts.neverPartOfCompilation(); + return ""; + } + + @Override + public Signature getSignature() { + return rootNode.getSignature(); + } + + @Override + public boolean isPythonInternal() { + return rootNode.isPythonInternal(); + } + + @Override + public SourceSection getSourceSection() { + return rootNode.getSourceSection(); + } + + @Override + protected byte[] extractCode() { + return rootNode.extractCode(); + } + + public PBytecodeDSLRootNode getBytecodeRootNode() { + return rootNode; + } +} \ No newline at end of file diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/bytecode_dsl/PBytecodeDSLRootNode.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/bytecode_dsl/PBytecodeDSLRootNode.java new file mode 100644 index 0000000000..a88c15510f --- /dev/null +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/bytecode_dsl/PBytecodeDSLRootNode.java @@ -0,0 +1,4143 @@ +package com.oracle.graal.python.nodes.bytecode_dsl; + +import static com.oracle.graal.python.builtins.PythonBuiltinClassType.AttributeError; +import static com.oracle.graal.python.builtins.PythonBuiltinClassType.GeneratorExit; +import static com.oracle.graal.python.builtins.PythonBuiltinClassType.RecursionError; +import static com.oracle.graal.python.builtins.PythonBuiltinClassType.SystemError; +import static com.oracle.graal.python.builtins.PythonBuiltinClassType.TypeError; +import static com.oracle.graal.python.builtins.PythonBuiltinClassType.ValueError; +import static com.oracle.graal.python.nodes.SpecialAttributeNames.T___ANNOTATIONS__; +import static com.oracle.graal.python.nodes.SpecialAttributeNames.T___DOC__; +import static com.oracle.graal.python.nodes.SpecialMethodNames.T___AENTER__; +import static com.oracle.graal.python.nodes.SpecialMethodNames.T___AEXIT__; +import static com.oracle.graal.python.nodes.SpecialMethodNames.T___ENTER__; +import static com.oracle.graal.python.nodes.SpecialMethodNames.T___EXIT__; +import static com.oracle.graal.python.runtime.exception.PythonErrorType.AssertionError; +import static com.oracle.graal.python.util.PythonUtils.tsLiteral; + +import java.math.BigInteger; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Iterator; + +import com.oracle.graal.python.PythonLanguage; +import com.oracle.graal.python.builtins.PythonBuiltinClassType; +import com.oracle.graal.python.builtins.modules.BuiltinFunctions.FormatNode; +import com.oracle.graal.python.builtins.modules.MarshalModuleBuiltins; +import com.oracle.graal.python.builtins.objects.PNone; +import com.oracle.graal.python.builtins.objects.PNotImplemented; +import com.oracle.graal.python.builtins.objects.asyncio.GetAwaitableNode; +import com.oracle.graal.python.builtins.objects.cell.PCell; +import com.oracle.graal.python.builtins.objects.code.PCode; +import com.oracle.graal.python.builtins.objects.common.EmptyStorage; +import com.oracle.graal.python.builtins.objects.common.HashingCollectionNodes; +import com.oracle.graal.python.builtins.objects.common.HashingStorage; +import com.oracle.graal.python.builtins.objects.common.HashingStorageNodes.HashingStorageSetItem; +import com.oracle.graal.python.builtins.objects.common.SequenceNodes; +import com.oracle.graal.python.builtins.objects.common.SequenceStorageNodes; +import com.oracle.graal.python.builtins.objects.common.SequenceStorageNodes.ListGeneralizationNode; +import com.oracle.graal.python.builtins.objects.dict.DictNodes; +import com.oracle.graal.python.builtins.objects.dict.PDict; +import com.oracle.graal.python.builtins.objects.exception.ChainExceptionsNode; +import com.oracle.graal.python.builtins.objects.exception.ExceptionNodes; +import com.oracle.graal.python.builtins.objects.exception.PBaseException; +import com.oracle.graal.python.builtins.objects.exception.StopIterationBuiltins; +import com.oracle.graal.python.builtins.objects.frame.PFrame; +import com.oracle.graal.python.builtins.objects.function.PArguments; +import com.oracle.graal.python.builtins.objects.function.PFunction; +import com.oracle.graal.python.builtins.objects.function.PKeyword; +import com.oracle.graal.python.builtins.objects.function.Signature; +import com.oracle.graal.python.builtins.objects.generator.CommonGeneratorBuiltins; +import com.oracle.graal.python.builtins.objects.generator.PGenerator; +import com.oracle.graal.python.builtins.objects.iterator.IteratorNodes; +import com.oracle.graal.python.builtins.objects.iterator.PDoubleSequenceIterator; +import com.oracle.graal.python.builtins.objects.iterator.PIntRangeIterator; +import com.oracle.graal.python.builtins.objects.iterator.PIntegerIterator; +import com.oracle.graal.python.builtins.objects.iterator.PIntegerSequenceIterator; +import com.oracle.graal.python.builtins.objects.iterator.PLongSequenceIterator; +import com.oracle.graal.python.builtins.objects.iterator.PObjectSequenceIterator; +import com.oracle.graal.python.builtins.objects.list.ListBuiltins.ListExtendNode; +import com.oracle.graal.python.builtins.objects.list.PList; +import com.oracle.graal.python.builtins.objects.set.PFrozenSet; +import com.oracle.graal.python.builtins.objects.set.PSet; +import com.oracle.graal.python.builtins.objects.set.SetNodes; +import com.oracle.graal.python.builtins.objects.str.StringUtils; +import com.oracle.graal.python.builtins.objects.tuple.PTuple; +import com.oracle.graal.python.builtins.objects.type.SpecialMethodSlot; +import com.oracle.graal.python.builtins.objects.type.TypeFlags; +import com.oracle.graal.python.compiler.CodeUnit; +import com.oracle.graal.python.compiler.OpCodes; +import com.oracle.graal.python.compiler.RaisePythonExceptionErrorCallback; +import com.oracle.graal.python.compiler.OpCodes.CollectionBits; +import com.oracle.graal.python.lib.GetNextNode; +import com.oracle.graal.python.lib.PyIterCheckNode; +import com.oracle.graal.python.lib.PyNumberAddNode; +import com.oracle.graal.python.lib.PyNumberMultiplyNode; +import com.oracle.graal.python.lib.PyObjectAsciiNode; +import com.oracle.graal.python.lib.PyObjectCallMethodObjArgs; +import com.oracle.graal.python.lib.PyObjectDelItem; +import com.oracle.graal.python.lib.PyObjectFunctionStr; +import com.oracle.graal.python.lib.PyObjectGetItem; +import com.oracle.graal.python.lib.PyObjectGetIter; +import com.oracle.graal.python.lib.PyObjectGetMethod; +import com.oracle.graal.python.lib.PyObjectIsTrueNode; +import com.oracle.graal.python.lib.PyObjectLookupAttr; +import com.oracle.graal.python.lib.PyObjectReprAsTruffleStringNode; +import com.oracle.graal.python.lib.PyObjectSetAttr; +import com.oracle.graal.python.lib.PyObjectSetAttrNodeGen; +import com.oracle.graal.python.lib.PyObjectSetItem; +import com.oracle.graal.python.lib.PyObjectSizeNode; +import com.oracle.graal.python.lib.PyObjectStrAsTruffleStringNode; +import com.oracle.graal.python.nodes.BuiltinNames; +import com.oracle.graal.python.nodes.ErrorMessages; +import com.oracle.graal.python.nodes.PGuards; +import com.oracle.graal.python.nodes.PRaiseNode; +import com.oracle.graal.python.nodes.PRootNode; +import com.oracle.graal.python.nodes.WriteUnraisableNode; +import com.oracle.graal.python.nodes.argument.keywords.ConcatDictToStorageNode; +import com.oracle.graal.python.nodes.argument.keywords.ExpandKeywordStarargsNode; +import com.oracle.graal.python.nodes.argument.keywords.NonMappingException; +import com.oracle.graal.python.nodes.argument.keywords.SameDictKeyException; +import com.oracle.graal.python.nodes.attributes.GetAttributeNode.GetFixedAttributeNode; +import com.oracle.graal.python.nodes.builtins.ListNodes; +import com.oracle.graal.python.nodes.bytecode.GetSendValueNode; +import com.oracle.graal.python.nodes.bytecode.GetTPFlagsNode; +import com.oracle.graal.python.nodes.bytecode.GetYieldFromIterNode; +import com.oracle.graal.python.nodes.bytecode.ImportFromNode; +import com.oracle.graal.python.nodes.bytecode.ImportNode; +import com.oracle.graal.python.nodes.bytecode.ImportStarNode; +import com.oracle.graal.python.nodes.bytecode.PBytecodeGeneratorFunctionRootNode; +import com.oracle.graal.python.nodes.bytecode.PBytecodeGeneratorRootNode; +import com.oracle.graal.python.nodes.bytecode.PBytecodeRootNode; +import com.oracle.graal.python.nodes.bytecode.PrintExprNode; +import com.oracle.graal.python.nodes.bytecode.RaiseNode; +import com.oracle.graal.python.nodes.bytecode.SetupAnnotationsNode; +import com.oracle.graal.python.nodes.call.CallNode; +import com.oracle.graal.python.nodes.call.special.CallBinaryMethodNode; +import com.oracle.graal.python.nodes.call.special.CallQuaternaryMethodNode; +import com.oracle.graal.python.nodes.call.special.CallTernaryMethodNode; +import com.oracle.graal.python.nodes.call.special.CallUnaryMethodNode; +import com.oracle.graal.python.nodes.call.special.LookupAndCallBinaryNode; +import com.oracle.graal.python.nodes.call.special.LookupAndCallTernaryNode; +import com.oracle.graal.python.nodes.call.special.LookupSpecialMethodSlotNode; +import com.oracle.graal.python.nodes.exception.ExceptMatchNode; +import com.oracle.graal.python.nodes.expression.BinaryArithmetic.BitAndNode; +import com.oracle.graal.python.nodes.expression.BinaryArithmetic.BitOrNode; +import com.oracle.graal.python.nodes.expression.BinaryArithmetic.BitXorNode; +import com.oracle.graal.python.nodes.expression.BinaryArithmetic.DivModNode; +import com.oracle.graal.python.nodes.expression.BinaryArithmetic.FloorDivNode; +import com.oracle.graal.python.nodes.expression.BinaryArithmetic.LShiftNode; +import com.oracle.graal.python.nodes.expression.BinaryArithmetic.MatMulNode; +import com.oracle.graal.python.nodes.expression.BinaryArithmetic.ModNode; +import com.oracle.graal.python.nodes.expression.BinaryArithmetic.PowNode; +import com.oracle.graal.python.nodes.expression.BinaryArithmetic.RShiftNode; +import com.oracle.graal.python.nodes.expression.BinaryArithmetic.SubNode; +import com.oracle.graal.python.nodes.expression.BinaryArithmetic.TrueDivNode; +import com.oracle.graal.python.nodes.expression.CoerceToBooleanNode.NotNode; +import com.oracle.graal.python.nodes.expression.CoerceToBooleanNode.YesNode; +import com.oracle.graal.python.nodes.expression.ContainsNode; +import com.oracle.graal.python.nodes.expression.InplaceArithmetic; +import com.oracle.graal.python.nodes.expression.LookupAndCallInplaceNode; +import com.oracle.graal.python.nodes.expression.UnaryArithmetic.InvertNode; +import com.oracle.graal.python.nodes.expression.UnaryArithmetic.NegNode; +import com.oracle.graal.python.nodes.expression.UnaryArithmetic.PosNode; +import com.oracle.graal.python.nodes.frame.DeleteGlobalNode; +import com.oracle.graal.python.nodes.frame.GetFrameLocalsNode; +import com.oracle.graal.python.nodes.frame.MaterializeFrameNode; +import com.oracle.graal.python.nodes.frame.ReadFromLocalsNode; +import com.oracle.graal.python.nodes.frame.ReadGlobalOrBuiltinNode; +import com.oracle.graal.python.nodes.frame.ReadNameNode; +import com.oracle.graal.python.nodes.frame.WriteGlobalNode; +import com.oracle.graal.python.nodes.frame.WriteNameNode; +import com.oracle.graal.python.nodes.object.BuiltinClassProfiles.IsBuiltinObjectProfile; +import com.oracle.graal.python.nodes.object.GetClassNode.GetPythonObjectClassNode; +import com.oracle.graal.python.nodes.object.GetClassNode; +import com.oracle.graal.python.nodes.object.IsNode; +import com.oracle.graal.python.nodes.truffle.PythonTypes; +import com.oracle.graal.python.nodes.util.ExceptionStateNodes; +import com.oracle.graal.python.runtime.ExecutionContext.CalleeContext; +import com.oracle.graal.python.runtime.PythonContext.ProfileEvent; +import com.oracle.graal.python.runtime.PythonContext.PythonThreadState; +import com.oracle.graal.python.runtime.PythonContext.TraceEvent; +import com.oracle.graal.python.runtime.PythonContext; +import com.oracle.graal.python.runtime.PythonOptions; +import com.oracle.graal.python.runtime.exception.ExceptionUtils; +import com.oracle.graal.python.runtime.exception.PException; +import com.oracle.graal.python.runtime.exception.PythonErrorType; +import com.oracle.graal.python.runtime.object.PythonObjectFactory; +import com.oracle.graal.python.runtime.sequence.PSequence; +import com.oracle.graal.python.runtime.sequence.storage.BoolSequenceStorage; +import com.oracle.graal.python.runtime.sequence.storage.DoubleSequenceStorage; +import com.oracle.graal.python.runtime.sequence.storage.IntSequenceStorage; +import com.oracle.graal.python.runtime.sequence.storage.LongSequenceStorage; +import com.oracle.graal.python.runtime.sequence.storage.ObjectSequenceStorage; +import com.oracle.graal.python.runtime.sequence.storage.SequenceStorage; +import com.oracle.graal.python.util.OverflowException; +import com.oracle.graal.python.util.PythonUtils; +import com.oracle.truffle.api.Assumption; +import com.oracle.truffle.api.CompilerAsserts; +import com.oracle.truffle.api.CompilerDirectives; +import com.oracle.truffle.api.RootCallTarget; +import com.oracle.truffle.api.CompilerDirectives.CompilationFinal; +import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; +import com.oracle.truffle.api.HostCompilerDirectives.InliningCutoff; +import com.oracle.truffle.api.Truffle; +import com.oracle.truffle.api.TruffleLanguage; +import com.oracle.truffle.api.bytecode.BytecodeConfig; +import com.oracle.truffle.api.bytecode.BytecodeLocation; +import com.oracle.truffle.api.bytecode.BytecodeNode; +import com.oracle.truffle.api.bytecode.BytecodeRootNode; +import com.oracle.truffle.api.bytecode.ConstantOperand; +import com.oracle.truffle.api.bytecode.EpilogExceptional; +import com.oracle.truffle.api.bytecode.EpilogReturn; +import com.oracle.truffle.api.bytecode.GenerateBytecode; +import com.oracle.truffle.api.bytecode.Instruction; +import com.oracle.truffle.api.bytecode.Instrumentation; +import com.oracle.truffle.api.bytecode.LocalSetter; +import com.oracle.truffle.api.bytecode.LocalSetterRange; +import com.oracle.truffle.api.bytecode.Operation; +import com.oracle.truffle.api.bytecode.OperationProxy; +import com.oracle.truffle.api.bytecode.Prolog; +import com.oracle.truffle.api.bytecode.ShortCircuitOperation; +import com.oracle.truffle.api.bytecode.ShortCircuitOperation.Operator; +import com.oracle.truffle.api.bytecode.Variadic; +import com.oracle.truffle.api.dsl.Bind; +import com.oracle.truffle.api.dsl.Cached; +import com.oracle.truffle.api.dsl.Cached.Exclusive; +import com.oracle.truffle.api.dsl.Cached.Shared; +import com.oracle.truffle.api.dsl.Fallback; +import com.oracle.truffle.api.dsl.Idempotent; +import com.oracle.truffle.api.dsl.ImportStatic; +import com.oracle.truffle.api.dsl.NeverDefault; +import com.oracle.truffle.api.dsl.NonIdempotent; +import com.oracle.truffle.api.dsl.Specialization; +import com.oracle.truffle.api.dsl.TypeSystemReference; +import com.oracle.truffle.api.exception.AbstractTruffleException; +import com.oracle.truffle.api.frame.Frame; +import com.oracle.truffle.api.frame.FrameDescriptor; +import com.oracle.truffle.api.frame.MaterializedFrame; +import com.oracle.truffle.api.frame.VirtualFrame; +import com.oracle.truffle.api.library.CachedLibrary; +import com.oracle.truffle.api.nodes.EncapsulatingNodeReference; +import com.oracle.truffle.api.nodes.ExplodeLoop; +import com.oracle.truffle.api.nodes.Node; +import com.oracle.truffle.api.nodes.UnexpectedResultException; +import com.oracle.truffle.api.nodes.Node.Child; +import com.oracle.truffle.api.object.DynamicObjectLibrary; +import com.oracle.truffle.api.profiles.InlinedBranchProfile; +import com.oracle.truffle.api.source.Source; +import com.oracle.truffle.api.source.SourceSection; +import com.oracle.truffle.api.strings.TruffleString; +import com.oracle.truffle.api.strings.TruffleStringBuilder; + +@GenerateBytecode(// + languageClass = PythonLanguage.class, // + enableLocalScoping = false, // + enableYield = true, // + enableSerialization = true, // + enableTagInstrumentation = true, // + boxingEliminationTypes = {int.class, boolean.class}, // + storeBytecodeIndexInFrame = true // +) +@TypeSystemReference(PythonTypes.class) +@OperationProxy(SubNode.class) +@OperationProxy(TrueDivNode.class) +@OperationProxy(FloorDivNode.class) +@OperationProxy(ModNode.class) +@OperationProxy(LShiftNode.class) +@OperationProxy(RShiftNode.class) +@OperationProxy(BitAndNode.class) +@OperationProxy(BitOrNode.class) +@OperationProxy(BitXorNode.class) +@OperationProxy(MatMulNode.class) +@OperationProxy(PowNode.class) +@OperationProxy(DivModNode.class) +@OperationProxy(PosNode.class) +@OperationProxy(NegNode.class) +@OperationProxy(InvertNode.class) +@OperationProxy(IsNode.class) +@OperationProxy(ContainsNode.class) +@OperationProxy(FormatNode.class) +@OperationProxy(ExceptMatchNode.class) +@OperationProxy(GetYieldFromIterNode.class) +@OperationProxy(GetAwaitableNode.class) +@OperationProxy(SetupAnnotationsNode.class) +@OperationProxy(value = ListNodes.AppendNode.class, name = "ListAppend") +@OperationProxy(value = SetNodes.AddNode.class, name = "SetAdd") +@ShortCircuitOperation(name = "BoolAnd", booleanConverter = PBytecodeDSLRootNode.Yes.class, operator = Operator.AND_RETURN_VALUE) +@ShortCircuitOperation(name = "BoolOr", booleanConverter = PBytecodeDSLRootNode.Yes.class, operator = Operator.OR_RETURN_VALUE) +@ShortCircuitOperation(name = "PrimitiveBoolAnd", booleanConverter = PBytecodeDSLRootNode.BooleanIdentity.class, operator = Operator.AND_RETURN_CONVERTED) +@ShortCircuitOperation(name = "PrimitiveBoolOr", booleanConverter = PBytecodeDSLRootNode.BooleanIdentity.class, operator = Operator.OR_RETURN_CONVERTED) +@SuppressWarnings("unused") +public abstract class PBytecodeDSLRootNode extends PRootNode implements BytecodeRootNode { + public static final int EXPLODE_LOOP_THRESHOLD = 32; + private static final BytecodeConfig TRACE_AND_PROFILE_CONFIG = PBytecodeDSLRootNodeGen.newConfigBuilder() // + .addInstrumentation(TraceOrProfileCall.class) // + .addInstrumentation(TraceLine.class) // + .addInstrumentation(TraceLineAtLoopHeader.class) // + .addInstrumentation(TraceOrProfileReturn.class) // + .addInstrumentation(TraceException.class) // + .build(); + + @Child protected transient PythonObjectFactory factory = PythonObjectFactory.create(); + @Child private transient CalleeContext calleeContext = CalleeContext.create(); + @Child private transient ExceptionStateNodes.GetCaughtExceptionNode getCaughtExceptionNode; + @Child private transient MaterializeFrameNode traceMaterializeFrameNode = null; + @Child private transient ChainExceptionsNode chainExceptionsNode; + + // These fields are effectively final, but can only be set after construction. + @CompilationFinal protected transient BytecodeDSLCodeUnit co; + @CompilationFinal protected transient Signature signature; + @CompilationFinal protected transient int selfIndex; + @CompilationFinal protected transient int classcellIndex; + + private transient boolean pythonInternal; + @CompilationFinal private transient boolean internal; + + // For deferred deprecation warnings + @CompilationFinal private transient RaisePythonExceptionErrorCallback parserErrorCallback; + + @SuppressWarnings("this-escape") + protected PBytecodeDSLRootNode(PythonLanguage language, FrameDescriptor.Builder frameDescriptorBuilder) { + super(language, frameDescriptorBuilder.info(new BytecodeDSLFrameInfo()).build()); + ((BytecodeDSLFrameInfo) getFrameDescriptor().getInfo()).setRootNode(this); + } + + public PythonObjectFactory getFactory() { + return factory; + } + + public void setMetadata(BytecodeDSLCodeUnit co, RaisePythonExceptionErrorCallback parserErrorCallback) { + CompilerDirectives.transferToInterpreterAndInvalidate(); + this.co = co; + this.signature = co.computeSignature(); + this.classcellIndex = co.classcellIndex; + this.selfIndex = co.selfIndex; + this.internal = getSource().isInternal(); + this.parserErrorCallback = parserErrorCallback; + } + + @Override + public boolean isInternal() { + return internal; + } + + @Override + public boolean isPythonInternal() { + return pythonInternal; + } + + public void setPythonInternal(boolean pythonInternal) { + this.pythonInternal = pythonInternal; + } + + public void triggerDeferredDeprecationWarnings() { + if (parserErrorCallback != null) { + parserErrorCallback.triggerDeprecationWarnings(); + } + } + + @Override + public String toString() { + return ""; + } + + @Prolog + public static final class EnterCalleeContext { + @Specialization + public static void doEnter(VirtualFrame frame, + @Bind PBytecodeDSLRootNode root) { + root.calleeContext.enter(frame); + + if (root.needsTraceAndProfileInstrumentation()) { + root.ensureTraceAndProfileEnabled(); + root.getThreadState().pushInstrumentationData(root); + } + } + } + + @EpilogReturn + public static final class EpilogForReturn { + @Specialization + public static Object doExit(VirtualFrame frame, Object returnValue, + @Bind PBytecodeDSLRootNode root, + @Bind Node location) { + if (root.needsTraceAndProfileInstrumentation()) { + root.getThreadState().popInstrumentationData(root); + } + + root.calleeContext.exit(frame, root, location); + return returnValue; + } + } + + @EpilogExceptional + public static final class EpilogForException { + @Specialization + public static void doExit(VirtualFrame frame, AbstractTruffleException ate, + @Bind PBytecodeDSLRootNode root, + @Bind Node location) { + if (ate instanceof PException pe) { + pe.notifyAddedTracebackFrame(!root.isInternal()); + } + + if (root.needsTraceAndProfileInstrumentation()) { + root.traceOrProfileReturn(frame, location, null); + root.getThreadState().popInstrumentationData(root); + } + + root.calleeContext.exit(frame, root, location); + } + } + + /* + * Data for tracing, profiling and instrumentation + */ + public static final class InstrumentationData { + private final InstrumentationData previous; + private final PBytecodeDSLRootNode rootNode; + private int pastLine; + + public InstrumentationData(PBytecodeDSLRootNode rootNode, InstrumentationData previous) { + this.previous = previous; + this.rootNode = rootNode; + this.pastLine = -1; + } + + public InstrumentationData getPrevious() { + return previous; + } + + public PBytecodeDSLRootNode getRootNode() { + return rootNode; + } + + int getPastLine() { + return pastLine; + } + + void setPastLine(int pastLine) { + this.pastLine = pastLine; + } + + void clearPastLine() { + this.pastLine = -1; + } + } + + @NonIdempotent + public boolean needsTraceAndProfileInstrumentation() { + // We need instrumentation only if the assumption is invalid and the root node is visible. + return !PythonLanguage.get(this).noTracingOrProfilingAssumption.isValid() && !isInternal(); + } + + @NonIdempotent + public PythonThreadState getThreadState() { + return PythonContext.get(this).getThreadState(PythonLanguage.get(this)); + } + + /** + * Reparses with instrumentations for settrace and setprofile enabled. + */ + public void ensureTraceAndProfileEnabled() { + assert !isInternal(); + getRootNodes().update(TRACE_AND_PROFILE_CONFIG); + } + + private PFrame ensurePyFrame(VirtualFrame frame, Node location) { + if (traceMaterializeFrameNode == null) { + CompilerDirectives.transferToInterpreterAndInvalidate(); + traceMaterializeFrameNode = insert(MaterializeFrameNode.create()); + } + return traceMaterializeFrameNode.execute(frame, location, true, true); + } + + private void syncLocalsBackToFrame(VirtualFrame frame, PFrame pyFrame) { + GetFrameLocalsNode.syncLocalsBackToFrame(co, this, pyFrame, frame); + } + + /** + * When tracing/profiling is enabled, we emit a lot of extra operations. Reduce compiled code + * size by putting the calls behind a boundary (the uncached invoke will eventually perform an + * indirect call anyway). + */ + @TruffleBoundary + private static Object doInvokeProfileOrTraceFunction(Object fun, PFrame pyFrame, TruffleString eventName, Object arg) { + return CallTernaryMethodNode.getUncached().execute(null, fun, pyFrame, eventName, arg == null ? PNone.NONE : arg); + } + + @InliningCutoff + private void invokeProfileFunction(VirtualFrame virtualFrame, Node location, Object profileFun, + PythonContext.PythonThreadState threadState, PythonContext.ProfileEvent event, Object arg) { + if (threadState.isProfiling()) { + return; + } + threadState.profilingStart(); + PFrame pyFrame = ensurePyFrame(virtualFrame, location); + EncapsulatingNodeReference encapsulating = EncapsulatingNodeReference.getCurrent(); + Node oldEncapsulatingNode = encapsulating.set(location); + try { + // Force locals dict sync, so that we can sync them back later + GetFrameLocalsNode.executeUncached(pyFrame); + Object result = doInvokeProfileOrTraceFunction(profileFun, pyFrame, event.name, arg); + syncLocalsBackToFrame(virtualFrame, pyFrame); + Object realResult = result == PNone.NONE ? null : result; + pyFrame.setLocalTraceFun(realResult); + } catch (Throwable e) { + threadState.setProfileFun(null, PythonLanguage.get(this)); + throw e; + } finally { + threadState.profilingStop(); + encapsulating.set(oldEncapsulatingNode); + } + } + + @InliningCutoff + private void invokeTraceFunction(VirtualFrame virtualFrame, Node location, Object traceFun, PythonContext.PythonThreadState threadState, + PythonContext.TraceEvent event, Object arg, int line) { + if (threadState.isTracing()) { + return; + } + assert event != PythonContext.TraceEvent.DISABLED; + threadState.tracingStart(event); + PFrame pyFrame = ensurePyFrame(virtualFrame, location); + /** + * Call events use the thread-local trace function, which returns a "local" trace function + * to use for other trace events. + */ + boolean useLocalFn = event != TraceEvent.CALL; + Object traceFn = useLocalFn ? pyFrame.getLocalTraceFun() : traceFun; + if (traceFn == null) { + threadState.tracingStop(); + return; + } + Object nonNullArg = arg == null ? PNone.NONE : arg; + + EncapsulatingNodeReference encapsulating = EncapsulatingNodeReference.getCurrent(); + Node oldEncapsulatingNode = encapsulating.set(location); + try { + /** + * The PFrame syncs to the line of the current bci. Sometimes this location is + * inaccurate and needs to be overridden (e.g., when tracing an implicit return at the + * end of a function, we need to trace the line of the last statement executed). + */ + if (line != -1) { + pyFrame.setLineLock(line); + } + + // Force locals dict sync, so that we can sync them back later + GetFrameLocalsNode.executeUncached(pyFrame); + Object result = doInvokeProfileOrTraceFunction(traceFn, pyFrame, event.pythonName, nonNullArg); + syncLocalsBackToFrame(virtualFrame, pyFrame); + // https://github.com/python/cpython/issues/104232 + if (useLocalFn) { + Object realResult = result == PNone.NONE ? traceFn : result; + pyFrame.setLocalTraceFun(realResult); + } else if (result != PNone.NONE) { + pyFrame.setLocalTraceFun(result); + } else { + pyFrame.setLocalTraceFun(null); + } + } catch (Throwable e) { + threadState.setTraceFun(null, PythonLanguage.get(this)); + throw e; + } finally { + if (line != -1) { + pyFrame.lineUnlock(); + } + threadState.tracingStop(); + encapsulating.set(oldEncapsulatingNode); + } + } + + private final void traceOrProfileCall(VirtualFrame frame, Node location, BytecodeNode bytecode, int bci) { + PythonThreadState threadState = getThreadState(); + Object traceFun = threadState.getTraceFun(); + if (traceFun != null) { + int line = bciToLine(bci, bytecode); + invokeTraceFunction(frame, location, traceFun, threadState, TraceEvent.CALL, null, line); + } + Object profileFun = threadState.getProfileFun(); + if (profileFun != null) { + invokeProfileFunction(frame, location, profileFun, threadState, ProfileEvent.CALL, null); + } + } + + @InliningCutoff + private final void traceLine(VirtualFrame frame, Node location, int line) { + PythonThreadState threadState = getThreadState(); + InstrumentationData instrumentationData = threadState.getInstrumentationData(this); + + // TODO: this should never happen by nature of how we emit TraceLine, but sometimes does. + // needs investigation. + if (line == instrumentationData.getPastLine()) { + return; + } + instrumentationData.setPastLine(line); + + PFrame pyFrame = ensurePyFrame(frame, location); + if (pyFrame.getTraceLine()) { + Object traceFun = threadState.getTraceFun(); + if (traceFun != null) { + invokeTraceFunction(frame, location, traceFun, threadState, TraceEvent.LINE, null, line); + } + } + } + + @InliningCutoff + private final void traceLineAtLoopHeader(VirtualFrame frame, Node location, int line) { + PythonThreadState threadState = getThreadState(); + InstrumentationData instrumentationData = threadState.getInstrumentationData(this); + int pastLine = instrumentationData.getPastLine(); + + /** + * A loop should always be traced once, even if it is not entered. We also need to trace the + * loop header on each iteration. To accomplish this, we emit a TraceLine at the top of each + * loop (before loop initialization) and a TraceLineAtLoopHeader before the loop condition + * evaluates. To avoid tracing twice on the first iteration, we need to check our line + * against pastLine. + */ + if (line != pastLine) { + Object traceFun = threadState.getTraceFun(); + if (traceFun != null) { + invokeTraceFunction(frame, location, traceFun, threadState, TraceEvent.LINE, null, line); + } + } + /** + * If the loop is all on one line, we need to trace on each iteration (even though the line + * hasn't changed). Clear pastLine so the line comparison above succeeds. + */ + instrumentationData.clearPastLine(); + } + + private final void traceOrProfileReturn(VirtualFrame frame, Node location, Object value) { + PythonThreadState threadState = getThreadState(); + Object traceFun = threadState.getTraceFun(); + if (traceFun != null) { + invokeTraceFunction(frame, location, traceFun, threadState, TraceEvent.RETURN, value, threadState.getInstrumentationData(this).getPastLine()); + } + Object profileFun = threadState.getProfileFun(); + if (profileFun != null) { + invokeProfileFunction(frame, location, profileFun, threadState, ProfileEvent.RETURN, value); + } + } + + @InliningCutoff + private final PException traceException(VirtualFrame frame, BytecodeNode bytecode, int bci, PException pe) { + PException result = pe; + + PythonThreadState threadState = getThreadState(); + // We should only trace the exception if tracing is enabled. + if (threadState.getTraceFun() != null) { + PFrame pyFrame = ensurePyFrame(frame, bytecode); + // We use the local function for tracing exceptions. + if (pyFrame.getLocalTraceFun() != null) { + pe.markAsCaught(frame, this); + Object peForPython = pe.getEscapedException(); + Object peType = GetClassNode.executeUncached(peForPython); + Object traceback = ExceptionNodes.GetTracebackNode.executeUncached(peForPython); + try { + invokeTraceFunction(frame, bytecode, null, threadState, TraceEvent.EXCEPTION, + factory.createTuple(new Object[]{peType, peForPython, traceback}), + bciToLine(bci, bytecode)); + } catch (PException newPe) { + // If the trace function raises, handle its exception instead. + result = newPe; + // Below, we get the exception for reraise in order to hide the original + // raising site that's being traced (i.e., hiding the original cause). + } + // The exception was reified already. Return a new exception that looks like this + // catch didn't happen. + result = result.getExceptionForReraise(!isInternal()); + result.setCatchLocation(bci, bytecode); + } + } + return result; + } + + @Instrumentation + public static final class TraceOrProfileCall { + @Specialization + public static void perform(VirtualFrame frame, + @Bind Node location, + @Bind PBytecodeDSLRootNode root, + @Bind BytecodeNode bytecode, + @Bind("$bytecodeIndex") int bci) { + root.traceOrProfileCall(frame, location, bytecode, bci); + } + } + + @Instrumentation + @ConstantOperand(type = int.class) + public static final class TraceLine { + @Specialization + public static void perform(VirtualFrame frame, + int line, + @Bind Node location, + @Bind PBytecodeDSLRootNode root) { + root.traceLine(frame, location, line); + } + } + + @Instrumentation + @ConstantOperand(type = int.class) + public static final class TraceLineAtLoopHeader { + @Specialization + public static void perform(VirtualFrame frame, + int line, + @Bind Node location, + @Bind PBytecodeDSLRootNode root) { + root.traceLineAtLoopHeader(frame, location, line); + } + } + + @Instrumentation + public static final class TraceOrProfileReturn { + @Specialization + public static Object perform(VirtualFrame frame, Object value, + @Bind Node location, + @Bind PBytecodeDSLRootNode root) { + root.traceOrProfileReturn(frame, location, value); + return value; + } + } + + @Instrumentation + public static final class TraceException { + @Specialization + public static void perform() { + throw new UnsupportedOperationException("trace exception not implemented"); + } + } + + @Override + public Throwable interceptInternalException(Throwable throwable, BytecodeNode bytecodeNode, int bci) { + if (throwable instanceof StackOverflowError soe) { + PythonContext.get(this).ensureGilAfterFailure(); + return ExceptionUtils.wrapJavaException(soe, this, factory.createBaseException(RecursionError, ErrorMessages.MAXIMUM_RECURSION_DEPTH_EXCEEDED, new Object[]{})); + } + return throwable; + } + + @Override + public AbstractTruffleException interceptTruffleException(AbstractTruffleException ex, VirtualFrame frame, BytecodeNode bytecodeNode, int bci) { + if (ex instanceof PException pe) { + pe.setCatchLocation(bci, bytecodeNode); + + if (needsTraceAndProfileInstrumentation() && !getThreadState().isTracing()) { + pe = traceException(frame, bytecodeNode, bci, pe); + } + + // Fill in the __context__, if available. + if (getCaughtExceptionNode == null) { + CompilerDirectives.transferToInterpreterAndInvalidate(); + getCaughtExceptionNode = insert(ExceptionStateNodes.GetCaughtExceptionNode.create()); + } + AbstractTruffleException context = getCaughtExceptionNode.execute(frame); + if (context instanceof PException pe2) { + if (chainExceptionsNode == null) { + CompilerDirectives.transferToInterpreterAndInvalidate(); + chainExceptionsNode = insert(ChainExceptionsNode.create()); + } + chainExceptionsNode.execute(pe, pe2); + } + return pe; + } + + return ex; + } + + @Override + public boolean setsUpCalleeContext() { + return true; + } + + @Override + public String getName() { + if (co == null) { + // getName can be called by validation code before the code unit has been set. + return null; + } + return co.name.toJavaStringUncached(); + } + + @Override + public Signature getSignature() { + return signature; + } + + public BytecodeDSLCodeUnit getCodeUnit() { + return co; + } + + public int getFirstLineno() { + return co.startLine; + } + + protected Source getSource() { + SourceSection section = getSourceSection(); + if (section == null) { + return PythonUtils.createFakeSource(); + } + return section.getSource(); + } + + @TruffleBoundary + public int bciToLine(int bci, BytecodeNode bytecodeNode) { + return getSourceSectionForLocation(bci, bytecodeNode).getStartLine(); + } + + @TruffleBoundary + public SourceSection getSourceSectionForLocation(BytecodeLocation location) { + SourceSection sourceSection = null; + if (location != null) { + sourceSection = location.getSourceLocation(); + } + + if (sourceSection == null) { + /** + * If we don't have a source section, fall back on the root node's source section. This + * can happen, for example, when throwing into an unstarted generator, where we don't + * have an actual location (and the first line of the root node is expected). + */ + sourceSection = getSourceSection(); + } + + return sourceSection; + } + + @TruffleBoundary + public SourceSection getSourceSectionForLocation(int bci, BytecodeNode bytecodeNode) { + BytecodeLocation location = null; + if (bytecodeNode != null) { + location = bytecodeNode.getBytecodeLocation(bci); + } + return getSourceSectionForLocation(location); + } + + public static int bciToLasti(int bci, BytecodeNode bytecodeNode) { + if (bci <= 0) { + return bci; + } + int number = 0; + for (Instruction instruction : bytecodeNode.getInstructions()) { + if (instruction.isInstrumentation()) { + continue; + } + if (instruction.getBytecodeIndex() >= bci) { + return number; + } + // Emulate CPython's fixed 2-word instructions. + number += 2; + } + return -1; + } + + public static int lastiToBci(int lasti, BytecodeNode bytecodeNode) { + if (lasti < 0) { + return 0; + } + Iterator iter = bytecodeNode.getInstructions().iterator(); + assert iter.hasNext(); + + int nexti = 0; + Instruction result; + do { + result = iter.next(); + if (result.isInstrumentation()) { + continue; + } + nexti += 2; + } while (nexti <= lasti && iter.hasNext()); + return result.getBytecodeIndex(); + } + + @Override + protected byte[] extractCode() { + return MarshalModuleBuiltins.serializeCodeUnit(co); + } + + private static Object checkUnboundCell(PCell cell, int index, PBytecodeDSLRootNode rootNode, Node inliningTarget, PRaiseNode.Lazy raiseNode) { + Object result = cell.getRef(); + if (result == null) { + CompilerDirectives.transferToInterpreterAndInvalidate(); + CodeUnit codeUnit = rootNode.getCodeUnit(); + if (index < codeUnit.cellvars.length) { + TruffleString localName = codeUnit.cellvars[index]; + throw raiseNode.get(inliningTarget).raise(PythonBuiltinClassType.UnboundLocalError, ErrorMessages.LOCAL_VAR_REFERENCED_BEFORE_ASSIGMENT, localName); + } else { + TruffleString localName = codeUnit.freevars[index - codeUnit.cellvars.length]; + throw raiseNode.get(inliningTarget).raise(PythonBuiltinClassType.NameError, ErrorMessages.UNBOUNDFREEVAR, localName); + } + } + return result; + } + + public PCell readClassCell(Frame frame) { + if (classcellIndex < 0) { + return null; + } + return (PCell) getBytecodeNode().getLocalValue(0, frame, classcellIndex); + } + + public Object readSelf(Frame frame) { + if (selfIndex < 0) { + return null; + } else if (selfIndex == 0) { + return getBytecodeNode().getLocalValue(0, frame, 0); + } else { + PCell selfCell = (PCell) getBytecodeNode().getLocalValue(0, frame, selfIndex); + return selfCell.getRef(); + } + } + + @Operation + public static final class BooleanIdentity { + @Specialization + public static boolean doBoolean(boolean b) { + return b; + } + } + + @Operation + public static final class ArrayIndex { + @Specialization + public static Object doObject(Object[] array, int i) { + return array[i]; + } + } + + @Operation + public static final class UnwrapException { + @Specialization + public static Object doPException(PException ex) { + return ex.getEscapedException(); + } + + @Fallback + public static Object doOther(Object ex) { + // Let interop exceptions be + assert ex instanceof AbstractTruffleException; + return ex; + } + } + + @Operation + public static final class Yes { + @Specialization + public static boolean perform(VirtualFrame frame, Object o, + @Bind Node inliningTarget, + @Cached YesNode yesNode) { + return yesNode.executeBoolean(frame, inliningTarget, o); + } + } + + @Operation + public static final class Not { + @Specialization + public static boolean perform(VirtualFrame frame, Object o, + @Bind Node inliningTarget, + @Cached NotNode notNode) { + return notNode.executeBoolean(frame, inliningTarget, o); + } + } + + @Operation + public static final class MakeVariadic { + @Specialization + public static Object[] perform(@Variadic Object[] values) { + return values; + } + } + + @Operation + public static final class GetVariableArguments { + @Specialization + public static Object[] perform(VirtualFrame frame) { + return PArguments.getVariableArguments(frame); + } + } + + @Operation + @ConstantOperand(type = TruffleString.class) + public static final class WriteName { + @Specialization + public static void perform(VirtualFrame frame, TruffleString name, Object value, + @Cached WriteNameNode writeNode) { + writeNode.execute(frame, name, value); + } + } + + @Operation + @ConstantOperand(type = TruffleString.class) + public static final class ReadName { + @Specialization + public static Object perform(VirtualFrame frame, TruffleString name, + @Cached ReadNameNode readNode) { + return readNode.execute(frame, name); + } + } + + @Operation + @ConstantOperand(type = TruffleString.class) + public static final class DeleteName { + @Specialization(guards = "hasLocals(frame)") + public static void performLocals(VirtualFrame frame, TruffleString name, + @Bind Node inliningTarget, + @Cached PyObjectDelItem deleteNode) { + deleteNode.execute(frame, inliningTarget, PArguments.getSpecialArgument(frame), name); + } + + @Specialization(guards = "!hasLocals(frame)") + public static void performGlobals(VirtualFrame frame, TruffleString name, + @Cached DeleteGlobalNode deleteNode) { + deleteNode.executeWithGlobals(frame, PArguments.getGlobals(frame), name); + } + + public static boolean hasLocals(VirtualFrame frame) { + return PArguments.getSpecialArgument(frame) != null; + } + } + + @Operation + public static final class LoadArgumentOptional { + @Specialization + public static Object perform(VirtualFrame frame, int argumentIndex) { + Object[] arguments = frame.getArguments(); + if (arguments.length > argumentIndex) { + return arguments[argumentIndex]; + } else { + return null; + } + } + } + + @Operation + public static final class LoadVariableArguments { + @Specialization + public static Object perform(VirtualFrame frame, + @Bind PBytecodeDSLRootNode rootNode) { + return rootNode.factory.createTuple(PArguments.getVariableArguments(frame)); + } + } + + @Operation + public static final class LoadKeywordArguments { + @Specialization + public static Object perform(VirtualFrame frame, + @Bind PBytecodeDSLRootNode rootNode) { + return rootNode.factory.createDict(PArguments.getKeywordArguments(frame)); + } + } + + @Operation + @ConstantOperand(type = double.class) + @ConstantOperand(type = double.class) + public static final class LoadComplex { + @Specialization + public static Object perform(double real, double imag, + @Bind PBytecodeDSLRootNode rootNode) { + return rootNode.factory.createComplex(real, imag); + } + } + + @Operation + @ConstantOperand(type = BigInteger.class) + public static final class LoadBigInt { + @Specialization + public static Object perform(BigInteger bigInt, + @Bind PBytecodeDSLRootNode rootNode) { + return rootNode.factory.createInt(bigInt); + } + } + + @Operation + @ConstantOperand(type = byte[].class, dimensions = 0) + public static final class LoadBytes { + @Specialization + public static Object perform(byte[] bytes, + @Bind PBytecodeDSLRootNode rootNode) { + return rootNode.factory.createBytes(bytes); + } + } + + @Operation + public static final class Add { + @Specialization(rewriteOn = UnexpectedResultException.class) + public static int performInts(VirtualFrame frame, int left, int right, + @Bind Node inliningTarget, + @Cached @Shared PyNumberAddNode pyAddNode) throws UnexpectedResultException { + return pyAddNode.executeInt(frame, inliningTarget, left, right); + } + + @Specialization(rewriteOn = UnexpectedResultException.class) + public static double performDoubles(VirtualFrame frame, double left, double right, + @Bind Node inliningTarget, + @Cached @Shared PyNumberAddNode pyAddNode) throws UnexpectedResultException { + return pyAddNode.executeDouble(frame, inliningTarget, left, right); + } + + @Specialization + public static Object performObjects(VirtualFrame frame, Object left, Object right, + @Bind Node inliningTarget, + @Cached @Shared PyNumberAddNode pyAddNode) { + return pyAddNode.execute(frame, inliningTarget, left, right); + } + } + + @Operation + public static final class Mul { + @Specialization(rewriteOn = UnexpectedResultException.class) + public static int performInts(VirtualFrame frame, int left, int right, + @Bind Node inliningTarget, + @Cached @Shared PyNumberMultiplyNode pyMultiplyNode) throws UnexpectedResultException { + return pyMultiplyNode.executeInt(frame, inliningTarget, left, right); + } + + @Specialization(rewriteOn = UnexpectedResultException.class) + public static double performDoubles(VirtualFrame frame, double left, double right, + @Bind Node inliningTarget, + @Cached @Shared PyNumberMultiplyNode pyMultiplyNode) throws UnexpectedResultException { + return pyMultiplyNode.executeDouble(frame, inliningTarget, left, right); + } + + @Specialization + public static Object performObjects(VirtualFrame frame, Object left, Object right, + @Bind Node inliningTarget, + @Cached @Shared PyNumberMultiplyNode pyMultiplyNode) { + return pyMultiplyNode.execute(frame, inliningTarget, left, right); + } + } + + @Operation + public static final class GetIter { + @Specialization + public static Object perform(VirtualFrame frame, Object receiver, + @Bind Node inliningTarget, + @Cached PyObjectGetIter getIterNode) { + return getIterNode.execute(frame, inliningTarget, receiver); + } + } + + @Operation + public static final class GetItem { + @Specialization + public static Object perform(VirtualFrame frame, Object key, Object value, + @Bind Node inliningTarget, + @Cached PyObjectGetItem getItemNode) { + return getItemNode.execute(frame, inliningTarget, key, value); + } + } + + @Operation + public static final class FormatStr { + @Specialization + public static TruffleString perform(VirtualFrame frame, Object object, + @Bind Node inliningTarget, + @Cached PyObjectStrAsTruffleStringNode asTruffleStringNode) { + return asTruffleStringNode.execute(frame, inliningTarget, object); + } + } + + @Operation + public static final class FormatRepr { + @Specialization + public static TruffleString perform(VirtualFrame frame, Object object, + @Bind Node inliningTarget, + @Cached PyObjectReprAsTruffleStringNode asTruffleStringNode) { + return asTruffleStringNode.execute(frame, inliningTarget, object); + } + } + + @Operation + public static final class FormatAscii { + @Specialization + public static TruffleString perform(VirtualFrame frame, Object object, + @Bind Node inliningTarget, + @Cached PyObjectAsciiNode asTruffleStringNode) { + return asTruffleStringNode.execute(frame, inliningTarget, object); + } + } + + @Operation + public static final class PrintExpr { + @Specialization + public static void perform(VirtualFrame frame, Object object, + @Cached PrintExprNode printExprNode) { + printExprNode.execute(frame, object); + } + } + + @Operation + @ConstantOperand(type = TruffleString.class, name = "name") + @ConstantOperand(type = TruffleString.class, name = "qualifiedName") + @ConstantOperand(type = BytecodeDSLCodeUnit.class) + public static final class MakeFunction { + @Specialization(guards = {"isSingleContext(rootNode)", "!codeUnit.isGeneratorOrCoroutine()"}) + public static Object functionSingleContext(VirtualFrame frame, + TruffleString name, + TruffleString qualifiedName, + BytecodeDSLCodeUnit codeUnit, + Object[] defaults, + Object[] kwDefaultsObject, + Object closure, + Object annotations, + @Bind PBytecodeDSLRootNode rootNode, + @Cached(value = "createFunctionRootNode(rootNode, codeUnit)", adopt = false) PBytecodeDSLRootNode functionRootNode, + @Cached("createCode(rootNode, codeUnit, functionRootNode)") PCode cachedCode, + @Shared @CachedLibrary(limit = "1") DynamicObjectLibrary dylib) { + return createFunction(frame, name, qualifiedName, codeUnit.getDocstring(), cachedCode, defaults, kwDefaultsObject, closure, annotations, rootNode, dylib); + } + + @Specialization(replaces = "functionSingleContext", guards = "!codeUnit.isGeneratorOrCoroutine()") + public static Object functionMultiContext(VirtualFrame frame, + TruffleString name, + TruffleString qualifiedName, + BytecodeDSLCodeUnit codeUnit, + Object[] defaults, + Object[] kwDefaultsObject, + Object closure, + Object annotations, + @Bind PBytecodeDSLRootNode rootNode, + @Cached(value = "createFunctionRootNode(rootNode, codeUnit)", adopt = false) PBytecodeDSLRootNode functionRootNode, + @Shared @CachedLibrary(limit = "1") DynamicObjectLibrary dylib) { + PCode code = createCode(rootNode, codeUnit, functionRootNode); + return createFunction(frame, name, qualifiedName, codeUnit.getDocstring(), code, defaults, kwDefaultsObject, closure, annotations, rootNode, dylib); + } + + @Specialization(guards = {"isSingleContext(rootNode)", "codeUnit.isGeneratorOrCoroutine()"}) + public static Object generatorOrCoroutineSingleContext(VirtualFrame frame, + TruffleString name, + TruffleString qualifiedName, + BytecodeDSLCodeUnit codeUnit, + Object[] defaults, + Object[] kwDefaultsObject, + Object closure, + Object annotations, + @Bind PBytecodeDSLRootNode rootNode, + @Cached(value = "createFunctionRootNode(rootNode, codeUnit)", adopt = false) PBytecodeDSLRootNode functionRootNode, + @Cached(value = "createGeneratorRootNode(rootNode, functionRootNode, codeUnit)", adopt = false) PBytecodeDSLGeneratorFunctionRootNode generatorRootNode, + @Cached("createCode(rootNode, codeUnit, generatorRootNode)") PCode cachedCode, + @Shared @CachedLibrary(limit = "1") DynamicObjectLibrary dylib) { + return createFunction(frame, name, qualifiedName, codeUnit.getDocstring(), cachedCode, defaults, kwDefaultsObject, closure, annotations, rootNode, dylib); + } + + @Specialization(replaces = "generatorOrCoroutineSingleContext", guards = "codeUnit.isGeneratorOrCoroutine()") + public static Object generatorOrCoroutineMultiContext(VirtualFrame frame, + TruffleString name, + TruffleString qualifiedName, + BytecodeDSLCodeUnit codeUnit, + Object[] defaults, + Object[] kwDefaultsObject, + Object closure, + Object annotations, + @Bind PBytecodeDSLRootNode rootNode, + @Cached(value = "createFunctionRootNode(rootNode, codeUnit)", adopt = false) PBytecodeDSLRootNode functionRootNode, + @Cached(value = "createGeneratorRootNode(rootNode, functionRootNode, codeUnit)", adopt = false) PBytecodeDSLGeneratorFunctionRootNode generatorRootNode, + @Shared @CachedLibrary(limit = "1") DynamicObjectLibrary dylib) { + PCode code = createCode(rootNode, codeUnit, generatorRootNode); + return createFunction(frame, name, qualifiedName, codeUnit.getDocstring(), code, defaults, kwDefaultsObject, closure, annotations, rootNode, dylib); + } + + @Idempotent + protected static boolean isSingleContext(Node node) { + return PythonLanguage.get(node).isSingleContext(); + } + + @NeverDefault + protected static PBytecodeDSLRootNode createFunctionRootNode(PBytecodeDSLRootNode outerRootNode, BytecodeDSLCodeUnit codeUnit) { + return codeUnit.createRootNode(PythonContext.get(outerRootNode), outerRootNode.getSource()); + } + + @NeverDefault + protected static PBytecodeDSLGeneratorFunctionRootNode createGeneratorRootNode(PBytecodeDSLRootNode outerRootNode, PBytecodeDSLRootNode functionRootNode, + BytecodeDSLCodeUnit codeUnit) { + return new PBytecodeDSLGeneratorFunctionRootNode(PythonLanguage.get(outerRootNode), functionRootNode.getFrameDescriptor(), functionRootNode, codeUnit.name); + } + + @NeverDefault + protected static PCode createCode(PBytecodeDSLRootNode outerRootNode, BytecodeDSLCodeUnit codeUnit, PRootNode rootNode) { + return outerRootNode.getFactory().createCode( + rootNode.getCallTarget(), + rootNode.getSignature(), + codeUnit); + } + + protected static PFunction createFunction(VirtualFrame frame, + TruffleString name, TruffleString qualifiedName, TruffleString doc, + PCode code, Object[] defaults, + Object[] kwDefaultsObject, Object closure, Object annotations, + PBytecodeDSLRootNode node, + DynamicObjectLibrary dylib) { + PKeyword[] kwDefaults = new PKeyword[kwDefaultsObject.length]; + System.arraycopy(kwDefaultsObject, 0, kwDefaults, 0, kwDefaults.length); + PFunction function = node.factory.createFunction(name, qualifiedName, code, PArguments.getGlobals(frame), defaults, kwDefaults, (PCell[]) closure); + + if (annotations != null) { + dylib.put(function, T___ANNOTATIONS__, annotations); + } + if (doc != null) { + dylib.put(function, T___DOC__, doc); + } + + return function; + } + } + + @Operation + public static final class IAdd { + + @Specialization(rewriteOn = ArithmeticException.class) + public static int add(int left, int right) { + return Math.addExact(left, right); + } + + @Specialization + public static long doIIOvf(int left, int right) { + return left + (long) right; + } + + @Specialization(rewriteOn = ArithmeticException.class) + public static long addLong(long left, long right) { + return Math.addExact(left, right); + } + + @Specialization + public static double doDD(double left, double right) { + return left + right; + } + + @Specialization + public static double doDL(double left, long right) { + return left + right; + } + + @Specialization + public static double doLD(long left, double right) { + return left + right; + } + + @Specialization + @InliningCutoff + public static Object doGeneric(VirtualFrame frame, Object left, Object right, + @Cached LookupAndCallInplaceNode callNode) { + return callNode.executeObject(frame, left, right); + } + + @NeverDefault + public static LookupAndCallInplaceNode create() { + return LookupAndCallInplaceNode.create(InplaceArithmetic.IAdd); + } + } + + @Operation + public static final class IAnd { + + @Specialization + public static int add(int left, int right) { + return left & right; + } + + @Specialization + public static long addLong(long left, long right) { + return left & right; + } + + @Specialization + @InliningCutoff + public static Object doGeneric(VirtualFrame frame, Object left, Object right, + @Cached LookupAndCallInplaceNode callNode) { + return callNode.executeObject(frame, left, right); + } + + @NeverDefault + public static LookupAndCallInplaceNode create() { + return LookupAndCallInplaceNode.create(InplaceArithmetic.IAnd); + } + } + + @Operation + public static final class IOr { + + @Specialization + public static int add(int left, int right) { + return left | right; + } + + @Specialization + public static long addLong(long left, long right) { + return left | right; + } + + @Specialization + @InliningCutoff + public static Object doGeneric(VirtualFrame frame, Object left, Object right, + @Cached LookupAndCallInplaceNode callNode) { + return callNode.executeObject(frame, left, right); + } + + @NeverDefault + public static LookupAndCallInplaceNode create() { + return LookupAndCallInplaceNode.create(InplaceArithmetic.IOr); + } + } + + @Operation + public static final class IXor { + + @Specialization + public static int add(int left, int right) { + return left ^ right; + } + + @Specialization + public static long addLong(long left, long right) { + return left ^ right; + } + + @Specialization + @InliningCutoff + public static Object doGeneric(VirtualFrame frame, Object left, Object right, + @Cached LookupAndCallInplaceNode callNode) { + return callNode.executeObject(frame, left, right); + } + + @NeverDefault + public static LookupAndCallInplaceNode create() { + return LookupAndCallInplaceNode.create(InplaceArithmetic.IXor); + } + } + + @Operation + public static final class IRShift { + + @Specialization(guards = {"right < 32", "right >= 0"}) + public static int doIISmall(int left, int right) { + return left >> right; + } + + @Specialization(guards = {"right < 64", "right >= 0"}) + public static long doIISmall(long left, long right) { + return left >> right; + } + + @Specialization + @InliningCutoff + public static Object doGeneric(VirtualFrame frame, Object left, Object right, + @Cached LookupAndCallInplaceNode callNode) { + return callNode.executeObject(frame, left, right); + } + + @NeverDefault + public static LookupAndCallInplaceNode create() { + return LookupAndCallInplaceNode.create(InplaceArithmetic.IRShift); + } + } + + @Operation + public static final class ILShift { + + @Specialization(guards = {"right < 32", "right >= 0"}, rewriteOn = OverflowException.class) + public static int doII(int left, int right) throws OverflowException { + int result = left << right; + if (left != result >> right) { + throw OverflowException.INSTANCE; + } + return result; + } + + @Specialization(guards = {"right < 64", "right >= 0"}, rewriteOn = OverflowException.class) + public static long doLL(long left, long right) throws OverflowException { + long result = left << right; + if (left != result >> right) { + throw OverflowException.INSTANCE; + } + return result; + } + + @Specialization + @InliningCutoff + public static Object doGeneric(VirtualFrame frame, Object left, Object right, + @Cached LookupAndCallInplaceNode callNode) { + return callNode.executeObject(frame, left, right); + } + + @NeverDefault + public static LookupAndCallInplaceNode create() { + return LookupAndCallInplaceNode.create(InplaceArithmetic.ILShift); + } + } + + @Operation + public static final class IMatMul { + + @Specialization + @InliningCutoff + public static Object doGeneric(VirtualFrame frame, Object left, Object right, + @Cached LookupAndCallInplaceNode callNode) { + return callNode.executeObject(frame, left, right); + } + + @NeverDefault + public static LookupAndCallInplaceNode create() { + return LookupAndCallInplaceNode.create(InplaceArithmetic.IMatMul); + } + } + + @Operation + public static final class ISub { + + @Specialization(rewriteOn = ArithmeticException.class) + public static int doII(int left, int right) { + return Math.subtractExact(left, right); + } + + @Specialization + public static long doIIOvf(int x, int y) { + return x - (long) y; + } + + @Specialization(rewriteOn = ArithmeticException.class) + public static long addLong(long left, long right) { + return Math.subtractExact(left, right); + } + + @Specialization + public static double doDD(double left, double right) { + return left - right; + } + + @Specialization + public static double doDL(double left, long right) { + return left - right; + } + + @Specialization + public static double doLD(long left, double right) { + return left - right; + } + + @Specialization + @InliningCutoff + public static Object doGeneric(VirtualFrame frame, Object left, Object right, + @Cached LookupAndCallInplaceNode callNode) { + return callNode.executeObject(frame, left, right); + } + + @NeverDefault + public static LookupAndCallInplaceNode create() { + return LookupAndCallInplaceNode.create(InplaceArithmetic.ISub); + } + } + + @Operation + public static final class IMult { + + @Specialization(rewriteOn = ArithmeticException.class) + public static int add(int left, int right) { + return Math.multiplyExact(left, right); + } + + @Specialization + public static long doIIOvf(int x, int y) { + return x * (long) y; + } + + @Specialization(rewriteOn = ArithmeticException.class) + public static long addLong(long left, long right) { + return Math.multiplyExact(left, right); + } + + @Specialization + public static double doDD(double left, double right) { + return left * right; + } + + @Specialization + public static double doDL(double left, long right) { + return left * right; + } + + @Specialization + public static double doLD(long left, double right) { + return left * right; + } + + @Specialization + @InliningCutoff + public static Object doGeneric(VirtualFrame frame, Object left, Object right, + @Cached LookupAndCallInplaceNode callNode) { + return callNode.executeObject(frame, left, right); + } + + @NeverDefault + public static LookupAndCallInplaceNode create() { + return LookupAndCallInplaceNode.create(InplaceArithmetic.IMul); + } + } + + @Operation + public static final class ITrueDiv { + @Specialization + public static double doII(int left, int right, + @Bind Node inliningTarget, + @Shared @Cached PRaiseNode.Lazy raiseNode) { + return TrueDivNode.doII(left, right, inliningTarget, raiseNode); + } + + @Specialization + public static double doLD(long left, double right, + @Bind Node inliningTarget, + @Shared @Cached PRaiseNode.Lazy raiseNode) { + return TrueDivNode.doLD(left, right, inliningTarget, raiseNode); + } + + @Specialization + public static double doDL(double left, long right, + @Bind Node inliningTarget, + @Shared @Cached PRaiseNode.Lazy raiseNode) { + return TrueDivNode.doDL(left, right, inliningTarget, raiseNode); + } + + @Specialization + public static double doDD(double left, double right, + @Bind Node inliningTarget, + @Shared @Cached PRaiseNode.Lazy raiseNode) { + return TrueDivNode.doDD(left, right, inliningTarget, raiseNode); + } + + @Specialization + @InliningCutoff + public static Object doGeneric(VirtualFrame frame, Object left, Object right, + @Cached("createCallNode()") LookupAndCallInplaceNode callNode) { + return callNode.executeObject(frame, left, right); + } + + @NeverDefault + public static LookupAndCallInplaceNode createCallNode() { + return LookupAndCallInplaceNode.create(InplaceArithmetic.ITrueDiv); + } + } + + @Operation + public static final class IFloorDiv { + @Specialization + public static int doII(int left, int right, + @Bind Node inliningTarget, + @Shared @Cached PRaiseNode.Lazy raiseNode) { + return FloorDivNode.doII(left, right, inliningTarget, raiseNode); + } + + @Specialization(rewriteOn = OverflowException.class) + public static long doLL(long left, long right, + @Bind Node inliningTarget, + @Shared @Cached PRaiseNode.Lazy raiseNode) throws OverflowException { + return FloorDivNode.doLL(left, right, inliningTarget, raiseNode); + } + + @Specialization + public static double doDL(double left, long right, + @Bind Node inliningTarget, + @Shared @Cached PRaiseNode.Lazy raiseNode) { + return FloorDivNode.doDL(left, right, inliningTarget, raiseNode); + } + + @Specialization + public static double doDD(double left, double right, + @Bind Node inliningTarget, + @Shared @Cached PRaiseNode.Lazy raiseNode) { + return FloorDivNode.doDD(left, right, inliningTarget, raiseNode); + } + + @Specialization + public static double doLD(long left, double right, + @Bind Node inliningTarget, + @Shared @Cached PRaiseNode.Lazy raiseNode) { + return FloorDivNode.doLD(left, right, inliningTarget, raiseNode); + } + + @Specialization + @InliningCutoff + public static Object doGeneric(VirtualFrame frame, Object left, Object right, + @Cached("createCallNode()") LookupAndCallInplaceNode callNode) { + return callNode.executeObject(frame, left, right); + } + + @NeverDefault + public static LookupAndCallInplaceNode createCallNode() { + return LookupAndCallInplaceNode.create(InplaceArithmetic.IFloorDiv); + } + } + + @Operation + public static final class IPow { + @Specialization + @InliningCutoff + public static Object doGeneric(VirtualFrame frame, Object left, Object right, + @Cached("createCallNode()") LookupAndCallInplaceNode callNode) { + return callNode.executeObject(frame, left, right); + } + + @NeverDefault + public static LookupAndCallInplaceNode createCallNode() { + return LookupAndCallInplaceNode.create(InplaceArithmetic.IPow); + } + } + + @Operation + public static final class IMod { + @Specialization + public static int doII(int left, int right, + @Bind Node inliningTarget, + @Shared @Cached PRaiseNode.Lazy raiseNode) { + return ModNode.doII(left, right, inliningTarget, raiseNode); + } + + @Specialization(rewriteOn = OverflowException.class) + public static long doLL(long left, long right, + @Bind Node inliningTarget, + @Shared @Cached PRaiseNode.Lazy raiseNode) throws OverflowException { + return ModNode.doLL(left, right, inliningTarget, raiseNode); + } + + @Specialization + public static double doDL(double left, long right, + @Bind Node inliningTarget, + @Shared @Cached PRaiseNode.Lazy raiseNode) { + return ModNode.doDL(left, right, inliningTarget, raiseNode); + } + + @Specialization + public static double doDD(double left, double right, + @Bind Node inliningTarget, + @Shared @Cached PRaiseNode.Lazy raiseNode) { + return ModNode.doDD(left, right, inliningTarget, raiseNode); + } + + @Specialization + public static double doLD(long left, double right, + @Bind Node inliningTarget, + @Shared @Cached PRaiseNode.Lazy raiseNode) { + return ModNode.doLD(left, right, inliningTarget, raiseNode); + } + + @Specialization + @InliningCutoff + public static Object doGeneric(VirtualFrame frame, Object left, Object right, + @Cached("createCallNode()") LookupAndCallInplaceNode callNode) { + return callNode.executeObject(frame, left, right); + } + + @NeverDefault + public static LookupAndCallInplaceNode createCallNode() { + return LookupAndCallInplaceNode.create(InplaceArithmetic.IMod); + } + } + + @Operation + @ConstantOperand(type = LocalSetter.class) + public static final class ForIterate { + + @Specialization + public static boolean doIntegerSequence(VirtualFrame frame, LocalSetter output, PIntegerSequenceIterator iterator, + @Bind BytecodeNode bytecode, + @Bind("$bytecodeIndex") int bci) { + return doInteger(frame, output, iterator, bytecode, bci); + } + + @Specialization + public static boolean doIntRange(VirtualFrame frame, LocalSetter output, PIntRangeIterator iterator, + @Bind BytecodeNode bytecode, + @Bind("$bytecodeIndex") int bci) { + return doInteger(frame, output, iterator, bytecode, bci); + } + + private static boolean doInteger(VirtualFrame frame, LocalSetter output, + PIntegerIterator iterator, BytecodeNode bytecode, int bci) { + if (!iterator.hasNext()) { + iterator.setExhausted(); + return false; + } + output.setInt(bytecode, bci, frame, iterator.next()); + return true; + } + + @Specialization + public static boolean doObjectIterator(VirtualFrame frame, LocalSetter output, PObjectSequenceIterator iterator, + @Bind BytecodeNode bytecode, + @Bind("$bytecodeIndex") int bci) { + if (!iterator.hasNext()) { + iterator.setExhausted(); + output.setObject(bytecode, bci, frame, null); + return false; + } + Object value = iterator.next(); + output.setObject(bytecode, bci, frame, value); + return value != null; + } + + @Specialization + public static boolean doLongIterator(VirtualFrame frame, LocalSetter output, PLongSequenceIterator iterator, + @Bind BytecodeNode bytecode, + @Bind("$bytecodeIndex") int bci) { + if (!iterator.hasNext()) { + iterator.setExhausted(); + return false; + } + output.setLong(bytecode, bci, frame, iterator.next()); + return true; + } + + @Specialization + public static boolean doDoubleIterator(VirtualFrame frame, LocalSetter output, PDoubleSequenceIterator iterator, + @Bind BytecodeNode bytecode, + @Bind("$bytecodeIndex") int bci) { + if (!iterator.hasNext()) { + iterator.setExhausted(); + return false; + } + output.setDouble(bytecode, bci, frame, iterator.next()); + return true; + } + + @Specialization + @InliningCutoff + public static boolean doIterator(VirtualFrame frame, LocalSetter output, Object object, + @Bind Node inliningTarget, + @Bind BytecodeNode bytecode, + @Bind("$bytecodeIndex") int bci, + @Cached GetNextNode next, + @Cached IsBuiltinObjectProfile errorProfile) { + try { + Object value = next.execute(frame, object); + output.setObject(bytecode, bci, frame, value); + return value != null; + } catch (PException e) { + output.setObject(bytecode, bci, frame, null); + e.expectStopIteration(inliningTarget, errorProfile); + return false; + } + } + } + + @Operation + @ConstantOperand(type = TruffleString.class) + public static final class GetMethod { + @Specialization + public static Object doIt(VirtualFrame frame, + TruffleString name, Object obj, + @Bind Node inliningTarget, + @Cached PyObjectGetMethod getMethod) { + return getMethod.execute(frame, inliningTarget, obj, name); + } + } + + @Operation + @ConstantOperand(type = TruffleString.class) + public static final class GetAttribute { + @Specialization + @InliningCutoff + public static Object doIt(VirtualFrame frame, + TruffleString name, + Object obj, + @Cached("create(name)") GetFixedAttributeNode getAttributeNode) { + return getAttributeNode.execute(frame, obj); + } + } + + @Operation + @ConstantOperand(type = TruffleString.class) + public static final class SetAttribute { + @Specialization + @InliningCutoff + public static void doIt(VirtualFrame frame, + TruffleString key, + Object value, + Object object, + @Bind Node inliningTarget, + @Cached PyObjectSetAttr setAttrNode) { + setAttrNode.execute(frame, inliningTarget, object, key, value); + } + } + + @Operation + @ConstantOperand(type = TruffleString.class) + public static final class DeleteAttribute { + @Specialization + @InliningCutoff + public static Object doObject(VirtualFrame frame, + TruffleString key, + Object object, + @Cached LookupAndCallBinaryNode call) { + return call.executeObject(frame, object, key); + } + + @NeverDefault + public static LookupAndCallBinaryNode create() { + return LookupAndCallBinaryNode.create(SpecialMethodSlot.DelAttr); + } + } + + @Operation + @ImportStatic(SpecialMethodSlot.class) + public static final class DeleteItem { + @Specialization + public static void doWithFrame(VirtualFrame frame, Object primary, Object index, + @Bind Node inliningTarget, + @Cached GetClassNode getClassNode, + @Cached("create(DelItem)") LookupSpecialMethodSlotNode lookupDelitem, + @Cached PRaiseNode raise, + @Cached CallBinaryMethodNode callDelitem) { + Object delitem = lookupDelitem.execute(frame, getClassNode.execute(inliningTarget, primary), primary); + if (delitem == PNone.NO_VALUE) { + throw raise.raise(TypeError, ErrorMessages.OBJ_DOESNT_SUPPORT_DELETION, primary); + } + callDelitem.executeObject(frame, delitem, primary, index); + } + } + + @Operation + @ConstantOperand(type = TruffleString.class) + public static final class ReadGlobal { + @Specialization + public static Object perform(VirtualFrame frame, TruffleString name, + @Cached ReadGlobalOrBuiltinNode readNode) { + return readNode.execute(frame, name); + } + } + + @Operation + @ConstantOperand(type = TruffleString.class) + public static final class WriteGlobal { + @Specialization + public static void perform(VirtualFrame frame, TruffleString name, Object value, + @Cached WriteGlobalNode writeNode) { + writeNode.executeObject(frame, name, value); + } + } + + @Operation + @ConstantOperand(type = TruffleString.class) + public static final class DeleteGlobal { + @Specialization + public static void perform(VirtualFrame frame, TruffleString name, + @Cached DeleteGlobalNode deleteNode) { + deleteNode.executeWithGlobals(frame, PArguments.getGlobals(frame), name); + } + } + + @Operation + public static final class BuildClass { + + public static final TruffleString NAME = BuiltinNames.T___BUILD_CLASS__; + + @Specialization + @InliningCutoff + public static Object perform(VirtualFrame frame, + @Cached ReadGlobalOrBuiltinNode readNode) { + return readNode.execute(frame, NAME); + } + } + + @Operation + public static final class MakeList { + @Specialization + public static PList perform(Object[] elements, + @Bind PBytecodeDSLRootNode rootNode) { + return rootNode.factory.createList(elements); + } + } + + @Operation + @ImportStatic({PBytecodeDSLRootNode.class}) + public static final class MakeSet { + @Specialization(guards = {"elements.length == length", "length <= EXPLODE_LOOP_THRESHOLD"}, limit = "1") + @ExplodeLoop + public static PSet performExploded(VirtualFrame frame, Object[] elements, + @Bind PBytecodeDSLRootNode rootNode, + @Bind Node node, + @Cached(value = "elements.length") int length, + @Shared @Cached SetNodes.AddNode addNode, + @Shared @Cached HashingCollectionNodes.SetItemNode setItemNode) { + PSet set = rootNode.factory.createSet(); + for (int i = 0; i < length; i++) { + SetNodes.AddNode.add(frame, set, elements[i], addNode, setItemNode); + } + return set; + } + + @Specialization + public static PSet performRegular(VirtualFrame frame, Object[] elements, + @Bind PBytecodeDSLRootNode rootNode, + @Bind Node node, + @Shared @Cached SetNodes.AddNode addNode, + @Shared @Cached HashingCollectionNodes.SetItemNode setItemNode) { + PSet set = rootNode.factory.createSet(); + for (int i = 0; i < elements.length; i++) { + SetNodes.AddNode.add(frame, set, elements[i], addNode, setItemNode); + } + return set; + } + } + + @Operation + public static final class MakeEmptySet { + @Specialization + public static PSet perform(VirtualFrame frame, + @Bind PBytecodeDSLRootNode rootNode) { + return rootNode.factory.createSet(); + } + } + + @Operation + @ConstantOperand(type = int.class) + public static final class MakeFrozenSet { + @Specialization + public static PFrozenSet perform(VirtualFrame frame, + int length, + @Variadic Object[] elements, + @Cached HashingStorageSetItem hashingStorageLibrary, + @Bind PBytecodeDSLRootNode rootNode, + @Bind Node inliningTarget) { + HashingStorage setStorage; + if (length <= EXPLODE_LOOP_THRESHOLD) { + setStorage = doExploded(frame, inliningTarget, elements, length, hashingStorageLibrary); + } else { + setStorage = doRegular(frame, inliningTarget, elements, length, hashingStorageLibrary); + } + return rootNode.factory.createFrozenSet(setStorage); + } + + @ExplodeLoop + private static HashingStorage doExploded(VirtualFrame frame, Node inliningTarget, Object[] elements, int length, HashingStorageSetItem hashingStorageLibrary) { + CompilerAsserts.partialEvaluationConstant(length); + HashingStorage setStorage = EmptyStorage.INSTANCE; + for (int i = 0; i < length; ++i) { + Object o = elements[i]; + setStorage = hashingStorageLibrary.execute(frame, inliningTarget, setStorage, o, PNone.NONE); + } + return setStorage; + } + + private static HashingStorage doRegular(VirtualFrame frame, Node inliningTarget, Object[] elements, int length, HashingStorageSetItem hashingStorageLibrary) { + HashingStorage setStorage = EmptyStorage.INSTANCE; + for (int i = 0; i < length; ++i) { + Object o = elements[i]; + setStorage = hashingStorageLibrary.execute(frame, inliningTarget, setStorage, o, PNone.NONE); + } + return setStorage; + } + + } + + @Operation + public static final class MakeEmptyList { + @Specialization + public static PList perform(@Bind PBytecodeDSLRootNode rootNode) { + return rootNode.factory.createList(); + } + } + + @Operation + public static final class MakeTuple { + @Specialization + public static Object perform(Object[] elements, + @Bind PBytecodeDSLRootNode rootNode) { + return rootNode.factory.createTuple(elements); + } + } + + @Operation + @ConstantOperand(type = int[].class, dimensions = 0) + public static final class MakeConstantIntList { + @Specialization + public static PList perform(int[] array, + @Bind PBytecodeDSLRootNode rootNode) { + SequenceStorage storage = new IntSequenceStorage(PythonUtils.arrayCopyOf(array, array.length)); + return rootNode.factory.createList(storage); + } + } + + @Operation + @ConstantOperand(type = long[].class, dimensions = 0) + public static final class MakeConstantLongList { + @Specialization + public static PList perform(long[] array, + @Bind PBytecodeDSLRootNode rootNode) { + SequenceStorage storage = new LongSequenceStorage(PythonUtils.arrayCopyOf(array, array.length)); + return rootNode.factory.createList(storage); + } + } + + @Operation + @ConstantOperand(type = boolean[].class, dimensions = 0) + public static final class MakeConstantBooleanList { + @Specialization + public static PList perform(boolean[] array, + @Bind PBytecodeDSLRootNode rootNode) { + SequenceStorage storage = new BoolSequenceStorage(PythonUtils.arrayCopyOf(array, array.length)); + return rootNode.factory.createList(storage); + } + } + + @Operation + @ConstantOperand(type = double[].class, dimensions = 0) + public static final class MakeConstantDoubleList { + @Specialization + public static PList perform(double[] array, + @Bind PBytecodeDSLRootNode rootNode) { + SequenceStorage storage = new DoubleSequenceStorage(PythonUtils.arrayCopyOf(array, array.length)); + return rootNode.factory.createList(storage); + } + } + + @Operation + @ConstantOperand(type = Object[].class, dimensions = 0) + public static final class MakeConstantObjectList { + @Specialization + public static PList perform(Object[] array, + @Bind PBytecodeDSLRootNode rootNode) { + SequenceStorage storage = new ObjectSequenceStorage(PythonUtils.arrayCopyOf(array, array.length)); + return rootNode.factory.createList(storage); + } + } + + @Operation + @ConstantOperand(type = int[].class, dimensions = 0) + public static final class MakeConstantIntTuple { + @Specialization + public static PTuple perform(int[] array, + @Bind PBytecodeDSLRootNode rootNode) { + SequenceStorage storage = new IntSequenceStorage(array); + return rootNode.factory.createTuple(storage); + } + } + + @Operation + @ConstantOperand(type = long[].class, dimensions = 0) + public static final class MakeConstantLongTuple { + @Specialization + public static PTuple perform(long[] array, + @Bind PBytecodeDSLRootNode rootNode) { + SequenceStorage storage = new LongSequenceStorage(array); + return rootNode.factory.createTuple(storage); + } + } + + @Operation + @ConstantOperand(type = boolean[].class, dimensions = 0) + public static final class MakeConstantBooleanTuple { + @Specialization + public static PTuple perform(boolean[] array, + @Bind PBytecodeDSLRootNode rootNode) { + SequenceStorage storage = new BoolSequenceStorage(array); + return rootNode.factory.createTuple(storage); + } + } + + @Operation + @ConstantOperand(type = double[].class, dimensions = 0) + public static final class MakeConstantDoubleTuple { + @Specialization + public static PTuple perform(double[] array, + @Bind PBytecodeDSLRootNode rootNode) { + SequenceStorage storage = new DoubleSequenceStorage(array); + return rootNode.factory.createTuple(storage); + } + } + + @Operation + @ConstantOperand(type = Object[].class, dimensions = 0) + public static final class MakeConstantObjectTuple { + @Specialization + public static PTuple perform(Object[] array, + @Bind PBytecodeDSLRootNode rootNode) { + SequenceStorage storage = new ObjectSequenceStorage(array); + return rootNode.factory.createTuple(storage); + } + } + + @Operation + public static final class MakeSlice { + + @Specialization + public static Object doIII(int start, int end, int step, + @Bind PBytecodeDSLRootNode rootNode) { + return rootNode.factory.createIntSlice(start, end, step); + } + + @Specialization + public static Object doNIN(PNone start, int end, PNone step, + @Bind PBytecodeDSLRootNode rootNode) { + return rootNode.factory.createIntSlice(0, end, 1, true, true); + } + + @Specialization + public static Object doIIN(int start, int end, PNone step, + @Bind PBytecodeDSLRootNode rootNode) { + return rootNode.factory.createIntSlice(start, end, 1, false, true); + } + + @Specialization + public static Object doNII(PNone start, int end, int step, + @Bind PBytecodeDSLRootNode rootNode) { + return rootNode.factory.createIntSlice(0, end, step, true, false); + } + + @Specialization + @InliningCutoff + public static Object doGeneric(Object start, Object end, Object step, + @Bind PBytecodeDSLRootNode rootNode) { + return rootNode.factory.createObjectSlice(start, end, step); + } + } + + @Operation + @ConstantOperand(type = TruffleString[].class, dimensions = 0, specifyAtEnd = true) + public static final class MakeKeywords { + @Specialization + public static PKeyword[] perform(@Variadic Object[] values, TruffleString[] keys) { + if (keys.length <= EXPLODE_LOOP_THRESHOLD) { + return doExploded(keys, values); + } else { + return doRegular(keys, values); + } + } + + @ExplodeLoop + private static PKeyword[] doExploded(TruffleString[] keys, Object[] values) { + CompilerAsserts.partialEvaluationConstant(keys.length); + PKeyword[] result = new PKeyword[keys.length]; + for (int i = 0; i < keys.length; i++) { + result[i] = new PKeyword(keys[i], values[i]); + } + return result; + } + + private static PKeyword[] doRegular(TruffleString[] keys, Object[] values) { + PKeyword[] result = new PKeyword[keys.length]; + for (int i = 0; i < keys.length; i++) { + result[i] = new PKeyword(keys[i], values[i]); + } + return result; + } + } + + @Operation + public static final class MappingToKeywords { + @Specialization + public static PKeyword[] perform(Object sourceCollection, + @Bind Node inliningTarget, + @Cached ExpandKeywordStarargsNode expandKeywordStarargsNode, + @Cached PRaiseNode raise) { + return expandKeywordStarargsNode.execute(inliningTarget, sourceCollection); + } + } + + @Operation + @ConstantOperand(type = int.class) + public static final class MakeDict { + @Specialization + public static PDict perform(VirtualFrame frame, + int entries, + @Variadic Object[] keysAndValues, + @Bind PBytecodeDSLRootNode rootNode, + @Cached DictNodes.UpdateNode updateNode) { + PDict dict = rootNode.factory.createDict(); + if (entries <= EXPLODE_LOOP_THRESHOLD) { + doExploded(frame, keysAndValues, entries, updateNode, dict); + } else { + doRegular(frame, keysAndValues, entries, updateNode, dict); + } + return dict; + } + + @ExplodeLoop + private static void doExploded(VirtualFrame frame, Object[] keysAndValues, int entries, DictNodes.UpdateNode updateNode, PDict dict) { + CompilerAsserts.partialEvaluationConstant(entries); + for (int i = 0; i < entries; i++) { + Object key = keysAndValues[i * 2]; + Object value = keysAndValues[i * 2 + 1]; + if (key == PNone.NO_VALUE) { + updateNode.execute(frame, dict, value); + } else { + dict.setItem(key, value); + } + } + } + + private static void doRegular(VirtualFrame frame, Object[] keysAndValues, int entries, DictNodes.UpdateNode updateNode, PDict dict) { + for (int i = 0; i < entries; i++) { + Object key = keysAndValues[i * 2]; + Object value = keysAndValues[i * 2 + 1]; + if (key == PNone.NO_VALUE) { + updateNode.execute(frame, dict, value); + } else { + dict.setItem(key, value); + } + } + } + } + + @Operation + public static final class MakeEmptyDict { + @Specialization + public static PDict perform(@Bind PBytecodeDSLRootNode rootNode) { + return rootNode.factory.createDict(); + } + } + + @Operation + public static final class SetDictItem { + @Specialization + public static void perform(VirtualFrame frame, PDict item, Object key, Object value, + @Bind Node inliningTarget, + @Cached HashingStorageSetItem setItem) { + item.setDictStorage(setItem.execute(frame, inliningTarget, item.getDictStorage(), key, value)); + } + } + + public static final class LiteralBoolean { + @Specialization + public static boolean doBoolean(boolean value) { + return value; + } + } + + @Operation + public static final class SetItem { + @Specialization + public static void performB(VirtualFrame frame, boolean value, Object primary, Object slice, + @Bind Node inliningTarget, + @Shared @Cached PyObjectSetItem setItemNode) { + setItemNode.execute(frame, inliningTarget, primary, slice, value); + } + + @Specialization + public static void performI(VirtualFrame frame, int value, Object primary, Object slice, + @Bind Node inliningTarget, + @Shared @Cached PyObjectSetItem setItemNode) { + setItemNode.execute(frame, inliningTarget, primary, slice, value); + } + + @Specialization + public static void performL(VirtualFrame frame, long value, Object primary, Object slice, + @Bind Node inliningTarget, + @Shared @Cached PyObjectSetItem setItemNode) { + setItemNode.execute(frame, inliningTarget, primary, slice, value); + } + + @Specialization + public static void performD(VirtualFrame frame, double value, Object primary, Object slice, + @Bind Node inliningTarget, + @Shared @Cached PyObjectSetItem setItemNode) { + setItemNode.execute(frame, inliningTarget, primary, slice, value); + } + + @Specialization + public static void performO(VirtualFrame frame, Object value, Object primary, Object slice, + @Bind Node inliningTarget, + @Shared @Cached PyObjectSetItem setItemNode) { + setItemNode.execute(frame, inliningTarget, primary, slice, value); + } + } + + @Operation + @ConstantOperand(type = LocalSetterRange.class) + @ImportStatic({PGuards.class}) + public static final class UnpackToLocals { + @Specialization(guards = "isBuiltinSequence(sequence)") + @ExplodeLoop + public static void doUnpackSequence(VirtualFrame localFrame, LocalSetterRange results, PSequence sequence, + @Bind Node inliningTarget, + @Bind BytecodeNode bytecode, + @Bind("$bytecodeIndex") int bci, + @Cached SequenceNodes.GetSequenceStorageNode getSequenceStorageNode, + @Cached SequenceStorageNodes.GetItemScalarNode getItemNode, + @Cached InlinedBranchProfile errorProfile, + @Shared @Cached PRaiseNode raiseNode) { + SequenceStorage storage = getSequenceStorageNode.execute(inliningTarget, sequence); + int len = storage.length(); + + int count = results.getLength(); + CompilerAsserts.partialEvaluationConstant(count); + + if (len == count) { + for (int i = 0; i < count; i++) { + results.setObject(bytecode, bci, localFrame, i, getItemNode.execute(inliningTarget, storage, i)); + } + } else { + errorProfile.enter(inliningTarget); + if (len < count) { + throw raiseNode.raise(ValueError, ErrorMessages.NOT_ENOUGH_VALUES_TO_UNPACK, count, len); + } else { + throw raiseNode.raise(ValueError, ErrorMessages.TOO_MANY_VALUES_TO_UNPACK, count); + } + } + } + + @Specialization + @ExplodeLoop + @InliningCutoff + public static void doUnpackIterable(VirtualFrame virtualFrame, LocalSetterRange results, Object collection, + @Bind Node inliningTarget, + @Bind BytecodeNode bytecode, + @Bind("$bytecodeIndex") int bci, + @Cached PyObjectGetIter getIter, + @Cached GetNextNode getNextNode, + @Cached IsBuiltinObjectProfile notIterableProfile, + @Cached IsBuiltinObjectProfile stopIterationProfile1, + @Cached IsBuiltinObjectProfile stopIterationProfile2, + @Shared @Cached PRaiseNode raiseNode) { + int count = results.getLength(); + CompilerAsserts.partialEvaluationConstant(count); + + Object iterator; + try { + iterator = getIter.execute(virtualFrame, inliningTarget, collection); + } catch (PException e) { + e.expectTypeError(inliningTarget, notIterableProfile); + throw raiseNode.raise(PythonBuiltinClassType.TypeError, ErrorMessages.CANNOT_UNPACK_NON_ITERABLE, collection); + } + for (int i = 0; i < count; i++) { + try { + results.setObject(bytecode, bci, virtualFrame, i, getNextNode.execute(virtualFrame, iterator)); + } catch (PException e) { + e.expectStopIteration(inliningTarget, stopIterationProfile1); + throw raiseNode.raise(ValueError, ErrorMessages.NOT_ENOUGH_VALUES_TO_UNPACK, count, i); + } + } + try { + getNextNode.execute(virtualFrame, iterator); + } catch (PException e) { + e.expectStopIteration(inliningTarget, stopIterationProfile2); + return; + } + throw raiseNode.raise(ValueError, ErrorMessages.TOO_MANY_VALUES_TO_UNPACK, count); + } + } + + @Operation + @ConstantOperand(type = int.class) + @ConstantOperand(type = LocalSetterRange.class) + @ImportStatic({PGuards.class}) + @SuppressWarnings("truffle-interpreted-performance") + public static final class UnpackStarredToLocals { + @Specialization(guards = "isBuiltinSequence(sequence)") + public static void doUnpackSequence(VirtualFrame localFrame, + int starIndex, + LocalSetterRange results, + PSequence sequence, + @Cached SequenceNodes.GetSequenceStorageNode getSequenceStorageNode, + @Shared @Cached SequenceStorageNodes.GetItemScalarNode getItemNode, + @Shared @Cached SequenceStorageNodes.GetItemSliceNode getItemSliceNode, + @Cached InlinedBranchProfile errorProfile, + @Bind PBytecodeDSLRootNode rootNode, + @Bind BytecodeNode bytecode, + @Bind("$bytecodeIndex") int bci, + @Bind Node inliningTarget, + @Shared @Cached PRaiseNode raiseNode) { + int resultsLength = results.getLength(); + int countBefore = starIndex; + int countAfter = resultsLength - starIndex - 1; + + SequenceStorage storage = getSequenceStorageNode.execute(inliningTarget, sequence); + int len = storage.length(); + + int starLen = len - resultsLength + 1; + if (starLen < 0) { + errorProfile.enter(inliningTarget); + throw raiseNode.raise(ValueError, ErrorMessages.NOT_ENOUGH_VALUES_TO_UNPACK_EX, countBefore + countAfter, len); + } + + copyToLocalsFromSequence(storage, 0, 0, countBefore, results, localFrame, inliningTarget, bytecode, bci, getItemNode); + PList starList = rootNode.factory.createList(getItemSliceNode.execute(storage, countBefore, countBefore + starLen, 1, starLen)); + results.setObject(bytecode, bci, localFrame, starIndex, starList); + copyToLocalsFromSequence(storage, starIndex + 1, len - countAfter, countAfter, results, localFrame, inliningTarget, bytecode, bci, getItemNode); + } + + @Specialization + @InliningCutoff + public static void doUnpackIterable(VirtualFrame frame, + int starIndex, + LocalSetterRange results, + Object collection, + @Cached PyObjectGetIter getIter, + @Cached GetNextNode getNextNode, + @Cached IsBuiltinObjectProfile notIterableProfile, + @Cached IsBuiltinObjectProfile stopIterationProfile, + @Cached ListNodes.ConstructListNode constructListNode, + @Shared @Cached SequenceStorageNodes.GetItemScalarNode getItemNode, + @Shared @Cached SequenceStorageNodes.GetItemSliceNode getItemSliceNode, + @Bind PBytecodeDSLRootNode rootNode, + @Bind BytecodeNode bytecode, + @Bind("$bytecodeIndex") int bci, + @Bind Node inliningTarget, + @Shared @Cached PRaiseNode raiseNode) { + int resultsLength = results.getLength(); + int countBefore = starIndex; + int countAfter = resultsLength - starIndex - 1; + + Object iterator; + try { + iterator = getIter.execute(frame, inliningTarget, collection); + } catch (PException e) { + e.expectTypeError(inliningTarget, notIterableProfile); + throw raiseNode.raise(TypeError, ErrorMessages.CANNOT_UNPACK_NON_ITERABLE, collection); + } + + copyToLocalsFromIterator(iterator, countBefore, results, frame, inliningTarget, bytecode, bci, countBefore + countAfter, getNextNode, stopIterationProfile, raiseNode); + + PList starAndAfter = constructListNode.execute(frame, iterator); + SequenceStorage storage = starAndAfter.getSequenceStorage(); + int lenAfter = storage.length(); + if (lenAfter < countAfter) { + throw raiseNode.raise(ValueError, ErrorMessages.NOT_ENOUGH_VALUES_TO_UNPACK_EX, countBefore + countAfter, countBefore + lenAfter); + } + if (countAfter == 0) { + results.setObject(bytecode, bci, frame, starIndex, starAndAfter); + } else { + int starLen = lenAfter - countAfter; + PList starList = rootNode.factory.createList(getItemSliceNode.execute(storage, 0, starLen, 1, starLen)); + results.setObject(bytecode, bci, frame, starIndex, starList); + + copyToLocalsFromSequence(storage, starIndex + 1, starLen, countAfter, results, frame, inliningTarget, bytecode, bci, getItemNode); + } + } + + @ExplodeLoop + private static void copyToLocalsFromIterator(Object iterator, int length, LocalSetterRange results, + VirtualFrame frame, Node inliningTarget, BytecodeNode bytecode, int bci, + int requiredLength, GetNextNode getNextNode, IsBuiltinObjectProfile stopIterationProfile, PRaiseNode raiseNode) { + CompilerAsserts.partialEvaluationConstant(length); + for (int i = 0; i < length; i++) { + try { + Object item = getNextNode.execute(frame, iterator); + results.setObject(bytecode, bci, frame, i, item); + } catch (PException e) { + e.expectStopIteration(inliningTarget, stopIterationProfile); + throw raiseNode.raise(ValueError, ErrorMessages.NOT_ENOUGH_VALUES_TO_UNPACK_EX, requiredLength, i); + } + } + } + + @ExplodeLoop + private static void copyToLocalsFromSequence(SequenceStorage storage, int runOffset, int offset, int length, LocalSetterRange run, + VirtualFrame localFrame, Node inliningTarget, BytecodeNode bytecode, int bci, + SequenceStorageNodes.GetItemScalarNode getItemNode) { + CompilerAsserts.partialEvaluationConstant(length); + for (int i = 0; i < length; i++) { + run.setObject(bytecode, bci, localFrame, runOffset + i, getItemNode.execute(inliningTarget, storage, offset + i)); + } + } + } + + private static final RuntimeException notSupported(Object left, Object right, PRaiseNode raiseNode, TruffleString operator) { + throw raiseNode.raise(PythonErrorType.TypeError, ErrorMessages.NOT_SUPPORTED_BETWEEN_INSTANCES, operator, left, right); + } + + @Operation + public static final class Le { + + private static final TruffleString T_OPERATOR = PythonUtils.tsLiteral("<="); + + @Specialization + public static boolean cmp(int left, int right) { + return left <= right; + } + + @Specialization + public static boolean cmp(long left, long right) { + return left <= right; + } + + @Specialization + public static boolean cmp(char left, char right) { + return left <= right; + } + + @Specialization + public static boolean cmp(byte left, byte right) { + return left <= right; + } + + @Specialization + public static boolean cmp(double left, double right) { + return left <= right; + } + + @Specialization + public static boolean cmp(TruffleString left, TruffleString right, + @Cached TruffleString.CompareIntsUTF32Node compareIntsUTF32Node) { + return StringUtils.compareStrings(left, right, compareIntsUTF32Node) <= 0; + } + + @Specialization + public static boolean cmp(int left, double right) { + return left <= right; + } + + @Specialization + public static boolean cmp(double left, int right) { + return left <= right; + } + + @Specialization + @InliningCutoff + public static final Object doGeneric(VirtualFrame frame, Object left, Object right, + @Cached("createCallNode()") LookupAndCallBinaryNode callNode, + @Cached PRaiseNode raiseNode) { + Object result = callNode.executeObject(frame, left, right); + if (result == PNotImplemented.NOT_IMPLEMENTED) { + throw notSupported(left, right, raiseNode, T_OPERATOR); + } + return result; + } + + static LookupAndCallBinaryNode createCallNode() { + return LookupAndCallBinaryNode.create(SpecialMethodSlot.Le, SpecialMethodSlot.Ge, true, true); + } + } + + @Operation + public static final class Lt { + + private static final TruffleString T_OPERATOR = PythonUtils.tsLiteral("<"); + + @Specialization + public static boolean cmp(int left, int right) { + return left < right; + } + + @Specialization + public static boolean cmp(long left, long right) { + return left < right; + } + + @Specialization + public static boolean cmp(char left, char right) { + return left < right; + } + + @Specialization + public static boolean cmp(byte left, byte right) { + return left < right; + } + + @Specialization + public static boolean cmp(double left, double right) { + return left < right; + } + + @Specialization + public static boolean cmp(TruffleString left, TruffleString right, + @Cached TruffleString.CompareIntsUTF32Node compareIntsUTF32Node) { + return StringUtils.compareStrings(left, right, compareIntsUTF32Node) < 0; + } + + @Specialization + public static boolean cmp(int left, double right) { + return left < right; + } + + @Specialization + public static boolean cmp(double left, int right) { + return left < right; + } + + @Specialization + @InliningCutoff + public static final Object doGeneric(VirtualFrame frame, Object left, Object right, + @Cached("createCallNode()") LookupAndCallBinaryNode callNode, + @Cached PRaiseNode raiseNode) { + Object result = callNode.executeObject(frame, left, right); + if (result == PNotImplemented.NOT_IMPLEMENTED) { + throw notSupported(left, right, raiseNode, T_OPERATOR); + } + return result; + } + + static LookupAndCallBinaryNode createCallNode() { + return LookupAndCallBinaryNode.create(SpecialMethodSlot.Lt, SpecialMethodSlot.Gt, true, true); + } + } + + @Operation + public static final class Ge { + + private static final TruffleString T_OPERATOR = PythonUtils.tsLiteral(">="); + + @Specialization + public static boolean cmp(int left, int right) { + return left >= right; + } + + @Specialization + public static boolean cmp(long left, long right) { + return left >= right; + } + + @Specialization + public static boolean cmp(char left, char right) { + return left >= right; + } + + @Specialization + public static boolean cmp(byte left, byte right) { + return left >= right; + } + + @Specialization + public static boolean cmp(double left, double right) { + return left >= right; + } + + @Specialization + public static boolean cmp(TruffleString left, TruffleString right, + @Cached TruffleString.CompareIntsUTF32Node compareIntsUTF32Node) { + return StringUtils.compareStrings(left, right, compareIntsUTF32Node) >= 0; + } + + @Specialization + public static boolean cmp(int left, double right) { + return left >= right; + } + + @Specialization + public static boolean cmp(double left, int right) { + return left >= right; + } + + @Specialization + @InliningCutoff + public static final Object doGeneric(VirtualFrame frame, Object left, Object right, + @Cached("createCallNode()") LookupAndCallBinaryNode callNode, + @Cached PRaiseNode raiseNode) { + Object result = callNode.executeObject(frame, left, right); + if (result == PNotImplemented.NOT_IMPLEMENTED) { + throw notSupported(left, right, raiseNode, T_OPERATOR); + } + return result; + } + + static LookupAndCallBinaryNode createCallNode() { + return LookupAndCallBinaryNode.create(SpecialMethodSlot.Ge, SpecialMethodSlot.Le, true, true); + } + } + + @Operation + public static final class Gt { + + private static final TruffleString T_OPERATOR = PythonUtils.tsLiteral(">"); + + @Specialization + public static boolean cmp(int left, int right) { + return left > right; + } + + @Specialization + public static boolean cmp(long left, long right) { + return left > right; + } + + @Specialization + public static boolean cmp(char left, char right) { + return left > right; + } + + @Specialization + public static boolean cmp(byte left, byte right) { + return left > right; + } + + @Specialization + public static boolean cmp(double left, double right) { + return left > right; + } + + @Specialization + public static boolean cmp(TruffleString left, TruffleString right, + @Cached TruffleString.CompareIntsUTF32Node compareIntsUTF32Node) { + return StringUtils.compareStrings(left, right, compareIntsUTF32Node) > 0; + } + + @Specialization + public static boolean cmp(int left, double right) { + return left > right; + } + + @Specialization + public static boolean cmp(double left, int right) { + return left > right; + } + + @Specialization + @InliningCutoff + public static final Object doGeneric(VirtualFrame frame, Object left, Object right, + @Cached("createCallNode()") LookupAndCallBinaryNode callNode, + @Cached PRaiseNode raiseNode) { + Object result = callNode.executeObject(frame, left, right); + if (result == PNotImplemented.NOT_IMPLEMENTED) { + throw notSupported(left, right, raiseNode, T_OPERATOR); + } + return result; + } + + static LookupAndCallBinaryNode createCallNode() { + return LookupAndCallBinaryNode.create(SpecialMethodSlot.Gt, SpecialMethodSlot.Lt, true, true); + } + } + + @Operation + public static final class Eq { + + @Specialization + public static boolean cmp(int left, int right) { + return left == right; + } + + @Specialization + public static boolean cmp(long left, long right) { + return left == right; + } + + @Specialization + public static boolean cmp(char left, char right) { + return left == right; + } + + @Specialization + public static boolean cmp(byte left, byte right) { + return left == right; + } + + @Specialization + public static boolean cmp(double left, double right) { + return left == right; + } + + @Specialization + public static boolean cmp(TruffleString left, TruffleString right, + @Cached TruffleString.EqualNode equalNode) { + return equalNode.execute(left, right, PythonUtils.TS_ENCODING); + } + + @Specialization + public static boolean cmp(int left, double right) { + return left == right; + } + + @Specialization + public static boolean cmp(double left, int right) { + return left == right; + } + + @Specialization + @InliningCutoff + public static Object doGeneric(VirtualFrame frame, Object left, Object right, + @Cached("createCallNode()") LookupAndCallBinaryNode callNode, + @Cached IsNode isNode) { + Object result = callNode.executeObject(frame, left, right); + if (result == PNotImplemented.NOT_IMPLEMENTED) { + return isNode.execute(left, right); + } + return result; + } + + static LookupAndCallBinaryNode createCallNode() { + return LookupAndCallBinaryNode.create(SpecialMethodSlot.Eq, SpecialMethodSlot.Eq, true, true); + } + } + + @Operation + public static final class Ne { + + @Specialization + public static boolean cmp(int left, int right) { + return left != right; + } + + @Specialization + public static boolean cmp(long left, long right) { + return left != right; + } + + @Specialization + public static boolean cmp(char left, char right) { + return left != right; + } + + @Specialization + public static boolean cmp(byte left, byte right) { + return left != right; + } + + @Specialization + public static boolean cmp(double left, double right) { + return left != right; + } + + @Specialization + public static boolean cmp(TruffleString left, TruffleString right, + @Cached TruffleString.EqualNode equalNode) { + return !equalNode.execute(left, right, PythonUtils.TS_ENCODING); + } + + @Specialization + public static boolean cmp(int left, double right) { + return left != right; + } + + @Specialization + public static boolean cmp(double left, int right) { + return left != right; + } + + @Specialization + @InliningCutoff + public static Object doGeneric(VirtualFrame frame, Object left, Object right, + @Cached("createCallNode()") LookupAndCallBinaryNode callNode, + @Cached IsNode isNode) { + Object result = callNode.executeObject(frame, left, right); + if (result == PNotImplemented.NOT_IMPLEMENTED) { + return !isNode.execute(left, right); + } + return result; + } + + static LookupAndCallBinaryNode createCallNode() { + return LookupAndCallBinaryNode.create(SpecialMethodSlot.Ne, SpecialMethodSlot.Ne, true, true); + } + } + + @Operation + @ConstantOperand(type = TruffleString.class) + @ConstantOperand(type = TruffleString[].class, dimensions = 0) + @ConstantOperand(type = int.class) + public static final class Import { + @Specialization + @InliningCutoff + public static Object doImport(VirtualFrame frame, TruffleString name, TruffleString[] fromList, int level, + @Cached ImportNode node) { + return node.execute(frame, name, PArguments.getGlobals(frame), fromList, level); + } + } + + @Operation + @ConstantOperand(type = TruffleString.class) + public static final class ImportFrom { + @Specialization + @InliningCutoff + public static Object doImport(VirtualFrame frame, TruffleString name, Object module, + @Cached ImportFromNode node) { + return node.execute(frame, module, name); + } + } + + @Operation + @ConstantOperand(type = TruffleString.class) + @ConstantOperand(type = int.class) + public static final class ImportStar { + @Specialization + @InliningCutoff + public static void doImport(VirtualFrame frame, TruffleString name, int level, + @Cached("create(name, level)") ImportStarNode node) { + node.execute(frame, name, level); + } + + @NeverDefault + static ImportStarNode create(TruffleString name, int level) { + return ImportStarNode.create(); + } + } + + @Operation + public static final class Raise { + @Specialization + public static void perform(VirtualFrame frame, Object typeOrExceptionObject, Object cause, + @Bind PBytecodeDSLRootNode root, + @Cached RaiseNode raiseNode) { + raiseNode.execute(frame, typeOrExceptionObject, cause, !root.isInternal()); + } + } + + @Operation + public static final class Reraise { + @Specialization + public static void doPException(PException ex, + @Bind PBytecodeDSLRootNode root) { + throw ex.getExceptionForReraise(!root.isInternal()); + } + + @Specialization + public static void doAbstractTruffleException(AbstractTruffleException ex) { + throw ex; + } + } + + /** + * Throw is used internally for our try-catch-finally implementation when we need to throw an + * exception and catch it elsewhere. We don't need to do any of the work done by RaiseNode. + */ + @Operation + public static final class Throw { + @Specialization + public static void doAbstractTruffleException(AbstractTruffleException ex) { + throw ex; + } + } + + @Operation + public static final class GetCurrentException { + @Specialization + public static AbstractTruffleException doPException(VirtualFrame frame) { + return PArguments.getException(frame); + } + } + + @Operation + public static final class SetCurrentException { + @Specialization + @InliningCutoff + public static void doPException(VirtualFrame frame, PException ex) { + PArguments.setException(frame, ex); + } + + @Specialization(guards = {"notPException(ex)"}) + @InliningCutoff + public static void doAbstractTruffleException(VirtualFrame frame, AbstractTruffleException ex, + @Bind PBytecodeDSLRootNode rootNode) { + PArguments.setException(frame, ExceptionUtils.wrapJavaException(ex, rootNode, rootNode.factory.createBaseException(SystemError, ErrorMessages.M, new Object[]{ex}))); + } + + @Fallback + @InliningCutoff + public static void doNull(VirtualFrame frame, Object ex) { + assert ex == null; + PArguments.setException(frame, PException.NO_EXCEPTION); + } + + static boolean notPException(AbstractTruffleException ex) { + return ex != null && !(ex instanceof PException); + } + } + + @Operation + public static final class MarkExceptionAsCaught { + @Specialization + @InliningCutoff + public static void doPException(VirtualFrame frame, PException ex, + @Bind PBytecodeDSLRootNode rootNode) { + ex.markAsCaught(frame, rootNode); + } + + @Fallback + @InliningCutoff + public static void doNothing(@SuppressWarnings("unused") Object ex) { + } + } + + @Operation + public static final class GetExceptionForReraise { + @Specialization + @InliningCutoff + public static PException doPException(PException ex, + @Bind PBytecodeDSLRootNode rootNode) { + return ex.getExceptionForReraise(!rootNode.isInternal()); + } + + @Fallback + @InliningCutoff + public static Object doNothing(@SuppressWarnings("unused") Object ex) { + return ex; + } + } + + @Operation + public static final class AssertFailed { + @Specialization + public static void doAssertFailed(VirtualFrame frame, Object assertionMessage, + @Cached PRaiseNode raise) { + if (assertionMessage == PNone.NO_VALUE) { + throw raise.raise(AssertionError); + } else { + throw raise.raise(AssertionError, new Object[]{assertionMessage}); + } + } + } + + @Operation + @ConstantOperand(type = int.class) + public static final class LoadCell { + @Specialization + public static Object doLoadCell(int index, PCell cell, + @Bind PBytecodeDSLRootNode rootNode, + @Bind Node inliningTarget, + @Cached PRaiseNode.Lazy raiseNode) { + return checkUnboundCell(cell, index, rootNode, inliningTarget, raiseNode); + } + } + + @Operation + @ConstantOperand(type = int.class) + public static final class ClassLoadCell { + @Specialization + public static Object doLoadCell(VirtualFrame frame, + int index, + PCell cell, + @Bind PBytecodeDSLRootNode rootNode, + @Bind Node inliningTarget, + @Cached ReadFromLocalsNode readLocalsNode, + @Cached PRaiseNode.Lazy raiseNode) { + CodeUnit co = rootNode.getCodeUnit(); + TruffleString name = co.freevars[index - co.cellvars.length]; + Object locals = PArguments.getSpecialArgument(frame); + Object value = readLocalsNode.execute(frame, inliningTarget, locals, name); + if (value != PNone.NO_VALUE) { + return value; + } else { + return checkUnboundCell(cell, index, rootNode, inliningTarget, raiseNode); + } + } + } + + @Operation + public static final class StoreCell { + @Specialization + public static void doStoreCell(PCell cell, Object value) { + cell.setRef(value); + } + } + + @Operation + public static final class CreateCell { + @Specialization + public static PCell doCreateCell(Object value) { + PCell cell = new PCell(Assumption.create()); + cell.setRef(value); + return cell; + } + } + + @Operation + @ConstantOperand(type = int.class) + public static final class ClearCell { + @Specialization + public static void doClearCell(int index, PCell cell, + @Bind PBytecodeDSLRootNode rootNode, + @Bind Node inliningTarget, + @Cached PRaiseNode.Lazy raiseNode) { + checkUnboundCell(cell, index, rootNode, inliningTarget, raiseNode); + cell.clearRef(); + } + } + + @Operation + @ConstantOperand(type = LocalSetter.class) + public static final class ClearLocal { + @Specialization + public static void doClearLocal(VirtualFrame frame, LocalSetter localSetter, + @Bind BytecodeNode bytecode, + @Bind("$bytecodeIndex") int bci) { + localSetter.setObject(bytecode, bci, frame, null); + } + } + + @Operation + public static final class LoadClosure { + @Specialization + public static PCell[] doLoadClosure(VirtualFrame frame) { + return PArguments.getClosure(frame); + } + } + + @Operation + @ConstantOperand(type = LocalSetterRange.class) + public static final class StoreRange { + @Specialization + public static void perform(VirtualFrame frame, LocalSetterRange locals, Object[] values, + @Bind BytecodeNode bytecode, + @Bind("$bytecodeIndex") int bci) { + if (values.length <= EXPLODE_LOOP_THRESHOLD) { + doExploded(frame, locals, values, bytecode, bci); + } else { + doRegular(frame, locals, values, bytecode, bci); + } + } + + @ExplodeLoop + private static void doExploded(VirtualFrame frame, LocalSetterRange locals, Object[] values, + BytecodeNode bytecode, int bci) { + CompilerAsserts.partialEvaluationConstant(values.length); + assert values.length == locals.getLength(); + for (int i = 0; i < values.length; i++) { + locals.setObject(bytecode, bci, frame, i, values[i]); + } + } + + private static void doRegular(VirtualFrame frame, LocalSetterRange locals, Object[] values, + BytecodeNode bytecode, int bci) { + assert values.length == locals.getLength(); + for (int i = 0; i < values.length; i++) { + locals.setObject(bytecode, bci, frame, i, values[i]); + } + } + } + + @Operation + public static final class MakeCellArray { + @Specialization + public static PCell[] doMakeCellArray(@Variadic Object[] cells) { + return PCell.toCellArray(cells); + } + } + + /** + * Flattens an array of arrays. Used for splatting Starred expressions. + */ + @Operation + @ConstantOperand(type = int.class, specifyAtEnd = true) + public static final class Unstar { + @Specialization + public static Object[] perform(@Variadic Object[] values, + int length) { + if (length <= EXPLODE_LOOP_THRESHOLD) { + return doExploded(values, length); + } else { + return doRegular(values, length); + } + } + + @ExplodeLoop + private static Object[] doExploded(Object[] values, int length) { + CompilerAsserts.partialEvaluationConstant(length); + int totalLength = 0; + for (int i = 0; i < length; i++) { + totalLength += ((Object[]) values[i]).length; + } + Object[] result = new Object[totalLength]; + int idx = 0; + for (int i = 0; i < length; i++) { + int nl = ((Object[]) values[i]).length; + System.arraycopy(values[i], 0, result, idx, nl); + idx += nl; + } + return result; + } + + private static Object[] doRegular(Object[] values, int length) { + int totalLength = 0; + for (int i = 0; i < length; i++) { + totalLength += ((Object[]) values[i]).length; + } + Object[] result = new Object[totalLength]; + int idx = 0; + for (int i = 0; i < length; i++) { + int nl = ((Object[]) values[i]).length; + System.arraycopy(values[i], 0, result, idx, nl); + idx += nl; + } + return result; + } + } + + @Operation + public static final class KwargsMerge { + @Specialization + public static PDict doMerge(VirtualFrame frame, + PDict dict, + Object toMerge, + Object function, + @Bind PBytecodeDSLRootNode rootNode, + @Cached ConcatDictToStorageNode concatNode, + @Cached PRaiseNode raise) { + try { + HashingStorage resultStorage = concatNode.execute(frame, dict.getDictStorage(), toMerge); + dict.setDictStorage(resultStorage); + } catch (SameDictKeyException e) { + throw raise.raise(PythonBuiltinClassType.TypeError, ErrorMessages.S_GOT_MULTIPLE_VALUES_FOR_KEYWORD_ARG, PyObjectFunctionStr.execute(function), e.getKey()); + } catch (NonMappingException e) { + throw raise.raise(PythonBuiltinClassType.TypeError, ErrorMessages.ARG_AFTER_MUST_BE_MAPPING, PyObjectFunctionStr.execute(function), toMerge); + } + return dict; + } + } + + @Operation + @ImportStatic({PGuards.class}) + public static final class UnpackStarred { + @Specialization(guards = "isBuiltinSequence(sequence)") + public static Object[] doUnpackSequence(VirtualFrame localFrame, PSequence sequence, + @Bind Node inliningTarget, + @Cached SequenceNodes.GetSequenceStorageNode getSequenceStorageNode, + @Cached SequenceStorageNodes.GetItemScalarNode getItemNode, + @Shared @Cached PRaiseNode raiseNode) { + SequenceStorage storage = getSequenceStorageNode.execute(inliningTarget, sequence); + int len = storage.length(); + Object[] result = new Object[len]; + for (int i = 0; i < len; i++) { + result[i] = getItemNode.execute(inliningTarget, storage, i); + } + return result; + } + + @Specialization + @InliningCutoff + public static Object[] doUnpackIterable(VirtualFrame virtualFrame, Object collection, + @Cached PyObjectGetIter getIter, + @Cached GetNextNode getNextNode, + @Cached IsBuiltinObjectProfile notIterableProfile, + @Cached IsBuiltinObjectProfile stopIterationProfile1, + @Bind Node inliningTarget, + @Shared @Cached PRaiseNode raiseNode) { + + Object iterator; + try { + iterator = getIter.execute(virtualFrame, inliningTarget, collection); + } catch (PException e) { + e.expectTypeError(inliningTarget, notIterableProfile); + throw raiseNode.raise(PythonBuiltinClassType.TypeError, ErrorMessages.CANNOT_UNPACK_NON_ITERABLE, collection); + } + ArrayList result = new ArrayList<>(); + while (true) { + try { + Object item = getNextNode.execute(virtualFrame, iterator); + appendItem(result, item); + } catch (PException e) { + e.expectStopIteration(inliningTarget, stopIterationProfile1); + return result.toArray(); + } + } + } + + @TruffleBoundary + private static void appendItem(ArrayList result, Object item) { + result.add(item); + } + } + + @Operation + @ConstantOperand(type = int.class) + @ImportStatic({PGuards.class}) + public static final class UnpackSequence { + @Specialization(guards = "isBuiltinSequence(sequence)") + @ExplodeLoop + public static Object[] doUnpackSequence(VirtualFrame localFrame, + int count, + PSequence sequence, + @Bind Node inliningTarget, + @Cached SequenceNodes.GetSequenceStorageNode getSequenceStorageNode, + @Cached SequenceStorageNodes.GetItemScalarNode getItemNode, + @Shared @Cached PRaiseNode raiseNode) { + SequenceStorage storage = getSequenceStorageNode.execute(inliningTarget, sequence); + int len = storage.length(); + if (len == count) { + Object[] result = new Object[len]; + for (int i = 0; i < count; i++) { + result[i] = getItemNode.execute(inliningTarget, storage, i); + } + return result; + } else { + if (len < count) { + throw raiseNode.raise(ValueError, ErrorMessages.NOT_ENOUGH_VALUES_TO_UNPACK, count, len); + } else { + throw raiseNode.raise(ValueError, ErrorMessages.TOO_MANY_VALUES_TO_UNPACK, count); + } + } + } + + @Specialization + @ExplodeLoop + @InliningCutoff + public static Object[] doUnpackIterable(VirtualFrame virtualFrame, + int count, + Object collection, + @Cached PyObjectGetIter getIter, + @Cached GetNextNode getNextNode, + @Cached IsBuiltinObjectProfile notIterableProfile, + @Cached IsBuiltinObjectProfile stopIterationProfile1, + @Cached IsBuiltinObjectProfile stopIterationProfile2, + @Bind Node inliningTarget, + @Shared @Cached PRaiseNode raiseNode) { + Object iterator; + try { + iterator = getIter.execute(virtualFrame, inliningTarget, collection); + } catch (PException e) { + e.expectTypeError(inliningTarget, notIterableProfile); + throw raiseNode.raise(PythonBuiltinClassType.TypeError, ErrorMessages.CANNOT_UNPACK_NON_ITERABLE, collection); + } + + Object[] result = new Object[count]; + for (int i = 0; i < count; i++) { + try { + result[i] = getNextNode.execute(virtualFrame, iterator); + } catch (PException e) { + e.expectStopIteration(inliningTarget, stopIterationProfile1); + throw raiseNode.raise(ValueError, ErrorMessages.NOT_ENOUGH_VALUES_TO_UNPACK, count, i); + } + } + try { + getNextNode.execute(virtualFrame, iterator); + } catch (PException e) { + e.expectStopIteration(inliningTarget, stopIterationProfile2); + return result; + } + throw raiseNode.raise(ValueError, ErrorMessages.TOO_MANY_VALUES_TO_UNPACK, count); + } + } + + @Operation + @ConstantOperand(type = int.class) + @ConstantOperand(type = int.class) + @ImportStatic({PGuards.class}) + public static final class UnpackEx { + @Specialization(guards = "isBuiltinSequence(sequence)") + public static Object[] doUnpackSequence(VirtualFrame localFrame, + int countBefore, + int countAfter, + PSequence sequence, + @Bind Node inliningTarget, + @SuppressWarnings("unused") @Cached GetPythonObjectClassNode getClassNode, + @Cached SequenceNodes.GetSequenceStorageNode getSequenceStorageNode, + @Exclusive @Cached SequenceStorageNodes.GetItemScalarNode getItemNode, + @Exclusive @Cached SequenceStorageNodes.GetItemSliceNode getItemSliceNode, + @Shared @Cached PythonObjectFactory factory, + @Exclusive @Cached PRaiseNode.Lazy raiseNode) { + SequenceStorage storage = getSequenceStorageNode.execute(inliningTarget, sequence); + int len = storage.length(); + int starLen = len - countBefore - countAfter; + if (starLen < 0) { + throw raiseNode.get(inliningTarget).raise(ValueError, ErrorMessages.NOT_ENOUGH_VALUES_TO_UNPACK_EX, countBefore + countAfter, len); + } + + Object[] result = new Object[countBefore + 1 + countAfter]; + copyItemsToArray(inliningTarget, storage, 0, result, 0, countBefore, getItemNode); + result[countBefore] = factory.createList(getItemSliceNode.execute(storage, countBefore, countBefore + starLen, 1, starLen)); + copyItemsToArray(inliningTarget, storage, len - countAfter, result, countBefore + 1, countAfter, getItemNode); + return result; + } + + @Specialization + @InliningCutoff + public static Object[] doUnpackIterable(VirtualFrame virtualFrame, + int countBefore, + int countAfter, + Object collection, + @Bind Node inliningTarget, + @Cached PyObjectGetIter getIter, + @Cached GetNextNode getNextNode, + @Cached IsBuiltinObjectProfile notIterableProfile, + @Cached IsBuiltinObjectProfile stopIterationProfile, + @Cached ListNodes.ConstructListNode constructListNode, + @Exclusive @Cached SequenceStorageNodes.GetItemScalarNode getItemNode, + @Exclusive @Cached SequenceStorageNodes.GetItemSliceNode getItemSliceNode, + @Shared @Cached PythonObjectFactory factory, + @Exclusive @Cached PRaiseNode.Lazy raiseNode) { + Object iterator; + try { + iterator = getIter.execute(virtualFrame, inliningTarget, collection); + } catch (PException e) { + e.expectTypeError(inliningTarget, notIterableProfile); + throw raiseNode.get(inliningTarget).raise(PythonBuiltinClassType.TypeError, ErrorMessages.CANNOT_UNPACK_NON_ITERABLE, collection); + } + + Object[] result = new Object[countBefore + 1 + countAfter]; + copyItemsToArray(virtualFrame, inliningTarget, iterator, result, 0, countBefore, countBefore + countAfter, getNextNode, stopIterationProfile, raiseNode); + PList starAndAfter = constructListNode.execute(virtualFrame, iterator); + SequenceStorage storage = starAndAfter.getSequenceStorage(); + int lenAfter = storage.length(); + if (lenAfter < countAfter) { + throw raiseNode.get(inliningTarget).raise(ValueError, ErrorMessages.NOT_ENOUGH_VALUES_TO_UNPACK_EX, countBefore + countAfter, countBefore + lenAfter); + } + if (countAfter == 0) { + result[countBefore] = starAndAfter; + } else { + int starLen = lenAfter - countAfter; + PList starList = factory.createList(getItemSliceNode.execute(storage, 0, starLen, 1, starLen)); + result[countBefore] = starList; + copyItemsToArray(inliningTarget, storage, starLen, result, countBefore + 1, countAfter, getItemNode); + } + return result; + } + + @ExplodeLoop + private static void copyItemsToArray(VirtualFrame frame, Node inliningTarget, Object iterator, Object[] destination, int destinationOffset, int length, int totalLength, + GetNextNode getNextNode, + IsBuiltinObjectProfile stopIterationProfile, PRaiseNode.Lazy raiseNode) { + CompilerAsserts.partialEvaluationConstant(destinationOffset); + CompilerAsserts.partialEvaluationConstant(length); + CompilerAsserts.partialEvaluationConstant(totalLength); + for (int i = 0; i < length; i++) { + try { + destination[destinationOffset + i] = getNextNode.execute(frame, iterator); + } catch (PException e) { + e.expectStopIteration(inliningTarget, stopIterationProfile); + throw raiseNode.get(inliningTarget).raise(ValueError, ErrorMessages.NOT_ENOUGH_VALUES_TO_UNPACK_EX, totalLength, destinationOffset + i); + } + } + } + + @ExplodeLoop + private static void copyItemsToArray(Node inliningTarget, SequenceStorage source, int sourceOffset, Object[] destination, int destinationOffset, int length, + SequenceStorageNodes.GetItemScalarNode getItemNode) { + CompilerAsserts.partialEvaluationConstant(sourceOffset); + CompilerAsserts.partialEvaluationConstant(destinationOffset); + CompilerAsserts.partialEvaluationConstant(length); + for (int i = 0; i < length; i++) { + destination[destinationOffset + i] = getItemNode.execute(inliningTarget, source, sourceOffset + i); + } + } + } + + @Operation + public static final class CallNilaryMethod { + @Specialization + @InliningCutoff + public static Object doCall(VirtualFrame frame, Object callable, + @Cached CallNode node) { + return node.execute(frame, callable, PythonUtils.EMPTY_OBJECT_ARRAY, PKeyword.EMPTY_KEYWORDS); + } + } + + @Operation + public static final class CallUnaryMethod { + @Specialization + @InliningCutoff + public static Object doCall(VirtualFrame frame, Object callable, Object arg0, + @Cached CallUnaryMethodNode node) { + return node.executeObject(frame, callable, arg0); + } + } + + @Operation + public static final class CallBinaryMethod { + @Specialization + @InliningCutoff + public static Object doObject(VirtualFrame frame, Object callable, Object arg0, Object arg1, + @Cached CallBinaryMethodNode node) { + return node.executeObject(frame, callable, arg0, arg1); + } + } + + @Operation + public static final class CallTernaryMethod { + @Specialization + @InliningCutoff + public static Object doCall(VirtualFrame frame, Object callable, Object arg0, Object arg1, Object arg2, + @Cached CallTernaryMethodNode node) { + return node.execute(frame, callable, arg0, arg1, arg2); + } + } + + @Operation + public static final class CallQuaternaryMethod { + @Specialization + @InliningCutoff + public static Object doCall(VirtualFrame frame, Object callable, Object arg0, Object arg1, Object arg2, Object arg3, + @Cached CallQuaternaryMethodNode node) { + return node.execute(frame, callable, arg0, arg1, arg2, arg3); + } + } + + @Operation + public static final class CallVarargsMethod { + @Specialization + @InliningCutoff + public static Object doCall(VirtualFrame frame, Object callable, Object[] args, PKeyword[] keywords, + @Cached CallNode node) { + return node.execute(frame, callable, args, keywords); + } + } + + @Operation + @ConstantOperand(type = LocalSetter.class) + @ConstantOperand(type = LocalSetter.class) + @ImportStatic({SpecialMethodSlot.class}) + public static final class ContextManagerEnter { + @Specialization + @InliningCutoff + public static void doEnter(VirtualFrame frame, + LocalSetter exitSetter, + LocalSetter resultSetter, + Object contextManager, + @Bind Node inliningTarget, + @Bind BytecodeNode bytecode, + @Bind("$bytecodeIndex") int bci, + @Cached GetClassNode getClass, + @Cached(parameters = "Enter") LookupSpecialMethodSlotNode lookupEnter, + @Cached(parameters = "Exit") LookupSpecialMethodSlotNode lookupExit, + @Cached CallUnaryMethodNode callEnter, + @Cached PRaiseNode.Lazy raiseNode) { + Object type = getClass.execute(inliningTarget, contextManager); + Object enter = lookupEnter.execute(frame, type, contextManager); + if (enter == PNone.NO_VALUE) { + throw raiseNode.get(inliningTarget).raise(TypeError, ErrorMessages.N_OBJECT_DOES_NOT_SUPPORT_CONTEXT_MANAGER_PROTOCOL, type); + } + Object exit = lookupExit.execute(frame, type, contextManager); + if (exit == PNone.NO_VALUE) { + throw raiseNode.get(inliningTarget).raise(TypeError, ErrorMessages.N_OBJECT_DOES_NOT_SUPPORT_CONTEXT_MANAGER_PROTOCOL_EXIT, type); + } + Object result = callEnter.executeObject(frame, enter, contextManager); + exitSetter.setObject(bytecode, bci, frame, exit); + resultSetter.setObject(bytecode, bci, frame, result); + } + } + + @Operation + public static final class ContextManagerExit { + @Specialization + public static void doRegular(VirtualFrame frame, PNone none, Object exit, Object contextManager, + @Shared @Cached CallQuaternaryMethodNode callExit) { + callExit.execute(frame, exit, contextManager, PNone.NONE, PNone.NONE, PNone.NONE); + } + + @Specialization + @InliningCutoff + public static void doExceptional(VirtualFrame frame, + Object exception, Object exit, Object contextManager, + @Bind Node inliningTarget, + @Bind PBytecodeDSLRootNode rootNode, + @Shared @Cached CallQuaternaryMethodNode callExit, + @Cached GetClassNode getClass, + @Cached ExceptionNodes.GetTracebackNode getTraceback, + @Cached PyObjectIsTrueNode isTrue, + @Cached PRaiseNode.Lazy raiseNode) { + AbstractTruffleException savedExcState = PArguments.getException(frame); + try { + Object pythonException = exception; + if (exception instanceof PException pException) { + PArguments.setException(frame, pException); + pythonException = pException.getEscapedException(); + } + Object excType = getClass.execute(inliningTarget, pythonException); + Object excTraceback = getTraceback.execute(inliningTarget, pythonException); + Object result = callExit.execute(frame, exit, contextManager, excType, pythonException, excTraceback); + if (!isTrue.execute(frame, inliningTarget, result)) { + if (exception instanceof PException pException) { + throw pException.getExceptionForReraise(!rootNode.isInternal()); + } else if (exception instanceof AbstractTruffleException ate) { + throw ate; + } else { + throw CompilerDirectives.shouldNotReachHere("Exception not on stack"); + } + } + } finally { + PArguments.setException(frame, savedExcState); + } + } + } + + @Operation + @ConstantOperand(type = LocalSetter.class) + @ConstantOperand(type = LocalSetter.class) + @ImportStatic({SpecialMethodSlot.class}) + public static final class AsyncContextManagerEnter { + @Specialization + @InliningCutoff + public static void doEnter(VirtualFrame frame, + LocalSetter exitSetter, + LocalSetter resultSetter, + Object contextManager, + @Bind Node inliningTarget, + @Bind BytecodeNode bytecode, + @Bind("$bytecodeIndex") int bci, + @Cached GetClassNode getClass, + @Cached(parameters = "AEnter") LookupSpecialMethodSlotNode lookupEnter, + @Cached(parameters = "AExit") LookupSpecialMethodSlotNode lookupExit, + @Cached CallUnaryMethodNode callEnter, + @Cached PRaiseNode raiseNode) { + Object type = getClass.execute(inliningTarget, contextManager); + Object enter = lookupEnter.execute(frame, type, contextManager); + if (enter == PNone.NO_VALUE) { + throw raiseNode.raise(AttributeError, new Object[]{T___AENTER__}); + } + Object exit = lookupExit.execute(frame, type, contextManager); + if (exit == PNone.NO_VALUE) { + throw raiseNode.raise(AttributeError, new Object[]{T___AEXIT__}); + } + Object result = callEnter.executeObject(frame, enter, contextManager); + exitSetter.setObject(bytecode, bci, frame, exit); + resultSetter.setObject(bytecode, bci, frame, result); + } + } + + @Operation + public static final class AsyncContextManagerCallExit { + @Specialization + public static Object doRegular(VirtualFrame frame, + PNone none, Object exit, Object contextManager, + @Shared @Cached CallQuaternaryMethodNode callExit) { + return callExit.execute(frame, exit, contextManager, PNone.NONE, PNone.NONE, PNone.NONE); + } + + @Specialization + @InliningCutoff + public static Object doExceptional(VirtualFrame frame, + Object exception, Object exit, Object contextManager, + @Bind Node inliningTarget, + @Bind PBytecodeDSLRootNode rootNode, + @Shared @Cached CallQuaternaryMethodNode callExit, + @Cached GetClassNode getClass, + @Cached ExceptionNodes.GetTracebackNode getTraceback, + @Cached PyObjectIsTrueNode isTrue, + @Cached PRaiseNode.Lazy raiseNode) { + AbstractTruffleException savedExcState = PArguments.getException(frame); + try { + Object pythonException = exception; + if (exception instanceof PException) { + PArguments.setException(frame, (PException) exception); + pythonException = ((PException) exception).getEscapedException(); + } + Object excType = getClass.execute(inliningTarget, pythonException); + Object excTraceback = getTraceback.execute(inliningTarget, pythonException); + return callExit.execute(frame, exit, contextManager, excType, pythonException, excTraceback); + } finally { + PArguments.setException(frame, savedExcState); + } + } + } + + @Operation + public static final class AsyncContextManagerExit { + /** + * NB: There is nothing to do after awaiting __exit__(None, None, None), so this operation + * is only emitted for the case where the context manager exits due to an exception. + */ + @Specialization + @InliningCutoff + public static void doExceptional(VirtualFrame frame, + Object exception, Object result, + @Bind Node inliningTarget, + @Bind PBytecodeDSLRootNode rootNode, + @Cached CallQuaternaryMethodNode callExit, + @Cached GetClassNode getClass, + @Cached ExceptionNodes.GetTracebackNode getTraceback, + @Cached PyObjectIsTrueNode isTrue, + @Cached PRaiseNode.Lazy raiseNode) { + if (!isTrue.execute(frame, inliningTarget, result)) { + if (exception instanceof PException) { + throw ((PException) exception).getExceptionForReraise(!rootNode.isInternal()); + } else if (exception instanceof AbstractTruffleException) { + throw (AbstractTruffleException) exception; + } else { + throw CompilerDirectives.shouldNotReachHere("Exception not on stack"); + } + } + } + } + + @Operation + @ConstantOperand(type = int.class) + public static final class BuildString { + @Specialization + public static Object perform( + int length, + @Variadic Object[] strings, + @Cached TruffleStringBuilder.AppendStringNode appendNode, + @Cached TruffleStringBuilder.ToStringNode toString) { + TruffleStringBuilder tsb = TruffleStringBuilder.create(PythonUtils.TS_ENCODING); + if (length <= EXPLODE_LOOP_THRESHOLD) { + doExploded(strings, length, appendNode, tsb); + } else { + doRegular(strings, length, appendNode, tsb); + } + return toString.execute(tsb); + } + + @ExplodeLoop + private static void doExploded(Object[] strings, int length, TruffleStringBuilder.AppendStringNode appendNode, TruffleStringBuilder tsb) { + CompilerAsserts.partialEvaluationConstant(length); + for (int i = 0; i < length; i++) { + appendNode.execute(tsb, (TruffleString) strings[i]); + } + } + + private static void doRegular(Object[] strings, int length, TruffleStringBuilder.AppendStringNode appendNode, TruffleStringBuilder tsb) { + for (int i = 0; i < length; i++) { + appendNode.execute(tsb, (TruffleString) strings[i]); + } + } + } + + @Operation + public static final class ListExtend { + @Specialization + public static void listExtend(VirtualFrame frame, PList list, Object obj, + @Bind Node inliningTarget, + @Cached IteratorNodes.GetLength lenNode, + @Cached("createExtend()") SequenceStorageNodes.ExtendNode extendNode) { + ListExtendNode.extendSequence(frame, list, obj, inliningTarget, lenNode, extendNode); + } + + @NeverDefault + public static SequenceStorageNodes.ExtendNode createExtend() { + return SequenceStorageNodes.ExtendNode.create(ListGeneralizationNode.SUPPLIER); + } + } + + @Operation + @ConstantOperand(type = LocalSetter.class) + public static final class TeeLocal { + @Specialization + public static int doInt(VirtualFrame frame, LocalSetter local, int value, + @Bind BytecodeNode bytecode, + @Bind("$bytecodeIndex") int bci) { + local.setInt(bytecode, bci, frame, value); + return value; + } + + @Specialization + public static double doDouble(VirtualFrame frame, LocalSetter local, double value, + @Bind BytecodeNode bytecode, + @Bind("$bytecodeIndex") int bci) { + local.setDouble(bytecode, bci, frame, value); + return value; + } + + @Specialization + public static long doLong(VirtualFrame frame, LocalSetter local, long value, + @Bind BytecodeNode bytecode, + @Bind("$bytecodeIndex") int bci) { + local.setLong(bytecode, bci, frame, value); + return value; + } + + @Specialization(replaces = {"doInt", "doDouble", "doLong"}) + public static Object doObject(VirtualFrame frame, LocalSetter local, Object value, + @Bind BytecodeNode bytecode, + @Bind("$bytecodeIndex") int bci) { + local.setObject(bytecode, bci, frame, value); + return value; + } + } + + @Operation + public static final class NonNull { + @Specialization + public static boolean doObject(Object value) { + return value != null; + } + } + + @Operation + public static final class GetLen { + @Specialization + public static int doObject(VirtualFrame frame, Object value, + @Bind Node inliningTarget, + @Cached PyObjectSizeNode sizeNode) { + return sizeNode.execute(frame, inliningTarget, value); + } + } + + @Operation + public static final class IsSequence { + @Specialization + public static boolean doObject(Object value, + @Cached GetTPFlagsNode getTPFlagsNode) { + return (getTPFlagsNode.execute(value) & TypeFlags.SEQUENCE) != 0; + } + } + + @Operation + public static final class IsMapping { + @Specialization + public static boolean doObject(Object value, + @Cached GetTPFlagsNode getTPFlagsNode) { + return (getTPFlagsNode.execute(value) & TypeFlags.MAPPING) != 0; + } + } + + @Operation + @ImportStatic(PGuards.class) + public static final class BinarySubscript { + // TODO: support boxing elimination +// @Specialization(guards = "cannotBeOverriddenForImmutableType(sequence)") +// public static int doIntSequence(PList sequence, int index, +// @Shared("list") @Cached("createForList()") SequenceStorageNodes.GetItemNode getItemNode) throws +// UnexpectedResultException { +// return getItemNode.executeInt(sequence.getSequenceStorage(), index); +// } +// +// @Specialization(guards = "cannotBeOverriddenForImmutableType(sequence)") +// public static int doIntTuple(PTuple sequence, int index, +// @Shared("tuple") @Cached("createForTuple()") SequenceStorageNodes.GetItemNode getItemNode) throws +// UnexpectedResultException { +// return getItemNode.executeInt(sequence.getSequenceStorage(), index); +// } +// +// @Specialization(guards = "cannotBeOverriddenForImmutableType(sequence)") +// public static double doDoubleSequence(PList sequence, int index, +// @Shared("list") @Cached("createForList()") SequenceStorageNodes.GetItemNode getItemNode) throws +// UnexpectedResultException { +// return getItemNode.executeDouble(sequence.getSequenceStorage(), index); +// } +// +// @Specialization(guards = "cannotBeOverriddenForImmutableType(sequence)") +// public static double doDoubleTuple(PTuple sequence, int index, +// @Shared("tuple") @Cached("createForTuple()") SequenceStorageNodes.GetItemNode getItemNode) throws +// UnexpectedResultException { +// return getItemNode.executeDouble(sequence.getSequenceStorage(), index); +// } +// TODO: add @Shared to GetItemNodes + + @Specialization(guards = "isBuiltinList(sequence)") + public static Object doObjectSequence(PList sequence, int index, + @Cached("createForList()") SequenceStorageNodes.GetItemNode getItemNode) { + return getItemNode.execute(sequence.getSequenceStorage(), index); + } + + @Specialization(guards = "isBuiltinTuple(sequence)") + public static Object doObjectTuple(PTuple sequence, int index, + @Cached("createForTuple()") SequenceStorageNodes.GetItemNode getItemNode) { + return getItemNode.execute(sequence.getSequenceStorage(), index); + } + + @Specialization + public static Object doObjectKey(VirtualFrame frame, Object receiver, Object key, + @Cached(inline = false) PyObjectGetItem getItemNode) { + return getItemNode.executeCached(frame, receiver, key); + } + } + + /** + * Performs some clean-up steps before suspending execution. + */ + @Operation + public static final class PreYield { + @Specialization + public static Object doObject(VirtualFrame frame, Object value, + @Bind Node location, + @Bind PBytecodeDSLRootNode root) { + if (root.needsTraceAndProfileInstrumentation()) { + root.traceOrProfileReturn(frame, location, value); + root.getThreadState().popInstrumentationData(root); + } + return value; + } + } + + /** + * Resumes execution after yield. + */ + @Operation + public static final class ResumeYield { + @Specialization + public static Object doObject(VirtualFrame frame, Object sendValue, + @Bind Node location, + @Bind PBytecodeDSLRootNode root, + @Bind BytecodeNode bytecode, + @Bind("$bytecodeIndex") int bci, + @Cached GetSendValueNode getSendValue) { + if (root.needsTraceAndProfileInstrumentation()) { + // We may not have reparsed the root with instrumentation yet. + root.ensureTraceAndProfileEnabled(); + root.getThreadState().pushInstrumentationData(root); + root.traceOrProfileCall(frame, location, bytecode, bci); + } + + return getSendValue.execute(sendValue); + } + } + + @Operation + @ConstantOperand(type = LocalSetter.class) + @ConstantOperand(type = LocalSetter.class) + @SuppressWarnings("truffle-interpreted-performance") + public static final class YieldFromSend { + private static final TruffleString T_SEND = tsLiteral("send"); + + @Specialization + static boolean doGenerator(VirtualFrame virtualFrame, + LocalSetter yieldedValue, + LocalSetter returnedValue, + PGenerator generator, + Object arg, + @Bind Node inliningTarget, + @Bind BytecodeNode bytecode, + @Bind("$bytecodeIndex") int bci, + @Cached CommonGeneratorBuiltins.SendNode sendNode, + @Shared @Cached IsBuiltinObjectProfile stopIterationProfile, + @Shared @Cached StopIterationBuiltins.StopIterationValueNode getValue) { + try { + Object value = sendNode.execute(virtualFrame, generator, arg); + yieldedValue.setObject(bytecode, bci, virtualFrame, value); + return false; + } catch (PException e) { + handleException(virtualFrame, e, inliningTarget, bytecode, bci, stopIterationProfile, getValue, returnedValue); + return true; + } + } + + @Specialization(guards = "iterCheck.execute(inliningTarget, iter)", limit = "1") + static boolean doIterator(VirtualFrame virtualFrame, + LocalSetter yieldedValue, + LocalSetter returnedValue, + Object iter, + @SuppressWarnings("unused") PNone arg, + @Bind Node inliningTarget, + @Bind BytecodeNode bytecode, + @Bind("$bytecodeIndex") int bci, + @SuppressWarnings("unused") @Cached PyIterCheckNode iterCheck, + @Cached GetNextNode getNextNode, + @Shared @Cached IsBuiltinObjectProfile stopIterationProfile, + @Shared @Cached StopIterationBuiltins.StopIterationValueNode getValue) { + try { + Object value = getNextNode.execute(virtualFrame, iter); + yieldedValue.setObject(bytecode, bci, virtualFrame, value); + return false; + } catch (PException e) { + handleException(virtualFrame, e, inliningTarget, bytecode, bci, stopIterationProfile, getValue, returnedValue); + return true; + } + } + + @Fallback + static boolean doOther(VirtualFrame virtualFrame, + LocalSetter yieldedValue, + LocalSetter returnedValue, + Object obj, + Object arg, + @Bind Node inliningTarget, + @Bind BytecodeNode bytecode, + @Bind("$bytecodeIndex") int bci, + @Cached PyObjectCallMethodObjArgs callMethodNode, + @Shared @Cached IsBuiltinObjectProfile stopIterationProfile, + @Shared @Cached StopIterationBuiltins.StopIterationValueNode getValue) { + try { + Object value = callMethodNode.execute(virtualFrame, inliningTarget, obj, T_SEND, arg); + yieldedValue.setObject(bytecode, bci, virtualFrame, value); + return false; + } catch (PException e) { + handleException(virtualFrame, e, inliningTarget, bytecode, bci, stopIterationProfile, getValue, returnedValue); + return true; + } + } + + private static void handleException(VirtualFrame frame, PException e, Node inliningTarget, BytecodeNode bytecode, int bci, + IsBuiltinObjectProfile stopIterationProfile, + StopIterationBuiltins.StopIterationValueNode getValue, + LocalSetter returnedValue) { + e.expectStopIteration(inliningTarget, stopIterationProfile); + returnedValue.setObject(bytecode, bci, frame, getValue.execute((PBaseException) e.getUnreifiedException())); + } + + } + + @Operation + @ConstantOperand(type = LocalSetter.class) + @ConstantOperand(type = LocalSetter.class) + @SuppressWarnings("truffle-interpreted-performance") + public static final class YieldFromThrow { + + private static final TruffleString T_CLOSE = tsLiteral("close"); + private static final TruffleString T_THROW = tsLiteral("throw"); + + @Specialization + static boolean doGenerator(VirtualFrame frame, + LocalSetter yieldedValue, + LocalSetter returnedValue, + PGenerator generator, + PException exception, + @Bind Node inliningTarget, + @Bind BytecodeNode bytecode, + @Bind("$bytecodeIndex") int bci, + @Cached CommonGeneratorBuiltins.ThrowNode throwNode, + @Cached CommonGeneratorBuiltins.CloseNode closeNode, + @Shared @Cached IsBuiltinObjectProfile profileExit, + @Shared @Cached IsBuiltinObjectProfile stopIterationProfile, + @Shared @Cached StopIterationBuiltins.StopIterationValueNode getValue) { + if (profileExit.profileException(inliningTarget, exception, GeneratorExit)) { + closeNode.execute(frame, generator); + throw exception; + } else { + try { + Object value = throwNode.execute(frame, generator, exception.getEscapedException(), PNone.NO_VALUE, PNone.NO_VALUE); + yieldedValue.setObject(bytecode, bci, frame, value); + return false; + } catch (PException e) { + handleException(frame, e, inliningTarget, bytecode, bci, stopIterationProfile, getValue, returnedValue); + return true; + } + } + } + + @Fallback + static boolean doOther(VirtualFrame frame, + LocalSetter yieldedValue, + LocalSetter returnedValue, + Object obj, + Object exception, + @Bind Node inliningTarget, + @Bind BytecodeNode bytecode, + @Bind("$bytecodeIndex") int bci, + @Cached PyObjectLookupAttr lookupThrow, + @Cached PyObjectLookupAttr lookupClose, + @Cached CallNode callThrow, + @Cached CallNode callClose, + @Cached WriteUnraisableNode writeUnraisableNode, + @Shared @Cached IsBuiltinObjectProfile profileExit, + @Shared @Cached IsBuiltinObjectProfile stopIterationProfile, + @Shared @Cached StopIterationBuiltins.StopIterationValueNode getValue) { + PException pException = (PException) exception; + if (profileExit.profileException(inliningTarget, pException, GeneratorExit)) { + Object close = PNone.NO_VALUE; + try { + close = lookupClose.execute(frame, inliningTarget, obj, T_CLOSE); + } catch (PException e) { + writeUnraisableNode.execute(frame, e.getEscapedException(), null, obj); + } + if (close != PNone.NO_VALUE) { + callClose.execute(frame, close); + } + throw pException; + } else { + Object throwMethod = lookupThrow.execute(frame, inliningTarget, obj, T_THROW); + if (throwMethod == PNone.NO_VALUE) { + throw pException; + } + try { + Object value = callThrow.execute(frame, throwMethod, pException.getEscapedException()); + yieldedValue.setObject(bytecode, bci, frame, value); + return false; + } catch (PException e) { + handleException(frame, e, inliningTarget, bytecode, bci, stopIterationProfile, getValue, returnedValue); + return true; + } + } + } + + private static void handleException(VirtualFrame frame, PException e, Node inliningTarget, BytecodeNode bytecode, int bci, + IsBuiltinObjectProfile stopIterationProfile, StopIterationBuiltins.StopIterationValueNode getValue, + LocalSetter returnedValue) { + e.expectStopIteration(inliningTarget, stopIterationProfile); + returnedValue.setObject(bytecode, bci, frame, getValue.execute((PBaseException) e.getUnreifiedException())); + } + } + + @Operation + @ConstantOperand(type = int.class) + public static final class CheckUnboundLocal { + @Specialization + public static Object doObject(int index, Object localValue, + @Bind PBytecodeDSLRootNode rootNode, + @Bind Node inliningTarget, + @Cached PRaiseNode.Lazy raiseNode) { + if (localValue == null) { + CompilerDirectives.transferToInterpreterAndInvalidate(); + TruffleString localName = rootNode.getCodeUnit().varnames[index]; + throw raiseNode.get(inliningTarget).raise(PythonBuiltinClassType.UnboundLocalError, ErrorMessages.LOCAL_VAR_REFERENCED_BEFORE_ASSIGMENT, localName); + } + return localValue; + } + } + + @Operation + public static final class RaiseNotImplementedError { + @Specialization + public static void doRaise(VirtualFrame frame, TruffleString name, + @Cached PRaiseNode raiseNode) { + throw raiseNode.raise(PythonBuiltinClassType.NotImplementedError, name); + + } + } +} diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/call/InvokeNode.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/call/InvokeNode.java index 68fbf82cfe..5230314fb8 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/call/InvokeNode.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/call/InvokeNode.java @@ -32,6 +32,7 @@ import com.oracle.graal.python.builtins.objects.method.PBuiltinMethod; import com.oracle.graal.python.nodes.builtins.FunctionNodes.GetCallTargetNode; import com.oracle.graal.python.nodes.bytecode.PBytecodeGeneratorFunctionRootNode; +import com.oracle.graal.python.nodes.bytecode_dsl.PBytecodeDSLGeneratorFunctionRootNode; import com.oracle.graal.python.runtime.PythonOptions; import com.oracle.truffle.api.CallTarget; import com.oracle.truffle.api.CompilerAsserts; @@ -66,12 +67,16 @@ protected static RootCallTarget getCallTarget(Object callee) { protected static void optionallySetGeneratorFunction(Node inliningTarget, Object[] arguments, CallTarget callTarget, InlinedConditionProfile isGeneratorFunctionProfile, PFunction callee) { RootNode rootNode = ((RootCallTarget) callTarget).getRootNode(); - if (isGeneratorFunctionProfile.profile(inliningTarget, rootNode instanceof PBytecodeGeneratorFunctionRootNode)) { + if (isGeneratorFunctionProfile.profile(inliningTarget, isGeneratorFunction(rootNode))) { assert callee != null : "generator function callee not passed to invoke node"; PArguments.setGeneratorFunction(arguments, callee); } } + private static boolean isGeneratorFunction(RootNode rootNode) { + return rootNode instanceof PBytecodeGeneratorFunctionRootNode || rootNode instanceof PBytecodeDSLGeneratorFunctionRootNode; + } + protected static boolean isBuiltin(Object callee) { return callee instanceof PBuiltinFunction || callee instanceof PBuiltinMethod; } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/exception/ExceptMatchNode.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/exception/ExceptMatchNode.java index 589bae4724..e86619c1c8 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/exception/ExceptMatchNode.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/exception/ExceptMatchNode.java @@ -51,6 +51,7 @@ import com.oracle.graal.python.runtime.exception.PythonErrorType; import com.oracle.graal.python.runtime.sequence.storage.SequenceStorage; import com.oracle.truffle.api.CompilerDirectives; +import com.oracle.truffle.api.bytecode.OperationProxy; import com.oracle.truffle.api.dsl.Bind; import com.oracle.truffle.api.dsl.Cached; import com.oracle.truffle.api.dsl.Cached.Shared; @@ -68,6 +69,7 @@ @ImportStatic(PGuards.class) @GenerateUncached +@OperationProxy.Proxyable @SuppressWarnings("truffle-inlining") // footprint reduction 44 -> 25 public abstract class ExceptMatchNode extends Node { public abstract boolean executeMatch(Frame frame, Object exception, Object clause); diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/exception/TopLevelExceptionHandler.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/exception/TopLevelExceptionHandler.java index f1d2eafe66..8fe502aa1f 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/exception/TopLevelExceptionHandler.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/exception/TopLevelExceptionHandler.java @@ -166,7 +166,8 @@ public Object execute(VirtualFrame frame) { @TruffleBoundary private void checkInitialized() { Python3Core core = getContext(); - if (core.isCoreInitialized() && PythonLanguage.MIME_TYPE.equals(source.getMimeType())) { + if (core.isCoreInitialized() && + (PythonLanguage.MIME_TYPE.equals(source.getMimeType()))) { getContext().initializeMainModule(toTruffleStringUncached(source.getPath())); } } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/expression/BinaryArithmetic.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/expression/BinaryArithmetic.java index 460af66496..6e922b9226 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/expression/BinaryArithmetic.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/expression/BinaryArithmetic.java @@ -64,6 +64,7 @@ import com.oracle.graal.python.util.OverflowException; import com.oracle.graal.python.util.Supplier; import com.oracle.truffle.api.CompilerDirectives; +import com.oracle.truffle.api.bytecode.OperationProxy; import com.oracle.truffle.api.dsl.Bind; import com.oracle.truffle.api.dsl.Cached; import com.oracle.truffle.api.dsl.Cached.Shared; @@ -180,6 +181,7 @@ public static LookupAndCallBinaryNode createBinaryOp(SpecialMethodSlot slot, Sup } } + @OperationProxy.Proxyable public abstract static class SubNode extends BinaryArithmeticNode { public static final Supplier NOT_IMPLEMENTED = createHandler("-"); @@ -237,6 +239,7 @@ protected static void raiseDivisionByZero(boolean cond, Node inliningTarget, PRa } } + @OperationProxy.Proxyable public abstract static class TrueDivNode extends BinaryArithmeticRaiseNode { public static final Supplier NOT_IMPLEMENTED = createHandler("/"); @@ -278,6 +281,7 @@ public static Object doGeneric(VirtualFrame frame, Object left, Object right, } } + @OperationProxy.Proxyable public abstract static class FloorDivNode extends BinaryArithmeticRaiseNode { public static final Supplier NOT_IMPLEMENTED = createHandler("//"); @@ -333,6 +337,7 @@ public static Object doGeneric(VirtualFrame frame, Object left, Object right, } } + @OperationProxy.Proxyable public abstract static class ModNode extends BinaryArithmeticRaiseNode { public static final Supplier NOT_IMPLEMENTED = createHandler("%"); @@ -384,6 +389,7 @@ public static Object doGeneric(VirtualFrame frame, Object left, Object right, } } + @OperationProxy.Proxyable public abstract static class LShiftNode extends BinaryArithmeticNode { public static final Supplier NOT_IMPLEMENTED = createHandler("<<"); @@ -414,6 +420,7 @@ public static Object doGeneric(VirtualFrame frame, Object left, Object right, } } + @OperationProxy.Proxyable public abstract static class RShiftNode extends BinaryArithmeticNode { public static final Supplier NOT_IMPLEMENTED = createHandler(">>"); @@ -436,6 +443,7 @@ public static Object doGeneric(VirtualFrame frame, Object left, Object right, } } + @OperationProxy.Proxyable public abstract static class BitAndNode extends BinaryArithmeticNode { public static final Supplier NOT_IMPLEMENTED = createHandler("&"); @@ -458,6 +466,7 @@ public static Object doGeneric(VirtualFrame frame, Object left, Object right, } + @OperationProxy.Proxyable public abstract static class BitOrNode extends BinaryArithmeticNode { public static final Supplier NOT_IMPLEMENTED = createHandler("|"); @@ -482,6 +491,7 @@ public static Object doGeneric(VirtualFrame frame, Object left, Object right, } + @OperationProxy.Proxyable public abstract static class BitXorNode extends BinaryArithmeticNode { public static final Supplier NOT_IMPLEMENTED = createHandler("^"); @@ -504,6 +514,7 @@ public static Object doGeneric(VirtualFrame frame, Object left, Object right, } + @OperationProxy.Proxyable public abstract static class MatMulNode extends BinaryArithmeticNode { public static final Supplier NOT_IMPLEMENTED = createHandler("@"); @@ -516,6 +527,7 @@ public static Object doGeneric(VirtualFrame frame, Object left, Object right, } } + @OperationProxy.Proxyable public abstract static class PowNode extends BinaryArithmeticNode { public static final Supplier NOT_IMPLEMENTED = createHandler("** or pow()"); @@ -528,6 +540,7 @@ public static Object doGeneric(VirtualFrame frame, Object left, Object right, } } + @OperationProxy.Proxyable public abstract static class DivModNode extends BinaryArithmeticRaiseNode { public static final Supplier NOT_IMPLEMENTED = createHandler("divmod"); diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/expression/ContainsNode.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/expression/ContainsNode.java index 755cdd8a19..dd9b82553e 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/expression/ContainsNode.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/expression/ContainsNode.java @@ -48,6 +48,7 @@ import com.oracle.graal.python.nodes.call.special.LookupAndCallBinaryNode; import com.oracle.graal.python.nodes.object.BuiltinClassProfiles.IsBuiltinObjectProfile; import com.oracle.graal.python.runtime.exception.PException; +import com.oracle.truffle.api.bytecode.OperationProxy; import com.oracle.truffle.api.dsl.Bind; import com.oracle.truffle.api.dsl.Cached; import com.oracle.truffle.api.dsl.Cached.Shared; @@ -59,6 +60,7 @@ import com.oracle.truffle.api.nodes.UnexpectedResultException; @GenerateInline(false) +@OperationProxy.Proxyable public abstract class ContainsNode extends BinaryOpNode { @NeverDefault public static ContainsNode create() { diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/expression/LookupAndCallInplaceNode.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/expression/LookupAndCallInplaceNode.java index 5753161223..cddf9ee255 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/expression/LookupAndCallInplaceNode.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/expression/LookupAndCallInplaceNode.java @@ -75,6 +75,7 @@ public abstract static class NotImplementedHandler extends PNodeWithContext { this.arithmetic = arithmetic; } + @NeverDefault public static LookupAndCallInplaceNode create(InplaceArithmetic arithmetic) { return LookupAndCallInplaceNodeGen.create(arithmetic); } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/expression/UnaryArithmetic.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/expression/UnaryArithmetic.java index 6cd97b2904..fde35f9c34 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/expression/UnaryArithmetic.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/expression/UnaryArithmetic.java @@ -53,6 +53,7 @@ import com.oracle.graal.python.nodes.call.special.LookupAndCallUnaryNode.NoAttributeHandler; import com.oracle.graal.python.util.Supplier; import com.oracle.truffle.api.CompilerDirectives; +import com.oracle.truffle.api.bytecode.OperationProxy; import com.oracle.truffle.api.dsl.Cached; import com.oracle.truffle.api.dsl.GenerateCached; import com.oracle.truffle.api.dsl.GenerateInline; @@ -153,6 +154,7 @@ public static LookupAndCallUnaryNode createCallNode(TruffleString name, Supplier @GenerateInline(inlineByDefault = true) @GenerateCached + @OperationProxy.Proxyable public abstract static class PosNode extends UnaryArithmeticNode { public static final Supplier NOT_IMPLEMENTED = createHandler("+"); @@ -174,7 +176,7 @@ public static double pos(double arg) { @Specialization public static Object doGeneric(VirtualFrame frame, Object arg, - @Cached(value = "createCallNode(T___POS__, NOT_IMPLEMENTED)", inline = false) LookupAndCallUnaryNode callNode) { + @Cached(value = "createCallNode(T___POS__, NOT_IMPLEMENTED)") LookupAndCallUnaryNode callNode) { return callNode.executeObject(frame, arg); } @@ -186,6 +188,7 @@ public static PosNode create() { @GenerateInline(inlineByDefault = true) @GenerateCached + @OperationProxy.Proxyable public abstract static class NegNode extends UnaryArithmeticNode { public static final Supplier NOT_IMPLEMENTED = createHandler("-"); @@ -212,7 +215,7 @@ public static double neg(double arg) { @Specialization public static Object doGeneric(VirtualFrame frame, Object arg, - @Cached(value = "createCallNode(T___NEG__, NOT_IMPLEMENTED)", inline = false) LookupAndCallUnaryNode callNode) { + @Cached(value = "createCallNode(T___NEG__, NOT_IMPLEMENTED)") LookupAndCallUnaryNode callNode) { return callNode.executeObject(frame, arg); } @@ -224,6 +227,7 @@ public static NegNode create() { @GenerateInline(inlineByDefault = true) @GenerateCached + @OperationProxy.Proxyable public abstract static class InvertNode extends UnaryArithmeticNode { public static final Supplier NOT_IMPLEMENTED = createHandler("~"); @@ -245,7 +249,7 @@ public static long invert(long arg) { @Specialization public static Object doGeneric(VirtualFrame frame, Object arg, - @Cached(value = "createCallNode(T___INVERT__, NOT_IMPLEMENTED)", inline = false) LookupAndCallUnaryNode callNode) { + @Cached(value = "createCallNode(T___INVERT__, NOT_IMPLEMENTED)") LookupAndCallUnaryNode callNode) { return callNode.executeObject(frame, arg); } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/frame/DeleteGlobalNode.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/frame/DeleteGlobalNode.java index e24332f33f..e8dea534c8 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/frame/DeleteGlobalNode.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/frame/DeleteGlobalNode.java @@ -50,6 +50,7 @@ import com.oracle.truffle.api.dsl.Cached; import com.oracle.truffle.api.dsl.Cached.Shared; import com.oracle.truffle.api.dsl.GenerateUncached; +import com.oracle.truffle.api.dsl.NeverDefault; import com.oracle.truffle.api.dsl.Specialization; import com.oracle.truffle.api.frame.VirtualFrame; import com.oracle.truffle.api.nodes.Node; @@ -58,12 +59,13 @@ @GenerateUncached @SuppressWarnings("truffle-inlining") // footprint reduction 52 -> 36 public abstract class DeleteGlobalNode extends PNodeWithContext { + @NeverDefault public static DeleteGlobalNode create() { return DeleteGlobalNodeGen.create(); } public final void executeWithGlobals(VirtualFrame frame, Object globals, TruffleString attributeId) { - CompilerAsserts.partialEvaluationConstant(attributeId); + CompilerAsserts.compilationConstant(attributeId); executeWithGlobalsImpl(frame, globals, attributeId); } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/frame/GetFrameLocalsNode.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/frame/GetFrameLocalsNode.java index fc9a5628bd..225076c8c9 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/frame/GetFrameLocalsNode.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/frame/GetFrameLocalsNode.java @@ -47,8 +47,15 @@ import com.oracle.graal.python.lib.PyDictDelItem; import com.oracle.graal.python.lib.PyDictGetItem; import com.oracle.graal.python.lib.PyDictSetItem; +import com.oracle.graal.python.nodes.PRootNode; +import com.oracle.graal.python.nodes.bytecode.BytecodeFrameInfo; import com.oracle.graal.python.nodes.bytecode.FrameInfo; +import com.oracle.graal.python.nodes.bytecode_dsl.BytecodeDSLFrameInfo; +import com.oracle.graal.python.nodes.bytecode_dsl.PBytecodeDSLRootNode; +import com.oracle.graal.python.runtime.PythonOptions; import com.oracle.graal.python.runtime.object.PythonObjectFactory; +import com.oracle.truffle.api.CompilerDirectives; +import com.oracle.truffle.api.bytecode.BytecodeNode; import com.oracle.truffle.api.dsl.Bind; import com.oracle.truffle.api.dsl.Cached; import com.oracle.truffle.api.dsl.Cached.Shared; @@ -120,10 +127,19 @@ void doCachedFd(MaterializedFrame locals, PDict dict, @Bind("info.getVariableCount()") int count, @Shared("setItem") @Cached PyDictSetItem setItem, @Shared("delItem") @Cached PyDictDelItem delItem) { - CodeUnit co = info.getRootNode().getCodeUnit(); - int regularVarCount = co.varnames.length; - for (int i = 0; i < count; i++) { - copyItem(inliningTarget, locals, info, dict, setItem, delItem, i, i >= regularVarCount); + int regularVarCount = info.getRegularVariableCount(); + + if (PythonOptions.ENABLE_BYTECODE_DSL_INTERPRETER) { + BytecodeDSLFrameInfo bytecodeDSLFrameInfo = (BytecodeDSLFrameInfo) info; + PBytecodeDSLRootNode rootNode = bytecodeDSLFrameInfo.getRootNode(); + Object[] localsArray = rootNode.getBytecodeNode().getLocalValues(0, locals); + for (int i = 0; i < count; i++) { + copyItem(inliningTarget, localsArray[i], info, dict, setItem, delItem, i, i >= regularVarCount); + } + } else { + for (int i = 0; i < count; i++) { + copyItem(inliningTarget, locals.getValue(i), info, dict, setItem, delItem, i, i >= regularVarCount); + } } } @@ -134,16 +150,25 @@ void doGeneric(MaterializedFrame locals, PDict dict, @Shared("delItem") @Cached PyDictDelItem delItem) { FrameInfo info = getInfo(locals.getFrameDescriptor()); int count = info.getVariableCount(); - CodeUnit co = info.getRootNode().getCodeUnit(); - int regularVarCount = co.varnames.length; - for (int i = 0; i < count; i++) { - copyItem(inliningTarget, locals, info, dict, setItem, delItem, i, i >= regularVarCount); + int regularVarCount = info.getRegularVariableCount(); + + if (PythonOptions.ENABLE_BYTECODE_DSL_INTERPRETER) { + BytecodeDSLFrameInfo bytecodeDSLFrameInfo = (BytecodeDSLFrameInfo) info; + PBytecodeDSLRootNode rootNode = bytecodeDSLFrameInfo.getRootNode(); + Object[] localsArray = rootNode.getBytecodeNode().getLocalValues(0, locals); + for (int i = 0; i < count; i++) { + copyItem(inliningTarget, localsArray[i], info, dict, setItem, delItem, i, i >= regularVarCount); + } + } else { + for (int i = 0; i < count; i++) { + copyItem(inliningTarget, locals.getValue(i), info, dict, setItem, delItem, i, i >= regularVarCount); + } } } - private static void copyItem(Node inliningTarget, MaterializedFrame locals, FrameInfo info, PDict dict, PyDictSetItem setItem, PyDictDelItem delItem, int i, boolean deref) { + private static void copyItem(Node inliningTarget, Object localValue, FrameInfo info, PDict dict, PyDictSetItem setItem, PyDictDelItem delItem, int i, boolean deref) { TruffleString name = info.getVariableName(i); - Object value = locals.getValue(i); + Object value = localValue; if (deref && value != null) { value = ((PCell) value).getRef(); } @@ -163,24 +188,39 @@ protected static FrameInfo getInfo(FrameDescriptor fd) { /** * Equivalent of CPython's {@code PyFrame_LocalsToFast} */ - public static void syncLocalsBackToFrame(CodeUnit co, PFrame pyFrame, Frame localFrame) { + public static void syncLocalsBackToFrame(CodeUnit co, PRootNode root, PFrame pyFrame, Frame localFrame) { if (!pyFrame.hasCustomLocals()) { PDict localsDict = (PDict) pyFrame.getLocalsDict(); - copyLocalsArray(localFrame, localsDict, co.varnames, 0, false); - copyLocalsArray(localFrame, localsDict, co.cellvars, co.varnames.length, true); - copyLocalsArray(localFrame, localsDict, co.freevars, co.varnames.length + co.cellvars.length, true); + copyLocalsArray(localFrame, root, localsDict, co.varnames, 0, false); + copyLocalsArray(localFrame, root, localsDict, co.cellvars, co.varnames.length, true); + copyLocalsArray(localFrame, root, localsDict, co.freevars, co.varnames.length + co.cellvars.length, true); } } - private static void copyLocalsArray(Frame localFrame, PDict localsDict, TruffleString[] namesArray, int offset, boolean deref) { - for (int i = 0; i < namesArray.length; i++) { - TruffleString varname = namesArray[i]; - Object value = PyDictGetItem.executeUncached(localsDict, varname); - if (deref) { - PCell cell = (PCell) localFrame.getObject(offset + i); - cell.setRef(value); - } else { - localFrame.setObject(offset + i, value); + private static void copyLocalsArray(Frame localFrame, PRootNode root, PDict localsDict, TruffleString[] namesArray, int offset, boolean deref) { + if (PythonOptions.ENABLE_BYTECODE_DSL_INTERPRETER) { + PBytecodeDSLRootNode bytecodeDSLRootNode = (PBytecodeDSLRootNode) root; + BytecodeNode bytecodeNode = bytecodeDSLRootNode.getBytecodeNode(); + for (int i = 0; i < namesArray.length; i++) { + TruffleString varname = namesArray[i]; + Object value = PyDictGetItem.executeUncached(localsDict, varname); + if (deref) { + PCell cell = (PCell) bytecodeNode.getLocalValue(0, localFrame, offset + i); + cell.setRef(value); + } else { + bytecodeNode.setLocalValue(0, localFrame, offset + i, value); + } + } + } else { + for (int i = 0; i < namesArray.length; i++) { + TruffleString varname = namesArray[i]; + Object value = PyDictGetItem.executeUncached(localsDict, varname); + if (deref) { + PCell cell = (PCell) localFrame.getObject(offset + i); + cell.setRef(value); + } else { + localFrame.setObject(offset + i, value); + } } } } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/frame/MaterializeFrameNode.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/frame/MaterializeFrameNode.java index 113f6234cc..914607b716 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/frame/MaterializeFrameNode.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/frame/MaterializeFrameNode.java @@ -42,10 +42,15 @@ import com.oracle.graal.python.builtins.objects.frame.PFrame; import com.oracle.graal.python.builtins.objects.function.PArguments; +import com.oracle.graal.python.nodes.bytecode.BytecodeFrameInfo; import com.oracle.graal.python.nodes.bytecode.FrameInfo; +import com.oracle.graal.python.nodes.bytecode_dsl.BytecodeDSLFrameInfo; +import com.oracle.graal.python.nodes.bytecode_dsl.PBytecodeDSLRootNode; +import com.oracle.graal.python.runtime.PythonOptions; import com.oracle.graal.python.runtime.object.PythonObjectFactory; import com.oracle.graal.python.util.PythonUtils; import com.oracle.truffle.api.Truffle; +import com.oracle.truffle.api.bytecode.BytecodeNode; import com.oracle.truffle.api.dsl.Bind; import com.oracle.truffle.api.dsl.Cached; import com.oracle.truffle.api.dsl.Cached.Shared; @@ -111,7 +116,7 @@ static PFrame freshPFrameCachedFD(Node location, boolean markAsEscaped, boolean @Shared("syncValuesNode") @Cached SyncFrameValuesNode syncValuesNode) { MaterializedFrame locals = createLocalsFrame(cachedFD); PFrame escapedFrame = factory.createPFrame(PArguments.getCurrentFrameInfo(frameToMaterialize), location, locals); - return doEscapeFrame(frameToMaterialize, escapedFrame, markAsEscaped, forceSync, syncValuesNode); + return doEscapeFrame(frameToMaterialize, escapedFrame, markAsEscaped, forceSync, location, syncValuesNode); } @Specialization(guards = {"getPFrame(frameToMaterialize) == null", "!hasGeneratorFrame(frameToMaterialize)", "!hasCustomLocals(frameToMaterialize)"}, replaces = "freshPFrameCachedFD") @@ -120,7 +125,7 @@ static PFrame freshPFrame(Node location, boolean markAsEscaped, boolean forceSyn @Shared("syncValuesNode") @Cached SyncFrameValuesNode syncValuesNode) { MaterializedFrame locals = createLocalsFrame(frameToMaterialize.getFrameDescriptor()); PFrame escapedFrame = factory.createPFrame(PArguments.getCurrentFrameInfo(frameToMaterialize), location, locals); - return doEscapeFrame(frameToMaterialize, escapedFrame, markAsEscaped, forceSync, syncValuesNode); + return doEscapeFrame(frameToMaterialize, escapedFrame, markAsEscaped, forceSync, location, syncValuesNode); } @Specialization(guards = {"getPFrame(frameToMaterialize) == null", "!hasGeneratorFrame(frameToMaterialize)", "hasCustomLocals(frameToMaterialize)"}) @@ -129,7 +134,7 @@ static PFrame freshPFrameCusstomLocals(Node location, boolean markAsEscaped, @Su @Shared("factory") @Cached PythonObjectFactory factory) { PFrame escapedFrame = factory.createPFrame(PArguments.getCurrentFrameInfo(frameToMaterialize), location, null); escapedFrame.setLocalsDict(PArguments.getSpecialArgument(frameToMaterialize)); - return doEscapeFrame(frameToMaterialize, escapedFrame, markAsEscaped, false, null); + return doEscapeFrame(frameToMaterialize, escapedFrame, markAsEscaped, false, location, null); } @Specialization(guards = {"getPFrame(frameToMaterialize) == null", "hasGeneratorFrame(frameToMaterialize)"}) @@ -139,7 +144,7 @@ static PFrame freshPFrameForGenerator(Node location, @SuppressWarnings("unused") PFrame.Reference frameRef = PArguments.getCurrentFrameInfo(frameToMaterialize); PFrame escapedFrame = materializeGeneratorFrame(location, generatorFrame, frameRef, factory); frameRef.setPyFrame(escapedFrame); - return doEscapeFrame(frameToMaterialize, escapedFrame, markAsEscaped, false, null); + return doEscapeFrame(frameToMaterialize, escapedFrame, markAsEscaped, false, location, null); } @Specialization(guards = "getPFrame(frameToMaterialize) != null") @@ -154,7 +159,7 @@ static PFrame alreadyEscapedFrame(@SuppressWarnings("unused") Node location, boo if (markAsEscaped) { pyFrame.getRef().markAsEscaped(); } - processBytecodeFrame(frameToMaterialize, pyFrame); + processBytecodeFrame(frameToMaterialize, pyFrame, location); return pyFrame; } @@ -168,16 +173,34 @@ public static PFrame materializeGeneratorFrame(Node location, MaterializedFrame return escapedFrame; } - private static void processBytecodeFrame(Frame frameToMaterialize, PFrame pyFrame) { - FrameInfo info = (FrameInfo) frameToMaterialize.getFrameDescriptor().getInfo(); - if (info != null) { - pyFrame.setBci(info.getBci(frameToMaterialize)); - pyFrame.setLocation(info.getRootNode()); + private static void processBytecodeFrame(Frame frameToMaterialize, PFrame pyFrame, Node location) { + Object info = frameToMaterialize.getFrameDescriptor().getInfo(); + if (info == null) { + return; + } + if (PythonOptions.ENABLE_BYTECODE_DSL_INTERPRETER) { + BytecodeNode bytecodeNode = BytecodeNode.get(location); + if (bytecodeNode == null) { + /** + * Sometimes we don't have a precise location (see + * {@link ReadCallerFrameNode#getFrame}). Set bci to -1 to mark the location as + * unknown. + */ + pyFrame.setBci(-1); + pyFrame.setLocation(location); + } else { + pyFrame.setBci(bytecodeNode.getBytecodeIndex(frameToMaterialize)); + pyFrame.setLocation(bytecodeNode); + } + } else { + BytecodeFrameInfo bytecodeFrameInfo = (BytecodeFrameInfo) info; + pyFrame.setBci(bytecodeFrameInfo.getBci(frameToMaterialize)); + pyFrame.setLocation(bytecodeFrameInfo.getRootNode()); } } private static PFrame doEscapeFrame(Frame frameToMaterialize, PFrame escapedFrame, boolean markAsEscaped, boolean forceSync, - SyncFrameValuesNode syncValuesNode) { + Node location, SyncFrameValuesNode syncValuesNode) { PFrame.Reference topFrameRef = PArguments.getCurrentFrameInfo(frameToMaterialize); topFrameRef.setPyFrame(escapedFrame); @@ -189,12 +212,13 @@ private static PFrame doEscapeFrame(Frame frameToMaterialize, PFrame escapedFram if (markAsEscaped) { topFrameRef.markAsEscaped(); } - processBytecodeFrame(frameToMaterialize, escapedFrame); + processBytecodeFrame(frameToMaterialize, escapedFrame, location); return escapedFrame; } protected static boolean hasGeneratorFrame(Frame frame) { - return PArguments.getGeneratorFrame(frame) != null; + return !PythonOptions.ENABLE_BYTECODE_DSL_INTERPRETER && + PArguments.getGeneratorFrame(frame) != null; } protected static boolean hasCustomLocals(Frame frame) { @@ -216,7 +240,8 @@ public abstract static class SyncFrameValuesNode extends Node { public abstract void execute(PFrame pyFrame, Frame frameToSync); - @Specialization(guards = {"!pyFrame.hasCustomLocals()", "frameToSync.getFrameDescriptor() == cachedFd", + @Specialization(guards = {"!pyFrame.hasCustomLocals()", + "frameToSync.getFrameDescriptor() == cachedFd", "variableSlotCount(cachedFd) < 32"}, limit = "1") @ExplodeLoop static void doSyncExploded(PFrame pyFrame, Frame frameToSync, @@ -224,17 +249,37 @@ static void doSyncExploded(PFrame pyFrame, Frame frameToSync, MaterializedFrame target = pyFrame.getLocals(); assert cachedFd == target.getFrameDescriptor(); int slotCount = variableSlotCount(cachedFd); - for (int slot = 0; slot < slotCount; slot++) { - PythonUtils.copyFrameSlot(frameToSync, target, slot); + + if (PythonOptions.ENABLE_BYTECODE_DSL_INTERPRETER) { + FrameInfo info = (FrameInfo) cachedFd.getInfo(); + if (info instanceof BytecodeDSLFrameInfo bytecodeDSLFrameInfo) { + PBytecodeDSLRootNode rootNode = bytecodeDSLFrameInfo.getRootNode(); + rootNode.getBytecodeNode().copyLocalValues(0, frameToSync, target, 0, slotCount); + } + } else { + for (int i = 0; i < slotCount; i++) { + PythonUtils.copyFrameSlot(frameToSync, target, i); + } } } @Specialization(guards = "!pyFrame.hasCustomLocals()", replaces = "doSyncExploded") - static void doSyncLoop(PFrame pyFrame, Frame frameToSync) { + @ExplodeLoop + static void doSync(PFrame pyFrame, Frame frameToSync) { MaterializedFrame target = pyFrame.getLocals(); - int slotCount = variableSlotCount(frameToSync.getFrameDescriptor()); - for (int slot = 0; slot < slotCount; slot++) { - PythonUtils.copyFrameSlot(frameToSync, target, slot); + FrameDescriptor fd = target.getFrameDescriptor(); + int slotCount = variableSlotCount(fd); + + if (PythonOptions.ENABLE_BYTECODE_DSL_INTERPRETER) { + FrameInfo info = (FrameInfo) fd.getInfo(); + if (info instanceof BytecodeDSLFrameInfo bytecodeDSLFrameInfo) { + PBytecodeDSLRootNode rootNode = bytecodeDSLFrameInfo.getRootNode(); + rootNode.getBytecodeNode().copyLocalValues(0, frameToSync, target, 0, slotCount); + } + } else { + for (int i = 0; i < slotCount; i++) { + PythonUtils.copyFrameSlot(frameToSync, target, i); + } } } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/frame/ReadCallerFrameNode.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/frame/ReadCallerFrameNode.java index cb377ffbd2..a70e54a0e2 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/frame/ReadCallerFrameNode.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/frame/ReadCallerFrameNode.java @@ -53,6 +53,7 @@ import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; import com.oracle.truffle.api.RootCallTarget; import com.oracle.truffle.api.Truffle; +import com.oracle.truffle.api.bytecode.ContinuationRootNode; import com.oracle.truffle.api.dsl.NeverDefault; import com.oracle.truffle.api.frame.Frame; import com.oracle.truffle.api.frame.FrameInstance; @@ -308,14 +309,13 @@ private static Frame getFrame(Node requestingNode, PFrame.Reference startFrame, * */ public Frame visitFrame(FrameInstance frameInstance) { - RootCallTarget target = (RootCallTarget) frameInstance.getCallTarget(); - RootNode rootNode = target.getRootNode(); + RootNode rootNode = getRootNode(frameInstance); Node callNode = frameInstance.getCallNode(); boolean didMark = IndirectCallData.setEncapsulatingNeedsToPassCallerFrame(callNode != null ? callNode : requestingNode); if (outputFrame[0] == null && rootNode instanceof PRootNode pRootNode && pRootNode.setsUpCalleeContext()) { pRootNode.setNeedsCallerFrame(); if (i < 0 && startFrame != null) { - Frame roFrame = frameInstance.getFrame(FrameInstance.FrameAccess.READ_ONLY); + Frame roFrame = getFrame(frameInstance, FrameInstance.FrameAccess.READ_ONLY); if (PArguments.getCurrentFrameInfo(roFrame) == startFrame) { i = 0; } @@ -324,7 +324,7 @@ public Frame visitFrame(FrameInstance frameInstance) { // a Python frame in CPython. if (!selector.skip(pRootNode)) { if (i == level || startFrame == null) { - Frame frame = frameInstance.getFrame(frameAccess); + Frame frame = getFrame(frameInstance, frameAccess); assert PArguments.isPythonFrame(frame); PFrame.Reference info = PArguments.getCurrentFrameInfo(frame); // avoid overriding the location if we don't know it @@ -355,6 +355,25 @@ public Frame visitFrame(FrameInstance frameInstance) { return outputFrame[0]; } + private static RootNode getRootNode(FrameInstance frameInstance) { + RootCallTarget target = (RootCallTarget) frameInstance.getCallTarget(); + RootNode rootNode = target.getRootNode(); + if (rootNode instanceof ContinuationRootNode continuationRoot) { + return (RootNode) continuationRoot.getSourceRootNode(); + } + return rootNode; + } + + private static Frame getFrame(FrameInstance frameInstance, FrameInstance.FrameAccess frameAccess) { + Frame frame = frameInstance.getFrame(frameAccess); + + RootCallTarget target = (RootCallTarget) frameInstance.getCallTarget(); + if (target.getRootNode() instanceof ContinuationRootNode) { + return (Frame) frame.getArguments()[0]; + } + return frame; + } + private MaterializeFrameNode ensureMaterializeNode() { if (materializeNode == null) { CompilerDirectives.transferToInterpreterAndInvalidate(); diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/frame/ReadGlobalOrBuiltinNode.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/frame/ReadGlobalOrBuiltinNode.java index 465481c0aa..c6a97f05fa 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/frame/ReadGlobalOrBuiltinNode.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/frame/ReadGlobalOrBuiltinNode.java @@ -65,14 +65,14 @@ @SuppressWarnings("truffle-inlining") // footprint reduction 48 -> 30 public abstract class ReadGlobalOrBuiltinNode extends PNodeWithContext { public final Object execute(VirtualFrame frame, TruffleString name) { - CompilerAsserts.partialEvaluationConstant(name); + CompilerAsserts.compilationConstant(name); return executeWithGlobals(frame, PArguments.getGlobals(frame), name); } protected abstract Object executeWithGlobals(VirtualFrame frame, Object globals, TruffleString name); public Object read(Frame frame, Object globals, TruffleString name) { - CompilerAsserts.partialEvaluationConstant(name); + CompilerAsserts.compilationConstant(name); return executeWithGlobals((VirtualFrame) frame, globals, name); } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/frame/ReadNameNode.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/frame/ReadNameNode.java index 2914a0f998..7bdf89d9ba 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/frame/ReadNameNode.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/frame/ReadNameNode.java @@ -53,6 +53,7 @@ import com.oracle.truffle.api.dsl.Cached; import com.oracle.truffle.api.dsl.Cached.Exclusive; import com.oracle.truffle.api.dsl.GenerateUncached; +import com.oracle.truffle.api.dsl.NeverDefault; import com.oracle.truffle.api.dsl.Specialization; import com.oracle.truffle.api.frame.VirtualFrame; import com.oracle.truffle.api.nodes.Node; @@ -62,12 +63,13 @@ @SuppressWarnings("truffle-inlining") // footprint reduction 36 -> 17 public abstract class ReadNameNode extends PNodeWithContext implements AccessNameNode { public final Object execute(VirtualFrame frame, TruffleString attributeId) { - CompilerAsserts.partialEvaluationConstant(attributeId); + CompilerAsserts.compilationConstant(attributeId); return executeImpl(frame, attributeId); } public abstract Object executeImpl(VirtualFrame frame, TruffleString attributeId); + @NeverDefault public static ReadNameNode create() { return ReadNameNodeGen.create(); } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/frame/WriteGlobalNode.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/frame/WriteGlobalNode.java index ade2eadf09..e4027b3705 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/frame/WriteGlobalNode.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/frame/WriteGlobalNode.java @@ -72,12 +72,12 @@ public static WriteGlobalNode create() { } public final void executeObject(VirtualFrame frame, TruffleString name, Object value) { - CompilerAsserts.partialEvaluationConstant(name); + CompilerAsserts.compilationConstant(name); executeObjectWithGlobals(frame, PArguments.getGlobals(frame), name, value); } public final void write(Frame frame, Object globals, TruffleString name, Object value) { - CompilerAsserts.partialEvaluationConstant(name); + CompilerAsserts.compilationConstant(name); executeObjectWithGlobals((VirtualFrame) frame, globals, name, value); } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/frame/WriteNameNode.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/frame/WriteNameNode.java index cadc06ea8e..eb45e7a047 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/frame/WriteNameNode.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/frame/WriteNameNode.java @@ -59,7 +59,7 @@ public abstract class WriteNameNode extends PNodeWithContext implements AccessNameNode { public final void execute(VirtualFrame frame, TruffleString attributeId, Object value) { - CompilerAsserts.partialEvaluationConstant(attributeId); + CompilerAsserts.compilationConstant(attributeId); executeImpl(frame, attributeId, value); } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/object/IsNode.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/object/IsNode.java index fb5dd6d72e..06b1557de1 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/object/IsNode.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/object/IsNode.java @@ -55,6 +55,8 @@ import com.oracle.graal.python.builtins.objects.type.PythonBuiltinClass; import com.oracle.graal.python.nodes.bytecode.PBytecodeGeneratorFunctionRootNode; import com.oracle.graal.python.nodes.bytecode.PBytecodeRootNode; +import com.oracle.graal.python.nodes.bytecode_dsl.PBytecodeDSLGeneratorFunctionRootNode; +import com.oracle.graal.python.nodes.bytecode_dsl.PBytecodeDSLRootNode; import com.oracle.graal.python.nodes.expression.BinaryOp; import com.oracle.graal.python.nodes.object.BuiltinClassProfiles.IsAnyBuiltinObjectProfile; import com.oracle.graal.python.runtime.PythonContext; @@ -62,6 +64,7 @@ import com.oracle.graal.python.util.ComparisonOp; import com.oracle.graal.python.util.OverflowException; import com.oracle.truffle.api.RootCallTarget; +import com.oracle.truffle.api.bytecode.OperationProxy; import com.oracle.truffle.api.dsl.Bind; import com.oracle.truffle.api.dsl.Cached; import com.oracle.truffle.api.dsl.Cached.Shared; @@ -79,6 +82,7 @@ @ImportStatic(PythonOptions.class) @GenerateUncached +@OperationProxy.Proxyable @SuppressWarnings("truffle-inlining") // footprint reduction 44 -> 26 public abstract class IsNode extends Node implements BinaryOp { @@ -229,14 +233,28 @@ public static boolean doCode(PCode left, PCode right, if (leftCt != null && rightCt != null) { RootNode leftRootNode = leftCt.getRootNode(); RootNode rightRootNode = rightCt.getRootNode(); - if (leftRootNode instanceof PBytecodeGeneratorFunctionRootNode) { - leftRootNode = ((PBytecodeGeneratorFunctionRootNode) leftRootNode).getBytecodeRootNode(); - } - if (rightRootNode instanceof PBytecodeGeneratorFunctionRootNode) { - rightRootNode = ((PBytecodeGeneratorFunctionRootNode) rightRootNode).getBytecodeRootNode(); - } - if (leftRootNode instanceof PBytecodeRootNode && rightRootNode instanceof PBytecodeRootNode) { - return ((PBytecodeRootNode) leftRootNode).getCodeUnit() == ((PBytecodeRootNode) rightRootNode).getCodeUnit(); + if (PythonOptions.ENABLE_BYTECODE_DSL_INTERPRETER) { + if (leftRootNode instanceof PBytecodeDSLGeneratorFunctionRootNode l) { + leftRootNode = l.getBytecodeRootNode(); + } + if (rightRootNode instanceof PBytecodeDSLGeneratorFunctionRootNode r) { + rightRootNode = r.getBytecodeRootNode(); + } + + if (leftRootNode instanceof PBytecodeDSLRootNode l && rightRootNode instanceof PBytecodeDSLRootNode r) { + return l.getCodeUnit() == r.getCodeUnit(); + } + } else { + if (leftRootNode instanceof PBytecodeGeneratorFunctionRootNode l) { + leftRootNode = l.getBytecodeRootNode(); + } + if (rightRootNode instanceof PBytecodeGeneratorFunctionRootNode r) { + rightRootNode = r.getBytecodeRootNode(); + } + + if (leftRootNode instanceof PBytecodeRootNode l && rightRootNode instanceof PBytecodeRootNode r) { + return l.getCodeUnit() == r.getCodeUnit(); + } } return leftRootNode == rightRootNode; } else { diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/ExecutionContext.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/ExecutionContext.java index 2dee13fd81..c025f592b7 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/ExecutionContext.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/ExecutionContext.java @@ -59,13 +59,16 @@ import com.oracle.truffle.api.CompilerDirectives.CompilationFinal; import com.oracle.truffle.api.CompilerDirectives.ValueType; import com.oracle.truffle.api.HostCompilerDirectives.InliningCutoff; +import com.oracle.truffle.api.bytecode.ContinuationRootNode; import com.oracle.truffle.api.RootCallTarget; import com.oracle.truffle.api.dsl.NeverDefault; import com.oracle.truffle.api.exception.AbstractTruffleException; import com.oracle.truffle.api.frame.Frame; import com.oracle.truffle.api.frame.FrameInstance; +import com.oracle.truffle.api.frame.MaterializedFrame; import com.oracle.truffle.api.frame.VirtualFrame; import com.oracle.truffle.api.nodes.Node; +import com.oracle.truffle.api.nodes.RootNode; import com.oracle.truffle.api.profiles.ConditionProfile; /** @@ -95,17 +98,33 @@ private CallContext(boolean adoptable) { * Prepare an indirect call from a Python frame to a Python function. */ public void prepareIndirectCall(VirtualFrame frame, Object[] callArguments, Node callNode) { - prepareCall(frame, callArguments, callNode, true, true); + prepareCall(frame, getActualCallArguments(callArguments), callNode, true, true); } /** * Prepare a call from a Python frame to a Python function. */ public void prepareCall(VirtualFrame frame, Object[] callArguments, RootCallTarget callTarget, Node callNode) { - // n.b.: The class cast should always be correct, since this context - // must only be used when calling from Python to Python - PRootNode calleeRootNode = (PRootNode) callTarget.getRootNode(); - prepareCall(frame, callArguments, callNode, calleeRootNode.needsCallerFrame(), calleeRootNode.needsExceptionState()); + RootNode rootNode = callTarget.getRootNode(); + + PRootNode calleeRootNode; + Object[] actualCallArguments; + boolean needsExceptionState; + if (rootNode instanceof ContinuationRootNode continuationRoot) { + calleeRootNode = (PRootNode) continuationRoot.getSourceRootNode(); + assert callArguments.length == 2; + actualCallArguments = ((MaterializedFrame) callArguments[0]).getArguments(); + // Local exception state takes precedence over any exception in the caller's context + needsExceptionState = calleeRootNode.needsExceptionState() && !PArguments.hasException(actualCallArguments); + } else { + // n.b.: The class cast should always be correct, since this context + // must only be used when calling from Python to Python + calleeRootNode = (PRootNode) rootNode; + actualCallArguments = callArguments; + needsExceptionState = calleeRootNode.needsExceptionState(); + } + prepareCall(frame, actualCallArguments, callNode, calleeRootNode.needsCallerFrame(), needsExceptionState); + } private void prepareCall(VirtualFrame frame, Object[] callArguments, Node callNode, boolean needsCallerFrame, boolean needsExceptionState) { @@ -162,6 +181,19 @@ private void prepareCall(VirtualFrame frame, Object[] callArguments, Node callNo } } + private static Object[] getActualCallArguments(Object[] callArguments) { + /** + * Bytecode DSL note: When resuming a generator/coroutine, the call target is a + * ContinuationRoot with a different calling convention from regular PRootNodes. The + * first argument is a materialized frame containing the arguments used for argument + * reads. + */ + if (callArguments.length == 2 && callArguments[0] instanceof MaterializedFrame materialized) { + return materialized.getArguments(); + } + return callArguments; + } + private PFrame materialize(VirtualFrame frame, Node callNode, boolean markAsEscaped, boolean forceSync) { if (adoptable) { return ensureMaterializeNode().execute(frame, callNode, markAsEscaped, forceSync); @@ -226,6 +258,10 @@ public void enter(VirtualFrame frame) { } public void exit(VirtualFrame frame, PRootNode node) { + exit(frame, node, node); + } + + public void exit(VirtualFrame frame, PRootNode node, Node location) { /* * equivalent to PyPy's ExecutionContext.leave. Note that got_exception in * their code is handled automatically by the Truffle lazy exceptions, so here we only @@ -234,12 +270,12 @@ public void exit(VirtualFrame frame, PRootNode node) { PFrame.Reference info = PArguments.getCurrentFrameInfo(frame); CompilerAsserts.partialEvaluationConstant(node); if (node.getFrameEscapedProfile().profile(info.isEscaped())) { - exitEscaped(frame, node, info); + exitEscaped(frame, node, location, info); } } @InliningCutoff - private void exitEscaped(VirtualFrame frame, PRootNode node, Reference info) { + private void exitEscaped(VirtualFrame frame, PRootNode node, Node location, Reference info) { if (!everEscaped) { CompilerDirectives.transferToInterpreterAndInvalidate(); everEscaped = true; @@ -267,7 +303,7 @@ private void exitEscaped(VirtualFrame frame, PRootNode node, Reference info) { } // force the frame so that it can be accessed later - ensureMaterializeNode().execute(frame, node, false, true); + ensureMaterializeNode().execute(frame, location, false, true); // if this frame escaped we must ensure that also f_back does callerInfo.markAsEscaped(); info.setBackref(callerInfo); diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/PythonContext.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/PythonContext.java index 1e1bdbc4f7..e1bfb84e0d 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/PythonContext.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/PythonContext.java @@ -157,6 +157,7 @@ import com.oracle.graal.python.nodes.SpecialMethodNames; import com.oracle.graal.python.nodes.WriteUnraisableNode; import com.oracle.graal.python.nodes.attributes.ReadAttributeFromObjectNode; +import com.oracle.graal.python.nodes.bytecode_dsl.PBytecodeDSLRootNode; import com.oracle.graal.python.nodes.call.CallNode; import com.oracle.graal.python.nodes.object.SetDictNode; import com.oracle.graal.python.nodes.statement.AbstractImportNode; @@ -182,6 +183,7 @@ import com.oracle.truffle.api.HostCompilerDirectives.InliningCutoff; import com.oracle.truffle.api.RootCallTarget; import com.oracle.truffle.api.ThreadLocalAction; +import com.oracle.truffle.api.Truffle; import com.oracle.truffle.api.TruffleContext; import com.oracle.truffle.api.TruffleContext.Builder; import com.oracle.truffle.api.TruffleFile; @@ -350,6 +352,13 @@ public static final class PythonThreadState { */ Object asyncgenFirstIter; + /* + * Instrumentation data (Bytecode DSL interpreter only). For the manual bytecode + * interpreter, this data is stored in a local state variable, but the DSL interpreter must + * use a thread local. + */ + PBytecodeDSLRootNode.InstrumentationData instrumentationData; + /* * Counter for C-level recursion depth used for Py_(Enter/Leave)RecursiveCall. */ @@ -528,13 +537,48 @@ public void dispose(PythonContext context) { } } + private static void invalidateNoTracingOrProfilingAssumption(PythonLanguage language) { + if (language.noTracingOrProfilingAssumption.isValid()) { + language.noTracingOrProfilingAssumption.invalidate(); + + if (PythonOptions.ENABLE_BYTECODE_DSL_INTERPRETER) { + enableTracingOrProfilingForActiveRootNodes(); + } + } + } + + @TruffleBoundary + private static void enableTracingOrProfilingForActiveRootNodes() { + final List rootNodes = new ArrayList<>(); + + // Ensure tracing + profiling are enabled for each method on the stack. + Truffle.getRuntime().iterateFrames((frameInstance) -> { + if (frameInstance.getCallTarget() instanceof RootCallTarget c && c.getRootNode() instanceof PBytecodeDSLRootNode r) { + if (r.needsTraceAndProfileInstrumentation()) { + r.ensureTraceAndProfileEnabled(); + } + rootNodes.add(r); + } + return null; + }); + + /** + * Normally, a root node will push + pop the instrumentation data in its prolog/epilog. + * Since these nodes are on stack, we need to push them manually, starting from the + * deepest stack frame. + */ + for (PBytecodeDSLRootNode rootNode : rootNodes.reversed()) { + rootNode.getThreadState().pushInstrumentationData(rootNode); + } + } + public Object getTraceFun() { return traceFun; } public void setTraceFun(Object traceFun, PythonLanguage language) { if (this.traceFun != traceFun) { - language.noTracingOrProfilingAssumption.invalidate(); + invalidateNoTracingOrProfilingAssumption(language); this.traceFun = traceFun; } } @@ -563,7 +607,7 @@ public void setTracingWhat(TraceEvent tracingWhat) { public void setProfileFun(Object profileFun, PythonLanguage language) { if (this.profileFun != profileFun) { - language.noTracingOrProfilingAssumption.invalidate(); + invalidateNoTracingOrProfilingAssumption(language); this.profileFun = profileFun; } } @@ -585,6 +629,23 @@ public void profilingStop() { this.profiling = false; } + public PBytecodeDSLRootNode.InstrumentationData getInstrumentationData(PBytecodeDSLRootNode rootNode) { + assert PythonOptions.ENABLE_BYTECODE_DSL_INTERPRETER; + assert instrumentationData != null && instrumentationData.getRootNode() == rootNode; + return instrumentationData; + } + + public void pushInstrumentationData(PBytecodeDSLRootNode rootNode) { + assert PythonOptions.ENABLE_BYTECODE_DSL_INTERPRETER; + instrumentationData = new PBytecodeDSLRootNode.InstrumentationData(rootNode, instrumentationData); + } + + public void popInstrumentationData(PBytecodeDSLRootNode rootNode) { + assert PythonOptions.ENABLE_BYTECODE_DSL_INTERPRETER; + assert instrumentationData != null && instrumentationData.getRootNode() == rootNode; + instrumentationData = instrumentationData.getPrevious(); + } + public Object getAsyncgenFirstIter() { return asyncgenFirstIter; } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/PythonOptions.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/PythonOptions.java index e50de17175..976ad32d40 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/PythonOptions.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/PythonOptions.java @@ -82,6 +82,12 @@ public final class PythonOptions { */ public static final boolean AUTOMATIC_ASYNC_ACTIONS = !"false".equalsIgnoreCase(System.getProperty("python.AutomaticAsyncActions")); + /** + * Whether to use the experimental Bytecode DSL interpreter instead of the manually-written + * bytecode interpreter. + */ + public static final boolean ENABLE_BYTECODE_DSL_INTERPRETER = Boolean.getBoolean("python.EnableBytecodeDSLInterpreter"); + public enum HPyBackendMode { NFI, JNI, diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/exception/ExceptionUtils.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/exception/ExceptionUtils.java index 9bfdff7330..5cd8b646ac 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/exception/ExceptionUtils.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/exception/ExceptionUtils.java @@ -57,6 +57,7 @@ import com.oracle.graal.python.builtins.objects.traceback.LazyTraceback; import com.oracle.graal.python.nodes.BuiltinNames; import com.oracle.graal.python.nodes.ErrorMessages; +import com.oracle.graal.python.nodes.bytecode.BytecodeFrameInfo; import com.oracle.graal.python.nodes.bytecode.FrameInfo; import com.oracle.graal.python.nodes.call.CallNode; import com.oracle.graal.python.nodes.exception.TopLevelExceptionHandler; @@ -64,6 +65,7 @@ import com.oracle.graal.python.nodes.object.GetClassNode; import com.oracle.graal.python.runtime.PythonContext; import com.oracle.graal.python.runtime.object.PythonObjectFactory; +import com.oracle.graal.python.runtime.PythonOptions; import com.oracle.truffle.api.CompilerAsserts; import com.oracle.truffle.api.CompilerDirectives; import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; @@ -72,8 +74,8 @@ import com.oracle.truffle.api.Truffle; import com.oracle.truffle.api.TruffleStackTrace; import com.oracle.truffle.api.TruffleStackTraceElement; +import com.oracle.truffle.api.bytecode.BytecodeNode; import com.oracle.truffle.api.frame.Frame; -import com.oracle.truffle.api.frame.FrameDescriptor; import com.oracle.truffle.api.frame.FrameInstance; import com.oracle.truffle.api.frame.FrameInstanceVisitor; import com.oracle.truffle.api.interop.InteropLibrary; @@ -98,24 +100,34 @@ public static void printPythonLikeStackTrace() { if (location == null) { location = rootNode; } - int lineno = getLineno(frameInstance.getFrame(FrameInstance.FrameAccess.READ_ONLY)); + int lineno = getLineno(frameInstance.getFrame(FrameInstance.FrameAccess.READ_ONLY), location, frameInstance); appendStackLine(stack, location, rootNode, true, lineno); return null; }); printStack(new PrintWriter(System.err, true), stack); } - private static int getLineno(Frame frame) { - int lineno = -1; - if (frame != null) { - FrameDescriptor fd = frame.getFrameDescriptor(); - if (fd.getInfo() instanceof FrameInfo) { - FrameInfo frameInfo = (FrameInfo) fd.getInfo(); - int bci = frameInfo.getBci(frame); - lineno = frameInfo.getRootNode().bciToLine(bci); + private static int getLineno(Frame frame, Node location, FrameInstance frameInstance) { + if (frame != null && frame.getFrameDescriptor().getInfo() instanceof FrameInfo frameInfo) { + if (PythonOptions.ENABLE_BYTECODE_DSL_INTERPRETER) { + BytecodeNode bytecodeNode = null; + if (frameInstance != null) { + bytecodeNode = BytecodeNode.get(frameInstance); + } else { + // NB: This fails for the top stack frame, which has no BytecodeNode. + bytecodeNode = BytecodeNode.get(location); + } + + if (bytecodeNode != null) { + int bci = bytecodeNode.getBytecodeIndex(frame); + return bytecodeNode.getBytecodeLocation(bci).getSourceLocation().getStartLine(); + } + } else { + return ((BytecodeFrameInfo) frameInfo).getLine(frame); } } - return lineno; + return -1; + } @TruffleBoundary @@ -149,7 +161,7 @@ public static void printPythonLikeStackTrace(PrintWriter p, Throwable e) { for (TruffleStackTraceElement frame : stackTrace) { Node location = frame.getLocation(); RootNode rootNode = frame.getTarget().getRootNode(); - int lineno = getLineno(frame.getFrame()); + int lineno = getLineno(frame.getFrame(), location, null); appendStackLine(stack, location, rootNode, false, lineno); } printStack(p, stack); diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/exception/PException.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/exception/PException.java index 0db09d048e..987ac2d3da 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/exception/PException.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/exception/PException.java @@ -50,11 +50,15 @@ import com.oracle.graal.python.builtins.objects.traceback.MaterializeLazyTracebackNode; import com.oracle.graal.python.builtins.objects.traceback.PTraceback; import com.oracle.graal.python.lib.PyExceptionInstanceCheckNode; +import com.oracle.graal.python.nodes.PRootNode; import com.oracle.graal.python.nodes.bytecode.PBytecodeRootNode; +import com.oracle.graal.python.nodes.bytecode_dsl.PBytecodeDSLRootNode; import com.oracle.graal.python.nodes.object.BuiltinClassProfiles.IsBuiltinObjectProfile; import com.oracle.graal.python.runtime.GilNode; +import com.oracle.graal.python.runtime.PythonOptions; import com.oracle.truffle.api.CompilerAsserts; import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; +import com.oracle.truffle.api.bytecode.BytecodeNode; import com.oracle.truffle.api.dsl.Bind; import com.oracle.truffle.api.dsl.Cached; import com.oracle.truffle.api.dsl.Cached.Exclusive; @@ -86,7 +90,12 @@ public final class PException extends AbstractTruffleException { private String message = null; final transient Object pythonException; private transient PFrame.Reference frameInfo; - private transient PBytecodeRootNode catchRootNode; + + // This node is a manual bytecode or Bytecode DSL root node. + private transient PRootNode rootNode; + // This node is present when using the Bytecode DSL. + private transient BytecodeNode bytecodeNode; + private int catchBci; private boolean reified = false; private boolean skipFirstTracebackFrame; @@ -176,17 +185,35 @@ public void skipFirstTracebackFrame() { } public boolean catchingFrameWantedForTraceback() { - return tracebackFrameCount >= 0 && catchRootNode != null && catchRootNode.frameIsVisibleToPython(); - } - - public PBytecodeRootNode getCatchRootNode() { - return catchRootNode; + if (tracebackFrameCount < 0 || rootNode == null) { + return false; + } + if (PythonOptions.ENABLE_BYTECODE_DSL_INTERPRETER) { + return !((PBytecodeDSLRootNode) rootNode).isInternal(); + } else { + return ((PBytecodeRootNode) rootNode).frameIsVisibleToPython(); + } } public int getCatchBci() { return catchBci; } + public BytecodeNode getBytecodeNode() { + return bytecodeNode; + } + + public int getCatchLine() { + if (rootNode == null) { + return -1; + } + if (PythonOptions.ENABLE_BYTECODE_DSL_INTERPRETER) { + return ((PBytecodeDSLRootNode) rootNode).bciToLine(catchBci, bytecodeNode); + } else { + return ((PBytecodeRootNode) rootNode).bciToLine(catchBci); + } + } + /** * Return the associated {@link PBaseException}. This method doesn't ensure traceback * consistency and should be avoided unless you can guarantee that the exception will not escape @@ -240,12 +267,67 @@ public void expect(Node inliningTarget, PythonBuiltinClassType error, IsBuiltinO } } + /** + * Set the catching frame reference for a manual bytecode node. + */ public void setCatchingFrameReference(Frame frame, PBytecodeRootNode catchLocation, int catchBci) { this.frameInfo = PArguments.getCurrentFrameInfo(frame); - this.catchRootNode = catchLocation; + this.rootNode = catchLocation; this.catchBci = catchBci; } + /** + * Sets the catching frame information for a Bytecode DSL node. + * + * NB: The manual bytecode interpreter sets all of the catching frame info in one step after it + * finds a handler for the bci. This is possible because it has control over the handler + * dispatch logic. + * + * The Bytecode DSL interpreter's generated code automatically dispatches to a handler. We can + * set the frame info inside the handler code, but the bci of the raising instruction is lost at + * that point. + * + * We thus set the catch information in two steps: + *
    + *
  1. First, we use a hook in the DSL to set the catch location every time an exception is + * thrown from the bytecode loop ({@link #setCatchLocation}).
  2. + *
  3. Then, we mark the exception as "caught" when we reach a handler + * ({@link #markAsCaught(Frame, PBytecodeDSLRootNode)}. Once it's "caught", the catch location + * is frozen.
  4. + *
+ * + * (It is necessary to freeze the location after calling + * {@link #markAsCaught(Frame, PBytecodeDSLRootNode)} because Python-level try-except-finally + * blocks are implemented with multiple DSL-level throws; these throws will trigger subsequent + * {@link #setCatchLocation} calls that would overwrite the location.) + * + * Since the catch location is set unconditionally, it could refer to a location that had no + * handler (i.e., the location is invalid). Code should not use the location unless the other + * frame info (e.g., the {@link #rootNode}) has been set, which indicates that a handler was + * found and that the catch location actually refers to a guarded instruction. + */ + public void setCatchLocation(int catchBci, BytecodeNode bytecodeNode) { + assert PythonOptions.ENABLE_BYTECODE_DSL_INTERPRETER; + // Overwrite the catchBci as long as no handler has been found yet. + if (!isCaught()) { + this.catchBci = catchBci; + this.bytecodeNode = bytecodeNode; + } + } + + public void markAsCaught(Frame frame, PBytecodeDSLRootNode catchLocation) { + assert PythonOptions.ENABLE_BYTECODE_DSL_INTERPRETER; + if (!isCaught()) { + this.frameInfo = PArguments.getCurrentFrameInfo(frame); + this.rootNode = catchLocation; + } + } + + private boolean isCaught() { + assert PythonOptions.ENABLE_BYTECODE_DSL_INTERPRETER; + return rootNode != null; + } + public void markEscaped() { markFrameEscaped(); if (!(pythonException instanceof PBaseException)) { diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/object/PythonObjectFactory.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/object/PythonObjectFactory.java index d9644dce77..9ecb2fc7da 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/object/PythonObjectFactory.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/object/PythonObjectFactory.java @@ -226,8 +226,11 @@ import com.oracle.graal.python.builtins.objects.types.PGenericAlias; import com.oracle.graal.python.builtins.objects.types.PGenericAliasIterator; import com.oracle.graal.python.builtins.objects.types.PUnionType; +import com.oracle.graal.python.compiler.BytecodeCodeUnit; import com.oracle.graal.python.compiler.CodeUnit; import com.oracle.graal.python.nodes.bytecode.PBytecodeRootNode; +import com.oracle.graal.python.nodes.bytecode_dsl.BytecodeDSLCodeUnit; +import com.oracle.graal.python.nodes.bytecode_dsl.PBytecodeDSLRootNode; import com.oracle.graal.python.runtime.NFIZlibSupport; import com.oracle.graal.python.runtime.PythonContext; import com.oracle.graal.python.runtime.PythonOptions; @@ -954,14 +957,26 @@ public final PGenerator createGenerator(TruffleString name, TruffleString qualna return trace(PGenerator.create(getLanguage(), name, qualname, rootNode, callTargets, arguments, PythonBuiltinClassType.PGenerator)); } + public final PGenerator createGenerator(TruffleString name, TruffleString qualname, PBytecodeDSLRootNode rootNode, Object[] arguments) { + return trace(PGenerator.create(getLanguage(), name, qualname, rootNode, arguments, PythonBuiltinClassType.PGenerator)); + } + public final PGenerator createIterableCoroutine(TruffleString name, TruffleString qualname, PBytecodeRootNode rootNode, RootCallTarget[] callTargets, Object[] arguments) { return trace(PGenerator.create(getLanguage(), name, qualname, rootNode, callTargets, arguments, PythonBuiltinClassType.PGenerator, true)); } + public final PGenerator createIterableCoroutine(TruffleString name, TruffleString qualname, PBytecodeDSLRootNode rootNode, Object[] arguments) { + return trace(PGenerator.create(getLanguage(), name, qualname, rootNode, arguments, PythonBuiltinClassType.PGenerator, true)); + } + public final PGenerator createCoroutine(TruffleString name, TruffleString qualname, PBytecodeRootNode rootNode, RootCallTarget[] callTargets, Object[] arguments) { return trace(PGenerator.create(getLanguage(), name, qualname, rootNode, callTargets, arguments, PythonBuiltinClassType.PCoroutine)); } + public final PGenerator createCoroutine(TruffleString name, TruffleString qualname, PBytecodeDSLRootNode rootNode, Object[] arguments) { + return trace(PGenerator.create(getLanguage(), name, qualname, rootNode, arguments, PythonBuiltinClassType.PCoroutine)); + } + public final PCoroutineWrapper createCoroutineWrapper(PGenerator generator) { return trace(new PCoroutineWrapper(getLanguage(), generator)); } @@ -1201,7 +1216,11 @@ public final PCode createCode(RootCallTarget ct, int flags, int firstlineno, byt return trace(new PCode(PythonBuiltinClassType.PCode, PythonBuiltinClassType.PCode.getInstanceShape(getLanguage()), ct, flags, firstlineno, linetable, filename)); } - public final PCode createCode(RootCallTarget callTarget, Signature signature, CodeUnit codeUnit) { + public final PCode createCode(RootCallTarget callTarget, Signature signature, BytecodeCodeUnit codeUnit) { + return trace(new PCode(PythonBuiltinClassType.PCode, getShape(PythonBuiltinClassType.PCode), callTarget, signature, codeUnit)); + } + + public final PCode createCode(RootCallTarget callTarget, Signature signature, BytecodeDSLCodeUnit codeUnit) { return trace(new PCode(PythonBuiltinClassType.PCode, getShape(PythonBuiltinClassType.PCode), callTarget, signature, codeUnit)); } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/util/PythonUtils.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/util/PythonUtils.java index 00c43e123d..5d4bdb03c3 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/util/PythonUtils.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/util/PythonUtils.java @@ -86,6 +86,7 @@ import com.oracle.graal.python.nodes.util.CastToTruffleStringNode; import com.oracle.graal.python.pegparser.scope.ScopeEnvironment; import com.oracle.graal.python.pegparser.sst.ConstantValue; +import com.oracle.graal.python.runtime.PythonOptions; import com.oracle.graal.python.runtime.object.PythonObjectFactory; import com.oracle.graal.python.runtime.object.PythonObjectSlowPathFactory; import com.oracle.truffle.api.CallTarget; @@ -779,7 +780,12 @@ public static Source createFakeSource() { @TruffleBoundary public static Source createFakeSource(TruffleString name) { - return Source.newBuilder(PythonLanguage.ID, EMPTY_BYTE_SEQUENCE, name.toJavaStringUncached()).build(); + if (PythonOptions.ENABLE_BYTECODE_DSL_INTERPRETER) { + // The DSL interpreter requires character-based sources. + return Source.newBuilder(PythonLanguage.ID, "", name.toJavaStringUncached()).content(Source.CONTENT_NONE).build(); + } else { + return Source.newBuilder(PythonLanguage.ID, EMPTY_BYTE_SEQUENCE, name.toJavaStringUncached()).build(); + } } public static Object[] prependArgument(Object primary, Object[] arguments) { diff --git a/graalpython/lib-python/3/test/support/__init__.py b/graalpython/lib-python/3/test/support/__init__.py index 065e1ef0d7..f06bcfcae9 100644 --- a/graalpython/lib-python/3/test/support/__init__.py +++ b/graalpython/lib-python/3/test/support/__init__.py @@ -1056,6 +1056,17 @@ def impl_detail(msg=None, **guards): msg = msg.format(' or '.join(guardnames)) return unittest.skip(msg) +def bytecode_dsl_excluded(test): + """ + Decorator for tests that don't apply to the Bytecode DSL interpreter. + """ + try: + if sys.implementation.name == 'graalpy' and __graalpython__.is_bytecode_dsl_interpreter: + return unittest.skip("Not supported by the Bytecode DSL interpreter") + except NameError: + pass + return test + def _parse_guards(guards): # Returns a tuple ({platform_name: run_me}, default_value) if not guards: diff --git a/graalpython/lib-python/3/test/test_fstring.py b/graalpython/lib-python/3/test/test_fstring.py index 8e719cdef2..733b65ba6a 100644 --- a/graalpython/lib-python/3/test/test_fstring.py +++ b/graalpython/lib-python/3/test/test_fstring.py @@ -13,7 +13,6 @@ import types import decimal import unittest -from test.support import impl_detail from test.support.os_helper import temp_cwd from test.support.script_helper import assert_python_failure diff --git a/graalpython/lib-python/3/test/test_sys_settrace.py b/graalpython/lib-python/3/test/test_sys_settrace.py index e97e122534..6f06ed017b 100644 --- a/graalpython/lib-python/3/test/test_sys_settrace.py +++ b/graalpython/lib-python/3/test/test_sys_settrace.py @@ -1869,6 +1869,7 @@ def no_jump_without_trace_function(): raise AssertionError("Trace-function-less jump failed to fail") +@support.bytecode_dsl_excluded class JumpTestCase(unittest.TestCase): def setUp(self): self.addCleanup(sys.settrace, sys.gettrace()) diff --git a/mx.graalpython/mx_graalpython.py b/mx.graalpython/mx_graalpython.py index 9c63f1ac43..a4057aaa59 100644 --- a/mx.graalpython/mx_graalpython.py +++ b/mx.graalpython/mx_graalpython.py @@ -119,6 +119,7 @@ def get_boolean_env(name, default=False): CI = get_boolean_env("CI") WIN32 = sys.platform == "win32" BUILD_NATIVE_IMAGE_WITH_ASSERTIONS = get_boolean_env('BUILD_WITH_ASSERTIONS', CI) +BYTECODE_DSL_INTERPRETER = get_boolean_env('BYTECODE_DSL_INTERPRETER', False) if CI and not os.environ.get("GRAALPYTEST_FAIL_FAST"): os.environ["GRAALPYTEST_FAIL_FAST"] = "true" @@ -386,6 +387,9 @@ def __str__(self): vm_args = ['-Dpolyglot.engine.WarnInterpreterOnly=false'] + if BYTECODE_DSL_INTERPRETER: + vm_args.append("-Dpython.EnableBytecodeDSLInterpreter=true") + # 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 graalpy_tests = ['com.oracle.graal.python.test', 'com.oracle.graal.python.pegparser.test', 'org.graalvm.python.embedding.utils.test'] @@ -481,8 +485,12 @@ def run_cpython_test(raw_args): for tag in test_tags or (): test_args += ['-k', tag] + vm_args = ['--vm.ea'] + if BYTECODE_DSL_INTERPRETER: + vm_args += ['--vm.Dpython.EnableBytecodeDSLInterpreter=true'] + python_args = [ - '--vm.ea', + *vm_args, *rest_args, os.path.join(SUITE.dir, "graalpython/com.oracle.graal.python.test/src/tests/run_cpython_test.py"), *test_args, @@ -880,6 +888,15 @@ def graalpy_standalone_home(standalone_type, enterprise=False, dev=False, build= import_managed_status = mx.run([launcher, "-c", "import sys; assert 'Oracle GraalVM' in sys.version"], nonZeroIsFatal=False, out=out, err=out) if enterprise != (import_managed_status == 0): mx.abort(f"GRAALPY_HOME is not compatible with requested distribution kind ({import_managed_status=}, {enterprise=}, {out=}).") + + out = mx.OutputCapture() + mx.run([launcher, "-c", "print(__graalpython__.is_bytecode_dsl_interpreter)"], nonZeroIsFatal=False, out=out, err=out) + is_bytecode_dsl_interpreter = out.data.strip() == "True" + if is_bytecode_dsl_interpreter != BYTECODE_DSL_INTERPRETER: + requested = "Bytecode DSL" if BYTECODE_DSL_INTERPRETER else "Manual" + actual = "Bytecode DSL" if is_bytecode_dsl_interpreter else "Manual" + mx.abort(f"GRAALPY_HOME is not compatible with requested interpreter kind ({requested=}, {actual=})") + return python_home env_file = 'ce-python' @@ -906,13 +923,20 @@ def graalpy_standalone_home(standalone_type, enterprise=False, dev=False, build= if mx_gate.get_jacoco_agent_args() or (build and not DISABLE_REBUILD): dep_type = 'JAVA' if standalone_type == 'jvm' else 'NATIVE' + mx_build_args = mx_args + if BYTECODE_DSL_INTERPRETER: + mx_build_args = mx_args + ["--extra-image-builder-argument=-Dpython.EnableBytecodeDSLInterpreter=true"] + # Example of a string we're building here: PYTHON_JAVA_STANDALONE_SVM_SVMEE_JAVA21 - mx.run_mx(mx_args + ["build", "--dep", f"PYTHON_{dep_type}_STANDALONE{svm_component}_JAVA{jdk_version.parts[0]}"]) + mx.run_mx(mx_build_args + ["build", "--dep", f"PYTHON_{dep_type}_STANDALONE{svm_component}_JAVA{jdk_version.parts[0]}"]) out = mx.OutputCapture() + + # note: 'quiet=True' is important otherwise if the outer MX runs verbose, # this might fail because of additional output mx.run_mx(mx_args + ["standalone-home", "--type", standalone_type, "python"], out=out, quiet=True) + python_home = out.data.splitlines()[-1].strip() if dev and standalone_type == 'native': path = Path(python_home) @@ -1101,8 +1125,13 @@ def graalpytest(args): if not DISABLE_REBUILD: mx.command_function("build")(["--only", "com.oracle.graal.python.test"]) - testfiles = _list_graalpython_unittests(args.test) cmd_args = [] + excludes = [] + + if BYTECODE_DSL_INTERPRETER: + cmd_args += ["--vm.Dpython.EnableBytecodeDSLInterpreter=true"] + + testfiles = _list_graalpython_unittests(args.test, exclude=excludes) # if we got a binary path it's most likely CPython, so don't add graalpython args if not args.python: cmd_args += ["--experimental-options=true", "--python.EnableDebuggingBuiltins"] @@ -1111,7 +1140,7 @@ def graalpytest(args): mx.log(f"Executable seems to be GraalPy, prepending arguments: {gp_args}") cmd_args += gp_args # we assume that unknown args are polyglot arguments and just prepend them to the test driver - cmd_args += unknown_args + [_graalpytest_driver()] + cmd_args += unknown_args + [_graalpytest_driver(), "--report", "test_report.json"] if args.verbose: cmd_args += ["-v"] cmd_args += testfiles @@ -1120,7 +1149,12 @@ def graalpytest(args): env = extend_os_env(PYTHONHASHSEED='0') delete_bad_env_keys(env) if args.python: - return mx.run([args.python] + cmd_args, nonZeroIsFatal=True, env=env) + try: + result = mx.run([args.python] + cmd_args, nonZeroIsFatal=True, env=env) + print(f"back from mx.run, returning {result}") + return result + except BaseException as e: + print(f"Exception raised: {e}") else: return full_python(cmd_args, env=env) @@ -1157,7 +1191,8 @@ def is_included(path): def run_python_unittests(python_binary, args=None, paths=None, aot_compatible=False, exclude=None, env=None, - use_pytest=False, cwd=None, lock=None, out=None, err=None, nonZeroIsFatal=True, timeout=None, report=False): + use_pytest=False, cwd=None, lock=None, out=None, err=None, nonZeroIsFatal=True, + timeout=None, report=False): if lock: lock.acquire() # ensure that the test distribution is up-to-date @@ -1172,6 +1207,7 @@ def run_python_unittests(python_binary, args=None, paths=None, aot_compatible=Fa "--python.CatchAllExceptions=true", *args, ] + exclude = exclude or [] if env is None: env = os.environ.copy() @@ -1187,6 +1223,9 @@ def run_python_unittests(python_binary, args=None, paths=None, aot_compatible=Fa else: exclude += AOT_ONLY_TESTS + if BYTECODE_DSL_INTERPRETER: + args += ['--vm.Dpython.EnableBytecodeDSLInterpreter=true'] + # just to be able to verify, print C ext mode (also works for CPython) mx.run([python_binary, "-c", @@ -1341,6 +1380,7 @@ def is_alive(self): mx.warn(message) + def run_tagged_unittests(python_binary, env=None, cwd=None, nonZeroIsFatal=True, checkIfWithGraalPythonEE=False, report=False): python_path = os.path.join(_dev_pythonhome(), 'lib-python/3') @@ -1353,12 +1393,13 @@ def run_tagged_unittests(python_binary, env=None, cwd=None, nonZeroIsFatal=True, ENABLE_CPYTHON_TAGGED_UNITTESTS="true", ) print(f"with PYTHONPATH={python_path}") + args = ["-v"] if checkIfWithGraalPythonEE: mx.run([python_binary, "-c", "import sys; print(sys.version)"]) run_python_unittests( python_binary, - args=["-v"], + args=args, paths=["test_tagged_unittests.py"], env=sub_env, cwd=cwd, @@ -1437,28 +1478,31 @@ def graalpython_gate_runner(args, tasks): excluded_tests = [] # JUnit tests + def do_junit(): + if WIN32: + punittest( + [ + "--verbose", + "--no-leak-tests", + "--regex", + r'((com\.oracle\.truffle\.tck\.tests)|(graal\.python\.test\.integration)|(graal\.python\.test\.(builtin|interop|util)))' + ], + report=True, + ) + else: + punittest(['--verbose'], report=report()) + # Run tests with static exclusion paths + jdk = mx.get_jdk() + prev = jdk.java_args_pfx + try: + jdk.java_args_pfx = (mx._opts.java_args or []) + ['-Dpython.WithoutPlatformAccess=true'] + punittest(['--verbose', '--no-leak-tests', '--regex', 'com.oracle.graal.python.test.advanced.ExclusionsTest']) + finally: + jdk.java_args_pfx = prev + with Task('GraalPython JUnit', tasks, tags=[GraalPythonTags.junit, GraalPythonTags.windows]) as task: if task: - if WIN32: - punittest( - [ - "--verbose", - "--no-leak-tests", - "--regex", - r'((com\.oracle\.truffle\.tck\.tests)|(graal\.python\.test\.integration)|(graal\.python\.test\.(builtin|interop|util)))' - ], - report=True - ) - else: - punittest(['--verbose'], report=report()) - # Run tests with static exclusion paths - jdk = mx.get_jdk() - prev = jdk.java_args_pfx - try: - jdk.java_args_pfx = (mx._opts.java_args or []) + ['-Dpython.WithoutPlatformAccess=true'] - punittest(['--verbose', '--no-leak-tests', '--regex', 'com.oracle.graal.python.test.advanced.ExclusionsTest']) - finally: - jdk.java_args_pfx = prev + do_junit() # JUnit tests with Maven with Task('GraalPython integration JUnit with Maven', tasks, tags=[GraalPythonTags.junit_maven]) as task: @@ -1490,7 +1534,8 @@ def graalpython_gate_runner(args, tasks): graalpy_standalone_jvm(), exclude=excluded_tests, nonZeroIsFatal=nonZeroIsFatal, - report=report() + report=report(), + env=extend_os_env(GRAALPYTEST_FAIL_FAST="False") if BYTECODE_DSL_INTERPRETER else None ) with Task('GraalPython Python unittests with CPython', tasks, tags=[GraalPythonTags.unittest_cpython]) as task: @@ -2160,6 +2205,9 @@ def _python_checkpatchfiles(): 'LLVM.org toolchain': ('lib/llvm-toolchain', []), } +def bytecode_dsl_build_args(): + return ['-Dpython.EnableBytecodeDSLInterpreter=true'] if BYTECODE_DSL_INTERPRETER else [] + mx_sdk.register_graalvm_component(mx_sdk.GraalVmLanguage( suite=SUITE, name='GraalVM Python', @@ -2215,7 +2263,7 @@ def _python_checkpatchfiles(): '-H:-CopyLanguageResources', '-Dpolyglot.python.PosixModuleBackend=native', '-Dpolyglot.python.Sha3ModuleBackend=native', - ], + ] + bytecode_dsl_build_args(), language='python', default_vm_args=[ '--vm.Xss16777216', # request 16M of stack @@ -2689,9 +2737,26 @@ def getBuildEnv(self, replaceVar=mx_subst.path_substitutions): return ret +class GraalpythonFrozenModuleBuildTask(GraalpythonBuildTask): + def build(self): + # We freeze modules twice: once for the manual Bytecode interpreter and once for the DSL interpreter. + args = [mx_subst.path_substitutions.substitute(a, dependency=self) for a in self.subject.args] + return self.run(args, "manual bytecode") or self.run(args, "dsl", extra_vm_args=["-Dpython.EnableBytecodeDSLInterpreter=true"]) + + def run(self, args, interpreter_kind, extra_vm_args=None): + mx.log(f"Building frozen modules for {interpreter_kind} interpreter.") + return super().run(args, extra_vm_args=extra_vm_args) + + +class GraalpythonFrozenProject(GraalpythonProject): + def getBuildTask(self, args): + return GraalpythonFrozenModuleBuildTask(args, self) + + orig_clean = mx.command_function("clean") def python_clean(args): - orig_clean(args) + if '--just-pyc' not in args: + orig_clean(args) count = 0 for path in os.walk(SUITE.dir): for file in glob.iglob(os.path.join(path[0], '*.pyc')): @@ -3059,7 +3124,7 @@ def graalpy_standalone_wrapper(args_in): 'python-coverage': [python_coverage, ''], 'punittest': [punittest, ''], 'graalpytest': [graalpytest, '[-h] [-v] [--python PYTHON] [-k TEST_PATTERN] [TESTS]'], - 'clean': [python_clean, ''], + 'clean': [python_clean, '[--just-pyc]'], 'python-update-hpy-import': [update_hpy_import_cmd, '[--no-pull] PATH_TO_HPY'], 'bisect-benchmark': [mx_graalpython_bisect.bisect_benchmark, ''], 'python-leak-test': [run_leak_launcher, ''], diff --git a/mx.graalpython/mx_graalpython_benchmark.py b/mx.graalpython/mx_graalpython_benchmark.py index 0495c1ce99..0f5766e15f 100644 --- a/mx.graalpython/mx_graalpython_benchmark.py +++ b/mx.graalpython/mx_graalpython_benchmark.py @@ -32,6 +32,7 @@ import mx import mx_benchmark +import mx_graalpython from mx_benchmark import StdOutRule, java_vm_registry, Vm, GuestVm, VmBenchmarkSuite, AveragingBenchmarkMixin from mx_graalpython_bench_param import HARNESS_PATH @@ -367,6 +368,9 @@ def run(self, cwd, args): _check_vm_args(self.name(), args) extra_polyglot_args = self.get_extra_polyglot_args() + if mx_graalpython.BYTECODE_DSL_INTERPRETER: + args.insert(0, "--vm.Dpython.EnableBytecodeDSLInterpreter=true") + host_vm = self.host_vm() if hasattr(host_vm, 'run_lang'): # this is a full GraalVM build return self.run_in_graalvm(cwd, args, extra_polyglot_args, host_vm) diff --git a/mx.graalpython/suite.py b/mx.graalpython/suite.py index 6afc099f36..c9466dcd1f 100644 --- a/mx.graalpython/suite.py +++ b/mx.graalpython/suite.py @@ -45,7 +45,7 @@ }, { "name": "sdk", - "version": "a9fa5622efc0ff127390f8986429df525a8948c2", + "version": "94b61743e339e2b5e0a01b4b35936ded1344d8b0", "subdir": True, "urls": [ {"url": "https://github.com/oracle/graal", "kind": "git"}, @@ -53,7 +53,7 @@ }, { "name": "tools", - "version": "a9fa5622efc0ff127390f8986429df525a8948c2", + "version": "94b61743e339e2b5e0a01b4b35936ded1344d8b0", "subdir": True, "urls": [ {"url": "https://github.com/oracle/graal", "kind": "git"}, @@ -61,7 +61,7 @@ }, { "name": "sulong", - "version": "a9fa5622efc0ff127390f8986429df525a8948c2", + "version": "94b61743e339e2b5e0a01b4b35936ded1344d8b0", "subdir": True, "urls": [ {"url": "https://github.com/oracle/graal", "kind": "git"}, @@ -69,7 +69,7 @@ }, { "name": "regex", - "version": "a9fa5622efc0ff127390f8986429df525a8948c2", + "version": "94b61743e339e2b5e0a01b4b35936ded1344d8b0", "subdir": True, "urls": [ {"url": "https://github.com/oracle/graal", "kind": "git"}, @@ -362,7 +362,7 @@ "com.oracle.graal.python.frozen": { "subDir": "graalpython", "vpath": True, - "type": "GraalpythonProject", + "type": "GraalpythonFrozenProject", "args": [ "/freeze_modules.py", "--python-lib",