Говнокод по пятницам. Эпизод 4. «Duff's device»

Вспоминается: Цвет - любой при условии, что покупатель выбирает черный.

Занудствовать, так занудствовать )))

Это высказывание принадлежит Генри Форду.

Он говорил: «Цвет автомобиля может быть любым, при условии, что он чёрный».

2 лайка

Историки не нашли подтверждения, что он это говорил

Спорить о том, какой из вариантов придуманной фразы правильный, довольно -таки странно.

Кажется у нас новый зануда )))

3 лайка

Еще раз глянул на этот говнокод. Да, говнокод, безусловно, уникальный.
Но во что он скомпилируется? Особенно switch - сase.
В кучу сравнений и условных переходов или компилятор там сумеет применить IJMP ?

Посмотрите и нам расскажете. Не забудьте только сказать о каком процессоре речь.

@ЕвгенийП, а мне всегда фишки с кейсами нравились. Типа,

strcpy(to, from, count) {
  register char *to, *from;
  register count;
  register n = (count + 7) / 8;
  
  if (!count)
    return;
  switch (count % 8) {
    case 0: do { *to++ = *from++;
    case 7:      *to++ = *from++;
    case 6:      *to++ = *from++;
    case 5:      *to++ = *from++;
    case 4:      *to++ = *from++;
    case 3:      *to++ = *from++;
    case 2:      *to++ = *from++;
    case 1:      *to++ = *from++;
               } while (--n);
  }
}

Ну, чем плохо то?
И как тут форматировать?!)

8H7pA5o

const int bufferSize = 253;//задать тут
byte myBuffer[bufferSize];

void setup() { Serial.begin(9600); }

void loop() {
if (bufferSize % 8 == 0) { Serial.println("1");
} else { Serial.println("2"); }
}// вооо, может будет полезно...

Так это ж ровно то же самое, что у меня в статье. Вы привели оригинал, а я немного под ардуино-пример поменял, но идея и основной приём остались неизменными.

1 лайк

Друзья, для несведующего, это что за тип такой?

Жаль. Жаль что не слежу за говнокодом. Да и не понимаю уже особо.(

Это подсказка компилятору, что переменная будет интенсивно использоваться и что вы рекомендуете по возможности хранить ее в регистре процессора.

Большинство современных компиляторов делают это автоматически, и у них это получается лучше, чем у нас, людей.

1 лайк

@ЕвгенийП, у вас талант.) У меня так не получится.

Ну, по сути Вам уже сказали - это int с просьбой разместить в регистре.

А так – забудьте, в С++ он устарел, считайте, что его уже (и пока) нет. В последний раз он фигурировал в стандарте 14-го года. Уже в стандарте 17-го (и 20-го тоже) сказано:

The export and register keywords are unused but are reserved for future use.

Впрочем, в кошерном Си это понятие по-прежнему присутствует. У них есть очевидные ограничения – нельзя брать их адрес, нельзя использовать под sizeof. Массивы опять же … поведение не определено.

1 лайк

atmega328p
В loop добавил несколько вызовов функции duffsDevice, чтобы компилятор не занимался самодеятельностью и оформил ее в виде именно отдельной подпрограммы.

  duffsDevice(buffer,3);
  duffsDevice(buffer,bufLength-50);
  duffsDevice(buffer,bufLength);

Оказалось, что да, компилятор использует ijmp, но также оказалось что компилятор генерит такой говнокод, который всем говнокодам говнокод.

