How to get address of value stored in regsters

hello.
I’m trying to implement a function with an array argument. I’m stucked on how to get address of that argument.

llvm::ArrayType* array_type = llvm::ArrayType::get(llvm::Type::getInt32Ty(*TheContext), 4);
/*
 * int foo(int array[4]){
 *      return array[0];
 * }
 */
{
    llvm::FunctionType* fn_type = llvm::FunctionType::get(llvm::Type::getInt32Ty(*TheContext), std::vector<llvm::Type*>{array_type}, false);
    llvm::Function*     fn      = llvm::Function::Create(fn_type, llvm::Function::ExternalLinkage, "foo", TheModule.get());

    llvm::BasicBlock* entry_block = llvm::BasicBlock::Create(*TheContext, "entry", fn);
    Builder->SetInsertPoint(entry_block);

    // how to get address of array[0] ??
}
/*
 * int main(){
 *      int array[4]={1,2,3,4};
 *      foo(array);
 *      return 0;
 * }
 */
{
    llvm::FunctionType* fn_type = llvm::FunctionType::get(llvm::Type::getInt32Ty(*TheContext), std::vector<llvm::Type*>{}, false);
    llvm::Function*     fn      = llvm::Function::Create(fn_type, llvm::Function::ExternalLinkage, "main", TheModule.get());

    llvm::BasicBlock* entry_block = llvm::BasicBlock::Create(*TheContext, "entry", fn);
    Builder->SetInsertPoint(entry_block);

    llvm::AllocaInst* array = Builder->CreateAlloca(array_type, nullptr, "array");
    for (int i = 0; i < 4; i++) {
        llvm::Value* ptr_element = Builder->CreateConstGEP2_64(array_type, array, 0, i);
        Builder->CreateStore(llvm::ConstantInt::get(*TheContext, llvm::APInt(32, i + 1, true)), ptr_element);
    }

    llvm::Value* loaded_array = Builder->CreateLoad(array_type, array);

    llvm::Function*           fn_foo = TheModule->getFunction("foo");
    std::vector<llvm::Value*> args{
        loaded_array,
    };
    llvm::CallInst* result_v = Builder->CreateCall(fn_foo, args, "s");

    Builder->CreateRet(Builder->getInt32(0));
}

I have tried this instructions below.

  • GEP
    The second parameter of GEP is required to be a pointer. But the array is stored in register. I have to alloca/store to get address. I want to avoid this unnecessary memory access.
  • extractelement
    The first parameter is required to be a vector. So I think this instruction is not help.
  • extractvalue
    This instruction is almost the perfect choice. The only problem is it only receive constant index.

Is there an instruction that can get address of array element that stored in registers? Is it a bad idea to pass array itself (instead of passing array address) as an argument?

Please help.

If you pass an LLVM array directly to a function it won’t necessarily have an address, each of the elements will be passed individually according to the calling convention (so the first few will be passed in registers and the rest on the stack).

What C code that passes an array does is it actually passes a pointer to the first element of that array, so you could then use GEP on it and load an element.

If you want to press on with passing an actual array (and do think about this, it generally involves copying lots of data and means you have to have a separate version of each function for different array sizes) then your best bet is probably to alloca another variable at the top of the function and store the incoming argument there. Then you can use GEP etc on it.

Thanks for the reply.

What C code that passes an array does is it actually passes a pointer to the first element of that array, so you could then use GEP on it and load an element.

I just use C code to illustrate my purpose. Maybe a bad one.

then your best bet is probably to alloca another variable at the top of the function and store the incoming argument there. Then you can use GEP etc on it.

Yes, I want to pass the array value itself (not the pointer) to the callee and if the array size is small (all passed through registers), the array element is extract from registers directly ( instead of alloca and store and GEP and load) .
If I alloca memory and store registers to memory and use GEP to get address and then load value, it seems mem2reg will not transform this to register access.

define i32 @foo([4 x i32] %0) {
entry:
  %1 = alloca [4 x i32], align 4
  store [4 x i32] %0, [4 x i32]* %1, align 4
  %2 = getelementptr [4 x i32], [4 x i32]* %1, i64 0, i64 0
  %3 = load i32, i32* %2, align 4
  ret i32 %3
}

define i32 @main() {
entry:
  %array = alloca [4 x i32], align 4
  %0 = getelementptr [4 x i32], [4 x i32]* %array, i64 0, i64 0
  store i32 1, i32* %0, align 4
  %1 = getelementptr [4 x i32], [4 x i32]* %array, i64 0, i64 1
  store i32 2, i32* %1, align 4
  %2 = getelementptr [4 x i32], [4 x i32]* %array, i64 0, i64 2
  store i32 3, i32* %2, align 4
  %3 = getelementptr [4 x i32], [4 x i32]* %array, i64 0, i64 3
  store i32 4, i32* %3, align 4
  %4 = load [4 x i32], [4 x i32]* %array, align 4
  %s = call i32 @foo([4 x i32] %4)
  ret i32 0
}

Right, there is no really good way to represent that in IR without going through memory. It’d have to be a switch or something, which would also be pretty awful on actual CPUs (and ironically often be implemented via a jump-table in memory anyway).

After reading ir generated by Clang, I think I have find a way.

byval
This indicates that the pointer parameter should really be passed by value to the function. The attribute implies that a hidden copy of the pointee is made between the caller and the callee, so the callee is unable to modify the value in the caller.

