mirror of
https://github.com/DarkPlacesEngine/gmqcc.git
synced 2025-03-20 01:44:17 +00:00
Merge branch 'cooking'
Conflicts: .gitignore
This commit is contained in:
commit
0eab97283f
34 changed files with 1924 additions and 1353 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -17,4 +17,3 @@ gmqcc
|
|||
gmqpak
|
||||
qcvm
|
||||
testsuite
|
||||
pak
|
||||
|
|
4
AUTHORS
4
AUTHORS
|
@ -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
30
CHANGES
|
@ -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.
|
||||
|
||||
|
|
73
Makefile
73
Makefile
|
@ -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
358
ast.c
|
@ -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, ¶m))
|
||||
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
172
ast.h
|
@ -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
219
code.c
|
@ -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;
|
||||
}
|
||||
|
|
21
conout.c
21
conout.c
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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"
|
||||
|
||||
/*
|
||||
|
|
|
@ -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
35
distro/gentoo/INSTALL
Normal 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
|
20
distro/gentoo/gmqcc-0.3.0.ebuild
Normal file
20
distro/gentoo/gmqcc-0.3.0.ebuild
Normal 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
|
||||
}
|
35
distro/slackware/this/Makefile
Normal file
35
distro/slackware/this/Makefile
Normal 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
|
12
distro/slackware/this/slack-desc
Normal file
12
distro/slackware/this/slack-desc
Normal 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:
|
12
doc/gmqcc.1
12
doc/gmqcc.1
|
@ -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
38
doc/gmqpak.1
Normal 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
141
exec.c
|
@ -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
31
ftepp.c
|
@ -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
86
gmqcc.h
|
@ -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))
|
||||
|
|
16
intrin.h
16
intrin.h
|
@ -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
90
ir.c
|
@ -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
112
ir.h
|
@ -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
59
lexer.c
|
@ -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
15
lexer.h
|
@ -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
17
main.c
|
@ -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
9
opts.c
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
2
opts.def
2
opts.def
|
@ -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
23
pak.c
|
@ -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;
|
||||
}
|
||||
|
|
628
stat.c
Normal file
628
stat.c
Normal 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
54
test.c
|
@ -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
35
tests/rassign.qc
Normal 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
8
tests/rassign.tmpl
Normal 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
464
util.c
|
@ -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:
|
||||
|
|
Loading…
Reference in a new issue