summaryrefslogtreecommitdiff
path: root/ghc/runtime/gum/GlobAddr.lc
diff options
context:
space:
mode:
Diffstat (limited to 'ghc/runtime/gum/GlobAddr.lc')
-rw-r--r--ghc/runtime/gum/GlobAddr.lc362
1 files changed, 362 insertions, 0 deletions
diff --git a/ghc/runtime/gum/GlobAddr.lc b/ghc/runtime/gum/GlobAddr.lc
new file mode 100644
index 0000000000..af690e3daf
--- /dev/null
+++ b/ghc/runtime/gum/GlobAddr.lc
@@ -0,0 +1,362 @@
+%
+% (c) The AQUA/Parade Projects, Glasgow University, 1995
+%
+%************************************************************************
+%* *
+\section[GlobAddr.lc]{Global Address Manipulation}
+%* *
+%************************************************************************
+
+\begin{code}
+#ifdef PAR /* whole file */
+
+#include "rtsdefs.h"
+\end{code}
+
+@globalAddr@ structures are allocated in chunks to reduce malloc overhead.
+
+\begin{code}
+
+GALA *freeGALAList = NULL;
+
+#define GCHUNK (1024 * sizeof(W_) / sizeof(GALA))
+ /* Number of globalAddr cells to allocate in one go */
+
+static GALA *
+allocGALA(STG_NO_ARGS)
+{
+ GALA *gl, *p;
+
+ if ((gl = freeGALAList) != NULL) {
+ freeGALAList = gl->next;
+ } else if ((gl = (GALA *) malloc(GCHUNK * sizeof(GALA))) != NULL) {
+ freeGALAList = gl + 1;
+ for (p = freeGALAList; p < gl + GCHUNK - 1; p++)
+ p->next = p + 1;
+ p->next = NULL;
+ } else {
+ fflush(stdout);
+ fprintf(stderr, "VM exhausted\n");
+ EXIT(EXIT_FAILURE);
+ }
+ return gl;
+}
+
+\end{code}
+
+We don't really like GLOBAL_TASK_ID, so we keep a table of TASK_ID to
+PE mappings. The idea is that a PE identifier will fit in 16 bits, whereas
+a TASK_ID may not.
+
+\begin{code}
+
+HashTable *taskIDtoPEtable = NULL;
+
+static int nextPE = 0;
+
+W_
+taskIDtoPE(gtid)
+GLOBAL_TASK_ID gtid;
+{
+ return (W_) lookupHashTable(taskIDtoPEtable, gtid);
+}
+
+int thisPE;
+
+void
+registerTask(gtid)
+GLOBAL_TASK_ID gtid;
+{
+ if (gtid == mytid)
+ thisPE = nextPE;
+
+ insertHashTable(taskIDtoPEtable, gtid, (void *) (W_) nextPE++);
+}
+
+\end{code}
+
+The local address to global address mapping returns a globalAddr structure
+(pe task id, slot, weight) for any closure in the local heap which has a
+global identity. Such closures may be copies of normal form objects with
+a remote `master' location, @FetchMe@ nodes referencing remote objects, or
+globally visible objects in the local heap (for which we are the master).
+
+\begin{code}
+
+HashTable *LAtoGALAtable = NULL;
+
+globalAddr *
+LAGAlookup(addr)
+P_ addr;
+{
+ GALA *gala;
+
+ /* We never look for GA's on indirections */
+ ASSERT(INFO_PTR(addr) != (W_) Ind_info);
+ if ((gala = lookupHashTable(LAtoGALAtable, (W_) addr)) == NULL)
+ return NULL;
+ else
+ return &(gala->ga);
+}
+
+\end{code}
+
+We also manage a mapping of global addresses to local addresses, so that
+we can ``common up'' multiple references to the same object as they arrive
+in data packets from remote PEs.
+
+The global address to local address mapping is actually managed via a
+``packed global address'' to GALA hash table. The packed global
+address takes the interesting part of the @globalAddr@ structure
+(i.e. the pe and slot fields) and packs them into a single word
+suitable for hashing.
+
+\begin{code}
+
+HashTable *pGAtoGALAtable = NULL;
+
+P_
+GALAlookup(ga)
+globalAddr *ga;
+{
+ W_ pga = PACK_GA(taskIDtoPE(ga->loc.gc.gtid), ga->loc.gc.slot);
+ GALA *gala;
+ P_ la;
+
+ if ((gala = (GALA *) lookupHashTable(pGAtoGALAtable, pga)) == NULL)
+ return NULL;
+ else {
+ la = gala->la;
+ /*
+ * Bypass any indirections when returning a local closure to the caller.
+ * Note that we do not short-circuit the entry in the GALA tables right
+ * now, because we would have to do a hash table delete and insert in
+ * the LAtoGALAtable to keep that table up-to-date for preferred GALA pairs.
+ * That's probably a bit expensive.
+ */
+ while (IS_INDIRECTION(INFO_PTR(la)))
+ la = (P_) IND_CLOSURE_PTR(la);
+ return la;
+ }
+}
+
+\end{code}
+
+External references to our globally-visible closures are managed through an
+indirection table. The idea is that the closure may move about as the result
+of local garbage collections, but its global identity is determined by its
+slot in the indirection table, which never changes.
+
+The indirection table is maintained implicitly as part of the global
+address to local address table. We need only keep track of the
+highest numbered indirection index allocated so far, along with a free
+list of lower numbered indices no longer in use.
+
+\begin{code}
+
+static I_ nextIndirection = 0;
+
+GALA *freeIndirections = NULL;
+
+\end{code}
+
+Allocate an indirection slot for the closure currently at address @addr@.
+
+\begin{code}
+
+static GALA *
+allocIndirection(addr)
+P_ addr;
+{
+ GALA *gala;
+
+ if ((gala = freeIndirections) != NULL) {
+ freeIndirections = gala->next;
+ } else {
+ gala = allocGALA();
+ gala->ga.loc.gc.gtid = mytid;
+ gala->ga.loc.gc.slot = nextIndirection++;
+ }
+ gala->ga.weight = MAX_GA_WEIGHT;
+ gala->la = addr;
+ return gala;
+}
+
+\end{code}
+
+Make a local closure at @addr@ globally visible. We have to allocate an
+indirection slot for it, and update both the local address to global address
+and global address to local address maps.
+
+\begin{code}
+
+GALA *liveIndirections = NULL;
+
+globalAddr *
+MakeGlobal(addr, preferred)
+P_ addr;
+rtsBool preferred;
+{
+ GALA *oldGALA = lookupHashTable(LAtoGALAtable, (W_) addr);
+ GALA *newGALA = allocIndirection(addr);
+ W_ pga = PACK_GA(thisPE, newGALA->ga.loc.gc.slot);
+
+ ASSERT(GALAlookup(&(newGALA->ga)) == NULL);
+
+ newGALA->la = addr;
+ newGALA->preferred = preferred;
+
+ if (preferred) {
+ /* The new GA is now the preferred GA for the LA */
+ if (oldGALA != NULL) {
+ oldGALA->preferred = rtsFalse;
+ (void) removeHashTable(LAtoGALAtable, (W_) addr, (void *) oldGALA);
+ }
+ insertHashTable(LAtoGALAtable, (W_) addr, (void *) newGALA);
+ }
+
+ newGALA->next = liveIndirections;
+ liveIndirections = newGALA;
+
+ insertHashTable(pGAtoGALAtable, pga, (void *) newGALA);
+
+ return &(newGALA->ga);
+}
+
+\end{code}
+
+Assign an existing remote global address to an existing closure.
+We do not retain the @globalAddr@ structure that's passed in as an argument,
+so it can be a static in the calling routine.
+
+\begin{code}
+
+GALA *liveRemoteGAs = NULL;
+
+globalAddr *
+setRemoteGA(addr, ga, preferred)
+P_ addr;
+globalAddr *ga;
+rtsBool preferred;
+{
+ GALA *oldGALA = lookupHashTable(LAtoGALAtable, (W_) addr);
+ GALA *newGALA = allocGALA();
+ W_ pga = PACK_GA(taskIDtoPE(ga->loc.gc.gtid), ga->loc.gc.slot);
+
+ ASSERT(ga->loc.gc.gtid != mytid);
+ ASSERT(ga->weight > 0);
+ ASSERT(GALAlookup(ga) == NULL);
+
+ newGALA->ga = *ga;
+ newGALA->la = addr;
+ newGALA->preferred = preferred;
+
+ if (preferred) {
+ /* The new GA is now the preferred GA for the LA */
+ if (oldGALA != NULL) {
+ oldGALA->preferred = rtsFalse;
+ (void) removeHashTable(LAtoGALAtable, (W_) addr, (void *) oldGALA);
+ }
+ insertHashTable(LAtoGALAtable, (W_) addr, (void *) newGALA);
+ }
+ newGALA->next = liveRemoteGAs;
+ liveRemoteGAs = newGALA;
+
+ insertHashTable(pGAtoGALAtable, pga, (void *) newGALA);
+
+ ga->weight = 0;
+
+ return &(newGALA->ga);
+}
+\end{code}
+
+Give me a bit of weight to give away on a new reference to a particular global
+address. If we run down to nothing, we have to assign a new GA.
+
+\begin{code}
+
+void
+splitWeight(to, from)
+globalAddr *to, *from;
+{
+ /* Make sure we have enough weight to split */
+ if (from->weight == 1)
+ from = MakeGlobal(GALAlookup(from), rtsTrue);
+
+ to->loc = from->loc;
+
+ if (from->weight == 0)
+ to->weight = 1L << (BITS_IN(unsigned) - 1);
+ else
+ to->weight = from->weight / 2;
+
+ from->weight -= to->weight;
+}
+
+\end{code}
+
+Here, I am returning a bit of weight that a remote PE no longer needs.
+
+\begin{code}
+
+globalAddr *
+addWeight(ga)
+globalAddr *ga;
+{
+ W_ pga = PACK_GA(taskIDtoPE(ga->loc.gc.gtid), ga->loc.gc.slot);
+ GALA *gala = (GALA *) lookupHashTable(pGAtoGALAtable, pga);
+
+#ifdef DEBUG_WEIGHT
+ fprintf(stderr, "Adding weight %x to (%x, %d, %x), preferred = %d\n", ga->weight,
+ gala->ga.loc.gc.gtid, gala->ga.loc.gc.slot, gala->ga.weight, gala->preferred);
+#endif
+ gala->ga.weight += ga->weight;
+ ga->weight = 0;
+
+ return &(gala->ga);
+}
+
+\end{code}
+
+Initialize all of the global address structures: the task ID to PE id
+map, the local address to global address map, the global address to
+local address map, and the indirection table.
+
+\begin{code}
+
+void
+initGAtables(STG_NO_ARGS)
+{
+ taskIDtoPEtable = allocHashTable();
+ LAtoGALAtable = allocHashTable();
+ pGAtoGALAtable = allocHashTable();
+}
+
+\end{code}
+
+Rebuild the LA->GA table, assuming that the addresses in the GALAs are correct.
+
+\begin{code}
+
+void
+RebuildLAGAtable(STG_NO_ARGS)
+{
+ GALA *gala;
+
+ /* The old LA->GA table is worthless */
+ freeHashTable(LAtoGALAtable, NULL);
+ LAtoGALAtable = allocHashTable();
+
+ for (gala = liveIndirections; gala != NULL; gala = gala->next) {
+ if (gala->preferred)
+ insertHashTable(LAtoGALAtable, (W_) gala->la, (void *) gala);
+ }
+
+ for (gala = liveRemoteGAs; gala != NULL; gala = gala->next) {
+ if (gala->preferred)
+ insertHashTable(LAtoGALAtable, (W_) gala->la, (void *) gala);
+ }
+}
+
+#endif /* PAR -- whole file */
+\end{code}