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.
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? –
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
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;) –