क्या जेवीएम के लिए कोई कंपाइलर "वाइड" गोटो का उपयोग करता है?


47

मुझे पता है कि आपमें से अधिकांश को पता है कि gotoजावा भाषा में एक आरक्षित कीवर्ड है, लेकिन वास्तव में इसका उपयोग नहीं किया जाता है। और आप शायद यह भी जानते हैं कि gotoजावा वर्चुअल मशीन (जेवीएम) ओपकोड है। मैं सभी जावा, स्काला और Kotlin के परिष्कृत नियंत्रण प्रवाह संरचनाओं मानना कर रहे हैं, JVM स्तर पर, के कुछ संयोजन का उपयोग कर कार्यान्वित किया gotoऔर ifeq, ifle, iflt, आदि

JVM कल्पना को देखते हुए https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html#jvms-6.5.goto_w मैं देख रहा हूँ कि वहाँ भी एक goto_wopcode है। जबकि goto2-बाइट शाखा ऑफ़सेट goto_wलेता है, 4-बाइट शाखा ऑफ़सेट लेता है। युक्ति बताती है कि

यद्यपि गोटो_व अनुदेश 4-बाइट शाखा ऑफसेट लेता है, अन्य कारक विधि के आकार को 65535 बाइट्स (to4.11) तक सीमित करते हैं। जावा वर्चुअल मशीन के भविष्य के रिलीज में यह सीमा बढ़ाई जा सकती है।

यह मुझे लगता है जैसे goto_wभविष्य के प्रूफिंग है, कुछ अन्य *_wऑपकोड की तरह। लेकिन यह मेरे साथ भी होता है कि शायद goto_wदो और महत्वपूर्ण बाइट्स का उपयोग शून्य से बाहर किया जा सकता है और दो कम महत्वपूर्ण बाइट्स gotoको आवश्यकतानुसार समायोजन के साथ उपयोग किया जा सकता है।

उदाहरण के लिए, यह जावा स्विच-केस (या स्काला मैच-केस) दिया गया है:

     12: lookupswitch  {
                112785: 48 // case "red"
               3027034: 76 // case "green"
              98619139: 62 // case "blue"
               default: 87
          }
      48: aload_2
      49: ldc           #17                 // String red
      51: invokevirtual #18
            // Method java/lang/String.equals:(Ljava/lang/Object;)Z
      54: ifeq          87
      57: iconst_0
      58: istore_3
      59: goto          87
      62: aload_2
      63: ldc           #19                 // String green
      65: invokevirtual #18
            // Method java/lang/String.equals:(Ljava/lang/Object;)Z
      68: ifeq          87
      71: iconst_1
      72: istore_3
      73: goto          87
      76: aload_2
      77: ldc           #20                 // String blue
      79: invokevirtual #18 
      // etc.

हम इसे फिर से लिख सकते हैं

     12: lookupswitch  { 
                112785: 48
               3027034: 78
              98619139: 64
               default: 91
          }
      48: aload_2
      49: ldc           #17                 // String red
      51: invokevirtual #18
            // Method java/lang/String.equals:(Ljava/lang/Object;)Z
      54: ifeq          91 // 00 5B
      57: iconst_0
      58: istore_3
      59: goto_w        91 // 00 00 00 5B
      64: aload_2
      65: ldc           #19                 // String green
      67: invokevirtual #18
            // Method java/lang/String.equals:(Ljava/lang/Object;)Z
      70: ifeq          91
      73: iconst_1
      74: istore_3
      75: goto_w          91
      79: aload_2
      81: ldc           #20                 // String blue
      83: invokevirtual #18 
      // etc.

मैंने वास्तव में यह कोशिश नहीं की है, क्योंकि मैंने शायद goto_wएस को समायोजित करने के लिए "लाइन नंबर" को बदलने में गलती की है । लेकिन चूंकि यह कल्पना में है, इसलिए इसे करना संभव होना चाहिए।

मेरा सवाल यह है कि क्या कोई कारण है कि बायटेकोड का कंपाइलर या अन्य जनरेटर goto_wवर्तमान 65535 सीमा के साथ उपयोग कर सकता है, यह दिखाने के अलावा कि यह किया जा सकता है?

जवाबों:


51

विधि कोड का आकार 64K जितना बड़ा हो सकता है।

शॉर्ट की शाखा ऑफसेट gotoएक हस्ताक्षरित 16-बिट पूर्णांक है: -32768 से 32767 तक।

तो, शॉर्ट ऑफ़ 65K विधि की शुरुआत से अंत तक एक छलांग लगाने के लिए पर्याप्त नहीं है।

यहां तक ​​कि javacकभी-कभी निकलता है goto_w। यहाँ एक उदाहरण है:

public class WideGoto {

    public static void main(String[] args) {
        for (int i = 0; i < 1_000_000_000; ) {
            i += 123456;
            // ... repeat 10K times ...
        }
    }
}

साथ विघटित javap -c:

  public static void main(java.lang.String[]);
    Code:
       0: iconst_0
       1: istore_1
       2: iload_1
       3: ldc           #2
       5: if_icmplt     13
       8: goto_w        50018     // <<< Here it is! A jump to the end of the loop
          ...

// ... repeat 10K times ...वह संकलन करता है? मुझे पता है कि एकल स्रोत वर्ग के आकार की एक सीमा है ... लेकिन मुझे नहीं पता कि यह वास्तव में क्या है (कोड पीढ़ी केवल एक बार मैंने कुछ देखा है वास्तव में इसे हिट किया है)।
इलियट फ्रिश

