1
0
mirror of https://github.com/catseye/SixtyPical.git synced 2024-06-06 15:29:30 +00:00

Merge contexts from both branches of an if.

This commit is contained in:
Cat's Eye Technologies 2014-04-04 18:27:51 +01:00
parent e3c257f4b9
commit 56f8407b55
3 changed files with 107 additions and 9 deletions

View File

@ -83,6 +83,13 @@ Along with routines, you get `if`, `repeat`, and `with` constructs which take
blocks. The `with` construct takes an instruction like `sei` and implicitly
(and unavoidably) inserts the corresponding `cli` at the end of the block.
Abstract interpretation extends to `if` blocks. The two incoming contexts are
merged, and any storage locations poisoned in either context are considered
poisoned in the result context.
(Same should apply for `repeat` and `with` and, really, many other cases
which there just aren't enough test cases for yet.)
For More Information
--------------------
@ -94,11 +101,6 @@ Ideas
-----
These aren't implemented yet:
* Abstract interpretation must extend to `if`, `repeat`, and `with`
blocks. The two incoming contexts must be merged, and any storage
locations updated differently or poisoned in either context, will be
considered poisoned in the result context.
* Inside a routine, an address may be declared with `temporary`. This is like
`static` in C, except the value at that address is not guaranteed to be

View File

@ -127,3 +127,72 @@ Routines can name registers as outputs.
=
= update_score ([A])
= A: UpdatedWith (Immediate 8)
If a location is poisoned in either branch of an `if`, it is poisoned
after the `if`.
| reserve byte score
| routine update_score
| {
| if beq {
| lda #8
| } else {
| ldx #8
| }
| }
| routine main {
| lda #4
| jsr update_score
| sta score
| }
? routine does not preserve 'A'
| reserve byte score
| routine update_score
| {
| if beq {
| ldx #8
| } else {
| lda #8
| }
| }
| routine main {
| lda #4
| jsr update_score
| sta score
| }
? routine does not preserve 'A'
| reserve byte score
| routine update_score
| {
| lda #4
| sta score
| }
| routine main {
| lda #4
| if beq {
| jsr update_score
| } else {
| ldx #3
| }
| sta score
| }
? routine does not preserve 'A'
| reserve byte score
| routine update_score
| {
| lda #4
| sta score
| }
| routine main {
| lda #4
| if beq {
| ldx #3
| } else {
| jsr update_score
| }
| sta score
| }
? routine does not preserve 'A'

View File

@ -73,10 +73,11 @@ analyzeProgram program@(Program decls routines) =
-- TODO: mark Carry bit as "touched" here
routCtx
checkInstr (IF _ branch b1 b2) progCtx routCtx =
-- TODO: oooh, this one's gonna be fun
--checkBlock b1 progCtx routCtx
--checkBlock b2 progCtx routCtx
routCtx
let
routCtx1 = checkBlock b1 progCtx routCtx
routCtx2 = checkBlock b2 progCtx routCtx
in
mergeAlternateRoutCtxs routCtx1 routCtx2
checkInstr (REPEAT _ branch blk) progCtx routCtx =
-- TODO: oooh, this one's gonna be fun too
--checkBlock blk progCtx routCtx
@ -140,3 +141,29 @@ untypedLocation (NamedLocation (Just _) name) =
NamedLocation Nothing name
untypedLocation x = x
--
-- Utility function:
-- Take 2 routine contexts -- one from each branch of an `if` -- and merge
-- them to create a new context for the remainder of the routine.
--
mergeAlternateRoutCtxs routCtx1 routCtx2 =
let
-- go through all the Usages in routCtx2
-- insert any that were updated, into routCtx1
poison location usage2 routCtxAccum =
case Map.lookup location routCtx1 of
Nothing ->
Map.insert location usage2 routCtxAccum
Just usage1 ->
-- it exists in both routCtxs.
-- if it is poisoned in either, it's poisoned here.
-- otherwise, it is OK to differ.
let
newUsage = case (usage1, usage2) of
(PoisonedWith _, _) -> usage1
(_, PoisonedWith _) -> usage2
_ -> usage1 -- or 2. doesn't matter.
in
Map.insert location newUsage routCtxAccum
in
Map.foldrWithKey (poison) routCtx1 routCtx2