Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do template instantiations get compiled by Clang?

I am using Clang as a library to compile some templated code:

template<typename T>
T getSevenTemplated() {
  return 7;
}

int getSeven() {
  return getSevenTemplated<int>();
}

Unfortunately, the compiled LLVM IR does not actually contain the implementation of getSevenTemplated<int>:

; ModuleID = './test.cpp'
source_filename = "./test.cpp"
target datalayout = "e-m:o-i64:64-f80:128-n8:16:32:64-S128"
target triple = "x86_64-apple-macosx10.12.0"

; Function Attrs: ssp uwtable
define i32 @_Z8getSevenv() #0 {
entry:
  %call = call i32 @_Z17getSevenTemplatedIiET_v()
  ret i32 %call
}

declare i32 @_Z17getSevenTemplatedIiET_v() #1

attributes #0 = { ssp uwtable "correctly-rounded-divide-sqrt-fp-math"="false" "disable-tail-calls"="false" "less-precise-fpmad"="false" "no-frame-pointer-elim"="true" "no-frame-pointer-elim-non-leaf" "no-infs-fp-math"="false" "no-jump-tables"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="penryn" "target-features"="+cx16,+fxsr,+mmx,+sse,+sse2,+sse3,+sse4.1,+ssse3,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" }
attributes #1 = { "correctly-rounded-divide-sqrt-fp-math"="false" "disable-tail-calls"="false" "less-precise-fpmad"="false" "no-frame-pointer-elim"="true" "no-frame-pointer-elim-non-leaf" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="penryn" "target-features"="+cx16,+fxsr,+mmx,+sse,+sse2,+sse3,+sse4.1,+ssse3,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" }

!llvm.module.flags = !{!0}
!llvm.ident = !{!1}

!0 = !{i32 1, !"PIC Level", i32 2}
!1 = !{!"clang version 5.0.0 (trunk 292778)"}

Here is the code I am using to generate the LLVM module:

auto* context = new llvm::LLVMContext(); // TODO: Fix leak 

auto codeGenerator =
  std::shared_ptr<clang::CodeGenerator>(
    clang::CreateLLVMCodeGen(
      compilerInstance.getDiagnostics(), 
      filePath,
      compilerInstance.getHeaderSearchOpts(), 
      preprocessor.getPreprocessorOpts(),
      compilerInstance.getCodeGenOpts(), 
      *context, 
      nullptr));

codeGenerator->Initialize(compilerInstance.getASTContext());

// declGroups are found by calling ParseAST with a special ASTConsumer earlier
for (auto declGroup : declGroups) { 
  codeGenerator->HandleTopLevelDecl(declGroup);
}

codeGenerator->HandleTranslationUnit(compilerInstance.getASTContext());

To investigate why, I looked through the source-code and I found that the CodeGenerator has specific logic for handling templates: it seems to skip function template instantiations! I presume that it handles the generated functions instead, but I don't know how it really works.

My questions are:

  1. At a high level, how does Clang go about instantiating templates and passing them to the ASTConsumer for code generation?
  2. Are there any special tricks for enabling the generation of function bodies for instantiated templates? What am I missing from the code above?
like image 766
sdgfsdh Avatar asked Feb 02 '17 11:02

sdgfsdh


1 Answers

The problem was that I had incremental parsing turned on. With this setting enabled, the ParseAST function does not call clang::Sema::ActOnEndOfTranslationUnit, which triggers the instantiation of templates.

The trick is to add this call after processing your decls:

// This triggers the instantiation of templated functions
sema.ActOnEndOfTranslationUnit();
like image 114
sdgfsdh Avatar answered Nov 29 '22 01:11

sdgfsdh