// Copyright 2018 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "src/codegen/external-reference.h"

#include "include/v8-fast-api-calls.h"
#include "src/api/api-inl.h"
#include "src/base/ieee754.h"
#include "src/codegen/cpu-features.h"
#include "src/common/globals.h"
#include "src/date/date.h"
#include "src/debug/debug.h"
#include "src/deoptimizer/deoptimizer.h"
#include "src/execution/encoded-c-signature.h"
#include "src/execution/isolate-utils.h"
#include "src/execution/isolate.h"
#include "src/execution/microtask-queue.h"
#include "src/execution/simulator.h"
#include "src/heap/heap-inl.h"
#include "src/heap/heap.h"
#include "src/ic/stub-cache.h"
#include "src/interpreter/interpreter.h"
#include "src/logging/counters.h"
#include "src/logging/log.h"
#include "src/numbers/hash-seed-inl.h"
#include "src/numbers/math-random.h"
#include "src/objects/elements.h"
#include "src/objects/object-type.h"
#include "src/objects/objects-inl.h"
#include "src/objects/ordered-hash-table.h"
#include "src/objects/simd.h"
#include "src/regexp/experimental/experimental.h"
#include "src/regexp/regexp-interpreter.h"
#include "src/regexp/regexp-macro-assembler-arch.h"
#include "src/regexp/regexp-stack.h"
#include "src/strings/string-search.h"

#if V8_ENABLE_WEBASSEMBLY
#include "src/wasm/wasm-external-refs.h"
#endif  // V8_ENABLE_WEBASSEMBLY

#ifdef V8_INTL_SUPPORT
#include "src/base/strings.h"
#include "src/objects/intl-objects.h"
#endif  // V8_INTL_SUPPORT

