Calling function pointer

I’m exploring the idea of implementing JIT compilation for my language buzz.

I’m first trying to compile this minimal use case:

extern fun print(str value) > void;

fun main([str] args) > void {
    print("hello world");
}

The extern function is available in a dynamic library and is written in zig. It simply casts the pointer back to the buzz representation of a string (ObjString) and prints its content:

const std = @import("std");
const api = @import("../../src/lib/buzz_api.zig");

export fn print(obj_string_addr: usize) void {
    const obj_string = @intToPtr(*api.ObjString, obj_string_addr);

    var len: usize = undefined;
    const string = obj_string.bz_objStringToString(&len);
    const slice = if (string) |ustring| ustring[0..len] else "";

    std.io.getStdOut().writer().print("{s}\n", .{slice}) catch unreachable;
}

Now here’s what IR i have so far:

; ModuleID = 'tests/llvm/basic.buzz'
source_filename = "tests/llvm/basic.buzz"

@print = external global ptr

define void @"tests/llvm/basic.buzz"() {
"tests/llvm/basic.buzz.block":
  ; 4398868384 is the value of the ptr to the `print` function
  store ptr inttoptr (i64 4398868384 to ptr), ptr @print, align 8
  %0 = call i64 @main(ptr inttoptr (i64 4396651264 to ptr))
  ret void
}

define void @main(ptr %0) {
main.block:
  %1 = load ptr, ptr @print, align 8
  ; 4395893696 is the value of the ptr to the buzz representation of the string "hello world"
  call void %1(ptr inttoptr (i64 4395893696 to ptr))
  ret void
}

As you can see I’m passing any buzz value that is not number/boolean/null or void as a pointer so I can use the same data from LLVM IR and from buzz’s VM.

Seems ok to me but i’m getting a bus error. I’m guessing it occurs when trying to call @print since the error is Bus error at address 0x1063167a0 and 1063167a0 == 4398868384 which the print function pointer value.

What am I missing?

I think it’s likely a mismatch between how Zig is exporing print and how you’re trying to import it. In all other language pairs I know of you’d declare print as a normal function in the IR:

declare void @print(ptr)
[...]
  call void @print(ptr inttoptr(...))

In some cases a GOT load would be inserted to actually calculate the address, or the linker would do similar tricks. But that’s abstracted away in the LLVM IR and you just tell it you want the symbol known as print.

It’s also worth getting familiar with running the code through a debugger. You ought to be able to get more precise information about just what’s gone wrong than just a bus error at 0x1063167a0. For example just what instruction sequence was running at the time.

There’s buzz between zig and llvm:

  • buzz imports the function from the shared library and wraps it in a buzz value so it can be passed around in buzz code
  • at runtime that buzz value is called

That’s why i’m trying to call a function pointer rather than declaring a function. Because i’m not importing the function in zig but rather loading a pointer to it.

Not sure i’m making sense.

As for debugging, I don’t have much to go on. I compiled LLVM with -DCMAKE_BUILD_TYPE=RelWithDebInfo but it doesn’t seem to help me once the stack is in LLVM code

The issue was that the @print was not initialized.