void duffsDevice(const uint8_t *from, size_t count) {
  90:	cf 93       	push	r28
  92:	df 93       	push	r29
C:\ARDUINO\testxxx/testxxx.ino:19

  size_t n = (count + 7) / 8;
  94:	9c 01       	movw	r18, r24
  96:	29 5f       	subi	r18, 0xF9	; 249
  98:	3f 4f       	sbci	r19, 0xFF	; 255
  9a:	43 e0       	ldi	r20, 0x03	; 3
  9c:	36 95       	lsr	r19
  9e:	27 95       	ror	r18
  a0:	4a 95       	dec	r20
  a2:	e1 f7       	brne	.-8      	; 0x9c <duffsDevice(unsigned char const*, unsigned int) [clone .constprop.0]+0xc>
C:\ARDUINO\testxxx/testxxx.ino:20
  switch (count % 8){
  a4:	fc 01       	movw	r30, r24
  a6:	e7 70       	andi	r30, 0x07	; 7
  a8:	ff 27       	eor	r31, r31
  aa:	31 97       	sbiw	r30, 0x01	; 1
  ac:	e7 30       	cpi	r30, 0x07	; 7
  ae:	f1 05       	cpc	r31, r1
  b0:	58 f4       	brcc	.+22     	; 0xc8 <duffsDevice(unsigned char const*, unsigned int) [clone .constprop.0]+0x38>
  b2:	e3 5a       	subi	r30, 0xA3	; 163
  b4:	ff 4f       	sbci	r31, 0xFF	; 255
  b6:	0c 94 42 01 	jmp	0x284	; 0x284 <__tablejump2__>
  ba:	9f 00       	.word	0x009f	; ????
  bc:	9c 00       	.word	0x009c	; ????
  be:	99 00       	.word	0x0099	; ????
  c0:	96 00       	.word	0x0096	; ????
  c2:	93 00       	.word	0x0093	; ????
  c4:	90 00       	.word	0x0090	; ????
  c6:	8d 00       	.word	0x008d	; ????
  c8:	e9 e0       	ldi	r30, 0x09	; 9
  ca:	f1 e0       	ldi	r31, 0x01	; 1
C:\ARDUINO\testxxx/testxxx.ino:23
    case 0: 
      do {
        PORTC = *from++;
  cc:	ef 01       	movw	r28, r30
  ce:	21 96       	adiw	r28, 0x01	; 1
  d0:	80 81       	ld	r24, Z
  d2:	88 b9       	out	0x08, r24	; 8
C:\ARDUINO\testxxx/testxxx.ino:24
        case 7: PORTC = *from++;
  d4:	de 01       	movw	r26, r28
  d6:	11 96       	adiw	r26, 0x01	; 1
  d8:	88 81       	ld	r24, Y
  da:	88 b9       	out	0x08, r24	; 8
C:\ARDUINO\testxxx/testxxx.ino:25
        case 6: PORTC = *from++;
  dc:	fd 01       	movw	r30, r26
  de:	31 96       	adiw	r30, 0x01	; 1
  e0:	8c 91       	ld	r24, X
  e2:	88 b9       	out	0x08, r24	; 8
C:\ARDUINO\testxxx/testxxx.ino:26
        case 5: PORTC = *from++;
  e4:	df 01       	movw	r26, r30
  e6:	11 96       	adiw	r26, 0x01	; 1
  e8:	80 81       	ld	r24, Z
  ea:	88 b9       	out	0x08, r24	; 8
C:\ARDUINO\testxxx/testxxx.ino:27
        case 4: PORTC = *from++;
  ec:	fd 01       	movw	r30, r26
  ee:	31 96       	adiw	r30, 0x01	; 1
  f0:	8c 91       	ld	r24, X
  f2:	88 b9       	out	0x08, r24	; 8
C:\ARDUINO\testxxx/testxxx.ino:28
        case 3: PORTC = *from++;
  f4:	df 01       	movw	r26, r30
  f6:	11 96       	adiw	r26, 0x01	; 1
  f8:	80 81       	ld	r24, Z
  fa:	88 b9       	out	0x08, r24	; 8
C:\ARDUINO\testxxx/testxxx.ino:29
        case 2: PORTC = *from++;
  fc:	fd 01       	movw	r30, r26
  fe:	31 96       	adiw	r30, 0x01	; 1
 100:	8c 91       	ld	r24, X
 102:	88 b9       	out	0x08, r24	; 8
C:\ARDUINO\testxxx/testxxx.ino:30
        case 1: PORTC = *from++;
 104:	80 81       	ld	r24, Z
 106:	88 b9       	out	0x08, r24	; 8
C:\ARDUINO\testxxx/testxxx.ino:31
      } while(--n > 0);
 108:	21 50       	subi	r18, 0x01	; 1
 10a:	31 09       	sbc	r19, r1
C:\ARDUINO\testxxx/testxxx.ino:30
        case 6: PORTC = *from++;
        case 5: PORTC = *from++;
        case 4: PORTC = *from++;
        case 3: PORTC = *from++;
        case 2: PORTC = *from++;
        case 1: PORTC = *from++;
 10c:	31 96       	adiw	r30, 0x01	; 1
C:\ARDUINO\testxxx/testxxx.ino:31
      } while(--n > 0);
 10e:	21 15       	cp	r18, r1
 110:	31 05       	cpc	r19, r1
 112:	e1 f6       	brne	.-72     	; 0xcc <duffsDevice(unsigned char const*, unsigned int) [clone .constprop.0]+0x3c>
C:\ARDUINO\testxxx/testxxx.ino:33
  }
}
 114:	df 91       	pop	r29
 116:	cf 91       	pop	r28
 118:	08 95       	ret
