Skip to content

jonathanvdc/flame-llvm

Repository files navigation

flame-llvm: an LLVM back-end for Flame

Build Status

flame-llvm is a tool that compiles Flame IR to LLVM IR using LLVMSharp. Since ecsc can compile C# source code to Flame IR, flame-llvm can (indirectly) turn C# programs into fast and small standalone binaries.

This repository also contains an implementation of a small but growing set of functionality from the .NET base class library (BCL) to offer some measure of source-level compatibility with C# code written for traditional CLR implementations.

The case for flame-llvm

Running managed languages in a virtual machine like .NET or Mono works great for a wide class of applications. But for some programs, it just doesn't make sense.

For example, suppose that you wanted to re-implement the GNU coreutils. It's been done in Go, Rust and even Haskell. But creating a competitive C# version of the coreutils would be rather tricky because most of the coreutils are:

  • short-lived applications (so their start-up time matters),
  • small (e.g., the echo command is about 34 KiB) and
  • standalone (they don't require a separate virtual machine to be installed before you can run them).

Mono and .NET Core can create standalone applications by bundling a VM with the actual program, but that's rather clumsy because the VM tends to be way bigger than the actual program. And using a VM adds to a program's start-up time regardless of whether you bundle it with the program or ship it separately. Also, bundling VMs with applications is just a hacky way to create an ersatz standalone program.

Bundled application

Enter flame-llvm, which you can use to compile C# code directly to a native executable. Programs compiled with flame-llvm run directly on the metal, and they're generally fast and small, too.

Here's a head-to-head comparison of flame-llvm and Mono's mkbundle tool on the stdlib-echo test (a simplified version of echo).

# Follow the build instructions in the `Compiling a C# program`
# section of the README to compile `stdlib-echo.cs` to `a.out`
# using `flame-llvm`.

# Use `mkbundle` to create a standalone version of `stdlib-echo.cs`.
$ mcs stdlib-echo.cs
$ mkbundle --simple stdlib-echo.exe -o stdlib-echo-mono

$ time ./a.out howdy
howdy

real 0m0.004s
user 0m0.004s
sys  0m0.000s

# a.out is less than 14 KiB
$ wc -c a.out
13752 a.out

$ time ./stdlib-echo-mono howdy
howdy

real 0m0.041s
user 0m0.028s
sys  0m0.008s

# stdlib-echo-mono is over 7 MiB
$ wc -c stdlib-echo-mono
7827636 stdlib-echo-mono

As you can see, compiling stdlib-echo.cs with flame-llvm results in an executable that starts up ten times faster and is five hundred times smaller than the executable produced by mkbundle.

Compiling flame-llvm

To use flame-llvm, you'll need to have the following installed:

  • basic command-line tools like bash, make, cp, etc (in your $PATH),
  • a .NET framework implementation (for example, Mono),
  • clang,
  • the Boehm-Demers-Weiser GC (Debian package: libgc-dev),
  • the libc++abi library (or equivalent) and
  • LLVM 4.0's libLLVM.(so|dll|dylib), whichever is appropriate for your platform. It doesn't really matter where you/an installer/your package manager put it; we'll copy it to the output directory.

For Debian-based distributions, the following command should install all dependencies:

sudo apt install mono-devel llvm-4.0-dev clang-4.0 libc++abi-dev libgc-dev

Clone flame-llvm, including its submodules, like so

git clone --recursive https://github.com/jonathanvdc/flame-llvm
cd flame-llvm

Now configure the LLVM dependency and build flame-llvm.

./build-libs.sh /path/to/libLLVM.so
make

You probably also want to compile the standard library.

make stdlib

Alternatively, you can use the MSBuild solution file (flame-llvm.sln) to build flame-llvm and then manually copy libLLVM.(so|dll|dylib) to the output directory, but I recommend using the Makefile.

Compiling a C# program

Compiling a program with flame-llvm consists of multiple steps so it's kind of tricky to get right. Assuming you've built the standard library, the following should work. You probably want to wrap all of this into a neat little makefile.

# First, compile the source code to Flame IR.
ecsc \
  program.cs \
  -platform ir -environment llvm -Wno-build \
  -rt-libs /path/to/flame-llvm-repo/stdlib/bin/flo/corlib.flo \
  -o program.flo

# Compile the Flame IR to LLVM IR, linking in the core library.
flame-llvm \
  program.flo \
  /path/to/flame-llvm-repo/stdlib/flo/corlib.flo \
  -platform llvm -Wno-build \
  -rt-libs /path/to/flame-llvm-repo/runtime/bin/flo/runtime.flo \
  -o program.ll

# Compile the LLVM IR to an executable. Link in the runtime and libgc.
clang \
  program.ll \
  /path/to/flame-llvm-repo/runtime/bin/native/runtime.o \
  -lgc -lc++abi -pthreads -Wno-override-module \
  -o a.out

Feature status

flame-llvm is not feature-complete yet, but it is mature enough to compile basic programs. Here's a feature list.

  • integer arithmetic
  • floating-point arithmetic
  • control flow (while, for, if, break, continue, etc)
  • direct function calls
  • extern functions
  • struct values
  • class values
  • enum values
  • arrays
  • support for strings
  • conservative garbage collection (Boehm-Demers-Weiser GC)
  • inheritance
  • dynamic casts, is, as
  • virtual calls
  • interface calls
  • generics
  • boxing/unboxing
  • static constructors
  • exception handling (throw, try, catch, finally)—Itanium ABI (Linux, Mac OS X) only for now
  • indirect function calls (delegates)
  • finalizers
  • reflection
  • precise garbage collection

Contributing

Your contribution to flame-llvm would be most welcome! If you want to help out but have questions, head over to the CONTRIBUTING.md file or simply drop me an issue.

About

LLVM front-end that consumes Flame IR; can be used to compile C# to native code.

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages