[embedded] Add support for ManagedBuffer to Embedded Swift

This makes ManagedBuffer available and usable in Embedded Swift, by:

- Removing an internal consistency check from ManagedBuffer that relies on metatypes.
- Making the .create() API transparent (to hoist the metatype to the callee).
- Adding a AllocRefDynamicInst simplification to convert `alloc_ref_dynamic` to `alloc_ref`, which removes a metatype use.
- Adding tests for the above.
This commit is contained in:
Kuba Mracek
2024-09-23 13:28:04 -07:00
parent a6c5e6274d
commit c7a5569c4f
11 changed files with 207 additions and 1 deletions

View File

@@ -7,6 +7,7 @@
# See http://swift.org/CONTRIBUTORS.txt for Swift project authors
swift_compiler_sources(Optimizer
SimplifyAllocRefDynamic.swift
SimplifyApply.swift
SimplifyBeginBorrow.swift
SimplifyBeginCOWMutation.swift

View File

@@ -0,0 +1,45 @@
//===--- SimplifyAllocRefDynamic.swift ------------------------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2023 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
import SIL
extension AllocRefDynamicInst : OnoneSimplifyable {
func simplify(_ context: SimplifyContext) {
/// Optimize alloc_ref_dynamic of a known type to alloc_ref:
///
/// %3 = metatype SubClass.Type
/// %4 = upcast %3 : SubClass.Type to BaseClass.Type
/// %6 = alloc_ref_dynamic [...] %4 : BaseClass.Type, $BaseClass
/// %8 = (... some use of ...) %6 : $BaseClass
/// ->
/// %6 = alloc_ref [...] $SubClass
/// %7 = upcast %6 : $SubClass to $BaseClass
/// %8 = (... some use of ...) %7 : $BaseClass
let type: Type
if let metatypeInst = operands[1].value as? MetatypeInst {
type = metatypeInst.type.loweredInstanceTypeOfMetatype(in: parentFunction)
} else if let upcastInst = operands[1].value as? UpcastInst,
let metatypeInst = upcastInst.operands[0].value as? MetatypeInst {
type = metatypeInst.type.loweredInstanceTypeOfMetatype(in: parentFunction)
} else {
return
}
let builder = Builder(before: self, context)
let newAlloc = builder.createAllocRef(type, isObjC: self.isObjC, canAllocOnStack: self.canAllocOnStack, isBare: false,
tailAllocatedTypes: self.tailAllocatedTypes, tailAllocatedCounts: Array(self.tailAllocatedCounts.values))
let newCast = builder.createUpcast(from: newAlloc, to: self.type)
uses.replaceAll(with: newCast, context)
context.erase(instruction: self)
}
}

View File

@@ -90,6 +90,14 @@ public struct Builder {
return notifyNew(literal.getAs(IntegerLiteralInst.self))
}
public func createAllocRef(_ type: Type, isObjC: Bool = false, canAllocOnStack: Bool = false, isBare: Bool = false,
tailAllocatedTypes: TypeArray, tailAllocatedCounts: [Value]) -> AllocRefInst {
return tailAllocatedCounts.withBridgedValues { countsRef in
let dr = bridged.createAllocRef(type.bridged, isObjC, canAllocOnStack, isBare, tailAllocatedTypes.bridged, countsRef)
return notifyNew(dr.getAs(AllocRefInst.self))
}
}
public func createAllocStack(_ type: Type, hasDynamicLifetime: Bool = false,
isLexical: Bool = false, isFromVarDecl: Bool = false,
usesMoveableValueDebugInfo: Bool = false) -> AllocStackInst {

View File

@@ -207,7 +207,7 @@ extension Type: Equatable {
}
public struct TypeArray : RandomAccessCollection, CustomReflectable {
private let bridged: BridgedSILTypeArray
public let bridged: BridgedSILTypeArray
public var startIndex: Int { return 0 }
public var endIndex: Int { return bridged.getCount() }

View File

@@ -43,6 +43,7 @@ This status table describes which of the following standard library features can
| Integer parsing | No |
| KeyPaths | Partial (only compile-time constant key paths to stored properties supported, only usable in MemoryLayout and UnsafePointer APIs) |
| Lazy collections | No |
| ManagedBuffer | Yes |
| Mirror (runtime reflection) | No, intentionally unsupported long-term |
| Objective-C bridging | No, intentionally unsupported long-term |
| Optional | Yes |

View File

@@ -804,9 +804,19 @@ struct BridgedSILTypeArray {
BridgedSILTypeArray(llvm::ArrayRef<swift::SILType> silTypes)
: typeArray(silTypes) {}
BridgedSILTypeArray(BridgedArrayRef typeArray)
: typeArray(typeArray) {}
llvm::ArrayRef<swift::SILType> unbridged() const {
return typeArray.unbridged<swift::SILType>();
}
llvm::ArrayRef<swift::SILType> getValues(llvm::SmallVectorImpl<swift::SILType> &storage) {
for (unsigned idx = 0; idx < getCount(); ++idx) {
storage.push_back(unbridged()[idx]);
}
return storage;
}
#endif
SwiftInt getCount() const { return SwiftInt(typeArray.Length); }
@@ -1254,6 +1264,11 @@ struct BridgedBuilder{
SWIFT_IMPORT_UNSAFE BRIDGED_INLINE BridgedInstruction createCondFail(BridgedValue condition,
BridgedStringRef message) const;
SWIFT_IMPORT_UNSAFE BRIDGED_INLINE BridgedInstruction createIntegerLiteral(BridgedType type, SwiftInt value) const;
SWIFT_IMPORT_UNSAFE BRIDGED_INLINE BridgedInstruction createAllocRef(BridgedType type,
bool objc, bool canAllocOnStack, bool isBare,
BridgedSILTypeArray elementTypes, BridgedValueArray elementCountOperands) const;
SWIFT_IMPORT_UNSAFE BRIDGED_INLINE BridgedInstruction
createAllocStack(BridgedType type, bool hasDynamicLifetime, bool isLexical,
bool isFromVarDecl, bool wasMoved) const;

View File

@@ -1628,6 +1628,18 @@ BridgedInstruction BridgedBuilder::createIntegerLiteral(BridgedType type, SwiftI
unbridged().createIntegerLiteral(regularLoc(), type.unbridged(), value)};
}
BridgedInstruction BridgedBuilder::createAllocRef(BridgedType type,
bool objc, bool canAllocOnStack, bool isBare,
BridgedSILTypeArray elementTypes, BridgedValueArray elementCountOperands) const {
llvm::SmallVector<swift::SILType, 16> elementTypesValues;
llvm::SmallVector<swift::SILValue, 16> elementCountOperandsValues;
return {unbridged().createAllocRef(
regularLoc(), type.unbridged(), objc, canAllocOnStack, isBare,
elementTypes.getValues(elementTypesValues),
elementCountOperands.getValues(elementCountOperandsValues)
)};
}
BridgedInstruction BridgedBuilder::createAllocStack(BridgedType type,
bool hasDynamicLifetime,
bool isLexical,

View File

@@ -206,6 +206,15 @@ public func swift_dynamicCastClass(object: UnsafeMutableRawPointer, targetMetada
return object
}
@_cdecl("swift_dynamicCastClassUnconditional")
public func swift_dynamicCastClassUnconditional(object: UnsafeMutableRawPointer, targetMetadata: UnsafeRawPointer,
file: UnsafePointer<CChar>, line: CUnsignedInt, column: CUnsignedInt) -> UnsafeMutableRawPointer {
guard let result = swift_dynamicCastClass(object: object, targetMetadata: targetMetadata) else {
Builtin.int_trap()
}
return result
}
@_cdecl("swift_isUniquelyReferenced_native")
public func swift_isUniquelyReferenced_native(object: Builtin.RawPointer) -> Bool {
if !isValidPointerForNativeRetain(object: object) { return false }

View File

@@ -77,6 +77,9 @@ extension ManagedBuffer where Element: ~Copyable {
/// an initial `Header`.
@_preInverseGenerics
@inlinable
#if $Embedded
@_transparent
#endif
public final class func create(
minimumCapacity: Int,
makingHeaderWith factory: (ManagedBuffer<Header, Element>) throws -> Header
@@ -290,7 +293,9 @@ public struct ManagedBufferPointer<
@_preInverseGenerics
@inlinable
public init(unsafeBufferObject buffer: AnyObject) {
#if !$Embedded
ManagedBufferPointer._checkValidBufferClass(type(of: buffer))
#endif
self._nativeBuffer = Builtin.unsafeCastToNativeObject(buffer)
}
@@ -308,7 +313,10 @@ public struct ManagedBufferPointer<
@_preInverseGenerics
@inlinable
internal init(_uncheckedUnsafeBufferObject buffer: AnyObject) {
#if !$Embedded
ManagedBufferPointer._internalInvariantValidBufferClass(type(of: buffer))
#endif
self._nativeBuffer = Builtin.unsafeCastToNativeObject(buffer)
}

View File

@@ -0,0 +1,35 @@
// RUN: %target-sil-opt -enable-sil-verify-all %s -onone-simplification -simplify-instruction=alloc_ref_dynamic | %FileCheck %s
// REQUIRES: swift_in_compiler
import Swift
import Builtin
class BaseClass {
init()
}
class SubClass: BaseClass {
override init()
}
// CHECK-LABEL: sil [ossa] @test
// CHECK: bb0(%0 : $Int):
// CHECK: struct_extract %0 : $Int, #Int._value
// CHECK: builtin "truncOrBitCast_Int64_Word"({{.*}} : $Builtin.Int64) : $Builtin.Word
// CHECK: [[AL:%.*]] = alloc_ref [tail_elems $Int * {{.*}} : $Builtin.Word] $SubClass
// CHECK: [[UP:%.*]] = upcast [[AL]] : $SubClass to $BaseClass
// CHECK: [[MO:%.*]] = move_value [lexical] [var_decl] [[UP]] : $BaseClass
// CHECK: return [[MO]] : $BaseClass
// CHECK: } // end sil function 'test'
sil [ossa] @test : $@convention(thin) (Int) -> @owned BaseClass {
// %0 "minimumCapacity"
bb0(%0 : $Int):
%4 = metatype $@thick SubClass.Type
%5 = upcast %4 : $@thick SubClass.Type to $@thick BaseClass.Type
%10 = struct_extract %0 : $Int, #Int._value
%11 = builtin "truncOrBitCast_Int64_Word"(%10 : $Builtin.Int64) : $Builtin.Word
%12 = alloc_ref_dynamic [tail_elems $Int * %11 : $Builtin.Word] %5 : $@thick BaseClass.Type, $BaseClass
%13 = move_value [lexical] [var_decl] %12 : $BaseClass
return %13 : $BaseClass
}

View File

@@ -0,0 +1,72 @@
// RUN: %target-run-simple-swift(-enable-experimental-feature Embedded -parse-as-library -wmo) | %FileCheck %s
// RUN: %target-run-simple-swift(-enable-experimental-feature Embedded -parse-as-library -wmo -O) | %FileCheck %s
// RUN: %target-run-simple-swift(-enable-experimental-feature Embedded -parse-as-library -wmo -Osize) | %FileCheck %s
// REQUIRES: swift_in_compiler
// REQUIRES: executable_test
// REQUIRES: optimized_stdlib
// REQUIRES: OS=macosx || OS=linux-gnu
struct CountAndCapacity {
var count: Int
let capacity: Int
}
final class TestManagedBuffer<T> : ManagedBuffer<CountAndCapacity, T> {
class func create(_ capacity: Int) -> TestManagedBuffer {
let r = super.create(minimumCapacity: capacity) {
CountAndCapacity(count: 0, capacity: $0.capacity)
}
return r as! TestManagedBuffer
}
var count: Int {
get { return header.count }
set { header.count = newValue }
}
var myCapacity: Int {
return header.capacity
}
deinit {
teardown()
}
func teardown() {
let count = self.count
withUnsafeMutablePointerToElements { (x: UnsafeMutablePointer<T>) -> () in
for i in stride(from: 0, to: count, by: 2) {
(x + i).deinitialize(count: 1)
}
}
}
func append(_ x: T) {
let count = self.count
precondition(count + 1 <= myCapacity)
withUnsafeMutablePointerToElements { (p: UnsafeMutablePointer<T>) -> () in
(p + count).initialize(to: x)
}
self.count = count + 1
}
}
@main
struct Main {
static func main() {
let s = TestManagedBuffer<Int>.create(10)
s.append(42)
s.append(777)
s.withUnsafeMutablePointerToElements {
for i in 0 ..< s.count {
print($0[i])
}
}
// CHECK: 42
// CHECK: 777
}
}