3
@ ElliottFrisch यह करता है। जब तक विधि का बायटेकोड का आकार 65535 से अधिक नहीं होता है, और निरंतर पूल की लंबाई भी 65535 से कम होती है।
अपंगिन

18
ठंडा। धन्यवाद। 64k मुझे लगता है कि किसी के लिए पर्याप्त होना चाहिए। ;)
इलियट फ्रिस

3
@ElliottFrisch - संदर्भ में टिप्स की टोपी।
टीजे क्राउडर

34

goto_wजब शाखा में फिट बैठता है तो उपयोग करने का कोई कारण नहीं है goto। लेकिन आपको लगता है कि शाखाएं सापेक्ष हैं , एक हस्ताक्षरित ऑफसेट का उपयोग करते हुए, एक शाखा भी पीछे जा सकती है।

जब आप किसी टूल के आउटपुट को देखते हैं, तो इसे नोटिस नहीं करते javap, क्योंकि यह प्रिंटिंग से पहले परिणामी पूर्ण लक्ष्य पते की गणना करता है।

इसलिए gotoसीमा में -327678 … +32767‬प्रत्येक संभावित लक्ष्य स्थान को संबोधित करने के लिए हमेशा पर्याप्त नहीं है 0 … +65535

उदाहरण के लिए, निम्नलिखित विधि goto_wमें शुरुआत में एक निर्देश होगा :

public static void methodWithLargeJump(int i) {
    for(; i == 0;) {
        try {x();} finally { switch(i){ case 1: try {x();} finally { switch(i){ case 1: 
        try {x();} finally { switch(i){ case 1: try {x();} finally { switch(i){ case 1: 
        try {x();} finally { switch(i){ case 1: try {x();} finally { switch(i){ case 1: 
        try {x();} finally { switch(i){ case 1: try {x();} finally { switch(i){ case 1: 
        try {x();} finally { switch(i){ case 1: try {x();} finally { switch(i){ case 1: 
        } } } } } } } } } } } } } } } } } } } } 
    }
}
static void x() {}

Ideone पर डेमो

Compiled from "Main.java"
class LargeJump {
  public static void methodWithLargeJump(int);
    Code:
       0: iload_0
       1: ifeq          9
       4: goto_w        57567
…

7
वाह! अद्भुत। मेरा सबसे बड़ा जावा प्रोजेक्ट, कुछ पैकेजों और उनके बीच कुछ दर्जन वर्गों के साथ, लगभग 200KB को संकलित करता है। लेकिन आपके Mainसाथ methodWithLargeJump()लगभग 400KB का संकलन है।
अलोंसो डेल एर्ट

4
यह दर्शाता है कि जावा सामान्य मामले के लिए कितना अनुकूलित है ...
होल्गर

1
आपको जंप टेबल के दुरुपयोग के बारे में कैसे पता चला? मशीन उत्पन्न कोड?
इलियट फ्रिश

14
@ElliottFrisch मुझे केवल यह याद रखना था कि finallyब्लॉक सामान्य और असाधारण प्रवाह (जावा 6 के बाद से अनिवार्य) के लिए डुप्लिकेट हो जाते हैं। तो उनमें से दस घोंसले के शिकार का मतलब × 2¹⁰ है, फिर, स्विच में हमेशा एक डिफ़ॉल्ट लक्ष्य होता है, इसलिए iload के साथ मिलकर, इसे दस बाइट्स प्लस पैडिंग की आवश्यकता होती है। मैंने अनुकूलन को रोकने के लिए प्रत्येक शाखा में एक nontrivial स्टेटमेंट भी जोड़ा है। शोषण सीमाएं एक आवर्ती विषय है, नेस्टेड अभिव्यक्तियाँ , लंबोदर , फ़ील्ड्स , कंस्ट्रक्टर्स
Holger

2
दिलचस्प बात यह है कि नेस्टेड एक्सप्रेशन और बहुत सारे कंस्ट्रक्टर भी बायलरोड की सीमाओं को ही नहीं, कंपाइलर कार्यान्वयन सीमाओं को भी प्रभावित करते हैं। अधिकतम वर्ग फ़ाइल आकार के बारे में एक प्रश्नोत्तर भी था (शायद मैं अनजाने में यह उत्तर लिखते समय टैगिर के उत्तर को याद कर लिया था)। अंत में अधिकतम पैकेज नाम की लंबाई और, जेवीएम की तरफ, अधिकतम नेस्टेड सिंक्रनाइज़ । लगता है, लोग उत्सुक रहते हैं।
होल्गर

5

ऐसा प्रतीत होता है कि कुछ कंपाइलरों में (1.6.0 और 11.0.7 में कोशिश की गई है), यदि कोई विधि पर्याप्त बड़ी है, जिसे कभी-कभी goto_w की आवश्यकता होती है, तो यह विशेष रूप से goto_w का उपयोग करता है । यहां तक ​​कि जब यह बहुत स्थानीय कूदता है, तब भी यह goto_w का उपयोग करता है।


1
ऐसा क्यों हो सकता है? यह निर्देश कैशिंग के साथ कुछ करना है?
अलेक्जेंडर - मोनिका

@ अलेक्जेंडर-रिंस्टोमोनिका शायद कार्यान्वयन में आसानी।
डेविड जी।
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.