8-bit MCU'larda C Tamsayı Tanıtımı


14

Örnek olarak avr-gcc kullanıldığında int türlerinin 16 bit genişliğinde olduğu belirtilir. C'de 8 bit işlenenlerde işlem yapılması, C'de tamsayı yükselmesi nedeniyle işlenenlerin 16 bit int türlerine dönüştürülmesiyle sonuçlanır. Bu, bir AVR'deki 8 bitlik aritmetik işlemlerin tümünün, C C'nin tamsayı tanıtımı nedeniyle mecliste yazılmışsa?


1
Ben öyle düşünmüyorum, derleyici hedef değişkenin (imzasız) bir char olduğunu fark edecektir, bu nedenle ilk 8 bit hesaplamak rahatsız olmaz. Yine de, GCC'nin bazen kodu optimize etmede çok iyi olmadığını gördüm, bu nedenle ASM'de kod yazarsanız, sonuç MGIHT daha hızlı olur. Bununla birlikte, çok güçlü bir bütçe kısıtlaması ile çok zaman açısından kritik görevler / incelemeler yapmıyorsanız, ya daha güçlü bir işlemci seçmeli ve C'de programlamalısınız ya da daha düşük performans hakkında endişelenmeyin (bunun yerine zamanı düşünün) piyasaya daha iyi kod okunabilirliği / yeniden kullanılabilirliği, daha az hata, ecc ..).
sonraki hack

Kontrol etmek için zamanım olmadığı için özür dilerim. Ancak, gcc'ye 'tamsayı yükseltmeyi' kontrol edecek bir komut satırı bayrağı olduğunu düşünüyorum. Belli kod parçaları için kontrol etmek için bir pragma bile olabilir. Performans ne kadar kritik? Bir AVR'nin birçok kullanımında, bazı aritmetik için hız farkı bir sorun değildir. Foxus önce kodun düzgün çalışmasını sağladı. Sonra bir performans sorunu varsa ne olduğunu öğrenin. Montajcıda zaman kodlamasını boşa harcamak kolay olurdu, sadece yo farketmediniz.
gbulmer

1
sadece sökün ve derleyicinin ne yaptığını görün. Saf dil açısından evet. buradaki uygulama atipiktir. int kendini kayıt boyutuyla hizalamaya çalışır ve 16 bit kayıtlarınız varsa 8 bit matematik aslında 8 bit 16 bitte daha ucuzdur. Ama bu tam tersi ve 8 bit mcu ile int 16 bit olarak. Bu yüzden muhtemelen bunu önemsediğiniz yerde uchar kullanmalısınız, ancak sizi her yerde en çok acı verdiği için ortak bir programlama alışkanlığı yapmayın.
old_timer

3
Unutmayın: Yorumlardaki soruları cevaplamaktan kaçının.
boru

4
Bu tür sorular SO'daki C uzmanlarına sormak daha iyidir, çünkü saf bir yazılım sorusudur. C'de tamsayı tanıtımı biraz karmaşık bir konudur - ortalama C programcısı bu konuda birçok yanlış anlama sahip olacaktır.
Lundin

Yanıtlar:


16

Uzun lafın kısası:

16 bite tamsayı yükseltmesi her zaman gerçekleşir - C standardı bunu zorlar. Ama derleyici 8 bite hesaplama geri aşağı optimize izin verilir, (gömülü sistemler derleyiciler genellikle optimizasyonlar de oldukça iyi) eğer o tip terfi olsaydı o olurdu olarak işaret aynı olacağını çıkarabiliriz.

Bu her zaman böyle değildir! Tamsayı yükseltmeden kaynaklanan dolaylı imza değişiklikleri, gömülü sistemlerde yaygın bir hata kaynağıdır.

Ayrıntılı açıklama burada bulunabilir: Örtük tür tanıtım kuralları .


8
unsigned int fun1 ( unsigned int a, unsigned int b )
{
    return(a+b);
}

unsigned char fun2 ( unsigned int a, unsigned int b )
{
    return(a+b);
}

unsigned int fun3 ( unsigned char a, unsigned char b )
{
    return(a+b);
}

unsigned char fun4 ( unsigned char a, unsigned char b )
{
    return(a+b);
}

