mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
[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:
@@ -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
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -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 {
|
||||
|
||||
@@ -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() }
|
||||
|
||||
@@ -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 |
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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 }
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
|
||||
35
test/SILOptimizer/simplify_alloc_ref_dynamic.sil
Normal file
35
test/SILOptimizer/simplify_alloc_ref_dynamic.sil
Normal 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
|
||||
}
|
||||
72
test/embedded/managed-buffer2.swift
Normal file
72
test/embedded/managed-buffer2.swift
Normal 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
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user