1
0
mirror of https://github.com/KarolS/millfork.git synced 2024-06-09 16:29:34 +00:00

Struct array fields performance improvements

This commit is contained in:
Karol Stasiak 2021-02-26 23:13:16 +01:00
parent 83393d49f1
commit a0c2eaabcf
4 changed files with 132 additions and 45 deletions

View File

@ -411,7 +411,7 @@ abstract class AbstractStatementPreprocessor(protected val ctx: CompilationConte
val b = env.get[Type]("byte")
var ok = true
var result = optimizeExpr(root, currentVarValues).pos(pos)
def applyIndex(result: Expression, index: Expression, guaranteedSmall: Boolean): Expression = {
def applyIndex(result: Expression, index: Expression, smallArraySizeInBytes: Option[Int]): Expression = {
AbstractExpressionCompiler.getExpressionType(env, env.log, result) match {
case pt@PointerType(_, _, Some(targetType)) =>
val zero = env.eval(index) match {
@ -424,17 +424,43 @@ abstract class AbstractStatementPreprocessor(protected val ctx: CompilationConte
DerefExpression(result, 0, targetType)
} else {
val indexType = AbstractExpressionCompiler.getExpressionType(env, env.log, index)
val (newResult, constantOffset) = result match {
case FunctionCallExpression(pType, List(SumExpression(List((false, newResult), (false, LiteralExpression(offset, _))), false)))
if offset >= 0 && offset < 100 && env.maybeGet[VariableType](pType).exists(_.isPointy) =>
(pType <| newResult) -> offset.toInt
case _ => result -> 0
}
env.eval(index) match {
case Some(NumericConstant(n, _)) if n >= 0 && (guaranteedSmall || (targetType.alignedSize * n) <= 127) =>
DerefExpression(
("pointer." + targetType.name) <| result,
targetType.alignedSize * n.toInt, targetType)
case Some(NumericConstant(n, _)) if n >= 0 && (smallArraySizeInBytes.isDefined || (targetType.alignedSize * n) <= 127) =>
if (constantOffset + (targetType.alignedSize * n) <= 127) {
DerefExpression(
("pointer." + targetType.name) <| newResult,
constantOffset + targetType.alignedSize * n.toInt, targetType)
} else {
DerefExpression(
("pointer." + targetType.name) <| result,
targetType.alignedSize * n.toInt, targetType)
}
case _ =>
val small = guaranteedSmall || (indexType.size == 1 && !indexType.isSigned)
val scaledIndex: Expression = scaleIndexForArrayAccess(index, targetType, if (small) Some(256) else None)
DerefExpression(("pointer." + targetType.name) <| (
("pointer" <| result) #+# optimizeExpr(scaledIndex, Map())
), 0, targetType)
val small = smallArraySizeInBytes.isDefined || (indexType.size == 1 && !indexType.isSigned)
smallArraySizeInBytes match {
case Some(sz) if constantOffset != 0 && sz + constantOffset <= 255 =>
val scaledIndex: Expression = scaleIndexForArrayAccess(index, targetType, smallArraySizeInBytes)
var scaledIndexWithOffset = optimizeExpr(scaledIndex #+# LiteralExpression(constantOffset, 1), Map())
val cpuFamily = ctx.options.platform.cpuFamily
if ((cpuFamily == CpuFamily.M6502 || cpuFamily == CpuFamily.I80)
&& AbstractExpressionCompiler.getExpressionType(ctx, scaledIndexWithOffset).size == 1) {
scaledIndexWithOffset = "byte" <| scaledIndexWithOffset
}
DerefExpression(("pointer." + targetType.name) <| (
("pointer" <| newResult) #+# scaledIndexWithOffset
), 0, targetType)
case _ =>
val scaledIndex: Expression = scaleIndexForArrayAccess(index, targetType, smallArraySizeInBytes)
DerefExpression(("pointer." + targetType.name) <| (
("pointer" <| result) #+# optimizeExpr(scaledIndex, Map())
), 0, targetType)
}
}
}
case x if x.isPointy =>
@ -473,9 +499,9 @@ abstract class AbstractStatementPreprocessor(protected val ctx: CompilationConte
}
for (index <- firstIndices) {
result = applyIndex(result, index, guaranteedSmall = false)
result = applyIndex(result, index, smallArraySizeInBytes = None)
}
var guaranteedSmall = false
var currentArraySizeInBytes = Option.empty[Int]
for ((dot, fieldName, indices) <- fieldPath) {
if (dot && ok) {
val pointer = result match {
@ -525,7 +551,7 @@ abstract class AbstractStatementPreprocessor(protected val ctx: CompilationConte
val offsetExpression = LiteralExpression(fieldOffset, 2).pos(pos)
subvariable.arrayIndexTypeAndSize match {
case Some((indexType, arraySize)) =>
guaranteedSmall = arraySize * target.alignedSize <= 256
currentArraySizeInBytes = Some(arraySize * subvariable.typ.alignedSize).filter(_ <= 256)
pointerWrap match {
case 0 | 1 =>
if (fieldOffset == 0) {
@ -554,7 +580,7 @@ abstract class AbstractStatementPreprocessor(protected val ctx: CompilationConte
case _ => throw new IllegalStateException
}
case None =>
guaranteedSmall = false
currentArraySizeInBytes = None
pointerWrap match {
case 0 =>
DerefExpression(inner, fieldOffset, fieldType)
@ -601,8 +627,8 @@ abstract class AbstractStatementPreprocessor(protected val ctx: CompilationConte
}
if (ok) {
for (index <- indices) {
result = applyIndex(result, index, guaranteedSmall)
guaranteedSmall = false
result = applyIndex(result, index, currentArraySizeInBytes)
currentArraySizeInBytes = None
}
}
}

View File

@ -1,13 +1,12 @@
package millfork.compiler.m6809
import java.util.concurrent.AbstractExecutorService
import millfork.CompilationFlag
import millfork.assembly.m6809.{Absolute, DAccumulatorIndexed, Immediate, Indexed, InherentB, MLine, MLine0, MOpcode, RegisterSet, TwoRegisters}
import millfork.compiler.{AbstractExpressionCompiler, BranchIfFalse, BranchIfTrue, BranchSpec, ComparisonType, CompilationContext, NoBranching}
import millfork.node.{DerefExpression, Expression, FunctionCallExpression, GeneratedConstantExpression, IndexedExpression, LhsExpression, LiteralExpression, M6809Register, SeparateBytesExpression, SumExpression, VariableExpression}
import millfork.assembly.m6809.MOpcode._
import millfork.env.{AssemblyOrMacroParamSignature, BuiltInBooleanType, Constant, ConstantBooleanType, ConstantPointy, ExternFunction, FatBooleanType, FlagBooleanType, FunctionInMemory, FunctionPointerType, KernalInterruptPointerType, Label, M6809RegisterVariable, MacroFunction, MathOperator, MemoryAddressConstant, MemoryVariable, NonFatalCompilationException, NormalFunction, NormalParamSignature, NumericConstant, StackOffsetThing, StackVariable, StackVariablePointy, StructureConstant, Thing, ThingInMemory, Type, Variable, VariableInMemory, VariableLikeThing, VariablePointy}
import millfork.env.{AssemblyOrMacroParamSignature, BuiltInBooleanType, Constant, ConstantBooleanType, ConstantPointy, ExternFunction, FatBooleanType, FlagBooleanType, FunctionInMemory, FunctionPointerType, KernalInterruptPointerType, Label, M6809RegisterVariable, MacroFunction, MathOperator, MemoryAddressConstant, MemoryVariable, NonFatalCompilationException, NormalFunction, NormalParamSignature, NumericConstant, StackOffsetThing, StackVariable, StackVariablePointy, StructureConstant, Thing, ThingInMemory, Type, Variable, VariableInMemory, VariableLikeThing, VariablePointy, VariableType}
import scala.collection.GenTraversableOnce
@ -46,6 +45,21 @@ import MExpressionTarget.toLd
object M6809ExpressionCompiler extends AbstractExpressionCompiler[MLine] {
def extractConstantOffset(ctx: CompilationContext, expr: Expression): (Expression, Int) = {
expr match {
case FunctionCallExpression(pType, List(e))
if ctx.env.maybeGet[VariableType](pType).exists(_.isPointy) =>
var (i, o) = extractConstantOffset(ctx, e)
FunctionCallExpression(pType, List(i)) -> o
case SumExpression(List((false, i), (false, LiteralExpression(o, 1|2))), false) if o >= 0 && o < 127 =>
i -> o.toInt
case SumExpression(List((false, i1), (false, i2), (false, LiteralExpression(o, 1|2))), false) if o >= 0 && o < 127 =>
(i1 #+# i2) -> o.toInt
case _ =>
expr -> 0
}
}
def compile(ctx: CompilationContext, expr: Expression, target: MExpressionTarget.Value, branches: BranchSpec = BranchSpec.None): List[MLine] = try {
val env = ctx.env
val exprType = getExpressionType(ctx, expr)
@ -104,13 +118,16 @@ object M6809ExpressionCompiler extends AbstractExpressionCompiler[MLine] {
case _ => List(MLine.immediate(MExpressionTarget.toLd(target), NumericConstant(c, MExpressionTarget.size(target))))
}
case DerefExpression(inner, offset, _) =>
compileToX(ctx, inner) match {
val (i, o) = if (offset == 0) {
extractConstantOffset(ctx, inner)
} else (inner, offset)
compileToX(ctx, i) match {
case List(l@MLine0(LDX, Immediate, _)) =>
List(l.copy(opcode = toLd(target), addrMode = Absolute(false), parameter = l.parameter + offset))
case List(l@MLine0(LDX, addrMode, _)) if addrMode.isDeferenceable && offset == 0 =>
List(l.copy(opcode = toLd(target), addrMode = Absolute(false), parameter = l.parameter + o))
case List(l@MLine0(LDX, addrMode, _)) if addrMode.isDeferenceable && o == 0 =>
List(l.copy(opcode = toLd(target), addrMode = addrMode.dereference()))
case _ =>
compileToX(ctx, inner) :+ MLine(toLd(target), Indexed(M6809Register.X, indirect = false), NumericConstant(offset, 2))
case other =>
other :+ MLine(toLd(target), Indexed(M6809Register.X, indirect = false), NumericConstant(o, 2))
}
case IndexedExpression(name, index) =>
env.getPointy(name) match {

View File

@ -435,8 +435,8 @@ object MosExpressionCompiler extends AbstractExpressionCompiler[AssemblyLine] {
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 Some((fastBase, fastAddrMode, fastIndex)) =>
return preserveRegisterIfNeeded(ctx, MosRegister.A, fastIndex(offset)) ++ List(AssemblyLine(STA, fastAddrMode, fastBase))
case _ =>
}
}
@ -560,13 +560,13 @@ 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, Option[(Constant, Int => List[AssemblyLine])]) = {
def getPhysicalPointerForDeref(ctx: CompilationContext, pointerExpression: Expression): (List[AssemblyLine], Constant, AddrMode.Value, Option[(Constant, AddrMode.Value, Int => List[AssemblyLine])]) = {
pointerExpression match {
case VariableExpression(name) =>
val p = ctx.env.get[ThingInMemory](name)
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)
if (array.sizeInBytes <= 256) Some((p.toAddress, AbsoluteY, (i: Int) => List(AssemblyLine.immediate(LDY, i)))) else None)
case _ =>
}
if (p.zeropage) return (Nil, p.toAddress, AddrMode.IndexedY, None)
@ -581,17 +581,39 @@ object MosExpressionCompiler extends AbstractExpressionCompiler[AssemblyLine] {
Some(base -> index)
case _ => None
}
(compileToZReg(ctx, pointerExpression), ctx.env.get[ThingInMemory]("__reg.loword").toAddress, AddrMode.IndexedY, baseAndIndex match {
val zreg = ctx.env.get[ThingInMemory]("__reg.loword").toAddress
(compileToZReg(ctx, pointerExpression), zreg, 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")
compileToY(ctx, index #+# i)
}
ctx.env.eval(base) match {
case Some(baseConst) =>
Some((baseConst, AbsoluteY, { (i: Int) =>
val b = ctx.env.get[Type]("byte")
compileToY(ctx, index #+# i)
}))
case _ =>
if (compileToY(ctx, index #+# 42).exists(l => OpcodeClasses.ChangesMemoryAlways(l.opcode) || OpcodeClasses.ChangesMemoryIfNotImplied(l.opcode))) {
None
} else {
val initZreg = compileToZReg(ctx, base)
initZreg match {
case List(
AssemblyLine0(LDA, ZeroPage, l),
AssemblyLine0(STA, ZeroPage, _),
AssemblyLine0(LDA, ZeroPage, h),
AssemblyLine0(STA, ZeroPage, _)) if h == l+1 =>
Some((zreg, IndexedY, { (i: Int) =>
compileToZReg(ctx, base) ++ compileToY(ctx, index #+# i)
}))
case initZreg =>
Some((zreg, IndexedY, { (i: Int) =>
initZreg ++ compileToY(ctx, index #+# i)
}))
}
}
}
}
case _ => None
@ -1106,18 +1128,26 @@ object MosExpressionCompiler extends AbstractExpressionCompiler[AssemblyLine] {
case DerefExpression(inner, offset, targetType) =>
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 (Some((fastBase, fastAddrMode, fastIndex)), 1, _) =>
fastIndex(offset) ++ List(AssemblyLine(LDA, fastAddrMode, 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, _) =>
prepare ++ List(AssemblyLine.immediate(LDY, offset), AssemblyLine(LDA, am, addr)) ++ expressionStorageFromA(ctx, exprTypeAndVariable, expr.position, exprType.isSigned)
case (Some((fastBase, fastIndex)), 2, _) =>
case (Some((fastBase, AbsoluteY, fastIndex)), 2, _) =>
fastIndex(offset) ++ List(
AssemblyLine.absoluteY(LDA, fastBase),
AssemblyLine.implied(INY),
AssemblyLine.absoluteY(LDX, fastBase)) ++
expressionStorageFromAX(ctx, exprTypeAndVariable, expr.position)
case (Some((fastBase, fastAddrMode, fastIndex)), 2, _) =>
fastIndex(offset) ++ List(
AssemblyLine.implied(INY),
AssemblyLine(LDA, fastAddrMode, fastBase),
AssemblyLine.implied(TAX),
AssemblyLine.implied(DEY),
AssemblyLine(LDA, fastAddrMode, fastBase)) ++
expressionStorageFromAX(ctx, exprTypeAndVariable, expr.position)
case (_, 2, AbsoluteY) =>
prepare ++
List(
@ -2189,12 +2219,12 @@ object MosExpressionCompiler extends AbstractExpressionCompiler[AssemblyLine] {
AssemblyLine.absolute(STA, addr + offset + i)))
case _ =>
fastTarget match {
case Some((constAddr, initializeY)) =>
case Some((constAddr, fastAddrMode, initializeY)) =>
initializeY(offset) ++ (0 until targetType.size).flatMap { i =>
val load = List(AssemblyLine.immediate(LDA, constant.subbyte(i)))
load ++ (if (i == 0) List(AssemblyLine.absoluteY(STA, constAddr)) else List(
load ++ (if (i == 0) List(AssemblyLine(STA, fastAddrMode, constAddr)) else List(
AssemblyLine.implied(INY),
AssemblyLine.absoluteY(STA, constAddr)))
AssemblyLine(STA, fastAddrMode, constAddr)))
}
case _ =>
prepare ++ (0 until targetType.size).flatMap(i => List(
@ -2225,12 +2255,12 @@ object MosExpressionCompiler extends AbstractExpressionCompiler[AssemblyLine] {
}
case (_, _) =>
fastTarget match {
case Some((constAddr, initializeY)) =>
case Some((constAddr, fastAddrMode, initializeY)) =>
initializeY(offset) ++ (0 until targetType.size).flatMap { i =>
val load = if (i >= sourceType.size) List(AssemblyLine.immediate(LDA, 0)) else AssemblyLine.variable(ctx, LDA, variable, i)
load ++ (if (i == 0) List(AssemblyLine.absoluteY(STA, constAddr)) else List(
load ++ (if (i == 0) List(AssemblyLine(STA, fastAddrMode, constAddr)) else List(
AssemblyLine.implied(INY),
AssemblyLine.absoluteY(STA, constAddr)))
AssemblyLine(STA, fastAddrMode, constAddr)))
}
case _ =>
prepare ++ (0 until targetType.size).flatMap { i =>
@ -2375,14 +2405,14 @@ object MosExpressionCompiler extends AbstractExpressionCompiler[AssemblyLine] {
case (2, _) =>
val someTuple = Some(targetType, RegisterVariable(MosRegister.AX, targetType))
fastTarget match {
case Some((baseOffset, initializeY)) =>
case Some((baseOffset, fastAddrMode, initializeY)) =>
compile(ctx, source, someTuple, BranchSpec.None) ++
preserveRegisterIfNeeded(ctx, MosRegister.AX, initializeY(offset)) ++
List(
AssemblyLine.absoluteY(STA, baseOffset),
AssemblyLine(STA, fastAddrMode, baseOffset),
AssemblyLine.implied(TXA),
AssemblyLine.implied(INY),
AssemblyLine.absoluteY(STA, baseOffset))
AssemblyLine(STA, fastAddrMode, baseOffset))
case _ =>
if (prepare.isEmpty) {
compile(ctx, source, someTuple, BranchSpec.None) ++ List(

View File

@ -346,4 +346,18 @@ class StructSuite extends FunSuite with Matchers {
| }
|""".stripMargin)
}
test("Structs with array fields performance") {
EmuCrossPlatformBenchmarkRun(Cpu.Mos, Cpu.Z80, Cpu.Intel8086, Cpu.Motorola6809)(
"""
| struct S { byte header, array data[20] }
| S dummy @$c000
| noinline byte f(pointer.S s, byte i) {
| return s[0].data[i]
| }
| void main() {
| f(dummy.pointer, 5)
| }
|""".stripMargin){m => }
}
}