consolidate byte comparison codegen

This commit is contained in:
Irmen de Jong 2024-03-01 19:45:16 +01:00
parent 82e0877e64
commit 047decd552
3 changed files with 101 additions and 522 deletions

View File

@ -429,35 +429,101 @@ internal class AssignmentAsmGen(private val program: PtProgram,
}
}
if(expr.left.type in ByteDatatypes && expr.right.type in ByteDatatypes) {
if(assignOptimizedComparisonBytes(expr, assign))
return true
} else if(expr.left.type in WordDatatypes && expr.right.type in WordDatatypes) {
if(assignOptimizedComparisonWords(expr, assign))
if(expr.left.type in WordDatatypes && expr.right.type in WordDatatypes) {
if(assignOptimizedComparisonWords(expr, assign)) // TODO remove this!
return true
}
val origTarget = assign.target.origAstTarget
if(origTarget!=null) {
assignConstantByte(assign.target, 0)
val assignTrue = PtNodeGroup()
val assignment = PtAssignment(assign.position)
assignment.add(origTarget)
assignment.add(PtNumber.fromBoolean(true, assign.position))
assignTrue.add(assignment)
val assignFalse = PtNodeGroup()
// b = v > 99 --> b=false , if v>99 b=true
val targetReg=assign.target.register
if(targetReg!=null) {
// a register as target should be handled slightly differently to avoid overwriting the value
val ifPart = PtNodeGroup()
val elsePart = PtNodeGroup()
val reg = when(targetReg) {
RegisterOrPair.A -> "a"
RegisterOrPair.X -> "x"
RegisterOrPair.Y -> "y"
else -> TODO("comparison to word register")
}
val assignTrue = PtInlineAssembly(" ld${reg} #1", false, assign.target.position)
val assignFalse = PtInlineAssembly(" ld${reg} #0", false, assign.target.position)
ifPart.add(assignTrue)
elsePart.add(assignFalse)
val ifelse = PtIfElse(assign.position)
val exprClone = PtBinaryExpression(expr.operator, expr.type, expr.position)
expr.children.forEach { exprClone.children.add(it) } // doesn't seem to need a deep clone
ifelse.add(exprClone)
ifelse.add(assignTrue)
ifelse.add(assignFalse)
ifelse.add(ifPart)
ifelse.add(elsePart)
ifelse.parent = expr.parent
asmgen.translate(ifelse)
return true
}
return false
val target = assign.target.origAstTarget
assignConstantByte(assign.target, 0)
val ifPart = PtNodeGroup()
val assignTrue: PtNode
if(target!=null) {
// set target to true
assignTrue = PtAssignment(assign.position)
assignTrue.add(target)
assignTrue.add(PtNumber.fromBoolean(true, assign.position))
} else {
require(assign.target.datatype in ByteDatatypes)
when(assign.target.kind) {
TargetStorageKind.VARIABLE -> {
if(assign.target.datatype in WordDatatypes) {
assignTrue = if(asmgen.isTargetCpu(CpuType.CPU65c02)) {
PtInlineAssembly("""
lda #1
sta ${assign.target.asmVarname}
stz ${assign.target.asmVarname}+1""", false, assign.target.position)
} else {
PtInlineAssembly("""
lda #1
sta ${assign.target.asmVarname}
lda #0
sta ${assign.target.asmVarname}+1""", false, assign.target.position)
}
} else {
assignTrue = PtInlineAssembly(" lda #1\n sta ${assign.target.asmVarname}", false, assign.target.position)
}
}
TargetStorageKind.MEMORY -> {
val tgt = PtAssignTarget(assign.target.position)
val targetmem = assign.target.memory!!
val mem = PtMemoryByte(targetmem.position)
mem.add(targetmem.address)
tgt.add(mem)
assignTrue = PtAssignment(assign.position)
assignTrue.add(tgt)
assignTrue.add(PtNumber.fromBoolean(true, assign.position))
}
TargetStorageKind.ARRAY -> {
val tgt = PtAssignTarget(assign.target.position)
val targetarray = assign.target.array!!
val array = PtArrayIndexer(assign.target.datatype, targetarray.position)
array.add(targetarray.variable)
array.add(targetarray.index)
tgt.add(array)
assignTrue = PtAssignment(assign.position)
assignTrue.add(tgt)
assignTrue.add(PtNumber.fromBoolean(true, assign.position))
}
TargetStorageKind.REGISTER -> { /* handled earlier */ return true }
}
}
ifPart.add(assignTrue)
val ifelse = PtIfElse(assign.position)
val exprClone = PtBinaryExpression(expr.operator, expr.type, expr.position)
expr.children.forEach { exprClone.children.add(it) } // doesn't seem to need a deep clone
ifelse.add(exprClone)
ifelse.add(ifPart)
ifelse.add(PtNodeGroup())
ifelse.parent = expr.parent
asmgen.translate(ifelse)
return true
}
private fun directIntoY(expr: PtExpression): Boolean {
@ -1140,386 +1206,6 @@ internal class AssignmentAsmGen(private val program: PtProgram,
return false
}
private fun assignOptimizedComparisonBytes(expr: PtBinaryExpression, assign: AsmAssignment): Boolean {
val signed = expr.left.type == DataType.BYTE || expr.right.type == DataType.BYTE
when(expr.operator) {
"==" -> byteEquals(expr)
"!=" -> byteNotEquals(expr)
"<" -> byteLess(expr, signed)
"<=" -> byteLessEquals(expr, signed)
">" -> byteGreater(expr, signed)
">=" -> byteGreaterEquals(expr, signed)
else -> return false
}
assignRegisterByte(assign.target, CpuRegister.A, false, true)
return true
}
private fun byteEquals(expr: PtBinaryExpression) {
when (expr.right) {
is PtNumber -> {
val number = (expr.right as PtNumber).number.toInt()
asmgen.assignExpressionToRegister(expr.left, RegisterOrPair.A)
asmgen.out("""
cmp #$number
beq +
lda #0
beq ++
+ lda #1
+""")
}
is PtIdentifier -> {
val varname = (expr.right as PtIdentifier).name
asmgen.assignExpressionToRegister(expr.left, RegisterOrPair.A)
asmgen.out("""
cmp $varname
beq +
lda #0
beq ++
+ lda #1
+""")
}
else -> {
asmgen.assignByteOperandsToAAndVar(expr.right, expr.left, "P8ZP_SCRATCH_B1")
asmgen.out("""
cmp P8ZP_SCRATCH_B1
beq +
lda #0
beq ++
+ lda #1
+""")
}
}
}
private fun byteNotEquals(expr: PtBinaryExpression) {
when(expr.right) {
is PtNumber -> {
val number = (expr.right as PtNumber).number.toInt()
asmgen.assignExpressionToRegister(expr.left, RegisterOrPair.A)
asmgen.out("""
cmp #$number
bne +
lda #0
beq ++
+ lda #1
+""")
}
is PtIdentifier -> {
val varname = (expr.right as PtIdentifier).name
asmgen.assignExpressionToRegister(expr.left, RegisterOrPair.A)
asmgen.out("""
cmp $varname
bne +
lda #0
beq ++
+ lda #1
+""")
}
else -> {
asmgen.assignByteOperandsToAAndVar(expr.right, expr.left, "P8ZP_SCRATCH_B1")
asmgen.out("""
cmp P8ZP_SCRATCH_B1
bne +
lda #0
beq ++
+ lda #1
+""")
}
}
}
private fun byteLess(expr: PtBinaryExpression, signed: Boolean) {
// note: this is the inverse of byteGreaterEqual
when(expr.right) {
is PtNumber -> {
val number = (expr.right as PtNumber).number.toInt()
asmgen.assignExpressionToRegister(expr.left, RegisterOrPair.A, signed)
if(signed) {
asmgen.out("""
sec
sbc #$number
bvs +
eor #$80
+ asl a
rol a
and #1
eor #1""")
}
else
asmgen.out("""
cmp #$number
rol a
and #1
eor #1""")
}
is PtIdentifier -> {
val varname = (expr.right as PtIdentifier).name
asmgen.assignExpressionToRegister(expr.left, RegisterOrPair.A, signed)
if(signed) {
asmgen.out("""
sec
sbc $varname
bvs +
eor #$80
+ asl a
rol a
and #1
eor #1""")
}
else
asmgen.out("""
cmp $varname
rol a
and #1
eor #1""")
}
else -> {
// note: left and right operands get reversed here to reduce code size
asmgen.assignByteOperandsToAAndVar(expr.right, expr.left, "P8ZP_SCRATCH_B1")
if(signed)
asmgen.out("""
clc
sbc P8ZP_SCRATCH_B1
bvs +
eor #$80
+ asl a
rol a
and #1""")
else
asmgen.out("""
cmp P8ZP_SCRATCH_B1
bcc +
beq +
lda #1
bne ++
+ lda #0
+""")
}
}
}
private fun byteLessEquals(expr: PtBinaryExpression, signed: Boolean) {
// note: this is the inverse of byteGreater
when(expr.right) {
is PtNumber -> {
val number = (expr.right as PtNumber).number.toInt()
asmgen.assignExpressionToRegister(expr.left, RegisterOrPair.A, signed)
if(signed)
asmgen.out("""
sec
sbc #$number
bvc +
eor #$80
+ bmi +
beq +
lda #0
beq ++
+ lda #1
+""")
else
asmgen.out("""
cmp #$number
bcc +
beq +
lda #0
beq ++
+ lda #1
+""")
}
is PtIdentifier -> {
val varname = (expr.right as PtIdentifier).name
asmgen.assignExpressionToRegister(expr.left, RegisterOrPair.A, signed)
if(signed)
asmgen.out("""
sec
sbc $varname
bvc +
eor #$80
+ bmi +
beq +
lda #0
beq ++
+ lda #1
+""")
else
asmgen.out("""
cmp $varname
bcc +
beq +
lda #0
beq ++
+ lda #1
+""")
}
else -> {
// note: left and right operands get reversed here to reduce code size
asmgen.assignByteOperandsToAAndVar(expr.right, expr.left, "P8ZP_SCRATCH_B1")
if(signed)
asmgen.out("""
sec
sbc P8ZP_SCRATCH_B1
bvc +
eor #$80
+ bmi +
lda #1
bne ++
+ lda #0
+""")
else
asmgen.out("""
cmp P8ZP_SCRATCH_B1
lda #0
rol a""")
}
}
}
private fun byteGreater(expr: PtBinaryExpression, signed: Boolean) {
// note: this is the inverse of byteLessEqual
when(expr.right) {
is PtNumber -> {
val number = (expr.right as PtNumber).number.toInt()
asmgen.assignExpressionToRegister(expr.left, RegisterOrPair.A, signed)
if(signed)
asmgen.out("""
sec
sbc #$number
beq +++
bvc +
eor #$80
+ bmi +
lda #1
bne ++
+ lda #0
+""")
else
asmgen.out("""
cmp #$number
bcc +
beq +
lda #1
bne ++
+ lda #0
+""")
}
is PtIdentifier -> {
val varname = (expr.right as PtIdentifier).name
asmgen.assignExpressionToRegister(expr.left, RegisterOrPair.A, signed)
if(signed)
asmgen.out("""
sec
sbc $varname
beq +++
bvc +
eor #$80
+ bmi +
lda #1
bne ++
+ lda #0
+""")
else
asmgen.out("""
cmp $varname
bcc +
beq +
lda #1
bne ++
+ lda #0
+""")
}
else -> {
// note: left and right operands get reversed here to reduce code size
asmgen.assignByteOperandsToAAndVar(expr.right, expr.left, "P8ZP_SCRATCH_B1")
if(signed)
asmgen.out("""
sec
sbc P8ZP_SCRATCH_B1
bvc +
eor #$80
+ bmi +
lda #0
beq ++
+ lda #1
+""")
else
asmgen.out("""
cmp P8ZP_SCRATCH_B1
lda #0
rol a
eor #1""")
}
}
}
private fun byteGreaterEquals(expr: PtBinaryExpression, signed: Boolean) {
// note: this is the inverse of byteLess
when(expr.right) {
is PtNumber -> {
val number = (expr.right as PtNumber).number.toInt()
asmgen.assignExpressionToRegister(expr.left, RegisterOrPair.A, signed)
if(signed) {
asmgen.out("""
sec
sbc #$number
bvs +
eor #$80
+ asl a
rol a
and #1""")
}
else
asmgen.out("""
cmp #$number
rol a
and #1""")
}
is PtIdentifier -> {
val varname = (expr.right as PtIdentifier).name
asmgen.assignExpressionToRegister(expr.left, RegisterOrPair.A, signed)
if(signed) {
asmgen.out("""
sec
sbc $varname
bvs +
eor #$80
+ asl a
rol a
and #1""")
}
else
asmgen.out("""
cmp $varname
rol a
and #1""")
}
else -> {
// note: left and right operands get reversed here to reduce code size
asmgen.assignByteOperandsToAAndVar(expr.right, expr.left, "P8ZP_SCRATCH_B1")
if(signed)
asmgen.out("""
clc
sbc P8ZP_SCRATCH_B1
bvs +
eor #$80
+ asl a
rol a
and #1
eor #1""")
else
asmgen.out("""
cmp P8ZP_SCRATCH_B1
bcc +
beq +
lda #0
beq ++
+ lda #1
+""")
}
}
}
private fun assignOptimizedComparisonWords(expr: PtBinaryExpression, assign: AsmAssignment): Boolean {
val signed = expr.left.type == DataType.WORD || expr.right.type == DataType.WORD

View File

@ -1,6 +1,12 @@
TODO
====
textelite is larger
optimize assignOptimizedComparisonWords for when comparing to simple things like number and identifier. (get rid of it completely by just rewriting everything into an if-else statement?)
optimize optimizedPlusMinExpr for when comparing to simple things like number and identifier.
replace Takes by Http4k in httpCompilerService project. https://github.com/http4k/examples/blob/master/hello-world/README.md
...

View File

@ -1,133 +1,20 @@
%import textio
%zeropage dontuse
%option no_sysinit
%zeropage basicsafe
main {
sub start() {
uword @shared pointer
cx16.r0L = 10
pointer = &label2
if cx16.r0L!=0
goto pointer
label1:
txt.print("fail\n")
return
label2:
txt.print("indirect jump ok\n")
return
ubyte @shared xx = 16
ubyte @shared yy = 20
txt.print_ub(xx>79 or yy > 49)
; if xx>79 or yy > 49 {
; if xx>79 or yy > 49 {
; txt.print("no\n")
; }
; else {
; txt.print("yes\n")
; }
}
}
;%import textio
;%import floats
;%zeropage dontuse
;%option no_sysinit
;
;main {
; sub start() {
; test_bool()
;; test_byte()
;; test_ubyte()
;; test_word()
;; test_uword()
;; test_float()
; }
;
; /*
; tests:
; - if with only a single goto, direct
; - if with only a single indirect goto
; - if without an else block
; - if with both if and else blocks
; carthesian product with:
; - comparison with const zero
; - comparison with const values
; - comparison with variable
; - comparison with array
; - comparison with expression
; */
;
; sub fail(uword test) {
; txt.print("failed ")
; txt.print_uw(test)
; txt.nl()
; }
;
; sub test_bool() {
; bool @shared var1, var2
; uword success = 0
;
; single_goto()
; single_goto_indirect()
; no_else()
; ifelse()
;
; sub single_goto() {
; txt.print("bool single_goto\n")
;test1:
; var1 = false
; if var1
; goto lbl1
; success++
; goto test2
;lbl1:
; fail(1)
;
;test2:
; var1 = true
; if var1
; goto lbl2
; fail(1)
; goto test3
;lbl2: success++
;
;test3:
; if success==2
; txt.print(" ok\n")
; else
; txt.print(" failed\n")
; }
;
; sub single_goto_indirect() {
; uword pointer
; txt.print("bool single_goto_indirect\n")
;test1:
; var1 = false
; pointer = &lbl1
; if var1
; goto pointer
; success++
; goto test2
;lbl1:
; fail(1)
;
;test2:
; var1 = true
; pointer = &lbl2
; if var1
; goto pointer
; fail(1)
; goto test3
;lbl2: success++
;
;test3:
; if success==2
; txt.print(" ok\n")
; else
; txt.print(" failed\n")
; }
;
; sub no_else() {
; }
;
; sub ifelse() {
; }
; }
;
;}