Implement ARC RTL and wire string header layout end-to-end
- blaise_arc.c: implement _StringAddRef, _StringRelease, _StringConcat using a 12-byte header (refcnt/length/capacity) at the string pointer; refcnt=-1 marks immortal static strings; _StringConcat returns refcnt=0 so the compiler-inserted _StringAddRef at assignment brings it to 1 - EmitDataSection: string literals now include the 12-byte immortal header so _StringAddRef/_StringRelease are safe when called on literal values - EmitWrite: add 12 to string pointer before calling printf so char data is passed correctly (header is now part of every string allocation) - Update TestWriteLn_StringLit_CallsPrintf to check for the +12 offset instruction instead of the no-longer-direct label argument
This commit is contained in:
parent
54bfe6e93a
commit
e555f1dea9
|
|
@ -109,14 +109,20 @@ end;
|
|||
|
||||
procedure TCodeGenQBE.EmitDataSection;
|
||||
var
|
||||
I: Integer;
|
||||
I: Integer;
|
||||
StrLen: Integer;
|
||||
begin
|
||||
if FStrLits.Count = 0 then
|
||||
Exit;
|
||||
EmitLine('# String literals');
|
||||
{ Each literal has a 12-byte ARC header: refcnt=-1 (immortal), length, capacity.
|
||||
The string pointer IS the header pointer; char data begins at ptr+12. }
|
||||
for I := 0 to FStrLits.Count - 1 do
|
||||
EmitLine(Format('data $__s%d = { b "%s", b 0 }',
|
||||
[I, QbeEscapeString(FStrLits[I])]));
|
||||
begin
|
||||
StrLen := Length(FStrLits[I]);
|
||||
EmitLine(Format('data $__s%d = { w -1, w %d, w %d, b "%s", b 0 }',
|
||||
[I, StrLen, StrLen, QbeEscapeString(FStrLits[I])]));
|
||||
end;
|
||||
EmitLine('data $__fmt_s_nl = { b "%s\n", b 0 }');
|
||||
EmitLine('data $__fmt_s = { b "%s", b 0 }');
|
||||
EmitLine('data $__fmt_d_nl = { b "%d\n", b 0 }');
|
||||
|
|
@ -873,6 +879,7 @@ procedure TCodeGenQBE.EmitWrite(ACall: TProcCall; ANewline: Boolean);
|
|||
var
|
||||
ArgExpr: TASTExpr;
|
||||
ArgTemp: string;
|
||||
CharPtr: string;
|
||||
FmtLabel: string;
|
||||
IsString: Boolean;
|
||||
begin
|
||||
|
|
@ -897,7 +904,12 @@ begin
|
|||
FmtLabel := IfThen(ANewline, '$__fmt_d_nl', '$__fmt_d');
|
||||
|
||||
if IsString then
|
||||
EmitLine(Format(' call $printf(l %s, ..., l %s)', [FmtLabel, ArgTemp]))
|
||||
begin
|
||||
{ String pointer is the header pointer; char data starts at ptr+12. }
|
||||
CharPtr := AllocTemp;
|
||||
EmitLine(Format(' %s =l add %s, 12', [CharPtr, ArgTemp]));
|
||||
EmitLine(Format(' call $printf(l %s, ..., l %s)', [FmtLabel, CharPtr]));
|
||||
end
|
||||
else
|
||||
EmitLine(Format(' call $printf(l %s, ..., w %s)', [FmtLabel, ArgTemp]));
|
||||
end;
|
||||
|
|
|
|||
|
|
@ -135,8 +135,9 @@ begin
|
|||
IR := GenerateIR('program P; begin WriteLn(''Hi'') end.');
|
||||
AssertTrue('Calls printf with s_nl format',
|
||||
IRContains(IR, '$__fmt_s_nl'));
|
||||
AssertTrue('Passes string pointer',
|
||||
IRContains(IR, '..., l $__s0'));
|
||||
{ String header is 12 bytes; char data is accessed via add $__s0, 12 }
|
||||
AssertTrue('Offsets past string header',
|
||||
IRContains(IR, 'add $__s0, 12'));
|
||||
end;
|
||||
|
||||
procedure TCodeGenTests.TestWriteLn_IntExpr_CallsPrintfInt;
|
||||
|
|
|
|||
|
|
@ -1,34 +1,80 @@
|
|||
/*
|
||||
* Blaise RTL — ARC string management stubs (Phase 2)
|
||||
* Blaise RTL — ARC string management (Phase 2)
|
||||
*
|
||||
* Phase 2: no-op implementations. nil-safe (null check guards).
|
||||
*
|
||||
* Phase 3 will implement real reference counting using the locked
|
||||
* string header layout:
|
||||
* String pointer convention:
|
||||
* A Blaise string value is a pointer to the 12-byte header below.
|
||||
* The character data starts immediately after the header.
|
||||
* nil (0) represents an empty / unassigned string.
|
||||
*
|
||||
* +--[4 bytes]--+--[4 bytes]--+--[4 bytes]--+--[N bytes]--+--[1 byte]--+
|
||||
* | RefCount | Length | Capacity | UTF-8 data | NUL |
|
||||
* +-------------+-------------+-------------+-------------+------------+
|
||||
* ^--- string pointer (header ptr) ^--- chars at ptr+12
|
||||
*
|
||||
* A RefCount of -1 marks a statically-allocated string (string literals
|
||||
* in the data section). _StringAddRef and _StringRelease are no-ops for
|
||||
* static strings and nil pointers.
|
||||
* RefCount = -1 marks a statically-allocated string (string literals in the
|
||||
* data section). _StringAddRef and _StringRelease are no-ops for static
|
||||
* strings and nil pointers.
|
||||
*
|
||||
* _StringConcat allocates a new header with RefCount = 0 (unowned). The
|
||||
* compiler inserts a _StringAddRef at every assignment, which brings the
|
||||
* count to 1. A corresponding _StringRelease at scope exit frees it.
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
void _StringAddRef(void *ptr) {
|
||||
(void)ptr;
|
||||
/* Phase 3: if (ptr && *(int32_t*)ptr != -1) ++*(int32_t*)ptr; */
|
||||
#define IMMORTAL_REFCNT (-1)
|
||||
|
||||
typedef struct {
|
||||
int32_t refcnt;
|
||||
int32_t length;
|
||||
int32_t capacity;
|
||||
/* char data[]; follows immediately */
|
||||
} BlaiseStrHdr;
|
||||
|
||||
static inline BlaiseStrHdr* hdr(void* ptr) {
|
||||
return (BlaiseStrHdr*)ptr;
|
||||
}
|
||||
|
||||
void _StringRelease(void *ptr) {
|
||||
(void)ptr;
|
||||
/* Phase 3:
|
||||
if (!ptr) return;
|
||||
int32_t *rc = (int32_t*)ptr;
|
||||
if (*rc == -1) return; // static string
|
||||
if (--(*rc) == 0) free(ptr);
|
||||
*/
|
||||
void _StringAddRef(void* ptr) {
|
||||
if (!ptr) return;
|
||||
BlaiseStrHdr* h = hdr(ptr);
|
||||
if (h->refcnt == IMMORTAL_REFCNT) return;
|
||||
h->refcnt++;
|
||||
}
|
||||
|
||||
void _StringRelease(void* ptr) {
|
||||
if (!ptr) return;
|
||||
BlaiseStrHdr* h = hdr(ptr);
|
||||
if (h->refcnt == IMMORTAL_REFCNT) return;
|
||||
if (--h->refcnt == 0) free(ptr);
|
||||
}
|
||||
|
||||
/*
|
||||
* Concatenate two Blaise strings. Either or both may be nil.
|
||||
* Returns a new header with RefCount = 0 (caller takes ownership via AddRef).
|
||||
* Returns nil if both inputs are nil.
|
||||
*/
|
||||
void* _StringConcat(void* s1, void* s2) {
|
||||
const char* c1 = s1 ? (const char*)s1 + sizeof(BlaiseStrHdr) : "";
|
||||
const char* c2 = s2 ? (const char*)s2 + sizeof(BlaiseStrHdr) : "";
|
||||
int32_t len1 = s1 ? hdr(s1)->length : 0;
|
||||
int32_t len2 = s2 ? hdr(s2)->length : 0;
|
||||
int32_t total = len1 + len2;
|
||||
BlaiseStrHdr* result;
|
||||
|
||||
result = (BlaiseStrHdr*)malloc(sizeof(BlaiseStrHdr) + total + 1);
|
||||
if (!result) return NULL;
|
||||
|
||||
result->refcnt = 0; /* unowned; caller's _StringAddRef brings it to 1 */
|
||||
result->length = total;
|
||||
result->capacity = total;
|
||||
|
||||
char* dest = (char*)result + sizeof(BlaiseStrHdr);
|
||||
if (len1 > 0) memcpy(dest, c1, len1);
|
||||
if (len2 > 0) memcpy(dest + len1, c2, len2);
|
||||
dest[total] = '\0';
|
||||
|
||||
return result;
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue