diff --git a/bytecode/src/main/kotlin/gay/pizza/pork/bytecode/Constant.kt b/bytecode/src/main/kotlin/gay/pizza/pork/bytecode/Constant.kt index d85a869..57486a5 100644 --- a/bytecode/src/main/kotlin/gay/pizza/pork/bytecode/Constant.kt +++ b/bytecode/src/main/kotlin/gay/pizza/pork/bytecode/Constant.kt @@ -11,6 +11,25 @@ data class Constant(val id: UInt, val tag: ConstantTag, val value: ByteArray) { return String(value) } + fun readAsNativeDefinition(): List { + val defs = mutableListOf() + val buffer = mutableListOf() + for (b in value) { + if (b == 0.toByte()) { + defs.add(String(buffer.toByteArray())) + buffer.clear() + continue + } + buffer.add(b) + } + + if (buffer.isNotEmpty()) { + defs.add(String(buffer.toByteArray())) + } + + return defs + } + override fun equals(other: Any?): Boolean { if (this === other) return true other as Constant diff --git a/bytecode/src/main/kotlin/gay/pizza/pork/bytecode/ConstantTag.kt b/bytecode/src/main/kotlin/gay/pizza/pork/bytecode/ConstantTag.kt index 12889af..151bc04 100644 --- a/bytecode/src/main/kotlin/gay/pizza/pork/bytecode/ConstantTag.kt +++ b/bytecode/src/main/kotlin/gay/pizza/pork/bytecode/ConstantTag.kt @@ -4,5 +4,6 @@ import kotlinx.serialization.Serializable @Serializable enum class ConstantTag { - String + String, + NativeDefinition, } diff --git a/compiler/src/main/kotlin/gay/pizza/pork/compiler/IrStubOpEmitter.kt b/compiler/src/main/kotlin/gay/pizza/pork/compiler/IrStubOpEmitter.kt index da2e91c..dc0c52c 100644 --- a/compiler/src/main/kotlin/gay/pizza/pork/compiler/IrStubOpEmitter.kt +++ b/compiler/src/main/kotlin/gay/pizza/pork/compiler/IrStubOpEmitter.kt @@ -129,6 +129,7 @@ class IrStubOpEmitter(val irDefinition: IrDefinition, val code: CodeBuilder) : I code.emit(Opcode.CompareEqual) code.emit(Opcode.Not) } + IrInfixOp.EuclideanModulo -> code.emit(Opcode.EuclideanModulo) IrInfixOp.Remainder -> code.emit(Opcode.Remainder) IrInfixOp.Lesser -> code.emit(Opcode.CompareLesser) @@ -207,7 +208,8 @@ class IrStubOpEmitter(val irDefinition: IrDefinition, val code: CodeBuilder) : I code.emit(Opcode.Integer, listOf(1u)) code.emit(Opcode.Add, emptyList()) } - IrSuffixOp.Decrement-> { + + IrSuffixOp.Decrement -> { code.emit(Opcode.Integer, listOf(1u)) code.emit(Opcode.Subtract, emptyList()) } @@ -216,18 +218,22 @@ class IrStubOpEmitter(val irDefinition: IrDefinition, val code: CodeBuilder) : I } override fun visitIrNativeDefinition(ir: IrNativeDefinition) { - for (def in ir.definitions.reversed()) { - val defConstant = symbol.compilableSlab.compiler.constantPool.assign( - ConstantTag.String, - def.encodeToByteArray() - ) - code.emit(Opcode.Constant, listOf(defConstant)) + val encodedDefinitions = ir.definitions.map { def -> def.encodeToByteArray() }.toMutableList() + encodedDefinitions.add(0, ir.form.encodeToByteArray()) + val buffer = ByteArray(encodedDefinitions.sumOf { it.size } + encodedDefinitions.size - 1) { 0 } + var i = 0 + for ((index, encoded) in encodedDefinitions.withIndex()) { + encoded.copyInto(buffer, i, 0) + i += encoded.size + if (index != encodedDefinitions.lastIndex) { + i += 1 + } } - val formConstant = symbol.compilableSlab.compiler.constantPool.assign( - ConstantTag.String, - ir.form.encodeToByteArray() + val nativeDefinitionConstant = symbol.compilableSlab.compiler.constantPool.assign( + ConstantTag.NativeDefinition, + buffer, ) - code.emit(Opcode.Native, listOf(formConstant, ir.definitions.size.toUInt(), functionArgumentCount.toUInt())) + code.emit(Opcode.Native, listOf(nativeDefinitionConstant, functionArgumentCount.toUInt())) } override fun visitIrIndex(ir: IrIndex) { diff --git a/tool/src/main/kotlin/gay/pizza/pork/tool/CompileCommand.kt b/tool/src/main/kotlin/gay/pizza/pork/tool/CompileCommand.kt index 8b37c0b..e9a6a0f 100644 --- a/tool/src/main/kotlin/gay/pizza/pork/tool/CompileCommand.kt +++ b/tool/src/main/kotlin/gay/pizza/pork/tool/CompileCommand.kt @@ -22,9 +22,11 @@ class CompileCommand : CliktCommand(help = "Compile Pork", name = "compile") { val path by argument("file") - private val yaml = Yaml(configuration = Yaml.default.configuration.copy( - polymorphismStyle = PolymorphismStyle.Property - )) + private val yaml = Yaml( + configuration = Yaml.default.configuration.copy( + polymorphismStyle = PolymorphismStyle.Property + ) + ) override fun run() { val tool = FileTool(PlatformFsProvider.resolve(path)) @@ -61,13 +63,14 @@ class CompileCommand : CliktCommand(help = "Compile Pork", name = "compile") { var annotation = "" val annotations = compiledWorld.annotations.filter { it.inst == (symbol.offset + index.toUInt()) } if (annotations.isNotEmpty()) { - annotation = " ; ${annotations.joinToString(", ") { it.text}}" + annotation = " ; ${annotations.joinToString(", ") { it.text }}" } print(" ${symbol.offset + index.toUInt()} ${op}${annotation}") - if (op.code == Opcode.Constant) { + if (op.code == Opcode.Constant || op.code == Opcode.Native) { val constant = compiledWorld.constantPool.constants[op.args[0].toInt()] val constantString = when (constant.tag) { - ConstantTag.String -> "\"" + constant.readAsString() + "\"" + ConstantTag.String -> "string = \"" + constant.readAsString() + "\"" + ConstantTag.NativeDefinition -> "native definition = " + constant.readAsNativeDefinition().joinToString(" ") { def -> "\"${def}\"" } } print(" ; constant: $constantString") } diff --git a/vm/src/main/kotlin/gay/pizza/pork/vm/InternalMachine.kt b/vm/src/main/kotlin/gay/pizza/pork/vm/InternalMachine.kt index 5fc04f4..fae67d2 100644 --- a/vm/src/main/kotlin/gay/pizza/pork/vm/InternalMachine.kt +++ b/vm/src/main/kotlin/gay/pizza/pork/vm/InternalMachine.kt @@ -6,9 +6,9 @@ import gay.pizza.pork.execution.NativeRegistry class InternalMachine(val world: CompiledWorld, val nativeRegistry: NativeRegistry, val handlers: List) { private val inlined = world.code.map { op -> - val handler = handlers.firstOrNull { it.code == op.code } ?: - throw VirtualMachineException("Opcode ${op.code.name} does not have a handler.") - op to handler + val handler = handlers.firstOrNull { it.code == op.code } + ?: throw VirtualMachineException("Opcode ${op.code.name} does not have a handler.") + op to (handler.optimize(machine = this, op) ?: handler) } private var inst: UInt = 0u diff --git a/vm/src/main/kotlin/gay/pizza/pork/vm/OpHandler.kt b/vm/src/main/kotlin/gay/pizza/pork/vm/OpHandler.kt index e2a20f7..1bbec4a 100644 --- a/vm/src/main/kotlin/gay/pizza/pork/vm/OpHandler.kt +++ b/vm/src/main/kotlin/gay/pizza/pork/vm/OpHandler.kt @@ -5,4 +5,6 @@ import gay.pizza.pork.bytecode.Opcode abstract class OpHandler(val code: Opcode) { abstract fun handle(machine: InternalMachine, op: Op) + + open fun optimize(machine: InternalMachine, op: Op): OpHandler? = null } diff --git a/vm/src/main/kotlin/gay/pizza/pork/vm/ops/NativeOpHandler.kt b/vm/src/main/kotlin/gay/pizza/pork/vm/ops/NativeOpHandler.kt index b44207e..f520967 100644 --- a/vm/src/main/kotlin/gay/pizza/pork/vm/ops/NativeOpHandler.kt +++ b/vm/src/main/kotlin/gay/pizza/pork/vm/ops/NativeOpHandler.kt @@ -8,23 +8,15 @@ import gay.pizza.pork.vm.OpHandler object NativeOpHandler : OpHandler(Opcode.Native) { override fun handle(machine: InternalMachine, op: Op) { - val argumentCount = op.args[2] - val arguments = mutableListOf() - var x = argumentCount - while (x > 0u) { - x-- - arguments.add(machine.localAt(x)) - } - val formConstant = machine.world.constantPool.read(op.args[0]) - val form = formConstant.readAsString() + 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 countOfNativeDefs = op.args[1].toInt() - val defs = mutableListOf() - for (i in 0 until countOfNativeDefs) { - defs.add(machine.pop()) - } - val function = provider.provideNativeFunction(defs) - val result = function.invoke(arguments) - machine.push(if (result == Unit) None else result) + val function = provider.provideNativeFunction(nativeDefinition.subList(1, nativeDefinition.size)) + return OptimizedNativeOpHandler(function) } } diff --git a/vm/src/main/kotlin/gay/pizza/pork/vm/ops/OptimizedNativeOpHandler.kt b/vm/src/main/kotlin/gay/pizza/pork/vm/ops/OptimizedNativeOpHandler.kt new file mode 100644 index 0000000..22a1df0 --- /dev/null +++ b/vm/src/main/kotlin/gay/pizza/pork/vm/ops/OptimizedNativeOpHandler.kt @@ -0,0 +1,22 @@ +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.None +import gay.pizza.pork.vm.InternalMachine +import gay.pizza.pork.vm.OpHandler + +class OptimizedNativeOpHandler(val function: NativeFunction) : OpHandler(Opcode.Native) { + override fun handle(machine: InternalMachine, op: Op) { + val argumentCount = op.args[1] + val arguments = mutableListOf() + var x = argumentCount + while (x > 0u) { + x-- + arguments.add(machine.localAt(x)) + } + val result = function.invoke(arguments) + machine.push(if (result == Unit) None else result) + } +}