// Copyright (c) 2011, the Dart project authors.  Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.

#ifndef RUNTIME_VM_STUB_CODE_H_
#define RUNTIME_VM_STUB_CODE_H_

#include "vm/allocation.h"
#include "vm/compiler/runtime_api.h"
#include "vm/object.h"
#include "vm/stub_code_list.h"

#if !defined(DART_PRECOMPILED_RUNTIME)
#include "vm/compiler/assembler/assembler.h"
#include "vm/compiler/stub_code_compiler.h"
#endif  // !defined(DART_PRECOMPILED_RUNTIME)

namespace dart {

// Forward declarations.
class Code;
class Isolate;
class ObjectPointerVisitor;

// Is it permitted for the stubs above to refer to Object::null(), which is
// allocated in the VM isolate and shared across all isolates.
// However, in cases where a simple GC-safe placeholder is needed on the stack,
// using Smi 0 instead of Object::null() is slightly more efficient, since a Smi
// does not require relocation.

// class StubCode is used to maintain the lifecycle of stubs.
class StubCode : public AllStatic {
 public:
  // Generate all stubs which are shared across all isolates, this is done
  // only once and the stub code resides in the vm_isolate heap.
  static void Init();

  static void Cleanup();

  // Returns true if stub code has been initialized.
  static bool HasBeenInitialized() {
    return initialized_.load(std::memory_order_acquire);
  }
  static void InitializationDone() {
    initialized_.store(true, std::memory_order_release);
  }

  // Check if specified pc is in the dart invocation stub used for
  // transitioning into dart code.
  static bool InInvocationStub(uword pc);

  // Check if the specified pc is in the jump to frame stub.
  static bool InJumpToFrameStub(uword pc);

  // Returns NULL if no stub found.
  static const char* NameOfStub(uword entry_point);

// Define the shared stub code accessors.
#define STUB_CODE_ACCESSOR(name)                                               \
  static const Code& name() { return *entries_[k##name##Index].code; }         \
  static intptr_t name##Size() { return name().Size(); }
  VM_STUB_CODE_LIST(STUB_CODE_ACCESSOR);
#undef STUB_CODE_ACCESSOR

#if !defined(DART_PRECOMPILED_RUNTIME)
  static CodePtr GetAllocationStubForClass(const Class& cls);
  static CodePtr GetAllocationStubForTypedData(classid_t class_id);
#endif  // !defined(DART_PRECOMPILED_RUNTIME)

#if !defined(TARGET_ARCH_IA32)
  static CodePtr GetBuildGenericMethodExtractorStub(
      compiler::ObjectPoolBuilder* pool) {
    return GetBuildMethodExtractorStub(pool, /*generic=*/true);
  }
  static CodePtr GetBuildNonGenericMethodExtractorStub(
      compiler::ObjectPoolBuilder* pool) {
    return GetBuildMethodExtractorStub(pool, /*generic=*/false);
  }
#endif

#if !defined(DART_PRECOMPILED_RUNTIME)
  // Generate the stub and finalize the generated code into the stub
  // code executable area.
  static CodePtr Generate(const char* name,
                          compiler::ObjectPoolBuilder* object_pool_builder,
                          void (*GenerateStub)(compiler::Assembler* assembler));
#endif  // !defined(DART_PRECOMPILED_RUNTIME)

  static const Code& UnoptimizedStaticCallEntry(intptr_t num_args_tested);

  static const char* NameAt(intptr_t index) { return entries_[index].name; }

  static const Code& EntryAt(intptr_t index) { return *(entries_[index].code); }
  static void EntryAtPut(intptr_t index, Code* entry) {
    ASSERT(entry->IsReadOnlyHandle());
    ASSERT(entries_[index].code == nullptr);
    entries_[index].code = entry;
  }
  static intptr_t NumEntries() { return kNumStubEntries; }

#if !defined(DART_PRECOMPILED_RUNTIME)
#define GENERATE_STUB(name)                                                    \
  static CodePtr BuildIsolateSpecific##name##Stub(                             \
      compiler::ObjectPoolBuilder* opw) {                                      \
    return StubCode::Generate(                                                 \
        "_iso_stub_" #name, opw,                                               \
        compiler::StubCodeCompiler::Generate##name##Stub);                     \
  }
  VM_STUB_CODE_LIST(GENERATE_STUB);
#undef GENERATE_STUB
#endif  // !defined(DART_PRECOMPILED_RUNTIME)

 private:
  friend class MegamorphicCacheTable;

  static CodePtr GetBuildMethodExtractorStub(compiler::ObjectPoolBuilder* pool,
                                             bool generic);

  enum {
#define STUB_CODE_ENTRY(name) k##name##Index,
    VM_STUB_CODE_LIST(STUB_CODE_ENTRY)
#undef STUB_CODE_ENTRY
        kNumStubEntries
  };

  struct StubCodeEntry {
    Code* code;
    const char* name;
#if !defined(DART_PRECOMPILED_RUNTIME)
    void (*generator)(compiler::Assembler* assembler);
#endif
  };
  static StubCodeEntry entries_[kNumStubEntries];
  static AcqRelAtomic<bool> initialized_;
};

}  // namespace dart

#endif  // RUNTIME_VM_STUB_CODE_H_
