X86 এর জন্য কীভাবে asm gcc / clang উত্পাদন করে সে সম্পর্কে আরও কিছু বিশদ সহ আরও একটি ঘোরানো প্রশ্নের এই উত্তরটির পূর্ববর্তী সংস্করণটিও দেখুন ।
সি এবং সি ++ এ কোনও ঘূর্ণন প্রকাশের সবচেয়ে সংকলক-বান্ধব উপায় যা কোনও পূর্বনির্ধারিত আচরণ এড়ানো যায় বলে মনে হয় জন রেগারের বাস্তবায়ন । আমি এটিকে প্রস্থের (যেমন স্থির-প্রস্থের ধরণের ব্যবহার করে uint32_t
) প্রস্থের সাথে ঘোরানোর জন্য অভিযোজিত করেছি ।
#include <stdint.h> // for uint32_t
#include <limits.h> // for CHAR_BIT
#include <assert.h>
static inline uint32_t rotl32 (uint32_t n, unsigned int c)
{
const unsigned int mask = (CHAR_BIT*sizeof(n) - 1);
c &= mask;
return (n<<c) | (n>>( (-c)&mask ));
}
static inline uint32_t rotr32 (uint32_t n, unsigned int c)
{
const unsigned int mask = (CHAR_BIT*sizeof(n) - 1);
c &= mask;
return (n>>c) | (n<<( (-c)&mask ));
}
কোনও স্বাক্ষরবিহীন পূর্ণসংখ্যার জন্য কাজ করে, কেবল uint32_t
তাই নয় , যাতে আপনি অন্যান্য আকারের জন্য সংস্করণ তৈরি করতে পারেন।
দেখুন একটি সি ++ 11 টেমপ্লেট সংস্করণ নিরাপত্তা চেক প্রচুর সঙ্গে (সহ একটি static_assert
যে টাইপ প্রস্থ 2 একটি ক্ষমতা হয়) , যা কিছু 24-বিট DSPs বা 36-বিট কারুরই, উদাহরণস্বরূপ উপর ঘটনা না।
আমি কেবলমাত্র টেমপ্লেটটি এমন নামের সাথে মোড়কের জন্য ব্যাক-এন্ড হিসাবে ব্যবহার করার পরামর্শ দেব যা স্পষ্টভাবে ঘোরানো প্রস্থকে অন্তর্ভুক্ত করে। পূর্ণসংখ্যা-প্রচারের নিয়মের মানে rotl_template(u16 & 0x11UL, 7)
হল একটি 16 বা নয় ( 32 এর প্রস্থের উপর নির্ভর করে unsigned long
) 32 বা 64-বিট ঘোরানো হবে । এমনকি uint16_t & uint16_t
উন্নীত করা হয় signed int
সি ++ এর পূর্ণসংখ্যা প্রচার বিধি দ্বারা, প্লাটফর্ম যেখানে উপর ব্যতীত int
চেয়ে ব্যাপকতর হয় uint16_t
।
এক্স 86 উপর , এই সংস্করণে একটি একক থেকে inlinesrol r32, cl
(অথবা rol r32, imm8
কম্পাইলার সহ) এটি grok, কারণ কম্পাইলার জানে যে এক্স 86 ঘোরান এবং শিফট নির্দেশাবলী মাস্ক একই ভাবে সি উৎস নেই Shift-গণনা।
X86-এ এই ইউবি-এড়ানো এডিয়োম, uint32_t x
এবং unsigned int n
ভেরিয়েবল-কাউন্ট শিফটগুলির জন্য সংকলক সমর্থন
- ঝাঁকুনি: চলক-গণনার জন্য স্বীকৃত ক্ল্যাং 3.5, একাধিক শিফট + বা তার আগে ইনসান থেকে আবর্তিত।
- জিসিসি: ভেরিয়েবল-কাউন্টের জন্য স্বীকৃত জিসিসি ৪.৯ থেকে একাধিক শিফট + বা তার আগে ইনসন হয়। জিসিসি 5 এবং পরে উইকিপিডিয়া সংস্করণে শাখাটি এবং মুখোশটি অপ্টিমাইজ করে, ভেরিয়েবল গণনাগুলির জন্য কেবল একটি
ror
বা rol
নির্দেশ ব্যবহার করে।
- আইসিসি: আইসিসি 13 বা তার আগের থেকে পরিবর্তনশীল-গণনা ঘোরার জন্য সমর্থিত । কনস্ট্যান্ট-কাউন্ট ব্যবহার ঘোরান
shld edi,edi,7
যা ধীরে ধীরে এবং rol edi,7
কিছু সিপিইউ (বিশেষত এএমডি, তবে কিছু ইনটেল) এর চেয়ে বেশি বাইট নেয় , যখন বিএমআই 2 এমওভিকে rorx eax,edi,25
সংরক্ষণ করার জন্য উপলব্ধ না থাকে ।
- এমএসভিসি: x86-64 সিলে 19: কেবল ধ্রুবক-গণনা ঘোরার জন্য স্বীকৃত। (উইকিপিডিয়া আইডিয়ামটি স্বীকৃত, তবে শাখা এবং এ্যান্ডটি অপ্টিমাইজ করা হয়নি)। X86 থেকে x
_rotl
/ ( _rotr
অন্তর্গত <intrin.h>
x86-64) ব্যবহার করুন intr
এআরএম জন্য জিসিসি একটি ব্যবহার and r1, r1, #31
পরিবর্তনশীল গোনা rotates জন্য, কিন্তু এখনও একটি একক নির্দেশ সঙ্গে প্রকৃত ঘোরান না : ror r0, r0, r1
। সুতরাং জিসিসি বুঝতে পারে না যে ঘোরানো গণনাগুলি সহজাতভাবে মডিউলার। এআরএম ডক্স যেমন বলেছে, "শিফটের দৈর্ঘ্যের সাথে আরওআর n
, 32-রও বেশি শিফটের দৈর্ঘ্যের সাথে আরওর হিসাবে সমান n-32
" । আমি মনে করি জিসিসি এখানে বিভ্রান্ত হয়ে পড়েছে কারণ এআরএম-এ বাম / ডান স্থানান্তরগুলি গণনাটি পূরণ করে, তাই 32 বা তার বেশি একটি শিফট রেজিস্টার সাফ করবে। (X86 এর বিপরীতে যেখানে শিফটগুলি গণনাগুলি ঘোরার মতোই মাস্ক করে)। ঘোরানো আইডিয়ামটি স্বীকৃতি দেওয়ার আগে এটির একটি এবং নির্দেশের দরকার সম্ভবত এটি স্থির করে, কারণ লক্ষ্যবস্তুতে নন-সার্কুলার শিফ্টগুলি কীভাবে কাজ করে।
বর্তমান x86 সংকলক এখনও 8 এবং 16-বিট ঘোরার জন্য একটি পরিবর্তনশীল গণনাটি মাস্ক করতে একটি অতিরিক্ত নির্দেশ ব্যবহার করে, সম্ভবত একই কারণে তারা এআরএমে ও এ্যান্ড এড়ায় না। এটি একটি মিস অপটিমাইজেশন, কারণ কর্মক্ষমতা কোনও x86-64 সিপিইউতে ঘোরানো গণনার উপর নির্ভর করে না। (গণনাগুলির মাস্কিং কার্য সম্পাদনের কারণে 286 দিয়ে প্রবর্তন করা হয়েছিল কারণ এটি শিফটগুলি পুনরাবৃত্তভাবে পরিচালনা করে, আধুনিক সিপিইউগুলির মতো ধ্রুবক-বিন্যাসের সাথে নয়))
বিটিডাব্লু, পরিবর্তনশীল-গণনা ঘোরার জন্য ঘোরানো-ডান পছন্দ করুন, সংকলকটিকে 32-n
এআরএম এবং এমআইপিএসের মতো আর্কিটেকচারগুলিতে বাম ঘোরানো বাস্তবায়ন করতে এড়াতে কেবলমাত্র একটি ঘোরানো-ডান সরবরাহ করে। (এটি সংকলন-সময়-ধ্রুবক গণনাগুলি ছাড়িয়ে যায়))
মজার বিষয়: এআরএম সত্যিই ডেডিকেটেড শিফট / ঘোরান নির্দেশাবলী নেই, এটা দিয়ে শুধু যে MOV এর উৎস প্রতীক ROR মোডে পিপা কর্মচারী মধ্য দিয়ে যাচ্ছে : mov r0, r0, ror r1
। সুতরাং একটি ঘোরানো কোনও EOR নির্দেশ বা কোনও কিছুর জন্য একটি নিবন্ধ-উত্স অপারেন্ডে ভাঁজ করতে পারে।
নিশ্চিত করুন যে আপনি স্বাক্ষরবিহীন প্রকারের জন্য n
এবং ফেরতের মানটি ব্যবহার করেছেন, অন্যথায় এটি কোনও ঘোরানো হবে না । (x86 টার্গেটের জন্য জিসিসি পাটিগণিতের ডান শিফটগুলি করে, শূন্যের পরিবর্তে সাইন-বিটের অনুলিপিগুলিতে স্থানান্তরিত করে, যখন আপনি OR
দু'টি স্থানান্তরিত মানগুলি এক সাথে করে তখন একটি সমস্যার সৃষ্টি করে negative
এছাড়াও, শিফট গণনাটি একটি স্বাক্ষরবিহীন প্রকারের তা নিশ্চিত করুন , কারণ (-n)&31
একটি স্বাক্ষরিত প্রকারের সাথে কারওর পরিপূরক বা চিহ্ন / মাত্রা হতে পারে, এবং সাইনড বা দু'জনের পরিপূরক সহ আপনি যে মডুলার 2 get n পেয়েছেন তার মতো নয়। (রেগারের ব্লগ পোস্টে মন্তব্যগুলি দেখুন)। unsigned int
আমি যে প্রতিটি সংকলক দেখেছি তার প্রতি প্রস্থের জন্য ভাল করে x
। কিছু অন্যান্য প্রকার আসলে কিছু সংকলকগুলির জন্য আইডিয়াম-স্বীকৃতিটিকে পরাস্ত করে, তাই কেবল একই ধরণের ব্যবহার করবেন না x
।
কিছু সংকলক ঘোরাঘুরির জন্য অন্তর্নিহিত সরবরাহ করে , যা পোর্টেবল সংস্করণটি আপনি যে সংস্থাগুলির লক্ষ্যবস্তু করছেন তা সংযোগকারীটিতে ভাল কোড তৈরি করে না তবে এটি ইনলাইন-এসেমের চেয়ে অনেক ভাল। আমার জানা কোনও সংকলকগুলির জন্য ক্রস-প্ল্যাটফর্ম অন্তর্নিহিত নেই। এগুলি কয়েকটি x86 অপশন:
- ইন্টেল ডকুমেন্টস যা
<immintrin.h>
সরবরাহ করে _rotl
এবং _rotl64
আন্তঃব্যবস্থা করে এবং ডান শিফ্টের জন্য একই। এমএসভিসি প্রয়োজন <intrin.h>
, যখন জিসিসির প্রয়োজন <x86intrin.h>
। একটি #ifdef
জিসিসি বনাম আইসিসি যত্ন নেয়, কিন্তু ঝনঝন, যেকোনো স্থানে সেগুলি প্রদান বলে মনে হচ্ছে না সঙ্গে MSVC উপযুক্ততা মোড ছাড়া-fms-extensions -fms-compatibility -fms-compatibility-version=17.00
। এবং তাদের জন্য যে asmটি বের হয় তা সফল হয় (অতিরিক্ত মাস্কিং এবং একটি সিএমওভি)।
- এমএসভিসি:
_rotr8
এবং_rotr16
।
- জিসিসি এবং আইসিসি (ঝনঝন না):
<x86intrin.h>
এছাড়াও উপলব্ধ __rolb
/ __rorb
জন্য 8 বিট বামদিকে ঘোরান / ডান, __rolw
/ __rorw
(16-বিট), __rold
/ __rord
(32-বিট), __rolq
/ __rorq
(64-বিট, শুধুমাত্র 64-বিট লক্ষ্যমাত্রা জন্য সংজ্ঞায়িত)। সংকীর্ণ ঘোরার জন্য, বাস্তবায়নটি ব্যবহার করে __builtin_ia32_rolhi
বা ...qi
, তবে 32 এবং 64-বিট ঘোরানো শিফট / বা (ইউবির বিরুদ্ধে কোনও সুরক্ষা ছাড়াই) ব্যবহার করে সংজ্ঞায়িত করা হয়েছে, কারণ কেবলমাত্র কোডটি ia32intrin.h
x86 এর জন্য জিসিসি-তে কাজ করতে হবে)। জিএনইউ সি এর কোনও ক্রস-প্ল্যাটফর্মের __builtin_rotate
কাজটি করে না বলে মনে হয় __builtin_popcount
(এটি লক্ষ্য প্ল্যাটফর্মের সর্বোত্তম যা কিছুতে প্রসারিত হয়, এমনকি এটি একক নির্দেশনা না হলেও)। বেশিরভাগ সময় আপনি আইডিয়ম-স্বীকৃতি থেকে ভাল কোড পান।
#if defined(__x86_64__) || defined(__i386__)
#ifdef _MSC_VER
#include <intrin.h>
#else
#include <x86intrin.h> // Not just <immintrin.h> for compilers other than icc
#endif
uint32_t rotl32_x86_intrinsic(rotwidth_t x, unsigned n) {
return _rotl(x, n);
}
#endif
সম্ভবত কিছু নন- x86 সংকলকের অন্তর্ভুক্ত রয়েছে তবে সেগুলি অন্তর্ভুক্ত করার জন্য এই সম্প্রদায়-উইকির উত্তরটি প্রসারিত করা যাক। (সম্ভবত অভ্যন্তরীণ সম্পর্কে বিদ্যমান উত্তরে এটি করুন )।
(এই উত্তরের পুরানো সংস্করণটি এমএসভিসি-নির্দিষ্ট ইনলাইন asm (যা কেবল 32 বিবিট x86 কোডের জন্য কাজ করে) বা সি সংস্করণের জন্য http://www.devx.com/tips/Tip/14043 প্রস্তাব দিয়েছে The মন্তব্যগুলি তার জবাব দিচ্ছে ।)
ইনলাইন asm অনেকগুলি অপ্টিমাইজেশানকে হারায় , বিশেষত এমএসভিসি-স্টাইল কারণ এটি ইনপুটগুলিকে স্টোর / পুনরায় লোড করতে বাধ্য করে । একটি সাবধানে-লিখিত GNU সি ইনলাইন-এসএম আবর্তিত গণনাটিকে সংকলন-সময়-ধ্রুবক শিফট গণনাগুলির জন্য তাত্ক্ষণিকভাবে কাজ করার অনুমতি দেবে, তবে মানটি স্থানান্তরিত করতে যদি এটি একটি সংকলন-সময় ধ্রুবক হয় তবে এটি সম্পূর্ণরূপে অপ্টিমাইজ করতে পারে না ইনলাইনিং পরে। https://gcc.gnu.org/wiki/DontUseInlineAsm ।