ludic deps: the reach bitsets hold 30 states to a word, not 60 - an int is 32 bits, so states 32 apart shared a bit and a function could reach fewer states than it takes; examples/state/reach_wide.ludic and a deps case hold it; reseeded (bootstrap-cfree fixpoint)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
d7adc28125
commit
2a18d9b51f
6 changed files with 2894 additions and 2814 deletions
9
changes/deps-reach-32-bits.md
Normal file
9
changes/deps-reach-32-bits.md
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
bump: patch
|
||||||
|
type: fix
|
||||||
|
**`ludic deps`: a reach counts every state apart, however the states are numbered.** The reach and
|
||||||
|
write-reach bitsets packed 60 states to a word, but an `int` is 32 bits, so `1 << 45` came back as bit
|
||||||
|
13 and states 32 apart shared a bit: a function taking both counted one, fewer than it takes, and the
|
||||||
|
counts (`widest_reach`, `widest_write_reach`, `--reach`, `--wreach`) rose and fell with how a program's
|
||||||
|
states happened to be numbered. The sets now hold 30 to a word. On Maroon Lake `widest_reach` goes
|
||||||
|
58 -> 76 and `widest_write_reach` 54 -> 64 - the real numbers, which the old count hid.
|
||||||
|
`examples/state/reach_wide.ludic` (40 states, S00 and S32 taken together) holds it.
|
||||||
58
examples/state/reach_wide.ludic
Normal file
58
examples/state/reach_wide.ludic
Normal file
|
|
@ -0,0 +1,58 @@
|
||||||
|
# ludic deps: states numbered past a word's 32 bits are still counted apart. S00 and S32 once shared
|
||||||
|
# a bit (an int is 32 bits and `1 << 32` wrapped), so `pair` reached 1 and `all4` reached 2
|
||||||
|
program ReachWide {
|
||||||
|
state S00 { n: int = 0 }
|
||||||
|
state S01 { n: int = 0 }
|
||||||
|
state S02 { n: int = 0 }
|
||||||
|
state S03 { n: int = 0 }
|
||||||
|
state S04 { n: int = 0 }
|
||||||
|
state S05 { n: int = 0 }
|
||||||
|
state S06 { n: int = 0 }
|
||||||
|
state S07 { n: int = 0 }
|
||||||
|
state S08 { n: int = 0 }
|
||||||
|
state S09 { n: int = 0 }
|
||||||
|
state S10 { n: int = 0 }
|
||||||
|
state S11 { n: int = 0 }
|
||||||
|
state S12 { n: int = 0 }
|
||||||
|
state S13 { n: int = 0 }
|
||||||
|
state S14 { n: int = 0 }
|
||||||
|
state S15 { n: int = 0 }
|
||||||
|
state S16 { n: int = 0 }
|
||||||
|
state S17 { n: int = 0 }
|
||||||
|
state S18 { n: int = 0 }
|
||||||
|
state S19 { n: int = 0 }
|
||||||
|
state S20 { n: int = 0 }
|
||||||
|
state S21 { n: int = 0 }
|
||||||
|
state S22 { n: int = 0 }
|
||||||
|
state S23 { n: int = 0 }
|
||||||
|
state S24 { n: int = 0 }
|
||||||
|
state S25 { n: int = 0 }
|
||||||
|
state S26 { n: int = 0 }
|
||||||
|
state S27 { n: int = 0 }
|
||||||
|
state S28 { n: int = 0 }
|
||||||
|
state S29 { n: int = 0 }
|
||||||
|
state S30 { n: int = 0 }
|
||||||
|
state S31 { n: int = 0 }
|
||||||
|
state S32 { n: int = 0 }
|
||||||
|
state S33 { n: int = 0 }
|
||||||
|
state S34 { n: int = 0 }
|
||||||
|
state S35 { n: int = 0 }
|
||||||
|
state S36 { n: int = 0 }
|
||||||
|
state S37 { n: int = 0 }
|
||||||
|
state S38 { n: int = 0 }
|
||||||
|
state S39 { n: int = 0 }
|
||||||
|
function low(a: mut S00) -> void { a.n += 1 }
|
||||||
|
function high(b: mut S32) -> void { b.n += 1 }
|
||||||
|
function pair(a: mut S00, b: mut S32) -> void {
|
||||||
|
low(a)
|
||||||
|
high(b)
|
||||||
|
}
|
||||||
|
function reads(c: S01, d: S33) -> int { return c.n + d.n }
|
||||||
|
function all4(a: mut S00, b: mut S32, c: S01, d: S33) -> int {
|
||||||
|
pair(a, b)
|
||||||
|
return reads(c, d)
|
||||||
|
}
|
||||||
|
entry (a: mut S00, b: mut S32, c: S01, d: S33) {
|
||||||
|
print(`{all4(a, b, c, d)}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -274,7 +274,7 @@ function deps_alias_write(t: Node) -> void {
|
||||||
var g_dr_name: []pointer = new []pointer # the declarations a name can reach: functions, globals
|
var g_dr_name: []pointer = new []pointer # the declarations a name can reach: functions, globals
|
||||||
var g_dr_node: []Node = new []Node
|
var g_dr_node: []Node = new []Node
|
||||||
var g_dr_refs: [][]int = new [][]int # what each one names
|
var g_dr_refs: [][]int = new [][]int # what each one names
|
||||||
var g_dr_bits: [][]int = new [][]int # the states it reaches, 60 to a word
|
var g_dr_bits: [][]int = new [][]int # the states it reaches, DR_BITS to a word
|
||||||
var g_dr_wbits: [][]int = new [][]int # ... and of them the ones it takes `mut` somewhere down
|
var g_dr_wbits: [][]int = new [][]int # ... and of them the ones it takes `mut` somewhere down
|
||||||
var g_dr_find_k: []pointer = new []pointer
|
var g_dr_find_k: []pointer = new []pointer
|
||||||
var g_dr_rl_name: []pointer = new []pointer # a local holding one entry of a registry ...
|
var g_dr_rl_name: []pointer = new []pointer # a local holding one entry of a registry ...
|
||||||
|
|
@ -294,8 +294,11 @@ function dr_state_ix(t: pointer) -> int {
|
||||||
}
|
}
|
||||||
return -1
|
return -1
|
||||||
}
|
}
|
||||||
function dr_set(bits: []int, s: int) -> void { bits[s / 60] = bits[s / 60] | (1 << (s % 60)) }
|
# an int is 32 bits: `1 << 45` is not bit 45, it came back as bit 13, so states 32 apart shared a bit
|
||||||
function dr_words() -> int { return len(g_state_names) / 60 + 1 }
|
# and a function that took both counted one - the counts rose and fell with how the states were numbered
|
||||||
|
const DR_BITS: int = 30
|
||||||
|
function dr_set(bits: []int, s: int) -> void { bits[s / DR_BITS] = bits[s / DR_BITS] | (1 << (s % DR_BITS)) }
|
||||||
|
function dr_words() -> int { return len(g_state_names) / DR_BITS + 1 }
|
||||||
function dr_index(name: pointer) -> int {
|
function dr_index(name: pointer) -> int {
|
||||||
let n = ck_tab_get(g_dr_find_k, g_dr_find_v, name)
|
let n = ck_tab_get(g_dr_find_k, g_dr_find_v, name)
|
||||||
if n == null { return -1 }
|
if n == null { return -1 }
|
||||||
|
|
|
||||||
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load diff
|
|
@ -698,6 +698,15 @@ function deps_wreach_case() -> void {
|
||||||
if not s_contains(top, " 0 2 2 changes (examples/state/write_reach.ludic:27)") or not s_contains(top, " 0 1 0 asks (examples/state/write_reach.ludic:22)") or s_contains(top, "asks_local") { bad2(lbl, s_trim(top)); return }
|
if not s_contains(top, " 0 2 2 changes (examples/state/write_reach.ludic:27)") or not s_contains(top, " 0 1 0 asks (examples/state/write_reach.ludic:22)") or s_contains(top, "asks_local") { bad2(lbl, s_trim(top)); return }
|
||||||
ok(lbl)
|
ok(lbl)
|
||||||
}
|
}
|
||||||
|
# an int is 32 bits: states numbered 32 apart once shared a bit of the reach, so `pair` (S00 and S32)
|
||||||
|
# reached 1 and could change 1; the counts must not depend on how the states are numbered
|
||||||
|
function deps_reach_wide_case() -> void {
|
||||||
|
let lbl = "ludic deps: a reach counts states past 32 apart (S00 and S32 are two states, not one)"
|
||||||
|
let top = capture(`bin/ludic deps examples/state/reach_wide.ludic --wreach 2 2>&1`)
|
||||||
|
if not s_contains(top, " 4 4 2 all4 (examples/state/reach_wide.ludic:51)\n 2 2 2 pair (examples/state/reach_wide.ludic:46)") { bad2(lbl, s_trim(top)); return }
|
||||||
|
let sum = capture(`bin/ludic deps examples/state/reach_wide.ludic 2>&1`)
|
||||||
|
if s_contains(sum, "widest_reach: 4") and s_contains(sum, "widest_write_reach: 2") { ok(lbl) } else { bad2(lbl, s_trim(sum)) }
|
||||||
|
}
|
||||||
# a program built headless with the toolchain and run: its first line
|
# a program built headless with the toolchain and run: its first line
|
||||||
function headless_case(path: pointer, exp: pointer, label: pointer) -> void {
|
function headless_case(path: pointer, exp: pointer, label: pointer) -> void {
|
||||||
let out = `{tmp_dir()}/h_{flat(path)}`
|
let out = `{tmp_dir()}/h_{flat(path)}`
|
||||||
|
|
@ -1234,6 +1243,7 @@ function cmd_dev_test() -> int {
|
||||||
actions_deps_case()
|
actions_deps_case()
|
||||||
deps_reach_case()
|
deps_reach_case()
|
||||||
deps_wreach_case()
|
deps_wreach_case()
|
||||||
|
deps_reach_wide_case()
|
||||||
feat_case("state/reach", "", "3", "reach.ludic (0.R4: a step list of fn values supplies each step its states)")
|
feat_case("state/reach", "", "3", "reach.ludic (0.R4: a step list of fn values supplies each step its states)")
|
||||||
feat_case("actions/phases", "dd a q", "1 0 2 0 2 1 1 1 1 2 1 2", "phases.ludic (0.R: the frame loop drains the queue after every phase - Input's actions are reduced before Update)")
|
feat_case("actions/phases", "dd a q", "1 0 2 0 2 1 1 1 1 2 1 2", "phases.ludic (0.R: the frame loop drains the queue after every phase - Input's actions are reduced before Update)")
|
||||||
feat_case("actions/runaway", "", "actions: Ping is still being dispatched after 64 rounds of reducers - a reducer dispatches what dispatches it", "runaway.ludic (0.R: a reducer that dispatches what dispatches it is stopped by name)")
|
feat_case("actions/runaway", "", "actions: Ping is still being dispatched after 64 rounds of reducers - a reducer dispatches what dispatches it", "runaway.ludic (0.R: a reducer that dispatches what dispatches it is stopped by name)")
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue