javaseiten.de   |   Version 0.6
 

4.20. JVM-Befehlssatz: Kurzbeschreibung T

 

 

Befehl Operandenstapel Opcode
tableswitch [pad1 [pad2 [pad3]]] default1 default2 default3 default4 low1 low2 low3 low4 high1 high2 high3 high4 offset11 offset12 offset13 offset14 offset21 offset22 offset23 offset24 ... offsetn1 offsetn2 offsetn3 offsetn4 ..., index --> ... 0xaa

Der JVM-Befehl tableswitch realisiert eine switch-Anweisung auf Bytecodeebene.

Im Anschluss an die Mnemonik tableswitch bzw. den Operationscode aa folgen eine Reihe von Operandenbytes, die für die Codierung der switch-Anweisung notwendig sind. Dabei ist die Anzahl der Operandenbytes von der Anzahl der verwendeten case-Labels der Mehrfachverzweigung abhängig.

Bezeichner Typ Beschreibung
pad1, pad2, pad3 Bytes mit Wert 0. Optionale Füllbytes.
default1, default2, default3, default4 Vorzeichenlose Bytes Offset zu default-Anweisungen.
low1, low2, low3, low4 Vorzeichenlose Bytes Niedrigste case-Konstante.
high1, high2, high3, high4 Vorzeichenlose Bytes Höchste case-Konstante.
offset11, offset12, offset13, offset14 Vorzeichenlose Bytes 1. Offset zu case-Anweisungen.
offset21, offset22, offset23, offset24 Vorzeichenlose Bytes 2. Offset zu case-Anweisungen.
offsetn1, offsetn2, offsetn3, offsetn4 Vorzeichenlose Bytes n. Offset zu case-Anweisungen.
index int Vergleichswert zu case-Konstanten.

Eine switch-Anweisung kann wie folgt aufgebaut sein:

switch (index) {
  case konstante:
    anweisung;
    ...
  default:
    ...
}

Eine Übersetzung einer switch-Anweisung in Bytecode kann auch den JVM-Befehl lookupswitch enthalten. Im unteren Beispiel wurden die drei case-Konstanten 3, 4 und 6 verwendet und der erzeugte Bytecode enthält eine tableswitch-Realisierung. Die folgende Zusammenstellung zeigt den Übergang von einer tableswitch-Realisierung einer switch-Anweisung zu einer Realisierung mit dem Befehl lookupswitch unter Verwendung unterschiedlicher case-Konstanten (verwendeter Compiler: javac, Version 1.6.0):

case-Konstanten Realisierung auf Bytecodeebene
1, 2, 3 tableswitch
1, 2, 4 tableswitch
1, 2, 5 tableswitch
1, 2, 6 lookupswitch
1, 2, 7 lookupswitch
-50, 230, 2 lookupswitch

Der interne Aufbau des Befehls tableswitch kann eine Konstantenfolge wie z.B. 1, 2, 3 effizienter umsetzen und wird deshalb in diesem Fall verwendet. Liegen die Werte der case-Konstanten zu weit auseinander wird auf eine lookupswitch-Lösung zurückgegriffen.

Die auf den Opcode von tableswitch folgenden Operandenbytes codieren eine Sprungtabelle. Der auf dem Operandenstapel befindliche Wert index wird mit den case-Konstanten verglichen. Stimmt eine dieser Konstanten mit dem Vergleichswert überein (case "matches"), dann werden die Anweisungen nach diesem case-Label ausgeführt. In der Sprungtabelle werden Offsets zu diesen case- bzw. dafault-Anweisungen gespeichert. Die Offsetwerte beziehen sich auf die Bytecodestelle des Opcodes von tableswitch. Aus jeweils vier vorzeichenlos gewerteten Operandenbytes können vorzeichenbehaftete 32-Bit-Ganzzahlen berechnet werden:

default = (default1 << 24) | (default2 << 16) | 
          (default3 << 8) | default4
    low = (low1 << 24) | (low2 << 16) | 
          (low3 << 8) | low4
   high = (high1 << 24) | (high2 << 16) | 
          (high3 << 8) | high4
offset1 = (offset11 << 24) | (offset12 << 16) | 
          (offset13 << 8) | offset14
...

Falls der auf dem Operandenstapel liegende Wert index kleiner als low oder größer als high ist, wird zur Bytecodestelle mit Offset default gesprungen. Gilt hingegen low <= index <= high dann wird die case-Offset-Nummer index - low + 1 beachtet.

