mirror of
https://git.code.sf.net/p/quake/quakeforge
synced 2024-11-10 07:11:41 +00:00
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:
commit
3e38c4aa48
185 changed files with 7306 additions and 1038 deletions
|
@ -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
|
||||
|
|
|
@ -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],
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
97
include/QF/alloc.h
Normal 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
|
|
@ -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
331
include/QF/set.h
Normal 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
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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]",
|
||||
},
|
||||
|
|
|
@ -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
588
libs/util/set.c
Normal 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;
|
||||
}
|
|
@ -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
233
libs/util/test/test-set.c
Normal 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;
|
||||
}
|
|
@ -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 {
|
||||
|
|
|
@ -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@
|
||||
|
|
|
@ -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];
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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@
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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--;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -26,7 +26,7 @@
|
|||
#
|
||||
AUTOMAKE_OPTIONS= foreign
|
||||
|
||||
SUBDIRS= include source doc
|
||||
SUBDIRS= include source doc test
|
||||
|
||||
dist-zip: distdir
|
||||
-chmod -R a+r $(distdir)
|
||||
|
|
|
@ -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?)
|
||||
|
|
|
@ -2,4 +2,5 @@ AUTOMAKE_OPTIONS= foreign
|
|||
|
||||
SUBDIRS= man
|
||||
|
||||
EXTRA_DIST= expressions.txt
|
||||
EXTRA_DIST= expressions.txt \
|
||||
dot/vector-alias.dot
|
||||
|
|
25
tools/qfcc/doc/dot/vector-alias.dot
Normal file
25
tools/qfcc/doc/dot/vector-alias.dot
Normal 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];
|
||||
}
|
|
@ -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
|
||||
|
|
|
@ -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
112
tools/qfcc/include/dags.h
Normal 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
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
37
tools/qfcc/include/dot.h
Normal 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
|
|
@ -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
124
tools/qfcc/include/flow.h
Normal 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
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 {
|
||||
|
|
42
tools/qfcc/include/pragma.h
Normal file
42
tools/qfcc/include/pragma.h
Normal 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
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
865
tools/qfcc/source/dags.c
Normal 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]]);
|
||||
}
|
|
@ -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++];
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
53
tools/qfcc/source/dot.c
Normal 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
194
tools/qfcc/source/dot_dag.c
Normal 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);
|
||||
}
|
|
@ -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\"><bool>(%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\"><block>(%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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
146
tools/qfcc/source/dot_sblock.c
Normal file
146
tools/qfcc/source/dot_sblock.c
Normal 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);
|
||||
}
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
1184
tools/qfcc/source/flow.c
Normal file
File diff suppressed because it is too large
Load diff
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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]);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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"))) {
|
||||
|
|
92
tools/qfcc/source/pragma.c
Normal file
92
tools/qfcc/source/pragma.c
Normal 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);
|
||||
}
|
|
@ -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},
|
||||
|
|
|
@ -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
|
||||
{
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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); }
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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]<%s>",
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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},
|
||||
|
|
|
@ -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
118
tools/qfcc/test/Makefile.am
Normal 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
11
tools/qfcc/test/build-run
Executable 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
|
21
tools/qfcc/test/chewed-alias.r
Normal file
21
tools/qfcc/test/chewed-alias.r
Normal 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
|
19
tools/qfcc/test/chewed-return.r
Normal file
19
tools/qfcc/test/chewed-return.r
Normal 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
|
||||
}
|
19
tools/qfcc/test/deadbool.r
Normal file
19
tools/qfcc/test/deadbool.r
Normal 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.
|
||||
}
|
17
tools/qfcc/test/func-static.r
Normal file
17
tools/qfcc/test/func-static.r
Normal 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
23
tools/qfcc/test/infloop.r
Normal 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 ();
|
||||
}
|
||||
}
|
|
@ -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
Loading…
Reference in a new issue