Lichen

Changeset

954:b07f6f6ce357
6 months ago Paul Boddie raw files shortlog changelog graph Introduced copyable and mutable flags in the tagged region of attribute values. Such flags are set when new integer and floating point values are created, but the mutable flag is cleared when such attributes are propagated between functions in order to prevent values being replaced upon assignment to parameter names, this being a concern when value copying is introduced. tagged-address-values
templates/types.h (file) translator.py (file) transresults.py (file)
     1.1 --- a/templates/types.h	Sun Nov 07 01:18:51 2021 +0100
     1.2 +++ b/templates/types.h	Mon Nov 08 00:25:46 2021 +0100
     1.3 @@ -135,12 +135,15 @@
     1.4  
     1.5  /* Attribute interpretation. */
     1.6  
     1.7 -#define __NUM_TAG_BITS      1
     1.8 -#define __TAG_MUTABLE       0b1
     1.9 -#define __TAG_MASK          0b1
    1.10 +#define __NUM_TAG_BITS      2
    1.11 +#define __TAG_COPYABLE      0b01UL
    1.12 +#define __TAG_MUTABLE       0b10UL
    1.13 +#define __TAG_MASK          0b11UL
    1.14  
    1.15 -#define __MUTABLE(ATTR)     (((ATTR).rawvalue & __TAG_MASK) == __TAG_MUTABLE)
    1.16 -#define __TO_MUTABLE(ATTR)  ((__attr) (((ATTR).rawvalue & (~__TAG_MASK)) | __TAG_MUTABLE))
    1.17 +#define __COPYABLE(ATTR)    ((ATTR).rawvalue & __TAG_COPYABLE)
    1.18 +#define __MUTABLE(ATTR)     ((ATTR).rawvalue & __TAG_MUTABLE)
    1.19 +#define __TO_IMMUTABLE(ATTR) ((__attr) {.rawvalue=(ATTR).rawvalue & (~__TAG_MUTABLE)})
    1.20 +#define __TO_MUTABLE(ATTR)   ((__attr) {.rawvalue=(ATTR).rawvalue | __TAG_MASK})
    1.21  
    1.22  /* Attribute value setting. */
    1.23  
     2.1 --- a/translator.py	Sun Nov 07 01:18:51 2021 +0100
     2.2 +++ b/translator.py	Mon Nov 08 00:25:46 2021 +0100
     2.3 @@ -1326,6 +1326,13 @@
     2.4          for i, arg in enumerate(n.args):
     2.5              argexpr = self.process_structure_node(arg)
     2.6  
     2.7 +            # Obtain an appropriate argument representation. This prevents
     2.8 +            # copyable values from being mutable, but care must be taken to
     2.9 +            # prevent special internal attribute values represented using
    2.10 +            # attributes from being modified.
    2.11 +
    2.12 +            argrepr = argexpr.as_arg()
    2.13 +
    2.14              # Store a keyword argument, either in the argument list or
    2.15              # in a separate keyword argument list for subsequent lookup.
    2.16  
    2.17 @@ -1340,12 +1347,12 @@
    2.18                      except ValueError:
    2.19                          raise TranslateError("Argument %s is not recognised." % arg.name,
    2.20                                               self.get_namespace_path(), n)
    2.21 -                    args[argnum + reserved_args] = str(argexpr)
    2.22 +                    args[argnum + reserved_args] = argrepr
    2.23  
    2.24                  # Otherwise, store the details in a separate collection.
    2.25  
    2.26                  else:
    2.27 -                    kwargs.append(str(argexpr))
    2.28 +                    kwargs.append(argrepr)
    2.29                      kwcodes.append("{%s, %s}" % (
    2.30                          encode_ppos(arg.name), encode_pcode(arg.name)))
    2.31  
    2.32 @@ -1354,7 +1361,7 @@
    2.33  
    2.34              else:
    2.35                  try:
    2.36 -                    args[i + reserved_args] = str(argexpr)
    2.37 +                    args[i + reserved_args] = argrepr
    2.38                  except IndexError:
    2.39                      raise TranslateError("Too many arguments specified.",
    2.40                                           self.get_namespace_path(), n)
     3.1 --- a/transresults.py	Sun Nov 07 01:18:51 2021 +0100
     3.2 +++ b/transresults.py	Mon Nov 08 00:25:46 2021 +0100
     3.3 @@ -59,6 +59,12 @@
     3.4      def __repr__(self):
     3.5          return "Expression(%r)" % self.s
     3.6  
     3.7 +    def as_arg(self):
     3.8 +
     3.9 +        "Return the expression without any mutable tag."
    3.10 +
    3.11 +        return "__TO_IMMUTABLE(%s)" % self.s
    3.12 +
    3.13  class TrResolvedNameRef(ResolvedNameRef):
    3.14  
    3.15      "A reference to a name in the translation."
    3.16 @@ -67,6 +73,20 @@
    3.17          ResolvedNameRef.__init__(self, name, ref, expr, is_global)
    3.18          self.location = location
    3.19  
    3.20 +        # For sources, any identified static origin will be constant and thus
    3.21 +        # usable directly. For targets, no constant should be assigned and thus
    3.22 +        # the alias (or any plain name) will be used.
    3.23 +
    3.24 +        self.static_ref = self.static()
    3.25 +        origin = self.static_ref and self.get_origin()
    3.26 +        self.static_name = origin and encode_path(origin)
    3.27 +
    3.28 +        # Determine whether a qualified name is involved.
    3.29 +
    3.30 +        t = (not self.is_constant_alias() and self.get_name() or self.name).rsplit(".", 1)
    3.31 +        self.parent = len(t) > 1 and t[0] or None
    3.32 +        self.attrname = t[-1] and encode_path(t[-1])
    3.33 +
    3.34      def access_location(self):
    3.35          return self.location
    3.36  
    3.37 @@ -82,55 +102,56 @@
    3.38              else:
    3.39                  return encode_path(self.name)
    3.40  
    3.41 -        # For sources, any identified static origin will be constant and thus
    3.42 -        # usable directly. For targets, no constant should be assigned and thus
    3.43 -        # the alias (or any plain name) will be used.
    3.44 -
    3.45 -        ref = self.static()
    3.46 -        origin = ref and self.get_origin()
    3.47 -        static_name = origin and encode_path(origin)
    3.48 -
    3.49 -        # Determine whether a qualified name is involved.
    3.50 -
    3.51 -        t = (not self.is_constant_alias() and self.get_name() or self.name).rsplit(".", 1)
    3.52 -        parent = len(t) > 1 and t[0] or None
    3.53 -        attrname = t[-1] and encode_path(t[-1])
    3.54 -
    3.55          # Assignments.
    3.56  
    3.57          if self.expr:
    3.58  
    3.59              # Eliminate assignments between constants.
    3.60  
    3.61 -            if ref and self.expr.static():
    3.62 +            if self.static_ref and self.expr.static():
    3.63                  return ""
    3.64  
    3.65              # Qualified names must be converted into parent-relative assignments.
    3.66  
    3.67 -            elif parent:
    3.68 +            elif self.parent:
    3.69                  return "__store_via_object(&%s, %s, %s)" % (
    3.70 -                    encode_path(parent), attrname, self.expr)
    3.71 +                    encode_path(self.parent), self.attrname, self.expr)
    3.72  
    3.73              # All other assignments involve the names as they were given.
    3.74  
    3.75              else:
    3.76 -                return "%s = %s" % (attrname, self.expr)
    3.77 +                return "%s = %s" % (self.attrname, self.expr)
    3.78  
    3.79          # Expressions.
    3.80  
    3.81 -        elif static_name:
    3.82 -            return "__ATTRVALUE(&%s)" % static_name
    3.83 +        elif self.static_name:
    3.84 +            return "__ATTRVALUE(&%s)" % self.static_name
    3.85  
    3.86          # Qualified names must be converted into parent-relative accesses.
    3.87  
    3.88 -        elif parent:
    3.89 +        elif self.parent:
    3.90              return "__load_via_object(&%s, %s)" % (
    3.91 -                encode_path(parent), attrname)
    3.92 +                encode_path(self.parent), self.attrname)
    3.93  
    3.94          # All other accesses involve the names as they were given.
    3.95  
    3.96          else:
    3.97 -            return "(%s)" % attrname
    3.98 +            return "(%s)" % self.attrname
    3.99 +
   3.100 +    def as_arg(self):
   3.101 +
   3.102 +        "Return the expression without any mutable tag."
   3.103 +
   3.104 +        s = self.__str__()
   3.105 +
   3.106 +        # NOTE: This is a superficial test for internal attributes that relies
   3.107 +        # NOTE: on such attributes being used directly and passed to native
   3.108 +        # NOTE: code.
   3.109 +
   3.110 +        if self.attrname in ("__data__", "__size__"):
   3.111 +            return s
   3.112 +        else:
   3.113 +            return "__TO_IMMUTABLE(%s)" % s
   3.114  
   3.115  class TrConstantValueRef(ConstantValueRef):
   3.116  
   3.117 @@ -139,6 +160,9 @@
   3.118      def __str__(self):
   3.119          return encode_literal_constant(self.number)
   3.120  
   3.121 +    def as_arg(self):
   3.122 +        return self.__str__()
   3.123 +
   3.124  class TrLiteralSequenceRef(LiteralSequenceRef):
   3.125  
   3.126      "A reference representing a sequence of values."
   3.127 @@ -146,6 +170,9 @@
   3.128      def __str__(self):
   3.129          return str(self.node)
   3.130  
   3.131 +    def as_arg(self):
   3.132 +        return self.__str__()
   3.133 +
   3.134  class TrInstanceRef(InstanceRef):
   3.135  
   3.136      "A reference representing instantiation of a class."
   3.137 @@ -166,6 +193,9 @@
   3.138      def __repr__(self):
   3.139          return "TrResolvedInstanceRef(%r, %r)" % (self.ref, self.expr)
   3.140  
   3.141 +    def as_arg(self):
   3.142 +        return self.__str__()
   3.143 +
   3.144  class AttrResult(Result, InstructionSequence):
   3.145  
   3.146      "A translation result for an attribute access."
   3.147 @@ -222,6 +252,9 @@
   3.148                  self.context_identity, self.context_identity_verified,
   3.149                  self.accessor_test, self.accessor_stored)
   3.150  
   3.151 +    def as_arg(self):
   3.152 +        return self.__str__()
   3.153 +
   3.154  class AliasResult(NameRef, Result):
   3.155  
   3.156      "An alias for other values."
   3.157 @@ -275,6 +308,9 @@
   3.158      def __repr__(self):
   3.159          return "AliasResult(%r, %r)" % (self.name_ref, self.refs)
   3.160  
   3.161 +    def as_arg(self):
   3.162 +        return self.__str__()
   3.163 +
   3.164  class InvocationResult(Result, InstructionSequence):
   3.165  
   3.166      "A translation result for an invocation."
   3.167 @@ -285,6 +321,9 @@
   3.168      def __repr__(self):
   3.169          return "InvocationResult(%r)" % self.instructions
   3.170  
   3.171 +    def as_arg(self):
   3.172 +        return self.__str__()
   3.173 +
   3.174  class InstantiationResult(InvocationResult, TrInstanceRef):
   3.175  
   3.176      "An instantiation result acting like an invocation result."
   3.177 @@ -325,6 +364,9 @@
   3.178      def __repr__(self):
   3.179          return "PredefinedConstantRef(%r)" % self.value
   3.180  
   3.181 +    def as_arg(self):
   3.182 +        return self.__str__()
   3.183 +
   3.184  class LogicalResult(Result):
   3.185  
   3.186      "A logical expression result."
   3.187 @@ -370,6 +412,9 @@
   3.188      def __repr__(self):
   3.189          return "NegationResult(%r)" % self.expr
   3.190  
   3.191 +    def as_arg(self):
   3.192 +        return self.__str__()
   3.193 +
   3.194  class LogicalOperationResult(LogicalResult):
   3.195  
   3.196      "A logical operation result."
   3.197 @@ -442,4 +487,7 @@
   3.198      def __repr__(self):
   3.199          return "LogicalOperationResult(%r, %r)" % (self.exprs, self.conjunction)
   3.200  
   3.201 +    def as_arg(self):
   3.202 +        return self.__str__()
   3.203 +
   3.204  # vim: tabstop=4 expandtab shiftwidth=4