Calling lowered MLIR function from C++

I tried to modify MLIR’s toy tutorial program so that instead of generating a main program that allocates and prints a tensor, the program generates a function that accepts a tensor and prints the values. The tensor has been mapped to a memref<1xf64>,which then is mapped to a data buffer pointer, data access pointer, offset, shape, and stride in LLVM IR.

I would expect this to result in the values being printed but instead the result is a EXC_BAD_ACCESS (code=EXC_I386_GPFLT) error on a vmovsd instruction. When I remove the print instruction but leave the load operation, the function compiles fine. When I replace the operand of the print statement with a constant, the program compiles fine and prints.

I suspect that my program is correct (it matches a c to llvm dump and the toy tutorial’s dump) and that the error is in the input to the execution engine. Does anyone know what circumstances result in that error or what input on the c++ side is expected given the llvm function signature? My llvm module and calling code is below:

define void @toy_print(double* %0, double* %1, i64 %2, i64 %3, i64 %4) !dbg !3 {
  %6 = insertvalue { double*, double*, i64, [1 x i64], [1 x i64] } undef, double* %0, 0, !dbg !7
  %7 = insertvalue { double*, double*, i64, [1 x i64], [1 x i64] } %6, double* %1, 1, !dbg !7
  %8 = insertvalue { double*, double*, i64, [1 x i64], [1 x i64] } %7, i64 %2, 2, !dbg !7
  %9 = insertvalue { double*, double*, i64, [1 x i64], [1 x i64] } %8, i64 %3, 3, 0, !dbg !7
  %10 = insertvalue { double*, double*, i64, [1 x i64], [1 x i64] } %9, i64 %4, 4, 0, !dbg !7
  br label %11, !dbg !7

11:                                               ; preds = %14, %5
  %12 = phi i64 [ 0, %5 ], [ %19, %14 ]
  %13 = icmp slt i64 %12, 1, !dbg !7
  br i1 %13, label %14, label %20, !dbg !7

14:                                               ; preds = %11
  %15 = extractvalue { double*, double*, i64, [1 x i64], [1 x i64] } %10, 1, !dbg !7
  %16 = getelementptr double, double* %15, i64 %12, !dbg !7
  %17 = load double, double* %16, align 8, !dbg !7
  %18 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([4 x i8], [4 x i8]* @frmt_spec, i64 0, i64 0), double %17), !dbg !7
  %19 = add i64 %12, 1, !dbg !7
  br label %11, !dbg !7

20:                                               ; preds = %11
  ret void, !dbg !7
}
define void @_mlir_ciface_toy_print({ double*, double*, i64, [1 x i64], [1 x i64] }* %0) !dbg !9 {
  %2 = load { double*, double*, i64, [1 x i64], [1 x i64] }, { double*, double*, i64, [1 x i64], [1 x i64] }* %0, align 8, !dbg !10
  %3 = extractvalue { double*, double*, i64, [1 x i64], [1 x i64] } %2, 0, !dbg !10
  %4 = extractvalue { double*, double*, i64, [1 x i64], [1 x i64] } %2, 1, !dbg !10
  %5 = extractvalue { double*, double*, i64, [1 x i64], [1 x i64] } %2, 2, !dbg !10
  %6 = extractvalue { double*, double*, i64, [1 x i64], [1 x i64] } %2, 3, 0, !dbg !10
  %7 = extractvalue { double*, double*, i64, [1 x i64], [1 x i64] } %2, 4, 0, !dbg !10
  call void @foo(double* %3, double* %4, i64 %5, i64 %6, i64 %7), !dbg !10
  ret void, !dbg !10
}

The corresponding C++ snippet:

double* test = new double[6];
for(int i=0;i<6;i++){
    test[i] = (double)(i+0.5);
}
// EXC_BAD_ACCESS (code=EXC_I386_GPFLT)
auto invocationResult = engine->invoke(function_name, test, test, 0, 5, 1);
if (invocationResult) {
    llvm::errs() << "JIT invocation failed\n";
}

passing arguments requires pointers to the arguments. If the parameters are pointers, the arguments must be pointers to pointers.