Skip to content

Commit

Permalink
feat: ryujin, a better llvm library & dsl
Browse files Browse the repository at this point in the history
  • Loading branch information
SrGaabriel committed Sep 6, 2024
1 parent a78fd91 commit aec92c9
Show file tree
Hide file tree
Showing 13 changed files with 369 additions and 0 deletions.
16 changes: 16 additions & 0 deletions ryujin/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
plugins {
kotlin("multiplatform")
}

repositories {
mavenCentral()
}

kotlin {
jvm()
mingwX64()
linuxX64()
iosArm64()
macosX64()
js().browser()
}
27 changes: 27 additions & 0 deletions ryujin/src/commonMain/kotlin/DragonModule.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package me.gabriel.ryujin

import me.gabriel.ryujin.function.DragonFunction
import me.gabriel.ryujin.struct.Dependency

// Yes, I will be using the dragon prefix.
// Blame camelCase/PascalCase for being so shitty. (LLVMModule... seriously?)
interface DragonModule {
val functions: MutableSet<DragonFunction>
val dependencies: MutableSet<Dependency>

fun addDependency(dependency: Dependency) {
dependencies.add(dependency)
}

fun addDependencies(vararg dependencies: Dependency) {
this.dependencies.addAll(dependencies)
}

fun removeDependency(dependency: Dependency) {
dependencies.remove(dependency)
}

fun removeDependencies(vararg dependencies: Dependency) {
this.dependencies.removeAll(dependencies)
}
}
13 changes: 13 additions & 0 deletions ryujin/src/commonMain/kotlin/dsl/FunctionScopeDsl.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package me.gabriel.ryujin.dsl

import me.gabriel.ryujin.function.DragonFunction
import me.gabriel.ryujin.statement.DragonStatement

class FunctionScopeDsl(
val module: ModuleScopeDsl,
val function: DragonFunction
) {
fun statement(statement: DragonStatement) {
function.statements.add(statement)
}
}
30 changes: 30 additions & 0 deletions ryujin/src/commonMain/kotlin/dsl/ModuleScopeDsl.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package me.gabriel.ryujin.dsl

import me.gabriel.ryujin.DragonModule
import me.gabriel.ryujin.function.DragonFunction
import me.gabriel.ryujin.struct.Dependency
import me.gabriel.ryujin.struct.DragonType

class ModuleScopeDsl: DragonModule {
override val functions: MutableSet<DragonFunction> = mutableSetOf()
override val dependencies: MutableSet<Dependency> = mutableSetOf()

fun function(
name: String,
parameters: Map<String, DragonType>,
returnType: DragonType,
block: FunctionScopeDsl.() -> Unit
) {
val function = DragonFunction(
module = this,
name = name,
parameters = parameters,
returnType = returnType
)
FunctionScopeDsl(
module = this,
function = function
).apply(block)
}
}

14 changes: 14 additions & 0 deletions ryujin/src/commonMain/kotlin/function/DragonFunction.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package me.gabriel.ryujin.function

import me.gabriel.ryujin.DragonModule
import me.gabriel.ryujin.statement.DragonStatement
import me.gabriel.ryujin.struct.DragonType

class DragonFunction(
val module: DragonModule,
val name: String,
val parameters: Map<String, DragonType>,
val returnType: DragonType
) {
val statements = mutableListOf<DragonStatement>()
}
16 changes: 16 additions & 0 deletions ryujin/src/commonMain/kotlin/statement/DragonStatement.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package me.gabriel.ryujin.statement

import me.gabriel.ryujin.struct.DragonType
import me.gabriel.ryujin.struct.Value

interface DragonStatement {
val memoryDependencies: Set<Value>

fun isValid(): Boolean = true

fun llvm(): String
}

interface TypedDragonStatement : DragonStatement {
val type: DragonType
}
63 changes: 63 additions & 0 deletions ryujin/src/commonMain/kotlin/statement/Pointers.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
package me.gabriel.ryujin.statement

