effekt-lang / effekt

A research language with effect handlers and lightweight effect polymorphism

Home Page:https://effekt-lang.org

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Top-level constants in the LLVM backend

jiribenes opened this issue · comments

Motivation

The LLVM backend currently does not support top-level let bindings of any kind.
As a first step, it would be nice to at least support top-level bindings of literals that don't use any capabilities (what I'll call "top-level constants" here), like for example:

val myString = "Hello, world!"
// ^^^
// [error] Toplevel def and let bindings not yet supported: Let(myString,Literal(Hello, world!,Data(String,List())))

def main() = {
  println(myString)
}

Existing use in the standard library

We even use top-level constants in the standard library in the string module:

// ANSI escape codes
namespace ANSI {
val BLACK = "\u001b[30m"
val RED = "\u001b[31m"
val GREEN = "\u001b[32m"
val YELLOW = "\u001b[33m"
val BLUE = "\u001b[34m"
val MAGENTA = "\u001b[35m"
val CYAN = "\u001b[36m"
val WHITE = "\u001b[37m"
val BG_BLACK = "\u001b[40m"
val BG_RED = "\u001b[41m"
val BG_GREEN = "\u001b[42m"
val BG_YELLOW = "\u001b[43m"
val BG_BLUE = "\u001b[44m"
val BG_MAGENTA = "\u001b[45m"
val BG_CYAN = "\u001b[46m"
val BG_WHITE = "\u001b[47m"
val RESET = "\u001b[0m"
}

which are then used by the test module:

println(ANSI::RED ++ "- (FAIL) " ++ name ++ "\n " ++ msg ++ ANSI::RESET);

Unfortunately since the LLVM backend doesn't support these top-level constants, one may use neither ANSI escape codes nor, by transitivity, the test module. :(

Implementation details

Based on my Clang-based testing, I think it should be enough to do the following to declare a top-level Pos constant:

@topLevelPos = constant %struct.Pos { i64 42, ptr null }, align 8

The biggest problem might be that we'd need to add these top-level constants to the Machine representation as that's where the current error is being thrown from:

val transformedDefinitions = definitions.foldLeft(mainEntry) {
case (rest, lifted.Definition.Def(id, lifted.BlockLit(tparams, params, body))) =>
Def(Label(transform(id), params.map(transform)), transform(body), rest)
case (rest, d) =>
ErrorReporter.abort(s"Toplevel def and let bindings not yet supported: ${d}")
}
Program(declarations, transformedDefinitions)
}

(One could start by pattern-matching on something like (rest, lifted.Definition.Let(constantName, lit@Literal(_, _)))...)

Technically, we should also be able to instead inline all of these top-level constants wherever they appear, but that's quite ad-hoc.