jit_node_t *compile_rpn(char *expr) { jit_node_t *in, *fn; int stack_base, stack_ptr; fn = jit_note(NULL, 0); jit_prolog(); in = jit_arg(); stack_ptr = stack_base = jit_allocai (32 * sizeof (int)); jit_getarg_i(JIT_R2, in); while (*expr) { char buf[32]; int n; if (sscanf(expr, "%[0-9]%n", buf, &n)) { expr += n - 1; stack_push(JIT_R0, &stack_ptr); jit_movi(JIT_R0, atoi(buf)); } else if (*expr == 'x') { stack_push(JIT_R0, &stack_ptr); jit_movr(JIT_R0, JIT_R2); } else if (*expr == '+') { stack_pop(JIT_R1, &stack_ptr); jit_addr(JIT_R0, JIT_R1, JIT_R0); } else if (*expr == '-') { stack_pop(JIT_R1, &stack_ptr); jit_subr(JIT_R0, JIT_R1, JIT_R0); } else if (*expr == '*') { stack_pop(JIT_R1, &stack_ptr); jit_mulr(JIT_R0, JIT_R1, JIT_R0); } else if (*expr == '/') { stack_pop(JIT_R1, &stack_ptr); jit_divr(JIT_R0, JIT_R1, JIT_R0); } else { fprintf(stderr, "cannot compile: %s\n", expr); abort(); } ++expr; } jit_retr(JIT_R0); jit_epilog(); return fn; }
bool compile(const Program &program, Translator &trans, Environment &env) { jit_node_t *ref; jit_node_t *fn = jit_note(NULL, 0); jit_prolog(); const int stack_base_offset = jit_allocai(32 * sizeof(double)); int stack_top_idx = 0; auto &&stackPush = [&](int reg) { jit_stxi_d(stack_base_offset + stack_top_idx * sizeof(double), JIT_FP, reg); ++stack_top_idx; }; auto &&stackPop = [&](int reg) { --stack_top_idx; jit_ldxi_d(reg, JIT_FP, stack_base_offset + stack_top_idx * sizeof(double)); return reg; }; size_t pc = 0; const size_t progsz = program.size(); while (pc < progsz) { const Instruction &instr = program[pc]; switch (instr.op) { case OP_NOP: break; case OP_PUSH: stackPush(JIT_F0); jit_movi_d(JIT_F0, instr.dval); break; case OP_ADD: { const int reg = stackPop(JIT_F1); jit_addr_d(JIT_F0, reg, JIT_F0); } break; case OP_SUB: { const int reg = stackPop(JIT_F1); jit_subr_d(JIT_F0, reg, JIT_F0); } break; case OP_MUL: { const int reg = stackPop(JIT_F1); jit_mulr_d(JIT_F0, reg, JIT_F0); } break; case OP_DIV: { const int reg = stackPop(JIT_F1); jit_divr_d(JIT_F0, reg, JIT_F0); } break; case OP_CALL: { stackPush(JIT_F0); const int sp = stack_top_idx - (instr.callop.nargs); jit_addi(JIT_R0, JIT_FP, stack_base_offset + sp * sizeof(double)); jit_prepare(); jit_pushargi((jit_word_t)&env); // 1st arg: userdata jit_pushargi(instr.callop.nargs); // 2nd arg: # of arguments jit_pushargr(JIT_R0); // 3rd arg: pointer to args on stack jit_pushargi((jit_word_t)&error_); // 4th arg: pointer to error message auto &&cb = trans.lookup(instr.callop.fidx); jit_finishi(reinterpret_cast<jit_pointer_t>(cb)); stack_top_idx -= instr.callop.nargs; // consume arguments on stack jit_retval_d(JIT_F0); jit_retval(JIT_R0); ref = jit_bnei(JIT_R0, 0); jit_reti_d(*reinterpret_cast<const double*>(&JIT_ERROR)); jit_patch(ref); } break; } ++pc; } jit_retr_d(JIT_F0); jit_epilog(); (void)jit_emit(); func_ = reinterpret_cast<JitFunction>(jit_address(fn)); return true; }
// reserve space on the stack jit::imm32 jit::allocai (imm32 n) { return jit_allocai (n); }
/* This function does all of lexing, parsing, and picking a good order of evaluation... Needless to say, this is not the best possible design, but it avoids cluttering everything with globals. */ pifi compile_rpn (char *expr) { struct stack_element stack[32]; int sp = 0; int curr_tos = -1; /* stack element currently in R0 */ int spill_base, spill_sp; pifi fn; int ofs; fn = (pifi) (jit_get_ip ().iptr); jit_leaf (1); ofs = jit_arg_i (); spill_sp = spill_base = jit_allocai (32 * sizeof (int)); while (*expr) { int with_imm; int imm; int tok; int src1, src2; /* This is the lexer. */ switch (*expr) { case ' ': case '\t': expr++; continue; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': stack[sp].kind = IMM; stack[sp++].imm = strtol (expr, &expr, 0); continue; case 'x': expr++; stack[sp++].kind = ARG; continue; case '~': /* NOT. Implemented as a XOR with -1. */ stack[sp].kind = IMM; stack[sp++].imm = ~0; tok = '^'; break; case '_': /* Unary minus. Transform to 0 - X and go on. Also used to enter negative constants (32_ = -32). */ expr++; stack[sp] = stack[sp - 1]; /* Ensure CURR_TOS is correct. */ if (curr_tos == sp - 1) curr_tos = sp; stack[sp - 1].kind = IMM; stack[sp - 1].imm = 0; sp++; tok = '-'; break; case '+': case '-': case '*': case '/': case '%': case '&': case '|': case '^': case '=': tok = *expr++; break; case '!': /* Get != */ expr++; assert (*expr == '='); tok = NE; break; case '<': /* Get <, <<, <= */ if (expr[1] == '=') expr += 2, tok = LE; else if (expr[1] == '<') expr += 2, tok = LSH; else expr++, tok = '<'; break; case '>': /* Get >, >>, >>>, >= */ if (expr[1] == '=') expr += 2, tok = GE; else if (expr[1] == '>' && expr[2] == '>') expr += 3, tok = RSHU; else if (expr[1] == '>') expr += 2, tok = RSH; else expr++, tok = '>'; break; default: abort (); } assert (sp >= 2); /* Constant folding. */ if (stack[sp - 1].kind == IMM && stack[sp - 2].kind == IMM) { stack[sp - 2].imm = fold (stack[sp - 2].imm, stack[sp - 1].imm, tok); sp--; continue; } /* If possible, ensure that the constant is the RHS, possibly by changing TOK (if it is a comparison). */ if (stack[sp - 2].kind == IMM) { int swapped_operation = swap_op (tok); if (swapped_operation) { tok = swapped_operation; stack[sp - 2].kind = stack[sp - 1].kind; stack[sp - 1].kind = IMM; stack[sp - 1].imm = stack[sp - 2].imm; /* Ensure CURR_TOS is correct. */ if (curr_tos == sp - 1) curr_tos = sp - 2; } } /* Get the second argument into a register, if not an immediate. Also decide which argument will be prepared into JIT_R0 and which will be prepared into JIT_V0. */ with_imm = 0; src1 = JIT_R0; src2 = JIT_V0; switch (stack[sp - 1].kind) { case IMM: /* RHS is an immediate, use an immediate instruction. */ with_imm = 1; imm = stack[sp - 1].imm; break; case EXPR: /* RHS is an expression, check if it is already in JIT_R0. */ if (curr_tos == sp - 1) { /* Invert the two sources. */ src1 = JIT_V0; src2 = JIT_R0; } else popr (JIT_V0, &spill_sp); curr_tos = -1; break; case ARG: jit_getarg_i (JIT_V0, ofs); break; } /* Get the first argument into a register indicated by SRC1. */ switch (stack[sp - 2].kind) { case IMM: /* LHS is an immediate, check if we must spill the top of stack. */ if (curr_tos != -1) { pushr (JIT_R0, &spill_sp); curr_tos = -1; } jit_movi_i (src1, stack[sp - 2].imm); break; case EXPR: /* LHS is an expression, check if it is already in JIT_R0. */ if (curr_tos != sp - 2) { popr (src1, &spill_sp); curr_tos = -1; } else assert (src1 == JIT_R0); break; case ARG: if (curr_tos != -1) { pushr (JIT_R0, &spill_sp); curr_tos = -1; } jit_getarg_i (src1, ofs); break; } /* Set up the new stack entry, which is cached in R0. */ sp -= 2; curr_tos = sp; stack[sp++].kind = EXPR; /* Perform the computation. */ if (with_imm) gen_reg_imm (src1, imm, tok); else gen_reg_reg (src1, src2, tok); } assert (sp == 1); switch (stack[0].kind) { case IMM: jit_movi_i (JIT_RET, stack[0].imm); break; case EXPR: assert (curr_tos == 0); jit_movr_i (JIT_RET, JIT_R0); break; case ARG: jit_getarg_i (JIT_V0, ofs); break; } jit_ret (); jit_flush_code ((char *) fn, jit_get_ip ().ptr); #ifdef LIGHTNING_DISASSEMBLE disassemble (stderr, (char *) fn, jit_get_ip ().ptr); #endif return fn; }