Skip to content

JIT with mulle clang

mulle-kybernetik-tv edited this page Sep 25, 2018 · 12 revisions

JIT with mulle-clang is possible with mulle-clang-7 and up

JIT compilation should be easy, if you have the proper mulle-objc-runtime.h header accessible. Your JIT code should import it, which it usually does by virtue of including Foundation.h, MulleObjC.h or some such header.

The resulting compiled object will contain a function __load_mulle_objc that is an __attribute__(( constructor)) function, meaning that it should execute as soon as loaded.

It is important that your JIT setup calls the "static constructor" __load_mulle_objc, to get classes, categories, strings, selectors etc. added to the runtime.

Configure llvm for mulle-objc

There are multiple JIT options available in llvm. Make sure the JIT is able to execute static constructor functions. This is how to do it fairly lowlevel:

static int Execute(std::unique_ptr<llvm::Module> Mod, char *const *envp) 
{
  llvm::Module &M = *Mod;
  std::string Error;

  llvm::InitializeNativeTarget();
  llvm::InitializeNativeTargetAsmPrinter();

  std::unique_ptr<llvm::ExecutionEngine> EE(
      createExecutionEngine(std::move(Mod), &Error));
  if (!EE) {
    llvm::errs() << "unable to make execution engine: " << Error << "\n";
    return 255;
  }

  EE->finalizeObject();
  EE->runStaticConstructorsDestructors( false); # the IMPORTANT part

  llvm::Function *EntryFn = M.getFunction("main");
  if( EntryFn) 
  {
    // pass in something to main
    std::vector<std::string> Args;
    Args.push_back(M.getModuleIdentifier());    
    return EE->runFunctionAsMain(EntryFn, Args, envp);
  }
  return( 0);
}

JIT example source code (for -O0 only) without mulle-objc-runtime.h

Adapt the version numbers to your actual runtime. See the <mulle-objc-runtime/mulle-objc-version.h> header for current values:

#define MULLE_OBJC_RUNTIME_VERSION_MAJOR  0  
#define MULLE_OBJC_RUNTIME_VERSION_MINOR  14 
#define MULLE_OBJC_RUNTIME_VERSION_PATCH  0  

#define MULLE_OBJC_RUNTIME_LOAD_VERSION   13


extern int  printf( const char *, ...);

@interface Foo 
@end

@implementation Foo

+ (void) print  
{
   printf( "VfL Bochum 1848\n");
}
@end


// this function is optional for demo purposes
// it is not required to add the classes to the
// runtime 

int   main( void)
{
   [Foo print];
   return( 1848);
}

Modify JIT example for compilation with optimization

Copy/paste the contents of <mulle-objc-runtime/mulle-objc-jit.inc> in front of your source code.

Memo: lldb JIT compile without mulle-objc-runtime.h for lldb

The lldb debugger needs to do some funky JIT stuff. With the mulle-objc-runtime.h not readily available, it will add a top level variable __mulle_objc_objccompilerinfo like so:

static const struct mulle_clang_objccompilerinfo
{
  unsigned int   load_version;
  unsigned int   runtime_version;
} __mulle_objc_objccompilerinfo =
{
  13, // load version must match!
      // check with: mulle-clang --version
  0   // 0 to not emit __load_mulle_objc 
      // otherwise provide the current runtime version
      // as: ((0 << 20) | (13 << 8) | 1) for 0.13.1
};