]> ocean-lang.org Git - ocean/commitdiff
oceani: move variable values to a stack frame.
authorNeilBrown <neil@brown.name>
Sun, 17 Oct 2021 10:03:01 +0000 (21:03 +1100)
committerNeilBrown <neil@brown.name>
Sat, 30 Oct 2021 22:19:48 +0000 (09:19 +1100)
We have two frames - one for global values (currently always constant)
and one for local variables.
When we get functions, the local variable frame will be managed with a
stack of frames.

Signed-off-by: NeilBrown <neil@brown.name>
csrc/oceani.mdc

index af2e8b958b329a4c76495fa2fba9721a52d5911a..879aa6e38824564dd0b915d323e72931924f4be7 100644 (file)
@@ -534,22 +534,6 @@ Named type are stored in a simple linked list.  Objects of each type are
                        printf("*Unknown*");            // NOTEST
        }
 
                        printf("*Unknown*");            // NOTEST
        }
 
-       static struct value *val_alloc(struct parse_context *c, struct type *t,
-                                      struct value *init)
-       {
-               struct value *ret;
-
-               if (t->prepare_type)
-                       t->prepare_type(c, t, 0);
-
-               ret = calloc(1, t->size);
-               if (init)
-                       memcpy(ret, init, t->size);
-               else
-                       val_init(t, ret);
-               return ret;
-       }
-
 ###### forward decls
 
        static void free_value(struct type *type, struct value *v);
 ###### forward decls
 
        static void free_value(struct type *type, struct value *v);
@@ -821,7 +805,6 @@ cannot nest, so a declaration while a name is in-scope is an error.
        struct variable {
                struct variable *previous;
                struct type *type;
        struct variable {
                struct variable *previous;
                struct type *type;
-               struct value *val;
                struct binding *name;
                struct exec *where_decl;// where name was declared
                struct exec *where_set; // where type was set
                struct binding *name;
                struct exec *where_decl;// where name was declared
                struct exec *where_set; // where type was set
@@ -971,6 +954,11 @@ recent instance.  These variables don't really belong in the
 is found.  Instead, they are detected and ignored when considering the
 list of in_scope names.
 
 is found.  Instead, they are detected and ignored when considering the
 list of in_scope names.
 
+The storage of the value of a variable will be described later.  For now
+we just need to know that when a variable goes out of scope, it might
+need to be freed.  For this we need to be able to find it, so assume that 
+`var_value()` will provide that.
+
 ###### variable fields
        struct variable *merged;
 
 ###### variable fields
        struct variable *merged;
 
@@ -993,6 +981,9 @@ list of in_scope names.
                        }
        }
 
                        }
        }
 
+###### forward decls
+       static struct value *var_value(struct parse_context *c, struct variable *v);
+
 ###### free context vars
 
        while (context.varlist) {
 ###### free context vars
 
        while (context.varlist) {
@@ -1004,8 +995,7 @@ list of in_scope names.
                        struct variable *t = v;
 
                        v = t->previous;
                        struct variable *t = v;
 
                        v = t->previous;
-                       free_value(t->type, t->val);
-                       free(t->val);
+                       free_value(t->type, var_value(&context, t));
                        if (t->depth == 0)
                                // This is a global constant
                                free_exec(t->where_decl);
                        if (t->depth == 0)
                                // This is a global constant
                                free_exec(t->where_decl);
@@ -1085,7 +1075,6 @@ all pending-scope variables become conditionally scoped.
                v->scope = InScope;
                v->in_scope = c->in_scope;
                c->in_scope = v;
                v->scope = InScope;
                v->in_scope = c->in_scope;
                c->in_scope = v;
-               v->val = NULL;
                return v;
        }
 
                return v;
        }
 
@@ -1194,6 +1183,112 @@ all pending-scope variables become conditionally scoped.
                }
        }
 
                }
        }
 
