about summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorKartik K. Agaram <vc@akkartik.com>2021-08-22 21:06:36 -0700
committerKartik K. Agaram <vc@akkartik.com>2021-08-22 21:09:28 -0700
commit827dd4a7fe98e80e776b206bb4966e22e22ce3c9 (patch)
tree699e33beb349cee09fc82d81dfd9c7ebef25e90a
parentb41aed4a9adda23ca2613a24fefa3eafbd611987 (diff)
downloadmu-827dd4a7fe98e80e776b206bb4966e22e22ce3c9.tar.gz
start throwing error on duplicate label
One less error that's only in the bootstrap phase.

On the other hand, for simplicity I got rid of the ability to override
the Entry label. One less special case, but we're also going further
from the ability to run subsets of layers. We haven't really been
exercising it for a long time, though (commit 7842, March 2021 when we
made baremetal the default).
-rw-r--r--linux/101_write.subx6
-rw-r--r--linux/102test.subx26
-rw-r--r--linux/103kernel-string-equal.subx10
-rw-r--r--linux/104new-segment.subx40
-rw-r--r--linux/105string-equal.subx12
-rw-r--r--linux/120allocate.subx24
-rw-r--r--linux/131table.subx311
-rwxr-xr-xlinux/assortbin46801 -> 47302 bytes
-rwxr-xr-xlinux/bracesbin48854 -> 49355 bytes
-rwxr-xr-xlinux/callsbin53837 -> 54338 bytes
-rwxr-xr-xlinux/dquotesbin50423 -> 50924 bytes
-rwxr-xr-xlinux/hexbin48981 -> 49482 bytes
-rwxr-xr-xlinux/labels_baremetalbin51541 -> 52042 bytes
-rw-r--r--linux/labels_baremetal.subx8
-rwxr-xr-xlinux/mubin608888 -> 609389 bytes
-rwxr-xr-xlinux/packbin59811 -> 60312 bytes
-rwxr-xr-xlinux/sigilsbin61208 -> 61709 bytes
-rwxr-xr-xlinux/survey_baremetalbin47482 -> 47983 bytes
-rw-r--r--linux/survey_baremetal.subx6
-rwxr-xr-xlinux/survey_elfbin56375 -> 56876 bytes
-rw-r--r--linux/survey_elf.subx8
-rwxr-xr-xlinux/test_apps6
-rwxr-xr-xlinux/testsbin45592 -> 46093 bytes
-rw-r--r--subx.md4
24 files changed, 385 insertions, 76 deletions
diff --git a/linux/101_write.subx b/linux/101_write.subx
index e1d64756..84718265 100644
--- a/linux/101_write.subx
+++ b/linux/101_write.subx
@@ -5,9 +5,9 @@
 # . op          subop               mod             rm32          base        index         scale       r32
 # . 1-3 bytes   3 bits              2 bits          3 bits        3 bits      3 bits        2 bits      2 bits      0/1/2/4 bytes   0/1/2/4 bytes
 
-Entry:  # just exit; can't test _write just yet
-    bb/copy-to-ebx  0/imm32
-    e8/call  syscall_exit/disp32
+#? Entry:  # just exit; can't test _write just yet
+#?     bb/copy-to-ebx  0/imm32
+#?     e8/call  syscall_exit/disp32
 
 _write:  # fd: int, s: (addr array byte)
     # . prologue
diff --git a/linux/102test.subx b/linux/102test.subx
index 90207e29..51ca2e12 100644
--- a/linux/102test.subx
+++ b/linux/102test.subx
@@ -5,19 +5,19 @@
 # . op          subop               mod             rm32          base        index         scale       r32
 # . 1-3 bytes   3 bits              2 bits          3 bits        3 bits      3 bits        2 bits      2 bits      0/1/2/4 bytes   0/1/2/4 bytes
 
-Entry:  # manual test
-    # check-ints-equal(34, 34)
-    # . . push args
-    68/push  "error in check-ints-equal"/imm32
-    68/push  34/imm32
-    68/push  34/imm32
-    # . . call
-    e8/call  check-ints-equal/disp32
-    # . . discard args
-    81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
-    # syscall_exit(0)
-    bb/copy-to-ebx  0/imm32
-    e8/call  syscall_exit/disp32
+#? Entry:  # manual test
+#?     # check-ints-equal(34, 34)
+#?     # . . push args
+#?     68/push  "error in check-ints-equal"/imm32
+#?     68/push  34/imm32
+#?     68/push  34/imm32
+#?     # . . call
+#?     e8/call  check-ints-equal/disp32
+#?     # . . discard args
+#?     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
+#?     # syscall_exit(0)
+#?     bb/copy-to-ebx  0/imm32
+#?     e8/call  syscall_exit/disp32
 
 # print msg to stderr if a != b, otherwise print "."
 check-ints-equal:  # a: int, b: int, msg: (addr array byte)
