eclipse-omr / omr

Eclipse OMR™ Cross platform components for building reliable, high performance language runtimes
http://www.eclipse.org/omr
Other
950 stars 397 forks source link

Write JitBuilder C API generator #3197

Open Leonardo2718 opened 6 years ago

Leonardo2718 commented 6 years ago

As a step towards addressing #2049, the following work needs to be completed:

daytonjallen commented 5 years ago

Hey, just to get the discussion started on what the C API implementation should look like, I've gone ahead and translated the Simple.cpp & Simple.hpp examples from the test suite to C as a proposal of how the API might look.

Simple.h

#ifndef SIMPLE_H
#define SIMPLE_H

#include "Jitbuilder.h"

typedef struct SimpleMethod {
    /* Struct embedding for inheritance? */
    MethodBuilder *methodBuilder;
} SimpleMethod;

SimpleMethod* SimpleMethod_init(TypeDictionary *);
bool SimpleMethod_buildIL(SimpleMethod *);

#endif SIMPLE_H

Simple.c

#include "Simple.h"

#include <stdio.h>
#include <stdbool.h>
#include <stdlib.h>

#define TOSTR(x) #x
#define LINETOSTR(x) TOSTR(x)

int main(int argc, char *argv[]) {
    puts("Step 1: initialize JIT");
    bool initialized = JB_initializeJit();
    if (!initialized) {
        fprintf(stderr, "FAIL: could not initialized JIT\n");
        exit(-1);
    }

    puts("Step 2: define type dictionary");
    TypeDictionary *types;
    puts("Step 3: compile method builder");
    SimpleMethod *method = SimpleMethod_init(types);

    void *entry = 0;
    int32_t rc = JB_compileMethodBuilder(method->methodBuilder, &entry);
    if (rc != 0) {
        fprintf(stderr, "FAIL: compilation error %d\n", rc);
        exit(-2);
    }

    puts("Step 4: invoke compiled code and print results\n");
    typedef int32_t (SimpleMethodFunction)(int32_t);
    SimpleMethodFunction *increment = (SimpleMethodFunction *) entry;

    int32_t v;
    v = 0;
    printf("increment(%d) == %d\n", v, increment(v));

    v = 1;
    printf("increment(%d) == %d\n", v, increment(v));

    v = 10;
    printf("increment(%d) == %d\n", v, increment(v));

    v = 15;
    printf("increment(%d) == %d\n", v, increment(v));

    puts("Step 5: shutdown JIT");
    JB_shutdownJit();
}

SimpleMethod* SimpleMethod_init(TypeDictionary *td) {
    SimpleMethod* sm = (SimpleMethod*)malloc(sizeof(SimpleMethod));
    sm->methodBuilder = MethodBuilder_init(td, (VirtualMachineState*) NULL);

    /* Emulate virtual methods using a function pointer? */
    sm->methodBuilder->ilBuilder->buildIL = SimpleMethod_buildIL;

    MB_DefineLine(sm->methodBuilder->, LINETOSTR(__LINE__));
    MB_DefineFile(sm->methodBuilder->, __FILE__);
    MB_DefineParameter(sm->methodBuilder->, "value", td->Int32);
    MB_DefineReturnType(sm->methodBuilder->, td->Int32);
}

MethodBuilder* MethodBuilder_init(TypeDictionary *td, VirtualMachineState *vms) {
    MethodBuilder* mb = (MethodBuilder *) malloc(sizeof(MethodBuilder));
    mb->ilBuilder = IlBuilder_init();
    return mb;
}

bool SimpleMethod_buildIL(SimpleMethod* simpleMethod) {
    puts("SimpleMethod::buildIL() running!");
    ILBuilder* ilBuilder = simpleMethod->methodBuilder->ilBuilder;
    IB_Return(ilBuilder,
            IB_Add(ilBuilder, 
                    IB_Load(ilBuilder,"value"),
                    IB_ConstInt32(ilBuilder, 1))
    );
    return true;
}
Leonardo2718 commented 5 years ago

@daytonallen I think this is headed in the right direction. A few thoughts:

@mstoodle any thoughts?

mstoodle commented 5 years ago

I don't think this part works for me:

    TypeDictionary *types;
    puts("Step 3: compile method builder");
    SimpleMethod *method = SimpleMethod_init(types);

I expected more of a TypeDictionary *types = TD_NewDictionary(); or something like that. Then I'm not sure you need a "SimpleMethod" struct in this scenario, as @Leonardo2718 pointed out. I think you'd just do MethodBuilder *sm = MB_NewMethod(types); or even MethodBuilder sm; MB_Initialize(&sm);

