From 6f00cacc6d33a5385374e1c98a0445ee2aa83ff0 Mon Sep 17 00:00:00 2001 From: Karol Stasiak Date: Mon, 18 Mar 2019 20:01:40 +0100 Subject: [PATCH] 6502: Optimize 16-bit multiplication by a constant --- .../scala/millfork/compiler/mos/BuiltIns.scala | 4 ++++ .../compiler/mos/PseudoregisterBuiltIns.scala | 14 ++++++++++++++ src/main/scala/millfork/env/Constant.scala | 3 +++ 3 files changed, 21 insertions(+) diff --git a/src/main/scala/millfork/compiler/mos/BuiltIns.scala b/src/main/scala/millfork/compiler/mos/BuiltIns.scala index 180ce211..68047505 100644 --- a/src/main/scala/millfork/compiler/mos/BuiltIns.scala +++ b/src/main/scala/millfork/compiler/mos/BuiltIns.scala @@ -862,6 +862,8 @@ object BuiltIns { } } + private def isPowerOfTwoUpTo15(n: Long): Boolean = if (n <= 0 || n >= 0x8000) false else 0 == ((n-1) & n) + def compileInPlaceWordMultiplication(ctx: CompilationContext, v: LhsExpression, addend: Expression): List[AssemblyLine] = { val b = ctx.env.get[Type]("byte") val w = ctx.env.get[Type]("word") @@ -870,6 +872,8 @@ object BuiltIns { MosExpressionCompiler.compile(ctx, v, None, NoBranching) ++ MosExpressionCompiler.compileAssignment(ctx, LiteralExpression(0, 2), v) case Some(NumericConstant(1, _)) => MosExpressionCompiler.compile(ctx, v, None, NoBranching) + case Some(NumericConstant(n, _)) if isPowerOfTwoUpTo15(n) => + BuiltIns.compileInPlaceWordOrLongShiftOps(ctx, v, LiteralExpression(java.lang.Long.bitCount(n - 1), 1), aslRatherThanLsr = true) case _ => // TODO: optimize? PseudoregisterBuiltIns.compileWordMultiplication(ctx, Some(v), addend, storeInRegLo = true) ++ diff --git a/src/main/scala/millfork/compiler/mos/PseudoregisterBuiltIns.scala b/src/main/scala/millfork/compiler/mos/PseudoregisterBuiltIns.scala index e707308b..ab7c8d5d 100644 --- a/src/main/scala/millfork/compiler/mos/PseudoregisterBuiltIns.scala +++ b/src/main/scala/millfork/compiler/mos/PseudoregisterBuiltIns.scala @@ -380,6 +380,8 @@ object PseudoregisterBuiltIns { load ++ calculate } + private def isPowerOfTwoUpTo15(n: Long): Boolean = if (n <= 0 || n >= 0x8000) false else 0 == ((n-1) & n) + def compileWordMultiplication(ctx: CompilationContext, param1OrRegister: Option[Expression], param2: Expression, storeInRegLo: Boolean): List[AssemblyLine] = { if (ctx.options.zpRegisterSize < 3) { ctx.log.error("Variable word multiplication requires the zeropage pseudoregister of size at least 3", param1OrRegister.flatMap(_.position)) @@ -391,6 +393,18 @@ object PseudoregisterBuiltIns { case (2 | 1, 1) => // ok case _ => ctx.log.fatal("Invalid code path", param2.position) } + if (!storeInRegLo && param1OrRegister.isDefined) { + (ctx.env.eval(param1OrRegister.get), ctx.env.eval(param2)) match { + case (Some(l), Some(r)) => + val product = CompoundConstant(MathOperator.Times, l, r).quickSimplify + return List(AssemblyLine.immediate(LDA, product.loByte), AssemblyLine.immediate(LDX, product.hiByte)) + case (Some(NumericConstant(c, _)), _) if isPowerOfTwoUpTo15(c)=> + return compileWordShiftOps(left = true, ctx, param2, LiteralExpression(java.lang.Long.bitCount(c - 1), 1)) + case (_, Some(NumericConstant(c, _))) if isPowerOfTwoUpTo15(c)=> + return compileWordShiftOps(left = true, ctx, param1OrRegister.get, LiteralExpression(java.lang.Long.bitCount(c - 1), 1)) + case _ => + } + } val b = ctx.env.get[Type]("byte") val w = ctx.env.get[Type]("word") val reg = ctx.env.get[VariableInMemory]("__reg") diff --git a/src/main/scala/millfork/env/Constant.scala b/src/main/scala/millfork/env/Constant.scala index 515b9675..11ff753d 100644 --- a/src/main/scala/millfork/env/Constant.scala +++ b/src/main/scala/millfork/env/Constant.scala @@ -98,6 +98,9 @@ sealed trait Constant { def refersTo(name: String): Boolean def fitsInto(typ: Type): Boolean = true // TODO + + final def succ: Constant = (this + 1).quickSimplify + } case class AssertByte(c: Constant) extends Constant {