summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--rts/sm/Compact.c162
-rw-r--r--testsuite/tests/rts/T17088.hs79
-rw-r--r--testsuite/tests/rts/T17088.stdout1
-rw-r--r--testsuite/tests/rts/all.T4
4 files changed, 179 insertions, 67 deletions
diff --git a/rts/sm/Compact.c b/rts/sm/Compact.c
index cd82944abd..1193fd765c 100644
--- a/rts/sm/Compact.c
+++ b/rts/sm/Compact.c
@@ -37,37 +37,35 @@
/* ----------------------------------------------------------------------------
Threading / unthreading pointers.
- The basic idea here is to chain together all the fields pointing at
- a particular object, with the root of the chain in the object's
- info table field. The original contents of the info pointer goes
- at the end of the chain.
-
- Adding a new field to the chain is a matter of swapping the
- contents of the field with the contents of the object's info table
- field.
-
- To unthread the chain, we walk down it updating all the fields on
- the chain with the new location of the object. We stop when we
- reach the info pointer at the end.
-
- The main difficulty here is that we need to be able to identify the
- info pointer at the end of the chain. We can't use the low bits of
- the pointer for this; they are already being used for
- pointer-tagging. What's more, we need to retain the
- pointer-tagging tag bits on each pointer during the
- threading/unthreading process.
-
- Our solution is as follows:
- - an info pointer (chain length zero) is identified by having tag 0
- - in a threaded chain of length > 0:
- - the pointer-tagging tag bits are attached to the info pointer
- - the first entry in the chain has tag 1
- - second and subsequent entries in the chain have tag 2
-
- This exploits the fact that the tag on each pointer to a given
- closure is normally the same (if they are not the same, then
- presumably the tag is not essential and it therefore doesn't matter
- if we throw away some of the tags).
+ The basic idea here is to chain together all the fields pointing at a
+ particular object, with the root of the chain in the object's info table
+ field. The original contents of the info pointer goes at the end of the
+ chain.
+
+ Adding a new field to the chain is a matter of swapping the contents of the
+ field with the contents of the object's info table field:
+
+ *field, **field = **field, field
+
+ To unthread the chain, we walk down it updating all the fields on the chain
+ with the new location of the object. We stop when we reach the info pointer
+ at the end.
+
+ The main difficulty here is that not all pointers to the same object are
+ tagged: pointers from roots (e.g. mut_lists) are not tagged, but pointers
+ from mutators are. So when unthreading a chain we need to distinguish a field
+ that had a tagged pointer from a field that had an untagged pointer.
+
+ Our solution is as follows: when chaining a field, if the field is NOT
+ tagged then we tag the pointer to the field with 1. I.e.
+
+ *field, **field = **field, field + 1
+
+ If the field is tagged then we tag to the pointer to it with 2.
+
+ When unchaining we look at the tag in the pointer to the field, if it's 1
+ then we write an untagged pointer to "free" to it, otherwise we tag the
+ pointer.
------------------------------------------------------------------------- */
STATIC_INLINE W_
@@ -82,10 +80,54 @@ GET_PTR_TAG(W_ p)
return p & TAG_MASK;
}
+static W_
+get_iptr_tag(StgInfoTable *iptr)
+{
+ const StgInfoTable *info = INFO_PTR_TO_STRUCT(iptr);
+ switch (info->type) {
+ case CONSTR:
+ case CONSTR_1_0:
+ case CONSTR_0_1:
+ case CONSTR_2_0:
+ case CONSTR_1_1:
+ case CONSTR_0_2:
+ case CONSTR_NOCAF:
+ {
+ W_ con_tag = info->srt + 1;
+ if (con_tag > TAG_MASK) {
+ return TAG_MASK;
+ } else {
+ return con_tag;
+ }
+ }
+
+ case FUN:
+ case FUN_1_0:
+ case FUN_0_1:
+ case FUN_2_0:
+ case FUN_1_1:
+ case FUN_0_2:
+ case FUN_STATIC:
+ {
+ const StgFunInfoTable *fun_itbl = FUN_INFO_PTR_TO_STRUCT(iptr);
+ W_ arity = fun_itbl->f.arity;
+ if (arity <= TAG_MASK) {
+ return arity;
+ } else {
+ return 0;
+ }
+ }
+
+ default:
+ return 0;
+ }
+}
+
STATIC_INLINE void
thread (StgClosure **p)
{
StgClosure *q0 = *p;
+ bool q0_tagged = GET_CLOSURE_TAG(q0) != 0;
P_ q = (P_)UNTAG_CLOSURE(q0);
// It doesn't look like a closure at the moment, because the info
@@ -98,21 +140,8 @@ thread (StgClosure **p)
if (bd->flags & BF_MARKED)
{
W_ iptr = *q;
- switch (GET_PTR_TAG(iptr))
- {
- case 0:
- // this is the info pointer; we are creating a new chain.
- // save the original tag at the end of the chain.
- *p = (StgClosure *)((W_)iptr + GET_CLOSURE_TAG(q0));
- *q = (W_)p + 1;
- break;
- case 1:
- case 2:
- // this is a chain of length 1 or more
- *p = (StgClosure *)iptr;
- *q = (W_)p + 2;
- break;
- }
+ *p = (StgClosure *)iptr;
+ *q = (W_)p + 1 + (q0_tagged ? 1 : 0);
}
}
}
@@ -128,7 +157,7 @@ thread_root (void *user STG_UNUSED, StgClosure **p)
STATIC_INLINE void thread_ (void *p) { thread((StgClosure **)p); }
STATIC_INLINE void
-unthread( P_ p, W_ free )
+unthread( const P_ p, W_ free, W_ tag )
{
W_ q = *p;
loop:
@@ -136,20 +165,21 @@ loop:
{
case 0:
// nothing to do; the chain is length zero
+ *p = q;
return;
case 1:
{
P_ q0 = (P_)(q-1);
- W_ r = *q0; // r is the info ptr, tagged with the pointer-tag
+ W_ r = *q0;
*q0 = free;
- *p = (W_)UNTAG_PTR(r);
- return;
+ q = r;
+ goto loop;
}
case 2:
{
P_ q0 = (P_)(q-2);
W_ r = *q0;
- *q0 = free;
+ *q0 = free + tag;
q = r;
goto loop;
}
@@ -162,7 +192,7 @@ loop:
// The info pointer is also tagged with the appropriate pointer tag
// for this closure, which should be attached to the pointer
// subsequently passed to unthread().
-STATIC_INLINE W_
+STATIC_INLINE StgInfoTable*
get_threaded_info( P_ p )
{
W_ q = (W_)GET_INFO(UNTAG_CLOSURE((StgClosure *)p));
@@ -172,16 +202,13 @@ loop:
{
case 0:
ASSERT(LOOKS_LIKE_INFO_PTR(q));
- return q;
+ return (StgInfoTable*)q;
case 1:
- {
- W_ r = *(P_)(q-1);
- ASSERT(LOOKS_LIKE_INFO_PTR((W_)UNTAG_CONST_CLOSURE((StgClosure *)r)));
- return r;
- }
case 2:
- q = *(P_)(q-2);
+ {
+ q = *(P_)(UNTAG_PTR(q));
goto loop;
+ }
default:
barf("get_threaded_info");
}
@@ -353,8 +380,7 @@ thread_stack(P_ p, P_ stack_end)
{
StgRetFun *ret_fun = (StgRetFun *)p;
StgFunInfoTable *fun_info =
- FUN_INFO_PTR_TO_STRUCT((StgInfoTable *)UNTAG_PTR(
- get_threaded_info((P_)ret_fun->fun)));
+ FUN_INFO_PTR_TO_STRUCT(get_threaded_info((P_)ret_fun->fun));
// *before* threading it!
thread(&ret_fun->fun);
p = thread_arg_block(fun_info, ret_fun->payload);
@@ -372,7 +398,7 @@ STATIC_INLINE P_
thread_PAP_payload (StgClosure *fun, StgClosure **payload, W_ size)
{
StgFunInfoTable *fun_info =
- FUN_INFO_PTR_TO_STRUCT((StgInfoTable *)UNTAG_PTR(get_threaded_info((P_)fun)));
+ FUN_INFO_PTR_TO_STRUCT(get_threaded_info((P_)fun));
ASSERT(fun_info->i.type != PAP);
P_ p = (P_)payload;
@@ -762,8 +788,8 @@ update_fwd_compact( bdescr *blocks )
// ToDo: one possible avenue of attack is to use the fact
// that if (p&BLOCK_MASK) >= (free&BLOCK_MASK), then we
// definitely have enough room. Also see bug #1147.
- W_ iptr = get_threaded_info(p);
- StgInfoTable *info = INFO_PTR_TO_STRUCT((StgInfoTable *)UNTAG_PTR(iptr));
+ StgInfoTable *iptr = get_threaded_info(p);
+ StgInfoTable *info = INFO_PTR_TO_STRUCT(iptr);
P_ q = p;
@@ -783,7 +809,8 @@ update_fwd_compact( bdescr *blocks )
ASSERT(!is_marked(q+1,bd));
}
- unthread(q,(W_)free + GET_PTR_TAG(iptr));
+ StgWord iptr_tag = get_iptr_tag(iptr);
+ unthread(q, (W_)free, iptr_tag);
free += size;
}
}
@@ -819,8 +846,9 @@ update_bkwd_compact( generation *gen )
free_blocks++;
}
- W_ iptr = get_threaded_info(p);
- unthread(p, (W_)free + GET_PTR_TAG(iptr));
+ StgInfoTable *iptr = get_threaded_info(p);
+ StgWord iptr_tag = get_iptr_tag(iptr);
+ unthread(p, (W_)free, iptr_tag);
ASSERT(LOOKS_LIKE_INFO_PTR((W_)((StgClosure *)p)->header.info));
const StgInfoTable *info = get_itbl((StgClosure *)p);
W_ size = closure_sizeW_((StgClosure *)p,info);
diff --git a/testsuite/tests/rts/T17088.hs b/testsuite/tests/rts/T17088.hs
new file mode 100644
index 0000000000..f607ed38e3
--- /dev/null
+++ b/testsuite/tests/rts/T17088.hs
@@ -0,0 +1,79 @@
+{-# LANGUAGE BangPatterns #-}
+{-# LANGUAGE MagicHash #-}
+{-# LANGUAGE UnboxedTuples #-}
+
+module Main (main) where
+
+import Data.Word
+import Foreign.Storable
+import GHC.Prim
+import GHC.Ptr
+import GHC.Types
+import System.IO.Unsafe
+
+----------------------------------------------------------------
+
+allocAndFreeze :: Int -> Bytes
+allocAndFreeze sz = unsafePerformIO (bytesAllocRet sz)
+
+data Bytes = Bytes (MutableByteArray# RealWorld)
+data IBA = IBA (ByteArray#)
+
+instance Show Bytes where
+ showsPrec p b = showsPrec p (bytesUnpackChars b)
+
+------------------------------------------------------------------------
+
+bytesAllocRet :: Int -> IO Bytes
+bytesAllocRet (I# sz) =
+ IO $ \s -> case newAlignedPinnedByteArray# sz 8# s of
+ (# s', mba #) -> (# s', Bytes mba #)
+
+------------------------------------------------------------------------
+
+bytesEq :: Bytes -> Bytes -> Bool
+bytesEq (Bytes m1) (Bytes m2)
+ | isTrue# (len /=# len') = False
+ | otherwise = unsafePerformIO $ IO $ \s -> loop 0# s
+ where
+ !len = sizeofMutableByteArray# m1
+ !len' = sizeofMutableByteArray# m2
+
+ loop i s
+ | isTrue# (i ==# len) = (# s, True #)
+ | otherwise =
+ case readWord8Array# m1 i s of
+ (# s', e1 #) ->
+ case readWord8Array# m2 i s' of
+ (# s'', e2 #) ->
+ if isTrue# (eqWord# e1 e2)
+ then loop (i +# 1#) s''
+ else (# s'', False #)
+
+
+bytesUnpackChars :: Bytes -> String
+bytesUnpackChars (Bytes mba)
+ | I# (sizeofMutableByteArray# mba) == 0 = []
+ | otherwise = unsafePerformIO $ do
+ c <- IO $ \s -> case readWord8Array# mba 0# s of
+ (# s'', w #) -> (# s'', C# (chr# (word2Int# w)) #)
+ return [c]
+
+----------------------------------------------------------------
+
+publicKeyStream :: [Bytes]
+publicKeyStream
+ = take 10000
+ $ map (go . fromIntegral) [1::Int ..]
+ where
+ go :: Word8 -> Bytes
+ go a = allocAndFreeze 1
+
+main :: IO ()
+main = do
+ let !pubK = head publicKeyStream
+ let (!k1) : _ = [ pk
+ | pk <- reverse publicKeyStream
+ , bytesEq pk pubK
+ ]
+ print k1
diff --git a/testsuite/tests/rts/T17088.stdout b/testsuite/tests/rts/T17088.stdout
new file mode 100644
index 0000000000..0218850619
--- /dev/null
+++ b/testsuite/tests/rts/T17088.stdout
@@ -0,0 +1 @@
+"\NUL"
diff --git a/testsuite/tests/rts/all.T b/testsuite/tests/rts/all.T
index 91f3dec387..e4e2561c2e 100644
--- a/testsuite/tests/rts/all.T
+++ b/testsuite/tests/rts/all.T
@@ -413,3 +413,7 @@ test('T13676',
test('InitEventLogging',
[only_ways(['normal']), extra_run_opts('+RTS -RTS')],
compile_and_run, ['-eventlog InitEventLogging_c.c'])
+
+test('T17088',
+ [only_ways(['normal']), extra_run_opts('+RTS -c -A256k -RTS')],
+ compile_and_run, ['-rtsopts -O2'])