Skip to content

Commit

Permalink
Add brief READMEs and some documentation
Browse files Browse the repository at this point in the history
Decided to finally publish the repository, so some guidance
about usage was needed. Most importantly, the warning not to use this in
production!
  • Loading branch information
bensku committed Jul 1, 2024
1 parent dc0d80b commit 195dd97
Show file tree
Hide file tree
Showing 4 changed files with 133 additions and 0 deletions.
7 changes: 7 additions & 0 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
Copyright 2022- bensku

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
39 changes: 39 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# code4jvm
code4jvm is a high-level JVM bytecode generation library for writing
compiler backends. It allows generating Java-like code without worrying
about low-level features, while still supporting JVM features that cannot
be expressed in Java.

The core code4jvm library consists of:
* Automatic stack management and local variable slot tracking
* Stack map frame generation (with control flow tracing)
* An extensible expression system that allows generation of custom bytecode
* Simple control flow (basic blocks, jumps)
* Java-like type conversions (complex casts, boxing)
* Exception handling (including try-finally block support)

On top of this, some higher-level features are included:
* Structured control flow (conditional blocks, loops)
* Method calls, including use of constructors
* Arithmetic and bitwise operations
* String concatenation
* Enum classes

Under the hood, JVM bytecode is generated using the low-level
[ASM](https://asm.ow2.io/) library.

## Usage
As of now, the only use case worth of mentioning is
[lua4jvm](lua4jvm/README.md), a work-in-progress Lua 5.4 implementation for JVM.

## Status
This library is work-in-progress. It is being worked on in author's spare time,
and there are no guarantees about when bug fixes or new features will be
available. Additionally, code4jvm is not yet published on Maven central.
Naturally, there are also zero guarantees about API stability.

As you might notice, the documentation is also quite sparse. There are
Javadocs, but beyond them you'd have to learn by example.

In short: don't use this in production. If you just want to hack around,
feel free to open issues (or even pull requests!).
37 changes: 37 additions & 0 deletions lua4jvm/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
# lua4jvm
lua4jvm is a work-in-progress JIT compiler for Lua 5.4 built on top
of JVM. Originally, it started as an university course project, but most of
*that* code has been rewritten at least once.

In its current form, you shouldn't use lua4jvm. While usually reliable, it
lacks the Lua standard library implementation; instead, the development has
focused on core VM features. Rest of the work is arguably easier; the author
has just been busy for a while.

## Architecture
The lua4jvm lacks formal documentation. However, below is a brief description
of its architecture.

### Frontend
Lua is parsed with a custom Antlr 4 grammar. From there, it is translated to
an internal IR format that the backend consumes. There is nothing too special
about this; initially, the project included a custom parser, but quite soon I
realized that I wanted to focus on the backend.

### Backend
Lua is a dynamically typed language with extensive runtime metaprogramming
facilities (metatables). Both of these characteristics make compiling it
into performant code rather difficult. This is especially true when the
compilation target has been primarily built for statically-typed languages;
on JVM, optimizations such as NaN tagging cannot be done.

To tackle this issue, lua4jvm generally compiles code only immediately before
it would be executed. Function calls generate their targets on first call
based on the real types at call sites; and should the types change, the target
will just get compiled again. The same system is also used for Lua's many
operators, since they can be overridden by metatables. The generated
specializations are cached between all call sites.

This approach also allows usage of 'static' analysis at runtime. Since the
function compiler effectively knows types of its arguments and upvalues, it
can for example use primitive types for numerical code.
50 changes: 50 additions & 0 deletions src/main/java/fi/benjami/code4jvm/block/Block.java
Original file line number Diff line number Diff line change
Expand Up @@ -31,14 +31,36 @@
import fi.benjami.code4jvm.statement.Jump;
import fi.benjami.code4jvm.statement.Return;
import fi.benjami.code4jvm.statement.Throw;
import fi.benjami.code4jvm.structure.IfBlock;
import fi.benjami.code4jvm.structure.LoopBlock;
import fi.benjami.code4jvm.typedef.ClassDef;

/**
* A basic block of code. Blocks are lists of statements and nested blocks
* that can be compiled as {@link Method methods}. Blocks can be entered by
* falling through from blocks above them in same method or by
* {@link Jump jumping} directly to them.
*
* <p>For structured control flow, it is recommended to use
* {@link IfBlock} and {@link LoopBlock}
*
*/
public class Block implements CompileHook.Carrier {

/**
* Creates a new, unnamed block.
* @return A new block.
*/
public static Block create() {
return create("unnamed");
}

/**
* Creates a new block with a debug name.
* @param debugName Name of the block to show in code4jvm's debug
* printouts. This is not included in the generated bytecode!
* @return A new block.
*/
public static Block create(String debugName) {
return new Block(new ArrayList<>(), new Scope(), debugName);
}
Expand Down Expand Up @@ -74,6 +96,10 @@ private Block(ArrayList<Node> nodes, Scope scope, String debugName) {
this.endFrame = new Frame();
}

/**
* Appends a statement to this block.
* @param stmt The statement.
*/
public void add(Statement stmt) {
if (stmt instanceof Return ret) {
stmt.emitVoid(this);
Expand All @@ -90,6 +116,12 @@ public void add(Statement stmt) {
}
}

/**
* Appends an expression to this block and returns the value that it will
* produce.
* @param expr Expression.
* @return Value that the expression produces.
*/
public Value add(Expression expr) {
if (expr instanceof Bytecode bc) {
scope.checkInputs(bc.inputs(), (bc.flags() & Bytecode.EXPLICIT_LOAD) == 0);
Expand All @@ -104,6 +136,13 @@ public Value add(Expression expr) {
}
}

/**
* Appends an expression to this block and assigns the value it will
* produce to the given variable.
* @param output Output variable.
* @param expr Expression.
* @return The same variable that was given.
*/
public Variable add(Variable output, Expression expr) {
nodes.add(new VarMarkerNode(true, (LocalVar) output));
var value = add(expr);
Expand All @@ -112,6 +151,13 @@ public Variable add(Variable output, Expression expr) {
return output;
}

/**
* Appends an expression to this block and stores the value it will produce
* to a variable with given name.
* @param outputName Name for the variable to be created.
* @param expr Expression.
* @return The output variable that this method created.
*/
public Variable add(String outputName, Expression expr) {
// Insert the marker without local variable, because we need it before
// initializer of the local variable, but it needs the type returned by
Expand All @@ -126,6 +172,10 @@ public Variable add(String outputName, Expression expr) {
return output;
}

/**
* Appends a nested block to this block.
* @param block A nested block.
*/
public void add(Block block) {
if (block.parent != null) {
throw new IllegalArgumentException("block cannot be added twice; try copy() instead");
Expand Down

0 comments on commit 195dd97

Please sign in to comment.