@@ -315,68 +315,65 @@ splitAggregateLoad(LoadInst *loadInst, CanonicalizeInstruction &pass) {
315
315
// (store (struct_element_addr %base) object)
316
316
// ->
317
317
// (store %base (struct object))
318
+ //
319
+ // TODO: supporting enums here would be very easy. The main thing is adding
320
+ // support in `createAggFromFirstLevelProjections`.
321
+ // Note: we will not be able to support tuples because we cannot have a
322
+ // single-element tuple.
318
323
static SILBasicBlock::iterator
319
324
broadenSingleElementStores (StoreInst *storeInst,
320
325
CanonicalizeInstruction &pass) {
321
326
// Keep track of the next iterator after any newly added or to-be-deleted
322
327
// instructions. This must be valid regardless of whether the pass immediately
323
328
// deletes the instructions or simply records them for later deletion.
324
329
auto nextII = std::next (storeInst->getIterator ());
325
-
326
- // Bail if the store's destination is not a struct_element_addr.
327
- auto *sea = dyn_cast<StructElementAddrInst>(storeInst->getDest ());
328
- if (!sea)
329
- return nextII;
330
-
331
330
auto *f = storeInst->getFunction ();
332
331
333
- // Continue up the struct_element_addr chain, as long as each struct only has
334
- // a single property, creating StoreInsts along the way.
335
- SILBuilderWithScope builder (storeInst);
336
-
337
- SILValue result = storeInst->getSrc ();
338
- SILValue baseAddr = sea->getOperand ();
339
- SILValue storeAddr;
340
- while (true ) {
332
+ ProjectionPath projections (storeInst->getDest ()->getType ());
333
+ SILValue op = storeInst->getDest ();
334
+ while (isa<StructElementAddrInst>(op)) {
335
+ auto *inst = cast<SingleValueInstruction>(op);
336
+ SILValue baseAddr = inst->getOperand (0 );
341
337
SILType baseAddrType = baseAddr->getType ();
342
-
343
- // If our aggregate has unreferenced storage then we can never prove if it
344
- // actually has a single field.
345
- if (baseAddrType.aggregateHasUnreferenceableStorage ())
346
- break ;
347
-
348
338
auto *decl = baseAddrType.getStructOrBoundGenericStruct ();
349
339
assert (
350
340
!decl->isResilient (f->getModule ().getSwiftModule (),
351
341
f->getResilienceExpansion ()) &&
352
342
" This code assumes resilient structs can not have fragile fields. If "
353
343
" this assert is hit, this has been changed. Please update this code." );
354
344
355
- // NOTE: If this is ever changed to support enums, we must check for address
356
- // only types here. For structs we do not have to check since a single
357
- // element struct with a loadable element can never be address only. We
358
- // additionally do not have to worry about our input value being address
359
- // only since we are storing into it.
360
- if (decl->getStoredProperties ().size () != 1 )
345
+ // Bail if the store's destination is not a struct_element_addr or if the
346
+ // store's destination (%base) is not a loadable type. If %base is not a
347
+ // loadable type, we can't create it as a struct later on.
348
+ // If our aggregate has unreferenced storage then we can never prove if it
349
+ // actually has a single field.
350
+ if (!baseAddrType.isLoadable (*f) ||
351
+ baseAddrType.aggregateHasUnreferenceableStorage () ||
352
+ decl->getStoredProperties ().size () != 1 )
361
353
break ;
362
354
363
- // Update the store location now that we know it is safe.
364
- storeAddr = baseAddr;
355
+ projections.push_back (Projection (inst));
356
+ op = baseAddr;
357
+ }
365
358
366
- // Otherwise, create the struct .
367
- result = builder. createStruct (storeInst-> getLoc (),
368
- baseAddrType. getObjectType (), result) ;
359
+ // If we couldn't create a projection, bail .
360
+ if (projections. empty ())
361
+ return nextII ;
369
362
370
- // See if we have another struct_element_addr we can strip off. If we don't
371
- // then this as much as we can promote.
372
- sea = dyn_cast<StructElementAddrInst>(sea->getOperand ());
373
- if (!sea)
374
- break ;
375
- baseAddr = sea->getOperand ();
363
+ // Now work our way back up. At this point we know all operations we are going
364
+ // to do succeed (cast<SingleValueInst>, createAggFromFirstLevelProjections,
365
+ // etc.) so we can omit null checks. We should not bail at this point (we
366
+ // could create a double consume, or worse).
367
+ SILBuilderWithScope builder (storeInst);
368
+ SILValue result = storeInst->getSrc ();
369
+ SILValue storeAddr = storeInst->getDest ();
370
+ for (Projection proj : llvm::reverse (projections)) {
371
+ storeAddr = cast<SingleValueInstruction>(storeAddr)->getOperand (0 );
372
+ result = proj.createAggFromFirstLevelProjections (
373
+ builder, storeInst->getLoc (),
374
+ storeAddr->getType ().getObjectType (), {result})
375
+ .get ();
376
376
}
377
- // If we failed to create any structs, bail.
378
- if (result == storeInst->getSrc ())
379
- return nextII;
380
377
381
378
// Store the new struct-wrapped value into the final base address.
382
379
builder.createStore (storeInst->getLoc (), result, storeAddr,
0 commit comments