diff --git a/linux/103kernel-string-equal.subx b/linux/103kernel-string-equal.subx
index 29dbb6e2..a5da1747 100644
--- a/linux/103kernel-string-equal.subx
+++ b/linux/103kernel-string-equal.subx
@@ -22,11 +22,11 @@
 # . op          subop               mod             rm32          base        index         scale       r32
 # . 1-3 bytes   3 bits              2 bits          3 bits        3 bits      3 bits        2 bits      2 bits      0/1/2/4 bytes   0/1/2/4 bytes
 
-Entry:  # run all tests
-    e8/call  run-tests/disp32  # 'run-tests' is a function created automatically by SubX. It calls all functions that start with 'test-'.
-    # syscall_exit(Num-test-failures)
-    8b/copy                         0/mod/indirect  5/rm32/.disp32            .             .           3/r32/ebx   Num-test-failures/disp32          # copy *Num-test-failures to ebx
-    e8/call  syscall_exit/disp32
+#? Entry:  # run all tests
+#?     e8/call  run-tests/disp32  # 'run-tests' is a function created automatically by SubX. It calls all functions that start with 'test-'.
+#?     # syscall_exit(Num-test-failures)
+#?     8b/copy                         0/mod/indirect  5/rm32/.disp32            .             .           3/r32/ebx   Num-test-failures/disp32          # copy *Num-test-failures to ebx
+#?     e8/call  syscall_exit/disp32
 
 kernel-string-equal?:  # s: (addr kernel-string), benchmark: (addr array byte) -> result/eax: boolean
     # pseudocode:
diff --git a/linux/104new-segment.subx b/linux/104new-segment.subx
index 723e1afb..5a79ff19 100644
--- a/linux/104new-segment.subx
+++ b/linux/104new-segment.subx
@@ -16,26 +16,26 @@
 # . op          subop               mod             rm32          base        index         scale       r32
 # . 1-3 bytes   3 bits              2 bits          3 bits        3 bits      3 bits        2 bits      2 bits      0/1/2/4 bytes   0/1/2/4 bytes
 
-Entry:   # manual test
-    # var ad/ecx: allocation-descriptor
-    68/push  0/imm32/limit
-    68/push  0/imm32/curr
-    89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
-    # new-segment(0x1000, ad)
-    # . . push args
-    51/push-ecx
-    68/push  0x1000/imm32
-    # . . call
-    e8/call  new-segment/disp32
-    # . . discard args
-    81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
-    # var eax: (addr _) = ad->curr
-    8b/copy                         0/mod/indirect  1/rm32/ecx    .           .             .           0/r32/eax   .               .                 # copy *ecx to eax
-    # write to *eax to check that we have access to the newly-allocated segment
-    c7          0/subop/copy        0/mod/direct    0/rm32/eax    .           .             .           .           .               0x34/imm32        # copy to *eax
-    # syscall_exit(eax)
-    89/copy                         3/mod/direct    3/rm32/ebx    .           .             .           0/r32/eax   .               .                 # copy eax to ebx
-    e8/call  syscall_exit/disp32
+#? Entry:   # manual test
+#?     # var ad/ecx: allocation-descriptor
+#?     68/push  0/imm32/limit
+#?     68/push  0/imm32/curr
+#?     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
+#?     # new-segment(0x1000, ad)
+#?     # . . push args
+#?     51/push-ecx
+#?     68/push  0x1000/imm32
+#?     # . . call
+#?     e8/call  new-segment/disp32
+#?     # . . discard args
+#?     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
+#?     # var eax: (addr _) = ad->curr
+#?     8b/copy                         0/mod/indirect  1/rm32/ecx    .           .             .           0/r32/eax   .               .                 # copy *ecx to eax
+#?     # write to *eax to check that we have access to the newly-allocated segment
+#?     c7          0/subop/copy        0/mod/direct    0/rm32/eax    .           .             .           .           .               0x34/imm32        # copy to *eax
+#?     # syscall_exit(eax)
+#?     89/copy                         3/mod/direct    3/rm32/ebx    .           .             .           0/r32/eax   .               .                 # copy eax to ebx
+#?     e8/call  syscall_exit/disp32
 
 new-segment:  # len: int, ad: (addr allocation-descriptor)
     # . prologue
