commit ebb6ead5854a278ec3e5f5c72a7a536a114e67d5
parent 877607adbf647dd1bf3942699d4efcef757571e4
Author: Ryan Sepassi <rsepassi@gmail.com>
Date: Sun, 3 May 2026 20:53:41 -0700
libp1pp: namespace mem* / str* helpers under libp1pp__
Rename libp1pp's freestanding string and byte primitives:
:memcpy → :libp1pp__memcpy, :memset → :libp1pp__memset,
:memcmp → :libp1pp__memcmp, :strlen → :libp1pp__strlen,
:streq → :libp1pp__streq, :strcmp → :libp1pp__strcmp.
Without the prefix, libp1pp's public entry points collide with libc's
own `:memcpy` / `:memcmp` / `:memset` once a translation unit links
against the mes-libc chain (cc-libc, tcc-boot2). hex2++ rejects the
duplicate global label, so the entire combined libc + tcc.flat
target failed to assemble. The prefix gives libp1pp's helpers their
own namespace; libc's libc-named entry points keep ownership of the
public C names.
Update %memcpy_call (the macro cc.scm's struct copies and scheme1's
heap copies route through) and scheme1.P1pp's direct callsites to
the prefixed names. tests/cc/129-extern-libp1pp.c — the regression
that pins cc.scm's bare-name extern passthrough — moves to the
prefixed extern decls so plain `tests/cc` fixtures can still link
against libp1pp without dragging libc in.
docs/LIBP1PP.md still documents these as bare unprefixed names; that
doc needs a follow-up update.
Diffstat:
3 files changed, 46 insertions(+), 42 deletions(-)
diff --git a/P1/P1pp.P1pp b/P1/P1pp.P1pp
@@ -289,7 +289,7 @@
%li(a2, n_imm)
%mov(a1, src_reg)
%mov(a0, dst_reg)
- %call(&memcpy)
+ %call(&libp1pp__memcpy)
%endm
# =========================================================================
@@ -890,10 +890,13 @@
# Memory and strings
# =========================================================================
-# memcpy(dst=a0, src=a1, n=a2) -> dst (a0)
+# libp1pp__memcpy(dst=a0, src=a1, n=a2) -> dst (a0)
# Leaf. Copies n bytes from src to dst. No overlap support where
-# dst > src && dst < src + n.
-:memcpy
+# dst > src && dst < src + n. Internal name so a libc that defines its
+# own :memcpy can be linked alongside libp1pp without duplicate-label
+# errors at hex2++ time. cc.scm's %memcpy_call macro and scheme1
+# bind this prefixed name directly.
+:libp1pp__memcpy
.scope
%mov(a3, a0)
%li(t0, 0)
@@ -910,8 +913,8 @@
%ret
.endscope
-# memset(dst=a0, byte=a1, n=a2) -> dst (a0)
-:memset
+# libp1pp__memset(dst=a0, byte=a1, n=a2) -> dst (a0)
+:libp1pp__memset
.scope
%mov(a3, a0)
%li(t0, 0)
@@ -926,8 +929,8 @@
%ret
.endscope
-# memcmp(a=a0, b=a1, n=a2) -> -1/0/1 (a0)
-:memcmp
+# libp1pp__memcmp(a=a0, b=a1, n=a2) -> -1/0/1 (a0)
+:libp1pp__memcmp
.scope
%li(t0, 0)
:.loop
@@ -951,8 +954,8 @@
%ret
.endscope
-# strlen(cstr=a0) -> n (a0)
-:strlen
+# libp1pp__strlen(cstr=a0) -> n (a0)
+:libp1pp__strlen
.scope
%mov(a1, a0)
:.loop
@@ -965,8 +968,8 @@
%ret
.endscope
-# streq(a=a0, b=a1) -> 0 or 1
-:streq
+# libp1pp__streq(a=a0, b=a1) -> 0 or 1
+:libp1pp__streq
.scope
:.loop
%lb(t0, a0, 0)
@@ -984,8 +987,8 @@
%ret
.endscope
-# strcmp(a=a0, b=a1) -> -1/0/1
-:strcmp
+# libp1pp__strcmp(a=a0, b=a1) -> -1/0/1
+:libp1pp__strcmp
.scope
:.loop
%lb(t0, a0, 0)
@@ -1470,7 +1473,7 @@
%fn(print_cstr, 16, {
%st(s0, sp, 0)
%mov(s0, a0)
- %call(&strlen)
+ %call(&libp1pp__strlen)
%mov(a1, a0)
%mov(a0, s0)
%call(&print)
@@ -1480,7 +1483,7 @@
%fn(eprint_cstr, 16, {
%st(s0, sp, 0)
%mov(s0, a0)
- %call(&strlen)
+ %call(&libp1pp__strlen)
%mov(a1, a0)
%mov(a0, s0)
%call(&eprint)
diff --git a/scheme1/scheme1.P1pp b/scheme1/scheme1.P1pp
@@ -881,7 +881,7 @@
%mov(a0, t2)
%la(a1, name_label)
%li(a2, len)
- %call(&memcmp)
+ %call(&libp1pp__memcmp)
%bnez(a0, &.bad)
%li(a0, value)
%mkfix(a0, a0)
@@ -2989,7 +2989,7 @@
%ld(a0, t1, %SYMENT.name_ptr)
%ldl(a1, name_ptr)
%ldl(a2, name_len)
- %call(&memcmp)
+ %call(&libp1pp__memcmp)
%beqz(a0, &.found)
:.next
@@ -3014,7 +3014,7 @@
%call(&alloc_bytes_main)
%ldl(a1, name_ptr)
%ldl(a2, name_len)
- %call(&memcpy) ; returns dst in a0 = stable copy
+ %call(&libp1pp__memcpy) ; returns dst in a0 = stable copy
%ldl(t0, idx)
%symtab_entry(t1, t0, t2)
@@ -3390,7 +3390,7 @@
%cdr(t0, t0)
%stl(t0, args)
- %call(&memcpy)
+ %call(&libp1pp__memcpy)
%b(&.copy_loop)
:.copy_done
@@ -3425,7 +3425,7 @@
%ldl(a2, len) ; len
%heap_ld(t0, a0, %BV.data) ; dst = bv.data
%mov(a0, t0)
- %call(&memcpy)
+ %call(&libp1pp__memcpy)
%ldl(a0, bv)
})
@@ -3985,7 +3985,7 @@
%ldl(a0, new_data_ptr)
%heap_ld(a1, t0, %BV.data) ; old data ptr
%ldl(a2, raw)
- %call(&memcpy)
+ %call(&libp1pp__memcpy)
%ldl(t0, bv)
%ldl(t1, new_data_ptr)
@@ -4051,7 +4051,7 @@
%fn(prim_string_length_entry, 0, {
%car(t0, a0)
%heap_ld(a0, t0, %BV.data)
- %call(&strlen)
+ %call(&libp1pp__strlen)
%mkfix(a0, a0)
%eret
})
@@ -4812,7 +4812,7 @@
%add(a0, a0, t1) ; dst = data + old_len
%ldl(a1, src)
%ldl(a2, n)
- %call(&memcpy)
+ %call(&libp1pp__memcpy)
# hdr = (old_len + n) << 8 | HDR.BV. HDR.BV is 0.
%ldl(t0, old_len)
@@ -5716,7 +5716,7 @@
# len = strlen(*argv)
%ldl(t0, argv)
%ld(a0, t0, 0)
- %call(&strlen)
+ %call(&libp1pp__strlen)
# bv = str_alloc(len). argv entries flow into syscalls (sys-openat,
# sys-execve) that read data_ptr as a C string, so the trailing NUL
@@ -5731,7 +5731,7 @@
%ld(a1, t1, 0)
%heap_ld(t1, t0, %BV.hdr)
%shri(a2, t1, 8)
- %call(&memcpy)
+ %call(&libp1pp__memcpy)
# cell = cons(bv, NIL); append to list head/tail.
%ldl(a0, bv)
diff --git a/tests/cc/129-extern-libp1pp.c b/tests/cc/129-extern-libp1pp.c
@@ -1,22 +1,23 @@
/* Calls into libp1pp routines via plain C `extern` declarations. The
- * libp1pp side already provides `:memcpy`, `:memcmp`, and `:memset`
- * as bare-name labels (see P1/P1pp.P1pp). For these to link,
- * cc.scm must NOT prefix `extern`-but-not-defined-here symbols with
- * its `cc__` namespace — bare-name extern decls should pass through.
+ * libp1pp side provides `:libp1pp__memcpy`, `:libp1pp__memcmp`, and
+ * `:libp1pp__memset` as bare-name labels (see P1/P1pp.P1pp). For these
+ * to link, cc.scm must NOT prefix `extern`-but-not-defined-here symbols
+ * with its `cc__` namespace — bare-name extern decls should pass through.
*
- * This is the linkage rule that lets the tcc-boot2 path resolve all
- * the low-level memory symbols (memcpy/memcmp/memset/etc.) tcc.c calls.
- * Do not test stdio/libc string APIs here; plain tests/cc fixtures should
- * stay freestanding.
+ * The `libp1pp__` prefix keeps these freestanding helpers from clashing
+ * with libc's own `:memcpy` / `:memcmp` / `:memset` when a translation
+ * unit is linked against the mes-libc chain (cc-libc / tcc-boot2).
+ * Plain tests/cc fixtures stay freestanding and bind the prefixed names
+ * directly. Do not test stdio/libc string APIs here.
*/
-extern void *memcpy(void *, const void *, unsigned long);
-extern int memcmp(const void *, const void *, unsigned long);
-extern void *memset(void *, int, unsigned long);
+extern void *libp1pp__memcpy(void *, const void *, unsigned long);
+extern int libp1pp__memcmp(const void *, const void *, unsigned long);
+extern void *libp1pp__memset(void *, int, unsigned long);
int test_memcpy(void) {
char buf[8];
- memcpy(buf, "abcdefg", 8);
+ libp1pp__memcpy(buf, "abcdefg", 8);
if (buf[0] != 'a') return 1;
if (buf[3] != 'd') return 2;
if (buf[6] != 'g') return 3;
@@ -25,15 +26,15 @@ int test_memcpy(void) {
}
int test_memcmp(void) {
- if (memcmp("hello", "hello", 5) != 0) return 1;
- if (memcmp("hello", "help!", 5) == 0) return 2;
- if (memcmp("a", "b", 1) == 0) return 3;
+ if (libp1pp__memcmp("hello", "hello", 5) != 0) return 1;
+ if (libp1pp__memcmp("hello", "help!", 5) == 0) return 2;
+ if (libp1pp__memcmp("a", "b", 1) == 0) return 3;
return 0;
}
int test_memset(void) {
char buf[6];
- memset(buf, 'X', 5);
+ libp1pp__memset(buf, 'X', 5);
buf[5] = 0;
if (buf[0] != 'X') return 1;
if (buf[2] != 'X') return 2;