PyObject * PyNumber_Multiply(PyObject *v, PyObject *w) { PyTypeObject *tp = v->ob_type; PySequenceMethods *m; BINOP(v, w, "__mul__", "__rmul__", PyNumber_Multiply); if (tp->tp_as_number != NULL && w->ob_type->tp_as_sequence != NULL && !PyInstance_Check(v)) { /* number*sequence -- swap v and w */ PyObject *tmp = v; v = w; w = tmp; tp = v->ob_type; } if (tp->tp_as_number != NULL) { PyObject *x = NULL; PyObject * (*f)(PyObject *, PyObject *); if (PyInstance_Check(v)) { /* Instances of user-defined classes get their other argument uncoerced, so they may implement sequence*number as well as number*number. */ Py_INCREF(v); Py_INCREF(w); } else if (PyNumber_Coerce(&v, &w) != 0) return NULL; if ((f = v->ob_type->tp_as_number->nb_multiply) != NULL) x = (*f)(v, w); Py_DECREF(v); Py_DECREF(w); if (f != NULL) return x; } m = tp->tp_as_sequence; if (m && m->sq_repeat) { long mul_value; if (PyInt_Check(w)) { mul_value = PyInt_AsLong(w); } else if (PyLong_Check(w)) { mul_value = PyLong_AsLong(w); if (mul_value == -1 && PyErr_Occurred()) return NULL; } else { return type_error( "can't multiply sequence with non-int"); } return (*m->sq_repeat)(v, (int)mul_value); } return type_error("bad operand type(s) for *"); }
PyObject * PyNumber_Divmod(PyObject *v, PyObject *w) { BINOP(v, w, "__divmod__", "__rdivmod__", PyNumber_Divmod); if (v->ob_type->tp_as_number != NULL) { PyObject *x = NULL; PyObject * (*f)(PyObject *, PyObject *); if (PyNumber_Coerce(&v, &w) != 0) return NULL; if ((f = v->ob_type->tp_as_number->nb_divmod) != NULL) x = (*f)(v, w); Py_DECREF(v); Py_DECREF(w); if (f != NULL) return x; } return type_error("bad operand type(s) for divmod()"); }
static PyObject * do_pow(PyObject *v, PyObject *w) { PyObject *res; PyObject * (*f)(PyObject *, PyObject *, PyObject *); BINOP(v, w, "__pow__", "__rpow__", do_pow); if (v->ob_type->tp_as_number == NULL || w->ob_type->tp_as_number == NULL) { PyErr_SetString(PyExc_TypeError, "pow(x, y) requires numeric arguments"); return NULL; } if (PyNumber_Coerce(&v, &w) != 0) return NULL; if ((f = v->ob_type->tp_as_number->nb_power) != NULL) res = (*f)(v, w, Py_None); else res = type_error("pow(x, y) not defined for these operands"); Py_DECREF(v); Py_DECREF(w); return res; }
PyObject * PyNumber_Remainder(PyObject *v, PyObject *w) { if (PyString_Check(v)) return PyString_Format(v, w); else if (PyUnicode_Check(v)) return PyUnicode_Format(v, w); BINOP(v, w, "__mod__", "__rmod__", PyNumber_Remainder); if (v->ob_type->tp_as_number != NULL) { PyObject *x = NULL; PyObject * (*f)(PyObject *, PyObject *); if (PyNumber_Coerce(&v, &w) != 0) return NULL; if ((f = v->ob_type->tp_as_number->nb_remainder) != NULL) x = (*f)(v, w); Py_DECREF(v); Py_DECREF(w); if (f != NULL) return x; } return type_error("bad operand type(s) for %"); }
PyObject * PyNumber_Add(PyObject *v, PyObject *w) { PySequenceMethods *m; BINOP(v, w, "__add__", "__radd__", PyNumber_Add); m = v->ob_type->tp_as_sequence; if (m && m->sq_concat) return (*m->sq_concat)(v, w); else if (v->ob_type->tp_as_number != NULL) { PyObject *x = NULL; PyObject * (*f)(PyObject *, PyObject *); if (PyNumber_Coerce(&v, &w) != 0) return NULL; if ((f = v->ob_type->tp_as_number->nb_add) != NULL) x = (*f)(v, w); Py_DECREF(v); Py_DECREF(w); if (f != NULL) return x; } return type_error("bad operand type(s) for +"); }
void GMachine::execute(GEnvironment& environment) { const std::vector<GInstruction>& code = environment.combinator->instructions; StackFrame<Address>& stack = environment.stack; for (size_t index = 0; index < code.size(); index++) { const GInstruction& instruction = code[index]; switch (instruction.op) { case GOP::ALLOC: { for (int i = 0; i < instruction.value; i++) { heap.push_back(Node(nullptr)); environment.stack.push(Address::indirection(&heap.back())); } } break; case GOP::EVAL: { static SuperCombinator unwind { "__uniwnd", Type(), 0, std::vector<GInstruction>{ GInstruction(GOP::UNWIND) } }; GEnvironment child = environment.child(&unwind); child.stack.push(environment.stack.top()); execute(child); environment.stack.top() = child.stack.top(); } break; case GOP::MKAP: { Address func = environment.stack.top(); environment.stack.pop(); Address arg = environment.stack.top(); heap.push_back(Node(func, arg)); environment.stack.top() = Address::application(&heap.back()); } break; case GOP::PACK: { int tag = instruction.value & (0xFFFF);//Low two bytes int arity = instruction.value >> 16;//High two bytes heap.push_back(Node(tag, new Address[arity+1])); Node& ctor = heap.back(); for (int ii = 0; ii < arity; ii++) { ctor.constructor.arguments[ii] = stack.pop(); } ctor.constructor.arguments[arity + 1] = Address::indirection(nullptr);//Use as end of this constructor stack.push(Address::constructor(&ctor)); } break; case GOP::SPLIT: { Address top = stack.pop(); assert(top.getType() == CONSTRUCTOR); ConstructorNode& ctor = top.getNode()->constructor; for (int ii = 0; ii < instruction.value; ii++) { assert(ctor.arguments[ii].getType() != NodeType::INDIRECTION || ctor.arguments[ii].getNode() != nullptr); stack.push(ctor.arguments[ii]); } } break; case GOP::CASEJUMP: { Address top = stack.top(); assert(top.getType() == CONSTRUCTOR); ConstructorNode& ctor = top.getNode()->constructor; if (ctor.tag != instruction.value) index++;//Skip the next instruction which is the jump instruction } break; case GOP::JUMP: index = instruction.value - 1; break; case GOP::POP: for (int i = 0; i < instruction.value; i++) { environment.stack.pop(); } break; case GOP::PUSH: { Address addr = environment.stack[instruction.value]; environment.stack.push(addr); } break; case GOP::PUSH_DICTIONARY_MEMBER: { assert(stack.base().getType() == NodeType::CONSTRUCTOR);//Must be instance dictionary ConstructorNode& ctor = stack.base().getNode()->constructor; Address& func = ctor.arguments[instruction.value]; stack.push(func); } break; case GOP::PUSH_GLOBAL: { Address addr = globals.at(instruction.value); environment.stack.push(addr); } break; case GOP::PUSH_INT: { heap.push_back(Node(instruction.value)); environment.stack.push(Address::number(&heap.back())); } break; case GOP::PUSH_DOUBLE: { heap.push_back(Node(instruction.doubleValue)); environment.stack.push(Address::numberDouble(&heap.back())); } break; case GOP::SLIDE: { slide(environment, instruction); } break; case GOP::UNWIND: { Address top = environment.stack.top(); switch (top.getType()) { case NUMBER: break; case APPLICATION: { Node& n = *top.getNode(); environment.stack.push(n.apply.func); --index;//Redo the unwind instruction } break; case FUNCTION_POINTER: { int arity = top.getNode()->function.args; if (stack.stackSize() - 1 < size_t(arity)) { while (stack.stackSize() > 1) { stack.pop(); } } else { size_t ii = environment.stack.stackSize() - arity - 1; for (; ii < environment.stack.stackSize() - 1; ii++) { Address& addr = environment.stack[ii]; assert(addr.getType() == APPLICATION); addr = addr.getNode()->apply.arg; } t_ffi_func func = top.getNode()->function.ptr; assert(func != nullptr); StackFrame<Address> newStack = stack.makeChildFrame(arity + 1); func(this, &newStack); Address result = newStack.top(); for (int i = 0; i < arity; i++) environment.stack.pop(); environment.stack.push(result); } } break; case GLOBAL: { SuperCombinator* comb = top.getNode()->global; if (environment.stack.stackSize() - 1 < size_t(comb->arity)) { while (stack.stackSize() > 1) { stack.pop(); } } else { //Before calling the function, replace all applications on the stack with the actual arguments //This gives faster access to a functions arguments when using PUSH size_t ii = environment.stack.stackSize() - comb->arity - 1; for (; ii < environment.stack.stackSize() - 1; ii++) { Address& addr = environment.stack[ii]; assert(addr.getType() == APPLICATION); addr = addr.getNode()->apply.arg; } GEnvironment child = environment.child(comb); if (debug) { std::cerr << "Executing function '" << comb->name << "'" << std::endl; std::cerr << "Arguments { "; for (size_t i = 0; i < child.stack.stackSize(); i++) { std::cerr << child.stack[i]; } std::cerr << " }" << std::endl; } execute(child); Address result = child.stack.top(); for (int i = 0; i < comb->arity; i++) environment.stack.pop(); environment.stack.push(result); } } break; case INDIRECTION: { environment.stack.top() = top.getNode()->indirection; --index;//Redo the unwind instruction } break; default: break; } } break; case GOP::UPDATE: { Address top = environment.stack.top(); heap.push_back(Node(top)); environment.stack[instruction.value] = Address::indirection(&heap.back()); } break; #define BINOP2(op, opname) \ case GOP:: opname:\ {\ Address rhs = environment.stack.pop(); \ Address lhs = environment.stack.top(); \ int result = lhs.getNode()->number op rhs.getNode()->number; \ heap.push_back(Node(result)); \ environment.stack.top() = Address::number(&heap.back()); \ }\ break; #define BINOP(f, name) case GOP:: name: binopInt<f>(environment, heap); break; BINOP(add<int>, ADD) BINOP(subtract<int>, SUBTRACT) BINOP(multiply<int>, MULTIPLY) BINOP(divide<int>, DIVIDE) BINOP(remainder<int>, REMAINDER) #define BINOP_DOUBLE(f, name) case GOP:: name: binopDouble<f>(environment, heap); break; BINOP_DOUBLE(add<double>, ADD_DOUBLE) BINOP_DOUBLE(subtract<double>, SUBTRACT_DOUBLE) BINOP_DOUBLE(multiply<double>, MULTIPLY_DOUBLE) BINOP_DOUBLE(divide<double>, DIVIDE_DOUBLE) #undef BINOP_DOUBLE case GOP::NEGATE: { Address x = environment.stack.top(); heap.push_back(Node(-x.getNode()->number)); environment.stack.top() = Address::number(&heap.back()); } break; BINOP2(== , COMPARE_EQ) BINOP2(!= , COMPARE_NEQ) BINOP2(> , COMPARE_GT) BINOP2(>=, COMPARE_GE) BINOP2(< , COMPARE_LT) BINOP2(<=, COMPARE_LE) #undef BINOP #undef BINOP2 default: std::cout << "Unimplemented instruction " << int(code[index].op) << std::endl; break; } } if (debug) { std::cerr << "Returning '" << stack.top() << "' from '" << environment.combinator->name << "'" << std::endl; } }
/* Build various compound expressions, and verify that they have sane debug strings. */ void create_code (gcc_jit_context *ctxt, void *user_data) { /* Make a singly-linked list type: struct node { struct node *next; int value; }; */ gcc_jit_type *t_int = gcc_jit_context_get_type (ctxt, GCC_JIT_TYPE_INT); gcc_jit_struct *t_node = gcc_jit_context_new_opaque_struct (ctxt, NULL, "node"); gcc_jit_type *t_node_ptr = gcc_jit_type_get_pointer (gcc_jit_struct_as_type (t_node)); gcc_jit_field *f_next = gcc_jit_context_new_field (ctxt, NULL, t_node_ptr, "next"); gcc_jit_field *f_value = gcc_jit_context_new_field (ctxt, NULL, t_int, "value"); gcc_jit_field *fields[] = {f_next, f_value}; gcc_jit_struct_set_fields (t_node, NULL, 2, fields); /* Create a dummy function so that we have locals/params to build expressions with. */ gcc_jit_type *t_void = gcc_jit_context_get_type (ctxt, GCC_JIT_TYPE_VOID); gcc_jit_function *fn = gcc_jit_context_new_function (ctxt, NULL, GCC_JIT_FUNCTION_EXPORTED, t_void, "test_debug_strings", 0, NULL, 0); gcc_jit_rvalue *ptr = gcc_jit_lvalue_as_rvalue ( gcc_jit_function_new_local (fn, NULL, t_node_ptr, "ptr")); gcc_jit_rvalue *a = gcc_jit_lvalue_as_rvalue ( gcc_jit_function_new_local (fn, NULL, t_int, "a")); gcc_jit_rvalue *b = gcc_jit_lvalue_as_rvalue ( gcc_jit_function_new_local (fn, NULL, t_int, "b")); gcc_jit_rvalue *c = gcc_jit_lvalue_as_rvalue ( gcc_jit_function_new_local (fn, NULL, t_int, "c")); gcc_jit_rvalue *d = gcc_jit_lvalue_as_rvalue ( gcc_jit_function_new_local (fn, NULL, t_int, "d")); #define CHECK_RVALUE_DEBUG_STRING(RVALUE, EXPECTED) \ CHECK_STRING_VALUE ( \ gcc_jit_object_get_debug_string (gcc_jit_rvalue_as_object (RVALUE)), \ (EXPECTED)) #define CHECK_LVALUE_DEBUG_STRING(LVALUE, EXPECTED) \ CHECK_STRING_VALUE ( \ gcc_jit_object_get_debug_string (gcc_jit_lvalue_as_object (LVALUE)), \ (EXPECTED)) /* Verify various simple compound expressions. */ { CHECK_RVALUE_DEBUG_STRING (ptr, "ptr"); gcc_jit_lvalue *deref = gcc_jit_rvalue_dereference_field (ptr, NULL, f_value); CHECK_LVALUE_DEBUG_STRING (deref, "ptr->value"); gcc_jit_rvalue *deref_as_rvalue = gcc_jit_lvalue_as_rvalue (deref); #define BINOP(OP, A, B) \ gcc_jit_context_new_binary_op (ctxt, NULL, \ GCC_JIT_BINARY_OP_##OP, t_int, (A), (B)) #define COMPARISON(OP, A, B) \ gcc_jit_context_new_comparison (ctxt, NULL, \ GCC_JIT_COMPARISON_##OP,(A), (B)) CHECK_RVALUE_DEBUG_STRING ( BINOP (PLUS, deref_as_rvalue, deref_as_rvalue), "ptr->value + ptr->value"); CHECK_RVALUE_DEBUG_STRING ( BINOP (MULT, deref_as_rvalue, deref_as_rvalue), "ptr->value * ptr->value"); /* Multiplication has higher precedence in C than addition, so this dump shouldn't contain parentheses. */ CHECK_RVALUE_DEBUG_STRING ( BINOP (PLUS, BINOP (MULT, a, b), BINOP (MULT, c, d)), "a * b + c * d"); /* ...but this one should. */ CHECK_RVALUE_DEBUG_STRING ( BINOP (MULT, BINOP (PLUS, a, b), BINOP (PLUS, c, d)), "(a + b) * (c + d)"); /* Equal precedences don't need parentheses. */ CHECK_RVALUE_DEBUG_STRING ( BINOP (MULT, BINOP (MULT, a, b), BINOP (MULT, c, d)), "a * b * c * d"); /* Comparisons and logical ops. */ CHECK_RVALUE_DEBUG_STRING ( COMPARISON (LT, a, b), "a < b"); CHECK_RVALUE_DEBUG_STRING ( BINOP (LOGICAL_AND, COMPARISON (LT, a, b), COMPARISON (GT, c, d)), "a < b && c > d"); CHECK_RVALUE_DEBUG_STRING ( BINOP (LOGICAL_AND, BINOP (LOGICAL_OR, COMPARISON (LT, a, b), COMPARISON (LT, a, c)), BINOP (LOGICAL_OR, COMPARISON (GT, d, b), COMPARISON (GT, d, c))), "(a < b || a < c) && (d > b || d > c)"); CHECK_RVALUE_DEBUG_STRING ( BINOP (LOGICAL_OR, BINOP (LOGICAL_AND, COMPARISON (LT, a, b), COMPARISON (LT, a, c)), BINOP (LOGICAL_AND, COMPARISON (GT, d, b), COMPARISON (GT, d, c))), "a < b && a < c || d > b && d > c"); #undef BINOP #undef COMPARISON } /* PR jit/66539 "Missing parentheses in jit dumps". Construct the equivalent of ((cast)ptr->next)->next and verify that the appropriate parentheses appear in the debug string. */ { /* "ptr->next". */ gcc_jit_lvalue *inner_deref = gcc_jit_rvalue_dereference_field (ptr, NULL, f_next); /* "((node *)ptr->next)"; the cast is redundant, purely to exercise dumping. */ gcc_jit_rvalue *test_cast = gcc_jit_context_new_cast (ctxt, NULL, gcc_jit_lvalue_as_rvalue (inner_deref), t_node_ptr); /* "((node *)ptr->next)->next". */ gcc_jit_lvalue *outer_deref = gcc_jit_rvalue_dereference_field (test_cast, /* gcc_jit_rvalue *ptr */ NULL, /* gcc_jit_location *loc */ f_next); /* gcc_jit_field *field */ CHECK_LVALUE_DEBUG_STRING (outer_deref, "((struct node *)ptr->next)->next"); } #undef CHECK_LVALUE_DEBUG_STRING }
/* The actual interpreter loop */ void interp(state_t *st) { while (1) { obj_t *x, *y, *z; char *p, c; int i; unsigned long u; /* fprintf(stderr, "Executing '%c'\n", *st->pc); */ switch (c = *st->pc++) { case '\0': return; case '\r': case '\n': case '\t': case ' ': /* nop */ break; case '!': /* call */ POP(x); if (x->type != function) { error("Not a function.\n"); } else { CALL(x); decref(x); } break; case '"': /* output string */ p = skip_string(st->pc); c = *p; /* Save old character */ *p = '\0'; /* Change it to NUL */ fputs(st->pc, stdout); /* Print the string */ *p = c; /* Replace original character */ st->pc = p + 1; /* Set the PC to one past the closing quote*/ break; case '#': /* over */ POP(y); POP(x); PUS(x); PUS(y); PUSH(x); break; case '$': /* dup */ POP(x); PUS(x); PUSH(x); break; case '%': /* mod */ BINOP(mpz_mod); break; case '&': /* && */ BINBOOL(NONZERO(x) && NONZERO(y)); break; case '\'': /* Set multi */ for (p = st->pc; *p && *p != '\''; p++); st->pc = p + 1; for (p--; *p != '\''; p--) { if (*p >= 'a' && *p <= 'z') { /* local */ POP(x); i = *p - 'a'; if ((y = st->frame->vars[i]) != NULL) decref(y); st->frame->vars[i] = x; } else if (*p >= 'A' && *p <= 'Z') { /* global */ POP(x); i = *p - 'A'; if ((y = st->vars[i]) != NULL) decref(y); st->vars[i] = x; } else if (*p != '\n' && *p != '\t' && *p != ' ') { error("Not a variable: '%c'", *p); } } break; case '(': /* Comment; can't be nested */ st->pc = skip_comment(st->pc); break; case '*': /* mul */ BINOP(mpz_mul); break; case '+': /* add */ BINOP(mpz_add); break; case ',': /* print character */ POP(x); u = to_ulong(x); putchar(u); decref(x); break; case '-': /* sub */ BINOP(mpz_sub); break; case '.': /* print */ POP(x); print_obj(stdout, x); decref(x); break; case '/': /* div */ BINOP(mpz_fdiv_q); break; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': p = st->pc; /* Skip to the first non-digit */ while ((c = *p) >= '0' && c <= '9') p++; *p = '\0'; /* Temporarily put a NUL there */ PUS(num_new_from_str(st->pc-1)); *p = c; /* Replace original character */ st->pc = p; break; case ':': /* Set variable */ POP(x); c = *st->pc++; if (c >= 'A' && c <= 'Z') { /* global */ i = c - 'A'; if ((y = st->vars[i]) != NULL) decref(y); st->vars[i] = x; } else if (c >= 'a' && c <= 'z') { /* local */ i = c - 'a'; if ((y = st->frame->vars[i]) != NULL) decref(y); st->frame->vars[i] = x; } else { error("Not a variable: '%c'\n", *st->pc); } break; /* case ';': */ /* break; */ case '<': /* less than */ BINBOOL(mpz_cmp(*to_mpz(x), *to_mpz(y)) < 0); break; case '=': /* equal to */ BINBOOL(mpz_cmp(*to_mpz(x), *to_mpz(y)) == 0); break; case '>': /* greater than */ BINBOOL(mpz_cmp(*to_mpz(x), *to_mpz(y)) > 0); break; case '?': /* if */ POP(z); POP(y); POP(x); if (x->type != number || y->type != function || z->type != function) error("Wrong argument type."); CALL(NONZERO(x) ? y : z); decref(z); decref(y); decref(x); break; case '@': /* rot */ POP(z); POP(y); POP(x); PUS(y); PUS(z); PUS(x); break; /* global variables */ case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': case 'G': case 'H': case 'I': case 'J': case 'K': case 'L': case 'M': case 'N': case 'O': case 'P': case 'Q': case 'R': case 'S': case 'T': case 'U': case 'V': case 'W': case 'X': case 'Y': case 'Z': /* global variables */ i = c - 'A'; if ((x = st->vars[i]) == NULL) { error("Uninitialized variable '%c'\n", c); } if (x->type == function) { CALL(x); } else { PUSH(x); } break; case '[': PUS(fun_new(st->pc)); i = 1; while (*st->pc) { switch (*st->pc++) { case '"': st->pc = skip_string(st->pc) + 1; break; case '(': st->pc = skip_comment(st->pc) + 1; break; case '[': i++; break; case ']': if (--i <= 0) goto done; break; } } done: break; case '\\': /* swap */ POP(y); POP(x); PUS(y); PUS(x); break; case ']': POPRET(); break; case '^': /* trace */ print_trace(st); break; /* case '^': /\* pow *\/ */ /* POP(y); POP(x); */ /* z = num_new(); */ /* mpz_pow_ui(z->data.mpz, *to_mpz(x), to_ulong(y)); */ /* PUSH(z); */ /* decref(y); decref(x); */ /* break; *\/ */ case '_': /* neg */ POP(x); mpz_neg(*to_mpz(x), x->data.mpz); PUS(x); break; case '`': /* drop */ POP(x); decref(x); break; case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': case 'g': case 'h': case 'i': case 'j': case 'k': case 'l': case 'm': case 'n': case 'o': case 'p': case 'q': case 'r': case 's': case 't': case 'u': case 'v': case 'w': case 'x': case 'y': case 'z': /* local variables */ i = c - 'a'; if ((x = st->frame->vars[i]) == NULL) { error("Uninitialized variable '%c'\n", c); } if (x->type == function) { CALL(x); } else { PUSH(x); } break; /* case '{': */ /* break; */ case '|': /* or */ BINBOOL(NONZERO(x) || NONZERO(y)); break; /* case '}': */ /* break; */ case '~': /* not */ POP(x); PUSH(ZERO(x) ? one : zero); decref(x); break; default: error("Undefined token '%c'\n", c); } } }