mirror of
https://github.com/KarolS/millfork.git
synced 2025-02-22 12:29:00 +00:00
Struct array fields performance improvements
This commit is contained in:
parent
83393d49f1
commit
a0c2eaabcf
@ -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,19 +424,45 @@ 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) =>
|
||||
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)
|
||||
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 =>
|
||||
val (targetType, arraySizeInBytes) = result match {
|
||||
case VariableExpression(maybePointy) =>
|
||||
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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,16 +581,38 @@ 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) =>
|
||||
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)
|
||||
}))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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(
|
||||
|
@ -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 => }
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user