diff --git a/mps/code/mpm.h b/mps/code/mpm.h index a5ac16b41a2..256940d4d5a 100644 --- a/mps/code/mpm.h +++ b/mps/code/mpm.h @@ -472,10 +472,9 @@ extern double TraceWorkFactor; } \ END -extern Res TraceScanArea(ScanState ss, Addr *base, Addr *limit); -extern Res TraceScanAreaTagged(ScanState ss, Addr *base, Addr *limit); -extern Res TraceScanAreaMasked(ScanState ss, - Addr *base, Addr *limit, Word mask); +extern Res TraceScanArea(ScanState ss, Word *base, Word *limit); +extern Res TraceScanAreaMasked(ScanState ss, Word *base, Word *limit, + Word mask, Word value); extern void TraceScanSingleRef(TraceSet ts, Rank rank, Arena arena, Seg seg, Ref *refIO); @@ -948,15 +947,19 @@ extern void LDMerge(mps_ld_t ld, Arena arena, mps_ld_t from); extern Res RootCreateTable(Root *rootReturn, Arena arena, Rank rank, RootMode mode, - Addr *base, Addr *limit); + Word *base, Word *limit); extern Res RootCreateTableMasked(Root *rootReturn, Arena arena, Rank rank, RootMode mode, - Addr *base, Addr *limit, + Word *base, Word *limit, Word mask); extern Res RootCreateReg(Root *rootReturn, Arena arena, Rank rank, Thread thread, mps_reg_scan_t scan, void *p, size_t s); +extern Res RootCreateRegMasked(Root *rootReturn, Arena arena, + Rank rank, Thread thread, + Word mask, Word pattern, + Addr stackBot); extern Res RootCreateFmt(Root *rootReturn, Arena arena, Rank rank, RootMode mode, mps_fmt_scan_t scan, diff --git a/mps/code/mpmst.h b/mps/code/mpmst.h index d212e0e3307..c5bf54bdc2b 100644 --- a/mps/code/mpmst.h +++ b/mps/code/mpmst.h @@ -791,7 +791,7 @@ typedef struct mps_arena_s { Bool emergency; /* garbage collect in emergency mode? */ - Addr *stackAtArenaEnter; /* NULL or top of client stack, in the thread */ + Word *stackAtArenaEnter; /* NULL or top of client stack, in the thread */ /* that then entered the MPS. */ Sig sig; diff --git a/mps/code/mpmtypes.h b/mps/code/mpmtypes.h index a6030e4c1e5..c241a04c64a 100644 --- a/mps/code/mpmtypes.h +++ b/mps/code/mpmtypes.h @@ -362,6 +362,7 @@ enum { RootTABLE, RootTABLE_MASKED, RootREG, + RootREG_MASKED, RootFMT, RootLIMIT }; diff --git a/mps/code/mps.h b/mps/code/mps.h index 4e75d49b73a..515edbbe5e0 100644 --- a/mps/code/mps.h +++ b/mps/code/mps.h @@ -672,6 +672,10 @@ extern mps_res_t mps_root_create_fmt(mps_root_t *, mps_arena_t, extern mps_res_t mps_root_create_reg(mps_root_t *, mps_arena_t, mps_rank_t, mps_rm_t, mps_thr_t, mps_reg_scan_t, void *, size_t); +extern mps_res_t mps_root_create_reg_masked(mps_root_t *, mps_arena_t, + mps_rank_t, mps_rm_t, mps_thr_t, + mps_word_t, mps_word_t, + void *); extern void mps_root_destroy(mps_root_t); extern mps_res_t mps_stack_scan_ambig(mps_ss_t, mps_thr_t, diff --git a/mps/code/mpsi.c b/mps/code/mpsi.c index 283fbc6123e..67f23e00859 100644 --- a/mps/code/mpsi.c +++ b/mps/code/mpsi.c @@ -1304,7 +1304,7 @@ mps_res_t mps_root_create_table(mps_root_t *mps_root_o, mps_arena_t arena, /* limit pointers. Be careful. */ res = RootCreateTable(&root, arena, rank, mode, - (Addr *)base, (Addr *)base + size); + (Word *)base, (Word *)base + size); ArenaLeave(arena); @@ -1335,7 +1335,7 @@ mps_res_t mps_root_create_table_masked(mps_root_t *mps_root_o, /* See .root.table-size. */ res = RootCreateTableMasked(&root, arena, rank, mode, - (Addr *)base, (Addr *)base + size, + (Word *)base, (Word *)base + size, mask); ArenaLeave(arena); @@ -1401,6 +1401,37 @@ mps_res_t mps_root_create_reg(mps_root_t *mps_root_o, mps_arena_t arena, } +mps_res_t mps_root_create_reg_masked(mps_root_t *mps_root_o, mps_arena_t arena, + mps_rank_t mps_rank, mps_rm_t mps_rm, + mps_thr_t thread, mps_word_t mask, + mps_word_t pattern, void *reg_scan_p) +{ + Rank rank = (Rank)mps_rank; + Root root; + Res res; + + ArenaEnter(arena); + + AVER(mps_root_o != NULL); + AVER(reg_scan_p != NULL); /* stackBot */ + AVER(AddrIsAligned(reg_scan_p, sizeof(Word))); + AVER(rank == mps_rank_ambig()); + AVER(mps_rm == (mps_rm_t)0); + AVER((~mask & pattern) == 0); + + /* See .root-mode. */ + res = RootCreateRegMasked(&root, arena, rank, thread, + mask, pattern, (Addr)reg_scan_p); + + ArenaLeave(arena); + + if (res != ResOK) + return (mps_res_t)res; + *mps_root_o = (mps_root_t)root; + return MPS_RES_OK; +} + + /* mps_stack_scan_ambig -- scan the thread state ambiguously * * See .reg-scan. */ @@ -1410,7 +1441,7 @@ mps_res_t mps_stack_scan_ambig(mps_ss_t mps_ss, { ScanState ss = PARENT(ScanStateStruct, ss_s, mps_ss); UNUSED(s); - return ThreadScan(ss, thread, p); + return ThreadScan(ss, thread, (Word *)p, sizeof(mps_word_t) - 1, 0); } diff --git a/mps/code/prmci3fr.c b/mps/code/prmci3fr.c index 75cf94e871f..2625559564c 100644 --- a/mps/code/prmci3fr.c +++ b/mps/code/prmci3fr.c @@ -38,17 +38,19 @@ Addr MutatorFaultContextSP(MutatorFaultContext mfc) } -Res MutatorFaultContextScan(ScanState ss, MutatorFaultContext mfc) +Res MutatorFaultContextScan(ScanState ss, MutatorFaultContext mfc, + Word mask, Word pattern) { Res res; /* This scans the root registers (.context.regroots). It also unnecessarily scans the rest of the context. The optimisation to scan only relevant parts would be machine dependent. */ - res = TraceScanAreaTagged( + res = TraceScanAreaMasked( ss, - (Addr *)mfc->ucontext, - (Addr *)((char *)mfc->ucontext + sizeof(*(mfc->ucontext))) + (Word *)mfc->ucontext, + (Word *)((char *)mfc->ucontext + sizeof(*(mfc->ucontext))), + mask, pattern ); return res; diff --git a/mps/code/prmci3li.c b/mps/code/prmci3li.c index 08737d363c3..7f6400943f1 100644 --- a/mps/code/prmci3li.c +++ b/mps/code/prmci3li.c @@ -101,7 +101,8 @@ Addr MutatorFaultContextSP(MutatorFaultContext mfc) } -Res MutatorFaultContextScan(ScanState ss, MutatorFaultContext mfc) +Res MutatorFaultContextScan(ScanState ss, MutatorFaultContext mfc, + Word mask, Word pattern) { mcontext_t *mc; Res res; @@ -110,9 +111,10 @@ Res MutatorFaultContextScan(ScanState ss, MutatorFaultContext mfc) unnecessarily scans the rest of the context. The optimisation to scan only relevant parts would be machine dependent. */ mc = &mfc->ucontext->uc_mcontext; - res = TraceScanAreaTagged(ss, - (Addr *)mc, - (Addr *)((char *)mc + sizeof(*mc))); + res = TraceScanAreaMasked(ss, + (Word *)mc, + (Word *)((char *)mc + sizeof(*mc)), + mask, pattern); return res; } diff --git a/mps/code/prmci3xc.c b/mps/code/prmci3xc.c index 67f3f5822df..036550ae3e0 100644 --- a/mps/code/prmci3xc.c +++ b/mps/code/prmci3xc.c @@ -96,7 +96,8 @@ Addr MutatorFaultContextSP(MutatorFaultContext mfc) } -Res MutatorFaultContextScan(ScanState ss, MutatorFaultContext mfc) +Res MutatorFaultContextScan(ScanState ss, MutatorFaultContext mfc, + Word mask, Word pattern) { x86_thread_state32_t *mc; Res res; @@ -105,9 +106,10 @@ Res MutatorFaultContextScan(ScanState ss, MutatorFaultContext mfc) unnecessarily scans the rest of the context. The optimisation to scan only relevant parts would be machine dependent. */ mc = mfc->threadState; - res = TraceScanAreaTagged(ss, - (Addr *)mc, - (Addr *)((char *)mc + sizeof(*mc))); + res = TraceScanAreaMasked(ss, + (Word *)mc, + (Word *)((char *)mc + sizeof(*mc)), + mask, pattern); return res; } diff --git a/mps/code/prmci6fr.c b/mps/code/prmci6fr.c index db20d01216b..debc9c760bf 100644 --- a/mps/code/prmci6fr.c +++ b/mps/code/prmci6fr.c @@ -32,17 +32,19 @@ Addr MutatorFaultContextSP(MutatorFaultContext mfc) } -Res MutatorFaultContextScan(ScanState ss, MutatorFaultContext mfc) +Res MutatorFaultContextScan(ScanState ss, MutatorFaultContext mfc, + Word mask, Word pattern) { Res res; /* This scans the root registers (.context.regroots). It also unnecessarily scans the rest of the context. The optimisation to scan only relevant parts would be machine dependent. */ - res = TraceScanAreaTagged( + res = TraceScanAreaMasked( ss, - (Addr *)mfc->ucontext, - (Addr *)((char *)mfc->ucontext + sizeof(*(mfc->ucontext))) + (Word *)mfc->ucontext, + (Word *)((char *)mfc->ucontext + sizeof(*(mfc->ucontext))), + mask, pattern ); return res; diff --git a/mps/code/prmci6li.c b/mps/code/prmci6li.c index 38b11c3a627..6a9379209f9 100644 --- a/mps/code/prmci6li.c +++ b/mps/code/prmci6li.c @@ -105,7 +105,8 @@ Addr MutatorFaultContextSP(MutatorFaultContext mfc) } -Res MutatorFaultContextScan(ScanState ss, MutatorFaultContext mfc) +Res MutatorFaultContextScan(ScanState ss, MutatorFaultContext mfc, + Word mask, Word pattern) { mcontext_t *mc; Res res; @@ -115,8 +116,9 @@ Res MutatorFaultContextScan(ScanState ss, MutatorFaultContext mfc) to scan only relevant parts would be machine dependent. */ mc = &mfc->ucontext->uc_mcontext; res = TraceScanAreaTagged(ss, - (Addr *)mc, - (Addr *)((char *)mc + sizeof(*mc))); + (Word *)mc, + (Word *)((char *)mc + sizeof(*mc)), + mask, pattern); return res; } diff --git a/mps/code/prmci6xc.c b/mps/code/prmci6xc.c index 4d3a5afe156..61e57728473 100644 --- a/mps/code/prmci6xc.c +++ b/mps/code/prmci6xc.c @@ -99,7 +99,8 @@ Addr MutatorFaultContextSP(MutatorFaultContext mfc) } -Res MutatorFaultContextScan(ScanState ss, MutatorFaultContext mfc) +Res MutatorFaultContextScan(ScanState ss, MutatorFaultContext mfc, + Word mask, Word pattern) { x86_thread_state64_t *mc; Res res; @@ -108,9 +109,10 @@ Res MutatorFaultContextScan(ScanState ss, MutatorFaultContext mfc) unnecessarily scans the rest of the context. The optimisation to scan only relevant parts would be machine dependent. */ mc = mfc->threadState; - res = TraceScanAreaTagged(ss, - (Addr *)mc, - (Addr *)((char *)mc + sizeof(*mc))); + res = TraceScanAreaMasked(ss, + (Word *)mc, + (Word *)((char *)mc + sizeof(*mc)), + mask, pattern); return res; } diff --git a/mps/code/prot.h b/mps/code/prot.h index 7191cd01e86..733c26c707b 100644 --- a/mps/code/prot.h +++ b/mps/code/prot.h @@ -30,7 +30,8 @@ extern void ProtSync(Arena arena); extern Bool ProtCanStepInstruction(MutatorFaultContext context); extern Res ProtStepInstruction(MutatorFaultContext context); extern Addr MutatorFaultContextSP(MutatorFaultContext mfc); -extern Res MutatorFaultContextScan(ScanState ss, MutatorFaultContext mfc); +extern Res MutatorFaultContextScan(ScanState ss, MutatorFaultContext mfc, + Word mask, Word pattern); #endif /* prot_h */ diff --git a/mps/code/root.c b/mps/code/root.c index 02f48b38e44..0815543c5be 100644 --- a/mps/code/root.c +++ b/mps/code/root.c @@ -38,13 +38,14 @@ typedef struct RootStruct { size_t s; /* environment for scan */ } fun; struct { - Addr *base; /* beginning of table */ - Addr *limit; /* one off end of table */ + Word *base; /* beginning of table */ + Word *limit; /* one off end of table */ } table; struct { - Addr *base; /* beginning of table */ - Addr *limit; /* one off end of table */ + Word *base; /* beginning of table */ + Word *limit; /* one off end of table */ Word mask; /* tag mask for scanning */ + Word pattern; /* tag pattern for scanning */ } tableMasked; struct { mps_reg_scan_t scan; /* function for scanning registers */ @@ -52,6 +53,12 @@ typedef struct RootStruct { void *p; /* passed to scan */ size_t s; /* passed to scan */ } reg; + struct { + Thread thread; /* passed to scan */ + Word mask; /* tag mask for scanning */ + Word pattern; /* tag pattern for scanning */ + Word *stackBot; /* bottom of stack */ + } regMasked; struct { mps_fmt_scan_t scan; /* format-like scanner */ Addr base, limit; /* passed to scan */ @@ -67,7 +74,8 @@ typedef struct RootStruct { Bool RootVarCheck(RootVar rootVar) { CHECKL(rootVar == RootTABLE || rootVar == RootTABLE_MASKED - || rootVar == RootFUN || rootVar == RootFMT || rootVar == RootREG); + || rootVar == RootFUN || rootVar == RootFMT || rootVar == RootREG + || rootVar == RootREG_MASKED); UNUSED(rootVar); return TRUE; } @@ -112,7 +120,7 @@ Bool RootCheck(Root root) case RootTABLE_MASKED: CHECKL(root->the.tableMasked.base != 0); CHECKL(root->the.tableMasked.base < root->the.tableMasked.limit); - /* Can't check anything about the mask. */ + CHECKL((~root->the.tableMasked.mask & root->the.tableMasked.pattern) == 0); break; case RootFUN: @@ -122,6 +130,13 @@ Bool RootCheck(Root root) case RootREG: CHECKL(root->the.reg.scan != NULL); CHECKD_NOSIG(Thread, root->the.reg.thread); /* */ + /* Can't check anything about p or s. */ + break; + + case RootREG_MASKED: + CHECKD_NOSIG(Thread, root->the.regMasked.thread); /* */ + CHECKL((~root->the.regMasked.mask & root->the.regMasked.pattern) == 0); + /* Can't check anything about stackBot. */ break; case RootFMT: @@ -254,7 +269,7 @@ static Res rootCreateProtectable(Root *rootReturn, Arena arena, } Res RootCreateTable(Root *rootReturn, Arena arena, - Rank rank, RootMode mode, Addr *base, Addr *limit) + Rank rank, RootMode mode, Word *base, Word *limit) { Res res; union RootUnion theUnion; @@ -276,7 +291,7 @@ Res RootCreateTable(Root *rootReturn, Arena arena, } Res RootCreateTableMasked(Root *rootReturn, Arena arena, - Rank rank, RootMode mode, Addr *base, Addr *limit, + Rank rank, RootMode mode, Word *base, Word *limit, Word mask) { union RootUnion theUnion; @@ -291,6 +306,7 @@ Res RootCreateTableMasked(Root *rootReturn, Arena arena, theUnion.tableMasked.base = base; theUnion.tableMasked.limit = limit; theUnion.tableMasked.mask = mask; + theUnion.tableMasked.pattern = 0; return rootCreateProtectable(rootReturn, arena, rank, mode, RootTABLE_MASKED, (Addr)base, (Addr)limit, &theUnion); @@ -317,6 +333,28 @@ Res RootCreateReg(Root *rootReturn, Arena arena, return rootCreate(rootReturn, arena, rank, (RootMode)0, RootREG, &theUnion); } +Res RootCreateRegMasked(Root *rootReturn, Arena arena, + Rank rank, Thread thread, + Word mask, Word pattern, Addr stackBot) +{ + union RootUnion theUnion; + + AVER(rootReturn != NULL); + AVERT(Arena, arena); + AVERT(Rank, rank); + AVERT(Thread, thread); + AVER(ThreadArena(thread) == arena); + AVER((~mask & pattern) == 0); + + theUnion.regMasked.thread = thread; + theUnion.regMasked.mask = mask; + theUnion.regMasked.pattern = pattern; + theUnion.regMasked.stackBot = stackBot; + + return rootCreate(rootReturn, arena, rank, (RootMode)0, RootREG_MASKED, + &theUnion); +} + /* RootCreateFmt -- create root from block of formatted objects * * .fmt.no-align-check: Note that we don't check the alignment of base @@ -481,7 +519,8 @@ Res RootScan(ScanState ss, Root root) res = TraceScanAreaMasked(ss, root->the.tableMasked.base, root->the.tableMasked.limit, - root->the.tableMasked.mask); + root->the.tableMasked.mask, + root->the.tableMasked.pattern); ss->scannedSize += AddrOffset(root->the.table.base, root->the.table.limit); if (res != ResOK) goto failScan; @@ -500,6 +539,15 @@ Res RootScan(ScanState ss, Root root) goto failScan; break; + case RootREG_MASKED: + res = ThreadScan(ss, root->the.regMasked.thread, + root->the.regMasked.stackBot, + root->the.regMasked.mask, + root->the.regMasked.pattern); + if (res != ResOK) + goto failScan; + break; + case RootFMT: res = (*root->the.fmt.scan)(&ss->ss_s, root->the.fmt.base, root->the.fmt.limit); ss->scannedSize += AddrOffset(root->the.fmt.base, root->the.fmt.limit); diff --git a/mps/code/ss.c b/mps/code/ss.c index 8c5bc44b022..7078c59d204 100644 --- a/mps/code/ss.c +++ b/mps/code/ss.c @@ -24,10 +24,8 @@ SRCID(ss, "$Id$"); * scanning. */ -Res StackScanInner(ScanState ss, - Addr *stackBot, - Addr *stackTop, - Count nSavedRegs) +Res StackScanInner(ScanState ss, Word *stackBot, Word *stackTop, + Count nSavedRegs, Word mask, Word pattern) { Arena arena; Res res; @@ -49,14 +47,16 @@ Res StackScanInner(ScanState ss, if (arena->stackAtArenaEnter != NULL) { AVER(stackTop < arena->stackAtArenaEnter); AVER(arena->stackAtArenaEnter < stackBot); - res = TraceScanAreaTagged(ss, stackTop, stackTop + nSavedRegs); + res = TraceScanAreaMasked(ss, stackTop, stackTop + nSavedRegs, + mask, pattern); if (res != ResOK) return res; - res = TraceScanAreaTagged(ss, arena->stackAtArenaEnter, stackBot); + res = TraceScanAreaMasked(ss, arena->stackAtArenaEnter, stackBot, + mask, pattern); if (res != ResOK) return res; } else { - res = TraceScanAreaTagged(ss, stackTop, stackBot); + res = TraceScanAreaMasked(ss, stackTop, stackBot, mask, pattern); if (res != ResOK) return res; } diff --git a/mps/code/ss.h b/mps/code/ss.h index 831ad98aea8..c4045bbfe47 100644 --- a/mps/code/ss.h +++ b/mps/code/ss.h @@ -32,13 +32,9 @@ * stack itself. */ -extern Res StackScan(ScanState ss, Addr *stackBot); - - -extern Res StackScanInner(ScanState ss, - Addr *stackBot, - Addr *stackTop, - Count nSavedRegs); +extern Res StackScan(ScanState ss, Word *stackBot, Word mask, Word pattern); +extern Res StackScanInner(ScanState ss, Word *stackBot, Word *stackTop, + Count nSavedRegs, Word mask, Word pattern); #endif /* ss_h */ diff --git a/mps/code/ssan.c b/mps/code/ssan.c index 27233e7b9f6..b746b28bb1d 100644 --- a/mps/code/ssan.c +++ b/mps/code/ssan.c @@ -21,21 +21,22 @@ SRCID(ssan, "$Id$"); -Res StackScan(ScanState ss, Addr *stackBot) +Res StackScan(ScanState ss, Word *stackBot, Word mask, Word pattern) { jmp_buf jb; - void *stackTop = &jb; + Word *stackTop = (Word *)&jb; /* .assume.stack: This implementation assumes that the stack grows * downwards, so that the address of the jmp_buf is the limit of the * part of the stack that needs to be scanned. (StackScanInner makes * the same assumption.) */ - AVER(stackTop < (void *)stackBot); + AVER(stackTop < stackBot); (void)setjmp(jb); - return StackScanInner(ss, stackBot, stackTop, sizeof jb / sizeof(Addr*)); + return StackScanInner(ss, stackBot, stackTop, sizeof jb / sizeof(Word*), + mask, pattern); } diff --git a/mps/code/ssixi3.c b/mps/code/ssixi3.c index 8cc1f8cbd45..db67d0e61e4 100644 --- a/mps/code/ssixi3.c +++ b/mps/code/ssixi3.c @@ -49,9 +49,9 @@ SRCID(ssixi3, "$Id$"); #define ASMV(x) __asm__ volatile (x) -Res StackScan(ScanState ss, Addr *stackBot) +Res StackScan(ScanState ss, Word *stackBot, Word mask, Word pattern) { - Addr calleeSaveRegs[4]; + Word calleeSaveRegs[4]; /* .assume.asm.stack */ /* Store the callee save registers on the stack so they get scanned @@ -62,7 +62,8 @@ Res StackScan(ScanState ss, Addr *stackBot) ASMV("mov %%edi, %0" : "=m" (calleeSaveRegs[2])); ASMV("mov %%ebp, %0" : "=m" (calleeSaveRegs[3])); - return StackScanInner(ss, stackBot, calleeSaveRegs, NELEMS(calleeSaveRegs)); + return StackScanInner(ss, stackBot, calleeSaveRegs, NELEMS(calleeSaveRegs), + mask, pattern); } diff --git a/mps/code/ssixi6.c b/mps/code/ssixi6.c index e61af2ee961..9fd9e8a1811 100644 --- a/mps/code/ssixi6.c +++ b/mps/code/ssixi6.c @@ -47,9 +47,9 @@ SRCID(ssixi6, "$Id$"); #define ASMV(x) __asm__ volatile (x) -Res StackScan(ScanState ss, Addr *stackBot) +Res StackScan(ScanState ss, Word *stackBot, Word mask, Word pattern) { - Addr calleeSaveRegs[6]; + Word calleeSaveRegs[6]; /* .assume.asm.stack */ /* Store the callee save registers on the stack so they get scanned @@ -62,7 +62,8 @@ Res StackScan(ScanState ss, Addr *stackBot) ASMV("mov %%r14, %0" : "=m" (calleeSaveRegs[4])); ASMV("mov %%r15, %0" : "=m" (calleeSaveRegs[5])); - return StackScanInner(ss, stackBot, calleeSaveRegs, NELEMS(calleeSaveRegs)); + return StackScanInner(ss, stackBot, calleeSaveRegs, NELEMS(calleeSaveRegs), + mask, pattern); } diff --git a/mps/code/ssw3i3mv.c b/mps/code/ssw3i3mv.c index d879780734a..feffdf3e7a3 100644 --- a/mps/code/ssw3i3mv.c +++ b/mps/code/ssw3i3mv.c @@ -22,7 +22,7 @@ SRCID(ssw3i3mv, "$Id$"); -Res StackScan(ScanState ss, Addr *stackBot) +Res StackScan(ScanState ss, Word *stackBot, Word mask, Word pattern) { jmp_buf jb; @@ -33,16 +33,17 @@ Res StackScan(ScanState ss, Addr *stackBot) /* These checks will just serve to warn us at compile-time if the setjmp.h header changes to indicate that the registers we want aren't saved any more. */ - AVER(sizeof(((_JUMP_BUFFER *)jb)->Edi) == sizeof(Addr)); - AVER(sizeof(((_JUMP_BUFFER *)jb)->Esi) == sizeof(Addr)); - AVER(sizeof(((_JUMP_BUFFER *)jb)->Ebx) == sizeof(Addr)); + AVER(sizeof(((_JUMP_BUFFER *)jb)->Edi) == sizeof(Word)); + AVER(sizeof(((_JUMP_BUFFER *)jb)->Esi) == sizeof(Word)); + AVER(sizeof(((_JUMP_BUFFER *)jb)->Ebx) == sizeof(Word)); /* Ensure that the callee-save registers will be found by StackScanInner when it's passed the address of the Ebx field. */ AVER(offsetof(_JUMP_BUFFER, Edi) == offsetof(_JUMP_BUFFER, Ebx) + 4); AVER(offsetof(_JUMP_BUFFER, Esi) == offsetof(_JUMP_BUFFER, Ebx) + 8); - return StackScanInner(ss, stackBot, (Addr *)&((_JUMP_BUFFER *)jb)->Ebx, 3); + return StackScanInner(ss, stackBot, (Word *)&((_JUMP_BUFFER *)jb)->Ebx, 3, + mask, pattern); } /* C. COPYRIGHT AND LICENSE diff --git a/mps/code/ssw3i3pc.c b/mps/code/ssw3i3pc.c index e03754c7eba..9c42fa6207f 100644 --- a/mps/code/ssw3i3pc.c +++ b/mps/code/ssw3i3pc.c @@ -46,7 +46,7 @@ typedef struct __JUMP_BUFFER { } _JUMP_BUFFER; -Res StackScan(ScanState ss, Addr *stackBot) +Res StackScan(ScanState ss, Word *stackBot, Word mask, Word pattern) { jmp_buf jb; @@ -58,7 +58,8 @@ Res StackScan(ScanState ss, Addr *stackBot) AVER(offsetof(_JUMP_BUFFER, Edi) == offsetof(_JUMP_BUFFER, Ebx) + 4); AVER(offsetof(_JUMP_BUFFER, Esi) == offsetof(_JUMP_BUFFER, Ebx) + 8); - return StackScanInner(ss, stackBot, (Addr *)&((_JUMP_BUFFER *)jb)->Ebx, 3); + return StackScanInner(ss, stackBot, (Word *)&((_JUMP_BUFFER *)jb)->Ebx, 3, + mask, pattern); } diff --git a/mps/code/ssw3i6mv.c b/mps/code/ssw3i6mv.c index 16c33e3fb74..7af35054d30 100644 --- a/mps/code/ssw3i6mv.c +++ b/mps/code/ssw3i6mv.c @@ -30,7 +30,7 @@ SRCID(ssw3i6mv, "$Id$"); -Res StackScan(ScanState ss, Addr *stackBot) +Res StackScan(ScanState ss, Word *stackBot, Word mask, Word pattern) { jmp_buf jb; @@ -41,13 +41,13 @@ Res StackScan(ScanState ss, Addr *stackBot) /* These checks will just serve to warn us at compile-time if the setjmp.h header changes to indicate that the registers we want aren't saved any more. */ - AVER(sizeof(((_JUMP_BUFFER *)jb)->Rdi) == sizeof(Addr)); - AVER(sizeof(((_JUMP_BUFFER *)jb)->Rsi) == sizeof(Addr)); - AVER(sizeof(((_JUMP_BUFFER *)jb)->Rbp) == sizeof(Addr)); - AVER(sizeof(((_JUMP_BUFFER *)jb)->R12) == sizeof(Addr)); - AVER(sizeof(((_JUMP_BUFFER *)jb)->R13) == sizeof(Addr)); - AVER(sizeof(((_JUMP_BUFFER *)jb)->R14) == sizeof(Addr)); - AVER(sizeof(((_JUMP_BUFFER *)jb)->R15) == sizeof(Addr)); + AVER(sizeof(((_JUMP_BUFFER *)jb)->Rdi) == sizeof(Word)); + AVER(sizeof(((_JUMP_BUFFER *)jb)->Rsi) == sizeof(Word)); + AVER(sizeof(((_JUMP_BUFFER *)jb)->Rbp) == sizeof(Word)); + AVER(sizeof(((_JUMP_BUFFER *)jb)->R12) == sizeof(Word)); + AVER(sizeof(((_JUMP_BUFFER *)jb)->R13) == sizeof(Word)); + AVER(sizeof(((_JUMP_BUFFER *)jb)->R14) == sizeof(Word)); + AVER(sizeof(((_JUMP_BUFFER *)jb)->R15) == sizeof(Word)); /* The layout of the jmp_buf forces us to harmlessly scan Rsp as well. */ AVER(offsetof(_JUMP_BUFFER, Rsp) == offsetof(_JUMP_BUFFER, Rbx) + 8); @@ -59,7 +59,8 @@ Res StackScan(ScanState ss, Addr *stackBot) AVER(offsetof(_JUMP_BUFFER, R14) == offsetof(_JUMP_BUFFER, Rbx) + 56); AVER(offsetof(_JUMP_BUFFER, R15) == offsetof(_JUMP_BUFFER, Rbx) + 64); - return StackScanInner(ss, stackBot, (Addr *)&((_JUMP_BUFFER *)jb)->Rbx, 9); + return StackScanInner(ss, stackBot, (Word *)&((_JUMP_BUFFER *)jb)->Rbx, 9, + mask, pattern); } /* C. COPYRIGHT AND LICENSE diff --git a/mps/code/ssw3i6pc.c b/mps/code/ssw3i6pc.c index 89fbbeac420..4b22d72b0d9 100644 --- a/mps/code/ssw3i6pc.c +++ b/mps/code/ssw3i6pc.c @@ -68,7 +68,7 @@ typedef struct _JUMP_BUFFER { } _JUMP_BUFFER; -Res StackScan(ScanState ss, Addr *stackBot) +Res StackScan(ScanState ss, Word *stackBot, Word mask, Word pattern) { jmp_buf jb; @@ -79,13 +79,13 @@ Res StackScan(ScanState ss, Addr *stackBot) /* These checks will just serve to warn us at compile-time if the setjmp.h header changes to indicate that the registers we want aren't saved any more. */ - AVER(sizeof(((_JUMP_BUFFER *)jb)->Rdi) == sizeof(Addr)); - AVER(sizeof(((_JUMP_BUFFER *)jb)->Rsi) == sizeof(Addr)); - AVER(sizeof(((_JUMP_BUFFER *)jb)->Rbp) == sizeof(Addr)); - AVER(sizeof(((_JUMP_BUFFER *)jb)->R12) == sizeof(Addr)); - AVER(sizeof(((_JUMP_BUFFER *)jb)->R13) == sizeof(Addr)); - AVER(sizeof(((_JUMP_BUFFER *)jb)->R14) == sizeof(Addr)); - AVER(sizeof(((_JUMP_BUFFER *)jb)->R15) == sizeof(Addr)); + AVER(sizeof(((_JUMP_BUFFER *)jb)->Rdi) == sizeof(Word)); + AVER(sizeof(((_JUMP_BUFFER *)jb)->Rsi) == sizeof(Word)); + AVER(sizeof(((_JUMP_BUFFER *)jb)->Rbp) == sizeof(Word)); + AVER(sizeof(((_JUMP_BUFFER *)jb)->R12) == sizeof(Word)); + AVER(sizeof(((_JUMP_BUFFER *)jb)->R13) == sizeof(Word)); + AVER(sizeof(((_JUMP_BUFFER *)jb)->R14) == sizeof(Word)); + AVER(sizeof(((_JUMP_BUFFER *)jb)->R15) == sizeof(Word)); /* The layout of the jmp_buf forces us to harmlessly scan Rsp as well. */ AVER(offsetof(_JUMP_BUFFER, Rsp) == offsetof(_JUMP_BUFFER, Rbx) + 8); @@ -97,7 +97,8 @@ Res StackScan(ScanState ss, Addr *stackBot) AVER(offsetof(_JUMP_BUFFER, R14) == offsetof(_JUMP_BUFFER, Rbx) + 56); AVER(offsetof(_JUMP_BUFFER, R15) == offsetof(_JUMP_BUFFER, Rbx) + 64); - return StackScanInner(ss, stackBot, (Addr *)&((_JUMP_BUFFER *)jb)->Rbx, 9); + return StackScanInner(ss, stackBot, (Word *)&((_JUMP_BUFFER *)jb)->Rbx, 9, + mask, pattern); } diff --git a/mps/code/th.h b/mps/code/th.h index 8c7da150fd0..ea56be2dd42 100644 --- a/mps/code/th.h +++ b/mps/code/th.h @@ -67,7 +67,8 @@ extern Thread ThreadRingThread(Ring threadRing); extern Arena ThreadArena(Thread thread); -extern Res ThreadScan(ScanState ss, Thread thread, void *stackBot); +extern Res ThreadScan(ScanState ss, Thread thread, Word *stackBot, + Word mask, Word pattern); #endif /* th_h */ diff --git a/mps/code/than.c b/mps/code/than.c index 8c5af222898..f9b0e51e148 100644 --- a/mps/code/than.c +++ b/mps/code/than.c @@ -115,10 +115,11 @@ Arena ThreadArena(Thread thread) } -Res ThreadScan(ScanState ss, Thread thread, void *stackBot) +Res ThreadScan(ScanState ss, Thread thread, Word *stackBot, + Word mask, Word pattern) { UNUSED(thread); - return StackScan(ss, stackBot); + return StackScan(ss, stackBot, mask, pattern); } diff --git a/mps/code/thix.c b/mps/code/thix.c index d32a7dd6601..6da41e572d6 100644 --- a/mps/code/thix.c +++ b/mps/code/thix.c @@ -222,7 +222,8 @@ Arena ThreadArena(Thread thread) /* ThreadScan -- scan the state of a thread (stack and regs) */ -Res ThreadScan(ScanState ss, Thread thread, void *stackBot) +Res ThreadScan(ScanState ss, Thread thread, Word *stackBot, + Word mask, Word pattern) { pthread_t self; Res res; @@ -231,12 +232,13 @@ Res ThreadScan(ScanState ss, Thread thread, void *stackBot) self = pthread_self(); if(pthread_equal(self, thread->id)) { /* scan this thread's stack */ - res = StackScan(ss, stackBot); + res = StackScan(ss, stackBot, mask, pattern); if(res != ResOK) return res; } else { MutatorFaultContext mfc; - Addr *stackBase, *stackLimit, stackPtr; + Word *stackBase, *stackLimit; + Addr stackPtr; mfc = thread->mfc; if(mfc == NULL) { @@ -248,20 +250,20 @@ Res ThreadScan(ScanState ss, Thread thread, void *stackBot) stackPtr = MutatorFaultContextSP(mfc); /* .stack.align */ - stackBase = (Addr *)AddrAlignUp(stackPtr, sizeof(Addr)); - stackLimit = (Addr *)stackBot; + stackBase = (Word *)AddrAlignUp(stackPtr, sizeof(Addr)); + stackLimit = stackBot; if (stackBase >= stackLimit) return ResOK; /* .stack.below-bottom */ /* scan stack inclusive of current sp and exclusive of * stackBot (.stack.full-descend) */ - res = TraceScanAreaTagged(ss, stackBase, stackLimit); + res = TraceScanAreaMasked(ss, stackBase, stackLimit, mask, pattern); if(res != ResOK) return res; /* scan the registers in the mutator fault context */ - res = MutatorFaultContextScan(ss, mfc); + res = MutatorFaultContextScan(ss, mfc, mask, pattern); if(res != ResOK) return res; } diff --git a/mps/code/thw3i3.c b/mps/code/thw3i3.c index 33424b6cf54..030319c3350 100644 --- a/mps/code/thw3i3.c +++ b/mps/code/thw3i3.c @@ -67,7 +67,8 @@ SRCID(thw3i3, "$Id$"); -Res ThreadScan(ScanState ss, Thread thread, void *stackBot) +Res ThreadScan(ScanState ss, Thread thread, Word *stackBot, + Word mask, Word pattern) { DWORD id; Res res; @@ -77,7 +78,8 @@ Res ThreadScan(ScanState ss, Thread thread, void *stackBot) if(id != thread->id) { /* .thread.id */ CONTEXT context; BOOL success; - Addr *stackBase, *stackLimit, stackPtr; + Word *stackBase, *stackLimit; + Addr stackPtr; /* scan stack and register roots in other threads */ @@ -95,15 +97,15 @@ Res ThreadScan(ScanState ss, Thread thread, void *stackBot) stackPtr = (Addr)context.Esp; /* .i3.sp */ /* .stack.align */ - stackBase = (Addr *)AddrAlignUp(stackPtr, sizeof(Addr)); - stackLimit = (Addr *)stackBot; + stackBase = (Word *)AddrAlignUp(stackPtr, sizeof(Addr)); + stackLimit = stackBot; if (stackBase >= stackLimit) return ResOK; /* .stack.below-bottom */ /* scan stack inclusive of current sp and exclusive of * stackBot (.stack.full-descend) */ - res = TraceScanAreaTagged(ss, stackBase, stackLimit); + res = TraceScanAreaMasked(ss, stackBase, stackLimit, mask, pattern); if(res != ResOK) return res; @@ -112,13 +114,14 @@ Res ThreadScan(ScanState ss, Thread thread, void *stackBot) * unnecessarily scans the rest of the context. The optimisation * to scan only relevant parts would be machine dependent. */ - res = TraceScanAreaTagged(ss, (Addr *)&context, - (Addr *)((char *)&context + sizeof(CONTEXT))); + res = TraceScanAreaMasked(ss, (Word *)&context, + (Word *)((char *)&context + sizeof(CONTEXT)), + mask, pattern); if(res != ResOK) return res; } else { /* scan this thread's stack */ - res = StackScan(ss, stackBot); + res = StackScan(ss, stackBot, mask, pattern); if(res != ResOK) return res; } diff --git a/mps/code/thw3i6.c b/mps/code/thw3i6.c index 9b0eae55db6..77a7aefe425 100644 --- a/mps/code/thw3i6.c +++ b/mps/code/thw3i6.c @@ -67,7 +67,8 @@ SRCID(thw3i6, "$Id$"); -Res ThreadScan(ScanState ss, Thread thread, void *stackBot) +Res ThreadScan(ScanState ss, Thread thread, Word *stackBot, + Word mask, Word pattern) { DWORD id; Res res; @@ -77,7 +78,8 @@ Res ThreadScan(ScanState ss, Thread thread, void *stackBot) if(id != thread->id) { /* .thread.id */ CONTEXT context; BOOL success; - Addr *stackBase, *stackLimit, stackPtr; + Word *stackBase, *stackLimit; + Addr stackPtr; /* scan stack and register roots in other threads */ @@ -95,15 +97,15 @@ Res ThreadScan(ScanState ss, Thread thread, void *stackBot) stackPtr = (Addr)context.Rsp; /* .i6.sp */ /* .stack.align */ - stackBase = (Addr *)AddrAlignUp(stackPtr, sizeof(Addr)); - stackLimit = (Addr *)stackBot; + stackBase = (Word *)AddrAlignUp(stackPtr, sizeof(Addr)); + stackLimit = stackBot; if (stackBase >= stackLimit) return ResOK; /* .stack.below-bottom */ /* scan stack inclusive of current sp and exclusive of * stackBot (.stack.full-descend) */ - res = TraceScanAreaTagged(ss, stackBase, stackLimit); + res = TraceScanAreaMasked(ss, stackBase, stackLimit, mask, pattern); if(res != ResOK) return res; @@ -112,13 +114,14 @@ Res ThreadScan(ScanState ss, Thread thread, void *stackBot) * unnecessarily scans the rest of the context. The optimisation * to scan only relevant parts would be machine dependent. */ - res = TraceScanAreaTagged(ss, (Addr *)&context, - (Addr *)((char *)&context + sizeof(CONTEXT))); + res = TraceScanAreaMasked(ss, (Word *)&context, + (Word *)((char *)&context + sizeof(CONTEXT)), + mask, pattern); if(res != ResOK) return res; } else { /* scan this thread's stack */ - res = StackScan(ss, stackBot); + res = StackScan(ss, stackBot, mask, pattern); if(res != ResOK) return res; } diff --git a/mps/code/thxc.c b/mps/code/thxc.c index 6aeffef6568..8beaea1930e 100644 --- a/mps/code/thxc.c +++ b/mps/code/thxc.c @@ -189,7 +189,8 @@ Arena ThreadArena(Thread thread) #include "prmcxc.h" -Res ThreadScan(ScanState ss, Thread thread, void *stackBot) +Res ThreadScan(ScanState ss, Thread thread, Word *stackBot, + Word mask, Word pattern) { mach_port_t self; Res res; @@ -199,13 +200,14 @@ Res ThreadScan(ScanState ss, Thread thread, void *stackBot) AVER(MACH_PORT_VALID(self)); if (thread->port == self) { /* scan this thread's stack */ - res = StackScan(ss, stackBot); + res = StackScan(ss, stackBot, mask, pattern); if(res != ResOK) return res; } else { MutatorFaultContextStruct mfcStruct; THREAD_STATE_S threadState; - Addr *stackBase, *stackLimit, stackPtr; + Word *stackBase, *stackLimit; + Addr stackPtr; mach_msg_type_number_t count; kern_return_t kern_return; @@ -227,20 +229,20 @@ Res ThreadScan(ScanState ss, Thread thread, void *stackBot) stackPtr = MutatorFaultContextSP(&mfcStruct); /* .stack.align */ - stackBase = (Addr *)AddrAlignUp(stackPtr, sizeof(Addr)); - stackLimit = (Addr *)stackBot; + stackBase = (Word *)AddrAlignUp(stackPtr, sizeof(Addr)); + stackLimit = stackBot; if (stackBase >= stackLimit) return ResOK; /* .stack.below-bottom */ /* scan stack inclusive of current sp and exclusive of * stackBot (.stack.full-descend) */ - res = TraceScanAreaTagged(ss, stackBase, stackLimit); + res = TraceScanAreaMasked(ss, stackBase, stackLimit, mask, pattern); if(res != ResOK) return res; /* scan the registers in the mutator fault context */ - res = MutatorFaultContextScan(ss, &mfcStruct); + res = MutatorFaultContextScan(ss, &mfcStruct, mask, pattern); if(res != ResOK) return res; } diff --git a/mps/code/trace.c b/mps/code/trace.c index 7aeb70305c7..6f44f724846 100644 --- a/mps/code/trace.c +++ b/mps/code/trace.c @@ -1425,11 +1425,10 @@ void TraceScanSingleRef(TraceSet ts, Rank rank, Arena arena, * [base, limit). I.e., it calls Fix on all words from base up to * limit, inclusive of base and exclusive of limit. */ -Res TraceScanArea(ScanState ss, Addr *base, Addr *limit) +Res TraceScanArea(ScanState ss, Word *base, Word *limit) { Res res; - Addr *p; - Ref ref; + Word word, *p; AVER(base != NULL); AVER(limit != NULL); @@ -1442,10 +1441,10 @@ Res TraceScanArea(ScanState ss, Addr *base, Addr *limit) loop: if (p >= limit) goto out; - ref = *p++; - if(!TRACE_FIX1(ss, ref)) + word = *p++; + if(!TRACE_FIX1(ss, (Ref)word)) goto loop; - res = TRACE_FIX2(ss, p-1); + res = TRACE_FIX2(ss, (Ref *)(p-1)); if(res == ResOK) goto loop; return res; @@ -1457,43 +1456,21 @@ Res TraceScanArea(ScanState ss, Addr *base, Addr *limit) } -/* TraceScanAreaTagged -- scan contiguous area of tagged references - * - * .tagging: This is as TraceScanArea except words are only fixed they are - * tagged as zero according to the alignment of a Word. - * - * See also PoolSingleAccess . - * - * TODO: Generalise the handling of tags so that pools can decide how - * their objects are tagged. This may use the user defined format - * to describe how tags are done */ -Res TraceScanAreaTagged(ScanState ss, Addr *base, Addr *limit) -{ - Word mask; - - /* NOTE: An optimisation that maybe worth considering is setting some of the - * top bits in the mask as an early catch of addresses outside the arena. - * This might help slightly on 64-bit windows. However these are picked up - * soon afterwards by later checks. The bottom bits are more important - * to check as we ignore them in AMCFix, so the non-reference could - * otherwise end up pinning an object. */ - mask = sizeof(Word) - 1; - AVER(WordIsP2(mask + 1)); - return TraceScanAreaMasked(ss, base, limit, mask); -} - - /* TraceScanAreaMasked -- scan contiguous area of filtered references * - * This is as TraceScanArea except words are only fixed if they are zero - * when masked with a mask. */ + * This is as TraceScanArea except words are only fixed if they have + * the given value when masked with a mask. + * + * This has ATTRIBUTE_NO_SANITIZE_ADDRESS otherwise Clang's address + * sanitizer will think we have run off the end of an array. + */ ATTRIBUTE_NO_SANITIZE_ADDRESS -Res TraceScanAreaMasked(ScanState ss, Addr *base, Addr *limit, Word mask) +Res TraceScanAreaMasked(ScanState ss, Word *base, Word *limit, Word mask, + Word pattern) { Res res; - Addr *p; - Ref ref; + Word word, *p; AVERT(ScanState, ss); AVER(base != NULL); @@ -1507,12 +1484,12 @@ Res TraceScanAreaMasked(ScanState ss, Addr *base, Addr *limit, Word mask) loop: if (p >= limit) goto out; - ref = *p++; - if (((Word)ref & mask) - != 0) goto loop; - if (!TRACE_FIX1(ss, ref)) + word = *p++; + if ((word & mask) != pattern) goto loop; - res = TRACE_FIX2(ss, p-1); + if (!TRACE_FIX1(ss, (Ref)word)) + goto loop; + res = TRACE_FIX2(ss, (Ref *)(p-1)); if(res == ResOK) goto loop; return res; diff --git a/mps/manual/source/release.rst b/mps/manual/source/release.rst index cb820397281..a9881375159 100644 --- a/mps/manual/source/release.rst +++ b/mps/manual/source/release.rst @@ -17,6 +17,10 @@ New features specifying the minimum size of the memory segments that the pool requests from the :term:`arena`. +#. New function :c:func:`mps_root_create_reg_masked` applies a mask + and pattern test to all words in registers and on the stack when + scanning them. This supports tagged references in these locations. + Interface changes ................. diff --git a/mps/manual/source/topic/root.rst b/mps/manual/source/topic/root.rst index 4a1e06686ae..0089b26e728 100644 --- a/mps/manual/source/topic/root.rst +++ b/mps/manual/source/topic/root.rst @@ -393,10 +393,12 @@ Root interface The registered root description persists until it is destroyed by calling :c:func:`mps_root_destroy`. + .. c:function:: mps_res_t mps_root_create_reg(mps_root_t *root_o, mps_arena_t arena, mps_rank_t rank, mps_rm_t rm, mps_thr_t thr, mps_reg_scan_t reg_scan, void *p, size_t s) Register a :term:`root` that consists of the :term:`references` - fixed in a :term:`thread's ` stack by a scanning function. + fixed in a :term:`thread's ` registers and stack by a + scanning function. ``root_o`` points to a location that will hold the address of the new root description. @@ -427,7 +429,10 @@ Root interface It is not supported for :term:`client programs` to pass their own scanning functions to this function. The built-in MPS - function :c:func:`mps_stack_scan_ambig` must be used. + function :c:func:`mps_stack_scan_ambig` must be used. In this + case the ``p`` argument must be a pointer into the thread's + stack, as described for :c:func:`mps_root_create_reg_masked` + below, and the ``s`` argument is ignored. This function is intended as a hook should we ever need to allow client-specific extension or customization of stack and @@ -483,11 +488,14 @@ Root interface It scans all integer registers and everything on the stack of the thread given, and can therefore only be used with :term:`ambiguous - roots`. It only scans locations that are at, or higher on the - stack (that is, more recently added), the stack bottom that was - passed to :c:func:`mps_thread_reg`. References are assumed to be - represented as machine words, and are required to be - 4-byte-aligned; unaligned values are ignored. + roots`. It scans locations that are more recently added than the + stack bottom that was passed in the ``p`` argument to + :c:func:`mps_root_create_reg`. + + References are assumed to be represented as machine words, and are + required to be word-aligned; unaligned values are ignored. If + references are tagged, use :c:func:`mps_root_create_reg_masked` + instead. .. note:: @@ -496,6 +504,63 @@ Root interface and in some cases on the compiler. +.. c:function:: mps_res_t mps_root_create_reg_masked(mps_root_t *root_o, mps_arena_t arena, mps_rank_t rank, mps_rm_t rm, mps_thr_t thr, mps_word_t mask, mps_word_t pattern, void *stack) + + Register a :term:`root` that consists of the :term:`references` in + a :term:`thread's ` registers and stack that match a + binary pattern. + + ``root_o`` points to a location that will hold the address of the + new root description. + + ``arena`` is the arena. + + ``rank`` is the :term:`rank` of references in the root. + + ``rm`` is the :term:`root mode`. + + ``thr`` is the thread. + + ``mask`` is an arbitrary mask that is applied to each word in the + thread's registers and stack. + + ``pattern`` is an arbitrary pattern; any word that is unequal to + this (after masking with ``mask``) is not considered to be a + reference. + + ``stack`` is a pointer into the thread's stack. On platforms where + the stack grows downwards (currently, all supported platforms), + locations below this address will be scanned. + + Returns :c:macro:`MPS_RES_OK` if the root was registered + successfully, :c:macro:`MPS_RES_MEMORY` if the new root + description could not be allocated, or another :term:`result code` + if there was another error. + + The registered root description persists until it is destroyed by + calling :c:func:`mps_root_destroy`. + + .. warning:: + + A risk of using tagged pointers in registers and on the stack + is that in some circumstances, an optimizing compiler might + optimize away the tagged pointer, keeping only the untagged + version of the pointer. In this situation the pointer would be + ignored and if it was the last reference to the object the MPS + might incorrectly determine that it was dead. + + You can avoid this risk by setting ``mask`` and ``pattern`` to + zero: in this case all words in registers and on the stack are + scanned, leading to possible additional scanning and retention. + + .. note:: + + An optimization that may be worth considering is setting some + of the top bits in ``mask`` so that addresses that cannot be + allocated by the MPS are rejected quickly. This requires + expertise with the platform's virtual memory interface. + + .. c:function:: mps_res_t mps_root_create_table(mps_root_t *root_o, mps_arena_t arena, mps_rank_t rank, mps_rm_t rm, mps_addr_t *base, size_t count) Register a :term:`root` that consists of a vector of