Skip to content

Commit

Permalink
Emit DWARF debug information
Browse files Browse the repository at this point in the history
  • Loading branch information
ajtulloch committed Jun 24, 2019
1 parent b98e2c7 commit 5b1a915
Show file tree
Hide file tree
Showing 6 changed files with 202 additions and 5 deletions.
3 changes: 3 additions & 0 deletions python/tvm/contrib/cc.py
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,9 @@ def _linux_shared(output, objects, options, cc="g++"):
msg += py_str(out)
raise RuntimeError(msg)

if sys.platform == "darwin":
cmd = ["dsymutil", output]
subprocess.check_call(cmd)

def _windows_shared(output, objects, options):
cl_cmd = ["cl"]
Expand Down
97 changes: 97 additions & 0 deletions src/codegen/llvm/codegen_llvm.cc
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ void CodeGenLLVM::Init(const std::string& module_name,
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) {
Expand Down Expand Up @@ -109,6 +110,29 @@ void CodeGenLLVM::InitFuncState() {
analyzer_.reset(new arith::Analyzer());
}

llvm::DIType* CodeGenLLVM::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 CodeGenLLVM::AddFunctionInternal(const LoweredFunc& f, bool ret_void) {
this->InitFuncState();
std::vector<llvm::Type*> arg_types;
Expand Down Expand Up @@ -164,11 +188,71 @@ void CodeGenLLVM::AddFunctionInternal(const LoweredFunc& f, bool ret_void) {
} else {
builder_->CreateRet(ConstInt32(0));
}
AddDebugInformation(function_);
}

// Following Glow |DebugInfo::generateFunctionDebugInfo|, https://git.io/fjadv
void CodeGenLLVM::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
}

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";
Expand Down Expand Up @@ -419,6 +503,19 @@ void CodeGenLLVM::GetAlignment(Type t,
*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));
Expand Down
17 changes: 17 additions & 0 deletions src/codegen/llvm/codegen_llvm.h
Original file line number Diff line number Diff line change
Expand Up @@ -293,6 +293,23 @@ class CodeGenLLVM :
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);
// 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
} // namespace tvm
Expand Down
5 changes: 3 additions & 2 deletions src/codegen/llvm/llvm_common.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@
* 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
Expand Down Expand Up @@ -38,6 +38,7 @@
#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>
Expand Down
12 changes: 9 additions & 3 deletions src/codegen/llvm/llvm_module.cc
Original file line number Diff line number Diff line change
Expand Up @@ -187,14 +187,20 @@ class LLVMModuleNode final : public runtime::ModuleNode {
}
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();
}
Expand Down
73 changes: 73 additions & 0 deletions tests/python/unittest/test_codegen_llvm.py
Original file line number Diff line number Diff line change
Expand Up @@ -471,6 +471,78 @@ def check_llvm_sigmoid(n):
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()
Expand All @@ -489,3 +561,4 @@ def check_llvm_sigmoid(n):
test_llvm_lookup_intrin()
test_llvm_div()
test_llvm_fp_math()
test_dwarf_debug_information()

0 comments on commit 5b1a915

Please sign in to comment.