JVM- এর জন্য কোনও সংকলক কি "প্রশস্ত" গোটো ব্যবহার করে?


47

আমি মনে করি আপনারা অধিকাংশই জানেন যে gotoএটি জাভা ভাষার একটি সংরক্ষিত কীওয়ার্ড তবে বাস্তবে ব্যবহৃত হয় না। এবং আপনি সম্ভবত এটিও জানেন যে gotoএটি একটি জাভা ভার্চুয়াল মেশিন (জেভিএম) অপকোড। আমি সব জাভা, Scala এবং Kotlin এর অত্যাধুনিক নিয়ন্ত্রণ প্রবাহ কাঠামো শ্রেণীভুক্ত করা হয়, জেভিএম স্তরে, কিছু সমন্বয় ব্যবহার করে বাস্তবায়িত gotoএবং ifeq, ifle, iflt, ইত্যাদি

জেভিএম স্পেসটি https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html#jvms-6.5.goto_w দেখছি আমি দেখতে পাই একটি goto_wঅপকোডও আছে। যেখানে gotoঅফসেট 2-বাইট শাখা goto_wনেয় , 4-বাইট শাখা অফসেট নেয়। জল্পনা বলে যে

যদিও geto_w নির্দেশনাটি 4-বাইট শাখা অফসেট নেয়, অন্য কারণগুলি একটি পদ্ধতির আকার 65535 বাইট (§4.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

পদ্ধতি কোডের আকারটি 64 কে হিসাবে বড় হতে পারে।

সংক্ষিপ্তটির শাখাটি gotoএকটি স্বাক্ষরিত 16-বিট পূর্ণসংখ্যা: -32768 থেকে 32767 পর্যন্ত।

সুতরাং, 65 কে পদ্ধতির শুরু থেকে শেষ পর্যন্ত ঝাঁপ দেওয়ার জন্য সংক্ষিপ্ত অফসেট যথেষ্ট নয়।

এমনকি 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
@ এলিয়টফ্রিশচ এটি করে। যতক্ষণ না পদ্ধতির বাইটকোড আকার 65535 অতিক্রম না করে এবং ধ্রুবক পুলের দৈর্ঘ্য 65535 এরও কম হয়
আপানগিন

18
কুল। ধন্যবাদ। আমি অনুমান করি এমন কারও পক্ষে 64 কে যথেষ্ট হওয়া উচিত। ;)
এলিয়ট ফ্রিচ

3
@ এলিয়টফ্রিশচ - টিপস টুপি রেফারেন্সে।
টিজে ক্রাউডার

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() {}

আইডিয়নে ডেমো

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

7
বাহ্ চমৎকার. আমার মধ্যে বৃহত্তম জাভা প্রকল্প, কয়েকটি প্যাকেজ এবং তাদের মধ্যে কয়েক ডজন ক্লাস সহ প্রায় 200 কেবি সংকলন করে। কিন্তু আপনার Mainসঙ্গে methodWithLargeJump()প্রায় 400KB থেকে প্রনয়ন।
অ্যালোনসো ডেল আর্টে

4
এটি দেখায় যে জাভা সাধারণ ক্ষেত্রে কতটা অপ্টিমাইজ হয়েছে ...
হোলার

1
জাম্প টেবিলগুলির অপব্যবহার আপনি কীভাবে আবিষ্কার করলেন? মেশিনটি কোড জেনারেট করে?
এলিয়ট ফ্রিচ

14
@ এলিয়টফ্রিশচ আমি কেবলমাত্র স্মরণ করতে হয়েছিল যে finallyব্লকগুলি স্বাভাবিক এবং ব্যতিক্রমী প্রবাহের জন্য নকল হয় (জাভা 6 এর পরে বাধ্যতামূলক)। সুতরাং এর মধ্যে দশটি নেস্টিং × 2¹⁰ বোঝায়, তারপরে, স্যুইচের সর্বদা একটি ডিফল্ট লক্ষ্য থাকে, সুতরাং ইলয়েডের সাথে এটির জন্য দশটি বাইট প্লাস প্যাডিং প্রয়োজন। অপ্টিমাইজেশন প্রতিরোধ করতে আমি প্রতিটি শাখায় একটি অননুমোদিত বিবৃতিও যুক্ত করেছি। সীমাবদ্ধতার সন্ধান করা একটি পুনরাবৃত্তিযোগ্য বিষয়, নেস্টেড এক্সপ্রেশন , ল্যাম্বডাস , ফিল্ড , কনস্ট্রাক্টর ...
হোলার

2
মজার বিষয় হল, নেস্টেড এক্সপ্রেশন এবং প্রচুর কনস্ট্রাক্টর সংকলক বাস্তবায়ন সীমাবদ্ধতাগুলিকেও আঘাত করেছে, কেবল বাইটকোড সীমাবদ্ধতা নয়। সর্বাধিক শ্রেণীর ফাইল আকার সম্পর্কে একটি প্রশ্নোত্তরও ছিল (সম্ভবত এই উত্তরটি লেখার সময় আমি অসচেতনভাবে তাগিরের উত্তরটি মনে রেখেছিলাম)। পরিশেষে সর্বোচ্চ প্যাকেজ নামের দৈর্ঘ্য এবং, জেভিএম দিকে, সর্বাধিক নেস্টেড সিঙ্ক্রোনাইজ করা । দেখে মনে হচ্ছে, মানুষ কৌতূহলী থেকে যায়।
হোলার

5

এটি প্রদর্শিত হয় যে কয়েকটি সংকলকগুলিতে (১.6.০ এবং ১১.০..7 এ চেষ্টা করা হয়েছে) যদি কোনও পদ্ধতি যদি গোটো_ডব্লিউয়ের পক্ষে যথেষ্ট হয় তবে এটি একচেটিয়াভাবে গোটো_উ ব্যবহার করে । এমনকি এটির খুব স্থানীয় জাম্প থাকলেও এটি এখনও গোটো_ডাব্লু ব্যবহার করে।


1
কেন হতে পারে? ইন্সট্রাকশন ক্যাশিংয়ের সাথে এটি করার কিছু কি?
আলেকজান্ডার - মনিকা

@ আলেকজান্ডার-রেইনস্টেটমোনিকা সম্ভবত বাস্তবায়নের সহজতা।
ডেভিড জি।
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.