2013-07-08 6 views
9

Ich stolperte in ein lustiges Problem, das ich nicht verstehen kann.LLVM Compiler Optimierungsfehler oder was?

Hintergrund ist:

  • LLVM 4.2 Compiler auf XCode
  • mit C++ kompiliert 11 Unterstützung
  • mit -Os zusammengestellt
  • zusammengestellt für ARMv7/armv7s Architektur

Jetzt Ich habe festgestellt, dass ein Problem mit Code vorliegt, der nur beim Kompilieren mit aktivierten Optimierungen vorhanden ist.

Der Code ist, wörtlich:

static int foo(int tx, int sx, int w) 
{ 
    int vs = 60; 

    if (sx < vs*2 && tx > w - vs*2) 
    return (sx + w - tx); 
    else if (sx > w - vs*2 && tx < vs*2) 
    return -(w - sx + tx); 
    else 
    return sx - tx; 
} 

nun durch mit LLDB werde ich trat Code einen seltsamen Fehler aufzuspüren, und dies führt mich zu erkennen, dass der erste Zweig der if mit Eingang genommen wird

sx = 648 
tx = 649 
w = 768 
vs = 60 

(diese Werte werden von direkt aus der Einheimischen Tisch in XCode genommen, bin ich LLDB über nicht in der Lage vs abzufragen, weil ich es wird optimiert erraten.)

