Creating a local symbol(?) for CreateCall instruction

Hello there,

So I’ll get right down to the chase because I am actually unsure how to ask this question.

I have a very simple source code that looks like this:

void *test (char* addr) {
        return test;
}
int main() {
char *buffer = malloc(sizeof(char)*50);
buffer = test(buffer);
return 0; }

The above code will generate an IR such as this:

<- omitted ->
  %46 = load i8*, i8** %7, align 8
  %47 = call i8* @test(i8* %46)
  store i8* %47, i8** %7, align 8
<- omitted ->

I have been basically trying to construct the above codes from the scratch using my own alloca instruction. But to be more specific, I’m most interested in creating the instruction shown below:

%47 = call i8* @test(i8* %46)

Assuming that I have an AllocaInst called alloc_target to use, here is my attempt at creating such instruction:

// function test()
FunctionCallee test_fun = currFun->getParent()->getOrInsertFunction("test", InstFunType); 

// assume this is a legitimate alloca instruction to use
AllocaInst *alloc_target; 

ir_builder.SetInsertPoint(curr_I->getPrevNode());
auto custom_load_I = ir_builder.CreateLoad(alloc_target);

ir_builder.SetInsertPoint(custom_load_I->getNextNode());
auto custom_call_I = ir_builder.CreateCall(test_fun, custom_load_I);

ir_builder.SetInsertPoint(custom_call_I ->getNextNode());
auto custom_store_I = mte_builder.CreateStore(custom_call_I, alloc_target);

However, using CreateCall like shown above simple results in:

call void @test(i8** %2)

without any local symbol.

Could anyone please give me any insights regarding this or suggestions on how to approach this? I feel that I need to basically create an instruction without relying on IRBuilder? But not too sure at the moment.
Kind regards,
Jay

I feel like you’ve cropped all of the bits of the code that are needed to understand what you’re trying to do. Is %2 your alloca instruction? How did you create the AllocaInst? Nothing in the code that you’ve pasted looks obviously wrong, so I presume your bug is in the code that you’ve elided. The only slightly dubious thing is that you’re not passing a type to CreateLoad / CreateStore, which implies that you’re using a very old version of LLVM.

Your terminology is extremely confusing and inaccurate, but I think what you mean is you have call void not %n = call i8*, in which case that’s just because your InstFunType has the wrong return type set.

Hello @davidchisnall and @jrtc27. Thank you very much for your responses.

@jrtc27 was able to pinpoint the problem:

I think what you mean is you have call void not %n = call i8*

and yes you were absolutely correct, thank you very much for your guidance.

I was able to figure out what was needed.

Something like this was able to solve it (in case anyone might need an example code):

PointerType* int_8_ptr = PointerType::get(IntegerType::get(M.getContext(), 8), 0);
PointerType* int_8_pointer_ptr = PointerType::get(int_8_ptr, 0);
Type *charPtrRetType = Type::getInt8PtrTy(context);   
std::vector<Type*> charParamTypes = {int_8_ptr};
FunctionCallee test_fun = currFun->getParent()->getOrInsertFunction("test", CharInstFunType);

PS: I apologize for the confusing question, at the time of asking, I didn’t quite understand how to phrase it properly. I purposely removed a lot of codes because I didn’t want to make my question too bloated, but that didn’t seem to have made my question a lot more confusing :sweat_smile: I’ll keep that in mind next time!

Even in that “example code” you managed to miss a rather critical line. The code style is also a mess (inconsistent * placement, and inconsistent snake_case vs camelCase; the latter is the LLVM style, though normally UpperCamelCase for variables), and it’ll probably soon break when opaque pointers become the only option in-tree. Also hard-coding 0 as the address space won’t always work.

Going through the example line by line:

PointerType* int_8_ptr = PointerType::get(IntegerType::get(M.getContext(), 8), 0);

As @jrtc27 says, don’t hard-code address-space 0. You probably want to query the data layout to get the stack address space, if you want to use this as a stack pointer.

PointerType* int_8_pointer_ptr = PointerType::get(int_8_ptr, 0);

You could probably propagate the address space here from the first type, though this may be the one that needs to be explicitly in the stack address space. It’s not clear in your example what this is for, but it if it’s the type of an alloca then that should use getAllocaAddrSpace() on the DataLayout to get the correct address space.

Type *charPtrRetType = Type::getInt8PtrTy(context);   

This type is the same as your int_8_ptr type, so I don’t know why you’re getting it two different ways. As above, this should explicitly specify the address space. The second parameter to Type::getInt8PtrTy has a default value of 0 but that will go away at some point.

std::vector<Type*> charParamTypes = {int_8_ptr};

Allocating a std::vector to store a single element is very wasteful. If it’s a small known number of elements, you should prefer std::array. If it’s a small and not-known number, llvm::SmallVector supports dynamic resizing and is optimised for cases where you can fit everything in statically-known size.

Neither is necessary here because llvm::ArrayRef<T> is constructable from a single T and so you can just pass {int_8_ptr} as the argument.

FunctionCallee test_fun = currFun->getParent()->getOrInsertFunction("test", CharInstFunType);

I’m not sure what CharInstFunType is here, presumably it’s a FunctionType that you’ve created from some of your other types? Note that, if you’re not setting function attributes, you can use the fourth overload of getOrInsertFunction and specify the return type and the argument types, something like:

FunctionCallee testFn = currFn->getParent().getOrInsertFunction("test", I8Ptr, I8Ptr);

Thank you for all of your suggestions!