[WebAssembly] v128.const
authorThomas Lively <tlively@google.com>
Tue, 21 Aug 2018 21:03:18 +0000 (21:03 +0000)
committerThomas Lively <tlively@google.com>
Tue, 21 Aug 2018 21:03:18 +0000 (21:03 +0000)
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

llvm/lib/Target/WebAssembly/AsmParser/WebAssemblyAsmParser.cpp
llvm/lib/Target/WebAssembly/Disassembler/WebAssemblyDisassembler.cpp
llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCCodeEmitter.cpp
llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCTargetDesc.h
llvm/lib/Target/WebAssembly/WebAssemblyInstrInfo.td
llvm/lib/Target/WebAssembly/WebAssemblyInstrSIMD.td
llvm/test/CodeGen/WebAssembly/simd.ll
llvm/test/MC/Disassembler/WebAssembly/wasm.txt
llvm/test/MC/WebAssembly/basic-assembly.s

index 2d92b93..0d591c0 100644 (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);
   }
 
index 2f09602..9f6797d 100644 (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;
     }
index 4179126..bff074d 100644 (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 {
index b4639b4..4ca9214 100644 (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.
index 3f97ef5..d9e87e0 100644 (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>;
 
index 2a45051..08b925a 100644 (file)
 ///
 //===----------------------------------------------------------------------===//
 
-// 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",
index 414c5b3..064df0b 100644 (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
index 63c9bb1..aefd9a9 100644 (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
index dbfd921..dd9bd3c 100644 (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