Преглед на файлове

[更新] :py文件夹下修改的文件。

SummerGift преди 8 години
родител
ревизия
c19c82b65e
променени са 67 файла, в които са добавени 1166 реда и са изтрити 963 реда
  1. 5 10
      py/asmarm.h
  2. 3 3
      py/asmbase.c
  3. 5 10
      py/asmthumb.h
  4. 5 10
      py/asmx64.h
  5. 5 10
      py/asmx86.h
  6. 5 10
      py/asmxtensa.h
  7. 0 6
      py/bc.c
  8. 1 1
      py/binary.c
  9. 1 1
      py/builtinhelp.c
  10. 17 3
      py/builtinimport.c
  11. 45 43
      py/compile.c
  12. 3 1
      py/emitbc.c
  13. 1 1
      py/emitglue.h
  14. 56 49
      py/emitnative.c
  15. 29 16
      py/gc.c
  16. 18 6
      py/malloc.c
  17. 14 0
      py/map.c
  18. 2 0
      py/misc.h
  19. 2 2
      py/mkenv.mk
  20. 22 1
      py/mkrules.mk
  21. 38 34
      py/modbuiltins.c
  22. 3 3
      py/modio.c
  23. 10 0
      py/modmicropython.c
  24. 6 0
      py/modthread.c
  25. 59 13
      py/mpconfig.h
  26. 20 8
      py/mpprint.c
  27. 6 8
      py/mpstate.h
  28. 0 117
      py/mpy_scheduler.c
  29. 51 82
      py/mpz.c
  30. 4 0
      py/mpz.h
  31. 62 24
      py/nlr.h
  32. 4 3
      py/nlrsetjmp.c
  33. 13 25
      py/nlrthumb.c
  34. 5 30
      py/nlrx64.c
  35. 4 29
      py/nlrx86.c
  36. 3 22
      py/nlrxtensa.c
  37. 4 3
      py/obj.h
  38. 1 2
      py/objarray.c
  39. 8 0
      py/objboundmeth.c
  40. 16 33
      py/objdict.c
  41. 1 16
      py/objexcept.c
  42. 6 6
      py/objfloat.c
  43. 43 23
      py/objfun.c
  44. 9 0
      py/objint_longlong.c
  45. 1 1
      py/objint_mpz.c
  46. 38 18
      py/objnamedtuple.c
  47. 6 3
      py/objobject.c
  48. 3 5
      py/objset.c
  49. 45 27
      py/objstr.c
  50. 1 0
      py/objstr.h
  51. 2 2
      py/objstrunicode.c
  52. 114 57
      py/objtype.c
  53. 5 0
      py/objtype.h
  54. 2 2
      py/opmethods.c
  55. 203 104
      py/parse.c
  56. 21 6
      py/parsenum.c
  57. 2 0
      py/py.mk
  58. 5 7
      py/qstr.c
  59. 1 0
      py/qstrdefs.h
  60. 40 45
      py/runtime.c
  61. 5 3
      py/runtime.h
  62. 4 0
      py/runtime0.h
  63. 8 4
      py/runtime_utils.c
  64. 3 3
      py/smallint.h
  65. 1 6
      py/stackctrl.c
  66. 0 1
      py/stream.c
  67. 41 5
      py/vm.c

+ 5 - 10
py/asmarm.h

@@ -167,17 +167,12 @@ void asm_arm_bl_ind(asm_arm_t *as, void *fun_ptr, uint fun_id, uint reg_temp);
     } while (0)
 #define ASM_CALL_IND(as, ptr, idx) asm_arm_bl_ind(as, ptr, idx, ASM_ARM_REG_R3)
 
-#define ASM_MOV_REG_TO_LOCAL(as, reg, local_num) asm_arm_mov_local_reg(as, (local_num), (reg))
-#define ASM_MOV_IMM_TO_REG(as, imm, reg) asm_arm_mov_reg_i32(as, (reg), (imm))
-#define ASM_MOV_ALIGNED_IMM_TO_REG(as, imm, reg) asm_arm_mov_reg_i32(as, (reg), (imm))
-#define ASM_MOV_IMM_TO_LOCAL_USING(as, imm, local_num, reg_temp) \
-    do { \
-        asm_arm_mov_reg_i32(as, (reg_temp), (imm)); \
-        asm_arm_mov_local_reg(as, (local_num), (reg_temp)); \
-    } while (false)
-#define ASM_MOV_LOCAL_TO_REG(as, local_num, reg) asm_arm_mov_reg_local(as, (reg), (local_num))
+#define ASM_MOV_LOCAL_REG(as, local_num, reg_src) asm_arm_mov_local_reg((as), (local_num), (reg_src))
+#define ASM_MOV_REG_IMM(as, reg_dest, imm) asm_arm_mov_reg_i32((as), (reg_dest), (imm))
+#define ASM_MOV_REG_ALIGNED_IMM(as, reg_dest, imm) asm_arm_mov_reg_i32((as), (reg_dest), (imm))
+#define ASM_MOV_REG_LOCAL(as, reg_dest, local_num) asm_arm_mov_reg_local((as), (reg_dest), (local_num))
 #define ASM_MOV_REG_REG(as, reg_dest, reg_src) asm_arm_mov_reg_reg((as), (reg_dest), (reg_src))
-#define ASM_MOV_LOCAL_ADDR_TO_REG(as, local_num, reg) asm_arm_mov_reg_local_addr(as, (reg), (local_num))
+#define ASM_MOV_REG_LOCAL_ADDR(as, reg_dest, local_num) asm_arm_mov_reg_local_addr((as), (reg_dest), (local_num))
 
 #define ASM_LSL_REG_REG(as, reg_dest, reg_shift) asm_arm_lsl_reg_reg((as), (reg_dest), (reg_shift))
 #define ASM_ASR_REG_REG(as, reg_dest, reg_shift) asm_arm_asr_reg_reg((as), (reg_dest), (reg_shift))

+ 3 - 3
py/asmbase.c

