diff --git a/py/emitglue.c b/py/emitglue.c
index 4bd9027317cc8e2b81b546060d4c4a5dca1e9665..7265a206a87301eb52473debbfde9f5ea1d6868e 100644
--- a/py/emitglue.c
+++ b/py/emitglue.c
@@ -74,7 +74,7 @@ mp_raw_code_t *mp_emit_glue_new_raw_code(void) {
 }
 
 void mp_emit_glue_assign_bytecode(mp_raw_code_t *rc, byte *code, uint len, uint n_pos_args, uint n_kwonly_args, qstr *arg_names, uint scope_flags) {
-    rc->kind = MP_CODE_BYTE;
+    rc->kind = MP_CODE_BYTECODE;
     rc->scope_flags = scope_flags;
     rc->n_pos_args = n_pos_args;
     rc->n_kwonly_args = n_kwonly_args;
@@ -104,40 +104,15 @@ void mp_emit_glue_assign_bytecode(mp_raw_code_t *rc, byte *code, uint len, uint
 #endif
 }
 
-void mp_emit_glue_assign_native_code(mp_raw_code_t *rc, void *fun, uint len, int n_args) {
-    rc->kind = MP_CODE_NATIVE;
+void mp_emit_glue_assign_native(mp_raw_code_t *rc, mp_raw_code_kind_t kind, void *fun, uint len, int n_args) {
+    assert(kind == MP_CODE_NATIVE_PY || kind == MP_CODE_NATIVE_VIPER || kind == MP_CODE_NATIVE_ASM);
+    rc->kind = kind;
     rc->scope_flags = 0;
     rc->n_pos_args = n_args;
     rc->u_native.fun = fun;
 
 #ifdef DEBUG_PRINT
-    DEBUG_printf("assign native code: fun=%p len=%u n_args=%d\n", fun, len, n_args);
-    byte *fun_data = (byte*)(((machine_uint_t)fun) & (~1)); // need to clear lower bit in case it's thumb code
-    for (int i = 0; i < 128 && i < len; i++) {
-        if (i > 0 && i % 16 == 0) {
-            DEBUG_printf("\n");
-        }
-        DEBUG_printf(" %02x", fun_data[i]);
-    }
-    DEBUG_printf("\n");
-
-#ifdef WRITE_CODE
-    if (fp_write_code != NULL) {
-        fwrite(fun_data, len, 1, fp_write_code);
-        fflush(fp_write_code);
-    }
-#endif
-#endif
-}
-
-void mp_emit_glue_assign_inline_asm_code(mp_raw_code_t *rc, void *fun, uint len, int n_args) {
-    rc->kind = MP_CODE_INLINE_ASM;
-    rc->scope_flags = 0;
-    rc->n_pos_args = n_args;
-    rc->u_inline_asm.fun = fun;
-
-#ifdef DEBUG_PRINT
-    DEBUG_printf("assign inline asm code: fun=%p len=%u n_args=%d\n", fun, len, n_args);
+    DEBUG_printf("assign native: kind=%d fun=%p len=%u n_args=%d\n", kind, fun, len, n_args);
     byte *fun_data = (byte*)(((machine_uint_t)fun) & (~1)); // need to clear lower bit in case it's thumb code
     for (int i = 0; i < 128 && i < len; i++) {
         if (i > 0 && i % 16 == 0) {
@@ -169,14 +144,15 @@ mp_obj_t mp_make_function_from_raw_code(mp_raw_code_t *rc, mp_obj_t def_args, mp
     // make the function, depending on the raw code kind
     mp_obj_t fun;
     switch (rc->kind) {
-        case MP_CODE_BYTE:
+        case MP_CODE_BYTECODE:
             fun = mp_obj_new_fun_bc(rc->scope_flags, rc->arg_names, rc->n_pos_args, rc->n_kwonly_args, def_args, rc->u_byte.code);
             break;
-        case MP_CODE_NATIVE:
+        case MP_CODE_NATIVE_PY:
             fun = mp_make_function_n(rc->n_pos_args, rc->u_native.fun);
             break;
-        case MP_CODE_INLINE_ASM:
-            fun = mp_obj_new_fun_asm(rc->n_pos_args, rc->u_inline_asm.fun);
+        case MP_CODE_NATIVE_VIPER:
+        case MP_CODE_NATIVE_ASM:
+            fun = mp_obj_new_fun_asm(rc->n_pos_args, rc->u_native.fun);
             break;
         default:
             // raw code was never set (this should not happen)
diff --git a/py/emitglue.h b/py/emitglue.h
index bf1a440516bc64b362f65fba651a2540bdf070a9..43bfb5e08a6ad0afd9b237d53ab0baf7ddc61bac 100644
--- a/py/emitglue.h
+++ b/py/emitglue.h
@@ -29,9 +29,10 @@
 typedef enum {
     MP_CODE_UNUSED,
     MP_CODE_RESERVED,
-    MP_CODE_BYTE,
-    MP_CODE_NATIVE,
-    MP_CODE_INLINE_ASM,
+    MP_CODE_BYTECODE,
+    MP_CODE_NATIVE_PY,
+    MP_CODE_NATIVE_VIPER,
+    MP_CODE_NATIVE_ASM,
 } mp_raw_code_kind_t;
 
 typedef struct _mp_code_t {
@@ -45,12 +46,9 @@ typedef struct _mp_code_t {
             byte *code;
             uint len;
         } u_byte;
-        struct {
-            mp_fun_t fun;
-        } u_native;
         struct {
             void *fun;
-        } u_inline_asm;
+        } u_native;
     };
 } mp_raw_code_t;
 
@@ -60,8 +58,7 @@ void mp_emit_glue_deinit(void);
 mp_raw_code_t *mp_emit_glue_new_raw_code(void);
 
 void mp_emit_glue_assign_bytecode(mp_raw_code_t *rc, byte *code, uint len, uint n_pos_args, uint n_kwonly_args, qstr *arg_names, uint scope_flags);
-void mp_emit_glue_assign_native_code(mp_raw_code_t *rc, void *f, uint len, int n_args);
-void mp_emit_glue_assign_inline_asm_code(mp_raw_code_t *rc, void *f, uint len, int n_args);
+void mp_emit_glue_assign_native(mp_raw_code_t *rc, mp_raw_code_kind_t kind, void *f, uint len, int n_args);
 
 mp_obj_t mp_make_function_from_raw_code(mp_raw_code_t *rc, mp_obj_t def_args, mp_obj_t def_kw_args);
 mp_obj_t mp_make_closure_from_raw_code(mp_raw_code_t *rc, uint n_closed_over, const mp_obj_t *args);
diff --git a/py/emitinlinethumb.c b/py/emitinlinethumb.c
index 83ee7f69bf6ee116709917702c9c5666bfe6db19..79ed1c4a0279704c071fb4be74d766b6103f5b1e 100644
--- a/py/emitinlinethumb.c
+++ b/py/emitinlinethumb.c
@@ -99,7 +99,7 @@ STATIC bool emit_inline_thumb_end_pass(emit_inline_asm_t *emit) {
 
     if (emit->pass == MP_PASS_EMIT) {
         void *f = asm_thumb_get_code(emit->as);
-        mp_emit_glue_assign_inline_asm_code(emit->scope->raw_code, f, asm_thumb_get_code_size(emit->as), emit->scope->num_pos_args);
+        mp_emit_glue_assign_native(emit->scope->raw_code, MP_CODE_NATIVE_ASM, f, asm_thumb_get_code_size(emit->as), emit->scope->num_pos_args);
     }
 
     return emit->success;
diff --git a/py/emitnative.c b/py/emitnative.c
index 4ce21e9c0e8fe5c18f033ee013409b20b1e7d6a7..261b1a2a5195281db6e407f9f3586b623fe4a31c 100644
--- a/py/emitnative.c
+++ b/py/emitnative.c
@@ -309,10 +309,10 @@ STATIC void emit_native_end_pass(emit_t *emit) {
     if (emit->pass == MP_PASS_EMIT) {
 #if N_X64
         void *f = asm_x64_get_code(emit->as);
-        mp_emit_glue_assign_native_code(emit->scope->raw_code, f, asm_x64_get_code_size(emit->as), emit->scope->num_pos_args);
+        mp_emit_glue_assign_native(emit->scope->raw_code, emit->do_viper_types ? MP_CODE_NATIVE_VIPER : MP_CODE_NATIVE_PY, f, asm_x64_get_code_size(emit->as), emit->scope->num_pos_args);
 #elif N_THUMB
         void *f = asm_thumb_get_code(emit->as);
-        mp_emit_glue_assign_native_code(emit->scope->raw_code, f, asm_thumb_get_code_size(emit->as), emit->scope->num_pos_args);
+        mp_emit_glue_assign_native(emit->scope->raw_code, emit->do_viper_types ? MP_CODE_NATIVE_VIPER : MP_CODE_NATIVE_PY, f, asm_thumb_get_code_size(emit->as), emit->scope->num_pos_args);
 #endif
     }
 }
@@ -438,6 +438,11 @@ STATIC void emit_access_stack(emit_t *emit, int pos, vtype_kind_t *vtype, int re
     }
 }
 
+STATIC void emit_pre_pop_discard(emit_t *emit, vtype_kind_t *vtype) {
+    emit->last_emit_was_return_value = false;
+    adjust_stack(emit, -1);
+}
+
 STATIC void emit_pre_pop_reg(emit_t *emit, vtype_kind_t *vtype, int reg_dest) {
     emit->last_emit_was_return_value = false;
     emit_access_stack(emit, 1, vtype, reg_dest);
@@ -938,7 +943,7 @@ STATIC void emit_native_dup_top_two(emit_t *emit) {
 
 STATIC void emit_native_pop_top(emit_t *emit) {
     vtype_kind_t vtype;
-    emit_pre_pop_reg(emit, &vtype, REG_TEMP0);
+    emit_pre_pop_discard(emit, &vtype);
     emit_post(emit);
 }
 
diff --git a/py/objfun.c b/py/objfun.c
index f5fbf95806d6f4cee1bb9873086e75a64d70d1c1..8994486c85ffbb79e05c40befa2b055462b25ae8 100644
--- a/py/objfun.c
+++ b/py/objfun.c
@@ -506,12 +506,7 @@ STATIC mp_obj_t convert_val_from_inline_asm(machine_uint_t val) {
 STATIC mp_obj_t fun_asm_call(mp_obj_t self_in, uint n_args, uint n_kw, const mp_obj_t *args) {
     mp_obj_fun_asm_t *self = self_in;
 
-    if (n_args != self->n_args) {
-        nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_TypeError, "function takes %d positional arguments but %d were given", self->n_args, n_args));
-    }
-    if (n_kw != 0) {
-        nlr_raise(mp_obj_new_exception_msg(&mp_type_TypeError, "function does not take keyword arguments"));
-    }
+    mp_arg_check_num(n_args, n_kw, self->n_args, self->n_args, false);
 
     machine_uint_t ret;
     if (n_args == 0) {