Created
January 2, 2020 14:51
-
-
Save benvanik/13c76ca68191870a7c6bee5b139ffed6 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Copyright 2019 Google LLC | |
// | |
// Licensed under the Apache License, Version 2.0 (the "License"); | |
// you may not use this file except in compliance with the License. | |
// You may obtain a copy of the License at | |
// | |
// https://www.apache.org/licenses/LICENSE-2.0 | |
// | |
// Unless required by applicable law or agreed to in writing, software | |
// distributed under the License is distributed on an "AS IS" BASIS, | |
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
// See the License for the specific language governing permissions and | |
// limitations under the License. | |
#ifdef SIMD_OPS | |
#else | |
#define SIMD_OPS | |
#ifdef OP_BASE | |
#else | |
include "third_party/llvm/llvm/projects/google_mlir/include/mlir/IR/OpBase.td" | |
#endif // OP_BASE | |
//===----------------------------------------------------------------------===// | |
// Types used in the SIMD dialect | |
//===----------------------------------------------------------------------===// | |
class SIMD_ElementsAttrBase<Pred predicate, int lanes> : ElementsAttrBase<And<[ | |
predicate, | |
CPred<"$_self.cast<ElementsAttr>().getType().isa<ShapedType>() && " | |
"$_self.cast<ElementsAttr>().getType().hasRank() && " | |
"$_self.cast<ElementsAttr>().getType().getShape() == " | |
"llvm::ArrayRef<int64_t>({" # lanes # "})">]>, | |
lanes # "-lane elements attribute"> { | |
int lanes = lanes; | |
} | |
class SIMD_IntElementsAttr<int width, int lanes> : SIMD_ElementsAttrBase< | |
CPred<"$_self.isa<DenseIntElementsAttr>() &&" | |
"$_self.cast<DenseIntElementsAttr>().getType().getElementType()." | |
"isInteger(" # width # ")">, | |
width # "-bit integer elements attribute", lanes> { | |
int bitWidth = width; | |
} | |
class SIMD_FloatElementsAttr<int width, int lanes> : SIMD_ElementsAttrBase< | |
CPred<"$_self.isa<DenseFPElementsAttr>() &&" | |
"$_self.cast<DenseFPElementsAttr>().getType().getElementType()." | |
"isF" # width # "()">, | |
width # "-bit floating-point elements attribute", lanes> { | |
int bitWidth = width; | |
} | |
def SIMD_I8x16ElementsAttr : SIMD_IntElementsAttr<8, 16>; | |
def SIMD_I16x8ElementsAttr : SIMD_IntElementsAttr<16, 8>; | |
def SIMD_I32x4ElementsAttr : SIMD_IntElementsAttr<32, 4>; | |
def SIMD_I64x2ElementsAttr : SIMD_IntElementsAttr<64, 2>; | |
def SIMD_F32x4ElementsAttr : SIMD_FloatElementsAttr<32, 4>; | |
def SIMD_F64x2ElementsAttr : SIMD_FloatElementsAttr<64, 2>; | |
class SIMD_ImmLaneIdxAttr<int lanes> : TypedAttrBase< | |
I8, "IntegerAttr", And<[ | |
CPred<"$_self.isa<IntegerAttr>()">, | |
CPred<"$_self.cast<IntegerAttr>().getType().isInteger(8)">, | |
CPred<"$_self.cast<IntegerAttr>().getValue().ult(" # lanes # ")">], | |
"lane index in bounds for " # lanes # " lanes"> { | |
int lanes = lanes; | |
let returnType = [{ APInt }]; | |
} | |
def SIMD_ImmLaneIdx2Attr : SIMD_ImmLaneIdxAttr<2>; | |
def SIMD_ImmLaneIdx4Attr : SIMD_ImmLaneIdxAttr<4>; | |
def SIMD_ImmLaneIdx8Attr : SIMD_ImmLaneIdxAttr<8>; | |
def SIMD_ImmLaneIdx16Attr : SIMD_ImmLaneIdxAttr<16>; | |
def SIMD_ImmLaneIdx32Attr : SIMD_ImmLaneIdxAttr<32>; | |
class SIMD_ImmLaneIdxElementsAttr<int lanes, int count> : TypedAttrBase< | |
I8, "IntegerAttr", And<[ | |
CPred<"$_self.isa<DenseIntElementsAttr>()">, | |
CPred<"$_self.cast<DenseIntElementsAttr>().getType().getElementType().isInteger(8)">, | |
CPred<"$_self.cast<DenseIntElementsAttr>().getType().hasRank() && " | |
"$_self.cast<DenseIntElementsAttr>().getType().getShape() == " | |
"llvm::ArrayRef<int64_t>({" # count # "})">, | |
CPred<"$_self.cast<DenseIntElementsAttr>().getValue({0}).ult(" # lanes # ")">], | |
count # " lane indices in bounds for " # lanes # " lanes"> { | |
int lanes = lanes; | |
int count = count; | |
let returnType = [{ APInt }]; | |
} | |
def SIMD_ImmLaneIdx32x16Attr : SIMD_ImmLaneIdxElementsAttr<32, 16>; | |
//===----------------------------------------------------------------------===// | |
// Top-level SIMD dialect and templates | |
//===----------------------------------------------------------------------===// | |
def SIMD_Dialect : Dialect { | |
let name = "simd"; | |
let cppNamespace = "SIMD"; | |
let description = [{ | |
A SIMD dialect for MLIR modeling the WebAssembly SIMD proposal. | |
}]; | |
} | |
class SIMD_Op<string mnemonic, list<OpTrait> traits = []> : | |
Op<SIMD_Op, mnemonic, traits> { | |
// DO NOT SUBMIT | |
} | |
// DO NOT SUBMIT PureOp with NoSideEffect on it | |
class SIMD_V128_UnaryOp<string mnemonic, list<OpTrait> traits = []> : | |
SIMD_Op<mnemonic, !listconcat(traits, [NoSideEffect])> { | |
let arguments = (ins SIMD_V128:$a); | |
let results = (outs SIMD_V128:$result); | |
} | |
class SIMD_V128_BinaryOp<string mnemonic, list<OpTrait> traits = []> : | |
SIMD_Op<mnemonic, !listconcat(traits, [NoSideEffect])> { | |
let arguments = (ins SIMD_V128:$a, SIMD_V128:$b); | |
let results = (outs SIMD_V128:$result); | |
} | |
class SIMD_V128_LanewiseUnaryOp<string mnemonic, Type elementType, int lanes, | |
list<OpTrait> traits = []> : | |
SIMD_Op<mnemonic, !listconcat(traits, [NoSideEffect])> { | |
let elementType = elementType; | |
let lanes = lanes; | |
let arguments = (ins SIMD_V128:$a); | |
let results = (outs SIMD_V128:$result); | |
} | |
class SIMD_V128_LanewiseBinaryOp<string mnemonic, Type elementType, int lanes, | |
list<OpTrait> traits = []> : | |
SIMD_Op<mnemonic, !listconcat(traits, [NoSideEffect])> { | |
let elementType = elementType; | |
let lanes = lanes; | |
let arguments = (ins SIMD_V128:$a, SIMD_V128:$b); | |
let results = (outs SIMD_V128:$result); | |
} | |
//===----------------------------------------------------------------------===// | |
// Constructing SIMD values | |
//===----------------------------------------------------------------------===// | |
// https://github.com/WebAssembly/simd/blob/master/proposals/simd/SIMD.md#constructing-simd-values | |
def SIMD_V128_ConstOp : SIMD_Op<"v128.const", [NoSideEffect]> { | |
let summary = [{ | |
Materializes a constant v128 SIMD value from the 16 immediate bytes. | |
}]; | |
let description = [{ | |
Materializes a constant v128 SIMD value from the 16 immediate bytes in the | |
immediate mode operand `imm`. The v128.const instruction is encoded with 16 | |
immediate bytes which provide the bits of the vector directly. | |
``` | |
v128.const(imm: ImmByte[16]) -> v128 | |
``` | |
}]; | |
// TODO(benvanik): figure out how to verify 16-byte sum. | |
let arguments = (ins ElementsAttr:$imm); | |
let results = (outs SIMD_V128:$result); | |
let builders = [OpBuilder<[{Builder *builder, OperationState &state, | |
ElementsAttr imm}]>]; | |
let parser = [{ return parseConst(parser, result); }]; | |
let printer = [{ printConst(p, *this); }]; | |
} | |
class SIMD_V128_SplatOp<string mnemonic, Type attrType, | |
list<OpTrait> traits = []> : | |
SIMD_Op<mnemonic, !listconcat(traits, [NoSideEffect])> { | |
let summary = [{ | |
Construct a vector with `x` replicated to all lanes. | |
}]; | |
let description = [{ | |
``` | |
def S.splat(x): | |
result = S.New() | |
for i in range(S.Lanes): | |
result[i] = x | |
return result | |
``` | |
}]; | |
let arguments = (ins attrType:$x); | |
let results = (outs SIMD_V128:$result); | |
let parser = "return parseSplat<" # attrType # ">(parser, result);"; | |
let printer = "printSplat<" # attrType # ">(p, *this);"; | |
} | |
def SIMD_I8x16_SplatOp : SIMD_V128_SplatOp<"i8x16.splat", SIMD_I8x16ElementsAttr> {} | |
def SIMD_I16x8_SplatOp : SIMD_V128_SplatOp<"i16x8.splat", SIMD_I16x8ElementsAttr> {} | |
def SIMD_I32x4_SplatOp : SIMD_V128_SplatOp<"i32x4.splat", SIMD_I32x4ElementsAttr> {} | |
def SIMD_I64x2_SplatOp : SIMD_V128_SplatOp<"i64x2.splat", SIMD_I64x2ElementsAttr> {} | |
def SIMD_F32x4_SplatOp : SIMD_V128_SplatOp<"f32x4.splat", SIMD_F32x4ElementsAttr> {} | |
def SIMD_F64x2_SplatOp : SIMD_V128_SplatOp<"f64x2.splat", SIMD_F64x2ElementsAttr> {} | |
//===----------------------------------------------------------------------===// | |
// Accessing lanes | |
//===----------------------------------------------------------------------===// | |
// https://github.com/WebAssembly/simd/blob/master/proposals/simd/SIMD.md#accessing-lanes | |
class SIMD_V128_ExtractLaneOp<string mnemonic, Type immType, Type elementType, | |
list<OpTrait> traits = []> : | |
SIMD_Op<mnemonic, !listconcat(traits, [NoSideEffect])> { | |
let summary = [{ | |
Extracts the scalar value of lane specified. | |
}]; | |
let description = [{ | |
Extract the scalar value of lane specified in the immediate mode operand | |
`imm` in `a`. The `{interpretation}.extract_lane` instructions are encoded | |
with one immediate byte providing the index of the lane to extract. | |
``` | |
def S.extract_lane(a, imm): | |
return a[imm] | |
``` | |
}]; | |
let arguments = (ins SIMD_V128:$a, immType:$imm); | |
let results = (outs elementType:$result); | |
let parser = "return parseExtractLane<" # immType # ", " # elementType # ">(parser, result);"; | |
let printer = "printExtractLane<" # immType # ", " # elementType # ">(p, *this);"; | |
} | |
def SIMD_I8x16_ExtractLaneOp : SIMD_V128_ExtractLaneOp<"i8x16.extract_lane", SIMD_ImmLaneIdx16Attr, I8> {} | |
def SIMD_I16x8_ExtractLaneOp : SIMD_V128_ExtractLaneOp<"i16x8.extract_lane", SIMD_ImmLaneIdx8Attr, I16> {} | |
def SIMD_I32x4_ExtractLaneOp : SIMD_V128_ExtractLaneOp<"i32x4.extract_lane", SIMD_ImmLaneIdx4Attr, I32> {} | |
def SIMD_I64x2_ExtractLaneOp : SIMD_V128_ExtractLaneOp<"i64x2.extract_lane", SIMD_ImmLaneIdx2Attr, I64> {} | |
def SIMD_F32x4_ExtractLaneOp : SIMD_V128_ExtractLaneOp<"f32x4.extract_lane", SIMD_ImmLaneIdx4Attr, F32> {} | |
def SIMD_F64x2_ExtractLaneOp : SIMD_V128_ExtractLaneOp<"f64x2.extract_lane", SIMD_ImmLaneIdx2Attr, F64> {} | |
class SIMD_V128_ReplaceLaneOp<string mnemonic, Type immType, Type elementType, | |
list<OpTrait> traits = []> : | |
SIMD_Op<mnemonic, !listconcat(traits, [NoSideEffect])> { | |
let summary = [{ | |
Replaces the value of lane `imm` within `a` with `x`. | |
}]; | |
let description = [{ | |
Return a new vector with lanes identical to `a`, except for the lane | |
specified in the immediate mode operand `imm` which has the value `x`. The | |
`{interpretation}.replace_lane` instructions are encoded with an immediate | |
byte providing the index of the lane the value of which is to be replaced. | |
``` | |
def S.replace_lane(a, imm, x): | |
result = S.New() | |
for j in range(S.Lanes): | |
result[j] = a[j] | |
result[imm] = x | |
return result | |
``` | |
The input lane value, `x`, is interpreted the same way as for the splat | |
instructions. For the i8 and i16 lanes, the high bits of `x` are ignored. | |
}]; | |
let arguments = (ins SIMD_V128:$a, immType:$imm, elementType:$x); | |
let results = (outs SIMD_V128:$result); | |
let parser = "return parseReplaceLane<" # immType # ", " # elementType # ">(parser, result);"; | |
let printer = "printReplaceLane<" # immType # ", " # elementType # ">(p, *this);"; | |
} | |
def SIMD_I8x16_ReplaceLaneOp : SIMD_V128_ReplaceLaneOp<"i8x16.replace_lane", SIMD_ImmLaneIdx16Attr, I8> {} | |
def SIMD_I16x8_ReplaceLaneOp : SIMD_V128_ReplaceLaneOp<"i16x8.replace_lane", SIMD_ImmLaneIdx8Attr, I16> {} | |
def SIMD_I32x4_ReplaceLaneOp : SIMD_V128_ReplaceLaneOp<"i32x4.replace_lane", SIMD_ImmLaneIdx4Attr, I32> {} | |
def SIMD_I64x2_ReplaceLaneOp : SIMD_V128_ReplaceLaneOp<"i64x2.replace_lane", SIMD_ImmLaneIdx2Attr, I64> {} | |
def SIMD_F32x4_ReplaceLaneOp : SIMD_V128_ReplaceLaneOp<"f32x4.replace_lane", SIMD_ImmLaneIdx4Attr, F32> {} | |
def SIMD_F64x2_ReplaceLaneOp : SIMD_V128_ReplaceLaneOp<"f64x2.replace_lane", SIMD_ImmLaneIdx2Attr, F64> {} | |
class SIMD_V128_ShuffleOp<string mnemonic, Type immType, int lanes, | |
list<OpTrait> traits = []> : | |
SIMD_Op<mnemonic, !listconcat(traits, [NoSideEffect])> { | |
let summary = [{ | |
Shuffles a using immediate indicies. | |
}]; | |
let description = [{ | |
Returns a new vector with lanes selected from the lanes of the two input | |
vectors `a` and `b` specified in the 16 byte wide immediate mode operand | |
`imm`. This instruction is encoded with 16 bytes providing the indices of | |
the elements to return. The indices `i` in range `[0, 15]` select the `i`-th | |
element of `a`. The indices in range `[16, 31]` select the `i - 16`-th | |
element of `b`. | |
``` | |
def S.shuffle(a, b, s): | |
result = S.New() | |
for i in range(S.Lanes): | |
if s[i] < S.lanes: | |
result[i] = a[s[i]] | |
else: | |
result[i] = b[s[i] - S.lanes] | |
return result | |
``` | |
}]; | |
let lanes = lanes; | |
let arguments = (ins SIMD_V128:$a, SIMD_V128:$b, immType:$imm); | |
let results = (outs SIMD_V128:$result); | |
let parser = "return parseShuffle<" # immType # ", " # lanes # ">(parser, result);"; | |
let printer = "printShuffle<" # immType # ", " # lanes # ">(p, *this);"; | |
} | |
def SIMD_V8x16_ShuffleOp : | |
SIMD_V128_ShuffleOp<"v8x16.shuffle", SIMD_ImmLaneIdx32Attr, 16> {} | |
class SIMD_V128_SwizzleOp<string mnemonic, int lanes, | |
list<OpTrait> traits = []> : | |
SIMD_Op<mnemonic, !listconcat(traits, [NoSideEffect])> { | |
let summary = [{ | |
Shuffles a using variable indicies. | |
}]; | |
let description = [{ | |
Returns a new vector with lanes selected from the lanes of the first input | |
vector `a` specified in the second input vector `s`. The indices `i` in | |
range `[0, 15]` select the `i`-th element of `a`. For indices outside of the | |
range the resulting lane is 0. | |
``` | |
def S.swizzle(a, s): | |
result = S.New() | |
for i in range(S.Lanes): | |
if s[i] < S.lanes: | |
result[i] = a[s[i]] | |
else: | |
result[i] = 0 | |
return result | |
``` | |
}]; | |
let lanes = lanes; | |
let arguments = (ins SIMD_V128:$a, SIMD_V128:$s); | |
let results = (outs SIMD_V128:$result); | |
let parser = "return parseSwizzle<" # lanes # ">(parser, result);"; | |
let printer = "parseSwizzle<" # lanes # ">(p, *this);"; | |
} | |
def SIMD_V8x16_SwizzleOp : SIMD_V128_SwizzleOp<"v8x16.swizzle", 16> {} | |
//===----------------------------------------------------------------------===// | |
// Integer arithmetic | |
//===----------------------------------------------------------------------===// | |
// https://github.com/WebAssembly/simd/blob/master/proposals/simd/SIMD.md#integer-arithmetic | |
// | |
// Wrapping integer arithmetic discards the high bits of the result. | |
// | |
// ``` | |
// def S.Reduce(x): | |
// bitmask = (1 << S.LaneBits) - 1 | |
// return x & bitmask | |
// ``` | |
// | |
// There is no integer division operation provided here. This operation is not | |
// commonly part of bit 128-bit SIMD ISAs. | |
class SIMD_V128_AddOp<string mnemonic, Type elementType, int lanes, | |
list<OpTrait> traits = []> : | |
SIMD_V128_LanewiseBinaryOp<mnemonic, elementType, lanes, traits> { | |
let summary = [{ | |
Lane-wise wrapping integer addition. | |
}]; | |
let description = [{ | |
``` | |
def S.add(a, b): | |
def add(x, y): | |
return S.Reduce(x + y) | |
return S.lanewise_binary(add, a, b) | |
``` | |
}]; | |
let arguments = (ins SIMD_V128:$a, SIMD_V128:$b); | |
let results = (outs SIMD_V128:$result); | |
let parser = "return parseAdd<" # elementType # ", " # lanes # ">(parser, result);"; | |
let printer = "parseAdd<" # elementType # ", " # lanes # ">(p, *this);"; | |
} | |
def SIMD_I8x16_AddOp : SIMD_V128_AddOp<"i8x16.add", I8, 16> {} | |
def SIMD_I16x8_AddOp : SIMD_V128_AddOp<"i16x8.add", I16, 8> {} | |
def SIMD_I32x4_AddOp : SIMD_V128_AddOp<"i32x4.add", I32, 4> {} | |
def SIMD_I64x2_AddOp : SIMD_V128_AddOp<"i64x2.add", I64, 2> {} | |
class SIMD_V128_SubOp<string mnemonic, Type elementType, int lanes, | |
list<OpTrait> traits = []> : | |
SIMD_V128_LanewiseBinaryOp<mnemonic, elementType, lanes, traits> { | |
let summary = [{ | |
Lane-wise wrapping integer subtraction. | |
}]; | |
let description = [{ | |
``` | |
def S.sub(a, b): | |
def sub(x, y): | |
return S.Reduce(x - y) | |
return S.lanewise_binary(sub, a, b) | |
``` | |
}]; | |
let arguments = (ins SIMD_V128:$a, SIMD_V128:$b); | |
let results = (outs SIMD_V128:$result); | |
let parser = "return parseSub<" # elementType # ", " # lanes # ">(parser, result);"; | |
let printer = "parseSub<" # elementType # ", " # lanes # ">(p, *this);"; | |
} | |
def SIMD_I8x16_SubOp : SIMD_V128_SubOp<"i8x16.sub", I8, 16> {} | |
def SIMD_I16x8_SubOp : SIMD_V128_SubOp<"i16x8.sub", I16, 8> {} | |
def SIMD_I32x4_SubOp : SIMD_V128_SubOp<"i32x4.sub", I32, 4> {} | |
def SIMD_I64x2_SubOp : SIMD_V128_SubOp<"i64x2.sub", I64, 2> {} | |
class SIMD_V128_MulOp<string mnemonic, Type elementType, int lanes, | |
list<OpTrait> traits = []> : | |
SIMD_V128_LanewiseBinaryOp<mnemonic, elementType, lanes, traits> { | |
let summary = [{ | |
Lane-wise wrapping integer multiplication. | |
}]; | |
let description = [{ | |
``` | |
def S.mul(a, b): | |
def mul(x, y): | |
return S.Reduce(x * y) | |
return S.lanewise_binary(mul, a, b) | |
``` | |
}]; | |
let arguments = (ins SIMD_V128:$a, SIMD_V128:$b); | |
let results = (outs SIMD_V128:$result); | |
let parser = "return parseMul<" # elementType # ", " # lanes # ">(parser, result);"; | |
let printer = "parseMul<" # elementType # ", " # lanes # ">(p, *this);"; | |
} | |
def SIMD_I16x8_MulOp : SIMD_V128_MulOp<"i16x8.mul", I16, 8> {} | |
def SIMD_I32x4_MulOp : SIMD_V128_MulOp<"i32x4.mul", I32, 4> {} | |
def SIMD_I64x2_MulOp : SIMD_V128_MulOp<"i64x2.mul", I64, 2> {} | |
class SIMD_V128_NegOp<string mnemonic, Type elementType, int lanes, | |
list<OpTrait> traits = []> : | |
SIMD_V128_LanewiseUnaryOp<mnemonic, elementType, lanes, traits> { | |
let summary = [{ | |
Lane-wise wrapping integer negation. | |
}]; | |
let description = [{ | |
In wrapping arithmetic, `y = -x` is the unique value such that `x + y == 0`. | |
``` | |
def S.neg(a): | |
def neg(x): | |
return S.Reduce(-x) | |
return S.lanewise_unary(neg, a) | |
``` | |
}]; | |
let arguments = (ins SIMD_V128:$a); | |
let results = (outs SIMD_V128:$result); | |
let parser = "return parseNeg<" # elementType # ", " # lanes # ">(parser, result);"; | |
let printer = "parseNeg<" # elementType # ", " # lanes # ">(p, *this);"; | |
} | |
def SIMD_I8x16_NegOp : SIMD_V128_NegOp<"i8x16.neg", I8, 16> {} | |
def SIMD_I16x8_NegOp : SIMD_V128_NegOp<"i16x8.neg", I16, 8> {} | |
def SIMD_I32x4_NegOp : SIMD_V128_NegOp<"i32x4.neg", I32, 4> {} | |
def SIMD_I64x2_NegOp : SIMD_V128_NegOp<"i64x2.neg", I64, 2> {} | |
//===----------------------------------------------------------------------===// | |
// Saturating integer arithmetic | |
//===----------------------------------------------------------------------===// | |
// https://github.com/WebAssembly/simd/blob/master/proposals/simd/SIMD.md#saturating-integer-arithmetic | |
// | |
// Saturating integer arithmetic behaves differently on signed and unsigned | |
// lanes. It is only defined here for 8-bit and 16-bit integer lanes. | |
// | |
// ``` | |
// def S.SignedSaturate(x): | |
// if x < S.Smin: | |
// return S.Smin | |
// if x > S.Smax: | |
// return S.Smax | |
// return x | |
// | |
// def S.UnsignedSaturate(x): | |
// if x < 0: | |
// return 0 | |
// if x > S.Umax: | |
// return S.Umax | |
// return x | |
// ``` | |
class SIMD_V128_AddSaturateSOp<string mnemonic, Type elementType, int lanes, | |
list<OpTrait> traits = []> : | |
SIMD_V128_LanewiseBinaryOp<mnemonic, elementType, lanes, traits> { | |
let summary = [{ | |
Lane-wise wrapping integer addition. | |
}]; | |
let description = [{ | |
``` | |
def S.add_saturate_s(a, b): | |
def addsat(x, y): | |
return S.SignedSaturate(x + y) | |
return S.lanewise_binary(addsat, S.AsSigned(a), S.AsSigned(b)) | |
``` | |
}]; | |
let arguments = (ins SIMD_V128:$a, SIMD_V128:$b); | |
let results = (outs SIMD_V128:$result); | |
let parser = "return parseAddSaturate<true, " # elementType # ", " # lanes # ">(parser, result);"; | |
let printer = "parseAddSaturate<true, " # elementType # ", " # lanes # ">(p, *this);"; | |
} | |
def SIMD_I8x16_AddSaturateUOp : SIMD_V128_AddSaturateUOp<"i8x16.add_saturate_u", I8, 16> {} | |
def SIMD_I16x8_AddSaturateUOp : SIMD_V128_AddSaturateUOp<"i16x8.add_saturate_u", I16, 8> {} | |
class SIMD_V128_AddSaturateUOp<string mnemonic, Type elementType, int lanes, | |
list<OpTrait> traits = []> : | |
SIMD_V128_LanewiseBinaryOp<mnemonic, elementType, lanes, traits> { | |
let summary = [{ | |
Lane-wise wrapping integer addition. | |
}]; | |
let description = [{ | |
``` | |
def S.add_saturate_u(a, b): | |
def addsat(x, y): | |
return S.UnsignedSaturate(x + y) | |
return S.lanewise_binary(addsat, S.AsUnsigned(a), S.AsUnsigned(b)) | |
``` | |
}]; | |
let arguments = (ins SIMD_V128:$a, SIMD_V128:$b); | |
let results = (outs SIMD_V128:$result); | |
let parser = "return parseAddSaturate<false, " # elementType # ", " # lanes # ">(parser, result);"; | |
let printer = "parseAddSaturate<false, " # elementType # ", " # lanes # ">(p, *this);"; | |
} | |
def SIMD_I8x16_AddSaturateUOp : SIMD_V128_AddSaturateUOp<"i8x16.add_saturate_u", I8, 16> {} | |
def SIMD_I16x8_AddSaturateUOp : SIMD_V128_AddSaturateUOp<"i16x8.add_saturate_u", I16, 8> {} | |
class SIMD_V128_SubSaturateSOp<string mnemonic, Type elementType, int lanes, | |
list<OpTrait> traits = []> : | |
SIMD_V128_LanewiseBinaryOp<mnemonic, elementType, lanes, traits> { | |
let summary = [{ | |
Lane-wise wrapping integer addition. | |
}]; | |
let description = [{ | |
``` | |
def S.sub_saturate_s(a, b): | |
def subsat(x, y): | |
return S.SignedSaturate(x - y) | |
return S.lanewise_binary(subsat, S.AsSigned(a), S.AsSigned(b)) | |
``` | |
}]; | |
let arguments = (ins SIMD_V128:$a, SIMD_V128:$b); | |
let results = (outs SIMD_V128:$result); | |
let parser = "return parseSubSaturate<true, " # elementType # ", " # lanes # ">(parser, result);"; | |
let printer = "parseSubSaturate<true, " # elementType # ", " # lanes # ">(p, *this);"; | |
} | |
def SIMD_I8x16_SubSaturateUOp : SIMD_V128_SubSaturateUOp<"i8x16.sub_saturate_u", I8, 16> {} | |
def SIMD_I16x8_SubSaturateUOp : SIMD_V128_SubSaturateUOp<"i16x8.sub_saturate_u", I16, 8> {} | |
class SIMD_V128_SubSaturateUOp<string mnemonic, Type elementType, int lanes, | |
list<OpTrait> traits = []> : | |
SIMD_V128_LanewiseBinaryOp<mnemonic, elementType, lanes, traits> { | |
let summary = [{ | |
Lane-wise wrapping integer addition. | |
}]; | |
let description = [{ | |
``` | |
def S.sub_saturate_u(a, b): | |
def subsat(x, y): | |
return S.UnsignedSaturate(x - y) | |
return S.lanewise_binary(subsat, S.AsUnsigned(a), S.AsUnsigned(b)) | |
``` | |
}]; | |
let arguments = (ins SIMD_V128:$a, SIMD_V128:$b); | |
let results = (outs SIMD_V128:$result); | |
let parser = "return parseSubSaturate<false, " # elementType # ", " # lanes # ">(parser, result);"; | |
let printer = "parseSubSaturate<false, " # elementType # ", " # lanes # ">(p, *this);"; | |
} | |
def SIMD_I8x16_SubSaturateUOp : SIMD_V128_SubSaturateUOp<"i8x16.sub_saturate_u", I8, 16> {} | |
def SIMD_I16x8_SubSaturateUOp : SIMD_V128_SubSaturateUOp<"i16x8.sub_saturate_u", I16, 8> {} | |
//===----------------------------------------------------------------------===// | |
// Bit shifts | |
//===----------------------------------------------------------------------===// | |
// https://github.com/WebAssembly/simd/blob/master/proposals/simd/SIMD.md#bit-shifts | |
class SIMD_V128_ShlOp<string mnemonic, Type elementType, int lanes, | |
list<OpTrait> traits = []> : | |
SIMD_Op<mnemonic, !listconcat(traits, [NoSideEffect])> { | |
let elementType = elementType; | |
let lanes = lanes; | |
let summary = [{ | |
Lane-wise bit shift. | |
}]; | |
let description = [{ | |
Shift the bits in each lane to the left by the same amount. The shift count | |
is taken modulo lane width: | |
``` | |
def S.shl(a, y): | |
# Number of bits to shift: 0 .. S.LaneBits - 1. | |
amount = y mod S.LaneBits | |
def shift(x): | |
return S.Reduce(x << amount) | |
return S.lanewise_unary(shift, a) | |
``` | |
}]; | |
let arguments = (ins SIMD_V128:$a, I32Attr:$y); | |
let results = (outs SIMD_V128:$result); | |
let parser = "return parseShl<" # elementType # ", " # lanes # ">(parser, result);"; | |
let printer = "parseShl<" # elementType # ", " # lanes # ">(p, *this);"; | |
} | |
def SIMD_I8x16_ShlOp : SIMD_V128_ShlOp<"i8x16.shl", I8, 16> {} | |
def SIMD_I16x8_ShlOp : SIMD_V128_ShlOp<"i16x8.shl", I16, 8> {} | |
def SIMD_I32x4_ShlOp : SIMD_V128_ShlOp<"i32x4.shl", I32, 4> {} | |
def SIMD_I64x2_ShlOp : SIMD_V128_ShlOp<"i64x2.shl", I64, 2> {} | |
class SIMD_V128_ShrSOp<string mnemonic, Type elementType, int lanes, | |
list<OpTrait> traits = []> : | |
SIMD_Op<mnemonic, !listconcat(traits, [NoSideEffect])> { | |
let elementType = elementType; | |
let lanes = lanes; | |
let summary = [{ | |
Lane-wise arithmetic right bit shift. | |
}]; | |
let description = [{ | |
Shift the bits in each lane to the right by the same amount. The shift count | |
is taken modulo lane width. This is an arithmetic right shift. | |
``` | |
def S.shr_s(a, y): | |
# Number of bits to shift: 0 .. S.LaneBits - 1. | |
amount = y mod S.LaneBits | |
def shift(x): | |
return x >> amount | |
return S.lanewise_unary(shift, S.AsSigned(a)) | |
``` | |
}]; | |
let arguments = (ins SIMD_V128:$a, I32Attr:$y); | |
let results = (outs SIMD_V128:$result); | |
let parser = "return parseShr<true, " # elementType # ", " # lanes # ">(parser, result);"; | |
let printer = "parseShr<true, " # elementType # ", " # lanes # ">(p, *this);"; | |
} | |
def SIMD_I8x16_ShrSOp : SIMD_V128_ShrSOp<"i8x16.shr_s", I8, 16> {} | |
def SIMD_I16x8_ShrSOp : SIMD_V128_ShrSOp<"i16x8.shr_s", I16, 8> {} | |
def SIMD_I32x4_ShrSOp : SIMD_V128_ShrSOp<"i32x4.shr_s", I32, 4> {} | |
def SIMD_I64x2_ShrSOp : SIMD_V128_ShrSOp<"i64x2.shr_s", I64, 2> {} | |
class SIMD_V128_ShrUOp<string mnemonic, Type elementType, int lanes, | |
list<OpTrait> traits = []> : | |
SIMD_Op<mnemonic, !listconcat(traits, [NoSideEffect])> { | |
let elementType = elementType; | |
let lanes = lanes; | |
let summary = [{ | |
Lane-wise logical right bit shift. | |
}]; | |
let description = [{ | |
Shift the bits in each lane to the right by the same amount. The shift count | |
is taken modulo lane width. This is a logical right shift. | |
``` | |
def S.shr_u(a, y): | |
# Number of bits to shift: 0 .. S.LaneBits - 1. | |
amount = y mod S.LaneBits | |
def shift(x): | |
return x >> amount | |
return S.lanewise_unary(shift, S.AsUnsigned(a)) | |
``` | |
}]; | |
let arguments = (ins SIMD_V128:$a, I32Attr:$y); | |
let results = (outs SIMD_V128:$result); | |
let parser = "return parseShr<false, " # elementType # ", " # lanes # ">(parser, result);"; | |
let printer = "parseShr<false, " # elementType # ", " # lanes # ">(p, *this);"; | |
} | |
def SIMD_I8x16_ShrUOp : SIMD_V128_ShrUOp<"i8x16.shr_u", I8, 16> {} | |
def SIMD_I16x8_ShrUOp : SIMD_V128_ShrUOp<"i16x8.shr_u", I16, 8> {} | |
def SIMD_I32x4_ShrUOp : SIMD_V128_ShrUOp<"i32x4.shr_u", I32, 4> {} | |
def SIMD_I64x2_ShrUOp : SIMD_V128_ShrUOp<"i64x2.shr_u", I64, 2> {} | |
//===----------------------------------------------------------------------===// | |
// Bitwise operations | |
//===----------------------------------------------------------------------===// | |
// https://github.com/WebAssembly/simd/blob/master/proposals/simd/SIMD.md#bitwise-operations | |
def SIMD_V128_AndOp : SIMD_V128_BinaryOp<"v128.and"> { | |
let summary = [{ | |
Bit-wise AND of bits. | |
}]; | |
let description = [{ | |
Acts like C's `&` operator on an unsigned type. | |
}]; | |
let parser = "return parseAnd(parser, result);"; | |
let printer = "parseAnd(p, *this);"; | |
} | |
def SIMD_V128_OrOp : SIMD_V128_BinaryOp<"v128.or"> { | |
let summary = [{ | |
Bit-wise OR of bits. | |
}]; | |
let description = [{ | |
Acts like C's `|` operator on an unsigned type. | |
}]; | |
let parser = "return parseOr(parser, result);"; | |
let printer = "parseOr(p, *this);"; | |
} | |
def SIMD_V128_XorOp : SIMD_V128_BinaryOp<"v128.xor"> { | |
let summary = [{ | |
Bit-wise XOR of bits. | |
}]; | |
let description = [{ | |
Acts like C's `^` operator on an unsigned type. | |
}]; | |
let parser = "return parseXor(parser, result);"; | |
let printer = "parseXor(p, *this);"; | |
} | |
def SIMD_V128_NotOp : SIMD_V128_UnaryOp<"v128.not"> { | |
let summary = [{ | |
Bit-wise NOT of bits. | |
}]; | |
let description = [{ | |
Acts like C's `~` operator on an unsigned type. | |
}]; | |
let parser = "return parseNot(parser, result);"; | |
let printer = "parseNot(p, *this);"; | |
} | |
def SIMD_V128_AndNotOp : SIMD_V128_BinaryOp<"v128.andnot"> { | |
let summary = [{ | |
Bit-wise AND of bits and the logical inverse of the operand. | |
}]; | |
let description = [{ | |
Bitwise AND of bits of `a` and the logical inverse of bits of `b`. This | |
operation is equivalent to `v128.and(a, v128.not(b))`. | |
}]; | |
let parser = "return parseAndNot(parser, result);"; | |
let printer = "parseAndNot(p, *this);"; | |
} | |
def SIMD_V128_BitSelectOp : SIMD_Op<"v128.bitselect", [NoSideEffect]> { | |
let summary = [{ | |
Bit-wise select based on control bits. | |
}]; | |
let description = [{ | |
Use the bits in the control mask `c` to select the corresponding bit from | |
`v1` when 1 and `v2` when 0. This is the same as | |
`v128.or(v128.and(v1, c), v128.and(v2, v128.not(c)))`. | |
}]; | |
let arguments = (ins SIMD_V128:$v1, SIMD_V128:$v2, SIMD_V128:$c); | |
let results = (outs SIMD_V128:$result); | |
let parser = "return parseBitSelect(parser, result);"; | |
let printer = "parseBitSelect(p, *this);"; | |
} | |
//===----------------------------------------------------------------------===// | |
// Boolean horizontal reductions | |
//===----------------------------------------------------------------------===// | |
// https://github.com/WebAssembly/simd/blob/master/proposals/simd/SIMD.md#boolean-horizontal-reductions | |
class SIMD_V128_AnyTrueOp<string mnemonic, Type elementType, int lanes, | |
list<OpTrait> traits = []> : | |
SIMD_UnaryOp<mnemonic, !listconcat(traits, [NoSideEffect])> { | |
let elementType = elementType; | |
let lanes = lanes; | |
let summary = [{ | |
Reduces all lanes to a single scalar of 0 or 1. | |
}]; | |
let description = [{ | |
Return 1 if any lane in `a` is non-zero, 0 otherwise. | |
``` | |
def S.any_true(a): | |
for i in range(S.Lanes): | |
if a[i] != 0: | |
return 1 | |
return 0 | |
``` | |
}]; | |
let parser = "return parseAnyTrue<" # elementType # ", " # lanes # ">(parser, result);"; | |
let printer = "parseAnyTrue<" # elementType # ", " # lanes # ">(p, *this);"; | |
} | |
def SIMD_I8x16_AnyTrueOp : SIMD_V128_AnyTrueOp<"i8x16.any_true", I8, 16> {} | |
def SIMD_I16x8_AnyTrueOp : SIMD_V128_AnyTrueOp<"i16x8.any_true", I16, 8> {} | |
def SIMD_I32x4_AnyTrueOp : SIMD_V128_AnyTrueOp<"i32x4.any_true", I32, 4> {} | |
class SIMD_V128_AllTrueOp<string mnemonic, Type elementType, int lanes, | |
list<OpTrait> traits = []> : | |
SIMD_UnaryOp<mnemonic, !listconcat(traits, [NoSideEffect])> { | |
let elementType = elementType; | |
let lanes = lanes; | |
let summary = [{ | |
Reduces all lanes to a single scalar of 0 or 1. | |
}]; | |
let description = [{ | |
Return 1 if all lanes in `a` is non-zero, 0 otherwise. | |
``` | |
def S.any_true(a): | |
for i in range(S.Lanes): | |
if a[i] == 0: | |
return 0 | |
return 1 | |
``` | |
}]; | |
let parser = "return parseAllTrue<" # elementType # ", " # lanes # ">(parser, result);"; | |
let printer = "parseAllTrue<" # elementType # ", " # lanes # ">(p, *this);"; | |
} | |
def SIMD_I8x16_AllTrueOp : SIMD_V128_AllTrueOp<"i8x16.all_true", I8, 16> {} | |
def SIMD_I16x8_AllTrueOp : SIMD_V128_AllTrueOp<"i16x8.all_true", I16, 8> {} | |
def SIMD_I32x4_AllTrueOp : SIMD_V128_AllTrueOp<"i32x4.all_true", I32, 4> {} | |
//===----------------------------------------------------------------------===// | |
// Comparisons | |
//===----------------------------------------------------------------------===// | |
// https://github.com/WebAssembly/simd/blob/master/proposals/simd/SIMD.md#comparisons | |
// DO NOT SUBMIT | |
//===----------------------------------------------------------------------===// | |
// Load and store | |
//===----------------------------------------------------------------------===// | |
// https://github.com/WebAssembly/simd/blob/master/proposals/simd/SIMD.md#load-and-store | |
// DO NOT SUBMIT | |
//===----------------------------------------------------------------------===// | |
// Floating-point sign bit operations | |
//===----------------------------------------------------------------------===// | |
// https://github.com/WebAssembly/simd/blob/master/proposals/simd/SIMD.md#floating-point-sign-bit-operations | |
// DO NOT SUBMIT | |
//===----------------------------------------------------------------------===// | |
// Floating-point min and max | |
//===----------------------------------------------------------------------===// | |
// https://github.com/WebAssembly/simd/blob/master/proposals/simd/SIMD.md#floating-point-min-and-max | |
// DO NOT SUBMIT | |
//===----------------------------------------------------------------------===// | |
// Floating-point arithmetic | |
//===----------------------------------------------------------------------===// | |
// https://github.com/WebAssembly/simd/blob/master/proposals/simd/SIMD.md#floating-point-arithmetic | |
// DO NOT SUBMIT | |
//===----------------------------------------------------------------------===// | |
// Conversions | |
//===----------------------------------------------------------------------===// | |
// https://github.com/WebAssembly/simd/blob/master/proposals/simd/SIMD.md#conversions | |
// DO NOT SUBMIT | |
#endif // SIMD_OPS |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment