LLVM JIT: pass C++ exception through JIT code back to host application - c++

I'm working on a project where I use clang to generate some LLVM IR and then JIT-compile and run it from within my host application. The JIT code calls some functions in the host application which may throw an exception. I expect the exception to be thrown through the JIT code and catched back in the host application. AFAIK this is supposed to work with LLVM, but unfortunately my test application always crashes with "terminate called after throwing an instance of 'int'". Let me give some simple example.
I use clang 3.5 to compile the following simple program into LLVM IR:
extern void test() ;
extern "C" void exec(void*) {
test();
}
with
./clang -O0 -S -emit-llvm test.cpp -c
The result is test.ll
; ModuleID = 'test.cpp'
target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
target triple = "x86_64-unknown-linux-gnu"
; Function Attrs: uwtable
define void #exec(i8*) #0 {
%2 = alloca i8*, align 8
store i8* %0, i8** %2, align 8
call void #_Z4testv()
ret void
}
declare void #_Z4testv() #1
attributes #0 = { uwtable "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" "stack-protector-buffer-size"="8" "unsafe-fp-math"="false" "use-soft-float"="false" }
attributes #1 = { "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" "stack-protector-buffer-size"="8" "unsafe-fp-math"="false" "use-soft-float"="false" }
!llvm.ident = !{!0}
!0 = metadata !{metadata !"clang version 3.5.0 (224841)"}
My host application looks like this:
static void test() {
throw 1;
}
int main(int, const char **) {
llvm::InitializeNativeTarget();
llvm::InitializeNativeTargetAsmPrinter();
llvm::InitializeNativeTargetAsmParser();
llvm::LLVMContext &Context = llvm::getGlobalContext();
llvm::SMDiagnostic Err;
llvm::Module *Mod = llvm::ParseIRFile("test.ll", Err, Context);
llvm::ExecutionEngine* m_EE = llvm::EngineBuilder(Mod)
.setEngineKind(llvm::EngineKind::JIT)
.create();
llvm::Function* f = Mod->getFunction("_Z4testv");
m_EE->addGlobalMapping(f, reinterpret_cast<void*>(test));
f = Mod->getFunction("exec");
void* poi = m_EE->getPointerToFunction(f);
void (*exec)(void*) = reinterpret_cast<void (*)(void*)>(poi);
try {
exec(NULL);
} catch (...) {
std::cout << "catched exception" << std::endl;
}
return 0;
}
I use LLVM 3.5 which I compiled with cmake. I set LLVM_ENABLE_EH=ON and LLVM_ENABLE_RTTI=ON. Did I miss something when compiling LLVM or is my host application code wrong?
Thanks!

Finally it works and here are a few things which are necessary to fix the issue.
First it's important to make sure MCJIT.h has been included, otherwise MCJIT is not linked in. Unfortunately LLVM silently falls back to the old JIT implementation if MCJIT.h has not been included even though MCJIT has been explicitly requested by:
llvm::EngineBuilder factory(Mod);
factory.setEngineKind(llvm::EngineKind::JIT);
factory.setUseMCJIT(true);
Only MCJIT supports propper exception handling.
In the example in the question I used
Execution::Engine::addGlobalMapping()
which does not work with MCJIT. External function must be reqistered via
llvm::sys::DynamicLibrary::AddSymbol()
Following the complete example:
static void test() {
throw 1;
}
int main(int, const char **) {
llvm::InitializeNativeTarget();
llvm::InitializeNativeTargetAsmPrinter();
llvm::InitializeNativeTargetAsmParser();
llvm::LLVMContext &Context = llvm::getGlobalContext();
llvm::SMDiagnostic Err;
llvm::Module *Mod = llvm::ParseIRFile("test.ll", Err, Context);
std::unique_ptr<llvm::RTDyldMemoryManager> MemMgr(new llvm::SectionMemoryManager());
// Build engine with JIT
std::string err;
llvm::EngineBuilder factory(Mod);
factory.setErrorStr(&err);
factory.setEngineKind(llvm::EngineKind::JIT);
factory.setUseMCJIT(true);
factory.setMCJITMemoryManager(MemMgr.release());
llvm::ExecutionEngine *m_EE = factory.create();
llvm::sys::DynamicLibrary::AddSymbol("_Z4testv", reinterpret_cast<void*>(test));
llvm::Function* f = Mod->getFunction("exec");
m_EE->finalizeObject();
void* poi = m_EE->getPointerToFunction(f);
void (*exec)(void*) = reinterpret_cast<void (*)(void*)>(poi);
try {
exec(NULL);
} catch (int e) {
std::cout << "catched " << e << std::endl;
}
return 0;
}
Additionally you can now also get Debug Symbols for the JIT code by adding:
Opts.JITEmitDebugInfo = true;

