From 22442924a8d1b32473fcfa56f9421b8ae58f7b8b Mon Sep 17 00:00:00 2001 From: Thomas Lively Date: Tue, 21 Aug 2018 21:03:18 +0000 Subject: [PATCH] [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 --- .../WebAssembly/AsmParser/WebAssemblyAsmParser.cpp | 9 +++ .../Disassembler/WebAssemblyDisassembler.cpp | 33 ++++++++-- .../MCTargetDesc/WebAssemblyMCCodeEmitter.cpp | 31 +++++++--- .../MCTargetDesc/WebAssemblyMCTargetDesc.h | 8 +++ .../lib/Target/WebAssembly/WebAssemblyInstrInfo.td | 12 ++++ .../lib/Target/WebAssembly/WebAssemblyInstrSIMD.td | 65 +++++++++++++++++-- llvm/test/CodeGen/WebAssembly/simd.ll | 72 ++++++++++++++++++++++ llvm/test/MC/Disassembler/WebAssembly/wasm.txt | 4 ++ llvm/test/MC/WebAssembly/basic-assembly.s | 14 ++++- 9 files changed, 229 insertions(+), 19 deletions(-) diff --git a/llvm/lib/Target/WebAssembly/AsmParser/WebAssemblyAsmParser.cpp b/llvm/lib/Target/WebAssembly/AsmParser/WebAssemblyAsmParser.cpp index 2d92b93..0d591c0 100644 --- a/llvm/lib/Target/WebAssembly/AsmParser/WebAssemblyAsmParser.cpp +++ b/llvm/lib/Target/WebAssembly/AsmParser/WebAssemblyAsmParser.cpp @@ -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); } diff --git a/llvm/lib/Target/WebAssembly/Disassembler/WebAssemblyDisassembler.cpp b/llvm/lib/Target/WebAssembly/Disassembler/WebAssemblyDisassembler.cpp index 2f09602..9f6797d 100644 --- a/llvm/lib/Target/WebAssembly/Disassembler/WebAssemblyDisassembler.cpp +++ b/llvm/lib/Target/WebAssembly/Disassembler/WebAssemblyDisassembler.cpp @@ -92,14 +92,18 @@ static bool parseLEBImmediate(MCInst &MI, uint64_t &Size, } template -bool parseFPImmediate(MCInst &MI, uint64_t &Size, ArrayRef Bytes) { +bool parseImmediate(MCInst &MI, uint64_t &Size, ArrayRef Bytes) { if (Size + sizeof(T) > Bytes.size()) return false; T Val; memcpy(&Val, Bytes.data() + Size, sizeof(T)); support::endian::byte_swap(Val); Size += sizeof(T); - MI.addOperand(MCOperand::createFPImm(static_cast(Val))); + if (std::is_floating_point::value) { + MI.addOperand(MCOperand::createFPImm(static_cast(Val))); + } else { + MI.addOperand(MCOperand::createImm(static_cast(Val))); + } return true; } @@ -160,12 +164,33 @@ MCDisassembler::DecodeStatus WebAssemblyDisassembler::getInstruction( } // FP operands. case WebAssembly::OPERAND_F32IMM: { - if (!parseFPImmediate(MI, Size, Bytes)) + if (!parseImmediate(MI, Size, Bytes)) return MCDisassembler::Fail; break; } case WebAssembly::OPERAND_F64IMM: { - if (!parseFPImmediate(MI, Size, Bytes)) + if (!parseImmediate(MI, Size, Bytes)) + return MCDisassembler::Fail; + break; + } + // Vector lane operands (not LEB encoded). + case WebAssembly::OPERAND_VEC_I8IMM: { + if (!parseImmediate(MI, Size, Bytes)) + return MCDisassembler::Fail; + break; + } + case WebAssembly::OPERAND_VEC_I16IMM: { + if (!parseImmediate(MI, Size, Bytes)) + return MCDisassembler::Fail; + break; + } + case WebAssembly::OPERAND_VEC_I32IMM: { + if (!parseImmediate(MI, Size, Bytes)) + return MCDisassembler::Fail; + break; + } + case WebAssembly::OPERAND_VEC_I64IMM: { + if (!parseImmediate(MI, Size, Bytes)) return MCDisassembler::Fail; break; } diff --git a/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCCodeEmitter.cpp b/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCCodeEmitter.cpp index 4179126..bff074d 100644 --- a/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCCodeEmitter.cpp +++ b/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCCodeEmitter.cpp @@ -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(OS, MO.getImm(), support::little); + break; + case WebAssembly::OPERAND_VEC_I16IMM: + support::endian::write(OS, MO.getImm(), support::little); + break; + case WebAssembly::OPERAND_VEC_I32IMM: + support::endian::write(OS, MO.getImm(), support::little); + break; + case WebAssembly::OPERAND_VEC_I64IMM: + support::endian::write(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 { diff --git a/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCTargetDesc.h b/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCTargetDesc.h index b4639b4..4ca9214 100644 --- a/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCTargetDesc.h +++ b/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCTargetDesc.h @@ -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. diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyInstrInfo.td b/llvm/lib/Target/WebAssembly/WebAssemblyInstrInfo.td index 3f97ef5..d9e87e0 100644 --- a/llvm/lib/Target/WebAssembly/WebAssemblyInstrInfo.td +++ b/llvm/lib/Target/WebAssembly/WebAssemblyInstrInfo.td @@ -118,6 +118,18 @@ def f32imm_op : Operand; let OperandType = "OPERAND_F64IMM" in def f64imm_op : Operand; +let OperandType = "OPERAND_VEC_I8IMM" in +def vec_i8imm_op : Operand; + +let OperandType = "OPERAND_VEC_I16IMM" in +def vec_i16imm_op : Operand; + +let OperandType = "OPERAND_VEC_I32IMM" in +def vec_i32imm_op : Operand; + +let OperandType = "OPERAND_VEC_I64IMM" in +def vec_i64imm_op : Operand; + let OperandType = "OPERAND_FUNCTION32" in def function32_op : Operand; diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyInstrSIMD.td b/llvm/lib/Target/WebAssembly/WebAssemblyInstrSIMD.td index 2a45051..08b925a 100644 --- a/llvm/lib/Target/WebAssembly/WebAssemblyInstrSIMD.td +++ b/llvm/lib/Target/WebAssembly/WebAssemblyInstrSIMD.td @@ -12,18 +12,71 @@ /// //===----------------------------------------------------------------------===// -// immediate argument types -def ImmByte : ImmLeaf; +// constrained immediate argument types +foreach SIZE = [8, 16] in +def ImmI#SIZE : ImmLeaf; foreach SIZE = [2, 4, 8, 16, 32] in def LaneIdx#SIZE : ImmLeaf; +// const vectors +multiclass ConstVec { + 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; +defm "" : ConstVec; +defm "" : ConstVec; +defm "" : ConstVec; +defm "" : ConstVec; +defm "" : ConstVec; + // lane extraction multiclass ExtractLane 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 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", diff --git a/llvm/test/CodeGen/WebAssembly/simd.ll b/llvm/test/CodeGen/WebAssembly/simd.ll index 414c5b3..064df0b 100644 --- a/llvm/test/CodeGen/WebAssembly/simd.ll +++ b/llvm/test/CodeGen/WebAssembly/simd.ll @@ -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> +} + ; 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> +} + ; 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> +} + ; 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> +} + ; 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> +} + ; 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> +} + ; CHECK-LABEL: splat_v2f64: ; NO-SIMD128-NOT: f64x2 ; SIMD128-VM-NOT: f64x2 diff --git a/llvm/test/MC/Disassembler/WebAssembly/wasm.txt b/llvm/test/MC/Disassembler/WebAssembly/wasm.txt index 63c9bb1..aefd9a9 100644 --- a/llvm/test/MC/Disassembler/WebAssembly/wasm.txt +++ b/llvm/test/MC/Disassembler/WebAssembly/wasm.txt @@ -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 diff --git a/llvm/test/MC/WebAssembly/basic-assembly.s b/llvm/test/MC/WebAssembly/basic-assembly.s index dbfd921..dd9bd3c 100644 --- a/llvm/test/MC/WebAssembly/basic-assembly.s +++ b/llvm/test/MC/WebAssembly/basic-assembly.s @@ -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 -- 2.7.4