#define _POSIX_C_SOURCE 200809L #include "pu2.out.c" // #include #include #include #include #include #include #include typedef char *string; string read_file(string path, int *out_size) { FILE *f = fopen(path, "rb"); string buf; int size; if (!f) return NULL; fseek(f, 0, SEEK_END); size = ftell(f); fseek(f, 0, SEEK_SET); buf = malloc(size + 1); if (!buf) { fclose(f); return NULL; } if (fread(buf, 1, size, f) != size) { free(buf); fclose(f); return NULL; } buf[ size ] = '\0'; fclose(f); if (out_size) *out_size = size; return buf; } error_debug(string fail_point, string code, int sz) { int error_pos = (int) (fail_point - code); int i; int line = 1, col = 1; for (i = 0; i < error_pos && i < sz; i++) { if (code[ i ] == '\n') { line++; col = 1; } else { col++; } } int line_start = error_pos; while (line_start > 0 && code[ line_start - 1 ] != '\n') { line_start--; } int line_end = error_pos; while (line_end < sz && code[ line_end ] != '\n' && code[ line_end ] != '\0') { line_end++; } int tmp = line; int lineno_width = 1; while (tmp >= 10) { lineno_width++; tmp /= 10; } fprintf( stderr, "\x1b[1m\x1b[31merror:\x1b[0m \x1b[1massembly failed\x1b[0m " "at \x1b[36mline %d\x1b[0m, \x1b[36mcolumn %d\x1b[0m " "(\x1b[33mposition %d\x1b[0m):\n", line, col, error_pos); fprintf( stderr, " %*d | \x1b[1m%.*s\x1b[0m\n", lineno_width, line, line_end - line_start, code + line_start); /* print caret line aligned under the column */ fprintf(stderr, " %*s | ", lineno_width, ""); for (i = line_start; i < error_pos; i++) { fputc((code[ i ] == '\t') ? '\t' : ' ', stderr); } fprintf(stderr, "\x1b[1m\x1b[31m^\x1b[0m \x1b[90mparsing stopped here\x1b[0m\n"); } long powi(long x, unsigned n) { long p = x, r = 1; while (n > 0) { if (n & 1) r *= p; p *= p, n /= 2; } return p == 0 ? 0 : r; } emu() { while (1) { pu2(); int e_code = m[ 0 ]; if (e_code != 2) return e_code; // handle hardware interrupts int int_code = m[ 1 ]; // 0: output string if (int_code == 0) { int ptr = m[ 2 ]; int size = m[ 3 ], i = 0; while (size--) putchar(m[ ptr++ ]); } // 1: input character else if (int_code == 1) { m[ 2 ] = getchar(); } // 2: arithmetics unit else if (int_code == 2) { int op = m[ 2 ]; int val1 = m[ 3 ]; int val2 = m[ 4 ]; m[ 2 ] = 0; if (op == 0) { // multiply m[ 3 ] = val1 * val2; } else if (op == 1) { // divide m[ 3 ] = val1 / val2; } else if (op == 2) { // modulo m[ 3 ] = val1 % val2; } else if (op == 3) { // left shift m[ 3 ] = val1 << val2; } else if (op == 4) { // right shift m[ 3 ] = val1 >> val2; } else if (op == 5) { // pow if (val1 == 2) m[ 3 ] = val1 << val2; else { m[ 3 ] = powi(val1, val2); } } else { // unknown op, set m[2] m[ 2 ] = 1; } } else { // unknown interrupt. what to do? } m[ 0 ] = 0; } } skip_space(string *I) { while ((**I == '\n' || isspace(**I)) && (**I != 0)) (*I)++; } // IR parser for pu2's as2 // clang-format off struct { int hlt, imm, lod, str, swp, add, sub, and, not, acc, a2u, u2a, b2u, u2b, nop, cb1, cb2, cb3, cb4; } opcode = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18 }; // clang-format on typedef unsigned char byte; typedef byte bool; const bool true = 1; const bool false = 0; // use this instead of strdup'ing and allocating extra storage! typedef struct { int len; string ref; } slice_t; typedef struct { slice_t key; slice_t value; } entry_t; typedef struct { slice_t name; int location; } label_t; typedef struct { entry_t data; byte arg_count; } macro_t; typedef struct { slice_t name; int *replace_location; // where to put the resolved address string init_I; } label_delegated_t; clean_slice_ref(slice_t *val) { free(val->ref); } #define static_len(G) (sizeof(G) / sizeof((G)[ 0 ])) #define run_err(result, ...) \ ({ \ ERR_code = init_I; \ fprintf(stderr, "\n"); \ error_debug((result), ERR_code, strlen(ERR_code)); \ fprintf(stderr, "\n\x1b[1m\x1b[31mir2 error:\x1b[0m "); \ fprintf(stderr, __VA_ARGS__); \ fprintf(stderr, "\n\n"); \ exit(1); \ }) #define checkskip(char, ...) \ ({ \ if (!*I) { run_err(I, __VA_ARGS__); } \ if (*I++ != (char) ) { run_err(I - 1, __VA_ARGS__); } \ }) #define scanword() \ ({ \ slice_t VAR = { 0, I }; \ while (isalnum(*I) || *I == '_') VAR.len++, I++; \ VAR; \ }) #define memslice_t slice_t __attribute__((cleanup(clean_slice_ref))) #define readd() ((used == new.len) ? (new.ref = realloc(new.ref, new.len *= 2)) : 0) #define scanvalue() \ ({ /* value scanner! numbers, characters, strings, variable derefs. */ \ int value; \ if (isdigit(*I)) { \ value = strtol(I, &I, 10); \ } else if (*I == '\'') { \ I++; \ value = *I++; \ checkskip('\'', "couldn't find closing quote on character"); \ } else if (*I == '"') { \ string str_start = ++I; \ bool T = false; \ int str_len = 0; \ while (*I != '"' || T) { \ if (*I == '\\') { \ T = !T; \ if (!T && I[ 1 ] == '"') str_len--; \ } \ str_len++; \ I++; \ } \ I++; \ str_len++; \ int *dest_start = Data -= str_len; \ int y; \ for (y = 0; y < str_len - 1; y++) { dest_start[ y ] = str_start[ y ]; } \ Data[ -1 ] = 0; \ value = Data - m; \ } else if (*I == '&') { \ I++; \ string starting_I = I; \ slice_t name = scanword(); \ value = __ir2_get_var_loc(name, variables, starting_I, init_I); \ } else { \ run_err(I, "expected compile-time value (number, character, string, dereference)"); \ } \ value; \ }) #define scanvar() \ ({ /* memory scanner! finds @10, var_name */ \ int value; \ if (*I == '@') { \ if (!isdigit(*++I)) run_err(I - 1, "expected memory index"); \ value = strtol(I, &I, 10); \ } else if (isalpha(*I) || *I == '_') { \ string starting_I = I; \ slice_t name = scanword(); \ value = __ir2_get_var_loc(name, variables, starting_I, init_I); \ } else \ run_err(I, "expected memory index or variable"); \ value; \ }) #define varqual(F) ((F) == '@' || isalpha(F) || (F) == '_') #define litqual(F) (isdigit(F) || (F) == '\'' || (F) == '"' || (F) == '&') // #define emit(OP, VAL) (*Code++ = (OP), *Code++ = (VAL)) #define store() (emit(opcode.acc, 1), op(str)) #define op(mode) emit(opcode.mode, 0) #define swap() op(swp) #define imm(value) emit(opcode.imm, value) #define load() op(lod) #define imm_in_mem(__mem, __imm) (imm(__mem), emit(opcode.cb3, __imm)) #define mov_mem_mem(__mem1, __mem2) (imm(__mem1), emit(opcode.cb4, __mem2)) #define mem_in_a(__mem) emit(opcode.cb2, __mem) #define perform_op(operation) \ ({ \ mem_in_a(mem2_index); \ swap(); \ mem_in_a(mem_index); \ op(operation); \ swap(); \ imm(mem_index); \ store(); \ }) string ERR; string ERR_code; int *Code; int *Data; init_ir2() { Code = &m[ 4000 ]; Data = &m[ S - 1 ]; // grows downwards } __attribute__((always_inline)) emit(int op, int val) { *Code++ = op; *Code++ = val; } int ir2_depth = 0; #define space skip_space(&I) bool has_jump_warned = false; bool has_useless_if_warned = false; bool has_false_while_warned = false; __ir2_get_var_loc(slice_t name, label_t variables[ 1 << 11 ], string error_I, string init_I) { int j; for (j = 0; j < 1 << 11; j++) { label_t cur = variables[ j ]; if (cur.name.ref == NULL) break; if (cur.name.len != name.len) continue; if (strncmp(cur.name.ref, name.ref, name.len) == 0) { return cur.location; } } run_err(error_I, "unknown variable name \"%.*s\"", name.len, name.ref); } string _ir2( string I, macro_t macros[ 1 << 9 ], label_t labels[ 1 << 10 ], label_delegated_t forward_refs[ 1 << 10 ], label_t variables[ 1 << 11 ]) { char T; string init_I = I; ERR = "all good"; while (T = *I) { if (isspace(T)) { space; continue; } string start_I = I; if (T == '}') { if (ir2_depth == 0) run_err(I, "unexpected closing bracket"); ir2_depth--; return ++I; } if (T == '/' && I[ 1 ] == '/') { // comment I += 2; while (*I != '\n' && *I != '\0') I++; continue; } if (T == '/' && I[ 1 ] == '*') { // multi-line comment string old_I = I; I += 2; while (*I != '*' || I[ 1 ] != '/') { if (I[ 1 ] == '\0') { run_err(old_I, "reached EOF whilst looking for closing comment delimeter"); } I++; } I += 2; continue; } // define variable if (strncmp("let", I, 3) == 0 && isspace(I[ 3 ])) { I += 3; space; string backname_I = I; slice_t name = scanword(); space; int *m_index = --Data; int start_index = m_index - m; // comment out this flag to initialize values at runtime instead of compile-time #define IR2_INIT_COMPILE_TIME if (*I == ';') { // no starter value, just initialize it to zero I++; #ifdef IR2_INIT_COMPILE_TIME *m_index = 0; #else imm_in_mem(start_index, 0); #endif } else { checkskip('=', "expected semicolon or assignment in variable declaration"); space; if (litqual(*I)) { // literal #ifdef IR2_INIT_COMPILE_TIME *m_index = scanvalue(); #else imm_in_mem(start_index, scanvalue()); #endif } else if (varqual(*I)) { // memory address I++; int mem_index = scanvar(); mov_mem_mem(start_index, mem_index); } else { run_err(I, "expected compile-time constant, variable or memory index"); } space; checkskip(';', "expected semicolon after variable value"); } int j; for (j = 0; j < 1 << 11; j++) { label_t cur = variables[ j ]; if (cur.name.ref == NULL) { variables[ j ].name = name; variables[ j ].location = start_index; goto VAR_DONE; } if (cur.name.len == name.len && strncmp(cur.name.ref, name.ref, name.len) == 0) { run_err(backname_I, "duplicate variable name \"%.*s\"", name.len, name.ref); } } run_err(backname_I, "variables count exceeded 1 << 11 (literally how)"); VAR_DONE: continue; } // register macros if (strncmp("macro", I, 5) == 0 && isspace(I[ 5 ])) { I += 6; space; // parse macro stuff if (!isalpha(*I) && *I != '_') { run_err(I - 1, "expected letter or underscore at macro name"); } string old_I = I; slice_t name = scanword(); space; checkskip('(', "expected open paren in macro defintion"); space; string before = I; int arg_count = strtol(I, &I, 10); if (I == before) { run_err(I, "expected number (argument count) in macro definition parens"); } space; checkskip( ')', "expected close paren in macro definition (hint: macro defintions only take an " "argument count)"); space; checkskip('{', "expected open curly bracket in macro body"); // find open space in macros int x; for (x = 0; x < (1 << 9); x++) { macro_t g = macros[ x ]; if (g.data.key.ref == NULL) goto DONE; if (g.data.key.len == name.len && strncmp(g.data.key.ref, name.ref, name.len) == 0) { run_err(old_I, "redefinition of macro"); } } run_err(I - 1, "registered macro count exceeded 1<<9 (how?)"); DONE:; space; // might aswell skip whitespace for funsies. macro_t *g = ¯os[ x ]; g->data.key = name; g->data.value.ref = I; g->data.value.len = 0; g->arg_count = arg_count; int dep = 0; while (dep != 0 || *I != '}') { if (*I == '{') dep++; if (*I == '}') dep--; g->data.value.len++; I++; if (*I == 0) { checkskip('}', "expected close curly bracket in macro body (met EOF instead)"); } } I++; continue; } // emit exit calls if (strncmp("exit", I, 4) == 0) { I += 4; checkskip('(', "expected open paren on exit blob"); string old = I; int value = strtol(I, &I, 10); if (old == I && *I != ')') { run_err(I - 1, "invalid input to exit blob, expected number"); } space; checkskip(')', "expected open paren on exit blob"); space; checkskip(';', "expected semicolon after call"); // actually emit stuff if (old != I) emit(opcode.imm, value); op(hlt); continue; } // emit system if (strncmp("system", I, 6) == 0 && !isalnum(I[ 6 ])) { I += 6; space; checkskip('(', "expected open paren on system blob"); space; checkskip( ')', "expected closing paren on system blob (hint: system does not take any arguments)"); space; checkskip(';', "expected semicolon after system blob"); imm_in_mem(0, 2); op(hlt); continue; } if (strncmp("jump", I, 4) == 0 && !isalnum(I[ 4 ])) { I += 4; goto PARSE_JUMP_BLOB; } if (strncmp("jmp", I, 3) == 0 && !isalnum(I[ 3 ])) { I += 3; PARSE_JUMP_BLOB: space; checkskip('(', "expected open paren on jump blob"); space; if (!isalpha(*I)) run_err(I, "expected a label name for jump blob argument"); slice_t label = scanword(); space; checkskip(')', "expected closing paren after label name in jump blob"); space; checkskip(';', "excpected semicolon after jump blob"); // resolve right now if the label exists label_t *lbdb = labels; while (lbdb->name.ref != NULL) { label_t cur = *lbdb++; if (cur.name.len == label.len && strncmp(cur.name.ref, label.ref, label.len) == 0) { imm_in_mem(7, cur.location); goto FINISH_JMP; } } // not resolved yet.. // find delegations slot label_delegated_t *del; int j; for (j = 0; j < 1 << 10; j++) { if (forward_refs[ j ].name.ref == NULL) { // found one! del = &forward_refs[ j ]; goto WRITE_DEL; } } run_err( label.ref, "couldn't reserve a spot for this label as the forward references table is full " "(how?)"); WRITE_DEL: imm_in_mem(7, 0xDEAD10C4); // DEADLOCK - placeholder for the resolver del->replace_location = Code - 1; // place of imm in code del->name = label; del->init_I = init_I; FINISH_JMP: continue; } if ((strncmp("jnz", I, 3) == 0 && !isalnum(I[ 3 ])) || (strncmp("jz", I, 2) == 0 && !isalnum(I[ 2 ]))) { bool was_jnz = !!strncmp("jz", I, 2); string name = was_jnz ? "jnz" : "jz"; I += was_jnz ? 3 : 2; space; checkskip('(', "expected open paren on %s blob", name); space; int target_mem = scanvar(); space; checkskip( ',', "expected comma (second argument) in %s blob (example: `%s(@5, label_name);`)", name, name); space; if (!isalpha(*I)) run_err( I, "expected a label name for %s blob's second argument (example: `%s(@5, " "label_name);`)", name, name); slice_t label = scanword(); space; checkskip( ')', "expected closing paren after label name in %s blob (do i really need to explain " "this to you again?)", name); space; checkskip(';', "excpected semicolon after %s blob", name); // resolve right now if the label exists label_t *lbdb = labels; while (lbdb->name.ref != NULL) { label_t cur = *lbdb++; if (cur.name.len == label.len && strncmp(cur.name.ref, label.ref, label.len) == 0) { // emit j(n)z mem_in_a(target_mem); if (!was_jnz) op(not); op(a2u); imm(cur.location); swap(); imm(7); op(str); goto FINISH_JNZ; } } // not resolved yet.. // find delegations slot label_delegated_t *del; int j; for (j = 0; j < 1 << 10; j++) { if (forward_refs[ j ].name.ref == NULL) { // found one! del = &forward_refs[ j ]; goto WRITE_DEL_JNZ; } } run_err( label.ref, "couldn't reserve a spot for this label as the forward references table is full " "(how?)"); WRITE_DEL_JNZ: // emit j(n)z mem_in_a(target_mem); if (!was_jnz) op(not); op(a2u); imm(0xDEAD10C4); // DEADLOCK - placeholder for the resolver del->replace_location = Code - 1; // place of imm in code swap(); imm(7); op(str); del->name = label; del->init_I = init_I; FINISH_JNZ: continue; } if (strncmp("if", I, 2) == 0 && !isalnum(I[ 2 ])) { // if statement I += 2; space; checkskip('(', "expected open paren for if statement"); space; ir2_depth++; if (isdigit(*I)) { if (!has_useless_if_warned) { has_useless_if_warned = true; printf("warn: redundant if statement\n"); } // redundant if statement: always true / always false int value = strtol(I, &I, 10); space; checkskip(')', "expected closing paren after if statement argument"); space; checkskip('{', "expected opening bracket for if statement body"); space; if (!value) { // just skip over everything inside int *Code_B = Code; int *Data_B = Data; // completely eliminate code generated in useless if statement int m2[ S / 4 ]; Data = Code = m2; label_t variables_G[ 1 << 5 ]; // get new I location string new_I = _ir2(I, macros, labels, forward_refs, variables_G); I = new_I; // restore values Code = Code_B; Data = Data_B; } else { // just... i mean, i guess run the code..? string new_I = _ir2(I, macros, labels, forward_refs, variables); I = new_I; } continue; } bool was_negated = false; if (*I == '!') { was_negated = true; I++; space; } int mem_index = scanvar(); space; checkskip(')', "expected closing paren after if statement argument"); space; checkskip('{', "expected opening bracket for if statement body"); space; mem_in_a(mem_index); if (!was_negated) op(not); op(a2u); imm(0xDEAD10C4); // DEADLOCK - placeholder for the patchback int *patchback = Code - 1; swap(); imm(7); op(str); // handle the code stuff string new_I = _ir2(I, macros, labels, forward_refs, variables); I = new_I; *patchback = Code - m; // patch the jump instruction continue; } if (strncmp("while", I, 5) == 0 && !isalnum(I[ 5 ])) { I += 5; space; checkskip('(', "expected open paren for while loop"); space; ir2_depth++; if (isdigit(*I)) { int value = strtol(I, &I, 10); space; checkskip(')', "expected closing paren after while loop argument"); space; checkskip('{', "expected opening bracket for while loop body"); space; if (!value) { // non-running while loop if (!has_false_while_warned) { has_false_while_warned = true; printf("warn: dead code inside while loop\n"); } // just skip over everything inside int *Code_B = Code; int *Data_B = Data; // completely eliminate code generated in useless while loop int m2[ S / 4 ]; Data = Code = m2; label_t variables_G[ 1 << 5 ]; // get new I location string new_I = _ir2(I, macros, labels, forward_refs, variables_G); I = new_I; // restore values Code = Code_B; Data = Data_B; } else { // run forever! int start = Code - m; string new_I = _ir2(I, macros, labels, forward_refs, variables); I = new_I; imm_in_mem(7, start); // always jump back to the start } continue; } bool was_negated = false; if (*I == '!') { was_negated = true; I++; space; } int mem_index = scanvar(); space; checkskip(')', "expected closing paren after while loop condition"); space; checkskip('{', "expected opening bracket for while loop body"); space; mem_in_a(mem_index); if (!was_negated) op(not); op(a2u); imm(0xDEAD10C4); // DEADLOCK - placeholder for the patchback int *patchback = Code - 1; swap(); imm(7); op(str); int start_index = Code - m; // handle the code stuff string new_I = _ir2(I, macros, labels, forward_refs, variables); I = new_I; *patchback = Code - m; // patch the jump instruction // check the condition and jump back if true mem_in_a(mem_index); if (was_negated) op(not); op(a2u); imm(start_index); swap(); imm(7); op(str); continue; } // macro call, maybe? if (isalpha(*I)) { string old_I = I; slice_t name = scanword(); space; if (*I == ':') { I++; // this is a label! label_t *slot; int j; for (j = 0; j < 1 << 10; j++) { if (labels[ j ].name.ref != NULL) { if (labels[ j ].name.len == name.len && strncmp(labels[ j ].name.ref, name.ref, name.len) == 0) { run_err(old_I, "redefinition of label"); } continue; } slot = &labels[ j ]; goto FOUND_SLOT; } run_err(old_I, "registered label count exceeded 1<<10 (how?)"); FOUND_SLOT: slot->name = name; slot->location = Code - m; continue; } if (*I != '(') { I = old_I; goto REST_AFTER_MACRO_CALL; } checkskip('(', "expected open paren after alphanumeric word (assuming this is a blob)"); space; slice_t arguments[ 1 << 7 ]; int arg_count = 0; string fail_I = I; while (*I != ')') { if (!*I) run_err(fail_I, "reached EOF whilst looking for closing paren on blob"); checkskip('[', "expected bracket in blob argument (macro-type)"); arguments[ arg_count ].len = 0; arguments[ arg_count ].ref = I; int dep = 0; while (dep != 0 || *I != ']') { if (*I == '[') dep++; if (*I == ']') dep--; arguments[ arg_count ].len++; I++; if (*I == 0) { checkskip( ']', "expected closing bracket in macro argument (met EOF instead)"); } } space; checkskip(']', "expected closing bracket in macro arguments"); space; if (*I != ',' && *I != ')') { run_err(I, "expected comma or closing paren in blob"); } if (*I == ',') { I++; space; } arg_count++; } fail_I = I; checkskip(')', "expected closing paren after blob arguments"); space; checkskip(';', "expected semicolon after blob"); // done reading blob! check it and parse macro :) // find index int i; for (i = 0; i < (1 << 9); i++) { macro_t cur = macros[ i ]; if (cur.data.key.len != name.len) continue; if (strncmp(name.ref, cur.data.key.ref, name.len) == 0) goto FOUND; } run_err(I, "could not find a macro by that name"); FOUND:; if (arg_count != macros[ i ].arg_count) { run_err( fail_I, "macro argument count mismatch (expected %i, got %i)", macros[ i ].arg_count, arg_count); } slice_t target = macros[ i ].data.value; string N = target.ref; string oldN = N; memslice_t new = { target.len, malloc(target.len) }; int used = 0; bool needs_scan = true; while (needs_scan) { needs_scan = false; while (*N) { if (N - oldN >= target.len) break; if (*N == '/' && isdigit(N[ 1 ])) { N++; string old = N - 1; int index = strtol(N, &N, 10); if (index > arg_count) { N--; goto END; } if (*N++ != '/') { N = old + 1; while (*N != '/') { new.ref[ used++ ] = *N++; readd(); } goto END; } needs_scan = true; slice_t rep = arguments[ index - 1 ]; string val = rep.ref; for (i = 0; i < rep.len; i++) { new.ref[ used++ ] = *val++; readd(); } continue; } END: new.ref[ used++ ] = *N++; readd(); } } new.ref[ used ] = 0; _ir2(new.ref, macros, labels, forward_refs, variables); continue; } REST_AFTER_MACRO_CALL: // handle assignments! if (varqual(T)) { string starting_qual = I; int mem_index = scanvar(); space; if (*I == '-' && I[ 1 ] == '-') { // decrement I += 2; space; checkskip(';', "expected semicolon"); imm(1); swap(); mem_in_a(mem_index); op(sub); swap(); imm(mem_index); store(); continue; } if (*I == '+' && I[ 1 ] == '+') { // increment I += 2; space; checkskip(';', "expected semicolon"); imm(1); swap(); mem_in_a(mem_index); op(add); swap(); imm(mem_index); store(); continue; } char extra_op = '='; if (*I == '+' || *I == '-' || *I == '&' || *I == '!') extra_op = *I++; checkskip('=', "expected assigment after memory blob (hint: only +-&! are supported)"); space; if (mem_index == 7 && !has_jump_warned) { has_jump_warned = true; printf("\nwarn: writing to memory index 7 is equivalent to a jump\n"); printf(" if this behavior is intended, use the jump blob instead.\n\n"); } if (varqual(*I)) { int mem2_index = scanvar(); space; if (extra_op == '=') { mov_mem_mem(mem_index, mem2_index); } else if (extra_op == '+') { perform_op(add); } else if (extra_op == '-') { perform_op(sub); } else if (extra_op == '&') { perform_op(and); } else if (extra_op == '!') { // @1 != @2; (@1 = !@2) mem_in_a(mem2_index); op(not); swap(); imm(mem_index); store(); } } else { int imm = scanvalue(); if (extra_op == '=') { imm_in_mem(mem_index, imm); } else if (extra_op == '+') { imm(imm); swap(); mem_in_a(mem_index); op(add); swap(); imm(mem_index); store(); } else if (extra_op == '-') { imm(imm); swap(); mem_in_a(mem_index); op(sub); swap(); imm(mem_index); store(); } else if (extra_op == '&') { imm(imm); swap(); mem_in_a(mem_index); op(and); swap(); imm(mem_index); store(); } else if (extra_op == '!') { // @1 != 5; (@1 = !5;) imm_in_mem(mem_index, !imm); } } space; checkskip(';', "expected semicolon"); continue; } // unknown run_err(I, "unknown token '%c' (%i)", T, T); } return I; } ir2(string I) { macro_t macros[ 1 << 9 ] = { 0 }; label_t labels[ 1 << 10 ] = { 0 }; label_t variables[ 1 << 11 ] = { 0 }; label_delegated_t forward_refs[ 1 << 10 ] = { 0 }; _ir2(I, macros, labels, forward_refs, variables); label_delegated_t *_forward_refs = forward_refs; // handle forward references! while (_forward_refs->name.ref != NULL) { label_delegated_t cur = *_forward_refs++; // find label int j; for (j = 0; j < static_len(labels); j++) { label_t clab = labels[ j ]; if (clab.name.len == cur.name.len && strncmp(clab.name.ref, cur.name.ref, cur.name.len) == 0) { // found it! *cur.replace_location = clab.location; goto REP_DONE; } } string init_I = cur.init_I; run_err(cur.name.ref, "could not find label %i %s", cur.name.len, cur.name.ref); REP_DONE:; } } string op_names[] = { "hlt", "imm", "lod", "str", "swp", "add", "sub", "and", "not", "acc", "a2u", "u2a", "b2u", "u2b", "nop", "cb1", "cb2", "cb3", "cb4" }; unsigned long long rdtsc(void) { #ifndef PU2_NO_ASM unsigned hi, lo; __asm__ __volatile__("rdtsc" : "=a"(lo), "=d"(hi)); return ((unsigned long long) hi << 32) | lo; #else return 0; #endif } #ifndef PU2_BAD_CLOCK clock_t ntime() { struct timespec t; clock_gettime(CLOCK_MONOTONIC_RAW, &t); return t.tv_nsec + t.tv_sec * 1e9; } #else clock_t ntime() { struct timeval tv; gettimeofday(&tv, NULL); return tv.tv_sec * 1000000000 + tv.tv_usec * 1000; } #endif // bench: measure using clock() double bench_cycle(int instr) { int i; for (i = 0; i < S - 2; i += 2) { m[ i ] = instr; m[ i + 1 ] = 10; } m[ S - 2 ] = 0; m[ S - 1 ] = 0; m[ 7 ] = 0; c = 0; clock_t start = ntime(); int wstart = rdtsc(); pu2(); int wend = rdtsc(); clock_t end = ntime(); double nanoseconds = (end - start); double inst_count = (double) m[ 7 ] / 2; double insts_per_ns = inst_count / nanoseconds; double insts_per_s = insts_per_ns * 1e9; printf( "%-8s | time: %.3fms | insts: %.0f (%li real) | ips: %.3fM inst/s\n", op_names[ instr ], nanoseconds / 1000000, inst_count, end - start, insts_per_ns * 1e3); return insts_per_s / 1e6; // return M inst/s } // bench2: measure using rdtsc double bench2_cycle(int instr) { unsigned long long start, end; double vminst_count, cpu_cycles; int i; for (i = 0; i < S - 2; i += 2) { m[ i ] = instr; m[ i + 1 ] = 5; } m[ S - 2 ] = 0; m[ S - 1 ] = 0; m[ 7 ] = 0; c = 0; start = rdtsc(); pu2(); end = rdtsc(); cpu_cycles = (double) (end - start); vminst_count = (double) m[ 7 ] / 2; double cycles_per_inst = cpu_cycles / vminst_count; printf( "%-6s | cycles/inst: %8.3f | cycles: %14.0f | insts: %12.0f\n", op_names[ instr ], cycles_per_inst, cpu_cycles, vminst_count); return cycles_per_inst; } sleep_nano(int nanoseconds) { struct timespec req = { 0 }; req.tv_sec = nanoseconds / 1000000000L; // Whole seconds req.tv_nsec = nanoseconds % 1000000000L; // Remaining nanoseconds // nanosleep might be interrupted, so loop until it completes while (nanosleep(&req, &req) == -1 && errno == EINTR) { continue; // Continue with remaining time if interrupted } } #ifdef PU2_OLD_CLANG #define inline() __attribute__((always_inline)) #else #define inline() [[clang::always_inline]] #endif inline() sleep_micro(int microseconds) { sleep_nano(microseconds * 1e3); } inline() sleep_milli(int milliseconds) { sleep_nano(milliseconds * 1e6); } // benchmark: run target for all opcodes and print summary benchmark(double (*target)(int), const char *mode_label) { double results[ 19 ]; double sum = 0; int count = 0; int i; printf("\n=== pu2 benchmark (%s) ===\n", mode_label); printf("op | result details\n"); printf("---------------------------------------------\n"); for (i = 1; i < 19; i++) { // printf("running: %s\n", op_names[ i ]); results[ i ] = target(i); sum += results[ i ]; count++; } sum /= count; printf("---------------------------------------------\n"); int start = rdtsc(); sleep_milli(100); int end = rdtsc(); double clock_speed_mhz = ((end - start) / 1e6) * 10; if (strcmp(mode_label, "time") == 0) { // printf("average : %.3f M inst/s\n", sum); printf("cpu clock : ~%.3f MHz (measured via rdtsc & msleep(100))\n", clock_speed_mhz); printf("vm clock (i/s) : %.3fMHz (ipn, higher is better)\n", sum); // printf("estimated : %.3f cycles per instruction (cpi, lower is better)\n", // clock_speed_mhz / sum); } else { // mode_label == "rdtsc" printf("average cycles/inst : %.3f cycles\n", sum); // printf("cpu clock : ~%.3f MHz (measured via rdtsc & msleep(100))\n", // clock_speed_mhz); printf("estimated ipc : %.3f (1 / cycles_per_inst, higher is better)\n", 1.0 / sum); // printf("estimated cpi : %.3f (cycles_per_inst, lower is better)\n", sum); } printf("=== end benchmark ===\n\n"); } volatile long out; main(int argc, char **argv) { if (argc == 1) { printf( "\nusage:\n" " %s open and compile ir2-compliant code\n" " %s benchmark benchmark pu2 (two modes: time and rdtsc)\n\n", argv[ 0 ], argv[ 0 ]); return 1; } init_pu2(); init_ir2(); if (strcmp(argv[ 1 ], "benchmark") == 0) { benchmark(bench_cycle, "time"); const long N = S / 2; // printably the same insts you report long sum = 0; unsigned aux; unsigned long long t0 = rdtsc(); int C; for (C = 0; C < N; ++C) { sum += 1; __asm__ volatile(""); } unsigned long long t1 = rdtsc(); out = sum; // single volatile write to make sum observable printf( "(absolute max) N=%llu cycles=%llu cycles/iter=%.6f\n", (unsigned long long) N, (unsigned long long) (t1 - t0), (t1 - t0) / (double) N); // benchmark(bench2_cycle, "rdtsc"); exit(0); } char *filename = argv[ 1 ]; int file_size; string contents = read_file(filename, &file_size); if (contents == NULL) { perror("unable to open file"); exit(1); } int o_size; long before = ntime(); long cycles_bef = rdtsc(); ir2(contents); long cycles_aft = rdtsc(); long after = ntime(); m[ 7 ] = 4000; /*int j = 0; for(j = 0; j < 64; j++) { printf("%i: %i %i\n", j, m[4000+j], m[4001+j]); j++; }*/ // printf("assembly took %i milliseconds\n", (after - before)/1000); free(contents); int res_size = Code - &m[ 4000 ]; emu(); }