Skip to content

Commit

Permalink
Add a "release" build target to jni.llvm (ballerina-platform#1241)
Browse files Browse the repository at this point in the history
* Fix incremental builds

Cleanup build script

Cleaned resource config

Update the README

Add Ballerina config for Aarch64

Refactor Makefile

Add dist targets for jar and native compilers

* Fix jar only build not including runtime

* Implement cross compilation

* Allow running the script from any directory

* Fix incremental builds

* Change gzip to xz

* Fix target

* Change the cross compiler to gcc

* Fix file name

* Fix aarch64 builds

Some of the cases where we were using GEPs don't work in aarch64 (most likely due to clang packing values differently). Workaround this by adding inlinable functions to access values

* Fix build scripts to use only sh

* Fix build script search depth

* Fix runner for OSX

* Fix naming

* Revert "Fix aarch64 builds"

This reverts commit f594e86.

* Change compilation scripts to use bash

* Fix bash scripts
  • Loading branch information
heshanpadmasiri authored Nov 23, 2023
1 parent 18d42be commit 9c70a7c
Show file tree
Hide file tree
Showing 13 changed files with 287 additions and 75 deletions.
24 changes: 24 additions & 0 deletions llvm_jni/BallerinaDarwinAArch64.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
[package]
org = "wso2"
name = "nballerina"
version = "0.1.0"
[build-options]
observabilityIncluded = false

[[platform.java11.dependency]]
path = "target/platform-libs/javacpp-1.5.9.jar"

[[platform.java11.dependency]]
path = "target/platform-libs/javacpp-platform-1.5.9.jar"

[[platform.java11.dependency]]
path = "target/platform-libs/javacpp-1.5.9-macosx-arm64.jar"

[[platform.java11.dependency]]
path = "target/platform-libs/llvm-16.0.4-1.5.9.jar"

[[platform.java11.dependency]]
path = "target/platform-libs/llvm-platform-16.0.4-1.5.9.jar"

[[platform.java11.dependency]]
path = "target/platform-libs/llvm-16.0.4-1.5.9-macosx-arm64.jar"
24 changes: 24 additions & 0 deletions llvm_jni/BallerinaLinuxAArch64.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
[package]
org = "wso2"
name = "nballerina"
version = "0.1.0"
[build-options]
observabilityIncluded = false

[[platform.java11.dependency]]
path = "target/platform-libs/javacpp-1.5.9.jar"

[[platform.java11.dependency]]
path = "target/platform-libs/javacpp-platform-1.5.9.jar"

[[platform.java11.dependency]]
path = "target/platform-libs/javacpp-1.5.9-linux-arm64.jar"

[[platform.java11.dependency]]
path = "target/platform-libs/llvm-16.0.4-1.5.9.jar"

[[platform.java11.dependency]]
path = "target/platform-libs/llvm-platform-16.0.4-1.5.9.jar"

[[platform.java11.dependency]]
path = "target/platform-libs/llvm-16.0.4-1.5.9-linux-arm64.jar"
File renamed without changes.
110 changes: 83 additions & 27 deletions llvm_jni/Makefile
Original file line number Diff line number Diff line change
@@ -1,12 +1,45 @@
LLVM_SUFFIX?=-16
COMBINED_SRC_DIR=../testbuild
COMPILER_DIR=../compiler
SUBSET_DOC=../docs/subset15.md
COMPILER_SRC=$(wildcard $(COMPILER_DIR)/*.bal) $(wildcard $(COMPILER_DIR)/modules/*/*.bal)
PRINT_LLVM_SRC=$(wildcard $(COMPILER_DIR)/modules/print.llvm/*.bal) $(COMPILER_DIR)/output.bal
COMBINED_SRC=$(subst $(COMPILER_DIR),$(COMBINED_SRC_DIR),$(filter-out $(PRINT_LLVM_SRC), $(COMPILER_SRC)))
JNI_SRC=$(wildcard ./modules/jni.llvm/*.bal)
INLINE_RUNTIME=../runtime/balrt_inline.bc
RUNTIME=../runtime/balrt.a
DEPENDECIES_DIR=$(COMBINED_SRC_DIR)/target/platform-libs
COMPILER_JAR=$(COMBINED_SRC_DIR)/target/nballerina.jar
NATIVE_EXE=$(COMBINED_SRC_DIR)/target/nballerina
BAL_CONFIG=Ballerina.toml
BASE_JAR=$(COMBINED_SRC_DIR)/target/bin/nballerina_base.jar
COMPILER_JAR=$(COMBINED_SRC_DIR)/target/bin/nballerina.jar
COMPILER_NATIVE=$(COMBINED_SRC_DIR)/target/bin/nballerina
AARCH64_RUNTIME=$(COMBINED_SRC_DIR)/target/bin/balrt_aarch64.a
AARCH64_INLINE_RUNTIME=$(COMBINED_SRC_DIR)/target/bin/balrt_inline_aarch64.bc
BAL_CONFIG?=Ballerina.toml
GENERATED_CONFIGS=jni_config.json reflection_config.json
REFLECTION_CONFIG=../../../llvm_jni/reflection_config.json
JNI_CONFIG=../../../llvm_jni/jni_config.json,../../../llvm_jni/jni_workaround.json
RESOURCE_CONFIG=../../../llvm_jni/resource_config.json
NATIVE_IMAGE_FLAGS?=--no-fallback -H:+StaticExecutableWithDynamicLibC -H:MaxDuplicationFactor=75 \
-H:ResourceConfigurationFiles=$(RESOURCE_CONFIG) -H:ReflectionConfigurationFiles=$(REFLECTION_CONFIG) \
-H:JNIConfigurationFiles=$(JNI_CONFIG)
MVN?=mvn
VERSION=0.1
SYSTEM?=linux
ARCH?=amd64
DIST_NAME=nballerina.$(VERSION)-$(SYSTEM)-$(ARCH)
DIST_DIR=../build/$(DIST_NAME)
DIST_AR=../build/$(DIST_NAME).tar.xz
ifeq ($(ARCH), amd64)
DIST_INLINE_RUNTIME=$(AARCH64_INLINE_RUNTIME) $(INLINE_RUNTIME)
DIST_RUNTIME=$(RUNTIME) $(AARCH64_RUNTIME)
DIST_COMPILER=$(COMPILER_NATIVE)
DIST_BASE_DIR=./dist/native
else
DIST_INLINE_RUNTIME= $(INLINE_RUNTIME)
DIST_RUNTIME=$(RUNTIME)
DIST_COMPILER=$(COMPILER_JAR)
DIST_BASE_DIR=./dist/jvm
endif

# Finished copying jar files of dependencies
DEPENDENCIES_STAMP=$(COMBINED_SRC_DIR)/dependencies.stamp
Expand All @@ -17,54 +50,77 @@ JNI_STAMP=$(COMBINED_SRC_DIR)/jni.stamp
# Added inline runtime to the nballerina.jar file
RUNTIME_STAMP=$(COMBINED_SRC_DIR)/runtime.stamp

all: $(COMPILER_NATIVE)

$(GENERATED_CONFIGS): jniConfigGen.py $(JNI_SRC)
python3 jniConfigGen.py $(JNI_SRC)

$(DEPENDENCIES_STAMP):pom.xml $(COMPILER_STAMP) $(JNI_STAMP)
-rm -rf $(DEPENDECIES_DIR)
mvn -U compile
$(MVN) -U compile
mkdir -p $(DEPENDECIES_DIR)
find ~/.m2/repository/org/bytedeco -name "*.jar" -exec cp "{}" $(DEPENDECIES_DIR) \;
@touch $@

$(COMPILER_STAMP): $(COMPILER_SRC)
mkdir -p $(COMBINED_SRC_DIR)
cp -r $(COMPILER_DIR)/* $(COMBINED_SRC_DIR)
find $(COMBINED_SRC_DIR) -type f -name "*.bal" -exec sed -i 's/print.llvm/jni.llvm/g' {} \;
rm -rf $(COMBINED_SRC_DIR)/modules/print.llvm
rm $(COMBINED_SRC_DIR)/output.bal
$(COMBINED_SRC_DIR)/%.bal: $(COMPILER_DIR)/%.bal
mkdir -p $(@D)
sed 's/print.llvm/jni.llvm/g' $< > $@

$(COMPILER_STAMP): $(COMBINED_SRC)
@touch $@

$(AARCH64_RUNTIME) $(AARCH64_INLINE_RUNTIME):
$(MAKE) -C ../runtime clean
$(MAKE) -C ../runtime TARGET="-target aarch64-linux-gnu" dist
mv $(RUNTIME) $(AARCH64_RUNTIME)
mv $(INLINE_RUNTIME) $(AARCH64_INLINE_RUNTIME)
$(MAKE) -C ../runtime clean

$(JNI_STAMP): $(COMPILER_STAMP) $(JNI_SRC)
cp -r ./modules/jni.llvm $(COMBINED_SRC_DIR)/modules
cp output.bal $(COMBINED_SRC_DIR)
cp $(BAL_CONFIG) $(COMBINED_SRC_DIR)/Ballerina.toml
@touch $@

$(INLINE_RUNTIME):
$(MAKE) -C ../runtime all
$(DIST_DIR): $(DIST_COMPILER) $(DIST_RUNTIME) $(RUNTIME_STAMP)
rm -rf $(DIST_DIR)
mkdir -p $(DIST_DIR)
cp $(DIST_RUNTIME) $(DIST_DIR)
cp $(DIST_COMPILER) $(DIST_DIR)/compiler$(suffix $(DIST_COMPILER))
cp $(DIST_BASE_DIR)/* $(DIST_DIR)
cp $(SUBSET_DOC) $(DIST_DIR)/subset.md
chmod +x $(DIST_DIR)/nballerina.sh

$(RUNTIME_STAMP): $(INLINE_RUNTIME) $(COMPILER_JAR)
$(RUNTIME_STAMP): $(DIST_INLINE_RUNTIME) $(BASE_JAR)
cp $(BASE_JAR) $(COMPILER_JAR)
cp $(INLINE_RUNTIME) $(COMBINED_SRC_DIR)/target/bin
cd $(COMBINED_SRC_DIR)/target/bin; jar uf nballerina.jar balrt_inline.bc
rm $(COMBINED_SRC_DIR)/target/bin/balrt_inline.bc
cd $(COMBINED_SRC_DIR)/target/bin; jar uf nballerina.jar *.bc
cd $(COMBINED_SRC_DIR)/target/bin; zip -d nballerina.jar ./META-INF/native-image/*
@touch $@

$(NATIVE_EXE): $(COMPILER_JAR) $(RUNTIME_STAMP)
cd $(COMBINED_SRC_DIR)/target/bin; zip -d nballerina.jar ./META-INF/native-image/*
find ./modules/jni.llvm -maxdepth 1 -name "*.bal"| xargs python3 jniConfigGen.py
cd $(COMBINED_SRC_DIR)/target/bin; native-image --no-fallback -H:MaxDuplicationFactor=100 -H:ResourceConfigurationFiles=../../../llvm_jni/resource_config.json -H:ReflectionConfigurationFiles=../../../llvm_jni/reflection_config.json -H:JNIConfigurationFiles=../../../llvm_jni/jni_config.json,../../../llvm_jni/jni_workaround.json -jar nballerina.jar
$(COMPILER_NATIVE): $(COMPILER_JAR) $(RUNTIME_STAMP) $(GENERATED_CONFIGS)
cd $(COMBINED_SRC_DIR)/target/bin; native-image $(NATIVE_IMAGE_FLAGS) -jar nballerina.jar

$(COMPILER_JAR): $(DEPENDENCIES_STAMP) $(COMPILER_STAMP) $(JNI_STAMP)
cd $(COMBINED_SRC_DIR); bal build --offline
$(COMPILER_JAR): $(BASE_JAR) $(RUNTIME_STAMP)

test: $(DEPENDENCIES_STAMP) $(COMPILER_STAMP) $(JNI_STAMP)
cd $(COMBINED_SRC_DIR); bal test --offline --tests nballerina:testCompileVPO
$(BASE_JAR): $(DEPENDENCIES_STAMP) $(COMPILER_STAMP) $(JNI_STAMP)
cd $(COMBINED_SRC_DIR); bal build --offline
mv $(COMPILER_JAR) $(BASE_JAR)

build: $(COMPILER_JAR)
$(RUNTIME) $(INLINE_RUNTIME):
$(MAKE) -C ../runtime dist

buildWithRuntime: $(RUNTIME_STAMP)
%.tar.xz: %
tar -cJf $@ -C $(@D) $(<F)

buildNative: $(NATIVE_EXE)
test: $(DEPENDENCIES_STAMP) $(COMPILER_STAMP) $(JNI_STAMP)
cd $(COMBINED_SRC_DIR); bal test --offline --tests nballerina:testCompileVPO

.PHONY: clean test build buildWithRuntime buildNative
dist: $(DIST_AR)

clean:
-rm -rf $(COMBINED_SRC_DIR)

.PHONY: all clean dist test
# We are building multiple runtimes (for each cross compilation target) which needs to be done sequentially
.NOTPARALLEL: $(RUNTIME_STAMP)
48 changes: 20 additions & 28 deletions llvm_jni/README.md
Original file line number Diff line number Diff line change
@@ -1,33 +1,25 @@
# Usage
## Build the jni.llvm
1. Run `./gradlew copyDependencies` to download and copy dependencies to the local `target/platform-libs` directory. (This needs to be done only once for the first build)
2. Run `bal build` to build the project

## Build nBallerina complier using JNI
Run `./gradlew testBuild`
## 1 Build nBallerina complier using JNI
Run `make`.

This will create new ballerina package in `testbuild` directory the root directory where
1. Change all the imports of `print.llvm` to `jni.llvm`
2. Remove the `print.llvm` module and insert `jni.llvm` module instead
3. Run `bal build` on the new ballerina package
This will create new ballerina package in `testbuild` directory by,
1. Replace `print.llvm` with `jni.llvm`.
2. Run `bal build` on the new package.
3. Add the inline runtime (`balrt_inline.bc`) into the resulting `jar` file.
4. Generate a native executable using [GraalVM](https://www.graalvm.org/).

To clean this build either delete the `testbuild` directory or run `./gradlew cleanTestBuild`.
Resulting compiler includes the necessary parts of the LLVM tool-chain (using [Bytedeco's LLVM presets](https://github.com/bytedeco/javacpp-presets/tree/master/llvm) as well as the inline runtime (`balrt_inline.bc`) and can compile Ballerina source files into object files directly.

## Compare llvm ir generated against print.llvm
Run `./gradlew compareTestCases`
# Sample code
```
import llvm_jni.llvm;
import ballerina/io;
public function main() {
llvm:Context context = new;
llvm:Builder builder = context.createBuilder();
llvm:Module m = context.createModule();
llvm:Function mainFunction = m.addFunction("main", {returnType: "void", paramTypes: ["i64", "i1"]});
llvm:BasicBlock initBlock = mainFunction.appendBasicBlock();
builder.positionAtEnd(initBlock);
builder.ret(llvm:constInt("i64",0));
io:println(m.toString());
checkpanic m.toFile("test.ll");
}
```
### 1.1 Pruning unwanted LLVM dependencies
When building the native executable as an intermediate step we crate a Java version of the compiler that can run in multiple operating systems. For this we include LLVM tool-chain for each of those operating systems. However this is not needed when building a single native executable targeting a single operating system/architecture combination. To prune unnecessary dependencies you can pass in a `Ballerina.toml` file with only the dependencies needed for your operating system. For example in a Linux AMD64 machine we can use `make BAL_CONFIG=BallerinaLinuxAMD64.toml`.

### 1.2 Creating distribution tar file
Run `make dist`

This will create the release artifacts in `build` directory. There are two possible versions
1. If you use `ARCH=amd64` (this is the default) it will generate a native executable with support for cross compiling to AArch64, along with AMD64 and AArch64 versions of the runtime.
2. Otherwise it will generate a `jar` file containing the compiler as well as runtime in the host systems architecture.

## 2.Testing
In order to test the compiler against the test suite run `make test LINK_FILE_EXTENSION=.o COMPILER=<Path to compiler>` in `test` directory.
12 changes: 12 additions & 0 deletions llvm_jni/dist/jvm/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# nBallerina:0.1
Compiler for the [Ballerina language](https://ballerina.io/) that can generate native executables.

## Requirements
+ Make sure you have Java 17 or later and `java` command is working.
+ Compiler uses the operating system's C compiler (`cc`) for linking.

## Usage
Run `nballerina.sh <Path to bal source file>` to compile. Resulting executable (along with build artifacts) will be in `./build` directory

## Language restrictions
Currently supported subset of the language is defined in `subset.md` file.
28 changes: 28 additions & 0 deletions llvm_jni/dist/jvm/nballerina.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
#!/bin/sh
set -e
if [ $# -ne 1 ]; then
echo "Error: expected $0 <source file>"
exit 1
fi

if [ "$1" = "--help" ] || [ "$1" = "-h" ]; then
echo "Usage: $0 <source file>"
exit 0
fi

scriptDir=$(realpath $(dirname "$0"))
src="$1"
buildDir="$(pwd)/build"
runtime="$scriptDir/balrt.a"
mkdir -p "$buildDir"

java -jar "$scriptDir/./compiler.jar" --outDir "$buildDir" "$src"

srcName=$(basename "$src" .bal)
cd "$buildDir"
objects=()
for f in "$srcName"*.o; do
objects+=("$buildDir/$f")
done

cc -O2 -o "$srcName" "$buildDir/$srcName"*.o -lm "$runtime"
18 changes: 18 additions & 0 deletions llvm_jni/dist/native/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# nBallerina:0.1
Compiler for the [Ballerina language](https://ballerina.io/) that can generate native executables.

## Requirements
+ Compiler uses the operating system's C compiler (`cc`) for linking.

## Usage
Run `nballerina.sh <Path to bal source file>` to compile. Resulting executable (along with build artifacts) will be in `./build` directory

### Cross compiling
Only supported target is `aarch64-linux-gnu`. In order to cross compile first install GCC cross compiler for aarch64.
```
sudo apt-get install gcc-aarch64-linux-gnu
```
Then run `nballerina.sh <Path to bal source file> --target aarch64`

## Language restrictions
Currently supported subset of the language is defined in `subset.md` file.
47 changes: 47 additions & 0 deletions llvm_jni/dist/native/nballerina.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
#!/bin/sh
set -e

print_usage_and_exit() {
echo "Usage: $0 <source file> [--target aarch64]"
exit "$1"
}

if [ $# -lt 1 ]; then
print_usage_and_exit 1
fi

if [ "$1" = "--help" ] || [ "$1" = "-h" ]; then
print_usage_and_exit 0
fi

scriptDir=$(realpath $(dirname "$0"))
src="$1"
buildDir="$(pwd)/build"
rm -rf "$buildDir"
mkdir -p "$buildDir"

c_compiler=cc
runtime="$scriptDir/balrt.a"
if [ $# -gt 1 ]; then
if [ "$2" = "--target" ]; then
if [ $# -lt 3 ]; then
print_usage_and_exit 1
fi
if [ "$3" = "aarch64" ]; then
"$scriptDir/./compiler" --outDir "$buildDir" --target aarch64-unknown-linux-gnu "$src"
c_compiler=aarch64-linux-gnu-gcc
runtime="$scriptDir/balrt_aarch64.a"
else
echo "Error: unsupported target $3"
exit 1
fi
else
print_usage_and_exit 1
fi
else
"$scriptDir"/./compiler --outDir "$buildDir" "$src"
fi

srcName=$(basename "$src" .bal)

"$c_compiler" -O2 -static -o "$srcName" "$buildDir/$srcName"*.o -lm "$runtime"
9 changes: 2 additions & 7 deletions llvm_jni/jniConfigGen.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,8 @@
"org.bytedeco.llvm.LLVM.LLVMOrcCLookupSetElement",
"org.bytedeco.llvm.LLVM.LLVMOrcCLookupSetElement",
"org.bytedeco.llvm.LLVM.LLVMOrcCSymbolsList",
"org.bytedeco.llvm.LLVM.LLVMOrcCSymbolAliasMapEntry"
"org.bytedeco.llvm.LLVM.LLVMOrcCSymbolAliasMapEntry",
"org.bytedeco.javacpp.Pointer$NativeDeallocator"
]


Expand Down Expand Up @@ -75,17 +76,11 @@ def get_default_reflection_config_with_name(self, name):
return {
"name": name,
"queryAllPublicConstructors": True,
"queryAllDeclaredConstructors": True,
"queryAllPublicMethods": True,
"queryAllDeclaredMethods": True,
"allPublicConstructors": True,
"allDeclaredConstructors": True,
"allPublicMethods": True,
"allDeclaredMethods": True,
"allPublicFields": True,
"allDeclaredFields": True,
"allPublicClasses": True,
"allDeclaredClasses": True
}


Expand Down
Loading

0 comments on commit 9c70a7c

Please sign in to comment.