Sebastian Graf pushed to branch wip/sg/enter-taggable-invariant at Glasgow Haskell Compiler / GHC
Commits:
-
ad5b71c3
by Sebastian Graf at 2026-07-31T15:55:27+02:00
-
76ca53df
by Sebastian Graf at 2026-07-31T15:55:35+02:00
-
e74ed796
by Sebastian Graf at 2026-07-31T15:55:46+02:00
12 changed files:
- compiler/GHC/StgToCmm/Prim.hs
- rts/CloneStack.c
- rts/Compact.cmm
- rts/PrimOps.cmm
- rts/RtsAPI.c
- rts/Schedule.c
- rts/Threads.c
- rts/TopHandler.c
- rts/Weak.c
- rts/sm/Compact.c
- rts/sm/Evac.c
- testsuite/tests/ffi/should_run/T21305_cmm.cmm
Changes:
| ... | ... | @@ -83,8 +83,19 @@ cgOpApp (StgPrimOp primop) args res_ty = do |
| 83 | 83 | cfg <- getStgToCmmConfig
|
| 84 | 84 | let platform = stgToCmmPlatform cfg
|
| 85 | 85 | cmm_args <- getNonVoidArgAmodes args
|
| 86 | - -- See Note [Pointer tagging of unlifted boxed primitives]
|
|
| 87 | - let cmm_args' = zipWith (untagPrimArg platform) (nonVoidStgArgs args) cmm_args
|
|
| 86 | + -- See Note [Pointer tagging of unlifted boxed primitives]. Untag by the
|
|
| 87 | + -- primop's DECLARED signature type, so only a container argument is untagged
|
|
| 88 | + -- while a polymorphic value/element argument keeps its tag. The declared arg
|
|
| 89 | + -- types line up with the (unarised) Cmm arguments only when the primop has no
|
|
| 90 | + -- unboxed-tuple argument; the primops that do (e.g. VecPackOp) take no boxed
|
|
| 91 | + -- unlifted container, so leaving their arguments untouched is correct.
|
|
| 92 | + let (_, arg_tys, _, _, _) = primOpSig primop
|
|
| 93 | + nonVoid_arg_tys = [ ty | (arg, ty) <- zip args arg_tys
|
|
| 94 | + , not (null (stgArgRep arg)) ]
|
|
| 95 | + cmm_args'
|
|
| 96 | + | length nonVoid_arg_tys == length cmm_args
|
|
| 97 | + = zipWith (untagPrimArg platform) nonVoid_arg_tys cmm_args
|
|
| 98 | + | otherwise = cmm_args
|
|
| 88 | 99 | cmmPrimOpApp cfg primop cmm_args' (Just res_ty)
|
| 89 | 100 | |
| 90 | 101 | cgOpApp (StgPrimCallOp primcall) args _res_ty
|
| ... | ... | @@ -110,10 +121,10 @@ cmmPrimOpApp cfg primop cmm_args mres_ty = do |
| 110 | 121 | -- | Strip the pointer tag from a primop argument that is an unlifted boxed
|
| 111 | 122 | -- primitive (ByteArray#, Array#, MVar#, ...). See
|
| 112 | 123 | -- Note [Pointer tagging of unlifted boxed primitives].
|
| 113 | -untagPrimArg :: Platform -> NonVoid StgArg -> CmmExpr -> CmmExpr
|
|
| 114 | -untagPrimArg platform nv_arg e
|
|
| 115 | - | isUnliftedBoxedTy (stgArgType (fromNonVoid nv_arg)) = cmmUntag platform e
|
|
| 116 | - | otherwise = e
|
|
| 124 | +untagPrimArg :: Platform -> Type -> CmmExpr -> CmmExpr
|
|
| 125 | +untagPrimArg platform arg_ty e
|
|
| 126 | + | isUnliftedBoxedTy arg_ty = cmmOffsetB platform e (negate 1)
|
|
| 127 | + | otherwise = e
|
|
| 117 | 128 | |
| 118 | 129 | -- | Is this the type of an unlifted boxed /primitive/ (ByteArray#, Array#,
|
| 119 | 130 | -- MVar#, ...), whose pointer a producer tags with 1? Restricted to primitive
|
| ... | ... | @@ -122,11 +133,13 @@ untagPrimArg platform nv_arg e |
| 122 | 133 | -- (e.g. an array element) must not be stripped here.
|
| 123 | 134 | isUnliftedBoxedTy :: Type -> Bool
|
| 124 | 135 | isUnliftedBoxedTy ty =
|
| 125 | - isUnliftedType ty && isBoxedType ty &&
|
|
| 126 | - -- Look through newtypes (e.g. the ArrayArray# wrappers, which are newtypes
|
|
| 127 | - -- over MutableArray#) so a wrapped primitive container is still recognised,
|
|
| 128 | - -- while a user-defined unlifted @data@ type is not.
|
|
| 129 | - maybe False isPrimTyCon (tyConAppTyCon_maybe (unwrapType ty))
|
|
| 136 | + -- Match the tycon head first: it is total and yields Nothing for a (possibly
|
|
| 137 | + -- representation-polymorphic) type variable, for which isUnliftedType panics.
|
|
| 138 | + -- Look through newtypes (e.g. the ArrayArray# wrappers, newtypes over
|
|
| 139 | + -- MutableArray#) so a wrapped primitive container is still recognised.
|
|
| 140 | + case tyConAppTyCon_maybe (unwrapType ty) of
|
|
| 141 | + Just tc -> isPrimTyCon tc && isUnliftedType ty && isBoxedType ty
|
|
| 142 | + Nothing -> False
|
|
| 130 | 143 | |
| 131 | 144 | -- | Strip the tag from an unlifted boxed argument passed across a foreign-prim
|
| 132 | 145 | -- (or FFI) boundary, where the callee works with raw pointers. Unlike
|
| ... | ... | @@ -182,6 +195,11 @@ Foreign and foreign-prim arguments |
| 182 | 195 | @Any \@UnliftedRep@ arguments, so the predicate here admits any unlifted boxed
|
| 183 | 196 | type rather than only the primitive ones.
|
| 184 | 197 | |
| 198 | + In the result direction the callee is a producer: an unlifted boxed value
|
|
| 199 | + returned by a 'foreign import prim' callee carries tag 1, following the
|
|
| 200 | + convention of the out-of-line primops in rts/PrimOps.cmm. Callers read the
|
|
| 201 | + result through that tag (see e.g. T21305).
|
|
| 202 | + |
|
| 185 | 203 | Stripping the tag from an already-untagged pointer is the identity, so the
|
| 186 | 204 | boundaries are correct whether or not a given producer has been taught to tag.
|
| 187 | 205 |
| ... | ... | @@ -78,7 +78,9 @@ void sendCloneStackMessage(StgTSO *tso, HsStablePtr mvar) { |
| 78 | 78 | MessageCloneStack *msg;
|
| 79 | 79 | msg = (MessageCloneStack *)allocate(srcCapability, sizeofW(MessageCloneStack));
|
| 80 | 80 | msg->tso = tso;
|
| 81 | - msg->result = (StgMVar*)deRefStablePtr(mvar);
|
|
| 81 | + // The stable pointer's referent (an MVar#) carries the boxed-unlifted-
|
|
| 82 | + // primitive pointer tag; strip it before the RTS dereferences it.
|
|
| 83 | + msg->result = (StgMVar*)UNTAG_CLOSURE((StgClosure*)deRefStablePtr(mvar));
|
|
| 82 | 84 | SET_HDR_RELEASE(msg, &stg_MSG_CLONE_STACK_info, CCS_SYSTEM);
|
| 83 | 85 | |
| 84 | 86 | sendMessage(srcCapability, tso->cap, (Message *)msg);
|
| ... | ... | @@ -143,7 +143,7 @@ eval: |
| 143 | 143 | case ARR_WORDS: {
|
| 144 | 144 | |
| 145 | 145 | (should) = ccall shouldCompact(compact "ptr", p "ptr");
|
| 146 | - if (should == SHOULDCOMPACT_IN_CNF) { P_[pp] = p; return(); }
|
|
| 146 | + if (should == SHOULDCOMPACT_IN_CNF) { P_[pp] = tag | p; return(); }
|
|
| 147 | 147 | if (should == SHOULDCOMPACT_PINNED) {
|
| 148 | 148 | jump stg_raisezh(HsIface_cannotCompactPinned_closure(W_[ghc_hs_iface]));
|
| 149 | 149 | }
|
| ... | ... | @@ -154,7 +154,7 @@ eval: |
| 154 | 154 | W_ size;
|
| 155 | 155 | size = SIZEOF_StgArrBytes + StgArrBytes_bytes(p);
|
| 156 | 156 | ALLOCATE(compact, ROUNDUP_BYTES_TO_WDS(size), p, to, tag);
|
| 157 | - P_[pp] = to;
|
|
| 157 | + P_[pp] = tag | to;
|
|
| 158 | 158 | prim %memcpy(to, p, size, 1);
|
| 159 | 159 | return();
|
| 160 | 160 | }
|
| ... | ... | @@ -164,7 +164,7 @@ eval: |
| 164 | 164 | MUT_ARR_PTRS_FROZEN_CLEAN: {
|
| 165 | 165 | |
| 166 | 166 | (should) = ccall shouldCompact(compact "ptr", p "ptr");
|
| 167 | - if (should == SHOULDCOMPACT_IN_CNF) { P_[pp] = p; return(); }
|
|
| 167 | + if (should == SHOULDCOMPACT_IN_CNF) { P_[pp] = tag | p; return(); }
|
|
| 168 | 168 | |
| 169 | 169 | CHECK_HASH();
|
| 170 | 170 | |
| ... | ... | @@ -196,7 +196,7 @@ eval: |
| 196 | 196 | SMALL_MUT_ARR_PTRS_FROZEN_CLEAN: {
|
| 197 | 197 | |
| 198 | 198 | (should) = ccall shouldCompact(compact "ptr", p "ptr");
|
| 199 | - if (should == SHOULDCOMPACT_IN_CNF) { P_[pp] = p; return(); }
|
|
| 199 | + if (should == SHOULDCOMPACT_IN_CNF) { P_[pp] = tag | p; return(); }
|
|
| 200 | 200 | |
| 201 | 201 | CHECK_HASH();
|
| 202 | 202 |
| ... | ... | @@ -1113,7 +1113,8 @@ stg_listThreadszh () |
| 1113 | 1113 | jump stg_raisezh(HsIface_heapOverflow_closure(W_[ghc_hs_iface]));
|
| 1114 | 1114 | }
|
| 1115 | 1115 | |
| 1116 | - return (arr);
|
|
| 1116 | + // Tag the freshly allocated Array# with the boxed-unlifted-primitive tag.
|
|
| 1117 | + return (arr + 1);
|
|
| 1117 | 1118 | }
|
| 1118 | 1119 | |
| 1119 | 1120 | stg_isCurrentThreadBoundzh (/* no args */)
|
| ... | ... | @@ -1130,7 +1131,8 @@ stg_threadLabelzh ( gcptr tso ) |
| 1130 | 1131 | if (r == 0) {
|
| 1131 | 1132 | return (0, 0);
|
| 1132 | 1133 | } else {
|
| 1133 | - return (1, r);
|
|
| 1134 | + // The label is a ByteArray#, stored untagged; tag it for the consumer.
|
|
| 1135 | + return (1, r + 1);
|
|
| 1134 | 1136 | }
|
| 1135 | 1137 | }
|
| 1136 | 1138 |
| ... | ... | @@ -982,7 +982,9 @@ void hs_try_putmvar_with_value (/* in */ int capability, |
| 982 | 982 | |
| 983 | 983 | #if !defined(THREADED_RTS)
|
| 984 | 984 | |
| 985 | - performTryPutMVar(cap, (StgMVar*)deRefStablePtr(mvar), value);
|
|
| 985 | + // The stable pointer's referent (an MVar#) carries the boxed-unlifted-
|
|
| 986 | + // primitive pointer tag; strip it before the RTS dereferences it.
|
|
| 987 | + performTryPutMVar(cap, (StgMVar*)UNTAG_CLOSURE((StgClosure*)deRefStablePtr(mvar)), value);
|
|
| 986 | 988 | freeStablePtr(mvar);
|
| 987 | 989 | |
| 988 | 990 | #else
|
| ... | ... | @@ -995,7 +997,9 @@ void hs_try_putmvar_with_value (/* in */ int capability, |
| 995 | 997 | task->cap = cap;
|
| 996 | 998 | RELEASE_LOCK(&cap->lock);
|
| 997 | 999 | |
| 998 | - performTryPutMVar(cap, (StgMVar*)deRefStablePtr(mvar), value);
|
|
| 1000 | + // The stable pointer's referent (an MVar#) carries the boxed-unlifted-
|
|
| 1001 | + // primitive pointer tag; strip it before the RTS dereferences it.
|
|
| 1002 | + performTryPutMVar(cap, (StgMVar*)UNTAG_CLOSURE((StgClosure*)deRefStablePtr(mvar)), value);
|
|
| 999 | 1003 | |
| 1000 | 1004 | freeStablePtr(mvar);
|
| 1001 | 1005 |
| ... | ... | @@ -1053,7 +1053,8 @@ scheduleProcessInbox (Capability **pcap USED_IF_THREADS) |
| 1053 | 1053 | |
| 1054 | 1054 | while (p != NULL) {
|
| 1055 | 1055 | pnext = p->link;
|
| 1056 | - performTryPutMVar(cap, (StgMVar*)deRefStablePtr(p->mvar),
|
|
| 1056 | + performTryPutMVar(cap,
|
|
| 1057 | + (StgMVar*)UNTAG_CLOSURE((StgClosure*)deRefStablePtr(p->mvar)),
|
|
| 1057 | 1058 | Unit_closure);
|
| 1058 | 1059 | freeStablePtr(p->mvar);
|
| 1059 | 1060 | stgFree(p);
|
| ... | ... | @@ -1135,7 +1136,10 @@ schedulePostRunThread (Capability *cap, StgTSO *t) |
| 1135 | 1136 | if(allocLimitRunHook)
|
| 1136 | 1137 | {
|
| 1137 | 1138 | // Create a thread to run the allocation limit handler.
|
| 1138 | - StgClosure* c = rts_apply(cap, runAllocationLimitHandler_closure, (StgClosure*)t);
|
|
| 1139 | + // The handler receives the offending thread as a ThreadId#, which
|
|
| 1140 | + // carries the boxed-unlifted-primitive pointer tag.
|
|
| 1141 | + StgClosure* c = rts_apply(cap, runAllocationLimitHandler_closure,
|
|
| 1142 | + TAG_CLOSURE(1, (StgClosure*)t));
|
|
| 1139 | 1143 | StgTSO* hookThread = createIOThread(cap, RtsFlags.GcFlags.initialStkSize, c);
|
| 1140 | 1144 | setThreadLabel(cap, hookThread, "allocation limit handler thread");
|
| 1141 | 1145 | // Schedule the handler to be run immediatelly.
|
| ... | ... | @@ -934,7 +934,9 @@ StgMutArrPtrs *listThreads(Capability *cap) |
| 934 | 934 | // Ignore them.
|
| 935 | 935 | if (i == n_threads)
|
| 936 | 936 | break;
|
| 937 | - arr->payload[i] = (StgClosure *) t;
|
|
| 937 | + // The array elements are ThreadId#s, which carry the
|
|
| 938 | + // boxed-unlifted-primitive pointer tag.
|
|
| 939 | + arr->payload[i] = TAG_CLOSURE(1, (StgClosure *) t);
|
|
| 938 | 940 | i++;
|
| 939 | 941 | }
|
| 940 | 942 | }
|
| ... | ... | @@ -19,7 +19,8 @@ void rts_setMainThread(StgWeak *weak) { |
| 19 | 19 | |
| 20 | 20 | // See Note [rts_setMainThread has an unsound type] in
|
| 21 | 21 | // libraries/base/GHC/TopHandler.hs.
|
| 22 | - ASSERT(weak->key->header.info == &stg_TSO_info);
|
|
| 22 | + // The key is a ThreadId#, which carries the boxed-unlifted-primitive tag.
|
|
| 23 | + ASSERT(UNTAG_CLOSURE(weak->key)->header.info == &stg_TSO_info);
|
|
| 23 | 24 | |
| 24 | 25 | RELEASE_LOCK(&m);
|
| 25 | 26 | }
|
| ... | ... | @@ -34,7 +35,7 @@ StgTSO *getTopHandlerThread(void) { |
| 34 | 35 | }
|
| 35 | 36 | const StgInfoTable *info = ACQUIRE_LOAD(&weak->header.info);
|
| 36 | 37 | if (info == &stg_WEAK_info) {
|
| 37 | - StgClosure *key = ((StgWeak*)weak)->key;
|
|
| 38 | + StgClosure *key = UNTAG_CLOSURE(((StgWeak*)weak)->key);
|
|
| 38 | 39 | |
| 39 | 40 | // See Note [rts_setMainThread has an unsound type] in
|
| 40 | 41 | // libraries/base/GHC/TopHandler.hs.
|
| ... | ... | @@ -173,7 +173,9 @@ scheduleFinalizers(Capability *cap, StgWeak *list) |
| 173 | 173 | rts_apply(cap,
|
| 174 | 174 | (StgClosure *)runFinalizerBatch_closure,
|
| 175 | 175 | rts_mkInt(cap,n)),
|
| 176 | - (StgClosure *)arr)
|
|
| 176 | + // runFinalizerBatch indexes this as an Array#, so it
|
|
| 177 | + // must carry the boxed-unlifted-primitive pointer tag.
|
|
| 178 | + TAG_CLOSURE(1, (StgClosure *)arr))
|
|
| 177 | 179 | );
|
| 178 | 180 | |
| 179 | 181 | scheduleThread(cap,t);
|
| ... | ... | @@ -122,6 +122,31 @@ get_iptr_tag(StgInfoTable *iptr) |
| 122 | 122 | }
|
| 123 | 123 | }
|
| 124 | 124 | |
| 125 | + case ARR_WORDS:
|
|
| 126 | + case MUT_ARR_PTRS_CLEAN:
|
|
| 127 | + case MUT_ARR_PTRS_DIRTY:
|
|
| 128 | + case MUT_ARR_PTRS_FROZEN_CLEAN:
|
|
| 129 | + case MUT_ARR_PTRS_FROZEN_DIRTY:
|
|
| 130 | + case SMALL_MUT_ARR_PTRS_CLEAN:
|
|
| 131 | + case SMALL_MUT_ARR_PTRS_DIRTY:
|
|
| 132 | + case SMALL_MUT_ARR_PTRS_FROZEN_CLEAN:
|
|
| 133 | + case SMALL_MUT_ARR_PTRS_FROZEN_DIRTY:
|
|
| 134 | + case MUT_VAR_CLEAN:
|
|
| 135 | + case MUT_VAR_DIRTY:
|
|
| 136 | + case MVAR_CLEAN:
|
|
| 137 | + case MVAR_DIRTY:
|
|
| 138 | + case TVAR:
|
|
| 139 | + case WEAK:
|
|
| 140 | + case PRIM:
|
|
| 141 | + case MUT_PRIM:
|
|
| 142 | + case TSO:
|
|
| 143 | + case STACK:
|
|
| 144 | + case TREC_CHUNK:
|
|
| 145 | + case CONTINUATION:
|
|
| 146 | + // Boxed unlifted primitives carry the pointer tag 1; restore it (this is
|
|
| 147 | + // only consulted for references that were tagged before threading).
|
|
| 148 | + return 1;
|
|
| 149 | + |
|
| 125 | 150 | default:
|
| 126 | 151 | return 0;
|
| 127 | 152 | }
|
| ... | ... | @@ -328,7 +328,7 @@ copy_tag_nolock(StgClosure **p, const StgInfoTable *info, |
| 328 | 328 | */
|
| 329 | 329 | ATTR_ALWAYS_INLINE static inline bool
|
| 330 | 330 | copyPart(StgClosure **p, StgClosure *src, uint32_t size_to_reserve,
|
| 331 | - uint32_t size_to_copy, uint32_t gen_no)
|
|
| 331 | + uint32_t size_to_copy, uint32_t gen_no, StgWord tag)
|
|
| 332 | 332 | {
|
| 333 | 333 | StgPtr to, from;
|
| 334 | 334 | uint32_t i;
|
| ... | ... | @@ -361,7 +361,7 @@ spin: |
| 361 | 361 | to[i] = from[i];
|
| 362 | 362 | }
|
| 363 | 363 | |
| 364 | - RELEASE_STORE(p, (StgClosure *) to);
|
|
| 364 | + RELEASE_STORE(p, TAG_CLOSURE(tag, (StgClosure *) to));
|
|
| 365 | 365 | RELEASE_STORE(&src->header.info, (const StgInfoTable*)MK_FORWARDING_PTR(to));
|
| 366 | 366 | |
| 367 | 367 | #if defined(PROFILING)
|
| ... | ... | @@ -965,11 +965,13 @@ loop: |
| 965 | 965 | case WEAK:
|
| 966 | 966 | case PRIM:
|
| 967 | 967 | case MUT_PRIM:
|
| 968 | - copy(p,info,q,sizeW_fromITBL(INFO_PTR_TO_STRUCT(info)),gen_no);
|
|
| 968 | + // These boxed unlifted primitives carry the pointer tag 1; preserve it
|
|
| 969 | + // across evacuation.
|
|
| 970 | + copy_tag(p,info,q,sizeW_fromITBL(INFO_PTR_TO_STRUCT(info)),gen_no,tag);
|
|
| 969 | 971 | return;
|
| 970 | 972 | |
| 971 | 973 | case BCO:
|
| 972 | - copy(p,info,q,bco_sizeW((StgBCO *)q),gen_no);
|
|
| 974 | + copy_tag(p,info,q,bco_sizeW((StgBCO *)q),gen_no,tag);
|
|
| 973 | 975 | return;
|
| 974 | 976 | |
| 975 | 977 | case THUNK_SELECTOR:
|
| ... | ... | @@ -1009,28 +1011,28 @@ loop: |
| 1009 | 1011 | return;
|
| 1010 | 1012 | |
| 1011 | 1013 | case ARR_WORDS:
|
| 1012 | - // just copy the block
|
|
| 1013 | - copy(p,info,q,arr_words_sizeW((StgArrBytes *)q),gen_no);
|
|
| 1014 | + // just copy the block, preserving the pointer tag
|
|
| 1015 | + copy_tag(p,info,q,arr_words_sizeW((StgArrBytes *)q),gen_no,tag);
|
|
| 1014 | 1016 | return;
|
| 1015 | 1017 | |
| 1016 | 1018 | case MUT_ARR_PTRS_CLEAN:
|
| 1017 | 1019 | case MUT_ARR_PTRS_DIRTY:
|
| 1018 | 1020 | case MUT_ARR_PTRS_FROZEN_CLEAN:
|
| 1019 | 1021 | case MUT_ARR_PTRS_FROZEN_DIRTY:
|
| 1020 | - // just copy the block
|
|
| 1021 | - copy(p,info,q,mut_arr_ptrs_sizeW((StgMutArrPtrs *)q),gen_no);
|
|
| 1022 | + // just copy the block, preserving the pointer tag
|
|
| 1023 | + copy_tag(p,info,q,mut_arr_ptrs_sizeW((StgMutArrPtrs *)q),gen_no,tag);
|
|
| 1022 | 1024 | return;
|
| 1023 | 1025 | |
| 1024 | 1026 | case SMALL_MUT_ARR_PTRS_CLEAN:
|
| 1025 | 1027 | case SMALL_MUT_ARR_PTRS_DIRTY:
|
| 1026 | 1028 | case SMALL_MUT_ARR_PTRS_FROZEN_CLEAN:
|
| 1027 | 1029 | case SMALL_MUT_ARR_PTRS_FROZEN_DIRTY:
|
| 1028 | - // just copy the block
|
|
| 1029 | - copy(p,info,q,small_mut_arr_ptrs_sizeW((StgSmallMutArrPtrs *)q),gen_no);
|
|
| 1030 | + // just copy the block, preserving the pointer tag
|
|
| 1031 | + copy_tag(p,info,q,small_mut_arr_ptrs_sizeW((StgSmallMutArrPtrs *)q),gen_no,tag);
|
|
| 1030 | 1032 | return;
|
| 1031 | 1033 | |
| 1032 | 1034 | case TSO:
|
| 1033 | - copy(p,info,q,sizeofW(StgTSO),gen_no);
|
|
| 1035 | + copy_tag(p,info,q,sizeofW(StgTSO),gen_no,tag);
|
|
| 1034 | 1036 | return;
|
| 1035 | 1037 | |
| 1036 | 1038 | case STACK:
|
| ... | ... | @@ -1045,7 +1047,7 @@ loop: |
| 1045 | 1047 | bool mine;
|
| 1046 | 1048 | |
| 1047 | 1049 | mine = copyPart(p,(StgClosure *)stack, stack_sizeW(stack),
|
| 1048 | - sizeofW(StgStack), gen_no);
|
|
| 1050 | + sizeofW(StgStack), gen_no, tag);
|
|
| 1049 | 1051 | if (mine) {
|
| 1050 | 1052 | new_stack = (StgStack *)*p;
|
| 1051 | 1053 | move_STACK(stack, new_stack);
|
| ... | ... | @@ -1059,11 +1061,11 @@ loop: |
| 1059 | 1061 | }
|
| 1060 | 1062 | |
| 1061 | 1063 | case TREC_CHUNK:
|
| 1062 | - copy(p,info,q,sizeofW(StgTRecChunk),gen_no);
|
|
| 1064 | + copy_tag(p,info,q,sizeofW(StgTRecChunk),gen_no,tag);
|
|
| 1063 | 1065 | return;
|
| 1064 | 1066 | |
| 1065 | 1067 | case CONTINUATION:
|
| 1066 | - copy(p,info,q,continuation_sizeW((StgContinuation*)q),gen_no);
|
|
| 1068 | + copy_tag(p,info,q,continuation_sizeW((StgContinuation*)q),gen_no,tag);
|
|
| 1067 | 1069 | return;
|
| 1068 | 1070 | |
| 1069 | 1071 | default:
|
| ... | ... | @@ -2,5 +2,7 @@ |
| 2 | 2 | |
| 3 | 3 | f(P_ a, P_ b, P_ c) {
|
| 4 | 4 | I64[c + SIZEOF_StgArrBytes + 8] = 770000;
|
| 5 | - return (b, a, c);
|
|
| 5 | + // c is a MutableByteArray#; unlifted boxed results carry tag 1.
|
|
| 6 | + // See Note [Pointer tagging of unlifted boxed primitives] in GHC.StgToCmm.Prim.
|
|
| 7 | + return (b, a, c + 1);
|
|
| 6 | 8 | } |