===================================================================
@@ -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::
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::
}
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(Bv
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.
===================================================================
@@ -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) $
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)
===================================================================
@@ -3075,7 +3075,8 @@ Function::Function(Function_type* type,
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, Expressio
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* gog
btype,
package != NULL,
Gogo::is_hidden_name(name),
+ this->in_unique_section_,
this->location_);
else if (function == NULL)
{
===================================================================
@@ -354,6 +354,10 @@ DEF_GO_RUNTIME(PRINT_SPACE, "__go_print_
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
===================================================================
@@ -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
===================================================================
@@ -1842,7 +1842,7 @@ class Field_reference_expression : publi
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 : publi
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 : publi
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.
===================================================================
@@ -1316,6 +1316,9 @@ Function::get_or_make_decl(Gogo* gogo, N
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)
===================================================================
@@ -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
===================================================================
@@ -10580,6 +10580,102 @@ Expression::make_map_index(Expression* m
// 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*
===================================================================
@@ -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;
+ }
+}
===================================================================
@@ -168,3 +168,10 @@ func BlockProfile(p []BlockProfileRecord
// 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)
===================================================================
@@ -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 \