mirror of
https://github.com/irmen/prog8.git
synced 2024-12-24 16:29:21 +00:00
fix compiler crash where it used wrong datatype in split assignment
fixes crash for "ubyte bb ;; uword ww ;; bb = not bb or not ww"
This commit is contained in:
parent
4cb383dccb
commit
4937e004b5
@ -15,6 +15,5 @@
|
||||
<orderEntry type="module" module-name="compilerAst" />
|
||||
<orderEntry type="library" name="io.kotest.runner.junit5.jvm" level="project" />
|
||||
<orderEntry type="library" name="io.kotest.assertions.core.jvm" level="project" />
|
||||
<orderEntry type="library" name="io.kotest.property.jvm" level="project" />
|
||||
</component>
|
||||
</module>
|
@ -209,7 +209,7 @@ internal class AsmAssignment(val source: AsmAssignSource,
|
||||
if(target.register !in arrayOf(RegisterOrPair.XY, RegisterOrPair.AX, RegisterOrPair.AY))
|
||||
require(source.datatype != DataType.UNDEFINED) { "must not be placeholder/undefined datatype" }
|
||||
require(memsizer.memorySize(source.datatype) <= memsizer.memorySize(target.datatype)) {
|
||||
"source storage size must be less or equal to target datatype storage size at $position"
|
||||
"source dt size must be less or equal to target dt size at $position"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -103,7 +103,21 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
||||
throw FatalAstException("assignment should be augmentable $binExpr")
|
||||
}
|
||||
|
||||
private fun inplaceModification(target: AsmAssignTarget, operator: String, value: Expression) {
|
||||
private fun inplaceModification(target: AsmAssignTarget, operator: String, origValue: Expression) {
|
||||
|
||||
|
||||
// the asm-gen code can deal with situations where you want to assign a byte into a word.
|
||||
// it will create the most optimized code to do this (so it type-extends for us).
|
||||
// But we can't deal with writing a word into a byte - explicit typeconversion is required
|
||||
val value = if(program.memsizer.memorySize(origValue.inferType(program).getOr(DataType.UNDEFINED)) > program.memsizer.memorySize(target.datatype)) {
|
||||
val typecast = TypecastExpression(origValue, target.datatype, true, origValue.position)
|
||||
typecast.linkParents(origValue.parent)
|
||||
typecast
|
||||
}
|
||||
else {
|
||||
origValue
|
||||
}
|
||||
|
||||
val valueLv = (value as? NumericLiteralValue)?.number
|
||||
val ident = value as? IdentifierReference
|
||||
val memread = value as? DirectMemoryRead
|
||||
|
@ -18,7 +18,6 @@
|
||||
<orderEntry type="module" module-name="compilerInterfaces" />
|
||||
<orderEntry type="module" module-name="codeGeneration" />
|
||||
<orderEntry type="library" name="io.kotest.assertions.core.jvm" level="project" />
|
||||
<orderEntry type="library" name="io.kotest.property.jvm" level="project" />
|
||||
<orderEntry type="library" name="io.kotest.runner.junit5.jvm" level="project" />
|
||||
</component>
|
||||
</module>
|
@ -46,14 +46,16 @@ internal class BeforeAsmGenerationAstChanger(val program: Program, private val o
|
||||
if(binExpr.operator in associativeOperators) {
|
||||
// A = <something-without-A> <associativeoperator> <otherthing-with-A>
|
||||
// use the other part of the expression to split.
|
||||
val assignRight = Assignment(assignment.target, binExpr.right, assignment.position)
|
||||
val (_, right) = binExpr.right.typecastTo(assignment.target.inferType(program).getOr(DataType.UNDEFINED), program, implicit=true)
|
||||
val assignRight = Assignment(assignment.target, right, assignment.position)
|
||||
return listOf(
|
||||
IAstModification.InsertBefore(assignment, assignRight, parent as IStatementContainer),
|
||||
IAstModification.ReplaceNode(binExpr.right, binExpr.left, binExpr),
|
||||
IAstModification.ReplaceNode(binExpr.left, assignment.target.toExpression(), binExpr))
|
||||
}
|
||||
} else {
|
||||
val assignLeft = Assignment(assignment.target, binExpr.left, assignment.position)
|
||||
val (_, left) = binExpr.left.typecastTo(assignment.target.inferType(program).getOr(DataType.UNDEFINED), program, implicit=true)
|
||||
val assignLeft = Assignment(assignment.target, left, assignment.position)
|
||||
return listOf(
|
||||
IAstModification.InsertBefore(assignment, assignLeft, parent as IStatementContainer),
|
||||
IAstModification.ReplaceNode(binExpr.left, assignment.target.toExpression(), binExpr))
|
||||
|
@ -1,5 +1,6 @@
|
||||
package prog8tests
|
||||
|
||||
import io.kotest.assertions.fail
|
||||
import io.kotest.assertions.withClue
|
||||
import io.kotest.core.spec.style.FunSpec
|
||||
import io.kotest.matchers.shouldBe
|
||||
@ -10,13 +11,18 @@ import prog8.ast.Program
|
||||
import prog8.ast.base.DataType
|
||||
import prog8.ast.base.ParentSentinel
|
||||
import prog8.ast.base.Position
|
||||
import prog8.ast.expressions.NumericLiteralValue
|
||||
import prog8.ast.expressions.TypecastExpression
|
||||
import prog8.ast.expressions.*
|
||||
import prog8.ast.statements.*
|
||||
import prog8.compiler.BeforeAsmGenerationAstChanger
|
||||
import prog8.compiler.target.C64Target
|
||||
import prog8.compiler.target.c64.C64MachineDefinition
|
||||
import prog8.compiler.target.cpu6502.codegen.AsmGen
|
||||
import prog8.compilerinterface.*
|
||||
import prog8tests.ast.helpers.outputDir
|
||||
import prog8tests.helpers.DummyFunctions
|
||||
import prog8tests.helpers.DummyMemsizer
|
||||
import prog8tests.helpers.DummyStringEncoder
|
||||
import prog8tests.helpers.ErrorReporterForTests
|
||||
import prog8tests.helpers.assertSuccess
|
||||
import prog8tests.helpers.compileText
|
||||
|
||||
@ -123,4 +129,69 @@ class TestOptimization: FunSpec({
|
||||
(initY2.value as NumericLiteralValue).type shouldBe DataType.UBYTE
|
||||
(initY2.value as NumericLiteralValue).number.toDouble() shouldBe 11.0
|
||||
}
|
||||
|
||||
test("intermediate assignment steps have correct types for codegen phase (BeforeAsmGenerationAstChanger)") {
|
||||
val src = """
|
||||
main {
|
||||
sub start() {
|
||||
ubyte bb
|
||||
uword ww
|
||||
bb = not bb or not ww ; expression combining ubyte and uword
|
||||
}
|
||||
}
|
||||
"""
|
||||
val result = compileText(C64Target, false, src, writeAssembly = false).assertSuccess()
|
||||
|
||||
// bb = (( not bb as uword) or not ww)
|
||||
val bbAssign = result.program.entrypoint.statements.last() as Assignment
|
||||
val expr = bbAssign.value as BinaryExpression
|
||||
expr.operator shouldBe "or"
|
||||
expr.left shouldBe instanceOf<TypecastExpression>() // casted to word
|
||||
expr.right shouldBe instanceOf<PrefixExpression>()
|
||||
expr.left.inferType(result.program).getOrElse { fail("dt") } shouldBe DataType.UWORD
|
||||
expr.right.inferType(result.program).getOrElse { fail("dt") } shouldBe DataType.UWORD
|
||||
expr.inferType(result.program).getOrElse { fail("dt") } shouldBe DataType.UBYTE
|
||||
|
||||
val options = CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.DONTUSE, emptyList(), false, true, C64Target)
|
||||
val changer = BeforeAsmGenerationAstChanger(result.program,
|
||||
options,
|
||||
ErrorReporterForTests()
|
||||
)
|
||||
|
||||
changer.visit(result.program)
|
||||
while(changer.applyModifications()>0) {
|
||||
changer.visit(result.program)
|
||||
}
|
||||
|
||||
// assignment is now split into:
|
||||
// bb = not bb
|
||||
// bb = (bb or not ww)
|
||||
|
||||
val assigns = result.program.entrypoint.statements.filterIsInstance<Assignment>()
|
||||
val bbAssigns = assigns.filter { it.value !is NumericLiteralValue }
|
||||
bbAssigns.size shouldBe 2
|
||||
println(bbAssigns[0])
|
||||
println(bbAssigns[1])
|
||||
|
||||
bbAssigns[0].target.identifier!!.nameInSource shouldBe listOf("bb")
|
||||
bbAssigns[0].value shouldBe instanceOf<PrefixExpression>()
|
||||
(bbAssigns[0].value as PrefixExpression).operator shouldBe "not"
|
||||
(bbAssigns[0].value as PrefixExpression).expression shouldBe IdentifierReference(listOf("bb"), Position.DUMMY)
|
||||
bbAssigns[0].value.inferType(result.program).getOrElse { fail("dt") } shouldBe DataType.UBYTE
|
||||
|
||||
bbAssigns[1].target.identifier!!.nameInSource shouldBe listOf("bb")
|
||||
val bbAssigns1expr = bbAssigns[1].value as BinaryExpression
|
||||
bbAssigns1expr.operator shouldBe "or"
|
||||
bbAssigns1expr.left shouldBe IdentifierReference(listOf("bb"), Position.DUMMY)
|
||||
bbAssigns1expr.right shouldBe instanceOf<PrefixExpression>()
|
||||
(bbAssigns1expr.right as PrefixExpression).operator shouldBe "not"
|
||||
(bbAssigns1expr.right as PrefixExpression).expression shouldBe IdentifierReference(listOf("ww"), Position.DUMMY)
|
||||
bbAssigns1expr.inferType(result.program).getOrElse { fail("dt") } shouldBe DataType.UBYTE
|
||||
|
||||
val zp = C64MachineDefinition.C64Zeropage(options)
|
||||
options.compTarget.machine.zeropage=zp
|
||||
val asmgen = AsmGen(result.program, ErrorReporterForTests(), zp, options, C64Target, outputDir)
|
||||
val asm = asmgen.compileToAssembly()
|
||||
asm.valid shouldBe true
|
||||
}
|
||||
})
|
||||
|
@ -15,7 +15,6 @@
|
||||
<orderEntry type="library" name="antlr.antlr4" level="project" />
|
||||
<orderEntry type="library" name="michael.bull.kotlin.result.jvm" level="project" />
|
||||
<orderEntry type="library" name="io.kotest.assertions.core.jvm" level="project" />
|
||||
<orderEntry type="library" name="io.kotest.property.jvm" level="project" />
|
||||
<orderEntry type="library" name="io.kotest.runner.junit5.jvm" level="project" />
|
||||
</component>
|
||||
</module>
|
@ -62,6 +62,21 @@ sealed class Expression: Node {
|
||||
else -> other==this
|
||||
}
|
||||
}
|
||||
|
||||
fun typecastTo(targetDt: DataType, program: Program, sourceDt: DataType?=null, implicit: Boolean=false): Pair<Boolean, Expression> {
|
||||
require(sourceDt==null || sourceDt!=DataType.UNDEFINED)
|
||||
require(targetDt!=DataType.UNDEFINED)
|
||||
if(this is TypecastExpression) {
|
||||
this.type = targetDt
|
||||
return Pair(false, this)
|
||||
}
|
||||
val exprDt = sourceDt ?: this.inferType(program).getOr(DataType.UNDEFINED)
|
||||
if(exprDt==targetDt)
|
||||
return Pair(false, this)
|
||||
|
||||
val typecast = TypecastExpression(this, targetDt, implicit, this.position)
|
||||
return Pair(true, typecast)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -1,5 +1,6 @@
|
||||
package prog8tests.ast
|
||||
|
||||
import io.kotest.assertions.fail
|
||||
import io.kotest.assertions.throwables.shouldThrow
|
||||
import io.kotest.assertions.withClue
|
||||
import io.kotest.core.spec.style.FunSpec
|
||||
@ -654,4 +655,24 @@ class TestProg8Parser: FunSpec( {
|
||||
correctPrios(orAssignmentExpr, "or")
|
||||
correctPrios(xorAssignmentExpr, "xor")
|
||||
}
|
||||
|
||||
test("inferred type correct for binaryexpression") {
|
||||
val src = SourceCode.Text("""
|
||||
main {
|
||||
ubyte bb
|
||||
uword ww
|
||||
ubyte bb2 = not bb or not ww ; expression combining ubyte and uword
|
||||
}
|
||||
""")
|
||||
val module = parseModule(src)
|
||||
val program = Program("test", DummyFunctions, DummyMemsizer, DummyStringEncoder)
|
||||
module.linkIntoProgram(program)
|
||||
val bb2 = (module.statements.single() as Block).statements[2] as VarDecl
|
||||
val expr = bb2.value as BinaryExpression
|
||||
println(expr)
|
||||
expr.operator shouldBe "or"
|
||||
expr.left.inferType(program).getOrElse { fail("dt") } shouldBe DataType.UBYTE
|
||||
expr.right.inferType(program).getOrElse { fail("dt") } shouldBe DataType.UWORD
|
||||
expr.inferType(program).getOrElse { fail("dt") } shouldBe DataType.UBYTE
|
||||
}
|
||||
})
|
||||
|
@ -5,11 +5,7 @@ For next compiler release (7.3)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
- if-statement expression simplification sometimes increases code size (Petaxian) FIX THIS!
|
||||
- add expression simplification to while and until loops as well.
|
||||
- fix crash about storage size mismatch
|
||||
ubyte bb
|
||||
uword ww
|
||||
bb = not bb or not ww ; CRASHES
|
||||
|
||||
- let typecasting code use expression.typecastTo()
|
||||
|
||||
|
||||
Blocked by Commander-x16 v39 release
|
||||
|
@ -6,11 +6,12 @@ main {
|
||||
|
||||
sub start() {
|
||||
|
||||
ubyte unused ; TODO FIX : why is this not removed as an unused variable?
|
||||
ubyte unused ; TODO FIX : why is this not removed as an unused variable?
|
||||
ubyte @shared unused2
|
||||
|
||||
ubyte bb
|
||||
uword ww
|
||||
bb = not bb or not ww ; TODO FIX COMPILER CRASH (STORAGE SIZE)
|
||||
ww = not bb or not ww ; TODO WHY DOES THIS USE STACK EVAL
|
||||
|
||||
; if not iteration_in_progress or not num_bytes
|
||||
; return
|
||||
|
Loading…
Reference in New Issue
Block a user