my $ipt = $progs->{statements}[$ip];
my $opprop = checkop $op;
+ if($highlight and $highlight->{$ip})
+ {
+ for(values %{$highlight->{$ip}})
+ {
+ for(@$_)
+ {
+ print PRE_MARK_STATEMENT;
+ printf INSTRUCTION_FORMAT, '', '<!>', '.WARN';
+ printf OPERAND_FORMAT, "$_ (in $func->{debugname})";
+ print INSTRUCTION_SEPARATOR;
+ }
+ }
+ }
+
print PRE_MARK_STATEMENT
if $highlight and $highlight->{$ip};
my $showip = $opprop->{isjump};
- printf INSTRUCTION_FORMAT, $showip ? $ip : '', $highlight->{$ip} ? "<!>" : "", $op;
+ printf INSTRUCTION_FORMAT, $showip ? $ip : '', $highlight->{$ip} ? '<!>' : '', $op;
my $cnt = 0;
for my $o(qw(a b c))
$watchme{$_}{valid} = [1, undef, undef]
if defined $watchme{$_};
}
- # an initial run of STORE instruction is for receiving extra parameters
- # (beyond 8). Only possible if the function is declared as having 8 params.
- # Extra parameters behave otherwise like temps, but are initialized at
- # startup.
- for($func->{first_statement} .. (@{$progs->{statements}}-1))
- {
- my $s = $progs->{statements}[$_];
- if($s->{op} eq 'STORE_V')
- {
- $watchme{$s->{a}}{valid} = [1, undef, undef]
- if defined $watchme{$s->{a}};
- $watchme{$s->{a}+1}{valid} = [1, undef, undef]
- if defined $watchme{$s->{a}+1};
- $watchme{$s->{a}+2}{valid} = [1, undef, undef]
- if defined $watchme{$s->{a}+2};
- }
- elsif($s->{op} =~ /^STORE_/)
- {
- $watchme{$s->{a}}{valid} = [1, undef, undef]
- if defined $watchme{$s->{a}};
- }
- else
- {
- last;
- }
- }
my %warned = ();
my %ip_seen = ();
my ($ip, $state, $s, $c) = @_;
my $op = $s->{op};
+ # QCVM BUG: RETURN always takes vector, there is no float equivalent
my $return_hack = $c->{isreturn} // 0;
+ if($op eq 'STORE_V')
+ {
+ # COMPILER BUG of QCC: params are always copied using STORE_V
+ if($s->{b} >= 4 && $s->{b} < 28) # parameter range
+ {
+ $return_hack = 1;
+ }
+ }
+
for(qw(a b c))
{
my $type = $c->{$_};
my $valid = $state->{$ofs}{valid};
if($valid->[0] == 0)
{
- if($return_hack <= 2 and ($op ne 'OR' && $op ne 'AND' || $_ ne 'b')) # fteqcc logicops cause this
+ # 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'))
{
- print "; Use of uninitialized value $ofs in $func->{debugname} at $ip.$_\n";
- ++$warned{$ip}{$_};
+ push @{$warned{$ip}{$_}}, "Use of uninitialized value";
}
}
elsif($valid->[0] < 0)
{
- if($return_hack <= 2 and ($op ne 'OR' && $op ne 'AND' || $_ ne 'b')) # fteqcc logicops cause this
+ # 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'))
{
- print "; Use of temporary $ofs across CALL in $func->{debugname} at $ip.$_\n";
- ++$warned{$ip}{$_};
+ push @{$warned{$ip}{$_}}, "Use of temporary across CALL";
}
}
else
if(!$isread)
{
- print "; Value is never used in $func->{debugname} at $ip.$operand\n";
- ++$warned{$ip}{$operand};
+ push @{$warned{$ip}{$operand}}, "Value is never used";
}
}
}
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
my @globalflags = (GLOBALFLAG_Q | GLOBALFLAG_U) x @{$progs->{globals}};
for(@{$progs->{functions}})
for keys %{$_->{globals_read}};
$globalflags[$_] |= GLOBALFLAG_W
for keys %{$_->{globals_written}};
+ 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 = ();
{
my $type = $_->{type};
my $name = $progs->{getstring}->($_->{s_name});
+ $name = ''
+ if $name eq 'IMMEDIATE';
if($type->{save})
{
for my $i(0..(typesize($_->{type}{type})-1))
{
$globaltypes[$_] = "global";
}
- elsif(($globalflags[$_] & (GLOBALFLAG_S | GLOBALFLAG_I | GLOBALFLAG_Q)) == GLOBALFLAG_Q)
+ elsif(($globalflags[$_] & (GLOBALFLAG_S | GLOBALFLAG_I)) == 0)
{
- $globaltypes[$_] = "uniquetemp";
- $istemp{$_} = 0;
- }
- elsif(($globalflags[$_] & (GLOBALFLAG_S | GLOBALFLAG_I | GLOBALFLAG_Q)) == 0)
- {
- $globaltypes[$_] = "temp";
- $istemp{$_} = 1;
+ 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)
{
for(@{$progs->{globaldefs}})
{
my $s = $progs->{getstring}->($_->{s_name});
+ $s = ''
+ if $s eq 'IMMEDIATE';
$_->{debugname} //= "\$" . "$s"
if length $s;
}
$s->{$_} &= 0xFFFF;
}
}
-
}
print STDERR "Parsing globaldefs...\n";