x86 32-bit machine code fragment, 1 byte
48 dec eax
Input in EAX, output in EAX: 0 for true, non-zero for false. (Also leaves the ZF flag set for true, unset for false, so you could
je was_equal). As a "bonus", you don't have to worry about wrapping; 32-bit x86 can only address 4GiB of memory, so you can't make M large enough to wrap all the way around and find
1 == 2**32 + 1 or something.
To make a callable function, append a
ret instruction after repeating
0x48 M times. (Not counted in the total count, because many languages need to repeat just the function body, or an expression, to be able to compete).
Calleable from GNU C with the prototype
__attribute__((regparm(1))) int checkeqM(int eax); GNU C's
regparm x86 function attribute, like
-mregparm, uses EAX to pass the first integer arg.
For example, this complete program takes 2 args, and JITs M copies of the instruction + a
ret into a buffer, and then calls it as a function. (Requires executable heap; compile with
gcc -O3 -m32 -z execstack)
/******* Test harness: JIT into a buffer and call it ******/
// compile with gcc -O3 -no-pie -fno-pie -m32 -z execstack
// or use mprotect or VirtualProtect instead of -z execstack
// or mmap(PROT_EXEC|PROT_READ|PROT_WRITE) instead of malloc
// declare a function pointer to a regparm=1 function
// The special calling convention applies to this function-pointer only
// So main() can still get its args properly, and call libc functions.
// unlike if you compile with -mregparm=1
typedef int __attribute__((regparm(1))) (*eax_arg_funcptr_t)(unsigned arg);
int main(int argc, char *argv)
if (argc<3) return -1;
unsigned N=strtoul(argv, NULL, 0), M = strtoul(argv, NULL, 0);
char *execbuf = malloc(M+1); // no error checking
memset(execbuf, 0x48, M); // times M dec eax
execbuf[M] = 0xC3; // ret
// Tell GCC we're about to run this data as code. x86 has coherent I-cache,
// but this also stops optimization from removing these as dead stores.
__builtin___clear_cache (execbuf, execbuf+M+1);
// asm("" ::: "memory"); // compiler memory barrier works too.
eax_arg_funcptr_t execfunc = (eax_arg_funcptr_t) execbuf;
int res = execfunc(N);
printf("%u == %u => %d\n", N,M, res );
return !!res; // exit status only takes the low 8 bits of return value
non-PIE executables are loaded lower in virtual memory; can do a bigger contiguous malloc.
$ gcc -g -O3 -m32 -no-pie -fno-pie -fno-plt -z execstack coderepeat-i386.c
$ time ./a.out 2747483748 2747483748 # 2^31 + 600000100 is close to as big as we can allocate successfully
2747483748 == 2747483748 => 0
real 0m1.590s # on a 3.9GHz Skylake with DDR4-2666
$ echo $?
# perf stat output:
670,816 page-faults # 0.418 M/sec
6,235,285,157 cycles # 3.885 GHz
5,370,142,756 instructions # 0.86 insn per cycle
Note that GNU C doesn't support object sizes larger than
ptrdiff_t (signed 32-bit), but
memset do still work, so this program succeeds.
ARM Thumb machine code fragment, 2 bytes
3802 subs r0, #2
First arg in
r0 and return value in
r0 is the standard ARM calling convention. This also sets flags (the
s suffix). Fun fact; the non-flag-setting version of
sub is a 32-bit wide instruction.
The return instruction you need to append is
AArch64 machine code fragment, 4 bytes
d1001000 sub x0, x0, #0x4
Works for 64-bit integers. Input / output in
x0, as per the standard calling convention.
AArch64 doesn't have a Thumb mode (yet), so 1 instruction is the best we can do.