Merge branch 'cooking'

Conflicts:
	.gitignore
This commit is contained in:
Dale Weiler 2013-06-06 06:20:58 +00:00
commit 0eab97283f
34 changed files with 1924 additions and 1353 deletions

1
.gitignore vendored
View file

@ -17,4 +17,3 @@ gmqcc
gmqpak
qcvm
testsuite
pak

View file

@ -1,6 +1,6 @@
Authors:
Dale Weiler - Charismatic Visionary / Programmer
Wolfgang Bumiller - Main Programmer
Dale `graphitemaster` Weiler - Charismatic Visionary / Programmer
Wolfgang `Blub\w` Bumiller - Main Programmer
Thanks to:
Forest `LordHavoc` Hale - Technical support and assistance

30
CHANGES
View file

@ -1,5 +1,25 @@
Release v0.3.0
* to fill
* Language:
- Return assignments, the ability to assign to the return keyword
as if it were a local variable.
* Compilation:
- Optimized memory usage (now uses on average %3 less memory for
compilation).
- Fixed dotranslate (translatable strings)
* QCVM:
- Escape strings for -printdefs
* Commandline:
- Added statistic dumps, gives information about the number of used
hashtables, vectors, and number of unique sizes of vectors and
hashtables. The amount of memory used for vectors. As well as the
number of strdups used in total for compilation.
* Testsuite:
- Fixed a floating point exception rasied by modulo operation in
-memchk.
* Build:
- Added gentoo ebuilds.
- Added win32 Makefile for building win32 packages.
- Added slackware pkg build files
2012-04-27 v0.2.9
* Preprocessor:
@ -56,12 +76,12 @@ Release v0.3.0
- Added defs.qh (auto included) for qcvm definitions
* Syntax Highlighting:
- Added various syntax highlighting description files for
various text editors / integrated development envirorments,
including support for: geany, kate, kwrite, kdevelop, QtCreator,
gtksourceview, gedit, sany, nano, jedit
various text editors / integrated development envirorments,
including support for: geany, kate, kwrite, kdevelop, QtCreator,
gtksourceview, gedit, sany, nano, jedit
* Build:
- Build scripts for building debian, archlinux and archbsd
packages for x86, and x86_64.
packages for x86, and x86_64.
- Makefile targets for gource visualization, and render of
gource visualization.

View file

@ -29,12 +29,13 @@ ifeq ($(CC), clang)
-Wno-conversion \
-Wno-missing-prototypes \
-Wno-float-equal \
-Wno-unknown-warning-option
-Wno-unknown-warning-option \
-Wstrict-prototypes
else
#Tiny C Compiler doesn't know what -pedantic-errors is
# and instead of ignoring .. just errors.
ifneq ($(CC), tcc)
CFLAGS += -pedantic-errors
CFLAGS += -Wstrict-prototypes -pedantic-errors
else
CFLAGS += -Wno-pointer-sign -fno-common
endif
@ -44,11 +45,17 @@ ifeq ($(track), no)
CFLAGS += -DNOTRACK
endif
OBJ_D = util.o code.o ast.o ir.o conout.o ftepp.o opts.o fs.o utf8.o correct.o
OBJ_P = util.o fs.o conout.o opts.o pak.o
OBJ_T = test.o util.o conout.o fs.o
OBJ_C = main.o lexer.o parser.o fs.o
OBJ_X = exec-standalone.o util.o conout.o fs.o
OBJ_D = util.o code.o ast.o ir.o conout.o ftepp.o opts.o fs.o utf8.o correct.o stat.o
OBJ_P = util.o fs.o conout.o opts.o pak.o stat.o
OBJ_T = test.o util.o conout.o fs.o stat.o
OBJ_C = main.o lexer.o parser.o fs.o stat.o
OBJ_X = exec-standalone.o util.o conout.o fs.o stat.o
#we have duplicate object files when dealing with creating a simple list
#for dependinces. To combat this we use some clever recrusive-make to
#filter the list and remove duplicates which we use for make depend
RMDUP = $(if $1,$(firstword $1) $(call RMDUP,$(filter-out $(firstword $1),$1)))
DEPS := $(call RMDUP, $(OBJ_D) $(OBJ_P) $(OBJ_T) $(OBJ_C) $(OBJ_X))
ifneq ("$(CYGWIN)", "")
#nullify the common variables that
@ -72,12 +79,12 @@ ifneq ("$(MINGW)", "")
QCVM = qcvm.exe
GMQCC = gmqcc.exe
TESTSUITE = testsuite.exe
PAK = pak.exe
PAK = gmqpak.exe
else
QCVM = qcvm
GMQCC = gmqcc
TESTSUITE = testsuite
PAK = pak
PAK = gmqpak
endif
endif
@ -129,7 +136,6 @@ SPLINTFLAGS = \
-onlytrans \
-predboolint \
-boolops \
-exportlocal \
-incondefs \
-macroredef \
-retvalint \
@ -142,7 +148,6 @@ SPLINTFLAGS = \
-temptrans \
-usereleased \
-warnposix \
-shiftimplementation \
+charindex \
-kepttrans \
-unqualifiedtrans \
@ -156,8 +161,6 @@ SPLINTFLAGS = \
-mayaliasunique \
-realcompare \
-observertrans \
-shiftnegative \
-freshtrans \
-abstract \
-statictrans \
-castfcnptr
@ -203,34 +206,32 @@ gource-record:
depend:
@makedepend -Y -w 65536 2> /dev/null \
$(subst .o,.c,$(OBJ_D))
@makedepend -a -Y -w 65536 2> /dev/null \
$(subst .o,.c,$(OBJ_T))
@makedepend -a -Y -w 65536 2> /dev/null \
$(subst .o,.c,$(OBJ_C))
@makedepend -a -Y -w 65536 2> /dev/null \
$(subst .o,.c,$(OBJ_X))
@makedepend -a -Y -w 65536 2> /dev/null \
$(subst .o,.c,$(OBJ_P))
$(subst .o,.c,$(DEPS))
#install rules
install: install-gmqcc install-qcvm install-doc
install: install-gmqcc install-qcvm install-gmqpak install-doc
install-gmqcc: $(GMQCC)
install -d -m755 $(DESTDIR)$(BINDIR)
install -m755 $(GMQCC) $(DESTDIR)$(BINDIR)/$(GMQCC)
install-qcvm: $(QCVM)
install -d -m755 $(DESTDIR)$(BINDIR)
install -m755 $(QCVM) $(DESTDIR)$(BINDIR)/$(QCVM)
install-gmqpak: $(PAK)
install -d -m755 $(DESTDIR)$(BINDIR)
install -m755 $(PAK) $(DESTDIR)$(BINDIR)/$(PAK)
install-doc:
install -d -m755 $(DESTDIR)$(MANDIR)/man1
install -m644 doc/gmqcc.1 $(DESTDIR)$(MANDIR)/man1/
install -m644 doc/qcvm.1 $(DESTDIR)$(MANDIR)/man1/
install -m644 doc/gmqpak.1 $(DESTDIR)$(MANDIR)/man1/
uninstall:
rm $(DESTDIR)$(BINDIR)/gmqcc
rm $(DESTDIR)$(BINDIR)/qcvm
rm $(DESTDIR)$(MANDIR)/man1/doc/gmqcc.1
rm $(DESTDIR)$(MANDIR)/man1/doc/qcvm.1
rm -f $(DESTDIR)$(BINDIR)/gmqcc
rm -f $(DESTDIR)$(BINDIR)/qcvm
rm -f $(DESTDIR)$(BINDIR)/gmqpak
rm -f $(DESTDIR)$(MANDIR)/man1/doc/gmqcc.1
rm -f $(DESTDIR)$(MANDIR)/man1/doc/qcvm.1
rm -f $(DESTDIR)$(MANDIR)/man1/doc/gmqpak.1
# DO NOT DELETE
@ -244,23 +245,9 @@ opts.o: gmqcc.h opts.def
fs.o: gmqcc.h opts.def
utf8.o: gmqcc.h opts.def
correct.o: gmqcc.h opts.def
stat.o: gmqcc.h opts.def
pak.o: gmqcc.h opts.def
test.o: gmqcc.h opts.def
util.o: gmqcc.h opts.def
conout.o: gmqcc.h opts.def
fs.o: gmqcc.h opts.def
main.o: gmqcc.h opts.def lexer.h
lexer.o: gmqcc.h opts.def lexer.h
parser.o: gmqcc.h opts.def lexer.h ast.h ir.h intrin.h
fs.o: gmqcc.h opts.def
util.o: gmqcc.h opts.def
conout.o: gmqcc.h opts.def
fs.o: gmqcc.h opts.def
util.o: gmqcc.h opts.def
fs.o: gmqcc.h opts.def
conout.o: gmqcc.h opts.def
opts.o: gmqcc.h opts.def
pak.o: gmqcc.h opts.def

358
ast.c
View file

