//===- include/Core/Instrumentation.h - Instrumentation API ---------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
///
/// \file
/// Provide an Instrumentation API that optionally uses VTune interfaces.
///
//===----------------------------------------------------------------------===//

#ifndef LLD_CORE_INSTRUMENTATION_H
#define LLD_CORE_INSTRUMENTATION_H

#include "llvm/Support/Compiler.h"
#include <utility>

#ifdef LLD_HAS_VTUNE
# include <ittnotify.h>
#endif

namespace lld {
#ifdef LLD_HAS_VTUNE
/// A unique global scope for instrumentation data.
///
/// Domains last for the lifetime of the application and cannot be destroyed.
/// Multiple Domains created with the same name represent the same domain.
class Domain {
  __itt_domain *_domain;

public:
  explicit Domain(const char *name) : _domain(__itt_domain_createA(name)) {}

  operator __itt_domain *() const { return _domain; }
  __itt_domain *operator->() const { return _domain; }
};

/// A global reference to a string constant.
///
/// These are uniqued by the ITT runtime and cannot be deleted. They are not
/// specific to a domain.
///
/// Prefer reusing a single StringHandle over passing a ntbs when the same
/// string will be used often.
class StringHandle {
  __itt_string_handle *_handle;

public:
  StringHandle(const char *name) : _handle(__itt_string_handle_createA(name)) {}

  operator __itt_string_handle *() const { return _handle; }
};

/// A task on a single thread. Nests within other tasks.
///
/// Each thread has its own task stack and tasks nest recursively on that stack.
/// A task cannot transfer threads.
///
/// SBRM is used to ensure task starts and ends are balanced. The lifetime of
/// a task is either the lifetime of this object, or until end is called.
class ScopedTask {
  __itt_domain *_domain;

  ScopedTask(const ScopedTask &) = delete;
  ScopedTask &operator=(const ScopedTask &) = delete;

public:
  /// Create a task in Domain \p d named \p s.
  ScopedTask(const Domain &d, const StringHandle &s) : _domain(d) {
    __itt_task_begin(d, __itt_null, __itt_null, s);
  }

  ScopedTask(ScopedTask &&other) {
    *this = std::move(other);
  }

  ScopedTask &operator=(ScopedTask &&other) {
    _domain = other._domain;
    other._domain = nullptr;
    return *this;
  }

  /// Prematurely end this task.
  void end() {
    if (_domain)
      __itt_task_end(_domain);
    _domain = nullptr;
  }

  ~ScopedTask() { end(); }
};

/// A specific point in time. Allows metadata to be associated.
class Marker {
public:
  Marker(const Domain &d, const StringHandle &s) {
    __itt_marker(d, __itt_null, s, __itt_scope_global);
  }
};
#else
class Domain {
public:
  Domain(const char *name) {}
};

class StringHandle {
public:
  StringHandle(const char *name) {}
};

class ScopedTask {
public:
  ScopedTask(const Domain &d, const StringHandle &s) {}
  void end() {}
};

class Marker {
public:
  Marker(const Domain &d, const StringHandle &s) {}
};
#endif

inline const Domain &getDefaultDomain() {
  static Domain domain("org.llvm.lld");
  return domain;
}
} // end namespace lld.

#endif
