diff --git a/include/swift/Demangling/DemangleNodes.def b/include/swift/Demangling/DemangleNodes.def index 69b7d151203d4..52acba9cf6c04 100644 --- a/include/swift/Demangling/DemangleNodes.def +++ b/include/swift/Demangling/DemangleNodes.def @@ -200,6 +200,7 @@ CONTEXT_NODE(Structure) CONTEXT_NODE(Subscript) NODE(Suffix) NODE(ThinFunctionType) +NODE(ThinToThickForwarder) NODE(Tuple) NODE(TupleElement) NODE(TupleElementName) diff --git a/lib/Demangling/Context.cpp b/lib/Demangling/Context.cpp index 7c2e986b3985f..23f771ae22af4 100644 --- a/lib/Demangling/Context.cpp +++ b/lib/Demangling/Context.cpp @@ -88,6 +88,7 @@ bool Context::isThunkSymbol(llvm::StringRef MangledName) { MangledName = stripSuffix(MangledName); // First do a quick check if (MangledName.endswith("TA") || // partial application forwarder + MangledName.endswith("Tu")|| // thin-to-thick forwarder MangledName.endswith("Ta") || // ObjC partial application forwarder MangledName.endswith("To") || // swift-as-ObjC thunk MangledName.endswith("TO") || // ObjC-as-swift thunk @@ -107,6 +108,7 @@ bool Context::isThunkSymbol(llvm::StringRef MangledName) { case Node::Kind::NonObjCAttribute: case Node::Kind::PartialApplyObjCForwarder: case Node::Kind::PartialApplyForwarder: + case Node::Kind::ThinToThickForwarder: case Node::Kind::ReabstractionThunkHelper: case Node::Kind::ReabstractionThunk: case Node::Kind::ProtocolWitness: diff --git a/lib/Demangling/Demangler.cpp b/lib/Demangling/Demangler.cpp index 90a2304df0064..236da80a73f5d 100644 --- a/lib/Demangling/Demangler.cpp +++ b/lib/Demangling/Demangler.cpp @@ -113,6 +113,7 @@ bool swift::Demangle::isFunctionAttr(Node::Kind kind) { case Node::Kind::DirectMethodReferenceAttribute: case Node::Kind::VTableAttribute: case Node::Kind::PartialApplyForwarder: + case Node::Kind::ThinToThickForwarder: case Node::Kind::PartialApplyObjCForwarder: case Node::Kind::OutlinedVariable: case Node::Kind::OutlinedBridgedMethod: @@ -549,7 +550,8 @@ NodePointer Demangler::demangleSymbol(StringRef MangledName, while (NodePointer FuncAttr = popNode(isFunctionAttr)) { Parent->addChild(FuncAttr, *this); if (FuncAttr->getKind() == Node::Kind::PartialApplyForwarder || - FuncAttr->getKind() == Node::Kind::PartialApplyObjCForwarder) + FuncAttr->getKind() == Node::Kind::PartialApplyObjCForwarder || + FuncAttr->getKind() == Node::Kind::ThinToThickForwarder) Parent = FuncAttr; } for (Node *Nd : NodeStack) { @@ -2146,6 +2148,7 @@ NodePointer Demangler::demangleThunkOrSpecialization() { case 'd': return createNode(Node::Kind::DirectMethodReferenceAttribute); case 'a': return createNode(Node::Kind::PartialApplyObjCForwarder); case 'A': return createNode(Node::Kind::PartialApplyForwarder); + case 'u': return createNode(Node::Kind::ThinToThickForwarder); case 'm': return createNode(Node::Kind::MergedFunction); case 'X': return createNode(Node::Kind::DynamicallyReplaceableFunctionVar); case 'x': return createNode(Node::Kind::DynamicallyReplaceableFunctionKey); diff --git a/lib/Demangling/NodePrinter.cpp b/lib/Demangling/NodePrinter.cpp index 94a5247a7063c..c38b2312e7ec9 100644 --- a/lib/Demangling/NodePrinter.cpp +++ b/lib/Demangling/NodePrinter.cpp @@ -466,6 +466,7 @@ class NodePrinter { case Node::Kind::Subscript: case Node::Kind::Suffix: case Node::Kind::ThinFunctionType: + case Node::Kind::ThinToThickForwarder: case Node::Kind::TupleElement: case Node::Kind::TypeMangling: case Node::Kind::TypeMetadata: @@ -1207,6 +1208,14 @@ NodePointer NodePrinter::print(NodePointer Node, bool asPrefixContext) { Printer << "@convention(thin) "; printFunctionType(nullptr, Node); return nullptr; + case Node::Kind::ThinToThickForwarder: + Printer << "thin-to-thick forwarder"; + + if (Node->hasChildren()) { + Printer << " for "; + printChildren(Node); + } + return nullptr; case Node::Kind::FunctionType: case Node::Kind::UncurriedFunctionType: printFunctionType(nullptr, Node); diff --git a/lib/Demangling/OldDemangler.cpp b/lib/Demangling/OldDemangler.cpp index ebe2bec4e5823..a505241ccec8d 100644 --- a/lib/Demangling/OldDemangler.cpp +++ b/lib/Demangling/OldDemangler.cpp @@ -364,6 +364,15 @@ class OldDemangler { DEMANGLE_CHILD_OR_RETURN(forwarder, Global); return forwarder; } + + // thin-to-thick thunks. + if (Mangled.nextIf("Pu")) { + Node::Kind kind = Node::Kind::ThinToThickForwarder; + auto forwarder = Factory.createNode(kind); + if (Mangled.nextIf("__T")) + DEMANGLE_CHILD_OR_RETURN(forwarder, Global); + return forwarder; + } // Top-level types, for various consumers. if (Mangled.nextIf('t')) { diff --git a/lib/Demangling/OldRemangler.cpp b/lib/Demangling/OldRemangler.cpp index 26ed34e4b6abd..8df02b939bbe6 100644 --- a/lib/Demangling/OldRemangler.cpp +++ b/lib/Demangling/OldRemangler.cpp @@ -574,6 +574,11 @@ void Remangler::mangleProtocolSelfConformanceDescriptor(Node *node) { mangleProtocol(node->begin()[0]); } +void Remangler::mangleThinToThickForwarder(Node *node) { + Buffer << "Pu__T"; + mangleSingleChildNode(node); // global +} + void Remangler::manglePartialApplyForwarder(Node *node) { Buffer << "PA__T"; mangleSingleChildNode(node); // global diff --git a/lib/Demangling/Remangler.cpp b/lib/Demangling/Remangler.cpp index 18c880eb43e50..e6434f93dcaef 100644 --- a/lib/Demangling/Remangler.cpp +++ b/lib/Demangling/Remangler.cpp @@ -1721,6 +1721,11 @@ void Remangler::mangleOwningMutableAddressor(Node *node) { mangleAbstractStorage(node->getFirstChild(), "aO"); } +void Remangler::mangleThinToThickForwarder(Node *node) { + mangleChildNodesReversed(node); + Buffer << "Tu"; +} + void Remangler::manglePartialApplyForwarder(Node *node) { mangleChildNodesReversed(node); Buffer << "TA"; diff --git a/lib/IRGen/GenFunc.cpp b/lib/IRGen/GenFunc.cpp index c2b9f730bddf0..3839d986a4a54 100644 --- a/lib/IRGen/GenFunc.cpp +++ b/lib/IRGen/GenFunc.cpp @@ -681,6 +681,62 @@ static unsigned findSinglePartiallyAppliedParameterIndexIgnoringEmptyTypes( return firstNonEmpty; } + +llvm::Function *irgen::getThinToThickForwarder(IRGenModule &IGM, + const Optional &staticFnPtr, + const CanSILFunctionType origType) { + auto origSig = IGM.getSignature(origType); + llvm::FunctionType *origFnTy = origSig.getType(); + auto origTy = origSig.getType()->getPointerTo(); + + llvm::SmallVector thunkParams; + + for (unsigned i = 0; i < origFnTy->getNumParams(); ++i) + thunkParams.push_back(origFnTy->getParamType(i)); + + thunkParams.push_back(IGM.RefCountedPtrTy); + + auto thunkType = llvm::FunctionType::get(origFnTy->getReturnType(), + thunkParams, + /*vararg*/ false); + + StringRef FnName; + if (staticFnPtr) + FnName = staticFnPtr->getPointer()->getName(); + + IRGenMangler Mangler; + std::string thunkName = Mangler.mangleThinToThickForwarder(FnName); + + + // FIXME: Maybe cache the thunk by function and closure types?. + llvm::Function *fwd = + llvm::Function::Create(thunkType, llvm::Function::InternalLinkage, + llvm::StringRef(thunkName), &IGM.Module); + + fwd->setAttributes(origSig.getAttributes()); + fwd->addAttribute(llvm::AttributeList::FirstArgIndex + origFnTy->getNumParams(), llvm::Attribute::SwiftSelf); + IRGenFunction IGF(IGM, fwd); + if (IGM.DebugInfo) + IGM.DebugInfo->emitArtificialFunction(IGF, fwd); + auto args = IGF.collectParameters(); + auto rawFnPtr = args.takeLast(); + + // It comes out of the context as an i8*. Cast to the function type. + rawFnPtr = IGF.Builder.CreateBitCast(rawFnPtr, origTy); + + auto fnPtr = FunctionPointer(rawFnPtr, origSig); + + auto result = IGF.Builder.CreateCall(fnPtr, args.claimAll()); + + // Return the result, if we have one. + if (result->getType()->isVoidTy()) + IGF.Builder.CreateRetVoid(); + else + IGF.Builder.CreateRet(result); + return fwd; +} + + /// Emit the forwarding stub function for a partial application. /// /// If 'layout' is null, there is a single captured value of diff --git a/lib/IRGen/GenFunc.h b/lib/IRGen/GenFunc.h index 04bc2e4732182..620b5ebeb54c0 100644 --- a/lib/IRGen/GenFunc.h +++ b/lib/IRGen/GenFunc.h @@ -54,6 +54,10 @@ namespace irgen { CanSILFunctionType origType, CanSILFunctionType substType, CanSILFunctionType outType, Explosion &out, bool isOutlined); + + llvm::Function *getThinToThickForwarder(IRGenModule &IGM, + const Optional &staticFnPtr, + const CanSILFunctionType origType); } // end namespace irgen } // end namespace swift diff --git a/lib/IRGen/IRGenMangler.cpp b/lib/IRGen/IRGenMangler.cpp index 54e3934a807ef..c8157ac956fed 100644 --- a/lib/IRGen/IRGenMangler.cpp +++ b/lib/IRGen/IRGenMangler.cpp @@ -78,6 +78,21 @@ std::string IRGenMangler::manglePartialApplyForwarder(StringRef FuncName) { return finalize(); } +std::string IRGenMangler::mangleThinToThickForwarder(StringRef FuncName) { + if (FuncName.empty()) { + beginMangling(); + } else { + if (FuncName.startswith(MANGLING_PREFIX_STR)) { + Buffer << FuncName; + } else { + beginMangling(); + appendIdentifier(FuncName); + } + } + appendOperator("Tu"); + return finalize(); +} + SymbolicMangling IRGenMangler::withSymbolicReferences(IRGenModule &IGM, llvm::function_ref body) { diff --git a/lib/IRGen/IRGenMangler.h b/lib/IRGen/IRGenMangler.h index aa4d644d0095b..77198ae596dcb 100644 --- a/lib/IRGen/IRGenMangler.h +++ b/lib/IRGen/IRGenMangler.h @@ -492,6 +492,7 @@ class IRGenMangler : public Mangle::ASTMangler { } std::string manglePartialApplyForwarder(StringRef FuncName); + std::string mangleThinToThickForwarder(StringRef FuncName); std::string mangleTypeForForeignMetadataUniquing(Type type) { return mangleTypeWithoutPrefix(type); diff --git a/lib/IRGen/IRGenSIL.cpp b/lib/IRGen/IRGenSIL.cpp index 82ffd7dc77f2b..154b84b28a45c 100644 --- a/lib/IRGen/IRGenSIL.cpp +++ b/lib/IRGen/IRGenSIL.cpp @@ -4691,8 +4691,61 @@ void IRGenSILFunction::visit##KIND##Inst(swift::KIND##Inst *i) { \ #include "swift/AST/ReferenceStorage.def" #undef NOOP_CONVERSION + +static FunctionPointer +getLoweredFunctionPointer(IRGenSILFunction &IGF, SILValue v) { + LoweredValue &lv = IGF.getLoweredValue(v); + auto fnType = v->getType().castTo(); + + switch (lv.kind) { + case LoweredValue::Kind::ContainedAddress: + case LoweredValue::Kind::StackAddress: + case LoweredValue::Kind::DynamicallyEnforcedAddress: + case LoweredValue::Kind::OwnedAddress: + case LoweredValue::Kind::EmptyExplosion: + case LoweredValue::Kind::CoroutineState: + case LoweredValue::Kind::ObjCMethod: + llvm_unreachable("not a valid function"); + + case LoweredValue::Kind::FunctionPointer: { + return lv.getFunctionPointer(); + } + case LoweredValue::Kind::SingletonExplosion: { + llvm::Value *fnPtr = lv.getKnownSingletonExplosion(); + return FunctionPointer::forExplosionValue(IGF, fnPtr, fnType); + } + case LoweredValue::Kind::ExplosionVector: { + Explosion ex = lv.getExplosion(IGF, v->getType()); + llvm::Value *fnPtr = ex.claimNext(); + auto fn = FunctionPointer::forExplosionValue(IGF, fnPtr, fnType); + return fn; + } + } + llvm_unreachable("bad kind"); +} + void IRGenSILFunction::visitThinToThickFunctionInst( swift::ThinToThickFunctionInst *i) { + + if (IGM.TargetInfo.OutputObjectFormat == llvm::Triple::Wasm) { + auto fn = getLoweredFunctionPointer(*this, i->getCallee()); + auto fnTy = i->getCallee()->getType().castTo(); + Optional staticFn; + if (fn.isConstant()) staticFn = fn; + auto thunkFn = getThinToThickForwarder(IGM, staticFn, fnTy); + Explosion from = getLoweredExplosion(i->getOperand()); + Explosion to; + auto fnPtr = Builder.CreateBitCast(thunkFn, IGM.Int8PtrTy); + to.add(fnPtr); + llvm::Value *ctx = from.claimNext(); + if (fnTy->isNoEscape()) + ctx = Builder.CreateBitCast(ctx, IGM.OpaquePtrTy); + else + ctx = Builder.CreateBitCast(ctx, IGM.RefCountedPtrTy); + to.add(ctx); + setLoweredExplosion(i, to); + return; + } // Take the incoming function pointer and add a null context pointer to it. Explosion from = getLoweredExplosion(i->getOperand()); Explosion to; diff --git a/test/IRGen/indirect_func_call_wasm.sil b/test/IRGen/indirect_func_call_wasm.sil new file mode 100644 index 0000000000000..f64cae7fcaa12 --- /dev/null +++ b/test/IRGen/indirect_func_call_wasm.sil @@ -0,0 +1,33 @@ +// RUN: %target-swift-frontend %s -emit-ir | %FileCheck %s + +// REQUIRES: CPU=wasm32 + +// CHECK-LABEL: define swiftcc void @closureToConvert() +// CHECK: entry: +// CHECK: ret void +// CHECK: } +sil @closureToConvert : $@convention(thin) () -> () { + %99 = tuple () + return %99 : $() +} +// CHECK-LABEL: define swiftcc void @testConvertFunc() +// CHECK: entry: +// CHECK: call swiftcc void @"$s{{.*}}Tu"(%swift.refcounted* swiftself bitcast (void ()* @closureToConvert to %swift.refcounted*)) +// CHECK: ret void +// CHECK-LABEL: } + +sil @testConvertFunc : $@convention(thin) () -> () { +bb0: + %f = function_ref @closureToConvert : $@convention(thin) () -> () + %cf = convert_function %f : $@convention(thin) () -> () to $@noescape @convention(thin) () -> () + %thick = thin_to_thick_function %cf : $@noescape @convention(thin) () -> () to $@noescape @callee_owned () -> () + %apply = apply %thick() : $@noescape @callee_owned () -> () + %99 = tuple () + return %99 : $() +} + +// CHECK-LABEL: define internal void @"$s{{.*}}Tu"(%swift.refcounted* swiftself %0) +// CHECK: entry: +// CHECK: %1 = bitcast %swift.refcounted* %0 to void ()* +// CHECK: call swiftcc void %1() +// CHECK: ret void