mstoodle commented 5 years ago

That MB_Initialize() call could even take the buildIL callback pointer.

Then the rest of the constructor can just be executed inline as a set of calls on mb (or collected together in an init() function as @daytonallen did).

mstoodle commented 5 years ago

@Leonardo2718 said "Instead, there should be an API function that takes care of this." on the setting of the buildIL callback pointer. It can be passed as a constructor argument, as I mentioned and primarily as a form of convenience. But it should also be changeable, and I agree that should definitely be recommended to be done via an API call rather than by reaching in to set the field directly.

mstoodle commented 5 years ago

Thanks for starting to push this one forward, @daytonallen and @Leonardo2718 . A working and flexible C API paves the way for other language bindings, and I'm very excited to see forward progress here!

Leonardo2718 commented 5 years ago

FYI: I've opened #3289, which does most of the refactoring I had planned in preparation for the C API generator work. I'm hoping to open a follow up PR with some initial work on the C generator very soon.

daytonjallen commented 5 years ago

Hey, sorry for the delayed response.

Leonardo2718 commented 5 years ago

@daytonallen

ILBuilder* ilBuilder = simpleMethod->super->super;
void setBuildILCallback(IlBuilder* self, bool (*buildIL)(void)) {
  static_cast<TR::IlBuilder *>(self->impl_)->setClientCallback_buildIL(static_cast<void*>(buildIL));
}
daytonjallen commented 5 years ago

Gotcha, thanks. Where can I find the API description that you're referencing?

Leonardo2718 commented 5 years ago

The API description is in the directory containing the C++ generator: omr/jitbuilder/apigen/jitbuilder.api.json.

daytonjallen commented 5 years ago

Hey, this is the second iteration with all the proposed changes thus far:

Simple.c

#include <stdio.h>
#include <stdbool.h>
#include <stdlib.h>

#include "Jitbuilder.h"

#define TOSTR(x) #x
#define LINETOSTR(x) TOSTR(x)

int main(int argc, char *argv[]) {
    puts("Step 1: initialize JIT");
    bool initialized = JBinitializeJit();
    if (!initialized) {
        fprintf(stderr, "FAIL: could not initialized JIT\n");
        exit(-1);
    }

    puts("Step 2: define type dictionary");
    TypeDictionary *types = TDInit();
    puts("Step 3: compile method builder");
    MethodBuilder *method = MBInit(types, (VirtualMachineState*) NULL, buildIL);

    MBDefineLine(method, LINETOSTR(__LINE__));
    MBDefineFile(method, __FILE__);
    MBDefineParameter(method, "value", types->Int32);
    MBDefineReturnType(method, types->Int32);

    void *entry = 0;
    int32_t rc = JBcompileMethodBuilder(method, &entry);
    if (rc != 0) {
        fprintf(stderr, "FAIL: compilation error %d\n", rc);
        exit(-2);
    }

    puts("Step 4: invoke compiled code and print results\n");
    typedef int32_t (SimpleMethodFunction)(int32_t);
    SimpleMethodFunction *increment = (SimpleMethodFunction *) entry;

    int32_t v;
    v = 0;
    printf("increment(%d) == %d\n", v, increment(v));

    v = 1;
    printf("increment(%d) == %d\n", v, increment(v));

    v = 10;
    printf("increment(%d) == %d\n", v, increment(v));

    v = 15;
    printf("increment(%d) == %d\n", v, increment(v));

    puts("Step 5: shutdown JIT");
    JBshutdownJit();
}

bool buildIL(MethodBuilder* mb) {
    puts("SimpleMethod::buildIL() running!");
    ILBuilder* ilBuilder = mb->super;
    IBReturn(ilBuilder,
              IBAdd(ilBuilder,
                     IBLoad(ilBuilder,"value"),
                     IBConstInt32(ilBuilder, 1))
    );
    return true;
}

The MBInit() would look something like

MethodBuilder* MBInit(TypeDictionary *td, VirtualMachineState *vms, bool (*buildIL)(void)) {
    MethodBuilder* mb = (MethodBuilder *) malloc(sizeof(MethodBuilder));
    mb->super = IBInit();
    if (buildIL) {
        ILBuilder* ilBuilder = mb->super;
        setBuildILCallback(ilBuilder, buildIL);
    }
    return mb;
}
daytonjallen commented 5 years ago

Hey @Leonardo2718, what would be the next step after we settle on an API design?

Leonardo2718 commented 5 years ago

@daytonallen

I'd say open a pull request adding the C Simple example to jitbuilder/release/c/samples. It will be easier to discuss details that way.