@@ -455,16 +455,23 @@ mp_obj_t rt_unary_op(int op, mp_obj_t arg) {
455455 if (MP_OBJ_IS_SMALL_INT (arg )) {
456456 mp_small_int_t val = MP_OBJ_SMALL_INT_VALUE (arg );
457457 switch (op ) {
458- case RT_UNARY_OP_BOOL : return MP_BOOL (val != 0 );
459- case RT_UNARY_OP_POSITIVE : break ;
460- case RT_UNARY_OP_NEGATIVE : val = - val ; break ;
461- case RT_UNARY_OP_INVERT : val = ~val ; break ;
462- default : assert (0 ); val = 0 ;
463- }
464- if (MP_OBJ_FITS_SMALL_INT (val )) {
465- return MP_OBJ_NEW_SMALL_INT (val );
458+ case RT_UNARY_OP_BOOL :
459+ return MP_BOOL (val != 0 );
460+ case RT_UNARY_OP_POSITIVE :
461+ return arg ;
462+ case RT_UNARY_OP_NEGATIVE :
463+ // check for overflow
464+ if (val == MP_SMALL_INT_MIN ) {
465+ return mp_obj_new_int (- val );
466+ } else {
467+ return MP_OBJ_NEW_SMALL_INT (- val );
468+ }
469+ case RT_UNARY_OP_INVERT :
470+ return MP_OBJ_NEW_SMALL_INT (~val );
471+ default :
472+ assert (0 );
473+ return arg ;
466474 }
467- return mp_obj_new_int (val );
468475 } else {
469476 mp_obj_type_t * type = mp_obj_get_type (arg );
470477 if (type -> unary_op != NULL ) {
@@ -532,6 +539,15 @@ mp_obj_t rt_binary_op(int op, mp_obj_t lhs, mp_obj_t rhs) {
532539 mp_small_int_t lhs_val = MP_OBJ_SMALL_INT_VALUE (lhs );
533540 if (MP_OBJ_IS_SMALL_INT (rhs )) {
534541 mp_small_int_t rhs_val = MP_OBJ_SMALL_INT_VALUE (rhs );
542+ // This is a binary operation: lhs_val op rhs_val
543+ // We need to be careful to handle overflow; see CERT INT32-C
544+ // Operations that can overflow:
545+ // + result always fits in machine_int_t, then handled by SMALL_INT check
546+ // - result always fits in machine_int_t, then handled by SMALL_INT check
547+ // * checked explicitly
548+ // / if lhs=MIN and rhs=-1; result always fits in machine_int_t, then handled by SMALL_INT check
549+ // % if lhs=MIN and rhs=-1; result always fits in machine_int_t, then handled by SMALL_INT check
550+ // << checked explicitly
535551 switch (op ) {
536552 case RT_BINARY_OP_OR :
537553 case RT_BINARY_OP_INPLACE_OR : lhs_val |= rhs_val ; break ;
@@ -540,41 +556,117 @@ mp_obj_t rt_binary_op(int op, mp_obj_t lhs, mp_obj_t rhs) {
540556 case RT_BINARY_OP_AND :
541557 case RT_BINARY_OP_INPLACE_AND : lhs_val &= rhs_val ; break ;
542558 case RT_BINARY_OP_LSHIFT :
543- case RT_BINARY_OP_INPLACE_LSHIFT : lhs_val <<= rhs_val ; break ;
559+ case RT_BINARY_OP_INPLACE_LSHIFT : {
560+ if (rhs_val < 0 ) {
561+ // negative shift not allowed
562+ nlr_jump (mp_obj_new_exception_msg (& mp_type_ValueError , "negative shift count" ));
563+ } else if (rhs_val >= BITS_PER_WORD || lhs_val > (MP_SMALL_INT_MAX >> rhs_val ) || lhs_val < (MP_SMALL_INT_MIN >> rhs_val )) {
564+ // left-shift will overflow, so use higher precision integer
565+ lhs = mp_obj_new_int_from_ll (lhs_val );
566+ goto generic_binary_op ;
567+ } else {
568+ // use standard precision
569+ lhs_val <<= rhs_val ;
570+ }
571+ break ;
572+ }
544573 case RT_BINARY_OP_RSHIFT :
545- case RT_BINARY_OP_INPLACE_RSHIFT : lhs_val >>= rhs_val ; break ;
574+ case RT_BINARY_OP_INPLACE_RSHIFT :
575+ if (rhs_val < 0 ) {
576+ // negative shift not allowed
577+ nlr_jump (mp_obj_new_exception_msg (& mp_type_ValueError , "negative shift count" ));
578+ } else {
579+ // standard precision is enough for right-shift
580+ lhs_val >>= rhs_val ;
581+ }
582+ break ;
546583 case RT_BINARY_OP_ADD :
547584 case RT_BINARY_OP_INPLACE_ADD : lhs_val += rhs_val ; break ;
548585 case RT_BINARY_OP_SUBTRACT :
549586 case RT_BINARY_OP_INPLACE_SUBTRACT : lhs_val -= rhs_val ; break ;
550587 case RT_BINARY_OP_MULTIPLY :
551- case RT_BINARY_OP_INPLACE_MULTIPLY : lhs_val *= rhs_val ; break ;
588+ case RT_BINARY_OP_INPLACE_MULTIPLY : {
589+
590+ // If long long type exists and is larger than machine_int_t, then
591+ // we can use the following code to perform overflow-checked multiplication.
592+ // Otherwise (eg in x64 case) we must use the branching code below.
593+ #if 0
594+ // compute result using long long precision
595+ long long res = (long long )lhs_val * (long long )rhs_val ;
596+ if (res > MP_SMALL_INT_MAX || res < MP_SMALL_INT_MIN ) {
597+ // result overflowed SMALL_INT, so return higher precision integer
598+ return mp_obj_new_int_from_ll (res );
599+ } else {
600+ // use standard precision
601+ lhs_val = (mp_small_int_t )res ;
602+ }
603+ #endif
604+
605+ if (lhs_val > 0 ) { // lhs_val is positive
606+ if (rhs_val > 0 ) { // lhs_val and rhs_val are positive
607+ if (lhs_val > (MP_SMALL_INT_MAX / rhs_val )) {
608+ goto mul_overflow ;
609+ }
610+ } else { // lhs_val positive, rhs_val nonpositive
611+ if (rhs_val < (MP_SMALL_INT_MIN / lhs_val )) {
612+ goto mul_overflow ;
613+ }
614+ } // lhs_val positive, rhs_val nonpositive
615+ } else { // lhs_val is nonpositive
616+ if (rhs_val > 0 ) { // lhs_val is nonpositive, rhs_val is positive
617+ if (lhs_val < (MP_SMALL_INT_MIN / rhs_val )) {
618+ goto mul_overflow ;
619+ }
620+ } else { // lhs_val and rhs_val are nonpositive
621+ if (lhs_val != 0 && rhs_val < (MP_SMALL_INT_MAX / lhs_val )) {
622+ goto mul_overflow ;
623+ }
624+ } // End if lhs_val and rhs_val are nonpositive
625+ } // End if lhs_val is nonpositive
626+
627+ // use standard precision
628+ return MP_OBJ_NEW_SMALL_INT (lhs_val * rhs_val );
629+
630+ mul_overflow :
631+ // use higher precision
632+ lhs = mp_obj_new_int_from_ll (lhs_val );
633+ goto generic_binary_op ;
634+
635+ break ;
636+ }
552637 case RT_BINARY_OP_FLOOR_DIVIDE :
553638 case RT_BINARY_OP_INPLACE_FLOOR_DIVIDE : lhs_val /= rhs_val ; break ;
554- #if MICROPY_ENABLE_FLOAT
639+ #if MICROPY_ENABLE_FLOAT
555640 case RT_BINARY_OP_TRUE_DIVIDE :
556641 case RT_BINARY_OP_INPLACE_TRUE_DIVIDE : return mp_obj_new_float ((mp_float_t )lhs_val / (mp_float_t )rhs_val );
557- #endif
642+ #endif
558643
559644 // TODO implement modulo as specified by Python
560645 case RT_BINARY_OP_MODULO :
561646 case RT_BINARY_OP_INPLACE_MODULO : lhs_val %= rhs_val ; break ;
562647
563- // TODO check for negative power, and overflow
564648 case RT_BINARY_OP_POWER :
565649 case RT_BINARY_OP_INPLACE_POWER :
566- {
567- int ans = 1 ;
568- while (rhs_val > 0 ) {
569- if (rhs_val & 1 ) {
570- ans *= lhs_val ;
650+ if (rhs_val < 0 ) {
651+ #if MICROPY_ENABLE_FLOAT
652+ lhs = mp_obj_new_float (lhs_val );
653+ goto generic_binary_op ;
654+ #else
655+ nlr_jump (mp_obj_new_exception_msg (& mp_type_ValueError , "negative power with no float support" ));
656+ #endif
657+ } else {
658+ // TODO check for overflow
659+ machine_int_t ans = 1 ;
660+ while (rhs_val > 0 ) {
661+ if (rhs_val & 1 ) {
662+ ans *= lhs_val ;
663+ }
664+ lhs_val *= lhs_val ;
665+ rhs_val /= 2 ;
571666 }
572- lhs_val *= lhs_val ;
573- rhs_val /= 2 ;
667+ lhs_val = ans ;
574668 }
575- lhs_val = ans ;
576669 break ;
577- }
578670 case RT_BINARY_OP_LESS : return MP_BOOL (lhs_val < rhs_val ); break ;
579671 case RT_BINARY_OP_MORE : return MP_BOOL (lhs_val > rhs_val ); break ;
580672 case RT_BINARY_OP_LESS_EQUAL : return MP_BOOL (lhs_val <= rhs_val ); break ;
@@ -585,8 +677,9 @@ mp_obj_t rt_binary_op(int op, mp_obj_t lhs, mp_obj_t rhs) {
585677 // TODO: We just should make mp_obj_new_int() inline and use that
586678 if (MP_OBJ_FITS_SMALL_INT (lhs_val )) {
587679 return MP_OBJ_NEW_SMALL_INT (lhs_val );
680+ } else {
681+ return mp_obj_new_int (lhs_val );
588682 }
589- return mp_obj_new_int (lhs_val );
590683#if MICROPY_ENABLE_FLOAT
591684 } else if (MP_OBJ_IS_TYPE (rhs , & mp_type_float )) {
592685 return mp_obj_float_binary_op (op , lhs_val , rhs );
@@ -628,7 +721,9 @@ mp_obj_t rt_binary_op(int op, mp_obj_t lhs, mp_obj_t rhs) {
628721 }
629722
630723 // generic binary_op supplied by type
631- mp_obj_type_t * type = mp_obj_get_type (lhs );
724+ mp_obj_type_t * type ;
725+ generic_binary_op :
726+ type = mp_obj_get_type (lhs );
632727 if (type -> binary_op != NULL ) {
633728 mp_obj_t result = type -> binary_op (op , lhs , rhs );
634729 if (result != MP_OBJ_NULL ) {
0 commit comments