Skip to content

[interop][SwiftToCxx] emit swift type metadata access function declar… #59406

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 1 commit into from
Jun 13, 2022
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
26 changes: 26 additions & 0 deletions include/swift/IRGen/IRABIDetailsProvider.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
#include "clang/AST/CharUnits.h"
#include "llvm/ADT/Optional.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/SmallVector.h"
#include <cstdint>
#include <memory>
#include <utility>
Expand Down Expand Up @@ -67,6 +68,31 @@ class IRABIDetailsProvider {
Type t, llvm::function_ref<void(clang::CharUnits, clang::CharUnits, Type)>
callback);

/// An representation of a single type, or a C struct with multiple members
/// with specified types. The C struct is expected to be passed via swiftcc
/// functions.
class TypeRecordABIRepresentation {
public:
ArrayRef<Type> getMembers() const { return members; }

using MemberVectorTy = SmallVector<Type, 4>;

private:
friend class IRABIDetailsProviderImpl;
TypeRecordABIRepresentation(MemberVectorTy members) : members(members) {}

MemberVectorTy members;
};

struct FunctionABISignature {
TypeRecordABIRepresentation returnType;
SmallVector<TypeRecordABIRepresentation, 4> parameterTypes;
};

/// Returns the function signature that is used for the the type metadata
/// access function.
FunctionABISignature getTypeMetadataAccessFunctionSignature();

private:
std::unique_ptr<IRABIDetailsProviderImpl> impl;
};
Expand Down
20 changes: 20 additions & 0 deletions lib/IRGen/IRABIDetailsProvider.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,21 @@ class IRABIDetailsProviderImpl {
return hasError;
}

IRABIDetailsProvider::FunctionABISignature
getTypeMetadataAccessFunctionSignature() {
auto &ctx = IGM.getSwiftModule()->getASTContext();
llvm::StructType *responseTy = IGM.getTypeMetadataResponseTy();
IRABIDetailsProvider::TypeRecordABIRepresentation::MemberVectorTy members;
for (auto *elementTy : responseTy->elements())
members.push_back(*getPrimitiveTypeFromLLVMType(ctx, elementTy));
auto returnTy =
IRABIDetailsProvider::TypeRecordABIRepresentation(std::move(members));
auto paramTy = IRABIDetailsProvider::TypeRecordABIRepresentation(
{*getPrimitiveTypeFromLLVMType(ctx,
IGM.getTypeMetadataRequestParamTy())});
return {returnTy, {paramTy}};
}

private:
Lowering::TypeConverter typeConverter;
// Default silOptions are sufficient, as we don't need to generated SIL.
Expand Down Expand Up @@ -142,3 +157,8 @@ bool IRABIDetailsProvider::enumerateDirectPassingRecordMembers(
callback) {
return impl->enumerateDirectPassingRecordMembers(t, callback);
}

