diff options
Diffstat (limited to 'js/src/devtools/rootAnalysis/t/hazards')
-rw-r--r-- | js/src/devtools/rootAnalysis/t/hazards/source.cpp | 186 | ||||
-rw-r--r-- | js/src/devtools/rootAnalysis/t/hazards/test.py | 47 |
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); |