From b4e336ebdbfa191a4e35a6bc0a9ebf592d1b89dc Mon Sep 17 00:00:00 2001 From: Nate Chandler Date: Fri, 9 Feb 2024 13:43:34 -0800 Subject: [PATCH] [Mem2Reg] Undef types in empty projection sources. Mem2Reg may materialize the unique instance of empty types when promoting an address of that empty type. Previously, it was required that the top-most type in the aggregate be empty. This failed to handle the case where a projection of empty type from a non-empty aggregate was promoted. For example: ``` %addr = alloc_stack $((), C) %empty_addr = tuple_element_addr $addr : $*((), C), 0 %addr = load %empty_addr : $*() ``` where `C` is some non-empty type. Here, that case is handled by using `undef` for each non-empty field in the projected-from aggregate. In the example, ``` %empty = tuple () %tuple = tuple (%empty : $(), undef : $C) %empty_again = tuple_extract %tuple : $((), C), 0 ``` rdar://122417297 --- lib/SILOptimizer/Transforms/SILMem2Reg.cpp | 23 +++++++++++----------- test/SILOptimizer/mem2reg.sil | 16 +++++++++++++++ 2 files changed, 27 insertions(+), 12 deletions(-) diff --git a/lib/SILOptimizer/Transforms/SILMem2Reg.cpp b/lib/SILOptimizer/Transforms/SILMem2Reg.cpp index 629f50d88f3..6d6db194014 100644 --- a/lib/SILOptimizer/Transforms/SILMem2Reg.cpp +++ b/lib/SILOptimizer/Transforms/SILMem2Reg.cpp @@ -679,18 +679,18 @@ replaceLoad(SILInstruction *inst, SILValue newValue, AllocStackInst *asi, } } -/// 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) { +/// Instantiate the specified type by recursively tupling and structing the +/// unique instances of the empty types and undef "instances" of the non-empty +/// types aggregated together at each level. +static SILValue createEmptyAndUndefValue(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); + auto element = createEmptyAndUndefValue(elementTy, insertionPoint, ctx); elements.push_back(element); } SILBuilderWithScope builder(insertionPoint, ctx); @@ -707,15 +707,14 @@ static SILValue createValueForEmptyType(SILType ty, SmallVector elements; for (auto *field : decl->getStoredProperties()) { auto elementTy = ty.getFieldType(field, module, tec); - auto element = createValueForEmptyType(elementTy, insertionPoint, ctx); + auto element = createEmptyAndUndefValue(elementTy, insertionPoint, ctx); elements.push_back(element); } SILBuilderWithScope builder(insertionPoint, ctx); return builder.createStruct(insertionPoint->getLoc(), ty, elements); + } else { + return SILUndef::get(ty, *insertionPoint->getFunction()); } - 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 @@ -1982,7 +1981,7 @@ void MemoryToRegisters::removeSingleBlockAllocation(AllocStackInst *asi) { // empty--an aggregate of types without storage. runningVals = { LiveValues::toReplace(asi, - /*replacement=*/createValueForEmptyType( + /*replacement=*/createEmptyAndUndefValue( asi->getElementType(), inst, ctx)), /*isStorageValid=*/true}; } diff --git a/test/SILOptimizer/mem2reg.sil b/test/SILOptimizer/mem2reg.sil index c33d80d127c..4ba3564907b 100644 --- a/test/SILOptimizer/mem2reg.sil +++ b/test/SILOptimizer/mem2reg.sil @@ -531,3 +531,19 @@ sil @dont_canonicalize_undef : $@convention(thin) () -> () { %retval = tuple () return %retval : $() } + +// CHECK-LABEL: sil @undef_for_load_of_empty_type_from_nontrivial_aggregate : {{.*}} { +// CHECK: [[EMPTY:%[^,]+]] = tuple () +// CHECK: [[AGGREGATE:%[^,]+]] = tuple (undef : $Builtin.BridgeObject, [[EMPTY]] : $()) +// CHECK: tuple_extract [[AGGREGATE]] : $(Builtin.BridgeObject, ()), 1 +// CHECK-LABEL: } // end sil function 'undef_for_load_of_empty_type_from_nontrivial_aggregate' +sil @undef_for_load_of_empty_type_from_nontrivial_aggregate : $@convention(thin) () -> () { +bb0: + %11 = alloc_stack $(Builtin.BridgeObject, ()) + %12 = tuple_element_addr %11 : $*(Builtin.BridgeObject, ()), 1 + %13 = load %12 : $*() + %empty = tuple () + %14 = tuple (%empty : $(), %13 : $()) + dealloc_stack %11 : $*(Builtin.BridgeObject, ()) + return undef : $() +}