beklendiği gibi fun1 tüm ints olduğu gibi 16 bit matematik

00000000 <fun1>:
   0:   86 0f           add r24, r22
   2:   97 1f           adc r25, r23
   4:   08 95           ret

Teknik olarak yanlış olmasına rağmen, kod tarafından çağrılan 16 bitlik bir eklenti olsa da, bu derleyicinin optimize edilmemesi bile sonuç boyutu nedeniyle adc'yi kaldırdı.

00000006 <fun2>:
   6:   86 0f           add r24, r22
   8:   08 95           ret

Burada gerçekten şaşırmadım, derleyiciler bunu yapmazdı, bu sürümün başlamasına neden olan bu emin değilim, kariyerimin başlarında bu işe girdi ve derleyiciler sıra dışı tanıtım yapmasına rağmen (tıpkı yukarıdaki gibi), tanıtım yapıyorum. söyledi uchar matematik yapmak, sürpriz değil.

0000000a <fun3>:
   a:   70 e0           ldi r23, 0x00   ; 0
   c:   26 2f           mov r18, r22
   e:   37 2f           mov r19, r23
  10:   28 0f           add r18, r24
  12:   31 1d           adc r19, r1
  14:   82 2f           mov r24, r18
  16:   93 2f           mov r25, r19
  18:   08 95           ret

ve ideal, 8 bit olduğunu biliyorum, 8 bit sonuç istiyorum, bu yüzden sonuna kadar 8 bit yapmasını söyledim.

0000001a <fun4>:
  1a:   86 0f           add r24, r22
  1c:   08 95           ret

Bu yüzden genel olarak, ideal bir (u) int büyüklüğü olan kayıt boyutunu hedeflemek daha iyidir, bunun gibi 8 bitlik bir mcu için derleyici yazarları bir uzlaşma yapmak zorundaydı ... Nokta olmak alışkanlık yapma bu kodu taşıdığınızda veya daha büyük kayıtlara sahip bir işlemcide böyle yeni bir kod yazarken, derleyicinin maskelemeye başlaması ve genişlemesi imzalaması gerektiği gibi bildiğiniz matematik için uchar'ı kullanmak 8 bitten fazlasına ihtiyaç duymaz, ve diğerleri değil.

00000000 <fun1>:
   0:   e0800001    add r0, r0, r1
   4:   e12fff1e    bx  lr

00000008 <fun2>:
   8:   e0800001    add r0, r0, r1
   c:   e20000ff    and r0, r0, #255    ; 0xff
  10:   e12fff1e    bx  lr

8 bit daha fazla maliyet zorlama. Biraz / lot aldattım, daha adil bir şekilde görmek için biraz daha karmaşık örneklere ihtiyacım olacaktı.

Yorum tartışmasına dayalı EDIT

unsigned int fun ( unsigned char a, unsigned char b )
{
    unsigned int c;
    c = (a<<8)|b;
    return(c);
}

00000000 <fun>:
   0:   70 e0           ldi r23, 0x00   ; 0
   2:   26 2f           mov r18, r22
   4:   37 2f           mov r19, r23
   6:   38 2b           or  r19, r24
   8:   82 2f           mov r24, r18
   a:   93 2f           mov r25, r19
   c:   08 95           ret

00000000 <fun>:
   0:   e1810400    orr r0, r1, r0, lsl #8
   4:   e12fff1e    bx  lr

sürpriz değil. Optimizer neden bu ekstra talimatı bıraksa da, r19'da ldi'yi kullanamazsınız? (Ben sorduğumda cevabı biliyordum).

EDIT2

avr için

avr-gcc --version
avr-gcc (GCC) 4.9.2
Copyright (C) 2014 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

Kötü alışkanlığı önlemek veya 8 bit karşılaştırmasını önlemek için

arm-none-eabi-gcc --version
arm-none-eabi-gcc (GCC) 7.2.0
Copyright (C) 2017 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

açıkça optimizasyon sadece benim çıktı ile karşılaştırmak için kendi derleyici ile denemek için bir saniye sürer, ama yine de:

whatever-gcc -O2 -c so.c -o so.o
whatever-objdump -D so.o

