mirror of
https://github.com/GayPizzaSpecifications/pork.git
synced 2025-08-03 21:21:33 +00:00
implement native type compilation
This commit is contained in:
@ -6,5 +6,6 @@ import kotlinx.serialization.Serializable
|
|||||||
enum class IrDefinitionType {
|
enum class IrDefinitionType {
|
||||||
Variable,
|
Variable,
|
||||||
CodeFunction,
|
CodeFunction,
|
||||||
NativeFunction
|
NativeFunction,
|
||||||
|
NativeType,
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,6 @@ package gay.pizza.pork.bir
|
|||||||
import kotlinx.serialization.Serializable
|
import kotlinx.serialization.Serializable
|
||||||
|
|
||||||
@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) {}
|
override fun crawl(block: (IrElement) -> Unit) {}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,9 @@
|
|||||||
|
package gay.pizza.pork.bir
|
||||||
|
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
enum class IrNativeDefinitionKind {
|
||||||
|
Function,
|
||||||
|
Type
|
||||||
|
}
|
@ -13,7 +13,8 @@ enum class Opcode(val id: UByte) {
|
|||||||
UnaryMinus(10u),
|
UnaryMinus(10u),
|
||||||
BinaryNot(11u),
|
BinaryNot(11u),
|
||||||
And(20u),
|
And(20u),
|
||||||
Native(24u),
|
NativeFunction(24u),
|
||||||
|
NativeType(43u),
|
||||||
Return(10u),
|
Return(10u),
|
||||||
StoreLocal(16u),
|
StoreLocal(16u),
|
||||||
LoadLocal(17u),
|
LoadLocal(17u),
|
||||||
|
@ -308,6 +308,13 @@ class AstIrEmitter(
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun visitNativeFunctionDescriptor(node: NativeFunctionDescriptor): IrCodeElement = IrNativeDefinition(
|
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,
|
form = node.form.id,
|
||||||
definitions = node.definitions.map { it.text }
|
definitions = node.definitions.map { it.text }
|
||||||
)
|
)
|
||||||
|
@ -3,6 +3,8 @@ package gay.pizza.pork.compiler
|
|||||||
import gay.pizza.pork.ast.gen.FunctionDefinition
|
import gay.pizza.pork.ast.gen.FunctionDefinition
|
||||||
import gay.pizza.pork.ast.gen.LetDefinition
|
import gay.pizza.pork.ast.gen.LetDefinition
|
||||||
import gay.pizza.pork.ast.gen.NativeFunctionDescriptor
|
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.ast.gen.visit
|
||||||
import gay.pizza.pork.bir.IrCodeBlock
|
import gay.pizza.pork.bir.IrCodeBlock
|
||||||
import gay.pizza.pork.bir.IrDefinition
|
import gay.pizza.pork.bir.IrDefinition
|
||||||
@ -36,25 +38,34 @@ class CompilableSymbol(val compilableSlab: CompilableSlab, val scopeSymbol: Scop
|
|||||||
scope = compilableSlab.slab.scope
|
scope = compilableSlab.slab.scope
|
||||||
)
|
)
|
||||||
irCodeEmitter.enterLocalScope()
|
irCodeEmitter.enterLocalScope()
|
||||||
val what = if (scopeSymbol.definition is FunctionDefinition) {
|
val what = when (scopeSymbol.definition) {
|
||||||
|
is FunctionDefinition -> {
|
||||||
val functionDefinition = scopeSymbol.definition as FunctionDefinition
|
val functionDefinition = scopeSymbol.definition as FunctionDefinition
|
||||||
irCodeEmitter.createFunctionArguments(functionDefinition)
|
irCodeEmitter.createFunctionArguments(functionDefinition)
|
||||||
functionDefinition.block ?: functionDefinition.nativeFunctionDescriptor!!
|
functionDefinition.block ?: functionDefinition.nativeFunctionDescriptor!!
|
||||||
} else {
|
}
|
||||||
|
|
||||||
|
is TypeDefinition -> {
|
||||||
|
val typeDefinition = scopeSymbol.definition as TypeDefinition
|
||||||
|
typeDefinition.nativeTypeDescriptor!!
|
||||||
|
}
|
||||||
|
|
||||||
|
else -> {
|
||||||
val letDefinition = scopeSymbol.definition as LetDefinition
|
val letDefinition = scopeSymbol.definition as LetDefinition
|
||||||
letDefinition.value
|
letDefinition.value
|
||||||
}
|
}
|
||||||
|
}
|
||||||
val type = if (what is NativeFunctionDescriptor) {
|
val type = if (what is NativeFunctionDescriptor) {
|
||||||
IrDefinitionType.NativeFunction
|
IrDefinitionType.NativeFunction
|
||||||
|
} else if (what is NativeTypeDescriptor) {
|
||||||
|
IrDefinitionType.NativeType
|
||||||
} else if (scopeSymbol.definition is LetDefinition) {
|
} else if (scopeSymbol.definition is LetDefinition) {
|
||||||
IrDefinitionType.Variable
|
IrDefinitionType.Variable
|
||||||
} else {
|
} else {
|
||||||
IrDefinitionType.CodeFunction
|
IrDefinitionType.CodeFunction
|
||||||
}
|
}
|
||||||
val irCodeElement = irCodeEmitter.visit(what)
|
val irCodeElement = irCodeEmitter.visit(what)
|
||||||
val irCodeBlock = if (irCodeElement is IrCodeBlock) {
|
val irCodeBlock = irCodeElement as? IrCodeBlock ?: IrCodeBlock(listOf(irCodeElement))
|
||||||
irCodeElement
|
|
||||||
} else IrCodeBlock(listOf(irCodeElement))
|
|
||||||
irCodeEmitter.exitLocalScope()
|
irCodeEmitter.exitLocalScope()
|
||||||
return IrDefinition(
|
return IrDefinition(
|
||||||
symbol = functionSymbol,
|
symbol = functionSymbol,
|
||||||
|
@ -233,7 +233,15 @@ class IrStubOpEmitter(val irDefinition: IrDefinition, val code: CodeBuilder) : I
|
|||||||
ConstantTag.NativeDefinition,
|
ConstantTag.NativeDefinition,
|
||||||
buffer,
|
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) {
|
override fun visitIrIndex(ir: IrIndex) {
|
||||||
|
@ -8,16 +8,35 @@ class InternalNativeProvider(val quiet: Boolean = false) : NativeProvider {
|
|||||||
"listInitWith" to NativeFunction(::listInitWith)
|
"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) {
|
fun add(name: String, function: NativeFunction) {
|
||||||
functions[name] = function
|
functions[name] = function
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun add(name: String, type: NativeType) {
|
||||||
|
types[name] = type
|
||||||
|
}
|
||||||
|
|
||||||
override fun provideNativeFunction(definitions: List<String>): NativeFunction {
|
override fun provideNativeFunction(definitions: List<String>): NativeFunction {
|
||||||
val definition = definitions[0]
|
val definition = definitions[0]
|
||||||
return functions[definition] ?:
|
return functions[definition] ?:
|
||||||
throw RuntimeException("Unknown internal function: $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 {
|
private fun printValues(arguments: ArgumentList): Any {
|
||||||
if (quiet || arguments.isEmpty()) return None
|
if (quiet || arguments.isEmpty()) return None
|
||||||
print(arguments.at<List<*>>(0).joinToString(" ") { it.toString() })
|
print(arguments.at<List<*>>(0).joinToString(" ") { it.toString() })
|
||||||
|
@ -2,4 +2,5 @@ package gay.pizza.pork.execution
|
|||||||
|
|
||||||
interface NativeProvider {
|
interface NativeProvider {
|
||||||
fun provideNativeFunction(definitions: List<String>): NativeFunction
|
fun provideNativeFunction(definitions: List<String>): NativeFunction
|
||||||
|
fun provideNativeType(definitions: List<String>): NativeType
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,5 @@
|
|||||||
|
package gay.pizza.pork.execution
|
||||||
|
|
||||||
|
fun interface NativeType {
|
||||||
|
fun value(): Any
|
||||||
|
}
|
@ -8,6 +8,7 @@ import gay.pizza.pork.evaluator.*
|
|||||||
import gay.pizza.pork.execution.ArgumentList
|
import gay.pizza.pork.execution.ArgumentList
|
||||||
import gay.pizza.pork.execution.NativeFunction
|
import gay.pizza.pork.execution.NativeFunction
|
||||||
import gay.pizza.pork.execution.NativeProvider
|
import gay.pizza.pork.execution.NativeProvider
|
||||||
|
import gay.pizza.pork.execution.NativeType
|
||||||
import gay.pizza.pork.execution.None
|
import gay.pizza.pork.execution.None
|
||||||
import kotlin.io.path.Path
|
import kotlin.io.path.Path
|
||||||
import kotlin.io.path.absolutePathString
|
import kotlin.io.path.absolutePathString
|
||||||
@ -231,6 +232,11 @@ class FfiNativeProvider : ExpandedNativeProvider, NativeProvider {
|
|||||||
callable.call(arguments, CallStack())
|
callable.call(arguments, CallStack())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun provideNativeType(definitions: List<String>): NativeType {
|
||||||
|
throw RuntimeException("Unknown native type")
|
||||||
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
fun typeConversion(type: FfiType): Type = when (type) {
|
fun typeConversion(type: FfiType): Type = when (type) {
|
||||||
FfiPrimitiveType.UnsignedByte -> Type.UINT8
|
FfiPrimitiveType.UnsignedByte -> Type.UINT8
|
||||||
|
@ -6,6 +6,7 @@ import gay.pizza.pork.evaluator.SlabContext
|
|||||||
import gay.pizza.pork.evaluator.ExpandedNativeProvider
|
import gay.pizza.pork.evaluator.ExpandedNativeProvider
|
||||||
import gay.pizza.pork.execution.NativeFunction
|
import gay.pizza.pork.execution.NativeFunction
|
||||||
import gay.pizza.pork.execution.NativeProvider
|
import gay.pizza.pork.execution.NativeProvider
|
||||||
|
import gay.pizza.pork.execution.NativeType
|
||||||
import gay.pizza.pork.execution.None
|
import gay.pizza.pork.execution.None
|
||||||
import java.lang.invoke.MethodHandles
|
import java.lang.invoke.MethodHandles
|
||||||
import java.lang.invoke.MethodType
|
import java.lang.invoke.MethodType
|
||||||
@ -68,4 +69,8 @@ class JavaNativeProvider : ExpandedNativeProvider, NativeProvider {
|
|||||||
override fun provideNativeFunction(definitions: List<String>): NativeFunction {
|
override fun provideNativeFunction(definitions: List<String>): NativeFunction {
|
||||||
throw RuntimeException("Invalid Native Function Usage")
|
throw RuntimeException("Invalid Native Function Usage")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun provideNativeType(definitions: List<String>): NativeType {
|
||||||
|
throw RuntimeException("Invalid Native Type Usage")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -16,6 +16,9 @@ class ExternalSymbolUsageAnalyzer : FunctionLevelVisitor<Unit>() {
|
|||||||
internalSymbols.removeLast()
|
internalSymbols.removeLast()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun visitTypeDefinition(node: TypeDefinition) {
|
||||||
|
}
|
||||||
|
|
||||||
override fun visitLetDefinition(node: LetDefinition) {
|
override fun visitLetDefinition(node: LetDefinition) {
|
||||||
node.value.visit(this)
|
node.value.visit(this)
|
||||||
}
|
}
|
||||||
|
@ -69,7 +69,7 @@ class CompileCommand : CliktCommand("compile") {
|
|||||||
annotation = " ; ${annotations.joinToString(", ") { it.text }}"
|
annotation = " ; ${annotations.joinToString(", ") { it.text }}"
|
||||||
}
|
}
|
||||||
print(" ${symbol.offset + index.toUInt()} ${op}${annotation}")
|
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 constant = compiledWorld.constantPool.constants[op.args[0].toInt()]
|
||||||
val constantString = when (constant.tag) {
|
val constantString = when (constant.tag) {
|
||||||
ConstantTag.String -> "string = \"" + constant.readAsString() + "\""
|
ConstantTag.String -> "string = \"" + constant.readAsString() + "\""
|
||||||
|
@ -47,7 +47,8 @@ val StandardOpHandlers: List<OpHandler> = listOf(
|
|||||||
CallOpHandler,
|
CallOpHandler,
|
||||||
ReturnOpHandler,
|
ReturnOpHandler,
|
||||||
|
|
||||||
NativeOpHandler,
|
NativeFunctionOpHandler,
|
||||||
|
NativeTypeOpHandler,
|
||||||
|
|
||||||
ScopeInOpHandler,
|
ScopeInOpHandler,
|
||||||
ScopeOutOpHandler,
|
ScopeOutOpHandler,
|
||||||
|
@ -2,11 +2,10 @@ package gay.pizza.pork.vm.ops
|
|||||||
|
|
||||||
import gay.pizza.pork.bytecode.Op
|
import gay.pizza.pork.bytecode.Op
|
||||||
import gay.pizza.pork.bytecode.Opcode
|
import gay.pizza.pork.bytecode.Opcode
|
||||||
import gay.pizza.pork.execution.None
|
|
||||||
import gay.pizza.pork.vm.InternalMachine
|
import gay.pizza.pork.vm.InternalMachine
|
||||||
import gay.pizza.pork.vm.OpHandler
|
import gay.pizza.pork.vm.OpHandler
|
||||||
|
|
||||||
object NativeOpHandler : OpHandler(Opcode.Native) {
|
object NativeFunctionOpHandler : OpHandler(Opcode.NativeFunction) {
|
||||||
override fun handle(machine: InternalMachine, op: Op) {
|
override fun handle(machine: InternalMachine, op: Op) {
|
||||||
val handler = optimize(machine, op)
|
val handler = optimize(machine, op)
|
||||||
handler.handle(machine, op)
|
handler.handle(machine, op)
|
||||||
@ -17,6 +16,6 @@ object NativeOpHandler : OpHandler(Opcode.Native) {
|
|||||||
val form = nativeDefinition[0]
|
val form = nativeDefinition[0]
|
||||||
val provider = machine.nativeRegistry.of(form)
|
val provider = machine.nativeRegistry.of(form)
|
||||||
val function = provider.provideNativeFunction(nativeDefinition.subList(1, nativeDefinition.size))
|
val function = provider.provideNativeFunction(nativeDefinition.subList(1, nativeDefinition.size))
|
||||||
return OptimizedNativeOpHandler(function)
|
return OptimizedNativeFunctionOpHandler(function)
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
@ -7,7 +7,7 @@ import gay.pizza.pork.execution.None
|
|||||||
import gay.pizza.pork.vm.InternalMachine
|
import gay.pizza.pork.vm.InternalMachine
|
||||||
import gay.pizza.pork.vm.OpHandler
|
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) {
|
override fun handle(machine: InternalMachine, op: Op) {
|
||||||
val argumentCount = op.args[1]
|
val argumentCount = op.args[1]
|
||||||
val arguments = mutableListOf<Any>()
|
val arguments = mutableListOf<Any>()
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
Reference in New Issue
Block a user