Sebastian Graf pushed to branch wip/sg/enter-taggable-invariant at Glasgow Haskell Compiler / GHC

Commits:

12 changed files:

Changes:

  • compiler/GHC/StgToCmm/Prim.hs
    ... ... @@ -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
     
    

  • rts/CloneStack.c
    ... ... @@ -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);
    

  • rts/Compact.cmm
    ... ... @@ -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
     
    

  • rts/PrimOps.cmm
    ... ... @@ -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
     
    

  • rts/RtsAPI.c
    ... ... @@ -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
     
    

  • rts/Schedule.c
    ... ... @@ -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.
    

  • rts/Threads.c
    ... ... @@ -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
         }
    

  • rts/TopHandler.c
    ... ... @@ -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.
    

  • rts/Weak.c
    ... ... @@ -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);
    

  • rts/sm/Compact.c
    ... ... @@ -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
         }
    

  • rts/sm/Evac.c
    ... ... @@ -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:
    

  • testsuite/tests/ffi/should_run/T21305_cmm.cmm
    ... ... @@ -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
     }