Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion python/tvm/target/codegen.py
Original file line number Diff line number Diff line change
Expand Up @@ -183,7 +183,8 @@ def llvm_get_cpu_features(target=None):
List of available CPU features.
"""
assert isinstance(target, Target) or target is None
return _ffi_api.llvm_get_cpu_features(target)
feature_map = _ffi_api.llvm_get_cpu_features(target)
return set(feature_map.keys())


def llvm_cpu_has_features(cpu_features, target=None):
Expand Down
95 changes: 43 additions & 52 deletions src/target/llvm/llvm_instance.cc
Original file line number Diff line number Diff line change
Expand Up @@ -199,32 +199,37 @@ std::ostream& operator<<(std::ostream& os, const LLVMTargetInfo::Option& opt) {
return os;
}

LLVMTargetInfo::LLVMTargetInfo(LLVMInstance& instance, const Target& target) {
triple_ = target->GetAttr<String>("mtriple").value_or("default");
LLVMTargetInfo::LLVMTargetInfo(LLVMInstance& instance, const Target& target)
: LLVMTargetInfo(instance, target->Export()) {}

LLVMTargetInfo::LLVMTargetInfo(LLVMInstance& instance, const TargetJSON& target) {
triple_ = Downcast<String>(target.Get("mtriple").value_or(String("default")));
if (triple_.empty() || triple_ == "default") {
triple_ = llvm::sys::getDefaultTargetTriple();
}
cpu_ = target->GetAttr<String>("mcpu").value_or(defaults::cpu);
cpu_ = Downcast<String>(target.Get("mcpu").value_or(String(defaults::cpu)));

if (const Optional<Array<String>>& v = target->GetAttr<Array<String>>("mattr")) {
if (const auto& v = Downcast<Optional<Array<String>>>(target.Get("mattr"))) {
for (const String& s : v.value()) {
attrs_.push_back(s);
}
}
// llvm module target
if (target->kind->name == "llvm") {
if (Downcast<String>(target.Get("kind")) == "llvm") {
// legalize -mcpu with the target -mtriple
auto arches = GetAllLLVMTargetArches();
bool has_arch =
std::any_of(arches.begin(), arches.end(), [&](const auto& var) { return var == cpu_; });
if (!has_arch) {
LOG(FATAL) << "LLVM cpu architecture `-mcpu=" << cpu_
<< "` is not valid in `-mtriple=" << triple_ << "`";
// Flag an error, but don't abort. This mimicks the behaviour of 'llc' to
// give the code a chance to run with a less-specific target.
LOG(ERROR) << "LLVM cpu architecture `-mcpu=" << cpu_
<< "` is not valid in `-mtriple=" << triple_ << "`"
<< ", using default `-mcpu=" << String(defaults::cpu) << "`";
}
}

if (const Optional<Array<String>>& v = target->GetAttr<Array<String>>("cl-opt")) {
if (const auto& v = Downcast<Optional<Array<String>>>(target.Get("cl-opt"))) {
llvm::StringMap<llvm::cl::Option*>& options = llvm::cl::getRegisteredOptions();
bool parse_error = false;
for (const String& s : v.value()) {
Expand All @@ -245,7 +250,7 @@ LLVMTargetInfo::LLVMTargetInfo(LLVMInstance& instance, const Target& target) {
}

llvm::FloatABI::ABIType float_abi = llvm::FloatABI::Default;
if (const Optional<String>& v = target->GetAttr<String>("mfloat-abi")) {
if (const auto& v = Downcast<Optional<String>>(target.Get("mfloat-abi"))) {
String value = v.value();
if (value == "hard") {
float_abi = llvm::FloatABI::Hard;
Expand All @@ -257,7 +262,7 @@ LLVMTargetInfo::LLVMTargetInfo(LLVMInstance& instance, const Target& target) {
}

// LLVM JIT engine options
if (const Optional<String>& v = target->GetAttr<String>("jit")) {
if (const auto& v = Downcast<Optional<String>>(target.Get("jit"))) {
String value = v.value();
if ((value == "mcjit") || (value == "orcjit")) {
jit_engine_ = value;
Expand All @@ -283,14 +288,14 @@ LLVMTargetInfo::LLVMTargetInfo(LLVMInstance& instance, const Target& target) {
target_options_.NoInfsFPMath = false;
target_options_.NoNaNsFPMath = true;
target_options_.FloatABIType = float_abi;
if (const Optional<String>& v = target->GetAttr<String>("mabi")) {
target_options_.MCOptions.ABIName = v.value();
if (target.find("mabi") != target.end()) {
target_options_.MCOptions.ABIName = Downcast<String>(target.Get("mabi"));
}

auto maybe_level = target->GetAttr<Integer>("opt-level");
auto maybe_level = Downcast<Integer>(target.Get("opt-level"));
#if TVM_LLVM_VERSION <= 170
if (maybe_level.defined()) {
int level = maybe_level.value()->value;
int level = maybe_level->value;
if (level <= 0) {
opt_level_ = llvm::CodeGenOpt::None;
} else if (level == 1) {
Expand Down Expand Up @@ -327,7 +332,7 @@ LLVMTargetInfo::LLVMTargetInfo(LLVMInstance& instance, const Target& target) {
// Fast math options

auto GetBoolFlag = [&target](llvm::StringRef flag) -> bool {
return target->GetAttr<Bool>(flag.str()).value_or(Bool(false));
return Downcast<Bool>(target.Get(flag.str()).value_or(Bool(false)));
};
if (GetBoolFlag("fast-math")) {
#if TVM_LLVM_VERSION >= 60
Expand Down Expand Up @@ -381,52 +386,31 @@ static const llvm::Target* CreateLLVMTargetInstance(const std::string triple,
return llvm_instance;
}

static llvm::TargetMachine* CreateLLVMTargetMachine(
static std::unique_ptr<llvm::TargetMachine> CreateLLVMTargetMachine(
const llvm::Target* llvm_instance, const std::string& triple, const std::string& cpu,
const std::string& features, const llvm::TargetOptions& target_options,
const llvm::Reloc::Model& reloc_model, const llvm::CodeModel::Model& code_model,
const std::string& features, const llvm::TargetOptions& target_options = {},
const llvm::Reloc::Model& reloc_model = llvm::Reloc::Static,
const llvm::CodeModel::Model& code_model = llvm::CodeModel::Small,
#if TVM_LLVM_VERSION <= 170
const llvm::CodeGenOpt::Level& opt_level) {
const llvm::CodeGenOpt::Level& opt_level = llvm::CodeGenOpt::Level(0)) {
#else
const llvm::CodeGenOptLevel& opt_level) {
const llvm::CodeGenOptLevel& opt_level = llvm::CodeGenOptLevel(0)) {
#endif
llvm::TargetMachine* tm = llvm_instance->createTargetMachine(
triple, cpu, features, target_options, reloc_model, code_model, opt_level);
ICHECK(tm != nullptr);

return tm;
}

static const llvm::MCSubtargetInfo* GetLLVMSubtargetInfo(const std::string& triple,
const std::string& cpu_name,
const std::string& feats) {
// create a LLVM instance
auto llvm_instance = CreateLLVMTargetInstance(triple, true);
// create a target machine
// required minimum: llvm::InitializeAllTargetMCs()
llvm::TargetOptions target_options;
auto tm = CreateLLVMTargetMachine(llvm_instance, triple, cpu_name, feats, target_options,
llvm::Reloc::Static, llvm::CodeModel::Small,
#if TVM_LLVM_VERSION <= 170
llvm::CodeGenOpt::Level(0));
#else
llvm::CodeGenOptLevel(0));
#endif
// create subtarget info module
const llvm::MCSubtargetInfo* MCInfo = tm->getMCSubtargetInfo();

return MCInfo;
return std::unique_ptr<llvm::TargetMachine>(tm);
}

llvm::TargetMachine* LLVMTargetInfo::GetOrCreateTargetMachine(bool allow_missing) {
if (target_machine_) return target_machine_.get();

std::string error;
if (const llvm::Target* llvm_instance = CreateLLVMTargetInstance(triple_, allow_missing)) {
llvm::TargetMachine* tm =
target_machine_ =
CreateLLVMTargetMachine(llvm_instance, triple_, cpu_, GetTargetFeatureString(),
target_options_, reloc_model_, code_model_, opt_level_);
target_machine_ = std::unique_ptr<llvm::TargetMachine>(tm);
}
ICHECK(target_machine_ != nullptr);
return target_machine_.get();
Expand Down Expand Up @@ -832,7 +816,11 @@ const Array<String> LLVMTargetInfo::GetAllLLVMTargets() const {
const Array<String> LLVMTargetInfo::GetAllLLVMTargetArches() const {
Array<String> cpu_arches;
// get the subtarget info module
const auto MCInfo = GetLLVMSubtargetInfo(triple_, "", "");
auto llvm_instance = CreateLLVMTargetInstance(triple_, true);
std::unique_ptr<llvm::TargetMachine> target_machine =
CreateLLVMTargetMachine(llvm_instance, triple_, "", "");
const auto MCInfo = target_machine->getMCSubtargetInfo();

if (!MCInfo) {
return cpu_arches;
}
Expand All @@ -850,24 +838,29 @@ const Array<String> LLVMTargetInfo::GetAllLLVMTargetArches() const {
return cpu_arches;
}

const Array<String> LLVMTargetInfo::GetAllLLVMCpuFeatures() const {
const Map<String, String> LLVMTargetInfo::GetAllLLVMCpuFeatures() const {
std::string feats = "";
for (const auto& attr : attrs_) {
feats += feats.empty() ? attr : ("," + attr);
}
// get the subtarget info module
const auto MCInfo = GetLLVMSubtargetInfo(triple_, cpu_.c_str(), feats);
auto llvm_instance = CreateLLVMTargetInstance(triple_, true);
std::unique_ptr<llvm::TargetMachine> target_machine =
CreateLLVMTargetMachine(llvm_instance, triple_, cpu_.c_str(), feats);
const auto MCInfo = target_machine->getMCSubtargetInfo();

// get all features for CPU
llvm::ArrayRef<llvm::SubtargetFeatureKV> llvm_features =
#if TVM_LLVM_VERSION < 180
llvm::featViewer(*(const llvm::MCSubtargetInfo*)MCInfo);
#else
MCInfo->getAllProcessorFeatures();
#endif
Array<String> cpu_features;
// TVM doesn't have an FFI friendly Set, so use a Map instead for now
Map<String, String> cpu_features;
for (const auto& feat : llvm_features) {
if (MCInfo->checkFeatures("+" + std::string(feat.Key))) {
cpu_features.push_back(feat.Key);
cpu_features.Set(feat.Key, "");
}
}

Expand All @@ -877,9 +870,7 @@ const Array<String> LLVMTargetInfo::GetAllLLVMCpuFeatures() const {
const bool LLVMTargetInfo::TargetHasCPUFeature(const std::string& feature) const {
// lookup features for `-mcpu`
auto feats = GetAllLLVMCpuFeatures();
bool has_feature =
std::any_of(feats.begin(), feats.end(), [&](const auto& var) { return var == feature; });

bool has_feature = feats.find(feature) != feats.end();
return has_feature;
}

Expand Down
13 changes: 11 additions & 2 deletions src/target/llvm/llvm_instance.h
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,14 @@ class LLVMTargetInfo {
*/
// 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 TargetJSON& target);

/*!
* \brief Destroys LLVMTargetInfo object
*/
Expand Down Expand Up @@ -290,11 +298,12 @@ class LLVMTargetInfo {

/*!
* \brief Get all CPU features from target
* \return list with all valid cpu features
* \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 Array<String> GetAllLLVMCpuFeatures() const;
const Map<String, String> GetAllLLVMCpuFeatures() const;

/*!
* \brief Check the target if has a specific cpu feature
Expand Down
7 changes: 3 additions & 4 deletions src/target/llvm/llvm_module.cc
Original file line number Diff line number Diff line change
Expand Up @@ -697,12 +697,12 @@ TVM_REGISTER_GLOBAL("target.llvm_get_cpu_archlist")
});

TVM_REGISTER_GLOBAL("target.llvm_get_cpu_features")
.set_body_typed([](const Target& target) -> Array<String> {
.set_body_typed([](const Target& target) -> Map<String, String> {
auto use_target = target.defined() ? target : Target::Current(false);
// ignore non "llvm" target
if (target.defined()) {
if (target->kind->name != "llvm") {
return Array<String>{};
return {};
}
}
auto llvm_instance = std::make_unique<LLVMInstance>();
Expand All @@ -722,8 +722,7 @@ TVM_REGISTER_GLOBAL("target.llvm_cpu_has_feature")
auto llvm_instance = std::make_unique<LLVMInstance>();
LLVMTargetInfo llvm_backend(*llvm_instance, use_target);
auto cpu_features = llvm_backend.GetAllLLVMCpuFeatures();
bool has_feature = std::any_of(cpu_features.begin(), cpu_features.end(),
[&](auto& var) { return var == feature; });
bool has_feature = cpu_features.find(feature) != cpu_features.end();
return has_feature;
});

Expand Down
88 changes: 35 additions & 53 deletions src/target/parsers/aprofile.cc
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,11 @@

#include "aprofile.h"

#include <memory>
#include <string>

#include "../../support/utils.h"
#include "../llvm/llvm_instance.h"

namespace tvm {
namespace target {
Expand All @@ -52,33 +54,6 @@ double GetArchVersion(Optional<Array<String>> attr) {
return GetArchVersion(attr.value());
}

static inline bool HasFlag(String attr, std::string flag) {
std::string attr_str = attr;
return attr_str.find(flag) != std::string::npos;
}

static inline bool HasFlag(Optional<String> attr, std::string flag) {
if (!attr) {
return false;
}
return HasFlag(attr.value(), flag);
}

static inline bool HasFlag(Optional<Array<String>> attr, std::string flag) {
if (!attr) {
return false;
}
Array<String> attr_array = attr.value();

auto matching_attr = std::find_if(attr_array.begin(), attr_array.end(),
[flag](String attr_str) { return HasFlag(attr_str, flag); });
return matching_attr != attr_array.end();
}

static bool HasFlag(Optional<String> mcpu, Optional<Array<String>> mattr, std::string flag) {
return HasFlag(mcpu, flag) || HasFlag(mattr, flag);
}

bool IsAArch32(Optional<String> mtriple, Optional<String> mcpu) {
if (mtriple) {
bool is_mprofile = mcpu && support::StartsWith(mcpu.value(), "cortex-m");
Expand All @@ -101,39 +76,46 @@ bool IsArch(TargetJSON attrs) {
return IsAArch32(mtriple, mcpu) || IsAArch64(mtriple);
}

static TargetFeatures GetFeatures(TargetJSON target) {
Optional<String> mcpu = Downcast<Optional<String>>(target.Get("mcpu"));
Optional<String> mtriple = Downcast<Optional<String>>(target.Get("mtriple"));
Optional<Array<String>> mattr = Downcast<Optional<Array<String>>>(target.Get("mattr"));
bool CheckContains(Array<String> array, String predicate) {
return std::any_of(array.begin(), array.end(), [&](String var) { return var == predicate; });
}

const double arch_version = GetArchVersion(mattr);
static TargetFeatures GetFeatures(TargetJSON target) {
#ifdef TVM_LLVM_VERSION
String kind = Downcast<String>(target.Get("kind"));
ICHECK_EQ(kind, "llvm") << "Expected target kind 'llvm', but got '" << kind << "'";

const bool is_aarch64 = IsAArch64(mtriple);
Optional<String> mtriple = Downcast<Optional<String>>(target.Get("mtriple"));
Optional<String> mcpu = Downcast<Optional<String>>(target.Get("mcpu"));

const bool simd_flag = HasFlag(mcpu, mattr, "+neon") || HasFlag(mcpu, mattr, "+simd");
const bool has_asimd = is_aarch64 || simd_flag;
const bool has_sve = HasFlag(mcpu, mattr, "+sve");
// Check that LLVM has been compiled with the correct target support
auto llvm_instance = std::make_unique<codegen::LLVMInstance>();
codegen::LLVMTargetInfo llvm_backend(*llvm_instance, {{"kind", String("llvm")}});
Array<String> targets = llvm_backend.GetAllLLVMTargets();
if ((IsAArch64(mtriple) && !CheckContains(targets, "aarch64")) ||
(IsAArch32(mtriple, mcpu) && !CheckContains(targets, "arm"))) {
LOG(WARNING) << "Cannot parse target features. LLVM was not compiled with support for "
"Arm(R)-based targets.";
return {};
}

const bool i8mm_flag = HasFlag(mcpu, mattr, "+i8mm");
const bool i8mm_disable = HasFlag(mcpu, mattr, "+noi8mm");
const bool i8mm_default = arch_version >= 8.6;
const bool i8mm_support = arch_version >= 8.2 && arch_version <= 8.5;
const bool has_i8mm = (i8mm_default && !i8mm_disable) || (i8mm_support && i8mm_flag);
codegen::LLVMTargetInfo llvm_target(*llvm_instance, target);
Map<String, String> features = llvm_target.GetAllLLVMCpuFeatures();

const bool dotprod_flag = HasFlag(mcpu, mattr, "+dotprod");
const bool dotprod_disable = HasFlag(mcpu, mattr, "+nodotprod");
const bool dotprod_default = arch_version >= 8.4;
const bool dotprod_support = arch_version >= 8.2 && arch_version <= 8.3;
const bool has_dotprod =
(dotprod_default && !dotprod_disable) || (dotprod_support && dotprod_flag);
auto has_feature = [features](const String& feature) {
return features.find(feature) != features.end();
};

const bool fp16_flag = HasFlag(mcpu, mattr, "+fullfp16");
const bool fp16_support = arch_version >= 8.2;
const bool has_fp16_simd = fp16_support && (fp16_flag || has_sve);
return {{"is_aarch64", Bool(IsAArch64(mtriple))},
{"has_asimd", Bool(has_feature("neon"))},
{"has_sve", Bool(has_feature("sve"))},
{"has_dotprod", Bool(has_feature("dotprod"))},
{"has_matmul_i8", Bool(has_feature("i8mm"))},
{"has_fp16_simd", Bool(has_feature("fullfp16"))}};
#endif

return {{"is_aarch64", Bool(is_aarch64)}, {"has_asimd", Bool(has_asimd)},
{"has_sve", Bool(has_sve)}, {"has_dotprod", Bool(has_dotprod)},
{"has_matmul_i8", Bool(has_i8mm)}, {"has_fp16_simd", Bool(has_fp16_simd)}};
LOG(WARNING) << "Cannot parse Arm(R)-based target features without LLVM support.";
return {};
}

static Array<String> MergeKeys(Optional<Array<String>> existing_keys) {
Expand Down
Loading