From aec92c915f9834502d076ef26a32cf3fbf0046d4 Mon Sep 17 00:00:00 2001 From: SrGaabriel Date: Fri, 6 Sep 2024 19:34:30 -0300 Subject: [PATCH] feat: ryujin, a better llvm library & dsl --- ryujin/build.gradle.kts | 16 +++++ ryujin/src/commonMain/kotlin/DragonModule.kt | 27 ++++++++ .../commonMain/kotlin/dsl/FunctionScopeDsl.kt | 13 ++++ .../commonMain/kotlin/dsl/ModuleScopeDsl.kt | 30 +++++++++ .../kotlin/function/DragonFunction.kt | 14 +++++ .../kotlin/statement/DragonStatement.kt | 16 +++++ .../commonMain/kotlin/statement/Pointers.kt | 63 +++++++++++++++++++ .../commonMain/kotlin/struct/Dependency.kt | 28 +++++++++ ryujin/src/commonMain/kotlin/struct/Type.kt | 54 ++++++++++++++++ ryujin/src/commonMain/kotlin/struct/Value.kt | 40 ++++++++++++ .../transcript/DefaultDragonIrTranscriber.kt | 54 ++++++++++++++++ .../kotlin/transcript/DragonIrTranscriber.kt | 13 ++++ settings.gradle.kts | 1 + 13 files changed, 369 insertions(+) create mode 100644 ryujin/build.gradle.kts create mode 100644 ryujin/src/commonMain/kotlin/DragonModule.kt create mode 100644 ryujin/src/commonMain/kotlin/dsl/FunctionScopeDsl.kt create mode 100644 ryujin/src/commonMain/kotlin/dsl/ModuleScopeDsl.kt create mode 100644 ryujin/src/commonMain/kotlin/function/DragonFunction.kt create mode 100644 ryujin/src/commonMain/kotlin/statement/DragonStatement.kt create mode 100644 ryujin/src/commonMain/kotlin/statement/Pointers.kt create mode 100644 ryujin/src/commonMain/kotlin/struct/Dependency.kt create mode 100644 ryujin/src/commonMain/kotlin/struct/Type.kt create mode 100644 ryujin/src/commonMain/kotlin/struct/Value.kt create mode 100644 ryujin/src/commonMain/kotlin/transcript/DefaultDragonIrTranscriber.kt create mode 100644 ryujin/src/commonMain/kotlin/transcript/DragonIrTranscriber.kt diff --git a/ryujin/build.gradle.kts b/ryujin/build.gradle.kts new file mode 100644 index 0000000..42d3f9d --- /dev/null +++ b/ryujin/build.gradle.kts @@ -0,0 +1,16 @@ +plugins { + kotlin("multiplatform") +} + +repositories { + mavenCentral() +} + +kotlin { + jvm() + mingwX64() + linuxX64() + iosArm64() + macosX64() + js().browser() +} \ No newline at end of file diff --git a/ryujin/src/commonMain/kotlin/DragonModule.kt b/ryujin/src/commonMain/kotlin/DragonModule.kt new file mode 100644 index 0000000..57addc4 --- /dev/null +++ b/ryujin/src/commonMain/kotlin/DragonModule.kt @@ -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 + val dependencies: MutableSet + + 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) + } +} \ No newline at end of file diff --git a/ryujin/src/commonMain/kotlin/dsl/FunctionScopeDsl.kt b/ryujin/src/commonMain/kotlin/dsl/FunctionScopeDsl.kt new file mode 100644 index 0000000..c66f26b --- /dev/null +++ b/ryujin/src/commonMain/kotlin/dsl/FunctionScopeDsl.kt @@ -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) + } +} \ No newline at end of file diff --git a/ryujin/src/commonMain/kotlin/dsl/ModuleScopeDsl.kt b/ryujin/src/commonMain/kotlin/dsl/ModuleScopeDsl.kt new file mode 100644 index 0000000..d5b25d2 --- /dev/null +++ b/ryujin/src/commonMain/kotlin/dsl/ModuleScopeDsl.kt @@ -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 = mutableSetOf() + override val dependencies: MutableSet = mutableSetOf() + + fun function( + name: String, + parameters: Map, + returnType: DragonType, + block: FunctionScopeDsl.() -> Unit + ) { + val function = DragonFunction( + module = this, + name = name, + parameters = parameters, + returnType = returnType + ) + FunctionScopeDsl( + module = this, + function = function + ).apply(block) + } +} + diff --git a/ryujin/src/commonMain/kotlin/function/DragonFunction.kt b/ryujin/src/commonMain/kotlin/function/DragonFunction.kt new file mode 100644 index 0000000..67941ac --- /dev/null +++ b/ryujin/src/commonMain/kotlin/function/DragonFunction.kt @@ -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, + val returnType: DragonType +) { + val statements = mutableListOf() +} \ No newline at end of file diff --git a/ryujin/src/commonMain/kotlin/statement/DragonStatement.kt b/ryujin/src/commonMain/kotlin/statement/DragonStatement.kt new file mode 100644 index 0000000..be8da05 --- /dev/null +++ b/ryujin/src/commonMain/kotlin/statement/DragonStatement.kt @@ -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 + + fun isValid(): Boolean = true + + fun llvm(): String +} + +interface TypedDragonStatement : DragonStatement { + val type: DragonType +} \ No newline at end of file diff --git a/ryujin/src/commonMain/kotlin/statement/Pointers.kt b/ryujin/src/commonMain/kotlin/statement/Pointers.kt new file mode 100644 index 0000000..795d3de --- /dev/null +++ b/ryujin/src/commonMain/kotlin/statement/Pointers.kt @@ -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 = 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 = 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 = 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()}" + } +} + diff --git a/ryujin/src/commonMain/kotlin/struct/Dependency.kt b/ryujin/src/commonMain/kotlin/struct/Dependency.kt new file mode 100644 index 0000000..950baf9 --- /dev/null +++ b/ryujin/src/commonMain/kotlin/struct/Dependency.kt @@ -0,0 +1,28 @@ +package me.gabriel.ryujin.struct + +sealed interface Dependency { + data class Function( + val name: String, + val returnType: DragonType, + val parameters: List + ): Dependency { + override fun asType(): DragonType = DragonType.Void + } + + data class Struct( + val name: String, + val types: Collection + ): 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 +} \ No newline at end of file diff --git a/ryujin/src/commonMain/kotlin/struct/Type.kt b/ryujin/src/commonMain/kotlin/struct/Type.kt new file mode 100644 index 0000000..74dc723 --- /dev/null +++ b/ryujin/src/commonMain/kotlin/struct/Type.kt @@ -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("%$name", 8, types.sumOf { it.size }) + + data class Trait( + val name: String, + val types: Collection + ) : DragonType("%$name", 8, 0) + + data class Dynamic( + val types: List + ): 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") +} \ No newline at end of file diff --git a/ryujin/src/commonMain/kotlin/struct/Value.kt b/ryujin/src/commonMain/kotlin/struct/Value.kt new file mode 100644 index 0000000..ddb82af --- /dev/null +++ b/ryujin/src/commonMain/kotlin/struct/Value.kt @@ -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(val value: T, override val type: DragonType): Value { + override fun llvm(): String = value.toString() +} + +data class VirtualTable( + val values: List +): 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" +} \ No newline at end of file diff --git a/ryujin/src/commonMain/kotlin/transcript/DefaultDragonIrTranscriber.kt b/ryujin/src/commonMain/kotlin/transcript/DefaultDragonIrTranscriber.kt new file mode 100644 index 0000000..c9f764a --- /dev/null +++ b/ryujin/src/commonMain/kotlin/transcript/DefaultDragonIrTranscriber.kt @@ -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 = "|") + } +} \ No newline at end of file diff --git a/ryujin/src/commonMain/kotlin/transcript/DragonIrTranscriber.kt b/ryujin/src/commonMain/kotlin/transcript/DragonIrTranscriber.kt new file mode 100644 index 0000000..218c13b --- /dev/null +++ b/ryujin/src/commonMain/kotlin/transcript/DragonIrTranscriber.kt @@ -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 +} \ No newline at end of file diff --git a/settings.gradle.kts b/settings.gradle.kts index bbde102..db7a097 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -17,4 +17,5 @@ include("analysis") include("compiler") include("tools") +include("ryujin") include("runestone") \ No newline at end of file