on master help sign in
Add String offsets to VM

Now strTail just stores an offset into a string, meaning that it doesn't need to reallocate or copy an entire string. Therefore traversing a large string is now much quicker. Note - this means the entire string gets GCed at once, so if there is a large string but only a pointer to the end of it, the whole string still needs to be stored. For traversal, this isn't really a problem, but we may want to reallocate if the GC discovers there is only one reference to an offset string.

commit9418fe0ecb723a2f2dbdae51f5a38110e58ec842 authorEdwin Brady <ecb10@st-andrews.ac.uk> dateMon 25 Nov 2013, 15:13 (13 years ago) parent73e2e7e
3 files changed+65−4
rts/idris_gc.c+8−1 rts/idris_rts.c+44−1 rts/idris_rts.h+13−2
rts/idris_gc.c+8−1
@@ -24,6 +24,9 @@ VAL copy(VM* vm, VAL x) {
2424 case STRING:
2525 cl = MKSTRc(vm, x->info.str);
2626 break;
27+ case STROFFSET:
28+ cl = MKSTROFFc(vm, x->info.str_offset);
29+ break;
2730 case BIGINT:
2831 cl = MKBIGMc(vm, x->info.ptr);
2932 break;
@@ -60,7 +63,7 @@ void cheney(VM *vm) {
6063 while(scan < vm->heap.next) {
6164 size_t inc = *((size_t*)scan);
6265 VAL heap_item = (VAL)(scan+sizeof(size_t));
63− // If it's a CON, copy its arguments
66+ // If it's a CON or STROFFSET, copy its arguments
6467 switch(GETTY(heap_item)) {
6568 case CON:
6669 ar = ARITY(heap_item);
@@ -71,6 +74,10 @@ void cheney(VM *vm) {
7174 heap_item->info.c.args[i] = newptr;
7275 }
7376 break;
77+ case STROFFSET:
78+ heap_item->info.str_offset->str
79+ = copy(vm, heap_item->info.str_offset->str);
80+ break;
7481 default: // Nothing to copy
7582 break;
7683 }
rts/idris_rts.c+44−1
@@ -84,6 +84,10 @@ void idris_doneAlloc(VM* vm) {
8484 }
8585}
8686
87+int space(VM* vm, size_t size) {
88+ return (vm->heap.next + size + sizeof(size_t) < vm->heap.end);
89+}
90+
8791void* allocate(VM* vm, size_t size, int outerlock) {
8892// return malloc(size);
8993 int lock = vm->processes > 0 && !outerlock;
@@ -161,6 +165,12 @@ VAL MKSTR(VM* vm, const char* str) {
161165 return cl;
162166}
163167
168+char* GETSTROFF(VAL stroff) {
169+ // Assume STROFF
170+ StrOffset* root = stroff->info.str_offset;
171+ return (root->str->info.str + root->offset);
172+}
173+
164174VAL MKPTR(VM* vm, void* ptr) {
165175 Closure* cl = allocate(vm, sizeof(Closure), 0);
166176 SETTY(cl, PTR);
@@ -397,8 +407,41 @@ VAL idris_strHead(VM* vm, VAL str) {
397407 return MKINT((i_int)(GETSTR(str)[0]));
398408}
399409
410+VAL MKSTROFFc(VM* vm, StrOffset* off) {
411+ Closure* cl = allocate(vm, sizeof(Closure) + sizeof(StrOffset), 1);
412+ SETTY(cl, STROFFSET);
413+ cl->info.str_offset = (StrOffset*)((char*)cl + sizeof(Closure));
414+
415+ cl->info.str_offset->str = off->str;
416+ cl->info.str_offset->offset = off->offset;
417+
418+ return cl;
419+}
420+
400421VAL idris_strTail(VM* vm, VAL str) {
401− return MKSTR(vm, GETSTR(str)+1);
422+ // If there's no room, just copy the string, or we'll have a problem after
423+ // gc moves str
424+ if (space(vm, sizeof(Closure) + sizeof(StrOffset))) {
425+ Closure* cl = allocate(vm, sizeof(Closure) + sizeof(StrOffset), 0);
426+ SETTY(cl, STROFFSET);
427+ cl->info.str_offset = (StrOffset*)((char*)cl + sizeof(Closure));
428+
429+ int offset = 0;
430+ VAL root = str;
431+
432+ while(root!=NULL && !ISSTR(root)) { // find the root, carry on.
433+ // In theory, at most one step here!
434+ offset += root->info.str_offset->offset;
435+ root = root->info.str_offset->str;
436+ }
437+
438+ cl->info.str_offset->str = root;
439+ cl->info.str_offset->offset = offset+1;
440+
441+ return cl;
442+ } else {
443+ return MKSTR(vm, GETSTR(str)+1);
444+ }
402445}
403446
404447VAL idris_strCons(VM* vm, VAL x, VAL xs) {
rts/idris_rts.h+13−2
@@ -15,7 +15,8 @@
1515// Closures
1616
1717typedef enum {
18− CON, INT, BIGINT, FLOAT, STRING, BITS8, BITS16, BITS32, BITS64, UNIT, PTR, FWD
18+ CON, INT, BIGINT, FLOAT, STRING, STROFFSET,
19+ BITS8, BITS16, BITS32, BITS64, UNIT, PTR, FWD
1920} ClosureType;
2021
2122typedef struct Closure *VAL;
@@ -25,6 +26,11 @@ typedef struct {
2526 VAL args[];
2627} con;
2728
29+typedef struct {
30+ VAL str;
31+ int offset;
32+} StrOffset;
33+
2834typedef struct Closure {
2935// Use top 16 bits of ty for saying which heap value is in
3036// Bottom 16 bits for closure type
@@ -34,6 +40,7 @@ typedef struct Closure {
3440 int i;
3541 double f;
3642 char* str;
43+ StrOffset* str_offset;
3744 void* ptr;
3845 uint8_t bits8;
3946 uint16_t bits16;
@@ -92,7 +99,7 @@ typedef void(*func)(VM*, VAL*);
9299
93100// Retrieving values
94101
95−#define GETSTR(x) (((VAL)(x))->info.str)
102+#define GETSTR(x) (ISSTR(x) ? (((VAL)(x))->info.str) : GETSTROFF(x))
96103#define GETPTR(x) (((VAL)(x))->info.ptr)
97104#define GETFLOAT(x) (((VAL)(x))->info.f)
98105
@@ -119,6 +126,7 @@ typedef intptr_t i_int;
119126#define MKINT(x) ((void*)((x)<<1)+1)
120127#define GETINT(x) ((i_int)(x)>>1)
121128#define ISINT(x) ((((i_int)x)&1) == 1)
129+#define ISSTR(x) (((VAL)(x))->ty == STRING)
122130
123131#define INTOP(op,x,y) MKINT((i_int)((((i_int)x)>>1) op (((i_int)y)>>1)))
124132#define UINTOP(op,x,y) MKINT((i_int)((((uintptr_t)x)>>1) op (((uintptr_t)y)>>1)))
@@ -151,9 +159,12 @@ VAL MKB64(VM* vm, uint64_t b);
151159
152160// following versions don't take a lock when allocating
153161VAL MKFLOATc(VM* vm, double val);
162+VAL MKSTROFFc(VM* vm, StrOffset* off);
154163VAL MKSTRc(VM* vm, char* str);
155164VAL MKPTRc(VM* vm, void* ptr);
156165
166+char* GETSTROFF(VAL stroff);
167+
157168// #define SETTAG(x, a) (x)->info.c.tag = (a)
158169#define SETARG(x, i, a) ((x)->info.c.args)[i] = ((VAL)(a))
159170#define GETARG(x, i) ((x)->info.c.args)[i]