From ae8d86758631e62466a55d179897d2111c3cb1c1 Mon Sep 17 00:00:00 2001
From: Damien George <damien.p.george@gmail.com>
Date: Sat, 9 Jan 2016 23:14:54 +0000
Subject: [PATCH] py: Add iter_buf to getiter type method.

Allows to iterate over the following without allocating on the heap:
- tuple
- list
- string, bytes
- bytearray, array
- dict (not dict.keys, dict.values, dict.items)
- set, frozenset

Allows to call the following without heap memory:
- all, any, min, max, sum

TODO: still need to allocate stack memory in bytecode for iter_buf.
---
 cc3200/mods/pybuart.c  |  2 +-
 esp8266/machine_uart.c |  2 +-
 extmod/modbtree.c      |  3 ++-
 extmod/vfs_fat_file.c  |  4 ++--
 py/emitnative.c        |  1 +
 py/modbuiltins.c       | 14 +++++++++-----
 py/obj.c               |  5 +++++
 py/obj.h               | 15 +++++++++++++--
 py/objarray.c          | 14 +++++++++-----
 py/objdict.c           | 24 +++++++++++++++---------
 py/objenumerate.c      |  6 +++---
 py/objfilter.c         |  4 ++--
 py/objgenerator.c      |  2 +-
 py/objgetitemiter.c    |  7 ++++---
 py/objlist.c           | 14 ++++++++------
 py/objmap.c            |  4 ++--
 py/objpolyiter.c       |  2 +-
 py/objrange.c          | 11 ++++++-----
 py/objreversed.c       |  2 +-
 py/objset.c            | 26 +++++++++++++++++---------
 py/objstr.c            | 17 ++++++++++-------
 py/objstringio.c       |  4 ++--
 py/objstrunicode.c     |  7 ++++---
 py/objtuple.c          | 18 +++++++-----------
 py/objtuple.h          |  2 +-
 py/objtype.c           |  4 ++--
 py/objzip.c            |  4 ++--
 py/runtime.c           | 26 ++++++++++++++++++--------
 py/runtime.h           |  2 +-
 py/vm.c                |  2 +-
 stmhal/pybstdio.c      |  4 ++--
 stmhal/uart.c          |  2 +-
 stmhal/usb.c           |  2 +-
 unix/file.c            |  4 ++--
 unix/modffi.c          |  6 ++++--
 unix/moduselect.c      |  2 +-
 36 files changed, 162 insertions(+), 106 deletions(-)

