blob: 0cb141de99bd3afc37a33e77dcf0d5f6b0c9dc34 [file]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.
*/
/*! \file llvm_instance.h
*/
#ifndef TVM_TARGET_LLVM_LLVM_INSTANCE_H_
#define TVM_TARGET_LLVM_LLVM_INSTANCE_H_
#ifdef TVM_LLVM_VERSION
#include <llvm/ADT/ArrayRef.h>
#include <llvm/IR/FMF.h>
#include <llvm/Support/CodeGen.h>
#include <llvm/Target/TargetOptions.h>
#include <tvm/ffi/container/array.h>
#include <tvm/ffi/optional.h>
#include <tvm/ffi/string.h>
#include <tvm/ir/expr.h>
#include <tvm/target/target.h>
#include <algorithm>
#include <memory>
#include <string>
#include <utility>
#include <vector>
// LLVM compatibility macro
#if TVM_LLVM_VERSION >= 200
#define llvmGetPointerTo(arg, offset) (llvm::PointerType::get((arg), (offset)))
#else
#define llvmGetPointerTo(arg, offset) (arg->getPointerTo(offset))
#endif
#define llvmGetIntrinName(id) \
std::string(llvm::Intrinsic::getBaseName(static_cast<llvm::Intrinsic::ID>(id)))
namespace llvm {
class LLVMContext;
class MemoryBuffer;
class Module;
class TargetMachine;
} // namespace llvm
namespace tvm {
namespace codegen {
class LLVMTarget;
/*!
* \class LLVMInstance
* \brief LLVMInstance is a class that (conceptually) starts and stops LLVM.
* All uses of LLVM should take place within a lifetime of an object
* of this class.
*
* E.g.
* ```{.cpp}
* {
* LLVMInstance llvm_instance;
* ...
* someFunctionFromLLVM(...);
* ...
* }
* // no more calls to LLVM here
* ```
* In addition to that, LLVMInstance provides an LLVM context (llvm::LLVMContext).
* The context is a structure in LLVM where common IR constructs are maintained,
* (such as types, constants, etc.) so that they can be identified by their
* address (i.e. pointer comparison). Because of that, it's important to use
* the same context throughout compilation.
*
* At the moment the "starting" of LLVM performs initialization of LLVM, but
* "stopping" doesn't do anything. In the future, if such a need arises, this
* functionality may be extended to perform dlopen/dlclose of the LLVM-based
* code in TVM.
*
* This class provides means to deserialize an LLVM module, either from text
* (in a string), or from a file. In either case, the serialized module can
* be LLVM IR assembly, or binary bitcode enconding.
*/
class LLVMInstance {
public:
/*!
* \brief Constructs LLVMInstance
*/
LLVMInstance();
/*!
* \brief Destroys LLVMInstance object
*/
~LLVMInstance(); // Must not be "= default" here in the header file.
/*!
* \brief Get the LLVM context for this scope.
*/
std::shared_ptr<llvm::LLVMContext> GetContext() const { return ctx_; }
/*!
* \brief Create `llvm::Module` from a string.
*
* Parse the string in \param llvm_ir, and return the `llvm::Module`.
* At the moment this function will abort if the parsing fails.
* \param llvm_ir string with the LLVM IR assembly or bitcode
* \return created `llvm::Module`
*/
std::unique_ptr<llvm::Module> ParseIR(const std::string& llvm_ir) const;
/*!
* \brief Load `llvm::Module` from a given file
*
* Read the file \param file_name, and return the `llvm::Module`.
* At the moment this function will abort if reading of the file or creation
* of the module fails.
* \param file_name file with the LLVM IR assembly or bitcode
* \return created `llvm::Module`
*/
std::unique_ptr<llvm::Module> LoadIR(const std::string& file_name) const;
private:
std::unique_ptr<llvm::Module> ParseBuffer(const llvm::MemoryBuffer& buffer) const;
std::shared_ptr<llvm::LLVMContext> ctx_;
};
/*!
* \class LLVMTargetInfo
* \brief Summary of information for this TVM target relevant to LLVM code
* generation.
*
* This class contains all information that LLVM needs for code generation for
* a particular target. The purpose of this class is only to provide information
* in an easily-accessible form (for example for querying the target properties).
*
* Note that objects of this class must be created within the lifetime of an
* LLVMInstance object.
*/
class LLVMTargetInfo {
public:
/*!
* \brief Constructs LLVMTargetInfo from `Target`
* \param scope LLVMInstance object
* \param target TVM Target object for target "llvm"
*/
LLVMTargetInfo(LLVMInstance& scope, const Target& target); // NOLINT(runtime/references)
/*!
* \brief Constructs LLVMTargetInfo from target string
* \param scope LLVMInstance object
* \param target TVM target string for target "llvm"
*/
// NOLINTNEXTLINE(runtime/references)
LLVMTargetInfo(LLVMInstance& scope, const std::string& target_str);
/*!
* \brief Constructs LLVMTargetInfo from `Target`
* \param scope LLVMInstance object
* \param target TVM JSON Target object for target "llvm"
*/
// NOLINTNEXTLINE(runtime/references)
LLVMTargetInfo(LLVMInstance& instance, const ffi::Map<ffi::String, ffi::Any>& target);
/*!
* \brief Destroys LLVMTargetInfo object
*/
~LLVMTargetInfo();
/*!
* \brief Returns string representation (as TVM target) of the LLVMTargetInfo
* \return Target string
*
* Note: If the LLVMTargetInfo object was created from a string `s`, the string
* returned here may not be exactly equal to `s`. For example, if the CPU
* was "default", the returned string will have CPU set to the detected host
* CPU.
*/
std::string str() const;
/*!
* \brief Return LLVM's `TargetMachine`, or nullptr
* \param allow_missing do not abort if the target machine cannot be created,
* return nullptr instead
* \return Pointer to the `TargetMachine` object (or nullptr if it cannot be
* created, \see allow_missing)
*/
llvm::TargetMachine* GetOrCreateTargetMachine(bool allow_missing = false);
/*!
* \brief Get the target triple
* \return the target triple
*/
const std::string& GetTargetTriple() const { return triple_; }
/*!
* \brief Get the CPU name
* \return the CPU name: the detected host CPU if the original TVM target
* specified it as "default"
*/
const std::string& GetCPU() const { return cpu_; }
/*!
* \brief Get the list of LLVM target features
* \return array of individual feature strings
*/
llvm::ArrayRef<std::string> GetTargetFeatures() const { return attrs_; }
/*!
* \brief Get the LLVM target feature string
* \return comma-separated list of LLVM target features
*/
std::string GetTargetFeatureString() const;
/*!
* \brief Get the LLVM target options
* \return `llvm::TargetOptions` object for this target
*/
const llvm::TargetOptions& GetTargetOptions() const { return target_options_; }
/*!
* \brief Get the LLVM target reloc model
* \return `llvm::Reloc::Model` object for this target
*/
const llvm::Reloc::Model& GetTargetRelocModel() const { return reloc_model_; }
/*!
* \brief Get the LLVM target code model
* \return `llvm::CodeModel::Model` object for this target
*/
const llvm::CodeModel::Model& GetTargetCodeModel() const { return code_model_; }
/*!
* \brief Get fast math flags
* \return `llvm::FastMathFlags` for this target
*/
llvm::FastMathFlags GetFastMathFlags() const { return fast_math_flags_; }
/*!
* \brief Get the LLVM JIT engine type
* \return the type name of the JIT engine (default "orcjit" or "mcjit")
*/
const std::string GetJITEngine() const { return jit_engine_; }
/*!
* \brief Get the TVM & LLVM vector_width
* \return number of bits for vector width
*/
const int GetVectorWidth();
/*!
* \brief Get the LLVM optimization level
* \return optimization level for this target
*/
#if TVM_LLVM_VERSION <= 170
llvm::CodeGenOpt::Level GetOptLevel() const { return opt_level_; }
#else
llvm::CodeGenOptLevel GetOptLevel() const { return opt_level_; }
#endif
/*!
* \class Option
* \brief Internal representation of command-line option
*/
struct Option {
enum class OptType {
Invalid = 0, //!< placeholder, indicates parsing error
Bool, //!< enum value corresponding to type string "bool"
Int, //!< enum value corresponding to type string "int"
UInt, //!< enum value corresponding to type string "uint"
String, //!< enum value corresponding to type string "string"
};
std::string name; //!< option name
OptType type; //!< type of the option value
struct {
union {
bool b; //!< bool option value
int i; //!< int option value
unsigned u = 0; //!< unsigned option value
};
std::string s; //!< string option value
} value; //!< option value specified in the option string
};
/*!
* \brief Get LLVM command line options
* \return the list of LLVM command line options specified for this target
*/
const std::vector<Option>& GetCommandLineOptions() const { return llvm_options_; }
/*!
* \brief Parse a string from the `cl-opt` target attribute
* \param str the option string
* \return parsed `Option` object, if parsing failed the type member will be
* set to `Option::OptType::Invalid`
*/
static Option ParseOptionString(const std::string& str);
/*!
* \brief Checks if the settings in this object that describe global state
* match the current global state
* \return true or false correspondingly
* \note The global state can be modified by command line options. This
* function checks if the specified options differ from their current
* values.
*/
bool MatchesGlobalState() const;
/*!
* \brief Get all supported targets from the LLVM backend
* \return list with all valid targets
*/
const ffi::Array<ffi::String> GetAllLLVMTargets() const;
/*!
* \brief Get all CPU arches from target
* \return list with all valid cpu architectures
* \note The arches are fetched from the LLVM backend using the target `-mtriple`.
*/
const ffi::Array<ffi::String> GetAllLLVMTargetArches() const;
/*!
* \brief Check if a CPU name is valid for this target triple
* \param cpu The CPU name to validate
* \return true if the CPU is recognized (including aliases)
*/
bool IsValidCPU(const std::string& cpu) const;
/*!
* \brief Get all CPU features from target
* \return Map with all valid cpu features as keys and empty string as value. The Map
* is intended to be used as a Set, which TVM does not currently support.
* \note The features are fetched from the LLVM backend using the target `-mtriple`
* and the `-mcpu` architecture, but also consider the `-mattr` attributes.
*/
const ffi::Map<ffi::String, ffi::String> GetAllLLVMCpuFeatures() const;
/*!
* \brief Check the target if has a specific cpu feature
* \param feature string with the feature to check
* \return true or false
* \note The feature is checked in the LLVM backend for the target `-mtriple`
* and `-mcpu` architecture, but also consider the `-mattr` attributes.
*/
const bool TargetHasCPUFeature(const std::string& feature) const;
protected:
/*!
* \brief Get the current value of given LLVM option
* \param opt Option with "type" and "name" set
* Fills in the "value" field in the provided Option argument, or sets the
* "type" to Invalid if the option value cannot be obtained.
*/
void GetOptionValue(Option* opt) const;
private:
std::string triple_;
std::string cpu_;
std::vector<std::string> attrs_;
std::vector<Option> llvm_options_;
llvm::TargetOptions target_options_;
llvm::FastMathFlags fast_math_flags_;
#if TVM_LLVM_VERSION <= 170
llvm::CodeGenOpt::Level opt_level_;
#else
llvm::CodeGenOptLevel opt_level_;
#endif
llvm::Reloc::Model reloc_model_ = llvm::Reloc::PIC_;
llvm::CodeModel::Model code_model_ = llvm::CodeModel::Small;
std::shared_ptr<llvm::TargetMachine> target_machine_;
std::string jit_engine_ = "orcjit";
int vector_width_{0};
};
/*!
* \class LLVMTarget
* \brief Information used by LLVM for code generation for particular target
*
* In addition to all information that LLVM needs for code generation for
* a particular target, objects of this class handle saving and restoring
* global LLVM state that may be affected by these flags. This way, code
* generation for each LLVM-based target in TVM will start with the same LLVM
* global state.
*
* Note that objects of this class must be created within the lifetime of an
* LLVMInstance object.
*/
class LLVMTarget : public LLVMTargetInfo {
public:
/*!
* \brief Constructs LLVMTarget from `Target`
* \param scope LLVMInstance object
* \param target_info Target info object for target "llvm"
*/
LLVMTarget(LLVMInstance& scope, const LLVMTargetInfo& target_info); // NOLINT(runtime/references)
/*!
* \brief Constructs LLVMTarget from `Target`
* \param scope LLVMInstance object
* \param target TVM Target object for target "llvm"
*/
LLVMTarget(LLVMInstance& scope, const Target& target); // NOLINT(runtime/references)
/*!
* \brief Constructs LLVMTarget from target string
* \param scope LLVMInstance object
* \param target TVM target string for target "llvm"
*/
LLVMTarget(LLVMInstance& scope, const std::string& target_str); // NOLINT(runtime/references)
/*!
* \brief Destroys LLVMTarget object
*/
~LLVMTarget();
/*!
* \brief Get the LLVMInstance object from which the LLVMTarget object was
* created
* \return The enclosing LLVMInstance object
*/
const LLVMInstance& GetInstance() const { return instance_; }
/*!
* \brief Get the current LLVM context
* \return the current LLVM context
*/
llvm::LLVMContext* GetContext() const;
/*!
* \brief Extract the target string from given `llvm::Module`
* \param module LLVM module with the TVM target string embedded as metadata
* \return the target string from module's metadata
*/
static std::string GetTargetMetadata(const llvm::Module& module);
/*!
* \brief Embed target string as metadata in given `llvm::Module`
* \param module the module to insert the target string into
*/
void SetTargetMetadata(llvm::Module* module) const;
// Stubs to enable use with `With`.
void EnterWithScope() {}
void ExitWithScope() {}
private:
std::vector<Option> saved_llvm_options_;
/*!
* \brief Apply or revert command-line LLVM options
* \param apply_otherwise_revert if true, apply the options (saving previous
* values, if false, then restore the saved values
* \param dry_run if true, do not make any changes (or save anything)
* \return true is changes were made (or would have been made in a dry run),
* false otherwise
*/
bool ApplyLLVMOptions(bool apply_otherwise_revert, bool dry_run = false);
const LLVMInstance& instance_;
std::weak_ptr<llvm::LLVMContext> ctx_;
/*!
* \brief Global singleton flag indicating whether LLVM's global state has
* been modified or not (via command-line flags). There can only be
* a single such modification in effect at any given time.
*/
static bool modified_llvm_state_;
};
} // namespace codegen
} // namespace tvm
#endif // TVM_LLVM_VERSION
#endif // TVM_TARGET_LLVM_LLVM_INSTANCE_H_