Related

<badref> when using CallInst::CreateMalloc for a struct type

I am trying to generate LLVM IR for code (in a toy language) similar to the following C++ code:
struct test {
int a;
int b;
int c;
};
int main() {
tempVar *a;
a = new test();
}
Unfortunately, when I run verifyModule, I receive the following error messages:
Instruction referencing instruction not embedded in a basic block!
%malloccall = tail call i8* #malloc(i64 mul nuw (i64 ptrtoint (i32* getelementptr (i32, i32* null, i32 1) to i64), i64 3))
<badref> = bitcast i8* %malloccall to %test*
The following MWE reproduces the issue:
#include "llvm/IR/DerivedTypes.h"
#include "llvm/IR/Function.h"
#include "llvm/IR/IRBuilder.h"
#include "llvm/IR/LLVMContext.h"
#include "llvm/IR/Verifier.h"
#include "llvm/Support/raw_ostream.h"
#include <map>
#include <vector>
using namespace llvm;
std::map<std::string, StructType *> allocatedClasses;
std::map<std::string, std::vector<Type *>> classSizes;
static LLVMContext TheContext;
static IRBuilder<> Builder(TheContext);
static std::map<std::string, AllocaInst *> NamedValues;
int main() {
static std::unique_ptr<Module> TheModule;
TheModule = std::make_unique<Module>("inputFile", TheContext);
allocatedClasses["test"] = StructType::create(TheContext, "test");
classSizes["test"] = std::vector<Type *>(3, Type::getInt32Ty(TheContext));
allocatedClasses["test"]->setBody(classSizes["test"]);
FunctionType *mainType = FunctionType::get(Builder.getInt32Ty(), false);
Function *main = Function::Create(mainType, Function::ExternalLinkage, "main",
TheModule.get());
BasicBlock *entry = BasicBlock::Create(TheContext, "entry", main);
Builder.SetInsertPoint(entry);
std::string tV = "tempVar";
NamedValues[tV] = Builder.CreateAlloca(
PointerType::get(allocatedClasses["test"], 0), nullptr, tV);
auto typeSize = ConstantExpr::getSizeOf(allocatedClasses["test"]);
typeSize =
ConstantExpr::getTruncOrBitCast(typeSize, Type::getInt64Ty(TheContext));
CallInst::CreateMalloc(Builder.GetInsertBlock(), Type::getInt64Ty(TheContext),
allocatedClasses["test"], typeSize, nullptr, nullptr,
"");
Builder.CreateRet(ConstantInt::get(TheContext, APInt(32, 0)));
TheModule->print(outs(), nullptr);
Module *test = TheModule.get();
verifyModule(*test, &errs());
}
I compile this with
clang++ `llvm-config --cxxflags --ldflags --system-libs --libs all` -g ex.cpp, using clang version 10.0.0-4ubuntu1, on x86. When executed, the program outputs:
; ModuleID = 'inputFile'
source_filename = "inputFile"
%test = type { i32, i32, i32 }
define i32 #main() {
entry:
%tempVar = alloca %test*
%malloccall = tail call i8* #malloc(i64 mul nuw (i64 ptrtoint (i32* getelementptr (i32, i32* null, i32 1) to i64), i64 3))
ret i32 0
}
declare noalias i8* #malloc(i64)
and the error message from above.
What am I doing wrong here?
The documentation for the function you call says "Note: This function does not add the bitcast to the basic block, that is the responsibility of the caller."
I have no idea why not, but the caller is you.