import me.gabriel.ryujin.struct.DragonType
import me.gabriel.ryujin.struct.Memory
import me.gabriel.ryujin.struct.Value
import me.gabriel.ryujin.struct.descendOneLevel

class StoreStatement(
val value: Value,
val target: Memory
): DragonStatement {
override val memoryDependencies: Set<Value> = setOf(value, target)

override fun llvm(): String =
"store ${value.type.llvm} ${value.llvm()}, ${target.type.llvm} ${target.llvm()}"
}

class GetElementPointerStatement(
val struct: Memory,
override val type: DragonType,
val index: Value,
val total: Boolean = true,
val inbounds: Boolean = true
): TypedDragonStatement {
override val memoryDependencies: Set<Value> = setOf(index, struct)

val originalType = if (struct.type !is DragonType.Pointer) {
struct.type
} else {
(struct.type as DragonType.Pointer).type
}
val pointerType = DragonType.Pointer(originalType)

override fun llvm(): String =
"getelementptr " +
(if (inbounds) {
"inbounds "
} else {
""
}) +
"${originalType.llvm}, ${pointerType.llvm} ${struct.llvm()}" + (if (total) {
", i32 0"
} else {
""
}) + ", i32 ${index.llvm()}"
}

class LoadStatement(
val target: Memory
): TypedDragonStatement {
override val memoryDependencies: Set<Value> = setOf(target)

override fun isValid(): Boolean =
target.type is DragonType.Pointer

override val type: DragonType = target.type.descendOneLevel()

override fun llvm(): String {
require(isValid()) { "Cannot load from non-pointer type" }
return "load ${type.llvm}, ${target.type.llvm} ${target.llvm()}"
}
}

28 changes: 28 additions & 0 deletions ryujin/src/commonMain/kotlin/struct/Dependency.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package me.gabriel.ryujin.struct

sealed interface Dependency {
data class Function(
val name: String,
val returnType: DragonType,
val parameters: List<DragonType>
): Dependency {
override fun asType(): DragonType = DragonType.Void
}

data class Struct(
val name: String,
val types: Collection<DragonType>
): Dependency {
override fun asType(): DragonType = DragonType.Struct(name, types)
}

data class Constant(
val name: String,
val type: DragonType,
val value: Value
): Dependency {
override fun asType(): DragonType = type
}

fun asType(): DragonType
}
54 changes: 54 additions & 0 deletions ryujin/src/commonMain/kotlin/struct/Type.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package me.gabriel.ryujin.struct

sealed class DragonType(
val llvm: String,
val defaultAlignment: Int,
val size: Int
) {
data object Void : DragonType("void", 0, 0)
data object Int1 : DragonType("i1", 1, 1)
data object Int8 : DragonType("i8", 1, 1)
data object Int16 : DragonType("i16", 2, 2)
data object Int32 : DragonType("i32", 4, 4)
data object Int64 : DragonType("i64", 8, 8)

data object Float32 : DragonType("float", 4, 4)
data object Float64 : DragonType("double", 8, 8)

data class Array(val type: DragonType, val length: Int) : DragonType("[$length x ${type.llvm}]", type.defaultAlignment, type.size * length)

data object Ptr : DragonType("ptr", 1, 1)
data class Pointer(val type: DragonType) : DragonType(if (type == Void) type.llvm else "${type.llvm}*", 8, 8)

data class Struct(
val name: String,
val types: Collection<DragonType>
) : DragonType("%$name", 8, types.sumOf { it.size })

data class Trait(
val name: String,
val types: Collection<DragonType>
) : DragonType("%$name", 8, 0)

data class Dynamic(
val types: List<DragonType>
): DragonType(types.joinToString(
prefix = "<{",
postfix = "}>",
separator = ", "
) { it.llvm }, 8, types.sumOf { it.size })

override fun toString(): String = llvm
}