C:\ARDUINO\testxxx/testxxx.ino:20


void duffsDevice(const uint8_t *from, size_t count) {

  size_t n = (count + 7) / 8;
  switch (count % 8){
 11a:	c9 e0       	ldi	r28, 0x09	; 9
 11c:	d1 e0       	ldi	r29, 0x01	; 1
 11e:	da cf       	rjmp	.-76     	; 0xd4 <duffsDevice(unsigned char const*, unsigned int) [clone .constprop.0]+0x44>
 120:	a9 e0       	ldi	r26, 0x09	; 9
 122:	b1 e0       	ldi	r27, 0x01	; 1
 124:	db cf       	rjmp	.-74     	; 0xdc <duffsDevice(unsigned char const*, unsigned int) [clone .constprop.0]+0x4c>
 126:	e9 e0       	ldi	r30, 0x09	; 9
 128:	f1 e0       	ldi	r31, 0x01	; 1
 12a:	dc cf       	rjmp	.-72     	; 0xe4 <duffsDevice(unsigned char const*, unsigned int) [clone .constprop.0]+0x54>
 12c:	a9 e0       	ldi	r26, 0x09	; 9
 12e:	b1 e0       	ldi	r27, 0x01	; 1
 130:	dd cf       	rjmp	.-70     	; 0xec <duffsDevice(unsigned char const*, unsigned int) [clone .constprop.0]+0x5c>
 132:	e9 e0       	ldi	r30, 0x09	; 9
 134:	f1 e0       	ldi	r31, 0x01	; 1
 136:	de cf       	rjmp	.-68     	; 0xf4 <duffsDevice(unsigned char const*, unsigned int) [clone .constprop.0]+0x64>
 138:	a9 e0       	ldi	r26, 0x09	; 9
 13a:	b1 e0       	ldi	r27, 0x01	; 1
 13c:	df cf       	rjmp	.-66     	; 0xfc <duffsDevice(unsigned char const*, unsigned int) [clone .constprop.0]+0x6c>
 13e:	e9 e0       	ldi	r30, 0x09	; 9
 140:	f1 e0       	ldi	r31, 0x01	; 1
 142:	e0 cf       	rjmp	.-64     	; 0x104 <duffsDevice(unsigned char const*, unsigned int) [clone .constprop.0]+0x74>

...

__tablejump2__():
 284:	ee 0f       	add	r30, r30
 286:	ff 1f       	adc	r31, r31
 288:	05 90       	lpm	r0, Z+
 28a:	f4 91       	lpm	r31, Z
 28c:	e0 2d       	mov	r30, r0
 28e:	09 94       	ijmp

Полный с++ код бы ещё, а то так трудно.

А там ничего особенного нет:

// 
constexpr size_t bufLength = 1024;
static uint8_t buffer[bufLength]; 

void setup() {

}


void loop() {
  duffsDevice(buffer,3);
  duffsDevice(buffer,bufLength-50);
  duffsDevice(buffer,bufLength);
}


void duffsDevice(const uint8_t *from, size_t count) {

  size_t n = (count + 7) / 8;
  switch (count % 8){
    case 0: 
      do {
        PORTC = *from++;
        case 7: PORTC = *from++;
        case 6: PORTC = *from++;
        case 5: PORTC = *from++;
        case 4: PORTC = *from++;
        case 3: PORTC = *from++;
        case 2: PORTC = *from++;
        case 1: PORTC = *from++;
      } while(--n > 0);
  }
}