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:
Vassil Vassilev 2013-10-22 10:00:31 -05:00 committed by sftnight
parent 3bee47f990
commit 846aac852c
2 changed files with 20 additions and 9 deletions

View File

@ -313,12 +313,20 @@ namespace cling {
llvm::GlobalValue* GV llvm::GlobalValue* GV
= m_CurTransaction->getModule()->getNamedValue(mangledName); = 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()) if (!GV->use_empty()) {
GV->replaceAllUsesWith(llvm::UndefValue::get(GV->getType())); // 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(); GV->eraseFromParent();
} }

View File

@ -6,9 +6,12 @@ int f = 0;
.U .U
.rawInput 1 .rawInput 1
extern "C" int printf(const char* fmt, ...); extern "C" int printf(const char* fmt, ...);
void f() { int f() {
printf("Now f is a function\n"); printf("Now f is a function\n");
} return 0;
} int a = f();
.U
.rawInput 0 .rawInput 0
f()
//CHECK: Now f is a function //CHECK: Now f is a function
double f = 3.14
//CHECK: (double) 3.14