[WebAssembly] v128.const

Summary:
This CL implements v128.const for each vector type. New operand types
are added to ensure the vector contents can be serialized without LEB
encoding. Tests are added for instruction selection, encoding,
assembly and disassembly.

Reviewers: aheejin, dschuff, aardappel

Subscribers: sbc100, jgravelle-google, sunfish, llvm-commits

Differential Revision: https://reviews.llvm.org/D50873

llvm-svn: 340336
This commit is contained in:
Thomas Lively 2018-08-21 21:03:18 +00:00
parent 3986243db8
commit 5e7745379a
9 changed files with 229 additions and 19 deletions

View File

@ -46,7 +46,9 @@ static unsigned MVTToWasmReg(MVT::SimpleValueType Type) {
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;
}
}
@ -194,6 +196,7 @@ public:
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
@ -232,7 +235,13 @@ public:
.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);
}

View File

@ -92,14 +92,18 @@ static bool parseLEBImmediate(MCInst &MI, uint64_t &Size,
}
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;
}
@ -160,12 +164,33 @@ MCDisassembler::DecodeStatus WebAssemblyDisassembler::getInstruction(
}
// 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;
}

View File

@ -90,17 +90,34 @@ void WebAssemblyMCCodeEmitter::encodeInstruction(
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 {

View File

@ -59,6 +59,14 @@ enum OperandType {
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.

View File

@ -118,6 +118,18 @@ def f32imm_op : Operand<f32>;
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>;

View File

@ -12,18 +12,71 @@
///
//===----------------------------------------------------------------------===//
// 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>;
@ -73,8 +126,8 @@ def : Pat<(i32 (vector_extract (v8i16 V128:$vec), (i32 LaneIdx8:$idx))),
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",

View File

@ -10,6 +10,19 @@ target triple = "wasm32-unknown-unknown"
; ==============================================================================
; 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{{$}}
@ -73,6 +86,18 @@ define <16 x i8> @replace_v16i8(<16 x i8> %v, i8 %x) {
; ==============================================================================
; 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{{$}}
@ -135,6 +160,17 @@ define <8 x i16> @replace_v8i16(<8 x i16> %v, i16 %x) {
; ==============================================================================
; 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{{$}}
@ -173,6 +209,18 @@ define <4 x i32> @replace_v4i32(<4 x i32> %v, i32 %x) {
; ==============================================================================
; 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
@ -213,6 +261,19 @@ define <2 x i64> @replace_v2i64(<2 x i64> %v, i64 %x) {
; ==============================================================================
; 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{{$}}
@ -251,6 +312,17 @@ define <4 x float> @replace_v4f32(<4 x float> %v, float %x) {
; ==============================================================================
; 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

View File

@ -31,3 +31,7 @@
# 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

View File

@ -1,17 +1,19 @@
# 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
@ -37,6 +39,9 @@ test0:
.LBB0_2:
end_loop
end_block # label0:
get_local $push12=, 4
get_local $push13=, 5
f32x4.add $4=, $pop12, $pop13
end_function
@ -48,6 +53,8 @@ test0:
# 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
@ -71,4 +78,7 @@ test0:
# 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