Segfault in emitted IR For `printf` call

I would like to use the system printf to be able to print a single integer from the programming language I'm writing a compiler for, as in print(3). I am running into a segfault when executing the compiled IR.
Following this example, my code is
#include "llvm/ADT/APInt.h"
#include "llvm/IR/BasicBlock.h"
#include "llvm/IR/Function.h"
#include "llvm/IR/IRBuilder.h"
#include "llvm/IR/Module.h"
#include "llvm/Support/raw_ostream.h"
#include <memory>
#include <vector>
using namespace llvm;
static LLVMContext TheContext;
static IRBuilder<> Builder(TheContext);
int main() {
static std::unique_ptr<Module> TheModule;
TheModule = std::make_unique<Module>("inputFile", TheContext);
std::vector<Type *> Nats(1, Type::getInt32Ty(TheContext));
FunctionType *PNFT =
FunctionType::get(Type::getInt32Ty(TheContext), Nats, false);
Function *PNF = Function::Create(PNFT, Function::ExternalLinkage, "printf",
TheModule.get());
for (auto &Arg : PNF->args()) {
Arg.setName("x");
}
FunctionType *mainType = FunctionType::get(Builder.getInt32Ty(), false);
Function *main = Function::Create(mainType, Function::ExternalLinkage, "main",
TheModule.get());
BasicBlock *entry = BasicBlock::Create(TheContext, "entry", main);
Builder.SetInsertPoint(entry);
std::vector<Value *> printArgs;
printArgs.push_back(ConstantInt::get(TheContext, APInt(32, 20)));
Builder.CreateCall(TheModule->getFunction("printf"), printArgs);
Builder.CreateRet(ConstantInt::get(TheContext, APInt(32, 0)));
TheModule->print(llvm::outs(), nullptr);
}
I compile this with clang++ `llvm-config --cxxflags --ldflags --system-libs --libs all` test.cpp
This outputs the LLVM IR
; ModuleID = 'inputFile'
source_filename = "inputFile"
declare i32 #printf(i32)
define i32 #main() {
entry:
%0 = call i32 #printf(i32 20)
ret i32 0
}
which I put into a file test.ll and compile as clang test.ll. I threw the segfaulting code into lldb and found that the code segfaults in strchr:
(lldb) bt
* thread #1, name = 'a.out', stop reason = signal SIGSEGV: invalid address (fault address: 0x14)
* frame #0: 0x00007ffff7f300fc libc.so.6`__strchrnul_avx2 + 28
frame #1: 0x00007ffff7e38a53 libc.so.6`__vfprintf_internal + 163
frame #2: 0x00007ffff7e25a2f libc.so.6`_IO_printf + 175
frame #3: 0x000055555555514b a.out`main + 11
frame #4: 0x00007ffff7df5002 libc.so.6`__libc_start_main + 242
frame #5: 0x000055555555506e a.out`_start + 46
I don't think the problem is with compiling the IR, because in my actual code (that is, not the MVE I've shown above) I'm emitting object code directly using a pass (as suggested in part eight of the kaleidescope tutorial) and still experiencing the same issue. What am I doing wrong here?
Thank you.
Thanks to #arnt's comment, I found this resource which lead me to modify my program to the following:
#include "llvm/ADT/APInt.h"
#include "llvm/IR/BasicBlock.h"
#include "llvm/IR/Function.h"
#include "llvm/IR/IRBuilder.h"
#include "llvm/IR/Module.h"
#include "llvm/Support/raw_ostream.h"
#include <memory>
#include <vector>
using namespace llvm;
static LLVMContext TheContext;
static IRBuilder<> Builder(TheContext);
int main() {
static std::unique_ptr<Module> TheModule;
TheModule = std::make_unique<Module>("inputFile", TheContext);
std::vector<llvm::Type *> args;
args.push_back(llvm::Type::getInt8PtrTy(TheContext));
llvm::FunctionType *printfType =
llvm::FunctionType::get(Builder.getInt32Ty(), args, true);
llvm::Constant *printfFunc = Function::Create(
printfType, Function::ExternalLinkage, "printf", TheModule.get());
/*begin codegen for `main`*/
FunctionType *mainType = FunctionType::get(Builder.getInt32Ty(), false);
Function *main = Function::Create(mainType, Function::ExternalLinkage, "main",
TheModule.get());
BasicBlock *entry = BasicBlock::Create(TheContext, "entry", main);
Builder.SetInsertPoint(entry);
std::vector<Value *> printArgs;
llvm::Value *formatStr = Builder.CreateGlobalStringPtr("%d\n");
printArgs.push_back(formatStr);
printArgs.push_back(ConstantInt::get(TheContext, APInt(32, 20)));
Builder.CreateCall(TheModule->getFunction("printf"), printArgs);
Builder.CreateRet(ConstantInt::get(TheContext, APInt(32, 0)));
TheModule->print(llvm::outs(), nullptr);
}
This emits the following IR:
; ModuleID = 'inputFile'
source_filename = "inputFile"
#0 = private unnamed_addr constant [4 x i8] c"%d\0A\00", align 1
declare i32 #printf(i8*, ...)
define i32 #main() {
entry:
%0 = call i32 (i8*, ...) #printf(i8* getelementptr inbounds ([4 x i8], [4 x i8]* #0, i32 0, i32 0), i32 20)
ret i32 0
}
which works as expected.

