Skip to content

Commit 86de21b

Browse files
committed
py: Viper can call functions with native types, and raise exceptions.
1 parent 339bdcc commit 86de21b

6 files changed

Lines changed: 146 additions & 92 deletions

File tree

py/emitnative.c

Lines changed: 77 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -530,56 +530,6 @@ STATIC void emit_post_push_reg_reg_reg_reg(emit_t *emit, vtype_kind_t vtypea, in
530530
emit_post_push_reg(emit, vtyped, regd);
531531
}
532532

533-
// vtype of all n_pop objects is VTYPE_PYOBJ
534-
// does not use any temporary registers (but may use reg_dest before loading it with stack pointer)
535-
// TODO this needs some thinking for viper code
536-
STATIC void emit_get_stack_pointer_to_reg_for_pop(emit_t *emit, int reg_dest, int n_pop) {
537-
need_reg_all(emit);
538-
for (int i = 0; i < n_pop; i++) {
539-
stack_info_t *si = &emit->stack_info[emit->stack_size - 1 - i];
540-
// must push any imm's to stack
541-
// must convert them to VTYPE_PYOBJ for viper code
542-
if (si->kind == STACK_IMM) {
543-
si->kind = STACK_VALUE;
544-
switch (si->vtype) {
545-
case VTYPE_PYOBJ:
546-
ASM_MOV_IMM_TO_LOCAL_USING(si->u_imm, emit->stack_start + emit->stack_size - 1 - i, reg_dest);
547-
break;
548-
case VTYPE_BOOL:
549-
si->vtype = VTYPE_PYOBJ;
550-
if (si->u_imm == 0) {
551-
ASM_MOV_IMM_TO_LOCAL_USING((mp_uint_t)mp_const_false, emit->stack_start + emit->stack_size - 1 - i, reg_dest);
552-
} else {
553-
ASM_MOV_IMM_TO_LOCAL_USING((mp_uint_t)mp_const_true, emit->stack_start + emit->stack_size - 1 - i, reg_dest);
554-
}
555-
break;
556-
case VTYPE_INT:
557-
si->vtype = VTYPE_PYOBJ;
558-
ASM_MOV_IMM_TO_LOCAL_USING((si->u_imm << 1) | 1, emit->stack_start + emit->stack_size - 1 - i, reg_dest);
559-
break;
560-
default:
561-
// not handled
562-
assert(0);
563-
}
564-
}
565-
assert(si->kind == STACK_VALUE);
566-
assert(si->vtype == VTYPE_PYOBJ);
567-
}
568-
adjust_stack(emit, -n_pop);
569-
ASM_MOV_LOCAL_ADDR_TO_REG(emit->stack_start + emit->stack_size, reg_dest);
570-
}
571-
572-
// vtype of all n_push objects is VTYPE_PYOBJ
573-
STATIC void emit_get_stack_pointer_to_reg_for_push(emit_t *emit, int reg_dest, int n_push) {
574-
need_reg_all(emit);
575-
for (int i = 0; i < n_push; i++) {
576-
emit->stack_info[emit->stack_size + i].kind = STACK_VALUE;
577-
emit->stack_info[emit->stack_size + i].vtype = VTYPE_PYOBJ;
578-
}
579-
ASM_MOV_LOCAL_ADDR_TO_REG(emit->stack_start + emit->stack_size, reg_dest);
580-
adjust_stack(emit, n_push);
581-
}
582-
583533
STATIC void emit_call(emit_t *emit, mp_fun_kind_t fun_kind, void *fun) {
584534
need_reg_all(emit);
585535
#if N_X64
@@ -634,26 +584,84 @@ STATIC void emit_call_with_3_imm_args_and_first_aligned(emit_t *emit, mp_fun_kin
634584
#endif
635585
}
636586

637-
STATIC void emit_native_load_id(emit_t *emit, qstr qstr) {
638-
// check for built-ins
639-
if (strcmp(qstr_str(qstr), "v_int") == 0) {
640-
assert(0);
641-
emit_native_pre(emit);
642-
//emit_post_push_blank(emit, VTYPE_BUILTIN_V_INT);
587+
// vtype of all n_pop objects is VTYPE_PYOBJ
588+
// Will convert any items that are not VTYPE_PYOBJ to this type and put them back on the stack.
589+
// If any conversions of non-immediate values are needed, then it uses REG_ARG_1, REG_ARG_2 and REG_RET.
590+
// Otherwise, it does not use any temporary registers (but may use reg_dest before loading it with stack pointer).
591+
STATIC void emit_get_stack_pointer_to_reg_for_pop(emit_t *emit, mp_uint_t reg_dest, mp_uint_t n_pop) {
592+
need_reg_all(emit);
643593

644-
// not a built-in, so do usual thing
645-
} else {
646-
emit_common_load_id(emit, &EXPORT_FUN(method_table), emit->scope, qstr);
594+
// First, store any immediate values to their respective place on the stack.
595+
for (mp_uint_t i = 0; i < n_pop; i++) {
596+
stack_info_t *si = &emit->stack_info[emit->stack_size - 1 - i];
597+
// must push any imm's to stack
598+
// must convert them to VTYPE_PYOBJ for viper code
599+
if (si->kind == STACK_IMM) {
600+
si->kind = STACK_VALUE;
601+
switch (si->vtype) {
602+
case VTYPE_PYOBJ:
603+
ASM_MOV_IMM_TO_LOCAL_USING(si->u_imm, emit->stack_start + emit->stack_size - 1 - i, reg_dest);
604+
break;
605+
case VTYPE_BOOL:
606+
if (si->u_imm == 0) {
607+
ASM_MOV_IMM_TO_LOCAL_USING((mp_uint_t)mp_const_false, emit->stack_start + emit->stack_size - 1 - i, reg_dest);
608+
} else {
609+
ASM_MOV_IMM_TO_LOCAL_USING((mp_uint_t)mp_const_true, emit->stack_start + emit->stack_size - 1 - i, reg_dest);
610+
}
611+
si->vtype = VTYPE_PYOBJ;
612+
break;
613+
case VTYPE_INT:
614+
case VTYPE_UINT:
615+
ASM_MOV_IMM_TO_LOCAL_USING((si->u_imm << 1) | 1, emit->stack_start + emit->stack_size - 1 - i, reg_dest);
616+
si->vtype = VTYPE_PYOBJ;
617+
break;
618+
default:
619+
// not handled
620+
assert(0);
621+
}
622+
}
623+
624+
// verify that this value is on the stack
625+
assert(si->kind == STACK_VALUE);
626+
}
627+
628+
// Second, convert any non-VTYPE_PYOBJ to that type.
629+
for (mp_uint_t i = 0; i < n_pop; i++) {
630+
stack_info_t *si = &emit->stack_info[emit->stack_size - 1 - i];
631+
if (si->vtype != VTYPE_PYOBJ) {
632+
mp_uint_t local_num = emit->stack_start + emit->stack_size - 1 - i;
633+
ASM_MOV_LOCAL_TO_REG(local_num, REG_ARG_1);
634+
emit_call_with_imm_arg(emit, MP_F_CONVERT_NATIVE_TO_OBJ, mp_convert_native_to_obj, si->vtype, REG_ARG_2); // arg2 = type
635+
ASM_MOV_REG_TO_LOCAL(REG_RET, local_num);
636+
si->vtype = VTYPE_PYOBJ;
637+
}
638+
}
639+
640+
// Adujust the stack for a pop of n_pop items, and load the stack pointer into reg_dest.
641+
adjust_stack(emit, -n_pop);
642+
ASM_MOV_LOCAL_ADDR_TO_REG(emit->stack_start + emit->stack_size, reg_dest);
643+
}
644+
645+
// vtype of all n_push objects is VTYPE_PYOBJ
646+
STATIC void emit_get_stack_pointer_to_reg_for_push(emit_t *emit, mp_uint_t reg_dest, mp_uint_t n_push) {
647+
need_reg_all(emit);
648+
for (mp_uint_t i = 0; i < n_push; i++) {
649+
emit->stack_info[emit->stack_size + i].kind = STACK_VALUE;
650+
emit->stack_info[emit->stack_size + i].vtype = VTYPE_PYOBJ;
647651
}
652+
ASM_MOV_LOCAL_ADDR_TO_REG(emit->stack_start + emit->stack_size, reg_dest);
653+
adjust_stack(emit, n_push);
654+
}
655+
656+
STATIC void emit_native_load_id(emit_t *emit, qstr qstr) {
657+
emit_common_load_id(emit, &EXPORT_FUN(method_table), emit->scope, qstr);
648658
}
649659

650660
STATIC void emit_native_store_id(emit_t *emit, qstr qstr) {
651-
// TODO check for built-ins and disallow
652661
emit_common_store_id(emit, &EXPORT_FUN(method_table), emit->scope, qstr);
653662
}
654663

655664
STATIC void emit_native_delete_id(emit_t *emit, qstr qstr) {
656-
// TODO check for built-ins and disallow
657665
emit_common_delete_id(emit, &EXPORT_FUN(method_table), emit->scope, qstr);
658666
}
659667

@@ -1396,7 +1404,7 @@ STATIC void emit_native_call_function(emit_t *emit, int n_positional, int n_keyw
13961404
vtype_kind_t vtype_fun;
13971405
emit_pre_pop_reg(emit, &vtype_fun, REG_ARG_1); // the function
13981406
assert(vtype_fun == VTYPE_PYOBJ);
1399-
emit_call_with_imm_arg(emit, MP_F_CALL_FUNCTION_N_KW_FOR_NATIVE, mp_call_function_n_kw_for_native, n_positional | (n_keyword << 8), REG_ARG_2);
1407+
emit_call_with_imm_arg(emit, MP_F_NATIVE_CALL_FUNCTION_N_KW, mp_native_call_function_n_kw, n_positional | (n_keyword << 8), REG_ARG_2);
14001408
emit_post_push_reg(emit, VTYPE_PYOBJ, REG_RET);
14011409
}
14021410

@@ -1453,13 +1461,13 @@ STATIC void emit_native_return_value(emit_t *emit) {
14531461

14541462
STATIC void emit_native_raise_varargs(emit_t *emit, int n_args) {
14551463
assert(n_args == 1);
1456-
vtype_kind_t vtype_err;
1457-
emit_pre_pop_reg(emit, &vtype_err, REG_ARG_1); // arg1 = object to raise
1458-
assert(vtype_err == VTYPE_PYOBJ);
1459-
emit_call(emit, 0, mp_make_raise_obj); // TODO need to add function to runtime table
1460-
emit_post_push_reg(emit, VTYPE_PYOBJ, REG_RET);
1461-
emit_pre_pop_reg(emit, &vtype_err, REG_ARG_1);
1462-
emit_call(emit, 0, nlr_jump); // TODO need to add function to runtime table
1464+
vtype_kind_t vtype_exc;
1465+
emit_pre_pop_reg(emit, &vtype_exc, REG_ARG_1); // arg1 = object to raise
1466+
if (vtype_exc != VTYPE_PYOBJ) {
1467+
printf("ViperTypeError: must raise an object\n");
1468+
}
1469+
// TODO probably make this 1 call to the runtime (which could even call convert, native_raise(obj, type))
1470+
emit_call(emit, MP_F_NATIVE_RAISE, mp_native_raise);
14631471
}
14641472

14651473
STATIC void emit_native_yield_value(emit_t *emit) {

py/runtime.c

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -517,12 +517,6 @@ mp_obj_t mp_call_function_2(mp_obj_t fun, mp_obj_t arg1, mp_obj_t arg2) {
517517
return mp_call_function_n_kw(fun, 2, 0, args);
518518
}
519519

520-
// wrapper that accepts n_args and n_kw in one argument
521-
// native emitter can only pass at most 3 arguments to a function
522-
mp_obj_t mp_call_function_n_kw_for_native(mp_obj_t fun_in, uint n_args_kw, const mp_obj_t *args) {
523-
return mp_call_function_n_kw(fun_in, n_args_kw & 0xff, (n_args_kw >> 8) & 0xff, args);
524-
}
525-
526520
// args contains, eg: arg0 arg1 key0 value0 key1 value1
527521
mp_obj_t mp_call_function_n_kw(mp_obj_t fun_in, uint n_args, uint n_kw, const mp_obj_t *args) {
528522
// TODO improve this: fun object can specify its type and we parse here the arguments,
@@ -1187,6 +1181,17 @@ mp_obj_t mp_convert_native_to_obj(mp_uint_t val, mp_uint_t type) {
11871181
}
11881182
}
11891183

1184+
// wrapper that accepts n_args and n_kw in one argument
1185+
// (native emitter can only pass at most 3 arguments to a function)
1186+
mp_obj_t mp_native_call_function_n_kw(mp_obj_t fun_in, uint n_args_kw, const mp_obj_t *args) {
1187+
return mp_call_function_n_kw(fun_in, n_args_kw & 0xff, (n_args_kw >> 8) & 0xff, args);
1188+
}
1189+
1190+
// wrapper that makes raise obj and raises it
1191+
NORETURN void mp_native_raise(mp_obj_t o) {
1192+
nlr_raise(mp_make_raise_obj(o));
1193+
}
1194+
11901195
// these must correspond to the respective enum
11911196
void *const mp_fun_table[MP_F_NUMBER_OF] = {
11921197
mp_convert_obj_to_native,
@@ -1216,10 +1221,11 @@ void *const mp_fun_table[MP_F_NUMBER_OF] = {
12161221
mp_obj_set_store,
12171222
#endif
12181223
mp_make_function_from_raw_code,
1219-
mp_call_function_n_kw_for_native,
1224+
mp_native_call_function_n_kw,
12201225
mp_call_method_n_kw,
12211226
mp_getiter,
12221227
mp_iternext,
1228+
mp_native_raise,
12231229
mp_import_name,
12241230
mp_import_from,
12251231
mp_import_all,

py/runtime.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,6 @@ mp_obj_t mp_make_function_var_between(int n_args_min, int n_args_max, mp_fun_var
8787
mp_obj_t mp_call_function_0(mp_obj_t fun);
8888
mp_obj_t mp_call_function_1(mp_obj_t fun, mp_obj_t arg);
8989
mp_obj_t mp_call_function_2(mp_obj_t fun, mp_obj_t arg1, mp_obj_t arg2);
90-
mp_obj_t mp_call_function_n_kw_for_native(mp_obj_t fun_in, uint n_args_kw, const mp_obj_t *args);
9190
mp_obj_t mp_call_function_n_kw(mp_obj_t fun, uint n_args, uint n_kw, const mp_obj_t *args);
9291
mp_obj_t mp_call_method_n_kw(uint n_args, uint n_kw, const mp_obj_t *args);
9392
mp_obj_t mp_call_method_n_kw_var(bool have_self, uint n_args_n_kw, const mp_obj_t *args);
@@ -115,8 +114,11 @@ void mp_import_all(mp_obj_t module);
115114
// Raise NotImplementedError with given message
116115
NORETURN void mp_not_implemented(const char *msg);
117116

117+
// helper functions for native/viper code
118118
mp_uint_t mp_convert_obj_to_native(mp_obj_t obj, mp_uint_t type);
119119
mp_obj_t mp_convert_native_to_obj(mp_uint_t val, mp_uint_t type);
120+
mp_obj_t mp_native_call_function_n_kw(mp_obj_t fun_in, uint n_args_kw, const mp_obj_t *args);
121+
NORETURN void mp_native_raise(mp_obj_t o);
120122

121123
extern struct _mp_obj_list_t mp_sys_path_obj;
122124
extern struct _mp_obj_list_t mp_sys_argv_obj;

py/runtime0.h

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -129,12 +129,13 @@ typedef enum {
129129
MP_F_STORE_SET,
130130
#endif
131131
MP_F_MAKE_FUNCTION_FROM_RAW_CODE,
132-
MP_F_CALL_FUNCTION_N_KW_FOR_NATIVE,
132+
MP_F_NATIVE_CALL_FUNCTION_N_KW,
133133
MP_F_CALL_METHOD_N_KW,
134134
MP_F_GETITER,
135135
MP_F_ITERNEXT,
136+
MP_F_NATIVE_RAISE,
136137
MP_F_IMPORT_NAME,
137-
MP_F_IMPORT_FROM,
138+
MP_F_IMPORT_FROM, // = 31 XXX this is the limit for thumb code...
138139
MP_F_IMPORT_ALL,
139140
#if MICROPY_PY_BUILTINS_SLICE
140141
MP_F_NEW_SLICE,

tests/micropython/viper.py

Lines changed: 45 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2,48 +2,80 @@
22

33
# viper function taking and returning ints
44
@micropython.viper
5-
def f(x:int, y:int) -> int:
5+
def viper_int(x:int, y:int) -> int:
66
return x + y + 3
7+
print(viper_int(1, 2))
78

89
# viper function taking and returning objects
910
@micropython.viper
10-
def g(x:object, y:object) -> object:
11+
def viper_object(x:object, y:object) -> object:
1112
return x + y
13+
print(viper_object(1, 2))
1214

1315
# a local (should have automatic type int)
1416
@micropython.viper
15-
def h(x:int) -> int:
17+
def viper_local(x:int) -> int:
1618
y = 4
1719
return x + y
20+
print(viper_local(3))
1821

1922
# without type annotation, types should default to object
2023
@micropython.viper
21-
def i(x, y):
24+
def viper_no_annotation(x, y):
2225
return x * y
26+
print(viper_no_annotation(4, 5))
2327

2428
# a for loop
2529
@micropython.viper
26-
def viper_sum(a:int, b:int) -> int:
30+
def viper_for(a:int, b:int) -> int:
2731
total = 0
2832
for x in range(a, b):
2933
total += x
3034
return total
35+
print(viper_for(10, 10000))
3136

3237
# accessing a global
3338
@micropython.viper
34-
def access_global():
39+
def viper_access_global():
3540
global gl
3641
gl = 1
3742
return gl
43+
print(viper_access_global(), gl)
44+
45+
# calling print with object and int types
46+
@micropython.viper
47+
def viper_print(x, y:int):
48+
print(x, y + 1)
49+
viper_print(1, 2)
50+
51+
# making a tuple from an object and an int
52+
@micropython.viper
53+
def viper_tuple(x, y:int):
54+
return (x, y + 1)
55+
print(viper_tuple(1, 2))
56+
57+
# making a list from an object and an int
58+
@micropython.viper
59+
def viper_list(x, y:int):
60+
return [x, y + 1]
61+
print(viper_list(1, 2))
62+
63+
# making a set from an object and an int
64+
@micropython.viper
65+
def viper_set(x, y:int):
66+
return {x, y + 1}
67+
print(sorted(list(viper_set(1, 2))))
68+
69+
# raising an exception
70+
@micropython.viper
71+
def viper_raise(x:int):
72+
raise SystemError(x)
73+
try:
74+
viper_raise(1)
75+
except SystemError as e:
76+
print(repr(e))
3877

3978
# this doesn't work at the moment
4079
#@micropython.viper
4180
#def g() -> uint:
4281
# return -1
43-
44-
print(f(1, 2))
45-
print(g(1, 2))
46-
print(h(3))
47-
print(i(4, 5))
48-
print(viper_sum(10, 10000))
49-
print(access_global(), gl)

tests/micropython/viper.py.exp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,8 @@
44
20
55
49994955
66
1 1
7+
1 3
8+
(1, 3)
9+
[1, 3]
10+
[1, 3]
11+
SystemError(1,)

0 commit comments

Comments
 (0)