| /* |
| * Copyright (C) 2024 Apple Inc. All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions |
| * are met: |
| * 1. Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * 2. Redistributions in binary form must reproduce the above copyright |
| * notice, this list of conditions and the following disclaimer in the |
| * documentation and/or other materials provided with the distribution. |
| * |
| * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY |
| * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
| * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
| * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR |
| * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
| * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
| * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
| * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY |
| * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| */ |
| |
| #pragma once |
| |
| #include <JavaScriptCore/JITOperationValidation.h> |
| #include <JavaScriptCore/ThrowScope.h> |
| |
| #include <type_traits> |
| #include <wtf/FunctionTraits.h> |
| #include <wtf/StdLibExtras.h> |
| |
| namespace JSC { |
| |
| // This is wrapped in a lambda since it's used by static_asserts thus needs to be an expression. |
| // Additionally, it should not participate in SFINAE: substitution failure IS an error when used in |
| // a type name below. |
| template<typename T> |
| constexpr bool assertNotOperationSignature = ([] { |
| static_assert(!std::is_function_v<typename std::remove_pointer_t<T>>, "This should take a return type, not an operator signature."); |
| return true; |
| })(); |
| |
| template<typename T> |
| concept canMakeExceptionOperationResult = |
| std::is_standard_layout_v<T> |
| && std::is_trivial_v<T> |
| #if CPU(ARM64) || CPU(ARM_THUMB2) |
| && !std::is_floating_point_v<T> // The ARM64 ABI says that this should be returned in x0 instead of d0. Seems unlikely it's worth it to do the extra fmov. |
| #endif |
| && sizeof(T) <= sizeof(CPURegister); |
| |
| #if USE(JSVALUE64) |
| |
| template<typename T> |
| struct ExceptionOperationResult { |
| using ResultType = T; |
| static_assert(assertNotOperationSignature<T>); |
| T value; |
| Exception* exception; |
| }; |
| |
| template<> |
| struct ExceptionOperationResult<void> { |
| using ResultType = void; |
| Exception* exception; |
| }; |
| |
| template<typename OperationType> |
| concept OperationHasResult = FunctionTraits<OperationType>::hasResult && !std::is_same_v<typename FunctionTraits<OperationType>::ResultType, ExceptionOperationResult<void>>; |
| |
| // Note we need this because e.g. `requires (!OperationHasResult<int>)` will pass the requirement as the SFINAE in `OperationHasResult<int>` |
| // means the concept returns false and the `!` makes it true. |
| template<typename OperationType> |
| concept OperationIsVoid = !FunctionTraits<OperationType>::hasResult || std::is_same_v<typename FunctionTraits<OperationType>::ResultType, ExceptionOperationResult<void>>; |
| |
| template<typename T> |
| struct IsExceptionOperationResult : std::false_type { }; |
| |
| template<typename T> |
| struct IsExceptionOperationResult<ExceptionOperationResult<T>> : std::true_type { }; |
| |
| template<typename T> |
| concept isExceptionOperationResult = IsExceptionOperationResult<T>::value; |
| |
| // Note: We always return an exception in a register if the operation's return type is void on all platforms (assuming it throws). |
| template<typename T> |
| using OperationReturnType = std::conditional_t<(std::is_same_v<T, void> || canMakeExceptionOperationResult<T>), ExceptionOperationResult<T>, T>; |
| |
| // This class exists so we can cast ExceptionOperationImplicitResult<From> to ExceptionOperationResult<To> implicitly while ExceptionOperationResult remains POD. |
| // FIXME: we could use __atribute__((trivial_abi)) when we no longer support MSVC. |
| template<typename From> |
| struct ExceptionOperationImplicitResult { |
| template<typename To> |
| requires std::is_convertible_v<From, To> |
| ALWAYS_INLINE operator ExceptionOperationResult<To>() |
| { |
| // For whatever reason aggregate initialization doesn't seem to work here. |
| ExceptionOperationResult<To> result; |
| result.value = value; |
| result.exception = exception; |
| return result; |
| } |
| |
| template<typename To> |
| ALWAYS_INLINE operator To() |
| { |
| return value; |
| } |
| |
| From value; |
| Exception* exception; |
| }; |
| |
| template<> |
| struct ExceptionOperationImplicitResult<void> { |
| ALWAYS_INLINE operator ExceptionOperationResult<void>() |
| { |
| ExceptionOperationResult<void> result; |
| result.exception = exception; |
| return result; |
| } |
| |
| Exception* exception; |
| }; |
| |
| // We can't have a constructor since that would make ExceptionOperationResult non-pod and thus not returned in registers. |
| template<typename Scope, typename T> |
| requires canMakeExceptionOperationResult<T> |
| ALWAYS_INLINE ExceptionOperationImplicitResult<T> makeOperationResult(Scope& scope, T result) |
| { |
| return { result, scope.exception() }; |
| } |
| |
| template<typename Scope> |
| ALWAYS_INLINE ExceptionOperationImplicitResult<void> makeOperationResult(Scope& scope) |
| { |
| return { scope.exception() }; |
| } |
| |
| template<typename Scope, typename T> |
| requires (!canMakeExceptionOperationResult<T>) |
| ALWAYS_INLINE T makeOperationResult(Scope& scope, T result) |
| { |
| RELEASE_AND_RETURN(scope, result); |
| } |
| |
| #else // USE(JSVALUE64) |
| |
| enum class ExceptionOperationResultTag : uint64_t { }; |
| |
| struct ExceptionOperationResultVoid { |
| Exception* exception; |
| }; |
| |
| template<typename T> |
| using ExceptionOperationResult = std::conditional_t<(std::is_same_v<T, void>), ExceptionOperationResultVoid, ExceptionOperationResultTag>; |
| |
| template<typename OperationType> |
| concept OperationHasResult = FunctionTraits<OperationType>::hasResult && !std::is_same_v<typename FunctionTraits<OperationType>::ResultType, ExceptionOperationResult<void>>; |
| |
| // Note we need this because e.g. `requires (!OperationHasResult<int>)` will pass the requirement as the SFINAE in `OperationHasResult<int>` |
| // means the concept returns false and the `!` makes it true. |
| template<typename OperationType> |
| concept OperationIsVoid = !FunctionTraits<OperationType>::hasResult || std::is_same_v<typename FunctionTraits<OperationType>::ResultType, ExceptionOperationResult<void>>; |
| |
| template<typename T> |
| struct IsExceptionOperationResult : std::false_type { }; |
| |
| template<> |
| struct IsExceptionOperationResult<ExceptionOperationResultTag> : std::true_type { }; |
| |
| template<> |
| struct IsExceptionOperationResult<ExceptionOperationResultVoid> : std::true_type { }; |
| |
| template<typename T> |
| concept isExceptionOperationResult = IsExceptionOperationResult<T>::value; |
| |
| // Note: We always return an exception in a register if the operation's return type is void on all platforms (assuming it throws). |
| template<typename T> |
| using OperationReturnType = std::conditional_t<(assertNotOperationSignature<T> && (std::is_same_v<T, void> || canMakeExceptionOperationResult<T>)), ExceptionOperationResult<T>, T>; |
| |
| static ALWAYS_INLINE ExceptionOperationResultTag asExceptionResultBase(uint32_t value, Exception* exception) |
| { |
| uint32_t v = std::bit_cast<uint32_t>(value); |
| return std::bit_cast<ExceptionOperationResultTag>( |
| static_cast<long long>(v) |
| | (static_cast<long long>(reinterpret_cast<uint32_t>(exception)) << 32)); |
| } |
| |
| template<typename T> |
| requires (sizeof(T) == sizeof(uint32_t)) |
| static ALWAYS_INLINE ExceptionOperationResultTag asExceptionResult(T value, Exception* exception) |
| { |
| uint32_t v = std::bit_cast<uint32_t>(value); |
| return asExceptionResultBase(v, exception); |
| } |
| |
| template<typename T> |
| requires (sizeof(T) < sizeof(uint32_t)) |
| static ALWAYS_INLINE ExceptionOperationResultTag asExceptionResult(T value, Exception* exception) |
| { |
| uint32_t v = static_cast<uint32_t>(value); |
| return asExceptionResultBase(v, exception); |
| } |
| |
| template<typename From> |
| struct ExceptionOperationImplicitResult { |
| |
| ALWAYS_INLINE operator ExceptionOperationResultTag() |
| { |
| return asExceptionResult(value, exception); |
| } |
| |
| template<typename To> |
| ALWAYS_INLINE operator To() |
| { |
| return static_cast<To>(value); |
| } |
| |
| From value; |
| Exception* exception; |
| }; |
| |
| template<> |
| struct ExceptionOperationImplicitResult<void> { |
| ALWAYS_INLINE operator ExceptionOperationResultVoid() |
| { |
| return { exception }; |
| } |
| |
| Exception* exception; |
| }; |
| |
| template<typename Scope, typename T> |
| requires canMakeExceptionOperationResult<T> |
| ALWAYS_INLINE ExceptionOperationImplicitResult<T> makeOperationResult(Scope& scope, T result) |
| { |
| static_assert(assertNotOperationSignature<T>); |
| return { result, scope.exception() }; |
| } |
| |
| template<typename Scope> |
| ALWAYS_INLINE ExceptionOperationImplicitResult<void> makeOperationResult(Scope& scope) |
| { |
| return { scope.exception() }; |
| } |
| |
| template<typename Scope, typename T> |
| requires (!canMakeExceptionOperationResult<T>) |
| ALWAYS_INLINE T makeOperationResult(Scope& scope, T result) |
| { |
| static_assert(assertNotOperationSignature<T>); |
| RELEASE_AND_RETURN(scope, result); |
| } |
| |
| #endif // USE(JSVALUE64) |
| |
| #define OPERATION_RETURN(scope__, ...) return makeOperationResult(scope__, ## __VA_ARGS__) |
| #define OPERATION_RETURN_IF_EXCEPTION(scope__, ...) RETURN_IF_EXCEPTION(scope__, makeOperationResult(scope__, ## __VA_ARGS__)) |
| |
| #if COMPILER(CLANG) |
| |
| #define JSC_DECLARE_JIT_OPERATION(functionName, returnType, parameters) \ |
| _Pragma("clang diagnostic push") \ |
| _Pragma("clang diagnostic ignored \"-Wreturn-type-c-linkage\"") \ |
| JSC_DECLARE_JIT_OPERATION_IMPL(functionName, JSC::OperationReturnType<returnType>, parameters) \ |
| _Pragma("clang diagnostic pop") |
| |
| #define JSC_DECLARE_JIT_OPERATION_WITHOUT_WTF_INTERNAL(functionName, returnType, parameters) \ |
| _Pragma("clang diagnostic push") \ |
| _Pragma("clang diagnostic ignored \"-Wreturn-type-c-linkage\"") \ |
| JSC_DECLARE_JIT_OPERATION_WITHOUT_WTF_INTERNAL_IMPL(functionName, JSC::OperationReturnType<returnType>, parameters) \ |
| _Pragma("clang diagnostic pop") |
| |
| #else |
| |
| #define JSC_DECLARE_JIT_OPERATION(functionName, returnType, parameters) JSC_DECLARE_JIT_OPERATION_IMPL(functionName, JSC::OperationReturnType<returnType>, parameters) |
| #define JSC_DECLARE_JIT_OPERATION_WITHOUT_WTF_INTERNAL(functionName, returnType, parameters) JSC_DECLARE_JIT_OPERATION_WITHOUT_WTF_INTERNAL_IMPL(functionName, JSC::OperationReturnType<returnType>, parameters) |
| |
| #endif |
| |
| #define JSC_DEFINE_JIT_OPERATION(functionName, returnType, parameters) JSC_DEFINE_JIT_OPERATION_IMPL(functionName, JSC::OperationReturnType<returnType>, parameters) |
| |
| #define JSC_DECLARE_NOEXCEPT_JIT_OPERATION(functionName, returnType, parameters) JSC_DECLARE_JIT_OPERATION_IMPL(functionName, returnType, parameters) |
| #define JSC_DECLARE_NOEXCEPT_JIT_OPERATION_WITH_ATTRIBUTES(functionName, attributes, returnType, parameters) JSC_DECLARE_JIT_OPERATION_WITH_ATTRIBUTES_IMPL(functionName, attributes, returnType, parameters) |
| #define JSC_DECLARE_NOEXCEPT_JIT_OPERATION_WITHOUT_WTF_INTERNAL(functionName, returnType, parameters) JSC_DECLARE_JIT_OPERATION_WITHOUT_WTF_INTERNAL_IMPL(functionName, returnType, parameters) |
| |
| #define JSC_DEFINE_NOEXCEPT_JIT_OPERATION_WITH_ATTRIBUTES(functionName, attributes, returnType, parameters) JSC_DEFINE_JIT_OPERATION_WITH_ATTRIBUTES_IMPL(functionName, attributes, returnType, parameters) |
| #define JSC_DEFINE_NOEXCEPT_JIT_OPERATION(functionName, returnType, parameters) JSC_DEFINE_JIT_OPERATION_IMPL(functionName, returnType, parameters) |
| |
| } // namespace JSC |