blob: b67aac480654c89b965680824ac77f1dd6e5b0b6 [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.
*/
/*!
* \file codegen_blob.cc
*/
#ifdef TVM_LLVM_VERSION
#include "codegen_blob.h"
#include <llvm/ADT/SmallVector.h>
#include <llvm/ADT/StringRef.h>
#include <llvm/ADT/Triple.h>
#include <llvm/ADT/Twine.h>
#include <llvm/IR/BasicBlock.h>
#include <llvm/IR/Constants.h>
#include <llvm/IR/DerivedTypes.h>
#include <llvm/IR/Function.h>
#include <llvm/IR/GlobalVariable.h>
#include <llvm/IR/IRBuilder.h>
#include <llvm/IR/LLVMContext.h>
#include <llvm/IR/Metadata.h>
#include <llvm/IR/Module.h>
#include <llvm/IR/Type.h>
#include <llvm/IR/Value.h>
#if TVM_LLVM_VERSION >= 100
#include <llvm/Support/Alignment.h>
#endif
#include <llvm/Target/TargetMachine.h>
#include <llvm/Transforms/Utils/ModuleUtils.h>
#include <tvm/runtime/module.h>
#include <tvm/target/target.h>
#include <cstring>
#include <memory>
#include <string>
#include <utility>
#include "llvm_instance.h"
namespace tvm {
namespace codegen {
std::unique_ptr<llvm::Module> CodeGenBlob(const std::string& data, bool system_lib,
LLVMTarget* llvm_target) {
llvm::TargetMachine* tm = llvm_target->GetOrCreateTargetMachine();
const llvm::Triple& triple = tm->getTargetTriple();
llvm::LLVMContext* ctx = llvm_target->GetContext();
std::string module_name = "devc";
auto module = std::make_unique<llvm::Module>(module_name, *ctx);
module->setTargetTriple(triple.str());
llvm_target->SetTargetMetadata(module.get());
module->setDataLayout(tm->createDataLayout());
auto* blob_value = llvm::ConstantDataArray::getString(*ctx, data, false);
auto* tvm_dev_mblob = new llvm::GlobalVariable(
*module, blob_value->getType(), true, llvm::GlobalValue::ExternalLinkage, blob_value,
runtime::symbol::tvm_dev_mblob, nullptr, llvm::GlobalVariable::NotThreadLocal, 0);
// If large const data (>2GB) is saved to default .rodata section
// then linking it to shared library will fail - relocation truncated to fit: R_X86_64_PC32.
// The issue exists on Linux x86_64 platform.
// GCC handles this situation by using -mcmodel=medium parameter but LLVM ignores it.
// The workaround is to explicitly put large const data to .lrodata section.
// Lets put const data which is larger than 1GB to .lrodata section
const size_t large_data_threshold = 1 << 30;
if (data.size() > large_data_threshold && triple.getArch() == llvm::Triple::x86_64 &&
triple.isOSBinFormatELF()) {
tvm_dev_mblob->setSection(".lrodata");
}
#if TVM_LLVM_VERSION >= 100
tvm_dev_mblob->setAlignment(llvm::Align(1));
#else
tvm_dev_mblob->setAlignment(1);
#endif
if (triple.isOSWindows()) {
tvm_dev_mblob->setDLLStorageClass(llvm::GlobalVariable::DLLExportStorageClass);
}
if (system_lib) {
// LLVM type helper
auto void_ty = llvm::Type::getVoidTy(*ctx);
auto int32_ty = llvm::Type::getInt32Ty(*ctx);
auto int8_ty = llvm::Type::getInt8Ty(*ctx);
auto int8_ptr_ty = int8_ty->getPointerTo(0);
llvm::Constant* constant_zero = llvm::Constant::getNullValue(int32_ty);
auto* tvm_dev_mblob_reg = new llvm::GlobalVariable(
*module, int32_ty, false, llvm::GlobalValue::InternalLinkage, constant_zero,
std::string(runtime::symbol::tvm_dev_mblob) + "_reg_");
auto tvm_dev_mblob_reg_alignment = module->getDataLayout().getABITypeAlignment(int32_ty);
#if TVM_LLVM_VERSION >= 100
tvm_dev_mblob_reg->setAlignment(llvm::Align(tvm_dev_mblob_reg_alignment));
#else
tvm_dev_mblob_reg->setAlignment(tvm_dev_mblob_reg_alignment);
#endif
auto* tvm_dev_mblob_string_ty =
llvm::ArrayType::get(int8_ty, std::strlen(runtime::symbol::tvm_dev_mblob) + 1);
auto* tvm_dev_mblob_string_value =
llvm::ConstantDataArray::getString(*ctx, runtime::symbol::tvm_dev_mblob, true);
auto* tvm_dev_mblob_string = new llvm::GlobalVariable(
*module, tvm_dev_mblob_string_ty, true, llvm::GlobalValue::PrivateLinkage,
tvm_dev_mblob_string_value, std::string(runtime::symbol::tvm_dev_mblob) + ".str");
#if TVM_LLVM_VERSION >= 100
tvm_dev_mblob_string->setAlignment(llvm::Align(1));
#else
tvm_dev_mblob_string->setAlignment(1);
#endif
// Global init function
llvm::Function* init_fn = llvm::Function::Create(
llvm::FunctionType::get(void_ty, false), llvm::GlobalValue::InternalLinkage,
llvm::Twine("_GLOBAL__sub_I_", module_name), module.get());
// Create variable initialization function.
llvm::Function* var_init_fn = llvm::Function::Create(
llvm::FunctionType::get(void_ty, false), llvm::GlobalValue::InternalLinkage,
llvm::Twine("__cxx_global_var_init"), module.get());
// Create TVMBackendRegisterSystemLibSymbol function
llvm::Function* tvm_backend_fn =
llvm::Function::Create(llvm::FunctionType::get(int32_ty, {int8_ptr_ty, int8_ptr_ty}, false),
llvm::GlobalValue::ExternalLinkage,
llvm::Twine("TVMBackendRegisterSystemLibSymbol"), module.get());
// Set necessary fn sections
auto get_static_init_section_specifier = [&triple]() -> std::string {
if (triple.isOSLinux()) {
return ".text.startup";
} else if (triple.isOSDarwin()) {
return "__TEXT,__StaticInit,regular,pure_instructions";
} else {
return "";
}
};
auto static_init_section_specifier = get_static_init_section_specifier();
if (!static_init_section_specifier.empty()) {
init_fn->setSection(static_init_section_specifier);
var_init_fn->setSection(static_init_section_specifier);
}
// The priority is 65535 for all platforms as clang do.
llvm::appendToGlobalCtors(*module, init_fn, 65535);
// Define init_fn body
llvm::IRBuilder<> ir_builder(*ctx);
llvm::BasicBlock* init_fn_bb = llvm::BasicBlock::Create(*ctx, "entry", init_fn);
ir_builder.SetInsertPoint(init_fn_bb);
ir_builder.CreateCall(var_init_fn);
ir_builder.CreateRetVoid();
// Define var_init_fn body
llvm::BasicBlock* var_init_fn_bb = llvm::BasicBlock::Create(*ctx, "entry", var_init_fn);
ir_builder.SetInsertPoint(var_init_fn_bb);
llvm::Constant* indices[] = {constant_zero, constant_zero};
llvm::SmallVector<llvm::Value*, 2> args;
args.push_back(llvm::ConstantExpr::getGetElementPtr(tvm_dev_mblob_string_ty,
tvm_dev_mblob_string, indices));
args.push_back(
llvm::ConstantExpr::getGetElementPtr(blob_value->getType(), tvm_dev_mblob, indices));
auto* tvm_backend_fn_ret_value = ir_builder.CreateCall(tvm_backend_fn, args);
ir_builder.CreateStore(tvm_backend_fn_ret_value, tvm_dev_mblob_reg);
ir_builder.CreateRetVoid();
}
return module;
}
} // namespace codegen
} // namespace tvm
#endif // TVM_LLVM_VERSION