mirror of
https://github.com/irmen/prog8.git
synced 2025-08-14 22:27:48 +00:00
vm: and/or/xor/not are all bitwise operations again
This commit is contained in:
@@ -22,9 +22,9 @@ class ConstExprEvaluator {
|
|||||||
"&" -> bitwiseand(left, right)
|
"&" -> bitwiseand(left, right)
|
||||||
"|" -> bitwiseor(left, right)
|
"|" -> bitwiseor(left, right)
|
||||||
"^" -> bitwisexor(left, right)
|
"^" -> bitwisexor(left, right)
|
||||||
"and" -> logicaland(left, right)
|
"and" -> logicaland(left, right) // TODO bitwise and?
|
||||||
"or" -> logicalor(left, right)
|
"or" -> logicalor(left, right) // TODO bitwise or?
|
||||||
"xor" -> logicalxor(left, right)
|
"xor" -> logicalxor(left, right) // TODO bitwise xor?
|
||||||
"<" -> NumericLiteral.fromBoolean(left < right, left.position)
|
"<" -> NumericLiteral.fromBoolean(left < right, left.position)
|
||||||
">" -> NumericLiteral.fromBoolean(left > right, left.position)
|
">" -> NumericLiteral.fromBoolean(left > right, left.position)
|
||||||
"<=" -> NumericLiteral.fromBoolean(left <= right, left.position)
|
"<=" -> NumericLiteral.fromBoolean(left <= right, left.position)
|
||||||
@@ -59,7 +59,7 @@ class ConstExprEvaluator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun logicalxor(left: NumericLiteral, right: NumericLiteral): NumericLiteral {
|
private fun logicalxor(left: NumericLiteral, right: NumericLiteral): NumericLiteral {
|
||||||
val error = "cannot compute $left locical-bitxor $right"
|
val error = "cannot compute $left logical-xor $right"
|
||||||
return when (left.type) {
|
return when (left.type) {
|
||||||
in IntegerDatatypes -> when (right.type) {
|
in IntegerDatatypes -> when (right.type) {
|
||||||
in IntegerDatatypes -> NumericLiteral.fromBoolean((left.number.toInt() != 0) xor (right.number.toInt() != 0), left.position)
|
in IntegerDatatypes -> NumericLiteral.fromBoolean((left.number.toInt() != 0) xor (right.number.toInt() != 0), left.position)
|
||||||
@@ -76,7 +76,7 @@ class ConstExprEvaluator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun logicalor(left: NumericLiteral, right: NumericLiteral): NumericLiteral {
|
private fun logicalor(left: NumericLiteral, right: NumericLiteral): NumericLiteral {
|
||||||
val error = "cannot compute $left locical-or $right"
|
val error = "cannot compute $left logical-or $right"
|
||||||
return when (left.type) {
|
return when (left.type) {
|
||||||
in IntegerDatatypes -> when (right.type) {
|
in IntegerDatatypes -> when (right.type) {
|
||||||
in IntegerDatatypes -> NumericLiteral.fromBoolean(left.number.toInt() != 0 || right.number.toInt() != 0, left.position)
|
in IntegerDatatypes -> NumericLiteral.fromBoolean(left.number.toInt() != 0 || right.number.toInt() != 0, left.position)
|
||||||
@@ -93,7 +93,7 @@ class ConstExprEvaluator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun logicaland(left: NumericLiteral, right: NumericLiteral): NumericLiteral {
|
private fun logicaland(left: NumericLiteral, right: NumericLiteral): NumericLiteral {
|
||||||
val error = "cannot compute $left locical-and $right"
|
val error = "cannot compute $left logical-and $right"
|
||||||
return when (left.type) {
|
return when (left.type) {
|
||||||
in IntegerDatatypes -> when (right.type) {
|
in IntegerDatatypes -> when (right.type) {
|
||||||
in IntegerDatatypes -> NumericLiteral.fromBoolean(left.number.toInt() != 0 && right.number.toInt() != 0, left.position)
|
in IntegerDatatypes -> NumericLiteral.fromBoolean(left.number.toInt() != 0 && right.number.toInt() != 0, left.position)
|
||||||
|
@@ -12,10 +12,7 @@ import prog8.ast.statements.IfElse
|
|||||||
import prog8.ast.statements.Jump
|
import prog8.ast.statements.Jump
|
||||||
import prog8.ast.walk.AstWalker
|
import prog8.ast.walk.AstWalker
|
||||||
import prog8.ast.walk.IAstModification
|
import prog8.ast.walk.IAstModification
|
||||||
import prog8.code.core.AssociativeOperators
|
import prog8.code.core.*
|
||||||
import prog8.code.core.DataType
|
|
||||||
import prog8.code.core.IntegerDatatypes
|
|
||||||
import prog8.code.core.NumericDatatypes
|
|
||||||
import kotlin.math.abs
|
import kotlin.math.abs
|
||||||
import kotlin.math.log2
|
import kotlin.math.log2
|
||||||
import kotlin.math.pow
|
import kotlin.math.pow
|
||||||
@@ -220,8 +217,8 @@ class ExpressionSimplifier(private val program: Program) : AstWalker() {
|
|||||||
when {
|
when {
|
||||||
leftVal != null && !leftVal.asBooleanValue -> expr.right
|
leftVal != null && !leftVal.asBooleanValue -> expr.right
|
||||||
rightVal != null && !rightVal.asBooleanValue -> expr.left
|
rightVal != null && !rightVal.asBooleanValue -> expr.left
|
||||||
leftVal != null && leftVal.asBooleanValue -> PrefixExpression("not", expr.right, expr.right.position)
|
leftVal != null && leftVal.asBooleanValue -> BinaryExpression(expr.right, "==", NumericLiteral.optimalInteger(0, Position.DUMMY), expr.right.position)
|
||||||
rightVal != null && rightVal.asBooleanValue -> PrefixExpression("not", expr.left, expr.left.position)
|
rightVal != null && rightVal.asBooleanValue -> BinaryExpression(expr.left, "==", NumericLiteral.optimalInteger(0, Position.DUMMY), expr.left.position)
|
||||||
else -> null
|
else -> null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -104,22 +104,28 @@ class AstPreprocessor(val program: Program, val errors: IErrorReporter, val comp
|
|||||||
// so a lot of useless checks and conversions are added. Here we can be smarter so the codegen
|
// 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.
|
// 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) {
|
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(
|
return listOf(
|
||||||
IAstModification.ReplaceNode(expr.left, wrapped(expr.left), expr),
|
IAstModification.ReplaceNodeSafe(expr.left, wrapWithBooleanConversion(expr.left), expr),
|
||||||
IAstModification.ReplaceNode(expr.right, wrapped(expr.right), expr)
|
IAstModification.ReplaceNodeSafe(expr.right, wrapWithBooleanConversion(expr.right), expr)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return noModifications
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun before(expr: PrefixExpression, parent: Node): Iterable<IAstModification> {
|
||||||
|
if(expr.operator == "not") {
|
||||||
|
// 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.
|
||||||
|
|
||||||
|
// not(x) --> boolean(x)==0
|
||||||
|
val replacement = BinaryExpression(wrapWithBooleanConversion(expr.expression), "==", NumericLiteral.optimalInteger(0, expr.position), expr.position)
|
||||||
|
return listOf(IAstModification.ReplaceNodeSafe(expr, replacement, parent))
|
||||||
|
}
|
||||||
return noModifications
|
return noModifications
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -156,4 +162,13 @@ class AstPreprocessor(val program: Program, val errors: IErrorReporter, val comp
|
|||||||
|
|
||||||
return noModifications
|
return noModifications
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun wrapWithBooleanConversion(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)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -107,15 +107,10 @@ class TypecastsAdder(val program: Program, val options: CompilationOptions, val
|
|||||||
val logical = parent as? BinaryExpression
|
val logical = parent as? BinaryExpression
|
||||||
if(logical!=null && logical.operator in LogicalOperators) {
|
if(logical!=null && logical.operator in LogicalOperators) {
|
||||||
// not x as operand in a logical expression --> cast it to ubyte
|
// not x as operand in a logical expression --> cast it to ubyte
|
||||||
|
// TODO is this cast really necessary or does boolean() wrapping take care of it?
|
||||||
val cast = TypecastExpression(expr, DataType.UBYTE, true, expr.position)
|
val cast = TypecastExpression(expr, DataType.UBYTE, true, expr.position)
|
||||||
return listOf(IAstModification.ReplaceNode(expr, cast, parent))
|
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
|
return noModifications
|
||||||
}
|
}
|
||||||
|
@@ -12,7 +12,6 @@ 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() {
|
||||||
@@ -250,20 +249,6 @@ internal class VariousCleanups(val program: Program, val errors: IErrorReporter,
|
|||||||
if(fcall!=null && fcall.target.nameInSource==listOf("boolean")) {
|
if(fcall!=null && fcall.target.nameInSource==listOf("boolean")) {
|
||||||
return listOf(IAstModification.ReplaceNode(functionCallExpr, fcall as Node, parent))
|
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
|
return noModifications
|
||||||
}
|
}
|
||||||
|
@@ -285,12 +285,12 @@ class TestOptimization: FunSpec({
|
|||||||
"""
|
"""
|
||||||
val result = compileText(C64Target(), false, src, writeAssembly = false)!!
|
val result = compileText(C64Target(), false, src, writeAssembly = false)!!
|
||||||
|
|
||||||
// bb = (not bb or (not ww as ubyte) )
|
// bb = ((boolean(bb)==0) or (boolean(ww)==0))
|
||||||
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<PrefixExpression>()
|
expr.left shouldBe instanceOf<BinaryExpression>()
|
||||||
expr.right shouldBe instanceOf<TypecastExpression>() // not of word typecast into ubyte
|
expr.right shouldBe instanceOf<BinaryExpression>()
|
||||||
expr.left.inferType(result.program).getOrElse { fail("dt") } shouldBe DataType.UBYTE
|
expr.left.inferType(result.program).getOrElse { fail("dt") } shouldBe DataType.UBYTE
|
||||||
expr.right.inferType(result.program).getOrElse { fail("dt") } shouldBe DataType.UBYTE
|
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
|
||||||
@@ -303,33 +303,33 @@ class TestOptimization: FunSpec({
|
|||||||
result.program.processAstBeforeAsmGeneration(options, ErrorReporterForTests())
|
result.program.processAstBeforeAsmGeneration(options, ErrorReporterForTests())
|
||||||
printProgram(result.program)
|
printProgram(result.program)
|
||||||
|
|
||||||
|
// TODO this is no longer the case:
|
||||||
// assignment is now split into:
|
// assignment is now split into:
|
||||||
// bb = not bb
|
// bb = not bb
|
||||||
// bb = (bb or (not ww as ubyte)
|
// 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 }
|
// bbAssigns.size shouldBe 2
|
||||||
bbAssigns.size shouldBe 2
|
//
|
||||||
|
// bbAssigns[0].target.identifier!!.nameInSource shouldBe listOf("bb")
|
||||||
bbAssigns[0].target.identifier!!.nameInSource shouldBe listOf("bb")
|
// bbAssigns[0].value shouldBe instanceOf<PrefixExpression>()
|
||||||
bbAssigns[0].value shouldBe instanceOf<PrefixExpression>()
|
// (bbAssigns[0].value as PrefixExpression).operator shouldBe "not"
|
||||||
(bbAssigns[0].value as PrefixExpression).operator shouldBe "not"
|
// ((bbAssigns[0].value as PrefixExpression).expression as? IdentifierReference)?.nameInSource shouldBe listOf("bb")
|
||||||
((bbAssigns[0].value as PrefixExpression).expression as? IdentifierReference)?.nameInSource shouldBe listOf("bb")
|
// bbAssigns[0].value.inferType(result.program).getOrElse { fail("dt") } shouldBe DataType.UBYTE
|
||||||
bbAssigns[0].value.inferType(result.program).getOrElse { fail("dt") } shouldBe DataType.UBYTE
|
//
|
||||||
|
// bbAssigns[1].target.identifier!!.nameInSource shouldBe listOf("bb")
|
||||||
bbAssigns[1].target.identifier!!.nameInSource shouldBe listOf("bb")
|
// 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<TypecastExpression>()
|
||||||
bbAssigns1expr.right shouldBe instanceOf<TypecastExpression>()
|
// val castedValue = (bbAssigns1expr.right as TypecastExpression).expression as PrefixExpression
|
||||||
val castedValue = (bbAssigns1expr.right as TypecastExpression).expression as PrefixExpression
|
// castedValue.operator shouldBe "not"
|
||||||
castedValue.operator shouldBe "not"
|
// (castedValue.expression as? IdentifierReference)?.nameInSource shouldBe listOf("ww")
|
||||||
(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)
|
// asm shouldNotBe null
|
||||||
asm shouldNotBe null
|
// asm!!.name.shouldNotBeBlank()
|
||||||
asm!!.name.shouldNotBeBlank()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
test("intermediate assignment steps generated for typecasted expression") {
|
test("intermediate assignment steps generated for typecasted expression") {
|
||||||
@@ -455,13 +455,15 @@ class TestOptimization: FunSpec({
|
|||||||
}
|
}
|
||||||
}"""
|
}"""
|
||||||
val result = compileText(C64Target(), optimize=true, src, writeAssembly=false)!!
|
val result = compileText(C64Target(), optimize=true, src, writeAssembly=false)!!
|
||||||
|
printProgram(result.program)
|
||||||
/* expected:
|
/* expected:
|
||||||
ubyte z1
|
ubyte z1
|
||||||
z1 = 10
|
z1 = 10
|
||||||
ubyte z2
|
ubyte z2
|
||||||
z2 = 255
|
z2 = 255
|
||||||
ubyte z3
|
ubyte z3
|
||||||
z3 = 1
|
z3 = 0
|
||||||
|
z3 = (boolean(z3)==0)
|
||||||
uword z4
|
uword z4
|
||||||
z4 = 0
|
z4 = 0
|
||||||
ubyte z5
|
ubyte z5
|
||||||
@@ -472,28 +474,30 @@ class TestOptimization: FunSpec({
|
|||||||
z6 -= 5
|
z6 -= 5
|
||||||
*/
|
*/
|
||||||
val statements = result.program.entrypoint.statements
|
val statements = result.program.entrypoint.statements
|
||||||
statements.size shouldBe 14
|
statements.size shouldBe 15
|
||||||
val z1decl = statements[0] as VarDecl
|
val z1decl = statements[0] as VarDecl
|
||||||
val z1init = statements[1] as Assignment
|
val z1init = statements[1] as Assignment
|
||||||
val z2decl = statements[2] as VarDecl
|
val z2decl = statements[2] as VarDecl
|
||||||
val z2init = statements[3] as Assignment
|
val z2init = statements[3] as Assignment
|
||||||
val z3decl = statements[4] as VarDecl
|
val z3decl = statements[4] as VarDecl
|
||||||
val z3init = statements[5] as Assignment
|
val z3init = statements[5] as Assignment
|
||||||
val z4decl = statements[6] as VarDecl
|
val z3not = statements[6] as Assignment
|
||||||
val z4init = statements[7] as Assignment
|
val z4decl = statements[7] as VarDecl
|
||||||
val z5decl = statements[8] as VarDecl
|
val z4init = statements[8] as Assignment
|
||||||
val z5init = statements[9] as Assignment
|
val z5decl = statements[9] as VarDecl
|
||||||
val z5plus = statements[10] as Assignment
|
val z5init = statements[10] as Assignment
|
||||||
val z6decl = statements[11] as VarDecl
|
val z5plus = statements[11] as Assignment
|
||||||
val z6init = statements[12] as Assignment
|
val z6decl = statements[12] as VarDecl
|
||||||
val z6plus = statements[13] as Assignment
|
val z6init = statements[13] as Assignment
|
||||||
|
val z6plus = statements[14] as Assignment
|
||||||
|
|
||||||
z1decl.name shouldBe "z1"
|
z1decl.name shouldBe "z1"
|
||||||
z1init.value shouldBe NumericLiteral(DataType.UBYTE, 10.0, Position.DUMMY)
|
z1init.value shouldBe NumericLiteral(DataType.UBYTE, 10.0, Position.DUMMY)
|
||||||
z2decl.name shouldBe "z2"
|
z2decl.name shouldBe "z2"
|
||||||
z2init.value shouldBe NumericLiteral(DataType.UBYTE, 255.0, Position.DUMMY)
|
z2init.value shouldBe NumericLiteral(DataType.UBYTE, 255.0, Position.DUMMY)
|
||||||
z3decl.name shouldBe "z3"
|
z3decl.name shouldBe "z3"
|
||||||
z3init.value shouldBe NumericLiteral(DataType.UBYTE, 1.0, Position.DUMMY)
|
z3init.value shouldBe NumericLiteral(DataType.UBYTE, 0.0, Position.DUMMY)
|
||||||
|
z3not.value shouldBe instanceOf<BinaryExpression>()
|
||||||
z4decl.name shouldBe "z4"
|
z4decl.name shouldBe "z4"
|
||||||
z4init.value shouldBe NumericLiteral(DataType.UBYTE, 0.0, Position.DUMMY)
|
z4init.value shouldBe NumericLiteral(DataType.UBYTE, 0.0, Position.DUMMY)
|
||||||
z5decl.name shouldBe "z5"
|
z5decl.name shouldBe "z5"
|
||||||
|
@@ -152,7 +152,6 @@ class TestTypecasts: FunSpec({
|
|||||||
}
|
}
|
||||||
"""
|
"""
|
||||||
val result = compileText(C64Target(), false, text, writeAssembly = true)!!
|
val result = compileText(C64Target(), false, text, writeAssembly = true)!!
|
||||||
printProgram(result.program)
|
|
||||||
val statements = result.program.entrypoint.statements
|
val statements = result.program.entrypoint.statements
|
||||||
statements.size shouldBe 27
|
statements.size shouldBe 27
|
||||||
}
|
}
|
||||||
@@ -181,7 +180,7 @@ class TestTypecasts: FunSpec({
|
|||||||
}"""
|
}"""
|
||||||
val result = compileText(C64Target(), false, text, writeAssembly = true)!!
|
val result = compileText(C64Target(), false, text, writeAssembly = true)!!
|
||||||
val statements = result.program.entrypoint.statements
|
val statements = result.program.entrypoint.statements
|
||||||
statements.size shouldBe 14
|
statements.size shouldBe 13
|
||||||
}
|
}
|
||||||
|
|
||||||
test("no infinite typecast loop in assignment asmgen") {
|
test("no infinite typecast loop in assignment asmgen") {
|
||||||
|
@@ -62,6 +62,7 @@ main {
|
|||||||
else
|
else
|
||||||
txt.print(" / fail")
|
txt.print(" / fail")
|
||||||
txt.nl()
|
txt.nl()
|
||||||
|
|
||||||
bvalue = bvalue and 128
|
bvalue = bvalue and 128
|
||||||
txt.print("bvl 1: ")
|
txt.print("bvl 1: ")
|
||||||
txt.print_ub(bvalue)
|
txt.print_ub(bvalue)
|
||||||
@@ -121,9 +122,9 @@ main {
|
|||||||
txt.print(" / fail")
|
txt.print(" / fail")
|
||||||
txt.nl()
|
txt.nl()
|
||||||
txt.print(" or 1: ")
|
txt.print(" or 1: ")
|
||||||
bvalue = ub4 or ub4 or 64
|
bvalue = ub4 or ub4 or ub1
|
||||||
txt.print_ub(bvalue)
|
txt.print_ub(bvalue)
|
||||||
if ub4 or ub4 or 64
|
if ub4 or ub4 or ub1
|
||||||
txt.print(" / ok")
|
txt.print(" / ok")
|
||||||
else
|
else
|
||||||
txt.print(" / fail")
|
txt.print(" / fail")
|
||||||
@@ -169,6 +170,5 @@ main {
|
|||||||
txt.print(" / fail")
|
txt.print(" / fail")
|
||||||
else
|
else
|
||||||
txt.print(" / ok")
|
txt.print(" / ok")
|
||||||
txt.nl()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -65,6 +65,18 @@ interface IAstModification {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class ReplaceNodeSafe(val node: Node, private val replacement: Node, private val parent: Node) :
|
||||||
|
IAstModification {
|
||||||
|
override fun perform() {
|
||||||
|
try {
|
||||||
|
parent.replaceChildNode(node, replacement)
|
||||||
|
replacement.linkParents(parent)
|
||||||
|
} catch (fa: FatalAstException) {
|
||||||
|
// possibly because of another replacement. Ignore here, we try again later.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class SwapOperands(private val expr: BinaryExpression): IAstModification {
|
class SwapOperands(private val expr: BinaryExpression): IAstModification {
|
||||||
override fun perform() {
|
override fun perform() {
|
||||||
require(expr.operator in AssociativeOperators)
|
require(expr.operator in AssociativeOperators)
|
||||||
|
@@ -3,10 +3,20 @@ TODO
|
|||||||
|
|
||||||
For next release
|
For next release
|
||||||
^^^^^^^^^^^^^^^^
|
^^^^^^^^^^^^^^^^
|
||||||
- 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)
|
- 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
|
all these testable with compiler/test/arithmetic/logical.p8
|
||||||
|
- 6502: also fix logical and/or/xor routines to just be bitwise routines.
|
||||||
|
|
||||||
|
- get rid of logical and/or/xor/not in the codegen (6502+vm)
|
||||||
|
because bitwise versions + correct use of boolean() operand wrapping are equivalent?
|
||||||
|
can do this for instance by replacing and/or/xor/not with their bitwise versions &, |, ^, ~
|
||||||
|
|
||||||
|
- compiling logical.p8 to virtual with optimization generates a lot larger code as without optimizations.
|
||||||
|
this is not the case for the 6502 codegen.
|
||||||
|
|
||||||
|
- add optimizations: not a or not b -> not(a and b) , not a and not b -> not(a or b)
|
||||||
|
add unit tests for that.
|
||||||
|
- bin expr splitter: split logical expressions on ands/ors/xors ?
|
||||||
|
|
||||||
- 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.
|
||||||
|
@@ -124,7 +124,7 @@ All have type b or w.
|
|||||||
and reg1, reg2 - reg1 = reg1 bitwise and reg2
|
and reg1, reg2 - reg1 = reg1 bitwise and reg2
|
||||||
or reg1, reg2 - reg1 = reg1 bitwise or reg2
|
or reg1, reg2 - reg1 = reg1 bitwise or reg2
|
||||||
xor reg1, reg2 - reg1 = reg1 bitwise xor reg2
|
xor reg1, reg2 - reg1 = reg1 bitwise xor reg2
|
||||||
not reg1 - reg1 = boolean not of reg1 (0->1 , ~0 -> 0)
|
not reg1 - reg1 = bitwise not of reg1 (all bits flipped)
|
||||||
lsrn reg1, reg2 - reg1 = multi-shift reg1 right by reg2 bits + set Carry to shifted bit
|
lsrn reg1, reg2 - reg1 = multi-shift reg1 right by reg2 bits + set Carry to shifted bit
|
||||||
asrn reg1, reg2 - reg1 = multi-shift reg1 right by reg2 bits (signed) + set Carry to shifted bit
|
asrn reg1, reg2 - reg1 = multi-shift reg1 right by reg2 bits (signed) + set Carry to shifted bit
|
||||||
lsln reg1, reg2 - reg1 = multi-shift reg1 left by reg2 bits + set Carry to shifted bit
|
lsln reg1, reg2 - reg1 = multi-shift reg1 left by reg2 bits + set Carry to shifted bit
|
||||||
@@ -138,7 +138,7 @@ roxl reg1 - rotate reg1 left by 1 bits, using
|
|||||||
andm reg1 address - memory = memory bitwise and reg1
|
andm reg1 address - memory = memory bitwise and reg1
|
||||||
orm reg1, address - memory = memory bitwise or reg1
|
orm reg1, address - memory = memory bitwise or reg1
|
||||||
xorm reg1, address - memory = memory bitwise xor reg1
|
xorm reg1, address - memory = memory bitwise xor reg1
|
||||||
notm address - memory = boolean not of that memory (0->1 , ~0 -> 0)
|
notm address - memory = bitwise not of that memory (all bits flipped)
|
||||||
lsrnm reg1, address - multi-shift memoryright by reg1 bits + set Carry to shifted bit
|
lsrnm reg1, address - multi-shift memoryright by reg1 bits + set Carry to shifted bit
|
||||||
asrnm reg1, address - multi-shift memory right by reg1 bits (signed) + set Carry to shifted bit
|
asrnm reg1, address - multi-shift memory right by reg1 bits (signed) + set Carry to shifted bit
|
||||||
lslnm reg1, address - multi-shift memory left by reg1 bits + set Carry to shifted bit
|
lslnm reg1, address - multi-shift memory left by reg1 bits + set Carry to shifted bit
|
||||||
|
@@ -1132,14 +1132,8 @@ 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 (leftV: UInt, rightV: UInt) = getLogicalOperandsU(i)
|
val (left: UInt, right: 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())
|
||||||
@@ -1152,13 +1146,13 @@ class VirtualMachine(val memory: Memory, program: List<Instruction>) {
|
|||||||
val address = i.value!!
|
val address = i.value!!
|
||||||
when(i.type!!) {
|
when(i.type!!) {
|
||||||
VmDataType.BYTE -> {
|
VmDataType.BYTE -> {
|
||||||
val left = booleanValue(memory.getUB(address))
|
val left = memory.getUB(address)
|
||||||
val right = booleanValue(registers.getUB(i.reg1!!))
|
val right = registers.getUB(i.reg1!!)
|
||||||
memory.setUB(address, left and right)
|
memory.setUB(address, left and right)
|
||||||
}
|
}
|
||||||
VmDataType.WORD -> {
|
VmDataType.WORD -> {
|
||||||
val left = booleanValue(memory.getUW(address))
|
val left = memory.getUW(address)
|
||||||
val right = booleanValue(registers.getUW(i.reg1!!))
|
val right = registers.getUW(i.reg1!!)
|
||||||
memory.setUW(address, left and right)
|
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")
|
||||||
@@ -1167,9 +1161,7 @@ class VirtualMachine(val memory: Memory, program: List<Instruction>) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun InsOR(i: Instruction) {
|
private fun InsOR(i: Instruction) {
|
||||||
val (leftV: UInt, rightV: UInt) = getLogicalOperandsU(i)
|
val (left: UInt, right: 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())
|
||||||
@@ -1182,13 +1174,13 @@ class VirtualMachine(val memory: Memory, program: List<Instruction>) {
|
|||||||
val address = i.value!!
|
val address = i.value!!
|
||||||
when(i.type!!) {
|
when(i.type!!) {
|
||||||
VmDataType.BYTE -> {
|
VmDataType.BYTE -> {
|
||||||
val left = booleanValue(memory.getUB(address))
|
val left = memory.getUB(address)
|
||||||
val right = booleanValue(registers.getUB(i.reg1!!))
|
val right = registers.getUB(i.reg1!!)
|
||||||
memory.setUB(address, left or right)
|
memory.setUB(address, left or right)
|
||||||
}
|
}
|
||||||
VmDataType.WORD -> {
|
VmDataType.WORD -> {
|
||||||
val left = booleanValue(memory.getUW(address))
|
val left = memory.getUW(address)
|
||||||
val right = booleanValue(registers.getUW(i.reg1!!))
|
val right = registers.getUW(i.reg1!!)
|
||||||
memory.setUW(address, left or right)
|
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")
|
||||||
@@ -1197,9 +1189,7 @@ class VirtualMachine(val memory: Memory, program: List<Instruction>) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun InsXOR(i: Instruction) {
|
private fun InsXOR(i: Instruction) {
|
||||||
val (leftV: UInt, rightV: UInt) = getLogicalOperandsU(i)
|
val (left: UInt, right: 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())
|
||||||
@@ -1212,13 +1202,13 @@ class VirtualMachine(val memory: Memory, program: List<Instruction>) {
|
|||||||
val address = i.value!!
|
val address = i.value!!
|
||||||
when(i.type!!) {
|
when(i.type!!) {
|
||||||
VmDataType.BYTE -> {
|
VmDataType.BYTE -> {
|
||||||
val left = booleanValue(memory.getUB(address))
|
val left = memory.getUB(address)
|
||||||
val right = booleanValue(registers.getUB(i.reg1!!))
|
val right = registers.getUB(i.reg1!!)
|
||||||
memory.setUB(address, left xor right)
|
memory.setUB(address, left xor right)
|
||||||
}
|
}
|
||||||
VmDataType.WORD -> {
|
VmDataType.WORD -> {
|
||||||
val left = booleanValue(memory.getUW(address))
|
val left = memory.getUW(address)
|
||||||
val right = booleanValue(registers.getUW(i.reg1!!))
|
val right = registers.getUW(i.reg1!!)
|
||||||
memory.setUW(address, left xor right)
|
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")
|
||||||
@@ -1228,8 +1218,8 @@ class VirtualMachine(val memory: Memory, program: List<Instruction>) {
|
|||||||
|
|
||||||
private fun InsNOT(i: Instruction) {
|
private fun InsNOT(i: Instruction) {
|
||||||
when(i.type!!) {
|
when(i.type!!) {
|
||||||
VmDataType.BYTE -> registers.setUB(i.reg1!!, if(registers.getUB(i.reg1)==0.toUByte()) 1u else 0u)
|
VmDataType.BYTE -> registers.setUB(i.reg1!!, registers.getUB(i.reg1).inv())
|
||||||
VmDataType.WORD -> registers.setUW(i.reg1!!, if(registers.getUW(i.reg1)==0.toUShort()) 1u else 0u)
|
VmDataType.WORD -> registers.setUW(i.reg1!!, registers.getUW(i.reg1).inv())
|
||||||
VmDataType.FLOAT -> throw IllegalArgumentException("invalid float type for this instruction $i")
|
VmDataType.FLOAT -> throw IllegalArgumentException("invalid float type for this instruction $i")
|
||||||
}
|
}
|
||||||
pc++
|
pc++
|
||||||
@@ -1238,8 +1228,8 @@ class VirtualMachine(val memory: Memory, program: List<Instruction>) {
|
|||||||
private fun InsNOTM(i: Instruction) {
|
private fun InsNOTM(i: Instruction) {
|
||||||
val address = i.value!!
|
val address = i.value!!
|
||||||
when(i.type!!) {
|
when(i.type!!) {
|
||||||
VmDataType.BYTE -> memory.setUB(address, if(memory.getUB(address)==0.toUByte()) 1u else 0u)
|
VmDataType.BYTE -> memory.setUB(address, memory.getUB(address).inv())
|
||||||
VmDataType.WORD -> memory.setUW(address, if(memory.getUW(address)==0.toUShort()) 1u else 0u)
|
VmDataType.WORD -> memory.setUW(address, memory.getUW(address).inv())
|
||||||
VmDataType.FLOAT -> throw IllegalArgumentException("invalid float type for this instruction $i")
|
VmDataType.FLOAT -> throw IllegalArgumentException("invalid float type for this instruction $i")
|
||||||
}
|
}
|
||||||
pc++
|
pc++
|
||||||
|
Reference in New Issue
Block a user