From ff46129c4e0ce25db9b579cec94534d81e909c98 Mon Sep 17 00:00:00 2001 From: Karol Stasiak Date: Tue, 22 Oct 2019 01:39:11 +0200 Subject: [PATCH] #11 Fix allocation of variables in functions with trampolines --- src/main/scala/millfork/env/Environment.scala | 5 + src/main/scala/millfork/node/CallGraph.scala | 6 + .../scala/millfork/test/Issue11Test.scala | 108 ++++++++++++++++++ 3 files changed, 119 insertions(+) create mode 100644 src/test/scala/millfork/test/Issue11Test.scala diff --git a/src/main/scala/millfork/env/Environment.scala b/src/main/scala/millfork/env/Environment.scala index f59ba0e1..33706974 100644 --- a/src/main/scala/millfork/env/Environment.scala +++ b/src/main/scala/millfork/env/Environment.scala @@ -119,6 +119,10 @@ class Environment(val parent: Option[Environment], val prefix: String, val cpuFa if (forZpOnly && !options.platform.hasZeroPage) { return } + if (nf.exists(_.name.endsWith(".trampoline"))) { + return + } + if (log.traceEnabled) log.trace("Allocating variables in " + nf.map(f => "function " + f.name).getOrElse("global scope")) val b = get[Type]("byte") val p = get[Type]("pointer") val params = nf.fold(List[String]()) { f => @@ -146,6 +150,7 @@ class Environment(val parent: Option[Environment], val prefix: String, val cpuFa } else 3 val toAdd = things.values.flatMap { case m: UninitializedMemory if passForAlloc(m.alloc) == pass && nf.isDefined == isLocalVariableName(m.name) && !m.name.endsWith(".addr") && maybeGet[Thing](m.name + ".array").isEmpty => + if (log.traceEnabled) log.trace("Allocating " + m.name) val vertex = if (options.flag(CompilationFlag.VariableOverlap)) { nf.fold[VariableVertex](GlobalVertex) { f => if (m.alloc == VariableAllocationMethod.Static) { diff --git a/src/main/scala/millfork/node/CallGraph.scala b/src/main/scala/millfork/node/CallGraph.scala index ad137dd9..24823fd1 100644 --- a/src/main/scala/millfork/node/CallGraph.scala +++ b/src/main/scala/millfork/node/CallGraph.scala @@ -182,6 +182,12 @@ class StandardCallGraph(program: Program, log: Logger) extends CallGraph(program if (multiaccessibleFunctions(a.function) || multiaccessibleFunctions(b.function)) { return false } + if (a.function + ".trampoline" == b.function) { + return false + } + if (a.function == b.function + ".trampoline") { + return false + } if (callEdges(a.function -> b.function) || callEdges(b.function -> a.function)) { return false } diff --git a/src/test/scala/millfork/test/Issue11Test.scala b/src/test/scala/millfork/test/Issue11Test.scala new file mode 100644 index 00000000..1e1a9102 --- /dev/null +++ b/src/test/scala/millfork/test/Issue11Test.scala @@ -0,0 +1,108 @@ +package millfork.test + +import millfork.Cpu +import millfork.test.emu.{EmuCrossPlatformBenchmarkRun, EmuUnoptimizedCmosRun} +import org.scalatest.{FunSuite, Matchers} + +/** + * @author Karol Stasiak + */ +class Issue11Test extends FunSuite with Matchers { + + test("Test issue 11") { + val src = + """ + |import c64_basic + |import stdio + | + |struct Box { + | byte x, + | byte y, + | byte width, + | byte height + |} + | + |struct Phys_Obj { + | Box pos, + | byte xfrac, + | byte yfrac, + | sbyte xvel, + | sbyte yvel, + | byte xaccel, + | byte yaccel, + | byte xspeed, + | bool on_ground, + | bool jumping, + | bool can_jump + |} + | + |Phys_Obj player1 + |byte output @$c000 + | + |inline pointer get_metatile_column(word metatiles_column) { + | //This is the function where the original player pointer + | //gets corrupted. + | return pointer(metatiles_column) + |} + | + |byte get_tile(byte column, byte row) { + | byte tile + | pointer metatiles_column + | + | metatiles_column = get_metatile_column($0000) + | tile = $00 + | return tile + |} + | + |void check_background_collis(byte screenx, byte screeny, byte spritewidth, byte spriteheight) { + | byte new_column1 + | byte new_column2 + | byte new_row1 + | byte new_row2 + | byte i + | byte limit + | byte current_tile + | + | current_tile = get_tile(0,0) + |} + | + |void check_player_collis_and_update_player_loc(pointer.Phys_Obj player) { + | sbyte temp_vel + | byte old_playerx + | byte old_playerx_frac + | byte old_playery + | byte old_playery_frac + | + | old_playerx = player1.pos.x + | old_playery = player1.pos.y + | old_playerx_frac = player1.xfrac + | old_playery_frac = player1.yfrac + | + | check_background_collis(player1.pos.x, player1.pos.y, player1.pos.width, player1.pos.height) + |} + | + |inline void game_logic(pointer.Phys_Obj player) { + | player->pos.x = 15 + | + | //comment out this function call and the player + | //pointer should still point to the correct location + | check_player_collis_and_update_player_loc(player) + | //after the function call, player points to garbage + | + | //expected val: 15 + | //if player no longer points to player1: garbage + | output = player->pos.x + |} + | + |void main() { + | pointer.Phys_Obj player + | player = pointer.Phys_Obj(player1.addr) + | game_logic(player) + |} + |""".stripMargin + + EmuCrossPlatformBenchmarkRun(Cpu.Mos)(src) { m => + m.readByte(0xc000) should equal(15) + } + } +}