Assert that the reference came from the static initializers and upgrade the test.
The compiled code is unloaded by iterating the AST out of which the compiled code came. The problem is that C++ supports static initialization. For example: int a = 5; on the global scope means set the value of a to 5 before the program starts. To support that clang implicitly generates functions that enforce the expected initialization order. That can become easily very complex. Let's see a more "real-life" example: int f() { printf("I am f()"); return 0; } int a = f(); Besides the code for a and f clang will emit: define internal void @__cxx_global_var_init() section "__TEXT,__StaticInit,regular,pure_instructions" { entry: %0 = call i32 @__cxa_atexit(void (i8*)* bitcast (void (%"struct.cling::runtime::internal::__trigger__cxa_atexit"*)* @_ZN5cling7runtime8internal21__trigger__cxa_atexitD1Ev to void (i8*)*), i8* getelementptr inbounds (%"struct.cling::runtime::internal::__trigger__cxa_atexit"* @_ZN5cling7runtime8internal1SE, i32 0, i32 0), i8* @__dso_handle) #1 ret void } define internal void @_GLOBAL__I_a() section "__TEXT,__StaticInit,regular,pure_instructions" { entry: call void @__cxx_global_var_init() ret void } and more importantly: define internal void @__cxx_global_var_init1() section "__TEXT,__StaticInit,regular,pure_instructions" { entry: %call = call i32 @_Z1fv() ; THAT IS A REFERENCE TO f() store i32 %call, i32* undef, align 4 ret void } So when iterating the AST and trying to remove f(), we cannot because it is still referenced by __cxx_global_var_init1. However we know this is not an issue, because that __cxx_global_var_init1 was meant to be executed only once and that already happened. So in first approximation we could drop the references of f in __cxx_global_var_init1 and remove the rest.
This commit is contained in:
parent
3bee47f990
commit
846aac852c
@ -313,12 +313,20 @@ namespace cling {
|
||||
|
||||
llvm::GlobalValue* GV
|
||||
= m_CurTransaction->getModule()->getNamedValue(mangledName);
|
||||
// Remove the mapping from the JIT, otherwise it locks the GV and
|
||||
// replaceAllUsesWith doesn't work
|
||||
m_EEngine->updateGlobalMapping(GV, 0);
|
||||
|
||||
if (!GV->use_empty())
|
||||
GV->replaceAllUsesWith(llvm::UndefValue::get(GV->getType()));
|
||||
if (!GV->use_empty()) {
|
||||
// Assert that if there was a use it is not coming from the explicit AST
|
||||
// node, but from the implicitly generated functions, which ensure
|
||||
// the initialization order semantics. Such functions are:
|
||||
// _GLOBAL__I* and __cxx_global_var_init*
|
||||
//
|
||||
assert(GV->hasOneUse()
|
||||
&& "Must have only one use coming from the static inits");
|
||||
// We can 'afford' to drop all the references because we know that the
|
||||
// static init functions must be called only once, and that was already
|
||||
// done.
|
||||
GV->use_back()->dropAllReferences();
|
||||
}
|
||||
GV->eraseFromParent();
|
||||
}
|
||||
|
||||
|
@ -6,9 +6,12 @@ int f = 0;
|
||||
.U
|
||||
.rawInput 1
|
||||
extern "C" int printf(const char* fmt, ...);
|
||||
void f() {
|
||||
int f() {
|
||||
printf("Now f is a function\n");
|
||||
}
|
||||
return 0;
|
||||
} int a = f();
|
||||
.U
|
||||
.rawInput 0
|
||||
f()
|
||||
//CHECK: Now f is a function
|
||||
double f = 3.14
|
||||
//CHECK: (double) 3.14
|
||||
|
Loading…
Reference in New Issue
Block a user