1 #include "CodeGen_WebAssembly.h"
2 
3 #include "ConciseCasts.h"
4 #include "IRMatch.h"
5 #include "IROperator.h"
6 #include "LLVM_Headers.h"
7 #include "Util.h"
8 
9 #include <sstream>
10 
11 namespace Halide {
12 namespace Internal {
13 
14 using namespace Halide::ConciseCasts;
15 using namespace llvm;
16 using std::string;
17 using std::vector;
18 
CodeGen_WebAssembly(Target t)19 CodeGen_WebAssembly::CodeGen_WebAssembly(Target t)
20     : CodeGen_Posix(t) {
21 #if !defined(WITH_WEBASSEMBLY)
22     user_error << "llvm build not configured with WebAssembly target enabled.\n";
23 #endif
24     user_assert(LLVM_VERSION >= 110) << "Generating WebAssembly is only supported under LLVM 11+.";
25     user_assert(llvm_WebAssembly_enabled) << "llvm build not configured with WebAssembly target enabled.\n";
26     user_assert(target.bits == 32) << "Only wasm32 is supported.";
27 }
28 
visit(const Cast * op)29 void CodeGen_WebAssembly::visit(const Cast *op) {
30     {
31         Halide::Type src = op->value.type();
32         Halide::Type dst = op->type;
33         if (upgrade_type_for_arithmetic(src) != src ||
34             upgrade_type_for_arithmetic(dst) != dst) {
35             // Handle casts to and from types for which we don't have native support.
36             CodeGen_Posix::visit(op);
37             return;
38         }
39     }
40 
41     if (!op->type.is_vector()) {
42         // We only have peephole optimizations for vectors in here.
43         CodeGen_Posix::visit(op);
44         return;
45     }
46 
47     vector<Expr> matches;
48 
49     struct Pattern {
50         Target::Feature feature;
51         bool wide_op;
52         Type type;
53         int min_lanes;
54         string intrin;
55         Expr pattern;
56     };
57 
58     static Pattern patterns[] = {
59         {Target::WasmSimd128, true, Int(8, 16), 0, "llvm.sadd.sat.v16i8", i8_sat(wild_i16x_ + wild_i16x_)},
60         {Target::WasmSimd128, true, UInt(8, 16), 0, "llvm.uadd.sat.v16i8", u8_sat(wild_u16x_ + wild_u16x_)},
61         {Target::WasmSimd128, true, Int(16, 8), 0, "llvm.sadd.sat.v8i16", i16_sat(wild_i32x_ + wild_i32x_)},
62         {Target::WasmSimd128, true, UInt(16, 8), 0, "llvm.uadd.sat.v8i16", u16_sat(wild_u32x_ + wild_u32x_)},
63         // N.B. Saturating subtracts are expressed by widening to a *signed* type
64         {Target::WasmSimd128, true, Int(8, 16), 0, "llvm.wasm.sub.saturate.signed.v16i8", i8_sat(wild_i16x_ - wild_i16x_)},
65         {Target::WasmSimd128, true, UInt(8, 16), 0, "llvm.wasm.sub.saturate.unsigned.v16i8", u8_sat(wild_i16x_ - wild_i16x_)},
66         {Target::WasmSimd128, true, Int(16, 8), 0, "llvm.wasm.sub.saturate.signed.v8i16", i16_sat(wild_i32x_ - wild_i32x_)},
67         {Target::WasmSimd128, true, UInt(16, 8), 0, "llvm.wasm.sub.saturate.unsigned.v8i16", u16_sat(wild_i32x_ - wild_i32x_)},
68 
69         {Target::WasmSimd128, true, UInt(8, 16), 0, "llvm.wasm.avgr.unsigned.v16i8", u8(((wild_u16x_ + wild_u16x_) + 1) / 2)},
70         {Target::WasmSimd128, true, UInt(8, 16), 0, "llvm.wasm.avgr.unsigned.v16i8", u8(((wild_u16x_ + wild_u16x_) + 1) >> 1)},
71         {Target::WasmSimd128, true, UInt(16, 8), 0, "llvm.wasm.avgr.unsigned.v8i16", u16(((wild_u32x_ + wild_u32x_) + 1) / 2)},
72         {Target::WasmSimd128, true, UInt(16, 8), 0, "llvm.wasm.avgr.unsigned.v8i16", u16(((wild_u32x_ + wild_u32x_) + 1) >> 1)},
73 
74         // TODO: LLVM should support this directly, but doesn't yet.
75         // To make this work, we need to be able to call the intrinsics with two vecs.
76         // @abadams sez: "The way I've had to do this in the past is with force-inlined implementations
77         // that accept the wider vec, e.g. see packsswbx16 in src/runtime/x86.ll"
78         // {Target::WasmSimd128, false, Int(8, 16), 0, "llvm.wasm.narrow.signed.v16i8.v8i16", i8(wild_i16x_)},
79         // {Target::WasmSimd128, false, Int(16, 8), 0, "llvm.wasm.narrow.signed.v8i16.v4i32", i16(wild_i32x_)},
80         // {Target::WasmSimd128, false, UInt(8, 16), 0, "llvm.wasm.narrow.unsigned.v16i8.v8i16", u8(wild_u16x_)},
81         // {Target::WasmSimd128, false, UInt(16, 8), 0, "llvm.wasm.narrow.unsigned.v8i16.v4i32", u16(wild_u32x_)},
82     };
83 
84     for (size_t i = 0; i < sizeof(patterns) / sizeof(patterns[0]); i++) {
85         const Pattern &pattern = patterns[i];
86 
87         if (!target.has_feature(pattern.feature)) {
88             continue;
89         }
90 
91         if (op->type.lanes() < pattern.min_lanes) {
92             continue;
93         }
94 
95         if (expr_match(pattern.pattern, op, matches)) {
96             bool match = true;
97             if (pattern.wide_op) {
98                 // Try to narrow the matches to the target type.
99                 for (size_t i = 0; i < matches.size(); i++) {
100                     matches[i] = lossless_cast(op->type, matches[i]);
101                     if (!matches[i].defined()) {
102                         match = false;
103                     }
104                 }
105             }
106             if (match) {
107                 value = call_intrin(op->type, pattern.type.lanes(), pattern.intrin, matches);
108                 return;
109             }
110         }
111     }
112 
113     CodeGen_Posix::visit(op);
114 }
115 
mcpu() const116 string CodeGen_WebAssembly::mcpu() const {
117     return "";
118 }
119 
mattrs() const120 string CodeGen_WebAssembly::mattrs() const {
121     std::ostringstream s;
122     string sep;
123 
124     if (target.has_feature(Target::WasmSignExt)) {
125         s << sep << "+sign-ext";
126         sep = ",";
127     }
128 
129     if (target.has_feature(Target::WasmSimd128)) {
130         s << sep << "+simd128";
131         sep = ",";
132     }
133 
134     if (target.has_feature(Target::WasmSatFloatToInt)) {
135         s << sep << "+nontrapping-fptoint";
136         sep = ",";
137     }
138 
139     user_assert(target.os == Target::WebAssemblyRuntime)
140         << "wasmrt is the only supported 'os' for WebAssembly at this time.";
141 
142     return s.str();
143 }
144 
use_soft_float_abi() const145 bool CodeGen_WebAssembly::use_soft_float_abi() const {
146     return false;
147 }
148 
use_pic() const149 bool CodeGen_WebAssembly::use_pic() const {
150     return false;
151 }
152 
native_vector_bits() const153 int CodeGen_WebAssembly::native_vector_bits() const {
154     return 128;
155 }
156 
157 }  // namespace Internal
158 }  // namespace Halide
159