Skip to content

Commit

Permalink
retain type of consts better to avoid precision loss
Browse files Browse the repository at this point in the history
this also fixed a difference in const calculation where the result could differ if you were using optimzations or not.
  • Loading branch information
irmen committed Sep 14, 2024
1 parent aba1a73 commit 0d3ad80
Show file tree
Hide file tree
Showing 11 changed files with 129 additions and 90 deletions.
12 changes: 6 additions & 6 deletions codeOptimizers/src/prog8/optimizer/ConstExprEvaluator.kt
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ class ConstExprEvaluator {
// DataType.WORD -> result = result.toShort().toInt()
// else -> { /* keep as it is */ }
// }
return NumericLiteral.optimalNumeric(result.toDouble(), left.position)
return NumericLiteral.optimalNumeric(left.type, null, result.toDouble(), left.position)
}

private fun bitwiseXor(left: NumericLiteral, right: NumericLiteral): NumericLiteral {
Expand Down Expand Up @@ -133,7 +133,7 @@ class ConstExprEvaluator {
val error = "cannot add $left and $right"
return when (left.type) {
in IntegerDatatypes -> when (right.type) {
in IntegerDatatypes -> NumericLiteral.optimalInteger(left.number.toInt() + right.number.toInt(), left.position)
in IntegerDatatypes -> NumericLiteral.optimalInteger(left.type, right.type, left.number.toInt() + right.number.toInt(), left.position)
DataType.FLOAT -> NumericLiteral(DataType.FLOAT, left.number.toInt() + right.number, left.position)
else -> throw ExpressionError(error, left.position)
}
Expand All @@ -150,7 +150,7 @@ class ConstExprEvaluator {
val error = "cannot subtract $left and $right"
return when (left.type) {
in IntegerDatatypes -> when (right.type) {
in IntegerDatatypes -> NumericLiteral.optimalInteger(left.number.toInt() - right.number.toInt(), left.position)
in IntegerDatatypes -> NumericLiteral.optimalInteger(left.type, right.type, left.number.toInt() - right.number.toInt(), left.position)
DataType.FLOAT -> NumericLiteral(DataType.FLOAT, left.number.toInt() - right.number, left.position)
else -> throw ExpressionError(error, left.position)
}
Expand All @@ -167,7 +167,7 @@ class ConstExprEvaluator {
val error = "cannot multiply ${left.type} and ${right.type}"
return when (left.type) {
in IntegerDatatypes -> when (right.type) {
in IntegerDatatypes -> NumericLiteral.optimalInteger(left.number.toInt() * right.number.toInt(), left.position)
in IntegerDatatypes -> NumericLiteral.optimalInteger(left.type, right.type, left.number.toInt() * right.number.toInt(), left.position)
DataType.FLOAT -> NumericLiteral(DataType.FLOAT, left.number.toInt() * right.number, left.position)
else -> throw ExpressionError(error, left.position)
}
Expand All @@ -190,7 +190,7 @@ class ConstExprEvaluator {
in IntegerDatatypes -> {
if(right.number.toInt()==0) divideByZeroError(right.position)
val result: Int = left.number.toInt() / right.number.toInt()
NumericLiteral.optimalInteger(result, left.position)
NumericLiteral.optimalInteger(left.type, right.type, result, left.position)
}
DataType.FLOAT -> {
if(right.number==0.0) divideByZeroError(right.position)
Expand Down Expand Up @@ -219,7 +219,7 @@ class ConstExprEvaluator {
in IntegerDatatypes -> when (right.type) {
in IntegerDatatypes -> {
if(right.number.toInt()==0) divideByZeroError(right.position)
NumericLiteral.optimalNumeric(left.number.toInt().toDouble() % right.number.toInt().toDouble(), left.position)
NumericLiteral.optimalNumeric(left.type, right.type, left.number.toInt().toDouble() % right.number.toInt().toDouble(), left.position)
}
DataType.FLOAT -> {
if(right.number ==0.0) divideByZeroError(right.position)
Expand Down
23 changes: 16 additions & 7 deletions codeOptimizers/src/prog8/optimizer/ConstantFoldingOptimizer.kt
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,14 @@ class ConstantFoldingOptimizer(private val program: Program, private val errors:
}

override fun after(numLiteral: NumericLiteral, parent: Node): Iterable<IAstModification> {

if(numLiteral.type==DataType.LONG) {
// see if LONG values may be reduced to something smaller
val smaller = NumericLiteral.optimalInteger(numLiteral.number.toInt(), numLiteral.position)
if(smaller.type!=DataType.LONG)
return listOf(IAstModification.ReplaceNode(numLiteral, smaller, parent))
}

if(parent is Assignment) {
val iDt = parent.target.inferType(program)
if(iDt.isKnown && !iDt.isBool && !iDt.istype(numLiteral.type)) {
Expand Down Expand Up @@ -164,13 +172,13 @@ class ConstantFoldingOptimizer(private val program: Program, private val errors:
if(leftconst==null && rightconst!=null && rightconst.number<0.0) {
if (expr.operator == "-") {
// X - -1 ---> X + 1
val posNumber = NumericLiteral.optimalNumeric(-rightconst.number, rightconst.position)
val posNumber = NumericLiteral.optimalNumeric(rightconst.type, null, -rightconst.number, rightconst.position)
val plusExpr = BinaryExpression(expr.left, "+", posNumber, expr.position)
return listOf(IAstModification.ReplaceNode(expr, plusExpr, parent))
}
else if (expr.operator == "+") {
// X + -1 ---> X - 1
val posNumber = NumericLiteral.optimalNumeric(-rightconst.number, rightconst.position)
val posNumber = NumericLiteral.optimalNumeric(rightconst.type, null, -rightconst.number, rightconst.position)
val plusExpr = BinaryExpression(expr.left, "-", posNumber, expr.position)
return listOf(IAstModification.ReplaceNode(expr, plusExpr, parent))
}
Expand Down Expand Up @@ -384,12 +392,13 @@ class ConstantFoldingOptimizer(private val program: Program, private val errors:
val numval = decl.value as? NumericLiteral
if(decl.type== VarDeclType.CONST && numval!=null) {
val valueDt = numval.inferType(program)
if(valueDt istype DataType.LONG) {
return noModifications // this is handled in the numericalvalue case
}
if(valueDt isnot decl.datatype) {
if(decl.datatype!=DataType.BOOL || valueDt.isnot(DataType.UBYTE)) {
val cast = numval.cast(decl.datatype, true)
if (cast.isValid)
return listOf(IAstModification.ReplaceNode(numval, cast.valueOrZero(), decl))
}
val cast = numval.cast(decl.datatype, true)
if (cast.isValid)
return listOf(IAstModification.ReplaceNode(numval, cast.valueOrZero(), decl))
}
}
return noModifications
Expand Down
3 changes: 3 additions & 0 deletions compiler/res/prog8lib/cx16/gfx2.p8
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@
;
; NOTE: the bitmap screen data is positioned in vram at $0:0000
;
; NOTE: In the future, these routines might be split out to separate modules, 1 for each screen mode,
; so they can be optimized a lot better. There's already a "gfx_lores" module with a few routines for lores 256C mode.
;
; SCREEN MODE LIST:
; mode 0 = reset back to default text mode
; mode 1 = bitmap 320 x 240 x 256c (8 bpp)
Expand Down
2 changes: 1 addition & 1 deletion compiler/res/prog8lib/virtual/math.p8
Original file line number Diff line number Diff line change
Expand Up @@ -233,7 +233,7 @@ math {
sub direction(ubyte x1, ubyte y1, ubyte x2, ubyte y2) -> ubyte {
; From a pair of positive coordinates, calculate discrete direction between 0 and 23 into A.
; This adjusts the atan() result so that the direction N is centered on the angle=N instead of having it as a boundary
ubyte angle = atan2(x1, y1, x2, y2) - 256/48
ubyte angle = atan2(x1, y1, x2, y2) - (256/48 as ubyte)
return 23-lsb(mkword(angle,0) / 2730)
}

Expand Down
75 changes: 59 additions & 16 deletions compiler/src/prog8/compiler/astprocessing/TypecastsAdder.kt
Original file line number Diff line number Diff line change
Expand Up @@ -190,21 +190,7 @@ class TypecastsAdder(val program: Program, val options: CompilationOptions, val
}
val cvalue = assignment.value.constValue(program)
if(cvalue!=null) {
val number = cvalue.number
// more complex comparisons if the type is different, but the constant value is compatible
if (valuetype == DataType.BYTE && targettype == DataType.UBYTE) {
if(number>0)
return castLiteral(cvalue)
} else if (valuetype == DataType.WORD && targettype == DataType.UWORD) {
if(number>0)
return castLiteral(cvalue)
} else if (valuetype == DataType.UBYTE && targettype == DataType.BYTE) {
if(number<0x80)
return castLiteral(cvalue)
} else if (valuetype == DataType.UWORD && targettype == DataType.WORD) {
if(number<0x8000)
return castLiteral(cvalue)
}
return castLiteral(cvalue)
}
}
}
Expand Down Expand Up @@ -357,15 +343,72 @@ class TypecastsAdder(val program: Program, val options: CompilationOptions, val
}

override fun after(range: RangeExpression, parent: Node): Iterable<IAstModification> {
val fromConst = range.from.constValue(program)
val toConst = range.to.constValue(program)

if(fromConst!=null) {
val smaller = NumericLiteral.optimalInteger(fromConst.number.toInt(), fromConst.position)
if(fromConst.type.largerThan(smaller.type)) {
val toType = range.to.inferType(program)
if(toType isnot smaller.type) {
if(toConst!=null) {
// can we make the to value into the same smaller type?
val smallerTo = NumericLiteral.optimalInteger(toConst.number.toInt(), toConst.position)
if(smaller.type==smallerTo.type) {
val newRange = RangeExpression(smaller, smallerTo, range.step, range.position)
return listOf(IAstModification.ReplaceNode(range, newRange, parent))
}
}
} else {
val newRange = RangeExpression(smaller, range.to, range.step, range.position)
return listOf(IAstModification.ReplaceNode(range, newRange, parent))
}
}
}
if(toConst!=null) {
val smaller = NumericLiteral.optimalInteger(toConst.number.toInt(), toConst.position)
if(toConst.type.largerThan(smaller.type)) {
val fromType = range.from.inferType(program)
if(fromType isnot smaller.type) {
if(fromConst!=null) {
// can we make the from value into the same smaller type?
val smallerFrom = NumericLiteral.optimalInteger(fromConst.number.toInt(), fromConst.position)
if(smaller.type==smallerFrom.type) {
val newRange = RangeExpression(smallerFrom, smaller, range.step, range.position)
return listOf(IAstModification.ReplaceNode(range, newRange, parent))
}
}
} else {
val newRange = RangeExpression(range.from, smaller, range.step, range.position)
return listOf(IAstModification.ReplaceNode(range, newRange, parent))
}
}
}

val modifications = mutableListOf<IAstModification>()
val fromDt = range.from.inferType(program).getOr(DataType.UNDEFINED)
val toDt = range.to.inferType(program).getOr(DataType.UNDEFINED)
val modifications = mutableListOf<IAstModification>()
val (commonDt, toChange) = BinaryExpression.commonDatatype(fromDt, toDt, range.from, range.to)
if(toChange!=null)
addTypecastOrCastedValueModification(modifications, toChange, commonDt, range)

return modifications
}

override fun after(arrayIndexedExpression: ArrayIndexedExpression, parent: Node): Iterable<IAstModification> {
val constIdx = arrayIndexedExpression.indexer.constIndex()
if(constIdx!=null) {
val smaller = NumericLiteral.optimalInteger(constIdx, arrayIndexedExpression.indexer.position)
val idxDt = arrayIndexedExpression.indexer.indexExpr.inferType(program).getOr(DataType.UNDEFINED)
if(idxDt.largerThan(smaller.type)) {
val newIdx = ArrayIndex(smaller, smaller.position)
val newIndexer = ArrayIndexedExpression(arrayIndexedExpression.arrayvar, newIdx, arrayIndexedExpression.position)
return listOf(IAstModification.ReplaceNode(arrayIndexedExpression, newIndexer, parent))
}
}
return noModifications
}

private fun addTypecastOrCastedValueModification(
modifications: MutableList<IAstModification>,
expressionToCast: Expression,
Expand Down
29 changes: 28 additions & 1 deletion compilerAst/src/prog8/ast/expressions/AstExpressions.kt
Original file line number Diff line number Diff line change
Expand Up @@ -498,6 +498,24 @@ class NumericLiteral(val type: DataType, // only numerical types allowed
fun fromBoolean(bool: Boolean, position: Position) =
NumericLiteral(DataType.BOOL, if(bool) 1.0 else 0.0, position)

fun optimalNumeric(origType1: DataType, origType2: DataType?, value: Number, position: Position) : NumericLiteral {
val optimal = optimalNumeric(value, position)
val largestOrig = if(origType2==null) origType1 else if(origType1.largerThan(origType2)) origType1 else origType2
if(largestOrig.largerThan(optimal.type))
return NumericLiteral(largestOrig, optimal.number, position)
else
return optimal
}

fun optimalInteger(origType1: DataType, origType2: DataType?, value: Int, position: Position): NumericLiteral {
val optimal = optimalInteger(value, position)
val largestOrig = if(origType2==null) origType1 else if(origType1.largerThan(origType2)) origType1 else origType2
if(largestOrig.largerThan(optimal.type))
return NumericLiteral(largestOrig, optimal.number, position)
else
return optimal
}

fun optimalNumeric(value: Number, position: Position): NumericLiteral {
val digits = floor(value.toDouble()) - value.toDouble()
return if(value is Double && digits!=0.0) {
Expand Down Expand Up @@ -1090,7 +1108,16 @@ data class IdentifierReference(val nameInSource: List<String>, override val posi
} else if(vardecl.type!= VarDeclType.CONST) {
return null
}
return vardecl.value?.constValue(program)

// the value of a variable can (temporarily) be a different type as the vardecl itself.
// don't return the value if the types don't match yet!
val value = vardecl.value?.constValue(program)
if(value==null || value.type==vardecl.datatype)
return value
val optimal = NumericLiteral.optimalNumeric(value.number, value.position)
if(optimal.type==vardecl.datatype)
return optimal
return null
}

override fun toString(): String {
Expand Down
6 changes: 4 additions & 2 deletions docs/source/todo.rst
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
TODO
====

wrong answer if cast as uword is not done in:
const uword W=320; uword x1 = ((WIDTH-256)/2 as uword) + math.sin8u(i)
Should give type error, but seems to make num a word !?:
const uword screenwidth = txt.DEFAULT_WIDTH
const ubyte num = (screenwidth-1) / 2


callgraph issue? : if a sub contains another sub and it calls that, the outer sub is never removed even if it doesn't get called?

Expand Down
4 changes: 2 additions & 2 deletions examples/cx16/diskspeed.p8
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,9 @@ main {
}

if fastserial
diskio.fastmode(3)
void diskio.fastmode(3)
else
diskio.fastmode(0)
void diskio.fastmode(0)

test_save()
test_save_blocks()
Expand Down
4 changes: 2 additions & 2 deletions examples/cx16/mandelbrot.p8
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
%zeropage basicsafe

main {
const uword width = 60
const uword height = 50
const ubyte width = 60
const ubyte height = 50
const ubyte max_iter = 16

sub start() {
Expand Down
4 changes: 2 additions & 2 deletions examples/mandelbrot.p8
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@
; Note: this program can be compiled for multiple target systems.

main {
const uword width = 30
const uword height = 20
const ubyte width = 30
const ubyte height = 20
const ubyte max_iter = 16

sub start() {
Expand Down
57 changes: 6 additions & 51 deletions examples/test.p8
Original file line number Diff line number Diff line change
@@ -1,61 +1,16 @@
%import textio
%import gfx_lores
%import emudbg
%zeropage basicsafe
%option no_sysinit

main {
const uword WIDTH = 320
const ubyte HEIGHT = 240

sub start() {
uword clo, chi

void cx16.set_screen_mode(128)

word x1, y1, x2, y2
ubyte i
ubyte color = 2

sys.set_irqd()
emudbg.reset_cpu_cycles()
for i in 0 to 254 step 4 {
x1 = ((WIDTH-256)/2 as word) + math.sin8u(i) as word
y1 = (HEIGHT-128)/2 + math.cos8u(i)/2
x2 = ((WIDTH-64)/2 as word) + math.sin8u(i)/4 as word
y2 = (HEIGHT-64)/2 + math.cos8u(i)/4
cx16.GRAPH_set_colors(color, 0, 1)
cx16.GRAPH_draw_line(x1 as uword, y1 as uword, x2 as uword, y2 as uword)
}
clo, chi = emudbg.cpu_cycles()
sys.clear_irqd()

txt.print_uwhex(chi, true)
txt.print_uwhex(clo, false)
uword @shared large = (320*240/8/8)
const uword WIDTH=320
uword x1 = ((WIDTH-256)/2 as uword) + 200
txt.print_uw(x1)
txt.nl()

sys.wait(50)
cx16.GRAPH_clear()
sys.wait(50)

sys.set_irqd()
emudbg.reset_cpu_cycles()
color = 5
for i in 0 to 254 step 4 {
x1 = ((WIDTH-256)/2 as word) + math.sin8u(i) as word
y1 = (HEIGHT-128)/2 + math.cos8u(i)/2
x2 = ((WIDTH-64)/2 as word) + math.sin8u(i)/4 as word
y2 = (HEIGHT-64)/2 + math.cos8u(i)/4
gfx_lores.line(x1 as uword, y1 as ubyte, x2 as uword, y2 as ubyte, color)
}
clo, chi = emudbg.cpu_cycles()
sys.clear_irqd()

txt.print_uwhex(chi, true)
txt.print_uwhex(clo, false)
txt.nl()


x1 = ((WIDTH-256)/2) + 200
txt.print_uw(x1)
}
}

0 comments on commit 0d3ad80

Please sign in to comment.