llvm-rs-cc: Compiler for Renderscript language

Introduction

llvm-rs-cc compiles a program in the Renderscript language to generate the following files:

Note that although Renderscript is C99-like, we enhance it with several distinct, effective features for Android programming. We will use some examples to illustrate these features.

llvm-rs-cc is run on the host and performs many aggressive optimizations. As a result, libbcc on the device can be lightweight and focus on machine-dependent code generation for some input bitcode.

llvm-rs-cc is a driver on top of libslang. The architecture of libslang and libbcc is depicted in the following figure:

libslang   libbcc
    |   \   |
    |    \  |
 clang     llvm

Usage

Example Command

First:

$ cd <Android_Root_Directory>

Using frameworks/base/tests/RenderScriptTests/Fountain as a simple app in both Java and Renderscript, we can find the following command line in the build log:

$ out/host/linux-x86/bin/llvm-rs-cc \
  -o out/target/common/obj/APPS/Fountain_intermediates/src/renderscript/res/raw \
  -p out/target/common/obj/APPS/Fountain_intermediates/src/renderscript/src \
  -d out/target/common/obj/APPS/Fountain_intermediates/src/renderscript \
  -a out/target/common/obj/APPS/Fountain_intermediates/src/RenderScript.stamp \
  -MD \
  -I frameworks/base/libs/rs/script_api/include \
  -I external/clang/lib/Headers \
  frameworks/base/libs/rs/java/Fountain/src/com/android/fountain/fountain.rscript

This command will generate:

The Script*.java files above will be documented below.

Example Program: fountain.rscript

fountain.rscript is in the Renderscript language, which is based on the standard C99. However, llvm-rs-cc goes beyond "clang -std=c99" and provides the following important features:

1. Pragma

2. Basic Reflection: Export Variables and Functions

llvm-rs-cc automatically exports the "externalizable and defined" functions and variables to Android's Java side. That is, scripts are accessible from Java.

For instance, for:

int foo = 0;

In ScriptC_fountain.java, llvm-rs-cc will reflect the following methods:

void set_foo(int v)...

int get_foo()...

This access takes the form of generated classes which provide access to the functions and global variables within a script. In summary, global variables and functions within a script that are not declared static will generate get, set, or invoke methods. This provides a way to set the data within a script and call its functions.

Take the addParticles function in fountain.rscript as an example:

void addParticles(int rate, float x, float y, int index, bool newColor) {
  ...
}

llvm-rs-cc will genearte ScriptC_fountain.java as follows:

void invoke_addParticles(int rate, float x, float y,
                         int index, bool newColor) {
  ...
}

3. Export User-Defined Structs

In fountain.rscript, we have:

typedef struct __attribute__((packed, aligned(4))) Point {
  float2 delta;
  float2 position;
  uchar4 color;
} Point_t;

Point_t *point;

llvm-rs-cc generates one ScriptField*.java file for each user-defined struct. In this case, llvm-rs-cc will reflect two files, ScriptC_fountain.java and ScriptField_Point.java.

Note that when the type of an exportable variable is a structure, Renderscript developers should avoid using anonymous structs. This is because llvm-rs-cc uses the struct name to identify the file, instead of the typedef name.

For the generated Java files, using ScriptC_fountain.java as an example we also have:

void bind_point(ScriptField_Point v)

This binds your object with the allocated memory.

You can bind the struct(e.g., Point), using the setter and getter methods in ScriptField_Point.java.

After binding, you can access the object with this method:

ScriptField_Point get_point()

In ScriptField_Point_s.java:

...
// Copying the Item, which is the object that stores every
// fields of struct, to the *index*\-th entry of byte array.
//
// In general, this method would not be invoked directly
// but is used to implement the setter.
void copyToArray(Item i, int index)

// The setter of Item array,
// index: the index of the Item array
// copyNow: If true, it will be copied to the *index*\-th entry
// of byte array.
void set(Item i, int index, boolean copyNow)

// The getter of Item array, which gets the *index*-th element
// of byte array.
Item get(int index)

set_delta(int index, Float2 v, boolean copyNow)

// The following is the individual setters and getters of
// each field of a struct.
public void set_delta(int index, Float2 v, boolean copyNow)
public void set_position(int index, Float2 v, boolean copyNow)
public void set_color(int index, Short4 v, boolean copyNow)
public Float2 get_delta(int index)
public Float2 get_position(int index)
public Short4 get_color(int index)

// Copying all Item array to byte array (i.e., memory allocation).
void copyAll()
...

4. Summary of the Java Reflection above

This section summarizes the high-level design of Renderscript's reflection.

5. Vector Types

Vector types such as float2, float4, and uint4 are included to support vector processing in environments where the processors provide vector instructions.

On non-vector systems the same code will continue to run but without the performance advantage. Function overloading is also supported. This allows the runtime to support vector version of the basic math routines without the need for special naming. For instance,