compiler, runtime: Track fields with tag go:"track".
authorIan Lance Taylor <iant@google.com>
Thu, 29 Nov 2012 18:11:17 +0000 (18:11 +0000)
committerIan Lance Taylor <ian@gcc.gnu.org>
Thu, 29 Nov 2012 18:11:17 +0000 (18:11 +0000)
* go-gcc.cc: Include "output.h".
(global_variable): Add is_unique_section parameter.
(global_variable_set_init): Adjust unique section if necessary.
* Make-lang.in (go/go-gcc.o): Add dependency on output.h.

From-SVN: r193945

14 files changed:
gcc/go/ChangeLog
gcc/go/Make-lang.in
gcc/go/go-gcc.cc
gcc/go/gofrontend/backend.h
gcc/go/gofrontend/expressions.cc
gcc/go/gofrontend/expressions.h
gcc/go/gofrontend/gogo-tree.cc
gcc/go/gofrontend/gogo.cc
gcc/go/gofrontend/gogo.h
gcc/go/gofrontend/runtime.def
libgo/Makefile.am
libgo/Makefile.in
libgo/go/runtime/debug.go
libgo/runtime/go-fieldtrack.c [new file with mode: 0644]

index 9a902475148991dd12d95e10e8d4cfcbc9ec0181..1797763be96f70953fde3c5a0d86660834f3878b 100644 (file)
@@ -1,3 +1,10 @@
+2012-11-29  Ian Lance Taylor  <iant@google.com>
+
+       * go-gcc.cc: Include "output.h".
+       (global_variable): Add is_unique_section parameter.
+       (global_variable_set_init): Adjust unique section if necessary.
+       * Make-lang.in (go/go-gcc.o): Add dependency on output.h.
+
 2012-11-17  Diego Novillo  <dnovillo@google.com>
 
        Adjust for new vec API (http://gcc.gnu.org/wiki/cxx-conversion/cxx-vec)
index b3edda6a164d93b545f72f7963df9a2ff822d2fa..473a5810d36c0a753e8f7f7172ad5b91f617a60b 100644 (file)
@@ -1,6 +1,6 @@
 # Make-lang.in -- Top level -*- makefile -*- fragment for gcc Go frontend.
 
-# Copyright (C) 2009, 2010, 2011 Free Software Foundation, Inc.
+# Copyright (C) 2009, 2010, 2011, 2012 Free Software Foundation, Inc.
 
 # This file is part of GCC.
 
@@ -254,7 +254,7 @@ go/go-lang.o: go/go-lang.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(OPTS_H) \
 GOINCLUDES = -I $(srcdir)/go -I $(srcdir)/go/gofrontend
 
 go/go-gcc.o: go/go-gcc.cc $(GO_SYSTEM_H) $(TREE_H) tree-iterator.h \
-               $(GIMPLE_H) toplev.h $(GO_C_H) $(GO_GOGO_H) \
+               $(GIMPLE_H) toplev.h output.h $(GO_C_H) $(GO_GOGO_H) \
                go/gofrontend/backend.h
        $(CXX) -c $(GOINCLUDES) $(ALL_CPPFLAGS) $(ALL_CXXFLAGS) $< $(OUTPUT_OPTION)
 
index 84bc97297ba96a28a6d5d6279fbd40b145eb897e..f8bbaf4bc2ab4ce80b67da7f20d40ca4145c4a41 100644 (file)
@@ -28,6 +28,7 @@
 #include "tree-iterator.h"
 #include "gimple.h"
 #include "toplev.h"
+#include "output.h"
 
 #include "go-c.h"
 
@@ -267,6 +268,7 @@ class Gcc_backend : public Backend
                  Btype* btype,
                  bool is_external,
                  bool is_hidden,
+                 bool in_unique_section,
                  Location location);
 
   void
