mirror of
https://github.com/RPCS3/llvm-mirror.git
synced 2024-11-21 18:22:53 +01:00
[WebAssembly] Added initial type checker to MC Assembler
This to protect against non-sensical instruction sequences being assembled, which would either cause asserts/crashes further down, or a Wasm module being output that doesn't validate. Unlike a validator, this type checker is able to give type-errors as part of the parsing process, which makes the assembler much friendlier to be used by humans writing manual input. Because the MC system is single pass (instructions aren't even stored in MC format, they are directly output) the type checker has to be single pass as well, which means that from now on .globaltype and .functype decls must come before their use. An extra pass is added to Codegen to collect information for this purpose, since AsmPrinter is normally single pass / streaming as well, and would otherwise generate this information on the fly. A `-no-type-check` flag was added to llvm-mc (and any other tools that take asm input) that surpresses type errors, as a quick escape hatch for tests that were not intended to be type correct. This is a first version of the type checker that ignores control flow, i.e. it checks that types are correct along the linear path, but not the branch path. This will still catch most errors. Branch checking could be added in the future. Differential Revision: https://reviews.llvm.org/D104945
This commit is contained in:
parent
6212b5a386
commit
538b137e0b
@ -185,7 +185,7 @@ public:
|
||||
/// Machine Function map.
|
||||
void deleteMachineFunctionFor(Function &F);
|
||||
|
||||
/// Keep track of various per-function pieces of information for backends
|
||||
/// Keep track of various per-module pieces of information for backends
|
||||
/// that would like to do so.
|
||||
template<typename Ty>
|
||||
Ty &getObjFileInfo() {
|
||||
|
@ -15,6 +15,7 @@
|
||||
#define LLVM_CODEGEN_MACHINEMODULEINFOIMPLS_H
|
||||
|
||||
#include "llvm/ADT/DenseMap.h"
|
||||
#include "llvm/ADT/StringSet.h"
|
||||
#include "llvm/CodeGen/MachineModuleInfo.h"
|
||||
#include <cassert>
|
||||
|
||||
@ -101,6 +102,17 @@ public:
|
||||
SymbolListTy GetGVStubList() { return getSortedStubs(GVStubs); }
|
||||
};
|
||||
|
||||
/// MachineModuleInfoWasm - This is a MachineModuleInfoImpl implementation
|
||||
/// for Wasm targets.
|
||||
class MachineModuleInfoWasm : public MachineModuleInfoImpl {
|
||||
virtual void anchor(); // Out of line virtual method.
|
||||
|
||||
public:
|
||||
MachineModuleInfoWasm(const MachineModuleInfo &) {}
|
||||
|
||||
StringSet<> MachineSymbolsUsed;
|
||||
};
|
||||
|
||||
} // end namespace llvm
|
||||
|
||||
#endif // LLVM_CODEGEN_MACHINEMODULEINFOIMPLS_H
|
||||
|
@ -45,6 +45,7 @@ public:
|
||||
bool MCFatalWarnings : 1;
|
||||
bool MCNoWarn : 1;
|
||||
bool MCNoDeprecatedWarn : 1;
|
||||
bool MCNoTypeCheck : 1;
|
||||
bool MCSaveTempLabels : 1;
|
||||
bool MCUseDwarfDirectory : 1;
|
||||
bool MCIncrementalLinkerCompatible : 1;
|
||||
|
@ -40,6 +40,8 @@ bool getNoWarn();
|
||||
|
||||
bool getNoDeprecatedWarn();
|
||||
|
||||
bool getNoTypeCheck();
|
||||
|
||||
std::string getABIName();
|
||||
|
||||
/// Create this object with static storage to register mc-related command
|
||||
|
@ -25,6 +25,7 @@ using namespace llvm;
|
||||
void MachineModuleInfoMachO::anchor() {}
|
||||
void MachineModuleInfoELF::anchor() {}
|
||||
void MachineModuleInfoCOFF::anchor() {}
|
||||
void MachineModuleInfoWasm::anchor() {}
|
||||
|
||||
using PairTy = std::pair<MCSymbol *, MachineModuleInfoImpl::StubValueTy>;
|
||||
static int SortSymbolPair(const PairTy *LHS, const PairTy *RHS) {
|
||||
|
@ -13,7 +13,8 @@ using namespace llvm;
|
||||
|
||||
MCTargetOptions::MCTargetOptions()
|
||||
: MCRelaxAll(false), MCNoExecStack(false), MCFatalWarnings(false),
|
||||
MCNoWarn(false), MCNoDeprecatedWarn(false), MCSaveTempLabels(false),
|
||||
MCNoWarn(false), MCNoDeprecatedWarn(false),
|
||||
MCNoTypeCheck(false), MCSaveTempLabels(false),
|
||||
MCUseDwarfDirectory(false), MCIncrementalLinkerCompatible(false),
|
||||
ShowMCEncoding(false), ShowMCInst(false), AsmVerbose(false),
|
||||
PreserveAsmComments(true), Dwarf64(false) {}
|
||||
|
@ -43,6 +43,7 @@ MCOPT(bool, ShowMCInst)
|
||||
MCOPT(bool, FatalWarnings)
|
||||
MCOPT(bool, NoWarn)
|
||||
MCOPT(bool, NoDeprecatedWarn)
|
||||
MCOPT(bool, NoTypeCheck)
|
||||
MCOPT(std::string, ABIName)
|
||||
|
||||
llvm::mc::RegisterMCTargetOptionsFlags::RegisterMCTargetOptionsFlags() {
|
||||
@ -90,6 +91,10 @@ llvm::mc::RegisterMCTargetOptionsFlags::RegisterMCTargetOptionsFlags() {
|
||||
"no-deprecated-warn", cl::desc("Suppress all deprecated warnings"));
|
||||
MCBINDOPT(NoDeprecatedWarn);
|
||||
|
||||
static cl::opt<bool> NoTypeCheck(
|
||||
"no-type-check", cl::desc("Suppress type errors (Wasm)"));
|
||||
MCBINDOPT(NoTypeCheck);
|
||||
|
||||
static cl::opt<std::string> ABIName(
|
||||
"target-abi", cl::Hidden,
|
||||
cl::desc("The name of the ABI to be targeted from the backend."),
|
||||
@ -110,5 +115,6 @@ MCTargetOptions llvm::mc::InitMCTargetOptionsFromFlags() {
|
||||
Options.MCFatalWarnings = getFatalWarnings();
|
||||
Options.MCNoWarn = getNoWarn();
|
||||
Options.MCNoDeprecatedWarn = getNoDeprecatedWarn();
|
||||
Options.MCNoTypeCheck = getNoTypeCheck();
|
||||
return Options;
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
add_llvm_component_library(LLVMWebAssemblyAsmParser
|
||||
WebAssemblyAsmParser.cpp
|
||||
WebAssemblyAsmTypeCheck.cpp
|
||||
|
||||
LINK_COMPONENTS
|
||||
MC
|
||||
|
@ -13,6 +13,7 @@
|
||||
///
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "AsmParser/WebAssemblyAsmTypeCheck.h"
|
||||
#include "MCTargetDesc/WebAssemblyMCTargetDesc.h"
|
||||
#include "MCTargetDesc/WebAssemblyTargetStreamer.h"
|
||||
#include "TargetInfo/WebAssemblyTargetInfo.h"
|
||||
@ -31,6 +32,7 @@
|
||||
#include "llvm/MC/MCSymbol.h"
|
||||
#include "llvm/MC/MCSymbolWasm.h"
|
||||
#include "llvm/Support/Endian.h"
|
||||
#include "llvm/Support/SourceMgr.h"
|
||||
#include "llvm/Support/TargetRegistry.h"
|
||||
|
||||
using namespace llvm;
|
||||
@ -225,17 +227,36 @@ class WebAssemblyAsmParser final : public MCTargetAsmParser {
|
||||
Else,
|
||||
Undefined,
|
||||
};
|
||||
std::vector<NestingType> NestingStack;
|
||||
struct Nested {
|
||||
NestingType NT;
|
||||
wasm::WasmSignature Sig;
|
||||
};
|
||||
std::vector<Nested> NestingStack;
|
||||
|
||||
MCSymbolWasm *DefaultFunctionTable = nullptr;
|
||||
MCSymbol *LastFunctionLabel = nullptr;
|
||||
|
||||
bool is64;
|
||||
|
||||
WebAssemblyAsmTypeCheck TC;
|
||||
// Don't type check if -no-type-check was set.
|
||||
bool SkipTypeCheck;
|
||||
|
||||
public:
|
||||
WebAssemblyAsmParser(const MCSubtargetInfo &STI, MCAsmParser &Parser,
|
||||
const MCInstrInfo &MII, const MCTargetOptions &Options)
|
||||
: MCTargetAsmParser(Options, STI, MII), Parser(Parser),
|
||||
Lexer(Parser.getLexer()) {
|
||||
Lexer(Parser.getLexer()),
|
||||
is64(STI.getTargetTriple().isArch64Bit()),
|
||||
TC(Parser, MII, is64), SkipTypeCheck(Options.MCNoTypeCheck) {
|
||||
setAvailableFeatures(ComputeAvailableFeatures(STI.getFeatureBits()));
|
||||
// Don't type check if this is inline asm, since that is a naked sequence of
|
||||
// instructions without a function/locals decl.
|
||||
auto &SM = Parser.getSourceManager();
|
||||
auto BufferName =
|
||||
SM.getBufferInfo(SM.getMainFileID()).Buffer->getBufferIdentifier();
|
||||
if (BufferName == "<inline asm>")
|
||||
SkipTypeCheck = true;
|
||||
}
|
||||
|
||||
void Initialize(MCAsmParser &Parser) override {
|
||||
@ -300,15 +321,16 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
void push(NestingType NT) { NestingStack.push_back(NT); }
|
||||
void push(NestingType NT) { NestingStack.push_back({NT}); }
|
||||
|
||||
bool pop(StringRef Ins, NestingType NT1, NestingType NT2 = Undefined) {
|
||||
if (NestingStack.empty())
|
||||
return error(Twine("End of block construct with no start: ") + Ins);
|
||||
auto Top = NestingStack.back();
|
||||
if (Top != NT1 && Top != NT2)
|
||||
if (Top.NT != NT1 && Top.NT != NT2)
|
||||
return error(Twine("Block construct type mismatch, expected: ") +
|
||||
nestingString(Top).second + ", instead got: " + Ins);
|
||||
nestingString(Top.NT).second + ", instead got: " + Ins);
|
||||
TC.setLastSig(Top.Sig);
|
||||
NestingStack.pop_back();
|
||||
return false;
|
||||
}
|
||||
@ -317,7 +339,7 @@ public:
|
||||
auto Err = !NestingStack.empty();
|
||||
while (!NestingStack.empty()) {
|
||||
error(Twine("Unmatched block construct(s) at function end: ") +
|
||||
nestingString(NestingStack.back()).first);
|
||||
nestingString(NestingStack.back().NT).first);
|
||||
NestingStack.pop_back();
|
||||
}
|
||||
return Err;
|
||||
@ -446,6 +468,11 @@ public:
|
||||
|
||||
void addBlockTypeOperand(OperandVector &Operands, SMLoc NameLoc,
|
||||
WebAssembly::BlockType BT) {
|
||||
if (BT != WebAssembly::BlockType::Void) {
|
||||
wasm::WasmSignature Sig({static_cast<wasm::ValType>(BT)}, {});
|
||||
TC.setLastSig(Sig);
|
||||
NestingStack.back().Sig = Sig;
|
||||
}
|
||||
Operands.push_back(std::make_unique<WebAssemblyOperand>(
|
||||
WebAssemblyOperand::Integer, NameLoc, NameLoc,
|
||||
WebAssemblyOperand::IntOp{static_cast<int64_t>(BT)}));
|
||||
@ -612,6 +639,9 @@ public:
|
||||
return true;
|
||||
// Got signature as block type, don't need more
|
||||
ExpectBlockType = false;
|
||||
TC.setLastSig(*Signature.get());
|
||||
if (ExpectBlockType)
|
||||
NestingStack.back().Sig = *Signature.get();
|
||||
auto &Ctx = getContext();
|
||||
// The "true" here will cause this to be a nameless symbol.
|
||||
MCSymbol *Sym = Ctx.createTempSymbol("typeindex", true);
|
||||
@ -855,6 +885,7 @@ public:
|
||||
auto Signature = std::make_unique<wasm::WasmSignature>();
|
||||
if (parseSignature(Signature.get()))
|
||||
return true;
|
||||
TC.funcDecl(*Signature);
|
||||
WasmSym->setSignature(Signature.get());
|
||||
addSignature(std::move(Signature));
|
||||
WasmSym->setType(wasm::WASM_SYMBOL_TYPE_FUNCTION);
|
||||
@ -922,6 +953,7 @@ public:
|
||||
SmallVector<wasm::ValType, 4> Locals;
|
||||
if (parseRegTypeList(Locals))
|
||||
return true;
|
||||
TC.localDecl(Locals);
|
||||
TOut.emitLocal(Locals);
|
||||
CurrentState = FunctionLocals;
|
||||
return expect(AsmToken::EndOfStatement, "EOL");
|
||||
@ -986,7 +1018,7 @@ public:
|
||||
if (Op0.getImm() == -1)
|
||||
Op0.setImm(Align);
|
||||
}
|
||||
if (getSTI().getTargetTriple().isArch64Bit()) {
|
||||
if (is64) {
|
||||
// Upgrade 32-bit loads/stores to 64-bit. These mostly differ by having
|
||||
// an offset64 arg instead of offset32, but to the assembler matcher
|
||||
// they're both immediates so don't get selected for.
|
||||
@ -996,9 +1028,11 @@ public:
|
||||
Inst.setOpcode(Opc64);
|
||||
}
|
||||
}
|
||||
if (!SkipTypeCheck && TC.typeCheck(IDLoc, Inst))
|
||||
return true;
|
||||
Out.emitInstruction(Inst, getSTI());
|
||||
if (CurrentState == EndFunction) {
|
||||
onEndOfFunction();
|
||||
onEndOfFunction(IDLoc);
|
||||
} else {
|
||||
CurrentState = Instructions;
|
||||
}
|
||||
@ -1078,7 +1112,9 @@ public:
|
||||
getContext().addGenDwarfSection(WS);
|
||||
}
|
||||
|
||||
void onEndOfFunction() {
|
||||
void onEndOfFunction(SMLoc ErrorLoc) {
|
||||
TC.endOfFunction(ErrorLoc);
|
||||
|
||||
// Automatically output a .size directive, so it becomes optional for the
|
||||
// user.
|
||||
if (!LastFunctionLabel) return;
|
||||
@ -1105,3 +1141,14 @@ extern "C" LLVM_EXTERNAL_VISIBILITY void LLVMInitializeWebAssemblyAsmParser() {
|
||||
#define GET_SUBTARGET_FEATURE_NAME
|
||||
#define GET_MATCHER_IMPLEMENTATION
|
||||
#include "WebAssemblyGenAsmMatcher.inc"
|
||||
|
||||
StringRef GetMnemonic(unsigned Opc) {
|
||||
// FIXME: linear search!
|
||||
for (auto &ME : MatchTable0) {
|
||||
if (ME.Opcode == Opc) {
|
||||
return ME.getMnemonic();
|
||||
}
|
||||
}
|
||||
assert(false && "mnemonic not found");
|
||||
return StringRef();
|
||||
}
|
||||
|
262
lib/Target/WebAssembly/AsmParser/WebAssemblyAsmTypeCheck.cpp
Normal file
262
lib/Target/WebAssembly/AsmParser/WebAssemblyAsmTypeCheck.cpp
Normal file
@ -0,0 +1,262 @@
|
||||
//==- WebAssemblyAsmTypeCheck.cpp - Assembler for WebAssembly -*- C++ -*-==//
|
||||
//
|
||||
// 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
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
///
|
||||
/// \file
|
||||
/// This file is part of the WebAssembly Assembler.
|
||||
///
|
||||
/// It contains code to translate a parsed .s file into MCInsts.
|
||||
///
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "AsmParser/WebAssemblyAsmTypeCheck.h"
|
||||
#include "MCTargetDesc/WebAssemblyMCTargetDesc.h"
|
||||
#include "MCTargetDesc/WebAssemblyTargetStreamer.h"
|
||||
#include "TargetInfo/WebAssemblyTargetInfo.h"
|
||||
#include "Utils/WebAssemblyTypeUtilities.h"
|
||||
#include "Utils/WebAssemblyUtilities.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"
|
||||
#include "llvm/MC/MCSymbolWasm.h"
|
||||
#include "llvm/Support/Endian.h"
|
||||
#include "llvm/Support/SourceMgr.h"
|
||||
#include "llvm/Support/TargetRegistry.h"
|
||||
|
||||
using namespace llvm;
|
||||
|
||||
#define DEBUG_TYPE "wasm-asm-parser"
|
||||
|
||||
extern StringRef GetMnemonic(unsigned Opc);
|
||||
|
||||
namespace llvm {
|
||||
|
||||
WebAssemblyAsmTypeCheck::WebAssemblyAsmTypeCheck(MCAsmParser &Parser,
|
||||
const MCInstrInfo &MII, bool is64)
|
||||
: Parser(Parser), MII(MII), is64(is64) {
|
||||
}
|
||||
|
||||
void WebAssemblyAsmTypeCheck::funcDecl(const wasm::WasmSignature &Sig) {
|
||||
LocalTypes.assign(Sig.Params.begin(), Sig.Params.end());
|
||||
ReturnTypes.assign(Sig.Returns.begin(), Sig.Returns.end());
|
||||
}
|
||||
|
||||
void WebAssemblyAsmTypeCheck::localDecl(const SmallVector<wasm::ValType, 4> &Locals) {
|
||||
LocalTypes.insert(LocalTypes.end(), Locals.begin(), Locals.end());
|
||||
}
|
||||
|
||||
void WebAssemblyAsmTypeCheck::dumpTypeStack(Twine Msg) {
|
||||
LLVM_DEBUG({
|
||||
std::string s;
|
||||
for (auto VT : Stack) {
|
||||
s += WebAssembly::typeToString(VT);
|
||||
s += " ";
|
||||
}
|
||||
dbgs() << Msg << s << '\n';
|
||||
});
|
||||
}
|
||||
|
||||
bool WebAssemblyAsmTypeCheck::typeError(SMLoc ErrorLoc, const Twine &Msg) {
|
||||
// Once you get one type error in a function, it will likely trigger more
|
||||
// which are mostly not helpful.
|
||||
if (TypeErrorThisFunction)
|
||||
return true;
|
||||
TypeErrorThisFunction = true;
|
||||
dumpTypeStack("current stack: ");
|
||||
return Parser.Error(ErrorLoc, Msg);
|
||||
}
|
||||
|
||||
bool WebAssemblyAsmTypeCheck::popType(SMLoc ErrorLoc,
|
||||
Optional<wasm::ValType> EVT) {
|
||||
if (Stack.empty()) {
|
||||
return typeError(ErrorLoc,
|
||||
EVT.hasValue()
|
||||
? StringRef("empty stack while popping ") +
|
||||
WebAssembly::typeToString(EVT.getValue())
|
||||
: StringRef(
|
||||
"empty stack while popping value"));
|
||||
}
|
||||
auto PVT = Stack.back();
|
||||
Stack.pop_back();
|
||||
if (EVT.hasValue() && EVT.getValue() != PVT) {
|
||||
return typeError(
|
||||
ErrorLoc, StringRef("popped ") + WebAssembly::typeToString(PVT) +
|
||||
", expected " +
|
||||
WebAssembly::typeToString(EVT.getValue()));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool WebAssemblyAsmTypeCheck::getLocal(SMLoc ErrorLoc, const MCInst &Inst,
|
||||
wasm::ValType &Type) {
|
||||
auto Local = Inst.getOperand(0).getImm();
|
||||
if (static_cast<size_t>(Local) > LocalTypes.size())
|
||||
return typeError(ErrorLoc, StringRef("no local type specified for index ") +
|
||||
std::to_string(Local));
|
||||
Type = LocalTypes[Local];
|
||||
return false;
|
||||
}
|
||||
|
||||
bool WebAssemblyAsmTypeCheck::checkEnd(SMLoc ErrorLoc) {
|
||||
if (LastSig.Returns.size() > Stack.size())
|
||||
return typeError(ErrorLoc, "end: insufficient values on the type stack");
|
||||
for (size_t i = 0; i < LastSig.Returns.size(); i++) {
|
||||
auto EVT = LastSig.Returns[i];
|
||||
auto PVT = Stack[Stack.size() - LastSig.Returns.size() + i];
|
||||
if (PVT != EVT)
|
||||
return typeError(
|
||||
ErrorLoc, StringRef("end got ") + WebAssembly::typeToString(PVT) +
|
||||
", expected " + WebAssembly::typeToString(EVT));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool WebAssemblyAsmTypeCheck::checkSig(SMLoc ErrorLoc,
|
||||
const wasm::WasmSignature& Sig) {
|
||||
for (auto VT : llvm::reverse(Sig.Params))
|
||||
if (popType(ErrorLoc, VT)) return true;
|
||||
Stack.insert(Stack.end(), Sig.Returns.begin(), Sig.Returns.end());
|
||||
return false;
|
||||
}
|
||||
|
||||
bool WebAssemblyAsmTypeCheck::getSymRef(SMLoc ErrorLoc, const MCInst &Inst,
|
||||
const MCSymbolRefExpr *&SymRef) {
|
||||
auto Op = Inst.getOperand(0);
|
||||
if (!Op.isExpr())
|
||||
return typeError(ErrorLoc, StringRef("expected expression operand"));
|
||||
SymRef = dyn_cast<MCSymbolRefExpr>(Op.getExpr());
|
||||
if (!SymRef)
|
||||
return typeError(ErrorLoc, StringRef("expected symbol operand"));
|
||||
return false;
|
||||
}
|
||||
|
||||
bool WebAssemblyAsmTypeCheck::getGlobal(SMLoc ErrorLoc, const MCInst &Inst,
|
||||
wasm::ValType &Type) {
|
||||
const MCSymbolRefExpr *SymRef;
|
||||
if (getSymRef(ErrorLoc, Inst, SymRef))
|
||||
return true;
|
||||
auto WasmSym = cast<MCSymbolWasm>(&SymRef->getSymbol());
|
||||
switch (WasmSym->getType().getValueOr(wasm::WASM_SYMBOL_TYPE_DATA)) {
|
||||
case wasm::WASM_SYMBOL_TYPE_GLOBAL:
|
||||
Type = static_cast<wasm::ValType>(WasmSym->getGlobalType().Type);
|
||||
break;
|
||||
case wasm::WASM_SYMBOL_TYPE_FUNCTION:
|
||||
case wasm::WASM_SYMBOL_TYPE_DATA:
|
||||
if (SymRef->getKind() == MCSymbolRefExpr::VK_GOT) {
|
||||
Type = is64 ? wasm::ValType::I64 : wasm::ValType::I32;
|
||||
break;
|
||||
}
|
||||
// FALL-THROUGH:
|
||||
default:
|
||||
return typeError(ErrorLoc, StringRef("symbol ") + WasmSym->getName() +
|
||||
" missing .globaltype");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void WebAssemblyAsmTypeCheck::endOfFunction(SMLoc ErrorLoc) {
|
||||
// Check the return types.
|
||||
for (auto RVT : llvm::reverse(ReturnTypes)) {
|
||||
popType(ErrorLoc, RVT);
|
||||
}
|
||||
if (!Stack.empty()) {
|
||||
typeError(ErrorLoc,
|
||||
std::to_string(Stack.size()) + " superfluous return values");
|
||||
}
|
||||
// Reset the type checker state.
|
||||
Clear();
|
||||
}
|
||||
|
||||
bool WebAssemblyAsmTypeCheck::typeCheck(SMLoc ErrorLoc, const MCInst &Inst) {
|
||||
auto Opc = Inst.getOpcode();
|
||||
auto Name = GetMnemonic(Opc);
|
||||
dumpTypeStack("typechecking " + Name + ": ");
|
||||
wasm::ValType Type;
|
||||
if (Name == "local.get") {
|
||||
if (getLocal(ErrorLoc, Inst, Type))
|
||||
return true;
|
||||
Stack.push_back(Type);
|
||||
} else if (Name == "local.set") {
|
||||
if (getLocal(ErrorLoc, Inst, Type))
|
||||
return true;
|
||||
if (popType(ErrorLoc, Type))
|
||||
return true;
|
||||
} else if (Name == "local.tee") {
|
||||
if (getLocal(ErrorLoc, Inst, Type))
|
||||
return true;
|
||||
if (popType(ErrorLoc, Type))
|
||||
return true;
|
||||
Stack.push_back(Type);
|
||||
} else if (Name == "global.get") {
|
||||
if (getGlobal(ErrorLoc, Inst, Type))
|
||||
return true;
|
||||
Stack.push_back(Type);
|
||||
} else if (Name == "global.set") {
|
||||
if (getGlobal(ErrorLoc, Inst, Type))
|
||||
return true;
|
||||
if (popType(ErrorLoc, Type))
|
||||
return true;
|
||||
} else if (Name == "drop") {
|
||||
if (popType(ErrorLoc, {}))
|
||||
return true;
|
||||
} else if (Name == "end_block" || Name == "end_loop" || Name == "end_if" ||
|
||||
Name == "else") {
|
||||
if (checkEnd(ErrorLoc))
|
||||
return true;
|
||||
} else if (Name == "call_indirect" || Name == "return_call_indirect") {
|
||||
// Function value.
|
||||
if (popType(ErrorLoc, wasm::ValType::I32)) return true;
|
||||
if (checkSig(ErrorLoc, LastSig)) return true;
|
||||
} else if (Name == "call" || Name == "return_call") {
|
||||
const MCSymbolRefExpr *SymRef;
|
||||
if (getSymRef(ErrorLoc, Inst, SymRef))
|
||||
return true;
|
||||
auto WasmSym = cast<MCSymbolWasm>(&SymRef->getSymbol());
|
||||
auto Sig = WasmSym->getSignature();
|
||||
if (!Sig || WasmSym->getType() != wasm::WASM_SYMBOL_TYPE_FUNCTION)
|
||||
return typeError(ErrorLoc, StringRef("symbol ") + WasmSym->getName() +
|
||||
" missing .functype");
|
||||
if (checkSig(ErrorLoc, *Sig)) return true;
|
||||
} else if (Name == "ref.null") {
|
||||
auto VT = static_cast<wasm::ValType>(Inst.getOperand(0).getImm());
|
||||
Stack.push_back(VT);
|
||||
} else {
|
||||
// The current instruction is a stack instruction which doesn't have
|
||||
// explicit operands that indicate push/pop types, so we get those from
|
||||
// the register version of the same instruction.
|
||||
auto RegOpc = WebAssembly::getRegisterOpcode(Opc);
|
||||
assert(RegOpc != -1 && "Failed to get register version of MC instruction");
|
||||
const auto &II = MII.get(RegOpc);
|
||||
// First pop all the uses off the stack and check them.
|
||||
for (unsigned I = II.getNumOperands(); I > II.getNumDefs(); I--) {
|
||||
const auto &Op = II.OpInfo[I - 1];
|
||||
if (Op.OperandType == MCOI::OPERAND_REGISTER) {
|
||||
auto VT = WebAssembly::regClassToValType(Op.RegClass);
|
||||
if (popType(ErrorLoc, VT))
|
||||
return true;
|
||||
}
|
||||
}
|
||||
// Now push all the defs onto the stack.
|
||||
for (unsigned I = 0; I < II.getNumDefs(); I++) {
|
||||
const auto &Op = II.OpInfo[I];
|
||||
assert(Op.OperandType == MCOI::OPERAND_REGISTER && "Register expected");
|
||||
auto VT = WebAssembly::regClassToValType(Op.RegClass);
|
||||
Stack.push_back(VT);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
} // end namespace llvm
|
66
lib/Target/WebAssembly/AsmParser/WebAssemblyAsmTypeCheck.h
Normal file
66
lib/Target/WebAssembly/AsmParser/WebAssemblyAsmTypeCheck.h
Normal file
@ -0,0 +1,66 @@
|
||||
//==- WebAssemblyAsmTypeCheck.h - Assembler for WebAssembly -*- C++ -*-==//
|
||||
//
|
||||
// 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
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
///
|
||||
/// \file
|
||||
/// This file is part of the WebAssembly Assembler.
|
||||
///
|
||||
/// It contains code to translate a parsed .s file into MCInsts.
|
||||
///
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef LLVM_LIB_TARGET_WEBASSEMBLY_ASMPARSER_TYPECHECK_H
|
||||
#define LLVM_LIB_TARGET_WEBASSEMBLY_ASMPARSER_TYPECHECK_H
|
||||
|
||||
#include "llvm/MC/MCParser/MCAsmParser.h"
|
||||
#include "llvm/MC/MCInstrInfo.h"
|
||||
#include "llvm/BinaryFormat/Wasm.h"
|
||||
#include "llvm/MC/MCSymbol.h"
|
||||
|
||||
namespace llvm {
|
||||
|
||||
class WebAssemblyAsmTypeCheck final {
|
||||
MCAsmParser &Parser;
|
||||
const MCInstrInfo &MII;
|
||||
|
||||
SmallVector<wasm::ValType, 8> Stack;
|
||||
SmallVector<wasm::ValType, 16> LocalTypes;
|
||||
SmallVector<wasm::ValType, 4> ReturnTypes;
|
||||
wasm::WasmSignature LastSig;
|
||||
bool TypeErrorThisFunction = false;
|
||||
bool is64;
|
||||
|
||||
void Clear() {
|
||||
Stack.clear();
|
||||
LocalTypes.clear();
|
||||
ReturnTypes.clear();
|
||||
TypeErrorThisFunction = false;
|
||||
}
|
||||
|
||||
void dumpTypeStack(Twine Msg);
|
||||
bool typeError(SMLoc ErrorLoc, const Twine &Msg);
|
||||
bool popType(SMLoc ErrorLoc, Optional<wasm::ValType> EVT);
|
||||
bool getLocal(SMLoc ErrorLoc, const MCInst &Inst, wasm::ValType &Type);
|
||||
bool checkEnd(SMLoc ErrorLoc);
|
||||
bool checkSig(SMLoc ErrorLoc, const wasm::WasmSignature &Sig);
|
||||
bool getSymRef(SMLoc ErrorLoc, const MCInst &Inst,
|
||||
const MCSymbolRefExpr *&SymRef);
|
||||
bool getGlobal(SMLoc ErrorLoc, const MCInst &Inst, wasm::ValType &Type);
|
||||
|
||||
public:
|
||||
WebAssemblyAsmTypeCheck(MCAsmParser &Parser, const MCInstrInfo &MII, bool is64);
|
||||
|
||||
void funcDecl(const wasm::WasmSignature &Sig);
|
||||
void localDecl(const SmallVector<wasm::ValType, 4> &Locals);
|
||||
void setLastSig(const wasm::WasmSignature &Sig) { LastSig = Sig; }
|
||||
void endOfFunction(SMLoc ErrorLoc);
|
||||
bool typeCheck(SMLoc ErrorLoc, const MCInst &Inst);
|
||||
};
|
||||
|
||||
} // end namespace llvm
|
||||
|
||||
#endif LLVM_LIB_TARGET_WEBASSEMBLY_ASMPARSER_TYPECHECK_H
|
@ -38,6 +38,7 @@ add_llvm_target(WebAssemblyCodeGen
|
||||
WebAssemblyLowerGlobalDtors.cpp
|
||||
WebAssemblyMachineFunctionInfo.cpp
|
||||
WebAssemblyMCInstLower.cpp
|
||||
WebAssemblyMCLowerPrePass.cpp
|
||||
WebAssemblyNullifyDebugValueLists.cpp
|
||||
WebAssemblyOptimizeLiveIntervals.cpp
|
||||
WebAssemblyOptimizeReturned.cpp
|
||||
|
@ -24,6 +24,7 @@ Target &getTheWebAssemblyTarget64();
|
||||
namespace WebAssembly {
|
||||
|
||||
int getStackOpcode(unsigned short Opcode);
|
||||
int getRegisterOpcode(unsigned short Opcode);
|
||||
int getWasm64Opcode(unsigned short Opcode);
|
||||
|
||||
} // namespace WebAssembly
|
||||
|
@ -14,6 +14,10 @@
|
||||
#include "WebAssemblyTypeUtilities.h"
|
||||
#include "llvm/ADT/StringSwitch.h"
|
||||
|
||||
// Get register classes enum.
|
||||
#define GET_REGINFO_ENUM
|
||||
#include "WebAssemblyGenRegisterInfo.inc"
|
||||
|
||||
using namespace llvm;
|
||||
|
||||
Optional<wasm::ValType> WebAssembly::parseType(StringRef Type) {
|
||||
@ -149,3 +153,24 @@ wasm::ValType WebAssembly::toValType(MVT Type) {
|
||||
llvm_unreachable("unexpected type");
|
||||
}
|
||||
}
|
||||
|
||||
wasm::ValType WebAssembly::regClassToValType(unsigned RC) {
|
||||
switch (RC) {
|
||||
case WebAssembly::I32RegClassID:
|
||||
return wasm::ValType::I32;
|
||||
case WebAssembly::I64RegClassID:
|
||||
return wasm::ValType::I64;
|
||||
case WebAssembly::F32RegClassID:
|
||||
return wasm::ValType::F32;
|
||||
case WebAssembly::F64RegClassID:
|
||||
return wasm::ValType::F64;
|
||||
case WebAssembly::V128RegClassID:
|
||||
return wasm::ValType::V128;
|
||||
case WebAssembly::FUNCREFRegClassID:
|
||||
return wasm::ValType::FUNCREF;
|
||||
case WebAssembly::EXTERNREFRegClassID:
|
||||
return wasm::ValType::EXTERNREF;
|
||||
default:
|
||||
llvm_unreachable("unexpected type");
|
||||
}
|
||||
}
|
||||
|
@ -73,6 +73,9 @@ std::string signatureToString(const wasm::WasmSignature *Sig);
|
||||
// Convert a MVT into its corresponding wasm ValType.
|
||||
wasm::ValType toValType(MVT Type);
|
||||
|
||||
// Convert a register class to a wasm ValType.
|
||||
wasm::ValType regClassToValType(unsigned RC);
|
||||
|
||||
} // end namespace WebAssembly
|
||||
} // end namespace llvm
|
||||
|
||||
|
@ -55,6 +55,7 @@ FunctionPass *createWebAssemblyLowerBrUnless();
|
||||
FunctionPass *createWebAssemblyRegNumbering();
|
||||
FunctionPass *createWebAssemblyDebugFixup();
|
||||
FunctionPass *createWebAssemblyPeephole();
|
||||
FunctionPass *createWebAssemblyMCLowerPrePass();
|
||||
|
||||
// PassRegistry initialization declarations.
|
||||
void initializeWebAssemblyAddMissingPrototypesPass(PassRegistry &);
|
||||
@ -82,6 +83,7 @@ void initializeWebAssemblyLowerBrUnlessPass(PassRegistry &);
|
||||
void initializeWebAssemblyRegNumberingPass(PassRegistry &);
|
||||
void initializeWebAssemblyDebugFixupPass(PassRegistry &);
|
||||
void initializeWebAssemblyPeepholePass(PassRegistry &);
|
||||
void initializeWebAssemblyMCLowerPrePassPass(PassRegistry &);
|
||||
|
||||
namespace WebAssembly {
|
||||
enum TargetIndex {
|
||||
|
@ -23,6 +23,7 @@
|
||||
#include "WebAssemblyMCInstLower.h"
|
||||
#include "WebAssemblyMachineFunctionInfo.h"
|
||||
#include "WebAssemblyRegisterInfo.h"
|
||||
#include "WebAssemblyRuntimeLibcallSignatures.h"
|
||||
#include "WebAssemblyTargetMachine.h"
|
||||
#include "llvm/ADT/SmallSet.h"
|
||||
#include "llvm/ADT/StringExtras.h"
|
||||
@ -207,7 +208,76 @@ void WebAssemblyAsmPrinter::emitGlobalVariable(const GlobalVariable *GV) {
|
||||
}
|
||||
}
|
||||
|
||||
void WebAssemblyAsmPrinter::emitEndOfAsmFile(Module &M) {
|
||||
MCSymbol *WebAssemblyAsmPrinter::getOrCreateWasmSymbol(StringRef Name) {
|
||||
auto *WasmSym = cast<MCSymbolWasm>(GetExternalSymbolSymbol(Name));
|
||||
|
||||
// May be called multiple times, so early out.
|
||||
if (WasmSym->getType().hasValue())
|
||||
return WasmSym;
|
||||
|
||||
const WebAssemblySubtarget &Subtarget = getSubtarget();
|
||||
|
||||
// Except for certain known symbols, all symbols used by CodeGen are
|
||||
// functions. It's OK to hardcode knowledge of specific symbols here; this
|
||||
// method is precisely there for fetching the signatures of known
|
||||
// Clang-provided symbols.
|
||||
if (Name == "__stack_pointer" || Name == "__tls_base" ||
|
||||
Name == "__memory_base" || Name == "__table_base" ||
|
||||
Name == "__tls_size" || Name == "__tls_align") {
|
||||
bool Mutable =
|
||||
Name == "__stack_pointer" || Name == "__tls_base";
|
||||
WasmSym->setType(wasm::WASM_SYMBOL_TYPE_GLOBAL);
|
||||
WasmSym->setGlobalType(wasm::WasmGlobalType{
|
||||
uint8_t(Subtarget.hasAddr64() ? wasm::WASM_TYPE_I64
|
||||
: wasm::WASM_TYPE_I32),
|
||||
Mutable});
|
||||
return WasmSym;
|
||||
}
|
||||
|
||||
SmallVector<wasm::ValType, 4> Returns;
|
||||
SmallVector<wasm::ValType, 4> Params;
|
||||
if (Name == "__cpp_exception") {
|
||||
WasmSym->setType(wasm::WASM_SYMBOL_TYPE_TAG);
|
||||
// We can't confirm its signature index for now because there can be
|
||||
// imported exceptions. Set it to be 0 for now.
|
||||
WasmSym->setTagType(
|
||||
{wasm::WASM_TAG_ATTRIBUTE_EXCEPTION, /* SigIndex */ 0});
|
||||
// We may have multiple C++ compilation units to be linked together, each of
|
||||
// which defines the exception symbol. To resolve them, we declare them as
|
||||
// weak.
|
||||
WasmSym->setWeak(true);
|
||||
WasmSym->setExternal(true);
|
||||
|
||||
// All C++ exceptions are assumed to have a single i32 (for wasm32) or i64
|
||||
// (for wasm64) param type and void return type. The reaon is, all C++
|
||||
// exception values are pointers, and to share the type section with
|
||||
// functions, exceptions are assumed to have void return type.
|
||||
Params.push_back(Subtarget.hasAddr64() ? wasm::ValType::I64
|
||||
: wasm::ValType::I32);
|
||||
} else { // Function symbols
|
||||
WasmSym->setType(wasm::WASM_SYMBOL_TYPE_FUNCTION);
|
||||
getLibcallSignature(Subtarget, Name, Returns, Params);
|
||||
}
|
||||
auto Signature = std::make_unique<wasm::WasmSignature>(std::move(Returns),
|
||||
std::move(Params));
|
||||
WasmSym->setSignature(Signature.get());
|
||||
addSignature(std::move(Signature));
|
||||
|
||||
return WasmSym;
|
||||
}
|
||||
|
||||
void WebAssemblyAsmPrinter::emitExternalDecls(const Module &M) {
|
||||
if (signaturesEmitted)
|
||||
return;
|
||||
signaturesEmitted = true;
|
||||
|
||||
// Normally symbols for globals get discovered as the MI gets lowered,
|
||||
// but we need to know about them ahead of time.
|
||||
MachineModuleInfoWasm &MMIW = MMI->getObjFileInfo<MachineModuleInfoWasm>();
|
||||
for (const auto &Name : MMIW.MachineSymbolsUsed) {
|
||||
getOrCreateWasmSymbol(Name.getKey());
|
||||
}
|
||||
|
||||
for (auto &It : OutContext.getSymbols()) {
|
||||
// Emit .globaltype, .tagtype, or .tabletype declarations.
|
||||
auto Sym = cast<MCSymbolWasm>(It.getValue());
|
||||
@ -224,14 +294,10 @@ void WebAssemblyAsmPrinter::emitEndOfAsmFile(Module &M) {
|
||||
}
|
||||
|
||||
DenseSet<MCSymbol *> InvokeSymbols;
|
||||
bool HasAddressTakenFunction = false;
|
||||
for (const auto &F : M) {
|
||||
if (F.isIntrinsic())
|
||||
continue;
|
||||
|
||||
if (F.hasAddressTaken())
|
||||
HasAddressTakenFunction = true;
|
||||
|
||||
// Emit function type info for all undefined functions
|
||||
if (F.isDeclarationForLinker()) {
|
||||
SmallVector<MVT, 4> Results;
|
||||
@ -289,6 +355,10 @@ void WebAssemblyAsmPrinter::emitEndOfAsmFile(Module &M) {
|
||||
getTargetStreamer()->emitExportName(Sym, Name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void WebAssemblyAsmPrinter::emitEndOfAsmFile(Module &M) {
|
||||
emitExternalDecls(M);
|
||||
|
||||
// When a function's address is taken, a TABLE_INDEX relocation is emitted
|
||||
// against the function symbol at the use site. However the relocation
|
||||
@ -296,10 +366,13 @@ void WebAssemblyAsmPrinter::emitEndOfAsmFile(Module &M) {
|
||||
// define a new kind of reloc against both the function and the table, so
|
||||
// that the linker can see that the function symbol keeps the table alive,
|
||||
// but for now manually mark the table as live.
|
||||
if (HasAddressTakenFunction) {
|
||||
MCSymbolWasm *FunctionTable =
|
||||
WebAssembly::getOrCreateFunctionTableSymbol(OutContext, Subtarget);
|
||||
OutStreamer->emitSymbolAttribute(FunctionTable, MCSA_NoDeadStrip);
|
||||
for (const auto &F : M) {
|
||||
if (!F.isIntrinsic() && F.hasAddressTaken()) {
|
||||
MCSymbolWasm *FunctionTable =
|
||||
WebAssembly::getOrCreateFunctionTableSymbol(OutContext, Subtarget);
|
||||
OutStreamer->emitSymbolAttribute(FunctionTable, MCSA_NoDeadStrip);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for (const auto &G : M.globals()) {
|
||||
@ -452,6 +525,17 @@ void WebAssemblyAsmPrinter::emitJumpTableInfo() {
|
||||
// Nothing to do; jump tables are incorporated into the instruction stream.
|
||||
}
|
||||
|
||||
void WebAssemblyAsmPrinter::emitLinkage(const GlobalValue *GV, MCSymbol *Sym)
|
||||
const {
|
||||
AsmPrinter::emitLinkage(GV, Sym);
|
||||
// This gets called before the function label and type are emitted.
|
||||
// We use it to emit signatures of external functions.
|
||||
// FIXME casts!
|
||||
const_cast<WebAssemblyAsmPrinter *>(this)
|
||||
->emitExternalDecls(*MMI->getModule());
|
||||
}
|
||||
|
||||
|
||||
void WebAssemblyAsmPrinter::emitFunctionBodyStart() {
|
||||
const Function &F = MF->getFunction();
|
||||
SmallVector<MVT, 1> ResultVTs;
|
||||
|
@ -25,6 +25,7 @@ class LLVM_LIBRARY_VISIBILITY WebAssemblyAsmPrinter final : public AsmPrinter {
|
||||
// TODO: Do the uniquing of Signatures here instead of ObjectFileWriter?
|
||||
std::vector<std::unique_ptr<wasm::WasmSignature>> Signatures;
|
||||
std::vector<std::unique_ptr<std::string>> Names;
|
||||
bool signaturesEmitted = false;
|
||||
|
||||
StringRef storeName(StringRef Name) {
|
||||
std::unique_ptr<std::string> N = std::make_unique<std::string>(Name);
|
||||
@ -68,6 +69,7 @@ public:
|
||||
void emitGlobalVariable(const GlobalVariable *GV) override;
|
||||
void emitJumpTableInfo() override;
|
||||
void emitConstantPool() override;
|
||||
void emitLinkage(const GlobalValue *, MCSymbol *) const override;
|
||||
void emitFunctionBodyStart() override;
|
||||
void emitInstruction(const MachineInstr *MI) override;
|
||||
bool PrintAsmOperand(const MachineInstr *MI, unsigned OpNo,
|
||||
@ -81,6 +83,8 @@ public:
|
||||
MCSymbolWasm *getMCSymbolForFunction(const Function *F, bool EnableEmEH,
|
||||
wasm::WasmSignature *Sig,
|
||||
bool &InvokeDetected);
|
||||
MCSymbol *getOrCreateWasmSymbol(StringRef Name);
|
||||
void emitExternalDecls(const Module &M);
|
||||
};
|
||||
|
||||
} // end namespace llvm
|
||||
|
@ -15,7 +15,7 @@
|
||||
// We instantiate 2 of these for every actual instruction (register based
|
||||
// and stack based), see below.
|
||||
class WebAssemblyInst<bits<32> inst, string asmstr, string stack, string is64>
|
||||
: StackRel, Wasm64Rel, Instruction {
|
||||
: StackRel, RegisterRel, Wasm64Rel, Instruction {
|
||||
bits<32> Inst = inst; // Instruction encoding.
|
||||
string StackBased = stack;
|
||||
string BaseName = NAME;
|
||||
|
@ -234,6 +234,19 @@ def getStackOpcode : InstrMapping {
|
||||
let ValueCols = [["true"]];
|
||||
}
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// WebAssembly Stack to Register instruction mapping
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
class RegisterRel;
|
||||
def getRegisterOpcode : InstrMapping {
|
||||
let FilterClass = "RegisterRel";
|
||||
let RowFields = ["BaseName"];
|
||||
let ColFields = ["StackBased"];
|
||||
let KeyCol = ["true"];
|
||||
let ValueCols = [["false"]];
|
||||
}
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// WebAssembly 32 to 64-bit instruction mapping
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
@ -13,10 +13,10 @@
|
||||
|
||||
|
||||
multiclass TABLE<WebAssemblyRegClass rt> {
|
||||
defm TABLE_GET_#rt : I<(outs rt:$res), (ins table32_op:$table),
|
||||
defm TABLE_GET_#rt : I<(outs rt:$res), (ins table32_op:$table, I32:$i),
|
||||
(outs), (ins table32_op:$table),
|
||||
[],
|
||||
"table.get\t$res, $table",
|
||||
"table.get\t$res, $table, $i",
|
||||
"table.get\t$table",
|
||||
0x25>;
|
||||
|
||||
|
@ -18,7 +18,6 @@
|
||||
#include "Utils/WebAssemblyUtilities.h"
|
||||
#include "WebAssemblyAsmPrinter.h"
|
||||
#include "WebAssemblyMachineFunctionInfo.h"
|
||||
#include "WebAssemblyRuntimeLibcallSignatures.h"
|
||||
#include "llvm/CodeGen/AsmPrinter.h"
|
||||
#include "llvm/CodeGen/MachineFunction.h"
|
||||
#include "llvm/IR/Constants.h"
|
||||
@ -92,56 +91,7 @@ WebAssemblyMCInstLower::GetGlobalAddressSymbol(const MachineOperand &MO) const {
|
||||
|
||||
MCSymbol *WebAssemblyMCInstLower::GetExternalSymbolSymbol(
|
||||
const MachineOperand &MO) const {
|
||||
const char *Name = MO.getSymbolName();
|
||||
auto *WasmSym = cast<MCSymbolWasm>(Printer.GetExternalSymbolSymbol(Name));
|
||||
const WebAssemblySubtarget &Subtarget = Printer.getSubtarget();
|
||||
|
||||
// Except for certain known symbols, all symbols used by CodeGen are
|
||||
// functions. It's OK to hardcode knowledge of specific symbols here; this
|
||||
// method is precisely there for fetching the signatures of known
|
||||
// Clang-provided symbols.
|
||||
if (strcmp(Name, "__stack_pointer") == 0 || strcmp(Name, "__tls_base") == 0 ||
|
||||
strcmp(Name, "__memory_base") == 0 || strcmp(Name, "__table_base") == 0 ||
|
||||
strcmp(Name, "__tls_size") == 0 || strcmp(Name, "__tls_align") == 0) {
|
||||
bool Mutable =
|
||||
strcmp(Name, "__stack_pointer") == 0 || strcmp(Name, "__tls_base") == 0;
|
||||
WasmSym->setType(wasm::WASM_SYMBOL_TYPE_GLOBAL);
|
||||
WasmSym->setGlobalType(wasm::WasmGlobalType{
|
||||
uint8_t(Subtarget.hasAddr64() ? wasm::WASM_TYPE_I64
|
||||
: wasm::WASM_TYPE_I32),
|
||||
Mutable});
|
||||
return WasmSym;
|
||||
}
|
||||
|
||||
SmallVector<wasm::ValType, 4> Returns;
|
||||
SmallVector<wasm::ValType, 4> Params;
|
||||
if (strcmp(Name, "__cpp_exception") == 0) {
|
||||
WasmSym->setType(wasm::WASM_SYMBOL_TYPE_TAG);
|
||||
// We can't confirm its signature index for now because there can be
|
||||
// imported exceptions. Set it to be 0 for now.
|
||||
WasmSym->setTagType({wasm::WASM_TAG_ATTRIBUTE_EXCEPTION, /* SigIndex */ 0});
|
||||
// We may have multiple C++ compilation units to be linked together, each of
|
||||
// which defines the exception symbol. To resolve them, we declare them as
|
||||
// weak.
|
||||
WasmSym->setWeak(true);
|
||||
WasmSym->setExternal(true);
|
||||
|
||||
// All C++ exceptions are assumed to have a single i32 (for wasm32) or i64
|
||||
// (for wasm64) param type and void return type. The reaon is, all C++
|
||||
// exception values are pointers, and to share the type section with
|
||||
// functions, exceptions are assumed to have void return type.
|
||||
Params.push_back(Subtarget.hasAddr64() ? wasm::ValType::I64
|
||||
: wasm::ValType::I32);
|
||||
} else { // Function symbols
|
||||
WasmSym->setType(wasm::WASM_SYMBOL_TYPE_FUNCTION);
|
||||
getLibcallSignature(Subtarget, Name, Returns, Params);
|
||||
}
|
||||
auto Signature =
|
||||
std::make_unique<wasm::WasmSignature>(std::move(Returns), std::move(Params));
|
||||
WasmSym->setSignature(Signature.get());
|
||||
Printer.addSignature(std::move(Signature));
|
||||
|
||||
return WasmSym;
|
||||
return Printer.getOrCreateWasmSymbol(MO.getSymbolName());
|
||||
}
|
||||
|
||||
MCOperand WebAssemblyMCInstLower::lowerSymbolOperand(const MachineOperand &MO,
|
||||
|
86
lib/Target/WebAssembly/WebAssemblyMCLowerPrePass.cpp
Normal file
86
lib/Target/WebAssembly/WebAssemblyMCLowerPrePass.cpp
Normal file
@ -0,0 +1,86 @@
|
||||
//===-- WebAssemblyMCLowerPrePass.cpp - Prepare for MC lower --------------===//
|
||||
//
|
||||
// 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
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
///
|
||||
/// \file
|
||||
/// Some information in MC lowering / asm printing gets generated as
|
||||
/// instructions get emitted, but may be necessary at the start, such as for
|
||||
/// .globaltype declarations. This pass collects this information.
|
||||
///
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "MCTargetDesc/WebAssemblyMCTargetDesc.h"
|
||||
#include "Utils/WebAssemblyUtilities.h"
|
||||
#include "WebAssembly.h"
|
||||
#include "WebAssemblyMachineFunctionInfo.h"
|
||||
#include "WebAssemblySubtarget.h"
|
||||
#include "llvm/ADT/SCCIterator.h"
|
||||
#include "llvm/CodeGen/MachineFrameInfo.h"
|
||||
#include "llvm/CodeGen/MachineFunction.h"
|
||||
#include "llvm/CodeGen/MachineInstrBuilder.h"
|
||||
#include "llvm/CodeGen/MachineLoopInfo.h"
|
||||
#include "llvm/CodeGen/MachineModuleInfoImpls.h"
|
||||
#include "llvm/CodeGen/MachineRegisterInfo.h"
|
||||
#include "llvm/CodeGen/Passes.h"
|
||||
#include "llvm/Support/Debug.h"
|
||||
#include "llvm/Support/raw_ostream.h"
|
||||
using namespace llvm;
|
||||
|
||||
#define DEBUG_TYPE "wasm-mclower-prepass"
|
||||
|
||||
namespace {
|
||||
class WebAssemblyMCLowerPrePass final : public MachineFunctionPass {
|
||||
StringRef getPassName() const override {
|
||||
return "WebAssembly MC Lower Pre Pass";
|
||||
}
|
||||
|
||||
void getAnalysisUsage(AnalysisUsage &AU) const override {
|
||||
AU.setPreservesCFG();
|
||||
MachineFunctionPass::getAnalysisUsage(AU);
|
||||
}
|
||||
|
||||
bool runOnMachineFunction(MachineFunction &MF) override;
|
||||
|
||||
public:
|
||||
static char ID; // Pass identification, replacement for typeid
|
||||
WebAssemblyMCLowerPrePass() : MachineFunctionPass(ID) {}
|
||||
};
|
||||
} // end anonymous namespace
|
||||
|
||||
char WebAssemblyMCLowerPrePass::ID = 0;
|
||||
INITIALIZE_PASS(
|
||||
WebAssemblyMCLowerPrePass, DEBUG_TYPE,
|
||||
"Collects information ahead of time for MC lowering",
|
||||
false, false)
|
||||
|
||||
FunctionPass *llvm::createWebAssemblyMCLowerPrePass() {
|
||||
return new WebAssemblyMCLowerPrePass();
|
||||
}
|
||||
|
||||
bool WebAssemblyMCLowerPrePass::runOnMachineFunction(MachineFunction &MF) {
|
||||
LLVM_DEBUG(dbgs() << "********** MC Lower Pre Pass **********\n"
|
||||
"********** Function: "
|
||||
<< MF.getName() << '\n');
|
||||
|
||||
MachineModuleInfo &MMI = MF.getMMI();
|
||||
MachineModuleInfoWasm &MMIW = MMI.getObjFileInfo<MachineModuleInfoWasm>();
|
||||
|
||||
for (MachineBasicBlock &MBB : MF) {
|
||||
for (auto &MI : MBB) {
|
||||
// FIXME: what should all be filtered out beyond these?
|
||||
if (MI.isDebugInstr() || MI.isInlineAsm())
|
||||
continue;
|
||||
for (MachineOperand &MO : MI.uses()) {
|
||||
if (MO.isSymbol()) {
|
||||
MMIW.MachineSymbolsUsed.insert(MO.getSymbolName());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
@ -889,14 +889,15 @@ static ManagedStatic<StaticLibcallNameMap> LibcallNameMap;
|
||||
// TODO: If the RTLIB::Libcall-taking flavor of GetSignature remains unsed
|
||||
// other than here, just roll its logic into this version.
|
||||
void llvm::getLibcallSignature(const WebAssemblySubtarget &Subtarget,
|
||||
const char *Name,
|
||||
StringRef Name,
|
||||
SmallVectorImpl<wasm::ValType> &Rets,
|
||||
SmallVectorImpl<wasm::ValType> &Params) {
|
||||
auto &Map = LibcallNameMap->Map;
|
||||
auto Val = Map.find(Name);
|
||||
#ifndef NDEBUG
|
||||
if (Val == Map.end()) {
|
||||
auto message = std::string("unexpected runtime library name: ") + Name;
|
||||
auto message = std::string("unexpected runtime library name: ") +
|
||||
std::string(Name);
|
||||
llvm_unreachable(message.c_str());
|
||||
}
|
||||
#endif
|
||||
|
@ -28,7 +28,7 @@ extern void getLibcallSignature(const WebAssemblySubtarget &Subtarget,
|
||||
SmallVectorImpl<wasm::ValType> &Params);
|
||||
|
||||
extern void getLibcallSignature(const WebAssemblySubtarget &Subtarget,
|
||||
const char *Name,
|
||||
StringRef Name,
|
||||
SmallVectorImpl<wasm::ValType> &Rets,
|
||||
SmallVectorImpl<wasm::ValType> &Params);
|
||||
|
||||
|
@ -88,6 +88,7 @@ extern "C" LLVM_EXTERNAL_VISIBILITY void LLVMInitializeWebAssemblyTarget() {
|
||||
initializeWebAssemblyRegNumberingPass(PR);
|
||||
initializeWebAssemblyDebugFixupPass(PR);
|
||||
initializeWebAssemblyPeepholePass(PR);
|
||||
initializeWebAssemblyMCLowerPrePassPass(PR);
|
||||
}
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
@ -507,6 +508,9 @@ void WebAssemblyPassConfig::addPreEmitPass() {
|
||||
// Fix debug_values whose defs have been stackified.
|
||||
if (!WasmDisableExplicitLocals)
|
||||
addPass(createWebAssemblyDebugFixup());
|
||||
|
||||
// Collect information to prepare for MC lowering / asm printing.
|
||||
addPass(createWebAssemblyMCLowerPrePass());
|
||||
}
|
||||
|
||||
yaml::MachineFunctionInfo *
|
||||
|
@ -9,6 +9,8 @@ target triple = "wasm32-unknown-unknown"
|
||||
|
||||
@_ZTIi = external dso_local constant i8*
|
||||
|
||||
; CHECK: .tagtype __cpp_exception i32
|
||||
|
||||
; CHECK-LABEL: test_throw:
|
||||
; CHECK: throw __cpp_exception, $0
|
||||
; CHECK-NOT: unreachable
|
||||
@ -372,4 +374,3 @@ attributes #0 = { nounwind }
|
||||
attributes #1 = { noreturn }
|
||||
|
||||
; CHECK: __cpp_exception:
|
||||
; CHECK: .tagtype __cpp_exception i32
|
||||
|
@ -70,10 +70,12 @@ define i32 @return_extern_i32_global() {
|
||||
; CHECK: .globaltype f64_global, f64
|
||||
; CHECK-LABEL: f64_global:
|
||||
|
||||
; FIXME: are we still expecting these to be emitted?
|
||||
|
||||
; CHECK-NOT: .global i32_external_used
|
||||
; CHECK: .globaltype i32_external_used, i32
|
||||
; CHECK-NOT: .globaltype i32_external_used, i32
|
||||
; CHECK-NOT: i32_external_used:
|
||||
|
||||
; CHECK-NOT: .global i32_external_unused
|
||||
; CHECK: .globaltype i32_external_unused, i32
|
||||
; CHECK-NOT: .globaltype i32_external_unused, i32
|
||||
; CHECK-NOT: i32_external_unused:
|
||||
|
@ -13,7 +13,8 @@ define i128 @foo(i128) {
|
||||
ret i128 %r
|
||||
}
|
||||
|
||||
; CHECK: .functype bar (i32, i64, i64) -> ()
|
||||
|
||||
; CHECK-LABEL: foo:
|
||||
; CHECK-NEXT: .functype foo (i32, i64, i64) -> ()
|
||||
|
||||
; CHECK: .functype bar (i32, i64, i64) -> ()
|
||||
|
@ -6,6 +6,14 @@ source_filename = "test/dot_s/indirect-import.c"
|
||||
target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128"
|
||||
target triple = "wasm32"
|
||||
|
||||
; CHECK: .functype extern_fd (f64) -> (f32)
|
||||
; CHECK: .functype extern_vj (i64) -> ()
|
||||
; CHECK: .functype extern_v () -> ()
|
||||
; CHECK: .functype extern_ijidf (i64, i32, f64, f32) -> (i32)
|
||||
; CHECK: .functype extern_struct (i32) -> ()
|
||||
; CHECK: .functype extern_sret (i32) -> ()
|
||||
; CHECK: .functype extern_i128ret (i32, i64) -> ()
|
||||
|
||||
%struct.big = type { float, double, i32 }
|
||||
|
||||
; Function Attrs: nounwind
|
||||
@ -70,10 +78,3 @@ attributes #0 = { nounwind "disable-tail-calls"="false" "less-precise-fpmad"="fa
|
||||
attributes #1 = { "disable-tail-calls"="false" "less-precise-fpmad"="false" "frame-pointer"="none" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="generic" "unsafe-fp-math"="false" "use-soft-float"="false" }
|
||||
|
||||
|
||||
; CHECK: .functype extern_fd (f64) -> (f32)
|
||||
; CHECK: .functype extern_vj (i64) -> ()
|
||||
; CHECK: .functype extern_v () -> ()
|
||||
; CHECK: .functype extern_ijidf (i64, i32, f64, f32) -> (i32)
|
||||
; CHECK: .functype extern_struct (i32) -> ()
|
||||
; CHECK: .functype extern_sret (i32) -> ()
|
||||
; CHECK: .functype extern_i128ret (i32, i64) -> ()
|
||||
|
@ -11,6 +11,8 @@
|
||||
@external_global = external global i32
|
||||
@external_global_array = external global [10 x i32]
|
||||
|
||||
; PIC: .globaltype __memory_base, [[PTR]]
|
||||
|
||||
declare i32 @foo();
|
||||
|
||||
; For hidden symbols PIC code needs to offset all loads and stores
|
||||
@ -128,5 +130,3 @@ define void @store_external_global_offset(i32 %n) {
|
||||
store i32 %n, i32* %1
|
||||
ret void
|
||||
}
|
||||
|
||||
; PIC: .globaltype __memory_base, [[PTR]]
|
||||
|
@ -6,6 +6,20 @@
|
||||
target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128"
|
||||
target triple = "wasm32-unknown-unknown"
|
||||
|
||||
; EH: .functype invoke_vi (i32, i32) -> ()
|
||||
; EH: .import_module invoke_vi, env
|
||||
; EH: .import_name invoke_vi, invoke_vi
|
||||
; EH-NOT: .functype __invoke_void_i32
|
||||
; EH-NOT: .import_module __invoke_void_i32
|
||||
; EH-NOT: .import_name __invoke_void_i32
|
||||
|
||||
; SJLJ: .functype emscripten_longjmp (i32, i32) -> ()
|
||||
; SJLJ: .import_module emscripten_longjmp, env
|
||||
; SJLJ: .import_name emscripten_longjmp, emscripten_longjmp
|
||||
; SJLJ-NOT: .functype emscripten_longjmp_jmpbuf
|
||||
; SJLJ-NOT: .import_module emscripten_longjmp_jmpbuf
|
||||
; SJLJ-NOT: .import_name emscripten_longjmp_jmpbuf
|
||||
|
||||
%struct.__jmp_buf_tag = type { [6 x i32], i32, [32 x i32] }
|
||||
|
||||
define void @exception() personality i8* bitcast (i32 (...)* @__gxx_personality_v0 to i8*) {
|
||||
@ -87,18 +101,4 @@ attributes #0 = { returns_twice }
|
||||
attributes #1 = { noreturn }
|
||||
attributes #2 = { nounwind }
|
||||
|
||||
; EH: .functype invoke_vi (i32, i32) -> ()
|
||||
; EH: .import_module invoke_vi, env
|
||||
; EH: .import_name invoke_vi, invoke_vi
|
||||
; EH-NOT: .functype __invoke_void_i32
|
||||
; EH-NOT: .import_module __invoke_void_i32
|
||||
; EH-NOT: .import_name __invoke_void_i32
|
||||
|
||||
; SJLJ: .functype emscripten_longjmp (i32, i32) -> ()
|
||||
; SJLJ: .import_module emscripten_longjmp, env
|
||||
; SJLJ: .import_name emscripten_longjmp, emscripten_longjmp
|
||||
; SJLJ-NOT: .functype emscripten_longjmp_jmpbuf
|
||||
; SJLJ-NOT: .import_module emscripten_longjmp_jmpbuf
|
||||
; SJLJ-NOT: .import_name emscripten_longjmp_jmpbuf
|
||||
|
||||
; WASM-EH-EM-EH: LLVM ERROR: -exception-model=wasm not allowed with -enable-emscripten-cxx-exceptions
|
||||
|
@ -51,6 +51,8 @@ declare void @after_the_null()
|
||||
{ i32, void ()*, i8* } { i32 65535, void ()* @after_the_null, i8* null }
|
||||
]
|
||||
|
||||
; CHECK-LABEL: .functype __cxa_atexit (i32, i32, i32) -> (i32){{$}}
|
||||
|
||||
; CHECK-LABEL: .Lcall_dtors.0:
|
||||
; CHECK-NEXT: .functype .Lcall_dtors.0 (i32) -> (){{$}}
|
||||
; CHECK-NEXT: call orig_dtor0{{$}}
|
||||
@ -186,8 +188,6 @@ declare void @after_the_null()
|
||||
|
||||
; CHECK-LABEL: .weak __dso_handle
|
||||
|
||||
; CHECK-LABEL: .functype __cxa_atexit (i32, i32, i32) -> (i32){{$}}
|
||||
|
||||
; We shouldn't make use of a .fini_array section.
|
||||
|
||||
; FINI-NOT: fini_array
|
||||
|
@ -4,6 +4,8 @@
|
||||
declare void @ext_func(i64* %ptr)
|
||||
declare void @ext_func_i32(i32* %ptr)
|
||||
|
||||
; CHECK: .globaltype __stack_pointer, i[[PTR]]{{$}}
|
||||
|
||||
; CHECK-LABEL: alloca32:
|
||||
; Check that there is an extra local for the stack pointer.
|
||||
; CHECK: .local i[[PTR]]{{$}}
|
||||
@ -344,6 +346,4 @@ define i8 @frame_offset_with_global_address() {
|
||||
ret i8 %5
|
||||
}
|
||||
|
||||
; CHECK: .globaltype __stack_pointer, i[[PTR]]{{$}}
|
||||
|
||||
; TODO: test over-aligned alloca
|
||||
|
@ -21,14 +21,16 @@ sym_a:
|
||||
.section .text,"",@
|
||||
main:
|
||||
.functype main () -> ()
|
||||
i32.const 0
|
||||
i32.const sym_a
|
||||
i32.store sym_b
|
||||
end_function
|
||||
|
||||
# CHECK-LABEL: <main>:
|
||||
# CHECK-EMPTY:
|
||||
# CHECK-NEXT: 3: 41 84 80 80 80 00 i32.const 4
|
||||
# CHECK-NEXT: 00000004: R_WASM_MEMORY_ADDR_SLEB sym_a+0
|
||||
# CHECK-NEXT: 9: 36 02 88 80 80 80 00 i32.store 8
|
||||
# CHECK-NEXT: 0000000b: R_WASM_MEMORY_ADDR_LEB sym_b+0
|
||||
# CHECK-NEXT: 10: 0b end
|
||||
# CHECK-NEXT: 3: 41 00 i32.const 0
|
||||
# CHECK-NEXT: 5: 41 84 80 80 80 00 i32.const 4
|
||||
# CHECK-NEXT: 00000006: R_WASM_MEMORY_ADDR_SLEB sym_a+0
|
||||
# CHECK-NEXT: b: 36 02 88 80 80 80 00 i32.store 8
|
||||
# CHECK-NEXT: 0000000d: R_WASM_MEMORY_ADDR_LEB sym_b+0
|
||||
# CHECK-NEXT: 12: 0b end
|
||||
|
@ -1,4 +1,4 @@
|
||||
# RUN: llvm-mc -triple=wasm32-unknown-unknown -mattr=+exception-handling < %s | FileCheck %s
|
||||
# RUN: llvm-mc -no-type-check -triple=wasm32-unknown-unknown -mattr=+exception-handling < %s | FileCheck %s
|
||||
|
||||
# Tests if block/loop/try/catch/end/branch/rethrow instructions are correctly
|
||||
# printed with their annotations.
|
||||
|
@ -24,11 +24,11 @@ entry:
|
||||
; ASM: .text
|
||||
; ASM: .file "assembler-binary.ll"
|
||||
; ASM: .globl foo
|
||||
; ASM: .functype bar () -> ()
|
||||
; ASM: foo:
|
||||
; ASM-NEXT: .functype foo (i32) -> ()
|
||||
; ASM-NEXT: call bar
|
||||
; ASM-NEXT: end_function
|
||||
; ASM: .functype bar () -> ()
|
||||
|
||||
|
||||
; CHECK: --- !WASM
|
||||
|
@ -1,4 +1,4 @@
|
||||
# RUN: llvm-mc -show-encoding -triple=wasm32-unknown-unknown -mattr=+atomics < %s | FileCheck %s
|
||||
# RUN: llvm-mc -no-type-check -show-encoding -triple=wasm32-unknown-unknown -mattr=+atomics < %s | FileCheck %s
|
||||
|
||||
main:
|
||||
.functype main () -> ()
|
||||
|
@ -32,7 +32,7 @@ segment1:
|
||||
.text
|
||||
.section .text.main,"",@
|
||||
main:
|
||||
.functype main () -> (i32)
|
||||
.functype main () -> (i32, i32, i32, i32, i32)
|
||||
// Expressions involving symbols within the same sections can be evaluated
|
||||
// prior to writing the object file.
|
||||
// CHECK-NOT: foo
|
||||
|
@ -2,6 +2,9 @@
|
||||
# Check that it converts to .o without errors, but don't check any output:
|
||||
# RUN: llvm-mc -triple=wasm32-unknown-unknown -filetype=obj -mattr=+reference-types,+atomics,+simd128,+nontrapping-fptoint,+exception-handling -o %t.o < %s
|
||||
|
||||
.functype something1 () -> ()
|
||||
.functype something2 (i64) -> (i32, f64)
|
||||
.globaltype __stack_pointer, i32
|
||||
|
||||
empty_func:
|
||||
.functype empty_func () -> ()
|
||||
@ -18,31 +21,37 @@ test0:
|
||||
local.get 2
|
||||
local.set 2
|
||||
# Immediates:
|
||||
i32.const -1
|
||||
f64.const 0x1.999999999999ap1
|
||||
f32.const -1.0
|
||||
drop
|
||||
f32.const -infinity
|
||||
f32.const nan
|
||||
drop
|
||||
v128.const 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15
|
||||
drop
|
||||
v128.const 0, 1, 2, 3, 4, 5, 6, 7
|
||||
# Indirect addressing:
|
||||
drop
|
||||
local.get 0
|
||||
f64.const 0x1.999999999999ap1
|
||||
# Indirect addressing:
|
||||
f64.store 1234:p2align=4
|
||||
i32.const -1
|
||||
f64.const nan
|
||||
f64.store 1234 # Natural alignment (3)
|
||||
# Loops, conditionals, binary ops, calls etc:
|
||||
block i32
|
||||
block f32
|
||||
f32.const 2.0
|
||||
i32.const 1
|
||||
local.get 0
|
||||
i32.ge_s
|
||||
br_if 0 # 0: down to label0
|
||||
.LBB0_1:
|
||||
loop i32 # label1:
|
||||
loop void # label1:
|
||||
call something1
|
||||
i64.const 1234
|
||||
call something2
|
||||
i32.const 0
|
||||
call_indirect (i32, f64) -> ()
|
||||
i32.const 1
|
||||
i32.const 2
|
||||
i32.add
|
||||
local.tee 0
|
||||
local.get 0
|
||||
@ -51,18 +60,16 @@ test0:
|
||||
.LBB0_2:
|
||||
end_loop
|
||||
end_block # label0:
|
||||
local.get 4
|
||||
local.get 5
|
||||
drop
|
||||
block i32
|
||||
block void
|
||||
block void
|
||||
block void
|
||||
block i64
|
||||
block f32
|
||||
block f64
|
||||
block () -> (i32, i32)
|
||||
i32.const 1
|
||||
i32.const 2
|
||||
end_block
|
||||
drop
|
||||
drop
|
||||
br_table {0, 1, 2} # 2 entries, default
|
||||
end_block # first entry jumps here.
|
||||
i32.const 1
|
||||
@ -78,14 +85,21 @@ test0:
|
||||
end_if
|
||||
else
|
||||
end_if
|
||||
drop
|
||||
local.get 4
|
||||
local.get 5
|
||||
f32x4.add
|
||||
drop
|
||||
# Test correct parsing of instructions with / and : in them:
|
||||
# TODO: enable once instruction has been added.
|
||||
#i32x4.trunc_sat_f32x4_s
|
||||
f32.const 1.0
|
||||
i32.trunc_f32_s
|
||||
try
|
||||
i32.atomic.load 0
|
||||
i32.const 0
|
||||
memory.atomic.notify 0
|
||||
drop
|
||||
.LBB0_3:
|
||||
catch __cpp_exception
|
||||
local.set 0
|
||||
@ -97,6 +111,7 @@ test0:
|
||||
.LBB0_4:
|
||||
#i32.trunc_sat_f32_s
|
||||
global.get __stack_pointer
|
||||
global.set __stack_pointer
|
||||
end_function
|
||||
|
||||
.section .rodata..L.str,"",@
|
||||
@ -115,7 +130,6 @@ test0:
|
||||
.int32 test0
|
||||
|
||||
.ident "clang version 9.0.0 (trunk 364502) (llvm/trunk 364571)"
|
||||
.globaltype __stack_pointer, i32
|
||||
|
||||
.tabletype empty_eref_table, externref
|
||||
empty_eref_table:
|
||||
@ -125,6 +139,8 @@ empty_fref_table:
|
||||
|
||||
|
||||
# CHECK: .text
|
||||
# CHECK: .globaltype __stack_pointer, i32
|
||||
|
||||
# CHECK-LABEL: empty_func:
|
||||
# CHECK-NEXT: .functype empty_func () -> ()
|
||||
# CHECK-NEXT: end_function
|
||||
@ -135,29 +151,35 @@ empty_fref_table:
|
||||
# CHECK-NEXT: .local f32, f64
|
||||
# CHECK-NEXT: local.get 2
|
||||
# CHECK-NEXT: local.set 2
|
||||
# CHECK-NEXT: i32.const -1
|
||||
# CHECK-NEXT: f64.const 0x1.999999999999ap1
|
||||
# CHECK-NEXT: f32.const -0x1p0
|
||||
# CHECK-NEXT: drop
|
||||
# CHECK-NEXT: f32.const -infinity
|
||||
# CHECK-NEXT: f32.const nan
|
||||
# CHECK-NEXT: drop
|
||||
# CHECK-NEXT: v128.const 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15
|
||||
# CHECK-NEXT: drop
|
||||
# CHECK-NEXT: v128.const 0, 1, 2, 3, 4, 5, 6, 7
|
||||
# CHECK-NEXT: drop
|
||||
# CHECK-NEXT: local.get 0
|
||||
# CHECK-NEXT: f64.const 0x1.999999999999ap1
|
||||
# CHECK-NEXT: f64.store 1234:p2align=4
|
||||
# CHECK-NEXT: i32.const -1
|
||||
# CHECK-NEXT: f64.const nan
|
||||
# CHECK-NEXT: f64.store 1234
|
||||
# CHECK-NEXT: block i32
|
||||
# CHECK-NEXT: block f32
|
||||
# CHECK-NEXT: f32.const 0x1p1
|
||||
# CHECK-NEXT: i32.const 1
|
||||
# CHECK-NEXT: local.get 0
|
||||
# CHECK-NEXT: i32.ge_s
|
||||
# CHECK-NEXT: br_if 0 # 0: down to label0
|
||||
# CHECK-NEXT: .LBB0_1:
|
||||
# CHECK-NEXT: loop i32 # label1:
|
||||
# CHECK-NEXT: loop # label1:
|
||||
# CHECK-NEXT: call something1
|
||||
# CHECK-NEXT: i64.const 1234
|
||||
# CHECK-NEXT: call something2
|
||||
# CHECK-NEXT: i32.const 0
|
||||
# CHECK-NEXT: call_indirect __indirect_function_table, (i32, f64) -> ()
|
||||
# CHECK-NEXT: i32.const 1
|
||||
# CHECK-NEXT: i32.const 2
|
||||
# CHECK-NEXT: i32.add
|
||||
# CHECK-NEXT: local.tee 0
|
||||
# CHECK-NEXT: local.get 0
|
||||
@ -166,18 +188,16 @@ empty_fref_table:
|
||||
# CHECK-NEXT: .LBB0_2:
|
||||
# CHECK-NEXT: end_loop
|
||||
# CHECK-NEXT: end_block # label0:
|
||||
# CHECK-NEXT: local.get 4
|
||||
# CHECK-NEXT: local.get 5
|
||||
# CHECK-NEXT: drop
|
||||
# CHECK-NEXT: block i32
|
||||
# CHECK-NEXT: block
|
||||
# CHECK-NEXT: block
|
||||
# CHECK-NEXT: block
|
||||
# CHECK-NEXT: block i64
|
||||
# CHECK-NEXT: block f32
|
||||
# CHECK-NEXT: block f64
|
||||
# CHECK-NEXT: block () -> (i32, i32)
|
||||
# CHECK-NEXT: i32.const 1
|
||||
# CHECK-NEXT: i32.const 2
|
||||
# CHECK-NEXT: end_block
|
||||
# CHECK-NEXT: drop
|
||||
# CHECK-NEXT: drop
|
||||
# CHECK-NEXT: br_table {0, 1, 2} # 1: down to label4
|
||||
# CHECK-NEXT: # 2: down to label3
|
||||
# CHECK-NEXT: end_block # label5:
|
||||
@ -194,11 +214,18 @@ empty_fref_table:
|
||||
# CHECK-NEXT: end_if
|
||||
# CHECK-NEXT: else
|
||||
# CHECK-NEXT: end_if
|
||||
# CHECK-NEXT: drop
|
||||
# CHECK-NEXT: local.get 4
|
||||
# CHECK-NEXT: local.get 5
|
||||
# CHECK-NEXT: f32x4.add
|
||||
# CHECK-NEXT: drop
|
||||
# CHECK-NEXT: f32.const 0x1p0
|
||||
# CHECK-NEXT: i32.trunc_f32_s
|
||||
# CHECK-NEXT: try
|
||||
# CHECK-NEXT: i32.atomic.load 0
|
||||
# CHECK-NEXT: i32.const 0
|
||||
# CHECK-NEXT: memory.atomic.notify 0
|
||||
# CHECK-NEXT: drop
|
||||
# CHECK-NEXT: .LBB0_3:
|
||||
# CHECK-NEXT: catch __cpp_exception
|
||||
# CHECK-NEXT: local.set 0
|
||||
@ -209,6 +236,7 @@ empty_fref_table:
|
||||
# CHECK-NEXT: throw 0
|
||||
# CHECK-NEXT: .LBB0_4:
|
||||
# CHECK-NEXT: global.get __stack_pointer
|
||||
# CHECK-NEXT: global.set __stack_pointer
|
||||
# CHECK-NEXT: end_function
|
||||
|
||||
# CHECK: .section .rodata..L.str,"",@
|
||||
@ -225,8 +253,6 @@ empty_fref_table:
|
||||
# CHECK-NEXT: .p2align 2
|
||||
# CHECK-NEXT: .int32 test0
|
||||
|
||||
# CHECK: .globaltype __stack_pointer, i32
|
||||
|
||||
# CHECK: .tabletype empty_eref_table, externref
|
||||
# CHECK-NEXT: empty_eref_table:
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
# RUN: llvm-mc -show-encoding -triple=wasm32-unknown-unknown -mattr=+bulk-memory < %s | FileCheck %s
|
||||
# RUN: llvm-mc -show-encoding -triple=wasm64-unknown-unknown -mattr=+bulk-memory < %s | FileCheck %s
|
||||
# RUN: llvm-mc -show-encoding -no-type-check -triple=wasm32-unknown-unknown -mattr=+bulk-memory < %s | FileCheck %s
|
||||
# RUN: llvm-mc -show-encoding -no-type-check -triple=wasm64-unknown-unknown -mattr=+bulk-memory < %s | FileCheck %s
|
||||
|
||||
main:
|
||||
.functype main () -> ()
|
||||
|
@ -3,7 +3,7 @@
|
||||
# RUN: llvm-mc -triple=wasm32-unknown-unknown < %s | FileCheck %s
|
||||
|
||||
test0:
|
||||
.functype test0 () -> (i32)
|
||||
.functype test0 () -> (i32, i32)
|
||||
i32.const a
|
||||
i32.const b
|
||||
end_function
|
||||
|
@ -16,11 +16,14 @@ read_global:
|
||||
end_function
|
||||
|
||||
write_global:
|
||||
.functype write_global (i32) -> ()
|
||||
.functype write_global (i32, i64, f32, f64) -> ()
|
||||
local.get 0
|
||||
global.set foo_global
|
||||
local.get 1
|
||||
global.set global2
|
||||
local.get 2
|
||||
global.set global3
|
||||
local.get 3
|
||||
global.set global4
|
||||
end_function
|
||||
|
||||
|
@ -1,14 +1,15 @@
|
||||
# RUN: llvm-mc -triple=wasm32 < %s | FileCheck %s -check-prefix=CHECK-ASM
|
||||
# RUN: llvm-mc -triple=wasm32 -filetype=obj -o - < %s | obj2yaml | FileCheck %s
|
||||
|
||||
.functype foo () -> ()
|
||||
.functype plain () -> ()
|
||||
|
||||
test:
|
||||
.functype test () -> ()
|
||||
call foo
|
||||
call plain
|
||||
end_function
|
||||
|
||||
.functype foo () -> ()
|
||||
.functype plain () -> ()
|
||||
.import_module foo, bar
|
||||
.import_name foo, qux
|
||||
|
||||
|
@ -5,6 +5,7 @@
|
||||
.type main,@function
|
||||
main:
|
||||
.functype main (i32, i32) -> (i32)
|
||||
local.get 0
|
||||
end_function
|
||||
.Lfunc_end0:
|
||||
.size main, .Lfunc_end0-main
|
||||
|
@ -2,13 +2,13 @@
|
||||
# RUN: llvm-objdump --triple=wasm32-unknown-unknown -d %t.o | FileCheck %s
|
||||
|
||||
test0:
|
||||
.functype test0 (i32, i64) -> (i32)
|
||||
.functype test0 (i32, i64) -> (f32)
|
||||
.local f32, f64, v128, v128
|
||||
local.get 2
|
||||
end_function
|
||||
|
||||
test1:
|
||||
.functype test1 (i32, i64) -> (i32)
|
||||
.functype test1 (i32, i64) -> (i64)
|
||||
.local i32, i64, funcref
|
||||
local.get 3
|
||||
end_function
|
||||
|
@ -16,12 +16,14 @@ ref_null_test:
|
||||
# CHECK-NEXT: .functype ref_sig_test_funcref (funcref) -> (funcref)
|
||||
ref_sig_test_funcref:
|
||||
.functype ref_sig_test_funcref (funcref) -> (funcref)
|
||||
local.get 0
|
||||
end_function
|
||||
|
||||
# CHECK-LABEL: ref_sig_test_externref:
|
||||
# CHECK-NEXT: .functype ref_sig_test_externref (externref) -> (externref)
|
||||
ref_sig_test_externref:
|
||||
.functype ref_sig_test_externref (externref) -> (externref)
|
||||
local.get 0
|
||||
end_function
|
||||
|
||||
# CHECK-LABEL: ref_select_test:
|
||||
@ -45,9 +47,11 @@ ref_select_test:
|
||||
# CHECK: block funcref
|
||||
# CHECK: block externref
|
||||
ref_block_test:
|
||||
.functype ref_block_test () -> ()
|
||||
.functype ref_block_test () -> (externref, funcref)
|
||||
block funcref
|
||||
block externref
|
||||
ref.null extern
|
||||
end_block
|
||||
ref.null func
|
||||
end_block
|
||||
end_function
|
||||
|
@ -5,6 +5,11 @@
|
||||
# against the corrsponding function or data symbol and that the corresponding
|
||||
# data symbols are imported as a wasm globals.
|
||||
|
||||
.functype default_func () -> (i32)
|
||||
|
||||
.globaltype __memory_base, i32
|
||||
.globaltype __table_base, i32
|
||||
|
||||
load_default_data:
|
||||
.functype load_default_data () -> (i32)
|
||||
global.get default_data@GOT
|
||||
@ -44,7 +49,6 @@ hidden_func:
|
||||
#.hidden hidden_func
|
||||
#.hidden hidden_data
|
||||
.size default_data, 4
|
||||
.functype default_func () -> (i32)
|
||||
|
||||
# CHECK: --- !WASM
|
||||
# CHECK-NEXT: FileHeader:
|
||||
@ -68,6 +72,16 @@ hidden_func:
|
||||
# CHECK-NEXT: Kind: FUNCTION
|
||||
# CHECK-NEXT: SigIndex: 0
|
||||
# CHECK-NEXT: - Module: env
|
||||
# CHECK-NEXT: Field: __memory_base
|
||||
# CHECK-NEXT: Kind: GLOBAL
|
||||
# CHECK-NEXT: GlobalType: I32
|
||||
# CHECK-NEXT: GlobalMutable: true
|
||||
# CHECK-NEXT: - Module: env
|
||||
# CHECK-NEXT: Field: __table_base
|
||||
# CHECK-NEXT: Kind: GLOBAL
|
||||
# CHECK-NEXT: GlobalType: I32
|
||||
# CHECK-NEXT: GlobalMutable: true
|
||||
# CHECK-NEXT: - Module: env
|
||||
# CHECK-NEXT: Field: __indirect_function_table
|
||||
# CHECK-NEXT: Kind: TABLE
|
||||
# CHECK-NEXT: Table:
|
||||
@ -103,13 +117,13 @@ hidden_func:
|
||||
# CHECK-NEXT: - Type: R_WASM_GLOBAL_INDEX_LEB
|
||||
# CHECK-NEXT: Index: 3
|
||||
# CHECK-NEXT: Offset: 0x10
|
||||
# CHECK-NEXT: - Type: R_WASM_MEMORY_ADDR_LEB
|
||||
# CHECK-NEXT: - Type: R_WASM_GLOBAL_INDEX_LEB
|
||||
# CHECK-NEXT: Index: 5
|
||||
# CHECK-NEXT: Offset: 0x1C
|
||||
# CHECK-NEXT: - Type: R_WASM_MEMORY_ADDR_REL_SLEB
|
||||
# CHECK-NEXT: Index: 6
|
||||
# CHECK-NEXT: Offset: 0x22
|
||||
# CHECK-NEXT: - Type: R_WASM_MEMORY_ADDR_LEB
|
||||
# CHECK-NEXT: - Type: R_WASM_GLOBAL_INDEX_LEB
|
||||
# CHECK-NEXT: Index: 8
|
||||
# CHECK-NEXT: Offset: 0x2C
|
||||
# CHECK-NEXT: - Type: R_WASM_TABLE_INDEX_REL_SLEB
|
||||
@ -118,16 +132,16 @@ hidden_func:
|
||||
# CHECK-NEXT: Functions:
|
||||
# CHECK-NEXT: - Index: 1
|
||||
# CHECK-NEXT: Locals: []
|
||||
# CHECK-NEXT: Body: 2380808080002802000B
|
||||
# CHECK-NEXT: Body: 2382808080002802000B
|
||||
# CHECK-NEXT: - Index: 2
|
||||
# CHECK-NEXT: Locals: []
|
||||
# CHECK-NEXT: Body: 2381808080002802000B
|
||||
# CHECK-NEXT: Body: 2383808080002802000B
|
||||
# CHECK-NEXT: - Index: 3
|
||||
# CHECK-NEXT: Locals: []
|
||||
# CHECK-NEXT: Body: 2380808080004180808080006A0B
|
||||
# CHECK-NEXT: - Index: 4
|
||||
# CHECK-NEXT: Locals: []
|
||||
# CHECK-NEXT: Body: 2380808080004180808080006A0B
|
||||
# CHECK-NEXT: Body: 2381808080004180808080006A0B
|
||||
# CHECK-NEXT: - Index: 5
|
||||
# CHECK-NEXT: Locals: []
|
||||
# CHECK-NEXT: Body: 41000B
|
||||
@ -168,9 +182,10 @@ hidden_func:
|
||||
# CHECK-NEXT: Flags: [ BINDING_LOCAL ]
|
||||
# CHECK-NEXT: Function: 3
|
||||
# CHECK-NEXT: - Index: 5
|
||||
# CHECK-NEXT: Kind: DATA
|
||||
# CHECK-NEXT: Kind: GLOBAL
|
||||
# CHECK-NEXT: Name: __memory_base
|
||||
# CHECK-NEXT: Flags: [ UNDEFINED ]
|
||||
# CHECK-NEXT: Global: 0
|
||||
# CHECK-NEXT: - Index: 6
|
||||
# CHECK-NEXT: Kind: DATA
|
||||
# CHECK-NEXT: Name: .L.hidden_data
|
||||
@ -183,9 +198,10 @@ hidden_func:
|
||||
# CHECK-NEXT: Flags: [ BINDING_LOCAL ]
|
||||
# CHECK-NEXT: Function: 4
|
||||
# CHECK-NEXT: - Index: 8
|
||||
# CHECK-NEXT: Kind: DATA
|
||||
# CHECK-NEXT: Kind: GLOBAL
|
||||
# CHECK-NEXT: Name: __table_base
|
||||
# CHECK-NEXT: Flags: [ UNDEFINED ]
|
||||
# CHECK-NEXT: Global: 1
|
||||
# CHECK-NEXT: - Index: 9
|
||||
# CHECK-NEXT: Kind: FUNCTION
|
||||
# CHECK-NEXT: Name: hidden_func
|
||||
|
@ -5,6 +5,11 @@
|
||||
# against the corrsponding function or data symbol and that the corresponding
|
||||
# data symbols are imported as a wasm globals.
|
||||
|
||||
.functype default_func () -> (i32)
|
||||
|
||||
.globaltype __memory_base, i64
|
||||
.globaltype __table_base, i64
|
||||
|
||||
load_default_data:
|
||||
.functype load_default_data () -> (i32)
|
||||
global.get default_data@GOT
|
||||
@ -44,7 +49,6 @@ hidden_func:
|
||||
#.hidden hidden_func
|
||||
#.hidden hidden_data
|
||||
.size default_data, 4
|
||||
.functype default_func () -> (i32)
|
||||
|
||||
# CHECK: --- !WASM
|
||||
# CHECK-NEXT: FileHeader:
|
||||
@ -73,6 +77,16 @@ hidden_func:
|
||||
# CHECK-NEXT: Kind: FUNCTION
|
||||
# CHECK-NEXT: SigIndex: 0
|
||||
# CHECK-NEXT: - Module: env
|
||||
# CHECK-NEXT: Field: __memory_base
|
||||
# CHECK-NEXT: Kind: GLOBAL
|
||||
# CHECK-NEXT: GlobalType: I64
|
||||
# CHECK-NEXT: GlobalMutable: true
|
||||
# CHECK-NEXT: - Module: env
|
||||
# CHECK-NEXT: Field: __table_base
|
||||
# CHECK-NEXT: Kind: GLOBAL
|
||||
# CHECK-NEXT: GlobalType: I64
|
||||
# CHECK-NEXT: GlobalMutable: true
|
||||
# CHECK-NEXT: - Module: env
|
||||
# CHECK-NEXT: Field: __indirect_function_table
|
||||
# CHECK-NEXT: Kind: TABLE
|
||||
# CHECK-NEXT: Table:
|
||||
@ -108,31 +122,31 @@ hidden_func:
|
||||
# CHECK-NEXT: - Type: R_WASM_GLOBAL_INDEX_LEB
|
||||
# CHECK-NEXT: Index: 3
|
||||
# CHECK-NEXT: Offset: 0x10
|
||||
# CHECK-NEXT: - Type: R_WASM_MEMORY_ADDR_LEB
|
||||
# CHECK-NEXT: - Type: R_WASM_GLOBAL_INDEX_LEB
|
||||
# CHECK-NEXT: Index: 5
|
||||
# CHECK-NEXT: Offset: 0x1C
|
||||
# CHECK-NEXT: - Type: R_WASM_MEMORY_ADDR_REL_SLEB
|
||||
# CHECK-NEXT: - Type: R_WASM_MEMORY_ADDR_REL_SLEB64
|
||||
# CHECK-NEXT: Index: 6
|
||||
# CHECK-NEXT: Offset: 0x22
|
||||
# CHECK-NEXT: - Type: R_WASM_MEMORY_ADDR_LEB
|
||||
# CHECK-NEXT: - Type: R_WASM_GLOBAL_INDEX_LEB
|
||||
# CHECK-NEXT: Index: 8
|
||||
# CHECK-NEXT: Offset: 0x31
|
||||
# CHECK-NEXT: - Type: R_WASM_TABLE_INDEX_REL_SLEB
|
||||
# CHECK-NEXT: - Type: R_WASM_TABLE_INDEX_REL_SLEB64
|
||||
# CHECK-NEXT: Index: 9
|
||||
# CHECK-NEXT: Offset: 0x37
|
||||
# CHECK-NEXT: Functions:
|
||||
# CHECK-NEXT: - Index: 1
|
||||
# CHECK-NEXT: Locals: []
|
||||
# CHECK-NEXT: Body: 2380808080002802000B
|
||||
# CHECK-NEXT: Body: 2382808080002802000B
|
||||
# CHECK-NEXT: - Index: 2
|
||||
# CHECK-NEXT: Locals: []
|
||||
# CHECK-NEXT: Body: 2381808080002802000B
|
||||
# CHECK-NEXT: Body: 2383808080002802000B
|
||||
# CHECK-NEXT: - Index: 3
|
||||
# CHECK-NEXT: Locals: []
|
||||
# CHECK-NEXT: Body: 23808080800042808080808080808080007C0B
|
||||
# CHECK-NEXT: - Index: 4
|
||||
# CHECK-NEXT: Locals: []
|
||||
# CHECK-NEXT: Body: 23808080800042808080808080808080007C0B
|
||||
# CHECK-NEXT: Body: 23818080800042808080808080808080007C0B
|
||||
# CHECK-NEXT: - Index: 5
|
||||
# CHECK-NEXT: Locals: []
|
||||
# CHECK-NEXT: Body: 41000B
|
||||
@ -173,9 +187,10 @@ hidden_func:
|
||||
# CHECK-NEXT: Flags: [ BINDING_LOCAL ]
|
||||
# CHECK-NEXT: Function: 3
|
||||
# CHECK-NEXT: - Index: 5
|
||||
# CHECK-NEXT: Kind: DATA
|
||||
# CHECK-NEXT: Kind: GLOBAL
|
||||
# CHECK-NEXT: Name: __memory_base
|
||||
# CHECK-NEXT: Flags: [ UNDEFINED ]
|
||||
# CHECK-NEXT: Global: 0
|
||||
# CHECK-NEXT: - Index: 6
|
||||
# CHECK-NEXT: Kind: DATA
|
||||
# CHECK-NEXT: Name: .L.hidden_data
|
||||
@ -188,9 +203,10 @@ hidden_func:
|
||||
# CHECK-NEXT: Flags: [ BINDING_LOCAL ]
|
||||
# CHECK-NEXT: Function: 4
|
||||
# CHECK-NEXT: - Index: 8
|
||||
# CHECK-NEXT: Kind: DATA
|
||||
# CHECK-NEXT: Kind: GLOBAL
|
||||
# CHECK-NEXT: Name: __table_base
|
||||
# CHECK-NEXT: Flags: [ UNDEFINED ]
|
||||
# CHECK-NEXT: Global: 1
|
||||
# CHECK-NEXT: - Index: 9
|
||||
# CHECK-NEXT: Kind: FUNCTION
|
||||
# CHECK-NEXT: Name: hidden_func
|
||||
|
@ -1,4 +1,4 @@
|
||||
# RUN: llvm-mc -show-encoding -triple=wasm32-unknown-unknown -mattr=+simd128 < %s | FileCheck %s
|
||||
# RUN: llvm-mc -no-type-check -show-encoding -triple=wasm32-unknown-unknown -mattr=+simd128 < %s | FileCheck %s
|
||||
|
||||
main:
|
||||
.functype main () -> ()
|
||||
|
@ -15,8 +15,10 @@ foo1:
|
||||
end_function
|
||||
|
||||
foo2:
|
||||
.functype foo2 () -> ()
|
||||
.functype foo2 () -> (i32)
|
||||
|
||||
i32.const 0
|
||||
i32.const 0
|
||||
# REF: return_call_indirect __indirect_function_table, (i32) -> (i32) # encoding: [0x13,
|
||||
# CHECK: return_call_indirect (i32) -> (i32) # encoding: [0x13,
|
||||
# CHECK-NEXT: fixup A - offset: 1, value: .Ltypeindex0@TYPEINDEX, kind: fixup_uleb128_i32
|
||||
|
@ -2,12 +2,15 @@
|
||||
# RUN: llvm-mc -triple=wasm32-unknown-unknown -filetype=obj -o %t.o < %s
|
||||
# RUN: obj2yaml %t.o | FileCheck %s --check-prefix=CHECK-OBJ
|
||||
|
||||
.globaltype __tls_base, i32
|
||||
|
||||
tls_store:
|
||||
.functype tls_store (i32) -> ()
|
||||
# CHECK: global.get __tls_base
|
||||
# CHECK-NEXT: i32.const tls1@TLSREL
|
||||
# CHECK-NEXT: i32.add
|
||||
# CHECK-NEXT: i32.store 0
|
||||
local.get 0
|
||||
global.get __tls_base
|
||||
i32.const tls1@TLSREL
|
||||
i32.add
|
||||
@ -28,12 +31,12 @@ tls2:
|
||||
|
||||
# CHECK-OBJ: - Type: CODE
|
||||
# CHECK-OBJ-NEXT: Relocations:
|
||||
# CHECK-OBJ-NEXT: - Type: R_WASM_MEMORY_ADDR_LEB
|
||||
# CHECK-OBJ-NEXT: - Type: R_WASM_GLOBAL_INDEX_LEB
|
||||
# CHECK-OBJ-NEXT: Index: 1
|
||||
# CHECK-OBJ-NEXT: Offset: 0x4
|
||||
# CHECK-OBJ-NEXT: Offset: 0x6
|
||||
# CHECK-OBJ-NEXT: - Type: R_WASM_MEMORY_ADDR_TLS_SLEB
|
||||
# CHECK-OBJ-NEXT: Index: 2
|
||||
# CHECK-OBJ-NEXT: Offset: 0xA
|
||||
# CHECK-OBJ-NEXT: Offset: 0xC
|
||||
|
||||
# CHECK-OBJ: - Type: CUSTOM
|
||||
# CHECK-OBJ-NEXT: Name: linking
|
||||
@ -45,9 +48,10 @@ tls2:
|
||||
# CHECK-OBJ-NEXT: Flags: [ BINDING_LOCAL ]
|
||||
# CHECK-OBJ-NEXT: Function: 0
|
||||
# CHECK-OBJ-NEXT: - Index: 1
|
||||
# CHECK-OBJ-NEXT: Kind: DATA
|
||||
# CHECK-OBJ-NEXT: Kind: GLOBAL
|
||||
# CHECK-OBJ-NEXT: Name: __tls_base
|
||||
# CHECK-OBJ-NEXT: Flags: [ UNDEFINED ]
|
||||
# CHECK-OBJ-NEXT: Global: 0
|
||||
# CHECK-OBJ-NEXT: - Index: 2
|
||||
# CHECK-OBJ-NEXT: Kind: DATA
|
||||
# CHECK-OBJ-NEXT: Name: tls1
|
||||
|
@ -6,13 +6,15 @@
|
||||
|
||||
test0:
|
||||
.functype test0 (i32) -> (i32)
|
||||
call_indirect (f64) -> (f64)
|
||||
f64.const 1.0
|
||||
local.get 0
|
||||
call_indirect (f64) -> (i32)
|
||||
end_function
|
||||
|
||||
# CHECK: .text
|
||||
# CHECK-LABEL: test0:
|
||||
# CHECK-NEXT: .functype test0 (i32) -> (i32)
|
||||
# CHECK-NEXT: call_indirect __indirect_function_table, (f64) -> (f64)
|
||||
# CHECK: call_indirect __indirect_function_table, (f64) -> (i32)
|
||||
# CHECK-NEXT: end_function
|
||||
|
||||
# BIN: --- !WASM
|
||||
@ -30,7 +32,7 @@ test0:
|
||||
# BIN-NEXT: ParamTypes:
|
||||
# BIN-NEXT: - F64
|
||||
# BIN-NEXT: ReturnTypes:
|
||||
# BIN-NEXT: - F64
|
||||
# BIN-NEXT: - I32
|
||||
# BIN-NEXT: - Type: IMPORT
|
||||
# BIN-NEXT: Imports:
|
||||
# BIN-NEXT: - Module: env
|
||||
@ -52,14 +54,14 @@ test0:
|
||||
# BIN-NEXT: Relocations:
|
||||
# BIN-NEXT: - Type: R_WASM_TYPE_INDEX_LEB
|
||||
# BIN-NEXT: Index: 1
|
||||
# BIN-NEXT: Offset: 0x4
|
||||
# BIN-NEXT: Offset: 0xF
|
||||
# BIN-NEXT: - Type: R_WASM_TABLE_NUMBER_LEB
|
||||
# BIN-NEXT: Index: 1
|
||||
# BIN-NEXT: Offset: 0x9
|
||||
# BIN-NEXT: Offset: 0x14
|
||||
# BIN-NEXT: Functions:
|
||||
# BIN-NEXT: - Index: 0
|
||||
# BIN-NEXT: Locals: []
|
||||
# BIN-NEXT: Body: 11818080800080808080000B
|
||||
# BIN-NEXT: Body: 44000000000000F03F200011818080800080808080000B
|
||||
# BIN-NEXT: - Type: CUSTOM
|
||||
# BIN-NEXT: Name: linking
|
||||
# BIN-NEXT: Version: 2
|
||||
|
@ -3,6 +3,9 @@
|
||||
|
||||
# Most of our other tests are for wasm32, this one adds some wasm64 specific tests.
|
||||
|
||||
.globaltype myglob64, i64
|
||||
.globaltype __stack_pointer, i64
|
||||
|
||||
test:
|
||||
.functype test (i64) -> ()
|
||||
.local i64
|
||||
@ -31,24 +34,24 @@ test:
|
||||
|
||||
### basic stores
|
||||
|
||||
f32.const 0.0
|
||||
i64.const 0 # get i64 from constant.
|
||||
f32.const 0.0
|
||||
f32.store 0
|
||||
|
||||
f32.const 0.0
|
||||
local.get 0 # get i64 from local.
|
||||
f32.const 0.0
|
||||
f32.store 0
|
||||
|
||||
f32.const 0.0
|
||||
i64.const .L.str # get i64 relocatable.
|
||||
f32.const 0.0
|
||||
f32.store 0
|
||||
|
||||
f32.const 0.0
|
||||
global.get myglob64 # get i64 from global
|
||||
f32.const 0.0
|
||||
f32.store 0
|
||||
|
||||
f32.const 0.0
|
||||
i64.const 0
|
||||
f32.const 0.0
|
||||
f32.store .L.str # relocatable offset!
|
||||
|
||||
### 64-bit SP
|
||||
@ -66,9 +69,8 @@ test:
|
||||
.int64 .L.str # relocatable inside data.
|
||||
.size .L.str, 24
|
||||
|
||||
.globaltype myglob64, i64
|
||||
.globaltype __stack_pointer, i64
|
||||
|
||||
# CHECK: .globaltype myglob64, i64
|
||||
|
||||
# CHECK: .functype test (i64) -> ()
|
||||
# CHECK-NEXT: .local i64
|
||||
@ -95,24 +97,24 @@ test:
|
||||
# CHECK-NEXT: drop
|
||||
|
||||
|
||||
# CHECK: f32.const 0x0p0
|
||||
# CHECK-NEXT: i64.const 0
|
||||
# CHECK: i64.const 0
|
||||
# CHECK-NEXT: f32.const 0x0p0
|
||||
# CHECK-NEXT: f32.store 0
|
||||
|
||||
# CHECK: f32.const 0x0p0
|
||||
# CHECK-NEXT: local.get 0
|
||||
# CHECK: local.get 0
|
||||
# CHECK-NEXT: f32.const 0x0p0
|
||||
# CHECK-NEXT: f32.store 0
|
||||
|
||||
# CHECK: f32.const 0x0p0
|
||||
# CHECK-NEXT: i64.const .L.str
|
||||
# CHECK: i64.const .L.str
|
||||
# CHECK-NEXT: f32.const 0x0p0
|
||||
# CHECK-NEXT: f32.store 0
|
||||
|
||||
# CHECK: f32.const 0x0p0
|
||||
# CHECK-NEXT: global.get myglob64
|
||||
# CHECK: global.get myglob64
|
||||
# CHECK-NEXT: f32.const 0x0p0
|
||||
# CHECK-NEXT: f32.store 0
|
||||
|
||||
# CHECK: f32.const 0x0p0
|
||||
# CHECK-NEXT: i64.const 0
|
||||
# CHECK: i64.const 0
|
||||
# CHECK-NEXT: f32.const 0x0p0
|
||||
# CHECK-NEXT: f32.store .L.str
|
||||
|
||||
|
||||
@ -127,8 +129,6 @@ test:
|
||||
# CHECK-NEXT: .int64 .L.str
|
||||
# CHECK-NEXT: .size .L.str, 24
|
||||
|
||||
# CHECK: .globaltype myglob64, i64
|
||||
|
||||
|
||||
|
||||
# BIN: --- !WASM
|
||||
@ -176,10 +176,10 @@ test:
|
||||
# BIN-NEXT: Offset: 0x2F
|
||||
# BIN-NEXT: - Type: R_WASM_MEMORY_ADDR_SLEB64
|
||||
# BIN-NEXT: Index: 1
|
||||
# BIN-NEXT: Offset: 0x54
|
||||
# BIN-NEXT: Offset: 0x4F
|
||||
# BIN-NEXT: - Type: R_WASM_GLOBAL_INDEX_LEB
|
||||
# BIN-NEXT: Index: 2
|
||||
# BIN-NEXT: Offset: 0x67
|
||||
# BIN-NEXT: Offset: 0x62
|
||||
# BIN-NEXT: - Type: R_WASM_MEMORY_ADDR_LEB64
|
||||
# BIN-NEXT: Index: 1
|
||||
# BIN-NEXT: Offset: 0x78
|
||||
@ -191,7 +191,7 @@ test:
|
||||
# BIN-NEXT: Locals:
|
||||
# BIN-NEXT: - Type: I64
|
||||
# BIN-NEXT: Count: 1
|
||||
# BIN-NEXT: Body: 42002A02001A20002A02001A42808080808080808080002A02001A2380808080002A02001A42002A02808080808080808080001A4300000000420038020043000000002000380200430000000042808080808080808080003802004300000000238080808000380200430000000042003802808080808080808080002381808080001A0B
|
||||
# BIN-NEXT: Body: 42002A02001A20002A02001A42808080808080808080002A02001A2380808080002A02001A42002A02808080808080808080001A4200430000000038020020004300000000380200428080808080808080800043000000003802002380808080004300000000380200420043000000003802808080808080808080002381808080001A0B
|
||||
# BIN-NEXT: - Type: DATA
|
||||
# BIN-NEXT: Relocations:
|
||||
# BIN-NEXT: - Type: R_WASM_MEMORY_ADDR_I64
|
||||
|
@ -5,6 +5,8 @@
|
||||
# 'bar_alias' is weak alias of global variable 'bar'
|
||||
# Generates two exports of the same function, one of them weak
|
||||
|
||||
.functype foo_alias () -> (i32)
|
||||
|
||||
foo:
|
||||
.hidden foo
|
||||
.globl foo
|
||||
|
@ -18,5 +18,5 @@ static_library("AsmParser") {
|
||||
"//llvm/lib/Target/WebAssembly/Utils",
|
||||
]
|
||||
include_dirs = [ ".." ]
|
||||
sources = [ "WebAssemblyAsmParser.cpp" ]
|
||||
sources = [ "WebAssemblyAsmParser.cpp", "WebAssemblyAsmTypeCheck.cpp" ]
|
||||
}
|
||||
|
@ -53,6 +53,7 @@ static_library("LLVMWebAssemblyCodeGen") {
|
||||
"WebAssemblyLowerEmscriptenEHSjLj.cpp",
|
||||
"WebAssemblyLowerGlobalDtors.cpp",
|
||||
"WebAssemblyMCInstLower.cpp",
|
||||
"WebAssemblyMCLowerPrePass.cpp",
|
||||
"WebAssemblyMachineFunctionInfo.cpp",
|
||||
"WebAssemblyMemIntrinsicResults.cpp",
|
||||
"WebAssemblyNullifyDebugValueLists.cpp",
|
||||
|
Loading…
Reference in New Issue
Block a user