added boolean() builtin function and use it to get rid of !=0 comparisons

This commit is contained in:
Irmen de Jong 2022-06-14 22:31:13 +02:00
parent bf9d120081
commit cc174b7b85
10 changed files with 87 additions and 71 deletions

View File

@ -44,6 +44,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program,
"abs" -> funcAbs(fcall, func, resultToStack, resultRegister, sscope)
"any", "all" -> funcAnyAll(fcall, func, resultToStack, resultRegister, sscope)
"sgn" -> funcSgn(fcall, func, resultToStack, resultRegister, sscope)
"boolean" -> funcBoolean(fcall, func, resultToStack, resultRegister, sscope)
"rnd", "rndw" -> funcRnd(func, resultToStack, resultRegister, sscope)
"sqrt16" -> funcSqrt16(fcall, func, resultToStack, resultRegister, sscope)
"rol" -> funcRol(fcall)
@ -697,6 +698,28 @@ internal class BuiltinFunctionsAsmGen(private val program: Program,
}
}
private fun funcBoolean(fcall: IFunctionCall, func: FSignature, resultToStack: Boolean, resultRegister: RegisterOrPair?, scope: Subroutine?) {
when (val dt = fcall.args.single().inferType(program).getOr(DataType.UNDEFINED)) {
in ByteDatatypes -> {
assignAsmGen.assignExpressionToRegister(fcall.args.single(), RegisterOrPair.A, dt==DataType.BYTE)
}
in WordDatatypes -> {
assignAsmGen.assignExpressionToRegister(fcall.args.single(), RegisterOrPair.AY, dt==DataType.WORD)
asmgen.out(" sty P8ZP_SCRATCH_B1 | ora P8ZP_SCRATCH_B1")
}
DataType.FLOAT -> {
assignAsmGen.assignExpressionToRegister(fcall.args.single(), RegisterOrPair.FAC1, true)
asmgen.out(" jsr floats.SIGN")
}
else -> throw AssemblyError("weird type")
}
if(resultToStack)
asmgen.out(" sta P8ESTACK_LO,x | dex")
else if(resultRegister!=null)
assignAsmGen.assignRegisterByte(AsmAssignTarget.fromRegisters(resultRegister, false, scope, program, asmgen), CpuRegister.A)
}
private fun funcRnd(func: FSignature, resultToStack: Boolean, resultRegister: RegisterOrPair?, scope: Subroutine?) {
when(func.name) {
"rnd" -> {

View File

@ -321,61 +321,6 @@ internal class AssignmentAsmGen(private val program: Program,
if(!expr.inferType(program).isInteger)
return false
/*
TODO re-add these optimizations? after we improved the unneeded addition of !=0 expressions
if(expr.operator=="and") {
val dt = expr.left.inferType(program).getOrElse { throw AssemblyError("weird dt") }
if (dt in ByteDatatypes) {
assignExpressionToRegister(expr.left, RegisterOrPair.A, dt==DataType.BYTE || dt==DataType.WORD)
asmgen.out(" pha")
assignExpressionToVariable(expr.right, "P8ZP_SCRATCH_B1", dt, expr.definingSubroutine)
asmgen.out(" pla | and P8ZP_SCRATCH_B1")
if(assign.target.datatype in ByteDatatypes)
assignRegisterByte(assign.target, CpuRegister.A)
else {
asmgen.out(" ldy #0")
assignRegisterpairWord(assign.target, RegisterOrPair.AY)
}
return true
}
else throw AssemblyError("weird dt for and, expected byte $expr @${expr.position}")
}
else if(expr.operator=="or") {
val dt = expr.left.inferType(program).getOrElse { throw AssemblyError("weird dt") }
if (dt in ByteDatatypes) {
assignExpressionToRegister(expr.left, RegisterOrPair.A, dt==DataType.BYTE || dt==DataType.WORD)
asmgen.out(" pha")
assignExpressionToVariable(expr.right, "P8ZP_SCRATCH_B1", dt, expr.definingSubroutine)
asmgen.out(" pla | ora P8ZP_SCRATCH_B1")
if(assign.target.datatype in ByteDatatypes)
assignRegisterByte(assign.target, CpuRegister.A)
else {
asmgen.out(" ldy #0")
assignRegisterpairWord(assign.target, RegisterOrPair.AY)
}
return true
}
else throw AssemblyError("weird dt for or, expected byte $expr @${expr.position}")
}
else if(expr.operator=="xor") {
val dt = expr.left.inferType(program).getOrElse { throw AssemblyError("weird dt") }
if (dt in ByteDatatypes) {
assignExpressionToRegister(expr.left, RegisterOrPair.A, dt==DataType.BYTE || dt==DataType.WORD)
asmgen.out(" pha")
assignExpressionToVariable(expr.right, "P8ZP_SCRATCH_B1", dt, expr.definingSubroutine)
asmgen.out(" pla | eor P8ZP_SCRATCH_B1")
if(assign.target.datatype in ByteDatatypes)
assignRegisterByte(assign.target, CpuRegister.A)
else {
asmgen.out(" ldy #0")
assignRegisterpairWord(assign.target, RegisterOrPair.AY)
}
return true
}
else throw AssemblyError("weird dt for xor, expected byte $expr @${expr.position}")
}
*/
if(expr.operator!="+" && expr.operator!="-")
return false

View File

@ -32,6 +32,7 @@ internal class BuiltinFuncGen(private val codeGen: CodeGen, private val exprGen:
"callrom" -> throw AssemblyError("callrom() is for cx16 target only")
"msb" -> funcMsb(call, resultRegister)
"lsb" -> funcLsb(call, resultRegister)
"boolean" -> funcBoolean(call, resultRegister)
"memory" -> funcMemory(call, resultRegister)
"peek" -> funcPeek(call, resultRegister)
"peekw" -> funcPeekW(call, resultRegister)
@ -354,6 +355,30 @@ internal class BuiltinFuncGen(private val codeGen: CodeGen, private val exprGen:
return code
}
private fun funcBoolean(call: PtBuiltinFunctionCall, resultRegister: Int): VmCodeChunk {
val code = VmCodeChunk()
val vmDt = codeGen.vmType(call.args[0].type)
when (vmDt) {
VmDataType.FLOAT -> {
val fpValueReg = codeGen.vmRegisters.nextFreeFloat()
val fpSignReg = codeGen.vmRegisters.nextFreeFloat()
code += exprGen.translateExpression(call.args.single(), -1, fpValueReg)
code += VmCodeInstruction(Opcode.SGN, VmDataType.FLOAT, fpReg1 = fpSignReg, fpReg2 = fpValueReg)
code += VmCodeInstruction(Opcode.FTOSB, VmDataType.FLOAT, reg1 = resultRegister, fpReg1 = fpSignReg)
}
VmDataType.WORD -> {
val msbReg = codeGen.vmRegisters.nextFree()
code += exprGen.translateExpression(call.args.single(), resultRegister, -1)
code += VmCodeInstruction(Opcode.MSIG, VmDataType.BYTE, reg1 = msbReg, reg2=resultRegister)
code += VmCodeInstruction(Opcode.OR, VmDataType.BYTE, reg1=resultRegister, reg2=msbReg)
}
else -> {
code += exprGen.translateExpression(call.args.single(), resultRegister, -1)
}
}
return code
}
private fun funcRolRor(opcode: Opcode, call: PtBuiltinFunctionCall, resultRegister: Int): VmCodeChunk {
val vmDt = codeGen.vmType(call.args[0].type)
val code = VmCodeChunk()

View File

@ -864,13 +864,9 @@ internal class AstChecker(private val program: Program,
}
}
"and", "or", "xor" -> {
// only integer numeric operands accepted, and if literal constants, only boolean values accepted (0 or 1)
// only integer numeric operands accepted
if(leftDt !in IntegerDatatypes || rightDt !in IntegerDatatypes)
errors.err("logical operator can only be used on boolean operands", expr.right.position)
val constLeft = expr.left.constValue(program)
val constRight = expr.right.constValue(program)
if(constLeft!=null && constLeft.number.toInt() !in 0..1 || constRight!=null && constRight.number.toInt() !in 0..1)
errors.err("const literal argument of logical operator must be boolean (0 or 1)", expr.position)
}
"&", "|", "^" -> {
// only integer numeric operands accepted

View File

@ -248,13 +248,12 @@ internal class StatementReorderer(val program: Program,
}
}
else if(expr.operator in LogicalOperators) {
// make sure that logical expressions like "var and other-logical-expression
// is rewritten as "var!=0 and other-logical-expression", to avoid bitwise boolean and
// generating the wrong results later
// TODO use bool(expr) instead of expr!=0 rewriting
fun wrapped(expr: Expression): Expression =
BinaryExpression(expr, "!=", NumericLiteral(DataType.UBYTE, 0.0, expr.position), expr.position)
fun wrapped(expr: Expression): Expression {
return if(expr.inferType(program).isBytes)
expr
else
FunctionCallExpression(IdentifierReference(listOf("boolean"), expr.position), mutableListOf(expr), expr.position)
}
fun isLogicalExpr(expr: Expression?): Boolean {
if(expr is BinaryExpression && expr.operator in (LogicalOperators + ComparisonOperators))

View File

@ -233,5 +233,14 @@ internal class VariousCleanups(val program: Program, val errors: IErrorReporter,
override fun after(functionCallStatement: FunctionCallStatement, parent: Node): Iterable<IAstModification> {
return tryReplaceCallWithGosub(functionCallStatement, parent, program, options)
}
override fun after(functionCallExpr: FunctionCallExpression, parent: Node): Iterable<IAstModification> {
if(functionCallExpr.target.nameInSource==listOf("boolean")) {
if(functionCallExpr.args[0].inferType(program).isBytes) {
return listOf(IAstModification.ReplaceNode(functionCallExpr, functionCallExpr.args[0], parent))
}
}
return noModifications
}
}

View File

@ -106,6 +106,7 @@ private val functionSignatures: List<FSignature> = listOf(
FSignature("lsb" , true, listOf(FParam("value", arrayOf(DataType.UWORD, DataType.WORD))), DataType.UBYTE) { a, p, prg -> oneIntArgOutputInt(a, p, prg) { x: Int -> (x and 255).toDouble() } },
FSignature("msb" , true, listOf(FParam("value", arrayOf(DataType.UWORD, DataType.WORD))), DataType.UBYTE) { a, p, prg -> oneIntArgOutputInt(a, p, prg) { x: Int -> (x ushr 8 and 255).toDouble()} },
FSignature("mkword" , true, listOf(FParam("msb", arrayOf(DataType.UBYTE)), FParam("lsb", arrayOf(DataType.UBYTE))), DataType.UWORD, ::builtinMkword),
FSignature("boolean" , true, listOf(FParam("value", NumericDatatypes)), DataType.UBYTE, ::builtinBoolean),
FSignature("peek" , true, listOf(FParam("address", arrayOf(DataType.UWORD))), DataType.UBYTE),
FSignature("peekw" , true, listOf(FParam("address", arrayOf(DataType.UWORD))), DataType.UWORD),
FSignature("poke" , false, listOf(FParam("address", arrayOf(DataType.UWORD)), FParam("value", arrayOf(DataType.UBYTE))), null),
@ -250,6 +251,23 @@ private fun builtinMkword(args: List<Expression>, position: Position, program: P
return NumericLiteral(DataType.UWORD, result.toDouble(), position)
}
private fun builtinBoolean(args: List<Expression>, position: Position, program: Program): NumericLiteral {
if (args.size != 1)
throw SyntaxError("boolean requires one argument", position)
val constvalue = args[0].constValue(program) ?: throw NotConstArgumentException()
return if(constvalue.number==0.0)
NumericLiteral(DataType.UBYTE, 0.0, args[0].position)
else if(constvalue.type==DataType.UBYTE)
constvalue
else if(constvalue.type==DataType.FLOAT)
NumericLiteral(DataType.UBYTE, constvalue.number.sign, args[0].position)
else {
val value = constvalue.number.toInt()
val byteValue = ((value ushr 8) or value) and 255
NumericLiteral(DataType.UBYTE, byteValue.toDouble(), args[0].position)
}
}
private fun builtinSgn(args: List<Expression>, position: Position, program: Program): NumericLiteral {
if (args.size != 1)
throw SyntaxError("sgn requires one argument", position)

View File

@ -775,6 +775,10 @@ sort(array)
Miscellaneous
^^^^^^^^^^^^^
boolean(x)
Returns a byte value representing the boolean (truthy) value of x, where x can be any numeric type.
This means it returns 0 (false) when x equals 0, and a value other than 0 otherwise.
cmp(x,y)
Compare the integer value x to integer value y. Doesn't return a value or boolean result, only sets the processor's status bits!
You can use a conditional jumps (``if_cc`` etcetera) to act on this.

View File

@ -3,9 +3,6 @@ TODO
For next release
^^^^^^^^^^^^^^^^
- add a builtin function 'bool' that takes a numeric value and returns ubyte false if it was zero and true otherwise
- statementreorderer: after(expr: BinaryExpression): get rid of unneeded !=0 added to logical expressions
by using this bool() function instead for the operand if it is not a byte type
- optimize logical expressions in attemptAssignOptimizedBinexpr()
- add McCarthy evaluation to shortcircuit and/or expressions. First do ifs by splitting them up? Then do expressions that compute a value?
...

View File

@ -1,5 +1,4 @@
%import textio
;%import test_stack
%zeropage basicsafe
; NOTE: meant to test to virtual machine output target (use -target vitual)
@ -117,6 +116,7 @@ main {
txt.print_ub(value)
txt.nl()
; a "pixelshader":
; sys.gfx_enable(0) ; enable lo res screen
; ubyte shifter