+#### Storing Values
+
+The value of a variable is store separately from the variable, on an
+analogue of a stack frame.  There are (currently) two frames that can be
+active.  A global frame which currently only stores constants, and a
+stacked frame which stores local variables.  Each variable knows if it
+is global or not, and what its index into the frame is.
+
+Values in the global frame are known immediately they are relevant, so
+the frame needs to be reallocated as it grows so it can store those
+values.  The local frame doesn't get values until the interpreted phase
+is started, so there is no need to allocate until the size is known.
+
+###### variable fields
+               short frame_pos;
+               short global;
+
+###### parse context
+
+       short global_size, global_alloc;
+       short local_size;
+       void *global, *local;
+
+###### ast functions
+
+       static struct value *var_value(struct parse_context *c, struct variable *v)
+       {
+               if (!v->global) {
+                       if (!c->local || !v->type)
+                               return NULL;
+                       if (v->frame_pos + v->type->size > c->local_size) {
+                               printf("INVALID frame_pos\n"); // NOTEST
+                               exit(2);
+                       }
+                       return c->local + v->frame_pos;
+               }
+               if (c->global_size > c->global_alloc) {
+                       int old = c->global_alloc;
+                       c->global_alloc = (c->global_size | 1023) + 1024;
+                       c->global = realloc(c->global, c->global_alloc);
+                       memset(c->global + old, 0, c->global_alloc - old);
+               }
+               return c->global + v->frame_pos;
+       }
+
+       static struct value *global_alloc(struct parse_context *c, struct type *t,
+                                         struct variable *v, struct value *init)
+       {
+               struct value *ret;
+               struct variable scratch;
+
+               if (t->prepare_type)
+                       t->prepare_type(c, t, 1);
+
+               if (c->global_size & (t->align - 1))
+                       c->global_size = (c->global_size + t->align) & ~(t->align-1);
+               if (!v) {
+                       v = &scratch;
+                       v->type = t;
+               }
+               v->frame_pos = c->global_size;
+               v->global = 1;
+               c->global_size += v->type->size;
+               ret = var_value(c, v);
+               if (init)
+                       memcpy(ret, init, t->size);
+               else
+                       val_init(t, ret);
+               return ret;
+       }
+
+As global values are found -- struct field initializers, labels etc --
+`global_alloc()` is called to record the value in the global frame.
+
+When the program is fully parsed, we need to walk the list of variables
+to find any that weren't merged away and that aren't global, and to
+calculate the frame size and assign a frame position for each variable.
+For this we have `scope_finalize()`.
+
+###### ast functions
+
+       static void scope_finalize(struct parse_context *c)
+       {
+               struct binding *b;
+
+               for (b = c->varlist; b; b = b->next) {
+                       struct variable *v;
+                       for (v = b->var; v; v = v->previous) {
+                               struct type *t = v->type;
+                               if (v->merged && v->merged != v)
+                                       continue;
+                               if (v->global)
+                                       continue;
+                               if (c->local_size & (t->align - 1))
+                                       c->local_size = (c->local_size + t->align) & ~(t->align-1);
+                               v->frame_pos = c->local_size;
+                               c->local_size += v->type->size;
+                       }
+               }
+               c->local = calloc(1, c->local_size);
+       }
+
+###### free context vars
+       free(context.global);
+       free(context.local);
+
 ### Executables
 
 Executables can be lots of different things.  In many cases an
 ### Executables
 
 Executables can be lots of different things.  In many cases an
@@ -1536,13 +1631,14 @@ with a const size by whether they are prepared at parse time or not.
        static void array_prepare_type(struct parse_context *c, struct type *type,
                                       int parse_time)
        {
        static void array_prepare_type(struct parse_context *c, struct type *type,
                                       int parse_time)
        {
+               struct value *vsize;
                mpz_t q;
                if (!type->array.vsize || type->array.static_size)
                        return;
 
                mpz_t q;
                if (!type->array.vsize || type->array.static_size)
                        return;
 
+               vsize = var_value(c, type->array.vsize);
                mpz_init(q);
                mpz_init(q);
-               mpz_tdiv_q(q, mpq_numref(type->array.vsize->val->num),
-                          mpq_denref(type->array.vsize->val->num));
+               mpz_tdiv_q(q, mpq_numref(vsize->num), mpq_denref(vsize->num));
                type->array.size = mpz_get_si(q);
                mpz_clear(q);
 
                type->array.size = mpz_get_si(q);
                mpz_clear(q);
 
@@ -1828,7 +1924,6 @@ function will be needed.
                        if (t->structure.fields[i].init) {
                                free_value(t->structure.fields[i].type,
                                           t->structure.fields[i].init);
                        if (t->structure.fields[i].init) {
                                free_value(t->structure.fields[i].type,
                                           t->structure.fields[i].init);
-                               free(t->structure.fields[i].init);
                        }
                free(t->structure.fields);
        }
                        }
                free(t->structure.fields);
        }
