(Desc.TSFlags & WebAssemblyII::VariableOpIsImmediate)) &&
"WebAssemblyII::VariableOpIsImmediate should be set for "
"variable_ops immediate ops");
-
- if (Desc.TSFlags & WebAssemblyII::VariableOpImmediateIsType) {
- switch (Op.getImm()) {
- case int64_t(WebAssembly::ValType::I32): O << "i32"; break;
- case int64_t(WebAssembly::ValType::I64): O << "i64"; break;
- case int64_t(WebAssembly::ValType::F32): O << "f32"; break;
- case int64_t(WebAssembly::ValType::F64): O << "f64"; break;
- default: llvm_unreachable("unknown local type");
- }
- } else {
- // TODO: (MII.get(MI->getOpcode()).TSFlags &
- // WebAssemblyII::VariableOpImmediateIsLabel)
- // can tell us whether this is an immediate referencing a label in the
- // control flow stack, and it may be nice to pretty-print.
- O << Op.getImm();
- }
+ // TODO: (MII.get(MI->getOpcode()).TSFlags &
+ // WebAssemblyII::VariableOpImmediateIsLabel)
+ // can tell us whether this is an immediate referencing a label in the
+ // control flow stack, and it may be nice to pretty-print.
+ O << Op.getImm();
} else if (Op.isFPImm()) {
const MCInstrDesc &Desc = MII.get(MI->getOpcode());
assert(OpNo < Desc.getNumOperands() &&
case WebAssembly::ExprType::I8x16: O << "i8x16"; break;
case WebAssembly::ExprType::I16x8: O << "i16x8"; break;
case WebAssembly::ExprType::I32x4: O << "i32x4"; break;
- case WebAssembly::ExprType::I64x2: O << "i32x4"; break;
case WebAssembly::ExprType::F32x4: O << "f32x4"; break;
- case WebAssembly::ExprType::F64x2: O << "f64x2"; break;
+ case WebAssembly::ExprType::B8x16: O << "b8x16"; break;
+ case WebAssembly::ExprType::B16x8: O << "b16x8"; break;
+ case WebAssembly::ExprType::B32x4: O << "b32x4"; break;
}
}
if (Count == 0)
return true;
- // FIXME: Do something.
- return false;
+ for (uint64_t i = 0; i < Count; ++i)
+ OW->write8(WebAssembly::Nop);
+
+ return true;
}
void WebAssemblyAsmBackend::applyFixup(const MCFixup &Fixup, char *Data,
uint64_t Start = OS.tell();
uint64_t Binary = getBinaryCodeForInstr(MI, Fixups, STI);
- encodeULEB128(Binary, OS);
+ assert(Binary < UINT8_MAX && "Multi-byte opcodes not supported yet");
+ OS << uint8_t(Binary);
const MCInstrDesc &Desc = MCII.get(MI.getOpcode());
for (unsigned i = 0, e = MI.getNumOperands(); i < e; ++i) {
if (MO.isReg()) {
/* nothing to encode */
} else if (MO.isImm()) {
- assert(i < Desc.getNumOperands() &&
- "Unexpected integer immediate as a non-fixed operand");
- assert(Desc.TSFlags == 0 &&
- "WebAssembly variable_ops integer ops don't use TSFlags");
- const MCOperandInfo &Info = Desc.OpInfo[i];
- if (Info.OperandType == WebAssembly::OPERAND_I32IMM) {
- encodeSLEB128(int32_t(MO.getImm()), OS);
- } else if (Info.OperandType == WebAssembly::OPERAND_I64IMM) {
- encodeSLEB128(int64_t(MO.getImm()), OS);
+ if (i < Desc.getNumOperands()) {
+ assert(Desc.TSFlags == 0 &&
+ "WebAssembly non-variable_ops don't use TSFlags");
+ const MCOperandInfo &Info = Desc.OpInfo[i];
+ if (Info.OperandType == WebAssembly::OPERAND_I32IMM) {
+ encodeSLEB128(int32_t(MO.getImm()), OS);
+ } else if (Info.OperandType == WebAssembly::OPERAND_I64IMM) {
+ encodeSLEB128(int64_t(MO.getImm()), OS);
+ } else {
+ encodeULEB128(uint64_t(MO.getImm()), OS);
+ }
} else {
+ assert(Desc.TSFlags == (WebAssemblyII::VariableOpIsImmediate |
+ WebAssemblyII::VariableOpImmediateIsLabel));
encodeULEB128(uint64_t(MO.getImm()), OS);
}
} else if (MO.isFPImm()) {
TargetRegistry::RegisterAsmTargetStreamer(*T, createAsmTargetStreamer);
}
}
+
+WebAssembly::ValType WebAssembly::toValType(const MVT &Ty) {
+ switch (Ty.SimpleTy) {
+ case MVT::i32: return WebAssembly::ValType::I32;
+ case MVT::i64: return WebAssembly::ValType::I64;
+ case MVT::f32: return WebAssembly::ValType::F32;
+ case MVT::f64: return WebAssembly::ValType::F64;
+ default: llvm_unreachable("unexpected type");
+ }
+}
class MCInstrInfo;
class MCObjectWriter;
class MCSubtargetInfo;
+class MVT;
class Target;
class Triple;
class raw_pwrite_stream;
/// signature immediate for block/loop.
OPERAND_SIGNATURE
};
-
-/// WebAssembly-specific directive identifiers.
-enum Directive {
- // FIXME: This is not the real binary encoding.
- DotParam = UINT64_MAX - 0, ///< .param
- DotResult = UINT64_MAX - 1, ///< .result
- DotLocal = UINT64_MAX - 2, ///< .local
- DotEndFunc = UINT64_MAX - 3, ///< .endfunc
- DotIndIdx = UINT64_MAX - 4, ///< .indidx
-};
-
} // end namespace WebAssembly
namespace WebAssemblyII {
VariableOpIsImmediate = (1 << 0),
// For immediate values in the variable_ops range, this flag indicates
// whether the value represents a control-flow label.
- VariableOpImmediateIsLabel = (1 << 1),
- // For immediate values in the variable_ops range, this flag indicates
- // whether the value represents a ValType.
- VariableOpImmediateIsType = (1 << 2),
+ VariableOpImmediateIsLabel = (1 << 1)
};
} // end namespace WebAssemblyII
/// This is used to indicate block signatures.
enum class ExprType {
- Void = 0,
- I32 = 1,
- I64 = 2,
- F32 = 3,
- F64 = 4,
- I8x16 = 5,
- I16x8 = 6,
- I32x4 = 7,
- I64x2 = 8,
- F32x4 = 9,
- F64x2 = 10
+ Void = 0x40,
+ I32 = 0x7f,
+ I64 = 0x7e,
+ F32 = 0x7d,
+ F64 = 0x7c,
+ I8x16 = 0x7b,
+ I16x8 = 0x7a,
+ I32x4 = 0x79,
+ F32x4 = 0x78,
+ B8x16 = 0x77,
+ B16x8 = 0x76,
+ B32x4 = 0x75
};
/// This is used to indicate local types.
enum class ValType {
- I32 = 1,
- I64 = 2,
- F32 = 3,
- F64 = 4,
- I8x16 = 5,
- I16x8 = 6,
- I32x4 = 7,
- I64x2 = 8,
- F32x4 = 9,
- F64x2 = 10
+ I32 = 0x7f,
+ I64 = 0x7e,
+ F32 = 0x7d,
+ F64 = 0x7c,
+ I8x16 = 0x7b,
+ I16x8 = 0x7a,
+ I32x4 = 0x79,
+ F32x4 = 0x78,
+ B8x16 = 0x77,
+ B16x8 = 0x76,
+ B32x4 = 0x75
};
+/// Instruction opcodes emitted via means other than CodeGen.
+static const unsigned Nop = 0x01;
+static const unsigned End = 0x0b;
+
+ValType toValType(const MVT &Ty);
+
} // end namespace WebAssembly
} // end namespace llvm
}
void WebAssemblyTargetAsmStreamer::emitLocal(ArrayRef<MVT> Types) {
- OS << "\t.local \t";
- PrintTypes(OS, Types);
+ if (!Types.empty()) {
+ OS << "\t.local \t";
+ PrintTypes(OS, Types);
+ }
}
void WebAssemblyTargetAsmStreamer::emitEndFunc() { OS << "\t.endfunc\n"; }
OS << "\t.indidx \t" << *Value << '\n';
}
-// FIXME: What follows is not the real binary encoding.
-
-static void EncodeTypes(MCStreamer &Streamer, ArrayRef<MVT> Types) {
- Streamer.EmitIntValue(Types.size(), sizeof(uint64_t));
- for (MVT Type : Types)
- Streamer.EmitIntValue(Type.SimpleTy, sizeof(uint64_t));
-}
-
void WebAssemblyTargetELFStreamer::emitParam(ArrayRef<MVT> Types) {
- Streamer.EmitIntValue(WebAssembly::DotParam, sizeof(uint64_t));
- EncodeTypes(Streamer, Types);
+ // Nothing to emit; params are declared as part of the function signature.
}
void WebAssemblyTargetELFStreamer::emitResult(ArrayRef<MVT> Types) {
- Streamer.EmitIntValue(WebAssembly::DotResult, sizeof(uint64_t));
- EncodeTypes(Streamer, Types);
+ // Nothing to emit; results are declared as part of the function signature.
}
void WebAssemblyTargetELFStreamer::emitLocal(ArrayRef<MVT> Types) {
- Streamer.EmitIntValue(WebAssembly::DotLocal, sizeof(uint64_t));
- EncodeTypes(Streamer, Types);
+ Streamer.EmitULEB128IntValue(Types.size());
+ for (MVT Type : Types)
+ Streamer.EmitIntValue(int64_t(WebAssembly::toValType(Type)), 1);
}
void WebAssemblyTargetELFStreamer::emitEndFunc() {
- Streamer.EmitIntValue(WebAssembly::DotEndFunc, sizeof(uint64_t));
+ Streamer.EmitIntValue(WebAssembly::End, 1);
}
void WebAssemblyTargetELFStreamer::emitIndIdx(const MCExpr *Value) {
- Streamer.EmitIntValue(WebAssembly::DotIndIdx, sizeof(uint64_t));
- Streamer.EmitValue(Value, sizeof(uint64_t));
+ llvm_unreachable(".indidx encoding not yet implemented");
+}
+
+void WebAssemblyTargetELFStreamer::emitIndirectFunctionType(
+ StringRef name, SmallVectorImpl<MVT> &Params, SmallVectorImpl<MVT> &Results) {
+ // Nothing to emit here. TODO: Re-design how linking works and re-evaluate
+ // whether it's necessary for .o files to declare indirect function types.
}
void emitResult(ArrayRef<MVT> Types) override;
void emitLocal(ArrayRef<MVT> Types) override;
void emitEndFunc() override;
+ void emitIndirectFunctionType(StringRef name,
+ SmallVectorImpl<MVT> &Params,
+ SmallVectorImpl<MVT> &Results) override;
void emitIndIdx(const MCExpr *Value) override;
};
class WebAssemblyAsmPrinter final : public AsmPrinter {
const MachineRegisterInfo *MRI;
- const WebAssemblyFunctionInfo *MFI;
+ WebAssemblyFunctionInfo *MFI;
public:
WebAssemblyAsmPrinter(TargetMachine &TM, std::unique_ptr<MCStreamer> Streamer)
if (ResultVTs.size() == 1)
getTargetStreamer()->emitResult(ResultVTs);
- bool AnyWARegs = false;
- SmallVector<MVT, 16> LocalTypes;
+ // FIXME: When ExplicitLocals is enabled by default, we won't need
+ // to define the locals here (and MFI can go back to being pointer-to-const).
for (unsigned Idx = 0, IdxE = MRI->getNumVirtRegs(); Idx != IdxE; ++Idx) {
unsigned VReg = TargetRegisterInfo::index2VirtReg(Idx);
unsigned WAReg = MFI->getWAReg(VReg);
// Don't declare stackified registers.
if (int(WAReg) < 0)
continue;
- LocalTypes.push_back(getRegType(VReg));
- AnyWARegs = true;
+ MFI->addLocal(getRegType(VReg));
}
- if (AnyWARegs)
- getTargetStreamer()->emitLocal(LocalTypes);
+
+ getTargetStreamer()->emitLocal(MFI->getLocals());
AsmPrinter::EmitFunctionBodyStart();
}
case MVT::v16i8: retType = WebAssembly::ExprType::I8x16; break;
case MVT::v8i16: retType = WebAssembly::ExprType::I16x8; break;
case MVT::v4i32: retType = WebAssembly::ExprType::I32x4; break;
- case MVT::v2i64: retType = WebAssembly::ExprType::I64x2; break;
case MVT::v4f32: retType = WebAssembly::ExprType::F32x4; break;
- case MVT::v2f64: retType = WebAssembly::ExprType::F64x2; break;
default: llvm_unreachable("unexpected return type");
}
}
/// Get the type associated with the given register class.
-static WebAssembly::ValType typeForRegClass(const TargetRegisterClass *RC) {
+static MVT typeForRegClass(const TargetRegisterClass *RC) {
if (RC == &WebAssembly::I32RegClass)
- return WebAssembly::ValType::I32;
+ return MVT::i32;
if (RC == &WebAssembly::I64RegClass)
- return WebAssembly::ValType::I64;
+ return MVT::i64;
if (RC == &WebAssembly::F32RegClass)
- return WebAssembly::ValType::F32;
+ return MVT::f32;
if (RC == &WebAssembly::F64RegClass)
- return WebAssembly::ValType::F64;
+ return MVT::f64;
llvm_unreachable("unrecognized register class");
}
}
}
- // Insert a .locals directive to declare the locals.
- MachineInstrBuilder DeclareLocals;
+ // Define the locals.
for (size_t i = 0, e = MRI.getNumVirtRegs(); i < e; ++i) {
unsigned Reg = TargetRegisterInfo::index2VirtReg(i);
auto I = Reg2Local.find(Reg);
if (I == Reg2Local.end() || I->second < MFI.getParams().size())
continue;
- if (!DeclareLocals) {
- DeclareLocals = BuildMI(*MF.begin(), MF.begin()->begin(), DebugLoc(),
- TII->get(WebAssembly::DECLARE_LOCALS));
- Changed = true;
- }
-
- DeclareLocals.addImm(int64_t(typeForRegClass(MRI.getRegClass(Reg))));
+ MFI.addLocal(typeForRegClass(MRI.getRegClass(Reg)));
+ Changed = true;
}
#ifndef NDEBUG
multiclass CALL<WebAssemblyRegClass vt, string prefix> {
def CALL_#vt : I<(outs vt:$dst), (ins i32imm:$callee, variable_ops),
[(set vt:$dst, (WebAssemblycall1 (i32 imm:$callee)))],
- !strconcat(prefix, "call\t$dst, $callee")>;
+ !strconcat(prefix, "call\t$dst, $callee"),
+ 0x10>;
let isCodeGenOnly = 1 in {
def PCALL_INDIRECT_#vt : I<(outs vt:$dst), (ins I32:$callee, variable_ops),
[(set vt:$dst, (WebAssemblycall1 I32:$callee))],
} // isCodeGenOnly = 1
def CALL_INDIRECT_#vt : I<(outs vt:$dst), (ins variable_ops),
[],
- !strconcat(prefix, "call_indirect\t$dst")>;
+ !strconcat(prefix, "call_indirect\t$dst"),
+ 0x11>;
}
multiclass SIMD_CALL<ValueType vt, string prefix> {
def CALL_#vt : SIMD_I<(outs V128:$dst), (ins i32imm:$callee, variable_ops),
[(set (vt V128:$dst),
(WebAssemblycall1 (i32 imm:$callee)))],
- !strconcat(prefix, "call\t$dst, $callee")>;
+ !strconcat(prefix, "call\t$dst, $callee"),
+ 0x10>;
let isCodeGenOnly = 1 in {
def PCALL_INDIRECT_#vt : SIMD_I<(outs V128:$dst),
(ins I32:$callee, variable_ops),
def CALL_INDIRECT_#vt : SIMD_I<(outs V128:$dst),
(ins variable_ops),
[],
- !strconcat(prefix, "call_indirect\t$dst")>;
+ !strconcat(prefix, "call_indirect\t$dst"),
+ 0x11>;
}
let Uses = [SP32, SP64], isCall = 1 in {
def CALL_VOID : I<(outs), (ins i32imm:$callee, variable_ops),
[(WebAssemblycall0 (i32 imm:$callee))],
- "call \t$callee">;
+ "call \t$callee", 0x10>;
let isCodeGenOnly = 1 in {
def PCALL_INDIRECT_VOID : I<(outs), (ins I32:$callee, variable_ops),
[(WebAssemblycall0 I32:$callee)],
} // isCodeGenOnly = 1
def CALL_INDIRECT_VOID : I<(outs), (ins variable_ops),
[],
- "call_indirect\t">;
+ "call_indirect\t", 0x11>;
} // Uses = [SP32,SP64], isCall = 1
} // Defs = [ARGUMENTS]
def LOOP : I<(outs), (ins Signature:$sig), [], "loop \t$sig", 0x03>;
// END_BLOCK and END_LOOP are represented with the same opcode in wasm.
-def END_BLOCK : I<(outs), (ins), [], "end_block", 0x0f>;
-def END_LOOP : I<(outs), (ins), [], "end_loop", 0x0f>;
+def END_BLOCK : I<(outs), (ins), [], "end_block", 0x0b>;
+def END_LOOP : I<(outs), (ins), [], "end_loop", 0x0b>;
} // Uses = [VALUE_STACK], Defs = [VALUE_STACK]
multiclass RETURN<WebAssemblyRegClass vt> {
def RETURN_#vt : I<(outs), (ins vt:$val), [(WebAssemblyreturn vt:$val)],
- "return \t$val">;
+ "return \t$val", 0x0f>;
// Equivalent to RETURN_#vt, for use at the end of a function when wasm
// semantics return by falling off the end of the block.
let isCodeGenOnly = 1 in
multiclass SIMD_RETURN<ValueType vt> {
def RETURN_#vt : SIMD_I<(outs), (ins V128:$val),
[(WebAssemblyreturn (vt V128:$val))],
- "return \t$val">;
+ "return \t$val", 0x0f>;
// Equivalent to RETURN_#vt, for use at the end of a function when wasm
// semantics return by falling off the end of the block.
let isCodeGenOnly = 1 in
defm : SIMD_RETURN<v4i32>;
defm : SIMD_RETURN<v4f32>;
- def RETURN_VOID : I<(outs), (ins), [(WebAssemblyreturn)], "return">;
+ def RETURN_VOID : I<(outs), (ins), [(WebAssemblyreturn)], "return", 0x0f>;
// This is to RETURN_VOID what FALLTHROUGH_RETURN_#vt is to RETURN_#vt.
let isCodeGenOnly = 1 in
def FALLTHROUGH_RETURN_VOID : I<(outs), (ins), []>;
} // isReturn = 1
-def UNREACHABLE : I<(outs), (ins), [(trap)], "unreachable">;
+def UNREACHABLE : I<(outs), (ins), [(trap)], "unreachable", 0x00>;
} // isTerminator = 1, hasCtrlDep = 1, isBarrier = 1
def SELECT_F32 : I<(outs F32:$dst), (ins F32:$lhs, F32:$rhs, I32:$cond),
[(set F32:$dst, (select I32:$cond, F32:$lhs, F32:$rhs))],
- "f32.select\t$dst, $lhs, $rhs, $cond">;
+ "f32.select\t$dst, $lhs, $rhs, $cond", 0x1b>;
def SELECT_F64 : I<(outs F64:$dst), (ins F64:$lhs, F64:$rhs, I32:$cond),
[(set F64:$dst, (select I32:$cond, F64:$lhs, F64:$rhs))],
- "f64.select\t$dst, $lhs, $rhs, $cond">;
+ "f64.select\t$dst, $lhs, $rhs, $cond", 0x1b>;
} // Defs = [ARGUMENTS]
defm : LOCAL<F64>;
defm : LOCAL<V128>, Requires<[HasSIMD128]>;
-// Set TSFlags{0} to 1 to indicate that the variable_ops are immediates.
-// Set TSFlags{2} to 1 to indicate that the immediates are ValTypes.
-def DECLARE_LOCALS : I<(outs), (ins variable_ops), [], ".local \t"> {
- let TSFlags{0} = 1;
- let TSFlags{2} = 1;
-}
-
let isMoveImm = 1, isAsCheapAsAMove = 1, isReMaterializable = 1 in {
def CONST_I32 : I<(outs I32:$res), (ins i32imm_op:$imm),
[(set I32:$res, imm:$imm)],
def SELECT_I32 : I<(outs I32:$dst), (ins I32:$lhs, I32:$rhs, I32:$cond),
[(set I32:$dst, (select I32:$cond, I32:$lhs, I32:$rhs))],
- "i32.select\t$dst, $lhs, $rhs, $cond">;
+ "i32.select\t$dst, $lhs, $rhs, $cond", 0x1b>;
def SELECT_I64 : I<(outs I64:$dst), (ins I64:$lhs, I64:$rhs, I32:$cond),
[(set I64:$dst, (select I32:$cond, I64:$lhs, I64:$rhs))],
- "i64.select\t$dst, $lhs, $rhs, $cond">;
+ "i64.select\t$dst, $lhs, $rhs, $cond", 0x1b>;
} // Defs = [ARGUMENTS]
std::vector<MVT> Params;
std::vector<MVT> Results;
+ std::vector<MVT> Locals;
/// A mapping from CodeGen vreg index to WebAssembly register number.
std::vector<unsigned> WARegs;
void addResult(MVT VT) { Results.push_back(VT); }
const std::vector<MVT> &getResults() const { return Results; }
+ void addLocal(MVT VT) { Locals.push_back(VT); }
+ const std::vector<MVT> &getLocals() const { return Locals; }
+
unsigned getVarargBufferVreg() const {
assert(VarargVreg != -1U && "Vararg vreg hasn't been set");
return VarargVreg;