Ve evet, bayt büyüklüğünde değişkenler için bayt kullanmak, kesinlikle bir avr, pic, vb. mümkün olduğunca kayıtlarda bellekte olacak, bu yüzden flaş tasarrufları ekstra değişkenler olmadan gelir, koç tasarrufları gerçek olabilir veya olmayabilir.


2
"derleyiciler bunu yapmaya başlamadı, hangi sürümün bu başlangıcını gerçekleştirdiğinden emin değildim, kariyerimin başlarında bu işe girdim ve derleyiciler sıra dışı tanıtım yapmasına rağmen (tıpkı yukarıdaki gibi), uchar math yapmasını söyledim bile promosyonu yaptım, şaşırmadım. " Gömülü sistemler C derleyicileri derleyici genellikle optimize izin verilir :) korkunç standart uygunluğunu olması kullanıldığından, ama burada sonuç bir in uyacak tenzil edemez unsigned charo kadar vardır 16 bite terfi gerçekleştirmek için gerekli olan, standart.
Lundin

1
@old_timer (a<<8)|b, int16 bit olan herhangi bir sistem için her zaman yanlıştır . imzalı olarak atanıtılacaktır int. aMSB'de bir değer tutulması durumunda , bu verileri tanımlanmamış davranışı başlatan 16 bitlik sayının işaret bitine kaydırırsınız.
Lundin

1
fun3 fun..ny ... derleyici tarafından tamamen optimize edilmemiştir ... r1'in GCC'de her zaman 0 olduğu ve a, b ve sonuç değişkenleri için ra, rb, {rh, rl} belirten, derleyici şunları yapabilirdi: 1) mov rh, r1; 2) mov rl, ra; 2) rl, rb ekleyin; 3) adc rh, rh; 4) ret. 4 Talimatlar, vs 7 veya 8 ... Talimat 1, ldi rh, 0 olarak değiştirilebilir.
sonraki hack

1
Bu, derleyiciyi ve kullanımdaki ilgili seçenekleri belirtmişse daha iyi bir yanıt olacaktır.
Russell Borogove

1
İnt / char vb. Kullanmaktan kaçınmak ve bunun yerine çok daha açık ve okunabilir int16_t ve int8_t kullanmak iyi bir fikirdir.
kullanıcı

7

Mutlaka değil, çünkü modern derleyiciler üretilen kodu optimize etmekte iyi bir iş çıkarıyor. Örneğin, z = x + y;tüm değişkenlerin olduğu yerleri yazarsanız , hesaplamaları gerçekleştirmeden önce unsigned charderleyicinin bu değişkenleri tanıtması gerekir unsigned int. Ancak, sonuç sonuç tanıtım olmadan tamamen aynı olacağından, derleyici sadece 8 bit değişkenler ekleyen bir kod üretecektir.

Tabii ki, bu her zaman böyle değildir, örneğin sonucu z = (x + y)/2;üst bayta bağlı olacaktır, bu nedenle tanıtım gerçekleşecektir. Ara sonucu tekrar dökerek montaja başvurmadan yine de önlenebilir unsigned char.

Bu tür verimsizliklerin bazıları derleyici seçenekleri kullanılarak önlenebilir. Örneğin, 8 bitlik bir çok derleyicide numaralandırma türlerini intC'nin gerektirdiği şekilde 1 bayta sığdırmak için bir pragma veya komut satırı anahtarı bulunur .


4
msgstr "derleyicinin imzasız int'e tanıtması gerekiyor". Hayır, derleyicinin bunları tanıtması gerekir int, çünkü charbüyük olasılıkla intherhangi bir platformdakiyle aynı dönüşüm derecesine sahip olmayacaktır .
Lundin

3
"Örneğin, birçok 8 bit derleyicide numaralandırma türlerini C'nin gerektirdiği int yerine 1 bayt sığdırmak için bir pragma veya komut satırı anahtarı bulunur." C standardı, numaralandırma değişkenlerinin 1 baytta tahsis edilmesine izin verir. Sadece numaralandırma sabitlerinin olması gerekir int(evet tutarsızdır). C11 6.7.2.2Each enumerated type shall be compatible with char, a signed integer type, or an unsigned integer type. The choice of type is implementation-defined...
Lundin
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.