Skip to content

[4.1][sil] When expanding aggregate instructions, do so consistently based… #14163

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 0 additions & 8 deletions include/swift/SIL/TypeLowering.h
Original file line number Diff line number Diff line change
Expand Up @@ -259,14 +259,6 @@ class TypeLowering {

enum class LoweringStyle { Shallow, Deep };

/// Given the result of the expansion heuristic,
/// return appropriate lowering style.
static LoweringStyle getLoweringStyle(bool shouldExpand) {
if (shouldExpand)
return TypeLowering::LoweringStyle::Deep;
return TypeLowering::LoweringStyle::Shallow;
}

//===--------------------------------------------------------------------===//
// DestroyValue
//===--------------------------------------------------------------------===//
Expand Down
21 changes: 15 additions & 6 deletions lib/SILOptimizer/Transforms/SILLowerAggregateInstrs.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -112,17 +112,23 @@ static bool expandCopyAddr(CopyAddrInst *CA) {
// retain_value %new : $*T
IsTake_t IsTake = CA->isTakeOfSrc();
if (IsTake_t::IsNotTake == IsTake) {
TL.emitLoweredCopyValue(Builder, CA->getLoc(), New,
TypeLowering::getLoweringStyle(expand));
if (expand) {
TL.emitLoweredCopyValueDeep(Builder, CA->getLoc(), New);
} else {
TL.emitCopyValue(Builder, CA->getLoc(), New);
}
}

// If we are not initializing:
// strong_release %old : $*T
// *or*
// release_value %old : $*T
if (Old) {
TL.emitLoweredDestroyValue(Builder, CA->getLoc(), Old,
TypeLowering::getLoweringStyle(expand));
if (expand) {
TL.emitLoweredDestroyValueDeep(Builder, CA->getLoc(), Old);
} else {
TL.emitDestroyValue(Builder, CA->getLoc(), Old);
}
}
}

Expand Down Expand Up @@ -155,8 +161,11 @@ static bool expandDestroyAddr(DestroyAddrInst *DA) {
LoadInst *LI = Builder.createLoad(DA->getLoc(), Addr,
LoadOwnershipQualifier::Unqualified);
auto &TL = Module.getTypeLowering(Type);
TL.emitLoweredDestroyValue(Builder, DA->getLoc(), LI,
TypeLowering::getLoweringStyle(expand));
if (expand) {
TL.emitLoweredDestroyValueDeep(Builder, DA->getLoc(), LI);
} else {
TL.emitDestroyValue(Builder, DA->getLoc(), LI);
}
}

++NumExpand;
Expand Down
8 changes: 6 additions & 2 deletions lib/SILOptimizer/Transforms/SILMem2Reg.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -350,8 +350,12 @@ static void replaceDestroy(DestroyAddrInst *DAI, SILValue NewValue) {

bool expand = shouldExpand(DAI->getModule(),
DAI->getOperand()->getType().getObjectType());
TL.emitLoweredDestroyValue(Builder, DAI->getLoc(), NewValue,
Lowering::TypeLowering::getLoweringStyle(expand));
if (expand) {
TL.emitLoweredDestroyValueDeep(Builder, DAI->getLoc(), NewValue);
} else {
TL.emitDestroyValue(Builder, DAI->getLoc(), NewValue);
}

DAI->eraseFromParent();
}

Expand Down
11 changes: 11 additions & 0 deletions test/Executable/Inputs/arc_36509461.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@

#ifndef ARC36509461_H
#define ARC36509461_H

#include <stdbool.h>

typedef bool (^fake_apply_t)(const char *key, id value);

bool fake_apply(id obj, fake_apply_t applier);

#endif
6 changes: 6 additions & 0 deletions test/Executable/Inputs/arc_36509461.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@

#import "arc_36509461.h"

bool fake_apply(id obj, fake_apply_t applier) {
return false;
}
55 changes: 55 additions & 0 deletions test/Executable/arc_36509461.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
// RUN: %empty-directory(%T)
// RUN: %target-clang -x objective-c -c %S/Inputs/arc_36509461.m -o %T/arc_36509461.m.o
// RUN: %target-swift-frontend -c -O -import-objc-header %S/Inputs/arc_36509461.h -sanitize=address %s -o %T/arc_36509461.swift.o
// RUN: %target-build-swift %T/arc_36509461.m.o %T/arc_36509461.swift.o -sanitize=address -o %t
// RUN: %t

// REQUIRES: executable_test
// REQUIRES: asan_runtime
// REQUIRES: objc_interop

import Foundation

struct FakeUUID {
var bigTuple: (UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8)

init() {
bigTuple = (0, 0, 0, 0, 0, 0, 0, 0)
}
}

struct Record {
let name: String
let uuid: FakeUUID
let storage: NSObject

init(storage: NSObject, name: String, uuid: FakeUUID) {
self.name = name
self.uuid = uuid
self.storage = storage
}

func copy() -> Record {
let copiedNSObject = NSObject()

fake_apply(self.storage) { (key, value) -> Bool in
let x = copiedNSObject
return true
}

var record = Record(storage: copiedNSObject, name: self.name, uuid: self.uuid)
return record
}
}

@inline(never)
func foo(record: Record) -> Record {
return record.copy()
}

func main() {
let record = Record(storage: NSObject(), name: "", uuid: FakeUUID())
_ = foo(record: record)
}

main()
4 changes: 4 additions & 0 deletions test/SILOptimizer/loweraggregateinstrs.sil
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
// RUN: %target-sil-opt -assume-parsing-unqualified-ownership-sil -enable-sil-verify-all -enable-expand-all %s -lower-aggregate-instrs | %FileCheck %s

// This file makes sure that the mechanics of expanding aggregate instructions
// work. With that in mind, we expand all structs here ignoring code-size
// trade-offs.

sil_stage canonical

import Swift
Expand Down
126 changes: 126 additions & 0 deletions test/SILOptimizer/loweraggregateinstrs_codesize.sil
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
// RUN: %target-sil-opt -assume-parsing-unqualified-ownership-sil -enable-sil-verify-all %s -lower-aggregate-instrs | %FileCheck %s

// This file makes sure that given the current code-size metric we properly
// expand operations for small structs and not for large structs in a consistent
// way for all operations we expand.

sil_stage canonical

import Swift
import Builtin

class C1 {
var data : Builtin.Int64
init()
}

class C2 {
var data : Builtin.FPIEEE32
init()
}

class C3 {
var data : Builtin.FPIEEE64
init()
}

struct S2 {
var cls1 : C1
var cls2 : C2
var trivial : Builtin.FPIEEE32
}

struct S {
var trivial : Builtin.Int64
var cls : C3
var inner_struct : S2
}

enum E {
case NoElement()
case TrivialElement(Builtin.Int64)
case ReferenceElement(C1)
case StructNonTrivialElt(S)
case TupleNonTrivialElt((Builtin.Int64, S, C3))
}

// This struct is larger than our current code-size limit (> 6 leaf nodes).
struct LargeStruct {
var trivial1 : Builtin.Int64
var cls : S
var trivial2 : Builtin.Int64
var trivial3 : Builtin.Int64
}

///////////
// Tests //
///////////

// This test makes sure that we /do not/ expand retain_value, release_value and
// promote copy_addr/destroy_value to non-expanded retain_value, release_value.
// CHECK-LABEL: sil @large_struct_test : $@convention(thin) (@owned LargeStruct, @in LargeStruct) -> () {
// CHECK: bb0([[ARG0:%.*]] : $LargeStruct, [[ARG1:%.*]] : $*LargeStruct):
// CHECK: retain_value [[ARG0]]
// CHECK: release_value [[ARG0]]
// CHECK: [[ALLOC_STACK:%.*]] = alloc_stack $LargeStruct
// CHECK: [[LOADED_ARG1:%.*]] = load [[ARG1]]
// CHECK: [[LOADED_OLD_VAL:%.*]] = load [[ALLOC_STACK]]
// CHECK: retain_value [[LOADED_ARG1]]
// CHECK: release_value [[LOADED_OLD_VAL]]
// CHECK: store [[LOADED_ARG1]] to [[ALLOC_STACK]]
// CHECK: [[LOADED_ARG1:%.*]] = load [[ARG1]]
// CHECK: release_value [[LOADED_ARG1]]
// CHECK: dealloc_stack [[ALLOC_STACK]]
// CHECK: } // end sil function 'large_struct_test'
sil @large_struct_test : $@convention(thin) (@owned LargeStruct, @in LargeStruct) -> () {
bb0(%0 : $LargeStruct, %1 : $*LargeStruct):
retain_value %0 : $LargeStruct
release_value %0 : $LargeStruct
%2 = alloc_stack $LargeStruct
copy_addr %1 to %2 : $*LargeStruct
destroy_addr %1 : $*LargeStruct
dealloc_stack %2 : $*LargeStruct
%9999 = tuple()
return %9999 : $()
}

// CHECK-LABEL: sil @small_struct_test : $@convention(thin) (@owned S2, @in S2) -> () {
// CHECK: bb0([[ARG0:%.*]] : $S2, [[ARG1:%.*]] : $*S2):
// CHECK: [[ARG0cls1:%.*]] = struct_extract [[ARG0]] : $S2, #S2.cls1
// CHECK: strong_retain [[ARG0cls1]] : $C1
// CHECK: [[ARG0cls2:%.*]] = struct_extract [[ARG0]] : $S2, #S2.cls2
// CHECK: strong_retain [[ARG0cls2]] : $C2
// CHECK: [[ARG0cls1:%.*]] = struct_extract [[ARG0]] : $S2, #S2.cls1
// CHECK: strong_release [[ARG0cls1]] : $C1
// CHECK: [[ARG0cls2:%.*]] = struct_extract [[ARG0]] : $S2, #S2.cls2
// CHECK: strong_release [[ARG0cls2]] : $C2
//
// CHECK: [[ALLOC_STACK:%.*]] = alloc_stack $S2
// CHECK: [[LOADED_ARG1:%.*]] = load [[ARG1]]
// CHECK: [[LOADED_OLDVALUE:%.*]] = load [[ALLOC_STACK]]
// CHECK: [[LOADED_ARG1cls1:%.*]] = struct_extract [[LOADED_ARG1]] : $S2, #S2.cls1
// CHECK: strong_retain [[LOADED_ARG1cls1]] : $C1
// CHECK: [[LOADED_ARG1cls2:%.*]] = struct_extract [[LOADED_ARG1]] : $S2, #S2.cls2
// CHECK: strong_retain [[LOADED_ARG1cls2]] : $C2
// CHECK: [[LOADED_OLDVALUEcls1:%.*]] = struct_extract [[LOADED_OLDVALUE]] : $S2, #S2.cls1
// CHECK: strong_release [[LOADED_OLDVALUEcls1]] : $C1
// CHECK: [[LOADED_OLDVALUEcls2:%.*]] = struct_extract [[LOADED_OLDVALUE]] : $S2, #S2.cls2
// CHECK: strong_release [[LOADED_OLDVALUEcls2]] : $C2
//
// CHECK: [[LOADED_ARG1:%.*]] = load [[ARG1]]
// CHECK: [[LOADED_ARG1cls1:%.*]] = struct_extract [[LOADED_ARG1]] : $S2, #S2.cls1
// CHECK: strong_release [[LOADED_ARG1cls1]] : $C1
// CHECK: [[LOADED_ARG1cls2:%.*]] = struct_extract [[LOADED_ARG1]] : $S2, #S2.cls2
// CHECK: strong_release [[LOADED_ARG1cls2]] : $C2
// CHECK: } // end sil function 'small_struct_test'
sil @small_struct_test : $@convention(thin) (@owned S2, @in S2) -> () {
bb0(%0 : $S2, %1 : $*S2):
retain_value %0 : $S2
release_value %0 : $S2
%2 = alloc_stack $S2
copy_addr %1 to %2 : $*S2
destroy_addr %1 : $*S2
dealloc_stack %2 : $*S2
%9999 = tuple()
return %9999 : $()
}
56 changes: 56 additions & 0 deletions test/SILOptimizer/mem2reg.sil
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,29 @@
import Builtin
import Swift

//////////////////
// Declarations //
//////////////////

class Klass {}

struct SmallCodesizeStruct {
var cls1 : Klass
var cls2 : Klass
}

struct LargeCodesizeStruct {
var s1: SmallCodesizeStruct
var s2: SmallCodesizeStruct
var s3: SmallCodesizeStruct
var s4: SmallCodesizeStruct
var s5: SmallCodesizeStruct
}

///////////
// Tests //
///////////

// CHECK-LABEL: sil @store_only_allocas
// CHECK-NOT: alloc_stack
// CHECK: return
Expand Down Expand Up @@ -356,3 +379,36 @@ bb0(%0 : $*Int64):
%4 = tuple ()
return %4 : $()
}

