Skip to content

Commit 051bf4d

Browse files
Merge pull request #69107 from kateinoigakukun/yt/extern-wasm-sym
[wasm] Add @_extern(wasm) attribute support
2 parents c25963d + bd898b0 commit 051bf4d

21 files changed

+311
-3
lines changed

docs/ReferenceGuides/UnderscoredAttributes.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -437,6 +437,18 @@ the export name.
437437

438438
It's the equivalent of clang's `__attribute__((export_name))`.
439439

440+
## `@_extern(<language>)`
441+
442+
Indicates that a particular declaration should be imported
443+
from the external environment.
444+
445+
### `@_extern(wasm, module: <"moduleName">, name: <"fieldName">)`
446+
447+
Indicates that a particular declaration should be imported
448+
through WebAssembly's import interface.
449+
450+
It's the equivalent of clang's `__attribute__((import_module("module"), import_name("field")))`.
451+
440452
## `@_fixed_layout`
441453

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

include/swift/AST/Attr.def

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -418,6 +418,9 @@ DECL_ATTR(_section, Section,
418418
DECL_ATTR(_rawLayout, RawLayout,
419419
OnStruct | UserInaccessible | ABIBreakingToAdd | ABIBreakingToRemove | APIStableToAdd | APIStableToRemove,
420420
146)
421+
DECL_ATTR(_extern, Extern,
422+
OnFunc | ABIStableToAdd | ABIBreakingToRemove | APIStableToAdd | APIStableToRemove,
423+
147)
421424
CONTEXTUAL_SIMPLE_DECL_ATTR(final, Final,
422425
OnClass | OnFunc | OnAccessor | OnVar | OnSubscript | DeclModifier | ABIBreakingToAdd | ABIBreakingToRemove | APIStableToAdd | APIStableToRemove,
423426
2)

include/swift/AST/Attr.h

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2335,6 +2335,28 @@ class ExposeAttr : public DeclAttribute {
23352335
}
23362336
};
23372337

