From 03d94b44a6ecb2a8e4e05e9ff55081209684d7f7 Mon Sep 17 00:00:00 2001 From: John McCall Date: Sat, 8 Aug 2020 02:28:47 -0400 Subject: [PATCH] Add default IR attributes to helper functions and convert several more places to use getOrCreateHelperFunction. This means that several of these places are now emitting shared functions rather than private ones, which I've verified is okay. There are some other places where privacy is still unfortunately necessary. I've also fixed the name of the store-extra-inhabitants helper function to say "store" instead of "get", which is longstanding (but harmless because it's private). Fixes rdar://66707994. --- lib/IRGen/GenCast.cpp | 147 ++++--- lib/IRGen/GenDecl.cpp | 1 + lib/IRGen/GenKeyPath.cpp | 21 +- lib/IRGen/GenOpaque.cpp | 4 +- lib/IRGen/IRGenModule.cpp | 1 + lib/IRGen/MetadataRequest.cpp | 362 +++++++++--------- test/IRGen/casts.sil | 8 +- .../default_function_ir_attributes.swift | 175 +++++++++ test/IRGen/keypaths_objc.sil | 2 +- test/IRGen/subclass_existentials.sil | 4 +- test/Serialization/autolinking.swift | 4 +- 11 files changed, 437 insertions(+), 292 deletions(-) create mode 100644 test/IRGen/default_function_ir_attributes.swift diff --git a/lib/IRGen/GenCast.cpp b/lib/IRGen/GenCast.cpp index 5054b5344303f..90ff58b9bfa15 100644 --- a/lib/IRGen/GenCast.cpp +++ b/lib/IRGen/GenCast.cpp @@ -357,7 +357,7 @@ llvm::Value *irgen::emitReferenceToObjCProtocol(IRGenFunction &IGF, /// The function's output type is (value, witnessTable...) /// /// The value is NULL if the cast failed. -static llvm::Function * +static llvm::Constant * emitExistentialScalarCastFn(IRGenModule &IGM, unsigned numProtocols, CheckedCastMode mode, @@ -385,13 +385,7 @@ emitExistentialScalarCastFn(IRGenModule &IGM, } } - // See if we already defined this function. - - if (auto fn = IGM.Module.getFunction(name)) - return fn; - // Build the function type. - llvm::SmallVector argTys; llvm::SmallVector returnTys; argTys.push_back(IGM.Int8PtrTy); @@ -405,84 +399,77 @@ emitExistentialScalarCastFn(IRGenModule &IGM, } llvm::Type *returnTy = llvm::StructType::get(IGM.getLLVMContext(), returnTys); - - auto fnTy = llvm::FunctionType::get(returnTy, argTys, /*vararg*/ false); - auto fn = llvm::Function::Create(fnTy, llvm::GlobalValue::PrivateLinkage, - llvm::Twine(name), IGM.getModule()); - fn->setAttributes(IGM.constructInitialAttributes()); - - IRGenFunction IGF(IGM, fn); - if (IGM.DebugInfo) - IGM.DebugInfo->emitArtificialFunction(IGF, fn); - Explosion args = IGF.collectParameters(); - - auto value = args.claimNext(); - auto ref = args.claimNext(); - auto failBB = IGF.createBasicBlock("fail"); - auto conformsToProtocol = IGM.getConformsToProtocolFn(); - - Explosion rets; - rets.add(value); - - // Check the class constraint if necessary. - if (checkSuperclassConstraint) { - auto superclassMetadata = args.claimNext(); - auto castFn = IGF.IGM.getDynamicCastMetatypeFn(); - auto castResult = IGF.Builder.CreateCall(castFn, {ref, - superclassMetadata}); - - auto cc = cast(castFn)->getCallingConv(); - - // FIXME: Eventually, we may want to throw. - castResult->setCallingConv(cc); - castResult->setDoesNotThrow(); - auto isClass = IGF.Builder.CreateICmpNE( - castResult, - llvm::ConstantPointerNull::get(IGF.IGM.TypeMetadataPtrTy)); + return IGM.getOrCreateHelperFunction(name, returnTy, argTys, + [&](IRGenFunction &IGF) { + Explosion args = IGF.collectParameters(); + + auto value = args.claimNext(); + auto ref = args.claimNext(); + auto failBB = IGF.createBasicBlock("fail"); + auto conformsToProtocol = IGM.getConformsToProtocolFn(); + + Explosion rets; + rets.add(value); + + // Check the class constraint if necessary. + if (checkSuperclassConstraint) { + auto superclassMetadata = args.claimNext(); + auto castFn = IGF.IGM.getDynamicCastMetatypeFn(); + auto castResult = IGF.Builder.CreateCall(castFn, {ref, + superclassMetadata}); + + auto cc = cast(castFn)->getCallingConv(); + + // FIXME: Eventually, we may want to throw. + castResult->setCallingConv(cc); + castResult->setDoesNotThrow(); + + auto isClass = IGF.Builder.CreateICmpNE( + castResult, + llvm::ConstantPointerNull::get(IGF.IGM.TypeMetadataPtrTy)); + + auto contBB = IGF.createBasicBlock("cont"); + IGF.Builder.CreateCondBr(isClass, contBB, failBB); + IGF.Builder.emitBlock(contBB); + } else if (checkClassConstraint) { + auto isClass = IGF.Builder.CreateCall(IGM.getIsClassTypeFn(), ref); + auto contBB = IGF.createBasicBlock("cont"); + IGF.Builder.CreateCondBr(isClass, contBB, failBB); + IGF.Builder.emitBlock(contBB); + } - auto contBB = IGF.createBasicBlock("cont"); - IGF.Builder.CreateCondBr(isClass, contBB, failBB); - IGF.Builder.emitBlock(contBB); - } else if (checkClassConstraint) { - auto isClass = IGF.Builder.CreateCall(IGM.getIsClassTypeFn(), ref); - auto contBB = IGF.createBasicBlock("cont"); - IGF.Builder.CreateCondBr(isClass, contBB, failBB); - IGF.Builder.emitBlock(contBB); - } + // Look up each protocol conformance we want. + for (unsigned i = 0; i < numProtocols; ++i) { + auto proto = args.claimNext(); + auto witness = IGF.Builder.CreateCall(conformsToProtocol, {ref, proto}); + auto isNull = IGF.Builder.CreateICmpEQ(witness, + llvm::ConstantPointerNull::get(IGM.WitnessTablePtrTy)); + auto contBB = IGF.createBasicBlock("cont"); + IGF.Builder.CreateCondBr(isNull, failBB, contBB); + + IGF.Builder.emitBlock(contBB); + rets.add(witness); + } - // Look up each protocol conformance we want. - for (unsigned i = 0; i < numProtocols; ++i) { - auto proto = args.claimNext(); - auto witness = IGF.Builder.CreateCall(conformsToProtocol, {ref, proto}); - auto isNull = IGF.Builder.CreateICmpEQ(witness, - llvm::ConstantPointerNull::get(IGM.WitnessTablePtrTy)); - auto contBB = IGF.createBasicBlock("cont"); - IGF.Builder.CreateCondBr(isNull, failBB, contBB); + // If we succeeded, return the witnesses. + IGF.emitScalarReturn(returnTy, rets); - IGF.Builder.emitBlock(contBB); - rets.add(witness); - } - - // If we succeeded, return the witnesses. - IGF.emitScalarReturn(returnTy, rets); - - // If we failed, return nil or trap. - IGF.Builder.emitBlock(failBB); - switch (mode) { - case CheckedCastMode::Conditional: { - auto null = llvm::ConstantStruct::getNullValue(returnTy); - IGF.Builder.CreateRet(null); - break; - } + // If we failed, return nil or trap. + IGF.Builder.emitBlock(failBB); + switch (mode) { + case CheckedCastMode::Conditional: { + auto null = llvm::ConstantStruct::getNullValue(returnTy); + IGF.Builder.CreateRet(null); + break; + } - case CheckedCastMode::Unconditional: { - IGF.emitTrap("type cast failed", /*EmitUnreachable=*/true); - break; - } - } - - return fn; + case CheckedCastMode::Unconditional: { + IGF.emitTrap("type cast failed", /*EmitUnreachable=*/true); + break; + } + } + }); } llvm::Value *irgen::emitMetatypeToAnyObjectDowncast(IRGenFunction &IGF, diff --git a/lib/IRGen/GenDecl.cpp b/lib/IRGen/GenDecl.cpp index 0cf022d9cbee3..b5fbcef362e4e 100644 --- a/lib/IRGen/GenDecl.cpp +++ b/lib/IRGen/GenDecl.cpp @@ -4862,6 +4862,7 @@ static llvm::Function *shouldDefineHelper(IRGenModule &IGM, if (!def) return nullptr; if (!def->empty()) return nullptr; + def->setAttributes(IGM.constructInitialAttributes()); ApplyIRLinkage(IRLinkage::InternalLinkOnceODR).to(def); def->setDoesNotThrow(); def->setCallingConv(IGM.DefaultCC); diff --git a/lib/IRGen/GenKeyPath.cpp b/lib/IRGen/GenKeyPath.cpp index 41128f62b8620..6cfc8d962c9b9 100644 --- a/lib/IRGen/GenKeyPath.cpp +++ b/lib/IRGen/GenKeyPath.cpp @@ -277,6 +277,7 @@ getLayoutFunctionForComputedComponent(IRGenModule &IGM, auto layoutFn = llvm::Function::Create(fnTy, llvm::GlobalValue::PrivateLinkage, "keypath_get_arg_layout", IGM.getModule()); + layoutFn->setAttributes(IGM.constructInitialAttributes()); { IRGenFunction IGF(IGM, layoutFn); @@ -378,6 +379,7 @@ getWitnessTableForComputedComponent(IRGenModule &IGM, auto destroyFn = llvm::Function::Create(destroyType, llvm::GlobalValue::PrivateLinkage, "keypath_destroy", IGM.getModule()); destroy = destroyFn; + destroyFn->setAttributes(IGM.constructInitialAttributes()); IRGenFunction IGF(IGM, destroyFn); if (IGM.DebugInfo) @@ -426,6 +428,7 @@ getWitnessTableForComputedComponent(IRGenModule &IGM, auto copyFn = llvm::Function::Create(copyType, llvm::GlobalValue::PrivateLinkage, "keypath_copy", IGM.getModule()); copy = copyFn; + copyFn->setAttributes(IGM.constructInitialAttributes()); IRGenFunction IGF(IGM, copyFn); if (IGM.DebugInfo) @@ -538,6 +541,7 @@ getInitializerForComputedComponent(IRGenModule &IGM, auto initFn = llvm::Function::Create(fnTy, llvm::GlobalValue::PrivateLinkage, "keypath_arg_init", IGM.getModule()); + initFn->setAttributes(IGM.constructInitialAttributes()); { IRGenFunction IGF(IGM, initFn); @@ -945,23 +949,16 @@ emitKeyPathComponent(IRGenModule &IGM, // Note that we'd need to do this anyway in JIT mode because we would // need to unique the selector at runtime anyway. auto selectorName = IGM.getObjCSelectorName(declRef); - llvm::Type *fnParams[] = {IGM.Int8PtrTy}; - auto fnTy = llvm::FunctionType::get(IGM.Int8PtrTy, fnParams, false); SmallString<32> fnName; fnName.append("keypath_get_selector_"); fnName.append(selectorName); - auto fn = cast( - IGM.Module.getOrInsertFunction(fnName, fnTy).getCallee()); - if (fn->empty()) { - fn->setLinkage(llvm::Function::PrivateLinkage); - IRGenFunction subIGF(IGM, fn); - if (IGM.DebugInfo) - IGM.DebugInfo->emitArtificialFunction(subIGF, fn); - + auto fn = IGM.getOrCreateHelperFunction(fnName, IGM.Int8PtrTy, + {IGM.Int8PtrTy}, + [&selectorName](IRGenFunction &subIGF) { auto selectorValue = subIGF.emitObjCSelectorRefLoad(selectorName); subIGF.Builder.CreateRet(selectorValue); - } - + }); + idValue = fn; idResolution = KeyPathComponentHeader::FunctionCall; } else { diff --git a/lib/IRGen/GenOpaque.cpp b/lib/IRGen/GenOpaque.cpp index a6a328f273aa4..a01ff1ad59adc 100644 --- a/lib/IRGen/GenOpaque.cpp +++ b/lib/IRGen/GenOpaque.cpp @@ -1354,6 +1354,7 @@ irgen::getOrCreateGetExtraInhabitantTagFunction(IRGenModule &IGM, auto fn = llvm::Function::Create(fnTy, llvm::Function::PrivateLinkage, "__swift_get_extra_inhabitant_index", &IGM.Module); + fn->setAttributes(IGM.constructInitialAttributes()); fn->setCallingConv(IGM.SwiftCC); IRGenFunction IGF(IGM, fn); auto parameters = IGF.collectParameters(); @@ -1427,8 +1428,9 @@ irgen::getOrCreateStoreExtraInhabitantTagFunction(IRGenModule &IGM, // TODO: use a meaningful mangled name and internal/shared linkage. auto fn = llvm::Function::Create(fnTy, llvm::Function::PrivateLinkage, - "__swift_get_extra_inhabitant_index", + "__swift_store_extra_inhabitant_index", &IGM.Module); + fn->setAttributes(IGM.constructInitialAttributes()); fn->setCallingConv(IGM.SwiftCC); IRGenFunction IGF(IGM, fn); auto parameters = IGF.collectParameters(); diff --git a/lib/IRGen/IRGenModule.cpp b/lib/IRGen/IRGenModule.cpp index 03c871a9ffe13..dd90ae9399526 100644 --- a/lib/IRGen/IRGenModule.cpp +++ b/lib/IRGen/IRGenModule.cpp @@ -1407,6 +1407,7 @@ void IRGenModule::emitAutolinkInfo() { llvm::Function::Create(llvm::FunctionType::get(VoidTy, false), llvm::GlobalValue::ExternalLinkage, buf, &Module); + ForceImportThunk->setAttributes(constructInitialAttributes()); ApplyIRLinkage(IRLinkage::ExternalExport).to(ForceImportThunk); if (Triple.supportsCOMDAT()) if (auto *GO = cast(ForceImportThunk)) diff --git a/lib/IRGen/MetadataRequest.cpp b/lib/IRGen/MetadataRequest.cpp index 7dfd2be9b2f44..5a794512e3ff1 100644 --- a/lib/IRGen/MetadataRequest.cpp +++ b/lib/IRGen/MetadataRequest.cpp @@ -2256,66 +2256,57 @@ MetadataResponse irgen::emitGenericTypeMetadataAccessFunction( // Factor out the buffer shuffling for metadata accessors that take their // arguments directly, so that the accessor function itself only needs to // materialize the nominal type descriptor and call this thunk. - auto thunkFn = cast( - IGM.getModule() - ->getOrInsertFunction("__swift_instantiateGenericMetadata", - IGM.TypeMetadataResponseTy, - IGM.SizeTy, // request - IGM.Int8PtrTy, // arg 0 - IGM.Int8PtrTy, // arg 1 - IGM.Int8PtrTy, // arg 2 - IGM.TypeContextDescriptorPtrTy) // type context descriptor - .getCallee() - ->stripPointerCasts()); - - if (thunkFn->empty()) { - ApplyIRLinkage(IRLinkage::InternalLinkOnceODR) - .to(thunkFn); - thunkFn->setDoesNotAccessMemory(); - thunkFn->setDoesNotThrow(); - thunkFn->setCallingConv(IGM.SwiftCC); - thunkFn->addAttribute(llvm::AttributeList::FunctionIndex, - llvm::Attribute::NoInline); - IGM.setHasNoFramePointer(thunkFn); - - [&IGM, thunkFn]{ - IRGenFunction subIGF(IGM, thunkFn); - - auto params = subIGF.collectParameters(); - auto request = params.claimNext(); - auto arg0 = params.claimNext(); - auto arg1 = params.claimNext(); - auto arg2 = params.claimNext(); - auto descriptor = params.claimNext(); - - // Allocate a buffer with enough storage for the arguments. - auto argsBufferTy = - llvm::ArrayType::get(IGM.Int8PtrTy, - NumDirectGenericTypeMetadataAccessFunctionArgs); - auto argsBuffer = subIGF.createAlloca(argsBufferTy, - IGM.getPointerAlignment(), - "generic.arguments"); - subIGF.Builder.CreateLifetimeStart(argsBuffer, - IGM.getPointerSize() * NumDirectGenericTypeMetadataAccessFunctionArgs); - - auto arg0Buf = subIGF.Builder.CreateConstInBoundsGEP2_32(argsBufferTy, - argsBuffer.getAddress(), 0, 0); - subIGF.Builder.CreateStore(arg0, arg0Buf, IGM.getPointerAlignment()); - auto arg1Buf = subIGF.Builder.CreateConstInBoundsGEP2_32(argsBufferTy, - argsBuffer.getAddress(), 0, 1); - subIGF.Builder.CreateStore(arg1, arg1Buf, IGM.getPointerAlignment()); - auto arg2Buf = subIGF.Builder.CreateConstInBoundsGEP2_32(argsBufferTy, - argsBuffer.getAddress(), 0, 2); - subIGF.Builder.CreateStore(arg2, arg2Buf, IGM.getPointerAlignment()); - - // Make the call. - auto argsAddr = subIGF.Builder.CreateBitCast(argsBuffer.getAddress(), - IGM.Int8PtrTy); - auto result = subIGF.Builder.CreateCall(IGM.getGetGenericMetadataFn(), - {request, argsAddr, descriptor}); - subIGF.Builder.CreateRet(result); - }(); - } + auto generateThunkFn = [&IGM](IRGenFunction &subIGF) { + subIGF.CurFn->setDoesNotAccessMemory(); + subIGF.CurFn->setCallingConv(IGM.SwiftCC); + IGM.setHasNoFramePointer(subIGF.CurFn); + + auto params = subIGF.collectParameters(); + auto request = params.claimNext(); + auto arg0 = params.claimNext(); + auto arg1 = params.claimNext(); + auto arg2 = params.claimNext(); + auto descriptor = params.claimNext(); + + // Allocate a buffer with enough storage for the arguments. + auto argsBufferTy = + llvm::ArrayType::get(IGM.Int8PtrTy, + NumDirectGenericTypeMetadataAccessFunctionArgs); + auto argsBuffer = subIGF.createAlloca(argsBufferTy, + IGM.getPointerAlignment(), + "generic.arguments"); + subIGF.Builder.CreateLifetimeStart(argsBuffer, + IGM.getPointerSize() * NumDirectGenericTypeMetadataAccessFunctionArgs); + + auto arg0Buf = subIGF.Builder.CreateConstInBoundsGEP2_32(argsBufferTy, + argsBuffer.getAddress(), 0, 0); + subIGF.Builder.CreateStore(arg0, arg0Buf, IGM.getPointerAlignment()); + auto arg1Buf = subIGF.Builder.CreateConstInBoundsGEP2_32(argsBufferTy, + argsBuffer.getAddress(), 0, 1); + subIGF.Builder.CreateStore(arg1, arg1Buf, IGM.getPointerAlignment()); + auto arg2Buf = subIGF.Builder.CreateConstInBoundsGEP2_32(argsBufferTy, + argsBuffer.getAddress(), 0, 2); + subIGF.Builder.CreateStore(arg2, arg2Buf, IGM.getPointerAlignment()); + + // Make the call. + auto argsAddr = subIGF.Builder.CreateBitCast(argsBuffer.getAddress(), + IGM.Int8PtrTy); + auto result = subIGF.Builder.CreateCall(IGM.getGetGenericMetadataFn(), + {request, argsAddr, descriptor}); + subIGF.Builder.CreateRet(result); + }; + auto thunkFn = IGM.getOrCreateHelperFunction( + "__swift_instantiateGenericMetadata", + IGM.TypeMetadataResponseTy, + { + IGM.SizeTy, // request + IGM.Int8PtrTy, // arg 0 + IGM.Int8PtrTy, // arg 1 + IGM.Int8PtrTy, // arg 2 + IGM.TypeContextDescriptorPtrTy // type context descriptor + }, + generateThunkFn, + /*noinline*/true); // Call out to the helper. auto arg0 = numArguments >= 1 @@ -2805,136 +2796,127 @@ emitMetadataAccessByMangledName(IRGenFunction &IGF, CanType type, request.isStaticallyAbstract() ? "__swift_instantiateConcreteTypeFromMangledNameAbstract" : "__swift_instantiateConcreteTypeFromMangledName"; - auto instantiationFn = cast( - IGM.getModule() - ->getOrInsertFunction(instantiationFnName, IGF.IGM.TypeMetadataPtrTy, - cache->getType()) - .getCallee() - ->stripPointerCasts()); - if (instantiationFn->empty()) { - ApplyIRLinkage(IRLinkage::InternalLinkOnceODR) - .to(instantiationFn); - instantiationFn->setDoesNotAccessMemory(); - instantiationFn->setDoesNotThrow(); - instantiationFn->addAttribute(llvm::AttributeList::FunctionIndex, - llvm::Attribute::NoInline); - IGM.setHasNoFramePointer(instantiationFn); - - [&IGM, instantiationFn, request]{ - IRGenFunction subIGF(IGM, instantiationFn); - - auto params = subIGF.collectParameters(); - auto cache = params.claimNext(); - - // Load the existing cache value. - // Conceptually, this needs to establish memory ordering with the - // store we do later in the function: if the metadata value is - // non-null, we must be able to see any stores performed by the - // initialization of the metadata. However, any attempt to read - // from the metadata will be address-dependent on the loaded - // metadata pointer, which is sufficient to provide adequate - // memory ordering guarantees on all the platforms we care about: - // ARM has special rules about address dependencies, and x86's - // memory ordering is strong enough to guarantee the visibility - // even without the address dependency. - // - // And we do not need to worry about the compiler because the - // address dependency naturally forces an order to the memory - // accesses. - // - // Therefore, we can perform a completely naked load here. - // FIXME: Technically should be "consume", but that introduces barriers - // in the current LLVM ARM backend. - auto cacheWordAddr = subIGF.Builder.CreateBitCast(cache, - IGM.Int64Ty->getPointerTo()); - auto load = subIGF.Builder.CreateLoad(cacheWordAddr, Alignment(8)); - // Make this barrier explicit when building for TSan to avoid false positives. - if (IGM.IRGen.Opts.Sanitizers & SanitizerKind::Thread) - load->setOrdering(llvm::AtomicOrdering::Acquire); - else - load->setOrdering(llvm::AtomicOrdering::Monotonic); - - // Compare the load result to see if it's negative. - auto isUnfilledBB = subIGF.createBasicBlock(""); - auto contBB = subIGF.createBasicBlock(""); - llvm::Value *comparison = subIGF.Builder.CreateICmpSLT(load, - llvm::ConstantInt::get(IGM.Int64Ty, 0)); - comparison = subIGF.Builder.CreateExpect(comparison, - llvm::ConstantInt::get(IGM.Int1Ty, 0)); - subIGF.Builder.CreateCondBr(comparison, isUnfilledBB, contBB); - auto loadBB = subIGF.Builder.GetInsertBlock(); - - // If the load is negative, emit the call to instantiate the type - // metadata. - subIGF.Builder.SetInsertPoint(&subIGF.CurFn->back()); - subIGF.Builder.emitBlock(isUnfilledBB); - - // Break up the loaded value into size and relative address to the - // string. - auto size = subIGF.Builder.CreateAShr(load, 32); - size = subIGF.Builder.CreateTruncOrBitCast(size, IGM.SizeTy); - size = subIGF.Builder.CreateNeg(size); - - auto stringAddrOffset = subIGF.Builder.CreateTrunc(load, - IGM.Int32Ty); - stringAddrOffset = subIGF.Builder.CreateSExtOrBitCast(stringAddrOffset, - IGM.SizeTy); - auto stringAddrBase = subIGF.Builder.CreatePtrToInt(cache, IGM.SizeTy); - if (IGM.getModule()->getDataLayout().isBigEndian()) { - stringAddrBase = subIGF.Builder.CreateAdd(stringAddrBase, - llvm::ConstantInt::get(IGM.SizeTy, 4)); - } - auto stringAddr = subIGF.Builder.CreateAdd(stringAddrBase, - stringAddrOffset); - stringAddr = subIGF.Builder.CreateIntToPtr(stringAddr, IGM.Int8PtrTy); - - llvm::CallInst *call; - if (request.isStaticallyAbstract()) { - call = subIGF.Builder.CreateCall( - IGM.getGetTypeByMangledNameInContextInMetadataStateFn(), - {llvm::ConstantInt::get(IGM.SizeTy, (size_t)MetadataState::Abstract), - stringAddr, size, - // TODO: Use mangled name lookup in generic - // contexts? - llvm::ConstantPointerNull::get(IGM.TypeContextDescriptorPtrTy), - llvm::ConstantPointerNull::get(IGM.Int8PtrPtrTy)}); - } else { - call = subIGF.Builder.CreateCall( - IGM.getGetTypeByMangledNameInContextFn(), - {stringAddr, size, - // TODO: Use mangled name lookup in generic - // contexts? - llvm::ConstantPointerNull::get(IGM.TypeContextDescriptorPtrTy), - llvm::ConstantPointerNull::get(IGM.Int8PtrPtrTy)}); - } - call->setDoesNotThrow(); - call->setDoesNotAccessMemory(); - call->setCallingConv(IGM.SwiftCC); - - // Store the result back to the cache. Metadata instantatiation should - // already have emitted the necessary barriers to publish the instantiated - // metadata to other threads, so we only need to expose the pointer. - // Worst case, another thread might race with us and reinstantiate the - // exact same metadata pointer. - auto resultWord = subIGF.Builder.CreatePtrToInt(call, IGM.SizeTy); - resultWord = subIGF.Builder.CreateZExtOrBitCast(resultWord, IGM.Int64Ty); - auto store = subIGF.Builder.CreateStore(resultWord, cacheWordAddr, - Alignment(8)); - store->setOrdering(llvm::AtomicOrdering::Monotonic); - subIGF.Builder.CreateBr(contBB); - - subIGF.Builder.SetInsertPoint(loadBB); - subIGF.Builder.emitBlock(contBB); - auto phi = subIGF.Builder.CreatePHI(IGM.Int64Ty, 2); - phi->addIncoming(load, loadBB); - phi->addIncoming(resultWord, isUnfilledBB); - - auto resultAddr = subIGF.Builder.CreateTruncOrBitCast(phi, IGM.SizeTy); - resultAddr = subIGF.Builder.CreateIntToPtr(resultAddr, - IGM.TypeMetadataPtrTy); - subIGF.Builder.CreateRet(resultAddr); - }(); - } + auto generateInstantiationFn = [&IGM, request](IRGenFunction &subIGF) { + subIGF.CurFn->setDoesNotAccessMemory(); + IGM.setHasNoFramePointer(subIGF.CurFn); + + auto params = subIGF.collectParameters(); + auto cache = params.claimNext(); + + // Load the existing cache value. + // Conceptually, this needs to establish memory ordering with the + // store we do later in the function: if the metadata value is + // non-null, we must be able to see any stores performed by the + // initialization of the metadata. However, any attempt to read + // from the metadata will be address-dependent on the loaded + // metadata pointer, which is sufficient to provide adequate + // memory ordering guarantees on all the platforms we care about: + // ARM has special rules about address dependencies, and x86's + // memory ordering is strong enough to guarantee the visibility + // even without the address dependency. + // + // And we do not need to worry about the compiler because the + // address dependency naturally forces an order to the memory + // accesses. + // + // Therefore, we can perform a completely naked load here. + // FIXME: Technically should be "consume", but that introduces barriers + // in the current LLVM ARM backend. + auto cacheWordAddr = subIGF.Builder.CreateBitCast(cache, + IGM.Int64Ty->getPointerTo()); + auto load = subIGF.Builder.CreateLoad(cacheWordAddr, Alignment(8)); + // Make this barrier explicit when building for TSan to avoid false positives. + if (IGM.IRGen.Opts.Sanitizers & SanitizerKind::Thread) + load->setOrdering(llvm::AtomicOrdering::Acquire); + else + load->setOrdering(llvm::AtomicOrdering::Monotonic); + + // Compare the load result to see if it's negative. + auto isUnfilledBB = subIGF.createBasicBlock(""); + auto contBB = subIGF.createBasicBlock(""); + llvm::Value *comparison = subIGF.Builder.CreateICmpSLT(load, + llvm::ConstantInt::get(IGM.Int64Ty, 0)); + comparison = subIGF.Builder.CreateExpect(comparison, + llvm::ConstantInt::get(IGM.Int1Ty, 0)); + subIGF.Builder.CreateCondBr(comparison, isUnfilledBB, contBB); + auto loadBB = subIGF.Builder.GetInsertBlock(); + + // If the load is negative, emit the call to instantiate the type + // metadata. + subIGF.Builder.SetInsertPoint(&subIGF.CurFn->back()); + subIGF.Builder.emitBlock(isUnfilledBB); + + // Break up the loaded value into size and relative address to the + // string. + auto size = subIGF.Builder.CreateAShr(load, 32); + size = subIGF.Builder.CreateTruncOrBitCast(size, IGM.SizeTy); + size = subIGF.Builder.CreateNeg(size); + + auto stringAddrOffset = subIGF.Builder.CreateTrunc(load, + IGM.Int32Ty); + stringAddrOffset = subIGF.Builder.CreateSExtOrBitCast(stringAddrOffset, + IGM.SizeTy); + auto stringAddrBase = subIGF.Builder.CreatePtrToInt(cache, IGM.SizeTy); + if (IGM.getModule()->getDataLayout().isBigEndian()) { + stringAddrBase = subIGF.Builder.CreateAdd(stringAddrBase, + llvm::ConstantInt::get(IGM.SizeTy, 4)); + } + auto stringAddr = subIGF.Builder.CreateAdd(stringAddrBase, + stringAddrOffset); + stringAddr = subIGF.Builder.CreateIntToPtr(stringAddr, IGM.Int8PtrTy); + + llvm::CallInst *call; + if (request.isStaticallyAbstract()) { + call = subIGF.Builder.CreateCall( + IGM.getGetTypeByMangledNameInContextInMetadataStateFn(), + {llvm::ConstantInt::get(IGM.SizeTy, (size_t)MetadataState::Abstract), + stringAddr, size, + // TODO: Use mangled name lookup in generic + // contexts? + llvm::ConstantPointerNull::get(IGM.TypeContextDescriptorPtrTy), + llvm::ConstantPointerNull::get(IGM.Int8PtrPtrTy)}); + } else { + call = subIGF.Builder.CreateCall( + IGM.getGetTypeByMangledNameInContextFn(), + {stringAddr, size, + // TODO: Use mangled name lookup in generic + // contexts? + llvm::ConstantPointerNull::get(IGM.TypeContextDescriptorPtrTy), + llvm::ConstantPointerNull::get(IGM.Int8PtrPtrTy)}); + } + call->setDoesNotThrow(); + call->setDoesNotAccessMemory(); + call->setCallingConv(IGM.SwiftCC); + + // Store the result back to the cache. Metadata instantatiation should + // already have emitted the necessary barriers to publish the instantiated + // metadata to other threads, so we only need to expose the pointer. + // Worst case, another thread might race with us and reinstantiate the + // exact same metadata pointer. + auto resultWord = subIGF.Builder.CreatePtrToInt(call, IGM.SizeTy); + resultWord = subIGF.Builder.CreateZExtOrBitCast(resultWord, IGM.Int64Ty); + auto store = subIGF.Builder.CreateStore(resultWord, cacheWordAddr, + Alignment(8)); + store->setOrdering(llvm::AtomicOrdering::Monotonic); + subIGF.Builder.CreateBr(contBB); + + subIGF.Builder.SetInsertPoint(loadBB); + subIGF.Builder.emitBlock(contBB); + auto phi = subIGF.Builder.CreatePHI(IGM.Int64Ty, 2); + phi->addIncoming(load, loadBB); + phi->addIncoming(resultWord, isUnfilledBB); + + auto resultAddr = subIGF.Builder.CreateTruncOrBitCast(phi, IGM.SizeTy); + resultAddr = subIGF.Builder.CreateIntToPtr(resultAddr, + IGM.TypeMetadataPtrTy); + subIGF.Builder.CreateRet(resultAddr); + }; + auto instantiationFn = + IGM.getOrCreateHelperFunction(instantiationFnName, + IGF.IGM.TypeMetadataPtrTy, + cache->getType(), + generateInstantiationFn, + /*noinline*/true); auto call = IGF.Builder.CreateCall(instantiationFn, cache); call->setDoesNotThrow(); diff --git a/test/IRGen/casts.sil b/test/IRGen/casts.sil index 3458ef87cab62..fa2dbf9f2df85 100644 --- a/test/IRGen/casts.sil +++ b/test/IRGen/casts.sil @@ -54,7 +54,7 @@ entry(%n : $Builtin.NativeObject): // CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc { %objc_object*, i8** } @u_cast_to_class_existential(%objc_object* %0) // CHECK: call { i8*, i8** } @dynamic_cast_existential_1_unconditional(i8* {{%.*}}, %swift.type* {{%.*}}, {{.*}} @"$s5casts2CPMp" -// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} private { i8*, i8** } @dynamic_cast_existential_1_unconditional(i8* %0, %swift.type* %1, %swift.protocol* %2) {{.*}} { +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} linkonce_odr hidden { i8*, i8** } @dynamic_cast_existential_1_unconditional(i8* %0, %swift.type* %1, %swift.protocol* %2) {{.*}} { // CHECK: [[WITNESS:%.*]] = call i8** @swift_conformsToProtocol(%swift.type* %1, %swift.protocol* %2) // CHECK: [[IS_NULL:%.*]] = icmp eq i8** [[WITNESS]], null // CHECK: br i1 [[IS_NULL]], label %fail, label %cont @@ -80,7 +80,7 @@ entry(%a : $@thick Any.Type): // CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc { %objc_object*, i8**, i8** } @u_cast_to_class_existential_2(%objc_object* %0) // CHECK: call { i8*, i8**, i8** } @dynamic_cast_existential_2_unconditional(i8* {{%.*}}, %swift.type* {{%.*}}, {{.*}} @"$s5casts2CPMp"{{[^,]*}}, {{.*}} @"$s5casts3CP2Mp" -// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} private { i8*, i8**, i8** } @dynamic_cast_existential_2_unconditional(i8* %0, %swift.type* %1, %swift.protocol* %2, %swift.protocol* %3) +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} linkonce_odr hidden { i8*, i8**, i8** } @dynamic_cast_existential_2_unconditional(i8* %0, %swift.type* %1, %swift.protocol* %2, %swift.protocol* %3) // CHECK: [[WITNESS:%.*]] = call i8** @swift_conformsToProtocol(%swift.type* %1, %swift.protocol* %2) // CHECK: [[IS_NULL:%.*]] = icmp eq i8** [[WITNESS]], null // CHECK: br i1 [[IS_NULL]], label %fail, label %cont @@ -122,7 +122,7 @@ entry(%a : $@thick Any.Type): // CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc { %objc_object*, i8** } @c_cast_to_class_existential(%objc_object* %0) // CHECK: call { i8*, i8** } @dynamic_cast_existential_1_conditional(i8* {{.*}}, %swift.type* %.Type, {{.*}} @"$s5casts2CPMp" -// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} private { i8*, i8** } @dynamic_cast_existential_1_conditional(i8* %0, %swift.type* %1, %swift.protocol* %2) +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} linkonce_odr hidden { i8*, i8** } @dynamic_cast_existential_1_conditional(i8* %0, %swift.type* %1, %swift.protocol* %2) // CHECK: [[WITNESS:%.*]] = call i8** @swift_conformsToProtocol(%swift.type* %1, %swift.protocol* %2) // CHECK: [[IS_NULL:%.*]] = icmp eq i8** [[WITNESS]], null // CHECK: br i1 [[IS_NULL]], label %fail, label %cont @@ -152,7 +152,7 @@ nay: // CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc { %objc_object*, i8**, i8** } @c_cast_to_class_existential_2(%objc_object* %0) // CHECK: call { i8*, i8**, i8** } @dynamic_cast_existential_2_conditional(i8* {{%.*}}, %swift.type* {{%.*}}, {{.*}} @"$s5casts2CPMp" {{[^,]*}}, {{.*}} @"$s5casts3CP2Mp" -// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} private { i8*, i8**, i8** } @dynamic_cast_existential_2_conditional(i8* %0, %swift.type* %1, %swift.protocol* %2, %swift.protocol* %3) +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} linkonce_odr hidden { i8*, i8**, i8** } @dynamic_cast_existential_2_conditional(i8* %0, %swift.type* %1, %swift.protocol* %2, %swift.protocol* %3) // CHECK: [[WITNESS:%.*]] = call i8** @swift_conformsToProtocol(%swift.type* %1, %swift.protocol* %2) // CHECK: [[IS_NULL:%.*]] = icmp eq i8** [[WITNESS]], null // CHECK: br i1 [[IS_NULL]], label %fail, label %cont diff --git a/test/IRGen/default_function_ir_attributes.swift b/test/IRGen/default_function_ir_attributes.swift new file mode 100644 index 0000000000000..e41e97be4acc5 --- /dev/null +++ b/test/IRGen/default_function_ir_attributes.swift @@ -0,0 +1,175 @@ +// RUN: %target-swift-frontend -primary-file %s -emit-ir | %FileCheck %s --check-prefix=CHECK-%target-runtime --check-prefix=CHECK -DINT=i%target-ptrsize + +protocol P0 {} +protocol P1 {} +struct P0Conformer: P0, Hashable {} +protocol CP0 : AnyObject {} +protocol CP1 : AnyObject {} +class C {} + +struct S { + var stored: Int + var computed: Int { + get { stored } + set {} + } + subscript(t: T) -> Int { + get { 0 } + set {} + } +} + +enum SinglePayloadEnum { + case value(T) + case different + case otherwise +} + +struct OutlinedOperations { + var first: T + var second: T + var third: T + var fourth: T +} +struct StructHoldingOutlined { + var outlined: OutlinedOperations + var element: T +} + +// main +// CHECK-LABEL: define {{.*}} @main( +// CHECK-SAME: [[ATTRS_SIMPLE:#[0-9]+]] + +// class deinit +// CHECK-LABEL: define {{.*}} @"$s30default_function_ir_attributes1CCfd"( +// CHECK-SAME: [[ATTRS_SIMPLE]] + +// outlined operation +// CHECK-LABEL: define {{.*}} @"$s30default_function_ir_attributes18OutlinedOperationsVyxGlWOc"( +// CHECK-SAME: [[ATTRS_NOINLINE_NOUNWIND:#[0-9]+]] + +// normal function +// CHECK-LABEL: define {{.*}} @"$s30default_function_ir_attributes3fooyyF"( +// CHECK-SAME: [[ATTRS_SIMPLE]] + +func foo() {} + +// helper function: __swift_instantiateConcreteTypeFromMangledName +// CHECK-LABEL: define {{.*}} @__swift_instantiateConcreteTypeFromMangledName( +// CHECK-SAME: [[ATTRS_NOINLINE_READNONE_NOUNWIND_NOFRAME:#[0-9]+]] + +func use_metadata() -> Any.Type { + return ((C) -> Int).self +} + +// helper function: dynamic_cast_existential_1_unconditional +// CHECK-LABEL: define {{.*}} @dynamic_cast_existential_1_unconditional( +// CHECK-SAME: [[ATTRS_NOUNWIND:#[0-9]+]] + +func test_class_existential_cast_0(value: AnyObject) -> CP0 { + value as! CP0 +} + +// helper function: dynamic_cast_existential_2_unconditional +// CHECK-LABEL: define {{.*}} @dynamic_cast_existential_2_unconditional( +// CHECK-SAME: [[ATTRS_NOUNWIND]] + +func test_class_existential_cast_1(value: AnyObject) -> CP0 & CP1 { + value as! CP0 & CP1 +} + +// helper function: dynamic_cast_existential_2_conditional +// CHECK-LABEL: define {{.*}} @dynamic_cast_existential_2_conditional( +// CHECK-SAME: [[ATTRS_NOUNWIND]] + +func test_class_existential_cast_2(value: AnyObject) -> (CP0 & CP1)? { + value as? CP0 & CP1 +} + +// helper function: dynamic_cast_existential_1_superclass_unconditional +// CHECK-LABEL: define {{.*}} @dynamic_cast_existential_1_superclass_unconditional( +// CHECK-SAME: [[ATTRS_NOUNWIND]] + +func test_class_existential_cast_3(value: AnyObject) -> C & CP0 { + value as! C & CP0 +} + +// metadata accessor +// CHECK-LABEL: define {{.*}} @"$s30default_function_ir_attributes1CCMa"( +// CHECK-SAME: [[ATTRS_NOINLINE_READNONE_NOUNWIND_NOFRAME]] + +// helper function: dynamic_cast_existential_1_superclass_conditional +// CHECK-LABEL: define {{.*}} @dynamic_cast_existential_1_superclass_conditional( +// CHECK-SAME: [[ATTRS_NOUNWIND]] + +func test_class_existential_cast_4(value: AnyObject) -> (C & CP0)? { + value as? C & CP0 +} + +// helper function: SIL-generated key path getter +// CHECK-LABEL: define {{.*}} @"$s30default_function_ir_attributes1SV8computedSivpACTK"( +// CHECK-SAME: [[ATTRS_SIMPLE]] + +// helper function: SIL-generated key path setter +// CHECK-LABEL: define {{.*}} @"$s30default_function_ir_attributes1SV8computedSivpACTk"( +// CHECK-SAME: [[ATTRS_SIMPLE]] + +func test_computed_key_path_sil_thunks() -> KeyPath { + \S.computed +} + +// helper function: IR-generated key path getter +// CHECK-LABEL: define {{.*}} @keypath_get( +// CHECK-SAME: [[ATTRS_SIMPLE]] + +// helper function: IR-generated key path setter +// CHECK-LABEL: define {{.*}} @keypath_set( +// CHECK-SAME: [[ATTRS_SIMPLE]] + +// helper function: IR-generated key path arg layout accessor +// CHECK-LABEL: define {{.*}} @keypath_get_arg_layout( +// CHECK-SAME: [[ATTRS_SIMPLE]] + +// helper function: IR-generated key path destroy function +// CHECK-LABEL: define {{.*}} @keypath_destroy( +// CHECK-SAME: [[ATTRS_SIMPLE]] + +// helper function: IR-generated key path copy function +// CHECK-LABEL: define {{.*}} @keypath_copy( +// CHECK-SAME: [[ATTRS_SIMPLE]] + +// helper function: IR-generated key path equals function +// CHECK-LABEL: define {{.*}} @keypath_equals( +// CHECK-SAME: [[ATTRS_SIMPLE]] + +// helper function: IR-generated key path hash function +// CHECK-LABEL: define {{.*}} @keypath_hash( +// CHECK-SAME: [[ATTRS_SIMPLE]] + +// helper function: IR-generated key path argument initializer +// CHECK-LABEL: define {{.*}} @keypath_arg_init( +// CHECK-SAME: [[ATTRS_SIMPLE]] + +func test_computed_key_path_generic_thunks(value: T) -> KeyPath { + return \S[value] +} + +// helper function: __swift_get_extra_inhabitant_index( +// CHECK-LABEL: define {{.*}} @__swift_get_extra_inhabitant_index( +// CHECK-SAME: [[ATTRS_SIMPLE]] + +// helper function: __swift_store_extra_inhabitant_index( +// CHECK-LABEL: define {{.*}} @__swift_store_extra_inhabitant_index( +// CHECK-SAME: [[ATTRS_SIMPLE]] + +// helper function: __swift_instantiateGenericMetadata +// CHECK-LABEL: define {{.*}} @__swift_instantiateGenericMetadata( +// CHECK-SAME: [[ATTRS_NOINLINE_READNONE_NOUNWIND_NOFRAME]] + +// Use the presence of a target-cpu attribute as a litmus for +// whether constructInitialAttributes was called, since it's very +// unlikely that handrolled code generation would think to add one. +// CHECK: attributes [[ATTRS_SIMPLE]] = { [[CUSTOM_ATTRS:.*target-cpu.*]] }{{$}} +// CHECK-DAG: attributes [[ATTRS_NOINLINE_NOUNWIND]] = { noinline nounwind {{.*target-cpu.*}} } +// CHECK-DAG: attributes [[ATTRS_NOINLINE_READNONE_NOUNWIND_NOFRAME]] = { noinline nounwind readnone {{.*}}"frame-pointer"="none"{{.*target-cpu.*}} } +// CHECK-DAG: attributes [[ATTRS_NOUNWIND]] = { nounwind [[CUSTOM_ATTRS]] }{{$}} diff --git a/test/IRGen/keypaths_objc.sil b/test/IRGen/keypaths_objc.sil index 91c31c8055139..4e8cb200f4e6f 100644 --- a/test/IRGen/keypaths_objc.sil +++ b/test/IRGen/keypaths_objc.sil @@ -45,7 +45,7 @@ entry(%0 : $@objc_metatype C.Type): unreachable } -// CHECK: define private i8* [[SELECTOR_FN]] +// CHECK: define linkonce_odr hidden i8* [[SELECTOR_FN]] // CHECK-NEXT: entry: // CHECK-NEXT: %1 = load {{.*}}selector(x) // CHECK-NEXT: ret i8* %1 diff --git a/test/IRGen/subclass_existentials.sil b/test/IRGen/subclass_existentials.sil index 28295cc27dfa1..efbea61883f6b 100644 --- a/test/IRGen/subclass_existentials.sil +++ b/test/IRGen/subclass_existentials.sil @@ -109,7 +109,7 @@ bb0(%0 : @owned $C, %1 : @owned $C & P): return %result : $() } -// CHECK-LABEL: define private { i8*, i8** } @dynamic_cast_existential_1_superclass_unconditional(i8* %0, %swift.type* %1, %swift.type* +// CHECK-LABEL: define linkonce_odr hidden { i8*, i8** } @dynamic_cast_existential_1_superclass_unconditional(i8* %0, %swift.type* %1, %swift.type* // CHECK: entry: // CHECK-NEXT: [[RESULT:%.*]] = call %swift.type* @swift_dynamicCastMetatype(%swift.type* %1, %swift.type* %2) // CHECK-NEXT: [[IS_SUBCLASS:%.*]] = icmp ne %swift.type* [[RESULT]], null @@ -150,7 +150,7 @@ bb0(%0 : @owned $C): return %result : $() } -// CHECK-LABEL: define private { i8*, i8** } @dynamic_cast_existential_1_unconditional(i8* %0, %swift.type* +// CHECK-LABEL: define linkonce_odr hidden { i8*, i8** } @dynamic_cast_existential_1_unconditional(i8* %0, %swift.type* // CHECK: entry: // CHECK-NEXT: [[WTABLE:%.*]] = call i8** @swift_conformsToProtocol(%swift.type* %1, {{.*}} %2) // CHECK-NEXT: [[IS_NOT_CONFORMING:%.*]] = icmp eq i8** [[WTABLE]], null diff --git a/test/Serialization/autolinking.swift b/test/Serialization/autolinking.swift index 864bd8db62caf..fac766f844928 100644 --- a/test/Serialization/autolinking.swift +++ b/test/Serialization/autolinking.swift @@ -40,10 +40,10 @@ import someModule // NO-FORCE-LOAD-NOT: FORCE_LOAD // NO-FORCE-LOAD-NOT -lmodule // NO-FORCE-LOAD-NOT -lmagic -// FORCE-LOAD: define{{( dllexport)?}} void @"_swift_FORCE_LOAD_$_module"() {{(comdat )?}}{ +// FORCE-LOAD: define{{( dllexport)?}} void @"_swift_FORCE_LOAD_$_module"() {{(comdat )?(#[0-9]+ )?}}{ // FORCE-LOAD: ret void // FORCE-LOAD: } -// FORCE-LOAD-HEX: define{{( dllexport)?}} void @"_swift_FORCE_LOAD_$306d6f64756c65"() {{(comdat )?}}{ +// FORCE-LOAD-HEX: define{{( dllexport)?}} void @"_swift_FORCE_LOAD_$306d6f64756c65"() {{(comdat )?(#[0-9]+ )?}}{ // FORCE-LOAD-HEX: ret void // FORCE-LOAD-HEX: }