blob: 61922a6342de3bb0cc2989c2de42a2dc7260efd0 [file] [log] [blame]
/*
* 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.
*/
#if defined(TVM_LLVM_VERSION) && TVM_LLVM_VERSION >= 70
#include <llvm/ADT/ArrayRef.h>
#include <llvm/ADT/SmallString.h>
#include <llvm/ADT/StringRef.h>
#include <llvm/Bitcode/BitcodeWriter.h>
#include <llvm/IR/Constants.h>
#include <llvm/IR/DerivedTypes.h>
#include <llvm/IR/Function.h>
#include <llvm/IR/GlobalVariable.h>
#include <llvm/IR/Instructions.h>
#include <llvm/IR/Intrinsics.h>
#include <tvm/ffi/reflection/registry.h>
#if TVM_LLVM_VERSION >= 100
#include <llvm/IR/IntrinsicsHexagon.h>
#endif
#include <llvm/IR/LLVMContext.h>
#include <llvm/IR/LegacyPassManager.h>
#include <llvm/IR/MDBuilder.h>
#include <llvm/IR/Module.h>
#if TVM_LLVM_VERSION >= 100
#include <llvm/Support/Alignment.h>
#endif
#include <llvm/Support/CodeGen.h>
#include <llvm/Support/CommandLine.h>
#include <llvm/Support/FileSystem.h>
#include <llvm/Support/raw_ostream.h>
#include <llvm/Target/TargetMachine.h>
#include <llvm/Transforms/Utils/Cloning.h>
#include <tvm/runtime/module.h>
#include <tvm/target/codegen.h>
#include <tvm/tir/analysis.h>
#include <cstdio>
#include <cstdlib>
#include <map>
#include <sstream>
#include <string>
#include <unordered_map>
#include <utility>
#include <vector>
#include "../../runtime/hexagon/hexagon_module.h"
#include "../build_common.h"
#include "codegen_cpu.h"
#include "llvm_instance.h"
namespace tvm {
namespace codegen {
// Hexagon code generation
class CodeGenHexagon final : public CodeGenCPU {
public:
void Init(const std::string& module_name, LLVMTarget* llvm_target,
ffi::Optional<ffi::String> system_lib_prefix, bool dynamic_lookup,
bool target_c_runtime) override;
void InitTarget() final;
using CodeGenCPU::VisitStmt_;
llvm::Value* VisitExpr_(const BufferLoadNode* op) override;
llvm::Value* CreateIntrinsic(const CallNode* op) override;
llvm::Value* CreateCallExtern(Type ret_type, ffi::String global_symbol,
const ffi::Array<PrimExpr>& args, bool skip_first_arg) override;
llvm::Value* CreateCallExternQHL(Type ret_type, ffi::String global_symbol,
const ffi::Array<PrimExpr>& args, bool skip_first_arg);
llvm::Module* GetModulePtr() const { return module_.get(); }
uint64_t GetTypeSizeInBits(llvm::Type* type) const {
#if TVM_LLVM_VERSION >= 160
return data_layout_->getTypeSizeInBits(type).getFixedValue();
#elif TVM_LLVM_VERSION >= 100
return data_layout_->getTypeSizeInBits(type).getFixedSize();
#else
return data_layout_->getTypeSizeInBits(type);
#endif
}
protected:
void CreatePrintf(const std::string& format, llvm::ArrayRef<llvm::Value*> format_args) final;
private:
TypedPointer CreateBufferPtr(llvm::Value* buffer_ptr, DataType buffer_element_dtype,
llvm::ArrayRef<llvm::Value*> indices, DataType value_dtype) final;
bool IsQHLFunction(const std::string& func);
llvm::Value* VectorLookupLoad(Buffer buffer, DataType buffer_type, ffi::Array<PrimExpr> indices);
llvm::Value* Intrinsic(llvm::Intrinsic::ID, llvm::ArrayRef<llvm::Value*> args);
std::vector<std::string> fqhl_list_ = {
"tvm_vect_qhmath_hvx_cos_ahf", "tvm_vect_qhmath_hvx_tanh_ahf",
"tvm_vect_qhmath_hvx_sigmoid_ahf", "tvm_vect_qhmath_hvx_sin_ahf",
"tvm_vect_qhmath_hvx_sqrt_ahf", "tvm_vect_qhmath_hvx_exp_ahf",
"tvm_vect_qhmath_hvx_tan_ahf", "tvm_vect_qhmath_hvx_floor_ahf",
"tvm_vect_qhmath_hvx_ceil_ahf", "tvm_vect_qhmath_hvx_pow_ahf"};
};
void CodeGenHexagon::Init(const std::string& module_name, LLVMTarget* llvm_target,
ffi::Optional<ffi::String> system_lib_prefix, bool dynamic_lookup,
bool target_c_runtime) {
CodeGenCPU::Init(module_name, llvm_target, system_lib_prefix, dynamic_lookup, target_c_runtime);
}
void CodeGenHexagon::InitTarget() {
native_vector_bits_ = 64; // Assume "scalar" vectors at first.
const auto hvx_length_feature = "+hvx-length"; // +hvx-length{64|128}b
for (const std::string& f : llvm_target_->GetTargetFeatures()) {
llvm::StringRef fs(f);
#if TVM_LLVM_VERSION >= 180
if (!fs.starts_with(hvx_length_feature)) continue;
TVM_FFI_ICHECK(fs.ends_with("b")) << "malformed target feature: " << f;
#else
if (!fs.startswith(hvx_length_feature)) continue;
TVM_FFI_ICHECK(fs.endswith("b")) << "malformed target feature: " << f;
#endif
int hvx_bytes = 0;
size_t len_begin = std::strlen(hvx_length_feature);
TVM_FFI_ICHECK(!fs.substr(len_begin, fs.size() - len_begin - 1).getAsInteger(10, hvx_bytes))
<< "invalid HVX length in feature string: " << f;
TVM_FFI_ICHECK(hvx_bytes == 64 || hvx_bytes == 128)
<< "invalid HVX vector length: " << hvx_bytes << ", should be 64 or 128";
native_vector_bits_ = hvx_bytes * 8;
// There should only be one hvx-length...
break;
}
CodeGenCPU::InitTarget();
}
llvm::Value* CodeGenHexagon::CreateCallExternQHL(Type ret_type, ffi::String global_symbol,
const ffi::Array<PrimExpr>& args,
bool skip_first_arg) {
int num_lanes = args[1].dtype().lanes();
int vector_length = native_vector_bits_ / args[1].dtype().bits();
num_lanes = ((num_lanes + vector_length - 1) / vector_length) * vector_length;
std::vector<llvm::Value*> vect_split;
for (int i = 0; i < num_lanes / vector_length; ++i) {
std::vector<llvm::Value*> sub_vect_val;
std::vector<llvm::Type*> arg_types;
for (size_t k = skip_first_arg; k < args.size(); ++k)
sub_vect_val.push_back(
CodeGenCPU::CreateVecSlice(MakeValue(args[k]), i * vector_length, vector_length));
for (llvm::Value* v : sub_vect_val) {
arg_types.push_back(v->getType());
}
llvm::FunctionType* ftype = llvm::FunctionType::get(arg_types[0], arg_types, false);
llvm::Function* f = module_->getFunction(MakeStringRef(global_symbol));
if (f == nullptr) {
f = llvm::Function::Create(ftype, llvm::Function::ExternalLinkage,
MakeStringRef(global_symbol), module_.get());
}
#if TVM_LLVM_VERSION >= 90
auto ext_callee = llvm::FunctionCallee(f);
#else
auto ext_callee = f;
#endif
vect_split.push_back(builder_->CreateCall(ext_callee, sub_vect_val));
}
return CodeGenCPU::CreateVecConcat(vect_split);
}
bool CodeGenHexagon::IsQHLFunction(const std::string& func) {
return std::find(fqhl_list_.begin(), fqhl_list_.end(), func) != fqhl_list_.end();
}
llvm::Value* CodeGenHexagon::CreateCallExtern(Type ret_type, ffi::String global_symbol,
const ffi::Array<PrimExpr>& args,
bool skip_first_arg) {
int num_lanes = args[1].dtype().lanes();
int vector_length = native_vector_bits_ / args[1].dtype().bits();
if (IsQHLFunction(global_symbol) && (num_lanes > vector_length))
return CreateCallExternQHL(ret_type, global_symbol, args, skip_first_arg);
return CodeGenCPU::CreateCallExtern(ret_type, global_symbol, args, skip_first_arg);
}
llvm::Value* CodeGenHexagon::VisitExpr_(const BufferLoadNode* op) {
if (!op->buffer.same_as(op->buffer->data)) {
// Check if we can generate a vector lookup.
if (!op->indices[0].as<RampNode>()) {
if (auto* vlut = VectorLookupLoad(op->buffer, op->dtype, op->indices)) {
return vlut;
}
}
}
return CodeGenCPU::VisitExpr_(op);
}
llvm::Value* CodeGenHexagon::CreateIntrinsic(const CallNode* op) {
#if TVM_LLVM_VERSION >= 150
if (op->op.same_as(builtin::start_profile_intrinsic()) ||
op->op.same_as(builtin::end_profile_intrinsic())) {
llvm::Value* id = MakeValue(op->args[0]);
auto instrprof_id = llvm::Intrinsic::hexagon_instrprof_custom;
#if TVM_LLVM_VERSION >= 200
llvm::Function* func = llvm::cast<llvm::Function>(
llvm::Intrinsic::getOrInsertDeclaration(module_.get(), instrprof_id, {}));
#else
llvm::Function* func = llvm::Intrinsic::getDeclaration(module_.get(), instrprof_id);
#endif
llvm::GlobalVariable* name_var = module_->getGlobalVariable("handler_name");
if (!name_var) {
llvm::StringRef init_str = "lwp_handler";
llvm::Constant* init = llvm::ConstantDataArray::getString(module_->getContext(), init_str);
name_var = new llvm::GlobalVariable(*module_, init->getType(), true,
llvm::GlobalValue::InternalLinkage, init, "handler_name");
}
llvm::Type* t_int8_p_ = llvmGetPointerTo(t_int8_, 0);
return builder_->CreateCall(func, {llvm::ConstantExpr::getBitCast(name_var, t_int8_p_), id});
}
#endif
return CodeGenCPU::CreateIntrinsic(op);
}
void CodeGenHexagon::CreatePrintf(const std::string& format,
llvm::ArrayRef<llvm::Value*> format_args) {
// This function generates LLVM instructions to call HAP_debug_v2,
// as if the FARF macro in `HAP_farf.h` were called as
// FARF(ALWAYS, format, format_args[0], format_args[1], ...)
std::string func_name = "HAP_debug_v2";
llvm::Function* func = module_->getFunction(func_name);
if (func == nullptr) {
llvm::FunctionType* ftype = llvm::FunctionType::get(
t_void_, {t_int32_, llvmGetPointerTo(t_char_, 0), t_int32_, llvmGetPointerTo(t_char_, 0)},
true);
func = llvm::Function::Create(ftype, llvm::Function::ExternalLinkage, func_name, module_.get());
}
// There is no such filename/line number for this print statement
#if TVM_LLVM_VERSION >= 200
llvm::Value* filename = builder_->CreateGlobalString("generated-LLVM-code", "dummy_filename");
llvm::Value* format_str = builder_->CreateGlobalString(format, "printf_format_str");
#else
llvm::Value* filename = builder_->CreateGlobalStringPtr("generated-LLVM-code", "dummy_filename");
llvm::Value* format_str = builder_->CreateGlobalStringPtr(format, "printf_format_str");
#endif
// The value of FARF_ALWAYS_LEVEL, defined as HAP_LEVEL_HIGH
llvm::Value* level = ConstInt32(2);
llvm::Value* line_number = ConstInt32(1);
std::vector<llvm::Value*> func_args = {level, filename, line_number, format_str};
func_args.insert(func_args.end(), format_args.begin(), format_args.end());
builder_->CreateCall(func, func_args);
}
CodeGenLLVM::TypedPointer CodeGenHexagon::CreateBufferPtr(llvm::Value* buffer_ptr,
DataType buffer_element_dtype,
llvm::ArrayRef<llvm::Value*> indices,
DataType value_dtype) {
// Flat indices get delegated to the LLVM codegen.
if (indices.size() == 1) {
return CodeGenCPU::CreateBufferPtr(buffer_ptr, buffer_element_dtype, indices, value_dtype);
}
TVM_FFI_ICHECK_EQ(indices.size(), 2)
<< "CodegenHexagon supports 1-d and 2-d physical buffers, received " << indices.size()
<< "-d buffer indices";
// Use the first index to identify the pointer.
DataType dtype_void_ptr = DataType::Handle();
CodeGenLLVM::TypedPointer buffer_chunk_ptr_ptr =
CodeGenCPU::CreateBufferPtr(buffer_ptr, dtype_void_ptr, {indices[0]}, dtype_void_ptr);
llvm::Value* buffer_chunk_ptr =
builder_->CreateLoad(buffer_chunk_ptr_ptr.type, buffer_chunk_ptr_ptr.addr);
// Then delegate the CodeGenLLVM to find the value from the second
// index.
return CodeGenCPU::CreateBufferPtr(buffer_chunk_ptr, buffer_element_dtype, {indices[1]},
value_dtype);
}
llvm::Value* CodeGenHexagon::Intrinsic(llvm::Intrinsic::ID IntID,
llvm::ArrayRef<llvm::Value*> args) {
#if TVM_LLVM_VERSION >= 200
llvm::Function* intf =
llvm::cast<llvm::Function>(llvm::Intrinsic::getOrInsertDeclaration(module_.get(), IntID, {}));
#else
llvm::Function* intf = llvm::Intrinsic::getDeclaration(module_.get(), IntID);
#endif
#if TVM_LLVM_VERSION >= 90
auto intf_callee = llvm::FunctionCallee(intf);
#else
auto intf_callee = intf;
#endif
std::vector<llvm::Value*> conv_args;
llvm::FunctionType* intf_type = intf->getFunctionType();
TVM_FFI_ICHECK(args.size() == intf_type->getNumParams());
for (int i = 0, e = args.size(); i != e; ++i) {
llvm::Value* arg = args[i];
auto* need_type = llvm::dyn_cast<llvm::VectorType>(intf_type->getParamType(i));
auto* have_type = llvm::dyn_cast<llvm::VectorType>(arg->getType());
if (need_type != nullptr && have_type != nullptr && need_type != have_type) {
int need_width = GetTypeSizeInBits(need_type);
int have_width = GetTypeSizeInBits(have_type);
if (need_width == have_width) {
if (need_width == native_vector_bits_ || need_width == 2 * native_vector_bits_) {
arg = builder_->CreateBitCast(arg, need_type);
}
} // TODO(joshherr-quic): add handling of v128i1 <-> v1024i1
}
conv_args.push_back(arg);
}
return builder_->CreateCall(intf_callee, conv_args);
}
llvm::Value* CodeGenHexagon::VectorLookupLoad(Buffer buffer, DataType buffer_type,
ffi::Array<PrimExpr> indices) {
PrimExpr index = indices[0];
if (!index.dtype().is_fixed_length_vector()) {
return nullptr;
}
if (buffer_type.bits() != 8) return nullptr;
int table_elem_count = arith::Analyzer().Simplify(buffer->shape[0]).as<IntImmNode>()->value;
if (table_elem_count <= 0 || table_elem_count > 256) return nullptr;
auto int32 = DataType::Int(32);
auto native_vector_bytes = native_vector_bits_ / 8;
// Indexes
llvm::Value* trunc = MakeValue(Cast(index.dtype().with_bits(8), index));
llvm::Value* index_pad = CreateVecPad(trunc, native_vector_bytes);
// Values
std::vector<llvm::Value*> vloads;
DataType table_type = buffer_type.with_lanes(table_elem_count);
auto table_all =
MakeValue(BufferLoad(buffer, {
Ramp(IntImm(int32, 0), IntImm(int32, 1), table_elem_count),
}));
// The number of value vectors should be a power of 2.
int table_vec_count = llvm::PowerOf2Ceil(GetVectorBytes(table_type) / native_vector_bytes);
int table_vec_length = native_vector_bytes / buffer_type.bytes();
for (int i = 0; i != table_vec_count; ++i) {
// CreateVecSlice will generate undefs for elements outside the source vector.
vloads.push_back(CreateVecSlice(table_all, i * table_vec_length, table_vec_length));
}
#define VLO(x) Intrinsic(llvm::Intrinsic::hexagon_V6_lo_128B, {x})
#define VHI(x) Intrinsic(llvm::Intrinsic::hexagon_V6_hi_128B, {x})
#define VXOR(x, y) Intrinsic(llvm::Intrinsic::hexagon_V6_vxor_128B, {x, y})
#define VSHUFF(x) Intrinsic(llvm::Intrinsic::hexagon_V6_vshuffb_128B, {x})
#define VSPLATB(x) Intrinsic(llvm::Intrinsic::hexagon_V6_lvsplatb_128B, {x})
#define VLUT32(x, y, z) Intrinsic(llvm::Intrinsic::hexagon_V6_vlutvvbi_128B, {x, y, z})
#define VLUT32_OR(v, x, y, z) \
Intrinsic(llvm::Intrinsic::hexagon_V6_vlutvvb_oracci_128B, {v, x, y, z})
// Shuffle table bytes:
// 127, 63, 126, 62,........68, 4, 67, 3, 66, 2, 65, 1, 64, 0
std::vector<llvm::Value*> table;
for (int i = 0; i != table_vec_count; ++i) table.push_back(VSHUFF(vloads[i]));
// Get each 32 byte sub-table's output
std::vector<llvm::Value*> results;
int table_iters = table_elem_count / 32;
for (int i = 0; i < table_iters; ++i)
results.push_back(VLUT32(index_pad, table[i / 4], ConstInt32(i % 8)));
// Combine outputs
llvm::Value* result = results[0];
for (int i = 1; i < table_iters; ++i) result = VXOR(result, results[i]);
llvm::Type* res_type = result->getType();
llvm::Type* ret_type = DTypeToLLVMType(buffer_type);
if (res_type == ret_type) {
return result;
}
int res_bits = GetTypeSizeInBits(res_type);
int ret_bits = GetTypeSizeInBits(ret_type);
TVM_FFI_ICHECK_GE(res_bits, ret_bits);
if (ret_bits < res_bits) {
#if TVM_LLVM_VERSION >= 110
llvm::Type* res_byte_type = llvm::VectorType::get(t_int8_, res_bits / 8, /*Scalable*/ false);
#else
llvm::Type* res_byte_type = llvm::VectorType::get(t_int8_, res_bits / 8);
#endif
result = CreateVecSlice(builder_->CreateBitCast(result, res_byte_type), 0, ret_bits / 8);
}
if (result->getType() != ret_type) {
return builder_->CreateBitCast(result, ret_type);
}
return result;
#undef VLUT32_OR
#undef VLUT32
#undef VSPLATB
#undef VSHUFF
#undef VXOR
#undef VHI
#undef VLO
}
namespace {
TVM_ATTRIBUTE_UNUSED std::ostream& operator<<(std::ostream& os, const llvm::Module& m) {
std::string ms;
llvm::raw_string_ostream sos(ms);
sos << m;
os << sos.str();
return os;
}
void ProcessLLVMOptions(const std::vector<std::string>& llvm_vec) {
if (llvm_vec.empty()) return;
// LLVM options.
std::vector<const char*> starts;
std::transform(llvm_vec.begin(), llvm_vec.end(), std::back_inserter(starts),
std::mem_fn(&std::string::c_str));
const char** args = &starts.front();
llvm::cl::ParseCommandLineOptions(llvm_vec.size(), args);
}
} // namespace
ffi::Module BuildHexagon(IRModule mod, Target target) {
LLVMInstance llvm_instance;
With<LLVMTarget> llvm_target(llvm_instance, target);
auto split = [](const std::string& str, char delim = ' ') {
std::vector<std::string> vec;
std::string tmp;
for (std::istringstream iss(str); std::getline(iss, tmp, delim);) {
vec.push_back(tmp);
}
return vec;
};
std::string llvm_options_str = "llvm";
if (const auto& llvm_options = target->GetAttr<ffi::Array<ffi::String>>("llvm-options")) {
for (const ffi::String& s : llvm_options.value()) llvm_options_str += "," + s;
}
// Postprocess the LLVM options string: replace '@' with '=', and ',' with ' '.
for (int i = 0, e = llvm_options_str.size(); i != e; ++i) {
switch (llvm_options_str[i]) {
case '@':
llvm_options_str[i] = '=';
break;
case ',':
llvm_options_str[i] = ' ';
break;
}
}
// The vector of LLVM options is treated at "argv" from "main(argc, argv)". The entry at
// position 0 is the name of the executable, and is ignored by the LLVM cl::option parser.
// Make sure it's set to "llvm" (tvm.target.hexagon does that).
std::vector<std::string> llvm_options_vec = split(llvm_options_str);
assert(llvm_options_vec.size() >= 1 && llvm_options_vec[0] == "llvm");
llvm_options_vec.insert(std::next(llvm_options_vec.begin()),
{"-hexagon-small-data-threshold=0",
"-force-target-max-vector-interleave=1", "-hexagon-autohvx=1"});
// Process extra command line options for LLVM. Make sure it's only
// done once.
static bool CallOnce = (ProcessLLVMOptions(llvm_options_vec), true);
(void)CallOnce;
auto cg = std::make_unique<CodeGenHexagon>();
std::string entry_func;
for (auto kv : mod->functions) {
if (!kv.second->IsInstance<PrimFuncNode>()) {
// (@jroesch): we relax constraints here, relax functions will just be ignored.
DLOG(INFO) << "Can only lower IR Module with PrimFuncs, but got " << kv.second->GetTypeKey();
continue;
}
auto f = Downcast<PrimFunc>(kv.second);
if (f->HasNonzeroAttr(tir::attr::kIsEntryFunc)) {
auto global_symbol = f->GetAttr<ffi::String>(tvm::attr::kGlobalSymbol);
TVM_FFI_ICHECK(global_symbol.has_value());
entry_func = global_symbol.value();
}
}
cg->Init("TVMHexagonModule", llvm_target.get(), std::nullopt, false, false);
cg->AddFunctionsOrdered(mod->functions.begin(), mod->functions.end());
if (entry_func.length() != 0) {
cg->AddMainFunction(entry_func);
}
// Uncomment to get the LLVM module right out of codegen, before optimizations.
// std::cerr << "HexagonModule.0 {\n" << *cg->GetModulePtr() << "}\n";
std::unique_ptr<llvm::Module> module = cg->Finish();
enum CodeGenFileType { Asm, Obj, IR, BC };
auto EmitToString = [&llvm_target](const llvm::Module& m, CodeGenFileType cgft) {
std::string out;
if (cgft == IR || cgft == BC) {
llvm::raw_string_ostream os(out);
if (cgft == IR)
m.print(os, nullptr);
else
llvm::WriteBitcodeToFile(m, os);
} else if (cgft == Asm || cgft == Obj) {
#if TVM_LLVM_VERSION <= 90
auto ft = cgft == Asm ? llvm::TargetMachine::CodeGenFileType::CGFT_AssemblyFile
: llvm::TargetMachine::CodeGenFileType::CGFT_ObjectFile;
#elif TVM_LLVM_VERSION <= 170
auto ft = cgft == Asm ? llvm::CGFT_AssemblyFile : llvm::CGFT_ObjectFile;
#else
auto ft =
cgft == Asm ? llvm::CodeGenFileType::AssemblyFile : llvm::CodeGenFileType::ObjectFile;
#endif
llvm::SmallString<16384> ss; // Will grow on demand.
llvm::raw_svector_ostream os(ss);
std::unique_ptr<llvm::Module> cm = llvm::CloneModule(m);
llvm::legacy::PassManager pass;
llvm::TargetMachine* tm = llvm_target->GetOrCreateTargetMachine();
TVM_FFI_ICHECK(tm->addPassesToEmitFile(pass, os, nullptr, ft) == 0)
<< "Cannot emit target code";
pass.run(*cm.get());
out.assign(ss.c_str(), ss.size());
}
return out;
};
auto SaveToFile = [](const std::string& data, const std::string& suffix) {
llvm::SmallString<64> file_name;
int fd;
std::error_code ec = llvm::sys::fs::createTemporaryFile("tvm", suffix, fd, file_name);
TVM_FFI_ICHECK_EQ(static_cast<bool>(ec), false) << ec.message();
llvm::raw_fd_ostream file(fd, true);
file << data;
TVM_FFI_ICHECK(!file.has_error()) << file.error().message();
// If there is an error, execution will never get here, but return
// {ec, name} anyway to allow caller to handle error conditions.
// This way the "ICHECK" above can be removed with minimal effort.
return std::make_pair(file.error(), std::string(file_name.c_str()));
};
std::string asm_str = EmitToString(*module.get(), Asm);
std::string obj_str = EmitToString(*module.get(), Obj);
std::string ir_str = EmitToString(*module.get(), IR);
std::string bc_str = EmitToString(*module.get(), BC);
std::string o_name = SaveToFile(obj_str, "o").second;
std::string so_name(o_name, 0, o_name.size() - 1);
so_name += "so";
const auto f = tvm::ffi::Function::GetGlobal("tvm.contrib.hexagon.link_shared");
TVM_FFI_ICHECK(f.has_value()) << "tvm.contrib.hexagon.link_shared does not to exist, "
"do import tvm.contrib.hexagon";
ffi::Array<PrimExpr> o_names = {StringImm(o_name)};
ffi::Map<ffi::String, ffi::String> extra_args;
if (target->attrs.count("mcpu")) {
std::string mcpu = Downcast<ffi::String>(target->attrs.at("mcpu"));
#if TVM_LLVM_VERSION >= 180
TVM_FFI_ICHECK(llvm::StringRef(mcpu).starts_with("hexagon"))
#else
TVM_FFI_ICHECK(llvm::StringRef(mcpu).startswith("hexagon"))
#endif
<< "unexpected -mcpu value in target:" << mcpu;
extra_args.Set("hex_arch", llvm::StringRef(mcpu).drop_front(strlen("hexagon")).str());
}
int rc = (*f)(so_name, o_names, extra_args).cast<int>();
TVM_FFI_ICHECK(rc == 0) << "Failed to link " << so_name;
return HexagonModuleCreate(so_name, "so", ExtractFuncInfo(mod), asm_str, obj_str, ir_str, bc_str);
}
TVM_FFI_STATIC_INIT_BLOCK() {
namespace refl = tvm::ffi::reflection;
refl::GlobalDef()
.def("target.build.hexagon", BuildHexagon)
.def_packed("tvm.codegen.llvm.target_hexagon",
[](const ffi::PackedArgs& targs, ffi::Any* rv) {
*rv = static_cast<void*>(new CodeGenHexagon());
});
}
} // namespace codegen
} // namespace tvm
#endif // TVM_LLVM_VERSION