@@ -1147,48 +1147,63 @@ pub fn with_cond<'blk, 'tcx, F>(bcx: Block<'blk, 'tcx>, val: ValueRef, f: F) ->
1147
1147
next_cx
1148
1148
}
1149
1149
1150
- pub fn call_lifetime_start ( cx : Block , ptr : ValueRef ) {
1151
- if cx. sess ( ) . opts . optimize == config:: No {
1150
+ enum Lifetime { Start , End }
1151
+
1152
+ // If LLVM lifetime intrinsic support is enabled (i.e. optimizations
1153
+ // on), and `ptr` is nonzero-sized, then extracts the size of `ptr`
1154
+ // and the intrinsic for `lt` and passes them to `emit`, which is in
1155
+ // charge of generating code to call the passed intrinsic on whatever
1156
+ // block of generated code is targetted for the intrinsic.
1157
+ //
1158
+ // If LLVM lifetime intrinsic support is disabled (i.e. optimizations
1159
+ // off) or `ptr` is zero-sized, then no-op (does not call `emit`).
1160
+ fn core_lifetime_emit < ' blk , ' tcx , F > ( ccx : & ' blk CrateContext < ' blk , ' tcx > ,
1161
+ ptr : ValueRef ,
1162
+ lt : Lifetime ,
1163
+ emit : F )
1164
+ where F : FnOnce ( & ' blk CrateContext < ' blk , ' tcx > , machine:: llsize , ValueRef )
1165
+ {
1166
+ if ccx. sess ( ) . opts . optimize == config:: No {
1152
1167
return ;
1153
1168
}
1154
1169
1155
- let _icx = push_ctxt ( "lifetime_start" ) ;
1156
- let ccx = cx. ccx ( ) ;
1170
+ let _icx = push_ctxt ( match lt {
1171
+ Lifetime :: Start => "lifetime_start" ,
1172
+ Lifetime :: End => "lifetime_end"
1173
+ } ) ;
1157
1174
1158
1175
let size = machine:: llsize_of_alloc ( ccx, val_ty ( ptr) . element_type ( ) ) ;
1159
1176
if size == 0 {
1160
1177
return ;
1161
1178
}
1162
1179
1163
- let ptr = PointerCast ( cx, ptr, Type :: i8p ( ccx) ) ;
1164
- let lifetime_start = ccx. get_intrinsic ( & "llvm.lifetime.start" ) ;
1165
- Call ( cx,
1166
- lifetime_start,
1167
- & [ C_u64 ( ccx, size) , ptr] ,
1168
- None ,
1169
- DebugLoc :: None ) ;
1180
+ let lifetime_intrinsic = ccx. get_intrinsic ( match lt {
1181
+ Lifetime :: Start => "llvm.lifetime.start" ,
1182
+ Lifetime :: End => "llvm.lifetime.end"
1183
+ } ) ;
1184
+ emit ( ccx, size, lifetime_intrinsic)
1170
1185
}
1171
1186
1172
- pub fn call_lifetime_end ( cx : Block , ptr : ValueRef ) {
1173
- if cx. sess ( ) . opts . optimize == config:: No {
1174
- return ;
1175
- }
1176
-
1177
- let _icx = push_ctxt ( "lifetime_end" ) ;
1178
- let ccx = cx. ccx ( ) ;
1179
-
1180
- let size = machine:: llsize_of_alloc ( ccx, val_ty ( ptr) . element_type ( ) ) ;
1181
- if size == 0 {
1182
- return ;
1183
- }
1187
+ pub fn call_lifetime_start ( cx : Block , ptr : ValueRef ) {
1188
+ core_lifetime_emit ( cx. ccx ( ) , ptr, Lifetime :: Start , |ccx, size, lifetime_start| {
1189
+ let ptr = PointerCast ( cx, ptr, Type :: i8p ( ccx) ) ;
1190
+ Call ( cx,
1191
+ lifetime_start,
1192
+ & [ C_u64 ( ccx, size) , ptr] ,
1193
+ None ,
1194
+ DebugLoc :: None ) ;
1195
+ } )
1196
+ }
1184
1197
1185
- let ptr = PointerCast ( cx, ptr, Type :: i8p ( ccx) ) ;
1186
- let lifetime_end = ccx. get_intrinsic ( & "llvm.lifetime.end" ) ;
1187
- Call ( cx,
1188
- lifetime_end,
1189
- & [ C_u64 ( ccx, size) , ptr] ,
1190
- None ,
1191
- DebugLoc :: None ) ;
1198
+ pub fn call_lifetime_end ( cx : Block , ptr : ValueRef ) {
1199
+ core_lifetime_emit ( cx. ccx ( ) , ptr, Lifetime :: End , |ccx, size, lifetime_end| {
1200
+ let ptr = PointerCast ( cx, ptr, Type :: i8p ( ccx) ) ;
1201
+ Call ( cx,
1202
+ lifetime_end,
1203
+ & [ C_u64 ( ccx, size) , ptr] ,
1204
+ None ,
1205
+ DebugLoc :: None ) ;
1206
+ } )
1192
1207
}
1193
1208
1194
1209
// Generates code for resumption of unwind at the end of a landing pad.
@@ -1285,12 +1300,81 @@ fn memfill<'a, 'tcx>(b: &Builder<'a, 'tcx>, llptr: ValueRef, ty: Ty<'tcx>, byte:
1285
1300
None ) ;
1286
1301
}
1287
1302
1288
- pub fn alloc_ty < ' blk , ' tcx > ( bcx : Block < ' blk , ' tcx > , t : Ty < ' tcx > , name : & str ) -> ValueRef {
1303
+ /// In general, when we create an scratch value in an alloca, the
1304
+ /// creator may not know if the block (that initializes the scratch
1305
+ /// with the desired value) actually dominates the cleanup associated
1306
+ /// with the scratch value.
1307
+ ///
1308
+ /// To deal with this, when we do an alloca (at the *start* of whole
1309
+ /// function body), we optionally can also set the associated
1310
+ /// dropped-flag state of the alloca to "dropped."
1311
+ #[ derive( Copy , Clone , Debug ) ]
1312
+ pub enum InitAlloca {
1313
+ /// Indicates that the state should have its associated drop flag
1314
+ /// set to "dropped" at the point of allocation.
1315
+ Dropped ,
1316
+ /// Indicates the value of the associated drop flag is irrelevant.
1317
+ /// The embedded string literal is a programmer provided argument
1318
+ /// for why. This is a safeguard forcing compiler devs to
1319
+ /// document; it might be a good idea to also emit this as a
1320
+ /// comment with the alloca itself when emitting LLVM output.ll.
1321
+ Uninit ( & ' static str ) ,
1322
+ }
1323
+
1324
+
1325
+ pub fn alloc_ty < ' blk , ' tcx > ( bcx : Block < ' blk , ' tcx > ,
1326
+ t : Ty < ' tcx > ,
1327
+ name : & str ) -> ValueRef {
1328
+ // pnkfelix: I do not know why alloc_ty meets the assumptions for
1329
+ // passing Uninit, but it was never needed (even back when we had
1330
+ // the original boolean `zero` flag on `lvalue_scratch_datum`).
1331
+ alloc_ty_init ( bcx, t, InitAlloca :: Uninit ( "all alloc_ty are uninit" ) , name)
1332
+ }
1333
+
1334
+ /// This variant of `fn alloc_ty` does not necessarily assume that the
1335
+ /// alloca should be created with no initial value. Instead the caller
1336
+ /// controls that assumption via the `init` flag.
1337
+ ///
1338
+ /// Note that if the alloca *is* initialized via `init`, then we will
1339
+ /// also inject an `llvm.lifetime.start` before that initialization
1340
+ /// occurs, and thus callers should not call_lifetime_start
1341
+ /// themselves. But if `init` says "uninitialized", then callers are
1342
+ /// in charge of choosing where to call_lifetime_start and
1343
+ /// subsequently populate the alloca.
1344
+ ///
1345
+ /// (See related discussion on PR #30823.)
1346
+ pub fn alloc_ty_init < ' blk , ' tcx > ( bcx : Block < ' blk , ' tcx > ,
1347
+ t : Ty < ' tcx > ,
1348
+ init : InitAlloca ,
1349
+ name : & str ) -> ValueRef {
1289
1350
let _icx = push_ctxt ( "alloc_ty" ) ;
1290
1351
let ccx = bcx. ccx ( ) ;
1291
1352
let ty = type_of:: type_of ( ccx, t) ;
1292
1353
assert ! ( !t. has_param_types( ) ) ;
1293
- alloca ( bcx, ty, name)
1354
+ match init {
1355
+ InitAlloca :: Dropped => alloca_dropped ( bcx, t, name) ,
1356
+ InitAlloca :: Uninit ( _) => alloca ( bcx, ty, name) ,
1357
+ }
1358
+ }
1359
+
1360
+ pub fn alloca_dropped < ' blk , ' tcx > ( cx : Block < ' blk , ' tcx > , ty : Ty < ' tcx > , name : & str ) -> ValueRef {
1361
+ let _icx = push_ctxt ( "alloca_dropped" ) ;
1362
+ let llty = type_of:: type_of ( cx. ccx ( ) , ty) ;
1363
+ if cx. unreachable . get ( ) {
1364
+ unsafe { return llvm:: LLVMGetUndef ( llty. ptr_to ( ) . to_ref ( ) ) ; }
1365
+ }
1366
+ let p = alloca ( cx, llty, name) ;
1367
+ let b = cx. fcx . ccx . builder ( ) ;
1368
+ b. position_before ( cx. fcx . alloca_insert_pt . get ( ) . unwrap ( ) ) ;
1369
+
1370
+ // This is just like `call_lifetime_start` (but latter expects a
1371
+ // Block, which we do not have for `alloca_insert_pt`).
1372
+ core_lifetime_emit ( cx. ccx ( ) , p, Lifetime :: Start , |ccx, size, lifetime_start| {
1373
+ let ptr = b. pointercast ( p, Type :: i8p ( ccx) ) ;
1374
+ b. call ( lifetime_start, & [ C_u64 ( ccx, size) , ptr] , None ) ;
1375
+ } ) ;
1376
+ memfill ( & b, p, ty, adt:: DTOR_DONE ) ;
1377
+ p
1294
1378
}
1295
1379
1296
1380
pub fn alloca ( cx : Block , ty : Type , name : & str ) -> ValueRef {
@@ -1639,6 +1723,8 @@ pub fn create_datums_for_fn_args<'a, 'tcx>(mut bcx: Block<'a, 'tcx>,
1639
1723
let fcx = bcx. fcx ;
1640
1724
let arg_scope_id = cleanup:: CustomScope ( arg_scope) ;
1641
1725
1726
+ debug ! ( "create_datums_for_fn_args" ) ;
1727
+
1642
1728
// Return an array wrapping the ValueRefs that we get from `get_param` for
1643
1729
// each argument into datums.
1644
1730
//
@@ -1650,6 +1736,7 @@ pub fn create_datums_for_fn_args<'a, 'tcx>(mut bcx: Block<'a, 'tcx>,
1650
1736
// This alloca should be optimized away by LLVM's mem-to-reg pass in
1651
1737
// the event it's not truly needed.
1652
1738
let mut idx = fcx. arg_offset ( ) as c_uint ;
1739
+ let uninit_reason = InitAlloca :: Uninit ( "fn_arg populate dominates dtor" ) ;
1653
1740
for ( i, & arg_ty) in arg_tys. iter ( ) . enumerate ( ) {
1654
1741
let arg_datum = if !has_tupled_arg || i < arg_tys. len ( ) - 1 {
1655
1742
if type_of:: arg_is_indirect ( bcx. ccx ( ) , arg_ty) &&
@@ -1669,9 +1756,12 @@ pub fn create_datums_for_fn_args<'a, 'tcx>(mut bcx: Block<'a, 'tcx>,
1669
1756
let data = get_param ( fcx. llfn , idx) ;
1670
1757
let extra = get_param ( fcx. llfn , idx + 1 ) ;
1671
1758
idx += 2 ;
1672
- unpack_datum ! ( bcx, datum:: lvalue_scratch_datum( bcx, arg_ty, "" ,
1759
+ unpack_datum ! ( bcx, datum:: lvalue_scratch_datum( bcx, arg_ty, "" , uninit_reason ,
1673
1760
arg_scope_id, ( data, extra) ,
1674
1761
|( data, extra) , bcx, dst| {
1762
+ debug!( "populate call for create_datum_for_fn_args \
1763
+ early fat arg, on arg[{}] ty={:?}", i, arg_ty) ;
1764
+
1675
1765
Store ( bcx, data, expr:: get_dataptr( bcx, dst) ) ;
1676
1766
Store ( bcx, extra, expr:: get_meta( bcx, dst) ) ;
1677
1767
bcx
@@ -1684,9 +1774,16 @@ pub fn create_datums_for_fn_args<'a, 'tcx>(mut bcx: Block<'a, 'tcx>,
1684
1774
datum:: lvalue_scratch_datum( bcx,
1685
1775
arg_ty,
1686
1776
"" ,
1777
+ uninit_reason,
1687
1778
arg_scope_id,
1688
1779
tmp,
1689
- |tmp, bcx, dst| tmp. store_to( bcx, dst) ) )
1780
+ |tmp, bcx, dst| {
1781
+
1782
+ debug!( "populate call for create_datum_for_fn_args \
1783
+ early thin arg, on arg[{}] ty={:?}", i, arg_ty) ;
1784
+
1785
+ tmp. store_to( bcx, dst)
1786
+ } ) )
1690
1787
}
1691
1788
} else {
1692
1789
// FIXME(pcwalton): Reduce the amount of code bloat this is responsible for.
@@ -1696,11 +1793,14 @@ pub fn create_datums_for_fn_args<'a, 'tcx>(mut bcx: Block<'a, 'tcx>,
1696
1793
datum:: lvalue_scratch_datum( bcx,
1697
1794
arg_ty,
1698
1795
"tupled_args" ,
1796
+ uninit_reason,
1699
1797
arg_scope_id,
1700
1798
( ) ,
1701
1799
|( ) ,
1702
1800
mut bcx,
1703
- llval| {
1801
+ llval| {
1802
+ debug!( "populate call for create_datum_for_fn_args \
1803
+ tupled_args, on arg[{}] ty={:?}", i, arg_ty) ;
1704
1804
for ( j, & tupled_arg_ty) in
1705
1805
tupled_arg_tys. iter( ) . enumerate( ) {
1706
1806
let lldest = StructGEP ( bcx, llval, j) ;
0 commit comments