namespace v8 {
namespace internal {

// -----------------------------------------------------------------------------
// Common double constants.

constexpr double double_min_int_constant = kMinInt;
constexpr double double_one_half_constant = 0.5;
constexpr uint64_t double_the_hole_nan_constant = kHoleNanInt64;
constexpr double double_uint32_bias_constant =
    static_cast<double>(kMaxUInt32) + 1;

constexpr struct alignas(16) {
  uint32_t a;
  uint32_t b;
  uint32_t c;
  uint32_t d;
} float_absolute_constant = {0x7FFFFFFF, 0x7FFFFFFF, 0x7FFFFFFF, 0x7FFFFFFF};

constexpr struct alignas(16) {
  uint32_t a;
  uint32_t b;
  uint32_t c;
  uint32_t d;
} float_negate_constant = {0x80000000, 0x80000000, 0x80000000, 0x80000000};

constexpr struct alignas(16) {
  uint64_t a;
  uint64_t b;
} double_absolute_constant = {uint64_t{0x7FFFFFFFFFFFFFFF},
                              uint64_t{0x7FFFFFFFFFFFFFFF}};

constexpr struct alignas(16) {
  uint64_t a;
  uint64_t b;
} double_negate_constant = {uint64_t{0x8000000000000000},
                            uint64_t{0x8000000000000000}};

constexpr struct alignas(16) {
  uint64_t a;
  uint64_t b;
} wasm_i8x16_swizzle_mask = {uint64_t{0x70707070'70707070},
                             uint64_t{0x70707070'70707070}};

constexpr struct alignas(16) {
  uint64_t a;
  uint64_t b;
} wasm_i8x16_popcnt_mask = {uint64_t{0x03020201'02010100},
                            uint64_t{0x04030302'03020201}};

constexpr struct alignas(16) {
  uint64_t a;
  uint64_t b;
} wasm_i8x16_splat_0x01 = {uint64_t{0x01010101'01010101},
                           uint64_t{0x01010101'01010101}};

constexpr struct alignas(16) {
  uint64_t a;
  uint64_t b;
} wasm_i8x16_splat_0x0f = {uint64_t{0x0F0F0F0F'0F0F0F0F},
                           uint64_t{0x0F0F0F0F'0F0F0F0F}};

constexpr struct alignas(16) {
  uint64_t a;
  uint64_t b;
} wasm_i8x16_splat_0x33 = {uint64_t{0x33333333'33333333},
                           uint64_t{0x33333333'33333333}};

constexpr struct alignas(16) {
  uint64_t a;
  uint64_t b;
} wasm_i8x16_splat_0x55 = {uint64_t{0x55555555'55555555},
                           uint64_t{0x55555555'55555555}};

constexpr struct alignas(16) {
  uint64_t a;
  uint64_t b;
} wasm_i16x8_splat_0x0001 = {uint64_t{0x00010001'00010001},
                             uint64_t{0x00010001'00010001}};

constexpr struct alignas(16) {
  uint64_t a;
  uint64_t b;
} wasm_f64x2_convert_low_i32x4_u_int_mask = {uint64_t{0x4330000043300000},
                                             uint64_t{0x4330000043300000}};

constexpr struct alignas(16) {
  uint64_t a;
  uint64_t b;
} wasm_double_2_power_52 = {uint64_t{0x4330000000000000},
                            uint64_t{0x4330000000000000}};

constexpr struct alignas(16) {
  uint64_t a;
  uint64_t b;
} wasm_int32_max_as_double = {uint64_t{0x41dfffffffc00000},
                              uint64_t{0x41dfffffffc00000}};

constexpr struct alignas(16) {
  uint64_t a;
  uint64_t b;
} wasm_uint32_max_as_double = {uint64_t{0x41efffffffe00000},
                               uint64_t{0x41efffffffe00000}};

// This is 2147483648.0, which is 1 more than INT32_MAX.
constexpr struct alignas(16) {
  uint32_t a;
  uint32_t b;
  uint32_t c;
  uint32_t d;
} wasm_int32_overflow_as_float = {
    uint32_t{0x4f00'0000},
    uint32_t{0x4f00'0000},
    uint32_t{0x4f00'0000},
    uint32_t{0x4f00'0000},
};

// Implementation of ExternalReference

static ExternalReference::Type BuiltinCallTypeForResultSize(int result_size) {
  switch (result_size) {
    case 1:
      return ExternalReference::BUILTIN_CALL;
    case 2:
      return ExternalReference::BUILTIN_CALL_PAIR;
  }
  UNREACHABLE();
}

// static
ExternalReference ExternalReference::Create(ApiFunction* fun, Type type) {
  return ExternalReference(Redirect(fun->address(), type));
}

// static
ExternalReference ExternalReference::Create(
    Isolate* isolate, ApiFunction* fun, Type type, Address* c_functions,
    const CFunctionInfo* const* c_signatures, unsigned num_functions) {
#ifdef V8_USE_SIMULATOR_WITH_GENERIC_C_CALLS
  isolate->simulator_data()->RegisterFunctionsAndSignatures(
      c_functions, c_signatures, num_functions);
#endif  //  V8_USE_SIMULATOR_WITH_GENERIC_C_CALLS
  return ExternalReference(Redirect(fun->address(), type));
}

// static
ExternalReference ExternalReference::Create(Runtime::FunctionId id) {
  return Create(Runtime::FunctionForId(id));
}

// static
ExternalReference ExternalReference::Create(const Runtime::Function* f) {
  return ExternalReference(
      Redirect(f->entry, BuiltinCallTypeForResultSize(f->result_size)));
}

// static
ExternalReference ExternalReference::Create(Address address, Type type) {
  return ExternalReference(Redirect(address, type));
}

ExternalReference ExternalReference::isolate_address(Isolate* isolate) {
  return ExternalReference(isolate);
}

ExternalReference ExternalReference::builtins_table(Isolate* isolate) {
  return ExternalReference(isolate->builtin_table());
}

ExternalReference ExternalReference::handle_scope_implementer_address(
    Isolate* isolate) {
  return ExternalReference(isolate->handle_scope_implementer_address());
}

#ifdef V8_ENABLE_SANDBOX
ExternalReference ExternalReference::sandbox_base_address() {
  return ExternalReference(GetProcessWideSandbox()->base_address());
}

ExternalReference ExternalReference::sandbox_end_address() {
  return ExternalReference(GetProcessWideSandbox()->end_address());
}

ExternalReference ExternalReference::empty_backing_store_buffer() {
  return ExternalReference(GetProcessWideSandbox()
                               ->constants()
                               .empty_backing_store_buffer_address());
}

ExternalReference ExternalReference::external_pointer_table_address(
    Isolate* isolate) {
  return ExternalReference(isolate->external_pointer_table_address());
}

ExternalReference
ExternalReference::shared_external_pointer_table_address_address(
    Isolate* isolate) {
  return ExternalReference(
      isolate->shared_external_pointer_table_address_address());
}
#endif  // V8_ENABLE_SANDBOX

ExternalReference ExternalReference::interpreter_dispatch_table_address(
    Isolate* isolate) {
  return ExternalReference(isolate->interpreter()->dispatch_table_address());
}

ExternalReference ExternalReference::interpreter_dispatch_counters(
    Isolate* isolate) {
  return ExternalReference(
      isolate->interpreter()->bytecode_dispatch_counters_table());
}

ExternalReference
ExternalReference::address_of_interpreter_entry_trampoline_instruction_start(
    Isolate* isolate) {
  return ExternalReference(
      isolate->interpreter()
          ->address_of_interpreter_entry_trampoline_instruction_start());
}

ExternalReference ExternalReference::bytecode_size_table_address() {
  return ExternalReference(
      interpreter::Bytecodes::bytecode_size_table_address());
}

// static
ExternalReference ExternalReference::Create(StatsCounter* counter) {
  return ExternalReference(
      reinterpret_cast<Address>(counter->GetInternalPointer()));
}

// static
ExternalReference ExternalReference::Create(IsolateAddressId id,
                                            Isolate* isolate) {
  return ExternalReference(isolate->get_address_from_id(id));
}

// static
ExternalReference ExternalReference::Create(const SCTableReference& table_ref) {
  return ExternalReference(table_ref.address());
}

namespace {

// Helper function to verify that all types in a list of types are scalar.
// This includes primitive types (int, Address) and pointer types. We also
// allow void.
template <typename T>
constexpr bool AllScalar() {
  return std::is_scalar<T>::value || std::is_void<T>::value;
}

template <typename T1, typename T2, typename... Rest>
constexpr bool AllScalar() {
  return AllScalar<T1>() && AllScalar<T2, Rest...>();
}

// Checks a function pointer's type for compatibility with the
// ExternalReference calling mechanism. Specifically, all arguments
// as well as the result type must pass the AllScalar check above,
// because we expect each item to fit into one register or stack slot.
template <typename T>
struct IsValidExternalReferenceType;

template <typename Result, typename... Args>
struct IsValidExternalReferenceType<Result (*)(Args...)> {
  static const bool value = AllScalar<Result, Args...>();
};

template <typename Result, typename Class, typename... Args>
struct IsValidExternalReferenceType<Result (Class::*)(Args...)> {
  static const bool value = AllScalar<Result, Args...>();
};

}  // namespace

#define FUNCTION_REFERENCE(Name, Target)                                   \
  ExternalReference ExternalReference::Name() {                            \
    static_assert(IsValidExternalReferenceType<decltype(&Target)>::value); \
    return ExternalReference(Redirect(FUNCTION_ADDR(Target)));             \
  }

#define FUNCTION_REFERENCE_WITH_TYPE(Name, Target, Type)                   \
  ExternalReference ExternalReference::Name() {                            \
    static_assert(IsValidExternalReferenceType<decltype(&Target)>::value); \
    return ExternalReference(Redirect(FUNCTION_ADDR(Target), Type));       \
  }

FUNCTION_REFERENCE(write_barrier_marking_from_code_function,
                   WriteBarrier::MarkingFromCode)

FUNCTION_REFERENCE(shared_barrier_from_code_function,
                   WriteBarrier::SharedFromCode)

FUNCTION_REFERENCE(insert_remembered_set_function,
                   Heap::InsertIntoRememberedSetFromCode)

FUNCTION_REFERENCE(delete_handle_scope_extensions,
                   HandleScope::DeleteExtensions)

FUNCTION_REFERENCE(ephemeron_key_write_barrier_function,
                   Heap::EphemeronKeyWriteBarrierFromCode)

FUNCTION_REFERENCE(get_date_field_function, JSDate::GetField)

ExternalReference ExternalReference::date_cache_stamp(Isolate* isolate) {
  return ExternalReference(isolate->date_cache()->stamp_address());
}

// static
ExternalReference
ExternalReference::runtime_function_table_address_for_unittests(
    Isolate* isolate) {
  return runtime_function_table_address(isolate);
}

// static
Address ExternalReference::Redirect(Address external_function, Type type) {
#ifdef USE_SIMULATOR
  return SimulatorBase::RedirectExternalReference(external_function, type);
#else
  return external_function;
#endif
}

// static
Address ExternalReference::UnwrapRedirection(Address redirection_trampoline) {
#ifdef USE_SIMULATOR
  return SimulatorBase::UnwrapRedirection(redirection_trampoline);
#else
  return redirection_trampoline;
#endif
}

ExternalReference ExternalReference::stress_deopt_count(Isolate* isolate) {
  return ExternalReference(isolate->stress_deopt_count_address());
}

ExternalReference ExternalReference::force_slow_path(Isolate* isolate) {
  return ExternalReference(isolate->force_slow_path_address());
}

FUNCTION_REFERENCE(new_deoptimizer_function, Deoptimizer::New)

FUNCTION_REFERENCE(compute_output_frames_function,
                   Deoptimizer::ComputeOutputFrames)

IF_WASM(FUNCTION_REFERENCE, wasm_f32_trunc, wasm::f32_trunc_wrapper)
IF_WASM(FUNCTION_REFERENCE, wasm_f32_floor, wasm::f32_floor_wrapper)
IF_WASM(FUNCTION_REFERENCE, wasm_f32_ceil, wasm::f32_ceil_wrapper)
IF_WASM(FUNCTION_REFERENCE, wasm_f32_nearest_int, wasm::f32_nearest_int_wrapper)
IF_WASM(FUNCTION_REFERENCE, wasm_f64_trunc, wasm::f64_trunc_wrapper)
IF_WASM(FUNCTION_REFERENCE, wasm_f64_floor, wasm::f64_floor_wrapper)
IF_WASM(FUNCTION_REFERENCE, wasm_f64_ceil, wasm::f64_ceil_wrapper)
IF_WASM(FUNCTION_REFERENCE, wasm_f64_nearest_int, wasm::f64_nearest_int_wrapper)
IF_WASM(FUNCTION_REFERENCE, wasm_int64_to_float32,
        wasm::int64_to_float32_wrapper)
IF_WASM(FUNCTION_REFERENCE, wasm_uint64_to_float32,
        wasm::uint64_to_float32_wrapper)
IF_WASM(FUNCTION_REFERENCE, wasm_int64_to_float64,
        wasm::int64_to_float64_wrapper)
IF_WASM(FUNCTION_REFERENCE, wasm_uint64_to_float64,
        wasm::uint64_to_float64_wrapper)
IF_WASM(FUNCTION_REFERENCE, wasm_float32_to_int64,
        wasm::float32_to_int64_wrapper)
IF_WASM(FUNCTION_REFERENCE, wasm_float32_to_uint64,
        wasm::float32_to_uint64_wrapper)
IF_WASM(FUNCTION_REFERENCE, wasm_float64_to_int64,
        wasm::float64_to_int64_wrapper)
IF_WASM(FUNCTION_REFERENCE, wasm_float64_to_uint64,
        wasm::float64_to_uint64_wrapper)
IF_WASM(FUNCTION_REFERENCE, wasm_float32_to_int64_sat,
        wasm::float32_to_int64_sat_wrapper)
IF_WASM(FUNCTION_REFERENCE, wasm_float32_to_uint64_sat,
        wasm::float32_to_uint64_sat_wrapper)
IF_WASM(FUNCTION_REFERENCE, wasm_float64_to_int64_sat,
        wasm::float64_to_int64_sat_wrapper)
IF_WASM(FUNCTION_REFERENCE, wasm_float64_to_uint64_sat,
        wasm::float64_to_uint64_sat_wrapper)
IF_WASM(FUNCTION_REFERENCE, wasm_int64_div, wasm::int64_div_wrapper)
IF_WASM(FUNCTION_REFERENCE, wasm_int64_mod, wasm::int64_mod_wrapper)
IF_WASM(FUNCTION_REFERENCE, wasm_uint64_div, wasm::uint64_div_wrapper)
IF_WASM(FUNCTION_REFERENCE, wasm_uint64_mod, wasm::uint64_mod_wrapper)
IF_WASM(FUNCTION_REFERENCE, wasm_word32_ctz, wasm::word32_ctz_wrapper)
IF_WASM(FUNCTION_REFERENCE, wasm_word64_ctz, wasm::word64_ctz_wrapper)
IF_WASM(FUNCTION_REFERENCE, wasm_word32_popcnt, wasm::word32_popcnt_wrapper)
IF_WASM(FUNCTION_REFERENCE, wasm_word64_popcnt, wasm::word64_popcnt_wrapper)
IF_WASM(FUNCTION_REFERENCE, wasm_word32_rol, wasm::word32_rol_wrapper)
IF_WASM(FUNCTION_REFERENCE, wasm_word32_ror, wasm::word32_ror_wrapper)
IF_WASM(FUNCTION_REFERENCE, wasm_word64_rol, wasm::word64_rol_wrapper)
IF_WASM(FUNCTION_REFERENCE, wasm_word64_ror, wasm::word64_ror_wrapper)
IF_WASM(FUNCTION_REFERENCE, wasm_f64x2_ceil, wasm::f64x2_ceil_wrapper)
IF_WASM(FUNCTION_REFERENCE, wasm_f64x2_floor, wasm::f64x2_floor_wrapper)
IF_WASM(FUNCTION_REFERENCE, wasm_f64x2_trunc, wasm::f64x2_trunc_wrapper)
IF_WASM(FUNCTION_REFERENCE, wasm_f64x2_nearest_int,
        wasm::f64x2_nearest_int_wrapper)
IF_WASM(FUNCTION_REFERENCE, wasm_f32x4_ceil, wasm::f32x4_ceil_wrapper)
IF_WASM(FUNCTION_REFERENCE, wasm_f32x4_floor, wasm::f32x4_floor_wrapper)
IF_WASM(FUNCTION_REFERENCE, wasm_f32x4_trunc, wasm::f32x4_trunc_wrapper)
IF_WASM(FUNCTION_REFERENCE, wasm_f32x4_nearest_int,
        wasm::f32x4_nearest_int_wrapper)
IF_WASM(FUNCTION_REFERENCE, wasm_memory_init, wasm::memory_init_wrapper)
IF_WASM(FUNCTION_REFERENCE, wasm_memory_copy, wasm::memory_copy_wrapper)
IF_WASM(FUNCTION_REFERENCE, wasm_memory_fill, wasm::memory_fill_wrapper)
IF_WASM(FUNCTION_REFERENCE, wasm_float64_pow, wasm::float64_pow_wrapper)
IF_WASM(FUNCTION_REFERENCE, wasm_call_trap_callback_for_testing,
        wasm::call_trap_callback_for_testing)
IF_WASM(FUNCTION_REFERENCE, wasm_array_copy, wasm::array_copy_wrapper)
IF_WASM(FUNCTION_REFERENCE, wasm_array_fill_with_zeroes,
        wasm::array_fill_with_zeroes_wrapper)

static void f64_acos_wrapper(Address data) {
  double input = ReadUnalignedValue<double>(data);
  WriteUnalignedValue(data, base::ieee754::acos(input));
}

FUNCTION_REFERENCE(f64_acos_wrapper_function, f64_acos_wrapper)

static void f64_asin_wrapper(Address data) {
  double input = ReadUnalignedValue<double>(data);
  WriteUnalignedValue<double>(data, base::ieee754::asin(input));
}

FUNCTION_REFERENCE(f64_asin_wrapper_function, f64_asin_wrapper)


static void f64_mod_wrapper(Address data) {
  double dividend = ReadUnalignedValue<double>(data);
  double divisor = ReadUnalignedValue<double>(data + sizeof(dividend));
  WriteUnalignedValue<double>(data, Modulo(dividend, divisor));
}

FUNCTION_REFERENCE(f64_mod_wrapper_function, f64_mod_wrapper)

ExternalReference ExternalReference::isolate_root(Isolate* isolate) {
  return ExternalReference(isolate->isolate_root());
}

ExternalReference ExternalReference::allocation_sites_list_address(
    Isolate* isolate) {
  return ExternalReference(isolate->heap()->allocation_sites_list_address());
}

ExternalReference ExternalReference::address_of_jslimit(Isolate* isolate) {
  Address address = isolate->stack_guard()->address_of_jslimit();
  // For efficient generated code, this should be root-register-addressable.
  DCHECK(isolate->root_register_addressable_region().contains(address));
  return ExternalReference(address);
}

ExternalReference ExternalReference::address_of_real_jslimit(Isolate* isolate) {
  Address address = isolate->stack_guard()->address_of_real_jslimit();
  // For efficient generated code, this should be root-register-addressable.
  DCHECK(isolate->root_register_addressable_region().contains(address));
  return ExternalReference(address);
}

ExternalReference ExternalReference::heap_is_marking_flag_address(
    Isolate* isolate) {
  return ExternalReference(isolate->heap()->IsMarkingFlagAddress());
}

ExternalReference ExternalReference::heap_is_minor_marking_flag_address(
    Isolate* isolate) {
  return ExternalReference(isolate->heap()->IsMinorMarkingFlagAddress());
}

ExternalReference ExternalReference::new_space_allocation_top_address(
    Isolate* isolate) {
  return ExternalReference(isolate->heap()->NewSpaceAllocationTopAddress());
}

ExternalReference ExternalReference::new_space_allocation_limit_address(
    Isolate* isolate) {
  return ExternalReference(isolate->heap()->NewSpaceAllocationLimitAddress());
}

ExternalReference ExternalReference::old_space_allocation_top_address(
    Isolate* isolate) {
  return ExternalReference(isolate->heap()->OldSpaceAllocationTopAddress());
}

ExternalReference ExternalReference::old_space_allocation_limit_address(
    Isolate* isolate) {
  return ExternalReference(isolate->heap()->OldSpaceAllocationLimitAddress());
}

ExternalReference ExternalReference::handle_scope_level_address(
    Isolate* isolate) {
  return ExternalReference(HandleScope::current_level_address(isolate));
}

ExternalReference ExternalReference::handle_scope_next_address(
    Isolate* isolate) {
  return ExternalReference(HandleScope::current_next_address(isolate));
}

ExternalReference ExternalReference::handle_scope_limit_address(
    Isolate* isolate) {
  return ExternalReference(HandleScope::current_limit_address(isolate));
}

ExternalReference ExternalReference::scheduled_exception_address(
    Isolate* isolate) {
  return ExternalReference(isolate->scheduled_exception_address());
}

ExternalReference ExternalReference::address_of_pending_message(
    Isolate* isolate) {
  return ExternalReference(isolate->pending_message_address());
}

ExternalReference ExternalReference::address_of_pending_message(
    LocalIsolate* local_isolate) {
  return ExternalReference(local_isolate->pending_message_address());
}

FUNCTION_REFERENCE(abort_with_reason, i::abort_with_reason)

ExternalReference ExternalReference::address_of_min_int() {
  return ExternalReference(reinterpret_cast<Address>(&double_min_int_constant));
}

ExternalReference
ExternalReference::address_of_mock_arraybuffer_allocator_flag() {
  return ExternalReference(&v8_flags.mock_arraybuffer_allocator);
}

ExternalReference
ExternalReference::address_of_FLAG_harmony_regexp_unicode_sets() {
  return ExternalReference(&v8_flags.harmony_regexp_unicode_sets);
}

// TODO(jgruber): Update the other extrefs pointing at v8_flags. addresses to be
// called address_of_FLAG_foo (easier grep-ability).
ExternalReference ExternalReference::address_of_log_or_trace_osr() {
  return ExternalReference(&v8_flags.log_or_trace_osr);
}

ExternalReference ExternalReference::address_of_builtin_subclassing_flag() {
  return ExternalReference(&v8_flags.builtin_subclassing);
}

ExternalReference ExternalReference::address_of_runtime_stats_flag() {
  return ExternalReference(&TracingFlags::runtime_stats);
}

ExternalReference ExternalReference::address_of_shared_string_table_flag() {
  return ExternalReference(&v8_flags.shared_string_table);
}

ExternalReference ExternalReference::address_of_load_from_stack_count(
    const char* function_name) {
  return ExternalReference(
      Isolate::load_from_stack_count_address(function_name));
}

ExternalReference ExternalReference::address_of_store_to_stack_count(
    const char* function_name) {
  return ExternalReference(
      Isolate::store_to_stack_count_address(function_name));
}

ExternalReference ExternalReference::address_of_one_half() {
  return ExternalReference(
      reinterpret_cast<Address>(&double_one_half_constant));
}

ExternalReference ExternalReference::address_of_the_hole_nan() {
  return ExternalReference(
      reinterpret_cast<Address>(&double_the_hole_nan_constant));
}

ExternalReference ExternalReference::address_of_uint32_bias() {
  return ExternalReference(
      reinterpret_cast<Address>(&double_uint32_bias_constant));
}

ExternalReference ExternalReference::address_of_float_abs_constant() {
  return ExternalReference(reinterpret_cast<Address>(&float_absolute_constant));
}

ExternalReference ExternalReference::address_of_float_neg_constant() {
  return ExternalReference(reinterpret_cast<Address>(&float_negate_constant));
}

ExternalReference ExternalReference::address_of_double_abs_constant() {
  return ExternalReference(
      reinterpret_cast<Address>(&double_absolute_constant));
}

ExternalReference ExternalReference::address_of_double_neg_constant() {
  return ExternalReference(reinterpret_cast<Address>(&double_negate_constant));
}

ExternalReference ExternalReference::address_of_wasm_i8x16_swizzle_mask() {
  return ExternalReference(reinterpret_cast<Address>(&wasm_i8x16_swizzle_mask));
}

ExternalReference ExternalReference::address_of_wasm_i8x16_popcnt_mask() {
  return ExternalReference(reinterpret_cast<Address>(&wasm_i8x16_popcnt_mask));
}

ExternalReference ExternalReference::address_of_wasm_i8x16_splat_0x01() {
  return ExternalReference(reinterpret_cast<Address>(&wasm_i8x16_splat_0x01));
}

ExternalReference ExternalReference::address_of_wasm_i8x16_splat_0x0f() {
  return ExternalReference(reinterpret_cast<Address>(&wasm_i8x16_splat_0x0f));
}

ExternalReference ExternalReference::address_of_wasm_i8x16_splat_0x33() {
  return ExternalReference(reinterpret_cast<Address>(&wasm_i8x16_splat_0x33));
}

ExternalReference ExternalReference::address_of_wasm_i8x16_splat_0x55() {
  return ExternalReference(reinterpret_cast<Address>(&wasm_i8x16_splat_0x55));
}

ExternalReference ExternalReference::address_of_wasm_i16x8_splat_0x0001() {
  return ExternalReference(reinterpret_cast<Address>(&wasm_i16x8_splat_0x0001));
}

ExternalReference
ExternalReference::address_of_wasm_f64x2_convert_low_i32x4_u_int_mask() {
  return ExternalReference(
      reinterpret_cast<Address>(&wasm_f64x2_convert_low_i32x4_u_int_mask));
}

ExternalReference ExternalReference::supports_wasm_simd_128_address() {
  return ExternalReference(
      reinterpret_cast<Address>(&CpuFeatures::supports_wasm_simd_128_));
}

ExternalReference ExternalReference::address_of_wasm_double_2_power_52() {
  return ExternalReference(reinterpret_cast<Address>(&wasm_double_2_power_52));
}

ExternalReference ExternalReference::address_of_wasm_int32_max_as_double() {
  return ExternalReference(
      reinterpret_cast<Address>(&wasm_int32_max_as_double));
}

ExternalReference ExternalReference::address_of_wasm_uint32_max_as_double() {
  return ExternalReference(
      reinterpret_cast<Address>(&wasm_uint32_max_as_double));
}

ExternalReference ExternalReference::address_of_wasm_int32_overflow_as_float() {
  return ExternalReference(
      reinterpret_cast<Address>(&wasm_int32_overflow_as_float));
}

ExternalReference ExternalReference::supports_cetss_address() {
  return ExternalReference(
      reinterpret_cast<Address>(&CpuFeatures::supports_cetss_));
}

ExternalReference
ExternalReference::address_of_enable_experimental_regexp_engine() {
  return ExternalReference(&v8_flags.enable_experimental_regexp_engine);
}

namespace {

static uintptr_t BaselinePCForBytecodeOffset(Address raw_code_obj,
                                             int bytecode_offset,
                                             Address raw_bytecode_array) {
  Code code_obj = Code::cast(Object(raw_code_obj));
  BytecodeArray bytecode_array =
      BytecodeArray::cast(Object(raw_bytecode_array));
  return code_obj.GetBaselineStartPCForBytecodeOffset(bytecode_offset,
                                                      bytecode_array);
}

static uintptr_t BaselinePCForNextExecutedBytecode(Address raw_code_obj,
                                                   int bytecode_offset,
                                                   Address raw_bytecode_array) {
  Code code_obj = Code::cast(Object(raw_code_obj));
  BytecodeArray bytecode_array =
      BytecodeArray::cast(Object(raw_bytecode_array));
  return code_obj.GetBaselinePCForNextExecutedBytecode(bytecode_offset,
                                                       bytecode_array);
}

}  // namespace

FUNCTION_REFERENCE(baseline_pc_for_bytecode_offset, BaselinePCForBytecodeOffset)
FUNCTION_REFERENCE(baseline_pc_for_next_executed_bytecode,
                   BaselinePCForNextExecutedBytecode)

ExternalReference ExternalReference::thread_in_wasm_flag_address_address(
    Isolate* isolate) {
  return ExternalReference(isolate->thread_in_wasm_flag_address_address());
}

ExternalReference ExternalReference::invoke_function_callback() {
  Address thunk_address = FUNCTION_ADDR(&InvokeFunctionCallback);
  ExternalReference::Type thunk_type = ExternalReference::PROFILING_API_CALL;
  ApiFunction thunk_fun(thunk_address);
  return ExternalReference::Create(&thunk_fun, thunk_type);
}

ExternalReference ExternalReference::invoke_accessor_getter_callback() {
  Address thunk_address = FUNCTION_ADDR(&InvokeAccessorGetterCallback);
  ExternalReference::Type thunk_type = ExternalReference::PROFILING_GETTER_CALL;
  ApiFunction thunk_fun(thunk_address);
  return ExternalReference::Create(&thunk_fun, thunk_type);
}

#if V8_TARGET_ARCH_X64
#define re_stack_check_func RegExpMacroAssemblerX64::CheckStackGuardState
#elif V8_TARGET_ARCH_IA32
#define re_stack_check_func RegExpMacroAssemblerIA32::CheckStackGuardState
#elif V8_TARGET_ARCH_ARM64
#define re_stack_check_func RegExpMacroAssemblerARM64::CheckStackGuardState
#elif V8_TARGET_ARCH_ARM
#define re_stack_check_func RegExpMacroAssemblerARM::CheckStackGuardState
#elif V8_TARGET_ARCH_PPC || V8_TARGET_ARCH_PPC64
#define re_stack_check_func RegExpMacroAssemblerPPC::CheckStackGuardState
#elif V8_TARGET_ARCH_MIPS
#define re_stack_check_func RegExpMacroAssemblerMIPS::CheckStackGuardState
#elif V8_TARGET_ARCH_MIPS64
#define re_stack_check_func RegExpMacroAssemblerMIPS::CheckStackGuardState
#elif V8_TARGET_ARCH_LOONG64
#define re_stack_check_func RegExpMacroAssemblerLOONG64::CheckStackGuardState
#elif V8_TARGET_ARCH_S390
#define re_stack_check_func RegExpMacroAssemblerS390::CheckStackGuardState
#elif V8_TARGET_ARCH_RISCV32 || V8_TARGET_ARCH_RISCV64
#define re_stack_check_func RegExpMacroAssemblerRISCV::CheckStackGuardState
#else
UNREACHABLE();
#endif

FUNCTION_REFERENCE(re_check_stack_guard_state, re_stack_check_func)
#undef re_stack_check_func

FUNCTION_REFERENCE(re_grow_stack, NativeRegExpMacroAssembler::GrowStack)

FUNCTION_REFERENCE(re_match_for_call_from_js,
                   IrregexpInterpreter::MatchForCallFromJs)

FUNCTION_REFERENCE(re_experimental_match_for_call_from_js,
                   ExperimentalRegExp::MatchForCallFromJs)

FUNCTION_REFERENCE(re_case_insensitive_compare_unicode,
                   NativeRegExpMacroAssembler::CaseInsensitiveCompareUnicode)

FUNCTION_REFERENCE(re_case_insensitive_compare_non_unicode,
                   NativeRegExpMacroAssembler::CaseInsensitiveCompareNonUnicode)

FUNCTION_REFERENCE(re_is_character_in_range_array,
                   RegExpMacroAssembler::IsCharacterInRangeArray)

ExternalReference ExternalReference::re_word_character_map() {
  return ExternalReference(
      NativeRegExpMacroAssembler::word_character_map_address());
}

ExternalReference ExternalReference::address_of_static_offsets_vector(
    Isolate* isolate) {
  return ExternalReference(
      reinterpret_cast<Address>(isolate->jsregexp_static_offsets_vector()));
}

ExternalReference ExternalReference::address_of_regexp_stack_limit_address(
    Isolate* isolate) {
  return ExternalReference(isolate->regexp_stack()->limit_address_address());
}

ExternalReference ExternalReference::address_of_regexp_stack_memory_top_address(
    Isolate* isolate) {
  return ExternalReference(
      isolate->regexp_stack()->memory_top_address_address());
}

ExternalReference ExternalReference::address_of_regexp_stack_stack_pointer(
    Isolate* isolate) {
  return ExternalReference(isolate->regexp_stack()->stack_pointer_address());
}

ExternalReference ExternalReference::javascript_execution_assert(
    Isolate* isolate) {
  return ExternalReference(isolate->javascript_execution_assert_address());
}

FUNCTION_REFERENCE_WITH_TYPE(ieee754_acos_function, base::ieee754::acos,
                             BUILTIN_FP_CALL)
FUNCTION_REFERENCE_WITH_TYPE(ieee754_acosh_function, base::ieee754::acosh,
                             BUILTIN_FP_CALL)
FUNCTION_REFERENCE_WITH_TYPE(ieee754_asin_function, base::ieee754::asin,
                             BUILTIN_FP_CALL)
FUNCTION_REFERENCE_WITH_TYPE(ieee754_asinh_function, base::ieee754::asinh,
                             BUILTIN_FP_CALL)
FUNCTION_REFERENCE_WITH_TYPE(ieee754_atan_function, base::ieee754::atan,
                             BUILTIN_FP_CALL)
FUNCTION_REFERENCE_WITH_TYPE(ieee754_atanh_function, base::ieee754::atanh,
                             BUILTIN_FP_CALL)
FUNCTION_REFERENCE_WITH_TYPE(ieee754_atan2_function, base::ieee754::atan2,
                             BUILTIN_FP_FP_CALL)
FUNCTION_REFERENCE_WITH_TYPE(ieee754_cbrt_function, base::ieee754::cbrt,
                             BUILTIN_FP_CALL)
FUNCTION_REFERENCE_WITH_TYPE(ieee754_cos_function, base::ieee754::cos,
                             BUILTIN_FP_CALL)
FUNCTION_REFERENCE_WITH_TYPE(ieee754_cosh_function, base::ieee754::cosh,
                             BUILTIN_FP_CALL)
FUNCTION_REFERENCE_WITH_TYPE(ieee754_exp_function, base::ieee754::exp,
                             BUILTIN_FP_CALL)
FUNCTION_REFERENCE_WITH_TYPE(ieee754_expm1_function, base::ieee754::expm1,
                             BUILTIN_FP_CALL)
FUNCTION_REFERENCE_WITH_TYPE(ieee754_log_function, base::ieee754::log,
                             BUILTIN_FP_CALL)
FUNCTION_REFERENCE_WITH_TYPE(ieee754_log1p_function, base::ieee754::log1p,
                             BUILTIN_FP_CALL)
FUNCTION_REFERENCE_WITH_TYPE(ieee754_log10_function, base::ieee754::log10,
                             BUILTIN_FP_CALL)
FUNCTION_REFERENCE_WITH_TYPE(ieee754_log2_function, base::ieee754::log2,
                             BUILTIN_FP_CALL)
FUNCTION_REFERENCE_WITH_TYPE(ieee754_sin_function, base::ieee754::sin,
                             BUILTIN_FP_CALL)
FUNCTION_REFERENCE_WITH_TYPE(ieee754_sinh_function, base::ieee754::sinh,
                             BUILTIN_FP_CALL)
FUNCTION_REFERENCE_WITH_TYPE(ieee754_tan_function, base::ieee754::tan,
                             BUILTIN_FP_CALL)
FUNCTION_REFERENCE_WITH_TYPE(ieee754_tanh_function, base::ieee754::tanh,
                             BUILTIN_FP_CALL)
FUNCTION_REFERENCE_WITH_TYPE(ieee754_pow_function, base::ieee754::pow,
                             BUILTIN_FP_FP_CALL)

void* libc_memchr(void* string, int character, size_t search_length) {
  return memchr(string, character, search_length);
}

FUNCTION_REFERENCE(libc_memchr_function, libc_memchr)

void* libc_memcpy(void* dest, const void* src, size_t n) {
  return memcpy(dest, src, n);
}

FUNCTION_REFERENCE(libc_memcpy_function, libc_memcpy)

void* libc_memmove(void* dest, const void* src, size_t n) {
  return memmove(dest, src, n);
}

FUNCTION_REFERENCE(libc_memmove_function, libc_memmove)

void* libc_memset(void* dest, int value, size_t n) {
  DCHECK_EQ(static_cast<byte>(value), value);
  return memset(dest, value, n);
}

FUNCTION_REFERENCE(libc_memset_function, libc_memset)

void relaxed_memcpy(volatile base::Atomic8* dest,
                    volatile const base::Atomic8* src, size_t n) {
  base::Relaxed_Memcpy(dest, src, n);
}

FUNCTION_REFERENCE(relaxed_memcpy_function, relaxed_memcpy)

void relaxed_memmove(volatile base::Atomic8* dest,
                     volatile const base::Atomic8* src, size_t n) {
  base::Relaxed_Memmove(dest, src, n);
}

FUNCTION_REFERENCE(relaxed_memmove_function, relaxed_memmove)

ExternalReference ExternalReference::printf_function() {
  return ExternalReference(Redirect(FUNCTION_ADDR(std::printf)));
}

FUNCTION_REFERENCE(refill_math_random, MathRandom::RefillCache)

template <typename SubjectChar, typename PatternChar>
ExternalReference ExternalReference::search_string_raw() {
  auto f = SearchStringRaw<SubjectChar, PatternChar>;
  return ExternalReference(Redirect(FUNCTION_ADDR(f)));
}

FUNCTION_REFERENCE(jsarray_array_join_concat_to_sequential_string,
                   JSArray::ArrayJoinConcatToSequentialString)

FUNCTION_REFERENCE(gsab_byte_length, JSArrayBuffer::GsabByteLength)

ExternalReference ExternalReference::search_string_raw_one_one() {
  return search_string_raw<const uint8_t, const uint8_t>();
}

ExternalReference ExternalReference::search_string_raw_one_two() {
  return search_string_raw<const uint8_t, const base::uc16>();
}

ExternalReference ExternalReference::search_string_raw_two_one() {
  return search_string_raw<const base::uc16, const uint8_t>();
}

ExternalReference ExternalReference::search_string_raw_two_two() {
  return search_string_raw<const base::uc16, const base::uc16>();
}

namespace {

void StringWriteToFlatOneByte(Address source, uint8_t* sink, int32_t start,
                              int32_t length) {
  return String::WriteToFlat<uint8_t>(String::cast(Object(source)), sink, start,
                                      length);
}

void StringWriteToFlatTwoByte(Address source, uint16_t* sink, int32_t start,
                              int32_t length) {
  return String::WriteToFlat<uint16_t>(String::cast(Object(source)), sink,
                                       start, length);
}

const uint8_t* ExternalOneByteStringGetChars(Address string) {
  PtrComprCageBase cage_base = GetPtrComprCageBaseFromOnHeapAddress(string);
  // The following CHECK is a workaround to prevent a CFI bug where
  // ExternalOneByteStringGetChars() and ExternalTwoByteStringGetChars() are
  // merged by the linker, resulting in one of the input type's vtable address
  // failing the address range check.
  // TODO(chromium:1160961): Consider removing the CHECK when CFI is fixed.
  CHECK(Object(string).IsExternalOneByteString(cage_base));
  return ExternalOneByteString::cast(Object(string)).GetChars(cage_base);
}
const uint16_t* ExternalTwoByteStringGetChars(Address string) {
  PtrComprCageBase cage_base = GetPtrComprCageBaseFromOnHeapAddress(string);
  // The following CHECK is a workaround to prevent a CFI bug where
  // ExternalOneByteStringGetChars() and ExternalTwoByteStringGetChars() are
  // merged by the linker, resulting in one of the input type's vtable address
  // failing the address range check.
  // TODO(chromium:1160961): Consider removing the CHECK when CFI is fixed.
  CHECK(Object(string).IsExternalTwoByteString(cage_base));
  return ExternalTwoByteString::cast(Object(string)).GetChars(cage_base);
}

}  // namespace

FUNCTION_REFERENCE(string_write_to_flat_one_byte, StringWriteToFlatOneByte)
FUNCTION_REFERENCE(string_write_to_flat_two_byte, StringWriteToFlatTwoByte)

FUNCTION_REFERENCE(external_one_byte_string_get_chars,
                   ExternalOneByteStringGetChars)
FUNCTION_REFERENCE(external_two_byte_string_get_chars,
                   ExternalTwoByteStringGetChars)

FUNCTION_REFERENCE(orderedhashmap_gethash_raw, OrderedHashMap::GetHash)

Address GetOrCreateHash(Isolate* isolate, Address raw_key) {
  DisallowGarbageCollection no_gc;
  return Object(raw_key).GetOrCreateHash(isolate).ptr();
}

FUNCTION_REFERENCE(get_or_create_hash_raw, GetOrCreateHash)

static Address JSReceiverCreateIdentityHash(Isolate* isolate, Address raw_key) {
  JSReceiver key = JSReceiver::cast(Object(raw_key));
  return JSReceiver::CreateIdentityHash(isolate, key).ptr();
}

FUNCTION_REFERENCE(jsreceiver_create_identity_hash,
                   JSReceiverCreateIdentityHash)

static uint32_t ComputeSeededIntegerHash(Isolate* isolate, int32_t key) {
  DisallowGarbageCollection no_gc;
  return ComputeSeededHash(static_cast<uint32_t>(key), HashSeed(isolate));
}

FUNCTION_REFERENCE(compute_integer_hash, ComputeSeededIntegerHash)
FUNCTION_REFERENCE(copy_fast_number_jsarray_elements_to_typed_array,
                   CopyFastNumberJSArrayElementsToTypedArray)
FUNCTION_REFERENCE(copy_typed_array_elements_to_typed_array,
                   CopyTypedArrayElementsToTypedArray)
FUNCTION_REFERENCE(copy_typed_array_elements_slice, CopyTypedArrayElementsSlice)
FUNCTION_REFERENCE(try_string_to_index_or_lookup_existing,
                   StringTable::TryStringToIndexOrLookupExisting)
FUNCTION_REFERENCE(string_from_forward_table,
                   StringForwardingTable::GetForwardStringAddress)
FUNCTION_REFERENCE(string_to_array_index_function, String::ToArrayIndex)
FUNCTION_REFERENCE(array_indexof_includes_smi_or_object,
                   ArrayIndexOfIncludesSmiOrObject)
FUNCTION_REFERENCE(array_indexof_includes_double, ArrayIndexOfIncludesDouble)

static Address LexicographicCompareWrapper(Isolate* isolate, Address smi_x,
                                           Address smi_y) {
  Smi x(smi_x);
  Smi y(smi_y);
  return Smi::LexicographicCompare(isolate, x, y);
}

FUNCTION_REFERENCE(smi_lexicographic_compare_function,
                   LexicographicCompareWrapper)

FUNCTION_REFERENCE(mutable_big_int_absolute_add_and_canonicalize_function,
                   MutableBigInt_AbsoluteAddAndCanonicalize)

FUNCTION_REFERENCE(mutable_big_int_absolute_compare_function,
                   MutableBigInt_AbsoluteCompare)

FUNCTION_REFERENCE(mutable_big_int_absolute_sub_and_canonicalize_function,
                   MutableBigInt_AbsoluteSubAndCanonicalize)

FUNCTION_REFERENCE(mutable_big_int_absolute_mul_and_canonicalize_function,
                   MutableBigInt_AbsoluteMulAndCanonicalize)

FUNCTION_REFERENCE(mutable_big_int_absolute_div_and_canonicalize_function,
                   MutableBigInt_AbsoluteDivAndCanonicalize)

FUNCTION_REFERENCE(mutable_big_int_bitwise_and_pp_and_canonicalize_function,
                   MutableBigInt_BitwiseAndPosPosAndCanonicalize)

FUNCTION_REFERENCE(mutable_big_int_bitwise_and_nn_and_canonicalize_function,
                   MutableBigInt_BitwiseAndNegNegAndCanonicalize)

FUNCTION_REFERENCE(mutable_big_int_bitwise_and_pn_and_canonicalize_function,
                   MutableBigInt_BitwiseAndPosNegAndCanonicalize)

FUNCTION_REFERENCE(check_object_type, CheckObjectType)

#ifdef V8_INTL_SUPPORT

static Address ConvertOneByteToLower(Address raw_src, Address raw_dst) {
  String src = String::cast(Object(raw_src));
  String dst = String::cast(Object(raw_dst));
  return Intl::ConvertOneByteToLower(src, dst).ptr();
}
FUNCTION_REFERENCE(intl_convert_one_byte_to_lower, ConvertOneByteToLower)

ExternalReference ExternalReference::intl_to_latin1_lower_table() {
  uint8_t* ptr = const_cast<uint8_t*>(Intl::ToLatin1LowerTable());
  return ExternalReference(reinterpret_cast<Address>(ptr));
}

ExternalReference ExternalReference::intl_ascii_collation_weights_l1() {
  uint8_t* ptr = const_cast<uint8_t*>(Intl::AsciiCollationWeightsL1());
  return ExternalReference(reinterpret_cast<Address>(ptr));
}

ExternalReference ExternalReference::intl_ascii_collation_weights_l3() {
  uint8_t* ptr = const_cast<uint8_t*>(Intl::AsciiCollationWeightsL3());
  return ExternalReference(reinterpret_cast<Address>(ptr));
}

#endif  // V8_INTL_SUPPORT

// Explicit instantiations for all combinations of 1- and 2-byte strings.
template ExternalReference
ExternalReference::search_string_raw<const uint8_t, const uint8_t>();
template ExternalReference
ExternalReference::search_string_raw<const uint8_t, const base::uc16>();
template ExternalReference
ExternalReference::search_string_raw<const base::uc16, const uint8_t>();
template ExternalReference
ExternalReference::search_string_raw<const base::uc16, const base::uc16>();

ExternalReference ExternalReference::FromRawAddress(Address address) {
  return ExternalReference(address);
}

ExternalReference ExternalReference::cpu_features() {
  DCHECK(CpuFeatures::initialized_);
  return ExternalReference(&CpuFeatures::supported_);
}

ExternalReference ExternalReference::promise_hook_flags_address(
    Isolate* isolate) {
  return ExternalReference(isolate->promise_hook_flags_address());
}

ExternalReference ExternalReference::promise_hook_address(Isolate* isolate) {
  return ExternalReference(isolate->promise_hook_address());
}

ExternalReference ExternalReference::async_event_delegate_address(
    Isolate* isolate) {
  return ExternalReference(isolate->async_event_delegate_address());
}

ExternalReference ExternalReference::debug_execution_mode_address(
    Isolate* isolate) {
  return ExternalReference(isolate->debug_execution_mode_address());
}

ExternalReference ExternalReference::debug_is_active_address(Isolate* isolate) {
  return ExternalReference(isolate->debug()->is_active_address());
}

ExternalReference ExternalReference::debug_hook_on_function_call_address(
    Isolate* isolate) {
  return ExternalReference(isolate->debug()->hook_on_function_call_address());
}

ExternalReference ExternalReference::runtime_function_table_address(
    Isolate* isolate) {
  return ExternalReference(
      const_cast<Runtime::Function*>(Runtime::RuntimeFunctionTable(isolate)));
}

static Address InvalidatePrototypeChainsWrapper(Address raw_map) {
  Map map = Map::cast(Object(raw_map));
  return JSObject::InvalidatePrototypeChains(map).ptr();
}

FUNCTION_REFERENCE(invalidate_prototype_chains_function,
                   InvalidatePrototypeChainsWrapper)

double modulo_double_double(double x, double y) { return Modulo(x, y); }

FUNCTION_REFERENCE_WITH_TYPE(mod_two_doubles_operation, modulo_double_double,
                             BUILTIN_FP_FP_CALL)

ExternalReference ExternalReference::debug_suspended_generator_address(
    Isolate* isolate) {
  return ExternalReference(isolate->debug()->suspended_generator_address());
}

ExternalReference ExternalReference::fast_c_call_caller_fp_address(
    Isolate* isolate) {
  return ExternalReference(
      isolate->isolate_data()->fast_c_call_caller_fp_address());
}

ExternalReference ExternalReference::fast_c_call_caller_pc_address(
    Isolate* isolate) {
  return ExternalReference(
      isolate->isolate_data()->fast_c_call_caller_pc_address());
}

ExternalReference ExternalReference::fast_api_call_target_address(
    Isolate* isolate) {
  return ExternalReference(
      isolate->isolate_data()->fast_api_call_target_address());
}

ExternalReference ExternalReference::stack_is_iterable_address(
    Isolate* isolate) {
  return ExternalReference(
      isolate->isolate_data()->stack_is_iterable_address());
}

ExternalReference ExternalReference::is_profiling_address(Isolate* isolate) {
  return ExternalReference(isolate->isolate_data()->is_profiling_address());
}

FUNCTION_REFERENCE(call_enqueue_microtask_function,
                   MicrotaskQueue::CallEnqueueMicrotask)

static int64_t atomic_pair_load(intptr_t address) {
  return std::atomic_load(reinterpret_cast<std::atomic<int64_t>*>(address));
}

ExternalReference ExternalReference::atomic_pair_load_function() {
  return ExternalReference(Redirect(FUNCTION_ADDR(atomic_pair_load)));
}

static void atomic_pair_store(intptr_t address, int value_low, int value_high) {
  int64_t value =
      static_cast<int64_t>(value_high) << 32 | (value_low & 0xFFFFFFFF);
  std::atomic_store(reinterpret_cast<std::atomic<int64_t>*>(address), value);
}

ExternalReference ExternalReference::atomic_pair_store_function() {
  return ExternalReference(Redirect(FUNCTION_ADDR(atomic_pair_store)));
}

static int64_t atomic_pair_add(intptr_t address, int value_low,
                               int value_high) {
  int64_t value =
      static_cast<int64_t>(value_high) << 32 | (value_low & 0xFFFFFFFF);
  return std::atomic_fetch_add(reinterpret_cast<std::atomic<int64_t>*>(address),
                               value);
}

ExternalReference ExternalReference::atomic_pair_add_function() {
  return ExternalReference(Redirect(FUNCTION_ADDR(atomic_pair_add)));
}

static int64_t atomic_pair_sub(intptr_t address, int value_low,
                               int value_high) {
  int64_t value =
      static_cast<int64_t>(value_high) << 32 | (value_low & 0xFFFFFFFF);
  return std::atomic_fetch_sub(reinterpret_cast<std::atomic<int64_t>*>(address),
                               value);
}

ExternalReference ExternalReference::atomic_pair_sub_function() {
  return ExternalReference(Redirect(FUNCTION_ADDR(atomic_pair_sub)));
}

static int64_t atomic_pair_and(intptr_t address, int value_low,
                               int value_high) {
  int64_t value =
      static_cast<int64_t>(value_high) << 32 | (value_low & 0xFFFFFFFF);
  return std::atomic_fetch_and(reinterpret_cast<std::atomic<int64_t>*>(address),
                               value);
}

ExternalReference ExternalReference::atomic_pair_and_function() {
  return ExternalReference(Redirect(FUNCTION_ADDR(atomic_pair_and)));
}

static int64_t atomic_pair_or(intptr_t address, int value_low, int value_high) {
  int64_t value =
      static_cast<int64_t>(value_high) << 32 | (value_low & 0xFFFFFFFF);
  return std::atomic_fetch_or(reinterpret_cast<std::atomic<int64_t>*>(address),
                              value);
}

ExternalReference ExternalReference::atomic_pair_or_function() {
  return ExternalReference(Redirect(FUNCTION_ADDR(atomic_pair_or)));
}

static int64_t atomic_pair_xor(intptr_t address, int value_low,
                               int value_high) {
  int64_t value =
      static_cast<int64_t>(value_high) << 32 | (value_low & 0xFFFFFFFF);
  return std::atomic_fetch_xor(reinterpret_cast<std::atomic<int64_t>*>(address),
                               value);
}

ExternalReference ExternalReference::atomic_pair_xor_function() {
  return ExternalReference(Redirect(FUNCTION_ADDR(atomic_pair_xor)));
}

static int64_t atomic_pair_exchange(intptr_t address, int value_low,
                                    int value_high) {
  int64_t value =
      static_cast<int64_t>(value_high) << 32 | (value_low & 0xFFFFFFFF);
  return std::atomic_exchange(reinterpret_cast<std::atomic<int64_t>*>(address),
                              value);
}

ExternalReference ExternalReference::atomic_pair_exchange_function() {
  return ExternalReference(Redirect(FUNCTION_ADDR(atomic_pair_exchange)));
}

static uint64_t atomic_pair_compare_exchange(intptr_t address,
                                             int old_value_low,
                                             int old_value_high,
                                             int new_value_low,
                                             int new_value_high) {
  uint64_t old_value = static_cast<uint64_t>(old_value_high) << 32 |
                       (old_value_low & 0xFFFFFFFF);
  uint64_t new_value = static_cast<uint64_t>(new_value_high) << 32 |
                       (new_value_low & 0xFFFFFFFF);
  std::atomic_compare_exchange_strong(
      reinterpret_cast<std::atomic<uint64_t>*>(address), &old_value, new_value);
  return old_value;
}

FUNCTION_REFERENCE(atomic_pair_compare_exchange_function,
                   atomic_pair_compare_exchange)

#ifdef V8_IS_TSAN
namespace {
// Mimics the store in generated code by having a relaxed store to the same
// address, with the same value. This is done in order for TSAN to see these
// stores from generated code.
// Note that {value} is an int64_t irrespective of the store size. This is on
// purpose to keep the function signatures the same across stores. The
// static_cast inside the method will ignore the bits which will not be stored.
void tsan_relaxed_store_8_bits(Address addr, int64_t value) {
#if V8_TARGET_ARCH_X64
  base::Relaxed_Store(reinterpret_cast<base::Atomic8*>(addr),
                      static_cast<base::Atomic8>(value));
#else
  UNREACHABLE();
#endif  // V8_TARGET_ARCH_X64
}

void tsan_relaxed_store_16_bits(Address addr, int64_t value) {
#if V8_TARGET_ARCH_X64
  base::Relaxed_Store(reinterpret_cast<base::Atomic16*>(addr),
                      static_cast<base::Atomic16>(value));
#else
  UNREACHABLE();
#endif  // V8_TARGET_ARCH_X64
}

void tsan_relaxed_store_32_bits(Address addr, int64_t value) {
#if V8_TARGET_ARCH_X64
    base::Relaxed_Store(reinterpret_cast<base::Atomic32*>(addr),
                        static_cast<base::Atomic32>(value));
#else
  UNREACHABLE();
#endif  // V8_TARGET_ARCH_X64
}

void tsan_relaxed_store_64_bits(Address addr, int64_t value) {
#if V8_TARGET_ARCH_X64
  base::Relaxed_Store(reinterpret_cast<base::Atomic64*>(addr),
                      static_cast<base::Atomic64>(value));
#else
  UNREACHABLE();
#endif  // V8_TARGET_ARCH_X64
}

// Same as above, for sequentially consistent stores.
void tsan_seq_cst_store_8_bits(Address addr, int64_t value) {
#if V8_TARGET_ARCH_X64
  base::SeqCst_Store(reinterpret_cast<base::Atomic8*>(addr),
                     static_cast<base::Atomic8>(value));
#else
  UNREACHABLE();
#endif  // V8_TARGET_ARCH_X64
}

void tsan_seq_cst_store_16_bits(Address addr, int64_t value) {
#if V8_TARGET_ARCH_X64
  base::SeqCst_Store(reinterpret_cast<base::Atomic16*>(addr),
                     static_cast<base::Atomic16>(value));
#else
  UNREACHABLE();
#endif  // V8_TARGET_ARCH_X64
}

void tsan_seq_cst_store_32_bits(Address addr, int64_t value) {
#if V8_TARGET_ARCH_X64
  base::SeqCst_Store(reinterpret_cast<base::Atomic32*>(addr),
                     static_cast<base::Atomic32>(value));
#else
  UNREACHABLE();
#endif  // V8_TARGET_ARCH_X64
}

void tsan_seq_cst_store_64_bits(Address addr, int64_t value) {
#if V8_TARGET_ARCH_X64
  base::SeqCst_Store(reinterpret_cast<base::Atomic64*>(addr),
                     static_cast<base::Atomic64>(value));
#else
  UNREACHABLE();
#endif  // V8_TARGET_ARCH_X64
}

// Same as above, for relaxed loads.
base::Atomic32 tsan_relaxed_load_32_bits(Address addr, int64_t value) {
#if V8_TARGET_ARCH_X64
  return base::Relaxed_Load(reinterpret_cast<base::Atomic32*>(addr));
#else
  UNREACHABLE();
#endif  // V8_TARGET_ARCH_X64
}

base::Atomic64 tsan_relaxed_load_64_bits(Address addr, int64_t value) {
#if V8_TARGET_ARCH_X64
  return base::Relaxed_Load(reinterpret_cast<base::Atomic64*>(addr));
#else
  UNREACHABLE();
#endif  // V8_TARGET_ARCH_X64
}

}  // namespace
#endif  // V8_IS_TSAN

IF_TSAN(FUNCTION_REFERENCE, tsan_relaxed_store_function_8_bits,
        tsan_relaxed_store_8_bits)
IF_TSAN(FUNCTION_REFERENCE, tsan_relaxed_store_function_16_bits,
        tsan_relaxed_store_16_bits)
IF_TSAN(FUNCTION_REFERENCE, tsan_relaxed_store_function_32_bits,
        tsan_relaxed_store_32_bits)
IF_TSAN(FUNCTION_REFERENCE, tsan_relaxed_store_function_64_bits,
        tsan_relaxed_store_64_bits)
IF_TSAN(FUNCTION_REFERENCE, tsan_seq_cst_store_function_8_bits,
        tsan_seq_cst_store_8_bits)
IF_TSAN(FUNCTION_REFERENCE, tsan_seq_cst_store_function_16_bits,
        tsan_seq_cst_store_16_bits)
IF_TSAN(FUNCTION_REFERENCE, tsan_seq_cst_store_function_32_bits,
        tsan_seq_cst_store_32_bits)
IF_TSAN(FUNCTION_REFERENCE, tsan_seq_cst_store_function_64_bits,
        tsan_seq_cst_store_64_bits)
IF_TSAN(FUNCTION_REFERENCE, tsan_relaxed_load_function_32_bits,
        tsan_relaxed_load_32_bits)
IF_TSAN(FUNCTION_REFERENCE, tsan_relaxed_load_function_64_bits,
        tsan_relaxed_load_64_bits)

static int EnterMicrotaskContextWrapper(HandleScopeImplementer* hsi,
                                        Address raw_context) {
  Context context = Context::cast(Object(raw_context));
  hsi->EnterMicrotaskContext(context);
  return 0;
}

FUNCTION_REFERENCE(call_enter_context_function, EnterMicrotaskContextWrapper)

FUNCTION_REFERENCE(
    js_finalization_registry_remove_cell_from_unregister_token_map,
    JSFinalizationRegistry::RemoveCellFromUnregisterTokenMap)

bool operator==(ExternalReference lhs, ExternalReference rhs) {
  return lhs.address() == rhs.address();
}

bool operator!=(ExternalReference lhs, ExternalReference rhs) {
  return !(lhs == rhs);
}

size_t hash_value(ExternalReference reference) {
  if (v8_flags.predictable) {
    // Avoid ASLR non-determinism in predictable mode. For this, just take the
    // lowest 12 bit corresponding to a 4K page size.
    return base::hash<Address>()(reference.address() & 0xfff);
  }
  return base::hash<Address>()(reference.address());
}

std::ostream& operator<<(std::ostream& os, ExternalReference reference) {
  os << reinterpret_cast<const void*>(reference.address());
  const Runtime::Function* fn = Runtime::FunctionForEntry(reference.address());
  if (fn) os << "<" << fn->name << ".entry>";
  return os;
}

void abort_with_reason(int reason) {
  if (IsValidAbortReason(reason)) {
    const char* message = GetAbortReason(static_cast<AbortReason>(reason));
    base::OS::PrintError("abort: %s\n", message);
  } else {
    base::OS::PrintError("abort: <unknown reason: %d>\n", reason);
  }
  base::OS::Abort();
  UNREACHABLE();
}

#undef FUNCTION_REFERENCE
#undef FUNCTION_REFERENCE_WITH_TYPE

}  // namespace internal
}  // namespace v8