Skip to content

Commit

Permalink
Add example project which generates a dynamic library using gccjit
Browse files Browse the repository at this point in the history
  • Loading branch information
junlarsen committed Mar 1, 2021
1 parent 896a3c2 commit e760c1f
Show file tree
Hide file tree
Showing 14 changed files with 496 additions and 4 deletions.
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
.idea
.gradle
build
*.so
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@

- Add example project which generates a dynamic library using gccjit
- Adjust generated sources path to match other JavaCPP presets
- Add experimental build for Linux ppc64le ([pull #5](https://github.com/bytedeco/gcc/pull/5))
- Add environment for running aarch64, ppc64le, armhf builds ([pull #6](https://github.com/bytedeco/gcc/pull/6))
Expand Down
135 changes: 134 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,5 +31,138 @@ from the rest of the JavaCPP Presets CI envs. This is a temporary solution and
primarily a proof-of-concept. Please open an issue/discussion if you're in need
of these architectures or if an architecture you need is not available.

## Sample Usage

This is an example showing how to build and generate code via libgccjit and
outputting it into a dynamic library.
[/samples/code-generation/](samples/code-generation)

> Sample showing how to call JIT-generated functions is work-in-progress.
We can use Gradle to install the required dependencies and native binaries
built by JavaCPP.

The `build.gradle` build file

```groovy
plugins {
id("application")
}
repositories {
mavenLocal()
mavenCentral()
maven { url 'https://oss.sonatype.org/content/repositories/snapshots/' }
}
dependencies {
implementation("org.bytedeco:gcc-platform:10.2.0-1.5.5-SNAPSHOT")
}
application {
mainClass = 'HelloWorld'
}
```

The `DylibGenerator.java` source file

```java
import org.bytedeco.gcc.gccjit.*;
import org.bytedeco.javacpp.*;

import static org.bytedeco.gcc.global.gccjit.*;

public class DylibGenerator {
public static void main(String[] args) {
gcc_jit_context ctxt = gcc_jit_context_acquire();
gcc_jit_context_set_bool_option(ctxt, GCC_JIT_BOOL_OPTION_DUMP_GENERATED_CODE, 1);

create_code(ctxt);

gcc_jit_context_compile_to_file(ctxt, GCC_JIT_OUTPUT_KIND_DYNAMIC_LIBRARY, "libhello.so");
gcc_jit_context_release(ctxt);
}

public static void create_code(gcc_jit_context ctxt) {
gcc_jit_type void_type = gcc_jit_context_get_type(ctxt, GCC_JIT_TYPE_VOID);
gcc_jit_type const_char_ptr_type = gcc_jit_context_get_type(ctxt, GCC_JIT_TYPE_CONST_CHAR_PTR);
gcc_jit_type int_type = gcc_jit_context_get_type(ctxt, GCC_JIT_TYPE_INT);
gcc_jit_param param_name = gcc_jit_context_new_param(ctxt, null, const_char_ptr_type, "name");
gcc_jit_param[] func_params = { param_name };

PointerPointer<gcc_jit_param> func_params_ptr = new PointerPointer<>(func_params);
BytePointer func_name = new BytePointer("greet");
gcc_jit_function greet_func = gcc_jit_context_new_function(ctxt, null, GCC_JIT_FUNCTION_EXPORTED, void_type,
func_name, func_params.length, func_params_ptr, 0);

gcc_jit_param param_format = gcc_jit_context_new_param(ctxt, null, const_char_ptr_type, "format");
gcc_jit_param[] printf_params = { param_format };

PointerPointer<gcc_jit_param> printf_params_ptr = new PointerPointer<>(printf_params);
BytePointer printf_name = new BytePointer("printf");
gcc_jit_function printf_func = gcc_jit_context_new_function(ctxt, null, GCC_JIT_FUNCTION_IMPORTED, int_type,
printf_name, printf_params.length, printf_params_ptr, 0);

gcc_jit_block block = gcc_jit_function_new_block(greet_func, "entry");
BytePointer format = new BytePointer("hello %s\\n");
gcc_jit_rvalue format_rvalue = gcc_jit_context_new_rvalue_from_ptr(ctxt, const_char_ptr_type, format);
gcc_jit_rvalue call = gcc_jit_context_new_call(ctxt, null, printf_func, 1, format_rvalue);
gcc_jit_block_add_eval(block, null, call);

gcc_jit_block_end_with_void_return(block, null);

func_name.deallocate();
func_params_ptr.deallocate();
printf_name.deallocate();
printf_params_ptr.deallocate();
format.deallocate();
}
}
```

> The below explanation assumes a Linux environment. I tested this on Linux
> x86_64
The above program should output something similar to the following to the
terminal window on x86_64. Assembler output for other architectures will
obviously be different.

```asm
.file "fake.c"
.text
.globl greet
.type greet, @function
greet:
.LFB0:
.cfi_startproc
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
subq $16, %rsp
movq %rdi, -8(%rbp)
.L2:
movabsq $139992918400400, %rdi
call printf@PLT
leave
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE0:
.size greet, .-greet
.ident "GCC: (GNU) 10.2.0"
.section .note.GNU-stack,"",@progbits
```

The `$139992918400400` operand to the movabsq instruction is expected as
it refers to our `"hello %s\n"` string we created.

If we look in the directory we invoked the program in we should have a
`libhello.so` file which is dynamically linkable. Looking inside the shared
library file with `nm libhello.so | grep "greet"` we can see that the greet
function is indeed there.

[javacpp-presets]: https://github.com/bytedeco/javacpp-presets#readme
[libgccjit]: https://gcc.gnu.org/wiki/JIT
[libgccjit]: https://gcc.gnu.org/wiki/JIT

3 changes: 2 additions & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,15 @@ group = "org.bytedeco"
version = "10.2.0-$javacppVersion"

repositories {
mavenLocal()
mavenCentral()
maven { url "https://oss.sonatype.org/content/repositories/snapshots/" }
}

dependencies {
api "org.bytedeco:javacpp:$javacppVersion"
javacppPlatform "org.bytedeco:javacpp-platform:$javacppVersion"
javacppPlatform "org.bytedeco:gcc:$version:linux-x86_64"
javacppPlatform "org.bytedeco:gcc:$version:linux-ppc64le"

testRuntimeOnly "org.bytedeco:javacpp:$javacppVersion:$javacppPlatform"
testImplementation "junit:junit:4.13.1"
Expand Down
4 changes: 2 additions & 2 deletions build.sh
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ case $PLATFORM in
--target=powerpc64le-linux-gnu \
--prefix=$GCC_INSTALL_PREFIX \
--enable-checking=release \
--enable-languages=jit \
--enable-languages=jit,c,c++ \
--enable-host-shared \
--disable-bootstrap \
--disable-multilib \
Expand All @@ -49,7 +49,7 @@ case $PLATFORM in
../gcc-$GCC_VERSION/configure \
--prefix=$GCC_INSTALL_PREFIX \
--enable-checking=release \
--enable-languages=jit \
--enable-languages=jit,c,c++ \
--enable-host-shared \
--disable-bootstrap \
--disable-multilib \
Expand Down
Binary file added gradle/wrapper/gradle-wrapper.jar
Binary file not shown.
5 changes: 5 additions & 0 deletions gradle/wrapper/gradle-wrapper.properties
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-6.7-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
17 changes: 17 additions & 0 deletions samples/code-generation/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
plugins {
id("application")
}

repositories {
mavenLocal()
mavenCentral()
maven { url 'https://oss.sonatype.org/content/repositories/snapshots/' }
}

dependencies {
implementation("org.bytedeco:gcc-platform:10.2.0-1.5.5-SNAPSHOT")
}

application {
mainClass = 'DylibGenerator'
}
Binary file not shown.
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-6.7-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
Loading

0 comments on commit e760c1f

Please sign in to comment.