Ник ODell имеет хорошее решение, но компилятор не может знать, что во всех ваших случаях будет ровно один байт кода.Это не будет известно, пока ассемблер не пройдет.Таким образом, компилятор должен генерировать что-то, что будет работать независимо от того, сколько кода было создано в каждом из ваших случаев, и косвенная таблица переходов - действительно единственный способ сделать это.
Таким образом, я думаю, чтобыполучить «идеальный» код, с одним байтом на nop, вам также придется написать логику перехода в сборке.
Вот что я придумал (для gcc / amd64 / gas в Linux). Вот это на Годболте .
#include <stdlib.h>
#define N 1000
#define xstr(s) str(s)
#define str(s) #s
void delay(unsigned ticks) {
if (ticks <= N) {
asm("movq $1f, %%rax \n"
"addq %0, %%rax \n"
"jmp *%%rax \n"
"1: \n"
".rept " xstr(N) " \n"
"nop \n"
".endr \n"
: : "g" ((unsigned long)(N-ticks)): "ax");
} else {
abort();
}
}
int main(void) {
delay(4);
return 0;
}
Обратите внимание, что он должен быть скомпилирован с -no-pie
.Если вы хотите, чтобы он работал как независимый от позиции исполняемый файл, вам, вероятно, понадобится хитрость, подобная call 2f ; 2f: popq %rax
, чтобы получить абсолютный программный адрес в регистр.
Конечно, всегда возникает вопрос, являются ли издержки на самом делеполучение этого кода испортит точность вашего времени задержки ...