optimize word [operator] byte, without translateExpression()

This commit is contained in:
Irmen de Jong 2020-11-24 22:26:11 +01:00
parent 378dcfe351
commit 936b046ed9
5 changed files with 169 additions and 137 deletions

View File

@ -1065,3 +1065,29 @@ _return_minusone
lda #-1 lda #-1
rts rts
.pend .pend
sign_extend_stack_byte .proc
; -- sign extend the (signed) byte on the stack to full 16 bits
lda P8ESTACK_LO+1,x
ora #$7f
bmi +
lda #0
+ sta P8ESTACK_HI+1,x
rts
.pend
sign_extend_AY_byte .proc
; -- sign extend the (signed) byte in AY to full 16 bits
pha
tya
and #$80
beq +
ldy #$ff
pla
rts
+ ldy #0
pla
rts
.pend

View File

@ -1192,20 +1192,20 @@ $label nop""")
assemblyLines.add(assembly) assemblyLines.add(assembly)
} }
internal fun signExtendAYlsb(valueDt: DataType) {
// sign extend signed byte in AY to full word in AY
when(valueDt) {
DataType.UBYTE -> out(" ldy #0")
DataType.BYTE -> out(" jsr prog8_lib.sign_extend_AY_byte")
else -> throw AssemblyError("need byte type")
}
}
internal fun signExtendStackLsb(valueDt: DataType) { internal fun signExtendStackLsb(valueDt: DataType) {
// sign extend signed byte on stack to signed word // sign extend signed byte on stack to signed word
when(valueDt) { when(valueDt) {
DataType.UBYTE -> { DataType.UBYTE -> out(" lda #0 | sta P8ESTACK_HI+1,x")
out(" lda #0 | sta P8ESTACK_HI+1,x") DataType.BYTE -> out(" jsr prog8_lib.sign_extend_stack_byte")
}
DataType.BYTE -> {
out("""
lda P8ESTACK_LO+1,x
ora #$7f
bmi +
lda #0
+ sta P8ESTACK_HI+1,x""")
}
else -> throw AssemblyError("need byte type") else -> throw AssemblyError("need byte type")
} }
} }

View File

@ -138,7 +138,7 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
is IdentifierReference -> throw AssemblyError("source kind should have been variable") is IdentifierReference -> throw AssemblyError("source kind should have been variable")
is ArrayIndexedExpression -> throw AssemblyError("source kind should have been array") is ArrayIndexedExpression -> throw AssemblyError("source kind should have been array")
is DirectMemoryRead -> throw AssemblyError("source kind should have been memory") is DirectMemoryRead -> throw AssemblyError("source kind should have been memory")
is TypecastExpression -> assignTypeCastedValue(assign.target, value.type, value.expression, assign) is TypecastExpression -> assignTypeCastedValue(assign.target, value.type, value.expression, value)
is FunctionCall -> { is FunctionCall -> {
when (val sub = value.target.targetStatement(program.namespace)) { when (val sub = value.target.targetStatement(program.namespace)) {
is Subroutine -> { is Subroutine -> {
@ -227,11 +227,14 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
} }
} }
private fun assignTypeCastedValue(target: AsmAssignTarget, targetDt: DataType, value: Expression, origAssign: AsmAssignment) { private fun assignTypeCastedValue(target: AsmAssignTarget, targetDt: DataType, value: Expression, origTypeCastExpression: TypecastExpression) {
val valueIDt = value.inferType(program) val valueIDt = value.inferType(program)
if(!valueIDt.isKnown) if(!valueIDt.isKnown)
throw AssemblyError("unknown dt") throw AssemblyError("unknown dt")
val valueDt = valueIDt.typeOrElse(DataType.STRUCT) val valueDt = valueIDt.typeOrElse(DataType.STRUCT)
if(valueDt==targetDt)
throw AssemblyError("type cast to identical dt should have been removed")
when(value) { when(value) {
is IdentifierReference -> { is IdentifierReference -> {
if(targetDt in WordDatatypes) { if(targetDt in WordDatatypes) {
@ -262,35 +265,26 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
else -> {} else -> {}
} }
when(value) {
is IdentifierReference -> { // special case optimizations
if(target.kind==TargetStorageKind.VARIABLE) { if (value is IdentifierReference) {
val sourceDt = value.inferType(program).typeOrElse(DataType.STRUCT) if(target.kind==TargetStorageKind.VARIABLE) {
if (sourceDt != DataType.STRUCT) if (valueDt != DataType.STRUCT)
return assignTypeCastedIdentifier(target.asmVarname, targetDt, asmgen.asmVariableName(value), sourceDt, origAssign.source.expression!!) return assignTypeCastedIdentifier(target.asmVarname, targetDt, asmgen.asmVariableName(value), valueDt)
}
}
is PrefixExpression -> {}
is BinaryExpression -> {}
is ArrayIndexedExpression -> {}
is TypecastExpression -> {}
is RangeExpr -> {}
is FunctionCall -> {}
else -> {
// TODO optimize the others further?
if(this.asmgen.options.slowCodegenWarnings)
println("warning: slow stack evaluation used for typecast: $value into $targetDt at ${value.position}")
} }
} }
// give up, do it via eval stack // give up, do it via eval stack
asmgen.translateExpression(origAssign.source.expression!!) // TODO optimize typecasts for more special cases?
// note: cannot use assignTypeCastedValue because that is ourselves :P
if(this.asmgen.options.slowCodegenWarnings)
println("warning: slow stack evaluation used for typecast: $value into $targetDt at ${value.position}")
asmgen.translateExpression(origTypeCastExpression) // this performs the actual type cast in translateExpression(Typecast)
assignStackValue(target) assignStackValue(target)
} }
private fun assignTypeCastedIdentifier(targetAsmVarName: String, targetDt: DataType, private fun assignTypeCastedIdentifier(targetAsmVarName: String, targetDt: DataType,
sourceAsmVarName: String, sourceDt: DataType, sourceAsmVarName: String, sourceDt: DataType) {
origExpr: Expression) {
if(sourceDt == targetDt) if(sourceDt == targetDt)
throw AssemblyError("typecast to identical value") throw AssemblyError("typecast to identical value")

View File

@ -1100,11 +1100,8 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
throw AssemblyError("unknown dt") throw AssemblyError("unknown dt")
val valueDt = valueiDt.typeOrElse(DataType.STRUCT) val valueDt = valueiDt.typeOrElse(DataType.STRUCT)
// TODO can use registers instead of stack value? fun multiplyVarByWordInAY() {
fun multiplyWord() {
asmgen.out(""" asmgen.out("""
lda P8ESTACK_LO+1,x
ldy P8ESTACK_HI+1,x
sta P8ZP_SCRATCH_W1 sta P8ZP_SCRATCH_W1
sty P8ZP_SCRATCH_W1+1 sty P8ZP_SCRATCH_W1+1
lda $name lda $name
@ -1117,51 +1114,36 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
""") """)
} }
// TODO can use registers instead of stack value? fun divideVarByWordInAY() {
fun divideWord() { asmgen.out("""
if (dt == DataType.WORD) { pha
asmgen.out("""
lda $name lda $name
ldy $name+1
sta P8ZP_SCRATCH_W1 sta P8ZP_SCRATCH_W1
sty P8ZP_SCRATCH_W1+1 lda $name+1
lda P8ESTACK_LO+1,x sta P8ZP_SCRATCH_W1+1
ldy P8ESTACK_HI+1,x pla""")
jsr math.divmod_w_asm if (dt == DataType.WORD)
sta $name asmgen.out(" jsr math.divmod_w_asm")
sty $name+1 else
""") asmgen.out(" jsr math.divmod_uw_asm")
} else { asmgen.out(" sta $name | sty $name+1")
asmgen.out("""
lda $name
ldy $name+1
sta P8ZP_SCRATCH_W1
sty P8ZP_SCRATCH_W1+1
lda P8ESTACK_LO+1,x
ldy P8ESTACK_HI+1,x
jsr math.divmod_uw_asm
sta $name
sty $name+1
""")
}
} }
// TODO can use registers instead of stack value? fun remainderVarByWordInAY() {
fun remainderWord() {
if(dt==DataType.WORD) if(dt==DataType.WORD)
throw AssemblyError("remainder of signed integers is not properly defined/implemented, use unsigned instead") throw AssemblyError("remainder of signed integers is not properly defined/implemented, use unsigned instead")
asmgen.out(""" asmgen.out("""
pha
lda $name lda $name
ldy $name+1
sta P8ZP_SCRATCH_W1 sta P8ZP_SCRATCH_W1
sty P8ZP_SCRATCH_W1+1 lda $name+1
lda P8ESTACK_LO+1,x sta P8ZP_SCRATCH_W1+1
ldy P8ESTACK_HI+1,x pla
jsr math.divmod_uw_asm jsr math.divmod_uw_asm
lda P8ZP_SCRATCH_W2 lda P8ZP_SCRATCH_W2
ldy P8ZP_SCRATCH_W2+1
sta $name sta $name
lda P8ZP_SCRATCH_W2+1 sty $name+1
sta $name+1
""") """)
} }
@ -1171,14 +1153,12 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
when (operator) { when (operator) {
// note: ** (power) operator requires floats. // note: ** (power) operator requires floats.
"+" -> { "+" -> {
if(asmgen.options.slowCodegenWarnings) asmgen.assignExpressionToVariable(value, "P8ZP_SCRATCH_B1", valueDt, null)
println("warning: slow stack evaluation used (4): $name += ${value::class.simpleName} at ${value.position}") // TODO
asmgen.translateExpression(value)
if(valueDt==DataType.UBYTE) if(valueDt==DataType.UBYTE)
asmgen.out(""" asmgen.out("""
lda $name lda $name
clc clc
adc P8ESTACK_LO+1,x adc P8ZP_SCRATCH_B1
sta $name sta $name
bcc + bcc +
inc $name+1 inc $name+1
@ -1186,7 +1166,7 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
else else
asmgen.out(""" asmgen.out("""
ldy #0 ldy #0
lda P8ESTACK_LO+1,x lda P8ZP_SCRATCH_B1
bpl + bpl +
dey ; sign extend dey ; sign extend
+ clc + clc
@ -1195,17 +1175,14 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
tya tya
adc $name+1 adc $name+1
sta $name+1""") sta $name+1""")
asmgen.out(" inx")
} }
"-" -> { "-" -> {
if(asmgen.options.slowCodegenWarnings) asmgen.assignExpressionToVariable(value, "P8ZP_SCRATCH_REG", valueDt, null)
println("warning: slow stack evaluation used (4): $name -= ${value::class.simpleName} at ${value.position}") // TODO
asmgen.translateExpression(value)
if(valueDt==DataType.UBYTE) if(valueDt==DataType.UBYTE)
asmgen.out(""" asmgen.out("""
lda $name lda $name
sec sec
sbc P8ESTACK_LO+1,x sbc P8ZP_SCRATCH_REG
sta $name sta $name
bcs + bcs +
dec $name+1 dec $name+1
@ -1213,45 +1190,38 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
else else
asmgen.out(""" asmgen.out("""
ldy #0 ldy #0
lda P8ESTACK_LO+1,x lda P8ZP_SCRATCH_REG
bpl + bpl +
dey ; sign extend dey ; sign extend
+ sty P8ZP_SCRATCH_B1 + sty P8ZP_SCRATCH_B1
lda $name lda $name
sec sec
sbc P8ESTACK_LO+1,x sbc P8ZP_SCRATCH_REG
sta $name sta $name
lda $name+1 lda $name+1
sbc P8ZP_SCRATCH_B1 sbc P8ZP_SCRATCH_B1
sta $name+1""") sta $name+1""")
asmgen.out(" inx")
} }
"*" -> { "*" -> {
// stack contains (u) byte value, sign extend that and proceed with regular 16 bit operation // stack contains (u) byte value, sign extend that and proceed with regular 16 bit operation
if(asmgen.options.slowCodegenWarnings) // TODO use an optimized word * byte multiplication routine
println("warning: slow stack evaluation used (4): $name *= ${value::class.simpleName} at ${value.position}") // TODO asmgen.assignExpressionToRegister(value, RegisterOrPair.A)
asmgen.translateExpression(value) asmgen.signExtendAYlsb(valueDt)
asmgen.signExtendStackLsb(valueDt) multiplyVarByWordInAY()
multiplyWord()
asmgen.out(" inx")
} }
"/" -> { "/" -> {
// stack contains (u) byte value, sign extend that and proceed with regular 16 bit operation // stack contains (u) byte value, sign extend that and proceed with regular 16 bit operation
if(asmgen.options.slowCodegenWarnings) // TODO use an optimized word / byte divmod routine
println("warning: slow stack evaluation used (4): $name /= ${value::class.simpleName} at ${value.position}") // TODO asmgen.assignExpressionToRegister(value, RegisterOrPair.A)
asmgen.translateExpression(value) asmgen.signExtendAYlsb(valueDt)
asmgen.signExtendStackLsb(valueDt) divideVarByWordInAY()
divideWord()
asmgen.out(" inx")
} }
"%" -> { "%" -> {
// stack contains (u) byte value, sign extend that and proceed with regular 16 bit operation // stack contains (u) byte value, sign extend that and proceed with regular 16 bit operation
if(asmgen.options.slowCodegenWarnings) // TODO use an optimized word / byte divmod routine
println("warning: slow stack evaluation used (4): $name %= ${value::class.simpleName} at ${value.position}") // TODO asmgen.assignExpressionToRegister(value, RegisterOrPair.A)
asmgen.translateExpression(value) asmgen.signExtendAYlsb(valueDt)
asmgen.signExtendStackLsb(valueDt) remainderVarByWordInAY()
remainderWord()
asmgen.out(" inx")
} }
"<<" -> { "<<" -> {
asmgen.assignExpressionToRegister(value, RegisterOrPair.Y) asmgen.assignExpressionToRegister(value, RegisterOrPair.Y)
@ -1306,56 +1276,37 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
when (operator) { when (operator) {
// note: ** (power) operator requires floats. // note: ** (power) operator requires floats.
"+" -> { "+" -> {
if(asmgen.options.slowCodegenWarnings) asmgen.assignExpressionToRegister(value, RegisterOrPair.AY)
println("warning: slow stack evaluation used (4w): $name += ${value::class.simpleName} at ${value.position}") // TODO asmgen.out(" clc | adc $name | sta $name | tya | adc $name+1 | sta $name+1")
asmgen.translateExpression(value)
asmgen.out(" lda $name | clc | adc P8ESTACK_LO+1,x | sta $name | lda $name+1 | adc P8ESTACK_HI+1,x | sta $name+1 | inx")
} }
"-" -> { "-" -> {
if(asmgen.options.slowCodegenWarnings) asmgen.assignExpressionToVariable(value, "P8ZP_SCRATCH_W1", valueDt, null)
println("warning: slow stack evaluation used (4w): $name -= ${value::class.simpleName} at ${value.position}") // TODO asmgen.out(" lda $name | sec | sbc P8ZP_SCRATCH_W1 | sta $name | lda $name+1 | sbc P8ZP_SCRATCH_W1+1 | sta $name+1")
asmgen.translateExpression(value)
asmgen.out(" lda $name | sec | sbc P8ESTACK_LO+1,x | sta $name | lda $name+1 | sbc P8ESTACK_HI+1,x | sta $name+1 | inx")
} }
"*" -> { "*" -> {
if(asmgen.options.slowCodegenWarnings) asmgen.assignExpressionToRegister(value, RegisterOrPair.AY)
println("warning: slow stack evaluation used (4w): $name *= ${value::class.simpleName} at ${value.position}") // TODO multiplyVarByWordInAY()
asmgen.translateExpression(value)
multiplyWord()
asmgen.out(" inx")
} }
"/" -> { "/" -> {
if(asmgen.options.slowCodegenWarnings) asmgen.assignExpressionToRegister(value, RegisterOrPair.AY)
println("warning: slow stack evaluation used (4w): $name /= ${value::class.simpleName} at ${value.position}") // TODO divideVarByWordInAY()
asmgen.translateExpression(value)
divideWord()
asmgen.out(" inx")
} }
"%" -> { "%" -> {
if(asmgen.options.slowCodegenWarnings) asmgen.assignExpressionToRegister(value, RegisterOrPair.AY)
println("warning: slow stack evaluation used (4w): $name %= ${value::class.simpleName} at ${value.position}") // TODO remainderVarByWordInAY()
asmgen.translateExpression(value)
remainderWord()
asmgen.out(" inx")
} }
"<<", ">>" -> throw AssemblyError("shift by a word value not supported, max is a byte") "<<", ">>" -> throw AssemblyError("shift by a word value not supported, max is a byte")
"&" -> { "&" -> {
if(asmgen.options.slowCodegenWarnings) asmgen.assignExpressionToRegister(value, RegisterOrPair.AY)
println("warning: slow stack evaluation used (4w): $name &= ${value::class.simpleName} at ${value.position}") // TODO asmgen.out(" and $name | sta $name | tya | and $name+1 | sta $name+1")
asmgen.translateExpression(value)
asmgen.out(" lda $name | and P8ESTACK_LO+1,x | sta $name | lda $name+1 | and P8ESTACK_HI+1,x | sta $name+1 | inx")
} }
"^" -> { "^" -> {
if(asmgen.options.slowCodegenWarnings) asmgen.assignExpressionToRegister(value, RegisterOrPair.AY)
println("warning: slow stack evaluation used (4w): $name ^= ${value::class.simpleName} at ${value.position}") // TODO asmgen.out(" eor $name | sta $name | tya | eor $name+1 | sta $name+1")
asmgen.translateExpression(value)
asmgen.out(" lda $name | eor P8ESTACK_LO+1,x | sta $name | lda $name+1 | eor P8ESTACK_HI+1,x | sta $name+1 | inx")
} }
"|" -> { "|" -> {
if(asmgen.options.slowCodegenWarnings) asmgen.assignExpressionToRegister(value, RegisterOrPair.AY)
println("warning: slow stack evaluation used (4w): $name |r= ${value::class.simpleName} at ${value.position}") // TODO asmgen.out(" ora $name | sta $name | tya | ora $name+1 | sta $name+1")
asmgen.translateExpression(value)
asmgen.out(" lda $name | ora P8ESTACK_LO+1,x | sta $name | lda $name+1 | ora P8ESTACK_HI+1,x | sta $name+1 | inx")
} }
else -> throw AssemblyError("invalid operator for in-place modification $operator") else -> throw AssemblyError("invalid operator for in-place modification $operator")
} }

View File

@ -10,6 +10,8 @@ main {
uword uw = $c000 uword uw = $c000
ubyte ub = 1 ubyte ub = 1
ubyte ub2 = 1 ubyte ub2 = 1
uword uv1 = 1
uword uv2 = 1
uw = 1000 uw = 1000
uw += ub+ub2 uw += ub+ub2
@ -46,6 +48,65 @@ main {
txt.print_uw(uw) txt.print_uw(uw)
txt.chrout('\n') txt.chrout('\n')
uw = $1111
uw &= (ub+ub2) | 15
txt.print_uwhex(uw, 1)
txt.chrout('\n')
uw = $1111
uw |= (ub+ub2) | 15
txt.print_uwhex(uw, 1)
txt.chrout('\n')
uw = $1111
uw ^= (ub+ub2) | 15
txt.print_uwhex(uw, 1)
txt.chrout('\n')
txt.chrout('\n')
uw = 1000
uw += uv1+uv2
txt.print_uw(uw)
txt.chrout('\n')
uw = 1000
uw -= uv1+uv2
txt.print_uw(uw)
txt.chrout('\n')
uw = 1000
uw *= uv1+uv2
txt.print_uw(uw)
txt.chrout('\n')
uw = 1000
uw /= uv1+uv2
txt.print_uw(uw)
txt.chrout('\n')
uw = 1000
uw %= 5*uv1+uv2+uv2
txt.print_uw(uw)
txt.chrout('\n')
uw = $1111
uw &= (uv1+uv2) | 1023
txt.print_uwhex(uw, 1)
txt.chrout('\n')
uw = $1111
uw |= (uv1+uv2) | 32768
txt.print_uwhex(uw, 1)
txt.chrout('\n')
uw = $1111
uw ^= (uv1+uv2) | 32768
txt.print_uwhex(uw, 1)
txt.chrout('\n')
test_stack.test() test_stack.test()
} }