Skip to content

Commit 2c915e1

Browse files
committed
py: Implement basic with support in native emitter.
1 parent ce8b4e8 commit 2c915e1

File tree

3 files changed

+116
-13
lines changed

3 files changed

+116
-13
lines changed

py/compile.c

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1624,6 +1624,11 @@ STATIC void compile_with_stmt_helper(compiler_t *comp, int n, mp_parse_node_t *n
16241624
compile_node(comp, body);
16251625
} else {
16261626
uint l_end = comp_next_label(comp);
1627+
if (MICROPY_EMIT_NATIVE && comp->scope_cur->emit_options != MP_EMIT_OPT_BYTECODE) {
1628+
// we need to allocate an extra label for the native emitter
1629+
// it will use l_end+1 as an auxiliary label
1630+
comp_next_label(comp);
1631+
}
16271632
if (MP_PARSE_NODE_IS_STRUCT_KIND(nodes[0], PN_with_item)) {
16281633
// this pre-bit is of the form "a as b"
16291634
mp_parse_node_struct_t *pns = (mp_parse_node_struct_t*)nodes[0];

py/emitnative.c

Lines changed: 110 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1984,15 +1984,114 @@ STATIC void emit_native_continue_loop(emit_t *emit, mp_uint_t label, mp_uint_t e
19841984
}
19851985

19861986
STATIC void emit_native_setup_with(emit_t *emit, mp_uint_t label) {
1987-
// not supported, or could be with runtime call
1988-
(void)emit;
1989-
(void)label;
1990-
assert(0);
1987+
// the context manager is on the top of the stack
1988+
// stack: (..., ctx_mgr)
1989+
1990+
// get __exit__ method
1991+
vtype_kind_t vtype;
1992+
emit_access_stack(emit, 1, &vtype, REG_ARG_1); // arg1 = ctx_mgr
1993+
assert(vtype == VTYPE_PYOBJ);
1994+
emit_get_stack_pointer_to_reg_for_push(emit, REG_ARG_3, 2); // arg3 = dest ptr
1995+
emit_call_with_imm_arg(emit, MP_F_LOAD_METHOD, MP_QSTR___exit__, REG_ARG_2);
1996+
// stack: (..., ctx_mgr, __exit__, self)
1997+
1998+
emit_pre_pop_reg(emit, &vtype, REG_ARG_3); // self
1999+
emit_pre_pop_reg(emit, &vtype, REG_ARG_2); // __exit__
2000+
emit_pre_pop_reg(emit, &vtype, REG_ARG_1); // ctx_mgr
2001+
emit_post_push_reg(emit, vtype, REG_ARG_2); // __exit__
2002+
emit_post_push_reg(emit, vtype, REG_ARG_3); // self
2003+
// stack: (..., __exit__, self)
2004+
// REG_ARG_1=ctx_mgr
2005+
2006+
// get __enter__ method
2007+
emit_get_stack_pointer_to_reg_for_push(emit, REG_ARG_3, 2); // arg3 = dest ptr
2008+
emit_call_with_imm_arg(emit, MP_F_LOAD_METHOD, MP_QSTR___enter__, REG_ARG_2); // arg2 = method name
2009+
// stack: (..., __exit__, self, __enter__, self)
2010+
2011+
// call __enter__ method
2012+
emit_get_stack_pointer_to_reg_for_pop(emit, REG_ARG_3, 2); // pointer to items, including meth and self
2013+
emit_call_with_2_imm_args(emit, MP_F_CALL_METHOD_N_KW, 0, REG_ARG_1, 0, REG_ARG_2);
2014+
emit_post_push_reg(emit, VTYPE_PYOBJ, REG_RET); // push return value of __enter__
2015+
// stack: (..., __exit__, self, as_value)
2016+
2017+
// need to commit stack because we may jump elsewhere
2018+
need_stack_settled(emit);
2019+
emit_get_stack_pointer_to_reg_for_push(emit, REG_ARG_1, sizeof(nlr_buf_t) / sizeof(mp_uint_t)); // arg1 = pointer to nlr buf
2020+
emit_call(emit, MP_F_NLR_PUSH);
2021+
ASM_JUMP_IF_REG_NONZERO(emit->as, REG_RET, label);
2022+
2023+
emit_access_stack(emit, sizeof(nlr_buf_t) / sizeof(mp_uint_t) + 1, &vtype, REG_RET); // access return value of __enter__
2024+
emit_post_push_reg(emit, VTYPE_PYOBJ, REG_RET); // push return value of __enter__
2025+
// stack: (..., __exit__, self, as_value, nlr_buf, as_value)
19912026
}
19922027

19932028
STATIC void emit_native_with_cleanup(emit_t *emit, mp_uint_t label) {
1994-
(void)emit;
1995-
assert(0);
2029+
// note: label+1 is available as an auxiliary label
2030+
2031+
// stack: (..., __exit__, self, as_value, nlr_buf)
2032+
emit_native_pre(emit);
2033+
emit_call(emit, MP_F_NLR_POP);
2034+
adjust_stack(emit, -(mp_int_t)(sizeof(nlr_buf_t) / sizeof(mp_uint_t)) - 1);
2035+
// stack: (..., __exit__, self)
2036+
2037+
// call __exit__
2038+
emit_post_push_imm(emit, VTYPE_PYOBJ, (mp_uint_t)mp_const_none);
2039+
emit_post_push_imm(emit, VTYPE_PYOBJ, (mp_uint_t)mp_const_none);
2040+
emit_post_push_imm(emit, VTYPE_PYOBJ, (mp_uint_t)mp_const_none);
2041+
emit_get_stack_pointer_to_reg_for_pop(emit, REG_ARG_3, 5);
2042+
emit_call_with_2_imm_args(emit, MP_F_CALL_METHOD_N_KW, 3, REG_ARG_1, 0, REG_ARG_2);
2043+
2044+
// jump to after with cleanup nlr_catch block
2045+
adjust_stack(emit, 1); // dummy nlr_buf.prev
2046+
emit_native_load_const_tok(emit, MP_TOKEN_KW_NONE); // nlr_buf.ret_val = no exception
2047+
emit_native_jump(emit, label + 1);
2048+
2049+
// nlr_catch
2050+
emit_native_label_assign(emit, label);
2051+
2052+
// adjust stack counter for: __exit__, self, as_value
2053+
adjust_stack(emit, 3);
2054+
// stack: (..., __exit__, self, as_value, nlr_buf.prev, nlr_buf.ret_val)
2055+
2056+
vtype_kind_t vtype;
2057+
emit_pre_pop_reg(emit, &vtype, REG_ARG_1); // get the thrown value (exc)
2058+
adjust_stack(emit, -2); // discard nlr_buf.prev and as_value
2059+
// stack: (..., __exit__, self)
2060+
// REG_ARG_1=exc
2061+
2062+
emit_pre_pop_reg(emit, &vtype, REG_ARG_2); // self
2063+
emit_pre_pop_reg(emit, &vtype, REG_ARG_3); // __exit__
2064+
adjust_stack(emit, 1); // dummy nlr_buf.prev
2065+
emit_post_push_reg(emit, vtype, REG_ARG_1); // push exc to save it for later
2066+
emit_post_push_reg(emit, vtype, REG_ARG_3); // __exit__
2067+
emit_post_push_reg(emit, vtype, REG_ARG_2); // self
2068+
// stack: (..., exc, __exit__, self)
2069+
// REG_ARG_1=exc
2070+
2071+
ASM_LOAD_REG_REG_OFFSET(emit->as, REG_ARG_2, REG_ARG_1, 0); // get type(exc)
2072+
emit_post_push_reg(emit, VTYPE_PYOBJ, REG_ARG_2); // push type(exc)
2073+
emit_post_push_reg(emit, VTYPE_PYOBJ, REG_ARG_1); // push exc value
2074+
emit_post_push_imm(emit, VTYPE_PYOBJ, (mp_uint_t)mp_const_none); // traceback info
2075+
// stack: (..., exc, __exit__, self, type(exc), exc, traceback)
2076+
2077+
// call __exit__ method
2078+
emit_get_stack_pointer_to_reg_for_pop(emit, REG_ARG_3, 5);
2079+
emit_call_with_2_imm_args(emit, MP_F_CALL_METHOD_N_KW, 3, REG_ARG_1, 0, REG_ARG_2);
2080+
// stack: (..., exc)
2081+
2082+
// if REG_RET is true then we need to replace top-of-stack with None (swallow exception)
2083+
if (REG_ARG_1 != REG_RET) {
2084+
ASM_MOV_REG_REG(emit->as, REG_ARG_1, REG_RET);
2085+
}
2086+
emit_call(emit, MP_F_OBJ_IS_TRUE);
2087+
ASM_JUMP_IF_REG_ZERO(emit->as, REG_RET, label + 1);
2088+
2089+
// replace exc with None
2090+
emit_pre_pop_discard(emit);
2091+
emit_post_push_imm(emit, VTYPE_PYOBJ, (mp_uint_t)mp_const_none);
2092+
2093+
// end of with cleanup nlr_catch block
2094+
emit_native_label_assign(emit, label + 1);
19962095
}
19972096

19982097
STATIC void emit_native_setup_except(emit_t *emit, mp_uint_t label) {
@@ -2016,7 +2115,8 @@ STATIC void emit_native_end_finally(emit_t *emit) {
20162115
// else: raise exc
20172116
// the check if exc is None is done in the MP_F_NATIVE_RAISE stub
20182117
vtype_kind_t vtype;
2019-
emit_pre_pop_reg(emit, &vtype, REG_ARG_1);
2118+
emit_pre_pop_reg(emit, &vtype, REG_ARG_1); // get nlr_buf.ret_val
2119+
emit_pre_pop_discard(emit); // discard nlr_buf.prev
20202120
emit_call(emit, MP_F_NATIVE_RAISE);
20212121
emit_post(emit);
20222122
}
@@ -2053,7 +2153,7 @@ STATIC void emit_native_for_iter_end(emit_t *emit) {
20532153
STATIC void emit_native_pop_block(emit_t *emit) {
20542154
emit_native_pre(emit);
20552155
emit_call(emit, MP_F_NLR_POP);
2056-
adjust_stack(emit, -(mp_int_t)(sizeof(nlr_buf_t) / sizeof(mp_uint_t)));
2156+
adjust_stack(emit, -(mp_int_t)(sizeof(nlr_buf_t) / sizeof(mp_uint_t)) + 1);
20572157
emit_post(emit);
20582158
}
20592159

@@ -2495,15 +2595,15 @@ STATIC void emit_native_start_except_handler(emit_t *emit) {
24952595
// This instruction follows an nlr_pop, so the stack counter is back to zero, when really
24962596
// it should be up by a whole nlr_buf_t. We then want to pop the nlr_buf_t here, but save
24972597
// the first 2 elements, so we can get the thrown value.
2498-
adjust_stack(emit, 2);
2598+
adjust_stack(emit, 1);
24992599
vtype_kind_t vtype_nlr;
25002600
emit_pre_pop_reg(emit, &vtype_nlr, REG_ARG_1); // get the thrown value
25012601
emit_pre_pop_discard(emit); // discard the linked-list pointer in the nlr_buf
25022602
emit_post_push_reg_reg_reg(emit, VTYPE_PYOBJ, REG_ARG_1, VTYPE_PYOBJ, REG_ARG_1, VTYPE_PYOBJ, REG_ARG_1); // push the 3 exception items
25032603
}
25042604

25052605
STATIC void emit_native_end_except_handler(emit_t *emit) {
2506-
adjust_stack(emit, -2);
2606+
adjust_stack(emit, -1);
25072607
}
25082608

25092609
const emit_method_table_t EXPORT_FUN(method_table) = {

tests/run-tests

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -244,7 +244,7 @@ def run_tests(pyb, tests, args):
244244
skip_tests.update({'basics/%s.py' % t for t in 'gen_yield_from gen_yield_from_close gen_yield_from_ducktype gen_yield_from_exc gen_yield_from_iter gen_yield_from_send gen_yield_from_throw generator1 generator2 generator_args generator_close generator_closure generator_exc generator_return generator_send'.split()}) # require yield
245245
skip_tests.update({'basics/%s.py' % t for t in 'bytes_gen class_store_class globals_del string_join'.split()}) # require yield
246246
skip_tests.update({'basics/%s.py' % t for t in 'try_reraise try_reraise2'.split()}) # require raise_varargs
247-
skip_tests.update({'basics/%s.py' % t for t in 'with1 with_break with_continue with_return'.split()}) # require with
247+
skip_tests.update({'basics/%s.py' % t for t in 'with_break with_continue with_return'.split()}) # require complete with support
248248
skip_tests.add('basics/array_construct2.py') # requires generators
249249
skip_tests.add('basics/bool1.py') # seems to randomly fail
250250
skip_tests.add('basics/class_bind_self.py') # requires yield
@@ -257,8 +257,6 @@ def run_tests(pyb, tests, args):
257257
skip_tests.add('basics/try_finally_return2.py') # requires proper try finally code
258258
skip_tests.add('basics/unboundlocal.py') # requires checking for unbound local
259259
skip_tests.add('import/gen_context.py') # requires yield_value
260-
skip_tests.add('io/file_with.py') # requires with
261-
skip_tests.add('io/stringio_with.py') # requires with
262260
skip_tests.add('misc/features.py') # requires raise_varargs
263261
skip_tests.add('misc/rge_sm.py') # requires yield
264262
skip_tests.add('misc/print_exception.py') # because native doesn't have proper traceback info

0 commit comments

Comments
 (0)