1 #ifndef LOCAL_STACK_H
2 #define LOCAL_STACK_H
3 
4 #include "lib/mlrval.h"
5 #include "containers/type_decl.h"
6 #include "containers/sllv.h"
7 #include "containers/mlhmmv.h"
8 
9 // ================================================================
10 // Bound & scoped variables for use in for-loops, function bodies, and
11 // subroutine bodies. Indices of local variables, and max-depth for top-level
12 // statement blocks, are compted by the stack-allocator which marks up the AST
13 // before the CST is built from it.
14 //
15 // A convention shared between the stack-allocator and this data structure is
16 // that slot 0 is an absent-null which is used for reads of undefined (or
17 // as-yet-undefined) local variables.
18 //
19 // Values assigned to a local-stack variable are owned by this container.
20 // They will be freed:
21 // * On overwrite, e.g. on 'x = oldval' then 'x = newval' the oldval
22 //   will be freed on the newval assignment, and
23 // * At stack-frame exit.
24 // For this reason values assigned to locals may be passed in by reference
25 // if they are ephemeral, i.e. if it is desired for this container to free
26 // them. Otherwise, values should be copied before being passed in.
27 // ================================================================
28 
29 // ================================================================
30 typedef struct _local_stack_frame_entry_t {
31 	char* name; // For type-check error messages. Not strduped; the caller must ensure extent.
32 	mlhmmv_xvalue_t xvalue;
33 	int   type_mask;
34 } local_stack_frame_entry_t;
35 
36 typedef struct _local_stack_frame_t {
37 	int in_use;
38 	int ephemeral;
39 	int size;
40 	int subframe_base;
41 	local_stack_frame_entry_t* pvars;
42 } local_stack_frame_t;
43 
44 // ----------------------------------------------------------------
45 // A stack is allocated for a top-level statement block: begin, end, or main, or
46 // user-defined function/subroutine. (The latter two may be called recursively
47 // in which case the in_use flag notes the need to allocate a new stack.)
48 
49 local_stack_frame_t* local_stack_frame_alloc(int size);
50 void local_stack_frame_free(local_stack_frame_t* pframe);
51 
52 // ================================================================
53 //#define LOCAL_STACK_TRACE_ENABLE
54 //#define LOCAL_STACK_BOUNDS_CHECK_ENABLE
55 
56 void local_stack_bounds_check(local_stack_frame_t* pframe, char* op, int set, int vardef_frame_relative_index);
57 #ifdef LOCAL_STACK_BOUNDS_CHECK_ENABLE
58 #define LOCAL_STACK_BOUNDS_CHECK(pframe, op, set, vardef_frame_relative_index) \
59 	local_stack_bounds_check((pframe), (op), (set), (vardef_frame_relative_index))
60 #else
61 #define LOCAL_STACK_BOUNDS_CHECK(pframe, op, set, vardef_frame_relative_index)
62 #endif
63 
64 #ifdef LOCAL_STACK_TRACE_ENABLE
65 #define LOCAL_STACK_TRACE(p) p
66 #else
67 #define LOCAL_STACK_TRACE(p)
68 #endif
69 
70 // These are unconditional. With the single added character 'X' they can be
71 // used to focus verbosity at specific callsites for dev/debug.
72 #define LOCAL_STACK_BOUNDS_CHECKX(pframe, op, set, vardef_frame_relative_index) \
73 	local_stack_bounds_check((pframe), (op), (set), (vardef_frame_relative_index))
74 
75 #define LOCAL_STACK_TRACEX(p) p
76 
77 // ----------------------------------------------------------------
78 // Sets/clears the in-use flag for top-level statement blocks, and verifies the
79 // contract for absent-null at slot 0.
80 
81 // For non-recursive functions/subroutines the enter method sets the in-use flag
82 // and returns its argument; the exit method clears that flag. For recursively
83 // invoked functions/subroutines the enter method returns another stack of the
84 // same size, and the exit method frees that.
85 //
86 // The reason we don't simply always allocate is that begin/main/end statements
87 // are never recursive, and most functions and subroutines are not recursive, so
88 // most of the time there will be a single frame for each. We allocate that once
89 // at startup, reuse it on every record, and free it at exit -- rather than
90 // allocating and freeing frames on every record.
91 
92 local_stack_frame_t* local_stack_frame_enter(local_stack_frame_t* pframe);
93 void local_stack_frame_exit(local_stack_frame_t* pframe);
94 void local_stack_frame_throw_type_mismatch_for_write(local_stack_frame_entry_t* pentry, mv_t* pval);
95 void local_stack_frame_throw_type_xmismatch_for_write(local_stack_frame_entry_t* pentry, mlhmmv_xvalue_t* pxval);
96 void local_stack_frame_throw_type_mismatch_for_read(local_stack_frame_entry_t* pentry);
97 void local_stack_frame_throw_type_xmismatch_for_read(local_stack_frame_entry_t* pentry);
98 
99 // ----------------------------------------------------------------
local_stack_frame_get_terminal_from_nonindexed(local_stack_frame_t * pframe,int vardef_frame_relative_index)100 static inline mv_t local_stack_frame_get_terminal_from_nonindexed(local_stack_frame_t* pframe, // move to reference semantics
101 	int vardef_frame_relative_index)
102 {
103 	LOCAL_STACK_TRACE(printf("LOCAL STACK FRAME %p GET %d\n", pframe, vardef_frame_relative_index));
104 	LOCAL_STACK_BOUNDS_CHECK(pframe, "GET", FALSE, vardef_frame_relative_index);
105 	local_stack_frame_entry_t* pentry = &pframe->pvars[vardef_frame_relative_index];
106 	mlhmmv_xvalue_t* pvalue = &pentry->xvalue;
107 	if (pvalue != NULL && pvalue->is_terminal) {
108 		return pvalue->terminal_mlrval;
109 	} else {
110 		return mv_absent();
111 	}
112 }
113 
114 // ----------------------------------------------------------------
local_stack_frame_assign_terminal_nonindexed(local_stack_frame_t * pframe,int vardef_frame_relative_index,mv_t val)115 static inline void local_stack_frame_assign_terminal_nonindexed(local_stack_frame_t* pframe,
116 	int vardef_frame_relative_index, mv_t val)
117 {
118 	LOCAL_STACK_TRACE(printf("LOCAL STACK FRAME %p SET %d\n", pframe, vardef_frame_relative_index));
119 	LOCAL_STACK_BOUNDS_CHECK(pframe, "ASSIGN", TRUE, vardef_frame_relative_index);
120 	local_stack_frame_entry_t* pentry = &pframe->pvars[vardef_frame_relative_index];
121 
122 	if (!(type_mask_from_mv(&val) & pentry->type_mask)) {
123 		local_stack_frame_throw_type_mismatch_for_write(pentry, &val);
124 	}
125 
126 	mlhmmv_xvalue_free(&pentry->xvalue);
127 	pentry->xvalue = mlhmmv_xvalue_wrap_terminal(val); // xxx deep-copy?
128 }
129 
130 // ----------------------------------------------------------------
local_stack_frame_ref_extended_from_nonindexed(local_stack_frame_t * pframe,int vardef_frame_relative_index)131 static inline mlhmmv_xvalue_t* local_stack_frame_ref_extended_from_nonindexed(local_stack_frame_t* pframe,
132 	int vardef_frame_relative_index)
133 {
134 	LOCAL_STACK_TRACE(printf("LOCAL STACK FRAME %p GET %d\n", pframe, vardef_frame_relative_index));
135 	LOCAL_STACK_BOUNDS_CHECK(pframe, "GET", FALSE, vardef_frame_relative_index);
136 
137 	local_stack_frame_entry_t* pentry = &pframe->pvars[vardef_frame_relative_index];
138 	mlhmmv_xvalue_t* pmvalue = &pentry->xvalue;
139 
140 	return pmvalue;
141 }
142 
143 // ----------------------------------------------------------------
144 mv_t local_stack_frame_ref_terminal_from_indexed(local_stack_frame_t* pframe,
145 	int vardef_frame_relative_index, sllmv_t* pmvkeys);
146 
147 mlhmmv_xvalue_t* local_stack_frame_ref_extended_from_indexed(local_stack_frame_t* pframe,
148 	int vardef_frame_relative_index, sllmv_t* pmvkeys);
149 
150 void local_stack_frame_define_terminal(local_stack_frame_t* pframe, char* variable_name,
151 	int vardef_frame_relative_index, int type_mask, mv_t val);
152 
153 void local_stack_frame_define_extended(local_stack_frame_t* pframe, char* variable_name,
154 	int vardef_frame_relative_index, int type_mask, mlhmmv_xvalue_t xval);
155 
156 void local_stack_frame_assign_extended_nonindexed(local_stack_frame_t* pframe,
157 	int vardef_frame_relative_index, mlhmmv_xvalue_t xval);
158 
159 void local_stack_frame_assign_terminal_indexed(local_stack_frame_t* pframe,
160 	int vardef_frame_relative_index, sllmv_t* pmvkeys,
161 	mv_t terminal_value);
162 
163 void local_stack_frame_assign_extended_indexed(local_stack_frame_t* pframe,
164 	int vardef_frame_relative_index, sllmv_t* pmvkeys,
165 	mlhmmv_xvalue_t terminal_value);
166 
167 // ----------------------------------------------------------------
168 // Frames are entered/exited for each curly-braced statement block, including
169 // the top-level block itself as well as ifs/fors/whiles.
170 
local_stack_subframe_enter(local_stack_frame_t * pframe,int count)171 static inline void local_stack_subframe_enter(local_stack_frame_t* pframe, int count) {
172 	LOCAL_STACK_TRACE(printf("LOCAL STACK SUBFRAME %p ENTER %d->%d\n",
173 		pframe, pframe->subframe_base, pframe->subframe_base+count));
174 	local_stack_frame_entry_t* psubframe = &pframe->pvars[pframe->subframe_base];
175 	for (int i = 0; i < count; i++) {
176 		LOCAL_STACK_TRACE(printf("LOCAL STACK FRAME %p CLEAR %d\n", pframe, pframe->subframe_base+i));
177 		LOCAL_STACK_BOUNDS_CHECK(pframe, "CLEAR", FALSE, pframe->subframe_base+i);
178 		local_stack_frame_entry_t* pentry = &psubframe[i];
179 
180 		mlhmmv_xvalue_reset(&pentry->xvalue);
181 
182 		pentry->type_mask = TYPE_MASK_ANY;
183 	}
184 	pframe->subframe_base += count;
185 }
186 
187 // ----------------------------------------------------------------
local_stack_subframe_exit(local_stack_frame_t * pframe,int count)188 static inline void local_stack_subframe_exit(local_stack_frame_t* pframe, int count) {
189 	LOCAL_STACK_TRACE(printf("LOCAL STACK SUBFRAME %p EXIT  %d->%d\n",
190 		pframe, pframe->subframe_base, pframe->subframe_base-count));
191 	pframe->subframe_base -= count;
192 	local_stack_frame_entry_t* psubframe = &pframe->pvars[pframe->subframe_base];
193 	for (int i = 0; i < count; i++) {
194 		local_stack_frame_entry_t* pentry = &psubframe[i];
195 		mlhmmv_xvalue_free(&pentry->xvalue);
196 	}
197 }
198 
199 // ================================================================
200 typedef struct _local_stack_t {
201 	sllv_t* pframes;
202 } local_stack_t;
203 
204 local_stack_t* local_stack_alloc();
205 void local_stack_free(local_stack_t* pstack);
206 
207 void local_stack_push(local_stack_t* pstack, local_stack_frame_t* pframe);
208 
209 local_stack_frame_t* local_stack_pop(local_stack_t* pstack);
210 
local_stack_get_top_frame(local_stack_t * pstack)211 static inline local_stack_frame_t* local_stack_get_top_frame(local_stack_t* pstack) {
212 	return pstack->pframes->phead->pvvalue;
213 }
214 
215 #endif // LOCAL_STACK_H
216