Skip to content

Lazy initialize Swift runtime in backwards interop synth provider #7593

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

Conversation

adrian-prantl
Copy link

@adrian-prantl adrian-prantl commented Oct 7, 2023

Otherwise the Swift runtime and scratch context will be initialized in
any process that contains C++ struct types, which costs performance
and produces spurious warnings if no Swift runtime is available.

rdar://116533409

@adrian-prantl
Copy link
Author

@swift-ci test

if (!ts)
return {};

swift_type = ts->GetTypeFromMangledTypename(ConstString(swift_name));

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you think you could change this loop to fid the swift_name instead, and get the type system after the loop, only if you found a name? This way you don't need to have TypeSystemSwift *ts = nullptr; and setting it inside the statement.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure.

@augusto2112
Copy link

An alternative implementation would be to extract the part that finds the swift name into it's own function:

static StringRef FindSwiftMangledNameFromCxxInteropType(CompilerType type) {
  Log *log(GetLog(LLDBLog::DataFormatters));
  // This only makes sense for Clang types.
  auto tsc = type.GetTypeSystem().dyn_cast_or_null<TypeSystemClang>();
  if (!tsc)
    return {};

  // We operate directly on the qualified type because the TypeSystem
  // interface doesn't allow us to check for static constexpr members.
  auto qual_type = TypeSystemClang::GetQualType(type.GetOpaqueQualType());
  auto *record_type =
      llvm::dyn_cast_or_null<clang::RecordType>(qual_type.getTypePtrOrNull());
  if (!record_type) {
    LLDB_LOGV(log, "[ExtractSwiftTypeFromCxxInteropType] "
                   "Type is not a record type.");
    return {};
  }

  const clang::RecordDecl *record_decl = record_type->getDecl();
  CompilerType swift_type;

  for (auto *child_decl : record_decl->decls()) {
    auto *var_decl = llvm::dyn_cast<clang::VarDecl>(child_decl);
    if (!var_decl)
      continue;

    auto name = var_decl->getName();
    if (name != "__swift_mangled_name")
      continue;

    const auto *typedef_type =
        llvm::dyn_cast<clang::TypedefType>(var_decl->getType());
    if (!typedef_type)
      break;

    auto *decl = typedef_type->getDecl();
    if (!decl)
      break;

    auto swift_name = decl->getName();
    if (!swift::Demangle::isMangledName(swift_name))
      break;

    return swift_name;
  }

  return {};
}

And have ExtractSwiftTypeFromCxxInteropType take in the swift name as well.

This way, you can check for the swift name in the beginning of the formatter closure:

    g_formatters.push_back([](lldb_private::ValueObject &valobj,
                              lldb::DynamicValueType dyn_type,
                              FormatManager &format_manager)
                               -> lldb::SyntheticChildrenSP {
      Log *log(GetLog(LLDBLog::DataFormatters));

      auto type = valobj.GetCompilerType();
      auto swift_name = FindSwiftMangledNameFromCxxInteropType(type);
      if (swift_name.empty())
        return nullptr;
      // If there's a swift name, it's safe to get the swift scratch context.

And no need to pass in closures to the extract function.

I think breaking ExtractSwiftTypeFromCxxInteropType into two functions would be a nice change, and not passing in functions to lazily initialize the scratch context makes things slightly simpler. What do you think?

@adrian-prantl
Copy link
Author

How would that work for the recursive case?

@adrian-prantl
Copy link
Author

@swift-ci test

@augusto2112
Copy link

augusto2112 commented Oct 7, 2023

How would that work for the recursive case?

You just call the other function in the closure:

    auto bound_type = runtime.BindGenericTypeParameters(
        swift_type, [&](unsigned depth, unsigned index) -> CompilerType {
          assert(depth == 0 && "Unexpected depth! C++ interop does not support "
                               "nested generic parameters");
          if (depth > 0)
            return {};

          auto templated_type = type.GetTypeTemplateArgument(index);
          auto templated_type_name = FindSwiftMangledNameFromCxxInteropType(templated_type);
          auto substituted_type =
              ExtractSwiftTypeFromCxxInteropType(templated_type, templated_type_name, ts, runtime);
          ...

Otherwise the Swift runtime and scratch context will be initialized in
any process that contains C++ struct types, which costs performance
and produces spurious warnings if no Swift runtime is available.

rdar://116533409
@adrian-prantl
Copy link
Author

Done.

@adrian-prantl
Copy link
Author

@swift-ci test

@adrian-prantl
Copy link
Author

@swift-ci test windows

1 similar comment
@adrian-prantl
Copy link
Author

@swift-ci test windows

@adrian-prantl adrian-prantl merged commit 8f355fd into swiftlang:swift/release/5.9 Oct 9, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants