// WebAssemblyInstrSIMD.td - WebAssembly SIMD codegen support -*- tablegen -*-// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// /// /// \file /// WebAssembly SIMD operand code-gen constructs. /// //===----------------------------------------------------------------------===// // Instructions requiring HasSIMD128 and the simd128 prefix byte multiclass SIMD_I pattern_r, string asmstr_r = "", string asmstr_s = "", bits<32> simdop = -1> { defm "" : I, Requires<[HasSIMD128]>; } defm "" : ARGUMENT; defm "" : ARGUMENT; defm "" : ARGUMENT; defm "" : ARGUMENT; defm "" : ARGUMENT; defm "" : ARGUMENT; // 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; //===----------------------------------------------------------------------===// // Load and store //===----------------------------------------------------------------------===// // Load: v128.load let mayLoad = 1, UseNamedOperandTable = 1 in defm LOAD_V128 : SIMD_I<(outs V128:$dst), (ins P2Align:$p2align, offset32_op:$off, I32:$addr), (outs), (ins P2Align:$p2align, offset32_op:$off), [], "v128.load\t$dst, ${off}(${addr})$p2align", "v128.load\t$off$p2align", 0>; // Def load and store patterns from WebAssemblyInstrMemory.td for vector types foreach vec_t = [v16i8, v8i16, v4i32, v2i64, v4f32, v2f64] in { def : LoadPatNoOffset; def : LoadPatImmOff; def : LoadPatImmOff; def : LoadPatOffsetOnly; def : LoadPatGlobalAddrOffOnly; } // vNxM.load_splat multiclass SIMDLoadSplat simdop> { let mayLoad = 1, UseNamedOperandTable = 1, Predicates = [HasUnimplementedSIMD128] in defm LOAD_SPLAT_#vec : SIMD_I<(outs V128:$dst), (ins P2Align:$p2align, offset32_op:$off, I32:$addr), (outs), (ins P2Align:$p2align, offset32_op:$off), [], vec#".load_splat\t$dst, ${off}(${addr})$p2align", vec#".load_splat\t$off$p2align", simdop>; } defm "" : SIMDLoadSplat<"v8x16", 194>; defm "" : SIMDLoadSplat<"v16x8", 195>; defm "" : SIMDLoadSplat<"v32x4", 196>; defm "" : SIMDLoadSplat<"v64x2", 197>; def wasm_load_splat_t : SDTypeProfile<1, 1, [SDTCisPtrTy<1>]>; def wasm_load_splat : SDNode<"WebAssemblyISD::LOAD_SPLAT", wasm_load_splat_t, [SDNPHasChain, SDNPMayLoad, SDNPMemOperand]>; def load_splat : PatFrag<(ops node:$addr), (wasm_load_splat node:$addr)>; let Predicates = [HasUnimplementedSIMD128] in foreach args = [["v16i8", "v8x16"], ["v8i16", "v16x8"], ["v4i32", "v32x4"], ["v2i64", "v64x2"], ["v4f32", "v32x4"], ["v2f64", "v64x2"]] in { def : LoadPatNoOffset(args[0]), load_splat, !cast("LOAD_SPLAT_"#args[1])>; def : LoadPatImmOff(args[0]), load_splat, regPlusImm, !cast("LOAD_SPLAT_"#args[1])>; def : LoadPatImmOff(args[0]), load_splat, or_is_add, !cast("LOAD_SPLAT_"#args[1])>; def : LoadPatOffsetOnly(args[0]), load_splat, !cast("LOAD_SPLAT_"#args[1])>; def : LoadPatGlobalAddrOffOnly(args[0]), load_splat, !cast("LOAD_SPLAT_"#args[1])>; } // Load and extend multiclass SIMDLoadExtend simdop> { let mayLoad = 1, UseNamedOperandTable = 1, Predicates = [HasUnimplementedSIMD128] in { defm LOAD_EXTEND_S_#vec_t : SIMD_I<(outs V128:$dst), (ins P2Align:$p2align, offset32_op:$off, I32:$addr), (outs), (ins P2Align:$p2align, offset32_op:$off), [], name#"_s\t$dst, ${off}(${addr})$p2align", name#"_s\t$off$p2align", simdop>; defm LOAD_EXTEND_U_#vec_t : SIMD_I<(outs V128:$dst), (ins P2Align:$p2align, offset32_op:$off, I32:$addr), (outs), (ins P2Align:$p2align, offset32_op:$off), [], name#"_u\t$dst, ${off}(${addr})$p2align", name#"_u\t$off$p2align", !add(simdop, 1)>; } } defm "" : SIMDLoadExtend; defm "" : SIMDLoadExtend; defm "" : SIMDLoadExtend; let Predicates = [HasUnimplementedSIMD128] in foreach types = [[v8i16, i8], [v4i32, i16], [v2i64, i32]] in foreach exts = [["sextloadv", "_S"], ["zextloadv", "_U"], ["extloadv", "_U"]] in { def : LoadPatNoOffset(exts[0]#types[1]), !cast("LOAD_EXTEND"#exts[1]#"_"#types[0])>; def : LoadPatImmOff(exts[0]#types[1]), regPlusImm, !cast("LOAD_EXTEND"#exts[1]#"_"#types[0])>; def : LoadPatImmOff(exts[0]#types[1]), or_is_add, !cast("LOAD_EXTEND"#exts[1]#"_"#types[0])>; def : LoadPatOffsetOnly(exts[0]#types[1]), !cast("LOAD_EXTEND"#exts[1]#"_"#types[0])>; def : LoadPatGlobalAddrOffOnly(exts[0]#types[1]), !cast("LOAD_EXTEND"#exts[1]#"_"#types[0])>; } // Store: v128.store let mayStore = 1, UseNamedOperandTable = 1 in defm STORE_V128 : SIMD_I<(outs), (ins P2Align:$p2align, offset32_op:$off, I32:$addr, V128:$vec), (outs), (ins P2Align:$p2align, offset32_op:$off), [], "v128.store\t${off}(${addr})$p2align, $vec", "v128.store\t$off$p2align", 1>; foreach vec_t = [v16i8, v8i16, v4i32, v2i64, v4f32, v2f64] in { // Def load and store patterns from WebAssemblyInstrMemory.td for vector types def : StorePatNoOffset; def : StorePatImmOff; def : StorePatImmOff; def : StorePatOffsetOnly; def : StorePatGlobalAddrOffOnly; } //===----------------------------------------------------------------------===// // Constructing SIMD values //===----------------------------------------------------------------------===// // Constant: v128.const multiclass ConstVec { let isMoveImm = 1, isReMaterializable = 1, Predicates = [HasUnimplementedSIMD128] in 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, 2>; } defm "" : ConstVec; defm "" : ConstVec; let IsCanonical = 1 in defm "" : ConstVec; defm "" : ConstVec; defm "" : ConstVec; defm "" : ConstVec; // Shuffle lanes: shuffle defm SHUFFLE : SIMD_I<(outs V128:$dst), (ins V128:$x, V128:$y, vec_i8imm_op:$m0, vec_i8imm_op:$m1, vec_i8imm_op:$m2, vec_i8imm_op:$m3, vec_i8imm_op:$m4, vec_i8imm_op:$m5, vec_i8imm_op:$m6, vec_i8imm_op:$m7, vec_i8imm_op:$m8, vec_i8imm_op:$m9, vec_i8imm_op:$mA, vec_i8imm_op:$mB, vec_i8imm_op:$mC, vec_i8imm_op:$mD, vec_i8imm_op:$mE, vec_i8imm_op:$mF), (outs), (ins vec_i8imm_op:$m0, vec_i8imm_op:$m1, vec_i8imm_op:$m2, vec_i8imm_op:$m3, vec_i8imm_op:$m4, vec_i8imm_op:$m5, vec_i8imm_op:$m6, vec_i8imm_op:$m7, vec_i8imm_op:$m8, vec_i8imm_op:$m9, vec_i8imm_op:$mA, vec_i8imm_op:$mB, vec_i8imm_op:$mC, vec_i8imm_op:$mD, vec_i8imm_op:$mE, vec_i8imm_op:$mF), [], "v8x16.shuffle\t$dst, $x, $y, "# "$m0, $m1, $m2, $m3, $m4, $m5, $m6, $m7, "# "$m8, $m9, $mA, $mB, $mC, $mD, $mE, $mF", "v8x16.shuffle\t"# "$m0, $m1, $m2, $m3, $m4, $m5, $m6, $m7, "# "$m8, $m9, $mA, $mB, $mC, $mD, $mE, $mF", 3>; // Shuffles after custom lowering def wasm_shuffle_t : SDTypeProfile<1, 18, []>; def wasm_shuffle : SDNode<"WebAssemblyISD::SHUFFLE", wasm_shuffle_t>; foreach vec_t = [v16i8, v8i16, v4i32, v2i64, v4f32, v2f64] in { def : Pat<(vec_t (wasm_shuffle (vec_t V128:$x), (vec_t V128:$y), (i32 LaneIdx32:$m0), (i32 LaneIdx32:$m1), (i32 LaneIdx32:$m2), (i32 LaneIdx32:$m3), (i32 LaneIdx32:$m4), (i32 LaneIdx32:$m5), (i32 LaneIdx32:$m6), (i32 LaneIdx32:$m7), (i32 LaneIdx32:$m8), (i32 LaneIdx32:$m9), (i32 LaneIdx32:$mA), (i32 LaneIdx32:$mB), (i32 LaneIdx32:$mC), (i32 LaneIdx32:$mD), (i32 LaneIdx32:$mE), (i32 LaneIdx32:$mF))), (vec_t (SHUFFLE (vec_t V128:$x), (vec_t V128:$y), (i32 LaneIdx32:$m0), (i32 LaneIdx32:$m1), (i32 LaneIdx32:$m2), (i32 LaneIdx32:$m3), (i32 LaneIdx32:$m4), (i32 LaneIdx32:$m5), (i32 LaneIdx32:$m6), (i32 LaneIdx32:$m7), (i32 LaneIdx32:$m8), (i32 LaneIdx32:$m9), (i32 LaneIdx32:$mA), (i32 LaneIdx32:$mB), (i32 LaneIdx32:$mC), (i32 LaneIdx32:$mD), (i32 LaneIdx32:$mE), (i32 LaneIdx32:$mF)))>; } // Swizzle lanes: v8x16.swizzle def wasm_swizzle_t : SDTypeProfile<1, 2, []>; def wasm_swizzle : SDNode<"WebAssemblyISD::SWIZZLE", wasm_swizzle_t>; let Predicates = [HasUnimplementedSIMD128] in defm SWIZZLE : SIMD_I<(outs V128:$dst), (ins V128:$src, V128:$mask), (outs), (ins), [(set (v16i8 V128:$dst), (wasm_swizzle (v16i8 V128:$src), (v16i8 V128:$mask)))], "v8x16.swizzle\t$dst, $src, $mask", "v8x16.swizzle", 192>; def : Pat<(int_wasm_swizzle (v16i8 V128:$src), (v16i8 V128:$mask)), (SWIZZLE V128:$src, V128:$mask)>; // Create vector with identical lanes: splat def splat2 : PatFrag<(ops node:$x), (build_vector node:$x, node:$x)>; def splat4 : PatFrag<(ops node:$x), (build_vector node:$x, node:$x, node:$x, node:$x)>; def splat8 : PatFrag<(ops node:$x), (build_vector node:$x, node:$x, node:$x, node:$x, node:$x, node:$x, node:$x, node:$x)>; def splat16 : PatFrag<(ops node:$x), (build_vector node:$x, node:$x, node:$x, node:$x, node:$x, node:$x, node:$x, node:$x, node:$x, node:$x, node:$x, node:$x, node:$x, node:$x, node:$x, node:$x)>; multiclass Splat simdop> { // Prefer splats over v128.const for const splats (65 is lowest that works) let AddedComplexity = 65 in defm SPLAT_#vec_t : SIMD_I<(outs V128:$dst), (ins reg_t:$x), (outs), (ins), [(set (vec_t V128:$dst), (splat_pat reg_t:$x))], vec#".splat\t$dst, $x", vec#".splat", simdop>; } defm "" : Splat; defm "" : Splat; defm "" : Splat; defm "" : Splat; defm "" : Splat; defm "" : Splat; // scalar_to_vector leaves high lanes undefined, so can be a splat class ScalarSplatPat : Pat<(vec_t (scalar_to_vector (lane_t reg_t:$x))), (!cast("SPLAT_"#vec_t) reg_t:$x)>; def : ScalarSplatPat; def : ScalarSplatPat; def : ScalarSplatPat; def : ScalarSplatPat; def : ScalarSplatPat; def : ScalarSplatPat; //===----------------------------------------------------------------------===// // Accessing lanes //===----------------------------------------------------------------------===// // Extract lane as a scalar: extract_lane / extract_lane_s / extract_lane_u multiclass ExtractLane simdop, string suffix = "", SDNode extract = vector_extract> { defm EXTRACT_LANE_#vec_t#suffix : SIMD_I<(outs reg_t:$dst), (ins V128:$vec, vec_i8imm_op:$idx), (outs), (ins vec_i8imm_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 ExtractPat { def _s : PatFrag<(ops node:$vec, node:$idx), (i32 (sext_inreg (i32 (vector_extract node:$vec, node:$idx )), lane_t ))>; def _u : PatFrag<(ops node:$vec, node:$idx), (i32 (and (i32 (vector_extract node:$vec, node:$idx )), (i32 mask) ))>; } defm extract_i8x16 : ExtractPat; defm extract_i16x8 : ExtractPat; multiclass ExtractLaneExtended baseInst> { defm "" : ExtractLane("extract_i8x16"#sign)>; defm "" : ExtractLane("extract_i16x8"#sign)>; } defm "" : ExtractLaneExtended<"_s", 5>; let Predicates = [HasUnimplementedSIMD128] in defm "" : ExtractLaneExtended<"_u", 6>; defm "" : ExtractLane; defm "" : ExtractLane; defm "" : ExtractLane; defm "" : ExtractLane; // It would be more conventional to use unsigned extracts, but v8 // doesn't implement them yet def : Pat<(i32 (vector_extract (v16i8 V128:$vec), (i32 LaneIdx16:$idx))), (EXTRACT_LANE_v16i8_s V128:$vec, (i32 LaneIdx16:$idx))>; def : Pat<(i32 (vector_extract (v8i16 V128:$vec), (i32 LaneIdx8:$idx))), (EXTRACT_LANE_v8i16_s V128:$vec, (i32 LaneIdx8:$idx))>; // Lower undef lane indices to zero def : Pat<(and (i32 (vector_extract (v16i8 V128:$vec), undef)), (i32 0xff)), (EXTRACT_LANE_v16i8_u V128:$vec, 0)>; def : Pat<(and (i32 (vector_extract (v8i16 V128:$vec), undef)), (i32 0xffff)), (EXTRACT_LANE_v8i16_u V128:$vec, 0)>; def : Pat<(i32 (vector_extract (v16i8 V128:$vec), undef)), (EXTRACT_LANE_v16i8_u V128:$vec, 0)>; def : Pat<(i32 (vector_extract (v8i16 V128:$vec), undef)), (EXTRACT_LANE_v8i16_u V128:$vec, 0)>; def : Pat<(sext_inreg (i32 (vector_extract (v16i8 V128:$vec), undef)), i8), (EXTRACT_LANE_v16i8_s V128:$vec, 0)>; def : Pat<(sext_inreg (i32 (vector_extract (v8i16 V128:$vec), undef)), i16), (EXTRACT_LANE_v8i16_s V128:$vec, 0)>; def : Pat<(vector_extract (v4i32 V128:$vec), undef), (EXTRACT_LANE_v4i32 V128:$vec, 0)>; def : Pat<(vector_extract (v2i64 V128:$vec), undef), (EXTRACT_LANE_v2i64 V128:$vec, 0)>; def : Pat<(vector_extract (v4f32 V128:$vec), undef), (EXTRACT_LANE_v4f32 V128:$vec, 0)>; def : Pat<(vector_extract (v2f64 V128:$vec), undef), (EXTRACT_LANE_v2f64 V128:$vec, 0)>; // Replace lane value: replace_lane multiclass ReplaceLane simdop> { defm REPLACE_LANE_#vec_t : SIMD_I<(outs V128:$dst), (ins V128:$vec, vec_i8imm_op:$idx, reg_t:$x), (outs), (ins vec_i8imm_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", vec#".replace_lane\t$idx", simdop>; } defm "" : ReplaceLane; defm "" : ReplaceLane; defm "" : ReplaceLane; defm "" : ReplaceLane; defm "" : ReplaceLane; defm "" : ReplaceLane; // Lower undef lane indices to zero def : Pat<(vector_insert (v16i8 V128:$vec), I32:$x, undef), (REPLACE_LANE_v16i8 V128:$vec, 0, I32:$x)>; def : Pat<(vector_insert (v8i16 V128:$vec), I32:$x, undef), (REPLACE_LANE_v8i16 V128:$vec, 0, I32:$x)>; def : Pat<(vector_insert (v4i32 V128:$vec), I32:$x, undef), (REPLACE_LANE_v4i32 V128:$vec, 0, I32:$x)>; def : Pat<(vector_insert (v2i64 V128:$vec), I64:$x, undef), (REPLACE_LANE_v2i64 V128:$vec, 0, I64:$x)>; def : Pat<(vector_insert (v4f32 V128:$vec), F32:$x, undef), (REPLACE_LANE_v4f32 V128:$vec, 0, F32:$x)>; def : Pat<(vector_insert (v2f64 V128:$vec), F64:$x, undef), (REPLACE_LANE_v2f64 V128:$vec, 0, F64:$x)>; //===----------------------------------------------------------------------===// // Comparisons //===----------------------------------------------------------------------===// multiclass SIMDCondition simdop> { defm _#vec_t : SIMD_I<(outs V128:$dst), (ins V128:$lhs, V128:$rhs), (outs), (ins), [(set (out_t V128:$dst), (setcc (vec_t V128:$lhs), (vec_t V128:$rhs), cond) )], vec#"."#name#"\t$dst, $lhs, $rhs", vec#"."#name, simdop>; } multiclass SIMDConditionInt baseInst> { defm "" : SIMDCondition; defm "" : SIMDCondition; defm "" : SIMDCondition; } multiclass SIMDConditionFP baseInst> { defm "" : SIMDCondition; defm "" : SIMDCondition; } // Equality: eq let isCommutable = 1 in { defm EQ : SIMDConditionInt<"eq", SETEQ, 24>; defm EQ : SIMDConditionFP<"eq", SETOEQ, 64>; } // isCommutable = 1 // Non-equality: ne let isCommutable = 1 in { defm NE : SIMDConditionInt<"ne", SETNE, 25>; defm NE : SIMDConditionFP<"ne", SETUNE, 65>; } // isCommutable = 1 // Less than: lt_s / lt_u / lt defm LT_S : SIMDConditionInt<"lt_s", SETLT, 26>; defm LT_U : SIMDConditionInt<"lt_u", SETULT, 27>; defm LT : SIMDConditionFP<"lt", SETOLT, 66>; // Greater than: gt_s / gt_u / gt defm GT_S : SIMDConditionInt<"gt_s", SETGT, 28>; defm GT_U : SIMDConditionInt<"gt_u", SETUGT, 29>; defm GT : SIMDConditionFP<"gt", SETOGT, 67>; // Less than or equal: le_s / le_u / le defm LE_S : SIMDConditionInt<"le_s", SETLE, 30>; defm LE_U : SIMDConditionInt<"le_u", SETULE, 31>; defm LE : SIMDConditionFP<"le", SETOLE, 68>; // Greater than or equal: ge_s / ge_u / ge defm GE_S : SIMDConditionInt<"ge_s", SETGE, 32>; defm GE_U : SIMDConditionInt<"ge_u", SETUGE, 33>; defm GE : SIMDConditionFP<"ge", SETOGE, 69>; // Lower float comparisons that don't care about NaN to standard WebAssembly // float comparisons. These instructions are generated with nnan and in the // target-independent expansion of unordered comparisons and ordered ne. foreach nodes = [[seteq, EQ_v4f32], [setne, NE_v4f32], [setlt, LT_v4f32], [setgt, GT_v4f32], [setle, LE_v4f32], [setge, GE_v4f32]] in def : Pat<(v4i32 (nodes[0] (v4f32 V128:$lhs), (v4f32 V128:$rhs))), (v4i32 (nodes[1] (v4f32 V128:$lhs), (v4f32 V128:$rhs)))>; foreach nodes = [[seteq, EQ_v2f64], [setne, NE_v2f64], [setlt, LT_v2f64], [setgt, GT_v2f64], [setle, LE_v2f64], [setge, GE_v2f64]] in def : Pat<(v2i64 (nodes[0] (v2f64 V128:$lhs), (v2f64 V128:$rhs))), (v2i64 (nodes[1] (v2f64 V128:$lhs), (v2f64 V128:$rhs)))>; //===----------------------------------------------------------------------===// // Bitwise operations //===----------------------------------------------------------------------===// multiclass SIMDBinary simdop> { defm _#vec_t : SIMD_I<(outs V128:$dst), (ins V128:$lhs, V128:$rhs), (outs), (ins), [(set (vec_t V128:$dst), (node (vec_t V128:$lhs), (vec_t V128:$rhs)) )], vec#"."#name#"\t$dst, $lhs, $rhs", vec#"."#name, simdop>; } multiclass SIMDBitwise simdop> { defm "" : SIMDBinary; defm "" : SIMDBinary; defm "" : SIMDBinary; defm "" : SIMDBinary; } multiclass SIMDUnary simdop> { defm _#vec_t : SIMD_I<(outs V128:$dst), (ins V128:$vec), (outs), (ins), [(set (vec_t V128:$dst), (vec_t (node (vec_t V128:$vec))) )], vec#"."#name#"\t$dst, $vec", vec#"."#name, simdop>; } // Bitwise logic: v128.not foreach vec_t = [v16i8, v8i16, v4i32, v2i64] in defm NOT: SIMDUnary; // Bitwise logic: v128.and / v128.or / v128.xor let isCommutable = 1 in { defm AND : SIMDBitwise; defm OR : SIMDBitwise; defm XOR : SIMDBitwise; } // isCommutable = 1 // Bitwise logic: v128.andnot def andnot : PatFrag<(ops node:$left, node:$right), (and $left, (vnot $right))>; let Predicates = [HasUnimplementedSIMD128] in defm ANDNOT : SIMDBitwise; // Bitwise select: v128.bitselect foreach vec_t = [v16i8, v8i16, v4i32, v2i64, v4f32, v2f64] in defm BITSELECT_#vec_t : SIMD_I<(outs V128:$dst), (ins V128:$v1, V128:$v2, V128:$c), (outs), (ins), [(set (vec_t V128:$dst), (vec_t (int_wasm_bitselect (vec_t V128:$v1), (vec_t V128:$v2), (vec_t V128:$c) )) )], "v128.bitselect\t$dst, $v1, $v2, $c", "v128.bitselect", 80>; // Bitselect is equivalent to (c & v1) | (~c & v2) foreach vec_t = [v16i8, v8i16, v4i32, v2i64] in def : Pat<(vec_t (or (and (vec_t V128:$c), (vec_t V128:$v1)), (and (vnot V128:$c), (vec_t V128:$v2)))), (!cast("BITSELECT_"#vec_t) V128:$v1, V128:$v2, V128:$c)>; //===----------------------------------------------------------------------===// // Integer unary arithmetic //===----------------------------------------------------------------------===// multiclass SIMDUnaryInt baseInst> { defm "" : SIMDUnary; defm "" : SIMDUnary; defm "" : SIMDUnary; defm "" : SIMDUnary; } multiclass SIMDReduceVec simdop> { defm _#vec_t : SIMD_I<(outs I32:$dst), (ins V128:$vec), (outs), (ins), [(set I32:$dst, (i32 (op (vec_t V128:$vec))))], vec#"."#name#"\t$dst, $vec", vec#"."#name, simdop>; } multiclass SIMDReduce baseInst> { defm "" : SIMDReduceVec; defm "" : SIMDReduceVec; defm "" : SIMDReduceVec; defm "" : SIMDReduceVec; } // Integer vector negation def ivneg : PatFrag<(ops node:$in), (sub immAllZerosV, node:$in)>; // Integer negation: neg defm NEG : SIMDUnaryInt; // Any lane true: any_true defm ANYTRUE : SIMDReduce; // All lanes true: all_true defm ALLTRUE : SIMDReduce; // Reductions already return 0 or 1, so and 1, setne 0, and seteq 1 // can be folded out foreach reduction = [["int_wasm_anytrue", "ANYTRUE"], ["int_wasm_alltrue", "ALLTRUE"]] in foreach ty = [v16i8, v8i16, v4i32, v2i64] in { def : Pat<(i32 (and (i32 (!cast(reduction[0]) (ty V128:$x))), (i32 1) )), (i32 (!cast(reduction[1]#"_"#ty) (ty V128:$x)))>; def : Pat<(i32 (setne (i32 (!cast(reduction[0]) (ty V128:$x))), (i32 0) )), (i32 (!cast(reduction[1]#"_"#ty) (ty V128:$x)))>; def : Pat<(i32 (seteq (i32 (!cast(reduction[0]) (ty V128:$x))), (i32 1) )), (i32 (!cast(reduction[1]#"_"#ty) (ty V128:$x)))>; } //===----------------------------------------------------------------------===// // Bit shifts //===----------------------------------------------------------------------===// multiclass SIMDShift simdop> { defm _#vec_t : SIMD_I<(outs V128:$dst), (ins V128:$vec, I32:$x), (outs), (ins), [(set (vec_t V128:$dst), (node V128:$vec, (vec_t shift_vec)))], vec#"."#name#"\t$dst, $vec, $x", vec#"."#name, simdop>; } multiclass SIMDShiftInt baseInst> { defm "" : SIMDShift; defm "" : SIMDShift; defm "" : SIMDShift; defm "" : SIMDShift; } // Left shift by scalar: shl defm SHL : SIMDShiftInt; // Right shift by scalar: shr_s / shr_u defm SHR_S : SIMDShiftInt; defm SHR_U : SIMDShiftInt; // Truncate i64 shift operands to i32s, except if they are already i32s foreach shifts = [[shl, SHL_v2i64], [sra, SHR_S_v2i64], [srl, SHR_U_v2i64]] in { def : Pat<(v2i64 (shifts[0] (v2i64 V128:$vec), (v2i64 (splat2 (i64 (sext I32:$x)))) )), (v2i64 (shifts[1] (v2i64 V128:$vec), (i32 I32:$x)))>; def : Pat<(v2i64 (shifts[0] (v2i64 V128:$vec), (v2i64 (splat2 I64:$x)))), (v2i64 (shifts[1] (v2i64 V128:$vec), (I32_WRAP_I64 I64:$x)))>; } // 2xi64 shifts with constant shift amounts are custom lowered to avoid wrapping def wasm_shift_t : SDTypeProfile<1, 2, [SDTCisVec<0>, SDTCisSameAs<0, 1>, SDTCisVT<2, i32>] >; def wasm_shl : SDNode<"WebAssemblyISD::VEC_SHL", wasm_shift_t>; def wasm_shr_s : SDNode<"WebAssemblyISD::VEC_SHR_S", wasm_shift_t>; def wasm_shr_u : SDNode<"WebAssemblyISD::VEC_SHR_U", wasm_shift_t>; foreach shifts = [[wasm_shl, SHL_v2i64], [wasm_shr_s, SHR_S_v2i64], [wasm_shr_u, SHR_U_v2i64]] in def : Pat<(v2i64 (shifts[0] (v2i64 V128:$vec), I32:$x)), (v2i64 (shifts[1] (v2i64 V128:$vec), I32:$x))>; //===----------------------------------------------------------------------===// // Integer binary arithmetic //===----------------------------------------------------------------------===// multiclass SIMDBinaryIntSmall baseInst> { defm "" : SIMDBinary; defm "" : SIMDBinary; } multiclass SIMDBinaryIntNoI64x2 baseInst> { defm "" : SIMDBinaryIntSmall; defm "" : SIMDBinary; } multiclass SIMDBinaryInt baseInst> { defm "" : SIMDBinaryIntNoI64x2; defm "" : SIMDBinary; } // Integer addition: add / add_saturate_s / add_saturate_u let isCommutable = 1 in { defm ADD : SIMDBinaryInt; defm ADD_SAT_S : SIMDBinaryIntSmall; defm ADD_SAT_U : SIMDBinaryIntSmall; } // isCommutable = 1 // Integer subtraction: sub / sub_saturate_s / sub_saturate_u defm SUB : SIMDBinaryInt; defm SUB_SAT_S : SIMDBinaryIntSmall; defm SUB_SAT_U : SIMDBinaryIntSmall; // Integer multiplication: mul let isCommutable = 1 in defm MUL : SIMDBinaryIntNoI64x2; // Integer min_s / min_u / max_s / max_u let isCommutable = 1 in { defm MIN_S : SIMDBinaryIntNoI64x2; defm MIN_U : SIMDBinaryIntNoI64x2; defm MAX_S : SIMDBinaryIntNoI64x2; defm MAX_U : SIMDBinaryIntNoI64x2; } // isCommutable = 1 // Integer unsigned rounding average: avgr_u let isCommutable = 1, Predicates = [HasUnimplementedSIMD128] in { defm AVGR_U : SIMDBinary; defm AVGR_U : SIMDBinary; } def add_nuw : PatFrag<(ops node:$lhs, node:$rhs), (add node:$lhs, node:$rhs), "return N->getFlags().hasNoUnsignedWrap();">; foreach nodes = [[v16i8, splat16], [v8i16, splat8]] in def : Pat<(srl (add_nuw (add_nuw (nodes[0] V128:$lhs), (nodes[0] V128:$rhs)), (nodes[1] (i32 1)) ), (nodes[0] (nodes[1] (i32 1))) ), (!cast("AVGR_U_"#nodes[0]) V128:$lhs, V128:$rhs)>; // Widening dot product: i32x4.dot_i16x8_s let isCommutable = 1 in defm DOT : SIMD_I<(outs V128:$dst), (ins V128:$lhs, V128:$rhs), (outs), (ins), [(set V128:$dst, (int_wasm_dot V128:$lhs, V128:$rhs))], "i32x4.dot_i16x8_s\t$dst, $lhs, $rhs", "i32x4.dot_i16x8_s", 219>; //===----------------------------------------------------------------------===// // Floating-point unary arithmetic //===----------------------------------------------------------------------===// multiclass SIMDUnaryFP baseInst> { defm "" : SIMDUnary; defm "" : SIMDUnary; } // Absolute value: abs defm ABS : SIMDUnaryFP; // Negation: neg defm NEG : SIMDUnaryFP; // Square root: sqrt let Predicates = [HasUnimplementedSIMD128] in defm SQRT : SIMDUnaryFP; //===----------------------------------------------------------------------===// // Floating-point binary arithmetic //===----------------------------------------------------------------------===// multiclass SIMDBinaryFP baseInst> { defm "" : SIMDBinary; defm "" : SIMDBinary; } // Addition: add let isCommutable = 1 in defm ADD : SIMDBinaryFP; // Subtraction: sub defm SUB : SIMDBinaryFP; // Multiplication: mul let isCommutable = 1 in defm MUL : SIMDBinaryFP; // Division: div let Predicates = [HasUnimplementedSIMD128] in defm DIV : SIMDBinaryFP; // NaN-propagating minimum: min defm MIN : SIMDBinaryFP; // NaN-propagating maximum: max defm MAX : SIMDBinaryFP; //===----------------------------------------------------------------------===// // Conversions //===----------------------------------------------------------------------===// multiclass SIMDConvert simdop> { defm op#_#vec_t#_#arg_t : SIMD_I<(outs V128:$dst), (ins V128:$vec), (outs), (ins), [(set (vec_t V128:$dst), (vec_t (op (arg_t V128:$vec))))], name#"\t$dst, $vec", name, simdop>; } // Integer to floating point: convert defm "" : SIMDConvert; defm "" : SIMDConvert; defm "" : SIMDConvert; defm "" : SIMDConvert; // Floating point to integer with saturation: trunc_sat defm "" : SIMDConvert; defm "" : SIMDConvert; defm "" : SIMDConvert; defm "" : SIMDConvert; // Widening operations multiclass SIMDWiden baseInst> { defm "" : SIMDConvert; defm "" : SIMDConvert; defm "" : SIMDConvert; defm "" : SIMDConvert; } defm "" : SIMDWiden; defm "" : SIMDWiden; // Narrowing operations multiclass SIMDNarrow baseInst> { defm NARROW_S_#vec_t : SIMD_I<(outs V128:$dst), (ins V128:$low, V128:$high), (outs), (ins), [(set (vec_t V128:$dst), (vec_t (int_wasm_narrow_signed (arg_t V128:$low), (arg_t V128:$high))))], vec#".narrow_"#arg#"_s\t$dst, $low, $high", vec#".narrow_"#arg#"_s", baseInst>; defm NARROW_U_#vec_t : SIMD_I<(outs V128:$dst), (ins V128:$low, V128:$high), (outs), (ins), [(set (vec_t V128:$dst), (vec_t (int_wasm_narrow_unsigned (arg_t V128:$low), (arg_t V128:$high))))], vec#".narrow_"#arg#"_u\t$dst, $low, $high", vec#".narrow_"#arg#"_u", !add(baseInst, 1)>; } defm "" : SIMDNarrow; defm "" : SIMDNarrow; // Lower llvm.wasm.trunc.saturate.* to saturating instructions def : Pat<(v4i32 (int_wasm_trunc_saturate_signed (v4f32 V128:$src))), (fp_to_sint_v4i32_v4f32 (v4f32 V128:$src))>; def : Pat<(v4i32 (int_wasm_trunc_saturate_unsigned (v4f32 V128:$src))), (fp_to_uint_v4i32_v4f32 (v4f32 V128:$src))>; def : Pat<(v2i64 (int_wasm_trunc_saturate_signed (v2f64 V128:$src))), (fp_to_sint_v2i64_v2f64 (v2f64 V128:$src))>; def : Pat<(v2i64 (int_wasm_trunc_saturate_unsigned (v2f64 V128:$src))), (fp_to_uint_v2i64_v2f64 (v2f64 V128:$src))>; // Bitcasts are nops // Matching bitcast t1 to t1 causes strange errors, so avoid repeating types foreach t1 = [v16i8, v8i16, v4i32, v2i64, v4f32, v2f64] in foreach t2 = !foldl( [], [v16i8, v8i16, v4i32, v2i64, v4f32, v2f64], acc, cur, !if(!eq(!cast(t1), !cast(cur)), acc, !listconcat(acc, [cur]) ) ) in def : Pat<(t1 (bitconvert (t2 V128:$v))), (t1 V128:$v)>; //===----------------------------------------------------------------------===// // Quasi-Fused Multiply- Add and Subtract (QFMA/QFMS) //===----------------------------------------------------------------------===// multiclass SIMDQFM baseInst> { defm QFMA_#vec_t : SIMD_I<(outs V128:$dst), (ins V128:$a, V128:$b, V128:$c), (outs), (ins), [(set (vec_t V128:$dst), (int_wasm_qfma (vec_t V128:$a), (vec_t V128:$b), (vec_t V128:$c)))], vec#".qfma\t$dst, $a, $b, $c", vec#".qfma", baseInst>; defm QFMS_#vec_t : SIMD_I<(outs V128:$dst), (ins V128:$a, V128:$b, V128:$c), (outs), (ins), [(set (vec_t V128:$dst), (int_wasm_qfms (vec_t V128:$a), (vec_t V128:$b), (vec_t V128:$c)))], vec#".qfms\t$dst, $a, $b, $c", vec#".qfms", !add(baseInst, 1)>; } defm "" : SIMDQFM; defm "" : SIMDQFM;