@ -21,7 +21,6 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@ -34,8 +33,45 @@
return NULL; \
} \
ast_node_init((ast_node*)self, ctx, TYPE_##T); \
( (ast_node*)self )->node.destroy = (ast_node_delete*)destroyfn
( (ast_node*)self )->destroy = (ast_node_delete*)destroyfn
/*
* forward declarations, these need not be in ast.h for obvious
* static reasons.
*/
static bool ast_member_codegen(ast_member*, ast_function*, bool lvalue, ir_value**);
static void ast_array_index_delete(ast_array_index*);
static bool ast_array_index_codegen(ast_array_index*, ast_function*, bool lvalue, ir_value**);
static void ast_store_delete(ast_store*);
static bool ast_store_codegen(ast_store*, ast_function*, bool lvalue, ir_value**);
static void ast_ifthen_delete(ast_ifthen*);
static bool ast_ifthen_codegen(ast_ifthen*, ast_function*, bool lvalue, ir_value**);
static void ast_ternary_delete(ast_ternary*);
static bool ast_ternary_codegen(ast_ternary*, ast_function*, bool lvalue, ir_value**);
static void ast_loop_delete(ast_loop*);
static bool ast_loop_codegen(ast_loop*, ast_function*, bool lvalue, ir_value**);
static void ast_breakcont_delete(ast_breakcont*);
static bool ast_breakcont_codegen(ast_breakcont*, ast_function*, bool lvalue, ir_value**);
static void ast_switch_delete(ast_switch*);
static bool ast_switch_codegen(ast_switch*, ast_function*, bool lvalue, ir_value**);
static void ast_label_delete(ast_label*);
static void ast_label_register_goto(ast_label*, ast_goto*);
static bool ast_label_codegen(ast_label*, ast_function*, bool lvalue, ir_value**);
static bool ast_goto_codegen(ast_goto*, ast_function*, bool lvalue, ir_value**);
static void ast_goto_delete(ast_goto*);
static void ast_call_delete(ast_call*);
static bool ast_call_codegen(ast_call*, ast_function*, bool lvalue, ir_value**);
static bool ast_block_codegen(ast_block*, ast_function*, bool lvalue, ir_value**);
static void ast_unary_delete(ast_unary*);
static bool ast_unary_codegen(ast_unary*, ast_function*, bool lvalue, ir_value**);
static void ast_entfield_delete(ast_entfield*);
static bool ast_entfield_codegen(ast_entfield*, ast_function*, bool lvalue, ir_value**);
static void ast_return_delete(ast_return*);
static bool ast_return_codegen(ast_return*, ast_function*, bool lvalue, ir_value**);
static void ast_binstore_delete(ast_binstore*);
static bool ast_binstore_codegen(ast_binstore*, ast_function*, bool lvalue, ir_value**);
static void ast_binary_delete(ast_binary*);
static bool ast_binary_codegen(ast_binary*, ast_function*, bool lvalue, ir_value**);
/* It must not be possible to get here. */
static GMQCC_NORETURN void _ast_node_destroy(ast_node *self)
@ -48,11 +84,11 @@ static GMQCC_NORETURN void _ast_node_destroy(ast_node *self)
/* Initialize main ast node aprts */
static void ast_node_init(ast_node *self, lex_ctx ctx, int nodetype)
{
self->node.context = ctx;
self->node.destroy = &_ast_node_destroy;
self->node.keep = false;
self->node.nodetype = nodetype;
self->node.side_effects = false;
self->context = ctx;
self->destroy = &_ast_node_destroy;
self->keep = false;
self->nodetype = nodetype;
self->side_effects = false;
}
/* weight and side effects */
@ -67,28 +103,28 @@ static void _ast_propagate_effects(ast_node *self, ast_node *other)
static void ast_expression_init(ast_expression *self,
ast_expression_codegen *codegen)
{
self->expression.codegen = codegen;
self->expression.vtype = TYPE_VOID;
self->expression.next = NULL;
self->expression.outl = NULL;
self->expression.outr = NULL;
self->expression.params = NULL;
self->expression.count = 0;
self->expression.flags = 0;
self->expression.varparam = NULL;
self->codegen = codegen;
self->vtype = TYPE_VOID;
self->next = NULL;
self->outl = NULL;
self->outr = NULL;
self->params = NULL;
self->count = 0;
self->flags = 0;
self->varparam = NULL;
}
static void ast_expression_delete(ast_expression *self)
{
size_t i;
if (self->expression.next)
ast_delete(self->expression.next);
for (i = 0; i < vec_size(self->expression.params); ++i) {
ast_delete(self->expression.params[i]);
if (self->next)
ast_delete(self->next);
for (i = 0; i < vec_size(self->params); ++i) {
ast_delete(self->params[i]);
}
vec_free(self->expression.params);
if (self->expression.varparam)
ast_delete(self->expression.varparam);
vec_free(self->params);
if (self->varparam)
ast_delete(self->varparam);
}
static void ast_expression_delete_full(ast_expression *self)
@ -100,8 +136,8 @@ static void ast_expression_delete_full(ast_expression *self)
ast_value* ast_value_copy(const ast_value *self)
{
size_t i;
const ast_expression_common *fromex;
ast_expression_common *selfex;
const ast_expression *fromex;
ast_expression *selfex;
ast_value *cp = ast_value_new(self->expression.node.context, self->name, self->expression.vtype);
if (self->expression.next) {
cp->expression.next = ast_type_copy(self->expression.node.context, self->expression.next);
@ -120,14 +156,14 @@ ast_value* ast_value_copy(const ast_value *self)
void ast_type_adopt_impl(ast_expression *self, const ast_expression *other)
{
size_t i;
const ast_expression_common *fromex;
ast_expression_common *selfex;
self->expression.vtype = other->expression.vtype;
if (other->expression.next) {
self->expression.next = (ast_expression*)ast_type_copy(ast_ctx(self), other->expression.next);
const ast_expression *fromex;
ast_expression *selfex;
self->vtype = other->vtype;
if (other->next) {
self->next = (ast_expression*)ast_type_copy(ast_ctx(self), other->next);
}
fromex = &other->expression;
selfex = &self->expression;
fromex = other;
selfex = self;
selfex->count = fromex->count;
selfex->flags = fromex->flags;
for (i = 0; i < vec_size(fromex->params); ++i) {
@ -140,17 +176,17 @@ static ast_expression* ast_shallow_type(lex_ctx ctx, int vtype)
{
ast_instantiate(ast_expression, ctx, ast_expression_delete_full);
ast_expression_init(self, NULL);
self->expression.codegen = NULL;
self->expression.next = NULL;
self->expression.vtype = vtype;
self->codegen = NULL;
self->next = NULL;
self->vtype = vtype;
return self;
}
ast_expression* ast_type_copy(lex_ctx ctx, const ast_expression *ex)
{
size_t i;
const ast_expression_common *fromex;
ast_expression_common *selfex;
const ast_expression *fromex;
ast_expression *selfex;
if (!ex)
return NULL;
@ -159,8 +195,8 @@ ast_expression* ast_type_copy(lex_ctx ctx, const ast_expression *ex)
ast_instantiate(ast_expression, ctx, ast_expression_delete_full);
ast_expression_init(self, NULL);
fromex = &ex->expression;
selfex = &self->expression;
fromex = ex;
selfex = self;
/* This may never be codegen()d */
selfex->codegen = NULL;
@ -184,30 +220,30 @@ ast_expression* ast_type_copy(lex_ctx ctx, const ast_expression *ex)
bool ast_compare_type(ast_expression *a, ast_expression *b)
{
if (a->expression.vtype == TYPE_NIL ||
b->expression.vtype == TYPE_NIL)
if (a->vtype == TYPE_NIL ||
b->vtype == TYPE_NIL)
return true;
if (a->expression.vtype != b->expression.vtype)
if (a->vtype != b->vtype)
return false;
if (!a->expression.next != !b->expression.next)
if (!a->next != !b->next)
return false;
if (vec_size(a->expression.params) != vec_size(b->expression.params))
if (vec_size(a->params) != vec_size(b->params))
return false;
if ((a->expression.flags & AST_FLAG_TYPE_MASK) !=
(b->expression.flags & AST_FLAG_TYPE_MASK) )
if ((a->flags & AST_FLAG_TYPE_MASK) !=
(b->flags & AST_FLAG_TYPE_MASK) )
{
return false;
}
if (vec_size(a->expression.params)) {
if (vec_size(a->params)) {
size_t i;
for (i = 0; i < vec_size(a->expression.params); ++i) {
if (!ast_compare_type((ast_expression*)a->expression.params[i],
(ast_expression*)b->expression.params[i]))
for (i = 0; i < vec_size(a->params); ++i) {
if (!ast_compare_type((ast_expression*)a->params[i],
(ast_expression*)b->params[i]))
return false;
}
}
if (a->expression.next)
return ast_compare_type(a->expression.next, b->expression.next);
if (a->next)
return ast_compare_type(a->next, b->next);
return true;
}
@ -227,43 +263,43 @@ static size_t ast_type_to_string_impl(ast_expression *e, char *buf, size_t bufsi
if (pos + 1 >= bufsize)
goto full;
switch (e->expression.vtype) {
switch (e->vtype) {
case TYPE_VARIANT:
util_strncpy(buf + pos, "(variant)", 9);
return pos + 9;
case TYPE_FIELD:
buf[pos++] = '.';
return ast_type_to_string_impl(e->expression.next, buf, bufsize, pos);
return ast_type_to_string_impl(e->next, buf, bufsize, pos);
case TYPE_POINTER:
if (pos + 3 >= bufsize)
goto full;
buf[pos++] = '*';
buf[pos++] = '(';
pos = ast_type_to_string_impl(e->expression.next, buf, bufsize, pos);
pos = ast_type_to_string_impl(e->next, buf, bufsize, pos);
if (pos + 1 >= bufsize)
goto full;
buf[pos++] = ')';
return pos;
case TYPE_FUNCTION:
pos = ast_type_to_string_impl(e->expression.next, buf, bufsize, pos);
pos = ast_type_to_string_impl(e->next, buf, bufsize, pos);
if (pos + 2 >= bufsize)
goto full;
if (!vec_size(e->expression.params)) {
if (!vec_size(e->params)) {
buf[pos++] = '(';
buf[pos++] = ')';
return pos;
}
buf[pos++] = '(';
pos = ast_type_to_string_impl((ast_expression*)(e->expression.params[0]), buf, bufsize, pos);
for (i = 1; i < vec_size(e->expression.params); ++i) {
pos = ast_type_to_string_impl((ast_expression*)(e->params[0]), buf, bufsize, pos);
for (i = 1; i < vec_size(e->params); ++i) {
if (pos + 2 >= bufsize)
goto full;
buf[pos++] = ',';
buf[pos++] = ' ';
pos = ast_type_to_string_impl((ast_expression*)(e->expression.params[i]), buf, bufsize, pos);
pos = ast_type_to_string_impl((ast_expression*)(e->params[i]), buf, bufsize, pos);
}
if (pos + 1 >= bufsize)
goto full;
@ -271,18 +307,18 @@ static size_t ast_type_to_string_impl(ast_expression *e, char *buf, size_t bufsi
return pos;
case TYPE_ARRAY:
pos = ast_type_to_string_impl(e->expression.next, buf, bufsize, pos);
pos = ast_type_to_string_impl(e->next, buf, bufsize, pos);
if (pos + 1 >= bufsize)
goto full;
buf[pos++] = '[';
pos += util_snprintf(buf + pos, bufsize - pos - 1, "%i", (int)e->expression.count);
pos += util_snprintf(buf + pos, bufsize - pos - 1, "%i", (int)e->count);
if (pos + 1 >= bufsize)
goto full;
buf[pos++] = ']';
return pos;
default:
typestr = type_name[e->expression.vtype];
typestr = type_name[e->vtype];
typelen = strlen(typestr);
if (pos + typelen >= bufsize)
goto full;
@ -303,6 +339,7 @@ void ast_type_to_string(ast_expression *e, char *buf, size_t bufsize)
buf[pos] = 0;
}
static bool ast_value_codegen(ast_value *self, ast_function *func, bool lvalue, ir_value **out);
ast_value* ast_value_new(lex_ctx ctx, const char *name, int t)
{
ast_instantiate(ast_value, ctx, ast_value_delete);
@ -317,8 +354,9 @@ ast_value* ast_value_new(lex_ctx ctx, const char *name, int t)
self->cvq = CV_NONE;
self->hasvalue = false;
self->isimm = false;
self->uses = 0;
self->uses = 0;
memset(&self->constval, 0, sizeof(self->constval));
self->initlist = NULL;
self->ir_v = NULL;
self->ir_values = NULL;
@ -362,6 +400,20 @@ void ast_value_delete(ast_value* self)
if (self->desc)
mem_d(self->desc);
if (self->initlist) {
if (self->expression.next->vtype == TYPE_STRING) {
/* strings are allocated, free them */
size_t i, len = vec_size(self->initlist);
/* in theory, len should be expression.count
* but let's not take any chances */
for (i = 0; i < len; ++i) {
if (self->initlist[i].vstring)
mem_d(self->initlist[i].vstring);
}
}
vec_free(self->initlist);
}
ast_expression_delete((ast_expression*)self);
mem_d(self);
}
@ -407,7 +459,7 @@ ast_binary* ast_binary_new(lex_ctx ctx, int op,
else if (op == INSTR_MUL_V)
self->expression.vtype = TYPE_FLOAT;
else
self->expression.vtype = left->expression.vtype;
self->expression.vtype = left->vtype;
/* references all */
self->refs = AST_REF_ALL;
@ -501,11 +553,11 @@ void ast_return_delete(ast_return *self)
ast_entfield* ast_entfield_new(lex_ctx ctx, ast_expression *entity, ast_expression *field)
{
if (field->expression.vtype != TYPE_FIELD) {
if (field->vtype != TYPE_FIELD) {
compile_error(ctx, "ast_entfield_new with expression not of type field");
return NULL;
}
return ast_entfield_new_force(ctx, entity, field, field->expression.next);
return ast_entfield_new_force(ctx, entity, field, field->next);
}
ast_entfield* ast_entfield_new_force(lex_ctx ctx, ast_expression *entity, ast_expression *field, const ast_expression *outtype)
@ -545,9 +597,9 @@ ast_member* ast_member_new(lex_ctx ctx, ast_expression *owner, unsigned int fiel
return NULL;
}
if (owner->expression.vtype != TYPE_VECTOR &&
owner->expression.vtype != TYPE_FIELD) {
compile_error(ctx, "member-access on an invalid owner of type %s", type_name[owner->expression.vtype]);
if (owner->vtype != TYPE_VECTOR &&
owner->vtype != TYPE_FIELD) {
compile_error(ctx, "member-access on an invalid owner of type %s", type_name[owner->vtype]);
mem_d(self);
return NULL;
}
@ -555,7 +607,7 @@ ast_member* ast_member_new(lex_ctx ctx, ast_expression *owner, unsigned int fiel
ast_expression_init((ast_expression*)self, (ast_expression_codegen*)&ast_member_codegen);
self->expression.node.keep = true; /* keep */
if (owner->expression.vtype == TYPE_VECTOR) {
if (owner->vtype == TYPE_VECTOR) {
self->expression.vtype = TYPE_FLOAT;
self->expression.next = NULL;
} else {
@ -604,7 +656,7 @@ ast_array_index* ast_array_index_new(lex_ctx ctx, ast_expression *array, ast_exp
ast_expression *outtype;
ast_instantiate(ast_array_index, ctx, ast_array_index_delete);
outtype = array->expression.next;
outtype = array->next;
if (!outtype) {
mem_d(self);
/* Error: field has no type... */
@ -619,7 +671,7 @@ ast_array_index* ast_array_index_new(lex_ctx ctx, ast_expression *array, ast_exp
ast_propagate_effects(self, index);
ast_type_adopt(self, outtype);
if (array->expression.vtype == TYPE_FIELD && outtype->expression.vtype == TYPE_ARRAY) {
if (array->vtype == TYPE_FIELD && outtype->vtype == TYPE_ARRAY) {
if (self->expression.vtype != TYPE_ARRAY) {
compile_error(ast_ctx(self), "array_index node on type");
ast_array_index_delete(self);
@ -693,7 +745,7 @@ ast_ternary* ast_ternary_new(lex_ctx ctx, ast_expression *cond, ast_expression *
ast_propagate_effects(self, ontrue);
ast_propagate_effects(self, onfalse);
if (ontrue->expression.vtype == TYPE_NIL)
if (ontrue->vtype == TYPE_NIL)
exprtype = onfalse;
ast_type_adopt(self, exprtype);
@ -830,7 +882,7 @@ void ast_label_delete(ast_label *self)
mem_d(self);
}
void ast_label_register_goto(ast_label *self, ast_goto *g)
static void ast_label_register_goto(ast_label *self, ast_goto *g)
{
vec_push(self->gotos, g);
}
@ -863,7 +915,7 @@ ast_call* ast_call_new(lex_ctx ctx,
ast_expression *funcexpr)
{
ast_instantiate(ast_call, ctx, ast_call_delete);
if (!funcexpr->expression.next) {
if (!funcexpr->next) {
compile_error(ctx, "not a function");
mem_d(self);
return NULL;
@ -876,7 +928,7 @@ ast_call* ast_call_new(lex_ctx ctx,
self->func = funcexpr;
self->va_count = NULL;
ast_type_adopt(self, funcexpr->expression.next);
ast_type_adopt(self, funcexpr->next);
return self;
}
@ -906,14 +958,14 @@ bool ast_call_check_types(ast_call *self)
bool retval = true;
const ast_expression *func = self->func;
size_t count = vec_size(self->params);
if (count > vec_size(func->expression.params))
count = vec_size(func->expression.params);
if (count > vec_size(func->params))
count = vec_size(func->params);
for (i = 0; i < count; ++i) {
if (!ast_compare_type(self->params[i], (ast_expression*)(func->expression.params[i])))
if (!ast_compare_type(self->params[i], (ast_expression*)(func->params[i])))
{
ast_type_to_string(self->params[i], tgot, sizeof(tgot));
ast_type_to_string((ast_expression*)func->expression.params[i], texp, sizeof(texp));
ast_type_to_string((ast_expression*)func->params[i], texp, sizeof(texp));
compile_error(ast_ctx(self), "invalid type for parameter %u in function call: expected %s, got %s",
(unsigned int)(i+1), texp, tgot);
/* we don't immediately return */
@ -921,12 +973,12 @@ bool ast_call_check_types(ast_call *self)
}
}
count = vec_size(self->params);
if (count > vec_size(func->expression.params) && func->expression.varparam) {
if (count > vec_size(func->params) && func->varparam) {
for (; i < count; ++i) {
if (!ast_compare_type(self->params[i], func->expression.varparam))
if (!ast_compare_type(self->params[i], func->varparam))
{
ast_type_to_string(self->params[i], tgot, sizeof(tgot));
ast_type_to_string(func->expression.varparam, texp, sizeof(texp));
ast_type_to_string(func->varparam, texp, sizeof(texp));
compile_error(ast_ctx(self), "invalid type for parameter %u in function call: expected %s, got %s",
(unsigned int)(i+1), texp, tgot);
/* we don't immediately return */
@ -990,7 +1042,7 @@ bool ast_block_add_expr(ast_block *self, ast_expression *e)
void ast_block_collect(ast_block *self, ast_expression *expr)
{
vec_push(self->collect, expr);
expr->expression.node.keep = true;
expr->node.keep = true;
}
void ast_block_delete(ast_block *self)
@ -1048,9 +1100,10 @@ ast_function* ast_function_new(lex_ctx ctx, const char *name, ast_value *vtype)
vtype->hasvalue = true;
vtype->constval.vfunc = self;
self->varargs = NULL;
self->argc = NULL;
self->fixedparams = NULL;
self->varargs = NULL;
self->argc = NULL;
self->fixedparams = NULL;
self->return_value = NULL;
return self;
}
@ -1080,10 +1133,12 @@ void ast_function_delete(ast_function *self)
ast_delete(self->argc);
if (self->fixedparams)
ast_unref(self->fixedparams);
if (self->return_value)
ast_unref(self->return_value);
mem_d(self);
}
const char* ast_function_label(ast_function *self, const char *prefix)
static const char* ast_function_label(ast_function *self, const char *prefix)
{
size_t id;
size_t len;
@ -1117,12 +1172,12 @@ const char* ast_function_label(ast_function *self, const char *prefix)
* But I can't imagine a pituation where the output is truly unnecessary.
*/
void _ast_codegen_output_type(ast_expression_common *self, ir_value *out)
static void _ast_codegen_output_type(ast_expression *self, ir_value *out)
{
if (out->vtype == TYPE_FIELD)
out->fieldtype = self->next->expression.vtype;
out->fieldtype = self->next->vtype;
if (out->vtype == TYPE_FUNCTION)
out->outtype = self->next->expression.vtype;
out->outtype = self->next->vtype;
}
#define codegen_output_type(a,o) (_ast_codegen_output_type(&((a)->expression),(o)))
@ -1163,7 +1218,7 @@ bool ast_global_codegen(ast_value *self, ir_builder *ir, bool isfield)
if (self->hasvalue && self->expression.vtype == TYPE_FUNCTION)
{
ir_function *func = ir_builder_create_function(ir, self->name, self->expression.next->expression.vtype);
ir_function *func = ir_builder_create_function(ir, self->name, self->expression.next->vtype);
if (!func)
return false;
func->context = ast_ctx(self);
@ -1185,14 +1240,14 @@ bool ast_global_codegen(ast_value *self, ir_builder *ir, bool isfield)
goto error;
}
if (fieldtype->expression.vtype == TYPE_ARRAY) {
if (fieldtype->vtype == TYPE_ARRAY) {
size_t ai;
char *name;
size_t namelen;
ast_expression_common *elemtype;
int vtype;
ast_value *array = (ast_value*)fieldtype;
ast_expression *elemtype;
int vtype;
ast_value *array = (ast_value*)fieldtype;
if (!ast_istype(fieldtype, ast_value)) {
compile_error(ast_ctx(self), "internal error: ast_value required");
@ -1203,7 +1258,7 @@ bool ast_global_codegen(ast_value *self, ir_builder *ir, bool isfield)
if (!array->expression.count || array->expression.count > OPTS_OPTION_U32(OPTION_MAX_ARRAY_SIZE))
compile_error(ast_ctx(self), "Invalid array of size %lu", (unsigned long)array->expression.count);
elemtype = &array->expression.next->expression;
elemtype = array->expression.next;
vtype = elemtype->vtype;
v = ir_builder_create_field(ir, self->name, vtype);
@ -1242,7 +1297,7 @@ bool ast_global_codegen(ast_value *self, ir_builder *ir, bool isfield)
}
else
{
v = ir_builder_create_field(ir, self->name, self->expression.next->expression.vtype);
v = ir_builder_create_field(ir, self->name, self->expression.next->vtype);
if (!v)
return false;
v->context = ast_ctx(self);
@ -1258,7 +1313,7 @@ bool ast_global_codegen(ast_value *self, ir_builder *ir, bool isfield)
char *name;
size_t namelen;
ast_expression_common *elemtype = &self->expression.next->expression;
ast_expression *elemtype = self->expression.next;
int vtype = elemtype->vtype;
/* same as with field arrays */
@ -1366,7 +1421,7 @@ error: /* clean up */
return false;
}
bool ast_local_codegen(ast_value *self, ir_function *func, bool param)
static bool ast_local_codegen(ast_value *self, ir_function *func, bool param)
{
ir_value *v = NULL;
@ -1388,7 +1443,7 @@ bool ast_local_codegen(ast_value *self, ir_function *func, bool param)
char *name;
size_t namelen;
ast_expression_common *elemtype = &self->expression.next->expression;
ast_expression *elemtype = self->expression.next;
int vtype = elemtype->vtype;
func->flags |= IR_FLAG_HAS_ARRAYS;
@ -1535,7 +1590,7 @@ bool ast_function_codegen(ast_function *self, ir_builder *ir)
{
ir_function *irf;
ir_value *dummy;
ast_expression_common *ec;
ast_expression *ec;
ast_expression_codegen *cgen;
size_t i;
@ -1552,7 +1607,7 @@ bool ast_function_codegen(ast_function *self, ir_builder *ir)
for (i = 0; i < vec_size(ec->params); ++i)
{
if (ec->params[i]->expression.vtype == TYPE_FIELD)
vec_push(irf->params, ec->params[i]->expression.next->expression.vtype);
vec_push(irf->params, ec->params[i]->expression.next->vtype);
else
vec_push(irf->params, ec->params[i]->expression.vtype);
if (!self->builtin) {
@ -1572,6 +1627,12 @@ bool ast_function_codegen(ast_function *self, ir_builder *ir)
return true;
}
/* have a local return value variable? */
if (self->return_value) {
if (!ast_local_codegen(self->return_value, self->ir_func, false))
return false;
}
if (!vec_size(self->blocks)) {
compile_error(ast_ctx(self), "function `%s` has no body", self->name);
return false;
@ -1617,14 +1678,19 @@ bool ast_function_codegen(ast_function *self, ir_builder *ir)
if (!self->curblock->final)
{
if (!self->vtype->expression.next ||
self->vtype->expression.next->expression.vtype == TYPE_VOID)
self->vtype->expression.next->vtype == TYPE_VOID)
{
return ir_block_create_return(self->curblock, ast_ctx(self), NULL);
}
else if (vec_size(self->curblock->entries) || self->curblock == irf->first)
{
/* error("missing return"); */
if (compile_warning(ast_ctx(self), WARN_MISSING_RETURN_VALUES,
if (self->return_value) {
cgen = self->return_value->expression.codegen;
if (!(*cgen)((ast_expression*)(self->return_value), self, false, &dummy))
return false;
return ir_block_create_return(self->curblock, ast_ctx(self), dummy);
}
else if (compile_warning(ast_ctx(self), WARN_MISSING_RETURN_VALUES,
"control reaches end of non-void function (`%s`) via %s",
self->name, self->curblock->label))
{
@ -1686,7 +1752,7 @@ bool ast_block_codegen(ast_block *self, ast_function *func, bool lvalue, ir_valu
return false;
continue;
}
gen = self->exprs[i]->expression.codegen;
gen = self->exprs[i]->codegen;
if (!(*gen)(self->exprs[i], func, false, out))
return false;
}
@ -1750,7 +1816,7 @@ bool ast_store_codegen(ast_store *self, ast_function *func, bool lvalue, ir_valu
if (!(*cgen)((ast_expression*)(arr->setter), func, true, &funval))
return false;
cgen = self->source->expression.codegen;
cgen = self->source->codegen;
if (!(*cgen)((ast_expression*)(self->source), func, false, &right))
return false;
@ -1765,13 +1831,13 @@ bool ast_store_codegen(ast_store *self, ast_function *func, bool lvalue, ir_valu
{
/* regular code */
cgen = self->dest->expression.codegen;
cgen = self->dest->codegen;
/* lvalue! */
if (!(*cgen)((ast_expression*)(self->dest), func, true, &left))
return false;
self->expression.outl = left;
cgen = self->source->expression.codegen;
cgen = self->source->codegen;
/* rvalue! */
if (!(*cgen)((ast_expression*)(self->source), func, false, &right))
return false;
@ -1823,7 +1889,7 @@ bool ast_binary_codegen(ast_binary *self, ast_function *func, bool lvalue, ir_va
merge = ir_function_create_block(ast_ctx(self), func->ir_func, ast_function_label(func, "sce_merge"));
/* generate the left expression */
cgen = self->left->expression.codegen;
cgen = self->left->codegen;
if (!(*cgen)((ast_expression*)(self->left), func, false, &left))
return false;
/* remember the block */
@ -1846,7 +1912,7 @@ bool ast_binary_codegen(ast_binary *self, ast_function *func, bool lvalue, ir_va
/* enter the right-expression's block */
func->curblock = other;
/* generate */
cgen = self->right->expression.codegen;
cgen = self->right->codegen;
if (!(*cgen)((ast_expression*)(self->right), func, false, &right))
return false;
/* remember block */
@ -1909,11 +1975,11 @@ bool ast_binary_codegen(ast_binary *self, ast_function *func, bool lvalue, ir_va
return true;
}
cgen = self->left->expression.codegen;
cgen = self->left->codegen;
if (!(*cgen)((ast_expression*)(self->left), func, false, &left))
return false;
cgen = self->right->expression.codegen;
cgen = self->right->codegen;
if (!(*cgen)((ast_expression*)(self->right), func, false, &right))
return false;
@ -1964,12 +2030,12 @@ bool ast_binstore_codegen(ast_binstore *self, ast_function *func, bool lvalue, i
if (!(*cgen)((ast_expression*)(idx), func, false, &iridx))
return false;
}
cgen = self->dest->expression.codegen;
cgen = self->dest->codegen;
if (!(*cgen)((ast_expression*)(self->dest), func, false, &leftr))
return false;
/* source as rvalue only */
cgen = self->source->expression.codegen;
cgen = self->source->codegen;
if (!(*cgen)((ast_expression*)(self->source), func, false, &right))
return false;
@ -2007,7 +2073,7 @@ bool ast_binstore_codegen(ast_binstore *self, ast_function *func, bool lvalue, i
self->expression.outr = bin;
} else {
/* now store them */
cgen = self->dest->expression.codegen;
cgen = self->dest->codegen;
/* lvalue of destination */
if (!(*cgen)((ast_expression*)(self->dest), func, true, &leftl))
return false;
@ -2046,7 +2112,7 @@ bool ast_unary_codegen(ast_unary *self, ast_function *func, bool lvalue, ir_valu
return true;
}
cgen = self->operand->expression.codegen;
cgen = self->operand->codegen;
/* lvalue! */
if (!(*cgen)((ast_expression*)(self->operand), func, false, &operand))
return false;
@ -2082,7 +2148,7 @@ bool ast_return_codegen(ast_return *self, ast_function *func, bool lvalue, ir_va
self->expression.outr = (ir_value*)1;
if (self->operand) {
cgen = self->operand->expression.codegen;
cgen = self->operand->codegen;
/* lvalue! */
if (!(*cgen)((ast_expression*)(self->operand), func, false, &operand))
return false;
@ -2117,11 +2183,11 @@ bool ast_entfield_codegen(ast_entfield *self, ast_function *func, bool lvalue, i
return true;
}
cgen = self->entity->expression.codegen;
cgen = self->entity->codegen;
if (!(*cgen)((ast_expression*)(self->entity), func, false, &ent))
return false;
cgen = self->field->expression.codegen;
cgen = self->field->codegen;
if (!(*cgen)((ast_expression*)(self->field), func, false, &field))
return false;
@ -2169,12 +2235,12 @@ bool ast_member_codegen(ast_member *self, ast_function *func, bool lvalue, ir_va
return true;
}
cgen = self->owner->expression.codegen;
cgen = self->owner->codegen;
if (!(*cgen)((ast_expression*)(self->owner), func, false, &vec))
return false;
if (vec->vtype != TYPE_VECTOR &&
!(vec->vtype == TYPE_FIELD && self->owner->expression.next->expression.vtype == TYPE_VECTOR))
!(vec->vtype == TYPE_FIELD && self->owner->next->vtype == TYPE_VECTOR))
{
return false;
}
@ -2228,7 +2294,7 @@ bool ast_array_index_codegen(ast_array_index *self, ast_function *func, bool lva
return false;
}
cgen = self->index->expression.codegen;
cgen = self->index->codegen;
if (!(*cgen)((ast_expression*)(self->index), func, false, &iridx))
return false;
@ -2250,7 +2316,7 @@ bool ast_array_index_codegen(ast_array_index *self, ast_function *func, bool lva
if (idx->expression.vtype == TYPE_FLOAT) {
unsigned int arridx = idx->constval.vfloat;
if (arridx >= self->array->expression.count)
if (arridx >= self->array->count)
{
compile_error(ast_ctx(self), "array index out of bounds: %i", arridx);
return false;
@ -2259,7 +2325,7 @@ bool ast_array_index_codegen(ast_array_index *self, ast_function *func, bool lva
}
else if (idx->expression.vtype == TYPE_INTEGER) {
unsigned int arridx = idx->constval.vint;
if (arridx >= self->array->expression.count)
if (arridx >= self->array->count)
{
compile_error(ast_ctx(self), "array index out of bounds: %i", arridx);
return false;
@ -2300,7 +2366,7 @@ bool ast_ifthen_codegen(ast_ifthen *self, ast_function *func, bool lvalue, ir_va
self->expression.outr = (ir_value*)1;
/* generate the condition */
cgen = self->cond->expression.codegen;
cgen = self->cond->codegen;
if (!(*cgen)((ast_expression*)(self->cond), func, false, &condval))
return false;
/* update the block which will get the jump - because short-logic or ternaries may have changed this */
@ -2318,7 +2384,7 @@ bool ast_ifthen_codegen(ast_ifthen *self, ast_function *func, bool lvalue, ir_va
func->curblock = ontrue;
/* generate */
cgen = self->on_true->expression.codegen;
cgen = self->on_true->codegen;
if (!(*cgen)((ast_expression*)(self->on_true), func, false, &dummy))
return false;
@ -2338,7 +2404,7 @@ bool ast_ifthen_codegen(ast_ifthen *self, ast_function *func, bool lvalue, ir_va
func->curblock = onfalse;
/* generate */
cgen = self->on_false->expression.codegen;
cgen = self->on_false->codegen;
if (!(*cgen)((ast_expression*)(self->on_false), func, false, &dummy))
return false;
@ -2407,7 +2473,7 @@ bool ast_ternary_codegen(ast_ternary *self, ast_function *func, bool lvalue, ir_
/* generate the condition */
func->curblock = cond;
cgen = self->cond->expression.codegen;
cgen = self->cond->codegen;
if (!(*cgen)((ast_expression*)(self->cond), func, false, &condval))
return false;
cond_out = func->curblock;
@ -2422,7 +2488,7 @@ bool ast_ternary_codegen(ast_ternary *self, ast_function *func, bool lvalue, ir_
func->curblock = ontrue;
/* generate */
cgen = self->on_true->expression.codegen;
cgen = self->on_true->codegen;
if (!(*cgen)((ast_expression*)(self->on_true), func, false, &trueval))
return false;
@ -2439,7 +2505,7 @@ bool ast_ternary_codegen(ast_ternary *self, ast_function *func, bool lvalue, ir_
func->curblock = onfalse;
/* generate */
cgen = self->on_false->expression.codegen;
cgen = self->on_false->codegen;
if (!(*cgen)((ast_expression*)(self->on_false), func, false, &falseval))
return false;
@ -2536,7 +2602,7 @@ bool ast_loop_codegen(ast_loop *self, ast_function *func, bool lvalue, ir_value
*/
if (self->initexpr)
{
cgen = self->initexpr->expression.codegen;
cgen = self->initexpr->codegen;
if (!(*cgen)((ast_expression*)(self->initexpr), func, false, &dummy))
return false;
}
@ -2560,7 +2626,7 @@ bool ast_loop_codegen(ast_loop *self, ast_function *func, bool lvalue, ir_value
func->curblock = bprecond;
/* generate */
cgen = self->precond->expression.codegen;
cgen = self->precond->codegen;
if (!(*cgen)((ast_expression*)(self->precond), func, false, &precond))
return false;
@ -2614,7 +2680,7 @@ bool ast_loop_codegen(ast_loop *self, ast_function *func, bool lvalue, ir_value
/* generate */
if (self->body) {
cgen = self->body->expression.codegen;
cgen = self->body->codegen;
if (!(*cgen)((ast_expression*)(self->body), func, false, &dummy))
return false;
}
@ -2631,7 +2697,7 @@ bool ast_loop_codegen(ast_loop *self, ast_function *func, bool lvalue, ir_value
func->curblock = bpostcond;
/* generate */
cgen = self->postcond->expression.codegen;
cgen = self->postcond->codegen;
if (!(*cgen)((ast_expression*)(self->postcond), func, false, &postcond))
return false;
@ -2645,7 +2711,7 @@ bool ast_loop_codegen(ast_loop *self, ast_function *func, bool lvalue, ir_value
func->curblock = bincrement;
/* generate */
cgen = self->increment->expression.codegen;
cgen = self->increment->codegen;
if (!(*cgen)((ast_expression*)(self->increment), func, false, &dummy))
return false;
@ -2794,7 +2860,7 @@ bool ast_switch_codegen(ast_switch *self, ast_function *func, bool lvalue, ir_va
(void)lvalue;
(void)out;
cgen = self->operand->expression.codegen;
cgen = self->operand->codegen;
if (!(*cgen)((ast_expression*)(self->operand), func, false, &irop))
return false;
@ -2827,7 +2893,7 @@ bool ast_switch_codegen(ast_switch *self, ast_function *func, bool lvalue, ir_va
if (swcase->value) {
/* A regular case */
/* generate the condition operand */
cgen = swcase->value->expression.codegen;
cgen = swcase->value->codegen;
if (!(*cgen)((ast_expression*)(swcase->value), func, false, &val))
return false;
/* generate the condition */
@ -2855,7 +2921,7 @@ bool ast_switch_codegen(ast_switch *self, ast_function *func, bool lvalue, ir_va
/* enter the case */
func->curblock = bcase;
cgen = swcase->code->expression.codegen;
cgen = swcase->code->codegen;
if (!(*cgen)((ast_expression*)swcase->code, func, false, &dummy))
return false;
@ -2900,7 +2966,7 @@ bool ast_switch_codegen(ast_switch *self, ast_function *func, bool lvalue, ir_va
}
/* Now generate the default code */
cgen = def_case->code->expression.codegen;
cgen = def_case->code->codegen;
if (!(*cgen)((ast_expression*)def_case->code, func, false, &dummy))
return false;
@ -3025,7 +3091,7 @@ bool ast_call_codegen(ast_call *self, ast_function *func, bool lvalue, ir_value
return true;
}
cgen = self->func->expression.codegen;
cgen = self->func->codegen;
if (!(*cgen)((ast_expression*)(self->func), func, false, &funval))
return false;
if (!funval)
@ -3039,7 +3105,7 @@ bool ast_call_codegen(ast_call *self, ast_function *func, bool lvalue, ir_value
ir_value *param;
ast_expression *expr = self->params[i];
cgen = expr->expression.codegen;
cgen = expr->codegen;
if (!(*cgen)(expr, func, false, &param))
goto error;
if (!param)
@ -3051,7 +3117,7 @@ bool ast_call_codegen(ast_call *self, ast_function *func, bool lvalue, ir_value
if (self->va_count) {
ir_value *va_count;
ir_builder *builder = func->curblock->owner->owner;
cgen = self->va_count->expression.codegen;
cgen = self->va_count->codegen;
if (!(*cgen)((ast_expression*)(self->va_count), func, false, &va_count))
return false;
if (!ir_block_create_store_op(func->curblock, ast_ctx(self), INSTR_STORE_F,
@ -3063,7 +3129,7 @@ bool ast_call_codegen(ast_call *self, ast_function *func, bool lvalue, ir_value
callinstr = ir_block_create_call(func->curblock, ast_ctx(self),
ast_function_label(func, "call"),
funval, !!(self->func->expression.flags & AST_FLAG_NORETURN));
funval, !!(self->func->flags & AST_FLAG_NORETURN));
if (!callinstr)
goto error;

172
ast.h
View file

@ -1,6 +1,7 @@
/*
* Copyright (C) 2012, 2013
* Wolfgang Bumiller
* Dale Weiler
*
* 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
@ -28,8 +29,8 @@
* "main" ast node types for now.
*/
typedef union ast_node_u ast_node;
typedef union ast_expression_u ast_expression;
typedef struct ast_node_common ast_node;
typedef struct ast_expression_common ast_expression;
typedef struct ast_value_s ast_value;
typedef struct ast_function_s ast_function;
@ -75,14 +76,14 @@ enum {
TYPE_ast_goto /* 20 */
};
#define ast_istype(x, t) ( ((ast_node_common*)x)->nodetype == (TYPE_##t) )
#define ast_ctx(node) (((ast_node_common*)(node))->context)
#define ast_side_effects(node) (((ast_node_common*)(node))->side_effects)
#define ast_istype(x, t) ( ((ast_node*)x)->nodetype == (TYPE_##t) )
#define ast_ctx(node) (((ast_node*)(node))->context)
#define ast_side_effects(node) (((ast_node*)(node))->side_effects)
/* Node interface with common components
*/
typedef void ast_node_delete(ast_node*);
typedef struct
struct ast_node_common
{
lex_ctx context;
/* I don't feel comfortable using keywords like 'delete' as names... */
@ -93,14 +94,14 @@ typedef struct
*/
bool keep;
bool side_effects;
} ast_node_common;
};
#define ast_delete(x) (*( ((ast_node*)(x))->node.destroy ))((ast_node*)(x))
#define ast_unref(x) do \
{ \
if (! (((ast_node*)(x))->node.keep) ) { \
ast_delete(x); \
} \
#define ast_delete(x) (*( ((ast_node*)(x))->destroy ))((ast_node*)(x))
#define ast_unref(x) do \
{ \
if (! (((ast_node*)(x))->keep) ) { \
ast_delete(x); \
} \
} while(0)
/* Expression interface
@ -122,9 +123,9 @@ typedef bool ast_expression_codegen(ast_expression*,
* type `expression`, so the ast_ident's codegen would search for
* variables through the environment (or functions, constants...).
*/
typedef struct
struct ast_expression_common
{
ast_node_common node;
ast_node node;
ast_expression_codegen *codegen;
int vtype;
ast_expression *next;
@ -143,7 +144,7 @@ typedef struct
*/
ir_value *outl;
ir_value *outr;
} ast_expression_common;
};
#define AST_FLAG_VARIADIC (1<<0)
#define AST_FLAG_NORETURN (1<<1)
#define AST_FLAG_INLINE (1<<2)
@ -161,9 +162,18 @@ typedef struct
* typedef float foo;
* is like creating a 'float foo', foo serving as the type's name.
*/
typedef union {
double vfloat;
int vint;
vector vvec;
const char *vstring;
int ventity;
ast_function *vfunc;
ast_value *vfield;
} basic_value_t;
struct ast_value_s
{
ast_expression_common expression;
ast_expression expression;
const char *name;
const char *desc;
@ -179,15 +189,12 @@ struct ast_value_s
bool isfield; /* this declares a field */
bool isimm; /* an immediate, not just const */
bool hasvalue;
union {
double vfloat;
int vint;
vector vvec;
const char *vstring;
int ventity;
ast_function *vfunc;
ast_value *vfield;
} constval;
basic_value_t constval;
/* for TYPE_ARRAY we have an optional vector
* of constants when an initializer list
* was provided.
*/
basic_value_t *initlist;
/* usecount for the parser */
size_t uses;
@ -208,8 +215,11 @@ void ast_value_delete(ast_value*);
bool ast_value_set_name(ast_value*, const char *name);
/*
bool ast_value_codegen(ast_value*, ast_function*, bool lvalue, ir_value**);
bool ast_local_codegen(ast_value *self, ir_function *func, bool isparam);
*/
bool ast_global_codegen(ast_value *self, ir_builder *ir, bool isfield);
void ast_value_params_add(ast_value*, ast_value*);
@ -233,7 +243,7 @@ typedef enum ast_binary_ref_s {
*/
struct ast_binary_s
{
ast_expression_common expression;
ast_expression expression;
int op;
ast_expression *left;
@ -245,9 +255,6 @@ ast_binary* ast_binary_new(lex_ctx ctx,
int op,
ast_expression *left,
ast_expression *right);
void ast_binary_delete(ast_binary*);
bool ast_binary_codegen(ast_binary*, ast_function*, bool lvalue, ir_value**);
/* Binstore
*
@ -256,7 +263,7 @@ bool ast_binary_codegen(ast_binary*, ast_function*, bool lvalue, ir_value**);
*/
struct ast_binstore_s
{
ast_expression_common expression;
ast_expression expression;
int opstore;
int opbin;
@ -270,9 +277,6 @@ ast_binstore* ast_binstore_new(lex_ctx ctx,
int op,
ast_expression *left,
ast_expression *right);
void ast_binstore_delete(ast_binstore*);
bool ast_binstore_codegen(ast_binstore*, ast_function*, bool lvalue, ir_value**);
/* Unary
*
@ -280,7 +284,7 @@ bool ast_binstore_codegen(ast_binstore*, ast_function*, bool lvalue, ir_value**)
*/
struct ast_unary_s
{
ast_expression_common expression;
ast_expression expression;
int op;
ast_expression *operand;
@ -288,9 +292,6 @@ struct ast_unary_s
ast_unary* ast_unary_new(lex_ctx ctx,
int op,
ast_expression *expr);
void ast_unary_delete(ast_unary*);
bool ast_unary_codegen(ast_unary*, ast_function*, bool lvalue, ir_value**);
/* Return
*
@ -300,14 +301,11 @@ bool ast_unary_codegen(ast_unary*, ast_function*, bool lvalue, ir_value**);
*/
struct ast_return_s
{
ast_expression_common expression;
ast_expression expression;
ast_expression *operand;
};
ast_return* ast_return_new(lex_ctx ctx,
ast_expression *expr);
void ast_return_delete(ast_return*);
bool ast_return_codegen(ast_return*, ast_function*, bool lvalue, ir_value**);
/* Entity-field
*
@ -324,7 +322,7 @@ bool ast_return_codegen(ast_return*, ast_function*, bool lvalue, ir_value**);
*/
struct ast_entfield_s
{
ast_expression_common expression;
ast_expression expression;
/* The entity can come from an expression of course. */
ast_expression *entity;
/* As can the field, it just must result in a value of TYPE_FIELD */
@ -332,9 +330,6 @@ struct ast_entfield_s
};
ast_entfield* ast_entfield_new(lex_ctx ctx, ast_expression *entity, ast_expression *field);
ast_entfield* ast_entfield_new_force(lex_ctx ctx, ast_expression *entity, ast_expression *field, const ast_expression *outtype);
void ast_entfield_delete(ast_entfield*);
bool ast_entfield_codegen(ast_entfield*, ast_function*, bool lvalue, ir_value**);
/* Member access:
*
@ -343,7 +338,7 @@ bool ast_entfield_codegen(ast_entfield*, ast_function*, bool lvalue, ir_value**)
*/
struct ast_member_s
{
ast_expression_common expression;
ast_expression expression;
ast_expression *owner;
unsigned int field;
const char *name;
@ -353,7 +348,6 @@ ast_member* ast_member_new(lex_ctx ctx, ast_expression *owner, unsigned int fiel
void ast_member_delete(ast_member*);
bool ast_member_set_name(ast_member*, const char *name);
bool ast_member_codegen(ast_member*, ast_function*, bool lvalue, ir_value**);
/* Array index access:
*
@ -367,14 +361,11 @@ bool ast_member_codegen(ast_member*, ast_function*, bool lvalue, ir_value**);
*/
struct ast_array_index_s
{
ast_expression_common expression;
ast_expression expression;
ast_expression *array;
ast_expression *index;
};
ast_array_index* ast_array_index_new(lex_ctx ctx, ast_expression *array, ast_expression *index);
void ast_array_index_delete(ast_array_index*);
bool ast_array_index_codegen(ast_array_index*, ast_function*, bool lvalue, ir_value**);
/* Store
*
@ -383,16 +374,13 @@ bool ast_array_index_codegen(ast_array_index*, ast_function*, bool lvalue, ir_va
*/
struct ast_store_s
{
ast_expression_common expression;
ast_expression expression;
int op;
ast_expression *dest;
ast_expression *source;
};
ast_store* ast_store_new(lex_ctx ctx, int op,
ast_expression *d, ast_expression *s);
void ast_store_delete(ast_store*);
bool ast_store_codegen(ast_store*, ast_function*, bool lvalue, ir_value**);
/* If
*
@ -407,16 +395,13 @@ bool ast_store_codegen(ast_store*, ast_function*, bool lvalue, ir_value**);
*/
struct ast_ifthen_s
{
ast_expression_common expression;
ast_expression expression;
ast_expression *cond;
/* It's all just 'expressions', since an ast_block is one too. */
ast_expression *on_true;
ast_expression *on_false;
};
ast_ifthen* ast_ifthen_new(lex_ctx ctx, ast_expression *cond, ast_expression *ontrue, ast_expression *onfalse);
void ast_ifthen_delete(ast_ifthen*);
bool ast_ifthen_codegen(ast_ifthen*, ast_function*, bool lvalue, ir_value**);
/* Ternary expressions...
*
@ -433,16 +418,13 @@ bool ast_ifthen_codegen(ast_ifthen*, ast_function*, bool lvalue, ir_value**);
*/
struct ast_ternary_s
{
ast_expression_common expression;
ast_expression expression;
ast_expression *cond;
/* It's all just 'expressions', since an ast_block is one too. */
ast_expression *on_true;
ast_expression *on_false;
};
ast_ternary* ast_ternary_new(lex_ctx ctx, ast_expression *cond, ast_expression *ontrue, ast_expression *onfalse);
void ast_ternary_delete(ast_ternary*);
bool ast_ternary_codegen(ast_ternary*, ast_function*, bool lvalue, ir_value**);
/* A general loop node
*
@ -469,7 +451,7 @@ continue: // a 'continue' will jump here
*/
struct ast_loop_s
{
ast_expression_common expression;
ast_expression expression;
ast_expression *initexpr;
ast_expression *precond;
ast_expression *postcond;
@ -490,22 +472,16 @@ ast_loop* ast_loop_new(lex_ctx ctx,
ast_expression *postcond, bool post_not,
ast_expression *increment,
ast_expression *body);
void ast_loop_delete(ast_loop*);
bool ast_loop_codegen(ast_loop*, ast_function*, bool lvalue, ir_value**);
/* Break/Continue
*/
struct ast_breakcont_s
{
ast_expression_common expression;
ast_expression expression;
bool is_continue;
unsigned int levels;
};
ast_breakcont* ast_breakcont_new(lex_ctx ctx, bool iscont, unsigned int levels);
void ast_breakcont_delete(ast_breakcont*);
bool ast_breakcont_codegen(ast_breakcont*, ast_function*, bool lvalue, ir_value**);
/* Switch Statements
*
@ -523,16 +499,13 @@ typedef struct {
} ast_switch_case;
struct ast_switch_s
{
ast_expression_common expression;
ast_expression expression;
ast_expression *operand;
ast_switch_case *cases;
};
ast_switch* ast_switch_new(lex_ctx ctx, ast_expression *op);
void ast_switch_delete(ast_switch*);
bool ast_switch_codegen(ast_switch*, ast_function*, bool lvalue, ir_value**);
/* Label nodes
*
@ -540,7 +513,7 @@ bool ast_switch_codegen(ast_switch*, ast_function*, bool lvalue, ir_value**);
*/
struct ast_label_s
{
ast_expression_common expression;
ast_expression expression;
const char *name;
ir_block *irblock;
ast_goto **gotos;
@ -549,10 +522,6 @@ struct ast_label_s
};
ast_label* ast_label_new(lex_ctx ctx, const char *name, bool undefined);
void ast_label_delete(ast_label*);
void ast_label_register_goto(ast_label*, ast_goto*);
bool ast_label_codegen(ast_label*, ast_function*, bool lvalue, ir_value**);
/* GOTO nodes
*
@ -560,18 +529,15 @@ bool ast_label_codegen(ast_label*, ast_function*, bool lvalue, ir_value**);
*/
struct ast_goto_s
{
ast_expression_common expression;
ast_expression expression;
const char *name;
ast_label *target;
ir_block *irblock_from;
};
ast_goto* ast_goto_new(lex_ctx ctx, const char *name);
void ast_goto_delete(ast_goto*);
void ast_goto_set_label(ast_goto*, ast_label*);
bool ast_goto_codegen(ast_goto*, ast_function*, bool lvalue, ir_value**);
/* CALL node
*
* Contains an ast_expression as target, rather than an ast_function/value.
@ -584,15 +550,13 @@ bool ast_goto_codegen(ast_goto*, ast_function*, bool lvalue, ir_value**);
*/
struct ast_call_s
{
ast_expression_common expression;
ast_expression expression;
ast_expression *func;
ast_expression* *params;
ast_expression *va_count;
};
ast_call* ast_call_new(lex_ctx ctx,
ast_expression *funcexpr);
void ast_call_delete(ast_call*);
bool ast_call_codegen(ast_call*, ast_function*, bool lvalue, ir_value**);
bool ast_call_check_types(ast_call*);
/* Blocks
@ -600,7 +564,7 @@ bool ast_call_check_types(ast_call*);
*/
struct ast_block_s
{
ast_expression_common expression;
ast_expression expression;
ast_value* *locals;
ast_expression* *exprs;
@ -609,8 +573,6 @@ struct ast_block_s
ast_block* ast_block_new(lex_ctx ctx);
void ast_block_delete(ast_block*);
void ast_block_set_type(ast_block*, ast_expression *from);
bool ast_block_codegen(ast_block*, ast_function*, bool lvalue, ir_value**);
void ast_block_collect(ast_block*, ast_expression*);
bool GMQCC_WARN ast_block_add_expr(ast_block*, ast_expression*);
@ -627,7 +589,7 @@ bool GMQCC_WARN ast_block_add_expr(ast_block*, ast_expression*);
*/
struct ast_function_s
{
ast_node_common node;
ast_node node;
ast_value *vtype;
const char *name;
@ -660,6 +622,7 @@ struct ast_function_s
ast_value *varargs;
ast_value *argc;
ast_value *fixedparams;
ast_value *return_value;
};
ast_function* ast_function_new(lex_ctx ctx, const char *name, ast_value *vtype);
/* This will NOT delete the underlying ast_value */
@ -667,32 +630,9 @@ void ast_function_delete(ast_function*);
/* For "optimized" builds this can just keep returning "foo"...
* or whatever...
*/
const char* ast_function_label(ast_function*, const char *prefix);
/*const char* ast_function_label(ast_function*, const char *prefix);*/
bool ast_function_codegen(ast_function *self, ir_builder *builder);
bool ast_generate_accessors(ast_value *asvalue, ir_builder *ir);
/* Expression union
*/
union ast_expression_u
{
ast_expression_common expression;
ast_value value;
ast_binary binary;
ast_block block;
ast_ternary ternary;
ast_ifthen ifthen;
ast_store store;
ast_entfield entfield;
};
/* Node union
*/
union ast_node_u
{
ast_node_common node;
ast_expression expression;
};
#endif

219
code.c
View file

@ -21,12 +21,28 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include <string.h>
#include "gmqcc.h"
/* This is outrageous! */
#define QCINT_ENTRY void*
#define QCINT_TO_HASH_ENTRY(q) ((void*)(uintptr_t)(q))
#define HASH_ENTRY_TO_QCINT(h) ((qcint)(uintptr_t)(h))
/*
* We could use the old method of casting to uintptr_t then to void*
* or qcint; however, it's incredibly unsafe for two reasons.
* 1) The compilers aliasing optimization can legally make it unstable
* (it's undefined behaviour).
*
* 2) The cast itself depends on fresh storage (newly allocated in which
* ever function is using the cast macros), the contents of which are
* transferred in a way that the obligation to release storage is not
* propagated.
*/
typedef union {
void *enter;
qcint leave;
} code_hash_entry_t;
/* Some sanity macros */
#define CODE_HASH_ENTER(ENTRY) ((ENTRY).enter)
#define CODE_HASH_LEAVE(ENTRY) ((ENTRY).leave)
void code_push_statement(code_t *code, prog_section_statement *stmt, int linenum)
{
@ -72,11 +88,9 @@ code_t *code_init() {
void *code_util_str_htgeth(hash_table_t *ht, const char *key, size_t bin);
uint32_t code_genstring(code_t *code, const char *str)
{
uint32_t off;
size_t hash;
QCINT_ENTRY existing;
uint32_t code_genstring(code_t *code, const char *str) {
size_t hash;
code_hash_entry_t existing;
if (!str)
return 0;
@ -90,21 +104,21 @@ uint32_t code_genstring(code_t *code, const char *str)
}
if (OPTS_OPTIMIZATION(OPTIM_OVERLAP_STRINGS)) {
hash = ((unsigned char*)str)[strlen(str)-1];
existing = code_util_str_htgeth(code->string_cache, str, hash);
hash = ((unsigned char*)str)[strlen(str)-1];
CODE_HASH_ENTER(existing) = code_util_str_htgeth(code->string_cache, str, hash);
} else {
hash = util_hthash(code->string_cache, str);
existing = util_htgeth(code->string_cache, str, hash);
hash = util_hthash(code->string_cache, str);
CODE_HASH_ENTER(existing) = util_htgeth(code->string_cache, str, hash);
}
if (existing)
return HASH_ENTRY_TO_QCINT(existing);
if (CODE_HASH_ENTER(existing))
return CODE_HASH_LEAVE(existing);
off = vec_size(code->chars);
CODE_HASH_LEAVE(existing) = vec_size(code->chars);
vec_upload(code->chars, str, strlen(str)+1);
util_htseth(code->string_cache, str, hash, QCINT_TO_HASH_ENTRY(off));
return off;
util_htseth(code->string_cache, str, hash, CODE_HASH_ENTER(existing));
return CODE_HASH_LEAVE(existing);
}
qcint code_alloc_field (code_t *code, size_t qcsize)
@ -114,30 +128,26 @@ qcint code_alloc_field (code_t *code, size_t qcsize)
return pos;
}
bool code_write(code_t *code, const char *filename, const char *lnofile) {
prog_header code_header;
FILE *fp = NULL;
size_t it = 2;
code_header.statements.offset = sizeof(prog_header);
code_header.statements.length = vec_size(code->statements);
code_header.defs.offset = code_header.statements.offset + (sizeof(prog_section_statement) * vec_size(code->statements));
code_header.defs.length = vec_size(code->defs);
code_header.fields.offset = code_header.defs.offset + (sizeof(prog_section_def) * vec_size(code->defs));
code_header.fields.length = vec_size(code->fields);
code_header.functions.offset = code_header.fields.offset + (sizeof(prog_section_field) * vec_size(code->fields));
code_header.functions.length = vec_size(code->functions);
code_header.globals.offset = code_header.functions.offset + (sizeof(prog_section_function) * vec_size(code->functions));
code_header.globals.length = vec_size(code->globals);
code_header.strings.offset = code_header.globals.offset + (sizeof(int32_t) * vec_size(code->globals));
code_header.strings.length = vec_size(code->chars);
code_header.version = 6;
static void code_create_header(code_t *code, prog_header *code_header) {
code_header->statements.offset = sizeof(prog_header);
code_header->statements.length = vec_size(code->statements);
code_header->defs.offset = code_header->statements.offset + (sizeof(prog_section_statement) * vec_size(code->statements));
code_header->defs.length = vec_size(code->defs);
code_header->fields.offset = code_header->defs.offset + (sizeof(prog_section_def) * vec_size(code->defs));
code_header->fields.length = vec_size(code->fields);
code_header->functions.offset = code_header->fields.offset + (sizeof(prog_section_field) * vec_size(code->fields));
code_header->functions.length = vec_size(code->functions);
code_header->globals.offset = code_header->functions.offset + (sizeof(prog_section_function) * vec_size(code->functions));
code_header->globals.length = vec_size(code->globals);
code_header->strings.offset = code_header->globals.offset + (sizeof(int32_t) * vec_size(code->globals));
code_header->strings.length = vec_size(code->chars);
code_header->version = 6;
if (OPTS_OPTION_BOOL(OPTION_FORCECRC))
code_header.crc16 = OPTS_OPTION_U16(OPTION_FORCED_CRC);
code_header->crc16 = OPTS_OPTION_U16(OPTION_FORCED_CRC);
else
code_header.crc16 = code->crc;
code_header.entfield = code->entfields;
code_header->crc16 = code->crc;
code_header->entfield = code->entfields;
if (OPTS_FLAG(DARKPLACES_STRING_TABLE_BUG)) {
util_debug("GEN", "Patching stringtable for -fdarkplaces-stringtablebug\n");
@ -149,20 +159,118 @@ bool code_write(code_t *code, const char *filename, const char *lnofile) {
}
/* ensure all data is in LE format */
util_endianswap(&code_header.version, 1, sizeof(code_header.version));
util_endianswap(&code_header.crc16, 1, sizeof(code_header.crc16));
util_endianswap(&code_header.statements, 2, sizeof(code_header.statements.offset));
util_endianswap(&code_header.defs, 2, sizeof(code_header.statements.offset));
util_endianswap(&code_header.fields, 2, sizeof(code_header.statements.offset));
util_endianswap(&code_header.functions, 2, sizeof(code_header.statements.offset));
util_endianswap(&code_header.strings, 2, sizeof(code_header.statements.offset));
util_endianswap(&code_header.globals, 2, sizeof(code_header.statements.offset));
util_endianswap(&code_header.entfield, 1, sizeof(code_header.entfield));
util_endianswap(&code_header->version, 1, sizeof(code_header->version));
util_endianswap(&code_header->crc16, 1, sizeof(code_header->crc16));
util_endianswap(&code_header->statements, 2, sizeof(code_header->statements.offset));
util_endianswap(&code_header->defs, 2, sizeof(code_header->statements.offset));
util_endianswap(&code_header->fields, 2, sizeof(code_header->statements.offset));
util_endianswap(&code_header->functions, 2, sizeof(code_header->statements.offset));
util_endianswap(&code_header->strings, 2, sizeof(code_header->statements.offset));
util_endianswap(&code_header->globals, 2, sizeof(code_header->statements.offset));
util_endianswap(&code_header->entfield, 1, sizeof(code_header->entfield));
/*
* These are not part of the header but we ensure LE format here to save on duplicated
* code.
*/
util_endianswap(code->statements, vec_size(code->statements), sizeof(prog_section_statement));
util_endianswap(code->defs, vec_size(code->defs), sizeof(prog_section_def));
util_endianswap(code->fields, vec_size(code->fields), sizeof(prog_section_field));
util_endianswap(code->functions, vec_size(code->functions), sizeof(prog_section_function));
util_endianswap(code->globals, vec_size(code->globals), sizeof(int32_t));
}
/*
* Same principle except this one allocates memory and writes the lno(optional) and the dat file
* directly out to allocated memory. Which is actually very useful for the future library support
* we're going to add.
*/
bool code_write_memory(code_t *code, uint8_t **datmem, size_t *sizedat, uint8_t **lnomem, size_t *sizelno) {
prog_header code_header;
uint32_t offset = 0;
if (!datmem)
return false;
code_create_header(code, &code_header);
#define WRITE_CHUNK(C,X,S) \
do { \
memcpy((void*)(&(*C)[offset]), (const void*)(X), (S)); \
offset += (S); \
} while (0)
/* Calculate size required to store entire file out to memory */
if (lnomem) {
uint32_t version = 1;
*sizelno += 4; /* LNOF */
*sizelno += sizeof(version);
*sizelno += sizeof(code_header.defs.length);
*sizelno += sizeof(code_header.globals.length);
*sizelno += sizeof(code_header.fields.length);
*sizelno += sizeof(code_header.statements.length);
*sizelno += sizeof(code->linenums[0]) * vec_size(code->linenums);
*lnomem = (uint8_t*)mem_a(*sizelno);
WRITE_CHUNK(lnomem, "LNOF", 4);
WRITE_CHUNK(lnomem, &version, sizeof(version));
WRITE_CHUNK(lnomem, &code_header.defs.length, sizeof(code_header.defs.length));
WRITE_CHUNK(lnomem, &code_header.globals.length, sizeof(code_header.globals.length));
WRITE_CHUNK(lnomem, &code_header.fields.length, sizeof(code_header.fields.length));
WRITE_CHUNK(lnomem, &code_header.statements.length, sizeof(code_header.statements.length));
/* something went terribly wrong */
if (offset != *sizelno) {
mem_d(*lnomem);
*sizelno = 0;
return false;
}
offset = 0;
}
/* Write out the dat */
*sizedat += sizeof(prog_header);
*sizedat += sizeof(prog_section_statement) * vec_size(code->statements);
*sizedat += sizeof(prog_section_def) * vec_size(code->defs);
*sizedat += sizeof(prog_section_field) * vec_size(code->fields);
*sizedat += sizeof(prog_section_function) * vec_size(code->functions);
*sizedat += sizeof(int32_t) * vec_size(code->globals);
*sizedat += 1 * vec_size(code->chars);
*datmem = (uint8_t*)mem_a(*sizedat);
WRITE_CHUNK(datmem, &code_header, sizeof(prog_header));
WRITE_CHUNK(datmem, code->statements, sizeof(prog_section_statement) * vec_size(code->statements));
WRITE_CHUNK(datmem, code->defs, sizeof(prog_section_def) * vec_size(code->defs));
WRITE_CHUNK(datmem, code->fields, sizeof(prog_section_field) * vec_size(code->fields));
WRITE_CHUNK(datmem, code->functions, sizeof(prog_section_function) * vec_size(code->functions));
WRITE_CHUNK(datmem, code->globals, sizeof(int32_t) * vec_size(code->globals));
WRITE_CHUNK(datmem, code->chars, 1 * vec_size(code->chars));
#undef WRITE_CHUNK
vec_free(code->statements);
vec_free(code->linenums);
vec_free(code->defs);
vec_free(code->fields);
vec_free(code->functions);
vec_free(code->globals);
vec_free(code->chars);
util_htdel(code->string_cache);
mem_d(code);
return true;
}
bool code_write(code_t *code, const char *filename, const char *lnofile) {
prog_header code_header;
FILE *fp = NULL;
size_t it = 2;
code_create_header(code, &code_header);
if (lnofile) {
uint32_t version = 1;
@ -175,13 +283,13 @@ bool code_write(code_t *code, const char *filename, const char *lnofile) {
util_endianswap(code->linenums, vec_size(code->linenums), sizeof(code->linenums[0]));
if (fs_file_write("LNOF", 4, 1, fp) != 1 ||
fs_file_write(&version, sizeof(version), 1, fp) != 1 ||
if (fs_file_write("LNOF", 4, 1, fp) != 1 ||
fs_file_write(&version, sizeof(version), 1, fp) != 1 ||
fs_file_write(&code_header.defs.length, sizeof(code_header.defs.length), 1, fp) != 1 ||
fs_file_write(&code_header.globals.length, sizeof(code_header.globals.length), 1, fp) != 1 ||
fs_file_write(&code_header.fields.length, sizeof(code_header.fields.length), 1, fp) != 1 ||
fs_file_write(&code_header.statements.length, sizeof(code_header.statements.length), 1, fp) != 1 ||
fs_file_write(code->linenums, sizeof(code->linenums[0]), vec_size(code->linenums), fp) != vec_size(code->linenums))
fs_file_write(code->linenums, sizeof(code->linenums[0]), vec_size(code->linenums), fp) != vec_size(code->linenums))
{
con_err("failed to write lno file\n");
}
@ -194,7 +302,7 @@ bool code_write(code_t *code, const char *filename, const char *lnofile) {
if (!fp)
return false;
if (1 != fs_file_write(&code_header, sizeof(prog_header) , 1 , fp) ||
if (1 != fs_file_write(&code_header, sizeof(prog_header) , 1 , fp) ||
vec_size(code->statements) != fs_file_write(code->statements, sizeof(prog_section_statement), vec_size(code->statements), fp) ||
vec_size(code->defs) != fs_file_write(code->defs, sizeof(prog_section_def) , vec_size(code->defs) , fp) ||
vec_size(code->fields) != fs_file_write(code->fields, sizeof(prog_section_field) , vec_size(code->fields) , fp) ||
@ -260,6 +368,11 @@ bool code_write(code_t *code, const char *filename, const char *lnofile) {
}
}
fs_file_close(fp);
return true;
}
void code_cleanup(code_t *code) {
vec_free(code->statements);
vec_free(code->linenums);
vec_free(code->defs);
@ -270,7 +383,5 @@ bool code_write(code_t *code, const char *filename, const char *lnofile) {
util_htdel(code->string_cache);
fs_file_close(fp);
mem_d(code);
return true;
}

View file

@ -21,7 +21,6 @@
* SOFTWARE.
*/
#include "gmqcc.h"
#include <stdio.h>
/*
* isatty/STDERR_FILENO/STDOUT_FILNO
@ -198,7 +197,7 @@ static con_t console;
* NOTE: This prevents colored output to piped stdout/err via isatty
* checks.
*/
static void con_enablecolor() {
static void con_enablecolor(void) {
if (console.handle_err == stderr || console.handle_err == stdout)
console.color_err = !!(isatty(STDERR_FILENO));
if (console.handle_out == stderr || console.handle_out == stdout)
@ -333,7 +332,7 @@ int con_out(const char *fmt, ...) {
* for reporting of file:line based on lexer context, These are used
* heavily in the parser/ir/ast.
*/
void con_vprintmsg_c(int level, const char *name, size_t line, const char *msgtype, const char *msg, va_list ap, const char *condname) {
static void con_vprintmsg_c(int level, const char *name, size_t line, size_t column, const char *msgtype, const char *msg, va_list ap, const char *condname) {
/* color selection table */
static int sel[] = {
CON_WHITE,
@ -347,9 +346,9 @@ void con_vprintmsg_c(int level, const char *name, size_t line, const char *msgty
int (*vprint)(const char *, va_list) = (err) ? &con_verr : &con_vout;
if (color)
print("\033[0;%dm%s:%d: \033[0;%dm%s: \033[0m", CON_CYAN, name, (int)line, sel[level], msgtype);
print("\033[0;%dm%s:%d:%d: \033[0;%dm%s: \033[0m", CON_CYAN, name, (int)line, (int)column, sel[level], msgtype);
else
print("%s:%d: %s: ", name, (int)line, msgtype);
print("%s:%d:%d: %s: ", name, (int)line, (int)column, msgtype);
vprint(msg, ap);
if (condname)
@ -358,19 +357,19 @@ void con_vprintmsg_c(int level, const char *name, size_t line, const char *msgty
print("\n");
}
void con_vprintmsg(int level, const char *name, size_t line, const char *msgtype, const char *msg, va_list ap) {
con_vprintmsg_c(level, name, line, msgtype, msg, ap, NULL);
void con_vprintmsg(int level, const char *name, size_t line, size_t column, const char *msgtype, const char *msg, va_list ap) {
con_vprintmsg_c(level, name, line, column, msgtype, msg, ap, NULL);
}
void con_printmsg(int level, const char *name, size_t line, const char *msgtype, const char *msg, ...) {
void con_printmsg(int level, const char *name, size_t line, size_t column, const char *msgtype, const char *msg, ...) {
va_list va;
va_start(va, msg);
con_vprintmsg(level, name, line, msgtype, msg, va);
con_vprintmsg(level, name, line, column, msgtype, msg, va);
va_end (va);
}
void con_cvprintmsg(void *ctx, int lvl, const char *msgtype, const char *msg, va_list ap) {
con_vprintmsg(lvl, ((lex_ctx*)ctx)->file, ((lex_ctx*)ctx)->line, msgtype, msg, ap);
con_vprintmsg(lvl, ((lex_ctx*)ctx)->file, ((lex_ctx*)ctx)->line, ((lex_ctx*)ctx)->column, msgtype, msg, ap);
}
void con_cprintmsg (void *ctx, int lvl, const char *msgtype, const char *msg, ...) {
@ -432,7 +431,7 @@ bool GMQCC_WARN vcompile_warning(lex_ctx ctx, int warntype, const char *fmt, va_
lvl = LVL_ERROR;
}
con_vprintmsg_c(lvl, ctx.file, ctx.line, msgtype, fmt, ap, warn_name);
con_vprintmsg_c(lvl, ctx.file, ctx.line, ctx.column, msgtype, fmt, ap, warn_name);
return OPTS_WERROR(warntype) && OPTS_FLAG(BAIL_ON_WERROR);
}

View file

@ -21,6 +21,7 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include <string.h>
#include "gmqcc.h"
/*

View file

@ -10,12 +10,14 @@ DEB := $(DEBDIR)-$(CARCH).deb
CONTROL := $(DEBDIR)/DEBIAN/control
ifneq (, $(findstring i686, $(CARCH)))
CFLAGS := -m32
CFLAGS += -m32
LDFLAGS += -m32
endif
base:
$(MAKE) -C $(BASEDIR) clean
$(MAKE) -C $(BASEDIR) DESTDIR=distro/deb/$(DEBDIR) PREFIX=$(PREFIX) install
CFLAGS="$(CFLAGS)" LDFLAGS="$(LDFLAGS)" \
$(MAKE) -C $(BASEDIR) DESTDIR=distro/deb/$(DEBDIR) PREFIX=$(PREFIX) install
@install -d -m755 $(DEBDIR)/DEBIAN
@echo "Package: gmqcc" > $(CONTROL)
@echo "Version: $(MAJOR).$(MINOR).$(PATCH)" >> $(CONTROL)

35
distro/gentoo/INSTALL Normal file
View file

@ -0,0 +1,35 @@
To use the ebuilds provided in this gentoo directory you first must
create a directory in your overlay tree.
If you don't already have your own directory for custom ebuilds, you can
create one. If you already have one, and that directory is set in your
/etc/make.conf for PORTDIR_OVERLAY, this step can be skiped. Otherwise
if you don't already, you can create one as such.
# mkdir -p /usr/local/portage
# vim /etc/make.conf
Set PORTDIR_OVERLAY=/usr/local/portage
Then save and exit
Once that is completed, or you skiped that step, you need to create a
directory in your overlay tree for gmqcc, this can be done as such:
(subsitute [[PORTDIR_OVERLAY]] with the one set in /etc/make.conf)
# mkdir -p [[PORTDIR_OVERLAY]]/gmqcc
After the directory is created you need to move the correct version ebuild
into that directory depending on which version of GMQCC you want. For
instance, if you want gmqcc 0.3.0, you move gmqcc-0.3.0.ebuild into that
directory.
# mv gmqcc-{version}.ebuild [[PORTDIR_OVERLAY]]/gmqcc/
After the file is moved into your newly created portage overlay tree, you'll
need to build a digest for it with ebuild. A digest is simply a Manifest and
digital signature for the source files used.
# ebuild gmqcc-0.3.0.ebuild digest
After the digest is built, you can emerge gmqcc as usual.
# emerge gmqcc

View file

@ -0,0 +1,20 @@
EAPI=5
DESCRIPTION="An Improved Quake C Compiler"
HOMEPAGE="http://graphitemaster.github.com/gmqcc/"
SRC_URI="https://github.com/graphitemaster/${PN}/archive/${PV}.tar.gz -> ${P}.tar.gz"
LICENSE="MIT"
SLOT="0"
IUSE=""
KEYWORDS="~amd64 ~x86"
src_prepare() {
sed -i -e "s:-Werror ::" Makefile || die
}
src_install() {
emake install PREFIX="${D}/usr"
dodoc README
}

View file

@ -0,0 +1,35 @@
BASEDIR := ../../../
PREFIX := /usr
HEADER := $(BASEDIR)/gmqcc.h
MAJOR := $(shell sed -n -e '/GMQCC_VERSION_MAJOR/{s/.* .* //;p;q;}' $(HEADER))
MINOR := $(shell sed -n -e '/GMQCC_VERSION_MINOR/{s/.* .* //;p;q;}' $(HEADER))
PATCH := $(shell sed -n -e '/GMQCC_VERSION_PATCH/{s/.* .* //;p;q;}' $(HEADER))
CARCH := $(shell uname -m)
PKGDIR := gmqcc-$(MAJOR).$(MINOR).$(PATCH)-$(CARCH)-git
PKG := $(PKGDIR).txz
PKGINFO := $(PKGDIR)/.PKGINFO
DESTDIR := distro/slackware/this/$(PKGDIR)
CFLAGS :=
ifneq (, $(findstring i686, $(CARCH)))
CFLAGS += -m32
LDFLAGS += -m32
endif
base:
$(MAKE) -C $(BASEDIR) clean
CFLAGS="$(CFLAGS)" LDFLAGS="$(LDFLAGS)" \
$(MAKE) -C $(BASEDIR) "DESTDIR=$(DESTDIR)" "PREFIX=$(PREFIX)" install
gzip -9 $(PKGDIR)/usr/share/man/man?/*.?
strip -s $(PKGDIR)/usr/bin/*
mkdir $(PKGDIR)/install
cp slack-desc $(PKGDIR)/install
@tar -cJvf $(PKG) -C $(PKGDIR)/ install/ usr/
@rm -rf $(PKGDIR)
clean:
$(MAKE) -C $(BASEDIR) clean
@rm -f *.txz
all: base

View file

@ -0,0 +1,12 @@
|-----handy-ruler------------------------------------------------------|
gmqcc: gmqcc (Quake C compiler)
gmqcc:
gmqcc: A modern written-from-scratch compiler for the QuakeC language with
gmqcc: support for many common features found in other QC compilers.
gmqcc: Additionally contains a standalone QCVM executor, and a tool to deal
gmqcc: with .pak archive files.
gmqcc:
gmqcc:
gmqcc: github page:
gmqcc: http://github.com/graphitemaster/gmqcc
gmqcc:

View file

@ -506,6 +506,18 @@ Example:
void printA() = #1; // the usual way
void printB() = #2-1; // with a constant expression
.Ed
.It Fl f Ns Cm return-assignments
Enabiling this option will allow assigning values or expressions to the
return keyword as if it were a local variable of the same type as the
function's signature's return type.
.Pp
Example:
.Bd -literal -offset indent
float bar() { return 1024; }
float fun() {
return = bar();
return; // returns value of bar
}
.El
.Sh OPTIMIZATIONS
.Bl -tag -width Ds

38
doc/gmqpak.1 Normal file
View file

@ -0,0 +1,38 @@
.\" gmqpak mdoc manpage
.Dd April 27, 2013
.Dt GMQPAK 2 PRM
.Os
.Sh NAME
.Nm gmqpak
.Nd A standalone Quake PAK utility
.Sh SYNOPSIS
.Nm gmqpak
.Op Cm options
.Op Cm files
.Sh DESCRIPTION
.Nm gmqpak
Is a standalone Quake PAK file utility supporting the extraction of files,
directories, or whole PAKs, as well as the opposite (creation of PAK files).
.Sh OPTIONS
.Bl -tag
.It Fl -file Ar file
Specify the PAK file to create or extract
.It Fl -e
Used to denote the extraction operation on a PAK file.
.It Fl -c
Used to denote the creation operation on a PAK file.
.El
.Sh EXAMPLES
Here's some examples of how to use the utility to manipulate PAK files.
.Bl -ohang
.It Li gmqpak -file id1.pak -e
.D1 extracts a PAK to ./
.It Li gmqpak -file new.pak -c file1 dir/file2
.D1 creates a PAK with the files specified
.It Li gmqpak -file new1.pak -c directory.
.D1 creates a PAK from files within the directory, including subdirectories and files.
.Sh AUTHOR
See <http://graphitemaster.github.com/gmqcc>.
.Sh BUGS
Please report bugs on <http://github.com/graphitemaster/gmqcc/issues>,
or see <http://graphitemaster.github.com/gmqcc> on how to contact us.

141
exec.c
View file

@ -26,6 +26,7 @@
#include <stdio.h>
#include <string.h>
#include <stdarg.h>
#include <stdlib.h>
#include "gmqcc.h"
@ -167,16 +168,15 @@ void prog_delete(qc_program *prog)
* VM code
*/
char* prog_getstring(qc_program *prog, qcint str)
{
const char* prog_getstring(qc_program *prog, qcint str) {
/* cast for return required for C++ */
if (str < 0 || str >= (qcint)vec_size(prog->strings))
return (char*)"<<<invalid string>>>";
return "<<<invalid string>>>";
return prog->strings + str;
}
prog_section_def* prog_entfield(qc_program *prog, qcint off)
{
prog_section_def* prog_entfield(qc_program *prog, qcint off) {
size_t i;
for (i = 0; i < vec_size(prog->fields); ++i) {
if (prog->fields[i].offset == off)
@ -195,8 +195,7 @@ prog_section_def* prog_getdef(qc_program *prog, qcint off)
return NULL;
}
qcany* prog_getedict(qc_program *prog, qcint e)
{
qcany* prog_getedict(qc_program *prog, qcint e) {
if (e >= (qcint)vec_size(prog->entitypool)) {
prog->vmerror++;
fprintf(stderr, "Accessing out of bounds edict %i\n", (int)e);
@ -205,8 +204,7 @@ qcany* prog_getedict(qc_program *prog, qcint e)
return (qcany*)(prog->entitydata + (prog->entityfields * e));
}
qcint prog_spawn_entity(qc_program *prog)
{
qcint prog_spawn_entity(qc_program *prog) {
char *data;
qcint e;
for (e = 0; e < (qcint)vec_size(prog->entitypool); ++e) {
@ -223,8 +221,7 @@ qcint prog_spawn_entity(qc_program *prog)
return e;
}
void prog_free_entity(qc_program *prog, qcint e)
{
void prog_free_entity(qc_program *prog, qcint e) {
if (!e) {
prog->vmerror++;
fprintf(stderr, "Trying to free world entity\n");
@ -243,8 +240,7 @@ void prog_free_entity(qc_program *prog, qcint e)
prog->entitypool[e] = false;
}
qcint prog_tempstring(qc_program *prog, const char *str)
{
qcint prog_tempstring(qc_program *prog, const char *str) {
size_t len = strlen(str);
size_t at = prog->tempstring_at;
@ -266,8 +262,7 @@ qcint prog_tempstring(qc_program *prog, const char *str)
return at;
}
static size_t print_escaped_string(const char *str, size_t maxlen)
{
static size_t print_escaped_string(const char *str, size_t maxlen) {
size_t len = 2;
putchar('"');
--maxlen; /* because we're lazy and have escape sequences */
@ -300,8 +295,7 @@ static size_t print_escaped_string(const char *str, size_t maxlen)
return len;
}
static void trace_print_global(qc_program *prog, unsigned int glob, int vtype)
{
static void trace_print_global(qc_program *prog, unsigned int glob, int vtype) {
static char spaces[28+1] = " ";
prog_section_def *def;
qcany *value;
@ -359,8 +353,7 @@ done:
}
}
static void prog_print_statement(qc_program *prog, prog_section_statement *st)
{
static void prog_print_statement(qc_program *prog, prog_section_statement *st) {
if (st->opcode >= (sizeof(asm_instr)/sizeof(asm_instr[0]))) {
printf("<illegal instruction %d>\n", st->opcode);
return;
@ -457,8 +450,7 @@ static void prog_print_statement(qc_program *prog, prog_section_statement *st)
}
}
static qcint prog_enterfunction(qc_program *prog, prog_section_function *func)
{
static qcint prog_enterfunction(qc_program *prog, prog_section_function *func) {
qc_exec_stack st;
size_t parampos;
int32_t p;
@ -507,8 +499,7 @@ static qcint prog_enterfunction(qc_program *prog, prog_section_function *func)
return func->entry;
}
static qcint prog_leavefunction(qc_program *prog)
{
static qcint prog_leavefunction(qc_program *prog) {
prog_section_function *prev = NULL;
size_t oldsp;
@ -540,8 +531,7 @@ static qcint prog_leavefunction(qc_program *prog)
return st.stmt - 1; /* offset the ++st */
}
bool prog_exec(qc_program *prog, prog_section_function *func, size_t flags, long maxjumps)
{
bool prog_exec(qc_program *prog, prog_section_function *func, size_t flags, long maxjumps) {
long jumpcount = 0;
size_t oldxflags = prog->xflags;
prog_section_statement *st;
@ -640,8 +630,7 @@ static qcvm_parameter *main_params = NULL;
#define GetArg(num) GetGlobal(OFS_PARM0 + 3*(num))
#define Return(any) *(GetGlobal(OFS_RETURN)) = (any)
static int qc_print(qc_program *prog)
{
static int qc_print(qc_program *prog) {
size_t i;
const char *laststr = NULL;
for (i = 0; i < (size_t)prog->argc; ++i) {
@ -657,16 +646,14 @@ static int qc_print(qc_program *prog)
return 0;
}
static int qc_error(qc_program *prog)
{
static int qc_error(qc_program *prog) {
fprintf(stderr, "*** VM raised an error:\n");
qc_print(prog);
prog->vmerror++;
return -1;
}
static int qc_ftos(qc_program *prog)
{
static int qc_ftos(qc_program *prog) {
char buffer[512];
qcany *num;
qcany str;
@ -678,8 +665,7 @@ static int qc_ftos(qc_program *prog)
return 0;
}
static int qc_stof(qc_program *prog)
{
static int qc_stof(qc_program *prog) {
qcany *str;
qcany num;
CheckArgs(1);
@ -689,8 +675,7 @@ static int qc_stof(qc_program *prog)
return 0;
}
static int qc_vtos(qc_program *prog)
{
static int qc_vtos(qc_program *prog) {
char buffer[512];
qcany *num;
qcany str;
@ -702,8 +687,7 @@ static int qc_vtos(qc_program *prog)
return 0;
}
static int qc_etos(qc_program *prog)
{
static int qc_etos(qc_program *prog) {
char buffer[512];
qcany *num;
qcany str;
@ -715,8 +699,7 @@ static int qc_etos(qc_program *prog)
return 0;
}
static int qc_spawn(qc_program *prog)
{
static int qc_spawn(qc_program *prog) {
qcany ent;
CheckArgs(0);
ent.edict = prog_spawn_entity(prog);
@ -724,8 +707,7 @@ static int qc_spawn(qc_program *prog)
return (ent.edict ? 0 : -1);
}
static int qc_kill(qc_program *prog)
{
static int qc_kill(qc_program *prog) {
qcany *ent;
CheckArgs(1);
ent = GetArg(0);
@ -733,8 +715,7 @@ static int qc_kill(qc_program *prog)
return 0;
}
static int qc_sqrt(qc_program *prog)
{
static int qc_sqrt(qc_program *prog) {
qcany *num, out;
CheckArgs(1);
num = GetArg(0);
@ -743,8 +724,7 @@ static int qc_sqrt(qc_program *prog)
return 0;
}
static int qc_vlen(qc_program *prog)
{
static int qc_vlen(qc_program *prog) {
qcany *vec, len;
CheckArgs(1);
vec = GetArg(0);
@ -755,8 +735,7 @@ static int qc_vlen(qc_program *prog)
return 0;
}
static int qc_normalize(qc_program *prog)
{
static int qc_normalize(qc_program *prog) {
double len;
qcany *vec;
qcany out;
@ -776,13 +755,14 @@ static int qc_normalize(qc_program *prog)
return 0;
}
static int qc_strcat(qc_program *prog)
{
static int qc_strcat(qc_program *prog) {
char *buffer;
size_t len1, len2;
char *cstr1, *cstr2;
qcany *str1, *str2;
qcany out;
const char *cstr1;
const char *cstr2;
CheckArgs(2);
str1 = GetArg(0);
@ -800,12 +780,13 @@ static int qc_strcat(qc_program *prog)
return 0;
}
static int qc_strcmp(qc_program *prog)
{
char *cstr1, *cstr2;
static int qc_strcmp(qc_program *prog) {
qcany *str1, *str2;
qcany out;
const char *cstr1;
const char *cstr2;
if (prog->argc != 2 && prog->argc != 3) {
fprintf(stderr, "ERROR: invalid number of arguments for strcmp/strncmp: %i, expected 2 or 3\n",
prog->argc);
@ -824,8 +805,7 @@ static int qc_strcmp(qc_program *prog)
return 0;
}
static int qc_floor(qc_program *prog)
{
static int qc_floor(qc_program *prog) {
qcany *num, out;
CheckArgs(1);
num = GetArg(0);
@ -855,7 +835,7 @@ static size_t qc_builtins_count = sizeof(qc_builtins) / sizeof(qc_builtins[0]);
static const char *arg0 = NULL;
static void version() {
static void version(void) {
printf("GMQCC-QCVM %d.%d.%d Built %s %s\n",
GMQCC_VERSION_MAJOR,
GMQCC_VERSION_MINOR,
@ -865,8 +845,7 @@ static void version() {
);
}
static void usage()
{
static void usage(void) {
printf("usage: %s [options] [parameters] file\n", arg0);
printf("options:\n");
printf(" -h, --help print this message\n"
@ -886,8 +865,7 @@ static void usage()
" -string <s> pass a string parameter to main() \n");
}
static void prog_main_setparams(qc_program *prog)
{
static void prog_main_setparams(qc_program *prog) {
size_t i;
qcany *arg;
@ -923,9 +901,35 @@ static void prog_main_setparams(qc_program *prog)
}
}
void escapestring(char* dest, const char* src) {
char c;
while ((c = *(src++))) {
switch(c) {
case '\t':
*(dest++) = '\\', *(dest++) = 't';
break;
case '\n':
*(dest++) = '\\', *(dest++) = 'n';
break;
case '\r':
*(dest++) = '\\', *(dest++) = 'r';
break;
case '\\':
*(dest++) = '\\', *(dest++) = '\\';
break;
case '\"':
*(dest++) = '\\', *(dest++) = '\"';
break;
default:
*(dest++) = c;
}
}
*dest = '\0';
}
void prog_disasm_function(qc_program *prog, size_t id);
int main(int argc, char **argv)
{
int main(int argc, char **argv) {
size_t i;
qcint fnmain = -1;
qc_program *prog;
@ -1137,6 +1141,9 @@ int main(int argc, char **argv)
return 0;
}
if (opts_printdefs) {
char *escape = NULL;
const char *getstring = NULL;
for (i = 0; i < vec_size(prog->defs); ++i) {
printf("Global: %8s %-16s at %u%s",
type_name[prog->defs[i].type & DEF_TYPEMASK],
@ -1158,7 +1165,12 @@ int main(int argc, char **argv)
printf(" [init: %u]", (unsigned)( ((qcany*)(prog->globals + prog->defs[i].offset))->_int ));
break;
case TYPE_STRING:
printf(" [init: `%s`]", prog_getstring(prog, ((qcany*)(prog->globals + prog->defs[i].offset))->string ));
getstring = prog_getstring(prog, ((qcany*)(prog->globals + prog->defs[i].offset))->string);
escape = (char*)mem_a(strlen(getstring) * 2 + 1); /* will be enough */
escapestring(escape, getstring);
printf(" [init: `%s`]", escape);
mem_d(escape); /* free */
break;
default:
break;
@ -1232,8 +1244,7 @@ int main(int argc, char **argv)
return 0;
}
void prog_disasm_function(qc_program *prog, size_t id)
{
void prog_disasm_function(qc_program *prog, size_t id) {
prog_section_function *fdef = prog->functions + id;
prog_section_statement *st;

31
ftepp.c
View file

@ -22,7 +22,10 @@
* SOFTWARE.
*/
#include <time.h>
#include <string.h>
#include <stdlib.h>
#include <sys/stat.h>
#include "gmqcc.h"
#include "lexer.h"
@ -81,7 +84,7 @@ static uint32_t ftepp_predef_countval = 0;
static uint32_t ftepp_predef_randval = 0;
/* __DATE__ */
char *ftepp_predef_date(lex_file *context) {
static char *ftepp_predef_date(lex_file *context) {
struct tm *itime = NULL;
time_t rtime;
char *value = (char*)mem_a(82);
@ -104,7 +107,7 @@ char *ftepp_predef_date(lex_file *context) {
}
/* __TIME__ */
char *ftepp_predef_time(lex_file *context) {
static char *ftepp_predef_time(lex_file *context) {
struct tm *itime = NULL;
time_t rtime;
char *value = (char*)mem_a(82);
@ -127,13 +130,13 @@ char *ftepp_predef_time(lex_file *context) {
}
/* __LINE__ */
char *ftepp_predef_line(lex_file *context) {
static char *ftepp_predef_line(lex_file *context) {
char *value;
util_asprintf(&value, "%d", (int)context->line);
return value;
}
/* __FILE__ */
char *ftepp_predef_file(lex_file *context) {
static char *ftepp_predef_file(lex_file *context) {
size_t length = strlen(context->name) + 3; /* two quotes and a terminator */
char *value = (char*)mem_a(length);
util_snprintf(value, length, "\"%s\"", context->name);
@ -141,7 +144,7 @@ char *ftepp_predef_file(lex_file *context) {
return value;
}
/* __COUNTER_LAST__ */
char *ftepp_predef_counterlast(lex_file *context) {
static char *ftepp_predef_counterlast(lex_file *context) {
char *value;
util_asprintf(&value, "%u", ftepp_predef_countval);
@ -149,7 +152,7 @@ char *ftepp_predef_counterlast(lex_file *context) {
return value;
}
/* __COUNTER__ */
char *ftepp_predef_counter(lex_file *context) {
static char *ftepp_predef_counter(lex_file *context) {
char *value;
ftepp_predef_countval ++;
util_asprintf(&value, "%u", ftepp_predef_countval);
@ -158,7 +161,7 @@ char *ftepp_predef_counter(lex_file *context) {
return value;
}
/* __RANDOM__ */
char *ftepp_predef_random(lex_file *context) {
static char *ftepp_predef_random(lex_file *context) {
char *value;
ftepp_predef_randval = (util_rand() % 0xFF) + 1;
util_asprintf(&value, "%u", ftepp_predef_randval);
@ -167,7 +170,7 @@ char *ftepp_predef_random(lex_file *context) {
return value;
}
/* __RANDOM_LAST__ */
char *ftepp_predef_randomlast(lex_file *context) {
static char *ftepp_predef_randomlast(lex_file *context) {
char *value;
util_asprintf(&value, "%u", ftepp_predef_randval);
@ -175,7 +178,7 @@ char *ftepp_predef_randomlast(lex_file *context) {
return value;
}
/* __TIMESTAMP__ */
char *ftepp_predef_timestamp(lex_file *context) {
static char *ftepp_predef_timestamp(lex_file *context) {
struct stat finfo;
char *find;
char *value;
@ -313,7 +316,7 @@ static void ppmacro_delete(ppmacro *self)
mem_d(self);
}
static ftepp_t* ftepp_new()
static ftepp_t* ftepp_new(void)
{
ftepp_t *ftepp;
@ -374,7 +377,7 @@ static GMQCC_INLINE ppmacro* ftepp_macro_find(ftepp_t *ftepp, const char *name)
static GMQCC_INLINE void ftepp_macro_delete(ftepp_t *ftepp, const char *name)
{
util_htrm(ftepp->macros, name, NULL);
util_htrm(ftepp->macros, name, (void (*)(void*))&ppmacro_delete);
}
static GMQCC_INLINE int ftepp_next(ftepp_t *ftepp)
@ -564,10 +567,6 @@ static bool ftepp_define(ftepp_t *ftepp)
return false;
}
#if 0
if (ftepp->output_on)
vec_push(ftepp->macros, macro);
#endif
if (ftepp->output_on)
util_htset(ftepp->macros, macro->name, (void*)macro);
else {
@ -1885,7 +1884,7 @@ ftepp_t *ftepp_create()
void ftepp_add_define(ftepp_t *ftepp, const char *source, const char *name)
{
ppmacro *macro;
lex_ctx ctx = { "__builtin__", 0 };
lex_ctx ctx = { "__builtin__", 0, 0 };
ctx.file = source;
macro = ppmacro_new(ctx, name);
/*vec_push(ftepp->macros, macro);*/

86
gmqcc.h
View file

@ -23,12 +23,8 @@
*/
#ifndef GMQCC_HDR
#define GMQCC_HDR
#include <limits.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
#include <stdio.h>
#include <ctype.h>
#include <stdio.h> /* TODO: remove this */
/*
* Disable some over protective warnings in visual studio because fixing them is a waste
@ -288,20 +284,30 @@ GMQCC_IND_STRING(GMQCC_VERSION_PATCH) \
# include <dirent.h>
#endif /*! _WIN32 && !defined(__MINGW32__) */
/*===================================================================*/
/*=========================== stat.c ================================*/
/*===================================================================*/
void stat_info (void);
char *stat_mem_strdup (const char *, size_t, const char *, bool);
void *stat_mem_reallocate(void *, size_t, size_t, const char *);
void stat_mem_deallocate(void *);
void *stat_mem_allocate (size_t, size_t, const char *);
#define mem_a(SIZE) stat_mem_allocate ((SIZE), __LINE__, __FILE__)
#define mem_d(PTRN) stat_mem_deallocate((void*)(PTRN))
#define mem_r(PTRN, SIZE) stat_mem_reallocate((void*)(PTRN), (SIZE), __LINE__, __FILE__)
#define mem_af(SIZE, FILE, LINE) stat_mem_allocate ((SIZE), (LINE), (FILE))
/* TODO: rename to mem variations */
#define util_strdup(SRC) stat_mem_strdup((char*)(SRC), __LINE__, __FILE__, false)
#define util_strdupe(SRC) stat_mem_strdup((char*)(SRC), __LINE__, __FILE__, true)
/*===================================================================*/
/*=========================== util.c ================================*/
/*===================================================================*/
void *util_memory_a (size_t, /*****/ unsigned int, const char *);
void *util_memory_r (void *, size_t, unsigned int, const char *);
void util_memory_d (void *);
void util_meminfo ();
bool util_filexists (const char *);
bool util_strupper (const char *);
bool util_strdigit (const char *);
char *_util_Estrdup (const char *, const char *, size_t);
char *_util_Estrdup_empty(const char *, const char *, size_t);
void util_debug (const char *, const char *, ...);
void util_endianswap (void *, size_t, unsigned int);
@ -311,7 +317,7 @@ size_t util_strtononcmd (const char *, char *, size_t);
uint16_t util_crc16(uint16_t crc, const char *data, size_t len);
void util_seed(uint32_t);
uint32_t util_rand();
uint32_t util_rand(void);
/*
* String functions (formatting, copying, concatenating, errors). These are wrapped
@ -326,22 +332,6 @@ char *util_strcat (char *dest, const char *src);
char *util_strncpy (char *dest, const char *src, size_t num);
const char *util_strerror (int num);
#ifdef NOTRACK
# define mem_a(x) malloc (x)
# define mem_d(x) free ((void*)x)
# define mem_r(x, n) realloc((void*)x, n)
# define mem_af(x,f,l) malloc (x)
#else
# define mem_a(x) util_memory_a((x), __LINE__, __FILE__)
# define mem_d(x) util_memory_d((void*)(x))
# define mem_r(x, n) util_memory_r((void*)(x), (n), __LINE__, __FILE__)
# define mem_af(x,f,l) util_memory_a((x), __LINE__, __FILE__)
#endif /*! NOTRACK */
#define util_strdup(X) _util_Estrdup((X), __FILE__, __LINE__)
#define util_strdupe(X) _util_Estrdup_empty((X), __FILE__, __LINE__)
/*
* A flexible vector implementation: all vector pointers contain some
* data about themselfs exactly - sizeof(vector_t) behind the pointer
@ -383,7 +373,7 @@ typedef struct trie_s {
struct trie_s *entries;
} correct_trie_t;
correct_trie_t* correct_trie_new();
correct_trie_t* correct_trie_new(void);
typedef struct hash_table_t {
size_t size;
@ -734,7 +724,9 @@ typedef struct {
* code_pop_statement -- keeps statements and linenumbers together
*/
bool code_write (code_t *, const char *filename, const char *lno);
GMQCC_WARN
code_t *code_init (void);
void code_cleanup (code_t *);
uint32_t code_genstring (code_t *, const char *string);
qcint code_alloc_field (code_t *, size_t qcsize);
void code_push_statement(code_t *, prog_section_statement *stmt, int linenum);
@ -747,6 +739,7 @@ void code_pop_statement (code_t *);
typedef struct {
const char *file;
size_t line;
size_t column;
} lex_ctx;
/*===================================================================*/
@ -770,17 +763,17 @@ enum {
LVL_ERROR
};
FILE *con_default_out();
FILE *con_default_err();
FILE *con_default_out(void);
FILE *con_default_err(void);
void con_vprintmsg (int level, const char *name, size_t line, const char *msgtype, const char *msg, va_list ap);
void con_printmsg (int level, const char *name, size_t line, const char *msgtype, const char *msg, ...);
void con_vprintmsg (int level, const char *name, size_t line, size_t column, const char *msgtype, const char *msg, va_list ap);
void con_printmsg (int level, const char *name, size_t line, size_t column, const char *msgtype, const char *msg, ...);
void con_cvprintmsg(void *ctx, int lvl, const char *msgtype, const char *msg, va_list ap);
void con_cprintmsg (void *ctx, int lvl, const char *msgtype, const char *msg, ...);
void con_close ();
void con_init ();
void con_reset ();
void con_close (void);
void con_init (void);
void con_reset (void);
void con_color (int);
int con_change(const char *, const char *);
int con_verr (const char *, va_list);
@ -797,7 +790,7 @@ void /********/ compile_error (lex_ctx ctx, /*LVL_ERROR*/ const char *msg, ...
void /********/ vcompile_error (lex_ctx ctx, /*LVL_ERROR*/ const char *msg, va_list ap);
bool GMQCC_WARN compile_warning (lex_ctx ctx, int warntype, const char *fmt, ...);
bool GMQCC_WARN vcompile_warning(lex_ctx ctx, int warntype, const char *fmt, va_list ap);
void compile_show_werrors();
void compile_show_werrors(void);
/*===================================================================*/
/*========================= assembler.c =============================*/
@ -991,7 +984,7 @@ void prog_delete(qc_program *prog);
bool prog_exec(qc_program *prog, prog_section_function *func, size_t flags, long maxjumps);
char* prog_getstring (qc_program *prog, qcint str);
const char* prog_getstring (qc_program *prog, qcint str);
prog_section_def* prog_entfield (qc_program *prog, qcint off);
prog_section_def* prog_getdef (qc_program *prog, qcint off);
qcany* prog_getedict (qc_program *prog, qcint e);
@ -1002,8 +995,7 @@ qcint prog_tempstring(qc_program *prog, const char *_str);
/*===================== parser.c commandline ========================*/
/*===================================================================*/
struct parser_s;
struct parser_s *parser_create ();
struct parser_s *parser_create (void);
bool parser_compile_file (struct parser_s *parser, const char *);
bool parser_compile_string(struct parser_s *parser, const char *, const char *, size_t);
bool parser_finish (struct parser_s *parser, const char *);
@ -1013,7 +1005,7 @@ void parser_cleanup (struct parser_s *parser);
/*====================== ftepp.c commandline ========================*/
/*===================================================================*/
struct ftepp_s;
struct ftepp_s *ftepp_create ();
struct ftepp_s *ftepp_create (void);
bool ftepp_preprocess_file (struct ftepp_s *ftepp, const char *filename);
bool ftepp_preprocess_string(struct ftepp_s *ftepp, const char *name, const char *str);
void ftepp_finish (struct ftepp_s *ftepp);
@ -1073,10 +1065,10 @@ void opts_setoptimlevel(unsigned int);
void opts_ini_init (const char *);
/* Saner flag handling */
void opts_backup_non_Wall();
void opts_restore_non_Wall();
void opts_backup_non_Werror_all();
void opts_restore_non_Werror_all();
void opts_backup_non_Wall(void);
void opts_restore_non_Wall(void);
void opts_backup_non_Werror_all(void);
void opts_restore_non_Werror_all(void);
enum {
# define GMQCC_TYPE_FLAGS
@ -1161,7 +1153,7 @@ typedef struct {
extern opts_cmd_t opts;
#define OPTS_GENERIC(f,i) (!! (((f)[(i)/32]) & (1<< ((i)%32))))
#define OPTS_GENERIC(f,i) (!! (((f)[(i)/32]) & (1<< (unsigned)((i)%32))))
#define OPTS_FLAG(i) OPTS_GENERIC(opts.flags, (i))
#define OPTS_WARN(i) OPTS_GENERIC(opts.warn, (i))
#define OPTS_WERROR(i) OPTS_GENERIC(opts.werror, (i))

View file

@ -36,7 +36,7 @@ typedef struct {
const char *alias;
} intrin_t;
ht intrin_intrinsics() {
static ht intrin_intrinsics(void) {
static ht intrinsics = NULL;
if (!intrinsics)
intrinsics = util_htnew(PARSER_HT_SIZE);
@ -69,12 +69,10 @@ ht intrin_intrinsics() {
vec_push(parser->globals, (ast_expression*)(VALUE)); \
} while (0)
ast_expression *intrin_func (parser_t *parser, const char *name);
#define QC_M_E 2.71828182845905
ast_expression *intrin_pow(parser_t *parser) {
static ast_expression *intrin_func(parser_t *parser, const char *name);
static ast_expression *intrin_pow (parser_t *parser) {
/*
* float pow(float x, float y) {
* float local = 1.0f;
@ -221,7 +219,7 @@ ast_expression *intrin_pow(parser_t *parser) {
return (ast_expression*)value;
}
ast_expression *intrin_mod(parser_t *parser) {
static ast_expression *intrin_mod(parser_t *parser) {
/*
* float mod(float x, float y) {
* return x - y * floor(x / y);
@ -276,7 +274,7 @@ ast_expression *intrin_mod(parser_t *parser) {
return (ast_expression*)value;
}
ast_expression *intrin_exp(parser_t *parser) {
static ast_expression *intrin_exp(parser_t *parser) {
/*
* float exp(float x) {
* return pow(QC_M_E, x);
@ -314,7 +312,7 @@ ast_expression *intrin_exp(parser_t *parser) {
return (ast_expression*)value;
}
ast_expression *intrin_isnan(parser_t *parser) {
static ast_expression *intrin_isnan(parser_t *parser) {
/*
* float isnan(float x) {
* float local;
@ -383,7 +381,7 @@ void intrin_intrinsics_destroy(parser_t *parser) {
}
ast_expression *intrin_func(parser_t *parser, const char *name) {
static ast_expression *intrin_func(parser_t *parser, const char *name) {
static bool init = false;
size_t i = 0;
void *find;

90
ir.c
View file

@ -1,6 +1,7 @@
/*
* Copyright (C) 2012, 2013
* Wolfgang Bumiller
* Dale Weiler
*
* 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
@ -22,6 +23,7 @@
*/
#include <stdlib.h>
#include <string.h>
#include "gmqcc.h"
#include "ir.h"
@ -211,9 +213,31 @@ const uint16_t type_not_instr[TYPE_COUNT] = {
};
/* protos */
static ir_value* ir_gen_extparam_proto(ir_builder *ir);
static void ir_gen_extparam (code_t *, ir_builder *ir);
static ir_value* ir_value_var(const char *name, int st, int vtype);
static bool ir_value_set_name(ir_value*, const char *name);
static void ir_value_dump(ir_value*, int (*oprintf)(const char*,...));
static ir_value* ir_gen_extparam_proto(ir_builder *ir);
static void ir_gen_extparam (code_t *, ir_builder *ir);
static bool ir_builder_set_name(ir_builder *self, const char *name);
static ir_function* ir_function_new(struct ir_builder_s *owner, int returntype);
static bool ir_function_set_name(ir_function*, const char *name);
static void ir_function_delete(ir_function*);
static void ir_function_dump(ir_function*, char *ind, int (*oprintf)(const char*,...));
static ir_value* ir_block_create_general_instr(ir_block *self, lex_ctx, const char *label,
int op, ir_value *a, ir_value *b, int outype);
static void ir_block_delete(ir_block*);
static ir_block* ir_block_new(struct ir_function_s *owner, const char *label);
static bool GMQCC_WARN ir_block_create_store(ir_block*, lex_ctx, ir_value *target, ir_value *what);
static bool ir_block_set_label(ir_block*, const char *label);
static void ir_block_dump(ir_block*, char *ind, int (*oprintf)(const char*,...));
static bool ir_instr_op(ir_instr*, int op, ir_value *value, bool writing);
static void ir_instr_delete(ir_instr*);
static void ir_instr_dump(ir_instr* in, char *ind, int (*oprintf)(const char*,...));
/* error functions */
static void irerror(lex_ctx ctx, const char *msg, ...)
@ -238,7 +262,7 @@ static bool irwarning(lex_ctx ctx, int warntype, const char *fmt, ...)
* Vector utility functions
*/
bool GMQCC_WARN vec_ir_value_find(ir_value **vec, const ir_value *what, size_t *idx)
static bool GMQCC_WARN vec_ir_value_find(ir_value **vec, const ir_value *what, size_t *idx)
{
size_t i;
size_t len = vec_size(vec);
@ -251,7 +275,7 @@ bool GMQCC_WARN vec_ir_value_find(ir_value **vec, const ir_value *what, size_t *
return false;
}
bool GMQCC_WARN vec_ir_block_find(ir_block **vec, ir_block *what, size_t *idx)
static bool GMQCC_WARN vec_ir_block_find(ir_block **vec, ir_block *what, size_t *idx)
{
size_t i;
size_t len = vec_size(vec);
@ -264,7 +288,7 @@ bool GMQCC_WARN vec_ir_block_find(ir_block **vec, ir_block *what, size_t *idx)
return false;
}
bool GMQCC_WARN vec_ir_instr_find(ir_instr **vec, ir_instr *what, size_t *idx)
static bool GMQCC_WARN vec_ir_instr_find(ir_instr **vec, ir_instr *what, size_t *idx)
{
size_t i;
size_t len = vec_size(vec);
@ -340,6 +364,7 @@ void ir_builder_delete(ir_builder* self)
ir_value_delete(self->extparams[i]);
}
vec_free(self->extparams);
vec_free(self->extparam_protos);
for (i = 0; i != vec_size(self->globals); ++i) {
ir_value_delete(self->globals[i]);
}
@ -362,7 +387,7 @@ bool ir_builder_set_name(ir_builder *self, const char *name)
return !!self->name;
}
ir_function* ir_builder_get_function(ir_builder *self, const char *name)
static ir_function* ir_builder_get_function(ir_builder *self, const char *name)
{
return (ir_function*)util_htget(self->htfunctions, name);
}
@ -397,7 +422,7 @@ ir_function* ir_builder_create_function(ir_builder *self, const char *name, int
return fn;
}
ir_value* ir_builder_get_global(ir_builder *self, const char *name)
static ir_value* ir_builder_get_global(ir_builder *self, const char *name)
{
return (ir_value*)util_htget(self->htglobals, name);
}
@ -427,7 +452,7 @@ ir_value* ir_builder_get_va_count(ir_builder *self)
return (self->reserved_va_count = ir_builder_create_global(self, "reserved:va_count", TYPE_FLOAT));
}
ir_value* ir_builder_get_field(ir_builder *self, const char *name)
static ir_value* ir_builder_get_field(ir_builder *self, const char *name)
{
return (ir_value*)util_htget(self->htfields, name);
}
@ -451,10 +476,10 @@ ir_value* ir_builder_create_field(ir_builder *self, const char *name, int vtype)
*IR Function
*/
bool ir_function_naive_phi(ir_function*);
void ir_function_enumerate(ir_function*);
bool ir_function_calculate_liferanges(ir_function*);
bool ir_function_allocate_locals(ir_function*);
static bool ir_function_naive_phi(ir_function*);
static void ir_function_enumerate(ir_function*);
static bool ir_function_calculate_liferanges(ir_function*);
static bool ir_function_allocate_locals(ir_function*);
ir_function* ir_function_new(ir_builder* owner, int outtype)
{
@ -551,7 +576,7 @@ void ir_function_delete(ir_function *self)
mem_d(self);
}
void ir_function_collect_value(ir_function *self, ir_value *v)
static void ir_function_collect_value(ir_function *self, ir_value *v)
{
vec_push(self->values, v);
}
@ -574,7 +599,7 @@ static bool instr_is_operation(uint16_t op)
(op >= INSTR_CALL0 && op <= INSTR_CALL8) );
}
bool ir_function_pass_peephole(ir_function *self)
static bool ir_function_pass_peephole(ir_function *self)
{
size_t b;
@ -690,7 +715,7 @@ bool ir_function_pass_peephole(ir_function *self)
return true;
}
bool ir_function_pass_tailrecursion(ir_function *self)
static bool ir_function_pass_tailrecursion(ir_function *self)
{
size_t b, p;
@ -926,7 +951,7 @@ bool ir_block_set_label(ir_block *self, const char *name)
*IR Instructions
*/
ir_instr* ir_instr_new(lex_ctx ctx, ir_block* owner, int op)
static ir_instr* ir_instr_new(lex_ctx ctx, ir_block* owner, int op)
{
ir_instr *self;
self = (ir_instr*)mem_a(sizeof(*self));
@ -958,7 +983,7 @@ static void ir_instr_delete_quick(ir_instr *self)
mem_d(self);
}
void ir_instr_delete(ir_instr *self)
static void ir_instr_delete(ir_instr *self)
{
size_t i;
/* The following calls can only delete from
@ -989,7 +1014,7 @@ void ir_instr_delete(ir_instr *self)
mem_d(self);
}
bool ir_instr_op(ir_instr *self, int op, ir_value *v, bool writing)
static bool ir_instr_op(ir_instr *self, int op, ir_value *v, bool writing)
{
if (self->_ops[op]) {
size_t idx;
@ -1012,7 +1037,7 @@ bool ir_instr_op(ir_instr *self, int op, ir_value *v, bool writing)
*IR Value
*/
void ir_value_code_setaddr(ir_value *self, int32_t gaddr)
static void ir_value_code_setaddr(ir_value *self, int32_t gaddr)
{
self->code.globaladdr = gaddr;
if (self->members[0]) self->members[0]->code.globaladdr = gaddr;
@ -1020,7 +1045,7 @@ void ir_value_code_setaddr(ir_value *self, int32_t gaddr)
if (self->members[2]) self->members[2]->code.globaladdr = gaddr;
}
int32_t ir_value_code_addr(const ir_value *self)
static int32_t ir_value_code_addr(const ir_value *self)
{
if (self->store == store_return)
return OFS_RETURN + self->code.addroffset;
@ -1133,7 +1158,7 @@ static GMQCC_INLINE size_t ir_value_sizeof(const ir_value *self)
return type_sizeof_[self->vtype];
}
ir_value* ir_value_out(ir_function *owner, const char *name, int storetype, int vtype)
static ir_value* ir_value_out(ir_function *owner, const char *name, int storetype, int vtype)
{
ir_value *v = ir_value_var(name, storetype, vtype);
if (!v)
@ -1240,7 +1265,7 @@ bool ir_value_lives(ir_value *self, size_t at)
return false;
}
bool ir_value_life_insert(ir_value *self, size_t idx, ir_life_entry_t e)
static bool ir_value_life_insert(ir_value *self, size_t idx, ir_life_entry_t e)
{
size_t k;
vec_push(self->life, e);
@ -1250,7 +1275,7 @@ bool ir_value_life_insert(ir_value *self, size_t idx, ir_life_entry_t e)
return true;
}
bool ir_value_life_merge(ir_value *self, size_t s)
static bool ir_value_life_merge(ir_value *self, size_t s)
{
size_t i;
const size_t vs = vec_size(self->life);
@ -1313,7 +1338,7 @@ bool ir_value_life_merge(ir_value *self, size_t s)
return ir_value_life_insert(self, i, new_entry);
}
bool ir_value_life_merge_into(ir_value *self, const ir_value *other)
static bool ir_value_life_merge_into(ir_value *self, const ir_value *other)
{
size_t i, myi;
@ -1387,7 +1412,7 @@ bool ir_value_life_merge_into(ir_value *self, const ir_value *other)
return true;
}
bool ir_values_overlap(const ir_value *a, const ir_value *b)
static bool ir_values_overlap(const ir_value *a, const ir_value *b)
{
/* For any life entry in A see if it overlaps with
* any life entry in B.
@ -1483,7 +1508,7 @@ bool ir_block_create_store_op(ir_block *self, lex_ctx ctx, int op, ir_value *tar
return true;
}
bool ir_block_create_store(ir_block *self, lex_ctx ctx, ir_value *target, ir_value *what)
static bool ir_block_create_store(ir_block *self, lex_ctx ctx, ir_value *target, ir_value *what)
{
int op = 0;
int vtype;
@ -1826,7 +1851,7 @@ ir_value* ir_block_create_unary(ir_block *self, lex_ctx ctx,
return ir_block_create_general_instr(self, ctx, label, opcode, operand, NULL, ot);
}
ir_value* ir_block_create_general_instr(ir_block *self, lex_ctx ctx, const char *label,
static ir_value* ir_block_create_general_instr(ir_block *self, lex_ctx ctx, const char *label,
int op, ir_value *a, ir_value *b, int outype)
{
ir_instr *instr;
@ -3418,7 +3443,8 @@ static bool ir_builder_gen_global(code_t *code, ir_builder *self, ir_value *glob
{
ir_value_code_setaddr(global, vec_size(code->globals));
if (global->hasvalue) {
vec_push(code->globals, code_genstring(code, global->constval.vstring));
uint32_t load = code_genstring(code, global->constval.vstring);
vec_push(code->globals, load);
} else {
vec_push(code->globals, 0);
}
@ -3568,8 +3594,6 @@ bool ir_builder_generate(code_t *code, ir_builder *self, const char *filename)
size_t i;
char *lnofile = NULL;
code_init();
for (i = 0; i < vec_size(self->fields); ++i)
{
ir_builder_prepare_field(code, self->fields[i]);
@ -3686,7 +3710,7 @@ bool ir_builder_generate(code_t *code, ir_builder *self, const char *filename)
# define strncat(dst, src, sz) strncat_s(dst, sz, src, _TRUNCATE)
#endif
const char *qc_opname(int op)
static const char *qc_opname(int op)
{
if (op < 0) return "<INVALID>";
if (op < (int)( sizeof(asm_instr) / sizeof(asm_instr[0]) ))
@ -3838,7 +3862,7 @@ void ir_block_dump(ir_block* b, char *ind,
ind[strlen(ind)-1] = 0;
}
void dump_phi(ir_instr *in, int (*oprintf)(const char*, ...))
static void dump_phi(ir_instr *in, int (*oprintf)(const char*, ...))
{
size_t i;
oprintf("%s <- phi ", in->_ops[0]->name);
@ -3908,7 +3932,7 @@ void ir_instr_dump(ir_instr *in, char *ind,
ind[strlen(ind)-1] = 0;
}
void ir_value_dump_string(const char *str, int (*oprintf)(const char*, ...))
static void ir_value_dump_string(const char *str, int (*oprintf)(const char*, ...))
{
oprintf("\"");
for (; *str; ++str) {

112
ir.h
View file

@ -23,7 +23,6 @@
#ifndef GMQCC_IR_HDR
#define GMQCC_IR_HDR
#include "gmqcc.h"
/* ir_value */
typedef struct
{
@ -86,42 +85,19 @@ typedef struct ir_value_s {
ir_life_entry_t *life;
} ir_value;
int32_t ir_value_code_addr(const ir_value*);
/* ir_value can be a variable, or created by an operation */
ir_value* ir_value_var(const char *name, int st, int vtype);
/* if a result of an operation: the function should store
* it to remember to delete it / garbage collect it
*/
ir_value* ir_value_out(struct ir_function_s *owner, const char *name, int st, int vtype);
void ir_value_delete(ir_value*);
bool ir_value_set_name(ir_value*, const char *name);
ir_value* ir_value_vector_member(ir_value*, unsigned int member);
bool GMQCC_WARN vec_ir_value_find(ir_value **vec, const ir_value *what, size_t *idx);
void ir_value_delete(ir_value*);
ir_value* ir_value_vector_member(ir_value*, unsigned int member);
bool GMQCC_WARN ir_value_set_float(ir_value*, float f);
bool GMQCC_WARN ir_value_set_func(ir_value*, int f);
#if 0
bool GMQCC_WARN ir_value_set_int(ir_value*, int i);
#endif
bool GMQCC_WARN ir_value_set_string(ir_value*, const char *s);
bool GMQCC_WARN ir_value_set_vector(ir_value*, vector v);
bool GMQCC_WARN ir_value_set_field(ir_value*, ir_value *fld);
/*bool ir_value_set_pointer_v(ir_value*, ir_value* p); */
/*bool ir_value_set_pointer_i(ir_value*, int i); */
/* merge an instruction into the life-range */
/* returns false if the lifepoint was already known */
bool ir_value_life_merge(ir_value*, size_t);
bool ir_value_life_merge_into(ir_value*, const ir_value*);
/* check if a value lives at a specific point */
bool ir_value_lives(ir_value*, size_t);
/* check if the life-range of 2 values overlaps */
bool ir_values_overlap(const ir_value*, const ir_value*);
void ir_value_dump(ir_value*, int (*oprintf)(const char*,...));
void ir_value_dump_life(const ir_value *self, int (*oprintf)(const char*,...));
bool ir_value_lives(ir_value*, size_t);
void ir_value_dump_life(const ir_value *self, int (*oprintf)(const char*,...));
/* PHI data */
typedef struct ir_phi_entry_s
@ -150,15 +126,6 @@ typedef struct ir_instr_s
struct ir_block_s *owner;
} ir_instr;
ir_instr* ir_instr_new(lex_ctx ctx, struct ir_block_s *owner, int opcode);
void ir_instr_delete(ir_instr*);
bool GMQCC_WARN vec_ir_instr_find(ir_instr **vec, ir_instr *what, size_t *idx);
bool ir_instr_op(ir_instr*, int op, ir_value *value, bool writing);
void ir_instr_dump(ir_instr* in, char *ind, int (*oprintf)(const char*,...));
/* block */
typedef struct ir_block_s
{
@ -182,30 +149,16 @@ typedef struct ir_block_s
size_t code_start;
} ir_block;
ir_block* ir_block_new(struct ir_function_s *owner, const char *label);
void ir_block_delete(ir_block*);
bool ir_block_set_label(ir_block*, const char *label);
ir_value* ir_block_create_binop(ir_block*, lex_ctx, const char *label, int op,
ir_value *left, ir_value *right);
ir_value* ir_block_create_unary(ir_block*, lex_ctx, const char *label, int op,
ir_value *operand);
ir_value* ir_block_create_binop(ir_block*, lex_ctx, const char *label, int op, ir_value *left, ir_value *right);
ir_value* ir_block_create_unary(ir_block*, lex_ctx, const char *label, int op, ir_value *operand);
bool GMQCC_WARN ir_block_create_store_op(ir_block*, lex_ctx, int op, ir_value *target, ir_value *what);
bool GMQCC_WARN ir_block_create_store(ir_block*, lex_ctx, ir_value *target, ir_value *what);
bool GMQCC_WARN ir_block_create_storep(ir_block*, lex_ctx, ir_value *target, ir_value *what);
/* field must be of TYPE_FIELD */
ir_value* ir_block_create_load_from_ent(ir_block*, lex_ctx, const char *label, ir_value *ent, ir_value *field, int outype);
ir_value* ir_block_create_fieldaddress(ir_block*, lex_ctx, const char *label, ir_value *entity, ir_value *field);
ir_value* ir_block_create_load_from_ent(ir_block*, lex_ctx, const char *label, ir_value *ent, ir_value *field, int outype);
ir_value* ir_block_create_fieldaddress(ir_block*, lex_ctx, const char *label, ir_value *entity, ir_value *field);
/* This is to create an instruction of the form
* <outtype>%label := opcode a, b
*/
ir_value* ir_block_create_general_instr(ir_block *self, lex_ctx, const char *label,
int op, ir_value *a, ir_value *b, int outype);
ir_instr* ir_block_create_phi(ir_block*, lex_ctx, const char *label, int vtype);
ir_value* ir_phi_value(ir_instr*);
void ir_phi_add(ir_instr*, ir_block *b, ir_value *v);
@ -226,10 +179,7 @@ bool GMQCC_WARN ir_block_create_if(ir_block*, lex_ctx, ir_value *cond,
bool GMQCC_WARN ir_block_create_jump(ir_block*, lex_ctx, ir_block *to);
bool GMQCC_WARN ir_block_create_goto(ir_block*, lex_ctx, ir_block *to);
void ir_block_dump(ir_block*, char *ind, int (*oprintf)(const char*,...));
/* function */
typedef struct ir_function_s
{
char *name;
@ -276,6 +226,7 @@ typedef struct ir_function_s
/* vararg support: */
size_t max_varargs;
} ir_function;
#define IR_FLAG_HAS_ARRAYS (1<<1)
#define IR_FLAG_HAS_UNINITIALIZED (1<<2)
#define IR_FLAG_HAS_GOTO (1<<3)
@ -283,25 +234,9 @@ typedef struct ir_function_s
#define IR_FLAG_MASK_NO_OVERLAP (IR_FLAG_HAS_ARRAYS | IR_FLAG_HAS_UNINITIALIZED)
#define IR_FLAG_MASK_NO_LOCAL_TEMPS (IR_FLAG_HAS_ARRAYS | IR_FLAG_HAS_UNINITIALIZED)
ir_function* ir_function_new(struct ir_builder_s *owner, int returntype);
void ir_function_delete(ir_function*);
void ir_function_collect_value(ir_function*, ir_value *value);
bool ir_function_set_name(ir_function*, const char *name);
ir_value* ir_function_create_local(ir_function *self, const char *name, int vtype, bool param);
ir_value* ir_function_create_local(ir_function *self, const char *name, int vtype, bool param);
bool GMQCC_WARN ir_function_finalize(ir_function*);
/*
bool ir_function_naive_phi(ir_function*);
bool ir_function_enumerate(ir_function*);
bool ir_function_calculate_liferanges(ir_function*);
*/
ir_block* ir_function_create_block(lex_ctx ctx, ir_function*, const char *label);
void ir_function_dump(ir_function*, char *ind, int (*oprintf)(const char*,...));
ir_block* ir_function_create_block(lex_ctx ctx, ir_function*, const char *label);
/* builder */
#define IR_HT_SIZE 1024
@ -334,25 +269,14 @@ typedef struct ir_builder_s
ir_value *reserved_va_count;
} ir_builder;
ir_builder* ir_builder_new(const char *modulename);
void ir_builder_delete(ir_builder*);
bool ir_builder_set_name(ir_builder *self, const char *name);
ir_function* ir_builder_get_function(ir_builder*, const char *fun);
ir_builder* ir_builder_new(const char *modulename);
void ir_builder_delete(ir_builder*);
ir_function* ir_builder_create_function(ir_builder*, const char *name, int outtype);
ir_value* ir_builder_get_global(ir_builder*, const char *fun);
ir_value* ir_builder_create_global(ir_builder*, const char *name, int vtype);
ir_value* ir_builder_get_field(ir_builder*, const char *fun);
ir_value* ir_builder_create_field(ir_builder*, const char *name, int vtype);
ir_value* ir_builder_get_va_count(ir_builder*);
bool ir_builder_generate(code_t *, ir_builder *self, const char *filename);
void ir_builder_dump(ir_builder*, int (*oprintf)(const char*, ...));
ir_value* ir_builder_create_global(ir_builder*, const char *name, int vtype);
ir_value* ir_builder_create_field(ir_builder*, const char *name, int vtype);
ir_value* ir_builder_get_va_count(ir_builder*);
bool ir_builder_generate(code_t *, 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

59
lexer.c
View file

@ -20,14 +20,12 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>
#include <stdarg.h>
#include <stdlib.h>
#include "gmqcc.h"
#include "lexer.h"
/*
* List of Keywords
*/
@ -60,19 +58,19 @@ static size_t num_keywords_fg = sizeof(keywords_fg) / sizeof(keywords_fg[0]);
static char* *lex_filenames;
void lexerror(lex_file *lex, const char *fmt, ...)
static void lexerror(lex_file *lex, const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
if (lex)
con_vprintmsg(LVL_ERROR, lex->name, lex->sline, "parse error", fmt, ap);
con_vprintmsg(LVL_ERROR, lex->name, lex->sline, lex->column, "parse error", fmt, ap);
else
con_vprintmsg(LVL_ERROR, "", 0, "parse error", fmt, ap);
con_vprintmsg(LVL_ERROR, "", 0, 0, "parse error", fmt, ap);
va_end(ap);
}
bool lexwarn(lex_file *lex, int warntype, const char *fmt, ...)
static bool lexwarn(lex_file *lex, int warntype, const char *fmt, ...)
{
bool r;
lex_ctx ctx;
@ -174,9 +172,11 @@ static void lex_token_new(lex_file *lex)
#else
if (lex->tok.value)
vec_shrinkto(lex->tok.value, 0);
lex->tok.constval.t = 0;
lex->tok.ctx.line = lex->sline;
lex->tok.ctx.file = lex->name;
lex->tok.ctx.line = lex->sline;
lex->tok.ctx.file = lex->name;
lex->tok.ctx.column = lex->column;
#endif
}
#endif
@ -200,12 +200,12 @@ lex_file* lex_open(const char *file)
memset(lex, 0, sizeof(*lex));
lex->file = in;
lex->name = util_strdup(file);
lex->line = 1; /* we start counting at 1 */
lex->file = in;
lex->name = util_strdup(file);
lex->line = 1; /* we start counting at 1 */
lex->column = 0;
lex->peekpos = 0;
lex->eof = false;
lex->eof = false;
vec_push(lex_filenames, lex->name);
return lex;
@ -228,11 +228,11 @@ lex_file* lex_open_string(const char *str, size_t len, const char *name)
lex->open_string_length = len;
lex->open_string_pos = 0;
lex->name = util_strdup(name ? name : "<string-source>");
lex->line = 1; /* we start counting at 1 */
lex->name = util_strdup(name ? name : "<string-source>");
lex->line = 1; /* we start counting at 1 */
lex->peekpos = 0;
lex->eof = false;
lex->eof = false;
lex->column = 0;
vec_push(lex_filenames, lex->name);
@ -271,11 +271,14 @@ void lex_close(lex_file *lex)
static int lex_fgetc(lex_file *lex)
{
if (lex->file)
if (lex->file) {
lex->column++;
return fs_file_getc(lex->file);
}
if (lex->open_string) {
if (lex->open_string_pos >= lex->open_string_length)
return EOF;
lex->column++;
return lex->open_string[lex->open_string_pos++];
}
return EOF;
@ -291,16 +294,22 @@ static int lex_try_trigraph(lex_file *lex, int old)
{
int c2, c3;
c2 = lex_fgetc(lex);
if (!lex->push_line && c2 == '\n')
if (!lex->push_line && c2 == '\n') {
lex->line++;
lex->column = 0;
}
if (c2 != '?') {
lex_ungetch(lex, c2);
return old;
}
c3 = lex_fgetc(lex);
if (!lex->push_line && c3 == '\n')
if (!lex->push_line && c3 == '\n') {
lex->line++;
lex->column = 0;
}
switch (c3) {
case '=': return '#';
case '/': return '\\';
@ -365,8 +374,11 @@ static int lex_getch(lex_file *lex)
static void lex_ungetch(lex_file *lex, int ch)
{
lex->peek[lex->peekpos++] = ch;
if (!lex->push_line && ch == '\n')
lex->column--;
if (!lex->push_line && ch == '\n') {
lex->line--;
lex->column = 0;
}
}
/* classify characters
@ -872,6 +884,7 @@ static int GMQCC_WARN lex_finish_string(lex_file *lex, int quote)
ch = 0;
else {
--u8len;
lex->column += u8len;
for (uc = 0; uc < u8len; ++uc)
lex_tokench(lex, u8buf[uc]);
/* the last character will be inserted with the tokench() call

15
lexer.h
View file

@ -22,7 +22,6 @@
*/
#ifndef GMQCC_LEXER_HDR
#define GMQCC_LEXER_HDR
typedef struct token_s token;
struct token_s {
@ -114,6 +113,7 @@ typedef struct lex_file_s {
char *name;
size_t line;
size_t sline; /* line at the start of a token */
size_t column;
int peek[256];
size_t peekpos;
@ -163,9 +163,14 @@ typedef struct {
unsigned int flags;
} oper_info;
#define opid1(a) (a)
#define opid2(a,b) ((a<<8)|b)
#define opid3(a,b,c) ((a<<16)|(b<<8)|c)
/*
* Explicit uint8_t casts since the left operand of shift operator cannot
* be negative, even though it won't happen, this supresses the future
* possibility.
*/
#define opid1(a) ((uint8_t)a)
#define opid2(a,b) (((uint8_t)a<<8) |(uint8_t)b)
#define opid3(a,b,c) (((uint8_t)a<<16)|((uint8_t)b<<8)|(uint8_t)c)
static const oper_info c_operators[] = {
{ "(", 0, opid1('('), ASSOC_LEFT, 99, OP_PREFIX}, /* paren expression - non function call */
@ -335,6 +340,6 @@ static const size_t qcc_operator_count = (sizeof(qcc_operators) / sizeof(qcc_ope
extern const oper_info *operators;
extern size_t operator_count;
void lexerror(lex_file*, const char *fmt, ...);
/*void lexerror(lex_file*, const char *fmt, ...);*/
#endif

17
main.c
View file

@ -21,9 +21,15 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include <time.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include "gmqcc.h"
#include "lexer.h"
#include <time.h>
/* TODO: cleanup this whole file .. it's a fuckign mess */
@ -41,9 +47,10 @@ static ppitem *ppems = NULL;
#define TYPE_ASM 1
#define TYPE_SRC 2
static const char *app_name;
static void version() {
static void version(void) {
con_out("GMQCC %d.%d.%d Built %s %s\n" GMQCC_DEV_VERSION_STRING,
GMQCC_VERSION_MAJOR,
GMQCC_VERSION_MINOR,
@ -53,7 +60,7 @@ static void version() {
);
}
static int usage() {
static int usage(void) {
con_out("usage: %s [options] [files...]", app_name);
con_out("options:\n"
" -h, --help show this help message\n"
@ -172,6 +179,7 @@ static bool options_parse(int argc, char **argv) {
OPTS_OPTION_U32(OPTION_STANDARD) = COMPILER_GMQCC;
OPTS_OPTION_BOOL(OPTION_STATISTICS) = true;
} else if (!strcmp(argarg, "qcc")) {
@ -794,6 +802,7 @@ cleanup:
mem_d((void*)operators);
lex_cleanup();
util_meminfo();
stat_info();
return retval;
}

9
opts.c
View file

@ -21,11 +21,16 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
#include "gmqcc.h"
unsigned int opts_optimizationcount[COUNT_OPTIMIZATIONS];
opts_cmd_t opts; /* command lien options */
static void opts_setdefault() {
static void opts_setdefault(void) {
memset(&opts, 0, sizeof(opts_cmd_t));
OPTS_OPTION_BOOL(OPTION_CORRECTION) = true;
@ -361,7 +366,7 @@ void opts_ini_init(const char *file) {
if ((line = opts_ini_parse(ini, &opts_ini_load, &error)) != 0) {
/* there was a parse error with the ini file */
con_printmsg(LVL_ERROR, file, line, "error", error);
con_printmsg(LVL_ERROR, file, line, 0 /*TODO: column for ini error*/, "error", error);
vec_free(error);
}

View file

@ -51,6 +51,7 @@
GMQCC_DEFINE_FLAG(VARIADIC_ARGS)
GMQCC_DEFINE_FLAG(LEGACY_VECTOR_MATHS)
GMQCC_DEFINE_FLAG(EXPRESSIONS_FOR_BUILTINS)
GMQCC_DEFINE_FLAG(RETURN_ASSIGNMENTS)
#endif
/* warning flags */
@ -120,6 +121,7 @@
GMQCC_DEFINE_FLAG(MAX_ARRAY_SIZE)
GMQCC_DEFINE_FLAG(ADD_INFO)
GMQCC_DEFINE_FLAG(CORRECTION)
GMQCC_DEFINE_FLAG(STATISTICS)
#endif
/* some cleanup so we don't have to */

23
pak.c
View file

@ -20,13 +20,16 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include <string.h>
#include <stdlib.h>
#include "gmqcc.h"
/*
* The PAK format uses a FOURCC concept for storing the magic ident within
* the header as a uint32_t.
*/
#define PAK_FOURCC ((uint32_t)(('P' | ('A' << 8) | ('C' << 16) | ('K' << 24))))
#define PAK_FOURCC ((uint32_t)(((uint8_t)'P'|((uint8_t)'A'<<8)|((uint8_t)'C'<<16)|((uint8_t)'K'<<24))))
typedef struct {
uint32_t magic; /* "PACK" */
@ -222,7 +225,7 @@ static pak_file_t *pak_open_write(const char *file) {
return pak;
}
pak_file_t *pak_open(const char *file, const char *mode) {
static pak_file_t *pak_open(const char *file, const char *mode) {
if (!file || !mode)
return NULL;
@ -234,7 +237,7 @@ pak_file_t *pak_open(const char *file, const char *mode) {
return NULL;
}
bool pak_exists(pak_file_t *pak, const char *file, pak_directory_t **dir) {
static bool pak_exists(pak_file_t *pak, const char *file, pak_directory_t **dir) {
size_t itr;
if (!pak || !file)
@ -259,7 +262,7 @@ bool pak_exists(pak_file_t *pak, const char *file, pak_directory_t **dir) {
/*
* Extraction abilities. These work as you expect them to.
*/
bool pak_extract_one(pak_file_t *pak, const char *file, const char *outdir) {
static bool pak_extract_one(pak_file_t *pak, const char *file, const char *outdir) {
pak_directory_t *dir = NULL;
unsigned char *dat = NULL;
char *local = NULL;
@ -310,7 +313,7 @@ bool pak_extract_one(pak_file_t *pak, const char *file, const char *outdir) {
return true;
}
bool pak_extract_all(pak_file_t *pak, const char *dir) {
static bool pak_extract_all(pak_file_t *pak, const char *dir) {
size_t itr;
if (!fs_dir_make(dir))
@ -328,7 +331,7 @@ bool pak_extract_all(pak_file_t *pak, const char *dir) {
* Insertion functions (the opposite of extraction). Yes for generating
* PAKs.
*/
bool pak_insert_one(pak_file_t *pak, const char *file) {
static bool pak_insert_one(pak_file_t *pak, const char *file) {
pak_directory_t dir;
unsigned char *dat;
FILE *fp;
@ -413,7 +416,7 @@ bool pak_insert_all(pak_file_t *pak, const char *dir) {
return true;
}
bool pak_close(pak_file_t *pak) {
static bool pak_close(pak_file_t *pak) {
size_t itr;
if (!pak)
@ -552,7 +555,8 @@ int main(int argc, char **argv) {
/* not possible */
pak_close(pak);
vec_free(files);
util_meminfo();
stat_info();
return EXIT_SUCCESS;
}
@ -575,7 +579,6 @@ int main(int argc, char **argv) {
pak_close(pak);
vec_free(files);
util_meminfo();
stat_info();
return EXIT_SUCCESS;
}

450
parser.c

File diff suppressed because it is too large Load diff

628
stat.c Normal file
View file

@ -0,0 +1,628 @@
/*
* Copyright (C) 2012, 2013
* 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
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
* of the Software, and to permit persons to whom the Software is furnished to do
* so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
#include "gmqcc.h"
/*
* GMQCC performs tons of allocations, constructions, and crazyness
* all around. When trying to optimizes systems, or just get fancy
* statistics out of the compiler, it's often printf mess. This file
* implements the statistics system of the compiler. I.E the allocator
* we use to track allocations, and other systems of interest.
*/
#define ST_SIZE 1024
typedef struct stat_mem_block_s {
const char *file;
size_t line;
size_t size;
struct stat_mem_block_s *next;
struct stat_mem_block_s *prev;
} stat_mem_block_t;
typedef struct {
size_t key;
size_t value;
} stat_size_entry_t, **stat_size_table_t;
static uint64_t stat_mem_allocated = 0;
static uint64_t stat_mem_deallocated = 0;
static uint64_t stat_mem_allocated_total = 0;
static uint64_t stat_mem_deallocated_total = 0;
static uint64_t stat_mem_high = 0;
static uint64_t stat_mem_peak = 0;
static uint64_t stat_used_strdups = 0;
static uint64_t stat_used_vectors = 0;
static uint64_t stat_used_hashtables = 0;
static uint64_t stat_type_vectors = 0;
static uint64_t stat_type_hashtables = 0;
static stat_size_table_t stat_size_vectors = NULL;
static stat_size_table_t stat_size_hashtables = NULL;
static stat_mem_block_t *stat_mem_block_root = NULL;
/*
* A tiny size_t key-value hashtbale for tracking vector and hashtable
* sizes. We can use it for other things too, if we need to. This is
* very TIGHT, and efficent in terms of space though.
*/
static stat_size_table_t stat_size_new(void) {
return (stat_size_table_t)memset(
mem_a(sizeof(stat_size_entry_t*) * ST_SIZE),
0, ST_SIZE * sizeof(stat_size_entry_t*)
);
}
static void stat_size_del(stat_size_table_t table) {
size_t i = 0;
for (; i < ST_SIZE; i++) if(table[i]) mem_d(table[i]);
mem_d(table);
}
static stat_size_entry_t *stat_size_get(stat_size_table_t table, size_t key) {
size_t hash = (key % ST_SIZE);
while (table[hash] && table[hash]->key != key)
hash = (hash + 1) % ST_SIZE;
return table[hash];
}
static void stat_size_put(stat_size_table_t table, size_t key, size_t value) {
size_t hash = (key % ST_SIZE);
while (table[hash] && table[hash]->key != key)
hash = (hash + 1) % ST_SIZE;
table[hash] = (stat_size_entry_t*)mem_a(sizeof(stat_size_entry_t));
table[hash]->key = key;
table[hash]->value = value;
}
/*
* A basic header of information wrapper allocator. Simply stores
* information as a header, returns the memory + 1 past it, can be
* retrieved again with - 1. Where type is stat_mem_block_t*.
*/
void *stat_mem_allocate(size_t size, size_t line, const char *file) {
stat_mem_block_t *info = (stat_mem_block_t*)malloc(sizeof(stat_mem_block_t) + size);
void *data = (void*)(info + 1);
if(!info)
return NULL;
info->line = line;
info->size = size;
info->file = file;
info->prev = NULL;
info->next = stat_mem_block_root;
if (stat_mem_block_root)
stat_mem_block_root->prev = info;
stat_mem_block_root = info;
stat_mem_allocated += size;
stat_mem_high += size;
stat_mem_allocated_total ++;
if (stat_mem_high > stat_mem_peak)
stat_mem_peak = stat_mem_high;
return data;
}
void stat_mem_deallocate(void *ptr) {
stat_mem_block_t *info = NULL;
if (!ptr)
return;
info = ((stat_mem_block_t*)ptr - 1);
stat_mem_deallocated += info->size;
stat_mem_high -= info->size;
stat_mem_deallocated_total ++;
if (info->prev) info->prev->next = info->next;
if (info->next) info->next->prev = info->prev;
/* move ahead */
if (info == stat_mem_block_root)
stat_mem_block_root = info->next;
free(info);
}
void *stat_mem_reallocate(void *ptr, size_t size, size_t line, const char *file) {
stat_mem_block_t *oldinfo = NULL;
stat_mem_block_t *newinfo;
if (!ptr)
return stat_mem_allocate(size, line, file);
/* stay consistent with glic */
if (!size) {
stat_mem_deallocate(ptr);
return NULL;
}
oldinfo = ((stat_mem_block_t*)ptr - 1);
newinfo = ((stat_mem_block_t*)malloc(sizeof(stat_mem_block_t) + size));
if (!newinfo) {
stat_mem_deallocate(ptr);
return NULL;
}
memcpy(newinfo+1, oldinfo+1, oldinfo->size);
if (oldinfo->prev) oldinfo->prev->next = oldinfo->next;
if (oldinfo->next) oldinfo->next->prev = oldinfo->prev;
/* move ahead */
if (oldinfo == stat_mem_block_root)
stat_mem_block_root = oldinfo->next;
newinfo->line = line;
newinfo->size = size;
newinfo->file = file;
newinfo->prev = NULL;
newinfo->next = stat_mem_block_root;
if (stat_mem_block_root)
stat_mem_block_root->prev = newinfo;
stat_mem_block_root = newinfo;
stat_mem_allocated -= oldinfo->size;
stat_mem_high -= oldinfo->size;
stat_mem_allocated += newinfo->size;
stat_mem_high += newinfo->size;
if (stat_mem_high > stat_mem_peak)
stat_mem_peak = stat_mem_high;
free(oldinfo);
return newinfo + 1;
}
/*
* strdup does it's own malloc, we need to track malloc. We don't want
* to overwrite malloc though, infact, we can't really hook it at all
* without library specific assumptions. So we re implement strdup.
*/
char *stat_mem_strdup(const char *src, size_t line, const char *file, bool empty) {
size_t len = 0;
char *ptr = NULL;
if (!src)
return NULL;
len = strlen(src);
if (((!empty) ? len : true) && (ptr = (char*)stat_mem_allocate(len + 1, line, file))) {
memcpy(ptr, src, len);
ptr[len] = '\0';
}
stat_used_strdups ++;
return ptr;
}
/*
* The reallocate function for resizing vectors.
*/
void _util_vec_grow(void **a, size_t i, size_t s) {
vector_t *d = vec_meta(*a);
size_t m = 0;
stat_size_entry_t *e = NULL;
void *p = NULL;
if (*a) {
m = 2 * d->allocated + i;
p = mem_r(d, s * m + sizeof(vector_t));
} else {
m = i + 1;
p = mem_a(s * m + sizeof(vector_t));
((vector_t*)p)->used = 0;
stat_used_vectors++;
}
if (!stat_size_vectors)
stat_size_vectors = stat_size_new();
if ((e = stat_size_get(stat_size_vectors, s))) {
e->value ++;
} else {
stat_size_put(stat_size_vectors, s, 1); /* start off with 1 */
stat_type_vectors++;
}
*a = (vector_t*)p + 1;
vec_meta(*a)->allocated = m;
}
/*
* Hash table for generic data, based on dynamic memory allocations
* all around. This is the internal interface, please look for
* EXPOSED INTERFACE comment below
*/
typedef struct hash_node_t {
char *key; /* the key for this node in table */
void *value; /* pointer to the data as void* */
struct hash_node_t *next; /* next node (linked list) */
} hash_node_t;
GMQCC_INLINE size_t util_hthash(hash_table_t *ht, const char *key) {
const uint32_t mix = 0x5BD1E995;
const uint32_t rot = 24;
size_t size = strlen(key);
uint32_t hash = 0x1EF0 /* LICRC TAB */ ^ size;
uint32_t alias = 0;
const unsigned char *data = (const unsigned char*)key;
while (size >= 4) {
alias = (data[0] | (data[1] << 8) | (data[2] << 16) | (data[3] << 24));
alias *= mix;
alias ^= alias >> rot;
alias *= mix;
hash *= mix;
hash ^= alias;
data += 4;
size -= 4;
}
switch (size) {
case 3: hash ^= data[2] << 16;
case 2: hash ^= data[1] << 8;
case 1: hash ^= data[0];
hash *= mix;
}
hash ^= hash >> 13;
hash *= mix;
hash ^= hash >> 15;
return (size_t) (hash % ht->size);
}
static hash_node_t *_util_htnewpair(const char *key, void *value) {
hash_node_t *node;
if (!(node = (hash_node_t*)mem_a(sizeof(hash_node_t))))
return NULL;
if (!(node->key = util_strdupe(key))) {
mem_d(node);
return NULL;
}
node->value = value;
node->next = NULL;
return node;
}
/*
* EXPOSED INTERFACE for the hashtable implementation
* util_htnew(size) -- to make a new hashtable
* util_htset(table, key, value, sizeof(value)) -- to set something in the table
* util_htget(table, key) -- to get something from the table
* util_htdel(table) -- to delete the table
*/
hash_table_t *util_htnew(size_t size) {
hash_table_t *hashtable = NULL;
stat_size_entry_t *find = NULL;
if (size < 1)
return NULL;
if (!stat_size_hashtables)
stat_size_hashtables = stat_size_new();
if (!(hashtable = (hash_table_t*)mem_a(sizeof(hash_table_t))))
return NULL;
if (!(hashtable->table = (hash_node_t**)mem_a(sizeof(hash_node_t*) * size))) {
mem_d(hashtable);
return NULL;
}
if ((find = stat_size_get(stat_size_hashtables, size)))
find->value++;
else {
stat_type_hashtables++;
stat_size_put(stat_size_hashtables, size, 1);
}
hashtable->size = size;
memset(hashtable->table, 0, sizeof(hash_node_t*) * size);
stat_used_hashtables++;
return hashtable;
}
void util_htseth(hash_table_t *ht, const char *key, size_t bin, void *value) {
hash_node_t *newnode = NULL;
hash_node_t *next = NULL;
hash_node_t *last = NULL;
next = ht->table[bin];
while (next && next->key && strcmp(key, next->key) > 0)
last = next, next = next->next;
/* already in table, do a replace */
if (next && next->key && strcmp(key, next->key) == 0) {
next->value = value;
} else {
/* not found, grow a pair man :P */
newnode = _util_htnewpair(key, value);
if (next == ht->table[bin]) {
newnode->next = next;
ht->table[bin] = newnode;
} else if (!next) {
last->next = newnode;
} else {
newnode->next = next;
last->next = newnode;
}
}
}
void util_htset(hash_table_t *ht, const char *key, void *value) {
util_htseth(ht, key, util_hthash(ht, key), value);
}
void *util_htgeth(hash_table_t *ht, const char *key, size_t bin) {
hash_node_t *pair = ht->table[bin];
while (pair && pair->key && strcmp(key, pair->key) > 0)
pair = pair->next;
if (!pair || !pair->key || strcmp(key, pair->key) != 0)
return NULL;
return pair->value;
}
void *util_htget(hash_table_t *ht, const char *key) {
return util_htgeth(ht, key, util_hthash(ht, key));
}
void *code_util_str_htgeth(hash_table_t *ht, const char *key, size_t bin) {
hash_node_t *pair;
size_t len, keylen;
int cmp;
keylen = strlen(key);
pair = ht->table[bin];
while (pair && pair->key) {
len = strlen(pair->key);
if (len < keylen) {
pair = pair->next;
continue;
}
if (keylen == len) {
cmp = strcmp(key, pair->key);
if (cmp == 0)
return pair->value;
if (cmp < 0)
return NULL;
pair = pair->next;
continue;
}
cmp = strcmp(key, pair->key + len - keylen);
if (cmp == 0) {
uintptr_t up = (uintptr_t)pair->value;
up += len - keylen;
return (void*)up;
}
pair = pair->next;
}
return NULL;
}
/*
* Free all allocated data in a hashtable, this is quite the amount
* of work.
*/
void util_htrem(hash_table_t *ht, void (*callback)(void *data)) {
size_t i = 0;
for (; i < ht->size; i++) {
hash_node_t *n = ht->table[i];
hash_node_t *p;
/* free in list */
while (n) {
if (n->key)
mem_d(n->key);
if (callback)
callback(n->value);
p = n;
n = n->next;
mem_d(p);
}
}
/* free table */
mem_d(ht->table);
mem_d(ht);
}
void util_htrmh(hash_table_t *ht, const char *key, size_t bin, void (*cb)(void*)) {
hash_node_t **pair = &ht->table[bin];
hash_node_t *tmp;
while (*pair && (*pair)->key && strcmp(key, (*pair)->key) > 0)
pair = &(*pair)->next;
tmp = *pair;
if (!tmp || !tmp->key || strcmp(key, tmp->key) != 0)
return;
if (cb)
(*cb)(tmp->value);
*pair = tmp->next;
mem_d(tmp->key);
mem_d(tmp);
}
void util_htrm(hash_table_t *ht, const char *key, void (*cb)(void*)) {
util_htrmh(ht, key, util_hthash(ht, key), cb);
}
void util_htdel(hash_table_t *ht) {
util_htrem(ht, NULL);
}
/*
* The following functions below implement printing / dumping of statistical
* information.
*/
static void stat_dump_mem_contents(stat_mem_block_t *memory, uint16_t cols) {
uint32_t i, j;
for (i = 0; i < memory->size + ((memory->size % cols) ? (cols - memory->size % cols) : 0); i++) {
if (i % cols == 0) con_out(" 0x%06X: ", i);
if (i < memory->size) con_out("%02X " , 0xFF & ((unsigned char*)(memory + 1))[i]);
else con_out(" ");
if ((uint16_t)(i % cols) == (cols - 1)) {
for (j = i - (cols - 1); j <= i; j++) {
con_out("%c",
(j >= memory->size)
? ' '
: (isprint(((unsigned char*)(memory + 1))[j]))
? 0xFF & ((unsigned char*)(memory + 1)) [j]
: '.'
);
}
con_out("\n");
}
}
}
static void stat_dump_mem_leaks(void) {
stat_mem_block_t *info;
for (info = stat_mem_block_root; info; info = info->next) {
con_out("lost: %u (bytes) at %s:%u\n",
info->size,
info->file,
info->line
);
stat_dump_mem_contents(info, OPTS_OPTION_U16(OPTION_MEMDUMPCOLS));
}
}
static void stat_dump_mem_info(void) {
con_out("Memory information:\n\
Total allocations: %llu\n\
Total deallocations: %llu\n\
Total allocated: %f (MB)\n\
Total deallocated: %f (MB)\n\
Total peak memory: %f (MB)\n\
Total leaked memory: %f (MB) in %llu allocations\n",
stat_mem_allocated_total,
stat_mem_deallocated_total,
(float)(stat_mem_allocated) / 1048576.0f,
(float)(stat_mem_deallocated) / 1048576.0f,
(float)(stat_mem_peak) / 1048576.0f,
(float)(stat_mem_allocated - stat_mem_deallocated) / 1048576.0f,
stat_mem_allocated_total - stat_mem_deallocated_total
);
}
static void stat_dump_stats_table(stat_size_table_t table, const char *string, uint64_t *size) {
size_t i,j;
if (!table)
return;
for (i = 0, j = 1; i < ST_SIZE; i++) {
stat_size_entry_t *entry;
if (!(entry = table[i]))
continue;
con_out(string, (unsigned)j, (unsigned)entry->key, (unsigned)entry->value);
j++;
if (size)
*size += entry->key * entry->value;
}
}
void stat_info() {
if (OPTS_OPTION_BOOL(OPTION_DEBUG))
stat_dump_mem_leaks();
if (OPTS_OPTION_BOOL(OPTION_DEBUG) ||
OPTS_OPTION_BOOL(OPTION_MEMCHK))
stat_dump_mem_info();
if (OPTS_OPTION_BOOL(OPTION_MEMCHK) ||
OPTS_OPTION_BOOL(OPTION_STATISTICS)) {
uint64_t mem = 0;
con_out("\nAdditional Statistics:\n\
Total vectors allocated: %llu\n\
Total string duplicates: %llu\n\
Total hashtables allocated: %llu\n\
Total unique vector sizes: %llu\n",
stat_used_vectors,
stat_used_strdups,
stat_used_hashtables,
stat_type_vectors
);
stat_dump_stats_table (
stat_size_vectors,
" %2u| # of %4u byte vectors: %u\n",
&mem
);
con_out (
" Total unique hashtable sizes: %llu\n",
stat_type_hashtables
);
stat_dump_stats_table (
stat_size_hashtables,
" %2u| # of %4u element hashtables: %u\n",
NULL
);
con_out (
" Total vector memory: %f (MB)\n",
(float)(mem) / 1048576.0f
);
}
if (stat_size_vectors)
stat_size_del(stat_size_vectors);
if (stat_size_hashtables)
stat_size_del(stat_size_hashtables);
}
#undef ST_SIZE

54
test.c
View file

@ -20,10 +20,13 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include "gmqcc.h"
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include "gmqcc.h"
opts_cmd_t opts;
static const char *task_bins[] = {
@ -60,7 +63,7 @@ typedef struct {
int pid;
} popen_t;
FILE ** task_popen(const char *command, const char *mode) {
static FILE ** task_popen(const char *command, const char *mode) {
int inhandle [2];
int outhandle [2];
int errhandle [2];
@ -137,7 +140,7 @@ task_popen_error_0:
return NULL;
}
int task_pclose(FILE **handles) {
static int task_pclose(FILE **handles) {
popen_t *data = (popen_t*)handles;
int status = 0;
@ -158,7 +161,7 @@ int task_pclose(FILE **handles) {
char name_out[L_tmpnam];
} popen_t;
FILE **task_popen(const char *command, const char *mode) {
static FILE **task_popen(const char *command, const char *mode) {
char *cmd = NULL;
popen_t *open = (popen_t*)mem_a(sizeof(popen_t));
@ -179,7 +182,7 @@ int task_pclose(FILE **handles) {
return open->handles;
}
void task_pclose(FILE **files) {
static void task_pclose(FILE **files) {
popen_t *open = ((popen_t*)files);
fs_file_close(files[1]);
fs_file_close(files[2]);
@ -281,7 +284,7 @@ typedef struct {
* This is very much like a compiler code generator :-). This generates
* a value from some data observed from the compiler.
*/
bool task_template_generate(task_template_t *tmpl, char tag, const char *file, size_t line, char *value, size_t *pad) {
static bool task_template_generate(task_template_t *tmpl, char tag, const char *file, size_t line, char *value, size_t *pad) {
size_t desclen = 0;
size_t filelen = 0;
char **destval = NULL;
@ -297,7 +300,7 @@ bool task_template_generate(task_template_t *tmpl, char tag, const char *file, s
case 'I': destval = &tmpl->sourcefile; break;
case 'F': destval = &tmpl->testflags; break;
default:
con_printmsg(LVL_ERROR, __FILE__, __LINE__, "internal error",
con_printmsg(LVL_ERROR, __FILE__, __LINE__, 0, "internal error",
"invalid tag `%c:` during code generation\n",
tag
);
@ -309,7 +312,7 @@ bool task_template_generate(task_template_t *tmpl, char tag, const char *file, s
* assigned value.
*/
if (*destval) {
con_printmsg(LVL_ERROR, file, line, "compile error",
con_printmsg(LVL_ERROR, file, line, 0, /*TODO: column for match*/ "compile error",
"tag `%c:` already assigned value: %s\n",
tag, *destval
);
@ -354,7 +357,7 @@ bool task_template_generate(task_template_t *tmpl, char tag, const char *file, s
return true;
}
bool task_template_parse(const char *file, task_template_t *tmpl, FILE *fp, size_t *pad) {
static bool task_template_parse(const char *file, task_template_t *tmpl, FILE *fp, size_t *pad) {
char *data = NULL;
char *back = NULL;
size_t size = 0;
@ -377,7 +380,7 @@ bool task_template_parse(const char *file, task_template_t *tmpl, FILE *fp, size
*/
case '/':
if (data[1] != '/') {
con_printmsg(LVL_ERROR, file, line, "tmpl parse error",
con_printmsg(LVL_ERROR, file, line, 0, /*TODO: column for match*/ "tmpl parse error",
"invalid character `/`, perhaps you meant `//` ?");
mem_d(back);
@ -407,14 +410,14 @@ bool task_template_parse(const char *file, task_template_t *tmpl, FILE *fp, size
case 'I':
case 'F':
if (data[1] != ':') {
con_printmsg(LVL_ERROR, file, line, "tmpl parse error",
con_printmsg(LVL_ERROR, file, line, 0, /*TODO: column for match*/ "tmpl parse error",
"expected `:` after `%c`",
*data
);
goto failure;
}
if (!task_template_generate(tmpl, *data, file, line, &data[3], pad)) {
con_printmsg(LVL_ERROR, file, line, "tmpl compile error",
con_printmsg(LVL_ERROR, file, line, 0, /*TODO: column for match*/ "tmpl compile error",
"failed to generate for given task\n"
);
goto failure;
@ -429,7 +432,7 @@ bool task_template_parse(const char *file, task_template_t *tmpl, FILE *fp, size
{
char *value = &data[3];
if (data[1] != ':') {
con_printmsg(LVL_ERROR, file, line, "tmpl parse error",
con_printmsg(LVL_ERROR, file, line, 0, /*TODO: column for match*/ "tmpl parse error",
"expected `:` after `%c`",
*data
);
@ -454,7 +457,7 @@ bool task_template_parse(const char *file, task_template_t *tmpl, FILE *fp, size
}
default:
con_printmsg(LVL_ERROR, file, line, "tmpl parse error",
con_printmsg(LVL_ERROR, file, line, 0, /*TODO: column for match*/ "tmpl parse error",
"invalid tag `%c`", *data
);
goto failure;
@ -480,7 +483,7 @@ failure:
* Nullifies the template data: used during initialization of a new
* template and free.
*/
void task_template_nullify(task_template_t *tmpl) {
static void task_template_nullify(task_template_t *tmpl) {
if (!tmpl)
return;
@ -495,7 +498,7 @@ void task_template_nullify(task_template_t *tmpl) {
tmpl->testflags = NULL;
}
task_template_t *task_template_compile(const char *file, const char *dir, size_t *pad) {
static task_template_t *task_template_compile(const char *file, const char *dir, size_t *pad) {
/* a page should be enough */
char fullfile[4096];
size_t filepadd = 0;
@ -609,7 +612,7 @@ failure:
return NULL;
}
void task_template_destroy(task_template_t **tmpl) {
static void task_template_destroy(task_template_t **tmpl) {
if (!tmpl)
return;
@ -660,7 +663,7 @@ static task_t *task_tasks = NULL;
* Read a directory and searches for all template files in it
* which is later used to run all tests.
*/
bool task_propagate(const char *curdir, size_t *pad, const char *defs) {
static bool task_propagate(const char *curdir, size_t *pad, const char *defs) {
bool success = true;
DIR *dir;
struct dirent *files;
@ -840,7 +843,7 @@ bool task_propagate(const char *curdir, size_t *pad, const char *defs) {
* Task precleanup removes any existing temporary files or log files
* left behind from a previous invoke of the test-suite.
*/
void task_precleanup(const char *curdir) {
static void task_precleanup(const char *curdir) {
DIR *dir;
struct dirent *files;
char buffer[4096];
@ -863,7 +866,7 @@ void task_precleanup(const char *curdir) {
fs_dir_close(dir);
}
void task_destroy(void) {
static void task_destroy(void) {
/*
* Free all the data in the task list and finally the list itself
* then proceed to cleanup anything else outside the program like
@ -912,7 +915,7 @@ void task_destroy(void) {
* messages IF the procedure type is -execute, otherwise it matches
* the preprocessor output.
*/
bool task_trymatch(task_template_t *tmpl, char ***line) {
static bool task_trymatch(task_template_t *tmpl, char ***line) {
bool success = true;
bool preprocessing = false;
FILE *execute;
@ -1020,7 +1023,7 @@ bool task_trymatch(task_template_t *tmpl, char ***line) {
return success;
}
const char *task_type(task_template_t *tmpl) {
static const char *task_type(task_template_t *tmpl) {
if (!strcmp(tmpl->proceduretype, "-pp"))
return "type: preprocessor";
if (!strcmp(tmpl->proceduretype, "-execute"))
@ -1037,7 +1040,7 @@ const char *task_type(task_template_t *tmpl) {
* from thin air and executed INLINE.
*/
#include <math.h>
void task_schedualize(size_t *pad) {
static void task_schedualize(size_t *pad) {
char space[2][64];
bool execute = false;
char *data = NULL;
@ -1210,7 +1213,7 @@ void task_schedualize(size_t *pad) {
*
* It expects con_init() was called before hand.
*/
GMQCC_WARN bool test_perform(const char *curdir, const char *defs) {
static GMQCC_WARN bool test_perform(const char *curdir, const char *defs) {
static const char *default_defs = "defs.qh";
size_t pad[] = {
@ -1283,6 +1286,7 @@ int main(int argc, char **argv) {
char *defs = NULL;
con_init();
OPTS_OPTION_U16(OPTION_MEMDUMPCOLS) = 16;
/*
* Command line option parsing commences now We only need to support
@ -1321,7 +1325,7 @@ int main(int argc, char **argv) {
}
con_change(redirout, redirerr);
succeed = test_perform("tests", defs);
util_meminfo();
stat_info();
return (succeed) ? EXIT_SUCCESS : EXIT_FAILURE;

35
tests/rassign.qc Normal file
View file

@ -0,0 +1,35 @@
float f_float() {
return = 100.0f;
return = 200.0f;
return;
}
vector f_vector() {
vector foo;
foo.x = f_float();
foo.y = f_float();
foo.z = f_float();
return = foo;
return;
}
string f_string() {
#ifndef FAIL_TEST
return = "hello";
return = "world";
#endif
return;
}
float factorial(float n) {
if (n == 0) return = 1;
else return = n * factorial(n - 1);
}
void main() {
print(ftos(f_float()), "\n"); // 200.0f
print(vtos(f_vector()), "\n"); // '1 2 3'
print(f_string(), "\n"); // world
print(ftos(factorial(4)), "\n"); // 24
}

8
tests/rassign.tmpl Normal file
View file

@ -0,0 +1,8 @@
I: rassign.qc
D: test return assignments
T: -execute
C: -fftepp -freturn-assignments
M: 200
M: '200 200 200'
M: world
M: 24

464
util.c
View file

@ -21,230 +21,11 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include <stdarg.h>
#include <errno.h>
#include <string.h>
#include <ctype.h>
#include "gmqcc.h"
/* TODO: remove globals ... */
static uint64_t mem_ab = 0;
static uint64_t mem_db = 0;
static uint64_t mem_at = 0;
static uint64_t mem_dt = 0;
static uint64_t mem_pk = 0;
static uint64_t mem_hw = 0;
struct memblock_t {
const char *file;
unsigned int line;
size_t byte;
struct memblock_t *next;
struct memblock_t *prev;
};
#define PEAK_MEM \
do { \
if (mem_hw > mem_pk) \
mem_pk = mem_hw; \
} while (0)
static struct memblock_t *mem_start = NULL;
void *util_memory_a(size_t byte, unsigned int line, const char *file) {
struct memblock_t *info = (struct memblock_t*)malloc(sizeof(struct memblock_t) + byte);
void *data = (void*)(info+1);
if (!info) return NULL;
info->line = line;
info->byte = byte;
info->file = file;
info->prev = NULL;
info->next = mem_start;
if (mem_start)
mem_start->prev = info;
mem_start = info;
mem_at++;
mem_ab += info->byte;
mem_hw += info->byte;
PEAK_MEM;
return data;
}
void util_memory_d(void *ptrn) {
struct memblock_t *info = NULL;
if (!ptrn) return;
info = ((struct memblock_t*)ptrn - 1);
mem_db += info->byte;
mem_hw -= info->byte;
mem_dt++;
if (info->prev)
info->prev->next = info->next;
if (info->next)
info->next->prev = info->prev;
if (info == mem_start)
mem_start = info->next;
free(info);
}
void *util_memory_r(void *ptrn, size_t byte, unsigned int line, const char *file) {
struct memblock_t *oldinfo = NULL;
struct memblock_t *newinfo;
if (!ptrn)
return util_memory_a(byte, line, file);
if (!byte) {
util_memory_d(ptrn);
return NULL;
}
oldinfo = ((struct memblock_t*)ptrn - 1);
newinfo = ((struct memblock_t*)malloc(sizeof(struct memblock_t) + byte));
/* new data */
if (!newinfo) {
util_memory_d(oldinfo+1);
return NULL;
}
/* copy old */
memcpy(newinfo+1, oldinfo+1, oldinfo->byte);
/* free old */
if (oldinfo->prev)
oldinfo->prev->next = oldinfo->next;
if (oldinfo->next)
oldinfo->next->prev = oldinfo->prev;
if (oldinfo == mem_start)
mem_start = oldinfo->next;
/* fill info */
newinfo->line = line;
newinfo->byte = byte;
newinfo->file = file;
newinfo->prev = NULL;
newinfo->next = mem_start;
if (mem_start)
mem_start->prev = newinfo;
mem_start = newinfo;
mem_ab -= oldinfo->byte;
mem_hw -= oldinfo->byte;
mem_ab += newinfo->byte;
mem_hw += newinfo->byte;
PEAK_MEM;
free(oldinfo);
return newinfo+1;
}
static void util_dumpmem(struct memblock_t *memory, uint16_t cols) {
uint32_t i, j;
for (i = 0; i < memory->byte + ((memory->byte % cols) ? (cols - memory->byte % cols) : 0); i++) {
if (i % cols == 0) con_out(" 0x%06X: ", i);
if (i < memory->byte) con_out("%02X " , 0xFF & ((char*)(memory + 1))[i]);
else con_out(" ");
if ((uint16_t)(i % cols) == (cols - 1)) {
for (j = i - (cols - 1); j <= i; j++) {
con_out("%c",
(j >= memory->byte)
? ' '
: (isprint(((char*)(memory + 1))[j]))
? 0xFF & ((char*)(memory + 1)) [j]
: '.'
);
}
con_out("\n");
}
}
}
void util_meminfo() {
struct memblock_t *info;
if (OPTS_OPTION_BOOL(OPTION_DEBUG)) {
for (info = mem_start; info; info = info->next) {
con_out("lost: %u (bytes) at %s:%u\n",
info->byte,
info->file,
info->line);
util_dumpmem(info, OPTS_OPTION_U16(OPTION_MEMDUMPCOLS));
}
}
if (OPTS_OPTION_BOOL(OPTION_DEBUG) ||
OPTS_OPTION_BOOL(OPTION_MEMCHK)) {
con_out("Memory information:\n\
Total allocations: %llu\n\
Total deallocations: %llu\n\
Total allocated: %f (MB)\n\
Total deallocated: %f (MB)\n\
Total peak memory: %f (MB)\n\
Total leaked memory: %f (MB) in %llu allocations\n",
mem_at,
mem_dt,
(float)(mem_ab) / 1048576.0f,
(float)(mem_db) / 1048576.0f,
(float)(mem_pk) / 1048576.0f,
(float)(mem_ab - mem_db) / 1048576.0f,
/* could be more clever */
(mem_at - mem_dt)
);
}
}
/*
* Some string utility functions, because strdup uses malloc, and we want
* to track all memory (without replacing malloc).
*/
char *_util_Estrdup(const char *s, const char *file, size_t line) {
size_t len = 0;
char *ptr = NULL;
/* in case of -DNOTRACK */
(void)file;
(void)line;
if (!s)
return NULL;
if ((len = strlen(s)) && (ptr = (char*)mem_af(len+1, line, file))) {
memcpy(ptr, s, len);
ptr[len] = '\0';
}
return ptr;
}
char *_util_Estrdup_empty(const char *s, const char *file, size_t line) {
size_t len = 0;
char *ptr = NULL;
/* in case of -DNOTRACK */
(void)file;
(void)line;
if (!s)
return NULL;
len = strlen(s);
if ((ptr = (char*)mem_af(len+1, line, file))) {
memcpy(ptr, s, len);
ptr[len] = '\0';
}
return ptr;
}
void util_debug(const char *area, const char *ms, ...) {
va_list va;
if (!OPTS_OPTION_BOOL(OPTION_DEBUG))
@ -432,243 +213,6 @@ size_t util_strtononcmd(const char *in, char *out, size_t outsz) {
return sz-1;
}
/* TODO: rewrite ... when I redo the ve cleanup */
void _util_vec_grow(void **a, size_t i, size_t s) {
vector_t *d = vec_meta(*a);
size_t m = *a ? 2 * d->allocated +i : i+1;
void *p = mem_r((*a ? d : NULL), s * m + sizeof(vector_t));
if (!*a)
((vector_t*)p)->used = 0;
*a = (vector_t*)p + 1;
vec_meta(*a)->allocated = m;
}
/*
* Hash table for generic data, based on dynamic memory allocations
* all around. This is the internal interface, please look for
* EXPOSED INTERFACE comment below
*/
typedef struct hash_node_t {
char *key; /* the key for this node in table */
void *value; /* pointer to the data as void* */
struct hash_node_t *next; /* next node (linked list) */
} hash_node_t;
GMQCC_INLINE size_t util_hthash(hash_table_t *ht, const char *key) {
const uint32_t mix = 0x5BD1E995;
const uint32_t rot = 24;
size_t size = strlen(key);
uint32_t hash = 0x1EF0 /* LICRC TAB */ ^ size;
uint32_t alias = 0;
const unsigned char *data = (const unsigned char*)key;
while (size >= 4) {
alias = (data[0] | (data[1] << 8) | (data[2] << 16) | (data[3] << 24));
alias *= mix;
alias ^= alias >> rot;
alias *= mix;
hash *= mix;
hash ^= alias;
data += 4;
size -= 4;
}
switch (size) {
case 3: hash ^= data[2] << 16;
case 2: hash ^= data[1] << 8;
case 1: hash ^= data[0];
hash *= mix;
}
hash ^= hash >> 13;
hash *= mix;
hash ^= hash >> 15;
return (size_t) (hash % ht->size);
}
hash_node_t *_util_htnewpair(const char *key, void *value) {
hash_node_t *node;
if (!(node = (hash_node_t*)mem_a(sizeof(hash_node_t))))
return NULL;
if (!(node->key = util_strdupe(key))) {
mem_d(node);
return NULL;
}
node->value = value;
node->next = NULL;
return node;
}
/*
* EXPOSED INTERFACE for the hashtable implementation
* util_htnew(size) -- to make a new hashtable
* util_htset(table, key, value, sizeof(value)) -- to set something in the table
* util_htget(table, key) -- to get something from the table
* util_htdel(table) -- to delete the table
*/
hash_table_t *util_htnew(size_t size) {
hash_table_t *hashtable = NULL;
if (size < 1)
return NULL;
if (!(hashtable = (hash_table_t*)mem_a(sizeof(hash_table_t))))
return NULL;
if (!(hashtable->table = (hash_node_t**)mem_a(sizeof(hash_node_t*) * size))) {
mem_d(hashtable);
return NULL;
}
hashtable->size = size;
memset(hashtable->table, 0, sizeof(hash_node_t*) * size);
return hashtable;
}
void util_htseth(hash_table_t *ht, const char *key, size_t bin, void *value) {
hash_node_t *newnode = NULL;
hash_node_t *next = NULL;
hash_node_t *last = NULL;
next = ht->table[bin];
while (next && next->key && strcmp(key, next->key) > 0)
last = next, next = next->next;
/* already in table, do a replace */
if (next && next->key && strcmp(key, next->key) == 0) {
next->value = value;
} else {
/* not found, grow a pair man :P */
newnode = _util_htnewpair(key, value);
if (next == ht->table[bin]) {
newnode->next = next;
ht->table[bin] = newnode;
} else if (!next) {
last->next = newnode;
} else {
newnode->next = next;
last->next = newnode;
}
}
}
void util_htset(hash_table_t *ht, const char *key, void *value) {
util_htseth(ht, key, util_hthash(ht, key), value);
}
void *util_htgeth(hash_table_t *ht, const char *key, size_t bin) {
hash_node_t *pair = ht->table[bin];
while (pair && pair->key && strcmp(key, pair->key) > 0)
pair = pair->next;
if (!pair || !pair->key || strcmp(key, pair->key) != 0)
return NULL;
return pair->value;
}
void *util_htget(hash_table_t *ht, const char *key) {
return util_htgeth(ht, key, util_hthash(ht, key));
}
void *code_util_str_htgeth(hash_table_t *ht, const char *key, size_t bin) {
hash_node_t *pair;
size_t len, keylen;
int cmp;
keylen = strlen(key);
pair = ht->table[bin];
while (pair && pair->key) {
len = strlen(pair->key);
if (len < keylen) {
pair = pair->next;
continue;
}
if (keylen == len) {
cmp = strcmp(key, pair->key);
if (cmp == 0)
return pair->value;
if (cmp < 0)
return NULL;
pair = pair->next;
continue;
}
cmp = strcmp(key, pair->key + len - keylen);
if (cmp == 0) {
uintptr_t up = (uintptr_t)pair->value;
up += len - keylen;
return (void*)up;
}
pair = pair->next;
}
return NULL;
}
/*
* Free all allocated data in a hashtable, this is quite the amount
* of work.
*/
void util_htrem(hash_table_t *ht, void (*callback)(void *data)) {
size_t i = 0;
for (; i < ht->size; i++) {
hash_node_t *n = ht->table[i];
hash_node_t *p;
/* free in list */
while (n) {
if (n->key)
mem_d(n->key);
if (callback)
callback(n->value);
p = n;
n = n->next;
mem_d(p);
}
}
/* free table */
mem_d(ht->table);
mem_d(ht);
}
void util_htrmh(hash_table_t *ht, const char *key, size_t bin, void (*cb)(void*)) {
hash_node_t **pair = &ht->table[bin];
hash_node_t *tmp;
while (*pair && (*pair)->key && strcmp(key, (*pair)->key) > 0)
pair = &(*pair)->next;
tmp = *pair;
if (!tmp || !tmp->key || strcmp(key, tmp->key) != 0)
return;
if (cb)
(*cb)(tmp->value);
*pair = tmp->next;
mem_d(tmp->key);
mem_d(tmp);
}
void util_htrm(hash_table_t *ht, const char *key, void (*cb)(void*)) {
util_htrmh(ht, key, util_hthash(ht, key), cb);
}
void util_htdel(hash_table_t *ht) {
util_htrem(ht, NULL);
}
/*
* Portable implementation of vasprintf/asprintf. Assumes vsnprintf
* exists, otherwise compiler error.
@ -845,7 +389,7 @@ int util_asprintf(char **ret, const char *fmt, ...) {
static uint32_t mt_state[MT_SIZE];
static size_t mt_index = 0;
static GMQCC_INLINE void mt_generate() {
static GMQCC_INLINE void mt_generate(void) {
/*
* The loop has been unrolled here: the original paper and implemenation
* Called for the following code: