Skip to content

Commit 024a7b6

Browse files
lforg37Ferdinand LemaireJessica Paquette
authored andcommitted
[mlir][wasm] Support for numeric instruction in Wasm importer
--------- Co-authored-by: Ferdinand Lemaire <[email protected]> Co-authored-by: Jessica Paquette <[email protected]>
1 parent 4441032 commit 024a7b6

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

42 files changed

+1585
-0
lines changed

mlir/include/mlir/Target/Wasm/WasmBinaryEncoding.h

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,72 @@ struct WasmBinaryEncoding {
2424
static constexpr std::byte constI64{0x42};
2525
static constexpr std::byte constFP32{0x43};
2626
static constexpr std::byte constFP64{0x44};
27+
28+
// Numeric operations.
29+
static constexpr std::byte clzI32{0x67};
30+
static constexpr std::byte ctzI32{0x68};
31+
static constexpr std::byte popcntI32{0x69};
32+
static constexpr std::byte addI32{0x6A};
33+
static constexpr std::byte subI32{0x6B};
34+
static constexpr std::byte mulI32{0x6C};
35+
static constexpr std::byte divSI32{0x6d};
36+
static constexpr std::byte divUI32{0x6e};
37+
static constexpr std::byte remSI32{0x6f};
38+
static constexpr std::byte remUI32{0x70};
39+
static constexpr std::byte andI32{0x71};
40+
static constexpr std::byte orI32{0x72};
41+
static constexpr std::byte xorI32{0x73};
42+
static constexpr std::byte shlI32{0x74};
43+
static constexpr std::byte shrSI32{0x75};
44+
static constexpr std::byte shrUI32{0x76};
45+
static constexpr std::byte rotlI32{0x77};
46+
static constexpr std::byte rotrI32{0x78};
47+
static constexpr std::byte clzI64{0x79};
48+
static constexpr std::byte ctzI64{0x7A};
49+
static constexpr std::byte popcntI64{0x7B};
50+
static constexpr std::byte addI64{0x7C};
51+
static constexpr std::byte subI64{0x7D};
52+
static constexpr std::byte mulI64{0x7E};
53+
static constexpr std::byte divSI64{0x7F};
54+
static constexpr std::byte divUI64{0x80};
55+
static constexpr std::byte remSI64{0x81};
56+
static constexpr std::byte remUI64{0x82};
57+
static constexpr std::byte andI64{0x83};
58+
static constexpr std::byte orI64{0x84};
59+
static constexpr std::byte xorI64{0x85};
60+
static constexpr std::byte shlI64{0x86};
61+
static constexpr std::byte shrSI64{0x87};
62+
static constexpr std::byte shrUI64{0x88};
63+
static constexpr std::byte rotlI64{0x89};
64+
static constexpr std::byte rotrI64{0x8A};
65+
static constexpr std::byte absF32{0x8B};
66+
static constexpr std::byte negF32{0x8C};
67+
static constexpr std::byte ceilF32{0x8D};
68+
static constexpr std::byte floorF32{0x8E};
69+
static constexpr std::byte truncF32{0x8F};
70+
static constexpr std::byte sqrtF32{0x91};
71+
static constexpr std::byte addF32{0x92};
72+
static constexpr std::byte subF32{0x93};
73+
static constexpr std::byte mulF32{0x94};
74+
static constexpr std::byte divF32{0x95};
75+
static constexpr std::byte minF32{0x96};
76+
static constexpr std::byte maxF32{0x97};
77+
static constexpr std::byte copysignF32{0x98};
78+
static constexpr std::byte absF64{0x99};
79+
static constexpr std::byte negF64{0x9A};
80+
static constexpr std::byte ceilF64{0x9B};
81+
static constexpr std::byte floorF64{0x9C};
82+
static constexpr std::byte truncF64{0x9D};
83+
static constexpr std::byte sqrtF64{0x9F};
84+
static constexpr std::byte addF64{0xA0};
85+
static constexpr std::byte subF64{0xA1};
86+
static constexpr std::byte mulF64{0xA2};
87+
static constexpr std::byte divF64{0xA3};
88+
static constexpr std::byte minF64{0xA4};
89+
static constexpr std::byte maxF64{0xA5};
90+
static constexpr std::byte copysignF64{0xA6};
91+
static constexpr std::byte wrap{0xA7};
92+
2793
};
2894

2995
/// Byte encodings of types in Wasm binaries

mlir/lib/Target/Wasm/TranslateFromWasm.cpp

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -232,6 +232,20 @@ class ExpressionParser {
232232
parseConstInst(OpBuilder &builder,
233233
std::enable_if_t<std::is_arithmetic_v<valueT>> * = nullptr);
234234

235+
/// Construct an operation with \p numOperands operands and a single result.
236+
/// Each operand must have the same type. Suitable for e.g. binops, unary
237+
/// ops, etc.
238+
///
239+
/// \p opcode - The WASM opcode to build.
240+
/// \p valueType - The operand and result type for the built instruction.
241+
/// \p numOperands - The number of operands for the built operation.
242+
///
243+
/// \returns The parsed instruction result, or failure.
244+
template <typename opcode, typename valueType, unsigned int numOperands>
245+
inline parsed_inst_t
246+
buildNumericOp(OpBuilder &builder,
247+
std::enable_if_t<std::is_arithmetic_v<valueType>> * = nullptr);
248+
235249
/// This function generates a dispatch tree to associate an opcode with a
236250
/// parser. Parsers are registered by specialising the
237251
/// `parseSpecificInstruction` function for the op code to handle.
@@ -863,6 +877,95 @@ inline parsed_inst_t ExpressionParser::parseSpecificInstruction<
863877
return parseConstInst<double>(builder);
864878
}
865879