diff --git a/cc3200/mods/pybuart.c b/cc3200/mods/pybuart.c
index fe710be92..dceb842d5 100644
--- a/cc3200/mods/pybuart.c
+++ b/cc3200/mods/pybuart.c
@@ -664,7 +664,7 @@ const mp_obj_type_t pyb_uart_type = {
     .name = MP_QSTR_UART,
     .print = pyb_uart_print,
     .make_new = pyb_uart_make_new,
-    .getiter = mp_identity,
+    .getiter = mp_identity_getiter,
     .iternext = mp_stream_unbuffered_iter,
     .protocol = &uart_stream_p,
     .locals_dict = (mp_obj_t)&pyb_uart_locals_dict,
diff --git a/esp8266/machine_uart.c b/esp8266/machine_uart.c
index efdfafd1a..ef52e8c9a 100644
--- a/esp8266/machine_uart.c
+++ b/esp8266/machine_uart.c
@@ -285,7 +285,7 @@ const mp_obj_type_t pyb_uart_type = {
     .name = MP_QSTR_UART,
     .print = pyb_uart_print,
     .make_new = pyb_uart_make_new,
-    .getiter = mp_identity,
+    .getiter = mp_identity_getiter,
     .iternext = mp_stream_unbuffered_iter,
     .protocol = &uart_stream_p,
     .locals_dict = (mp_obj_dict_t*)&pyb_uart_locals_dict,
diff --git a/extmod/modbtree.c b/extmod/modbtree.c
index bb75845b1..27f700eea 100644
--- a/extmod/modbtree.c
+++ b/extmod/modbtree.c
@@ -184,7 +184,8 @@ STATIC mp_obj_t btree_items(size_t n_args, const mp_obj_t *args) {
 }
 STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(btree_items_obj, 1, 4, btree_items);
 
-STATIC mp_obj_t btree_getiter(mp_obj_t self_in) {
+STATIC mp_obj_t btree_getiter(mp_obj_t self_in, mp_obj_iter_buf_t *iter_buf) {
+    (void)iter_buf;
     mp_obj_btree_t *self = MP_OBJ_TO_PTR(self_in);
     if (self->next_flags != 0) {
         // If we're called immediately after keys(), values(), or items(),
diff --git a/extmod/vfs_fat_file.c b/extmod/vfs_fat_file.c
index 62da23f94..6263492cb 100644
--- a/extmod/vfs_fat_file.c
+++ b/extmod/vfs_fat_file.c
@@ -264,7 +264,7 @@ const mp_obj_type_t mp_type_fileio = {
     .name = MP_QSTR_FileIO,
     .print = file_obj_print,
     .make_new = file_obj_make_new,
-    .getiter = mp_identity,
+    .getiter = mp_identity_getiter,
     .iternext = mp_stream_unbuffered_iter,
     .protocol = &fileio_stream_p,
     .locals_dict = (mp_obj_dict_t*)&rawfile_locals_dict,
@@ -283,7 +283,7 @@ const mp_obj_type_t mp_type_textio = {
     .name = MP_QSTR_TextIOWrapper,
     .print = file_obj_print,
     .make_new = file_obj_make_new,
-    .getiter = mp_identity,
+    .getiter = mp_identity_getiter,
     .iternext = mp_stream_unbuffered_iter,
     .protocol = &textio_stream_p,
     .locals_dict = (mp_obj_dict_t*)&rawfile_locals_dict,
diff --git a/py/emitnative.c b/py/emitnative.c
index bdb590e77..e8e3754e1 100644
--- a/py/emitnative.c
+++ b/py/emitnative.c
@@ -1806,6 +1806,7 @@ STATIC void emit_native_get_iter(emit_t *emit) {
     vtype_kind_t vtype;
     emit_pre_pop_reg(emit, &vtype, REG_ARG_1);
     assert(vtype == VTYPE_PYOBJ);
+    assert(0); // TODO allocate memory for iter_buf
     emit_call(emit, MP_F_GETITER);
     emit_post_push_reg(emit, VTYPE_PYOBJ, REG_RET);
 }
diff --git a/py/modbuiltins.c b/py/modbuiltins.c
index a0c68930d..13312d229 100644
--- a/py/modbuiltins.c
+++ b/py/modbuiltins.c
@@ -117,7 +117,8 @@ STATIC mp_obj_t mp_builtin_abs(mp_obj_t o_in) {
 MP_DEFINE_CONST_FUN_OBJ_1(mp_builtin_abs_obj, mp_builtin_abs);
 
 STATIC mp_obj_t mp_builtin_all(mp_obj_t o_in) {
-    mp_obj_t iterable = mp_getiter(o_in);
+    mp_obj_iter_buf_t iter_buf;
+    mp_obj_t iterable = mp_getiter(o_in, &iter_buf);
     mp_obj_t item;
     while ((item = mp_iternext(iterable)) != MP_OBJ_STOP_ITERATION) {
         if (!mp_obj_is_true(item)) {
@@ -129,7 +130,8 @@ STATIC mp_obj_t mp_builtin_all(mp_obj_t o_in) {
 MP_DEFINE_CONST_FUN_OBJ_1(mp_builtin_all_obj, mp_builtin_all);
 
 STATIC mp_obj_t mp_builtin_any(mp_obj_t o_in) {
-    mp_obj_t iterable = mp_getiter(o_in);
+    mp_obj_iter_buf_t iter_buf;
+    mp_obj_t iterable = mp_getiter(o_in, &iter_buf);
     mp_obj_t item;
     while ((item = mp_iternext(iterable)) != MP_OBJ_STOP_ITERATION) {
         if (mp_obj_is_true(item)) {
@@ -258,7 +260,7 @@ STATIC mp_obj_t mp_builtin_hex(mp_obj_t o_in) {
 MP_DEFINE_CONST_FUN_OBJ_1(mp_builtin_hex_obj, mp_builtin_hex);
 
 STATIC mp_obj_t mp_builtin_iter(mp_obj_t o_in) {
-    return mp_getiter(o_in);
+    return mp_getiter(o_in, NULL);
 }
 MP_DEFINE_CONST_FUN_OBJ_1(mp_builtin_iter_obj, mp_builtin_iter);
 
@@ -270,7 +272,8 @@ STATIC mp_obj_t mp_builtin_min_max(size_t n_args, const mp_obj_t *args, mp_map_t
     mp_obj_t key_fn = key_elem == NULL ? MP_OBJ_NULL : key_elem->value;
     if (n_args == 1) {
         // given an iterable
-        mp_obj_t iterable = mp_getiter(args[0]);
+        mp_obj_iter_buf_t iter_buf;
+        mp_obj_t iterable = mp_getiter(args[0], &iter_buf);
         mp_obj_t best_key = MP_OBJ_NULL;
         mp_obj_t best_obj = MP_OBJ_NULL;
         mp_obj_t item;
@@ -495,7 +498,8 @@ STATIC mp_obj_t mp_builtin_sum(size_t n_args, const mp_obj_t *args) {
         case 1: value = MP_OBJ_NEW_SMALL_INT(0); break;
         default: value = args[1]; break;
     }
-    mp_obj_t iterable = mp_getiter(args[0]);
+    mp_obj_iter_buf_t iter_buf;
+    mp_obj_t iterable = mp_getiter(args[0], &iter_buf);
     mp_obj_t item;
     while ((item = mp_iternext(iterable)) != MP_OBJ_STOP_ITERATION) {
         value = mp_binary_op(MP_BINARY_OP_ADD, value, item);
diff --git a/py/obj.c b/py/obj.c
index 1d0c80ab9..4534ef1b2 100644
--- a/py/obj.c
+++ b/py/obj.c
@@ -481,6 +481,11 @@ mp_obj_t mp_identity(mp_obj_t self) {
 }
 MP_DEFINE_CONST_FUN_OBJ_1(mp_identity_obj, mp_identity);
 
+mp_obj_t mp_identity_getiter(mp_obj_t self, mp_obj_iter_buf_t *iter_buf) {
+    (void)iter_buf;
+    return self;
+}
+
 bool mp_get_buffer(mp_obj_t obj, mp_buffer_info_t *bufinfo, mp_uint_t flags) {
     mp_obj_type_t *type = mp_obj_get_type(obj);
     if (type->buffer_p.get_buffer == NULL) {
diff --git a/py/obj.h b/py/obj.h
index 1169a9095..79a6d56fa 100644
--- a/py/obj.h
+++ b/py/obj.h
@@ -417,6 +417,11 @@ typedef enum {
     PRINT_EXC_SUBCLASS = 0x80, // Internal flag for printing exception subclasses
 } mp_print_kind_t;
 
+typedef struct _mp_obj_iter_buf_t {
+    mp_obj_base_t base;
+    mp_obj_t buf[3];
+} mp_obj_iter_buf_t;
+
 typedef void (*mp_print_fun_t)(const mp_print_t *print, mp_obj_t o, mp_print_kind_t kind);
 typedef mp_obj_t (*mp_make_new_fun_t)(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args);
 typedef mp_obj_t (*mp_call_fun_t)(mp_obj_t fun, size_t n_args, size_t n_kw, const mp_obj_t *args);
@@ -424,6 +429,7 @@ typedef mp_obj_t (*mp_unary_op_fun_t)(mp_uint_t op, mp_obj_t);
 typedef mp_obj_t (*mp_binary_op_fun_t)(mp_uint_t op, mp_obj_t, mp_obj_t);
 typedef void (*mp_attr_fun_t)(mp_obj_t self_in, qstr attr, mp_obj_t *dest);
 typedef mp_obj_t (*mp_subscr_fun_t)(mp_obj_t self_in, mp_obj_t index, mp_obj_t value);
+typedef mp_obj_t (*mp_getiter_fun_t)(mp_obj_t self_in, mp_obj_iter_buf_t *iter_buf);
 
 // Buffer protocol
 typedef struct _mp_buffer_info_t {
@@ -486,7 +492,11 @@ struct _mp_obj_type_t {
                                     // value=MP_OBJ_NULL means delete, value=MP_OBJ_SENTINEL means load, else store
                                     // can return MP_OBJ_NULL if op not supported
 
-    mp_fun_1_t getiter;             // corresponds to __iter__ special method
+    // corresponds to __iter__ special method
+    // can use given mp_obj_iter_buf_t to store iterator
+    // otherwise can return a pointer to an object on the heap
+    mp_getiter_fun_t getiter;
+
     mp_fun_1_t iternext; // may return MP_OBJ_STOP_ITERATION as an optimisation instead of raising StopIteration() (with no args)
 
     mp_buffer_p_t buffer_p;
@@ -637,7 +647,7 @@ mp_obj_t mp_obj_new_set(size_t n_args, mp_obj_t *items);
 mp_obj_t mp_obj_new_slice(mp_obj_t start, mp_obj_t stop, mp_obj_t step);
 mp_obj_t mp_obj_new_super(mp_obj_t type, mp_obj_t obj);
 mp_obj_t mp_obj_new_bound_meth(mp_obj_t meth, mp_obj_t self);
-mp_obj_t mp_obj_new_getitem_iter(mp_obj_t *args);
+mp_obj_t mp_obj_new_getitem_iter(mp_obj_t *args, mp_obj_iter_buf_t *iter_buf);
 mp_obj_t mp_obj_new_module(qstr module_name);
 mp_obj_t mp_obj_new_memoryview(byte typecode, size_t nitems, void *items);
 
@@ -775,6 +785,7 @@ qstr mp_obj_code_get_name(const byte *code_info);
 
 mp_obj_t mp_identity(mp_obj_t self);
 MP_DECLARE_CONST_FUN_OBJ_1(mp_identity_obj);
+mp_obj_t mp_identity_getiter(mp_obj_t self, mp_obj_iter_buf_t *iter_buf);
 
 // module
 typedef struct _mp_obj_module_t {
diff --git a/py/objarray.c b/py/objarray.c
index f23791857..c81aebb50 100644
--- a/py/objarray.c
+++ b/py/objarray.c
@@ -59,7 +59,7 @@
 #define TYPECODE_MASK (~(size_t)0)
 #endif
 
-STATIC mp_obj_t array_iterator_new(mp_obj_t array_in);
+STATIC mp_obj_t array_iterator_new(mp_obj_t array_in, mp_obj_iter_buf_t *iter_buf);
 STATIC mp_obj_t array_append(mp_obj_t self_in, mp_obj_t arg);
 STATIC mp_obj_t array_extend(mp_obj_t self_in, mp_obj_t arg_in);
 STATIC mp_int_t array_get_buffer(mp_obj_t o_in, mp_buffer_info_t *bufinfo, mp_uint_t flags);
@@ -141,7 +141,8 @@ STATIC mp_obj_t array_construct(char typecode, mp_obj_t initializer) {
 
     mp_obj_array_t *array = array_new(typecode, len);
 
-    mp_obj_t iterable = mp_getiter(initializer);
+    mp_obj_iter_buf_t iter_buf;
+    mp_obj_t iterable = mp_getiter(initializer, &iter_buf);
     mp_obj_t item;
     size_t i = 0;
     while ((item = mp_iternext(iterable)) != MP_OBJ_STOP_ITERATION) {
@@ -608,15 +609,18 @@ STATIC mp_obj_t array_it_iternext(mp_obj_t self_in) {
 STATIC const mp_obj_type_t array_it_type = {
     { &mp_type_type },
     .name = MP_QSTR_iterator,
-    .getiter = mp_identity,
+    .getiter = mp_identity_getiter,
     .iternext = array_it_iternext,
 };
 
-STATIC mp_obj_t array_iterator_new(mp_obj_t array_in) {
+STATIC mp_obj_t array_iterator_new(mp_obj_t array_in, mp_obj_iter_buf_t *iter_buf) {
+    assert(sizeof(mp_obj_array_t) <= sizeof(mp_obj_iter_buf_t));
     mp_obj_array_t *array = MP_OBJ_TO_PTR(array_in);
-    mp_obj_array_it_t *o = m_new0(mp_obj_array_it_t, 1);
+    mp_obj_array_it_t *o = (mp_obj_array_it_t*)iter_buf;
     o->base.type = &array_it_type;
     o->array = array;
+    o->offset = 0;
+    o->cur = 0;
     #if MICROPY_PY_BUILTINS_MEMORYVIEW
     if (array->base.type == &mp_type_memoryview) {
         o->offset = array->free;
diff --git a/py/objdict.c b/py/objdict.c
index 640df7b62..c52403f71 100644
--- a/py/objdict.c
+++ b/py/objdict.c
@@ -210,8 +210,9 @@ STATIC mp_obj_t dict_it_iternext(mp_obj_t self_in) {
     }
 }
 
-STATIC mp_obj_t dict_getiter(mp_obj_t self_in) {
-    mp_obj_dict_it_t *o = m_new_obj(mp_obj_dict_it_t);
+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;
@@ -249,7 +250,8 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_1(dict_copy_obj, dict_copy);
 
 // this is a classmethod
 STATIC mp_obj_t dict_fromkeys(size_t n_args, const mp_obj_t *args) {
-    mp_obj_t iter = mp_getiter(args[1]);
+    mp_obj_iter_buf_t iter_buf;
+    mp_obj_t iter = mp_getiter(args[1], &iter_buf);
     mp_obj_t value = mp_const_none;
     mp_obj_t next = MP_OBJ_NULL;
 
@@ -375,10 +377,12 @@ STATIC mp_obj_t dict_update(size_t n_args, const mp_obj_t *args, mp_map_t *kwarg
             }
         } else {
             // update from a generic iterable of pairs
-            mp_obj_t iter = mp_getiter(args[1]);
+            mp_obj_iter_buf_t iter_buf;
+            mp_obj_t iter = mp_getiter(args[1], &iter_buf);
             mp_obj_t next = MP_OBJ_NULL;
             while ((next = mp_iternext(iter)) != MP_OBJ_STOP_ITERATION) {
-                mp_obj_t inneriter = mp_getiter(next);
+                mp_obj_iter_buf_t inner_iter_buf;
+                mp_obj_t inneriter = mp_getiter(next, &inner_iter_buf);
                 mp_obj_t key = mp_iternext(inneriter);
                 mp_obj_t value = mp_iternext(inneriter);
                 mp_obj_t stop = mp_iternext(inneriter);
@@ -457,14 +461,15 @@ STATIC mp_obj_t dict_view_it_iternext(mp_obj_t self_in) {
 STATIC const mp_obj_type_t dict_view_it_type = {
     { &mp_type_type },
     .name = MP_QSTR_iterator,
-    .getiter = mp_identity,
+    .getiter = mp_identity_getiter,
     .iternext = dict_view_it_iternext,
 };
 
-STATIC mp_obj_t dict_view_getiter(mp_obj_t view_in) {
+STATIC mp_obj_t dict_view_getiter(mp_obj_t view_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_TYPE(view_in, &dict_view_type));
     mp_obj_dict_view_t *view = MP_OBJ_TO_PTR(view_in);
-    mp_obj_dict_view_it_t *o = m_new_obj(mp_obj_dict_view_it_t);
+    mp_obj_dict_view_it_t *o = (mp_obj_dict_view_it_t*)iter_buf;
     o->base.type = &dict_view_it_type;
     o->kind = view->kind;
     o->dict = view->dict;
@@ -479,7 +484,8 @@ STATIC void dict_view_print(const mp_print_t *print, mp_obj_t self_in, mp_print_
     bool first = true;
     mp_print_str(print, mp_dict_view_names[self->kind]);
     mp_print_str(print, "([");
-    mp_obj_t self_iter = dict_view_getiter(self_in);
+    mp_obj_iter_buf_t iter_buf;
+    mp_obj_t self_iter = dict_view_getiter(self_in, &iter_buf);
     mp_obj_t next = MP_OBJ_NULL;
     while ((next = dict_view_it_iternext(self_iter)) != MP_OBJ_STOP_ITERATION) {
         if (!first) {
diff --git a/py/objenumerate.c b/py/objenumerate.c
index 2b646ca45..faae6516c 100644
--- a/py/objenumerate.c
+++ b/py/objenumerate.c
@@ -56,13 +56,13 @@ STATIC mp_obj_t enumerate_make_new(const mp_obj_type_t *type, size_t n_args, siz
     // create enumerate object
     mp_obj_enumerate_t *o = m_new_obj(mp_obj_enumerate_t);
     o->base.type = type;
-    o->iter = mp_getiter(arg_vals.iterable.u_obj);
+    o->iter = mp_getiter(arg_vals.iterable.u_obj, NULL);
     o->cur = arg_vals.start.u_int;
 #else
     (void)n_kw;
     mp_obj_enumerate_t *o = m_new_obj(mp_obj_enumerate_t);
     o->base.type = type;
-    o->iter = mp_getiter(args[0]);
+    o->iter = mp_getiter(args[0], NULL);
     o->cur = n_args > 1 ? mp_obj_get_int(args[1]) : 0;
 #endif
 
@@ -74,7 +74,7 @@ const mp_obj_type_t mp_type_enumerate = {
     .name = MP_QSTR_enumerate,
     .make_new = enumerate_make_new,
     .iternext = enumerate_iternext,
-    .getiter = mp_identity,
+    .getiter = mp_identity_getiter,
 };
 
 STATIC mp_obj_t enumerate_iternext(mp_obj_t self_in) {
diff --git a/py/objfilter.c b/py/objfilter.c
index a5c85b2ce..a655b8a78 100644
--- a/py/objfilter.c
+++ b/py/objfilter.c
@@ -39,7 +39,7 @@ STATIC mp_obj_t filter_make_new(const mp_obj_type_t *type, size_t n_args, size_t
     mp_obj_filter_t *o = m_new_obj(mp_obj_filter_t);
     o->base.type = type;
     o->fun = args[0];
-    o->iter = mp_getiter(args[1]);
+    o->iter = mp_getiter(args[1], NULL);
     return MP_OBJ_FROM_PTR(o);
 }
 
@@ -65,7 +65,7 @@ const mp_obj_type_t mp_type_filter = {
     { &mp_type_type },
     .name = MP_QSTR_filter,
     .make_new = filter_make_new,
-    .getiter = mp_identity,
+    .getiter = mp_identity_getiter,
     .iternext = filter_iternext,
 };
 
diff --git a/py/objgenerator.c b/py/objgenerator.c
index 0be9b8d59..654b18670 100644
--- a/py/objgenerator.c
+++ b/py/objgenerator.c
@@ -236,7 +236,7 @@ const mp_obj_type_t mp_type_gen_instance = {
     { &mp_type_type },
     .name = MP_QSTR_generator,
     .print = gen_instance_print,
-    .getiter = mp_identity,
+    .getiter = mp_identity_getiter,
     .iternext = gen_instance_iternext,
     .locals_dict = (mp_obj_dict_t*)&gen_instance_locals_dict,
 };
diff --git a/py/objgetitemiter.c b/py/objgetitemiter.c
index 526180fdb..a3c754448 100644
--- a/py/objgetitemiter.c
+++ b/py/objgetitemiter.c
@@ -61,13 +61,14 @@ STATIC mp_obj_t it_iternext(mp_obj_t self_in) {
 STATIC const mp_obj_type_t it_type = {
     { &mp_type_type },
     .name = MP_QSTR_iterator,
-    .getiter = mp_identity,
+    .getiter = mp_identity_getiter,
     .iternext = it_iternext,
 };
 
 // args are those returned from mp_load_method_maybe (ie either an attribute or a method)
-mp_obj_t mp_obj_new_getitem_iter(mp_obj_t *args) {
-    mp_obj_getitem_iter_t *o = m_new_obj(mp_obj_getitem_iter_t);
+mp_obj_t mp_obj_new_getitem_iter(mp_obj_t *args, mp_obj_iter_buf_t *iter_buf) {
+    assert(sizeof(mp_obj_getitem_iter_t) <= sizeof(mp_obj_iter_buf_t));
+    mp_obj_getitem_iter_t *o = (mp_obj_getitem_iter_t*)iter_buf;
     o->base.type = &it_type;
     o->args[0] = args[0];
     o->args[1] = args[1];
diff --git a/py/objlist.c b/py/objlist.c
index 210140388..28da10991 100644
--- a/py/objlist.c
+++ b/py/objlist.c
@@ -33,7 +33,7 @@
 #include "py/runtime.h"
 #include "py/stackctrl.h"
 
-STATIC mp_obj_t mp_obj_new_list_iterator(mp_obj_t list, size_t cur);
+STATIC mp_obj_t mp_obj_new_list_iterator(mp_obj_t list, size_t cur, mp_obj_iter_buf_t *iter_buf);
 STATIC mp_obj_list_t *list_new(size_t n);
 STATIC mp_obj_t list_extend(mp_obj_t self_in, mp_obj_t arg_in);
 STATIC mp_obj_t list_pop(size_t n_args, const mp_obj_t *args);
@@ -60,7 +60,8 @@ STATIC void list_print(const mp_print_t *print, mp_obj_t o_in, mp_print_kind_t k
 }
 
 STATIC mp_obj_t list_extend_from_iter(mp_obj_t list, mp_obj_t iterable) {
-    mp_obj_t iter = mp_getiter(iterable);
+    mp_obj_iter_buf_t iter_buf;
+    mp_obj_t iter = mp_getiter(iterable, &iter_buf);
     mp_obj_t item;
     while ((item = mp_iternext(iter)) != MP_OBJ_STOP_ITERATION) {
         mp_obj_list_append(list, item);
@@ -225,8 +226,8 @@ STATIC mp_obj_t list_subscr(mp_obj_t self_in, mp_obj_t index, mp_obj_t value) {
     }
 }
 
-STATIC mp_obj_t list_getiter(mp_obj_t o_in) {
-    return mp_obj_new_list_iterator(o_in, 0);
+STATIC mp_obj_t list_getiter(mp_obj_t o_in, mp_obj_iter_buf_t *iter_buf) {
+    return mp_obj_new_list_iterator(o_in, 0, iter_buf);
 }
 
 mp_obj_t mp_obj_list_append(mp_obj_t self_in, mp_obj_t arg) {
@@ -516,8 +517,9 @@ STATIC mp_obj_t list_it_iternext(mp_obj_t self_in) {
     }
 }
 
-mp_obj_t mp_obj_new_list_iterator(mp_obj_t list, size_t cur) {
-    mp_obj_list_it_t *o = m_new_obj(mp_obj_list_it_t);
+mp_obj_t mp_obj_new_list_iterator(mp_obj_t list, size_t cur, mp_obj_iter_buf_t *iter_buf) {
+    assert(sizeof(mp_obj_list_it_t) <= sizeof(mp_obj_iter_buf_t));
+    mp_obj_list_it_t *o = (mp_obj_list_it_t*)iter_buf;
     o->base.type = &mp_type_polymorph_iter;
     o->iternext = list_it_iternext;
     o->list = list;
diff --git a/py/objmap.c b/py/objmap.c
index ed0291435..0b2189016 100644
--- a/py/objmap.c
+++ b/py/objmap.c
@@ -43,7 +43,7 @@ STATIC mp_obj_t map_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_
     o->n_iters = n_args - 1;
     o->fun = args[0];
     for (mp_uint_t i = 0; i < n_args - 1; i++) {
-        o->iters[i] = mp_getiter(args[i + 1]);
+        o->iters[i] = mp_getiter(args[i + 1], NULL);
     }
     return MP_OBJ_FROM_PTR(o);
 }
@@ -68,6 +68,6 @@ const mp_obj_type_t mp_type_map = {
     { &mp_type_type },
     .name = MP_QSTR_map,
     .make_new = map_make_new,
-    .getiter = mp_identity,
+    .getiter = mp_identity_getiter,
     .iternext = map_iternext,
 };
diff --git a/py/objpolyiter.c b/py/objpolyiter.c
index 9bba538c5..61bd1e0ac 100644
--- a/py/objpolyiter.c
+++ b/py/objpolyiter.c
@@ -49,6 +49,6 @@ STATIC mp_obj_t polymorph_it_iternext(mp_obj_t self_in) {
 const mp_obj_type_t mp_type_polymorph_iter = {
     { &mp_type_type },
     .name = MP_QSTR_iterator,
-    .getiter = mp_identity,
+    .getiter = mp_identity_getiter,
     .iternext = polymorph_it_iternext,
 };
diff --git a/py/objrange.c b/py/objrange.c
index 79459316b..dd074a98a 100644
--- a/py/objrange.c
+++ b/py/objrange.c
@@ -55,12 +55,13 @@ STATIC mp_obj_t range_it_iternext(mp_obj_t o_in) {
 STATIC const mp_obj_type_t range_it_type = {
     { &mp_type_type },
     .name = MP_QSTR_iterator,
-    .getiter = mp_identity,
+    .getiter = mp_identity_getiter,
     .iternext = range_it_iternext,
 };
 
-STATIC mp_obj_t mp_obj_new_range_iterator(mp_int_t cur, mp_int_t stop, mp_int_t step) {
-    mp_obj_range_it_t *o = m_new_obj(mp_obj_range_it_t);
+STATIC mp_obj_t mp_obj_new_range_iterator(mp_int_t cur, mp_int_t stop, mp_int_t step, mp_obj_iter_buf_t *iter_buf) {
+    assert(sizeof(mp_obj_range_it_t) <= sizeof(mp_obj_iter_buf_t));
+    mp_obj_range_it_t *o = (mp_obj_range_it_t*)iter_buf;
     o->base.type = &range_it_type;
     o->cur = cur;
     o->stop = stop;
@@ -161,9 +162,9 @@ STATIC mp_obj_t range_subscr(mp_obj_t self_in, mp_obj_t index, mp_obj_t value) {
     }
 }
 
-STATIC mp_obj_t range_getiter(mp_obj_t o_in) {
+STATIC mp_obj_t range_getiter(mp_obj_t o_in, mp_obj_iter_buf_t *iter_buf) {
     mp_obj_range_t *o = MP_OBJ_TO_PTR(o_in);
-    return mp_obj_new_range_iterator(o->start, o->stop, o->step);
+    return mp_obj_new_range_iterator(o->start, o->stop, o->step, iter_buf);
 }
 
 
diff --git a/py/objreversed.c b/py/objreversed.c
index 4343c1978..fc85e72bf 100644
--- a/py/objreversed.c
+++ b/py/objreversed.c
@@ -74,7 +74,7 @@ const mp_obj_type_t mp_type_reversed = {
     { &mp_type_type },
     .name = MP_QSTR_reversed,
     .make_new = reversed_make_new,
-    .getiter = mp_identity,
+    .getiter = mp_identity_getiter,
     .iternext = reversed_iternext,
 };
 
diff --git a/py/objset.c b/py/objset.c
index b9da44a44..99e1e8ca5 100644
--- a/py/objset.c
+++ b/py/objset.c
@@ -129,7 +129,8 @@ STATIC mp_obj_t set_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_
         default: { // can only be 0 or 1 arg
             // 1 argument, an iterable from which we make a new set
             mp_obj_t set = mp_obj_new_set(0, NULL);
-            mp_obj_t iterable = mp_getiter(args[0]);
+            mp_obj_iter_buf_t iter_buf;
+            mp_obj_t iterable = mp_getiter(args[0], &iter_buf);
             mp_obj_t item;
             while ((item = mp_iternext(iterable)) != MP_OBJ_STOP_ITERATION) {
                 mp_obj_set_store(set, item);
@@ -156,8 +157,9 @@ STATIC mp_obj_t set_it_iternext(mp_obj_t self_in) {
     return MP_OBJ_STOP_ITERATION;
 }
 
-STATIC mp_obj_t set_getiter(mp_obj_t set_in) {
-    mp_obj_set_it_t *o = m_new_obj(mp_obj_set_it_t);
+STATIC mp_obj_t set_getiter(mp_obj_t set_in, mp_obj_iter_buf_t *iter_buf) {
+    assert(sizeof(mp_obj_set_it_t) <= sizeof(mp_obj_iter_buf_t));
+    mp_obj_set_it_t *o = (mp_obj_set_it_t*)iter_buf;
     o->base.type = &mp_type_polymorph_iter;
     o->iternext = set_it_iternext;
     o->set = (mp_obj_set_t *)MP_OBJ_TO_PTR(set_in);
@@ -233,7 +235,8 @@ STATIC mp_obj_t set_diff_int(size_t n_args, const mp_obj_t *args, bool update) {
         if (self == other) {
             set_clear(self);
         } else {
-            mp_obj_t iter = mp_getiter(other);
+            mp_obj_iter_buf_t iter_buf;
+            mp_obj_t iter = mp_getiter(other, &iter_buf);
             mp_obj_t next;
             while ((next = mp_iternext(iter)) != MP_OBJ_STOP_ITERATION) {
                 set_discard(self, next);
@@ -270,7 +273,8 @@ STATIC mp_obj_t set_intersect_int(mp_obj_t self_in, mp_obj_t other, bool update)
     mp_obj_set_t *self = MP_OBJ_TO_PTR(self_in);
     mp_obj_set_t *out = MP_OBJ_TO_PTR(mp_obj_new_set(0, NULL));
 
-    mp_obj_t iter = mp_getiter(other);
+    mp_obj_iter_buf_t iter_buf;
+    mp_obj_t iter = mp_getiter(other, &iter_buf);
     mp_obj_t next;
     while ((next = mp_iternext(iter)) != MP_OBJ_STOP_ITERATION) {
         if (mp_set_lookup(&self->set, next, MP_MAP_LOOKUP)) {
@@ -302,7 +306,8 @@ STATIC mp_obj_t set_isdisjoint(mp_obj_t self_in, mp_obj_t other) {
     check_set_or_frozenset(self_in);
     mp_obj_set_t *self = MP_OBJ_TO_PTR(self_in);
 
-    mp_obj_t iter = mp_getiter(other);
+    mp_obj_iter_buf_t iter_buf;
+    mp_obj_t iter = mp_getiter(other, &iter_buf);
     mp_obj_t next;
     while ((next = mp_iternext(iter)) != MP_OBJ_STOP_ITERATION) {
         if (mp_set_lookup(&self->set, next, MP_MAP_LOOKUP)) {
@@ -335,7 +340,8 @@ STATIC mp_obj_t set_issubset_internal(mp_obj_t self_in, mp_obj_t other_in, bool
     if (proper && self->set.used == other->set.used) {
         out = false;
     } else {
-        mp_obj_t iter = set_getiter(MP_OBJ_FROM_PTR(self));
+        mp_obj_iter_buf_t iter_buf;
+        mp_obj_t iter = set_getiter(MP_OBJ_FROM_PTR(self), &iter_buf);
         mp_obj_t next;
         while ((next = set_it_iternext(iter)) != MP_OBJ_STOP_ITERATION) {
             if (!mp_set_lookup(&other->set, next, MP_MAP_LOOKUP)) {
@@ -408,7 +414,8 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_2(set_remove_obj, set_remove);
 STATIC mp_obj_t set_symmetric_difference_update(mp_obj_t self_in, mp_obj_t other_in) {
     check_set(self_in);
     mp_obj_set_t *self = MP_OBJ_TO_PTR(self_in);
-    mp_obj_t iter = mp_getiter(other_in);
+    mp_obj_iter_buf_t iter_buf;
+    mp_obj_t iter = mp_getiter(other_in, &iter_buf);
     mp_obj_t next;
     while ((next = mp_iternext(iter)) != MP_OBJ_STOP_ITERATION) {
         mp_set_lookup(&self->set, next, MP_MAP_LOOKUP_ADD_IF_NOT_FOUND_OR_REMOVE_IF_FOUND);
@@ -427,7 +434,8 @@ STATIC mp_obj_t set_symmetric_difference(mp_obj_t self_in, mp_obj_t other_in) {
 STATIC MP_DEFINE_CONST_FUN_OBJ_2(set_symmetric_difference_obj, set_symmetric_difference);
 
 STATIC void set_update_int(mp_obj_set_t *self, mp_obj_t other_in) {
-    mp_obj_t iter = mp_getiter(other_in);
+    mp_obj_iter_buf_t iter_buf;
+    mp_obj_t iter = mp_getiter(other_in, &iter_buf);
     mp_obj_t next;
     while ((next = mp_iternext(iter)) != MP_OBJ_STOP_ITERATION) {
         mp_set_lookup(&self->set, next, MP_MAP_LOOKUP_ADD_IF_NOT_FOUND);
diff --git a/py/objstr.c b/py/objstr.c
index 262b89ddd..c137afe67 100644
--- a/py/objstr.c
+++ b/py/objstr.c
@@ -38,7 +38,7 @@
 
 STATIC mp_obj_t str_modulo_format(mp_obj_t pattern, size_t n_args, const mp_obj_t *args, mp_obj_t dict);
 
-STATIC mp_obj_t mp_obj_new_bytes_iterator(mp_obj_t str);
+STATIC mp_obj_t mp_obj_new_bytes_iterator(mp_obj_t str, mp_obj_iter_buf_t *iter_buf);
 STATIC NORETURN void bad_implicit_conversion(mp_obj_t self_in);
 
 /******************************************************************************/
@@ -231,7 +231,8 @@ STATIC mp_obj_t bytes_make_new(const mp_obj_type_t *type_in, size_t n_args, size
         vstr_init(&vstr, len);
     }
 
-    mp_obj_t iterable = mp_getiter(args[0]);
+    mp_obj_iter_buf_t iter_buf;
+    mp_obj_t iterable = mp_getiter(args[0], &iter_buf);
     mp_obj_t item;
     while ((item = mp_iternext(iterable)) != MP_OBJ_STOP_ITERATION) {
         mp_int_t val = mp_obj_get_int(item);
@@ -1942,7 +1943,7 @@ STATIC const mp_rom_map_elem_t str8_locals_dict_table[] = {
 STATIC MP_DEFINE_CONST_DICT(str8_locals_dict, str8_locals_dict_table);
 
 #if !MICROPY_PY_BUILTINS_STR_UNICODE
-STATIC mp_obj_t mp_obj_new_str_iterator(mp_obj_t str);
+STATIC mp_obj_t mp_obj_new_str_iterator(mp_obj_t str, mp_obj_iter_buf_t *iter_buf);
 
 const mp_obj_type_t mp_type_str = {
     { &mp_type_type },
@@ -2142,8 +2143,9 @@ STATIC mp_obj_t str_it_iternext(mp_obj_t self_in) {
     }
 }
 
-STATIC mp_obj_t mp_obj_new_str_iterator(mp_obj_t str) {
-    mp_obj_str8_it_t *o = m_new_obj(mp_obj_str8_it_t);
+STATIC mp_obj_t mp_obj_new_str_iterator(mp_obj_t str, mp_obj_iter_buf_t *iter_buf) {
+    assert(sizeof(mp_obj_str8_it_t) <= sizeof(mp_obj_iter_buf_t));
+    mp_obj_str8_it_t *o = (mp_obj_str8_it_t*)iter_buf;
     o->base.type = &mp_type_polymorph_iter;
     o->iternext = str_it_iternext;
     o->str = str;
@@ -2164,8 +2166,9 @@ STATIC mp_obj_t bytes_it_iternext(mp_obj_t self_in) {
     }
 }
 
-mp_obj_t mp_obj_new_bytes_iterator(mp_obj_t str) {
-    mp_obj_str8_it_t *o = m_new_obj(mp_obj_str8_it_t);
+mp_obj_t mp_obj_new_bytes_iterator(mp_obj_t str, mp_obj_iter_buf_t *iter_buf) {
+    assert(sizeof(mp_obj_str8_it_t) <= sizeof(mp_obj_iter_buf_t));
+    mp_obj_str8_it_t *o = (mp_obj_str8_it_t*)iter_buf;
     o->base.type = &mp_type_polymorph_iter;
     o->iternext = bytes_it_iternext;
     o->str = str;
diff --git a/py/objstringio.c b/py/objstringio.c
index a77ffae24..61a30752e 100644
--- a/py/objstringio.c
+++ b/py/objstringio.c
@@ -215,7 +215,7 @@ const mp_obj_type_t mp_type_stringio = {
     .name = MP_QSTR_StringIO,
     .print = stringio_print,
     .make_new = stringio_make_new,
-    .getiter = mp_identity,
+    .getiter = mp_identity_getiter,
     .iternext = mp_stream_unbuffered_iter,
     .protocol = &stringio_stream_p,
     .locals_dict = (mp_obj_dict_t*)&stringio_locals_dict,
@@ -227,7 +227,7 @@ const mp_obj_type_t mp_type_bytesio = {
     .name = MP_QSTR_BytesIO,
     .print = stringio_print,
     .make_new = stringio_make_new,
-    .getiter = mp_identity,
+    .getiter = mp_identity_getiter,
     .iternext = mp_stream_unbuffered_iter,
     .protocol = &bytesio_stream_p,
     .locals_dict = (mp_obj_dict_t*)&stringio_locals_dict,
diff --git a/py/objstrunicode.c b/py/objstrunicode.c
index 0091edd72..441ec293d 100644
--- a/py/objstrunicode.c
+++ b/py/objstrunicode.c
@@ -36,7 +36,7 @@
 
 #if MICROPY_PY_BUILTINS_STR_UNICODE
 
-STATIC mp_obj_t mp_obj_new_str_iterator(mp_obj_t str);
+STATIC mp_obj_t mp_obj_new_str_iterator(mp_obj_t str, mp_obj_iter_buf_t *iter_buf);
 
 /******************************************************************************/
 /* str                                                                        */
@@ -301,8 +301,9 @@ STATIC mp_obj_t str_it_iternext(mp_obj_t self_in) {
     }
 }
 
-STATIC mp_obj_t mp_obj_new_str_iterator(mp_obj_t str) {
-    mp_obj_str_it_t *o = m_new_obj(mp_obj_str_it_t);
+STATIC mp_obj_t mp_obj_new_str_iterator(mp_obj_t str, mp_obj_iter_buf_t *iter_buf) {
+    assert(sizeof(mp_obj_str_it_t) <= sizeof(mp_obj_iter_buf_t));
+    mp_obj_str_it_t *o = (mp_obj_str_it_t*)iter_buf;
     o->base.type = &mp_type_polymorph_iter;
     o->iternext = str_it_iternext;
     o->str = str;
diff --git a/py/objtuple.c b/py/objtuple.c
index 1935a6709..ad45696ca 100644
--- a/py/objtuple.c
+++ b/py/objtuple.c
@@ -32,8 +32,6 @@
 #include "py/runtime0.h"
 #include "py/runtime.h"
 
-STATIC mp_obj_t mp_obj_new_tuple_iterator(mp_obj_tuple_t *tuple, size_t cur);
-
 /******************************************************************************/
 /* tuple                                                                      */
 
@@ -84,7 +82,8 @@ STATIC mp_obj_t mp_obj_tuple_make_new(const mp_obj_type_t *type_in, size_t n_arg
             size_t len = 0;
             mp_obj_t *items = m_new(mp_obj_t, alloc);
 
-            mp_obj_t iterable = mp_getiter(args[0]);
+            mp_obj_iter_buf_t iter_buf;
+            mp_obj_t iterable = mp_getiter(args[0], &iter_buf);
             mp_obj_t item;
             while ((item = mp_iternext(iterable)) != MP_OBJ_STOP_ITERATION) {
                 if (len >= alloc) {
@@ -195,10 +194,6 @@ mp_obj_t mp_obj_tuple_subscr(mp_obj_t self_in, mp_obj_t index, mp_obj_t value) {
     }
 }
 
-mp_obj_t mp_obj_tuple_getiter(mp_obj_t o_in) {
-    return mp_obj_new_tuple_iterator(MP_OBJ_TO_PTR(o_in), 0);
-}
-
 STATIC mp_obj_t tuple_count(mp_obj_t self_in, mp_obj_t value) {
     mp_check_self(MP_OBJ_IS_TYPE(self_in, &mp_type_tuple));
     mp_obj_tuple_t *self = MP_OBJ_TO_PTR(self_in);
@@ -284,11 +279,12 @@ STATIC mp_obj_t tuple_it_iternext(mp_obj_t self_in) {
     }
 }
 
-STATIC mp_obj_t mp_obj_new_tuple_iterator(mp_obj_tuple_t *tuple, size_t cur) {
-    mp_obj_tuple_it_t *o = m_new_obj(mp_obj_tuple_it_t);
+mp_obj_t mp_obj_tuple_getiter(mp_obj_t o_in, mp_obj_iter_buf_t *iter_buf) {
+    assert(sizeof(mp_obj_tuple_it_t) <= sizeof(mp_obj_iter_buf_t));
+    mp_obj_tuple_it_t *o = (mp_obj_tuple_it_t*)iter_buf;
     o->base.type = &mp_type_polymorph_iter;
     o->iternext = tuple_it_iternext;
-    o->tuple = tuple;
-    o->cur = cur;
+    o->tuple = MP_OBJ_TO_PTR(o_in);
+    o->cur = 0;
     return MP_OBJ_FROM_PTR(o);
 }
diff --git a/py/objtuple.h b/py/objtuple.h
index 760135f86..555c3b3c2 100644
--- a/py/objtuple.h
+++ b/py/objtuple.h
@@ -44,7 +44,7 @@ void mp_obj_tuple_print(const mp_print_t *print, mp_obj_t o_in, mp_print_kind_t
 mp_obj_t mp_obj_tuple_unary_op(mp_uint_t op, mp_obj_t self_in);
 mp_obj_t mp_obj_tuple_binary_op(mp_uint_t op, mp_obj_t lhs, mp_obj_t rhs);
 mp_obj_t mp_obj_tuple_subscr(mp_obj_t base, mp_obj_t index, mp_obj_t value);
-mp_obj_t mp_obj_tuple_getiter(mp_obj_t o_in);
+mp_obj_t mp_obj_tuple_getiter(mp_obj_t o_in, mp_obj_iter_buf_t *iter_buf);
 
 extern const mp_obj_type_t mp_type_attrtuple;
 
diff --git a/py/objtype.c b/py/objtype.c
index 60f630c3d..a4c424929 100644
--- a/py/objtype.c
+++ b/py/objtype.c
@@ -758,7 +758,7 @@ mp_obj_t mp_obj_instance_call(mp_obj_t self_in, size_t n_args, size_t n_kw, cons
     return mp_call_method_self_n_kw(member[0], member[1], n_args, n_kw, args);
 }
 
-STATIC mp_obj_t instance_getiter(mp_obj_t self_in) {
+STATIC mp_obj_t instance_getiter(mp_obj_t self_in, mp_obj_iter_buf_t *iter_buf) {
     mp_obj_instance_t *self = MP_OBJ_TO_PTR(self_in);
     mp_obj_t member[2] = {MP_OBJ_NULL};
     struct class_lookup_data lookup = {
@@ -773,7 +773,7 @@ STATIC mp_obj_t instance_getiter(mp_obj_t self_in) {
         return MP_OBJ_NULL;
     } else if (member[0] == MP_OBJ_SENTINEL) {
         mp_obj_type_t *type = mp_obj_get_type(self->subobj[0]);
-        return type->getiter(self->subobj[0]);
+        return type->getiter(self->subobj[0], iter_buf);
     } else {
         return mp_call_method_n_kw(0, 0, member);
     }
diff --git a/py/objzip.c b/py/objzip.c
index 6edefc361..5d9ba48e7 100644
--- a/py/objzip.c
+++ b/py/objzip.c
@@ -43,7 +43,7 @@ STATIC mp_obj_t zip_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_
     o->base.type = type;
     o->n_iters = n_args;
     for (mp_uint_t i = 0; i < n_args; i++) {
-        o->iters[i] = mp_getiter(args[i]);
+        o->iters[i] = mp_getiter(args[i], NULL);
     }
     return MP_OBJ_FROM_PTR(o);
 }
@@ -71,6 +71,6 @@ const mp_obj_type_t mp_type_zip = {
     { &mp_type_type },
     .name = MP_QSTR_zip,
     .make_new = zip_make_new,
-    .getiter = mp_identity,
+    .getiter = mp_identity_getiter,
     .iternext = zip_iternext,
 };
diff --git a/py/runtime.c b/py/runtime.c
index 3ac30f58e..b9d7b72dc 100644
--- a/py/runtime.c
+++ b/py/runtime.c
@@ -520,7 +520,8 @@ mp_obj_t mp_binary_op(mp_uint_t op, mp_obj_t lhs, mp_obj_t rhs) {
         }
         if (type->getiter != NULL) {
             /* second attempt, walk the iterator */
-            mp_obj_t iter = mp_getiter(rhs);
+            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)) {
@@ -698,7 +699,8 @@ void mp_call_prepare_args_n_kw_var(bool have_self, size_t n_args_n_kw, const mp_
         args2_len += n_args;
 
         // extract the variable position args from the iterator
-        mp_obj_t iterable = mp_getiter(pos_seq);
+        mp_obj_iter_buf_t iter_buf;
+        mp_obj_t iterable = mp_getiter(pos_seq, &iter_buf);
         mp_obj_t item;
         while ((item = mp_iternext(iterable)) != MP_OBJ_STOP_ITERATION) {
             if (args2_len >= args2_alloc) {
@@ -743,7 +745,8 @@ void mp_call_prepare_args_n_kw_var(bool have_self, size_t n_args_n_kw, const mp_
         // get the keys iterable
         mp_obj_t dest[3];
         mp_load_method(kw_dict, MP_QSTR_keys, dest);
-        mp_obj_t iterable = mp_getiter(mp_call_method_n_kw(0, 0, dest));
+        mp_obj_iter_buf_t iter_buf;
+        mp_obj_t iterable = mp_getiter(mp_call_method_n_kw(0, 0, dest), &iter_buf);
 
         mp_obj_t key;
         while ((key = mp_iternext(iterable)) != MP_OBJ_STOP_ITERATION) {
@@ -809,7 +812,8 @@ void mp_unpack_sequence(mp_obj_t seq_in, size_t num, mp_obj_t *items) {
             items[i] = seq_items[num - 1 - i];
         }
     } else {
-        mp_obj_t iterable = mp_getiter(seq_in);
+        mp_obj_iter_buf_t iter_buf;
+        mp_obj_t iterable = mp_getiter(seq_in, &iter_buf);
 
         for (seq_len = 0; seq_len < num; seq_len++) {
             mp_obj_t el = mp_iternext(iterable);
@@ -873,7 +877,8 @@ void mp_unpack_ex(mp_obj_t seq_in, size_t num_in, mp_obj_t *items) {
         // items destination array, then the rest to a dynamically created list.  Once the
         // iterable is exhausted, we take from this list for the right part of the items.
         // TODO Improve to waste less memory in the dynamically created list.
-        mp_obj_t iterable = mp_getiter(seq_in);
+        mp_obj_iter_buf_t iter_buf;
+        mp_obj_t iterable = mp_getiter(seq_in, &iter_buf);
         mp_obj_t item;
         for (seq_len = 0; seq_len < num_left; seq_len++) {
             item = mp_iternext(iterable);
@@ -1096,13 +1101,18 @@ void mp_store_attr(mp_obj_t base, qstr attr, mp_obj_t value) {
     }
 }
 
-mp_obj_t mp_getiter(mp_obj_t o_in) {
+mp_obj_t mp_getiter(mp_obj_t o_in, mp_obj_iter_buf_t *iter_buf) {
     assert(o_in);
 
+    // if caller did not provide a buffer then allocate one on the heap
+    if (iter_buf == NULL) {
+        iter_buf = m_new_obj(mp_obj_iter_buf_t);
+    }
+
     // check for native getiter (corresponds to __iter__)
     mp_obj_type_t *type = mp_obj_get_type(o_in);
     if (type->getiter != NULL) {
-        mp_obj_t iter = type->getiter(o_in);
+        mp_obj_t iter = type->getiter(o_in, iter_buf);
         if (iter != MP_OBJ_NULL) {
             return iter;
         }
@@ -1113,7 +1123,7 @@ mp_obj_t mp_getiter(mp_obj_t o_in) {
     mp_load_method_maybe(o_in, MP_QSTR___getitem__, dest);
     if (dest[0] != MP_OBJ_NULL) {
         // __getitem__ exists, create and return an iterator
-        return mp_obj_new_getitem_iter(dest);
+        return mp_obj_new_getitem_iter(dest, iter_buf);
     }
 
     // object not iterable
diff --git a/py/runtime.h b/py/runtime.h
index e25f2a483..954833b67 100644
--- a/py/runtime.h
+++ b/py/runtime.h
@@ -123,7 +123,7 @@ void mp_load_method(mp_obj_t base, qstr attr, mp_obj_t *dest);
 void mp_load_method_maybe(mp_obj_t base, qstr attr, mp_obj_t *dest);
 void mp_store_attr(mp_obj_t base, qstr attr, mp_obj_t val);
 
-mp_obj_t mp_getiter(mp_obj_t o);
+mp_obj_t mp_getiter(mp_obj_t o, mp_obj_iter_buf_t *iter_buf);
 mp_obj_t mp_iternext_allow_raise(mp_obj_t o); // may return MP_OBJ_STOP_ITERATION instead of raising StopIteration()
 mp_obj_t mp_iternext(mp_obj_t o); // will always return MP_OBJ_STOP_ITERATION instead of raising StopIteration(...)
 mp_vm_return_kind_t mp_resume(mp_obj_t self_in, mp_obj_t send_value, mp_obj_t throw_value, mp_obj_t *ret_val);
diff --git a/py/vm.c b/py/vm.c
index 97f344b08..917d41a11 100644
--- a/py/vm.c
+++ b/py/vm.c
@@ -723,7 +723,7 @@ unwind_jump:;
 
                 ENTRY(MP_BC_GET_ITER):
                     MARK_EXC_IP_SELECTIVE();
-                    SET_TOP(mp_getiter(TOP()));
+                    SET_TOP(mp_getiter(TOP(), NULL));
                     DISPATCH();
 
                 ENTRY(MP_BC_FOR_ITER): {
diff --git a/stmhal/pybstdio.c b/stmhal/pybstdio.c
index dec4f227a..9b1bfff90 100644
--- a/stmhal/pybstdio.c
+++ b/stmhal/pybstdio.c
@@ -120,7 +120,7 @@ STATIC const mp_obj_type_t stdio_obj_type = {
     .name = MP_QSTR_FileIO,
     // TODO .make_new?
     .print = stdio_obj_print,
-    .getiter = mp_identity,
+    .getiter = mp_identity_getiter,
     .iternext = mp_stream_unbuffered_iter,
     .protocol = &stdio_obj_stream_p,
     .locals_dict = (mp_obj_t)&stdio_locals_dict,
@@ -153,7 +153,7 @@ STATIC const mp_obj_type_t stdio_buffer_obj_type = {
     { &mp_type_type },
     .name = MP_QSTR_FileIO,
     .print = stdio_obj_print,
-    .getiter = mp_identity,
+    .getiter = mp_identity_getiter,
     .iternext = mp_stream_unbuffered_iter,
     .protocol = &stdio_buffer_obj_stream_p,
     .locals_dict = (mp_obj_t)&stdio_locals_dict,
diff --git a/stmhal/uart.c b/stmhal/uart.c
index d53ca5b80..4fae3a80c 100644
--- a/stmhal/uart.c
+++ b/stmhal/uart.c
@@ -1037,7 +1037,7 @@ const mp_obj_type_t pyb_uart_type = {
     .name = MP_QSTR_UART,
     .print = pyb_uart_print,
     .make_new = pyb_uart_make_new,
-    .getiter = mp_identity,
+    .getiter = mp_identity_getiter,
     .iternext = mp_stream_unbuffered_iter,
     .protocol = &uart_stream_p,
     .locals_dict = (mp_obj_t)&pyb_uart_locals_dict,
diff --git a/stmhal/usb.c b/stmhal/usb.c
index c413ce4ba..f71c70665 100644
--- a/stmhal/usb.c
+++ b/stmhal/usb.c
@@ -518,7 +518,7 @@ const mp_obj_type_t pyb_usb_vcp_type = {
     .name = MP_QSTR_USB_VCP,
     .print = pyb_usb_vcp_print,
     .make_new = pyb_usb_vcp_make_new,
-    .getiter = mp_identity,
+    .getiter = mp_identity_getiter,
     .iternext = mp_stream_unbuffered_iter,
     .protocol = &pyb_usb_vcp_stream_p,
     .locals_dict = (mp_obj_t)&pyb_usb_vcp_locals_dict,
diff --git a/unix/file.c b/unix/file.c
index 96fe78c49..a60840c81 100644
--- a/unix/file.c
+++ b/unix/file.c
@@ -244,7 +244,7 @@ const mp_obj_type_t mp_type_fileio = {
     .name = MP_QSTR_FileIO,
     .print = fdfile_print,
     .make_new = fdfile_make_new,
-    .getiter = mp_identity,
+    .getiter = mp_identity_getiter,
     .iternext = mp_stream_unbuffered_iter,
     .protocol = &fileio_stream_p,
     .locals_dict = (mp_obj_dict_t*)&rawfile_locals_dict,
@@ -263,7 +263,7 @@ const mp_obj_type_t mp_type_textio = {
     .name = MP_QSTR_TextIOWrapper,
     .print = fdfile_print,
     .make_new = fdfile_make_new,
-    .getiter = mp_identity,
+    .getiter = mp_identity_getiter,
     .iternext = mp_stream_unbuffered_iter,
     .protocol = &textio_stream_p,
     .locals_dict = (mp_obj_dict_t*)&rawfile_locals_dict,
diff --git a/unix/modffi.c b/unix/modffi.c
index cae16c579..74194e2cc 100644
--- a/unix/modffi.c
+++ b/unix/modffi.c
@@ -194,7 +194,8 @@ STATIC mp_obj_t make_func(mp_obj_t rettype_in, void *func, mp_obj_t argtypes_in)
     o->rettype = *rettype;
     o->argtypes = argtypes;
 
-    mp_obj_t iterable = mp_getiter(argtypes_in);
+    mp_obj_iter_buf_t iter_buf;
+    mp_obj_t iterable = mp_getiter(argtypes_in, &iter_buf);
     mp_obj_t item;
     int i = 0;
     while ((item = mp_iternext(iterable)) != MP_OBJ_STOP_ITERATION) {
@@ -251,7 +252,8 @@ STATIC mp_obj_t mod_ffi_callback(mp_obj_t rettype_in, mp_obj_t func_in, mp_obj_t
 
     o->rettype = *rettype;
 
-    mp_obj_t iterable = mp_getiter(paramtypes_in);
+    mp_obj_iter_buf_t iter_buf;
+    mp_obj_t iterable = mp_getiter(paramtypes_in, &iter_buf);
     mp_obj_t item;
     int i = 0;
     while ((item = mp_iternext(iterable)) != MP_OBJ_STOP_ITERATION) {
diff --git a/unix/moduselect.c b/unix/moduselect.c
index f966efa41..76938329f 100644
--- a/unix/moduselect.c
+++ b/unix/moduselect.c
@@ -288,7 +288,7 @@ STATIC MP_DEFINE_CONST_DICT(poll_locals_dict, poll_locals_dict_table);
 STATIC const mp_obj_type_t mp_type_poll = {
     { &mp_type_type },
     .name = MP_QSTR_poll,
-    .getiter = mp_identity,
+    .getiter = mp_identity_getiter,
     .iternext = poll_iternext,
     .locals_dict = (void*)&poll_locals_dict,
 };
-- 
GitLab