Skip to content

IRGen: special case VWT emission linkage computation #78427

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
Jan 25, 2025
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
2 changes: 2 additions & 0 deletions include/swift/IRGen/Linking.h
Original file line number Diff line number Diff line change
Expand Up @@ -1664,6 +1664,8 @@ class LinkEntity {
/// Determine whether entity that represents a symbol is in DATA segment.
bool isData() const { return !isText(); }

bool isTypeKind() const { return isTypeKind(getKind()); }

bool isAlwaysSharedLinkage() const;
#undef LINKENTITY_GET_FIELD
#undef LINKENTITY_SET_FIELD
Expand Down
28 changes: 28 additions & 0 deletions lib/IRGen/GenDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3907,6 +3907,34 @@ IRGenModule::getAddrOfLLVMVariable(LinkEntity entity,
auto var = createVariable(*this, link, definitionType,
entity.getAlignment(*this), DbgTy);

// @escaping () -> ()
// NOTE: we explicitly desugar the `Void` type for the return as the test
// suite makes assumptions that it can emit the value witness table without a
// standard library for the target. `Context.getVoidType()` will attempt to
// lookup the `Decl` before returning the canonical type. To workaround this
// dependency, we simply desugar the `Void` return type to `()`.
static CanType kAnyFunctionType =
FunctionType::get({}, Context.TheEmptyTupleType,
ASTExtInfo{})->getCanonicalType();

// Adjust the linkage for the well-known VWTs that are strongly defined
// in the runtime.
//
// We special case the "AnyFunctionType" here as this type is referened
// inside the standard library with the definition being in the runtime
// preventing the normal detection from identifying that this is module
// local.
if (getSwiftModule()->isStdlibModule())
if (entity.isTypeKind() &&
(IsWellKnownBuiltinOrStructralType(entity.getType()) ||
entity.getType() == kAnyFunctionType))
if (auto *GV = dyn_cast<llvm::GlobalValue>(var))
if (GV->hasDLLImportStorageClass())
ApplyIRLinkage({llvm::GlobalValue::ExternalLinkage,
llvm::GlobalValue::DefaultVisibility,
llvm::GlobalValue::DefaultStorageClass})
.to(GV);

// Install the concrete definition if we have one.
if (definition && definition.hasInit()) {
definition.getInit().installInGlobal(var);
Expand Down
33 changes: 33 additions & 0 deletions lib/IRGen/IRGenModule.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1375,6 +1375,39 @@ llvm::Module *IRGenModule::getModule() const {
return ClangCodeGen->GetModule();
}

bool IRGenModule::IsWellKnownBuiltinOrStructralType(CanType T) const {
static const CanType kStructural[] = {
Context.TheEmptyTupleType, Context.TheNativeObjectType,
Context.TheBridgeObjectType, Context.TheRawPointerType,
Context.getAnyObjectType()
};

if (std::any_of(std::begin(kStructural), std::end(kStructural),
[T](const CanType &ST) { return T == ST; }))
return true;

if (auto IntTy = dyn_cast<BuiltinIntegerType>(T)) {
auto Width = IntTy->getWidth();
if (Width.isPointerWidth())
return true;
if (!Width.isFixedWidth())
return false;
switch (Width.getFixedWidth()) {
case 8:
case 16:
case 32:
case 64:
case 128:
case 256:
return true;
default:
break;
}
}

return false;
}

GeneratedModule IRGenModule::intoGeneratedModule() && {
return GeneratedModule{
std::move(LLVMContext),
Expand Down
2 changes: 2 additions & 0 deletions lib/IRGen/IRGenModule.h
Original file line number Diff line number Diff line change
Expand Up @@ -1137,6 +1137,8 @@ class IRGenModule {

ClassMetadataStrategy getClassMetadataStrategy(const ClassDecl *theClass);

bool IsWellKnownBuiltinOrStructralType(CanType type) const;

private:
TypeConverter &Types;
friend TypeConverter;
Expand Down
40 changes: 4 additions & 36 deletions lib/IRGen/MetadataRequest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3694,41 +3694,6 @@ namespace {
return nullptr;
}

bool hasVisibleValueWitnessTable(CanType t) const {
// Some builtin and structural types have value witnesses exported from
// the runtime.
auto &C = IGF.IGM.Context;
if (t == C.TheEmptyTupleType
|| t == C.TheNativeObjectType
|| t == C.TheBridgeObjectType
|| t == C.TheRawPointerType
|| t == C.getAnyObjectType())
return true;
if (auto intTy = dyn_cast<BuiltinIntegerType>(t)) {
auto width = intTy->getWidth();
if (width.isPointerWidth())
return true;
if (width.isFixedWidth()) {
switch (width.getFixedWidth()) {
case 8:
case 16:
case 32:
case 64:
case 128:
case 256:
return true;
default:
return false;
}
}
return false;
}

// TODO: If a nominal type is in the same source file as we're currently
// emitting, we would be able to see its value witness table.
return false;
}

/// Fallback default implementation.
llvm::Value *visitType(CanType t, DynamicMetadataRequest request) {
auto silTy = IGF.IGM.getLoweredType(t);
Expand All @@ -3737,7 +3702,10 @@ namespace {
// If the type is in the same source file, or has a common value
// witness table exported from the runtime, we can project from the
// value witness table instead of emitting a new record.
if (hasVisibleValueWitnessTable(t))
//
// TODO: If a nominal type is in the same source file as we're currently
// emitting, we would be able to see its value witness table.
if (IGF.IGM.IsWellKnownBuiltinOrStructralType(t))
return emitFromValueWitnessTable(t);

// If the type is a singleton aggregate, the field's layout is equivalent
Expand Down