C verwendet zusammenstellen: operand Typenkonflikt für push
Ich versuche es zu aktivieren/deaktivieren, cache im Linux-kernel-space.
Den code, den ich verwenden, ist
__asm__ __volatile__(
"pushw %eax\n\t" /*line 646*/
"movl %cr0,%eax\n\t"
"orl $0x40000000,%eax\n\t"
"movl %eax,%cr0\n\t"
"wbinvd\n\t"
"pop %eax");
Nachdem ich kompilieren, bekam ich die Fehlermeldung wie folgt:
memory.c: Assembler messages:
memory.c:645: Error: operand type mismatch for `push'
memory.c:646: Error: unsupported for `mov'
memory.c:648: Error: unsupported for `mov'
memory.c:650: Error: operand type mismatch for `pop'
make[4]: *** [memory.o] Error 1
Mein Rechner Intel(R) Xeon(R) CPU E5-1650 v2 @ 3.50 GHz. 64bit Maschine.
Könnte mir jemand helfen, darauf hinweisen, welcher Teil falsch ist und wie ich es beheben kann?
Ich vermute, es ist, weil die Abweichung von der Anweisung und dem register. Aber ich bin verwirrt, wie, um es zu beheben. 🙁
Vielen Dank im Voraus!
- nicht
pushw
für word-Größe (16bit)? eax ist ein 32bit, versuchenpushl
- Hi @Leeor, ich Danke Ihnen sehr für Ihren Kommentar! aber pushl wird meldet Fehler: die Erinnerung.c:645: Fehler: ungültige Anweisung suffix für "push"; ich habe auch versucht pushq, die funktioniert entweder nicht,
Du musst angemeldet sein, um einen Kommentar abzugeben.
Obwohl die meisten 32-bit-Registern bestehen in 64bit-Architekturen, Sie sind nicht mehr in der Lage die Interaktion mit dem Stapel. Also, versuchen, push-oder pop -
%eax
ist eine illegale operation. Also, wenn Sie möchten, spielen mit dem stack verwenden, müssen Sie%rax
, das ist die 64bit-Architekturen entspricht%eax
.Gibt es mehrere Probleme mit Ihrem inline-assembly-Anweisung, die meisten, die sich durch die Fehlermeldungen.
Die erste Fehlermeldung
Error: operand type mismatch for `push'
, entspricht derpushw %eax
Unterricht. Der Fehler ist ein Ergebnis der Tatsache, dass der operand-size-suffix, die Sie verwendet,w
, entspricht nicht der tatsächlichen Größe des Operanden%eax
. Sie haben gesagt, Sie verwenden die Anweisung zum schieben 16-bit-Wert auf dem Stapel, stellte jedoch eine 32-bit-register als einen Operanden. Sie beheben könnte, dass durch die Verwendungpushw %ax
aber das ist nicht, was Sie wollen. Es würde beibehalten, nur die unteren 16-bits der RAX-register, nicht das gesamte register.Anderen "offensichtlich" zu beheben wäre, die Nutzung
pushl %eax
, aber es gibt zwei Probleme mit diesem. Zunächst, um zu lösen andere Probleme, die Sie ändern müssen, das gesamte RAX-register, und das bedeutet, dass Sie benötigen, zu erhalten Sie alle 64 bit des es, nicht nur die unteren 32 bits. Das zweite ist, dass es keine 32-bit-PUSH-Befehl in der 64-bit-Modus, so dass Ihr gezwungen Sie zu verwendenpushq %rax
unabhängig.Den nächsten zwei Fehlermeldungen sind beide
Error: unsupported for `mov'
. Diese Fehlermeldungen entsprechen denmovl %cr0,%eax
undmovl %eax,%cr0
Anweisungen. und beide sind das Ergebnis des gleichen Problems. Im 64-bit Modus gibt ' s keine 32-bit-operand-Größe version dieser Anleitung. Benötigen Sie ein 64-bit-operand, also der fix ist einfach zu verwenden, RAX statt EAX. Dies ist, wo die gesamte 64-bits RAX bekommt clobbered und warum ich sagte, Sie benötigt, um zu bewahren, das gesamte register.Die Letzte Fehlermeldung
Error: operand type mismatch for `pop'
. Dies ist ein Ergebnis der ein ähnliches problem wie die erste. In diesem Fall wird Sie nicht verwendet haben, eine operand-size-suffix, was bedeutet, dass assembler wird versuchen zu bestimmen, wird der operand Größe auf der Grundlage der Operanden. Da hast du verwendet eine 32-bit-Operanden, die%eax
es verwendet eine 32-bit-operand-Größe. Jedoch gerade wie mit PUSH, es gibt 32-bit-POP-Anweisung in der 64-bit-Modus, so dass Sie nicht verwenden können%eax
entweder. In jedem Fall, da der PUSH-Befehl muss 64-bit, die POP-Anweisung muss ein 64-bit übereinstimmen, damit der fix ist die Verwendungpopq %rax
.Schließlich ein problem, das nicht durch Fehlermeldungen angezeigt ist, dass im 64-bit Modus die Größe des CR0 erweitert, um 64-bit. Während die zusätzlichen 32-bits sind reserviert und müssen auf null gesetzt werden, Sie könnte definiert werden, die in zukünftige Prozessoren. Also die
orl $0x40000000,%eax
Unterricht erhalten sollten die oberen 64-bit. Leider ist es nicht, es wird klar, dass die oberen 32-bit-bits RAX Bedeutung, dass dieser Befehl würde auch unabsichtlich löschen der bits, die zukünftigen CPUs könnte einen Sinn zu geben. So sollte es ersetzt werden, mitorq $0x40000000,%rax
.Also die Feste Abfolge von Anweisungen, die wäre:
Dies ist nicht das, was ich werde vorschlagen, Sie in Ihre inline-Montage jedoch. Es ist möglich, um es zu vereinfachen, indem man GCC wählen das register verwendet. Auf diese Weise gibt es keine Notwendigkeit, Sie zu bewahren. Hier ist, was ich würde vorschlagen statt:
long long
ist 64bit noch in der 32bit-Modus. Ich dachte, es würde nicht funktionieren, aber anscheinend nicht. Mit-m32
der compiler ist wohl das reservieren von zwei Registern, aber%0
erweitert nur zu einer von Ihnen? Vielleicht ist es Kommissionierung derA
contraint:edx:eax
? In meiner Antwort, die ich vorgeschlagen, mitlong
. (Ich hattetmp
bis ich sah, eine Antwort darauf innerhalb von Minuten von mir mit dem Hinweis, dass x86-64 kann nicht mov a cr um eine 32-bit-reg.)Der richtige Ansatz ist, um erklären Klamotten auf
%eax
statt speichern/wiederherstellen es sich. Der compiler vermutlich etwas effizienter als die push/pop, wie die Verwendung von verschiedenen Registern für alle Werte, die es bleiben will Leben. Dies bedeutet auch, Sie brauchen nicht verschiedene code für 64-bit-speichern/wiederherstellen%rax
statt.Beachten Sie, dass
pushq %rax
/popq %rax
würde nicht werden sicher in den user-space-code auf x86-64. Es gibt keine Möglichkeit zu sagen, dass gcc inline-asm clobbers der red-zone. Es wäre sicher in der kernel-code, wo das ABI nicht mit einem red-zone, aber wieder, es ist immer noch besiegen den Zweck der GNU-C-inline-asm-syntax.Es ist eine zusätzliche Falten-hier:
mov %cr0, %eax
ist das nicht eine gültige 64bit instruction. Verwenden Sie ein 64bit-register.Lassen den compiler wählen eines Registers für uns löst dieses problem, und gibt dem compiler mehr Freiheit, so ist es eh besser. Deklarieren Sie eine C-variable mit einem Typ, der ist 64bit in die x86-64-ABI, und 32bit im i386-ABI. (z.B.
long
, da dies für die Linux-kernel-ABI, nicht Windows, wolong
ist immer 32bit.uintptr_t
ist eine andere option, die würde die Arbeit in den Linux-kernel. (Aber nicht im Benutzer-Raum: x32 ist lang-Modus mit 32-bit-Pointer).)Diese kompiliert und assembliert, was wir wollen, mit oder ohne
-m32
, wie Sie sehen können auf dem Godbolt Compiler Explorer.Beim schreiben mit der hand, es ist einfacher zu lassen, das operand-size-konkludent durch die Operanden, statt immer mit einem suffix auf der Eselsbrücke. also
push %eax
würde gearbeitet haben (im 32bit Modus), aber immer noch schlechter als die Vermietung der compiler kümmern.Konnten wir verwendet haben
%k[tmp]
zu bekommen%eax
(oder was auch immer) sogar im 64bit Modus, aber das würde null aus dem oberen 32b. Ausgaben 1 byte auf einen REX-Präfix für dieor
Unterricht, der es Wert ist mehr Zukunftssicherheit für CPUs, könnte egal, was Sie schreiben, zu den oberen 32b ein control-register.Den
volatile
stellt sicher, dass die asm-Anweisung nicht optimiert Weg, auch wenn der output-Wert wird nie verwendet.Laut intel -- http://download.intel.com/products/processor/manual/325383.pdf Ein Wort ist 16 bits, so pushw rechnet mit einem 16 bit-Operanden. Das register eax ist 32 bit und geschoben werden müssen mit pushl.
Edit:
Sind Sie auf Montage für 32-bit-oder 64-bit?
Wenn Sie nie herausgefunden, verwenden Sie
pushq %rax
wenn Sie kompilieren 64 bit