]> git.xonotic.org Git - xonotic/gmqcc.git/blob - assembler.c
Fix memory leaks, more memory tracker stuff as well.
[xonotic/gmqcc.git] / assembler.c
1 /*
2  * Copyright (C) 2012 
3  *     Dale Weiler
4  *
5  * Permission is hereby granted, free of charge, to any person obtaining a copy of
6  * this software and associated documentation files (the "Software"), to deal in
7  * the Software without restriction, including without limitation the rights to
8  * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
9  * of the Software, and to permit persons to whom the Software is furnished to do
10  * so, subject to the following conditions:
11  *
12  * The above copyright notice and this permission notice shall be included in all
13  * copies or substantial portions of the Software.
14  *
15  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21  * SOFTWARE.
22  */
23 #include "gmqcc.h"
24 /*
25  * This is the assembler, gmqas, this is being implemented because I'm
26  * not exactly sure how codegen would work for the C compiler as of yet
27  * and also I plan to allow inline assembly for the compiler.
28  */
29 static const struct {
30     const char  *m; /* menomic     */
31     const size_t o; /* operands    */ 
32     const size_t l; /* menomic len */
33 } const asm_instr[] = {
34     [INSTR_DONE]       = { "DONE"      , 1, 4 },
35     [INSTR_MUL_F]      = { "MUL_F"     , 3, 5 },
36     [INSTR_MUL_V]      = { "MUL_V"     , 3, 5 },
37     [INSTR_MUL_FV]     = { "MUL_FV"    , 3, 6 },
38     [INSTR_MUL_VF]     = { "MUL_VF"    , 3, 6 },
39     [INSTR_DIV_F]      = { "DIV"       , 0, 3 },
40     [INSTR_ADD_F]      = { "ADD_F"     , 3, 5 },
41     [INSTR_ADD_V]      = { "ADD_V"     , 3, 5 },
42     [INSTR_SUB_F]      = { "SUB_F"     , 3, 5 },
43     [INSTR_SUB_V]      = { "DUB_V"     , 3, 5 },
44     [INSTR_EQ_F]       = { "EQ_F"      , 0, 4 },
45     [INSTR_EQ_V]       = { "EQ_V"      , 0, 4 },
46     [INSTR_EQ_S]       = { "EQ_S"      , 0, 4 },
47     [INSTR_EQ_E]       = { "EQ_E"      , 0, 4 },
48     [INSTR_EQ_FNC]     = { "ES_FNC"    , 0, 6 },
49     [INSTR_NE_F]       = { "NE_F"      , 0, 4 },
50     [INSTR_NE_V]       = { "NE_V"      , 0, 4 },
51     [INSTR_NE_S]       = { "NE_S"      , 0, 4 },
52     [INSTR_NE_E]       = { "NE_E"      , 0, 4 },
53     [INSTR_NE_FNC]     = { "NE_FNC"    , 0, 6 },
54     [INSTR_LE]         = { "LE"        , 0, 2 },
55     [INSTR_GE]         = { "GE"        , 0, 2 },
56     [INSTR_LT]         = { "LT"        , 0, 2 },
57     [INSTR_GT]         = { "GT"        , 0, 2 },
58     [INSTR_LOAD_F]     = { "FIELD_F"   , 0, 7 },
59     [INSTR_LOAD_V]     = { "FIELD_V"   , 0, 7 },
60     [INSTR_LOAD_S]     = { "FIELD_S"   , 0, 7 },
61     [INSTR_LOAD_ENT]   = { "FIELD_ENT" , 0, 9 },
62     [INSTR_LOAD_FLD]   = { "FIELD_FLD" , 0, 9 },
63     [INSTR_LOAD_FNC]   = { "FIELD_FNC" , 0, 9 },
64     [INSTR_ADDRESS]    = { "ADDRESS"   , 0, 7 },
65     [INSTR_STORE_F]    = { "STORE_F"   , 0, 7 },
66     [INSTR_STORE_V]    = { "STORE_V"   , 0, 7 },
67     [INSTR_STORE_S]    = { "STORE_S"   , 0, 7 },
68     [INSTR_STORE_ENT]  = { "STORE_ENT" , 0, 9 },
69     [INSTR_STORE_FLD]  = { "STORE_FLD" , 0, 9 },
70     [INSTR_STORE_FNC]  = { "STORE_FNC" , 0, 9 },
71     [INSTR_STOREP_F]   = { "STOREP_F"  , 0, 8 },
72     [INSTR_STOREP_V]   = { "STOREP_V"  , 0, 8 },
73     [INSTR_STOREP_S]   = { "STOREP_S"  , 0, 8 },
74     [INSTR_STOREP_ENT] = { "STOREP_ENT", 0, 10},
75     [INSTR_STOREP_FLD] = { "STOREP_FLD", 0, 10},
76     [INSTR_STOREP_FNC] = { "STOREP_FNC", 0, 10},
77     [INSTR_RETURN]     = { "RETURN"    , 0, 6 },
78     [INSTR_NOT_F]      = { "NOT_F"     , 0, 5 },
79     [INSTR_NOT_V]      = { "NOT_V"     , 0, 5 },
80     [INSTR_NOT_S]      = { "NOT_S"     , 0, 5 },
81     [INSTR_NOT_ENT]    = { "NOT_ENT"   , 0, 7 },
82     [INSTR_NOT_FNC]    = { "NOT_FNC"   , 0, 7 },
83     [INSTR_IF]         = { "IF"        , 0, 2 },
84     [INSTR_IFNOT]      = { "IFNOT"     , 0, 5 },
85     [INSTR_CALL0]      = { "CALL0"     , 0, 5 },
86     [INSTR_CALL1]      = { "CALL1"     , 0, 5 },
87     [INSTR_CALL2]      = { "CALL2"     , 0, 5 },
88     [INSTR_CALL3]      = { "CALL3"     , 0, 5 },
89     [INSTR_CALL4]      = { "CALL4"     , 0, 5 },
90     [INSTR_CALL5]      = { "CALL5"     , 0, 5 },
91     [INSTR_CALL6]      = { "CALL6"     , 0, 5 },
92     [INSTR_CALL7]      = { "CALL7"     , 0, 5 },
93     [INSTR_CALL8]      = { "CALL8"     , 0, 5 },
94     [INSTR_STATE]      = { "STATE"     , 0, 5 },
95     [INSTR_GOTO]       = { "GOTO"      , 0, 4 },
96     [INSTR_AND]        = { "AND"       , 0, 3 },
97     [INSTR_OR]         = { "OR"        , 0, 2 },
98     [INSTR_BITAND]     = { "BITAND"    , 0, 6 },
99     [INSTR_BITOR]      = { "BITOR"     , 0, 5 }
100 };
101
102 /*
103  * Some assembler keywords not part of the opcodes above: these are
104  * for creating functions, or constants.
105  */
106 const char *const asm_keys[] = {
107     "FLOAT"    , /* define float  */
108     "VECTOR"   , /* define vector */
109     "ENTITY"   , /* define ent    */
110     "FIELD"    , /* define field  */
111     "STRING"   , /* define string */
112     "FUNCTION"
113 };
114
115 static char *const asm_getline(size_t *byte, FILE *fp) {
116     char   *line = NULL;
117     ssize_t read = util_getline(&line, byte, fp);
118     *byte = read;
119     if (read == -1) {
120         mem_d (line);
121         return NULL;
122     }
123     return line;
124 }
125
126 #define asm_rmnewline(L,S) *((L)+*(S)-1) = '\0'
127 #define asm_skipwhite(L)             \
128     while((*(L)==' '||*(L)=='\t')) { \
129         (L)++;                       \
130     }
131     
132 void asm_init(const char *file, FILE **fp) {
133     *fp = fopen(file, "r");
134     code_init();
135 }
136
137 void asm_close(FILE *fp) {
138     fclose(fp);
139     code_write();
140 }
141
142 /*
143  * Following parse states:
144  *     ASM_FUNCTION -- in a function accepting input statements
145  *     ....
146  */
147 typedef enum {
148     ASM_NULL,
149     ASM_FUNCTION
150 } asm_state;
151
152 typedef struct {
153     char *name;   /* name of constant    */
154     int   offset; /* location in globals */
155 } globals;
156 VECTOR_MAKE(globals, assembly_constants);
157
158 void asm_clear() {
159         size_t i = 0;
160         for (; i < assembly_constants_elements; i++)
161                 mem_d(assembly_constants_data[i].name);
162         mem_d(assembly_constants_data);
163 }
164     
165 void asm_parse(FILE *fp) {
166     char     *data  = NULL;
167     char     *skip  = NULL;
168     long      line  = 1; /* current line */
169     size_t    size  = 0; /* size of line */
170     asm_state state = ASM_NULL;
171     
172     while ((data = asm_getline(&size, fp)) != NULL) {
173         skip = data;
174         asm_skipwhite(skip);
175         asm_rmnewline(skip, &size);
176         
177         #define DECLTYPE(X, CODE)                                         \
178             if (!strncmp(X, skip, strlen(X))) {                           \
179                 if (skip[strlen(X)] != ':') {                             \
180                     printf("%li: Missing `:` after decltype\n",line);     \
181                     exit (1);                                             \
182                 }                                                         \
183                 skip += strlen(X)+1;                                      \
184                 asm_skipwhite(skip);                                      \
185                 if(!isalpha(*skip)) {                                     \
186                     printf("%li: Invalid identififer: %s\n", line, skip); \
187                     exit (1);                                             \
188                 } else {                                                  \
189                     size_t offset_code      = code_statements_elements+1; \
190                     size_t offset_chars     = code_strings_elements   +1; \
191                     size_t offset_globals   = code_globals_elements   +1; \
192                     size_t offset_functions = code_functions_elements +1; \
193                     size_t offset_fields    = code_fields_elements    +1; \
194                     size_t offset_defs      = code_defs_elements      +1; \
195                     CODE                                                  \
196                     /* silent unused warnings */                          \
197                     (void)offset_code;                                    \
198                     (void)offset_chars;                                   \
199                     (void)offset_globals;                                 \
200                     (void)offset_functions;                               \
201                     (void)offset_fields;                                  \
202                     (void)offset_defs;                                    \
203                     assembly_constants_add((globals){                     \
204                         .name   = util_strdup(skip),                      \
205                         .offset = offset_globals                          \
206                     });                                                   \
207                 }                                                         \
208                 goto end;                                                 \
209             }
210         
211         /* FLOAT    */
212         DECLTYPE(asm_keys[0], {
213             code_defs_add((prog_section_def){
214                 .type   = TYPE_FLOAT,
215                 .offset = offset_globals, /* global table */
216                 .name   = offset_chars    /* string table TODO */
217             });
218             float f = 0; /*TODO*/
219             code_globals_add(*(int*)&f);
220             
221         });
222         DECLTYPE(asm_keys[1], {
223             code_defs_add((prog_section_def){
224                 .type   = TYPE_FLOAT,
225                 .offset = offset_globals, /* global table */
226                 .name   = offset_chars    /* string table TODO */
227             });
228             float f1 = 0;
229             float f2 = 0;
230             float f3 = 0;
231             code_globals_add(*(int*)&f1);
232             code_globals_add(*(int*)&f2);
233             code_globals_add(*(int*)&f3);
234         });
235         /* ENTITY   */ DECLTYPE(asm_keys[2], {});
236         /* FIELD    */ DECLTYPE(asm_keys[3], {});
237         /* STRING   */
238         DECLTYPE(asm_keys[4], {
239             code_defs_add((prog_section_def){
240                 .type   = TYPE_STRING,    
241                 .offset = offset_globals, /* offset to offset in string table (for data)*/
242                 .name   = offset_chars    /* location of name in string table (for name)*/
243             });
244             code_strings_add('h');
245         });
246         /* FUNCTION */
247         DECLTYPE(asm_keys[5], {
248             /* TODO: parse */
249             if (state != ASM_NULL) {
250                 printf("%li: Error unfinished function block, expected DONE or RETURN\n", line);
251                 goto end;
252             }
253             
254             state = ASM_FUNCTION;
255             code_defs_add((prog_section_def){
256                 .type   = TYPE_VOID,
257                 .offset = offset_globals,
258                 .name   = offset_chars
259             });
260             code_globals_add(offset_functions);
261             code_functions_add((prog_section_function){
262                 .entry      =  offset_code,      
263                 .firstlocal =  0,
264                 .locals     =  0,
265                 .profile    =  0,
266                 .name       =  offset_chars,
267                 .file       =  0,
268                 .nargs      =  0,
269                 .argsize    = {0}
270             });
271         });
272         
273         /* if we make it this far then we have statements */
274         {
275             size_t i = 0;    /* counter   */
276             size_t o = 0;    /* operands  */
277             char  *t = NULL; /* token     */
278             
279             /*
280              * Most ops a single statement can have is three.
281              * lets allocate some space for all of those here.
282              */
283             char op[3][32768] = {{0},{0},{0}};
284             for (; i < sizeof(asm_instr)/sizeof(*asm_instr); i++) {
285                 if (!strncmp(skip, asm_instr[i].m, asm_instr[i].l)) {
286                     if (state != ASM_FUNCTION) {
287                         printf("%li: Statement not inside function block\n", line);
288                         goto end;
289                     }
290                     
291                     /* update parser state */
292                     if (i == INSTR_DONE || i == INSTR_RETURN) {
293                         goto end;
294                         state = ASM_NULL;
295                     }
296                     
297                     /* parse the statement */
298                     o     = asm_instr[i].o; /* operands         */
299                     skip += asm_instr[i].l; /* skip instruction */
300                     t     = strtok(skip, " ,");
301                     i     = 0;
302                     while (t != NULL && i < 3) {
303                         strcpy(op[i], t);
304                         t = strtok(NULL, " ,");
305                         i ++;
306                     }
307                     
308                     util_debug("ASM", "Operand 1: %s\n", util_strrnl(op[0]));
309                     util_debug("ASM", "Operand 2: %s\n", util_strrnl(op[1]));
310                     util_debug("ASM", "Operand 3: %s\n", util_strrnl(op[2]));
311                     
312                     /* check */
313                     if (i != o) {
314                         printf("not enough operands, expected: %li, got %li\n", o, i);
315                     }
316                     
317                     /* TODO: hashtable value LOAD .... etc */
318                     code_statements_add((prog_section_statement){
319                         i, {0}, {0}, {0}
320                     });
321                     goto end;
322                 }
323             }
324         }
325         
326         /* if we made it this far something is wrong */
327         if (*skip != '\0')
328             printf("%li: Invalid statement, expression, or decleration\n", line);
329         
330         end:
331         mem_d(data);
332         line ++;
333     }
334     asm_clear();
335 }