IRABIDetailsProvider::FunctionABISignature
IRABIDetailsProvider::getTypeMetadataAccessFunctionSignature() {
return impl->getTypeMetadataAccessFunctionSignature();
}
1 change: 1 addition & 0 deletions lib/PrintAsClang/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ add_swift_host_library(swiftPrintAsClang STATIC
PrintAsClang.cpp
PrintClangFunction.cpp
PrintClangValueType.cpp
PrintSwiftToClangCoreScaffold.cpp
SwiftToClangInteropContext.cpp)
target_link_libraries(swiftPrintAsClang PRIVATE
swiftAST
Expand Down
15 changes: 15 additions & 0 deletions lib/PrintAsClang/ClangSyntaxPrinter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,21 @@ void ClangSyntaxPrinter::printNamespace(
printNamespace([&](raw_ostream &os) { os << name; }, bodyPrinter);
}

void ClangSyntaxPrinter::printExternC(
llvm::function_ref<void(raw_ostream &OS)> bodyPrinter) const {
os << "#ifdef __cplusplus\n";
os << "extern \"C\" {\n";
os << "#endif\n\n";
bodyPrinter(os);
os << "\n#ifdef __cplusplus\n";
os << "}\n";
os << "#endif\n";
}

void ClangSyntaxPrinter::printSwiftImplQualifier() const {
os << "swift::" << cxx_synthesis::getCxxImplNamespaceName() << "::";
}

void ClangSyntaxPrinter::printNullability(
Optional<OptionalTypeKind> kind, NullabilityPrintKind printKind) const {
if (!kind)
Expand Down
7 changes: 7 additions & 0 deletions lib/PrintAsClang/ClangSyntaxPrinter.h
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,13 @@ class ClangSyntaxPrinter {
printNamespace(StringRef name,
llvm::function_ref<void(raw_ostream &OS)> bodyPrinter) const;

/// Print an extern C block with given body.
void
printExternC(llvm::function_ref<void(raw_ostream &OS)> bodyPrinter) const;

/// Print the `swift::_impl::` namespace qualifier.
void printSwiftImplQualifier() const;

/// Where nullability information should be printed.
enum class NullabilityPrintKind {
Before,
Expand Down
14 changes: 11 additions & 3 deletions lib/PrintAsClang/ModuleContentsWriter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
#include "DeclAndTypePrinter.h"
#include "OutputLanguageMode.h"
#include "PrimitiveTypeMapping.h"
#include "PrintSwiftToClangCoreScaffold.h"

#include "swift/AST/ExistentialLayout.h"
#include "swift/AST/Module.h"
Expand Down Expand Up @@ -138,6 +139,8 @@ class ModuleWriter {
access, outputLang),
outputLangMode(outputLang) {}

PrimitiveTypeMapping &getTypeMapping() { return typeMapping; }

/// Returns true if we added the decl's module to the import set, false if
/// the decl is a local decl.
///
Expand Down Expand Up @@ -659,9 +662,14 @@ void swift::printModuleContentsAsCxx(
std::string modulePrologueBuf;
llvm::raw_string_ostream prologueOS{modulePrologueBuf};

ModuleWriter(moduleOS, prologueOS, imports, M, interopContext,
getRequiredAccess(M), OutputLanguageMode::Cxx)
.write();
ModuleWriter writer(moduleOS, prologueOS, imports, M, interopContext,
getRequiredAccess(M), OutputLanguageMode::Cxx);
writer.write();

os << "#ifndef SWIFT_PRINTED_CORE\n";
os << "#define SWIFT_PRINTED_CORE\n";
printSwiftToClangCoreScaffold(interopContext, writer.getTypeMapping(), os);
os << "#endif\n";

// FIXME: refactor.
if (!prologueOS.str().empty()) {
Expand Down
23 changes: 22 additions & 1 deletion lib/PrintAsClang/PrintClangValueType.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
#include "swift/AST/TypeVisitor.h"
#include "swift/ClangImporter/ClangImporter.h"
#include "swift/IRGen/IRABIDetailsProvider.h"
#include "swift/IRGen/Linking.h"
#include "llvm/ADT/STLExtras.h"

using namespace swift;
Expand Down Expand Up @@ -59,6 +60,22 @@ printCValueTypeStorageStruct(raw_ostream &os, const NominalTypeDecl *typeDecl,
os << "};\n\n";
}

void printCTypeMetadataTypeFunction(raw_ostream &os,
const NominalTypeDecl *typeDecl) {
os << "// Type metadata accessor for " << typeDecl->getNameStr() << "\n";
auto entity = irgen::LinkEntity::forTypeMetadataAccessFunction(
typeDecl->getDeclaredType()->getCanonicalType());
os << "SWIFT_EXTERN ";
ClangSyntaxPrinter printer(os);
printer.printSwiftImplQualifier();
os << "MetadataResponseTy ";
entity.mangle(os);
os << '(';
printer.printSwiftImplQualifier();
os << "MetadataRequestTy)";
os << " SWIFT_NOEXCEPT SWIFT_CALL;\n\n";
}

void ClangValueTypePrinter::printStructDecl(const StructDecl *SD) {
auto typeSizeAlign =
interopContext.getIrABIDetails().getTypeSizeAlignment(SD);
Expand All @@ -78,7 +95,11 @@ void ClangValueTypePrinter::printStructDecl(const StructDecl *SD) {
[&](raw_ostream &os) {
os << "class ";
printCxxImplClassName(os, SD);
os << ";\n";
os << ";\n\n";

// Print out special functions, like functions that
// access type metadata.
printCTypeMetadataTypeFunction(os, SD);
});

// Print out the C++ class itself.
Expand Down
88 changes: 88 additions & 0 deletions lib/PrintAsClang/PrintSwiftToClangCoreScaffold.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
//===--- PrintSwiftToClangCoreScaffold.cpp - Print core decls ---*- C++ -*-===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2022 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
//
//===----------------------------------------------------------------------===//

#include "PrintSwiftToClangCoreScaffold.h"
#include "ClangSyntaxPrinter.h"
#include "PrimitiveTypeMapping.h"
#include "SwiftToClangInteropContext.h"
#include "swift/AST/Decl.h"
#include "swift/AST/Type.h"
#include "swift/IRGen/IRABIDetailsProvider.h"

using namespace swift;

static void printKnownCType(Type t, PrimitiveTypeMapping &typeMapping,
raw_ostream &os) {
auto info =
typeMapping.getKnownCTypeInfo(t->getNominalOrBoundGenericNominal());
assert(info.hasValue() && "not a known type");
os << info->name;
if (info->canBeNullable)
os << " _Null_unspecified";
}

static void printKnownStruct(
PrimitiveTypeMapping &typeMapping, raw_ostream &os, StringRef name,
const IRABIDetailsProvider::TypeRecordABIRepresentation &typeRecord) {
assert(typeRecord.getMembers().size() > 1);
os << "struct " << name << " {\n";
for (const auto &ty : llvm::enumerate(typeRecord.getMembers())) {
os << " ";
printKnownCType(ty.value(), typeMapping, os);
os << " _" << ty.index() << ";\n";
}
os << "};\n";
}

static void printKnownTypedef(
PrimitiveTypeMapping &typeMapping, raw_ostream &os, StringRef name,
const IRABIDetailsProvider::TypeRecordABIRepresentation &typeRecord) {
assert(typeRecord.getMembers().size() == 1);
os << "typedef ";
printKnownCType(typeRecord.getMembers()[0], typeMapping, os);
os << " " << name << ";\n";
}

static void printKnownType(
PrimitiveTypeMapping &typeMapping, raw_ostream &os, StringRef name,
const IRABIDetailsProvider::TypeRecordABIRepresentation &typeRecord) {
if (typeRecord.getMembers().size() == 1)
return printKnownTypedef(typeMapping, os, name, typeRecord);
printKnownStruct(typeMapping, os, name, typeRecord);
}

static void printTypeMetadataResponseType(SwiftToClangInteropContext &ctx,
PrimitiveTypeMapping &typeMapping,
raw_ostream &os) {
os << "// Swift type metadata response type.\n";
// Print out the type metadata structure.
auto funcSig = ctx.getIrABIDetails().getTypeMetadataAccessFunctionSignature();
printKnownType(typeMapping, os, "MetadataResponseTy", funcSig.returnType);
assert(funcSig.parameterTypes.size() == 1);
os << "// Swift type metadata request type.\n";
printKnownType(typeMapping, os, "MetadataRequestTy",
funcSig.parameterTypes[0]);
}

void swift::printSwiftToClangCoreScaffold(SwiftToClangInteropContext &ctx,
PrimitiveTypeMapping &typeMapping,
raw_ostream &os) {
ClangSyntaxPrinter printer(os);
printer.printNamespace("swift", [&](raw_ostream &) {
printer.printNamespace(
cxx_synthesis::getCxxImplNamespaceName(), [&](raw_ostream &) {
printer.printExternC([&](raw_ostream &os) {
printTypeMetadataResponseType(ctx, typeMapping, os);
});
});
});
}
31 changes: 31 additions & 0 deletions lib/PrintAsClang/PrintSwiftToClangCoreScaffold.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
//===--- PrintSwiftToClangCoreScaffold.h - Print core decls -----*- C++ -*-===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2022 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
//
//===----------------------------------------------------------------------===//

#ifndef SWIFT_PRINTASCLANG_PRINTSWIFTTOCLANGCORESCAFFOLD_H
#define SWIFT_PRINTASCLANG_PRINTSWIFTTOCLANGCORESCAFFOLD_H

#include "llvm/Support/raw_ostream.h"

namespace swift {

class PrimitiveTypeMapping;
class SwiftToClangInteropContext;

/// Print out the core declarations required by C/C++ that are part of the core
/// Swift stdlib code.
void printSwiftToClangCoreScaffold(SwiftToClangInteropContext &ctx,
PrimitiveTypeMapping &typeMapping,
llvm::raw_ostream &os);

} // end namespace swift

#endif
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// RUN: %empty-directory(%t)
// RUN: %target-swift-frontend %s -typecheck -module-name Core -clang-header-expose-public-decls -emit-clang-header-path %t/core.h
// RUN: %FileCheck %s < %t/core.h

// RUN: %check-interop-cxx-header-in-clang(%t/core.h)

// REQUIRES: PTRSIZE=64

// CHECK: namespace swift {

// CHECK: namespace _impl {

// CHECK: // Swift type metadata response type.
// CHECK-NEXT: struct MetadataResponseTy {
// CHECK-NEXT: void * _Null_unspecified _0;
// CHECK-NEXT: uint64_t _1;
// CHECK-NEXT: };
// CHECK-NEXT: // Swift type metadata request type.
// CHECK-NEXT: typedef uint64_t MetadataRequestTy;
35 changes: 35 additions & 0 deletions test/Interop/SwiftToCxx/core/swift-impl-defs-in-cxx.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
// RUN: %empty-directory(%t)
// RUN: %target-swift-frontend %s -typecheck -module-name Core -clang-header-expose-public-decls -emit-clang-header-path %t/core.h
// RUN: %FileCheck %s < %t/core.h

// RUN: %check-interop-cxx-header-in-clang(%t/core.h)


// CHECK: #ifndef SWIFT_PRINTED_CORE
// CHECK-NEXT: #define SWIFT_PRINTED_CORE
// CHECK-NEXT: namespace swift {
// CHECK-EMPTY:
// CHECK-NEXT: namespace _impl {
// CHECK-EMPTY:
// CHECK-NEXT: #ifdef __cplusplus
// CHECK-NEXT: extern "C" {
// CHECK-NEXT: #endif
// CHECK-EMPTY:
// CHECK-NEXT: // Swift type metadata response type.
// CHECK-NEXT: struct MetadataResponseTy {
// CHECK-NEXT: void * _Null_unspecified _0;
// CHECK-NEXT: uint{{.*}}_t _1;
// CHECK-NEXT: };
// CHECK-NEXT: // Swift type metadata request type.
// CHECK-NEXT: typedef uint{{.*}}_t MetadataRequestTy;
// CHECK-EMPTY:
// CHECK-NEXT: #ifdef __cplusplus
// CHECK-NEXT: }
// CHECK-NEXT: #endif
// CHECK-EMPTY:
// CHECK-NEXT: } // namespace _impl
// CHECK-EMPTY:
// CHECK-EMPTY:
// CHECK-NEXT: } // namespace swift
// CHECK-EMPTY:
// CHECK-NEXT: #endif
4 changes: 4 additions & 0 deletions test/Interop/SwiftToCxx/structs/swift-struct-in-cxx.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@
// CHECK-EMPTY:
// CHECK-NEXT: class _impl_StructWithIntField;
// CHECK-EMPTY:
// CHECK-NEXT: // Type metadata accessor for StructWithIntField
// CHECK-NEXT: SWIFT_EXTERN swift::_impl::MetadataResponseTy $s7Structs18StructWithIntFieldVMa(swift::_impl::MetadataRequestTy) SWIFT_NOEXCEPT SWIFT_CALL;
// CHECK-EMPTY:
// CHECK-EMPTY:
// CHECK-NEXT: }

// CHECK: class StructWithIntField final {
Expand Down
6 changes: 5 additions & 1 deletion test/PrintAsCxx/empty.swift
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,11 @@
// CHECK-LABEL: #if defined(__OBJC__)
// CHECK-NEXT: #endif
// CHECK-NEXT: #if defined(__cplusplus)
// CHECK-NEXT: namespace empty {
// CHECK-NEXT: #ifndef SWIFT_PRINTED_CORE
// CHECK: } // namespace swift
// CHECK-EMPTY:
// CHECK-NEXT: #endif
// CHECK: namespace empty {
// CHECK: } // namespace empty
// CHECK: #endif

Expand Down