Merge branch 'qfcc-cse'

qfcc now does local common subexpression elimination. It seems to work, but
is optional (default off): use -O to enable. Also, uninitialized variable
detection is finally back :)

The progs engine now has very basic valgrind-like functionality for
checking pointer accesses. Enable with pr_boundscheck 2
This commit is contained in:
Bill Currie 2012-12-13 20:17:28 +09:00
commit 3e38c4aa48
185 changed files with 7306 additions and 1038 deletions

View file

@ -62,6 +62,7 @@
tools/qfcc/doc/man/Makefile
tools/qfcc/include/Makefile
tools/qfcc/source/Makefile
tools/qfcc/test/Makefile
tools/qflight/Makefile
tools/qflight/include/Makefile
tools/qflight/source/Makefile

View file

@ -369,6 +369,12 @@ QF_DEPS(QFCC,
$(top_builddir)/libs/util/libQFutil.la],
[$(WIN32_LIBS)],
)
QF_DEPS(QFCC_TEST,
[],
[$(top_builddir)/libs/ruamoko/libQFruamoko.la
$(top_builddir)/libs/util/libQFutil.la],
[$(WIN32_LIBS)],
)
QF_DEPS(QFLIGHT,
[-I$(top_srcdir)/tools/qflight/include],
[$(top_builddir)/libs/util/libQFutil.la],

View file

@ -1770,7 +1770,7 @@ DOT_PATH =
# contain dot files that are included in the documentation (see the
# \dotfile command).
DOTFILE_DIRS =
DOTFILE_DIRS = @TOPSRC@/tools/qfcc/doc/dot
# The MSCFILE_DIRS tag can be used to specify one or more directories that
# contain msc files that are included in the documentation (see the

View file

@ -1,15 +1,15 @@
AUTOMAKE_OPTIONS = foreign
pkgincludedir = $(includedir)/QF
nobase_pkginclude_HEADERS = \
bspfile.h cbuf.h cdaudio.h checksum.h clip_hull.h cmd.h \
alloc.h bspfile.h cbuf.h cdaudio.h checksum.h clip_hull.h cmd.h \
console.h crc.h csqc.h cvar.h dstring.h draw.h gib.h hash.h hl.h \
idparse.h image.h in_event.h info.h input.h iqm.h joystick.h keys.h \
link.h llist.h locs.h mathlib.h mdfour.h model.h modelgen.h msg.h \
object.h pak.h pakfile.h pcx.h png.h plugin.h pr_comp.h pr_debug.h \
pr_obj.h progs.h qargs.h qdefs.h qendian.h qfplist.h qtypes.h quakefs.h \
quakeio.h render.h riff.h ruamoko.h screen.h script.h sizebuf.h skin.h \
sound.h spritegn.h sys.h teamplay.h tga.h uint32.h va.h ver_check.h vid.h \
vrect.h view.h wad.h wadfile.h winding.h zone.h \
quakeio.h render.h riff.h ruamoko.h set.h screen.h script.h sizebuf.h \
skin.h sound.h spritegn.h sys.h teamplay.h tga.h uint32.h va.h \
ver_check.h vid.h vrect.h view.h wad.h wadfile.h winding.h zone.h \
\
GL/ati.h GL/defines.h GL/extensions.h GL/funcs.h GL/qf_draw.h \
GL/qf_explosions.h GL/qf_funcs_list.h GL/qf_iqm.h GL/qf_lightmap.h \

97
include/QF/alloc.h Normal file
View file

@ -0,0 +1,97 @@
/*
alloc.h
High-tide allocator.
Copyright (C) 2012 Bill Currie <bill@taniwha.org>
Author: Bill Currie <bill@taniwha.org>
Date: 2012/12/06
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to:
Free Software Foundation, Inc.
59 Temple Place - Suite 330
Boston, MA 02111-1307, USA
*/
#ifndef __QF_alloc_h
#define __QF_alloc_h
#include <stdlib.h>
#include <string.h>
/** \defgroup alloc High-tide allocator.
\ingroup utils
*/
//@{
#ifndef DEBUG_QF_MEMORY
/** High-tide structure allocator for use in linked lists.
Using a free-list with the name of \c free_NAME, return a single element.
The type of the element must be a structure with a field named \c next.
When the free-list is empty, memory is claimed from the system in blocks.
elements may be returned to the pool by linking them into the free-list.
\param s The number of structures in the block.
\param t The structure type.
\param n The \c NAME portion of the \c free_NAME free-list.
\param v The destination of the pointer to the allocated
element. The contents of the allocated element will be
memset to 0.
\hideinitializer
*/
#define ALLOC(s, t, n, v) \
do { \
if (!free_##n) { \
int i; \
free_##n = malloc ((s) * sizeof (t)); \
for (i = 0; i < (s) - 1; i++) \
free_##n[i].next = &free_##n[i + 1];\
free_##n[i].next = 0; \
} \
v = free_##n; \
free_##n = free_##n->next; \
memset (v, 0, sizeof (*v)); \
} while (0)
/** Free a block allocated by #ALLOC
\param n The \c NAME portion of the \c free_NAME free-list.
\param p The pointer to the block to be freed.
\hideinitializer
*/
#define FREE(n, p) \
do { \
p->next = free_##n; \
free_##n = p->next; \
} while (0)
#else
#define ALLOC(s, t, n, v) \
do { \
__attribute__((unused)) t **dummy = &free_##n; \
v = (t *) calloc (1, sizeof (t)); \
} while (0)
#define FREE(n, p) do { free (p); } while (0)
#endif
//@}
#endif//__QF_alloc_h

View file

@ -147,6 +147,7 @@ typedef int pr_load_func_t (progs_t *pr);
\param size bytes of \p file to read
\param max_edicts \e number of entities to allocate space for
\param zone minimum size of dynamic memory to allocate space for
dynamic memory (bytes).
\note \e All runtime strings (permanent or temporary) are allocated from
the VM's dynamic memory space, so be sure \p zone is of sufficient size.

331
include/QF/set.h Normal file
View file

@ -0,0 +1,331 @@
/*
set.h
Set manipulation.
Copyright (C) 2012 Bill Currie <bill@taniwha.org>
Author: Bill Currie <bill@taniwha.org>
Date: 2012/8/4
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to:
Free Software Foundation, Inc.
59 Temple Place - Suite 330
Boston, MA 02111-1307, USA
*/
#ifndef __QF_set_h
#define __QF_set_h
/** \defgroup set Set handling
\ingroup utils
*/
//@{
#define DEFMAP_SIZE ((32 - sizeof (struct set_s *) \
- sizeof (unsigned *) \
- sizeof (int) - sizeof (unsigned))\
/ sizeof (unsigned))
/** Represent a set using a bitmap.
When \a inverted is zero, ones in the bitmap represent members, but when
\a inverted is non-zero, zeros in the bitmap represent members. However,
this really is all private implementation details and it is best to treat
set_t as a black box.
*/
typedef struct set_s {
struct set_s *next; ///< private. for ALLOC
unsigned *map; ///< bitmap of set members
int inverted; ///< if true, 0 indicates membership
unsigned size; ///< number of representable members
unsigned defmap[DEFMAP_SIZE];///< backing store for small sets
} set_t;
/** Represent the state of a scan through a set.
Very useful in for-loops:
\code
set_t *set;
set_iter_t *iter;
create_and_populate (set);
for (iter = set_first (set); iter; iter = set_next (iter))
do_something (iter->value);
\endcode
*/
typedef struct set_iter_s {
struct set_iter_s *next; ///< private. for ALLOC
const set_t *set; ///< the set to which this iterator belongs
/** The result of set_first() or set_next(). set_next() will start at the
following value.
\note For inverted sets, indicates a non-member.
*/
unsigned value;
} set_iter_t;
/** Delete a set iterator that is no longer needed.
\param set_iter The set iterator to be deleted.
*/
void set_del_iter (set_iter_t *set_iter);
/** Create a new set.
The set is initialized to be the empty set.
\return The newly created, empty set.
*/
set_t *set_new (void);
/** Delete a set that is no longer needed.
\param set The set to be deleted.
*/
void set_delete (set_t *set);
/** Add a value to a set.
It is not an error to add a value that is already a member of the set.
\note \a set is modified.
\param set The set to which the value will be added.
\param x The value to be added.
\return The modified set.
*/
set_t *set_add (set_t *set, unsigned x);
/** Remove a value from a set.
It is not an error to remove a value that is not a member of the set.
\note \a set is modified.
\param set The set from which the value will be removed.
\param x The value to be removed.
\return The modified set.
*/
set_t *set_remove (set_t *set, unsigned x);
/** Compute the inverse of a set.
The computation is done as \a set = ~\a set.
\note \a set is modified.
\param set The set to be inverted.
\return The set modified to be ~\a src.
*/
set_t *set_invert (set_t *set);
/** Compute the union of two sets.
The computation is done as \a dst = \a dst | \a src.
\note \a dst is modified.
\param dst The destination set to be modified.
\param src The source set.
\return The destination set modified to be \a dst | \a src.
*/
set_t *set_union (set_t *dst, const set_t *src);
/** Compute the intersection of two sets.
The computation is done as \a dst = \a dst & \a src.
\note \a dst is modified.
\param dst The destination set to be modified.
\param src The source set.
\return The destination set modified to be \a dst & \a src.
*/
set_t *set_intersection (set_t *dst, const set_t *src);
/** Compute the diffedrence of two sets.
The computation is done as \a dst = \a dst - \a src.
\note \a dst is modified.
\param dst The destination set to be modified.
\param src The source set.
\return The destination set modified to be \a dst - \a src.
*/
set_t *set_difference (set_t *dst, const set_t *src);
/** Compute the diffedrence of two sets.
The computation is done as \a dst = \a src - \a dst.
\note \a dst is modified.
\param dst The destination set to be modified.
\param src The source set.
\return The destination set modified to be \a src - \a dst.
*/
set_t *set_reverse_difference (set_t *dst, const set_t *src);
/** Make a set equivalent to another.
\note \a dst is modified.
\param dst The destination set to make equivalent.
\param src The source set to assign to \a dst.
\return The modified destination set.
*/
set_t *set_assign (set_t *dst, const set_t *src);
/** Make a set the empty set.
\note \a set is modified.
\param set The set to make the empty set.
\return \a set.
*/
set_t *set_empty (set_t *set);
/** Make a set the set of everything.
\note \a set is modified.
\param set The set to make the set of everything.
\return \a set.
*/
set_t *set_everything (set_t *set);
/** Test if a set is the set of everything.
\param set The set to test.
\return 1 if \a set is empty (non-inverted).
*/
int set_is_empty (const set_t *set);
/** Test if a set is the set of everything.
\param set The set to test.
\return 1 if \a set is the set of everything (empty inverted set).
*/
int set_is_everything (const set_t *set);
/** Test if two sets are disjoint.
\param s1 The first set to test.
\param s2 The second set to test.
\return 1 if \a s2 is disjoint from \a s1, 0 if not.
\note The emtpy set is disjoint with itself.
*/
int set_is_disjoint (const set_t *s1, const set_t *s2);
/** Test if two sets intersect.
\param s1 The first set to test.
\param s2 The second set to test.
\return 1 if \a s2 intersects \a s1, 0 if not.
\note Equivalent non-empty sets are treated as intersecting.
*/
int set_is_intersecting (const set_t *s1, const set_t *s2);
/** Test if two sets are equivalent.
\param s1 The first set to test.
\param s2 The second set to test.
\return 1 if \a s2 is equivalent to \a s1, 0 if not.
*/
int set_is_equivalent (const set_t *s1, const set_t *s2);
/** Test if a set is a subset of another set.
An equivalent set is considered to be a subset.
\param set The potential super-set.
\param sub The potential subset.
\return 1 if \a sub is a subset of \a set, or if the sets are
equivalent.
*/
int set_is_subset (const set_t *set, const set_t *sub);
/** Test a value for membership in a set.
\param set The set to test.
\param x The value to test.
\return 1 if the value is a member of the set, otherwise 0.
*/
int set_is_member (const set_t *set, unsigned x);
/** Obtain the number of members (or non-members) of a set.
Normal sets return the number of members, inverted sets return the number
of non-members.
\param set The set from which the number of (non-)members will be
obtained.
\return The number of (non-)members. Both empty sets and sets of
evertything will return 0.
*/
unsigned set_size (const set_t *set);
/** Find the first "member" of the set.
\warning For normal sets, the set iterator represents a member of the
set, but for inverted sets, the set iterator represetns a
<em>non</em>-member of the set.
\param set The set to scan.
\return A pointer to a set iterator indicating the first
(non-)member of the set, or null if the set is empty or
of everything.
*/
set_iter_t *set_first (const set_t *set);
/** Find the next "member" of the set.
\warning For normal sets, the set iterator represents a member of the
set, but for inverted sets, the set iterator represetns a
<em>non</em>-member of the set.
\param set_iter The set iterator representing the state of the current
scan.
\return A pointer to a set iterator indicating the next
(non-)member of the set, or null if no mor (non-)members
are available.
\note The set iterator is automatically deleted when the end of the set
is reached.
*/
set_iter_t *set_next (set_iter_t *set_iter);
/** Return a human-readable string representing the set.
Empty sets will be represented by the string "[empty]". Sets of everything
will be represented by the string "[everything]". Inverted sets will have
the first implicit member followed by "..." (eg, "256 ...").
\param set The set to be converted to a string.
\return The human readable representation of the string.
\warning The string is kept in a static variable, so subsequent calls
will overwrite the results of preceeding calls.
*/
const char *set_as_string (const set_t *set);
//@}
#endif//__QF_set_h

View file

@ -103,7 +103,7 @@ void Z_Print (memzone_t *zone);
void Z_CheckHeap (memzone_t *zone);
void Z_SetError (memzone_t *zone, void (*err) (void *data, const char *msg),
void *data);
void Z_CheckPointer (const memzone_t *zone, const void *ptr, int size);
void *Hunk_Alloc (int size); // returns 0 filled memory
void *Hunk_AllocName (int size, const char *name);

View file

@ -261,6 +261,11 @@ PR_BoundsCheckSize (progs_t *pr, int addr, unsigned size)
|| size > (unsigned) (pr->globals_size - addr))
PR_RunError (pr, "invalid memory access: %d (0 to %d-%d)", addr,
pr->globals_size, size);
if (pr_boundscheck->int_val >= 2
&& PR_GetPointer (pr, addr + size) > (pr_type_t *) pr->zone) {
void *mem = (void *) PR_GetPointer (pr, addr);
Z_CheckPointer (pr->zone, mem, size * sizeof (pr_type_t));
}
}
VISIBLE void

View file

@ -847,7 +847,7 @@ VISIBLE opcode_t pr_opcodes[] = {
"%Ga",
},
{"<JUMPB>", "jumpb", OP_JUMPB, false,
ev_integer, ev_integer, ev_invalid,
ev_void, ev_integer, ev_invalid,
PROG_VERSION,
"%Ga[%Gb]",
},

View file

@ -52,8 +52,8 @@ libQFutil_la_SOURCES= \
bspfile.c buildnum.c cbuf.c checksum.c cmd.c crc.c cvar.c dstring.c \
fendian.c hash.c idparse.c info.c link.c llist.c \
mathlib.c mdfour.c msg.c pakfile.c plugin.c qargs.c qendian.c \
qfplist.c quakefs.c quakeio.c riff.c script.c sizebuf.c string.c sys.c \
va.c ver_check.c vrect.c wad.c wadfile.c zone.c $(dirent) $(fnmatch) \
$(getopt)
qfplist.c quakefs.c quakeio.c riff.c script.c set.c sizebuf.c string.c \
sys.c va.c ver_check.c vrect.c wad.c wadfile.c zone.c \
$(dirent) $(fnmatch) $(getopt)
EXTRA_DIST= $(fnmatch_src) $(getopt_src)

588
libs/util/set.c Normal file
View file

@ -0,0 +1,588 @@
/*
set.c
Set manipulation.
Copyright (C) 2012 Bill Currie <bill@taniwha.org>
Author: Bill Currie <bill@taniwha.org>
Date: 2012/8/4
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to:
Free Software Foundation, Inc.
59 Temple Place - Suite 330
Boston, MA 02111-1307, USA
*/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#ifdef HAVE_STRING_H
# include <string.h>
#endif
#ifdef HAVE_STRINGS_H
# include <strings.h>
#endif
#include <stdlib.h>
#include "QF/alloc.h"
#include "QF/dstring.h"
#include "QF/mathlib.h"
#include "QF/set.h"
#define BITS (sizeof (((set_t *) 0)->map[0]) * 8)
set_t *free_sets;
set_iter_t *free_set_iters;
static set_iter_t *
new_setiter (void)
{
set_iter_t *set_iter;
ALLOC (16, set_iter_t, set_iters, set_iter);
return set_iter;
}
static void
delete_setiter (set_iter_t *set_iter)
{
FREE (set_iters, set_iter);
}
void
set_del_iter (set_iter_t *set_iter)
{
delete_setiter (set_iter);
}
set_t *
set_new (void)
{
set_t *set;
ALLOC (16, set_t, sets, set);
set->size = sizeof (set->defmap) * 8;
set->map = set->defmap;
return set;
}
void
set_delete (set_t *set)
{
if (set->map != set->defmap)
free (set->map);
FREE (sets, set);
}
static void
set_expand (set_t *set, unsigned x)
{
unsigned *map = set->map;
size_t size;
if (x <= set->size)
return;
size = (x + BITS) & ~(BITS - 1);
set->map = malloc (size / 8);
memcpy (set->map, map, set->size / 8);
memset (set->map + set->size / BITS, 0, (size - set->size) / 8);
set->size = size;
if (map != set->defmap)
free (map);
}
static inline void
_set_add (set_t *set, unsigned x)
{
if (x >= set->size)
set_expand (set, x + 1);
set->map[x / BITS] |= 1 << (x % BITS);
}
static inline void
_set_remove (set_t *set, unsigned x)
{
if (x >= set->size)
return;
set->map[x / BITS] &= ~(1 << (x % BITS));
}
set_t *
set_add (set_t *set, unsigned x)
{
if (set->inverted)
_set_remove (set, x);
else
_set_add (set, x);
return set;
}
set_t *
set_remove (set_t *set, unsigned x)
{
if (set->inverted)
_set_add (set, x);
else
_set_remove (set, x);
return set;
}
set_t *
set_invert (set_t *set)
{
set->inverted = !set->inverted;
return set;
}
static set_t *
_set_union (set_t *dst, const set_t *src)
{
unsigned size;
unsigned i;
size = max (dst->size, src->size);
set_expand (dst, size);
for (i = 0; i < src->size / BITS; i++)
dst->map[i] |= src->map[i];
return dst;
}
static set_t *
_set_intersection (set_t *dst, const set_t *src)
{
unsigned size;
unsigned i;
size = max (dst->size, src->size);
set_expand (dst, size);
for (i = 0; i < src->size / BITS; i++)
dst->map[i] &= src->map[i];
for ( ; i < dst->size / BITS; i++)
dst->map[i] = 0;
return dst;
}
static set_t *
_set_difference (set_t *dst, const set_t *src)
{
unsigned size;
unsigned i;
size = max (dst->size, src->size);
set_expand (dst, size);
for (i = 0; i < src->size / BITS; i++)
dst->map[i] &= ~src->map[i];
return dst;
}
static set_t *
_set_reverse_difference (set_t *dst, const set_t *src)
{
unsigned size;
unsigned i;
size = max (dst->size, src->size);
set_expand (dst, size);
for (i = 0; i < src->size / BITS; i++)
dst->map[i] = ~dst->map[i] & src->map[i];
return dst;
}
set_t *
set_union (set_t *dst, const set_t *src)
{
if (dst->inverted && src->inverted) {
return _set_intersection (dst, src);
} else if (src->inverted) {
dst->inverted = 1;
return _set_difference (dst, src);
} else if (dst->inverted) {
return _set_reverse_difference (dst, src);
} else {
return _set_union (dst, src);
}
}
set_t *
set_intersection (set_t *dst, const set_t *src)
{
if (dst->inverted && src->inverted) {
return _set_union (dst, src);
} else if (src->inverted) {
return _set_difference (dst, src);
} else if (dst->inverted) {
dst->inverted = 0;
return _set_reverse_difference (dst, src);
} else {
return _set_intersection (dst, src);
}
}
set_t *
set_difference (set_t *dst, const set_t *src)
{
if (dst->inverted && src->inverted) {
dst->inverted = 0;
return _set_reverse_difference (dst, src);
} else if (src->inverted) {
return _set_intersection (dst, src);
} else if (dst->inverted) {
return _set_union (dst, src);
} else {
return _set_difference (dst, src);
}
}
set_t *
set_reverse_difference (set_t *dst, const set_t *src)
{
if (dst->inverted && src->inverted) {
dst->inverted = 0;
return _set_difference (dst, src);
} else if (src->inverted) {
dst->inverted = 1;
return _set_union (dst, src);
} else if (dst->inverted) {
dst->inverted = 0;
return _set_intersection (dst, src);
} else {
return _set_reverse_difference (dst, src);
}
}
set_t *
set_assign (set_t *dst, const set_t *src)
{
unsigned size;
unsigned i;
size = max (dst->size, src->size);
set_expand (dst, size);
dst->inverted = src->inverted;
for (i = 0; i < src->size / BITS; i++)
dst->map[i] = src->map[i];
for ( ; i < dst->size / BITS; i++)
dst->map[i] = 0;
return dst;
}
set_t *
set_empty (set_t *set)
{
unsigned i;
set->inverted = 0;
for (i = 0; i < set->size / BITS; i++)
set->map[i] = 0;
return set;
}
set_t *
set_everything (set_t *set)
{
unsigned i;
set->inverted = 1;
for (i = 0; i < set->size / BITS; i++)
set->map[i] = 0;
return set;
}
static inline int
_set_is_empty (const set_t *set)
{
unsigned i;
for (i = 0; i < set->size / BITS; i++)
if (set->map[i])
return 0;
return 1;
}
int
set_is_empty (const set_t *set)
{
if (set->inverted)
return 0;
return _set_is_empty (set);
}
int
set_is_everything (const set_t *set)
{
if (!set->inverted)
return 0;
return _set_is_empty (set);
}
static int
set_test_n_n (const set_t *s1, const set_t *s2)
{
unsigned i, end;
unsigned intersection = 0;
unsigned difference = 0;
end = min (s1->size, s2->size) / BITS;
for (i = 0; i < end; i++) {
unsigned m1 = s1->map[i];
unsigned m2 = s2->map[i];
intersection |= m1 & m2;
difference |= m1 ^ m2;
}
for ( ; i < s1->size / BITS; i++) {
difference |= s1->map[i];
}
for ( ; i < s2->size / BITS; i++) {
difference |= s2->map[i];
}
return (difference != 0) | ((intersection != 0) << 1);
}
static int
set_test_n_i (const set_t *s1, const set_t *s2)
{
unsigned i, end;
unsigned intersection = 0;
unsigned difference = 0;
end = min (s1->size, s2->size) / BITS;
for (i = 0; i < end; i++) {
unsigned m1 = s1->map[i];
unsigned m2 = ~s2->map[i];
intersection |= m1 & m2;
difference |= m1 ^ m2;
}
for ( ; i < s1->size / BITS; i++) {
intersection |= s1->map[i];
difference |= ~s1->map[i];
}
for ( ; i < s2->size / BITS; i++) {
difference |= ~s2->map[i];
}
return (difference != 0) | ((intersection != 0) << 1);
}
static int
set_test_i_n (const set_t *s1, const set_t *s2)
{
unsigned i, end;
unsigned intersection = 0;
unsigned difference = 0;
end = min (s1->size, s2->size) / BITS;
for (i = 0; i < end; i++) {
unsigned m1 = ~s1->map[i];
unsigned m2 = s2->map[i];
intersection |= m1 & m2;
difference |= m1 ^ m2;
}
for ( ; i < s1->size / BITS; i++) {
difference |= ~s1->map[i];
}
for ( ; i < s2->size / BITS; i++) {
intersection |= s2->map[i];
difference |= ~s2->map[i];
}
return (difference != 0) | ((intersection != 0) << 1);
}
static int
set_test_i_i (const set_t *s1, const set_t *s2)
{
unsigned i, end;
unsigned intersection = 0;
unsigned difference = 0;
end = min (s1->size, s2->size) / BITS;
for (i = 0; i < end; i++) {
unsigned m1 = ~s1->map[i];
unsigned m2 = ~s2->map[i];
intersection |= m1 & m2;
difference |= m1 ^ m2;
}
for ( ; i < s1->size / BITS; i++) {
difference |= ~s1->map[i];
}
for ( ; i < s2->size / BITS; i++) {
intersection |= s2->map[i];
difference |= ~s2->map[i];
}
intersection |= ~0; // two inverted sets can never be disjoint
return (difference != 0) | ((intersection != 0) << 1);
}
static int
set_test (const set_t *s1, const set_t *s2)
{
if (s1->inverted && s2->inverted)
return set_test_i_i (s1, s2);
else if (s2->inverted)
return set_test_n_i (s1, s2);
else if (s1->inverted)
return set_test_i_n (s1, s2);
else
return set_test_n_n (s1, s2);
}
int
set_is_disjoint (const set_t *s1, const set_t *s2)
{
return !(set_test (s1, s2) & 2);
}
int
set_is_intersecting (const set_t *s1, const set_t *s2)
{
return !!(set_test (s1, s2) & 2);
}
int
set_is_equivalent (const set_t *s1, const set_t *s2)
{
return !(set_test (s1, s2) & 1);
}
int
set_is_subset (const set_t *set, const set_t *sub)
{
unsigned i, end;
end = min (set->size, sub->size) / BITS;
if (set->inverted && sub->inverted) {
for (i = 0; i < end; i++) {
if (~sub->map[i] & set->map[i])
return 0;
}
for ( ; i < set->size / BITS; i++)
if (set->map[i])
return 0;
} else if (set->inverted) {
for (i = 0; i < end; i++) {
if (sub->map[i] & set->map[i])
return 0;
}
} else if (sub->inverted) {
// an inverted set cannot be a subset of a set that is not inverted
return 0;
} else {
for (i = 0; i < end; i++) {
if (sub->map[i] & ~set->map[i])
return 0;
}
for ( ; i < sub->size / BITS; i++)
if (sub->map[i])
return 0;
}
return 1;
}
static inline int
_set_is_member (const set_t *set, unsigned x)
{
if (x >= set->size)
return 0;
return (set->map[x / BITS] & (1 << (x % BITS))) != 0;
}
int
set_is_member (const set_t *set, unsigned x)
{
if (set->inverted)
return !_set_is_member (set, x);
return _set_is_member (set, x);
}
unsigned
set_size (const set_t *set)
{
unsigned count = 0;
unsigned i;
for (i = 0; i < set->size; i++)
if (_set_is_member (set, i))
count++;
return count;
}
set_iter_t *
set_first (const set_t *set)
{
unsigned x;
set_iter_t *set_iter;
for (x = 0; x < set->size; x++) {
if (_set_is_member (set, x)) {
set_iter = new_setiter ();
set_iter->set = set;
set_iter->value = x;
return set_iter;
}
}
return 0;
}
set_iter_t *
set_next (set_iter_t *set_iter)
{
unsigned x;
for (x = set_iter->value + 1; x < set_iter->set->size; x++) {
if (_set_is_member (set_iter->set, x)) {
set_iter->value = x;
return set_iter;
}
}
delete_setiter (set_iter);
return 0;
}
const char *
set_as_string (const set_t *set)
{
static dstring_t *str;
unsigned i;
if (!str)
str = dstring_new ();
if (set_is_empty (set)) {
dstring_copystr (str, "{}");
return str->str;
}
if (set_is_everything (set)) {
dstring_copystr (str, "{...}");
return str->str;
}
dstring_copystr (str, "{");
for (i = 0; i < set->size; i++) {
if (set_is_member (set, i)) {
if (str->str[1])
dasprintf (str, " %d", i);
else
dasprintf (str, "%d", i);
}
}
if (set->inverted)
dasprintf (str, "%s%d ...", str->str[1] ? " " : "", i);
dstring_appendstr (str, "}");
return str->str;
}

View file

@ -3,7 +3,8 @@ AUTOMAKE_OPTIONS= foreign
INCLUDES= -I$(top_srcdir)/include
check_PROGRAMS= \
test-dq test-half test-mat3 test-mat4 test-qfs test-quat test-vrect
test-dq test-half test-mat3 test-mat4 test-qfs test-quat test-set \
test-vrect
test_dq_SOURCES=test-dq.c
test_dq_LDADD=$(top_builddir)/libs/util/libQFutil.la
@ -29,6 +30,10 @@ test_quat_SOURCES=test-quat.c
test_quat_LDADD=$(top_builddir)/libs/util/libQFutil.la
test_quat_DEPENDENCIES=$(top_builddir)/libs/util/libQFutil.la
test_set_SOURCES=test-set.c
test_set_LDADD=$(top_builddir)/libs/util/libQFutil.la
test_set_DEPENDENCIES=$(top_builddir)/libs/util/libQFutil.la
test_vrect_SOURCES=test-vrect.c
test_vrect_LDADD=$(top_builddir)/libs/util/libQFutil.la
test_vrect_DEPENDENCIES=$(top_builddir)/libs/util/libQFutil.la

233
libs/util/test/test-set.c Normal file
View file

@ -0,0 +1,233 @@
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include <stdio.h>
#include <string.h>
#include "QF/set.h"
#define SIZE (DEFMAP_SIZE * sizeof (unsigned) * 8)
#define BITS (sizeof (((set_t *) 0)->map[0]) * 8)
typedef set_t *(*setup_func) (void);
typedef set_t *(*op_func) (set_t *s1, const set_t *s2);
typedef int (*test_func) (const set_t *s1, const set_t *s2);
static set_t *
make_empty (void)
{
set_t *set = set_new ();
return set;
}
static set_t *
make_empty2 (void)
{
set_t *set = set_new ();
return set_empty (set);
}
static set_t *
make_everything (void)
{
set_t *set = set_new ();
return set_everything (set);
}
static set_t *
make_empty_invert (void)
{
set_t *set = set_new ();
return set_invert (set);
}
static set_t *
make_everything_invert (void)
{
set_t *set = set_new ();
set_everything (set);
return set_invert (set);
}
static set_t *
make_SIZE (void)
{
set_t *set = set_new ();
set_add (set, SIZE);
return set;
}
static set_t *
make_not_SIZE (void)
{
set_t *set = set_new ();
set_add (set, SIZE);
return set_invert (set);
}
static set_t *
make_0_to_SIZEm1 (void)
{
set_t *set = set_new ();
size_t i;
for (i = 0; i < SIZE; i++)
set_add (set, i);
return set;
}
static int
check_size (const set_t *set, const set_t *unused)
{
return set->size;
}
static set_t *
make_5 (void)
{
set_t *set = set_new ();
return set_add (set, 5);
}
static set_t *
make_55 (void)
{
set_t *set = set_new ();
return set_add (set, 55);
}
static set_t *
make_not_5 (void)
{
return set_invert (make_5 ());
}
static set_t *
make_not_55 (void)
{
return set_invert (make_55 ());
}
struct {
setup_func set1;
setup_func set2;
op_func op;
test_func test;
int test_expect;
const char *str_expect;
} tests[] = {
{make_empty, 0, 0, check_size, SIZE, "{}"},
{make_empty2, 0, 0, check_size, SIZE, "{}"},
{make_everything, 0, 0, check_size, SIZE, "{...}"},
{make_empty_invert, 0, 0, check_size, SIZE, "{...}"},
{make_everything_invert, 0, 0, check_size, SIZE, "{}"},
{make_SIZE, 0, 0, check_size, SIZE + BITS, "{64}"},
{make_0_to_SIZEm1, 0, 0, check_size, SIZE,
"{0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15"
" 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31"
" 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47"
" 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63}"
},
{make_everything, make_0_to_SIZEm1, set_difference, check_size,
SIZE, "{64 ...}"
},
{make_0_to_SIZEm1, make_everything, set_reverse_difference,
check_size, SIZE, "{64 ...}"
},
{make_everything, make_empty, 0, set_is_subset, 1, 0},
{make_everything, make_empty, 0, set_is_equivalent, 0, 0},
{make_everything, make_empty, 0, set_is_intersecting, 0, 0},
{make_everything, make_empty, 0, set_is_disjoint, 1, 0},
{make_empty, make_everything, 0, set_is_subset, 0, 0},
{make_empty, make_everything, 0, set_is_equivalent, 0, 0},
{make_empty, make_everything, 0, set_is_intersecting, 0, 0},
{make_empty, make_everything, 0, set_is_disjoint, 1, 0},
{make_everything, make_everything, 0, set_is_subset, 1, 0},
{make_everything, make_everything, 0, set_is_equivalent, 1, 0},
{make_everything, make_everything, 0, set_is_intersecting, 1, 0},
{make_everything, make_everything, 0, set_is_disjoint, 0, 0},
{make_empty, make_empty, 0, set_is_subset, 1, 0},
{make_empty, make_empty, 0, set_is_equivalent, 1, 0},
{make_empty, make_empty, 0, set_is_intersecting, 0, 0},
{make_empty, make_empty, 0, set_is_disjoint, 1, 0},
{make_5, make_5, 0, set_is_equivalent, 1, 0},
{make_5, make_5, 0, set_is_intersecting, 1, 0},
{make_5, make_5, 0, set_is_disjoint, 0, 0},
{make_5, make_55, 0, set_is_equivalent, 0, 0},
{make_5, make_55, 0, set_is_intersecting, 0, 0},
{make_5, make_55, 0, set_is_disjoint, 1, 0},
{make_not_5, make_55, 0, set_is_equivalent, 0, 0},
{make_not_5, make_55, 0, set_is_intersecting, 1, 0},
{make_not_5, make_55, 0, set_is_disjoint, 0, 0},
{make_5, make_not_55, 0, set_is_equivalent, 0, 0},
{make_5, make_not_55, 0, set_is_intersecting, 1, 0},
{make_5, make_not_55, 0, set_is_disjoint, 0, 0},
{make_not_5, make_not_55, 0, set_is_equivalent, 0, 0},
{make_not_5, make_not_55, 0, set_is_intersecting, 1, 0},
{make_not_5, make_not_55, 0, set_is_disjoint, 0, 0},
{make_5, make_55, set_union, set_is_equivalent, 0, "{5 55}"},
{make_5, make_55, set_union, set_is_intersecting, 1, "{5 55}"},
{make_5, make_55, set_union, set_is_disjoint, 0, "{5 55}"},
{make_55, make_5, set_union, set_is_equivalent, 0, "{5 55}"},
{make_55, make_5, set_union, set_is_intersecting, 1, "{5 55}"},
{make_55, make_5, set_union, set_is_disjoint, 0, "{5 55}"},
{make_not_SIZE, make_everything, 0, set_is_equivalent, 0, 0},
{make_not_SIZE, make_everything, 0, set_is_intersecting, 1, 0},
{make_not_SIZE, make_everything, 0, set_is_disjoint, 0, 0},
{make_SIZE, make_everything, 0, set_is_equivalent, 0, 0},
{make_SIZE, make_everything, 0, set_is_intersecting, 1, 0},
{make_SIZE, make_everything, 0, set_is_disjoint, 0, 0},
{make_not_5, make_everything, 0, set_is_equivalent, 0, 0},
{make_not_5, make_everything, 0, set_is_intersecting, 1, 0},
{make_not_5, make_everything, 0, set_is_disjoint, 0, 0},
{make_5, make_everything, 0, set_is_equivalent, 0, 0},
{make_5, make_everything, 0, set_is_intersecting, 1, 0},
{make_5, make_everything, 0, set_is_disjoint, 0, 0},
};
#define num_tests (sizeof (tests) / sizeof (tests[0]))
int
main (int argc, const char **argv)
{
size_t i;
int res = 0;
for (i = 0; i < num_tests; i++) {
set_t *s1, *s2 = 0;
const char *set_str;
s1 = tests[i].set1 ();
if (tests[i].set2)
s2 = tests[i].set2 ();
if (tests[i].op) {
if (tests[i].op (s1, s2) != s1) {
res |= 1;
printf ("test op %d didn't return s1\n", (int) i);
continue;
}
}
if (tests[i].test) {
int test_res;
test_res = tests[i].test (s1, s2);
if (test_res != tests[i].test_expect) {
res |= 1;
printf ("test %d failed\n", (int) i);
printf ("expect: %d\n", tests[i].test_expect);
printf ("got : %d\n", test_res);
continue;
}
}
set_str = set_as_string (s1);
if (tests[i].str_expect && strcmp (set_str, tests[i].str_expect)) {
res |= 1;
printf ("test %d failed\n", (int) i);
printf ("expect: %s\n", tests[i].str_expect);
printf ("got : %s\n", set_str);
continue;
}
set_delete (s1);
if (s2)
set_delete (s2);
}
return res;
}

View file

