From 75b38d7b845ddb2da419a1f65b5bd7d55b5fbaa3 Mon Sep 17 00:00:00 2001 From: Irmen de Jong Date: Fri, 18 Jan 2019 01:33:54 +0100 Subject: [PATCH] added swap() slow version --- compiler/src/prog8/compiler/Compiler.kt | 76 +++++++++++++++++++ .../src/prog8/functions/BuiltinFunctions.kt | 1 + .../prog8/optimizing/StatementOptimizer.kt | 7 +- docs/source/programming.rst | 3 + examples/cube3d-sprites.p8 | 17 ++--- examples/test.p8 | 12 +-- 6 files changed, 93 insertions(+), 23 deletions(-) diff --git a/compiler/src/prog8/compiler/Compiler.kt b/compiler/src/prog8/compiler/Compiler.kt index a443bddcb..78b8fe027 100644 --- a/compiler/src/prog8/compiler/Compiler.kt +++ b/compiler/src/prog8/compiler/Compiler.kt @@ -5,6 +5,7 @@ import prog8.ast.RegisterOrPair.* import prog8.compiler.intermediate.IntermediateProgram import prog8.compiler.intermediate.Opcode import prog8.compiler.intermediate.Value +import prog8.optimizing.same import prog8.stackvm.Syscall import java.util.* import kotlin.math.abs @@ -760,6 +761,12 @@ private class StatementTranslator(private val prog: IntermediateProgram, private fun translateFunctionCall(funcname: String, args: List) { // some functions are implemented as vm opcodes + + if(funcname == "swap") { + translateSwap(args) + return + } + args.forEach { translate(it) } // place function argument(s) on the stack when (funcname) { "len" -> { @@ -923,6 +930,75 @@ private class StatementTranslator(private val prog: IntermediateProgram, } } + private fun StatementTranslator.translateSwap(args: List) { + // swap(x,y) is treated differently, it's not a normal function call + if (args.size != 2) + throw AstException("swap requires 2 arguments") + val dt1 = args[0].resultingDatatype(namespace, heap)!! + val dt2 = args[1].resultingDatatype(namespace, heap)!! + if (dt1 != dt2) + throw AstException("swap requires 2 args of identical type") + if (args[0].constValue(namespace, heap) != null || args[1].constValue(namespace, heap) != null) + throw AstException("swap requires 2 variables, not constant value(s)") + if (dt1 !in NumericDatatypes) + throw AstException("swap requires args of numerical type") + if(same(args[0], args[1])) + throw AstException("swap should have 2 different args") + + // @todo implement the above errors as nice AstChecker expression errors. + // @todo implement this more efficiently with using the xor trick instead of the stack! + // Swap(X,Y) := + // X ^= Y + // Y ^= X + // X ^= Y + // for floats, this doesn't work, use a temp variable instead. + + // pop first then second arg + translate(args[0]) + translate(args[1]) + // pop stack in reverse order + when { + args[0] is IdentifierReference -> { + val target = AssignTarget(null, args[0] as IdentifierReference, null, null, args[0].position) + popValueIntoTarget(target, dt1) + } + args[0] is RegisterExpr -> { + val target = AssignTarget((args[0] as RegisterExpr).register, null, null, null, args[0].position) + popValueIntoTarget(target, dt1) + } + args[0] is ArrayIndexedExpression -> { + val target = AssignTarget(null, null, args[0] as ArrayIndexedExpression, null, args[0].position) + popValueIntoTarget(target, dt1) + } + args[0] is DirectMemoryRead -> { + val target = AssignTarget(null, null, null, DirectMemoryWrite((args[0] as DirectMemoryRead).addressExpression, args[0].position), args[0].position) + popValueIntoTarget(target, dt1) + } + else -> TODO("unpop type ${args[0]}") + } + + when { + args[1] is IdentifierReference -> { + val target = AssignTarget(null, args[1] as IdentifierReference, null, null, args[1].position) + popValueIntoTarget(target, dt2) + } + args[1] is RegisterExpr -> { + val target = AssignTarget((args[1] as RegisterExpr).register, null, null, null, args[1].position) + popValueIntoTarget(target, dt2) + } + args[1] is ArrayIndexedExpression -> { + val target = AssignTarget(null, null, args[1] as ArrayIndexedExpression, null, args[1].position) + popValueIntoTarget(target, dt2) + } + args[1] is DirectMemoryRead -> { + val target = AssignTarget(null, null, null, DirectMemoryWrite((args[1] as DirectMemoryRead).addressExpression, args[1].position), args[1].position) + popValueIntoTarget(target, dt2) + } + else -> TODO("unpop type ${args[1]}") + } + return + } + private fun translateSubroutineCall(subroutine: Subroutine, arguments: List, callPosition: Position) { // evaluate the arguments and assign them into the subroutine's argument variables. var restoreX = Register.X in subroutine.asmClobbers diff --git a/compiler/src/prog8/functions/BuiltinFunctions.kt b/compiler/src/prog8/functions/BuiltinFunctions.kt index 396a2cf16..b88e29ef0 100644 --- a/compiler/src/prog8/functions/BuiltinFunctions.kt +++ b/compiler/src/prog8/functions/BuiltinFunctions.kt @@ -68,6 +68,7 @@ val BuiltinFunctions = mapOf( "clear_carry" to FunctionSignature(false, emptyList(), null), "set_irqd" to FunctionSignature(false, emptyList(), null), "clear_irqd" to FunctionSignature(false, emptyList(), null), + "swap" to FunctionSignature(false, listOf(BuiltinFunctionParam("first", NumericDatatypes), BuiltinFunctionParam("second", NumericDatatypes)), null), "memcopy" to FunctionSignature(false, listOf( BuiltinFunctionParam("from", IntegerDatatypes + IterableDatatypes), BuiltinFunctionParam("to", IntegerDatatypes + IterableDatatypes), diff --git a/compiler/src/prog8/optimizing/StatementOptimizer.kt b/compiler/src/prog8/optimizing/StatementOptimizer.kt index be53d73bc..f7db7016a 100644 --- a/compiler/src/prog8/optimizing/StatementOptimizer.kt +++ b/compiler/src/prog8/optimizing/StatementOptimizer.kt @@ -485,14 +485,17 @@ fun same(left: IExpression, right: IExpression): Boolean { return (right is RegisterExpr && right.register==left.register) is IdentifierReference -> return (right is IdentifierReference && right.nameInSource==left.nameInSource) - is ArrayIndexedExpression -> - return (right is ArrayIndexedExpression && right.identifier==left.identifier && right.arrayspec==left.arrayspec) is PrefixExpression -> return (right is PrefixExpression && right.operator==left.operator && same(right.expression, left.expression)) is BinaryExpression -> return (right is BinaryExpression && right.operator==left.operator && same(right.left, left.left) && same(right.right, left.right)) + is ArrayIndexedExpression -> { + return (right is ArrayIndexedExpression && right.identifier.nameInSource == left.identifier.nameInSource + && same(right.arrayspec.x, left.arrayspec.x)) + } + is LiteralValue -> return (right is LiteralValue && right==left) } return false } diff --git a/docs/source/programming.rst b/docs/source/programming.rst index 9e456f1f2..70c857cfd 100644 --- a/docs/source/programming.rst +++ b/docs/source/programming.rst @@ -664,6 +664,9 @@ memcopy(from, to, numbytes) it is only faster if the number of bytes is larger than a certain threshold. Compare the generated code to see if it was beneficial or not. +swap(x, y) + Swap the values of numerical variables (or memory locations) x and y in a fast way. + set_carry() / clear_carry() Set (or clear) the CPU status register Carry flag. No result value. (translated into ``SEC`` or ``CLC`` cpu instruction) diff --git a/examples/cube3d-sprites.p8 b/examples/cube3d-sprites.p8 index 3a6ad37a1..f0059c013 100644 --- a/examples/cube3d-sprites.p8 +++ b/examples/cube3d-sprites.p8 @@ -128,8 +128,6 @@ } - ubyte[8] spritecolors = [1,1,7,15,12,11,9,9] - sub position_sprites() { ; set each of the 8 sprites to the correct vertex of the cube @@ -140,20 +138,15 @@ for ubyte i1 in 0 to sorti { ubyte i2 = i1+1 if(rotatedz[i1] > rotatedz[i2]) { - ; @todo use a swap() builtin function? - word temp = rotatedx[i1] - rotatedx[i1] = rotatedx[i2] - rotatedx[i2] = temp - temp = rotatedy[i1] - rotatedy[i1] = rotatedy[i2] - rotatedy[i2] = temp - temp = rotatedz[i1] - rotatedz[i1] = rotatedz[i2] - rotatedz[i2] = temp + swap(rotatedx[i1], rotatedx[i2]) + swap(rotatedy[i1], rotatedy[i2]) + swap(rotatedz[i1], rotatedz[i2]) } } } + ubyte[8] spritecolors = [1,1,7,15,12,11,9,9] + for ubyte i in 0 to 7 { word zc = rotatedz[i] word persp = 300+zc/256 diff --git a/examples/test.p8 b/examples/test.p8 index 8bf2cd1c9..8af47f7c2 100644 --- a/examples/test.p8 +++ b/examples/test.p8 @@ -18,15 +18,9 @@ for ubyte i1 in 0 to sorti { ubyte i2=i1+1 if(rotatedz[i2]>rotatedz[i1]) { - word t = rotatedx[i1] - rotatedx[i1] = rotatedx[i2] - rotatedx[i2] = t - t = rotatedy[i1] - rotatedy[i1] = rotatedy[i2] - rotatedy[i2] = t - t = rotatedz[i1] - rotatedz[i1] = rotatedz[i2] - rotatedz[i2] = t + swap(rotatedx[i1], rotatedx[i2]) + swap(rotatedy[i1], rotatedy[i2]) + swap(rotatedz[i1], rotatedz[i2]) } } }