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 | |
57 | namespace tvm { |
58 | namespace codegen { |
59 | |
60 | std::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 | |