case MVT::v16i8: return WebAssembly::V128_0;
case MVT::v8i16: return WebAssembly::V128_0;
case MVT::v4i32: return WebAssembly::V128_0;
+ case MVT::v2i64: return WebAssembly::V128_0;
case MVT::v4f32: return WebAssembly::V128_0;
+ case MVT::v2f64: return WebAssembly::V128_0;
default: return MVT::INVALID_SIMPLE_VALUE_TYPE;
}
}
const MCInstrInfo &mii, const MCTargetOptions &Options)
: MCTargetAsmParser(Options, sti, mii), Parser(Parser),
Lexer(Parser.getLexer()), LastLabel(nullptr) {
+ setAvailableFeatures(ComputeAvailableFeatures(sti.getFeatureBits()));
}
#define GET_ASSEMBLER_HEADER
.Case("i8x16", MVT::v16i8)
.Case("i16x8", MVT::v8i16)
.Case("i32x4", MVT::v4i32)
+ .Case("i64x2", MVT::v2i64)
.Case("f32x4", MVT::v4f32)
+ .Case("f64x2", MVT::v2f64)
+ // arbitrarily chosen vector type to associate with "v128"
+ // FIXME: should these be EVTs to avoid this arbitrary hack? Do we want
+ // to accept more specific SIMD register types?
+ .Case("v128", MVT::v16i8)
.Default(MVT::INVALID_SIMPLE_VALUE_TYPE);
}
}
template <typename T>
-bool parseFPImmediate(MCInst &MI, uint64_t &Size, ArrayRef<uint8_t> Bytes) {
+bool parseImmediate(MCInst &MI, uint64_t &Size, ArrayRef<uint8_t> Bytes) {
if (Size + sizeof(T) > Bytes.size())
return false;
T Val;
memcpy(&Val, Bytes.data() + Size, sizeof(T));
support::endian::byte_swap<T, support::endianness::little>(Val);
Size += sizeof(T);
- MI.addOperand(MCOperand::createFPImm(static_cast<double>(Val)));
+ if (std::is_floating_point<T>::value) {
+ MI.addOperand(MCOperand::createFPImm(static_cast<double>(Val)));
+ } else {
+ MI.addOperand(MCOperand::createImm(static_cast<int64_t>(Val)));
+ }
return true;
}
}
// FP operands.
case WebAssembly::OPERAND_F32IMM: {
- if (!parseFPImmediate<float>(MI, Size, Bytes))
+ if (!parseImmediate<float>(MI, Size, Bytes))
return MCDisassembler::Fail;
break;
}
case WebAssembly::OPERAND_F64IMM: {
- if (!parseFPImmediate<double>(MI, Size, Bytes))
+ if (!parseImmediate<double>(MI, Size, Bytes))
+ return MCDisassembler::Fail;
+ break;
+ }
+ // Vector lane operands (not LEB encoded).
+ case WebAssembly::OPERAND_VEC_I8IMM: {
+ if (!parseImmediate<uint8_t>(MI, Size, Bytes))
+ return MCDisassembler::Fail;
+ break;
+ }
+ case WebAssembly::OPERAND_VEC_I16IMM: {
+ if (!parseImmediate<uint16_t>(MI, Size, Bytes))
+ return MCDisassembler::Fail;
+ break;
+ }
+ case WebAssembly::OPERAND_VEC_I32IMM: {
+ if (!parseImmediate<uint32_t>(MI, Size, Bytes))
+ return MCDisassembler::Fail;
+ break;
+ }
+ case WebAssembly::OPERAND_VEC_I64IMM: {
+ if (!parseImmediate<uint64_t>(MI, Size, Bytes))
return MCDisassembler::Fail;
break;
}
const MCOperandInfo &Info = Desc.OpInfo[i];
LLVM_DEBUG(dbgs() << "Encoding immediate: type="
<< int(Info.OperandType) << "\n");
- if (Info.OperandType == WebAssembly::OPERAND_I32IMM) {
+ switch (Info.OperandType) {
+ case WebAssembly::OPERAND_I32IMM:
encodeSLEB128(int32_t(MO.getImm()), OS);
- } else if (Info.OperandType == WebAssembly::OPERAND_OFFSET32) {
+ break;
+ case WebAssembly::OPERAND_OFFSET32:
encodeULEB128(uint32_t(MO.getImm()), OS);
- } else if (Info.OperandType == WebAssembly::OPERAND_I64IMM) {
+ break;
+ case WebAssembly::OPERAND_I64IMM:
encodeSLEB128(int64_t(MO.getImm()), OS);
- } else if (Info.OperandType == WebAssembly::OPERAND_GLOBAL) {
- llvm_unreachable("wasm globals should only be accessed symbolicly");
- } else if (Info.OperandType == WebAssembly::OPERAND_SIGNATURE) {
+ break;
+ case WebAssembly::OPERAND_SIGNATURE:
OS << uint8_t(MO.getImm());
- } else {
+ break;
+ case WebAssembly::OPERAND_VEC_I8IMM:
+ support::endian::write<uint8_t>(OS, MO.getImm(), support::little);
+ break;
+ case WebAssembly::OPERAND_VEC_I16IMM:
+ support::endian::write<uint16_t>(OS, MO.getImm(), support::little);
+ break;
+ case WebAssembly::OPERAND_VEC_I32IMM:
+ support::endian::write<uint32_t>(OS, MO.getImm(), support::little);
+ break;
+ case WebAssembly::OPERAND_VEC_I64IMM:
+ support::endian::write<uint64_t>(OS, MO.getImm(), support::little);
+ break;
+ case WebAssembly::OPERAND_GLOBAL:
+ llvm_unreachable("wasm globals should only be accessed symbolicly");
+ default:
encodeULEB128(uint64_t(MO.getImm()), OS);
}
} else {
OPERAND_F32IMM,
/// 64-bit floating-point immediates.
OPERAND_F64IMM,
+ /// 8-bit vector lane immediate
+ OPERAND_VEC_I8IMM,
+ /// 16-bit vector lane immediate
+ OPERAND_VEC_I16IMM,
+ /// 32-bit vector lane immediate
+ OPERAND_VEC_I32IMM,
+ /// 64-bit vector lane immediate
+ OPERAND_VEC_I64IMM,
/// 32-bit unsigned function indices.
OPERAND_FUNCTION32,
/// 32-bit unsigned memory offsets.
let OperandType = "OPERAND_F64IMM" in
def f64imm_op : Operand<f64>;
+let OperandType = "OPERAND_VEC_I8IMM" in
+def vec_i8imm_op : Operand<i32>;
+
+let OperandType = "OPERAND_VEC_I16IMM" in
+def vec_i16imm_op : Operand<i32>;
+
+let OperandType = "OPERAND_VEC_I32IMM" in
+def vec_i32imm_op : Operand<i32>;
+
+let OperandType = "OPERAND_VEC_I64IMM" in
+def vec_i64imm_op : Operand<i64>;
+
let OperandType = "OPERAND_FUNCTION32" in
def function32_op : Operand<i32>;
///
//===----------------------------------------------------------------------===//
-// immediate argument types
-def ImmByte : ImmLeaf<i32, [{ return 0 <= Imm && Imm < 256; }]>;
+// constrained immediate argument types
+foreach SIZE = [8, 16] in
+def ImmI#SIZE : ImmLeaf<i32, "return (Imm & ((1UL << "#SIZE#") - 1)) == Imm;">;
foreach SIZE = [2, 4, 8, 16, 32] in
def LaneIdx#SIZE : ImmLeaf<i32, "return 0 <= Imm && Imm < "#SIZE#";">;
+// const vectors
+multiclass ConstVec<ValueType vec_t, dag ops, dag pat, string args> {
+ defm CONST_V128_#vec_t : SIMD_I<(outs V128:$dst), ops, (outs), ops,
+ [(set V128:$dst, (vec_t pat))],
+ "v128.const\t$dst, "#args,
+ "v128.const\t"#args, 0>;
+}
+defm "" : ConstVec<v16i8,
+ (ins vec_i8imm_op:$i0, vec_i8imm_op:$i1,
+ vec_i8imm_op:$i2, vec_i8imm_op:$i3,
+ vec_i8imm_op:$i4, vec_i8imm_op:$i5,
+ vec_i8imm_op:$i6, vec_i8imm_op:$i7,
+ vec_i8imm_op:$i8, vec_i8imm_op:$i9,
+ vec_i8imm_op:$iA, vec_i8imm_op:$iB,
+ vec_i8imm_op:$iC, vec_i8imm_op:$iD,
+ vec_i8imm_op:$iE, vec_i8imm_op:$iF),
+ (build_vector ImmI8:$i0, ImmI8:$i1, ImmI8:$i2, ImmI8:$i3,
+ ImmI8:$i4, ImmI8:$i5, ImmI8:$i6, ImmI8:$i7,
+ ImmI8:$i8, ImmI8:$i9, ImmI8:$iA, ImmI8:$iB,
+ ImmI8:$iC, ImmI8:$iD, ImmI8:$iE, ImmI8:$iF),
+ !strconcat("$i0, $i1, $i2, $i3, $i4, $i5, $i6, $i7, ",
+ "$i8, $i9, $iA, $iB, $iC, $iD, $iE, $iF")>;
+defm "" : ConstVec<v8i16,
+ (ins vec_i16imm_op:$i0, vec_i16imm_op:$i1,
+ vec_i16imm_op:$i2, vec_i16imm_op:$i3,
+ vec_i16imm_op:$i4, vec_i16imm_op:$i5,
+ vec_i16imm_op:$i6, vec_i16imm_op:$i7),
+ (build_vector
+ ImmI16:$i0, ImmI16:$i1, ImmI16:$i2, ImmI16:$i3,
+ ImmI16:$i4, ImmI16:$i5, ImmI16:$i6, ImmI16:$i7),
+ "$i0, $i1, $i2, $i3, $i4, $i5, $i6, $i7">;
+defm "" : ConstVec<v4i32,
+ (ins vec_i32imm_op:$i0, vec_i32imm_op:$i1,
+ vec_i32imm_op:$i2, vec_i32imm_op:$i3),
+ (build_vector (i32 imm:$i0), (i32 imm:$i1),
+ (i32 imm:$i2), (i32 imm:$i3)),
+ "$i0, $i1, $i2, $i3">;
+defm "" : ConstVec<v2i64,
+ (ins vec_i64imm_op:$i0, vec_i64imm_op:$i1),
+ (build_vector (i64 imm:$i0), (i64 imm:$i1)),
+ "$i0, $i1">;
+defm "" : ConstVec<v4f32,
+ (ins f32imm_op:$i0, f32imm_op:$i1,
+ f32imm_op:$i2, f32imm_op:$i3),
+ (build_vector (f32 fpimm:$i0), (f32 fpimm:$i1),
+ (f32 fpimm:$i2), (f32 fpimm:$i3)),
+ "$i0, $i1, $i2, $i3">;
+defm "" : ConstVec<v2f64,
+ (ins f64imm_op:$i0, f64imm_op:$i1),
+ (build_vector (f64 fpimm:$i0), (f64 fpimm:$i1)),
+ "$i0, $i1">;
+
// lane extraction
multiclass ExtractLane<ValueType vec_t, string vec, ImmLeaf imm_t,
WebAssemblyRegClass reg_t, bits<32> simdop,
string suffix = "", SDNode extract = vector_extract> {
defm EXTRACT_LANE_#vec_t#suffix :
- SIMD_I<(outs reg_t:$dst), (ins V128:$vec, I32:$idx),
- (outs), (ins I32:$idx),
+ SIMD_I<(outs reg_t:$dst), (ins V128:$vec, i32imm_op:$idx),
+ (outs), (ins i32imm_op:$idx),
[(set reg_t:$dst, (extract (vec_t V128:$vec), (i32 imm_t:$idx)))],
vec#".extract_lane"#suffix#"\t$dst, $vec, $idx",
vec#".extract_lane"#suffix#"\t$idx", simdop>;
multiclass ReplaceLane<ValueType vec_t, string vec, WebAssemblyRegClass reg_t,
ValueType lane_t, ImmLeaf imm_t, bits<32> simdop> {
defm REPLACE_LANE_#vec_t :
- SIMD_I<(outs V128:$dst), (ins V128:$vec, I32:$idx, reg_t:$x),
- (outs), (ins I32:$idx),
+ SIMD_I<(outs V128:$dst), (ins V128:$vec, i32imm_op:$idx, reg_t:$x),
+ (outs), (ins i32imm_op:$idx),
[(set V128:$dst, (vector_insert
(vec_t V128:$vec), (lane_t reg_t:$x), (i32 imm_t:$idx)))],
vec#".replace_lane\t$dst, $vec, $idx, $x",
; ==============================================================================
; 16 x i8
; ==============================================================================
+; CHECK-LABEL: const_v16i8:
+; NO-SIMD128-NOT: i8x16
+; SIMD128: .result v128{{$}}
+; SIMD128: v128.const $push0=,
+; SIMD128-SAME: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15
+; SIMD128-SAME: # encoding: [0xfd,0x00,
+; SIMD128-SAME: 0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,
+; SIMD128-SAME: 0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f]{{$}}
+define <16 x i8> @const_v16i8() {
+ ret <16 x i8> <i8 00, i8 01, i8 02, i8 03, i8 04, i8 05, i8 06, i8 07,
+ i8 08, i8 09, i8 10, i8 11, i8 12, i8 13, i8 14, i8 15>
+}
+
; CHECK-LABEL: splat_v16i8:
; NO-SIMD128-NOT: i8x16
; SIMD128: .param i32{{$}}
; ==============================================================================
; 8 x i16
; ==============================================================================
+; CHECK-LABEL: const_v8i16:
+; NO-SIMD128-NOT: i16x8
+; SIMD128: .result v128{{$}}
+; SIMD128: v128.const $push0=, 256, 770, 1284, 1798, 2312, 2826, 3340, 3854
+; SIMD128-SAME: # encoding: [0xfd,0x00,
+; SIMD128-SAME: 0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,
+; SIMD128-SAME: 0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f]{{$}}
+define <8 x i16> @const_v8i16() {
+ ret <8 x i16> <i16 256, i16 770, i16 1284, i16 1798,
+ i16 2312, i16 2826, i16 3340, i16 3854>
+}
+
; CHECK-LABEL: splat_v8i16:
; NO-SIMD128-NOT: i16x8
; SIMD128: .param i32{{$}}
; ==============================================================================
; 4 x i32
; ==============================================================================
+; CHECK-LABEL: const_v4i32:
+; NO-SIMD128-NOT: i32x4
+; SIMD128: .result v128{{$}}
+; SIMD128: v128.const $push0=, 50462976, 117835012, 185207048, 252579084
+; SIMD128-SAME: # encoding: [0xfd,0x00,
+; SIMD128-SAME: 0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,
+; SIMD128-SAME: 0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f]{{$}}
+define <4 x i32> @const_v4i32() {
+ ret <4 x i32> <i32 50462976, i32 117835012, i32 185207048, i32 252579084>
+}
+
; CHECK-LABEL: splat_v4i32:
; NO-SIMD128-NOT: i32x4
; SIMD128: .param i32{{$}}
; ==============================================================================
; 2 x i64
; ==============================================================================
+; CHECK-LABEL: const_v2i64:
+; NO-SIMD128-NOT: i64x2
+; SIMD128-VM-NOT: i64x2
+; SIMD128: .result v128{{$}}
+; SIMD128: v128.const $push0=, 506097522914230528, 1084818905618843912
+; SIMD128-SAME: # encoding: [0xfd,0x00,
+; SIMD128-SAME: 0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,
+; SIMD128-SAME: 0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f]{{$}}
+define <2 x i64> @const_v2i64() {
+ ret <2 x i64> <i64 506097522914230528, i64 1084818905618843912>
+}
+
; CHECK-LABEL: splat_v2i64:
; NO-SIMD128-NOT: i64x2
; SIMD128-VM-NOT: i64x2
; ==============================================================================
; 4 x f32
; ==============================================================================
+; CHECK-LABEL: const_v4f32:
+; NO-SIMD128-NOT: f32x4
+; SIMD128: .result v128{{$}}
+; SIMD128: v128.const $push0=,
+; SIMD128-SAME: 0x1.0402p-121, 0x1.0c0a08p-113, 0x1.14121p-105, 0x1.1c1a18p-97
+; SIMD128-SAME: # encoding: [0xfd,0x00,
+; SIMD128-SAME: 0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,
+; SIMD128-SAME: 0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f]{{$}}
+define <4 x float> @const_v4f32() {
+ ret <4 x float> <float 0x3860402000000000, float 0x38e0c0a080000000,
+ float 0x3961412100000000, float 0x39e1c1a180000000>
+}
+
; CHECK-LABEL: splat_v4f32:
; NO-SIMD128-NOT: f32x4
; SIMD128: .param f32{{$}}
; ==============================================================================
; 2 x f64
; ==============================================================================
+; CHECK-LABEL: const_v2f64:
+; NO-SIMD128-NOT: f64x2
+; SIMD128: .result v128{{$}}
+; SIMD128: v128.const $push0=, 0x1.60504030201p-911, 0x1.e0d0c0b0a0908p-783
+; SIMD128-SAME: # encoding: [0xfd,0x00,
+; SIMD128-SAME: 0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,
+; SIMD128-SAME: 0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f]{{$}}
+define <2 x double> @const_v2f64() {
+ ret <2 x double> <double 0x0706050403020100, double 0x0F0E0D0C0B0A0908>
+}
+
; CHECK-LABEL: splat_v2f64:
; NO-SIMD128-NOT: f64x2
; SIMD128-VM-NOT: f64x2
# Prefix byte example:
# CHECK: i64.trunc_u:sat/f64
0xFC 0x07
+
+# v128.const is arbitrarily disassembled as v2f64
+# CHECK: v128.const 0x1.60504030201p-911, 0x1.e0d0c0b0a0908p-783
+0xFD 0x00 0x00 0x01 0x02 0x03 0x04 0x05 0x06 0x07 0x08 0x09 0x0A 0x0B 0x0C 0x0D 0x0E 0x0F
-# RUN: llvm-mc -triple=wasm32-unknown-unknown < %s | FileCheck %s
+# RUN: llvm-mc -triple=wasm32-unknown-unknown -mattr=+sign_ext,+simd128 < %s | FileCheck %s
.text
.type test0,@function
test0:
# Test all types:
.param i32, i64
- .local f32, f64 #, i8x16, i16x8, i32x4, f32x4
+ .local f32, f64, v128, v128
# Explicit getlocal/setlocal:
get_local $push0=, 2
set_local 2, $pop0=
# Implicit locals & immediates:
i32.const $0=, -1
f64.const $3=, 0x1.999999999999ap1
+ v128.const $4=, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15
+ v128.const $5=, 0, 1, 2, 3, 4, 5, 6, 7
# Indirect addressing:
get_local $push1=, 0
f64.store 0($pop1), $3
.LBB0_2:
end_loop
end_block # label0:
+ get_local $push12=, 4
+ get_local $push13=, 5
+ f32x4.add $4=, $pop12, $pop13
end_function
# CHECK-NEXT: set_local 2, $pop0
# CHECK-NEXT: i32.const $0=, -1
# CHECK-NEXT: f64.const $3=, 0x1.999999999999ap1
+# CHECK-NEXT: v128.const $4=, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15
+# CHECK-NEXT: v128.const $5=, 0, 1, 2, 3, 4, 5, 6, 7
# CHECK-NEXT: get_local $push1=, 0
# CHECK-NEXT: f64.store 0($pop1):p2align=0, $3
# CHECK-NEXT: block
# CHECK-NEXT: .LBB0_2:
# CHECK-NEXT: end_loop
# CHECK-NEXT: end_block # label0:
+# CHECK-NEXT: get_local $push12=, 4
+# CHECK-NEXT: get_local $push13=, 5
+# CHECK-NEXT: f32x4.add $4=, $pop12, $pop13
# CHECK-NEXT: end_function