+#!/usr/bin/perl
+
use strict;
use warnings;
use Digest::SHA;
{
return { a => 'inglobal', b => 'inglobalfunc' };
}
+ if($op =~ /^INVALID#/)
+ {
+ return { isinvalid => 1 };
+ }
return { a => 'inglobal', b => 'inglobal', c => 'outglobal' };
}
{
for(values %{$highlight->{$ip}})
{
- for(@$_)
+ for(sort keys %$_)
{
print PRE_MARK_STATEMENT;
printf INSTRUCTION_FORMAT, '', '<!>', '.WARN';
}
}
+ if($c->{isinvalid})
+ {
+ ++$warned{$ip}{''}{"Invalid opcode"};
+ }
for(qw(a b c))
{
my $type = $c->{$_};
# COMPILER BUG of FTEQCC: AND and OR may take uninitialized as second argument (logicops)
if($return_hack <= 2 and ($op ne 'OR' && $op ne 'AND' || $_ ne 'b'))
{
- push @{$warned{$ip}{$_}}, "Use of uninitialized value";
+ ++$warned{$ip}{$_}{"Use of uninitialized value"};
}
}
elsif($valid->[0] < 0)
# COMPILER BUG of FTEQCC: AND and OR may take uninitialized as second argument (logicops)
if($return_hack <= 2 and ($op ne 'OR' && $op ne 'AND' || $_ ne 'b'))
{
- push @{$warned{$ip}{$_}}, "Use of temporary across CALL";
+ ++$warned{$ip}{$_}{"Use of temporary across CALL"};
}
}
else
}
elsif($type eq 'ipoffset')
{
- push @{$warned{$ip}{$_}}, "Endless loop"
+ ++$warned{$ip}{$_}{"Endless loop"}
if $ofs == 0;
- push @{$warned{$ip}{$_}}, "No-operation jump"
+ ++$warned{$ip}{$_}{"No-operation jump"}
if $ofs == 1;
}
}
if(!$isread)
{
- push @{$warned{$ip}{$operand}}, "Value is never used";
+ ++$warned{$ip}{$operand}{"Value is never used"};
}
}
}
use constant GLOBALFLAG_Q => 32; # unique to function
use constant GLOBALFLAG_U => 64; # unused
use constant GLOBALFLAG_P => 128; # possibly parameter passing
- my @globalflags = (GLOBALFLAG_Q | GLOBALFLAG_U) x @{$progs->{globals}};
+ use constant GLOBALFLAG_D => 256; # has a def
+ my @globalflags = (GLOBALFLAG_Q | GLOBALFLAG_U) x (@{$progs->{globals}} + 2);
for(@{$progs->{functions}})
{
if $name eq 'IMMEDIATE' or $name =~ /^\./;
$_->{debugname} = $name
if $name ne '';
+ $globalflags[$_->{ofs}] |= GLOBALFLAG_D;
if($type->{save})
{
- for my $i(0..(typesize($_->{type}{type})-1))
- {
- $globalflags[$_->{ofs}] |= GLOBALFLAG_S;
- }
+ $globalflags[$_->{ofs}] |= GLOBALFLAG_S;
}
- if($name ne '')
+ if(defined $_->{debugname})
{
- for my $i(0..(typesize($_->{type}{type})-1))
+ $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))
{
- $globalflags[$_->{ofs}] |= GLOBALFLAG_N;
+ 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))
{
$globaldefs[$_] //= {
ofs => $_,
s_name => undef,
- debugname => undef
+ debugname => undef,
+ type => undef
};
}
for(0..(@{(DEFAULTGLOBALS)}-1))
die "Out of range name in globaldef $_"
if $g->{s_name} < 0 || $g->{s_name} >= length $p{strings};
my $name = $p{getstring}->($g->{s_name});
- die "Out of range ofs in globaldef $_ (name: \"$name\")"
+ die "Out of range ofs $g->{ofs} in globaldef $_ (name: \"$name\")"
if $g->{ofs} >= $p{globals};
}
die "Out of range name in fielddef $_"
if $g->{s_name} < 0 || $g->{s_name} >= length $p{strings};
my $name = $p{getstring}->($g->{s_name});
- die "Out of range ofs in globaldef $_ (name: \"$name\")"
+ die "Out of range ofs $g->{ofs} in fielddef $_ (name: \"$name\")"
if $g->{ofs} >= $p{header}{entityfields};
}
die "Out of range file in function $_"
if $f->{s_file} < 0 || $f->{s_file} >= length $p{strings};
my $file = $p{getstring}->($f->{s_file});
- die "Out of range first_statement in function $_ (name: \"$name\", file: \"$file\")"
+ die "Out of range first_statement in function $_ (name: \"$name\", file: \"$file\", first statement: $f->{first_statement})"
if $f->{first_statement} >= @{$p{statements}};
- die "Out of range parm_start in function $_ (name: \"$name\", file: \"$file\")"
- if $f->{parm_start} < 0 || $f->{parm_start} >= @{$p{globals}};
- die "Out of range locals in function $_ (name: \"$name\", file: \"$file\")"
- if $f->{locals} < 0 || $f->{parm_start} + $f->{locals} >= @{$p{globals}};
- die "Out of range numparms in function $_ (name: \"$name\", file: \"$file\")"
- if $f->{numparms} < 0 || $f->{numparms} > 8;
- my $totalparms = 0;
- for(0..($f->{numparms}-1))
+ if($f->{first_statement} >= 0)
{
- die "Out of range parm_size[$_] in function $_ (name: \"$name\", file: \"$file\")"
- unless { 0 => 1, 1 => 1, 3 => 1 }->{$f->{parm_size}[$_]};
- $totalparms += $f->{parm_size}[$_];
+ die "Out of range parm_start in function $_ (name: \"$name\", file: \"$file\", first statement: $f->{first_statement})"
+ if $f->{parm_start} < 0 || $f->{parm_start} >= @{$p{globals}};
+ die "Out of range locals in function $_ (name: \"$name\", file: \"$file\", first statement: $f->{first_statement})"
+ if $f->{locals} < 0 || $f->{parm_start} + $f->{locals} > @{$p{globals}};
+ die "Out of range numparms $f->{numparms} in function $_ (name: \"$name\", file: \"$file\", first statement: $f->{first_statement})"
+ if $f->{numparms} < 0 || $f->{numparms} > 8;
+ my $totalparms = 0;
+ for(0..($f->{numparms}-1))
+ {
+ die "Out of range parm_size[$_] in function $_ (name: \"$name\", file: \"$file\", first statement: $f->{first_statement})"
+ unless { 0 => 1, 1 => 1, 3 => 1 }->{$f->{parm_size}[$_]};
+ $totalparms += $f->{parm_size}[$_];
+ }
+ die "Out of range parms in function $_ (name: \"$name\", file: \"$file\", first statement: $f->{first_statement})"
+ if $f->{parm_start} + $totalparms > @{$p{globals}};
+ die "More parms than locals in function $_ (name: \"$name\", file: \"$file\", first statement: $f->{first_statement})"
+ if $totalparms > $f->{locals};
}
- die "Out of range parms in function $_ (name: \"$name\", file: \"$file\")"
- if $f->{locals} < 0 || $f->{parm_start} + $totalparms >= @{$p{globals}};
}
print STDERR "Range checking statements...\n";
elsif($type eq 'inglobalvec')
{
$s->{$_} &= 0xFFFF;
- die "Out of range global offset in statement $ip - cannot continue"
- if $s->{$_} >= @{$p{globals}}-2;
+ if($c->{isreturn})
+ {
+ die "Out of range global offset in statement $ip - cannot continue"
+ if $s->{$_} >= @{$p{globals}};
+ print "Potentially out of range global offset in statement $ip - may crash engines"
+ if $s->{$_} >= @{$p{globals}}-2;
+ }
+ else
+ {
+ die "Out of range global offset in statement $ip - cannot continue"
+ if $s->{$_} >= @{$p{globals}}-2;
+ }
}
elsif($type eq 'outglobal')
{