add struct and pointer benchmark to benchmark program (btree, subscore=654, total 7420)

fix nullpointer in array initalizer
This commit is contained in:
Irmen de Jong
2025-09-12 04:46:37 +02:00
parent 5d9fbd2ccc
commit db2f28c4cd
7 changed files with 264 additions and 22 deletions

View File

@@ -0,0 +1,234 @@
; Binary Search Tree.
; It's a simple implementation for test/demonstration purposes of the pointer support;
; no balancing is done and memory is not freed when elements are removed.
%import textio
btree {
sub benchmark(uword max_time) -> uword {
txt.nl()
cbm.SETTIM(0,0,0)
uword score
while cbm.RDTIM16() < max_time {
bench_operations()
txt.chrout('.')
score++
}
txt.nl()
return score
}
sub bench_operations() {
arena.freeall()
btree.root = 0
for cx16.r0 in [321, 719, 194, 550, 187, 203, 520, 562, 221, 676, 97, 852, 273, 326, 589, 606, 275, 794, 63, 716]
btree.add(cx16.r0)
cx16.r0L = btree.size()
btree.process_tree_inorder()
btree.process_tree_preorder()
void btree.contains(203)
void btree.contains(204)
void btree.contains(605)
void btree.contains(606)
btree.remove(9999)
btree.remove(97)
btree.remove(187)
btree.remove(203)
btree.remove(275)
btree.remove(321)
btree.remove(520)
btree.remove(562)
btree.remove(606)
btree.remove(719)
btree.remove(794)
cx16.r0L = btree.size()
btree.process_tree_inorder()
btree.process_tree_preorder()
}
struct Node {
^^Node left
^^Node right
uword value
}
^^Node root = 0
sub add(uword value) {
^^Node node = arena.alloc(sizeof(Node))
node.value = value
node.left = node.right = 0
if root==0
root=node
else {
^^Node parent = root
repeat {
if parent.value >= value {
if parent.left!=0
parent = parent.left
else {
parent.left = node
return
}
} else {
if parent.right!=0
parent = parent.right
else {
parent.right = node
return
}
}
}
}
}
sub contains(uword value) -> bool {
^^Node r = root
while r!=0 {
if r.value==value
return true
if r.value>value
r = r.left
else
r = r.right
}
return false
}
sub size() -> ubyte {
ubyte count
if root!=0
count_node(root)
return count
sub count_node(^^Node r) {
count++
if r.left!=0 {
sys.pushw(r)
count_node(r.left)
r = sys.popw()
}
if r.right!=0 {
sys.pushw(r)
count_node(r.right)
r = sys.popw()
}
}
}
sub remove(uword value) {
; note: we don't deallocate the memory from the node, for simplicity sake
^^Node n = root
^^Node parent = 0
while n!=0 {
if n.value==value {
if n.left==0
replacechild(parent, n, n.right)
else if n.right==0
replacechild(parent, n, n.left)
else {
; Both left & right subtrees are present.
; N = node to delete.
; Find N's successor S. (N's right subtree's minimum element)
; Attach N's left subtree to S.left (S doesn't have a left child)
; Attach N's right subtree to Parent in place of N.
^^Node successor = find_successor(n)
successor.left = n.left
replacechild(parent, n, n.right)
}
return
}
parent = n
if n.value>value
n = n.left
else
n = n.right
}
sub find_successor(^^Node p) -> ^^Node {
^^Node succ = p
p = p.right
while p!=0 {
succ = p
p = p.left
}
return succ
}
sub replacechild(^^Node p, ^^Node child, ^^Node newchild) {
if p.left==child
p.left = newchild
else
p.right = newchild
}
}
sub process_tree_inorder() {
if root!=0
process_tree(root)
sub process_tree(^^Node r) {
if r.left!=0 {
sys.pushw(r)
process_tree(r.left)
r = sys.popw()
}
cx16.r0 = r.value
if r.right!=0 {
sys.pushw(r)
process_tree(r.right)
r = sys.popw()
}
}
}
sub process_tree_preorder() {
if root!=0
process_tree(root,0)
sub process_tree(^^Node r, ubyte depth) {
cx16.r0 = r.value
if r.left!=0 {
sys.pushw(r)
sys.push(depth)
process_tree(r.left, depth+1)
depth = sys.pop()
r = sys.popw()
}
if r.right!=0 {
sys.pushw(r)
sys.push(depth)
process_tree(r.right, depth+1)
depth = sys.pop()
r = sys.popw()
}
}
}
}
arena {
; extremely trivial arena allocator (that never frees)
uword buffer = memory("arena", 2000, 0)
uword next = buffer
sub alloc(ubyte size) -> uword {
defer next += size
return next
}
sub freeall() {
next = buffer
}
}

View File

