From 469e0422160f73c95ac61b8c1efc0974fc15e760 Mon Sep 17 00:00:00 2001 From: Irmen de Jong Date: Fri, 4 Nov 2022 22:37:42 +0100 Subject: [PATCH] vm: replaced prog8_lib.string_compare and others with syscalls --- .../codegen/intermediate/ExpressionGen.kt | 125 ++++++++++++------ compiler/res/prog8lib/virtual/prog8_lib.p8 | 45 ------- compiler/res/prog8lib/virtual/string.p8 | 7 +- docs/source/todo.rst | 3 +- examples/test.p8 | 5 +- .../src/prog8/intermediate/IMSyscall.kt | 6 +- virtualmachine/src/prog8/vm/SysCalls.kt | 40 +++++- .../src/prog8/vm/VmProgramLoader.kt | 4 + 8 files changed, 143 insertions(+), 92 deletions(-) diff --git a/codeGenIntermediate/src/prog8/codegen/intermediate/ExpressionGen.kt b/codeGenIntermediate/src/prog8/codegen/intermediate/ExpressionGen.kt index 1aa179b03..6a890f51d 100644 --- a/codeGenIntermediate/src/prog8/codegen/intermediate/ExpressionGen.kt +++ b/codeGenIntermediate/src/prog8/codegen/intermediate/ExpressionGen.kt @@ -101,24 +101,49 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) { val iterable = codeGen.symbolTable.flat.getValue(check.iterable.targetName) as StStaticVariable when(iterable.dt) { DataType.STR -> { - val call = PtFunctionCall(listOf("prog8_lib", "string_contains"), false, DataType.UBYTE, check.position) - call.children.add(check.element) - call.children.add(check.iterable) - result += translate(call, resultRegister, resultFpRegister) + val push = IRCodeChunk(null, null) + push += IRInstruction(Opcode.PUSH, IRDataType.WORD, reg1=1) + result += push + result += translateExpression(check.element, 0, -1) + result += translateExpression(check.iterable, 1, -1) + val syscall = IRCodeChunk(null, null) + syscall += IRInstruction(Opcode.SYSCALL, value = IMSyscall.STRING_CONTAINS.number) + syscall += IRInstruction(Opcode.POP, IRDataType.WORD, reg1=1) + if(resultRegister!=0) + syscall += IRInstruction(Opcode.LOADR, IRDataType.BYTE, reg1=resultRegister, reg2=0) + result += syscall } DataType.ARRAY_UB, DataType.ARRAY_B -> { - val call = PtFunctionCall(listOf("prog8_lib", "bytearray_contains"), false, DataType.UBYTE, check.position) - call.children.add(check.element) - call.children.add(check.iterable) - call.children.add(PtNumber(DataType.UBYTE, iterable.length!!.toDouble(), iterable.position)) - result += translate(call, resultRegister, resultFpRegister) + val push = IRCodeChunk(null, null) + push += IRInstruction(Opcode.PUSH, IRDataType.WORD, reg1=1) + push += IRInstruction(Opcode.PUSH, IRDataType.BYTE, reg1=2) + result += push + result += translateExpression(check.element, 0, -1) + result += translateExpression(check.iterable, 1, -1) + val syscall = IRCodeChunk(null, null) + syscall += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=2, value = iterable.length!!) + syscall += IRInstruction(Opcode.SYSCALL, value = IMSyscall.BYTEARRAY_CONTAINS.number) + syscall += IRInstruction(Opcode.POP, IRDataType.BYTE, reg1=2) + syscall += IRInstruction(Opcode.POP, IRDataType.WORD, reg1=1) + if(resultRegister!=0) + syscall += IRInstruction(Opcode.LOADR, IRDataType.BYTE, reg1=resultRegister, reg2=0) + result += syscall } DataType.ARRAY_UW, DataType.ARRAY_W -> { - val call = PtFunctionCall(listOf("prog8_lib", "wordarray_contains"), false, DataType.UBYTE, check.position) - call.children.add(check.element) - call.children.add(check.iterable) - call.children.add(PtNumber(DataType.UBYTE, iterable.length!!.toDouble(), iterable.position)) - result += translate(call, resultRegister, resultFpRegister) + val push = IRCodeChunk(null, null) + push += IRInstruction(Opcode.PUSH, IRDataType.WORD, reg1=1) + push += IRInstruction(Opcode.PUSH, IRDataType.BYTE, reg1=2) + result += push + result += translateExpression(check.element, 0, -1) + result += translateExpression(check.iterable, 1, -1) + val syscall = IRCodeChunk(null, null) + syscall += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=2, value = iterable.length!!) + syscall += IRInstruction(Opcode.SYSCALL, value = IMSyscall.WORDARRAY_CONTAINS.number) + syscall += IRInstruction(Opcode.POP, IRDataType.BYTE, reg1=2) + syscall += IRInstruction(Opcode.POP, IRDataType.WORD, reg1=1) + if(resultRegister!=0) + syscall += IRInstruction(Opcode.LOADR, IRDataType.BYTE, reg1=resultRegister, reg2=0) + result += syscall } DataType.ARRAY_F -> throw AssemblyError("containment check in float-array not supported") else -> throw AssemblyError("weird iterable dt ${iterable.dt} for ${check.iterable.targetName}") @@ -307,17 +332,23 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) { addInstr(result, IRInstruction(ins, IRDataType.BYTE, reg1 = resultRegister, reg2 = zeroRegister), null) } else { if(binExpr.left.type==DataType.STR && binExpr.right.type==DataType.STR) { - val comparisonCall = PtFunctionCall(listOf("prog8_lib", "string_compare"), false, DataType.BYTE, Position.DUMMY) - comparisonCall.children.add(binExpr.left) - comparisonCall.children.add(binExpr.right) - result += translate(comparisonCall, resultRegister, -1) - val zeroRegister = codeGen.registers.nextFree() - addInstr(result, IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=zeroRegister, value=0), null) - val instr = if(greaterEquals) - IRInstruction(Opcode.SGES, IRDataType.BYTE, reg1=resultRegister, reg2=zeroRegister) + require(codeGen.registers.peekNext() > 1) + val push = IRCodeChunk(null, null) + push += IRInstruction(Opcode.PUSH, IRDataType.WORD, reg1=1) + result += push + result += translateExpression(binExpr.left, 0, -1) + result += translateExpression(binExpr.right, 1, -1) + val syscall = IRCodeChunk(null, null) + syscall += IRInstruction(Opcode.SYSCALL, value = IMSyscall.COMPARE_STRINGS.number) + if(resultRegister!=0) + syscall += IRInstruction(Opcode.LOADR, vmDt, reg1=resultRegister, reg2=0) + syscall += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=1, value=0) + syscall += if(greaterEquals) + IRInstruction(Opcode.SGES, IRDataType.BYTE, reg1=resultRegister, reg2=1) else - IRInstruction(Opcode.SGTS, IRDataType.BYTE, reg1=resultRegister, reg2=zeroRegister) - addInstr(result, instr, null) + IRInstruction(Opcode.SGTS, IRDataType.BYTE, reg1=resultRegister, reg2=1) + syscall += IRInstruction(Opcode.POP, IRDataType.WORD, reg1=1) + result += syscall } else { val rightResultReg = codeGen.registers.nextFree() result += translateExpression(binExpr.left, resultRegister, -1) @@ -357,17 +388,23 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) { addInstr(result, IRInstruction(ins, IRDataType.BYTE, reg1 = resultRegister, reg2 = zeroRegister), null) } else { if(binExpr.left.type==DataType.STR && binExpr.right.type==DataType.STR) { - val comparisonCall = PtFunctionCall(listOf("prog8_lib", "string_compare"), false, DataType.BYTE, Position.DUMMY) - comparisonCall.children.add(binExpr.left) - comparisonCall.children.add(binExpr.right) - result += translate(comparisonCall, resultRegister, -1) - val zeroRegister = codeGen.registers.nextFree() - addInstr(result, IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=zeroRegister, value=0), null) - val ins = if(lessEquals) - IRInstruction(Opcode.SLES, IRDataType.BYTE, reg1=resultRegister, reg2=zeroRegister) + require(codeGen.registers.peekNext() > 1) + val push = IRCodeChunk(null, null) + push += IRInstruction(Opcode.PUSH, IRDataType.WORD, reg1=1) + result += push + result += translateExpression(binExpr.left, 0, -1) + result += translateExpression(binExpr.right, 1, -1) + val syscall = IRCodeChunk(null, null) + syscall += IRInstruction(Opcode.SYSCALL, value = IMSyscall.COMPARE_STRINGS.number) + if(resultRegister!=0) + syscall += IRInstruction(Opcode.LOADR, vmDt, reg1=resultRegister, reg2=0) + syscall += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=1, value=0) + syscall += if(lessEquals) + IRInstruction(Opcode.SLES, IRDataType.BYTE, reg1=resultRegister, reg2=1) else - IRInstruction(Opcode.SLTS, IRDataType.BYTE, reg1=resultRegister, reg2=zeroRegister) - addInstr(result, ins, null) + IRInstruction(Opcode.SLTS, IRDataType.BYTE, reg1=resultRegister, reg2=1) + syscall += IRInstruction(Opcode.POP, IRDataType.WORD, reg1=1) + result += syscall } else { val rightResultReg = codeGen.registers.nextFree() result += translateExpression(binExpr.left, resultRegister, -1) @@ -403,13 +440,21 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) { } } else { if(binExpr.left.type==DataType.STR && binExpr.right.type==DataType.STR) { - val comparisonCall = PtFunctionCall(listOf("prog8_lib", "string_compare"), false, DataType.BYTE, Position.DUMMY) - comparisonCall.children.add(binExpr.left) - comparisonCall.children.add(binExpr.right) - result += translate(comparisonCall, resultRegister, -1) + require(codeGen.registers.peekNext() > 1) + val push = IRCodeChunk(null, null) + push += IRInstruction(Opcode.PUSH, IRDataType.WORD, reg1=1) + result += push + result += translateExpression(binExpr.left, 0, -1) + result += translateExpression(binExpr.right, 1, -1) + val syscall = IRCodeChunk(null, null) + syscall += IRInstruction(Opcode.SYSCALL, value = IMSyscall.COMPARE_STRINGS.number) + syscall += IRInstruction(Opcode.POP, IRDataType.WORD, reg1=1) + if(resultRegister!=0) + syscall += IRInstruction(Opcode.LOADR, vmDt, reg1=resultRegister, reg2=0) if(!notEquals) - addInstr(result, IRInstruction(Opcode.INV, vmDt, reg1=resultRegister), null) - addInstr(result, IRInstruction(Opcode.AND, vmDt, reg1=resultRegister, value=1), null) + syscall += IRInstruction(Opcode.INV, vmDt, reg1=resultRegister) + syscall += IRInstruction(Opcode.AND, vmDt, reg1=resultRegister, value=1) + result += syscall } else { val rightResultReg = codeGen.registers.nextFree() result += translateExpression(binExpr.left, resultRegister, -1) diff --git a/compiler/res/prog8lib/virtual/prog8_lib.p8 b/compiler/res/prog8lib/virtual/prog8_lib.p8 index 318baafea..359a0b248 100644 --- a/compiler/res/prog8lib/virtual/prog8_lib.p8 +++ b/compiler/res/prog8lib/virtual/prog8_lib.p8 @@ -3,49 +3,4 @@ prog8_lib { %option force_output - sub string_contains(ubyte needle, str haystack) -> ubyte { - repeat { - if @(haystack)==0 - return false - if @(haystack)==needle - return true - haystack++ - } - } - - sub bytearray_contains(ubyte needle, uword haystack_ptr, ubyte num_elements) -> ubyte { - haystack_ptr-- - while num_elements { - if haystack_ptr[num_elements]==needle - return true - num_elements-- - } - return false - } - - sub wordarray_contains(uword needle, uword haystack_ptr, ubyte num_elements) -> ubyte { - haystack_ptr += (num_elements-1) * 2 - while num_elements { - if peekw(haystack_ptr)==needle - return true - haystack_ptr -= 2 - num_elements-- - } - return false - } - - sub string_compare(str st1, str st2) -> byte { - ; Compares two strings for sorting. - ; Returns -1 (255), 0 or 1 depending on wether string1 sorts before, equal or after string2. - ; Note that you can also directly compare strings and string values with eachother using - ; comparison operators ==, < etcetera (it will use strcmp for you under water automatically). - %ir {{ - loadm.w r0,prog8_lib.string_compare.st1 - loadm.w r1,prog8_lib.string_compare.st2 - syscall 29 - return - }} - } - } - diff --git a/compiler/res/prog8lib/virtual/string.p8 b/compiler/res/prog8lib/virtual/string.p8 index 1c5729b69..03d0b5c38 100644 --- a/compiler/res/prog8lib/virtual/string.p8 +++ b/compiler/res/prog8lib/virtual/string.p8 @@ -83,7 +83,12 @@ string { ; Returns -1 (255), 0 or 1 depending on wether string1 sorts before, equal or after string2. ; Note that you can also directly compare strings and string values with eachother using ; comparison operators ==, < etcetera (it will use strcmp for you under water automatically). - return prog8_lib.string_compare(st1, st2) + %ir {{ + loadm.w r0,string.compare.st1 + loadm.w r1,string.compare.st2 + syscall 29 + return + }} } sub lower(str st) -> ubyte { diff --git a/docs/source/todo.rst b/docs/source/todo.rst index 98bdc1f56..4982d6873 100644 --- a/docs/source/todo.rst +++ b/docs/source/todo.rst @@ -3,8 +3,6 @@ TODO For next release ^^^^^^^^^^^^^^^^ -- fix expericodegen crashes from missing functions from virtual/prog8_lib.p8 - ... @@ -19,6 +17,7 @@ Future Things and Ideas ^^^^^^^^^^^^^^^^^^^^^^^ Compiler: +- ir/vm: SYSCALL opcode should take args in r65500, r65501 etc instead of r0, r1. Then also remove excess PUSH/POP of regs to save them. - create BSS section in output program and put StStaticVariables in there with bss=true. Don't forget to add init code to zero out everything that was put in bss. If array in bss->only zero ONCE! So requires self-modifying code - ir: mechanism to determine for chunks which registers are getting input values from "outside" - ir: mechanism to determine for chunks which registers are passing values out? (i.e. are used again in another chunk) diff --git a/examples/test.p8 b/examples/test.p8 index 226ccc08d..02fa048ba 100644 --- a/examples/test.p8 +++ b/examples/test.p8 @@ -15,10 +15,11 @@ main { uword value2 = 3333 txt.print_ub(value in name1) txt.print_ub('c' in name1) - txt.print_ub(name1 == name2) - txt.print_ub(name1 < name2) txt.print_ub(value in arr1) txt.print_ub(value2 in arr2) + txt.print_ub(name1 == name2) + txt.print_ub(name1 < name2) + txt.print_ub(name1 >= name2) } } diff --git a/intermediate/src/prog8/intermediate/IMSyscall.kt b/intermediate/src/prog8/intermediate/IMSyscall.kt index 60b3dd57a..7bf5cc27e 100644 --- a/intermediate/src/prog8/intermediate/IMSyscall.kt +++ b/intermediate/src/prog8/intermediate/IMSyscall.kt @@ -17,5 +17,9 @@ enum class IMSyscall(val number: Int) { ALL_FLOAT(10009), REVERSE_BYTES(10010), REVERSE_WORDS(10011), - REVERSE_FLOATS(10012) + REVERSE_FLOATS(10012), + COMPARE_STRINGS(10013), + STRING_CONTAINS(10014), + BYTEARRAY_CONTAINS(10015), + WORDARRAY_CONTAINS(10016) } \ No newline at end of file diff --git a/virtualmachine/src/prog8/vm/SysCalls.kt b/virtualmachine/src/prog8/vm/SysCalls.kt index 03b85e09d..a589699b8 100644 --- a/virtualmachine/src/prog8/vm/SysCalls.kt +++ b/virtualmachine/src/prog8/vm/SysCalls.kt @@ -77,7 +77,10 @@ enum class Syscall { RNDFSEED, RND, RNDW, - RNDF + RNDF, + STRING_CONTAINS, + BYTEARRAY_CONTAINS, + WORDARRAY_CONTAINS } object SysCalls { @@ -311,6 +314,41 @@ object SysCalls { Syscall.RNDF -> { vm.registers.setFloat(0, vm.randomGeneratorFloats.nextFloat()) } + Syscall.STRING_CONTAINS -> { + val char = vm.registers.getUB(0).toInt().toChar() + val stringAddr = vm.registers.getUW(1) + val string = vm.memory.getString(stringAddr.toInt()) + vm.registers.setUB(0, if(char in string) 1u else 0u) + } + Syscall.BYTEARRAY_CONTAINS -> { + val value = vm.registers.getUB(0) + var array = vm.registers.getUW(1).toInt() + var length = vm.registers.getUB(2) + while(length>0u) { + if(vm.memory.getUB(array)==value) { + vm.registers.setUB(0, 1u) + return + } + array++ + length-- + } + vm.registers.setUB(0, 0u) + } + Syscall.WORDARRAY_CONTAINS -> { + // r0.w = value, r1.w = array, r2.b = array length + val value = vm.registers.getUW(0) + var array = vm.registers.getUW(1).toInt() + var length = vm.registers.getUB(2) + while(length>0u) { + if(vm.memory.getUW(array)==value) { + vm.registers.setUB(0, 1u) + return + } + array += 2 + length-- + } + vm.registers.setUB(0, 0u) + } else -> throw AssemblyError("missing syscall ${call.name}") } } diff --git a/virtualmachine/src/prog8/vm/VmProgramLoader.kt b/virtualmachine/src/prog8/vm/VmProgramLoader.kt index b4e7873d4..d09a79c38 100644 --- a/virtualmachine/src/prog8/vm/VmProgramLoader.kt +++ b/virtualmachine/src/prog8/vm/VmProgramLoader.kt @@ -117,6 +117,10 @@ class VmProgramLoader { IMSyscall.REVERSE_BYTES.number -> Syscall.REVERSE_BYTES IMSyscall.REVERSE_WORDS.number -> Syscall.REVERSE_WORDS IMSyscall.REVERSE_FLOATS.number -> Syscall.REVERSE_FLOATS + IMSyscall.COMPARE_STRINGS.number -> Syscall.COMPARE_STRINGS + IMSyscall.STRING_CONTAINS.number -> Syscall.STRING_CONTAINS + IMSyscall.BYTEARRAY_CONTAINS.number -> Syscall.BYTEARRAY_CONTAINS + IMSyscall.WORDARRAY_CONTAINS.number -> Syscall.WORDARRAY_CONTAINS else -> null }