mirror of
https://github.com/KarolS/millfork.git
synced 2024-12-22 16:31:02 +00:00
Various optimization improvements and fixes, mostly for 6809
This commit is contained in:
parent
21d4d3252f
commit
9028d55a7e
@ -168,6 +168,7 @@ case class MLine(opcode: MOpcode.Value, addrMode: MAddrMode, parameter: Constant
|
||||
|
||||
def changesRegister(reg: M6809Register.Value): Boolean = {
|
||||
import M6809Register._
|
||||
if (MOpcode.NotActualOpcodes(opcode)) return false
|
||||
def overlaps(other: M6809Register.Value): Boolean = {
|
||||
if (reg == D && (other == A || other == B)) true
|
||||
else if (other == D && (reg == A || reg == B)) true
|
||||
@ -202,12 +203,13 @@ case class MLine(opcode: MOpcode.Value, addrMode: MAddrMode, parameter: Constant
|
||||
}
|
||||
}
|
||||
|
||||
def changesCarryFlag: Boolean = !MOpcode.PreservesC(opcode)
|
||||
def changesCarryFlag: Boolean = !MOpcode.NotActualOpcodes(opcode) && !MOpcode.PreservesC(opcode)
|
||||
|
||||
|
||||
|
||||
def readsRegister(reg: M6809Register.Value): Boolean = {
|
||||
import M6809Register._
|
||||
if (MOpcode.NotActualOpcodes(opcode)) return false
|
||||
def overlaps(other: M6809Register.Value): Boolean = {
|
||||
if (reg == D && (other == A || other == B)) true
|
||||
else if (other == D && (reg == A || reg == B)) true
|
||||
@ -261,6 +263,7 @@ case class MLine(opcode: MOpcode.Value, addrMode: MAddrMode, parameter: Constant
|
||||
|
||||
def readsMemory(): Boolean = {
|
||||
import MOpcode._
|
||||
if (MOpcode.NotActualOpcodes(opcode)) return false
|
||||
val opcodeIsForReading = opcode match {
|
||||
case LDA | LDB | LDD | LDX | LDY | LDU | LDS | PULU | PULS => true
|
||||
case ADDA | SUBA | ADCA | SBCA | ORA | EORA | ANDA | CMPA | BITA => true
|
||||
@ -289,6 +292,7 @@ case class MLine(opcode: MOpcode.Value, addrMode: MAddrMode, parameter: Constant
|
||||
|
||||
def changesMemory(): Boolean = {
|
||||
import MOpcode._
|
||||
if (NotActualOpcodes(opcode)) return false
|
||||
val opcodeIsForWriting = opcode match {
|
||||
case LDA | LDB | LDD | LDX | LDY | LDU | LDS | PULU | PULS => false
|
||||
case ADDA | SUBA | ADCA | SBCA | ORA | EORA | ANDA | CMPA | BITA => false
|
||||
|
@ -32,9 +32,10 @@ object MOpcode extends Enumeration {
|
||||
TFR, TST,
|
||||
DISCARD_D, DISCARD_X, DISCARD_Y, DISCARD_CC, CHANGED_MEM, BYTE, LABEL = Value
|
||||
|
||||
val NotActualOpcodes: Set[MOpcode.Value] = Set(DISCARD_D, DISCARD_X, DISCARD_Y, DISCARD_CC, CHANGED_MEM, BYTE, LABEL)
|
||||
|
||||
private val toMap: Map[String, MOpcode.Value] = {
|
||||
val notActualOpcodes: Set[MOpcode.Value] = Set(DISCARD_D, DISCARD_X, DISCARD_Y, DISCARD_CC, CHANGED_MEM, BYTE, LABEL)
|
||||
values.filterNot(notActualOpcodes).map(o => o.toString -> o).toMap ++ Map("BHS" -> BCC, "BLO" -> BCS, "LSL" -> ASL)
|
||||
values.filterNot(NotActualOpcodes).map(o => o.toString -> o).toMap ++ Map("BHS" -> BCC, "BLO" -> BCS, "LSL" -> ASL)
|
||||
}
|
||||
val NoopDiscard: Set[MOpcode.Value] = Set(DISCARD_D, DISCARD_X, DISCARD_Y, DISCARD_CC)
|
||||
val PrefixedBy10: Set[MOpcode.Value] = Set(CMPD, CMPY, LDS, LDY, SWI2, STS, STY) // TODO: branches
|
||||
|
@ -3,7 +3,8 @@ package millfork.assembly.m6809.opt
|
||||
|
||||
import millfork.assembly.AssemblyOptimization
|
||||
import millfork.assembly.m6809.MOpcode._
|
||||
import millfork.assembly.m6809.{Absolute, DAccumulatorIndexed, Immediate, LongRelative, MAddrMode, MLine, MState, PostIncremented, RegisterSet}
|
||||
import millfork.assembly.m6809.{Absolute, DAccumulatorIndexed, Immediate, InherentA, InherentB, LongRelative, MAddrMode, MLine, MState, PostIncremented, RegisterSet}
|
||||
import millfork.env.{CompoundConstant, Constant, MathOperator}
|
||||
import millfork.node.M6809Register
|
||||
|
||||
/**
|
||||
@ -34,6 +35,21 @@ object AlwaysGoodMOptimizations {
|
||||
(Elidable & HasOpcode(CMPA) & HasImmediate(0) & DoesntMatterWhatItDoesWith(MState.VF, MState.CF, MState.HF)) ~~> {code => code.init},
|
||||
(HasOpcodeIn(LDB, ANDB, ORB, EORB, ADDB, ADCB, SUBB, SBCB)) ~
|
||||
(Elidable & HasOpcode(CMPB) & HasImmediate(0) & DoesntMatterWhatItDoesWith(MState.VF, MState.CF, MState.HF)) ~~> {code => code.init},
|
||||
|
||||
(Elidable & HasOpcode(EORA) & HasAddrMode(Immediate) & MatchParameter(0)) ~
|
||||
(Elidable & HasOpcode(CMPA) & HasAddrMode(Immediate) & MatchParameter(1)
|
||||
& DoesntMatterWhatItDoesWith(MState.A, MState.NF, MState.VF, MState.CF, MState.HF)) ~~> { (code, ctx) =>
|
||||
val c0 = ctx.get[Constant](0)
|
||||
val c1 = ctx.get[Constant](1)
|
||||
List(code.last.copy(parameter = CompoundConstant(MathOperator.Exor, c0, c1).quickSimplify))
|
||||
},
|
||||
(Elidable & HasOpcode(EORB) & HasAddrMode(Immediate) & MatchParameter(0)) ~
|
||||
(Elidable & HasOpcode(CMPB) & HasAddrMode(Immediate) & MatchParameter(1)
|
||||
& DoesntMatterWhatItDoesWith(MState.B, MState.NF, MState.VF, MState.CF, MState.HF)) ~~> { (code, ctx) =>
|
||||
val c0 = ctx.get[Constant](0)
|
||||
val c1 = ctx.get[Constant](1)
|
||||
List(code.last.copy(parameter = CompoundConstant(MathOperator.Exor, c0, c1).quickSimplify))
|
||||
},
|
||||
)
|
||||
|
||||
val SimplifiableZeroStore = new RuleBasedAssemblyOptimization("Simplifiable zero store",
|
||||
@ -58,6 +74,75 @@ object AlwaysGoodMOptimizations {
|
||||
},
|
||||
)
|
||||
|
||||
val SimplifiableComparison = new RuleBasedAssemblyOptimization("Simplifiable comparison",
|
||||
needsFlowInfo = FlowInfoRequirement.BothFlows,
|
||||
|
||||
(Elidable & HasOpcode(EORB) & HasAddrMode(Immediate) & MatchParameter(0)) ~
|
||||
(Elidable & HasOpcode(CMPB) & HasAddrMode(Immediate) & MatchParameter(1)
|
||||
& DoesntMatterWhatItDoesWith(MState.CF, MState.B, MState.VF, MState.HF)) ~~> { (code, ctx) =>
|
||||
val c0 = ctx.get[Constant](0)
|
||||
val c1 = ctx.get[Constant](1)
|
||||
List(code.last.copy(parameter = CompoundConstant(MathOperator.Exor, c0, c1).quickSimplify))
|
||||
},
|
||||
(Elidable & HasOpcode(ADDB) & HasImmediate(1)) ~
|
||||
(Elidable & HasOpcode(CMPB) & HasAddrMode(Immediate) & MatchParameter(1)
|
||||
& DoesntMatterWhatItDoesWith(MState.CF, MState.B, MState.VF, MState.HF)) ~~> { (code, ctx) =>
|
||||
val c1 = ctx.get[Constant](1)
|
||||
List(code.last.copy(parameter = (c1 - 1).quickSimplify))
|
||||
},
|
||||
(Elidable & HasOpcode(SUBB) & HasImmediate(1)) ~
|
||||
(Elidable & HasOpcode(CMPB) & HasAddrMode(Immediate) & MatchParameter(1)
|
||||
& DoesntMatterWhatItDoesWith(MState.CF, MState.B, MState.VF, MState.HF)) ~~> { (code, ctx) =>
|
||||
val c1 = ctx.get[Constant](1)
|
||||
List(code.last.copy(parameter = (c1 + 1).quickSimplify))
|
||||
},
|
||||
(Elidable & HasOpcode(INC) & HasAddrMode(InherentB)) ~
|
||||
(Elidable & HasOpcode(CMPB) & HasAddrMode(Immediate) & MatchParameter(1)
|
||||
& DoesntMatterWhatItDoesWith(MState.CF, MState.B, MState.VF, MState.HF)) ~~> { (code, ctx) =>
|
||||
val c1 = ctx.get[Constant](1)
|
||||
List(code.last.copy(parameter = (c1 - 1).quickSimplify))
|
||||
},
|
||||
(Elidable & HasOpcode(DEC) & HasAddrMode(InherentB)) ~
|
||||
(Elidable & HasOpcode(CMPB) & HasAddrMode(Immediate) & MatchParameter(1)
|
||||
& DoesntMatterWhatItDoesWith(MState.CF, MState.B, MState.VF, MState.HF)) ~~> { (code, ctx) =>
|
||||
val c1 = ctx.get[Constant](1)
|
||||
List(code.last.copy(parameter = (c1 + 1).quickSimplify))
|
||||
},
|
||||
|
||||
(Elidable & HasOpcode(EORA) & HasAddrMode(Immediate) & MatchParameter(0)) ~
|
||||
(Elidable & HasOpcode(CMPA) & HasAddrMode(Immediate) & MatchParameter(1)
|
||||
& DoesntMatterWhatItDoesWith(MState.CF, MState.A, MState.VF, MState.HF)) ~~> { (code, ctx) =>
|
||||
val c0 = ctx.get[Constant](0)
|
||||
val c1 = ctx.get[Constant](1)
|
||||
List(code.last.copy(parameter = CompoundConstant(MathOperator.Exor, c0, c1).quickSimplify))
|
||||
},
|
||||
(Elidable & HasOpcode(ADDA) & HasImmediate(1)) ~
|
||||
(Elidable & HasOpcode(CMPA) & HasAddrMode(Immediate) & MatchParameter(1)
|
||||
& DoesntMatterWhatItDoesWith(MState.CF, MState.A, MState.VF, MState.HF)) ~~> { (code, ctx) =>
|
||||
val c1 = ctx.get[Constant](1)
|
||||
List(code.last.copy(parameter = (c1 - 1).quickSimplify))
|
||||
},
|
||||
(Elidable & HasOpcode(SUBA) & HasImmediate(1)) ~
|
||||
(Elidable & HasOpcode(CMPA) & HasAddrMode(Immediate) & MatchParameter(1)
|
||||
& DoesntMatterWhatItDoesWith(MState.CF, MState.A, MState.VF, MState.HF)) ~~> { (code, ctx) =>
|
||||
val c1 = ctx.get[Constant](1)
|
||||
List(code.last.copy(parameter = (c1 + 1).quickSimplify))
|
||||
},
|
||||
(Elidable & HasOpcode(INC) & HasAddrMode(InherentA)) ~
|
||||
(Elidable & HasOpcode(CMPA) & HasAddrMode(Immediate) & MatchParameter(1)
|
||||
& DoesntMatterWhatItDoesWith(MState.CF, MState.A, MState.VF, MState.HF)) ~~> { (code, ctx) =>
|
||||
val c1 = ctx.get[Constant](1)
|
||||
List(code.last.copy(parameter = (c1 - 1).quickSimplify))
|
||||
},
|
||||
(Elidable & HasOpcode(DEC) & HasAddrMode(InherentA)) ~
|
||||
(Elidable & HasOpcode(CMPA) & HasAddrMode(Immediate) & MatchParameter(1)
|
||||
& DoesntMatterWhatItDoesWith(MState.CF, MState.A, MState.VF, MState.HF)) ~~> { (code, ctx) =>
|
||||
val c1 = ctx.get[Constant](1)
|
||||
List(code.last.copy(parameter = (c1 + 1).quickSimplify))
|
||||
},
|
||||
|
||||
)
|
||||
|
||||
val PointlessRegisterTransfers = new RuleBasedAssemblyOptimization("Pointless register transfers",
|
||||
needsFlowInfo = FlowInfoRequirement.BackwardFlow,
|
||||
(Elidable & IsTfr(M6809Register.D, M6809Register.X)) ~
|
||||
@ -161,15 +246,27 @@ object AlwaysGoodMOptimizations {
|
||||
(HasOpcode(LABEL) & MatchParameter(1)) ~~> { code =>
|
||||
List(code.head, code(3).copy(opcode = ORB)) ++ code.drop(4)
|
||||
},
|
||||
(Elidable & HasOpcode(ADDB) & HasImmediate(1) & DoesntMatterWhatItDoesWith(MState.VF, MState.CF, MState.HF)) ~~> { code => List(MLine.inherentB(INC)) },
|
||||
(Elidable & HasOpcode(ADDA) & HasImmediate(1) & DoesntMatterWhatItDoesWith(MState.VF, MState.CF, MState.HF)) ~~> { code => List(MLine.inherentA(INC)) },
|
||||
(Elidable & HasOpcode(SUBB) & HasImmediate(1) & DoesntMatterWhatItDoesWith(MState.VF, MState.CF, MState.HF)) ~~> { code => List(MLine.inherentB(DEC)) },
|
||||
(Elidable & HasOpcode(SUBA) & HasImmediate(1) & DoesntMatterWhatItDoesWith(MState.VF, MState.CF, MState.HF)) ~~> { code => List(MLine.inherentA(DEC)) },
|
||||
|
||||
)
|
||||
|
||||
val UnusedLabelRemoval = new RuleBasedAssemblyOptimization("Unused label removal",
|
||||
needsFlowInfo = FlowInfoRequirement.JustLabels,
|
||||
(Elidable & HasOpcode(LABEL) & HasCallerCount(0) & ParameterIsLocalLabel) ~~> (_ => Nil)
|
||||
)
|
||||
|
||||
|
||||
val All: Seq[AssemblyOptimization[MLine]] = Seq(
|
||||
PointlessLoad,
|
||||
PointlessCompare,
|
||||
PointlessRegisterTransfers,
|
||||
SimplifiableArithmetics,
|
||||
SimplifiableComparison,
|
||||
SimplifiableJumps,
|
||||
SimplifiableZeroStore
|
||||
SimplifiableZeroStore,
|
||||
UnusedLabelRemoval
|
||||
)
|
||||
}
|
||||
|
@ -110,8 +110,9 @@ object ReverseFlowAnalyzer {
|
||||
var changed = true
|
||||
changed = true
|
||||
val actualFinalImportance = f.returnType match {
|
||||
case FlagBooleanType(_, _, _) => finalImportance.copy(cf = Important, zf =Important, nf = Important, vf = Important)
|
||||
case FlagBooleanType(_, _, _) => finalImportance.copy(cf = Important, zf = Important, nf = Important, vf = Important)
|
||||
case t if t.size == 1 => finalImportance.copy(a = Unimportant)
|
||||
case t if t.size == 0 => finalImportance.copy(a = Unimportant, b = Unimportant)
|
||||
case _ => finalImportance
|
||||
}
|
||||
while (changed) {
|
||||
@ -133,12 +134,21 @@ object ReverseFlowAnalyzer {
|
||||
case _ => false
|
||||
}
|
||||
currentImportance = if (labelIndex < 0) actualFinalImportance else importanceArray(labelIndex) ~ currentImportance
|
||||
case MLine0(JMP | BRA, _, MemoryAddressConstant(Label(l))) =>
|
||||
val L = l
|
||||
val labelIndex = codeArray.indexWhere {
|
||||
case MLine0(LABEL, _, MemoryAddressConstant(Label(L))) => true
|
||||
case _ => false
|
||||
}
|
||||
currentImportance = if (labelIndex < 0) actualFinalImportance else importanceArray(labelIndex)
|
||||
case _ =>
|
||||
}
|
||||
currentLine match {
|
||||
|
||||
case MLine0(RTS, _, _) =>
|
||||
currentImportance = actualFinalImportance
|
||||
case MLine0(LABEL, _, _) =>
|
||||
// do nothing
|
||||
case MLine0(JSR | JMP, Absolute(false), MemoryAddressConstant(fun: FunctionInMemory)) =>
|
||||
// this case has to be handled first, because the generic JSR importance handler is too conservative
|
||||
var result = importanceBeforeJsr
|
||||
|
@ -1290,3 +1290,14 @@ case object IsNotALabelUsedManyTimes extends MLinePattern {
|
||||
|
||||
override def hitRate: Double = 0.92 // ?
|
||||
}
|
||||
|
||||
|
||||
object ParameterIsLocalLabel extends MLinePattern {
|
||||
override def matchLineTo(ctx: AssemblyMatchingContext, flowInfo: FlowInfo, line: MLine): Boolean =
|
||||
line match {
|
||||
case MLine0(MOpcode.LABEL, _, MemoryAddressConstant(Label(l))) => l.startsWith(".")
|
||||
case _ => false
|
||||
}
|
||||
|
||||
override def hitRate: Double = 0.056
|
||||
}
|
||||
|
@ -2991,7 +2991,9 @@ object AlwaysGoodOptimizations {
|
||||
|
||||
(Elidable & HasOpcode(EOR) & MatchImmediate(0)) ~
|
||||
(Elidable & HasOpcode(CMP) & MatchImmediate(1) & DoesntMatterWhatItDoesWith(State.A, State.N, State.C)) ~~> { (code, ctx) =>
|
||||
List(code.last.copy(parameter = CompoundConstant(MathOperator.Exor, ctx.get[Constant](0), ctx.get[Constant](1))))
|
||||
val c0 = ctx.get[Constant](0)
|
||||
val c1 = ctx.get[Constant](1)
|
||||
List(code.last.copy(parameter = CompoundConstant(MathOperator.Exor, c0, c1).quickSimplify))
|
||||
},
|
||||
|
||||
MultipleAssemblyRules(for {
|
||||
@ -3115,6 +3117,42 @@ object AlwaysGoodOptimizations {
|
||||
code(1).copy(opcode = branch, parameter = code(3).parameter),
|
||||
code(4))
|
||||
},
|
||||
|
||||
(Elidable & HasOpcode(INC) & HasAddrMode(Implied)) ~
|
||||
(Elidable & HasOpcode(CMP) & HasAddrMode(Immediate) & MatchParameter(1) & DoesntMatterWhatItDoesWith(State.N, State.A, State.C, State.V)) ~~> { (code, ctx) =>
|
||||
val c1 = ctx.get[Constant](1)
|
||||
List(code.last.copy(parameter = (c1 - 1).quickSimplify))
|
||||
},
|
||||
|
||||
(Elidable & HasOpcode(INX) & HasAddrMode(Implied)) ~
|
||||
(Elidable & HasOpcode(CPX) & HasAddrMode(Immediate) & MatchParameter(1) & DoesntMatterWhatItDoesWith(State.N, State.X, State.C, State.V)) ~~> { (code, ctx) =>
|
||||
val c1 = ctx.get[Constant](1)
|
||||
List(code.last.copy(parameter = (c1 - 1).quickSimplify))
|
||||
},
|
||||
|
||||
(Elidable & HasOpcode(INY) & HasAddrMode(Implied)) ~
|
||||
(Elidable & HasOpcode(CPY) & HasAddrMode(Immediate) & MatchParameter(1) & DoesntMatterWhatItDoesWith(State.N, State.Y, State.C, State.V)) ~~> { (code, ctx) =>
|
||||
val c1 = ctx.get[Constant](1)
|
||||
List(code.last.copy(parameter = (c1 - 1).quickSimplify))
|
||||
},
|
||||
|
||||
(Elidable & HasOpcode(DEC) & HasAddrMode(Implied)) ~
|
||||
(Elidable & HasOpcode(CMP) & HasAddrMode(Immediate) & MatchParameter(1) & DoesntMatterWhatItDoesWith(State.N, State.A, State.C, State.V)) ~~> { (code, ctx) =>
|
||||
val c1 = ctx.get[Constant](1)
|
||||
List(code.last.copy(parameter = (c1 + 1).quickSimplify))
|
||||
},
|
||||
|
||||
(Elidable & HasOpcode(DEX) & HasAddrMode(Implied)) ~
|
||||
(Elidable & HasOpcode(CPX) & HasAddrMode(Immediate) & MatchParameter(1) & DoesntMatterWhatItDoesWith(State.N, State.X, State.C, State.V)) ~~> { (code, ctx) =>
|
||||
val c1 = ctx.get[Constant](1)
|
||||
List(code.last.copy(parameter = (c1 + 1).quickSimplify))
|
||||
},
|
||||
|
||||
(Elidable & HasOpcode(DEY) & HasAddrMode(Implied)) ~
|
||||
(Elidable & HasOpcode(CPY) & HasAddrMode(Immediate) & MatchParameter(1) & DoesntMatterWhatItDoesWith(State.N, State.Y, State.C, State.V)) ~~> { (code, ctx) =>
|
||||
val c1 = ctx.get[Constant](1)
|
||||
List(code.last.copy(parameter = (c1 + 1).quickSimplify))
|
||||
},
|
||||
)
|
||||
|
||||
private val powersOf2: List[(Int, Int)] = List(
|
||||
|
@ -928,9 +928,16 @@ object AlwaysGoodI80Optimizations {
|
||||
code(2).copy(opcode=JP, registers = branch, parameter = code(4).parameter),
|
||||
code(5))
|
||||
},
|
||||
)
|
||||
),
|
||||
|
||||
|
||||
(Elidable & HasOpcode(XOR) & HasRegisterParam(ZRegister.IMM_8) & MatchParameter(0)) ~
|
||||
(Elidable & HasOpcode(CP) & HasRegisterParam(ZRegister.IMM_8) & MatchParameter(1)
|
||||
& DoesntMatterWhatItDoesWith(ZRegister.A) & DoesntMatterWhatItDoesWithFlagsOtherThanSZ) ~~> { (code, ctx) =>
|
||||
val c0 = ctx.get[Constant](0)
|
||||
val c1 = ctx.get[Constant](1)
|
||||
List(code.last.copy(parameter = CompoundConstant(MathOperator.Exor, c0, c1).quickSimplify))
|
||||
},
|
||||
)
|
||||
|
||||
val FreeHL = new RuleBasedAssemblyOptimization("Free HL",
|
||||
|
@ -986,4 +986,74 @@ class AssemblyOptimizationSuite extends FunSuite with Matchers {
|
||||
m.readByte(0xc000) should equal(code.count(_ == '↑'))
|
||||
}
|
||||
}
|
||||
|
||||
test("Optimize XOR comparisons") {
|
||||
val code =
|
||||
"""
|
||||
|byte output @$c000
|
||||
|const byte c = 4
|
||||
|
|
||||
|noinline void inc(byte x) {
|
||||
| if x ^ c == c { output += 1 }
|
||||
|}
|
||||
|
|
||||
|void main() {
|
||||
| output = 0
|
||||
| inc(0)
|
||||
| inc(1)
|
||||
| inc(2)
|
||||
|}
|
||||
|""".stripMargin
|
||||
EmuCrossPlatformBenchmarkRun(Cpu.Mos, Cpu.Z80, Cpu.Motorola6809)(
|
||||
code) { m =>
|
||||
m.readByte(0xc000) should equal(1)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
test("Optimize +1 comparisons") {
|
||||
val code =
|
||||
"""
|
||||
|byte output @$c000
|
||||
|const byte c = 4
|
||||
|
|
||||
|noinline void inc(byte x) {
|
||||
| if x + 1 == c { output += 1 }
|
||||
|}
|
||||
|
|
||||
|void main() {
|
||||
| output = 0
|
||||
| inc(3)
|
||||
| inc(4)
|
||||
| inc(4)
|
||||
| inc(5)
|
||||
| inc(5)
|
||||
|}
|
||||
|""".stripMargin
|
||||
EmuCrossPlatformBenchmarkRun(Cpu.Cmos, Cpu.Z80, Cpu.Motorola6809)(
|
||||
code) { m =>
|
||||
m.readByte(0xc000) should equal(1)
|
||||
}
|
||||
}
|
||||
|
||||
test("Don't optimize other XOR comparisons") {
|
||||
val code =
|
||||
"""
|
||||
|byte output @$c000
|
||||
|const byte c = 4
|
||||
|
|
||||
|noinline void inc(byte x) {
|
||||
| if x ^ c >= c { output += 1 }
|
||||
|}
|
||||
|
|
||||
|void main() {
|
||||
| output = 0
|
||||
| inc(4)
|
||||
|}
|
||||
|""".stripMargin
|
||||
EmuCrossPlatformBenchmarkRun(Cpu.Mos, Cpu.Z80, Cpu.Motorola6809)(
|
||||
code) { m =>
|
||||
m.readByte(0xc000) should equal(0)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2,12 +2,12 @@ package millfork.test
|
||||
|
||||
import millfork.Cpu
|
||||
import millfork.test.emu.{EmuCrossPlatformBenchmarkRun, EmuUnoptimizedCrossPlatformRun, EmuUnoptimizedRun, ShouldNotCompile}
|
||||
import org.scalatest.{FunSuite, Matchers}
|
||||
import org.scalatest.{AppendedClues, FunSuite, Matchers}
|
||||
|
||||
/**
|
||||
* @author Karol Stasiak
|
||||
*/
|
||||
class BooleanSuite extends FunSuite with Matchers {
|
||||
class BooleanSuite extends FunSuite with Matchers with AppendedClues {
|
||||
|
||||
test("Not") {
|
||||
EmuCrossPlatformBenchmarkRun(Cpu.Mos, Cpu.Z80, Cpu.Intel8080, Cpu.Sharp, Cpu.Intel8086, Cpu.Motorola6809)(
|
||||
@ -336,7 +336,7 @@ class BooleanSuite extends FunSuite with Matchers {
|
||||
|""".stripMargin) { m =>
|
||||
val MAX_SIZE = 4
|
||||
val bool = x < MAX_SIZE && y < MAX_SIZE && x + w < MAX_SIZE && y + h < MAX_SIZE
|
||||
m.readByte(0xc000) should equal(if (bool) 1 else 0)
|
||||
m.readByte(0xc000) should equal(if (bool) 1 else 0) withClue s"x=$x y=$y w=$w h=$h"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
package millfork.test
|
||||
|
||||
import millfork.Cpu
|
||||
import millfork.env.{BasicPlainType, DerivedPlainType, NumericConstant}
|
||||
import millfork.env.{BasicPlainType, CompoundConstant, DerivedPlainType, MathOperator, NumericConstant}
|
||||
import millfork.test.emu.{EmuBenchmarkRun, EmuOptimizedCmosRun, EmuUnoptimizedCrossPlatformRun, EmuUnoptimizedRun, ShouldNotCompile}
|
||||
import org.scalatest.{FunSuite, Matchers}
|
||||
|
||||
@ -34,6 +34,10 @@ class ConstantSuite extends FunSuite with Matchers {
|
||||
NumericConstant(-1, 8).isProvablyNegative(signed(8)) should be(true)
|
||||
}
|
||||
|
||||
test("Constants should simplify nicely") {
|
||||
CompoundConstant(MathOperator.Exor, NumericConstant(4, 1), NumericConstant(4, 1)).quickSimplify should equal(NumericConstant(0, 1))
|
||||
}
|
||||
|
||||
test ("Overflow errors should be nice") {
|
||||
ShouldNotCompile(
|
||||
"""
|
||||
|
Loading…
Reference in New Issue
Block a user