ExecutionEngine usage

I’m having hard times in finding out how to properly use the ExecutionEngine.

I have a function with the following signature:

llvm.func @main(%arg0: i32, %arg1: i32) -> !llvm.struct<(ptr<i32>, i64, array<2 x i64>)>

Thus, its wrapper has signature:

llvm.func @_mlir_ciface_main(%arg0: !llvm.ptr<struct<(ptr<i32>, i64, array<2 x i64>)>>, %arg1: i32, %arg2: i32)

Right now, this is the way I use the ExecutionEngine:

auto maybeEngine = ExecutionEngine::create(module, nullptr, optPipeline, llvm::None, allLibraries);
// ... error checking ...
auto engine =  std::move(maybeEngine.get());

int arg0 = 1;
int arg1 = 2;

struct {
  int* ptr;
  long dim;
  long v[2];
} result;

auto* resultPtr = &result;

engine->invoke("main", ExecutionEngine::Result(resultPtr), arg0, arg1);

Given the fact that the struct is originally a return value, and not a pointer, I would expect to just pass result wrapped in the ExecutionEngine::Result class, instead of the wrapped resultPtr. Instead, I had to inspect the generated wrapper to understand that the struct was moved to the arguments list as a pointer, thus needing to pass resultPtr. Is there any way to avoid the need of resultPtr? I also don’t understand why the struct is moved to the arguments list at first, and secondly why it becomes a pointer.