Im unmittelbaren Anschluss an den Opcode aa können mehrere sogenannte Füllbytes (padding bytes) mit dem numerischen Wert 0 angeordnet sein (0 bis 3 dieser Bytes sind möglich). Diese Füllbytes werden eingefügt, damit die Bytecodestelle des Operandenbytes default1 ein vielfaches der Zahl 4 ist. Die genannte Stelle im Bytecode bezieht sich dabei auf die Bytecodestelle 0 einer jeden Methode. Die Operandenbytes unterliegen damit einem gewünschten 4-Byte-Raster.

Beispiel:

Eine switch-Anweisung soll auf Bytecodeebene mit Hilfe des folgenden kurzen Listings näher untersucht werden:

public class Test {
  public static void main(String[] args)   {
    int a = 4;
    int b = 0;
    switch (a) {
      case 3: b++; 
              break;
      case 4: b += 2; 
              break;
      case 6: b += 3; 
              break;
      default: b += 5;
    }  
    b--;
  }
}

Das kurze Beispielprogramm kann mittels javac in die Klassendatei Test.class übersetzt werden. Die in dieser erzeugten Datei enthaltenen Zahlenwerte (Bytecode) für die switch-Anweisung lauten in hexadezimaler Schreibweise:

aa 00 00 00 00 00 31 00 00 00 03 00 00 00 06 00
00 00 1f 00 00 00 25 00 00 00 31 00 00 00 2b

Auf den Opcode aa für die zugehörige Mnemonik tableswitch folgen im Beispiel 30 Operandenbytes, die der Reihe nach wie folgt zugeordnet werden können:

Operandenbytes Zugeordnete Bezeichner
00 00 pad1 pad2
00 00 00 31 default1 default2 default3 default4
00 00 00 03 low1 low2 low3 low4
00 00 00 06 high1 high2 high3 high4
00 00 00 1f offset11 offset12 offset13 offset14
00 00 00 25 offset21 offset22 offset23 offset24
00 00 00 31 offset31 offset32 offset33 offset34
00 00 00 2b offset41 offset42 offset43 offset44

Die folgende Übersicht listet die zum betrachteten Programmausschnitt korrespondierenden JVM-Befehle auf, wie sie mit Hilfe des Klassendatei-Disassemblers javap erhalten werden können. Die einzelnen Operandenbyte-Blöcke zu tableswitch werden anschließend kurz erläutert.

int a = 4;                 0: iconst_4
                           1: istore_1
int b = 0;                 2: iconst_0
                           3: istore_2
switch (a) {               4: iload_1
                           5: tableswitch { //3 to 6
                              3: 36;
                              4: 42;
                              5: 54;
                              6: 48;
                              default: 54 }
  case 3: b++;            36: iinc 2, 1
          break;          39: goto 57
  case 4: b += 2;         42: iinc 2,2
          break;          45: goto 57
  case 6: b += 3;         48: iinc 2,3
          break;          51: goto 57
  default: b += 5;        54: iinc 2,5
}  
b--;                      57: iinc 2,-1


Operationscode des Befehls tableswitch und dessen Operandenbytes (5 - 35):

 5: aa              Opcode tableswitch
 6: 00 00           2 Padding-Bytes
 8: 00 00 00 31     default = 49, (5 + 49 = 54)
12: 00 00 00 03     low = 3
16: 00 00 00 06     high = 6
20: 00 00 00 1f     offset1 = 31, (5 + 31 = 36)
24: 00 00 00 25     offset2 = 37, (5 + 37 = 42)
28: 00 00 00 31     offset3 = 49, (5 + 49 = 54)
32: 00 00 00 2b     offset4 = 43, (5 + 43 = 48)

Der 3. Offsetwert offset3 hat den gleichen Wert wie default und steht "virtuell" für die im Quelltext nicht vorhandene case-Konstante 5. Bei Konstanten wie z.B. 3, 4 und 10 müssten unter Verwendung von tableswitch zu viele "virtuelle" Offsets eingefügt werden und eine derartige switch-Realisierung wäre deshalb nicht effizient. Es wird daher lookupswitch unter Verwendung von Match-Offset-Paaren verwendet.

 

 

 

Diese Seite nutzt Google-Dienste - siehe dazu Datenschutz.

Copyright © 2006, 2007 Harald Roeder