diff --git a/lib/SILOptimizer/Transforms/SILMem2Reg.cpp b/lib/SILOptimizer/Transforms/SILMem2Reg.cpp index abb5dc33f73cf..c7de45389480e 100644 --- a/lib/SILOptimizer/Transforms/SILMem2Reg.cpp +++ b/lib/SILOptimizer/Transforms/SILMem2Reg.cpp @@ -327,19 +327,43 @@ replaceLoad(SILInstruction *inst, SILValue newValue, AllocStackInst *asi, } } -/// Create a tuple value for an empty tuple or a tuple of empty tuples. -static SILValue createValueForEmptyTuple(SILType ty, - SILInstruction *insertionPoint, - SILBuilderContext &ctx) { - auto tupleTy = ty.castTo(); - SmallVector elements; - for (unsigned idx : range(tupleTy->getNumElements())) { - SILType elementTy = ty.getTupleElementType(idx); - elements.push_back( - createValueForEmptyTuple(elementTy, insertionPoint, ctx)); +/// Instantiate the specified empty type by recursively tupling and structing +/// the empty types aggregated together at each level. +static SILValue createValueForEmptyType(SILType ty, + SILInstruction *insertionPoint, + SILBuilderContext &ctx) { + auto *function = insertionPoint->getFunction(); + assert(ty.isEmpty(*function)); + if (auto tupleTy = ty.getAs()) { + SmallVector elements; + for (unsigned idx : range(tupleTy->getNumElements())) { + SILType elementTy = ty.getTupleElementType(idx); + auto element = createValueForEmptyType(elementTy, insertionPoint, ctx); + elements.push_back(element); + } + SILBuilderWithScope builder(insertionPoint, ctx); + return builder.createTuple(insertionPoint->getLoc(), ty, elements); + } else if (auto *decl = ty.getStructOrBoundGenericStruct()) { + TypeExpansionContext tec = *function; + auto &module = function->getModule(); + if (decl->isResilient(tec.getContext()->getParentModule(), + tec.getResilienceExpansion())) { + llvm::errs() << "Attempting to create value for illegal empty type:\n"; + ty.print(llvm::errs()); + llvm::report_fatal_error("illegal empty type: resilient struct"); + } + SmallVector elements; + for (auto *field : decl->getStoredProperties()) { + auto elementTy = ty.getFieldType(field, module, tec); + auto element = createValueForEmptyType(elementTy, insertionPoint, ctx); + elements.push_back(element); + } + SILBuilderWithScope builder(insertionPoint, ctx); + return builder.createStruct(insertionPoint->getLoc(), ty, elements); } - SILBuilderWithScope builder(insertionPoint, ctx); - return builder.createTuple(insertionPoint->getLoc(), ty, elements); + llvm::errs() << "Attempting to create value for illegal empty type:\n"; + ty.print(llvm::errs()); + llvm::report_fatal_error("illegal empty type: neither tuple nor struct."); } /// Whether lexical lifetimes should be added for the values stored into the @@ -1607,11 +1631,11 @@ void MemoryToRegisters::removeSingleBlockAllocation(AllocStackInst *asi) { // with our running value. if (isLoadFromStack(inst, asi)) { if (!runningVals) { - // Loading without a previous store is only acceptable if the type is - // Void (= empty tuple) or a tuple of Voids. + // Loading from uninitialized memory is only acceptable if the type is + // empty--an aggregate of types without storage. runningVals = { LiveValues::toReplace(asi, - /*replacement=*/createValueForEmptyTuple( + /*replacement=*/createValueForEmptyType( asi->getElementType(), inst, ctx)), /*isStorageValid=*/true}; } diff --git a/test/SILOptimizer/mem2reg.sil b/test/SILOptimizer/mem2reg.sil index 7da05d58a1a40..e5c8947f126d6 100644 --- a/test/SILOptimizer/mem2reg.sil +++ b/test/SILOptimizer/mem2reg.sil @@ -22,6 +22,12 @@ struct LargeCodesizeStruct { var s5: SmallCodesizeStruct } +struct EmptyStruct {} +struct Pair { + var t: T + var u: U +} + /////////// // Tests // /////////// @@ -492,3 +498,23 @@ bb3: %11 = tuple () return %11 : $() } + +// CHECK-LABEL: sil @load_from_uninitialized_empty : {{.*}} { +// CHECK: [[V_3_0:%[^,]+]] = struct $EmptyStruct +// CHECK: [[V_3_1:%[^,]+]] = struct $EmptyStruct +// CHECK: [[V_2_0:%[^,]+]] = struct $Pair{{.*}} ([[V_3_0]]{{.*}}, [[V_3_1]] +// CHECK: [[V_2_1:%[^,]+]] = struct $EmptyStruct () +// CHECK: [[V_1_0:%[^,]+]] = struct $Pair{{.*}} ([[V_2_0]]{{.*}}, [[V_2_1]] +// CHECK: [[V_2_2:%[^,]+]] = struct $EmptyStruct +// CHECK: [[V_2_3:%[^,]+]] = struct $EmptyStruct +// CHECK: [[V_1_1:%[^,]+]] = tuple ([[V_2_2]]{{.*}}, [[V_2_3]] +// CHECK: [[V_1_2:%[^,]+]] = struct $EmptyStruct +// CHECK: [[V_0:%[^,]+]] = tuple ([[V_1_0]]{{.*}}, [[V_1_1]]{{.*}}, [[V_1_2]]{{.*}}) +// CHECK: return [[V_0]] +// CHECK-LABEL: } // end sil function 'load_from_uninitialized_empty' +sil @load_from_uninitialized_empty : $@convention(thin) () -> (Pair, EmptyStruct>, (EmptyStruct, EmptyStruct), EmptyStruct) { + %addr = alloc_stack $(Pair, EmptyStruct>, (EmptyStruct, EmptyStruct), EmptyStruct) + %value = load %addr : $*(Pair, EmptyStruct>, (EmptyStruct, EmptyStruct), EmptyStruct) + dealloc_stack %addr : $*(Pair, EmptyStruct>, (EmptyStruct, EmptyStruct), EmptyStruct) + return %value : $(Pair, EmptyStruct>, (EmptyStruct, EmptyStruct), EmptyStruct) +}