According to this document, I just need to add byval attribute to the array argument of the function, LLVM will do the hidden copy work. Below is my test.

 // callee
 {
     llvm::FunctionType* fn_type = llvm::FunctionType::get(llvm::Type::getInt32Ty(*TheContext), std::vector<llvm::Type*>{array_type->getPointerTo()}, false);
     llvm::Function*     fn      = llvm::Function::Create(fn_type, llvm::Function::ExternalLinkage, "foo", TheModule.get());

     llvm::AttrBuilder attr_builder;
     attr_builder.addByValAttr(array_type);
     fn->getArg(0)->addAttrs(attr_builder);

     llvm::BasicBlock* entry_block = llvm::BasicBlock::Create(*TheContext, "entry", fn);
     Builder->SetInsertPoint(entry_block);

     llvm::Value* element_ptr = Builder->CreateConstGEP2_64(array_type, fn->getArg(0), 0, 0);
     Builder->CreateStore(Builder->getInt32(100), element_ptr);

     Builder->CreateRet(Builder->getInt32(0));
 }

// caller
{
    llvm::FunctionType* fn_type = llvm::FunctionType::get(llvm::Type::getInt32Ty(*TheContext), std::vector<llvm::Type*>{}, false);
    llvm::Function*     fn      = llvm::Function::Create(fn_type, llvm::Function::ExternalLinkage, "main", TheModule.get());

    llvm::BasicBlock* entry_block = llvm::BasicBlock::Create(*TheContext, "entry", fn);
    Builder->SetInsertPoint(entry_block);

    llvm::AllocaInst* array = Builder->CreateAlloca(array_type, nullptr, "array");
    for (int i = 0; i < 4; i++) {
        llvm::Value* ptr_element = Builder->CreateConstGEP2_64(array_type, array, 0, i);
        Builder->CreateStore(llvm::ConstantInt::get(*TheContext, llvm::APInt(32, i + 1, true)), ptr_element);
    }

    llvm::Function*           fn_foo = TheModule->getFunction("foo");
    std::vector<llvm::Value*> args{
        array,
    };

    llvm::CallInst* result_v = Builder->CreateCall(fn_foo, args, "s");

    llvm::Value* element_ptr   = Builder->CreateConstGEP2_64(array_type, array, 0, 0);
    llvm::Value* element_value = Builder->CreateLoad(llvm::Type::getInt32Ty(*TheContext), element_ptr);

    //Builder->CreateRet(Builder->getInt32(0));
    Builder->CreateRet(element_value);
}
define i32 @foo([4 x i32]* byval([4 x i32]) %0) {
entry:
  %1 = getelementptr [4 x i32], [4 x i32]* %0, i64 0, i64 0
  store i32 100, i32* %1, align 4
  ret i32 0
}

define i32 @main() {
entry:
  %array = alloca [4 x i32], align 4
  %0 = getelementptr [4 x i32], [4 x i32]* %array, i64 0, i64 0
  store i32 1, i32* %0, align 4
  %1 = getelementptr [4 x i32], [4 x i32]* %array, i64 0, i64 1
  store i32 2, i32* %1, align 4
  %2 = getelementptr [4 x i32], [4 x i32]* %array, i64 0, i64 2
  store i32 3, i32* %2, align 4
  %3 = getelementptr [4 x i32], [4 x i32]* %array, i64 0, i64 3
  store i32 4, i32* %3, align 4
  %s = call i32 @foo([4 x i32]* %array)
  %4 = getelementptr [4 x i32], [4 x i32]* %array, i64 0, i64 0
  %5 = load i32, i32* %4, align 4
  ret i32 %5
}
0000000000401110 <foo>:
  401110:       48 8d 44 24 08          lea    rax,[rsp+0x8]
  401115:       c7 00 64 00 00 00       mov    DWORD PTR [rax],0x64
  40111b:       31 c0                   xor    eax,eax
  40111d:       c3                      ret
  40111e:       66 90                   xchg   ax,ax

0000000000401120 <main>:
  401120:       48 83 ec 28             sub    rsp,0x28
  401124:       c7 44 24 18 01 00 00    mov    DWORD PTR [rsp+0x18],0x1
  40112b:       00
  40112c:       c7 44 24 1c 02 00 00    mov    DWORD PTR [rsp+0x1c],0x2
  401133:       00
  401134:       c7 44 24 20 03 00 00    mov    DWORD PTR [rsp+0x20],0x3
  40113b:       00
  40113c:       c7 44 24 24 04 00 00    mov    DWORD PTR [rsp+0x24],0x4
  401143:       00
  401144:       48 8d 44 24 18          lea    rax,[rsp+0x18]
  401149:       48 8b 08                mov    rcx,QWORD PTR [rax]
  40114c:       48 89 0c 24             mov    QWORD PTR [rsp],rcx
  401150:       48 8b 40 08             mov    rax,QWORD PTR [rax+0x8]
  401154:       48 89 44 24 08          mov    QWORD PTR [rsp+0x8],rax
  401159:       e8 b2 ff ff ff          call   401110 <foo>
  40115e:       8b 44 24 18             mov    eax,DWORD PTR [rsp+0x18]
  401162:       48 83 c4 28             add    rsp,0x28
  401166:       c3                      ret