mirror of
https://github.com/irmen/prog8.git
synced 2025-02-13 18:31:04 +00:00
better way of doing BIT instructions
This commit is contained in:
parent
827df04b32
commit
056c0a24d9
@ -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)),
|
||||
|
@ -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()
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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")
|
||||
|
@ -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)
|
||||
|
@ -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) {
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
}
|
||||
})
|
@ -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"
|
||||
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
@ -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}")
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user