better way of doing BIT instructions

This commit is contained in:
Irmen de Jong 2024-12-26 02:02:30 +01:00
parent 827df04b32
commit 056c0a24d9
13 changed files with 236 additions and 290 deletions

View File

@ -27,8 +27,6 @@ class FParam(val name: String, vararg val possibleDatatypes: BaseDataType)
private val IterableDatatypes = arrayOf(BaseDataType.STR, BaseDataType.ARRAY, BaseDataType.ARRAY_SPLITW)
private val IntegerDatatypes = arrayOf(BaseDataType.UBYTE, BaseDataType.BYTE, BaseDataType.UWORD, BaseDataType.WORD, BaseDataType.LONG)
private val NumericDatatypes = arrayOf(BaseDataType.UBYTE, BaseDataType.BYTE, BaseDataType.UWORD, BaseDataType.WORD, BaseDataType.LONG, BaseDataType.FLOAT)
private val ByteDatatypes = arrayOf(BaseDataType.UBYTE, BaseDataType.BYTE)
private val ArrayDatatypes = arrayOf(BaseDataType.ARRAY, BaseDataType.ARRAY_SPLITW)
class FSignature(val pure: Boolean, // does it have side effects?
@ -69,7 +67,7 @@ class FSignature(val pure: Boolean, // does it have side effects?
}
CallConvention(listOf(paramConv), returns)
}
actualParamTypes.size==2 && (actualParamTypes[0] in ByteDatatypes && actualParamTypes[1].isWord) -> {
actualParamTypes.size==2 && (actualParamTypes[0].isByte && actualParamTypes[1].isWord) -> {
TODO("opportunity to pass word+byte arguments in A,Y and X registers but not implemented yet")
}
actualParamTypes.size==2 && (actualParamTypes[0].isWord && actualParamTypes[1].isByte) -> {
@ -98,8 +96,6 @@ val BuiltinFunctions: Map<String, FSignature> = mapOf(
"prog8_lib_stringcompare" to FSignature(true, BaseDataType.BYTE, FParam("str1", BaseDataType.STR), FParam("str2", BaseDataType.STR)),
"prog8_lib_square_byte" to FSignature(true, BaseDataType.UBYTE, FParam("value", BaseDataType.BYTE, BaseDataType.UBYTE)),
"prog8_lib_square_word" to FSignature(true, BaseDataType.UWORD, FParam("value", BaseDataType.WORD, BaseDataType.UWORD)),
"prog8_ifelse_bittest_set" to FSignature(true, BaseDataType.BOOL, FParam("variable", *ByteDatatypes), FParam("bitnumber", BaseDataType.UBYTE)),
"prog8_ifelse_bittest_notset" to FSignature(true, BaseDataType.BOOL, FParam("variable", *ByteDatatypes), FParam("bitnumber", BaseDataType.UBYTE)),
"abs" to FSignature(true, null, FParam("value", *NumericDatatypes)),
"abs__byte" to FSignature(true, BaseDataType.BYTE, FParam("value", BaseDataType.BYTE)),
"abs__word" to FSignature(true, BaseDataType.WORD, FParam("value", BaseDataType.WORD)),

View File

@ -9,10 +9,7 @@ import prog8.code.core.*
fun optimizeIntermediateAst(program: PtProgram, options: CompilationOptions, st: SymbolTable, errors: IErrorReporter) {
if (!options.optimize)
return
while (errors.noErrors() &&
(optimizeBitTest(program, options)
+ optimizeAssignTargets(program, st)) > 0
) {
while (errors.noErrors() && optimizeAssignTargets(program, st) > 0) {
// keep rolling
}
}
@ -74,82 +71,6 @@ private fun optimizeAssignTargets(program: PtProgram, st: SymbolTable): Int {
}
private fun optimizeBitTest(program: PtProgram, options: CompilationOptions): Int {
fun makeBittestCall(condition: PtBinaryExpression, and: PtBinaryExpression, variable: PtIdentifier, bitmask: Int): PtBuiltinFunctionCall {
require(bitmask==128 || bitmask==64)
val setOrNot = if(condition.operator=="!=") "set" else "notset"
val bittestCall = PtBuiltinFunctionCall("prog8_ifelse_bittest_$setOrNot", false, true, DataType.forDt(BaseDataType.BOOL), condition.position)
bittestCall.add(variable)
if(bitmask==128)
bittestCall.add(PtNumber(BaseDataType.UBYTE, 7.0, and.right.position))
else
bittestCall.add(PtNumber(BaseDataType.UBYTE, 6.0, and.right.position))
return bittestCall
}
fun isAndByteConditionForBRK(condition: PtBinaryExpression?): Triple<PtBinaryExpression, PtIdentifier, Int>? {
if(condition!=null && (condition.operator=="==" || condition.operator=="!=")) {
if (condition.right.asConstInteger() == 0) {
val and = condition.left as? PtBinaryExpression
if (and != null && and.operator == "&" && and.type.isUnsignedByte) {
val bitmask = and.right.asConstInteger()
if(bitmask==128 || bitmask==64) {
val variable = and.left as? PtIdentifier
if (variable != null && variable.type.isByte) {
return Triple(and, variable, bitmask)
}
val typecast = and.left as? PtTypeCast
if (typecast != null && typecast.type.isUnsignedByte) {
val castedVariable = typecast.value as? PtIdentifier
if(castedVariable!=null && castedVariable.type.isByte)
return Triple(and, castedVariable, bitmask)
}
}
}
}
}
return null
}
var changes = 0
var recurse = true
walkAst(program) { node: PtNode, depth: Int ->
if(node is PtIfElse) {
val condition = node.condition as? PtBinaryExpression
val check = isAndByteConditionForBRK(condition)
if(check!=null) {
val (and, variable, bitmask) = check
val bittestCall = makeBittestCall(condition!!, and, variable, bitmask)
val ifElse = PtIfElse(node.position)
ifElse.add(bittestCall)
ifElse.add(node.ifScope)
if (node.hasElse())
ifElse.add(node.elseScope)
val index = node.parent.children.indexOf(node)
node.parent.children[index] = ifElse
ifElse.parent = node.parent
changes++
recurse = false
}
}
if (node is PtIfExpression) {
val condition = node.condition as? PtBinaryExpression
val check = isAndByteConditionForBRK(condition)
if(check!=null) {
val (and, variable, bitmask) = check
val bittestCall = makeBittestCall(condition!!, and, variable, bitmask)
node.children[0] = bittestCall
bittestCall.parent = node
changes++
recurse = false
}
}
recurse
}
return changes
}
internal fun isSame(identifier: PtIdentifier, type: DataType, returnedRegister: RegisterOrPair): Boolean {
if(returnedRegister in Cx16VirtualRegisters) {
val regname = returnedRegister.name.lowercase()

View File

@ -1556,6 +1556,30 @@ $repeatLabel""")
}
}
}
internal fun checkIfConditionCanUseBIT(condition: PtBinaryExpression): Triple<Boolean, PtIdentifier, Int>? {
if(condition.operator == "==" || condition.operator == "!=") {
if (condition.right.asConstInteger() == 0) {
val and = condition.left as? PtBinaryExpression
if (and != null && and.operator == "&" && and.type.isUnsignedByte) {
val bitmask = and.right.asConstInteger()
if(bitmask==128 || bitmask==64) {
val variable = and.left as? PtIdentifier
if (variable != null && variable.type.isByte) {
return Triple(condition.operator=="!=", variable, bitmask)
}
val typecast = and.left as? PtTypeCast
if (typecast != null && typecast.type.isUnsignedByte) {
val castedVariable = typecast.value as? PtIdentifier
if(castedVariable!=null && castedVariable.type.isByte)
return Triple(condition.operator=="!=", castedVariable, bitmask)
}
}
}
}
}
return null
}
}
/**

View File

@ -66,8 +66,6 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
"callfar" -> funcCallFar(fcall, resultRegister)
"callfar2" -> funcCallFar2(fcall, resultRegister)
"call" -> funcCall(fcall)
"prog8_ifelse_bittest_set" -> throw AssemblyError("prog8_ifelse_bittest_set() should have been translated as part of an ifElse statement")
"prog8_ifelse_bittest_notset" -> throw AssemblyError("prog8_ifelse_bittest_notset() should have been translated as part of an ifElse statement")
"prog8_lib_stringcompare" -> funcStringCompare(fcall, resultRegister)
"prog8_lib_square_byte" -> funcSquare(fcall, BaseDataType.UBYTE, resultRegister)
"prog8_lib_square_word" -> funcSquare(fcall, BaseDataType.UWORD, resultRegister)

View File

@ -32,6 +32,15 @@ internal class IfElseAsmGen(private val program: PtProgram,
val compareCond = stmt.condition as? PtBinaryExpression
if(compareCond!=null) {
val useBIT = asmgen.checkIfConditionCanUseBIT(compareCond)
if(useBIT!=null) {
// use a BIT instruction to test for bit 7 or 6 set/clear
val (testBitSet, variable, bitmask) = useBIT
return translateIfBIT(stmt, jumpAfterIf, testBitSet, variable, bitmask)
return
}
val rightDt = compareCond.right.type
return when {
rightDt.isByteOrBool -> translateIfByte(stmt, jumpAfterIf)
@ -58,6 +67,63 @@ internal class IfElseAsmGen(private val program: PtProgram,
throw AssemblyError("weird non-boolean condition node type ${stmt.condition} at ${stmt.condition.position}")
}
private fun translateIfBIT(ifElse: PtIfElse, jumpAfterIf: PtJump?, testForBitSet: Boolean, variable: PtIdentifier, bitmask: Int) {
// use a BIT instruction to test for bit 7 or 6 set/clear
fun branch(branchInstr: String, target: AsmGen6502Internal.JumpTarget) {
require(!target.needsExpressionEvaluation)
if(target.indirect)
throw AssemblyError("cannot BIT to indirect label ${ifElse.position}")
if(ifElse.hasElse())
throw AssemblyError("didn't expect else part here ${ifElse.position}")
else
asmgen.out(" $branchInstr ${target.asmLabel}")
}
when (bitmask) {
128 -> {
// test via bit + N flag
asmgen.out(" bit ${variable.name}")
if(testForBitSet) {
if(jumpAfterIf!=null) {
val target = asmgen.getJumpTarget(jumpAfterIf)
branch("bmi", target)
}
else
translateIfElseBodies("bpl", ifElse)
} else {
if(jumpAfterIf!=null) {
val target = asmgen.getJumpTarget(jumpAfterIf)
branch("bpl", target)
}
else
translateIfElseBodies("bmi", ifElse)
}
return
}
64 -> {
// test via bit + V flag
asmgen.out(" bit ${variable.name}")
if(testForBitSet) {
if(jumpAfterIf!=null) {
val target = asmgen.getJumpTarget(jumpAfterIf)
branch("bvs", target)
}
else
translateIfElseBodies("bvc", ifElse)
} else {
if(jumpAfterIf!=null) {
val target = asmgen.getJumpTarget(jumpAfterIf)
branch("bvc", target)
}
else
translateIfElseBodies("bvs", ifElse)
}
return
}
else -> throw AssemblyError("BIT only works for bits 6 and 7")
}
}
private fun checkNotExtsubReturnsStatusReg(condition: PtExpression) {
val fcall = condition as? PtFunctionCall
@ -70,82 +136,8 @@ internal class IfElseAsmGen(private val program: PtProgram,
}
private fun fallbackTranslateForSimpleCondition(ifElse: PtIfElse) {
val bittest = ifElse.condition as? PtBuiltinFunctionCall
val jumpAfterIf = ifElse.ifScope.children.singleOrNull() as? PtJump
if(bittest!=null && bittest.name.startsWith("prog8_ifelse_bittest_")) {
val variable = bittest.args[0] as PtIdentifier
val bitnumber = (bittest.args[1] as PtNumber).number.toInt()
val testForBitSet = bittest.name.endsWith("_set")
when (bitnumber) {
7 -> {
// test via bit + N flag
asmgen.out(" bit ${variable.name}")
if(testForBitSet) {
if(jumpAfterIf!=null) {
val target = asmgen.getJumpTarget(jumpAfterIf)
require(!target.needsExpressionEvaluation)
if(target.indirect)
throw AssemblyError("cannot BIT to indirect label ${ifElse.position}")
if(ifElse.hasElse())
throw AssemblyError("didn't expect else part here ${ifElse.position}")
else
asmgen.out(" bmi ${target.asmLabel}")
}
else
translateIfElseBodies("bpl", ifElse)
} else {
if(jumpAfterIf!=null) {
val target = asmgen.getJumpTarget(jumpAfterIf)
require(!target.needsExpressionEvaluation)
if(target.indirect)
throw AssemblyError("cannot BIT to indirect label ${ifElse.position}")
if(ifElse.hasElse())
throw AssemblyError("didn't expect else part here ${ifElse.position}")
else
asmgen.out(" bpl ${target.asmLabel}")
}
else
translateIfElseBodies("bmi", ifElse)
}
return
}
6 -> {
// test via bit + V flag
asmgen.out(" bit ${variable.name}")
if(testForBitSet) {
if(jumpAfterIf!=null) {
val target = asmgen.getJumpTarget(jumpAfterIf)
require(!target.needsExpressionEvaluation)
if(target.indirect)
throw AssemblyError("cannot BIT to indirect label ${ifElse.position}")
if(ifElse.hasElse())
throw AssemblyError("didn't expect else part here ${ifElse.position}")
else
asmgen.out(" bvs ${target.asmLabel}")
}
else
translateIfElseBodies("bvc", ifElse)
} else {
if(jumpAfterIf!=null) {
val target = asmgen.getJumpTarget(jumpAfterIf)
require(!target.needsExpressionEvaluation)
if(target.indirect)
throw AssemblyError("cannot BIT to indirect label ${ifElse.position}")
if(ifElse.hasElse())
throw AssemblyError("didn't expect else part here ${ifElse.position}")
else
asmgen.out(" bvc ${target.asmLabel}")
}
else
translateIfElseBodies("bvs", ifElse)
}
return
}
else -> throw AssemblyError("prog8_ifelse_bittest can only work on bits 7 and 6")
}
}
// the condition is "simple" enough to just assign its 0/1 value to a register and branch on that
asmgen.assignConditionValueToRegisterAndTest(ifElse.condition)
if(jumpAfterIf!=null)

View File

@ -1,19 +1,7 @@
package prog8.codegen.cpu6502
import prog8.code.ast.PtBinaryExpression
import prog8.code.ast.PtBuiltinFunctionCall
import prog8.code.ast.PtExpression
import prog8.code.ast.PtIdentifier
import prog8.code.ast.PtIfExpression
import prog8.code.ast.PtNumber
import prog8.code.ast.PtPrefix
import prog8.code.core.AssemblyError
import prog8.code.core.BaseDataType
import prog8.code.core.CpuRegister
import prog8.code.core.DataType
import prog8.code.core.IErrorReporter
import prog8.code.core.LogicalOperators
import prog8.code.core.RegisterOrPair
import prog8.code.ast.*
import prog8.code.core.*
import prog8.codegen.cpu6502.assignment.AsmAssignTarget
import prog8.codegen.cpu6502.assignment.AssignmentAsmGen
import prog8.codegen.cpu6502.assignment.TargetStorageKind
@ -67,31 +55,6 @@ internal class IfExpressionAsmGen(private val asmgen: AsmGen6502Internal, privat
else if(condition is PtPrefix && condition.operator=="not") {
throw AssemblyError("not prefix in ifexpression should have been replaced by swapped values")
} else {
// 'simple' condition, check if it is a byte bittest
val bittest = condition as? PtBuiltinFunctionCall
if(bittest!=null && bittest.name.startsWith("prog8_ifelse_bittest_")) {
val variable = bittest.args[0] as PtIdentifier
val bitnumber = (bittest.args[1] as PtNumber).number.toInt()
val testForBitSet = bittest.name.endsWith("_set")
when (bitnumber) {
7 -> {
// test via bit + N flag
asmgen.out(" bit ${variable.name}")
if(testForBitSet) asmgen.out(" bpl $falseLabel")
else asmgen.out(" bmi $falseLabel")
return
}
6 -> {
// test via bit + V flag
asmgen.out(" bit ${variable.name}")
if(testForBitSet) asmgen.out(" bvc $falseLabel")
else asmgen.out(" bvs $falseLabel")
return
}
else -> throw AssemblyError("prog8_ifelse_bittest can only work on bits 7 and 6")
}
}
// the condition is "simple" enough to just assign its 0/1 value to a register and branch on that
asmgen.assignConditionValueToRegisterAndTest(condition)
asmgen.out(" beq $falseLabel")
@ -338,6 +301,30 @@ internal class IfExpressionAsmGen(private val asmgen: AsmGen6502Internal, privat
private fun translateIfCompareWithZeroByteBranch(condition: PtBinaryExpression, signed: Boolean, falseLabel: String) {
// optimized code for byte comparisons with 0
val useBIT = asmgen.checkIfConditionCanUseBIT(condition)
if(useBIT!=null) {
// use a BIT instruction to test for bit 7 or 6 set/clear
val (testForBitSet, variable, bitmask) = useBIT
when (bitmask) {
128 -> {
// test via bit + N flag
asmgen.out(" bit ${variable.name}")
if(testForBitSet) asmgen.out(" bpl $falseLabel")
else asmgen.out(" bmi $falseLabel")
return
}
64 -> {
// test via bit + V flag
asmgen.out(" bit ${variable.name}")
if(testForBitSet) asmgen.out(" bvc $falseLabel")
else asmgen.out(" bvs $falseLabel")
return
}
else -> throw AssemblyError("BIT can only work on bits 7 and 6")
}
}
asmgen.assignConditionValueToRegisterAndTest(condition.left)
when (condition.operator) {
"==" -> asmgen.out(" bne $falseLabel")

View File

@ -42,8 +42,6 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
"ror" -> funcRolRor(call)
"rol2" -> funcRolRor(call)
"ror2" -> funcRolRor(call)
"prog8_ifelse_bittest_set" -> throw AssemblyError("prog8_ifelse_bittest_set() should have been translated as part of an ifElse/ifExpression statement")
"prog8_ifelse_bittest_notset" -> throw AssemblyError("prog8_ifelse_bittest_notset() should have been translated as part of an ifElse/ifExpression statement")
"prog8_lib_stringcompare" -> funcStringCompare(call)
"prog8_lib_square_byte" -> funcSquare(call, IRDataType.BYTE)
"prog8_lib_square_word" -> funcSquare(call, IRDataType.WORD)

View File

@ -96,15 +96,46 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
if((ifExpr.condition as? PtPrefix)?.operator=="not")
throw AssemblyError("not prefix in ifexpression should have been replaced by swapped values")
// TODO don't store condition as expression result but just use the flags, like a normal PtIfElse translation does
val condTr = translateExpression(ifExpr.condition)
val result = mutableListOf<IRCodeChunkBase>()
val trueTr = translateExpression(ifExpr.truevalue)
val falseTr = translateExpression(ifExpr.falsevalue)
val irDt = irType(ifExpr.type)
val result = mutableListOf<IRCodeChunkBase>()
val falseLabel = codeGen.createLabelName()
val endLabel = codeGen.createLabelName()
val irDt = irType(ifExpr.type)
if(ifExpr.condition is PtBinaryExpression) {
val useBIT = checkIfConditionCanUseBIT(ifExpr.condition as PtBinaryExpression)
if(useBIT!=null) {
// use a BIT instruction to test for bit 7 or 6 set/clear
val (testBitSet, variable, bitmask) = useBIT
val bitBranchOpcode = when(testBitSet) {
true -> when(bitmask) {
64 -> Opcode.BSTVC
128 -> Opcode.BSTPOS
else -> throw AssemblyError("need bit 6 or 7")
}
false -> when(bitmask) {
64 -> Opcode.BSTVS
128 -> Opcode.BSTNEG
else -> throw AssemblyError("need bit 6 or 7")
}
}
result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.BIT, IRDataType.BYTE, labelSymbol = variable.name)
it += IRInstruction(bitBranchOpcode, labelSymbol = falseLabel)
}
addToResult(result, trueTr, trueTr.resultReg, -1)
addInstr(result, IRInstruction(Opcode.JUMP, labelSymbol = endLabel), null)
result += IRCodeChunk(falseLabel, null)
addToResult(result, falseTr, trueTr.resultReg, -1)
result += IRCodeChunk(endLabel, null)
return ExpressionCodeResult(result, irDt, trueTr.resultReg, -1)
}
}
// TODO don't store condition as expression result but just use the flags, like a normal PtIfElse translation does
val condTr = translateExpression(ifExpr.condition)
addToResult(result, condTr, condTr.resultReg, -1)
addInstr(result, IRInstruction(Opcode.CMPI, IRDataType.BYTE, reg1=condTr.resultReg, immediate = 0), null)
addInstr(result, IRInstruction(Opcode.BSTEQ, labelSymbol = falseLabel), null)
@ -533,7 +564,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
}
}
fun translate(fcall: PtFunctionCall): ExpressionCodeResult {
internal fun translate(fcall: PtFunctionCall): ExpressionCodeResult {
val callTarget = codeGen.symbolTable.flat.getValue(fcall.name)
if(callTarget.scopedName in listOf("sys.push", "sys.pushw", "sys.pop", "sys.popw", "floats.push", "floats.pop")) {
@ -725,6 +756,31 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
}
}
internal fun checkIfConditionCanUseBIT(condition: PtBinaryExpression): Triple<Boolean, PtIdentifier, Int>? {
// test for occurrence of: x & 64 != 0 (or 128) , this can be performed with a BIT instruction
if(condition.operator == "==" || condition.operator == "!=") {
if (condition.right.asConstInteger() == 0) {
val and = condition.left as? PtBinaryExpression
if (and != null && and.operator == "&" && and.type.isUnsignedByte) {
val bitmask = and.right.asConstInteger()
if(bitmask==128 || bitmask==64) {
val variable = and.left as? PtIdentifier
if (variable != null && variable.type.isByte) {
return Triple(condition.operator=="!=", variable, bitmask)
}
val typecast = and.left as? PtTypeCast
if (typecast != null && typecast.type.isUnsignedByte) {
val castedVariable = typecast.value as? PtIdentifier
if(castedVariable!=null && castedVariable.type.isByte)
return Triple(condition.operator=="!=", castedVariable, bitmask)
}
}
}
}
}
return null
}
private fun translateStackFunctions(fcall: PtFunctionCall, callTarget: StNode): ExpressionCodeResult {
val chunk = mutableListOf<IRCodeChunkBase>()
when(callTarget.scopedName) {

View File

@ -1453,53 +1453,6 @@ class IRCodeGen(
val result = mutableListOf<IRCodeChunkBase>()
fun translateSimple(condition: PtExpression, jumpFalseOpcode: Opcode, addCmpiZero: Boolean) {
fun ifElseUsingBIT(testBitSet: Boolean, value: PtIdentifier, bitnumber: Int) {
addInstr(result, IRInstruction(Opcode.BIT, IRDataType.BYTE, labelSymbol = value.name), null)
val bitBranchOpcode = when(testBitSet) {
true -> when(bitnumber) {
6 -> Opcode.BSTVC
7 -> Opcode.BSTPOS
else -> throw AssemblyError("need bit 6 or 7")
}
false -> when(bitnumber) {
6 -> Opcode.BSTVS
7 -> Opcode.BSTNEG
else -> throw AssemblyError("need bit 6 or 7")
}
}
if(ifElse.hasElse()) {
val elseLabel = createLabelName()
val afterIfLabel = createLabelName()
addInstr(result, IRInstruction(bitBranchOpcode, labelSymbol = elseLabel), null)
result += translateNode(ifElse.ifScope)
addInstr(result, IRInstruction(Opcode.JUMP, labelSymbol = afterIfLabel), null)
result += labelFirstChunk(translateNode(ifElse.elseScope), elseLabel)
result += IRCodeChunk(afterIfLabel, null)
} else {
val afterIfLabel = createLabelName()
addInstr(result, IRInstruction(bitBranchOpcode, labelSymbol = afterIfLabel), null)
result += translateNode(ifElse.ifScope)
result += IRCodeChunk(afterIfLabel, null)
}
}
if(condition is PtBuiltinFunctionCall && condition.name.startsWith("prog8_ifelse_bittest_")) {
// use a BIT instruction to test for bit 8 or 7
when(condition.name) {
"prog8_ifelse_bittest_set" -> {
ifElseUsingBIT(true, condition.args[0] as PtIdentifier, condition.args[1].asConstInteger()!!)
return
}
"prog8_ifelse_bittest_notset" -> {
ifElseUsingBIT(false, condition.args[0] as PtIdentifier, condition.args[1].asConstInteger()!!)
return
}
else -> throw AssemblyError("weird bittest")
}
}
val tr = expressionEval.translateExpression(condition)
if(addCmpiZero)
tr.chunks.last().instructions.add(IRInstruction(Opcode.CMPI, tr.dt, reg1 = tr.resultReg, immediate = 0))
@ -1524,6 +1477,41 @@ class IRCodeGen(
if(condition.operator in LogicalOperators)
return translateSimple(condition, Opcode.BSTEQ, false)
val useBIT = expressionEval.checkIfConditionCanUseBIT(condition)
if(useBIT!=null) {
// use a BIT instruction to test for bit 7 or 6 set/clear
val (testBitSet, variable, bitmask) = useBIT
addInstr(result, IRInstruction(Opcode.BIT, IRDataType.BYTE, labelSymbol = variable.name), null)
val bitBranchOpcode = when(testBitSet) {
true -> when(bitmask) {
64 -> Opcode.BSTVC
128 -> Opcode.BSTPOS
else -> throw AssemblyError("need bit 6 or 7")
}
false -> when(bitmask) {
64 -> Opcode.BSTVS
128 -> Opcode.BSTNEG
else -> throw AssemblyError("need bit 6 or 7")
}
}
if(ifElse.hasElse()) {
val elseLabel = createLabelName()
val afterIfLabel = createLabelName()
addInstr(result, IRInstruction(bitBranchOpcode, labelSymbol = elseLabel), null)
result += translateNode(ifElse.ifScope)
addInstr(result, IRInstruction(Opcode.JUMP, labelSymbol = afterIfLabel), null)
result += labelFirstChunk(translateNode(ifElse.elseScope), elseLabel)
result += IRCodeChunk(afterIfLabel, null)
} else {
val afterIfLabel = createLabelName()
addInstr(result, IRInstruction(bitBranchOpcode, labelSymbol = afterIfLabel), null)
result += translateNode(ifElse.ifScope)
result += IRCodeChunk(afterIfLabel, null)
}
return
}
val signed = condition.left.type.isSigned
val elseBranchFirstReg: Int
val elseBranchSecondReg: Int

View File

@ -570,7 +570,7 @@ main {
test("bit instruction is used for testing bits 6 and 7 of a byte") {
val text = """
main {
sub start() {
sub start() {
if cx16.r0L & ${'$'}80 != 0
return
if cx16.r1L & ${'$'}80 == 0
@ -579,6 +579,8 @@ main {
return
if cx16.r3L & ${'$'}40 == 0
return
cx16.r9L = if cx16.r4L & ${'$'}80 != 0 11 else 22
cx16.r10L = if cx16.r5L & ${'$'}40 == 0 11 else 22
}
}"""
val result = compileText(C64Target(), true, text, writeAssembly = true)!!
@ -588,6 +590,8 @@ main {
assembly shouldContain "bit cx16.r1L"
assembly shouldContain "bit cx16.r2L"
assembly shouldContain "bit cx16.r3L"
assembly shouldContain "bit cx16.r4L"
assembly shouldContain "bit cx16.r5L"
val resultIR = compileText(VMTarget(), true, text, writeAssembly = true)!!
val irFile = resultIR.compilationOptions.outputDir.resolve(result.compilerAst.name + ".p8ir")
@ -596,5 +600,7 @@ main {
ir shouldContain "bit.b ${'$'}ff04" // r1
ir shouldContain "bit.b ${'$'}ff06" // f2
ir shouldContain "bit.b ${'$'}ff08" // r3
ir shouldContain "bit.b ${'$'}ff0a" // r4
ir shouldContain "bit.b ${'$'}ff0c" // r5
}
})

View File

@ -35,7 +35,6 @@ Future Things and Ideas
Once new codegen is written that is based on the IR, this point is mostly moot anyway as that will have its own dead code removal on the IR level.
- Allow normal subroutines to return multiple values as well (just as asmsubs already can)
- don't do BIT instruction tests via optimizeBitTest() using a fake builtin function. Do it in the code generator when it encounters the correct bitwise and sequence. (also IR)
- Change scoping rules for qualified symbols so that they don't always start from the root but behave like other programming languages (look in local scope first)
- something to reduce the need to use fully qualified names all the time. 'with' ? Or 'using <prefix>'?
- Improve register load order in subroutine call args assignments:
@ -55,7 +54,7 @@ Future Things and Ideas
IR/VM
-----
- make a liast in the P8IR file of the data type of every used virtual register (it should have 1 unique type assigned to it when it is allocated, and never used for other types)
- make a list in the P8IR file of the data type of every used virtual register (it should have 1 unique type assigned to it when it is allocated, and never used for other types) via usedRegisters()
- getting it in shape for code generation...
- fix TODO("IR rol/ror on split words array")
- fix "<< in array" / ">> in array"

View File

@ -1,4 +1,3 @@
%import floats
%import textio
%zeropage basicsafe
%option no_sysinit
@ -6,35 +5,22 @@
main {
sub start() {
if cx16.r0L & $80 != 0
return
if cx16.r1L & $80 == 0
return
if cx16.r2L & $40 != 0
return
if cx16.r3L & $40 == 0
return
cx16.r0L = 0
test()
txt.nl()
cx16.r0L = 255
test()
cx16.r9L = if cx16.r0L & $80 != 0 11 else 22
cx16.r10L = if cx16.r0L & $40 == 0 11 else 22
txt.print_ub(cx16.r9L)
txt.spc()
txt.print_ub(cx16.r10L)
txt.nl()
sub test() {
if cx16.r0L & $80 != 0
txt.chrout('1')
if cx16.r0L & $80 == 0
txt.chrout('2')
if cx16.r0L & $40 != 0
txt.chrout('3')
if cx16.r0L & $40 == 0
txt.chrout('4')
if cx16.r0L & $20 != 0
txt.chrout('5')
if cx16.r0L & $20 == 0
txt.chrout('6')
}
cx16.r0L = 255
cx16.r9L = if cx16.r0L & $80 != 0 11 else 22
cx16.r10L = if cx16.r0L & $40 == 0 11 else 22
txt.print_ub(cx16.r9L)
txt.spc()
txt.print_ub(cx16.r10L)
txt.nl()
}
}

View File

@ -172,9 +172,6 @@ class VirtualMachine(irProgram: IRProgram) {
}
is IRInlineAsmChunk -> TODO("branch to inline asm chunk")
is IRInlineBinaryChunk -> throw IllegalArgumentException("can't branch to inline binary chunk")
else -> {
throw IllegalArgumentException("VM can't execute code in a non-codechunk: $target")
}
}
}
@ -349,8 +346,6 @@ class VirtualMachine(irProgram: IRProgram) {
Opcode.FCEIL -> InsFCEIL(ins)
Opcode.FCOMP -> InsFCOMP(ins)
Opcode.ALIGN -> nextPc() // actual alignment ignored in the VM
else -> throw IllegalArgumentException("invalid opcode ${ins.opcode}")
}
}