[WebAssembly][MC] Record limit constraints for table sizes

This commit adds a full WasmTableType to MCSymbolWasm, differing from
the current situation (just an ElemType) in that it additionally records
a WasmLimits.

We add support for specifying the limits in .S files also, via the
following syntax variations:

  .tabletype SYM, ELEMTYPE
  .tabletype SYM, ELEMTYPE, MINSIZE
  .tabletype SYM, ELEMTYPE, MINSIZE, MAXSIZE

Depends on D99186.

Differential Revision: https://reviews.llvm.org/D99191
This commit is contained in:
Andy Wingo 2021-03-23 16:13:54 +01:00
parent d905c10353
commit c9801db2eb
5 changed files with 67 additions and 25 deletions

View File

@ -26,7 +26,7 @@ class MCSymbolWasm : public MCSymbol {
Optional<StringRef> ExportName; Optional<StringRef> ExportName;
wasm::WasmSignature *Signature = nullptr; wasm::WasmSignature *Signature = nullptr;
Optional<wasm::WasmGlobalType> GlobalType; Optional<wasm::WasmGlobalType> GlobalType;
Optional<wasm::ValType> TableType; Optional<wasm::WasmTableType> TableType;
Optional<wasm::WasmEventType> EventType; Optional<wasm::WasmEventType> EventType;
/// An expression describing how to calculate the size of a symbol. If a /// An expression describing how to calculate the size of a symbol. If a
@ -108,7 +108,7 @@ public:
bool isFunctionTable() const { bool isFunctionTable() const {
return isTable() && hasTableType() && return isTable() && hasTableType() &&
getTableType() == wasm::ValType::FUNCREF; getTableType().ElemType == wasm::WASM_TYPE_FUNCREF;
} }
void setFunctionTable() { void setFunctionTable() {
setType(wasm::WASM_SYMBOL_TYPE_TABLE); setType(wasm::WASM_SYMBOL_TYPE_TABLE);
@ -131,11 +131,17 @@ public:
void setGlobalType(wasm::WasmGlobalType GT) { GlobalType = GT; } void setGlobalType(wasm::WasmGlobalType GT) { GlobalType = GT; }
bool hasTableType() const { return TableType.hasValue(); } bool hasTableType() const { return TableType.hasValue(); }
wasm::ValType getTableType() const { const wasm::WasmTableType &getTableType() const {
assert(hasTableType()); assert(hasTableType());
return TableType.getValue(); return TableType.getValue();
} }
void setTableType(wasm::ValType TT) { TableType = TT; } void setTableType(wasm::WasmTableType TT) { TableType = TT; }
void setTableType(wasm::ValType VT) {
// Declare a table with element type VT and no limits (min size 0, no max
// size).
wasm::WasmLimits Limits = {wasm::WASM_LIMITS_FLAG_NONE, 0, 0};
setTableType({uint8_t(VT), Limits});
}
const wasm::WasmEventType &getEventType() const { const wasm::WasmEventType &getEventType() const {
assert(EventType.hasValue()); assert(EventType.hasValue());

View File

@ -1338,12 +1338,7 @@ void WasmObjectWriter::prepareImports(
Import.Module = WS.getImportModule(); Import.Module = WS.getImportModule();
Import.Field = WS.getImportName(); Import.Field = WS.getImportName();
Import.Kind = wasm::WASM_EXTERNAL_TABLE; Import.Kind = wasm::WASM_EXTERNAL_TABLE;
wasm::ValType ElemType = WS.getTableType(); Import.Table = WS.getTableType();
Import.Table.ElemType = uint8_t(ElemType);
// FIXME: Extend table type to include limits? For now we don't specify
// a min or max which does not place any restrictions on the size of the
// imported table.
Import.Table.Limits = {wasm::WASM_LIMITS_FLAG_NONE, 0, 0};
Imports.push_back(Import); Imports.push_back(Import);
assert(WasmIndices.count(&WS) == 0); assert(WasmIndices.count(&WS) == 0);
WasmIndices[&WS] = NumTableImports++; WasmIndices[&WS] = NumTableImports++;
@ -1626,9 +1621,7 @@ uint64_t WasmObjectWriter::writeOneObject(MCAssembler &Asm,
if (WS.isDefined()) { if (WS.isDefined()) {
wasm::WasmTable Table; wasm::WasmTable Table;
Table.Index = NumTableImports + Tables.size(); Table.Index = NumTableImports + Tables.size();
Table.Type.ElemType = static_cast<uint8_t>(WS.getTableType()); Table.Type = WS.getTableType();
// FIXME: Work on custom limits is ongoing
Table.Type.Limits = {wasm::WASM_LIMITS_FLAG_NONE, 0, 0};
assert(WasmIndices.count(&WS) == 0); assert(WasmIndices.count(&WS) == 0);
WasmIndices[&WS] = Table.Index; WasmIndices[&WS] = Table.Index;
Tables.push_back(Table); Tables.push_back(Table);

View File

@ -169,6 +169,11 @@ struct WebAssemblyOperand : public MCParsedAsmOperand {
} }
}; };
// Perhaps this should go somewhere common.
static wasm::WasmLimits DefaultLimits() {
return {wasm::WASM_LIMITS_FLAG_NONE, 0, 0};
}
static MCSymbolWasm *GetOrCreateFunctionTableSymbol(MCContext &Ctx, static MCSymbolWasm *GetOrCreateFunctionTableSymbol(MCContext &Ctx,
const StringRef &Name) { const StringRef &Name) {
MCSymbolWasm *Sym = cast_or_null<MCSymbolWasm>(Ctx.lookupSymbol(Name)); MCSymbolWasm *Sym = cast_or_null<MCSymbolWasm>(Ctx.lookupSymbol(Name));
@ -487,6 +492,28 @@ public:
WebAssemblyOperand::IntOp{static_cast<int64_t>(BT)})); WebAssemblyOperand::IntOp{static_cast<int64_t>(BT)}));
} }
bool parseLimits(wasm::WasmLimits *Limits) {
auto Tok = Lexer.getTok();
if (!Tok.is(AsmToken::Integer))
return error("Expected integer constant, instead got: ", Tok);
int64_t Val = Tok.getIntVal();
assert(Val >= 0);
Limits->Minimum = Val;
Parser.Lex();
if (isNext(AsmToken::Comma)) {
Limits->Flags |= wasm::WASM_LIMITS_FLAG_HAS_MAX;
auto Tok = Lexer.getTok();
if (!Tok.is(AsmToken::Integer))
return error("Expected integer constant, instead got: ", Tok);
int64_t Val = Tok.getIntVal();
assert(Val >= 0);
Limits->Maximum = Val;
Parser.Lex();
}
return false;
}
bool parseFunctionTableOperand(std::unique_ptr<WebAssemblyOperand> *Op) { bool parseFunctionTableOperand(std::unique_ptr<WebAssemblyOperand> *Op) {
if (STI->checkFeatures("+reference-types")) { if (STI->checkFeatures("+reference-types")) {
// If the reference-types feature is enabled, there is an explicit table // If the reference-types feature is enabled, there is an explicit table
@ -819,24 +846,31 @@ public:
} }
if (DirectiveID.getString() == ".tabletype") { if (DirectiveID.getString() == ".tabletype") {
// .tabletype SYM, ELEMTYPE[, MINSIZE[, MAXSIZE]]
auto SymName = expectIdent(); auto SymName = expectIdent();
if (SymName.empty()) if (SymName.empty())
return true; return true;
if (expect(AsmToken::Comma, ",")) if (expect(AsmToken::Comma, ","))
return true; return true;
auto TypeTok = Lexer.getTok();
auto TypeName = expectIdent(); auto ElemTypeTok = Lexer.getTok();
if (TypeName.empty()) auto ElemTypeName = expectIdent();
if (ElemTypeName.empty())
return true;
Optional<wasm::ValType> ElemType = parseType(ElemTypeName);
if (!ElemType)
return error("Unknown type in .tabletype directive: ", ElemTypeTok);
wasm::WasmLimits Limits = DefaultLimits();
if (isNext(AsmToken::Comma) && parseLimits(&Limits))
return true; return true;
auto Type = parseType(TypeName);
if (!Type)
return error("Unknown type in .tabletype directive: ", TypeTok);
// Now that we have the name and table type, we can actually create the // Now that we have the name and table type, we can actually create the
// symbol // symbol
auto WasmSym = cast<MCSymbolWasm>(Ctx.getOrCreateSymbol(SymName)); auto WasmSym = cast<MCSymbolWasm>(Ctx.getOrCreateSymbol(SymName));
WasmSym->setType(wasm::WASM_SYMBOL_TYPE_TABLE); WasmSym->setType(wasm::WASM_SYMBOL_TYPE_TABLE);
WasmSym->setTableType(Type.getValue()); wasm::WasmTableType Type = {uint8_t(ElemType.getValue()), Limits};
WasmSym->setTableType(Type);
TOut.emitTableType(WasmSym); TOut.emitTableType(WasmSym);
return expect(AsmToken::EndOfStatement, "EOL"); return expect(AsmToken::EndOfStatement, "EOL");
} }

View File

@ -79,8 +79,15 @@ void WebAssemblyTargetAsmStreamer::emitGlobalType(const MCSymbolWasm *Sym) {
void WebAssemblyTargetAsmStreamer::emitTableType(const MCSymbolWasm *Sym) { void WebAssemblyTargetAsmStreamer::emitTableType(const MCSymbolWasm *Sym) {
assert(Sym->isTable()); assert(Sym->isTable());
const wasm::WasmTableType &Type = Sym->getTableType();
OS << "\t.tabletype\t" << Sym->getName() << ", " OS << "\t.tabletype\t" << Sym->getName() << ", "
<< WebAssembly::typeToString(Sym->getTableType()); << WebAssembly::typeToString(static_cast<wasm::ValType>(Type.ElemType));
bool HasMaximum = Type.Limits.Flags & wasm::WASM_LIMITS_FLAG_HAS_MAX;
if (Type.Limits.Minimum != 0 || HasMaximum) {
OS << ", " << Type.Limits.Minimum;
if (HasMaximum)
OS << ", " << Type.Limits.Maximum;
}
OS << '\n'; OS << '\n';
} }

View File

@ -16,9 +16,9 @@ bar:
.tabletype bar, funcref .tabletype bar, funcref
table1: table1:
.tabletype table1, funcref .tabletype table1, funcref, 42
table2: table2:
.tabletype table2, funcref .tabletype table2, funcref, 42, 100
# Table instructions # Table instructions
@ -132,11 +132,13 @@ table_fill:
# BIN-NEXT: - Index: 2 # BIN-NEXT: - Index: 2
# BIN-NEXT: ElemType: FUNCREF # BIN-NEXT: ElemType: FUNCREF
# BIN-NEXT: Limits: # BIN-NEXT: Limits:
# BIN-NEXT: Minimum: 0x0 # BIN-NEXT: Minimum: 0x2A
# BIN-NEXT: - Index: 3 # BIN-NEXT: - Index: 3
# BIN-NEXT: ElemType: FUNCREF # BIN-NEXT: ElemType: FUNCREF
# BIN-NEXT: Limits: # BIN-NEXT: Limits:
# BIN-NEXT: Minimum: 0x0 # BIN-NEXT: Flags: [ HAS_MAX ]
# BIN-NEXT: Minimum: 0x2A
# BIN-NEXT: Maximum: 0x64
# BIN: - Type: CODE # BIN: - Type: CODE
# BIN-NEXT: Relocations: # BIN-NEXT: Relocations: