diff options
author | Ian Lance Taylor <iant@google.com> | 2012-11-29 18:11:17 +0000 |
---|---|---|
committer | Ian Lance Taylor <ian@gcc.gnu.org> | 2012-11-29 18:11:17 +0000 |
commit | 744c3195ef778200a0acfc4779e3f2d48237bbf1 (patch) | |
tree | d146677ebcdb05c9d7a8c8b84cab5fb7fa9a0f99 | |
parent | 53750ab003876bae145f36431392e2531e70e7cc (diff) | |
download | gcc-744c3195ef778200a0acfc4779e3f2d48237bbf1.zip gcc-744c3195ef778200a0acfc4779e3f2d48237bbf1.tar.gz gcc-744c3195ef778200a0acfc4779e3f2d48237bbf1.tar.bz2 |
compiler, runtime: Track fields with tag go:"track".
* 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
-rw-r--r-- | gcc/go/ChangeLog | 7 | ||||
-rw-r--r-- | gcc/go/Make-lang.in | 4 | ||||
-rw-r--r-- | gcc/go/go-gcc.cc | 16 | ||||
-rw-r--r-- | gcc/go/gofrontend/backend.h | 8 | ||||
-rw-r--r-- | gcc/go/gofrontend/expressions.cc | 96 | ||||
-rw-r--r-- | gcc/go/gofrontend/expressions.h | 7 | ||||
-rw-r--r-- | gcc/go/gofrontend/gogo-tree.cc | 3 | ||||
-rw-r--r-- | gcc/go/gofrontend/gogo.cc | 6 | ||||
-rw-r--r-- | gcc/go/gofrontend/gogo.h | 19 | ||||
-rw-r--r-- | gcc/go/gofrontend/runtime.def | 4 | ||||
-rw-r--r-- | libgo/Makefile.am | 1 | ||||
-rw-r--r-- | libgo/Makefile.in | 11 | ||||
-rw-r--r-- | libgo/go/runtime/debug.go | 7 | ||||
-rw-r--r-- | libgo/runtime/go-fieldtrack.c | 101 |
14 files changed, 282 insertions, 8 deletions
diff --git a/gcc/go/ChangeLog b/gcc/go/ChangeLog index 9a90247..1797763 100644 --- a/gcc/go/ChangeLog +++ b/gcc/go/ChangeLog @@ -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) diff --git a/gcc/go/Make-lang.in b/gcc/go/Make-lang.in index b3edda6..473a581 100644 --- a/gcc/go/Make-lang.in +++ b/gcc/go/Make-lang.in @@ -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) diff --git a/gcc/go/go-gcc.cc b/gcc/go/go-gcc.cc index 84bc972..f8bbaf4 100644 --- a/gcc/go/go-gcc.cc +++ b/gcc/go/go-gcc.cc @@ -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. diff --git a/gcc/go/gofrontend/backend.h b/gcc/go/gofrontend/backend.h index 2b14132..fe6db74 100644 --- a/gcc/go/gofrontend/backend.h +++ b/gcc/go/gofrontend/backend.h @@ -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 diff --git a/gcc/go/gofrontend/expressions.cc b/gcc/go/gofrontend/expressions.cc index 4ff6272..911ce5a 100644 --- a/gcc/go/gofrontend/expressions.cc +++ b/gcc/go/gofrontend/expressions.cc @@ -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* diff --git a/gcc/go/gofrontend/expressions.h b/gcc/go/gofrontend/expressions.h index eea141f..7bc4242 100644 --- a/gcc/go/gofrontend/expressions.h +++ b/gcc/go/gofrontend/expressions.h @@ -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. diff --git a/gcc/go/gofrontend/gogo-tree.cc b/gcc/go/gofrontend/gogo-tree.cc index 95ec70e..7159dfb 100644 --- a/gcc/go/gofrontend/gogo-tree.cc +++ b/gcc/go/gofrontend/gogo-tree.cc @@ -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) diff --git a/gcc/go/gofrontend/gogo.cc b/gcc/go/gofrontend/gogo.cc index 766fe90..0f63afb 100644 --- a/gcc/go/gofrontend/gogo.cc +++ b/gcc/go/gofrontend/gogo.cc @@ -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) { diff --git a/gcc/go/gofrontend/gogo.h b/gcc/go/gofrontend/gogo.h index e23b3c6..4fbd637 100644 --- a/gcc/go/gofrontend/gogo.h +++ b/gcc/go/gofrontend/gogo.h @@ -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 diff --git a/gcc/go/gofrontend/runtime.def b/gcc/go/gofrontend/runtime.def index d689533..0d9ff03 100644 --- a/gcc/go/gofrontend/runtime.def +++ b/gcc/go/gofrontend/runtime.def @@ -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 diff --git a/libgo/Makefile.am b/libgo/Makefile.am index fc0d991..50a21b8 100644 --- a/libgo/Makefile.am +++ b/libgo/Makefile.am @@ -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 \ diff --git a/libgo/Makefile.in b/libgo/Makefile.in index 147d9c8..38b8ddf 100644 --- a/libgo/Makefile.in +++ b/libgo/Makefile.in @@ -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 diff --git a/libgo/go/runtime/debug.go b/libgo/go/runtime/debug.go index 0211ce6..9da71a7 100644 --- a/libgo/go/runtime/debug.go +++ b/libgo/go/runtime/debug.go @@ -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 index 0000000..62b36dc --- /dev/null +++ b/libgo/runtime/go-fieldtrack.c @@ -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; + } +} |