mirror of
https://github.com/catseye/SixtyPical.git
synced 2025-01-07 12:29:52 +00:00
Merge contexts from both branches of an if
.
This commit is contained in:
parent
e3c257f4b9
commit
56f8407b55
@ -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
|
||||
|
@ -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'
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user