diff --git a/py/obj.h b/py/obj.h
index 88d9253ec6e94b83af8e064227e1343909623f9d..eff021e70cb1cf402a00b4eb5c809f968af0fa60 100644
--- a/py/obj.h
+++ b/py/obj.h
@@ -422,6 +422,7 @@ mp_obj_t mp_obj_complex_binary_op(int op, mp_float_t lhs_real, mp_float_t lhs_im
 void mp_obj_tuple_get(mp_obj_t self_in, uint *len, mp_obj_t **items);
 void mp_obj_tuple_del(mp_obj_t self_in);
 machine_int_t mp_obj_tuple_hash(mp_obj_t self_in);
+mp_obj_t mp_obj_tuple_make_new(mp_obj_t type_in, uint n_args, uint n_kw, const mp_obj_t *args);
 
 // list
 mp_obj_t mp_obj_list_append(mp_obj_t self_in, mp_obj_t arg);
@@ -485,7 +486,7 @@ typedef struct _mp_obj_static_class_method_t {
 void mp_seq_multiply(const void *items, uint item_sz, uint len, uint times, void *dest);
 bool m_seq_get_fast_slice_indexes(machine_uint_t len, mp_obj_t slice, machine_uint_t *begin, machine_uint_t *end);
 #define m_seq_copy(dest, src, len, item_t) memcpy(dest, src, len * sizeof(item_t))
-#define m_seq_cat(dest, src1, len1, src2, len2, item_t) { memcpy(dest, src1, len1 * sizeof(item_t)); memcpy(dest + len1, src2, len2 * sizeof(item_t)); }
+#define m_seq_cat(dest, src1, len1, src2, len2, item_t) { memcpy(dest, src1, (len1) * sizeof(item_t)); memcpy(dest + (len1), src2, (len2) * sizeof(item_t)); }
 bool mp_seq_cmp_bytes(int op, const byte *data1, uint len1, const byte *data2, uint len2);
 bool mp_seq_cmp_objs(int op, const mp_obj_t *items1, uint len1, const mp_obj_t *items2, uint len2);
 mp_obj_t mp_seq_index_obj(const mp_obj_t *items, uint len, uint n_args, const mp_obj_t *args);
diff --git a/py/objtuple.c b/py/objtuple.c
index 61866406042646fb86cf17a849b5ef7680c8b73f..7f145090290a23f7e011bffa29021ac9ed830ced 100644
--- a/py/objtuple.c
+++ b/py/objtuple.c
@@ -30,7 +30,7 @@ void tuple_print(void (*print)(void *env, const char *fmt, ...), void *env, mp_o
     print(env, ")");
 }
 
-STATIC mp_obj_t tuple_make_new(mp_obj_t type_in, uint n_args, uint n_kw, const mp_obj_t *args) {
+mp_obj_t mp_obj_tuple_make_new(mp_obj_t type_in, uint n_args, uint n_kw, const mp_obj_t *args) {
     // TODO check n_kw == 0
 
     switch (n_args) {
@@ -175,7 +175,7 @@ const mp_obj_type_t mp_type_tuple = {
     { &mp_type_type },
     .name = MP_QSTR_tuple,
     .print = tuple_print,
-    .make_new = tuple_make_new,
+    .make_new = mp_obj_tuple_make_new,
     .unary_op = tuple_unary_op,
     .binary_op = tuple_binary_op,
     .getiter = tuple_getiter,
diff --git a/py/vm.c b/py/vm.c
index bcd13389e9fbabcd9d160c18dc243df7d809fb30..7195d941eecbcaac386092d0c56bb1f0c8dab610 100644
--- a/py/vm.c
+++ b/py/vm.c
@@ -1,4 +1,5 @@
 #include <stdio.h>
+#include <string.h>
 #include <assert.h>
 
 #include "nlr.h"
@@ -10,6 +11,7 @@
 #include "bc0.h"
 #include "bc.h"
 #include "objgenerator.h"
+#include "objtuple.h"
 
 // Value stack grows up (this makes it incompatible with native C stack, but
 // makes sure that arguments to functions are in natural order arg1..argN
@@ -665,6 +667,32 @@ unwind_jump:
                         SET_TOP(mp_call_function_n_kw(*sp, unum & 0xff, (unum >> 8) & 0xff, sp + 1));
                         break;
 
+                    case MP_BC_CALL_FUNCTION_VAR: {
+                        DECODE_UINT;
+                        // unum & 0xff == n_positional
+                        // (unum >> 8) & 0xff == n_keyword
+                        // We have folowing stack layout here:
+                        // arg0 arg1 ... kw0 val0 kw1 val1 ... seq <- TOS
+                        // We need to splice seq after all positional args and before kwargs
+                        // TODO: optimize one day to avoid constructing new arg array? Will be hard.
+                        mp_obj_t seq = POP();
+                        int total_stack_args = (unum & 0xff) + ((unum >> 7) & 0x1fe);
+                        sp -= total_stack_args;
+
+                        // Convert vararg sequence to tuple. Note that it can be arbitrary iterator.
+                        // This is null call for tuple, and TODO: we actually could optimize case of list.
+                        mp_obj_tuple_t *varargs = mp_obj_tuple_make_new(MP_OBJ_NULL, 1, 0, &seq);
+
+                        int pos_args_len = (unum & 0xff) + varargs->len;
+                        mp_obj_t *args = m_new(mp_obj_t, total_stack_args + varargs->len);
+                        m_seq_cat(args, sp + 1, unum & 0xff, varargs->items, varargs->len, mp_obj_t);
+                        m_seq_copy(args + pos_args_len, sp + (unum & 0xff) + 1, ((unum >> 7) & 0x1fe), mp_obj_t);
+
+                        SET_TOP(mp_call_function_n_kw(*sp, pos_args_len, (unum >> 8) & 0xff, args));
+                        m_del(mp_obj_t, args, total_stack_args + varargs->len);
+                        break;
+                    }
+
                     case MP_BC_CALL_METHOD:
                         DECODE_UINT;
                         // unum & 0xff == n_positional
diff --git a/tests/basics/fun-callstar.py b/tests/basics/fun-callstar.py
new file mode 100644
index 0000000000000000000000000000000000000000..49b40d95946a93ba8cf781ba1dfbed17229cfc84
--- /dev/null
+++ b/tests/basics/fun-callstar.py
@@ -0,0 +1,13 @@
+def foo(a, b, c):
+    print(a, b, c)
+
+foo(*(1, 2, 3))
+foo(1, *(2, 3))
+foo(1, 2, *(3,))
+foo(1, 2, 3, *())
+
+# Another sequence type
+foo(1, 2, *[100])
+
+# Iterator
+foo(*range(3))