1/*
2 * Licensed to the Apache Software Foundation (ASF) under one
3 * or more contributor license agreements. See the NOTICE file
4 * distributed with this work for additional information
5 * regarding copyright ownership. The ASF licenses this file
6 * to you under the Apache License, Version 2.0 (the
7 * "License"); you may not use this file except in compliance
8 * with the License. You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing,
13 * software distributed under the License is distributed on an
14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 * KIND, either express or implied. See the License for the
16 * specific language governing permissions and limitations
17 * under the License.
18 */
19
20/*!
21 * \file codegen_blob.cc
22 */
23#ifdef TVM_LLVM_VERSION
24
25#include "codegen_blob.h"
26
27#include <llvm/ADT/SmallVector.h>
28#include <llvm/ADT/StringRef.h>
29#include <llvm/ADT/Triple.h>
30#include <llvm/ADT/Twine.h>
31#include <llvm/IR/BasicBlock.h>
32#include <llvm/IR/Constants.h>
33#include <llvm/IR/DerivedTypes.h>
34#include <llvm/IR/Function.h>
35#include <llvm/IR/GlobalVariable.h>
36#include <llvm/IR/IRBuilder.h>
37#include <llvm/IR/LLVMContext.h>
38#include <llvm/IR/Metadata.h>
39#include <llvm/IR/Module.h>
40#include <llvm/IR/Type.h>
41#include <llvm/IR/Value.h>
42#if TVM_LLVM_VERSION >= 100
43#include <llvm/Support/Alignment.h>
44#endif
45#include <llvm/Target/TargetMachine.h>
46#include <llvm/Transforms/Utils/ModuleUtils.h>
47#include <tvm/runtime/module.h>
48#include <tvm/target/target.h>
49
50#include <cstring>
51#include <memory>
52#include <string>
53#include <utility>
54
55#include "llvm_instance.h"
56
57namespace tvm {
58namespace codegen {
59
60std::unique_ptr<llvm::Module> CodeGenBlob(const std::string& data, bool system_lib,
61 LLVMTarget* llvm_target) {
62 llvm::TargetMachine* tm = llvm_target->GetOrCreateTargetMachine();
63 const llvm::Triple& triple = tm->getTargetTriple();
64 llvm::LLVMContext* ctx = llvm_target->GetContext();
65 std::string module_name = "devc";
66 auto module = std::make_unique<llvm::Module>(module_name, *ctx);
67 module->setTargetTriple(triple.str());
68 llvm_target->SetTargetMetadata(module.get());
69 module->setDataLayout(tm->createDataLayout());
70 auto* blob_value = llvm::ConstantDataArray::getString(*ctx, data, false);
71 auto* tvm_dev_mblob = new llvm::GlobalVariable(
72 *module, blob_value->getType(), true, llvm::GlobalValue::ExternalLinkage, blob_value,
73 runtime::symbol::tvm_dev_mblob, nullptr, llvm::GlobalVariable::NotThreadLocal, 0);
74
75 // If large const data (>2GB) is saved to default .rodata section
76 // then linking it to shared library will fail - relocation truncated to fit: R_X86_64_PC32.
77 // The issue exists on Linux x86_64 platform.
78 // GCC handles this situation by using -mcmodel=medium parameter but LLVM ignores it.
79 // The workaround is to explicitly put large const data to .lrodata section.
80 // Lets put const data which is larger than 1GB to .lrodata section
81 const size_t large_data_threshold = 1 << 30;
82 if (data.size() > large_data_threshold && triple.getArch() == llvm::Triple::x86_64 &&
83 triple.isOSBinFormatELF()) {
84 tvm_dev_mblob->setSection(".lrodata");
85 }
86
87#if TVM_LLVM_VERSION >= 100
88 tvm_dev_mblob->setAlignment(llvm::Align(1));
89#else
90 tvm_dev_mblob->setAlignment(1);
91#endif
92
93 if (triple.isOSWindows()) {
94 tvm_dev_mblob->setDLLStorageClass(llvm::GlobalVariable::DLLExportStorageClass);
95 }
96
97 if (system_lib) {
98 // LLVM type helper
99 auto void_ty = llvm::Type::getVoidTy(*ctx);
100 auto int32_ty = llvm::Type::getInt32Ty(*ctx);
101 auto int8_ty = llvm::Type::getInt8Ty(*ctx);
102 auto int8_ptr_ty = int8_ty->getPointerTo(0);
103
104 llvm::Constant* constant_zero = llvm::Constant::getNullValue(int32_ty);
105 auto* tvm_dev_mblob_reg = new llvm::GlobalVariable(
106 *module, int32_ty, false, llvm::GlobalValue::InternalLinkage, constant_zero,
107 std::string(runtime::symbol::tvm_dev_mblob) + "_reg_");
108 auto tvm_dev_mblob_reg_alignment = module->getDataLayout().getABITypeAlignment(int32_ty);
109#if TVM_LLVM_VERSION >= 100
110 tvm_dev_mblob_reg->setAlignment(llvm::Align(tvm_dev_mblob_reg_alignment));
111#else
112 tvm_dev_mblob_reg->setAlignment(tvm_dev_mblob_reg_alignment);
113#endif
114
115 auto* tvm_dev_mblob_string_ty =
116 llvm::ArrayType::get(int8_ty, std::strlen(runtime::symbol::tvm_dev_mblob) + 1);
117 auto* tvm_dev_mblob_string_value =
118 llvm::ConstantDataArray::getString(*ctx, runtime::symbol::tvm_dev_mblob, true);
119 auto* tvm_dev_mblob_string = new llvm::GlobalVariable(
120 *module, tvm_dev_mblob_string_ty, true, llvm::GlobalValue::PrivateLinkage,
121 tvm_dev_mblob_string_value, std::string(runtime::symbol::tvm_dev_mblob) + ".str");
122#if TVM_LLVM_VERSION >= 100
123 tvm_dev_mblob_string->setAlignment(llvm::Align(1));
124#else
125 tvm_dev_mblob_string->setAlignment(1);
126#endif
127
128 // Global init function
129 llvm::Function* init_fn = llvm::Function::Create(
130 llvm::FunctionType::get(void_ty, false), llvm::GlobalValue::InternalLinkage,
131 llvm::Twine("_GLOBAL__sub_I_", module_name), module.get());
132
133 // Create variable initialization function.
134 llvm::Function* var_init_fn = llvm::Function::Create(
135 llvm::FunctionType::get(void_ty, false), llvm::GlobalValue::InternalLinkage,
136 llvm::Twine("__cxx_global_var_init"), module.get());
137
138 // Create TVMBackendRegisterSystemLibSymbol function
139 llvm::Function* tvm_backend_fn =
140 llvm::Function::Create(llvm::FunctionType::get(int32_ty, {int8_ptr_ty, int8_ptr_ty}, false),
141 llvm::GlobalValue::ExternalLinkage,
142 llvm::Twine("TVMBackendRegisterSystemLibSymbol"), module.get());
143
144 // Set necessary fn sections
145 auto get_static_init_section_specifier = [&triple]() -> std::string {
146 if (triple.isOSLinux()) {
147 return ".text.startup";
148 } else if (triple.isOSDarwin()) {
149 return "__TEXT,__StaticInit,regular,pure_instructions";
150 } else {
151 return "";
152 }
153 };
154
155 auto static_init_section_specifier = get_static_init_section_specifier();
156
157 if (!static_init_section_specifier.empty()) {
158 init_fn->setSection(static_init_section_specifier);
159 var_init_fn->setSection(static_init_section_specifier);
160 }
161
162 // The priority is 65535 for all platforms as clang do.
163 llvm::appendToGlobalCtors(*module, init_fn, 65535);
164
165 // Define init_fn body
166 llvm::IRBuilder<> ir_builder(*ctx);
167 llvm::BasicBlock* init_fn_bb = llvm::BasicBlock::Create(*ctx, "entry", init_fn);
168 ir_builder.SetInsertPoint(init_fn_bb);
169 ir_builder.CreateCall(var_init_fn);
170 ir_builder.CreateRetVoid();
171
172 // Define var_init_fn body
173 llvm::BasicBlock* var_init_fn_bb = llvm::BasicBlock::Create(*ctx, "entry", var_init_fn);
174 ir_builder.SetInsertPoint(var_init_fn_bb);
175 llvm::Constant* indices[] = {constant_zero, constant_zero};
176 llvm::SmallVector<llvm::Value*, 2> args;
177 args.push_back(llvm::ConstantExpr::getGetElementPtr(tvm_dev_mblob_string_ty,
178 tvm_dev_mblob_string, indices));
179 args.push_back(
180 llvm::ConstantExpr::getGetElementPtr(blob_value->getType(), tvm_dev_mblob, indices));
181 auto* tvm_backend_fn_ret_value = ir_builder.CreateCall(tvm_backend_fn, args);
182 ir_builder.CreateStore(tvm_backend_fn_ret_value, tvm_dev_mblob_reg);
183 ir_builder.CreateRetVoid();
184 }
185
186 return module;
187}
188
189} // namespace codegen
190} // namespace tvm
191
192#endif // TVM_LLVM_VERSION
193