Clang adds noinline attribute to all functions when emitting LLVM IR

Consider the following simple function:
int foo() { return 42; }
Compiling this to LLVM via clang -emit-llvm -S foo.cpp produces the following module:
; ModuleID = 'foo.cpp'
source_filename = "foo.cpp"
target datalayout = "e-m:o-i64:64-f80:128-n8:16:32:64-S128"
target triple = "x86_64-apple-macosx10.13.0"
; Function Attrs: noinline nounwind ssp uwtable
define i32 #_Z3foov() #0 {
ret i32 42
}
attributes #0 = { noinline nounwind 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" }
!llvm.module.flags = !{!0}
!llvm.ident = !{!1}
!0 = !{i32 1, !"PIC Level", i32 2}
!1 = !{!"Apple LLVM version 9.0.0 (clang-900.0.37)"}
Why is the foo function declared as noinline? The flag is not added if an optimization level (other than -O0) is specified, but I would like to avoid that.
Is there another way / flag?
With -O0, you can't enable inlining globally, judging from Clang's source code
(Frontend\CompilerInvocation.cpp):
// At O0 we want to fully disable inlining outside of cases marked with
// 'alwaysinline' that are required for correctness.
Opts.setInlining((Opts.OptimizationLevel == 0)
? CodeGenOptions::OnlyAlwaysInlining
: CodeGenOptions::NormalInlining);
Depending on your requirements, you may:
Use -O1, which is the closest to -O0.
Use -O1 in conjuction with disabling of optimization flags that it enables. See the following answer for optimization flags enabled with -O1: Clang optimization levels
Apply always_inline attribute selectively on functions that should be inlined.
For example: int __attribute__((always_inline)) foo() { return 42; }

Using Clang and LLVM