880+
template <typename opcode, typename valueType, unsigned int numOperands>
881+
inline parsed_inst_t ExpressionParser::buildNumericOp(
882+
OpBuilder &builder, std::enable_if_t<std::is_arithmetic_v<valueType>> *) {
883+
auto ty = buildLiteralType<valueType>(builder);
884+
LLVM_DEBUG(llvm::dbgs() << "*** buildNumericOp: numOperands = " << numOperands
885+
<< ", type = " << ty << " ***\n");
886+
auto tysToPop = llvm::SmallVector<Type, numOperands>();
887+
tysToPop.resize(numOperands);
888+
std::fill(tysToPop.begin(), tysToPop.end(), ty);
889+
auto operands = popOperands(tysToPop);
890+
if (failed(operands))
891+
return failure();
892+
auto op = builder.create<opcode>(*currentOpLoc, *operands).getResult();
893+
LLVM_DEBUG(llvm::dbgs() << "Built: ");
894+
LLVM_DEBUG(op.dump());
895+
return {{op}};
896+
}
897+
898+
// Convenience macro for generating numerical operations.
899+
#define BUILD_NUMERIC_OP(OP_NAME, N_ARGS, PREFIX, SUFFIX, TYPE) \
900+
template <> \
901+
inline parsed_inst_t ExpressionParser::parseSpecificInstruction< \
902+
WasmBinaryEncoding::OpCode::PREFIX##SUFFIX>(OpBuilder & builder) { \
903+
return buildNumericOp<OP_NAME, TYPE, N_ARGS>(builder); \
904+
}
905+
906+
// Macro to define binops that only support integer types.
907+
#define BUILD_NUMERIC_BINOP_INT(OP_NAME, PREFIX) \
908+
BUILD_NUMERIC_OP(OP_NAME, 2, PREFIX, I32, int32_t) \
909+
BUILD_NUMERIC_OP(OP_NAME, 2, PREFIX, I64, int64_t)
910+
911+
// Macro to define binops that only support floating point types.
912+
#define BUILD_NUMERIC_BINOP_FP(OP_NAME, PREFIX) \
913+
BUILD_NUMERIC_OP(OP_NAME, 2, PREFIX, F32, float) \
914+
BUILD_NUMERIC_OP(OP_NAME, 2, PREFIX, F64, double)
915+
916+
// Macro to define binops that support both floating point and integer types.
917+
#define BUILD_NUMERIC_BINOP_INTFP(OP_NAME, PREFIX) \
918+
BUILD_NUMERIC_BINOP_INT(OP_NAME, PREFIX) \
919+
BUILD_NUMERIC_BINOP_FP(OP_NAME, PREFIX)
920+
921+
// Macro to implement unary ops that only support integers.
922+
#define BUILD_NUMERIC_UNARY_OP_INT(OP_NAME, PREFIX) \
923+
BUILD_NUMERIC_OP(OP_NAME, 1, PREFIX, I32, int32_t) \
924+
BUILD_NUMERIC_OP(OP_NAME, 1, PREFIX, I64, int64_t)
925+
926+
// Macro to implement unary ops that support integer and floating point types.
927+
#define BUILD_NUMERIC_UNARY_OP_FP(OP_NAME, PREFIX) \
928+
BUILD_NUMERIC_OP(OP_NAME, 1, PREFIX, F32, float) \
929+
BUILD_NUMERIC_OP(OP_NAME, 1, PREFIX, F64, double)
930+
931+
BUILD_NUMERIC_BINOP_FP(CopySignOp, copysign)
932+
BUILD_NUMERIC_BINOP_FP(DivOp, div)
933+
BUILD_NUMERIC_BINOP_FP(MaxOp, max)
934+
BUILD_NUMERIC_BINOP_FP(MinOp, min)
935+
BUILD_NUMERIC_BINOP_INT(AndOp, and)
936+
BUILD_NUMERIC_BINOP_INT(DivSIOp, divS)
937+
BUILD_NUMERIC_BINOP_INT(DivUIOp, divU)
938+
BUILD_NUMERIC_BINOP_INT(OrOp, or)
939+
BUILD_NUMERIC_BINOP_INT(RemSIOp, remS)
940+
BUILD_NUMERIC_BINOP_INT(RemUIOp, remU)
941+
BUILD_NUMERIC_BINOP_INT(RotlOp, rotl)
942+
BUILD_NUMERIC_BINOP_INT(RotrOp, rotr)
943+
BUILD_NUMERIC_BINOP_INT(ShLOp, shl)
944+
BUILD_NUMERIC_BINOP_INT(ShRSOp, shrS)
945+
BUILD_NUMERIC_BINOP_INT(ShRUOp, shrU)
946+
BUILD_NUMERIC_BINOP_INT(XOrOp, xor)
947+
BUILD_NUMERIC_BINOP_INTFP(AddOp, add)
948+
BUILD_NUMERIC_BINOP_INTFP(MulOp, mul)
949+
BUILD_NUMERIC_BINOP_INTFP(SubOp, sub)
950+
BUILD_NUMERIC_UNARY_OP_FP(AbsOp, abs)
951+
BUILD_NUMERIC_UNARY_OP_FP(CeilOp, ceil)
952+
BUILD_NUMERIC_UNARY_OP_FP(FloorOp, floor)
953+
BUILD_NUMERIC_UNARY_OP_FP(NegOp, neg)
954+
BUILD_NUMERIC_UNARY_OP_FP(SqrtOp, sqrt)
955+
BUILD_NUMERIC_UNARY_OP_FP(TruncOp, trunc)
956+
BUILD_NUMERIC_UNARY_OP_INT(ClzOp, clz)
957+
BUILD_NUMERIC_UNARY_OP_INT(CtzOp, ctz)
958+
BUILD_NUMERIC_UNARY_OP_INT(PopCntOp, popcnt)
959+
960+
// Don't need these anymore so let's undef them.
961+
#undef BUILD_NUMERIC_BINOP_FP
962+
#undef BUILD_NUMERIC_BINOP_INT
963+
#undef BUILD_NUMERIC_BINOP_INTFP
964+
#undef BUILD_NUMERIC_UNARY_OP_FP
965+
#undef BUILD_NUMERIC_UNARY_OP_INT
966+
#undef BUILD_NUMERIC_OP
967+
#undef BUILD_NUMERIC_CAST_OP
968+
866969
class WasmBinaryParser {
867970
private:
868971
struct SectionRegistry {

mlir/test/Target/Wasm/abs.mlir

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
// RUN: yaml2obj %S/inputs/abs.yaml.wasm -o - | mlir-translate --import-wasm | FileCheck %s
2+
3+
/* Source code used to generate this test:
4+
(module
5+
(func (export "abs_f32") (result f32)
6+
f32.const 10
7+
f32.abs)
8+
9+
(func (export "abs_f64") (result f64)
10+
f64.const 10
11+
f64.abs)
12+
)
13+
*/
14+
15+
// CHECK-LABEL: wasmssa.func @abs_f32() -> f32 {
16+
// CHECK: %[[VAL_0:.*]] = wasmssa.const 1.000000e+01 : f32
17+
// CHECK: %[[VAL_1:.*]] = wasmssa.abs %[[VAL_0]] : f32
18+
// CHECK: wasmssa.return %[[VAL_1]] : f32
19+
20+
// CHECK-LABEL: wasmssa.func @abs_f64() -> f64 {
21+
// CHECK: %[[VAL_0:.*]] = wasmssa.const 1.000000e+01 : f64
22+
// CHECK: %[[VAL_1:.*]] = wasmssa.abs %[[VAL_0]] : f64
23+
// CHECK: wasmssa.return %[[VAL_1]] : f64

mlir/test/Target/Wasm/and.mlir

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
// RUN: yaml2obj %S/inputs/and.yaml.wasm -o - | mlir-translate --import-wasm | FileCheck %s
2+
3+
/* Source code used to generate this test:
4+
(module
5+
(func (export "and_i32") (result i32)
6+
i32.const 10
7+
i32.const 3
8+
i32.and)
9+
10+
(func (export "and_i64") (result i64)
11+
i64.const 10
12+
i64.const 3
13+
i64.and)
14+
)
15+
*/
16+
17+
// CHECK-LABEL: wasmssa.func @and_i32() -> i32 {
18+
// CHECK: %0 = wasmssa.const 10 : i32
19+
// CHECK: %1 = wasmssa.const 3 : i32
20+
// CHECK: %2 = wasmssa.and %0 %1 : i32
21+
// CHECK: wasmssa.return %2 : i32
22+
23+
// CHECK-LABEL: wasmssa.func @and_i64() -> i64 {
24+
// CHECK: %0 = wasmssa.const 10 : i64
25+
// CHECK: %1 = wasmssa.const 3 : i64
26+
// CHECK: %2 = wasmssa.and %0 %1 : i64
27+
// CHECK: wasmssa.return %2 : i64

mlir/test/Target/Wasm/clz.mlir

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
// RUN: yaml2obj %S/inputs/clz.yaml.wasm -o - | mlir-translate --import-wasm | FileCheck %s
2+
3+
/* Source code used to generate this test:
4+
(module
5+
(func (export "clz_i32") (result i32)
6+
i32.const 10
7+
i32.clz
8+
)
9+
10+
(func (export "clz_i64") (result i64)
11+
i64.const 10
12+
i64.clz
13+
)
14+
)
15+
*/
16+
17+
// CHECK-LABEL: wasmssa.func @clz_i32() -> i32 {
18+
// CHECK: %[[VAL_0:.*]] = wasmssa.const 10 : i32
19+
// CHECK: %[[VAL_1:.*]] = wasmssa.clz %[[VAL_0]] : i32
20+
// CHECK: wasmssa.return %[[VAL_1]] : i32
21+
22+
// CHECK-LABEL: wasmssa.func @clz_i64() -> i64 {
23+
// CHECK: %[[VAL_0:.*]] = wasmssa.const 10 : i64
24+
// CHECK: %[[VAL_1:.*]] = wasmssa.clz %[[VAL_0]] : i64
25+
// CHECK: wasmssa.return %[[VAL_1]] : i64
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
// RUN: yaml2obj %S/inputs/copysign.yaml.wasm -o - | mlir-translate --import-wasm | FileCheck %s
2+
3+
/* Source code used to generate this test:
4+
(module
5+
(func (export "copysign_f32") (result f32)
6+
f32.const 10
7+
f32.const 1
8+
f32.copysign
9+
)
10+
11+
(func (export "copysign_f64") (result f64)
12+
f64.const 10
13+
f64.const 1
14+
f64.copysign
15+
)
16+
)
17+
*/
18+
19+
// CHECK-LABEL: wasmssa.func @copysign_f32() -> f32 {
20+
// CHECK: %[[VAL_0:.*]] = wasmssa.const 1.000000e+01 : f32
21+
// CHECK: %[[VAL_1:.*]] = wasmssa.const 1.000000e+00 : f32
22+
// CHECK: %[[VAL_2:.*]] = wasmssa.copysign %[[VAL_0]] %[[VAL_1]] : f32
23+
// CHECK: wasmssa.return %[[VAL_2]] : f32
24+
// CHECK: }
25+
26+
// CHECK-LABEL: wasmssa.func @copysign_f64() -> f64 {
27+
// CHECK: %[[VAL_0:.*]] = wasmssa.const 1.000000e+01 : f64
28+
// CHECK: %[[VAL_1:.*]] = wasmssa.const 1.000000e+00 : f64
29+
// CHECK: %[[VAL_2:.*]] = wasmssa.copysign %[[VAL_0]] %[[VAL_1]] : f64
30+
// CHECK: wasmssa.return %[[VAL_2]] : f64
31+
// CHECK: }

mlir/test/Target/Wasm/ctz.mlir

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
// RUN: yaml2obj %S/inputs/ctz.yaml.wasm -o - | mlir-translate --import-wasm | FileCheck %s
2+
3+
/* Source code used to generate this test:
4+
(module
5+
(func (export "ctz_i32") (result i32)
6+
i32.const 10
7+
i32.ctz
8+
)
9+
10+
(func (export "ctz_i64") (result i64)
11+
i64.const 10
12+
i64.ctz
13+
)
14+
)
15+
*/
16+
17+
// CHECK-LABEL: wasmssa.func @ctz_i32() -> i32 {
18+
// CHECK: %[[VAL_0:.*]] = wasmssa.const 10 : i32
19+
// CHECK: %[[VAL_1:.*]] = wasmssa.ctz %[[VAL_0]] : i32
20+
// CHECK: wasmssa.return %[[VAL_1]] : i32
21+
22+
// CHECK-LABEL: wasmssa.func @ctz_i64() -> i64 {
23+
// CHECK: %[[VAL_0:.*]] = wasmssa.const 10 : i64
24+
// CHECK: %[[VAL_1:.*]] = wasmssa.ctz %[[VAL_0]] : i64
25+
// CHECK: wasmssa.return %[[VAL_1]] : i64

0 commit comments

Comments
 (0)