From 2342b0a8f591591d807fbf6c94d4a7e14f74bd7a Mon Sep 17 00:00:00 2001 From: ooichu <50176726+ooichu@users.noreply.github.com> Date: Sat, 4 Feb 2023 16:49:21 +0300 Subject: [PATCH 1/3] Add optional support for big-endian architectures Big-endian compatibility tested with `qemu-mips` --- doc/capi.md | 2 +- doc/impl.md | 4 ++-- src/fe.c | 14 ++++++++++---- 3 files changed, 13 insertions(+), 7 deletions(-) diff --git a/doc/capi.md b/doc/capi.md index a7444f9..57ad275 100644 --- a/doc/capi.md +++ b/doc/capi.md @@ -19,7 +19,7 @@ fe_Context *ctx = fe_open(data, size); fe_close(ctx); free(data); ``` - +If you are building a project on a big-endian system, you must `#define FE_BIGENDIAN` before compiling. ## Running a script To run a script it should first be read then evaluated; this should be diff --git a/doc/impl.md b/doc/impl.md index 01b9183..4c9603d 100644 --- a/doc/impl.md +++ b/doc/impl.md @@ -148,8 +148,8 @@ to keep the implementation terse, but should not hinder normal usage: * The garbage collector recurses on the `CAR` of objects thus deeply nested `CAR`s may overflow the C stack — an object's `CDR` is looped on and will not overflow the stack -* The storage of an object's type and GC mark assumes a little-endian system and - will not work correctly on systems of other endianness +* The storage of an object's type and GC mark assumes a little-endian or + big-endian system and will not work correctly on systems of other endianness * Proper tailcalls are not implemented — `while` can be used for iterating over lists * Strings are null-terminated and therefor not binary safe diff --git a/src/fe.c b/src/fe.c index b4a1634..2fed4f0 100644 --- a/src/fe.c +++ b/src/fe.c @@ -26,14 +26,20 @@ #define unused(x) ( (void) (x) ) #define car(x) ( (x)->car.o ) #define cdr(x) ( (x)->cdr.o ) -#define tag(x) ( (x)->car.c ) #define isnil(x) ( (x) == &nil ) #define type(x) ( tag(x) & 0x1 ? tag(x) >> 2 : FE_TPAIR ) #define settype(x,t) ( tag(x) = (t) << 2 | 1 ) #define number(x) ( (x)->cdr.n ) -#define prim(x) ( (x)->cdr.c ) #define cfunc(x) ( (x)->cdr.f ) -#define strbuf(x) ( &(x)->car.c + 1 ) +#define prim(x) ( *(x)->cdr.s ) + +#ifdef FE_BIGENDIAN +#define tag(x) ( (x)->car.s[STRBUFSIZE] ) +#define strbuf(x) ( (x)->car.s ) +#else +#define tag(x) ( *(x)->car.s ) +#define strbuf(x) ( (x)->car.s + 1 ) +#endif #define STRBUFSIZE ( (int) sizeof(fe_Object*) - 1 ) #define GCMARKBIT ( 0x2 ) @@ -57,7 +63,7 @@ static const char *typenames[] = { "func", "macro", "prim", "cfunc", "ptr" }; -typedef union { fe_Object *o; fe_CFunc f; fe_Number n; char c; } Value; +typedef union { fe_Object *o; fe_CFunc f; fe_Number n; char s[STRBUFSIZE + 1]; } Value; struct fe_Object { Value car, cdr; }; From 84cf0b9c01fb436193172b00524743a44c5ebe07 Mon Sep 17 00:00:00 2001 From: ooichu <50176726+ooichu@users.noreply.github.com> Date: Tue, 7 Feb 2023 00:55:45 +0300 Subject: [PATCH 2/3] Removed 'endianess issue' from `impl.md` --- doc/impl.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/doc/impl.md b/doc/impl.md index 4c9603d..5be7485 100644 --- a/doc/impl.md +++ b/doc/impl.md @@ -148,8 +148,6 @@ to keep the implementation terse, but should not hinder normal usage: * The garbage collector recurses on the `CAR` of objects thus deeply nested `CAR`s may overflow the C stack — an object's `CDR` is looped on and will not overflow the stack -* The storage of an object's type and GC mark assumes a little-endian or - big-endian system and will not work correctly on systems of other endianness * Proper tailcalls are not implemented — `while` can be used for iterating over lists * Strings are null-terminated and therefor not binary safe From 75931af26b460f5bbdea330271c8d74a999bf675 Mon Sep 17 00:00:00 2001 From: ooichu <50176726+ooichu@users.noreply.github.com> Date: Sat, 25 Feb 2023 23:56:40 +0300 Subject: [PATCH 3/3] Added automatic endianess detection --- doc/capi.md | 2 +- src/fe.c | 13 ++++--------- 2 files changed, 5 insertions(+), 10 deletions(-) diff --git a/doc/capi.md b/doc/capi.md index 57ad275..a7444f9 100644 --- a/doc/capi.md +++ b/doc/capi.md @@ -19,7 +19,7 @@ fe_Context *ctx = fe_open(data, size); fe_close(ctx); free(data); ``` -If you are building a project on a big-endian system, you must `#define FE_BIGENDIAN` before compiling. + ## Running a script To run a script it should first be read then evaluated; this should be diff --git a/src/fe.c b/src/fe.c index 2fed4f0..9ace7d5 100644 --- a/src/fe.c +++ b/src/fe.c @@ -26,25 +26,20 @@ #define unused(x) ( (void) (x) ) #define car(x) ( (x)->car.o ) #define cdr(x) ( (x)->cdr.o ) +#define tag(x) ( (x)->car.s[!endian.c * STRBUFSIZE] ) #define isnil(x) ( (x) == &nil ) #define type(x) ( tag(x) & 0x1 ? tag(x) >> 2 : FE_TPAIR ) #define settype(x,t) ( tag(x) = (t) << 2 | 1 ) #define number(x) ( (x)->cdr.n ) +#define prim(x) ( (x)->cdr.s[!endian.c * STRBUFSIZE] ) #define cfunc(x) ( (x)->cdr.f ) -#define prim(x) ( *(x)->cdr.s ) - -#ifdef FE_BIGENDIAN -#define tag(x) ( (x)->car.s[STRBUFSIZE] ) -#define strbuf(x) ( (x)->car.s ) -#else -#define tag(x) ( *(x)->car.s ) -#define strbuf(x) ( (x)->car.s + 1 ) -#endif +#define strbuf(x) ( (x)->car.s + endian.c ) #define STRBUFSIZE ( (int) sizeof(fe_Object*) - 1 ) #define GCMARKBIT ( 0x2 ) #define GCSTACKSIZE ( 256 ) +static const union { size_t u; char c; } endian = { 0x1 }; enum { P_LET, P_SET, P_IF, P_FN, P_MAC, P_WHILE, P_QUOTE, P_AND, P_OR, P_DO, P_CONS,