Index: lib/Target/WebAssembly/AsmParser/WebAssemblyAsmParser.cpp =================================================================== --- lib/Target/WebAssembly/AsmParser/WebAssemblyAsmParser.cpp +++ lib/Target/WebAssembly/AsmParser/WebAssemblyAsmParser.cpp @@ -46,7 +46,9 @@ 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 @@ 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 @@ .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); } Index: lib/Target/WebAssembly/Disassembler/WebAssemblyDisassembler.cpp =================================================================== --- lib/Target/WebAssembly/Disassembler/WebAssemblyDisassembler.cpp +++ lib/Target/WebAssembly/Disassembler/WebAssemblyDisassembler.cpp @@ -91,6 +91,18 @@ return true; } +template +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::createImm(static_cast(Val))); + return true; +} + template bool parseFPImmediate(MCInst &MI, uint64_t &Size, ArrayRef Bytes) { if (Size + sizeof(T) > Bytes.size()) @@ -169,6 +181,27 @@ 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; + } case MCOI::OPERAND_REGISTER: { // These are NOT actually in the instruction stream, but MC is going to // expect operands to be present for them! Index: lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCCodeEmitter.cpp =================================================================== --- lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCCodeEmitter.cpp +++ lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCCodeEmitter.cpp @@ -90,17 +90,34 @@ 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 { Index: lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCTargetDesc.h =================================================================== --- lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCTargetDesc.h +++ lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCTargetDesc.h @@ -59,6 +59,14 @@ 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. Index: lib/Target/WebAssembly/WebAssemblyInstrInfo.td =================================================================== --- lib/Target/WebAssembly/WebAssemblyInstrInfo.td +++ lib/Target/WebAssembly/WebAssemblyInstrInfo.td @@ -118,6 +118,18 @@ 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; Index: lib/Target/WebAssembly/WebAssemblyInstrSIMD.td =================================================================== --- lib/Target/WebAssembly/WebAssemblyInstrSIMD.td +++ 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 @@ 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", Index: test/CodeGen/WebAssembly/simd.ll =================================================================== --- test/CodeGen/WebAssembly/simd.ll +++ test/CodeGen/WebAssembly/simd.ll @@ -10,6 +10,19 @@ ; ============================================================================== ; 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 @@ ; ============================================================================== ; 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 @@ ; ============================================================================== ; 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 @@ ; ============================================================================== ; 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 @@ ; ============================================================================== ; 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 @@ ; ============================================================================== ; 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 Index: test/MC/Disassembler/WebAssembly/wasm.txt =================================================================== --- test/MC/Disassembler/WebAssembly/wasm.txt +++ 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 Index: test/MC/WebAssembly/basic-assembly.s =================================================================== --- test/MC/WebAssembly/basic-assembly.s +++ 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 @@ .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 @@ # 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 @@ # 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