rebuilding floating point stack evaluation (using cpu stack)

This commit is contained in:
Irmen de Jong 2023-07-18 01:24:27 +02:00
parent 9f247901d4
commit d5707b7bf3
9 changed files with 413 additions and 34 deletions

View File

@ -207,7 +207,8 @@ class AsmGen6502Internal (
private val postincrdecrAsmGen = PostIncrDecrAsmGen(program, this)
private val functioncallAsmGen = FunctionCallAsmGen(program, this)
private val programGen = ProgramAndVarsGen(program, options, errors, symbolTable, functioncallAsmGen, this, allocator, zeropage)
private val assignmentAsmGen = AssignmentAsmGen(program, this, allocator)
private val anyExprGen = AnyExprAsmGen(this)
private val assignmentAsmGen = AssignmentAsmGen(program, this, anyExprGen, allocator)
private val builtinFunctionsAsmGen = BuiltinFunctionsAsmGen(program, this, assignmentAsmGen)
fun compileToAssembly(): IAssemblyProgram? {
@ -3013,6 +3014,14 @@ $repeatLabel""")
}
}
internal fun pushFAC1() {
out(" jsr floats.pushFAC1")
}
internal fun popFAC1() {
out(" jsr floats.popFAC1")
}
internal fun needAsaveForExpr(arg: PtExpression): Boolean =
arg !is PtNumber && arg !is PtIdentifier && (arg !is PtMemoryByte || !arg.isSimple())

View File

@ -0,0 +1,266 @@
package prog8.codegen.cpu6502.assignment
import prog8.code.ast.PtBinaryExpression
import prog8.code.core.*
import prog8.codegen.cpu6502.AsmGen6502Internal
//
// This contains codegen for stack-based evaluation of binary expressions.
// It uses the CPU stack so depth is limited.
// It is called "as a last resort" if the optimized codegen path is unable
// to come up with a special case of the expression.
//
internal class AnyExprAsmGen(
private val asmgen: AsmGen6502Internal
) {
fun assignAnyExpressionUsingStack(expr: PtBinaryExpression, assign: AsmAssignment): Boolean {
when(expr.type) {
in ByteDatatypes -> {
if(expr.left.type in ByteDatatypes && expr.right.type in ByteDatatypes)
return assignByteBinExpr(expr, assign)
if (expr.left.type in WordDatatypes && expr.right.type in WordDatatypes) {
require(expr.operator in ComparisonOperators)
TODO("words operands comparison -> byte")
}
if (expr.left.type==DataType.FLOAT && expr.right.type==DataType.FLOAT) {
require(expr.operator in ComparisonOperators)
return assignFloatBinExpr(expr, assign)
}
TODO("weird expr operand types")
}
in WordDatatypes -> {
require(expr.left.type in WordDatatypes && expr.right.type in WordDatatypes) {
"both operands must be words"
}
return assignWordBinExpr(expr, assign)
}
DataType.FLOAT -> {
require(expr.left.type==DataType.FLOAT && expr.right.type==DataType.FLOAT) {
"both operands must be floats"
}
return assignFloatBinExpr(expr, assign)
}
else -> throw AssemblyError("weird expression type in assignment")
}
}
private fun assignWordBinExpr(expr: PtBinaryExpression, assign: AsmAssignment): Boolean {
when(expr.operator) {
"+" -> {
TODO("word + at ${expr.position}")
}
"-" -> {
TODO("word - at ${expr.position}")
}
"*" -> {
TODO("word * at ${expr.position}")
}
"/" -> {
TODO("word / at ${expr.position}")
}
"<<" -> {
TODO("word << at ${expr.position}")
}
">>" -> {
TODO("word >> at ${expr.position}")
}
"%" -> {
TODO("word % at ${expr.position}")
}
"&", "and" -> {
TODO("word and at ${expr.position}")
}
"|", "or" -> {
TODO("word or at ${expr.position}")
}
"^", "xor" -> {
TODO("word xor at ${expr.position}")
}
"==" -> {
TODO("word == at ${expr.position}")
}
"!=" -> {
TODO("word != at ${expr.position}")
}
"<" -> {
TODO("word < at ${expr.position}")
}
"<=" -> {
TODO("word <= at ${expr.position}")
}
">" -> {
TODO("word > at ${expr.position}")
}
">=" -> {
TODO("word >= at ${expr.position}")
}
else -> return false
}
}
private fun assignByteBinExpr(expr: PtBinaryExpression, assign: AsmAssignment): Boolean {
when(expr.operator) {
"+" -> {
asmgen.assignExpressionToRegister(expr.left, RegisterOrPair.A, false)
asmgen.out(" pha")
asmgen.assignExpressionToVariable(expr.right, "P8ZP_SCRATCH_B1", DataType.UBYTE)
asmgen.out(" pla | clc | adc P8ZP_SCRATCH_B1")
asmgen.assignRegister(RegisterOrPair.A, assign.target)
return true
}
"-" -> {
asmgen.assignExpressionToRegister(expr.left, RegisterOrPair.A, false)
asmgen.out(" pha")
asmgen.assignExpressionToVariable(expr.right, "P8ZP_SCRATCH_B1", DataType.UBYTE)
asmgen.out(" pla | sec | sbc P8ZP_SCRATCH_B1")
asmgen.assignRegister(RegisterOrPair.A, assign.target)
return true
}
"*" -> {
TODO("byte * at ${expr.position}")
}
"/" -> {
TODO("byte / at ${expr.position}")
}
"<<" -> {
TODO("byte << at ${expr.position}")
}
">>" -> {
TODO("byte >> at ${expr.position}")
}
"%" -> {
TODO("byte % at ${expr.position}")
}
"&", "and" -> {
asmgen.assignExpressionToRegister(expr.left, RegisterOrPair.A, false)
asmgen.out(" pha")
asmgen.assignExpressionToVariable(expr.right, "P8ZP_SCRATCH_B1", DataType.UBYTE)
asmgen.out(" pla | and P8ZP_SCRATCH_B1")
asmgen.assignRegister(RegisterOrPair.A, assign.target)
return true
}
"|", "or" -> {
asmgen.assignExpressionToRegister(expr.left, RegisterOrPair.A, false)
asmgen.out(" pha")
asmgen.assignExpressionToVariable(expr.right, "P8ZP_SCRATCH_B1", DataType.UBYTE)
asmgen.out(" pla | ora P8ZP_SCRATCH_B1")
asmgen.assignRegister(RegisterOrPair.A, assign.target)
return true
}
"^", "xor" -> {
asmgen.assignExpressionToRegister(expr.left, RegisterOrPair.A, false)
asmgen.out(" pha")
asmgen.assignExpressionToVariable(expr.right, "P8ZP_SCRATCH_B1", DataType.UBYTE)
asmgen.out(" pla | eor P8ZP_SCRATCH_B1")
asmgen.assignRegister(RegisterOrPair.A, assign.target)
return true
}
"==" -> {
TODO("byte == at ${expr.position}")
}
"!=" -> {
TODO("byte != at ${expr.position}")
}
"<" -> {
TODO("byte < at ${expr.position}")
}
"<=" -> {
TODO("byte <= at ${expr.position}")
}
">" -> {
TODO("byte > at ${expr.position}")
}
">=" -> {
TODO("byte >= at ${expr.position}")
}
else -> return false
}
}
private fun assignFloatBinExpr(expr: PtBinaryExpression, assign: AsmAssignment): Boolean {
when(expr.operator) {
"+" -> {
asmgen.assignExpressionToRegister(expr.left, RegisterOrPair.FAC1, true)
if(!expr.right.isSimple()) asmgen.pushFAC1()
asmgen.assignExpressionToRegister(expr.right, RegisterOrPair.FAC2, true)
if(!expr.right.isSimple()) asmgen.popFAC1()
asmgen.out(" jsr floats.FADDT")
asmgen.assignRegister(RegisterOrPair.FAC1, assign.target)
return true
}
"-" -> {
asmgen.assignExpressionToRegister(expr.right, RegisterOrPair.FAC1, true)
if(!expr.left.isSimple()) asmgen.pushFAC1()
asmgen.assignExpressionToRegister(expr.left, RegisterOrPair.FAC2, true)
if(!expr.left.isSimple()) asmgen.popFAC1()
asmgen.out(" jsr floats.FSUBT")
asmgen.assignRegister(RegisterOrPair.FAC1, assign.target)
return true
}
"*" -> {
asmgen.assignExpressionToRegister(expr.left, RegisterOrPair.FAC1, true)
if(!expr.right.isSimple()) asmgen.pushFAC1()
asmgen.assignExpressionToRegister(expr.right, RegisterOrPair.FAC2, true)
if(!expr.right.isSimple()) asmgen.popFAC1()
asmgen.out(" jsr floats.FMULTT")
asmgen.assignRegister(RegisterOrPair.FAC1, assign.target)
return true
}
"/" -> {
asmgen.assignExpressionToRegister(expr.right, RegisterOrPair.FAC1, true)
if(!expr.left.isSimple()) asmgen.pushFAC1()
asmgen.assignExpressionToRegister(expr.left, RegisterOrPair.FAC2, true)
if(!expr.left.isSimple()) asmgen.popFAC1()
asmgen.out(" jsr floats.FDIVT")
asmgen.assignRegister(RegisterOrPair.FAC1, assign.target)
return true
}
"==" -> {
setupFloatComparisonFAC1vsVarAY(expr)
asmgen.out(" jsr floats.var_fac1_equal_f")
asmgen.assignRegister(RegisterOrPair.A, assign.target)
return true
}
"!=" -> {
setupFloatComparisonFAC1vsVarAY(expr)
asmgen.out(" jsr floats.var_fac1_notequal_f")
asmgen.assignRegister(RegisterOrPair.A, assign.target)
return true
}
"<" -> {
setupFloatComparisonFAC1vsVarAY(expr)
asmgen.out(" jsr floats.var_fac1_less_f")
asmgen.assignRegister(RegisterOrPair.A, assign.target)
return true
}
">" -> {
setupFloatComparisonFAC1vsVarAY(expr)
asmgen.out(" jsr floats.var_fac1_greater_f")
asmgen.assignRegister(RegisterOrPair.A, assign.target)
return true
}
"<=" -> {
setupFloatComparisonFAC1vsVarAY(expr)
asmgen.out(" jsr floats.var_fac1_lesseq_f")
asmgen.assignRegister(RegisterOrPair.A, assign.target)
return true
}
">=" -> {
setupFloatComparisonFAC1vsVarAY(expr)
asmgen.out(" jsr floats.var_fac1_greatereq_f")
asmgen.assignRegister(RegisterOrPair.A, assign.target)
return true
}
else -> TODO("float expression operator ${expr.operator}")
}
return false
}
private fun setupFloatComparisonFAC1vsVarAY(expr: PtBinaryExpression) {
asmgen.assignExpressionToRegister(expr.left, RegisterOrPair.FAC1, true)
if(!expr.right.isSimple()) asmgen.pushFAC1()
asmgen.assignExpressionToVariable(expr.right, "floats.floats_temp_var", DataType.FLOAT)
if(!expr.right.isSimple()) asmgen.popFAC1()
asmgen.out(" lda #<floats.floats_temp_var | ldy #>floats.floats_temp_var")
}
}

View File

@ -9,6 +9,7 @@ import prog8.codegen.cpu6502.returnsWhatWhere
internal class AssignmentAsmGen(private val program: PtProgram,
private val asmgen: AsmGen6502Internal,
private val anyExprGen: AnyExprAsmGen,
private val allocator: VariableAllocator) {
private val augmentableAsmGen = AugmentableAssignmentAsmGen(program, this, asmgen, allocator)
@ -411,7 +412,7 @@ internal class AssignmentAsmGen(private val program: PtProgram,
}
if(expr.type !in IntegerDatatypes)
return false
return anyExprGen.assignAnyExpressionUsingStack(expr, assign)
if(expr.operator in setOf("&", "|", "^", "and", "or", "xor"))
return optimizedLogicalOrBitwiseExpr(expr, assign.target)
@ -428,7 +429,7 @@ internal class AssignmentAsmGen(private val program: PtProgram,
if(expr.operator=="%")
return optimizedRemainderExpr(expr, assign.target)
return false
return anyExprGen.assignAnyExpressionUsingStack(expr, assign)
}
private fun optimizedRemainderExpr(expr: PtBinaryExpression, target: AsmAssignTarget): Boolean {
@ -1579,6 +1580,8 @@ internal class AssignmentAsmGen(private val program: PtProgram,
}
if(targetDt==DataType.FLOAT && (target.register==RegisterOrPair.FAC1 || target.register==RegisterOrPair.FAC2)) {
if(target.register==RegisterOrPair.FAC2)
asmgen.pushFAC1()
when(valueDt) {
DataType.UBYTE -> {
assignExpressionToRegister(value, RegisterOrPair.Y, false)
@ -1599,7 +1602,8 @@ internal class AssignmentAsmGen(private val program: PtProgram,
else -> throw AssemblyError("invalid dt")
}
if(target.register==RegisterOrPair.FAC2) {
asmgen.out(" jsr floats.MOVEF")
asmgen.out(" jsr floats.MOVEF")
asmgen.popFAC1()
}
return
}
@ -2091,8 +2095,9 @@ internal class AssignmentAsmGen(private val program: PtProgram,
}
internal fun assignFAC2float(target: AsmAssignTarget) {
asmgen.out(" jsr floats.MOVFA") // fac2 -> fac1
assignFAC1float(target)
asmgen.out(" jsr floats.MOVFA")
if(target.register != RegisterOrPair.FAC1)
assignFAC1float(target)
}
internal fun assignFAC1float(target: AsmAssignTarget) {
@ -2121,7 +2126,9 @@ internal class AssignmentAsmGen(private val program: PtProgram,
}
TargetStorageKind.MEMORY -> throw AssemblyError("can't assign float to mem byte")
TargetStorageKind.REGISTER -> {
if (target.register!! != RegisterOrPair.FAC1)
if(target.register==RegisterOrPair.FAC2)
asmgen.out(" jsr floats.MOVAF")
else if (target.register!! != RegisterOrPair.FAC1)
throw AssemblyError("can't assign Fac1 float to another register")
}
}

View File

@ -11,6 +11,7 @@ import prog8.ast.walk.AstWalker
import prog8.ast.walk.IAstModification
import prog8.code.core.AugmentAssignmentOperators
import prog8.code.core.CompilationOptions
import prog8.code.core.DataType
import prog8.code.target.VMTarget
@ -21,6 +22,9 @@ class BinExprSplitter(private val program: Program, private val options: Compila
if(options.compTarget.name == VMTarget.NAME)
return noModifications // don't split expressions when targeting the vm codegen, it handles nested expressions well
if(assignment.value.inferType(program) istype DataType.FLOAT)
return noModifications
val binExpr = assignment.value as? BinaryExpression
if (binExpr != null) {

View File

@ -208,7 +208,6 @@ var_fac1_greater_f .proc
.pend
var_fac1_greatereq_f .proc
ldx P8ZP_SCRATCH_REG
; -- is the float in FAC1 >= the variable AY? Result in A. Clobbers X.
jsr FCOMP
cmp #0
@ -221,6 +220,14 @@ var_fac1_greatereq_f .proc
rts
.pend
var_fac1_equal_f .proc
; -- are the floats numbers in FAC1 and the variable AY *not* identical? Result in A. Clobbers X.
jsr FCOMP
and #1
eor #1
rts
.pend
var_fac1_notequal_f .proc
; -- are the floats numbers in FAC1 and the variable AY *not* identical? Result in A. Clobbers X.
jsr FCOMP
@ -381,3 +388,61 @@ set_array_float .proc
; -- copies the 5 bytes of the mflt value pointed to by SCRATCH_ZPWORD1,
; into the 5 bytes pointed to by A/Y. Clobbers A,Y.
.pend
pushFAC1 .proc
;-- push floating point in FAC onto the cpu stack
; save return address
pla
sta P8ZP_SCRATCH_W2
pla
sta P8ZP_SCRATCH_W2+1
ldx #<floats.floats_temp_var
ldy #>floats.floats_temp_var
jsr floats.MOVMF
lda floats.floats_temp_var
pha
lda floats.floats_temp_var+1
pha
lda floats.floats_temp_var+2
pha
lda floats.floats_temp_var+3
pha
lda floats.floats_temp_var+4
pha
; re-push return address
lda P8ZP_SCRATCH_W2+1
pha
lda P8ZP_SCRATCH_W2
pha
rts
.pend
popFAC1 .proc
; -- pop floating point value from cpu stack into FAC1
; save return address
pla
sta P8ZP_SCRATCH_W2
pla
sta P8ZP_SCRATCH_W2+1
pla
sta floats.floats_temp_var+4
pla
sta floats.floats_temp_var+3
pla
sta floats.floats_temp_var+2
pla
sta floats.floats_temp_var+1
pla
sta floats.floats_temp_var
lda #<floats.floats_temp_var
ldy #>floats.floats_temp_var
jsr floats.MOVFM
; re-push return address
lda P8ZP_SCRATCH_W2+1
pha
lda P8ZP_SCRATCH_W2
pha
rts
.pend

View File

@ -6,10 +6,10 @@ package prog8.buildversion
const val MAVEN_GROUP = "prog8"
const val MAVEN_NAME = "compiler"
const val VERSION = "9.2-SNAPSHOT"
const val GIT_REVISION = 3974
const val GIT_SHA = "ab8173637a9939352ddb027ecd67f1c42b63b1fc"
const val GIT_DATE = "2023-07-16T09:15:28Z"
const val GIT_REVISION = 3980
const val GIT_SHA = "9f247901d484ca36d047cdcf77f5b905ba772f82"
const val GIT_DATE = "2023-07-16T21:45:04Z"
const val GIT_BRANCH = "remove_evalstack"
const val BUILD_DATE = "2023-07-16T11:04:18Z"
const val BUILD_UNIX_TIME = 1689505458888L
const val BUILD_DATE = "2023-07-17T23:11:43Z"
const val BUILD_UNIX_TIME = 1689635503506L
const val DIRTY = 1

View File

@ -1,8 +1,9 @@
TODO
====
- (branch): fix float expressions codegen, it relied heavily on the evalstack
- (branch): fix float expressions codegen, it relied heavily on the evalstack (mandelbrot example zero division error)
- (branch): improve integer expression codegen even more to support even more cases?
- (branch): fully remove BinExprSplitter???
- IR: instructions that do type conversion (SZ etc, CONCAT, SGN) should put the result in a DIFFERENT register.
- IR: reduce the number of branch instructions (gradually), replace with CMP(I) + status branch instruction

View File

@ -9,6 +9,7 @@ main {
sub start() {
txt.print("calculating mandelbrot fractal...\n\n")
cbm.SETTIM(0,0,0)
ubyte pixelx
ubyte pixely
@ -37,5 +38,10 @@ main {
}
txt.nl()
}
float duration = (cbm.RDTIM16() as float) / 60
txt.print("\nfinished in ")
floats.print_f(duration)
txt.print(" seconds!\n")
}
}

View File

@ -1,26 +1,47 @@
%import textio
%import floats
%zeropage basicsafe
main
{
sub start()
{
uword uw = 54321
ubyte ub = 123
word sw = -12345
byte sb = -123
main {
const uword width = 60
const uword height = 50
const ubyte max_iter = 16
txt.print_uw(~ub as uword) ;132
txt.nl()
txt.print_ub(~uw as ubyte) ;206
txt.nl()
txt.print_uw(~sb as uword) ;122
txt.nl()
txt.print_ub(~sw as ubyte) ;56
txt.nl()
txt.print_w(-sb as word) ;123
txt.nl()
txt.print_b(-sw as byte) ;57
txt.nl()
sub start() {
txt.print("calculating mandelbrot fractal...\n\n")
cbm.SETTIM(0,0,0)
ubyte pixelx
ubyte pixely
for pixely in 0 to height-1 {
float yy = (pixely as float)/0.40/height - 1.3
for pixelx in 0 to width-1 {
float xx = (pixelx as float)/0.32/width - 2.2
float xsquared = 0.0
float ysquared = 0.0
float x = 0.0
float y = 0.0
ubyte iter = 0
while iter<max_iter and xsquared+ysquared<4.0 {
y = x*y*2.0 + yy
x = xsquared - ysquared + xx
xsquared = x*x
ysquared = y*y
iter++
}
txt.color2(1, max_iter-iter)
txt.spc()
}
txt.nl()
}
float duration = (cbm.RDTIM16() as float) / 60
txt.print("\nfinished in ")
floats.print_f(duration)
txt.print(" seconds!\n")
}
}