diff --git a/lib/SILPasses/LoadStoreOpts.cpp b/lib/SILPasses/LoadStoreOpts.cpp index 832eccc5c39..3957de2f904 100644 --- a/lib/SILPasses/LoadStoreOpts.cpp +++ b/lib/SILPasses/LoadStoreOpts.cpp @@ -409,6 +409,15 @@ forwardAddressValueToUncheckedAddrToLoad(SILValue Address, // If the output is trivial, we have a trivial bit cast. if (OutputIsTrivial) { + + // The structs could have different size. We have code in the stdlib that + // casts pointers to differently sized integer types. This code prevents + // that we bitcast the values. + SILType InputTy = UADCI->getOperand().getType(); + if (OutputTy.getStructOrBoundGenericStruct() && + InputTy.getStructOrBoundGenericStruct()) + return SILValue(); + CastValue = B.createUncheckedTrivialBitCast(UADCI->getLoc(), StoredValue, OutputTy.getObjectType()); } else { diff --git a/lib/SILPasses/SILCombine.cpp b/lib/SILPasses/SILCombine.cpp index 89533714921..1de11129b11 100644 --- a/lib/SILPasses/SILCombine.cpp +++ b/lib/SILPasses/SILCombine.cpp @@ -1506,6 +1506,12 @@ visitPointerToAddressInst(PointerToAddressInst *PTAI) { return nullptr; } +static bool areBothStructs(SILType Ty1, SILType Ty2) { + if (Ty1.getStructOrBoundGenericStruct() && Ty2.getStructOrBoundGenericStruct()) + return true; + else return false; +} + SILInstruction * SILCombiner::visitUncheckedAddrCastInst(UncheckedAddrCastInst *UADCI) { SILModule &Mod = UADCI->getModule(); @@ -1546,6 +1552,13 @@ SILCombiner::visitUncheckedAddrCastInst(UncheckedAddrCastInst *UADCI) { if (InputIsTrivial && !OutputIsTrivial) return nullptr; + // The structs could have different size. We have code in the stdlib that + // casts pointers to differently sized integer types. This code prevents that + // we bitcast the values. + if (InputTy.getStructOrBoundGenericStruct() && + OutputTy.getStructOrBoundGenericStruct()) + return nullptr; + // For each user U of the unchecked_addr_cast... for (auto U : UADCI->getUses()) // Check if it is load. If it is not a load, bail... diff --git a/test/SILPasses/globalloadstoreopts.sil b/test/SILPasses/globalloadstoreopts.sil index 4236611eb22..85c86b9c4ae 100644 --- a/test/SILPasses/globalloadstoreopts.sil +++ b/test/SILPasses/globalloadstoreopts.sil @@ -638,3 +638,23 @@ bb2: %9999 = tuple() return %9999 : $() } + +struct C { + var i : Builtin.Int16 +} + +// Don't bitcast differently sized structs. +// CHECK-LABEL: sil @store_to_load_forward_unchecked_addr_cast_different_sized_struct +// CHECK-NOT: unchecked_trivial_bit_cast +// CHECK: return + +sil @store_to_load_forward_unchecked_addr_cast_different_sized_struct : $@thin (A) -> () { +bb0(%0 : $A): + %1 = alloc_stack $A + store %0 to %1#1 : $*A + %2 = unchecked_addr_cast %1#1 : $*A to $*C + %3 = load %2 : $*C + dealloc_stack %1#0 : $*@local_storage A + %9999 = tuple() + return %9999 : $() +} diff --git a/test/SILPasses/sil_combine.sil b/test/SILPasses/sil_combine.sil index 2cf365aa4c9..63c8f496740 100644 --- a/test/SILPasses/sil_combine.sil +++ b/test/SILPasses/sil_combine.sil @@ -1704,3 +1704,25 @@ bb0(%0 : $Builtin.NativeObject): return %3 : $Builtin.NativeObject } +struct FakeInt16 { + var val : Builtin.Int16 +} + +struct FakeInt32 { + var val : Builtin.Int32 +} + +// Don't promote this to an unchecked_trivial_bit_cast. + +// CHECK-LABEL: sil @unchecked_addr_cast_of_different_size_structs +// CHECK-NOT: unchecked_trivial_bit_cast +// CHECK: return + +sil @unchecked_addr_cast_of_different_size_structs : $@thin (@inout FakeInt32) -> Builtin.Int16 { +bb0(%0 : $*FakeInt32): + %1 = unchecked_addr_cast %0 : $*FakeInt32 to $*FakeInt16 + %2 = load %1 : $*FakeInt16 + %3 = struct_extract %2 : $FakeInt16, #FakeInt16.val + return %3 : $Builtin.Int16 +} +