How to get a llvm.ptr to a LLVM type without size

I have defined a opaque struct type like:

auto structTy  = LLVM::LLVMStructType::getOpaque("external_struct_name", context);

I can create a Value from that type pretty easily:

Value myStruct = builder.create<LLVM::UndefOp>(loc, structTy);

However, I need both the struct itself (currently on myStruct) and a pointer to the struct. I think that the approach should be to use a LLVM::LoadOp to get the address of the struct. This is what I usally do in LLVM. So in MLIR, I tried:

auto structTy  = LLVM::LLVMStructType::getOpaque("external_struct_name", context);
auto ptr = LLVM::LLVMPointerType::get(structTy, 0);
Value myStructPtr = builder.create<LLVM::UndefOp>(loc, ptr);
// Now that I have a ptr to myStruct, get the struct value of it
Value myStruct = builder.create<LLVM::LoadOp>(loc, myStructPtr);

However, MLIR complains that the structure type that I want to load from does not have a size (which is true, is a opaque struct):

error: 'llvm.load' op operand #0 must be LLVM pointer to LLVM type with size, but got '!llvm.ptr<struct<"external_struct_name", opaque>>'

According to MLIR docs ('llvm' Dialect - MLIR), this is the expected behaviour.

So my question is: Is there a way to have the struct itself and a pointer to it in two different Values?

I don’t understand what you want to do. LLVM Language Reference (LLVM Language Reference Manual — LLVM 16.0.0git documentation) clearly specifies that

The argument to the load instruction specifies the memory address from which to load. The type specified must be a first class type of known size (i.e. not containing an opaque structural type).

so it shouldn’t be possible to load a value of an opaque struct type in LLVM IR and therefore in the LLVM dialect.

Alright, I didn’t know that.

Then, I don’t know why my LLVM implementation works. In a LLVM work I have:

StructType * handle = llvm::StructType::create(M->getContext(), struct_name);
PointerType * ptr = PointerType::get(handle, 0);
AllocaInst * new_alloca = new AllocaInst(ptr, 0, alloca_name, I);
LoadInst* load_str = new LoadInst(new_alloca->getType()->getPointerElementType(), new_alloca, load_name, I);

This works perfectly. My goal with the load is to get what the alloca points to (it points to the structure). In MLIR I tried the same approach:

auto structTy  = LLVM::LLVMStructType::getOpaque(struct_name, context);            // Same as llvm::StructType::create
auto ptr = LLVM::LLVMPointerType::get(structTy, 0);                                // Same as PointerType::get
Value cst0 = builder.create<LLVM::ConstantOp>(...);                                //
Value myStructPtr = builder.create<LLVM::AllocaOp>(loc, ptr, cst0);                // Same as AllocaInst
Value myStruct = builder.create<LLVM::LoadOp>(loc, myStructPtr);                   // Not exactly the same as LoadInst

but I got the error I mentioned. I cannot replicate the LoadInst exactly because in MLIR there is no getPointerElementType(). However, I can pass the type of what the pointer is pointing to:

Value myStruct = builder.create<LLVM::LoadOp>(loc, structTy, myStructPtr); 

but it does not work either.

I want to create a Value that represents the struct, but also a Value that represents a pointer to the same struct, as I did in the LLVM code example below.

You can’t alloca opaque structs either. LLVM Language Reference Manual — LLVM 16.0.0git documentation has:

type ’ may be any sized type

Generally, an opaque struct is a struct of which you know nothing. It’s unclear to me how you want to have a value of a type you don’t know.

LLVM API allows you to construct IR that uses opaque struct types because you may be able to specify the body of the struct type at some later point using StructType::setBody. It expects you to ultimately bring the IR to a valid state (So does MLIR, by the way). Have you tried running LLVM verifier on your IR? Unlike MLIR, it does not always run by default…

Alright, after your comment I reviewed my MLIR code and now I understand what is going on. Instead of doing an alloca of opaque structs (illegal), I have done an alloca of pointer of opaque structs, which (I hope) is not a illegal thing to do. Sorry for the confusion.

One last question @ftynse, do you know a way of creating a LLVM::LLVMStructType with its body set (not opaque) automatically from a C/C++ header file? I mean, right now I do:

ArrayRef<Type> strTypes({... a bunch of types ...});
auto strTy = LLVM::LLVMStructType::getIdentified(context, struct_name);
LogicalResult bodySet = strTy.setBody(strTypes, false);
assert(succeeded(bodySet) && "could not set the body of an identified struct");

Can I just get the LLVMStructType automatically instead of filling it with the types manually? I need this to declare the struct of a C library that I want to call from MLIR. I guess it is not a straightforward thing to do in MLIR (nor LLVM), but just wanted to make sure. Thanks!

You can use LLVMStructType::getNewIdentified to set the body immediately upon construction if you know the types.

Getting an equivalent of a C++ type automatically by parsing a header needs more than just MLIR or LLVM. You will need a C++ frontend (e.g., clang) that will do things like template instantiation and macro substitution, and either a backend that converts C++ types directly to the LLVM dialect or a translation from LLVM IR (clang can emit LLVM IR types) to the LLVM dialect.