diff --git a/linux/105string-equal.subx b/linux/105string-equal.subx
index ae753855..38e88f76 100644
--- a/linux/105string-equal.subx
+++ b/linux/105string-equal.subx
@@ -5,12 +5,12 @@
 # . op          subop               mod             rm32          base        index         scale       r32
 # . 1-3 bytes   3 bits              2 bits          3 bits        3 bits      3 bits        2 bits      2 bits      0/1/2/4 bytes   0/1/2/4 bytes
 
-Entry:  # run all tests
-#?     e8/call test-compare-equal-strings/disp32
-    e8/call  run-tests/disp32  # 'run-tests' is a function created automatically by SubX. It calls all functions that start with 'test-'.
-    # syscall_exit(Num-test-failures)
-    8b/copy                         0/mod/indirect  5/rm32/.disp32            .             .           3/r32/ebx   Num-test-failures/disp32          # copy *Num-test-failures to ebx
-    e8/call  syscall_exit/disp32
+#? Entry:  # run all tests
+#? #?     e8/call test-compare-equal-strings/disp32
+#?     e8/call  run-tests/disp32  # 'run-tests' is a function created automatically by SubX. It calls all functions that start with 'test-'.
+#?     # syscall_exit(Num-test-failures)
+#?     8b/copy                         0/mod/indirect  5/rm32/.disp32            .             .           3/r32/ebx   Num-test-failures/disp32          # copy *Num-test-failures to ebx
+#?     e8/call  syscall_exit/disp32
 
 string-equal?:  # s: (addr array byte), benchmark: (addr array byte) -> result/eax: boolean
     # pseudocode:
diff --git a/linux/120allocate.subx b/linux/120allocate.subx
index 254c5f33..58c6c26d 100644
--- a/linux/120allocate.subx
+++ b/linux/120allocate.subx
@@ -42,18 +42,18 @@ Next-alloc-id:  # int
 # . op          subop               mod             rm32          base        index         scale       r32
 # . 1-3 bytes   3 bits              2 bits          3 bits        3 bits      3 bits        2 bits      2 bits      0/1/2/4 bytes   0/1/2/4 bytes
 
-# Let's start initializing the default allocation descriptor.
-
-Entry:
-    # initialize heap
-    # . Heap = new-segment(Heap-size)
-    # . . push args
-    68/push  Heap/imm32
-    ff          6/subop/push        0/mod/indirect  5/rm32/.disp32            .             .           .           Heap-size/disp32                  # push *Heap-size
-    # . . call
-    e8/call  new-segment/disp32
-    # . . discard args
-    81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
+#? # Let's start initializing the default allocation descriptor.
+#? 
+#? Entry:
+#?     # initialize heap
+#?     # . Heap = new-segment(Heap-size)
+#?     # . . push args
+#?     68/push  Heap/imm32
+#?     ff          6/subop/push        0/mod/indirect  5/rm32/.disp32            .             .           .           Heap-size/disp32                  # push *Heap-size
+#?     # . . call
+#?     e8/call  new-segment/disp32
+#?     # . . discard args
+#?     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 
     e8/call  run-tests/disp32  # 'run-tests' is a function created automatically by SubX. It calls all functions that start with 'test-'.
 $array-equal-main:end:
diff --git a/linux/131table.subx b/linux/131table.subx
index aa47204b..4df7a7a9 100644
--- a/linux/131table.subx
+++ b/linux/131table.subx
@@ -700,6 +700,154 @@ $test-get-or-insert:end:
     5d/pop-to-ebp
     c3/return
 
