+sub detect_constants($)
+{
+ my ($progs) = @_;
+ use constant GLOBALFLAG_R => 1; # read
+ use constant GLOBALFLAG_W => 2; # written
+ use constant GLOBALFLAG_S => 4; # saved
+ use constant GLOBALFLAG_I => 8; # initialized
+ use constant GLOBALFLAG_N => 16; # named
+ use constant GLOBALFLAG_Q => 32; # unique to function
+ use constant GLOBALFLAG_U => 64; # unused
+ use constant GLOBALFLAG_P => 128; # possibly parameter passing
+ use constant GLOBALFLAG_D => 256; # has a def
+ my @globalflags = (GLOBALFLAG_Q | GLOBALFLAG_U) x (@{$progs->{globals}} + 2);
+
+ for(@{$progs->{functions}})
+ {
+ for(keys %{$_->{globals_used}})
+ {
+ if($globalflags[$_] & GLOBALFLAG_U)
+ {
+ $globalflags[$_] &= ~GLOBALFLAG_U;
+ }
+ elsif($globalflags[$_] & GLOBALFLAG_Q)
+ {
+ $globalflags[$_] &= ~GLOBALFLAG_Q;
+ }
+ }
+ $globalflags[$_] |= GLOBALFLAG_R
+ for keys %{$_->{globals_read}};
+ $globalflags[$_] |= GLOBALFLAG_W
+ for keys %{$_->{globals_written}};
+ next
+ if $_->{first_statement} < 0;
+ for my $ip($_->{first_statement} .. (@{$progs->{statements}}-1))
+ {
+ my $s = $progs->{statements}[$ip];
+ if($s->{op} eq 'STORE_V')
+ {
+ $globalflags[$s->{a}] |= GLOBALFLAG_P
+ if $s->{b} >= $_->{parm_start} and $s->{b} < $_->{parm_start} + $_->{locals};
+ $globalflags[$s->{a}+1] |= GLOBALFLAG_P
+ if $s->{b}+1 >= $_->{parm_start} and $s->{b}+1 < $_->{parm_start} + $_->{locals};
+ $globalflags[$s->{a}+2] |= GLOBALFLAG_P
+ if $s->{b}+2 >= $_->{parm_start} and $s->{b}+2 < $_->{parm_start} + $_->{locals};
+ }
+ elsif($s->{op} =~ /^STORE_/)
+ {
+ $globalflags[$s->{a}] |= GLOBALFLAG_P
+ if $s->{b} >= $_->{parm_start} and $s->{b} < $_->{parm_start} + $_->{locals};
+ }
+ else
+ {
+ last;
+ }
+ }
+ }
+
+ # parameter passing globals are only ever used in STORE_ instructions
+ for my $s(@{$progs->{statements}})
+ {
+ next
+ if $s->{op} =~ /^STORE_/;
+
+ my $c = checkop $s->{op};
+
+ for(qw(a b c))
+ {
+ my $type = $c->{$_};
+ next
+ unless defined $type;
+
+ my $ofs = $s->{$_};
+ if($type eq 'inglobal' || $type eq 'inglobalfunc' || $type eq 'outglobal')
+ {
+ $globalflags[$ofs] &= ~GLOBALFLAG_P;
+ }
+ if($type eq 'inglobalvec' || $type eq 'outglobalvec')
+ {
+ $globalflags[$ofs] &= ~GLOBALFLAG_P;
+ $globalflags[$ofs+1] &= ~GLOBALFLAG_P;
+ $globalflags[$ofs+2] &= ~GLOBALFLAG_P;
+ }
+ }
+ }
+
+ my %offsets_saved = ();
+ for(@{$progs->{globaldefs}})
+ {
+ my $type = $_->{type};
+ my $name = $progs->{getstring}->($_->{s_name});
+ $name = ''
+ if $name eq 'IMMEDIATE'; # for fteqcc I had: or $name =~ /^\./;
+ $_->{debugname} = $name
+ if $name ne '';
+ $globalflags[$_->{ofs}] |= GLOBALFLAG_D;
+ if($type->{save})
+ {
+ $globalflags[$_->{ofs}] |= GLOBALFLAG_S;
+ }
+ if(defined $_->{debugname})
+ {
+ $globalflags[$_->{ofs}] |= GLOBALFLAG_N;
+ }
+ }
+ # fix up vectors
+ my @extradefs = ();
+ for(@{$progs->{globaldefs}})
+ {
+ my $type = $_->{type};
+ for my $i(1..(typesize($type->{type})-1))
+ {
+ # add missing def
+ if(!($globalflags[$_->{ofs}+$i] & GLOBALFLAG_D))
+ {
+ print "Missing globaldef for a component@{[defined $_->{debugname} ? ' of ' . $_->{debugname} : '']} at $_->{ofs}+$i\n";
+ push @extradefs, {
+ type => {
+ saved => 0,
+ type => 'float'
+ },
+ ofs => $_->{ofs} + $i,
+ debugname => defined $_->{debugname} ? $_->{debugname} . "[$i]" : undef
+ };
+ }
+ # "saved" and "named" states hit adjacent globals too
+ $globalflags[$_->{ofs}+$i] |= $globalflags[$_->{ofs}] & (GLOBALFLAG_S | GLOBALFLAG_N | GLOBALFLAG_D);
+ }
+ }
+ push @{$progs->{globaldefs}}, @extradefs;
+
+ my %offsets_initialized = ();
+ for(0..(@{$progs->{globals}}-1))
+ {
+ if($progs->{globals}[$_]{v}{int})
+ {
+ $globalflags[$_] |= GLOBALFLAG_I;
+ }
+ }
+
+ my @globaltypes = (undef) x @{$progs->{globals}};
+
+ my %istemp = ();
+ for(0..(@{$progs->{globals}}-1))
+ {
+ next
+ if $_ < @{(DEFAULTGLOBALS)};
+ if(($globalflags[$_] & (GLOBALFLAG_R | GLOBALFLAG_W)) == 0)
+ {
+ $globaltypes[$_] = "unused";
+ }
+ elsif(($globalflags[$_] & (GLOBALFLAG_R | GLOBALFLAG_W)) == GLOBALFLAG_R)
+ {
+ # so it is ro
+ if(($globalflags[$_] & GLOBALFLAG_N) == GLOBALFLAG_N)
+ {
+ $globaltypes[$_] = "read_only";
+ }
+ elsif(($globalflags[$_] & GLOBALFLAG_S) == 0)
+ {
+ $globaltypes[$_] = "const";
+ }
+ else
+ {
+ $globaltypes[$_] = "read_only";
+ }
+ }
+ elsif(($globalflags[$_] & (GLOBALFLAG_R | GLOBALFLAG_W)) == GLOBALFLAG_W)
+ {
+ $globaltypes[$_] = "write_only";
+ }
+ else
+ {
+ # now we know it is rw
+ if(($globalflags[$_] & GLOBALFLAG_N) == GLOBALFLAG_N)
+ {
+ $globaltypes[$_] = "global";
+ }
+ elsif(($globalflags[$_] & (GLOBALFLAG_S | GLOBALFLAG_I)) == 0)
+ {
+ if($globalflags[$_] & GLOBALFLAG_P)
+ {
+ $globaltypes[$_] = "OFS_PARM";
+ }
+ elsif($globalflags[$_] & GLOBALFLAG_Q)
+ {
+ $globaltypes[$_] = "uniquetemp";
+ $istemp{$_} = 0;
+ }
+ else
+ {
+ $globaltypes[$_] = "temp";
+ $istemp{$_} = 1;
+ }
+ }
+ elsif(($globalflags[$_] & (GLOBALFLAG_S | GLOBALFLAG_I)) == GLOBALFLAG_I)
+ {
+ $globaltypes[$_] = "not_saved";
+ }
+ else
+ {
+ $globaltypes[$_] = "global";
+ }
+ }
+ }
+ $progs->{temps} = \%istemp;
+
+ # globaldefs
+ my @globaldefs = (undef) x @{$progs->{globals}};
+ for(@{$progs->{globaldefs}})
+ {
+ $globaldefs[$_->{ofs}] //= $_
+ if defined $_->{debugname};
+ }
+ for(@{$progs->{globaldefs}})
+ {
+ $globaldefs[$_->{ofs}] //= $_;
+ }
+ for(0..(@{$progs->{globals}}-1))
+ {
+ $globaldefs[$_] //= {
+ ofs => $_,
+ s_name => undef,
+ debugname => undef,
+ type => undef
+ };
+ }
+ for(0..(@{(DEFAULTGLOBALS)}-1))
+ {
+ $globaldefs[$_] = { ofs => $_, s_name => undef, debugname => DEFAULTGLOBALS->[$_], type => undef };
+ $globaltypes[$_] = 'defglobal';
+ }
+ my %globaldefs_namecount = ();
+ for(@globaldefs)
+ {
+ $_->{globaltype} = $globaltypes[$_->{ofs}];
+ if(defined $_->{debugname})
+ {
+ # already has debugname
+ }
+ elsif($_->{globaltype} eq 'const')
+ {
+ $_->{debugname} = get_constant($progs, $progs->{globals}[$_->{ofs}]{v}, $_->{type}{type});
+ }
+ else
+ {
+ $_->{debugname} = "$_->{globaltype}_$_->{ofs}";
+ }
+ ++$globaldefs_namecount{$_->{debugname}};
+ }
+ for(@globaldefs)
+ {
+ next
+ if $globaldefs_namecount{$_->{debugname}} <= 1 && !$ENV{FORCE_OFFSETS};
+ #print "Not unique: $_->{debugname} at $_->{ofs}\n";
+ $_->{debugname} .= "\@$_->{ofs}";
+ }
+ $progs->{globaldef_byoffset} = sub
+ {
+ my ($ofs) = @_;
+ my $def = $globaldefs[$ofs];
+ return $def;
+ };
+}
+
+sub parse_progs($$)