|
@@ -576,3 +576,388 @@ DEFINE_PRIM(_BOOL, type_enum_eq, _DYN _DYN);
|
|
|
DEFINE_PRIM(_DYN, alloc_enum_dyn, _TYPE _I32 _ARR _I32);
|
|
|
DEFINE_PRIM(_ARR, enum_parameters, _DYN);
|
|
|
DEFINE_PRIM(_BOOL, type_set_global, _TYPE _DYN);
|
|
|
+
|
|
|
+
|
|
|
+typedef struct {
|
|
|
+ char *buf;
|
|
|
+ int buf_pos;
|
|
|
+ int buf_size;
|
|
|
+ int *offsets;
|
|
|
+ int offsets_pos;
|
|
|
+ int offsets_size;
|
|
|
+ void **lookup;
|
|
|
+ int *lookup_index;
|
|
|
+ int lookup_pos;
|
|
|
+ int lookup_size;
|
|
|
+ int *remap_target;
|
|
|
+ int remap_pos;
|
|
|
+ int remap_size;
|
|
|
+ void **todos;
|
|
|
+ int todos_pos;
|
|
|
+ int todos_size;
|
|
|
+ int flags;
|
|
|
+} mem_context;
|
|
|
+
|
|
|
+#define compact_grow(buf,pos,size,req,type) \
|
|
|
+ if( ctx->pos + req > ctx->size ) { \
|
|
|
+ int nsize = ctx->size; \
|
|
|
+ if( nsize == 0 ) nsize = 256 /sizeof(type); \
|
|
|
+ while( nsize < ctx->pos + req ) nsize = (nsize * 3) / 2; \
|
|
|
+ type *nbuf = (type*)malloc(nsize * sizeof(type)); \
|
|
|
+ memcpy(nbuf,ctx->buf,ctx->pos * sizeof(type)); \
|
|
|
+ free(ctx->buf); \
|
|
|
+ ctx->buf = nbuf; \
|
|
|
+ ctx->size = nsize; \
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+static void compact_write_mem( mem_context *ctx, void *mem, int size ) {
|
|
|
+ compact_grow(buf,buf_pos,buf_size,size,char);
|
|
|
+ memcpy(ctx->buf + ctx->buf_pos, mem, size);
|
|
|
+ ctx->buf_pos += size;
|
|
|
+}
|
|
|
+
|
|
|
+static void compact_write_ptr( mem_context *ctx, void *ptr ) {
|
|
|
+ compact_write_mem(ctx,&ptr,sizeof(void*));
|
|
|
+}
|
|
|
+
|
|
|
+static void compact_write_int( mem_context *ctx, int v ) {
|
|
|
+ compact_write_mem(ctx,&v,4);
|
|
|
+}
|
|
|
+
|
|
|
+static void compact_write_offset( mem_context *ctx, int position ) {
|
|
|
+ compact_grow(offsets,offsets_pos,offsets_size,1,int);
|
|
|
+ ctx->offsets[ctx->offsets_pos++] = ctx->buf_pos;
|
|
|
+ compact_write_ptr(ctx,(void*)(int_val)position);
|
|
|
+}
|
|
|
+
|
|
|
+static int compact_lookup_index( mem_context *ctx, void *addr ) {
|
|
|
+ int min = 0;
|
|
|
+ int max = ctx->lookup_pos;
|
|
|
+ while( min < max ) {
|
|
|
+ int mid = (min + max) >> 1;
|
|
|
+ void *a = ctx->lookup[mid];
|
|
|
+ if( a < addr ) min = mid + 1; else if( a > addr ) max = mid; else return mid;
|
|
|
+ }
|
|
|
+ return -1;
|
|
|
+}
|
|
|
+
|
|
|
+#define BYTE_MARK 0x40000000
|
|
|
+
|
|
|
+static int compact_lookup_ref( mem_context *ctx, void *addr, bool is_bytes ) {
|
|
|
+ int min = 0;
|
|
|
+ int max = ctx->lookup_pos;
|
|
|
+ while( min < max ) {
|
|
|
+ int mid = (min + max) >> 1;
|
|
|
+ void *a = ctx->lookup[mid];
|
|
|
+ if( a < addr ) min = mid + 1; else if( a > addr ) max = mid; else return ctx->remap_target[ctx->lookup_index[mid]&~BYTE_MARK];
|
|
|
+ }
|
|
|
+ if( ctx->lookup_pos == ctx->lookup_size ) {
|
|
|
+ int nsize = ctx->lookup_size == 0 ? 128 : (ctx->lookup_size * 3) / 2;
|
|
|
+ void **nlookup = (void**)malloc(nsize * sizeof(void*));
|
|
|
+ int *nindex = (int*)malloc(nsize * sizeof(int));
|
|
|
+ memcpy(nlookup,ctx->lookup,ctx->lookup_pos * sizeof(void*));
|
|
|
+ memcpy(nindex,ctx->lookup_index,ctx->lookup_pos * sizeof(int));
|
|
|
+ free(ctx->lookup);
|
|
|
+ free(ctx->lookup_index);
|
|
|
+ ctx->lookup = nlookup;
|
|
|
+ ctx->lookup_index = nindex;
|
|
|
+ ctx->lookup_size = nsize;
|
|
|
+ }
|
|
|
+ int pos = (min + max) >> 1;
|
|
|
+ memmove(ctx->lookup + pos + 1, ctx->lookup + pos, (ctx->lookup_pos - pos) * sizeof(void*));
|
|
|
+ memmove(ctx->lookup_index + pos + 1, ctx->lookup_index + pos, (ctx->lookup_pos - pos) * sizeof(int));
|
|
|
+ int id = ctx->lookup_pos++;
|
|
|
+ ctx->lookup[pos] = addr;
|
|
|
+ ctx->lookup_index[pos] = id | (is_bytes ? BYTE_MARK : 0);
|
|
|
+ compact_grow(todos,todos_pos,todos_size,1,void*);
|
|
|
+ ctx->todos[ctx->todos_pos++] = addr;
|
|
|
+ compact_grow(remap_target,remap_pos,remap_size,1,int);
|
|
|
+ int target = -id-1;
|
|
|
+ ctx->remap_target[ctx->remap_pos++] = target;
|
|
|
+ return target;
|
|
|
+}
|
|
|
+
|
|
|
+static void compact_write_ref( mem_context *ctx, void *ptr, bool is_bytes ) {
|
|
|
+ if( !ptr ) {
|
|
|
+ compact_write_ptr(ctx, NULL);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ int ref = compact_lookup_ref(ctx,ptr,is_bytes);
|
|
|
+ compact_write_offset(ctx, ref);
|
|
|
+}
|
|
|
+
|
|
|
+static void compact_write_data( mem_context *ctx, hl_type *t, void *addr ) {
|
|
|
+ if( hl_is_dynamic(t) ) {
|
|
|
+ vdynamic *v = *(vdynamic**)addr;
|
|
|
+ if( v == NULL || (v->t->kind == HENUM && v->t->tenum->constructs[((venum*)v)->index].nparams == 0) ) {
|
|
|
+ compact_write_ptr(ctx,v);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ compact_write_ref(ctx,v,false);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ switch( t->kind ) {
|
|
|
+ case HUI8:
|
|
|
+ compact_write_mem(ctx, addr, 1);
|
|
|
+ break;
|
|
|
+ case HUI16:
|
|
|
+ compact_write_mem(ctx, addr, 2);
|
|
|
+ break;
|
|
|
+ case HI32:
|
|
|
+ case HF32:
|
|
|
+ compact_write_mem(ctx, addr, 4);
|
|
|
+ break;
|
|
|
+ case HF64:
|
|
|
+ case HI64:
|
|
|
+ compact_write_mem(ctx, addr, 8);
|
|
|
+ break;
|
|
|
+ case HBOOL:
|
|
|
+ compact_write_mem(ctx, addr, sizeof(bool));
|
|
|
+ break;
|
|
|
+ case HBYTES:
|
|
|
+ {
|
|
|
+ void *bytes = *(void**)addr;
|
|
|
+ if( bytes == NULL || !hl_is_gc_ptr(bytes) ) {
|
|
|
+ compact_write_ptr(ctx, bytes);
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ compact_write_ref(ctx, bytes, true);
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ case HABSTRACT:
|
|
|
+ hl_error("Unsupported abstract %s", t->abs_name);
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ hl_error("Unsupported type %d", t->kind);
|
|
|
+ break;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+static void compact_pad( mem_context *ctx, hl_type *t ) {
|
|
|
+ int sz = hl_pad_size(ctx->buf_pos,t);
|
|
|
+ ctx->buf_pos += sz;
|
|
|
+}
|
|
|
+
|
|
|
+static void compact_write_content( mem_context *ctx, vdynamic *d ) {
|
|
|
+ int i;
|
|
|
+ hl_type *t = d->t;
|
|
|
+ if( !hl_is_ptr(t) ) {
|
|
|
+ compact_write_ptr(ctx, t);
|
|
|
+ compact_write_mem(ctx,&d->v,hl_type_size(t));
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ switch( t->kind ) {
|
|
|
+ case HOBJ: {
|
|
|
+ char *obj_data = (char*)d;
|
|
|
+ hl_runtime_obj *rt = hl_get_obj_rt(t);
|
|
|
+ compact_grow(buf,buf_pos,buf_size,rt->size,char);
|
|
|
+ memset(ctx->buf + ctx->buf_pos, 0xCD, rt->size);
|
|
|
+ int buf_start = ctx->buf_pos;
|
|
|
+ int fstart = rt->nfields;
|
|
|
+ compact_write_ptr(ctx,t);
|
|
|
+ while( t ) {
|
|
|
+ fstart -= t->obj->nfields;
|
|
|
+ for(i=0;i<t->obj->nfields;i++) {
|
|
|
+ int fid = i + fstart;
|
|
|
+ ctx->buf_pos = buf_start + rt->fields_indexes[fid];
|
|
|
+ compact_write_data(ctx, t->obj->fields[i].t, obj_data + rt->fields_indexes[fid]);
|
|
|
+ }
|
|
|
+ t = t->obj->super;
|
|
|
+ }
|
|
|
+ ctx->buf_pos = buf_start + rt->size;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ case HVIRTUAL: {
|
|
|
+ vvirtual *v = (vvirtual*)d;
|
|
|
+ int start = ctx->buf_pos;
|
|
|
+ compact_write_ptr(ctx, t);
|
|
|
+ if( ctx->flags & 4 )
|
|
|
+ compact_write_offset(ctx, start); // virtual self value
|
|
|
+ else if( ctx->flags & 2 )
|
|
|
+ compact_write_ptr(ctx, NULL); // optimize virtuals
|
|
|
+ else
|
|
|
+ compact_write_data(ctx, &hlt_dyn, &v->value);
|
|
|
+ compact_write_data(ctx, &hlt_dyn, &v->next);
|
|
|
+ if( !v->value || (ctx->flags&6) ) {
|
|
|
+ int target = ctx->buf_pos + t->virt->nfields * sizeof(void*);
|
|
|
+ for(i=0;i<t->virt->nfields;i++) {
|
|
|
+ hl_type *ft = t->virt->fields[i].t;
|
|
|
+ target += hl_pad_size(target, ft);
|
|
|
+ compact_write_offset(ctx, target);
|
|
|
+ target += hl_type_size(ft);
|
|
|
+ }
|
|
|
+ for(i=0;i<t->virt->nfields;i++) {
|
|
|
+ void *addr = ((void**)(v + 1))[i];
|
|
|
+ hl_type *ft = t->virt->fields[i].t;
|
|
|
+ compact_pad(ctx,ft);
|
|
|
+ if( !addr ) {
|
|
|
+ if( !hl_is_ptr(ft) ) hl_error("assert");
|
|
|
+ compact_write_ptr(ctx,NULL);
|
|
|
+ } else
|
|
|
+ compact_write_data(ctx,ft,addr);
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ vdynobj *obj = (vdynobj*)v->value;
|
|
|
+ if( obj->t->kind != HDYNOBJ ) hl_error("assert");
|
|
|
+ int todo_save = ctx->todos_pos;
|
|
|
+ for(i=0;i<t->virt->nfields;i++) {
|
|
|
+ void *addr = ((void**)(v + 1))[i];
|
|
|
+ compact_write_ref(ctx, addr, false);
|
|
|
+ }
|
|
|
+ ctx->todos_pos = todo_save;
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ case HDYNOBJ: {
|
|
|
+ vdynobj *obj = (vdynobj*)d;
|
|
|
+ int lookup_data = ctx->buf_pos + sizeof(vdynobj);
|
|
|
+ int raw_data = lookup_data + obj->nfields * sizeof(hl_field_lookup);
|
|
|
+ int values_data = raw_data + obj->raw_size;
|
|
|
+ values_data += hl_pad_size(values_data,&hlt_dyn);
|
|
|
+
|
|
|
+ compact_write_ptr(ctx, t);
|
|
|
+ if( obj->lookup )
|
|
|
+ compact_write_offset(ctx, lookup_data);
|
|
|
+ else
|
|
|
+ compact_write_ptr(ctx, NULL);
|
|
|
+ if( obj->raw_data )
|
|
|
+ compact_write_offset(ctx, raw_data);
|
|
|
+ else
|
|
|
+ compact_write_ptr(ctx, NULL);
|
|
|
+ if( obj->values )
|
|
|
+ compact_write_offset(ctx, values_data);
|
|
|
+ else
|
|
|
+ compact_write_ptr(ctx, NULL);
|
|
|
+ compact_write_int(ctx,obj->nfields);
|
|
|
+ compact_write_int(ctx,obj->raw_size);
|
|
|
+ compact_write_int(ctx,obj->nvalues);
|
|
|
+# ifdef HL_64
|
|
|
+ compact_write_int(ctx,0);
|
|
|
+# endif
|
|
|
+ compact_write_ref(ctx,obj->virtuals,false);
|
|
|
+ if( obj->lookup )
|
|
|
+ compact_write_mem(ctx,obj->lookup,sizeof(hl_field_lookup) * obj->nfields);
|
|
|
+ if( obj->raw_data )
|
|
|
+ compact_write_mem(ctx,obj->raw_data,obj->raw_size);
|
|
|
+ if( obj->values ) {
|
|
|
+ compact_pad(ctx,&hlt_dyn);
|
|
|
+ for(i=0;i<obj->nvalues;i++) {
|
|
|
+ int j;
|
|
|
+ for(j=0;i<obj->nfields;j++) {
|
|
|
+ if( obj->lookup[j].field_index == i && hl_is_ptr(obj->lookup[j].t) ) {
|
|
|
+ compact_write_data(ctx, obj->lookup[j].t, obj->values + i);
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ int save_pos = ctx->todos_pos;
|
|
|
+ for(i=0;i<obj->nfields;i++) {
|
|
|
+ hl_field_lookup *f = obj->lookup + i;
|
|
|
+ int idx = compact_lookup_ref(ctx, hl_is_ptr(f->t) ? (char*)(obj->values + f->field_index) : (char*)(obj->raw_data + f->field_index), false);
|
|
|
+ idx = -idx-1;
|
|
|
+ ctx->remap_target[idx] = hl_is_ptr(f->t) ? values_data + sizeof(void*)*f->field_index : raw_data + f->field_index;
|
|
|
+ }
|
|
|
+ ctx->todos_pos = save_pos;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ case HARRAY: {
|
|
|
+ varray *a = (varray*)d;
|
|
|
+ compact_write_ptr(ctx, a->t);
|
|
|
+ compact_write_ptr(ctx, a->at);
|
|
|
+ compact_write_int(ctx, a->size);
|
|
|
+ compact_write_int(ctx, 0);
|
|
|
+ char *array_data = (char*)(a + 1);
|
|
|
+ int stride = hl_type_size(a->at);
|
|
|
+ for(i=0;i<a->size;i++) {
|
|
|
+ compact_write_data(ctx,a->at, array_data + stride * i);
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ case HENUM: {
|
|
|
+ venum *e = (venum*)d;
|
|
|
+ hl_enum_construct *c = &t->tenum->constructs[e->index];
|
|
|
+ int buf_start = ctx->buf_pos;
|
|
|
+ compact_write_ptr(ctx, e->t);
|
|
|
+ compact_write_int(ctx, e->index);
|
|
|
+ for(i=0;i<c->nparams;i++) {
|
|
|
+ compact_pad(ctx,c->params[i]);
|
|
|
+ compact_write_data(ctx,c->params[i],(char*)e+(ctx->buf_pos-buf_start));
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ default:
|
|
|
+ hl_error("Unsupported type %d", t->kind);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+HL_PRIM vdynamic *hl_mem_compact( vdynamic *d, varray *exclude, int flags, int *outCount ) {
|
|
|
+ mem_context _ctx;
|
|
|
+ mem_context *ctx = &_ctx;
|
|
|
+ int i;
|
|
|
+ int object_count = 0;
|
|
|
+ memset(ctx,0,sizeof(mem_context));
|
|
|
+ ctx->flags = flags;
|
|
|
+ compact_lookup_ref(ctx,d,false);
|
|
|
+ if( exclude ) {
|
|
|
+ for(i=0;i<exclude->size;i++) {
|
|
|
+ vdynamic *ptr = (vdynamic*)hl_aptr(exclude,void*)[i];
|
|
|
+ compact_lookup_ref(ctx,ptr,false);
|
|
|
+ ctx->todos_pos--;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ while( ctx->todos_pos > 0 ) {
|
|
|
+ void *addr = ctx->todos[--ctx->todos_pos];
|
|
|
+ int pos = compact_lookup_index(ctx, addr);
|
|
|
+ int index = ctx->lookup_index[pos];
|
|
|
+ compact_pad(ctx, &hlt_dyn);
|
|
|
+ ctx->remap_target[index&~BYTE_MARK] = ctx->buf_pos;
|
|
|
+ if( index & BYTE_MARK ) {
|
|
|
+ int size = hl_gc_get_memsize(addr);
|
|
|
+ if( size < 0 ) hl_error("assert");
|
|
|
+ compact_write_mem(ctx, addr, size);
|
|
|
+ } else
|
|
|
+ compact_write_content(ctx, (vdynamic*)addr);
|
|
|
+ object_count++;
|
|
|
+ }
|
|
|
+ vbyte *data = NULL;
|
|
|
+# ifdef HL_WIN
|
|
|
+ if( flags & 1 )
|
|
|
+ data = (vbyte*)VirtualAlloc(NULL,ctx->buf_pos,MEM_COMMIT|MEM_RESERVE,PAGE_READWRITE);
|
|
|
+# endif
|
|
|
+ if( data == NULL )
|
|
|
+ data = hl_gc_alloc_noptr(ctx->buf_pos);
|
|
|
+ memcpy(data,ctx->buf,ctx->buf_pos);
|
|
|
+ int exclude_count = exclude ? exclude->size : 0;
|
|
|
+ for(i=0;i<ctx->offsets_pos;i++) {
|
|
|
+ int pos = ctx->offsets[i];
|
|
|
+ int target = *(int*)(data + pos);
|
|
|
+ if( target < 0 ) {
|
|
|
+ int eid = -target-1;
|
|
|
+ if( eid > 0 && eid <= exclude_count ) {
|
|
|
+ *(void**)(data+pos) = hl_aptr(exclude,void*)[eid-1];
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ target = ctx->remap_target[eid];
|
|
|
+ }
|
|
|
+ *(void**)(data+pos) = data + target;
|
|
|
+ }
|
|
|
+ free(ctx->buf);
|
|
|
+ free(ctx->offsets);
|
|
|
+ free(ctx->lookup);
|
|
|
+ free(ctx->lookup_index);
|
|
|
+ free(ctx->remap_target);
|
|
|
+ free(ctx->todos);
|
|
|
+# ifdef HL_WIN
|
|
|
+ if( flags & 1 ) {
|
|
|
+ DWORD old = 0;
|
|
|
+ VirtualProtect(data,ctx->buf_pos,PAGE_READONLY,&old);
|
|
|
+ }
|
|
|
+# endif
|
|
|
+ if( outCount )
|
|
|
+ *outCount = object_count;
|
|
|
+ return (vdynamic*)data;
|
|
|
+}
|
|
|
+
|
|
|
+DEFINE_PRIM(_DYN, mem_compact, _DYN _ARR _I32 _REF(_I32));
|