Kerugian RISC-V

Awalnya, saya menulis dokumen ini beberapa tahun yang lalu, sebagai insinyur verifikasi inti eksekusi di ARM. Tentu saja, pendapat saya dipengaruhi oleh kerja mendalam dengan core eksekutif dari prosesor yang berbeda. Jadi lakukan untuk diskon, tolong: mungkin saya terlalu kategoris.

Namun, saya masih percaya bahwa pencipta RISC-V dapat melakukan jauh lebih baik. Di sisi lain, jika saya mendesain prosesor 32-bit atau 64-bit hari ini, saya mungkin akan mengimplementasikan arsitektur seperti itu untuk mengambil keuntungan dari alat yang ada.

Artikel awalnya menggambarkan set instruksi RISC-V 2.0. Untuk versi 2.2, itu membuat beberapa pembaruan.

Kata Pengantar Asli: Beberapa Opini Pribadi


Set instruksi RISC-V telah dikurangi menjadi minimum absolut. Banyak perhatian diberikan untuk meminimalkan jumlah instruksi, pengkodean normalisasi, dll. Keinginan untuk minimalis ini telah menyebabkan ortogonalitas palsu (seperti menggunakan kembali instruksi yang sama untuk transisi, panggilan, dan pengembalian) dan verbositas wajib, yang mengembang baik ukuran dan kuantitas instruksi.

Sebagai contoh, ini adalah kode C:

int readidx(int *p, size_t idx) { return p[idx]; } 

Ini adalah kasus sederhana dari pengindeksan array, operasi yang sangat umum. Ini adalah kompilasi untuk x86_64:

 mov eax, [rdi+rsi*4] ret 