I am compiling this:
int main(){
}
With clang, using this command line:
clang++.exe -S -o %OUTFILE%.clang -emit-llvm %INFILE% -I. -I%INCLUDE_PATH% -std=c++14 -ftemplate-depth=1000
Which gives me llvm byte-code.
Then I use llc like so, to convert the byte-code into c code:
llc "%IN_FILE%.clang" -march=c -o foo.c
And get this error:
error: unterminated attribute group
attributes #0 = { norecurse nounwind uwtable "disable-tail-calls"="false" "less-precise-fpmad"="false" "no-frame-pointer-elim"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+fxsr,+mmx,+sse,+sse2" "unsafe-fp-math"="false" "use-soft-float"="false" }
What I am doing wrong?
This is what clang++ is giving me:
; ModuleID = 'C:\Users\Owner\Documents\C++\SVN\prototypeInd\util\StaticBigInt.cpp'
target datalayout = "e-m:w-i64:64-f80:128-n8:16:32:64-S128"
target triple = "x86_64-pc-windows-msvc18.0.0"
; Function Attrs: norecurse nounwind readnone uwtable
define i32 #main() #0 {
entry:
ret i32 0
}
attributes #0 = { norecurse nounwind readnone uwtable "disable-tail-calls"="false" "less-precise-fpmad"="false" "no-frame-pointer-elim"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+fxsr,+mmx,+sse,+sse2" "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 3.8.0 (branches/release_38)"}
Note:
I am using clang version 3.8 and llc version 3.4
When you run a command such as:
clang -S -emit-llvm ...
Then the compiler emits not an IR in a bitcode form, but human readable representation.
It makes sense if all tools you use have the same versions, or if you just want to inspect the output manually.
However, the human readable IR may be incompatible with old tools.
In this case I can recommend to use bitcode directly (note that there is no -S parameter anymore):
clang -emit-llvm
C backend in LLVM was removed several years ago. It seems that you're trying to feed LLVM IR from the recent LLVM version to llc from old LLVM. This is certainly not supported - the IR is not compatible between the versions.

Understanding how memory allocation works (LLVM)