@@ -1277,6 +1279,7 @@ Gcc_backend::global_variable(const std::string& package_name,
                             Btype* btype,
                             bool is_external,
                             bool is_hidden,
+                            bool in_unique_section,
                             Location location)
 {
   tree type_tree = btype->get_tree();
@@ -1308,6 +1311,9 @@ Gcc_backend::global_variable(const std::string& package_name,
     }
   TREE_USED(decl) = 1;
 
+  if (in_unique_section)
+    resolve_unique_section (decl, 0, 1);
+
   go_preserve_from_gc(decl);
 
   return new Bvariable(decl);
@@ -1326,6 +1332,16 @@ Gcc_backend::global_variable_set_init(Bvariable* var, Bexpression* expr)
   if (var_decl == error_mark_node)
     return;
   DECL_INITIAL(var_decl) = expr_tree;
+
+  // If this variable goes in a unique section, it may need to go into
+  // a different one now that DECL_INITIAL is set.
+  if (DECL_HAS_IMPLICIT_SECTION_NAME_P (var_decl))
+    {
+      DECL_SECTION_NAME (var_decl) = NULL_TREE;
+      resolve_unique_section (var_decl,
+                             compute_reloc_for_constant (expr_tree),
+                             1);
+    }
 }
 
 // Make a local variable.
index 2b14132804f1f1e8fe68ba3a74670167e4b54f80..fe6db743cf06897d6deef93dc865f931d1050687 100644 (file)
@@ -326,8 +326,11 @@ class Backend
   // option.  NAME is the name of the variable.  BTYPE is the type of
   // the variable.  IS_EXTERNAL is true if the variable is defined in
   // some other package.  IS_HIDDEN is true if the variable is not
-  // exported (name begins with a lower case letter).  LOCATION is
-  // where the variable was defined.
+  // exported (name begins with a lower case letter).
+  // IN_UNIQUE_SECTION is true if the variable should be put into a
+  // unique section if possible; this is intended to permit the linker
+  // to garbage collect the variable if it is not referenced.
+  // LOCATION is where the variable was defined.
   virtual Bvariable*
   global_variable(const std::string& package_name,
                  const std::string& pkgpath,
@@ -335,6 +338,7 @@ class Backend
                  Btype* btype,
                  bool is_external,
                  bool is_hidden,
+                 bool in_unique_section,
                  Location location) = 0;
 
   // A global variable will 1) be initialized to zero, or 2) be