@@ -16,6 +16,7 @@
%import b_textelite %import b_textelite
%import b_maze %import b_maze
%import b_sprites %import b_sprites
%import b_btree
%zeropage basicsafe %zeropage basicsafe
%option no_sysinit %option no_sysinit
@@ -67,10 +68,6 @@ main {
benchmark_score[benchmark_number] = circles.draw(false, 300) benchmark_score[benchmark_number] = circles.draw(false, 300)
benchmark_number++ benchmark_number++
; announce_benchmark("circles with kernal")
; benchmark_score[benchmark_number] = circles.draw(true, 300)
; benchmark_number++
announce_benchmark("text-elite") announce_benchmark("text-elite")
benchmark_score[benchmark_number] = textelite.bench(120) benchmark_score[benchmark_number] = textelite.bench(120)
benchmark_number++ benchmark_number++
@@ -79,13 +76,17 @@ main {
benchmark_score[benchmark_number] = animsprites.benchmark(300) benchmark_score[benchmark_number] = animsprites.benchmark(300)
benchmark_number++ benchmark_number++
announce_benchmark("btree-struct-pointers")
benchmark_score[benchmark_number] = btree.benchmark(200)
benchmark_number++
benchmark_names[benchmark_number] = 0 benchmark_names[benchmark_number] = 0
benchmark_score[benchmark_number] = 0 benchmark_score[benchmark_number] = 0
cx16.set_screen_mode(3) cx16.set_screen_mode(3)
txt.uppercase() txt.uppercase()
txt.color2(1, 6) txt.color2(1, 6)
uword final_score uword total_score
benchmark_number = 0 benchmark_number = 0
txt.print("\nscore benchmark\n\n") txt.print("\nscore benchmark\n\n")
do { do {
@@ -93,14 +94,14 @@ main {
txt.print_uw(benchmark_score[benchmark_number]) txt.print_uw(benchmark_score[benchmark_number])
txt.column(6) txt.column(6)
txt.print(benchmark_names[benchmark_number]) txt.print(benchmark_names[benchmark_number])
final_score += benchmark_score[benchmark_number] total_score += benchmark_score[benchmark_number]
txt.nl() txt.nl()
benchmark_number++ benchmark_number++
} until benchmark_names[benchmark_number]==0 } until benchmark_names[benchmark_number]==0
txt.print("\n\nfinal score : ") txt.print("\n\ntotal score : ")
txt.print_uw(final_score) txt.print_uw(total_score)
txt.nl() txt.print(" (higher=better)\n")
sub announce_benchmark(str name) { sub announce_benchmark(str name) {
benchmark_names[benchmark_number] = name benchmark_names[benchmark_number] = name

View File

@@ -169,10 +169,12 @@ internal class AstIdentifiersChecker(private val errors: IErrorReporter,
override fun visit(functionCallStatement: FunctionCallStatement) = visitFunctionCall(functionCallStatement) override fun visit(functionCallStatement: FunctionCallStatement) = visitFunctionCall(functionCallStatement)
override fun visit(initializer: StaticStructInitializer) { override fun visit(initializer: StaticStructInitializer) {
val fields = initializer.structname.targetStructDecl()!!.fields val struct = initializer.structname.targetStructDecl()
if(initializer.args.isNotEmpty() && initializer.args.size != fields.size) { if(struct!=null) {
val pos = (if(initializer.args.any()) initializer.args[0] else initializer).position if (initializer.args.isNotEmpty() && initializer.args.size != struct.fields.size) {
invalidNumberOfArgsError(pos, initializer.args.size, fields.map { it.second }, true) val pos = (if (initializer.args.any()) initializer.args[0] else initializer).position
invalidNumberOfArgsError(pos, initializer.args.size, struct.fields.map { it.second }, true)
}
} }
} }

View File

@@ -5,9 +5,15 @@
Porting Guide Porting Guide
************* *************
Here is a guide for porting Prog8 to other compilation targets. Here is a guide for making Prog8 work for other compilation targets.
Answers to the questions below are used to configure the new target and supporting libraries. Answers to the questions below are used to configure the new target and supporting libraries.
**It is not required to change the compiler itself to make it support new compilation targets.**
A few of the most commonly used ones are built-in (such as c64, cx16), but you can use
separate configuration files to create new targets that the compiler can use.
See :ref:`customizable_target` for details about this. You still need to provide most of the
information asked for in this porting guide and code that into the configuration file.
.. note:: .. note::
The assembly code that prog8 generates is not suitable to be put into ROM. (It contains The assembly code that prog8 generates is not suitable to be put into ROM. (It contains
embedded variables, and self-modifying code). embedded variables, and self-modifying code).
@@ -25,7 +31,7 @@ Memory Map
Zeropage Zeropage
======== ========
#. *Absolute requirement:* Provide four times 2 consecutive bytes (i.e. four 16-bit words) in the zeropage that are free to use at all times. #. *Absolute requirement:* Provide four words (16 bit byte pairs) in the zeropage that are free to use at all times.
#. Provide list of any additional free zeropage locations for a normal running system (BASIC + Kernal enabled) #. Provide list of any additional free zeropage locations for a normal running system (BASIC + Kernal enabled)
#. Provide list of any additional free zeropage locations when BASIC is off, but floating point routines should still work #. Provide list of any additional free zeropage locations when BASIC is off, but floating point routines should still work
#. Provide list of any additional free zeropage locations when only the Kernal remains enabled #. Provide list of any additional free zeropage locations when only the Kernal remains enabled
@@ -43,14 +49,13 @@ RAM, ROM, I/O
#. what part(s) of the address space is memory-mapped I/O registers? #. what part(s) of the address space is memory-mapped I/O registers?
#. is there a block of "high ram" available (ram that is not the main ram used to load programs in) that could be used for variables? #. is there a block of "high ram" available (ram that is not the main ram used to load programs in) that could be used for variables?
#. is there a banking system? How does it work (how do you select Ram/Rom banks)? How is the default bank configuration set? #. is there a banking system? How does it work (how do you select Ram/Rom banks)? How is the default bank configuration set?
Note that prog8 itself has no notion of banking, but this knowledge may be required for proper system initialization.
Character encodings Character encodings
------------------- -------------------
#. if not PETSCII or CBM screencodes: provide the primary character encoding table that the system uses (i.e. how is text represented in memory) #. provide the primary character encoding table that the system uses (i.e. how is text represented in memory. For example, PETSCII)
#. provide alternate character encodings (if any) #. provide alternate character encodings (if any)
#. what are the system's standard character screen dimensions? #. what are the system's standard character screen dimensions?
#. is there a screen character matrix directly accessible in Ram? What's it address? Same for color attributes if any. #. is there a screen character matrix directly accessible in RAM? What's it address? Same for color attributes if any.
ROM routines ROM routines
@@ -63,7 +68,7 @@ The more the merrier.
Floating point Floating point
============== ==============
Prog8 can support floating point math *if* the target system has floating point math routines in ROM. If that is the case: Prog8 can support floating point math *if* the target system has suitable floating point math routines in ROM. If that is the case:
#. what is the binary representation format of the floating point numbers? (how many bytes, how the bits are set up) #. what is the binary representation format of the floating point numbers? (how many bytes, how the bits are set up)
#. what are the valid minimum negative and maximum positive floating point values? #. what are the valid minimum negative and maximum positive floating point values?

View File

@@ -16,6 +16,7 @@ Currently these machines can be selected as a compilation target (via the ``-tar
- 'c128': the Commodore 128 (*limited support*) - 'c128': the Commodore 128 (*limited support*)
- 'pet32': the Commodore PET 4032 (*limited support*) - 'pet32': the Commodore PET 4032 (*limited support*)
- 'virtual': a builtin virtual machine - 'virtual': a builtin virtual machine
- custom targets via a separate configuration file (see :ref:`customizable_target`)
This chapter explains some relevant system details of the c64 and cx16 machines. This chapter explains some relevant system details of the c64 and cx16 machines.

View File

@@ -58,7 +58,7 @@ and for example the below code omits line 5::
STRUCTS and TYPED POINTERS STRUCTS and TYPED POINTERS
-------------------------- --------------------------
- allow struct initialization syntax in an array such as [ Node(), Node(), Node() ], update sorting example to use list of countries like that - allow struct initialization syntax in an array such as [ ^^Node:[], ^^Node:[], ^^Node:[] ], update sorting example to use list of countries like that
- fix code size regressions (if any left) - fix code size regressions (if any left)
- optimize deref in PointerAssignmentsGen: optimize 'forceTemporary' to only use a temporary when the offset is >0 - optimize deref in PointerAssignmentsGen: optimize 'forceTemporary' to only use a temporary when the offset is >0
- update structpointers.rst docs with 6502 specific things? - update structpointers.rst docs with 6502 specific things?
@@ -68,7 +68,6 @@ STRUCTS and TYPED POINTERS
- try to optimize pointer arithmetic used in peek/poke a bit more so the routines in sorting module can use typed pointers without increasing code size, see test.p8 in commit d394dc1e - try to optimize pointer arithmetic used in peek/poke a bit more so the routines in sorting module can use typed pointers without increasing code size, see test.p8 in commit d394dc1e
- should @(wordpointer) be equivalent to wordpointer^^ (that would require a LOT of code rewrite that now knows that @() is strictly byte based) ? - should @(wordpointer) be equivalent to wordpointer^^ (that would require a LOT of code rewrite that now knows that @() is strictly byte based) ?
or do an implicit cast @(wpointer as ubyte^^) ? And/or add a warning about that? or do an implicit cast @(wpointer as ubyte^^) ? And/or add a warning about that?
- add struct and pointer benchmark to benchmark program?
- optimize addUnsignedByteOrWordToAY in PointerAssignmentsGen a bit more - optimize addUnsignedByteOrWordToAY in PointerAssignmentsGen a bit more
- support for typed function pointers? (&routine could be typed by default as well then) - support for typed function pointers? (&routine could be typed by default as well then)
- support @nosplit pointer arrays? - support @nosplit pointer arrays?

View File

@@ -3,4 +3,4 @@ org.gradle.console=rich
org.gradle.parallel=true org.gradle.parallel=true
org.gradle.daemon=true org.gradle.daemon=true
kotlin.code.style=official kotlin.code.style=official
version=12.0-BETA1 version=12.0-BETA2