Hi everyone,
I am writing an application that uses LLVM JIT and I would like to throw an exception from the JIT and catch it in the C++ code that invokes the JIT.
This does not seem to work.
I've written what is hopefully a super simple demonstration to reproduce this.
I would appreciate any help with this.
Thank you
The demonstration is composed of:
1) thrower.cpp - a source file that contains a single function, throwInt(), that throws an int. This is compiled to thrower.s with clang and then to thrower.s.bc with llvm-as.
2) catcher.cpp - a source file that contains a main() that JITs thrower.s.bc and then calls throwInt(). This is compiled using g++.
When I run catcher it just prints this:
terminate called after throwing an instance of 'int'
I am using LLVM and CLANG 3.0, and gcc 4.7.2.
Here are all of the files needed to reproduce this:
1) thrower.cpp
2) catcher.cpp
3) Makefile
This is how I run the test:
make LLVM_BIN=path/to/llvm-3.0.src/Release/bin/
$ cat thrower.cpp ################
void throwInt()
{
throw 1;
}
$ cat thrower.s ################
; ModuleID = 'thrower.cpp'
target datalayout = "e-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-v64:64:64-v128:128:128-a0:0:64-s0:64:64-f80:128:128-n8:16:32:64-S128"
target triple = "x86_64-unknown-linux-gnu"
@_ZTIi = external constant i8*
define void @_Z8throwIntv() uwtable {
%1 = call i8* @__cxa_allocate_exception(i64 4) nounwind
%2 = bitcast i8* %1 to i32*
store i32 1, i32* %2
call void @__cxa_throw(i8* %1, i8* bitcast (i8** @_ZTIi to i8*), i8* null) noreturn
unreachable
; No predecessors!
ret void
}
declare i8* @__cxa_allocate_exception(i64)
declare void @__cxa_throw(i8*, i8*, i8*)
$ cat catcher.cpp ################
#include "llvm/LLVMContext.h"
#include "llvm/Module.h"
#include "llvm/ExecutionEngine/JIT.h"
#include "llvm/Support/IRReader.h"
#include "llvm/Support/TargetSelect.h"
#include
using namespace llvm;
int main(int, char**)
{
LLVMContext& context = getGlobalContext();
InitializeNativeTarget();
SMDiagnostic error;
Module* m = ParseIRFile("./thrower.s.bc", error, context);
if (!m)
{
printf("could not load module\n");
return 1;
}
ExecutionEngine* ee = ExecutionEngine::create(m);
if (!ee)
{
printf("could not create execution engine\n");
return 1;
}
Function* throwsIntFunction = ee->FindFunctionNamed("_Z8throwIntv");
if (!throwsIntFunction)
{
printf("could not find function\n");
return 1;
}
typedef void (*throwsIntType)();
throwsIntType throwsInt = reinterpret_cast(ee->getPointerToFunction(throwsIntFunction));
if (!throwsInt)
{
printf("could not get pointer to function\n");
return 1;
}
try
{
throwsInt();
}
catch (int ex)
{
printf("caught an int\n");
}
}
$ cat Makefile ################
LLVM_CXX_FLAGS=$(shell $(LLVM_BIN)/llvm-config --cxxflags)
LLVM_LIBS=$(shell $(LLVM_BIN)/llvm-config --libs)
LLVM_LD_FLAGS=$(shell $(LLVM_BIN)/llvm-config --ldflags)
all : tested
.PHONY: clean
clean:
rm thrower.s thrower.s.bc catcher tested
thrower.s : thrower.cpp Makefile
$(LLVM_BIN)/clang -S -emit-llvm thrower.cpp
thrower.s.bc : thrower.s Makefile
$(LLVM_BIN)/llvm-as thrower.s
catcher : catcher.cpp Makefile
g++ -g catcher.cpp -o catcher $(LLVM_CXX_FLAGS) $(LLVM_LIBS) $(LLVM_LD_FLAGS) -Wno-cast-qual -fexceptions -O0
tested : thrower.s.bc catcher Makefile
./catcher
touch tested