Skip to content

Commit 6d378a3

Browse files
[wasm] add @_expose(wasm) attribute support
This attribute instructs the compiler that this function declaration should be "export"ed from this .wasm module. It's equivalent of Clang's `__attribute__((export_name("name")))`
1 parent cd3e6e3 commit 6d378a3

File tree

17 files changed

+216
-48
lines changed

17 files changed

+216
-48
lines changed

docs/ReferenceGuides/UnderscoredAttributes.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -427,6 +427,16 @@ in the generated C++ binding header.
427427
The optional "cxxName" string will be used as the name of
428428
the generated C++ declaration.
429429

430+
### `_expose(wasm[, <"wasmExportName">])`
431+
432+
Indicates that a particular function declaration should be
433+
exported from the linked WebAssembly.
434+
435+
The optional "wasmExportName" string will be used as the
436+
the export name.
437+
438+
It's the equivalent of clang's `__attribute__((export_name))`.
439+
430440
## `@_fixed_layout`
431441

432442
Same as `@frozen` but also works for classes.

include/swift/AST/Attr.h

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,10 @@ class DeclAttribute : public AttributeBase {
172172
kind : 1
173173
);
174174

175+
SWIFT_INLINE_BITFIELD(ExposeAttr, DeclAttribute, NumExposureKindBits,
176+
kind : NumExposureKindBits
177+
);
178+
175179
SWIFT_INLINE_BITFIELD(SynthesizedProtocolAttr, DeclAttribute, 1,
176180
isUnchecked : 1
177181
);
@@ -2309,15 +2313,23 @@ class BackDeployedAttr : public DeclAttribute {
23092313
/// header used by C/C++ to interoperate with Swift.
23102314
class ExposeAttr : public DeclAttribute {
23112315
public:
2312-
ExposeAttr(StringRef Name, SourceLoc AtLoc, SourceRange Range, bool Implicit)
2313-
: DeclAttribute(DAK_Expose, AtLoc, Range, Implicit), Name(Name) {}
2316+
ExposeAttr(StringRef Name, SourceLoc AtLoc, SourceRange Range,
2317+
ExposureKind Kind, bool Implicit)
2318+
: DeclAttribute(DAK_Expose, AtLoc, Range, Implicit), Name(Name) {
2319+
Bits.ExposeAttr.kind = static_cast<unsigned>(Kind);
2320+
}
23142321

2315-
ExposeAttr(StringRef Name, bool Implicit)
2316-
: ExposeAttr(Name, SourceLoc(), SourceRange(), Implicit) {}
2322+
ExposeAttr(StringRef Name, ExposureKind Kind, bool Implicit)
2323+
: ExposeAttr(Name, SourceLoc(), SourceRange(), Kind, Implicit) {}
23172324

23182325
/// The exposed declaration name.
23192326
const StringRef Name;
23202327

2328+
/// Returns the kind of exposure.
2329+
ExposureKind getExposureKind() const {
2330+
return static_cast<ExposureKind>(Bits.ExposeAttr.kind);
2331+
}
2332+
23212333
static bool classof(const DeclAttribute *DA) {
23222334
return DA->getKind() == DAK_Expose;
23232335
}

include/swift/AST/AttrKind.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,15 @@ enum class EffectsKind : uint8_t {
102102
enum : unsigned { NumEffectsKindBits =
103103
countBitsUsed(static_cast<unsigned>(EffectsKind::Last_EffectsKind)) };
104104

105+
/// This enum represents the possible values of the @_expose attribute.
106+
enum class ExposureKind: uint8_t {
107+
Cxx,
108+
Wasm,
109+
Last_ExposureKind = Wasm
110+
};
111+
112+
enum : unsigned { NumExposureKindBits =
113+
countBitsUsed(static_cast<unsigned>(ExposureKind::Last_ExposureKind)) };
105114

106115
enum DeclAttrKind : unsigned {
107116
#define DECL_ATTR(_, NAME, ...) DAK_##NAME,

include/swift/AST/DiagnosticsSema.def

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1838,6 +1838,9 @@ ERROR(section_not_at_top_level,none,
18381838
ERROR(section_empty_name,none,
18391839
"@_section section name cannot be empty", ())
18401840

1841+
ERROR(expose_wasm_not_at_top_level_func,none,
1842+
"@_expose attribute with 'wasm' can only be applied to global functions", ())
1843+
18411844
ERROR(expose_only_non_other_attr,none,
18421845
"@_expose attribute cannot be applied to an '%0' declaration", (StringRef))
18431846
ERROR(expose_inside_unexposed_decl,none,

include/swift/SIL/SILFunction.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -299,6 +299,10 @@ class SILFunction
299299
/// Name of a section if @_section attribute was used, otherwise empty.
300300
StringRef Section;
301301

302+
/// Name of a Wasm export if @_expose(wasm) attribute was used, otherwise
303+
/// empty.
304+
StringRef WasmExportName;
305+
302306
/// Has value if there's a profile for this function
303307
/// Contains Function Entry Count
304308
ProfileCounter EntryCount;
@@ -1264,6 +1268,10 @@ class SILFunction
12641268
StringRef section() const { return Section; }
12651269
void setSection(StringRef value) { Section = value; }
12661270

1271+
/// Return Wasm export name if @_expose(wasm) was used, otherwise empty
1272+
StringRef wasmExportName() const { return WasmExportName; }
1273+
void setWasmExportName(StringRef value) { WasmExportName = value; }
1274+
12671275
/// Returns true if this function belongs to a declaration that returns
12681276
/// an opaque result type with one or more availability conditions that are
12691277
/// allowed to produce a different underlying type at runtime.

lib/AST/Attr.cpp

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1122,14 +1122,22 @@ bool DeclAttribute::printImpl(ASTPrinter &Printer, const PrintOptions &Options,
11221122
Printer << "@_cdecl(\"" << cast<CDeclAttr>(this)->Name << "\")";
11231123
break;
11241124

1125-
case DAK_Expose:
1125+
case DAK_Expose: {
11261126
Printer.printAttrName("@_expose");
1127-
Printer << "(Cxx";
1127+
auto Attr = cast<ExposeAttr>(this);
1128+
switch (Attr->getExposureKind()) {
1129+
case ExposureKind::Wasm:
1130+
Printer << "(wasm";
1131+
break;
1132+
case ExposureKind::Cxx:
1133+
Printer << "(Cxx";
1134+
break;
1135+
}
11281136
if (!cast<ExposeAttr>(this)->Name.empty())
11291137
Printer << ", \"" << cast<ExposeAttr>(this)->Name << "\"";
11301138
Printer << ")";
11311139
break;
1132-
1140+
}
11331141
case DAK_Section:
11341142
Printer.printAttrName("@_section");
11351143
Printer << "(\"" << cast<SectionAttr>(this)->Name << "\")";

lib/AST/SwiftNameTranslation.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -159,7 +159,7 @@ swift::cxx_translation::getNameForCxx(const ValueDecl *VD,
159159
ASTContext& ctx = VD->getASTContext();
160160

161161
if (const auto *Expose = VD->getAttrs().getAttribute<ExposeAttr>()) {
162-
if (!Expose->Name.empty())
162+
if (Expose->getExposureKind() == ExposureKind::Cxx && !Expose->Name.empty())
163163
return Expose->Name;
164164
}
165165

lib/IRGen/GenDecl.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3554,6 +3554,11 @@ llvm::Function *IRGenModule::getAddrOfSILFunction(
35543554
addUsedGlobal(fn);
35553555
if (!f->section().empty())
35563556
fn->setSection(f->section());
3557+
if (!f->wasmExportName().empty()) {
3558+
llvm::AttrBuilder attrBuilder(getLLVMContext());
3559+
attrBuilder.addAttribute("wasm-export-name", f->wasmExportName());
3560+
fn->addFnAttrs(attrBuilder);
3561+
}
35573562

35583563
// If `hasCReferences` is true, then the function is either marked with
35593564
// @_silgen_name OR @_cdecl. If it is the latter, it must have a definition

lib/Parse/ParseDecl.cpp

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3015,12 +3015,24 @@ ParserStatus Parser::parseNewDeclAttribute(DeclAttributes &Attributes,
30153015
}
30163016

30173017
bool ParseSymbolName = true;
3018+
3019+
ExposureKind ExpKind;
30183020
if (DK == DAK_Expose) {
3019-
if (Tok.isNot(tok::identifier) || Tok.getText() != "Cxx") {
3021+
auto diagnoseExpectOption = [&]() {
30203022
diagnose(Tok.getLoc(), diag::attr_expected_option_such_as, AttrName,
30213023
"Cxx");
3022-
if (Tok.isNot(tok::identifier))
3023-
return makeParserSuccess();
3024+
ParseSymbolName = false;
3025+
};
3026+
if (Tok.isNot(tok::identifier)) {
3027+
diagnoseExpectOption();
3028+
return makeParserSuccess();
3029+
}
3030+
if (Tok.getText() == "Cxx") {
3031+
ExpKind = ExposureKind::Cxx;
3032+
} else if (Tok.getText() == "wasm") {
3033+
ExpKind = ExposureKind::Wasm;
3034+
} else {
3035+
diagnoseExpectOption();
30243036
DiscardAttribute = true;
30253037
}
30263038
consumeToken(tok::identifier);
@@ -3079,7 +3091,7 @@ ParserStatus Parser::parseNewDeclAttribute(DeclAttributes &Attributes,
30793091
else if (DK == DAK_Expose)
30803092
Attributes.add(new (Context) ExposeAttr(
30813093
AsmName ? AsmName.value() : StringRef(""), AtLoc, AttrRange,
3082-
/*Implicit=*/false));
3094+
ExpKind, /*Implicit=*/false));
30833095
else
30843096
llvm_unreachable("out of sync with switch");
30853097
}

lib/PrintAsClang/DeclAndTypePrinter.cpp

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2825,8 +2825,10 @@ static bool hasExposeAttr(const ValueDecl *VD, bool isExtension = false) {
28252825
// Clang decls don't need to be explicitly exposed.
28262826
if (VD->hasClangNode())
28272827
return true;
2828-
if (VD->getAttrs().hasAttribute<ExposeAttr>())
2829-
return true;
2828+
if (auto *EA = VD->getAttrs().getAttribute<ExposeAttr>()) {
2829+
if (EA->getExposureKind() == ExposureKind::Cxx)
2830+
return true;
2831+
}
28302832
if (const auto *NMT = dyn_cast<NominalTypeDecl>(VD->getDeclContext()))
28312833
return hasExposeAttr(NMT);
28322834
if (const auto *ED = dyn_cast<ExtensionDecl>(VD->getDeclContext())) {

0 commit comments

Comments
 (0)