/mozart2-module-test

Test project to keep track of how to build a module for Mozart VM 2

Primary LanguageShell

This is a sample module to show how to write a separate module against the Mozart VM 2.0.

The following forms of the compiled Oz code are currently supported:

  1. The C++ code generated by the bootstrap compiler (this branch) at commit mozart/mozart2-bootcompiler@6bff6fd.

How it works

The sole purpose of the bootstrap compiler is to get the real Oz-based compiler running. Therefore, the modules provided will only be "just enough", and the module system will not have an extensible interface uncorrelated to the VM itself.

Nevertheless, the library is flexible enough to allow us to add an unrelated module without polluting the VM project or require much human intervention.

Writing a native module comes in 3 steps.

  1. Write the C++ header and source code to bridge the Mozart VM to the native code
  2. Generate the meta-data required by the bootstrap compiler
  3. Compile everything into the executable

C++

Modules in VM 2.0 is vastly different from the 1.4 foreign interface. Benefiting from C++11 and clang, most of the macros can be eliminated. The way to define a module is to create several classes like this in a header file:

#include <mozart.hh>

namespace some_namespace {

using mozart::builtins::Module;
using mozart::builtins::Builtin;
using mozart::builtins::In;
using mozart::builtins::Out;
using mozart::VM;               // <-- Note: this 'using' is required.

#ifndef MOZART_BUILTIN_GENERATOR
#include "somethingbuiltins.hh"
#endif

struct ModSomething : Module
{
    ModSomething() : Module("Something") {}

    struct SomeFunction : Builtin<SomeFunction>
    {
        SomeFunction() : Builtin("someFunction") {}

        void operator()(VM vm, In in_arg, Out out_arg);
    };

    struct AnotherFunction : mozart::builtins::Builtin<AnotherFunction>
    {
        AnotherFunction() : mozart::builtins::Builtin("anotherFunction") {}

        void operator()(mozart::VM vm, In in_arg, In in_arg_2);
    };
};

}

and something like this in the source file:

#include "something.hh"

namespace some_namespace {

void ModSomething::SomeFunction::operator()(VM vm, In in_arg, Out out_arg)
{
    // implement the function here.
}

void ModSomething::AnotherFunction::operator()(VM vm, In in_arg, In in_arg_2)
{
    // implement the function here.
}

#include "somethingbuiltins.cc"

}

  1. All new modules will inherit from mozart::builtins::Module.
  2. The default constructor should call the base class, with the name of the module provided as an argument.
  3. All functions will be implemented as function objects, inherited from mozart::builtins::Builtin with CRTP.
  4. Again, the default constructor of each function object calls the base class with its name.
  5. The real implementation is written in the operator(), as expected.
  6. We will also need to include some generated functions for use by the Mozart platform.

Generator

In 1.4, the mapping from atoms to functions is filled out manually in oz_init_module(). In 2.0, currently this task is done automatically with help of clang/LLVM. But this made compiling the module a 3-step process.

  1. Dump the AST of the header file.
  2. Parse the AST and generate the module information into a JSON file, which will be read by the bootstrap compiler.
  3. Compile the implementation for linking from Oz to C++ code.

The AST of the header file is dumped using clang with the command:

clang++ -std=c++11 -stdlib=libc++ \
        -I/path/to/mozart2-vm/vm/main \
        -emit-ast \
        -o something.astbi
        -DMOZART_BUILTIN_GENERATOR
        something.hh

This .astbi file used to generate metadata using Mozart VM 2.0's "generator":

mkdir something.out

/path/to/mozart2-vm/generator/main/generator \
    builtins \
        something.astbi \
        something.out/ \
    somethingbuiltins

The generated files are:

  • something.out/ModSomething-builtin.json — The JSON description of the module, to be read by the bootstrap compiler.
  • something.out/somethingbuiltins.cc and .hh — Some extra C++ source code for use in the VM.

Bootstrap compiler

The bootstrap compiler currently supports multiple built-in modules. The way we write the module means the the compiler will provide two Oz procedures:

{Something.someFunction InArg ?OutArg}
{Something.anotherFunction InArg InArg2}

As described in mozart2-bootcompiler's description, we first build the base environment, including our built-ins:

java -jar /path/to/bootcompiler.jar \
--baseenv \
-o Base.out.cc \
-h boostenv.hh \
    -h something.hh \
-m path/to/mozart2-vm/vm/main \
-m path/to/mozart2-vm/boostenv/main \
    -m something.out \
-b baseenv.out.txt \
path/to/mozart2-library/base/Base.oz \
path/to/mozart2-library/boot/BootBase.oz

Then compile the file executable as usual:

java -jar /path/to/bootcompiler.jar \
-o Example.out.cc \
-h boostenv.hh \
    -h something.hh \
-m path/to/mozart2-vm/vm/main \
-m path/to/mozart2-vm/boostenv/main \
    -m something.out \
-b baseenv.out.txt \
Example.oz

Important: you almost always need to compile the following .oz files with this method too:

  1. These modules are required to boot the program:
  • mozart2-vm/boostenv/lib/OS.oz
  • mozart2-library/init/Init.oz
  • mozart2-library/dp/URL.oz
  • mozart2-library/sys/Property.oz
  • mozart2-library/support/DefaultURL.oz
  1. These modules are required to let the program actually run:
  • mozart2-library/sys/System.oz

Finally we create the linker file:

java -jar /path/to/bootcompiler.jar \
--linker \
-o Example.linked.out.cc \
-h boostenv.hh \
-m path/to/mozart2-vm/vm/main \
-m path/to/mozart2-vm/boostenv/main \
-m something.out \
-b baseenv.out.txt \
Example.oz \
    /path/to/mozart2-vm/boostenv/lib/OS.oz \
    ......

We can see the pattern here, that you must repeat the six -h, -m and -b arguments.

After all the .cc files are generated, we could compile them into native code:

g++ -std=c++11 \
    -I/path/to/mozart2-vm/vm/main \
    -I/path/to/mozart2-vm/boostenv/main \
    -Isomething.out \
    -I. \
    Example.out.cc Example.linked.out.cc something.cc .... \
    /path/to/mozart2-vm/boostenv/main/libmozartvmboost.a \
    /path/to/mozart2-vm/vm/main/libmozartvm.a \
    -lboost_system -lboost_filesystem -lboost_thread -pthread \