mirror of
https://github.com/DarkPlacesEngine/gmqcc.git
synced 2025-02-17 09:02:25 +00:00
Merge branch 'irgen'
This commit is contained in:
commit
04cf1d549f
12 changed files with 591 additions and 78 deletions
20
Makefile
20
Makefile
|
@ -1,5 +1,21 @@
|
|||
CC ?= clang
|
||||
CFLAGS += -Wall -I. -pedantic-errors -std=c90 -Wno-attributes
|
||||
CFLAGS += -Wall -I. -pedantic-errors -std=c90
|
||||
|
||||
#turn on tons of warnings if clang is present
|
||||
ifeq ($(CC), clang)
|
||||
CFLAGS += \
|
||||
-Weverything \
|
||||
-Wno-missing-prototypes \
|
||||
-Wno-unused-parameter \
|
||||
-Wno-sign-compare \
|
||||
-Wno-implicit-fallthrough \
|
||||
-Wno-sign-conversion \
|
||||
-Wno-conversion \
|
||||
-Wno-disabled-macro-expansion \
|
||||
-Wno-padded \
|
||||
-Wno-format-nonliteral
|
||||
|
||||
endif
|
||||
OBJ = lex.o \
|
||||
error.o \
|
||||
parse.o \
|
||||
|
@ -34,3 +50,5 @@ all: test gmqcc
|
|||
|
||||
clean:
|
||||
rm -f *.o gmqcc test_ast test_ir test/*.o
|
||||
|
||||
|
||||
|
|
23
asm.c
23
asm.c
|
@ -44,7 +44,7 @@ VECTOR_MAKE(asm_sym, asm_symbols);
|
|||
* Assembly text processing: this handles the internal collection
|
||||
* of text to allow parsing and assemblation.
|
||||
*/
|
||||
static char *const asm_getline(size_t *byte, FILE *fp) {
|
||||
static char* asm_getline(size_t *byte, FILE *fp) {
|
||||
char *line = NULL;
|
||||
size_t read = util_getline(&line, byte, fp);
|
||||
*byte = read;
|
||||
|
@ -65,7 +65,7 @@ void asm_init(const char *file, FILE **fp) {
|
|||
}
|
||||
void asm_close(FILE *fp) {
|
||||
fclose(fp);
|
||||
code_write();
|
||||
code_write("program.dat");
|
||||
}
|
||||
void asm_clear() {
|
||||
size_t i = 0;
|
||||
|
@ -530,24 +530,6 @@ static GMQCC_INLINE bool asm_parse_stmt(const char *skip, size_t line, asm_state
|
|||
* This needs to have a fall state, we start from the
|
||||
* end of the string and work backwards.
|
||||
*/
|
||||
#define OPFILL(X) \
|
||||
do { \
|
||||
size_t w = 0; \
|
||||
if (!(c = strrchr(c, ','))) { \
|
||||
printf("error, expected more operands\n"); \
|
||||
return false; \
|
||||
} \
|
||||
c++; \
|
||||
w++; \
|
||||
while (*c == ' ' || *c == '\t') { \
|
||||
c++; \
|
||||
w++; \
|
||||
} \
|
||||
X = (const char*)c; \
|
||||
c -= w; \
|
||||
*c = '\0'; \
|
||||
c = (char*)skip; \
|
||||
} while (0)
|
||||
#define OPEATS(X,Y) X##Y
|
||||
#define OPCCAT(X,Y) OPEATS(X,Y)
|
||||
#define OPLOAD(X,Y) \
|
||||
|
@ -597,7 +579,6 @@ static GMQCC_INLINE bool asm_parse_stmt(const char *skip, size_t line, asm_state
|
|||
OPLOAD(s.o1.s1, c);
|
||||
break;
|
||||
}
|
||||
#undef OPFILL
|
||||
#undef OPLOAD
|
||||
#undef OPCCAT
|
||||
}
|
||||
|
|
28
ast.c
28
ast.c
|
@ -36,7 +36,7 @@
|
|||
( (ast_node*)self )->node.destroy = (ast_node_delete*)destroyfn
|
||||
|
||||
/* It must not be possible to get here. */
|
||||
static void _ast_node_destroy(ast_node *self)
|
||||
static GMQCC_NORETURN void _ast_node_destroy(ast_node *self)
|
||||
{
|
||||
fprintf(stderr, "ast node missing destroy()\n");
|
||||
abort();
|
||||
|
@ -925,31 +925,31 @@ bool ast_loop_codegen(ast_loop *self, ast_function *func, bool lvalue, ir_value
|
|||
{
|
||||
ast_expression_codegen *cgen;
|
||||
|
||||
ir_value *dummy;
|
||||
ir_value *precond;
|
||||
ir_value *postcond;
|
||||
ir_value *dummy = NULL;
|
||||
ir_value *precond = NULL;
|
||||
ir_value *postcond = NULL;
|
||||
|
||||
/* Since we insert some jumps "late" so we have blocks
|
||||
* ordered "nicely", we need to keep track of the actual end-blocks
|
||||
* of expressions to add the jumps to.
|
||||
*/
|
||||
ir_block *bbody, *end_bbody;
|
||||
ir_block *bprecond, *end_bprecond;
|
||||
ir_block *bpostcond, *end_bpostcond;
|
||||
ir_block *bincrement, *end_bincrement;
|
||||
ir_block *bout, *bin;
|
||||
ir_block *bbody = NULL, *end_bbody = NULL;
|
||||
ir_block *bprecond = NULL, *end_bprecond = NULL;
|
||||
ir_block *bpostcond = NULL, *end_bpostcond = NULL;
|
||||
ir_block *bincrement = NULL, *end_bincrement = NULL;
|
||||
ir_block *bout = NULL, *bin = NULL;
|
||||
|
||||
/* let's at least move the outgoing block to the end */
|
||||
size_t bout_id;
|
||||
|
||||
/* 'break' and 'continue' need to be able to find the right blocks */
|
||||
ir_block *bcontinue = NULL;
|
||||
ir_block *bbreak = NULL;
|
||||
ir_block *bcontinue = NULL;
|
||||
ir_block *bbreak = NULL;
|
||||
|
||||
ir_block *old_bcontinue;
|
||||
ir_block *old_bbreak;
|
||||
ir_block *old_bcontinue = NULL;
|
||||
ir_block *old_bbreak = NULL;
|
||||
|
||||
ir_block *tmpblock;
|
||||
ir_block *tmpblock = NULL;
|
||||
|
||||
(void)lvalue;
|
||||
(void)out;
|
||||
|
|
54
code.c
54
code.c
|
@ -1,6 +1,6 @@
|
|||
/*
|
||||
* Copyright (C) 2012
|
||||
* Dale Weiler
|
||||
* Dale Weiler, Wolfgang Bumiller
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
|
@ -103,6 +103,32 @@ void code_init() {
|
|||
code_statements_add(empty_statement);
|
||||
}
|
||||
|
||||
uint32_t code_genstring(const char *str)
|
||||
{
|
||||
uint32_t off = code_chars_elements;
|
||||
while (*str) {
|
||||
code_chars_add(*str);
|
||||
++str;
|
||||
}
|
||||
return off;
|
||||
}
|
||||
|
||||
uint32_t code_cachedstring(const char *str)
|
||||
{
|
||||
size_t s = 0;
|
||||
/* We could implement knuth-morris-pratt or something
|
||||
* and also take substrings, but I'm uncomfortable with
|
||||
* pointing to subparts of strings for the sake of clarity...
|
||||
*/
|
||||
while (s < code_chars_elements) {
|
||||
if (!strcmp(str, code_chars_data + s))
|
||||
return s;
|
||||
while (code_chars_data[s]) ++s;
|
||||
++s;
|
||||
}
|
||||
return code_genstring(str);
|
||||
}
|
||||
|
||||
void code_test() {
|
||||
prog_section_def d1 = { TYPE_VOID, 28, 1 };
|
||||
prog_section_def d2 = { TYPE_FUNCTION, 29, 8 };
|
||||
|
@ -144,7 +170,7 @@ void code_test() {
|
|||
code_statements_add(s3);
|
||||
}
|
||||
|
||||
void code_write() {
|
||||
bool code_write(const char *filename) {
|
||||
prog_header code_header;
|
||||
FILE *fp = NULL;
|
||||
size_t it = 2;
|
||||
|
@ -184,14 +210,21 @@ void code_write() {
|
|||
util_endianswap(code_functions_data, code_functions_elements, sizeof(prog_section_function));
|
||||
util_endianswap(code_globals_data, code_globals_elements, sizeof(int32_t));
|
||||
|
||||
fp = fopen("program.dat", "wb");
|
||||
fwrite(&code_header, 1, sizeof(prog_header), fp);
|
||||
fwrite(code_statements_data, 1, sizeof(prog_section_statement)*code_statements_elements, fp);
|
||||
fwrite(code_defs_data, 1, sizeof(prog_section_def) *code_defs_elements, fp);
|
||||
fwrite(code_fields_data, 1, sizeof(prog_section_field) *code_fields_elements, fp);
|
||||
fwrite(code_functions_data, 1, sizeof(prog_section_function) *code_functions_elements, fp);
|
||||
fwrite(code_globals_data, 1, sizeof(int32_t) *code_globals_elements, fp);
|
||||
fwrite(code_chars_data, 1, 1 *code_chars_elements, fp);
|
||||
fp = fopen(filename, "wb");
|
||||
if (!fp)
|
||||
return false;
|
||||
|
||||
if (1 != fwrite(&code_header, sizeof(prog_header), 1, fp) ||
|
||||
code_statements_elements != fwrite(code_statements_data, sizeof(prog_section_statement), code_statements_elements, fp) ||
|
||||
code_defs_elements != fwrite(code_defs_data, sizeof(prog_section_def) , code_defs_elements , fp) ||
|
||||
code_fields_elements != fwrite(code_fields_data, sizeof(prog_section_field) , code_fields_elements , fp) ||
|
||||
code_functions_elements != fwrite(code_functions_data, sizeof(prog_section_function) , code_functions_elements , fp) ||
|
||||
code_globals_elements != fwrite(code_globals_data, sizeof(int32_t) , code_globals_elements , fp) ||
|
||||
code_chars_elements != fwrite(code_chars_data, 1 , code_chars_elements , fp))
|
||||
{
|
||||
fclose(fp);
|
||||
return false;
|
||||
}
|
||||
|
||||
util_debug("GEN","HEADER:\n");
|
||||
util_debug("GEN"," version: = %d\n", code_header.version );
|
||||
|
@ -254,4 +287,5 @@ void code_write() {
|
|||
mem_d(code_globals_data);
|
||||
mem_d(code_chars_data);
|
||||
fclose(fp);
|
||||
return true;
|
||||
}
|
||||
|
|
18
error.c
18
error.c
|
@ -29,14 +29,16 @@
|
|||
* intereting like colors for the console.
|
||||
*/
|
||||
#ifndef WIN32
|
||||
# define CON_BLACK 30
|
||||
# define CON_RED 31
|
||||
# define CON_GREEN 32
|
||||
# define CON_BROWN 33
|
||||
# define CON_BLUE 34
|
||||
# define CON_MAGENTA 35
|
||||
# define CON_CYAN 36
|
||||
# define CON_WHITE 37
|
||||
enum {
|
||||
CON_BLACK = 30,
|
||||
CON_RED,
|
||||
CON_GREEN,
|
||||
CON_BROWN,
|
||||
CON_BLUE,
|
||||
CON_MAGENTA,
|
||||
CON_CYAN ,
|
||||
CON_WHITE
|
||||
};
|
||||
static const int error_color[] = {
|
||||
CON_RED,
|
||||
CON_CYAN,
|
||||
|
|
61
gmqcc.h
61
gmqcc.h
|
@ -52,11 +52,16 @@
|
|||
# endif /* !true */
|
||||
# define false (0)
|
||||
# define true (1)
|
||||
# define bool _Bool
|
||||
# if __STDC_VERSION__ < 199901L && __GNUC__ < 3
|
||||
typedef int _Bool
|
||||
# endif
|
||||
# endif /* !__cplusplus */
|
||||
# ifdef __STDC_VERSION__
|
||||
# if __STDC_VERSION__ < 199901L && __GNUC__ < 3
|
||||
typedef int bool;
|
||||
# else
|
||||
typedef _Bool bool;
|
||||
# endif
|
||||
# else
|
||||
typedef int bool;
|
||||
# endif /* !__STDC_VERSION__ */
|
||||
#endif /* !__cplusplus */
|
||||
|
||||
/*
|
||||
* Of some functions which are generated we want to make sure
|
||||
|
@ -79,18 +84,34 @@
|
|||
* like gcc and clang might have an inline attribute we can
|
||||
* use if present.
|
||||
*/
|
||||
#if __STDC_VERSION__ < 199901L
|
||||
# if defined(__GNUC__) || defined (__CLANG__)
|
||||
# if __GNUC__ < 2
|
||||
# define GMQCC_INLINE
|
||||
#ifdef __STDC_VERSION__
|
||||
# if __STDC_VERSION__ < 199901L
|
||||
# if defined(__GNUC__) || defined (__CLANG__)
|
||||
# if __GNUC__ < 2
|
||||
# define GMQCC_INLINE
|
||||
# else
|
||||
# define GMQCC_INLINE __attribute__ ((always_inline))
|
||||
# endif
|
||||
# else
|
||||
# define GMQCC_INLINE __attribute__ ((always_inline))
|
||||
# define GMQCC_INLINE
|
||||
# endif
|
||||
# else
|
||||
# define GMQCC_INLINE
|
||||
# endif
|
||||
# else
|
||||
# define GMQCC_INLINE inline
|
||||
# endif
|
||||
#else
|
||||
# define GMQCC_INLINE inline
|
||||
# define GMQCC_INLINE
|
||||
#endif /* !__STDC_VERSION__ */
|
||||
|
||||
/*
|
||||
* noreturn is present in GCC and clang
|
||||
* it's required for _ast_node_destory otherwise -Wmissing-noreturn
|
||||
* in clang complains about there being no return since abort() is
|
||||
* called.
|
||||
*/
|
||||
#if (defined(__GNUC__) && __GNUC__ >= 2) || defined(__CLANG__)
|
||||
# define GMQCC_NORETURN __attribute__ ((noreturn))
|
||||
#else
|
||||
# define GMQCC_NORETURN
|
||||
#endif
|
||||
|
||||
/*
|
||||
|
@ -135,8 +156,8 @@
|
|||
* fail. There is no valid way to get a 64bit type at this point
|
||||
* without making assumptions of too many things.
|
||||
*/
|
||||
typedef char int64_t;
|
||||
typedef char uint64_t;
|
||||
typedef struct { char _fail : 0; } int64_t;
|
||||
typedef struct { char _fail : 0; } uint64_t;
|
||||
# endif
|
||||
#endif
|
||||
#ifdef _LP64 /* long pointer == 64 */
|
||||
|
@ -148,7 +169,7 @@
|
|||
#endif
|
||||
/* Ensure type sizes are correct: */
|
||||
typedef char uint8_size_is_correct [sizeof(uint8_t) == 1?1:-1];
|
||||
typedef char uint16_size_if_correct [sizeof(uint16_t) == 2?1:-1];
|
||||
typedef char uint16_size_is_correct [sizeof(uint16_t) == 2?1:-1];
|
||||
typedef char uint32_size_is_correct [sizeof(uint32_t) == 4?1:-1];
|
||||
typedef char uint64_size_is_correct [sizeof(uint64_t) == 8?1:-1];
|
||||
typedef char int16_size_if_correct [sizeof(int16_t) == 2?1:-1];
|
||||
|
@ -532,8 +553,10 @@ VECTOR_PROT(char, code_chars );
|
|||
* code_write -- writes out the compiled file
|
||||
* code_init -- prepares the code file
|
||||
*/
|
||||
void code_write ();
|
||||
void code_init ();
|
||||
bool code_write (const char *filename);
|
||||
void code_init ();
|
||||
uint32_t code_genstring (const char *string);
|
||||
uint32_t code_cachedstring(const char *string);
|
||||
|
||||
/*===================================================================*/
|
||||
/*========================= assembler.c =============================*/
|
||||
|
|
436
ir.c
436
ir.c
|
@ -34,6 +34,9 @@ ir_builder* ir_builder_new(const char *modulename)
|
|||
ir_builder* self;
|
||||
|
||||
self = (ir_builder*)mem_a(sizeof(*self));
|
||||
if (!self)
|
||||
return NULL;
|
||||
|
||||
MEM_VECTOR_INIT(self, functions);
|
||||
MEM_VECTOR_INIT(self, globals);
|
||||
self->name = NULL;
|
||||
|
@ -140,6 +143,10 @@ ir_function* ir_function_new(ir_builder* owner)
|
|||
{
|
||||
ir_function *self;
|
||||
self = (ir_function*)mem_a(sizeof(*self));
|
||||
|
||||
if (!self)
|
||||
return NULL;
|
||||
|
||||
self->name = NULL;
|
||||
if (!ir_function_set_name(self, "<@unnamed>")) {
|
||||
mem_d(self);
|
||||
|
@ -252,6 +259,11 @@ ir_block* ir_block_new(ir_function* owner, const char *name)
|
|||
{
|
||||
ir_block *self;
|
||||
self = (ir_block*)mem_a(sizeof(*self));
|
||||
if (!self)
|
||||
return NULL;
|
||||
|
||||
memset(self, 0, sizeof(*self));
|
||||
|
||||
self->label = NULL;
|
||||
if (!ir_block_set_label(self, name)) {
|
||||
mem_d(self);
|
||||
|
@ -269,6 +281,9 @@ ir_block* ir_block_new(ir_function* owner, const char *name)
|
|||
self->is_return = false;
|
||||
self->run_id = 0;
|
||||
MEM_VECTOR_INIT(self, living);
|
||||
|
||||
self->generated = false;
|
||||
|
||||
return self;
|
||||
}
|
||||
MEM_VEC_FUNCTIONS(ir_block, ir_instr*, instr)
|
||||
|
@ -305,6 +320,9 @@ ir_instr* ir_instr_new(ir_block* owner, int op)
|
|||
{
|
||||
ir_instr *self;
|
||||
self = (ir_instr*)mem_a(sizeof(*self));
|
||||
if (!self)
|
||||
return NULL;
|
||||
|
||||
self->owner = owner;
|
||||
self->context.file = "<@no context>";
|
||||
self->context.line = 0;
|
||||
|
@ -381,6 +399,7 @@ ir_value* ir_value_var(const char *name, int storetype, int vtype)
|
|||
ir_value *self;
|
||||
self = (ir_value*)mem_a(sizeof(*self));
|
||||
self->vtype = vtype;
|
||||
self->fieldtype = TYPE_VOID;
|
||||
self->store = storetype;
|
||||
MEM_VECTOR_INIT(self, reads);
|
||||
MEM_VECTOR_INIT(self, writes);
|
||||
|
@ -390,6 +409,9 @@ ir_value* ir_value_var(const char *name, int storetype, int vtype)
|
|||
self->name = NULL;
|
||||
ir_value_set_name(self, name);
|
||||
|
||||
memset(&self->constval, 0, sizeof(self->constval));
|
||||
memset(&self->code, 0, sizeof(self->code));
|
||||
|
||||
MEM_VECTOR_INIT(self, life);
|
||||
return self;
|
||||
}
|
||||
|
@ -1655,6 +1677,420 @@ on_error:
|
|||
return false;
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
*IR Code-Generation
|
||||
*
|
||||
* Since the IR has the convention of putting 'write' operands
|
||||
* at the beginning, we have to rotate the operands of instructions
|
||||
* properly in order to generate valid QCVM code.
|
||||
*
|
||||
* Having destinations at a fixed position is more convenient. In QC
|
||||
* this is *mostly* OPC, but FTE adds at least 2 instructions which
|
||||
* read from from OPA, and store to OPB rather than OPC. Which is
|
||||
* partially the reason why the implementation of these instructions
|
||||
* in darkplaces has been delayed for so long.
|
||||
*
|
||||
* Breaking conventions is annoying...
|
||||
*/
|
||||
static bool ir_builder_gen_global(ir_builder *self, ir_value *global);
|
||||
|
||||
static bool gen_global_field(ir_value *global)
|
||||
{
|
||||
if (global->isconst)
|
||||
{
|
||||
ir_value *fld = global->constval.vpointer;
|
||||
if (!fld) {
|
||||
printf("Invalid field constant with no field: %s\n", global->name);
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Now, in this case, a relocation would be impossible to code
|
||||
* since it looks like this:
|
||||
* .vector v = origin; <- parse error, wtf is 'origin'?
|
||||
* .vector origin;
|
||||
*
|
||||
* But we will need a general relocation support later anyway
|
||||
* for functions... might as well support that here.
|
||||
*/
|
||||
if (!fld->code.globaladdr) {
|
||||
printf("FIXME: Relocation support\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
/* copy the field's value */
|
||||
global->code.globaladdr = code_globals_add(code_globals_data[fld->code.globaladdr]);
|
||||
}
|
||||
else
|
||||
{
|
||||
prog_section_field fld;
|
||||
|
||||
fld.name = global->code.name;
|
||||
fld.offset = code_fields_elements;
|
||||
fld.type = global->fieldtype;
|
||||
|
||||
if (fld.type == TYPE_VOID) {
|
||||
printf("Field is missing a type: %s\n", global->name);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (code_fields_add(fld) < 0)
|
||||
return false;
|
||||
|
||||
global->code.globaladdr = code_globals_add(fld.offset);
|
||||
}
|
||||
if (global->code.globaladdr < 0)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool gen_global_pointer(ir_value *global)
|
||||
{
|
||||
if (global->isconst)
|
||||
{
|
||||
ir_value *target = global->constval.vpointer;
|
||||
if (!target) {
|
||||
printf("Invalid pointer constant: %s\n", global->name);
|
||||
/* NULL pointers are pointing to the NULL constant, which also
|
||||
* sits at address 0, but still has an ir_value for itself.
|
||||
*/
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Here, relocations ARE possible - in fteqcc-enhanced-qc:
|
||||
* void() foo; <- proto
|
||||
* void() *fooptr = &foo;
|
||||
* void() foo = { code }
|
||||
*/
|
||||
if (!target->code.globaladdr) {
|
||||
/* FIXME: Check for the constant nullptr ir_value!
|
||||
* because then code.globaladdr being 0 is valid.
|
||||
*/
|
||||
printf("FIXME: Relocation support\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
global->code.globaladdr = code_globals_add(target->code.globaladdr);
|
||||
}
|
||||
else
|
||||
{
|
||||
global->code.globaladdr = code_globals_add(0);
|
||||
}
|
||||
if (global->code.globaladdr < 0)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool gen_blocks_recursive(ir_function *func, ir_block *block)
|
||||
{
|
||||
prog_section_statement stmt;
|
||||
prog_section_statement *stptr;
|
||||
ir_instr *instr;
|
||||
ir_block *target;
|
||||
ir_block *ontrue;
|
||||
ir_block *onfalse;
|
||||
size_t stidx;
|
||||
size_t i;
|
||||
|
||||
tailcall:
|
||||
block->generated = true;
|
||||
block->code_start = code_statements_elements;
|
||||
for (i = 0; i < block->instr_count; ++i)
|
||||
{
|
||||
instr = block->instr[i];
|
||||
|
||||
if (instr->opcode == VINSTR_PHI) {
|
||||
printf("cannot generate virtual instruction (phi)\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (instr->opcode == VINSTR_JUMP) {
|
||||
target = instr->bops[0];
|
||||
/* for uncoditional jumps, if the target hasn't been generated
|
||||
* yet, we generate them right here.
|
||||
*/
|
||||
if (!target->generated) {
|
||||
block = target;
|
||||
goto tailcall;
|
||||
}
|
||||
|
||||
/* otherwise we generate a jump instruction */
|
||||
stmt.opcode = INSTR_GOTO;
|
||||
stmt.o1.s1 = (target->code_start-1) - code_statements_elements;
|
||||
stmt.o2.s1 = 0;
|
||||
stmt.o3.s1 = 0;
|
||||
if (code_statements_add(stmt) < 0)
|
||||
return false;
|
||||
|
||||
/* no further instructions can be in this block */
|
||||
return true;
|
||||
}
|
||||
|
||||
if (instr->opcode == VINSTR_COND) {
|
||||
ontrue = instr->bops[0];
|
||||
onfalse = instr->bops[1];
|
||||
/* TODO: have the AST signal which block should
|
||||
* come first: eg. optimize IFs without ELSE...
|
||||
*/
|
||||
|
||||
stmt.o1.s1 = instr->_ops[0]->code.globaladdr;
|
||||
|
||||
stmt.o3.s1 = 0;
|
||||
if (ontrue->generated) {
|
||||
stmt.opcode = INSTR_IF;
|
||||
stmt.o2.s1 = (ontrue->code_start-1) - code_statements_elements;
|
||||
if (code_statements_add(stmt) < 0)
|
||||
return false;
|
||||
}
|
||||
if (onfalse->generated) {
|
||||
stmt.opcode = INSTR_IFNOT;
|
||||
stmt.o2.s1 = (onfalse->code_start-1) - code_statements_elements;
|
||||
if (code_statements_add(stmt) < 0)
|
||||
return false;
|
||||
}
|
||||
if (!ontrue->generated) {
|
||||
if (onfalse->generated) {
|
||||
block = ontrue;
|
||||
goto tailcall;
|
||||
}
|
||||
}
|
||||
if (!onfalse->generated) {
|
||||
if (ontrue->generated) {
|
||||
block = onfalse;
|
||||
goto tailcall;
|
||||
}
|
||||
}
|
||||
/* neither ontrue nor onfalse exist */
|
||||
stmt.opcode = INSTR_IFNOT;
|
||||
stidx = code_statements_elements - 1;
|
||||
if (code_statements_add(stmt) < 0)
|
||||
return false;
|
||||
stptr = &code_statements_data[stidx];
|
||||
/* on false we jump, so add ontrue-path */
|
||||
if (!gen_blocks_recursive(func, ontrue))
|
||||
return false;
|
||||
/* fixup the jump address */
|
||||
stptr->o2.s1 = (ontrue->code_start-1) - (stidx+1);
|
||||
/* generate onfalse path */
|
||||
if (onfalse->generated) {
|
||||
/* may have been generated in the previous recursive call */
|
||||
stmt.opcode = INSTR_GOTO;
|
||||
stmt.o2.s1 = 0;
|
||||
stmt.o3.s1 = 0;
|
||||
stmt.o1.s1 = (onfalse->code_start-1) - code_statements_elements;
|
||||
return (code_statements_add(stmt) >= 0);
|
||||
}
|
||||
/* if not, generate now */
|
||||
block = onfalse;
|
||||
goto tailcall;
|
||||
}
|
||||
|
||||
if (instr->opcode >= INSTR_CALL0 && instr->opcode <= INSTR_CALL8) {
|
||||
printf("TODO: call instruction\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (instr->opcode == INSTR_STATE) {
|
||||
printf("TODO: state instruction\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
stmt.opcode = instr->opcode;
|
||||
stmt.o1.u1 = 0;
|
||||
stmt.o2.u1 = 0;
|
||||
stmt.o3.u1 = 0;
|
||||
|
||||
/* This is the general order of operands */
|
||||
if (instr->_ops[0])
|
||||
stmt.o3.u1 = instr->_ops[0]->code.globaladdr;
|
||||
|
||||
if (instr->_ops[1])
|
||||
stmt.o1.u1 = instr->_ops[1]->code.globaladdr;
|
||||
|
||||
if (instr->_ops[2])
|
||||
stmt.o2.u1 = instr->_ops[2]->code.globaladdr;
|
||||
|
||||
if (stmt.opcode == INSTR_RETURN)
|
||||
{
|
||||
stmt.o1.u1 = stmt.o3.u1;
|
||||
stmt.o3.u1 = 0;
|
||||
}
|
||||
|
||||
if (code_statements_add(stmt) < 0)
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool gen_function_code(ir_function *self)
|
||||
{
|
||||
ir_block *block;
|
||||
|
||||
/* Starting from entry point, we generate blocks "as they come"
|
||||
* for now. Dead blocks will not be translated obviously.
|
||||
*/
|
||||
if (!self->blocks_count) {
|
||||
printf("Function '%s' declared without body.\n", self->name);
|
||||
return false;
|
||||
}
|
||||
|
||||
block = self->blocks[0];
|
||||
if (block->generated)
|
||||
return true;
|
||||
|
||||
if (!gen_blocks_recursive(self, block)) {
|
||||
printf("failed to generate blocks for '%s'\n", self->name);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool gen_global_function(ir_builder *ir, ir_value *global)
|
||||
{
|
||||
prog_section_function fun;
|
||||
ir_function *irfun;
|
||||
|
||||
size_t i;
|
||||
|
||||
if (!global->isconst ||
|
||||
!global->constval.vfunc)
|
||||
{
|
||||
printf("Invalid state of function-global: not constant: %s\n", global->name);
|
||||
return false;
|
||||
}
|
||||
|
||||
irfun = global->constval.vfunc;
|
||||
|
||||
fun.name = global->code.name;
|
||||
fun.file = code_cachedstring(global->context.file);
|
||||
fun.profile = 0; /* always 0 */
|
||||
fun.nargs = irfun->params_count;
|
||||
|
||||
for (i = 0;i < 8; ++i) {
|
||||
if (i >= fun.nargs)
|
||||
fun.argsize[i] = 0;
|
||||
else if (irfun->params[i] == TYPE_VECTOR)
|
||||
fun.argsize[i] = 3;
|
||||
else
|
||||
fun.argsize[i] = 1;
|
||||
}
|
||||
|
||||
fun.locals = irfun->locals_count;
|
||||
fun.firstlocal = code_globals_elements;
|
||||
for (i = 0; i < irfun->locals_count; ++i) {
|
||||
if (!ir_builder_gen_global(ir, irfun->locals[i])) {
|
||||
printf("Failed to generate global %s\n", irfun->locals[i]->name);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
fun.entry = code_statements_elements;
|
||||
if (!gen_function_code(irfun)) {
|
||||
printf("Failed to generate code for function %s\n", irfun->name);
|
||||
return false;
|
||||
}
|
||||
|
||||
return (code_functions_add(fun) >= 0);
|
||||
}
|
||||
|
||||
static bool ir_builder_gen_global(ir_builder *self, ir_value *global)
|
||||
{
|
||||
int32_t *iptr;
|
||||
prog_section_def def;
|
||||
|
||||
def.type = global->vtype;
|
||||
def.offset = code_globals_elements;
|
||||
def.name = global->code.name = code_genstring(global->name);
|
||||
|
||||
switch (global->vtype)
|
||||
{
|
||||
case TYPE_POINTER:
|
||||
if (code_defs_add(def) < 0)
|
||||
return false;
|
||||
return gen_global_pointer(global);
|
||||
case TYPE_FIELD:
|
||||
if (code_defs_add(def) < 0)
|
||||
return false;
|
||||
return gen_global_field(global);
|
||||
case TYPE_ENTITY:
|
||||
if (code_defs_add(def) < 0)
|
||||
return false;
|
||||
case TYPE_FLOAT:
|
||||
{
|
||||
if (code_defs_add(def) < 0)
|
||||
return false;
|
||||
|
||||
if (global->isconst) {
|
||||
iptr = (int32_t*)&global->constval.vfloat;
|
||||
global->code.globaladdr = code_globals_add(*iptr);
|
||||
} else
|
||||
global->code.globaladdr = code_globals_add(0);
|
||||
|
||||
return global->code.globaladdr >= 0;
|
||||
}
|
||||
case TYPE_STRING:
|
||||
{
|
||||
if (code_defs_add(def) < 0)
|
||||
return false;
|
||||
if (global->isconst)
|
||||
global->code.globaladdr = code_globals_add(code_cachedstring(global->constval.vstring));
|
||||
else
|
||||
global->code.globaladdr = code_globals_add(0);
|
||||
return global->code.globaladdr >= 0;
|
||||
}
|
||||
case TYPE_VECTOR:
|
||||
{
|
||||
if (code_defs_add(def) < 0)
|
||||
return false;
|
||||
|
||||
if (global->isconst) {
|
||||
iptr = (int32_t*)&global->constval.vvec;
|
||||
global->code.globaladdr = code_globals_add(iptr[0]);
|
||||
if (code_globals_add(iptr[1]) < 0 || code_globals_add(iptr[2]) < 0)
|
||||
return false;
|
||||
} else {
|
||||
global->code.globaladdr = code_globals_add(0);
|
||||
if (code_globals_add(0) < 0 || code_globals_add(0) < 0)
|
||||
return false;
|
||||
}
|
||||
return global->code.globaladdr >= 0;
|
||||
}
|
||||
case TYPE_FUNCTION:
|
||||
if (code_defs_add(def) < 0)
|
||||
return false;
|
||||
return gen_global_function(self, global);
|
||||
case TYPE_VARIANT:
|
||||
/* assume biggest type */
|
||||
global->code.globaladdr = code_globals_add(0);
|
||||
code_globals_add(0);
|
||||
code_globals_add(0);
|
||||
return true;
|
||||
default:
|
||||
/* refuse to create 'void' type or any other fancy business. */
|
||||
printf("Invalid type for global variable %s\n", global->name);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool ir_builder_generate(ir_builder *self, const char *filename)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
code_init();
|
||||
|
||||
/* FIXME: generate TYPE_FUNCTION globals and link them
|
||||
* to their ir_function.
|
||||
*/
|
||||
|
||||
for (i = 0; i < self->globals_count; ++i)
|
||||
{
|
||||
if (!ir_builder_gen_global(self, self->globals[i]))
|
||||
return false;
|
||||
}
|
||||
|
||||
printf("writing '%s'...\n", filename);
|
||||
return code_write(filename);
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
*IR DEBUG Dump functions...
|
||||
*/
|
||||
|
|
17
ir.h
17
ir.h
|
@ -38,6 +38,8 @@ typedef struct ir_value_s {
|
|||
int vtype;
|
||||
int store;
|
||||
lex_ctx context;
|
||||
/* even the IR knows the subtype of a field */
|
||||
int fieldtype;
|
||||
|
||||
MEM_VECTOR_MAKE(struct ir_instr_s*, reads);
|
||||
MEM_VECTOR_MAKE(struct ir_instr_s*, writes);
|
||||
|
@ -50,8 +52,14 @@ typedef struct ir_value_s {
|
|||
vector vvec;
|
||||
char *vstring;
|
||||
struct ir_value_s *vpointer;
|
||||
struct ir_function_s *vfunc;
|
||||
} constval;
|
||||
|
||||
struct {
|
||||
int32_t globaladdr;
|
||||
int32_t name;
|
||||
} code;
|
||||
|
||||
/* For the temp allocator */
|
||||
MEM_VECTOR_MAKE(ir_life_entry_t, life);
|
||||
} ir_value;
|
||||
|
@ -137,6 +145,9 @@ typedef struct ir_block_s
|
|||
size_t run_id;
|
||||
|
||||
struct ir_function_s *owner;
|
||||
|
||||
bool generated;
|
||||
size_t code_start;
|
||||
} ir_block;
|
||||
|
||||
ir_block* ir_block_new(struct ir_function_s *owner, const char *label);
|
||||
|
@ -264,6 +275,12 @@ ir_function* ir_builder_create_function(ir_builder*, const char *name);
|
|||
ir_value* ir_builder_get_global(ir_builder*, const char *fun);
|
||||
ir_value* ir_builder_create_global(ir_builder*, const char *name, int vtype);
|
||||
|
||||
bool ir_builder_generate(ir_builder *self, const char *filename);
|
||||
|
||||
void ir_builder_dump(ir_builder*, int (*oprintf)(const char*, ...));
|
||||
|
||||
/* This code assumes 32 bit floats while generating binary */
|
||||
extern int check_int_and_float_size
|
||||
[ (sizeof(int32_t) == sizeof(( (ir_value*)(NULL) )->constval.vvec.x)) ? 1 : -1 ];
|
||||
|
||||
#endif
|
||||
|
|
1
lex.c
1
lex.c
|
@ -124,7 +124,6 @@ static int lex_trigraph(lex_file *file) {
|
|||
default:
|
||||
lex_unget('?', file);
|
||||
lex_unget(ch , file);
|
||||
return '?';
|
||||
}
|
||||
return '?';
|
||||
}
|
||||
|
|
3
main.c
3
main.c
|
@ -24,7 +24,7 @@
|
|||
typedef struct { char *name, type; } argitem;
|
||||
VECTOR_MAKE(argitem, items);
|
||||
|
||||
static const int usage(const char *const app) {
|
||||
static int usage(const char *app) {
|
||||
printf("usage:\n"
|
||||
" %s -c<file> -oprog.dat -- compile file\n"
|
||||
" %s -a<file> -oprog.dat -- assemble file\n"
|
||||
|
@ -105,7 +105,6 @@ int main(int argc, char **argv) {
|
|||
if (util_strncmpexact(&argv[1][1], "memchk", 6)) { opts_memchk = true; break; }
|
||||
if (util_strncmpexact(&argv[1][1], "help", 4)) {
|
||||
return usage(app);
|
||||
break;
|
||||
}
|
||||
/* compiler type selection */
|
||||
if (util_strncmpexact(&argv[1][1], "std=qcc" , 7 )) { opts_compiler = COMPILER_QCC; break; }
|
||||
|
|
|
@ -192,7 +192,7 @@ STATE(ASSIGN(STORE_F, vi, BIN(ADD_F, vi, f1)));
|
|||
ENDWHILE();
|
||||
|
||||
ENDFUNCTION(main);
|
||||
|
||||
|
||||
ir = ir_builder_new("ast_test");
|
||||
assert(ir);
|
||||
|
||||
|
@ -216,6 +216,10 @@ ENDFUNCTION(main);
|
|||
/* dump */
|
||||
ir_builder_dump(ir, printf);
|
||||
|
||||
/* Now create a file */
|
||||
if (!ir_builder_generate(ir, "test_ast.dat"))
|
||||
printf("*** failed to generate code\n");
|
||||
|
||||
/* ir cleanup */
|
||||
ir_builder_delete(ir);
|
||||
|
||||
|
|
|
@ -71,7 +71,7 @@ int typedef_add(lex_file *file, const char *from, const char *to) {
|
|||
return -100;
|
||||
} else {
|
||||
/* search the typedefs for it (typedef-a-typedef?) */
|
||||
typedef_node *find = typedef_table[typedef_hash(from)];
|
||||
find = typedef_table[typedef_hash(from)];
|
||||
if (find) {
|
||||
typedef_table[hash] = mem_a(sizeof(typedef_node));
|
||||
if (typedef_table[hash])
|
||||
|
|
Loading…
Reference in a new issue