@@ -46,10 +46,10 @@ void mp_asm_base_deinit(mp_asm_base_t *as, bool free_code) {
 }
 
 void mp_asm_base_start_pass(mp_asm_base_t *as, int pass) {
-    if (pass == MP_ASM_PASS_COMPUTE) {
-        // reset all labels
+    if (pass < MP_ASM_PASS_EMIT) {
+        // Reset labels so we can detect backwards jumps (and verify unique assignment)
         memset(as->label_offsets, -1, as->max_num_labels * sizeof(size_t));
-    } else if (pass == MP_ASM_PASS_EMIT) {
+    } else {
         // allocating executable RAM is platform specific
         MP_PLAT_ALLOC_EXEC(as->code_offset, (void**)&as->code_base, &as->code_size);
         assert(as->code_base != NULL);

+ 5 - 10
py/asmthumb.h

@@ -283,17 +283,12 @@ void asm_thumb_bl_ind(asm_thumb_t *as, void *fun_ptr, uint fun_id, uint reg_temp
     } while (0)
 #define ASM_CALL_IND(as, ptr, idx) asm_thumb_bl_ind(as, ptr, idx, ASM_THUMB_REG_R3)
 
-#define ASM_MOV_REG_TO_LOCAL(as, reg, local_num) asm_thumb_mov_local_reg(as, (local_num), (reg))
-#define ASM_MOV_IMM_TO_REG(as, imm, reg) asm_thumb_mov_reg_i32_optimised(as, (reg), (imm))
-#define ASM_MOV_ALIGNED_IMM_TO_REG(as, imm, reg) asm_thumb_mov_reg_i32_aligned(as, (reg), (imm))
-#define ASM_MOV_IMM_TO_LOCAL_USING(as, imm, local_num, reg_temp) \
-    do { \
-        asm_thumb_mov_reg_i32_optimised(as, (reg_temp), (imm)); \
-        asm_thumb_mov_local_reg(as, (local_num), (reg_temp)); \
-    } while (false)
-#define ASM_MOV_LOCAL_TO_REG(as, local_num, reg) asm_thumb_mov_reg_local(as, (reg), (local_num))
+#define ASM_MOV_LOCAL_REG(as, local_num, reg) asm_thumb_mov_local_reg((as), (local_num), (reg))
+#define ASM_MOV_REG_IMM(as, reg_dest, imm) asm_thumb_mov_reg_i32_optimised((as), (reg_dest), (imm))
+#define ASM_MOV_REG_ALIGNED_IMM(as, reg_dest, imm) asm_thumb_mov_reg_i32_aligned((as), (reg_dest), (imm))
+#define ASM_MOV_REG_LOCAL(as, reg_dest, local_num) asm_thumb_mov_reg_local((as), (reg_dest), (local_num))
 #define ASM_MOV_REG_REG(as, reg_dest, reg_src) asm_thumb_mov_reg_reg((as), (reg_dest), (reg_src))
-#define ASM_MOV_LOCAL_ADDR_TO_REG(as, local_num, reg) asm_thumb_mov_reg_local_addr(as, (reg), (local_num))
+#define ASM_MOV_REG_LOCAL_ADDR(as, reg_dest, local_num) asm_thumb_mov_reg_local_addr((as), (reg_dest), (local_num))
 
 #define ASM_LSL_REG_REG(as, reg_dest, reg_shift) asm_thumb_format_4((as), ASM_THUMB_FORMAT_4_LSL, (reg_dest), (reg_shift))
 #define ASM_ASR_REG_REG(as, reg_dest, reg_shift) asm_thumb_format_4((as), ASM_THUMB_FORMAT_4_ASR, (reg_dest), (reg_shift))

+ 5 - 10
py/asmx64.h

@@ -162,17 +162,12 @@ void asm_x64_call_ind(asm_x64_t* as, void* ptr, int temp_r32);
     } while (0)
 #define ASM_CALL_IND(as, ptr, idx) asm_x64_call_ind(as, ptr, ASM_X64_REG_RAX)
 
-#define ASM_MOV_REG_TO_LOCAL        asm_x64_mov_r64_to_local
-#define ASM_MOV_IMM_TO_REG          asm_x64_mov_i64_to_r64_optimised
-#define ASM_MOV_ALIGNED_IMM_TO_REG  asm_x64_mov_i64_to_r64_aligned
-#define ASM_MOV_IMM_TO_LOCAL_USING(as, imm, local_num, reg_temp) \
-    do { \
-        asm_x64_mov_i64_to_r64_optimised(as, (imm), (reg_temp)); \
-        asm_x64_mov_r64_to_local(as, (reg_temp), (local_num)); \
-    } while (false)
-#define ASM_MOV_LOCAL_TO_REG        asm_x64_mov_local_to_r64
+#define ASM_MOV_LOCAL_REG(as, local_num, reg_src) asm_x64_mov_r64_to_local((as), (reg_src), (local_num))
+#define ASM_MOV_REG_IMM(as, reg_dest, imm) asm_x64_mov_i64_to_r64_optimised((as), (imm), (reg_dest))
+#define ASM_MOV_REG_ALIGNED_IMM(as, reg_dest, imm) asm_x64_mov_i64_to_r64_aligned((as), (imm), (reg_dest))
+#define ASM_MOV_REG_LOCAL(as, reg_dest, local_num) asm_x64_mov_local_to_r64((as), (local_num), (reg_dest))
 #define ASM_MOV_REG_REG(as, reg_dest, reg_src) asm_x64_mov_r64_r64((as), (reg_dest), (reg_src))
-#define ASM_MOV_LOCAL_ADDR_TO_REG   asm_x64_mov_local_addr_to_r64
+#define ASM_MOV_REG_LOCAL_ADDR(as, reg_dest, local_num) asm_x64_mov_local_addr_to_r64((as), (local_num), (reg_dest))
 
 #define ASM_LSL_REG(as, reg) asm_x64_shl_r64_cl((as), (reg))
 #define ASM_ASR_REG(as, reg) asm_x64_sar_r64_cl((as), (reg))

+ 5 - 10
py/asmx86.h

@@ -160,17 +160,12 @@ void asm_x86_call_ind(asm_x86_t* as, void* ptr, mp_uint_t n_args, int temp_r32);
     } while (0)
 #define ASM_CALL_IND(as, ptr, idx) asm_x86_call_ind(as, ptr, mp_f_n_args[idx], ASM_X86_REG_EAX)
 
-#define ASM_MOV_REG_TO_LOCAL        asm_x86_mov_r32_to_local
-#define ASM_MOV_IMM_TO_REG          asm_x86_mov_i32_to_r32
-#define ASM_MOV_ALIGNED_IMM_TO_REG  asm_x86_mov_i32_to_r32_aligned
-#define ASM_MOV_IMM_TO_LOCAL_USING(as, imm, local_num, reg_temp) \
-    do { \
-        asm_x86_mov_i32_to_r32(as, (imm), (reg_temp)); \
-        asm_x86_mov_r32_to_local(as, (reg_temp), (local_num)); \
-    } while (false)
-#define ASM_MOV_LOCAL_TO_REG        asm_x86_mov_local_to_r32
+#define ASM_MOV_LOCAL_REG(as, local_num, reg_src) asm_x86_mov_r32_to_local((as), (reg_src), (local_num))
+#define ASM_MOV_REG_IMM(as, reg_dest, imm) asm_x86_mov_i32_to_r32((as), (imm), (reg_dest))
+#define ASM_MOV_REG_ALIGNED_IMM(as, reg_dest, imm) asm_x86_mov_i32_to_r32_aligned((as), (imm), (reg_dest))
+#define ASM_MOV_REG_LOCAL(as, reg_dest, local_num) asm_x86_mov_local_to_r32((as), (local_num), (reg_dest))
 #define ASM_MOV_REG_REG(as, reg_dest, reg_src) asm_x86_mov_r32_r32((as), (reg_dest), (reg_src))
-#define ASM_MOV_LOCAL_ADDR_TO_REG   asm_x86_mov_local_addr_to_r32
+#define ASM_MOV_REG_LOCAL_ADDR(as, reg_dest, local_num) asm_x86_mov_local_addr_to_r32((as), (local_num), (reg_dest))
 
 #define ASM_LSL_REG(as, reg) asm_x86_shl_r32_cl((as), (reg))
 #define ASM_ASR_REG(as, reg) asm_x86_sar_r32_cl((as), (reg))

+ 5 - 10
py/asmxtensa.h

@@ -280,17 +280,12 @@ void asm_xtensa_mov_reg_local_addr(asm_xtensa_t *as, uint reg_dest, int local_nu
         asm_xtensa_op_callx0(as, ASM_XTENSA_REG_A0); \
     } while (0)
 
-#define ASM_MOV_REG_TO_LOCAL(as, reg, local_num) asm_xtensa_mov_local_reg(as, (local_num), (reg))
-#define ASM_MOV_IMM_TO_REG(as, imm, reg) asm_xtensa_mov_reg_i32(as, (reg), (imm))
-#define ASM_MOV_ALIGNED_IMM_TO_REG(as, imm, reg) asm_xtensa_mov_reg_i32(as, (reg), (imm))
-#define ASM_MOV_IMM_TO_LOCAL_USING(as, imm, local_num, reg_temp) \
-    do { \
-        asm_xtensa_mov_reg_i32(as, (reg_temp), (imm)); \
-        asm_xtensa_mov_local_reg(as, (local_num), (reg_temp)); \
-    } while (0)
-#define ASM_MOV_LOCAL_TO_REG(as, local_num, reg) asm_xtensa_mov_reg_local(as, (reg), (local_num))
+#define ASM_MOV_LOCAL_REG(as, local_num, reg_src) asm_xtensa_mov_local_reg((as), (local_num), (reg_src))
+#define ASM_MOV_REG_IMM(as, reg_dest, imm) asm_xtensa_mov_reg_i32((as), (reg_dest), (imm))
+#define ASM_MOV_REG_ALIGNED_IMM(as, reg_dest, imm) asm_xtensa_mov_reg_i32((as), (reg_dest), (imm))
+#define ASM_MOV_REG_LOCAL(as, reg_dest, local_num) asm_xtensa_mov_reg_local((as), (reg_dest), (local_num))
 #define ASM_MOV_REG_REG(as, reg_dest, reg_src) asm_xtensa_op_mov_n((as), (reg_dest), (reg_src))
-#define ASM_MOV_LOCAL_ADDR_TO_REG(as, local_num, reg) asm_xtensa_mov_reg_local_addr(as, (reg), (local_num))
+#define ASM_MOV_REG_LOCAL_ADDR(as, reg_dest, local_num) asm_xtensa_mov_reg_local_addr((as), (reg_dest), (local_num))
 
 #define ASM_LSL_REG_REG(as, reg_dest, reg_shift) \
     do { \

+ 0 - 6
py/bc.c

@@ -26,21 +26,15 @@
  */
 
 #include <stdbool.h>
-#include <stdio.h>
-#include <stdlib.h>
 #include <string.h>
 #include <assert.h>
 
-#include "py/mpconfig.h"
-#include "py/misc.h"
-#include "py/mpstate.h"
 #include "py/runtime.h"
 #include "py/bc0.h"
 #include "py/bc.h"
 
 #if MICROPY_DEBUG_VERBOSE // print debugging info
 #define DEBUG_PRINT (1)
-#define DEBUG_printf DEBUG_printf
 #else // don't print debugging info
 #define DEBUG_PRINT (0)
 #define DEBUG_printf(...) (void)0

+ 1 - 1
py/binary.c

@@ -206,7 +206,7 @@ mp_obj_t mp_binary_get_val(char struct_type, char val_type, byte **ptr) {
         return (mp_obj_t)(mp_uint_t)val;
     } else if (val_type == 'S') {
         const char *s_val = (const char*)(uintptr_t)(mp_uint_t)val;
-        return mp_obj_new_str(s_val, strlen(s_val), false);
+        return mp_obj_new_str(s_val, strlen(s_val));
 #if MICROPY_PY_BUILTINS_FLOAT
     } else if (val_type == 'f') {
         union { uint32_t i; float f; } fpu = {val};

+ 1 - 1
py/builtinhelp.c

@@ -69,7 +69,7 @@ STATIC void mp_help_add_from_names(mp_obj_t list, const char *name) {
     while (*name) {
         size_t l = strlen(name);
         // name should end in '.py' and we strip it off
-        mp_obj_list_append(list, mp_obj_new_str(name, l - 3, false));
+        mp_obj_list_append(list, mp_obj_new_str(name, l - 3));
         name += l + 1;
     }
 }

+ 17 - 3
py/builtinimport.c

@@ -318,7 +318,7 @@ mp_obj_t mp_builtin___import__(size_t n_args, const mp_obj_t *args) {
         }
 
         uint new_mod_l = (mod_len == 0 ? (size_t)(p - this_name) : (size_t)(p - this_name) + 1 + mod_len);
-        char *new_mod = alloca(new_mod_l);
+        char *new_mod = mp_local_alloc(new_mod_l);
         memcpy(new_mod, this_name, p - this_name);
         if (mod_len != 0) {
             new_mod[p - this_name] = '.';
@@ -326,9 +326,10 @@ mp_obj_t mp_builtin___import__(size_t n_args, const mp_obj_t *args) {
         }
 
         qstr new_mod_q = qstr_from_strn(new_mod, new_mod_l);
+        mp_local_free(new_mod);
         DEBUG_printf("Resolved base name for relative import: '%s'\n", qstr_str(new_mod_q));
         module_name = MP_OBJ_NEW_QSTR(new_mod_q);
-        mod_str = new_mod;
+        mod_str = qstr_str(new_mod_q);
         mod_len = new_mod_l;
     }
 
@@ -388,6 +389,19 @@ mp_obj_t mp_builtin___import__(size_t n_args, const mp_obj_t *args) {
                     }
                     // found weak linked module
                     module_obj = el->value;
+                    if (MICROPY_MODULE_BUILTIN_INIT) {
+                        // look for __init__ and call it if it exists
+                        // Note: this code doesn't work fully correctly because it allows the
+                        // __init__ function to be called twice if the module is imported by its
+                        // non-weak-link name.  Also, this code is duplicated in objmodule.c.
+                        mp_obj_t dest[2];
+                        mp_load_method_maybe(el->value, MP_QSTR___init__, dest);
+                        if (dest[0] != MP_OBJ_NULL) {
+                            mp_call_method_n_kw(0, 0, dest);
+                            // register module so __init__ is not called again
+                            mp_module_register(mod_name, el->value);
+                        }
+                    }
                 } else {
                     no_exist:
                 #else
@@ -434,7 +448,7 @@ mp_obj_t mp_builtin___import__(size_t n_args, const mp_obj_t *args) {
                     DEBUG_printf("%.*s is dir\n", vstr_len(&path), vstr_str(&path));
                     // https://docs.python.org/3/reference/import.html
                     // "Specifically, any module that contains a __path__ attribute is considered a package."
-                    mp_store_attr(module_obj, MP_QSTR___path__, mp_obj_new_str(vstr_str(&path), vstr_len(&path), false));
+                    mp_store_attr(module_obj, MP_QSTR___path__, mp_obj_new_str(vstr_str(&path), vstr_len(&path)));
                     size_t orig_path_len = path.len;
                     vstr_add_char(&path, PATH_SEP_CHAR);
                     vstr_add_str(&path, "__init__.py");

+ 45 - 43
py/compile.c

@@ -1050,7 +1050,7 @@ STATIC void do_import_name(compiler_t *comp, mp_parse_node_t pn, qstr *q_base) {
             for (int i = 0; i < n; i++) {
                 len += qstr_len(MP_PARSE_NODE_LEAF_ARG(pns->nodes[i]));
             }
-            char *q_ptr = alloca(len);
+            char *q_ptr = mp_local_alloc(len);
             char *str_dest = q_ptr;
             for (int i = 0; i < n; i++) {
                 if (i > 0) {
@@ -1062,6 +1062,7 @@ STATIC void do_import_name(compiler_t *comp, mp_parse_node_t pn, qstr *q_base) {
                 str_dest += str_src_len;
             }
             qstr q_full = qstr_from_strn(q_ptr, len);
+            mp_local_free(q_ptr);
             EMIT_ARG(import_name, q_full);
             if (is_as) {
                 for (int i = 1; i < n; i++) {
@@ -1940,51 +1941,52 @@ STATIC void compile_expr_stmt(compiler_t *comp, mp_parse_node_struct_t *pns) {
             }
         } else {
         plain_assign:
-            if (MICROPY_COMP_DOUBLE_TUPLE_ASSIGN
-                && MP_PARSE_NODE_IS_STRUCT_KIND(pns->nodes[1], PN_testlist_star_expr)
-                && MP_PARSE_NODE_IS_STRUCT_KIND(pns->nodes[0], PN_testlist_star_expr)
-                && MP_PARSE_NODE_STRUCT_NUM_NODES((mp_parse_node_struct_t*)pns->nodes[1]) == 2
-                && MP_PARSE_NODE_STRUCT_NUM_NODES((mp_parse_node_struct_t*)pns->nodes[0]) == 2) {
-                // optimisation for a, b = c, d
-                mp_parse_node_struct_t *pns10 = (mp_parse_node_struct_t*)pns->nodes[1];
+            #if MICROPY_COMP_DOUBLE_TUPLE_ASSIGN
+            if (MP_PARSE_NODE_IS_STRUCT_KIND(pns->nodes[1], PN_testlist_star_expr)
+                && MP_PARSE_NODE_IS_STRUCT_KIND(pns->nodes[0], PN_testlist_star_expr)) {
                 mp_parse_node_struct_t *pns0 = (mp_parse_node_struct_t*)pns->nodes[0];
-                if (MP_PARSE_NODE_IS_STRUCT_KIND(pns0->nodes[0], PN_star_expr)
-                    || MP_PARSE_NODE_IS_STRUCT_KIND(pns0->nodes[1], PN_star_expr)) {
-                    // can't optimise when it's a star expression on the lhs
-                    goto no_optimisation;
-                }
-                compile_node(comp, pns10->nodes[0]); // rhs
-                compile_node(comp, pns10->nodes[1]); // rhs
-                EMIT(rot_two);
-                c_assign(comp, pns0->nodes[0], ASSIGN_STORE); // lhs store
-                c_assign(comp, pns0->nodes[1], ASSIGN_STORE); // lhs store
-            } else if (MICROPY_COMP_TRIPLE_TUPLE_ASSIGN
-                && MP_PARSE_NODE_IS_STRUCT_KIND(pns->nodes[1], PN_testlist_star_expr)
-                && MP_PARSE_NODE_IS_STRUCT_KIND(pns->nodes[0], PN_testlist_star_expr)
-                && MP_PARSE_NODE_STRUCT_NUM_NODES((mp_parse_node_struct_t*)pns->nodes[1]) == 3
-                && MP_PARSE_NODE_STRUCT_NUM_NODES((mp_parse_node_struct_t*)pns->nodes[0]) == 3) {
-                // optimisation for a, b, c = d, e, f
-                mp_parse_node_struct_t *pns10 = (mp_parse_node_struct_t*)pns->nodes[1];
-                mp_parse_node_struct_t *pns0 = (mp_parse_node_struct_t*)pns->nodes[0];
-                if (MP_PARSE_NODE_IS_STRUCT_KIND(pns0->nodes[0], PN_star_expr)
-                    || MP_PARSE_NODE_IS_STRUCT_KIND(pns0->nodes[1], PN_star_expr)
-                    || MP_PARSE_NODE_IS_STRUCT_KIND(pns0->nodes[2], PN_star_expr)) {
-                    // can't optimise when it's a star expression on the lhs
-                    goto no_optimisation;
+                pns1 = (mp_parse_node_struct_t*)pns->nodes[1];
+                uint32_t n_pns0 = MP_PARSE_NODE_STRUCT_NUM_NODES(pns0);
+                // Can only optimise a tuple-to-tuple assignment when all of the following hold:
+                //  - equal number of items in LHS and RHS tuples
+                //  - 2 or 3 items in the tuples
+                //  - there are no star expressions in the LHS tuple
+                if (n_pns0 == MP_PARSE_NODE_STRUCT_NUM_NODES(pns1)
+                    && (n_pns0 == 2
+                        #if MICROPY_COMP_TRIPLE_TUPLE_ASSIGN
+                        || n_pns0 == 3
+                        #endif
+                        )
+                    && !MP_PARSE_NODE_IS_STRUCT_KIND(pns0->nodes[0], PN_star_expr)
+                    && !MP_PARSE_NODE_IS_STRUCT_KIND(pns0->nodes[1], PN_star_expr)
+                    #if MICROPY_COMP_TRIPLE_TUPLE_ASSIGN
+                    && (n_pns0 == 2 || !MP_PARSE_NODE_IS_STRUCT_KIND(pns0->nodes[2], PN_star_expr))
+                    #endif
+                    ) {
+                    // Optimisation for a, b = c, d or a, b, c = d, e, f
+                    compile_node(comp, pns1->nodes[0]); // rhs
+                    compile_node(comp, pns1->nodes[1]); // rhs
+                    #if MICROPY_COMP_TRIPLE_TUPLE_ASSIGN
+                    if (n_pns0 == 3) {
+                        compile_node(comp, pns1->nodes[2]); // rhs
+                        EMIT(rot_three);
+                    }
+                    #endif
+                    EMIT(rot_two);
+                    c_assign(comp, pns0->nodes[0], ASSIGN_STORE); // lhs store
+                    c_assign(comp, pns0->nodes[1], ASSIGN_STORE); // lhs store
+                    #if MICROPY_COMP_TRIPLE_TUPLE_ASSIGN
+                    if (n_pns0 == 3) {
+                        c_assign(comp, pns0->nodes[2], ASSIGN_STORE); // lhs store
+                    }
+                    #endif
+                    return;
                 }
-                compile_node(comp, pns10->nodes[0]); // rhs
-                compile_node(comp, pns10->nodes[1]); // rhs
-                compile_node(comp, pns10->nodes[2]); // rhs
-                EMIT(rot_three);
-                EMIT(rot_two);
-                c_assign(comp, pns0->nodes[0], ASSIGN_STORE); // lhs store
-                c_assign(comp, pns0->nodes[1], ASSIGN_STORE); // lhs store
-                c_assign(comp, pns0->nodes[2], ASSIGN_STORE); // lhs store
-            } else {
-                no_optimisation:
-                compile_node(comp, pns->nodes[1]); // rhs
-                c_assign(comp, pns->nodes[0], ASSIGN_STORE); // lhs store
             }
+            #endif
+
+            compile_node(comp, pns->nodes[1]); // rhs
+            c_assign(comp, pns->nodes[0], ASSIGN_STORE); // lhs store
         }
     } else {
         goto plain_assign;

+ 3 - 1
py/emitbc.c

@@ -313,9 +313,12 @@ void mp_emit_bc_start_pass(emit_t *emit, pass_kind_t pass, scope_t *scope) {
     emit->scope = scope;
     emit->last_source_line_offset = 0;
     emit->last_source_line = 1;
+    #ifndef NDEBUG
+    // With debugging enabled labels are checked for unique assignment
     if (pass < MP_PASS_EMIT) {
         memset(emit->label_offsets, -1, emit->max_num_labels * sizeof(mp_uint_t));
     }
+    #endif
     emit->bytecode_offset = 0;
     emit->code_info_offset = 0;
 
@@ -495,7 +498,6 @@ void mp_emit_bc_label_assign(emit_t *emit, mp_uint_t l) {
         emit->label_offsets[l] = emit->bytecode_offset;
     } else {
         // ensure label offset has not changed from MP_PASS_CODE_SIZE to MP_PASS_EMIT
-        //printf("l%d: (at %d vs %d)\n", l, emit->bytecode_offset, emit->label_offsets[l]);
         assert(emit->label_offsets[l] == emit->bytecode_offset);
     }
 }

+ 1 - 1
py/emitglue.h

@@ -40,7 +40,7 @@ typedef enum {
 } mp_raw_code_kind_t;
 
 typedef struct _mp_raw_code_t {
-    mp_raw_code_kind_t kind : 3;
+    mp_uint_t kind : 3; // of type mp_raw_code_kind_t
     mp_uint_t scope_flags : 7;
     mp_uint_t n_pos_args : 11;
     union {

+ 56 - 49
py/emitnative.c

@@ -66,6 +66,13 @@
 // this is defined so that the assembler exports generic assembler API macros
 #define GENERIC_ASM_API (1)
 
+// define additional generic helper macros
+#define ASM_MOV_LOCAL_IMM_VIA(as, local_num, imm, reg_temp) \
+    do { \
+        ASM_MOV_REG_IMM((as), (reg_temp), (imm)); \
+        ASM_MOV_LOCAL_REG((as), (local_num), (reg_temp)); \
+    } while (false)
+
 #if N_X64
 
 // x64 specific stuff
@@ -389,7 +396,7 @@ STATIC void emit_native_start_pass(emit_t *emit, pass_kind_t pass, scope_t *scop
                 ASM_MOV_REG_REG(emit->as, REG_LOCAL_3, REG_ARG_3);
             } else {
                 assert(i == 3); // should be true; max 4 args is checked above
-                ASM_MOV_REG_TO_LOCAL(emit->as, REG_ARG_4, i - REG_LOCAL_NUM);
+                ASM_MOV_LOCAL_REG(emit->as, i - REG_LOCAL_NUM, REG_ARG_4);
             }
         }
         #endif
@@ -418,14 +425,14 @@ STATIC void emit_native_start_pass(emit_t *emit, pass_kind_t pass, scope_t *scop
         #endif
 
         // set code_state.fun_bc
-        ASM_MOV_REG_TO_LOCAL(emit->as, REG_ARG_1, offsetof(mp_code_state_t, fun_bc) / sizeof(uintptr_t));
+        ASM_MOV_LOCAL_REG(emit->as, offsetof(mp_code_state_t, fun_bc) / sizeof(uintptr_t), REG_ARG_1);
 
         // set code_state.ip (offset from start of this function to prelude info)
         // XXX this encoding may change size
-        ASM_MOV_IMM_TO_LOCAL_USING(emit->as, emit->prelude_offset, offsetof(mp_code_state_t, ip) / sizeof(uintptr_t), REG_ARG_1);
+        ASM_MOV_LOCAL_IMM_VIA(emit->as, offsetof(mp_code_state_t, ip) / sizeof(uintptr_t), emit->prelude_offset, REG_ARG_1);
 
         // put address of code_state into first arg
-        ASM_MOV_LOCAL_ADDR_TO_REG(emit->as, 0, REG_ARG_1);
+        ASM_MOV_REG_LOCAL_ADDR(emit->as, REG_ARG_1, 0);
 
         // call mp_setup_code_state to prepare code_state structure
         #if N_THUMB
@@ -438,11 +445,11 @@ STATIC void emit_native_start_pass(emit_t *emit, pass_kind_t pass, scope_t *scop
 
         // cache some locals in registers
         if (scope->num_locals > 0) {
-            ASM_MOV_LOCAL_TO_REG(emit->as, STATE_START + emit->n_state - 1 - 0, REG_LOCAL_1);
+            ASM_MOV_REG_LOCAL(emit->as, REG_LOCAL_1, STATE_START + emit->n_state - 1 - 0);
             if (scope->num_locals > 1) {
-                ASM_MOV_LOCAL_TO_REG(emit->as, STATE_START + emit->n_state - 1 - 1, REG_LOCAL_2);
+                ASM_MOV_REG_LOCAL(emit->as, REG_LOCAL_2, STATE_START + emit->n_state - 1 - 1);
                 if (scope->num_locals > 2) {
-                    ASM_MOV_LOCAL_TO_REG(emit->as, STATE_START + emit->n_state - 1 - 2, REG_LOCAL_3);
+                    ASM_MOV_REG_LOCAL(emit->as, REG_LOCAL_3, STATE_START + emit->n_state - 1 - 2);
                 }
             }
         }
@@ -606,7 +613,7 @@ STATIC void need_reg_single(emit_t *emit, int reg_needed, int skip_stack_pos) {
             stack_info_t *si = &emit->stack_info[i];
             if (si->kind == STACK_REG && si->data.u_reg == reg_needed) {
                 si->kind = STACK_VALUE;
-                ASM_MOV_REG_TO_LOCAL(emit->as, si->data.u_reg, emit->stack_start + i);
+                ASM_MOV_LOCAL_REG(emit->as, emit->stack_start + i, si->data.u_reg);
             }
         }
     }
@@ -617,7 +624,7 @@ STATIC void need_reg_all(emit_t *emit) {
         stack_info_t *si = &emit->stack_info[i];
         if (si->kind == STACK_REG) {
             si->kind = STACK_VALUE;
-            ASM_MOV_REG_TO_LOCAL(emit->as, si->data.u_reg, emit->stack_start + i);
+            ASM_MOV_LOCAL_REG(emit->as, emit->stack_start + i, si->data.u_reg);
         }
     }
 }
@@ -629,7 +636,7 @@ STATIC void need_stack_settled(emit_t *emit) {
         if (si->kind == STACK_REG) {
             DEBUG_printf("    reg(%u) to local(%u)\n", si->data.u_reg, emit->stack_start + i);
             si->kind = STACK_VALUE;
-            ASM_MOV_REG_TO_LOCAL(emit->as, si->data.u_reg, emit->stack_start + i);
+            ASM_MOV_LOCAL_REG(emit->as, emit->stack_start + i, si->data.u_reg);
         }
     }
     for (int i = 0; i < emit->stack_size; i++) {
@@ -637,7 +644,7 @@ STATIC void need_stack_settled(emit_t *emit) {
         if (si->kind == STACK_IMM) {
             DEBUG_printf("    imm(" INT_FMT ") to local(%u)\n", si->data.u_imm, emit->stack_start + i);
             si->kind = STACK_VALUE;
-            ASM_MOV_IMM_TO_LOCAL_USING(emit->as, si->data.u_imm, emit->stack_start + i, REG_TEMP0);
+            ASM_MOV_LOCAL_IMM_VIA(emit->as, emit->stack_start + i, si->data.u_imm, REG_TEMP0);
         }
     }
 }
@@ -649,7 +656,7 @@ STATIC void emit_access_stack(emit_t *emit, int pos, vtype_kind_t *vtype, int re
     *vtype = si->vtype;
     switch (si->kind) {
         case STACK_VALUE:
-            ASM_MOV_LOCAL_TO_REG(emit->as, emit->stack_start + emit->stack_size - pos, reg_dest);
+            ASM_MOV_REG_LOCAL(emit->as, reg_dest, emit->stack_start + emit->stack_size - pos);
             break;
 
         case STACK_REG:
@@ -659,7 +666,7 @@ STATIC void emit_access_stack(emit_t *emit, int pos, vtype_kind_t *vtype, int re
             break;
 
         case STACK_IMM:
-            ASM_MOV_IMM_TO_REG(emit->as, si->data.u_imm, reg_dest);
+            ASM_MOV_REG_IMM(emit->as, reg_dest, si->data.u_imm);
             break;
     }
 }
@@ -671,7 +678,7 @@ STATIC void emit_fold_stack_top(emit_t *emit, int reg_dest) {
     si[0] = si[1];
     if (si->kind == STACK_VALUE) {
         // if folded element was on the stack we need to put it in a register
-        ASM_MOV_LOCAL_TO_REG(emit->as, emit->stack_start + emit->stack_size - 1, reg_dest);
+        ASM_MOV_REG_LOCAL(emit->as, reg_dest, emit->stack_start + emit->stack_size - 1);
         si->kind = STACK_REG;
         si->data.u_reg = reg_dest;
     }
@@ -765,30 +772,30 @@ STATIC void emit_call(emit_t *emit, mp_fun_kind_t fun_kind) {
 
 STATIC void emit_call_with_imm_arg(emit_t *emit, mp_fun_kind_t fun_kind, mp_int_t arg_val, int arg_reg) {
     need_reg_all(emit);
-    ASM_MOV_IMM_TO_REG(emit->as, arg_val, arg_reg);
+    ASM_MOV_REG_IMM(emit->as, arg_reg, arg_val);
     ASM_CALL_IND(emit->as, mp_fun_table[fun_kind], fun_kind);
 }
 
 // the first arg is stored in the code aligned on a mp_uint_t boundary
 STATIC void emit_call_with_imm_arg_aligned(emit_t *emit, mp_fun_kind_t fun_kind, mp_int_t arg_val, int arg_reg) {
     need_reg_all(emit);
-    ASM_MOV_ALIGNED_IMM_TO_REG(emit->as, arg_val, arg_reg);
+    ASM_MOV_REG_ALIGNED_IMM(emit->as, arg_reg, arg_val);
     ASM_CALL_IND(emit->as, mp_fun_table[fun_kind], fun_kind);
 }
 
 STATIC void emit_call_with_2_imm_args(emit_t *emit, mp_fun_kind_t fun_kind, mp_int_t arg_val1, int arg_reg1, mp_int_t arg_val2, int arg_reg2) {
     need_reg_all(emit);
-    ASM_MOV_IMM_TO_REG(emit->as, arg_val1, arg_reg1);
-    ASM_MOV_IMM_TO_REG(emit->as, arg_val2, arg_reg2);
+    ASM_MOV_REG_IMM(emit->as, arg_reg1, arg_val1);
+    ASM_MOV_REG_IMM(emit->as, arg_reg2, arg_val2);
     ASM_CALL_IND(emit->as, mp_fun_table[fun_kind], fun_kind);
 }
 
 // the first arg is stored in the code aligned on a mp_uint_t boundary
 STATIC void emit_call_with_3_imm_args_and_first_aligned(emit_t *emit, mp_fun_kind_t fun_kind, mp_int_t arg_val1, int arg_reg1, mp_int_t arg_val2, int arg_reg2, mp_int_t arg_val3, int arg_reg3) {
     need_reg_all(emit);
-    ASM_MOV_ALIGNED_IMM_TO_REG(emit->as, arg_val1, arg_reg1);
-    ASM_MOV_IMM_TO_REG(emit->as, arg_val2, arg_reg2);
-    ASM_MOV_IMM_TO_REG(emit->as, arg_val3, arg_reg3);
+    ASM_MOV_REG_ALIGNED_IMM(emit->as, arg_reg1, arg_val1);
+    ASM_MOV_REG_IMM(emit->as, arg_reg2, arg_val2);
+    ASM_MOV_REG_IMM(emit->as, arg_reg3, arg_val3);
     ASM_CALL_IND(emit->as, mp_fun_table[fun_kind], fun_kind);
 }
 
@@ -808,19 +815,19 @@ STATIC void emit_get_stack_pointer_to_reg_for_pop(emit_t *emit, mp_uint_t reg_de
             si->kind = STACK_VALUE;
             switch (si->vtype) {
                 case VTYPE_PYOBJ:
-                    ASM_MOV_IMM_TO_LOCAL_USING(emit->as, si->data.u_imm, emit->stack_start + emit->stack_size - 1 - i, reg_dest);
+                    ASM_MOV_LOCAL_IMM_VIA(emit->as, emit->stack_start + emit->stack_size - 1 - i, si->data.u_imm, reg_dest);
                     break;
                 case VTYPE_BOOL:
                     if (si->data.u_imm == 0) {
-                        ASM_MOV_IMM_TO_LOCAL_USING(emit->as, (mp_uint_t)mp_const_false, emit->stack_start + emit->stack_size - 1 - i, reg_dest);
+                        ASM_MOV_LOCAL_IMM_VIA(emit->as, emit->stack_start + emit->stack_size - 1 - i, (mp_uint_t)mp_const_false, reg_dest);
                     } else {
-                        ASM_MOV_IMM_TO_LOCAL_USING(emit->as, (mp_uint_t)mp_const_true, emit->stack_start + emit->stack_size - 1 - i, reg_dest);
+                        ASM_MOV_LOCAL_IMM_VIA(emit->as, emit->stack_start + emit->stack_size - 1 - i, (mp_uint_t)mp_const_true, reg_dest);
                     }
                     si->vtype = VTYPE_PYOBJ;
                     break;
                 case VTYPE_INT:
                 case VTYPE_UINT:
-                    ASM_MOV_IMM_TO_LOCAL_USING(emit->as, (uintptr_t)MP_OBJ_NEW_SMALL_INT(si->data.u_imm), emit->stack_start + emit->stack_size - 1 - i, reg_dest);
+                    ASM_MOV_LOCAL_IMM_VIA(emit->as, emit->stack_start + emit->stack_size - 1 - i, (uintptr_t)MP_OBJ_NEW_SMALL_INT(si->data.u_imm), reg_dest);
                     si->vtype = VTYPE_PYOBJ;
                     break;
                 default:
@@ -838,9 +845,9 @@ STATIC void emit_get_stack_pointer_to_reg_for_pop(emit_t *emit, mp_uint_t reg_de
         stack_info_t *si = &emit->stack_info[emit->stack_size - 1 - i];
         if (si->vtype != VTYPE_PYOBJ) {
             mp_uint_t local_num = emit->stack_start + emit->stack_size - 1 - i;
-            ASM_MOV_LOCAL_TO_REG(emit->as, local_num, REG_ARG_1);
+            ASM_MOV_REG_LOCAL(emit->as, REG_ARG_1, local_num);
             emit_call_with_imm_arg(emit, MP_F_CONVERT_NATIVE_TO_OBJ, si->vtype, REG_ARG_2); // arg2 = type
-            ASM_MOV_REG_TO_LOCAL(emit->as, REG_RET, local_num);
+            ASM_MOV_LOCAL_REG(emit->as, local_num, REG_RET);
             si->vtype = VTYPE_PYOBJ;
             DEBUG_printf("  convert_native_to_obj(local_num=" UINT_FMT ")\n", local_num);
         }
@@ -848,7 +855,7 @@ STATIC void emit_get_stack_pointer_to_reg_for_pop(emit_t *emit, mp_uint_t reg_de
 
     // Adujust the stack for a pop of n_pop items, and load the stack pointer into reg_dest.
     adjust_stack(emit, -n_pop);
-    ASM_MOV_LOCAL_ADDR_TO_REG(emit->as, emit->stack_start + emit->stack_size, reg_dest);
+    ASM_MOV_REG_LOCAL_ADDR(emit->as, reg_dest, emit->stack_start + emit->stack_size);
 }
 
 // vtype of all n_push objects is VTYPE_PYOBJ
@@ -858,7 +865,7 @@ STATIC void emit_get_stack_pointer_to_reg_for_push(emit_t *emit, mp_uint_t reg_d
         emit->stack_info[emit->stack_size + i].kind = STACK_VALUE;
         emit->stack_info[emit->stack_size + i].vtype = VTYPE_PYOBJ;
     }
-    ASM_MOV_LOCAL_ADDR_TO_REG(emit->as, emit->stack_start + emit->stack_size, reg_dest);
+    ASM_MOV_REG_LOCAL_ADDR(emit->as, reg_dest, emit->stack_start + emit->stack_size);
     adjust_stack(emit, n_push);
 }
 
@@ -881,7 +888,7 @@ STATIC void emit_native_import_name(emit_t *emit, qstr qst) {
         stack_info_t *top = peek_stack(emit, 0);
         if (top->vtype == VTYPE_PTR_NONE) {
             emit_pre_pop_discard(emit);
-            ASM_MOV_IMM_TO_REG(emit->as, (mp_uint_t)mp_const_none, REG_ARG_2);
+            ASM_MOV_REG_IMM(emit->as, REG_ARG_2, (mp_uint_t)mp_const_none);
         } else {
             vtype_kind_t vtype_fromlist;
             emit_pre_pop_reg(emit, &vtype_fromlist, REG_ARG_2);
@@ -891,7 +898,7 @@ STATIC void emit_native_import_name(emit_t *emit, qstr qst) {
         // level argument should be an immediate integer
         top = peek_stack(emit, 0);
         assert(top->vtype == VTYPE_INT && top->kind == STACK_IMM);
-        ASM_MOV_IMM_TO_REG(emit->as, (mp_uint_t)MP_OBJ_NEW_SMALL_INT(top->data.u_imm), REG_ARG_3);
+        ASM_MOV_REG_IMM(emit->as, REG_ARG_3, (mp_uint_t)MP_OBJ_NEW_SMALL_INT(top->data.u_imm));
         emit_pre_pop_discard(emit);
 
     } else {
@@ -981,7 +988,7 @@ STATIC void emit_native_load_const_str(emit_t *emit, qstr qst) {
 STATIC void emit_native_load_const_obj(emit_t *emit, mp_obj_t obj) {
     emit_native_pre(emit);
     need_reg_single(emit, REG_RET, 0);
-    ASM_MOV_ALIGNED_IMM_TO_REG(emit->as, (mp_uint_t)obj, REG_RET);
+    ASM_MOV_REG_ALIGNED_IMM(emit->as, REG_RET, (mp_uint_t)obj);
     emit_post_push_reg(emit, VTYPE_PYOBJ, REG_RET);
 }
 
@@ -1006,9 +1013,9 @@ STATIC void emit_native_load_fast(emit_t *emit, qstr qst, mp_uint_t local_num) {
     } else {
         need_reg_single(emit, REG_TEMP0, 0);
         if (emit->do_viper_types) {
-            ASM_MOV_LOCAL_TO_REG(emit->as, local_num - REG_LOCAL_NUM, REG_TEMP0);
+            ASM_MOV_REG_LOCAL(emit->as, REG_TEMP0, local_num - REG_LOCAL_NUM);
         } else {
-            ASM_MOV_LOCAL_TO_REG(emit->as, STATE_START + emit->n_state - 1 - local_num, REG_TEMP0);
+            ASM_MOV_REG_LOCAL(emit->as, REG_TEMP0, STATE_START + emit->n_state - 1 - local_num);
         }
         emit_post_push_reg(emit, vtype, REG_TEMP0);
     }
@@ -1134,7 +1141,7 @@ STATIC void emit_native_load_subscr(emit_t *emit) {
                             break;
                         }
                         #endif
-                        ASM_MOV_IMM_TO_REG(emit->as, index_value, reg_index);
+                        ASM_MOV_REG_IMM(emit->as, reg_index, index_value);
                         ASM_ADD_REG_REG(emit->as, reg_index, reg_base); // add index to base
                         reg_base = reg_index;
                     }
@@ -1151,7 +1158,7 @@ STATIC void emit_native_load_subscr(emit_t *emit) {
                             break;
                         }
                         #endif
-                        ASM_MOV_IMM_TO_REG(emit->as, index_value << 1, reg_index);
+                        ASM_MOV_REG_IMM(emit->as, reg_index, index_value << 1);
                         ASM_ADD_REG_REG(emit->as, reg_index, reg_base); // add 2*index to base
                         reg_base = reg_index;
                     }
@@ -1168,7 +1175,7 @@ STATIC void emit_native_load_subscr(emit_t *emit) {
                             break;
                         }
                         #endif
-                        ASM_MOV_IMM_TO_REG(emit->as, index_value << 2, reg_index);
+                        ASM_MOV_REG_IMM(emit->as, reg_index, index_value << 2);
                         ASM_ADD_REG_REG(emit->as, reg_index, reg_base); // add 4*index to base
                         reg_base = reg_index;
                     }
@@ -1233,9 +1240,9 @@ STATIC void emit_native_store_fast(emit_t *emit, qstr qst, mp_uint_t local_num)
     } else {
         emit_pre_pop_reg(emit, &vtype, REG_TEMP0);
         if (emit->do_viper_types) {
-            ASM_MOV_REG_TO_LOCAL(emit->as, REG_TEMP0, local_num - REG_LOCAL_NUM);
+            ASM_MOV_LOCAL_REG(emit->as, local_num - REG_LOCAL_NUM, REG_TEMP0);
         } else {
-            ASM_MOV_REG_TO_LOCAL(emit->as, REG_TEMP0, STATE_START + emit->n_state - 1 - local_num);
+            ASM_MOV_LOCAL_REG(emit->as, STATE_START + emit->n_state - 1 - local_num, REG_TEMP0);
         }
     }
     emit_post(emit);
@@ -1354,7 +1361,7 @@ STATIC void emit_native_store_subscr(emit_t *emit) {
                             break;
                         }
                         #endif
-                        ASM_MOV_IMM_TO_REG(emit->as, index_value, reg_index);
+                        ASM_MOV_REG_IMM(emit->as, reg_index, index_value);
                         #if N_ARM
                         asm_arm_strb_reg_reg_reg(emit->as, reg_value, reg_base, reg_index);
                         return;
@@ -1375,7 +1382,7 @@ STATIC void emit_native_store_subscr(emit_t *emit) {
                             break;
                         }
                         #endif
-                        ASM_MOV_IMM_TO_REG(emit->as, index_value << 1, reg_index);
+                        ASM_MOV_REG_IMM(emit->as, reg_index, index_value << 1);
                         #if N_ARM
                         asm_arm_strh_reg_reg_reg(emit->as, reg_value, reg_base, reg_index);
                         return;
@@ -1396,7 +1403,7 @@ STATIC void emit_native_store_subscr(emit_t *emit) {
                             break;
                         }
                         #endif
-                        ASM_MOV_IMM_TO_REG(emit->as, index_value << 2, reg_index);
+                        ASM_MOV_REG_IMM(emit->as, reg_index, index_value << 2);
                         #if N_ARM
                         asm_arm_str_reg_reg_reg(emit->as, reg_value, reg_base, reg_index);
                         return;
@@ -1773,7 +1780,7 @@ STATIC void emit_native_get_iter(emit_t *emit, bool use_stack) {
         emit_call(emit, MP_F_NATIVE_GETITER);
     } else {
         // mp_getiter will allocate the iter_buf on the heap
-        ASM_MOV_IMM_TO_REG(emit->as, 0, REG_ARG_2);
+        ASM_MOV_REG_IMM(emit->as, REG_ARG_2, 0);
         emit_call(emit, MP_F_NATIVE_GETITER);
         emit_post_push_reg(emit, VTYPE_PYOBJ, REG_RET);
     }
@@ -1784,7 +1791,7 @@ STATIC void emit_native_for_iter(emit_t *emit, mp_uint_t label) {
     emit_get_stack_pointer_to_reg_for_pop(emit, REG_ARG_1, MP_OBJ_ITER_BUF_NSLOTS);
     adjust_stack(emit, MP_OBJ_ITER_BUF_NSLOTS);
     emit_call(emit, MP_F_NATIVE_ITERNEXT);
-    ASM_MOV_IMM_TO_REG(emit->as, (mp_uint_t)MP_OBJ_STOP_ITERATION, REG_TEMP1);
+    ASM_MOV_REG_IMM(emit->as, REG_TEMP1, (mp_uint_t)MP_OBJ_STOP_ITERATION);
     ASM_JUMP_IF_REG_EQ(emit->as, REG_RET, REG_TEMP1, label);
     emit_post_push_reg(emit, VTYPE_PYOBJ, REG_RET);
 }
@@ -2128,12 +2135,12 @@ STATIC void emit_native_make_closure(emit_t *emit, scope_t *scope, mp_uint_t n_c
     emit_native_pre(emit);
     if (n_pos_defaults == 0 && n_kw_defaults == 0) {
         emit_get_stack_pointer_to_reg_for_pop(emit, REG_ARG_3, n_closed_over);
-        ASM_MOV_IMM_TO_REG(emit->as, n_closed_over, REG_ARG_2);
+        ASM_MOV_REG_IMM(emit->as, REG_ARG_2, n_closed_over);
     } else {
         emit_get_stack_pointer_to_reg_for_pop(emit, REG_ARG_3, n_closed_over + 2);
-        ASM_MOV_IMM_TO_REG(emit->as, 0x100 | n_closed_over, REG_ARG_2);
+        ASM_MOV_REG_IMM(emit->as, REG_ARG_2, 0x100 | n_closed_over);
     }
-    ASM_MOV_ALIGNED_IMM_TO_REG(emit->as, (mp_uint_t)scope->raw_code, REG_ARG_1);
+    ASM_MOV_REG_ALIGNED_IMM(emit->as, REG_ARG_1, (mp_uint_t)scope->raw_code);
     ASM_CALL_IND(emit->as, mp_fun_table[MP_F_MAKE_CLOSURE_FROM_RAW_CODE], MP_F_MAKE_CLOSURE_FROM_RAW_CODE);
     emit_post_push_reg(emit, VTYPE_PYOBJ, REG_RET);
 }
@@ -2212,9 +2219,9 @@ STATIC void emit_native_return_value(emit_t *emit) {
         if (peek_vtype(emit, 0) == VTYPE_PTR_NONE) {
             emit_pre_pop_discard(emit);
             if (emit->return_vtype == VTYPE_PYOBJ) {
-                ASM_MOV_IMM_TO_REG(emit->as, (mp_uint_t)mp_const_none, REG_RET);
+                ASM_MOV_REG_IMM(emit->as, REG_RET, (mp_uint_t)mp_const_none);
             } else {
-                ASM_MOV_IMM_TO_REG(emit->as, 0, REG_RET);
+                ASM_MOV_REG_IMM(emit->as, REG_RET, 0);
             }
         } else {
             vtype_kind_t vtype;

+ 29 - 16
py/gc.c

@@ -44,6 +44,10 @@
 // make this 1 to dump the heap each time it changes
 #define EXTENSIVE_HEAP_PROFILING (0)
 
+// make this 1 to zero out swept memory to more eagerly
+// detect untraced object still in use
+#define CLEAR_ON_SWEEP (0)
+
 #define WORDS_PER_BLOCK ((MICROPY_BYTES_PER_GC_BLOCK) / BYTES_PER_WORD)
 #define BYTES_PER_BLOCK (MICROPY_BYTES_PER_GC_BLOCK)
 
@@ -191,6 +195,14 @@ bool gc_is_locked(void) {
         && ptr < (void*)MP_STATE_MEM(gc_pool_end)        /* must be below end of pool */ \
     )
 
+#ifndef TRACE_MARK
+#if DEBUG_PRINT
+#define TRACE_MARK(block, ptr) DEBUG_printf("gc_mark(%p)\n", ptr)
+#else
+#define TRACE_MARK(block, ptr)
+#endif
+#endif
+
 // ptr should be of type void*
 #define VERIFY_MARK_AND_PUSH(ptr) \
     do { \
@@ -198,7 +210,7 @@ bool gc_is_locked(void) {
             size_t _block = BLOCK_FROM_PTR(ptr); \
             if (ATB_GET_KIND(_block) == AT_HEAD) { \
                 /* an unmarked head, mark it, and push it on gc stack */ \
-                DEBUG_printf("gc_mark(%p)\n", ptr); \
+                TRACE_MARK(_block, ptr); \
                 ATB_HEAD_TO_MARK(_block); \
                 if (MP_STATE_MEM(gc_sp) < &MP_STATE_MEM(gc_stack)[MICROPY_ALLOC_GC_STACK_SIZE]) { \
                     *MP_STATE_MEM(gc_sp)++ = _block; \
@@ -277,7 +289,7 @@ STATIC void gc_sweep(void) {
                 }
 #endif
                 free_tail = 1;
-                DEBUG_printf("gc_sweep(%x)\n", PTR_FROM_BLOCK(block));
+                DEBUG_printf("gc_sweep(%p)\n", PTR_FROM_BLOCK(block));
                 #if MICROPY_PY_GC_COLLECT_RETVAL
                 MP_STATE_MEM(gc_collected)++;
                 #endif
@@ -286,6 +298,9 @@ STATIC void gc_sweep(void) {
             case AT_TAIL:
                 if (free_tail) {
                     ATB_ANY_TO_FREE(block);
+                    #if CLEAR_ON_SWEEP
+                    memset((void*)PTR_FROM_BLOCK(block), 0, BYTES_PER_BLOCK);
+                    #endif
                 }
                 break;
 
@@ -305,11 +320,18 @@ void gc_collect_start(void) {
     #endif
     MP_STATE_MEM(gc_stack_overflow) = 0;
     MP_STATE_MEM(gc_sp) = MP_STATE_MEM(gc_stack);
+
     // Trace root pointers.  This relies on the root pointers being organised
     // correctly in the mp_state_ctx structure.  We scan nlr_top, dict_locals,
     // dict_globals, then the root pointer section of mp_state_vm.
     void **ptrs = (void**)(void*)&mp_state_ctx;
     gc_collect_root(ptrs, offsetof(mp_state_ctx_t, vm.qstr_last_chunk) / sizeof(void*));
+
+    #if MICROPY_ENABLE_PYSTACK
+    // Trace root pointers from the Python stack.
+    ptrs = (void**)(void*)MP_STATE_THREAD(pystack_start);
+    gc_collect_root(ptrs, (MP_STATE_THREAD(pystack_cur) - MP_STATE_THREAD(pystack_start)) / sizeof(void*));
+    #endif
 }
 
 void gc_collect_root(void **ptrs, size_t len) {
@@ -628,27 +650,18 @@ void *gc_realloc(void *ptr_in, size_t n_bytes, bool allow_move) {
 
     void *ptr = ptr_in;
 
-    // sanity check the ptr
-    if (!VERIFY_PTR(ptr)) {
-        return NULL;
-    }
-
-    // get first block
-    size_t block = BLOCK_FROM_PTR(ptr);
-
     GC_ENTER();
 
-    // sanity check the ptr is pointing to the head of a block
-    if (ATB_GET_KIND(block) != AT_HEAD) {
-        GC_EXIT();
-        return NULL;
-    }
-
     if (MP_STATE_MEM(gc_lock_depth) > 0) {
         GC_EXIT();
         return NULL;
     }
 
+    // get the GC block number corresponding to this pointer
+    assert(VERIFY_PTR(ptr));
+    size_t block = BLOCK_FROM_PTR(ptr);
+    assert(ATB_GET_KIND(block) == AT_HEAD);
+
     // compute number of new blocks that are requested
     size_t new_blocks = (n_bytes + BYTES_PER_BLOCK - 1) / BYTES_PER_BLOCK;
 

+ 18 - 6
py/malloc.c

@@ -39,6 +39,9 @@
 #endif
 
 #if MICROPY_MEM_STATS
+#if !MICROPY_MALLOC_USES_ALLOCATED_SIZE
+#error MICROPY_MEM_STATS requires MICROPY_MALLOC_USES_ALLOCATED_SIZE
+#endif
 #define UPDATE_PEAK() { if (MP_STATE_MEM(current_bytes_allocated) > MP_STATE_MEM(peak_bytes_allocated)) MP_STATE_MEM(peak_bytes_allocated) = MP_STATE_MEM(current_bytes_allocated); }
 #endif
 
@@ -114,9 +117,6 @@ void *m_malloc_with_finaliser(size_t num_bytes) {
 
 void *m_malloc0(size_t num_bytes) {
     void *ptr = m_malloc(num_bytes);
-    if (ptr == NULL && num_bytes != 0) {
-        m_malloc_fail(num_bytes);
-    }
     // If this config is set then the GC clears all memory, so we don't need to.
     #if !MICROPY_GC_CONSERVATIVE_CLEAR
     memset(ptr, 0, num_bytes);
@@ -144,7 +144,11 @@ void *m_realloc(void *ptr, size_t new_num_bytes) {
     MP_STATE_MEM(current_bytes_allocated) += diff;
     UPDATE_PEAK();
 #endif
-    //DEBUG_printf("realloc %p, %d, %d : %p\n", ptr, old_num_bytes, new_num_bytes, new_ptr);
+    #if MICROPY_MALLOC_USES_ALLOCATED_SIZE
+    DEBUG_printf("realloc %p, %d, %d : %p\n", ptr, old_num_bytes, new_num_bytes, new_ptr);
+    #else
+    DEBUG_printf("realloc %p, %d : %p\n", ptr, new_num_bytes, new_ptr);
+    #endif
     return new_ptr;
 }
 
@@ -168,7 +172,11 @@ void *m_realloc_maybe(void *ptr, size_t new_num_bytes, bool allow_move) {
         UPDATE_PEAK();
     }
 #endif
-    //DEBUG_printf("realloc %p, %d, %d : %p\n", ptr, old_num_bytes, new_num_bytes, new_ptr);
+    #if MICROPY_MALLOC_USES_ALLOCATED_SIZE
+    DEBUG_printf("realloc %p, %d, %d : %p\n", ptr, old_num_bytes, new_num_bytes, new_ptr);
+    #else
+    DEBUG_printf("realloc %p, %d, %d : %p\n", ptr, new_num_bytes, new_ptr);
+    #endif
     return new_ptr;
 }
 
@@ -181,7 +189,11 @@ void m_free(void *ptr) {
 #if MICROPY_MEM_STATS
     MP_STATE_MEM(current_bytes_allocated) -= num_bytes;
 #endif
-    //DEBUG_printf("free %p, %d\n", ptr, num_bytes);
+    #if MICROPY_MALLOC_USES_ALLOCATED_SIZE
+    DEBUG_printf("free %p, %d\n", ptr, num_bytes);
+    #else
+    DEBUG_printf("free %p\n", ptr);
+    #endif
 }
 
 #if MICROPY_MEM_STATS

+ 14 - 0
py/map.c

@@ -33,6 +33,13 @@
 #include "py/misc.h"
 #include "py/runtime.h"
 
+#if MICROPY_DEBUG_VERBOSE // print debugging info
+#define DEBUG_PRINT (1)
+#else // don't print debugging info
+#define DEBUG_PRINT (0)
+#define DEBUG_printf(...) (void)0
+#endif
+
 // Fixed empty map. Useful when need to call kw-receiving functions
 // without any keywords from C, etc.
 const mp_map_t mp_const_empty_map = {
@@ -114,6 +121,7 @@ void mp_map_clear(mp_map_t *map) {
 STATIC void mp_map_rehash(mp_map_t *map) {
     size_t old_alloc = map->alloc;
     size_t new_alloc = get_hash_alloc_greater_or_equal_to(map->alloc + 1);
+    DEBUG_printf("mp_map_rehash(%p): " UINT_FMT " -> " UINT_FMT "\n", map, old_alloc, new_alloc);
     mp_map_elem_t *old_table = map->table;
     mp_map_elem_t *new_table = m_new0(mp_map_elem_t, new_alloc);
     // If we reach this point, table resizing succeeded, now we can edit the old map.
@@ -162,6 +170,7 @@ mp_map_elem_t *mp_map_lookup(mp_map_t *map, mp_obj_t index, mp_map_lookup_kind_t
     if (map->is_ordered) {
         for (mp_map_elem_t *elem = &map->table[0], *top = &map->table[map->used]; elem < top; elem++) {
             if (elem->key == index || (!compare_only_ptrs && mp_obj_equal(elem->key, index))) {
+                #if MICROPY_PY_COLLECTIONS_ORDEREDDICT
                 if (MP_UNLIKELY(lookup_kind == MP_MAP_LOOKUP_REMOVE_IF_FOUND)) {
                     // remove the found element by moving the rest of the array down
                     mp_obj_t value = elem->value;
@@ -172,9 +181,11 @@ mp_map_elem_t *mp_map_lookup(mp_map_t *map, mp_obj_t index, mp_map_lookup_kind_t
                     elem->key = MP_OBJ_NULL;
                     elem->value = value;
                 }
+                #endif
                 return elem;
             }
         }
+        #if MICROPY_PY_COLLECTIONS_ORDEREDDICT
         if (MP_LIKELY(lookup_kind != MP_MAP_LOOKUP_ADD_IF_NOT_FOUND)) {
             return NULL;
         }
@@ -190,6 +201,9 @@ mp_map_elem_t *mp_map_lookup(mp_map_t *map, mp_obj_t index, mp_map_lookup_kind_t
             map->all_keys_are_qstrs = 0;
         }
         return elem;
+        #else
+        return NULL;
+        #endif
     }
 
     // map is a hash table (not an ordered array), so do a hash lookup

+ 2 - 0
py/misc.h

@@ -63,8 +63,10 @@ typedef unsigned int uint;
 #define m_new_obj_var_maybe(obj_type, var_type, var_num) ((obj_type*)m_malloc_maybe(sizeof(obj_type) + sizeof(var_type) * (var_num)))
 #if MICROPY_ENABLE_FINALISER
 #define m_new_obj_with_finaliser(type) ((type*)(m_malloc_with_finaliser(sizeof(type))))
+#define m_new_obj_var_with_finaliser(type, var_type, var_num) ((type*)m_malloc_with_finaliser(sizeof(type) + sizeof(var_type) * (var_num)))
 #else
 #define m_new_obj_with_finaliser(type) m_new_obj(type)
+#define m_new_obj_var_with_finaliser(type, var_type, var_num) m_new_obj_var(type, var_type, var_num)
 #endif
 #if MICROPY_MALLOC_USES_ALLOCATED_SIZE
 #define m_renew(type, ptr, old_num, new_num) ((type*)(m_realloc((ptr), sizeof(type) * (old_num), sizeof(type) * (new_num))))

+ 2 - 2
py/mkenv.mk

@@ -58,9 +58,9 @@ CXX += -m32
 LD += -m32
 endif
 
-MAKE_FROZEN = $(TOP)/tools/make-frozen.py
+MAKE_FROZEN = $(PYTHON) $(TOP)/tools/make-frozen.py
 MPY_CROSS = $(TOP)/mpy-cross/mpy-cross
-MPY_TOOL = $(TOP)/tools/mpy-tool.py
+MPY_TOOL = $(PYTHON) $(TOP)/tools/mpy-tool.py
 
 all:
 .PHONY: all

+ 22 - 1
py/mkrules.mk

@@ -119,7 +119,7 @@ $(BUILD)/frozen_mpy/%.mpy: $(FROZEN_MPY_DIR)/%.py $(TOP)/mpy-cross/mpy-cross
 # to build frozen_mpy.c from all .mpy files
 $(BUILD)/frozen_mpy.c: $(FROZEN_MPY_MPY_FILES) $(BUILD)/genhdr/qstrdefs.generated.h
 	@$(ECHO) "Creating $@"
-	$(Q)$(PYTHON) $(MPY_TOOL) -f -q $(BUILD)/genhdr/qstrdefs.preprocessed.h $(FROZEN_MPY_MPY_FILES) > $@
+	$(Q)$(MPY_TOOL) -f -q $(BUILD)/genhdr/qstrdefs.preprocessed.h $(FROZEN_MPY_MPY_FILES) > $@
 endif
 
 ifneq ($(PROG),)
@@ -160,6 +160,27 @@ clean:
 	$(RM) -rf $(BUILD) $(CLEAN_EXTRA)
 .PHONY: clean
 
+# Clean every non-git file from FROZEN_DIR/FROZEN_MPY_DIR, but making a backup.
+# We run rmdir below to avoid empty backup dir (it will silently fail if backup
+# is non-empty).
+clean-frozen:
+	if [ -n "$(FROZEN_MPY_DIR)" ]; then \
+	backup_dir=$(FROZEN_MPY_DIR).$$(date +%Y%m%dT%H%M%S); mkdir $$backup_dir; \
+	cd $(FROZEN_MPY_DIR); git status --ignored -u all -s . | awk ' {print $$2}' \
+	| xargs --no-run-if-empty cp --parents -t ../$$backup_dir; \
+	rmdir ../$$backup_dir 2>/dev/null || true; \
+	git clean -d -f .; \
+	fi
+
+	if [ -n "$(FROZEN_DIR)" ]; then \
+	backup_dir=$(FROZEN_DIR).$$(date +%Y%m%dT%H%M%S); mkdir $$backup_dir; \
+	cd $(FROZEN_DIR); git status --ignored -u all -s . | awk ' {print $$2}' \
+	| xargs --no-run-if-empty cp --parents -t ../$$backup_dir; \
+	rmdir ../$$backup_dir 2>/dev/null || true; \
+	git clean -d -f .; \
+	fi
+.PHONY: clean-frozen
+
 print-cfg:
 	$(ECHO) "PY_SRC = $(PY_SRC)"
 	$(ECHO) "BUILD  = $(BUILD)"

+ 38 - 34
py/modbuiltins.c

@@ -137,7 +137,7 @@ MP_DEFINE_CONST_FUN_OBJ_1(mp_builtin_callable_obj, mp_builtin_callable);
 STATIC mp_obj_t mp_builtin_chr(mp_obj_t o_in) {
     #if MICROPY_PY_BUILTINS_STR_UNICODE
     mp_uint_t c = mp_obj_get_int(o_in);
-    char str[4];
+    uint8_t str[4];
     int len = 0;
     if (c < 0x80) {
         *str = c; len = 1;
@@ -159,12 +159,12 @@ STATIC mp_obj_t mp_builtin_chr(mp_obj_t o_in) {
     } else {
         mp_raise_ValueError("chr() arg not in range(0x110000)");
     }
-    return mp_obj_new_str(str, len, true);
+    return mp_obj_new_str_via_qstr((char*)str, len);
     #else
     mp_int_t ord = mp_obj_get_int(o_in);
     if (0 <= ord && ord <= 0xff) {
-        char str[1] = {ord};
-        return mp_obj_new_str(str, 1, true);
+        uint8_t str[1] = {ord};
+        return mp_obj_new_str_via_qstr((char*)str, 1);
     } else {
         mp_raise_ValueError("chr() arg not in range(256)");
     }
@@ -383,46 +383,52 @@ STATIC mp_obj_t mp_builtin_pow(size_t n_args, const mp_obj_t *args) {
 }
 MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mp_builtin_pow_obj, 2, 3, mp_builtin_pow);
 
-STATIC mp_obj_t mp_builtin_print(size_t n_args, const mp_obj_t *args, mp_map_t *kwargs) {
-    mp_map_elem_t *sep_elem = mp_map_lookup(kwargs, MP_OBJ_NEW_QSTR(MP_QSTR_sep), MP_MAP_LOOKUP);
-    mp_map_elem_t *end_elem = mp_map_lookup(kwargs, MP_OBJ_NEW_QSTR(MP_QSTR_end), MP_MAP_LOOKUP);
-    const char *sep_data = " ";
-    size_t sep_len = 1;
-    const char *end_data = "\n";
-    size_t end_len = 1;
-    if (sep_elem != NULL && sep_elem->value != mp_const_none) {
-        sep_data = mp_obj_str_get_data(sep_elem->value, &sep_len);
-    }
-    if (end_elem != NULL && end_elem->value != mp_const_none) {
-        end_data = mp_obj_str_get_data(end_elem->value, &end_len);
-    }
-    #if MICROPY_PY_IO && MICROPY_PY_SYS_STDFILES
-    void *stream_obj = &mp_sys_stdout_obj;
-    mp_map_elem_t *file_elem = mp_map_lookup(kwargs, MP_OBJ_NEW_QSTR(MP_QSTR_file), MP_MAP_LOOKUP);
-    if (file_elem != NULL && file_elem->value != mp_const_none) {
-        stream_obj = MP_OBJ_TO_PTR(file_elem->value); // XXX may not be a concrete object
-    }
+STATIC mp_obj_t mp_builtin_print(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
+    enum { ARG_sep, ARG_end, ARG_file };
+    static const mp_arg_t allowed_args[] = {
+        { MP_QSTR_sep, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_QSTR(MP_QSTR__space_)} },
+        { MP_QSTR_end, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_QSTR(MP_QSTR__0x0a_)} },
+        #if MICROPY_PY_IO && MICROPY_PY_SYS_STDFILES
+        { MP_QSTR_file, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_PTR(&mp_sys_stdout_obj)} },
+        #endif
+    };
 
-    mp_print_t print = {stream_obj, mp_stream_write_adaptor};
+    // parse args (a union is used to reduce the amount of C stack that is needed)
+    union {
+        mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
+        size_t len[2];
+    } u;
+    mp_arg_parse_all(0, NULL, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, u.args);
+
+    #if MICROPY_PY_IO && MICROPY_PY_SYS_STDFILES
+    // TODO file may not be a concrete object (eg it could be a small-int)
+    mp_print_t print = {MP_OBJ_TO_PTR(u.args[ARG_file].u_obj), mp_stream_write_adaptor};
     #endif
+
+    // extract the objects first because we are going to use the other part of the union
+    mp_obj_t sep = u.args[ARG_sep].u_obj;
+    mp_obj_t end = u.args[ARG_end].u_obj;
+    const char *sep_data = mp_obj_str_get_data(sep, &u.len[0]);
+    const char *end_data = mp_obj_str_get_data(end, &u.len[1]);
+
     for (size_t i = 0; i < n_args; i++) {
         if (i > 0) {
             #if MICROPY_PY_IO && MICROPY_PY_SYS_STDFILES
-            mp_stream_write_adaptor(stream_obj, sep_data, sep_len);
+            mp_stream_write_adaptor(print.data, sep_data, u.len[0]);
             #else
-            mp_print_strn(&mp_plat_print, sep_data, sep_len, 0, 0, 0);
+            mp_print_strn(&mp_plat_print, sep_data, u.len[0], 0, 0, 0);
             #endif
         }
         #if MICROPY_PY_IO && MICROPY_PY_SYS_STDFILES
-        mp_obj_print_helper(&print, args[i], PRINT_STR);
+        mp_obj_print_helper(&print, pos_args[i], PRINT_STR);
         #else
-        mp_obj_print_helper(&mp_plat_print, args[i], PRINT_STR);
+        mp_obj_print_helper(&mp_plat_print, pos_args[i], PRINT_STR);
         #endif
     }
     #if MICROPY_PY_IO && MICROPY_PY_SYS_STDFILES
-    mp_stream_write_adaptor(stream_obj, end_data, end_len);
+    mp_stream_write_adaptor(print.data, end_data, u.len[1]);
     #else
-    mp_print_strn(&mp_plat_print, end_data, end_len, 0, 0, 0);
+    mp_print_strn(&mp_plat_print, end_data, u.len[1], 0, 0, 0);
     #endif
     return mp_const_none;
 }
@@ -457,16 +463,14 @@ STATIC mp_obj_t mp_builtin_round(size_t n_args, const mp_obj_t *args) {
         return o_in;
     }
 #if MICROPY_PY_BUILTINS_FLOAT
-    mp_int_t num_dig = 0;
+    mp_float_t val = mp_obj_get_float(o_in);
     if (n_args > 1) {
-        num_dig = mp_obj_get_int(args[1]);
-        mp_float_t val = mp_obj_get_float(o_in);
+        mp_int_t num_dig = mp_obj_get_int(args[1]);
         mp_float_t mult = MICROPY_FLOAT_C_FUN(pow)(10, num_dig);
         // TODO may lead to overflow
         mp_float_t rounded = MICROPY_FLOAT_C_FUN(nearbyint)(val * mult) / mult;
         return mp_obj_new_float(rounded);
     }
-    mp_float_t val = mp_obj_get_float(o_in);
     mp_float_t rounded = MICROPY_FLOAT_C_FUN(nearbyint)(val);
     return mp_obj_new_int_from_float(rounded);
 #else

+ 3 - 3
py/modio.c

@@ -131,7 +131,7 @@ STATIC const mp_obj_type_t bufwriter_type = {
 };
 #endif // MICROPY_PY_IO_BUFFEREDWRITER
 
-#if MICROPY_MODULE_FROZEN_STR
+#if MICROPY_PY_IO_RESOURCE_STREAM
 STATIC mp_obj_t resource_stream(mp_obj_t package_in, mp_obj_t path_in) {
     VSTR_FIXED(path_buf, MICROPY_ALLOC_PATH_MAX);
     size_t len;
@@ -176,10 +176,10 @@ STATIC mp_obj_t resource_stream(mp_obj_t package_in, mp_obj_t path_in) {
         return MP_OBJ_FROM_PTR(o);
     }
 
-    mp_obj_t path_out = mp_obj_new_str(path_buf.buf, path_buf.len, false);
+    mp_obj_t path_out = mp_obj_new_str(path_buf.buf, path_buf.len);
     return mp_builtin_open(1, &path_out, (mp_map_t*)&mp_const_empty_map);
 }
-MP_DEFINE_CONST_FUN_OBJ_2(resource_stream_obj, resource_stream);
+STATIC MP_DEFINE_CONST_FUN_OBJ_2(resource_stream_obj, resource_stream);
 #endif
 
 STATIC const mp_rom_map_elem_t mp_module_io_globals_table[] = {

+ 10 - 0
py/modmicropython.c

@@ -112,6 +112,13 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_0(mp_micropython_stack_use_obj, mp_micropython_st
 
 #endif // MICROPY_PY_MICROPYTHON_MEM_INFO
 
+#if MICROPY_ENABLE_PYSTACK
+STATIC mp_obj_t mp_micropython_pystack_use(void) {
+    return MP_OBJ_NEW_SMALL_INT(mp_pystack_usage());
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_0(mp_micropython_pystack_use_obj, mp_micropython_pystack_use);
+#endif
+
 #if MICROPY_ENABLE_GC
 STATIC mp_obj_t mp_micropython_heap_lock(void) {
     gc_lock();
@@ -167,6 +174,9 @@ STATIC const mp_rom_map_elem_t mp_module_micropython_globals_table[] = {
 #if MICROPY_ENABLE_EMERGENCY_EXCEPTION_BUF && (MICROPY_EMERGENCY_EXCEPTION_BUF_SIZE == 0)
     { MP_ROM_QSTR(MP_QSTR_alloc_emergency_exception_buf), MP_ROM_PTR(&mp_alloc_emergency_exception_buf_obj) },
 #endif
+    #if MICROPY_ENABLE_PYSTACK
+    { MP_ROM_QSTR(MP_QSTR_pystack_use), MP_ROM_PTR(&mp_micropython_pystack_use_obj) },
+    #endif
     #if MICROPY_ENABLE_GC
     { MP_ROM_QSTR(MP_QSTR_heap_lock), MP_ROM_PTR(&mp_micropython_heap_lock_obj) },
     { MP_ROM_QSTR(MP_QSTR_heap_unlock), MP_ROM_PTR(&mp_micropython_heap_unlock_obj) },

+ 6 - 0
py/modthread.c

@@ -165,6 +165,12 @@ STATIC void *thread_entry(void *args_in) {
     mp_stack_set_top(&ts + 1); // need to include ts in root-pointer scan
     mp_stack_set_limit(args->stack_size);
 
+    #if MICROPY_ENABLE_PYSTACK
+    // TODO threading and pystack is not fully supported, for now just make a small stack
+    mp_obj_t mini_pystack[128];
+    mp_pystack_init(mini_pystack, &mini_pystack[128]);
+    #endif
+
     // set locals and globals from the calling context
     mp_locals_set(args->dict_locals);
     mp_globals_set(args->dict_globals);

+ 59 - 13
py/mpconfig.h

@@ -79,7 +79,7 @@
 //  - seeeeeee eeeeffff ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff 64-bit fp, e != 0x7ff
 //  - s1111111 11110000 00000000 00000000 00000000 00000000 00000000 00000000 +/- inf
 //  - 01111111 11111000 00000000 00000000 00000000 00000000 00000000 00000000 normalised nan
-//  - 01111111 11111101 00000000 00000000 iiiiiiii iiiiiiii iiiiiiii iiiiiii1 small int
+//  - 01111111 11111101 iiiiiiii iiiiiiii iiiiiiii iiiiiiii iiiiiiii iiiiiii1 small int
 //  - 01111111 11111110 00000000 00000000 qqqqqqqq qqqqqqqq qqqqqqqq qqqqqqq1 str
 //  - 01111111 11111100 00000000 00000000 pppppppp pppppppp pppppppp pppppp00 ptr (4 byte alignment)
 // Stored as O = R + 0x8004000000000000, retrieved as R = O - 0x8004000000000000.
@@ -347,7 +347,7 @@
 #endif
 
 // Whether to enable optimisation of: a, b, c = d, e, f
-// Cost 156 bytes (Thumb2)
+// Requires MICROPY_COMP_DOUBLE_TUPLE_ASSIGN and costs 68 bytes (Thumb2)
 #ifndef MICROPY_COMP_TRIPLE_TUPLE_ASSIGN
 #define MICROPY_COMP_TRIPLE_TUPLE_ASSIGN (0)
 #endif
@@ -441,6 +441,17 @@
 #define MICROPY_ENABLE_FINALISER (0)
 #endif
 
+// Whether to enable a separate allocator for the Python stack.
+// If enabled then the code must call mp_pystack_init before mp_init.
+#ifndef MICROPY_ENABLE_PYSTACK
+#define MICROPY_ENABLE_PYSTACK (0)
+#endif
+
+// Number of bytes that memory returned by mp_pystack_alloc will be aligned by.
+#ifndef MICROPY_PYSTACK_ALIGN
+#define MICROPY_PYSTACK_ALIGN (8)
+#endif
+
 // Whether to check C stack usage. C stack used for calling Python functions,
 // etc. Not checking means segfault on overflow.
 #ifndef MICROPY_STACK_CHECK
@@ -666,6 +677,13 @@ typedef double mp_float_t;
 /*****************************************************************************/
 /* Fine control over Python builtins, classes, modules, etc                  */
 
+// Whether to support multiple inheritance of Python classes.  Multiple
+// inheritance makes some C functions inherently recursive, and adds a bit of
+// code overhead.
+#ifndef MICROPY_MULTIPLE_INHERITANCE
+#define MICROPY_MULTIPLE_INHERITANCE (1)
+#endif
+
 // Whether to implement attributes on functions
 #ifndef MICROPY_PY_FUNCTION_ATTRS
 #define MICROPY_PY_FUNCTION_ATTRS (0)
@@ -688,6 +706,15 @@ typedef double mp_float_t;
 #define MICROPY_PY_ASYNC_AWAIT (1)
 #endif
 
+// Non-standard .pend_throw() method for generators, allowing for
+// Future-like behavior with respect to exception handling: an
+// exception set with .pend_throw() will activate on the next call
+// to generator's .send() or .__next__(). (This is useful to implement
+// async schedulers.)
+#ifndef MICROPY_PY_GENERATOR_PEND_THROW
+#define MICROPY_PY_GENERATOR_PEND_THROW (1)
+#endif
+
 // Issue a warning when comparing str and bytes objects
 #ifndef MICROPY_PY_STR_BYTES_CMP_WARN
 #define MICROPY_PY_STR_BYTES_CMP_WARN (0)
@@ -894,6 +921,11 @@ typedef double mp_float_t;
 #define MICROPY_PY_COLLECTIONS_ORDEREDDICT (0)
 #endif
 
+// Whether to provide the _asdict function for namedtuple
+#ifndef MICROPY_PY_COLLECTIONS_NAMEDTUPLE__ASDICT
+#define MICROPY_PY_COLLECTIONS_NAMEDTUPLE__ASDICT (0)
+#endif
+
 // Whether to provide "math" module
 #ifndef MICROPY_PY_MATH
 #define MICROPY_PY_MATH (1)
@@ -926,7 +958,11 @@ typedef double mp_float_t;
 
 // Whether to provide "uio.resource_stream()" function with
 // the semantics of CPython's pkg_resources.resource_stream()
-// (allows to access resources in frozen packages).
+// (allows to access binary resources in frozen source packages).
+// Note that the same functionality can be achieved in "pure
+// Python" by prepocessing binary resources into Python source
+// and bytecode-freezing it (with a simple helper module available
+// e.g. in micropython-lib).
 #ifndef MICROPY_PY_IO_RESOURCE_STREAM
 #define MICROPY_PY_IO_RESOURCE_STREAM (0)
 #endif
@@ -1153,16 +1189,6 @@ typedef double mp_float_t;
 #define MICROPY_PORT_ROOT_POINTERS
 #endif
 
-// Non-standard .pend_throw() method for generators, allowing for
-// Future-like behavior with respect to exception handling: an
-// exception set with .pend_throw() will activate on the next call
-// to generator's .send() or .__next__(). (This is useful to implement
-// async schedulers.)
-#ifndef MICROPY_PY_GENERATOR_PEND_THROW
-#define MICROPY_PY_GENERATOR_PEND_THROW (1)
-#endif
-
-
 /*****************************************************************************/
 /* Miscellaneous settings                                                    */
 
@@ -1303,4 +1329,24 @@ typedef double mp_float_t;
 #define MP_UNLIKELY(x) __builtin_expect((x), 0)
 #endif
 
+#ifndef MP_HTOBE16
+#if MP_ENDIANNESS_LITTLE
+# define MP_HTOBE16(x) ((uint16_t)( (((x) & 0xff) << 8) | (((x) >> 8) & 0xff) ))
+# define MP_BE16TOH(x) MP_HTOBE16(x)
+#else
+# define MP_HTOBE16(x) (x)
+# define MP_BE16TOH(x) (x)
+#endif
+#endif
+
+#ifndef MP_HTOBE32
+#if MP_ENDIANNESS_LITTLE
+# define MP_HTOBE32(x) ((uint32_t)( (((x) & 0xff) << 24) | (((x) & 0xff00) << 8) | (((x) >> 8)  & 0xff00) | (((x) >> 24) & 0xff) ))
+# define MP_BE32TOH(x) MP_HTOBE32(x)
+#else
+# define MP_HTOBE32(x) (x)
+# define MP_BE32TOH(x) (x)
+#endif
+#endif
+
 #endif // MICROPY_INCLUDED_PY_MPCONFIG_H

+ 20 - 8
py/mpprint.c

@@ -446,11 +446,16 @@ int mp_vprintf(const mp_print_t *print, const char *fmt, va_list args) {
             }
         }
 
-        // parse long specifiers (current not used)
-        //bool long_arg = false;
+        // parse long specifiers (only for LP64 model where they make a difference)
+        #ifndef __LP64__
+        const
+        #endif
+        bool long_arg = false;
         if (*fmt == 'l') {
             ++fmt;
-            //long_arg = true;
+            #ifdef __LP64__
+            long_arg = true;
+            #endif
         }
 
         if (*fmt == '\0') {
@@ -505,14 +510,21 @@ int mp_vprintf(const mp_print_t *print, const char *fmt, va_list args) {
                 chrs += mp_print_int(print, va_arg(args, int), 1, 10, 'a', flags, fill, width);
                 break;
             case 'x':
-                chrs += mp_print_int(print, va_arg(args, unsigned int), 0, 16, 'a', flags, fill, width);
-                break;
-            case 'X':
-                chrs += mp_print_int(print, va_arg(args, unsigned int), 0, 16, 'A', flags, fill, width);
+            case 'X': {
+                char fmt_c = *fmt - 'X' + 'A';
+                mp_uint_t val;
+                if (long_arg) {
+                    val = va_arg(args, unsigned long int);
+                } else {
+                    val = va_arg(args, unsigned int);
+                }
+                chrs += mp_print_int(print, val, 0, 16, fmt_c, flags, fill, width);
                 break;
+            }
             case 'p':
             case 'P': // don't bother to handle upcase for 'P'
-                chrs += mp_print_int(print, va_arg(args, unsigned int), 0, 16, 'a', flags, fill, width);
+                // Use unsigned long int to work on both ILP32 and LP64 systems
+                chrs += mp_print_int(print, va_arg(args, unsigned long int), 0, 16, 'a', flags, fill, width);
                 break;
 #if MICROPY_PY_BUILTINS_FLOAT
             case 'e':

+ 6 - 8
py/mpstate.h

@@ -176,13 +176,6 @@ typedef struct _mp_state_vm_t {
     mp_obj_t lwip_slip_stream;
     #endif
 
-    #if !MICROPY_VFS  //If not difined MICROPY_VFS,only define MICROPY_MODUOS_FILE
-    #if MICROPY_MODUOS_FILE
-    struct _mp_vfs_mount_t *vfs_cur;
-    struct _mp_vfs_mount_t *vfs_mount_table;
-    #endif
-    #endif
-
     #if MICROPY_VFS
     struct _mp_vfs_mount_t *vfs_cur;
     struct _mp_vfs_mount_t *vfs_mount_table;
@@ -222,7 +215,6 @@ typedef struct _mp_state_thread_t {
     mp_obj_dict_t *dict_locals;
     mp_obj_dict_t *dict_globals;
 
-    // Note: nlr asm code has the offset of this hard-coded
     nlr_buf_t *nlr_top; // ROOT POINTER
 
     // Stack top at the start of program
@@ -231,6 +223,12 @@ typedef struct _mp_state_thread_t {
     #if MICROPY_STACK_CHECK
     size_t stack_limit;
     #endif
+
+    #if MICROPY_ENABLE_PYSTACK
+    uint8_t *pystack_start;
+    uint8_t *pystack_end;
+    uint8_t *pystack_cur;
+    #endif
 } mp_state_thread_t;
 
 // This structure combines the above 3 structures.

+ 0 - 117
py/mpy_scheduler.c

@@ -1,117 +0,0 @@
-/*
- * This file is part of the MicroPython project, http://micropython.org/
- *
- * The MIT License (MIT)
- *
- * Copyright (c) 2017 Damien P. George
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
-
-#include <stdio.h>
-
-#include "py/runtime.h"
-
-#if MICROPY_ENABLE_SCHEDULER
-
-// A variant of this is inlined in the VM at the pending exception check
-void mp_handle_pending(void) {
-    if (MP_STATE_VM(sched_state) == MP_SCHED_PENDING) {
-        mp_uint_t atomic_state = MICROPY_BEGIN_ATOMIC_SECTION();
-        mp_obj_t obj = MP_STATE_VM(mp_pending_exception);
-        if (obj != MP_OBJ_NULL) {
-            MP_STATE_VM(mp_pending_exception) = MP_OBJ_NULL;
-            if (!mp_sched_num_pending()) {
-                MP_STATE_VM(sched_state) = MP_SCHED_IDLE;
-            }
-            MICROPY_END_ATOMIC_SECTION(atomic_state);
-            nlr_raise(obj);
-        }
-        mp_handle_pending_tail(atomic_state);
-    }
-}
-
-// This function should only be called be mp_sched_handle_pending,
-// or by the VM's inlined version of that function.
-void mp_handle_pending_tail(mp_uint_t atomic_state) {
-    MP_STATE_VM(sched_state) = MP_SCHED_LOCKED;
-    if (MP_STATE_VM(sched_sp) > 0) {
-        mp_sched_item_t item = MP_STATE_VM(sched_stack)[--MP_STATE_VM(sched_sp)];
-        MICROPY_END_ATOMIC_SECTION(atomic_state);
-        mp_call_function_1_protected(item.func, item.arg);
-    } else {
-        MICROPY_END_ATOMIC_SECTION(atomic_state);
-    }
-    mp_sched_unlock();
-}
-
-void mp_sched_lock(void) {
-    mp_uint_t atomic_state = MICROPY_BEGIN_ATOMIC_SECTION();
-    if (MP_STATE_VM(sched_state) < 0) {
-        --MP_STATE_VM(sched_state);
-    } else {
-        MP_STATE_VM(sched_state) = MP_SCHED_LOCKED;
-    }
-    MICROPY_END_ATOMIC_SECTION(atomic_state);
-}
-
-void mp_sched_unlock(void) {
-    mp_uint_t atomic_state = MICROPY_BEGIN_ATOMIC_SECTION();
-    if (++MP_STATE_VM(sched_state) == 0) {
-        // vm became unlocked
-        if (MP_STATE_VM(mp_pending_exception) != MP_OBJ_NULL || mp_sched_num_pending()) {
-            MP_STATE_VM(sched_state) = MP_SCHED_PENDING;
-        } else {
-            MP_STATE_VM(sched_state) = MP_SCHED_IDLE;
-        }
-    }
-    MICROPY_END_ATOMIC_SECTION(atomic_state);
-}
-
-bool mp_sched_schedule(mp_obj_t function, mp_obj_t arg) {
-    mp_uint_t atomic_state = MICROPY_BEGIN_ATOMIC_SECTION();
-    bool ret;
-    if (MP_STATE_VM(sched_sp) < MICROPY_SCHEDULER_DEPTH) {
-        if (MP_STATE_VM(sched_state) == MP_SCHED_IDLE) {
-            MP_STATE_VM(sched_state) = MP_SCHED_PENDING;
-        }
-        MP_STATE_VM(sched_stack)[MP_STATE_VM(sched_sp)].func = function;
-        MP_STATE_VM(sched_stack)[MP_STATE_VM(sched_sp)].arg = arg;
-        ++MP_STATE_VM(sched_sp);
-        ret = true;
-    } else {
-        // schedule stack is full
-        ret = false;
-    }
-    MICROPY_END_ATOMIC_SECTION(atomic_state);
-    return ret;
-}
-
-#else // MICROPY_ENABLE_SCHEDULER
-
-// A variant of this is inlined in the VM at the pending exception check
-void mp_handle_pending(void) {
-    if (MP_STATE_VM(mp_pending_exception) != MP_OBJ_NULL) {
-        mp_obj_t obj = MP_STATE_VM(mp_pending_exception);
-        MP_STATE_VM(mp_pending_exception) = MP_OBJ_NULL;
-        nlr_raise(obj);
-    }
-}
-
-#endif // MICROPY_ENABLE_SCHEDULER

+ 51 - 82
py/mpz.c

@@ -537,83 +537,57 @@ STATIC void mpn_div(mpz_dig_t *num_dig, size_t *num_len, const mpz_dig_t *den_di
         //      not to overflow the borrow variable.  And the shifting of
         //      borrow needs some special logic (it's a shift right with
         //      round up).
-
-        if (DIG_SIZE < 8 * sizeof(mpz_dbl_dig_t) / 2) {
-            const mpz_dig_t *d = den_dig;
-            mpz_dbl_dig_t d_norm = 0;
-            mpz_dbl_dig_signed_t borrow = 0;
-
-            for (mpz_dig_t *n = num_dig - den_len; n < num_dig; ++n, ++d) {
-                d_norm = ((mpz_dbl_dig_t)*d << norm_shift) | (d_norm >> DIG_SIZE);
-                borrow += (mpz_dbl_dig_t)*n - (mpz_dbl_dig_t)quo * (d_norm & DIG_MASK); // will overflow if DIG_SIZE >= 8*sizeof(mpz_dbl_dig_t)/2
-                *n = borrow & DIG_MASK;
-                borrow >>= DIG_SIZE;
-            }
-            borrow += *num_dig; // will overflow if DIG_SIZE >= 8*sizeof(mpz_dbl_dig_t)/2
-            *num_dig = borrow & DIG_MASK;
-            borrow >>= DIG_SIZE;
-
-            // adjust quotient if it is too big
-            for (; borrow != 0; --quo) {
-                d = den_dig;
-                d_norm = 0;
-                mpz_dbl_dig_t carry = 0;
-                for (mpz_dig_t *n = num_dig - den_len; n < num_dig; ++n, ++d) {
-                    d_norm = ((mpz_dbl_dig_t)*d << norm_shift) | (d_norm >> DIG_SIZE);
-                    carry += (mpz_dbl_dig_t)*n + (d_norm & DIG_MASK);
-                    *n = carry & DIG_MASK;
-                    carry >>= DIG_SIZE;
-                }
-                carry += *num_dig;
-                *num_dig = carry & DIG_MASK;
-                carry >>= DIG_SIZE;
-
-                borrow += carry;
-            }
-        } else { // DIG_SIZE == 8 * sizeof(mpz_dbl_dig_t) / 2
-            const mpz_dig_t *d = den_dig;
-            mpz_dbl_dig_t d_norm = 0;
-            mpz_dbl_dig_t borrow = 0;
-
-            for (mpz_dig_t *n = num_dig - den_len; n < num_dig; ++n, ++d) {
-                d_norm = ((mpz_dbl_dig_t)*d << norm_shift) | (d_norm >> DIG_SIZE);
-                mpz_dbl_dig_t x = (mpz_dbl_dig_t)quo * (d_norm & DIG_MASK);
-                if (x >= *n || *n - x <= borrow) {
-                    borrow += (mpz_dbl_dig_t)x - (mpz_dbl_dig_t)*n;
-                    *n = (-borrow) & DIG_MASK;
-                    borrow = (borrow >> DIG_SIZE) + ((borrow & DIG_MASK) == 0 ? 0 : 1); // shift-right with round-up
-                } else {
-                    *n = ((mpz_dbl_dig_t)*n - (mpz_dbl_dig_t)x - (mpz_dbl_dig_t)borrow) & DIG_MASK;
-                    borrow = 0;
-                }
-            }
-            if (borrow >= *num_dig) {
-                borrow -= (mpz_dbl_dig_t)*num_dig;
-                *num_dig = (-borrow) & DIG_MASK;
+        //
+        const mpz_dig_t *d = den_dig;
+        mpz_dbl_dig_t d_norm = 0;
+        mpz_dbl_dig_t borrow = 0;
+        for (mpz_dig_t *n = num_dig - den_len; n < num_dig; ++n, ++d) {
+            d_norm = ((mpz_dbl_dig_t)*d << norm_shift) | (d_norm >> DIG_SIZE);
+            mpz_dbl_dig_t x = (mpz_dbl_dig_t)quo * (d_norm & DIG_MASK);
+            #if DIG_SIZE < MPZ_DBL_DIG_SIZE / 2
+            borrow += (mpz_dbl_dig_t)*n - x; // will overflow if DIG_SIZE >= MPZ_DBL_DIG_SIZE/2
+            *n = borrow & DIG_MASK;
+            borrow = (mpz_dbl_dig_signed_t)borrow >> DIG_SIZE;
+            #else // DIG_SIZE == MPZ_DBL_DIG_SIZE / 2
+            if (x >= *n || *n - x <= borrow) {
+                borrow += x - (mpz_dbl_dig_t)*n;
+                *n = (-borrow) & DIG_MASK;
                 borrow = (borrow >> DIG_SIZE) + ((borrow & DIG_MASK) == 0 ? 0 : 1); // shift-right with round-up
             } else {
-                *num_dig = (*num_dig - borrow) & DIG_MASK;
+                *n = ((mpz_dbl_dig_t)*n - x - borrow) & DIG_MASK;
                 borrow = 0;
             }
+            #endif
+        }
 
-            // adjust quotient if it is too big
-            for (; borrow != 0; --quo) {
-                d = den_dig;
-                d_norm = 0;
-                mpz_dbl_dig_t carry = 0;
-                for (mpz_dig_t *n = num_dig - den_len; n < num_dig; ++n, ++d) {
-                    d_norm = ((mpz_dbl_dig_t)*d << norm_shift) | (d_norm >> DIG_SIZE);
-                    carry += (mpz_dbl_dig_t)*n + (d_norm & DIG_MASK);
-                    *n = carry & DIG_MASK;
-                    carry >>= DIG_SIZE;
-                }
-                carry += (mpz_dbl_dig_t)*num_dig;
-                *num_dig = carry & DIG_MASK;
-                carry >>= DIG_SIZE;
+        #if DIG_SIZE < MPZ_DBL_DIG_SIZE / 2
+        // Borrow was negative in the above for-loop, make it positive for next if-block.
+        borrow = -borrow;
+        #endif
 
-                //assert(borrow >= carry); // enable this to check the logic
-                borrow -= carry;
+        // At this point we have either:
+        //
+        //   1. quo was the correct value and the most-sig-digit of num is exactly
+        //      cancelled by borrow (borrow == *num_dig).  In this case there is
+        //      nothing more to do.
+        //
+        //   2. quo was too large, we subtracted too many den from num, and the
+        //      most-sig-digit of num is 1 less than borrow (borrow == *num_dig + 1).
+        //      In this case we must reduce quo and add back den to num until the
+        //      carry from this operation cancels out the borrow.
+        //
+        borrow -= *num_dig;
+        for (; borrow != 0; --quo) {
+            d = den_dig;
+            d_norm = 0;
+            mpz_dbl_dig_t carry = 0;
+            for (mpz_dig_t *n = num_dig - den_len; n < num_dig; ++n, ++d) {
+                d_norm = ((mpz_dbl_dig_t)*d << norm_shift) | (d_norm >> DIG_SIZE);
+                carry += (mpz_dbl_dig_t)*n + (d_norm & DIG_MASK);
+                *n = carry & DIG_MASK;
+                carry >>= DIG_SIZE;
             }
+            borrow -= carry;
         }
 
         // store this digit of the quotient
@@ -1390,13 +1364,14 @@ void mpz_pow_inpl(mpz_t *dest, const mpz_t *lhs, const mpz_t *rhs) {
    can have dest, lhs, rhs the same; mod can't be the same as dest
 */
 void mpz_pow3_inpl(mpz_t *dest, const mpz_t *lhs, const mpz_t *rhs, const mpz_t *mod) {
-    if (lhs->len == 0 || rhs->neg != 0) {
+    if (lhs->len == 0 || rhs->neg != 0 || (mod->len == 1 && mod->dig[0] == 1)) {
         mpz_set_from_int(dest, 0);
         return;
     }
 
+    mpz_set_from_int(dest, 1);
+
     if (rhs->len == 0) {
-        mpz_set_from_int(dest, 1);
         return;
     }
 
@@ -1404,8 +1379,6 @@ void mpz_pow3_inpl(mpz_t *dest, const mpz_t *lhs, const mpz_t *rhs, const mpz_t
     mpz_t *n = mpz_clone(rhs);
     mpz_t quo; mpz_init_zero(&quo);
 
-    mpz_set_from_int(dest, 1);
-
     while (n->len > 0) {
         if ((n->dig[0] & 1) != 0) {
             mpz_mul_inpl(dest, dest, x);
@@ -1674,16 +1647,12 @@ char *mpz_as_str(const mpz_t *i, unsigned int base) {
 }
 #endif
 
-// assumes enough space as calculated by mp_int_format_size
+// assumes enough space in str as calculated by mp_int_format_size
+// base must be between 2 and 32 inclusive
 // returns length of string, not including null byte
 size_t mpz_as_str_inpl(const mpz_t *i, unsigned int base, const char *prefix, char base_char, char comma, char *str) {
-    if (str == NULL) {
-        return 0;
-    }
-    if (base < 2 || base > 32) {
-        str[0] = 0;
-        return 0;
-    }
+    assert(str != NULL);
+    assert(2 <= base && base <= 32);
 
     size_t ilen = i->len;
 

+ 4 - 0
py/mpz.h

@@ -55,18 +55,22 @@
 #endif
 
 #if MPZ_DIG_SIZE > 16
+#define MPZ_DBL_DIG_SIZE (64)
 typedef uint32_t mpz_dig_t;
 typedef uint64_t mpz_dbl_dig_t;
 typedef int64_t mpz_dbl_dig_signed_t;
 #elif MPZ_DIG_SIZE > 8
+#define MPZ_DBL_DIG_SIZE (32)
 typedef uint16_t mpz_dig_t;
 typedef uint32_t mpz_dbl_dig_t;
 typedef int32_t mpz_dbl_dig_signed_t;
 #elif MPZ_DIG_SIZE > 4
+#define MPZ_DBL_DIG_SIZE (16)
 typedef uint8_t mpz_dig_t;
 typedef uint16_t mpz_dbl_dig_t;
 typedef int16_t mpz_dbl_dig_signed_t;
 #else
+#define MPZ_DBL_DIG_SIZE (8)
 typedef uint8_t mpz_dig_t;
 typedef uint8_t mpz_dbl_dig_t;
 typedef int8_t mpz_dbl_dig_signed_t;

+ 62 - 24
py/nlr.h

@@ -30,29 +30,34 @@
 // exception handling, basically a stack of setjmp/longjmp buffers
 
 #include <limits.h>
-#include <setjmp.h>
 #include <assert.h>
 
 #include "py/mpconfig.h"
 
-typedef struct _nlr_buf_t nlr_buf_t;
-struct _nlr_buf_t {
-    // the entries here must all be machine word size
-    nlr_buf_t *prev;
-    void *ret_val; // always a concrete object (an exception instance)
-#if !defined(MICROPY_NLR_SETJMP) || !MICROPY_NLR_SETJMP
+// If MICROPY_NLR_SETJMP is not enabled then auto-detect the machine arch
+#if !MICROPY_NLR_SETJMP
+// A lot of nlr-related things need different treatment on Windows
+#if defined(_WIN32) || defined(__CYGWIN__)
+#define MICROPY_NLR_OS_WINDOWS 1
+#else
+#define MICROPY_NLR_OS_WINDOWS 0
+#endif
 #if defined(__i386__)
-    void *regs[6];
+    #define MICROPY_NLR_X86 (1)
+    #define MICROPY_NLR_NUM_REGS (6)
 #elif defined(__x86_64__)
-  #if defined(__CYGWIN__)
-    void *regs[12];
-  #else
-    void *regs[8];
-  #endif
+    #define MICROPY_NLR_X64 (1)
+    #if MICROPY_NLR_OS_WINDOWS
+        #define MICROPY_NLR_NUM_REGS (10)
+    #else
+        #define MICROPY_NLR_NUM_REGS (8)
+    #endif
 #elif defined(__thumb2__) || defined(__thumb__) || defined(__arm__)
-    void *regs[10];
+    #define MICROPY_NLR_THUMB (1)
+    #define MICROPY_NLR_NUM_REGS (10)
 #elif defined(__xtensa__)
-    void *regs[10];
+    #define MICROPY_NLR_XTENSA (1)
+    #define MICROPY_NLR_NUM_REGS (10)
 #else
     #define MICROPY_NLR_SETJMP (1)
     //#warning "No native NLR support for this arch, using setjmp implementation"
@@ -60,24 +65,58 @@ struct _nlr_buf_t {
 #endif
 
 #if MICROPY_NLR_SETJMP
-    jmp_buf jmpbuf;
+#include <setjmp.h>
 #endif
+
+typedef struct _nlr_buf_t nlr_buf_t;
+struct _nlr_buf_t {
+    // the entries here must all be machine word size
+    nlr_buf_t *prev;
+    void *ret_val; // always a concrete object (an exception instance)
+
+    #if MICROPY_NLR_SETJMP
+    jmp_buf jmpbuf;
+    #else
+    void *regs[MICROPY_NLR_NUM_REGS];
+    #endif
+
+    #if MICROPY_ENABLE_PYSTACK
+    void *pystack;
+    #endif
 };
 
-#if MICROPY_NLR_SETJMP
-#include "py/mpstate.h"
+// Helper macros to save/restore the pystack state
+#if MICROPY_ENABLE_PYSTACK
+#define MP_NLR_SAVE_PYSTACK(nlr_buf) (nlr_buf)->pystack = MP_STATE_THREAD(pystack_cur)
+#define MP_NLR_RESTORE_PYSTACK(nlr_buf) MP_STATE_THREAD(pystack_cur) = (nlr_buf)->pystack
+#else
+#define MP_NLR_SAVE_PYSTACK(nlr_buf) (void)nlr_buf
+#define MP_NLR_RESTORE_PYSTACK(nlr_buf) (void)nlr_buf
+#endif
+
+// Helper macro to use at the start of a specific nlr_jump implementation
+#define MP_NLR_JUMP_HEAD(val, top) \
+    nlr_buf_t **_top_ptr = &MP_STATE_THREAD(nlr_top); \
+    nlr_buf_t *top = *_top_ptr; \
+    if (top == NULL) { \
+        nlr_jump_fail(val); \
+    } \
+    top->ret_val = val; \
+    MP_NLR_RESTORE_PYSTACK(top); \
+    *_top_ptr = top->prev; \
 
-NORETURN void nlr_setjmp_jump(void *val);
+#if MICROPY_NLR_SETJMP
 // nlr_push() must be defined as a macro, because "The stack context will be
 // invalidated if the function which called setjmp() returns."
-#define nlr_push(buf) ((buf)->prev = MP_STATE_THREAD(nlr_top), MP_STATE_THREAD(nlr_top) = (buf), setjmp((buf)->jmpbuf))
-#define nlr_pop() { MP_STATE_THREAD(nlr_top) = MP_STATE_THREAD(nlr_top)->prev; }
-#define nlr_jump(val) nlr_setjmp_jump(val)
+// For this case it is safe to call nlr_push_tail() first.
+#define nlr_push(buf) (nlr_push_tail(buf), setjmp((buf)->jmpbuf))
 #else
 unsigned int nlr_push(nlr_buf_t *);
+#endif
+
+unsigned int nlr_push_tail(nlr_buf_t *top);
 void nlr_pop(void);
 NORETURN void nlr_jump(void *val);
-#endif
 
 // This must be implemented by a port.  It's called by nlr_jump
 // if no nlr buf has been pushed.  It must not return, but rather
@@ -106,7 +145,6 @@ NORETURN void nlr_jump_fail(void *val);
 /*
 #define nlr_push(val) \
     printf("nlr_push: before: nlr_top=%p, val=%p\n", MP_STATE_THREAD(nlr_top), val),assert(MP_STATE_THREAD(nlr_top) != val),nlr_push(val)
-#endif
 */
 #endif
 

+ 4 - 3
py/nlrsetjmp.c

@@ -3,7 +3,7 @@
  *
  * The MIT License (MIT)
  *
- * Copyright (c) 2013, 2014 Damien P. George
+ * Copyright (c) 2013-2017 Damien P. George
  *
  * Permission is hereby granted, free of charge, to any person obtaining a copy
  * of this software and associated documentation files (the "Software"), to deal
@@ -24,17 +24,18 @@
  * THE SOFTWARE.
  */
 
-#include "py/nlr.h"
+#include "py/mpstate.h"
 
 #if MICROPY_NLR_SETJMP
 
-void nlr_setjmp_jump(void *val) {
+void nlr_jump(void *val) {
     nlr_buf_t **top_ptr = &MP_STATE_THREAD(nlr_top);
     nlr_buf_t *top = *top_ptr;
     if (top == NULL) {
         nlr_jump_fail(val);
     }
     top->ret_val = val;
+    MP_NLR_RESTORE_PYSTACK(top);
     *top_ptr = top->prev;
     longjmp(top->jmpbuf, 1);
 }

+ 13 - 25
py/nlrthumb.c

@@ -3,7 +3,7 @@
  *
  * The MIT License (MIT)
  *
- * Copyright (c) 2013-2016 Damien P. George
+ * Copyright (c) 2013-2017 Damien P. George
  *
  * Permission is hereby granted, free of charge, to any person obtaining a copy
  * of this software and associated documentation files (the "Software"), to deal
@@ -26,7 +26,7 @@
 
 #include "py/mpstate.h"
 
-#if (!defined(MICROPY_NLR_SETJMP) || !MICROPY_NLR_SETJMP) && (defined(__thumb2__) || defined(__thumb__) || defined(__arm__))
+#if MICROPY_NLR_THUMB
 
 #undef nlr_push
 
@@ -76,30 +76,14 @@ __attribute__((naked)) unsigned int nlr_push(nlr_buf_t *nlr) {
 #endif
     );
 
-    return 0; // needed to silence compiler warning
+    #if defined(__GNUC__)
+    // Older versions of gcc give an error when naked functions don't return a value
+    __builtin_unreachable();
+    #endif
 }
 
-__attribute__((used)) unsigned int nlr_push_tail(nlr_buf_t *nlr) {
-    nlr_buf_t **top = &MP_STATE_THREAD(nlr_top);
-    nlr->prev = *top;
-    *top = nlr;
-    return 0; // normal return
-}
-
-void nlr_pop(void) {
-    nlr_buf_t **top = &MP_STATE_THREAD(nlr_top);
-    *top = (*top)->prev;
-}
-
-NORETURN __attribute__((naked)) void nlr_jump(void *val) {
-    nlr_buf_t **top_ptr = &MP_STATE_THREAD(nlr_top);
-    nlr_buf_t *top = *top_ptr;
-    if (top == NULL) {
-        nlr_jump_fail(val);
-    }
-
-    top->ret_val = val;
-    *top_ptr = top->prev;
+NORETURN void nlr_jump(void *val) {
+    MP_NLR_JUMP_HEAD(val, top)
 
     __asm volatile (
     "mov    r0, %0              \n" // r0 points to nlr_buf
@@ -136,7 +120,11 @@ NORETURN __attribute__((naked)) void nlr_jump(void *val) {
     :                               // clobbered registers
     );
 
+    #if defined(__GNUC__)
+    __builtin_unreachable();
+    #else
     for (;;); // needed to silence compiler warning
+    #endif
 }
 
-#endif // (!defined(MICROPY_NLR_SETJMP) || !MICROPY_NLR_SETJMP) && (defined(__thumb2__) || defined(__thumb__) || defined(__arm__))
+#endif // MICROPY_NLR_THUMB

+ 5 - 30
py/nlrx64.c

@@ -26,25 +26,19 @@
 
 #include "py/mpstate.h"
 
-#if !MICROPY_NLR_SETJMP && defined(__x86_64__)
+#if MICROPY_NLR_X64
 
 #undef nlr_push
 
 // x86-64 callee-save registers are:
 //  rbx, rbp, rsp, r12, r13, r14, r15
 
-#if defined(_WIN32) || defined(__CYGWIN__)
-#define NLR_OS_WINDOWS 1
-#else
-#define NLR_OS_WINDOWS 0
-#endif
-
 __attribute__((used)) unsigned int nlr_push_tail(nlr_buf_t *nlr);
 
 unsigned int nlr_push(nlr_buf_t *nlr) {
     (void)nlr;
 
-    #if NLR_OS_WINDOWS
+    #if MICROPY_NLR_OS_WINDOWS
 
     __asm volatile (
     "movq   (%rsp), %rax        \n" // load return %rip
@@ -88,31 +82,12 @@ unsigned int nlr_push(nlr_buf_t *nlr) {
     return 0; // needed to silence compiler warning
 }
 
-__attribute__((used)) unsigned int nlr_push_tail(nlr_buf_t *nlr) {
-    nlr_buf_t **top = &MP_STATE_THREAD(nlr_top);
-    nlr->prev = *top;
-    *top = nlr;
-    return 0; // normal return
-}
-
-void nlr_pop(void) {
-    nlr_buf_t **top = &MP_STATE_THREAD(nlr_top);
-    *top = (*top)->prev;
-}
-
 NORETURN void nlr_jump(void *val) {
-    nlr_buf_t **top_ptr = &MP_STATE_THREAD(nlr_top);
-    nlr_buf_t *top = *top_ptr;
-    if (top == NULL) {
-        nlr_jump_fail(val);
-    }
-
-    top->ret_val = val;
-    *top_ptr = top->prev;
+    MP_NLR_JUMP_HEAD(val, top)
 
     __asm volatile (
     "movq   %0, %%rcx           \n" // %rcx points to nlr_buf
-    #if NLR_OS_WINDOWS
+    #if MICROPY_NLR_OS_WINDOWS
     "movq   88(%%rcx), %%rsi    \n" // load saved %rsi
     "movq   80(%%rcx), %%rdi    \n" // load saved %rdr
     #endif
@@ -136,4 +111,4 @@ NORETURN void nlr_jump(void *val) {
     for (;;); // needed to silence compiler warning
 }
 
-#endif // !MICROPY_NLR_SETJMP && defined(__x86_64__)
+#endif // MICROPY_NLR_X64

+ 4 - 29
py/nlrx86.c

@@ -26,20 +26,14 @@
 
 #include "py/mpstate.h"
 
-#if !MICROPY_NLR_SETJMP && defined(__i386__)
+#if MICROPY_NLR_X86
 
 #undef nlr_push
 
 // For reference, x86 callee save regs are:
 //  ebx, esi, edi, ebp, esp, eip
 
-#if defined(_WIN32) || defined(__CYGWIN__)
-#define NLR_OS_WINDOWS 1
-#else
-#define NLR_OS_WINDOWS 0
-#endif
-
-#if NLR_OS_WINDOWS
+#if MICROPY_NLR_OS_WINDOWS
 unsigned int nlr_push_tail(nlr_buf_t *nlr) asm("nlr_push_tail");
 #else
 __attribute__((used)) unsigned int nlr_push_tail(nlr_buf_t *nlr);
@@ -70,27 +64,8 @@ unsigned int nlr_push(nlr_buf_t *nlr) {
     return 0; // needed to silence compiler warning
 }
 
-__attribute__((used)) unsigned int nlr_push_tail(nlr_buf_t *nlr) {
-    nlr_buf_t **top = &MP_STATE_THREAD(nlr_top);
-    nlr->prev = *top;
-    *top = nlr;
-    return 0; // normal return
-}
-
-void nlr_pop(void) {
-    nlr_buf_t **top = &MP_STATE_THREAD(nlr_top);
-    *top = (*top)->prev;
-}
-
 NORETURN void nlr_jump(void *val) {
-    nlr_buf_t **top_ptr = &MP_STATE_THREAD(nlr_top);
-    nlr_buf_t *top = *top_ptr;
-    if (top == NULL) {
-        nlr_jump_fail(val);
-    }
-
-    top->ret_val = val;
-    *top_ptr = top->prev;
+    MP_NLR_JUMP_HEAD(val, top)
 
     __asm volatile (
     "mov    %0, %%edx           \n" // %edx points to nlr_buf
@@ -112,4 +87,4 @@ NORETURN void nlr_jump(void *val) {
     for (;;); // needed to silence compiler warning
 }
 
-#endif // !MICROPY_NLR_SETJMP && defined(__i386__)
+#endif // MICROPY_NLR_X86

+ 3 - 22
py/nlrxtensa.c

@@ -26,7 +26,7 @@
 
 #include "py/mpstate.h"
 
-#if !MICROPY_NLR_SETJMP && defined(__xtensa__)
+#if MICROPY_NLR_XTENSA
 
 #undef nlr_push
 
@@ -55,27 +55,8 @@ unsigned int nlr_push(nlr_buf_t *nlr) {
     return 0; // needed to silence compiler warning
 }
 
-__attribute__((used)) unsigned int nlr_push_tail(nlr_buf_t *nlr) {
-    nlr_buf_t **top = &MP_STATE_THREAD(nlr_top);
-    nlr->prev = *top;
-    *top = nlr;
-    return 0; // normal return
-}
-
-void nlr_pop(void) {
-    nlr_buf_t **top = &MP_STATE_THREAD(nlr_top);
-    *top = (*top)->prev;
-}
-
 NORETURN void nlr_jump(void *val) {
-    nlr_buf_t **top_ptr = &MP_STATE_THREAD(nlr_top);
-    nlr_buf_t *top = *top_ptr;
-    if (top == NULL) {
-        nlr_jump_fail(val);
-    }
-
-    top->ret_val = val;
-    *top_ptr = top->prev;
+    MP_NLR_JUMP_HEAD(val, top)
 
     __asm volatile (
     "mov.n   a2, %0             \n" // a2 points to nlr_buf
@@ -99,4 +80,4 @@ NORETURN void nlr_jump(void *val) {
     for (;;); // needed to silence compiler warning
 }
 
-#endif // !MICROPY_NLR_SETJMP && defined(__xtensa__)
+#endif // MICROPY_NLR_XTENSA

+ 4 - 3
py/obj.h

@@ -170,8 +170,8 @@ static inline bool MP_OBJ_IS_OBJ(mp_const_obj_t o)
 
 static inline bool MP_OBJ_IS_SMALL_INT(mp_const_obj_t o)
     { return ((((mp_int_t)(o)) & 0xffff000000000000) == 0x0001000000000000); }
-#define MP_OBJ_SMALL_INT_VALUE(o) (((intptr_t)(o)) >> 1)
-#define MP_OBJ_NEW_SMALL_INT(small_int) ((mp_obj_t)(((uintptr_t)(small_int)) << 1) | 0x0001000000000001)
+#define MP_OBJ_SMALL_INT_VALUE(o) (((mp_int_t)((o) << 16)) >> 17)
+#define MP_OBJ_NEW_SMALL_INT(small_int) (((((uint64_t)(small_int)) & 0x7fffffffffff) << 1) | 0x0001000000000001)
 
 static inline bool MP_OBJ_IS_QSTR(mp_const_obj_t o)
     { return ((((mp_int_t)(o)) & 0xffff000000000000) == 0x0002000000000000); }
@@ -637,7 +637,8 @@ mp_obj_t mp_obj_new_int_from_uint(mp_uint_t value);
 mp_obj_t mp_obj_new_int_from_str_len(const char **str, size_t len, bool neg, unsigned int base);
 mp_obj_t mp_obj_new_int_from_ll(long long val); // this must return a multi-precision integer object (or raise an overflow exception)
 mp_obj_t mp_obj_new_int_from_ull(unsigned long long val); // this must return a multi-precision integer object (or raise an overflow exception)
-mp_obj_t mp_obj_new_str(const char* data, size_t len, bool make_qstr_if_not_already);
+mp_obj_t mp_obj_new_str(const char* data, size_t len);
+mp_obj_t mp_obj_new_str_via_qstr(const char* data, size_t len);
 mp_obj_t mp_obj_new_str_from_vstr(const mp_obj_type_t *type, vstr_t *vstr);
 mp_obj_t mp_obj_new_bytes(const byte* data, size_t len);
 mp_obj_t mp_obj_new_bytearray(size_t n, void *items);

+ 1 - 2
py/objarray.c

@@ -269,8 +269,7 @@ STATIC mp_obj_t array_binary_op(mp_binary_op_t op, mp_obj_t lhs_in, mp_obj_t rhs
             return lhs_in;
         }
 
-        case MP_BINARY_OP_IN: {
-            /* NOTE `a in b` is `b.__contains__(a)` */
+        case MP_BINARY_OP_CONTAINS: {
             mp_buffer_info_t lhs_bufinfo;
             mp_buffer_info_t rhs_bufinfo;
 

+ 8 - 0
py/objboundmeth.c

@@ -51,6 +51,9 @@ mp_obj_t mp_call_method_self_n_kw(mp_obj_t meth, mp_obj_t self, size_t n_args, s
     // need to insert self before all other args and then call meth
     size_t n_total = n_args + 2 * n_kw;
     mp_obj_t *args2 = NULL;
+    #if MICROPY_ENABLE_PYSTACK
+    args2 = mp_pystack_alloc(sizeof(mp_obj_t) * (1 + n_total));
+    #else
     mp_obj_t *free_args2 = NULL;
     if (n_total > 4) {
         // try to use heap to allocate temporary args array
@@ -61,12 +64,17 @@ mp_obj_t mp_call_method_self_n_kw(mp_obj_t meth, mp_obj_t self, size_t n_args, s
         // (fallback to) use stack to allocate temporary args array
         args2 = alloca(sizeof(mp_obj_t) * (1 + n_total));
     }
+    #endif
     args2[0] = self;
     memcpy(args2 + 1, args, n_total * sizeof(mp_obj_t));
     mp_obj_t res = mp_call_function_n_kw(meth, n_args + 1, n_kw, args2);
+    #if MICROPY_ENABLE_PYSTACK
+    mp_pystack_free(args2);
+    #else
     if (free_args2 != NULL) {
         m_del(mp_obj_t, free_args2, 1 + n_total);
     }
+    #endif
     return res;
 }
 

+ 16 - 33
py/objdict.c

@@ -115,7 +115,7 @@ STATIC mp_obj_t dict_unary_op(mp_unary_op_t op, mp_obj_t self_in) {
 STATIC mp_obj_t dict_binary_op(mp_binary_op_t op, mp_obj_t lhs_in, mp_obj_t rhs_in) {
     mp_obj_dict_t *o = MP_OBJ_TO_PTR(lhs_in);
     switch (op) {
-        case MP_BINARY_OP_IN: {
+        case MP_BINARY_OP_CONTAINS: {
             mp_map_elem_t *elem = mp_map_lookup(&o->map, rhs_in, MP_MAP_LOOKUP);
             return mp_obj_new_bool(elem != NULL);
         }
@@ -192,37 +192,6 @@ STATIC mp_obj_t dict_subscr(mp_obj_t self_in, mp_obj_t index, mp_obj_t value) {
     }
 }
 
-/******************************************************************************/
-/* dict iterator                                                              */
-
-typedef struct _mp_obj_dict_it_t {
-    mp_obj_base_t base;
-    mp_fun_1_t iternext;
-    mp_obj_t dict;
-    size_t cur;
-} mp_obj_dict_it_t;
-
-STATIC mp_obj_t dict_it_iternext(mp_obj_t self_in) {
-    mp_obj_dict_it_t *self = MP_OBJ_TO_PTR(self_in);
-    mp_map_elem_t *next = dict_iter_next(MP_OBJ_TO_PTR(self->dict), &self->cur);
-
-    if (next == NULL) {
-        return MP_OBJ_STOP_ITERATION;
-    } else {
-        return next->key;
-    }
-}
-
-STATIC mp_obj_t dict_getiter(mp_obj_t self_in, mp_obj_iter_buf_t *iter_buf) {
-    assert(sizeof(mp_obj_dict_it_t) <= sizeof(mp_obj_iter_buf_t));
-    mp_obj_dict_it_t *o = (mp_obj_dict_it_t*)iter_buf;
-    o->base.type = &mp_type_polymorph_iter;
-    o->iternext = dict_it_iternext;
-    o->dict = self_in;
-    o->cur = 0;
-    return MP_OBJ_FROM_PTR(o);
-}
-
 /******************************************************************************/
 /* dict methods                                                               */
 
@@ -485,7 +454,7 @@ STATIC mp_obj_t dict_view_binary_op(mp_binary_op_t op, mp_obj_t lhs_in, mp_obj_t
     if (o->kind != MP_DICT_VIEW_KEYS) {
         return MP_OBJ_NULL; // op not supported
     }
-    if (op != MP_BINARY_OP_IN) {
+    if (op != MP_BINARY_OP_CONTAINS) {
         return MP_OBJ_NULL; // op not supported
     }
     return dict_binary_op(op, o->dict, rhs_in);
@@ -527,6 +496,20 @@ STATIC mp_obj_t dict_values(mp_obj_t self_in) {
 }
 STATIC MP_DEFINE_CONST_FUN_OBJ_1(dict_values_obj, dict_values);
 
+/******************************************************************************/
+/* dict iterator                                                              */
+
+STATIC mp_obj_t dict_getiter(mp_obj_t self_in, mp_obj_iter_buf_t *iter_buf) {
+    assert(sizeof(mp_obj_dict_view_it_t) <= sizeof(mp_obj_iter_buf_t));
+    mp_check_self(MP_OBJ_IS_DICT_TYPE(self_in));
+    mp_obj_dict_view_it_t *o = (mp_obj_dict_view_it_t*)iter_buf;
+    o->base.type = &dict_view_it_type;
+    o->kind = MP_DICT_VIEW_KEYS;
+    o->dict = self_in;
+    o->cur = 0;
+    return MP_OBJ_FROM_PTR(o);
+}
+
 /******************************************************************************/
 /* dict constructors & public C API                                           */
 

+ 1 - 16
py/objexcept.c

@@ -118,7 +118,7 @@ STATIC void mp_obj_exception_print(const mp_print_t *print, mp_obj_t o_in, mp_pr
             if (o->base.type == &mp_type_OSError && MP_OBJ_IS_SMALL_INT(o->args->items[0])) {
                 qstr qst = mp_errno_to_str(o->args->items[0]);
                 if (qst != MP_QSTR_NULL) {
-                    mp_printf(print, "[Errno %d] %q", MP_OBJ_SMALL_INT_VALUE(o->args->items[0]), qst);
+                    mp_printf(print, "[Errno " INT_FMT "] %q", MP_OBJ_SMALL_INT_VALUE(o->args->items[0]), qst);
                     return;
                 }
             }
@@ -212,27 +212,12 @@ STATIC void exception_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest) {
     }
 }
 
-STATIC mp_obj_t exc___init__(size_t n_args, const mp_obj_t *args) {
-    mp_obj_exception_t *self = MP_OBJ_TO_PTR(args[0]);
-    mp_obj_t argst = mp_obj_new_tuple(n_args - 1, args + 1);
-    self->args = MP_OBJ_TO_PTR(argst);
-    return mp_const_none;
-}
-STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(exc___init___obj, 1, MP_OBJ_FUN_ARGS_MAX, exc___init__);
-
-STATIC const mp_rom_map_elem_t exc_locals_dict_table[] = {
-    { MP_ROM_QSTR(MP_QSTR___init__), MP_ROM_PTR(&exc___init___obj) },
-};
-
-STATIC MP_DEFINE_CONST_DICT(exc_locals_dict, exc_locals_dict_table);
-
 const mp_obj_type_t mp_type_BaseException = {
     { &mp_type_type },
     .name = MP_QSTR_BaseException,
     .print = mp_obj_exception_print,
     .make_new = mp_obj_exception_make_new,
     .attr = exception_attr,
-    .locals_dict = (mp_obj_dict_t*)&exc_locals_dict,
 };
 
 #define MP_DEFINE_EXCEPTION(exc_name, base_name) \

+ 6 - 6
py/objfloat.c

@@ -137,12 +137,11 @@ STATIC mp_obj_t float_make_new(const mp_obj_type_t *type_in, size_t n_args, size
             return mp_obj_new_float(0);
 
         case 1:
-        default:
-            if (MP_OBJ_IS_STR(args[0])) {
-                // a string, parse it
-                size_t l;
-                const char *s = mp_obj_str_get_data(args[0], &l);
-                return mp_parse_num_decimal(s, l, false, false, NULL);
+        default: {
+            mp_buffer_info_t bufinfo;
+            if (mp_get_buffer(args[0], &bufinfo, MP_BUFFER_READ)) {
+                // a textual representation, parse it
+                return mp_parse_num_decimal(bufinfo.buf, bufinfo.len, false, false, NULL);
             } else if (mp_obj_is_float(args[0])) {
                 // a float, just return it
                 return args[0];
@@ -150,6 +149,7 @@ STATIC mp_obj_t float_make_new(const mp_obj_type_t *type_in, size_t n_args, size
                 // something else, try to cast it to a float
                 return mp_obj_new_float(mp_obj_get_float(args[0]));
             }
+        }
     }
 }
 

+ 43 - 23
py/objfun.c

@@ -195,32 +195,53 @@ STATIC void dump_args(const mp_obj_t *a, size_t sz) {
 // than this will try to use the heap, with fallback to stack allocation.
 #define VM_MAX_STATE_ON_STACK (11 * sizeof(mp_uint_t))
 
-// Set this to enable a simple stack overflow check.
+// Set this to 1 to enable a simple stack overflow check.
 #define VM_DETECT_STACK_OVERFLOW (0)
 
+#define DECODE_CODESTATE_SIZE(bytecode, n_state_out_var, state_size_out_var) \
+    { \
+        /* bytecode prelude: state size and exception stack size */               \
+        n_state_out_var = mp_decode_uint_value(bytecode);                         \
+        size_t n_exc_stack = mp_decode_uint_value(mp_decode_uint_skip(bytecode)); \
+                                                                                  \
+        n_state += VM_DETECT_STACK_OVERFLOW;                                      \
+                                                                                  \
+        /* state size in bytes */                                                 \
+        state_size_out_var = n_state * sizeof(mp_obj_t) + n_exc_stack * sizeof(mp_exc_stack_t); \
+    }
+
+#define INIT_CODESTATE(code_state, _fun_bc, n_args, n_kw, args) \
+    code_state->fun_bc = _fun_bc; \
+    code_state->ip = 0; \
+    mp_setup_code_state(code_state, n_args, n_kw, args); \
+    code_state->old_globals = mp_globals_get();
+
 #if MICROPY_STACKLESS
 mp_code_state_t *mp_obj_fun_bc_prepare_codestate(mp_obj_t self_in, size_t n_args, size_t n_kw, const mp_obj_t *args) {
     MP_STACK_CHECK();
     mp_obj_fun_bc_t *self = MP_OBJ_TO_PTR(self_in);
 
-    // bytecode prelude: state size and exception stack size
-    size_t n_state = mp_decode_uint_value(self->bytecode);
-    size_t n_exc_stack = mp_decode_uint_value(mp_decode_uint_skip(self->bytecode));
+    size_t n_state, state_size;
+    DECODE_CODESTATE_SIZE(self->bytecode, n_state, state_size);
 
-    // allocate state for locals and stack
-    size_t state_size = n_state * sizeof(mp_obj_t) + n_exc_stack * sizeof(mp_exc_stack_t);
     mp_code_state_t *code_state;
+    #if MICROPY_ENABLE_PYSTACK
+    code_state = mp_pystack_alloc(sizeof(mp_code_state_t) + state_size);
+    #else
+    // If we use m_new_obj_var(), then on no memory, MemoryError will be
+    // raised. But this is not correct exception for a function call,
+    // RuntimeError should be raised instead. So, we use m_new_obj_var_maybe(),
+    // return NULL, then vm.c takes the needed action (either raise
+    // RuntimeError or fallback to stack allocation).
     code_state = m_new_obj_var_maybe(mp_code_state_t, byte, state_size);
     if (!code_state) {
         return NULL;
     }
+    #endif
 
-    code_state->fun_bc = self;
-    code_state->ip = 0;
-    mp_setup_code_state(code_state, n_args, n_kw, args);
+    INIT_CODESTATE(code_state, self, n_args, n_kw, args);
 
     // execute the byte code with the correct globals context
-    code_state->old_globals = mp_globals_get();
     mp_globals_set(self->globals);
 
     return code_state;
@@ -236,19 +257,16 @@ STATIC mp_obj_t fun_bc_call(mp_obj_t self_in, size_t n_args, size_t n_kw, const
     DEBUG_printf("Input kw args: ");
     dump_args(args + n_args, n_kw * 2);
     mp_obj_fun_bc_t *self = MP_OBJ_TO_PTR(self_in);
-    //DEBUG_printf("Func n_def_args: %d\n", self->n_def_args);
+    DEBUG_printf("Func n_def_args: %d\n", self->n_def_args);
 
-    // bytecode prelude: state size and exception stack size
-    size_t n_state = mp_decode_uint_value(self->bytecode);
-    size_t n_exc_stack = mp_decode_uint_value(mp_decode_uint_skip(self->bytecode));
-
-#if VM_DETECT_STACK_OVERFLOW
-    n_state += 1;
-#endif
+    size_t n_state, state_size;
+    DECODE_CODESTATE_SIZE(self->bytecode, n_state, state_size);
 
     // allocate state for locals and stack
-    size_t state_size = n_state * sizeof(mp_obj_t) + n_exc_stack * sizeof(mp_exc_stack_t);
     mp_code_state_t *code_state = NULL;
+    #if MICROPY_ENABLE_PYSTACK
+    code_state = mp_pystack_alloc(sizeof(mp_code_state_t) + state_size);
+    #else
     if (state_size > VM_MAX_STATE_ON_STACK) {
         code_state = m_new_obj_var_maybe(mp_code_state_t, byte, state_size);
     }
@@ -256,13 +274,11 @@ STATIC mp_obj_t fun_bc_call(mp_obj_t self_in, size_t n_args, size_t n_kw, const
         code_state = alloca(sizeof(mp_code_state_t) + state_size);
         state_size = 0; // indicate that we allocated using alloca
     }
+    #endif
 
-    code_state->fun_bc = self;
-    code_state->ip = 0;
-    mp_setup_code_state(code_state, n_args, n_kw, args);
+    INIT_CODESTATE(code_state, self, n_args, n_kw, args);
 
     // execute the byte code with the correct globals context
-    code_state->old_globals = mp_globals_get();
     mp_globals_set(self->globals);
     mp_vm_return_kind_t vm_return_kind = mp_execute_bytecode(code_state, MP_OBJ_NULL);
     mp_globals_set(code_state->old_globals);
@@ -304,10 +320,14 @@ STATIC mp_obj_t fun_bc_call(mp_obj_t self_in, size_t n_args, size_t n_kw, const
         result = code_state->state[n_state - 1];
     }
 
+    #if MICROPY_ENABLE_PYSTACK
+    mp_pystack_free(code_state);
+    #else
     // free the state if it was allocated on the heap
     if (state_size != 0) {
         m_del_var(mp_code_state_t, byte, state_size, code_state);
     }
+    #endif
 
     if (vm_return_kind == MP_VM_RETURN_NORMAL) {
         return result;

+ 9 - 0
py/objint_longlong.c

@@ -151,9 +151,15 @@ mp_obj_t mp_obj_int_binary_op(mp_binary_op_t op, mp_obj_t lhs_in, mp_obj_t rhs_i
             return mp_obj_new_int_from_ll(lhs_val * rhs_val);
         case MP_BINARY_OP_FLOOR_DIVIDE:
         case MP_BINARY_OP_INPLACE_FLOOR_DIVIDE:
+            if (rhs_val == 0) {
+                goto zero_division;
+            }
             return mp_obj_new_int_from_ll(lhs_val / rhs_val);
         case MP_BINARY_OP_MODULO:
         case MP_BINARY_OP_INPLACE_MODULO:
+            if (rhs_val == 0) {
+                goto zero_division;
+            }
             return mp_obj_new_int_from_ll(lhs_val % rhs_val);
 
         case MP_BINARY_OP_AND:
@@ -210,6 +216,9 @@ mp_obj_t mp_obj_int_binary_op(mp_binary_op_t op, mp_obj_t lhs_in, mp_obj_t rhs_i
         default:
             return MP_OBJ_NULL; // op not supported
     }
+
+zero_division:
+    mp_raise_msg(&mp_type_ZeroDivisionError, "division by zero");
 }
 
 mp_obj_t mp_obj_new_int(mp_int_t value) {

+ 1 - 1
py/objint_mpz.c

@@ -207,7 +207,7 @@ mp_obj_t mp_obj_int_binary_op(mp_binary_op_t op, mp_obj_t lhs_in, mp_obj_t rhs_i
         return mp_obj_new_float(flhs / frhs);
 #endif
 
-    } else if (op >= MP_BINARY_OP_INPLACE_OR) {
+    } else if (op >= MP_BINARY_OP_INPLACE_OR && op < MP_BINARY_OP_CONTAINS) {
         mp_obj_int_t *res = mp_obj_int_new_mpz();
 
         switch (op) {

+ 38 - 18
py/objnamedtuple.c

@@ -30,20 +30,11 @@
 #include "py/objtuple.h"
 #include "py/runtime.h"
 #include "py/objstr.h"
+#include "py/objnamedtuple.h"
 
 #if MICROPY_PY_COLLECTIONS
 
-typedef struct _mp_obj_namedtuple_type_t {
-    mp_obj_type_t base;
-    size_t n_fields;
-    qstr fields[];
-} mp_obj_namedtuple_type_t;
-
-typedef struct _mp_obj_namedtuple_t {
-    mp_obj_tuple_t tuple;
-} mp_obj_namedtuple_t;
-
-STATIC size_t namedtuple_find_field(const mp_obj_namedtuple_type_t *type, qstr name) {
+size_t mp_obj_namedtuple_find_field(const mp_obj_namedtuple_type_t *type, qstr name) {
     for (size_t i = 0; i < type->n_fields; i++) {
         if (type->fields[i] == name) {
             return i;
@@ -52,6 +43,23 @@ STATIC size_t namedtuple_find_field(const mp_obj_namedtuple_type_t *type, qstr n
     return (size_t)-1;
 }
 
+#if MICROPY_PY_COLLECTIONS_NAMEDTUPLE__ASDICT
+STATIC mp_obj_t namedtuple_asdict(mp_obj_t self_in) {
+    mp_obj_namedtuple_t *self = MP_OBJ_TO_PTR(self_in);
+    const qstr *fields = ((mp_obj_namedtuple_type_t*)self->tuple.base.type)->fields;
+    mp_obj_t dict = mp_obj_new_dict(self->tuple.len);
+    //make it an OrderedDict
+    mp_obj_dict_t *dictObj = MP_OBJ_TO_PTR(dict);
+    dictObj->base.type = &mp_type_ordereddict;
+    dictObj->map.is_ordered = 1;
+    for (size_t i = 0; i < self->tuple.len; ++i) {
+        mp_obj_dict_store(dict, MP_OBJ_NEW_QSTR(fields[i]), self->tuple.items[i]);
+    }
+    return dict;
+}
+MP_DEFINE_CONST_FUN_OBJ_1(namedtuple_asdict_obj, namedtuple_asdict);
+#endif
+
 STATIC void namedtuple_print(const mp_print_t *print, mp_obj_t o_in, mp_print_kind_t kind) {
     (void)kind;
     mp_obj_namedtuple_t *o = MP_OBJ_TO_PTR(o_in);
@@ -64,7 +72,14 @@ STATIC void namedtuple_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest) {
     if (dest[0] == MP_OBJ_NULL) {
         // load attribute
         mp_obj_namedtuple_t *self = MP_OBJ_TO_PTR(self_in);
-        size_t id = namedtuple_find_field((mp_obj_namedtuple_type_t*)self->tuple.base.type, attr);
+        #if MICROPY_PY_COLLECTIONS_NAMEDTUPLE__ASDICT
+        if (attr == MP_QSTR__asdict) {
+            dest[0] = MP_OBJ_FROM_PTR(&namedtuple_asdict_obj);
+            dest[1] = self_in;
+            return;
+        }
+        #endif
+        size_t id = mp_obj_namedtuple_find_field((mp_obj_namedtuple_type_t*)self->tuple.base.type, attr);
         if (id == (size_t)-1) {
             return;
         }
@@ -104,7 +119,7 @@ STATIC mp_obj_t namedtuple_make_new(const mp_obj_type_t *type_in, size_t n_args,
     memset(&tuple->items[n_args], 0, sizeof(mp_obj_t) * n_kw);
     for (size_t i = n_args; i < n_args + 2 * n_kw; i += 2) {
         qstr kw = mp_obj_str_get_qstr(args[i]);
-        size_t id = namedtuple_find_field(type, kw);
+        size_t id = mp_obj_namedtuple_find_field(type, kw);
         if (id == (size_t)-1) {
             if (MICROPY_ERROR_REPORTING == MICROPY_ERROR_REPORTING_TERSE) {
                 mp_arg_error_terse_mismatch();
@@ -127,9 +142,18 @@ STATIC mp_obj_t namedtuple_make_new(const mp_obj_type_t *type_in, size_t n_args,
     return MP_OBJ_FROM_PTR(tuple);
 }
 
-STATIC mp_obj_t mp_obj_new_namedtuple_type(qstr name, size_t n_fields, mp_obj_t *fields) {
+mp_obj_namedtuple_type_t *mp_obj_new_namedtuple_base(size_t n_fields, mp_obj_t *fields) {
     mp_obj_namedtuple_type_t *o = m_new_obj_var(mp_obj_namedtuple_type_t, qstr, n_fields);
     memset(&o->base, 0, sizeof(o->base));
+    o->n_fields = n_fields;
+    for (size_t i = 0; i < n_fields; i++) {
+        o->fields[i] = mp_obj_str_get_qstr(fields[i]);
+    }
+    return o;
+}
+
+STATIC mp_obj_t mp_obj_new_namedtuple_type(qstr name, size_t n_fields, mp_obj_t *fields) {
+    mp_obj_namedtuple_type_t *o = mp_obj_new_namedtuple_base(n_fields, fields);
     o->base.base.type = &mp_type_type;
     o->base.name = name;
     o->base.print = namedtuple_print;
@@ -140,10 +164,6 @@ STATIC mp_obj_t mp_obj_new_namedtuple_type(qstr name, size_t n_fields, mp_obj_t
     o->base.subscr = mp_obj_tuple_subscr;
     o->base.getiter = mp_obj_tuple_getiter;
     o->base.parent = &mp_type_tuple;
-    o->n_fields = n_fields;
-    for (size_t i = 0; i < n_fields; i++) {
-        o->fields[i] = mp_obj_str_get_qstr(fields[i]);
-    }
     return MP_OBJ_FROM_PTR(o);
 }
 

+ 6 - 3
py/objobject.c

@@ -52,9 +52,12 @@ STATIC mp_obj_t object___new__(mp_obj_t cls) {
     if (!MP_OBJ_IS_TYPE(cls, &mp_type_type) || !mp_obj_is_instance_type((mp_obj_type_t*)MP_OBJ_TO_PTR(cls))) {
         mp_raise_TypeError("__new__ arg must be a user-type");
     }
-    mp_obj_t o = MP_OBJ_SENTINEL;
-    mp_obj_t res = mp_obj_instance_make_new(MP_OBJ_TO_PTR(cls), 1, 0, &o);
-    return res;
+    // This executes only "__new__" part of instance creation.
+    // TODO: This won't work well for classes with native bases.
+    // TODO: This is a hack, should be resolved along the lines of
+    // https://github.com/micropython/micropython/issues/606#issuecomment-43685883
+    const mp_obj_type_t *native_base;
+    return MP_OBJ_FROM_PTR(mp_obj_new_instance(MP_OBJ_TO_PTR(cls), &native_base));
 }
 STATIC MP_DEFINE_CONST_FUN_OBJ_1(object___new___fun_obj, object___new__);
 STATIC MP_DEFINE_CONST_STATICMETHOD_OBJ(object___new___obj, MP_ROM_PTR(&object___new___fun_obj));

+ 3 - 5
py/objset.c

@@ -351,11 +351,9 @@ STATIC mp_obj_t set_issuperset_proper(mp_obj_t self_in, mp_obj_t other_in) {
 }
 
 STATIC mp_obj_t set_equal(mp_obj_t self_in, mp_obj_t other_in) {
+    assert(is_set_or_frozenset(other_in));
     check_set_or_frozenset(self_in);
     mp_obj_set_t *self = MP_OBJ_TO_PTR(self_in);
-    if (!is_set_or_frozenset(other_in)) {
-        return mp_const_false;
-    }
     mp_obj_set_t *other = MP_OBJ_TO_PTR(other_in);
     if (self->set.used != other->set.used) {
         return mp_const_false;
@@ -461,7 +459,7 @@ STATIC mp_obj_t set_binary_op(mp_binary_op_t op, mp_obj_t lhs, mp_obj_t rhs) {
     #else
     bool update = true;
     #endif
-    if (op != MP_BINARY_OP_IN && !is_set_or_frozenset(rhs)) {
+    if (op != MP_BINARY_OP_CONTAINS && !is_set_or_frozenset(rhs)) {
         // For all ops except containment the RHS must be a set/frozenset
         return MP_OBJ_NULL;
     }
@@ -507,7 +505,7 @@ STATIC mp_obj_t set_binary_op(mp_binary_op_t op, mp_obj_t lhs, mp_obj_t rhs) {
             return set_issubset(lhs, rhs);
         case MP_BINARY_OP_MORE_EQUAL:
             return set_issuperset(lhs, rhs);
-        case MP_BINARY_OP_IN: {
+        case MP_BINARY_OP_CONTAINS: {
             mp_obj_set_t *o = MP_OBJ_TO_PTR(lhs);
             mp_obj_t elem = mp_set_lookup(&o->set, rhs, MP_MAP_LOOKUP);
             return mp_obj_new_bool(elem != MP_OBJ_NULL);

+ 45 - 27
py/objstr.c

@@ -164,7 +164,14 @@ mp_obj_t mp_obj_str_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_
                     mp_raise_msg(&mp_type_UnicodeError, NULL);
                 }
                 #endif
-                mp_obj_str_t *o = MP_OBJ_TO_PTR(mp_obj_new_str_of_type(type, NULL, str_len));
+
+                // Check if a qstr with this data already exists
+                qstr q = qstr_find_strn((const char*)str_data, str_len);
+                if (q != MP_QSTR_NULL) {
+                    return MP_OBJ_NEW_QSTR(q);
+                }
+
+                mp_obj_str_t *o = MP_OBJ_TO_PTR(mp_obj_new_str_copy(type, NULL, str_len));
                 o->data = str_data;
                 o->hash = str_hash;
                 return MP_OBJ_FROM_PTR(o);
@@ -176,7 +183,7 @@ mp_obj_t mp_obj_str_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_
                     mp_raise_msg(&mp_type_UnicodeError, NULL);
                 }
                 #endif
-                return mp_obj_new_str(bufinfo.buf, bufinfo.len, false);
+                return mp_obj_new_str(bufinfo.buf, bufinfo.len);
             }
     }
 }
@@ -205,7 +212,7 @@ STATIC mp_obj_t bytes_make_new(const mp_obj_type_t *type_in, size_t n_args, size
         if (str_hash == 0) {
             str_hash = qstr_compute_hash(str_data, str_len);
         }
-        mp_obj_str_t *o = MP_OBJ_TO_PTR(mp_obj_new_str_of_type(&mp_type_bytes, NULL, str_len));
+        mp_obj_str_t *o = MP_OBJ_TO_PTR(mp_obj_new_str_copy(&mp_type_bytes, NULL, str_len));
         o->data = str_data;
         o->hash = str_hash;
         return MP_OBJ_FROM_PTR(o);
@@ -226,7 +233,7 @@ STATIC mp_obj_t bytes_make_new(const mp_obj_type_t *type_in, size_t n_args, size
     // check if argument has the buffer protocol
     mp_buffer_info_t bufinfo;
     if (mp_get_buffer(args[0], &bufinfo, MP_BUFFER_READ)) {
-        return mp_obj_new_str_of_type(&mp_type_bytes, bufinfo.buf, bufinfo.len);
+        return mp_obj_new_bytes(bufinfo.buf, bufinfo.len);
     }
 
     vstr_t vstr;
@@ -377,8 +384,7 @@ mp_obj_t mp_obj_str_binary_op(mp_binary_op_t op, mp_obj_t lhs_in, mp_obj_t rhs_i
             return mp_obj_new_str_from_vstr(lhs_type, &vstr);
         }
 
-        case MP_BINARY_OP_IN:
-            /* NOTE `a in b` is `b.__contains__(a)` */
+        case MP_BINARY_OP_CONTAINS:
             return mp_obj_new_bool(find_subbytes(lhs_data, lhs_len, rhs_data, rhs_len, 1) != NULL);
 
         //case MP_BINARY_OP_NOT_EQUAL: // This is never passed here
@@ -423,7 +429,7 @@ STATIC mp_obj_t bytes_subscr(mp_obj_t self_in, mp_obj_t index, mp_obj_t value) {
         if (MICROPY_PY_BUILTINS_STR_UNICODE || type == &mp_type_bytes) {
             return MP_OBJ_NEW_SMALL_INT(self_data[index_val]);
         } else {
-            return mp_obj_new_str((char*)&self_data[index_val], 1, true);
+            return mp_obj_new_str_via_qstr((char*)&self_data[index_val], 1);
         }
     } else {
         return MP_OBJ_NULL; // op not supported
@@ -1046,7 +1052,7 @@ STATIC vstr_t mp_obj_str_format_helper(const char *str, const char *top, int *ar
             } else {
                 const char *lookup;
                 for (lookup = field_name; lookup < field_name_top && *lookup != '.' && *lookup != '['; lookup++);
-                mp_obj_t field_q = mp_obj_new_str(field_name, lookup - field_name, true/*?*/);
+                mp_obj_t field_q = mp_obj_new_str_via_qstr(field_name, lookup - field_name); // should it be via qstr?
                 field_name = lookup;
                 mp_map_elem_t *key_elem = mp_map_lookup(kwargs, field_q, MP_MAP_LOOKUP);
                 if (key_elem == NULL) {
@@ -1413,7 +1419,7 @@ STATIC mp_obj_t str_modulo_format(mp_obj_t pattern, size_t n_args, const mp_obj_
                 }
                 ++str;
             }
-            mp_obj_t k_obj = mp_obj_new_str((const char*)key, str - key, true);
+            mp_obj_t k_obj = mp_obj_new_str_via_qstr((const char*)key, str - key);
             arg = mp_obj_dict_get(dict, k_obj);
             str++;
         }
@@ -1977,8 +1983,9 @@ const mp_obj_type_t mp_type_bytes = {
 const mp_obj_str_t mp_const_empty_bytes_obj = {{&mp_type_bytes}, 0, 0, (const byte*)""};
 
 // Create a str/bytes object using the given data.  New memory is allocated and
-// the data is copied across.
-mp_obj_t mp_obj_new_str_of_type(const mp_obj_type_t *type, const byte* data, size_t len) {
+// the data is copied across.  This function should only be used if the type is bytes,
+// or if the type is str and the string data is known to be not interned.
+mp_obj_t mp_obj_new_str_copy(const mp_obj_type_t *type, const byte* data, size_t len) {
     mp_obj_str_t *o = m_new_obj(mp_obj_str_t);
     o->base.type = type;
     o->len = len;
@@ -1992,6 +1999,22 @@ mp_obj_t mp_obj_new_str_of_type(const mp_obj_type_t *type, const byte* data, siz
     return MP_OBJ_FROM_PTR(o);
 }
 
+// Create a str/bytes object using the given data.  If the type is str and the string
+// data is already interned, then a qstr object is returned.  Otherwise new memory is
+// allocated for the object and the data is copied across.
+mp_obj_t mp_obj_new_str_of_type(const mp_obj_type_t *type, const byte* data, size_t len) {
+    if (type == &mp_type_str) {
+        return mp_obj_new_str((const char*)data, len);
+    } else {
+        return mp_obj_new_bytes(data, len);
+    }
+}
+
+// Create a str using a qstr to store the data; may use existing or new qstr.
+mp_obj_t mp_obj_new_str_via_qstr(const char* data, size_t len) {
+    return MP_OBJ_NEW_QSTR(qstr_from_strn(data, len));
+}
+
 // Create a str/bytes object from the given vstr.  The vstr buffer is resized to
 // the exact length required and then reused for the str/bytes object.  The vstr
 // is cleared and can safely be passed to vstr_free if it was heap allocated.
@@ -2022,29 +2045,24 @@ mp_obj_t mp_obj_new_str_from_vstr(const mp_obj_type_t *type, vstr_t *vstr) {
     return MP_OBJ_FROM_PTR(o);
 }
 
-mp_obj_t mp_obj_new_str(const char* data, size_t len, bool make_qstr_if_not_already) {
-    if (make_qstr_if_not_already) {
-        // use existing, or make a new qstr
-        return MP_OBJ_NEW_QSTR(qstr_from_strn(data, len));
+mp_obj_t mp_obj_new_str(const char* data, size_t len) {
+    qstr q = qstr_find_strn(data, len);
+    if (q != MP_QSTR_NULL) {
+        // qstr with this data already exists
+        return MP_OBJ_NEW_QSTR(q);
     } else {
-        qstr q = qstr_find_strn(data, len);
-        if (q != MP_QSTR_NULL) {
-            // qstr with this data already exists
-            return MP_OBJ_NEW_QSTR(q);
-        } else {
-            // no existing qstr, don't make one
-            return mp_obj_new_str_of_type(&mp_type_str, (const byte*)data, len);
-        }
+        // no existing qstr, don't make one
+        return mp_obj_new_str_copy(&mp_type_str, (const byte*)data, len);
     }
 }
 
 mp_obj_t mp_obj_str_intern(mp_obj_t str) {
     GET_STR_DATA_LEN(str, data, len);
-    return MP_OBJ_NEW_QSTR(qstr_from_strn((const char*)data, len));
+    return mp_obj_new_str_via_qstr((const char*)data, len);
 }
 
 mp_obj_t mp_obj_new_bytes(const byte* data, size_t len) {
-    return mp_obj_new_str_of_type(&mp_type_bytes, data, len);
+    return mp_obj_new_str_copy(&mp_type_bytes, data, len);
 }
 
 bool mp_obj_str_equal(mp_obj_t s1, mp_obj_t s2) {
@@ -2066,7 +2084,7 @@ bool mp_obj_str_equal(mp_obj_t s1, mp_obj_t s2) {
     }
 }
 
-STATIC void bad_implicit_conversion(mp_obj_t self_in) {
+STATIC NORETURN void bad_implicit_conversion(mp_obj_t self_in) {
     if (MICROPY_ERROR_REPORTING == MICROPY_ERROR_REPORTING_TERSE) {
         mp_raise_TypeError("can't convert to str implicitly");
     } else {
@@ -2138,7 +2156,7 @@ STATIC mp_obj_t str_it_iternext(mp_obj_t self_in) {
     mp_obj_str8_it_t *self = MP_OBJ_TO_PTR(self_in);
     GET_STR_DATA_LEN(self->str, str, len);
     if (self->cur < len) {
-        mp_obj_t o_out = mp_obj_new_str((const char*)str + self->cur, 1, true);
+        mp_obj_t o_out = mp_obj_new_str_via_qstr((const char*)str + self->cur, 1);
         self->cur += 1;
         return o_out;
     } else {

+ 1 - 0
py/objstr.h

@@ -65,6 +65,7 @@ mp_obj_t mp_obj_str_make_new(const mp_obj_type_t *type_in, size_t n_args, size_t
 void mp_str_print_json(const mp_print_t *print, const byte *str_data, size_t str_len);
 mp_obj_t mp_obj_str_format(size_t n_args, const mp_obj_t *args, mp_map_t *kwargs);
 mp_obj_t mp_obj_str_split(size_t n_args, const mp_obj_t *args);
+mp_obj_t mp_obj_new_str_copy(const mp_obj_type_t *type, const byte* data, size_t len);
 mp_obj_t mp_obj_new_str_of_type(const mp_obj_type_t *type, const byte* data, size_t len);
 
 mp_obj_t mp_obj_str_binary_op(mp_binary_op_t op, mp_obj_t lhs_in, mp_obj_t rhs_in);

+ 2 - 2
py/objstrunicode.c

@@ -216,7 +216,7 @@ STATIC mp_obj_t str_subscr(mp_obj_t self_in, mp_obj_t index, mp_obj_t value) {
                 ++len;
             }
         }
-        return mp_obj_new_str((const char*)s, len, true); // This will create a one-character string
+        return mp_obj_new_str_via_qstr((const char*)s, len); // This will create a one-character string
     } else {
         return MP_OBJ_NULL; // op not supported
     }
@@ -291,7 +291,7 @@ STATIC mp_obj_t str_it_iternext(mp_obj_t self_in) {
     if (self->cur < len) {
         const byte *cur = str + self->cur;
         const byte *end = utf8_next_char(str + self->cur);
-        mp_obj_t o_out = mp_obj_new_str((const char*)cur, end - cur, true);
+        mp_obj_t o_out = mp_obj_new_str_via_qstr((const char*)cur, end - cur);
         self->cur += end - cur;
         return o_out;
     } else {

+ 114 - 57
py/objtype.c

@@ -46,14 +46,6 @@ STATIC mp_obj_t static_class_method_make_new(const mp_obj_type_t *self_in, size_
 /******************************************************************************/
 // instance object
 
-STATIC mp_obj_t mp_obj_new_instance(const mp_obj_type_t *class, size_t subobjs) {
-    mp_obj_instance_t *o = m_new_obj_var(mp_obj_instance_t, mp_obj_t, subobjs);
-    o->base.type = class;
-    mp_map_init(&o->members, 0);
-    mp_seq_clear(o->subobj, 0, subobjs, sizeof(*o->subobj));
-    return MP_OBJ_FROM_PTR(o);
-}
-
 STATIC int instance_count_native_bases(const mp_obj_type_t *type, const mp_obj_type_t **last_native_base) {
     int count = 0;
     for (;;) {
@@ -67,6 +59,7 @@ STATIC int instance_count_native_bases(const mp_obj_type_t *type, const mp_obj_t
         } else if (type->parent == NULL) {
             // No parents so end search here.
             return count;
+        #if MICROPY_MULTIPLE_INHERITANCE
         } else if (((mp_obj_base_t*)type->parent)->type == &mp_type_tuple) {
             // Multiple parents, search through them all recursively.
             const mp_obj_tuple_t *parent_tuple = type->parent;
@@ -78,6 +71,7 @@ STATIC int instance_count_native_bases(const mp_obj_type_t *type, const mp_obj_t
                 count += instance_count_native_bases(bt, last_native_base);
             }
             return count;
+        #endif
         } else {
             // A single parent, use iteration to continue the search.
             type = type->parent;
@@ -85,6 +79,35 @@ STATIC int instance_count_native_bases(const mp_obj_type_t *type, const mp_obj_t
     }
 }
 
+// This wrapper function is allows a subclass of a native type to call the
+// __init__() method (corresponding to type->make_new) of the native type.
+STATIC mp_obj_t native_base_init_wrapper(size_t n_args, const mp_obj_t *args) {
+    mp_obj_instance_t *self = MP_OBJ_TO_PTR(args[0]);
+    const mp_obj_type_t *native_base = NULL;
+    instance_count_native_bases(self->base.type, &native_base);
+    self->subobj[0] = native_base->make_new(native_base, n_args - 1, 0, args + 1);
+    return mp_const_none;
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(native_base_init_wrapper_obj, 1, MP_OBJ_FUN_ARGS_MAX, native_base_init_wrapper);
+
+#if !MICROPY_CPYTHON_COMPAT
+STATIC
+#endif
+mp_obj_instance_t *mp_obj_new_instance(const mp_obj_type_t *class, const mp_obj_type_t **native_base) {
+    size_t num_native_bases = instance_count_native_bases(class, native_base);
+    assert(num_native_bases < 2);
+    mp_obj_instance_t *o = m_new_obj_var(mp_obj_instance_t, mp_obj_t, num_native_bases);
+    o->base.type = class;
+    mp_map_init(&o->members, 0);
+    // Initialise the native base-class slot (should be 1 at most) with a valid
+    // object.  It doesn't matter which object, so long as it can be uniquely
+    // distinguished from a native class that is initialised.
+    if (num_native_bases != 0) {
+        o->subobj[0] = MP_OBJ_FROM_PTR(&native_base_init_wrapper_obj);
+    }
+    return o;
+}
+
 // TODO
 // This implements depth-first left-to-right MRO, which is not compliant with Python3 MRO
 // http://python-history.blogspot.com/2010/06/method-resolution-order.html
@@ -172,6 +195,7 @@ STATIC void mp_obj_class_lookup(struct class_lookup_data  *lookup, const mp_obj_
         if (type->parent == NULL) {
             DEBUG_printf("mp_obj_class_lookup: No more parents\n");
             return;
+        #if MICROPY_MULTIPLE_INHERITANCE
         } else if (((mp_obj_base_t*)type->parent)->type == &mp_type_tuple) {
             const mp_obj_tuple_t *parent_tuple = type->parent;
             const mp_obj_t *item = parent_tuple->items;
@@ -192,6 +216,7 @@ STATIC void mp_obj_class_lookup(struct class_lookup_data  *lookup, const mp_obj_
             // search last base (simple tail recursion elimination)
             assert(MP_OBJ_IS_TYPE(*item, &mp_type_type));
             type = (mp_obj_type_t*)MP_OBJ_TO_PTR(*item);
+        #endif
         } else {
             type = type->parent;
         }
@@ -247,20 +272,6 @@ STATIC void instance_print(const mp_print_t *print, mp_obj_t self_in, mp_print_k
 mp_obj_t mp_obj_instance_make_new(const mp_obj_type_t *self, size_t n_args, size_t n_kw, const mp_obj_t *args) {
     assert(mp_obj_is_instance_type(self));
 
-    const mp_obj_type_t *native_base;
-    size_t num_native_bases = instance_count_native_bases(self, &native_base);
-    assert(num_native_bases < 2);
-
-    mp_obj_instance_t *o = MP_OBJ_TO_PTR(mp_obj_new_instance(self, num_native_bases));
-
-    // This executes only "__new__" part of instance creation.
-    // TODO: This won't work well for classes with native bases.
-    // TODO: This is a hack, should be resolved along the lines of
-    // https://github.com/micropython/micropython/issues/606#issuecomment-43685883
-    if (n_args == 1 && *args == MP_OBJ_SENTINEL) {
-        return MP_OBJ_FROM_PTR(o);
-    }
-
     // look for __new__ function
     mp_obj_t init_fn[2] = {MP_OBJ_NULL};
     struct class_lookup_data lookup = {
@@ -272,13 +283,22 @@ mp_obj_t mp_obj_instance_make_new(const mp_obj_type_t *self, size_t n_args, size
     };
     mp_obj_class_lookup(&lookup, self);
 
-    mp_obj_t new_ret = MP_OBJ_FROM_PTR(o);
-    if (init_fn[0] == MP_OBJ_SENTINEL) {
-        // Native type's constructor is what wins - it gets all our arguments,
-        // and none Python classes are initialized at all.
-        o->subobj[0] = native_base->make_new(native_base, n_args, n_kw, args);
-    } else if (init_fn[0] != MP_OBJ_NULL) {
-        // now call Python class __new__ function with all args
+    const mp_obj_type_t *native_base = NULL;
+    mp_obj_instance_t *o;
+    if (init_fn[0] == MP_OBJ_NULL || init_fn[0] == MP_OBJ_SENTINEL) {
+        // Either there is no __new__() method defined or there is a native
+        // constructor.  In both cases create a blank instance.
+        o = mp_obj_new_instance(self, &native_base);
+
+        // Since type->make_new() implements both __new__() and __init__() in
+        // one go, of which the latter may be overridden by the Python subclass,
+        // we defer (see the end of this function) the call of the native
+        // constructor to give a chance for the Python __init__() method to call
+        // said native constructor.
+
+    } else {
+        // Call Python class __new__ function with all args to create an instance
+        mp_obj_t new_ret;
         if (n_args == 0 && n_kw == 0) {
             mp_obj_t args2[1] = {MP_OBJ_FROM_PTR(self)};
             new_ret = mp_call_function_n_kw(init_fn[0], 1, 0, args2);
@@ -290,17 +310,20 @@ mp_obj_t mp_obj_instance_make_new(const mp_obj_type_t *self, size_t n_args, size
             m_del(mp_obj_t, args2, 1 + n_args + 2 * n_kw);
         }
 
-    }
+        // https://docs.python.org/3.4/reference/datamodel.html#object.__new__
+        // "If __new__() does not return an instance of cls, then the new
+        // instance's __init__() method will not be invoked."
+        if (mp_obj_get_type(new_ret) != self) {
+            return new_ret;
+        }
 
-    // https://docs.python.org/3.4/reference/datamodel.html#object.__new__
-    // "If __new__() does not return an instance of cls, then the new instance's __init__() method will not be invoked."
-    if (mp_obj_get_type(new_ret) != self) {
-        return new_ret;
+        // The instance returned by __new__() becomes the new object
+        o = MP_OBJ_TO_PTR(new_ret);
     }
 
-    o = MP_OBJ_TO_PTR(new_ret);
-
     // now call Python class __init__ function with all args
+    // This method has a chance to call super().__init__() to construct a
+    // possible native base class.
     init_fn[0] = init_fn[1] = MP_OBJ_NULL;
     lookup.obj = o;
     lookup.attr = MP_QSTR___init__;
@@ -329,6 +352,12 @@ mp_obj_t mp_obj_instance_make_new(const mp_obj_type_t *self, size_t n_args, size
 
     }
 
+    // If the type had a native base that was not explicitly initialised
+    // (constructed) by the Python __init__() method then construct it now.
+    if (native_base != NULL && o->subobj[0] == MP_OBJ_FROM_PTR(&native_base_init_wrapper_obj)) {
+        o->subobj[0] = native_base->make_new(native_base, n_args, n_kw, args);
+    }
+
     return MP_OBJ_FROM_PTR(o);
 }
 
@@ -420,7 +449,7 @@ const byte mp_binary_op_method_name[MP_BINARY_OP_NUM_RUNTIME] = {
     [MP_BINARY_OP_LESS_EQUAL] = MP_QSTR___le__,
     [MP_BINARY_OP_MORE_EQUAL] = MP_QSTR___ge__,
     // MP_BINARY_OP_NOT_EQUAL, // a != b calls a == b and inverts result
-    [MP_BINARY_OP_IN] = MP_QSTR___contains__,
+    [MP_BINARY_OP_CONTAINS] = MP_QSTR___contains__,
 
     // All inplace methods are optional, and normal methods will be used
     // as a fallback.
@@ -946,21 +975,21 @@ STATIC void type_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest) {
         if (self->locals_dict != NULL) {
             assert(self->locals_dict->base.type == &mp_type_dict); // MicroPython restriction, for now
             mp_map_t *locals_map = &self->locals_dict->map;
+            if (locals_map->is_fixed) {
+                // can't apply delete/store to a fixed map
+                return;
+            }
             if (dest[1] == MP_OBJ_NULL) {
                 // delete attribute
                 mp_map_elem_t *elem = mp_map_lookup(locals_map, MP_OBJ_NEW_QSTR(attr), MP_MAP_LOOKUP_REMOVE_IF_FOUND);
-                // note that locals_map may be in ROM, so remove will fail in that case
                 if (elem != NULL) {
                     dest[0] = MP_OBJ_NULL; // indicate success
                 }
             } else {
                 // store attribute
                 mp_map_elem_t *elem = mp_map_lookup(locals_map, MP_OBJ_NEW_QSTR(attr), MP_MAP_LOOKUP_ADD_IF_NOT_FOUND);
-                // note that locals_map may be in ROM, so add will fail in that case
-                if (elem != NULL) {
-                    elem->value = dest[1];
-                    dest[0] = MP_OBJ_NULL; // indicate success
-                }
+                elem->value = dest[1];
+                dest[0] = MP_OBJ_NULL; // indicate success
             }
         }
     }
@@ -983,12 +1012,12 @@ mp_obj_t mp_obj_new_type(qstr name, mp_obj_t bases_tuple, mp_obj_t locals_dict)
     // TODO might need to make a copy of locals_dict; at least that's how CPython does it
 
     // Basic validation of base classes
-    size_t len;
-    mp_obj_t *items;
-    mp_obj_tuple_get(bases_tuple, &len, &items);
-    for (size_t i = 0; i < len; i++) {
-        assert(MP_OBJ_IS_TYPE(items[i], &mp_type_type));
-        mp_obj_type_t *t = MP_OBJ_TO_PTR(items[i]);
+    size_t bases_len;
+    mp_obj_t *bases_items;
+    mp_obj_tuple_get(bases_tuple, &bases_len, &bases_items);
+    for (size_t i = 0; i < bases_len; i++) {
+        assert(MP_OBJ_IS_TYPE(bases_items[i], &mp_type_type));
+        mp_obj_type_t *t = MP_OBJ_TO_PTR(bases_items[i]);
         // TODO: Verify with CPy, tested on function type
         if (t->make_new == NULL) {
             if (MICROPY_ERROR_REPORTING == MICROPY_ERROR_REPORTING_TERSE) {
@@ -1014,17 +1043,21 @@ mp_obj_t mp_obj_new_type(qstr name, mp_obj_t bases_tuple, mp_obj_t locals_dict)
     //o->iternext = ; not implemented
     o->buffer_p.get_buffer = instance_get_buffer;
 
-    if (len > 0) {
+    if (bases_len > 0) {
         // Inherit protocol from a base class. This allows to define an
         // abstract base class which would translate C-level protocol to
         // Python method calls, and any subclass inheriting from it will
         // support this feature.
-        o->protocol = ((mp_obj_type_t*)MP_OBJ_TO_PTR(items[0]))->protocol;
+        o->protocol = ((mp_obj_type_t*)MP_OBJ_TO_PTR(bases_items[0]))->protocol;
 
-        if (len >= 2) {
+        if (bases_len >= 2) {
+            #if MICROPY_MULTIPLE_INHERITANCE
             o->parent = MP_OBJ_TO_PTR(bases_tuple);
+            #else
+            mp_raise_NotImplementedError("multiple inheritance not supported");
+            #endif
         } else {
-            o->parent = MP_OBJ_TO_PTR(items[0]);
+            o->parent = MP_OBJ_TO_PTR(bases_items[0]);
         }
     }
 
@@ -1099,26 +1132,48 @@ STATIC void super_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest) {
         .is_type = false,
     };
 
+    // Allow a call super().__init__() to reach any native base classes
+    if (attr == MP_QSTR___init__) {
+        lookup.meth_offset = offsetof(mp_obj_type_t, make_new);
+    }
+
     if (type->parent == NULL) {
         // no parents, do nothing
+    #if MICROPY_MULTIPLE_INHERITANCE
     } else if (((mp_obj_base_t*)type->parent)->type == &mp_type_tuple) {
         const mp_obj_tuple_t *parent_tuple = type->parent;
         size_t len = parent_tuple->len;
         const mp_obj_t *items = parent_tuple->items;
         for (size_t i = 0; i < len; i++) {
             assert(MP_OBJ_IS_TYPE(items[i], &mp_type_type));
+            if (MP_OBJ_TO_PTR(items[i]) == &mp_type_object) {
+                // The "object" type will be searched at the end of this function,
+                // and we don't want to lookup native methods in object.
+                continue;
+            }
             mp_obj_class_lookup(&lookup, (mp_obj_type_t*)MP_OBJ_TO_PTR(items[i]));
             if (dest[0] != MP_OBJ_NULL) {
-                return;
+                break;
             }
         }
-    } else {
+    #endif
+    } else if (type->parent != &mp_type_object) {
         mp_obj_class_lookup(&lookup, type->parent);
-        if (dest[0] != MP_OBJ_NULL) {
-            return;
+    }
+
+    if (dest[0] != MP_OBJ_NULL) {
+        if (dest[0] == MP_OBJ_SENTINEL) {
+            // Looked up native __init__ so defer to it
+            dest[0] = MP_OBJ_FROM_PTR(&native_base_init_wrapper_obj);
+            dest[1] = self->obj;
         }
+        return;
     }
 
+    // Reset meth_offset so we don't look up any native methods in object,
+    // because object never takes up the native base-class slot.
+    lookup.meth_offset = 0;
+
     mp_obj_class_lookup(&lookup, &mp_type_object);
 }
 
@@ -1158,6 +1213,7 @@ bool mp_obj_is_subclass_fast(mp_const_obj_t object, mp_const_obj_t classinfo) {
         if (self->parent == NULL) {
             // type has no parents
             return false;
+        #if MICROPY_MULTIPLE_INHERITANCE
         } else if (((mp_obj_base_t*)self->parent)->type == &mp_type_tuple) {
             // get the base objects (they should be type objects)
             const mp_obj_tuple_t *parent_tuple = self->parent;
@@ -1173,6 +1229,7 @@ bool mp_obj_is_subclass_fast(mp_const_obj_t object, mp_const_obj_t classinfo) {
 
             // search last base (simple tail recursion elimination)
             object = *item;
+        #endif
         } else {
             // type has 1 parent
             object = MP_OBJ_FROM_PTR(self->parent);

+ 5 - 0
py/objtype.h

@@ -37,6 +37,11 @@ typedef struct _mp_obj_instance_t {
     // TODO maybe cache __getattr__ and __setattr__ for efficient lookup of them
 } mp_obj_instance_t;
 
+#if MICROPY_CPYTHON_COMPAT
+// this is needed for object.__new__
+mp_obj_instance_t *mp_obj_new_instance(const mp_obj_type_t *cls, const mp_obj_type_t **native_base);
+#endif
+
 // this needs to be exposed for MICROPY_OPT_CACHE_MAP_LOOKUP_IN_BYTECODE to work
 void mp_obj_instance_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest);
 

+ 2 - 2
py/opmethods.c

@@ -24,7 +24,7 @@
  * THE SOFTWARE.
  */
 
-#include "py/runtime0.h"
+#include "py/obj.h"
 #include "py/builtin.h"
 
 STATIC mp_obj_t op_getitem(mp_obj_t self_in, mp_obj_t key_in) {
@@ -47,6 +47,6 @@ MP_DEFINE_CONST_FUN_OBJ_2(mp_op_delitem_obj, op_delitem);
 
 STATIC mp_obj_t op_contains(mp_obj_t lhs_in, mp_obj_t rhs_in) {
     mp_obj_type_t *type = mp_obj_get_type(lhs_in);
-    return type->binary_op(MP_BINARY_OP_IN, lhs_in, rhs_in);
+    return type->binary_op(MP_BINARY_OP_CONTAINS, lhs_in, rhs_in);
 }
 MP_DEFINE_CONST_FUN_OBJ_2(mp_op_contains_obj, op_contains);

+ 203 - 104
py/parse.c

@@ -3,7 +3,7 @@
  *
  * The MIT License (MIT)
  *
- * Copyright (c) 2013-2015 Damien P. George
+ * Copyright (c) 2013-2017 Damien P. George
  *
  * Permission is hereby granted, free of charge, to any person obtaining a copy
  * of this software and associated documentation files (the "Software"), to deal
@@ -27,7 +27,6 @@
 #include <stdbool.h>
 #include <stdint.h>
 #include <stdio.h>
-#include <sys/types.h>
 #include <unistd.h> // for ssize_t
 #include <assert.h>
 #include <string.h>
@@ -59,15 +58,6 @@
 // (un)comment to use rule names; for debugging
 //#define USE_RULE_NAME (1)
 
-typedef struct _rule_t {
-    byte rule_id;
-    byte act;
-#ifdef USE_RULE_NAME
-    const char *rule_name;
-#endif
-    uint16_t arg[];
-} rule_t;
-
 enum {
 // define rules with a compile function
 #define DEF_RULE(rule, comp, kind, ...) RULE_##rule,
@@ -85,6 +75,8 @@ enum {
 #undef DEF_RULE_NC
 };
 
+// Define an array of actions corresponding to each rule
+STATIC const uint8_t rule_act_table[] = {
 #define or(n)                   (RULE_ACT_OR | n)
 #define and(n)                  (RULE_ACT_AND | n)
 #define and_ident(n)            (RULE_ACT_AND | n | RULE_ACT_ALLOW_IDENT)
@@ -92,44 +84,128 @@ enum {
 #define one_or_more             (RULE_ACT_LIST | 2)
 #define list                    (RULE_ACT_LIST | 1)
 #define list_with_end           (RULE_ACT_LIST | 3)
-#define tok(t)                  (RULE_ARG_TOK | MP_TOKEN_##t)
-#define rule(r)                 (RULE_ARG_RULE | RULE_##r)
-#define opt_rule(r)             (RULE_ARG_OPT_RULE | RULE_##r)
-#ifdef USE_RULE_NAME
-#define DEF_RULE(rule, comp, kind, ...) static const rule_t rule_##rule = { RULE_##rule, kind, #rule, { __VA_ARGS__ } };
-#define DEF_RULE_NC(rule, kind, ...) static const rule_t rule_##rule = { RULE_##rule, kind, #rule, { __VA_ARGS__ } };
-#else
-#define DEF_RULE(rule, comp, kind, ...) static const rule_t rule_##rule = { RULE_##rule, kind, { __VA_ARGS__ } };
-#define DEF_RULE_NC(rule, kind, ...) static const rule_t rule_##rule = { RULE_##rule, kind, { __VA_ARGS__ } };
-#endif
+
+#define DEF_RULE(rule, comp, kind, ...) kind,
+#define DEF_RULE_NC(rule, kind, ...)
+#include "py/grammar.h"
+#undef DEF_RULE
+#undef DEF_RULE_NC
+
+    0, // RULE_const_object
+
+#define DEF_RULE(rule, comp, kind, ...)
+#define DEF_RULE_NC(rule, kind, ...) kind,
 #include "py/grammar.h"
+#undef DEF_RULE
+#undef DEF_RULE_NC
+
 #undef or
 #undef and
+#undef and_ident
+#undef and_blank
+#undef one_or_more
 #undef list
 #undef list_with_end
+};
+
+// Define the argument data for each rule, as a combined array
+STATIC const uint16_t rule_arg_combined_table[] = {
+#define tok(t)                  (RULE_ARG_TOK | MP_TOKEN_##t)
+#define rule(r)                 (RULE_ARG_RULE | RULE_##r)
+#define opt_rule(r)             (RULE_ARG_OPT_RULE | RULE_##r)
+
+#define DEF_RULE(rule, comp, kind, ...) __VA_ARGS__,
+#define DEF_RULE_NC(rule, kind, ...)
+#include "py/grammar.h"
+#undef DEF_RULE
+#undef DEF_RULE_NC
+
+#define DEF_RULE(rule, comp, kind, ...)
+#define DEF_RULE_NC(rule, kind, ...)  __VA_ARGS__,
+#include "py/grammar.h"
+#undef DEF_RULE
+#undef DEF_RULE_NC
+
 #undef tok
 #undef rule
 #undef opt_rule
-#undef one_or_more
+};
+
+// Macro to create a list of N identifiers where N is the number of variable arguments to the macro
+#define RULE_EXPAND(x) x
+#define RULE_PADDING(rule, ...) RULE_PADDING2(rule, __VA_ARGS__, RULE_PADDING_IDS(rule))
+#define RULE_PADDING2(rule, ...) RULE_EXPAND(RULE_PADDING3(rule, __VA_ARGS__))
+#define RULE_PADDING3(rule, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, ...) __VA_ARGS__
+#define RULE_PADDING_IDS(r) PAD12_##r, PAD11_##r, PAD10_##r, PAD9_##r, PAD8_##r, PAD7_##r, PAD6_##r, PAD5_##r, PAD4_##r, PAD3_##r, PAD2_##r, PAD1_##r,
+
+// Use an enum to create constants specifying how much room a rule takes in rule_arg_combined_table
+enum {
+#define DEF_RULE(rule, comp, kind, ...) RULE_PADDING(rule, __VA_ARGS__)
+#define DEF_RULE_NC(rule, kind, ...)
+#include "py/grammar.h"
+#undef DEF_RULE
+#undef DEF_RULE_NC
+#define DEF_RULE(rule, comp, kind, ...)
+#define DEF_RULE_NC(rule, kind, ...) RULE_PADDING(rule, __VA_ARGS__)
+#include "py/grammar.h"
 #undef DEF_RULE
 #undef DEF_RULE_NC
+};
 
-STATIC const rule_t *const rules[] = {
-// define rules with a compile function
-#define DEF_RULE(rule, comp, kind, ...) &rule_##rule,
+// Macro to compute the start of a rule in rule_arg_combined_table
+#define RULE_ARG_OFFSET(rule, ...) RULE_ARG_OFFSET2(rule, __VA_ARGS__, RULE_ARG_OFFSET_IDS(rule))
+#define RULE_ARG_OFFSET2(rule, ...) RULE_EXPAND(RULE_ARG_OFFSET3(rule, __VA_ARGS__))
+#define RULE_ARG_OFFSET3(rule, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, ...) _13
+#define RULE_ARG_OFFSET_IDS(r) PAD12_##r, PAD11_##r, PAD10_##r, PAD9_##r, PAD8_##r, PAD7_##r, PAD6_##r, PAD5_##r, PAD4_##r, PAD3_##r, PAD2_##r, PAD1_##r, PAD0_##r,
+
+// Use the above enum values to create a table of offsets for each rule's arg
+// data, which indexes rule_arg_combined_table.  The offsets require 9 bits of
+// storage but only the lower 8 bits are stored here.  The 9th bit is computed
+// in get_rule_arg using the FIRST_RULE_WITH_OFFSET_ABOVE_255 constant.
+STATIC const uint8_t rule_arg_offset_table[] = {
+#define DEF_RULE(rule, comp, kind, ...) RULE_ARG_OFFSET(rule, __VA_ARGS__) & 0xff,
 #define DEF_RULE_NC(rule, kind, ...)
 #include "py/grammar.h"
 #undef DEF_RULE
 #undef DEF_RULE_NC
-    NULL, // RULE_const_object
+    0, // RULE_const_object
+#define DEF_RULE(rule, comp, kind, ...)
+#define DEF_RULE_NC(rule, kind, ...) RULE_ARG_OFFSET(rule, __VA_ARGS__) & 0xff,
+#include "py/grammar.h"
+#undef DEF_RULE
+#undef DEF_RULE_NC
+};
 
-// define rules without a compile function
+// Define a constant that's used to determine the 9th bit of the values in rule_arg_offset_table
+static const size_t FIRST_RULE_WITH_OFFSET_ABOVE_255 =
+#define DEF_RULE(rule, comp, kind, ...) RULE_ARG_OFFSET(rule, __VA_ARGS__) >= 0x100 ? RULE_##rule :
+#define DEF_RULE_NC(rule, kind, ...)
+#include "py/grammar.h"
+#undef DEF_RULE
+#undef DEF_RULE_NC
 #define DEF_RULE(rule, comp, kind, ...)
-#define DEF_RULE_NC(rule, kind, ...) &rule_##rule,
+#define DEF_RULE_NC(rule, kind, ...) RULE_ARG_OFFSET(rule, __VA_ARGS__) >= 0x100 ? RULE_##rule :
+#include "py/grammar.h"
+#undef DEF_RULE
+#undef DEF_RULE_NC
+0;
+
+#if USE_RULE_NAME
+// Define an array of rule names corresponding to each rule
+STATIC const char *const rule_name_table[] = {
+#define DEF_RULE(rule, comp, kind, ...) #rule,
+#define DEF_RULE_NC(rule, kind, ...)
+#include "py/grammar.h"
+#undef DEF_RULE
+#undef DEF_RULE_NC
+    "", // RULE_const_object
+#define DEF_RULE(rule, comp, kind, ...)
+#define DEF_RULE_NC(rule, kind, ...) #rule,
 #include "py/grammar.h"
 #undef DEF_RULE
 #undef DEF_RULE_NC
 };
+#endif
 
 typedef struct _rule_stack_t {
     size_t src_line : 8 * sizeof(size_t) - 8; // maximum bits storing source line number
@@ -165,6 +241,14 @@ typedef struct _parser_t {
     #endif
 } parser_t;
 
+STATIC const uint16_t *get_rule_arg(uint8_t r_id) {
+    size_t off = rule_arg_offset_table[r_id];
+    if (r_id >= FIRST_RULE_WITH_OFFSET_ABOVE_255) {
+        off |= 0x100;
+    }
+    return &rule_arg_combined_table[off];
+}
+
 STATIC void *parser_alloc(parser_t *parser, size_t num_bytes) {
     // use a custom memory allocator to store parse nodes sequentially in large chunks
 
@@ -206,7 +290,7 @@ STATIC void *parser_alloc(parser_t *parser, size_t num_bytes) {
     return ret;
 }
 
-STATIC void push_rule(parser_t *parser, size_t src_line, const rule_t *rule, size_t arg_i) {
+STATIC void push_rule(parser_t *parser, size_t src_line, uint8_t rule_id, size_t arg_i) {
     if (parser->rule_stack_top >= parser->rule_stack_alloc) {
         rule_stack_t *rs = m_renew(rule_stack_t, parser->rule_stack, parser->rule_stack_alloc, parser->rule_stack_alloc + MICROPY_ALLOC_PARSE_RULE_INC);
         parser->rule_stack = rs;
@@ -214,21 +298,22 @@ STATIC void push_rule(parser_t *parser, size_t src_line, const rule_t *rule, siz
     }
     rule_stack_t *rs = &parser->rule_stack[parser->rule_stack_top++];
     rs->src_line = src_line;
-    rs->rule_id = rule->rule_id;
+    rs->rule_id = rule_id;
     rs->arg_i = arg_i;
 }
 
 STATIC void push_rule_from_arg(parser_t *parser, size_t arg) {
     assert((arg & RULE_ARG_KIND_MASK) == RULE_ARG_RULE || (arg & RULE_ARG_KIND_MASK) == RULE_ARG_OPT_RULE);
     size_t rule_id = arg & RULE_ARG_ARG_MASK;
-    push_rule(parser, parser->lexer->tok_line, rules[rule_id], 0);
+    push_rule(parser, parser->lexer->tok_line, rule_id, 0);
 }
 
-STATIC void pop_rule(parser_t *parser, const rule_t **rule, size_t *arg_i, size_t *src_line) {
+STATIC uint8_t pop_rule(parser_t *parser, size_t *arg_i, size_t *src_line) {
     parser->rule_stack_top -= 1;
-    *rule = rules[parser->rule_stack[parser->rule_stack_top].rule_id];
+    uint8_t rule_id = parser->rule_stack[parser->rule_stack_top].rule_id;
     *arg_i = parser->rule_stack[parser->rule_stack_top].arg_i;
     *src_line = parser->rule_stack[parser->rule_stack_top].src_line;
+    return rule_id;
 }
 
 bool mp_parse_node_is_const_false(mp_parse_node_t pn) {
@@ -314,11 +399,11 @@ void mp_parse_node_print(mp_parse_node_t pn, size_t indent) {
             #endif
         } else {
             size_t n = MP_PARSE_NODE_STRUCT_NUM_NODES(pns);
-#ifdef USE_RULE_NAME
-            printf("%s(%u) (n=%u)\n", rules[MP_PARSE_NODE_STRUCT_KIND(pns)]->rule_name, (uint)MP_PARSE_NODE_STRUCT_KIND(pns), (uint)n);
-#else
+            #if USE_RULE_NAME
+            printf("%s(%u) (n=%u)\n", rule_name_table[MP_PARSE_NODE_STRUCT_KIND(pns)], (uint)MP_PARSE_NODE_STRUCT_KIND(pns), (uint)n);
+            #else
             printf("rule(%u) (n=%u)\n", (uint)MP_PARSE_NODE_STRUCT_KIND(pns), (uint)n);
-#endif
+            #endif
             for (size_t i = 0; i < n; i++) {
                 mp_parse_node_print(pns->nodes[i], indent + 2);
             }
@@ -370,7 +455,19 @@ STATIC mp_parse_node_t make_node_const_object(parser_t *parser, size_t src_line,
     return (mp_parse_node_t)pn;
 }
 
-STATIC void push_result_token(parser_t *parser, const rule_t *rule) {
+STATIC mp_parse_node_t mp_parse_node_new_small_int_checked(parser_t *parser, mp_obj_t o_val) {
+    (void)parser;
+    mp_int_t val = MP_OBJ_SMALL_INT_VALUE(o_val);
+    #if MICROPY_OBJ_REPR == MICROPY_OBJ_REPR_D
+    // A parse node is only 32-bits and the small-int value must fit in 31-bits
+    if (((val ^ (val << 1)) & 0xffffffff80000000) != 0) {
+        return make_node_const_object(parser, 0, o_val);
+    }
+    #endif
+    return mp_parse_node_new_small_int(val);
+}
+
+STATIC void push_result_token(parser_t *parser, uint8_t rule_id) {
     mp_parse_node_t pn;
     mp_lexer_t *lex = parser->lexer;
     if (lex->tok_kind == MP_TOKEN_NAME) {
@@ -378,10 +475,10 @@ STATIC void push_result_token(parser_t *parser, const rule_t *rule) {
         #if MICROPY_COMP_CONST
         // if name is a standalone identifier, look it up in the table of dynamic constants
         mp_map_elem_t *elem;
-        if (rule->rule_id == RULE_atom
+        if (rule_id == RULE_atom
             && (elem = mp_map_lookup(&parser->consts, MP_OBJ_NEW_QSTR(id), MP_MAP_LOOKUP)) != NULL) {
             if (MP_OBJ_IS_SMALL_INT(elem->value)) {
-                pn = mp_parse_node_new_small_int(MP_OBJ_SMALL_INT_VALUE(elem->value));
+                pn = mp_parse_node_new_small_int_checked(parser, elem->value);
             } else {
                 pn = make_node_const_object(parser, lex->tok_line, elem->value);
             }
@@ -389,13 +486,13 @@ STATIC void push_result_token(parser_t *parser, const rule_t *rule) {
             pn = mp_parse_node_new_leaf(MP_PARSE_NODE_ID, id);
         }
         #else
-        (void)rule;
+        (void)rule_id;
         pn = mp_parse_node_new_leaf(MP_PARSE_NODE_ID, id);
         #endif
     } else if (lex->tok_kind == MP_TOKEN_INTEGER) {
         mp_obj_t o = mp_parse_num_integer(lex->vstr.buf, lex->vstr.len, 0, lex);
         if (MP_OBJ_IS_SMALL_INT(o)) {
-            pn = mp_parse_node_new_small_int(MP_OBJ_SMALL_INT_VALUE(o));
+            pn = mp_parse_node_new_small_int_checked(parser, o);
         } else {
             pn = make_node_const_object(parser, lex->tok_line, o);
         }
@@ -418,7 +515,7 @@ STATIC void push_result_token(parser_t *parser, const rule_t *rule) {
             pn = mp_parse_node_new_leaf(lex->tok_kind == MP_TOKEN_STRING ? MP_PARSE_NODE_STRING : MP_PARSE_NODE_BYTES, qst);
         } else {
             // not interned, make a node holding a pointer to the string/bytes object
-            mp_obj_t o = mp_obj_new_str_of_type(
+            mp_obj_t o = mp_obj_new_str_copy(
                 lex->tok_kind == MP_TOKEN_STRING ? &mp_type_str : &mp_type_bytes,
                 (const byte*)lex->vstr.buf, lex->vstr.len);
             pn = make_node_const_object(parser, lex->tok_line, o);
@@ -443,12 +540,12 @@ STATIC const mp_rom_map_elem_t mp_constants_table[] = {
 STATIC MP_DEFINE_CONST_MAP(mp_constants_map, mp_constants_table);
 #endif
 
-STATIC void push_result_rule(parser_t *parser, size_t src_line, const rule_t *rule, size_t num_args);
+STATIC void push_result_rule(parser_t *parser, size_t src_line, uint8_t rule_id, size_t num_args);
 
 #if MICROPY_COMP_CONST_FOLDING
-STATIC bool fold_logical_constants(parser_t *parser, const rule_t *rule, size_t *num_args) {
-    if (rule->rule_id == RULE_or_test
-        || rule->rule_id == RULE_and_test) {
+STATIC bool fold_logical_constants(parser_t *parser, uint8_t rule_id, size_t *num_args) {
+    if (rule_id == RULE_or_test
+        || rule_id == RULE_and_test) {
         // folding for binary logical ops: or and
         size_t copy_to = *num_args;
         for (size_t i = copy_to; i > 0;) {
@@ -458,7 +555,7 @@ STATIC bool fold_logical_constants(parser_t *parser, const rule_t *rule, size_t
                 // always need to keep the last value
                 break;
             }
-            if (rule->rule_id == RULE_or_test) {
+            if (rule_id == RULE_or_test) {
                 if (mp_parse_node_is_const_true(pn)) {
                     //
                     break;
@@ -485,7 +582,7 @@ STATIC bool fold_logical_constants(parser_t *parser, const rule_t *rule, size_t
         // we did a complete folding if there's only 1 arg left
         return *num_args == 1;
 
-    } else if (rule->rule_id == RULE_not_test_2) {
+    } else if (rule_id == RULE_not_test_2) {
         // folding for unary logical op: not
         mp_parse_node_t pn = peek_result(parser, 0);
         if (mp_parse_node_is_const_false(pn)) {
@@ -503,23 +600,23 @@ STATIC bool fold_logical_constants(parser_t *parser, const rule_t *rule, size_t
     return false;
 }
 
-STATIC bool fold_constants(parser_t *parser, const rule_t *rule, size_t num_args) {
+STATIC bool fold_constants(parser_t *parser, uint8_t rule_id, size_t num_args) {
     // this code does folding of arbitrary integer expressions, eg 1 + 2 * 3 + 4
     // it does not do partial folding, eg 1 + 2 + x -> 3 + x
 
     mp_obj_t arg0;
-    if (rule->rule_id == RULE_expr
-        || rule->rule_id == RULE_xor_expr
-        || rule->rule_id == RULE_and_expr) {
+    if (rule_id == RULE_expr
+        || rule_id == RULE_xor_expr
+        || rule_id == RULE_and_expr) {
         // folding for binary ops: | ^ &
         mp_parse_node_t pn = peek_result(parser, num_args - 1);
         if (!mp_parse_node_get_int_maybe(pn, &arg0)) {
             return false;
         }
         mp_binary_op_t op;
-        if (rule->rule_id == RULE_expr) {
+        if (rule_id == RULE_expr) {
             op = MP_BINARY_OP_OR;
-        } else if (rule->rule_id == RULE_xor_expr) {
+        } else if (rule_id == RULE_xor_expr) {
             op = MP_BINARY_OP_XOR;
         } else {
             op = MP_BINARY_OP_AND;
@@ -532,9 +629,9 @@ STATIC bool fold_constants(parser_t *parser, const rule_t *rule, size_t num_args
             }
             arg0 = mp_binary_op(op, arg0, arg1);
         }
-    } else if (rule->rule_id == RULE_shift_expr
-        || rule->rule_id == RULE_arith_expr
-        || rule->rule_id == RULE_term) {
+    } else if (rule_id == RULE_shift_expr
+        || rule_id == RULE_arith_expr
+        || rule_id == RULE_term) {
         // folding for binary ops: << >> + - * / % //
         mp_parse_node_t pn = peek_result(parser, num_args - 1);
         if (!mp_parse_node_get_int_maybe(pn, &arg0)) {
@@ -578,7 +675,7 @@ STATIC bool fold_constants(parser_t *parser, const rule_t *rule, size_t num_args
             }
             arg0 = mp_binary_op(op, arg0, arg1);
         }
-    } else if (rule->rule_id == RULE_factor_2) {
+    } else if (rule_id == RULE_factor_2) {
         // folding for unary ops: + - ~
         mp_parse_node_t pn = peek_result(parser, 0);
         if (!mp_parse_node_get_int_maybe(pn, &arg0)) {
@@ -597,7 +694,7 @@ STATIC bool fold_constants(parser_t *parser, const rule_t *rule, size_t num_args
         arg0 = mp_unary_op(op, arg0);
 
     #if MICROPY_COMP_CONST
-    } else if (rule->rule_id == RULE_expr_stmt) {
+    } else if (rule_id == RULE_expr_stmt) {
         mp_parse_node_t pn1 = peek_result(parser, 0);
         if (!MP_PARSE_NODE_IS_NULL(pn1)
             && !(MP_PARSE_NODE_IS_STRUCT_KIND(pn1, RULE_expr_stmt_augassign)
@@ -636,7 +733,7 @@ STATIC bool fold_constants(parser_t *parser, const rule_t *rule, size_t num_args
                 if (qstr_str(id)[0] == '_') {
                     pop_result(parser); // pop const(value)
                     pop_result(parser); // pop id
-                    push_result_rule(parser, 0, rules[RULE_pass_stmt], 0); // replace with "pass"
+                    push_result_rule(parser, 0, RULE_pass_stmt, 0); // replace with "pass"
                     return true;
                 }
 
@@ -652,7 +749,7 @@ STATIC bool fold_constants(parser_t *parser, const rule_t *rule, size_t num_args
     #endif
 
     #if MICROPY_COMP_MODULE_CONST
-    } else if (rule->rule_id == RULE_atom_expr_normal) {
+    } else if (rule_id == RULE_atom_expr_normal) {
         mp_parse_node_t pn0 = peek_result(parser, 1);
         mp_parse_node_t pn1 = peek_result(parser, 0);
         if (!(MP_PARSE_NODE_IS_ID(pn0)
@@ -687,7 +784,7 @@ STATIC bool fold_constants(parser_t *parser, const rule_t *rule, size_t num_args
         pop_result(parser);
     }
     if (MP_OBJ_IS_SMALL_INT(arg0)) {
-        push_result_node(parser, mp_parse_node_new_small_int(MP_OBJ_SMALL_INT_VALUE(arg0)));
+        push_result_node(parser, mp_parse_node_new_small_int_checked(parser, arg0));
     } else {
         // TODO reuse memory for parse node struct?
         push_result_node(parser, make_node_const_object(parser, 0, arg0));
@@ -697,9 +794,9 @@ STATIC bool fold_constants(parser_t *parser, const rule_t *rule, size_t num_args
 }
 #endif
 
-STATIC void push_result_rule(parser_t *parser, size_t src_line, const rule_t *rule, size_t num_args) {
+STATIC void push_result_rule(parser_t *parser, size_t src_line, uint8_t rule_id, size_t num_args) {
     // optimise away parenthesis around an expression if possible
-    if (rule->rule_id == RULE_atom_paren) {
+    if (rule_id == RULE_atom_paren) {
         // there should be just 1 arg for this rule
         mp_parse_node_t pn = peek_result(parser, 0);
         if (MP_PARSE_NODE_IS_NULL(pn)) {
@@ -713,11 +810,11 @@ STATIC void push_result_rule(parser_t *parser, size_t src_line, const rule_t *ru
     }
 
     #if MICROPY_COMP_CONST_FOLDING
-    if (fold_logical_constants(parser, rule, &num_args)) {
+    if (fold_logical_constants(parser, rule_id, &num_args)) {
         // we folded this rule so return straight away
         return;
     }
-    if (fold_constants(parser, rule, num_args)) {
+    if (fold_constants(parser, rule_id, num_args)) {
         // we folded this rule so return straight away
         return;
     }
@@ -725,7 +822,7 @@ STATIC void push_result_rule(parser_t *parser, size_t src_line, const rule_t *ru
 
     mp_parse_node_struct_t *pn = parser_alloc(parser, sizeof(mp_parse_node_struct_t) + sizeof(mp_parse_node_t) * num_args);
     pn->source_line = src_line;
-    pn->kind_num_nodes = (rule->rule_id & 0xff) | (num_args << 8);
+    pn->kind_num_nodes = (rule_id & 0xff) | (num_args << 8);
     for (size_t i = num_args; i > 0; i--) {
         pn->nodes[i - 1] = pop_result(parser);
     }
@@ -762,14 +859,11 @@ mp_parse_tree_t mp_parse(mp_lexer_t *lex, mp_parse_input_kind_t input_kind) {
         case MP_PARSE_EVAL_INPUT: top_level_rule = RULE_eval_input; break;
         default: top_level_rule = RULE_file_input;
     }
-    push_rule(&parser, lex->tok_line, rules[top_level_rule], 0);
+    push_rule(&parser, lex->tok_line, top_level_rule, 0);
 
     // parse!
 
-    size_t n, i; // state for the current rule
-    size_t rule_src_line; // source line for the first token matched by the current rule
     bool backtrack = false;
-    const rule_t *rule = NULL;
 
     for (;;) {
         next_rule:
@@ -777,19 +871,24 @@ mp_parse_tree_t mp_parse(mp_lexer_t *lex, mp_parse_input_kind_t input_kind) {
             break;
         }
 
-        pop_rule(&parser, &rule, &i, &rule_src_line);
-        n = rule->act & RULE_ACT_ARG_MASK;
+        // Pop the next rule to process it
+        size_t i; // state for the current rule
+        size_t rule_src_line; // source line for the first token matched by the current rule
+        uint8_t rule_id = pop_rule(&parser, &i, &rule_src_line);
+        uint8_t rule_act = rule_act_table[rule_id];
+        const uint16_t *rule_arg = get_rule_arg(rule_id);
+        size_t n = rule_act & RULE_ACT_ARG_MASK;
 
-        /*
+        #if 0
         // debugging
-        printf("depth=%d ", parser.rule_stack_top);
+        printf("depth=" UINT_FMT " ", parser.rule_stack_top);
         for (int j = 0; j < parser.rule_stack_top; ++j) {
             printf(" ");
         }
-        printf("%s n=%d i=%d bt=%d\n", rule->rule_name, n, i, backtrack);
-        */
+        printf("%s n=" UINT_FMT " i=" UINT_FMT " bt=%d\n", rule_name_table[rule_id], n, i, backtrack);
+        #endif
 
-        switch (rule->act & RULE_ACT_KIND_MASK) {
+        switch (rule_act & RULE_ACT_KIND_MASK) {
             case RULE_ACT_OR:
                 if (i > 0 && !backtrack) {
                     goto next_rule;
@@ -797,19 +896,19 @@ mp_parse_tree_t mp_parse(mp_lexer_t *lex, mp_parse_input_kind_t input_kind) {
                     backtrack = false;
                 }
                 for (; i < n; ++i) {
-                    uint16_t kind = rule->arg[i] & RULE_ARG_KIND_MASK;
+                    uint16_t kind = rule_arg[i] & RULE_ARG_KIND_MASK;
                     if (kind == RULE_ARG_TOK) {
-                        if (lex->tok_kind == (rule->arg[i] & RULE_ARG_ARG_MASK)) {
-                            push_result_token(&parser, rule);
+                        if (lex->tok_kind == (rule_arg[i] & RULE_ARG_ARG_MASK)) {
+                            push_result_token(&parser, rule_id);
                             mp_lexer_to_next(lex);
                             goto next_rule;
                         }
                     } else {
                         assert(kind == RULE_ARG_RULE);
                         if (i + 1 < n) {
-                            push_rule(&parser, rule_src_line, rule, i + 1); // save this or-rule
+                            push_rule(&parser, rule_src_line, rule_id, i + 1); // save this or-rule
                         }
-                        push_rule_from_arg(&parser, rule->arg[i]); // push child of or-rule
+                        push_rule_from_arg(&parser, rule_arg[i]); // push child of or-rule
                         goto next_rule;
                     }
                 }
@@ -821,7 +920,7 @@ mp_parse_tree_t mp_parse(mp_lexer_t *lex, mp_parse_input_kind_t input_kind) {
                 // failed, backtrack if we can, else syntax error
                 if (backtrack) {
                     assert(i > 0);
-                    if ((rule->arg[i - 1] & RULE_ARG_KIND_MASK) == RULE_ARG_OPT_RULE) {
+                    if ((rule_arg[i - 1] & RULE_ARG_KIND_MASK) == RULE_ARG_OPT_RULE) {
                         // an optional rule that failed, so continue with next arg
                         push_result_node(&parser, MP_PARSE_NODE_NULL);
                         backtrack = false;
@@ -838,13 +937,13 @@ mp_parse_tree_t mp_parse(mp_lexer_t *lex, mp_parse_input_kind_t input_kind) {
 
                 // progress through the rule
                 for (; i < n; ++i) {
-                    if ((rule->arg[i] & RULE_ARG_KIND_MASK) == RULE_ARG_TOK) {
+                    if ((rule_arg[i] & RULE_ARG_KIND_MASK) == RULE_ARG_TOK) {
                         // need to match a token
-                        mp_token_kind_t tok_kind = rule->arg[i] & RULE_ARG_ARG_MASK;
+                        mp_token_kind_t tok_kind = rule_arg[i] & RULE_ARG_ARG_MASK;
                         if (lex->tok_kind == tok_kind) {
                             // matched token
                             if (tok_kind == MP_TOKEN_NAME) {
-                                push_result_token(&parser, rule);
+                                push_result_token(&parser, rule_id);
                             }
                             mp_lexer_to_next(lex);
                         } else {
@@ -859,8 +958,8 @@ mp_parse_tree_t mp_parse(mp_lexer_t *lex, mp_parse_input_kind_t input_kind) {
                             }
                         }
                     } else {
-                        push_rule(&parser, rule_src_line, rule, i + 1); // save this and-rule
-                        push_rule_from_arg(&parser, rule->arg[i]); // push child of and-rule
+                        push_rule(&parser, rule_src_line, rule_id, i + 1); // save this and-rule
+                        push_rule_from_arg(&parser, rule_arg[i]); // push child of and-rule
                         goto next_rule;
                     }
                 }
@@ -871,7 +970,7 @@ mp_parse_tree_t mp_parse(mp_lexer_t *lex, mp_parse_input_kind_t input_kind) {
 
                 #if !MICROPY_ENABLE_DOC_STRING
                 // this code discards lonely statements, such as doc strings
-                if (input_kind != MP_PARSE_SINGLE_INPUT && rule->rule_id == RULE_expr_stmt && peek_result(&parser, 0) == MP_PARSE_NODE_NULL) {
+                if (input_kind != MP_PARSE_SINGLE_INPUT && rule_id == RULE_expr_stmt && peek_result(&parser, 0) == MP_PARSE_NODE_NULL) {
                     mp_parse_node_t p = peek_result(&parser, 1);
                     if ((MP_PARSE_NODE_IS_LEAF(p) && !MP_PARSE_NODE_IS_ID(p))
                         || MP_PARSE_NODE_IS_STRUCT_KIND(p, RULE_const_object)) {
@@ -880,7 +979,7 @@ mp_parse_tree_t mp_parse(mp_lexer_t *lex, mp_parse_input_kind_t input_kind) {
                         // Pushing the "pass" rule here will overwrite any RULE_const_object
                         // entry that was on the result stack, allowing the GC to reclaim
                         // the memory from the const object when needed.
-                        push_result_rule(&parser, rule_src_line, rules[RULE_pass_stmt], 0);
+                        push_result_rule(&parser, rule_src_line, RULE_pass_stmt, 0);
                         break;
                     }
                 }
@@ -891,8 +990,8 @@ mp_parse_tree_t mp_parse(mp_lexer_t *lex, mp_parse_input_kind_t input_kind) {
                 size_t num_not_nil = 0;
                 for (size_t x = n; x > 0;) {
                     --x;
-                    if ((rule->arg[x] & RULE_ARG_KIND_MASK) == RULE_ARG_TOK) {
-                        mp_token_kind_t tok_kind = rule->arg[x] & RULE_ARG_ARG_MASK;
+                    if ((rule_arg[x] & RULE_ARG_KIND_MASK) == RULE_ARG_TOK) {
+                        mp_token_kind_t tok_kind = rule_arg[x] & RULE_ARG_ARG_MASK;
                         if (tok_kind == MP_TOKEN_NAME) {
                             // only tokens which were names are pushed to stack
                             i += 1;
@@ -907,7 +1006,7 @@ mp_parse_tree_t mp_parse(mp_lexer_t *lex, mp_parse_input_kind_t input_kind) {
                     }
                 }
 
-                if (num_not_nil == 1 && (rule->act & RULE_ACT_ALLOW_IDENT)) {
+                if (num_not_nil == 1 && (rule_act & RULE_ACT_ALLOW_IDENT)) {
                     // this rule has only 1 argument and should not be emitted
                     mp_parse_node_t pn = MP_PARSE_NODE_NULL;
                     for (size_t x = 0; x < i; ++x) {
@@ -920,19 +1019,19 @@ mp_parse_tree_t mp_parse(mp_lexer_t *lex, mp_parse_input_kind_t input_kind) {
                 } else {
                     // this rule must be emitted
 
-                    if (rule->act & RULE_ACT_ADD_BLANK) {
+                    if (rule_act & RULE_ACT_ADD_BLANK) {
                         // and add an extra blank node at the end (used by the compiler to store data)
                         push_result_node(&parser, MP_PARSE_NODE_NULL);
                         i += 1;
                     }
 
-                    push_result_rule(&parser, rule_src_line, rule, i);
+                    push_result_rule(&parser, rule_src_line, rule_id, i);
                 }
                 break;
             }
 
             default: {
-                assert((rule->act & RULE_ACT_KIND_MASK) == RULE_ACT_LIST);
+                assert((rule_act & RULE_ACT_KIND_MASK) == RULE_ACT_LIST);
 
                 // n=2 is: item item*
                 // n=1 is: item (sep item)*
@@ -970,13 +1069,13 @@ mp_parse_tree_t mp_parse(mp_lexer_t *lex, mp_parse_input_kind_t input_kind) {
                     }
                 } else {
                     for (;;) {
-                        size_t arg = rule->arg[i & 1 & n];
+                        size_t arg = rule_arg[i & 1 & n];
                         if ((arg & RULE_ARG_KIND_MASK) == RULE_ARG_TOK) {
                             if (lex->tok_kind == (arg & RULE_ARG_ARG_MASK)) {
                                 if (i & 1 & n) {
                                     // separators which are tokens are not pushed to result stack
                                 } else {
-                                    push_result_token(&parser, rule);
+                                    push_result_token(&parser, rule_id);
                                 }
                                 mp_lexer_to_next(lex);
                                 // got element of list, so continue parsing list
@@ -989,7 +1088,7 @@ mp_parse_tree_t mp_parse(mp_lexer_t *lex, mp_parse_input_kind_t input_kind) {
                             }
                         } else {
                             assert((arg & RULE_ARG_KIND_MASK) == RULE_ARG_RULE);
-                            push_rule(&parser, rule_src_line, rule, i + 1); // save this list-rule
+                            push_rule(&parser, rule_src_line, rule_id, i + 1); // save this list-rule
                             push_rule_from_arg(&parser, arg); // push child of list-rule
                             goto next_rule;
                         }
@@ -999,7 +1098,7 @@ mp_parse_tree_t mp_parse(mp_lexer_t *lex, mp_parse_input_kind_t input_kind) {
 
                 // compute number of elements in list, result in i
                 i -= 1;
-                if ((n & 1) && (rule->arg[1] & RULE_ARG_KIND_MASK) == RULE_ARG_TOK) {
+                if ((n & 1) && (rule_arg[1] & RULE_ARG_KIND_MASK) == RULE_ARG_TOK) {
                     // don't count separators when they are tokens
                     i = (i + 1) / 2;
                 }
@@ -1008,12 +1107,12 @@ mp_parse_tree_t mp_parse(mp_lexer_t *lex, mp_parse_input_kind_t input_kind) {
                     // list matched single item
                     if (had_trailing_sep) {
                         // if there was a trailing separator, make a list of a single item
-                        push_result_rule(&parser, rule_src_line, rule, i);
+                        push_result_rule(&parser, rule_src_line, rule_id, i);
                     } else {
                         // just leave single item on stack (ie don't wrap in a list)
                     }
                 } else {
-                    push_result_rule(&parser, rule_src_line, rule, i);
+                    push_result_rule(&parser, rule_src_line, rule_id, i);
                 }
                 break;
             }

+ 21 - 6
py/parsenum.c

@@ -170,6 +170,14 @@ typedef enum {
 
 mp_obj_t mp_parse_num_decimal(const char *str, size_t len, bool allow_imag, bool force_complex, mp_lexer_t *lex) {
 #if MICROPY_PY_BUILTINS_FLOAT
+
+// DEC_VAL_MAX only needs to be rough and is used to retain precision while not overflowing
+#if MICROPY_FLOAT_IMPL == MICROPY_FLOAT_IMPL_FLOAT
+#define DEC_VAL_MAX 1e20F
+#elif MICROPY_FLOAT_IMPL == MICROPY_FLOAT_IMPL_DOUBLE
+#define DEC_VAL_MAX 1e200
+#endif
+
     const char *top = str + len;
     mp_float_t dec_val = 0;
     bool dec_neg = false;
@@ -214,8 +222,8 @@ mp_obj_t mp_parse_num_decimal(const char *str, size_t len, bool allow_imag, bool
         // string should be a decimal number
         parse_dec_in_t in = PARSE_DEC_IN_INTG;
         bool exp_neg = false;
-        mp_float_t frac_mult = 0.1;
         mp_int_t exp_val = 0;
+        mp_int_t exp_extra = 0;
         while (str < top) {
             mp_uint_t dig = *str++;
             if ('0' <= dig && dig <= '9') {
@@ -223,11 +231,18 @@ mp_obj_t mp_parse_num_decimal(const char *str, size_t len, bool allow_imag, bool
                 if (in == PARSE_DEC_IN_EXP) {
                     exp_val = 10 * exp_val + dig;
                 } else {
-                    if (in == PARSE_DEC_IN_FRAC) {
-                        dec_val += dig * frac_mult;
-                        frac_mult *= MICROPY_FLOAT_CONST(0.1);
-                    } else {
+                    if (dec_val < DEC_VAL_MAX) {
+                        // dec_val won't overflow so keep accumulating
                         dec_val = 10 * dec_val + dig;
+                        if (in == PARSE_DEC_IN_FRAC) {
+                            --exp_extra;
+                        }
+                    } else {
+                        // dec_val might overflow and we anyway can't represent more digits
+                        // of precision, so ignore the digit and just adjust the exponent
+                        if (in == PARSE_DEC_IN_INTG) {
+                            ++exp_extra;
+                        }
                     }
                 }
             } else if (in == PARSE_DEC_IN_INTG && dig == '.') {
@@ -261,7 +276,7 @@ mp_obj_t mp_parse_num_decimal(const char *str, size_t len, bool allow_imag, bool
         }
 
         // apply the exponent
-        dec_val *= MICROPY_FLOAT_C_FUN(pow)(10, exp_val);
+        dec_val *= MICROPY_FLOAT_C_FUN(pow)(10, exp_val + exp_extra);
     }
 
     // negate value if needed

+ 2 - 0
py/py.mk

@@ -103,6 +103,7 @@ endif
 # py object files
 PY_O_BASENAME = \
 	mpstate.o \
+	nlr.o \
 	nlrx86.o \
 	nlrx64.o \
 	nlrthumb.o \
@@ -110,6 +111,7 @@ PY_O_BASENAME = \
 	nlrsetjmp.o \
 	malloc.o \
 	gc.o \
+	pystack.o \
 	qstr.o \
 	vstr.o \
 	mpprint.o \

+ 5 - 7
py/qstr.c

@@ -127,14 +127,12 @@ void qstr_init(void) {
 
 STATIC const byte *find_qstr(qstr q) {
     // search pool for this qstr
-    for (qstr_pool_t *pool = MP_STATE_VM(last_pool); pool != NULL; pool = pool->prev) {
-        if (q >= pool->total_prev_len) {
-            return pool->qstrs[q - pool->total_prev_len];
-        }
+    // total_prev_len==0 in the final pool, so the loop will always terminate
+    qstr_pool_t *pool = MP_STATE_VM(last_pool);
+    while (q < pool->total_prev_len) {
+        pool = pool->prev;
     }
-
-    // not found
-    return 0;
+    return pool->qstrs[q - pool->total_prev_len];
 }
 
 // qstr_mutex must be taken while in this function

+ 1 - 0
py/qstrdefs.h

@@ -40,6 +40,7 @@ Q(/)
 Q(%#o)
 Q(%#x)
 Q({:#b})
+Q( )
 Q(\n)
 Q(maximum recursion depth exceeded)
 Q(<module>)

+ 40 - 45
py/runtime.c

@@ -214,7 +214,7 @@ void mp_delete_global(qstr qst) {
 }
 
 mp_obj_t mp_unary_op(mp_unary_op_t op, mp_obj_t arg) {
-    DEBUG_OP_printf("unary " UINT_FMT " %p\n", op, arg);
+    DEBUG_OP_printf("unary " UINT_FMT " %q %p\n", op, mp_unary_op_method_name[op], arg);
 
     if (op == MP_UNARY_OP_NOT) {
         // "not x" is the negative of whether "x" is true per Python semantics
@@ -275,7 +275,7 @@ mp_obj_t mp_unary_op(mp_unary_op_t op, mp_obj_t arg) {
 }
 
 mp_obj_t mp_binary_op(mp_binary_op_t op, mp_obj_t lhs, mp_obj_t rhs) {
-    DEBUG_OP_printf("binary " UINT_FMT " %p %p\n", op, lhs, rhs);
+    DEBUG_OP_printf("binary " UINT_FMT " %q %p %p\n", op, mp_binary_op_method_name[op], lhs, rhs);
 
     // TODO correctly distinguish inplace operators for mutable objects
     // lookup logic that CPython uses for +=:
@@ -413,7 +413,6 @@ mp_obj_t mp_binary_op(mp_binary_op_t op, mp_obj_t lhs, mp_obj_t rhs) {
                         // use standard precision
                         return MP_OBJ_NEW_SMALL_INT(lhs_val * rhs_val);
                     }
-                    break;
                 }
                 case MP_BINARY_OP_FLOOR_DIVIDE:
                 case MP_BINARY_OP_INPLACE_FLOOR_DIVIDE:
@@ -488,10 +487,10 @@ mp_obj_t mp_binary_op(mp_binary_op_t op, mp_obj_t lhs, mp_obj_t rhs) {
                     return MP_OBJ_FROM_PTR(tuple);
                 }
 
-                case MP_BINARY_OP_LESS: return mp_obj_new_bool(lhs_val < rhs_val); break;
-                case MP_BINARY_OP_MORE: return mp_obj_new_bool(lhs_val > rhs_val); break;
-                case MP_BINARY_OP_LESS_EQUAL: return mp_obj_new_bool(lhs_val <= rhs_val); break;
-                case MP_BINARY_OP_MORE_EQUAL: return mp_obj_new_bool(lhs_val >= rhs_val); break;
+                case MP_BINARY_OP_LESS: return mp_obj_new_bool(lhs_val < rhs_val);
+                case MP_BINARY_OP_MORE: return mp_obj_new_bool(lhs_val > rhs_val);
+                case MP_BINARY_OP_LESS_EQUAL: return mp_obj_new_bool(lhs_val <= rhs_val);
+                case MP_BINARY_OP_MORE_EQUAL: return mp_obj_new_bool(lhs_val >= rhs_val);
 
                 default:
                     goto unsupported_op;
@@ -523,38 +522,12 @@ mp_obj_t mp_binary_op(mp_binary_op_t op, mp_obj_t lhs, mp_obj_t rhs) {
         }
     }
 
-    /* deal with `in`
-     *
-     * NOTE `a in b` is `b.__contains__(a)`, hence why the generic dispatch
-     * needs to go below with swapped arguments
-     */
+    // Convert MP_BINARY_OP_IN to MP_BINARY_OP_CONTAINS with swapped args.
     if (op == MP_BINARY_OP_IN) {
-        mp_obj_type_t *type = mp_obj_get_type(rhs);
-        if (type->binary_op != NULL) {
-            mp_obj_t res = type->binary_op(op, rhs, lhs);
-            if (res != MP_OBJ_NULL) {
-                return res;
-            }
-        }
-        if (type->getiter != NULL) {
-            /* second attempt, walk the iterator */
-            mp_obj_iter_buf_t iter_buf;
-            mp_obj_t iter = mp_getiter(rhs, &iter_buf);
-            mp_obj_t next;
-            while ((next = mp_iternext(iter)) != MP_OBJ_STOP_ITERATION) {
-                if (mp_obj_equal(next, lhs)) {
-                    return mp_const_true;
-                }
-            }
-            return mp_const_false;
-        }
-
-        if (MICROPY_ERROR_REPORTING == MICROPY_ERROR_REPORTING_TERSE) {
-            mp_raise_TypeError("object not iterable");
-        } else {
-            nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_TypeError,
-                "'%s' object is not iterable", mp_obj_get_type_str(rhs)));
-        }
+        op = MP_BINARY_OP_CONTAINS;
+        mp_obj_t temp = lhs;
+        lhs = rhs;
+        rhs = temp;
     }
 
     // generic binary_op supplied by type
@@ -583,6 +556,20 @@ generic_binary_op:
     }
 #endif
 
+    if (op == MP_BINARY_OP_CONTAINS) {
+        // If type didn't support containment then explicitly walk the iterator.
+        // mp_getiter will raise the appropriate exception if lhs is not iterable.
+        mp_obj_iter_buf_t iter_buf;
+        mp_obj_t iter = mp_getiter(lhs, &iter_buf);
+        mp_obj_t next;
+        while ((next = mp_iternext(iter)) != MP_OBJ_STOP_ITERATION) {
+            if (mp_obj_equal(next, rhs)) {
+                return mp_const_true;
+            }
+        }
+        return mp_const_false;
+    }
+
 unsupported_op:
     if (MICROPY_ERROR_REPORTING == MICROPY_ERROR_REPORTING_TERSE) {
         mp_raise_TypeError("unsupported type for operator");
@@ -681,7 +668,7 @@ void mp_call_prepare_args_n_kw_var(bool have_self, size_t n_args_n_kw, const mp_
 
         // allocate memory for the new array of args
         args2_alloc = 1 + n_args + 2 * (n_kw + kw_dict_len);
-        args2 = m_new(mp_obj_t, args2_alloc);
+        args2 = mp_nonlocal_alloc(args2_alloc * sizeof(mp_obj_t));
 
         // copy the self
         if (self != MP_OBJ_NULL) {
@@ -702,7 +689,7 @@ void mp_call_prepare_args_n_kw_var(bool have_self, size_t n_args_n_kw, const mp_
 
         // allocate memory for the new array of args
         args2_alloc = 1 + n_args + len + 2 * (n_kw + kw_dict_len);
-        args2 = m_new(mp_obj_t, args2_alloc);
+        args2 = mp_nonlocal_alloc(args2_alloc * sizeof(mp_obj_t));
 
         // copy the self
         if (self != MP_OBJ_NULL) {
@@ -718,7 +705,7 @@ void mp_call_prepare_args_n_kw_var(bool have_self, size_t n_args_n_kw, const mp_
 
         // allocate memory for the new array of args
         args2_alloc = 1 + n_args + 2 * (n_kw + kw_dict_len) + 3;
-        args2 = m_new(mp_obj_t, args2_alloc);
+        args2 = mp_nonlocal_alloc(args2_alloc * sizeof(mp_obj_t));
 
         // copy the self
         if (self != MP_OBJ_NULL) {
@@ -735,7 +722,7 @@ void mp_call_prepare_args_n_kw_var(bool have_self, size_t n_args_n_kw, const mp_
         mp_obj_t item;
         while ((item = mp_iternext(iterable)) != MP_OBJ_STOP_ITERATION) {
             if (args2_len >= args2_alloc) {
-                args2 = m_renew(mp_obj_t, args2, args2_alloc, args2_alloc * 2);
+                args2 = mp_nonlocal_realloc(args2, args2_alloc * sizeof(mp_obj_t), args2_alloc * 2 * sizeof(mp_obj_t));
                 args2_alloc *= 2;
             }
             args2[args2_len++] = item;
@@ -786,7 +773,7 @@ void mp_call_prepare_args_n_kw_var(bool have_self, size_t n_args_n_kw, const mp_
                 if (new_alloc < 4) {
                     new_alloc = 4;
                 }
-                args2 = m_renew(mp_obj_t, args2, args2_alloc, new_alloc);
+                args2 = mp_nonlocal_realloc(args2, args2_alloc * sizeof(mp_obj_t), new_alloc * sizeof(mp_obj_t));
                 args2_alloc = new_alloc;
             }
 
@@ -818,7 +805,7 @@ mp_obj_t mp_call_method_n_kw_var(bool have_self, size_t n_args_n_kw, const mp_ob
     mp_call_prepare_args_n_kw_var(have_self, n_args_n_kw, args, &out_args);
 
     mp_obj_t res = mp_call_function_n_kw(out_args.fun, out_args.n_args, out_args.n_kw, out_args.args);
-    m_del(mp_obj_t, out_args.args, out_args.n_alloc);
+    mp_nonlocal_free(out_args.args, out_args.n_alloc * sizeof(mp_obj_t));
 
     return res;
 }
@@ -1360,11 +1347,12 @@ import_error:
     const char *pkg_name = mp_obj_str_get_data(dest[0], &pkg_name_len);
 
     const uint dot_name_len = pkg_name_len + 1 + qstr_len(name);
-    char *dot_name = alloca(dot_name_len);
+    char *dot_name = mp_local_alloc(dot_name_len);
     memcpy(dot_name, pkg_name, pkg_name_len);
     dot_name[pkg_name_len] = '.';
     memcpy(dot_name + pkg_name_len + 1, qstr_str(name), qstr_len(name));
     qstr dot_name_q = qstr_from_strn(dot_name, dot_name_len);
+    mp_local_free(dot_name);
 
     mp_obj_t args[5];
     args[0] = MP_OBJ_NEW_QSTR(dot_name_q);
@@ -1468,3 +1456,10 @@ NORETURN void mp_raise_OSError(int errno_) {
 NORETURN void mp_raise_NotImplementedError(const char *msg) {
     mp_raise_msg(&mp_type_NotImplementedError, msg);
 }
+
+#if MICROPY_STACK_CHECK || MICROPY_ENABLE_PYSTACK
+NORETURN void mp_raise_recursion_depth(void) {
+    nlr_raise(mp_obj_new_exception_arg1(&mp_type_RuntimeError,
+        MP_OBJ_NEW_QSTR(MP_QSTR_maximum_space_recursion_space_depth_space_exceeded)));
+}
+#endif

+ 5 - 3
py/runtime.h

@@ -27,6 +27,7 @@
 #define MICROPY_INCLUDED_PY_RUNTIME_H
 
 #include "py/mpstate.h"
+#include "py/pystack.h"
 
 typedef enum {
     MP_VM_RETURN_NORMAL,
@@ -106,8 +107,9 @@ mp_obj_t mp_call_method_n_kw(size_t n_args, size_t n_kw, const mp_obj_t *args);
 mp_obj_t mp_call_method_n_kw_var(bool have_self, size_t n_args_n_kw, const mp_obj_t *args);
 mp_obj_t mp_call_method_self_n_kw(mp_obj_t meth, mp_obj_t self, size_t n_args, size_t n_kw, const mp_obj_t *args);
 // Call function and catch/dump exception - for Python callbacks from C code
-void mp_call_function_1_protected(mp_obj_t fun, mp_obj_t arg);
-void mp_call_function_2_protected(mp_obj_t fun, mp_obj_t arg1, mp_obj_t arg2);
+// (return MP_OBJ_NULL in case of exception).
+mp_obj_t mp_call_function_1_protected(mp_obj_t fun, mp_obj_t arg);
+mp_obj_t mp_call_function_2_protected(mp_obj_t fun, mp_obj_t arg1, mp_obj_t arg2);
 
 typedef struct _mp_call_args_t {
     mp_obj_t fun;
@@ -150,7 +152,7 @@ NORETURN void mp_raise_ValueError(const char *msg);
 NORETURN void mp_raise_TypeError(const char *msg);
 NORETURN void mp_raise_NotImplementedError(const char *msg);
 NORETURN void mp_raise_OSError(int errno_);
-NORETURN void mp_exc_recursion_depth(void);
+NORETURN void mp_raise_recursion_depth(void);
 
 #if MICROPY_BUILTIN_METHOD_CHECK_SELF_ARG
 #undef mp_check_self

+ 4 - 0
py/runtime0.h

@@ -131,6 +131,10 @@ typedef enum {
         #endif
     ,
 
+    // The runtime will convert MP_BINARY_OP_IN to this operator with swapped args.
+    // A type should implement this containment operator instead of MP_BINARY_OP_IN.
+    MP_BINARY_OP_CONTAINS,
+
     MP_BINARY_OP_NUM_RUNTIME,
 
     // These 2 are not supported by the runtime and must be synthesised by the emitter

+ 8 - 4
py/runtime_utils.c

@@ -27,22 +27,26 @@
 
 #include "py/runtime.h"
 
-void mp_call_function_1_protected(mp_obj_t fun, mp_obj_t arg) {
+mp_obj_t mp_call_function_1_protected(mp_obj_t fun, mp_obj_t arg) {
     nlr_buf_t nlr;
     if (nlr_push(&nlr) == 0) {
-        mp_call_function_1(fun, arg);
+        mp_obj_t ret = mp_call_function_1(fun, arg);
         nlr_pop();
+        return ret;
     } else {
         mp_obj_print_exception(&mp_plat_print, MP_OBJ_FROM_PTR(nlr.ret_val));
+        return MP_OBJ_NULL;
     }
 }
 
-void mp_call_function_2_protected(mp_obj_t fun, mp_obj_t arg1, mp_obj_t arg2) {
+mp_obj_t mp_call_function_2_protected(mp_obj_t fun, mp_obj_t arg1, mp_obj_t arg2) {
     nlr_buf_t nlr;
     if (nlr_push(&nlr) == 0) {
-        mp_call_function_2(fun, arg1, arg2);
+        mp_obj_t ret = mp_call_function_2(fun, arg1, arg2);
         nlr_pop();
+        return ret;
     } else {
         mp_obj_print_exception(&mp_plat_print, MP_OBJ_FROM_PTR(nlr.ret_val));
+        return MP_OBJ_NULL;
     }
 }

+ 3 - 3
py/smallint.h

@@ -50,10 +50,10 @@
 
 #elif MICROPY_OBJ_REPR == MICROPY_OBJ_REPR_D
 
-#define MP_SMALL_INT_MIN ((mp_int_t)(((mp_int_t)0xffffffff80000000) >> 1))
-#define MP_SMALL_INT_FITS(n) ((((n) ^ ((n) << 1)) & 0xffffffff80000000) == 0)
+#define MP_SMALL_INT_MIN ((mp_int_t)(((mp_int_t)0xffff800000000000) >> 1))
+#define MP_SMALL_INT_FITS(n) ((((n) ^ ((n) << 1)) & 0xffff800000000000) == 0)
 // Mask to truncate mp_int_t to positive value
-#define MP_SMALL_INT_POSITIVE_MASK ~(0xffffffff80000000 | (0xffffffff80000000 >> 1))
+#define MP_SMALL_INT_POSITIVE_MASK ~(0xffff800000000000 | (0xffff800000000000 >> 1))
 
 #endif
 

+ 1 - 6
py/stackctrl.c

@@ -48,14 +48,9 @@ void mp_stack_set_limit(mp_uint_t limit) {
     MP_STATE_THREAD(stack_limit) = limit;
 }
 
-void mp_exc_recursion_depth(void) {
-    nlr_raise(mp_obj_new_exception_arg1(&mp_type_RuntimeError,
-        MP_OBJ_NEW_QSTR(MP_QSTR_maximum_space_recursion_space_depth_space_exceeded)));
-}
-
 void mp_stack_check(void) {
     if (mp_stack_usage() >= MP_STATE_THREAD(stack_limit)) {
-        mp_exc_recursion_depth();
+        mp_raise_recursion_depth();
     }
 }
 

+ 0 - 1
py/stream.c

@@ -25,7 +25,6 @@
  * THE SOFTWARE.
  */
 
-#include <stdio.h>
 #include <string.h>
 #include <unistd.h>
 

+ 41 - 5
py/vm.c

@@ -935,8 +935,11 @@ unwind_jump:;
                         #if MICROPY_STACKLESS_STRICT
                         else {
                         deep_recursion_error:
-                            mp_exc_recursion_depth();
+                            mp_raise_recursion_depth();
                         }
+                        #else
+                        // If we couldn't allocate codestate on heap, in
+                        // non non-strict case fall thru to stack allocation.
                         #endif
                     }
                     #endif
@@ -963,7 +966,11 @@ unwind_jump:;
 
                         mp_code_state_t *new_state = mp_obj_fun_bc_prepare_codestate(out_args.fun,
                             out_args.n_args, out_args.n_kw, out_args.args);
-                        m_del(mp_obj_t, out_args.args, out_args.n_alloc);
+                        #if !MICROPY_ENABLE_PYSTACK
+                        // Freeing args at this point does not follow a LIFO order so only do it if
+                        // pystack is not enabled.  For pystack, they are freed when code_state is.
+                        mp_nonlocal_free(out_args.args, out_args.n_alloc * sizeof(mp_obj_t));
+                        #endif
                         if (new_state) {
                             new_state->prev = code_state;
                             code_state = new_state;
@@ -974,6 +981,9 @@ unwind_jump:;
                         else {
                             goto deep_recursion_error;
                         }
+                        #else
+                        // If we couldn't allocate codestate on heap, in
+                        // non non-strict case fall thru to stack allocation.
                         #endif
                     }
                     #endif
@@ -1008,6 +1018,9 @@ unwind_jump:;
                         else {
                             goto deep_recursion_error;
                         }
+                        #else
+                        // If we couldn't allocate codestate on heap, in
+                        // non non-strict case fall thru to stack allocation.
                         #endif
                     }
                     #endif
@@ -1034,7 +1047,11 @@ unwind_jump:;
 
                         mp_code_state_t *new_state = mp_obj_fun_bc_prepare_codestate(out_args.fun,
                             out_args.n_args, out_args.n_kw, out_args.args);
-                        m_del(mp_obj_t, out_args.args, out_args.n_alloc);
+                        #if !MICROPY_ENABLE_PYSTACK
+                        // Freeing args at this point does not follow a LIFO order so only do it if
+                        // pystack is not enabled.  For pystack, they are freed when code_state is.
+                        mp_nonlocal_free(out_args.args, out_args.n_alloc * sizeof(mp_obj_t));
+                        #endif
                         if (new_state) {
                             new_state->prev = code_state;
                             code_state = new_state;
@@ -1045,6 +1062,9 @@ unwind_jump:;
                         else {
                             goto deep_recursion_error;
                         }
+                        #else
+                        // If we couldn't allocate codestate on heap, in
+                        // non non-strict case fall thru to stack allocation.
                         #endif
                     }
                     #endif
@@ -1096,7 +1116,15 @@ unwind_return:
                     if (code_state->prev != NULL) {
                         mp_obj_t res = *sp;
                         mp_globals_set(code_state->old_globals);
-                        code_state = code_state->prev;
+                        mp_code_state_t *new_code_state = code_state->prev;
+                        #if MICROPY_ENABLE_PYSTACK
+                        // Free code_state, and args allocated by mp_call_prepare_args_n_kw_var
+                        // (The latter is implicitly freed when using pystack due to its LIFO nature.)
+                        // The sizeof in the following statement does not include the size of the variable
+                        // part of the struct.  This arg is anyway not used if pystack is enabled.
+                        mp_nonlocal_free(code_state, sizeof(mp_code_state_t));
+                        #endif
+                        code_state = new_code_state;
                         *code_state->sp = res;
                         goto run_code_state;
                     }
@@ -1438,7 +1466,15 @@ unwind_loop:
             #if MICROPY_STACKLESS
             } else if (code_state->prev != NULL) {
                 mp_globals_set(code_state->old_globals);
-                code_state = code_state->prev;
+                mp_code_state_t *new_code_state = code_state->prev;
+                #if MICROPY_ENABLE_PYSTACK
+                // Free code_state, and args allocated by mp_call_prepare_args_n_kw_var
+                // (The latter is implicitly freed when using pystack due to its LIFO nature.)
+                // The sizeof in the following statement does not include the size of the variable
+                // part of the struct.  This arg is anyway not used if pystack is enabled.
+                mp_nonlocal_free(code_state, sizeof(mp_code_state_t));
+                #endif
+                code_state = new_code_state;
                 size_t n_state = mp_decode_uint_value(code_state->fun_bc->bytecode);
                 fastn = &code_state->state[n_state - 1];
                 exc_stack = (mp_exc_stack_t*)(code_state->state + n_state);