Bu kritik bölümlerle ilgili birkaç potansiyel sorun görüyorum. Bunların hepsine uyarılar ve çözümler var, ancak bir özet olarak:
- Optimizasyon veya rastgele başka nedenlerle derleyicinin bu makrolar arasında kod taşımasını engelleyen hiçbir şey yoktur.
- İşlemcinin bazı bölümlerini kaydeder ve geri yüklerler, derleyici satır içi düzeneğin yalnız bırakılmasını bekler (aksi belirtilmedikçe).
- Sekansın ortasında bir kesmenin meydana gelmesini engelleyen ve okunduğu zaman ve yazıldığı zaman arasındaki durumu değiştiren hiçbir şey yoktur.
Öncelikle, derleyici bellek engellerine kesinlikle ihtiyacınız var . GCC bunları clobbers olarak uygular . Temel olarak, bu derleyiciye "Hayır, bellek erişimlerini bu satır içi montaj parçası boyunca taşıyamazsınız çünkü bellek erişimlerinin sonucunu etkileyebilir." Özellikle, hem başlangıç hem de bitiş makrolarında hem "memory"
ve hem de "cc"
clobber'lara ihtiyacınız vardır . Bunlar, diğer işlevlerin (işlev çağrıları gibi) satır içi derlemeye göre yeniden sıralanmasını da önler, çünkü derleyici bellek erişimine sahip olabileceğini bilir. "memory"
Clobbers ile satır içi montaj genelinde koşul kodu kayıtları ARM tutma durumu için GCC gördüm , bu yüzden kesinlikle clobber gerekir "cc"
.
İkincisi, bu kritik bölümler, kesintilerin etkin olup olmadığından çok daha fazlasını kaydeder ve geri yükler. Özellikle, CPSR'nin (Mevcut Program Durum Kaydı) çoğunu kaydediyor ve geri yüklüyorlar ( A9 için güzel bir diyagram bulamadığım için bağlantı Cortex-R4 içindir, ancak aynı olmalıdır). Hangi devlet parçalarının gerçekten değiştirilebileceği konusunda ince kısıtlamalar vardır , ancak burada gereğinden fazladır.
Diğer şeylerin yanı sıra, bu durum kodlarını içerir (gibi talimatların sonuçları cmp
saklanır, böylece sonraki koşullu talimatlar sonuca etki edebilir). Derleyici kesinlikle bununla karıştırılacaktır. Bu, "cc"
yukarıda belirtildiği gibi klozet kullanılarak kolayca çözülebilir . Ancak, bu her seferinde kodun başarısız olmasını sağlayacaktır, bu yüzden problemlerle karşılaştığınız gibi görünmüyor. Yine de bir saatli bomba, rastgele diğer kodu değiştirmek derleyicinin biraz farklı bir şey yapmasına neden olabilir, bu da kırılacak.
Bu aynı zamanda Thumb koşullu yürütmeyi uygulamak için kullanılan BT bitlerini kaydetmeye / geri yüklemeye çalışacaktır . Başparmak kodunu asla yürütmezseniz bunun önemli olmadığını unutmayın. GCC'nin satır içi derlemesinin, BT bitleriyle nasıl başa çıktığını, hiç bitmediği, nasıl çözdüğünü hiç anlamadım, yani derleyici hiçbir zaman bir BT bloğuna satır içi derleme yerleştirmemeli ve her zaman derlemenin bir BT bloğunun dışında bitmesini beklemelidir. GCC'nin bu varsayımları ihlal eden kod oluşturduğunu hiç görmedim ve ağır optimizasyonla oldukça karmaşık bir satır içi montaj yaptım, bu yüzden makul bir şekilde tuttuklarından eminim. Bu, muhtemelen BT bitlerini değiştirmeye çalışmadığı anlamına gelir, bu durumda her şey yolundadır. Bu bitleri değiştirmeye çalışmak "mimari olarak öngörülemez" olarak sınıflandırılır, bu yüzden her türlü kötü şeyi yapabilir, ama muhtemelen hiçbir şey yapmaz.
Kaydedilecek / geri yüklenecek son bit kategorisi (gerçekten kesintileri devre dışı bırakacak olanların yanı sıra) mod bitleridir. Bunlar muhtemelen değişmeyecektir, bu yüzden muhtemelen önemli değildir, ancak kasıtlı olarak modları değiştiren herhangi bir kodunuz varsa, bu kesme bölümleri sorunlara neden olabilir. Bunu yapmayı bekleyebileceğim tek ayrıcalık ve kullanıcı modu arasında geçiş yapmak.
Üçüncü olarak, aralarında CPSR diğer kısımlarını değiştirmesini bir kesme engelleyen bir şey yok MRS
ve MSR
de ARM_INT_LOCK
. Bu tür değişikliklerin üzerine yazılabilir. Çoğu makul sistemde, eşzamansız kesmeler, kesildikleri kodun durumunu değiştirmez (CPSR dahil). Eğer yaparlarsa, kodun ne yapacağını anlamak çok zorlaşır. Ancak, mümkündür (FIQ devre dışı bitini değiştirmek benim için büyük olasılıktır), bu nedenle sisteminizin bunu yapıp yapmadığını göz önünde bulundurmalısınız.
Bunları, işaret ettiğim tüm potansiyel sorunlara hitap edecek şekilde nasıl uygulayacağım:
#define ARM_INT_KEY_TYPE unsigned int
#define ARM_INT_LOCK(key_) \
asm volatile(\
"mrs %[key], cpsr\n\t"\
"ands %[key], %[key], #0xC0\n\t"\
"cpsid if\n\t" : [key]"=r"(key_) :: "memory", "cc" );
#define ARM_INT_UNLOCK(key_) asm volatile (\
"tst %[key], #0x40\n\t"\
"beq 0f\n\t"\
"cpsie f\n\t"\
"0: tst %[key], #0x80\n\t"\
"beq 1f\n\t"\
"cpsie i\n\t"
"1:\n\t" :: [key]"r" (key_) : "memory", "cc")
İle derlemeye emin olun -mcpu=cortex-a9
desteklemeyen eski bir ARM CPU çünkü en azından bazı GCC sürümleri (benim gibi) varsayılan cpsie
ve cpsid
.
Kullandığım ands
yerine sadece and
içinde ARM_INT_LOCK
bu Başparmak kodunda kullanıldığı takdirde 16 bit talimat yüzden. "cc"
Bu kesinlikle bir performans / kod boyutu fayda yüzden clobber neyse gereklidir.
0
ve 1
olduğu yerel etiketler referans için.
Bunlar, sürümlerinizle aynı şekilde kullanılabilir olmalıdır. Orijinali ARM_INT_LOCK
kadar hızlı / küçük. Ne yazık ki, ARM_INT_UNLOCK
birkaç talimatın yakınında herhangi bir yerde güvenle yapmanın bir yolunu bulamadım .
Sisteminizde IRQ'ların ve FIQ'ların devre dışı bırakılmasıyla ilgili kısıtlamalar varsa, bu basitleştirilebilir. Örneğin, her zaman birlikte devre dışı bırakılmışlarsa, aşağıdakine benzer bir cbz
+ olarak birleştirebilirsiniz cpsie if
:
#define ARM_INT_UNLOCK(key_) asm volatile (\
"cbz %[key], 0f\n\t"\
"cpsie if\n\t"\
"0:\n\t" :: [key]"r" (key_) : "memory", "cc")
Alternatif olarak, FIQ'ları hiç umursamıyorsanız, onları tamamen etkinleştirmeyi / devre dışı bırakmayı bırakmaya benzer.
Başka hiçbir şeyin biliyorsanız hiç o zaman da kullanım hem haricinde orijinal koduna çok benzer bir şey ile devam edebilir, kilit ve kilidini arasındaki CPSR diğer devlet bit herhangi değiştirir "memory"
ve "cc"
clobbers hem de ARM_INT_LOCK
veARM_INT_UNLOCK