@@ -2014,7 +2109,7 @@ function will be needed.
                                c->parse_error = 1;
                        else {
                                struct value vl = interp_exec(c, $5, NULL);
                                c->parse_error = 1;
                        else {
                                struct value vl = interp_exec(c, $5, NULL);
-                               $0->f.init = val_alloc(c, $0->f.type, &vl);
+                               $0->f.init = global_alloc(c, $0->f.type, NULL, &vl);
                        }
                } }$
                | IDENTIFIER : Type ${
                        }
                } }$
                | IDENTIFIER : Type ${
@@ -2272,7 +2367,6 @@ link to find the primary instance.
                        v->where_decl = $0;
                        v->where_set = $0;
                        v->type = $<Type;
                        v->where_decl = $0;
                        v->where_set = $0;
                        v->type = $<Type;
-                       v->val = NULL;
                } else {
                        v = var_ref(c, $1.txt);
                        $0->var = v;
                } else {
                        v = var_ref(c, $1.txt);
                        $0->var = v;
@@ -2290,7 +2384,6 @@ link to find the primary instance.
                        v->where_decl = $0;
                        v->where_set = $0;
                        v->type = $<Type;
                        v->where_decl = $0;
                        v->where_set = $0;
                        v->type = $<Type;
-                       v->val = NULL;
                        v->constant = 1;
                } else {
                        v = var_ref(c, $1.txt);
                        v->constant = 1;
                } else {
                        v = var_ref(c, $1.txt);
@@ -2310,7 +2403,6 @@ link to find the primary instance.
                        /* This might be a label - allocate a var just in case */
                        v = var_decl(c, $1.txt);
                        if (v) {
                        /* This might be a label - allocate a var just in case */
                        v = var_decl(c, $1.txt);
                        if (v) {
-                               v->val = NULL;
                                v->type = Tnone;
                                v->where_decl = $0;
                                v->where_set = $0;
                                v->type = Tnone;
                                v->where_decl = $0;
                                v->where_set = $0;
@@ -2381,7 +2473,6 @@ link to find the primary instance.
                if (v->type == NULL) {
                        if (type && *ok != 0) {
                                v->type = type;
                if (v->type == NULL) {
                        if (type && *ok != 0) {
                                v->type = type;
-                               v->val = NULL;
                                v->where_set = prog;
                                *ok = 2;
                        }
                                v->where_set = prog;
                                *ok = 2;
                        }
@@ -2406,7 +2497,7 @@ link to find the primary instance.
 
                if (v->merged)
                        v = v->merged;
 
                if (v->merged)
                        v = v->merged;
-               lrv = v->val;
+               lrv = var_value(c, v);
                rvtype = v->type;
                break;
        }
                rvtype = v->type;
                break;
        }
@@ -3414,16 +3505,19 @@ it is declared, and error will be raised as the name is created as
        case Declare:
        {
                struct variable *v = cast(var, b->left)->var;
        case Declare:
        {
                struct variable *v = cast(var, b->left)->var;
+               struct value *val;
                if (v->merged)
                        v = v->merged;
                if (v->merged)
                        v = v->merged;
-               free_value(v->type, v->val);
-               free(v->val);
+               val = var_value(c, v);
+               free_value(v->type, val);
+               if (v->type->prepare_type)
+                       v->type->prepare_type(c, v->type, 0);
                if (b->right) {
                        right = interp_exec(c, b->right, &rtype);
                if (b->right) {
                        right = interp_exec(c, b->right, &rtype);
-                       v->val = val_alloc(c, v->type, &right);
+                       memcpy(val, &right, rtype->size);
                        rtype = Tnone;
                } else {
                        rtype = Tnone;
                } else {
-                       v->val = val_alloc(c, v->type, NULL);
+                       val_init(v->type, val);
                }
                break;
        }
                }
                break;
        }
@@ -3450,9 +3544,11 @@ function.
                        struct var *v = cast(var, $0->right);
                        if (v->var->type == Tnone) {
                                /* Convert this to a label */
                        struct var *v = cast(var, $0->right);
                        if (v->var->type == Tnone) {
                                /* Convert this to a label */
+                               struct value *val;
+
                                v->var->type = Tlabel;
                                v->var->type = Tlabel;
-                               v->var->val = val_alloc(c, Tlabel, NULL);
-                               v->var->val->label = v->var->val;
+                               val = global_alloc(c, Tlabel, v->var, NULL);
+                               val->label = val;
                        }
                }
        }$
                        }
                }
        }$