atau ARM:

 ldr r0, [r0, r1, lsl #2] bx lr // return 

Namun, untuk RISC-V, diperlukan kode berikut:

 slli a1, a1, 2 add a0, a1, a1 lw a0, a0, 0 jalr r0, r1, 0 // return 

Penyederhanaan RISC-V menyederhanakan dekoder (mis., Front-end CPU) dengan mengeksekusi lebih banyak instruksi. Tetapi penskalaan lebar pipa merupakan masalah yang sulit, sementara decoding sedikit (atau sangat) instruksi tidak teratur dilaksanakan dengan baik (kesulitan utama muncul ketika sulit untuk menentukan panjang instruksi: ini terutama jelas dalam set instruksi x86 dengan banyak awalan).

Penyederhanaan seperangkat instruksi tidak boleh dibatasi. Mendaftar dan mendaftar tambahan dengan pergeseran memori register adalah instruksi sederhana dan sangat umum dalam program, dan sangat mudah bagi prosesor untuk mengimplementasikannya secara efektif. Jika prosesor tidak dapat mengimplementasikan instruksi secara langsung, maka relatif mudah untuk memecahnya menjadi komponen-komponennya; ini adalah masalah yang jauh lebih sederhana daripada menggabungkan urutan operasi sederhana.

Kita harus membedakan antara instruksi spesifik "kompleks" dari prosesor CISC - instruksi yang rumit, jarang digunakan dan tidak efisien - dari instruksi "fungsional" yang umum untuk prosesor CISC dan RISC, yang menggabungkan serangkaian kecil operasi. Yang terakhir sering digunakan dan dengan kinerja tinggi.

Implementasi biasa-biasa saja


  • Ekstensibilitas yang hampir tak terbatas. Meskipun ini adalah tujuan dari RISC-V, itu menciptakan ekosistem yang terfragmentasi dan tidak kompatibel yang harus dikelola dengan sangat hati-hati.
  • Instruksi yang sama ( JALR ) digunakan untuk panggilan, dan untuk pengembalian, dan untuk cabang register-tidak langsung, di mana decoding tambahan diperlukan untuk prediksi cabang
    • Panggil: Rd = R1
    • Kembali: Rd = R0 , Rs = R1
    • Transisi tidak langsung: Rd = R0 , RsR1
    • (Transisi aneh: RdR0 , RdR1 )
  • Pengkodean variabel-panjang dari bidang rekaman tidak menyinkronkan diri sendiri (ini umum - misalnya, masalah yang sama dengan x86 dan Thumb-2 - tetapi ini menyebabkan berbagai masalah dengan implementasi dan keamanan, misalnya, pemrograman berorientasi terbalik, mis. Serangan ROP )
  • RV64I membutuhkan ekstensi karakter untuk semua nilai 32-bit. Ini mengarah pada fakta bahwa bagian atas register 64-bit menjadi tidak mungkin digunakan untuk menyimpan hasil antara, yang mengarah pada penempatan khusus yang tidak perlu dari bagian atas register. Lebih optimal untuk menggunakan ekstensi dengan nol (karena mengurangi jumlah perpindahan dan biasanya dapat dioptimalkan dengan melacak bit "nol" ketika bagian atas diketahui nol)
  • Perkalian adalah opsional. Meskipun blok multiplikasi cepat dapat menempati area yang cukup besar pada kristal kecil, Anda selalu dapat menggunakan sirkuit yang sedikit lebih lambat yang secara aktif menggunakan ALU yang ada untuk beberapa siklus multiplikasi.
  • LR / SC persyaratan perkembangan yang ketat untuk subset aplikasi yang terbatas. Meskipun pembatasan ini cukup ketat, ini berpotensi menciptakan beberapa masalah untuk implementasi kecil (terutama tanpa cache)
    • Ini sepertinya pengganti untuk instruksi CAS, lihat komentar di bawah
  • Memory sticky bits FP dan mode pembulatan berada pada register yang sama. Ini membutuhkan serialisasi saluran FP jika operasi RMW dilakukan untuk mengubah mode pembulatan.
  • Instruksi FP dikodekan untuk presisi 32, 64 dan 128-bit, tetapi tidak 16-bit (yang jauh lebih umum pada perangkat keras daripada 128 bit)
    • Ini dapat dengan mudah diperbaiki: kode dimensi 0b10 gratis.
    • Pembaruan: Penampung desimal muncul di versi 2.2, tetapi tidak ada penampung setengah presisi. Pikiran tidak bisa dipahami.
  • Cara nilai-nilai FP direpresentasikan dalam file register FP tidak didefinisikan, tetapi dapat diamati (via load / store)
    • Penulis emulator akan membencimu
    • Migrasi mesin virtual mungkin menjadi tidak mungkin
    • Pembaruan: versi 2.2 membutuhkan nilai tinju NaN yang lebih luas

Buruk


  • Tidak ada kode kondisi, dan sebagai gantinya pernyataan membandingkan dan cabang digunakan. Ini bukan masalah itu sendiri, tetapi konsekuensinya tidak menyenangkan:
    • Mengurangi ruang pengkodean di cabang bersyarat karena kebutuhan untuk menyandikan satu atau dua penentu register
    • Tidak ada pilihan kondisional (berguna untuk transisi yang sangat tidak terduga)
    • Tidak ada carry-over / pengurangan dengan carry-over atau pinjaman
    • (Perhatikan bahwa ini masih lebih baik daripada set perintah yang menulis flag ke register umum, dan kemudian beralih ke flag yang diterima)
  • Tampaknya penghitung presisi tinggi (siklus perangkat keras) diperlukan dalam ISA yang tidak terjangkau. Dalam praktiknya, menyediakan aplikasi bagi mereka merupakan vektor yang sangat baik untuk serangan pada saluran pihak ketiga
  • Perkalian dan pembagian adalah bagian dari ekstensi yang sama, dan tampaknya jika satu diterapkan, maka yang lain juga harus. Perkalian jauh lebih sederhana daripada pembagian, dan umum pada kebanyakan prosesor, tetapi pembagian tidak.
  • Tidak ada instruksi atom dalam arsitektur set instruksi dasar. Mikrokontroler multi-inti menjadi lebih umum, sehingga instruksi atom seperti LL / SC tidak mahal (untuk implementasi minimal dalam satu prosesor [multi-inti] tunggal, hanya diperlukan 1 bit status prosesor)
  • LR / SC berada dalam ekstensi yang sama dengan instruksi atom yang lebih kompleks, yang membatasi fleksibilitas untuk implementasi kecil
  • Instruksi atom umum (bukan LR / SC ) tidak termasuk CAS primitif
    • CmpHi:CmpLo untuk menghindari perlunya instruksi yang bertuliskan lima register ( Addr , CmpHi:CmpLo , SwapHi:SwapLo ), tetapi ini kemungkinan akan mengenakan biaya implementasi yang lebih sedikit daripada LR / SC forward forward yang dijamin, yang disediakan sebagai penggantian
  • Instruksi atom ditawarkan yang bekerja pada nilai 32-bit dan 64-bit, tetapi tidak pada yang 8-bit atau 16-bit
  • Untuk RV32I, tidak ada cara untuk mentransfer nilai DP FP antara integer dan file register FP, kecuali melalui memori, yaitu, dari register integer 32-bit tidak mungkin untuk membuat angka floating-point presisi ganda 64-bit, Anda harus terlebih dahulu menulis nilai perantara ke memori dan memuat dia ke dalam file register dari sana
  • Sebagai contoh, instruksi ADD 32-bit di RV32I dan ADD 64-bit di RVI64 memiliki pengkodean yang sama, dan di RVI64 ADD ADD lain ADD.W . Ini adalah komplikasi yang tidak perlu untuk prosesor yang mengimplementasikan kedua instruksi - akan lebih baik untuk menambahkan pengkodean 64-bit baru sebagai gantinya.
  • Tidak ada instruksi MOV . Kode mnemonik dari perintah MV diterjemahkan oleh assembler ke dalam instruksi MV rD, rS -> ADDI rD, rS, 0 . Prosesor berkinerja tinggi biasanya mengoptimalkan instruksi MOV , sambil menyusun ulang instruksi secara ekstensif. Instruksi dengan operan 12-bit langsung dipilih sebagai bentuk kanonik dari instruksi MV di RISC-V.
    • Dengan tidak adanya MOV instruksi ADD rD, rS, r0 sebenarnya menjadi lebih disukai daripada MOV kanonik, karena lebih mudah untuk memecahkan kode, dan operasi dengan nol register (r0) dalam CPU biasanya dioptimalkan

Mengerikan


  • JAL menghabiskan 5 bit pada pengkodean register komunikasi, yang selalu sama dengan R1 (atau R0 untuk transisi)
    • Ini berarti bahwa RV32I menggunakan perpindahan cabang 21-bit. Ini tidak cukup untuk aplikasi besar - misalnya, browser web - tanpa menggunakan beberapa urutan perintah dan / atau "pulau-pulau cabang"
    • Ini adalah kerusakan dibandingkan dengan versi 1.0 dari arsitektur perintah!
  • Meskipun upaya besar untuk menyandikan secara seragam, instruksi memuat / menyimpan disandikan secara berbeda (case dan bidang langsung berubah)
    • Tampaknya, ortogonalitas pengkodean dari register keluaran lebih disukai daripada ortogonalitas pengkodean dari dua instruksi yang sangat terkait. Pilihan ini tampaknya agak aneh mengingat bahwa pembuatan alamat lebih kritis waktu.
  • Tidak ada instruksi pemuatan memori dengan register offset ( Rbase + Roffset ) atau indeks ( Rbase + Rindex << Scale ).
  • FENCE.I menyiratkan sinkronisasi lengkap dari cache instruksi dengan semua repositori sebelumnya, dengan atau tanpa pagar. Implementasi perlu menghapus semua I $ pada pagar, atau mencari D $ dan buffer penyimpanan
  • Dalam RV32I, membaca penghitung 64-bit membutuhkan membaca bagian atas dua kali, membandingkan dan bercabang dalam hal mentransfer antara bagian bawah dan atas selama operasi membaca
    • Biasanya, ISA 32-bit menyertakan instruksi register pasangan khusus baca untuk menghindari masalah ini.
  • Tidak ada ruang yang ditentukan secara arsitektur untuk petunjuk kode, sehingga instruksi dari ruang ini tidak menyebabkan kesalahan pada prosesor yang lebih lama (diproses sebagai NOP ), tetapi melakukan sesuatu pada CPU yang paling modern
    • Contoh khas petunjuk NOP murni adalah hal-hal seperti hasil spinlock
    • Prosesor yang lebih baru juga memiliki petunjuk yang lebih canggih (dengan efek samping yang terlihat pada prosesor yang lebih baru; misalnya, petunjuk pemeriksaan perbatasan x86 dikodekan dalam ruang petunjuk sehingga biner tetap kompatibel ke belakang)

Source: https://habr.com/ru/post/id461785/


All Articles