+# save 'key' to the next available row
+# if key already exists, abort
+# if there are no rows free, abort
+# return the address of the value
+insert-or-abort:  # table: (addr stream {(handle array byte), T}), key: (addr array byte), row-size: int, ad: (addr allocation-descriptor) -> result/eax: (addr T)
+    # pseudocode:
+    #   curr = table->data
+    #   max = &table->data[table->write]
+    #   while curr < max
+    #     var c: (addr array byte) = lookup(*curr)
+    #     if string-equal?(key, c)
+    #       abort
+    #     curr += row-size
+    #   if table->write >= table->size
+    #     abort
+    #   zero-out(max, row-size)
+    #   copy-array(ad, key, max)
+    #   table->write += row-size
+    #   return max+8
+    #
+    # . prologue
+    55/push-ebp
+    89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
+    # . save registers
+    51/push-ecx
+    52/push-edx
+    56/push-esi
+    # esi = table
+    8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           6/r32/esi   8/disp8         .                 # copy *(ebp+8) to esi
+    # var curr/ecx: (addr handle array byte) = table->data
+    8d/copy-address                 1/mod/*+disp8   6/rm32/esi    .           .             .           1/r32/ecx   0xc/disp8       .                 # copy esi+12 to ecx
+    # var max/edx: (addr byte) = &table->data[table->write]
+    8b/copy                         0/mod/indirect  6/rm32/esi    .           .             .           2/r32/edx   .               .                 # copy *esi to edx
+    8d/copy-address                 0/mod/indirect  4/rm32/sib    1/base/ecx  2/index/edx   .           2/r32/edx   .               .                 # copy ecx+edx to edx
+$insert-or-abort:search-loop:
+    # if (curr >= max) break
+    39/compare                      3/mod/direct    1/rm32/ecx    .           .             .           2/r32/edx   .               .                 # compare ecx with edx
+    73/jump-if-addr>=  $insert-or-abort:not-found/disp8
+    # var c/eax: (addr array byte) = lookup(*curr)
+    # . . push args
+    ff          6/subop/push        1/mod/*+disp8   1/rm32/ecx    .           .             .           .           4/disp8         .                 # push *(ecx+4)
+    ff          6/subop/push        0/mod/indirect  1/rm32/ecx    .           .             .           .           .               .                 # push *ecx
+    # . . call
+    e8/call  lookup/disp32
+    # . . discard args
+    81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
+    # if (string-equal?(key, c)) abort
+    # . eax = string-equal?(key, c)
+    # . . push args
+    50/push-eax
+    ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
+    # . . call
+    e8/call  string-equal?/disp32
+    # . . discard args
+    81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
+    # . if (eax != false) abort
+    3d/compare-eax-and  0/imm32/false
+    0f 85/jump-if-!=  $insert-or-abort:error-duplicate/disp32
+    # curr += row-size
+    03/add                          1/mod/*+disp8   5/rm32/ebp    .           .             .           1/r32/ecx   0x10/disp8      .                 # add *(ebp+16) to ecx
+    # loop
+    eb/jump  $insert-or-abort:search-loop/disp8
+$insert-or-abort:not-found:
+    # if (table->write >= table->size) abort
+    8b/copy                         0/mod/indirect  6/rm32/esi    .           .             .           1/r32/ecx   .               .                 # copy *esi to ecx
+    3b/compare                      1/mod/*+disp8   6/rm32/esi    .           .             .           1/r32/ecx   8/disp8         .                 # compare ecx with *(esi+8)
+    0f 83/jump-if-addr>=  $insert-or-abort:error-full/disp32
+    # zero-out(max, row-size)
+    # . . push args
+    ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0x10/disp8      .                 # push *(ebp+16)
+    52/push-edx
+    # . . call
+    e8/call  zero-out/disp32
+    # . . discard args
+    81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
+    # copy-array(ad, key, max)
+    # . . push args
+    52/push-edx
+    ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
+    ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0x14/disp8      .                 # push *(ebp+20)
+    # . . call
+    e8/call  copy-array/disp32
+    # . . discard args
+    81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
+    # table->write += row-size
+    # . eax = row-size
+    8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           0/r32/eax   0x10/disp8      .                 # copy *(ebp+16) to eax
+    # . table->write += eax
+    01/add                          0/mod/indirect  6/rm32/esi    .           .             .           0/r32/eax   .               .                 # add eax to *esi
+    # return max+8
+    # . eax = max
+    89/copy                         3/mod/direct    0/rm32/eax    .           .             .           2/r32/edx   .               .                 # copy edx to eax
+    # . eax += 8
+    05/add-to-eax  8/imm32
+$insert-or-abort:end:
+    # . restore registers
+    5e/pop-to-esi
+    5a/pop-to-edx
+    59/pop-to-ecx
+    # . epilogue
+    89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
+    5d/pop-to-ebp
+    c3/return
+
+$insert-or-abort:error-duplicate:
+    # . _write(2/stderr, error)
+    # . . push args
+    68/push  "insert-or-abort: key already exists: "/imm32
+    68/push  2/imm32/stderr
+    # . . call
+    e8/call  _write/disp32
+    # . . discard args
+    81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
+    # . _write(2/stderr, key)
+    # . . push args
+    ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
+    68/push  2/imm32/stderr
+    # . . call
+    e8/call  _write/disp32
+    # . . discard args
+    81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
+    # . _write(2/stderr, "\n")
+    # . . push args
+    68/push  "\n"/imm32
+    68/push  2/imm32/stderr
+    # . . call
+    e8/call  _write/disp32
+    # . . discard args
+    81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
+    # . syscall_exit(1)
+    bb/copy-to-ebx  1/imm32
+    e8/call  syscall_exit/disp32
+    # never gets here
+
+$insert-or-abort:error-full:
+    # . _write(2/stderr, error)
+    # . . push args
+    68/push  "insert-or-abort: table is full\n"/imm32
+    68/push  2/imm32/stderr
+    # . . call
+    e8/call  _write/disp32
+    # . . discard args
+    81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
+    # . syscall_exit(1)
+    bb/copy-to-ebx  1/imm32
+    e8/call  syscall_exit/disp32
+    # never gets here
+
 # if no row is found, save 'key' to the next available row
 # if there are no rows free, abort
 # return the address of the value
@@ -1321,6 +1469,169 @@ $test-get-or-insert-slice:end:
     5d/pop-to-ebp
     c3/return
 
+# if no row is found, save 'key' in the next available row
+# if there are no rows free, abort
+insert-slice-or-abort:  # table: (addr stream {(handle array byte), T}), key: (addr slice), row-size: int, ad: (addr allocation-descriptor) -> result/eax: (addr T)
+    # pseudocode:
+    #   curr = table->data
+    #   max = &table->data[table->write]
+    #   while curr < max
+    #     var c: (addr array byte) = lookup(*curr)
+    #     if slice-equal?(key, *curr)
+    #       return curr+8
+    #     curr += row-size
+    #   if table->write >= table->size
+    #     abort
+    #   zero-out(max, row-size)
+    #   slice-to-string(ad, key, max)
+    #   table->write += row-size
+    #   return max+8
+    #
+    # . prologue
+    55/push-ebp
+    89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
+    # . save registers
+    51/push-ecx
+    52/push-edx
+    56/push-esi
+    # esi = table
+    8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           6/r32/esi   8/disp8         .                 # copy *(ebp+8) to esi
+    # var curr/ecx: (addr handle array byte) = table->data
+    8d/copy-address                 1/mod/*+disp8   6/rm32/esi    .           .             .           1/r32/ecx   0xc/disp8       .                 # copy esi+12 to ecx
+    # var max/edx: (addr byte) = &table->data[table->write]
+    8b/copy                         0/mod/indirect  6/rm32/esi    .           .             .           2/r32/edx   .               .                 # copy *esi to edx
+    8d/copy-address                 0/mod/indirect  4/rm32/sib    1/base/ecx  2/index/edx   .           2/r32/edx   .               .                 # copy ecx+edx to edx
+$insert-slice-or-abort:search-loop:
+    # if (curr >= max) break
+    39/compare                      3/mod/direct    1/rm32/ecx    .           .             .           2/r32/edx   .               .                 # compare ecx with edx
+    73/jump-if-addr>=  $insert-slice-or-abort:not-found/disp8
+    # var c/eax: (addr array byte) = lookup(*curr)
+    # . . push args
+    ff          6/subop/push        1/mod/*+disp8   1/rm32/ecx    .           .             .           .           4/disp8         .                 # push *(ecx+4)
+    ff          6/subop/push        0/mod/indirect  1/rm32/ecx    .           .             .           .           .               .                 # push *ecx
+    # . . call
+    e8/call  lookup/disp32
+    # . . discard args
+    81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
+    # if (slice-equal?(key, c)) abort
+    # . eax = slice-equal?(key, c)
+    # . . push args
+    50/push-eax
+    ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
+    # . . call
+    e8/call  slice-equal?/disp32
+    # . . discard args
+    81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
+    # . if (eax != false) abort
+    3d/compare-eax-and  0/imm32/false
+    0f 85/jump-if-!=  $insert-slice-or-abort:error-duplicate/disp32
+    # curr += row-size
+    03/add                          1/mod/*+disp8   5/rm32/ebp    .           .             .           1/r32/ecx   0x10/disp8      .                 # add *(ebp+16) to ecx
+    # loop
+    eb/jump  $insert-slice-or-abort:search-loop/disp8
+$insert-slice-or-abort:not-found:
+    # result/eax = 0
+    31/xor                          3/mod/direct    0/rm32/eax    .           .             .           0/r32/eax   .               .                 # clear eax
+    # if (table->write >= table->size) abort
+    8b/copy                         0/mod/indirect  6/rm32/esi    .           .             .           1/r32/ecx   .               .                 # copy *esi to ecx
+    3b/compare                      1/mod/*+disp8   6/rm32/esi    .           .             .           1/r32/ecx   8/disp8         .                 # compare ecx with *(esi+8)
+    0f 8d/jump-if->=  $insert-slice-or-abort:error-full/disp32
+    # zero-out(max, row-size)
+    # . . push args
+    ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0x10/disp8      .                 # push *(ebp+16)
+    52/push-edx
+    # . . call
+    e8/call  zero-out/disp32
+    # . . discard args
+    81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
+    # slice-to-string(ad, key, max)
+    # . . push args
+    52/push-edx
+    ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
+    ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0x14/disp8      .                 # push *(ebp+20)
+    # . . call
+    e8/call  slice-to-string/disp32
+    # . . discard args
+    81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
+    # table->write += row-size
+    # . eax = row-size
+    8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           0/r32/eax   0x10/disp8      .                 # copy *(ebp+16) to eax
+    # . table->write += eax
+    01/add                          0/mod/indirect  6/rm32/esi    .           .             .           0/r32/eax   .               .                 # add eax to *esi
+    # return max+8
+    # . eax = max
+    89/copy                         3/mod/direct    0/rm32/eax    .           .             .           2/r32/edx   .               .                 # copy edx to eax
+    # . eax += 8
+    05/add-to-eax  8/imm32
+$insert-slice-or-abort:end:
+    # . restore registers
+    5e/pop-to-esi
+    5a/pop-to-edx
+    59/pop-to-ecx
+    # . epilogue
+    89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
+    5d/pop-to-ebp
+    c3/return
+
+$insert-slice-or-abort:error-duplicate:
+    # . flush(Stderr)
+    # . . push args
+    68/push  Stderr/imm32
+    # . . call
+    e8/call  flush/disp32
+    # . . discard args
+    81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
+    # . _write(2/stderr, error)
+    # . . push args
+    68/push  "insert-slice-or-abort: key already exists: "/imm32
+    68/push  2/imm32/stderr
+    # . . call
+    e8/call  _write/disp32
+    # . . discard args
+    81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
+    # . write-slice-buffered(Stderr, key)
+    # . . push args
+    ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
+    68/push  Stderr/imm32
+    # . . call
+    e8/call  write-slice-buffered/disp32
+    # . . discard args
+    81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
+    # . flush(Stderr)
+    # . . push args
+    68/push  Stderr/imm32
+    # . . call
+    e8/call  flush/disp32
+    # . . discard args
+    81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
+    # . _write(2/stderr, "\n")
+    # . . push args
+    68/push  "\n"/imm32
+    68/push  2/imm32/stderr
+    # . . call
+    e8/call  _write/disp32
+    # . . discard args
+    81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
+    # . syscall_exit(1)
+    bb/copy-to-ebx  1/imm32
+    e8/call  syscall_exit/disp32
+    # never gets here
+
+$insert-slice-or-abort:error-full:
+    # . _write(2/stderr, error)
+    # . . push args
+    68/push  "insert-slice-or-abort: table is full\n"/imm32
+    68/push  2/imm32/stderr
+    # . . call
+    e8/call  _write/disp32
+    # . . discard args
+    81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
+    # . syscall_exit(1)
+    bb/copy-to-ebx  1/imm32
+    e8/call  syscall_exit/disp32
+    # never gets here
+
+
 # if no row is found, stop(ed)
 get-or-stop:  # table: (addr stream {(handle array byte), T}), key: (addr array byte), row-size: int,
               # abort-message-prefix: (addr array byte), err: (addr buffered-file), ed: (addr exit-descriptor)
diff --git a/linux/assort b/linux/assort
index f4f73847..960b45df 100755
--- a/linux/assort
+++ b/linux/assort
Binary files differdiff --git a/linux/braces b/linux/braces
index 0930284c..bb79fe27 100755
--- a/linux/braces
+++ b/linux/braces
Binary files differdiff --git a/linux/calls b/linux/calls
index e2c8b5bb..a4b5c875 100755
--- a/linux/calls
+++ b/linux/calls
Binary files differdiff --git a/linux/dquotes b/linux/dquotes
index 0339ae16..e43323f3 100755
--- a/linux/dquotes
+++ b/linux/dquotes
Binary files differdiff --git a/linux/hex b/linux/hex
index e6c63c83..e242824f 100755
--- a/linux/hex
+++ b/linux/hex
Binary files differdiff --git a/linux/labels_baremetal b/linux/labels_baremetal
index 12a545ca..66fcbd97 100755
--- a/linux/labels_baremetal
+++ b/linux/labels_baremetal
Binary files differdiff --git a/linux/labels_baremetal.subx b/linux/labels_baremetal.subx
index e1743c0b..6e1d559b 100644
--- a/linux/labels_baremetal.subx
+++ b/linux/labels_baremetal.subx
@@ -216,7 +216,7 @@ load-labels:  # in: (addr buffered-file), labels: (stream {label-name, address}
     #     read-line-buffered(in, line)
     #     if (line->write == 0) break               # end of file
     #     var word-slice/ecx: (addr slice) = next-word(line)
-    #     var dest/edi: (addr int) = get-or-insert-slice(labels, word-slice, 12 bytes/row)
+    #     var dest/edi: (addr int) = insert-slice-or-abort(labels, word-slice, 12 bytes/row)
     #     word-slice = next-word(line)
     #     var address/eax: int = parse-hex-int-from-slice(word-slice)
     #     *dest = address
@@ -333,15 +333,15 @@ $load-labels:loop:
 #?     # . . discard args
 #?     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 #?     # }}}
-    # var dest/edi: (addr int) = get-or-insert-slice(labels, word-slice, 12 bytes/row, Heap)
-    # . eax = get-or-insert-slice(labels, word-slice, 12 bytes/row, Heap)
+    # var dest/edi: (addr int) = insert-slice-or-abort(labels, word-slice, 12 bytes/row, Heap)
+    # . eax = insert-slice-or-abort(labels, word-slice, 12 bytes/row, Heap)
     # . . push args
     68/push  Heap/imm32
     68/push  0xc/imm32/row-size
     52/push-edx
     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
     # . . call
-    e8/call  get-or-insert-slice/disp32
+    e8/call  insert-slice-or-abort/disp32
     # . . discard args
     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x10/imm32        # add to esp
     # . edi = eax
diff --git a/linux/mu b/linux/mu
index ecf884a2..ff127f40 100755
--- a/linux/mu
+++ b/linux/mu
Binary files differdiff --git a/linux/pack b/linux/pack
index 32db6ad3..659fafe7 100755
--- a/linux/pack
+++ b/linux/pack
Binary files differdiff --git a/linux/sigils b/linux/sigils
index 3bcd2d9c..f390b17e 100755
--- a/linux/sigils
+++ b/linux/sigils
Binary files differdiff --git a/linux/survey_baremetal b/linux/survey_baremetal
index b5429495..d794d9b0 100755
--- a/linux/survey_baremetal
+++ b/linux/survey_baremetal
Binary files differdiff --git a/linux/survey_baremetal.subx b/linux/survey_baremetal.subx
index 40115cd8..e7d12b7c 100644
--- a/linux/survey_baremetal.subx
+++ b/linux/survey_baremetal.subx
@@ -468,6 +468,8 @@ compute-addresses:  # in: (addr stream byte), labels: (addr stream {(handle arra
     #         current-address = new-address
     #       else if label?(word-slice)
     #         strip trailing ':' from word-slice
+    #         var tmp/eax: (addr int) = insert-slice-or-abort(labels, word-slice)
+    #         *tmp = current-address
     #         trace("label '" word-slice "' is at address " current-address ".")
     #         # labels occupy no space, so no need to increment offsets
     #       else
@@ -661,14 +663,14 @@ $compute-addresses:case-label:
     0f 84/jump-if-=  $compute-addresses:case-default/disp32
     # strip trailing ':' from word-slice
     ff          1/subop/decrement   1/mod/*+disp8   2/rm32/edx    .           .             .           .           4/disp8         .                 # decrement *(edx+4)
-    # var tmp/eax: (addr int) = get-or-insert-slice(labels, word-slice, row-size=12)
+    # var tmp/eax: (addr int) = insert-slice-or-abort(labels, word-slice, row-size=12)
     # . . push args
     68/push  Heap/imm32
     68/push  0xc/imm32/row-size
     52/push-edx
     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
     # . . call
-    e8/call  get-or-insert-slice/disp32
+    e8/call  insert-slice-or-abort/disp32
     # . . discard args
     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x10/imm32        # add to esp
     # *tmp = current-address
diff --git a/linux/survey_elf b/linux/survey_elf
index 9d1a715c..c5f59cca 100755
--- a/linux/survey_elf
+++ b/linux/survey_elf
Binary files differdiff --git a/linux/survey_elf.subx b/linux/survey_elf.subx
index 838feb88..59fb5af4 100644
--- a/linux/survey_elf.subx
+++ b/linux/survey_elf.subx
@@ -451,7 +451,7 @@ compute-offsets:  # in: (addr stream byte), segments: (addr stream {(handle arra
     #         break  (next line)
     #       else if label?(word-slice)
     #         strip trailing ':' from word-slice
-    #         linfo: (addr label-info) = get-or-insert-slice(labels, word-slice)
+    #         linfo: (addr label-info) = insert-slice-or-abort(labels, word-slice)
     #         linfo->segment-name = curr-segment-name
     #         trace("label '", word-slice, "' is in segment '", curr-segment-name, "'.")
     #         linfo->segment-offset = segment-offset
@@ -753,15 +753,15 @@ $compute-offsets:case-label:
     0f 84/jump-if-=  $compute-offsets:case-default/disp32
     # strip trailing ':' from word-slice
     ff          1/subop/decrement   1/mod/*+disp8   2/rm32/edx    .           .             .           .           4/disp8         .                 # decrement *(edx+4)
-    # linfo/edi = get-or-insert-slice(labels, word-slice, row-size=24)
-    # . eax = get-or-insert-slice(labels, word-slice, row-size=24)
+    # linfo/edi = insert-slice-or-abort(labels, word-slice, row-size=24)
+    # . eax = insert-slice-or-abort(labels, word-slice, row-size=24)
     # . . push args
     68/push  Heap/imm32
     68/push  0x18/imm32/row-size
     52/push-edx
     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0x10/disp8      .                 # push *(ebp+16)
     # . . call
-    e8/call  get-or-insert-slice/disp32
+    e8/call  insert-slice-or-abort/disp32
     # . . discard args
     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x10/imm32        # add to esp
     # . edi = eax
diff --git a/linux/test_apps b/linux/test_apps
index 9dab3e41..a72b1ce5 100755
--- a/linux/test_apps
+++ b/linux/test_apps
@@ -227,7 +227,7 @@ test "$1" = 'record'  ||  git diff --exit-code sigils
 }
 
 echo calls
-cat [012]*.subx subx-params.subx calls.subx  |  sigils  > a.sigils
+cat [012]*.subx subx-params.subx calls.subx  |  ./sigils  > a.sigils
 bootstrap/bootstrap translate a.sigils -o calls
 test "$1" = 'record'  ||  git diff --exit-code calls
 {
@@ -241,7 +241,7 @@ test "$1" = 'record'  ||  git diff --exit-code calls
 }
 
 echo braces
-cat [012]*.subx subx-params.subx braces.subx  |  calls  |  sigils  > a.sigils
+cat [012]*.subx subx-params.subx braces.subx  |  ./calls  |  ./sigils  > a.sigils
 bootstrap/bootstrap translate a.sigils -o braces
 test "$1" = 'record'  ||  git diff --exit-code braces
 {
@@ -255,7 +255,7 @@ test "$1" = 'record'  ||  git diff --exit-code braces
 }
 
 echo mu
-cat [0-2]*.subx mu.subx  |  braces  |  calls  |  sigils  > a.sigils
+cat [0-2]*.subx mu.subx  |  ./braces  |  ./calls  |  ./sigils  > a.sigils
 bootstrap/bootstrap translate a.sigils -o mu
 test "$1" = 'record'  ||  git diff --exit-code mu
 {
diff --git a/linux/tests b/linux/tests
index 04a112ef..e9b31bb9 100755
--- a/linux/tests
+++ b/linux/tests
Binary files differdiff --git a/subx.md b/subx.md
index d2b9ad50..1d281ced 100644
--- a/subx.md
+++ b/subx.md
@@ -138,10 +138,6 @@ Functions are called using the following syntax:
 Function arguments must be either literals (integers or strings) or a reg/mem
 operand using the syntax in the previous section.
 
-A special label on Linux is `Entry`, which can be used to specify/override the
-entry point of the program. It doesn't have to be unique, and the latest
-definition will override earlier ones.
-
 Another special pair of labels are the block delimiters `{` and `}`. They can
 be nested, and jump instructions can take arguments `loop` or `break` that
 jump to the enclosing `{` and `}` respectively.