Der erste Zweig ist dann if (648 < 120 && ..., also sollte es keine Möglichkeit geben, es zu nehmen, aber es passiert tatsächlich. Wenn ich mit -O0 kompiliere, verschwindet der Fehler einfach. Eine weitere lustige Sache ist die Tatsache, dass für sx = 647 und tx = 648 der Fehler nicht auftritt.

Jetzt sind die Dinge zwei: oder ich vermisse etwas so offensichtlich, dass 10 Stunden Debuggen verbieten mir zu sehen, oder es gibt eine Art von Bug in einer Optimierung.

Irgendwelche Hinweise?

Einige mehr Hintergrund, ist dies die ASM generiert:

.private_extern __ZN5Utils12wrapDistanceEiii 
    .globl __ZN5Utils12wrapDistanceEiii 
    .align 2 
    .code 16      @ @_ZN5Utils12wrapDistanceEiii 
    .thumb_func __ZN5Utils12wrapDistanceEiii 
__ZN5Utils12wrapDistanceEiii: 
    .cfi_startproc 
Lfunc_begin9: 
@ BB#0: 
    @DEBUG_VALUE: wrapDistance:tx <- R0+0 
    @DEBUG_VALUE: wrapDistance:sx <- R1+0 
    @DEBUG_VALUE: wrapDistance:w <- R2+0 
    @DEBUG_VALUE: vs <- 60+0 
    sub.w r3, r2, #120 
    cmp r1, #119 
    @DEBUG_VALUE: wrapDistance:tx <- R0+0 
    @DEBUG_VALUE: wrapDistance:sx <- R1+0 
    @DEBUG_VALUE: wrapDistance:w <- R2+0 
    it le 
    cmple r3, r0 
Ltmp42: 
    @DEBUG_VALUE: wrapDistance:tx <- R0+0 
    @DEBUG_VALUE: wrapDistance:sx <- R1+0 
    @DEBUG_VALUE: wrapDistance:w <- R2+0 
    ittt lt 
    sublt r0, r1, r0 
Ltmp43: 
    addlt r0, r2 
    @DEBUG_VALUE: vs <- 60+0 
    bxlt lr 
Ltmp44: 
    @DEBUG_VALUE: wrapDistance:tx <- R0+0 
    @DEBUG_VALUE: wrapDistance:sx <- R1+0 
    @DEBUG_VALUE: wrapDistance:w <- R2+0 
    @DEBUG_VALUE: vs <- 60+0 
    cmp r3, r1 
    @DEBUG_VALUE: wrapDistance:tx <- R0+0 
    @DEBUG_VALUE: wrapDistance:sx <- R1+0 
    @DEBUG_VALUE: wrapDistance:w <- R2+0 
    it lt 
    cmplt r0, #119 
Ltmp45: 
    @DEBUG_VALUE: wrapDistance:tx <- R0+0 
    @DEBUG_VALUE: wrapDistance:sx <- R1+0 
    @DEBUG_VALUE: wrapDistance:w <- R2+0 
    itttt le 
    suble r1, r2, r1 
Ltmp46: 
    addle r0, r1 
Ltmp47: 
    rsble r0, r0, #0 
    @DEBUG_VALUE: vs <- 60+0 
    bxle lr 
Ltmp48: 
    @DEBUG_VALUE: wrapDistance:tx <- R0+0 
    @DEBUG_VALUE: wrapDistance:sx <- R1+0 
    @DEBUG_VALUE: vs <- 60+0 
    subs r0, r1, r0 
Ltmp49: 
    @DEBUG_VALUE: vs <- 60+0 
    bx lr 
Ltmp50: 
Lfunc_end9: 
    .cfi_endproc 

Wenn ich einen Druck setzen, zB printf("%d < %d - %d",sx,vs*2,sx < vs*2) vor dem if-Klausel dann verschwindet der Fehler.

Diese einfache Testfall exibits das Problem:

for (int i = 0; i < 767; ++i) 
{ 
    printf("test: %d, %d, %d",i,i+1,Utils::wrapDistance(i+1, i, 768)) 
} 

... 
test: 641, 642, -1 
test: 642, 643, -1 
test: 643, 644, -1 
test: 644, 645, -1 
test: 645, 646, -1 
test: 646, 647, -1 
test: 647, 648, -1 
test: 648, 649, -769 
test: 649, 650, -1 
test: 650, 651, -1 
test: 651, 652, -1 
test: 652, 653, -1 
test: 653, 654, -1 
test: 654, 655, -1 
... 

EDIT2

ich es geschafft, die Fehler in einem Stand-alone-Programm zu reproduzieren, ich ein leeres iOS Projekt gerade erstellt haben, dann definiert ich die Funktion zweimal, einmal in der AppDelegate.mm, um direkt von der gleichen Datei aufgerufen zu werden, und eine andere in einer separaten Datei:

Test.h

Test.cav

#include "Test.h" 

int Utils::wrapDistance(int tx, int sx, int w) 
{ 
    int vs = 60; 

    if (sx < vs*2 && tx > w - vs*2) 
    return (sx + w - tx); 
    else if (sx > w - vs*2 && tx < vs*2) 
    return -(w - sx + tx); 
    else 
    return sx - tx; 
} 

AppDelegate.mm

#import "AppDelegate.h" 
#include "Test.h" 

int wrapDistance(int tx, int sx, int w) 
{ 
    int vs = 60; 

    if (sx < vs*2 && tx > w - vs*2) 
    return (sx + w - tx); 
    else if (sx > w - vs*2 && tx < vs*2) 
    return -(w - sx + tx); 
    else 
    return sx - tx; 
} 

@implementation AppDelegate 

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions 
{ 
    ... 

    for (int i = 0; i < 767; ++i) 
    { 
    NSLog(@"test inside: %d, %d, %d",i,i+1,wrapDistance(i+1, i, 768)); 
    NSLog(@"test outside: %d, %d, %d",i,i+1,Utils::wrapDistance(i+1, i, 768)); 
    } 

    return YES; 
} 

... 

OUTPUT

test inside: 644, 645, -1 
test outside: 644, 645, -1 
test inside: 645, 646, -1 
test outside: 645, 646, -1 
test inside: 646, 647, -1 
test outside: 646, 647, -1 
test inside: 647, 648, -1 
test outside: 647, 648, -1 
test inside: 648, 649, -1 
test outside: 648, 649, -769 
test inside: 649, 650, -1 
test outside: 649, 650, -1 
test inside: 650, 651, -1 
test outside: 650, 651, -1 
test inside: 651, 652, -1 
test outside: 651, 652, -1 

Wie Sie sehen können, ist das Verhalten für die Funktion, die von dem in der Datei definiert ist, genannt ist korrekt, aber das gleiche gilt nicht für den anderen, der th zeigt Der gleiche Fehler. Wenn ich zwinge, die innere Funktion nicht mit __attribute__ ((noinline)) zu verbinden, dann schlagen beide Funktionen fehl. Ich tappe wirklich in die Dunkelheit.

+0

Es ist sehr wahrscheinlich, dass Sie nicht definiertes Verhalten sind zu sehen, verursacht durch ein Problem an anderer Stelle in Ihrem Code. Können Sie einen vollständigen Testfall erstellen? –

+0

Dies ist der vollständige Testfall, die Funktion ist nicht auf externe Eingaben angewiesen, es ist eine statische Dienstprogrammfunktion, die nur zum Berechnen der Entfernung zwischen zwei Kacheln in einer umhüllten Umgebung verwendet wird. Der Fehler tritt immer bei diesen Eingabewerten auf. Ich sollte versuchen, es vom Projekt zu isolieren oder den ASM-Code zu überprüfen. – Jack

+0

Mit "Testfall" meine ich einen [SSCCE] (http://sscce.org), der den Treibercode enthalten würde (d. H. Den Komponententest oder was auch immer benötigt wird, um das Verhalten zu zeigen). Wie Sie sicher wissen, haben viele Fehler die Angewohnheit, sich selbst zu reparieren, sobald der störende Code vom Rest des Programms isoliert ist;) –

Antwort

5

Erstens bedeutet Ihr Testfall, dass es tatsächlich irrtümlicherweise den Zweig else if übernimmt.

Aber ich nehme es zurück; Es scheint einen Fehler in der resultierenden ASM zu geben. *

Hier ist eine formatiert/kommentierte Version des ASM für den fehlerhaften Test:

% r0 = tx = 649 
% r1 = sx = 648 
% r2 = w = 768 

% w - vs*2 
sub.w r3, r2, #120   % r3 = 648 

% if (sx < vs*2) 
cmp r1, #119 
it le      % (1) Not taken 
    % if ((w - vs*2) < tx) 
    cmple r3, r0 
ittt lt     % (2) Not taken 
    % return (sx + w - tx) 
    sublt r0, r1, r0 
    addlt r0, r2 
    bxlt lr 

% if ((w - vs*2) < sx) 
cmp r3, r1 
it lt      % (3) Not taken 
    % if (tx < vs*2) 
    cmplt r0, #119 
itttt le     % (4) Taken! <<<<<<<<<<<<< 
    % return -(w - sx + tx) 
    suble r1, r2, r1 
    addle r0, r1 
    rsble r0, r0, #0 
    bxle lr 

% return sx - tx 
subs r0, r1, r0 
bx lr 

Conditionals (3) und (4) sollen zusammenarbeiten, eine logische UND-Verknüpfung des erreichen zwei Unterausdrücke. Theoretisch wird Block (4) nur ausgeführt, wenn Block (3) und ausgeführt wird, der nachfolgende Vergleich setzt die entsprechenden Statusflags. **

Dies ist jedoch falsch implementiert. Vergleich (3) setzt Z, was bedeutet, dass Bedingung (3) nicht ausgelöst wird (erfordert N!=V), so dass Bedingung (4) nicht ausgeführt wird. Gut so weit. Aber es ist ausreichend, um Bedingung (4) auszulösen (erfordert (Z==1) || (N!=V)), was zu dem Problem führt, das Sie sehen.

Zusammenfassend gibt es vier Möglichkeiten:

  1. Es gibt wirklich ein Fehler im Backend LLVM ist ARM7-Targeting.
  2. Der von Ihnen bereitgestellte C-Code ist nicht wirklich der Code, den Sie kompilieren.
  3. Sie haben irgendwo ungültigen C-Code, der undefiniertes Verhalten auslöst, was dazu führt, dass Nonsense-ASM als Nebeneffekt generiert wird.
  4. Meine obige Analyse ist falsch!


* Auch wenn es jetzt 0.40 ist, so kann ich irren ...

** http://blogs.arm.com/software-enablement/206-condition-codes-1-condition-flags-and-codes/

+0

Dank für Ihre Analyse, nie direkt mit Arm Asm gearbeitet, so hätte ich viel mehr Zeit benötigt, um die Anweisungen und die allgemeine Architektur zu sehen, die Verzweigung verwendet. Jetzt neige ich immer dazu, Compiler-Fehler auszuschließen, weil 99,9999% der Male ein Entwicklerfehler ist, aber ich würde Hypothese 2 und 4 ausschließen. So ist es möglich, dass ein ungültiger Code undefiniertes Verhalten auslöst, aber ich sehe nicht, wie dies entstehen könnte falsche binäre? Sonst ist das wirklich ein Fehler. – Jack

+0

@Jack: Der Compiler darf annehmen, dass Ihr Code gültig ist, und entsprechend Optimierungen vornehmen. Wenn Ihr Code irgendwie ungültig ist, sind diese Optimierungen möglicherweise nicht mehr sinnvoll (undefiniertes Verhalten). Ich würde sagen, es ist ziemlich unwahrscheinlich, dass sich solch ein Problem in einer eigenständigen Funktion wie dieser manifestieren könnte. (Allerdings dachte ich auch, es wäre ziemlich unwahrscheinlich, dass es einen Compiler-Bug geben könnte ...) –

+0

Ich konnte den Fehler in einem leeren Projekt reproduzieren. Alles, was ich tun musste, ist, die Funktion in eine separate Datei zu stellen und sie aus dem 'applicationDidFinishLaunching:' des App-Delegaten aufzurufen. Das komische Ding: wenn ich die Funktion in der gleichen Akte des Delegierten behalte, tritt der Fehler nicht auf. Wenn ich es auf eine eigene Datei verschiebe, tritt es auf. Überprüfen Sie meine Bearbeitung. – Jack