#include "codegen.h" #include "codemanager.h" #include "Python/frames.h" #include "Python/pycompiler.h" #include #include "pycodegen.h" /*** The processor-independent part of the code generator. ***/ DEFINEFN void psyco_emit_header(PsycoObject* po, int nframelocal) { int j = nframelocal; vinfo_array_t* array; extra_assert(LOC_CONTINUATION->array->count == 0); BEGIN_CODE INITIALIZE_FRAME_LOCALS(nframelocal); po->stack_depth += 4*nframelocal; END_CODE array = LOC_CONTINUATION->array = array_new(nframelocal); while (j--) array->items[j] = vinfo_new(RunTime_NewStack( po->stack_depth - sizeof(long)*j, false, false)); } #define INITIAL_STACK_DEPTH_INUSE (INITIAL_STACK_DEPTH + \ (!NEED_STACK_FRAME_HACK) * sizeof(long)) DEFINEFN code_t* psyco_finish_return(PsycoObject* po, Source retval) { code_t* code = po->code; int retpos; int nframelocal = LOC_CONTINUATION->array->count; /* 'retpos' is the position in the stack of the return address. */ retpos = getstack(LOC_CONTINUATION->source); extra_assert(retpos != RunTime_StackNone); /* write the epilogue */ WRITE_FRAME_EPILOGUE(retval, nframelocal); /* now clean up the stack up to retpos */ STACK_CORRECTION(retpos - po->stack_depth); /* emit the 'RET' instruction */ retpos -= INITIAL_STACK_DEPTH_INUSE; FUNCTION_RET(retpos); PsycoObject_Delete(po); return code; } /***************************************************************/ /*** Condition Codes (a.k.a. the processor 'flags' register) ***/ #if HAVE_CCREG static source_virtual_t cc_functions_table[CC_TOTAL]; DEFINEFN condition_code_t cc_from_vsource(Source source) { source_virtual_t* sv = VirtualTime_Get(source); return (condition_code_t) (sv - cc_functions_table); } static bool generic_computed_cc(PsycoObject* po, vinfo_t* v) { /* also upon forking, because the condition codes cannot be sent as arguments (function calls typically require adds and subs to adjust the stack). */ /* 'v' should be one of the po->ccregs[_] here */ BEGIN_CODE code = psyco_compute_cc(po, code #if REG_TOTAL > 0 , REG_NONE #endif ); END_CODE return true; } DEFINEFN vinfo_t* psyco_vinfo_condition(PsycoObject* po, condition_code_t cc) { vinfo_t* result; if ((int)cc < CC_TOTAL) { int index = INDEX_CC(cc); if (po->ccregs[index] != NULL) { /* there is already a value in the processor flags register */ condition_code_t prevcc = psyco_vsource_cc(po->ccregs[index]->source); extra_assert(prevcc != CC_ALWAYS_FALSE); if (prevcc == cc) { /* it is the same condition, so reuse it */ result = po->ccregs[index]; vinfo_incref(result); return result; } else { /* it is not the same condition at all, save the old one and make a new one (should never occur with the ivm backend) */ BEGIN_CODE NEED_CC(); END_CODE } } extra_assert(po->ccregs[index] == NULL); result = vinfo_new(VirtualTime_New(cc_functions_table+(int)cc)); po->ccregs[index] = result; } else result = vinfo_new(CompileTime_New(cc == CC_ALWAYS_TRUE)); return result; } DEFINEFN VirtualTimeSource psyco_source_condition(condition_code_t cc) { return VirtualTime_New(cc_functions_table+(int)cc); } DEFINEFN condition_code_t psyco_vsource_cc(Source source) { if (is_virtualtime(source)) { source_virtual_t* sv = VirtualTime_Get(source); if (cc_functions_table <= sv && sv < cc_functions_table+CC_TOTAL) { /* 'sv' points within the cc_functions_table */ return (condition_code_t) (sv - cc_functions_table); } } return CC_ALWAYS_FALSE; } DEFINEFN void psyco_resolved_cc(PsycoObject* po, condition_code_t cc_known_true) { if ((int)cc_known_true < CC_TOTAL) { vinfo_t* v; int index; index = INDEX_CC(cc_known_true); v = po->ccregs[index]; if (v != NULL && psyco_vsource_cc(v->source) == cc_known_true) { sk_incref(&psyco_skOne); v->source = CompileTime_NewSk(&psyco_skOne); po->ccregs[index] = NULL; } index = INDEX_CC(INVERT_CC(cc_known_true)); v = po->ccregs[index]; if (v != NULL && psyco_vsource_cc(v->source) == INVERT_CC(cc_known_true)) { sk_incref(&psyco_skZero); v->source = CompileTime_NewSk(&psyco_skZero); po->ccregs[index] = NULL; } } } #else /* if !HAVE_CCREG */ # error "this was meant for ivm, but now ivm also defines HAVE_CCREG." #endif /* HAVE_CCREG */ /*****************************************************************/ /*** run-time switches ***/ static bool computed_promotion(PsycoObject* po, vinfo_t* v) { /* uncomputable, but still use the address of computed_promotion() as a tag to figure out if a virtual source is a c_promotion_s structure. */ return psyco_vsource_not_important.compute_fn(po, v); } DEFINEVAR c_promotion_t psyco_nonfixed_promotion; DEFINEVAR c_promotion_t psyco_nonfixed_pyobj_promotion; /*DEFINEVAR c_promotion_t psyco_nonfixed_promotion_mega;*/ DEFINEVAR c_promotion_t psyco_nonfixed_pyobj_promotion_mega; DEFINEFN bool psyco_vsource_is_promotion(VirtualTimeSource source) { return VirtualTime_Get(source)->compute_fn == &computed_promotion; } /*****************************************************************/ /*** Calling C functions ***/ #define MAX_ARGUMENTS_COUNT 16 DEFINEFN vinfo_t* psyco_generic_call(PsycoObject* po, void* c_function, int flags, const char* arguments, ...) { char argtags[MAX_ARGUMENTS_COUNT]; long raw_args[MAX_ARGUMENTS_COUNT], args[MAX_ARGUMENTS_COUNT]; int count, i, j, stackbase, totalstackspace = 0; vinfo_t* vresult; bool has_refs = false; va_list vargs; #ifdef HAVE_STDARG_PROTOTYPES va_start(vargs, arguments); #else va_start(vargs); #endif extra_assert(c_function != NULL); for (count=0; arguments[count]; count++) { long arg; char tag; vinfo_t* vi; extra_assert(count <= MAX_ARGUMENTS_COUNT); raw_args[count] = arg = va_arg(vargs, long); tag = arguments[count]; switch (tag) { case 'l': break; case 'v': /* Compute all values first */ vi = (vinfo_t*) arg; if (!compute_vinfo(vi, po)) return NULL; if (!is_compiletime(vi->source)) { flags &= ~CfPure; } else { /* compile-time: get the value */ arg = CompileTime_Get(vi->source)->value; tag = 'l'; } break; case 'r': /* Push by-reference values in the stack now */ vi = (vinfo_t*) arg; extra_assert(is_runtime(vi->source)); #if REG_TOTAL > 0 if (getstack(vi->source) == RunTime_StackNone) { reg_t rg = getreg(vi->source); if (rg == REG_NONE) { /* for undefined sources, pushing just any register will be fine */ rg = REG_ANY_CALLER_SAVED; } BEGIN_CODE SAVE_REG_VINFO(vi, rg); END_CODE } #endif arg = RunTime_NewStack(getstack(vi->source), false, false); has_refs = true; break; case 'a': case 'A': has_refs = true; totalstackspace += sizeof(long) * ((vinfo_array_t*) arg)->count; break; default: Py_FatalError("unknown character argument in" " psyco_generic_call()"); } args[count] = arg; argtags[count] = tag; } va_end(vargs); if (flags & CfPure) { /* calling a pure function with no run-time argument */ long result; if (has_refs) { for (i = 0; i < count; i++) { if (argtags[i] == 'a' || argtags[i] == 'A') { int cnt = ((vinfo_array_t*)args[i])->count; args[i] = (long)malloc(cnt*sizeof(long)); } #if ALL_CHECKS if (argtags[i] == 'r') Py_FatalError("psyco_generic_call(): arg mode " "incompatible with CfPure"); #endif } } result = psyco_call_var(c_function, count, args); if (PyErr_Occurred()) { if (has_refs) for (i = 0; i < count; i++) if (argtags[i] == 'a' || argtags[i] == 'A') free((void*)args[i]); psyco_virtualize_exception(po); return NULL; } if (has_refs) { for (i = 0; i < count; i++) if (argtags[i] == 'a' || argtags[i] == 'A') { vinfo_array_t* array = (vinfo_array_t*)raw_args[i]; long sk_flag = (argtags[i] == 'a') ? 0 : SkFlagPyObj; for (j = 0; j < array->count; j++) { array->items[j] = vinfo_new(CompileTime_NewSk( sk_new( ((long*)args[i])[j], sk_flag))); } free((void*)args[i]); } } if (flags & CfPyErrMask) { /* such functions are rarely pure, but there are exceptions with CfPyErrNotImplemented */ vresult = generic_call_ct(flags, result); if (vresult != NULL) return vresult; } switch (flags & CfReturnMask) { case CfReturnNormal: vresult = vinfo_new(CompileTime_New(result)); break; case CfReturnRef: vresult = vinfo_new(CompileTime_NewSk(sk_new(result, SkFlagPyObj))); break; default: vresult = (vinfo_t*) 1; /* anything non-NULL */ } return vresult; } #ifdef CALL_SET_ARG_FROM_ADDR_CLOBBER_REG if (has_refs) { /* we will need a trash register to compute the references we push later. The following three lines prevent another argument which would currently be in the same trash register from being pushed from the register after we clobbered it. */ BEGIN_CODE NEED_REGISTER(CALL_SET_ARG_FROM_ADDR_CLOBBER_REG); END_CODE } #endif BEGIN_CODE NEED_CC(); for (count=0; arguments[count]; count++) { if (argtags[count] == 'v') { /* We collect all the sources in 'args' now, before SAVE_REGS_FN_CALLS which might move some run-time values into the stack. In this case the old copy in the registers is still useable to PUSH it for the C function call. */ RunTimeSource src = ((vinfo_t*)(args[count]))->source; args[count] = (long) src; } } SAVE_REGS_FN_CALLS(false); /* CC saved above */ stackbase = po->stack_depth; po->stack_depth += totalstackspace; STACK_CORRECTION(totalstackspace); for (i=count; i--; ) { switch (argtags[i]) { case 'v': CALL_SET_ARG_FROM_RT (args[i], i, count); break; case 'r': CALL_SET_ARG_FROM_ADDR (args[i], i, count); break; case 'a': case 'A': { vinfo_array_t* array = (vinfo_array_t*) args[i]; bool with_reference = (argtags[i] == 'A'); int j = array->count; while (j > 0) { stackbase += sizeof(long); array->items[--j] = vinfo_new( RunTime_NewStack(stackbase, with_reference, false)); } CALL_SET_ARG_FROM_ADDR(array->items[0]->source, i,count); break; } default: CALL_SET_ARG_IMMED (args[i], i, count); break; } } CALL_C_FUNCTION (c_function, count); END_CODE switch (flags & CfReturnMask) { case CfReturnNormal: vresult = bfunction_result(po, false); break; case CfReturnRef: vresult = bfunction_result(po, true); break; default: if ((flags & CfPyErrMask) == 0) return (vinfo_t*) 1; /* anything non-NULL */ vresult = bfunction_result(po, false); vresult = generic_call_check(po, flags, vresult); if (vresult == NULL) goto error_detected; vinfo_decref(vresult, po); return (vinfo_t*) 1; /* anything non-NULL */ } if (flags & CfPyErrMask) { vresult = generic_call_check(po, flags, vresult); if (vresult == NULL) goto error_detected; } return vresult; error_detected: /* if the called function returns an error, we then assume that it did not actually fill the arrays */ if (has_refs) { for (i = 0; i < count; i++) if (argtags[i] == 'a' || argtags[i] == 'A') { vinfo_array_t* array = (vinfo_array_t*)args[i]; int j = array->count; while (j--) { vinfo_t* v = array->items[j]; array->items[j] = NULL; v->source = remove_rtref(v->source); vinfo_decref(v, po); } } } return NULL; } DEFINEFN void psyco_inline_enter(PsycoObject* po) { struct stack_frame_info_s* finfo = psyco_finfo(NULL, po); BEGIN_CODE ABOUT_TO_CALL_SUBFUNCTION((long) finfo); END_CODE } DEFINEFN void psyco_inline_exit (PsycoObject* po) { BEGIN_CODE RETURNED_FROM_SUBFUNCTION(); END_CODE } /*****************************************************************/ /*** Emit common instructions ***/ /* forward */ static condition_code_t int_cmp_i(PsycoObject* po, vinfo_t* rt1, long immed2, int py_op); DEFINEFN condition_code_t integer_non_null(PsycoObject* po, vinfo_t* vi) { condition_code_t result; if (is_virtualtime(vi->source)) { result = psyco_vsource_cc(vi->source); if (result != CC_ALWAYS_FALSE) return result; if (!compute_vinfo(vi, po)) return CC_ERROR; } if (is_compiletime(vi->source)) { if (CompileTime_Get(vi->source)->value != 0) return CC_ALWAYS_TRUE; else return CC_ALWAYS_FALSE; } BEGIN_CODE CHECK_NONZERO_FROM_RT(vi->source, result); END_CODE return result; } DEFINEFN condition_code_t integer_NON_NULL(PsycoObject* po, vinfo_t* vi) { condition_code_t result; if (vi == NULL) return CC_ERROR; result = integer_non_null(po, vi); /* 'vi' cannot be a reference to a Python object if we are asking ourselves if it is NULL or not. So the following vinfo_decref() will not emit a Py_DECREF() that would clobber the condition code. We check all this. */ #if ALL_CHECKS extra_assert(!has_rtref(vi->source)); { code_t* code1 = po->code; #endif vinfo_decref(vi, po); #if ALL_CHECKS extra_assert(po->code == code1); } #endif return result; } #define GENERIC_BINARY_HEADER \ if (!compute_vinfo(v2, po) || !compute_vinfo(v1, po)) return NULL #define GENERIC_BINARY_HEADER_i \ if (!compute_vinfo(v1, po)) return NULL #define GENERIC_BINARY_CT_CT(c_code) \ if (is_compiletime(v1->source) && is_compiletime(v2->source)) \ { \ long a = CompileTime_Get(v1->source)->value; \ long b = CompileTime_Get(v2->source)->value; \ long c = (c_code); \ return vinfo_new(CompileTime_New(c)); \ } DEFINEFN vinfo_t* integer_add(PsycoObject* po, vinfo_t* v1, vinfo_t* v2, bool ovf) { GENERIC_BINARY_HEADER; if (is_compiletime(v1->source)) { long a = CompileTime_Get(v1->source)->value; if (a == 0) { /* adding zero to v2 */ vinfo_incref(v2); return v2; } if (is_compiletime(v2->source)) { long b = CompileTime_Get(v2->source)->value; long c = a + b; if (ovf && (c^a) < 0 && (c^b) < 0) return NULL; /* overflow */ return vinfo_new(CompileTime_New(c)); } if (!ovf) return bint_add_i(po, v2, a, false); } else if (is_compiletime(v2->source)) { long b = CompileTime_Get(v2->source)->value; if (b == 0) { /* adding zero to v1 */ vinfo_incref(v1); return v1; } if (!ovf) return bint_add_i(po, v1, b, false); } return BINARY_INSTR_ADD(ovf, ovf && is_nonneg(v1->source) && is_nonneg(v2->source)); } DEFINEFN vinfo_t* integer_add_i(PsycoObject* po, vinfo_t* v1, long value2, bool unsafe) { if (value2 == 0) { /* adding zero to v1 */ vinfo_incref(v1); return v1; } else { GENERIC_BINARY_HEADER_i; if (is_compiletime(v1->source)) { long c = CompileTime_Get(v1->source)->value + value2; return vinfo_new(CompileTime_New(c)); } return bint_add_i(po, v1, value2, unsafe); } } DEFINEFN vinfo_t* integer_sub(PsycoObject* po, vinfo_t* v1, vinfo_t* v2, bool ovf) { GENERIC_BINARY_HEADER; if (is_compiletime(v1->source)) { long a = CompileTime_Get(v1->source)->value; if (is_compiletime(v2->source)) { long b = CompileTime_Get(v2->source)->value; long c = a - b; if (ovf && (c^a) < 0 && (c^~b) < 0) return NULL; /* overflow */ return vinfo_new(CompileTime_New(c)); } } else if (is_compiletime(v2->source)) { long b = CompileTime_Get(v2->source)->value; if (b == 0) { /* subtracting zero from v1 */ vinfo_incref(v1); return v1; } if (!ovf) return bint_add_i(po, v1, -b, false); } return BINARY_INSTR_SUB(ovf, false); } DEFINEFN vinfo_t* integer_or(PsycoObject* po, vinfo_t* v1, vinfo_t* v2) { GENERIC_BINARY_HEADER; GENERIC_BINARY_CT_CT(a | b); return BINARY_INSTR_OR(false, is_nonneg(v1->source) && is_nonneg(v2->source)); } DEFINEFN vinfo_t* integer_xor(PsycoObject* po, vinfo_t* v1, vinfo_t* v2) { GENERIC_BINARY_HEADER; GENERIC_BINARY_CT_CT(a ^ b); return BINARY_INSTR_XOR(false, is_nonneg(v1->source) && is_nonneg(v2->source)); } DEFINEFN vinfo_t* integer_and(PsycoObject* po, vinfo_t* v1, vinfo_t* v2) { GENERIC_BINARY_HEADER; GENERIC_BINARY_CT_CT(a & b); return BINARY_INSTR_AND(false, is_nonneg(v1->source) || is_nonneg(v2->source)); } static vinfo_t* int_mul_i(PsycoObject* po, vinfo_t* v1, long value2, bool ovf) { switch (value2) { case 0: return vinfo_new(CompileTime_New(0)); case 1: vinfo_incref(v1); return v1; } if (((value2-1) & value2) == 0 && value2 >= 0 && !ovf) { /* value2 is a power of two */ return bint_lshift_i(po, v1, intlog2(value2)); } else { return bint_mul_i(po, v1, value2, ovf); } } DEFINEFN vinfo_t* integer_mul(PsycoObject* po, vinfo_t* v1, vinfo_t* v2, bool ovf) { GENERIC_BINARY_HEADER; if (is_compiletime(v1->source)) { long a = CompileTime_Get(v1->source)->value; if (is_compiletime(v2->source)) { long b = CompileTime_Get(v2->source)->value; /* unlike Python, we use a function written in assembly to perform the product overflow checking */ if (ovf && psyco_int_mul_ovf(a, b)) return NULL; /* overflow */ return vinfo_new(CompileTime_New(a * b)); } return int_mul_i(po, v2, a, ovf); } else if (is_compiletime(v2->source)) { long b = CompileTime_Get(v2->source)->value; return int_mul_i(po, v1, b, ovf); } return BINARY_INSTR_MUL(ovf, ovf && is_rtnonneg(v1->source) && is_rtnonneg(v2->source)); } DEFINEFN vinfo_t* integer_mul_i(PsycoObject* po, vinfo_t* v1, long value2) { GENERIC_BINARY_HEADER_i; if (is_compiletime(v1->source)) { long c = CompileTime_Get(v1->source)->value * value2; return vinfo_new(CompileTime_New(c)); } return int_mul_i(po, v1, value2, false); } DEFINEFN vinfo_t* integer_lshift(PsycoObject* po, vinfo_t* v1, vinfo_t* v2) { condition_code_t cc; GENERIC_BINARY_HEADER; if (is_compiletime(v2->source)) return integer_lshift_i(po, v1, CompileTime_Get(v2->source)->value); cc = int_cmp_i(po, v2, LONG_BIT, Py_GE|COMPARE_UNSIGNED); if (cc == CC_ERROR) return NULL; if (runtime_condition_f(po, cc)) { cc = int_cmp_i(po, v2, 0, Py_LT); if (cc == CC_ERROR) return NULL; if (runtime_condition_f(po, cc)) { PycException_SetString(po, PyExc_ValueError, "negative shift count"); return NULL; } return vinfo_new(CompileTime_New(0)); } return BINARY_INSTR_LSHIFT(false); } DEFINEFN vinfo_t* integer_lshift_i(PsycoObject* po, vinfo_t* v1, long counter) { GENERIC_BINARY_HEADER_i; if (0 < counter && counter < LONG_BIT) { if (is_compiletime(v1->source)) { long c = CompileTime_Get(v1->source)->value << counter; return vinfo_new(CompileTime_New(c)); } else return bint_lshift_i(po, v1, counter); } else if (counter == 0) { vinfo_incref(v1); return v1; } else if (counter >= LONG_BIT) return vinfo_new(CompileTime_New(0)); else { PycException_SetString(po, PyExc_ValueError, "negative shift count"); return NULL; } } DEFINEFN /* signed */ vinfo_t* integer_rshift(PsycoObject* po, vinfo_t* v1, vinfo_t* v2) { condition_code_t cc; GENERIC_BINARY_HEADER; if (is_compiletime(v2->source)) return integer_rshift_i(po, v1, CompileTime_Get(v2->source)->value); cc = int_cmp_i(po, v2, LONG_BIT, Py_GE|COMPARE_UNSIGNED); if (cc == CC_ERROR) return NULL; if (runtime_condition_f(po, cc)) { cc = int_cmp_i(po, v2, 0, Py_LT); if (cc == CC_ERROR) return NULL; if (runtime_condition_f(po, cc)) { PycException_SetString(po, PyExc_ValueError, "negative shift count"); return NULL; } return integer_rshift_i(po, v1, LONG_BIT-1); } return BINARY_INSTR_RSHIFT(is_nonneg(v1->source)); } DEFINEFN /* signed */ vinfo_t* integer_rshift_i(PsycoObject* po, vinfo_t* v1, long counter) { GENERIC_BINARY_HEADER_i; if (counter >= LONG_BIT-1) { if (is_nonneg(v1->source)) return vinfo_new(CompileTime_New(0)); counter = LONG_BIT-1; } if (counter > 0) { if (is_compiletime(v1->source)) { long c = ((long)(CompileTime_Get(v1->source)->value)) >> counter; return vinfo_new(CompileTime_New(c)); } else return bint_rshift_i(po, v1, counter); } else if (counter == 0) { vinfo_incref(v1); return v1; } else { PycException_SetString(po, PyExc_ValueError, "negative shift count"); return NULL; } } DEFINEFN vinfo_t* integer_urshift_i(PsycoObject* po, vinfo_t* v1, long counter) { GENERIC_BINARY_HEADER_i; if (0 < counter && counter < LONG_BIT) { if (is_compiletime(v1->source)) { long c = ((unsigned long)(CompileTime_Get(v1->source)->value)) >> counter; return vinfo_new(CompileTime_New(c)); } else return bint_urshift_i(po, v1, counter); } else if (counter == 0) { vinfo_incref(v1); return v1; } else if (counter >= LONG_BIT) return vinfo_new(CompileTime_New(0)); else { PycException_SetString(po, PyExc_ValueError, "negative shift count"); return NULL; } } /* DEFINEFN */ /* vinfo_t* integer_lshift(PsycoObject* po, vinfo_t* v1, vinfo_t* v2) */ /* { */ /* NonVirtualSource v1s, v2s; */ /* v2s = vinfo_compute(v2, po); */ /* if (v2s == SOURCE_ERROR) return NULL; */ /* if (is_compiletime(v2s)) */ /* return integer_lshift_i(po, v1, CompileTime_Get(v2s)->value); */ /* v1s = vinfo_compute(v1, po); */ /* if (v1s == SOURCE_ERROR) return NULL; */ /* XXX implement me */ /* } */ #define GENERIC_UNARY_HEADER \ if (!compute_vinfo(v1, po)) return NULL; #define GENERIC_UNARY_CT(c_code, ovf, c_ovf) \ if (is_compiletime(v1->source)) \ { \ long a = CompileTime_Get(v1->source)->value; \ long c = (c_code); \ if ((ovf) && (c_ovf)) \ return NULL; \ return vinfo_new(CompileTime_New(c)); \ } DEFINEFN vinfo_t* integer_inv(PsycoObject* po, vinfo_t* v1) { GENERIC_UNARY_HEADER; GENERIC_UNARY_CT(~a, false, false); return UNARY_INSTR_INV(false, false); } DEFINEFN vinfo_t* integer_neg(PsycoObject* po, vinfo_t* v1, bool ovf) { GENERIC_UNARY_HEADER; GENERIC_UNARY_CT(-a, ovf, c == (-LONG_MAX-1)); return UNARY_INSTR_NEG(ovf, false); } DEFINEFN vinfo_t* integer_abs(PsycoObject* po, vinfo_t* v1, bool ovf) { GENERIC_UNARY_HEADER; if (is_nonneg(v1->source)) { vinfo_incref(v1); return v1; } GENERIC_UNARY_CT(a<0 ? -a : a, ovf, c == (-LONG_MAX-1)); return UNARY_INSTR_ABS(ovf, true); /* nb. result assumed to be positive, which is false in case of overflow */ } static condition_code_t immediate_compare(long a, long b, int base_py_op) { switch (base_py_op) { case Py_LT: return a < b ? CC_ALWAYS_TRUE : CC_ALWAYS_FALSE; case Py_LE: return a <= b ? CC_ALWAYS_TRUE : CC_ALWAYS_FALSE; case Py_EQ|COMPARE_UNSIGNED: case Py_EQ: return a == b ? CC_ALWAYS_TRUE : CC_ALWAYS_FALSE; case Py_NE|COMPARE_UNSIGNED: case Py_NE: return a != b ? CC_ALWAYS_TRUE : CC_ALWAYS_FALSE; case Py_GT: return a > b ? CC_ALWAYS_TRUE : CC_ALWAYS_FALSE; case Py_GE: return a >= b ? CC_ALWAYS_TRUE : CC_ALWAYS_FALSE; case Py_LT|COMPARE_UNSIGNED: return ((unsigned long) a) < ((unsigned long) b) ? CC_ALWAYS_TRUE : CC_ALWAYS_FALSE; case Py_LE|COMPARE_UNSIGNED: return ((unsigned long) a) <= ((unsigned long) b) ? CC_ALWAYS_TRUE : CC_ALWAYS_FALSE; case Py_GT|COMPARE_UNSIGNED: return ((unsigned long) a) > ((unsigned long) b) ? CC_ALWAYS_TRUE : CC_ALWAYS_FALSE; case Py_GE|COMPARE_UNSIGNED: return ((unsigned long) a) >= ((unsigned long) b) ? CC_ALWAYS_TRUE : CC_ALWAYS_FALSE; default: Py_FatalError("immediate_compare(): bad py_op"); return CC_ERROR; } } static condition_code_t int_cmp_i(PsycoObject* po, vinfo_t* rt1, long immed2, int py_op) { int base_py_op = py_op & COMPARE_OP_MASK; extra_assert(is_runtime(rt1->source)); /* detect easy cases */ if (immed2 == 0) { if (is_rtnonneg(rt1->source)) /* if we know that rt1>=0 */ switch (base_py_op) { case Py_LT: /* rt1 < 0 */ return CC_ALWAYS_FALSE; case Py_GE: /* rt1 >= 0 */ return CC_ALWAYS_TRUE; default: ; /* pass */ } switch (base_py_op) { case Py_LT|COMPARE_UNSIGNED: /* (unsigned) rt1 < 0 */ return CC_ALWAYS_FALSE; case Py_GE|COMPARE_UNSIGNED: /* (unsigned) rt1 >= 0 */ return CC_ALWAYS_TRUE; default: ; /* pass */ } } else if (immed2 < 0) { if (is_rtnonneg(rt1->source)) /* rt1>=0 && immed2<0 */ { switch (base_py_op) { case Py_EQ: /* rt1 == immed2 */ case Py_LT: /* rt1 < immed2 */ case Py_LE: /* rt1 <= immed2 */ case Py_GT|COMPARE_UNSIGNED: /* (unsigned) rt1 > immed2 */ case Py_GE|COMPARE_UNSIGNED: /* (unsigned) rt1 >= immed2 */ return CC_ALWAYS_FALSE; case Py_NE: /* rt1 != immed2 */ case Py_GE: /* rt1 >= immed2 */ case Py_GT: /* rt1 > immed2 */ case Py_LE|COMPARE_UNSIGNED: /* (unsigned) rt1 <= immed2 */ case Py_LT|COMPARE_UNSIGNED: /* (unsigned) rt1 < immed2 */ return CC_ALWAYS_TRUE; default: ; /* pass */ } } if (immed2 == LONG_MIN) { switch (base_py_op) { case Py_LT: /* rt1 < LONG_MIN */ return CC_ALWAYS_FALSE; case Py_GE: /* rt1 >= LONG_MIN */ return CC_ALWAYS_TRUE; case Py_EQ: /* rt1 == LONG_MIN */ case Py_LE: /* rt1 <= LONG_MIN */ if (py_op & CHEAT_MAXINT) return CC_ALWAYS_FALSE; break; case Py_NE: /* rt1 != LONG_MIN */ case Py_GT: /* rt1 > LONG_MIN */ if (py_op & CHEAT_MAXINT) return CC_ALWAYS_TRUE; break; default: ; /* pass */ } } } else if (immed2 == LONG_MAX) { if (is_rtnonneg(rt1->source)) /* if we know that rt1>=0 */ { switch (base_py_op) { case Py_GT|COMPARE_UNSIGNED: /* (unsigned) rt1 > LONG_MAX */ return CC_ALWAYS_FALSE; case Py_LE|COMPARE_UNSIGNED: /* (unsigned) rt1 <= LONG_MAX */ return CC_ALWAYS_TRUE; case Py_GE|COMPARE_UNSIGNED: /* (unsigned) rt1 >= LONG_MAX */ if (py_op & CHEAT_MAXINT) return CC_ALWAYS_FALSE; break; case Py_LT|COMPARE_UNSIGNED: /* (unsigned) rt1 < LONG_MAX */ if (py_op & CHEAT_MAXINT) return CC_ALWAYS_TRUE; break; default: ; /* pass */ } } switch (base_py_op) { case Py_LE: /* rt1 <= LONG_MAX */ return CC_ALWAYS_TRUE; case Py_GT: /* rt1 > LONG_MAX */ return CC_ALWAYS_FALSE; case Py_EQ: /* rt1 == LONG_MAX */ case Py_GE: /* rt1 >= LONG_MAX */ if (py_op & CHEAT_MAXINT) return CC_ALWAYS_FALSE; break; case Py_NE: /* rt1 != LONG_MAX */ case Py_LT: /* rt1 < LONG_MAX */ if (py_op & CHEAT_MAXINT) return CC_ALWAYS_TRUE; break; default: ; /* pass */ } } /* end of easy cases */ return bint_cmp_i(po, base_py_op, rt1, immed2); } static const int inverted_py_op[8] = { /* Py_LT: */ Py_GT, /* Py_LE: */ Py_GE, /* Py_EQ: */ Py_EQ, /* Py_NE: */ Py_NE, /* Py_GT: */ Py_LT, /* Py_GE: */ Py_LE, /* (6) */ -1, /* (7) */ -1 }; DEFINEFN condition_code_t integer_cmp(PsycoObject* po, vinfo_t* v1, vinfo_t* v2, int py_op) { int base_py_op = py_op & COMPARE_OP_MASK; if (vinfo_known_equal(v1, v2)) goto same_source; if (!compute_vinfo(v1, po) || !compute_vinfo(v2, po)) return CC_ERROR; if (vinfo_known_equal(v1, v2)) { same_source: /* comparing equal sources */ switch (base_py_op) { case Py_LE: case Py_EQ: case Py_GE: return CC_ALWAYS_TRUE; default: return CC_ALWAYS_FALSE; } } if (is_compiletime(v1->source)) if (is_compiletime(v2->source)) { long a = CompileTime_Get(v1->source)->value; long b = CompileTime_Get(v2->source)->value; return immediate_compare(a, b, base_py_op); } else { /* invert the two operands because (assumedly) the processor has only CMP xxx,immed and not CMP immed,xxx */ py_op = inverted_py_op[py_op & COMPARE_BASE_MASK] | (py_op & ~COMPARE_BASE_MASK); return int_cmp_i(po, v2, CompileTime_Get(v1->source)->value, py_op); } else if (is_compiletime(v2->source)) { return int_cmp_i(po, v1, CompileTime_Get(v2->source)->value, py_op); } else { return BINARY_INSTR_CMP(base_py_op); } } DEFINEFN condition_code_t integer_cmp_i(PsycoObject* po, vinfo_t* v1, long value2, int py_op) { if (!compute_vinfo(v1, po)) return CC_ERROR; if (is_compiletime(v1->source)) { long a = CompileTime_Get(v1->source)->value; return immediate_compare(a, value2, py_op); } else return int_cmp_i(po, v1, value2, py_op); } DEFINEFN vinfo_t* integer_conditional(PsycoObject* po, condition_code_t cc, long immed_true, long immed_false) { switch ((int)cc) { case (int)CC_ALWAYS_FALSE: return vinfo_new(CompileTime_New(immed_false)); case (int)CC_ALWAYS_TRUE: return vinfo_new(CompileTime_New(immed_true)); default: return BINARY_INSTR_COND(cc, immed_true, immed_false); } } INITIALIZATIONFN void psyco_codegen_init(void) { #if HAVE_CCREG int i; for (i=0; i