index 4ff6272ad6f2ff2c0c4f18308fee8a46e9996c6e..911ce5a7defe8d2f0cd78faa23f48aba76312043 100644 (file)
@@ -10580,6 +10580,102 @@ Expression::make_map_index(Expression* map, Expression* index,
 
 // Class Field_reference_expression.
 
+// Lower a field reference expression.  There is nothing to lower, but
+// this is where we generate the tracking information for fields with
+// the magic go:"track" tag.
+
+Expression*
+Field_reference_expression::do_lower(Gogo* gogo, Named_object* function,
+                                    Statement_inserter* inserter, int)
+{
+  Struct_type* struct_type = this->expr_->type()->struct_type();
+  if (struct_type == NULL)
+    {
+      // Error will be reported elsewhere.
+      return this;
+    }
+  const Struct_field* field = struct_type->field(this->field_index_);
+  if (field == NULL)
+    return this;
+  if (!field->has_tag())
+    return this;
+  if (field->tag().find("go:\"track\"") == std::string::npos)
+    return this;
+
+  // We have found a reference to a tracked field.  Build a call to
+  // the runtime function __go_fieldtrack with a string that describes
+  // the field.  FIXME: We should only call this once per referenced
+  // field per function, not once for each reference to the field.
+
+  if (this->called_fieldtrack_)
+    return this;
+  this->called_fieldtrack_ = true;
+
+  Location loc = this->location();
+
+  std::string s = "fieldtrack \"";
+  Named_type* nt = this->expr_->type()->named_type();
+  if (nt == NULL || nt->named_object()->package() == NULL)
+    s.append(gogo->pkgpath());
+  else
+    s.append(nt->named_object()->package()->pkgpath());
+  s.push_back('.');
+  if (nt != NULL)
+    s.append(nt->name());
+  s.push_back('.');
+  s.append(field->field_name());
+  s.push_back('"');
+
+  // We can't use a string here, because internally a string holds a
+  // pointer to the actual bytes; when the linker garbage collects the
+  // string, it won't garbage collect the bytes.  So we use a
+  // [...]byte.
+
+  mpz_t val;
+  mpz_init_set_ui(val, s.length());
+  Expression* length_expr = Expression::make_integer(&val, NULL, loc);
+  mpz_clear(val);
+
+  Type* byte_type = gogo->lookup_global("byte")->type_value();
+  Type* array_type = Type::make_array_type(byte_type, length_expr);
+
+  Expression_list* bytes = new Expression_list();
+  for (std::string::const_iterator p = s.begin(); p != s.end(); p++)
+    {
+      mpz_init_set_ui(val, *p);
+      Expression* byte = Expression::make_integer(&val, NULL, loc);
+      mpz_clear(val);
+      bytes->push_back(byte);
+    }
+
+  Expression* e = Expression::make_composite_literal(array_type, 0, false,
+                                                    bytes, loc);
+
+  Variable* var = new Variable(array_type, e, true, false, false, loc);
+
+  static int count;
+  char buf[50];
+  snprintf(buf, sizeof buf, "fieldtrack.%d", count);
+  ++count;
+
+  Named_object* no = gogo->add_variable(buf, var);
+  e = Expression::make_var_reference(no, loc);
+  e = Expression::make_unary(OPERATOR_AND, e, loc);
+
+  Expression* call = Runtime::make_call(Runtime::FIELDTRACK, loc, 1, e);
+  inserter->insert(Statement::make_statement(call, false));
+
+  // Put this function, and the global variable we just created, into
+  // unique sections.  This will permit the linker to garbage collect
+  // them if they are not referenced.  The effect is that the only
+  // strings, indicating field references, that will wind up in the
+  // executable will be those for functions that are actually needed.
+  function->func_value()->set_in_unique_section();
+  var->set_in_unique_section();
+
+  return this;
+}
+
 // Return the type of a field reference.
 
 Type*
index eea141fe776c855ec6fc693b4ec3d6b857b4d7df..7bc42424bc1b601174f03503a7797be428111822 100644 (file)
@@ -1842,7 +1842,7 @@ class Field_reference_expression : public Expression
   Field_reference_expression(Expression* expr, unsigned int field_index,
                             Location location)
     : Expression(EXPRESSION_FIELD_REFERENCE, location),
-      expr_(expr), field_index_(field_index)
+      expr_(expr), field_index_(field_index), called_fieldtrack_(false)
   { }
 
   // Return the struct expression.
@@ -1868,6 +1868,9 @@ class Field_reference_expression : public Expression
   do_traverse(Traverse* traverse)
   { return Expression::traverse(&this->expr_, traverse); }
 
+  Expression*
+  do_lower(Gogo*, Named_object*, Statement_inserter*, int);
+
   Type*
   do_type();
 
@@ -1906,6 +1909,8 @@ class Field_reference_expression : public Expression
   Expression* expr_;
   // The zero-based index of the field we are retrieving.
   unsigned int field_index_;
+  // Whether we have already emitted a fieldtrack call.
+  bool called_fieldtrack_;
 };
 
 // A reference to a field of an interface.
index 95ec70eafc2a1bb538432f387ed0214edb7016d1..7159dfb6244ede5f7e2a38f615788e286cf3d156 100644 (file)
@@ -1316,6 +1316,9 @@ Function::get_or_make_decl(Gogo* gogo, Named_object* no, tree id)
              DECL_ATTRIBUTES(decl) = tree_cons(attr, NULL_TREE, NULL_TREE);
            }
 