I'm making progress on a toy compiler (first time), and trying to understand how to allocate/construct an LLVM struct type.
The Kaleidoscope tutorial doesn't include or even mention this and I don't know what I'm looking for in the LLVM source/tests to find possible examples.
So I've written a simply C++ example, dumped the IR with clang in an effort to try to understand what it produces but to be honest I don't follow it all. The things obvious to me are the function definition/declarations and some function calls and a memset call so I get pieces of it but it doesn't all come together for me yet. (P.S my interpretation of the alloca instruction docs is that it anything created from that gets freed on return so I can't use that right, it's essentially only for local variables?)
What I've done is:
alloc.cpp
struct Alloc {
int age;
};
//Alloc allocCpy() {
// return *new Alloc();
//}
Alloc *allocPtr() {
return new Alloc();
}
int main() {
Alloc *ptr = allocPtr();
// ptr->name = "Courtney";
// Alloc cpy = allocCpy();
// cpy.name = "Robinson";
// std::cout << ptr->name << std::endl;
// std::cout << cpy.name << std::endl;
return 0;
}
Then run clang -S -emit-llvm alloc.cpp to produce alloc.ll
; ModuleID = 'alloc.cpp'
target datalayout = "e-m:o-i64:64-f80:128-n8:16:32:64-S128"
target triple = "x86_64-apple-macosx10.11.0"
%struct.Alloc = type { i32 }
; Function Attrs: ssp uwtable
define %struct.Alloc* #_Z8allocPtrv() #0 {
entry:
%call = call noalias i8* #_Znwm(i64 4) #3
%0 = bitcast i8* %call to %struct.Alloc*
%1 = bitcast %struct.Alloc* %0 to i8*
call void #llvm.memset.p0i8.i64(i8* %1, i8 0, i64 4, i32 4, i1 false)
ret %struct.Alloc* %0
}
; Function Attrs: nobuiltin
declare noalias i8* #_Znwm(i64) #1
; Function Attrs: nounwind
declare void #llvm.memset.p0i8.i64(i8* nocapture, i8, i64, i32, i1) #2
; Function Attrs: ssp uwtable
define i32 #main() #0 {
entry:
%retval = alloca i32, align 4
%ptr = alloca %struct.Alloc*, align 8
store i32 0, i32* %retval
%call = call %struct.Alloc* #_Z8allocPtrv()
store %struct.Alloc* %call, %struct.Alloc** %ptr, align 8
ret i32 0
}
attributes #0 = { ssp uwtable "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" "stack-protector-buffer-size"="8" "target-cpu"="core2" "target-features"="+cx16,+sse,+sse2,+sse3,+ssse3" "unsafe-fp-math"="false" "use-soft-float"="false" }
attributes #1 = { nobuiltin "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" "stack-protector-buffer-size"="8" "target-cpu"="core2" "target-features"="+cx16,+sse,+sse2,+sse3,+ssse3" "unsafe-fp-math"="false" "use-soft-float"="false" }
attributes #2 = { nounwind }
attributes #3 = { builtin }
!llvm.module.flags = !{!0}
!llvm.ident = !{!1}
!0 = !{i32 1, !"PIC Level", i32 2}
!1 = !{!"clang version 3.7.0 (tags/RELEASE_370/final)"}
Can someone explain what's happening in this IR and how it maps back to the C++? Or ignoring this specific example how one would/should go about allocating heap memory for an LLVM StructType that out lives the function within which it was created (and if you're feeling generous, how to later release the memory).
The bits I've commented out are from my original example but being a total novice the IR from that was even less insightful...
my interpretation of the alloca instruction docs is that it anything
created from that gets freed on return so I can't use that right, it's
essentially only for local variables?
Yes. Furthermore, the current advice on LLVM IR is that although alloca works as you expect it to, optimizations are another case. They advise that you alloca all of your locals in the entry block right away, even if you don't allow the user access to them or they don't always contain meaningful data.
Heap allocation is a library feature. It is not a feature of LLVM or the compiler. When you use new T(), the compiler simply calls operator new to get the memory and then constructs T there. There is no magic involved. Most of the junk that you see there is C++-ABI specific rather than any requirement of LLVM. It eventually lowers into something like void* p = malloc(size); new(p) T();. For pretty much all types T, this pretty much boils down to a series of stores into p or calling a user-defined function.
You can use the memory allocation function from the runtime library of your choice.
trying to understand how to allocate/construct an LLVM struct type
The LLVM type system does not include the notion of construction. That is a notion of the source language.
As far as LLVM is concerned, a struct is just a bunch of bits, and all memory locations are more-or-less the same. If you want the bits to be a particular thing, then store the bits you want to that location. If you want to put the bits on the heap, then call a runtime library heap allocation function and store the bits into that location.
Note that garbage collection, however, is a somewhat different story, as there is some awkward stuff going on w.r.t. finding locals on the stack for marking.
For the record, you will not get far trying to understand Clang's LLVM IR. I've been doing that for several years now and it is batshit crazy and will take you that long to start to get a grip, not to mention full of C++-specific ABI details that you don't want to know about. You will get a lot further asking in #llvm in their IRC channel or asking specific questions here than in trying to reverse-engineer that.
I don't recommend looking at unoptimized IR emitted by Clang - it's way too verbose. -O1 makes it a lot more readable - here's the -O1 version with comments annotating the lines (also I've reordered two lines to make it slightly more readable):
%struct.Alloc = type { i32 } ; Define the Alloc type.
define noalias %struct.Alloc* #_Z8allocPtrv() #0 {
%1 = tail call noalias i8* #_Znwj(i32 4) #2 ; Call _Znwj(4). This retuns i8*.
%3 = bitcast i8* %1 to i32* ; Cast the returned value to i32* (int*)...
store i32 0, i32* %3, align 4 ; ...and zero its content.
%2 = bitcast i8* %1 to %struct.Alloc* ; Cast the returned value to Alloc*...
ret %struct.Alloc* %2 ; ...and return it.
}
; Declare the _Znwj function. This doesn't need to be defined since it's already defined
; in libstdc++: this is 'operator new'. You can see this by passing this string through a
; C++ demangler, for example the one at http://demangler.com/.
declare noalias i8* #_Znwj(i32) #1
define i32 #main() #0 {
%1 = tail call %struct.Alloc* #_Z8allocPtrv() ; Call _Z8allocPtrv (Defined above).
ret i32 0
}
This is a new call, not a local allocation, so it will not be cleared when leaving #_Z8allocPtrv. Local allocations are indeed performed in LLVM IR with the alloca instruction, and not a new call.
If you're curious how new works, I believe its standard implementation uses malloc, which is translated by the compiler that compiled the library to some function that includes system call(s).