# HG changeset patch # User Paul Boddie # Date 1638061401 -3600 # Node ID e986990044652cd2b3c04fe85d4baa5d54be94fb # Parent 64fe7b9073ac0b8ec154c54a6789703bd1632188 Introduced support for values that can be allocated on a special thread-local stack, copied around and overwritten/mutated, demonstrating the concept using integer and floating-point numbers. Various complications arise with special attributes (such as __data__) due to the way references are tagged to indicate mutable values, and attribute slots must be cleared in locals, objects and fragments before values are stored since target slots are tested for mutable values. diff -r 64fe7b9073ac -r e98699004465 encoders.py --- a/encoders.py Sun Nov 14 00:50:17 2021 +0100 +++ b/encoders.py Sun Nov 28 02:03:21 2021 +0100 @@ -3,7 +3,7 @@ """ Encoder functions, producing representations of program objects. -Copyright (C) 2016, 2017, 2018 Paul Boddie +Copyright (C) 2016, 2017, 2018, 2021 Paul Boddie This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software @@ -286,6 +286,16 @@ "", "", "", "", "" ) +# Instructions that need to be customised for certain attributes. + +via_object_ops = ( + "__store_via_object", "__check_and_store_via_object", + ) + +# NOTE: This duplicates a definition in transresults. + +special_attributes = ("__args__", "__data__", "__key__", "__size__") + def encode_access_instruction(instruction, subs, accessor_index, context_index): """ @@ -379,6 +389,11 @@ elif not args: op = "&%s" % encode_path(op) + # Substitute the operation for certain attributes. + + if op in via_object_ops and args[1] in special_attributes: + op = "%s_internal" % op + return "%s%s" % (op, argstr), substituted def encode_access_instruction_arg(arg, subs, op, accessor_index, context_index): diff -r 64fe7b9073ac -r e98699004465 generator.py --- a/generator.py Sun Nov 14 00:50:17 2021 +0100 +++ b/generator.py Sun Nov 28 02:03:21 2021 +0100 @@ -192,6 +192,7 @@ print >>f_code, """\ #include #include +#include #include "gc.h" #include "signals.h" #include "types.h" @@ -277,8 +278,9 @@ # Write a table for all objects. table = [] - self.populate_table(Reference(kind, path), table) - self.write_table(f_decls, f_defs, table_name, structure_size, table) + self.populate_table(ref, table) + self.write_table(f_decls, f_defs, table_name, structure_size, + table, ref) # Generate function instances. @@ -320,6 +322,9 @@ # Signature: __attr (...); parameters = self.importer.function_parameters[path] + + # Include the context plus the original parameters. + l = ["__attr"] * (len(parameters) + 1) print >>f_signatures, "__attr %s(%s);" % (encode_function_pointer(path), ", ".join(l)) @@ -766,26 +771,41 @@ f_consts.write(" %s = %d" % (pos_encoder(attrname), i)) print >>f_consts, "\n };" - def write_table(self, f_decls, f_defs, table_name, structure_size, table): + def write_table(self, f_decls, f_defs, table_name, structure_size, table, + ref): """ Write the declarations to 'f_decls' and definitions to 'f_defs' for the object having the given 'table_name' and the given 'structure_size', with 'table' details used to populate the definition. + + To support value copying, the 'ref' is provided to be able to encode the + structure size. """ print >>f_decls, "extern const __table %s;\n" % table_name + # Generate an object size of 0 for non-copyable types. + + path = ref.get_origin() + + if ref.get_kind() == "" and self.trailing_data_types.has_key(path): + obj_type = encode_symbol("obj", path) or "__obj" + obj_size = "sizeof(%s)" % obj_type + else: + obj_size = "0" + # Write the corresponding definition. print >>f_defs, """\ const __table %s = { %s, + %s, { %s } }; -""" % (table_name, structure_size, +""" % (table_name, structure_size, obj_size, ",\n ".join(table)) def write_parameter_table(self, f_decls, f_defs, table_name, min_parameters, @@ -1137,13 +1157,24 @@ the 'attrs' mapping, adding entries to the 'trailing' member collection. """ + if self.get_trailing_data_type(ref): + trailing.append(encode_literal_constant_value(attrs["__trailing__"])) + + def get_trailing_data_type(self, ref): + + """ + For the structure having the given 'ref', return the type of any + trailing data. + """ + structure_ref = self.get_target_structure(ref) # Instances with trailing data. - if structure_ref.get_kind() == "" and \ - structure_ref.get_origin() in self.trailing_data_types: - trailing.append(encode_literal_constant_value(attrs["__trailing__"])) + if structure_ref.get_kind() == "": + return self.trailing_data_types.get(structure_ref.get_origin()) + else: + return None def get_target_structure(self, ref): @@ -1300,28 +1331,44 @@ """ print >>f_code, """\ +_Thread_local __attr __stack; + +/* Global used to prevent compiler over-optimisation. */ + +__attr __stack_global; + int main(int argc, char *argv[]) { __exc __tmp_exc; + __attr __stack_base; GC_INIT(); __signals_install_handlers(); + __stack = __stack_init(); + + /* Record the stack accessor on the function stack for libgc. */ + + __stack_base = __stack; + __stack_global = __stack_base; __Try {""" + # Write a main function invocation for all but the native modules. + for name in self.importer.order_modules(): + parts = name.split(".") + + if parts[0] == "native": + continue + function_name = "__main_%s" % encode_path(name) print >>f_signatures, "void %s();" % function_name - - # Omit the native modules. + print >>f_code, """\ + %s();""" % function_name - parts = name.split(".") - - if parts[0] != "native": - print >>f_code, """\ - %s();""" % function_name + # Finish the main section with an exception handler. print >>f_code, """\ } diff -r 64fe7b9073ac -r e98699004465 templates/native/common.c --- a/templates/native/common.c Sun Nov 14 00:50:17 2021 +0100 +++ b/templates/native/common.c Sun Nov 28 02:03:21 2021 +0100 @@ -29,7 +29,7 @@ __attr __new_int(__int n) { /* Create a new int and set the trailing data. */ - __attr attr = __NEWINSTANCEIM(__builtins___int_int); + __attr attr = __NEWINSTANCE_STACK(__builtins___int_int); __set_trailing_data(attr, __builtins___int_int, n); return __TO_MUTABLE(attr); } @@ -38,9 +38,9 @@ { /* Create a new string and mutate the __data__, __size__ and __key__ attributes. */ __attr attr = __NEWINSTANCE(__builtins___str_str); - __store_via_object(__VALUE(attr), __data__, (__attr) {.strvalue=s}); - __store_via_object(__VALUE(attr), __size__, (__attr) {.sizevalue=size}); - __store_via_object(__VALUE(attr), __key__, __NULL); + __store_via_object_internal(__VALUE(attr), __data__, (__attr) {.strvalue=s}); + __store_via_object_internal(__VALUE(attr), __size__, (__attr) {.sizevalue=size}); + __store_via_object_internal(__VALUE(attr), __key__, __NULL); return attr; } @@ -48,14 +48,14 @@ { /* Create a new list and mutate the __data__ attribute. */ __attr attr = __NEWINSTANCE(__builtins___list_list); - __store_via_object(__VALUE(attr), __data__, (__attr) {.seqvalue=f}); + __store_via_object_internal(__VALUE(attr), __data__, (__attr) {.seqvalue=f}); return attr; } __attr __new_float(__float n) { /* Create a new float and set the trailing data. */ - __attr attr = __NEWINSTANCEIM(__builtins___float_float); + __attr attr = __NEWINSTANCE_STACK(__builtins___float_float); __set_trailing_data(attr, __builtins___float_float, n); return __TO_MUTABLE(attr); } @@ -70,13 +70,14 @@ if (size >= capacity) { /* NOTE: Consider various restrictions on capacity increases. */ - n = capacity ? capacity * 2 : 1; + n = capacity * 2 > size ? capacity * 2 : size + 1; newdata = (__fragment *) __REALLOCATE(data, __FRAGMENT_SIZE(n)); newdata->capacity = n; } /* Insert the new element and increment the list size. */ - newdata->attrs[size] = value; + newdata->attrs[size] = __RAWVALUE(0); + __store_target(&newdata->attrs[size], value); newdata->size = size + 1; return newdata; diff -r 64fe7b9073ac -r e98699004465 templates/native/iconv.c --- a/templates/native/iconv.c Sun Nov 14 00:50:17 2021 +0100 +++ b/templates/native/iconv.c Sun Nov 28 02:03:21 2021 +0100 @@ -87,8 +87,10 @@ /* Mutate the state to indicate the next input buffer position. */ - f->attrs[1] = __new_int(start + remaining - inbytesleft); - f->attrs[2] = __new_int(inbytesleft); + f->attrs[1] = __RAWVALUE(0); + f->attrs[2] = __RAWVALUE(0); + __store_target(&f->attrs[1], __new_int(start + remaining - inbytesleft)); + __store_target(&f->attrs[2], __new_int(inbytesleft)); /* Incomplete sequence: raise the string in an OSError instead. */ diff -r 64fe7b9073ac -r e98699004465 templates/native/list.c --- a/templates/native/list.c Sun Nov 14 00:50:17 2021 +0100 +++ b/templates/native/list.c Sun Nov 28 02:03:21 2021 +0100 @@ -56,7 +56,7 @@ /* Replace the __data__ attribute if appropriate. */ if (newdata != data) - __store_via_object(__VALUE(self), __data__, ((__attr) {.seqvalue=newdata})); + __store_via_object_internal(__VALUE(self), __data__, ((__attr) {.seqvalue=newdata})); return __builtins___none_None; } @@ -79,12 +79,16 @@ /* Copy the elements from the other list and increment the list size. */ for (i = size, j = 0; j < other_size; i++, j++) - newdata->attrs[i] = other_data->attrs[j]; + { + newdata->attrs[i] = __RAWVALUE(0); + __store_target(&newdata->attrs[i], other_data->attrs[j]); + } + newdata->size = n; /* Replace the __data__ attribute if appropriate. */ if (newdata != data) - __store_via_object(__VALUE(self), __data__, ((__attr) {.seqvalue=newdata})); + __store_via_object_internal(__VALUE(self), __data__, ((__attr) {.seqvalue=newdata})); return __builtins___none_None; } @@ -120,7 +124,7 @@ __int i = __TOINT(index); /* Set the element. */ - elements[i] = value; + __store_target(&elements[i], value); return __builtins___none_None; } diff -r 64fe7b9073ac -r e98699004465 templates/ops.c --- a/templates/ops.c Sun Nov 14 00:50:17 2021 +0100 +++ b/templates/ops.c Sun Nov 28 02:03:21 2021 +0100 @@ -37,6 +37,24 @@ return (pos < obj->table->size) && (obj->table->attrs[pos] == code); } +/* Generic load wrapper employing temporary stack storage. */ + +__attr __load(__attr value) +{ + /* Copy values where appropriate. */ + + if (__COPYABLE(value)) + { + size_t value_size = __INSTANCE_SIZE(__VALUE(value)); + __attr target = __stack_allocate(__stack, value_size); + + __COPY_TO(__VALUE(value), __VALUE(target), value_size); + return __TO_MUTABLE(target); + } + else + return value; +} + /* Direct access and manipulation of static objects. */ __attr __load_static_ignore(__ref obj) @@ -76,8 +94,71 @@ /* Direct storage operations. */ +__attr __store_local(__attr target, __attr value) +{ + /* Copy values where appropriate. */ + + if (__COPYABLE(value)) + { + size_t value_size = __INSTANCE_SIZE(__VALUE(value)); + + /* Allocate new space for values if the target is not a mutable value + or refers to an object that is too small for the value. */ + + if ((__VALUE(target) == NULL) || !__MUTABLE(target) || (value_size > __INSTANCE_SIZE(__VALUE(target)))) + target = __stack_allocate(__stack, value_size); + + __COPY_TO(__VALUE(value), __VALUE(target), value_size); + return __TO_MUTABLE(target); + } + else + return value; +} + +void __store_target(__attr *target, __attr value) +{ + /* Copy values where appropriate. */ + + if (__COPYABLE(value)) + { + size_t value_size = __INSTANCE_SIZE(__VALUE(value)); + + if ((__VALUE(*target) == NULL) || (!__MUTABLE(*target)) || (value_size > __INSTANCE_SIZE(__VALUE(*target)))) + *target = __ATTRVALUE(__ALLOCATEIM(1, value_size)); + + __COPY_TO(__VALUE(value), __VALUE(*target), value_size); + *target = __TO_MUTABLE(*target); + } + else + *target = value; +} + int __store_via_object__(__ref obj, int pos, __attr value) { + /* Copy values where appropriate. */ + + if (__COPYABLE(value)) + { + size_t value_size = __INSTANCE_SIZE(__VALUE(value)); + __attr target = obj->attrs[pos]; + + /* Allocate new space for values if the target is not a mutable value + or refers to an object that is too small for the value. */ + + if ((__VALUE(target) == NULL) || (!__MUTABLE(target)) || (value_size > __INSTANCE_SIZE(__VALUE(target)))) + target = __ATTRVALUE(__ALLOCATEIM(1, value_size)); + + __COPY_TO(__VALUE(value), __VALUE(target), value_size); + obj->attrs[pos] = __TO_MUTABLE(target); + } + else + obj->attrs[pos] = value; + + return 1; +} + +int __store_via_object_internal__(__ref obj, int pos, __attr value) +{ obj->attrs[pos] = value; return 1; } @@ -215,6 +296,20 @@ return 0; } +int __check_and_store_via_object_internal__(__ref obj, int pos, int code, __attr value) +{ + if (__HASATTR(obj, pos, code)) + { + __store_via_object_internal__(obj, pos, value); + return 1; + } + + /* No suitable attribute. */ + + __raise_type_error(); + return 0; +} + int __check_and_store_via_any__(__ref obj, int pos, int code, __attr value) { if (__check_and_store_via_object__(obj, pos, code, value)) @@ -438,9 +533,95 @@ /* Copying of structures. */ -__ref __COPY(__ref obj, int size) +__ref __COPY(__ref obj, size_t size) { __ref copy = (__ref) __ALLOCATE(1, size); memcpy(copy, obj, size); return copy; } + +void __COPY_TO(__ref source, __ref target, size_t size) +{ + memcpy(target, source, size); +} + +/* Stack management. */ + +__attr __stack_init() +{ + __attr __stack; + + __stack.stackdesc = (__stackdesc *) __ALLOCATE(1, sizeof(__stackdesc)); + __stack.stackdesc->current = NULL; + __stack_expand(__stack); + + return __stack; +} + +__attr __stack_allocate(__attr __stack, size_t size) +{ + char *result; + __section *section = __stack.stackdesc->current; + + if (section->limit - section->level < size) + { + __stack_expand(__stack); + section = __stack.stackdesc->current; + } + + result = section->level; + section->level += size; + + return __ATTRVALUE(result); +} + +void __stack_expand(__attr __stack) +{ + __section *current = __stack.stackdesc->current; + __section *section = (__section *) __ALLOCATE(1, sizeof(__section)); + char *base = (char *) __ALLOCATEIM(1, __STACK_SECTION_SIZE); + + section->base = base; + section->level = base; + section->limit = base + __STACK_SECTION_SIZE; + section->previous = current; + + __stack.stackdesc->current = section; +} + +__attr __return(__attr result, __section *section, char *level) +{ + __ref obj = __VALUE(result); + char *target = level; + size_t size; + + if (__COPYABLE(result)) + { + size = __INSTANCE_SIZE(obj); + + /* Test for space in the section. */ + + if (size > section->limit - level) + { + __stack_expand(__stack); + section = __stack.stackdesc->current; + target = section->base; + } + + /* Copy into the stack and adjust the level. */ + + __COPY_TO(obj, (__ref) target, size); + level = target + size; + + /* Reference the new location of the object. */ + + result = __MUTABLEVALUE(target); + } + + /* Set the level and current section.*/ + + section->level = level; + __stack.stackdesc->current = section; + + return result; +} diff -r 64fe7b9073ac -r e98699004465 templates/ops.h --- a/templates/ops.h Sun Nov 14 00:50:17 2021 +0100 +++ b/templates/ops.h Sun Nov 28 02:03:21 2021 +0100 @@ -44,12 +44,15 @@ /* Direct storage operations. */ +void __store_target(__attr *target, __attr value); int __store_via_class__(__ref obj, int pos, __attr value); int __store_via_object__(__ref obj, int pos, __attr value); +int __store_via_object_internal__(__ref obj, int pos, __attr value); int __get_class_and_store__(__ref obj, int pos, __attr value); #define __store_via_class(OBJ, ATTRNAME, VALUE) (__store_via_class__(OBJ, __ATTRPOS(ATTRNAME), VALUE)) #define __store_via_object(OBJ, ATTRNAME, VALUE) (__store_via_object__(OBJ, __ATTRPOS(ATTRNAME), VALUE)) +#define __store_via_object_internal(OBJ, ATTRNAME, VALUE) (__store_via_object_internal__(OBJ, __ATTRPOS(ATTRNAME), VALUE)) #define __get_class_and_store(OBJ, ATTRNAME, VALUE) (__get_class_and_store__(OBJ, __ATTRPOS(ATTRNAME), VALUE)) /* Introspection. */ @@ -93,10 +96,12 @@ int __check_and_store_via_class__(__ref obj, int pos, int code, __attr value); int __check_and_store_via_object__(__ref obj, int pos, int code, __attr value); +int __check_and_store_via_object_internal__(__ref obj, int pos, int code, __attr value); int __check_and_store_via_any__(__ref obj, int pos, int code, __attr value); #define __check_and_store_via_class(OBJ, ATTRNAME, VALUE) (__check_and_store_via_class__(OBJ, __ATTRPOS(ATTRNAME), __ATTRCODE(ATTRNAME), VALUE)) #define __check_and_store_via_object(OBJ, ATTRNAME, VALUE) (__check_and_store_via_object__(OBJ, __ATTRPOS(ATTRNAME), __ATTRCODE(ATTRNAME), VALUE)) +#define __check_and_store_via_object_internal(OBJ, ATTRNAME, VALUE) (__check_and_store_via_object_internal__(OBJ, __ATTRPOS(ATTRNAME), __ATTRCODE(ATTRNAME), VALUE)) #define __check_and_store_via_any(OBJ, ATTRNAME, VALUE) (__check_and_store_via_any__(OBJ, __ATTRPOS(ATTRNAME), __ATTRCODE(ATTRNAME), VALUE)) /* Context-related operations. */ @@ -151,6 +156,24 @@ /* Copying of structures. */ -__ref __COPY(__ref obj, int size); +__ref __COPY(__ref obj, size_t size); +void __COPY_TO(__ref source, __ref target, size_t size); + +/* Stack management. */ + +extern _Thread_local __attr __stack; + +#define __STACK_SECTION_SIZE 4096 + +__attr __stack_init(); +__attr __stack_allocate(__attr __stack, size_t size); +void __stack_expand(__attr __stack); +void __stack_contract(__attr __stack, char *level); + +/* Stack access. */ + +__attr __load(__attr value); +__attr __store_local(__attr target, __attr value); +__attr __return(__attr result, __section *section, char *level); #endif /* __OPS_H__ */ diff -r 64fe7b9073ac -r e98699004465 templates/progops.c --- a/templates/progops.c Sun Nov 14 00:50:17 2021 +0100 +++ b/templates/progops.c Sun Nov 28 02:03:21 2021 +0100 @@ -42,6 +42,13 @@ return __ATTRVALUE(obj); } +__attr __new_stack(const __table * table, __ref cls, size_t size) +{ + __attr attr = __stack_allocate(__stack, size); + __init(__VALUE(attr), table, cls); + return attr; +} + __attr __new_wrapper(__attr context, __attr attr) { return __new___builtins___core_wrapper(__NULL, context, attr); @@ -69,7 +76,10 @@ /* Copy the given number of values. */ for (i = 0; i < number; i++) - data->attrs[i] = args[i]; + { + data->attrs[i] = __RAWVALUE(0); + __store_target(&data->attrs[i], args[i]); + } data->size = number; } @@ -81,7 +91,7 @@ /* Store a reference to the data in the object's __data__ attribute. */ - __store_via_object(__VALUE(self), __data__, (__attr) {.seqvalue=data}); + __store_via_object_internal(__VALUE(self), __data__, (__attr) {.seqvalue=data}); __newdata_sequence(number, data, args); return self; } @@ -97,7 +107,7 @@ /* Store a reference to the data in the object's __data__ attribute. */ - __store_via_object(__VALUE(self), __data__, (__attr) {.seqvalue=data}); + __store_via_object_internal(__VALUE(self), __data__, (__attr) {.seqvalue=data}); __newdata_sequence(number, data, args); return self; } @@ -207,8 +217,10 @@ /* Invoke the given callable, supplying keyword argument details in the given codes and arguments arrays, indicating the number of arguments described. The number of positional arguments is specified, and such arguments then - follow as conventional function arguments. Typically, at least one argument - is specified, starting with any context argument. + follow as conventional function arguments. + + Typically, at least one argument is specified, starting with any context + argument. */ __attr __invoke(__attr callable, int always_callable, @@ -220,9 +232,11 @@ __attr target = __unwrap_callable(callable); /* Obtain the __args__ special member, referencing the parameter table. */ + + const __ptable *ptable = __check_and_load_via_object(__VALUE(target), __args__).ptable; + /* Refer to the table and minimum/maximum. */ - const __ptable *ptable = __check_and_load_via_object(__VALUE(target), __args__).ptable; const unsigned int min = ptable->min, max = ptable->max; /* Reserve enough space for the arguments. */ @@ -270,10 +284,13 @@ /* Check the table entry against the supplied argument details. Set the argument but only if it does not overwrite positional arguments. */ - /* NOTE: Should use a more specific exception. */ if ((pos == -1) || (pos < nargs)) + { + /* NOTE: Should use a more specific exception. */ + __raise_type_error(); + } /* Set the argument using the appropriate position. */ @@ -290,7 +307,8 @@ } /* Call with the prepared arguments via a special adaptor function that - converts the array to an argument list. */ + converts the array to an argument list. The context argument occupies + position #0. */ return __call_with_args( always_callable ? @@ -336,7 +354,8 @@ return value == (__ref) &__predefined___builtins___boolean_True ? 1 : value == (__ref) &__predefined___builtins___boolean_False ? 0 : - __VALUE(__fn___builtins___boolean_bool(__NULL, attr)) == (__ref) &__predefined___builtins___boolean_True; + __VALUE(__fn___builtins___boolean_bool(__NULL, attr)) == + (__ref) &__predefined___builtins___boolean_True; } /* Conversion of trailing data to an integer. */ diff -r 64fe7b9073ac -r e98699004465 templates/progops.h --- a/templates/progops.h Sun Nov 14 00:50:17 2021 +0100 +++ b/templates/progops.h Sun Nov 28 02:03:21 2021 +0100 @@ -26,6 +26,7 @@ /* Generic instantiation operations, defining common members. */ __attr __new(const __table *table, __ref cls, size_t size, int immutable); +__attr __new_stack(const __table *table, __ref cls, size_t size); __attr __new_wrapper(__attr context, __attr attr); /* Generic internal data allocation. */ @@ -86,6 +87,7 @@ #define __INSTANCETABLE(CLS) (__InstanceTable_##CLS) #define __NEWINSTANCE(CLS) __new(&__INSTANCETABLE(CLS), &CLS, __INSTANCESIZE(CLS), 0) #define __NEWINSTANCEIM(CLS) __new(&__INSTANCETABLE(CLS), &CLS, __INSTANCESIZE(CLS), 1) +#define __NEWINSTANCE_STACK(CLS) __new_stack(&__INSTANCETABLE(CLS), &CLS, __INSTANCESIZE(CLS)) #define __ISINSTANCE(ATTR, TYPE) __BOOL(__fn_native_introspection_isinstance(__NULL, ATTR, TYPE)) /* Operations for accessing trailing data. */ diff -r 64fe7b9073ac -r e98699004465 templates/types.h --- a/templates/types.h Sun Nov 14 00:50:17 2021 +0100 +++ b/templates/types.h Sun Nov 28 02:03:21 2021 +0100 @@ -42,6 +42,7 @@ typedef struct __table { const __pos size; + const size_t obj_size; /* size for value instance copying */ const __code attrs[]; } __table; @@ -80,6 +81,11 @@ typedef _Float64 __float; +/* Introduce value stack section and descriptor. */ + +typedef struct __section __section; +typedef struct __stackdesc __stackdesc; + /* Attribute value interpretations. */ typedef union __attr @@ -101,6 +107,11 @@ __fragment * seqvalue; /* sequence data */ void * datavalue; /* object-specific data */ __int sizevalue; /* object-specific size */ + + /* Value stack parameter member. */ + + __stackdesc *stackdesc; /* reference to value stack descriptor */ + } __attr; typedef struct __obj @@ -115,7 +126,7 @@ } __obj; -#define __INSTANCE_SIZE(NUMBER) ((NUMBER) * sizeof(__attr) + sizeof(__table *) + sizeof(__ppos)) +#define __INSTANCE_SIZE(REF) ((REF)->table->obj_size) /* Fragments are simple collections of attributes employed by sequence types. They provide the basis of lists and tuples. */ @@ -128,17 +139,33 @@ #define __FRAGMENT_SIZE(NUMBER) ((NUMBER) * sizeof(__attr) + 2 * sizeof(__int)) +/* Sections are used to provide the value stack. */ + +typedef struct __section +{ + char *base, *level, *limit; + __section *previous; +} __section; + +/* The value stack descriptor references the current value stack section. */ + +typedef struct __stackdesc +{ + __section *current; +} __stackdesc; + /* Attribute interpretation. */ #define __NUM_TAG_BITS 2 -#define __TAG_COPYABLE 0b01UL #define __TAG_MUTABLE 0b10UL #define __TAG_MASK 0b11UL -#define __COPYABLE(ATTR) ((ATTR).rawvalue & __TAG_COPYABLE) +#define __COPYABLE(ATTR) (__VALUE(ATTR)->table->obj_size != 0) #define __MUTABLE(ATTR) ((ATTR).rawvalue & __TAG_MUTABLE) #define __TO_IMMUTABLE(ATTR) ((__attr) {.rawvalue=(ATTR).rawvalue & (~__TAG_MUTABLE)}) -#define __TO_MUTABLE(ATTR) ((__attr) {.rawvalue=(ATTR).rawvalue | __TAG_MASK}) +#define __TO_MUTABLE(ATTR) ((__attr) {.rawvalue=(ATTR).rawvalue | __TAG_MUTABLE}) +#define __MUTABLEVALUE(REF) ((__attr) {.rawvalue=(uintptr_t) REF | __TAG_MUTABLE}) +#define __RAWVALUE(VALUE) ((__attr) {.rawvalue=VALUE}) /* Attribute value setting. */ diff -r 64fe7b9073ac -r e98699004465 tests/values.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/values.py Sun Nov 28 02:03:21 2021 +0100 @@ -0,0 +1,19 @@ +class X: + def __init__(self, a, b): + self.a = a + self.b = b + +def f(a, x): + x.a = 3 # x.b + print a # 2 + print x.a # 3 + return a + x.a + +def g(x): + return x.a, x.b + +x = X(2, 3) +y = f(x.a, x) +print y # 5 +z = g(x) +print z # (3, 3) diff -r 64fe7b9073ac -r e98699004465 translator.py --- a/translator.py Sun Nov 14 00:50:17 2021 +0100 +++ b/translator.py Sun Nov 28 02:03:21 2021 +0100 @@ -3,7 +3,7 @@ """ Translate programs. -Copyright (C) 2015, 2016, 2017, 2018 Paul Boddie +Copyright (C) 2015-2018, 2021 Paul Boddie This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software @@ -37,7 +37,7 @@ AliasResult, AttrResult, Expression, InstantiationResult, \ InvocationResult, LogicalOperationResult, \ LogicalResult, NegationResult, PredefinedConstantRef, \ - ReturnRef + ReturnRef, special_attributes from StringIO import StringIO import compiler import sys @@ -619,7 +619,8 @@ del self.attrs[0] return AttrResult(output, refs, location, context_identity, context_identity_verified, - accessor_test, accessor_stored) + accessor_test, accessor_stored, n.attrname, + self.in_assignment) def init_substitutions(self): @@ -806,9 +807,23 @@ if ref and not ref.static(): parent, attrname = path.rsplit(".", 1) - self.writestmt("__store_via_object(&%s, %s, __load_via_object(&%s, %s));" % ( - encode_path(class_name), name, - encode_path(parent), attrname + # NOTE: This is a superficial test for internal attributes that + # NOTE: relies on such attributes being used directly and passed + # NOTE: to native code. + + if name in special_attributes: + store_op = "__store_via_object_internal" + else: + store_op = "__store_via_object" + + if attrname in special_attributes: + load_op = "__load_via_object_internal" + else: + load_op = "__load_via_object" + + self.writestmt("%s(&%s, %s, %s(&%s, %s));" % ( + store_op, encode_path(class_name), name, + load_op, encode_path(parent), attrname )) def process_from_node(self, n): @@ -883,6 +898,11 @@ compiler.ast.AssAttr(compiler.ast.Name("self"), name, "OP_ASSIGN"), compiler.ast.Name(name)) + # Record the value stack level. + + self.writestmt("__section *__stack_section = __stack.stackdesc->current;") + self.writestmt("char *__stack_level = __stack_section->level; (void) __stack_level;") + # Produce the body and any additional return statement. expr = self.process_structure_node(n.code) or \ @@ -1281,8 +1301,8 @@ self.record_temp("__tmp_values") # Arguments are presented in a temporary frame array with any context - # always being the first argument. Where it would be unused, it may be - # set to null. + # always being the first argument. Where the context would be unused, it + # may be set to null. if context_required: if have_access_context: @@ -1292,6 +1312,8 @@ else: context_arg = "__NULL" + # Introduce any context. + args = [context_arg] reserved_args = 1 @@ -1324,12 +1346,9 @@ for i, arg in enumerate(n.args): argexpr = self.process_structure_node(arg) - # Obtain an appropriate argument representation. This prevents - # copyable values from being mutable, but care must be taken to - # prevent special internal attribute values represented using - # attributes from being modified. - - argrepr = argexpr.as_arg() + # Obtain an appropriate argument representation. + + argrepr = str(argexpr) # Store a keyword argument, either in the argument list or # in a separate keyword argument list for subsequent lookup. @@ -1758,7 +1777,7 @@ if self.in_try_finally or self.in_try_except: self.writestmt("__Return(%s);" % expr) else: - self.writestmt("return %s;" % expr) + self.writestmt("return __return(%s, __stack_section, __stack_level);" % expr) return ReturnRef() @@ -2131,11 +2150,13 @@ if names: names.sort() - self.writeline("__attr %s;" % ", ".join(names)) + for n in names: + self.writeline("__attr %s = (__attr) {.rawvalue = 0};" % n) if volatile_names: volatile_names.sort() - self.writeline("volatile __attr %s;" % ", ".join(volatile_names)) + for n in volatile_names: + self.writeline("volatile __attr %s = (__attr) {.rawvalue = 0};" % n) self.flush_unit(name, out) diff -r 64fe7b9073ac -r e98699004465 transresults.py --- a/transresults.py Sun Nov 14 00:50:17 2021 +0100 +++ b/transresults.py Sun Nov 28 02:03:21 2021 +0100 @@ -24,6 +24,8 @@ from results import ConstantValueRef, InstanceRef, LiteralSequenceRef, NameRef, \ ResolvedNameRef, Result +special_attributes = ("__args__", "__data__", "__key__", "__size__") + # Classes representing intermediate translation results. class ReturnRef: @@ -59,12 +61,6 @@ def __repr__(self): return "Expression(%r)" % self.s - def as_arg(self): - - "Return the expression without any mutable tag." - - return self.s - class TrResolvedNameRef(ResolvedNameRef): "A reference to a name in the translation." @@ -114,54 +110,58 @@ # Qualified names must be converted into parent-relative assignments. elif self.parent: - return "__store_via_object(&%s, %s, %s)" % ( - encode_path(self.parent), self.attrname, self.expr) + + # NOTE: This is a superficial test for internal attributes that + # NOTE: relies on such attributes being used directly and passed + # NOTE: to native code. + + if self.attrname in special_attributes: + op = "__store_via_object_internal" + else: + op = "__store_via_object" + + return "%s(&%s, %s, %s)" % ( + op, encode_path(self.parent), self.attrname, self.expr) # All other assignments involve the names as they were given. else: - return "%s = %s" % (self.attrname, self.expr) + return "%s = __store_local(%s, %s)" % (self.attrname, self.attrname, self.expr) # Expressions. - elif self.static_name: - return "__ATTRVALUE(&%s)" % self.static_name + if self.static_name: + s = "__ATTRVALUE(&%s)" % self.static_name # Qualified names must be converted into parent-relative accesses. elif self.parent: - return "__load_via_object(&%s, %s)" % ( - encode_path(self.parent), self.attrname) + + # NOTE: This is a superficial test for internal attributes that + # NOTE: relies on such attributes being used directly and passed + # NOTE: to native code. + + if self.attrname in special_attributes: + op = "__load_via_object_internal" + else: + op = "__load_via_object" + + s = "%s(&%s, %s)" % ( + op, encode_path(self.parent), self.attrname) # All other accesses involve the names as they were given. else: - return "(%s)" % self.attrname - - def as_arg(self): - - "Return the expression without any mutable tag." - - s = self.__str__() + s = "(%s)" % self.attrname - # NOTE: This is a superficial test for internal attributes that relies - # NOTE: on such attributes being used directly and passed to native - # NOTE: code. - - if self.attrname in ("__data__", "__size__"): - return s - else: - return "__TO_IMMUTABLE(%s)" % s + return "__load(%s)" % s class TrConstantValueRef(ConstantValueRef): "A constant value reference in the translation." def __str__(self): - return encode_literal_constant(self.number) - - def as_arg(self): - return self.__str__() + return "__load(%s)" % encode_literal_constant(self.number) class TrLiteralSequenceRef(LiteralSequenceRef): @@ -170,9 +170,6 @@ def __str__(self): return str(self.node) - def as_arg(self): - return self.__str__() - class TrInstanceRef(InstanceRef): "A reference representing instantiation of a class." @@ -193,15 +190,13 @@ def __repr__(self): return "TrResolvedInstanceRef(%r, %r)" % (self.ref, self.expr) - def as_arg(self): - return self.__str__() - class AttrResult(Result, InstructionSequence): "A translation result for an attribute access." def __init__(self, instructions, refs, location, context_identity, - context_identity_verified, accessor_test, accessor_stored): + context_identity_verified, accessor_test, accessor_stored, + attrname, assignment): InstructionSequence.__init__(self, instructions) self.refs = refs @@ -210,6 +205,8 @@ self.context_identity_verified = context_identity_verified self.accessor_test = accessor_test self.accessor_stored = accessor_stored + self.attrname = attrname + self.assignment = assignment def references(self): return self.refs @@ -244,16 +241,23 @@ return bool(self.instructions) def __str__(self): - return encode_instructions(self.instructions) + s = encode_instructions(self.instructions) + + # NOTE: This includes a superficial test for internal attributes that + # NOTE: relies on such attributes being used directly and passedto + # NOTE: native code. + + if self.assignment or self.accessor_test or self.attrname in special_attributes: + return s + else: + return "__load(%s)" % s def __repr__(self): - return "AttrResult(%r, %r, %r, %r, %r, %r, %r)" % ( + return "AttrResult(%r, %r, %r, %r, %r, %r, %r, %r, %r)" % ( self.instructions, self.refs, self.location, self.context_identity, self.context_identity_verified, - self.accessor_test, self.accessor_stored) - - def as_arg(self): - return self.__str__() + self.accessor_test, self.accessor_stored, self.attrname, + self.assignment) class AliasResult(NameRef, Result): @@ -308,9 +312,6 @@ def __repr__(self): return "AliasResult(%r, %r)" % (self.name_ref, self.refs) - def as_arg(self): - return self.__str__() - class InvocationResult(Result, InstructionSequence): "A translation result for an invocation." @@ -321,9 +322,6 @@ def __repr__(self): return "InvocationResult(%r)" % self.instructions - def as_arg(self): - return self.__str__() - class InstantiationResult(InvocationResult, TrInstanceRef): "An instantiation result acting like an invocation result." @@ -364,9 +362,6 @@ def __repr__(self): return "PredefinedConstantRef(%r)" % self.value - def as_arg(self): - return self.__str__() - class LogicalResult(Result): "A logical expression result." @@ -412,9 +407,6 @@ def __repr__(self): return "NegationResult(%r)" % self.expr - def as_arg(self): - return self.__str__() - class LogicalOperationResult(LogicalResult): "A logical operation result." @@ -487,7 +479,4 @@ def __repr__(self): return "LogicalOperationResult(%r, %r)" % (self.exprs, self.conjunction) - def as_arg(self): - return self.__str__() - # vim: tabstop=4 expandtab shiftwidth=4