mirror of
https://github.com/KarolS/millfork.git
synced 2025-01-11 12:29:46 +00:00
6502: Fix and optimize sign extension
This commit is contained in:
parent
d08f7ee77c
commit
7ce088514f
@ -179,6 +179,7 @@ object OptimizationPresets {
|
|||||||
AlwaysGoodOptimizations.SimplifiableStackOperation,
|
AlwaysGoodOptimizations.SimplifiableStackOperation,
|
||||||
LaterOptimizations.UseBit,
|
LaterOptimizations.UseBit,
|
||||||
LaterOptimizations.ReplaceableLoad,
|
LaterOptimizations.ReplaceableLoad,
|
||||||
|
LaterOptimizations.BranchlessSignExtension,
|
||||||
)
|
)
|
||||||
|
|
||||||
val Good: List[AssemblyOptimization[AssemblyLine]] = List[AssemblyOptimization[AssemblyLine]](
|
val Good: List[AssemblyOptimization[AssemblyLine]] = List[AssemblyOptimization[AssemblyLine]](
|
||||||
@ -261,6 +262,7 @@ object OptimizationPresets {
|
|||||||
UseAccumulatorInsteadOfYRegister,
|
UseAccumulatorInsteadOfYRegister,
|
||||||
VariableToRegisterOptimization,
|
VariableToRegisterOptimization,
|
||||||
TwoVariablesToIndexRegistersOptimization,
|
TwoVariablesToIndexRegistersOptimization,
|
||||||
|
LaterOptimizations.BranchlessSignExtension,
|
||||||
)
|
)
|
||||||
|
|
||||||
val QuickPreset: List[AssemblyOptimization[AssemblyLine]] = List[AssemblyOptimization[AssemblyLine]](
|
val QuickPreset: List[AssemblyOptimization[AssemblyLine]] = List[AssemblyOptimization[AssemblyLine]](
|
||||||
|
@ -559,7 +559,34 @@ object LaterOptimizations {
|
|||||||
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
val BranchlessSignExtension = new RuleBasedAssemblyOptimization("Branchless sign extension",
|
||||||
|
needsFlowInfo = FlowInfoRequirement.BothFlows,
|
||||||
|
(NotFixed & HasOpcode(LDA) & HasAddrModeIn(Absolute, ZeroPage, AbsoluteX, IndexedX, IndexedY, AbsoluteY, ZeroPageX) & HasClear(State.D)) ~
|
||||||
|
(Elidable & HasOpcode(ORA) & HasImmediate(0x7F)) ~
|
||||||
|
(Elidable & HasOpcode(BMI) & MatchParameter(10)) ~
|
||||||
|
(Elidable & HasOpcode(LDA) & HasImmediate(0)) ~
|
||||||
|
(Elidable & HasOpcode(LABEL) & IsNotALabelUsedManyTimes & MatchParameter(10) & DoesntMatterWhatItDoesWith(State.V)) ~~> { code =>
|
||||||
|
List(AssemblyLine.immediate(LDA, 0x7F), code.head.copy(opcode = CMP), AssemblyLine.immediate(SBC, 0x7F))
|
||||||
|
},
|
||||||
|
|
||||||
|
(NotFixed & HasOpcode(LDA) & HasAddrModeIn(Absolute, ZeroPage, AbsoluteX, IndexedX, IndexedY, AbsoluteY, ZeroPageX) & HasClear(State.D)) ~
|
||||||
|
(Elidable & HasOpcode(AND) & HasImmediate(0x80)) ~
|
||||||
|
(Elidable & HasOpcode(BPL) & MatchParameter(10)) ~
|
||||||
|
(Elidable & HasOpcode(LDA) & HasImmediate(0xff)) ~
|
||||||
|
(Elidable & HasOpcode(LABEL) & IsNotALabelUsedManyTimes & MatchParameter(10) & DoesntMatterWhatItDoesWith(State.V)) ~~> { code =>
|
||||||
|
List(AssemblyLine.immediate(LDA, 0x7F), code.head.copy(opcode = CMP), AssemblyLine.immediate(SBC, 0x7F))
|
||||||
|
},
|
||||||
|
|
||||||
|
)
|
||||||
|
|
||||||
|
// AssemblyLine.immediate(ORA, 0x7F),
|
||||||
|
// AssemblyLine.relative(BMI, label),
|
||||||
|
// AssemblyLine.immediate(LDA, 0),
|
||||||
|
// AssemblyLine.label(label))
|
||||||
|
|
||||||
|
// use the lists in OptimizationPresets to actually add these to the normal pipeline
|
||||||
val All = List(
|
val All = List(
|
||||||
|
BranchlessSignExtension,
|
||||||
CommutativeInPlaceModifications,
|
CommutativeInPlaceModifications,
|
||||||
DontUseIndexRegisters,
|
DontUseIndexRegisters,
|
||||||
DoubleLoadToDifferentRegisters,
|
DoubleLoadToDifferentRegisters,
|
||||||
|
@ -61,9 +61,9 @@ object MosStatementCompiler extends AbstractStatementCompiler[AssemblyLine] {
|
|||||||
AssemblyLine.zeropage(STA, reg,i).copy(elidability = Elidability.Volatile))
|
AssemblyLine.zeropage(STA, reg,i).copy(elidability = Elidability.Volatile))
|
||||||
}.toList
|
}.toList
|
||||||
} else Nil)
|
} else Nil)
|
||||||
val someRegisterA = Some(b, RegisterVariable(MosRegister.A, b))
|
// val someRegisterA = Some(b, RegisterVariable(MosRegister.A, b))
|
||||||
val someRegisterAX = Some(w, RegisterVariable(MosRegister.AX, w))
|
// val someRegisterAX = Some(w, RegisterVariable(MosRegister.AX, w))
|
||||||
val someRegisterYA = Some(w, RegisterVariable(MosRegister.YA, w))
|
// val someRegisterYA = Some(w, RegisterVariable(MosRegister.YA, w))
|
||||||
lazy val returnInstructions = if (m.interrupt) {
|
lazy val returnInstructions = if (m.interrupt) {
|
||||||
if (ctx.options.flag(CompilationFlag.EmitNative65816Opcodes)) {
|
if (ctx.options.flag(CompilationFlag.EmitNative65816Opcodes)) {
|
||||||
if (zpRegisterSize > 0) {
|
if (zpRegisterSize > 0) {
|
||||||
@ -241,6 +241,7 @@ object MosStatementCompiler extends AbstractStatementCompiler[AssemblyLine] {
|
|||||||
case s : ReturnDispatchStatement =>
|
case s : ReturnDispatchStatement =>
|
||||||
MosReturnDispatch.compile(ctx, s) -> Nil
|
MosReturnDispatch.compile(ctx, s) -> Nil
|
||||||
case ReturnStatement(Some(e)) =>
|
case ReturnStatement(Some(e)) =>
|
||||||
|
val exprType = AbstractExpressionCompiler.getExpressionType(ctx, e)
|
||||||
(m.returnType match {
|
(m.returnType match {
|
||||||
case _: BooleanType =>
|
case _: BooleanType =>
|
||||||
m.returnType.size match {
|
m.returnType.size match {
|
||||||
@ -248,9 +249,9 @@ object MosStatementCompiler extends AbstractStatementCompiler[AssemblyLine] {
|
|||||||
ctx.log.error("Cannot return anything from a void function", statement.position)
|
ctx.log.error("Cannot return anything from a void function", statement.position)
|
||||||
stackPointerFixBeforeReturn(ctx) ++ returnInstructions
|
stackPointerFixBeforeReturn(ctx) ++ returnInstructions
|
||||||
case 1 =>
|
case 1 =>
|
||||||
MosExpressionCompiler.compile(ctx, e, someRegisterA, NoBranching) ++ stackPointerFixBeforeReturn(ctx, preserveA = true) ++ returnInstructions
|
MosExpressionCompiler.compile(ctx, e, Some(exprType, RegisterVariable(MosRegister.A, b)), NoBranching) ++ stackPointerFixBeforeReturn(ctx, preserveA = true) ++ returnInstructions
|
||||||
case 2 =>
|
case 2 =>
|
||||||
MosExpressionCompiler.compile(ctx, e, someRegisterAX, NoBranching) ++ stackPointerFixBeforeReturn(ctx, preserveA = true, preserveX = true) ++ returnInstructions
|
MosExpressionCompiler.compile(ctx, e, Some(exprType, RegisterVariable(MosRegister.AX, w)), NoBranching) ++ stackPointerFixBeforeReturn(ctx, preserveA = true, preserveX = true) ++ returnInstructions
|
||||||
case _ =>
|
case _ =>
|
||||||
// TODO: is this case ever used?
|
// TODO: is this case ever used?
|
||||||
MosExpressionCompiler.compileAssignment(ctx, e, VariableExpression(ctx.function.name + "`return")) ++
|
MosExpressionCompiler.compileAssignment(ctx, e, VariableExpression(ctx.function.name + "`return")) ++
|
||||||
@ -267,14 +268,14 @@ object MosStatementCompiler extends AbstractStatementCompiler[AssemblyLine] {
|
|||||||
ctx.log.error("Cannot return anything from a void function", statement.position)
|
ctx.log.error("Cannot return anything from a void function", statement.position)
|
||||||
stackPointerFixBeforeReturn(ctx) ++ List(AssemblyLine.discardAF(), AssemblyLine.discardXF(), AssemblyLine.discardYF()) ++ returnInstructions
|
stackPointerFixBeforeReturn(ctx) ++ List(AssemblyLine.discardAF(), AssemblyLine.discardXF(), AssemblyLine.discardYF()) ++ returnInstructions
|
||||||
case 1 =>
|
case 1 =>
|
||||||
MosExpressionCompiler.compile(ctx, e, someRegisterA, NoBranching) ++ stackPointerFixBeforeReturn(ctx, preserveA = true) ++ List(AssemblyLine.discardXF(), AssemblyLine.discardYF()) ++ returnInstructions
|
MosExpressionCompiler.compile(ctx, e, Some(exprType, RegisterVariable(MosRegister.A, w)), NoBranching) ++ stackPointerFixBeforeReturn(ctx, preserveA = true) ++ List(AssemblyLine.discardXF(), AssemblyLine.discardYF()) ++ returnInstructions
|
||||||
case 2 =>
|
case 2 =>
|
||||||
// TODO: ???
|
// TODO: ???
|
||||||
val stackPointerFix = stackPointerFixBeforeReturn(ctx, preserveA = true, preserveY = true)
|
val stackPointerFix = stackPointerFixBeforeReturn(ctx, preserveA = true, preserveY = true)
|
||||||
if (stackPointerFix.isEmpty) {
|
if (stackPointerFix.isEmpty) {
|
||||||
MosExpressionCompiler.compile(ctx, e, someRegisterAX, NoBranching) ++ List(AssemblyLine.discardYF()) ++ returnInstructions
|
MosExpressionCompiler.compile(ctx, e, Some(exprType, RegisterVariable(MosRegister.AX, w)), NoBranching) ++ List(AssemblyLine.discardYF()) ++ returnInstructions
|
||||||
} else {
|
} else {
|
||||||
MosExpressionCompiler.compile(ctx, e, someRegisterYA, NoBranching) ++
|
MosExpressionCompiler.compile(ctx, e, Some(exprType, RegisterVariable(MosRegister.YA, w)), NoBranching) ++
|
||||||
stackPointerFix ++
|
stackPointerFix ++
|
||||||
List(AssemblyLine.implied(TAX), AssemblyLine.implied(TYA), AssemblyLine.discardYF()) ++
|
List(AssemblyLine.implied(TAX), AssemblyLine.implied(TYA), AssemblyLine.discardYF()) ++
|
||||||
returnInstructions
|
returnInstructions
|
||||||
|
@ -14,11 +14,11 @@ class SignExtensionSuite extends FunSuite with Matchers {
|
|||||||
| word output @$c000
|
| word output @$c000
|
||||||
| void main () {
|
| void main () {
|
||||||
| sbyte b
|
| sbyte b
|
||||||
| b = -1
|
| b = -2
|
||||||
| output = b
|
| output = b
|
||||||
| }
|
| }
|
||||||
""".stripMargin){m =>
|
""".stripMargin){m =>
|
||||||
m.readWord(0xc000) should equal(0xffff)
|
m.readWord(0xc000) should equal(0xfffe)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
test("Sbyte to Word 2") {
|
test("Sbyte to Word 2") {
|
||||||
@ -28,9 +28,9 @@ class SignExtensionSuite extends FunSuite with Matchers {
|
|||||||
| output = b()
|
| output = b()
|
||||||
| }
|
| }
|
||||||
| sbyte b() {
|
| sbyte b() {
|
||||||
| return -1
|
| return -2
|
||||||
| }
|
| }
|
||||||
""".stripMargin){m => m.readWord(0xc000) should equal(0xffff)}
|
""".stripMargin){m => m.readWord(0xc000) should equal(0xfffe)}
|
||||||
}
|
}
|
||||||
test("Sbyte to Long") {
|
test("Sbyte to Long") {
|
||||||
EmuCrossPlatformBenchmarkRun(Cpu.Mos, Cpu.Z80, Cpu.Intel8080, Cpu.Sharp, Cpu.Intel8086)("""
|
EmuCrossPlatformBenchmarkRun(Cpu.Mos, Cpu.Z80, Cpu.Intel8080, Cpu.Sharp, Cpu.Intel8086)("""
|
||||||
@ -75,12 +75,12 @@ class SignExtensionSuite extends FunSuite with Matchers {
|
|||||||
| word output @$c000
|
| word output @$c000
|
||||||
| void main () {
|
| void main () {
|
||||||
| sbyte b
|
| sbyte b
|
||||||
| b = -1
|
| b = -2
|
||||||
| memory_barrier()
|
| memory_barrier()
|
||||||
| output = byte(b)
|
| output = byte(b)
|
||||||
| }
|
| }
|
||||||
""".stripMargin){m =>
|
""".stripMargin){m =>
|
||||||
m.readWord(0xc000) should equal(0x00ff)
|
m.readWord(0xc000) should equal(0x00fe)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -89,12 +89,12 @@ class SignExtensionSuite extends FunSuite with Matchers {
|
|||||||
| word output @$c000
|
| word output @$c000
|
||||||
| void main () {
|
| void main () {
|
||||||
| sbyte b
|
| sbyte b
|
||||||
| b = -1
|
| b = -2
|
||||||
| memory_barrier()
|
| memory_barrier()
|
||||||
| output = word(byte(b))
|
| output = word(byte(b))
|
||||||
| }
|
| }
|
||||||
""".stripMargin){m =>
|
""".stripMargin){m =>
|
||||||
m.readWord(0xc000) should equal(0x00ff)
|
m.readWord(0xc000) should equal(0x00fe)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -102,11 +102,11 @@ class SignExtensionSuite extends FunSuite with Matchers {
|
|||||||
EmuUnoptimizedCrossPlatformRun(Cpu.Mos, Cpu.Z80, Cpu.Motorola6809)("""
|
EmuUnoptimizedCrossPlatformRun(Cpu.Mos, Cpu.Z80, Cpu.Motorola6809)("""
|
||||||
| word output @$c000
|
| word output @$c000
|
||||||
| void main () {
|
| void main () {
|
||||||
| output = f($ff)
|
| output = f($f4)
|
||||||
| }
|
| }
|
||||||
| noinline word f(byte x) = sbyte(x)
|
| noinline word f(byte x) = sbyte(x)
|
||||||
""".stripMargin){m =>
|
""".stripMargin){m =>
|
||||||
m.readWord(0xc000) should equal(0xffff)
|
m.readWord(0xc000) should equal(0xfff4)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -130,11 +130,11 @@ class SignExtensionSuite extends FunSuite with Matchers {
|
|||||||
|word output @$c000
|
|word output @$c000
|
||||||
|word output2 @$c002
|
|word output2 @$c002
|
||||||
|void main() {
|
|void main() {
|
||||||
| output = -1
|
| output = -3
|
||||||
| output2 = -30000
|
| output2 = -30000
|
||||||
|}
|
|}
|
||||||
|""".stripMargin) { m =>
|
|""".stripMargin) { m =>
|
||||||
m.readWord(0xc000) should equal(0xffff)
|
m.readWord(0xc000) should equal(0xfffd)
|
||||||
m.readWord(0xc002).toShort.toInt should equal(-30000)
|
m.readWord(0xc002).toShort.toInt should equal(-30000)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -146,12 +146,12 @@ class SignExtensionSuite extends FunSuite with Matchers {
|
|||||||
|
|
|
|
||||||
|void main() {
|
|void main() {
|
||||||
| static volatile signed16 tmp
|
| static volatile signed16 tmp
|
||||||
| tmp = -1
|
| tmp = -3
|
||||||
| memory_barrier()
|
| memory_barrier()
|
||||||
| output = tmp
|
| output = tmp
|
||||||
|}
|
|}
|
||||||
|""".stripMargin){ m =>
|
|""".stripMargin){ m =>
|
||||||
m.readLong(0xc000) should equal(-1)
|
m.readLong(0xc000) should equal(-3)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -162,13 +162,33 @@ class SignExtensionSuite extends FunSuite with Matchers {
|
|||||||
|
|
|
|
||||||
|void main() {
|
|void main() {
|
||||||
| static volatile signed16 tmp
|
| static volatile signed16 tmp
|
||||||
| tmp = -1
|
| tmp = -3
|
||||||
| output = 0
|
| output = 0
|
||||||
| memory_barrier()
|
| memory_barrier()
|
||||||
| output += tmp
|
| output += tmp
|
||||||
|}
|
|}
|
||||||
|""".stripMargin){ m =>
|
|""".stripMargin){ m =>
|
||||||
m.readLong(0xc000) should equal(-1)
|
m.readLong(0xc000) should equal(-3)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
test("Trivial implicit sbyte to word extension") {
|
||||||
|
EmuCrossPlatformBenchmarkRun(Cpu.Mos, Cpu.Z80, Cpu.Motorola6809)(
|
||||||
|
"""
|
||||||
|
|word output @$c000
|
||||||
|
|
|
||||||
|
|sbyte c
|
||||||
|
|
|
||||||
|
|noinline word f() {
|
||||||
|
| return c
|
||||||
|
|}
|
||||||
|
|
|
||||||
|
|void main() {
|
||||||
|
| c = -2
|
||||||
|
| output = f()
|
||||||
|
|}
|
||||||
|
|""".stripMargin){ m =>
|
||||||
|
m.readWord(0xc000) should equal(0xFFFE)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user