2338+
/// Define the `@_extern` attribute, used to import external declarations in
2339+
/// the specified way to interoperate with Swift.
2340+
class ExternAttr : public DeclAttribute {
2341+
public:
2342+
ExternAttr(StringRef ModuleName, StringRef Name, SourceLoc AtLoc, SourceRange Range, bool Implicit)
2343+
: DeclAttribute(DAK_Extern, AtLoc, Range, Implicit),
2344+
ModuleName(ModuleName), Name(Name) {}
2345+
2346+
ExternAttr(StringRef ModuleName, StringRef Name, bool Implicit)
2347+
: ExternAttr(ModuleName, Name, SourceLoc(), SourceRange(), Implicit) {}
2348+
2349+
/// The module name to import the named declaration in it
2350+
const StringRef ModuleName;
2351+
2352+
/// The declaration name to import
2353+
const StringRef Name;
2354+
2355+
static bool classof(const DeclAttribute *DA) {
2356+
return DA->getKind() == DAK_Extern;
2357+
}
2358+
};
2359+
23382360
/// The `@_documentation(...)` attribute, used to override a symbol's visibility
23392361
/// in symbol graphs, and/or adding arbitrary metadata to it.
23402362
class DocumentationAttr: public DeclAttribute {

include/swift/AST/DiagnosticsCommon.def

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,9 @@ ERROR(require_const_initializer_for_const,none,
8484
ERROR(func_decl_without_brace,PointsToFirstBadToken,
8585
"expected '{' in body of function declaration", ())
8686

87+
ERROR(func_decl_no_body_expected,PointsToFirstBadToken,
88+
"unexpected body of function declaration", ())
89+
8790
NOTE(convert_let_to_var,none,
8891
"change 'let' to 'var' to make it mutable", ())
8992

include/swift/AST/DiagnosticsParse.def

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1867,6 +1867,8 @@ ERROR(attr_rawlayout_expected_integer_count,none,
18671867
ERROR(attr_rawlayout_expected_params,none,
18681868
"expected %1 argument after %0 argument in @_rawLayout attribute", (StringRef, StringRef))
18691869

1870+
ERROR(attr_extern_expected_label,none,
1871+
"expected %0 argument to @_extern attribute", (StringRef))
18701872
//------------------------------------------------------------------------------
18711873
// MARK: Generics parsing diagnostics
18721874
//------------------------------------------------------------------------------

include/swift/AST/DiagnosticsSema.def

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1869,6 +1869,10 @@ ERROR(section_not_at_top_level,none,
18691869
ERROR(section_empty_name,none,
18701870
"@_section section name cannot be empty", ())
18711871

1872+
// @_extern
1873+
ERROR(extern_not_at_top_level_func,none,
1874+
"@_extern attribute can only be applied to global functions", ())
1875+
18721876
ERROR(expose_wasm_not_at_top_level_func,none,
18731877
"@_expose attribute with 'wasm' can only be applied to global functions", ())
18741878

include/swift/Parse/Parser.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1095,6 +1095,10 @@ class Parser {
10951095
ParserResult<DifferentiableAttr> parseDifferentiableAttribute(SourceLoc AtLoc,
10961096
SourceLoc Loc);
10971097

1098+
/// Parse the @_extern attribute.
1099+
bool parseExternAttribute(DeclAttributes &Attributes, bool &DiscardAttribute,
1100+
StringRef AttrName, SourceLoc AtLoc, SourceLoc Loc);
1101+
10981102
/// Parse the arguments inside the @differentiable attribute.
10991103
bool parseDifferentiableAttributeArguments(
11001104
DifferentiabilityKind &diffKind,

include/swift/SIL/SILFunction.h

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -303,6 +303,9 @@ class SILFunction
303303
/// empty.
304304
StringRef WasmExportName;
305305

306+
/// Name of a Wasm import module and field if @_extern(wasm) attribute
307+
llvm::Optional<std::pair<StringRef, StringRef>> WasmImportModuleAndField;
308+
306309
/// Has value if there's a profile for this function
307310
/// Contains Function Entry Count
308311
ProfileCounter EntryCount;
@@ -1278,6 +1281,24 @@ class SILFunction
12781281
StringRef wasmExportName() const { return WasmExportName; }
12791282
void setWasmExportName(StringRef value) { WasmExportName = value; }
12801283

1284+
/// Return Wasm import module name if @_extern(wasm) was used otherwise empty
1285+
StringRef wasmImportModuleName() const {
1286+
if (WasmImportModuleAndField)
1287+
return WasmImportModuleAndField->first;
1288+
return StringRef();
1289+
}
1290+
1291+
/// Return Wasm import field name if @_extern(wasm) was used otherwise empty
1292+
StringRef wasmImportFieldName() const {
1293+
if (WasmImportModuleAndField)
1294+
return WasmImportModuleAndField->second;
1295+
return StringRef();
1296+
}
1297+
1298+
void setWasmImportModuleAndField(StringRef module, StringRef field) {
1299+
WasmImportModuleAndField = std::make_pair(module, field);
1300+
}
1301+
12811302
/// Returns true if this function belongs to a declaration that returns
12821303
/// an opaque result type with one or more availability conditions that are
12831304
/// allowed to produce a different underlying type at runtime.

lib/AST/Attr.cpp

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1138,6 +1138,15 @@ bool DeclAttribute::printImpl(ASTPrinter &Printer, const PrintOptions &Options,
11381138
Printer << ")";
11391139
break;
11401140
}
1141+
1142+
case DAK_Extern: {
1143+
auto *Attr = cast<ExternAttr>(this);
1144+
Printer.printAttrName("@_extern");
1145+
// For now, it accepts only "wasm" as its kind.
1146+
Printer << "(wasm, module: \"" << Attr->ModuleName << "\", name: \"" << Attr->Name << "\")";
1147+
break;
1148+
}
1149+
11411150
case DAK_Section:
11421151
Printer.printAttrName("@_section");
11431152
Printer << "(\"" << cast<SectionAttr>(this)->Name << "\")";
@@ -1708,6 +1717,8 @@ StringRef DeclAttribute::getAttrName() const {
17081717
}
17091718
case DAK_RawLayout:
17101719
return "_rawLayout";
1720+
case DAK_Extern:
1721+
return "_extern";
17111722
}
17121723
llvm_unreachable("bad DeclAttrKind");
17131724
}

lib/IRGen/GenDecl.cpp

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3577,11 +3577,18 @@ llvm::Function *IRGenModule::getAddrOfSILFunction(
35773577
addUsedGlobal(fn);
35783578
if (!f->section().empty())
35793579
fn->setSection(f->section());
3580+
3581+
llvm::AttrBuilder attrBuilder(getLLVMContext());
35803582
if (!f->wasmExportName().empty()) {
3581-
llvm::AttrBuilder attrBuilder(getLLVMContext());
35823583
attrBuilder.addAttribute("wasm-export-name", f->wasmExportName());
3583-
fn->addFnAttrs(attrBuilder);
35843584
}
3585+
if (!f->wasmImportFieldName().empty()) {
3586+
attrBuilder.addAttribute("wasm-import-name", f->wasmImportFieldName());
3587+
}
3588+
if (!f->wasmImportModuleName().empty()) {
3589+
attrBuilder.addAttribute("wasm-import-module", f->wasmImportModuleName());
3590+
}
3591+
fn->addFnAttrs(attrBuilder);
35853592

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

lib/Parse/ParseDecl.cpp

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1431,6 +1431,83 @@ Parser::parseDifferentiableAttribute(SourceLoc atLoc, SourceLoc loc) {
14311431
parameters, whereClause));
14321432
}
14331433

1434+
bool Parser::parseExternAttribute(DeclAttributes &Attributes,
1435+
bool &DiscardAttribute, StringRef AttrName,
1436+
SourceLoc AtLoc, SourceLoc Loc) {
1437+
1438+
// Parse @_extern(<language>, ...)
1439+
if (!consumeIf(tok::l_paren)) {
1440+
diagnose(Loc, diag::attr_expected_lparen, AttrName,
1441+
DeclAttribute::isDeclModifier(DAK_Extern));
1442+
return false;
1443+
}
1444+
auto diagnoseExpectLanguage = [&]() {
1445+
diagnose(Tok.getLoc(), diag::attr_expected_option_such_as, AttrName,
1446+
"wasm");
1447+
};
1448+
if (Tok.isNot(tok::identifier)) {
1449+
diagnoseExpectLanguage();
1450+
return false;
1451+
}
1452+
1453+
if (Tok.getText() != "wasm") {
1454+
diagnoseExpectLanguage();
1455+
DiscardAttribute = true;
1456+
}
1457+
consumeToken(tok::identifier);
1458+
1459+
// Parse @_extern(wasm, module: "x", name: "y")
1460+
auto parseStringLiteralArgument = [&](StringRef fieldName, StringRef &fieldValue) {
1461+
if (!consumeIf(tok::comma) || Tok.isNot(tok::identifier) || Tok.getText() != fieldName) {
1462+
diagnose(Loc, diag::attr_extern_expected_label, fieldName);
1463+
return false;
1464+
}
1465+
consumeToken(tok::identifier);
1466+
1467+
if (!consumeIf(tok::colon)) {
1468+
diagnose(Tok.getLoc(), diag::attr_expected_colon_after_label, fieldName);
1469+
return false;
1470+
}
1471+
1472+
if (Tok.isNot(tok::string_literal)) {
1473+
diagnose(Loc, diag::attr_expected_string_literal, AttrName);
1474+
return false;
1475+
}
1476+
llvm::Optional<StringRef> importModuleName =
1477+
getStringLiteralIfNotInterpolated(Loc, ("'" + AttrName + "'").str());
1478+
consumeToken(tok::string_literal);
1479+
1480+
if (!importModuleName.has_value()) {
1481+
DiscardAttribute = true;
1482+
return false;
1483+
}
1484+
fieldValue = importModuleName.value();
1485+
return true;
1486+
};
1487+
1488+
StringRef importModuleName, importName;
1489+
if (!parseStringLiteralArgument("module", importModuleName))
1490+
DiscardAttribute = true;
1491+
1492+
if (!parseStringLiteralArgument("name", importName))
1493+
DiscardAttribute = true;
1494+
1495+
if (!consumeIf(tok::r_paren)) {
1496+
diagnose(Loc, diag::attr_expected_rparen, AttrName,
1497+
DeclAttribute::isDeclModifier(DAK_Extern));
1498+
return false;
1499+
}
1500+
1501+
auto AttrRange = SourceRange(Loc, Tok.getLoc());
1502+
1503+
if (!DiscardAttribute) {
1504+
Attributes.add(new (Context) ExternAttr(importModuleName, importName, AtLoc,
1505+
AttrRange,
1506+
/*Implicit=*/false));
1507+
}
1508+
return false;
1509+
}
1510+
14341511
// Attribute parsing error helper.
14351512
// For the given parentheses depth, skip until ')' and consume it if possible.
14361513
// If no ')' is found, produce error.
@@ -3168,6 +3245,12 @@ ParserStatus Parser::parseNewDeclAttribute(DeclAttributes &Attributes,
31683245
break;
31693246
}
31703247

3248+
case DAK_Extern: {
3249+
if (!parseExternAttribute(Attributes, DiscardAttribute, AttrName, AtLoc, Loc))
3250+
return makeParserSuccess();
3251+
break;
3252+
}
3253+
31713254
case DAK_Section: {
31723255
if (!consumeIf(tok::l_paren)) {
31733256
diagnose(Loc, diag::attr_expected_lparen, AttrName,

lib/SIL/IR/SILFunctionBuilder.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,10 @@ void SILFunctionBuilder::addFunctionAttributes(
180180
}
181181
}
182182

183+
if (auto *EA = Attrs.getAttribute<ExternAttr>()) {
184+
F->setWasmImportModuleAndField(EA->ModuleName, EA->Name);
185+
}
186+
183187
if (Attrs.hasAttribute<UsedAttr>())
184188
F->setMarkedAsUsed(true);
185189

lib/Sema/TypeCheckAttr.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -253,6 +253,7 @@ class AttributeChecker : public AttributeVisitor<AttributeChecker> {
253253

254254
void visitCDeclAttr(CDeclAttr *attr);
255255
void visitExposeAttr(ExposeAttr *attr);
256+
void visitExternAttr(ExternAttr *attr);
256257
void visitUsedAttr(UsedAttr *attr);
257258
void visitSectionAttr(SectionAttr *attr);
258259

@@ -2064,6 +2065,13 @@ void AttributeChecker::visitExposeAttr(ExposeAttr *attr) {
20642065
}
20652066
}
20662067

2068+
void AttributeChecker::visitExternAttr(ExternAttr *attr) {
2069+
// Only top-level func decls are currently supported.
2070+
if (!isa<FuncDecl>(D) || D->getDeclContext()->isTypeContext()) {
2071+
diagnose(attr->getLocation(), diag::extern_not_at_top_level_func);
2072+
}
2073+
}
2074+
20672075
void AttributeChecker::visitUsedAttr(UsedAttr *attr) {
20682076
if (!Ctx.LangOpts.hasFeature(Feature::SymbolLinkageMarkers)) {
20692077
diagnoseAndRemoveAttr(attr, diag::section_linkage_markers_disabled);

lib/Sema/TypeCheckDeclOverride.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1511,6 +1511,7 @@ namespace {
15111511
UNINTERESTING_ATTR(Inlinable)
15121512
UNINTERESTING_ATTR(Effects)
15131513
UNINTERESTING_ATTR(Expose)
1514+
UNINTERESTING_ATTR(Extern)
15141515
UNINTERESTING_ATTR(Final)
15151516
UNINTERESTING_ATTR(MoveOnly)
15161517
UNINTERESTING_ATTR(FixedLayout)

lib/Sema/TypeCheckDeclPrimary.cpp

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3247,6 +3247,16 @@ class DeclChecker : public DeclVisitor<DeclChecker> {
32473247
// PatternBindingDecl.
32483248
}
32493249

3250+
/// Determine whether the given declaration should not have a definition.
3251+
static bool requiresNoDefinition(Decl *decl) {
3252+
if (auto func = dyn_cast<AbstractFunctionDecl>(decl)) {
3253+
// Function with @_extern should not have a body.
3254+
return func->getAttrs().hasAttribute<ExternAttr>();
3255+
}
3256+
// Everything else can have a definition.
3257+
return false;
3258+
}
3259+
32503260
/// Determine whether the given declaration requires a definition.
32513261
///
32523262
/// Only valid for declarations that can have definitions, i.e.,
@@ -3264,6 +3274,7 @@ class DeclChecker : public DeclVisitor<DeclChecker> {
32643274
// Functions can have _silgen_name, semantics, and NSManaged attributes.
32653275
if (auto func = dyn_cast<AbstractFunctionDecl>(decl)) {
32663276
if (func->getAttrs().hasAttribute<SILGenNameAttr>() ||
3277+
func->getAttrs().hasAttribute<ExternAttr>() ||
32673278
func->getAttrs().hasAttribute<SemanticsAttr>() ||
32683279
func->getAttrs().hasAttribute<NSManagedAttr>())
32693280
return false;
@@ -3346,6 +3357,9 @@ class DeclChecker : public DeclVisitor<DeclChecker> {
33463357
if (requiresDefinition(FD) && !FD->hasBody()) {
33473358
// Complain if we should have a body.
33483359
FD->diagnose(diag::func_decl_without_brace);
3360+
} else if (requiresNoDefinition(FD) && FD->hasBody()) {
3361+
// Complain if we have a body but shouldn't.
3362+
FD->diagnose(diag::func_decl_no_body_expected);
33493363
} else if (FD->getDeclContext()->isLocalContext()) {
33503364
// Check local function bodies right away.
33513365
(void)FD->getTypecheckedBody();

lib/Serialization/Deserialization.cpp

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5860,6 +5860,18 @@ llvm::Error DeclDeserializer::deserializeDeclCommon() {
58605860
break;
58615861
}
58625862

5863+
case decls_block::Extern_DECL_ATTR: {
5864+
bool isImplicit;
5865+
unsigned moduleNameSize, declNameSize;
5866+
serialization::decls_block::ExternDeclAttrLayout::readRecord(
5867+
scratch, isImplicit, moduleNameSize, declNameSize);
5868+
StringRef moduleName = blobData.substr(0, moduleNameSize);
5869+
blobData = blobData.substr(moduleNameSize);
5870+
StringRef declName = blobData.substr(0, declNameSize);
5871+
Attr = new (ctx) ExternAttr(moduleName, declName, isImplicit);
5872+
break;
5873+
}
5874+
58635875
case decls_block::Documentation_DECL_ATTR: {
58645876
bool isImplicit;
58655877
uint64_t CategoryID;

lib/Serialization/ModuleFormat.h

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ const uint16_t SWIFTMODULE_VERSION_MAJOR = 0;
5858
/// describe what change you made. The content of this comment isn't important;
5959
/// it just ensures a conflict if two people change the module format.
6060
/// Don't worry about adhering to the 80-column limit for this line.
61-
const uint16_t SWIFTMODULE_VERSION_MINOR = 811; // default argument isolation
61+
const uint16_t SWIFTMODULE_VERSION_MINOR = 812; // @_extern(wasm)
6262

6363
/// A standard hash seed used for all string hashes in a serialized module.
6464
///
@@ -2340,6 +2340,13 @@ namespace decls_block {
23402340
BCBlob // declaration name
23412341
>;
23422342

2343+
using ExternDeclAttrLayout = BCRecordLayout<Extern_DECL_ATTR,
2344+
BCFixed<1>, // implicit flag
2345+
BCVBR<4>, // number of bytes in module name
2346+
BCVBR<4>, // number of bytes in name
2347+
BCBlob // module name and declaration name
2348+
>;
2349+
23432350
using DocumentationDeclAttrLayout = BCRecordLayout<
23442351
Documentation_DECL_ATTR,
23452352
BCFixed<1>, // implicit flag

0 commit comments

Comments
 (0)