vm: implemented Pipe expression

This commit is contained in:
Irmen de Jong 2022-04-03 15:25:32 +02:00
parent 9874fe2c23
commit d616cb283b
7 changed files with 108 additions and 36 deletions

View File

@ -107,6 +107,9 @@ class PtPipe(type: DataType, val void: Boolean, position: Position) : PtExpressi
require(type!=DataType.UNDEFINED)
}
val segments: List<PtExpression>
get() = children.map { it as PtExpression }
override fun printProperties() {}
}

View File

@ -98,6 +98,18 @@ internal class BuiltinFuncGen(private val codeGen: CodeGen, private val exprGen:
code += exprGen.translateExpression(call.args[1], lsbReg)
code += VmCodeInstruction(Opcode.CONCAT, VmDataType.BYTE, reg1=resultRegister, reg2=msbReg, reg3=lsbReg)
}
"sin8u" -> {
code += exprGen.translateExpression(call.args[0], 0)
code += VmCodeInstruction(Opcode.SYSCALL, value=Syscall.SIN8U.ordinal)
if(resultRegister!=0)
code += VmCodeInstruction(Opcode.LOADR, VmDataType.BYTE, reg1=resultRegister, reg2=0)
}
"cos8u" -> {
code += exprGen.translateExpression(call.args[0], 0)
code += VmCodeInstruction(Opcode.SYSCALL, value=Syscall.COS8U.ordinal)
if(resultRegister!=0)
code += VmCodeInstruction(Opcode.LOADR, VmDataType.BYTE, reg1=resultRegister, reg2=0)
}
else -> {
TODO("builtinfunc ${call.name}")
// code += VmCodeInstruction(Opcode.NOP))

View File

@ -16,22 +16,30 @@ internal class ExpressionGen(private val codeGen: CodeGen) {
require(codeGen.vmRegisters.peekNext() > resultRegister)
val code = VmCodeChunk()
val vmDt = codeGen.vmType(expr.type)
when (expr) {
is PtNumber -> {
val vmDt = codeGen.vmType(expr.type)
code += VmCodeInstruction(Opcode.LOAD, vmDt, reg1=resultRegister, value=expr.number.toInt())
}
is PtIdentifier -> {
val mem = codeGen.allocations.get(expr.targetName)
code += if(expr.type in PassByValueDatatypes) {
VmCodeInstruction(Opcode.LOADM, vmDt, reg1=resultRegister, value=mem)
val vmDt = codeGen.vmType(expr.type)
if(expr.targetName[0].startsWith(":vmreg-")) {
// special direct reference to a register in the VM
val reg = expr.targetName[0].substring(7).toInt()
code += VmCodeInstruction(Opcode.LOADR, vmDt, reg1=resultRegister, reg2=reg)
} else {
// for strings and arrays etc., load the *address* of the value instead
VmCodeInstruction(Opcode.LOAD, vmDt, reg1=resultRegister, value=mem)
val mem = codeGen.allocations.get(expr.targetName)
code += if (expr.type in PassByValueDatatypes) {
VmCodeInstruction(Opcode.LOADM, vmDt, reg1 = resultRegister, value = mem)
} else {
// for strings and arrays etc., load the *address* of the value instead
VmCodeInstruction(Opcode.LOAD, vmDt, reg1 = resultRegister, value = mem)
}
}
}
is PtAddressOf -> {
val vmDt = codeGen.vmType(expr.type)
val mem = codeGen.allocations.get(expr.identifier.targetName)
code += VmCodeInstruction(Opcode.LOAD, vmDt, reg1=resultRegister, value=mem)
}
@ -57,7 +65,41 @@ internal class ExpressionGen(private val codeGen: CodeGen) {
}
internal fun translate(pipe: PtPipe, resultRegister: Int): VmCodeChunk {
TODO("Not yet implemented: pipe expression")
val segments = pipe.segments
var valueDt = segments[0].type
var valueReg = if(pipe.void) codeGen.vmRegisters.nextFree() else resultRegister
fun addImplicitArgToSegment(segment: PtExpression, sourceReg: Int, sourceDt: DataType): PtExpression {
return when (segment) {
is PtFunctionCall -> {
val segWithArg = PtFunctionCall(segment.functionName, segment.void, segment.type, segment.position)
segWithArg.children.add(0, PtIdentifier(listOf(":vmreg-$sourceReg"), listOf(":vmreg-$sourceReg"), sourceDt, segment.position))
segWithArg
}
is PtBuiltinFunctionCall -> {
val segWithArg = PtBuiltinFunctionCall(segment.name, segment.void, segment.type, segment.position)
segWithArg.children.add(0, PtIdentifier(listOf(":vmreg-$sourceReg"), listOf(":vmreg-$sourceReg"), sourceDt, segment.position))
segWithArg
}
else -> throw AssemblyError("weird segment type")
}
}
val code = VmCodeChunk()
code += translateExpression(segments[0], valueReg)
for (segment in segments.subList(1, segments.size-1)) {
val sourceReg = valueReg
val sourceDt = valueDt
if(segment.type!=valueDt) {
valueDt = segment.type
valueReg = codeGen.vmRegisters.nextFree()
}
val segmentWithImplicitArgument = addImplicitArgToSegment(segment, sourceReg, sourceDt)
code += translateExpression(segmentWithImplicitArgument, valueReg)
}
val segWithArg = addImplicitArgToSegment(segments.last(), valueReg, valueDt)
code += translateExpression(segWithArg, resultRegister)
return code
}
private fun translate(check: PtContainmentCheck, resultRegister: Int): VmCodeChunk {

View File

@ -211,8 +211,14 @@ class IntermediateAstMaker(val program: Program) {
private fun transform(srcCall: FunctionCallExpression): PtFunctionCall {
val (target, _) = targetOf(srcCall.target)
val type = srcCall.inferType(program).getOrElse { throw FatalAstException("unknown dt") }
val call = PtFunctionCall(target, false, type, srcCall.position)
val type = srcCall.inferType(program).getOrElse {
if((srcCall.parent as? Pipe)?.segments?.last() === srcCall)
// for a pipe, the last segment is allowed to be a call to a function not returning anything.
DataType.UNDEFINED
else
throw FatalAstException("unknown dt $srcCall")
}
val call = PtFunctionCall(target, type==DataType.UNDEFINED, type, srcCall.position)
for (arg in srcCall.args)
call.add(transformExpression(arg))
return call
@ -291,8 +297,7 @@ class IntermediateAstMaker(val program: Program) {
PtLabel(label.name, label.position)
private fun transform(srcPipe: Pipe): PtPipe {
val type = srcPipe.segments.last().inferType(program).getOrElse { throw FatalAstException("unknown dt") }
val pipe = PtPipe(type, true, srcPipe.position)
val pipe = PtPipe(DataType.UNDEFINED, true, srcPipe.position)
pipe.add(transformExpression(srcPipe.source))
for (segment in srcPipe.segments)
pipe.add(transformExpression(segment))

View File

@ -3,8 +3,8 @@ TODO
For next release
^^^^^^^^^^^^^^^^
- vm codegen: Pipe expression
- vm: support no globals re-init option
- vm: make registers typed? so that it's immediately obvious what type they represent. Much like regular variables in memory.
- vm codegen/assembler: variable memory locations should also be referenced by the variable name instead of just the address, to make the output more human-readable
- vm: how to remove all unused subroutines? (in the assembly codegen, we let 64tass solve this for us)
- vm: rather than being able to jump to any 'address' (IPTR), use 'blocks' that have entry and exit points -> even better dead code elimination possible too

View File

@ -5,32 +5,30 @@
main {
sub calculate(ubyte value) -> uword {
when value {
1 -> return "one"
2 -> return "two"
3 -> return "three"
4,5,6 -> return "four to six"
else -> return "other"
}
sub func1(ubyte arg1) -> uword {
return arg1 * 3
}
sub func2(uword arg1) -> uword {
return arg1+1000
}
sub func3(uword arg1) {
txt.print_uw(arg1+2000)
txt.nl()
}
sub start() {
txt.print(calculate(0))
ubyte source = 99
uword result = func2(func1(cos8u(sin8u(source))))
txt.print_uw(result) ; 1043
txt.nl()
txt.print(calculate(1))
txt.nl()
txt.print(calculate(2))
txt.nl()
txt.print(calculate(3))
txt.nl()
txt.print(calculate(4))
txt.nl()
txt.print(calculate(5))
txt.nl()
txt.print(calculate(50))
result = source |> sin8u() |> cos8u() |> func1() |> func2()
txt.print_uw(result) ; 1043
txt.nl()
source |> sin8u() |> cos8u() |> func1() |> func3() ; 2043
; a "pixelshader":
; syscall1(8, 0) ; enable lo res creen

View File

@ -1,6 +1,6 @@
package prog8.vm
import kotlin.math.min
import kotlin.math.*
import kotlin.random.Random
/*
@ -20,6 +20,8 @@ SYSCALLS:
11 = rnd ; random BYTE
12 = wait ; wait certain amount of jiffies (1/60 sec)
13 = waitvsync ; wait on vsync
14 = sin8u
15 = cos8u
*/
enum class Syscall {
@ -37,7 +39,8 @@ enum class Syscall {
RND,
WAIT,
WAITVSYNC,
TMP_PRINT_UW
SIN8U,
COS8U
}
object SysCalls {
@ -92,8 +95,17 @@ object SysCalls {
Thread.sleep(millis)
}
Syscall.WAITVSYNC -> vm.waitvsync()
Syscall.TMP_PRINT_UW -> {
println("VM: UW=${vm.registers.getUW(0)}")
Syscall.SIN8U -> {
val arg = vm.registers.getUB(0).toDouble()
val rad = arg /256.0 * 2.0 * PI
val answer = truncate(128.0 + 127.5 * sin(rad))
vm.registers.setUB(0, answer.toUInt().toUByte())
}
Syscall.COS8U -> {
val arg = vm.registers.getUB(0).toDouble()
val rad = arg /256.0 * 2.0 * PI
val answer = truncate(128.0 + 127.5 * cos(rad))
vm.registers.setUB(0, answer.toUInt().toUByte())
}
else -> TODO("syscall ${call.name}")
}