diff options
Diffstat (limited to 'py')
-rw-r--r-- | py/compile.c | 30 | ||||
-rw-r--r-- | py/emit.h | 8 | ||||
-rw-r--r-- | py/emitbc.c | 5 | ||||
-rw-r--r-- | py/emitinlinethumb.c | 10 | ||||
-rw-r--r-- | py/emitnative.c | 9 | ||||
-rw-r--r-- | py/makeqstrdata.py | 22 | ||||
-rw-r--r-- | py/mkenv.mk | 52 | ||||
-rw-r--r-- | py/mkrules.mk | 92 | ||||
-rw-r--r-- | py/py.mk | 92 | ||||
-rw-r--r-- | py/qstr.c | 2 | ||||
-rw-r--r-- | py/qstr.h | 2 | ||||
-rw-r--r-- | py/qstrdefs.h | 8 | ||||
-rw-r--r-- | py/scope.c | 12 |
13 files changed, 252 insertions, 92 deletions
diff --git a/py/compile.c b/py/compile.c index 7e77832bc3..e1b62a11bf 100644 --- a/py/compile.c +++ b/py/compile.c @@ -3154,8 +3154,28 @@ mp_obj_t mp_compile(mp_parse_node_t pn, qstr source_file, bool is_repl) { } } - bool had_error = comp->had_error; - m_del_obj(compiler_t, comp); + // free the emitters +#if !MICROPY_EMIT_CPYTHON + if (emit_bc != NULL) { + emit_bc_free(emit_bc); + } +#if MICROPY_EMIT_NATIVE + if (emit_native != NULL) { +#if MICROPY_EMIT_X64 + emit_native_x64_free(emit_native); +#elif MICROPY_EMIT_THUMB + emit_native_thumb_free(emit_native); +#endif + } +#endif +#if MICROPY_EMIT_INLINE_THUMB + if (emit_inline_thumb != NULL) { + emit_inline_thumb_free(emit_inline_thumb); + } +#endif +#endif // !MICROPY_EMIT_CPYTHON + + // free the scopes uint unique_code_id = module_scope->unique_code_id; for (scope_t *s = module_scope; s;) { scope_t *next = s->next; @@ -3163,13 +3183,17 @@ mp_obj_t mp_compile(mp_parse_node_t pn, qstr source_file, bool is_repl) { s = next; } + // free the compiler + bool had_error = comp->had_error; + m_del_obj(compiler_t, comp); + if (had_error) { // TODO return a proper error message return mp_const_none; } else { #if MICROPY_EMIT_CPYTHON // can't create code, so just return true - (void)unique_code_id; // to suppress warning that module_scope is unused + (void)unique_code_id; // to suppress warning that unique_code_id is unused return mp_const_true; #else // return function that executes the outer module @@ -118,12 +118,16 @@ extern const emit_method_table_t emit_native_x64_method_table; extern const emit_method_table_t emit_native_thumb_method_table; emit_t *emit_pass1_new(qstr qstr___class__); -void emit_pass1_free(emit_t *emit); emit_t *emit_cpython_new(uint max_num_labels); emit_t *emit_bc_new(uint max_num_labels); emit_t *emit_native_x64_new(uint max_num_labels); emit_t *emit_native_thumb_new(uint max_num_labels); +void emit_pass1_free(emit_t *emit); +void emit_bc_free(emit_t *emit); +void emit_native_x64_free(emit_t *emit); +void emit_native_thumb_free(emit_t *emit); + typedef struct _emit_inline_asm_t emit_inline_asm_t; typedef struct _emit_inline_asm_method_table_t { @@ -137,3 +141,5 @@ typedef struct _emit_inline_asm_method_table_t { extern const emit_inline_asm_method_table_t emit_inline_thumb_method_table; emit_inline_asm_t *emit_inline_thumb_new(uint max_num_labels); +void emit_inline_thumb_free(emit_inline_asm_t *emit); + diff --git a/py/emitbc.c b/py/emitbc.c index c3385e0b66..10a95fbcfa 100644 --- a/py/emitbc.c +++ b/py/emitbc.c @@ -43,6 +43,11 @@ emit_t *emit_bc_new(uint max_num_labels) { return emit; } +void emit_bc_free(emit_t *emit) { + m_del(uint, emit->label_offsets, emit->max_num_labels); + m_del_obj(emit_t, emit); +} + // all functions must go through this one to emit code info static byte* emit_get_cur_to_write_code_info(emit_t* emit, int num_bytes_to_write) { //printf("emit %d\n", num_bytes_to_write); diff --git a/py/emitinlinethumb.c b/py/emitinlinethumb.c index 9d5a4206a0..c35192210c 100644 --- a/py/emitinlinethumb.c +++ b/py/emitinlinethumb.c @@ -20,13 +20,13 @@ struct _emit_inline_asm_t { int pass; scope_t *scope; - int max_num_labels; + uint max_num_labels; qstr *label_lookup; asm_thumb_t *as; }; emit_inline_asm_t *emit_inline_thumb_new(uint max_num_labels) { - emit_inline_asm_t *emit = m_new(emit_inline_asm_t, 1); + emit_inline_asm_t *emit = m_new_obj(emit_inline_asm_t); emit->max_num_labels = max_num_labels; emit->label_lookup = m_new(qstr, max_num_labels); memset(emit->label_lookup, 0, emit->max_num_labels * sizeof(qstr)); @@ -34,6 +34,12 @@ emit_inline_asm_t *emit_inline_thumb_new(uint max_num_labels) { return emit; } +void emit_inline_thumb_free(emit_inline_asm_t *emit) { + m_del(qstr, emit->label_lookup, emit->max_num_labels); + asm_thumb_free(emit->as, false); + m_del_obj(emit_inline_asm_t, emit); +} + static void emit_inline_thumb_start_pass(emit_inline_asm_t *emit, pass_kind_t pass, scope_t *scope) { emit->pass = pass; emit->scope = scope; diff --git a/py/emitnative.c b/py/emitnative.c index 6fc1742489..258aa9fce5 100644 --- a/py/emitnative.c +++ b/py/emitnative.c @@ -146,6 +146,15 @@ emit_t *EXPORT_FUN(new)(uint max_num_labels) { return emit; } +void EXPORT_FUN(free)(emit_t *emit) { +#if N_X64 + asm_x64_free(emit->as, false); +#elif N_THUMB + asm_thumb_free(emit->as, false); +#endif + m_del_obj(emit_t, emit); +} + static void emit_native_set_viper_types(emit_t *emit, bool do_viper_types) { emit->do_viper_types = do_viper_types; } diff --git a/py/makeqstrdata.py b/py/makeqstrdata.py index c4b7be42e7..c5ad708e89 100644 --- a/py/makeqstrdata.py +++ b/py/makeqstrdata.py @@ -1,6 +1,13 @@ import argparse import re +# codepoint2name is different in Python 2 to Python 3 +import platform +if platform.python_version_tuple()[0] == '2': + from htmlentitydefs import codepoint2name +elif platform.python_version_tuple()[0] == '3': + from html.entities import codepoint2name + # this must match the equivalent function in qstr.c def compute_hash(qstr): hash = 0 @@ -10,7 +17,7 @@ def compute_hash(qstr): def do_work(infiles): # read the qstrs in from the input files - qstrs = [] + qstrs = {} for infile in infiles: with open(infile, 'rt') as f: line_number = 0 @@ -23,28 +30,29 @@ def do_work(infiles): continue # verify line is of the correct form - match = re.match(r'Q\(([0-9A-Za-z_]+)\)$', line) + match = re.match(r'Q\((.+)\)$', line) if not match: print('({}:{}) bad qstr format, got {}'.format(infile, line_number, line)) return False # get the qstr value qstr = match.group(1) + ident = re.sub(r'[^A-Za-z0-9_]', lambda s: "_" + codepoint2name[ord(s.group(0))] + "_", qstr) # don't add duplicates - if qstr in qstrs: + if ident in qstrs: continue - # add the qstr to the list - qstrs.append(qstr) + # add the qstr to the list, with order number to retain original order in file + qstrs[ident] = (len(qstrs), ident, qstr) # process the qstrs, printing out the generated C header file print('// This file was automatically generated by makeqstrdata.py') print('') - for qstr in qstrs: + for order, ident, qstr in sorted(qstrs.values(), key=lambda x: x[0]): qhash = compute_hash(qstr) qlen = len(qstr) - print('Q({}, (const byte*)"\\x{:02x}\\x{:02x}\\x{:02x}\\x{:02x}" "{}")'.format(qstr, qhash & 0xff, (qhash >> 8) & 0xff, qlen & 0xff, (qlen >> 8) & 0xff, qstr)) + print('Q({}, (const byte*)"\\x{:02x}\\x{:02x}\\x{:02x}\\x{:02x}" "{}")'.format(ident, qhash & 0xff, (qhash >> 8) & 0xff, qlen & 0xff, (qlen >> 8) & 0xff, qstr)) return True diff --git a/py/mkenv.mk b/py/mkenv.mk new file mode 100644 index 0000000000..ed4e22096d --- /dev/null +++ b/py/mkenv.mk @@ -0,0 +1,52 @@ +ifneq ($(lastword a b),b) +$(error These Makefiles require make 3.81 or newer) +endif + +# Set TOP to be the path to get from the current directory (where make was +# invoked) to the top of the tree. $(lastword $(MAKEFILE_LIST)) returns +# the name of this makefile relative to where make was invoked. +# +# We assume that this file is in the py directory so we use $(dir ) twice +# to get to the top of the tree. + +THIS_MAKEFILE := $(lastword $(MAKEFILE_LIST)) +TOP := $(patsubst %/py/mkenv.mk,%,$(THIS_MAKEFILE)) + +# Turn on increased build verbosity by defining BUILD_VERBOSE in your main +# Makefile or in your environment. You can also use V=1 on the make command +# line. + +ifeq ("$(origin V)", "command line") +BUILD_VERBOSE=$(V) +endif +ifndef BUILD_VERBOSE +BUILD_VERBOSE = 0 +endif +ifeq ($(BUILD_VERBOSE),0) +Q = @ +else +Q = +endif +# Since this is a new feature, advertise it +ifeq ($(BUILD_VERBOSE),0) +$(info Use make V=1 or set BUILD_VERBOSE in your environment to increase build verbosity.) +endif + +# default settings; can be overriden in main Makefile + +PY_SRC ?= $(TOP)/py +BUILD ?= build + +RM = rm +ECHO = @echo + +AS = $(CROSS_COMPILE)as +CC = $(CROSS_COMPILE)gcc +LD = $(CROSS_COMPILE)ld +OBJCOPY = $(CROSS_COMPILE)objcopy +SIZE = $(CROSS_COMPILE)size + +all: +.PHONY: all + +MKENV_INCLUDED = 1 diff --git a/py/mkrules.mk b/py/mkrules.mk new file mode 100644 index 0000000000..4c1ea08332 --- /dev/null +++ b/py/mkrules.mk @@ -0,0 +1,92 @@ +ifneq ($(MKENV_INCLUDED),1) +# We assume that mkenv is in the same directory as this file. +THIS_MAKEFILE = $(lastword $(MAKEFILE_LIST)) +include $(dir $(THIS_MAKEFILE))mkenv.mk +endif + +# This file expects that OBJ contains a list of all of the object files. +# The directory portion of each object file is used to locate the source +# and should not contain any ..'s but rather be relative to the top of the +# tree. +# +# So for example, py/map.c would have an object file name py/map.o +# The object files will go into the build directory and mantain the same +# directory structure as the source tree. So the final dependency will look +# like this: +# +# build/py/map.o: py/map.c +# +# We set vpath to point to the top of the tree so that the source files +# can be located. By following this scheme, it allows a single build rule +# to be used to compile all .c files. + +vpath %.S . $(TOP) +$(BUILD)/%.o: %.S + $(ECHO) "CC $<" + $(Q)$(CC) $(CFLAGS) -c -o $@ $< + +vpath %.s . $(TOP) +$(BUILD)/%.o: %.s + $(ECHO) "AS $<" + $(Q)$(AS) -o $@ $< + +define compile_c +$(ECHO) "CC $<" +$(Q)$(CC) $(CFLAGS) -c -MD -o $@ $< +@# The following fixes the dependency file. +@# See http://make.paulandlesley.org/autodep.html for details. +@cp $(@:.o=.d) $(@:.o=.P); \ + sed -e 's/#.*//' -e 's/^[^:]*: *//' -e 's/ *\\$$//' \ + -e '/^$$/ d' -e 's/$$/ :/' < $(@:.o=.d) >> $(@:.o=.P); \ + rm -f $(@:.o=.d) +endef + +vpath %.c . $(TOP) +$(BUILD)/%.o: %.c + $(call compile_c) + +# The following rule uses | to create an order only prereuisite. Order only +# prerequisites only get built if they don't exist. They don't cause timestamp +# checkng to be performed. +# +# $(sort $(var)) removes duplicates +# +# The net effect of this, is it causes the objects to depend on the +# object directories (but only for existance), and the object directories +# will be created if they don't exist. +OBJ_DIRS = $(sort $(dir $(OBJ))) +$(OBJ): | $(OBJ_DIRS) +$(OBJ_DIRS): + mkdir -p $@ + +ifneq ($(PROG),) +# Build a standalone executable (unix and unix-cpy do this) + +all: $(PROG) + +$(PROG): $(OBJ) + $(ECHO) "LINK $<" + $(Q)$(CC) -o $@ $(OBJ) $(LIB) $(LDFLAGS) +ifndef DEBUG + $(Q)strip $(PROG) +endif + $(Q)size $(PROG) + +clean: clean-prog +clean-prog: + $(RM) -f $(PROG) + +.PHONY: clean-prog +endif + +clean: + $(RM) -rf $(BUILD) +.PHONY: clean + +print-cfg: + $(ECHO) "PY_SRC = $(PY_SRC)" + $(ECHO) "BUILD = $(BUILD)" + $(ECHO) "OBJ = $(OBJ)" +.PHONY: print-cfg + +-include $(OBJ:.o=.P) @@ -1,48 +1,11 @@ -########## -# The following should eventually go into a more central location -# when a reorg is done. -# -# Turn on increased build verbosity by defining BUILD_VERBOSE in your main -# Makefile or in your environment. You can also use V=1 on the make command -# line. -ifeq ("$(origin V)", "command line") -BUILD_VERBOSE=$(V) -endif -ifndef BUILD_VERBOSE -BUILD_VERBOSE = 0 -endif -ifeq ($(BUILD_VERBOSE),0) -Q = @ -else -Q = -endif -# Since this is a new feature, advertise it -ifeq ($(BUILD_VERBOSE),0) -$(info Use make V=1 or set BUILD_VERBOSE in your environment to increase build verbosity.) -endif -# -######### - -# default settings; can be overriden in main Makefile - -PY_SRC ?= ../py -BUILD ?= build - -# to create the build directory - -$(BUILD): - $(Q)mkdir -p $@ - # where py object files go (they have a name prefix to prevent filename clashes) - -PY_BUILD = $(BUILD)/py. +PY_BUILD = $(BUILD)/py # file containing qstr defs for the core Python bit PY_QSTR_DEFS = $(PY_SRC)/qstrdefs.h # py object files - PY_O_BASENAME = \ nlrx86.o \ nlrx64.o \ @@ -108,50 +71,37 @@ PY_O_BASENAME = \ repl.o \ # prepend the build destination prefix to the py object files - -PY_O = $(addprefix $(PY_BUILD), $(PY_O_BASENAME)) +PY_O = $(addprefix $(PY_BUILD)/, $(PY_O_BASENAME)) # qstr data -$(PY_BUILD)qstr.o: $(PY_BUILD)qstrdefs.generated.h - -$(PY_BUILD)qstrdefs.generated.h: $(PY_QSTR_DEFS) $(QSTR_DEFS) $(PY_SRC)/makeqstrdata.py +# Adding an order only dependency on $(PY_BUILD) causes $(PY_BUILD) to get +# created before we run the script to generate the .h +$(PY_BUILD)/qstrdefs.generated.h: | $(PY_BUILD) +$(PY_BUILD)/qstrdefs.generated.h: $(PY_QSTR_DEFS) $(QSTR_DEFS) $(PY_SRC)/makeqstrdata.py $(ECHO) "makeqstrdata $(PY_QSTR_DEFS) $(QSTR_DEFS)" $(Q)python $(PY_SRC)/makeqstrdata.py $(PY_QSTR_DEFS) $(QSTR_DEFS) > $@ -# emitters - -$(PY_BUILD)emitnx64.o: $(PY_SRC)/emitnative.c $(PY_SRC)/emit.h mpconfigport.h - $(ECHO) "CC $<" - $(Q)$(CC) $(CFLAGS) -DN_X64 -c -o $@ $< +# We don't know which source files actually need the generated.h (since +# it is #included from str.h). The compiler generated dependencies will cause +# the right .o's to get recompiled if the generated.h file changes. Adding +# an order-only dependendency to all of the .o's will cause the generated .h +# to get built before we try to compile any of them. +$(PY_O): | $(PY_BUILD)/qstrdefs.generated.h -$(PY_BUILD)emitnthumb.o: $(PY_SRC)/emitnative.c $(PY_SRC)/emit.h mpconfigport.h - $(ECHO) "CC $<" - $(Q)$(CC) $(CFLAGS) -DN_THUMB -c -o $@ $< - -# general source files +# emitters -$(PY_BUILD)%.o: $(PY_SRC)/%.S - $(ECHO) "CC $<" - $(Q)$(CC) $(CFLAGS) -c -o $@ $< +$(PY_BUILD)/emitnx64.o: CFLAGS += -DN_X64 +$(PY_BUILD)/emitnx64.o: py/emitnative.c + $(call compile_c) -$(PY_BUILD)%.o: $(PY_SRC)/%.c mpconfigport.h $(PY_SRC)/qstr.h $(PY_QSTR_DEFS) $(QSTR_DEFS) - $(ECHO) "CC $<" - $(Q)$(CC) $(CFLAGS) -c -o $@ $< +$(PY_BUILD)/emitnthumb.o: CFLAGS += -DN_THUMB +$(PY_BUILD)/emitnthumb.o: py/emitnative.c + $(call compile_c) # optimising gc for speed; 5ms down to 4ms on pybv2 -$(PY_BUILD)gc.o: $(PY_SRC)/gc.c - $(ECHO) "CC $<" - $(Q)$(CC) $(CFLAGS) -O3 -c -o $@ $< +$(PY_BUILD)gc.o: CFLAGS += -O3 # optimising vm for speed, adds only a small amount to code size but makes a huge difference to speed (20% faster) -$(PY_BUILD)vm.o: $(PY_SRC)/vm.c - $(ECHO) "CC $<" - $(Q)$(CC) $(CFLAGS) -O3 -c -o $@ $< - -# header dependencies +$(PY_BUILD)vm.o: CFLAGS += -O3 -$(PY_BUILD)parse.o: $(PY_SRC)/grammar.h -$(PY_BUILD)compile.o: $(PY_SRC)/grammar.h -$(PY_BUILD)emitcpy.o: $(PY_SRC)/emit.h -$(PY_BUILD)emitbc.o: $(PY_SRC)/emit.h @@ -55,7 +55,7 @@ const static qstr_pool_t const_pool = { (const byte*) "\0\0\0\0", // empty qstr #define Q(id, str) str, // TODO having 'build/' here is a bit of a hack, should take config variable from Makefile -#include "build/py.qstrdefs.generated.h" +#include "build/py/qstrdefs.generated.h" #undef Q }, }; @@ -9,7 +9,7 @@ enum { MP_QSTR_ = 1, // the empty qstr #define Q(id, str) MP_QSTR_##id, // TODO having 'build/py.' here is a bit of a hack, should take config variable from Makefile -#include "build/py.qstrdefs.generated.h" +#include "build/py/qstrdefs.generated.h" #undef Q MP_QSTR_number_of, } category_t; diff --git a/py/qstrdefs.h b/py/qstrdefs.h index 9bc01c5851..f2c4dfd97f 100644 --- a/py/qstrdefs.h +++ b/py/qstrdefs.h @@ -80,3 +80,11 @@ Q(sort) Q(join) Q(strip) Q(format) + +Q(<module>) +Q(<lambda>) +Q(<listcomp>) +Q(<dictcomp>) +Q(<setcomp>) +Q(<genexpr>) +Q(<stdin>) diff --git a/py/scope.c b/py/scope.c index 1f602ac9c0..ab20f61f37 100644 --- a/py/scope.c +++ b/py/scope.c @@ -18,7 +18,7 @@ scope_t *scope_new(scope_kind_t kind, mp_parse_node_t pn, qstr source_file, uint scope->source_file = source_file; switch (kind) { case SCOPE_MODULE: - scope->simple_name = QSTR_FROM_STR_STATIC("<module>"); + scope->simple_name = MP_QSTR__lt_module_gt_; break; case SCOPE_FUNCTION: case SCOPE_CLASS: @@ -26,19 +26,19 @@ scope_t *scope_new(scope_kind_t kind, mp_parse_node_t pn, qstr source_file, uint scope->simple_name = MP_PARSE_NODE_LEAF_ARG(((mp_parse_node_struct_t*)pn)->nodes[0]); break; case SCOPE_LAMBDA: - scope->simple_name = QSTR_FROM_STR_STATIC("<lambda>"); + scope->simple_name = MP_QSTR__lt_lambda_gt_; break; case SCOPE_LIST_COMP: - scope->simple_name = QSTR_FROM_STR_STATIC("<listcomp>"); + scope->simple_name = MP_QSTR__lt_listcomp_gt_; break; case SCOPE_DICT_COMP: - scope->simple_name = QSTR_FROM_STR_STATIC("<dictcomp>"); + scope->simple_name = MP_QSTR__lt_dictcomp_gt_; break; case SCOPE_SET_COMP: - scope->simple_name = QSTR_FROM_STR_STATIC("<setcomp>"); + scope->simple_name = MP_QSTR__lt_setcomp_gt_; break; case SCOPE_GEN_EXPR: - scope->simple_name = QSTR_FROM_STR_STATIC("<genexpr>"); + scope->simple_name = MP_QSTR__lt_genexpr_gt_; break; default: assert(0); |