| /* | |
| * Copyright 2016 WebAssembly Community Group participants | |
| * | |
| * Licensed under the Apache License, Version 2.0 (the "License"); | |
| * you may not use this file except in compliance with the License. | |
| * You may obtain a copy of the License at | |
| * | |
| * http://www.apache.org/licenses/LICENSE-2.0 | |
| * | |
| * Unless required by applicable law or agreed to in writing, software | |
| * distributed under the License is distributed on an "AS IS" BASIS, | |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| * See the License for the specific language governing permissions and | |
| * limitations under the License. | |
| */ | |
| #ifndef wasm_support_utilities_h | |
| #define wasm_support_utilities_h | |
| #include "compiler-support.h" | |
| #include <cassert> | |
| #include <cstdint> | |
| #include <cstring> | |
| #include <iostream> | |
| #include <memory> | |
| #include <sstream> | |
| #include <type_traits> | |
| #include "support/bits.h" | |
| namespace wasm { | |
| // Type punning needs to be done through this function to avoid undefined | |
| // behavior: unions and reinterpret_cast aren't valid approaches. | |
| template<class Destination, class Source> | |
| inline Destination bit_cast(const Source& source) { | |
| static_assert(sizeof(Destination) == sizeof(Source), | |
| "bit_cast needs to be between types of the same size"); | |
| static_assert(std::is_trivial_v<Destination> && | |
| std::is_standard_layout_v<Destination>, | |
| "non-POD bit_cast undefined"); | |
| static_assert(std::is_trivial_v<Source> && std::is_standard_layout_v<Source>, | |
| "non-POD bit_cast undefined"); | |
| Destination destination; | |
| std::memcpy(&destination, &source, sizeof(destination)); | |
| return destination; | |
| } | |
| // For fatal errors which could arise from input (i.e. not assertion failures) | |
| class Fatal { | |
| private: | |
| std::stringstream buffer; | |
| public: | |
| Fatal() { buffer << "Fatal: "; } | |
| template<typename T> Fatal& operator<<(T&& arg) { | |
| buffer << arg; | |
| return *this; | |
| } | |
| #ifndef THROW_ON_FATAL | |
| [[noreturn]] ~Fatal() { | |
| std::cerr << buffer.str() << std::endl; | |
| // Use _Exit here to avoid calling static destructors. This avoids deadlocks | |
| // in (for example) the thread worker pool, where workers hold a lock while | |
| // performing their work. | |
| _Exit(EXIT_FAILURE); | |
| } | |
| #else | |
| // This variation is a best-effort attempt to make fatal errors recoverable | |
| // for embedders of Binaryen as a library, namely wasm-opt-rs. | |
| // | |
| // Throwing in destructors is strongly discouraged, since it is easy to | |
| // accidentally throw during unwinding, which will trigger an abort. Since | |
| // `Fatal` is a special type that only occurs on error paths, we are hoping it | |
| // is never constructed during unwinding or while destructing another type. | |
| [[noreturn]] ~Fatal() noexcept(false) { | |
| throw std::runtime_error(buffer.str()); | |
| } | |
| #endif | |
| }; | |
| [[noreturn]] void handle_unreachable(const char* msg = nullptr, | |
| const char* file = nullptr, | |
| unsigned line = 0); | |
| // If control flow reaches the point of the WASM_UNREACHABLE(), the program is | |
| // undefined. | |
| #ifndef NDEBUG | |
| #define WASM_UNREACHABLE(msg) wasm::handle_unreachable(msg, __FILE__, __LINE__) | |
| #elif defined(WASM_BUILTIN_UNREACHABLE) | |
| #define WASM_UNREACHABLE(msg) WASM_BUILTIN_UNREACHABLE | |
| #else | |
| #define WASM_UNREACHABLE(msg) wasm::handle_unreachable() | |
| #endif | |
| // Helper to create an invocable with an overloaded operator(), for use with | |
| // std::visit e.g. | |
| // std::visit( | |
| // overloaded{ | |
| // [](const A& a) { ... }, | |
| // [](const B& b) { ... }}, | |
| // variant) | |
| template<typename... Ts> struct overloaded : Ts... { | |
| using Ts::operator()...; | |
| }; | |
| template<typename... Ts> overloaded(Ts...) -> overloaded<Ts...>; | |
| // Lookup a value from `map` and return a pointer to the underlying value | |
| // or nullptr if not present. Returns a const pointer if `map` is const and | |
| // non-const otherwise | |
| auto* find_or_null(auto& map, const auto& key) { | |
| auto it = map.find(key); | |
| return it != map.end() ? &it->second : nullptr; | |
| } | |
| } // namespace wasm | |
| #endif // wasm_support_utilities_h |