//===--- SILGenBuiltin.cpp - SIL generation for builtin call sites -------===// // // This source file is part of the Swift.org open source project // // Copyright (c) 2014 - 2017 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 "SpecializedEmitter.h" #include "ArgumentSource.h" #include "Cleanup.h" #include "Conversion.h" #include "Initialization.h" #include "LValue.h" #include "RValue.h" #include "Scope.h" #include "SILGenFunction.h" #include "swift/AST/ASTContext.h" #include "swift/AST/Builtins.h" #include "swift/AST/DiagnosticsSIL.h" #include "swift/AST/DistributedDecl.h" #include "swift/AST/FileUnit.h" #include "swift/AST/GenericEnvironment.h" #include "swift/AST/Module.h" #include "swift/AST/ProtocolConformance.h" #include "swift/AST/ReferenceCounting.h" #include "swift/Basic/Assertions.h" #include "swift/SIL/SILArgument.h" #include "swift/SIL/SILUndef.h" #include "swift/AST/TypeCheckRequests.h" // FIXME: Temporary #include "swift/AST/NameLookupRequests.h" // FIXME: Temporary using namespace swift; using namespace Lowering; /// Break down an expression that's the formal argument expression to /// a builtin function, returning its individualized arguments. /// /// Because these are builtin operations, we can make some structural /// assumptions about the expression used to call them. static std::optional> decomposeArguments(SILGenFunction &SGF, SILLocation loc, PreparedArguments &&args, unsigned expectedCount) { SmallVector result; auto sources = std::move(args).getSources(); if (sources.size() == expectedCount) { for (auto &&source : sources) result.push_back(std::move(source).asKnownExpr()); return result; } SGF.SGM.diagnose(loc, diag::invalid_sil_builtin, "argument to builtin should be a literal tuple"); return std::nullopt; } static ManagedValue emitBuiltinRetain(SILGenFunction &SGF, SILLocation loc, SubstitutionMap substitutions, ArrayRef args, SGFContext C) { // The value was produced at +1; we can produce an unbalanced retain simply by // disabling the cleanup. But this would violate ownership semantics. Instead, // we must allow for the cleanup and emit a new unmanaged retain value. SGF.B.createUnmanagedRetainValue(loc, args[0].getValue(), SGF.B.getDefaultAtomicity()); return ManagedValue::forObjectRValueWithoutOwnership(SGF.emitEmptyTuple(loc)); } static ManagedValue emitBuiltinRelease(SILGenFunction &SGF, SILLocation loc, SubstitutionMap substitutions, ArrayRef args, SGFContext C) { // The value was produced at +1, so to produce an unbalanced // release we need to leave the cleanup intact and then do a *second* // release. SGF.B.createUnmanagedReleaseValue(loc, args[0].getValue(), SGF.B.getDefaultAtomicity()); return ManagedValue::forObjectRValueWithoutOwnership(SGF.emitEmptyTuple(loc)); } static ManagedValue emitBuiltinAutorelease(SILGenFunction &SGF, SILLocation loc, SubstitutionMap substitutions, ArrayRef args, SGFContext C) { SGF.B.createUnmanagedAutoreleaseValue(loc, args[0].getValue(), SGF.B.getDefaultAtomicity()); return ManagedValue::forObjectRValueWithoutOwnership(SGF.emitEmptyTuple(loc)); } /// Specialized emitter for Builtin.load and Builtin.take. static ManagedValue emitBuiltinLoadOrTake(SILGenFunction &SGF, SILLocation loc, SubstitutionMap substitutions, ArrayRef args, SGFContext C, IsTake_t isTake, bool isStrict, bool isInvariant, llvm::MaybeAlign align) { assert(substitutions.getReplacementTypes().size() == 1 && "load should have single substitution"); assert(args.size() == 1 && "load should have a single argument"); // The substitution gives the type of the load. This is always a // first-class type; there is no way to e.g. produce a @weak load // with this builtin. auto &rvalueTL = SGF.getTypeLowering(substitutions.getReplacementTypes()[0]); SILType loadedType = rvalueTL.getLoweredType(); // Convert the pointer argument to a SIL address. // // Default to an unaligned pointer. This can be optimized in the presence of // Builtin.assumeAlignment. SILValue addr = SGF.B.createPointerToAddress(loc, args[0].getUnmanagedValue(), loadedType.getAddressType(), isStrict, isInvariant, align); // Perform the load. return SGF.emitLoad(loc, addr, rvalueTL, C, isTake); } static ManagedValue emitBuiltinLoad(SILGenFunction &SGF, SILLocation loc, SubstitutionMap substitutions, ArrayRef args, SGFContext C) { // Regular loads assume natural alignment. return emitBuiltinLoadOrTake(SGF, loc, substitutions, args, C, IsNotTake, /*isStrict*/ true, /*isInvariant*/ false, llvm::MaybeAlign()); } static ManagedValue emitBuiltinLoadRaw(SILGenFunction &SGF, SILLocation loc, SubstitutionMap substitutions, ArrayRef args, SGFContext C) { // Raw loads cannot assume alignment. return emitBuiltinLoadOrTake(SGF, loc, substitutions, args, C, IsNotTake, /*isStrict*/ false, /*isInvariant*/ false, llvm::MaybeAlign(1)); } static ManagedValue emitBuiltinLoadInvariant(SILGenFunction &SGF, SILLocation loc, SubstitutionMap substitutions, ArrayRef args, SGFContext C) { // Regular loads assume natural alignment. return emitBuiltinLoadOrTake(SGF, loc, substitutions, args, C, IsNotTake, /*isStrict*/ false, /*isInvariant*/ true, llvm::MaybeAlign()); } static ManagedValue emitBuiltinTake(SILGenFunction &SGF, SILLocation loc, SubstitutionMap substitutions, ArrayRef args, SGFContext C) { // Regular loads assume natural alignment. return emitBuiltinLoadOrTake(SGF, loc, substitutions, args, C, IsTake, /*isStrict*/ true, /*isInvariant*/ false, llvm::MaybeAlign()); } /// Specialized emitter for Builtin.destroy. static ManagedValue emitBuiltinDestroy(SILGenFunction &SGF, SILLocation loc, SubstitutionMap substitutions, ArrayRef args, SGFContext C) { assert(args.size() == 2 && "destroy should have two arguments"); assert(substitutions.getReplacementTypes().size() == 1 && "destroy should have a single substitution"); // The substitution determines the type of the thing we're destroying. auto &ti = SGF.getTypeLowering(substitutions.getReplacementTypes()[0]); // Destroy is a no-op for trivial types. if (ti.isTrivial()) return ManagedValue::forObjectRValueWithoutOwnership( SGF.emitEmptyTuple(loc)); SILType destroyType = ti.getLoweredType(); // Convert the pointer argument to a SIL address. SILValue addr = SGF.B.createPointerToAddress(loc, args[1].getUnmanagedValue(), destroyType.getAddressType(), /*isStrict*/ true, /*isInvariant*/ false); // Destroy the value indirectly. Canonicalization will promote to loads // and releases if appropriate. SGF.B.createDestroyAddr(loc, addr); return ManagedValue::forObjectRValueWithoutOwnership(SGF.emitEmptyTuple(loc)); } static ManagedValue emitBuiltinStore(SILGenFunction &SGF, SILLocation loc, SubstitutionMap substitutions, ArrayRef args, SGFContext C, bool isStrict, bool isInvariant, llvm::MaybeAlign alignment) { assert(args.size() >= 2 && "should have two arguments"); assert(substitutions.getReplacementTypes().size() == 1 && "should have a single substitution"); // The substitution determines the type of the thing we're destroying. CanType formalTy = substitutions.getReplacementTypes()[0]->getCanonicalType(); SILType loweredTy = SGF.getLoweredType(formalTy); // Convert the destination pointer argument to a SIL address. SILValue addr = SGF.B.createPointerToAddress( loc, args.back().getUnmanagedValue(), loweredTy.getAddressType(), isStrict, isInvariant, alignment); // Build the value to be stored, reconstructing tuples if needed. auto src = RValue(SGF, args.slice(0, args.size() - 1), formalTy); std::move(src).ensurePlusOne(SGF, loc).assignInto(SGF, loc, addr); return ManagedValue::forObjectRValueWithoutOwnership(SGF.emitEmptyTuple(loc)); } static ManagedValue emitBuiltinAssign(SILGenFunction &SGF, SILLocation loc, SubstitutionMap substitutions, ArrayRef args, SGFContext C) { return emitBuiltinStore(SGF, loc, substitutions, args, C, /*isStrict=*/true, /*isInvariant=*/false, llvm::MaybeAlign()); } static ManagedValue emitBuiltinStoreRaw(SILGenFunction &SGF, SILLocation loc, SubstitutionMap substitutions, ArrayRef args, SGFContext C) { return emitBuiltinStore(SGF, loc, substitutions, args, C, /*isStrict=*/false, /*isInvariant=*/false, llvm::MaybeAlign(1)); } /// Emit Builtin.initialize by evaluating the operand directly into /// the address. static ManagedValue emitBuiltinInit(SILGenFunction &SGF, SILLocation loc, SubstitutionMap substitutions, PreparedArguments &&preparedArgs, SGFContext C) { auto argsOrError = decomposeArguments(SGF, loc, std::move(preparedArgs), 2); if (!argsOrError) return ManagedValue::forObjectRValueWithoutOwnership( SGF.emitEmptyTuple(loc)); auto args = *argsOrError; CanType formalType = substitutions.getReplacementTypes()[0]->getCanonicalType(); auto &formalTL = SGF.getTypeLowering(formalType); SILValue addr = SGF.emitRValueAsSingleValue(args[1]).getUnmanagedValue(); addr = SGF.B.createPointerToAddress( loc, addr, formalTL.getLoweredType().getAddressType(), /*isStrict*/ true, /*isInvariant*/ false); TemporaryInitialization init(addr, CleanupHandle::invalid()); SGF.emitExprInto(args[0], &init); return ManagedValue::forObjectRValueWithoutOwnership(SGF.emitEmptyTuple(loc)); } /// Specialized emitter for Builtin.fixLifetime. static ManagedValue emitBuiltinFixLifetime(SILGenFunction &SGF, SILLocation loc, SubstitutionMap substitutions, ArrayRef args, SGFContext C) { for (auto arg : args) { SGF.B.createFixLifetime(loc, arg.getValue()); } return ManagedValue::forObjectRValueWithoutOwnership(SGF.emitEmptyTuple(loc)); } static ManagedValue emitCastToReferenceType(SILGenFunction &SGF, SILLocation loc, SubstitutionMap substitutions, ArrayRef args, SGFContext C, SILType objPointerType) { assert(args.size() == 1 && "cast should have a single argument"); assert(substitutions.getReplacementTypes().size() == 1 && "cast should have a type substitution"); // Bail if the source type is not a class reference of some kind. Type argTy = substitutions.getReplacementTypes()[0]; if (!argTy->mayHaveSuperclass() && !argTy->isClassExistentialType()) { SGF.SGM.diagnose(loc, diag::invalid_sil_builtin, "castToNativeObject source must be a class"); return SGF.emitUndef(objPointerType); } // Grab the argument. ManagedValue arg = args[0]; // If the argument is existential, open it. if (argTy->isClassExistentialType()) { auto openedTy = ExistentialArchetypeType::get(argTy->getCanonicalType()); SILType loweredOpenedTy = SGF.getLoweredLoadableType(openedTy); arg = SGF.B.createOpenExistentialRef(loc, arg, loweredOpenedTy); } // Return the cast result. return SGF.B.createUncheckedRefCast(loc, arg, objPointerType); } /// Specialized emitter for Builtin.unsafeCastToNativeObject. static ManagedValue emitBuiltinUnsafeCastToNativeObject(SILGenFunction &SGF, SILLocation loc, SubstitutionMap substitutions, ArrayRef args, SGFContext C) { return emitCastToReferenceType(SGF, loc, substitutions, args, C, SILType::getNativeObjectType(SGF.F.getASTContext())); } /// Specialized emitter for Builtin.castToNativeObject. static ManagedValue emitBuiltinCastToNativeObject(SILGenFunction &SGF, SILLocation loc, SubstitutionMap substitutions, ArrayRef args, SGFContext C) { auto ty = args[0].getType().getASTType(); (void)ty; assert(ty->getReferenceCounting() == ReferenceCounting::Native && "Can only cast types that use native reference counting to native " "object"); return emitBuiltinUnsafeCastToNativeObject(SGF, loc, substitutions, args, C); } static ManagedValue emitCastFromReferenceType(SILGenFunction &SGF, SILLocation loc, SubstitutionMap substitutions, ArrayRef args, SGFContext C) { assert(args.size() == 1 && "cast should have a single argument"); assert(substitutions.getReplacementTypes().size() == 1 && "cast should have a single substitution"); // The substitution determines the destination type. SILType destType = SGF.getLoweredType(substitutions.getReplacementTypes()[0]); // Bail if the source type is not a class reference of some kind. if (!substitutions.getReplacementTypes()[0]->isBridgeableObjectType() || !destType.isObject()) { SGF.SGM.diagnose(loc, diag::invalid_sil_builtin, "castFromNativeObject dest must be an object type"); // Recover by propagating an undef result. return SGF.emitUndef(destType); } return SGF.B.createUncheckedRefCast(loc, args[0], destType); } /// Specialized emitter for Builtin.castFromNativeObject. static ManagedValue emitBuiltinCastFromNativeObject(SILGenFunction &SGF, SILLocation loc, SubstitutionMap substitutions, ArrayRef args, SGFContext C) { return emitCastFromReferenceType(SGF, loc, substitutions, args, C); } /// Specialized emitter for Builtin.bridgeToRawPointer. static ManagedValue emitBuiltinBridgeToRawPointer(SILGenFunction &SGF, SILLocation loc, SubstitutionMap substitutions, ArrayRef args, SGFContext C) { assert(args.size() == 1 && "bridge should have a single argument"); // Take the reference type argument and cast it to RawPointer. // RawPointers do not have ownership semantics, so the cleanup on the // argument remains. SILType rawPointerType = SILType::getRawPointerType(SGF.F.getASTContext()); SILValue result = SGF.B.createRefToRawPointer(loc, args[0].getValue(), rawPointerType); return ManagedValue::forObjectRValueWithoutOwnership(result); } /// Specialized emitter for Builtin.bridgeFromRawPointer. static ManagedValue emitBuiltinBridgeFromRawPointer(SILGenFunction &SGF, SILLocation loc, SubstitutionMap substitutions, ArrayRef args, SGFContext C) { assert(substitutions.getReplacementTypes().size() == 1 && "bridge should have a single substitution"); assert(args.size() == 1 && "bridge should have a single argument"); // The substitution determines the destination type. // FIXME: Archetype destination type? auto &destLowering = SGF.getTypeLowering(substitutions.getReplacementTypes()[0]); assert(destLowering.isLoadable()); SILType destType = destLowering.getLoweredType(); // Take the raw pointer argument and cast it to the destination type. SILValue result = SGF.B.createRawPointerToRef(loc, args[0].getUnmanagedValue(), destType); // The result has ownership semantics, so retain it with a cleanup. return SGF.emitManagedCopy(loc, result, destLowering); } static ManagedValue emitBuiltinAddressOfBuiltins(SILGenFunction &SGF, SILLocation loc, SubstitutionMap substitutions, PreparedArguments &&preparedArgs, SGFContext C, bool stackProtected) { SILType rawPointerType = SILType::getRawPointerType(SGF.getASTContext()); auto argsOrError = decomposeArguments(SGF, loc, std::move(preparedArgs), 1); if (!argsOrError) return SGF.emitUndef(rawPointerType); auto argument = (*argsOrError)[0]; // If the argument is inout, try forming its lvalue. This builtin only works // if it's trivially physically projectable. auto inout = cast(argument->getSemanticsProvidingExpr()); auto lv = SGF.emitLValue(inout->getSubExpr(), SGFAccessKind::ReadWrite); if (!lv.isPhysical() || !lv.isLoadingPure()) { SGF.SGM.diagnose(argument->getLoc(), diag::non_physical_addressof); return SGF.emitUndef(rawPointerType); } auto addr = SGF.emitAddressOfLValue(argument, std::move(lv)) .getLValueAddress(); // Take the address argument and cast it to RawPointer. SILValue result = SGF.B.createAddressToPointer(loc, addr, rawPointerType, stackProtected); return ManagedValue::forObjectRValueWithoutOwnership(result); } /// Specialized emitter for Builtin.addressof. static ManagedValue emitBuiltinAddressOf(SILGenFunction &SGF, SILLocation loc, SubstitutionMap substitutions, PreparedArguments &&preparedArgs, SGFContext C) { return emitBuiltinAddressOfBuiltins(SGF, loc, substitutions, std::move(preparedArgs), C, /*stackProtected=*/ true); } static ManagedValue emitBuiltinUnprotectedAddressOf(SILGenFunction &SGF, SILLocation loc, SubstitutionMap substitutions, PreparedArguments &&preparedArgs, SGFContext C) { return emitBuiltinAddressOfBuiltins(SGF, loc, substitutions, std::move(preparedArgs), C, /*stackProtected=*/ false); } /// Specialized emitter for Builtin.addressOfBorrow. static ManagedValue emitBuiltinAddressOfBorrowBuiltins(SILGenFunction &SGF, SILLocation loc, SubstitutionMap substitutions, PreparedArguments &&preparedArgs, SGFContext C, bool stackProtected) { SILType rawPointerType = SILType::getRawPointerType(SGF.getASTContext()); auto argsOrError = decomposeArguments(SGF, loc, std::move(preparedArgs), 1); if (!argsOrError) return SGF.emitUndef(rawPointerType); auto argument = (*argsOrError)[0]; SILValue addr; // Try to borrow the argument at +0 indirect. // If the argument is a reference to a borrowed addressable parameter, then // use that parameter's stable address. if (auto addressableAddr = SGF.tryEmitAddressableParameterAsAddress( ArgumentSource(argument), ValueOwnership::Shared)) { addr = addressableAddr.getValue(); } else { // We otherwise only support the builtin applied to values that // are naturally emitted borrowed in memory. (But it would probably be good // to phase this out since it's not really well-defined how long // the resulting pointer is good for without something like addressability.) auto borrow = SGF.emitRValue(argument, SGFContext::AllowGuaranteedPlusZero) .getAsSingleValue(SGF, argument); if (!SGF.F.getConventions().useLoweredAddresses()) { auto &context = SGF.getASTContext(); auto identifier = stackProtected ? context.getIdentifier("addressOfBorrowOpaque") : context.getIdentifier("unprotectedAddressOfBorrowOpaque"); auto builtin = SGF.B.createBuiltin(loc, identifier, rawPointerType, substitutions, {borrow.getValue()}); return ManagedValue::forObjectRValueWithoutOwnership(builtin); } if (!borrow.isPlusZero() || !borrow.getType().isAddress()) { SGF.SGM.diagnose(argument->getLoc(), diag::non_borrowed_indirect_addressof); return SGF.emitUndef(rawPointerType); } addr = borrow.getValue(); } // Take the address argument and cast it to RawPointer. SILValue result = SGF.B.createAddressToPointer(loc, addr, rawPointerType, stackProtected); return ManagedValue::forObjectRValueWithoutOwnership(result); } /// Specialized emitter for Builtin.addressOfBorrow. static ManagedValue emitBuiltinAddressOfBorrow(SILGenFunction &SGF, SILLocation loc, SubstitutionMap substitutions, PreparedArguments &&preparedArgs, SGFContext C) { return emitBuiltinAddressOfBorrowBuiltins(SGF, loc, substitutions, std::move(preparedArgs), C, /*stackProtected=*/ true); } /// Specialized emitter for Builtin.addressOfBorrow. static ManagedValue emitBuiltinUnprotectedAddressOfBorrow(SILGenFunction &SGF, SILLocation loc, SubstitutionMap substitutions, PreparedArguments &&preparedArgs, SGFContext C) { return emitBuiltinAddressOfBorrowBuiltins(SGF, loc, substitutions, std::move(preparedArgs), C, /*stackProtected=*/ false); } /// Specialized emitter for Builtin.gepRaw. static ManagedValue emitBuiltinGepRaw(SILGenFunction &SGF, SILLocation loc, SubstitutionMap substitutions, ArrayRef args, SGFContext C) { assert(args.size() == 2 && "gepRaw should be given two arguments"); SILValue offsetPtr = SGF.B.createIndexRawPointer(loc, args[0].getUnmanagedValue(), args[1].getUnmanagedValue()); return ManagedValue::forObjectRValueWithoutOwnership(offsetPtr); } /// Specialized emitter for Builtin.gep. static ManagedValue emitBuiltinGep(SILGenFunction &SGF, SILLocation loc, SubstitutionMap substitutions, ArrayRef args, SGFContext C) { assert(substitutions.getReplacementTypes().size() == 1 && "gep should have two substitutions"); assert(args.size() == 3 && "gep should be given three arguments"); SILType ElemTy = SGF.getLoweredType(substitutions.getReplacementTypes()[0]); SILType RawPtrType = args[0].getUnmanagedValue()->getType(); SILValue addr = SGF.B.createPointerToAddress(loc, args[0].getUnmanagedValue(), ElemTy.getAddressType(), /*strict*/ true, /*invariant*/ false); addr = SGF.B.createIndexAddr(loc, addr, args[1].getUnmanagedValue(), /*needsStackProtection=*/ true); addr = SGF.B.createAddressToPointer(loc, addr, RawPtrType, /*needsStackProtection=*/ true); return ManagedValue::forObjectRValueWithoutOwnership(addr); } /// Specialized emitter for Builtin.getTailAddr. static ManagedValue emitBuiltinGetTailAddr(SILGenFunction &SGF, SILLocation loc, SubstitutionMap substitutions, ArrayRef args, SGFContext C) { assert(substitutions.getReplacementTypes().size() == 2 && "getTailAddr should have two substitutions"); assert(args.size() == 4 && "gep should be given four arguments"); SILType ElemTy = SGF.getLoweredType(substitutions.getReplacementTypes()[0]); SILType TailTy = SGF.getLoweredType(substitutions.getReplacementTypes()[1]); SILType RawPtrType = args[0].getUnmanagedValue()->getType(); SILValue addr = SGF.B.createPointerToAddress(loc, args[0].getUnmanagedValue(), ElemTy.getAddressType(), /*strict*/ true, /*invariant*/ false); addr = SGF.B.createTailAddr(loc, addr, args[1].getUnmanagedValue(), TailTy.getAddressType()); addr = SGF.B.createAddressToPointer(loc, addr, RawPtrType, /*needsStackProtection=*/ false); return ManagedValue::forObjectRValueWithoutOwnership(addr); } /// Specialized emitter for Builtin.beginUnpairedModifyAccess. static ManagedValue emitBuiltinBeginUnpairedModifyAccess(SILGenFunction &SGF, SILLocation loc, SubstitutionMap substitutions, ArrayRef args, SGFContext C) { assert(substitutions.getReplacementTypes().size() == 1 && "Builtin.beginUnpairedModifyAccess should have one substitution"); assert(args.size() == 3 && "beginUnpairedModifyAccess should be given three arguments"); SILType elemTy = SGF.getLoweredType(substitutions.getReplacementTypes()[0]); SILValue addr = SGF.B.createPointerToAddress(loc, args[0].getUnmanagedValue(), elemTy.getAddressType(), /*strict*/ true, /*invariant*/ false); SILType valueBufferTy = SGF.getLoweredType(SGF.getASTContext().TheUnsafeValueBufferType); SILValue buffer = SGF.B.createPointerToAddress(loc, args[1].getUnmanagedValue(), valueBufferTy.getAddressType(), /*strict*/ true, /*invariant*/ false); SGF.B.createBeginUnpairedAccess(loc, addr, buffer, SILAccessKind::Modify, SILAccessEnforcement::Dynamic, /*noNestedConflict*/ false, /*fromBuiltin*/ true); return ManagedValue::forObjectRValueWithoutOwnership(SGF.emitEmptyTuple(loc)); } /// Specialized emitter for Builtin.performInstantaneousReadAccess static ManagedValue emitBuiltinPerformInstantaneousReadAccess( SILGenFunction &SGF, SILLocation loc, SubstitutionMap substitutions, ArrayRef args, SGFContext C) { assert(substitutions.getReplacementTypes().size() == 1 && "Builtin.performInstantaneousReadAccess should have one substitution"); assert(args.size() == 2 && "Builtin.performInstantaneousReadAccess should be given " "two arguments"); SILType elemTy = SGF.getLoweredType(substitutions.getReplacementTypes()[0]); SILValue addr = SGF.B.createPointerToAddress(loc, args[0].getUnmanagedValue(), elemTy.getAddressType(), /*strict*/ true, /*invariant*/ false); SILType valueBufferTy = SGF.getLoweredType(SGF.getASTContext().TheUnsafeValueBufferType); SILValue unusedBuffer = SGF.emitTemporaryAllocation(loc, valueBufferTy); // Begin an "unscoped" read access. No nested conflict is possible because // the compiler should generate the actual read for the KeyPath expression // immediately after the call to this builtin, which forms the address of // that real access. When noNestedConflict=true, no EndUnpairedAccess should // be emitted. // // Unpaired access is necessary because a BeginAccess/EndAccess pair with no // use will be trivially optimized away. SGF.B.createBeginUnpairedAccess(loc, addr, unusedBuffer, SILAccessKind::Read, SILAccessEnforcement::Dynamic, /*noNestedConflict*/ true, /*fromBuiltin*/ true); return ManagedValue::forObjectRValueWithoutOwnership(SGF.emitEmptyTuple(loc)); } /// Specialized emitter for Builtin.endUnpairedAccessModifyAccess. static ManagedValue emitBuiltinEndUnpairedAccess(SILGenFunction &SGF, SILLocation loc, SubstitutionMap substitutions, ArrayRef args, SGFContext C) { assert(substitutions.empty() && "Builtin.endUnpairedAccess should have no substitutions"); assert(args.size() == 1 && "endUnpairedAccess should be given one argument"); SILType valueBufferTy = SGF.getLoweredType(SGF.getASTContext().TheUnsafeValueBufferType); SILValue buffer = SGF.B.createPointerToAddress(loc, args[0].getUnmanagedValue(), valueBufferTy.getAddressType(), /*strict*/ true, /*invariant*/ false); SGF.B.createEndUnpairedAccess(loc, buffer, SILAccessEnforcement::Dynamic, /*aborted*/ false, /*fromBuiltin*/ true); return ManagedValue::forObjectRValueWithoutOwnership(SGF.emitEmptyTuple(loc)); } /// Specialized emitter for the legacy Builtin.condfail. static ManagedValue emitBuiltinLegacyCondFail(SILGenFunction &SGF, SILLocation loc, SubstitutionMap substitutions, ArrayRef args, SGFContext C) { assert(args.size() == 1 && "condfail should be given one argument"); SGF.B.createCondFail(loc, args[0].getUnmanagedValue(), "unknown runtime failure"); return ManagedValue::forObjectRValueWithoutOwnership(SGF.emitEmptyTuple(loc)); } /// Specialized emitter for Builtin.castReference. static ManagedValue emitBuiltinCastReference(SILGenFunction &SGF, SILLocation loc, SubstitutionMap substitutions, ArrayRef args, SGFContext C) { assert(args.size() == 1 && "castReference should be given one argument"); assert(substitutions.getReplacementTypes().size() == 2 && "castReference should have two subs"); auto fromTy = substitutions.getReplacementTypes()[0]; auto toTy = substitutions.getReplacementTypes()[1]; auto &fromTL = SGF.getTypeLowering(fromTy); auto &toTL = SGF.getTypeLowering(toTy); assert(!fromTL.isTrivial() && !toTL.isTrivial() && "expected ref type"); auto arg = args[0]; // TODO: Fix this API. if (!fromTL.isAddress() || !toTL.isAddress()) { if (SILType::canRefCast(arg.getType(), toTL.getLoweredType(), SGF.SGM.M)) { // Create a reference cast, forwarding the cleanup. // The cast takes the source reference. return SGF.B.createUncheckedRefCast(loc, arg, toTL.getLoweredType()); } } // We are either casting between address-only types, or cannot promote to a // cast of reference values. // // If the from/to types are invalid, then use a cast that will fail at // runtime. We cannot catch these errors with SIL verification because they // may legitimately occur during code specialization on dynamically // unreachable paths. // // TODO: For now, we leave invalid casts in address form so that the runtime // will trap. We could emit a noreturn call here instead which would provide // more information to the optimizer. SILValue srcVal = arg.ensurePlusOne(SGF, loc).forward(SGF); SILValue fromAddr; if (!fromTL.isAddress()) { // Move the loadable value into a "source temp". Since the source and // dest are RC identical, store the reference into the source temp without // a retain. The cast will load the reference from the source temp and // store it into a dest temp effectively forwarding the cleanup. fromAddr = SGF.emitTemporaryAllocation(loc, srcVal->getType()); fromTL.emitStore(SGF.B, loc, srcVal, fromAddr, StoreOwnershipQualifier::Init); } else { // The cast loads directly from the source address. fromAddr = srcVal; } // Create a "dest temp" to hold the reference after casting it. SILValue toAddr = SGF.emitTemporaryAllocation(loc, toTL.getLoweredType()); SGF.B.createUncheckedRefCastAddr(loc, fromAddr, fromTy->getCanonicalType(), toAddr, toTy->getCanonicalType()); // Forward it along and register a cleanup. if (toTL.isAddress()) return SGF.emitManagedBufferWithCleanup(toAddr); // Load the destination value. auto result = toTL.emitLoad(SGF.B, loc, toAddr, LoadOwnershipQualifier::Take); return SGF.emitManagedRValueWithCleanup(result); } /// Specialized emitter for Builtin.reinterpretCast. static ManagedValue emitBuiltinReinterpretCast(SILGenFunction &SGF, SILLocation loc, SubstitutionMap substitutions, ArrayRef args, SGFContext C) { assert(args.size() == 1 && "reinterpretCast should be given one argument"); assert(substitutions.getReplacementTypes().size() == 2 && "reinterpretCast should have two subs"); auto &fromTL = SGF.getTypeLowering(substitutions.getReplacementTypes()[0]); auto &toTL = SGF.getTypeLowering(substitutions.getReplacementTypes()[1]); // If casting between address types, cast the address. if (fromTL.isAddress() || toTL.isAddress()) { SILValue fromAddr; // If the from value is not an address, move it to a buffer. if (!fromTL.isAddress()) { fromAddr = SGF.emitTemporaryAllocation(loc, args[0].getValue()->getType()); fromTL.emitStore(SGF.B, loc, args[0].getValue(), fromAddr, StoreOwnershipQualifier::Init); } else { fromAddr = args[0].getValue(); } auto toAddr = SGF.B.createUncheckedAddrCast(loc, fromAddr, toTL.getLoweredType().getAddressType()); // Load and retain the destination value if it's loadable. Leave the cleanup // on the original value since we don't know anything about it's type. if (!toTL.isAddress()) { return SGF.emitManagedLoadCopy(loc, toAddr, toTL); } // Leave the cleanup on the original value. if (toTL.isTrivial()) return ManagedValue::forTrivialAddressRValue(toAddr); // Initialize the +1 result buffer without taking the incoming value. The // source and destination cleanups will be independent. return SGF.B.bufferForExpr( loc, toTL.getLoweredType(), toTL, C, [&](SILValue bufferAddr) { SGF.B.createCopyAddr(loc, toAddr, bufferAddr, IsNotTake, IsInitialization); }); } // Create the appropriate bitcast based on the source and dest types. ManagedValue in = args[0]; SILType resultTy = toTL.getLoweredType(); return SGF.B.createUncheckedBitCast(loc, in, resultTy); } /// Specialized emitter for Builtin.castToBridgeObject. static ManagedValue emitBuiltinCastToBridgeObject(SILGenFunction &SGF, SILLocation loc, SubstitutionMap subs, ArrayRef args, SGFContext C) { assert(args.size() == 2 && "cast should have two arguments"); assert(subs.getReplacementTypes().size() == 1 && "cast should have a type substitution"); // Take the reference type argument and cast it to BridgeObject. SILType objPointerType = SILType::getBridgeObjectType(SGF.F.getASTContext()); // Bail if the source type is not a class reference of some kind. auto sourceType = subs.getReplacementTypes()[0]; if (!sourceType->mayHaveSuperclass() && !sourceType->isClassExistentialType()) { SGF.SGM.diagnose(loc, diag::invalid_sil_builtin, "castToBridgeObject source must be a class"); return SGF.emitUndef(objPointerType); } ManagedValue ref = args[0]; SILValue bits = args[1].getUnmanagedValue(); // If the argument is existential, open it. if (sourceType->isClassExistentialType()) { auto openedTy = ExistentialArchetypeType::get(sourceType->getCanonicalType()); SILType loweredOpenedTy = SGF.getLoweredLoadableType(openedTy); ref = SGF.B.createOpenExistentialRef(loc, ref, loweredOpenedTy); } return SGF.B.createRefToBridgeObject(loc, ref, bits); } /// Specialized emitter for Builtin.castReferenceFromBridgeObject. static ManagedValue emitBuiltinCastReferenceFromBridgeObject( SILGenFunction &SGF, SILLocation loc, SubstitutionMap subs, ArrayRef args, SGFContext C) { assert(args.size() == 1 && "cast should have one argument"); assert(subs.getReplacementTypes().size() == 1 && "cast should have a type substitution"); // The substitution determines the destination type. auto destTy = subs.getReplacementTypes()[0]; SILType destType = SGF.getLoweredType(destTy); // Bail if the source type is not a class reference of some kind. if (!destTy->isBridgeableObjectType() || !destType.isObject()) { SGF.SGM.diagnose(loc, diag::invalid_sil_builtin, "castReferenceFromBridgeObject dest must be an object type"); // Recover by propagating an undef result. return SGF.emitUndef(destType); } return SGF.B.createBridgeObjectToRef(loc, args[0], destType); } static ManagedValue emitBuiltinCastBitPatternFromBridgeObject( SILGenFunction &SGF, SILLocation loc, SubstitutionMap subs, ArrayRef args, SGFContext C) { assert(args.size() == 1 && "cast should have one argument"); assert(subs.empty() && "cast should not have subs"); SILType wordType = SILType::getBuiltinWordType(SGF.getASTContext()); SILValue result = SGF.B.createBridgeObjectToWord(loc, args[0].getValue(), wordType); return ManagedValue::forObjectRValueWithoutOwnership(result); } static ManagedValue emitBuiltinClassifyBridgeObject(SILGenFunction &SGF, SILLocation loc, SubstitutionMap subs, ArrayRef args, SGFContext C) { assert(args.size() == 1 && "classify should have one argument"); assert(subs.empty() && "classify should not have subs"); SILValue result = SGF.B.createClassifyBridgeObject(loc, args[0].getValue()); return ManagedValue::forObjectRValueWithoutOwnership(result); } static ManagedValue emitBuiltinValueToBridgeObject(SILGenFunction &SGF, SILLocation loc, SubstitutionMap subs, ArrayRef args, SGFContext C) { assert(args.size() == 1 && "ValueToBridgeObject should have one argument"); assert(subs.getReplacementTypes().size() == 1 && "ValueToBridgeObject should have one sub"); Type argTy = subs.getReplacementTypes()[0]; if (!argTy->is()) { SGF.SGM.diagnose(loc, diag::invalid_sil_builtin, "argument to builtin should be a builtin integer"); SILType objPointerType = SILType::getBridgeObjectType(SGF.F.getASTContext()); return SGF.emitUndef(objPointerType); } SILValue result = SGF.B.createValueToBridgeObject(loc, args[0].getValue()); return SGF.emitManagedCopy(loc, result); } // This should only accept as an operand type single-refcounted-pointer types, // class existentials, or single-payload enums (optional). Type checking must be // deferred until IRGen so Builtin.isUnique can be called from a transparent // generic wrapper (we can only type check after specialization). static ManagedValue emitBuiltinIsUnique(SILGenFunction &SGF, SILLocation loc, SubstitutionMap subs, ArrayRef args, SGFContext C) { assert(subs.getReplacementTypes().size() == 1 && "isUnique should have a single substitution"); assert(args.size() == 1 && "isUnique should have a single argument"); assert((args[0].getType().isAddress() && !args[0].hasCleanup()) && "Builtin.isUnique takes an address."); return ManagedValue::forObjectRValueWithoutOwnership( SGF.B.createIsUnique(loc, args[0].getValue())); } // This force-casts the incoming address to NativeObject assuming the caller has // performed all necessary checks. For example, this may directly cast a // single-payload enum to a NativeObject reference. static ManagedValue emitBuiltinIsUnique_native(SILGenFunction &SGF, SILLocation loc, SubstitutionMap subs, ArrayRef args, SGFContext C) { assert(subs.getReplacementTypes().size() == 1 && "isUnique_native should have one sub."); assert(args.size() == 1 && "isUnique_native should have one arg."); auto ToType = SILType::getNativeObjectType(SGF.getASTContext()).getAddressType(); auto toAddr = SGF.B.createUncheckedAddrCast(loc, args[0].getValue(), ToType); SILValue result = SGF.B.createIsUnique(loc, toAddr); return ManagedValue::forObjectRValueWithoutOwnership(result); } static ManagedValue emitBuiltinBeginCOWMutation(SILGenFunction &SGF, SILLocation loc, SubstitutionMap subs, ArrayRef args, SGFContext C) { assert(subs.getReplacementTypes().size() == 1 && "BeginCOWMutation should have one sub."); assert(args.size() == 1 && "isUnique_native should have one arg."); SILValue refAddr = args[0].getValue(); auto *ref = SGF.B.createLoad(loc, refAddr, LoadOwnershipQualifier::Take); BeginCOWMutationInst *beginCOW = SGF.B.createBeginCOWMutation(loc, ref, /*isNative*/ false); SGF.B.createStore(loc, beginCOW->getBufferResult(), refAddr, StoreOwnershipQualifier::Init); return ManagedValue::forObjectRValueWithoutOwnership( beginCOW->getUniquenessResult()); } static ManagedValue emitBuiltinBeginCOWMutation_native(SILGenFunction &SGF, SILLocation loc, SubstitutionMap subs, ArrayRef args, SGFContext C) { assert(subs.getReplacementTypes().size() == 1 && "BeginCOWMutation should have one sub."); assert(args.size() == 1 && "isUnique_native should have one arg."); SILValue refAddr = args[0].getValue(); auto *ref = SGF.B.createLoad(loc, refAddr, LoadOwnershipQualifier::Take); BeginCOWMutationInst *beginCOW = SGF.B.createBeginCOWMutation(loc, ref, /*isNative*/ true); SGF.B.createStore(loc, beginCOW->getBufferResult(), refAddr, StoreOwnershipQualifier::Init); return ManagedValue::forObjectRValueWithoutOwnership( beginCOW->getUniquenessResult()); } static ManagedValue emitBuiltinEndCOWMutation(SILGenFunction &SGF, SILLocation loc, SubstitutionMap subs, ArrayRef args, SGFContext C) { assert(subs.getReplacementTypes().size() == 1 && "EndCOWMutation should have one sub."); assert(args.size() == 1 && "isUnique_native should have one arg."); SILValue refAddr = args[0].getValue(); auto ref = SGF.B.createLoad(loc, refAddr, LoadOwnershipQualifier::Take); auto endRef = SGF.B.createEndCOWMutation(loc, ref, /*keepUnique=*/ false); SGF.B.createStore(loc, endRef, refAddr, StoreOwnershipQualifier::Init); return ManagedValue::forObjectRValueWithoutOwnership(SGF.emitEmptyTuple(loc)); } static ManagedValue emitBuiltinBindMemory(SILGenFunction &SGF, SILLocation loc, SubstitutionMap subs, ArrayRef args, SGFContext C) { assert(subs.getReplacementTypes().size() == 1 && "bindMemory should have a single substitution"); assert(args.size() == 3 && "bindMemory should have three arguments"); // The substitution determines the element type for bound memory. CanType boundFormalType = subs.getReplacementTypes()[0]->getCanonicalType(); SILType boundType = SGF.getLoweredType(boundFormalType); auto *bindMemory = SGF.B.createBindMemory(loc, args[0].getValue(), args[1].getValue(), boundType); return ManagedValue::forObjectRValueWithoutOwnership(bindMemory); } static ManagedValue emitBuiltinRebindMemory(SILGenFunction &SGF, SILLocation loc, SubstitutionMap subs, ArrayRef args, SGFContext C) { assert(subs.empty() && "rebindMemory should have no substitutions"); assert(args.size() == 2 && "rebindMemory should have two arguments"); auto *rebindMemory = SGF.B.createRebindMemory(loc, args[0].getValue(), args[1].getValue()); return ManagedValue::forObjectRValueWithoutOwnership(rebindMemory); } static ManagedValue emitBuiltinAllocWithTailElems(SILGenFunction &SGF, SILLocation loc, SubstitutionMap subs, ArrayRef args, SGFContext C) { unsigned NumTailTypes = subs.getReplacementTypes().size() - 1; assert(args.size() == NumTailTypes * 2 + 1 && "wrong number of substitutions for allocWithTailElems"); // The substitution determines the element type for bound memory. auto replacementTypes = subs.getReplacementTypes(); SILType RefType = SGF.getLoweredType(replacementTypes[0]-> getCanonicalType()).getObjectType(); SmallVector Counts; SmallVector ElemTypes; for (unsigned Idx = 0; Idx < NumTailTypes; ++Idx) { Counts.push_back(args[Idx * 2 + 1]); ElemTypes.push_back(SGF.getLoweredType(replacementTypes[Idx+1]-> getCanonicalType()).getObjectType()); } ManagedValue Metatype = args[0]; if (isa(Metatype)) { auto InstanceType = Metatype.getType().castTo().getInstanceType(); assert(InstanceType == RefType.getASTType() && "substituted type does not match operand metatype"); (void) InstanceType; return SGF.B.createAllocRef(loc, RefType, false, ElemTypes, Counts); } else { return SGF.B.createAllocRefDynamic(loc, Metatype, RefType, false, ElemTypes, Counts); } } static ManagedValue emitBuiltinProjectTailElems(SILGenFunction &SGF, SILLocation loc, SubstitutionMap subs, ArrayRef args, SGFContext C) { assert(subs.getReplacementTypes().size() == 2 && "allocWithTailElems should have two substitutions"); assert(args.size() == 2 && "allocWithTailElems should have three arguments"); // The substitution determines the element type for bound memory. SILType ElemType = SGF.getLoweredType(subs.getReplacementTypes()[1]-> getCanonicalType()).getObjectType(); SILValue result = SGF.B.createRefTailAddr( loc, args[0].borrow(SGF, loc).getValue(), ElemType.getAddressType()); SILType rawPointerType = SILType::getRawPointerType(SGF.F.getASTContext()); result = SGF.B.createAddressToPointer(loc, result, rawPointerType, /*needsStackProtection=*/ false); return ManagedValue::forObjectRValueWithoutOwnership(result); } /// Specialized emitter for type traits. template static ManagedValue emitBuiltinTypeTrait(SILGenFunction &SGF, SILLocation loc, SubstitutionMap substitutions, ArrayRef args, SGFContext C) { assert(substitutions.getReplacementTypes().size() == 1 && "type trait should take a single type parameter"); assert(args.size() == 1 && "type trait should take a single argument"); unsigned result; auto traitTy = substitutions.getReplacementTypes()[0]->getCanonicalType(); switch ((traitTy.getPointer()->*Trait)()) { // If the type obviously has or lacks the trait, emit a constant result. case TypeTraitResult::IsNot: result = 0; break; case TypeTraitResult::Is: result = 1; break; // If not, emit the builtin call normally. Specialization may be able to // eliminate it later, or we'll lower it away at IRGen time. case TypeTraitResult::CanBe: { auto &C = SGF.getASTContext(); auto int8Ty = BuiltinIntegerType::get(8, C)->getCanonicalType(); auto apply = SGF.B.createBuiltin(loc, C.getIdentifier(getBuiltinName(Kind)), SILType::getPrimitiveObjectType(int8Ty), substitutions, args[0].getValue()); return ManagedValue::forObjectRValueWithoutOwnership(apply); } } // Produce the result as an integer literal constant. auto val = SGF.B.createIntegerLiteral( loc, SILType::getBuiltinIntegerType(8, SGF.getASTContext()), (uintmax_t)result); return ManagedValue::forObjectRValueWithoutOwnership(val); } static ManagedValue emitBuiltinAutoDiffApplyDerivativeFunction( AutoDiffDerivativeFunctionKind kind, unsigned arity, bool throws, SILGenFunction &SGF, SILLocation loc, SubstitutionMap substitutions, ArrayRef args, SGFContext C) { // FIXME(https://github.com/apple/swift/issues/54259): Support throwing functions. assert(!throws && "Throwing functions are not yet supported"); auto origFnVal = args[0]; SmallVector origFnArgVals; for (auto& arg : args.drop_front(1)) origFnArgVals.push_back(arg.getValue()); auto origFnType = origFnVal.getType().castTo(); auto origFnUnsubstType = origFnType->getUnsubstitutedType(SGF.getModule()); if (origFnType != origFnUnsubstType) { origFnVal = SGF.B.createConvertFunction( loc, origFnVal, SILType::getPrimitiveObjectType(origFnUnsubstType), /*withoutActuallyEscaping*/ false); } // Get the derivative function. origFnVal = SGF.B.createBeginBorrow(loc, origFnVal); SILValue derivativeFn = SGF.B.createDifferentiableFunctionExtract( loc, kind, origFnVal.getValue()); auto derivativeFnType = derivativeFn->getType().castTo(); assert(derivativeFnType->getNumResults() == 2); assert(derivativeFnType->getNumParameters() == origFnArgVals.size()); auto derivativeFnUnsubstType = derivativeFnType->getUnsubstitutedType(SGF.getModule()); if (derivativeFnType != derivativeFnUnsubstType) { derivativeFn = SGF.B.createConvertFunction( loc, derivativeFn, SILType::getPrimitiveObjectType(derivativeFnUnsubstType), /*withoutActuallyEscaping*/ false); } // We don't need to destroy the original function or retain the // `derivativeFn`, because they are noescape. assert(origFnType->isTrivialNoEscape()); assert(derivativeFnType->isTrivialNoEscape()); // Do the apply for the indirect result case. if (derivativeFnType->hasIndirectFormalResults() && SGF.SGM.M.useLoweredAddresses()) { auto indResBuffer = SGF.getBufferForExprResult( loc, derivativeFnType->getAllResultsInterfaceType(), C); SmallVector applyArgs; applyArgs.push_back(SGF.B.createTupleElementAddr(loc, indResBuffer, 0)); for (auto origFnArgVal : origFnArgVals) applyArgs.push_back(origFnArgVal); auto differential = SGF.B.createApply(loc, derivativeFn, SubstitutionMap(), applyArgs); derivativeFn = SILValue(); SGF.B.createStore(loc, differential, SGF.B.createTupleElementAddr(loc, indResBuffer, 1), StoreOwnershipQualifier::Init); return SGF.manageBufferForExprResult( indResBuffer, SGF.getTypeLowering(indResBuffer->getType()), C); } // Do the apply for the direct result case. auto resultTuple = SGF.B.createApply( loc, derivativeFn, SubstitutionMap(), origFnArgVals); derivativeFn = SILValue(); return SGF.emitManagedRValueWithCleanup(resultTuple); } static ManagedValue emitBuiltinAutoDiffApplyTransposeFunction( unsigned arity, bool throws, SILGenFunction &SGF, SILLocation loc, SubstitutionMap substitutions, ArrayRef args, SGFContext C) { // FIXME(https://github.com/apple/swift/issues/54259): Support throwing functions. assert(!throws && "Throwing functions are not yet supported"); auto origFnVal = args.front().getValue(); SmallVector origFnArgVals; for (auto &arg : args.drop_front(1)) origFnArgVals.push_back(arg.getValue()); // Get the transpose function. SILValue transposeFn = SGF.B.createLinearFunctionExtract( loc, LinearDifferentiableFunctionTypeComponent::Transpose, origFnVal); auto transposeFnType = transposeFn->getType().castTo(); auto transposeFnUnsubstType = transposeFnType->getUnsubstitutedType(SGF.getModule()); if (transposeFnType != transposeFnUnsubstType) { transposeFn = SGF.B.createConvertFunction( loc, transposeFn, SILType::getPrimitiveObjectType(transposeFnUnsubstType), /*withoutActuallyEscaping*/ false); transposeFnType = transposeFn->getType().castTo(); } SmallVector applyArgs; if (transposeFnType->hasIndirectFormalResults()) applyArgs.push_back( SGF.getBufferForExprResult( loc, transposeFnType->getAllResultsInterfaceType(), C)); for (auto paramArg : args.drop_front()) { applyArgs.push_back(paramArg.getValue()); } auto *apply = SGF.B.createApply( loc, transposeFn, SubstitutionMap(), applyArgs); if (transposeFnType->hasIndirectFormalResults()) { auto resultAddress = applyArgs.front(); AbstractionPattern pattern( SGF.F.getLoweredFunctionType()->getSubstGenericSignature(), resultAddress->getType().getASTType()); auto &tl = SGF.getTypeLowering(pattern, resultAddress->getType().getASTType()); return SGF.manageBufferForExprResult(resultAddress, tl, C); } else { return SGF.emitManagedRValueWithCleanup(apply); } } static ManagedValue emitBuiltinApplyDerivative( SILGenFunction &SGF, SILLocation loc, SubstitutionMap substitutions, ArrayRef args, SGFContext C) { auto *callExpr = loc.castToASTNode(); auto builtinDecl = cast(cast( cast(callExpr->getDirectCallee())->getRHS()) ->getDecl()); const auto builtinName = builtinDecl->getBaseIdentifier().str(); AutoDiffDerivativeFunctionKind kind; unsigned arity; bool throws; auto successfullyParsed = autodiff::getBuiltinApplyDerivativeConfig( builtinName, kind, arity, throws); assert(successfullyParsed); return emitBuiltinAutoDiffApplyDerivativeFunction( kind, arity, throws, SGF, loc, substitutions, args, C); } static ManagedValue emitBuiltinApplyTranspose( SILGenFunction &SGF, SILLocation loc, SubstitutionMap substitutions, ArrayRef args, SGFContext C) { auto *callExpr = loc.castToASTNode(); auto builtinDecl = cast(cast( cast(callExpr->getDirectCallee())->getRHS()) ->getDecl()); const auto builtinName = builtinDecl->getBaseIdentifier().str(); unsigned arity; bool throws; auto successfullyParsed = autodiff::getBuiltinApplyTransposeConfig( builtinName, arity, throws); assert(successfullyParsed); return emitBuiltinAutoDiffApplyTransposeFunction( arity, throws, SGF, loc, substitutions, args, C); } /// Emit SIL for the named builtin: globalStringTablePointer. Unlike the default /// ownership convention for named builtins, which is to take (non-trivial) /// arguments as Owned, this builtin accepts owned as well as guaranteed /// arguments, and hence doesn't require the arguments to be at +1. Therefore, /// this builtin is emitted specially. static ManagedValue emitBuiltinGlobalStringTablePointer(SILGenFunction &SGF, SILLocation loc, SubstitutionMap subs, ArrayRef args, SGFContext C) { assert(args.size() == 1); SILValue argValue = args[0].getValue(); auto &astContext = SGF.getASTContext(); Identifier builtinId = astContext.getIdentifier( getBuiltinName(BuiltinValueKind::GlobalStringTablePointer)); auto resultVal = SGF.B.createBuiltin(loc, builtinId, SILType::getRawPointerType(astContext), subs, ArrayRef(argValue)); return SGF.emitManagedRValueWithCleanup(resultVal); } /// Emit SIL for the named builtin: /// convertStrongToUnownedUnsafe. Unlike the default ownership /// convention for named builtins, which is to take (non-trivial) /// arguments as Owned, this builtin accepts owned as well as /// guaranteed arguments, and hence doesn't require the arguments to /// be at +1. Therefore, this builtin is emitted specially. /// /// We assume our convention is (T, @inout @unmanaged T) -> () static ManagedValue emitBuiltinConvertStrongToUnownedUnsafe( SILGenFunction &SGF, SILLocation loc, SubstitutionMap subs, PreparedArguments &&preparedArgs, SGFContext C) { auto argsOrError = decomposeArguments(SGF, loc, std::move(preparedArgs), 2); if (!argsOrError) return ManagedValue::forObjectRValueWithoutOwnership( SGF.emitEmptyTuple(loc)); auto args = *argsOrError; // First get our object at +0 if we can. auto object = SGF.emitRValue(args[0], SGFContext::AllowGuaranteedPlusZero) .getAsSingleValue(SGF, args[0]); // Borrow it and get the value. SILValue objectSrcValue = object.borrow(SGF, loc).getValue(); // Then create our inout. auto inout = cast(args[1]->getSemanticsProvidingExpr()); auto lv = SGF.emitLValue(inout->getSubExpr(), SGFAccessKind::BorrowedAddressRead); lv.unsafelyDropLastComponent(PathComponent::OwnershipKind); if (!lv.isPhysical() || !lv.isLoadingPure()) { llvm::report_fatal_error("Builtin.convertStrongToUnownedUnsafe passed " "non-physical, non-pure lvalue as 2nd arg"); } SILValue inoutDest = SGF.emitAddressOfLValue(args[1], std::move(lv)).getLValueAddress(); SILType destType = inoutDest->getType().getObjectType(); // Make sure our types match up as we expect. if (objectSrcValue->getType() != destType.getReferenceStorageReferentType().getObjectType()) { llvm::errs() << "Invalid usage of Builtin.convertStrongToUnownedUnsafe. lhsType " "must be T and rhsType must be inout unsafe(unowned) T" << "lhsType: " << objectSrcValue->getType() << "\n" << "rhsType: " << inoutDest->getType() << "\n"; llvm::report_fatal_error("standard fatal error msg"); } SILType unmanagedOptType = objectSrcValue->getType().getReferenceStorageType( SGF.getASTContext(), ReferenceOwnership::Unmanaged); SILValue unownedObjectSrcValue = SGF.B.createRefToUnmanaged( loc, objectSrcValue, unmanagedOptType.getObjectType()); SGF.B.emitStoreValueOperation(loc, unownedObjectSrcValue, inoutDest, StoreOwnershipQualifier::Trivial); return ManagedValue::forObjectRValueWithoutOwnership(SGF.emitEmptyTuple(loc)); } /// Emit SIL for the named builtin: convertUnownedUnsafeToGuaranteed. /// /// We assume our convention is: /// /// (BaseT, @inout @unowned(unsafe) T) -> @guaranteed T /// static ManagedValue emitBuiltinConvertUnownedUnsafeToGuaranteed( SILGenFunction &SGF, SILLocation loc, SubstitutionMap subs, PreparedArguments &&preparedArgs, SGFContext C) { auto argsOrError = decomposeArguments(SGF, loc, std::move(preparedArgs), 2); if (!argsOrError) return ManagedValue::forObjectRValueWithoutOwnership( SGF.emitEmptyTuple(loc)); auto args = *argsOrError; // First grab our base and borrow it. auto baseMV = SGF.emitRValueAsSingleValue(args[0], SGFContext::AllowGuaranteedPlusZero) .borrow(SGF, args[0]); // Then grab our LValue operand, drop the last ownership component. auto srcLV = SGF.emitLValue(args[1]->getSemanticsProvidingExpr(), SGFAccessKind::BorrowedAddressRead); srcLV.unsafelyDropLastComponent(PathComponent::OwnershipKind); if (!srcLV.isPhysical() || !srcLV.isLoadingPure()) { llvm::report_fatal_error("Builtin.convertUnownedUnsafeToGuaranteed passed " "non-physical, non-pure lvalue as 2nd arg"); } // Grab our address and load our unmanaged and convert it to a ref. SILValue srcAddr = SGF.emitAddressOfLValue(args[1], std::move(srcLV)).getLValueAddress(); SILValue srcValue = SGF.B.emitLoadValueOperation( loc, srcAddr, LoadOwnershipQualifier::Trivial); SILValue unownedNonTrivialRef = SGF.B.createUnmanagedToRef( loc, srcValue, srcValue->getType().getReferenceStorageReferentType()); // Now convert our unownedNonTrivialRef from unowned ownership to guaranteed // ownership and create a cleanup for it. SILValue guaranteedNonTrivialRef = SGF.B.createUncheckedOwnershipConversion( loc, unownedNonTrivialRef, OwnershipKind::Guaranteed); auto guaranteedNonTrivialRefMV = SGF.emitManagedBorrowedRValueWithCleanup(guaranteedNonTrivialRef); // Now create a mark dependence on our base and return the result. return SGF.B.createMarkDependence(loc, guaranteedNonTrivialRefMV, baseMV, MarkDependenceKind::Escaping); } // Emit SIL for the named builtin: getCurrentAsyncTask. static ManagedValue emitBuiltinGetCurrentAsyncTask( SILGenFunction &SGF, SILLocation loc, SubstitutionMap subs, PreparedArguments &&preparedArgs, SGFContext C) { ASTContext &ctx = SGF.getASTContext(); auto apply = SGF.B.createBuiltin( loc, ctx.getIdentifier(getBuiltinName(BuiltinValueKind::GetCurrentAsyncTask)), SGF.getLoweredType(ctx.TheNativeObjectType), SubstitutionMap(), { }); return SGF.emitManagedRValueWithEndLifetimeCleanup(apply); } // Emit SIL for the named builtin: cancelAsyncTask. static ManagedValue emitBuiltinCancelAsyncTask( SILGenFunction &SGF, SILLocation loc, SubstitutionMap subs, ArrayRef args, SGFContext C) { return SGF.emitCancelAsyncTask(loc, args[0].borrow(SGF, loc).forward(SGF)); } // Emit SIL for the named builtin: endAsyncLet. static ManagedValue emitBuiltinEndAsyncLet( SILGenFunction &SGF, SILLocation loc, SubstitutionMap subs, ArrayRef args, SGFContext C) { return SGF.emitCancelAsyncTask(loc, args[0].borrow(SGF, loc).forward(SGF)); } // Emit SIL for the named builtin: getCurrentExecutor. static ManagedValue emitBuiltinGetCurrentExecutor( SILGenFunction &SGF, SILLocation loc, SubstitutionMap subs, PreparedArguments &&preparedArgs, SGFContext C) { // We don't support this builtin anymore in SILGen. // TODO: just remove it? SGF.SGM.diagnose(loc, diag::unsupported_sil_builtin, getBuiltinName(BuiltinValueKind::GetCurrentExecutor)); auto &ctx = SGF.getASTContext(); auto executorType = SILType::getPrimitiveObjectType(ctx.TheExecutorType); auto optionalExecutorType = SILType::getOptionalType(executorType); return SGF.emitUndef(optionalExecutorType); } // Emit SIL for sizeof/strideof/alignof. // These formally take a metatype argument that's never actually used, so // we ignore it. static ManagedValue emitBuiltinSizeof( SILGenFunction &SGF, SILLocation loc, SubstitutionMap subs, PreparedArguments &&preparedArgs, SGFContext C) { auto &ctx = SGF.getASTContext(); return ManagedValue::forObjectRValueWithoutOwnership(SGF.B.createBuiltin( loc, ctx.getIdentifier(getBuiltinName(BuiltinValueKind::Sizeof)), SILType::getBuiltinWordType(ctx), subs, {})); } static ManagedValue emitBuiltinStrideof( SILGenFunction &SGF, SILLocation loc, SubstitutionMap subs, PreparedArguments &&preparedArgs, SGFContext C) { auto &ctx = SGF.getASTContext(); return ManagedValue::forObjectRValueWithoutOwnership(SGF.B.createBuiltin( loc, ctx.getIdentifier(getBuiltinName(BuiltinValueKind::Strideof)), SILType::getBuiltinWordType(ctx), subs, {})); } static ManagedValue emitBuiltinAlignof( SILGenFunction &SGF, SILLocation loc, SubstitutionMap subs, PreparedArguments &&preparedArgs, SGFContext C) { auto &ctx = SGF.getASTContext(); return ManagedValue::forObjectRValueWithoutOwnership(SGF.B.createBuiltin( loc, ctx.getIdentifier(getBuiltinName(BuiltinValueKind::Alignof)), SILType::getBuiltinWordType(ctx), subs, {})); } enum class CreateTaskOptions { /// The builtin has optional arguments for everything. OptionalEverything = 0x1, /// The builtin is for a DiscardingTaskGroup and so expects the function /// to return Void. Discarding = 0x2, /// The builtin has a non-optional TaskGroup argument. TaskGroup = 0x4, /// The builtin has a non-optional TaskExecutor argument. TaskExecutor = 0x8, }; /// Emit SIL for the various createAsyncTask builtins. static ManagedValue emitCreateAsyncTask(SILGenFunction &SGF, SILLocation loc, SubstitutionMap subs, PreparedArguments &&preparedArgs, OptionSet options) { #ifndef NDEBUG if (options & CreateTaskOptions::Discarding) { assert(!subs); } else { assert(subs && subs.getReplacementTypes().size() == 1); } #endif ASTContext &ctx = SGF.getASTContext(); auto args = std::move(preparedArgs).getSources(); unsigned nextArgIdx = 0; auto nextArg = [&]() -> ArgumentSource && { return std::move(args[nextArgIdx++]); }; auto emitOptionalSome = [&](ArgumentSource &&arg) { auto loc = arg.getLocation(); auto value = std::move(arg).getAsSingleValue(SGF); return SGF.B.createOptionalSome(loc, value); }; auto emitOptionalNone = [&](CanType argType) { auto ty = SGF.getLoweredType(argType.wrapInOptionalType()); return SGF.B.createManagedOptionalNone(loc, ty); }; ManagedValue flags = nextArg().getAsSingleValue(SGF); ManagedValue initialExecutor = [&] { if (options & CreateTaskOptions::OptionalEverything) { return nextArg().getAsSingleValue(SGF); } else { return emitOptionalNone(ctx.TheExecutorType); } }(); ManagedValue taskGroup = [&] { if (options & CreateTaskOptions::OptionalEverything) { return nextArg().getAsSingleValue(SGF); } else if (options & CreateTaskOptions::TaskGroup) { return emitOptionalSome(nextArg()); } else { return emitOptionalNone(ctx.TheRawPointerType); } }(); ManagedValue taskExecutorDeprecated = [&] { if (options & CreateTaskOptions::OptionalEverything) { return nextArg().getAsSingleValue(SGF); } else if (options & CreateTaskOptions::TaskExecutor) { return emitOptionalSome(nextArg()); } else { return emitOptionalNone(ctx.TheExecutorType); } }(); ManagedValue taskExecutorConsuming = [&] { if (options & CreateTaskOptions::OptionalEverything) { return nextArg().getAsSingleValue(SGF); } else if (auto theTaskExecutorProto = ctx.getProtocol(KnownProtocolKind::TaskExecutor)) { return emitOptionalNone(theTaskExecutorProto->getDeclaredExistentialType() ->getCanonicalType()); } else { // This builtin executor type here is just a placeholder type for being // able to pass 'nil' for it with SDKs which do not have the TaskExecutor // type. return emitOptionalNone(ctx.TheExecutorType); } }(); ManagedValue taskName = [&] { if (options & CreateTaskOptions::OptionalEverything) { return nextArg().getAsSingleValue(SGF); } else { return emitOptionalNone(ctx.TheRawPointerType); } }(); auto functionValue = [&] { // No reabstraction required. if (options & CreateTaskOptions::Discarding) { return nextArg().getAsSingleValue(SGF); } // We need to emit the function properly reabstracted. // This generally isn't a problem because this builtin is used in a // generic context, but we can be safe. auto &&fnArg = nextArg(); bool hasSending = ctx.LangOpts.hasFeature(Feature::SendingArgsAndResults); auto extInfo = ASTExtInfoBuilder() .withAsync() .withThrows() .withSendable(!hasSending) .withRepresentation(GenericFunctionType::Representation::Swift) .build(); auto genericSig = subs.getGenericSignature().getCanonicalSignature(); auto genericResult = SGF.getASTContext().TheSelfType; // () async throws -> T CanType functionTy = GenericFunctionType::get(genericSig, {}, genericResult, extInfo) ->getCanonicalType(); AbstractionPattern origParamType(genericSig, functionTy); CanType substParamType = fnArg.getSubstRValueType(); auto loweredParamTy = SGF.getLoweredType(origParamType, substParamType); // The main actor path doesn't give us a value that actually matches the // formal type at all, so this is the best we can do. SILType loweredSubstParamTy; if (fnArg.isRValue()) { loweredSubstParamTy = fnArg.peekRValue().getTypeOfSingleValue(); } else { loweredSubstParamTy = SGF.getLoweredType(substParamType); } auto conversion = Conversion::getSubstToOrig(origParamType, substParamType, loweredSubstParamTy, loweredParamTy); return std::move(fnArg).getConverted(SGF, conversion); }(); assert(nextArgIdx == args.size() && "didn't exhaust builtin arguments?"); SILValue builtinArgs[] = { flags.getUnmanagedValue(), initialExecutor.getUnmanagedValue(), taskGroup.getUnmanagedValue(), taskExecutorDeprecated.getUnmanagedValue(), taskExecutorConsuming.forward(SGF), taskName.forward(SGF), functionValue.forward(SGF) }; auto builtinID = ctx.getIdentifier(getBuiltinName(BuiltinValueKind::CreateAsyncTask)); auto resultTy = SGF.getLoweredType(getAsyncTaskAndContextType(ctx)); auto apply = SGF.B.createBuiltin(loc, builtinID, resultTy, subs, builtinArgs); return SGF.emitManagedRValueWithCleanup(apply); } // Emit SIL for the named builtin: createAsyncTaskInGroup. static ManagedValue emitBuiltinCreateAsyncTaskInGroup( SILGenFunction &SGF, SILLocation loc, SubstitutionMap subs, PreparedArguments &&args, SGFContext C) { return emitCreateAsyncTask(SGF, loc, subs, std::move(args), { CreateTaskOptions::TaskGroup }); } // Emit SIL for the named builtin: createAsyncDiscardingTaskInGroup. static ManagedValue emitBuiltinCreateAsyncDiscardingTaskInGroup( SILGenFunction &SGF, SILLocation loc, SubstitutionMap subs, PreparedArguments &&args, SGFContext C) { return emitCreateAsyncTask(SGF, loc, subs, std::move(args), { CreateTaskOptions::TaskGroup, CreateTaskOptions::Discarding }); } // Emit SIL for the named builtin: createAsyncTaskWithExecutor. static ManagedValue emitBuiltinCreateAsyncTaskWithExecutor( SILGenFunction &SGF, SILLocation loc, SubstitutionMap subs, PreparedArguments &&args, SGFContext C) { return emitCreateAsyncTask(SGF, loc, subs, std::move(args), { CreateTaskOptions::TaskExecutor }); } // Emit SIL for the named builtin: createAsyncTaskInGroupWithExecutor. static ManagedValue emitBuiltinCreateAsyncTaskInGroupWithExecutor( SILGenFunction &SGF, SILLocation loc, SubstitutionMap subs, PreparedArguments &&args, SGFContext C) { return emitCreateAsyncTask(SGF, loc, subs, std::move(args), { CreateTaskOptions::TaskGroup, CreateTaskOptions::TaskExecutor }); } // Emit SIL for the named builtin: createAsyncTaskInGroupWithExecutor. static ManagedValue emitBuiltinCreateAsyncDiscardingTaskInGroupWithExecutor( SILGenFunction &SGF, SILLocation loc, SubstitutionMap subs, PreparedArguments &&args, SGFContext C) { return emitCreateAsyncTask(SGF, loc, subs, std::move(args), { CreateTaskOptions::TaskGroup, CreateTaskOptions::TaskExecutor, CreateTaskOptions::Discarding }); } // Emit SIL for the named builtin: createAsyncTask. static ManagedValue emitBuiltinCreateAsyncTask( SILGenFunction &SGF, SILLocation loc, SubstitutionMap subs, PreparedArguments &&args, SGFContext C) { return emitCreateAsyncTask(SGF, loc, subs, std::move(args), {}); } // Emit SIL for the named builtin: createTask. static ManagedValue emitBuiltinCreateTask( SILGenFunction &SGF, SILLocation loc, SubstitutionMap subs, PreparedArguments &&args, SGFContext C) { return emitCreateAsyncTask(SGF, loc, subs, std::move(args), { CreateTaskOptions::OptionalEverything }); } // Emit SIL for the named builtin: createDiscardingTask. static ManagedValue emitBuiltinCreateDiscardingTask( SILGenFunction &SGF, SILLocation loc, SubstitutionMap subs, PreparedArguments &&args, SGFContext C) { return emitCreateAsyncTask(SGF, loc, subs, std::move(args), { CreateTaskOptions::OptionalEverything, CreateTaskOptions::Discarding }); } // Emit SIL for the named builtin: createTaskGroup. // These formally take a metatype argument that's never actually used, so // we ignore it. static ManagedValue emitBuiltinCreateTaskGroup(SILGenFunction &SGF, SILLocation loc, SubstitutionMap subs, ArrayRef args, SGFContext C) { auto &ctx = SGF.getASTContext(); auto resultType = SILType::getRawPointerType(ctx); auto value = SGF.B.createBuiltin( loc, ctx.getIdentifier(getBuiltinName(BuiltinValueKind::CreateTaskGroup)), resultType, subs, {}); return ManagedValue::forObjectRValueWithoutOwnership(value); } // Emit SIL for the named builtin: createTaskGroupWithFlags. // These formally take a metatype argument that's never actually used, so // we ignore it. static ManagedValue emitBuiltinCreateTaskGroupWithFlags( SILGenFunction &SGF, SILLocation loc, SubstitutionMap subs, ArrayRef args, SGFContext C) { auto &ctx = SGF.getASTContext(); auto resultType = SILType::getRawPointerType(ctx); auto value = SGF.B.createBuiltin( loc, ctx.getIdentifier( getBuiltinName(BuiltinValueKind::CreateTaskGroupWithFlags)), resultType, subs, {args[0].getValue()}); return ManagedValue::forObjectRValueWithoutOwnership(value); } ManagedValue SILGenFunction::emitCreateAsyncMainTask(SILLocation loc, SubstitutionMap subs, ManagedValue flags, ManagedValue mainFunctionRef) { auto &ctx = getASTContext(); CanType flagsType = ctx.getIntType()->getCanonicalType(); bool hasSending = ctx.LangOpts.hasFeature(Feature::SendingArgsAndResults); CanType functionType = FunctionType::get( {}, ctx.TheEmptyTupleType, ASTExtInfo().withAsync().withThrows().withSendable(!hasSending)) ->getCanonicalType(); using Param = FunctionType::Param; PreparedArguments args({Param(flagsType), Param(functionType)}); args.add(loc, RValue(*this, loc, flagsType, flags)); args.add(loc, RValue(*this, loc, functionType, mainFunctionRef)); return emitCreateAsyncTask(*this, loc, subs, std::move(args), {}); } // Shared implementation of withUnsafeContinuation and // withUnsafe[Throwing]Continuation. static ManagedValue emitBuiltinWithUnsafeContinuation( SILGenFunction &SGF, SILLocation loc, SubstitutionMap subs, ArrayRef args, SGFContext C, bool throws) { // Allocate space to receive the resume value when the continuation is // resumed. auto substResultType = subs.getReplacementTypes()[0]->getCanonicalType(); auto opaqueResumeType = SGF.getLoweredType(AbstractionPattern::getOpaque(), substResultType); auto resumeBuf = SGF.emitTemporaryAllocation(loc, opaqueResumeType); // Capture the current continuation. auto continuation = SGF.B.createGetAsyncContinuationAddr(loc, resumeBuf, substResultType, throws); // Get the callee value. auto substFnType = args[0].getType().castTo(); SILValue fnValue = (substFnType->isCalleeConsumed() ? args[0].forward(SGF) : args[0].getValue()); // Call the provided function value. SGF.B.createApply(loc, fnValue, {}, {continuation}); // Await the continuation. SILBasicBlock *resumeBlock = SGF.createBasicBlock(); SILBasicBlock *errorBlock = nullptr; if (throws) errorBlock = SGF.createBasicBlock(FunctionSection::Postmatter); SGF.B.createAwaitAsyncContinuation(loc, continuation, resumeBlock, errorBlock); // Propagate an error if we have one. if (throws) { SGF.B.emitBlock(errorBlock); Scope errorScope(SGF, loc); auto errorTy = SGF.getASTContext().getErrorExistentialType(); auto errorVal = SGF.B.createTermResult( SILType::getPrimitiveObjectType(errorTy), OwnershipKind::Owned); SGF.emitThrow(loc, errorVal, true); } SGF.B.emitBlock(resumeBlock); // The incoming value is the maximally-abstracted result type of the // continuation. Move it out of the resume buffer and reabstract it if // necessary. auto resumeResult = SGF.emitLoad(loc, resumeBuf, AbstractionPattern::getOpaque(), substResultType, SGF.getTypeLowering(substResultType), SGFContext(), IsTake); return resumeResult; } // Emit SIL for the named builtin: withUnsafeContinuation static ManagedValue emitBuiltinWithUnsafeContinuation( SILGenFunction &SGF, SILLocation loc, SubstitutionMap subs, ArrayRef args, SGFContext C) { return emitBuiltinWithUnsafeContinuation(SGF, loc, subs, args, C, /*throws=*/false); } // Emit SIL for the named builtin: withUnsafeThrowingContinuation static ManagedValue emitBuiltinWithUnsafeThrowingContinuation( SILGenFunction &SGF, SILLocation loc, SubstitutionMap subs, ArrayRef args, SGFContext C) { return emitBuiltinWithUnsafeContinuation(SGF, loc, subs, args, C, /*throws=*/true); } static ManagedValue emitBuiltinHopToActor(SILGenFunction &SGF, SILLocation loc, SubstitutionMap subs, ArrayRef args, SGFContext C) { SGF.emitHopToActorValue(loc, args[0]); return ManagedValue::forObjectRValueWithoutOwnership(SGF.emitEmptyTuple(loc)); } static ManagedValue emitBuiltinPackLength(SILGenFunction &SGF, SILLocation loc, SubstitutionMap subs, ArrayRef args, SGFContext C) { auto argTy = args[0].getType().getASTType(); auto tupleTy = CanTupleType(argTy->getMetatypeInstanceType() ->castTo()); auto packTy = tupleTy.getInducedPackType(); return ManagedValue::forObjectRValueWithoutOwnership( SGF.B.createPackLength(loc, packTy)); } static ManagedValue emitBuiltinAutoDiffCreateLinearMapContextWithType( SILGenFunction &SGF, SILLocation loc, SubstitutionMap subs, ArrayRef args, SGFContext C) { ASTContext &ctx = SGF.getASTContext(); auto *builtinApply = SGF.B.createBuiltin( loc, ctx.getIdentifier(getBuiltinName( BuiltinValueKind::AutoDiffCreateLinearMapContextWithType)), SILType::getNativeObjectType(ctx), subs, /*args*/ {args[0].getValue()}); return SGF.emitManagedRValueWithCleanup(builtinApply); } static ManagedValue emitBuiltinAutoDiffProjectTopLevelSubcontext( SILGenFunction &SGF, SILLocation loc, SubstitutionMap subs, ArrayRef args, SGFContext C) { ASTContext &ctx = SGF.getASTContext(); auto *builtinApply = SGF.B.createBuiltin( loc, ctx.getIdentifier( getBuiltinName(BuiltinValueKind::AutoDiffProjectTopLevelSubcontext)), SILType::getRawPointerType(ctx), subs, /*args*/ {args[0].borrow(SGF, loc).getValue()}); return ManagedValue::forObjectRValueWithoutOwnership(builtinApply); } static ManagedValue emitBuiltinAutoDiffAllocateSubcontextWithType( SILGenFunction &SGF, SILLocation loc, SubstitutionMap subs, ArrayRef args, SGFContext C) { ASTContext &ctx = SGF.getASTContext(); auto *builtinApply = SGF.B.createBuiltin( loc, ctx.getIdentifier( getBuiltinName(BuiltinValueKind::AutoDiffAllocateSubcontextWithType)), SILType::getRawPointerType(ctx), subs, /*args*/ {args[0].borrow(SGF, loc).getValue(), args[1].getValue()}); return ManagedValue::forObjectRValueWithoutOwnership(builtinApply); } // The only reason these need special attention is that we want these to // be borrowed arguments, but the default emitter doesn't handle borrowed // arguments correctly. static ManagedValue emitBuildExecutorRef(SILGenFunction &SGF, SILLocation loc, SubstitutionMap subs, ArrayRef args, BuiltinValueKind builtin) { ASTContext &ctx = SGF.getASTContext(); auto builtinID = ctx.getIdentifier(getBuiltinName(builtin)); SmallVector argValues; if (!args.empty()) argValues.push_back(args[0].borrow(SGF, loc).getValue()); auto builtinApply = SGF.B.createBuiltin(loc, builtinID, SILType::getPrimitiveObjectType(ctx.TheExecutorType), subs, argValues); return ManagedValue::forObjectRValueWithoutOwnership(builtinApply); } static ManagedValue emitBuiltinBuildOrdinaryTaskExecutorRef( SILGenFunction &SGF, SILLocation loc, SubstitutionMap subs, ArrayRef args, SGFContext C) { return emitBuildExecutorRef(SGF, loc, subs, args, BuiltinValueKind::BuildOrdinaryTaskExecutorRef); } static ManagedValue emitBuiltinBuildOrdinarySerialExecutorRef( SILGenFunction &SGF, SILLocation loc, SubstitutionMap subs, ArrayRef args, SGFContext C) { return emitBuildExecutorRef(SGF, loc, subs, args, BuiltinValueKind::BuildOrdinarySerialExecutorRef); } static ManagedValue emitBuiltinBuildComplexEqualitySerialExecutorRef( SILGenFunction &SGF, SILLocation loc, SubstitutionMap subs, ArrayRef args, SGFContext C) { return emitBuildExecutorRef(SGF, loc, subs, args, BuiltinValueKind::BuildComplexEqualitySerialExecutorRef); } static ManagedValue emitBuiltinBuildDefaultActorExecutorRef( SILGenFunction &SGF, SILLocation loc, SubstitutionMap subs, ArrayRef args, SGFContext C) { return emitBuildExecutorRef(SGF, loc, subs, args, BuiltinValueKind::BuildDefaultActorExecutorRef); } static ManagedValue emitBuiltinBuildMainActorExecutorRef( SILGenFunction &SGF, SILLocation loc, SubstitutionMap subs, ArrayRef args, SGFContext C) { return emitBuildExecutorRef(SGF, loc, subs, args, BuiltinValueKind::BuildMainActorExecutorRef); } static ManagedValue emitBuiltinExtractFunctionIsolation( SILGenFunction &SGF, SILLocation loc, SubstitutionMap subs, PreparedArguments &&args, SGFContext C) { auto argSources = std::move(args).getSources(); assert(argSources.size() == 1); auto argType = argSources[0].getSubstRValueType(); if (auto fnType = dyn_cast(argType); !fnType || !fnType->getIsolation().isErased()) { SGF.SGM.diagnose(argSources[0].getLocation(), diag::builtin_get_function_isolation_bad_argument); return SGF.emitUndef(SILType::getOpaqueIsolationType(SGF.getASTContext())); } return SGF.emitExtractFunctionIsolation(loc, std::move(argSources[0]), C); } static ManagedValue emitBuiltinGetEnumTag(SILGenFunction &SGF, SILLocation loc, SubstitutionMap subs, ArrayRef args, SGFContext C) { auto &ctx = SGF.getASTContext(); auto bi = SGF.B.createBuiltin( loc, ctx.getIdentifier(getBuiltinName(BuiltinValueKind::GetEnumTag)), SILType::getBuiltinIntegerType(32, ctx), subs, { args[0].getValue() }); return ManagedValue::forObjectRValueWithoutOwnership(bi); } static ManagedValue emitBuiltinInjectEnumTag(SILGenFunction &SGF, SILLocation loc, SubstitutionMap subs, ArrayRef args, SGFContext C) { auto &ctx = SGF.getASTContext(); auto bi = SGF.B.createBuiltin( loc, ctx.getIdentifier(getBuiltinName(BuiltinValueKind::InjectEnumTag)), SILType::getEmptyTupleType(ctx), subs, { args[0].getValue(), args[1].getValue() }); return ManagedValue::forObjectRValueWithoutOwnership(bi); } void SILGenModule::noteMemberRefExpr(MemberRefExpr *e) { VarDecl *var = cast(e->getMember().getDecl()); // If the member is the special `asLocalActor` operation on // distributed actors, make sure we have the conformance needed // for a builtin. ASTContext &ctx = var->getASTContext(); if (isDistributedActorAsLocalActorComputedProperty(var)) { useConformance(getDistributedActorAsActorConformanceRef(ctx)); } } static ManagedValue emitBuiltinDistributedActorAsAnyActor( SILGenFunction &SGF, SILLocation loc, SubstitutionMap subs, ArrayRef args, SGFContext C) { return SGF.emitDistributedActorAsAnyActor(loc, subs, args[0]); } static ManagedValue emitBuiltinAddressOfRawLayout(SILGenFunction &SGF, SILLocation loc, SubstitutionMap subs, ArrayRef args, SGFContext C) { auto &ctx = SGF.getASTContext(); auto bi = SGF.B.createBuiltin( loc, ctx.getIdentifier(getBuiltinName(BuiltinValueKind::AddressOfRawLayout)), SILType::getRawPointerType(ctx), subs, { args[0].getValue() }); return ManagedValue::forObjectRValueWithoutOwnership(bi); } static ManagedValue emitBuiltinZeroInitializer(SILGenFunction &SGF, SILLocation loc, SubstitutionMap subs, ArrayRef args, SGFContext C) { auto valueType = subs.getReplacementTypes()[0]->getCanonicalType(); auto &valueTL = SGF.getTypeLowering(valueType); auto loweredValueTy = valueTL.getLoweredType().getObjectType(); if (valueTL.isLoadable() || !SGF.F.getConventions().useLoweredAddresses()) { auto value = SGF.B.createZeroInitValue(loc, loweredValueTy); return SGF.emitManagedRValueWithCleanup(value, valueTL); } SILValue valueAddr = SGF.getBufferForExprResult(loc, loweredValueTy, C); SGF.B.createZeroInitAddr(loc, valueAddr); return SGF.manageBufferForExprResult(valueAddr, valueTL, C); } static ManagedValue emitBuiltinEmplace(SILGenFunction &SGF, SILLocation loc, SubstitutionMap subs, ArrayRef args, SGFContext C) { // TODO: deal with reabstraction of the result type auto &Ctx = SGF.getASTContext(); auto resultASTTy = subs.getReplacementTypes()[0]; auto &loweredBufferTy = SGF.getTypeLowering(AbstractionPattern::getOpaque(), resultASTTy); bool didEmitInto; Initialization *dest; TemporaryInitialization *destTemporary = nullptr; InitializationPtr destOwner; // Use the context destination if available. if (C.getEmitInto() && C.getEmitInto()->canPerformInPlaceInitialization()) { didEmitInto = true; dest = C.getEmitInto(); } else { didEmitInto = false; auto destTempOwner = SGF.emitTemporary(loc, loweredBufferTy); dest = destTemporary = destTempOwner.get(); destOwner = std::move(destTempOwner); } auto buffer = dest->getAddressForInPlaceInitialization(SGF, loc); // Mark the buffer as initializedto communicate to DI that the memory // is considered initialized from this point. auto markInit = getBuiltinValueDecl(Ctx, Ctx.getIdentifier("prepareInitialization")); SGF.B.createBuiltin(loc, markInit->getBaseIdentifier(), SILType::getEmptyTupleType(Ctx), SubstitutionMap(), buffer); SILValue bufferPtr = SGF.B.createAddressToPointer(loc, buffer, SILType::getPrimitiveObjectType(SGF.getASTContext().TheRawPointerType), /*needs stack protection*/ true); auto fnType = args[0].getValue()->getType().castTo(); if (fnType->hasErrorResult()) { auto normalBB = SGF.createBasicBlock(); auto errorBB = SGF.createBasicBlock(); SGF.B.createTryApply(loc, args[0].getValue(), {}, {SGF.IndirectErrorResult, bufferPtr}, normalBB, errorBB); // Error branch { SGF.B.emitBlock(errorBB); // When the closure throws an error, it needs to clean up the buffer. This // means that the buffer is uninitialized at this point. // We need an `end_lifetime` so that the move-only checker doesn't insert // a wrong `destroy_addr` because it thinks that the buffer is initialized // here. SGF.B.createEndLifetime(loc, buffer); SGF.Cleanups.emitCleanupsForReturn(CleanupLocation(loc), IsForUnwind); SGF.B.createThrowAddr(loc); } SGF.B.emitBlock(normalBB); normalBB->createPhiArgument(SILType::getEmptyTupleType(Ctx), OwnershipKind::Owned); } else { SGF.B.createApply(loc, args[0].getValue(), {}, bufferPtr); } dest->finishInitialization(SGF); if (didEmitInto) { return ManagedValue::forInContext(); } assert(destTemporary && "didn't emit into context but also didn't emit into temporary?"); auto result = destTemporary->getManagedAddress(); auto resultTy = SGF.getLoweredType(subs.getReplacementTypes()[0]); // If the result is naturally address-only, then we can adopt the stack slot // as the value directly. if (resultTy == loweredBufferTy.getLoweredType().getAddressType()) { return result; } // If the result is loadable, load it. return SGF.B.createLoadTake(loc, result); } std::optional SpecializedEmitter::forDecl(SILGenModule &SGM, SILDeclRef function) { // Only consider standalone declarations in the Builtin module. if (function.kind != SILDeclRef::Kind::Func) return std::nullopt; if (!function.hasDecl()) return std::nullopt; ValueDecl *decl = function.getDecl(); if (!isa(decl->getDeclContext())) return std::nullopt; const auto name = decl->getBaseIdentifier(); const BuiltinInfo &builtin = SGM.M.getBuiltinInfo(name); switch (builtin.ID) { // All the non-SIL, non-type-trait builtins should use the // named-builtin logic, which just emits the builtin as a call to a // builtin function. This includes builtins that aren't even declared // in Builtins.def, i.e. all of the LLVM intrinsics. // // We do this in a separate pass over Builtins.def to avoid creating // a bunch of identical cases. #define BUILTIN(Id, Name, Attrs) \ case BuiltinValueKind::Id: #define BUILTIN_SIL_OPERATION(Id, Name, Overload) #define BUILTIN_MISC_OPERATION_WITH_SILGEN(Id, Name, Attrs, Overload) #define BUILTIN_SANITIZER_OPERATION(Id, Name, Attrs) #define BUILTIN_TYPE_CHECKER_OPERATION(Id, Name) #define BUILTIN_TYPE_TRAIT_OPERATION(Id, Name) #include "swift/AST/Builtins.def" case BuiltinValueKind::None: return SpecializedEmitter(name); // Do a second pass over Builtins.def, ignoring all the cases for // which we emitted something above. #define BUILTIN(Id, Name, Attrs) // Use specialized emitters for SIL builtins. #define BUILTIN_SIL_OPERATION(Id, Name, Overload) \ case BuiltinValueKind::Id: \ return SpecializedEmitter(&emitBuiltin##Id); #define BUILTIN_MISC_OPERATION_WITH_SILGEN(Id, Name, Attrs, Overload) \ case BuiltinValueKind::Id: \ return SpecializedEmitter(&emitBuiltin##Id); // Sanitizer builtins should never directly be called; they should only // be inserted as instrumentation by SILGen. #define BUILTIN_SANITIZER_OPERATION(Id, Name, Attrs) \ case BuiltinValueKind::Id: \ llvm_unreachable("Sanitizer builtin called directly?"); #define BUILTIN_TYPE_CHECKER_OPERATION(Id, Name) \ case BuiltinValueKind::Id: \ llvm_unreachable( \ "Compile-time type checker operation should not make it to SIL!"); // Lower away type trait builtins when they're trivially solvable. #define BUILTIN_TYPE_TRAIT_OPERATION(Id, Name) \ case BuiltinValueKind::Id: \ return SpecializedEmitter(&emitBuiltinTypeTrait<&TypeBase::Name, \ BuiltinValueKind::Id>); #include "swift/AST/Builtins.def" } llvm_unreachable("bad builtin kind"); }