@ -71,11 +71,12 @@ static qboolean Cache_FreeLRU (void);
typedef struct memblock_s
{
int size; // including the header and possibly tiny fragments
int block_size; // including the header and possibly tiny fragments
int tag; // a tag of 0 is a free block
struct memblock_s *next, *prev;
int size; // requested size
int id; // should be ZONEID
int id2; // pad to 64 bit boundary
//int id2; // pad to 64 bit boundary
} memblock_t;
struct memzone_s
@ -94,7 +95,7 @@ struct memzone_s
static int
z_block_size (memblock_t *block)
{
return block->size - sizeof (memblock_t) - 4;
return block->block_size - sizeof (memblock_t) - 4;
}
static int
@ -117,6 +118,7 @@ Z_ClearZone (memzone_t *zone, int size, int zone_offset, int ele_size)
zone->blocklist.prev = block;
zone->blocklist.tag = 1; // in use block
zone->blocklist.id = 0;
zone->blocklist.block_size = 0;
zone->blocklist.size = 0;
zone->offset = zone_offset;
zone->ele_size = ele_size;
@ -129,8 +131,9 @@ Z_ClearZone (memzone_t *zone, int size, int zone_offset, int ele_size)
block->prev = block->next = &zone->blocklist;
block->tag = 0; // free block
block->id = ZONEID;
block->id2 = ZONEID;
block->size = size - sizeof (memzone_t);
//block->id2 = ZONEID;
block->block_size = size - sizeof (memzone_t);
block->size = 0;
}
VISIBLE void
@ -154,7 +157,7 @@ Z_Free (memzone_t *zone, void *ptr)
zone->error (zone->data, msg);
Sys_Error ("%s", msg);
}
if (block->id != ZONEID || block->id2 != ZONEID) {
if (block->id != ZONEID/* || block->id2 != ZONEID*/) {
const char *msg;
msg = nva ("bad pointer %x", z_offset (zone, block));
Sys_Printf ("%s\n", msg);
@ -171,12 +174,13 @@ Z_Free (memzone_t *zone, void *ptr)
}
block->tag = 0; // mark as free
zone->used -= block->size;
block->size = 0;
zone->used -= block->block_size;
other = block->prev;
if (!other->tag) {
// merge with previous free block
other->size += block->size;
other->block_size += block->block_size;
other->next = block->next;
other->next->prev = other;
if (block == zone->rover)
@ -187,7 +191,7 @@ Z_Free (memzone_t *zone, void *ptr)
other = block->next;
if (!other->tag) {
// merge the next free block onto the end
block->size += other->size;
block->block_size += other->block_size;
block->next = other->next;
block->next->prev = block;
if (other == zone->rover)
@ -218,8 +222,9 @@ Z_Malloc (memzone_t *zone, int size)
void *
Z_TagMalloc (memzone_t *zone, int size, int tag)
{
int extra;
memblock_t *start, *rover, *new, *base;
int extra;
int requested_size = size;
memblock_t *start, *rover, *new, *base;
if (!tag) {
if (zone->error)
@ -243,35 +248,36 @@ Z_TagMalloc (memzone_t *zone, int size, int tag)
base = rover = rover->next;
else
rover = rover->next;
} while (base->tag || base->size < size);
} while (base->tag || base->block_size < size);
// found a block big enough
extra = base->size - size;
extra = base->block_size - size;
if (extra > MINFRAGMENT) {
// there will be a free fragment after the allocated block
new = (memblock_t *) ((byte *) base + size);
new->size = extra;
new->block_size = extra;
new->tag = 0; // free block
new->prev = base;
new->id = ZONEID;
new->id2 = ZONEID;
//new->id2 = ZONEID;
new->next = base->next;
new->next->prev = new;
base->next = new;
base->size = size;
base->block_size = size;
}
base->tag = tag; // no longer a free block
base->size = requested_size;
zone->rover = base->next; // next allocation will start looking here
base->id = ZONEID;
base->id2 = ZONEID;
//base->id2 = ZONEID;
zone->used += base->size;
zone->used += base->block_size;
// marker for memory trash testing
*(int *) ((byte *) base + base->size - 4) = ZONEID;
*(int *) ((byte *) base + base->block_size - 4) = ZONEID;
return (void *) (base + 1);
}
@ -287,7 +293,7 @@ Z_Realloc (memzone_t *zone, void *ptr, int size)
return Z_Malloc (zone, size);
block = (memblock_t *) ((byte *) ptr - sizeof (memblock_t));
if (block->id != ZONEID || block->id2 != ZONEID) {
if (block->id != ZONEID/* || block->id2 != ZONEID*/) {
if (zone->error)
zone->error (zone->data,
"Z_Realloc: realloced a pointer without ZONEID");
@ -299,7 +305,7 @@ Z_Realloc (memzone_t *zone, void *ptr, int size)
Sys_Error ("Z_Realloc: realloced a freed pointer");
}
old_size = block->size;
old_size = block->block_size;
old_size -= sizeof (memblock_t); // account for size of block header
old_size -= 4; // space for memory trash tester
old_ptr = ptr;
@ -337,16 +343,16 @@ Z_Print (memzone_t *zone)
if (block->next == &zone->blocklist)
break; // all blocks have been hit
if (block->id != ZONEID || block->id2 != ZONEID)
if (block->id != ZONEID/* || block->id2 != ZONEID*/)
Sys_Printf ("ERROR: block ids incorrect\n");
if ( (byte *)block + block->size != (byte *)block->next)
if ((byte *) block + block->block_size != (byte *) block->next)
Sys_Printf ("ERROR: block size does not touch the next block\n");
if ( block->next->prev != block)
if (block->next->prev != block)
Sys_Printf ("ERROR: next block doesn't have proper back link\n");
if (!block->tag && !block->next->tag)
Sys_Printf ("ERROR: two consecutive free blocks\n");
if (block->tag
&& (*(int *) ((byte *) block + block->size - 4) != ZONEID))
&& (*(int *) ((byte *) block + block->block_size - 4) != ZONEID))
Sys_Printf ("ERROR: memory trashed in block\n");
fflush (stdout);
}
@ -360,10 +366,10 @@ Z_CheckHeap (memzone_t *zone)
for (block = zone->blocklist.next ; ; block = block->next) {
if (block->next == &zone->blocklist)
break; // all blocks have been hit
if ( (byte *)block + block->size != (byte *)block->next)
if ((byte *) block + block->block_size != (byte *) block->next)
Sys_Error ("Z_CheckHeap: block size does not touch the next "
"block\n");
if ( block->next->prev != block)
if (block->next->prev != block)
Sys_Error ("Z_CheckHeap: next block doesn't have proper back "
"link\n");
if (!block->tag && !block->next->tag)
@ -378,6 +384,29 @@ Z_SetError (memzone_t *zone, void (*err) (void *, const char *), void *data)
zone->data = data;
}
void
Z_CheckPointer (const memzone_t *zone, const void *ptr, int size)
{
const memblock_t *block;
const char *block_mem;
const char *check = (char *) ptr;
for (block = zone->blocklist.next ; ; block = block->next) {
if (block->next == &zone->blocklist)
break; // all blocks have been hit
if (check < (const char *) block
|| check >= (const char *) block + block->block_size)
continue;
// a block that overlaps with the memory region has been found
if (!block->tag)
zone->error (zone->data, "invalid access to unallocated memory");
block_mem = (char *) &block[1];
if (check < block_mem || check + size > block_mem + block->size)
zone->error (zone->data, "invalid access to allocated memory");
return; // access ok
}
}
//============================================================================
typedef struct {

View file

@ -5,7 +5,7 @@ pkgdatadir=@sharepath@/QF
QFCC_DEP=$(top_builddir)/tools/qfcc/source/qfcc$(EXEEXT)
QFCC=$(QFCC_DEP)
QCFLAGS=-qq -g -Wall -Werror -Wno-integer-divide --no-default-paths
QCFLAGS=-qq -O -g -Wall -Werror -Wno-integer-divide --no-default-paths
QCPPFLAGS=-I. -I$(srcdir) -I$(top_builddir)/ruamoko/include -I$(top_srcdir)/ruamoko/include -I$(top_builddir)/include -I$(top_srcdir)/include
GZIP=if echo $@ | grep -q .gz; then gzip -f `basename $@ .gz`; if test -f `basename $@ .dat.gz`.sym; then gzip -f `basename $@ .dat.gz`.sym; fi; fi
GZ=@progs_gz@

View file

@ -662,6 +662,10 @@ void menu_pre ()
void menu_post ()
{
[autorelease_pool release];
//FIXME called too often by the engine?
AutoreleasePool *ar;
ar = autorelease_pool;
autorelease_pool = nil;
[ar release];
}

View file

@ -49,6 +49,7 @@ class_from_plist (PLDictionary *pldict)
local SEL sel;
local PLItem *item;
ret = nil;
classname = [(PLString*) [pldict getObjectForKey:"Class"] string];
class = obj_lookup_class (classname);
if (!class) {
@ -97,6 +98,7 @@ array_from_plist (PLArray *plarray)
local int i, count;
local @param ret;
ret = nil;
array = [[Array alloc] init];
count = [plarray count];
for (i = 0; i < count; i++) {
@ -140,6 +142,7 @@ string_from_plist (PLString *plstring)
local @param ret;
local string str = [plstring string];
ret.quaternion_val = nil; //FIXME should be ret = nil;
if (str_mid (str, 0, 1) == "[")
return rect_from_plist (plstring);
@ -152,6 +155,7 @@ object_from_plist (PLItem *plist)
{
local @param ret;
ret = nil;
switch ([plist type]) {
case QFDictionary:
return class_from_plist ((PLDictionary *) plist);

View file

@ -7,7 +7,7 @@ pkgdatadir=@sharepath@/id1
QFCC_DEP=$(top_builddir)/tools/qfcc/source/qfcc$(EXEEXT)
QFCC=$(QFCC_DEP)
QCFLAGS=-qq -g -Werror -Wall -Wno-integer-divide --no-default-paths
QCFLAGS=-qq -O -g -Werror -Wall -Wno-integer-divide --no-default-paths
QCPPFLAGS=-I. -I$(srcdir) -I$(top_builddir)/ruamoko/include -I$(top_srcdir)/ruamoko/include
GZIP=if echo $@ | grep -q .gz; then gzip -f `basename $@ .gz`; if test -f `basename $@ .dat.gz`.sym; then gzip -f `basename $@ .dat.gz`.sym; fi; fi
GZ=@progs_gz@

View file

@ -3,7 +3,7 @@ AUTOMAKE_OPTIONS= foreign
pkglibdir=$(datarootdir)/qfcc/lib
QFCC=$(top_builddir)/tools/qfcc/source/qfcc$(EXEEXT)
QCFLAGS=-qq -g -Werror -Wall -Wno-integer-divide --no-default-paths
QCFLAGS=-qq -O -g -Werror -Wall -Wno-integer-divide --no-default-paths
QCPPFLAGS=$(INCLUDES)
PAK=$(top_builddir)/tools/pak/pak$(EXEEXT)
RANLIB=touch

View file

@ -22,7 +22,7 @@
do {
if (_objs[--i] == anObject) {
for (tmp = i; tmp < count; tmp++) {
for (tmp = i; tmp < count - 1; tmp++) {
_objs[tmp] = _objs[tmp + 1];
}
count--;

View file

@ -3,7 +3,7 @@ AUTOMAKE_OPTIONS= foreign
pkglibdir=$(datarootdir)/qfcc/lib
QFCC=$(top_builddir)/tools/qfcc/source/qfcc$(EXEEXT)
QCFLAGS=-qq -g -Wall -Wno-integer-divide -Werror --no-default-paths
QCFLAGS=-qq -O -g -Wall -Wno-integer-divide -Werror --no-default-paths
QCPPFLAGS=$(INCLUDES)
PAK=$(top_builddir)/tools/pak/pak$(EXEEXT)
RANLIB=touch

View file

@ -237,7 +237,7 @@ BOOL (id object) object_is_meta_class = #0;
- (id) performSelector: (SEL)aSelector
{
local IMP msg;
local IMP msg = nil; // FIXME teach qfcc about noreturn
if (!aSelector || !(msg = obj_msg_lookup (self, aSelector)))
[self error: "invalid selector passed to %s: %s",
@ -249,7 +249,7 @@ BOOL (id object) object_is_meta_class = #0;
- (id) performSelector: (SEL)aSelector withObject: (id)anObject
{
local IMP msg;
local IMP msg = nil; // FIXME teach qfcc about noreturn
if (!aSelector || !(msg = obj_msg_lookup (self, aSelector)))
[self error: "invalid selector passed to %s: %s",
@ -265,9 +265,11 @@ BOOL (id object) object_is_meta_class = #0;
{
local IMP msg;
if (!aSelector || !(msg = obj_msg_lookup (self, aSelector)))
if (!aSelector || !(msg = obj_msg_lookup (self, aSelector))) {
[self error: "invalid selector passed to %s",
sel_get_name (_cmd)];
return nil; //FIXME teach qfcc about noreturn
}
return msg (self, aSelector, anObject, anotherObject);
}
@ -292,10 +294,13 @@ BOOL (id object) object_is_meta_class = #0;
- (/*oneway*/ void) release
{
if ([self retainCount] == 1) // don't let retain count reach zero
int rc;
rc = obj_decrement_retaincount (self);
if (rc < 0)
obj_error (self, 0, "retain count went negative");
if (rc == 0)
[self dealloc];
else
obj_decrement_retaincount (self);
}
- (id) autorelease

View file

@ -4,7 +4,7 @@ pkglibdir=$(datarootdir)/qfcc/lib
QFCC_DEP=$(top_builddir)/tools/qfcc/source/qfcc$(EXEEXT)
QFCC=$(QFCC_DEP)
QCFLAGS=-qq -g -Werror -Wall -Wno-integer-divide --no-default-paths
QCFLAGS=-qq -O -g -Werror -Wall -Wno-integer-divide --no-default-paths
QCPPFLAGS=$(INCLUDES)
PAK=$(top_builddir)/tools/pak/pak$(EXEEXT)
GZIP=if echo $@ | grep -q .gz; then gzip -f `basename $@ .gz`; if test -f `basename $@ .dat.gz`.sym; then gzip -f `basename $@ .dat.gz`.sym; fi; fi

View file

@ -26,7 +26,7 @@
#
AUTOMAKE_OPTIONS= foreign
SUBDIRS= include source doc
SUBDIRS= include source doc test
dist-zip: distdir
-chmod -R a+r $(distdir)

View file

@ -5,17 +5,15 @@ M = more testing
I = in progress
W = waiting on other work
X C style pointer/array declarations (type system in general?)
X better error handling to continue parsing after a syntax error
X fix field defs
X fix object files
M arrays in structures
M unnamed function parameters for prototypes/typdefs etc.
o fix object files
o optimizations (esp CSE)
W fix used/uninitialized warnings
o optional arguments for functions
vector(vector fwd, optional vector up) vectoangles = #51;
I optimizations (esp CSE)
I fix used/uninitialized warnings
o fix enums in switch statements (use tables (treat as int), check missing)
o arrays in entities
o embedded nul characters in strings
o optional arguments for functions (alternative to overloading)
vector(vector fwd, optional vector up) vectoangles = #51;
? try to reduce memory consumption
? "":"" string concatenation (parser issues)
?? embedded nul characters in strings (why?)
?? "":"" string concatenation (parser issues) (why?)

View file

@ -2,4 +2,5 @@ AUTOMAKE_OPTIONS= foreign
SUBDIRS= man
EXTRA_DIST= expressions.txt
EXTRA_DIST= expressions.txt \
dot/vector-alias.dot

View file

@ -0,0 +1,25 @@
digraph vector_alias {
rankdir=LR;
subgraph types {
rank = same;
vector [fontsize=10,label="type\nvector"];
float [fontsize=10,label="type\nfloat"];
}
subgraph alias {
vec [fontsize=10,shape=record,label="<v>vec|<n>next|<t>type|<a>alias|<d>alias_defs|offset ?|..."];
vec_x [fontsize=10,shape=record,label="<v>vec.x|<n>next|<t>type|<a>alias|<d>alias_defs|offset 0|..."];
vec_y [fontsize=10,shape=record,label="<v>vec.y|<n>next|<t>type|<a>alias|<d>alias_defs|offset 1|..."];
vec_z [fontsize=10,shape=record,label= "<v>vec.z|<n>next|<t>type|<a>alias|<d>alias_defs|offset 2|..."];
}
vector -> float [style=invis];
vec:t -> vector [weight=2];
vec_x:t -> float [weight=2];
vec_y:t -> float [weight=2];
vec_z:t -> float [weight=2];
vec:d -> vec_z:v [weight=5];
vec_z:n -> vec_y:v [weight=15];
vec_y:n -> vec_x:v [weight=15];
vec:v -> vec_z:a [dir=back,weight=4];
vec:v -> vec_y:a [dir=back,weight=4];
vec:v -> vec_x:a [dir=back,weight=4];
}

View file

@ -1,7 +1,7 @@
AUTOMAKE_OPTIONS= foreign
EXTRA_DIST= class.h codespace.h cpp.h debug.h def.h defspace.h diagnostic.h \
emit.h expr.h function.h grab.h idstuff.h linker.h method.h \
obj_file.h obj_type.h opcodes.h options.h qfcc.h qfprogs.h reloc.h \
shared.h statements.h strpool.h struct.h switch.h symtab.h type.h \
value.h
EXTRA_DIST= class.h codespace.h cpp.h dags.h debug.h def.h defspace.h \
diagnostic.h dot.h emit.h expr.h flow.h function.h grab.h idstuff.h \
linker.h method.h obj_file.h obj_type.h opcodes.h options.h pragma.h \
qfcc.h qfprogs.h reloc.h shared.h statements.h strpool.h struct.h \
switch.h symtab.h type.h value.h

View file

@ -37,7 +37,7 @@ void parse_cpp_name (void);
void add_cpp_def (const char *arg);
void intermediate_file (struct dstring_s *ifile, const char *filename,
const char *ext, int local);
FILE * preprocess_file (const char *filename, const char *ext);
FILE *preprocess_file (const char *filename, const char *ext);
extern const char *cpp_name;
extern struct dstring_s *tempname;

112
tools/qfcc/include/dags.h Normal file
View file

@ -0,0 +1,112 @@
/*
dags.h
DAG representation of basic blocks
Copyright (C) 2012 Bill Currie <bill@taniwha.org>
Author: Bill Currie <bill@taniwha.org>
Date: 2012/05/08
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to:
Free Software Foundation, Inc.
59 Temple Place - Suite 330
Boston, MA 02111-1307, USA
*/
#ifndef dags_h
#define dags_h
/** \defgroup qfcc_dags DAG building
\ingroup qfcc
*/
//@{
#include "QF/pr_comp.h"
#include "statements.h"
struct dstring_s;
struct flownode_s;
typedef struct daglabel_s {
struct daglabel_s *next;
struct daglabel_s *daglabel_chain; ///< all labels created for a dag
int number; ///< index into array of labels in dag_t
unsigned live:1; ///< accessed via an alias
const char *opcode; ///< not if op
struct operand_s *op; ///< not if opcode;
struct dagnode_s *dagnode; ///< node with which this label is associated
struct expr_s *expr; ///< expression associated with this label
} daglabel_t;
typedef struct dagnode_s {
struct dagnode_s *next;
int number; ///< index into array of nodes in dag_t
int topo; ///< topological sort order
struct set_s *parents; ///< empty if root node
int cost; ///< cost of this node in temp vars
unsigned killed:1; ///< node is unavailable for cse
st_type_t type; ///< type of node (st_node = leaf)
daglabel_t *label; ///< ident/const if leaf node, or operator
etype_t tl;
struct operand_s *value; ///< operand holding the value of this node
/// \name child nodes
/// if \a children[0] is null, the rest must be null as well. Similar for
/// \a children[1].
///
/// \a edges is the set of all nodes upon which this node depends. ie,
/// they must be evaluated before this node is evaluted. So while nodes
/// in \a edges may not be true children of this node, they are effective
/// children in the DAG. That is, \a edges is for producing a correct
/// topological sort of the DAG.
//@{
struct dagnode_s *children[3];
etype_t types[3]; ///< desired type of each operand (to alias)
struct set_s *edges; ///< includes nodes pointed to by \a children
//@}
struct set_s *identifiers; ///< set of identifiers attached to this node
} dagnode_t;
typedef struct dag_s {
struct dag_s *next;
dagnode_t **nodes; ///< array of all dagnodes in this dag
int num_nodes;
int *topo; ///< nodes in topological sort order
daglabel_t **labels; ///< array of all daglabels in this dag
int num_labels;;
struct set_s *roots; ///< set of root nodes
struct flownode_s *flownode;///< flow node this dag represents
} dag_t;
const char *daglabel_string (daglabel_t *label);
void print_dag (struct dstring_s *dstr, dag_t *dag, const char *label);
void dot_dump_dag (void *_dag, const char *filename);
/** Make a dag for a single basic block.
\param flownode The flow graph node representing the basic block for which
the dag will be created. The node should have its live
variable information already computed.
\return The dag representing the basic block.
*/
dag_t *dag_create (struct flownode_s *flownode);
void dag_generate (dag_t *dag, sblock_t *block);
//@}
#endif//dags_h

View file

@ -33,12 +33,8 @@
#include "QF/pr_debug.h"
void push_source_file (void);
void pop_source_file (void);
void line_info (char *text);
pr_auxfunction_t *new_auxfunction (void);
pr_lineno_t *new_lineno (void);
struct ddef_s *new_local (void);
extern int lineno_base;

View file

@ -34,57 +34,279 @@
#include "QF/pr_comp.h"
#include "QF/pr_debug.h"
typedef struct def_s {
struct def_s *next; ///< general purpose linking
struct type_s *type;
const char *name;
struct defspace_s *space;
int offset;
struct def_s *alias;
struct reloc_s *relocs; ///< for relocations
unsigned offset_reloc:1; ///< use *_def_ofs relocs
unsigned initialized:1;
unsigned constant:1; ///< stores constant value
unsigned global:1; ///< globally declared def
unsigned external:1; ///< externally declared def
unsigned local:1; ///< function local def
unsigned system:1; ///< system def
unsigned nosave:1; ///< don't set DEF_SAVEGLOBAL
string_t file; ///< source file
int line; ///< source line
int qfo_def; ///< index to def in qfo defs
void *return_addr; ///< who allocated this
} def_t;
typedef enum storage_class_e {
st_global,
st_system,
st_extern,
st_static,
st_local
} storage_class_t;
extern storage_class_t current_storage;
def_t *new_def (const char *name, struct type_s *type,
struct defspace_s *space, storage_class_t storage);
def_t *alias_def (def_t *def, struct type_s *type);
def_t *temp_def (etype_t type, int size);
void free_def (def_t *def);
void def_to_ddef (def_t *def, ddef_t *ddef, int aux);
/** \defgroup qfcc_def Def handling
\ingroup qfcc
*/
//@{
struct symbol_s;
struct expr_s;
/** Represent a memory location that holds a QuakeC/Ruamoko object.
The object represented by the def may be of any type (either internally
defined by qfcc (float, int, vector, etc) or user defined (structs, arrays
etc)).
Unless the def is external (ie, declared to be in another compilation
unit), defs are always attached to a defspace. Storage for the def's
object is allocated from that space.
*/
typedef struct def_s {
struct def_s *next; ///< general purpose linking
struct def_s *temp_next; ///< linked list of "free" temp defs
struct type_s *type; ///< QC type of this def
const char *name; ///< the def's name
struct defspace_s *space; ///< defspace to which this def belongs
int offset; ///< address of this def in its defspace
/** \name Def aliasing.
Aliasing a def provides a different view of the def providing access
via a different type, or access to members of structs, unions and
arrays.
Alias defs are very simple: they store only the type and relative
offset off the def they alias. All other information is held in the
def they alias, including relocation records. However, they do keep
track of the source file and line that first created the alias.
The relations between a def an any of its aliases are maintained by
a linked list headed by def_t::alias_defs and connected by
def_t::next. def_t::alias is used to find the main def via one if its
aliases. The order of the aliases in the list is arbitrary: it is the
reverse of the order in which they were created.
\dotfile vector-alias.dot "Accessing a vector via its components."
*/
//@{
struct def_s *alias_defs; ///< defs that alias this def
struct def_s *alias; ///< real def which this def aliases
//@}
struct reloc_s *relocs; ///< for relocations
struct expr_s *initializer;///< initialer expression
struct daglabel_s *daglabel;///< daglabel for this def
struct flowvar_s *flowvar; ///< flowvar for this def
unsigned offset_reloc:1; ///< use *_def_ofs relocs
unsigned initialized:1; ///< the def has been initialized
unsigned constant:1; ///< stores constant value
unsigned global:1; ///< globally declared def
unsigned external:1; ///< externally declared def
unsigned local:1; ///< function local def
unsigned param:1; ///< function param def
unsigned system:1; ///< system def
unsigned nosave:1; ///< don't set DEF_SAVEGLOBAL
string_t file; ///< declaring/defining source file
int line; ///< declaring/defining source line
int qfo_def; ///< index to def in qfo defs
void *return_addr; ///< who allocated this
void *free_addr; ///< who freed this
} def_t;
/** Specify the storage class of a def.
*/
typedef enum storage_class_e {
sc_global, ///< def is globally visibil across units
sc_system, ///< def may be redefined once
sc_extern, ///< def is externally allocated
sc_static, ///< def is private to the current unit
sc_param, ///< def is an incoming function parameter
sc_local ///< def is local to the current function
} storage_class_t;
/** Create a new def.
Defs may be unnamed.
Untyped defs (used by type encodings) never allocate space for the def.
If \a storage is not sc_extern and \a type is not null, space of the
size specified by \a type will be allocated to the def from the defspace
specified by \a space.
\param name The name for the def. May be null for unnamed defs.
\param type The type for the def. May be null for untyped defs.
\param space The defspace to which the def belongs. Must not be null
for if \a storage is not sc_extern.
\param storage The storage class for the def.
\return The new def.
*/
def_t *new_def (const char *name, struct type_s *type,
struct defspace_s *space, storage_class_t storage);
/** Create a def that aliases another def.
Aliasing a def to the same type is useless, but not checked. Aliasing a
def to a type larger than the def's type will generate an internal error.
If the offset is negative, or the offset plus the size of the aliasing type
is greater than the size of the def's type, then an internal error will
be generated.
The alias def keeps track of its base def and is attached to the base def
so any aliases of that def can be found.
For any combination of type and offset, there will be only one alias def
created.
\param def The def to be aliased.
\param type The type of the alias.
\param offset Offset of the alias relative to the def.
\return The def aliasing \a def.
\todo Make aliasing to the same type a no-op?
*/
def_t *alias_def (def_t *def, struct type_s *type, int offset);
/** Free a def.
If the def has space allocated to it, the space will be freed.
\param def The def to be freed.
*/
void free_def (def_t *def);
/** \name Temporary defs
Temporary defs are used for recycling memory for tempary variables. They
always have names in the form of <code>.tmpN</code> where N is a
non-negative integer. The type of the temporary def can change throughout
its life, but the size will never change.
Temporary defs are bound to the current function (::current_func must
be valid). They are always allocated from the funciont's local defspace.
*/
//@{
/** Get a temporary def.
If the current function has a free temp def of the same size as \a size,
then that def will be recycled, otherwise a new temp will be created. When
a new temp is created, its name is set to <code>.tmpN</code> where \c N
comes from function_t::temp_num, which is then incremented.
\note ::current_func must be valid.
\param type The low-level type of the temporary variable.
\param size The amount of space to allocate to the temp.
\return The def for the temparary variable.
\bug \a size is not checked for validity (must be 1-4).
\todo support arbitrary sizes
*/
def_t *temp_def (etype_t type, int size);
/** Free a tempary def so it may be recycled.
The temp is put into a list of free temp defs maintained by ::current_func.
\note ::current_func must be valid.
\param temp The temp def to be recycled.
*/
void free_temp_def (def_t *temp);
//@}
/** Initialize a vm def from a qfcc def.
\param def The qfcc def to copy.
\param ddef The vm def to be initialized.
\param aux Copy the field object's type rather than the def's.
\bug The def is not verified to be a field def when \a aux is true.
*/
void def_to_ddef (def_t *def, ddef_t *ddef, int aux);
/** Initialize a def referenced by the given symbol.
The symbol is checked for redefinition. (FIXME check rules)
If \a type is null, then the def will be given the default type (as
specified by ::type_default).
If an initializer expressions is given (\a init is not null), then it
must be a constant expression (eg, 2 + 3) for non-local defs. Aggregate
types (structs, arrays (unions?)) use block expressions where each
expression in the block initializes one element of the aggregate. Nested
aggregates simply use nested block expressions. As for simple types, each
expression in a block expression must also be constant for non-local defs.
For \a space and \a storage, see new_def().
\param sym The symbol for which to create and initialize a def.
\param type The type of the def. sym_t::type is set to this. If null,
the default type is used.
\param init If not null, the expressions to use to initialize the def.
\param space The space from which to allocate space for the def.
\param storage The storage class of the def.
*/
void initialize_def (struct symbol_s *sym, struct type_s *type,
struct expr_s *init, struct defspace_s *space,
storage_class_t storage);
/** Determine if two defs overlap.
\param d1 The first def to check. May be an alias def.
\param d2 The second def to check. May be an alias def.
\return 1 if the defs overlap, 2 if \a d1 fully overlaps \a d2,
otherwise 0.
*/
int def_overlap (def_t *d1, def_t *d2);
/** Convenience function for obtaining a def's actual offset.
Takes care of an alias def's relative offset.
\param def The def of which to obtain the offset. May be an alias def.
\return The actual offset of the def in the def's defspace.
*/
int def_offset (def_t *def);
/** Convenience function for obtaining a def's size.
Whether the def is an alias or not is irrelevant.
\param def The def of which to obtain the size.
\return The size of the def.
*/
int def_size (def_t *def);
/** Visit all defs that alias the given def, including itself.
First, the given def is visited, and then every candidate def connected
to the given def via the aliasing links will be visited. Candidate defs
are those that overlap the given def when \a overlap is true, otherwise
every connected def is a candidate. \a overlap has no meaning if the
given def is not an alias def as all alias defs connected to the given
def are guaranteed to overlap with the given def. Any def will be visited
at most once.
The \a visit function may return non-zero to terminate the loop early.
Any data needed by \a visit should be made available via \a data, which
will be passed on to \a visit via the second parameter. The first
parameter of \a visit is the def currently being visted.
This function is useful also for defs that are not alias defs and do not
have any aliases: \a visit will be called for the def and then the
function will return.
\param def The def representing the alias cluster to visit.
\param overlap If non-zero, then only defs that overlap \a def will
be visited. If 2, then the given def must fully overlap
the visited def.
\param visit The function to call when visiting a def. The first
parameter is the def being visited, and the second
parameter is \a data passed on. If non-zero is returned,
the pass through the alias cluster will terminate.
\param data Pointer to the data needed by \a visit.
\return The value returned by \a visit returned non-zero,
otherwise 0.
*/
int def_visit_all (def_t *def, int overlap,
int (*visit) (def_t *, void *), void *data);
//@}
#endif//__def_h

View file

@ -34,21 +34,117 @@
#include "QF/pr_comp.h"
#include "QF/pr_debug.h"
/** \defgroup qfcc_defspace Defspace handling
\ingroup qfcc
*/
//@{
typedef enum {
ds_backed, ///< data space is globally addressable (near/far/type) and
///< has backing store
ds_virtual, ///< data space has no backing store (local vars, entity
///< fields)
} ds_type_t;
/** Represent a block of memory in the progs data space.
*/
typedef struct defspace_s {
struct defspace_s *next;
struct locref_s *free_locs;
struct def_s *defs;
struct def_s **def_tail;
pr_type_t *data;
int size;
int max_size;
struct defspace_s *next; ///< for ALLOC
ds_type_t type; ///< basic type of defspace
struct locref_s *free_locs; ///< list of free blocks in this space
struct def_s *defs; ///< list of defs using this space
struct def_s **def_tail; ///< for appending to \a defs
pr_type_t *data; ///< backing memory for this space
int size; ///< current high-water mark for alloced data
int max_size; ///< size of backing memory
/** Grow the backing memory of the defspace.
This function is called when more memory is needed for the space.
The default \a grow function for ds_backed defspaces expands the
backing memory by 1024 pr_type_t words and initializes them to 0.
Other defspace types do not actually allocate backing memory as it
is not needed. If null, more space cannot be allocated and an
internal error will be generated.
\param space This defspace.
\return 1 for success, 0 for failure. On failure, an internal
error will be generated.
\note Setting to null forces failure and thus an internal error.
*/
int (*grow) (struct defspace_s *space);
int qfo_space;
int qfo_space; ///< index to space in qfo spaces
} defspace_t;
defspace_t *defspace_new (void);
/** Create an empty defspace.
No backing memory is allocated, but the defspace_t::grow function is
initialized to the default grow function so the backing memory may be
accessed after space has been allocated via defspace_alloc_loc().
\param type The type of defspace to create. Affects only the default
defspace_t::grow function: ds_backed uses a grow function
that allocates backing memory, while ds_virtual uses a
grow function that only increases defspace_t::max_size
\return The new, empty defspace.
*/
defspace_t *defspace_new (ds_type_t type);
/** Allocate space from the defspace's backing memory.
If the memory is fragmented, then the first available location at least
as large as \a size is returned. This means that freeing a location then
allocating the same amount of space may return a different location.
If memory cannot be allocated (there is no free space in the currently
available memory and defspace_t::grow is null), then an internal error
will be generated.
\param space The space from which to allocate data.
\param size The amount of pr_type_t words to allocated. int and float
need 1 word, vector 3 words, and quaternion 4.
\return The offset of the first word of the freshly allocated
space. May be 0 if the allocated space is at the beginning
of the defspace.
*/
int defspace_alloc_loc (defspace_t *space, int size);
/** Free a block of contiguous words, returning them to the defspace.
The block to be freed is specified by \a ofs indicating the offset of the
first word of the block and \a size indicating the number of words in the
block.
If the block to be freed has 0 words, or if the is partly or fully outside
the defspace (as defined by defspace_t::size), or if the block overlaps
any unallocated space in the defspace, then an internal error will be
generated. However, it is perfectly valid to allocate a large block and
subsequently free a small block from anywhere within the larger block.
This is because when memory is not fragmented, there is no difference
between allocating one large block and allocating several smaller blocks
when allocating the same amount of memory.
\param space The space to which the freed block will be returned.
\param ofs The first word of the block to be freed.
\param size The number of words in the block to be freed.
*/
void defspace_free_loc (defspace_t *space, int ofs, int size);
/** Copy a block of data into a defspace.
Space for the data is allocated from the defspace via
defspace_alloc_loc().
If \a data is null, then the copying stage is skipped and this function
because a synonym for defspace_alloc_loc().
\param space The space to which the data will be added.
\param data The data to be copied into the space.
\param size The number of words of data to be copied.
\return The offset of the block allocated for the data.
*/
int defspace_add_data (defspace_t *space, pr_type_t *data, int size);
//@}
#endif//__defspace_h

37
tools/qfcc/include/dot.h Normal file
View file

@ -0,0 +1,37 @@
/*
dot.h
General dot output support
Copyright (C) 2012 Bill Currie <bill@taniwha.org>
Author: Bill Currie <bill@taniwha.org>
Date: 2012/11/07
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to:
Free Software Foundation, Inc.
59 Temple Place - Suite 330
Boston, MA 02111-1307, USA
*/
#ifndef dot_h
#define dot_h
void dump_dot (const char *stage, void *data,
void (*dump_func) (void *data, const char *fname));
#endif//dot_h

View file

@ -75,6 +75,7 @@ typedef struct ex_label_s {
struct sblock_s *dest; ///< the location of this label if known
const char *name; ///< the name of this label
int used; ///< label is used as a target
struct daglabel_s *daglabel;
} ex_label_t;
typedef struct {
@ -105,6 +106,11 @@ typedef struct ex_pointer_s {
struct def_s *def;
} ex_pointer_t;
typedef struct ex_func_s {
int val;
struct type_s *type;
} ex_func_t;
typedef struct {
int size;
struct expr_s *e[1];
@ -157,13 +163,15 @@ typedef struct {
} ex_state_t;
typedef struct ex_value_s {
struct ex_value_s *next;
struct daglabel_s *daglabel;///< dag label for this value
etype_t type;
union {
const char *string_val; ///< string constant
float float_val; ///< float constant
float vector_val[3]; ///< vector constant
int entity_val; ///< entity constant
int func_val; ///< function constant
ex_func_t func_val; ///< function constant
ex_pointer_t pointer; ///< pointer constant
float quaternion_val[4]; ///< quaternion constant
int integer_val; ///< integer constant
@ -191,7 +199,7 @@ typedef struct expr_s {
ex_expr_t expr; ///< binary or unary expression
struct symbol_s *symbol; ///< symbol reference expression
ex_temp_t temp; ///< temporary variable expression
ex_value_t value; ///< constant value
ex_value_t *value; ///< constant value
} e;
} expr_t;
@ -403,10 +411,11 @@ expr_t *new_field_expr (int field_val, struct type_s *type, struct def_s *def);
/** Create a new function constant expression node.
\param func_val The function constant being represented.
\param type The type of the function
\return The new function constant expression node
(expr_t::e::func_val).
*/
expr_t *new_func_expr (int func_val);
expr_t *new_func_expr (int func_val, struct type_s *type);
/** Create a new pointer constant expression node.
@ -556,6 +565,7 @@ void convert_name (expr_t *e);
expr_t *append_expr (expr_t *block, expr_t *e);
void print_expr (expr_t *e);
void dump_dot_expr (void *e, const char *filename);
void convert_int (expr_t *e);
void convert_short (expr_t *e);
@ -581,7 +591,7 @@ expr_t *incop_expr (int op, expr_t *e, int postop);
expr_t *array_expr (expr_t *array, expr_t *index);
expr_t *pointer_expr (expr_t *pointer);
expr_t *address_expr (expr_t *e1, expr_t *e2, struct type_s *t);
expr_t *build_if_statement (expr_t *test, expr_t *s1, expr_t *s2);
expr_t *build_if_statement (expr_t *test, expr_t *s1, expr_t *els, expr_t *s2);
expr_t *build_while_statement (expr_t *test, expr_t *statement,
expr_t *break_label, expr_t *continue_label);
expr_t *build_do_while_statement (expr_t *statement, expr_t *test,

124
tools/qfcc/include/flow.h Normal file
View file

@ -0,0 +1,124 @@
/*
flow.h
Flow graph analysis.
Copyright (C) 2012 Bill Currie <bill@taniwha.org>
Author: Bill Currie <bill@taniwha.org>
Date: 2012/10/30
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to:
Free Software Foundation, Inc.
59 Temple Place - Suite 330
Boston, MA 02111-1307, USA
*/
#ifndef flow_h
#define flow_h
/** \defgroup qfcc_flow Flow graph analysis
\ingroup qfcc
*/
//@{
struct function_s;
struct sblock_s;
struct statement_s;
struct operand_s;
typedef struct flowvar_s {
struct flowvar_s *next; ///< for ALLOC
struct set_s *use; ///< set of statements that use this var
struct set_s *define; ///< set of statements that define this var
struct operand_s *op; ///< an operand using this var
int number; ///< number of variable in func's ref list
} flowvar_t;
typedef struct flowloop_s {
struct flowloop_s *next;
unsigned head;
struct set_s *nodes;
} flowloop_t;
typedef struct flowedge_s {
unsigned tail;
unsigned head;
} flowedge_t;
/** Represent a node in a flow graph.
*/
typedef struct flownode_s {
struct flownode_s *next; ///< for ALLOC
unsigned id; ///< index of this node in the flow graph
unsigned dfn; ///< depth-first ordering of this node
struct flowgraph_s *graph; ///< graph owning this node
struct set_s *predecessors; ///< predecessors of this node
struct set_s *successors; ///< successors of this node
struct set_s *edges; ///< edges leaving this node to successor nodes
struct set_s *dom; ///< dominating nodes
struct set_s *global_vars; ///< global vars used by function
struct {
struct set_s *gen;
struct set_s *kill;
struct set_s *in;
struct set_s *out;
} reaching_defs;
struct {
struct set_s *use;
struct set_s *def;
struct set_s *in;
struct set_s *out;
} live_vars;
struct {
struct set_s *use;
struct set_s *def;
struct set_s *in;
struct set_s *out;
} init_vars;
struct sblock_s *sblock; ///< original statement block
struct dag_s *dag; ///< dag for this node
} flownode_t;
typedef struct flowgraph_s {
struct flowgraph_s *next; ///< for ALLOC
struct function_s *func; ///< function to which this graph is attached
flownode_t **nodes; ///< array of nodes in the graph
int num_nodes; ///< number of real nodes in the graph
flowedge_t *edges; ///< array of all edges in the graph
int num_edges;
struct set_s *dfst; ///< edges in the depth-first search tree
unsigned *dfo; ///< depth-first order of nodes
flowloop_t *loops; ///< linked list of natural loops
} flowgraph_t;
flowvar_t *flow_get_var (struct operand_s *op);
void flow_analyze_statement (struct statement_s *s, struct set_s *use,
struct set_s *def, struct set_s *kill,
struct operand_s *operands[4]);
void flow_data_flow (struct function_s *func);
void dump_dot_flow (void *g, const char *filename);
void dump_dot_flow_dags (void *g, const char *filename);
void dump_dot_flow_live (void *g, const char *filename);
void dump_dot_flow_reaching (void *g, const char *filename);
void dump_dot_flow_statements (void *g, const char *filename);
//@}
#endif//flow_h

View file

@ -31,11 +31,21 @@
#ifndef __function_h
#define __function_h
/** \defgroup qfcc_function Internal function structures.
\ingroup qfcc
*/
//@{
#include "QF/pr_comp.h"
#include "QF/pr_debug.h"
#include "def.h"
/** Represent an overloading of a function.
Every function, whether overloaded or not, has an entry in the overloaded
function database.
*/
typedef struct overloaded_function_s {
struct overloaded_function_s *next;
const char *name; ///< source level name of function
@ -47,6 +57,8 @@ typedef struct overloaded_function_s {
int line; ///< source line of this function
} overloaded_function_t;
/** Internal representation of a function.
*/
typedef struct function_s {
struct function_s *next;
int builtin; ///< if non 0, call an internal function
@ -55,21 +67,42 @@ typedef struct function_s {
int line_info;
int local_defs;
string_t s_file; ///< source file with definition
string_t s_name;
string_t s_name; ///< name of function in output
int temp_num; ///< number for next temp var
struct def_s *def;
struct symbol_s *sym;
struct def_s *temp_defs[4]; ///< freed temp vars (by size)
struct def_s *def; ///< output def holding function number
struct symbol_s *sym; ///< internal symbol for this function
/** Root scope symbol table of the function.
Sub-scope symbol tables are not directly accessible, but all defs
created in the function's local data space are recorded in the root
scope symbol table's defspace.
*/
struct symtab_s *symtab;
struct reloc_s *refs;
struct reloc_s *refs; ///< relocation targets for this function
struct expr_s *var_init;
const char *name; ///< nice name for __PRETTY_FUNCTION__
struct sblock_s *sblock; ///< initial node of function's code
struct flowgraph_s *graph; ///< the function's flow graph
/** Array of pointers to all variables referenced by the function's code.
This permits ready mapping of (function specific) variable number to
variable in the flow analyzer.
*/
struct flowvar_s **vars;
int num_vars; ///< total number of variables referenced
struct set_s *global_vars;///< set indicating which vars are global
struct statement_s **statements;
int num_statements;
} function_t;
extern function_t *current_func;
/** Representation of a function parameter.
\note The first two fields match the first two fields of keywordarg_t
in method.h
*/
typedef struct param_s {
// the first two fields match the first two fiels of keywordarg_t in
// method.h
struct param_s *next;
const char *selector;
struct type_s *type; //FIXME redundant
@ -113,4 +146,6 @@ void emit_function (function_t *f, struct expr_s *e);
int function_parms (function_t *f, byte *parm_size);
void clear_functions (void);
//@}
#endif//__function_h

View file

@ -37,6 +37,7 @@ extern int grab_write;
int do_grab (const char *token);
void add_frame_macro (const char *token);
void clear_frame_macros (void);
void write_frame_macros (const char *filename);
#endif//__grab_h

View file

@ -54,12 +54,12 @@ typedef struct selector_s {
typedef struct methodlist_s {
method_t *head;
method_t **tail;
int count; ///< used only for emitting
int count; ///< used only for emitting
int instance; ///< used only for emitting
} methodlist_t;
typedef struct keywordarg_s {
// the first two fields match the first two fiels of param_t in
// the first two fields match the first two fields of param_t in
// functionl.h
struct keywordarg_s *next;
const char *selector;

View file

@ -67,7 +67,7 @@ typedef struct qfo_header_s {
pr_int_t num_funcs; ///< number of function records
pr_int_t num_lines; ///< number of line records
pr_int_t num_loose_relocs; ///< number of loose relocation records
///< included in num_relocs
///< (included in num_relocs)
} qfo_header_t;
typedef enum qfos_type_e {
@ -161,6 +161,13 @@ typedef struct qfo_def_s {
\hideinitializer
*/
#define QFOD_NOSAVE (1u<<7)
/** The def is a parameter to a function and is considered to be initialized.
QFOD_LOCAL will be set too.
\hideinitializer
*/
#define QFOD_PARAM (1u<<8)
//@}
/** \addtogroup qfcc_qfo

View file

@ -46,10 +46,10 @@ extern struct opcode_s *op_goto;
extern struct opcode_s *op_jump;
extern struct opcode_s *op_jumpb;
struct def_s;
struct operand_s;
struct opcode_s *opcode_find (const char *name, struct def_s *def_a,
struct def_s *def_b, struct def_s *def_c);
struct opcode_s *opcode_find (const char *name, struct operand_s *op_a,
struct operand_s *op_b, struct operand_s *op_c);
void opcode_init (void);
#endif//__opcodes_h

View file

@ -38,6 +38,7 @@ typedef struct {
qboolean crc; // Write progsdef.h crc to progs.dat
qboolean debug; // Generate debug info for the engine
qboolean short_circuit; // short circuit logic for && and ||
qboolean optimize; // perform optimizations
qboolean fast_float; // use floats directly in ifs
qboolean vector_calls; // use floats instead of vectors for constant function args
qboolean local_merging; // merge function locals into one block
@ -72,6 +73,12 @@ typedef struct {
qboolean thread;
qboolean dead;
qboolean final;
qboolean dags;
qboolean expr;
qboolean reaching;
qboolean live;
qboolean flow;
qboolean post;
} blockdot_options_t;
typedef struct {

View file

@ -0,0 +1,42 @@
/*
praga.h
pragma handling
Copyright (C) 2012 Bill Currie <bill@taniwha.org>
Author: Bill Currie <bill@taniwha.org>
Date: 2012/11/22
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to:
Free Software Foundation, Inc.
59 Temple Place - Suite 330
Boston, MA 02111-1307, USA
*/
#ifndef pragma_h
#define pragma_h
/** \defgroup qfcc_pragma pragma handling
\ingroup qfcc
*/
//@{
void pragma (const char *id);
//@}
#endif//pragma_h

View file

@ -60,7 +60,6 @@ typedef struct pr_info_s {
struct strpool_s *strings; ///< progs string data
struct codespace_s *code; ///< progs code data
struct defspace_s *data; ///< combined near and far data
struct defspace_s *near_data; ///< data directly addressable by
///< statments (address < 64k)
struct defspace_s *far_data; ///< data that might not be directly
@ -87,19 +86,10 @@ typedef struct pr_info_s {
struct pr_lineno_s *linenos;
int linenos_size;
int num_linenos;
ddef_t *locals;
int locals_size;
int num_locals;
} pr_info_t;
extern pr_info_t pr;
extern char destfile[];
extern struct symtab_s *current_symtab;
#define GETSTR(s) (pr.strings->strings + (s))
#define D_var(t, d) ((d)->space->data[(d)->offset].t##_var)
#define D_FLOAT(d) D_var (float, d)
@ -119,7 +109,6 @@ extern struct symtab_s *current_symtab;
const char *strip_path (const char *filename);
void clear_frame_macros (void);
extern FILE *qc_yyin;
extern FILE *qp_yyin;
int qc_yyparse (void);
@ -134,36 +123,6 @@ char *fix_backslash (char *path);
#define NORMALIZE(x) x
#endif
/** High-tide structure allocator for use in linked lists.
Using a free-list with the name of \c free_NAME, return a single element.
The type of the element must be a structure with a field named \c next.
When the free-list is empty, memory is claimed from the system in blocks.
elements may be returned to the pool by linking them into the free-list.
\param s The number of structures in the block.
\param t The structure type.
\param n The \c NAME portion of the \c free_NAME free-list.
\param v The destination of the pointer to the allocated
element. The contents of the allocated element will be
memset to 0.
\hideinitializer
*/
#define ALLOC(s, t, n, v) \
do { \
if (!free_##n) { \
int i; \
free_##n = malloc ((s) * sizeof (t)); \
for (i = 0; i < (s) - 1; i++) \
free_##n[i].next = &free_##n[i + 1];\
free_##n[i].next = 0; \
} \
v = free_##n; \
free_##n = free_##n->next; \
memset (v, 0, sizeof (*v)); \
} while (0)
/** Round \a x up to the next multiple of \a a.
\note \a a must be a power of two or this will break.
\note There are no side effects on \a x.

View file

@ -33,36 +33,66 @@
#include "QF/pr_comp.h"
typedef enum {
op_symbol,
op_def,
op_value,
op_label,
op_temp,
op_pointer,
op_alias,
} op_type_e;
typedef struct {
struct def_s *def;
struct type_s *type;
struct flowvar_s *flowvar;
struct daglabel_s *daglabel;
struct operand_s *alias;
struct operand_s *alias_ops;
int users;
} tempop_t;
typedef struct operand_s {
struct operand_s *next;
op_type_e op_type;
etype_t type; ///< possibly override symbol's type
etype_t type; ///< possibly override def's type
int size; ///< for structures
union {
struct symbol_s *symbol;
struct def_s *def;
struct ex_value_s *value;
struct ex_label_s *label;
struct ex_pointer_s *pointer;
struct def_s *def;
tempop_t tempop;
struct operand_s *alias;
} o;
} operand_t;
/** Overall type of statement.
Statement types are broken down into expressions (binary and unary,
includes address and pointer dereferencing (read)), assignment, pointer
assignment (write to dereference pointer), move (special case of pointer
assignment), state, function related (call, rcall, return and done), and
flow control (conditional branches, goto, jump (single pointer and jump
table)).
*/
typedef enum {
st_none, ///< not a (valid) statement. Used in dags.
st_expr, ///< c = a op b; or c = op a;
st_assign, ///< b = a
st_ptrassign, ///< *b = a; or *(b + c) = a;
st_move, ///< memcpy (c, a, b);
st_state, ///< state (a, b); or state (a, b, c)
st_func, ///< call, rcall or return/done
st_flow, ///< if/ifa/ifae/ifb/ifbe/ifnot or goto or jump/jumpb
} st_type_t;
typedef struct statement_s {
struct statement_s *next;
st_type_t type;
const char *opcode;
operand_t *opa;
operand_t *opb;
operand_t *opc;
struct expr_s *expr; ///< source expression for this statement
struct expr_s *expr; ///< source expression for this statement
int number; ///< number of this statement in function
} statement_t;
typedef struct sblock_s {
@ -71,15 +101,41 @@ typedef struct sblock_s {
struct ex_label_s *labels;
int offset; ///< offset of first statement of block
int reachable;
int number; ///< number of this block in flow graph
statement_t *statements;
statement_t **tail;
} sblock_t;
struct expr_s;
struct type_s;
struct dstring_s;
const char *optype_str (op_type_e type);
operand_t *def_operand (struct def_s *def, struct type_s *type);
operand_t *value_operand (struct ex_value_s *value);
operand_t *temp_operand (struct type_s *type);
operand_t *alias_operand (etype_t type, operand_t *op);
void free_operand (operand_t *op);
sblock_t *new_sblock (void);
statement_t *new_statement (st_type_t type, const char *opcode,
struct expr_s *expr);
int statement_is_cond (statement_t *s);
int statement_is_goto (statement_t *s);
int statement_is_jumpb (statement_t *s);
int statement_is_call (statement_t *s);
int statement_is_return (statement_t *s);
sblock_t *statement_get_target (statement_t *s);
sblock_t **statement_get_targetlist (statement_t *s);
void sblock_add_statement (sblock_t *sblock, statement_t *statement);
sblock_t *make_statements (struct expr_s *expr);
void statements_count_temps (sblock_t *sblock);
void print_statement (statement_t *s);
void print_flow (sblock_t *sblock, const char *filename);
void dump_dot_sblock (void *data, const char *fname);
void dot_sblock (struct dstring_s *dstr, sblock_t *sblock, int blockno);
void print_sblock (sblock_t *sblock, const char *filename);
const char *operand_string (operand_t *op);
#endif//statement_h

View file

@ -61,15 +61,15 @@ typedef struct symbol_s {
struct symtab_s *table; ///< symbol table that owns this symbol
vis_t visibility; ///< symbol visiblity. defaults to public
const char *name; ///< symbol name
sy_type_e sy_type; ///< symbol type (st_type)
sy_type_e sy_type; ///< symbol type
struct type_s *type; ///< type of object to which symbol refers
struct param_s *params; ///< the parameters if a function
union {
int offset; ///< st_var (in a struct/union)
struct def_s *def; ///< st_var
struct ex_value_s value; ///< st_const
struct expr_s *expr; ///< st_expr
struct function_s *func; ///< st_func
int offset; ///< sy_var (in a struct/union)
struct def_s *def; ///< sy_var
struct ex_value_s *value; ///< sy_const
struct expr_s *expr; ///< sy_expr
struct function_s *func; ///< sy_func
} s;
} symbol_t;
@ -92,6 +92,8 @@ typedef struct symtab_s {
struct defspace_s *space; ///< storage for vars in scope symtabs
} symtab_t;
const char *symtype_str (sy_type_e type);
/** Create a new, empty named symbol.
Only the symbol name field will be filled in. \a name will be copied
@ -224,10 +226,10 @@ symtab_t *symtab_flat_copy (symtab_t *symtab, symtab_t *parent);
\param name The name of the symbol.
\param type The type of the symbol.
\param space The defspace from which space will be allocated for the
symbol. Ignored for st_extern, must not be null for
symbol. Ignored for sc_extern, must not be null for
others.
\param storage The storage class for the symbol. Only st_extern,
st_global, and st_static are valid.
\param storage The storage class for the symbol. Only sc_extern,
sc_global, and sc_static are valid.
*/
symbol_t *make_symbol (const char *name, struct type_s *type,
struct defspace_s *space, enum storage_class_e storage);

View file

@ -143,21 +143,21 @@ type_t *field_type (type_t *aux);
type_t *pointer_type (type_t *aux);
type_t *array_type (type_t *aux, int size);
type_t *based_array_type (type_t *aux, int base, int top);
void print_type_str (struct dstring_s *str, type_t *type);
void print_type (type_t *type);
const char *encode_params (type_t *type);
void encode_type (struct dstring_s *encoding, type_t *type);
const char *type_get_encoding (type_t *type);
int is_enum (type_t *type);
int is_integral (type_t *type);
int is_float (type_t *type);
int is_scalar (type_t *type);
int is_math (type_t *type);
int is_struct (type_t *type);
int is_class (type_t *type);
int is_array (type_t *type);
int type_assignable (type_t *dst, type_t *src);
int type_size (type_t *type);
void print_type_str (struct dstring_s *str, const type_t *type);
void print_type (const type_t *type);
const char *encode_params (const type_t *type);
void encode_type (struct dstring_s *encoding, const type_t *type);
const char *type_get_encoding (const type_t *type);
int is_enum (const type_t *type);
int is_integral (const type_t *type);
int is_float (const type_t *type);
int is_scalar (const type_t *type);
int is_math (const type_t *type);
int is_struct (const type_t *type);
int is_class (const type_t *type);
int is_array (const type_t *type);
int type_assignable (const type_t *dst, const type_t *src);
int type_size (const type_t *type);
void init_types (void);
void chain_initial_types (void);

View file

@ -31,14 +31,37 @@
#ifndef __value_h
#define __value_h
/** \defgroup qfcc_value Constant values.
\ingroup qfcc_expr
*/
//@{
struct ex_value_s;
struct type_s;
void convert_value (struct ex_value_s *value, struct type_s *type);
struct ex_value_s *new_string_val (const char *string_val);
struct ex_value_s *new_float_val (float float_val);
struct ex_value_s *new_vector_val (const float *vector_val);
struct ex_value_s *new_entity_val (int entity_val);
struct ex_value_s *new_field_val (int field_val, struct type_s *type,
struct def_s *def);
struct ex_value_s *new_func_val (int func_val, struct type_s *type);
struct ex_value_s *new_pointer_val (int val, struct type_s *type,
struct def_s *def);
struct ex_value_s *new_quaternion_val (const float *quaternion_val);
struct ex_value_s *new_integer_val (int integer_val);
struct ex_value_s *new_uinteger_val (int uinteger_val);
struct ex_value_s *new_short_val (short short_val);
struct ex_value_s *new_nil_val (struct type_s *type);
struct ex_value_s * convert_value (struct ex_value_s *value,
struct type_s *type);
struct def_s *emit_value (struct ex_value_s *value, struct def_s *def);
int ReuseString (const char *str);
void clear_immediates (void);
//@}
#endif//__value_h

View file

@ -39,11 +39,11 @@ bin_PROGRAMS= qfcc qfprogs
bin_SCRIPTS= qfpreqcc
common_src=\
class.c codespace.c constfold.c cpp.c debug.c def.c defspace.c \
diagnostic.c dot_expr.c dot_flow.c emit.c expr.c function.c grab.c \
idstuff.c linker.c method.c obj_file.c obj_type.c opcodes.c options.c \
qfcc.c reloc.c shared.c statements.c strpool.c struct.c switch.c symtab.c \
type.c value.c
class.c codespace.c constfold.c cpp.c dags.c debug.c def.c defspace.c \
diagnostic.c dot.c dot_dag.c dot_expr.c dot_flow.c dot_sblock.c emit.c \
expr.c flow.c function.c grab.c idstuff.c linker.c method.c obj_file.c \
obj_type.c opcodes.c options.c pragma.c qfcc.c reloc.c shared.c \
statements.c strpool.c struct.c switch.c symtab.c type.c value.c
qfcc_SOURCES= qc-lex.l qc-parse.y qp-lex.l qp-parse.y $(common_src)
qfcc_LDADD= $(QFCC_LIBS)

View file

@ -56,6 +56,7 @@
#include "method.h"
#include "options.h"
#include "reloc.h"
#include "shared.h"
#include "strpool.h"
#include "struct.h"
#include "symtab.h"
@ -228,7 +229,7 @@ class_symbol (class_type_t *class_type, int external)
return 0; // probably in error recovery
}
sym = make_symbol (name, type, pr.far_data,
external ? st_extern : st_global);
external ? sc_extern : sc_global);
if (!sym->table)
symtab_addsymbol (pr.symtab, sym);
return sym;
@ -432,7 +433,7 @@ emit_ivars (symtab_t *ivars, const char *name)
ivar_list_struct[1].type = array_type (&type_obj_ivar, ivar_data.count);
def = emit_structure (va ("_OBJ_INSTANCE_VARIABLES_%s", name), 's',
ivar_list_struct, 0, &ivar_data, st_static);
ivar_list_struct, 0, &ivar_data, sc_static);
dstring_delete (ivar_data.encoding);
return def;
@ -449,7 +450,7 @@ begin_class (class_t *class)
defspace_t *space;
sym = make_symbol (va ("_OBJ_METACLASS_%s", class->name),
&type_obj_class, pr.far_data, st_static);
&type_obj_class, pr.far_data, sc_static);
meta_def = sym->s.def;
meta_def->initialized = meta_def->constant = meta_def->nosave = 1;
space = meta_def->space;
@ -511,7 +512,7 @@ emit_class_ref (const char *class_name)
def_t *name_def;
ref_sym = make_symbol (va (".obj_class_ref_%s", class_name), &type_pointer,
pr.far_data, st_static);
pr.far_data, sc_static);
if (!ref_sym->table)
symtab_addsymbol (pr.symtab, ref_sym);
ref_def = ref_sym->s.def;
@ -519,7 +520,7 @@ emit_class_ref (const char *class_name)
return;
ref_def->initialized = ref_def->constant = ref_def->nosave = 1;
name_sym = make_symbol (va (".obj_class_name_%s", class_name),
&type_pointer, pr.far_data, st_extern);
&type_pointer, pr.far_data, sc_extern);
if (!name_sym->table)
symtab_addsymbol (pr.symtab, name_sym);
name_def = name_sym->s.def;
@ -535,7 +536,7 @@ emit_class_name (const char *class_name)
def_t *name_def;
name_sym = make_symbol (va (".obj_class_name_%s", class_name),
&type_pointer, pr.far_data, st_global);
&type_pointer, pr.far_data, sc_global);
if (!name_sym->table)
symtab_addsymbol (pr.symtab, name_sym);
name_def = name_sym->s.def;
@ -556,7 +557,7 @@ emit_category_ref (const char *class_name, const char *category_name)
ref_sym = make_symbol (va (".obj_category_ref_%s_%s",
class_name, category_name),
&type_pointer, pr.far_data, st_static);
&type_pointer, pr.far_data, sc_static);
if (!ref_sym->table)
symtab_addsymbol (pr.symtab, ref_sym);
ref_def = ref_sym->s.def;
@ -566,7 +567,7 @@ emit_category_ref (const char *class_name, const char *category_name)
ref_def->nosave = 1;
name_sym = make_symbol (va (".obj_category_name_%s_%s",
class_name, category_name),
&type_pointer, pr.far_data, st_extern);
&type_pointer, pr.far_data, sc_extern);
if (!name_sym->table)
symtab_addsymbol (pr.symtab, name_sym);
name_def = name_sym->s.def;
@ -583,7 +584,7 @@ emit_category_name (const char *class_name, const char *category_name)
name_sym = make_symbol (va (".obj_category_name_%s_%s",
class_name, category_name),
&type_pointer, pr.far_data, st_global);
&type_pointer, pr.far_data, sc_global);
if (!name_sym->table)
symtab_addsymbol (pr.symtab, name_sym);
name_def = name_sym->s.def;
@ -959,7 +960,7 @@ class_pointer_symbol (class_t *class)
sym = make_symbol (va ("_OBJ_CLASS_POINTER_%s", class->name),
&type_Class,
pr.near_data, st_static);
pr.near_data, sc_static);
if (!sym->table)
symtab_addsymbol (pr.symtab, sym);
def = sym->s.def;
@ -1100,12 +1101,12 @@ class_finish_module (void)
symtab_struct[4].type = array_type (&type_pointer,
data.cls_def_cnt + data.cat_def_cnt);
symtab_def = emit_structure ("_OBJ_SYMTAB", 's', symtab_struct, 0, &data,
st_static);
sc_static);
free (data.classes);
free (data.categories);
module_sym = make_symbol ("_OBJ_MODULE", &type_obj_module, pr.far_data,
st_static);
sc_static);
symtab_addsymbol (current_symtab, module_sym);
module = &D_STRUCT (pr_module_t, module_sym->s.def);
module->size = type_size (&type_obj_module);
@ -1119,7 +1120,7 @@ class_finish_module (void)
&type_obj_exec_class);
exec_class_sym = function_symbol (exec_class_sym, 0, 1);
make_function (exec_class_sym, 0, exec_class_sym->table->space,
st_extern);
sc_extern);
}
init_sym = new_symbol_type (".ctor", &type_function);
@ -1133,7 +1134,7 @@ class_finish_module (void)
0, 0)));
save_storage = current_storage;
current_storage = st_static;
current_storage = sc_static;
current_func = begin_function (init_sym, 0, current_symtab, 1);
build_code_function (init_sym, 0, init_expr);;
current_func = 0;
@ -1184,7 +1185,7 @@ def_t *
protocol_def (protocol_t *protocol)
{
return make_symbol (protocol->name, &type_obj_protocol,
pr.far_data, st_static)->s.def;
pr.far_data, sc_static)->s.def;
}
protocollist_t *
@ -1221,7 +1222,7 @@ emit_protocol (protocol_t *protocol)
defspace_t *space;
proto_def = make_symbol (va ("_OBJ_PROTOCOL_%s", protocol->name),
&type_obj_protocol, pr.far_data, st_static)->s.def;
&type_obj_protocol, pr.far_data, sc_static)->s.def;
if (proto_def->initialized)
return proto_def;
proto_def->initialized = proto_def->constant = 1;
@ -1263,7 +1264,7 @@ emit_protocol_list (protocollist_t *protocols, const char *name)
proto_list_type = make_structure (0, 's', proto_list_struct, 0)->type;
proto_list_def = make_symbol (va ("_OBJ_PROTOCOLS_%s", name),
proto_list_type,
pr.far_data, st_static)->s.def;
pr.far_data, sc_static)->s.def;
proto_list_def->initialized = proto_list_def->constant = 1;
proto_list_def->nosave = 1;
space = proto_list_def->space;

View file

@ -140,7 +140,7 @@ convert_to_float (expr_t *e)
switch (e->type) {
case ex_value:
switch (e->e.value.type) {
switch (e->e.value->type) {
case ev_integer:
convert_int (e);
return e;
@ -876,6 +876,9 @@ do_op_invalid (int op, expr_t *e, expr_t *e1, expr_t *e2)
{
type_t *t1 = get_type (e1);
type_t *t2 = get_type (e2);
if (e->e.expr.op == 'm')
return e; // assume the rest of the compiler got it right
if (is_scalar (t1) && is_scalar (t2)) {
// one or both expressions are an enum, and the other is one of
// int, float or short. Treat the enum as the other type, or as

865
tools/qfcc/source/dags.c Normal file
View file

@ -0,0 +1,865 @@
/*
dags.c
DAG representation of basic blocks
Copyright (C) 2012 Bill Currie <bill@taniwha.org>
Author: Bill Currie <bill@taniwha.org>
Date: 2012/05/08
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to:
Free Software Foundation, Inc.
59 Temple Place - Suite 330
Boston, MA 02111-1307, USA
*/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#ifdef HAVE_STRING_H
# include <string.h>
#endif
#ifdef HAVE_STRINGS_H
# include <strings.h>
#endif
#include <stdlib.h>
#include <ctype.h>
#include "QF/alloc.h"
#include "QF/dstring.h"
#include "QF/mathlib.h"
#include "QF/set.h"
#include "dags.h"
#include "diagnostic.h"
#include "flow.h"
#include "function.h"
#include "qfcc.h"
#include "statements.h"
#include "strpool.h"
#include "symtab.h"
#include "type.h"
static daglabel_t *free_labels;
static dagnode_t *free_nodes;
static dag_t *free_dags;
static daglabel_t *daglabel_chain;
static void
flush_daglabels (void)
{
while (daglabel_chain) {
operand_t *op;
if ((op = daglabel_chain->op)) {
if (op->op_type == op_def)
op->o.def->daglabel = 0;
else if (op->op_type == op_temp)
op->o.tempop.daglabel = 0;
else if (op->op_type == op_value)
op->o.value->daglabel = 0;
else if (op->op_type == op_label)
op->o.label->daglabel = 0;
else
internal_error (0, "unexpected operand type");
}
daglabel_chain = daglabel_chain->daglabel_chain;
}
}
static dag_t *
new_dag (void)
{
dag_t *dag;
ALLOC (256, dag_t, dags, dag);
return dag;
}
static daglabel_t *
new_label (dag_t *dag)
{
daglabel_t *label;
ALLOC (256, daglabel_t, labels, label);
label->daglabel_chain = daglabel_chain;
daglabel_chain = label;
label->number = dag->num_labels;
dag->labels[dag->num_labels++] = label;
return label;
}
static dagnode_t *
new_node (dag_t *dag)
{
dagnode_t *node;
ALLOC (256, dagnode_t, nodes, node);
node->parents = set_new ();
node->edges = set_new ();
node->identifiers = set_new ();
node->number = dag->num_nodes;
set_add (dag->roots, node->number); // nodes start out as root nodes
dag->nodes[dag->num_nodes++] = node;
return node;
}
const char *
daglabel_string (daglabel_t *label)
{
static dstring_t *str;
if ((label->opcode && label->op) || (!label->opcode && !label->op))
return "bad label";
if (label->opcode)
return label->opcode;
if (!str)
str = dstring_new ();
// operand_string might use quote_string, which returns a pointer to
// a static variable.
dstring_copystr (str, operand_string (label->op));
return quote_string (str->str);
}
static daglabel_t *
opcode_label (dag_t *dag, const char *opcode, expr_t *expr)
{
daglabel_t *label;
label = new_label (dag);
label->opcode = opcode;
label->expr = expr;
return label;
}
static daglabel_t *
operand_label (dag_t *dag, operand_t *op)
{
def_t *def = 0;
ex_value_t *val = 0;
daglabel_t *label;
if (!op)
return 0;
if (op->op_type == op_temp) {
while (op->o.tempop.alias)
op = op->o.tempop.alias;
if (op->o.tempop.daglabel)
return op->o.tempop.daglabel;
label = new_label (dag);
label->op = op;
op->o.tempop.daglabel = label;
} else if (op->op_type == op_def) {
def = op->o.def;
if (def->daglabel)
return def->daglabel;
label = new_label (dag);
label->op = op;
def->daglabel = label;
} else if (op->op_type == op_value) {
val = op->o.value;
if (val->daglabel)
return val->daglabel;
label = new_label (dag);
label->op = op;
val->daglabel = label;
} else if (op->op_type == op_label) {
if (op->o.label->daglabel)
return op->o.label->daglabel;
label = new_label (dag);
label->op = op;
op->o.label->daglabel = label;
} else {
internal_error (0, "unexpected operand type: %d", op->op_type);
}
return label;
}
static dagnode_t *
leaf_node (dag_t *dag, operand_t *op, expr_t *expr)
{
daglabel_t *label;
dagnode_t *node;
if (!op)
return 0;
node = new_node (dag);
node->tl = op->type;
label = operand_label (dag, op);
label->dagnode = node;
label->expr = expr;
node->label = label;
return node;
}
static dagnode_t *
dag_node (operand_t *op)
{
def_t *def;
dagnode_t *node = 0;
if (!op)
return 0;
if (op->op_type == op_def) {
def = op->o.def;
if (def->daglabel)
node = def->daglabel->dagnode;
} else if (op->op_type == op_temp) {
while (op->o.tempop.alias)
op = op->o.tempop.alias;
if (op->o.tempop.daglabel)
node = op->o.tempop.daglabel->dagnode;
} else if (op->op_type == op_value) {
if (op->o.value->daglabel)
node = op->o.value->daglabel->dagnode;
} else if (op->op_type == op_label) {
if (op->o.label->daglabel)
node = op->o.label->daglabel->dagnode;
}
if (node && node->killed)
node = 0;
return node;
}
static void
dag_make_children (dag_t *dag, statement_t *s, operand_t *operands[4],
dagnode_t *children[3])
{
int i;
flow_analyze_statement (s, 0, 0, 0, operands);
for (i = 0; i < 3; i++) {
if (!(children[i] = dag_node (operands[i + 1])))
children[i] = leaf_node (dag, operands[i + 1], s->expr);
}
}
static int
dagnode_deref_match (const dagnode_t *n, const daglabel_t *op,
dagnode_t *children[3])
{
int i;
for (i = 0; i < 2; i++) {
if (n->children[i + 1] != children[i])
return 0;
}
return 1;
}
static int
dagnode_match (const dagnode_t *n, const daglabel_t *op,
dagnode_t *children[3])
{
int i;
if (n->killed)
return 0;
if (!strcmp (op->opcode, ".")
&& n->label->opcode && !strcmp (n->label->opcode, ".="))
return dagnode_deref_match (n, op, children);
if (n->label->opcode != op->opcode)
return 0;
for (i = 0; i < 3; i++) {
if (n->children[i] != children[i])
return 0;
}
return 1;
}
static dagnode_t *
dagnode_search (dag_t *dag, daglabel_t *op, dagnode_t *children[3])
{
int i;
for (i = 0; i < dag->num_nodes; i++)
if (dagnode_match (dag->nodes[i], op, children))
return dag->nodes[i];
return 0;
}
static void
dagnode_add_children (dag_t *dag, dagnode_t *n, operand_t *operands[4],
dagnode_t *children[3])
{
int i;
for (i = 0; i < 3; i++) {
dagnode_t *child = children[i];
if ((n->children[i] = child)) {
n->types[i] = operands[i + 1]->type;
set_remove (dag->roots, child->number);
set_add (child->parents, n->number);
}
}
}
static int
dagnode_set_edges_visit (def_t *def, void *_node)
{
dagnode_t *node = (dagnode_t *) _node;
daglabel_t *label;
label = def->daglabel;
if (label && label->dagnode) {
set_add (node->edges, label->dagnode->number);
label->live = 1;
}
return 0;
}
static int
dag_find_node (def_t *def, void *unused)
{
if (def->daglabel && def->daglabel->dagnode)
return def->daglabel->dagnode->number + 1; // ensure never 0
return 0;
}
static void
dagnode_set_edges (dag_t *dag, dagnode_t *n)
{
int i;
for (i = 0; i < 3; i++) {
dagnode_t *child = n->children[i];
if (child && n != child)
set_add (n->edges, child->number);
}
if (n->type == st_flow)
return;
for (i = 0; i < 3; i++) {
dagnode_t *child = n->children[i];
if (child) {
if (child->label->op) {
dagnode_t *node = child->label->dagnode;
operand_t *op = child->label->op;
if (node != child && node != n)
set_add (node->edges, n->number);
if (op->op_type == op_value
&& op->o.value->type == ev_pointer
&& op->o.value->v.pointer.def)
def_visit_all (op->o.value->v.pointer.def, 1,
dagnode_set_edges_visit, n);
if (op->op_type == op_def
&& (op->o.def->alias || op->o.def->alias_defs))
def_visit_all (op->o.def, 1, dagnode_set_edges_visit, n);
}
if (n != child)
set_add (n->edges, child->number);
}
}
if (n->type == st_func) {
const char *num_params = 0;
int first_param = 0;
flowvar_t **flowvars = dag->flownode->graph->func->vars;
if (!strncmp (n->label->opcode, "<RCALL", 6)) {
num_params = n->label->opcode + 6;
first_param = 2;
} else if (!strncmp (n->label->opcode, "<CALL", 5)) {
num_params = n->label->opcode + 5;
}
if (num_params && isdigit (*num_params)) {
for (i = first_param; i < *num_params - '0'; i++) {
flowvar_t *var = flowvars[i + 1];
def_t *param_def = var->op->o.def;
int param_node;
// FIXME hopefully only the one alias :P
param_node = def_visit_all (param_def, 0, dag_find_node, 0);
if (!param_node) {
bug (0, ".param_%d not set for %s", i, n->label->opcode);
continue;
}
set_add (n->edges, param_node - 1);
}
}
}
}
static int
op_is_identifier (operand_t *op)
{
if (op->op_type == op_label)
return 0;
if (op->op_type == op_value)
return 0;
if (op->op_type == op_temp)
return 1;
if (op->op_type != op_def)
return 0;
return 1;
}
static int
dag_kill_aliases_visit (def_t *def, void *_node)
{
dagnode_t *node = (dagnode_t *) _node;
daglabel_t *label;
label = def->daglabel;
if (label && label->dagnode) {
set_add (node->edges, label->dagnode->number);
set_remove (node->edges, node->number);
label->dagnode->killed = 1;
}
return 0;
}
static void
dag_kill_aliases (daglabel_t *l)
{
operand_t *op = l->op;
if (op->op_type == op_temp) {
} else if (op->op_type == op_def) {
if (op->o.def->alias || op->o.def->alias_defs)
def_visit_all (op->o.def, 1, dag_kill_aliases_visit, l->dagnode);
} else {
internal_error (0, "rvalue assignment?");
}
}
static void
dagnode_attach_label (dagnode_t *n, daglabel_t *l)
{
if (!l->op)
internal_error (0, "attempt to attach operator label to dagnode "
"identifers");
if (!op_is_identifier (l->op))
internal_error (0, "attempt to attach non-identifer label to dagnode "
"identifers");
if (l->dagnode) {
dagnode_t *node = l->dagnode;
set_union (n->edges, node->parents);
set_remove (n->edges, n->number);
set_remove (node->identifiers, l->number);
}
l->live = 0; // remove live forcing on assignment
l->dagnode = n;
set_add (n->identifiers, l->number);
dag_kill_aliases (l);
}
static int
dag_alias_live (def_t *def, void *_live_vars)
{
set_t *live_vars = (set_t *) _live_vars;
if (!def->flowvar)
return 0;
return set_is_member (live_vars, def->flowvar->number);
}
static void
dag_remove_dead_vars (dag_t *dag, set_t *live_vars)
{
int i;
for (i = 0; i < dag->num_labels; i++) {
daglabel_t *l = dag->labels[i];
flowvar_t *var;
if (!l->op || !l->dagnode)
continue;
if (l->live) // label forced live (probably via an alias)
continue;
var = flow_get_var (l->op);
if (!var)
continue;
if (set_is_member (dag->flownode->global_vars, var->number))
continue;
if (l->op->op_type == op_def
&& def_visit_all (l->op->o.def, 1, dag_alias_live, live_vars))
continue;
if (!set_is_member (live_vars, var->number))
set_remove (l->dagnode->identifiers, l->number);
}
}
static void
dag_sort_visit (dag_t *dag, set_t *visited, int node_index, int *topo)
{
set_iter_t *node_iter;
dagnode_t *node;
if (set_is_member (visited, node_index))
return;
set_add (visited, node_index);
node = dag->nodes[node_index];
for (node_iter = set_first (node->edges); node_iter;
node_iter = set_next (node_iter))
dag_sort_visit (dag, visited, node_iter->value, topo);
node->topo = *topo;
dag->topo[(*topo)++] = node_index;
}
static void
dag_sort_nodes (dag_t *dag)
{
set_iter_t *root_iter;
set_t *visited = set_new ();
int topo = 0;
dag->topo = malloc (dag->num_nodes * sizeof (int));
for (root_iter = set_first (dag->roots); root_iter;
root_iter = set_next (root_iter))
dag_sort_visit (dag, visited, root_iter->value, &topo);
set_delete (visited);
}
static void
dag_make_var_live (set_t *live_vars, operand_t *op)
{
flowvar_t *var = 0;
if (op)
var = flow_get_var (op);
if (var)
set_add (live_vars, var->number);
}
static void
dag_set_live_vars (set_t *live_vars, dag_t *dag, dagnode_t *n)
{
if (!strncmp (n->label->opcode, "<CALL", 5)
|| !strncmp (n->label->opcode, "<RCALL", 6)) {
int start, end;
if (!strncmp (n->label->opcode, "<CALL", 5)) {
start = 0;
end = 0;
if (n->label->opcode[5] != '>')
end = n->label->opcode[5] - '0';
} else {
start = 2;
end = n->label->opcode[6] - '0';
}
while (start < end) {
set_add (live_vars, start + 1);
start++;
}
} else if (!strncmp (n->label->opcode, "<RETURN", 7)) {
set_union (live_vars, dag->flownode->global_vars);
}
}
static void
dag_kill_nodes (dag_t *dag, dagnode_t *n)
{
int i;
dagnode_t *node;
for (i = 0; i < dag->num_nodes; i++) {
node = dag->nodes[i];
if (node == n->children[1]) {
// assume the pointer does not point to itself. This should be
// reasonable because without casting, only a void pointer can
// point to itself (the required type is recursive).
continue;
}
if (node->label->op && !op_is_identifier (node->label->op)) {
// While constants in the Quake VM can be changed via a pointer,
// doing so would cause much more fun than a simple
// mis-optimization would, so consider them safe from pointer
// operations.
continue;
}
node->killed = 1;
}
n->killed = 0;
}
dag_t *
dag_create (flownode_t *flownode)
{
dag_t *dag;
sblock_t *block = flownode->sblock;
statement_t *s;
dagnode_t **nodes;
daglabel_t **labels;
int num_statements = 0;
set_t *live_vars = set_new ();
flush_daglabels ();
// count the number of statements so the number of nodes and labels can be
// guessed
for (s = block->statements; s; s = s->next)
num_statements++;
set_assign (live_vars, flownode->live_vars.out);
dag = new_dag ();
dag->flownode = flownode;
// at most 4 per statement
dag->nodes = alloca (num_statements * 4 * sizeof (dagnode_t));
// at most 4 per statement, + return + params
dag->labels = alloca (num_statements * (4 + 1 + 8) * sizeof (daglabel_t));
dag->roots = set_new ();
for (s = block->statements; s; s = s->next) {
operand_t *operands[4];
dagnode_t *n = 0, *children[3] = {0, 0, 0};
daglabel_t *op, *lx;
int i;
dag_make_children (dag, s, operands, children);
if (s->type == st_flow)
for (i = 0; i < 3; i++)
if (children[i])
dag_make_var_live (live_vars, operands[i]);
op = opcode_label (dag, s->opcode, s->expr);
n = children[0];
if (s->type != st_assign
&& !(n = dagnode_search (dag, op, children))) {
n = new_node (dag);
n->type = s->type;
n->label = op;
dagnode_add_children (dag, n, operands, children);
dagnode_set_edges (dag, n);
}
lx = operand_label (dag, operands[0]);
if (lx && lx->dagnode != n) {
lx->expr = s->expr;
dagnode_attach_label (n, lx);
}
if (n->type == st_func)
dag_set_live_vars (live_vars, dag, n);
if (n->type == st_ptrassign)
dag_kill_nodes (dag, n);
}
nodes = malloc (dag->num_nodes * sizeof (dagnode_t *));
memcpy (nodes, dag->nodes, dag->num_nodes * sizeof (dagnode_t *));
dag->nodes = nodes;
labels = malloc (dag->num_labels * sizeof (daglabel_t *));
memcpy (labels, dag->labels, dag->num_labels * sizeof (daglabel_t *));
dag->labels = labels;
dag_remove_dead_vars (dag, live_vars);
dag_sort_nodes (dag);
set_delete (live_vars);
return dag;
}
static statement_t *
build_statement (const char *opcode, operand_t **operands, expr_t *expr)
{
int i;
operand_t *op;
statement_t *st = new_statement (st_none, opcode, expr);
for (i = 0; i < 3; i++) {
if ((op = operands[i])) {
while (op->op_type == op_alias)
op = op->o.alias;
if (op->op_type == op_temp) {
while (op->o.tempop.alias)
op = op->o.tempop.alias;
op->o.tempop.users++;
}
}
}
st->opa = operands[0];
st->opb = operands[1];
st->opc = operands[2];
return st;
}
#if 0
static void
dag_calc_node_costs (dagnode_t *dagnode)
{
int i;
for (i = 0; i < 3; i++)
if (dagnode->children[i])
dag_calc_node_costs (dagnode->children[i]);
// if dagnode->a is null, then this is a leaf (as b and c are guaranted to
// be null)
if (!dagnode->children[0]) {
// Because qc vm statements don't mix source and destination operands,
// leaves never need temporary variables.
dagnode->cost = 0;
} else {
int different = 0;
// a non-leaf is guaranteed to have a valid first child
dagnode->cost = dagnode->children[0]->cost;
for (i = 1; i < 3; i++) {
if (dagnode->children[i]
&& dagnode->children[i]->cost != dagnode->cost) {
dagnode->cost = max (dagnode->cost,
dagnode->children[i]->cost);
different = 1;
}
}
if (!different)
dagnode->cost += 1;
}
}
#endif
static operand_t *
fix_op_type (operand_t *op, etype_t type)
{
if (op && op->op_type != op_label && op->type != type)
op = alias_operand (type, op);
return op;
}
static operand_t *
make_operand (dag_t *dag, sblock_t *block, const dagnode_t *dagnode, int index)
{
operand_t *op;
op = dagnode->children[index]->value;
op = fix_op_type (op, dagnode->types[index]);
return op;
}
static operand_t *
generate_moves (dag_t *dag, sblock_t *block, dagnode_t *dagnode)
{
set_iter_t *var_iter;
daglabel_t *var;
operand_t *operands[3] = {0, 0, 0};
statement_t *st;
operand_t *dst;
operands[0] = make_operand (dag, block, dagnode, 0);
operands[1] = make_operand (dag, block, dagnode, 1);
dst = operands[0];
for (var_iter = set_first (dagnode->identifiers); var_iter;
var_iter = set_next (var_iter)) {
var = dag->labels[var_iter->value];
operands[2] = var->op;
dst = operands[2];
st = build_statement ("<MOVE>", operands, var->expr);
sblock_add_statement (block, st);
}
return dst;
}
static operand_t *
generate_assignments (dag_t *dag, sblock_t *block, operand_t *src,
set_iter_t *var_iter, etype_t type)
{
statement_t *st;
operand_t *dst = 0;
operand_t *operands[3] = {0, 0, 0};
daglabel_t *var;
operands[0] = fix_op_type (src, type);
for ( ; var_iter; var_iter = set_next (var_iter)) {
var = dag->labels[var_iter->value];
operands[1] = fix_op_type (var->op, type);
if (!dst)
dst = operands[1];
st = build_statement ("=", operands, var->expr);
sblock_add_statement (block, st);
}
return dst;
}
static void
dag_gencode (dag_t *dag, sblock_t *block, dagnode_t *dagnode)
{
operand_t *operands[3] = {0, 0, 0};
operand_t *dst = 0;
statement_t *st;
set_iter_t *var_iter;
int i;
etype_t type;
switch (dagnode->type) {
case st_none:
if (!dagnode->label->op)
internal_error (0, "non-leaf label in leaf node");
dst = dagnode->label->op;
if ((var_iter = set_first (dagnode->identifiers))) {
type = dst->type;
dst = generate_assignments (dag, block, dst, var_iter, type);
}
break;
case st_expr:
operands[0] = make_operand (dag, block, dagnode, 0);
if (dagnode->children[1])
operands[1] = make_operand (dag, block, dagnode, 1);
type = low_level_type (get_type (dagnode->label->expr));
if (!(var_iter = set_first (dagnode->identifiers))) {
operands[2] = temp_operand (get_type (dagnode->label->expr));
} else {
daglabel_t *var = dag->labels[var_iter->value];
operands[2] = fix_op_type (var->op, type);
var_iter = set_next (var_iter);
}
dst = operands[2];
st = build_statement (dagnode->label->opcode, operands,
dagnode->label->expr);
sblock_add_statement (block, st);
generate_assignments (dag, block, operands[2], var_iter, type);
break;
case st_assign:
internal_error (0, "unexpected assignment node");
case st_ptrassign:
operands[0] = make_operand (dag, block, dagnode, 0);
operands[1] = make_operand (dag, block, dagnode, 1);
if (dagnode->children[2])
operands[2] = make_operand (dag, block, dagnode, 2);
st = build_statement (dagnode->label->opcode, operands,
dagnode->label->expr);
sblock_add_statement (block, st);
// the source location is suitable for use in other nodes
dst = operands[0];
break;
case st_move:
if (!strcmp (dagnode->label->opcode, "<MOVE>")) {
dst = generate_moves (dag, block, dagnode);
break;
}
//fall through
case st_state:
case st_func:
for (i = 0; i < 3; i++)
if (dagnode->children[i])
operands[i] = make_operand (dag, block, dagnode, i);
st = build_statement (dagnode->label->opcode, operands,
dagnode->label->expr);
sblock_add_statement (block, st);
break;
case st_flow:
operands[0] = make_operand (dag, block, dagnode, 0);
if (dagnode->children[1])
operands[1] = make_operand (dag, block, dagnode, 1);
st = build_statement (dagnode->label->opcode, operands,
dagnode->label->expr);
sblock_add_statement (block, st);
break;
}
dagnode->value = dst;
}
void
dag_generate (dag_t *dag, sblock_t *block)
{
int i;
for (i = 0; i < dag->num_nodes; i++)
dag_gencode (dag, block, dag->nodes[dag->topo[i]]);
}

View file

@ -40,6 +40,7 @@
#include <stdlib.h>
#include <ctype.h>
#include "QF/alloc.h"
#include "QF/pr_comp.h"
#include "debug.h"
@ -53,7 +54,7 @@ int lineno_base;
static srcline_t *free_srclines;
void
static void
push_source_file (void)
{
srcline_t *srcline;
@ -64,7 +65,7 @@ push_source_file (void)
pr.srcline_stack = srcline;
}
void
static void
pop_source_file (void)
{
srcline_t *tmp;
@ -75,8 +76,7 @@ pop_source_file (void)
}
tmp = pr.srcline_stack;
pr.srcline_stack = tmp->next;
tmp->next = free_srclines;
free_srclines = tmp;
FREE (srclines, tmp);
}
void
@ -125,14 +125,3 @@ new_lineno (void)
memset (&pr.linenos[pr.num_linenos], 0, sizeof (pr_lineno_t));
return &pr.linenos[pr.num_linenos++];
}
ddef_t *
new_local (void)
{
if (pr.num_locals == pr.locals_size) {
pr.locals_size += 1024;
pr.locals = realloc (pr.locals, pr.locals_size * sizeof (ddef_t));
}
memset (&pr.locals[pr.num_locals], 0, sizeof (ddef_t));
return &pr.locals[pr.num_locals++];
}

View file

@ -40,9 +40,10 @@
#endif
#include <stdlib.h>
#include <QF/hash.h>
#include <QF/sys.h>
#include <QF/va.h>
#include "QF/alloc.h"
#include "QF/hash.h"
#include "QF/sys.h"
#include "QF/va.h"
#include "qfcc.h"
#include "def.h"
@ -53,6 +54,7 @@
#include "function.h"
#include "options.h"
#include "reloc.h"
#include "shared.h"
#include "strpool.h"
#include "struct.h"
#include "symtab.h"
@ -65,28 +67,38 @@ static void
set_storage_bits (def_t *def, storage_class_t storage)
{
switch (storage) {
case st_system:
case sc_system:
def->system = 1;
// fall through
case st_global:
case sc_global:
def->global = 1;
def->external = 0;
def->local = 0;
def->param = 0;
break;
case st_extern:
case sc_extern:
def->global = 1;
def->external = 1;
def->local = 0;
def->param = 0;
break;
case st_static:
case sc_static:
def->external = 0;
def->global = 0;
def->local = 0;
def->param = 0;
break;
case st_local:
case sc_local:
def->external = 0;
def->global = 0;
def->local = 1;
def->param = 0;
break;
case sc_param:
def->external = 0;
def->global = 0;
def->local = 1;
def->param = 1;
break;
}
def->initialized = 0;
@ -111,6 +123,11 @@ new_def (const char *name, type_t *type, defspace_t *space,
set_storage_bits (def, storage);
if (space) {
if (space->type == ds_virtual && storage == sc_static)
internal_error (0, "static in a virtual space");
if (space->type != ds_virtual
&& (storage == sc_param || storage == sc_local))
internal_error (0, "param or local in a non-virtual space");
def->space = space;
*space->def_tail = def;
space->def_tail = &def->next;
@ -119,10 +136,10 @@ new_def (const char *name, type_t *type, defspace_t *space,
if (!type)
return def;
if (!space && storage != st_extern)
if (!space && storage != sc_extern)
internal_error (0, "non-external def with no storage space");
if (storage != st_extern) {
if (storage != sc_extern) {
int size = type_size (type);
if (!size) {
error (0, "%s has incomplete type", name);
@ -135,24 +152,37 @@ new_def (const char *name, type_t *type, defspace_t *space,
}
def_t *
alias_def (def_t *def, type_t *type)
alias_def (def_t *def, type_t *type, int offset)
{
def_t *alias;
if (def->alias) {
expr_t e;
e.file = def->file;
e.line = def->line;
bug (&e, "aliasing an alias def");
internal_error (&e, "aliasing an alias def");
}
if (type_size (type) > type_size (def->type))
internal_error (0, "aliasing a def to a larger type");
if (offset < 0 || offset + type_size (type) > type_size (def->type))
internal_error (0, "invalid alias offset");
if (type == def->type)
return def;
for (alias = def->alias_defs; alias; alias = alias->next) {
if (alias->type == type && alias->offset == offset)
return alias;
}
ALLOC (16384, def_t, defs, alias);
alias->name = save_string (va ("[%s:%d]", def->name, offset));
alias->return_addr = __builtin_return_address (0);
alias->offset = def->offset;
alias->offset = offset;
alias->offset_reloc = 1;
alias->type = type;
alias->alias = def;
alias->line = pr.source_line;
alias->file = pr.source_file;
alias->next = def->alias_defs;
def->alias_defs = alias;
return alias;
}
@ -162,24 +192,50 @@ temp_def (etype_t type, int size)
def_t *temp;
defspace_t *space = current_func->symtab->space;
ALLOC (16384, def_t, defs, temp);
if ((temp = current_func->temp_defs[size - 1])) {
current_func->temp_defs[size - 1] = temp->temp_next;
temp->temp_next = 0;
} else {
ALLOC (16384, def_t, defs, temp);
temp->offset = defspace_alloc_loc (space, size);
*space->def_tail = temp;
space->def_tail = &temp->next;
temp->name = save_string (va (".tmp%d", current_func->temp_num++));
}
temp->return_addr = __builtin_return_address (0);
temp->name = save_string (va (".tmp%d", current_func->temp_num++));
temp->type = ev_types[type];
temp->file = pr.source_file;
temp->line = pr.source_line;
set_storage_bits (temp, st_local);
temp->offset = defspace_alloc_loc (space, size);
set_storage_bits (temp, sc_local);
temp->space = space;
*space->def_tail = temp;
space->def_tail = &temp->next;
return temp;
}
void
free_temp_def (def_t *temp)
{
int size = type_size (temp->type) - 1;
temp->temp_next = current_func->temp_defs[size];
current_func->temp_defs[size] = temp;
}
void
free_def (def_t *def)
{
if (!def->alias && def->space) {
if (!def->return_addr)
internal_error (0, "double free of def");
if (def->alias) {
def_t **a;
// pull the alias out of the base def's list of alias defs to avoid
// messing up the list when the alias def is freed.
for (a = &def->alias->alias_defs; *a; a = &(*a)->next) {
if (*a == def) {
*a = def->next;
break;
}
}
} else if (def->space) {
def_t **d;
for (d = &def->space->defs; *d && *d != def; d = &(*d)->next)
@ -192,8 +248,9 @@ free_def (def_t *def)
if (!def->external)
defspace_free_loc (def->space, def->offset, type_size (def->type));
}
def->next = free_defs;
free_defs = def;
def->return_addr = 0;
def->free_addr = __builtin_return_address (0);
FREE (defs, def);
}
void
@ -284,7 +341,7 @@ init_elements (struct def_s *def, expr_t *eles)
reloc_def_op (c->e.labelref.label, &loc);
continue;
} else if (c->type == ex_value) {
if (c->e.value.type == ev_integer
if (c->e.value->type == ev_integer
&& elements[i].type->type == ev_float)
convert_int (c);
if (get_type (c) != elements[i].type) {
@ -306,11 +363,11 @@ init_elements (struct def_s *def, expr_t *eles)
} else {
if (c->type != ex_value)
internal_error (c, "bogus expression type in init_elements()");
if (c->e.value.type == ev_string) {
if (c->e.value->type == ev_string) {
EMIT_STRING (def->space, g->string_var,
c->e.value.v.string_val);
c->e.value->v.string_val);
} else {
memcpy (g, &c->e.value.v, type_size (get_type (c)) * 4);
memcpy (g, &c->e.value->v, type_size (get_type (c)) * 4);
}
}
}
@ -341,12 +398,12 @@ init_vector_components (symbol_t *vector_sym, int is_field)
expr = sym->s.expr;
if (is_field) {
if (expr->type != ex_value
|| expr->e.value.type != ev_field) {
|| expr->e.value->type != ev_field) {
error (0, "%s redefined", name);
sym = 0;
} else {
expr->e.value.v.pointer.def = vector_sym->s.def;
expr->e.value.v.pointer.val = i;
expr->e.value->v.pointer.def = vector_sym->s.def;
expr->e.value->v.pointer.val = i;
}
}
}
@ -397,7 +454,7 @@ init_field_def (def_t *def, expr_t *init, storage_class_t storage)
field_def = field_sym->s.def;
if (!field_sym->table)
symtab_addsymbol (pr.entity_fields, field_sym);
if (storage != st_extern) {
if (storage != sc_extern) {
D_INT (def) = field_def->offset;
reloc_def_field (field_def, def);
def->constant = 1;
@ -436,7 +493,7 @@ initialize_def (symbol_t *sym, type_t *type, expr_t *init, defspace_t *space,
// is var and same type
if (!check->s.def)
internal_error (0, "half defined var");
if (storage == st_extern) {
if (storage == sc_extern) {
if (init)
warning (0, "initializing external variable");
return;
@ -451,7 +508,7 @@ initialize_def (symbol_t *sym, type_t *type, expr_t *init, defspace_t *space,
sym->type = type;
if (!sym->table)
symtab_addsymbol (current_symtab, sym);
// if (storage == st_global && init && is_scalar (type)) {
// if (storage == sc_global && init && is_scalar (type)) {
// sym->sy_type = sy_const;
// memset (&sym->s.value, 0, sizeof (&sym->s.value));
// if (init->type != ex_value) { //FIXME arrays/structs
@ -474,9 +531,9 @@ initialize_def (symbol_t *sym, type_t *type, expr_t *init, defspace_t *space,
}
if (type == &type_vector && options.code.vector_components)
init_vector_components (sym, 0);
if (type->type == ev_field && storage != st_local)
if (type->type == ev_field && storage != sc_local && storage != sc_param)
init_field_def (sym->s.def, init, storage);
if (storage == st_extern) {
if (storage == sc_extern) {
if (init)
warning (0, "initializing external variable");
return;
@ -491,12 +548,14 @@ initialize_def (symbol_t *sym, type_t *type, expr_t *init, defspace_t *space,
if ((is_array (type) || is_struct (type))
&& init->type == ex_block && !init->e.block.result) {
init_elements (sym->s.def, init);
sym->s.def->initialized = 1;
} else {
if (!type_assignable (type, get_type (init))) {
error (init, "type mismatch in initializer");
return;
}
if (local_expr) {
sym->s.def->initialized = 1;
init = assign_expr (new_symbol_expr (sym), init);
// fold_constants takes care of int/float conversions
append_expr (local_expr, fold_constants (init));
@ -505,21 +564,21 @@ initialize_def (symbol_t *sym, type_t *type, expr_t *init, defspace_t *space,
error (0, "non-constant initializier");
return;
}
if (init->e.value.type == ev_pointer
|| init->e.value.type == ev_field) {
if (init->e.value->type == ev_pointer
|| init->e.value->type == ev_field) {
// FIXME offset pointers
D_INT (sym->s.def) = init->e.value.v.pointer.val;
if (init->e.value.v.pointer.def)
reloc_def_field (init->e.value.v.pointer.def, sym->s.def);
D_INT (sym->s.def) = init->e.value->v.pointer.val;
if (init->e.value->v.pointer.def)
reloc_def_field (init->e.value->v.pointer.def, sym->s.def);
} else {
ex_value_t v = init->e.value;
ex_value_t *v = init->e.value;
if (is_scalar (sym->type))
convert_value (&v, sym->type);
if (v.type == ev_string) {
v = convert_value (v, sym->type);
if (v->type == ev_string) {
EMIT_STRING (sym->s.def->space, D_STRING (sym->s.def),
v.v.string_val);
v->v.string_val);
} else {
memcpy (D_POINTER (void, sym->s.def), &v.v,
memcpy (D_POINTER (void, sym->s.def), &v->v,
type_size (type) * sizeof (pr_type_t));
}
}
@ -527,4 +586,78 @@ initialize_def (symbol_t *sym, type_t *type, expr_t *init, defspace_t *space,
sym->s.def->nosave = 1;
}
}
sym->s.def->initializer = init;
}
int
def_overlap (def_t *d1, def_t *d2)
{
int offs1, size1;
int offs2, size2;
defspace_t *s1 = d1->space;
defspace_t *s2 = d2->space;
if (d1->alias)
s1 = d1->alias->space;
if (d2->alias)
s2 = d2->alias->space;
/// Defs in different spaces never overlap.
if (s1 != s2)
return 0;
offs1 = d1->offset;
if (d1->alias)
offs1 += d1->alias->offset;
size1 = type_size (d1->type);
offs2 = d2->offset;
if (d2->alias)
offs2 += d2->alias->offset;
size2 = type_size (d2->type);
if (offs1 <= offs2 && offs1 + size1 >= offs2 + size2)
return 2; // d1 fully overlaps d2
if (offs1 < offs2 + size2 && offs2 < offs1 + size1)
return 1; // d1 and d2 at least partially overlap
return 0;
}
int
def_offset (def_t *def)
{
int offset = def->offset;
if (def->alias)
offset += def->alias->offset;
return offset;
}
int
def_size (def_t *def)
{
return type_size (def->type);
}
int
def_visit_all (def_t *def, int overlap,
int (*visit) (def_t *, void *), void *data)
{
def_t *start_def = def;
int ret;
if ((ret = visit (def, data)))
return ret;
if (def->alias) {
def = def->alias;
if ((ret = visit (def, data)))
return ret;
}
for (def = def->alias_defs; def; def = def->next) {
if (def == start_def)
continue;
if (overlap && def_overlap (def, start_def) < overlap)
continue;
if ((ret = visit (def, data)))
return ret;
}
return 0;
}

View file

@ -39,9 +39,10 @@
#endif
#include <stdlib.h>
#include <QF/hash.h>
#include <QF/sys.h>
#include <QF/va.h>
#include "QF/alloc.h"
#include "QF/hash.h"
#include "QF/sys.h"
#include "QF/va.h"
#include "qfcc.h"
#include "defspace.h"
@ -65,30 +66,50 @@ static locref_t *free_locrefs;
#define GROW 1024
static int
grow_space (defspace_t *space)
grow_space_global (defspace_t *space)
{
int size;
if (space->size >= space->size) {
size = space->size + GROW;
size -= size % GROW;
} else {
size = space->max_size + GROW;
}
if (space->size <= space->max_size)
return 1;
size = space->size + GROW;
size -= size % GROW;
space->data = realloc (space->data, size * sizeof (pr_type_t));
memset (space->data + space->max_size, 0, GROW * sizeof (pr_type_t));
space->max_size = size;
return 1;
}
static int
grow_space_virtual (defspace_t *space)
{
int size;
if (space->size <= space->max_size)
return 1;
size = space->size + GROW;
size -= size % GROW;
space->max_size = size;
return 1;
}
defspace_t *
defspace_new (void)
defspace_new (ds_type_t type)
{
defspace_t *space;
ALLOC (1024, defspace_t, spaces, space);
space->def_tail = &space->defs;
space->grow = grow_space;
space->type = type;
if (type == ds_backed) {
space->grow = grow_space_global;
} else if (type == ds_virtual) {
space->grow = grow_space_virtual;
} else {
internal_error (0, "unknown defspace type");
}
return space;
}
@ -99,6 +120,8 @@ defspace_alloc_loc (defspace_t *space, int size)
locref_t *loc;
locref_t **l = &space->free_locs;
if (size <= 0)
internal_error (0, "invalid number of words requested: %d", size);
while (*l && (*l)->size < size)
l = &(*l)->next;
if ((loc = *l)) {
@ -106,8 +129,7 @@ defspace_alloc_loc (defspace_t *space, int size)
if ((*l)->size == size) {
loc = *l;
*l = (*l)->next;
loc->next = free_locrefs;
free_locrefs = loc;
FREE (locrefs, loc);
} else {
(*l)->ofs += size;
(*l)->size -= size;
@ -117,11 +139,8 @@ defspace_alloc_loc (defspace_t *space, int size)
ofs = space->size;
space->size += size;
if (space->size > space->max_size) {
if (!space->grow) {
error (0, "unable to allocate %d globals", size);
exit (1);
}
space->grow (space);
if (!space->grow || !space->grow (space))
internal_error (0, "unable to allocate %d words", size);
}
return ofs;
}
@ -132,8 +151,8 @@ defspace_free_loc (defspace_t *space, int ofs, int size)
locref_t **l;
locref_t *loc;
if (!size)
internal_error (0, "defspace: freeing size 0 location");
if (size <= 0)
internal_error (0, "defspace: freeing size %d location", size);
if (ofs < 0 || ofs >= space->size || ofs + size > space->size)
internal_error (0, "defspace: freeing bogus location %d:%d",
ofs, size);
@ -165,8 +184,7 @@ defspace_free_loc (defspace_t *space, int ofs, int size)
loc->size += loc->next->size;
loc = loc->next;
*l = loc->next;
loc->next = free_locrefs;
free_locrefs = loc;
FREE (locrefs, loc);
}
return;
}

View file

@ -79,6 +79,6 @@ disassemble_progs (progs_t *pr)
Sys_Printf ("%s:\n", PR_GetString (pr, desc->s_name));
pr->pr_xfunction = &func;
}
PR_PrintStatement (pr, &pr->pr_statements[i], 2);
PR_PrintStatement (pr, &pr->pr_statements[i], 2 | (verbosity > 1));
}
}

53
tools/qfcc/source/dot.c Normal file
View file

@ -0,0 +1,53 @@
/*
dot.c
General dot output support
Copyright (C) 2012 Bill Currie <bill@taniwha.org>
Author: Bill Currie <bill@taniwha.org>
Date: 2012/11/07
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to:
Free Software Foundation, Inc.
59 Temple Place - Suite 330
Boston, MA 02111-1307, USA
*/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include <stdlib.h>
#include "QF/va.h"
#include "dot.h"
#include "function.h"
#include "qfcc.h"
#include "strpool.h"
void
dump_dot (const char *stage, void *data,
void (*dump_func) (void *data, const char *fname))
{
char *fname;
fname = nva ("%s.%s.%s.dot", GETSTR (pr.source_file), current_func->name,
stage);
dump_func (data, fname);
free (fname);
}

194
tools/qfcc/source/dot_dag.c Normal file
View file

@ -0,0 +1,194 @@
/*
dot_dag.c
Output dags to dot (graphvis).
Copyright (C) 2012 Bill Currie <bill@taniwha.org>
Author: Bill Currie <bill@taniwha.org>
Date: 2012/05/08
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to:
Free Software Foundation, Inc.
59 Temple Place - Suite 330
Boston, MA 02111-1307, USA
*/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#ifdef HAVE_STRING_H
# include <string.h>
#endif
#ifdef HAVE_STRINGS_H
# include <strings.h>
#endif
#include <stdlib.h>
#include "QF/dstring.h"
#include "QF/quakeio.h"
#include "QF/set.h"
#include "QF/va.h"
#include "dags.h"
#include "statements.h"
#include "strpool.h"
#include "symtab.h"
#include "type.h"
static void
print_node_def (dstring_t *dstr, dag_t *dag, dagnode_t *node)
{
set_iter_t *id_iter;
daglabel_t *id;
dasprintf (dstr, " \"dagnode_%p\" [%slabel=\"%s%s (%d)", node,
node->type != st_none ? "" : "shape=box,",
daglabel_string (node->label),
node->killed ? " k" : "",
node->topo);
for (id_iter = set_first (node->identifiers); id_iter;
id_iter = set_next (id_iter)) {
id = dag->labels[id_iter->value];
dasprintf (dstr, "\\n%s", daglabel_string(id));
}
dasprintf (dstr, "\"];\n");
}
static void
print_root_nodes (dstring_t *dstr, dag_t *dag)
{
set_iter_t *node_iter;
// dasprintf (dstr, " subgraph roots_%p {", dag);
// dasprintf (dstr, " rank=same;");
for (node_iter = set_first (dag->roots); node_iter;
node_iter = set_next (node_iter)) {
dagnode_t *node = dag->nodes[node_iter->value];
print_node_def (dstr, dag, node);
dasprintf (dstr, " dag_enter_%p ->dagnode_%p [style=invis];\n",
dag, node);
}
// dasprintf (dstr, " }\n");
}
static void
print_child_nodes (dstring_t *dstr, dag_t *dag)
{
int i;
dagnode_t *node;
for (i = 0; i < dag->num_nodes; i++) {
node = dag->nodes[i];
if (!set_is_empty (node->parents))
print_node_def (dstr, dag, node);
}
}
static void
print_node (dstring_t *dstr, dag_t *dag, dagnode_t *node)
{
int i;
set_t *edges = set_new ();
set_iter_t *edge_iter;
set_assign (edges, node->edges);
for (i = 0; i < 3; i++) {
if (node->children[i]) {
set_remove (edges, node->children[i]->number);
dasprintf (dstr,
" \"dagnode_%p\" -> \"dagnode_%p\" [label=%c];\n",
node, node->children[i], i + 'a');
}
}
for (edge_iter = set_first (edges); edge_iter;
edge_iter = set_next (edge_iter)) {
dasprintf (dstr,
" \"dagnode_%p\" -> \"dagnode_%p\" [style=dashed];\n",
node, dag->nodes[edge_iter->value]);
}
set_delete (edges);
if (0 && !set_is_empty (node->identifiers)) {
set_iter_t *id_iter;
daglabel_t *id;
dasprintf (dstr, " \"dagnode_%p\" -> \"dagid_%p\" "
"[style=dashed,dir=none];\n", node, node);
dasprintf (dstr, " \"dagid_%p\" [shape=none,label=<\n", node);
dasprintf (dstr, " <table border=\"0\" cellborder=\"1\" "
"cellspacing=\"0\">\n");
dasprintf (dstr, " <tr>\n");
dasprintf (dstr, " <td>");
for (id_iter = set_first (node->identifiers); id_iter;
id_iter = set_next (id_iter)) {
id = dag->labels[id_iter->value];
dasprintf (dstr, " %s", html_string (daglabel_string(id)));
}
dasprintf (dstr, " </td>");
dasprintf (dstr, " </tr>\n");
dasprintf (dstr, " </table>>];\n");
}
if (set_is_empty (node->edges))
dasprintf (dstr,
" \"dagnode_%p\" -> \"dag_leave_%p\" [style=invis];\n",
node, dag);
}
void
print_dag (dstring_t *dstr, dag_t *dag, const char *label)
{
int i;
if (label) {
dasprintf (dstr, " subgraph cluster_dag_%p {\n", dag);
dasprintf (dstr, " label=\"%s\";\n", label);
}
dasprintf (dstr, " dag_enter_%p [label=\"\", style=invis];\n", dag);
print_root_nodes (dstr, dag);
print_child_nodes (dstr, dag);
for (i = 0; i < dag->num_nodes; i++)
print_node (dstr, dag, dag->nodes[i]);
dasprintf (dstr, " dag_leave_%p [label=\"\", style=invis];\n", dag);
if (label)
dasprintf (dstr, " }\n");
}
void
dot_dump_dag (void *_dag, const char *filename)
{
dag_t *dag = _dag;
dstring_t *dstr = dstring_newstr();
dasprintf (dstr, "digraph dag_%p {\n", dag);
dasprintf (dstr, " layout=dot;\n");
dasprintf (dstr, " clusterrank=local;\n");
dasprintf (dstr, " rankdir=TB;\n");
dasprintf (dstr, " compound=true;\n");
print_dag (dstr, dag, "");
dasprintf (dstr, "}\n");
if (filename) {
QFile *file;
file = Qopen (filename, "wt");
Qwrite (file, dstr->str, dstr->size - 1);
Qclose (file);
} else {
fputs (dstr->str, stdout);
}
dstring_delete (dstr);
}

View file

@ -40,12 +40,31 @@
#include <stdlib.h>
#include <QF/dstring.h>
#include <QF/mathlib.h>
#include <QF/quakeio.h>
#include <QF/va.h>
#include "expr.h"
#include "symtab.h"
#include "type.h"
#include "qc-parse.h"
#include "strpool.h"
static const char *expr_names[] =
{
"error",
"state",
"bool",
"label",
"labelref",
"block",
"expr",
"uexpr",
"symbol",
"temp",
"nil",
"value",
};
const char *
get_op_string (int op)
@ -87,120 +106,163 @@ get_op_string (int op)
case 'A': return "<alias>";
case 'C': return "<cast>";
case 'M': return "<move>";
case 'm': return "<move>";
default:
return "unknown";
}
}
typedef void (*print_f) (expr_t *, int, int);
static void _print_expr (expr_t *e, int level, int id);
typedef void (*print_f) (dstring_t *dstr, expr_t *, int, int, expr_t *);
static void _print_expr (dstring_t *dstr, expr_t *e, int level, int id,
expr_t *next);
static void
print_error (expr_t *e, int level, int id)
print_error (dstring_t *dstr, expr_t *e, int level, int id, expr_t *next)
{
int indent = level * 2 + 2;
printf ("%*s\"e_%p\" [label=\"(error)\\n%d\"];\n", indent, "", e, e->line);
dasprintf (dstr, "%*se_%p [label=\"(error)\\n%d\"];\n", indent, "", e,
e->line);
}
static void
print_state (expr_t *e, int level, int id)
print_state (dstring_t *dstr, expr_t *e, int level, int id, expr_t *next)
{
int indent = level * 2 + 2;
_print_expr (e->e.state.frame, level, id);
_print_expr (e->e.state.think, level, id);
_print_expr (dstr, e->e.state.frame, level, id, next);
_print_expr (dstr, e->e.state.think, level, id, next);
if (e->e.state.step)
_print_expr (e->e.state.step, level, id);
printf ("%*s\"e_%p\":f -> \"e_%p\";\n", indent, "", e, e->e.state.frame);
printf ("%*s\"e_%p\":t -> \"e_%p\";\n", indent, "", e, e->e.state.think);
_print_expr (dstr, e->e.state.step, level, id, next);
dasprintf (dstr, "%*se_%p:f -> \"e_%p\";\n", indent, "", e,
e->e.state.frame);
dasprintf (dstr, "%*se_%p:t -> \"e_%p\";\n", indent, "", e,
e->e.state.think);
if (e->e.state.step)
printf ("%*s\"e_%p\":s -> \"e_%p\";\n", indent, "", e,
e->e.state.step);
printf ("%*s\"e_%p\" [label=\"<f>state|<t>think|<s>step\","
"shape=record];\n", indent, "", e);
dasprintf (dstr, "%*se_%p:s -> e_%p;\n", indent, "", e,
e->e.state.step);
dasprintf (dstr,
"%*se_%p [label=\"<f>state|<t>think|<s>step\",shape=record];\n",
indent, "", e);
}
static void
print_bool (expr_t *e, int level, int id)
print_bool (dstring_t *dstr, expr_t *e, int level, int id, expr_t *next)
{
int indent = level * 2 + 2;
int i, count;
int tl_count = 0, fl_count = 0;
ex_bool_t *bool = &e->e.bool;
_print_expr (e->e.bool.e, level, id);
if (e->e.bool.e->type == ex_block && e->e.bool.e->e.block.head) {
expr_t *se;
printf ("%*s\"e_%p\" -> \"e_%p\";\n", indent, "", e, e->e.bool.e);
se = (expr_t *) e->e.bool.e->e.block.tail;
if (se && se->type == ex_label && e->next)
printf ("%*s\"e_%p\" -> \"e_%p\" "
"[constraint=false,style=dashed];\n", indent, "",
se, e->next);
} else {
printf ("%*s\"e_%p\" -> \"e_%p\";\n", indent, "", e, e->e.bool.e);
}
printf ("%*s\"e_%p\" [label=\"<bool>\\n%d\"];\n", indent, "", e, e->line);
dasprintf (dstr, "%*se_%p [shape=none,label=<\n", indent, "", e);
dasprintf (dstr, "%*s<table border=\"0\" cellborder=\"1\" "
"cellspacing=\"0\">\n",
indent + 2, "");
dasprintf (dstr, "%*s<tr><td colspan=\"2\">&lt;bool&gt;(%d)</td></tr>\n",
indent + 4, "", e->line);
dasprintf (dstr, "%*s<tr><td>true</td><td>false</td></tr>\n",
indent + 4, "");
if (bool->true_list)
tl_count = bool->true_list->size;
if (bool->false_list)
fl_count = bool->false_list->size;
count = min (tl_count, fl_count);
for (i = 0; i < count; i++)
dasprintf (dstr, "%*s<tr><td port=\"t%d\">t</td>"
"<td port=\"f%d\">f</td></tr>\n", indent, "", i, i);
for ( ; i < tl_count; i++)
dasprintf (dstr, "%*s<tr><td port=\"t%d\">t</td>%s</tr>\n",
indent, "", i,
i == count ? va ("<td rowspan=\"%d\"></td>",
bool->true_list->size - count)
: "");
for ( ; i < fl_count; i++)
dasprintf (dstr, "%*s<tr>%s<td port=\"f%d\">f</td></tr>\n",
indent, "",
i == count ? va ("<td rowspan=\"%d\"></td>",
bool->false_list->size - count)
: "",
i);
dasprintf (dstr, "%*s</table>\n", indent + 2, "");
dasprintf (dstr, "%*s>];\n", indent, "");
if (e->next)
next = e->next;
_print_expr (dstr, e->e.bool.e, level, id, next);
for (i = 0; i < tl_count; i++)
dasprintf (dstr, "%*se_%p:t%d -> e_%p;\n", indent, "", e, i,
bool->true_list->e[i]);
for (i = 0; i < fl_count; i++)
dasprintf (dstr, "%*se_%p:f%d -> e_%p;\n", indent, "", e, i,
bool->false_list->e[i]);
dasprintf (dstr, "%*se_%p -> e_%p;\n", indent, "", e, e->e.bool.e);
}
static void
print_label (expr_t *e, int level, int id)
print_label (dstring_t *dstr, expr_t *e, int level, int id, expr_t *next)
{
int indent = level * 2 + 2;
if (e->next)
printf ("%*s\"e_%p\" -> \"e_%p\" "
"[constraint=false,style=dashed];\n", indent, "",
e, e->next);
printf ("%*s\"e_%p\" [label=\"%s\\n%d\"];\n", indent, "", e,
e->e.label.name, e->line);
next = e->next;
if (next)
dasprintf (dstr, "%*se_%p -> e_%p [constraint=false,style=dashed];\n",
indent, "", e, next);
dasprintf (dstr, "%*se_%p [label=\"%s\\n%d\"];\n", indent, "", e,
e->e.label.name, e->line);
}
static void
print_labelref (expr_t *e, int level, int id)
print_labelref (dstring_t *dstr, expr_t *e, int level, int id, expr_t *next)
{
int indent = level * 2 + 2;
if (e->next)
printf ("%*s\"e_%p\" -> \"e_%p\" "
"[constraint=false,style=dashed];\n", indent, "",
e, e->next);
printf ("%*s\"e_%p\" [label=\"&%s\\n%d\"];\n", indent, "", e,
e->e.label.name, e->line);
next = e->next;
if (next)
dasprintf (dstr, "%*se_%p -> e_%p [constraint=false,style=dashed];\n",
indent, "", e, e->next);
dasprintf (dstr, "%*se_%p [label=\"&%s\\n%d\"];\n", indent, "", e,
e->e.label.name, e->line);
}
static void
print_block (expr_t *e, int level, int id)
print_block (dstring_t *dstr, expr_t *e, int level, int id, expr_t *next)
{
int indent = level * 2 + 2;
int i;
expr_t *se;
dasprintf (dstr, "%*se_%p [shape=none,label=<\n", indent, "", e);
dasprintf (dstr, "%*s<table border=\"0\" cellborder=\"1\" "
"cellspacing=\"0\">\n", indent + 2, "");
dasprintf (dstr, "%*s<tr><td colspan=\"2\">&lt;block&gt;(%d)%s</td>"
"</tr>\n", indent + 4, "", e->line,
e->e.block.is_call ? "c" : "");
if (e->e.block.result)
dasprintf (dstr, "%*s<tr><td colspan=\"2\" port=\"result\">=</td>"
"</tr>\n", indent + 4, "");
for (se = e->e.block.head, i = 0; se; se = se->next, i++)
dasprintf (dstr, "%*s<tr><td>%d</td><td port=\"b%d\">%s</td></tr>\n",
indent + 4, "", i, i, expr_names[se->type]);
dasprintf (dstr, "%*s</table>\n", indent + 2, "");
dasprintf (dstr, "%*s>];\n", indent, "");
if (e->e.block.result) {
_print_expr (e->e.block.result, level + 1, id);
printf ("%*s\"e_%p\" -> \"e_%p\";\n", indent, "",
e, e->e.block.result);
_print_expr (dstr, e->e.block.result, level + 1, id, next);
dasprintf (dstr, "%*se_%p:result -> e_%p;\n", indent, "", e,
e->e.block.result);
}
printf ("%*s\"e_%p\" -> \"e_%p\" [style=dashed];\n", indent, "",
e, e->e.block.head);
//printf ("%*ssubgraph cluster_%p {\n", indent, "", e);
for (se = e->e.block.head; se; se = se->next) {
_print_expr (se, level + 1, id);
if (e->next)
next = e->next;
for (se = e->e.block.head, i = 0; se; se = se->next, i++) {
_print_expr (dstr, se, level + 1, id, next);
dasprintf (dstr, "%*se_%p:b%d -> e_%p;\n", indent, "", e,
i, se);
}
for (se = e->e.block.head; se && se->next; se = se->next) {
if ((se->type == ex_uexpr && se->e.expr.op == 'g')
|| se->type == ex_label || se->type == ex_bool)
continue;
printf ("%*s\"e_%p\" -> \"e_%p\" [constraint=false,style=dashed];\n",
indent, "", se, se->next);
}
if (se && se->type == ex_label && e->next)
printf ("%*s\"e_%p\" -> \"e_%p\" [constraint=false,style=dashed];\n",
indent, "", se, e->next);
//printf ("%*s}\n", indent, "");
printf ("%*s\"e_%p\" [label=\"<block>\\n%d\"];\n", indent, "", e, e->line);
}
static void
print_call (expr_t *e, int level, int id)
print_call (dstring_t *dstr, expr_t *e, int level, int id, expr_t *next)
{
int indent = level * 2 + 2;
expr_t *p;
@ -213,147 +275,155 @@ print_call (expr_t *e, int level, int id)
for (i = 0, p = e->e.expr.e2; p; p = p->next, i++)
args[count - 1 - i] = p;
_print_expr (e->e.expr.e1, level, id);
printf ("%*s\"e_%p\" [label=\"<c>call", indent, "", e);
_print_expr (dstr, e->e.expr.e1, level, id, next);
dasprintf (dstr, "%*se_%p [label=\"<c>call", indent, "", e);
for (i = 0; i < count; i++)
printf ("|<p%d>p%d", i, i);
printf ("\",shape=record];\n");
dasprintf (dstr, "|<p%d>p%d", i, i);
dasprintf (dstr, "\",shape=record];\n");
for (i = 0; i < count; i++) {
_print_expr (args[i], level + 1, id);
printf ("%*s\"e_%p\":p%d -> \"e_%p\";\n", indent + 2, "", e, i,
args[i]);
_print_expr (dstr, args[i], level + 1, id, next);
dasprintf (dstr, "%*se_%p:p%d -> e_%p;\n", indent + 2, "", e, i,
args[i]);
}
printf ("%*s\"e_%p\":c -> \"e_%p\";\n", indent, "", e, e->e.expr.e1);
dasprintf (dstr, "%*se_%p:c -> e_%p;\n", indent, "", e, e->e.expr.e1);
}
static void
print_subexpr (expr_t *e, int level, int id)
print_subexpr (dstring_t *dstr, expr_t *e, int level, int id, expr_t *next)
{
int indent = level * 2 + 2;
if (e->e.expr.op == 'c') {
print_call (e, level, id);
print_call (dstr, e, level, id, next);
return;
} else if (e->e.expr.op == 'i' || e->e.expr.op == 'n'
|| e->e.expr.op == IFB || e->e.expr.op ==IFBE
|| e->e.expr.op == IFA || e->e.expr.op ==IFAE) {
_print_expr (e->e.expr.e1, level, id);
printf ("%*s\"e_%p\" -> \"e_%p\" [label=\"t\"];\n", indent, "",
e, e->e.expr.e1);
printf ("%*s\"e_%p\" -> \"e_%p\" [label=\"g\"];\n", indent, "",
e, e->e.expr.e2);
_print_expr (dstr, e->e.expr.e1, level, id, next);
dasprintf (dstr, "%*se_%p -> \"e_%p\" [label=\"t\"];\n", indent, "", e,
e->e.expr.e1);
dasprintf (dstr, "%*se_%p -> \"e_%p\" [label=\"g\"];\n", indent, "", e,
e->e.expr.e2);
if (e->next)
next = e->next;
if (next)
dasprintf (dstr, "%*se_%p -> e_%p [constraint=false,"
"style=dashed];\n", indent, "", e, next);
} else {
_print_expr (e->e.expr.e1, level, id);
_print_expr (e->e.expr.e2, level, id);
printf ("%*s\"e_%p\" -> \"e_%p\" [label=\"l\"];\n", indent, "",
e, e->e.expr.e1);
printf ("%*s\"e_%p\" -> \"e_%p\" [label=\"r\"];\n", indent, "",
e, e->e.expr.e2);
_print_expr (dstr, e->e.expr.e1, level, id, next);
_print_expr (dstr, e->e.expr.e2, level, id, next);
dasprintf (dstr, "%*se_%p -> \"e_%p\" [label=\"l\"];\n", indent, "", e,
e->e.expr.e1);
dasprintf (dstr, "%*se_%p -> \"e_%p\" [label=\"r\"];\n", indent, "", e,
e->e.expr.e2);
}
printf ("%*s\"e_%p\" [label=\"%s\\n%d\"];\n", indent, "", e,
get_op_string (e->e.expr.op), e->line);
dasprintf (dstr, "%*se_%p [label=\"%s\\n%d\"];\n", indent, "", e,
get_op_string (e->e.expr.op), e->line);
}
static void
print_uexpr (expr_t *e, int level, int id)
print_uexpr (dstring_t *dstr, expr_t *e, int level, int id, expr_t *next)
{
int indent = level * 2 + 2;
dstring_t *dstr = dstring_newstr();
dstring_t *typestr = dstring_newstr();
if (e->e.expr.op != 'g')
_print_expr (e->e.expr.e1, level, id);
if (e->e.expr.op != 'g' && e->e.expr.e1)
_print_expr (dstr, e->e.expr.e1, level, id, next);
if (e->e.expr.op == 'A') {
dstring_copystr (dstr, "\\n");
print_type_str (dstr, e->e.expr.type);
dstring_copystr (typestr, "\\n");
print_type_str (typestr, e->e.expr.type);
}
printf ("%*s\"e_%p\" -> \"e_%p\";\n", indent, "", e, e->e.expr.e1);
printf ("%*s\"e_%p\" [label=\"%s%s\\n%d\"];\n", indent, "", e,
get_op_string (e->e.expr.op), dstr->str, e->line);
dstring_delete (dstr);
if (e->e.expr.op != 'r' || e->e.expr.e1)
dasprintf (dstr, "%*se_%p -> \"e_%p\";\n", indent, "", e,
e->e.expr.e1);
dasprintf (dstr, "%*se_%p [label=\"%s%s\\n%d\"];\n", indent, "", e,
get_op_string (e->e.expr.op), typestr->str, e->line);
dstring_delete (typestr);
}
static void
print_symbol (expr_t *e, int level, int id)
print_symbol (dstring_t *dstr, expr_t *e, int level, int id, expr_t *next)
{
int indent = level * 2 + 2;
printf ("%*s\"e_%p\" [label=\"%s\\n%d\"];\n", indent, "", e,
e->e.symbol->name, e->line);
dasprintf (dstr, "%*se_%p [label=\"%s\\n%d\"];\n", indent, "", e,
e->e.symbol->name, e->line);
}
static void
print_temp (expr_t *e, int level, int id)
print_temp (dstring_t *dstr, expr_t *e, int level, int id, expr_t *next)
{
int indent = level * 2 + 2;
printf ("%*s\"e_%p\" [label=\"tmp_%p\\n%d\"];\n", indent, "", e, e,
e->line);
dasprintf (dstr, "%*se_%p [label=\"tmp_%p\\n%d\"];\n", indent, "", e, e,
e->line);
}
static void
print_nil (expr_t *e, int level, int id)
print_nil (dstring_t *dstr, expr_t *e, int level, int id, expr_t *next)
{
int indent = level * 2 + 2;
printf ("%*s\"e_%p\" [label=\"nil\\n%d\"];\n", indent, "", e, e->line);
dasprintf (dstr, "%*se_%p [label=\"nil\\n%d\"];\n", indent, "", e,
e->line);
}
static void
print_value (expr_t *e, int level, int id)
print_value (dstring_t *dstr, expr_t *e, int level, int id, expr_t *next)
{
int indent = level * 2 + 2;
type_t *type;
const char *label = "?!?";
switch (e->e.value.type) {
switch (e->e.value->type) {
case ev_string:
label = va ("\\\"%s\\\"", e->e.value.v.string_val);
label = va ("\\\"%s\\\"", quote_string (e->e.value->v.string_val));
break;
case ev_float:
label = va ("%g", e->e.value.v.float_val);
label = va ("%g", e->e.value->v.float_val);
break;
case ev_vector:
label = va ("'%g %g %g'",
e->e.value.v.vector_val[0],
e->e.value.v.vector_val[1],
e->e.value.v.vector_val[2]);
e->e.value->v.vector_val[0],
e->e.value->v.vector_val[1],
e->e.value->v.vector_val[2]);
break;
case ev_quat:
label = va ("'%g %g %g %g'",
e->e.value.v.quaternion_val[0],
e->e.value.v.quaternion_val[1],
e->e.value.v.quaternion_val[2],
e->e.value.v.quaternion_val[3]);
e->e.value->v.quaternion_val[0],
e->e.value->v.quaternion_val[1],
e->e.value->v.quaternion_val[2],
e->e.value->v.quaternion_val[3]);
break;
case ev_pointer:
type = e->e.value.v.pointer.type;
if (e->e.value.v.pointer.def)
type = e->e.value->v.pointer.type;
if (e->e.value->v.pointer.def)
label = va ("(%s)[%d]<%s>",
type ? pr_type_name[type->type] : "???",
e->e.value.v.pointer.val,
e->e.value.v.pointer.def->name);
e->e.value->v.pointer.val,
e->e.value->v.pointer.def->name);
else
label = va ("(%s)[%d]",
type ? pr_type_name[type->type] : "???",
e->e.value.v.pointer.val);
e->e.value->v.pointer.val);
break;
case ev_field:
label = va ("field %d", e->e.value.v.pointer.val);
label = va ("field %d", e->e.value->v.pointer.val);
break;
case ev_entity:
label = va ("ent %d", e->e.value.v.integer_val);
label = va ("ent %d", e->e.value->v.integer_val);
break;
case ev_func:
label = va ("func %d", e->e.value.v.integer_val);
label = va ("func %d", e->e.value->v.integer_val);
break;
case ev_integer:
label = va ("%d", e->e.value.v.integer_val);
label = va ("i %d", e->e.value->v.integer_val);
break;
case ev_uinteger:
label = va ("%u", e->e.value.v.uinteger_val);
label = va ("u %u", e->e.value->v.uinteger_val);
break;
case ev_short:
label = va ("%d", e->e.value.v.short_val);
label = va ("s %d", e->e.value->v.short_val);
break;
case ev_void:
label = "<void>";
@ -365,12 +435,12 @@ print_value (expr_t *e, int level, int id)
label = "<type_count>";
break;
}
printf ("%*s\"e_%p\" [label=\"%s\\n%d\"];\n", indent, "", e, label,
e->line);
dasprintf (dstr, "%*se_%p [label=\"%s\\n%d\"];\n", indent, "", e, label,
e->line);
}
static void
_print_expr (expr_t *e, int level, int id)
_print_expr (dstring_t *dstr, expr_t *e, int level, int id, expr_t *next)
{
static print_f print_funcs[] = {
print_error,
@ -389,7 +459,7 @@ _print_expr (expr_t *e, int level, int id)
int indent = level * 2 + 2;
if (!e) {
printf ("%*s\"e_%p\" [label=\"(null)\"];\n", indent, "", e);
dasprintf (dstr, "%*s\"e_%p\" [label=\"(null)\"];\n", indent, "", e);
return;
}
if (e->printid == id) // already printed this expression
@ -397,20 +467,40 @@ _print_expr (expr_t *e, int level, int id)
e->printid = id;
if (e->type < 0 || e->type > ex_value) {
printf ("%*s\"e_%p\" [label=\"(bad expr type)\\n%d\"];\n", indent, "",
e, e->line);
dasprintf (dstr, "%*se_%p [label=\"(bad expr type)\\n%d\"];\n",
indent, "", e, e->line);
return;
}
print_funcs [e->type] (e, level, id);
print_funcs [e->type] (dstr, e, level, id, next);
}
void
dump_dot_expr (void *_e, const char *filename)
{
static int id = 0;
dstring_t *dstr = dstring_newstr ();
expr_t *e = (expr_t *) _e;
dasprintf (dstr, "digraph expr_%p {\n", e);
dasprintf (dstr, " layout=dot; rankdir=TB; compound=true;\n");
_print_expr (dstr, e, 0, ++id, 0);
dasprintf (dstr, "}\n");
if (filename) {
QFile *file;
file = Qopen (filename, "wt");
Qwrite (file, dstr->str, dstr->size - 1);
Qclose (file);
} else {
fputs (dstr->str, stdout);
}
dstring_delete (dstr);
}
void
print_expr (expr_t *e)
{
static int id = 0;
printf ("digraph expr_%p {\n", e);
printf (" layout=dot; rankdir=TB; compound=true;\n");
_print_expr (e, 0, ++id);
printf ("}\n");
dump_dot_expr (e, 0);
}

View file

@ -3,10 +3,10 @@
"emit" flow graphs to dot (graphvis).
Copyright (C) 2011 Bill Currie <bill@taniwha.org>
Copyright (C) 2012 Bill Currie <bill@taniwha.org>
Author: Bill Currie <bill@taniwha.org>
Date: 2011/01/21
Date: 2012/11/01
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
@ -39,101 +39,277 @@
#endif
#include <stdlib.h>
#include <QF/dstring.h>
#include <QF/quakeio.h>
#include <QF/va.h>
#include "QF/dstring.h"
#include "QF/quakeio.h"
#include "QF/set.h"
#include "QF/va.h"
#include "dags.h"
#include "flow.h"
#include "function.h"
#include "expr.h"
#include "statements.h"
#include "strpool.h"
#include "symtab.h"
#include "type.h"
typedef struct {
const char *type;
void (*print_node) (dstring_t *, flowgraph_t *, flownode_t *, int);
void (*print_edge) (dstring_t *, flowgraph_t *, flowedge_t *, int);
} flow_dot_t;
static void
flow_statement (dstring_t *dstr, statement_t *s)
print_flow_node (dstring_t *dstr, flowgraph_t *graph, flownode_t *node,
int level)
{
dasprintf (dstr, " <tr>");
dasprintf (dstr, "<td>%s</td>", html_string(quote_string (s->opcode)));
dasprintf (dstr, "<td>%s</td>", html_string(operand_string (s->opa)));
dasprintf (dstr, "<td>%s</td>", html_string(operand_string (s->opb)));
dasprintf (dstr, "<td>%s</td>", html_string(operand_string (s->opc)));
dasprintf (dstr, "</tr>\n");
}
int indent = level * 2 + 2;
static int
is_goto (statement_t *s)
{
if (!s)
return 0;
return !strcmp (s->opcode, "<GOTO>");
}
static int
is_return (statement_t *s)
{
if (!s)
return 0;
return !strncmp (s->opcode, "<RETURN", 7);
}
static sblock_t *
get_target (statement_t *s)
{
if (!s)
return 0;
if (!strncmp (s->opcode, "<IF", 3))
return s->opb->o.label->dest;
if (!strcmp (s->opcode, "<GOTO>"))
return s->opa->o.label->dest;
return 0;
dasprintf (dstr, "%*s\"fn_%p\" [label=\"%d (%d)\"];\n", indent, "",
node, node->id, node->dfn);
}
static void
flow_sblock (dstring_t *dstr, sblock_t *sblock, int blockno)
print_flow_edge (dstring_t *dstr, flowgraph_t *graph, flowedge_t *edge,
int level)
{
statement_t *s;
sblock_t *target;
ex_label_t *l;
int indent = level * 2 + 2;
int edge_num = edge - graph->edges;
flownode_t *t, *h;
const char *style;
const char *dir;
int weight;
dasprintf (dstr, " sb_%p [shape=none,label=<\n", sblock);
dasprintf (dstr, " <table border=\"0\" cellborder=\"1\" "
"cellspacing=\"0\">\n");
dasprintf (dstr, " <tr>\n");
dasprintf (dstr, " <td>%p(%d)</td>\n", sblock, blockno);
dasprintf (dstr, " <td height=\"0\" colspan=\"2\" port=\"s\">\n");
for (l = sblock->labels; l; l = l->next)
dasprintf (dstr, " %s(%d)\n", l->name, l->used);
dasprintf (dstr, " </td>\n");
dasprintf (dstr, " <td></td>\n");
dasprintf (dstr, " </tr>\n");
for (s = sblock->statements; s; s = s->next)
flow_statement (dstr, s);
dasprintf (dstr, " <tr>\n");
dasprintf (dstr, " <td></td>\n");
dasprintf (dstr, " <td height=\"0\" colspan=\"2\" "
"port=\"e\"></td>\n");
dasprintf (dstr, " <td></td>\n");
dasprintf (dstr, " </tr>\n");
dasprintf (dstr, " </table>>];\n");
if (sblock->next && !is_goto ((statement_t *) sblock->tail)
&& !is_return ((statement_t *) sblock->tail))
dasprintf (dstr, " sb_%p:e -> sb_%p:s;\n", sblock, sblock->next);
if ((target = get_target ((statement_t *) sblock->tail)))
dasprintf (dstr, " sb_%p:e -> sb_%p:s [label=\"%s\"];\n", sblock,
target, ((statement_t *) sblock->tail)->opcode);
dasprintf (dstr, "\n");
t = graph->nodes[edge->tail];
h = graph->nodes[edge->head];
if (t->dfn >= h->dfn) {
flownode_t *temp;
temp = h;
h = t;
t = temp;
dir = "dir=back,";
style = "dashed";
weight = 0;
} else if (set_is_member (graph->dfst, edge_num)) {
dir = "";
style = "bold";
weight = 10;
} else {
dir = "";
style = "solid";
weight = 0;
}
dasprintf (dstr, "%*s", indent, "");
dasprintf (dstr, "fn_%p -> ", t);
dasprintf (dstr, "fn_%p [%sstyle=%s,weight=%d", h, dir, style, weight);
dasprintf (dstr, "];\n");
}
void
print_flow (sblock_t *sblock, const char *filename)
static void
print_flow_node_dag (dstring_t *dstr, flowgraph_t *graph, flownode_t *node,
int level)
{
if (node->dag)
print_dag (dstr, node->dag, va ("%d (%d)", node->id, node->dfn));
else
print_flow_node (dstr, graph, node, level);
}
static void
print_flow_edge_dag (dstring_t *dstr, flowgraph_t *graph, flowedge_t *edge,
int level)
{
int indent = level * 2 + 2;
int edge_num = edge - graph->edges;
flownode_t *t, *h;
const char *tpref;
const char *hpref;
const char *style;
const char *dir;
int weight;
t = graph->nodes[edge->tail];
h = graph->nodes[edge->head];
if (t->dfn >= h->dfn) {
flownode_t *temp;
temp = h;
h = t;
t = temp;
tpref = "enter";
hpref = "leave";
dir = "dir=back,";
style = "dashed";
weight = 0;
} else if (set_is_member (graph->dfst, edge_num)) {
tpref = "leave";
hpref = "enter";
dir = "";
style = "bold";
weight = 10;
} else {
tpref = "leave";
hpref = "enter";
dir = "";
style = "solid";
weight = 0;
}
dasprintf (dstr, "%*s", indent, "");
if (t->dag)
dasprintf (dstr, "dag_%s_%p -> ", tpref, t->dag);
else
dasprintf (dstr, "fn_%p -> ", t);
if (h->dag)
dasprintf (dstr, "dag_%s_%p [%sstyle=%s,weight=%d",
hpref, h->dag, dir, style, weight);
else
dasprintf (dstr, "fn_%p [%sstyle=%s,weight=%d",
h, dir, style, weight);
if (t->dag)
dasprintf (dstr, ",ltail=cluster_dag_%p", t->dag);
if (h->dag)
dasprintf (dstr, ",lhead=cluster_dag_%p", h->dag);
dasprintf (dstr, "];\n");
}
static void
print_flow_node_live (dstring_t *dstr, flowgraph_t *graph, flownode_t *node,
int level)
{
int indent = level * 2 + 2;
set_iter_t *var_iter;
flowvar_t *var;
int live;
live = node->live_vars.out && !set_is_empty (node->live_vars.out);
if (live) {
dasprintf (dstr, "%*sfn_%p [shape=none,label=<\n", indent, "", node);
dasprintf (dstr, "%*s<table border=\"0\" cellborder=\"1\" "
"cellspacing=\"0\">\n", indent + 2, "");
dasprintf (dstr, "%*s<tr><td>%d (%d)</td></tr>\n", indent + 4, "",
node->id, node->dfn);
for (var_iter = set_first (node->live_vars.out); var_iter;
var_iter = set_next (var_iter)) {
var = graph->func->vars[var_iter->value];
dasprintf (dstr, "%*s<tr><td>(%d) %s</td></tr>\n", indent + 4, "",
var->number, html_string(operand_string (var->op)));
}
dasprintf (dstr, "%*s</table>>];\n", indent + 2, "");
} else {
print_flow_node (dstr, graph, node, level);
}
}
static void
print_flow_node_reaching (dstring_t *dstr, flowgraph_t *graph,
flownode_t *node, int level)
{
int indent = level * 2 + 2;
int reach;
set_t *gen = node->reaching_defs.gen;
set_t *kill = node->reaching_defs.kill;
set_t *in = node->reaching_defs.in;
set_t *out = node->reaching_defs.out;
reach = gen && kill && in && out;
if (reach) {
dasprintf (dstr, "%*sfn_%p [label=\"", indent, "", node);
dasprintf (dstr, "gen: %s\\n", set_as_string (gen));
dasprintf (dstr, "kill: %s\\n", set_as_string (kill));
dasprintf (dstr, "in: %s\\n", set_as_string (in));
dasprintf (dstr, "out: %s\"];\n", set_as_string (out));
} else {
print_flow_node (dstr, graph, node, level);
}
}
static void
print_flow_node_statements (dstring_t *dstr, flowgraph_t *graph,
flownode_t *node, int level)
{
if (node->sblock) {
dot_sblock (dstr, node->sblock, node->id);
} else {
print_flow_node (dstr, graph, node, level);
}
}
static void
print_flow_edge_statements (dstring_t *dstr, flowgraph_t *graph,
flowedge_t *edge, int level)
{
int indent = level * 2 + 2;
int edge_num = edge - graph->edges;
flownode_t *h, *t;
const char *hpre = "fn_", *tpre = "fn_";
const char *style;
const char *dir;
int weight;
t = graph->nodes[edge->tail];
h = graph->nodes[edge->head];
if (t->dfn >= h->dfn) {
flownode_t *temp;
temp = h;
h = t;
t = temp;
dir = "dir=back,";
style = "dashed";
weight = 0;
} else if (set_is_member (graph->dfst, edge_num)) {
dir = "";
style = "bold";
weight = 10;
} else {
dir = "";
style = "solid";
weight = 0;
}
if (t->sblock) {
tpre = "sb_";
t = (flownode_t *) t->sblock;
}
if (h->sblock) {
hpre = "sb_";
h = (flownode_t *) h->sblock;
}
dasprintf (dstr, "%*s", indent, "");
dasprintf (dstr, "%s%p -> ", tpre, t);
dasprintf (dstr, "%s%p [%sstyle=%s,weight=%d", hpre, h, dir, style,
weight);
dasprintf (dstr, "];\n");
}
static flow_dot_t flow_dot_methods[] = {
{"", print_flow_node, print_flow_edge},
{"dag", print_flow_node_dag, print_flow_edge_dag},
{"live", print_flow_node_live, print_flow_edge},
{"reaching", print_flow_node_reaching, print_flow_edge},
{"statements", print_flow_node_statements, print_flow_edge_statements},
};
static void
print_flowgraph (flow_dot_t *method, flowgraph_t *graph, const char *filename)
{
int i;
dstring_t *dstr = dstring_newstr();
dasprintf (dstr, "digraph flow_%p {\n", sblock);
dasprintf (dstr, " layout=dot; rankdir=TB;\n");
for (i = 0; sblock; sblock = sblock->next, i++)
flow_sblock (dstr, sblock, i);
dasprintf (dstr, "digraph flowgraph_%s_%p {\n", method->type, graph);
dasprintf (dstr, " layout=dot;\n");
dasprintf (dstr, " clusterrank=local;\n");
dasprintf (dstr, " rankdir=TB;\n");
dasprintf (dstr, " compound=true;\n");
for (i = 0; i < graph->num_nodes; i++) {
method->print_node (dstr, graph, graph->nodes[i], 0);
}
for (i = 0; i < graph->num_edges; i++) {
if ((int) graph->edges[i].head >= graph->num_nodes
|| (int) graph->edges[i].tail >= graph->num_nodes)
continue; // dummy node
method->print_edge (dstr, graph, &graph->edges[i], 0);
}
dasprintf (dstr, "}\n");
if (filename) {
@ -147,3 +323,33 @@ print_flow (sblock_t *sblock, const char *filename)
}
dstring_delete (dstr);
}
void
dump_dot_flow (void *g, const char *filename)
{
print_flowgraph (&flow_dot_methods[0], (flowgraph_t *) g, filename);
}
void
dump_dot_flow_dags (void *g, const char *filename)
{
print_flowgraph (&flow_dot_methods[1], (flowgraph_t *) g, filename);
}
void
dump_dot_flow_live (void *g, const char *filename)
{
print_flowgraph (&flow_dot_methods[2], (flowgraph_t *) g, filename);
}
void
dump_dot_flow_reaching (void *g, const char *filename)
{
print_flowgraph (&flow_dot_methods[3], (flowgraph_t *) g, filename);
}
void
dump_dot_flow_statements (void *g, const char *filename)
{
print_flowgraph (&flow_dot_methods[4], (flowgraph_t *) g, filename);
}

View file

@ -0,0 +1,146 @@
/*
dot_sblock.c
"emit" sblock graphs to dot (graphvis).
Copyright (C) 2011 Bill Currie <bill@taniwha.org>
Author: Bill Currie <bill@taniwha.org>
Date: 2011/01/21
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to:
Free Software Foundation, Inc.
59 Temple Place - Suite 330
Boston, MA 02111-1307, USA
*/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#ifdef HAVE_STRING_H
# include <string.h>
#endif
#ifdef HAVE_STRINGS_H
# include <strings.h>
#endif
#include <stdlib.h>
#include <QF/dstring.h>
#include <QF/quakeio.h>
#include <QF/va.h>
#include "dags.h"
#include "flow.h"
#include "expr.h"
#include "qfcc.h"
#include "function.h"
#include "statements.h"
#include "strpool.h"
#include "symtab.h"
#include "type.h"
static void
flow_statement (dstring_t *dstr, statement_t *s)
{
dasprintf (dstr, " <tr>");
dasprintf (dstr, "<td>%d</td>", s->number);
dasprintf (dstr, "<td>%s</td>", html_string(quote_string (s->opcode)));
dasprintf (dstr, "<td>%s</td>", html_string(operand_string (s->opa)));
dasprintf (dstr, "<td>%s</td>", html_string(operand_string (s->opb)));
dasprintf (dstr, "<td>%s</td>", html_string(operand_string (s->opc)));
dasprintf (dstr, "</tr>\n");
}
void
dot_sblock (dstring_t *dstr, sblock_t *sblock, int blockno)
{
statement_t *s;
ex_label_t *l;
dasprintf (dstr, " sb_%p [shape=none,label=<\n", sblock);
dasprintf (dstr, " <table border=\"0\" cellborder=\"1\" "
"cellspacing=\"0\">\n");
dasprintf (dstr, " <tr>\n");
dasprintf (dstr, " <td colspan=\"2\" >%p(%d)</td>\n", sblock,
blockno);
dasprintf (dstr, " <td height=\"0\" colspan=\"2\" port=\"s\">\n");
for (l = sblock->labels; l; l = l->next)
dasprintf (dstr, " %s(%d)\n", l->name, l->used);
dasprintf (dstr, " </td>\n");
dasprintf (dstr, " <td></td>\n");
dasprintf (dstr, " </tr>\n");
for (s = sblock->statements; s; s = s->next)
flow_statement (dstr, s);
dasprintf (dstr, " <tr>\n");
dasprintf (dstr, " <td colspan=\"2\"></td>\n");
dasprintf (dstr, " <td height=\"0\" colspan=\"2\" "
"port=\"e\"></td>\n");
dasprintf (dstr, " <td></td>\n");
dasprintf (dstr, " </tr>\n");
dasprintf (dstr, " </table>>];\n");
}
static void
flow_sblock (dstring_t *dstr, sblock_t *sblock, int blockno)
{
sblock_t **target;
sblock_t **target_list;
dot_sblock (dstr, sblock, blockno);
if (sblock->statements) {
statement_t *st = (statement_t *) sblock->tail;
if (sblock->next
&& !statement_is_goto (st)
&& !statement_is_jumpb (st)
&& !statement_is_return (st))
dasprintf (dstr, " sb_%p:e -> sb_%p:s;\n", sblock, sblock->next);
if ((target_list = statement_get_targetlist (st))) {
for (target = target_list; *target; target++)
dasprintf (dstr, " sb_%p:e -> sb_%p:s [label=\"%s\"];\n",
sblock, *target, st->opcode);
free (target_list);
}
} else {
if (sblock->next)
dasprintf (dstr, " sb_%p:e -> sb_%p:s;\n", sblock, sblock->next);
}
dasprintf (dstr, "\n");
}
void
print_sblock (sblock_t *sblock, const char *filename)
{
int i;
dstring_t *dstr = dstring_newstr();
dasprintf (dstr, "digraph sblock_%p {\n", sblock);
dasprintf (dstr, " layout=dot; rankdir=TB;\n");
for (i = 0; sblock; sblock = sblock->next, i++)
flow_sblock (dstr, sblock, i);
dasprintf (dstr, "}\n");
if (filename) {
QFile *file;
file = Qopen (filename, "wt");
Qwrite (file, dstr->str, dstr->size - 1);
Qclose (file);
} else {
fputs (dstr->str, stdout);
}
dstring_delete (dstr);
}

View file

@ -249,6 +249,7 @@ flags_string (pr_uint_t flags)
dstring_appendstr (str, (flags & QFOD_LOCAL) ? "L" : "-");
dstring_appendstr (str, (flags & QFOD_SYSTEM) ? "S" : "-");
dstring_appendstr (str, (flags & QFOD_NOSAVE) ? "N" : "-");
dstring_appendstr (str, (flags & QFOD_PARAM) ? "P" : "-");
return str->str;
}
@ -463,7 +464,7 @@ dump_qfo_types (qfo_t *qfo, int base_address)
? "invalid type"
: pr_type_name[type->t.type]);
if (type->t.type == ev_func) {
printf ("%5x %d", type->t.func.return_type,
printf (" %4x %d", type->t.func.return_type,
count = type->t.func.num_params);
if (count < 0)
count = ~count; //ones complement
@ -471,7 +472,7 @@ dump_qfo_types (qfo_t *qfo, int base_address)
printf (" %x", type->t.func.param_types[i]);
} else if (type->t.type == ev_pointer
|| type->t.type == ev_field) {
printf (" %x", type->t.fldptr.aux_type);
printf (" %4x", type->t.fldptr.aux_type);
}
printf ("\n");
break;

View file

@ -66,64 +66,38 @@ get_value_def (ex_value_t *value, etype_t type)
def_t *def;
if (type == ev_short) {
def = new_def (0, &type_short, 0, st_extern);
def = new_def (0, &type_short, 0, sc_extern);
def->offset = value->v.short_val;
return def;
}
def = emit_value (value, 0);
if (type != def->type->type)
return alias_def (def, ev_types[type]);
return alias_def (def, ev_types[type], 0);
return def;
}
static def_t *
get_operand_def (expr_t *expr, operand_t *op)
{
def_t *def;
if (!op)
return 0;
switch (op->op_type) {
case op_symbol:
switch (op->o.symbol->sy_type) {
case sy_var:
if (op->type != op->o.symbol->type->type)
return alias_def (op->o.symbol->s.def,
ev_types[op->type]);
return op->o.symbol->s.def;
case sy_func:
return op->o.symbol->s.func->def;
case sy_const:
return get_value_def (&op->o.symbol->s.value, op->type);
case sy_type:
case sy_expr:
case sy_class:
internal_error (expr, "invalid operand type");
}
break;
case op_def:
return op->o.def;
case op_value:
return get_value_def (op->o.value, op->type);
case op_label:
op->type = ev_short;
zero_def.type = &type_short;
return &zero_def; //FIXME
case op_temp:
if (!op->o.def)
op->o.def = temp_def (op->type, op->size);
return op->o.def;
case op_pointer:
def = op->o.pointer->def;
if (op->o.pointer->val || op->type != def->type->type) {
def = alias_def (def, ev_types[op->type]);
def->offset = op->o.pointer->val;
def->offset_reloc = 1;
}
return def;
while (op->o.tempop.alias)
op = op->o.tempop.alias;
if (!op->o.tempop.def)
op->o.tempop.def = temp_def (op->type, op->size);
return op->o.tempop.def;
case op_alias:
def = get_operand_def (expr, op->o.alias);
if (def->alias)
def = def->alias;
def = alias_def (def, ev_types[op->type]);
return def;
return get_operand_def (expr, op->o.alias);
}
return 0;
}
@ -141,13 +115,11 @@ add_statement_def_ref (def_t *def, dstatement_t *st, int field)
alias_depth_expr.line = def->line;
while (def->alias) {
alias_depth++;
def_t *a = def;
offset_reloc |= def->offset_reloc;
def = def->alias;
free_def (a);
}
if (alias_depth > 1) {
bug (&alias_depth_expr, "alias chain detected: %d %s",
internal_error (&alias_depth_expr, "alias chain detected: %d %s",
alias_depth, def->name);
}
if (offset_reloc)
@ -169,16 +141,35 @@ add_statement_op_ref (operand_t *op, dstatement_t *st, int field)
}
}
static void
use_tempop (operand_t *op, expr_t *expr)
{
if (!op || op->op_type != op_temp)
return;
while (op->o.tempop.alias)
op = op->o.tempop.alias;
if (--op->o.tempop.users == 0)
free_temp_def (op->o.tempop.def);
if (op->o.tempop.users <= -1)
bug (expr, "temp users went negative: %s", operand_string (op));
}
static void
emit_statement (statement_t *statement)
{
const char *opcode = statement->opcode;
def_t *def_a = get_operand_def (statement->expr, statement->opa);
def_t *def_b = get_operand_def (statement->expr, statement->opb);
def_t *def_c = get_operand_def (statement->expr, statement->opc);
opcode_t *op = opcode_find (opcode, def_a, def_b, def_c);
def_t *def_a, *def_b, *def_c;
opcode_t *op;
dstatement_t *s;
def_a = get_operand_def (statement->expr, statement->opa);
use_tempop (statement->opa, statement->expr);
def_b = get_operand_def (statement->expr, statement->opb);
use_tempop (statement->opb, statement->expr);
def_c = get_operand_def (statement->expr, statement->opc);
use_tempop (statement->opc, statement->expr);
op = opcode_find (opcode, statement->opa, statement->opb, statement->opc);
if (!op) {
print_expr (statement->expr);
print_statement (statement);

View file

@ -39,10 +39,11 @@
#endif
#include <stdlib.h>
#include <QF/dstring.h>
#include <QF/mathlib.h>
#include <QF/sys.h>
#include <QF/va.h>
#include "QF/alloc.h"
#include "QF/dstring.h"
#include "QF/mathlib.h"
#include "QF/sys.h"
#include "QF/va.h"
#include "qfcc.h"
#include "class.h"
@ -56,10 +57,12 @@
#include "method.h"
#include "options.h"
#include "reloc.h"
#include "shared.h"
#include "strpool.h"
#include "struct.h"
#include "symtab.h"
#include "type.h"
#include "value.h"
#include "qc-parse.h"
static expr_t *free_exprs;
@ -164,16 +167,17 @@ get_type (expr_t *e)
case ex_temp:
return e->e.temp.type;
case ex_value:
if (e->e.value.type == ev_pointer)
return pointer_type (e->e.value.v.pointer.type);
if (e->e.value.type == ev_field)
return field_type (e->e.value.v.pointer.type);
if (e->e.value.type == ev_integer
if (e->e.value->type == ev_func)
return e->e.value->v.func_val.type;
if (e->e.value->type == ev_pointer)
return pointer_type (e->e.value->v.pointer.type);
if (e->e.value->type == ev_field)
return field_type (e->e.value->v.pointer.type);
if (e->e.value->type == ev_integer
&& options.code.progsversion == PROG_ID_VERSION) {
e->e.value.type = ev_float;
e->e.value.v.float_val = e->e.value.v.integer_val;
convert_int (e);
}
return ev_types[e->e.value.type];
return ev_types[e->e.value->type];
}
return 0;
}
@ -293,7 +297,7 @@ copy_expr (expr_t *e)
n->e.bool.true_list = malloc (size);
while (count--)
n->e.bool.true_list->e[count] =
copy_expr ( e->e.bool.true_list->e[count]);
copy_expr (e->e.bool.true_list->e[count]);
}
if (e->e.bool.false_list) {
int count = e->e.bool.false_list->size;
@ -301,7 +305,7 @@ copy_expr (expr_t *e)
n->e.bool.false_list = malloc (size);
while (count--)
n->e.bool.false_list->e[count] =
copy_expr ( e->e.bool.false_list->e[count]);
copy_expr (e->e.bool.false_list->e[count]);
}
n->e.bool.e = copy_expr (e->e.bool.e);
return n;
@ -511,8 +515,7 @@ new_string_expr (const char *string_val)
{
expr_t *e = new_expr ();
e->type = ex_value;
e->e.value.type = ev_string;
e->e.value.v.string_val = string_val;
e->e.value = new_string_val (string_val);
return e;
}
@ -521,8 +524,7 @@ new_float_expr (float float_val)
{
expr_t *e = new_expr ();
e->type = ex_value;
e->e.value.type = ev_float;
e->e.value.v.float_val = float_val;
e->e.value = new_float_val (float_val);
return e;
}
@ -531,8 +533,7 @@ new_vector_expr (const float *vector_val)
{
expr_t *e = new_expr ();
e->type = ex_value;
e->e.value.type = ev_vector;
VectorCopy (vector_val, e->e.value.v.vector_val);
e->e.value = new_vector_val (vector_val);
return e;
}
@ -541,8 +542,7 @@ new_entity_expr (int entity_val)
{
expr_t *e = new_expr ();
e->type = ex_value;
e->e.value.type = ev_entity;
e->e.value.v.entity_val = entity_val;
e->e.value = new_entity_val (entity_val);
return e;
}
@ -551,20 +551,16 @@ new_field_expr (int field_val, type_t *type, def_t *def)
{
expr_t *e = new_expr ();
e->type = ex_value;
e->e.value.type = ev_field;
e->e.value.v.pointer.val = field_val;
e->e.value.v.pointer.type = type;
e->e.value.v.pointer.def = def;
e->e.value = new_field_val (field_val, type, def);
return e;
}
expr_t *
new_func_expr (int func_val)
new_func_expr (int func_val, type_t *type)
{
expr_t *e = new_expr ();
e->type = ex_value;
e->e.value.type = ev_func;
e->e.value.v.func_val = func_val;
e->e.value = new_func_val (func_val, type);
return e;
}
@ -573,10 +569,7 @@ new_pointer_expr (int val, type_t *type, def_t *def)
{
expr_t *e = new_expr ();
e->type = ex_value;
e->e.value.type = ev_pointer;
e->e.value.v.pointer.val = val;
e->e.value.v.pointer.type = type;
e->e.value.v.pointer.def = def;
e->e.value = new_pointer_val (val, type, def);
return e;
}
@ -585,8 +578,7 @@ new_quaternion_expr (const float *quaternion_val)
{
expr_t *e = new_expr ();
e->type = ex_value;
e->e.value.type = ev_quat;
QuatCopy (quaternion_val, e->e.value.v.quaternion_val);
e->e.value = new_quaternion_val (quaternion_val);
return e;
}
@ -595,8 +587,7 @@ new_integer_expr (int integer_val)
{
expr_t *e = new_expr ();
e->type = ex_value;
e->e.value.type = ev_integer;
e->e.value.v.integer_val = integer_val;
e->e.value = new_integer_val (integer_val);
return e;
}
@ -605,8 +596,7 @@ new_uinteger_expr (unsigned uinteger_val)
{
expr_t *e = new_expr ();
e->type = ex_value;
e->e.value.type = ev_uinteger;
e->e.value.v.uinteger_val = uinteger_val;
e->e.value = new_uinteger_val (uinteger_val);
return e;
}
@ -615,8 +605,7 @@ new_short_expr (short short_val)
{
expr_t *e = new_expr ();
e->type = ex_value;
e->e.value.type = ev_short;
e->e.value.v.short_val = short_val;
e->e.value = new_short_val (short_val);
return e;
}
@ -636,7 +625,7 @@ constant_expr (expr_t *e)
{
expr_t *new;
symbol_t *sym;
ex_value_t value;
ex_value_t *value;
if (!is_constant (e))
return e;
@ -649,9 +638,10 @@ constant_expr (expr_t *e)
value = sym->s.value;
} else if (sym->sy_type == sy_var && sym->s.def->constant) {
//FIXME pointers and fields
memset (&value, 0, sizeof (value));
memcpy (&value.v, &D_INT (sym->s.def),
type_size (sym->s.def->type) * sizeof (pr_type_t));
internal_error (e, "what to do here?");
//memset (&value, 0, sizeof (value));
//memcpy (&value.v, &D_INT (sym->s.def),
//type_size (sym->s.def->type) * sizeof (pr_type_t));
} else {
return e;
}
@ -668,7 +658,7 @@ is_string_val (expr_t *e)
{
if (e->type == ex_nil)
return 1;
if (e->type == ex_value && e->e.value.type == ev_string)
if (e->type == ex_value && e->e.value->type == ev_string)
return 1;
if (e->type == ex_symbol && e->e.symbol->sy_type == sy_const
&& e->e.symbol->type->type == ev_string)
@ -681,11 +671,8 @@ expr_string (expr_t *e)
{
if (e->type == ex_nil)
return 0;
if (e->type == ex_value && e->e.value.type == ev_string)
return e->e.value.v.string_val;
//if (e->type == ex_symbol && e->e.symbol->sy_type == sy_const
// && e->e.symbol->type->type == ev_string)
// return e->e.symbol->s.value;
if (e->type == ex_value && e->e.value->type == ev_string)
return e->e.value->v.string_val;
internal_error (e, "not a string constant");
}
@ -694,7 +681,7 @@ is_float_val (expr_t *e)
{
if (e->type == ex_nil)
return 1;
if (e->type == ex_value && e->e.value.type == ev_float)
if (e->type == ex_value && e->e.value->type == ev_float)
return 1;
if (e->type == ex_symbol && e->e.symbol->sy_type == sy_const
&& e->e.symbol->type->type == ev_float)
@ -707,11 +694,11 @@ expr_float (expr_t *e)
{
if (e->type == ex_nil)
return 0;
if (e->type == ex_value && e->e.value.type == ev_float)
return e->e.value.v.float_val;
if (e->type == ex_value && e->e.value->type == ev_float)
return e->e.value->v.float_val;
if (e->type == ex_symbol && e->e.symbol->sy_type == sy_const
&& e->e.symbol->type->type == ev_float)
return e->e.symbol->s.value.v.float_val;
return e->e.symbol->s.value->v.float_val;
if (e->type == ex_symbol && e->e.symbol->sy_type == sy_var
&& e->e.symbol->s.def->constant
&& is_float (e->e.symbol->s.def->type))
@ -724,7 +711,7 @@ is_vector_val (expr_t *e)
{
if (e->type == ex_nil)
return 1;
if (e->type == ex_value && e->e.value.type == ev_vector)
if (e->type == ex_value && e->e.value->type == ev_vector)
return 1;
if (e->type == ex_symbol && e->e.symbol->sy_type == sy_const
&& e->e.symbol->type->type == ev_vector)
@ -737,11 +724,11 @@ expr_vector (expr_t *e)
{
if (e->type == ex_nil)
return vec3_origin;
if (e->type == ex_value && e->e.value.type == ev_vector)
return e->e.value.v.vector_val;
if (e->type == ex_value && e->e.value->type == ev_vector)
return e->e.value->v.vector_val;
if (e->type == ex_symbol && e->e.symbol->sy_type == sy_const
&& e->e.symbol->type->type == ev_vector)
return e->e.symbol->s.value.v.vector_val;
return e->e.symbol->s.value->v.vector_val;
if (e->type == ex_symbol && e->e.symbol->sy_type == sy_var
&& e->e.symbol->s.def->constant
&& e->e.symbol->s.def->type->type == ev_vector)
@ -754,7 +741,7 @@ is_quaternion_val (expr_t *e)
{
if (e->type == ex_nil)
return 1;
if (e->type == ex_value && e->e.value.type == ev_quat)
if (e->type == ex_value && e->e.value->type == ev_quat)
return 1;
if (e->type == ex_symbol && e->e.symbol->sy_type == sy_const
&& e->e.symbol->type->type == ev_quat)
@ -767,11 +754,11 @@ expr_quaternion (expr_t *e)
{
if (e->type == ex_nil)
return quat_origin;
if (e->type == ex_value && e->e.value.type == ev_quat)
return e->e.value.v.quaternion_val;
if (e->type == ex_value && e->e.value->type == ev_quat)
return e->e.value->v.quaternion_val;
if (e->type == ex_symbol && e->e.symbol->sy_type == sy_const
&& e->e.symbol->type->type == ev_quat)
return e->e.symbol->s.value.v.quaternion_val;
return e->e.symbol->s.value->v.quaternion_val;
if (e->type == ex_symbol && e->e.symbol->sy_type == sy_var
&& e->e.symbol->s.def->constant
&& e->e.symbol->s.def->type->type == ev_quat)
@ -784,7 +771,7 @@ is_integer_val (expr_t *e)
{
if (e->type == ex_nil)
return 1;
if (e->type == ex_value && e->e.value.type == ev_integer)
if (e->type == ex_value && e->e.value->type == ev_integer)
return 1;
if (e->type == ex_symbol && e->e.symbol->sy_type == sy_const
&& (e->e.symbol->type->type == ev_integer
@ -798,12 +785,12 @@ expr_integer (expr_t *e)
{
if (e->type == ex_nil)
return 0;
if (e->type == ex_value && e->e.value.type == ev_integer)
return e->e.value.v.integer_val;
if (e->type == ex_value && e->e.value->type == ev_integer)
return e->e.value->v.integer_val;
if (e->type == ex_symbol && e->e.symbol->sy_type == sy_const
&& (e->e.symbol->type->type == ev_integer
|| is_enum (e->e.symbol->type)))
return e->e.symbol->s.value.v.integer_val;
return e->e.symbol->s.value->v.integer_val;
if (e->type == ex_symbol && e->e.symbol->sy_type == sy_var
&& e->e.symbol->s.def->constant
&& is_integral (e->e.symbol->s.def->type))
@ -816,11 +803,11 @@ expr_uinteger (expr_t *e)
{
if (e->type == ex_nil)
return 0;
if (e->type == ex_value && e->e.value.type == ev_uinteger)
return e->e.value.v.uinteger_val;
if (e->type == ex_value && e->e.value->type == ev_uinteger)
return e->e.value->v.uinteger_val;
if (e->type == ex_symbol && e->e.symbol->sy_type == sy_const
&& e->e.symbol->type->type == ev_uinteger)
return e->e.symbol->s.value.v.uinteger_val;
return e->e.symbol->s.value->v.uinteger_val;
if (e->type == ex_symbol && e->e.symbol->sy_type == sy_var
&& e->e.symbol->s.def->constant
&& is_integral (e->e.symbol->s.def->type))
@ -833,7 +820,7 @@ is_short_val (expr_t *e)
{
if (e->type == ex_nil)
return 1;
if (e->type == ex_value && e->e.value.type == ev_short)
if (e->type == ex_value && e->e.value->type == ev_short)
return 1;
if (e->type == ex_symbol && e->e.symbol->sy_type == sy_const
&& e->e.symbol->type->type == ev_short)
@ -846,11 +833,11 @@ expr_short (expr_t *e)
{
if (e->type == ex_nil)
return 0;
if (e->type == ex_value && e->e.value.type == ev_short)
return e->e.value.v.short_val;
if (e->type == ex_value && e->e.value->type == ev_short)
return e->e.value->v.short_val;
if (e->type == ex_symbol && e->e.symbol->sy_type == sy_const
&& e->e.symbol->type->type == ev_short)
return e->e.symbol->s.value.v.short_val;
return e->e.symbol->s.value->v.short_val;
internal_error (e, "not a short constant");
}
@ -859,7 +846,7 @@ new_self_expr (void)
{
symbol_t *sym;
sym = make_symbol (".self", &type_entity, pr.near_data, st_extern);
sym = make_symbol (".self", &type_entity, pr.near_data, sc_extern);
if (!sym->table)
symtab_addsymbol (pr.symtab, sym);
return new_symbol_expr (sym);
@ -870,7 +857,7 @@ new_this_expr (void)
{
symbol_t *sym;
sym = make_symbol (".this", field_type (&type_id), pr.near_data, st_extern);
sym = make_symbol (".this", field_type (&type_id), pr.near_data, sc_extern);
if (!sym->table)
symtab_addsymbol (pr.symtab, sym);
return new_symbol_expr (sym);
@ -896,7 +883,7 @@ param_expr (const char *name, type_t *type)
symbol_t *sym;
expr_t *sym_expr;
sym = make_symbol (name, &type_param, pr.symtab->space, st_extern);
sym = make_symbol (name, &type_param, pr.symtab->space, sc_extern);
if (!sym->table)
symtab_addsymbol (pr.symtab, sym);
sym_expr = new_symbol_expr (sym);
@ -998,8 +985,7 @@ field_expr (expr_t *e1, expr_t *e2)
return e1;
e2->type = ex_value;
e2->e.value.type = ev_short;
e2->e.value.v.short_val = field->s.offset;
e2->e.value = new_short_val (field->s.offset);
e = new_binary_expr ('&', e1, e2);
e->e.expr.type = pointer_type (field->type);
return unary_expr ('.', e);
@ -1012,8 +998,7 @@ field_expr (expr_t *e1, expr_t *e2)
if (!ivar)
return new_error_expr ();
e2->type = ex_value;
e2->e.value.type = ev_short;
e2->e.value.v.short_val = ivar->s.offset;
e2->e.value = new_short_val (ivar->s.offset);
e = new_binary_expr ('&', e1, e2);
e->e.expr.type = pointer_type (ivar->type);
return unary_expr ('.', e);
@ -1042,17 +1027,15 @@ field_expr (expr_t *e1, expr_t *e2)
}
def = sym->s.def;
e2 = new_field_expr (0, field->type, def);
} else if (e2->type != ex_value || e2->e.value.type != ev_field) {
} else if (e2->type != ex_value || e2->e.value->type != ev_field) {
internal_error (e2, "unexpected field exression");
}
e2->e.value.v.pointer.val += field->s.offset;
e2->e.value.v.pointer.type = field->type;
e2->e.value = new_field_val (e2->e.value->v.pointer.val + field->s.offset, field->type, e2->e.value->v.pointer.def);
// create a new . expression
return field_expr (e1, e2);
} else {
e2->type = ex_value;
e2->e.value.type = ev_short;
e2->e.value.v.short_val = field->s.offset;
e2->e.value = new_short_val (field->s.offset);
e = address_expr (e1, e2, field->type);
return unary_expr ('.', e);
}
@ -1302,35 +1285,32 @@ bool_expr (int op, expr_t *label, expr_t *e1, expr_t *e2)
void
convert_int (expr_t *e)
{
e->e.value.v.float_val = expr_integer (e);
e->e.value.type = ev_float;
float float_val = expr_integer (e);
e->type = ex_value;
e->e.value = new_float_val (float_val);
}
void
convert_short (expr_t *e)
{
e->e.value.v.float_val = expr_short (e);
e->e.value.type = ev_float;
float float_val = expr_short (e);
e->type = ex_value;
e->e.value = new_float_val (float_val);
}
void
convert_short_int (expr_t *e)
{
e->e.value.v.integer_val = expr_short (e);
e->e.value.type = ev_integer;
float integer_val = expr_short (e);
e->type = ex_value;
e->e.value = new_integer_val (integer_val);
}
void
convert_nil (expr_t *e, type_t *t)
{
memset (&e->e.value, 0, sizeof (e->e.value));
e->e.value.type = low_level_type (t);
if (t->type == ev_pointer || t->type == ev_field)
e->e.value.v.pointer.type = t->t.fldptr.type;
e->type = ex_value;
e->e.value = new_nil_val (t);
}
int
@ -1541,13 +1521,14 @@ binary_expr (int op, expr_t *e1, expr_t *e2)
switch (op) {
case '%':
{
expr_t *tmp1, *tmp2, *tmp3, *t1, *t2;
expr_t *tmp1, *tmp2, *tmp3, *tmp4, *t1, *t2;
e = new_block_expr ();
t1 = new_temp_def_expr (&type_float);
t2 = new_temp_def_expr (&type_float);
tmp1 = new_temp_def_expr (&type_float);
tmp2 = new_temp_def_expr (&type_float);
tmp3 = new_temp_def_expr (&type_float);
tmp4 = new_temp_def_expr (&type_float);
append_expr (e, assign_expr (t1, e1));
e1 = binary_expr ('&', t1, t1);
@ -1561,9 +1542,9 @@ binary_expr (int op, expr_t *e1, expr_t *e2)
append_expr (e, assign_expr (tmp3, e1));
e2 = binary_expr ('&', tmp3, tmp3);
append_expr (e, assign_expr (tmp3, e2));
append_expr (e, assign_expr (tmp4, e2));
e1 = binary_expr ('*', tmp2, tmp3);
e1 = binary_expr ('*', tmp2, tmp4);
e2 = binary_expr ('-', tmp1, e1);
e->e.block.result = e2;
return e;
@ -2196,10 +2177,7 @@ address_expr (expr_t *e1, expr_t *e2, type_t *t)
if (is_array (type)) {
e = e1;
e->type = ex_value;
e->e.value.type = ev_pointer;
e->e.value.v.pointer.val = 0;
e->e.value.v.pointer.type = t;
e->e.value.v.pointer.def = def;
e->e.value = new_pointer_val (0, t, def);
} else {
e = new_pointer_expr (0, t, def);
e->line = e1->line;
@ -2215,6 +2193,16 @@ address_expr (expr_t *e1, expr_t *e2, type_t *t)
e->e.expr.type = pointer_type (e->e.expr.type);
break;
}
if (e1->e.expr.op == 'm') {
// direct move, so obtain the address of the source
e = address_expr (e1->e.expr.e2, 0, t);
break;
}
if (e1->e.expr.op == 'M') {
// indirect move, so we already have the address of the source
e = e1->e.expr.e2;
break;
}
return error (e1, "invalid type for unary &");
case ex_uexpr:
if (e1->e.expr.op == '.') {
@ -2244,10 +2232,9 @@ address_expr (expr_t *e1, expr_t *e2, type_t *t)
if (e2) {
if (e2->type == ex_error)
return e2;
if (e->type == ex_value && e->e.value.type == ev_pointer
if (e->type == ex_value && e->e.value->type == ev_pointer
&& is_short_val (e2)) {
e->e.value.v.pointer.val += expr_short (e2);
e->e.value.v.pointer.type = t;
e->e.value = new_pointer_val (e->e.value->v.pointer.val + expr_short (e2), t, e->e.value->v.pointer.def);
} else {
if (!is_short_val (e2) || expr_short (e2)) {
if (e->type == ex_expr && e->e.expr.op == '&') {
@ -2265,7 +2252,7 @@ address_expr (expr_t *e1, expr_t *e2, type_t *t)
}
expr_t *
build_if_statement (expr_t *test, expr_t *s1, expr_t *s2)
build_if_statement (expr_t *test, expr_t *s1, expr_t *els, expr_t *s2)
{
int line = pr.source_line;
string_t file = pr.source_file;
@ -2287,6 +2274,11 @@ build_if_statement (expr_t *test, expr_t *s1, expr_t *s2)
}
append_expr (if_expr, s1);
if (els) {
pr.source_line = els->line;
pr.source_file = els->file;
}
if (s2) {
expr_t *nl = new_label_expr ();
append_expr (if_expr, goto_expr (nl));
@ -2474,9 +2466,9 @@ is_indirect (expr_t *e)
if (!(e->type == ex_uexpr && e->e.expr.op == '.'))
return 0;
e = e->e.expr.e1;
if (e->type != ex_value || e->e.value.type != ev_pointer
|| !(POINTER_VAL (e->e.value.v.pointer) >= 0
&& POINTER_VAL (e->e.value.v.pointer) < 65536)) {
if (e->type != ex_value || e->e.value->type != ev_pointer
|| !(POINTER_VAL (e->e.value->v.pointer) >= 0
&& POINTER_VAL (e->e.value->v.pointer) < 65536)) {
return 1;
}
return 0;
@ -2614,9 +2606,9 @@ assign_expr (expr_t *e1, expr_t *e2)
op = PAS;
} else {
e = e1->e.expr.e1;
if ((e->type != ex_value || e->e.value.type != ev_pointer)
|| !(POINTER_VAL (e->e.value.v.pointer) > 0
&& POINTER_VAL (e->e.value.v.pointer) < 65536)) {
if ((e->type != ex_value || e->e.value->type != ev_pointer)
|| !(POINTER_VAL (e->e.value->v.pointer) > 0
&& POINTER_VAL (e->e.value->v.pointer) < 65536)) {
e1 = e;
op = PAS;
}
@ -2630,9 +2622,9 @@ assign_expr (expr_t *e1, expr_t *e2)
}
if (e2->type == ex_uexpr) {
e = e2->e.expr.e1;
if ((e->type != ex_value || e->e.value.type != ev_pointer)
|| !(POINTER_VAL (e->e.value.v.pointer) > 0
&& POINTER_VAL (e->e.value.v.pointer) < 65536)) {
if ((e->type != ex_value || e->e.value->type != ev_pointer)
|| !(POINTER_VAL (e->e.value->v.pointer) > 0
&& POINTER_VAL (e->e.value->v.pointer) < 65536)) {
if (e->type == ex_expr && e->e.expr.op == '&'
&& e->e.expr.type->type == ev_pointer
&& !is_constant (e)) {
@ -2645,7 +2637,7 @@ assign_expr (expr_t *e1, expr_t *e2)
}
}
if (is_struct (get_type (e1))) {
return new_move_expr (e1, e2, get_type (e2), 0);
return new_move_expr (e1, e2, get_type (e1), 0);
}
if (!type)
internal_error (e1, 0);
@ -2684,7 +2676,10 @@ cast_expr (type_t *type, expr_t *e)
}
if (is_array (e_type))
return address_expr (e, 0, 0);
if ((is_float (type) && is_integral (e_type))
if (is_constant (e) && is_scalar (type) && is_scalar (e_type)) {
e->e.value = convert_value (e->e.value, type);
c = e;
} else if ((is_float (type) && is_integral (e_type))
|| (is_integral (type) && is_float (e_type))) {
c = new_unary_expr ('C', e);
c->e.expr.type = type;
@ -2712,12 +2707,12 @@ selector_expr (keywordarg_t *selector)
index = selector_index (sel_id->str);
index *= type_size (type_SEL.t.fldptr.type);
sel_sym = make_symbol ("_OBJ_SELECTOR_TABLE_PTR", &type_SEL,
pr.near_data, st_static);
pr.near_data, sc_static);
if (!sel_sym->table) {
symtab_addsymbol (pr.symtab, sel_sym);
sel_table = make_symbol ("_OBJ_SELECTOR_TABLE",
array_type (type_SEL.t.fldptr.type, 0),
pr.far_data, st_extern);
pr.far_data, sc_extern);
if (!sel_table->table)
symtab_addsymbol (pr.symtab, sel_table);
reloc_def_def (sel_table->s.def, sel_sym->s.def);
@ -2768,7 +2763,7 @@ super_expr (class_type_t *class_type)
if (!sym || sym->table != current_symtab) {
sym = new_symbol (".super");
initialize_def (sym, &type_obj_super, 0, current_symtab->space,
st_local);
sc_local);
}
super = new_symbol_expr (sym);

1184
tools/qfcc/source/flow.c Normal file

File diff suppressed because it is too large Load diff

View file

@ -39,6 +39,7 @@
#endif
#include <stdlib.h>
#include "QF/alloc.h"
#include "QF/dstring.h"
#include "QF/hash.h"
#include "QF/va.h"
@ -52,10 +53,12 @@
#include "diagnostic.h"
#include "emit.h"
#include "expr.h"
#include "flow.h"
#include "function.h"
#include "opcodes.h"
#include "options.h"
#include "reloc.h"
#include "shared.h"
#include "statements.h"
#include "strpool.h"
#include "symtab.h"
@ -450,12 +453,12 @@ build_scope (symbol_t *fsym, symtab_t *parent)
symtab = new_symtab (parent, stab_local);
fsym->s.func->symtab = symtab;
symtab->space = defspace_new ();
symtab->space = defspace_new (ds_virtual);
current_symtab = symtab;
if (fsym->type->t.func.num_params < 0) {
args = new_symbol_type (".args", &type_va_list);
initialize_def (args, args->type, 0, symtab->space, st_local);
initialize_def (args, args->type, 0, symtab->space, sc_param);
}
for (p = fsym->params, i = 0; p; p = p->next) {
@ -464,14 +467,14 @@ build_scope (symbol_t *fsym, symtab_t *parent)
if (!p->type)
continue; // non-param selector
param = new_symbol_type (p->name, p->type);
initialize_def (param, param->type, 0, symtab->space, st_local);
initialize_def (param, param->type, 0, symtab->space, sc_param);
i++;
}
if (args) {
while (i < MAX_PARMS) {
param = new_symbol_type (va (".par%d", i), &type_param);
symtab_addsymbol (symtab, param);
initialize_def (param, &type_param, 0, symtab->space, sc_param);
i++;
}
}
@ -498,14 +501,14 @@ make_function (symbol_t *sym, const char *nice_name, defspace_t *space,
reloc_t *relocs = 0;
if (sym->sy_type != sy_func)
internal_error (0, "%s is not a function", sym->name);
if (storage == st_extern && sym->s.func)
if (storage == sc_extern && sym->s.func)
return;
if (!sym->s.func) {
sym->s.func = new_function (sym->name, nice_name);
sym->s.func->sym = sym;
}
if (sym->s.func->def && sym->s.func->def->external
&& storage != st_extern) {
&& storage != sc_extern) {
//FIXME this really is not the right way
relocs = sym->s.func->def->relocs;
free_def (sym->s.func->def);
@ -558,7 +561,6 @@ begin_function (symbol_t *sym, const char *nicename, symtab_t *parent,
if (options.code.debug) {
pr_lineno_t *lineno = new_lineno ();
sym->s.func->line_info = lineno - pr.linenos;
sym->s.func->local_defs = pr.num_locals;
}
build_scope (sym, parent);
@ -639,12 +641,15 @@ finish_function (function_t *f)
void
emit_function (function_t *f, expr_t *e)
{
sblock_t *sblock;
f->code = pr.code->size;
lineno_base = f->def->line;
sblock = make_statements (e);
emit_statements (sblock);
f->sblock = make_statements (e);
if (options.code.optimize) {
flow_data_flow (f);
} else {
statements_count_temps (f->sblock);
}
emit_statements (f->sblock);
}
int

View file

@ -40,6 +40,7 @@
#include <ctype.h>
#include "QF/alloc.h"
#include "QF/hash.h"
#include "QF/quakeio.h"
@ -86,8 +87,7 @@ static void
frame_free (void *_f, void *unused)
{
frame_t *f = (frame_t *)_f;
f->next = free_frames;
free_frames = f;
FREE (frames, f);
}
int

View file

@ -50,6 +50,7 @@
#include <fcntl.h>
#include <errno.h>
#include "QF/alloc.h"
#include "QF/dstring.h"
#include "QF/hash.h"
#include "QF/pakfile.h"
@ -536,7 +537,8 @@ add_data (int space, qfo_mspace_t *data)
{
if (space < 0 || space >= qfo_num_spaces || !work_spaces[space])
linker_internal_error ("bad space for add_data (): %d", space);
defspace_add_data (*work_spaces[space], data->d.data, data->data_size);
if (data->data_size)
defspace_add_data (*work_spaces[space], data->d.data, data->data_size);
work->spaces[space].d.data = (*work_spaces[space])->data;
work->spaces[space].data_size = (*work_spaces[space])->size;
}
@ -648,10 +650,10 @@ linker_begin (void)
work_strings = strpool_new ();
work_code = codespace_new ();
work_near_data = defspace_new ();
work_far_data = defspace_new ();
work_entity_data = defspace_new ();
work_type_data = defspace_new ();
work_near_data = defspace_new (ds_backed);
work_far_data = defspace_new (ds_backed);
work_entity_data = defspace_new (ds_virtual);
work_type_data = defspace_new (ds_backed);
pr.strings = work_strings;

View file

@ -55,6 +55,7 @@
#include "method.h"
#include "options.h"
#include "reloc.h"
#include "shared.h"
#include "strpool.h"
#include "struct.h"
#include "symtab.h"
@ -260,7 +261,7 @@ send_message (int super)
current_symtab = pr.symtab;
sym = new_symbol_type (sm_name, sm_type);
sym = function_symbol (sym, 0, 1);
make_function (sym, 0, sym->table->space, st_extern);
make_function (sym, 0, sym->table->space, sc_extern);
current_symtab = save;
}
return new_symbol_expr (sym);
@ -387,7 +388,7 @@ emit_selectors (void)
sel_type = array_type (type_SEL.t.fldptr.type, sel_index);
sel_sym = make_symbol ("_OBJ_SELECTOR_TABLE", sel_type,
pr.far_data, st_static);
pr.far_data, sc_static);
if (!sel_sym->table)
symtab_addsymbol (pr.symtab, sel_sym);
sel_def = sel_sym->s.def;
@ -489,7 +490,7 @@ emit_methods (methodlist_t *methods, const char *name, int instance)
methods_struct[2].type = array_type (&type_obj_method, count);
return emit_structure (va ("_OBJ_%s_METHODS_%s", type, name), 's',
methods_struct, 0, methods, st_static);
methods_struct, 0, methods, sc_static);
}
static void
@ -556,7 +557,7 @@ emit_method_descriptions (methodlist_t *methods, const char *name,
method_list_struct[1].type = array_type (&type_obj_method_description,
count);
return emit_structure (va ("_OBJ_%s_METHODS_%s", type, name), 's',
method_list_struct, 0, methods, st_static);
method_list_struct, 0, methods, sc_static);
}
void

View file

@ -90,6 +90,8 @@ qfo_def_flags (def_t *def)
flags |= QFOD_SYSTEM;
if (def->nosave)
flags |= QFOD_NOSAVE;
if (def->param)
flags |= QFOD_PARAM;
return flags;
}
@ -312,8 +314,12 @@ qfo_from_progs (pr_info_t *pr)
qfo_encode_functions (qfo, &def, &reloc, qfo->spaces + qfo_num_spaces,
pr->func_head);
qfo->lines = pr->linenos;
qfo->num_lines = pr->num_linenos;
if (pr->num_linenos) {
qfo->lines = malloc (pr->num_linenos * sizeof (pr_lineno_t));
memcpy (qfo->lines, pr->linenos,
pr->num_linenos * sizeof (pr_lineno_t));
qfo->num_lines = pr->num_linenos;
}
// strings must be done last because encoding defs and functions adds the
// names
qfo_init_string_space (qfo, &qfo->spaces[qfo_strings_space], pr->strings);

View file

@ -84,7 +84,7 @@ qfo_new_encoding (type_t *type, int size)
size += sizeof (qfot_type_t) - sizeof (enc->t);
size /= sizeof (pr_type_t);
def = new_def (type->encoding, 0, pr.type_data, st_static);
def = new_def (type->encoding, 0, pr.type_data, sc_static);
def->offset = defspace_alloc_loc (pr.type_data, size);
enc = D_POINTER (qfot_type_t, def);
@ -176,7 +176,7 @@ qfo_encode_struct (type_t *type)
if (type->meta == ty_enum)
sy = sy_const;
if (!type->t.symtab) {
def = new_def (type->encoding, 0, pr.type_data, st_extern);
def = new_def (type->encoding, 0, pr.type_data, sc_extern);
return def;
}
for (num_fields = 0, sym = type->t.symtab->symbols; sym; sym = sym->next) {
@ -212,7 +212,7 @@ qfo_encode_struct (type_t *type)
if (i == num_fields)
internal_error (0, "whoa, what happened?");
if (sym->sy_type == sy_const)
offset = sym->s.value.v.integer_val;
offset = sym->s.value->v.integer_val;
else
offset = sym->s.offset;
ENC_DEF (strct->fields[i].type, field_types[i]);

View file

@ -41,10 +41,10 @@
#include <QF/hash.h>
#include "def.h"
#include "opcodes.h"
#include "options.h"
#include "qfcc.h"
#include "statements.h"
#include "type.h"
hashtab_t *opcode_type_table;
@ -92,7 +92,8 @@ check_operand_type (etype_t ot1, etype_t ot2)
}
opcode_t *
opcode_find (const char *name, def_t *def_a, def_t *def_b, def_t *def_c)
opcode_find (const char *name, operand_t *op_a, operand_t *op_b,
operand_t *op_c)
{
opcode_t search_op;
opcode_t *op;
@ -101,9 +102,9 @@ opcode_find (const char *name, def_t *def_a, def_t *def_b, def_t *def_c)
int i;
search_op.name = name;
search_op.type_a = def_a ? def_a->type->type : ev_invalid;
search_op.type_b = def_b ? def_b->type->type : ev_invalid;
search_op.type_c = def_c ? def_c->type->type : ev_invalid;
search_op.type_a = op_a ? op_a->type : ev_invalid;
search_op.type_b = op_b ? op_b->type : ev_invalid;
search_op.type_c = op_c ? op_c->type : ev_invalid;
op = Hash_FindElement (opcode_type_table, &search_op);
if (op)
return op;
@ -121,33 +122,46 @@ opcode_find (const char *name, def_t *def_a, def_t *def_b, def_t *def_c)
return op;
}
static void
opcode_free (void *_op, void *unused)
{
free (_op);
}
void
opcode_init (void)
{
opcode_t *op;
opcode_t *op, *mop;
PR_Opcode_Init ();
opcode_type_table = Hash_NewTable (1021, 0, 0, 0);
Hash_SetHashCompare (opcode_type_table, get_hash, compare);
opcode_void_table = Hash_NewTable (1021, get_key, 0, 0);
if (opcode_type_table) {
Hash_FlushTable (opcode_void_table);
Hash_FlushTable (opcode_type_table);
} else {
PR_Opcode_Init ();
opcode_type_table = Hash_NewTable (1021, 0, opcode_free, 0);
Hash_SetHashCompare (opcode_type_table, get_hash, compare);
opcode_void_table = Hash_NewTable (1021, get_key, 0, 0);
}
for (op = pr_opcodes; op->name; op++) {
if (op->min_version > options.code.progsversion)
continue;
mop = malloc (sizeof (opcode_t));
*mop = *op;
if (options.code.progsversion == PROG_ID_VERSION) {
// v6 progs have no concept of integer, but the QF engine
// treats the operands of certain operands as integers
// irrespective the progs version, so convert the engine's
// view of the operands to the prog's view.
if (op->type_a == ev_integer)
op->type_a = ev_float;
if (op->type_b == ev_integer)
op->type_b = ev_float;
if (op->type_c == ev_integer)
op->type_c = ev_float;
if (mop->type_a == ev_integer)
mop->type_a = ev_float;
if (mop->type_b == ev_integer)
mop->type_b = ev_float;
if (mop->type_c == ev_integer)
mop->type_c = ev_float;
}
Hash_AddElement (opcode_type_table, op);
if (op->type_a == ev_void || op->type_b == ev_void
|| op->type_c == ev_void)
Hash_Add (opcode_void_table, op);
Hash_AddElement (opcode_type_table, mop);
if (mop->type_a == ev_void || mop->type_b == ev_void
|| mop->type_c == ev_void)
Hash_Add (opcode_void_table, mop);
}
}

View file

@ -116,6 +116,7 @@ static const char *short_options =
"M::"
"N:" // notice options
"o:" // output file
"O" // optimize
"P:" // progs.src name
"p:" // strip path
"q" // quiet
@ -197,6 +198,7 @@ code_usage (void)
" [no-]fast-float Use float values directly in \"if\" statements.\n"
" help Display his text.\n"
" [no-]local-merging Merge the local variable blocks into one.\n"
" [no-]optimize Perform various optimizations on the code.\n"
" [no-]short-circuit Generate short circuit code for logical\n"
" operators.\n"
" [no-]single-cpp Convert progs.src to cpp input file.\n"
@ -357,6 +359,9 @@ DecodeArgs (int argc, char **argv)
case 'g': // debug
options.code.debug = true;
break;
case 'O': // optimize
options.code.optimize = true;
break;
case OPT_FRAMES:
options.frames_files = 1;
break;
@ -395,6 +400,18 @@ DecodeArgs (int argc, char **argv)
options.block_dot.dead = flag;
} else if (!(strcasecmp (temp, "final"))) {
options.block_dot.final = flag;
} else if (!(strcasecmp (temp, "dags"))) {
options.block_dot.dags = flag;
} else if (!(strcasecmp (temp, "expr"))) {
options.block_dot.expr = flag;
} else if (!(strcasecmp (temp, "flow"))) {
options.block_dot.flow = flag;
} else if (!(strcasecmp (temp, "reaching"))) {
options.block_dot.reaching = flag;
} else if (!(strcasecmp (temp, "live"))) {
options.block_dot.live = flag;
} else if (!(strcasecmp (temp, "post"))) {
options.block_dot.post = flag;
}
temp = strtok (NULL, ",");
}
@ -404,6 +421,12 @@ DecodeArgs (int argc, char **argv)
options.block_dot.thread = true;
options.block_dot.dead = true;
options.block_dot.final = true;
options.block_dot.dags = true;
options.block_dot.expr = true;
options.block_dot.flow = true;
options.block_dot.reaching = true;
options.block_dot.live = true;
options.block_dot.post = true;
}
break;
case 'c':
@ -440,6 +463,8 @@ DecodeArgs (int argc, char **argv)
code_usage ();
} else if (!(strcasecmp (temp, "local-merging"))) {
options.code.local_merging = flag;
} else if (!(strcasecmp (temp, "optimize"))) {
options.code.optimize = flag;
} else if (!(strcasecmp (temp, "short-circuit"))) {
options.code.short_circuit = flag;
} else if (!(strcasecmp (temp, "single-cpp"))) {

View file

@ -0,0 +1,92 @@
/*
praga.c
pragma handling
Copyright (C) 2012 Bill Currie <bill@taniwha.org>
Author: Bill Currie <bill@taniwha.org>
Date: 2012/11/22
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to:
Free Software Foundation, Inc.
59 Temple Place - Suite 330
Boston, MA 02111-1307, USA
*/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#ifdef HAVE_STRING_H
# include <string.h>
#endif
#ifdef HAVE_STRINGS_H
# include <strings.h>
#endif
#include <stdlib.h>
#include "QF/pr_comp.h"
#include "diagnostic.h"
#include "opcodes.h"
#include "options.h"
#include "pragma.h"
#include "type.h"
static void
set_traditional (int traditional)
{
switch (traditional) {
case 0:
options.traditional = 0;
options.advanced = true;
options.code.progsversion = PROG_VERSION;
type_default = &type_integer;
break;
case 1:
options.traditional = 1;
options.advanced = false;
options.code.progsversion = PROG_ID_VERSION;
type_default = &type_float;
break;
case 2:
options.traditional = 2;
options.advanced = false;
options.code.progsversion = PROG_ID_VERSION;
type_default = &type_float;
break;
}
opcode_init (); // reset the opcode table
}
void
pragma (const char *id)
{
if (!strcmp (id, "traditional")) {
set_traditional (2);
return;
}
if (!strcmp (id, "extended")) {
set_traditional (1);
return;
}
if (!strcmp (id, "advanced")) {
set_traditional (0);
return;
}
warning (0, "unknown pragma: %s", id);
}

View file

@ -51,7 +51,9 @@
#include "expr.h"
#include "grab.h"
#include "options.h"
#include "pragma.h"
#include "qfcc.h"
#include "shared.h"
#include "strpool.h"
#include "struct.h"
#include "symtab.h"
@ -98,9 +100,10 @@ INT ({D}+|0[xX]{X}+)
RANGE \.\.
ELLIPSIS \.\.\.
FRAMEID {ID}(\.{ID})*
PRAGMAID {ID}(-{ID})*
STRING \"(\\.|[^"\\])*\"
%x GRAB_FRAME GRAB_OTHER GRAB_WRITE COMMENT
%x GRAB_FRAME GRAB_OTHER GRAB_WRITE COMMENT PRAGMA
%%
grab_frame = GRAB_FRAME;
@ -118,7 +121,7 @@ STRING \"(\\.|[^"\\])*\"
^#{s}+{D}+{s}+\"(\.|[^"\n])*\".*$ { line_info (yytext + 1); }
^#line{s}+{D}+{s}+\"(\.|[^"\n])*\".*$ { line_info (yytext + 5); }
^{s}*#{s}*pragma.*$ /* skip */
^{s}*#{s}*pragma{s}+ { BEGIN (PRAGMA); }
{INT}+[uU]? {
const char *c = yytext + yyleng - 1;
@ -240,6 +243,7 @@ STRING \"(\\.|[^"\\])*\"
write_frame_macros (s);
BEGIN (GRAB_OTHER); // ignore rest of line
}
<PRAGMA>{ID} { pragma (yytext); }
<*>\r*\n {
pr.source_line++;
@ -297,15 +301,15 @@ static keyword_t keywords[] = {
{"case", CASE, 0, 1, PROG_ID_VERSION, 0},
{"default", DEFAULT, 0, 1, PROG_ID_VERSION, 0},
{"nil", NIL, 0, 0, PROG_ID_VERSION, 0},
{"@nil", NIL, 0, 2, PROG_ID_VERSION, 0},
{"struct", STRUCT, 0, 0, PROG_VERSION, 0},
{"union", STRUCT, 0, 0, PROG_VERSION, 0},
{"enum", ENUM, 0, 0, PROG_ID_VERSION, 0},
{"typedef", TYPEDEF, 0, 0, PROG_ID_VERSION, 0},
// these two are a hack to trigger the initialization of the class
// this is a hack to trigger the initialization of the class
// sytem if they are seen before any other Objective-QC symbol
{"obj_module_s", 0, 0, 0, PROG_VERSION, 1},
{"obj_module_t", 0, 0, 0, PROG_VERSION, 1},
{"obj_module", 0, 0, 0, PROG_VERSION, 1},
{"@class", CLASS, 0, 0, PROG_VERSION, 1},
{"@defs", DEFS, 0, 0, PROG_VERSION, 1},

View file

@ -183,7 +183,7 @@ int yylex (void);
%type <expr> opt_expr fexpr expr element_list element
%type <expr> optional_state_expr think opt_step texpr
%type <expr> statement statements compound_statement
%type <expr> label break_label continue_label
%type <expr> else label break_label continue_label
%type <expr> unary_expr cast_expr opt_arg_list arg_list
%type <switch_block> switch_block
%type <symbol> identifier
@ -333,7 +333,7 @@ function_body
$<symtab>$ = current_symtab;
current_func = begin_function ($<symbol>2, 0, current_symtab, 0);
current_symtab = current_func->symtab;
current_storage = st_local;
current_storage = sc_local;
}
compound_statement
{
@ -413,10 +413,10 @@ external_decl
;
storage_class
: EXTERN { $$ = make_spec (0, st_extern, 0, 0); }
| STATIC { $$ = make_spec (0, st_static, 0, 0); }
| SYSTEM { $$ = make_spec (0, st_system, 0, 0); }
| TYPEDEF { $$ = make_spec (0, st_global, 1, 0); }
: EXTERN { $$ = make_spec (0, sc_extern, 0, 0); }
| STATIC { $$ = make_spec (0, sc_static, 0, 0); }
| SYSTEM { $$ = make_spec (0, sc_system, 0, 0); }
| TYPEDEF { $$ = make_spec (0, sc_global, 1, 0); }
| OVERLOAD { $$ = make_spec (0, current_storage, 0, 1); }
;
@ -852,12 +852,15 @@ decl
{
specifier_t spec = $<spec>0;
type_t *type;
storage_class_t sc = $<spec>0.storage;
struct defspace_s *space = current_symtab->space;
if (!spec.type)
spec.type = type_default;
if (sc == sc_static)
space = pr.near_data;
type = find_type (append_type ($1->type, spec.type));
initialize_def ($1, type, $2, current_symtab->space,
$<spec>0.storage);
initialize_def ($1, type, $2, space, sc);
}
;
@ -939,7 +942,7 @@ code_func
$<symtab>$ = current_symtab;
current_func = begin_function ($<symbol>0, 0, current_symtab, 0);
current_symtab = current_func->symtab;
current_storage = st_local;
current_storage = sc_local;
}
compound_statement
{
@ -1034,7 +1037,7 @@ local_def
: local_specifiers
{
if (!$1.storage)
$1.storage = st_local;
$1.storage = sc_local;
$<spec>$ = $1;
local_expr = new_block_expr ();
}
@ -1044,6 +1047,7 @@ local_def
local_expr = 0;
(void) ($<spec>2);
}
;
statement
: ';' { $$ = 0; }
@ -1086,11 +1090,11 @@ statement
}
| IF '(' texpr ')' statement %prec IFX
{
$$ = build_if_statement ($3, $5, 0);
$$ = build_if_statement ($3, $5, 0, 0);
}
| IF '(' texpr ')' statement ELSE statement
| IF '(' texpr ')' statement else statement
{
$$ = build_if_statement ($3, $5, $7);
$$ = build_if_statement ($3, $5, $6, $7);
}
| FOR break_label continue_label
'(' opt_expr ';' opt_expr ';' opt_expr ')' statement
@ -1119,6 +1123,14 @@ statement
}
;
else
: ELSE
{
// this is only to get the the file and line number info
$$ = new_nil_expr ();
}
;
label
: /* empty */
{
@ -1605,7 +1617,7 @@ methoddef
method->func = sym->s.func;
method->def = sym->s.func->def;
current_symtab = current_func->symtab;
current_storage = st_local;
current_storage = sc_local;
}
compound_statement
{

View file

@ -84,6 +84,7 @@
#include "opcodes.h"
#include "options.h"
#include "reloc.h"
#include "shared.h"
#include "strpool.h"
#include "struct.h"
#include "symtab.h"
@ -137,6 +138,10 @@ InitData (void)
codespace_delete (pr.code);
strpool_delete (pr.strings);
}
if (pr.linenos)
free (pr.linenos);
memset (&pr, 0, sizeof (pr));
pr.source_line = 1;
pr.error_count = 0;
@ -150,23 +155,21 @@ InitData (void)
line->fa.func = -1;
line->line = -1;
pr.data = defspace_new ();
pr.far_data = defspace_new (ds_backed);
pr.far_data = defspace_new ();
pr.near_data = defspace_new ();
pr.near_data = defspace_new (ds_backed);
pr.near_data->data = calloc (65536, sizeof (pr_type_t));
pr.near_data->max_size = 65536;
pr.near_data->grow = 0;
pr.type_data = defspace_new ();
pr.type_data = defspace_new (ds_backed);
defspace_alloc_loc (pr.type_data, 4);// reserve space for a null descriptor
pr.symtab = new_symtab (0, stab_global);
pr.symtab->space = pr.near_data;
current_symtab = pr.symtab;
pr.entity_data = defspace_new ();
pr.entity_data = defspace_new (ds_virtual);
pr.entity_fields = new_symtab (0, stab_global);
pr.entity_fields->space = pr.entity_data;;
@ -484,8 +487,8 @@ separate_compile (void)
const char **file, *ext;
const char **temp_files;
lang_t lang;
dstring_t *output_file = dstring_newstr ();
dstring_t *extension = dstring_newstr ();
dstring_t *output_file;
dstring_t *extension;
int err = 0;
int i;
@ -500,6 +503,9 @@ separate_compile (void)
i++;
temp_files = calloc (i + 1, sizeof (const char*));
output_file = dstring_newstr ();
extension = dstring_newstr ();
for (file = source_files, i = 0; *file; file++) {
ext = QFS_FileExtension (*file);
dstring_copysubstr (output_file, *file, ext - *file);
@ -524,6 +530,8 @@ separate_compile (void)
"not done\n", this_program, *file);
}
}
dstring_delete (output_file);
dstring_delete (extension);
if (!err && !options.compile) {
InitData ();
linker_begin ();
@ -538,14 +546,17 @@ separate_compile (void)
} else {
err = linker_add_lib (*file);
}
if (err)
if (err) {
free (temp_files);
return err;
}
}
err = finish_link ();
if (!options.save_temps)
for (file = temp_files; *file; file++)
unlink (*file);
}
free (temp_files);
return err;
}

View file

@ -39,6 +39,7 @@
#include "expr.h"
#include "grab.h"
#include "qfcc.h"
#include "shared.h"
#include "strpool.h"
#include "symtab.h"
#include "type.h"

View file

@ -133,7 +133,7 @@ int yylex (void);
%type <symbol> program_head identifier_list subprogram_head
%type <symtab> param_scope
%type <param> arguments parameter_list
%type <expr> compound_statement optional_statements statement_list
%type <expr> compound_statement else optional_statements statement_list
%type <expr> statement procedure_statement
%type <expr> expression_list expression unary_expr primary variable name
%type <op> sign
@ -239,7 +239,7 @@ subprogram_declaration
$<storage>$ = current_storage;
current_func = begin_function ($1, 0, current_symtab, 0);
current_symtab = current_func->symtab;
current_storage = st_local;
current_storage = sc_local;
}
declarations compound_statement ';'
{
@ -352,13 +352,13 @@ statement
}
| procedure_statement
| compound_statement
| IF expression THEN statement ELSE statement
| IF expression THEN statement else statement
{
$$ = build_if_statement ($2, $4, $6);
$$ = build_if_statement ($2, $4, $5, $6);
}
| IF expression THEN statement %prec IFX
{
$$ = build_if_statement ($2, $4, 0);
$$ = build_if_statement ($2, $4, 0, 0);
}
| WHILE expression DO statement
{
@ -372,6 +372,13 @@ statement
}
;
else
: ELSE
{
$$ = new_nil_expr ();
}
;
variable
: name
| name '[' expression ']' { $$ = array_expr ($1, $3); }

View file

@ -39,6 +39,8 @@
#endif
#include <stdlib.h>
#include "QF/alloc.h"
#include "codespace.h"
#include "def.h"
#include "defspace.h"
@ -165,7 +167,9 @@ relocate_refs (reloc_t *reloc, int offset)
RELOC (reloc) += offset;
break;
case rel_def_field_ofs:
RELOC (reloc) += pr.data->data[offset].integer_var;
//FIXME what is correct here?
//RELOC (reloc) += pr.data->data[offset].integer_var;
RELOC (reloc) += pr.near_data->data[offset].integer_var;
break;
}
reloc = reloc->next;

View file

@ -44,7 +44,7 @@ function_t *current_func;
class_type_t *current_class;
expr_t *local_expr;
vis_t current_visibility;
storage_class_t current_storage = st_global;
storage_class_t current_storage = sc_global;
symtab_t *current_symtab;
/* When defining a new symbol, already existing symbols must be in a

View file

@ -39,9 +39,12 @@
#include <stdlib.h>
#include "QF/alloc.h"
#include "QF/va.h"
#include "dags.h"
#include "diagnostic.h"
#include "dot.h"
#include "expr.h"
#include "function.h"
#include "options.h"
@ -51,18 +54,32 @@
#include "strpool.h"
#include "symtab.h"
#include "type.h"
#include "value.h"
#include "qc-parse.h"
static const char *op_type_names[] = {
"op_def",
"op_value",
"op_label",
"op_temp",
};
const char *
optype_str (op_type_e type)
{
if (type < 0 || type > op_temp)
return "<invalid op_type>";
return op_type_names[type];
}
const char *
operand_string (operand_t *op)
{
type_t *type;
if (!op)
return "";
switch (op->op_type) {
case op_symbol:
return op->o.symbol->name;
case op_def:
return op->o.def->name;
case op_value:
switch (op->o.value->type) {
case ev_string:
@ -106,19 +123,18 @@ operand_string (operand_t *op)
case op_label:
return op->o.label->name;
case op_temp:
return va ("tmp %p", op);
case op_pointer:
type = op->o.pointer->type;
if (op->o.pointer->def)
return va ("(%s)[%d]&lt;%s&gt;",
type ? pr_type_name[type->type] : "???",
op->o.pointer->val, op->o.pointer->def->name);
else
return va ("(%s)[%d]",
type ? pr_type_name[type->type] : "???",
op->o.pointer->val);
if (op->o.tempop.alias)
return va ("<tmp %p:%d:%p:%d>", op, op->o.tempop.users,
op->o.tempop.alias,
op->o.tempop.alias->o.tempop.users);
return va ("<tmp %p:%d>", op, op->o.tempop.users);
case op_alias:
return operand_string (op->o.alias);//FIXME better output
{
const char *alias = operand_string (op->o.alias);
char *buf = alloca (strlen (alias) + 1);
strcpy (buf, alias);
return va ("alias(%s,%s)", pr_type_name[op->type], buf);
}
}
return ("??");
}
@ -127,9 +143,9 @@ static void
print_operand (operand_t *op)
{
switch (op->op_type) {
case op_symbol:
case op_def:
printf ("(%s) ", pr_type_name[op->type]);
printf ("%s", op->o.symbol->name);
printf ("%s", op->o.def->name);
break;
case op_value:
printf ("(%s) ", pr_type_name[op->type]);
@ -181,14 +197,13 @@ print_operand (operand_t *op)
break;
case op_temp:
printf ("tmp (%s) %p", pr_type_name[op->type], op);
break;
case op_pointer:
printf ("ptr %p", op->o.pointer);
if (op->o.tempop.def)
printf (" %s", op->o.tempop.def->name);
break;
case op_alias:
printf ("alias %s ", pr_type_name[op->type]);
printf ("alias(%s,", pr_type_name[op->type]);
print_operand (op->o.alias);
break;
printf (")");
}
}
@ -211,7 +226,7 @@ static sblock_t *free_sblocks;
static statement_t *free_statements;
static operand_t *free_operands;
static sblock_t *
sblock_t *
new_sblock (void)
{
sblock_t *sblock;
@ -220,7 +235,7 @@ new_sblock (void)
return sblock;
}
static void
void
sblock_add_statement (sblock_t *sblock, statement_t *statement)
{
// this should normally be null, but might be inserting
@ -229,11 +244,12 @@ sblock_add_statement (sblock_t *sblock, statement_t *statement)
sblock->tail = &statement->next;
}
static statement_t *
new_statement (const char *opcode, expr_t *expr)
statement_t *
new_statement (st_type_t type, const char *opcode, expr_t *expr)
{
statement_t *statement;
ALLOC (256, statement_t, statements, statement);
statement->type = type;
statement->opcode = save_string (opcode);
statement->expr = expr;
return statement;
@ -248,30 +264,22 @@ new_operand (op_type_e op)
return operand;
}
static void
void
free_operand (operand_t *op)
{
if (op->next) {
//FIXME this should be an error, but due to the way operands are used,
//it can happen.
debug (0, "free_operand: double free");
return;
}
op->next = free_operands;
free_operands = op;
FREE (operands, op);
}
static void
free_statement (statement_t *s)
{
if (s->opa)
free_operand (s->opa);
if (s->opb)
free_operand (s->opb);
if (s->opc)
free_operand (s->opc);
s->next = free_statements;
free_statements = s;
// if (s->opa)
// free_operand (s->opa);
// if (s->opb)
// free_operand (s->opb);
// if (s->opc)
// free_operand (s->opc);
FREE (statements, s);
}
static void
@ -282,30 +290,62 @@ free_sblock (sblock_t *sblock)
sblock->statements = s->next;
free_statement (s);
}
sblock->next = free_sblocks;
free_sblocks = sblock;
FREE (sblocks, sblock);
}
static operand_t *
operand_t *
def_operand (def_t *def, type_t *type)
{
operand_t *op;
if (!type)
type = def->type;
op = new_operand (op_def);
op->type = low_level_type (type);
op->o.def = def;
return op;
}
operand_t *
value_operand (ex_value_t *value)
{
operand_t *op;
op = new_operand (op_value);
op->type = value->type;
op->o.value = value;
return op;
}
operand_t *
temp_operand (type_t *type)
{
operand_t *op = new_operand (op_temp);
op->o.tempop.type = type;
op->type = low_level_type (type);
op->size = type_size (type);
return op;
}
operand_t *
alias_operand (etype_t type, operand_t *op)
{
operand_t *aop;
if (pr_type_size[type] != pr_type_size[op->type])
internal_error (0, "aliasing operand with type of diffent size");
aop = new_operand (op_alias);
aop->o.alias = op;
aop->type = type;
aop->size = pr_type_size[type];
return aop;
}
static operand_t *
short_operand (short short_val)
{
operand_t *op = new_operand (op_value);
op->type = ev_short;
op->o.value = calloc (1, sizeof (ex_value_t));
op->o.value->type = ev_short;
op->o.value->v.short_val = short_val;
return op;
ex_value_t *val = new_short_val (short_val);
return value_operand (val);
}
static const char *
@ -348,22 +388,90 @@ convert_op (int op)
}
}
static int
is_goto (statement_t *s)
int
statement_is_cond (statement_t *s)
{
if (!s)
return 0;
return !strncmp (s->opcode, "<IF", 3);
}
int
statement_is_goto (statement_t *s)
{
if (!s)
return 0;
return !strcmp (s->opcode, "<GOTO>");
}
static int
is_return (statement_t *s)
int
statement_is_jumpb (statement_t *s)
{
if (!s)
return 0;
return !strcmp (s->opcode, "<JUMPB>");
}
int
statement_is_call (statement_t *s)
{
if (!s)
return 0;
if (!strncmp (s->opcode, "<CALL", 5))
return 1;
if (!strncmp (s->opcode, "<RCALL", 6))
return 2;
return 0;
}
int
statement_is_return (statement_t *s)
{
if (!s)
return 0;
return !strncmp (s->opcode, "<RETURN", 7);
}
static int
is_conditional (statement_t *s)
sblock_t *
statement_get_target (statement_t *s)
{
return !strncmp (s->opcode, "<IF", 3);
if (statement_is_cond (s))
return s->opb->o.label->dest;
if (statement_is_goto (s))
return s->opa->o.label->dest;
return 0;
}
sblock_t **
statement_get_targetlist (statement_t *s)
{
sblock_t **target_list;
int count = 0, i;
def_t *table = 0;
expr_t *e;
if (statement_is_cond (s)) {
count = 1;
} else if (statement_is_goto (s)) {
count = 1;
} else if (statement_is_jumpb (s)) {
table = s->opa->o.def;
count = table->type->t.array.size;
}
target_list = malloc ((count + 1) * sizeof (sblock_t *));
target_list[count] = 0;
if (statement_is_cond (s)) {
target_list[0] = statement_get_target (s);
} else if (statement_is_goto (s)) {
target_list[0] = statement_get_target (s);
} else if (statement_is_jumpb (s)) {
if (table->alias)
internal_error (0, "aliased jump table");
e = table->initializer->e.block.head; //FIXME check!!!
for (i = 0; i < count; e = e->next, i++)
target_list[i] = e->e.labelref.label->dest;
}
return target_list;
}
static void
@ -397,17 +505,17 @@ statement_branch (sblock_t *sblock, expr_t *e)
const char *opcode;
if (e->type == ex_uexpr && e->e.expr.op == 'g') {
s = new_statement ("<GOTO>", e);
s = new_statement (st_flow, "<GOTO>", e);
s->opa = new_operand (op_label);
s->opa->o.label = &e->e.expr.e1->e.label;
} else {
if (e->e.expr.op == 'g') {
s = new_statement ("<JUMPB>", e);
s = new_statement (st_flow, "<JUMPB>", e);
sblock = statement_subexpr (sblock, e->e.expr.e1, &s->opa);
sblock = statement_subexpr (sblock, e->e.expr.e2, &s->opb);
} else {
opcode = convert_op (e->e.expr.op);
s = new_statement (opcode, e);
s = new_statement (st_flow, opcode, e);
sblock = statement_subexpr (sblock, e->e.expr.e1, &s->opa);
s->opb = new_operand (op_label);
s->opb->o.label = &e->e.expr.e2->e.label;
@ -429,6 +537,7 @@ expr_assign (sblock_t *sblock, expr_t *e, operand_t **op)
operand_t *dst = 0;
operand_t *ofs = 0;
const char *opcode = convert_op (e->e.expr.op);
st_type_t type;
if (e->e.expr.op == '=') {
sblock = statement_subexpr (sblock, dst_expr, &dst);
@ -439,6 +548,7 @@ expr_assign (sblock_t *sblock, expr_t *e, operand_t **op)
*op = dst;
if (src == dst)
return sblock;
type = st_assign;
} else {
//FIXME this sucks. find a better way to handle both pointer
//dereferences and pointer assignements
@ -459,8 +569,9 @@ expr_assign (sblock_t *sblock, expr_t *e, operand_t **op)
}
if (op)
*op = src;
type = st_ptrassign;
}
s = new_statement (opcode, e);
s = new_statement (type, opcode, e);
s->opa = src;
s->opb = dst;
s->opc = ofs;
@ -487,7 +598,7 @@ expr_move (sblock_t *sblock, expr_t *e, operand_t **op)
dst = *op;
sblock = statement_subexpr (sblock, src_expr, &src);
sblock = statement_subexpr (sblock, size_expr, &size);
s = new_statement (convert_op (e->e.expr.op), e);
s = new_statement (st_move, convert_op (e->e.expr.op), e);
s->opa = src;
s->opb = size;
s->opc = dst;
@ -507,7 +618,7 @@ vector_call (sblock_t *sblock, expr_t *earg, expr_t *param, int ind,
for (i = 0; i < 3; i++) {
n = new_name_expr (names[i]);
v = new_float_expr (earg->e.value.v.vector_val[i]);
v = new_float_expr (earg->e.value->v.vector_val[i]);
a = assign_expr (binary_expr ('.', param, n), v);
param = new_param_expr (get_type (earg), ind);
a->line = earg->line;
@ -543,7 +654,7 @@ expr_call (sblock_t *sblock, expr_t *call, operand_t **op)
pref = "R";
sblock = statement_subexpr (sblock, param, &arguments[ind]);
if (options.code.vector_calls && a->type == ex_value
&& a->e.value.type == ev_vector)
&& a->e.value->type == ev_vector)
sblock = vector_call (sblock, a, param, ind, &arguments[ind]);
else
sblock = statement_subexpr (sblock, a, &arguments[ind]);
@ -557,7 +668,7 @@ expr_call (sblock_t *sblock, expr_t *call, operand_t **op)
sblock = statement_slist (sblock, mov);
} else {
if (options.code.vector_calls && a->type == ex_value
&& a->e.value.type == ev_vector) {
&& a->e.value->type == ev_vector) {
sblock = vector_call (sblock, a, param, ind, 0);
} else {
operand_t *p = 0;
@ -566,7 +677,7 @@ expr_call (sblock_t *sblock, expr_t *call, operand_t **op)
arg = p;
sblock = statement_subexpr (sblock, a, &arg);
if (arg != p) {
s = new_statement ("=", a);
s = new_statement (st_assign, "=", a);
s->opa = arg;
s->opb = p;
sblock_add_statement (sblock, s);
@ -575,7 +686,7 @@ expr_call (sblock_t *sblock, expr_t *call, operand_t **op)
}
}
opcode = va ("<%sCALL%d>", pref, count);
s = new_statement (opcode, call);
s = new_statement (st_func, opcode, call);
sblock = statement_subexpr (sblock, func, &s->opa);
s->opb = arguments[0];
s->opc = arguments[1];
@ -597,7 +708,7 @@ expr_address (sblock_t *sblock, expr_t *e, operand_t **op)
static statement_t *
lea_statement (operand_t *pointer, operand_t *offset, expr_t *e)
{
statement_t *s = new_statement ("&", e);
statement_t *s = new_statement (st_expr, "&", e);
s->opa = pointer;
s->opb = offset;
s->opc = temp_operand (&type_pointer);
@ -607,7 +718,7 @@ lea_statement (operand_t *pointer, operand_t *offset, expr_t *e)
static statement_t *
address_statement (operand_t *value, expr_t *e)
{
statement_t *s = new_statement ("&", e);
statement_t *s = new_statement (st_expr, "&", e);
s->opa = value;
s->opc = temp_operand (&type_pointer);
return s;
@ -622,9 +733,9 @@ expr_deref (sblock_t *sblock, expr_t *deref, operand_t **op)
e = deref->e.expr.e1;
if (e->type == ex_uexpr && e->e.expr.op == '&'
&& e->e.expr.e1->type == ex_symbol) {
*op = new_operand (op_symbol);
(*op)->type = low_level_type (type);
(*op)->o.symbol = e->e.expr.e1->e.symbol;
if (e->e.expr.e1->e.symbol->sy_type != sy_var)
internal_error (e, "address of non-var");
*op = def_operand (e->e.expr.e1->e.symbol->s.def, type);
} else if (e->type == ex_expr && e->e.expr.op == '&') {
statement_t *s;
operand_t *ptr = 0;
@ -646,22 +757,22 @@ expr_deref (sblock_t *sblock, expr_t *deref, operand_t **op)
dst_addr = s->opc;
sblock_add_statement (sblock, s);
s = new_statement ("<MOVEP>", deref);
s = new_statement (st_move, "<MOVEP>", deref);
s->opa = src_addr;
s->opb = short_operand (type_size (type));
s->opc = dst_addr;
sblock_add_statement (sblock, s);
} else {
s = new_statement (".", deref);
s = new_statement (st_expr, ".", deref);
s->opa = ptr;
s->opb = offs;
s->opc = *op;
sblock_add_statement (sblock, s);
}
} else if (e->type == ex_value && e->e.value.type == ev_pointer) {
*op = new_operand (op_pointer);
(*op)->type = low_level_type (e->e.value.v.pointer.type);
(*op)->o.pointer = &e->e.value.v.pointer;
} else if (e->type == ex_value && e->e.value->type == ev_pointer) {
ex_pointer_t *ptr = &e->e.value->v.pointer;
*op = def_operand (alias_def (ptr->def, ptr->type, ptr->val),
ptr->type);
} else {
statement_t *s;
operand_t *ptr = 0;
@ -669,7 +780,7 @@ expr_deref (sblock_t *sblock, expr_t *deref, operand_t **op)
sblock = statement_subexpr (sblock, e, &ptr);
if (!*op)
*op = temp_operand (type);
s = new_statement (".", deref);
s = new_statement (st_expr, ".", deref);
s->opa = ptr;
s->opb = short_operand (0);
s->opc = *op;
@ -710,7 +821,7 @@ expr_expr (sblock_t *sblock, expr_t *e, operand_t **op)
opcode = convert_op (e->e.expr.op);
if (!opcode)
internal_error (e, "ice ice baby");
s = new_statement (opcode, e);
s = new_statement (st_expr, opcode, e);
sblock = statement_subexpr (sblock, e->e.expr.e1, &s->opa);
sblock = statement_subexpr (sblock, e->e.expr.e2, &s->opb);
if (!*op)
@ -725,9 +836,43 @@ expr_expr (sblock_t *sblock, expr_t *e, operand_t **op)
static sblock_t *
expr_alias (sblock_t *sblock, expr_t *e, operand_t **op)
{
*op = new_operand (op_alias);
(*op)->type = low_level_type (e->e.expr.type);
sblock = statement_subexpr (sblock, e->e.expr.e1, &(*op)->o.alias);
operand_t *aop = 0;
operand_t *top;
etype_t type;
def_t *def;
type = low_level_type (e->e.expr.type);
sblock = statement_subexpr (sblock, e->e.expr.e1, &aop);
if (aop->type == type) {
*op = aop;
return sblock;
}
if (aop->op_type == op_temp) {
while (aop->o.tempop.alias) {
aop = aop->o.tempop.alias;
if (aop->op_type != op_temp)
internal_error (e, "temp alias of non-temp var");
}
for (top = aop->o.tempop.alias_ops; top; top = top->next)
if (top->type == type)
break;
if (!top) {
top = new_operand (op_temp);
top->type = type;
top->o.tempop.alias = aop;
top->next = aop->o.tempop.alias_ops;
aop->o.tempop.alias_ops = top;
}
*op = top;
} else if (aop->op_type == op_def) {
def = aop->o.def;
while (def->alias)
def = def->alias;
*op = def_operand (alias_def (def, ev_types[type], 0), 0);
} else {
internal_error (e, "invalid alias target: %s: %s",
optype_str (aop->op_type), operand_string (aop));
}
return sblock;
}
@ -744,7 +889,7 @@ expr_cast (sblock_t *sblock, expr_t *e, operand_t **op)
operand_t *src = 0;
sblock = statement_subexpr (sblock, e->e.expr.e1, &src);
*op = temp_operand (e->e.expr.type);
s = new_statement ("<CONV>", e);
s = new_statement (st_expr, "<CONV>", e);
s->opa = src;
s->opc = *op;
sblock_add_statement (sblock, s);
@ -797,7 +942,7 @@ expr_uexpr (sblock_t *sblock, expr_t *e, operand_t **op)
opcode = convert_op (e->e.expr.op);
if (!opcode)
internal_error (e, "ice ice baby");
s = new_statement (opcode, e);
s = new_statement (st_expr, opcode, e);
sblock = statement_subexpr (sblock, e->e.expr.e1, &s->opa);
if (!*op)
*op = temp_operand (e->e.expr.type);
@ -810,9 +955,18 @@ expr_uexpr (sblock_t *sblock, expr_t *e, operand_t **op)
static sblock_t *
expr_symbol (sblock_t *sblock, expr_t *e, operand_t **op)
{
*op = new_operand (op_symbol);
(*op)->type = low_level_type (e->e.symbol->type);
(*op)->o.symbol = e->e.symbol;
symbol_t *sym = e->e.symbol;
if (sym->sy_type == sy_var) {
*op = def_operand (sym->s.def, sym->type);
} else if (sym->sy_type == sy_const) {
*op = value_operand (sym->s.value);
} else if (sym->sy_type == sy_func) {
*op = def_operand (sym->s.func->def, 0);
} else {
internal_error (e, "unexpected symbol type: %s",
symtype_str(sym->sy_type));
}
return sblock;
}
@ -828,9 +982,7 @@ expr_temp (sblock_t *sblock, expr_t *e, operand_t **op)
static sblock_t *
expr_value (sblock_t *sblock, expr_t *e, operand_t **op)
{
*op = new_operand (op_value);
(*op)->type = e->e.value.type;
(*op)->o.value = &e->e.value;
*op = value_operand (e->e.value);
return sblock;
}
@ -876,7 +1028,7 @@ statement_state (sblock_t *sblock, expr_t *e)
{
statement_t *s;
s = new_statement ("<STATE>", e);
s = new_statement (st_state, "<STATE>", e);
sblock = statement_subexpr (sblock, e->e.state.frame, &s->opa);
sblock = statement_subexpr (sblock, e->e.state.think, &s->opb);
sblock = statement_subexpr (sblock, e->e.state.step, &s->opc);
@ -989,6 +1141,7 @@ statement_bool (sblock_t *sblock, expr_t *e)
l = (*s)->e.expr.e1;
for (e = (*s)->next; e && e->type == ex_label; e = e->next) {
if (e == l) {
l->e.label.used--;
*s = (*s)->next;
l = 0;
break;
@ -1085,7 +1238,7 @@ statement_uexpr (sblock_t *sblock, expr_t *e)
e->e.expr.e1 = new_float_expr (0);
}
}
s = new_statement (opcode, e);
s = new_statement (st_func, opcode, e);
if (e->e.expr.e1)
sblock = statement_subexpr (sblock, e->e.expr.e1, &s->opa);
sblock_add_statement (sblock, s);
@ -1166,17 +1319,6 @@ unuse_label (ex_label_t *label)
remove_label_from_dest (label);
}
static void
dump_flow (sblock_t *sblock, const char *stage)
{
char *fname;
fname = nva ("%s.%s.%s.dot", GETSTR (pr.source_file), current_func->name,
stage);
print_flow (sblock, fname);
free (fname);
}
static void
thread_jumps (sblock_t *blocks)
{
@ -1188,15 +1330,17 @@ thread_jumps (sblock_t *blocks)
statement_t *s;
ex_label_t **label, *l;
if (!sblock->statements)
continue;
s = (statement_t *) sblock->tail;
if (is_goto (s))
if (statement_is_goto (s))
label = &s->opa->o.label;
else if (is_conditional (s))
else if (statement_is_cond (s))
label = &s->opb->o.label;
else
continue;
for (l = *label;
l->dest->statements && is_goto (l->dest->statements);
l->dest->statements && statement_is_goto (l->dest->statements);
l = l->dest->statements->opa->o.label) {
}
if (l != *label) {
@ -1204,6 +1348,15 @@ thread_jumps (sblock_t *blocks)
l->used++;
*label = l;
}
if (statement_is_goto (s) && (*label)->dest == sblock->next) {
statement_t **p;
unuse_label (*label);
for (p = &sblock->statements; *p != s; p = &(*p)->next)
;
free_statement (s);
*p = 0;
sblock->tail = p;
}
}
}
@ -1234,7 +1387,7 @@ join_blocks (sblock_t *sblock, sblock_t *dest)
label->e.label.next = dest->next->labels;
label->e.label.used++;
dest->next->labels = &label->e.label;
g = new_statement ("<GOTO>", 0);
g = new_statement (st_flow, "<GOTO>", 0);
g->opa = new_operand (op_label);
g->opa->o.label = &label->e.label;
sblock_add_statement (dest, g);
@ -1256,8 +1409,10 @@ merge_blocks (sblock_t *blocks)
sblock_t *dest;
sblock_t *sb;
if (!sblock->statements)
continue;
s = (statement_t *) sblock->tail;
if (!is_goto (s))
if (!statement_is_goto (s))
continue;
dest = s->opa->o.label->dest;
// The destination block must not be the current block
@ -1279,10 +1434,12 @@ merge_blocks (sblock_t *blocks)
break;
if (!sb) // dest is
internal_error (0, "dangling label");
s = (statement_t *) sb->tail;
if (!is_goto (s) && !is_return (s))
if (!sb->statements)
continue;
// desination block is reachable via only goto of the current block
s = (statement_t *) sb->tail;
if (!statement_is_goto (s) && !statement_is_return (s))
continue;
// desination block is reachable only via goto of the current block
if (!dest->next)
dest->next = new_sblock ();
sb->next = dest->next; // pull dest out of the chain
@ -1314,8 +1471,13 @@ remove_dead_blocks (sblock_t *blocks)
sb->reachable = 1;
continue;
}
if (!sblock->statements) {
sb->reachable = 1;
continue;
}
s = (statement_t *) sblock->tail;
if (is_conditional (s) && is_goto (sb->statements)
if (statement_is_cond (s)
&& sb->statements && statement_is_goto (sb->statements)
&& s->opb->o.label->dest == sb->next) {
debug (0, "merging if/goto %p %p", sblock, sb);
unuse_label (s->opb->o.label);
@ -1326,7 +1488,7 @@ remove_dead_blocks (sblock_t *blocks)
for (sb = sb->next; sb; sb = sb->next)
sb->reachable = 1;
break;
} else if (!is_goto (s) && !is_return (s)) {
} else if (!statement_is_goto (s) && !statement_is_return (s)) {
sb->reachable = 1;
continue;
}
@ -1340,11 +1502,13 @@ remove_dead_blocks (sblock_t *blocks)
debug (0, "removing dead block %p", sb);
s = (statement_t *) sb->tail;
if (is_goto (s))
label = s->opa->o.label;
else if (is_conditional (s))
label = s->opb->o.label;
if (sb->statements) {
s = (statement_t *) sb->tail;
if (statement_is_goto (s))
label = s->opa->o.label;
else if (statement_is_cond (s))
label = s->opb->o.label;
}
unuse_label (label);
did_something = 1;
@ -1360,6 +1524,7 @@ check_final_block (sblock_t *sblock)
{
statement_t *s;
symbol_t *return_symbol = 0;
def_t *return_def = 0;
operand_t *return_operand = 0;
const char *return_opcode = "<RETURN_V>";
@ -1367,46 +1532,81 @@ check_final_block (sblock_t *sblock)
return;
while (sblock->next)
sblock = sblock->next;
s = (statement_t *) sblock->tail;
if (is_goto (s))
return; // the end of function is the end of a loop
if (is_return (s))
return;
if (sblock->statements) {
s = (statement_t *) sblock->tail;
if (statement_is_goto (s))
return; // the end of function is the end of a loop
if (statement_is_return (s))
return;
}
if (current_func->sym->type->t.func.type != &type_void)
warning (0, "control reaches end of non-void function");
if (options.traditional || options.code.progsversion == PROG_ID_VERSION) {
expr_t *e = new_ret_expr (current_func->sym->type->t.func.type);
return_symbol = e->e.expr.e1->e.symbol;//FIXME ick
return_symbol = make_symbol (".return", &type_param, pr.symtab->space,
sc_extern);
return_def = return_symbol->s.def;
return_opcode = "<RETURN>";
}
if (return_symbol) {
return_operand = new_operand (op_symbol);
return_operand->type = ev_void;
return_operand->o.symbol = return_symbol;
}
s = new_statement (return_opcode, 0);
if (return_symbol)
return_operand = def_operand (return_def, &type_void);
s = new_statement (st_func, return_opcode, 0);
s->opa = return_operand;
sblock_add_statement (sblock, s);
}
void
dump_dot_sblock (void *data, const char *fname)
{
print_sblock ((sblock_t *) data, fname);
}
sblock_t *
make_statements (expr_t *e)
{
sblock_t *sblock = new_sblock ();
// print_expr (e);
if (options.block_dot.expr)
dump_dot ("expr", e, dump_dot_expr);
statement_slist (sblock, e);
if (options.block_dot.initial)
dump_flow (sblock, "initial");
dump_dot ("initial", sblock, dump_dot_sblock);
thread_jumps (sblock);
if (options.block_dot.thread)
dump_flow (sblock, "thread");
dump_dot ("thread", sblock, dump_dot_sblock);
do {
remove_dead_blocks (sblock);
} while (merge_blocks (sblock));
if (options.block_dot.dead)
dump_flow (sblock, "dead");
dump_dot ("dead", sblock, dump_dot_sblock);
check_final_block (sblock);
if (options.block_dot.final)
dump_flow (sblock, "final");
dump_dot ("final", sblock, dump_dot_sblock);
return sblock;
}
static void
count_temp (operand_t *op)
{
if (!op)
return;
if (op->op_type == op_temp) {
while (op->o.tempop.alias)
op = op->o.tempop.alias;
op->o.tempop.users++;
}
}
void
statements_count_temps (sblock_t *sblock)
{
statement_t *st;
while (sblock) {
for (st = sblock->statements; st; st = st->next) {
count_temp (st->opa);
count_temp (st->opb);
count_temp (st->opc);
}
sblock = sblock->next;
}
}

View file

@ -55,10 +55,12 @@
#include "obj_type.h"
#include "qfcc.h"
#include "reloc.h"
#include "shared.h"
#include "strpool.h"
#include "struct.h"
#include "symtab.h"
#include "type.h"
#include "value.h"
static symbol_t *
find_tag (ty_meta_e meta, symbol_t *tag, type_t *type)
@ -172,7 +174,7 @@ add_enum (symbol_t *enm, symbol_t *name, expr_t *val)
enum_tab = enum_type->t.symtab;
value = 0;
if (enum_tab->symbols)
value = ((symbol_t *)(enum_tab->symtail))->s.value.v.integer_val + 1;
value = ((symbol_t *)(enum_tab->symtail))->s.value->v.integer_val + 1;
if (val) {
if (!is_constant (val))
error (val, "non-constant initializer");
@ -181,8 +183,7 @@ add_enum (symbol_t *enm, symbol_t *name, expr_t *val)
else
value = expr_integer (val);
}
name->s.value.type = ev_integer;
name->s.value.v.integer_val = value;
name->s.value = new_integer_val (value);
symtab_addsymbol (enum_tab, name);
sym = new_symbol_type (name->name, name->type);
sym->sy_type = sy_const;
@ -204,12 +205,12 @@ enum_as_bool (type_t *enm, expr_t **zero, expr_t **one)
for (sym = symtab->symbols; sym; sym = sym->next) {
if (sym->sy_type != sy_const)
continue;
val = sym->s.value.v.integer_val;
val = sym->s.value->v.integer_val;
if (!val) {
zero_sym = sym;
} else {
if (one_sym) {
v = one_sym->s.value.v.integer_val;
v = one_sym->s.value->v.integer_val;
if (val * val > v * v)
continue;
}
@ -281,7 +282,7 @@ emit_structure (const char *name, int su, struct_def_t *defs, type_t *type,
}
if (defs[i].name)
internal_error (0, "structure %s too many defs", name);
if (storage != st_global && storage != st_static)
if (storage != sc_global && storage != sc_static)
internal_error (0, "structure %s must be global or static", name);
struct_sym = make_symbol (name, type, pr.far_data, storage);

View file

@ -24,12 +24,10 @@ int num_linenos;
pr_lineno_t *linenos;
pr_info_t pr;
string_t ReuseString (const char *str) {return 0;}
void encode_type (struct dstring_s *str, type_t *type) {}
void encode_type (struct dstring_s *str, const type_t *type) {}
codespace_t *codespace_new (void) {return 0;}
void codespace_addcode (codespace_t *codespace, struct dstatement_s *code, int size) {}
int function_parms (function_t *f, byte *parm_size) {return 0;}
pr_auxfunction_t *new_auxfunction (void) {return 0;}
ddef_t *new_local (void) {return 0;}
void def_to_ddef (def_t *def, ddef_t *ddef, int aux) {}
expr_t *warning (expr_t *e, const char *fmt, ...) {return 0;}
expr_t *error (expr_t *e, const char *fmt, ...) {return 0;}

View file

@ -67,8 +67,8 @@ static ex_value_t *
get_value (expr_t *e)
{
if (e->type == ex_symbol)
return &e->e.symbol->s.value;
return &e->e.value;
return e->e.symbol->s.value;
return e->e.value;
}
static uintptr_t
@ -343,9 +343,8 @@ build_switch (expr_t *sw, case_node_t *tree, int op, expr_t *sw_val,
}
table_sym = new_symbol (table_name);
initialize_def (table_sym, array_type (&type_integer, high - low + 1),
table_init, pr.near_data, st_static);
table_init, pr.near_data, sc_static);
table_expr = new_symbol_expr (table_sym);
table_expr = new_alias_expr (&type_integer, table_expr);
if (tree->left) {
branch = branch_expr (IFB, temp, low_label);
@ -357,6 +356,7 @@ build_switch (expr_t *sw, case_node_t *tree, int op, expr_t *sw_val,
append_expr (sw, branch);
branch = new_binary_expr ('g', table_expr, temp);
append_expr (sw, branch);
debug (sw, "switch using jump table");
if (tree->left) {
append_expr (sw, low_label);
build_switch (sw, tree->left, op, sw_val, temp, default_label);
@ -394,7 +394,6 @@ switch_expr (switch_block_t *switch_block, expr_t *break_label,
default_label = &_default_label;
default_label->label = break_label;
}
default_expr = goto_expr (default_label->label);
append_expr (sw, assign_expr (sw_val, switch_block->test));
@ -411,6 +410,7 @@ switch_expr (switch_block_t *switch_block, expr_t *break_label,
append_expr (sw, test);
}
default_expr = goto_expr (default_label->label);
append_expr (sw, default_expr);
} else {
expr_t *temp;

View file

@ -34,6 +34,7 @@
#include <stdlib.h>
#include <string.h>
#include "QF/alloc.h"
#include "QF/hash.h"
#include "class.h"
@ -50,6 +51,23 @@
static symtab_t *free_symtabs;
static symbol_t *free_symbols;
static const char *sy_type_names[] = {
"sy_var",
"sy_const",
"sy_type",
"sy_expr",
"sy_func",
"sy_class",
};
const char *
symtype_str (sy_type_e type)
{
if (type < 0 || type > sy_class)
return "<invalid sy_type>";
return sy_type_names[type];
}
symbol_t *
new_symbol (const char *name)
{
@ -189,9 +207,9 @@ make_symbol (const char *name, type_t *type, defspace_t *space,
symbol_t *sym;
struct reloc_s *relocs = 0;
if (storage != st_extern && storage != st_global && storage != st_static)
if (storage != sc_extern && storage != sc_global && storage != sc_static)
internal_error (0, "invalid storage class for %s", __FUNCTION__);
if (storage != st_extern && !space)
if (storage != sc_extern && !space)
internal_error (0, "null space for non-external storage");
sym = symtab_lookup (pr.symtab, name);
if (!sym) {
@ -206,7 +224,7 @@ make_symbol (const char *name, type_t *type, defspace_t *space,
sym = new_symbol_type (name, type);
}
}
if (sym->s.def && sym->s.def->external && storage != st_extern) {
if (sym->s.def && sym->s.def->external && storage != sc_extern) {
//FIXME this really is not the right way
relocs = sym->s.def->relocs;
free_def (sym->s.def);

View file

@ -41,6 +41,7 @@
#include <ctype.h>
#include <ctype.h>
#include "QF/alloc.h"
#include "QF/dstring.h"
#include "QF/hash.h"
#include "QF/sys.h"
@ -80,9 +81,9 @@ type_t *type_nil;
type_t *type_default;
// these will be built up further
type_t type_va_list = { ev_invalid, "@va_list", ty_struct };
type_t type_param = { ev_invalid, "@param", ty_struct };
type_t type_zero = { ev_invalid, "@zero", ty_struct };
type_t type_va_list = { ev_invalid, 0, ty_struct };
type_t type_param = { ev_invalid, 0, ty_struct };
type_t type_zero = { ev_invalid, 0, ty_struct };
type_t type_type_encodings = { ev_invalid, "@type_encodings", ty_struct };
type_t type_floatfield = { ev_field, ".float", ty_none, {{&type_float}} };
@ -113,7 +114,7 @@ low_level_type (type_t *type)
}
const char *
type_get_encoding (type_t *type)
type_get_encoding (const type_t *type)
{
static dstring_t *encoding;
@ -182,8 +183,7 @@ free_type (type_t *type)
break;
}
memset (type, 0, sizeof (type));
type->next = free_types;
free_types = type;
FREE (types, type);
}
type_t *
@ -411,7 +411,7 @@ based_array_type (type_t *aux, int base, int top)
}
void
print_type_str (dstring_t *str, type_t *type)
print_type_str (dstring_t *str, const type_t *type)
{
if (!type) {
dasprintf (str, " (null)");
@ -489,7 +489,7 @@ print_type_str (dstring_t *str, type_t *type)
}
void
print_type (type_t *type)
print_type (const type_t *type)
{
dstring_t *str = dstring_newstr ();
print_type_str (str, type);
@ -498,7 +498,7 @@ print_type (type_t *type)
}
const char *
encode_params (type_t *type)
encode_params (const type_t *type)
{
const char *ret;
dstring_t *encoding = dstring_newstr ();
@ -519,7 +519,7 @@ encode_params (type_t *type)
}
static void
encode_class (dstring_t *encoding, type_t *type)
encode_class (dstring_t *encoding, const type_t *type)
{
class_t *class = type->t.class;
const char *name ="?";
@ -530,7 +530,7 @@ encode_class (dstring_t *encoding, type_t *type)
}
static void
encode_struct (dstring_t *encoding, type_t *type)
encode_struct (dstring_t *encoding, const type_t *type)
{
const char *name ="?";
char su = ' ';
@ -545,7 +545,7 @@ encode_struct (dstring_t *encoding, type_t *type)
}
static void
encode_enum (dstring_t *encoding, type_t *type)
encode_enum (dstring_t *encoding, const type_t *type)
{
const char *name ="?";
@ -555,7 +555,7 @@ encode_enum (dstring_t *encoding, type_t *type)
}
void
encode_type (dstring_t *encoding, type_t *type)
encode_type (dstring_t *encoding, const type_t *type)
{
if (!type)
return;
@ -646,7 +646,7 @@ encode_type (dstring_t *encoding, type_t *type)
}
int
is_enum (type_t *type)
is_enum (const type_t *type)
{
if (type->type == ev_invalid && type->meta == ty_enum)
return 1;
@ -654,7 +654,7 @@ is_enum (type_t *type)
}
int
is_integral (type_t *type)
is_integral (const type_t *type)
{
etype_t t = type->type;
@ -664,19 +664,19 @@ is_integral (type_t *type)
}
int
is_float (type_t *type)
is_float (const type_t *type)
{
return type->type == ev_float;
}
int
is_scalar (type_t *type)
is_scalar (const type_t *type)
{
return is_float (type) || is_integral (type);
}
int
is_math (type_t *type)
is_math (const type_t *type)
{
etype_t t = type->type;
@ -684,7 +684,7 @@ is_math (type_t *type)
}
int
is_struct (type_t *type)
is_struct (const type_t *type)
{
if (type->type == ev_invalid
&& (type->meta == ty_struct || type->meta == ty_union))
@ -693,7 +693,7 @@ is_struct (type_t *type)
}
int
is_class (type_t *type)
is_class (const type_t *type)
{
if (type->type == ev_invalid && type->meta == ty_class)
return 1;
@ -701,7 +701,7 @@ is_class (type_t *type)
}
int
is_array (type_t *type)
is_array (const type_t *type)
{
if (type->type == ev_invalid && type->meta == ty_array)
return 1;
@ -709,7 +709,7 @@ is_array (type_t *type)
}
int
type_assignable (type_t *dst, type_t *src)
type_assignable (const type_t *dst, const type_t *src)
{
class_t *dst_class, *src_class;
@ -760,7 +760,7 @@ type_assignable (type_t *dst, type_t *src)
}
int
type_size (type_t *type)
type_size (const type_t *type)
{
if (!type)
return 0;
@ -822,6 +822,8 @@ init_types (void)
{"func_val", &type_function},
{"pointer_val", &type_pointer},
{"vector_val", &type_vector},
{"int_val", &type_integer},
{"uint_val", &type_uinteger},
{"integer_val", &type_integer},
{"uinteger_val", &type_uinteger},
{"quaternion_val", &type_quaternion},
@ -835,6 +837,8 @@ init_types (void)
{"field_val", &type_field},
{"func_val", &type_function},
{"pointer_val", &type_pointer},
{"int_val", &type_integer},
{"uint_val", &type_uinteger},
{"integer_val", &type_integer},
{"uinteger_val", &type_uinteger},
{"quaternion_val", &type_quaternion},

View file

@ -39,6 +39,7 @@
#endif
#include <stdlib.h>
#include "QF/alloc.h"
#include "QF/dstring.h"
#include "QF/hash.h"
#include "QF/mathlib.h"
@ -71,6 +72,177 @@ typedef struct {
} i;
} immediate_t;
static hashtab_t *value_table;
static ex_value_t *free_values;
static uintptr_t
value_get_hash (const void *_val, void *unused)
{
const ex_value_t *val = (const ex_value_t *) _val;
return Hash_Buffer (&val->v, sizeof (val->v)) + val->type;
}
static int
value_compare (const void *_val1, const void *_val2, void *unused)
{
const ex_value_t *val1 = (const ex_value_t *) _val1;
const ex_value_t *val2 = (const ex_value_t *) _val2;
if (val1->type != val2->type)
return 0;
return memcmp (&val1->v, &val2->v, sizeof (val1->v)) == 0;
}
static ex_value_t *
new_value (void)
{
ex_value_t *value;
ALLOC (256, ex_value_t, values, value);
return value;
}
static ex_value_t *
find_value (const ex_value_t *val)
{
ex_value_t *value;
value = Hash_FindElement (value_table, val);
if (value)
return value;
value = new_value ();
*value = *val;
Hash_AddElement (value_table, value);
return value;
}
ex_value_t *
new_string_val (const char *string_val)
{
ex_value_t val;
memset (&val, 0, sizeof (val));
val.type = ev_string;
if (string_val)
val.v.string_val = save_string (string_val);
return find_value (&val);
}
ex_value_t *
new_float_val (float float_val)
{
ex_value_t val;
memset (&val, 0, sizeof (val));
val.type = ev_float;
val.v.float_val = float_val;
return find_value (&val);
}
ex_value_t *
new_vector_val (const float *vector_val)
{
ex_value_t val;
memset (&val, 0, sizeof (val));
val.type = ev_vector;
VectorCopy (vector_val, val.v.vector_val);
return find_value (&val);
}
ex_value_t *
new_entity_val (int entity_val)
{
ex_value_t val;
memset (&val, 0, sizeof (val));
val.type = ev_entity;
val.v.entity_val = entity_val;
return find_value (&val);
}
ex_value_t *
new_field_val (int field_val, type_t *type, def_t *def)
{
ex_value_t val;
memset (&val, 0, sizeof (val));
val.type = ev_field;
val.v.pointer.val = field_val;
val.v.pointer.type = type;
val.v.pointer.def = def;
return find_value (&val);
}
ex_value_t *
new_func_val (int func_val, type_t *type)
{
ex_value_t val;
memset (&val, 0, sizeof (val));
val.type = ev_func;
val.v.func_val.val = func_val;
val.v.func_val.type = type;
return find_value (&val);
}
ex_value_t *
new_pointer_val (int pointer_val, type_t *type, def_t *def)
{
ex_value_t val;
memset (&val, 0, sizeof (val));
val.type = ev_pointer;
val.v.pointer.val = pointer_val;
val.v.pointer.type = type;
val.v.pointer.def = def;
return find_value (&val);
}
ex_value_t *
new_quaternion_val (const float *quaternion_val)
{
ex_value_t val;
memset (&val, 0, sizeof (val));
val.type = ev_quat;
QuatCopy (quaternion_val, val.v.quaternion_val);
return find_value (&val);
}
ex_value_t *
new_integer_val (int integer_val)
{
ex_value_t val;
memset (&val, 0, sizeof (val));
val.type = ev_integer;
val.v.integer_val = integer_val;
return find_value (&val);
}
ex_value_t *
new_uinteger_val (int uinteger_val)
{
ex_value_t val;
memset (&val, 0, sizeof (val));
val.type = ev_uinteger;
val.v.uinteger_val = uinteger_val;
return find_value (&val);
}
ex_value_t *
new_short_val (short short_val)
{
ex_value_t val;
memset (&val, 0, sizeof (val));
val.type = ev_short;
val.v.short_val = short_val;
return find_value (&val);
}
ex_value_t *
new_nil_val (type_t *type)
{
ex_value_t val;
memset (&val, 0, sizeof (val));
val.type = low_level_type (type);
if (val.type == ev_pointer|| val.type == ev_field )
val.v.pointer.type = type->t.fldptr.type;
if (val.type == ev_func)
val.v.func_val.type = type;
return find_value (&val);
}
static hashtab_t *string_imm_defs;
static hashtab_t *float_imm_defs;
static hashtab_t *vector_imm_defs;
@ -81,6 +253,12 @@ static hashtab_t *pointer_imm_defs;
static hashtab_t *quaternion_imm_defs;
static hashtab_t *integer_imm_defs;
static void
imm_free (void *_imm, void *unused)
{
free (_imm);
}
static uintptr_t
imm_get_hash (const void *_imm, void *_tab)
{
@ -196,33 +374,42 @@ value_as_uint (ex_value_t *value)
return 0;
}
void
ex_value_t *
convert_value (ex_value_t *value, type_t *type)
{
if (!is_scalar (type) || !is_scalar (ev_types[value->type])) {
error (0, "unable to convert non-scalar value");
return;
return value;
}
if (is_float (type)) {
float val = value_as_float (value);
value->type = ev_float;
value->v.float_val = val;
return new_float_val (val);
} else if (type->type == ev_short) {
int val = value_as_int (value);
value->type = ev_short;
value->v.short_val = val;
return new_short_val (val);
} else if (type->type == ev_uinteger) {
unsigned val = value_as_uint (value);
value->type = ev_uinteger;
value->v.uinteger_val = val;
return new_uinteger_val (val);
} else {
//FIXME handle enums separately?
int val = value_as_int (value);
value->type = ev_integer;
value->v.integer_val = val;
return new_integer_val (val);
}
}
static immediate_t *
make_def_imm (def_t *def, hashtab_t *tab)
{
immediate_t *imm;
imm = calloc (1, sizeof (immediate_t));
imm->def = def;
Hash_AddElement (tab, imm);
return imm;
}
def_t *
emit_value (ex_value_t *value, def_t *def)
{
@ -291,7 +478,7 @@ emit_value (ex_value_t *value, def_t *def)
imm = 0; //FIXME do full def aliasing
} else {
symbol_t *sym;
sym = make_symbol (".zero", &type_zero, 0, st_extern);
sym = make_symbol (".zero", &type_zero, 0, sc_extern);
return sym->s.def;
}
}
@ -306,7 +493,7 @@ emit_value (ex_value_t *value, def_t *def)
cn = def;
} else {
if (cn->type != type) {
def = new_def (".imm", type, pr.near_data, st_static);
def = new_def (".imm", type, pr.near_data, sc_static);
def->offset = cn->offset;
cn = def;
}
@ -317,13 +504,13 @@ emit_value (ex_value_t *value, def_t *def)
// always share immediates
if (def) {
if (def->type != type) {
cn = new_def (".imm", type, pr.near_data, st_static);
cn = new_def (".imm", type, pr.near_data, sc_static);
cn->offset = def->offset;
} else {
cn = def;
}
} else {
cn = new_def (".imm", type, pr.near_data, st_static);
cn = new_def (".imm", type, pr.near_data, sc_static);
}
cn->initialized = cn->constant = 1;
cn->nosave = 1;
@ -333,7 +520,7 @@ emit_value (ex_value_t *value, def_t *def)
reloc_def_string (cn);
break;
case ev_func:
if (val.v.func_val) {
if (val.v.func_val.val) {
reloc_t *reloc;
reloc = new_reloc (cn->space, cn->offset, rel_def_func);
reloc->next = pr.relocs;
@ -356,21 +543,19 @@ emit_value (ex_value_t *value, def_t *def)
memcpy (D_POINTER (void, cn), &val.v, 4 * type_size (type));
imm = malloc (sizeof (immediate_t));
imm->def = cn;
imm = make_def_imm (cn, tab);
memcpy (&imm->i, &val.v, sizeof (imm->i));
Hash_AddElement (tab, imm);
return cn;
}
void
clear_immediates (void)
{
immediate_t *imm;
def_t *def;
if (string_imm_defs) {
if (value_table) {
Hash_FlushTable (value_table);
Hash_FlushTable (string_imm_defs);
Hash_FlushTable (float_imm_defs);
Hash_FlushTable (vector_imm_defs);
@ -381,43 +566,45 @@ clear_immediates (void)
Hash_FlushTable (quaternion_imm_defs);
Hash_FlushTable (integer_imm_defs);
} else {
string_imm_defs = Hash_NewTable (16381, 0, 0, &string_imm_defs);
value_table = Hash_NewTable (16381, 0, 0, 0);
Hash_SetHashCompare (value_table, value_get_hash, value_compare);
string_imm_defs = Hash_NewTable (16381, 0, imm_free, &string_imm_defs);
Hash_SetHashCompare (string_imm_defs, imm_get_hash, imm_compare);
float_imm_defs = Hash_NewTable (16381, 0, 0, &float_imm_defs);
float_imm_defs = Hash_NewTable (16381, 0, imm_free, &float_imm_defs);
Hash_SetHashCompare (float_imm_defs, imm_get_hash, imm_compare);
vector_imm_defs = Hash_NewTable (16381, 0, 0, &vector_imm_defs);
vector_imm_defs = Hash_NewTable (16381, 0, imm_free, &vector_imm_defs);
Hash_SetHashCompare (vector_imm_defs, imm_get_hash, imm_compare);
entity_imm_defs = Hash_NewTable (16381, 0, 0, &entity_imm_defs);
entity_imm_defs = Hash_NewTable (16381, 0, imm_free, &entity_imm_defs);
Hash_SetHashCompare (entity_imm_defs, imm_get_hash, imm_compare);
field_imm_defs = Hash_NewTable (16381, 0, 0, &field_imm_defs);
field_imm_defs = Hash_NewTable (16381, 0, imm_free, &field_imm_defs);
Hash_SetHashCompare (field_imm_defs, imm_get_hash, imm_compare);
func_imm_defs = Hash_NewTable (16381, 0, 0, &func_imm_defs);
func_imm_defs = Hash_NewTable (16381, 0, imm_free, &func_imm_defs);
Hash_SetHashCompare (func_imm_defs, imm_get_hash, imm_compare);
pointer_imm_defs = Hash_NewTable (16381, 0, 0, &pointer_imm_defs);
pointer_imm_defs =
Hash_NewTable (16381, 0, imm_free, &pointer_imm_defs);
Hash_SetHashCompare (pointer_imm_defs, imm_get_hash, imm_compare);
quaternion_imm_defs =
Hash_NewTable (16381, 0, 0, &quaternion_imm_defs);
Hash_NewTable (16381, 0, imm_free, &quaternion_imm_defs);
Hash_SetHashCompare (quaternion_imm_defs, imm_get_hash, imm_compare);
integer_imm_defs = Hash_NewTable (16381, 0, 0, &integer_imm_defs);
integer_imm_defs =
Hash_NewTable (16381, 0, imm_free, &integer_imm_defs);
Hash_SetHashCompare (integer_imm_defs, imm_get_hash, imm_compare);
}
imm = calloc (1, sizeof (immediate_t));
imm->def = make_symbol (".zero", &type_zero, 0, st_extern)->s.def;
imm->def->initialized = imm->def->constant = 1;
imm->def->nosave = 1;
def = make_symbol (".zero", &type_zero, 0, sc_extern)->s.def;
Hash_AddElement (string_imm_defs, imm);
Hash_AddElement (float_imm_defs, imm);
Hash_AddElement (entity_imm_defs, imm);
Hash_AddElement (pointer_imm_defs, imm);
Hash_AddElement (integer_imm_defs, imm);
make_def_imm (def, string_imm_defs);
make_def_imm (def, float_imm_defs);
make_def_imm (def, entity_imm_defs);
make_def_imm (def, pointer_imm_defs);
make_def_imm (def, integer_imm_defs);
}

118
tools/qfcc/test/Makefile.am Normal file
View file

@ -0,0 +1,118 @@
AUTOMAKE_OPTIONS= foreign
INCLUDES= -I$(top_srcdir)/include $(QFCC_INCS)
QFCC_DEP=$(builddir)/../source/qfcc$(EXEEXT)
QFCC=$(QFCC_DEP)
QCFLAGS=-qq -O -g --no-default-paths -Werror
QCPPFLAGS=
QCOMPILE=$(QFCC) $(QCFLAGS) $(QCPPFLAGS)
SUFFIXES=.qfo .r
.r.qfo:
$(QCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tqo -c -o $@ $<
sed -i -e '1s@:@: $(QFCC_DEP)@' $(DEPDIR)/$*.Tqo
$(am__mv) $(DEPDIR)/$*.Tqo $(DEPDIR)/$*.Qo
QFCC_TEST_LIBS=@QFCC_TEST_LIBS@
QFCC_TEST_DEPS=@QFCC_TEST_DEPS@
QFCC_TEST_INCS=@QFCC_TEST_INCS@
test_progs_dat=\
chewed-alias.dat \
chewed-return.dat \
func-static.dat \
deadbool.dat \
infloop.dat \
modulo.dat \
structlive.dat \
structptr.dat \
vecinit.dat \
while.dat
fail_progs_dat=\
$E
TESTS=$(test_progs_dat:.dat=.run)
XFAIL_TESTS=$(fail_progs_dat:.dat=.run)
check_PROGRAMS=test-harness $(test_progs_dat)
test_harness_SOURCES= test-bi.c test-harness.c
test_harness_LDADD= $(QFCC_TEST_LIBS)
test_harness_DEPENDENCIES= $(QFCC_TEST_DEPS)
chewed_alias_dat_SOURCES=chewed-alias.r
chewed_alias_obj=$(chewed_alias_dat_SOURCES:.r=.qfo)
chewed-alias.dat: $(chewed_alias_obj) $(QFCC_DEP)
$(QFCC) $(QCFLAGS) -o $@ $(chewed_alias_obj)
chewed-alias.run: Makefile build-run
$(srcdir)/build-run $@
chewed_return_dat_SOURCES=chewed-return.r
chewed_return_obj=$(chewed_return_dat_SOURCES:.r=.qfo)
chewed-return.dat: $(chewed_return_obj) $(QFCC_DEP)
$(QFCC) $(QCFLAGS) -o $@ $(chewed_return_obj)
chewed-return.run: Makefile build-run
TEST_HARNESS_OPTS=--float $(srcdir)/build-run $@
func_static_dat_SOURCES=func-static.r
func_static_obj=$(func_static_dat_SOURCES:.r=.qfo)
func-static.dat: $(func_static_obj) $(QFCC_DEP)
$(QFCC) $(QCFLAGS) -o $@ $(func_static_obj)
func-static.run: Makefile build-run
$(srcdir)/build-run $@
deadbool_dat_SOURCES=deadbool.r
deadbool_obj=$(deadbool_dat_SOURCES:.r=.qfo)
deadbool.dat: $(deadbool_obj) $(QFCC_DEP)
$(QFCC) $(QCFLAGS) -o $@ $(deadbool_obj)
deadbool.run: Makefile build-run
$(srcdir)/build-run $@
infloop_dat_SOURCES=infloop.r
infloop_obj=$(infloop_dat_SOURCES:.r=.qfo)
infloop.dat: $(infloop_obj) $(QFCC_DEP)
$(QFCC) $(QCFLAGS) -o $@ $(infloop_obj)
infloop.run: Makefile build-run
$(srcdir)/build-run $@
modulo_dat_SOURCES=modulo.r
modulo_obj=$(modulo_dat_SOURCES:.r=.qfo)
modulo.dat: $(modulo_obj) $(QFCC_DEP)
$(QFCC) $(QCFLAGS) -o $@ $(modulo_obj)
modulo.run: Makefile build-run
TEST_HARNESS_OPTS=--float $(srcdir)/build-run $@
structlive_dat_SOURCES=structlive.r
structlive_obj=$(structlive_dat_SOURCES:.r=.qfo)
structlive.dat: $(structlive_obj) $(QFCC_DEP)
$(QFCC) $(QCFLAGS) -o $@ $(structlive_obj)
structlive.run: Makefile build-run
$(srcdir)/build-run $@
structptr_dat_SOURCES=structptr.r
structptr_obj=$(structptr_dat_SOURCES:.r=.qfo)
structptr.dat: $(structptr_obj) $(QFCC_DEP)
$(QFCC) $(QCFLAGS) -o $@ $(structptr_obj)
structptr.run: Makefile build-run
$(srcdir)/build-run $@
vecinit_dat_SOURCES=vecinit.r
vecinit_obj=$(vecinit_dat_SOURCES:.r=.qfo)
vecinit.dat: $(vecinit_obj) $(QFCC_DEP)
$(QFCC) $(QCFLAGS) -o $@ $(vecinit_obj)
vecinit.run: Makefile build-run
$(srcdir)/build-run $@
while_dat_SOURCES=while.r
while_obj=$(while_dat_SOURCES:.r=.qfo)
while.dat: $(while_obj) $(QFCC_DEP)
$(QFCC) $(QCFLAGS) -o $@ $(while_obj)
while.run: Makefile build-run
$(srcdir)/build-run $@
include ./$(DEPDIR)/*.Qo
EXTRA_DIST= test-bi.h build-run
CLEANFILES= *.dat *.sym *.qfo *.run

11
tools/qfcc/test/build-run Executable file
View file

@ -0,0 +1,11 @@
#! /bin/sh
script=$1
progs=`basename $script .run`.dat
shift
cat > $script <<EOF
#! /bin/sh
./test-harness $TEST_HARNESS_OPTS $progs "\$@"
EOF
chmod +x $script

View file

@ -0,0 +1,21 @@
@class Array,Object;
@static entity waypoint_thinker;
@static Array *waypoint_queue;
void foo (void)
{
if ([waypoint_queue count]
&& (waypoint_thinker.@this = [waypoint_queue lastObject])) {
waypoint_thinker = nil; // just to deconfuse the dot graphs
}
}
int main ()
{
return 0; // test succeeds if compile succeeds
}
id obj_msgSend (id receiver, SEL op, ...) = #0;
void __obj_exec_class (struct obj_module *msg) = #0;
@implementation Object
@end
@implementation Array
@end

View file

@ -0,0 +1,19 @@
#pragma traditional
entity spawn (void) {return @nil;}
float time;
.void () think;
.float nextthink;
void rthink(){}
void foo (void)
{
entity rspawn;
rspawn = spawn();
rspawn.nextthink = time + 0.1;
rspawn.think = rthink;
};
float main ()
{
return 0; // compile test
}

View file

@ -0,0 +1,19 @@
entity spawn (void) { return nil; }
.float f;
float time;
void foo (void)
{
local entity ent;
return;
if (!time) {
ent = spawn ();
ent.f = time + 0.1;
}
}
int main ()
{
return 0; // if the test compiles, it passes.
}

View file

@ -0,0 +1,17 @@
int count;
void foo (void)
{
@static int x;
if (!x)
count++;
x = 1;
}
int main(void)
{
foo ();
foo ();
return count != 1;
}

23
tools/qfcc/test/infloop.r Normal file
View file

@ -0,0 +1,23 @@
void exit (int code) = #0;
int foo;
int calc_foo (void)
{
return 1;
}
void check_foo (void)
{
if (foo)
exit (0);
exit (1);
}
int main ()
{
while (1) {
foo = calc_foo ();
check_foo ();
}
}

View file

@ -1,4 +1,5 @@
#if 1
#pragma traditional
void (...) printf = #0;
float foo (float a, float b)
{
@ -13,26 +14,48 @@ float bar (float a, float b)
c = a - ((a / b) & -1) * b;
return c;
}
#endif
float baz (float a, float b)
{
float c = (a + b) % (a - b);
return c;
}
#if 1
float main (void)
float test (string name, float (func)(float a, float b),
float a, float b, float c)
{
printf ("foo: 5 %% 3: %f\n", foo (5, 3));
printf ("bar: 5 %% 3: %f\n", bar (5, 3));
float ret;
printf ("foo: -5 %% 3: %f\n", foo (-5, 3));
printf ("bar: -5 %% 3: %f\n", bar (-5, 3));
printf ("foo: 5 %% -3: %f\n", foo (5, -3));
printf ("bar: 5 %% -3: %f\n", bar (5, -3));
printf ("foo: -5 %% -3: %f\n", foo (-5, -3));
printf ("bar: -5 %% -3: %f\n", bar (-5, -3));
ret = func (a, b);
if (ret != c) {
if (func == baz)
printf ("%s: (%g + %g) %% (%g - %g): %g != %g\n",
name, a, b, a, b, ret, c);
else
printf ("%s: %g %% %g: %g != %g\n",
name, a, b, ret, c);
return 1;
}
return 0;
}
#endif
float main (void)
{
float res = 0;
res |= test ("foo", foo, 5, 3, 2);
res |= test ("bar", bar, 5, 3, 2);
res |= test ("baz", baz, 5, 3, 0);
res |= test ("foo", foo, -5, 3, -2);
res |= test ("bar", bar, -5, 3, -2);
res |= test ("baz", baz, -5, 3, -2);
res |= test ("foo", foo, 5, -3, 2);
res |= test ("bar", bar, 5, -3, 2);
res |= test ("baz", baz, 5, -3, 2);
res |= test ("foo", foo, -5, -3, -2);
res |= test ("bar", bar, -5, -3, -2);
res |= test ("baz", baz, -5, -3, 0);
return res;
}

Some files were not shown because too many files have changed in this diff Show more