Step 05 · Symbol lookup & calling main

auto sym = jit->lookup("main");
if (!sym) /* surface toString(sym.takeError()) */;
auto fnPtr = sym->toPtr<int(*)()>();
int  exitCode = fnPtr();

lookup triggers materialisation: parsing/optimising hasn't finished when addIRModule returns; the work happens when a symbol is asked for. This is the entry point to ORC's laziness.

Type-safe call

toPtr<Fn>() is a templated cast that returns a function pointer of the requested signature. Get the signature wrong and you'll see UB — since mlcc always produces define i32 @main(), we ask for int(*)().

Capturing stdout

Tests need to assert on what printf printed. We dup fd 1 to a temp file around the call, then slurp the file. POSIX-only, but plenty for cp-12. Production code would either:

  • register a custom puts/printf symbol that writes to a buffer, or
  • run the JIT in a subprocess and read its stdout.