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: }