implement native type compilation

This commit is contained in:
Alex Zenla 2025-07-20 19:57:09 -07:00
parent f7ff896f81
commit 837e0c1b38
No known key found for this signature in database
GPG Key ID: 067B238899B51269
19 changed files with 133 additions and 20 deletions

View File

@ -6,5 +6,6 @@ import kotlinx.serialization.Serializable
enum class IrDefinitionType {
Variable,
CodeFunction,
NativeFunction
NativeFunction,
NativeType,
}

View File

@ -3,6 +3,6 @@ package gay.pizza.pork.bir
import kotlinx.serialization.Serializable
@Serializable
data class IrNativeDefinition(var form: String, var definitions: List<String>) : IrCodeElement() {
data class IrNativeDefinition(var kind: IrNativeDefinitionKind, var form: String, var definitions: List<String>) : IrCodeElement() {
override fun crawl(block: (IrElement) -> Unit) {}
}

View File

@ -0,0 +1,9 @@
package gay.pizza.pork.bir
import kotlinx.serialization.Serializable
@Serializable
enum class IrNativeDefinitionKind {
Function,
Type
}

View File

@ -13,7 +13,8 @@ enum class Opcode(val id: UByte) {
UnaryMinus(10u),
BinaryNot(11u),
And(20u),
Native(24u),
NativeFunction(24u),
NativeType(43u),
Return(10u),
StoreLocal(16u),
LoadLocal(17u),

View File

@ -308,6 +308,13 @@ class AstIrEmitter(
}
override fun visitNativeFunctionDescriptor(node: NativeFunctionDescriptor): IrCodeElement = IrNativeDefinition(
kind = IrNativeDefinitionKind.Function,
form = node.form.id,
definitions = node.definitions.map { it.text }
)
override fun visitNativeTypeDescriptor(node: NativeTypeDescriptor): IrCodeElement = IrNativeDefinition(
kind = IrNativeDefinitionKind.Type,
form = node.form.id,
definitions = node.definitions.map { it.text }
)

View File

@ -3,6 +3,8 @@ package gay.pizza.pork.compiler
import gay.pizza.pork.ast.gen.FunctionDefinition
import gay.pizza.pork.ast.gen.LetDefinition
import gay.pizza.pork.ast.gen.NativeFunctionDescriptor
import gay.pizza.pork.ast.gen.NativeTypeDescriptor
import gay.pizza.pork.ast.gen.TypeDefinition
import gay.pizza.pork.ast.gen.visit
import gay.pizza.pork.bir.IrCodeBlock
import gay.pizza.pork.bir.IrDefinition
@ -36,25 +38,34 @@ class CompilableSymbol(val compilableSlab: CompilableSlab, val scopeSymbol: Scop
scope = compilableSlab.slab.scope
)
irCodeEmitter.enterLocalScope()
val what = if (scopeSymbol.definition is FunctionDefinition) {
val functionDefinition = scopeSymbol.definition as FunctionDefinition
irCodeEmitter.createFunctionArguments(functionDefinition)
functionDefinition.block ?: functionDefinition.nativeFunctionDescriptor!!
} else {
val letDefinition = scopeSymbol.definition as LetDefinition
letDefinition.value
val what = when (scopeSymbol.definition) {
is FunctionDefinition -> {
val functionDefinition = scopeSymbol.definition as FunctionDefinition
irCodeEmitter.createFunctionArguments(functionDefinition)
functionDefinition.block ?: functionDefinition.nativeFunctionDescriptor!!
}
is TypeDefinition -> {
val typeDefinition = scopeSymbol.definition as TypeDefinition
typeDefinition.nativeTypeDescriptor!!
}
else -> {
val letDefinition = scopeSymbol.definition as LetDefinition
letDefinition.value
}
}
val type = if (what is NativeFunctionDescriptor) {
IrDefinitionType.NativeFunction
} else if (what is NativeTypeDescriptor) {
IrDefinitionType.NativeType
} else if (scopeSymbol.definition is LetDefinition) {
IrDefinitionType.Variable
} else {
IrDefinitionType.CodeFunction
}
val irCodeElement = irCodeEmitter.visit(what)
val irCodeBlock = if (irCodeElement is IrCodeBlock) {
irCodeElement
} else IrCodeBlock(listOf(irCodeElement))
val irCodeBlock = irCodeElement as? IrCodeBlock ?: IrCodeBlock(listOf(irCodeElement))
irCodeEmitter.exitLocalScope()
return IrDefinition(
symbol = functionSymbol,

View File

@ -233,7 +233,15 @@ class IrStubOpEmitter(val irDefinition: IrDefinition, val code: CodeBuilder) : I
ConstantTag.NativeDefinition,
buffer,
)
code.emit(Opcode.Native, listOf(nativeDefinitionConstant, functionArgumentCount.toUInt()))
when (ir.kind) {
IrNativeDefinitionKind.Function -> {
code.emit(Opcode.NativeFunction, listOf(nativeDefinitionConstant, functionArgumentCount.toUInt()))
}
IrNativeDefinitionKind.Type -> {
code.emit(Opcode.NativeType, listOf(nativeDefinitionConstant))
}
}
}
override fun visitIrIndex(ir: IrIndex) {

View File

@ -8,16 +8,35 @@ class InternalNativeProvider(val quiet: Boolean = false) : NativeProvider {
"listInitWith" to NativeFunction(::listInitWith)
)
private val types = mutableMapOf(
"int32" to NativeType { Int::class.java },
"int64" to NativeType { Long::class.java },
"string" to NativeType { String::class.java },
"float32" to NativeType { Float::class.java },
"float64" to NativeType { Double::class.java },
"bool" to NativeType { Boolean::class.java },
)
fun add(name: String, function: NativeFunction) {
functions[name] = function
}
fun add(name: String, type: NativeType) {
types[name] = type
}
override fun provideNativeFunction(definitions: List<String>): NativeFunction {
val definition = definitions[0]
return functions[definition] ?:
throw RuntimeException("Unknown internal function: $definition")
}
override fun provideNativeType(definitions: List<String>): NativeType {
val definition = definitions[0]
return types[definition] ?:
throw RuntimeException("Unknown internal type: $definition")
}
private fun printValues(arguments: ArgumentList): Any {
if (quiet || arguments.isEmpty()) return None
print(arguments.at<List<*>>(0).joinToString(" ") { it.toString() })

View File

@ -2,4 +2,5 @@ package gay.pizza.pork.execution
interface NativeProvider {
fun provideNativeFunction(definitions: List<String>): NativeFunction
fun provideNativeType(definitions: List<String>): NativeType
}

View File

@ -0,0 +1,5 @@
package gay.pizza.pork.execution
fun interface NativeType {
fun value(): Any
}

View File

@ -8,6 +8,7 @@ import gay.pizza.pork.evaluator.*
import gay.pizza.pork.execution.ArgumentList
import gay.pizza.pork.execution.NativeFunction
import gay.pizza.pork.execution.NativeProvider
import gay.pizza.pork.execution.NativeType
import gay.pizza.pork.execution.None
import kotlin.io.path.Path
import kotlin.io.path.absolutePathString
@ -231,6 +232,11 @@ class FfiNativeProvider : ExpandedNativeProvider, NativeProvider {
callable.call(arguments, CallStack())
}
}
override fun provideNativeType(definitions: List<String>): NativeType {
throw RuntimeException("Unknown native type")
}
companion object {
fun typeConversion(type: FfiType): Type = when (type) {
FfiPrimitiveType.UnsignedByte -> Type.UINT8

View File

@ -6,6 +6,7 @@ import gay.pizza.pork.evaluator.SlabContext
import gay.pizza.pork.evaluator.ExpandedNativeProvider
import gay.pizza.pork.execution.NativeFunction
import gay.pizza.pork.execution.NativeProvider
import gay.pizza.pork.execution.NativeType
import gay.pizza.pork.execution.None
import java.lang.invoke.MethodHandles
import java.lang.invoke.MethodType
@ -68,4 +69,8 @@ class JavaNativeProvider : ExpandedNativeProvider, NativeProvider {
override fun provideNativeFunction(definitions: List<String>): NativeFunction {
throw RuntimeException("Invalid Native Function Usage")
}
override fun provideNativeType(definitions: List<String>): NativeType {
throw RuntimeException("Invalid Native Type Usage")
}
}

View File

@ -16,6 +16,9 @@ class ExternalSymbolUsageAnalyzer : FunctionLevelVisitor<Unit>() {
internalSymbols.removeLast()
}
override fun visitTypeDefinition(node: TypeDefinition) {
}
override fun visitLetDefinition(node: LetDefinition) {
node.value.visit(this)
}

View File

@ -69,7 +69,7 @@ class CompileCommand : CliktCommand("compile") {
annotation = " ; ${annotations.joinToString(", ") { it.text }}"
}
print(" ${symbol.offset + index.toUInt()} ${op}${annotation}")
if (op.code == Opcode.Constant || op.code == Opcode.Native) {
if (op.code == Opcode.Constant || op.code == Opcode.NativeFunction) {
val constant = compiledWorld.constantPool.constants[op.args[0].toInt()]
val constantString = when (constant.tag) {
ConstantTag.String -> "string = \"" + constant.readAsString() + "\""

View File

@ -47,7 +47,8 @@ val StandardOpHandlers: List<OpHandler> = listOf(
CallOpHandler,
ReturnOpHandler,
NativeOpHandler,
NativeFunctionOpHandler,
NativeTypeOpHandler,
ScopeInOpHandler,
ScopeOutOpHandler,

View File

@ -2,11 +2,10 @@ package gay.pizza.pork.vm.ops
import gay.pizza.pork.bytecode.Op
import gay.pizza.pork.bytecode.Opcode
import gay.pizza.pork.execution.None
import gay.pizza.pork.vm.InternalMachine
import gay.pizza.pork.vm.OpHandler
object NativeOpHandler : OpHandler(Opcode.Native) {
object NativeFunctionOpHandler : OpHandler(Opcode.NativeFunction) {
override fun handle(machine: InternalMachine, op: Op) {
val handler = optimize(machine, op)
handler.handle(machine, op)
@ -17,6 +16,6 @@ object NativeOpHandler : OpHandler(Opcode.Native) {
val form = nativeDefinition[0]
val provider = machine.nativeRegistry.of(form)
val function = provider.provideNativeFunction(nativeDefinition.subList(1, nativeDefinition.size))
return OptimizedNativeOpHandler(function)
return OptimizedNativeFunctionOpHandler(function)
}
}

View File

@ -0,0 +1,21 @@
package gay.pizza.pork.vm.ops
import gay.pizza.pork.bytecode.Op
import gay.pizza.pork.bytecode.Opcode
import gay.pizza.pork.vm.InternalMachine
import gay.pizza.pork.vm.OpHandler
object NativeTypeOpHandler : OpHandler(Opcode.NativeType) {
override fun handle(machine: InternalMachine, op: Op) {
val handler = optimize(machine, op)
handler.handle(machine, op)
}
override fun optimize(machine: InternalMachine, op: Op): OpHandler {
val nativeDefinition = machine.world.constantPool.read(op.args[0]).readAsNativeDefinition()
val form = nativeDefinition[0]
val provider = machine.nativeRegistry.of(form)
val type = provider.provideNativeType(nativeDefinition.subList(1, nativeDefinition.size))
return OptimizedNativeTypeOpHandler(type)
}
}

View File

@ -7,7 +7,7 @@ import gay.pizza.pork.execution.None
import gay.pizza.pork.vm.InternalMachine
import gay.pizza.pork.vm.OpHandler
class OptimizedNativeOpHandler(val function: NativeFunction) : OpHandler(Opcode.Native) {
class OptimizedNativeFunctionOpHandler(val function: NativeFunction) : OpHandler(Opcode.NativeFunction) {
override fun handle(machine: InternalMachine, op: Op) {
val argumentCount = op.args[1]
val arguments = mutableListOf<Any>()

View File

@ -0,0 +1,16 @@
package gay.pizza.pork.vm.ops
import gay.pizza.pork.bytecode.Op
import gay.pizza.pork.bytecode.Opcode
import gay.pizza.pork.execution.NativeFunction
import gay.pizza.pork.execution.NativeType
import gay.pizza.pork.execution.None
import gay.pizza.pork.vm.InternalMachine
import gay.pizza.pork.vm.OpHandler
class OptimizedNativeTypeOpHandler(val type: NativeType) : OpHandler(Opcode.NativeType) {
override fun handle(machine: InternalMachine, op: Op) {
val result = type.value()
machine.push(if (result == Unit) None else result)
}
}