]> git.xonotic.org Git - xonotic/xonotic.git/blobdiff - misc/tools/progs-analyzer.pl
IMMEDIATE: Ceci n'est pas un nom.
[xonotic/xonotic.git] / misc / tools / progs-analyzer.pl
index a6528d2bfe7bc1f515b08e531ebda310b2921e78..ff6edec11065dbf16ef81700a8343f56b28e8d72 100644 (file)
@@ -484,11 +484,25 @@ sub disassemble_function($$;$)
                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))
@@ -591,32 +605,6 @@ sub find_uninitialized_locals($$)
                $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 = ();
@@ -661,8 +649,18 @@ sub find_uninitialized_locals($$)
                        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->{$_};
@@ -681,18 +679,18 @@ sub find_uninitialized_locals($$)
                                        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
@@ -819,8 +817,7 @@ sub find_uninitialized_locals($$)
 
                        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";
                        }
                }
        }
@@ -880,6 +877,7 @@ sub detect_constants($)
        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}})
@@ -899,6 +897,56 @@ sub detect_constants($)
                        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 = ();
@@ -906,6 +954,8 @@ sub detect_constants($)
        {
                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))
@@ -968,15 +1018,22 @@ sub detect_constants($)
                        {
                                $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)
                        {
@@ -995,6 +1052,8 @@ sub detect_constants($)
        for(@{$progs->{globaldefs}})
        {
                my $s = $progs->{getstring}->($_->{s_name});
+               $s = ''
+                       if $s eq 'IMMEDIATE';
                $_->{debugname} //= "\$" . "$s"
                        if length $s;
        }
@@ -1102,7 +1161,6 @@ sub parse_progs($)
                                $s->{$_} &= 0xFFFF;
                        }
                }
-
        }
 
        print STDERR "Parsing globaldefs...\n";