* 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
export_system_symbols_.emplace_back(
std::make_pair(f->name, builder_->CreatePointerCast(function_, t_void_p_)));
}
+ AddDebugInformation(function_);
+}
+
+ // Following Glow |DebugInfo::generateFunctionDebugInfo|, https://git.io/fjadv
+void CodeGenCPU::AddDebugInformation(llvm::Function* function) {
+#if TVM_LLVM_VERSION >= 50
+ CHECK(!function->getSubprogram());
+ llvm::SmallVector<llvm::Metadata*, 4> paramTys;
+ llvm::DIType* returnTy =
+ getDebugType(builder_.get(), dbg_info_->di_builder_.get(), function->getReturnType());
+ paramTys.push_back(returnTy);
+ for (size_t i = 0; i < function->arg_size(); ++i) {
+ paramTys.push_back(getDebugType(builder_.get(), dbg_info_->di_builder_.get(),
+ function->getFunctionType()->getParamType(i)));
+ }
+ auto* DIFunctionTy = dbg_info_->di_builder_->createSubroutineType(
+ dbg_info_->di_builder_->getOrCreateTypeArray(paramTys));
+ auto* DIFunction = dbg_info_->di_builder_->createFunction(
+ dbg_info_->file_, function->getName(), "", dbg_info_->file_, 0 /* line number */,
+ DIFunctionTy, false /* internal linkage */, true /* definition */, 0 /* line number */,
+ llvm::DINode::FlagPrototyped, true /* isOptimized */);
+
+ CHECK(DIFunction);
+ function->setSubprogram(DIFunction);
+ CHECK_EQ(function->getSubprogram(), DIFunction);
+
+ IRBuilder builder(&function->getEntryBlock());
+ if (!function->getEntryBlock().empty()) {
+ builder.SetInsertPoint(&function->getEntryBlock().front());
+ }
+ llvm::DebugLoc DL;
+ builder.SetCurrentDebugLocation(DL);
+ for (size_t i = 0; i < function->arg_size(); ++i) {
+ auto* paramAlloca = builder.CreateAlloca(function->getFunctionType()->getParamType(i));
+ std::string paramName = "arg" + std::to_string(i + 1);
+ auto param = dbg_info_->di_builder_->createParameterVariable(
+ DIFunction, paramName, i + 1, dbg_info_->file_, 0,
+ getDebugType(builder_.get(), dbg_info_->di_builder_.get(),
+ function->getFunctionType()->getParamType(i)),
+ /* alwaysPreserve */ true);
+ auto* store = builder.CreateStore(function->arg_begin() + i, paramAlloca);
+ dbg_info_->di_builder_->insertDeclare(paramAlloca, param,
+ dbg_info_->di_builder_->createExpression(),
+ llvm::DebugLoc::get(0, 0, DIFunction), store);
+ }
+ dbg_info_->di_builder_->finalizeSubprogram(function->getSubprogram());
+ auto* scope = function->getSubprogram();
+ if (!scope) {
+ return;
+ }
+ for (auto& BB : *function) {
+ for (auto& I : BB) {
+ if (I.getDebugLoc()) {
+ continue;
+ }
+ I.setDebugLoc(llvm::DebugLoc::get(0, 0, scope));
+ }
+ }
+#endif
+}
+
+llvm::DIType* CodeGenCPU::getDebugType(IRBuilder* builder, llvm::DIBuilder* di_builder,
+ llvm::Type* ty) {
+ if (ty == builder->getVoidTy()) {
+ return nullptr;
+ } else if (ty == builder->getFloatTy()) {
+ return di_builder->createBasicType("float", 32, llvm::dwarf::DW_ATE_float);
+ } else if (ty == builder->getInt8Ty()) {
+ return di_builder->createBasicType("int8", 8, llvm::dwarf::DW_ATE_signed);
+ } else if (ty == builder->getInt32Ty()) {
+ return di_builder->createBasicType("int32", 32, llvm::dwarf::DW_ATE_signed);
+ } else if (ty->isPointerTy()) {
+ return di_builder->createPointerType(
+ getDebugType(builder, di_builder, ty->getPointerElementType()),
+ ty->getPrimitiveSizeInBits());
+ } else {
+ std::string type_str;
+ llvm::raw_string_ostream rso(type_str);
+ ty->print(rso);
+ LOG(FATAL) << "Unknown LLVM type:" << rso.str();
+ }
+ return nullptr;
}
void CodeGenCPU::AddMainFunction(const std::string& entry_func_name) {
* 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
std::unordered_map<std::string, llvm::GlobalVariable*> func_handle_map_;
// List of symbols to be exported to TVM system lib.
std::vector<std::pair<std::string, llvm::Value*> > export_system_symbols_;
+
+ // Get the DWARF type corresponding to the LLVM type |ty|. The current API in practice only
+ // generates |int32|, and |int8*|.
+ static llvm::DIType* getDebugType(IRBuilder* builder, llvm::DIBuilder* di_builder,
+ llvm::Type* ty);
+ // Adds the DWARF debug information for |function| to |dbg_info_|.
+ void AddDebugInformation(llvm::Function* function);
};
} // namespace codegen
md_tbaa_root_ = md_builder_->createTBAARoot("tvm-tbaa");
md_tbaa_alias_set_ = md_builder_->createTBAANode("tvm-alias", md_tbaa_root_);
this->InitTarget(tm);
+ dbg_info_ = CreateDebugInfo(module_.get());
}
void CodeGenLLVM::InitTarget(llvm::TargetMachine* tm) {
analyzer_.reset(new arith::Analyzer());
}
+
void CodeGenLLVM::AddFunctionInternal(const LoweredFunc& f, bool ret_void) {
this->InitFuncState();
std::vector<llvm::Type*> arg_types;
}
}
+
std::unique_ptr<llvm::Module> CodeGenLLVM::Finish() {
this->AddStartupFunction();
// link modules
+ dbg_info_->di_builder_->finalize();
for (size_t i = 0; i < link_modules_.size(); ++i) {
CHECK(!llvm::Linker::linkModules(*module_, std::move(link_modules_[i])))
<< "Failed to link modules";
*p_alignment = align_bits / 8;
}
+std::unique_ptr<CodeGenLLVM::DebugInfo> CodeGenLLVM::CreateDebugInfo(llvm::Module* module) {
+ auto debug_info = llvm::make_unique<CodeGenLLVM::DebugInfo>();
+ debug_info->di_builder_ = llvm::make_unique<llvm::DIBuilder>(*module);
+ // TODO(tulloch): pass this information through relay::Span classes to the LoweredFunc instance?
+ debug_info->file_ = debug_info->di_builder_->createFile("model.tvm", "/tmp/");
+ debug_info->compilation_unit_ = debug_info->di_builder_->createCompileUnit(
+ llvm::dwarf::DW_LANG_C, debug_info->file_, "TVM", 0, "", 0, "",
+ llvm::DICompileUnit::DebugEmissionKind::FullDebug,
+ /* SplitDebugInlining */ true,
+ /* DebugInfoForProfiling */ true);
+ return debug_info;
+}
+
llvm::Value* CodeGenLLVM::CreateBroadcast(llvm::Value* value, int lanes) {
llvm::Constant* undef = llvm::UndefValue::get(
llvm::VectorType::get(value->getType(), lanes));
std::unordered_set<const Variable*> alias_var_set_;
// set of volatile buffer.
std::unordered_set<const Variable*> volatile_buf_;
+
+ struct DebugInfo {
+ std::unique_ptr<llvm::DIBuilder> di_builder_;
+ llvm::DICompileUnit* compilation_unit_{nullptr};
+ llvm::DIFile* file_{nullptr};
+ };
+ std::unique_ptr<DebugInfo> dbg_info_;
+
+ // Create a new DebugInfo struct from the given Module that initializes the |file_| and
+ // |compilation_unit_| to TVM defaults.
+ static std::unique_ptr<DebugInfo> CreateDebugInfo(llvm::Module* module);
};
} // namespace codegen
} // namespace tvm
* 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
#include <llvm/IR/BasicBlock.h>
#include <llvm/IR/Constants.h>
#include <llvm/IR/DerivedTypes.h>
+#include <llvm/IR/DIBuilder.h>
#include <llvm/IR/Function.h>
#include <llvm/IR/IRBuilder.h>
#include <llvm/IR/Instructions.h>
}
cg->AddMainFunction(funcs[0]->name);
module_ = cg->Finish();
+
+ module_->addModuleFlag(llvm::Module::Warning, "tvm_target", llvm::MDString::get(*ctx_, target));
+ module_->addModuleFlag(llvm::Module::Override, "Debug Info Version",
+ llvm::DEBUG_METADATA_VERSION);
+
+ if (tm_->getTargetTriple().isOSDarwin()) {
+ module_->addModuleFlag(llvm::Module::Override, "Dwarf Version", 2);
+ }
+
std::string verify_errors_storage;
llvm::raw_string_ostream verify_errors(verify_errors_storage);
LOG_IF(FATAL, llvm::verifyModule(*module_, &verify_errors))
<< "LLVM module verification failed with the following errors: \n"
<< verify_errors.str();
- module_->addModuleFlag(
- llvm::Module::Warning, "tvm_target",
- llvm::MDString::get(*ctx_, target));
target_ = target;
mptr_ = module_.get();
}
check_llvm_sigmoid(8)
check_llvm_sigmoid(16)
+
+def test_dwarf_debug_information():
+ nn = 1024
+ n = tvm.convert(nn)
+ A = tvm.placeholder((n,), name='A')
+ B = tvm.placeholder((n,), name='B')
+ C = tvm.compute(A.shape, lambda *i: A(*i) + B(*i), name='C')
+ s = tvm.create_schedule(C.op)
+ xo, xi = s[C].split(C.op.axis[0], factor=4)
+ s[C].parallel(xo)
+ s[C].vectorize(xi)
+ def check_llvm_object():
+ if not tvm.module.enabled("llvm"):
+ return
+ if tvm.codegen.llvm_version_major() < 5:
+ return
+ # build two functions
+ f2 = tvm.lower(s, [A, B, C], name="fadd1")
+ f1 = tvm.lower(s, [A, B, C], name="fadd2")
+ m = tvm.build([f1, f2], "llvm")
+ temp = util.tempdir()
+ o_path = temp.relpath("temp.o")
+ m.save(o_path)
+ import re
+ import shutil
+ import subprocess
+ import sys
+
+ # Try the dwarfdump utility (OS X)
+ if shutil.which("dwarfdump"):
+ output = subprocess.check_output(["dwarfdump", o_path])
+ assert re.search(r"""DW_AT_name\\t\("fadd1"\)""", str(output))
+ assert re.search(r"""DW_AT_name\\t\("fadd2"\)""", str(output))
+
+ # Try gobjdump (OS X)
+ if shutil.which("gobjdump"):
+ output = subprocess.check_output(["gobjdump", "--dwarf", o_path])
+ assert re.search(r"""DW_AT_name.*fadd1""", str(output))
+ assert re.search(r"""DW_AT_name.*fadd2""", str(output))
+
+ # Try objdump (Linux) - Darwin objdump has different DWARF syntax.
+ if shutil.which("objdump") and sys.platform != 'darwin':
+ output = subprocess.check_output(["objdump", "--dwarf", o_path])
+ assert re.search(r"""DW_AT_name.*fadd1""", str(output))
+ assert re.search(r"""DW_AT_name.*fadd2""", str(output))
+
+ def check_llvm_ir():
+ if not tvm.module.enabled("llvm"):
+ return
+ if tvm.codegen.llvm_version_major() < 5:
+ return
+ # build two functions
+ f2 = tvm.lower(s, [A, B, C], name="fadd1")
+ f1 = tvm.lower(s, [A, B, C], name="fadd2")
+ m = tvm.build([f1, f2], target="llvm -target=aarch64-linux-gnu")
+ ll = m.get_source("ll")
+
+ # On non-Darwin OS, don't explicitly specify DWARF version.
+ import re
+ assert not re.search(r""""Dwarf Version""""", ll)
+ assert re.search(r"""llvm.dbg.value""", ll)
+
+ # Try Darwin, require DWARF-2
+ m = tvm.build([f1, f2],
+ target="llvm -target=x86_64-apple-darwin-macho")
+ ll = m.get_source("ll")
+ assert re.search(r"""i32 4, !"Dwarf Version", i32 2""", ll)
+ assert re.search(r"""llvm.dbg.value""", ll)
+
+ check_llvm_object()
+ check_llvm_ir()
+
if __name__ == "__main__":
test_llvm_import()
test_alignment()
test_llvm_lookup_intrin()
test_llvm_div()
test_llvm_fp_math()
+ test_dwarf_debug_information()