+         if (this->in_unique_section_)
+           resolve_unique_section (decl, 0, 1);
+
          go_preserve_from_gc(decl);
 
          if (this->closure_var_ != NULL)
index 766fe908095a83b4818a0b420682075a79989d8a..0f63afb53cbf6281557e430f3179bd7a1d6d35ae 100644 (file)
@@ -3075,7 +3075,8 @@ Function::Function(Function_type* type, Function* enclosing, Block* block,
     closure_var_(NULL), block_(block), location_(location), labels_(),
     local_type_count_(0), fndecl_(NULL), defer_stack_(NULL),
     results_are_named_(false), nointerface_(false), calls_recover_(false),
-    is_recover_thunk_(false), has_recover_thunk_(false)
+    is_recover_thunk_(false), has_recover_thunk_(false),
+    in_unique_section_(false)
 {
 }
 
@@ -3896,7 +3897,7 @@ Variable::Variable(Type* type, Expression* init, bool is_global,
     seen_(false), init_is_lowered_(false), type_from_init_tuple_(false),
     type_from_range_index_(false), type_from_range_value_(false),
     type_from_chan_element_(false), is_type_switch_var_(false),
-    determined_type_(false)
+    determined_type_(false), in_unique_section_(false)
 {
   go_assert(type != NULL || init != NULL);
   go_assert(!is_parameter || init == NULL);
@@ -4315,6 +4316,7 @@ Variable::get_backend_variable(Gogo* gogo, Named_object* function,
                                            btype,
                                            package != NULL,
                                            Gogo::is_hidden_name(name),
+                                           this->in_unique_section_,
                                            this->location_);
          else if (function == NULL)
            {
index e23b3c6507266d231af33c3d2239d1321877259d..4fbd6374331aea95085924eb89a4104e8935bd24 100644 (file)
@@ -1032,6 +1032,11 @@ class Function
   set_has_recover_thunk()
   { this->has_recover_thunk_ = true; }
 
+  // Mark the function as going into a unique section.
+  void
+  set_in_unique_section()
+  { this->in_unique_section_ = true; }
+
   // Swap with another function.  Used only for the thunk which calls
   // recover.
   void
@@ -1139,6 +1144,9 @@ class Function
   bool is_recover_thunk_;
   // True if this function already has a recover thunk.
   bool has_recover_thunk_;
+  // True if this function should be put in a unique section.  This is
+  // turned on for field tracking.
+  bool in_unique_section_ : 1;
 };
 
 // A snapshot of the current binding state.
@@ -1414,6 +1422,14 @@ class Variable
   set_is_type_switch_var()
   { this->is_type_switch_var_ = true; }
 
+  // Mark the variable as going into a unique section.
+  void
+  set_in_unique_section()
+  {
+    go_assert(this->is_global_);
+    this->in_unique_section_ = true;
+  }
+
   // Traverse the initializer expression.
   int
   traverse_expression(Traverse*, unsigned int traverse_mask);
@@ -1504,6 +1520,9 @@ class Variable
   bool is_type_switch_var_ : 1;
   // True if we have determined types.
   bool determined_type_ : 1;
+  // True if this variable should be put in a unique section.  This is
+  // used for field tracking.
+  bool in_unique_section_ : 1;
 };
 
 // A variable which is really the name for a function return value, or
index d6895336657b0b8b846ba27097a51e1a06a6f571..0d9ff0380ac3940f56c9137046d727eed4360889 100644 (file)
@@ -354,6 +354,10 @@ DEF_GO_RUNTIME(PRINT_SPACE, "__go_print_space", P0(), R0())
 DEF_GO_RUNTIME(PRINT_NL, "__go_print_nl", P0(), R0())
 
 
+// Used for field tracking for data analysis.
+DEF_GO_RUNTIME(FIELDTRACK, "__go_fieldtrack", P1(POINTER), R0())
+
+
 // Remove helper macros.
 #undef ABFT6
 #undef ABFT2
index fc0d9917ebe03c1125ee347200942853e2fda54d..50a21b8cd1bc2c6aac5d52407776b144955b29de 100644 (file)
@@ -450,6 +450,7 @@ runtime_files = \
        runtime/go-deferred-recover.c \
        runtime/go-eface-compare.c \
        runtime/go-eface-val-compare.c \
+       runtime/go-fieldtrack.c \
        runtime/go-getgoroot.c \
        runtime/go-int-array-to-string.c \
        runtime/go-int-to-string.c \
index 147d9c8e6dca4ae93feb6820c8893f3ae306310e..38b8ddfcc4da13b08b1a65a765d6789fc375a0df 100644 (file)
@@ -196,7 +196,7 @@ am__objects_5 = go-append.lo go-assert.lo go-assert-interface.lo \
        go-check-interface.lo go-construct-map.lo \
        go-convert-interface.lo go-copy.lo go-defer.lo \
        go-deferred-recover.lo go-eface-compare.lo \
-       go-eface-val-compare.lo go-getgoroot.lo \
+       go-eface-val-compare.lo go-fieldtrack.lo go-getgoroot.lo \
        go-int-array-to-string.lo go-int-to-string.lo \
        go-interface-compare.lo go-interface-eface-compare.lo \
        go-interface-val-compare.lo go-make-slice.lo go-map-delete.lo \
@@ -781,6 +781,7 @@ runtime_files = \
        runtime/go-deferred-recover.c \
        runtime/go-eface-compare.c \
        runtime/go-eface-val-compare.c \
+       runtime/go-fieldtrack.c \
        runtime/go-getgoroot.c \
        runtime/go-int-array-to-string.c \
        runtime/go-int-to-string.c \
@@ -2421,6 +2422,7 @@ distclean-compile:
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/go-deferred-recover.Plo@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/go-eface-compare.Plo@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/go-eface-val-compare.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/go-fieldtrack.Plo@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/go-getgoroot.Plo@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/go-int-array-to-string.Plo@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/go-int-to-string.Plo@am__quote@
@@ -2657,6 +2659,13 @@ go-eface-val-compare.lo: runtime/go-eface-val-compare.c
 @AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
 @am__fastdepCC_FALSE@  $(LIBTOOL)  --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o go-eface-val-compare.lo `test -f 'runtime/go-eface-val-compare.c' || echo '$(srcdir)/'`runtime/go-eface-val-compare.c
 
+go-fieldtrack.lo: runtime/go-fieldtrack.c
+@am__fastdepCC_TRUE@   $(LIBTOOL)  --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT go-fieldtrack.lo -MD -MP -MF $(DEPDIR)/go-fieldtrack.Tpo -c -o go-fieldtrack.lo `test -f 'runtime/go-fieldtrack.c' || echo '$(srcdir)/'`runtime/go-fieldtrack.c
+@am__fastdepCC_TRUE@   $(am__mv) $(DEPDIR)/go-fieldtrack.Tpo $(DEPDIR)/go-fieldtrack.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      source='runtime/go-fieldtrack.c' object='go-fieldtrack.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(LIBTOOL)  --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o go-fieldtrack.lo `test -f 'runtime/go-fieldtrack.c' || echo '$(srcdir)/'`runtime/go-fieldtrack.c
+
 go-getgoroot.lo: runtime/go-getgoroot.c
 @am__fastdepCC_TRUE@   $(LIBTOOL)  --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT go-getgoroot.lo -MD -MP -MF $(DEPDIR)/go-getgoroot.Tpo -c -o go-getgoroot.lo `test -f 'runtime/go-getgoroot.c' || echo '$(srcdir)/'`runtime/go-getgoroot.c
 @am__fastdepCC_TRUE@   $(am__mv) $(DEPDIR)/go-getgoroot.Tpo $(DEPDIR)/go-getgoroot.Plo
index 0211ce61b88560d7275d1d47838463725ef9df32..9da71a7857c952fb1888fc5b7dc4d1c192b09ede 100644 (file)
@@ -168,3 +168,10 @@ func BlockProfile(p []BlockProfileRecord) (n int, ok bool)
 // If all is true, Stack formats stack traces of all other goroutines
 // into buf after the trace for the current goroutine.
 func Stack(buf []byte, all bool) int
+
+// Get field tracking information.  Only fields with a tag go:"track"
+// are tracked.  This function will add every such field that is
+// referenced to the map.  The keys in the map will be
+// PkgPath.Name.FieldName.  The value will be true for each field
+// added.
+func Fieldtrack(map[string]bool)
diff --git a/libgo/runtime/go-fieldtrack.c b/libgo/runtime/go-fieldtrack.c
new file mode 100644 (file)
index 0000000..62b36dc
--- /dev/null
@@ -0,0 +1,101 @@
+/* go-fieldtrack.c -- structure field data analysis.
+
+   Copyright 2012 The Go Authors. All rights reserved.
+   Use of this source code is governed by a BSD-style
+   license that can be found in the LICENSE file.  */
+
+#include "runtime.h"
+#include "go-type.h"
+#include "map.h"
+
+/* The compiler will track fields that have the tag go:"track".  Any
+   function that refers to such a field will call this function with a
+   string
+       fieldtrack "package.type.field"
+
+   This function does not actually do anything.  Instead, we gather
+   the field tracking information by looking for strings of that form
+   in the read-only data section.  This is, of course, a horrible
+   hack, but it's good enough for now.  We can improve it, e.g., by a
+   linker plugin, if this turns out to be useful.  */
+
+void
+__go_fieldtrack (byte *p __attribute__ ((unused)))
+{
+}
+
+/* A runtime function to add all the tracked fields to a
+   map[string]bool.  */
+
+extern const char _etext[] __attribute__ ((weak));
+extern const char __etext[] __attribute__ ((weak));
+extern const char __data_start[] __attribute__ ((weak));
+extern const char _edata[] __attribute__ ((weak));
+extern const char __edata[] __attribute__ ((weak));
+extern const char __bss_start[] __attribute__ ((weak));
+
+void runtime_Fieldtrack (struct __go_map *) __asm__ ("runtime.Fieldtrack");
+
+void
+runtime_Fieldtrack (struct __go_map *m)
+{
+  const char *p;
+  const char *pend;
+  const char *prefix;
+  size_t prefix_len;
+
+  p = __data_start;
+  if (p == NULL)
+    p = __etext;
+  if (p == NULL)
+    p = _etext;
+  if (p == NULL)
+    return;
+
+  pend = __edata;
+  if (pend == NULL)
+    pend = _edata;
+  if (pend == NULL)
+    pend = __bss_start;
+  if (pend == NULL)
+    return;
+
+  prefix = "fieldtrack ";
+  prefix_len = __builtin_strlen (prefix);
+
+  while (p < pend)
+    {
+      const char *q1;
+      const char *q2;
+
+      q1 = __builtin_memchr (p + prefix_len, '"', pend - (p + prefix_len));
+      if (q1 == NULL)
+       break;
+
+      if (__builtin_memcmp (q1 - prefix_len, prefix, prefix_len) != 0)
+       {
+         p = q1 + 1;
+         continue;
+       }
+
+      q1++;
+      q2 = __builtin_memchr (q1, '"', pend - q1);
+      if (q2 == NULL)
+       break;
+
+      if (__builtin_memchr (q1, '\0', q2 - q1) == NULL)
+       {
+         String s;
+         void *v;
+         _Bool *pb;
+
+         s.str = (const byte *) q1;
+         s.len = q2 - q1;
+         v = __go_map_index (m, &s, 1);
+         pb = (_Bool *) v;
+         *pb = 1;
+       }
+
+      p = q2;
+    }
+}