millfork/src/test/scala/millfork/test/AssemblyOptimizationSuite.s...

282 lines
7.4 KiB
Scala

package millfork.test
import millfork.{Cpu, OptimizationPresets}
import millfork.assembly.opt.{AlwaysGoodOptimizations, LaterOptimizations, VariableToRegisterOptimization}
import millfork.test.emu.{EmuBenchmarkRun, EmuUltraBenchmarkRun, EmuRun}
import org.scalatest.{FunSuite, Matchers}
/**
* @author Karol Stasiak
*/
class AssemblyOptimizationSuite extends FunSuite with Matchers {
test("Duplicate RTS") {
EmuBenchmarkRun(
"""
| void main () {
| if 1 == 1 {
| return
| }
| }
""".stripMargin) { _ => }
}
test("Inlining variable") {
EmuBenchmarkRun(
"""
| array output [5] @$C000
| void main () {
| byte i
| i = 0
| while (i<5) {
| output[i] = i
| i += 1
| }
| }
""".stripMargin)(_.readByte(0xc003) should equal(3))
}
test("Loading modified variables") {
EmuBenchmarkRun(
"""
| byte output @$C000
| void main () {
| byte x
| output = 5
| output += 1
| output += 1
| output += 1
| x = output
| output = x
| }
""".stripMargin)(_.readByte(0xc000) should equal(8))
}
test("Bit ops") {
EmuBenchmarkRun(
"""
| byte output @$C000
| void main () {
| output ^= output
| output |= 5 | 6
| output |= 5 | 6
| output &= 5 & 6
| output ^= 8 ^ 16
| }
""".stripMargin)(_.readByte(0xc000) should equal(28))
}
test("Inlining after a while") {
EmuBenchmarkRun(
"""
| array output [2]@$C000
| void main () {
| byte i
| output[0] = 6
| lol()
| i = 1
| if (i > 0) {
| output[i] = 4
| }
| }
| void lol() {}
""".stripMargin)(_.readWord(0xc000) should equal(0x406))
}
test("Tail call") {
EmuBenchmarkRun(
"""
| byte output @$C000
| void main () {
| if (output != 55) {
| output += 1
| main()
| }
| }
""".stripMargin)(_.readByte(0xc000) should equal(55))
}
test("LDA-TAY elimination") {
new EmuRun(Cpu.StrictMos, OptimizationPresets.NodeOpt, List(VariableToRegisterOptimization, AlwaysGoodOptimizations.YYY), false)(
"""
| array mouse_pointer[64]
| array arrow[64]
| byte output @$C000
| void main () {
| byte i
| i = 0
| while i < 63 {
| mouse_pointer[i] = arrow[i]
| i += 1
| }
| }
""".stripMargin)
}
test("Carry flag after AND-LSR") {
EmuUltraBenchmarkRun(
"""
| byte output @$C000
| void main () {
| output = f(5)
| }
| byte f(byte x) {
| return ((x & $1E) >> 1) + 3
| }
|
""".stripMargin)(_.readByte(0xc000) should equal(5))
}
test("Index sequence") {
EmuUltraBenchmarkRun(
"""
| array output[6] @$C000
| void main () {
| pointer o
| o = output.addr
| o[3] = 8
| o[4] = 8
| o[5] = 8
| }
|
""".stripMargin){m =>
m.readByte(0xc005) should equal(8)
}
}
test("Index switching") {
EmuUltraBenchmarkRun(
"""
| array output1[6] @$C000
| array output2[6] @$C010
| array input[6] @$C010
| void main () {
| static byte a
| static byte b
| input[5] = 3
| a = five()
| b = five()
| output1[a] = input[b]
| output2[a] = input[b]
| }
| byte five() {
| return 5
| }
|
""".stripMargin){m =>
m.readByte(0xc005) should equal(3)
m.readByte(0xc015) should equal(3)
}
}
test("TAX-BCC-RTS-TXA optimization") {
new EmuRun(Cpu.StrictMos,
OptimizationPresets.NodeOpt, List(
AlwaysGoodOptimizations.PointlessStoreAfterLoad,
LaterOptimizations.PointlessLoadAfterStore,
VariableToRegisterOptimization,
LaterOptimizations.DoubleLoadToDifferentRegisters,
LaterOptimizations.DoubleLoadToTheSameRegister,
AlwaysGoodOptimizations.PointlessRegisterTransfers,
AlwaysGoodOptimizations.PointlessRegisterTransfersBeforeReturn,
AlwaysGoodOptimizations.PointlessLoadBeforeReturn,
AlwaysGoodOptimizations.PoinlessLoadBeforeAnotherLoad,
AlwaysGoodOptimizations.PointlessRegisterTransfers,
AlwaysGoodOptimizations.PointlessRegisterTransfersBeforeReturn,
AlwaysGoodOptimizations.PointlessRegisterTransfersBeforeReturn,
AlwaysGoodOptimizations.PointlessLoadBeforeReturn,
AlwaysGoodOptimizations.PoinlessLoadBeforeAnotherLoad,
AlwaysGoodOptimizations.PointlessStashingToIndexOverShortSafeBranch,
AlwaysGoodOptimizations.PointlessRegisterTransfersBeforeReturn,
AlwaysGoodOptimizations.IdempotentDuplicateRemoval,
AlwaysGoodOptimizations.IdempotentDuplicateRemoval,
AlwaysGoodOptimizations.IdempotentDuplicateRemoval), false)(
"""
| byte output @$C000
| void main(){ delta() }
| byte delta () {
| output = 0
| byte mouse_delta
| mouse_delta = 6
| mouse_delta &= $3f
| if mouse_delta >= $20 {
| mouse_delta |= $c0
| return 3
| }
| return mouse_delta
| }
""".stripMargin).readByte(0xc000) should equal(0)
}
test("Memory access detection"){
EmuUltraBenchmarkRun(
"""
| array h [4] @$C000
| array l [4] @$C404
| word output @$C00C
| word a @$C200
| void main () {
| byte i
| a = 0x102
| barrier()
| for i,0,until,4 {
| h[i]:l[i] = a
| }
| a.lo:a.hi=a
| output = a
| }
| void barrier (){}
|
""".stripMargin){m =>
m.readByte(0xc000) should equal(1)
m.readByte(0xc001) should equal(1)
m.readByte(0xc002) should equal(1)
m.readByte(0xc003) should equal(1)
m.readByte(0xc404) should equal(2)
m.readByte(0xc405) should equal(2)
m.readByte(0xc406) should equal(2)
m.readByte(0xc407) should equal(2)
m.readWord(0xc00c) should equal(0x201)
}
}
test("Memory access detection 2"){
EmuUltraBenchmarkRun(
"""
| array h [4]
| array l [4]
| word output @$C00C
| word ptrh @$C000
| word ptrl @$C002
| void main () {
| ptrh = h.addr
| ptrl = l.addr
| byte i
| word a
| a = 0x102
| barrier()
| for i,0,until,4 {
| h[i]:l[i] = a
| }
| a.lo:a.hi=a
| output = a
| couput
| }
| void barrier (){}
|
""".stripMargin){m =>
val ptrh = 0xffff & m.readWord(0xC000)
val ptrl = 0xffff & m.readWord(0xC002)
m.readByte(ptrh + 0) should equal(1)
m.readByte(ptrh + 1) should equal(1)
m.readByte(ptrh + 2) should equal(1)
m.readByte(ptrh + 3) should equal(1)
m.readByte(ptrl + 0) should equal(2)
m.readByte(ptrl + 1) should equal(2)
m.readByte(ptrl + 2) should equal(2)
m.readByte(ptrl + 3) should equal(2)
m.readWord(0xc00c) should equal(0x201)
}
}
}