Backend

You don’t need to implement an interpreter for Gazprea. You only need to implement a MLIR code generator that outputs LLVM IR.

Memory Management

It is important that you are able to automatically free and allocate memory for vectors and matrices when they enter and exit scope. You may use malloc and free for these purposes. This may be done in either your runtime or directly within MLIR.

Below is an example of how to use malloc and free within MLIR using the LLVM dialect:

module {
  llvm.func @malloc(i32) -> !llvm.ptr<i8>
  llvm.func @free(!llvm.ptr<i8>)
  llvm.func @main() -> i32 {
    %0 = llvm.mlir.constant(128 : i32) : i32
    %1 = llvm.call @malloc(%0) : (i32) -> !llvm.ptr<i8>
    llvm.call @free(%1) : (!llvm.ptr<i8>) -> ()
    %c0_i32 = llvm.mlir.constant(0 : i32) : i32
    llvm.return %c0_i32 : i32
  }
}

It is important that the code generated by your compiler has no memory leaks, and that all memory is freed as it leaves scope.

Runtime Libraries

If you make a runtime library, the runtime library must be implemented in a runtime directory (runtime). Beware that in C++ there is additional name mangling that occurs to allow class functions. Thus, we recommend that all runtime functions should be written in C and not in C++. There is a Makefile in the runtime folder designed to turn all *.c and *.h pairs into part of the unified runtime library libruntime.a. An example of how to make a runtime function is provided bellow.

functions.c

#include "functions.h"

uint64_t factorial(uint64_t n) {
    uint64_t fact = 1;

    while (n > 0) {
        fact *= n;
        n--;
    }

    return fact;
}

functions.h

#pragma once

#include <stdint.h>

uint64_t factorial(uint64_t n);

If your compiler was compiling the following input

3! + (2 + 7)!

Here is how to call the function in the LLVM dialect of MLIR:

MLIR src

 module {
   // This makes the function available for calling
   llvm.func @factorial(i64) -> i64

   llvm.func @main() -> i32 {
     // Calls factorial with the constant 3 as an argument
     %0 = llvm.mlir.constant(3 : i64) : i64
     %1 = llvm.call @factorial(%0) : (i64) -> (i64)

     // Adds 2 and 7 together
     %2 = llvm.mlir.constant(2 : i64) : i64
     %3 = llvm.mlir.constant(7 : i64) : i64
     %4 = llvm.add %2, %3 : i64

     // Calls factorial with the result of 2+7
     %5 = llvm.call @factorial(%4) : (i64) -> (i64)

     // Adds the result of 3! with (2+7)!
     %6 = llvm.add %1, %5 : i64

     // Done, return 0
     %c0_i32 = llvm.mlir.constant(0 : i32) : i32
     llvm.return %c0_i32 : i32
  }
}