// clang-format off // COMPILE TIME FLAGS EXPLANATION // - NO_MMAP Prevents usage of mmap+hugepages, which is only supported on linux // - NO_ASM Prevents usage of inline assembly, useful for non-x86 platforms // - BAD_CLOCK Use if your system clock does not support nanoseconds. Uses microseconds instead. // - OLD_CLANG Use if your compiler version is not very up-to-date. // - SPEED_IMPROVEMENT Almost doubles clock rate for the VM with a few optimizations. // - EXTENDED_INSTRUCTION_SET Adds a few instructions which decrease token throughput for ir2. The high-level IR emitter will not work otherwise. // clang-format on /* -- BEGIN COMPILE TIME FLAGS -- */ // #define PU2_NO_MMAP // #define PU2_NO_ASM // #define PU2_BAD_CLOCK // #define PU2_OLD_CLANG #define PU2_SPEED_IMPROVEMENT #define PU2_EXTENDED_INSTRUCTION_SET /* -- END COMPILE TIME FLAGS --*/ #ifdef PU2_NO_ASM #warning Enabling this flag will invalidate all clock speed and cycle count measurements (thus, your CPU clock rate will be reported as 0MHz) #endif #define _GNU_SOURCE #include #include #include #include #ifndef PU2_NO_MMAP #include #endif #pragma clang diagnostic ignored "-Wparentheses" #pragma clang diagnostic ignored "-Wreturn-type" #pragma clang diagnostic ignored "-Wempty-body" // pu2! // maybe compacter? idk #define A 8 // change this for error reporting on unknown instructions! // examples: // #define A __builtin_trap() // ^ does SIGTRAP // #define A my_custom_cpu_error_trap() // ^ calls my_custom_cpu_error_trap() #define S (1 << 24) #define J while #define H !--c #ifndef PU2_SPEED_IMPROVEMENT #ifndef PU2_EXTENDED_INSTRUCTION_SET // clang-format off m[S],c,v,a,b,u;pu2(){J(c=m[m[7]++],v=m[m[7]++],c){H?a=v:H?a=m[b]:H?u?m[a]=b:0:H?v=a,a=b,b=v:H?a+=b:H?a-=b:H?a&=b:H?a=!a:H?u=v:H?u=a:H?a=u:H?u=b:H?b=u:A;}} // clang-format on #else // clang-format off m[S],c,v,a,b,u;pu2(){J(c=m[m[7]++],v=m[m[7]++],c){H?a=v:H?a=m[b]:H?u?m[a]=b:0:H?v=a,a=b,b=v:H?a+=b:H?a-=b:H?a&=b:H?a=!a:H?u=v:H?u=a:H?a=u:H?u=b:H?b=u:H?m[0]+=v*2:H?m[v]=m[a]:H?a=m[v]:H?m[a]=v:H?m[a]=m[v]:A;}} // clang-format on #endif #else #include #undef A #define A \ ({ \ printf("ERROR: Invalid instruction!\n"); \ exit(1); \ }) // pu2 optimized for speed implements a few new instructions for speed, // and has a "bake" mode which will preprocess the memory and perform the required modifications. // // 14: nop // 15: m[imm] = m[a] // 16: a = m[imm] // 17: m[a] = imm // 18: m[a] = m[imm] // not implemented yet, was lazy // bake(int start, int end) {} #define unlikely(JJ) __builtin_expect((JJ), 0) #define likely(JJ) __builtin_expect((JJ), 1) #ifndef PU2_NO_MMAP __attribute__((aligned(64))) int *m; #else __attribute__((aligned(64))) int m[ S ]; #endif int a, b, u, c; typedef struct { int c; int v; } vminst; const int hugepage_size = 2 * 1024 * 1024; #ifndef PU2_NO_ASM // unused void set_affinity(int cpu) { cpu_set_t mask; CPU_ZERO(&mask); CPU_SET(cpu, &mask); if (sched_setaffinity(0, sizeof(mask), &mask) != 0) { perror("sched_setaffinity failed"); } } #endif void init_pu2() { // set_affinity(0); #ifndef PU2_NO_MMAP const int S_val = S * sizeof(int); int page_count = (S_val / hugepage_size) + 1; if (page_count < 1) page_count = 1; const int page_size = page_count * hugepage_size; m = mmap( NULL, page_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_HUGE_2MB | MAP_HUGETLB, -1, 0); if (m == MAP_FAILED) { perror("[warn] couldn't allocate pu2 memory"); printf( " ( hint: enable hugepages in your kernel settings\n `echo %i | " "sudo " "tee /proc/sys/vm/nr_hugepages` )", page_count); exit(1); } #endif // size_t i; // for (i = 0; i < page_count; i++) ((char *) m)[ i * hugepage_size ] = 0; } #define asm(...) __asm__(__VA_ARGS__) void __attribute__((hot, aligned(64))) pu2() { vminst *M2 = (vminst *) m; vminst *M = &M2[ m[ 7 ] >> 1 ]; static int s1, s2, s3; static const __attribute__((aligned(64))) void *PRECOMPUTED_TABLE[] = { &&C_ZERO, &&C_ONE, &&C_TWO, &&C_THREE, &&C_FOUR, &&C_FIVE, &&C_SIX, &&C_SEVEN, &&C_EIGHT, &&C_NINE, &&C_TEN, &&C_ELEVEN, &&C_TWELVE, &&C_THIRTEEN, &&CB_FOURTEEN, &&CB_FIFTEEN, &&CB_SIXTEEN, &&CB_SEVENTEEN, &&CB_EIGHTEEN, [32] = 0 }; static vminst inst __asm__("r21"); while (1) { PU2_START: inst = *M++; // if (unlikely(inst.c > 18)) A; __builtin_prefetch(M + 1, 0, 3); goto *PRECOMPUTED_TABLE[ inst.c ]; C_ZERO: goto PU2_END; C_ONE: #ifndef PU2_NO_ASM asm("mov %1, %0" : "=r"(a) : "r"(inst.v)); #else a = inst.v; #endif goto PU2_START; C_TWO: #ifndef PU2_NO_ASM asm("mov %1, %0" : "=r"(a) : "r"(m[b])); #else a = m[ b ]; #endif goto PU2_START; C_THREE: if (likely(u)) { if (unlikely(a == 7)) M = M2 + (b >> 1); m[ a ] = b; } goto PU2_START; C_FOUR: #ifndef PU2_NO_ASM asm("xchg %0, %1" : "+r"(a), "+r"(b)); #else s1 = a; a = b; b = s1; #endif goto PU2_START; C_FIVE: a += b; goto PU2_START; C_SIX: a -= b; goto PU2_START; C_SEVEN: a &= b; goto PU2_START; C_EIGHT: a = !a; goto PU2_START; C_NINE: u = inst.v; goto PU2_START; C_TEN: u = a; goto PU2_START; C_ELEVEN: a = u; goto PU2_START; C_TWELVE: u = b; goto PU2_START; C_THIRTEEN: b = u; goto PU2_START; CB_FOURTEEN: // nop // this instruction allows for essentially embedding data inside a program, as pu2 will skip // right over it M += inst.v; goto PU2_START; CB_FIFTEEN: //if (unlikely(inst.v == 7)) M = M2 + (m[a] >> 1); //if (unlikely(inst.v == a)) goto PU2_START; m[ inst.v ] = m[ a ]; // asm("mov %1, %0" : "=r"(m[inst.v]) : "r"(m[a])); goto PU2_START; CB_SIXTEEN: // asm("mov %1, %0" : "=r"(a) : "r"(m[inst.v])); a = m[ inst.v ]; goto PU2_START; CB_SEVENTEEN: // beware, only instruction 17 (cb3) actually updates pc on an assignment to m[7]! if (unlikely(a == 7)) M = M2 + (inst.v >> 1); // asm("mov %1, %0" : "=r"(m[a]) : "r"(inst.v)); m[ a ] = inst.v; goto PU2_START; CB_EIGHTEEN: //if (unlikely(a == 7)) M = M2 + (m[inst.v] >> 1); //if (unlikely(inst.v == a)) goto PU2_START; m[ a ] = m[ inst.v ]; // asm("mov %1, %0" : "=r"(m[a]) : "r"(m[inst.v])); goto PU2_START; } PU2_END: m[ 7 ] = (M - M2) << 1; } #endif // there's just memory. no, really // ok actually there's two registers called a and b // 0 HLT: halt // 1 IMM: a = imm // 2 LOD: a = m[b] // 3 STR: m[a] = b (only happens if acc != 0) // 4 SWP: swap a and b // 5 ADD: a += b // 6 SUB: a -= b // 7 AND: a &= b // 8 NOT: a = !a // 9 ACC: u = imm // 10 A2U: u = a // 11 U2A: a = u // 12 B2U: u = b // 13 U2B: b = u // NOTE: Memory indexes less than 1024 should be considered as temporary/compute memory, and // programs should be written at index 1024 and upwards. // // NOTE: Memory index 7 is the program // counter, in which the current instruction is read from m[m[7]] at each cycle // ------------------------- // yeehaw baby it's time for as2 // E is the error code loc. zero it before calling as2 // Y is macro storage. extend as needed // set O for output before running. keep in mind, it gets modified whilst as2 is running. // therefore, i recommend you store a copy somewhere! // clang-format off char*C="hltimmlodstrswpaddsubandnotacca2uu2ab2uu2bnopcb1cb2cb3",*Y[1<<10],*E,*L,i;*K;as2(char*I){J(c=*I++){if(c==35){J(*++I-10);c=*I++;}L=C;(!isspace(c)?c-39?c-125?c-123?c-64?({i=0,I--;J(i++,*L)strncmp(L,I,3)?L+=3:({I+=3;*K++=i-1;*K++=(i-2&&i-9)?0:strtol(I,&I,10);goto Q;});E=I;return 1;Q:;}):as2(Y[strtol(I,&I,10)]):({Y[strtol(I,&I,10)]=I+1;J(*I++-125);}):({break;}):({*K++=1;*K++=*I++;}):0);}} // clang-format on // #undef S #undef H #undef J #undef E