micropython

rsvplib.py

450:5382a26943ca
2011-08-21 Paul Boddie Changed the nature of RSVP instructions so that they have explicit working, target and source register inputs and no longer consume simple instructions as direct inputs. This changes the nature of the generated code and of certain optimisations, as well as making some previous optimisations redundant and necessitating additional optimisations.
     1 #!/usr/bin/env python     2      3 """     4 A native function library for a really simple virtual processor.     5 NOTE: Ultimately, this should only implement only operations at the lowest     6 NOTE: possible level, with generated native methods providing the functionality     7 NOTE: instead.     8      9 Copyright (C) 2007, 2008, 2009, 2010, 2011 Paul Boddie <paul@boddie.org.uk>    10     11 This program is free software; you can redistribute it and/or modify it under    12 the terms of the GNU General Public License as published by the Free Software    13 Foundation; either version 3 of the License, or (at your option) any later    14 version.    15     16 This program is distributed in the hope that it will be useful, but WITHOUT    17 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS    18 FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more    19 details.    20     21 You should have received a copy of the GNU General Public License along with    22 this program.  If not, see <http://www.gnu.org/licenses/>.    23 """    24     25 from micropython.program import DataValue    26 import operator    27     28 class Library:    29     30     "Native function implementations."    31     32     # NOTE: These attributes need changing if the instance layout changes.    33     34     instance_template_size = instance_data_offset = 1    35     instance_size = instance_template_size + 1    36     fragment_data_offset = 1    37     38     def __init__(self, machine, constants):    39     40         """    41         Initialise the library with the 'machine' and the 'constants' addresses    42         dictionary.    43         """    44     45         self.machine = machine    46         self.constants = constants    47     48         # Native class constants.    49     50         self.int_class, self.int_instance = self._get_builtin_class_and_template("int")    51         self.list_class, self.list_instance = self._get_builtin_class_and_template("list")    52         self.index_error, self.index_error_instance = self._get_builtin_class_and_template("IndexError")    53         self.str_class, self.str_instance = self._get_builtin_class_and_template("basestring")    54         self.accessor_class, self.accessor_instance = self._get_builtin_class_and_template("_accessor")    55     56         self.tuple_class = self.machine.tuple_class    57         self.tuple_instance = self.machine.tuple_instance    58     59         self.attr_error_instance = self.machine.attr_error_instance    60         self.type_error_instance = self.machine.type_error_instance    61     62     def _get_builtin_class_and_template(self, name):    63         cls = self.machine._get_class("__builtins__", name)    64         if cls is not None:    65             return cls.location, cls.instance_template_location    66         else:    67             return None, None    68     69     def _check_index(self, pos, nelements):    70         return pos >= 0 and pos < nelements    71     72     # Native functionality.    73     74     def builtins_no_op(self):    75         pass    76     77     def native_int_arithmetic_op(self, op):    78         left = self.machine.load_from_frame(0)    79         left_data = left.ref + self.instance_data_offset    80         right = self.machine.load_from_frame(1)    81         right_data = right.ref + self.instance_data_offset    82     83         # Make a new object.    84     85         addr = self.machine._MakeObject(self.instance_size, self.int_instance)    86     87         # Store the result.    88         # NOTE: The data is considered ready to use.    89     90         self.machine.save(addr + self.instance_data_offset, op(self.machine.load(left_data), self.machine.load(right_data)))    91     92         # Return the new object.    93         # Introduce object as context for the new object.    94     95         self.machine.LoadImmediate(addr, "result_context")    96         self.machine.LoadImmediate(addr, "result")    97     98     def native_logical_op(self, op):    99         left = self.machine.load_from_frame(0)   100         left_data = left.ref + self.instance_data_offset   101         right = self.machine.load_from_frame(1)   102         right_data = right.ref + self.instance_data_offset   103    104         # Test the data.   105         # NOTE: The data is considered ready to use.   106    107         if op(self.machine.load(left_data), self.machine.load(right_data)):   108             self.machine.LoadImmediate(self.constants[True], "result_context")   109             self.machine.LoadImmediate(self.constants[True], "result")   110         else:   111             self.machine.LoadImmediate(self.constants[False], "result_context")   112             self.machine.LoadImmediate(self.constants[False], "result")   113    114     # Operators.   115     # Although this takes a short-cut by using the operator module, testing is   116     # still performed on the operands to ensure that they qualify for these   117     # native operations.   118    119     def native_int_add(self):   120         return self.native_int_arithmetic_op(operator.add)   121    122     def native_int_sub(self):   123         return self.native_int_arithmetic_op(operator.sub)   124    125     def native_int_pow(self):   126         return self.native_int_arithmetic_op(operator.pow)   127    128     def native_int_and(self):   129         return self.native_int_arithmetic_op(operator.and_)   130    131     def native_int_or(self):   132         return self.native_int_arithmetic_op(operator.or_)   133    134     def native_int_lt(self):   135         return self.native_logical_op(operator.lt)   136    137     def native_int_gt(self):   138         return self.native_logical_op(operator.gt)   139    140     def native_int_eq(self):   141         return self.native_logical_op(operator.eq)   142    143     def native_str_lt(self):   144         return self.native_logical_op(operator.lt)   145    146     def native_str_gt(self):   147         return self.native_logical_op(operator.gt)   148    149     def native_str_eq(self):   150         return self.native_logical_op(operator.eq)   151    152     # Specific operator methods.   153    154     def builtins_int_neg(self):   155         left = self.machine.load_from_frame(0)   156         left_data = left.ref + self.instance_data_offset   157    158         # Make a new object.   159    160         addr = self.machine._MakeObject(self.instance_size, self.int_instance)   161    162         # Store the result.   163         # NOTE: The data is considered ready to use.   164    165         self.machine.save(addr + self.instance_data_offset, -self.machine.load(left_data))   166    167         # Return the new object.   168         # Introduce object as context for the new object.   169    170         self.machine.LoadImmediate(addr, "result_context")   171         self.machine.LoadImmediate(addr, "result")   172    173     # Various built-in methods.   174    175     def builtins_list_new(self):   176         list_value = self.machine.load_from_frame(0)   177         list_addr = list_value.ref + self.instance_data_offset   178    179         # Make a new sequence.   180         # NOTE: Using an arbitrary size.   181    182         new_fragment = self.machine._MakeFragment(self.fragment_data_offset, 5) # include the header   183    184         # Complete the list instance by saving the fragment reference.   185         # NOTE: This requires an attribute in the list structure.   186    187         self.machine.save(list_addr, DataValue(None, new_fragment))   188    189     def builtins_list_get_single_item(self):   190         obj_value = self.machine.load_from_frame(0)   191         fragment_member = obj_value.ref + self.instance_data_offset   192         item_value = self.machine.load_from_frame(1)   193         item_pos_member = item_value.ref + self.instance_data_offset   194    195         # Get the fragment address.   196    197         fragment = self.machine.load(fragment_member)   198    199         # Get the fragment header.   200    201         header = self.machine.load(fragment.ref)   202         nelements = header.occupied_size - self.fragment_data_offset   203    204         # Get the item position.   205    206         item_pos = self.machine.load(item_pos_member)   207    208         if not self._check_index(item_pos, nelements):   209             self.machine.LoadImmediate(self.machine._MakeObject(self.instance_size, self.index_error_instance), "exception")   210             return self.machine.RaiseException()   211    212         # Get the item itself.   213    214         data = self.machine.load(fragment.ref + self.fragment_data_offset + item_pos)   215         self.machine.LoadImmediate(data.context, "result_context")   216         self.machine.LoadImmediate(data.ref, "result")   217    218     def builtins_list_len(self):   219         obj_value = self.machine.load_from_frame(0)   220         fragment_member = obj_value.ref + self.instance_data_offset   221    222         # Get the fragment address.   223    224         fragment = self.machine.load(fragment_member)   225    226         # Get the fragment header.   227    228         header = self.machine.load(fragment.ref)   229         nelements = header.occupied_size - self.fragment_data_offset   230    231         # Make a new object.   232    233         addr = self.machine._MakeObject(self.instance_size, self.int_instance)   234    235         # Store the result.   236         # NOTE: The data is considered ready to use.   237    238         self.machine.save(addr + self.instance_data_offset, nelements)   239    240         # Return the new object.   241         # Introduce object as context for the new object.   242    243         self.machine.LoadImmediate(addr, "result_context")   244         self.machine.LoadImmediate(addr, "result")   245    246     def builtins_list_append(self):   247         obj_value = self.machine.load_from_frame(0)   248         fragment_member = obj_value.ref + self.instance_data_offset   249         arg_value = self.machine.load_from_frame(1)   250    251         # Get the fragment address.   252    253         fragment = self.machine.load(fragment_member)   254    255         # Get the fragment header.   256    257         header = self.machine.load(fragment.ref)   258    259         # Attempt to add the reference.   260    261         if header.occupied_size < header.allocated_size:   262             self.machine.save(fragment.ref + header.occupied_size, arg_value)   263             header.occupied_size += 1   264         else:   265    266             # Make a new fragment, maintaining more space than currently   267             # occupied in order to avoid reallocation.   268    269             new_fragment = self.machine._MakeFragment(header.occupied_size + 1, header.occupied_size * 2)   270    271             # Copy existing elements.   272    273             for i in range(self.fragment_data_offset, header.occupied_size):   274                 self.machine.save(new_fragment + i, self.machine.load(fragment.ref + i))   275    276             self.machine.save(new_fragment + header.occupied_size, arg_value)   277    278             # Set the new fragment in the object.   279             # NOTE: The old fragment could be deallocated.   280    281             self.machine.save(fragment_member, DataValue(None, new_fragment))   282    283     def builtins_tuple_new(self):   284    285         # Get the sequence address.   286         # The first argument should be empty since this function acts as an   287         # instantiator, and in instantiators the first argument is reserved so   288         # that it can be filled in for the call to an initialiser without   289         # allocating a new frame.   290    291         obj_value = self.machine.load_from_frame(1)   292         return self._builtins_tuple(obj_value)   293    294     def builtins_tuple(self):   295    296         # Get the sequence address.   297    298         obj_value = self.machine.load_from_frame(0)   299         return self._builtins_tuple(obj_value)   300    301     def _builtins_tuple(self, obj_value):   302         fragment_member = obj_value.ref + self.instance_data_offset   303    304         if self.machine._CheckInstance(obj_value.ref, self.tuple_class):   305             self.machine.LoadImmediate(obj_value.context, "result_context")   306             self.machine.LoadImmediate(obj_value.ref, "result")   307             return   308    309         # Reject non-list, non-tuple types.   310         # NOTE: This should probably accept any sequence.   311    312         elif not self.machine._CheckInstance(obj_value.ref, self.list_class):   313             self.machine.LoadImmediate(self.machine._MakeObject(self.instance_size, self.type_error_instance), "exception")   314             return self.machine.RaiseException()   315    316         # Get the fragment address.   317    318         fragment = self.machine.load(fragment_member)   319    320         # Get the fragment header.   321    322         header = self.machine.load(fragment.ref)   323    324         # Make a new object.   325    326         addr = self.machine._MakeObject(self.instance_data_offset + header.occupied_size - self.fragment_data_offset, self.tuple_instance)   327    328         # Copy the fragment contents into the tuple.   329         # NOTE: This might be done by repurposing the fragment in some situations.   330    331         for i in range(self.fragment_data_offset, header.occupied_size):   332             self.machine.save(addr + self.instance_data_offset + i - self.fragment_data_offset, self.machine.load(fragment.ref + i))   333    334         # Return the new object.   335         # Introduce object as context for the new object.   336    337         self.machine.LoadImmediate(addr, "result_context")   338         self.machine.LoadImmediate(addr, "result")   339    340     def builtins_tuple_len(self):   341         obj_value = self.machine.load_from_frame(0)   342    343         # Get the header.   344    345         header = self.machine.load(obj_value.ref)   346         nelements = header.size - self.instance_data_offset   347    348         # Make a new object.   349    350         addr = self.machine._MakeObject(self.instance_size, self.int_instance)   351    352         # Store the result.   353         # NOTE: The data is considered ready to use.   354    355         self.machine.save(addr + self.instance_data_offset, nelements)   356    357         # Return the new object.   358         # Introduce object as context for the new object.   359    360         self.machine.LoadImmediate(addr, "result_context")   361         self.machine.LoadImmediate(addr, "result")   362    363     def builtins_tuple_get_single_item(self):   364         obj_value = self.machine.load_from_frame(0)   365         fragment_member = obj_value.ref + self.instance_data_offset   366         item_value = self.machine.load_from_frame(1)   367         item_pos_member = item_value.ref + self.instance_data_offset   368    369         # Get the header.   370    371         header = self.machine.load(obj_value.ref)   372         nelements = header.size - self.instance_data_offset   373    374         # NOTE: Assume single location for data and header.   375    376         item_pos = self.machine.load(item_pos_member)   377    378         if not self._check_index(item_pos, nelements):   379             self.machine.LoadImmediate(self.machine._MakeObject(self.instance_size, self.index_error_instance), "exception")   380             return self.machine.RaiseException()   381    382         # Get the item.   383    384         data = self.machine.load(fragment_member + item_pos)   385         self.machine.LoadImmediate(data.context, "result_context")   386         self.machine.LoadImmediate(data.ref, "result")   387    388     def builtins_getattr(self):   389         obj_value = self.machine.load_from_frame(0)   390         name_value = self.machine.load_from_frame(1)   391         index_member = name_value.ref + self.instance_data_offset + 1   392    393         if not self.machine._CheckInstance(name_value.ref, self.accessor_class):   394             self.machine.LoadImmediate(self.machine._MakeObject(self.instance_size, self.attr_error_instance), "exception")   395             return self.machine.RaiseException()   396    397         # Get the object table index from the name. It is a bare integer, not a reference.   398    399         index = self.machine.load(index_member)   400    401         # NOTE: This is very much like LoadAttrIndexContextCond.   402    403         data = self.machine.load(obj_value.ref)   404         element = self.machine.objlist[data.classcode + index]   405    406         if element is not None:   407             attr_index, static_attr, offset = element   408             if attr_index == index:   409                 if static_attr:   410                     loaded_data = self.machine.load(offset) # offset is address of class/module attribute   411                     if data.attrcode is not None: # absent attrcode == class/module   412                         loaded_data = self.machine._LoadAddressContextCond(loaded_data.context, loaded_data.ref, obj_value.ref)   413                 else:   414                     loaded_data = self.machine.load(obj_value.ref + offset)   415                 self.machine.LoadImmediate(loaded_data.context, "result_context")   416                 self.machine.LoadImmediate(loaded_data.ref, "result")   417                 return   418    419         self.machine.LoadImmediate(self.machine._MakeObject(self.instance_size, self.attr_error_instance), "exception")   420         return self.machine.RaiseException()   421    422     def builtins_isinstance(self):   423         obj_value = self.machine.load_from_frame(0)   424         cls_value = self.machine.load_from_frame(1)   425    426         if self.machine._CheckInstance(obj_value.ref, cls_value.ref):   427             self.machine.LoadImmediate(self.constants[True], "result_context")   428             self.machine.LoadImmediate(self.constants[True], "result")   429         else:   430             self.machine.LoadImmediate(self.constants[False], "result_context")   431             self.machine.LoadImmediate(self.constants[False], "result")   432    433     def builtins_print(self):   434         # NOTE: Do nothing for now.   435         pass   436    437     def builtins_printnl(self):   438         # NOTE: Do nothing for now.   439         pass   440    441     native_functions = {   442    443         # Native method implementations:   444    445         "native._int_add"                           : native_int_add,   446         "native._int_sub"                           : native_int_sub,   447         "native._int_pow"                           : native_int_pow,   448         "native._int_and"                           : native_int_and,   449         "native._int_or"                            : native_int_or,   450         "native._int_lt"                            : native_int_lt,   451         "native._int_gt"                            : native_int_gt,   452         "native._int_eq"                            : native_int_eq,   453         "native._str_lt"                            : native_str_lt,   454         "native._str_gt"                            : native_str_gt,   455         "native._str_eq"                            : native_str_eq,   456         "__builtins__.int.__neg__"                  : builtins_int_neg,   457         "__builtins__.list.__get_single_item__"     : builtins_list_get_single_item,   458         "__builtins__.list.__len__"                 : builtins_list_len,   459         "__builtins__.list.append"                  : builtins_list_append,   460         "__builtins__.tuple"                        : builtins_tuple_new,   461         "__builtins__.tuple.__len__"                : builtins_tuple_len,   462         "__builtins__.tuple.__get_single_item__"    : builtins_tuple_get_single_item,   463    464         # Native initialisers:   465    466         "__builtins__.BaseException.__init__"       : builtins_no_op, # NOTE: To be made distinct, potentially in the builtins module.   467    468         # Native functions:   469    470         "__builtins__._getattr"                     : builtins_getattr,   471    472         # Native instantiator helpers:   473    474         "__builtins__.list.__new__"                 : builtins_list_new,   475    476         # Native helper functions:   477    478         "__builtins__._isinstance"                  : builtins_isinstance,   479         "__builtins__._print"                       : builtins_print,   480         "__builtins__._printnl"                     : builtins_printnl,   481         "__builtins__._tuple"                       : builtins_tuple,   482         }   483    484 # vim: tabstop=4 expandtab shiftwidth=4