-
Notifications
You must be signed in to change notification settings - Fork 30
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #214 from DougGregor/c-type-lowering
Introduce staged lowering of Swift to `@_cdecl` Swift to C
- Loading branch information
Showing
22 changed files
with
2,058 additions
and
742 deletions.
There are no files selected for viewing
195 changes: 195 additions & 0 deletions
195
Sources/JExtractSwift/CDeclLowering/CDeclConversions.swift
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,195 @@ | ||
//===----------------------------------------------------------------------===// | ||
// | ||
// This source file is part of the Swift.org open source project | ||
// | ||
// Copyright (c) 2025 Apple Inc. and the Swift.org project authors | ||
// Licensed under Apache License v2.0 | ||
// | ||
// See LICENSE.txt for license information | ||
// See CONTRIBUTORS.txt for the list of Swift.org project authors | ||
// | ||
// SPDX-License-Identifier: Apache-2.0 | ||
// | ||
//===----------------------------------------------------------------------===// | ||
|
||
extension ConversionStep { | ||
/// Produce a conversion that takes in a value (or set of values) that | ||
/// would be available in a @_cdecl function to represent the given Swift | ||
/// type, and convert that to an instance of the Swift type. | ||
init(cdeclToSwift swiftType: SwiftType) throws { | ||
// If there is a 1:1 mapping between this Swift type and a C type, then | ||
// there is no translation to do. | ||
if let cType = try? CType(cdeclType: swiftType) { | ||
_ = cType | ||
self = .placeholder | ||
return | ||
} | ||
|
||
switch swiftType { | ||
case .function, .optional: | ||
throw LoweringError.unhandledType(swiftType) | ||
|
||
case .metatype(let instanceType): | ||
self = .unsafeCastPointer( | ||
.placeholder, | ||
swiftType: instanceType | ||
) | ||
|
||
case .nominal(let nominal): | ||
if let knownType = nominal.nominalTypeDecl.knownStandardLibraryType { | ||
// Typed pointers | ||
if let firstGenericArgument = nominal.genericArguments?.first { | ||
switch knownType { | ||
case .unsafePointer, .unsafeMutablePointer: | ||
self = .typedPointer( | ||
.explodedComponent(.placeholder, component: "pointer"), | ||
swiftType: firstGenericArgument | ||
) | ||
return | ||
|
||
case .unsafeBufferPointer, .unsafeMutableBufferPointer: | ||
self = .initialize( | ||
swiftType, | ||
arguments: [ | ||
LabeledArgument( | ||
label: "start", | ||
argument: .typedPointer( | ||
.explodedComponent(.placeholder, component: "pointer"), | ||
swiftType: firstGenericArgument) | ||
), | ||
LabeledArgument( | ||
label: "count", | ||
argument: .explodedComponent(.placeholder, component: "count") | ||
) | ||
] | ||
) | ||
return | ||
|
||
default: | ||
break | ||
} | ||
} | ||
} | ||
|
||
// Arbitrary nominal types. | ||
switch nominal.nominalTypeDecl.kind { | ||
case .actor, .class: | ||
// For actor and class, we pass around the pointer directly. | ||
self = .unsafeCastPointer(.placeholder, swiftType: swiftType) | ||
case .enum, .struct, .protocol: | ||
// For enums, structs, and protocol types, we pass around the | ||
// values indirectly. | ||
self = .passIndirectly( | ||
.pointee(.typedPointer(.placeholder, swiftType: swiftType)) | ||
) | ||
} | ||
|
||
case .tuple(let elements): | ||
self = .tuplify(try elements.map { try ConversionStep(cdeclToSwift: $0) }) | ||
} | ||
} | ||
|
||
/// Produce a conversion that takes in a value that would be available in a | ||
/// Swift function and convert that to the corresponding cdecl values. | ||
/// | ||
/// This conversion goes in the opposite direction of init(cdeclToSwift:), and | ||
/// is used for (e.g.) returning the Swift value from a cdecl function. When | ||
/// there are multiple cdecl values that correspond to this one Swift value, | ||
/// the result will be a tuple that can be assigned to a tuple of the cdecl | ||
/// values, e.g., (<placeholder>.baseAddress, <placeholder>.count). | ||
init( | ||
swiftToCDecl swiftType: SwiftType, | ||
stdlibTypes: SwiftStandardLibraryTypes | ||
) throws { | ||
// If there is a 1:1 mapping between this Swift type and a C type, then | ||
// there is no translation to do. | ||
if let cType = try? CType(cdeclType: swiftType) { | ||
_ = cType | ||
self = .placeholder | ||
return | ||
} | ||
|
||
switch swiftType { | ||
case .function, .optional: | ||
throw LoweringError.unhandledType(swiftType) | ||
|
||
case .metatype: | ||
self = .unsafeCastPointer( | ||
.placeholder, | ||
swiftType: .nominal( | ||
SwiftNominalType( | ||
nominalTypeDecl: stdlibTypes[.unsafeRawPointer] | ||
) | ||
) | ||
) | ||
return | ||
|
||
case .nominal(let nominal): | ||
if let knownType = nominal.nominalTypeDecl.knownStandardLibraryType { | ||
// Typed pointers | ||
if nominal.genericArguments?.first != nil { | ||
switch knownType { | ||
case .unsafePointer, .unsafeMutablePointer: | ||
let isMutable = knownType == .unsafeMutablePointer | ||
self = ConversionStep( | ||
initializeRawPointerFromTyped: .placeholder, | ||
isMutable: isMutable, | ||
isPartOfBufferPointer: false, | ||
stdlibTypes: stdlibTypes | ||
) | ||
return | ||
|
||
case .unsafeBufferPointer, .unsafeMutableBufferPointer: | ||
let isMutable = knownType == .unsafeMutableBufferPointer | ||
self = .tuplify( | ||
[ | ||
ConversionStep( | ||
initializeRawPointerFromTyped: .explodedComponent( | ||
.placeholder, | ||
component: "pointer" | ||
), | ||
isMutable: isMutable, | ||
isPartOfBufferPointer: true, | ||
stdlibTypes: stdlibTypes | ||
), | ||
.explodedComponent(.placeholder, component: "count") | ||
] | ||
) | ||
return | ||
|
||
default: | ||
break | ||
} | ||
} | ||
} | ||
|
||
// Arbitrary nominal types. | ||
switch nominal.nominalTypeDecl.kind { | ||
case .actor, .class: | ||
// For actor and class, we pass around the pointer directly. Case to | ||
// the unsafe raw pointer type we use to represent it in C. | ||
self = .unsafeCastPointer( | ||
.placeholder, | ||
swiftType: .nominal( | ||
SwiftNominalType( | ||
nominalTypeDecl: stdlibTypes[.unsafeRawPointer] | ||
) | ||
) | ||
) | ||
|
||
case .enum, .struct, .protocol: | ||
// For enums, structs, and protocol types, we leave the value alone. | ||
// The indirection will be handled by the caller. | ||
self = .placeholder | ||
} | ||
|
||
case .tuple(let elements): | ||
// Convert all of the elements. | ||
self = .tuplify( | ||
try elements.map { element in | ||
try ConversionStep(swiftToCDecl: element, stdlibTypes: stdlibTypes) | ||
} | ||
) | ||
} | ||
} | ||
} |
116 changes: 116 additions & 0 deletions
116
Sources/JExtractSwift/CDeclLowering/CRepresentation.swift
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,116 @@ | ||
//===----------------------------------------------------------------------===// | ||
// | ||
// This source file is part of the Swift.org open source project | ||
// | ||
// Copyright (c) 2025 Apple Inc. and the Swift.org project authors | ||
// Licensed under Apache License v2.0 | ||
// | ||
// See LICENSE.txt for license information | ||
// See CONTRIBUTORS.txt for the list of Swift.org project authors | ||
// | ||
// SPDX-License-Identifier: Apache-2.0 | ||
// | ||
//===----------------------------------------------------------------------===// | ||
|
||
extension CType { | ||
/// Lower the given Swift type down to a its corresponding C type. | ||
/// | ||
/// This operation only supports the subset of Swift types that are | ||
/// representable in a Swift `@_cdecl` function, which means that they are | ||
/// also directly representable in C. If lowering an arbitrary Swift | ||
/// function, first go through Swift -> cdecl lowering. This function | ||
/// will throw an error if it encounters a type that is not expressible in | ||
/// C. | ||
init(cdeclType: SwiftType) throws { | ||
switch cdeclType { | ||
case .nominal(let nominalType): | ||
if let knownType = nominalType.nominalTypeDecl.knownStandardLibraryType, | ||
let primitiveCType = knownType.primitiveCType { | ||
self = primitiveCType | ||
return | ||
} | ||
|
||
throw CDeclToCLoweringError.invalidNominalType(nominalType.nominalTypeDecl) | ||
|
||
case .function(let functionType): | ||
switch functionType.convention { | ||
case .swift: | ||
throw CDeclToCLoweringError.invalidFunctionConvention(functionType) | ||
|
||
case .c: | ||
let resultType = try CType(cdeclType: functionType.resultType) | ||
let parameterTypes = try functionType.parameters.map { param in | ||
try CType(cdeclType: param.type) | ||
} | ||
|
||
self = .function( | ||
resultType: resultType, | ||
parameters: parameterTypes, | ||
variadic: false | ||
) | ||
} | ||
|
||
case .tuple([]): | ||
self = .void | ||
|
||
case .metatype, .optional, .tuple: | ||
throw CDeclToCLoweringError.invalidCDeclType(cdeclType) | ||
} | ||
} | ||
} | ||
|
||
extension CFunction { | ||
/// Produce a C function that represents the given @_cdecl Swift function. | ||
init(cdeclSignature: SwiftFunctionSignature, cName: String) throws { | ||
assert(cdeclSignature.selfParameter == nil) | ||
|
||
let cResultType = try CType(cdeclType: cdeclSignature.result.type) | ||
let cParameters = try cdeclSignature.parameters.map { parameter in | ||
CParameter( | ||
name: parameter.parameterName, | ||
type: try CType(cdeclType: parameter.type).parameterDecay | ||
) | ||
} | ||
|
||
self = CFunction( | ||
resultType: cResultType, | ||
name: cName, | ||
parameters: cParameters, | ||
isVariadic: false | ||
) | ||
} | ||
} | ||
|
||
enum CDeclToCLoweringError: Error { | ||
case invalidCDeclType(SwiftType) | ||
case invalidNominalType(SwiftNominalTypeDeclaration) | ||
case invalidFunctionConvention(SwiftFunctionType) | ||
} | ||
|
||
extension KnownStandardLibraryType { | ||
/// Determine the primitive C type that corresponds to this C standard | ||
/// library type, if there is one. | ||
var primitiveCType: CType? { | ||
switch self { | ||
case .bool: .integral(.bool) | ||
case .int: .integral(.ptrdiff_t) | ||
case .uint: .integral(.size_t) | ||
case .int8: .integral(.signed(bits: 8)) | ||
case .uint8: .integral(.unsigned(bits: 8)) | ||
case .int16: .integral(.signed(bits: 16)) | ||
case .uint16: .integral(.unsigned(bits: 16)) | ||
case .int32: .integral(.signed(bits: 32)) | ||
case .uint32: .integral(.unsigned(bits: 32)) | ||
case .int64: .integral(.signed(bits: 64)) | ||
case .uint64: .integral(.unsigned(bits: 64)) | ||
case .float: .floating(.float) | ||
case .double: .floating(.double) | ||
case .unsafeMutableRawPointer: .pointer(.void) | ||
case .unsafeRawPointer: .pointer( | ||
.qualified(const: true, volatile: false, type: .void) | ||
) | ||
case .unsafePointer, .unsafeMutablePointer, .unsafeBufferPointer, .unsafeMutableBufferPointer: | ||
nil | ||
} | ||
} | ||
} |
Oops, something went wrong.