aboutsummaryrefslogtreecommitdiff
path: root/lib/Target/WebAssembly/AsmParser/WebAssemblyAsmParser.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'lib/Target/WebAssembly/AsmParser/WebAssemblyAsmParser.cpp')
-rw-r--r--lib/Target/WebAssembly/AsmParser/WebAssemblyAsmParser.cpp278
1 files changed, 203 insertions, 75 deletions
diff --git a/lib/Target/WebAssembly/AsmParser/WebAssemblyAsmParser.cpp b/lib/Target/WebAssembly/AsmParser/WebAssemblyAsmParser.cpp
index 0a5908f43790..09628e872dd5 100644
--- a/lib/Target/WebAssembly/AsmParser/WebAssemblyAsmParser.cpp
+++ b/lib/Target/WebAssembly/AsmParser/WebAssemblyAsmParser.cpp
@@ -1,9 +1,8 @@
//==- WebAssemblyAsmParser.cpp - Assembler for WebAssembly -*- C++ -*-==//
//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
///
@@ -16,12 +15,15 @@
#include "MCTargetDesc/WebAssemblyMCTargetDesc.h"
#include "MCTargetDesc/WebAssemblyTargetStreamer.h"
+#include "TargetInfo/WebAssemblyTargetInfo.h"
#include "WebAssembly.h"
#include "llvm/MC/MCContext.h"
+#include "llvm/MC/MCExpr.h"
#include "llvm/MC/MCInst.h"
#include "llvm/MC/MCInstrInfo.h"
#include "llvm/MC/MCParser/MCParsedAsmOperand.h"
#include "llvm/MC/MCParser/MCTargetAsmParser.h"
+#include "llvm/MC/MCSectionWasm.h"
#include "llvm/MC/MCStreamer.h"
#include "llvm/MC/MCSubtargetInfo.h"
#include "llvm/MC/MCSymbol.h"
@@ -87,9 +89,8 @@ struct WebAssemblyOperand : public MCParsedAsmOperand {
}
bool isToken() const override { return Kind == Token; }
- bool isImm() const override {
- return Kind == Integer || Kind == Float || Kind == Symbol;
- }
+ bool isImm() const override { return Kind == Integer || Kind == Symbol; }
+ bool isFPImm() const { return Kind == Float; }
bool isMem() const override { return false; }
bool isReg() const override { return false; }
bool isBrList() const { return Kind == BrList; }
@@ -116,12 +117,18 @@ struct WebAssemblyOperand : public MCParsedAsmOperand {
assert(N == 1 && "Invalid number of operands!");
if (Kind == Integer)
Inst.addOperand(MCOperand::createImm(Int.Val));
- else if (Kind == Float)
- Inst.addOperand(MCOperand::createFPImm(Flt.Val));
else if (Kind == Symbol)
Inst.addOperand(MCOperand::createExpr(Sym.Exp));
else
- llvm_unreachable("Should be immediate or symbol!");
+ llvm_unreachable("Should be integer immediate or symbol!");
+ }
+
+ void addFPImmOperands(MCInst &Inst, unsigned N) const {
+ assert(N == 1 && "Invalid number of operands!");
+ if (Kind == Float)
+ Inst.addOperand(MCOperand::createFPImm(Flt.Val));
+ else
+ llvm_unreachable("Should be float immediate!");
}
void addBrListOperands(MCInst &Inst, unsigned N) const {
@@ -170,6 +177,8 @@ class WebAssemblyAsmParser final : public MCTargetAsmParser {
FunctionStart,
FunctionLocals,
Instructions,
+ EndFunction,
+ DataSection,
} CurrentState = FileStart;
// For ensuring blocks are properly nested.
@@ -187,6 +196,7 @@ class WebAssemblyAsmParser final : public MCTargetAsmParser {
// We track this to see if a .functype following a label is the same,
// as this is how we recognize the start of a function.
MCSymbol *LastLabel = nullptr;
+ MCSymbol *LastFunctionLabel = nullptr;
public:
WebAssemblyAsmParser(const MCSubtargetInfo &STI, MCAsmParser &Parser,
@@ -250,13 +260,13 @@ public:
}
bool ensureEmptyNestingStack() {
- auto err = !NestingStack.empty();
+ auto Err = !NestingStack.empty();
while (!NestingStack.empty()) {
error(Twine("Unmatched block construct(s) at function end: ") +
nestingString(NestingStack.back()).first);
NestingStack.pop_back();
}
- return err;
+ return Err;
}
bool isNext(AsmToken::TokenKind Kind) {
@@ -298,6 +308,8 @@ public:
Type == "i32x4" || Type == "i64x2" || Type == "f32x4" ||
Type == "f64x2")
return wasm::ValType::V128;
+ if (Type == "exnref")
+ return wasm::ValType::EXNREF;
return Optional<wasm::ValType>();
}
@@ -308,7 +320,7 @@ public:
.Case("f32", WebAssembly::ExprType::F32)
.Case("f64", WebAssembly::ExprType::F64)
.Case("v128", WebAssembly::ExprType::V128)
- .Case("except_ref", WebAssembly::ExprType::ExceptRef)
+ .Case("exnref", WebAssembly::ExprType::Exnref)
.Case("void", WebAssembly::ExprType::Void)
.Default(WebAssembly::ExprType::Invalid);
}
@@ -317,7 +329,7 @@ public:
while (Lexer.is(AsmToken::Identifier)) {
auto Type = parseType(Lexer.getTok().getString());
if (!Type)
- return true;
+ return error("unknown type: ", Lexer.getTok());
Types.push_back(Type.getValue());
Parser.Lex();
if (!isNext(AsmToken::Comma))
@@ -337,27 +349,67 @@ public:
Parser.Lex();
}
- bool parseOperandStartingWithInteger(bool IsNegative, OperandVector &Operands,
- StringRef InstName) {
- parseSingleInteger(IsNegative, Operands);
+ bool parseSingleFloat(bool IsNegative, OperandVector &Operands) {
+ auto &Flt = Lexer.getTok();
+ double Val;
+ if (Flt.getString().getAsDouble(Val, false))
+ return error("Cannot parse real: ", Flt);
+ if (IsNegative)
+ Val = -Val;
+ Operands.push_back(make_unique<WebAssemblyOperand>(
+ WebAssemblyOperand::Float, Flt.getLoc(), Flt.getEndLoc(),
+ WebAssemblyOperand::FltOp{Val}));
+ Parser.Lex();
+ return false;
+ }
+
+ bool parseSpecialFloatMaybe(bool IsNegative, OperandVector &Operands) {
+ if (Lexer.isNot(AsmToken::Identifier))
+ return true;
+ auto &Flt = Lexer.getTok();
+ auto S = Flt.getString();
+ double Val;
+ if (S.compare_lower("infinity") == 0) {
+ Val = std::numeric_limits<double>::infinity();
+ } else if (S.compare_lower("nan") == 0) {
+ Val = std::numeric_limits<double>::quiet_NaN();
+ } else {
+ return true;
+ }
+ if (IsNegative)
+ Val = -Val;
+ Operands.push_back(make_unique<WebAssemblyOperand>(
+ WebAssemblyOperand::Float, Flt.getLoc(), Flt.getEndLoc(),
+ WebAssemblyOperand::FltOp{Val}));
+ Parser.Lex();
+ return false;
+ }
+
+ bool checkForP2AlignIfLoadStore(OperandVector &Operands, StringRef InstName) {
// FIXME: there is probably a cleaner way to do this.
- auto IsLoadStore = InstName.startswith("load") ||
- InstName.startswith("store") ||
- InstName.startswith("atomic_load") ||
- InstName.startswith("atomic_store");
- if (IsLoadStore) {
- // Parse load/store operands of the form: offset align
- auto &Offset = Lexer.getTok();
- if (Offset.is(AsmToken::Integer)) {
+ auto IsLoadStore = InstName.find(".load") != StringRef::npos ||
+ InstName.find(".store") != StringRef::npos;
+ auto IsAtomic = InstName.find("atomic.") != StringRef::npos;
+ if (IsLoadStore || IsAtomic) {
+ // Parse load/store operands of the form: offset:p2align=align
+ if (IsLoadStore && isNext(AsmToken::Colon)) {
+ auto Id = expectIdent();
+ if (Id != "p2align")
+ return error("Expected p2align, instead got: " + Id);
+ if (expect(AsmToken::Equal, "="))
+ return true;
+ if (!Lexer.is(AsmToken::Integer))
+ return error("Expected integer constant");
parseSingleInteger(false, Operands);
} else {
- // Alignment not specified.
- // FIXME: correctly derive a default from the instruction.
+ // Alignment not specified (or atomics, must use default alignment).
// We can't just call WebAssembly::GetDefaultP2Align since we don't have
- // an opcode until after the assembly matcher.
+ // an opcode until after the assembly matcher, so set a default to fix
+ // up later.
+ auto Tok = Lexer.getTok();
Operands.push_back(make_unique<WebAssemblyOperand>(
- WebAssemblyOperand::Integer, Offset.getLoc(), Offset.getEndLoc(),
- WebAssemblyOperand::IntOp{0}));
+ WebAssemblyOperand::Integer, Tok.getLoc(), Tok.getEndLoc(),
+ WebAssemblyOperand::IntOp{-1}));
}
}
return false;
@@ -400,51 +452,45 @@ public:
Operands.push_back(make_unique<WebAssemblyOperand>(
WebAssemblyOperand::Token, NameLoc, SMLoc::getFromPointer(Name.end()),
WebAssemblyOperand::TokOp{Name}));
- auto NamePair = Name.split('.');
- // If no '.', there is no type prefix.
- auto BaseName = NamePair.second.empty() ? NamePair.first : NamePair.second;
// If this instruction is part of a control flow structure, ensure
// proper nesting.
bool ExpectBlockType = false;
- if (BaseName == "block") {
+ if (Name == "block") {
push(Block);
ExpectBlockType = true;
- } else if (BaseName == "loop") {
+ } else if (Name == "loop") {
push(Loop);
ExpectBlockType = true;
- } else if (BaseName == "try") {
+ } else if (Name == "try") {
push(Try);
ExpectBlockType = true;
- } else if (BaseName == "if") {
+ } else if (Name == "if") {
push(If);
ExpectBlockType = true;
- } else if (BaseName == "else") {
- if (pop(BaseName, If))
+ } else if (Name == "else") {
+ if (pop(Name, If))
return true;
push(Else);
- } else if (BaseName == "catch") {
- if (pop(BaseName, Try))
- return true;
- push(Try);
- } else if (BaseName == "catch_all") {
- if (pop(BaseName, Try))
+ } else if (Name == "catch") {
+ if (pop(Name, Try))
return true;
push(Try);
- } else if (BaseName == "end_if") {
- if (pop(BaseName, If, Else))
+ } else if (Name == "end_if") {
+ if (pop(Name, If, Else))
return true;
- } else if (BaseName == "end_try") {
- if (pop(BaseName, Try))
+ } else if (Name == "end_try") {
+ if (pop(Name, Try))
return true;
- } else if (BaseName == "end_loop") {
- if (pop(BaseName, Loop))
+ } else if (Name == "end_loop") {
+ if (pop(Name, Loop))
return true;
- } else if (BaseName == "end_block") {
- if (pop(BaseName, Block))
+ } else if (Name == "end_block") {
+ if (pop(Name, Block))
return true;
- } else if (BaseName == "end_function") {
- if (pop(BaseName, Function) || ensureEmptyNestingStack())
+ } else if (Name == "end_function") {
+ CurrentState = EndFunction;
+ if (pop(Name, Function) || ensureEmptyNestingStack())
return true;
}
@@ -452,6 +498,8 @@ public:
auto &Tok = Lexer.getTok();
switch (Tok.getKind()) {
case AsmToken::Identifier: {
+ if (!parseSpecialFloatMaybe(false, Operands))
+ break;
auto &Id = Lexer.getTok();
if (ExpectBlockType) {
// Assume this identifier is a block_type.
@@ -464,33 +512,39 @@ public:
// Assume this identifier is a label.
const MCExpr *Val;
SMLoc End;
- if (Parser.parsePrimaryExpr(Val, End))
+ if (Parser.parseExpression(Val, End))
return error("Cannot parse symbol: ", Lexer.getTok());
Operands.push_back(make_unique<WebAssemblyOperand>(
WebAssemblyOperand::Symbol, Id.getLoc(), Id.getEndLoc(),
WebAssemblyOperand::SymOp{Val}));
+ if (checkForP2AlignIfLoadStore(Operands, Name))
+ return true;
}
break;
}
case AsmToken::Minus:
Parser.Lex();
- if (Lexer.isNot(AsmToken::Integer))
- return error("Expected integer instead got: ", Lexer.getTok());
- if (parseOperandStartingWithInteger(true, Operands, BaseName))
- return true;
+ if (Lexer.is(AsmToken::Integer)) {
+ parseSingleInteger(true, Operands);
+ if (checkForP2AlignIfLoadStore(Operands, Name))
+ return true;
+ } else if(Lexer.is(AsmToken::Real)) {
+ if (parseSingleFloat(true, Operands))
+ return true;
+ } else if (!parseSpecialFloatMaybe(true, Operands)) {
+ } else {
+ return error("Expected numeric constant instead got: ",
+ Lexer.getTok());
+ }
break;
case AsmToken::Integer:
- if (parseOperandStartingWithInteger(false, Operands, BaseName))
+ parseSingleInteger(false, Operands);
+ if (checkForP2AlignIfLoadStore(Operands, Name))
return true;
break;
case AsmToken::Real: {
- double Val;
- if (Tok.getString().getAsDouble(Val, false))
- return error("Cannot parse real: ", Tok);
- Operands.push_back(make_unique<WebAssemblyOperand>(
- WebAssemblyOperand::Float, Tok.getLoc(), Tok.getEndLoc(),
- WebAssemblyOperand::FltOp{Val}));
- Parser.Lex();
+ if (parseSingleFloat(false, Operands))
+ return true;
break;
}
case AsmToken::LCurly: {
@@ -547,6 +601,17 @@ public:
return false;
}
+ bool CheckDataSection() {
+ if (CurrentState != DataSection) {
+ auto WS = cast<MCSectionWasm>(getStreamer().getCurrentSection().first);
+ if (WS && WS->getKind().isText())
+ return error("data directive must occur in a data segment: ",
+ Lexer.getTok());
+ }
+ CurrentState = DataSection;
+ return false;
+ }
+
// This function processes wasm-specific directives streamed to
// WebAssemblyTargetStreamer, all others go to the generic parser
// (see WasmAsmParser).
@@ -561,6 +626,7 @@ public:
auto &Out = getStreamer();
auto &TOut =
reinterpret_cast<WebAssemblyTargetStreamer &>(*Out.getTargetStreamer());
+ auto &Ctx = Out.getContext();
// TODO: any time we return an error, at least one token must have been
// consumed, otherwise this will not signal an error to the caller.
@@ -578,8 +644,7 @@ public:
if (!Type)
return error("Unknown type in .globaltype directive: ", TypeTok);
// Now set this symbol with the correct type.
- auto WasmSym = cast<MCSymbolWasm>(
- TOut.getStreamer().getContext().getOrCreateSymbol(SymName));
+ auto WasmSym = cast<MCSymbolWasm>(Ctx.getOrCreateSymbol(SymName));
WasmSym->setType(wasm::WASM_SYMBOL_TYPE_GLOBAL);
WasmSym->setGlobalType(
wasm::WasmGlobalType{uint8_t(Type.getValue()), true});
@@ -597,13 +662,13 @@ public:
auto SymName = expectIdent();
if (SymName.empty())
return true;
- auto WasmSym = cast<MCSymbolWasm>(
- TOut.getStreamer().getContext().getOrCreateSymbol(SymName));
+ auto WasmSym = cast<MCSymbolWasm>(Ctx.getOrCreateSymbol(SymName));
if (CurrentState == Label && WasmSym == LastLabel) {
// This .functype indicates a start of a function.
if (ensureEmptyNestingStack())
return true;
CurrentState = FunctionStart;
+ LastFunctionLabel = LastLabel;
push(Function);
}
auto Signature = make_unique<wasm::WasmSignature>();
@@ -621,8 +686,7 @@ public:
auto SymName = expectIdent();
if (SymName.empty())
return true;
- auto WasmSym = cast<MCSymbolWasm>(
- TOut.getStreamer().getContext().getOrCreateSymbol(SymName));
+ auto WasmSym = cast<MCSymbolWasm>(Ctx.getOrCreateSymbol(SymName));
auto Signature = make_unique<wasm::WasmSignature>();
if (parseRegTypeList(Signature->Params))
return true;
@@ -646,6 +710,30 @@ public:
return expect(AsmToken::EndOfStatement, "EOL");
}
+ if (DirectiveID.getString() == ".int8" ||
+ DirectiveID.getString() == ".int16" ||
+ DirectiveID.getString() == ".int32" ||
+ DirectiveID.getString() == ".int64") {
+ if (CheckDataSection()) return true;
+ const MCExpr *Val;
+ SMLoc End;
+ if (Parser.parseExpression(Val, End))
+ return error("Cannot parse .int expression: ", Lexer.getTok());
+ size_t NumBits = 0;
+ DirectiveID.getString().drop_front(4).getAsInteger(10, NumBits);
+ Out.EmitValue(Val, NumBits / 8, End);
+ return expect(AsmToken::EndOfStatement, "EOL");
+ }
+
+ if (DirectiveID.getString() == ".asciz") {
+ if (CheckDataSection()) return true;
+ std::string S;
+ if (Parser.parseEscapedString(S))
+ return error("Cannot parse string constant: ", Lexer.getTok());
+ Out.EmitBytes(StringRef(S.c_str(), S.length() + 1));
+ return expect(AsmToken::EndOfStatement, "EOL");
+ }
+
return true; // We didn't process this directive.
}
@@ -667,8 +755,19 @@ public:
*Out.getTargetStreamer());
TOut.emitLocal(SmallVector<wasm::ValType, 0>());
}
- CurrentState = Instructions;
+ // Fix unknown p2align operands.
+ auto Align = WebAssembly::GetDefaultP2AlignAny(Inst.getOpcode());
+ if (Align != -1U) {
+ auto &Op0 = Inst.getOperand(0);
+ if (Op0.getImm() == -1)
+ Op0.setImm(Align);
+ }
Out.EmitInstruction(Inst, getSTI());
+ if (CurrentState == EndFunction) {
+ onEndOfFunction();
+ } else {
+ CurrentState = Instructions;
+ }
return false;
}
case Match_MissingFeature:
@@ -694,6 +793,35 @@ public:
llvm_unreachable("Implement any new match types added!");
}
+ void doBeforeLabelEmit(MCSymbol *Symbol) override {
+ // Start a new section for the next function automatically, since our
+ // object writer expects each function to have its own section. This way
+ // The user can't forget this "convention".
+ auto SymName = Symbol->getName();
+ if (SymName.startswith(".L"))
+ return; // Local Symbol.
+ // Only create a new text section if we're already in one.
+ auto CWS = cast<MCSectionWasm>(getStreamer().getCurrentSection().first);
+ if (!CWS || !CWS->getKind().isText())
+ return;
+ auto SecName = ".text." + SymName;
+ auto WS = getContext().getWasmSection(SecName, SectionKind::getText());
+ getStreamer().SwitchSection(WS);
+ }
+
+ void onEndOfFunction() {
+ // Automatically output a .size directive, so it becomes optional for the
+ // user.
+ if (!LastFunctionLabel) return;
+ auto TempSym = getContext().createLinkerPrivateTempSymbol();
+ getStreamer().EmitLabel(TempSym);
+ auto Start = MCSymbolRefExpr::create(LastFunctionLabel, getContext());
+ auto End = MCSymbolRefExpr::create(TempSym, getContext());
+ auto Expr =
+ MCBinaryExpr::create(MCBinaryExpr::Sub, End, Start, getContext());
+ getStreamer().emitELFSize(LastFunctionLabel, Expr);
+ }
+
void onEndOfFile() override { ensureEmptyNestingStack(); }
};
} // end anonymous namespace