@@ -4059,7 +4155,7 @@ searching through for the Nth constant for decreasing N.
                        c->parse_error = 1;
                else if (v) {
                        struct value res = interp_exec(c, $5, &v->type);
                        c->parse_error = 1;
                else if (v) {
                        struct value res = interp_exec(c, $5, &v->type);
-                       v->val = val_alloc(c, v->type, &res);
+                       global_alloc(c, v->type, v, &res);
                }
        } }$
 
                }
        } }$
 
@@ -4082,12 +4178,13 @@ searching through for the Nth constant for decreasing N.
                                        printf("const\n");
                                target = i;
                        } else {
                                        printf("const\n");
                                target = i;
                        } else {
+                               struct value *val = var_value(&context, v);
                                printf("    %.*s :: ", v->name->name.len, v->name->name.txt);
                                type_print(v->type, stdout);
                                printf(" = ");
                                if (v->type == Tstr)
                                        printf("\"");
                                printf("    %.*s :: ", v->name->name.len, v->name->name.txt);
                                type_print(v->type, stdout);
                                printf(" = ");
                                if (v->type == Tstr)
                                        printf("\"");
-                               print_value(v->type, v->val);
+                               print_value(v->type, val);
                                if (v->type == Tstr)
                                        printf("\"");
                                printf("\n");
                                if (v->type == Tstr)
                                        printf("\"");
                                printf("\n");
@@ -4192,7 +4289,6 @@ analysis is a bit more interesting at this level.
                        if (!v->var->type) {
                                v->var->where_set = b;
                                v->var->type = Tstr;
                        if (!v->var->type) {
                                v->var->where_set = b;
                                v->var->type = Tstr;
-                               v->var->val = NULL;
                        }
                }
                b = cast(binode, prog);
                        }
                }
                b = cast(binode, prog);
@@ -4205,7 +4301,10 @@ analysis is a bit more interesting at this level.
 
                /* Make sure everything is still consistent */
                propagate_types(b->right, c, &ok, Tnone, 0);
 
                /* Make sure everything is still consistent */
                propagate_types(b->right, c, &ok, Tnone, 0);
-               return !!ok;
+               if (!ok)
+                       return 0;
+               scope_finalize(c);
+               return 1;
        }
 
        static void interp_prog(struct parse_context *c, struct exec *prog, char **argv)
        }
 
        static void interp_prog(struct parse_context *c, struct exec *prog, char **argv)
@@ -4220,7 +4319,7 @@ analysis is a bit more interesting at this level.
                al = cast(binode, p->left);
                while (al) {
                        struct var *v = cast(var, al->left);
                al = cast(binode, p->left);
                while (al) {
                        struct var *v = cast(var, al->left);
-                       struct value *vl = v->var->val;
+                       struct value *vl = var_value(c, v->var);
                        struct value arg;
 
                        if (argv[0] == NULL) {
                        struct value arg;
 
                        if (argv[0] == NULL) {
@@ -4231,10 +4330,6 @@ analysis is a bit more interesting at this level.
                                printf("Arg not string!!\n"); // NOTEST
                                exit(2);                      // NOTEST
                        }
                                printf("Arg not string!!\n"); // NOTEST
                                exit(2);                      // NOTEST
                        }
-                       if (!vl) {
-                               vl = val_alloc(c, v->var->type, NULL);
-                               v->var->val = vl;
-                       }
 
                        arg.str.txt = argv[0];
                        arg.str.len = strlen(argv[0]);
 
                        arg.str.txt = argv[0];
                        arg.str.len = strlen(argv[0]);