fixed and optimized branches and gotos in if statements

This commit is contained in:
Irmen de Jong 2019-01-22 22:52:43 +01:00
parent 209b50ddf7
commit dd02d97db4
6 changed files with 89 additions and 62 deletions

View File

@ -5,13 +5,11 @@ import prog8.ast.RegisterOrPair.*
import prog8.compiler.intermediate.IntermediateProgram
import prog8.compiler.intermediate.Opcode
import prog8.compiler.intermediate.Value
import prog8.compiler.intermediate.branchOpcodes
import prog8.optimizing.same
import prog8.parser.tryGetEmbeddedResource
import prog8.stackvm.Syscall
import java.io.File
import java.io.InputStream
import java.io.InputStreamReader
import java.nio.file.Paths
import java.util.*
import kotlin.math.abs
@ -514,10 +512,24 @@ private class StatementTranslator(private val prog: IntermediateProgram,
* _stmt_999_end:
* nop
*
* @todo generate more efficient bytecode for the form with just jumps: if(..) goto .. [else goto ..]
* For if statements with goto's, more efficient code is generated.
*/
prog.line(stmt.position)
translate(stmt.condition)
val trueGoto = stmt.truepart.statements.singleOrNull() as? Jump
if(trueGoto!=null) {
// optimization for if (condition) goto ....
val conditionJumpOpcode = when(stmt.condition.resultingDatatype(namespace, heap)) {
DataType.UBYTE, DataType.BYTE -> Opcode.JNZ
DataType.UWORD, DataType.WORD -> Opcode.JNZW
else -> throw CompilerException("invalid condition datatype (expected byte or word) $stmt")
}
translate(trueGoto, conditionJumpOpcode)
translate(stmt.elsepart)
return
}
val conditionJumpOpcode = when(stmt.condition.resultingDatatype(namespace, heap)) {
DataType.UBYTE, DataType.BYTE -> Opcode.JZ
DataType.UWORD, DataType.WORD -> Opcode.JZW
@ -1388,7 +1400,7 @@ private class StatementTranslator(private val prog: IntermediateProgram,
when {
stmt.generatedLabel!=null -> jumpLabel = stmt.generatedLabel
stmt.address!=null -> {
if(branchOpcode!=null)
if(branchOpcode in branchOpcodes)
throw CompilerException("cannot branch to address, should use absolute jump instead")
jumpAddress = Value(DataType.UWORD, stmt.address)
}

View File

@ -285,3 +285,8 @@ val opcodesWithVarArgument = setOf(
Opcode.DEC_INDEXED_VAR_UB, Opcode.DEC_INDEXED_VAR_B, Opcode.DEC_INDEXED_VAR_UW,
Opcode.DEC_INDEXED_VAR_W, Opcode.DEC_INDEXED_VAR_FLOAT
)
val branchOpcodes = setOf(
Opcode.BCS, Opcode.BCC, Opcode.BZ, Opcode.BNZ,
Opcode.BNEG, Opcode.BPOS, Opcode.BVS, Opcode.BVC
)

View File

@ -700,44 +700,72 @@ class AsmGen(val options: CompilationOptions, val program: IntermediateProgram,
Opcode.INV_WORD -> " jsr prog8_lib.inv_word"
Opcode.NOT_BYTE -> " jsr prog8_lib.not_byte"
Opcode.NOT_WORD -> " jsr prog8_lib.not_word"
Opcode.BCS -> " bcs ${ins.callLabel}"
Opcode.BCC -> " bcc ${ins.callLabel}"
Opcode.BNEG -> " bmi ${ins.callLabel}"
Opcode.BPOS -> " bpl ${ins.callLabel}"
Opcode.BVC -> " bvc ${ins.callLabel}"
Opcode.BVS -> " bvs ${ins.callLabel}"
Opcode.BZ -> " beq ${ins.callLabel}"
Opcode.BNZ -> " bne ${ins.callLabel}"
Opcode.BCS -> {
val label = ins.callLabel ?: hexVal(ins)
" bcs $label"
}
Opcode.BCC -> {
val label = ins.callLabel ?: hexVal(ins)
" bcc $label"
}
Opcode.BNEG -> {
val label = ins.callLabel ?: hexVal(ins)
" bmi $label"
}
Opcode.BPOS -> {
val label = ins.callLabel ?: hexVal(ins)
" bpl $label"
}
Opcode.BVC -> {
val label = ins.callLabel ?: hexVal(ins)
" bvc $label"
}
Opcode.BVS -> {
val label = ins.callLabel ?: hexVal(ins)
" bvs $label"
}
Opcode.BZ -> {
val label = ins.callLabel ?: hexVal(ins)
" beq $label"
}
Opcode.BNZ -> {
val label = ins.callLabel ?: hexVal(ins)
" bne $label"
}
Opcode.JZ -> {
val label = ins.callLabel ?: hexVal(ins)
"""
inx
lda ${(ESTACK_LO).toHex()},x
beq ${ins.callLabel}
beq $label
"""
}
Opcode.JZW -> {
val label = ins.callLabel ?: hexVal(ins)
"""
inx
lda ${(ESTACK_LO).toHex()},x
beq ${ins.callLabel}
beq $label
lda ${(ESTACK_HI).toHex()},x
beq ${ins.callLabel}
beq $label
"""
}
Opcode.JNZ -> {
val label = ins.callLabel ?: hexVal(ins)
"""
inx
lda ${(ESTACK_LO).toHex()},x
bne ${ins.callLabel}
bne $label
"""
}
Opcode.JNZW -> {
val label = ins.callLabel ?: hexVal(ins)
"""
inx
lda ${(ESTACK_LO).toHex()},x
bne ${ins.callLabel}
bne $label
lda ${(ESTACK_HI).toHex()},x
bne ${ins.callLabel}
bne $label
"""
}
Opcode.CAST_B_TO_UB -> "" // is a no-op, just carry on with the byte as-is

View File

@ -368,7 +368,6 @@ class SimplifyExpressions(private val namespace: INameScope, private val heap: H
}
// todo: get rid of this?
private data class ReorderedAssociativeBinaryExpr(val expr: BinaryExpression, val leftVal: LiteralValue?, val rightVal: LiteralValue?)
private fun reorderAssociative(expr: BinaryExpression, leftVal: LiteralValue?): ReorderedAssociativeBinaryExpr {

View File

@ -18,43 +18,4 @@ Memory Block Operations
these should call (or emit inline) optimized pieces of assembly code, so they run as fast as possible
At least we have memcopy() already and some screen related routines in asm
Bitmap Definition (for Sprites and Characters)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
to define CHARACTERS (8x8 monochrome or 4x8 multicolor = 8 bytes)
--> PLACE in memory on correct address (???k aligned)
and SPRITES (24x21 monochrome or 12x21 multicolor = 63 bytes)
--> PLACE in memory on correct address (base+sprite pointer, 64-byte aligned)
--> actually not needed because a block at the correct address, and an array using 3x21 binary values, more or less does exactly this already::
~ spritedata $0a00 {
; this memory block contains the sprite data
; it must start on an address aligned to 64 bytes.
%option force_output ; make sure the data in this block appears in the resulting program
ubyte[63] balloonsprite = [ %00000000,%01111111,%00000000,
%00000001,%11111111,%11000000,
%00000011,%11111111,%11100000,
%00000011,%11100011,%11100000,
%00000111,%11011100,%11110000,
%00000111,%11011101,%11110000,
%00000111,%11011100,%11110000,
%00000011,%11100011,%11100000,
%00000011,%11111111,%11100000,
%00000011,%11111111,%11100000,
%00000010,%11111111,%10100000,
%00000001,%01111111,%01000000,
%00000001,%00111110,%01000000,
%00000000,%10011100,%10000000,
%00000000,%10011100,%10000000,
%00000000,%01001001,%00000000,
%00000000,%01001001,%00000000,
%00000000,%00111110,%00000000,
%00000000,%00111110,%00000000,
%00000000,%00111110,%00000000,
%00000000,%00011100,%00000000 ]
}
@todo add memset() to easily set a part of memory to a specific byte value

View File

@ -1,11 +1,33 @@
%output raw
%launcher none
%import c64lib
~ main {
sub start() {
; memset($0400, $0400+40, 81)
A=99
if(A<99) goto first else goto second
first:
c64scr.print("a<99 !\n")
goto next
second:
c64scr.print("wrong: a>=99 ?!\n")
next:
A=99
if(A<99) goto first2 else {
c64scr.print("wrong: a>=99 ?!\n")
}
return
first2:
c64scr.print("a<99 !\n")
return
ubyte ub1
ubyte ub2
byte b1