I forgot to add func->num_statements :P. Fixes the weirdness where only
some alias temps were being (bogusly) detected as uninitialized. Now they
all are.
When the naive uninitialized variable detection finds a node with possible
uses of uninitialized variables, the statements in the node are scanned one
at a time checking each usage and removing uninitialized definitions as
appropriate. vectest.r now compiles without warnings. As an added bonus,
accurate line number information is reported for uninitialized variables.
Unfortunately, there is still a problem with uninitialized temps in
switch.r, but that might just be poor handling of temp op aliases.
Only definitions for the def used in the current statement (whether an
alias or not) are suitable for killing. Doing otherwise defeats the purpose
of this work :P
Fixes the false negatives found in a modified quattest.r (commented out the
"tq.s = 0;" line).
Nicely, the use sets from live_variable analysis can be used too, though
there are some problems with the naive implementation. For:
vector foo (float x, float y, float z)
{
vector v;
v.x = x;
v.y = y;
v.z = z;
return v;
}
qfcc thinks v is uninitialized, but if "if (x) return nil;" (or any other
basic-block splitter) is put just before the return v; qfcc correctly
detects that v is initialized. The reason is that the inits are in the same
basic block as the return, and thus aren't affecting the reaching
definitions, which are stored per-block.
The naive implementation should be good for a fast-cull before doing a
per-statement check.
The exit dummy block is setup to provide dummy uses of global variables to
the live variable analysis doesn't miss global variables. Much cleaner than
the previous code :) There may be some issues with aliases, though.
The entry dummy block is setup to provide dummy definitions of local
variables so the reaching definitions analysis can be used to detect
uninitialized variables (not implemented yet). Fake statement numbers
(func->num_statements + X) are used to represent the definitions. Local
variables (ie, not temp ops) use their offsets (ie, the offset range they
cover) for X. Temp ops use their flowvar number + the size of the
function's defspace for X. flow_kill_aliases() should take care of temp op
aliasing, while the use of the actual offsets spanned by the variable's def
should take care of any wild aliasing so structures and unions should
become a non-issue.
The dummy nodes are for detectining uninitialized variables (entry dummy)
and making globals live at function exit (exit dummy). The reaching defs
and live vars code currently seg because neither node has had its sets
initialized.
Fixed aliases are those that will never change through the life of the
code. They are generated from structure accesses and thus what they alias
is always known.
Also move the ALLOC/FREE macros from qfcc.h to QF/alloc.h (needed to for
set.c).
Both modules are more generally useful than just for qfcc (eg, set
builtins for ruamoko).
With the need to handle aliasing in the optimizer, it has become apparent
that having the flow data attached to symbols is not nearly as useful as
having it attached to defs (which are views of the actual variables).
This also involves a bit of a cleanup of operand types: op_pointer and
op_alias are gone (this seems to greatly simplify the optimizer)
There is a bit of a problem with enums in switch statements, but this might
actually be a sign that something is not quite right in the switch code
(other than enums not being recognized as ints for jump table
optimization).
With the intoduction of the statement type enum came a prefix clash. As
"st" makes sense for "statement type", I decided that "storage class"
should be "sc". Although there haven't been any problems as of yet, I
decided it would be a good idea to clean up the clash now. It also helps
avoid confusion (I was a bit surprised after working with st_assign etc to
be reminded of st_extern etc).
It doesn't quite work yet, but...
It has proven necessary to know what type .return has at any point in the
function. The segfault in ctf is caused by the return statement added to
the end of the void function messing with the expr pointer stored in the
daglabel for .return. While this is actually by design (though the
statement really should have a valid expr pointer rather than), it actually
highlights a bigger problem: there's no stable knowledge of the current
type of .return. This is not a problem in expression statements as the
dagnodes for expression statements store the desired types of all operands.
However, when assigning from .return to attached variables in a leaf node,
the type of .return is not stored anywhere but the expression last
accessing .return.
Now information like dags or live variables are dumped separately, and the
live variable information replaces the flow node in the diagram (like dags
have recently).
They really should have been in statements.[ch] in the first place
(actually, they sort of were: is_goto etc, so some redundant code has been
removed, too).
Such nodes are unreachable code (ie, dead blocks), but the dead block
removal code failed to remove them (current known cause: miscounted label
userrs). As such blocks cause problems for data flow analysis, ignoring
them is not a good idea. Thus make them an internal error.
vectors, quaternions and structs are a little tricky. I need to think about
how to get them working, but I also want qfcc to get through as much code
as possible.
It really should be impossible, but I'm not sure where the bug is yet
(though there are uninitialized variables that are false positives that
most definitely are initialized, might be related)
Pointing to aliases of the var causes all sorts of problems, but this time
it was causing the uninitialized variable detector to miss certain
parameters.
.return and .param_N are not classed as global variables for data flow
analysis. .return is taken care of by return statements, and .param_N by
call statements.
With this, the menus work up to attempting to load the menu plist.
Something is corrupting zmalloc's blocks.
While things are quite broken now (very incorrect code is being generated),
the dag is much easier to work with. The dag is now stored in an array of
nodes (the children pointers are still used for dagnode operands), and sets
are used for marking node parents, attached identifiers and (when done,
extra edges).
flow_analyze_statement uses the statement type to quickly determin which
operands are inputs and which are outputs. It takes (optional) sets for
used variables, defined variables and killed variables (only partially
working, but I don't actually use kill sets yet). It also takes an optional
array for storing the operands: index 0 is the output, 1-3 are the inputs.
flow_analyze_statement clears any given sets on entry.
Live variable analysis now uses the sets rather than individual vars. Much
cleaner code :).
Dags are completely broken.