mirror of
https://github.com/KarolS/millfork.git
synced 2025-04-13 21:37:08 +00:00
6502: Optimize some word operations
This commit is contained in:
parent
b68e4b67c8
commit
5c3637e2ea
@ -1085,9 +1085,7 @@ object MosExpressionCompiler extends AbstractExpressionCompiler[AssemblyLine] {
|
||||
val store = expressionStorageFromAW(ctx, exprTypeAndVariable, expr.position)
|
||||
calculate ++ store
|
||||
} else {
|
||||
val calculate = PseudoregisterBuiltIns.compileWordAdditionToAX(ctx, params, decimal = decimal)
|
||||
val store = expressionStorageFromAX(ctx, exprTypeAndVariable, expr.position)
|
||||
calculate ++ store
|
||||
PseudoregisterBuiltIns.compileWordAdditionViaAX(ctx, exprTypeAndVariable, expr.position, params, decimal = decimal)
|
||||
}
|
||||
case _ =>
|
||||
ctx.log.error("Non-in-place addition or subtraction of variables larger than 2 bytes is not supported", expr.position)
|
||||
|
@ -9,11 +9,87 @@ import millfork.env._
|
||||
import millfork.error.ConsoleLogger
|
||||
import millfork.node._
|
||||
|
||||
import scala.collection.mutable
|
||||
import scala.collection.mutable.ListBuffer
|
||||
|
||||
/**
|
||||
* @author Karol Stasiak
|
||||
*/
|
||||
object PseudoregisterBuiltIns {
|
||||
|
||||
def compileWordAdditionViaAX(ctx: CompilationContext, target: Option[(Type, Variable)], position: Option[Position], params: List[(Boolean, Expression)], decimal: Boolean): List[AssemblyLine] = {
|
||||
val b = ctx.env.get[Type]("byte")
|
||||
val w = ctx.env.get[Type]("word")
|
||||
val store = MosExpressionCompiler.expressionStorageFromAX(ctx, target, position)
|
||||
if (!decimal && params.length == 2) {
|
||||
store match {
|
||||
case List(AssemblyLine0(STA, Absolute | ZeroPage, _), AssemblyLine0(STX, Absolute | ZeroPage, _)) =>
|
||||
val reads = params.map(p => p._1 -> MosExpressionCompiler.compileToAX(ctx, p._2))
|
||||
fastBinaryWordAddition(reads, store.map(x => List(x.copy(opcode = STA)))).foreach(return _)
|
||||
case _ =>
|
||||
}
|
||||
}
|
||||
compileWordAdditionToAX(ctx, params, decimal = decimal) ++ store
|
||||
}
|
||||
|
||||
private def fastBinaryWordAddition(reads: List[(Boolean, List[AssemblyLine])], stores: List[List[AssemblyLine]]): Option[List[AssemblyLine]] = {
|
||||
val countYs = reads.count(_._2.exists(_.concernsY))
|
||||
if (countYs > 1 || countYs == 1 && stores.exists(_.exists(_.concernsY))) return None
|
||||
val result = ListBuffer[AssemblyLine]()
|
||||
var hard = Option.empty[List[AssemblyLine]]
|
||||
val niceReads = mutable.ListBuffer[(List[AssemblyLine], List[AssemblyLine])]()
|
||||
var constant = Constant.Zero
|
||||
var counter = 0
|
||||
for ((subtract, read) <- reads) {
|
||||
read match {
|
||||
case List(AssemblyLine0(LDA, Immediate, l), AssemblyLine0(LDX, Immediate, h)) =>
|
||||
if (subtract) constant -= h.asl(8).+(l).quickSimplify
|
||||
else constant += h.asl(8).+(l).quickSimplify
|
||||
case List(l@AssemblyLine0(LDA, Absolute | ZeroPage | Immediate, _), h@AssemblyLine0(LDX, Absolute | ZeroPage | Immediate, _)) =>
|
||||
if (subtract) niceReads.+=(List(AssemblyLine.implied(SEC), l.copy(opcode = SBC)) -> List(h.copy(opcode = SBC)))
|
||||
else niceReads += (List(AssemblyLine.implied(CLC), l.copy(opcode = ADC)) -> List(h.copy(opcode = ADC)))
|
||||
counter += 1
|
||||
case _ =>
|
||||
if (hard.isDefined || subtract) return None
|
||||
hard = Some(read)
|
||||
counter += 1
|
||||
}
|
||||
}
|
||||
if (!constant.isProvablyZero) {
|
||||
counter += 1
|
||||
}
|
||||
if (counter > 2) {
|
||||
return None
|
||||
}
|
||||
hard match {
|
||||
case Some(ax) =>
|
||||
niceReads.prepend(ax -> List(AssemblyLine.implied(TXA)))
|
||||
if (!constant.isProvablyZero) {
|
||||
if (constant.isQuiteNegative) {
|
||||
niceReads += List(AssemblyLine.implied(CLC), AssemblyLine.immediate(ADC, constant.loByte)) -> List(AssemblyLine.immediate(ADC, constant.hiByte))
|
||||
} else {
|
||||
val negC = Constant.Zero.-(constant).quickSimplify.quickSimplify
|
||||
niceReads += List(AssemblyLine.implied(SEC), AssemblyLine.immediate(SBC, negC.loByte)) -> List(AssemblyLine.immediate(SBC, negC.hiByte))
|
||||
}
|
||||
}
|
||||
case None =>
|
||||
if (constant.isProvablyZero) {
|
||||
if (niceReads.nonEmpty && niceReads.head._1.head.opcode == SEC) return None
|
||||
niceReads(0) = (List(niceReads.head._1.last.copy(opcode = LDA)) -> List(niceReads.head._2.last.copy(opcode = LDA)))
|
||||
} else {
|
||||
niceReads.prepend(List(AssemblyLine.immediate(LDA, constant.loByte)) -> List(AssemblyLine.immediate(LDA, constant.hiByte)))
|
||||
}
|
||||
}
|
||||
for (b <- 0 to 1) {
|
||||
for (read <- niceReads) {
|
||||
if (b == 0) result ++= read._1
|
||||
else result ++= read._2
|
||||
}
|
||||
result ++= stores(b)
|
||||
}
|
||||
Some(result.toList)
|
||||
}
|
||||
|
||||
def compileWordAdditionToAX(ctx: CompilationContext, params: List[(Boolean, Expression)], decimal: Boolean): List[AssemblyLine] = {
|
||||
val b = ctx.env.get[Type]("byte")
|
||||
val w = ctx.env.get[Type]("word")
|
||||
@ -200,6 +276,27 @@ object PseudoregisterBuiltIns {
|
||||
}
|
||||
|
||||
|
||||
private def compileFastWordBitOpsToAX(reads: List[List[AssemblyLine]], op: Opcode.Value): Option[List[AssemblyLine]] = {
|
||||
val resultLo = mutable.ListBuffer[AssemblyLine]()
|
||||
val resultHi = mutable.ListBuffer[AssemblyLine]()
|
||||
for(read <- reads) {
|
||||
read match {
|
||||
case List(l@AssemblyLine0(LDA, Immediate | Absolute | ZeroPage, _), h@AssemblyLine0(LDX, Immediate | Absolute | ZeroPage, _)) =>
|
||||
if (resultHi.isEmpty) {
|
||||
resultHi += h.copy(opcode = LDA)
|
||||
resultLo += l
|
||||
} else {
|
||||
resultHi += h.copy(opcode = op)
|
||||
resultLo += l.copy(opcode = op)
|
||||
}
|
||||
case _ => return None
|
||||
}
|
||||
}
|
||||
resultHi += AssemblyLine.implied(TAX)
|
||||
resultHi ++= resultLo
|
||||
Some(resultHi.toList)
|
||||
}
|
||||
|
||||
def compileWordBitOpsToAX(ctx: CompilationContext, params: List[Expression], op: Opcode.Value): List[AssemblyLine] = {
|
||||
if (ctx.options.zpRegisterSize < 2) {
|
||||
ctx.log.error("Word bit operation requires the zeropage pseudoregister", params.headOption.flatMap(_.position))
|
||||
@ -208,6 +305,8 @@ object PseudoregisterBuiltIns {
|
||||
if (params.isEmpty) {
|
||||
return List(AssemblyLine.immediate(LDA, 0), AssemblyLine.immediate(LDX, 0))
|
||||
}
|
||||
compileFastWordBitOpsToAX(params.map(p => MosExpressionCompiler.compileToAX(ctx, p)), op).foreach(return _)
|
||||
|
||||
val b = ctx.env.get[Type]("byte")
|
||||
val w = ctx.env.get[Type]("word")
|
||||
val reg = ctx.env.get[VariableInMemory]("__reg")
|
||||
@ -292,6 +391,31 @@ object PseudoregisterBuiltIns {
|
||||
ctx.env.eval(r) match {
|
||||
case Some(NumericConstant(0, _)) =>
|
||||
List(AssemblyLine.zeropage(LDA, reg), AssemblyLine.zeropage(LDX, reg, 1))
|
||||
case Some(NumericConstant(1, _)) if (firstParamCompiled match {
|
||||
case List(
|
||||
AssemblyLine0(LDA, ZeroPage | Absolute | Immediate, _),
|
||||
AssemblyLine0(STA, ZeroPage, _),
|
||||
AssemblyLine0(LDA, ZeroPage | Absolute | Immediate, _),
|
||||
AssemblyLine0(STA, ZeroPage, _)) => true
|
||||
case _ => false
|
||||
}) =>
|
||||
if (left) {
|
||||
List(
|
||||
firstParamCompiled(0),
|
||||
AssemblyLine.implied(ASL),
|
||||
AssemblyLine.zeropage(STA, reg),
|
||||
firstParamCompiled(2),
|
||||
AssemblyLine.implied(ROL),
|
||||
AssemblyLine.implied(TAX),
|
||||
AssemblyLine.zeropage(LDA, reg))
|
||||
} else {
|
||||
List(
|
||||
firstParamCompiled(2),
|
||||
AssemblyLine.implied(LSR),
|
||||
AssemblyLine.implied(TAX),
|
||||
firstParamCompiled(0),
|
||||
AssemblyLine.implied(ROR))
|
||||
}
|
||||
case Some(NumericConstant(v, _)) if v > 0 && unrollShift(ctx, v, 2, 4) =>
|
||||
if (ctx.options.flag(CompilationFlag.EmitNative65816Opcodes)) {
|
||||
firstParamCompiled ++
|
||||
|
11
src/main/scala/millfork/env/Constant.scala
vendored
11
src/main/scala/millfork/env/Constant.scala
vendored
@ -20,6 +20,7 @@ sealed trait Constant {
|
||||
|
||||
def toIntelString: String
|
||||
|
||||
def isQuiteNegative: Boolean = false
|
||||
def isProvablyZero: Boolean = false
|
||||
def isProvably(value: Int): Boolean = false
|
||||
def isProvablyInRange(startInclusive: Int, endInclusive: Int): Boolean = false
|
||||
@ -105,10 +106,10 @@ sealed trait Constant {
|
||||
def fitsInto(typ: Type): Boolean = true // TODO
|
||||
|
||||
final def succ: Constant = (this + 1).quickSimplify
|
||||
|
||||
}
|
||||
|
||||
case class AssertByte(c: Constant) extends Constant {
|
||||
override def isQuiteNegative: Boolean = c.isQuiteNegative
|
||||
override def isProvablyGreaterOrEqualThan(other: Constant): Boolean = c.isProvablyGreaterOrEqualThan(other)
|
||||
override def isProvablyZero: Boolean = c.isProvablyZero
|
||||
override def isProvably(i: Int): Boolean = c.isProvably(i)
|
||||
@ -185,6 +186,7 @@ case class NumericConstant(value: Long, requiredSize: Int) extends Constant {
|
||||
throw new IllegalArgumentException(s"The constant $value is too big")
|
||||
}
|
||||
}
|
||||
override def isQuiteNegative: Boolean = value < 0
|
||||
override def isProvablyGreaterOrEqualThan(other: Constant): Boolean = value >= 0 && (other match {
|
||||
case NumericConstant(o, _) if o >= 0 => value >= o
|
||||
case _ => false
|
||||
@ -327,6 +329,13 @@ object MathOperator extends Enumeration {
|
||||
|
||||
case class CompoundConstant(operator: MathOperator.Value, lhs: Constant, rhs: Constant) extends Constant {
|
||||
|
||||
override def isQuiteNegative: Boolean = operator match {
|
||||
case MathOperator.Minus =>
|
||||
lhs.isQuiteNegative || lhs.isInstanceOf[NumericConstant] && rhs.isInstanceOf[MemoryAddressConstant]
|
||||
case _ =>
|
||||
false
|
||||
}
|
||||
|
||||
override def isProvablyNonnegative: Boolean = {
|
||||
import MathOperator._
|
||||
operator match {
|
||||
|
@ -59,4 +59,17 @@ class BitOpSuite extends FunSuite with Matchers {
|
||||
| }
|
||||
""".stripMargin)(_.readLong(0xc000) should equal(6))
|
||||
}
|
||||
test("Word AND 2") {
|
||||
EmuCrossPlatformBenchmarkRun(Cpu.Mos, Cpu.Z80, Cpu.Intel8080, Cpu.Sharp, Cpu.Intel8086)("""
|
||||
| word output @$c000
|
||||
| void main () {
|
||||
| word v, w
|
||||
| v = $ee0e
|
||||
| w = $d0dd
|
||||
| barrier()
|
||||
| output = v & w & v & w
|
||||
| }
|
||||
| noinline void barrier(){}
|
||||
""".stripMargin)(_.readWord(0xc000) should equal(0xc00c))
|
||||
}
|
||||
}
|
||||
|
@ -80,6 +80,19 @@ class ShiftSuite extends FunSuite with Matchers {
|
||||
""".stripMargin)(_.readWord(0xc000) should equal(0x180))
|
||||
}
|
||||
|
||||
test("Word shifting via pseudoregister 2") {
|
||||
EmuCrossPlatformBenchmarkRun(Cpu.Mos/*, Cpu.Z80, Cpu.Intel8080, Cpu.Sharp, Cpu.Intel8086*/)("""
|
||||
| word output @$c000
|
||||
| void main () {
|
||||
| word w
|
||||
| w = three()
|
||||
| output = w << 1
|
||||
| }
|
||||
| word three() { return 3 }
|
||||
| word identity(word w) { return w }
|
||||
""".stripMargin)(_.readWord(0xc000) should equal(6))
|
||||
}
|
||||
|
||||
test("Variable shifting") {
|
||||
EmuCrossPlatformBenchmarkRun(Cpu.Mos, Cpu.Z80, Cpu.Intel8080, Cpu.Sharp, Cpu.Intel8086)("""
|
||||
| word output0 @$c000
|
||||
|
Loading…
x
Reference in New Issue
Block a user