cling PR 233 (#2038)

Add llvm module pass to generate unique cuda module ctor/dtor names.

This llvm module pass address the follow problem. Every llvm module has a cuda ctor and dtor (if a cuda fatbinary exist), with at least a function call to register the fatbinary. The ctor/dtor can also include function calls to register global functions and variables at runtime, depending on user's code. The lazy compilation detects functions by the name. If the name (symbol) already exists it uses the existing translation. Otherwise it translates the function on first use (but it never translates twice). Without the module pass, Cling will always use the translation of the first module.

The testcase use the reflection of the gCling interpreter object. It takes two random modules and compare the symbols of the cuda module ctor and dtor.

Also add function, which change the symbol of the cuda module ctor and dtor to preprocessor compliant symbols.
This commit is contained in:
Axel Naumann 2018-05-18 11:07:36 +02:00 committed by sftnight
parent 98719ad352
commit 673a1d63a4
2 changed files with 120 additions and 0 deletions

View File

@ -25,6 +25,7 @@
//#include "clang/Basic/LangOptions.h" //#include "clang/Basic/LangOptions.h"
//#include "clang/Basic/TargetOptions.h" //#include "clang/Basic/TargetOptions.h"
#include "clang/Frontend/CodeGenOptions.h" #include "clang/Frontend/CodeGenOptions.h"
#include "clang/Basic/CharInfo.h"
using namespace cling; using namespace cling;
using namespace clang; using namespace clang;
@ -69,6 +70,51 @@ namespace {
char KeepLocalGVPass::ID = 0; char KeepLocalGVPass::ID = 0;
namespace {
// Add a suffix to the CUDA module ctor/dtor to generate a unique name.
// This is necessary for lazy compilation. Without suffix, cling cannot
// distinguish ctor/dtor of subsequent modules.
class UniqueCUDAStructorName : public ModulePass {
static char ID;
bool runOnFunction(Function& F, const StringRef ModuleName){
if(F.hasName() && (F.getName() == "__cuda_module_ctor"
|| F.getName() == "__cuda_module_dtor") ){
llvm::SmallString<128> NewFunctionName;
NewFunctionName.append(F.getName());
NewFunctionName.append("_");
NewFunctionName.append(ModuleName);
for (size_t i = 0; i < NewFunctionName.size(); ++i) {
// Replace everything that is not [a-zA-Z0-9._] with a _. This set
// happens to be the set of C preprocessing numbers.
if (!isPreprocessingNumberBody(NewFunctionName[i]))
NewFunctionName[i] = '_';
}
F.setName(NewFunctionName);
return true;
}
return false;
}
public:
UniqueCUDAStructorName() : ModulePass(ID) {}
bool runOnModule(Module &M) override {
bool ret = false;
const StringRef ModuleName = M.getName();
for (auto &&F: M)
ret |= runOnFunction(F, ModuleName);
return ret;
}
};
}
char UniqueCUDAStructorName::ID = 0;
BackendPasses::~BackendPasses() { BackendPasses::~BackendPasses() {
//delete m_PMBuilder->Inliner; //delete m_PMBuilder->Inliner;
@ -140,6 +186,11 @@ void BackendPasses::CreatePasses(llvm::Module& M, int OptLevel)
m_MPM[OptLevel].reset(new legacy::PassManager()); m_MPM[OptLevel].reset(new legacy::PassManager());
m_MPM[OptLevel]->add(new KeepLocalGVPass()); m_MPM[OptLevel]->add(new KeepLocalGVPass());
// The function __cuda_module_ctor and __cuda_module_dtor will just generated,
// if a CUDA fatbinary file exist. Without file path there is no need for the
// function pass.
if(!m_CGOpts.CudaGpuBinaryFileNames.empty())
m_MPM[OptLevel]->add(new UniqueCUDAStructorName());
m_MPM[OptLevel]->add(createTargetTransformInfoWrapperPass( m_MPM[OptLevel]->add(createTargetTransformInfoWrapperPass(
m_TM.getTargetIRAnalysis())); m_TM.getTargetIRAnalysis()));

View File

@ -0,0 +1,69 @@
//------------------------------------------------------------------------------
// CLING - the C++ LLVM-based InterpreterG :)
//
// This file is dual-licensed: you can choose to license it under the University
// of Illinois Open Source License or the GNU Lesser General Public License. See
// LICENSE.TXT for details.
//------------------------------------------------------------------------------
// The Test checks, if the symbols __cuda_module_ctor and __cuda_module_dtor are
// unique for every module. Attention, for a working test case, a cuda
// fatbinary is necessary.
// RUN: cat %s | %cling -x cuda -Xclang -verify 2>&1 | FileCheck %s
// REQUIRES: cuda-runtime
#include "cling/Interpreter/Interpreter.h"
#include "cling/Interpreter/Transaction.h"
#include "llvm/IR/Function.h"
#include "llvm/IR/Module.h"
#include <iostream>
// Compare the cuda module ctor and dtor of two random modules.
std::string ctor1, ctor2, dtor1, dtor2;
auto M1 = gCling->getLatestTransaction()->getModule();
for(auto &I : *M1){
// The trailing '_' identify the function name as modified name.
if(I.getName().startswith_lower("__cuda_module_ctor_")){
ctor1 = I.getName().str();
}
if(I.getName().startswith_lower("__cuda_module_dtor_")){
dtor1 = I.getName().str();
}
}
auto M2 = gCling->getLatestTransaction()->getModule();
// The two modules should have different names, because of the for loop.
M1->getName().str() != M2->getName().str()
// CHECK: (bool) true
for(auto &I : *M2){
if(I.getName().startswith_lower("__cuda_module_ctor_")){
ctor2 = I.getName().str();
}
if(I.getName().startswith_lower("__cuda_module_dtor_")){
dtor2 = I.getName().str();
}
}
// Check if the ctor and dtor of the two modules are different.
ctor1 != ctor2 // expected-note {{use '|=' to turn this inequality comparison into an or-assignment}}
// CHECK: (bool) true
dtor1 != dtor2 // expected-note {{use '|=' to turn this inequality comparison into an or-assignment}}
// CHECK: (bool) true
// Check if the ctor symbol starts with the correct prefix.
std::string expectedCtorPrefix = "__cuda_module_ctor_cling_module_";
ctor1.compare(0, expectedCtorPrefix.length(), expectedCtorPrefix)
// CHECK: (int) 0
// Check if the dtor symbol starts with the correct prefix.
std::string expectedDtorPrefix = "__cuda_module_dtor_cling_module_";
dtor1.compare(0, expectedDtorPrefix.length(), expectedDtorPrefix)
// CHECK: (int) 0
.q