Skip to content

[WASM] Implement thin-to-thick semantic for WebAssembly #186

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions include/swift/Demangling/DemangleNodes.def
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,7 @@ CONTEXT_NODE(Structure)
CONTEXT_NODE(Subscript)
NODE(Suffix)
NODE(ThinFunctionType)
NODE(ThinToThickForwarder)
NODE(Tuple)
NODE(TupleElement)
NODE(TupleElementName)
Expand Down
2 changes: 2 additions & 0 deletions lib/Demangling/Context.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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:
Expand Down
5 changes: 4 additions & 1 deletion lib/Demangling/Demangler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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);
Expand Down
9 changes: 9 additions & 0 deletions lib/Demangling/NodePrinter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down Expand Up @@ -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);
Expand Down
9 changes: 9 additions & 0 deletions lib/Demangling/OldDemangler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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')) {
Expand Down
5 changes: 5 additions & 0 deletions lib/Demangling/OldRemangler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
5 changes: 5 additions & 0 deletions lib/Demangling/Remangler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand Down
56 changes: 56 additions & 0 deletions lib/IRGen/GenFunc.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -681,6 +681,62 @@ static unsigned findSinglePartiallyAppliedParameterIndexIgnoringEmptyTypes(
return firstNonEmpty;
}


llvm::Function *irgen::getThinToThickForwarder(IRGenModule &IGM,
const Optional<FunctionPointer> &staticFnPtr,
const CanSILFunctionType origType) {
auto origSig = IGM.getSignature(origType);
llvm::FunctionType *origFnTy = origSig.getType();
auto origTy = origSig.getType()->getPointerTo();

llvm::SmallVector<llvm::Type *, 4> 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
Expand Down
4 changes: 4 additions & 0 deletions lib/IRGen/GenFunc.h
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,10 @@ namespace irgen {
CanSILFunctionType origType, CanSILFunctionType substType,
CanSILFunctionType outType, Explosion &out, bool isOutlined);


llvm::Function *getThinToThickForwarder(IRGenModule &IGM,
const Optional<FunctionPointer> &staticFnPtr,
const CanSILFunctionType origType);
} // end namespace irgen
} // end namespace swift

Expand Down
15 changes: 15 additions & 0 deletions lib/IRGen/IRGenMangler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<void ()> body) {
Expand Down
1 change: 1 addition & 0 deletions lib/IRGen/IRGenMangler.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
53 changes: 53 additions & 0 deletions lib/IRGen/IRGenSIL.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<SILFunctionType>();

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<SILFunctionType>();
Optional<FunctionPointer> 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;
Expand Down
33 changes: 33 additions & 0 deletions test/IRGen/indirect_func_call_wasm.sil
Original file line number Diff line number Diff line change
@@ -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