fun DragonType.extractPrimitiveType() = when (this) {
is DragonType.Array -> this.type
is DragonType.Pointer -> this.type
else -> this
}

fun DragonType.descendOneLevel(): DragonType = when (this) {
is DragonType.Array -> this.type
is DragonType.Pointer -> this.type
else -> error("Cannot descend one level on type $this")
}
40 changes: 40 additions & 0 deletions ryujin/src/commonMain/kotlin/struct/Value.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package me.gabriel.ryujin.struct

sealed interface Value {
val type: DragonType

fun llvm(): String
}

data object Void : Value {
override val type: DragonType = DragonType.Int1

override fun llvm(): String = "void"
}

data class Constant<T : Any>(val value: T, override val type: DragonType): Value {
override fun llvm(): String = value.toString()
}

data class VirtualTable(
val values: List<Value>
): Value {
override val type: DragonType = DragonType.Dynamic(listOf())

override fun llvm(): String = """
|<{
${values.joinToString(",\n") { "|${it.type.llvm} ${it.llvm()}" }}
|}>
""".trimMargin()
}

sealed class Memory(
val register: Int,
override val type: DragonType
): Value {
class Sized(register: Int, type: DragonType, val size: Int) : Memory(register, type)

class Unsized(register: Int, type: DragonType) : Memory(register, type)

override fun llvm(): String = "%$register"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package me.gabriel.ryujin.transcript

import me.gabriel.ryujin.DragonModule
import me.gabriel.ryujin.function.DragonFunction
import me.gabriel.ryujin.struct.Dependency

class DefaultDragonIrTranscriber: DragonIrTranscriber {
override fun transcribe(module: DragonModule): String {
val dependencies = module.dependencies.joinToString("\n") { transcribeDependency(it) }
val functions = module.functions.joinToString("\n") { transcribeFunction(it) }

return """
|$dependencies
|
|$functions
""".trimMargin(marginPrefix = "|")
}

override fun transcribeDependency(dependency: Dependency): String {
return when (dependency) {
is Dependency.Constant -> transcribeConstantDependency(dependency)
is Dependency.Struct -> transcribeStructDependency(dependency)
is Dependency.Function -> transcribeFunctionDependency(dependency)
}
}

override fun transcribeFunction(function: DragonFunction): String {
return buildString {
append("define ${function.returnType} @${function.name}(")
append(function.parameters.entries.joinToString(", ") { "${it.value} %${it.key}" })
append(") {\n")
function.statements.forEach { append(" $it\n") }
append("}")
}
}

private fun transcribeConstantDependency(dependency: Dependency.Constant): String {
return """
|@${dependency.name} = unnamed_addr constant ${dependency.type} ${dependency.value.llvm()}
""".trimMargin(marginPrefix = "|")
}

private fun transcribeStructDependency(dependency: Dependency.Struct): String {
return """
|%${dependency.name} = type { ${dependency.types.joinToString(", ") {it.llvm}} }
""".trimMargin(marginPrefix = "|")
}

private fun transcribeFunctionDependency(dependency: Dependency.Function): String {
return """
|declare ${dependency.returnType} @${dependency.name}(${dependency.parameters.joinToString(", ") {it.llvm}})
""".trimMargin(marginPrefix = "|")
}
}
13 changes: 13 additions & 0 deletions ryujin/src/commonMain/kotlin/transcript/DragonIrTranscriber.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package me.gabriel.ryujin.transcript

import me.gabriel.ryujin.DragonModule
import me.gabriel.ryujin.function.DragonFunction
import me.gabriel.ryujin.struct.Dependency

interface DragonIrTranscriber {
fun transcribe(module: DragonModule): String

fun transcribeDependency(dependency: Dependency): String

fun transcribeFunction(function: DragonFunction): String
}
1 change: 1 addition & 0 deletions settings.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,5 @@ include("analysis")
include("compiler")
include("tools")

include("ryujin")
include("runestone")

0 comments on commit aec92c9

Please sign in to comment.