[WebAssembly] Basic support for Wasm object file encoding.
With the "wasm32-unknown-unknown-wasm" triple, this allows writing out
simple wasm object files, and is another step in a larger series toward
migrating from ELF to general wasm object support. Note that this code
and the binary format itself is still experimental.
llvm-svn: 296190
diff --git a/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyAsmBackend.cpp b/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyAsmBackend.cpp
index 337c18c..5b21074 100644
--- a/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyAsmBackend.cpp
+++ b/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyAsmBackend.cpp
@@ -13,6 +13,7 @@
//===----------------------------------------------------------------------===//
#include "MCTargetDesc/WebAssemblyMCTargetDesc.h"
+#include "MCTargetDesc/WebAssemblyFixupKinds.h"
#include "llvm/MC/MCAsmBackend.h"
#include "llvm/MC/MCAssembler.h"
#include "llvm/MC/MCDirectives.h"
@@ -70,6 +71,12 @@
: MCAsmBackend(), Is64Bit(Is64Bit) {}
~WebAssemblyAsmBackend() override {}
+ unsigned getNumFixupKinds() const override {
+ return WebAssembly::NumTargetFixupKinds;
+ }
+
+ const MCFixupKindInfo &getFixupKindInfo(MCFixupKind Kind) const override;
+
void applyFixup(const MCFixup &Fixup, char *Data, unsigned DataSize,
uint64_t Value, bool IsPCRel) const override;
@@ -82,12 +89,6 @@
return false;
}
- unsigned getNumFixupKinds() const override {
- // We currently just use the generic fixups in MCFixup.h and don't have any
- // target-specific fixups.
- return 0;
- }
-
bool mayNeedRelaxation(const MCInst &Inst) const override { return false; }
void relaxInstruction(const MCInst &Inst, const MCSubtargetInfo &STI,
@@ -131,6 +132,26 @@
return createWebAssemblyELFObjectWriter(OS, Is64Bit, 0);
}
+const MCFixupKindInfo &
+WebAssemblyAsmBackend::getFixupKindInfo(MCFixupKind Kind) const {
+ const static MCFixupKindInfo Infos[WebAssembly::NumTargetFixupKinds] = {
+ // This table *must* be in the order that the fixup_* kinds are defined in
+ // WebAssemblyFixupKinds.h.
+ //
+ // Name Offset (bits) Size (bits) Flags
+ { "fixup_code_sleb128_i32", 0, 5*8, 0 },
+ { "fixup_code_sleb128_i64", 0, 10*8, 0 },
+ { "fixup_code_uleb128_i32", 0, 5*8, 0 },
+ };
+
+ if (Kind < FirstTargetFixupKind)
+ return MCAsmBackend::getFixupKindInfo(Kind);
+
+ assert(unsigned(Kind - FirstTargetFixupKind) < getNumFixupKinds() &&
+ "Invalid kind!");
+ return Infos[Kind - FirstTargetFixupKind];
+}
+
bool WebAssemblyAsmBackend::writeNopData(uint64_t Count,
MCObjectWriter *OW) const {
if (Count == 0)
diff --git a/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyFixupKinds.h b/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyFixupKinds.h
new file mode 100644
index 0000000..b0af63c
--- /dev/null
+++ b/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyFixupKinds.h
@@ -0,0 +1,31 @@
+//=- WebAssemblyFixupKinds.h - WebAssembly Specific Fixup Entries -*- C++ -*-=//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIB_TARGET_WEBASSEMBLY_MCTARGETDESC_WEBASSEMBLYFIXUPKINDS_H
+#define LLVM_LIB_TARGET_WEBASSEMBLY_MCTARGETDESC_WEBASSEMBLYFIXUPKINDS_H
+
+#include "llvm/MC/MCFixup.h"
+
+namespace llvm {
+namespace WebAssembly {
+enum Fixups {
+ fixup_code_sleb128_i32 = FirstTargetFixupKind, // 32-bit signed
+ fixup_code_sleb128_i64, // 64-bit signed
+ fixup_code_uleb128_i32, // 32-bit unsigned
+
+ fixup_code_global_index, // 32-bit unsigned
+
+ // Marker
+ LastTargetFixupKind,
+ NumTargetFixupKinds = LastTargetFixupKind - FirstTargetFixupKind
+};
+} // end namespace WebAssembly
+} // end namespace llvm
+
+#endif
diff --git a/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCCodeEmitter.cpp b/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCCodeEmitter.cpp
index 91583ef..a0b0089 100644
--- a/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCCodeEmitter.cpp
+++ b/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCCodeEmitter.cpp
@@ -13,6 +13,7 @@
//===----------------------------------------------------------------------===//
#include "MCTargetDesc/WebAssemblyMCTargetDesc.h"
+#include "MCTargetDesc/WebAssemblyFixupKinds.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/Statistic.h"
#include "llvm/MC/MCCodeEmitter.h"
@@ -48,9 +49,7 @@
public:
WebAssemblyMCCodeEmitter(const MCInstrInfo &mcii, MCContext &ctx)
- : MCII(mcii), Ctx(ctx) {
- (void)Ctx;
- }
+ : MCII(mcii), Ctx(ctx) {}
};
} // end anonymous namespace
@@ -68,6 +67,13 @@
assert(Binary < UINT8_MAX && "Multi-byte opcodes not supported yet");
OS << uint8_t(Binary);
+ // For br_table instructions, encode the size of the table. In the MCInst,
+ // there's an index operand, one operand for each table entry, and the
+ // default operand.
+ if (MI.getOpcode() == WebAssembly::BR_TABLE_I32 ||
+ MI.getOpcode() == WebAssembly::BR_TABLE_I64)
+ encodeULEB128(MI.getNumOperands() - 2, OS);
+
const MCInstrDesc &Desc = MCII.get(MI.getOpcode());
for (unsigned i = 0, e = MI.getNumOperands(); i < e; ++i) {
const MCOperand &MO = MI.getOperand(i);
@@ -82,6 +88,12 @@
encodeSLEB128(int32_t(MO.getImm()), OS);
} else if (Info.OperandType == WebAssembly::OPERAND_I64IMM) {
encodeSLEB128(int64_t(MO.getImm()), OS);
+ } else if (Info.OperandType == WebAssembly::OPERAND_GLOBAL) {
+ Fixups.push_back(MCFixup::create(
+ OS.tell() - Start, MCConstantExpr::create(MO.getImm(), Ctx),
+ MCFixupKind(WebAssembly::fixup_code_global_index), MI.getLoc()));
+ ++MCNumFixups;
+ encodeULEB128(uint64_t(MO.getImm()), OS);
} else {
encodeULEB128(uint64_t(MO.getImm()), OS);
}
@@ -107,14 +119,28 @@
support::endian::Writer<support::little>(OS).write<double>(d);
}
} else if (MO.isExpr()) {
+ const MCOperandInfo &Info = Desc.OpInfo[i];
+ llvm::MCFixupKind FixupKind;
+ size_t PaddedSize;
+ if (Info.OperandType == WebAssembly::OPERAND_I32IMM) {
+ FixupKind = MCFixupKind(WebAssembly::fixup_code_sleb128_i32);
+ PaddedSize = 5;
+ } else if (Info.OperandType == WebAssembly::OPERAND_I64IMM) {
+ FixupKind = MCFixupKind(WebAssembly::fixup_code_sleb128_i64);
+ PaddedSize = 10;
+ } else if (Info.OperandType == WebAssembly::OPERAND_FUNCTION32 ||
+ Info.OperandType == WebAssembly::OPERAND_OFFSET32 ||
+ Info.OperandType == WebAssembly::OPERAND_TYPEINDEX) {
+ FixupKind = MCFixupKind(WebAssembly::fixup_code_uleb128_i32);
+ PaddedSize = 5;
+ } else {
+ llvm_unreachable("unexpected symbolic operand kind");
+ }
Fixups.push_back(MCFixup::create(
OS.tell() - Start, MO.getExpr(),
- STI.getTargetTriple().isArch64Bit() ? FK_Data_8 : FK_Data_4,
- MI.getLoc()));
+ FixupKind, MI.getLoc()));
++MCNumFixups;
- encodeULEB128(STI.getTargetTriple().isArch64Bit() ? UINT64_MAX
- : uint64_t(UINT32_MAX),
- OS);
+ encodeULEB128(0, OS, PaddedSize - 1);
} else {
llvm_unreachable("unexpected operand kind");
}
diff --git a/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCTargetDesc.h b/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCTargetDesc.h
index 93ad574..1108f5f 100644
--- a/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCTargetDesc.h
+++ b/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCTargetDesc.h
@@ -68,7 +68,9 @@
/// p2align immediate for load and store address alignment.
OPERAND_P2ALIGN,
/// signature immediate for block/loop.
- OPERAND_SIGNATURE
+ OPERAND_SIGNATURE,
+ /// type signature immediate for call_indirect.
+ OPERAND_TYPEINDEX,
};
} // end namespace WebAssembly
diff --git a/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyTargetStreamer.cpp b/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyTargetStreamer.cpp
index b8a0f58..c198a26 100644
--- a/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyTargetStreamer.cpp
+++ b/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyTargetStreamer.cpp
@@ -22,8 +22,10 @@
#include "llvm/MC/MCSubtargetInfo.h"
#include "llvm/MC/MCSymbolELF.h"
#include "llvm/MC/MCSymbolWasm.h"
+#include "llvm/Support/Casting.h"
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/FormattedStream.h"
+#include "llvm/Support/Wasm.h"
using namespace llvm;
WebAssemblyTargetStreamer::WebAssemblyTargetStreamer(MCStreamer &S)
@@ -51,14 +53,28 @@
OS << '\n';
}
-void WebAssemblyTargetAsmStreamer::emitParam(ArrayRef<MVT> Types) {
- OS << "\t.param \t";
- PrintTypes(OS, Types);
+void WebAssemblyTargetAsmStreamer::emitParam(MCSymbol *Symbol,
+ ArrayRef<MVT> Types) {
+ if (!Types.empty()) {
+ OS << "\t.param \t";
+
+ // FIXME: Currently this applies to the "current" function; it may
+ // be cleaner to specify an explicit symbol as part of the directive.
+
+ PrintTypes(OS, Types);
+ }
}
-void WebAssemblyTargetAsmStreamer::emitResult(ArrayRef<MVT> Types) {
- OS << "\t.result \t";
- PrintTypes(OS, Types);
+void WebAssemblyTargetAsmStreamer::emitResult(MCSymbol *Symbol,
+ ArrayRef<MVT> Types) {
+ if (!Types.empty()) {
+ OS << "\t.result \t";
+
+ // FIXME: Currently this applies to the "current" function; it may
+ // be cleaner to specify an explicit symbol as part of the directive.
+
+ PrintTypes(OS, Types);
+ }
}
void WebAssemblyTargetAsmStreamer::emitLocal(ArrayRef<MVT> Types) {
@@ -92,11 +108,13 @@
OS << "\t.indidx \t" << *Value << '\n';
}
-void WebAssemblyTargetELFStreamer::emitParam(ArrayRef<MVT> Types) {
+void WebAssemblyTargetELFStreamer::emitParam(MCSymbol *Symbol,
+ ArrayRef<MVT> Types) {
// Nothing to emit; params are declared as part of the function signature.
}
-void WebAssemblyTargetELFStreamer::emitResult(ArrayRef<MVT> Types) {
+void WebAssemblyTargetELFStreamer::emitResult(MCSymbol *Symbol,
+ ArrayRef<MVT> Types) {
// Nothing to emit; results are declared as part of the function signature.
}
@@ -123,22 +141,52 @@
void WebAssemblyTargetELFStreamer::emitGlobalImport(StringRef name) {
}
-void WebAssemblyTargetWasmStreamer::emitParam(ArrayRef<MVT> Types) {
- // Nothing to emit; params are declared as part of the function signature.
+static unsigned MVT2WasmType(MVT Ty) {
+ switch (Ty.SimpleTy) {
+ case MVT::i32: return wasm::WASM_TYPE_I32;
+ case MVT::i64: return wasm::WASM_TYPE_I64;
+ case MVT::f32: return wasm::WASM_TYPE_F32;
+ case MVT::f64: return wasm::WASM_TYPE_F64;
+ default: llvm_unreachable("unsupported type");
+ }
}
-void WebAssemblyTargetWasmStreamer::emitResult(ArrayRef<MVT> Types) {
- // Nothing to emit; results are declared as part of the function signature.
+void WebAssemblyTargetWasmStreamer::emitParam(MCSymbol *Symbol,
+ ArrayRef<MVT> Types) {
+ SmallVector<unsigned, 4> Params;
+ for (MVT Ty : Types)
+ Params.push_back(MVT2WasmType(Ty));
+
+ cast<MCSymbolWasm>(Symbol)->setParams(std::move(Params));
+}
+
+void WebAssemblyTargetWasmStreamer::emitResult(MCSymbol *Symbol,
+ ArrayRef<MVT> Types) {
+ SmallVector<unsigned, 4> Returns;
+ for (MVT Ty : Types)
+ Returns.push_back(MVT2WasmType(Ty));
+
+ cast<MCSymbolWasm>(Symbol)->setReturns(std::move(Returns));
}
void WebAssemblyTargetWasmStreamer::emitLocal(ArrayRef<MVT> Types) {
- Streamer.EmitULEB128IntValue(Types.size());
- for (MVT Type : Types)
- Streamer.EmitIntValue(int64_t(WebAssembly::toValType(Type)), 1);
+ SmallVector<std::pair<MVT, uint32_t>, 4> Grouped;
+ for (MVT Type : Types) {
+ if (Grouped.empty() || Grouped.back().first != Type)
+ Grouped.push_back(std::make_pair(Type, 1));
+ else
+ ++Grouped.back().second;
+ }
+
+ Streamer.EmitULEB128IntValue(Grouped.size());
+ for (auto Pair : Grouped) {
+ Streamer.EmitULEB128IntValue(Pair.second);
+ Streamer.EmitULEB128IntValue(uint64_t(WebAssembly::toValType(Pair.first)));
+ }
}
void WebAssemblyTargetWasmStreamer::emitEndFunc() {
- Streamer.EmitIntValue(WebAssembly::End, 1);
+ llvm_unreachable(".end_func is not needed for direct wasm output");
}
void WebAssemblyTargetWasmStreamer::emitIndIdx(const MCExpr *Value) {
diff --git a/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyTargetStreamer.h b/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyTargetStreamer.h
index 35b14bc..1ad3ffc 100644
--- a/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyTargetStreamer.h
+++ b/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyTargetStreamer.h
@@ -31,9 +31,9 @@
explicit WebAssemblyTargetStreamer(MCStreamer &S);
/// .param
- virtual void emitParam(ArrayRef<MVT> Types) = 0;
+ virtual void emitParam(MCSymbol *Symbol, ArrayRef<MVT> Types) = 0;
/// .result
- virtual void emitResult(ArrayRef<MVT> Types) = 0;
+ virtual void emitResult(MCSymbol *Symbol, ArrayRef<MVT> Types) = 0;
/// .local
virtual void emitLocal(ArrayRef<MVT> Types) = 0;
/// .endfunc
@@ -57,8 +57,8 @@
public:
WebAssemblyTargetAsmStreamer(MCStreamer &S, formatted_raw_ostream &OS);
- void emitParam(ArrayRef<MVT> Types) override;
- void emitResult(ArrayRef<MVT> Types) override;
+ void emitParam(MCSymbol *Symbol, ArrayRef<MVT> Types) override;
+ void emitResult(MCSymbol *Symbol, ArrayRef<MVT> Types) override;
void emitLocal(ArrayRef<MVT> Types) override;
void emitEndFunc() override;
void emitIndirectFunctionType(StringRef name,
@@ -73,8 +73,8 @@
public:
explicit WebAssemblyTargetELFStreamer(MCStreamer &S);
- void emitParam(ArrayRef<MVT> Types) override;
- void emitResult(ArrayRef<MVT> Types) override;
+ void emitParam(MCSymbol *Symbol, ArrayRef<MVT> Types) override;
+ void emitResult(MCSymbol *Symbol, ArrayRef<MVT> Types) override;
void emitLocal(ArrayRef<MVT> Types) override;
void emitEndFunc() override;
void emitIndirectFunctionType(StringRef name,
@@ -89,8 +89,8 @@
public:
explicit WebAssemblyTargetWasmStreamer(MCStreamer &S);
- void emitParam(ArrayRef<MVT> Types) override;
- void emitResult(ArrayRef<MVT> Types) override;
+ void emitParam(MCSymbol *Symbol, ArrayRef<MVT> Types) override;
+ void emitResult(MCSymbol *Symbol, ArrayRef<MVT> Types) override;
void emitLocal(ArrayRef<MVT> Types) override;
void emitEndFunc() override;
void emitIndirectFunctionType(StringRef name,
diff --git a/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyWasmObjectWriter.cpp b/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyWasmObjectWriter.cpp
index 3fb3e65..2846ec5 100644
--- a/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyWasmObjectWriter.cpp
+++ b/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyWasmObjectWriter.cpp
@@ -14,9 +14,13 @@
//===----------------------------------------------------------------------===//
#include "MCTargetDesc/WebAssemblyMCTargetDesc.h"
+#include "MCTargetDesc/WebAssemblyFixupKinds.h"
#include "llvm/MC/MCFixup.h"
+#include "llvm/MC/MCSymbolWasm.h"
#include "llvm/MC/MCWasmObjectWriter.h"
+#include "llvm/Support/Casting.h"
#include "llvm/Support/ErrorHandling.h"
+#include "llvm/Support/Wasm.h"
using namespace llvm;
namespace {
@@ -33,12 +37,52 @@
WebAssemblyWasmObjectWriter::WebAssemblyWasmObjectWriter(bool Is64Bit)
: MCWasmObjectTargetWriter(Is64Bit) {}
+// Test whether the given expression computes a function address.
+static bool IsFunctionExpr(const MCExpr *Expr) {
+ if (const MCSymbolRefExpr *SyExp =
+ dyn_cast<MCSymbolRefExpr>(Expr))
+ return cast<MCSymbolWasm>(SyExp->getSymbol()).isFunction();
+
+ if (const MCBinaryExpr *BinOp =
+ dyn_cast<MCBinaryExpr>(Expr))
+ return IsFunctionExpr(BinOp->getLHS()) != IsFunctionExpr(BinOp->getRHS());
+
+ if (const MCUnaryExpr *UnOp =
+ dyn_cast<MCUnaryExpr>(Expr))
+ return IsFunctionExpr(UnOp->getSubExpr());
+
+ return false;
+}
+
unsigned WebAssemblyWasmObjectWriter::getRelocType(MCContext &Ctx,
const MCValue &Target,
const MCFixup &Fixup,
bool IsPCRel) const {
- llvm_unreachable("Relocations not yet implemented");
- return 0;
+ // WebAssembly functions are not allocated in the data address space. To
+ // resolve a pointer to a function, we must use a special relocation type.
+ bool IsFunction = IsFunctionExpr(Fixup.getValue());
+
+ assert(!IsPCRel);
+ switch (unsigned(Fixup.getKind())) {
+ case WebAssembly::fixup_code_sleb128_i32:
+ if (IsFunction)
+ return wasm::R_WEBASSEMBLY_TABLE_INDEX_SLEB;
+ return wasm::R_WEBASSEMBLY_GLOBAL_ADDR_SLEB;
+ case WebAssembly::fixup_code_sleb128_i64:
+ llvm_unreachable("fixup_sleb128_i64 not implemented yet");
+ case WebAssembly::fixup_code_uleb128_i32:
+ if (IsFunction)
+ return wasm::R_WEBASSEMBLY_FUNCTION_INDEX_LEB;
+ return wasm::R_WEBASSEMBLY_GLOBAL_ADDR_LEB;
+ case FK_Data_4:
+ if (IsFunction)
+ return wasm::R_WEBASSEMBLY_TABLE_INDEX_I32;
+ return wasm::R_WEBASSEMBLY_GLOBAL_ADDR_I32;
+ case FK_Data_8:
+ llvm_unreachable("FK_Data_8 not implemented yet");
+ default:
+ llvm_unreachable("unimplemented fixup kind");
+ }
}
MCObjectWriter *llvm::createWebAssemblyWasmObjectWriter(raw_pwrite_stream &OS,