summaryrefslogtreecommitdiffstats
path: root/js/src/devtools/rootAnalysis/t/hazards
diff options
context:
space:
mode:
Diffstat (limited to 'js/src/devtools/rootAnalysis/t/hazards')
-rw-r--r--js/src/devtools/rootAnalysis/t/hazards/source.cpp186
-rw-r--r--js/src/devtools/rootAnalysis/t/hazards/test.py47
2 files changed, 233 insertions, 0 deletions
diff --git a/js/src/devtools/rootAnalysis/t/hazards/source.cpp b/js/src/devtools/rootAnalysis/t/hazards/source.cpp
new file mode 100644
index 000000000..7f84a99db
--- /dev/null
+++ b/js/src/devtools/rootAnalysis/t/hazards/source.cpp
@@ -0,0 +1,186 @@
+#define ANNOTATE(property) __attribute__((tag(property)))
+
+struct Cell { int f; } ANNOTATE("GC Thing");
+
+struct RootedCell { RootedCell(Cell*) {} } ANNOTATE("Rooted Pointer");
+
+class AutoSuppressGC_Base {
+ public:
+ AutoSuppressGC_Base() {}
+ ~AutoSuppressGC_Base() {}
+} ANNOTATE("Suppress GC");
+
+class AutoSuppressGC_Child : public AutoSuppressGC_Base {
+ public:
+ AutoSuppressGC_Child() : AutoSuppressGC_Base() {}
+};
+
+class AutoSuppressGC {
+ AutoSuppressGC_Child helpImBeingSuppressed;
+
+ public:
+ AutoSuppressGC() {}
+};
+
+extern void GC() ANNOTATE("GC Call");
+extern void invisible();
+
+void GC()
+{
+ // If the implementation is too trivial, the function body won't be emitted at all.
+ asm("");
+ invisible();
+}
+
+extern void usecell(Cell*);
+
+void suppressedFunction() {
+ GC(); // Calls GC, but is always called within AutoSuppressGC
+}
+
+void halfSuppressedFunction() {
+ GC(); // Calls GC, but is sometimes called within AutoSuppressGC
+}
+
+void unsuppressedFunction() {
+ GC(); // Calls GC, never within AutoSuppressGC
+}
+
+volatile static int x = 3;
+volatile static int* xp = &x;
+struct GCInDestructor {
+ ~GCInDestructor() {
+ invisible();
+ asm("");
+ *xp = 4;
+ GC();
+ }
+};
+
+Cell*
+f()
+{
+ GCInDestructor kaboom;
+
+ Cell cell;
+ Cell* cell1 = &cell;
+ Cell* cell2 = &cell;
+ Cell* cell3 = &cell;
+ Cell* cell4 = &cell;
+ {
+ AutoSuppressGC nogc;
+ suppressedFunction();
+ halfSuppressedFunction();
+ }
+ usecell(cell1);
+ halfSuppressedFunction();
+ usecell(cell2);
+ unsuppressedFunction();
+ {
+ // Old bug: it would look from the first AutoSuppressGC constructor it
+ // found to the last destructor. This statement *should* have no effect.
+ AutoSuppressGC nogc;
+ }
+ usecell(cell3);
+ Cell* cell5 = &cell;
+ usecell(cell5);
+
+ // Hazard in return value due to ~GCInDestructor
+ Cell* cell6 = &cell;
+ return cell6;
+}
+
+Cell* copy_and_gc(Cell* src)
+{
+ GC();
+ return reinterpret_cast<Cell*>(88);
+}
+
+void use(Cell* cell)
+{
+ static int x = 0;
+ if (cell)
+ x++;
+}
+
+struct CellContainer {
+ Cell* cell;
+ CellContainer() {
+ asm("");
+ }
+};
+
+void loopy()
+{
+ Cell cell;
+
+ // No hazard: haz1 is not live during call to copy_and_gc.
+ Cell* haz1;
+ for (int i = 0; i < 10; i++) {
+ haz1 = copy_and_gc(haz1);
+ }
+
+ // No hazard: haz2 is live up to just before the GC, and starting at the
+ // next statement after it, but not across the GC.
+ Cell* haz2 = &cell;
+ for (int j = 0; j < 10; j++) {
+ use(haz2);
+ GC();
+ haz2 = &cell;
+ }
+
+ // Hazard: haz3 is live from the final statement in one iteration, across
+ // the GC in the next, to the use in the 2nd statement.
+ Cell* haz3;
+ for (int k = 0; k < 10; k++) {
+ GC();
+ use(haz3);
+ haz3 = &cell;
+ }
+
+ // Hazard: haz4 is live across a GC hidden in a loop.
+ Cell* haz4 = &cell;
+ for (int i2 = 0; i2 < 10; i2++) {
+ GC();
+ }
+ use(haz4);
+
+ // Hazard: haz5 is live from within a loop across a GC.
+ Cell* haz5;
+ for (int i3 = 0; i3 < 10; i3++) {
+ haz5 = &cell;
+ }
+ GC();
+ use(haz5);
+
+ // No hazard: similar to the haz3 case, but verifying that we do not get
+ // into an infinite loop.
+ Cell* haz6;
+ for (int i4 = 0; i4 < 10; i4++) {
+ GC();
+ haz6 = &cell;
+ }
+
+ // No hazard: haz7 is constructed within the body, so it can't make a
+ // hazard across iterations. Note that this requires CellContainer to have
+ // a constructor, because otherwise the analysis doesn't see where
+ // variables are declared. (With the constructor, it knows that
+ // construction of haz7 obliterates any previous value it might have had.
+ // Not that that's possible given its scope, but the analysis doesn't get
+ // that information.)
+ for (int i5 = 0; i5 < 10; i5++) {
+ GC();
+ CellContainer haz7;
+ use(haz7.cell);
+ haz7.cell = &cell;
+ }
+
+ // Hazard: make sure we *can* see hazards across iterations involving
+ // CellContainer;
+ CellContainer haz8;
+ for (int i6 = 0; i6 < 10; i6++) {
+ GC();
+ use(haz8.cell);
+ haz8.cell = &cell;
+ }
+}
diff --git a/js/src/devtools/rootAnalysis/t/hazards/test.py b/js/src/devtools/rootAnalysis/t/hazards/test.py
new file mode 100644
index 000000000..3eb08aa09
--- /dev/null
+++ b/js/src/devtools/rootAnalysis/t/hazards/test.py
@@ -0,0 +1,47 @@
+test.compile("source.cpp")
+test.run_analysis_script('gcTypes')
+
+# gcFunctions should be the inverse, but we get to rely on unmangled names here.
+gcFunctions = test.load_gcFunctions()
+print(gcFunctions)
+assert('void GC()' in gcFunctions)
+assert('void suppressedFunction()' not in gcFunctions)
+assert('void halfSuppressedFunction()' in gcFunctions)
+assert('void unsuppressedFunction()' in gcFunctions)
+assert('Cell* f()' in gcFunctions)
+
+hazards = test.load_hazards()
+hazmap = {haz.variable: haz for haz in hazards}
+assert('cell1' not in hazmap)
+assert('cell2' in hazmap)
+assert('cell3' in hazmap)
+assert('cell4' not in hazmap)
+assert('cell5' not in hazmap)
+assert('cell6' not in hazmap)
+assert('<returnvalue>' in hazmap)
+
+# All hazards should be in f() and loopy()
+assert(hazmap['cell2'].function == 'Cell* f()')
+print(len(set(haz.function for haz in hazards)))
+assert(len(set(haz.function for haz in hazards)) == 2)
+
+# Check that the correct GC call is reported for each hazard. (cell3 has a
+# hazard from two different GC calls; it doesn't really matter which is
+# reported.)
+assert(hazmap['cell2'].GCFunction == 'void halfSuppressedFunction()')
+assert(hazmap['cell3'].GCFunction in ('void halfSuppressedFunction()', 'void unsuppressedFunction()'))
+assert(hazmap['<returnvalue>'].GCFunction == 'void GCInDestructor::~GCInDestructor()')
+
+# Type names are handy to have in the report.
+assert(hazmap['cell2'].type == 'Cell*')
+assert(hazmap['<returnvalue>'].type == 'Cell*')
+
+# loopy hazards. See comments in source.
+assert('haz1' not in hazmap);
+assert('haz2' not in hazmap);
+assert('haz3' in hazmap);
+assert('haz4' in hazmap);
+assert('haz5' in hazmap);
+assert('haz6' not in hazmap);
+assert('haz7' not in hazmap);
+assert('haz8' in hazmap);