// Make sure that we do expand destroy_addr appropriately for code-size
// trade-offs.
// CHECK-LABEL: sil @large_struct_test : $@convention(thin) (@owned LargeCodesizeStruct) -> () {
// CHECK: bb0([[ARG0:%.*]] : $LargeCodesizeStruct):
// CHECK: release_value [[ARG0]]
// CHECK: } // end sil function 'large_struct_test'
sil @large_struct_test : $@convention(thin) (@owned LargeCodesizeStruct) -> () {
bb0(%0 : $LargeCodesizeStruct):
%1 = alloc_stack $LargeCodesizeStruct
store %0 to %1 : $*LargeCodesizeStruct
destroy_addr %1 : $*LargeCodesizeStruct
dealloc_stack %1 : $*LargeCodesizeStruct
%7 = tuple ()
return %7 : $()
}

// CHECK-LABEL: sil @small_struct_test : $@convention(thin) (@owned SmallCodesizeStruct) -> () {
// CHECK: bb0([[ARG0:%.*]] : $SmallCodesizeStruct):
// CHECK: [[ARG0cls1:%.*]] = struct_extract [[ARG0]]
// CHECK: strong_release [[ARG0cls1]]
// CHECK: [[ARG0cls2:%.*]] = struct_extract [[ARG0]]
// CHECK: strong_release [[ARG0cls2]]
// CHECK: } // end sil function 'small_struct_test'
sil @small_struct_test : $@convention(thin) (@owned SmallCodesizeStruct) -> () {
bb0(%0 : $SmallCodesizeStruct):
%1 = alloc_stack $SmallCodesizeStruct
store %0 to %1 : $*SmallCodesizeStruct
destroy_addr %1 : $*SmallCodesizeStruct
dealloc_stack %1 : $*SmallCodesizeStruct
%7 = tuple ()
return %7 : $()
}