├── .dockerignore ├── src ├── images │ ├── attribute_access.png │ └── class_identifier_storage.png ├── CONTRIBUTING.md ├── event_counters.proto ├── strongjit │ ├── dis6.pyi │ ├── ingestion_handlers.h │ ├── parser.h │ ├── cursor.cc │ ├── base.h │ ├── value_map.cc │ ├── deoptimization.h │ ├── callees.cc │ ├── optimize_cfg.h │ ├── string_table.h │ ├── util.cc │ ├── value_traits_test.cc │ ├── optimize_type_construction.h │ ├── instruction_traits_test.cc │ ├── value_map.h │ ├── dis6.cc │ ├── instruction.cc │ ├── block.cc │ ├── optimize_constants.h │ ├── dis6_test.py │ ├── instructions.cc │ ├── value_casts.h │ ├── cursor.h │ ├── instruction_traits.h │ ├── value.h │ ├── optimize_constants.cc │ └── optimize_liveness.cc ├── python │ ├── hello_world.py │ ├── type_feedback.pyi │ ├── CMakeLists.txt │ ├── type_feedback.cc │ ├── api.pyi │ ├── type_feedback_test.py │ └── api_test.py ├── global_intern_table.cc ├── runtime │ ├── interposer.h │ ├── python_function_info_table.cc │ ├── callee_address.h │ ├── interpreter_stub.h │ ├── callee_address.cc │ ├── util.h │ ├── slot_indexes.cc │ ├── deoptimization_runtime.h │ ├── interpreter_stub.cc │ ├── generator.cc │ ├── deoptimization_map.cc │ ├── deoptimization_map_test.cc │ └── deoptimization_map.h ├── classes │ ├── python │ │ ├── classes.pyi │ │ ├── CMakeLists.txt │ │ ├── adopt_existing_types_test.py │ │ └── classes.cc │ ├── getsetattr.h │ ├── CMakeLists.txt │ ├── util.cc │ └── class_manager.cc ├── __init__.py ├── utils │ ├── logging.cc │ ├── demangle.cc │ ├── range_test.cc │ ├── demangle_test.cc │ ├── path.h │ ├── demangle.h │ ├── status_builder_test.cc │ ├── path_test.cc │ ├── range.h │ ├── matchers.cc │ ├── keyed_intern_table_test.cc │ ├── mathutil_test.cc │ ├── mathutil.h │ ├── status_builder.h │ ├── path.cc │ ├── diffs_test.cc │ ├── no_destructor_test.cc │ ├── no_destructor.h │ ├── status_macros.h │ ├── interval_map_test.cc │ └── keyed_intern_table.h ├── arithmetic.cc ├── eval.h ├── metadata.cc ├── code_generation │ ├── prolog_epilog_insertion.h │ ├── asmjit_util.h │ ├── jit_stub_test.cc │ └── code_generator.h ├── interpreter_test.cc ├── profiler_test_util.h ├── event_counters.cc ├── code_object.cc ├── event_counters.h ├── global_intern_table.h ├── api.h ├── allocator.h └── api.cc ├── Dockerfile └── setup_python_module.sh /.dockerignore: -------------------------------------------------------------------------------- 1 | Dockerfile 2 | build/ 3 | s6/ 4 | pybind11_abseil/ 5 | -------------------------------------------------------------------------------- /src/images/attribute_access.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google-deepmind/s6/HEAD/src/images/attribute_access.png -------------------------------------------------------------------------------- /src/images/class_identifier_storage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google-deepmind/s6/HEAD/src/images/class_identifier_storage.png -------------------------------------------------------------------------------- /src/CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | We have stopped working on S6 internally. As such, this repository has been 4 | archived and we are not accepting pull requests or issues. We open-sourced the 5 | code and provided a design overview below to spur conversations within the 6 | Python community and inspire future work on improving Python. 7 | -------------------------------------------------------------------------------- /src/event_counters.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package deepmind.s6; 4 | 5 | 6 | // A single name/count pair. 7 | message EventCounterProto { 8 | // The name of the counter. 9 | string name = 1; 10 | 11 | // The value of the counter. 12 | int64 value = 2; 13 | } 14 | 15 | // A collection of EventCounters. 16 | message EventCountersProto { 17 | repeated EventCounterProto counters = 1; 18 | } 19 | -------------------------------------------------------------------------------- /src/strongjit/dis6.pyi: -------------------------------------------------------------------------------- 1 | # Copyright 2021 The s6 Authors. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http:#www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS-IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | def dis6(func: function) -> str: ... 16 | -------------------------------------------------------------------------------- /src/python/hello_world.py: -------------------------------------------------------------------------------- 1 | # Copyright 2021 The s6 Authors. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http:#www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS-IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | """Hello, S6!""" 16 | 17 | print(__doc__) 18 | -------------------------------------------------------------------------------- /src/python/type_feedback.pyi: -------------------------------------------------------------------------------- 1 | # Copyright 2021 The s6 Authors. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http:#www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS-IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | def extract_from_code_object(arg0: object) -> list: ... 16 | -------------------------------------------------------------------------------- /src/global_intern_table.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2021 The s6 Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS-IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include "global_intern_table.h" 16 | 17 | namespace deepmind::s6 { 18 | 19 | GlobalInternTable& GlobalInternTable::Instance() { 20 | static NoDestructor instance; 21 | return *instance; 22 | } 23 | 24 | } // namespace deepmind::s6 25 | -------------------------------------------------------------------------------- /src/runtime/interposer.h: -------------------------------------------------------------------------------- 1 | // Copyright 2021 The s6 Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS-IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #ifndef THIRD_PARTY_DEEPMIND_S6_RUNTIME_INTERPOSER_H_ 16 | #define THIRD_PARTY_DEEPMIND_S6_RUNTIME_INTERPOSER_H_ 17 | 18 | namespace deepmind::s6 { 19 | 20 | // Installs all known interposers. 21 | void SetUpInterposers(); 22 | 23 | } // namespace deepmind::s6 24 | 25 | #endif // THIRD_PARTY_DEEPMIND_S6_RUNTIME_INTERPOSER_H_ 26 | -------------------------------------------------------------------------------- /src/classes/python/classes.pyi: -------------------------------------------------------------------------------- 1 | # Copyright 2021 The s6 Authors. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http:#www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS-IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | def adopt(arg0: object) -> None: ... 16 | def adopt_existing_types() -> None: ... 17 | def adopt_new_types() -> None: ... 18 | def adoptable(arg0: object) -> bool: ... 19 | def class_is_valid(arg0: object) -> bool: ... 20 | def classid(arg0: object) -> int: ... 21 | def get_class_attributes(arg0: object) -> dict: ... 22 | def get_type_attributes(arg0: object) -> dict: ... 23 | def stop_adopting_new_types() -> None: ... 24 | -------------------------------------------------------------------------------- /src/classes/python/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Copyright 2021 The s6 Authors. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http:#www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS-IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | 16 | s6_pybind_extension( 17 | NAME s6_classes_python_classes 18 | OUTPUT_NAME classes 19 | SRCS classes.cc 20 | DEPS s6_classes_class s6_classes_object s6_utils_status_macros 21 | INCLUDE_DIRS ${pybind11_abseil_INCLUDE_DIRS}) 22 | 23 | s6_py_test(NAME s6_classes_python_classes_test SRCS classes_test.py) 24 | 25 | s6_py_test(NAME s6_classes_python_adopt_existing_types_test 26 | SRCS adopt_existing_types_test.py) 27 | -------------------------------------------------------------------------------- /src/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright 2021 The s6 Authors. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http:#www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS-IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | """S6 is a just-in-time compiler and profiler for Python.""" 16 | 17 | from s6.python.api import CompilationFailedError 18 | from s6.python.api import inspect 19 | from s6.python.api import jit 20 | from s6.python.api import NotCompiledError 21 | from s6.python.api import S6CodeDetail 22 | from s6.python.api import S6JitCallable 23 | __all__ = [ 24 | "CompilationFailedError", 25 | "NotCompiledError", 26 | "S6CodeDetail", 27 | "S6JitCallable", 28 | "inspect", 29 | "jit", 30 | ] 31 | -------------------------------------------------------------------------------- /src/utils/logging.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2021 The s6 Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS-IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include "utils/logging.h" 16 | 17 | #include "absl/flags/flag.h" 18 | #include "absl/synchronization/mutex.h" 19 | #include "utils/no_destructor.h" 20 | 21 | ABSL_FLAG(int, s6_vlog_level, 0, "Verbose log-level"); 22 | 23 | namespace deepmind::s6::internal { 24 | 25 | absl::Mutex ostream_mutex(absl::kConstInit); 26 | 27 | bool VLogIsEnabled(int verbose_level) { 28 | return verbose_level <= absl::GetFlag(FLAGS_s6_vlog_level); 29 | } 30 | 31 | } // namespace deepmind::s6::internal 32 | -------------------------------------------------------------------------------- /src/runtime/python_function_info_table.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2021 The s6 Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS-IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include "runtime/python_function_info_table.h" 16 | 17 | namespace deepmind::s6 { 18 | 19 | thread_local std::array 21 | PythonFunctionInfoTable::cache_; 22 | 23 | PythonFunctionInfoTable& PythonFunctionInfoTable::Instance() { 24 | static NoDestructor instance; 25 | return *instance; 26 | } 27 | 28 | } // namespace deepmind::s6 29 | -------------------------------------------------------------------------------- /src/runtime/callee_address.h: -------------------------------------------------------------------------------- 1 | // Copyright 2021 The s6 Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS-IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #ifndef THIRD_PARTY_DEEPMIND_S6_RUNTIME_CALLEE_ADDRESS_H_ 16 | #define THIRD_PARTY_DEEPMIND_S6_RUNTIME_CALLEE_ADDRESS_H_ 17 | 18 | #include "absl/status/statusor.h" 19 | #include "strongjit/callees.h" 20 | 21 | namespace deepmind::s6 { 22 | 23 | // Attempts to obtain the address of a Callee. This will return 24 | // FAILED_PRECONDITION if the symbol is not linked in. It will never return 25 | // nullptr. 26 | absl::StatusOr GetCalleeSymbolAddress(Callee callee); 27 | 28 | } // namespace deepmind::s6 29 | 30 | #endif // THIRD_PARTY_DEEPMIND_S6_RUNTIME_CALLEE_ADDRESS_H_ 31 | -------------------------------------------------------------------------------- /src/utils/demangle.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2021 The s6 Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS-IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include "utils/demangle.h" 16 | 17 | #include 18 | 19 | #include 20 | #include 21 | 22 | namespace deepmind::s6 { 23 | 24 | std::string Demangle(const char* mangled) { 25 | int status = 0; 26 | char* demangled_c_str = 27 | abi::__cxa_demangle(mangled, nullptr, nullptr, &status); 28 | 29 | if (status == 0 && demangled_c_str != nullptr) { 30 | std::string demangled(demangled_c_str); 31 | std::free(demangled_c_str); 32 | return demangled; 33 | } else { 34 | return std::string(mangled); 35 | } 36 | } 37 | 38 | } // namespace deepmind::s6 39 | -------------------------------------------------------------------------------- /src/arithmetic.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2021 The s6 Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS-IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include "arithmetic.h" 16 | 17 | #include 18 | 19 | #include "absl/numeric/int128.h" 20 | 21 | namespace deepmind::s6 { 22 | 23 | namespace arithmetic { 24 | 25 | static_assert(static_cast(absl::uint128(-1LL)) == -1LL, 26 | "S6 will not work with this compiler, because absl::uint128" 27 | "does not round trip properly"); 28 | 29 | Result ResultFromUint128(absl::uint128 from) { 30 | Result result; 31 | result.result = static_cast(from); 32 | result.overflowed = from != absl::uint128(result.result); 33 | return result; 34 | } 35 | 36 | } // namespace arithmetic 37 | 38 | } // namespace deepmind::s6 39 | -------------------------------------------------------------------------------- /src/strongjit/ingestion_handlers.h: -------------------------------------------------------------------------------- 1 | // Copyright 2021 The s6 Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS-IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #ifndef THIRD_PARTY_DEEPMIND_S6_STRONGJIT_INGESTION_HANDLERS_H_ 16 | #define THIRD_PARTY_DEEPMIND_S6_STRONGJIT_INGESTION_HANDLERS_H_ 17 | 18 | #include 19 | 20 | #include "absl/status/statusor.h" 21 | #include "strongjit/ingestion.h" 22 | 23 | namespace deepmind::s6 { 24 | 25 | // Returns the Analyze function for an opcode. 26 | absl::StatusOr GetAnalyzeFunction(int32_t opcode); 27 | 28 | // Returns the Translate function for an opcode. 29 | absl::StatusOr GetTranslateFunction(int32_t opcode); 30 | 31 | } // namespace deepmind::s6 32 | 33 | #endif // THIRD_PARTY_DEEPMIND_S6_STRONGJIT_INGESTION_HANDLERS_H_ 34 | -------------------------------------------------------------------------------- /src/utils/range_test.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2021 The s6 Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS-IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include "utils/range.h" 16 | 17 | #include 18 | #include 19 | 20 | #include "gmock/gmock.h" 21 | #include "gtest/gtest.h" 22 | 23 | namespace deepmind::s6 { 24 | namespace { 25 | 26 | using ::testing::ElementsAre; 27 | 28 | TEST(IteratorRange, ArrayMakeRange) { 29 | int v[] = {2, 3, 5, 7, 11, 13}; 30 | EXPECT_THAT(MakeRange(&v[1], &v[4]), ElementsAre(3, 5, 7)); 31 | } 32 | 33 | TEST(IteratorRange, VectorMakeRange) { 34 | std::vector v = {2, 3, 5, 7, 11, 13}; 35 | EXPECT_THAT(MakeRange(std::next(v.begin()), std::prev(v.end())), 36 | ElementsAre(3, 5, 7, 11)); 37 | } 38 | 39 | } // namespace 40 | } // namespace deepmind::s6 41 | -------------------------------------------------------------------------------- /src/classes/getsetattr.h: -------------------------------------------------------------------------------- 1 | // Copyright 2021 The s6 Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS-IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #ifndef THIRD_PARTY_DEEPMIND_S6_CLASSES_GETSETATTR_H_ 16 | #define THIRD_PARTY_DEEPMIND_S6_CLASSES_GETSETATTR_H_ 17 | 18 | #include 19 | 20 | namespace deepmind::s6 { 21 | 22 | // A version of PyObject_GenericSetAttr that uses Classes to speed up its 23 | // implementation. 24 | int GenericSetAttrUsingClasses(PyObject* obj, PyObject* name, PyObject* value); 25 | 26 | // A version of PyDict_SetItem, used for stores to global dicts. It keeps 27 | // the class of the globals dict up to date. 28 | int SetAttrForGlobalsDict(PyObject* dict, PyObject* name, PyObject* value); 29 | 30 | } // namespace deepmind::s6 31 | 32 | #endif // THIRD_PARTY_DEEPMIND_S6_CLASSES_GETSETATTR_H_ 33 | -------------------------------------------------------------------------------- /src/utils/demangle_test.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2021 The s6 Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS-IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include "utils/demangle.h" 16 | 17 | #include "gmock/gmock.h" 18 | #include "gtest/gtest.h" 19 | 20 | namespace deepmind::s6 { 21 | namespace { 22 | 23 | TEST(NoDestructorTest, SimpleSymbol) { EXPECT_EQ(Demangle("main"), "main"); } 24 | 25 | TEST(NoDestructorTest, Function) { EXPECT_EQ(Demangle("_Z3barv"), "bar()"); } 26 | 27 | TEST(NoDestructorTest, FunctionWithArgs) { 28 | EXPECT_EQ(Demangle("_Z3bazifdPv"), "baz(int, float, double, void*)"); 29 | } 30 | 31 | TEST(NoDestructorTest, FunctionWithReferenceArg) { 32 | EXPECT_EQ(Demangle("_Z4quuxRKSsPS_"), 33 | "quux(std::string const&, std::string const*)"); 34 | } 35 | 36 | } // namespace 37 | } // namespace deepmind::s6 38 | -------------------------------------------------------------------------------- /src/utils/path.h: -------------------------------------------------------------------------------- 1 | // Copyright 2021 The s6 Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS-IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #ifndef THIRD_PARTY_DEEPMIND_S6_UTILS_PATH_H_ 16 | #define THIRD_PARTY_DEEPMIND_S6_UTILS_PATH_H_ 17 | 18 | #include 19 | #include 20 | #include 21 | #include 22 | 23 | #include "absl/strings/string_view.h" 24 | 25 | namespace deepmind::s6::file { 26 | 27 | std::string JoinPathImpl(std::initializer_list fragments); 28 | 29 | template 30 | std::string JoinPath(Ts&&... ts) { 31 | return JoinPathImpl(std::initializer_list{ts...}); 32 | } 33 | 34 | absl::string_view Basename(absl::string_view path); 35 | 36 | } // namespace deepmind::s6::file 37 | 38 | #endif // THIRD_PARTY_DEEPMIND_S6_UTILS_PATH_H_ 39 | -------------------------------------------------------------------------------- /src/eval.h: -------------------------------------------------------------------------------- 1 | // Copyright 2021 The s6 Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS-IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #ifndef THIRD_PARTY_DEEPMIND_S6_EVAL_H_ 16 | #define THIRD_PARTY_DEEPMIND_S6_EVAL_H_ 17 | 18 | #include 19 | #include 20 | 21 | namespace deepmind::s6 { 22 | 23 | // A frame evaluation function that evaluates a frame by compiling it to 24 | // strongJIT then evaluating it with the S6 evaluator. See `EvaluateFunction`. 25 | PyObject* S6EvalFrameWithEvaluator(PyFrameObject* frame, int throwflag); 26 | 27 | // Returns a frame evaluation function. The returned function depends on if 28 | // fastjit or strongjit is enabled, and if any bisection commands are enabled. 29 | _PyFrameEvalFunction GetFrameEvalFunction(); 30 | 31 | } // namespace deepmind::s6 32 | 33 | #endif // THIRD_PARTY_DEEPMIND_S6_EVAL_H_ 34 | -------------------------------------------------------------------------------- /src/runtime/interpreter_stub.h: -------------------------------------------------------------------------------- 1 | // Copyright 2021 The s6 Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS-IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #ifndef THIRD_PARTY_DEEPMIND_S6_RUNTIME_INTERPRETER_STUB_H_ 16 | #define THIRD_PARTY_DEEPMIND_S6_RUNTIME_INTERPRETER_STUB_H_ 17 | 18 | #include 19 | 20 | #include 21 | 22 | namespace deepmind::s6 { 23 | 24 | // Accepts the Strongjit fast calling convention and forwards directly to 25 | // the interpreter. 26 | PyObject* StrongjitInterpreterStub(PyObject* pyfunc_object, ...); 27 | 28 | // As StrongjitInterpreterStub, but accepts an array of arguments. 29 | PyObject* StrongjitInterpreterStubArrayArgs(PyObject* pyfunc_object, int64_t n, 30 | PyObject** args); 31 | 32 | } // namespace deepmind::s6 33 | 34 | #endif // THIRD_PARTY_DEEPMIND_S6_RUNTIME_INTERPRETER_STUB_H_ 35 | -------------------------------------------------------------------------------- /src/utils/demangle.h: -------------------------------------------------------------------------------- 1 | // Copyright 2021 The s6 Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS-IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | // Function for demangling symbol names. 16 | // Calls a demangler API defined by C++ Itanium ABI. 17 | // 18 | // Reference: https://itanium-cxx-abi.github.io/cxx-abi/abi.html#demangler 19 | 20 | #ifndef THIRD_PARTY_DEEPMIND_S6_UTILS_DEMANGLE_H_ 21 | #define THIRD_PARTY_DEEPMIND_S6_UTILS_DEMANGLE_H_ 22 | 23 | #include 24 | 25 | namespace deepmind::s6 { 26 | 27 | // Demangle a mangled symbol name and return the demangled name. 28 | // REQUIRES: mangled is not null. 29 | // Note: This function uses __cxa_demangle which is prone to vulnerabilities and 30 | // should only be used on known valid input. 31 | std::string Demangle(const char* mangled); 32 | 33 | } // namespace deepmind::s6 34 | 35 | #endif // THIRD_PARTY_DEEPMIND_S6_UTILS_DEMANGLE_H_ 36 | -------------------------------------------------------------------------------- /src/classes/python/adopt_existing_types_test.py: -------------------------------------------------------------------------------- 1 | # Copyright 2021 The s6 Authors. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http:#www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS-IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | """Tests classes.adopt_existing_types(). 16 | 17 | This function, once called, cannot be undone, so this test is isolated from the 18 | other adoption tests in classes_test.py. 19 | """ 20 | 21 | from absl.testing import absltest 22 | import numpy 23 | 24 | from s6.classes.python import classes 25 | 26 | classes.adopt_existing_types() 27 | 28 | 29 | class AdoptExistingTypesTest(absltest.TestCase): 30 | 31 | def assertHasClass(self, c): 32 | return self.assertNotEqual(classes.classid(c), 0) 33 | 34 | def test_existing_types_adopted(self): 35 | self.assertHasClass(42) 36 | self.assertHasClass(2.0) 37 | self.assertHasClass("str") 38 | self.assertHasClass(True) 39 | self.assertHasClass(numpy.ndarray([])) 40 | 41 | 42 | if __name__ == "__main__": 43 | absltest.main() 44 | -------------------------------------------------------------------------------- /src/utils/status_builder_test.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2021 The s6 Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS-IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include "utils/status_builder.h" 16 | 17 | #include "absl/status/status.h" 18 | #include "gmock/gmock.h" 19 | #include "gtest/gtest.h" 20 | 21 | namespace deepmind::s6 { 22 | namespace { 23 | TEST(StatusBuilder, EmptyMessage) { 24 | absl::Status status = StatusBuilder(absl::StatusCode::kOk); 25 | EXPECT_TRUE(status.ok()); 26 | EXPECT_TRUE(status.message().empty()); 27 | } 28 | 29 | TEST(StatusBuilder, NoEmptyMessage) { 30 | absl::Status status = StatusBuilder(absl::StatusCode::kUnknown) 31 | << "These are not the droids you're looking for. " 32 | << "Move along."; 33 | EXPECT_EQ(status.code(), absl::StatusCode::kUnknown); 34 | EXPECT_EQ(status.message(), 35 | "These are not the droids you're looking for. Move along."); 36 | } 37 | } // namespace 38 | } // namespace deepmind::s6 39 | -------------------------------------------------------------------------------- /src/runtime/callee_address.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2021 The s6 Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS-IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include "runtime/callee_address.h" 16 | 17 | #include 18 | 19 | #include "absl/status/status.h" 20 | #include "absl/status/statusor.h" 21 | #include "classes/getsetattr.h" 22 | #include "runtime/runtime.h" 23 | 24 | namespace deepmind::s6 { 25 | 26 | absl::StatusOr GetCalleeSymbolAddress(Callee callee) { 27 | switch (callee) { 28 | #define CALLEE(symbol) \ 29 | case Callee::k##symbol: \ 30 | return reinterpret_cast(&symbol); 31 | #define CPP_CALLEE(namespace, symbol) \ 32 | case Callee::k##symbol: \ 33 | return reinterpret_cast(&namespace ::symbol); 34 | // callees.inc undefs CALLEE and CPP_CALLEE. 35 | #include "strongjit/callees.inc" 36 | default: 37 | return absl::FailedPreconditionError( 38 | absl::StrCat("Symbol not linked: ", ToString(callee))); 39 | } 40 | } 41 | 42 | } // namespace deepmind::s6 43 | -------------------------------------------------------------------------------- /src/python/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Copyright 2021 The s6 Authors. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http:#www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS-IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | 16 | s6_pybind_extension( 17 | NAME s6_python_api 18 | OUTPUT_NAME api 19 | SRCS api.cc 20 | DEPS absl::status 21 | s6_api 22 | s6_code_object 23 | s6_interpreter 24 | s6_metadata 25 | s6_oracle 26 | s6_strongjit_formatter 27 | s6_strongjit_ingestion 28 | INCLUDE_DIRS ${pybind11_abseil_INCLUDE_DIRS}) 29 | 30 | s6_py_test(NAME s6_python_api_test SRCS api_test.py) 31 | 32 | s6_pybind_extension( 33 | NAME s6_python_type_feedback 34 | OUTPUT_NAME type_feedback 35 | SRCS type_feedback.cc 36 | DEPS absl::inlined_vector 37 | absl::status 38 | s6_classes_class 39 | s6_classes_object 40 | s6_metadata 41 | s6_type_feedback 42 | INCLUDE_DIRS ${pybind11_abseil_INCLUDE_DIRS}) 43 | 44 | s6_py_test(NAME s6_python_type_feedback_test SRCS type_feedback_test.py) 45 | 46 | s6_py_test(NAME s6_python_jit_test SRCS jit_test.py) 47 | -------------------------------------------------------------------------------- /src/strongjit/parser.h: -------------------------------------------------------------------------------- 1 | // Copyright 2021 The s6 Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS-IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #ifndef THIRD_PARTY_DEEPMIND_S6_STRONGJIT_PARSER_H_ 16 | #define THIRD_PARTY_DEEPMIND_S6_STRONGJIT_PARSER_H_ 17 | 18 | #include 19 | 20 | #include "absl/status/statusor.h" 21 | #include "absl/strings/string_view.h" 22 | #include "classes/class_manager.h" 23 | #include "strongjit/base.h" 24 | 25 | namespace deepmind::s6 { 26 | 27 | class OptimizationInfoEntry; 28 | 29 | // Parses an Instruction from `str` and returns it. It will be created inside 30 | // `f`. 31 | absl::StatusOr ParseInstruction( 32 | absl::string_view str, Function* f, 33 | const ClassManager& mgr = ClassManager::Instance()); 34 | 35 | // Parses a Function from `str` and returns it. 36 | absl::StatusOr ParseFunction( 37 | absl::string_view str, const ClassManager& mgr = ClassManager::Instance()); 38 | 39 | } // namespace deepmind::s6 40 | 41 | #endif // THIRD_PARTY_DEEPMIND_S6_STRONGJIT_PARSER_H_ 42 | -------------------------------------------------------------------------------- /src/strongjit/cursor.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2021 The s6 Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS-IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include "strongjit/cursor.h" 16 | 17 | #include "strongjit/function.h" 18 | 19 | namespace deepmind::s6 { 20 | 21 | void Cursor::Update() { 22 | S6_CHECK(!Finished()); 23 | Block* block = GetBlock(); 24 | Function* function = GetFunction(); 25 | 26 | next_ = curr_; 27 | next_valid_ = true; 28 | ++next_; 29 | if (next_ == block->end()) { 30 | auto next_block = ++block->GetIterator(); 31 | if (next_block != function->end()) { 32 | next_ = next_block->begin(); 33 | } else { 34 | next_valid_ = false; 35 | } 36 | } 37 | 38 | prev_ = curr_; 39 | prev_valid_ = true; 40 | if (prev_ != block->begin()) { 41 | --prev_; 42 | } else { 43 | auto block_iter = block->GetIterator(); 44 | if (block_iter != function->begin()) { 45 | prev_ = --std::end(*--block_iter); 46 | } else { 47 | prev_valid_ = false; 48 | } 49 | } 50 | } 51 | 52 | } // namespace deepmind::s6 53 | -------------------------------------------------------------------------------- /src/runtime/util.h: -------------------------------------------------------------------------------- 1 | // Copyright 2021 The s6 Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS-IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #ifndef THIRD_PARTY_DEEPMIND_S6_RUNTIME_UTIL_H_ 16 | #define THIRD_PARTY_DEEPMIND_S6_RUNTIME_UTIL_H_ 17 | 18 | #include 19 | 20 | #include 21 | 22 | namespace deepmind::s6 { 23 | 24 | // The type of an address within generated code. 25 | using ProgramAddress = uint64_t; 26 | 27 | // The implementation of PyDictKeysObject, which is private to CPython. 28 | struct _PyDictKeysObject { 29 | Py_ssize_t dk_refcnt; 30 | Py_ssize_t dk_size; 31 | void* dk_lookup; 32 | Py_ssize_t dk_usable; 33 | Py_ssize_t dk_nentries; 34 | char dk_indices[]; 35 | }; 36 | 37 | // An entry in a combined dictionary. This is an implementation detail of the 38 | // dict implementation that we copy here so the code generator can use it. 39 | struct _PyDictKeyEntry { 40 | PyObject* hash; 41 | PyObject* key; 42 | PyObject* value; 43 | }; 44 | 45 | } // namespace deepmind::s6 46 | 47 | #endif // THIRD_PARTY_DEEPMIND_S6_RUNTIME_UTIL_H_ 48 | -------------------------------------------------------------------------------- /src/strongjit/base.h: -------------------------------------------------------------------------------- 1 | // Copyright 2021 The s6 Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS-IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #ifndef THIRD_PARTY_DEEPMIND_S6_STRONGJIT_BASE_H_ 16 | #define THIRD_PARTY_DEEPMIND_S6_STRONGJIT_BASE_H_ 17 | 18 | #include 19 | #include 20 | #include 21 | 22 | #include "absl/algorithm/container.h" 23 | #include "absl/container/flat_hash_map.h" 24 | #include "absl/container/inlined_vector.h" 25 | #include "absl/status/status.h" 26 | #include "absl/status/statusor.h" 27 | #include "absl/strings/string_view.h" 28 | #include "absl/types/optional.h" 29 | #include "absl/types/span.h" 30 | #include "strongjit/block.h" 31 | #include "strongjit/cursor.h" 32 | #include "strongjit/instruction.h" 33 | #include "strongjit/util.h" 34 | #include "strongjit/value.h" 35 | #include "utils/intrusive_list.h" 36 | 37 | namespace deepmind::s6 { 38 | 39 | class Block; 40 | class Function; 41 | class Instruction; 42 | class TerminatorInst; 43 | 44 | } // namespace deepmind::s6 45 | 46 | #endif // THIRD_PARTY_DEEPMIND_S6_STRONGJIT_BASE_H_ 47 | -------------------------------------------------------------------------------- /src/metadata.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2021 The s6 Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS-IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include "metadata.h" 16 | 17 | #include "utils/logging.h" 18 | namespace deepmind::s6 { 19 | 20 | Metadata* Metadata::Get(PyCodeObject* co) { 21 | int index = GetPyCodeExtraOffset(); 22 | 23 | void* meta = nullptr; 24 | S6_CHECK_EQ(_PyCode_GetExtra(reinterpret_cast(co), index, &meta), 25 | 0); 26 | if (meta == nullptr) { 27 | meta = new Metadata(co); 28 | S6_CHECK_EQ(_PyCode_SetExtra(reinterpret_cast(co), index, meta), 29 | 0); 30 | } 31 | return static_cast(meta); 32 | } 33 | 34 | void Metadata::FreeMetadata(void* meta) { 35 | if (meta) { 36 | delete static_cast(meta); 37 | } 38 | } 39 | 40 | int Metadata::GetPyCodeExtraOffset() { 41 | // TODO: Request an index in s6::GlobalEnable. 42 | static const int kIndex = _PyEval_RequestCodeExtraIndex(FreeMetadata); 43 | S6_DCHECK_GE(kIndex, 0); 44 | return kIndex; 45 | } 46 | 47 | } // namespace deepmind::s6 48 | -------------------------------------------------------------------------------- /src/strongjit/value_map.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2021 The s6 Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS-IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include "strongjit/value_map.h" 16 | 17 | #include 18 | 19 | #include "strongjit/function.h" 20 | #include "utils/logging.h" 21 | 22 | namespace deepmind::s6 { 23 | ValueMap::ValueMap(const Function& f) { 24 | if (S6_VLOG_IS_ON(1)) { 25 | // We need a value numbering if we're expected to log values. 26 | value_numbering_ = ComputeValueNumbering(f); 27 | } 28 | } 29 | 30 | void ValueMap::Set(const Value* v, int64_t value) { 31 | EVLOG(2) << absl::StrFormat(" %%%d <- %16d %#16x", value_numbering_->at(v), 32 | value, value); 33 | values_[v] = value; 34 | } 35 | 36 | int64_t ValueMap::Get(const Value* v) const { 37 | S6_CHECK(values_.contains(v)) 38 | << "Requested value " << value_numbering_->at(v); 39 | int64_t value = values_.at(v); 40 | EVLOG(3) << absl::StrFormat(" %%%d -> %16d %#16x", value_numbering_->at(v), 41 | value, value); 42 | return value; 43 | } 44 | } // namespace deepmind::s6 45 | -------------------------------------------------------------------------------- /src/strongjit/deoptimization.h: -------------------------------------------------------------------------------- 1 | // Copyright 2021 The s6 Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS-IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #ifndef THIRD_PARTY_DEEPMIND_S6_STRONGJIT_DEOPTIMIZATION_H_ 16 | #define THIRD_PARTY_DEEPMIND_S6_STRONGJIT_DEOPTIMIZATION_H_ 17 | 18 | #include 19 | 20 | #include "absl/status/status.h" 21 | #include "strongjit/base.h" 22 | #include "strongjit/util.h" 23 | 24 | namespace deepmind::s6 { 25 | 26 | // Identifies blocks that are only branched to by 'deoptimize' BrInst edges, 27 | // and marks them as 'deoptimized'. 28 | absl::Status MarkDeoptimizedBlocks(Function& f); 29 | 30 | // Converts deoptimized edges in non-deoptimized blocks to deoptimize_if 31 | // instructions. Deoptimized blocks are moved to the end of the function. 32 | absl::Status RewriteFunctionForDeoptimization(Function& f); 33 | 34 | // Testing only: stress tests deoptimization by inserting random deoptimization 35 | // edges. 36 | absl::Status StressTestByDeoptimizingRandomly( 37 | Function& f, std::default_random_engine& rng, 38 | float probability_to_deoptimize_edge = 0.1f); 39 | 40 | } // namespace deepmind::s6 41 | 42 | #endif // THIRD_PARTY_DEEPMIND_S6_STRONGJIT_DEOPTIMIZATION_H_ 43 | -------------------------------------------------------------------------------- /src/strongjit/callees.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2021 The s6 Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS-IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include "strongjit/callees.h" 16 | 17 | #include "core_util.h" 18 | 19 | namespace deepmind::s6 { 20 | 21 | absl::optional CalleeInfo::Get(Callee callee) { 22 | switch (callee) { 23 | #define CALLEE(symbol) \ 24 | case Callee::k##symbol: \ 25 | return absl::nullopt; 26 | #define CPP_CALLEE(namespace, symbol) \ 27 | case Callee::k##symbol: \ 28 | return absl::nullopt; 29 | #define CALLEE_INFO(symbol, info) \ 30 | case Callee::k##symbol: \ 31 | return info; 32 | #define CPP_CALLEE_INFO(namespace, symbol, info) \ 33 | case Callee::k##symbol: \ 34 | return info; 35 | #define CINFO CalleeInfo 36 | #define NEWREF true 37 | #define PLAIN false 38 | #define NOTNULL Nullness::kNotNull 39 | #define MNULL Nullness::kMaybeNull 40 | #define STOLEN ArgInfo(Nullness::kNotNull, true) 41 | #define STOLEN_MNULL ArgInfo(Nullness::kMaybeNull, true) 42 | 43 | // callees.inc undefs all the previous macros. 44 | #include "strongjit/callees.inc" 45 | } 46 | S6_UNREACHABLE(); 47 | } 48 | 49 | } // namespace deepmind::s6 50 | -------------------------------------------------------------------------------- /src/strongjit/optimize_cfg.h: -------------------------------------------------------------------------------- 1 | // Copyright 2021 The s6 Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS-IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #ifndef THIRD_PARTY_DEEPMIND_S6_STRONGJIT_OPTIMIZE_CFG_H_ 16 | #define THIRD_PARTY_DEEPMIND_S6_STRONGJIT_OPTIMIZE_CFG_H_ 17 | 18 | #include 19 | 20 | #include 21 | 22 | #include "absl/status/status.h" 23 | #include "strongjit/base.h" 24 | #include "strongjit/instructions.h" 25 | #include "strongjit/optimizer_util.h" 26 | 27 | namespace deepmind::s6 { 28 | 29 | // Optimizes JmpInsts to targets with a single predecessor by removing the 30 | // JmpInst and splicing the target. 31 | class EliminateTrivialJumpsPattern 32 | : public PatternT { 33 | public: 34 | static absl::Status Apply(JmpInst* jmp, Rewriter& rewriter); 35 | }; 36 | 37 | // Identifies BlockArguments that are unused and removes them. 38 | class EliminateUnusedBlockArgumentsPattern 39 | : public PatternT { 40 | public: 41 | static absl::Status Apply(Block* block, Rewriter& rewriter); 42 | }; 43 | 44 | } // namespace deepmind::s6 45 | 46 | #endif // THIRD_PARTY_DEEPMIND_S6_STRONGJIT_OPTIMIZE_CFG_H_ 47 | -------------------------------------------------------------------------------- /src/strongjit/string_table.h: -------------------------------------------------------------------------------- 1 | // Copyright 2021 The s6 Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS-IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #ifndef THIRD_PARTY_DEEPMIND_S6_STRONGJIT_STRING_TABLE_H_ 16 | #define THIRD_PARTY_DEEPMIND_S6_STRONGJIT_STRING_TABLE_H_ 17 | 18 | #include 19 | 20 | #include "absl/strings/string_view.h" 21 | #include "utils/keyed_intern_table.h" 22 | 23 | namespace deepmind::s6 { 24 | 25 | class StringTable { 26 | public: 27 | // The type of an interned string ID in the function's string table. 28 | using key_type = uint16_t; 29 | 30 | StringTable() : string_table_() {} 31 | 32 | // String tables can't be copied. 33 | StringTable(const StringTable&) = delete; 34 | StringTable& operator=(const StringTable&) = delete; 35 | 36 | ~StringTable() = default; 37 | 38 | // Interns a string in this function's string table. 39 | key_type InternString(absl::string_view s) { 40 | return string_table_.Insert(s).key; 41 | } 42 | 43 | // Returns the string content of an InternedString. 44 | absl::string_view GetInternedString(key_type id) const { 45 | return string_table_.ToStringView(id); 46 | } 47 | 48 | private: 49 | KeyedInternTable string_table_; 50 | }; 51 | 52 | } // namespace deepmind::s6 53 | 54 | #endif // THIRD_PARTY_DEEPMIND_S6_STRONGJIT_STRING_TABLE_H_ 55 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ubuntu:20.04 2 | RUN apt-get update 3 | 4 | # Install some required dependencies. 5 | RUN DEBIAN_FRONTEND=noninteractive TZ=Etc/UTC apt-get -y install tzdata 6 | RUN apt-get update && apt-get install -y \ 7 | zlib1g zlib1g-dev libsqlite3-dev libssl-dev git wget cmake build-essential \ 8 | ninja-build libprotobuf-dev protobuf-compiler lsb-release \ 9 | software-properties-common dh-autoreconf && \ 10 | rm -rf /var/lib/apt/lists/* 11 | 12 | RUN wget https://apt.llvm.org/llvm.sh && \ 13 | bash llvm.sh 13 && \ 14 | ln -s /usr/bin/clang-13 /usr/bin/clang && \ 15 | ln -s /usr/bin/clang++-13 /usr/bin/clang++ 16 | 17 | ENV CC /usr/bin/clang 18 | ENV CXX /usr/bin/clang++ 19 | 20 | # S6 requires CPython to have been built with frame-pointers enabled. 21 | ENV CFLAGS=-fno-omit-frame-pointer 22 | ENV CXXFLAGS=-fno-omit-frame-pointer 23 | 24 | # Install python 3.7 from source. 25 | ARG PYTHON_SRC="/open_s6/python_src" 26 | ENV PYTHON_INSTALL="/open_s6/python_3_7" 27 | WORKDIR /open_s6 28 | RUN mkdir -p $PYTHON_INSTALL $PYTHON_SRC && \ 29 | git clone https://github.com/python/cpython.git $PYTHON_SRC && \ 30 | cd $PYTHON_SRC && \ 31 | git checkout 3.7 && \ 32 | ./configure -enable-shared \ 33 | --prefix=$PYTHON_INSTALL LDFLAGS=-Wl,-rpath=$PYTHON_INSTALL/lib && \ 34 | make -j install && cd /open_s6 && rm -rf $PYTHON_SRC 35 | 36 | ENV PATH="${PYTHON_INSTALL}/bin:${PATH}" 37 | RUN python3.7 -m pip install --upgrade pip absl-py numpy ipython jupyterlab 38 | 39 | # Copy all local files to the container. 40 | RUN mkdir s6 41 | COPY . s6/ 42 | 43 | # Build the project. 44 | WORKDIR /open_s6/s6 45 | RUN ./build.sh `pwd`/src `pwd`/build -GNinja -DPython_ROOT_DIR=$PYTHON_INSTALL 46 | 47 | # Setup S6's python module layout. 48 | ENV PYTHONPATH="/open_s6/python_modules/" 49 | RUN bash ./setup_python_module.sh `pwd`/src `pwd`/build "${PYTHONPATH}" 50 | -------------------------------------------------------------------------------- /src/classes/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Copyright 2021 The s6 Authors. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http:#www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS-IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | 16 | s6_cc_library( 17 | NAME s6_classes_class 18 | SRCS attribute.cc class.cc class_manager.cc 19 | HDRS attribute.h class.h class_manager.h 20 | DEPS Python::Python 21 | absl::base 22 | absl::flat_hash_map 23 | absl::status 24 | absl::statusor 25 | absl::strings 26 | absl::synchronization 27 | s6_classes_util 28 | s6_core_util 29 | s6_event_counters 30 | s6_utils_intrusive_list 31 | s6_utils_keyed_intern_table 32 | s6_utils_no_destructor 33 | s6_utils_status_macros) 34 | 35 | s6_cc_test( 36 | NAME s6_classes_class_test 37 | SRCS class_test.cc 38 | DEPS GTest::gmock 39 | GTest::gtest_main 40 | Python::Python 41 | absl::hash 42 | s6_classes_class 43 | s6_utils_status_macros) 44 | 45 | s6_cc_library( 46 | NAME s6_classes_object 47 | SRCS getsetattr.cc object.cc 48 | HDRS getsetattr.h object.h 49 | DEPS Python::Python 50 | absl::status 51 | s6_classes_class 52 | s6_classes_util 53 | s6_event_counters 54 | s6_utils_status_macros) 55 | 56 | s6_cc_library( 57 | NAME s6_classes_util 58 | SRCS util.cc 59 | HDRS util.h 60 | DEPS Python::Python absl::base) 61 | 62 | add_subdirectory(python) 63 | -------------------------------------------------------------------------------- /src/runtime/slot_indexes.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2021 The s6 Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS-IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include "runtime/slot_indexes.h" 16 | 17 | #include 18 | 19 | #include "strongjit/base.h" 20 | 21 | namespace deepmind::s6 { 22 | 23 | SlotIndexes::SlotIndexes(const Function& f) { 24 | // TODO: Should we use a depth-first ordering here? 25 | 26 | // Note that we assign values every other slot, so all values have an even 27 | // slot. This leaves room for copies to be inserted between value slots. 28 | int64_t index = 0; 29 | auto append = [&](const Value* v) { 30 | slots_[v] = index; 31 | values_.push_back(v); 32 | values_.push_back(nullptr); 33 | index += 2; 34 | }; 35 | 36 | int64_t block_index = 0; 37 | // Try to ensure we don't re-reserve during allocation; this was observed to 38 | // be costly in profiling. 39 | values_.reserve(f.capacity() * 2); 40 | slots_.reserve(f.capacity() * 2); 41 | 42 | for (const Block& b : f) { 43 | int64_t start = index; 44 | append(&b); 45 | for (const BlockArgument* arg : b.block_arguments()) { 46 | append(arg); 47 | } 48 | for (const Instruction& inst : b) { 49 | append(&inst); 50 | } 51 | block_info_.push_back({&b, block_index++, start, index}); 52 | block_info_map_[&b] = block_info_.back(); 53 | } 54 | } 55 | 56 | } // namespace deepmind::s6 57 | -------------------------------------------------------------------------------- /src/utils/path_test.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2021 The s6 Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS-IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include "utils/path.h" 16 | 17 | #include "gmock/gmock.h" 18 | #include "gtest/gtest.h" 19 | 20 | namespace deepmind::s6::file { 21 | namespace { 22 | 23 | TEST(PathTest, JoinEmptyListIsEmpty) { // clang-format: force newline 24 | EXPECT_EQ(JoinPath(), ""); 25 | } 26 | 27 | TEST(PathTest, JoinWithSinglePath) { // clang-format: force newline 28 | EXPECT_EQ(JoinPath("path"), "path"); 29 | } 30 | 31 | TEST(PathTest, JoinWithLeadingSlashes) { 32 | EXPECT_EQ(JoinPath("/a", "/b"), "/a/b"); 33 | } 34 | 35 | TEST(PathTest, JoinWithTrailingSlashes) { 36 | EXPECT_EQ(JoinPath("a/", "b/"), "a/b/"); 37 | } 38 | 39 | TEST(PathTest, JoinWithLeadingAndTrailingSlashes) { 40 | EXPECT_EQ(JoinPath("/a/", "/b/"), "/a/b/"); 41 | } 42 | 43 | TEST(PathTest, JoinWithNoSlashes) { 44 | EXPECT_EQ(JoinPath("a", "b", "c"), "a/b/c"); 45 | } 46 | 47 | TEST(PathTest, BasenameWithNoSlashes) { // clang-format: force newline 48 | EXPECT_EQ(Basename("a"), "a"); 49 | } 50 | 51 | TEST(PathTest, BasenameWithSingleSlash) { // clang-format: force newline 52 | EXPECT_EQ(Basename("a/b"), "b"); 53 | } 54 | 55 | TEST(PathTest, BasenameWithMultipleSlashes) { // clang-format: force newline 56 | EXPECT_EQ(Basename("a/b/c"), "c"); 57 | } 58 | 59 | } // namespace 60 | } // namespace deepmind::s6::file 61 | -------------------------------------------------------------------------------- /setup_python_module.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Copyright 2021 The s6 Authors. 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http:#www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS-IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | # Link S6 components into a directory that can be added to PYTHONPATH. 18 | # 19 | # Usage: 20 | # setup_python_module.py SRC_DIR BUILD_DIR MODULE_DIR 21 | # 22 | # Where: 23 | # SRC_DIR is the absolute path to S6 source. 24 | # BUILD_DIR is the absolute path to an S6 build. 25 | # MODULE_DIR will be added to the environment variable PYTHONPATH. 26 | 27 | readonly SRC_DIR=${1} 28 | readonly BUILD_DIR=${2} 29 | readonly MODULE_DIR=${3} 30 | 31 | mkdir -p "${MODULE_DIR}"/s6 32 | mkdir -p "${MODULE_DIR}"/s6/classes/python 33 | mkdir -p "${MODULE_DIR}"/s6/python 34 | mkdir -p "${MODULE_DIR}"/s6/strongjit 35 | mkdir -p "${MODULE_DIR}"/pybind11_abseil 36 | ln -s "${SRC_DIR}"/__init__.py "${MODULE_DIR}"/s6/ 37 | ln -s "${BUILD_DIR}"/classes/python/classes.so "${MODULE_DIR}"/s6/classes/python/ 38 | ln -s "${BUILD_DIR}"/python/api.so "${MODULE_DIR}"/s6/python/ 39 | ln -s "${BUILD_DIR}"/python/type_feedback.so "${MODULE_DIR}"/s6/python/ 40 | ln -s "${BUILD_DIR}"/strongjit/dis6.so "${MODULE_DIR}"/s6/strongjit/ 41 | ln -s "${BUILD_DIR}"/External/pybind11_abseil/lib/status.so "${MODULE_DIR}"/pybind11_abseil 42 | touch "${MODULE_DIR}"/pybind11_abseil/__init__.py 43 | touch "${MODULE_DIR}"/s6/classes/python/__init__.py 44 | touch "${MODULE_DIR}"/s6/python/__init__.py 45 | touch "${MODULE_DIR}"/s6/strongjit/__init__.py 46 | -------------------------------------------------------------------------------- /src/code_generation/prolog_epilog_insertion.h: -------------------------------------------------------------------------------- 1 | // Copyright 2021 The s6 Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS-IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #ifndef THIRD_PARTY_DEEPMIND_S6_CODE_GENERATION_PROLOG_EPILOG_INSERTION_H_ 16 | #define THIRD_PARTY_DEEPMIND_S6_CODE_GENERATION_PROLOG_EPILOG_INSERTION_H_ 17 | 18 | #include "absl/status/status.h" 19 | #include "code_generation/code_generation_context.h" 20 | 21 | namespace deepmind::s6 { 22 | 23 | // Generates code for the function prolog. This saves callee-saved registers 24 | // and sets up the stack frame. 25 | // 26 | // Calls ctx.BindPyFrameEntryPoint() and (optionally) ctx.BindFastEntryPoint(). 27 | absl::Status GenerateProlog(CodeGenerationContext& ctx); 28 | 29 | // Generates code to clean up the stack frame. This includes clearing all 30 | // fastlocals, returning the PyFrameObject to the PyFrameObjectCache and 31 | // changing PyThreadState::frame. 32 | // 33 | // Preserves rax; does not preserve any other registers. 34 | // 35 | // Calls ctx.BindCleanupPoint(). 36 | absl::Status GenerateCleanup(CodeGenerationContext& ctx); 37 | 38 | // Generates code to return to the caller. This restores the value of callee- 39 | // saved registers, restores the stack pointer and returns. 40 | // 41 | // Calls ctx.BindEpilogPoint(). 42 | absl::Status GenerateEpilog(CodeGenerationContext& ctx); 43 | 44 | } // namespace deepmind::s6 45 | 46 | #endif // THIRD_PARTY_DEEPMIND_S6_CODE_GENERATION_PROLOG_EPILOG_INSERTION_H_ 47 | -------------------------------------------------------------------------------- /src/strongjit/util.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2021 The s6 Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS-IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include "strongjit/util.h" 16 | 17 | #include 18 | #include 19 | 20 | #include 21 | 22 | #include "utils/logging.h" 23 | 24 | namespace deepmind::s6 { 25 | 26 | std::vector ExtractInstructions(PyCodeObject* co) { 27 | absl::Span insts( 28 | reinterpret_cast<_Py_CODEUNIT*>(PyBytes_AS_STRING(co->co_code)), 29 | PyBytes_Size(co->co_code) / sizeof(_Py_CODEUNIT)); 30 | std::vector bytecode_insts; 31 | 32 | int64_t index = 0; 33 | for (_Py_CODEUNIT inst : insts) { 34 | bytecode_insts.emplace_back(index++ * 2, _Py_OPCODE(inst), _Py_OPARG(inst)); 35 | } 36 | 37 | return bytecode_insts; 38 | } 39 | 40 | // Converts a TryHandler::Kind into its CPython equivalent. 41 | int TryHandlerKindToOpcode(TryHandler::Kind kind) { 42 | switch (kind) { 43 | case TryHandler::kExcept: 44 | return SETUP_EXCEPT; 45 | case TryHandler::kLoop: 46 | return SETUP_LOOP; 47 | case TryHandler::kFinally: 48 | return SETUP_FINALLY; 49 | case TryHandler::kExceptHandler: 50 | return EXCEPT_HANDLER; 51 | case TryHandler::kFinallyHandler: 52 | S6_CHECK(false) << "kFinallyHandler has no opcode"; 53 | return -1; // Unreachable. 54 | } 55 | S6_UNREACHABLE(); 56 | } 57 | 58 | } // namespace deepmind::s6 59 | -------------------------------------------------------------------------------- /src/runtime/deoptimization_runtime.h: -------------------------------------------------------------------------------- 1 | // Copyright 2021 The s6 Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS-IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #ifndef THIRD_PARTY_DEEPMIND_S6_RUNTIME_DEOPTIMIZATION_RUNTIME_H_ 16 | #define THIRD_PARTY_DEEPMIND_S6_RUNTIME_DEOPTIMIZATION_RUNTIME_H_ 17 | 18 | #include 19 | #include 20 | 21 | #include 22 | 23 | #include "runtime/generator.h" 24 | #include "runtime/stack_frame.h" 25 | #include "strongjit/util.h" 26 | 27 | namespace deepmind::s6 { 28 | 29 | // Deoptimizes a function. Returns the result of the function. 30 | PyObject* Deoptimize(); 31 | 32 | // Attempts to scan the stack and find a StackFrame corresponding to the given 33 | // PyFrameObject. This is best-effort, and will only traverse `max_frames` in 34 | // an attempt to find a matching stack frame. 35 | StackFrame* FindStackFrameForPyFrameObject(PyFrameObject* pyframe, 36 | int64_t max_frames = 5); 37 | 38 | // Converts a Location as recorded by the code generator to a real value. 39 | // REQUIRES: !l.IsRegister(); 40 | int64_t LookupLocation(const Location& l, int64_t* spill_slots); 41 | 42 | // Deoptimizes this generator state. `pyframe` is the frame for this generator. 43 | // After this function, this GeneratorState object is no longer valid. 44 | void DeoptimizePausedGenerator(GeneratorState* gen_state, 45 | PyFrameObject* pyframe); 46 | 47 | } // namespace deepmind::s6 48 | 49 | #endif // THIRD_PARTY_DEEPMIND_S6_RUNTIME_DEOPTIMIZATION_RUNTIME_H_ 50 | -------------------------------------------------------------------------------- /src/utils/range.h: -------------------------------------------------------------------------------- 1 | // Copyright 2021 The s6 Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS-IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #ifndef THIRD_PARTY_DEEPMIND_S6_UTILS_RANGE_H_ 16 | #define THIRD_PARTY_DEEPMIND_S6_UTILS_RANGE_H_ 17 | 18 | #include 19 | #include 20 | 21 | namespace deepmind::s6 { 22 | 23 | // A range adapter for a pair of iterators. 24 | // 25 | // Wraps two iterators into a range-compatible view-like object. 26 | // 27 | // Does not propagate const - if constructed with non-const iterators `I` 28 | // then members of the underlying collection can be mutated through an 29 | // IteratorRange accessed through a const-access path. 30 | template 31 | class IteratorRange { 32 | public: 33 | using iterator = T; 34 | using const_iterator = T; 35 | using value_type = typename std::iterator_traits::value_type; 36 | 37 | IteratorRange() : begin_iterator_(), end_iterator_() {} 38 | IteratorRange(T begin_iterator, T end_iterator) 39 | : begin_iterator_(std::move(begin_iterator)), 40 | end_iterator_(std::move(end_iterator)) {} 41 | 42 | T begin() const { return begin_iterator_; } 43 | T end() const { return end_iterator_; } 44 | 45 | private: 46 | T begin_iterator_, end_iterator_; 47 | }; 48 | 49 | // Makes a range out of two iterators, similar to std::make_pair. 50 | template 51 | IteratorRange MakeRange(T begin, T end) { 52 | return IteratorRange(std::move(begin), std::move(end)); 53 | } 54 | 55 | } // namespace deepmind::s6 56 | 57 | #endif // THIRD_PARTY_DEEPMIND_S6_UTILS_RANGE_H_ 58 | -------------------------------------------------------------------------------- /src/utils/matchers.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2021 The s6 Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS-IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include "utils/matchers.h" 16 | 17 | #include "utils/status_builder.h" 18 | 19 | namespace deepmind::s6::matchers { 20 | 21 | void StatusIsMatcherCommonImpl::DescribeTo(std::ostream* os) const { 22 | *os << ", has a status code that "; 23 | code_matcher_.DescribeTo(os); 24 | *os << ", and has an error message that "; 25 | message_matcher_.DescribeTo(os); 26 | } 27 | 28 | void StatusIsMatcherCommonImpl::DescribeNegationTo(std::ostream* os) const { 29 | *os << ", or has a status code that "; 30 | code_matcher_.DescribeNegationTo(os); 31 | *os << ", or has an error message that "; 32 | message_matcher_.DescribeNegationTo(os); 33 | } 34 | 35 | bool StatusIsMatcherCommonImpl::MatchAndExplain( 36 | const ::absl::Status& status, 37 | ::testing::MatchResultListener* result_listener) const { 38 | ::testing::StringMatchResultListener inner_listener; 39 | if (!code_matcher_.MatchAndExplain(status.code(), &inner_listener)) { 40 | *result_listener << (inner_listener.str().empty() 41 | ? "whose status code is wrong" 42 | : "which has a status code " + 43 | inner_listener.str()); 44 | return false; 45 | } 46 | 47 | if (!message_matcher_.Matches(std::string(status.message()))) { 48 | *result_listener << "whose error message is wrong"; 49 | return false; 50 | } 51 | 52 | return true; 53 | } 54 | 55 | } // namespace deepmind::s6::matchers 56 | -------------------------------------------------------------------------------- /src/code_generation/asmjit_util.h: -------------------------------------------------------------------------------- 1 | // Copyright 2021 The s6 Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS-IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #ifndef THIRD_PARTY_DEEPMIND_S6_CODE_GENERATION_ASMJIT_UTIL_H_ 16 | #define THIRD_PARTY_DEEPMIND_S6_CODE_GENERATION_ASMJIT_UTIL_H_ 17 | 18 | #include 19 | #include 20 | 21 | #include "absl/status/status.h" 22 | #include "absl/strings/str_cat.h" 23 | #include "asmjit/asmjit.h" 24 | #include "utils/status_macros.h" 25 | 26 | #define RETURN_IF_ASMJIT_ERROR(x) S6_RETURN_IF_ERROR(AsmjitErrorToStatus(x)); 27 | 28 | // Converts an asmjit::Error to an absl::Status. 29 | inline absl::Status AsmjitErrorToStatus(asmjit::Error error) { 30 | if (error == asmjit::kErrorOk) return absl::OkStatus(); 31 | return absl::InternalError(absl::StrCat( 32 | "error in asmjit: ", asmjit::DebugUtils::errorAsString(error))); 33 | } 34 | 35 | namespace asmjit { 36 | // Defines operator<< on std::ostream for asmjit Operands. This allows macros 37 | // such as EXPECT_EQ, S6_RET_CHECK_EQ etc to print operands. 38 | inline std::ostream& operator<<(std::ostream& os, const Operand& operand) { 39 | ::asmjit::String s; 40 | ::asmjit::Logging::formatOperand(s, /*flags=*/0, /*emitter=*/nullptr, 41 | ::asmjit::ArchInfo::kIdX64, operand); 42 | os << std::string(s.data(), s.size()); 43 | return os; 44 | } 45 | 46 | namespace x86 { 47 | inline bool operator<(const Gp& a, const Gp& b) { return a.id() < b.id(); } 48 | } // namespace x86 49 | 50 | } // namespace asmjit 51 | 52 | #endif // THIRD_PARTY_DEEPMIND_S6_CODE_GENERATION_ASMJIT_UTIL_H_ 53 | -------------------------------------------------------------------------------- /src/strongjit/value_traits_test.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2021 The s6 Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS-IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include "strongjit/value_traits.h" 16 | 17 | #include "gmock/gmock.h" 18 | #include "gtest/gtest.h" 19 | #include "strongjit/instruction_traits.h" 20 | #include "strongjit/instructions.h" 21 | #include "strongjit/value.h" 22 | #include "strongjit/value_casts.h" 23 | 24 | namespace deepmind::s6 { 25 | namespace { 26 | 27 | template 28 | struct IsATest { 29 | template 30 | static std::optional Visit() { 31 | // Hide ASSERT_EQ return 32 | []() { 33 | constexpr bool is_base_class = 34 | std::is_base_of::value; 35 | DerivedClass fake; 36 | ASSERT_EQ(is_base_class, isa(fake)) 37 | << absl::StrCat("Failed at ", DerivedClass::kMnemonic); 38 | }(); 39 | return {}; 40 | } 41 | static bool Default() { return false; } 42 | }; 43 | 44 | template 45 | void TestAllInstructions() { 46 | ForAllInstructionKinds>(); 47 | } 48 | 49 | #define TEST_CLASS(CLASS) \ 50 | TEST(ValueTraits, CLASS) { TestAllInstructions(); } 51 | 52 | TEST_CLASS(Instruction) 53 | TEST_CLASS(CallPythonInst) 54 | TEST_CLASS(NumericInst) 55 | TEST_CLASS(BinaryInst) 56 | TEST_CLASS(UnaryInst) 57 | TEST_CLASS(RefcountInst) 58 | TEST_CLASS(SafepointInst) 59 | TEST_CLASS(TerminatorInst) 60 | TEST_CLASS(ConditionalTerminatorInst) 61 | TEST_CLASS(UnconditionalTerminatorInst) 62 | 63 | } // namespace 64 | } // namespace deepmind::s6 65 | -------------------------------------------------------------------------------- /src/strongjit/optimize_type_construction.h: -------------------------------------------------------------------------------- 1 | // Copyright 2021 The s6 Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS-IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #ifndef THIRD_PARTY_DEEPMIND_S6_STRONGJIT_OPTIMIZE_TYPE_CONSTRUCTION_H_ 16 | #define THIRD_PARTY_DEEPMIND_S6_STRONGJIT_OPTIMIZE_TYPE_CONSTRUCTION_H_ 17 | 18 | #include 19 | 20 | #include 21 | 22 | #include "absl/status/status.h" 23 | #include "strongjit/base.h" 24 | #include "strongjit/optimizer_util.h" 25 | 26 | namespace deepmind::s6 { 27 | 28 | // Identifies calling a type object to construct a new object instance. 29 | // Determines the required __new__ and __init__ functions. 30 | class OptimizeTypeConstructionPattern 31 | : public PatternT, NeedsSafepoint> { 33 | public: 34 | static absl::Status Apply(CallPythonInst* call, Rewriter& rewriter, 35 | ConstantAttributeInst* constant_attribute, 36 | SafepointInst* safepoint); 37 | 38 | private: 39 | static Value* CallInitDunder(Builder& builder, const Class& cls, 40 | const FunctionAttribute& attr, 41 | CallPythonInst& call_python, Value* object); 42 | static Value* CallNewDunder(Builder& builder, const Class& cls, 43 | const FunctionAttribute& attr, 44 | CallPythonInst& call_python); 45 | }; 46 | 47 | } // namespace deepmind::s6 48 | 49 | #endif // THIRD_PARTY_DEEPMIND_S6_STRONGJIT_OPTIMIZE_TYPE_CONSTRUCTION_H_ 50 | -------------------------------------------------------------------------------- /src/interpreter_test.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2021 The s6 Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS-IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | // Note that most of the tests for interpreter.cc are in 16 | // python/interpreter_test.py. 17 | 18 | #include "interpreter.h" 19 | 20 | #include 21 | 22 | #include // NOLINT 23 | #include 24 | 25 | #include "api.h" 26 | #include "gmock/gmock.h" 27 | #include "gtest/gtest.h" 28 | #include "utils/status_macros.h" 29 | 30 | namespace deepmind::s6 { 31 | namespace { 32 | 33 | using ::testing::Contains; 34 | using ::testing::NotNull; 35 | using ::testing::StrEq; 36 | 37 | // A program that compute Fibonnacci numbers for ~2 seconds. We use a sampling 38 | // profiler, so we need to run for enough time to guarantee a reasonable sample. 39 | const char kProgram[] = R"( 40 | import time 41 | 42 | def pyfib(n): 43 | if n <= 1: 44 | return 1 45 | return pyfib(n-1) + pyfib(n-2) 46 | 47 | start = time.perf_counter() 48 | # Run Fibonacci for 2 seconds so we can assume to get some reasonable 49 | # profiling samples. 50 | while (time.perf_counter() < start + 2.0): 51 | pyfib(20) 52 | )"; 53 | 54 | void InitializeOnce() { 55 | // Ensure Python is initialized. 56 | // TODO: Even if we call Py_Finalize, the leak checker thinks we 57 | // leak a load of Python objects. Understand why. A test as simple as 58 | // Py_Initialize(); Py_Finalize(); leaks objects. 59 | static std::once_flag once; 60 | std::call_once(once, [&]() { 61 | Py_Initialize(); 62 | S6_ASSERT_OK(s6::Initialize()); 63 | }); 64 | } 65 | 66 | } // namespace 67 | } // namespace deepmind::s6 68 | -------------------------------------------------------------------------------- /src/utils/keyed_intern_table_test.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2021 The s6 Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS-IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include "utils/keyed_intern_table.h" 16 | 17 | #include 18 | #include 19 | 20 | #include "gmock/gmock.h" 21 | #include "gtest/gtest.h" 22 | 23 | namespace deepmind::s6 { 24 | namespace { 25 | 26 | TEST(KeyedInternTable, DefaultConstructedTableHasZeroSize) { 27 | KeyedInternTable table; 28 | EXPECT_EQ(table.size(), 0); 29 | } 30 | 31 | TEST(KeyedInternTable, InsertString) { 32 | KeyedInternTable table; 33 | 34 | // Insert a new string. 35 | { 36 | auto [key, value] = table.Insert("Test"); 37 | EXPECT_EQ(key, 0); 38 | EXPECT_STREQ(value, "Test"); 39 | EXPECT_EQ(table.size(), 1); 40 | } 41 | 42 | // Insert another new string. 43 | { 44 | auto [key, value] = table.Insert("Hello"); 45 | EXPECT_EQ(key, 1); 46 | EXPECT_STREQ(value, "Hello"); 47 | EXPECT_EQ(table.size(), 2); 48 | } 49 | 50 | // Insert an existing string. 51 | { 52 | auto [key, value] = table.Insert("Test"); 53 | EXPECT_EQ(key, 0); 54 | EXPECT_STREQ(value, "Test"); 55 | EXPECT_EQ(table.size(), 2); 56 | } 57 | } 58 | 59 | TEST(KeyedInternTable, LookupKey) { 60 | // Create table with a single entry. 61 | KeyedInternTable table; 62 | table.Insert("Test"); 63 | 64 | // Lookup a non-inserted key. 65 | EXPECT_EQ(table.ToKey("Not in table"), std::nullopt); 66 | 67 | // Lookup an inserted key. 68 | EXPECT_EQ(table.ToKey("Test"), std::optional(0)); 69 | } 70 | 71 | } // namespace 72 | } // namespace deepmind::s6 73 | -------------------------------------------------------------------------------- /src/utils/mathutil_test.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2021 The s6 Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS-IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include "utils/mathutil.h" 16 | 17 | #include 18 | 19 | #include "gmock/gmock.h" 20 | #include "gtest/gtest.h" 21 | 22 | namespace deepmind::s6 { 23 | namespace { 24 | TEST(MathUtil, RoundUpTo) { 25 | EXPECT_EQ(RoundUpTo(10, 1), 10); 26 | EXPECT_EQ(RoundUpTo(10, 2), 10); 27 | EXPECT_EQ(RoundUpTo(10, 5), 10); 28 | EXPECT_EQ(RoundUpTo(10, 10), 10); 29 | EXPECT_EQ(RoundUpTo(10, 3), 12); 30 | EXPECT_EQ(RoundUpTo(9, 10), 10); 31 | EXPECT_EQ(RoundUpTo(11, 10), 20); 32 | EXPECT_EQ(RoundUpTo(0, 10), 0); 33 | EXPECT_EQ(RoundUpTo(10000000001, 10000000000), 20000000000); 34 | EXPECT_EQ(RoundUpTo(9999999999, 10000000000), 10000000000); 35 | EXPECT_EQ(RoundUpTo(1, 10000000000), 10000000000); 36 | } 37 | 38 | TEST(MathUtil, RoundDownTo) { 39 | EXPECT_EQ(RoundDownTo(10, 1), 10); 40 | EXPECT_EQ(RoundDownTo(10, 2), 10); 41 | EXPECT_EQ(RoundDownTo(10, 5), 10); 42 | EXPECT_EQ(RoundDownTo(10, 10), 10); 43 | EXPECT_EQ(RoundDownTo(10, 3), 9); 44 | EXPECT_EQ(RoundDownTo(9, 10), 0); 45 | EXPECT_EQ(RoundDownTo(11, 10), 10); 46 | EXPECT_EQ(RoundDownTo(0, 10), 0); 47 | EXPECT_EQ(RoundDownTo(10000000001, 10000000000), 10000000000); 48 | EXPECT_EQ(RoundDownTo(9999999999, 10000000000), 0); 49 | EXPECT_EQ(RoundDownTo(1, 10000000000), 0); 50 | } 51 | } // namespace 52 | } // namespace deepmind::s6 53 | -------------------------------------------------------------------------------- /src/strongjit/instruction_traits_test.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2021 The s6 Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS-IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include "strongjit/instruction_traits.h" 16 | 17 | #include 18 | 19 | #include "gmock/gmock.h" 20 | #include "gtest/gtest.h" 21 | #include "strongjit/function.h" 22 | 23 | namespace deepmind::s6 { 24 | namespace { 25 | 26 | TEST(InstructionTraitsTest, InstructionList) { 27 | Function f("test"); 28 | ConstantInst* constant_inst = f.Create(42); 29 | CallNativeInst* call_native_inst = f.Create( 30 | Callee::kPyObject_GetAttr, absl::Span{}); 31 | ASSERT_FALSE(InstructionTraits::ClobbersAllRegisters(*constant_inst)); 32 | ASSERT_TRUE(InstructionTraits::ClobbersAllRegisters(*call_native_inst)); 33 | } 34 | 35 | TEST(InstructionTraitsTest, InstructionAttr) { 36 | Function f("test"); 37 | IncrefInst* incref_inst = f.Create(); 38 | ConstantInst* constant_inst = f.Create(42); 39 | ASSERT_FALSE(InstructionTraits::ProducesValue(*incref_inst)); 40 | ASSERT_TRUE(InstructionTraits::ProducesValue(*constant_inst)); 41 | } 42 | 43 | TEST(InstructionTraitsTest, InstructionInheritance) { 44 | Function f("test"); 45 | IncrefInst* incref_inst = f.Create(); 46 | ASSERT_FALSE(ValueTraits::IsA(*incref_inst)); 47 | ASSERT_TRUE(ValueTraits::IsA(*incref_inst)); 48 | } 49 | 50 | TEST(InstructionTraitsTest, Block) { 51 | Function f("test"); 52 | Block* block = f.CreateBlock(); 53 | ASSERT_TRUE(ValueTraits::IsA(*block)); 54 | } 55 | 56 | } // namespace 57 | } // namespace deepmind::s6 58 | -------------------------------------------------------------------------------- /src/utils/mathutil.h: -------------------------------------------------------------------------------- 1 | // Copyright 2021 The s6 Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS-IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #ifndef THIRD_PARTY_DEEPMIND_S6_UTILS_MATHUTIL_H_ 16 | #define THIRD_PARTY_DEEPMIND_S6_UTILS_MATHUTIL_H_ 17 | 18 | #include 19 | #include 20 | 21 | #include "utils/logging.h" 22 | 23 | namespace deepmind::s6 { 24 | // Returns the maximum integer value which is a multiple of rounding_value, 25 | // and less than or equal to input_value. 26 | // The input_value must be greater than or equal to zero, and the 27 | // rounding_value must be greater than zero. 28 | template 29 | static T RoundDownTo(T input_value, T rounding_value) { 30 | static_assert(std::is_integral_v, "Operand type is not an integer"); 31 | S6_DCHECK_GE(input_value, 0); 32 | S6_DCHECK_GT(rounding_value, 0); 33 | return (input_value / rounding_value) * rounding_value; 34 | } 35 | 36 | // Returns the minimum integer value which is a multiple of rounding_value, 37 | // and greater than or equal to input_value. 38 | // The input_value must be greater than or equal to zero, and the 39 | // rounding_value must be greater than zero. 40 | template 41 | static T RoundUpTo(T input_value, T rounding_value) { 42 | static_assert(std::is_integral_v, "Operand type is not an integer"); 43 | S6_DCHECK_GE(input_value, 0); 44 | S6_DCHECK_GT(rounding_value, 0); 45 | const T remainder = input_value % rounding_value; 46 | return (remainder == 0) ? input_value 47 | : (input_value - remainder + rounding_value); 48 | } 49 | } // namespace deepmind::s6 50 | 51 | #endif // THIRD_PARTY_DEEPMIND_S6_UTILS_MATHUTIL_H_ 52 | -------------------------------------------------------------------------------- /src/python/type_feedback.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2021 The s6 Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS-IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include "type_feedback.h" 16 | 17 | #include "absl/container/inlined_vector.h" 18 | #include "absl/status/status.h" 19 | #include "classes/class.h" 20 | #include "classes/class_manager.h" 21 | #include "classes/object.h" 22 | #include "metadata.h" 23 | #include "pybind11/pybind11.h" 24 | #include "pybind11/pytypes.h" 25 | #include "pybind11_abseil/status_casters.h" 26 | 27 | namespace pybind11 { 28 | using ::deepmind::s6::ClassManager; 29 | using ::deepmind::s6::Metadata; 30 | 31 | PYBIND11_MODULE(type_feedback, py_module) { 32 | pybind11::google::ImportStatusModule(); 33 | py_module.doc() = "Defines internal S6 functions related to type feedback"; 34 | 35 | py_module.def( 36 | "extract_from_code_object", 37 | [](object obj) -> absl::StatusOr { 38 | if (!PyCode_Check(obj.ptr())) 39 | return absl::FailedPreconditionError("Expected a code object"); 40 | const auto& type_feedback = 41 | Metadata::Get(reinterpret_cast(obj.ptr())) 42 | ->type_feedback(); 43 | list l; 44 | for (const absl::InlinedVector& 45 | distributions : type_feedback) { 46 | for (const deepmind::s6::ClassDistribution& distribution : 47 | distributions) { 48 | l.append( 49 | distribution.Summarize().ToString(&ClassManager::Instance())); 50 | } 51 | } 52 | return l; 53 | }, 54 | "Returns the class ID of an object."); 55 | } 56 | 57 | } // namespace pybind11 58 | -------------------------------------------------------------------------------- /src/python/api.pyi: -------------------------------------------------------------------------------- 1 | # Copyright 2021 The s6 Authors. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http:#www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS-IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | from typing import Any 16 | 17 | from typing import overload 18 | 19 | class CompilationFailedError(Exception): ... 20 | 21 | class NotCompiledError(Exception): ... 22 | 23 | class S6CodeDetail: 24 | def __init__(self, *args, **kwargs) -> None: ... 25 | def deoptimize(self) -> None: ... 26 | def force_compile(self) -> None: ... 27 | @property 28 | def is_compiled(self) -> bool: ... 29 | @property 30 | def strongjit(self) -> str: ... 31 | 32 | class S6JitCallable: 33 | def __init__(self, *args, **kwargs) -> None: ... 34 | def _evaluate(self, *args, **kwargs) -> handle: ... 35 | def _interpret(self, *args, **kwargs) -> handle: ... 36 | def __call__(self, *args, **kwargs) -> handle: ... 37 | def __get__(self, arg0: object, arg1: object) -> S6JitCallable: ... 38 | @property 39 | def fn(self) -> function: ... 40 | 41 | class S6Profile: 42 | def __init__(self, *args, **kwargs) -> None: ... 43 | def export_as_pprof(self, arg0: str, url: bool = ...) -> str: ... 44 | @overload 45 | def prune_common_frames(self) -> S6Profile: ... 46 | @overload 47 | def prune_common_frames() -> Any: ... 48 | @property 49 | def sample_count(self) -> int: ... 50 | 51 | class S6ProfileCallable: 52 | def __init__(self, *args, **kwargs) -> None: ... 53 | def __call__(self, *args, **kwargs) -> Any: ... 54 | @property 55 | def fn(self) -> object: ... 56 | 57 | def inspect(fn: object) -> S6CodeDetail: ... 58 | def jit(fn: function) -> object: ... 59 | @overload 60 | def profile(callable: object) -> S6ProfileCallable: ... 61 | @overload 62 | def profile(f) -> Any: ... 63 | -------------------------------------------------------------------------------- /src/strongjit/value_map.h: -------------------------------------------------------------------------------- 1 | // Copyright 2021 The s6 Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS-IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #ifndef THIRD_PARTY_DEEPMIND_S6_STRONGJIT_VALUE_MAP_H_ 16 | #define THIRD_PARTY_DEEPMIND_S6_STRONGJIT_VALUE_MAP_H_ 17 | 18 | #include 19 | 20 | #include "absl/types/optional.h" 21 | #include "strongjit/base.h" 22 | 23 | // Like S6_VLOG(n), but prepends the recursion depth. This allows distinguishing 24 | // log output when evaluating function calls. 25 | #define EVLOG(n) S6_VLOG(n) << PyThreadState_GET()->recursion_depth << "> " 26 | 27 | namespace deepmind::s6 { 28 | 29 | using ValueNumbering = absl::flat_hash_map; 30 | 31 | // Maps a virtual register file of Value*s to 64-bit values at runtime. If given 32 | // a RegisterAllocation object, this also simulates the physical register file. 33 | class ValueMap { 34 | public: 35 | explicit ValueMap(const Function& f); 36 | 37 | // Sets a value. 38 | void Set(const Value* v, int64_t value); 39 | 40 | template 41 | void Set(const Value* v, T value) { 42 | static_assert(sizeof(T) == sizeof(int64_t), "T must be int64_t-sized!"); 43 | Set(v, reinterpret_cast(value)); 44 | } 45 | 46 | // Returns the current content of a virtual register `v`. 47 | int64_t Get(const Value* v) const; 48 | 49 | template 50 | T Get(const Value* v) const { 51 | static_assert(sizeof(T) == sizeof(int64_t), "T must be int64_t-sized!"); 52 | return reinterpret_cast(Get(v)); 53 | } 54 | 55 | private: 56 | absl::optional value_numbering_; 57 | absl::flat_hash_map values_; 58 | }; 59 | 60 | } // namespace deepmind::s6 61 | 62 | #endif // THIRD_PARTY_DEEPMIND_S6_STRONGJIT_VALUE_MAP_H_ 63 | -------------------------------------------------------------------------------- /src/strongjit/dis6.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2021 The s6 Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS-IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include 16 | #include 17 | 18 | #include "absl/status/status.h" 19 | #include "absl/status/statusor.h" 20 | #include "code_object.h" 21 | #include "pybind11/pybind11.h" 22 | #include "pybind11_abseil/status_casters.h" 23 | #include "strongjit/base.h" 24 | #include "strongjit/formatter.h" 25 | #include "strongjit/ingestion.h" 26 | #include "utils/status_macros.h" 27 | 28 | namespace deepmind::s6 { 29 | namespace { 30 | 31 | namespace py = ::pybind11; 32 | 33 | // Wrapper around `FormatOrDie` to act as a disassembler API of a Python 34 | // function to its StrongJIT IR. 35 | absl::StatusOr dis6(const py::function& py_func) { 36 | PyCodeObject* co; 37 | if (PyFunction_Check(py_func.ptr())) { 38 | co = reinterpret_cast(PyFunction_GET_CODE(py_func.ptr())); 39 | } else if (PyMethod_Check(py_func.ptr())) { 40 | co = reinterpret_cast( 41 | PyFunction_GET_CODE(PyMethod_GET_FUNCTION(py_func.ptr()))); 42 | } else { 43 | return absl::InvalidArgumentError("Argument must be a function or method."); 44 | } 45 | 46 | std::vector bytecode_insts = ExtractInstructions(co); 47 | S6_ASSIGN_OR_RETURN( 48 | Function function, 49 | IngestProgram(bytecode_insts, PyObjectToString(co->co_name), 50 | co->co_nlocals, co->co_argcount)); 51 | 52 | return Format(function); 53 | } 54 | 55 | PYBIND11_MODULE(dis6, m) { 56 | pybind11::google::ImportStatusModule(); 57 | 58 | m.def("dis6", &dis6, py::arg("func"), 59 | "Disassembles a function to its Strongjit IR."); 60 | } 61 | 62 | } // namespace 63 | } // namespace deepmind::s6 64 | -------------------------------------------------------------------------------- /src/code_generation/jit_stub_test.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2021 The s6 Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS-IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include "code_generation/jit_stub.h" 16 | 17 | #include 18 | 19 | #include "asmjit/asmjit.h" 20 | #include "asmjit/core/builder.h" 21 | #include "code_generation/asmjit_util.h" 22 | #include "gmock/gmock.h" 23 | #include "gtest/gtest.h" 24 | 25 | namespace deepmind::s6 { 26 | namespace { 27 | namespace x86 = ::asmjit::x86; 28 | 29 | TEST(JitStubTest, Simple) { 30 | JitStub stub(x86::ptr(x86::rax)); 31 | JitStub type = stub.ob_type(); 32 | EXPECT_EQ(type.Mem(), x86::qword_ptr(x86::rax, offsetof(PyObject, ob_type))); 33 | EXPECT_EQ(type.dereference_kind(), DereferenceKind::kDereferenced); 34 | 35 | // Now latch `type` into rbx. 36 | asmjit::x86::Builder b; 37 | x86::Emitter& e = *b.as(); 38 | type = type.Load(x86::rbx, e); 39 | EXPECT_EQ(type.Mem(), x86::qword_ptr(x86::rbx)); 40 | EXPECT_EQ(type.dereference_kind(), DereferenceKind::kEffectiveAddress); 41 | 42 | // Now we should be able to access tp_version_tag. 43 | JitStub version_tag = type.tp_version_tag(); 44 | EXPECT_EQ(version_tag.Mem(), 45 | x86::dword_ptr(x86::rbx, offsetof(PyTypeObject, tp_version_tag))); 46 | EXPECT_EQ(version_tag.dereference_kind(), DereferenceKind::kDereferenced); 47 | } 48 | 49 | TEST(JitStubTest, BaseOffset) { 50 | // This PyObject is at [rax + 24]. 51 | JitStub stub(x86::ptr(x86::rax, 24)); 52 | JitStub type = stub.ob_type(); 53 | // The offset of ob_type and 24 should be summed into the offset field. 54 | EXPECT_EQ(type.Mem(), 55 | x86::qword_ptr(x86::rax, 24 + offsetof(PyObject, ob_type))); 56 | EXPECT_EQ(type.dereference_kind(), DereferenceKind::kDereferenced); 57 | } 58 | } // namespace 59 | 60 | } // namespace deepmind::s6 61 | -------------------------------------------------------------------------------- /src/utils/status_builder.h: -------------------------------------------------------------------------------- 1 | // Copyright 2021 The s6 Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS-IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #ifndef THIRD_PARTY_DEEPMIND_S6_UTILS_STATUS_BUILDER_H_ 16 | #define THIRD_PARTY_DEEPMIND_S6_UTILS_STATUS_BUILDER_H_ 17 | 18 | #include 19 | #include 20 | 21 | #include "absl/status/status.h" 22 | 23 | namespace deepmind::s6 { 24 | 25 | // StatusBuilder allows an absl::Status and message to be built with a streaming 26 | // interface. 27 | // 28 | // StatusBuilder is designed so that it can only be used as a temporary - 29 | // conversion to absl::Status and streaming operations require an rvalue. 30 | // 31 | // StatusBuilder is used in S6 macros to produce error messages: 32 | // S6_RET_CHECK(some_condition) << "Error message " << built << "with streaming" 33 | // << interface; 34 | class StatusBuilder { 35 | public: 36 | template 37 | explicit StatusBuilder(absl::StatusCode code, Ts&&... ts) : code_(code) { 38 | AppendMessage(std::forward(ts)...); 39 | } 40 | 41 | template 42 | friend StatusBuilder&& operator<<(StatusBuilder&& s, T&& t) { 43 | s.stream_ << std::forward(t); 44 | return std::move(s); 45 | } 46 | 47 | operator absl::Status() && { // NOLINT - allow explicit 48 | return absl::Status(code_, stream_.str()); 49 | } 50 | 51 | private: 52 | template 53 | void AppendMessage(T&& t, Ts&&... ts) { 54 | stream_ << std::forward(t); 55 | AppendMessage(std::forward(ts)...); 56 | } 57 | 58 | template 59 | void AppendMessage(T&& t) { 60 | stream_ << std::forward(t); 61 | } 62 | 63 | void AppendMessage() {} 64 | 65 | std::ostringstream stream_; 66 | absl::StatusCode code_; 67 | }; 68 | } // namespace deepmind::s6 69 | 70 | #endif // THIRD_PARTY_DEEPMIND_S6_UTILS_STATUS_BUILDER_H_ 71 | -------------------------------------------------------------------------------- /src/utils/path.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2021 The s6 Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS-IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include "utils/path.h" 16 | 17 | #include 18 | #include 19 | 20 | namespace deepmind::s6::file { 21 | 22 | std::string JoinPathImpl(std::initializer_list fragments) { 23 | std::string joined_path; 24 | 25 | if (fragments.size() == 0) { 26 | return joined_path; 27 | } 28 | 29 | // Assume no fragment has a leading or trailing '/'. This means that we need 30 | // an extra character between each pair of fragments. 31 | size_t path_size = fragments.size() - 1; 32 | for (absl::string_view fragment : fragments) { 33 | path_size += fragment.size(); 34 | } 35 | joined_path.resize(path_size); // Will zero-initialize the string. 36 | 37 | auto out = joined_path.begin(); 38 | const auto begin = joined_path.begin(); 39 | bool path_has_trailing_slash = false; 40 | for (absl::string_view fragment : fragments) { 41 | if (fragment.empty()) { 42 | continue; 43 | } 44 | 45 | if (fragment.front() == '/') { 46 | if (path_has_trailing_slash) { 47 | fragment.remove_prefix(1); 48 | } 49 | } else { 50 | // Allow path to start without a leading '/'. 51 | if (!path_has_trailing_slash && out != begin) { 52 | *(out++) = '/'; 53 | } 54 | } 55 | out = std::copy(fragment.begin(), fragment.end(), out); 56 | path_has_trailing_slash = fragment.back() == '/'; 57 | } 58 | 59 | // Trim unnecessary reserved space. 60 | joined_path.erase(out, joined_path.end()); 61 | return joined_path; 62 | } 63 | 64 | absl::string_view Basename(absl::string_view path) { 65 | auto pos = path.find_last_of('/'); 66 | if (pos == std::string::npos) { 67 | return path; 68 | } 69 | path.remove_prefix(pos + 1); 70 | return path; 71 | } 72 | 73 | } // namespace deepmind::s6::file 74 | -------------------------------------------------------------------------------- /src/classes/util.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2021 The s6 Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS-IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include "classes/util.h" 16 | 17 | #include 18 | 19 | #include 20 | 21 | #include "absl/base/casts.h" 22 | 23 | namespace deepmind::s6 { 24 | int64_t GetClassIdFromObjectDict(PyDictObject* dict) { 25 | // The class ID is stored in the upper 20 bits of ma_version_tag. All 26 | // ma_version_tags that were not set by S6 will have these bits clear (unless 27 | // the global ma_version_tag has reached 2**44, but that would take 5 years 28 | // at 100kHz). 29 | return dict->ma_version_tag >> (64 - kNumClassIdBits); 30 | } 31 | 32 | PyDictObject* GetObjectDict(PyObject* object) { 33 | PyTypeObject* type = Py_TYPE(object); 34 | if (type->tp_dictoffset <= 0) { 35 | return nullptr; 36 | } 37 | 38 | PyDictObject** dict = absl::bit_cast( 39 | absl::bit_cast(object) + type->tp_dictoffset); 40 | return *dict; 41 | } 42 | 43 | // Sets the class ID of a PyObject, given its dict as returned by 44 | // GetObjectDict(). 45 | void SetClassId(PyDictObject* object_dict, int64_t class_id) { 46 | object_dict->ma_version_tag = static_cast(class_id) 47 | << (64 - kNumClassIdBits); 48 | } 49 | 50 | int64_t GetClassId(PyObject* object) { 51 | PyTypeObject* type = Py_TYPE(object); 52 | if (type->tp_dictoffset < 0) { 53 | // Classes are not handled for this type. 54 | return 0; 55 | } 56 | 57 | if (type->tp_dictoffset > 0) { 58 | PyDictObject** dict = absl::bit_cast( 59 | absl::bit_cast(object) + type->tp_dictoffset); 60 | if (*dict) { 61 | return GetClassIdFromObjectDict(*dict); 62 | } 63 | } 64 | // We get here if an object cannot have a dict OR if it doesn't have a dict. 65 | return GetClassIdFromType(type); 66 | } 67 | 68 | } // namespace deepmind::s6 69 | -------------------------------------------------------------------------------- /src/utils/diffs_test.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2021 The s6 Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS-IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include "utils/diffs.h" 16 | 17 | #include "gmock/gmock.h" 18 | #include "gtest/gtest.h" 19 | 20 | namespace deepmind::s6 { 21 | namespace { 22 | 23 | TEST(DiffVecTest, IntegerTest) { 24 | // A DiffVecTrait implementation for int. 25 | class DiffVecTraitsInt { 26 | public: 27 | using Diff = int; 28 | static void Apply(int& t, int d) { t += d; } 29 | /*static Diff MakeDiff(const T& origin, const T& dest) { 30 | return origin.MakeDiff(dest); 31 | }*/ 32 | }; 33 | DiffVec dv; 34 | 35 | dv.set_front(4); 36 | dv.push_back(2); 37 | dv.push_back(-3); 38 | ASSERT_EQ(dv.size(), 3); 39 | ASSERT_EQ(dv.at(0), 4); 40 | ASSERT_EQ(dv.at(1), 6); 41 | ASSERT_EQ(dv.at(2), 3); 42 | 43 | dv.resize(2); 44 | ASSERT_EQ(dv.size(), 2); 45 | ASSERT_EQ(dv.at(0), 4); 46 | ASSERT_EQ(dv.at(1), 6); 47 | 48 | auto cur = dv.BeginCursor(); 49 | ASSERT_EQ(*cur, 4); 50 | cur.StepForward(); 51 | ASSERT_EQ(*cur, 6); 52 | dv.push_back_maintain(-5, cur); 53 | // it is still valid because it was maintained. 54 | ASSERT_EQ(*cur, 6); 55 | ASSERT_TRUE(cur.NextDiff()); 56 | ASSERT_EQ(*cur.NextDiff(), -5); 57 | ASSERT_FALSE(cur.IsEnd()); 58 | ASSERT_FALSE(cur.IsLast()); 59 | cur.StepForward(); 60 | ASSERT_EQ(*cur, 1); 61 | // There is no next diff, this is the last element. 62 | ASSERT_FALSE(cur.NextDiff()); 63 | ASSERT_FALSE(cur.IsEnd()); 64 | ASSERT_TRUE(cur.IsLast()); 65 | cur.StepForward(); 66 | ASSERT_EQ(cur, dv.EndCursor()); 67 | ASSERT_TRUE(cur.IsEnd()); 68 | ASSERT_FALSE(cur.IsLast()); 69 | ASSERT_EQ(dv.size(), 3); 70 | ASSERT_EQ(dv.at(0), 4); 71 | ASSERT_EQ(dv.at(1), 6); 72 | ASSERT_EQ(dv.at(2), 1); 73 | 74 | dv.front() = 7; 75 | ASSERT_EQ(dv.size(), 3); 76 | ASSERT_EQ(dv.at(0), 7); 77 | ASSERT_EQ(dv.at(1), 9); 78 | ASSERT_EQ(dv.at(2), 4); 79 | } 80 | 81 | } // namespace 82 | } // namespace deepmind::s6 83 | -------------------------------------------------------------------------------- /src/code_generation/code_generator.h: -------------------------------------------------------------------------------- 1 | // Copyright 2021 The s6 Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS-IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #ifndef THIRD_PARTY_DEEPMIND_S6_CODE_GENERATION_CODE_GENERATOR_H_ 16 | #define THIRD_PARTY_DEEPMIND_S6_CODE_GENERATION_CODE_GENERATOR_H_ 17 | 18 | #include 19 | 20 | #include 21 | #include 22 | 23 | #include "absl/status/statusor.h" 24 | #include "absl/types/span.h" 25 | #include "allocator.h" 26 | #include "asmjit/asmjit.h" 27 | #include "code_generation/register_allocator.h" 28 | #include "code_object.h" 29 | #include "core_util.h" 30 | #include "strongjit/base.h" 31 | #include "strongjit/function.h" 32 | 33 | namespace deepmind { 34 | namespace s6 { 35 | class Metadata; 36 | } // namespace s6 37 | } // namespace deepmind 38 | 39 | namespace deepmind::s6 { 40 | 41 | using AddPassesFunction = std::function; 42 | 43 | struct CodeGeneratorOptions { 44 | // A callback that can modify the Builder object before it is 45 | // code-generated. The only supported usecase is to call Builder::addPassT<>() 46 | // to add one or more passes, which will be run before the code is generated. 47 | const AddPassesFunction& add_passes = {}; 48 | 49 | // Generates profiling information for BrInsts. 50 | bool profile_branches = false; 51 | }; 52 | 53 | // Generates machine code for `f`. The returned CodeObject takes ownership of 54 | // `f`. 55 | // 56 | // `add_passes` is a callback that can modify the Builder object before it is 57 | // code-generated. The only supported usecase is to call Builder::addPassT<>() 58 | // to add one or more passes, which will be run before the code is generated. 59 | absl::StatusOr> GenerateCode( 60 | Function&& f, const RegisterAllocation& ra, 61 | absl::Span program, JitAllocator& allocator, 62 | PyCodeObject* code_object, Metadata* metadata = nullptr, 63 | const CodeGeneratorOptions& options = {}); 64 | 65 | } // namespace deepmind::s6 66 | 67 | #endif // THIRD_PARTY_DEEPMIND_S6_CODE_GENERATION_CODE_GENERATOR_H_ 68 | -------------------------------------------------------------------------------- /src/utils/no_destructor_test.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2021 The s6 Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS-IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include "utils/no_destructor.h" 16 | 17 | #include 18 | 19 | #include "absl/container/flat_hash_map.h" 20 | #include "gmock/gmock.h" 21 | #include "gtest/gtest.h" 22 | 23 | namespace deepmind::s6 { 24 | namespace { 25 | 26 | struct StructWithDeletedDestructor { 27 | ~StructWithDeletedDestructor() = delete; 28 | }; 29 | 30 | TEST(NoDestructorTest, DestructorNeverCalled) { 31 | [[maybe_unused]] NoDestructor t; 32 | } 33 | 34 | struct Data { 35 | int x; 36 | explicit Data(int x_) : x(x_) {} 37 | Data(std::initializer_list xs) : x(0) { 38 | for (int value : xs) { 39 | x += value; 40 | } 41 | } 42 | }; 43 | 44 | TEST(NoDestructorTest, ConstAccessors) { 45 | static const NoDestructor data(42); 46 | EXPECT_EQ((*data).x, 42); 47 | EXPECT_EQ(data.get()->x, 42); 48 | EXPECT_EQ(data->x, 42); 49 | } 50 | 51 | TEST(NoDestructorTest, NonConstAccessors) { 52 | static NoDestructor data(0); 53 | 54 | (*data).x = 1; 55 | EXPECT_EQ((*data).x, 1); 56 | 57 | // Split `get()` and `->` to avoid triggering 58 | // readability-redundant-smartptr-get 59 | // https://clang.llvm.org/extra/clang-tidy/checks/readability-redundant-smartptr-get.html#readability-redundant-smartptr-get 60 | Data* p = data.get(); 61 | p->x = 2; 62 | EXPECT_EQ(data.get()->x, 2); 63 | 64 | data->x = 3; 65 | EXPECT_EQ(data->x, 3); 66 | } 67 | 68 | TEST(NoDestructorTest, InitialiserListConstruction) { 69 | static NoDestructor> m( 70 | {{0, 1}, {1, 2}, {2, 4}, {3, 8}}); 71 | EXPECT_EQ(m->size(), 4); 72 | } 73 | 74 | TEST(NoDestructorTest, IsTriviallyDestructible) { 75 | EXPECT_TRUE(std::is_trivially_destructible_v>); 76 | EXPECT_TRUE(std::is_trivially_destructible_v>); 77 | } 78 | 79 | TEST(NoDestructorTest, ZeroInitialization) { 80 | static NoDestructor x; 81 | EXPECT_EQ(*x, 0); 82 | } 83 | 84 | } // namespace 85 | } // namespace deepmind::s6 86 | -------------------------------------------------------------------------------- /src/profiler_test_util.h: -------------------------------------------------------------------------------- 1 | // Copyright 2021 The s6 Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS-IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #ifndef THIRD_PARTY_DEEPMIND_S6_PROFILER_TEST_UTIL_H_ 16 | #define THIRD_PARTY_DEEPMIND_S6_PROFILER_TEST_UTIL_H_ 17 | 18 | #include 19 | #include 20 | #include 21 | 22 | #include "absl/status/status.h" 23 | #include "absl/status/statusor.h" 24 | #include "absl/synchronization/notification.h" 25 | #include "perftools/profiles/proto/profile.pb.h" 26 | 27 | namespace deepmind::s6 { 28 | 29 | // Symbolizes the stack for `sample`. All unknown entries become "(unknown)". 30 | // Entries that already have symbol data (Location.line_size() != 0) are 31 | // symbolized based on their first .line() entry. 32 | std::vector SymbolizeSample( 33 | const perftools::profiles::Profile& profile, 34 | const perftools::profiles::Sample& sample); 35 | 36 | // Finds the sample with the deepest stack trace in the profile. Return nullptr 37 | // if the profile is empty. 38 | // 39 | // If `includes` is nonzero, attempts to get a sample that contains `includes`. 40 | const perftools::profiles::Sample* GetDeepestSample( 41 | const perftools::profiles::Profile& profile, int64_t includes = 0); 42 | 43 | // A Profiler implementation intended for unit testing. It does not sample; 44 | // instead the unit test calls Sample() to deterministically trigger a SIGPROF 45 | // event. 46 | // 47 | // Only one TestOnlyProfiler may be collecting at any one time. 48 | class TestOnlyProfiler : public Profiler { 49 | public: 50 | // As Profiler::StartCollecting(). This is thread-hostile. 51 | absl::Status StartCollecting(); 52 | 53 | // As Profiler::StopCollecting(). This is thread-hostile. 54 | absl::StatusOr> 55 | StopCollectingPprof(); 56 | 57 | absl::StatusOr StopCollecting(); 58 | 59 | // Simulates a SIGPROF event. This will occur synchronously on the current 60 | // thread. This is thread-hostile. 61 | void Sample(); 62 | }; 63 | 64 | } // namespace deepmind::s6 65 | 66 | #endif // THIRD_PARTY_DEEPMIND_S6_PROFILER_TEST_UTIL_H_ 67 | -------------------------------------------------------------------------------- /src/event_counters.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2021 The s6 Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS-IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include "event_counters.h" 16 | 17 | #include 18 | 19 | #include 20 | #include 21 | 22 | #include "absl/status/status.h" 23 | #include "google/protobuf/text_format.h" 24 | #include "utils/no_destructor.h" 25 | #include "utils/path.h" 26 | #include "utils/status_macros.h" 27 | 28 | namespace deepmind::s6 { 29 | 30 | EventCounters& EventCounters::Instance() { 31 | static NoDestructor instance; 32 | return *instance; 33 | } 34 | 35 | int64_t* EventCounters::GetEventCounter(absl::string_view counter_name) { 36 | absl::MutexLock lock(&mu_); 37 | auto& counter = counters_[GlobalInternTable::Instance().Intern(counter_name)]; 38 | if (!counter) counter = absl::make_unique(0); 39 | return counter.get(); 40 | } 41 | 42 | EventCountersProto EventCounters::Snapshot() { 43 | EventCountersProto proto; 44 | for (const auto& [name, value_ptr] : counters_) { 45 | EventCounterProto counter; 46 | counter.set_name(std::string(name.get())); 47 | counter.set_value(*value_ptr); 48 | *proto.add_counters() = std::move(counter); 49 | } 50 | return proto; 51 | } 52 | 53 | absl::Status EventCounters::DumpToDirectory(absl::string_view directory) { 54 | if (::mkdir(directory.data(), 0777) != 0) { 55 | // EEXIST is benign. Anything else we abort. 56 | if (errno != EEXIST) { 57 | return absl::InternalError( 58 | absl::StrCat("error creating profile directory: ", strerror(errno))); 59 | } 60 | } 61 | std::string filename = file::JoinPath( 62 | directory, absl::StrCat("event_counters.", getpid(), ".pbtxt")); 63 | 64 | // Don't use //base:file; we can't guarantee InitGoogle has been called or 65 | // completed. 66 | std::ofstream stream(filename); 67 | S6_RET_CHECK(stream.is_open()) << "Unable to open profiling output file"; 68 | 69 | std::string s; 70 | S6_RET_CHECK(google::protobuf::TextFormat::PrintToString(Snapshot(), &s)); 71 | stream << s; 72 | return absl::OkStatus(); 73 | } 74 | 75 | } // namespace deepmind::s6 76 | -------------------------------------------------------------------------------- /src/strongjit/instruction.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2021 The s6 Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS-IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include "strongjit/instruction.h" 16 | 17 | #include "strongjit/function.h" 18 | #include "strongjit/instruction_traits.h" 19 | 20 | namespace deepmind::s6 { 21 | 22 | namespace { 23 | struct InstrCloner { 24 | template 25 | static absl::optional Visit( 26 | const Instruction* inst, Function& function, 27 | const absl::flat_hash_map& mapping) { 28 | if (inst->kind() != InstrType::kKind) return {}; 29 | 30 | auto cloned_instr = function.Create(*cast(inst)); 31 | 32 | // Update operands using the value mapping. 33 | for (auto& op : cloned_instr->mutable_operands()) { 34 | auto find_it = mapping.find(op); 35 | if (find_it != mapping.end()) { 36 | op = find_it->second; 37 | } 38 | } 39 | 40 | return cloned_instr; 41 | } 42 | 43 | static Instruction* Default( 44 | const Instruction* instr, Function& function, 45 | const absl::flat_hash_map& mapping) { 46 | S6_LOG(FATAL) << "Unknown instruction kind in clone"; 47 | return nullptr; 48 | } 49 | }; 50 | } // namespace 51 | 52 | // Provide vtable anchor. 53 | InstructionModificationListener::~InstructionModificationListener() {} 54 | 55 | void Instruction::CallListenerErased() { 56 | if (!function_) return; 57 | if (auto listener = function_->listener()) { 58 | listener->InstructionErased(this); 59 | } 60 | } 61 | 62 | void Instruction::CallListenerMutated() { 63 | if (!function_) return; 64 | if (auto listener = function_->listener()) { 65 | listener->OperandsMayBeModified(this); 66 | } 67 | } 68 | 69 | Instruction* Instruction::CloneWithNewOperands( 70 | Function& function, 71 | const absl::flat_hash_map& mapping) const { 72 | auto clone = ForAllInstructionKinds(this, function, mapping); 73 | S6_CHECK(clone != nullptr) << "Null pointer returned by InstrCloner"; 74 | return clone; 75 | } 76 | 77 | } // namespace deepmind::s6 78 | -------------------------------------------------------------------------------- /src/strongjit/block.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2021 The s6 Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS-IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include "strongjit/block.h" 16 | 17 | #include "strongjit/function.h" 18 | #include "strongjit/instructions.h" 19 | 20 | namespace deepmind::s6 { 21 | 22 | void Block::insert(iterator insert_pt, Instruction* inst) { 23 | instructions_.insert(insert_pt, inst); 24 | inst->parent_ = this; 25 | ++size_; 26 | } 27 | 28 | void Block::UnlinkInstruction(Instruction* inst) { 29 | instructions_.erase(inst); 30 | inst->parent_ = nullptr; 31 | --size_; 32 | } 33 | 34 | void Block::RemoveFromParent() { parent_->UnlinkBlock(this); } 35 | 36 | void Block::erase() { 37 | S6_CHECK(parent_); 38 | parent_->UnlinkBlock(this); 39 | } 40 | 41 | Block* Block::Split(iterator split_point) { 42 | Block* b = parent_->CreateBlock(std::next(GetIterator())); 43 | b->splice(b->begin(), this, split_point, end()); 44 | 45 | Create(b); 46 | b->AddPredecessor(this); 47 | 48 | if (b->GetTerminator()) { 49 | for (Block* succ : b->GetTerminator()->successors()) { 50 | succ->ReplacePredecessor(this, b); 51 | } 52 | } 53 | return b; 54 | } 55 | 56 | void Block::splice(iterator insert_pt, Block* other) { 57 | splice(insert_pt, other, other->instructions_.begin(), 58 | other->instructions_.end()); 59 | } 60 | 61 | void Block::splice(iterator insert_pt, Block* other, iterator other_begin, 62 | iterator other_end) { 63 | for (auto it = other_begin; it != other_end; ++it) { 64 | it->parent_ = this; 65 | ++size_; 66 | --other->size_; 67 | } 68 | instructions_.splice(insert_pt, other->instructions_, other_begin, other_end); 69 | } 70 | 71 | const TerminatorInst* Block::GetTerminator() const { 72 | if (empty() || !isa(*rbegin())) return nullptr; 73 | return cast(&*rbegin()); 74 | } 75 | 76 | TerminatorInst* Block::GetTerminator() { 77 | if (empty() || !isa(*rbegin())) return nullptr; 78 | return cast(&*rbegin()); 79 | } 80 | 81 | BlockArgument* Block::CreateBlockArgument() { 82 | return parent_->CreateBlockArgument(this); 83 | } 84 | } // namespace deepmind::s6 85 | -------------------------------------------------------------------------------- /src/strongjit/optimize_constants.h: -------------------------------------------------------------------------------- 1 | // Copyright 2021 The s6 Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS-IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #ifndef THIRD_PARTY_DEEPMIND_S6_STRONGJIT_OPTIMIZE_CONSTANTS_H_ 16 | #define THIRD_PARTY_DEEPMIND_S6_STRONGJIT_OPTIMIZE_CONSTANTS_H_ 17 | 18 | #include 19 | 20 | #include 21 | 22 | #include "absl/status/status.h" 23 | #include "strongjit/base.h" 24 | #include "strongjit/instructions.h" 25 | #include "strongjit/optimizer_util.h" 26 | 27 | // This file provide some constant folding as patterns. For some of them it is 28 | // somewhat redundant with the nullconst analysis but they can be applied as 29 | // clean-up at any stage of the optimization process so they are still useful. 30 | // Some other, like cleaning up BrInst or DeoptimzizeIfSafepoint are critical 31 | // and relied upon by the nullconst optimization 32 | 33 | namespace deepmind::s6 { 34 | 35 | // Replaces "cmp i64 (constant), (constant)" by a constant. 36 | class ConstantFoldCompareInstPattern 37 | : public PatternT { 38 | public: 39 | static absl::Status Apply(CompareInst* cmp, Rewriter& rewriter); 40 | }; 41 | 42 | // Replaces (br (constant)) -> (jmp) 43 | class ConstantFoldBrInstPattern 44 | : public PatternT { 45 | public: 46 | static absl::Status Apply(BrInst* br, Rewriter& rewriter); 47 | }; 48 | 49 | // Replaces (unbox (constant)) -> (constant). 50 | // 51 | // Also fixes up any `overflowed?` that immediately follows the unbox. 52 | class ConstantFoldUnboxPattern 53 | : public PatternT { 54 | public: 55 | static absl::Status Apply(UnboxInst* unbox, Rewriter& rewriter); 56 | }; 57 | 58 | // Delete (deoptimize_if_safepoint (constant)) if possible. 59 | class ConstantFoldDeoptimizeIfSafepointInstPattern 60 | : public PatternT { 62 | public: 63 | static absl::Status Apply(DeoptimizeIfSafepointInst* deopt, 64 | Rewriter& rewriter); 65 | }; 66 | 67 | } // namespace deepmind::s6 68 | 69 | #endif // THIRD_PARTY_DEEPMIND_S6_STRONGJIT_OPTIMIZE_CONSTANTS_H_ 70 | -------------------------------------------------------------------------------- /src/code_object.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2021 The s6 Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS-IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include "code_object.h" 16 | 17 | #include 18 | 19 | #include "absl/strings/str_format.h" 20 | #include "core_util.h" 21 | #include "udis86/udis86.h" 22 | 23 | namespace deepmind::s6 { 24 | 25 | std::string BytecodeInstruction::ToString() const { 26 | return absl::StrFormat("@%d:\t%s\t%d", program_offset(), 27 | BytecodeOpcodeToString(opcode()), argument()); 28 | } 29 | 30 | CodeObject::~CodeObject() { 31 | if (allocator_) allocator_->Free(reinterpret_cast(body_ptr_)); 32 | } 33 | 34 | std::string CodeObject::ToString() const { 35 | std::string s; 36 | for (const BytecodeInstruction& inst : program_) { 37 | absl::StrAppend(&s, inst.ToString(), "\n"); 38 | } 39 | return s; 40 | } 41 | 42 | std::string CodeObject::Disassemble() const { 43 | uint64_t address = reinterpret_cast(body_ptr_); 44 | uint64_t end = address + body_size_; 45 | 46 | ud_t ud_obj; 47 | ud_init(&ud_obj); 48 | ud_set_mode(&ud_obj, 64); 49 | ud_set_input_buffer(&ud_obj, reinterpret_cast(body_ptr_), 50 | body_size_); 51 | ud_set_pc(&ud_obj, address); 52 | ud_set_syntax(&ud_obj, UD_SYN_INTEL); 53 | 54 | std::string s; 55 | while (unsigned int size = ud_disassemble(&ud_obj)) { 56 | if (auto it = debug_annotations_.find(reinterpret_cast(address)); 57 | it != debug_annotations_.end()) { 58 | if (!it->second.is_code()) { 59 | // Format the rest of the program as data. This is the constant pool. 60 | absl::StrAppend(&s, "// \n"); 61 | while (address < end) { 62 | absl::StrAppendFormat(&s, "%#05x: %08x\n", address, 63 | *reinterpret_cast(address)); 64 | address += sizeof(void*); 65 | } 66 | break; 67 | } 68 | absl::StrAppendFormat(&s, "// %s\n", it->second.ToString()); 69 | } 70 | absl::StrAppendFormat(&s, "%#05x: %s\n", address, ud_insn_asm(&ud_obj)); 71 | address += size; 72 | } 73 | absl::StrAppendFormat(&s, "%#05x: \n", address); 74 | return s; 75 | } 76 | } // namespace deepmind::s6 77 | -------------------------------------------------------------------------------- /src/strongjit/dis6_test.py: -------------------------------------------------------------------------------- 1 | # Copyright 2021 The s6 Authors. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http:#www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS-IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | """Tests for s6.strongjit.dis6.""" 16 | 17 | from absl.testing import absltest 18 | from absl.testing import parameterized 19 | 20 | from s6.strongjit import dis6 21 | from pybind11_abseil import status 22 | 23 | 24 | # Each test checks if the first line, the definition of the function, is 25 | # correct. 26 | class Dis6Test(parameterized.TestCase): 27 | 28 | def test_dis6_function(self): 29 | 30 | def foo(): 31 | pass 32 | 33 | ir_string = dis6.dis6(foo) 34 | self.assertNotEmpty(ir_string) 35 | self.assertTrue(ir_string.startswith('function foo {')) 36 | 37 | def test_dis6_lambda(self): 38 | x = lambda a: a + 1 39 | 40 | ir_string = dis6.dis6(x) 41 | self.assertNotEmpty(ir_string) 42 | self.assertTrue(ir_string.startswith('function {')) 43 | 44 | def test_dis6_bound_method(self): 45 | 46 | class Foo: 47 | 48 | def bar(self): 49 | pass 50 | 51 | foo = Foo() 52 | ir_string = dis6.dis6(foo.bar) 53 | self.assertNotEmpty(ir_string) 54 | self.assertTrue(ir_string.startswith('function bar {')) 55 | 56 | def test_dis6_unbound_method(self): 57 | 58 | class Foo: 59 | 60 | def bar(): 61 | pass 62 | 63 | ir_string = dis6.dis6(Foo.bar) 64 | self.assertNotEmpty(ir_string) 65 | self.assertTrue(ir_string.startswith('function bar {')) 66 | 67 | def test_dis6_callable(self): 68 | 69 | class Foo: 70 | 71 | def __call__(): 72 | pass 73 | 74 | foo = Foo() 75 | ir_string = dis6.dis6(foo.__call__) 76 | self.assertNotEmpty(ir_string) 77 | self.assertTrue(ir_string.startswith('function __call__ {')) 78 | 79 | class A: 80 | pass 81 | 82 | @parameterized.parameters(1, 'test123', A, A()) 83 | def test_dis6_invalid_argument(self, obj): 84 | if obj == self.A: 85 | with self.assertRaisesRegex(status.StatusNotOk, 86 | 'Argument must be a function or method.'): 87 | dis6.dis6(obj) 88 | else: 89 | with self.assertRaisesRegex(TypeError, 90 | 'incompatible function arguments.'): 91 | dis6.dis6(obj) 92 | 93 | 94 | if __name__ == '__main__': 95 | absltest.main() 96 | -------------------------------------------------------------------------------- /src/strongjit/instructions.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2021 The s6 Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS-IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include "strongjit/instructions.h" 16 | 17 | #include 18 | 19 | #include "strongjit/base.h" 20 | #include "strongjit/instruction_traits.h" 21 | #include "strongjit/value_casts.h" 22 | 23 | namespace deepmind::s6 { 24 | 25 | namespace { 26 | template 27 | struct PreciseLocationValues { 28 | static constexpr bool value = 29 | std::is_base_of::value; 30 | }; 31 | 32 | struct PreciseLocationPtr { 33 | template 34 | static absl::optional Visit(Value* inst) { 35 | if (I::kKind != inst->kind()) { 36 | return {}; 37 | } 38 | return static_cast(static_cast(inst)); 39 | } 40 | static PreciseLocationInst* Default(Value*) { return nullptr; } 41 | }; 42 | } // namespace 43 | 44 | const PreciseLocationInst* PreciseLocationInst::Get(const Value* inst) { 45 | return ForFilteredValues( 46 | const_cast(inst)); 47 | } 48 | 49 | PreciseLocationInst* PreciseLocationInst::Get(Value* inst) { 50 | return ForFilteredValues(inst); 51 | } 52 | 53 | absl::Span TerminatorInst::successors() { 54 | return span_cast(operands()).subspan(0, successor_size()); 55 | } 56 | 57 | absl::Span TerminatorInst::successors() const { 58 | return span_cast(operands()).subspan(0, successor_size()); 59 | } 60 | absl::Span TerminatorInst::mutable_successors() { 61 | return span_cast(mutable_operands().subspan(0, successor_size())); 62 | } 63 | 64 | const Block* UnconditionalTerminatorInst::unique_successor() const { 65 | return cast(operands().front()); 66 | } 67 | 68 | Block* UnconditionalTerminatorInst::unique_successor() { 69 | return cast(operands().front()); 70 | } 71 | 72 | Block* ConditionalTerminatorInst::true_successor() { 73 | return cast(operands().at(0)); 74 | } 75 | 76 | const Block* ConditionalTerminatorInst::true_successor() const { 77 | return cast(operands().at(0)); 78 | } 79 | 80 | Block* ConditionalTerminatorInst::false_successor() { 81 | return cast(operands().at(1)); 82 | } 83 | 84 | const Block* ConditionalTerminatorInst::false_successor() const { 85 | return cast(operands().at(1)); 86 | } 87 | 88 | } // namespace deepmind::s6 89 | -------------------------------------------------------------------------------- /src/utils/no_destructor.h: -------------------------------------------------------------------------------- 1 | // Copyright 2021 The s6 Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS-IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #ifndef THIRD_PARTY_DEEPMIND_S6_UTILS_NO_DESTRUCTOR_H_ 16 | #define THIRD_PARTY_DEEPMIND_S6_UTILS_NO_DESTRUCTOR_H_ 17 | 18 | #include 19 | 20 | namespace deepmind::s6 { 21 | 22 | // NoDestructor is a wrapper around an object of type T that stores the 23 | // object in an internal buffer and allows access through optional-like 24 | // const/non-const accessors. 25 | // 26 | // NoDestructor never calls T's destructor, NoDestructor objects created 27 | // on the stack or as member variables will lead to memory/resource leaks. 28 | // 29 | // NoDestructor is useful for on-demand construction of objects with static 30 | // storage where destruction never happens. 31 | // using NoDestructor avoids having to use heap-storage and memory 32 | // indirection. 33 | // 34 | // const std::string& () { 35 | // static const std::string* x = new std::string("hello"); 36 | // return *x; 37 | // } 38 | // 39 | // Can be replaced with: 40 | // 41 | // const std::string& MyString() { 42 | // static const NoDestructor x("hello"); 43 | // return *x; 44 | // } 45 | // 46 | template 47 | class NoDestructor { 48 | public: 49 | template 50 | explicit NoDestructor(Ts&&... ts) { 51 | new (&buffer_) T(std::forward(ts)...); 52 | } 53 | 54 | // Prevent copy and move. 55 | NoDestructor(const NoDestructor&) = delete; 56 | NoDestructor& operator=(const NoDestructor&) = delete; 57 | NoDestructor(NoDestructor&&) = delete; 58 | NoDestructor& operator=(NoDestructor&&) = delete; 59 | 60 | // Forwards move construction for T. 61 | // Needed for initialiser-list construction. 62 | explicit NoDestructor(T&& t) { new (&buffer_) T(std::move(t)); } 63 | 64 | // std::optional-like const accessors. 65 | // Never returns nullptr. 66 | const T* get() const { return reinterpret_cast(&buffer_); } 67 | const T* operator->() const { return get(); } 68 | const T& operator*() const { return *get(); } 69 | 70 | // std::optional-like non-const accessors. 71 | // Never returns nullptr. 72 | T* get() { return reinterpret_cast(&buffer_); } 73 | T* operator->() { return get(); } 74 | T& operator*() { return *get(); } 75 | 76 | private: 77 | alignas(T) unsigned char buffer_[sizeof(T)]; 78 | }; 79 | 80 | } // namespace deepmind::s6 81 | 82 | #endif // THIRD_PARTY_DEEPMIND_S6_UTILS_NO_DESTRUCTOR_H_ 83 | -------------------------------------------------------------------------------- /src/utils/status_macros.h: -------------------------------------------------------------------------------- 1 | // Copyright 2021 The s6 Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS-IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #ifndef LEARNING_DEEPMIND_S6_UTILS_STATUS_MACROS_H_ 16 | #define LEARNING_DEEPMIND_S6_UTILS_STATUS_MACROS_H_ 17 | 18 | #include "utils/logging.h" 19 | #include "utils/status_builder.h" 20 | 21 | #define S6_RETURN_IF_ERROR(...) \ 22 | do { \ 23 | auto _status = (__VA_ARGS__); \ 24 | if (S6_PREDICT_FALSE(!_status.ok())) return _status; \ 25 | } while (0) 26 | 27 | #define S6_STATUS_MACROS_CONCAT_NAME(x, y) S6_STATUS_MACROS_CONCAT_IMPL(x, y) 28 | #define S6_STATUS_MACROS_CONCAT_IMPL(x, y) x##y 29 | 30 | #define S6_ASSIGN_OR_RETURN(lhs, rexpr) \ 31 | S6_ASSIGN_OR_RETURN_IMPL( \ 32 | S6_STATUS_MACROS_CONCAT_NAME(_status_or_value, __COUNTER__), lhs, rexpr) 33 | 34 | #define S6_ASSIGN_OR_RETURN_IMPL(statusor, lhs, rexpr) \ 35 | auto statusor = (rexpr); \ 36 | if (S6_PREDICT_FALSE(!statusor.ok())) { \ 37 | return statusor.status(); \ 38 | } \ 39 | lhs = std::move(*statusor) 40 | 41 | #define S6_ASSERT_OK_AND_ASSIGN(lhs, rexpr) \ 42 | S6_ASSERT_OK_AND_ASSIGN_IMPL( \ 43 | S6_STATUS_MACROS_CONCAT_NAME(_status_or_value, __COUNTER__), lhs, rexpr) 44 | 45 | #define S6_ASSERT_OK_AND_ASSIGN_IMPL(statusor, lhs, rexpr) \ 46 | auto statusor = (rexpr); \ 47 | if (S6_PREDICT_FALSE(!statusor.ok())) { \ 48 | S6_LOG(FATAL) << "Assignment failed " << #rexpr; \ 49 | } \ 50 | lhs = std::move(*statusor) 51 | 52 | #define S6_ASSERT_OK(status_expr) \ 53 | if (S6_PREDICT_FALSE(!(status_expr).ok())) { \ 54 | S6_LOG(FATAL) << "Not OK " << #status_expr; \ 55 | } 56 | 57 | #define S6_RET_CHECK(condition) \ 58 | if (!(condition)) \ 59 | return deepmind::s6::StatusBuilder(absl::StatusCode::kInternal, \ 60 | " Check failed: ", #condition) 61 | 62 | #define S6_RET_CHECK_FAIL(condition) \ 63 | return deepmind::s6::StatusBuilder(absl::StatusCode::kInternal) 64 | 65 | #define S6_RET_CHECK_EQ(lhs, rhs) S6_RET_CHECK(lhs == rhs) 66 | 67 | #endif // LEARNING_DEEPMIND_S6_UTILS_STATUS_MACROS_H_ 68 | -------------------------------------------------------------------------------- /src/strongjit/value_casts.h: -------------------------------------------------------------------------------- 1 | // Copyright 2021 The s6 Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS-IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #ifndef THIRD_PARTY_DEEPMIND_S6_STRONGJIT_VALUE_CASTS_H_ 16 | #define THIRD_PARTY_DEEPMIND_S6_STRONGJIT_VALUE_CASTS_H_ 17 | 18 | #include "absl/types/span.h" 19 | #include "strongjit/value_traits.h" 20 | #include "utils/logging.h" 21 | namespace deepmind::s6 { 22 | class Value; 23 | 24 | //////////////////////////////////////////////////////////////////////////////// 25 | // Casting - isa, dyn_cast, cast 26 | 27 | // These helpers mirror those in LLVM - they can be used for lightweight 28 | // dynamic_casting. Note that all of these functions return sane values on 29 | // nullptr. 30 | 31 | template 32 | bool isa(const Value* v) { 33 | if (!v) return false; 34 | return ValueTraits::IsA(*v); 35 | } 36 | 37 | template 38 | bool isa(const Value& v) { 39 | return ValueTraits::IsA(v); 40 | } 41 | 42 | template 43 | const T* cast(const Value* v) { 44 | if (!v) return nullptr; 45 | S6_DCHECK(isa(v)); 46 | return reinterpret_cast(v); 47 | } 48 | 49 | template 50 | const T& cast(const Value& v) { 51 | S6_DCHECK(isa(v)); 52 | return reinterpret_cast(v); 53 | } 54 | 55 | template 56 | T* cast(Value* v) { 57 | if (!v) return nullptr; 58 | S6_DCHECK(isa(v)); 59 | return reinterpret_cast(v); 60 | } 61 | 62 | template 63 | T& cast(Value& v) { // NOLINT 64 | S6_DCHECK(isa(v)); 65 | return reinterpret_cast(v); 66 | } 67 | 68 | template 69 | const T* dyn_cast(const Value* v) { 70 | return isa(v) ? reinterpret_cast(v) : nullptr; 71 | } 72 | 73 | template 74 | T* dyn_cast(Value* v) { 75 | return isa(v) ? reinterpret_cast(v) : nullptr; 76 | } 77 | 78 | template 79 | absl::Span span_cast(absl::Span s) { 80 | return absl::MakeSpan(reinterpret_cast(s.data()), s.size()); 81 | } 82 | 83 | template 84 | absl::Span span_cast(absl::Span s) { 85 | return absl::MakeSpan(reinterpret_cast(s.data()), s.size()); 86 | } 87 | 88 | template 89 | absl::Span span_cast(absl::Span s) { 90 | return absl::MakeSpan(reinterpret_cast(s.data()), s.size()); 91 | } 92 | 93 | } // namespace deepmind::s6 94 | 95 | #endif // THIRD_PARTY_DEEPMIND_S6_STRONGJIT_VALUE_CASTS_H_ 96 | -------------------------------------------------------------------------------- /src/event_counters.h: -------------------------------------------------------------------------------- 1 | // Copyright 2021 The s6 Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS-IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #ifndef THIRD_PARTY_DEEPMIND_S6_EVENT_COUNTERS_H_ 16 | #define THIRD_PARTY_DEEPMIND_S6_EVENT_COUNTERS_H_ 17 | 18 | #include 19 | #include 20 | #include 21 | 22 | #include "absl/container/flat_hash_map.h" 23 | #include "absl/status/status.h" 24 | #include "absl/strings/string_view.h" 25 | #include "absl/synchronization/mutex.h" 26 | #include "event_counters.pb.h" 27 | #include "global_intern_table.h" 28 | #include "utils/no_destructor.h" 29 | 30 | namespace deepmind::s6 { 31 | 32 | // Singleton class for managing event counters. 33 | // 34 | // Event counters count the number of times that an event occurs. This could be 35 | // in compiled code, in the runtime, or during compilation. Each event counter 36 | // has an associated name, which enables the event point to be incremented from 37 | // several locations. 38 | // 39 | // Once allocated, the underlying `int64_t*` counter is guaranteed not to move, 40 | // which means that its value is safe to encode directly in generated asm. 41 | class EventCounters { 42 | public: 43 | // Return the `EventCounters` singleton instance. 44 | static EventCounters& Instance(); 45 | 46 | const absl::flat_hash_map>& 48 | counters() const { 49 | return counters_; 50 | } 51 | 52 | // Get a event counter by name. The event counter is allocated and initialized 53 | // to zero if needed. 54 | int64_t* GetEventCounter(absl::string_view counter_name); 55 | 56 | // Convenience function to get or create a counter and add `n`. 57 | void Add(absl::string_view counter_name, int64_t n) { 58 | *GetEventCounter(counter_name) += n; 59 | } 60 | 61 | // Creates an EventCountersProto. 62 | EventCountersProto Snapshot(); 63 | 64 | // Dumps the event counters to the given directory. A new file is created 65 | // within this directory. The directory is created if required. 66 | absl::Status DumpToDirectory(absl::string_view directory); 67 | 68 | private: 69 | EventCounters() = default; 70 | ~EventCounters() = delete; 71 | 72 | EventCounters& operator=(const EventCounters&) = delete; 73 | EventCounters(const EventCounters&) = delete; 74 | 75 | friend class NoDestructor; 76 | 77 | absl::flat_hash_map> 79 | counters_; 80 | absl::Mutex mu_; 81 | }; 82 | 83 | } // namespace deepmind::s6 84 | 85 | #endif // THIRD_PARTY_DEEPMIND_S6_EVENT_COUNTERS_H_ 86 | -------------------------------------------------------------------------------- /src/classes/class_manager.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2021 The s6 Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS-IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include "classes/class_manager.h" 16 | 17 | #include 18 | 19 | #include "classes/class.h" 20 | #include "core_util.h" 21 | #include "utils/no_destructor.h" 22 | 23 | namespace deepmind::s6 { 24 | 25 | Class* ClassManager::GetClassById(int64_t id) { 26 | absl::ReaderMutexLock lock(&mu_); 27 | // The zero ID is reserved, so the index into classes_ is id - 1. 28 | int64_t class_index = id - 1; 29 | if (class_index < 0 || class_index >= classes_.size()) return nullptr; 30 | return classes_[class_index]; 31 | } 32 | 33 | const Class* ClassManager::GetClassById(int64_t id) const { 34 | absl::ReaderMutexLock lock(&mu_); 35 | // The zero ID is reserved, so the index into classes_ is id - 1. 36 | int64_t class_index = id - 1; 37 | if (class_index < 0 || class_index >= classes_.size()) return nullptr; 38 | return classes_[class_index]; 39 | } 40 | 41 | absl::StatusOr ClassManager::AllocateNewClassId() { 42 | int64_t current_num_ids = classes_.size() + 1; 43 | if (current_num_ids == kMaxClassId) { 44 | return absl::ResourceExhaustedError("Maximum number of classes reached"); 45 | } 46 | return current_num_ids; 47 | } 48 | 49 | ClassManager& ClassManager::Instance() { 50 | static NoDestructor instance; 51 | return *instance; 52 | } 53 | 54 | InternedString ClassManager::InternString(absl::string_view str) { 55 | absl::MutexLock lock(&table_mu_); 56 | return InternedString(intern_table_.Insert(str).value); 57 | } 58 | 59 | InternedString ClassManager::InternString(PyObject* unicode_object) { 60 | S6_CHECK(PyUnicode_Check(unicode_object)); 61 | if (!PyUnicode_CHECK_INTERNED(unicode_object)) { 62 | return InternString(GetObjectAsCheapStringRequiringGil(unicode_object)); 63 | } 64 | if (PyUnicode_CHECK_INTERNED(unicode_object) == SSTATE_INTERNED_MORTAL) { 65 | PyUnicode_InternImmortal(&unicode_object); 66 | } 67 | absl::MutexLock lock(&mu_); 68 | if (auto it = interned_unicode_objects_.find(unicode_object); 69 | it != interned_unicode_objects_.end()) { 70 | return it->second; 71 | } 72 | InternedString s = InternString(GetObjectAsCheapString(unicode_object)); 73 | S6_CHECK(!s.empty()); 74 | // Note that this is racy, but InternString by definition is idempotent, so 75 | // we could only ever race `s` with `s`. 76 | interned_unicode_objects_.emplace(unicode_object, s); 77 | return s; 78 | } 79 | 80 | ClassManager::TypeModificationClient::~TypeModificationClient() {} 81 | 82 | ClassManager::~ClassManager() { 83 | for (Class* cls : classes_) { 84 | delete cls; 85 | } 86 | } 87 | 88 | } // namespace deepmind::s6 89 | -------------------------------------------------------------------------------- /src/utils/interval_map_test.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2021 The s6 Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS-IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include "utils/interval_map.h" 16 | 17 | #include "gmock/gmock.h" 18 | #include "gtest/gtest.h" 19 | 20 | namespace deepmind::s6 { 21 | namespace { 22 | using testing::ElementsAre; 23 | using testing::Key; 24 | using testing::Pair; 25 | 26 | TEST(IntervalMap, NonOverlapping) { 27 | IntervalMap map; 28 | map.Set(4, 10, "hello"); 29 | map.Set(12, 14, "world"); 30 | map.Set(10, 12, "yes"); 31 | 32 | EXPECT_THAT(map, 33 | ElementsAre(Pair(Pair(4, 10), "hello"), Pair(Pair(10, 12), "yes"), 34 | Pair(Pair(12, 14), "world"))); 35 | } 36 | 37 | TEST(IntervalMap, Overlapping1) { 38 | IntervalMap map; 39 | map.Set(4, 10, "hello"); 40 | map.Set(2, 6, "world"); 41 | 42 | EXPECT_THAT( 43 | map, ElementsAre(Pair(Pair(2, 6), "world"), Pair(Pair(6, 10), "hello"))); 44 | } 45 | 46 | TEST(IntervalMap, Overlapping2) { 47 | IntervalMap map; 48 | map.Set(4, 10, "hello"); 49 | map.Set(6, 13, "world"); 50 | 51 | EXPECT_THAT( 52 | map, ElementsAre(Pair(Pair(4, 6), "hello"), Pair(Pair(6, 13), "world"))); 53 | } 54 | 55 | TEST(IntervalMap, Subsuming) { 56 | IntervalMap map; 57 | map.Set(4, 10, "hello"); 58 | map.Set(2, 14, "world"); 59 | 60 | EXPECT_THAT(map, ElementsAre(Pair(Pair(2, 14), "world"))); 61 | } 62 | 63 | TEST(IntervalMap, Erase) { 64 | IntervalMap map; 65 | map.Set(4, 10, "hello"); 66 | map.Erase(6, 8); 67 | 68 | EXPECT_THAT( 69 | map, ElementsAre(Pair(Pair(4, 6), "hello"), Pair(Pair(8, 10), "hello"))); 70 | } 71 | 72 | TEST(IntervalMap, Coalescing1) { 73 | IntervalMap map; 74 | map.Set(2, 6, "hello"); 75 | map.Set(6, 8, "hello"); 76 | 77 | EXPECT_THAT(map, ElementsAre(Pair(Pair(2, 8), "hello"))); 78 | } 79 | 80 | TEST(IntervalMap, Coalescing2) { 81 | IntervalMap map; 82 | map.Set(6, 8, "hello"); 83 | map.Set(2, 6, "hello"); 84 | 85 | EXPECT_THAT(map, ElementsAre(Pair(Pair(2, 8), "hello"))); 86 | } 87 | 88 | TEST(IntervalMap, NonEqualityComparableKeyDoesNotCoalesce) { 89 | class C { 90 | public: 91 | explicit C(std::string s) : s_(s) {} 92 | 93 | private: 94 | std::string s_; 95 | }; 96 | 97 | // Ensure that IntervalMap can compile. 98 | IntervalMap map; 99 | map.Set(6, 8, C("hello")); 100 | map.Set(2, 6, C("hello")); 101 | 102 | EXPECT_THAT(map, ElementsAre(Key(Pair(2, 6)), Key(Pair(6, 8)))); 103 | } 104 | 105 | } // namespace 106 | } // namespace deepmind::s6 107 | -------------------------------------------------------------------------------- /src/python/type_feedback_test.py: -------------------------------------------------------------------------------- 1 | # Copyright 2021 The s6 Authors. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http:#www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS-IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | """Tests for s6.python.type_feedback.""" 16 | 17 | import sys 18 | 19 | from absl.testing import absltest 20 | 21 | from s6.classes.python import classes 22 | from s6.python import type_feedback 23 | 24 | classes.adopt_existing_types() 25 | classes.adopt_new_types() 26 | 27 | 28 | def _type_feedback(f): 29 | return type_feedback.extract_from_code_object(f.__code__) 30 | 31 | 32 | def _only_nonempty_type_feedback(f): 33 | return list(filter(lambda x: x != "empty", _type_feedback(f)))[0] 34 | 35 | 36 | @absltest.skipIf(sys.version_info.minor >= 7, "CALL_METHOD not supported yet") 37 | class TypeFeedbackTest(absltest.TestCase): 38 | 39 | def test_binary_add_custom_class(self): 40 | 41 | def f(x): 42 | return x + x 43 | 44 | class C(object): 45 | 46 | def __add__(self, other): 47 | pass 48 | 49 | self.assertNotEqual(classes.classid(C()), 0) 50 | f(C()) 51 | f(C()) 52 | 53 | self.assertStartsWith(_only_nonempty_type_feedback(f), "monomorphic, C#") 54 | 55 | def test_binary_add_longs(self): 56 | 57 | def f(x): 58 | return x + x 59 | 60 | # Run f with only longs. 61 | for i in range(5): 62 | f(i) 63 | 64 | self.assertStartsWith(_only_nonempty_type_feedback(f), "monomorphic, int#") 65 | 66 | def test_binary_mul_multiple_types_is_polymorphic(self): 67 | 68 | def f(x, y): 69 | return x * y 70 | 71 | for i in range(5): 72 | f(i, float(i)) 73 | 74 | self.assertRegex( 75 | _only_nonempty_type_feedback(f), 76 | r"polymorphic, either float#\d+ or int#") 77 | 78 | def test_many_types_is_megamorphic(self): 79 | 80 | def f(x): 81 | return x * x 82 | 83 | for _ in range(5): 84 | 85 | # This is a new Class every time through the loop. 86 | class C(object): 87 | 88 | def __mul__(self, other): 89 | pass 90 | 91 | f(C()) 92 | 93 | self.assertEqual(_only_nonempty_type_feedback(f), "megamorphic") 94 | 95 | def test_getattr(self): 96 | 97 | def f(x): 98 | return x.__len__ 99 | 100 | f("abc") 101 | 102 | self.assertStartsWith(_only_nonempty_type_feedback(f), "monomorphic, str#") 103 | 104 | def test_setattr(self): 105 | 106 | def f(x): 107 | x.y = 2 108 | 109 | class C(object): 110 | pass 111 | 112 | class D(object): 113 | pass 114 | 115 | f(D()) 116 | f(C()) 117 | 118 | self.assertRegex( 119 | _only_nonempty_type_feedback(f), r"polymorphic, either D#\d+ or C#") 120 | 121 | 122 | if __name__ == "__main__": 123 | absltest.main() 124 | -------------------------------------------------------------------------------- /src/strongjit/cursor.h: -------------------------------------------------------------------------------- 1 | // Copyright 2021 The s6 Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS-IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #ifndef THIRD_PARTY_DEEPMIND_S6_STRONGJIT_CURSOR_H_ 16 | #define THIRD_PARTY_DEEPMIND_S6_STRONGJIT_CURSOR_H_ 17 | 18 | #include "strongjit/block.h" 19 | #include "strongjit/instruction.h" 20 | 21 | namespace deepmind::s6 { 22 | 23 | // A cursor pointing to a particular instruction within a Function. The cursor 24 | // guarantees that mutating the currently pointed to instruction does not 25 | // affect the next or previous instructions, even if the current instruction 26 | // is removed, or additional instructions are inserted immediately after or 27 | // before the current instruction. 28 | // 29 | // Iteration order is instructions within a block, and then blocks in program 30 | // order within a function. 31 | // 32 | // Next and previous positions are computed eagerly, so it is not safe to 33 | // erase any instructions other than the current instruction. 34 | class Cursor { 35 | public: 36 | // Creates an invalid cursor. 37 | Cursor() : curr_valid_(false) {} 38 | 39 | // Creates a cursor pointing at instruction. 40 | explicit Cursor(Instruction* instruction) 41 | : Cursor(instruction->GetIterator(), true) {} 42 | 43 | // Creates a cursor from an instruction iterator. 44 | explicit Cursor(Block::iterator curr) : Cursor(curr, true) {} 45 | 46 | // Returns the pointed to instruction. 47 | Instruction* GetInstruction() const { 48 | S6_CHECK(!Finished()); 49 | return &*curr_; 50 | } 51 | 52 | // Returns the block of the pointed to instruction. 53 | Block* GetBlock() const { 54 | S6_CHECK(!Finished()); 55 | return GetInstruction()->parent(); 56 | } 57 | 58 | // Returns the function being iterated over. 59 | Function* GetFunction() const { 60 | S6_CHECK(!Finished()); 61 | return GetBlock()->parent(); 62 | } 63 | 64 | // Returns true if the cursor has iterated past the first or last instruction 65 | // of the function. From this point on the cursor is invalid. 66 | bool Finished() const { return !curr_valid_; } 67 | 68 | // Moves the cursor back one instruction. 69 | void StepBackward() { *this = Cursor(prev_, prev_valid_); } 70 | 71 | // Moves the cursor forward one instruction. 72 | void StepForward() { *this = Cursor(next_, next_valid_); } 73 | 74 | private: 75 | Cursor(Block::iterator curr, bool curr_valid) 76 | : curr_(curr), curr_valid_(curr_valid) { 77 | if (curr_valid_) Update(); 78 | } 79 | 80 | // Update next and previous iterators, given current. 81 | void Update(); 82 | 83 | Block::iterator prev_; 84 | Block::iterator curr_; 85 | Block::iterator next_; 86 | bool prev_valid_ = false; 87 | bool curr_valid_ = false; 88 | bool next_valid_ = false; 89 | }; 90 | 91 | } // namespace deepmind::s6 92 | 93 | #endif // THIRD_PARTY_DEEPMIND_S6_STRONGJIT_CURSOR_H_ 94 | -------------------------------------------------------------------------------- /src/strongjit/instruction_traits.h: -------------------------------------------------------------------------------- 1 | // Copyright 2021 The s6 Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS-IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #ifndef THIRD_PARTY_DEEPMIND_S6_STRONGJIT_INSTRUCTION_TRAITS_H_ 16 | #define THIRD_PARTY_DEEPMIND_S6_STRONGJIT_INSTRUCTION_TRAITS_H_ 17 | 18 | #include "strongjit/value_traits.h" 19 | 20 | namespace deepmind::s6 { 21 | 22 | namespace detail { 23 | 24 | // Predicate whose value is true for all subclasses of Instruction, not 25 | // including Instruction itself. 26 | template 27 | struct IsInstructionSubclass { 28 | static constexpr bool value = std::is_base_of::value && 29 | !std::is_same::value; 30 | }; 31 | 32 | } // namespace detail 33 | 34 | //////////////////////////////////////////////////////////////////////////////// 35 | // ForAllInstructionKinds 36 | // 37 | // This allows iteration at compile time over all instruction kinds. 38 | 39 | template 40 | constexpr auto ForAllInstructionKinds(Args&&... args) { 41 | return ForFilteredValues( 42 | std::forward(args)...); 43 | } 44 | 45 | class InstructionTraits { 46 | public: 47 | static bool ClobbersAllRegisters(const Value& value) { 48 | return kClobbersAllRegisters.Contains(value.kind()); 49 | } 50 | 51 | static bool ProducesValue(const Value& value) { 52 | return kProducesValue.Contains(value.kind()); 53 | } 54 | 55 | static bool HasSideEffects(const Value& value) { 56 | return kHasSideEffects.Contains(value.kind()); 57 | } 58 | 59 | static bool HasPreciseLocation(const Value& value) { 60 | return ValueTraits::IsA(value); 61 | } 62 | 63 | private: 64 | template 65 | struct ProducesValuePredicate { 66 | static const bool value = InsnCls::kProducesValue; 67 | }; 68 | 69 | template 70 | struct HasSideEffectsPredicate { 71 | static const bool value = InsnCls::kHasSideEffects; 72 | }; 73 | 74 | template 75 | struct ClobbersAllRegistersPredicate { 76 | static const bool value = InsnCls::kClobbersAllRegisters; 77 | }; 78 | 79 | static constexpr ValueSet kProducesValue = 80 | ValueSet::FromPredicate(); 82 | 83 | static constexpr ValueSet kHasSideEffects = 84 | ValueSet::FromPredicate(); 86 | 87 | static constexpr ValueSet kClobbersAllRegisters = 88 | ValueSet::FromPredicate(); 90 | }; 91 | 92 | } // namespace deepmind::s6 93 | 94 | #endif // THIRD_PARTY_DEEPMIND_S6_STRONGJIT_INSTRUCTION_TRAITS_H_ 95 | -------------------------------------------------------------------------------- /src/runtime/interpreter_stub.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2021 The s6 Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS-IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include "runtime/interpreter_stub.h" 16 | 17 | #include 18 | 19 | #include "utils/logging.h" 20 | 21 | namespace deepmind::s6 { 22 | 23 | PyObject* StrongjitInterpreterStub(PyObject* pyfunc_object, ...) { 24 | S6_DCHECK(pyfunc_object); 25 | PyCodeObject* co = 26 | reinterpret_cast(PyFunction_GET_CODE(pyfunc_object)); 27 | bool is_varargs = co->co_flags & CO_VARARGS; 28 | bool is_varkeywords = co->co_flags & CO_VARKEYWORDS; 29 | 30 | int64_t n = co->co_argcount + is_varargs + is_varkeywords; 31 | PyObject** args = reinterpret_cast(alloca(n * sizeof(PyObject*))); 32 | va_list vl; 33 | va_start(vl, pyfunc_object); 34 | 35 | for (int64_t i = 0; i < n; ++i) { 36 | args[i] = va_arg(vl, PyObject*); 37 | } 38 | va_end(vl); 39 | return StrongjitInterpreterStubArrayArgs(pyfunc_object, n, args); 40 | } 41 | 42 | PyObject* StrongjitInterpreterStubArrayArgs(PyObject* pyfunc_object, int64_t n, 43 | PyObject** args) { 44 | PyCodeObject* co = 45 | reinterpret_cast(PyFunction_GET_CODE(pyfunc_object)); 46 | bool is_varargs = co->co_flags & CO_VARARGS; 47 | bool is_varkeywords = co->co_flags & CO_VARKEYWORDS; 48 | if (!is_varargs && !is_varkeywords) { 49 | PyObject* ret = _PyFunction_FastCallKeywords(pyfunc_object, args, n, 50 | /*kwnames=*/nullptr); 51 | // Strongjit expects us to steal a reference to args. 52 | // _PyFunction_FastCallKeywords does not, so simulate by decreffing all 53 | // arguments now. 54 | for (int64_t i = 0; i < n; ++i) { 55 | Py_DECREF(args[i]); 56 | } 57 | return ret; 58 | } 59 | 60 | // This is the variadic case. 61 | PyObject* star_args = nullptr; 62 | int64_t star_args_size = 0; 63 | if (is_varargs) { 64 | star_args = args[co->co_argcount]; 65 | S6_DCHECK(PyTuple_Check(star_args)); 66 | star_args_size = PyTuple_GET_SIZE(star_args); 67 | } 68 | PyObject* positional_args = PyTuple_New(star_args_size + co->co_argcount); 69 | 70 | for (int64_t i = 0; i < co->co_argcount; ++i) { 71 | PyTuple_SET_ITEM(positional_args, i, args[i]); 72 | } 73 | for (int64_t i = 0; i < star_args_size; ++i) { 74 | PyObject* v = PyTuple_GET_ITEM(star_args, i); 75 | Py_INCREF(v); 76 | PyTuple_SET_ITEM(positional_args, co->co_argcount + i, v); 77 | } 78 | 79 | PyObject* star_kwargs = nullptr; 80 | if (is_varkeywords) { 81 | star_kwargs = args[co->co_argcount + is_varargs]; 82 | } 83 | 84 | PyObject* result = PyObject_Call(pyfunc_object, positional_args, star_kwargs); 85 | Py_DECREF(positional_args); 86 | Py_XDECREF(star_args); 87 | Py_XDECREF(star_kwargs); 88 | return result; 89 | } 90 | 91 | } // namespace deepmind::s6 92 | -------------------------------------------------------------------------------- /src/runtime/generator.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2021 The s6 Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS-IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include "runtime/generator.h" 16 | 17 | #include 18 | #include 19 | 20 | #include 21 | 22 | #include "strongjit/formatter.h" 23 | #include "strongjit/instructions.h" 24 | 25 | namespace deepmind::s6 { 26 | 27 | void DeallocGeneratorState(GeneratorStateObject* obj) { 28 | obj->state.~GeneratorState(); 29 | } 30 | 31 | PyTypeObject GeneratorState_Type = { 32 | // clang-format off 33 | PyVarObject_HEAD_INIT(&PyType_Type, 0) 34 | // clang-format on 35 | "s6_internal.GeneratorState", /* tp_name */ 36 | sizeof(GeneratorStateObject), /* tp_basicsize */ 37 | sizeof(uint64_t), /* tp_itemsize */ 38 | reinterpret_cast(DeallocGeneratorState), /* tp_dealloc */ 39 | nullptr, /* tp_print */ 40 | nullptr, /* tp_getattr */ 41 | nullptr, /* tp_setattr */ 42 | nullptr, /* tp_as_async */ 43 | nullptr, /* tp_repr */ 44 | nullptr, /* tp_as_number */ 45 | nullptr, /* tp_as_sequence */ 46 | nullptr, /* tp_as_mapping */ 47 | nullptr, /* tp_hash */ 48 | nullptr, /* tp_call */ 49 | nullptr, /* tp_str */ 50 | nullptr, /* tp_getattro */ 51 | nullptr, /* tp_setattro */ 52 | nullptr, /* tp_as_buffer */ 53 | Py_TPFLAGS_DEFAULT, /* tp_flags */ 54 | "S6 generator state" /* tp_doc */ 55 | }; 56 | 57 | GeneratorState* GeneratorState::Create(PyFrameObject* frame, 58 | int64_t num_spill_slots) { 59 | GeneratorStateObject* state_obj = PyObject_NewVar( 60 | GeneratorStateObject, &GeneratorState_Type, num_spill_slots); 61 | new (&state_obj->state) GeneratorState(&state_obj->spill_slots[0]); 62 | 63 | // Store in frame->f_valuestack[0] and donate our reference to it. 64 | frame->f_valuestack[0] = reinterpret_cast(state_obj); 65 | frame->f_stacktop = &frame->f_valuestack[1]; 66 | return &state_obj->state; 67 | } 68 | 69 | void GeneratorState::EnsureValueMapCreated(const Function& f) { 70 | if (value_map_) return; 71 | value_map_ = new ValueMap(f); 72 | } 73 | 74 | void DeallocateGeneratorState(PyFrameObject* frame) { 75 | Py_CLEAR(frame->f_valuestack[0]); 76 | frame->f_stacktop = nullptr; 77 | } 78 | } // namespace deepmind::s6 79 | -------------------------------------------------------------------------------- /src/global_intern_table.h: -------------------------------------------------------------------------------- 1 | // Copyright 2021 The s6 Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS-IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #ifndef THIRD_PARTY_DEEPMIND_S6_GLOBAL_INTERN_TABLE_H_ 16 | #define THIRD_PARTY_DEEPMIND_S6_GLOBAL_INTERN_TABLE_H_ 17 | 18 | #include 19 | 20 | #include 21 | #include 22 | 23 | #include "absl/base/thread_annotations.h" 24 | #include "absl/strings/string_view.h" 25 | #include "absl/synchronization/mutex.h" 26 | #include "utils/keyed_intern_table.h" 27 | #include "utils/no_destructor.h" 28 | 29 | namespace deepmind::s6 { 30 | 31 | // Singleton maintaining a global table of interned null-terminated strings. 32 | // 33 | // Strings in the global intern table are guaranteed to be remain allocated at 34 | // a constant address for the lifetime of the process. This makes them useable 35 | // as trace event names. 36 | class GlobalInternTable { 37 | public: 38 | // Fetch the global intern table singleton. 39 | static GlobalInternTable& Instance(); 40 | 41 | // Opaque wrapper for an interned string. 42 | class InternedString { 43 | public: 44 | InternedString() = default; 45 | 46 | absl::string_view get() const { return absl::string_view(value_); } 47 | 48 | // Returns a non-owning raw pointer to the underlying string. This pointer 49 | // and the memory pointed to will not change or be deallocated in the 50 | // lifetime of the process, so it is safe to be used as an immediate value 51 | // in generated code. 52 | const char* AsPtr() const { return value_; } 53 | 54 | private: 55 | friend class GlobalInternTable; 56 | 57 | // Only GlobalInternTable can create InternedString instances with non-null 58 | // values. 59 | explicit InternedString(const char* value) : value_(value) {} 60 | 61 | // Raw pointer to a null terminated string owned by `intern_table_`. 62 | const char* value_ = nullptr; 63 | }; 64 | 65 | InternedString Intern(absl::string_view str) { 66 | absl::MutexLock lock(&mtx_); 67 | return InternedString(intern_table_->Insert(str).value); 68 | } 69 | 70 | private: 71 | GlobalInternTable() 72 | : intern_table_(std::make_unique>()) {} 73 | ~GlobalInternTable() = default; 74 | 75 | friend class NoDestructor; 76 | 77 | GlobalInternTable(const GlobalInternTable&) = delete; 78 | GlobalInternTable(const GlobalInternTable&&) = delete; 79 | GlobalInternTable& operator=(const GlobalInternTable&) = delete; 80 | 81 | std::unique_ptr> intern_table_ 82 | ABSL_GUARDED_BY(mtx_); 83 | absl::Mutex mtx_; 84 | }; 85 | 86 | inline bool operator==(GlobalInternTable::InternedString a, 87 | GlobalInternTable::InternedString b) { 88 | return a.AsPtr() == b.AsPtr(); 89 | } 90 | 91 | template 92 | H AbslHashValue(H h, const GlobalInternTable::InternedString& str) { 93 | return H::combine(std::move(h), reinterpret_cast(str.AsPtr())); 94 | } 95 | 96 | } // namespace deepmind::s6 97 | 98 | #endif // THIRD_PARTY_DEEPMIND_S6_GLOBAL_INTERN_TABLE_H_ 99 | -------------------------------------------------------------------------------- /src/api.h: -------------------------------------------------------------------------------- 1 | // Copyright 2021 The s6 Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS-IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #ifndef THIRD_PARTY_DEEPMIND_S6_API_H_ 16 | #define THIRD_PARTY_DEEPMIND_S6_API_H_ 17 | 18 | #include 19 | 20 | #include "absl/status/statusor.h" 21 | 22 | namespace deepmind::s6 { 23 | 24 | // Initializes S6. This performs once-only initialization: 25 | // * Consumes flags from the environment variable "S6FLAGS", if it exists. 26 | // * Ensures the stack rlimit is large enough in debug mode. 27 | // 28 | // JIT is not enabled. 29 | // `adopt_types`: Initializes with type adoption. Type adoption is required 30 | // for performance just-in-time compilation, but not required for profiling. 31 | // Turning this on provides a small performance penalty for all attribute 32 | // stores. 33 | absl::Status Initialize(bool adopt_types = false); 34 | 35 | // RAII object that enables to change temporarily change the main frame 36 | // evaluation function. The original frame evaluation function is restored when 37 | // the object is destroyed. 38 | // 39 | // These objects nest; enabling profiling within a JIT scope will enable both 40 | // profiling and JIT, for example. 41 | class EvalFrameScope { 42 | public: 43 | explicit EvalFrameScope(_PyFrameEvalFunction new_eval_frame_function); 44 | ~EvalFrameScope() { RestoreNow(); } 45 | 46 | // Retores the original frame evaluation function before destruction. 47 | void RestoreNow(); 48 | 49 | // Not copyable or moveable. 50 | EvalFrameScope(const EvalFrameScope&) = delete; 51 | EvalFrameScope& operator=(const EvalFrameScope&) const = delete; 52 | EvalFrameScope(EvalFrameScope&& other) = delete; 53 | EvalFrameScope& operator=(EvalFrameScope&&) = delete; 54 | 55 | private: 56 | // The eval function to restore when this scope exits. 57 | _PyFrameEvalFunction previous_; 58 | }; 59 | 60 | // RAII object that enables JIT compilation using S6 within a scope. Behavior of 61 | // this function is well defined within a threaded context. 62 | // 63 | // These objects nest; enabling profiling within a JIT scope will enable both 64 | // profiling and JIT, for example. 65 | class JitScope : public EvalFrameScope { 66 | public: 67 | JitScope(); 68 | }; 69 | 70 | // RAII object that enables JIT compilation using S6 within a scope, but then 71 | // forces the use of the evaluator. Behavior of this function is well defined 72 | // within a threaded context. 73 | // 74 | // These objects nest; enabling profiling within a JIT scope will enable both 75 | // profiling and JIT, for example. 76 | class EvaluatorScope : public EvalFrameScope { 77 | public: 78 | EvaluatorScope(); 79 | }; 80 | 81 | // RAII object that forces the use of S6 Interpreter. Behavior of this function 82 | // is well defined within a threaded context. 83 | // 84 | // These objects nest; enabling profiling within a JIT scope will enable both 85 | // profiling and JIT, for example. 86 | class InterpreterScope : public EvalFrameScope { 87 | public: 88 | InterpreterScope(); 89 | }; 90 | 91 | } // namespace deepmind::s6 92 | 93 | #endif // THIRD_PARTY_DEEPMIND_S6_API_H_ 94 | -------------------------------------------------------------------------------- /src/strongjit/value.h: -------------------------------------------------------------------------------- 1 | // Copyright 2021 The s6 Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS-IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #ifndef THIRD_PARTY_DEEPMIND_S6_STRONGJIT_VALUE_H_ 16 | #define THIRD_PARTY_DEEPMIND_S6_STRONGJIT_VALUE_H_ 17 | 18 | #include 19 | #include 20 | 21 | #include "absl/container/inlined_vector.h" 22 | #include "absl/types/span.h" 23 | #include "strongjit/util.h" 24 | 25 | namespace deepmind::s6 { 26 | // Value is the base of the strongjit hierarchy. All instructions, blocks and 27 | // block arguments are Values. A Value can be used by Instruction as an operand. 28 | // All Values are discriminated by Kind, which is an opcode. This 29 | // kind can be used by the helper functions isa<>, dyn_cast<> and cast<> to 30 | // perform run-time downcasting without RTTI overhead. 31 | class Value { 32 | public: 33 | // LINT.IfChange 34 | enum Kind : uint8_t { 35 | // Invalid value, distinct from the tombstone value. This will be 36 | // the default-constructed value and allows to determine invalid 37 | // iterators such as end(), distinct from tombstones. 38 | kSentinel = 0, 39 | kBlock, 40 | kBlockArgument, 41 | 42 | // Instructions 43 | kConstant, 44 | kLoad, 45 | kLoadGlobal, 46 | kStore, 47 | kIncref, 48 | kDecref, 49 | 50 | kCompare, 51 | 52 | // Unary instructions 53 | kNegate, 54 | kNot, 55 | kSext, 56 | 57 | // Binary instructions 58 | kAdd, 59 | kAnd, 60 | kDivide, 61 | kMultiply, 62 | kOr, 63 | kRemainder, 64 | kShiftLeft, 65 | kShiftRightSigned, 66 | kSubtract, 67 | kXor, 68 | 69 | kIntToFloat, 70 | kFrameVariable, 71 | kCallNative, 72 | kCallPython, 73 | kCallAttribute, 74 | kCallVectorcall, 75 | kBytecodeBegin, 76 | kDeoptimizeIfSafepoint, 77 | kCallNativeIndirect, 78 | kYieldValue, 79 | kBox, 80 | kUnbox, 81 | kOverflowed, 82 | kFloatZero, 83 | kRematerialize, 84 | kGetClassId, 85 | kGetObjectDict, 86 | kGetInstanceClassId, 87 | kCheckClassId, 88 | kLoadFromDict, 89 | kStoreToDict, 90 | kConstantAttribute, 91 | kDeoptimizedAsynchronously, 92 | kSetObjectClass, 93 | 94 | // Terminators 95 | kDeoptimizeIf, 96 | kJmp, 97 | kBr, 98 | kExcept, 99 | kUnreachable, 100 | kReturn, 101 | 102 | // Profiling instructions 103 | kAdvanceProfileCounter, 104 | kIncrementEventCounter, 105 | kTraceBegin, 106 | kTraceEnd, 107 | 108 | kMaxValue = kTraceEnd, 109 | }; 110 | // LINT.ThenChange(value_traits.h) 111 | 112 | Value() = default; 113 | Value& operator=(const Value&) = default; 114 | Value(const Value&) = default; 115 | Value& operator=(Value&&) = default; 116 | Value(Value&&) = default; 117 | 118 | virtual ~Value() = default; 119 | 120 | // Returns the kind of this Value. 121 | virtual Kind kind() const = 0; 122 | 123 | static constexpr Kind kKind = kSentinel; 124 | }; 125 | 126 | } // namespace deepmind::s6 127 | 128 | #endif // THIRD_PARTY_DEEPMIND_S6_STRONGJIT_VALUE_H_ 129 | -------------------------------------------------------------------------------- /src/runtime/deoptimization_map.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2021 The s6 Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS-IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include "runtime/deoptimization_map.h" 16 | 17 | #include 18 | 19 | #include "runtime/util.h" 20 | 21 | namespace deepmind::s6 { 22 | 23 | DeoptimizationMap::DeoptimizationMap(SlotIndexes slot_indexes) 24 | : slot_indexes_(std::move(slot_indexes)) { 25 | SlotIndexes::Slot end_slot = slot_indexes_.num_slots() + 1; 26 | for (const Value* v : slot_indexes_.ordered_values()) { 27 | const BytecodeBeginInst* bytecode_begin = dyn_cast(v); 28 | if (!bytecode_begin) continue; 29 | SlotIndexes::Slot slot = slot_indexes_.SlotForValue(bytecode_begin); 30 | fastlocals_.resize(std::max(fastlocals_.size(), 31 | bytecode_begin->fastlocals().size())); 32 | int64_t i = 0; 33 | for (const Value* fastlocal : bytecode_begin->fastlocals()) { 34 | IntervalMap& map = fastlocals_[i++]; 35 | map.SetAndCoalesce(slot, end_slot, fastlocal); 36 | } 37 | while (i != fastlocals_.size()) { 38 | IntervalMap& map = fastlocals_[i++]; 39 | map.Erase(slot, end_slot); 40 | } 41 | } 42 | } 43 | 44 | const Instruction* DeoptimizationMap::GetInstructionAtAddress( 45 | ProgramAddress program_point) const { 46 | const Instruction* inst; 47 | if (!instruction_addresses_.Lookup(program_point, &inst)) return nullptr; 48 | return inst; 49 | } 50 | 51 | IteratorRange 52 | DeoptimizationMap::live_values(ProgramAddress program_point) const { 53 | const Instruction* inst = GetInstructionAtAddress(program_point); 54 | return live_values(inst); 55 | } 56 | 57 | IteratorRange 58 | DeoptimizationMap::live_values(const Instruction* inst) const { 59 | auto it = live_values_.find(inst); 60 | if (it == live_values_.end()) { 61 | static std::vector empty; 62 | return {empty.begin(), empty.end()}; 63 | } 64 | return {it->second.begin(), it->second.end()}; 65 | } 66 | 67 | void DeoptimizationMap::AddInstructionAddress(const Instruction* inst, 68 | ProgramAddress begin, 69 | ProgramAddress end) { 70 | instruction_addresses_.Set(begin, end, inst); 71 | } 72 | 73 | void DeoptimizationMap::AddLiveValue(const Value* value, Location location, 74 | const Instruction* inst) { 75 | live_values_[inst].push_back(ValueAndLocation{value, location}); 76 | } 77 | 78 | std::vector DeoptimizationMap::GetLiveFastLocals( 79 | SlotIndexes::Slot slot) const { 80 | std::vector live; 81 | for (const IntervalMap& map : fastlocals_) { 82 | const Value* v = nullptr; 83 | if (!map.Lookup(slot, &v)) { 84 | // Live fastlocals are contiguous, so as soon as we find a map that does 85 | // not have a live fastlocal we have finished. 86 | return live; 87 | } 88 | live.push_back(v); 89 | } 90 | return live; 91 | } 92 | 93 | } // namespace deepmind::s6 94 | -------------------------------------------------------------------------------- /src/classes/python/classes.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2021 The s6 Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS-IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include "classes/class.h" 16 | #include "classes/object.h" 17 | #include "pybind11/pybind11.h" 18 | #include "pybind11/pytypes.h" 19 | #include "pybind11_abseil/status_casters.h" 20 | #include "utils/status_macros.h" 21 | namespace pybind11 { 22 | using ::deepmind::s6::Adopt; 23 | using ::deepmind::s6::AdoptExistingTypes; 24 | using ::deepmind::s6::AdoptNewTypes; 25 | using ::deepmind::s6::Class; 26 | using ::deepmind::s6::ClassManager; 27 | using ::deepmind::s6::GetClass; 28 | using ::deepmind::s6::GetClassId; 29 | using ::deepmind::s6::GetTypeAttributesAsMap; 30 | using ::deepmind::s6::IsAdoptable; 31 | 32 | dict AttributeMapToDict(const Class::AttributeMap& map) { 33 | dict d; 34 | for (const auto& [name, attr] : map) { 35 | handle h = Py_None; 36 | if (attr->value()) h = attr->value(); 37 | d[str(std::string(name))] = h; 38 | } 39 | return d; 40 | } 41 | 42 | PYBIND11_MODULE(classes, py_module) { 43 | pybind11::google::ImportStatusModule(); 44 | py_module.doc() = "Defines internal S6 functions related to classes"; 45 | 46 | py_module.def( 47 | "classid", [](object obj) { return GetClassId(obj.ptr()); }, 48 | "Returns the class ID of an object."); 49 | 50 | py_module.def( 51 | "class_is_valid", 52 | [](object obj) { 53 | Class* cls = GetClass(obj.ptr()); 54 | return cls != nullptr && !GetClass(obj.ptr())->invalid(); 55 | }, 56 | "Returns True if this object has a class and the class is valid."); 57 | 58 | py_module.def( 59 | "adoptable", [](object obj) { return IsAdoptable(obj.ptr()).ok(); }, 60 | "Returns True if the given object is Adoptable."); 61 | 62 | py_module.def( 63 | "adopt", [](object obj) { return Adopt(obj.ptr()); }, 64 | "Adopts the given object."); 65 | 66 | py_module.def( 67 | "adopt_new_types", []() { AdoptNewTypes(); }, 68 | "Attempts to adopt all newly created types."); 69 | 70 | py_module.def( 71 | "stop_adopting_new_types", 72 | []() { ::deepmind::s6::StopAdoptingNewTypes(); }, 73 | "Undoes adopt_new_types()."); 74 | 75 | py_module.def( 76 | "get_type_attributes", 77 | [](object obj) -> absl::StatusOr { 78 | S6_ASSIGN_OR_RETURN( 79 | Class::AttributeMap result, 80 | GetTypeAttributesAsMap(ClassManager::Instance(), 81 | reinterpret_cast(obj.ptr()))); 82 | return AttributeMapToDict(result); 83 | }, 84 | "Reads all attributes from the MRO of an object and returns them as a " 85 | "dict."); 86 | 87 | py_module.def( 88 | "get_class_attributes", 89 | [](object obj) -> absl::StatusOr { 90 | Class* cls = GetClass(obj.ptr()); 91 | S6_RET_CHECK(cls); 92 | return AttributeMapToDict(cls->attributes()); 93 | }, 94 | "Reads all attributes from the MRO of an object and returns them as a " 95 | "dict."); 96 | 97 | py_module.def( 98 | "adopt_existing_types", []() { AdoptExistingTypes(); }, 99 | "Attempts to adopt all existing subclasses of Type, transitively."); 100 | } 101 | 102 | } // namespace pybind11 103 | -------------------------------------------------------------------------------- /src/strongjit/optimize_constants.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2021 The s6 Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS-IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include "strongjit/optimize_constants.h" 16 | 17 | #include 18 | 19 | #include "strongjit/instructions.h" 20 | 21 | namespace deepmind::s6 { 22 | 23 | absl::Status ConstantFoldCompareInstPattern::Apply(CompareInst* cmp, 24 | Rewriter& rewriter) { 25 | if (cmp->IsDoubleType()) { 26 | return absl::FailedPreconditionError("operand type is double"); 27 | } 28 | 29 | auto lhs_or = GetValueAsConstantInt(cmp->lhs(), rewriter.code_object()); 30 | auto rhs_or = GetValueAsConstantInt(cmp->rhs(), rewriter.code_object()); 31 | if (!lhs_or.has_value() || !rhs_or.has_value()) { 32 | return absl::FailedPreconditionError("lhs or rhs is not a constant"); 33 | } 34 | 35 | int64_t lhs = *lhs_or; 36 | int64_t rhs = *rhs_or; 37 | 38 | Builder builder = rewriter.CreateBuilder(cmp->GetIterator()); 39 | rewriter.ReplaceAllUsesWith(*cmp, *builder.Bool(cmp->Evaluate(lhs, rhs))); 40 | rewriter.erase(*cmp); 41 | return absl::OkStatus(); 42 | } 43 | 44 | absl::Status ConstantFoldBrInstPattern::Apply(BrInst* br, Rewriter& rewriter) { 45 | auto cond = GetValueAsConstantInt(br->condition(), rewriter.code_object()); 46 | if (!cond.has_value()) return absl::FailedPreconditionError("not a constant"); 47 | bool true_successor = *cond != 0; 48 | rewriter.ConvertBranchToJump(*br, true_successor); 49 | return absl::OkStatus(); 50 | } 51 | 52 | absl::Status ConstantFoldUnboxPattern::Apply(UnboxInst* unbox, 53 | Rewriter& rewriter) { 54 | PyObject* boxed = 55 | GetValueAsConstantObject(unbox->boxed(), rewriter.code_object()); 56 | if (!boxed) return absl::FailedPreconditionError("not a constant"); 57 | 58 | auto unboxed = unbox->Evaluate(boxed); 59 | if (!unboxed.has_value()) { 60 | EventCounters::Instance().Add("optimizer.tautological deoptimize", 1); 61 | return absl::FailedPreconditionError("tautological deoptimize!"); 62 | } 63 | 64 | OverflowedInst* overflowed = 65 | dyn_cast(&*std::next(unbox->GetIterator())); 66 | 67 | Builder builder = rewriter.CreateBuilder(unbox->GetIterator()); 68 | if (overflowed) { 69 | // At this point the overflowed? will always return zero because the unbox 70 | // succeeded. 71 | rewriter.ReplaceAllUsesWith(*overflowed, *builder.Int64(0)); 72 | rewriter.erase(*overflowed); 73 | } 74 | 75 | rewriter.ReplaceAllUsesWith(*unbox, *builder.Int64(*unboxed)); 76 | rewriter.erase(*unbox); 77 | return absl::OkStatus(); 78 | } 79 | 80 | absl::Status ConstantFoldDeoptimizeIfSafepointInstPattern::Apply( 81 | DeoptimizeIfSafepointInst* deopt, Rewriter& rewriter) { 82 | auto cond = GetValueAsConstantInt(deopt->condition(), rewriter.code_object()); 83 | if (!cond.has_value()) return absl::FailedPreconditionError("not a constant"); 84 | 85 | bool test_result = deopt->negated() ? *cond == 0 : *cond != 0; 86 | if (test_result) { 87 | // Maybe move backward the information that this deoptimize will be taken 88 | // in all cases. 89 | EventCounters::Instance().Add("optimizer.tautological deoptimize", 1); 90 | return absl::FailedPreconditionError("tautological deoptimize"); 91 | } 92 | 93 | rewriter.erase(*deopt); 94 | return absl::OkStatus(); 95 | } 96 | 97 | } // namespace deepmind::s6 98 | -------------------------------------------------------------------------------- /src/strongjit/optimize_liveness.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2021 The s6 Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS-IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include "strongjit/optimize_liveness.h" 16 | 17 | namespace deepmind::s6 { 18 | 19 | // The LiveInfo constructor actually performs the analysis. 20 | LiveInfo::LiveInfo(const Function& f) { 21 | // This function implement a reverse/backward data-flow analysis. 22 | // See https://en.wikipedia.org/wiki/Data-flow_analysis. 23 | 24 | numbering_ = ComputeValueNumbering(f); 25 | 26 | // First compute the gen and kill set of each block. 27 | // Since this is a backward live value analysis: 28 | // - The generated live values are values used by the block. 29 | // - The killed live values are values produced by the block that are thus 30 | // not live before the block entry. 31 | absl::flat_hash_map gens; 32 | absl::flat_hash_map kills; 33 | for (const Block& b : f) { 34 | BlockInfoBits& gen = gens[&b]; 35 | BlockInfoBits& kill = kills[&b]; 36 | gen.resize(numbering_.size()); 37 | kill.resize(numbering_.size()); 38 | 39 | for (const Instruction& inst : b) { 40 | for (const Value* operand : inst.operands()) { 41 | if (operand && (InstructionTraits::ProducesValue(*operand) || 42 | isa(operand))) { 43 | gen.set_bit(numbering_[operand]); 44 | } 45 | } 46 | // If that instruction produce a value, that value must be killed. 47 | if (InstructionTraits::ProducesValue(inst)) { 48 | kill.set_bit(numbering_[&inst]); 49 | } 50 | } 51 | 52 | // Block argument are also value produced by a block. 53 | for (const Value* v : b.block_arguments()) kill.set_bit(numbering_[v]); 54 | } 55 | 56 | // Now do the proper reverse data-flow algorithm using a worklist. 57 | Worklist worklist; 58 | // Initialize each block live variable to be their gen minus their kill 59 | // It is certain that at least all values that are used by a block but not 60 | // produced by it are live at the block entry. Thus this is a sound 61 | // lower-bound for the set of live values of a block. The remaining algorithm 62 | // can only increase that set. 63 | // 64 | // The invariant that the live info of a block does not contains its kill set 65 | // is maintained by the algorithm. 66 | for (const Block& b : f) { 67 | worklist.Push(&b); 68 | infos_[&b].bits = gens[&b]; 69 | infos_[&b].bits.Difference(kills[&b]); 70 | } 71 | 72 | while (!worklist.empty()) { 73 | const Block* b = worklist.Pop(); 74 | BlockInfoBits info = infos_.at(b).bits; 75 | // Process a new block b by adding the live variable of b to all it's 76 | // predecessor live variable. 77 | for (const Block* pred : b->predecessors()) { 78 | BlockInfoBits& pred_info = infos_.at(pred).bits; 79 | BlockInfoBits newinfo = pred_info; 80 | newinfo.Union(info); 81 | // Maintain the invariant that a block live info does not contain its kill 82 | // set. 83 | newinfo.Difference(kills[pred]); 84 | if (newinfo != pred_info) { 85 | // If the live info of the predecessor has changed, then the predecessor 86 | // needs to be processed again to propagate the change. 87 | pred_info = newinfo; 88 | worklist.PushIfNew(pred); 89 | } 90 | } 91 | } 92 | FillListFromBits(); 93 | } 94 | 95 | } // namespace deepmind::s6 96 | -------------------------------------------------------------------------------- /src/allocator.h: -------------------------------------------------------------------------------- 1 | // Copyright 2021 The s6 Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS-IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #ifndef THIRD_PARTY_DEEPMIND_S6_ALLOCATOR_H_ 16 | #define THIRD_PARTY_DEEPMIND_S6_ALLOCATOR_H_ 17 | 18 | #include 19 | #include 20 | #include 21 | 22 | #include "absl/base/thread_annotations.h" 23 | #include "absl/container/flat_hash_map.h" 24 | #include "absl/container/flat_hash_set.h" 25 | #include "absl/strings/string_view.h" 26 | #include "absl/synchronization/mutex.h" 27 | #include "absl/types/span.h" 28 | #include "utils/interval_map.h" 29 | 30 | namespace deepmind::s6 { 31 | struct GdbCodeEntry; 32 | // Forward-declare internal implementation. 33 | struct MinimalElf; 34 | 35 | // The JitAllocator is a simple arena allocator that mprotects its pages to 36 | // allow execution. 37 | // 38 | // Behavior is undefined if more than one JitAllocator instance is alive at 39 | // any one time (as it interacts with a global linked-list provided to GDB). 40 | class JitAllocator { 41 | public: 42 | // Creates a new allocator with an arena blocks size of `block_size`. 43 | // We provide a default block size of 4K, which is a normal page size. 44 | explicit JitAllocator(int64_t block_size = 4096); 45 | ~JitAllocator(); 46 | 47 | // Allocates at least `size` bytes and returns a pointer to the allocated 48 | // space. `symbol` is the name of the code symbol that will be generated 49 | // inside the allocated space, and will be registered with GDB. 50 | void* Alloc(int64_t size, absl::string_view symbol); 51 | 52 | // Frees space allocated by `Alloc`. 53 | void Free(void* memory); 54 | 55 | // Allocates at least `size` bytes and returns a pointer to the allocated 56 | // space. No symbol is registered with the location. 57 | void* AllocData(int64_t size); 58 | 59 | // Registers information about a symbol with GDB and optionally Linux perf. 60 | // `memory` must be the exact pointer allocated by a call to `Alloc()`. 61 | // 62 | // When `memory` is freed, GDB will be informed that this symbol has gone. 63 | // `debug_annotations`, if specified, maps addresses with string identifiers. 64 | void RegisterSymbol( 65 | void* memory, absl::string_view symbol_name, 66 | absl::Span> debug_annotations); 67 | 68 | // Returns an interval map with all known symbols. 69 | // 70 | // This function is thread-safe. 71 | IntervalMap CreateSymbolMap() const; 72 | 73 | private: 74 | // Per-allocation information. 75 | struct AllocInfo { 76 | std::shared_ptr elf; 77 | std::shared_ptr code_entry; 78 | int64_t size; 79 | std::string backtrace_symbol; 80 | }; 81 | 82 | // Unregisters `symbol` with GDB. 83 | void UnregisterSymbol(const AllocInfo* info); 84 | 85 | // Allocates raw data from a region. Creates a new region if the current is 86 | // too small. 87 | void* AllocateRaw(int64_t size) ABSL_EXCLUSIVE_LOCKS_REQUIRED(mu_); 88 | 89 | // Holds the mmapped regions. 90 | std::vector regions_; 91 | 92 | // The current pointer within regions_.back(). 93 | void* region_ptr_ = nullptr; 94 | 95 | // The amount of memory left in the current region. 96 | int64_t region_available_ = 0; 97 | 98 | int64_t block_size_; 99 | 100 | // Per-alloc information. 101 | absl::flat_hash_map infos_ ABSL_GUARDED_BY(mu_); 102 | 103 | mutable absl::Mutex mu_; 104 | }; 105 | } // namespace deepmind::s6 106 | 107 | #endif // THIRD_PARTY_DEEPMIND_S6_ALLOCATOR_H_ 108 | -------------------------------------------------------------------------------- /src/runtime/deoptimization_map_test.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2021 The s6 Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS-IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include "runtime/deoptimization_map.h" 16 | 17 | #include "absl/algorithm/container.h" 18 | #include "gmock/gmock.h" 19 | #include "gtest/gtest.h" 20 | #include "runtime/slot_indexes.h" 21 | #include "strongjit/base.h" 22 | #include "strongjit/builder.h" 23 | 24 | namespace deepmind::s6 { 25 | namespace { 26 | using ValueAndLocation = DeoptimizationMap::ValueAndLocation; 27 | using testing::UnorderedElementsAre; 28 | 29 | TEST(DeoptimizationMapTest, LiveValueIteratorTest) { 30 | Function f("live_value_iterator_test"); 31 | Builder b(&f); 32 | 33 | std::array values; 34 | absl::c_generate(values, [&]() { return b.Int64(0); }); 35 | 36 | SlotIndexes slot_indexes(f); 37 | DeoptimizationMap deopt_map(slot_indexes); 38 | 39 | deopt_map.AddLiveValue(values[0], Location(), values[1]); 40 | deopt_map.AddLiveValue(values[0], Location(), values[2]); 41 | deopt_map.AddLiveValue(values[0], Location(), values[3]); 42 | deopt_map.AddLiveValue(values[1], Location(), values[2]); 43 | deopt_map.AddLiveValue(values[2], Location(), values[3]); 44 | deopt_map.AddLiveValue(values[2], Location(), values[4]); 45 | deopt_map.AddLiveValue(values[2], Location(), values[5]); 46 | 47 | deopt_map.AddInstructionAddress(values[0], 1, 2); 48 | deopt_map.AddInstructionAddress(values[1], 2, 3); 49 | deopt_map.AddInstructionAddress(values[2], 3, 4); 50 | 51 | EXPECT_THAT(deopt_map.live_values((ProgramAddress)0), UnorderedElementsAre()); 52 | EXPECT_THAT(deopt_map.live_values(2), 53 | UnorderedElementsAre(ValueAndLocation{values[0], Location()})); 54 | EXPECT_THAT(deopt_map.live_values(3), 55 | UnorderedElementsAre(ValueAndLocation{values[0], Location()}, 56 | ValueAndLocation{values[1], Location()})); 57 | } 58 | 59 | TEST(DeoptimizationMapTest, LiveFastLocals) { 60 | Function f("live_fastlocals_test"); 61 | Builder b(&f); 62 | 63 | std::array values; 64 | absl::c_generate(values, [&]() { return b.Int64(0); }); 65 | 66 | auto make_bytecode_begin = [&](absl::Span vs) { 67 | return b.inserter().Create( 68 | 0, absl::Span{}, vs, absl::Span{}); 69 | }; 70 | 71 | // At `b1`, the fastlocals are {values[0], values[1]}. 72 | BytecodeBeginInst* b1 = make_bytecode_begin({values[0], values[1]}); 73 | // At `b2`, the fastlocals are {values[2], values[1]}. 74 | BytecodeBeginInst* b2 = make_bytecode_begin({values[2], values[1]}); 75 | // At `b3`, the fastlocals are {values[0]}. 76 | BytecodeBeginInst* b3 = make_bytecode_begin({values[0]}); 77 | // At `b4`, the fastlocals are {values[0], values[0], values[2], values[4]}. 78 | BytecodeBeginInst* b4 = 79 | make_bytecode_begin({values[0], values[0], values[2], values[4]}); 80 | 81 | SlotIndexes slot_indexes(f); 82 | DeoptimizationMap deopt_map(slot_indexes); 83 | 84 | EXPECT_THAT(deopt_map.GetLiveFastLocals(slot_indexes.SlotForValue(b1)), 85 | testing::ElementsAre(values[0], values[1])); 86 | EXPECT_THAT(deopt_map.GetLiveFastLocals(slot_indexes.SlotForValue(b2)), 87 | testing::ElementsAre(values[2], values[1])); 88 | EXPECT_THAT(deopt_map.GetLiveFastLocals(slot_indexes.SlotForValue(b3)), 89 | testing::ElementsAre(values[0])); 90 | EXPECT_THAT(deopt_map.GetLiveFastLocals(slot_indexes.SlotForValue(b4)), 91 | testing::ElementsAre(values[0], values[0], values[2], values[4])); 92 | } 93 | 94 | } // namespace 95 | } // namespace deepmind::s6 96 | -------------------------------------------------------------------------------- /src/runtime/deoptimization_map.h: -------------------------------------------------------------------------------- 1 | // Copyright 2021 The s6 Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS-IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #ifndef THIRD_PARTY_DEEPMIND_S6_RUNTIME_DEOPTIMIZATION_MAP_H_ 16 | #define THIRD_PARTY_DEEPMIND_S6_RUNTIME_DEOPTIMIZATION_MAP_H_ 17 | 18 | #include 19 | 20 | #include "core_util.h" 21 | #include "runtime/slot_indexes.h" 22 | #include "runtime/util.h" 23 | #include "strongjit/value.h" 24 | #include "utils/interval_map.h" 25 | #include "utils/range.h" 26 | 27 | namespace deepmind::s6 { 28 | 29 | // Holds a mapping of strongjit Values to Locations during a function's 30 | // execution, and a mapping of program point to strongjit Instruction. 31 | // 32 | // This supports two types of query: 33 | // 1) Which strongjit Instruction is being executed at a given program point? 34 | // 2) Which Values are live at a given Instruction and where are they 35 | // located? 36 | // 37 | // A "program point" here means a program counter location within generated 38 | // code. 39 | class DeoptimizationMap { 40 | public: 41 | struct ValueAndLocation { 42 | using first_type = const Value*; 43 | using second_type = Location; 44 | 45 | const Value* value; 46 | Location location; 47 | 48 | friend bool operator==(const ValueAndLocation& lhs, 49 | const ValueAndLocation& rhs) { 50 | return lhs.value == rhs.value && lhs.location == rhs.location; 51 | } 52 | }; 53 | 54 | using LiveValuesIterator = std::vector::const_iterator; 55 | 56 | // Attempts to find the strongjit Instruction being executed at 57 | // `program_point`. If no Instruction was found, returns nullptr. 58 | const Instruction* GetInstructionAtAddress( 59 | ProgramAddress program_point) const; 60 | 61 | // Returns an iterator range over all values live on entry to the Instruction 62 | // at `program_point`. The order in which the values are returned is 63 | // undefined. 64 | IteratorRange live_values( 65 | ProgramAddress program_point) const; 66 | // Returns an iterator range over all values live on entry to `inst`. The 67 | // order in which the values are returned is undefined. 68 | IteratorRange live_values(const Instruction* inst) const; 69 | 70 | // Returns the fastlocals live at a program slot. 71 | std::vector GetLiveFastLocals(SlotIndexes::Slot slot) const; 72 | 73 | std::vector GetLiveFastLocals(const Instruction* inst) const { 74 | return GetLiveFastLocals(slot_indexes_.SlotForValue(inst)); 75 | } 76 | 77 | // Builder methods 78 | // Adds an instruction in the half-open range of program addresses [begin, 79 | // end). 80 | void AddInstructionAddress(const Instruction* inst, ProgramAddress begin, 81 | ProgramAddress end); 82 | 83 | // Adds a live value at the given instruction. 84 | void AddLiveValue(const Value* value, Location location, 85 | const Instruction* inst); 86 | 87 | explicit DeoptimizationMap(SlotIndexes slot_indexes); 88 | 89 | const SlotIndexes& slot_indexes() const { return slot_indexes_; } 90 | 91 | private: 92 | SlotIndexes slot_indexes_; 93 | absl::flat_hash_map> 94 | live_values_; 95 | IntervalMap instruction_addresses_; 96 | std::vector> fastlocals_; 97 | }; 98 | 99 | } // namespace deepmind::s6 100 | 101 | #endif // THIRD_PARTY_DEEPMIND_S6_RUNTIME_DEOPTIMIZATION_MAP_H_ 102 | -------------------------------------------------------------------------------- /src/utils/keyed_intern_table.h: -------------------------------------------------------------------------------- 1 | // Copyright 2021 The s6 Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS-IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #ifndef THIRD_PARTY_DEEPMIND_S6_UTILS_KEYED_INTERN_TABLE_H_ 16 | #define THIRD_PARTY_DEEPMIND_S6_UTILS_KEYED_INTERN_TABLE_H_ 17 | 18 | #include 19 | #include 20 | #include 21 | #include 22 | 23 | #include "absl/container/flat_hash_set.h" 24 | #include "utils/logging.h" 25 | 26 | // KeyedInternTable is a set of strings where each string is only stored once. 27 | // This allows reduction in memory consumption where a single string is used 28 | // repeatedly. 29 | // 30 | // Strings can be looked up by key and are stored so that the underlying char* 31 | // pointer is stable after further insertions into the table. 32 | // 33 | // As a result of pointer stability, string-views of interned strings can be 34 | // validly stored for the lifetime of the KeyedInternTable. 35 | 36 | template 37 | class KeyedInternTable { 38 | static_assert(std::is_integral_v && !std::is_enum_v); 39 | 40 | public: 41 | struct KeyValue { 42 | Key key; 43 | const char* value; // null-terminated, non-null. 44 | }; 45 | 46 | // Finds the string relating to the given key. 47 | // 48 | // REQUIRES: The string was previously inserted. 49 | absl::string_view ToStringView(Key key) const { 50 | S6_DCHECK(key >= 0 && key < values_.size()) 51 | << "Key " << key << " does not correspond to an interned string."; 52 | return values_[static_cast::size_type>(key)]; 53 | } 54 | 55 | // Returns the key-value corresponding to the given string. 56 | // Inserts the string if not already inserted. 57 | KeyValue Insert(absl::string_view s) { 58 | auto find_it = keys_.find(s); 59 | if (find_it != keys_.end()) { 60 | Key key = *find_it; 61 | return {key, values_[key].c_str()}; 62 | } 63 | Key key = values_.size(); 64 | values_.push_back(std::string(s)); 65 | keys_.insert(key); 66 | return {key, values_.back().c_str()}; 67 | } 68 | 69 | // Returns the key for the specified string if previously inserted, otherwise 70 | // returns `std::nullopt`. 71 | std::optional ToKey(absl::string_view s) const { 72 | auto find_it = keys_.find(s); 73 | if (find_it != keys_.end()) { 74 | return *find_it; 75 | } 76 | return std::nullopt; 77 | } 78 | 79 | // Returns the number of unique string inserted. 80 | size_t size() const { return values_.size(); } 81 | 82 | private: 83 | // Implement Hash and Equals to allow (heterogeneous) lookup of strings in the 84 | // keys set. 85 | struct Hash { 86 | using is_transparent = void; 87 | 88 | const std::deque& strings; 89 | 90 | size_t operator()(absl::string_view x) const { 91 | return absl::Hash()(x); 92 | } 93 | 94 | size_t operator()(Key i) const { 95 | return (*this)(strings[static_cast(i)]); 96 | } 97 | }; 98 | 99 | struct Equals { 100 | using is_transparent = void; 101 | 102 | const std::deque& strings; 103 | 104 | bool operator()(Key a, Key b) const { return a == b; } 105 | 106 | bool operator()(Key a, absl::string_view b) const { 107 | return strings[static_cast(a)] == b; 108 | } 109 | }; 110 | using KeySet = absl::flat_hash_set; 111 | 112 | std::deque values_; 113 | KeySet keys_{0, Hash{values_}, Equals{values_}}; 114 | }; 115 | 116 | #endif // THIRD_PARTY_DEEPMIND_S6_UTILS_KEYED_INTERN_TABLE_H_ 117 | -------------------------------------------------------------------------------- /src/api.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2021 The s6 Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS-IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include "api.h" 16 | 17 | #include 18 | #include 19 | 20 | #include 21 | #include // NOLINT 22 | #include 23 | 24 | #include "absl/base/attributes.h" 25 | #include "absl/base/const_init.h" 26 | #include "absl/base/thread_annotations.h" 27 | #include "absl/flags/parse.h" 28 | #include "absl/status/status.h" 29 | #include "absl/strings/str_split.h" 30 | #include "absl/strings/string_view.h" 31 | #include "classes/object.h" 32 | #include "core_util.h" // For IsEnabled(). 33 | #include "eval.h" 34 | #include "interpreter.h" 35 | #include "oracle.h" 36 | #include "runtime/interposer.h" 37 | #include "strongjit/optimizer_util.h" 38 | #include "utils/logging.h" 39 | #include "utils/no_destructor.h" 40 | #include "utils/path.h" 41 | #include "utils/status_macros.h" 42 | 43 | namespace deepmind::s6 { 44 | // The frame evaluation function to use if JIT is enabled. 45 | _PyFrameEvalFunction jit_eval_frame_function; 46 | 47 | absl::Status Initialize(bool adopt_types) { 48 | static std::once_flag init_once; 49 | std::call_once(init_once, [&]() { 50 | // Parse flags passed in the environment variable "S6FLAGS". 51 | if (const char* flags_from_env = getenv("S6FLAGS")) { 52 | std::vector split = absl::StrSplit(flags_from_env, ' '); 53 | // We set argv[0] to UNKNOWN. An argv[0] for the program name is 54 | // required by absl::ParseCommandLine, and the string "UNKNOWN" is 55 | // handled specially (it is the initial "program name not yet set" 56 | // sentinel). 57 | std::vector argv(1, "UNKNOWN"); 58 | for (const std::string& str : split) { 59 | argv.push_back(str.data()); 60 | } 61 | absl::ParseCommandLine(argv.size(), const_cast(argv.data())); 62 | } 63 | 64 | // In debug mode we need a reasonable stack size to run all tests. 65 | #ifndef NDEBUG 66 | const rlim_t kStackSize = 64L * 1024L * 1024L; // min stack size = 16 Mb 67 | struct rlimit rl; 68 | S6_CHECK(getrlimit(RLIMIT_STACK, &rl) == 0); 69 | if (rl.rlim_cur < kStackSize) { 70 | rl.rlim_cur = kStackSize; 71 | S6_CHECK(setrlimit(RLIMIT_STACK, &rl) == 0); 72 | } 73 | #endif 74 | 75 | BuiltinObjects::Instance().Initialize(); 76 | 77 | SetUpInterposers(); 78 | jit_eval_frame_function = GetFrameEvalFunction(); 79 | }); 80 | 81 | if (adopt_types) { 82 | static std::once_flag adopt_once; 83 | std::call_once(adopt_once, [&]() { 84 | AdoptExistingTypes(); 85 | AdoptNewTypes(); 86 | }); 87 | } 88 | 89 | return absl::OkStatus(); 90 | } 91 | 92 | EvalFrameScope::EvalFrameScope(_PyFrameEvalFunction new_eval_frame_function) { 93 | PyThreadState* tstate = PyThreadState_GET(); 94 | previous_ = tstate->interp->eval_frame; 95 | 96 | tstate->interp->eval_frame = new_eval_frame_function; 97 | } 98 | 99 | void EvalFrameScope::RestoreNow() { 100 | if (!previous_) return; 101 | PyThreadState* tstate = _PyThreadState_UncheckedGet(); 102 | // This could be called during shutdown, so be tolerant of there not being 103 | // a thread state. 104 | if (tstate) tstate->interp->eval_frame = previous_; 105 | previous_ = nullptr; 106 | } 107 | 108 | JitScope::JitScope() : EvalFrameScope(jit_eval_frame_function) {} 109 | 110 | EvaluatorScope::EvaluatorScope() : EvalFrameScope(S6EvalFrameWithEvaluator) {} 111 | 112 | InterpreterScope::InterpreterScope() : EvalFrameScope(s6::EvalFrame) {} 113 | 114 | } // namespace deepmind::s6 115 | -------------------------------------------------------------------------------- /src/python/api_test.py: -------------------------------------------------------------------------------- 1 | # Copyright 2021 The s6 Authors. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http:#www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS-IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | """Tests for s6.python.api.""" 16 | 17 | import time 18 | 19 | from absl import flags 20 | from absl.testing import absltest 21 | 22 | from s6.python import api 23 | 24 | FLAGS = flags.FLAGS 25 | 26 | 27 | class ApiTest(absltest.TestCase): 28 | 29 | def test_jit_decorator(self): 30 | 31 | @api.jit 32 | def f(a, b=2): 33 | return a + b 34 | 35 | self.assertEqual(f(3, b=1), 4) 36 | 37 | def test_hot_jit_decorator(self): 38 | 39 | @api.jit 40 | def f(a, b=2): 41 | return a + b 42 | 43 | # TODO: Does this demonstrate that our barrier to compilation 44 | # (profile_instruction_interval) is too high? 45 | for i in range(500000): 46 | f(i) 47 | self.assertTrue(api.inspect(f).is_compiled) 48 | self.assertEqual(f(3, b=1), 4) 49 | 50 | def test_hot_jit_decorator_as_method(self): 51 | 52 | class C(object): 53 | 54 | @api.jit 55 | def f(self, a, b=2): 56 | return a + b 57 | 58 | # TODO: Does this demonstrate that our barrier to compilation 59 | # (profile_instruction_interval) is too high? 60 | c = C() 61 | for i in range(500000): 62 | c.f(i) 63 | self.assertTrue(api.inspect(c.f).is_compiled) 64 | self.assertEqual(c.f(3, b=1), 4) 65 | 66 | def test_jit_as_method(self): 67 | 68 | class C(object): 69 | 70 | @api.jit 71 | def f(self, a): 72 | return a + 1 73 | 74 | c = C() 75 | self.assertEqual(c.f(2), 3) 76 | 77 | def test_jit_decorator_on_recursive_function(self): 78 | 79 | @api.jit 80 | def fib(n): 81 | return fib(n - 1) + fib(n - 2) if n >= 2 else 1 82 | 83 | for _ in range(1000): 84 | fib(10) 85 | 86 | self.assertTrue(api.inspect(fib).is_compiled) 87 | 88 | def test_inspect(self): 89 | 90 | @api.jit 91 | def f(a, b=2): 92 | return a + b 93 | 94 | f(2) 95 | i = api.inspect(f) 96 | self.assertFalse(i.is_compiled) 97 | self.assertRaises(api.NotCompiledError, lambda: i.strongjit) 98 | 99 | i.force_compile() 100 | self.assertTrue(i.is_compiled) 101 | self.assertIn('type_feedback', i.strongjit) 102 | self.assertNotEmpty('type_feedback', i.x86asm) 103 | self.assertEqual(f(2), 4) 104 | 105 | i.deoptimize() 106 | self.assertFalse(i.is_compiled) 107 | self.assertEqual(f(2), 4) 108 | 109 | def test_jit_and_interpret(self): 110 | 111 | @api.jit 112 | def f(a, b=2): 113 | return a + b 114 | 115 | i = api.inspect(f) 116 | i.force_compile() 117 | self.assertTrue(i.is_compiled) 118 | self.assertNotIn('type_feedback', i.strongjit) 119 | self.assertEqual(f._interpret(2), 4) 120 | i.deoptimize() 121 | self.assertFalse(i.is_compiled) 122 | i.force_compile() 123 | self.assertTrue(i.is_compiled) 124 | self.assertIn('type_feedback', i.strongjit) 125 | self.assertEqual(f(2), 4) 126 | 127 | def test_jit_and_evaluate(self): 128 | 129 | @api.jit 130 | def f(a, b=2): 131 | return a + b 132 | 133 | i = api.inspect(f) 134 | i.force_compile() 135 | self.assertEqual(f._evaluate(2), 4) 136 | self.assertEqual(f(2), 4) 137 | 138 | def test_jit_forwards_docstring(self): 139 | 140 | @api.jit 141 | def f(): 142 | """I am docstring.""" 143 | return None 144 | 145 | self.assertEqual(f.__doc__, 'I am docstring.') 146 | 147 | def test_jit_forwards_name(self): 148 | 149 | @api.jit 150 | def f(): 151 | return None 152 | 153 | self.assertEqual(f.__name__, 'f') 154 | 155 | 156 | 157 | if __name__ == '__main__': 158 | absltest.main() 159 | --------------------------------------------------------------------------------