min/max give proper error for string args

als implmented more vm builtin functions/syscalls
This commit is contained in:
Irmen de Jong 2022-04-13 23:05:50 +02:00
parent bf7f4bba7b
commit 349e5a15e9
6 changed files with 263 additions and 31 deletions

5
.idea/misc.xml generated
View File

@ -22,4 +22,9 @@
<component name="ProjectRootManager" version="2" languageLevel="JDK_11" project-jdk-name="11" project-jdk-type="JavaSDK"> <component name="ProjectRootManager" version="2" languageLevel="JDK_11" project-jdk-name="11" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/out" /> <output url="file://$PROJECT_DIR$/out" />
</component> </component>
<component name="SwUserDefinedSpecifications">
<option name="specTypeByUrl">
<map />
</option>
</component>
</project> </project>

View File

@ -2,6 +2,7 @@ package prog8.codegen.virtual
import prog8.code.StStaticVariable import prog8.code.StStaticVariable
import prog8.code.ast.* import prog8.code.ast.*
import prog8.code.core.ArrayToElementTypes
import prog8.code.core.AssemblyError import prog8.code.core.AssemblyError
import prog8.code.core.DataType import prog8.code.core.DataType
import prog8.vm.Opcode import prog8.vm.Opcode
@ -13,10 +14,10 @@ internal class BuiltinFuncGen(private val codeGen: CodeGen, private val exprGen:
fun translate(call: PtBuiltinFunctionCall, resultRegister: Int): VmCodeChunk { fun translate(call: PtBuiltinFunctionCall, resultRegister: Int): VmCodeChunk {
return when(call.name) { return when(call.name) {
"max" -> funcMax(call, resultRegister) "max" -> funcMax(call, resultRegister)
"min" -> TODO() "min" -> funcMin(call, resultRegister)
"sum" -> TODO() "sum" -> funcSum(call, resultRegister)
"any" -> TODO() "any" -> funcAny(call, resultRegister)
"all" -> TODO() "all" -> funcAll(call, resultRegister)
"abs" -> TODO("abs once we can compare plus minus") "abs" -> TODO("abs once we can compare plus minus")
"cmp" -> TODO("cmp() can't be used on vm because no processor status bits implemented") "cmp" -> TODO("cmp() can't be used on vm because no processor status bits implemented")
"sgn" -> funcSgn(call, resultRegister) "sgn" -> funcSgn(call, resultRegister)
@ -70,22 +71,113 @@ internal class BuiltinFuncGen(private val codeGen: CodeGen, private val exprGen:
} }
} }
private fun funcMax(call: PtBuiltinFunctionCall, resultRegister: Int): VmCodeChunk { private fun funcSum(call: PtBuiltinFunctionCall, resultRegister: Int): VmCodeChunk {
val arrayName = call.args[0] as PtIdentifier
val array = codeGen.symbolTable.flat.getValue(arrayName.targetName) as StStaticVariable
val elementDt: VmDataType = codeGen.vmType(ArrayToElementTypes.getValue(array.dt))
val syscall =
when(array.dt) {
DataType.ARRAY_UB,
DataType.ARRAY_B -> Syscall.SUM_BYTE
DataType.ARRAY_UW,
DataType.ARRAY_W -> Syscall.SUM_WORD
DataType.ARRAY_F -> TODO("float sum")
else -> throw IllegalArgumentException("weird type")
}
val code = VmCodeChunk() val code = VmCodeChunk()
val arrayName = (call.args.single() as PtIdentifier).targetName code += exprGen.translateExpression(call.args[0], 0)
val array = codeGen.symbolTable.flat.getValue(arrayName) as StStaticVariable code += VmCodeInstruction(Opcode.LOAD, VmDataType.BYTE, reg1=1, value=array.length)
when (array.dt) { code += VmCodeInstruction(Opcode.SYSCALL, value=syscall.ordinal)
DataType.ARRAY_UW, DataType.ARRAY_W -> { if(resultRegister!=0)
TODO("max word array") code += VmCodeInstruction(Opcode.LOADR, elementDt, reg1=resultRegister, reg2=0)
} return code
DataType.STR -> { }
TODO("max string")
}
else -> {
TODO("max byte array")
}
}
private fun funcAny(call: PtBuiltinFunctionCall, resultRegister: Int): VmCodeChunk {
val arrayName = call.args[0] as PtIdentifier
val array = codeGen.symbolTable.flat.getValue(arrayName.targetName) as StStaticVariable
val elementDt: VmDataType = codeGen.vmType(ArrayToElementTypes.getValue(array.dt))
val syscall =
when(array.dt) {
DataType.ARRAY_UB,
DataType.ARRAY_B -> Syscall.ANY_BYTE
DataType.ARRAY_UW,
DataType.ARRAY_W -> Syscall.ANY_WORD
DataType.ARRAY_F -> TODO("float any")
else -> throw IllegalArgumentException("weird type")
}
val code = VmCodeChunk()
code += exprGen.translateExpression(call.args[0], 0)
code += VmCodeInstruction(Opcode.LOAD, VmDataType.BYTE, reg1=1, value=array.length)
code += VmCodeInstruction(Opcode.SYSCALL, value=syscall.ordinal)
if(resultRegister!=0)
code += VmCodeInstruction(Opcode.LOADR, elementDt, reg1=resultRegister, reg2=0)
return code
}
private fun funcAll(call: PtBuiltinFunctionCall, resultRegister: Int): VmCodeChunk {
val arrayName = call.args[0] as PtIdentifier
val array = codeGen.symbolTable.flat.getValue(arrayName.targetName) as StStaticVariable
val elementDt: VmDataType = codeGen.vmType(ArrayToElementTypes.getValue(array.dt))
val syscall =
when(array.dt) {
DataType.ARRAY_UB,
DataType.ARRAY_B -> Syscall.ALL_BYTE
DataType.ARRAY_UW,
DataType.ARRAY_W -> Syscall.ALL_WORD
DataType.ARRAY_F -> TODO("float all")
else -> throw IllegalArgumentException("weird type")
}
val code = VmCodeChunk()
code += exprGen.translateExpression(call.args[0], 0)
code += VmCodeInstruction(Opcode.LOAD, VmDataType.BYTE, reg1=1, value=array.length)
code += VmCodeInstruction(Opcode.SYSCALL, value=syscall.ordinal)
if(resultRegister!=0)
code += VmCodeInstruction(Opcode.LOADR, elementDt, reg1=resultRegister, reg2=0)
return code
}
private fun funcMax(call: PtBuiltinFunctionCall, resultRegister: Int): VmCodeChunk {
val arrayName = call.args[0] as PtIdentifier
val array = codeGen.symbolTable.flat.getValue(arrayName.targetName) as StStaticVariable
val elementDt: VmDataType = codeGen.vmType(ArrayToElementTypes.getValue(array.dt))
val syscall =
when(array.dt) {
DataType.ARRAY_UB -> Syscall.MAX_UBYTE
DataType.ARRAY_B -> Syscall.MAX_BYTE
DataType.ARRAY_UW -> Syscall.MAX_UWORD
DataType.ARRAY_W -> Syscall.MAX_WORD
DataType.ARRAY_F -> TODO("float max")
else -> throw IllegalArgumentException("weird type")
}
val code = VmCodeChunk()
code += exprGen.translateExpression(call.args[0], 0)
code += VmCodeInstruction(Opcode.LOAD, VmDataType.BYTE, reg1=1, value=array.length)
code += VmCodeInstruction(Opcode.SYSCALL, value=syscall.ordinal)
if(resultRegister!=0)
code += VmCodeInstruction(Opcode.LOADR, elementDt, reg1=resultRegister, reg2=0)
return code
}
private fun funcMin(call: PtBuiltinFunctionCall, resultRegister: Int): VmCodeChunk {
val arrayName = call.args[0] as PtIdentifier
val array = codeGen.symbolTable.flat.getValue(arrayName.targetName) as StStaticVariable
val elementDt: VmDataType = codeGen.vmType(ArrayToElementTypes.getValue(array.dt))
val syscall =
when(array.dt) {
DataType.ARRAY_UB -> Syscall.MIN_UBYTE
DataType.ARRAY_B -> Syscall.MIN_BYTE
DataType.ARRAY_UW -> Syscall.MIN_UWORD
DataType.ARRAY_W -> Syscall.MIN_WORD
DataType.ARRAY_F -> TODO("float min")
else -> throw IllegalArgumentException("weird type")
}
val code = VmCodeChunk()
code += exprGen.translateExpression(call.args[0], 0)
code += VmCodeInstruction(Opcode.LOAD, VmDataType.BYTE, reg1=1, value=array.length)
code += VmCodeInstruction(Opcode.SYSCALL, value=syscall.ordinal)
if(resultRegister!=0)
code += VmCodeInstruction(Opcode.LOADR, elementDt, reg1=resultRegister, reg2=0)
return code return code
} }
@ -151,7 +243,7 @@ internal class BuiltinFuncGen(private val codeGen: CodeGen, private val exprGen:
when(array.dt) { when(array.dt) {
DataType.ARRAY_UB, DataType.ARRAY_B, DataType.STR -> Syscall.REVERSE_BYTES DataType.ARRAY_UB, DataType.ARRAY_B, DataType.STR -> Syscall.REVERSE_BYTES
DataType.ARRAY_UW, DataType.ARRAY_W -> Syscall.REVERSE_WORDS DataType.ARRAY_UW, DataType.ARRAY_W -> Syscall.REVERSE_WORDS
DataType.FLOAT -> TODO("reverse floats") DataType.ARRAY_F -> TODO("float reverse")
else -> throw IllegalArgumentException("weird type to reverse") else -> throw IllegalArgumentException("weird type to reverse")
} }
val code = VmCodeChunk() val code = VmCodeChunk()
@ -170,7 +262,7 @@ internal class BuiltinFuncGen(private val codeGen: CodeGen, private val exprGen:
DataType.ARRAY_B -> Syscall.SORT_BYTE DataType.ARRAY_B -> Syscall.SORT_BYTE
DataType.ARRAY_UW -> Syscall.SORT_UWORD DataType.ARRAY_UW -> Syscall.SORT_UWORD
DataType.ARRAY_W -> Syscall.SORT_WORD DataType.ARRAY_W -> Syscall.SORT_WORD
DataType.FLOAT -> TODO("float sort") DataType.ARRAY_F -> TODO("float sort")
DataType.STR -> Syscall.SORT_UBYTE DataType.STR -> Syscall.SORT_UBYTE
else -> throw IllegalArgumentException("weird type to sort") else -> throw IllegalArgumentException("weird type to sort")
} }

View File

@ -1069,11 +1069,15 @@ internal class AstChecker(private val program: Program,
errors.err("swap requires args of numerical type", position) errors.err("swap requires args of numerical type", position)
} }
else if(target.name=="all" || target.name=="any") { else if(target.name=="all" || target.name=="any") {
if((args[0] as? AddressOf)?.identifier?.targetVarDecl(program)?.datatype == DataType.STR) { if((args[0] as? AddressOf)?.identifier?.targetVarDecl(program)?.datatype == DataType.STR
|| args[0].inferType(program).getOr(DataType.STR) == DataType.STR) {
errors.err("any/all on a string is useless (is always true unless the string is empty)", position) errors.err("any/all on a string is useless (is always true unless the string is empty)", position)
} }
if(args[0].inferType(program).getOr(DataType.STR) == DataType.STR) { }
errors.err("any/all on a string is useless (is always true unless the string is empty)", position) else if(target.name=="min" || target.name=="max") {
if((args[0] as? AddressOf)?.identifier?.targetVarDecl(program)?.datatype == DataType.STR
|| args[0].inferType(program).getOr(DataType.STR) == DataType.STR) {
errors.err("min/max operate on arrays, not on strings", position)
} }
} }
} else if(target is Subroutine) { } else if(target is Subroutine) {

View File

@ -3,9 +3,7 @@ TODO
For next release For next release
^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^
- min/max/any/all should give error when string arg is given instead of array
- can't use abs() etc in pipe expression because return type depends on argument type - can't use abs() etc in pipe expression because return type depends on argument type
- vm: add support for all builtin functions
- pipe operator: allow non-unary function calls in the pipe that specify the other argument(s) in the calls. - pipe operator: allow non-unary function calls in the pipe that specify the other argument(s) in the calls.
- createAssemblyAndAssemble(): make it possible to actually get rid of the VarDecl nodes by fixing the rest of the code mentioned there. - createAssemblyAndAssemble(): make it possible to actually get rid of the VarDecl nodes by fixing the rest of the code mentioned there.
- allow "xxx" * constexpr (where constexpr is not a number literal), now gives expression error not same type - allow "xxx" * constexpr (where constexpr is not a number literal), now gives expression error not same type
@ -14,6 +12,7 @@ For next release
If we can do that why not perhaps also able to inline multi-line subroutines? Why would it be limited to just 1 line? Maybe to protect against code size bloat. If we can do that why not perhaps also able to inline multi-line subroutines? Why would it be limited to just 1 line? Maybe to protect against code size bloat.
Inlined subroutines cannot contain further nested subroutines! Inlined subroutines cannot contain further nested subroutines!
Once this works, look for library subroutines that should be inlined. Once this works, look for library subroutines that should be inlined.
- vm: add support for status bits, status-branch instructions, and cmp() and abs() functions.
... ...

View File

@ -9,8 +9,6 @@ main {
word[] values = [1111, -222, -9999, 88, 20222, 0, 0, 1111] word[] values = [1111, -222, -9999, 88, 20222, 0, 0, 1111]
word[] values2 = [0,0,0,0,0,1,0,0,0] word[] values2 = [0,0,0,0,0,1,0,0,0]
txt.print_w(max("abcde"))
txt.nl()
txt.print_w(max(values)) txt.print_w(max(values))
txt.nl() txt.nl()
txt.print_w(min(values)) txt.print_w(min(values))

View File

@ -25,8 +25,22 @@ SYSCALLS:
16 = sort_byte array 16 = sort_byte array
17 = sort_uword array 17 = sort_uword array
18 = sort_word array 18 = sort_word array
19 = reverse_bytes array 19 = max_ubyte array
20 = reverse_words array 20 = max_byte array
21 = max_uword array
22 = max_word array
23 = min_ubyte array
24 = min_byte array
25 = min_uword array
26 = min_word array
27 = sum_byte array
28 = sum_word array
29 = any_byte array
30 = any_word array
31 = all_byte array
32 = all_word array
33 = reverse_bytes array
34 = reverse_words array
*/ */
enum class Syscall { enum class Syscall {
@ -49,8 +63,22 @@ enum class Syscall {
SORT_BYTE, SORT_BYTE,
SORT_UWORD, SORT_UWORD,
SORT_WORD, SORT_WORD,
REVERSE_BYTES, // TODO not as syscall MAX_UBYTE,
REVERSE_WORDS // TODO not as syscall MAX_BYTE,
MAX_UWORD,
MAX_WORD,
MIN_UBYTE,
MIN_BYTE,
MIN_UWORD,
MIN_WORD,
SUM_BYTE,
SUM_WORD,
ANY_BYTE,
ANY_WORD,
ALL_BYTE,
ALL_WORD,
REVERSE_BYTES,
REVERSE_WORDS
} }
object SysCalls { object SysCalls {
@ -168,6 +196,112 @@ object SysCalls {
vm.memory.setUW(address+index*2, value) vm.memory.setUW(address+index*2, value)
} }
} }
Syscall.MAX_UBYTE -> {
val address = vm.registers.getUW(0).toInt()
val length = vm.registers.getUB(1).toInt()
val addresses = IntProgression.fromClosedRange(address, address+length*2-2, 2)
val value = addresses.map { vm.memory.getUB(it) }.maxOf { it }
vm.registers.setUB(0, value)
}
Syscall.MAX_BYTE -> {
val address = vm.registers.getUW(0).toInt()
val length = vm.registers.getUB(1).toInt()
val addresses = IntProgression.fromClosedRange(address, address+length*2-2, 2)
val value = addresses.map { vm.memory.getSB(it) }.maxOf { it }
vm.registers.setSB(0, value)
}
Syscall.MAX_UWORD -> {
val address = vm.registers.getUW(0).toInt()
val length = vm.registers.getUB(1).toInt()
val addresses = IntProgression.fromClosedRange(address, address+length*2-2, 2)
val value = addresses.map { vm.memory.getUW(it) }.maxOf { it }
vm.registers.setUW(0, value)
}
Syscall.MAX_WORD -> {
val address = vm.registers.getUW(0).toInt()
val length = vm.registers.getUB(1).toInt()
val addresses = IntProgression.fromClosedRange(address, address+length*2-2, 2)
val value = addresses.map { vm.memory.getSW(it) }.maxOf { it }
vm.registers.setSW(0, value)
}
Syscall.MIN_UBYTE -> {
val address = vm.registers.getUW(0).toInt()
val length = vm.registers.getUB(1).toInt()
val addresses = IntProgression.fromClosedRange(address, address+length*2-2, 2)
val value = addresses.map { vm.memory.getUB(it) }.minOf { it }
vm.registers.setUB(0, value)
}
Syscall.MIN_BYTE -> {
val address = vm.registers.getUW(0).toInt()
val length = vm.registers.getUB(1).toInt()
val addresses = IntProgression.fromClosedRange(address, address+length*2-2, 2)
val value = addresses.map { vm.memory.getSB(it) }.minOf { it }
vm.registers.setSB(0, value)
}
Syscall.MIN_UWORD -> {
val address = vm.registers.getUW(0).toInt()
val length = vm.registers.getUB(1).toInt()
val addresses = IntProgression.fromClosedRange(address, address+length*2-2, 2)
val value = addresses.map { vm.memory.getUW(it) }.minOf { it }
vm.registers.setUW(0, value)
}
Syscall.MIN_WORD -> {
val address = vm.registers.getUW(0).toInt()
val length = vm.registers.getUB(1).toInt()
val addresses = IntProgression.fromClosedRange(address, address+length*2-2, 2)
val value = addresses.map { vm.memory.getSW(it) }.minOf { it }
vm.registers.setSW(0, value)
}
Syscall.SUM_BYTE -> {
val address = vm.registers.getUW(0).toInt()
val length = vm.registers.getUB(1).toInt()
val addresses = IntProgression.fromClosedRange(address, address+length*2-2, 2)
val value = addresses.map { vm.memory.getUB(it) }.sum()
vm.registers.setUB(0, value.toUByte())
}
Syscall.SUM_WORD -> {
val address = vm.registers.getUW(0).toInt()
val length = vm.registers.getUB(1).toInt()
val addresses = IntProgression.fromClosedRange(address, address+length*2-2, 2)
val value = addresses.map { vm.memory.getUW(it) }.sum()
vm.registers.setUW(0, value.toUShort())
}
Syscall.ANY_BYTE -> {
val address = vm.registers.getUW(0).toInt()
val length = vm.registers.getUB(1).toInt()
val addresses = IntProgression.fromClosedRange(address, address+length*2-2, 2)
if(addresses.any { vm.memory.getUB(it).toInt()!=0 })
vm.registers.setUB(0, 1u)
else
vm.registers.setUB(0, 0u)
}
Syscall.ANY_WORD -> {
val address = vm.registers.getUW(0).toInt()
val length = vm.registers.getUB(1).toInt()
val addresses = IntProgression.fromClosedRange(address, address+length*2-2, 2)
if(addresses.any { vm.memory.getUW(it).toInt()!=0 })
vm.registers.setUB(0, 1u)
else
vm.registers.setUB(0, 0u)
}
Syscall.ALL_BYTE -> {
val address = vm.registers.getUW(0).toInt()
val length = vm.registers.getUB(1).toInt()
val addresses = IntProgression.fromClosedRange(address, address+length*2-2, 2)
if(addresses.all { vm.memory.getUB(it).toInt()!=0 })
vm.registers.setUB(0, 1u)
else
vm.registers.setUB(0, 0u)
}
Syscall.ALL_WORD -> {
val address = vm.registers.getUW(0).toInt()
val length = vm.registers.getUB(1).toInt()
val addresses = IntProgression.fromClosedRange(address, address+length*2-2, 2)
if(addresses.all { vm.memory.getUW(it).toInt()!=0 })
vm.registers.setUB(0, 1u)
else
vm.registers.setUB(0, 0u)
}
else -> TODO("syscall ${call.name}") else -> TODO("syscall ${call.name}")
} }
} }