mirror of
https://github.com/irmen/prog8.git
synced 2024-09-29 08:57:51 +00:00
fix logical expressions on arbitrary values, for now with boolean() around the operands
This commit is contained in:
parent
06184bdcb1
commit
ef92451d1a
@ -323,6 +323,7 @@ internal class AssignmentAsmGen(private val program: Program,
|
|||||||
return false
|
return false
|
||||||
|
|
||||||
// optimized code for logical expressions
|
// optimized code for logical expressions
|
||||||
|
// note: due to boolean() wrapping of operands, we can use simple bitwise and/or/xor
|
||||||
// TODO assume (hope) cx16.r9 isn't used for anything else during the use of this...
|
// TODO assume (hope) cx16.r9 isn't used for anything else during the use of this...
|
||||||
if(expr.operator=="and") {
|
if(expr.operator=="and") {
|
||||||
val iDt = expr.left.inferType(program)
|
val iDt = expr.left.inferType(program)
|
||||||
@ -2150,10 +2151,7 @@ internal class AssignmentAsmGen(private val program: Program,
|
|||||||
RegisterOrPair.XY -> asmgen.out(" ldx #0 | ldy #0")
|
RegisterOrPair.XY -> asmgen.out(" ldx #0 | ldy #0")
|
||||||
RegisterOrPair.FAC1, RegisterOrPair.FAC2 -> throw AssemblyError("expected typecasted byte to float")
|
RegisterOrPair.FAC1, RegisterOrPair.FAC2 -> throw AssemblyError("expected typecasted byte to float")
|
||||||
in Cx16VirtualRegisters -> {
|
in Cx16VirtualRegisters -> {
|
||||||
asmgen.out(
|
asmgen.out(" stz cx16.${target.register.toString().lowercase()} | stz cx16.${target.register.toString().lowercase()}+1")
|
||||||
" stz cx16.${
|
|
||||||
target.register.toString().lowercase()
|
|
||||||
} | stz cx16.${target.register.toString().lowercase()}+1")
|
|
||||||
}
|
}
|
||||||
else -> throw AssemblyError("weird register")
|
else -> throw AssemblyError("weird register")
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
package prog8.compiler.astprocessing
|
package prog8.compiler.astprocessing
|
||||||
|
|
||||||
|
import prog8.ast.IFunctionCall
|
||||||
import prog8.ast.Node
|
import prog8.ast.Node
|
||||||
import prog8.ast.Program
|
import prog8.ast.Program
|
||||||
import prog8.ast.base.SyntaxError
|
import prog8.ast.base.SyntaxError
|
||||||
@ -7,10 +8,7 @@ import prog8.ast.expressions.*
|
|||||||
import prog8.ast.statements.*
|
import prog8.ast.statements.*
|
||||||
import prog8.ast.walk.AstWalker
|
import prog8.ast.walk.AstWalker
|
||||||
import prog8.ast.walk.IAstModification
|
import prog8.ast.walk.IAstModification
|
||||||
import prog8.code.core.Encoding
|
import prog8.code.core.*
|
||||||
import prog8.code.core.ICompilationTarget
|
|
||||||
import prog8.code.core.IErrorReporter
|
|
||||||
import prog8.code.core.NumericDatatypes
|
|
||||||
|
|
||||||
|
|
||||||
class AstPreprocessor(val program: Program, val errors: IErrorReporter, val compTarget: ICompilationTarget) : AstWalker() {
|
class AstPreprocessor(val program: Program, val errors: IErrorReporter, val compTarget: ICompilationTarget) : AstWalker() {
|
||||||
@ -98,6 +96,30 @@ class AstPreprocessor(val program: Program, val errors: IErrorReporter, val comp
|
|||||||
val containment = ContainmentCheck(expr.left, expr.right, expr.position)
|
val containment = ContainmentCheck(expr.left, expr.right, expr.position)
|
||||||
return listOf(IAstModification.ReplaceNode(expr, containment, parent))
|
return listOf(IAstModification.ReplaceNode(expr, containment, parent))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// To enable simple bitwise and/or/xor/not instructions in the codegen for the logical and/or/xor/not,
|
||||||
|
// we wrap the operands in a call to boolean() if required so that they are 0 or 1 as needed.
|
||||||
|
// Making the codegen more generic to do this by itself all the time will generate much larger
|
||||||
|
// code because it is hard to decide there if the value conversion to 0 or 1 is needed or not,
|
||||||
|
// so a lot of useless checks and conversions are added. Here we can be smarter so the codegen
|
||||||
|
// can just rely on the correct value of the operands (0 or 1) if they're boolean, and just use bitwise instructions.
|
||||||
|
if(expr.operator in LogicalOperators) {
|
||||||
|
fun wrapped(expr: Expression): Expression {
|
||||||
|
return if(expr is IFunctionCall && expr.target.nameInSource==listOf("boolean"))
|
||||||
|
expr
|
||||||
|
else if(expr is BinaryExpression && expr.operator in LogicalOperators+ComparisonOperators)
|
||||||
|
expr
|
||||||
|
else
|
||||||
|
FunctionCallExpression(IdentifierReference(listOf("boolean"), expr.position), mutableListOf(expr), expr.position)
|
||||||
|
}
|
||||||
|
|
||||||
|
return listOf(
|
||||||
|
IAstModification.ReplaceNode(expr.left, wrapped(expr.left), expr),
|
||||||
|
IAstModification.ReplaceNode(expr.right, wrapped(expr.right), expr)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
return noModifications
|
return noModifications
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -190,7 +190,6 @@ internal class StatementReorderer(val program: Program,
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun after(expr: BinaryExpression, parent: Node): Iterable<IAstModification> {
|
override fun after(expr: BinaryExpression, parent: Node): Iterable<IAstModification> {
|
||||||
|
|
||||||
// ConstValue <associativeoperator> X --> X <associativeoperator> ConstValue
|
// ConstValue <associativeoperator> X --> X <associativeoperator> ConstValue
|
||||||
// (this should be done by the ExpressionSimplifier when optimizing is enabled,
|
// (this should be done by the ExpressionSimplifier when optimizing is enabled,
|
||||||
// but the current assembly code generator for IF statements now also depends on it, so we do it here regardless of optimization.)
|
// but the current assembly code generator for IF statements now also depends on it, so we do it here regardless of optimization.)
|
||||||
@ -247,38 +246,6 @@ internal class StatementReorderer(val program: Program,
|
|||||||
else -> return noModifications
|
else -> return noModifications
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if(expr.operator in LogicalOperators) {
|
|
||||||
fun wrapped(expr: Expression): Expression {
|
|
||||||
return if(expr is IFunctionCall && expr.target.nameInSource==listOf("boolean"))
|
|
||||||
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))
|
|
||||||
return true
|
|
||||||
if(expr is PrefixExpression && expr.operator in LogicalOperators)
|
|
||||||
return true
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
return if(isLogicalExpr(expr.left)) {
|
|
||||||
if(isLogicalExpr(expr.right))
|
|
||||||
noModifications
|
|
||||||
else
|
|
||||||
listOf(IAstModification.ReplaceNode(expr.right, wrapped(expr.right), expr))
|
|
||||||
} else {
|
|
||||||
if(isLogicalExpr(expr.right))
|
|
||||||
listOf(IAstModification.ReplaceNode(expr.left, wrapped(expr.left), expr))
|
|
||||||
else {
|
|
||||||
listOf(
|
|
||||||
IAstModification.ReplaceNode(expr.left, wrapped(expr.left), expr),
|
|
||||||
IAstModification.ReplaceNode(expr.right, wrapped(expr.right), expr)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return noModifications
|
return noModifications
|
||||||
}
|
}
|
||||||
|
@ -102,6 +102,24 @@ class TypecastsAdder(val program: Program, val options: CompilationOptions, val
|
|||||||
return noModifications
|
return noModifications
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun after(expr: PrefixExpression, parent: Node): Iterable<IAstModification> {
|
||||||
|
if(expr.operator=="not") {
|
||||||
|
val logical = parent as? BinaryExpression
|
||||||
|
if(logical!=null && logical.operator in LogicalOperators) {
|
||||||
|
// not x as operand in a logical expression --> cast it to ubyte
|
||||||
|
val cast = TypecastExpression(expr, DataType.UBYTE, true, expr.position)
|
||||||
|
return listOf(IAstModification.ReplaceNode(expr, cast, parent))
|
||||||
|
}
|
||||||
|
val boolCast = parent as? IFunctionCall
|
||||||
|
if(boolCast!=null && boolCast.target.nameInSource==listOf("boolean")) {
|
||||||
|
// boolean(not x) --> (not x) as ubyte
|
||||||
|
val cast = TypecastExpression(expr, DataType.UBYTE, true, expr.position)
|
||||||
|
return listOf(IAstModification.ReplaceNode(boolCast as Node, cast, boolCast.parent))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return noModifications
|
||||||
|
}
|
||||||
|
|
||||||
override fun after(assignment: Assignment, parent: Node): Iterable<IAstModification> {
|
override fun after(assignment: Assignment, parent: Node): Iterable<IAstModification> {
|
||||||
// see if a typecast is needed to convert the value's type into the proper target type
|
// see if a typecast is needed to convert the value's type into the proper target type
|
||||||
val valueItype = assignment.value.inferType(program)
|
val valueItype = assignment.value.inferType(program)
|
||||||
|
@ -12,6 +12,7 @@ import prog8.ast.statements.FunctionCallStatement
|
|||||||
import prog8.ast.walk.AstWalker
|
import prog8.ast.walk.AstWalker
|
||||||
import prog8.ast.walk.IAstModification
|
import prog8.ast.walk.IAstModification
|
||||||
import prog8.code.core.*
|
import prog8.code.core.*
|
||||||
|
import prog8.code.target.VMTarget
|
||||||
|
|
||||||
|
|
||||||
internal class VariousCleanups(val program: Program, val errors: IErrorReporter, val options: CompilationOptions): AstWalker() {
|
internal class VariousCleanups(val program: Program, val errors: IErrorReporter, val options: CompilationOptions): AstWalker() {
|
||||||
@ -233,5 +234,38 @@ internal class VariousCleanups(val program: Program, val errors: IErrorReporter,
|
|||||||
override fun after(functionCallStatement: FunctionCallStatement, parent: Node): Iterable<IAstModification> {
|
override fun after(functionCallStatement: FunctionCallStatement, parent: Node): Iterable<IAstModification> {
|
||||||
return tryReplaceCallWithGosub(functionCallStatement, parent, program, options)
|
return tryReplaceCallWithGosub(functionCallStatement, parent, program, options)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun after(functionCallExpr: FunctionCallExpression, parent: Node): Iterable<IAstModification> {
|
||||||
|
if(functionCallExpr.target.nameInSource==listOf("boolean")) {
|
||||||
|
// boolean(expr) can be removed if expr is a logical expression or comparison expression itself, or boolean()
|
||||||
|
val binexpr = functionCallExpr.args.single() as? BinaryExpression
|
||||||
|
if(binexpr!=null && binexpr.operator in LogicalOperators + ComparisonOperators) {
|
||||||
|
return listOf(IAstModification.ReplaceNode(functionCallExpr, binexpr, parent))
|
||||||
|
}
|
||||||
|
val prefixExpression = functionCallExpr.args.single() as? PrefixExpression
|
||||||
|
if(prefixExpression!=null && prefixExpression.operator in LogicalOperators) {
|
||||||
|
return listOf(IAstModification.ReplaceNode(functionCallExpr, prefixExpression, parent))
|
||||||
|
}
|
||||||
|
val fcall = functionCallExpr.args.single() as? IFunctionCall
|
||||||
|
if(fcall!=null && fcall.target.nameInSource==listOf("boolean")) {
|
||||||
|
return listOf(IAstModification.ReplaceNode(functionCallExpr, fcall as Node, parent))
|
||||||
|
}
|
||||||
|
|
||||||
|
if(options.compTarget.name == VMTarget.NAME) {
|
||||||
|
// if target is Virtual, remove ALL boolean() conversions of operands to logical expressions
|
||||||
|
// we can do this because the logical and/or/xor/not in the virtual machine behave correctly
|
||||||
|
// with operand values other than 0 and 1 (0 = false, everything else=true, result is always 0 or 1)
|
||||||
|
val logicalExpr = functionCallExpr.parent as? BinaryExpression
|
||||||
|
if(logicalExpr!=null && logicalExpr.operator in LogicalOperators + ComparisonOperators) {
|
||||||
|
return listOf(IAstModification.ReplaceNode(functionCallExpr, functionCallExpr.args.single(), parent))
|
||||||
|
}
|
||||||
|
val prefixExpr = functionCallExpr.parent as? PrefixExpression
|
||||||
|
if(prefixExpr!=null && prefixExpr.operator in LogicalOperators) {
|
||||||
|
return listOf(IAstModification.ReplaceNode(functionCallExpr, functionCallExpr.args.single(), parent))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return noModifications
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -285,14 +285,14 @@ class TestOptimization: FunSpec({
|
|||||||
"""
|
"""
|
||||||
val result = compileText(C64Target(), false, src, writeAssembly = false)!!
|
val result = compileText(C64Target(), false, src, writeAssembly = false)!!
|
||||||
|
|
||||||
// bb = (( not bb as uword) or not ww)
|
// bb = (not bb or (not ww as ubyte) )
|
||||||
val bbAssign = result.program.entrypoint.statements.last() as Assignment
|
val bbAssign = result.program.entrypoint.statements.last() as Assignment
|
||||||
val expr = bbAssign.value as BinaryExpression
|
val expr = bbAssign.value as BinaryExpression
|
||||||
expr.operator shouldBe "or"
|
expr.operator shouldBe "or"
|
||||||
expr.left shouldBe instanceOf<TypecastExpression>() // casted to word
|
expr.left shouldBe instanceOf<PrefixExpression>()
|
||||||
expr.right shouldBe instanceOf<PrefixExpression>()
|
expr.right shouldBe instanceOf<TypecastExpression>() // not of word typecast into ubyte
|
||||||
expr.left.inferType(result.program).getOrElse { fail("dt") } shouldBe DataType.UWORD
|
expr.left.inferType(result.program).getOrElse { fail("dt") } shouldBe DataType.UBYTE
|
||||||
expr.right.inferType(result.program).getOrElse { fail("dt") } shouldBe DataType.UWORD
|
expr.right.inferType(result.program).getOrElse { fail("dt") } shouldBe DataType.UBYTE
|
||||||
expr.inferType(result.program).getOrElse { fail("dt") } shouldBe DataType.UBYTE
|
expr.inferType(result.program).getOrElse { fail("dt") } shouldBe DataType.UBYTE
|
||||||
|
|
||||||
val options = CompilationOptions(OutputType.PRG, CbmPrgLauncherType.BASIC, ZeropageType.DONTUSE, emptyList(),
|
val options = CompilationOptions(OutputType.PRG, CbmPrgLauncherType.BASIC, ZeropageType.DONTUSE, emptyList(),
|
||||||
@ -301,10 +301,11 @@ class TestOptimization: FunSpec({
|
|||||||
compTarget = C64Target(),
|
compTarget = C64Target(),
|
||||||
loadAddress = 0u, outputDir= outputDir)
|
loadAddress = 0u, outputDir= outputDir)
|
||||||
result.program.processAstBeforeAsmGeneration(options, ErrorReporterForTests())
|
result.program.processAstBeforeAsmGeneration(options, ErrorReporterForTests())
|
||||||
|
printProgram(result.program)
|
||||||
|
|
||||||
// assignment is now split into:
|
// assignment is now split into:
|
||||||
// bb = not bb
|
// bb = not bb
|
||||||
// bb = (bb or not ww)
|
// bb = (bb or (not ww as ubyte)
|
||||||
|
|
||||||
val assigns = result.program.entrypoint.statements.filterIsInstance<Assignment>()
|
val assigns = result.program.entrypoint.statements.filterIsInstance<Assignment>()
|
||||||
val bbAssigns = assigns.filter { it.value !is NumericLiteral }
|
val bbAssigns = assigns.filter { it.value !is NumericLiteral }
|
||||||
@ -320,9 +321,10 @@ class TestOptimization: FunSpec({
|
|||||||
val bbAssigns1expr = bbAssigns[1].value as BinaryExpression
|
val bbAssigns1expr = bbAssigns[1].value as BinaryExpression
|
||||||
bbAssigns1expr.operator shouldBe "or"
|
bbAssigns1expr.operator shouldBe "or"
|
||||||
(bbAssigns1expr.left as? IdentifierReference)?.nameInSource shouldBe listOf("bb")
|
(bbAssigns1expr.left as? IdentifierReference)?.nameInSource shouldBe listOf("bb")
|
||||||
bbAssigns1expr.right shouldBe instanceOf<PrefixExpression>()
|
bbAssigns1expr.right shouldBe instanceOf<TypecastExpression>()
|
||||||
(bbAssigns1expr.right as PrefixExpression).operator shouldBe "not"
|
val castedValue = (bbAssigns1expr.right as TypecastExpression).expression as PrefixExpression
|
||||||
((bbAssigns1expr.right as PrefixExpression).expression as? IdentifierReference)?.nameInSource shouldBe listOf("ww")
|
castedValue.operator shouldBe "not"
|
||||||
|
(castedValue.expression as? IdentifierReference)?.nameInSource shouldBe listOf("ww")
|
||||||
bbAssigns1expr.inferType(result.program).getOrElse { fail("dt") } shouldBe DataType.UBYTE
|
bbAssigns1expr.inferType(result.program).getOrElse { fail("dt") } shouldBe DataType.UBYTE
|
||||||
|
|
||||||
val asm = generateAssembly(result.program, options)
|
val asm = generateAssembly(result.program, options)
|
||||||
|
174
compiler/test/arithmetic/logical.p8
Normal file
174
compiler/test/arithmetic/logical.p8
Normal file
@ -0,0 +1,174 @@
|
|||||||
|
%import textio
|
||||||
|
%zeropage basicsafe
|
||||||
|
|
||||||
|
main {
|
||||||
|
|
||||||
|
sub ftrue(ubyte arg) -> ubyte {
|
||||||
|
arg++
|
||||||
|
return 128
|
||||||
|
}
|
||||||
|
|
||||||
|
sub ffalse(ubyte arg) -> ubyte {
|
||||||
|
arg++
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
sub start() {
|
||||||
|
ubyte ub1 = 2
|
||||||
|
ubyte ub2 = 4
|
||||||
|
ubyte ub3 = 8
|
||||||
|
ubyte ub4 = 0
|
||||||
|
ubyte bvalue
|
||||||
|
|
||||||
|
txt.print("bitwise or 14: ")
|
||||||
|
txt.print_ub(ub1 | ub2 | ub3 | ub4)
|
||||||
|
txt.nl()
|
||||||
|
txt.print("bitwise or 142: ")
|
||||||
|
txt.print_ub(ub1 | ub2 | ub3 | ub4 | 128)
|
||||||
|
txt.nl()
|
||||||
|
txt.print("bitwise and 0: ")
|
||||||
|
txt.print_ub(ub1 & ub2 & ub3 & ub4)
|
||||||
|
txt.nl()
|
||||||
|
txt.print("bitwise and 8: ")
|
||||||
|
txt.print_ub(ub3 & ub3 & 127)
|
||||||
|
txt.nl()
|
||||||
|
txt.print("bitwise xor 14: ")
|
||||||
|
txt.print_ub(ub1 ^ ub2 ^ ub3 ^ ub4)
|
||||||
|
txt.nl()
|
||||||
|
txt.print("bitwise xor 6: ")
|
||||||
|
txt.print_ub(ub1 ^ ub2 ^ ub3 ^ 8)
|
||||||
|
txt.nl()
|
||||||
|
txt.print("bitwise not 247: ")
|
||||||
|
txt.print_ub(~ub3)
|
||||||
|
txt.nl()
|
||||||
|
txt.print("bitwise not 255: ")
|
||||||
|
txt.print_ub(~ub4)
|
||||||
|
txt.nl()
|
||||||
|
|
||||||
|
txt.print("not 0: ")
|
||||||
|
bvalue = not ub3
|
||||||
|
txt.print_ub(bvalue)
|
||||||
|
if not ub1
|
||||||
|
txt.print(" / fail")
|
||||||
|
else
|
||||||
|
txt.print(" / ok")
|
||||||
|
txt.nl()
|
||||||
|
|
||||||
|
txt.print("not 1: ")
|
||||||
|
bvalue = not ub4
|
||||||
|
txt.print_ub(bvalue)
|
||||||
|
if not ub4
|
||||||
|
txt.print(" / ok")
|
||||||
|
else
|
||||||
|
txt.print(" / fail")
|
||||||
|
txt.nl()
|
||||||
|
bvalue = bvalue and 128
|
||||||
|
txt.print("bvl 1: ")
|
||||||
|
txt.print_ub(bvalue)
|
||||||
|
if bvalue and 128
|
||||||
|
txt.print(" / ok")
|
||||||
|
else
|
||||||
|
txt.print(" / fail")
|
||||||
|
txt.nl()
|
||||||
|
|
||||||
|
txt.print("and 1: ")
|
||||||
|
bvalue = ub1 and ub2 and ub3
|
||||||
|
txt.print_ub(bvalue)
|
||||||
|
if ub1 and ub2 and ub3
|
||||||
|
txt.print(" / ok")
|
||||||
|
else
|
||||||
|
txt.print(" / fail")
|
||||||
|
txt.nl()
|
||||||
|
txt.print("and 1: ")
|
||||||
|
bvalue = ub1 and ub2 and ub3 and 64
|
||||||
|
txt.print_ub(bvalue)
|
||||||
|
if ub1 and ub2 and ub3 and 64
|
||||||
|
txt.print(" / ok")
|
||||||
|
else
|
||||||
|
txt.print(" / fail")
|
||||||
|
txt.nl()
|
||||||
|
txt.print("and 1: ")
|
||||||
|
bvalue = ub1 and ub2 and ub3 and ftrue(99)
|
||||||
|
txt.print_ub(bvalue)
|
||||||
|
if ub1 and ub2 and ub3 and ftrue(99)
|
||||||
|
txt.print(" / ok")
|
||||||
|
else
|
||||||
|
txt.print(" / fail")
|
||||||
|
txt.nl()
|
||||||
|
txt.print("and 0: ")
|
||||||
|
bvalue = ub1 and ub2 and ub3 and ub4
|
||||||
|
txt.print_ub(bvalue)
|
||||||
|
if ub1 and ub2 and ub3 and ub4
|
||||||
|
txt.print(" / fail")
|
||||||
|
else
|
||||||
|
txt.print(" / ok")
|
||||||
|
txt.nl()
|
||||||
|
txt.print("and 0: ")
|
||||||
|
bvalue = ub1 and ub2 and ub3 and ffalse(99)
|
||||||
|
txt.print_ub(bvalue)
|
||||||
|
if ub1 and ub2 and ub3 and ffalse(99)
|
||||||
|
txt.print(" / fail")
|
||||||
|
else
|
||||||
|
txt.print(" / ok")
|
||||||
|
txt.nl()
|
||||||
|
|
||||||
|
txt.print(" or 1: ")
|
||||||
|
bvalue = ub1 or ub2 or ub3 or ub4
|
||||||
|
txt.print_ub(bvalue)
|
||||||
|
if ub1 or ub2 or ub3 or ub4
|
||||||
|
txt.print(" / ok")
|
||||||
|
else
|
||||||
|
txt.print(" / fail")
|
||||||
|
txt.nl()
|
||||||
|
txt.print(" or 1: ")
|
||||||
|
bvalue = ub4 or ub4 or 64
|
||||||
|
txt.print_ub(bvalue)
|
||||||
|
if ub4 or ub4 or 64
|
||||||
|
txt.print(" / ok")
|
||||||
|
else
|
||||||
|
txt.print(" / fail")
|
||||||
|
txt.nl()
|
||||||
|
txt.print(" or 1: ")
|
||||||
|
bvalue = ub1 or ub2 or ub3 or ftrue(99)
|
||||||
|
txt.print_ub(bvalue)
|
||||||
|
if ub1 or ub2 or ub3 or ftrue(99)
|
||||||
|
txt.print(" / ok")
|
||||||
|
else
|
||||||
|
txt.print(" / fail")
|
||||||
|
txt.nl()
|
||||||
|
|
||||||
|
txt.print("xor 1: ")
|
||||||
|
bvalue = ub1 xor ub2 xor ub3 xor ub4
|
||||||
|
txt.print_ub(bvalue)
|
||||||
|
if ub1 xor ub2 xor ub3 xor ub4
|
||||||
|
txt.print(" / ok")
|
||||||
|
else
|
||||||
|
txt.print(" / fail")
|
||||||
|
txt.nl()
|
||||||
|
txt.print("xor 1: ")
|
||||||
|
bvalue = ub1 xor ub2 xor ub3 xor ffalse(99)
|
||||||
|
txt.print_ub(bvalue)
|
||||||
|
if ub1 xor ub2 xor ub3 xor ffalse(99)
|
||||||
|
txt.print(" / ok")
|
||||||
|
else
|
||||||
|
txt.print(" / fail")
|
||||||
|
txt.nl()
|
||||||
|
|
||||||
|
txt.print("xor 0: ")
|
||||||
|
bvalue = ub1 xor ub2 xor ub3 xor ub4 xor true
|
||||||
|
txt.print_ub(bvalue)
|
||||||
|
if ub1 xor ub2 xor ub3 xor ub4 xor true
|
||||||
|
txt.print(" / fail")
|
||||||
|
else
|
||||||
|
txt.print(" / ok")
|
||||||
|
txt.nl()
|
||||||
|
txt.print("xor 0: ")
|
||||||
|
bvalue = ub1 xor ub2 xor ub3 xor ftrue(99)
|
||||||
|
txt.print_ub(bvalue)
|
||||||
|
if ub1 xor ub2 xor ub3 xor ftrue(99)
|
||||||
|
txt.print(" / fail")
|
||||||
|
else
|
||||||
|
txt.print(" / ok")
|
||||||
|
txt.nl()
|
||||||
|
}
|
||||||
|
}
|
@ -124,6 +124,10 @@ class PrefixExpression(val operator: String, var expression: Expression, overrid
|
|||||||
return when(operator) {
|
return when(operator) {
|
||||||
"+" -> inferred
|
"+" -> inferred
|
||||||
"~", "not" -> {
|
"~", "not" -> {
|
||||||
|
// note: "not" should ideally result in UBYTE (boolean) but the way the type system
|
||||||
|
// currently works means the result of an operator is the same type as the operand(s).
|
||||||
|
// So not(byte)->byte, not(word)->word. This is taken care of via a cast to ubyte later.
|
||||||
|
// If we give not a BYTE type here, the asmassignment validation will sometimes crash.
|
||||||
when(inferred.getOr(DataType.UNDEFINED)) {
|
when(inferred.getOr(DataType.UNDEFINED)) {
|
||||||
in ByteDatatypes -> InferredTypes.knownFor(DataType.UBYTE)
|
in ByteDatatypes -> InferredTypes.knownFor(DataType.UBYTE)
|
||||||
in WordDatatypes -> InferredTypes.knownFor(DataType.UWORD)
|
in WordDatatypes -> InferredTypes.knownFor(DataType.UWORD)
|
||||||
|
@ -3,7 +3,11 @@ TODO
|
|||||||
|
|
||||||
For next release
|
For next release
|
||||||
^^^^^^^^^^^^^^^^
|
^^^^^^^^^^^^^^^^
|
||||||
- can we optimize redundant calls to boolean() away? imageviewer.prg got larger because of them
|
- vm: fix and/or/xor to be bitwise again (need to remove optimization from VariousCleanups too?)
|
||||||
|
- vm: fix not to be bitwise not instead of boolean not
|
||||||
|
- 6502: fix not codegen to be bitwise not instead of boolean not (maybe need to change boolean() wrapping / variouscleanups)
|
||||||
|
all these testable with compiler/test/arithmetic/logical.p8
|
||||||
|
|
||||||
- add some more optimizations in vmPeepholeOptimizer
|
- add some more optimizations in vmPeepholeOptimizer
|
||||||
- vm Instruction needs to know what the read-registers/memory are, and what the write-register/memory is.
|
- vm Instruction needs to know what the read-registers/memory are, and what the write-register/memory is.
|
||||||
this info is needed for more advanced optimizations and later code generation steps.
|
this info is needed for more advanced optimizations and later code generation steps.
|
||||||
|
156
examples/test.p8
156
examples/test.p8
@ -5,147 +5,23 @@
|
|||||||
|
|
||||||
main {
|
main {
|
||||||
|
|
||||||
sub funcFalse() -> ubyte {
|
|
||||||
txt.print("false() ")
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
sub funcFalseWord() -> uword {
|
|
||||||
txt.print("falseword() ")
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
sub funcTrue() -> ubyte {
|
|
||||||
txt.print("ftrue() ")
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
sub func1(ubyte a1) -> ubyte {
|
|
||||||
txt.print("func1() ")
|
|
||||||
return a1>1
|
|
||||||
}
|
|
||||||
|
|
||||||
sub func2(ubyte a1) -> ubyte {
|
|
||||||
txt.print("func2() ")
|
|
||||||
return a1>2
|
|
||||||
}
|
|
||||||
|
|
||||||
sub func3(ubyte a1) -> ubyte {
|
|
||||||
txt.print("func3() ")
|
|
||||||
return a1>3
|
|
||||||
}
|
|
||||||
|
|
||||||
sub func4(ubyte a1) -> ubyte {
|
|
||||||
txt.print("func4() ")
|
|
||||||
return a1>4
|
|
||||||
}
|
|
||||||
|
|
||||||
sub funcw() -> uword {
|
|
||||||
txt.print("funcw() ")
|
|
||||||
return 9999
|
|
||||||
}
|
|
||||||
|
|
||||||
sub start() {
|
sub start() {
|
||||||
ubyte value
|
|
||||||
uword wvalue
|
|
||||||
|
|
||||||
ubyte ub1 = 11
|
|
||||||
ubyte ub2 = 22
|
|
||||||
ubyte ub3 = 33
|
|
||||||
ubyte ub4 = 44
|
|
||||||
ubyte ub5 = 55
|
|
||||||
|
|
||||||
ub4 = 0
|
|
||||||
txt.print("and with bytes: ")
|
|
||||||
ub5 = ub1 and ub2 and ub3 and ub4 and ub5
|
|
||||||
txt.print_ub(ub5)
|
|
||||||
txt.nl()
|
|
||||||
|
|
||||||
ub4 = 0
|
|
||||||
txt.print("or with bytes: ")
|
|
||||||
ub5 = ub1 or ub2 or ub3 or ub4 or ub5
|
|
||||||
txt.print_ub(ub5)
|
|
||||||
txt.nl()
|
|
||||||
|
|
||||||
txt.print("short and with false (word): ")
|
|
||||||
wvalue = funcw() and funcFalseWord() and funcw() and funcw()
|
|
||||||
txt.print_uw(wvalue)
|
|
||||||
txt.nl()
|
|
||||||
|
|
||||||
txt.print("short and with false: ")
|
|
||||||
value = func1(25) and funcFalse()
|
|
||||||
txt.print_ub(value)
|
|
||||||
txt.nl()
|
|
||||||
txt.print("short or with true: ")
|
|
||||||
value = func1(25) or funcTrue()
|
|
||||||
txt.print_ub(value)
|
|
||||||
txt.nl()
|
|
||||||
txt.print("short xor with false: ")
|
|
||||||
value = func1(25) xor funcFalse()
|
|
||||||
txt.print_ub(value)
|
|
||||||
txt.nl()
|
|
||||||
|
|
||||||
txt.print("and with false: ")
|
|
||||||
value = func1(25) and func2(25) and funcFalse() and func3(25) and func4(25)
|
|
||||||
txt.print_ub(value)
|
|
||||||
txt.nl()
|
|
||||||
txt.print("and with true: ")
|
|
||||||
value = func1(25) and func2(25) and funcTrue() and func3(25) and func4(25)
|
|
||||||
txt.print_ub(value)
|
|
||||||
txt.nl()
|
|
||||||
txt.print("or with false: ")
|
|
||||||
value = func1(0) or func2(0) or funcFalse() or func3(25) or func4(25)
|
|
||||||
txt.print_ub(value)
|
|
||||||
txt.nl()
|
|
||||||
txt.print("or with true: ")
|
|
||||||
value = func1(0) or func2(0) or funcTrue() or func3(25) or func4(25)
|
|
||||||
txt.print_ub(value)
|
|
||||||
txt.nl()
|
|
||||||
txt.print("xor with false: ")
|
|
||||||
value = func1(25) xor func2(25) xor funcFalse() xor func3(25) xor func4(25)
|
|
||||||
txt.print_ub(value)
|
|
||||||
txt.nl()
|
|
||||||
txt.print("xor with true: ")
|
|
||||||
value = func1(25) xor func2(25) xor funcTrue() xor func3(25) xor func4(25)
|
|
||||||
txt.print_ub(value)
|
|
||||||
txt.nl()
|
|
||||||
|
|
||||||
txt.print("\nif and with false: [nothing]: ")
|
|
||||||
if func1(25) and func2(25) and funcFalse() and func3(25) and func4(25)
|
|
||||||
txt.print("failure!")
|
|
||||||
txt.print("\nif and with true: [ok]: ")
|
|
||||||
if func1(25) and func2(25) and funcTrue() and func3(25) and func4(25)
|
|
||||||
txt.print("ok!")
|
|
||||||
txt.print("\nif or with false: [ok]: ")
|
|
||||||
if func1(0) or func2(0) or funcFalse() or func3(25) or func4(25)
|
|
||||||
txt.print("ok!")
|
|
||||||
txt.print("\nif or with true: [ok]: ")
|
|
||||||
if func1(0) or func2(0) or funcTrue() or func3(25) or func4(25)
|
|
||||||
txt.print("ok!")
|
|
||||||
txt.print("\nif xor with false: [nothing]: ")
|
|
||||||
if func1(25) xor func2(25) xor funcFalse() xor func3(25) xor func4(25)
|
|
||||||
txt.print("failure!")
|
|
||||||
txt.print("\nif xor with true: [ok]: ")
|
|
||||||
if func1(25) xor func2(25) xor funcTrue() xor func3(25) xor func4(25)
|
|
||||||
txt.print("ok!")
|
|
||||||
txt.nl()
|
|
||||||
|
|
||||||
; a "pixelshader":
|
; a "pixelshader":
|
||||||
; sys.gfx_enable(0) ; enable lo res screen
|
sys.gfx_enable(0) ; enable lo res screen
|
||||||
; ubyte shifter
|
ubyte shifter
|
||||||
;
|
|
||||||
; repeat {
|
repeat {
|
||||||
; uword xx
|
uword xx
|
||||||
; uword yy = 0
|
uword yy = 0
|
||||||
; repeat 240 {
|
repeat 240 {
|
||||||
; xx = 0
|
xx = 0
|
||||||
; repeat 320 {
|
repeat 320 {
|
||||||
; sys.gfx_plot(xx, yy, xx*yy + shifter as ubyte)
|
sys.gfx_plot(xx, yy, xx*yy + shifter as ubyte)
|
||||||
; xx++
|
xx++
|
||||||
; }
|
}
|
||||||
; yy++
|
yy++
|
||||||
; }
|
}
|
||||||
; shifter+=4
|
shifter+=4
|
||||||
; }
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,9 +8,9 @@ Virtual machine:
|
|||||||
65536 virtual floating point registers (32 bits single precision floats) fr0-fr65535
|
65536 virtual floating point registers (32 bits single precision floats) fr0-fr65535
|
||||||
65536 bytes of memory. Thus memory pointers (addresses) are limited to 16 bits.
|
65536 bytes of memory. Thus memory pointers (addresses) are limited to 16 bits.
|
||||||
Value stack, max 128 entries of 1 byte each.
|
Value stack, max 128 entries of 1 byte each.
|
||||||
Status registers: Carry, Zero, Negative.
|
Status flags: Carry, Zero, Negative. NOTE: status flags are only affected by the CMP instruction or explicit CLC/SEC!!!
|
||||||
|
logical or arithmetic operations DO NOT AFFECT THE STATUS FLAGS UNLESS EXPLICITLY NOTED!
|
||||||
Program to execute is not stored in this memory, it's just a separate list of instructions.
|
Program to execute is not stored in this memory, it's just a separate list of instructions.
|
||||||
|
|
||||||
Most instructions have an associated data type 'b','w','f'. (omitting it means 'b'/byte).
|
Most instructions have an associated data type 'b','w','f'. (omitting it means 'b'/byte).
|
||||||
Currently NO support for 24 or 32 bits integers.
|
Currently NO support for 24 or 32 bits integers.
|
||||||
Floating point operations are just 'f' typed regular instructions, and additionally there are
|
Floating point operations are just 'f' typed regular instructions, and additionally there are
|
||||||
|
@ -39,7 +39,7 @@ class VirtualMachine(val memory: Memory, program: List<Instruction>) {
|
|||||||
numIns++
|
numIns++
|
||||||
|
|
||||||
if(throttle && stepCount and 32767 == 0) {
|
if(throttle && stepCount and 32767 == 0) {
|
||||||
Thread.sleep(0, 10) // avoid 100% cpu core usage
|
Thread.sleep(1) // avoid 100% cpu core usage
|
||||||
}
|
}
|
||||||
|
|
||||||
if(stepCount and 0xffffff == 0) {
|
if(stepCount and 0xffffff == 0) {
|
||||||
@ -1132,8 +1132,14 @@ class VirtualMachine(val memory: Memory, program: List<Instruction>) {
|
|||||||
pc++
|
pc++
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun booleanValue(number: UInt): UInt = if(number == 0u) 0u else 1u
|
||||||
|
private fun booleanValue(number: UByte): UByte = if(number == 0u.toUByte()) 0u else 1u
|
||||||
|
private fun booleanValue(number: UShort): UShort = if(number == 0u.toUShort()) 0u else 1u
|
||||||
|
|
||||||
private fun InsAND(i: Instruction) {
|
private fun InsAND(i: Instruction) {
|
||||||
val (left: UInt, right: UInt) = getLogicalOperandsU(i)
|
val (leftV: UInt, rightV: UInt) = getLogicalOperandsU(i)
|
||||||
|
val left = booleanValue(leftV)
|
||||||
|
val right = booleanValue(rightV)
|
||||||
when(i.type!!) {
|
when(i.type!!) {
|
||||||
VmDataType.BYTE -> registers.setUB(i.reg1!!, (left and right).toUByte())
|
VmDataType.BYTE -> registers.setUB(i.reg1!!, (left and right).toUByte())
|
||||||
VmDataType.WORD -> registers.setUW(i.reg1!!, (left and right).toUShort())
|
VmDataType.WORD -> registers.setUW(i.reg1!!, (left and right).toUShort())
|
||||||
@ -1145,15 +1151,25 @@ class VirtualMachine(val memory: Memory, program: List<Instruction>) {
|
|||||||
private fun InsANDM(i: Instruction) {
|
private fun InsANDM(i: Instruction) {
|
||||||
val address = i.value!!
|
val address = i.value!!
|
||||||
when(i.type!!) {
|
when(i.type!!) {
|
||||||
VmDataType.BYTE -> memory.setUB(address, (memory.getUB(address) and registers.getUB(i.reg1!!)))
|
VmDataType.BYTE -> {
|
||||||
VmDataType.WORD -> memory.setUW(address, (memory.getUW(address) and registers.getUW(i.reg1!!)))
|
val left = booleanValue(memory.getUB(address))
|
||||||
|
val right = booleanValue(registers.getUB(i.reg1!!))
|
||||||
|
memory.setUB(address, left and right)
|
||||||
|
}
|
||||||
|
VmDataType.WORD -> {
|
||||||
|
val left = booleanValue(memory.getUW(address))
|
||||||
|
val right = booleanValue(registers.getUW(i.reg1!!))
|
||||||
|
memory.setUW(address, left and right)
|
||||||
|
}
|
||||||
VmDataType.FLOAT -> throw IllegalArgumentException("invalid float type for this instruction $i")
|
VmDataType.FLOAT -> throw IllegalArgumentException("invalid float type for this instruction $i")
|
||||||
}
|
}
|
||||||
pc++
|
pc++
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun InsOR(i: Instruction) {
|
private fun InsOR(i: Instruction) {
|
||||||
val (left: UInt, right: UInt) = getLogicalOperandsU(i)
|
val (leftV: UInt, rightV: UInt) = getLogicalOperandsU(i)
|
||||||
|
val left = booleanValue(leftV)
|
||||||
|
val right = booleanValue(rightV)
|
||||||
when(i.type!!) {
|
when(i.type!!) {
|
||||||
VmDataType.BYTE -> registers.setUB(i.reg1!!, (left or right).toUByte())
|
VmDataType.BYTE -> registers.setUB(i.reg1!!, (left or right).toUByte())
|
||||||
VmDataType.WORD -> registers.setUW(i.reg1!!, (left or right).toUShort())
|
VmDataType.WORD -> registers.setUW(i.reg1!!, (left or right).toUShort())
|
||||||
@ -1165,15 +1181,25 @@ class VirtualMachine(val memory: Memory, program: List<Instruction>) {
|
|||||||
private fun InsORM(i: Instruction) {
|
private fun InsORM(i: Instruction) {
|
||||||
val address = i.value!!
|
val address = i.value!!
|
||||||
when(i.type!!) {
|
when(i.type!!) {
|
||||||
VmDataType.BYTE -> memory.setUB(address, (memory.getUB(address) or registers.getUB(i.reg1!!)))
|
VmDataType.BYTE -> {
|
||||||
VmDataType.WORD -> memory.setUW(address, (memory.getUW(address) or registers.getUW(i.reg1!!)))
|
val left = booleanValue(memory.getUB(address))
|
||||||
|
val right = booleanValue(registers.getUB(i.reg1!!))
|
||||||
|
memory.setUB(address, left or right)
|
||||||
|
}
|
||||||
|
VmDataType.WORD -> {
|
||||||
|
val left = booleanValue(memory.getUW(address))
|
||||||
|
val right = booleanValue(registers.getUW(i.reg1!!))
|
||||||
|
memory.setUW(address, left or right)
|
||||||
|
}
|
||||||
VmDataType.FLOAT -> throw IllegalArgumentException("invalid float type for this instruction $i")
|
VmDataType.FLOAT -> throw IllegalArgumentException("invalid float type for this instruction $i")
|
||||||
}
|
}
|
||||||
pc++
|
pc++
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun InsXOR(i: Instruction) {
|
private fun InsXOR(i: Instruction) {
|
||||||
val (left: UInt, right: UInt) = getLogicalOperandsU(i)
|
val (leftV: UInt, rightV: UInt) = getLogicalOperandsU(i)
|
||||||
|
val left = booleanValue(leftV)
|
||||||
|
val right = booleanValue(rightV)
|
||||||
when(i.type!!) {
|
when(i.type!!) {
|
||||||
VmDataType.BYTE -> registers.setUB(i.reg1!!, (left xor right).toUByte())
|
VmDataType.BYTE -> registers.setUB(i.reg1!!, (left xor right).toUByte())
|
||||||
VmDataType.WORD -> registers.setUW(i.reg1!!, (left xor right).toUShort())
|
VmDataType.WORD -> registers.setUW(i.reg1!!, (left xor right).toUShort())
|
||||||
@ -1185,8 +1211,16 @@ class VirtualMachine(val memory: Memory, program: List<Instruction>) {
|
|||||||
private fun InsXORM(i: Instruction) {
|
private fun InsXORM(i: Instruction) {
|
||||||
val address = i.value!!
|
val address = i.value!!
|
||||||
when(i.type!!) {
|
when(i.type!!) {
|
||||||
VmDataType.BYTE -> memory.setUB(address, (memory.getUB(address) xor registers.getUB(i.reg1!!)))
|
VmDataType.BYTE -> {
|
||||||
VmDataType.WORD -> memory.setUW(address, (memory.getUW(address) xor registers.getUW(i.reg1!!)))
|
val left = booleanValue(memory.getUB(address))
|
||||||
|
val right = booleanValue(registers.getUB(i.reg1!!))
|
||||||
|
memory.setUB(address, left xor right)
|
||||||
|
}
|
||||||
|
VmDataType.WORD -> {
|
||||||
|
val left = booleanValue(memory.getUW(address))
|
||||||
|
val right = booleanValue(registers.getUW(i.reg1!!))
|
||||||
|
memory.setUW(address, left xor right)
|
||||||
|
}
|
||||||
VmDataType.FLOAT -> throw IllegalArgumentException("invalid float type for this instruction $i")
|
VmDataType.FLOAT -> throw IllegalArgumentException("invalid float type for this instruction $i")
|
||||||
}
|
}
|
||||||
pc++
|
pc++
|
||||||
|
Loading…
Reference in New Issue
Block a user