1
0
mirror of https://github.com/KarolS/millfork.git synced 2024-05-31 18:41:30 +00:00

6502: Faster accesses to small arrays

This commit is contained in:
Karol Stasiak 2019-10-22 16:35:17 +02:00
parent d36b83421c
commit b7300616d1
4 changed files with 72 additions and 17 deletions

View File

@ -28,7 +28,7 @@
* Optimized certain byte comparisons.
* 6502: Optimized certain boolean conversions.
* 6502: Optimized certain boolean conversions and some small array accesses.
* Unused built-in functions are now removed more accurately.

View File

@ -408,7 +408,14 @@ object MosExpressionCompiler extends AbstractExpressionCompiler[AssemblyLine] {
Nil
}
case DerefExpression(inner, offset, targetType) =>
val (prepare, addr, am) = getPhysicalPointerForDeref(ctx, inner)
val (prepare, addr, am, fast) = getPhysicalPointerForDeref(ctx, inner)
if (targetType.size == 1) {
fast match {
case Some((fastBase, fastIndex)) =>
return preserveRegisterIfNeeded(ctx, MosRegister.A, fastIndex(offset)) ++ List(AssemblyLine.absoluteY(STA, fastBase))
case _ =>
}
}
val lo = preserveRegisterIfNeeded(ctx, MosRegister.A, prepare) ++ List(AssemblyLine.immediate(LDY, offset), AssemblyLine(STA, am, addr))
if (targetType.size == 1) {
lo
@ -523,17 +530,42 @@ object MosExpressionCompiler extends AbstractExpressionCompiler[AssemblyLine] {
compile(ctx, expr, Some(p -> env.get[Variable]("__reg.b2b3")), BranchSpec.None)
}
def getPhysicalPointerForDeref(ctx: CompilationContext, pointerExpression: Expression): (List[AssemblyLine], Constant, AddrMode.Value) = {
def getPhysicalPointerForDeref(ctx: CompilationContext, pointerExpression: Expression): (List[AssemblyLine], Constant, AddrMode.Value, Option[(Constant, Int => List[AssemblyLine])]) = {
pointerExpression match {
case VariableExpression(name) =>
val p = ctx.env.get[ThingInMemory](name)
if (p.isInstanceOf[MfArray]) return (Nil, p.toAddress, AddrMode.AbsoluteY)
if (p.zeropage) return (Nil, p.toAddress, AddrMode.IndexedY)
p match {
case array: MfArray => return (Nil, p.toAddress, AddrMode.AbsoluteY,
if (array.sizeInBytes <= 256) Some(p.toAddress, (i: Int) => List(AssemblyLine.immediate(LDY, i))) else None)
case _ =>
}
if (p.zeropage) return (Nil, p.toAddress, AddrMode.IndexedY, None)
case _ =>
}
ctx.env.eval(pointerExpression) match {
case Some(addr) => (Nil, addr, AddrMode.AbsoluteY)
case _ => (compileToZReg(ctx, pointerExpression), ctx.env.get[ThingInMemory]("__reg.loword").toAddress, AddrMode.IndexedY)
case Some(addr) => (Nil, addr, AddrMode.AbsoluteY, None)
case _ =>
val baseAndIndex: Option[(Expression, Expression)] = pointerExpression match {
case SumExpression(List((false, base), (false, index)), false) => Some(base -> index)
case FunctionCallExpression(pointerType, List(SumExpression(List((false, base), (false, index)), false))) if pointerType.startsWith("pointer.") =>
Some(base -> index)
case _ => None
}
(compileToZReg(ctx, pointerExpression), ctx.env.get[ThingInMemory]("__reg.loword").toAddress, AddrMode.IndexedY, baseAndIndex match {
case Some((base, index)) =>
val itype = AbstractExpressionCompiler.getExpressionType(ctx, index)
if (itype.size != 1 || itype.isSigned) {
None
} else {
ctx.env.eval(base).map { baseConst =>
baseConst -> { (i: Int) =>
val b = ctx.env.get[Type]("byte")
compile(ctx, index #+# i, Some(b -> RegisterVariable(MosRegister.Y, b)), BranchSpec.None)
}
}
}
case _ => None
})
}
}
@ -1035,19 +1067,27 @@ object MosExpressionCompiler extends AbstractExpressionCompiler[AssemblyLine] {
}
}
case DerefExpression(inner, offset, targetType) =>
val (prepare, addr, am) = getPhysicalPointerForDeref(ctx, inner)
(targetType.size, am) match {
case (1, AbsoluteY) =>
val (prepare, addr, am, fast) = getPhysicalPointerForDeref(ctx, inner)
(fast, targetType.size, am) match {
case (Some((fastBase, fastIndex)), 1, _) =>
fastIndex(offset) ++ List(AssemblyLine.absoluteY(LDA, fastBase)) ++ expressionStorageFromA(ctx, exprTypeAndVariable, expr.position, exprType.isSigned)
case (_, 1, AbsoluteY) =>
prepare ++ List(AssemblyLine.absolute(LDA, addr + offset)) ++ expressionStorageFromA(ctx, exprTypeAndVariable, expr.position, exprType.isSigned)
case (1, _) =>
case (_, 1, _) =>
prepare ++ List(AssemblyLine.immediate(LDY, offset), AssemblyLine(LDA, am, addr)) ++ expressionStorageFromA(ctx, exprTypeAndVariable, expr.position, exprType.isSigned)
case (2, AbsoluteY) =>
case (Some((fastBase, fastIndex)), 2, _) =>
fastIndex(offset) ++ List(
AssemblyLine.absoluteY(LDA, fastBase),
AssemblyLine.implied(INY),
AssemblyLine.absoluteY(LDX, fastBase)) ++
expressionStorageFromAX(ctx, exprTypeAndVariable, expr.position)
case (_, 2, AbsoluteY) =>
prepare ++
List(
AssemblyLine.absolute(LDA, addr + offset),
AssemblyLine.absolute(LDX, addr + offset + 1)) ++
expressionStorageFromAX(ctx, exprTypeAndVariable, expr.position)
case (2, _) =>
case (_, 2, _) =>
prepare ++
List(
AssemblyLine.immediate(LDY, offset+1),
@ -2058,7 +2098,7 @@ object MosExpressionCompiler extends AbstractExpressionCompiler[AssemblyLine] {
ctx.log.error("Invalid left-hand-side use of `:`")
Nil
case DerefExpression(inner, offset, targetType) =>
val (prepare, addr, am) = getPhysicalPointerForDeref(ctx, inner)
val (prepare, addr, am, fastTarget) = getPhysicalPointerForDeref(ctx, inner)
env.eval(source) match {
case Some(constant) =>
am match {
@ -2104,7 +2144,7 @@ object MosExpressionCompiler extends AbstractExpressionCompiler[AssemblyLine] {
Nil
}
case DerefExpression(innerSource, sourceOffset, _) =>
val (prepareSource, addrSource, amSource) = getPhysicalPointerForDeref(ctx, innerSource)
val (prepareSource, addrSource, amSource, fastSource) = getPhysicalPointerForDeref(ctx, innerSource)
(am, amSource) match {
case (AbsoluteY, AbsoluteY) =>
prepare ++ prepareSource ++ (0 until targetType.size).flatMap { i =>

View File

@ -39,14 +39,14 @@ sealed trait Expression extends Node {
def isPure: Boolean
def getAllIdentifiers: Set[String]
def #+#(smallInt: Int): Expression = (this #+# LiteralExpression(smallInt, 1).pos(this.position)).pos(this.position)
def #+#(smallInt: Int): Expression = if (smallInt == 0) this else (this #+# LiteralExpression(smallInt, 1).pos(this.position)).pos(this.position)
def #+#(that: Expression): Expression = that match {
case SumExpression(params, false) => SumExpression((false -> this) :: params, decimal = false)
case _ => SumExpression(List(false -> this, false -> that), decimal = false)
}
def #*#(smallInt: Int): Expression =
if (smallInt == 1) this else FunctionCallExpression("*", List(this, LiteralExpression(smallInt, 1).pos(this.position))).pos(this.position)
def #-#(smallInt: Int): Expression = (this #-# LiteralExpression(smallInt, 1).pos(this.position)).pos(this.position)
def #-#(smallInt: Int): Expression = if (smallInt == 0) this else (this #-# LiteralExpression(smallInt, 1).pos(this.position)).pos(this.position)
def #-#(that: Expression): Expression = SumExpression(List(false -> this, true -> that), decimal = false)
@transient var typeCache: Type = _

View File

@ -610,4 +610,19 @@ class ArraySuite extends FunSuite with Matchers with AppendedClues {
m.readLong(0xc024) should equal(0) withClue "b4..b7 of a[4] initted from stack int40"
}
}
test("Fast small array indexing") {
EmuBenchmarkRun(
"""
| array(word) words[10] @$c000
| noinline byte getLo(byte i) = words[i].lo
| noinline byte getHi(byte i) = words[i].hi
| noinline word get(byte i) = words[i]
| void main () {
| get(5)
| }
""".stripMargin){ m =>
}
}
}