1 | //===--- WebAssembly.cpp - Implement WebAssembly target feature support ---===// |
2 | // |
3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
4 | // See https://llvm.org/LICENSE.txt for license information. |
5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
6 | // |
7 | //===----------------------------------------------------------------------===// |
8 | // |
9 | // This file implements WebAssembly TargetInfo objects. |
10 | // |
11 | //===----------------------------------------------------------------------===// |
12 | |
13 | #include "WebAssembly.h" |
14 | #include "Targets.h" |
15 | #include "clang/Basic/Builtins.h" |
16 | #include "clang/Basic/Diagnostic.h" |
17 | #include "clang/Basic/TargetBuiltins.h" |
18 | #include "llvm/ADT/StringSwitch.h" |
19 | |
20 | using namespace clang; |
21 | using namespace clang::targets; |
22 | |
23 | static constexpr int NumBuiltins = |
24 | clang::WebAssembly::LastTSBuiltin - Builtin::FirstTSBuiltin; |
25 | |
26 | static constexpr llvm::StringTable BuiltinStrings = |
27 | CLANG_BUILTIN_STR_TABLE_START |
28 | #define BUILTIN CLANG_BUILTIN_STR_TABLE |
29 | #define TARGET_BUILTIN CLANG_TARGET_BUILTIN_STR_TABLE |
30 | #include "clang/Basic/BuiltinsWebAssembly.def" |
31 | ; |
32 | |
33 | static constexpr auto BuiltinInfos = Builtin::MakeInfos<NumBuiltins>(Infos: { |
34 | #define BUILTIN CLANG_BUILTIN_ENTRY |
35 | #define TARGET_BUILTIN CLANG_TARGET_BUILTIN_ENTRY |
36 | #define LIBBUILTIN CLANG_LIBBUILTIN_ENTRY |
37 | #include "clang/Basic/BuiltinsWebAssembly.def" |
38 | }); |
39 | |
40 | static constexpr llvm::StringLiteral ValidCPUNames[] = { |
41 | {"mvp" }, {"bleeding-edge" }, {"generic" }, {"lime1" }}; |
42 | |
43 | StringRef WebAssemblyTargetInfo::getABI() const { return ABI; } |
44 | |
45 | bool WebAssemblyTargetInfo::setABI(const std::string &Name) { |
46 | if (Name != "mvp" && Name != "experimental-mv" ) |
47 | return false; |
48 | |
49 | ABI = Name; |
50 | return true; |
51 | } |
52 | |
53 | bool WebAssemblyTargetInfo::hasFeature(StringRef Feature) const { |
54 | return llvm::StringSwitch<bool>(Feature) |
55 | .Case(S: "atomics" , Value: HasAtomics) |
56 | .Case(S: "bulk-memory" , Value: HasBulkMemory) |
57 | .Case(S: "bulk-memory-opt" , Value: HasBulkMemoryOpt) |
58 | .Case(S: "call-indirect-overlong" , Value: HasCallIndirectOverlong) |
59 | .Case(S: "exception-handling" , Value: HasExceptionHandling) |
60 | .Case(S: "extended-const" , Value: HasExtendedConst) |
61 | .Case(S: "fp16" , Value: HasFP16) |
62 | .Case(S: "multimemory" , Value: HasMultiMemory) |
63 | .Case(S: "multivalue" , Value: HasMultivalue) |
64 | .Case(S: "mutable-globals" , Value: HasMutableGlobals) |
65 | .Case(S: "nontrapping-fptoint" , Value: HasNontrappingFPToInt) |
66 | .Case(S: "reference-types" , Value: HasReferenceTypes) |
67 | .Case(S: "relaxed-simd" , Value: SIMDLevel >= RelaxedSIMD) |
68 | .Case(S: "sign-ext" , Value: HasSignExt) |
69 | .Case(S: "simd128" , Value: SIMDLevel >= SIMD128) |
70 | .Case(S: "tail-call" , Value: HasTailCall) |
71 | .Case(S: "wide-arithmetic" , Value: HasWideArithmetic) |
72 | .Default(Value: false); |
73 | } |
74 | |
75 | bool WebAssemblyTargetInfo::isValidCPUName(StringRef Name) const { |
76 | return llvm::is_contained(Range: ValidCPUNames, Element: Name); |
77 | } |
78 | |
79 | void WebAssemblyTargetInfo::fillValidCPUList( |
80 | SmallVectorImpl<StringRef> &Values) const { |
81 | Values.append(in_start: std::begin(arr: ValidCPUNames), in_end: std::end(arr: ValidCPUNames)); |
82 | } |
83 | |
84 | void WebAssemblyTargetInfo::getTargetDefines(const LangOptions &Opts, |
85 | MacroBuilder &Builder) const { |
86 | defineCPUMacros(Builder, CPUName: "wasm" , /*Tuning=*/false); |
87 | if (HasAtomics) |
88 | Builder.defineMacro(Name: "__wasm_atomics__" ); |
89 | if (HasBulkMemory) |
90 | Builder.defineMacro(Name: "__wasm_bulk_memory__" ); |
91 | if (HasBulkMemoryOpt) |
92 | Builder.defineMacro(Name: "__wasm_bulk_memory_opt__" ); |
93 | if (HasExceptionHandling) |
94 | Builder.defineMacro(Name: "__wasm_exception_handling__" ); |
95 | if (HasExtendedConst) |
96 | Builder.defineMacro(Name: "__wasm_extended_const__" ); |
97 | if (HasMultiMemory) |
98 | Builder.defineMacro(Name: "__wasm_multimemory__" ); |
99 | if (HasFP16) |
100 | Builder.defineMacro(Name: "__wasm_fp16__" ); |
101 | if (HasMultivalue) |
102 | Builder.defineMacro(Name: "__wasm_multivalue__" ); |
103 | if (HasMutableGlobals) |
104 | Builder.defineMacro(Name: "__wasm_mutable_globals__" ); |
105 | if (HasNontrappingFPToInt) |
106 | Builder.defineMacro(Name: "__wasm_nontrapping_fptoint__" ); |
107 | if (HasReferenceTypes) |
108 | Builder.defineMacro(Name: "__wasm_reference_types__" ); |
109 | if (SIMDLevel >= RelaxedSIMD) |
110 | Builder.defineMacro(Name: "__wasm_relaxed_simd__" ); |
111 | if (HasSignExt) |
112 | Builder.defineMacro(Name: "__wasm_sign_ext__" ); |
113 | if (SIMDLevel >= SIMD128) |
114 | Builder.defineMacro(Name: "__wasm_simd128__" ); |
115 | if (HasTailCall) |
116 | Builder.defineMacro(Name: "__wasm_tail_call__" ); |
117 | if (HasWideArithmetic) |
118 | Builder.defineMacro(Name: "__wasm_wide_arithmetic__" ); |
119 | |
120 | Builder.defineMacro(Name: "__GCC_HAVE_SYNC_COMPARE_AND_SWAP_1" ); |
121 | Builder.defineMacro(Name: "__GCC_HAVE_SYNC_COMPARE_AND_SWAP_2" ); |
122 | Builder.defineMacro(Name: "__GCC_HAVE_SYNC_COMPARE_AND_SWAP_4" ); |
123 | Builder.defineMacro(Name: "__GCC_HAVE_SYNC_COMPARE_AND_SWAP_8" ); |
124 | } |
125 | |
126 | void WebAssemblyTargetInfo::setSIMDLevel(llvm::StringMap<bool> &Features, |
127 | SIMDEnum Level, bool Enabled) { |
128 | if (Enabled) { |
129 | switch (Level) { |
130 | case RelaxedSIMD: |
131 | Features["relaxed-simd" ] = true; |
132 | [[fallthrough]]; |
133 | case SIMD128: |
134 | Features["simd128" ] = true; |
135 | [[fallthrough]]; |
136 | case NoSIMD: |
137 | break; |
138 | } |
139 | return; |
140 | } |
141 | |
142 | switch (Level) { |
143 | case NoSIMD: |
144 | case SIMD128: |
145 | Features["simd128" ] = false; |
146 | [[fallthrough]]; |
147 | case RelaxedSIMD: |
148 | Features["relaxed-simd" ] = false; |
149 | break; |
150 | } |
151 | } |
152 | |
153 | void WebAssemblyTargetInfo::setFeatureEnabled(llvm::StringMap<bool> &Features, |
154 | StringRef Name, |
155 | bool Enabled) const { |
156 | if (Name == "simd128" ) |
157 | setSIMDLevel(Features, Level: SIMD128, Enabled); |
158 | else if (Name == "relaxed-simd" ) |
159 | setSIMDLevel(Features, Level: RelaxedSIMD, Enabled); |
160 | else |
161 | Features[Name] = Enabled; |
162 | } |
163 | |
164 | bool WebAssemblyTargetInfo::initFeatureMap( |
165 | llvm::StringMap<bool> &Features, DiagnosticsEngine &Diags, StringRef CPU, |
166 | const std::vector<std::string> &FeaturesVec) const { |
167 | auto addGenericFeatures = [&]() { |
168 | Features["bulk-memory" ] = true; |
169 | Features["bulk-memory-opt" ] = true; |
170 | Features["call-indirect-overlong" ] = true; |
171 | Features["multivalue" ] = true; |
172 | Features["mutable-globals" ] = true; |
173 | Features["nontrapping-fptoint" ] = true; |
174 | Features["reference-types" ] = true; |
175 | Features["sign-ext" ] = true; |
176 | }; |
177 | auto addLime1Features = [&]() { |
178 | // Lime1: |
179 | // <https://github.com/WebAssembly/tool-conventions/blob/main/Lime.md#lime1> |
180 | Features["bulk-memory-opt" ] = true; |
181 | Features["call-indirect-overlong" ] = true; |
182 | Features["extended-const" ] = true; |
183 | Features["multivalue" ] = true; |
184 | Features["mutable-globals" ] = true; |
185 | Features["nontrapping-fptoint" ] = true; |
186 | Features["sign-ext" ] = true; |
187 | }; |
188 | auto addBleedingEdgeFeatures = [&]() { |
189 | addGenericFeatures(); |
190 | Features["atomics" ] = true; |
191 | Features["exception-handling" ] = true; |
192 | Features["extended-const" ] = true; |
193 | Features["fp16" ] = true; |
194 | Features["multimemory" ] = true; |
195 | Features["tail-call" ] = true; |
196 | Features["wide-arithmetic" ] = true; |
197 | setSIMDLevel(Features, Level: RelaxedSIMD, Enabled: true); |
198 | }; |
199 | if (CPU == "generic" ) { |
200 | addGenericFeatures(); |
201 | } else if (CPU == "lime1" ) { |
202 | addLime1Features(); |
203 | } else if (CPU == "bleeding-edge" ) { |
204 | addBleedingEdgeFeatures(); |
205 | } |
206 | |
207 | return TargetInfo::initFeatureMap(Features, Diags, CPU, FeatureVec: FeaturesVec); |
208 | } |
209 | |
210 | bool WebAssemblyTargetInfo::handleTargetFeatures( |
211 | std::vector<std::string> &Features, DiagnosticsEngine &Diags) { |
212 | for (const auto &Feature : Features) { |
213 | if (Feature == "+atomics" ) { |
214 | HasAtomics = true; |
215 | continue; |
216 | } |
217 | if (Feature == "-atomics" ) { |
218 | HasAtomics = false; |
219 | continue; |
220 | } |
221 | if (Feature == "+bulk-memory" ) { |
222 | HasBulkMemory = true; |
223 | continue; |
224 | } |
225 | if (Feature == "-bulk-memory" ) { |
226 | HasBulkMemory = false; |
227 | continue; |
228 | } |
229 | if (Feature == "+bulk-memory-opt" ) { |
230 | HasBulkMemoryOpt = true; |
231 | continue; |
232 | } |
233 | if (Feature == "-bulk-memory-opt" ) { |
234 | HasBulkMemoryOpt = false; |
235 | continue; |
236 | } |
237 | if (Feature == "+call-indirect-overlong" ) { |
238 | HasCallIndirectOverlong = true; |
239 | continue; |
240 | } |
241 | if (Feature == "-call-indirect-overlong" ) { |
242 | HasCallIndirectOverlong = false; |
243 | continue; |
244 | } |
245 | if (Feature == "+exception-handling" ) { |
246 | HasExceptionHandling = true; |
247 | continue; |
248 | } |
249 | if (Feature == "-exception-handling" ) { |
250 | HasExceptionHandling = false; |
251 | continue; |
252 | } |
253 | if (Feature == "+extended-const" ) { |
254 | HasExtendedConst = true; |
255 | continue; |
256 | } |
257 | if (Feature == "-extended-const" ) { |
258 | HasExtendedConst = false; |
259 | continue; |
260 | } |
261 | if (Feature == "+fp16" ) { |
262 | SIMDLevel = std::max(a: SIMDLevel, b: SIMD128); |
263 | HasFP16 = true; |
264 | continue; |
265 | } |
266 | if (Feature == "-fp16" ) { |
267 | HasFP16 = false; |
268 | continue; |
269 | } |
270 | if (Feature == "+multimemory" ) { |
271 | HasMultiMemory = true; |
272 | continue; |
273 | } |
274 | if (Feature == "-multimemory" ) { |
275 | HasMultiMemory = false; |
276 | continue; |
277 | } |
278 | if (Feature == "+multivalue" ) { |
279 | HasMultivalue = true; |
280 | continue; |
281 | } |
282 | if (Feature == "-multivalue" ) { |
283 | HasMultivalue = false; |
284 | continue; |
285 | } |
286 | if (Feature == "+mutable-globals" ) { |
287 | HasMutableGlobals = true; |
288 | continue; |
289 | } |
290 | if (Feature == "-mutable-globals" ) { |
291 | HasMutableGlobals = false; |
292 | continue; |
293 | } |
294 | if (Feature == "+nontrapping-fptoint" ) { |
295 | HasNontrappingFPToInt = true; |
296 | continue; |
297 | } |
298 | if (Feature == "-nontrapping-fptoint" ) { |
299 | HasNontrappingFPToInt = false; |
300 | continue; |
301 | } |
302 | if (Feature == "+reference-types" ) { |
303 | HasReferenceTypes = true; |
304 | continue; |
305 | } |
306 | if (Feature == "-reference-types" ) { |
307 | HasReferenceTypes = false; |
308 | continue; |
309 | } |
310 | if (Feature == "+relaxed-simd" ) { |
311 | SIMDLevel = std::max(a: SIMDLevel, b: RelaxedSIMD); |
312 | continue; |
313 | } |
314 | if (Feature == "-relaxed-simd" ) { |
315 | SIMDLevel = std::min(a: SIMDLevel, b: SIMDEnum(RelaxedSIMD - 1)); |
316 | continue; |
317 | } |
318 | if (Feature == "+sign-ext" ) { |
319 | HasSignExt = true; |
320 | continue; |
321 | } |
322 | if (Feature == "-sign-ext" ) { |
323 | HasSignExt = false; |
324 | continue; |
325 | } |
326 | if (Feature == "+simd128" ) { |
327 | SIMDLevel = std::max(a: SIMDLevel, b: SIMD128); |
328 | continue; |
329 | } |
330 | if (Feature == "-simd128" ) { |
331 | SIMDLevel = std::min(a: SIMDLevel, b: SIMDEnum(SIMD128 - 1)); |
332 | continue; |
333 | } |
334 | if (Feature == "+tail-call" ) { |
335 | HasTailCall = true; |
336 | continue; |
337 | } |
338 | if (Feature == "-tail-call" ) { |
339 | HasTailCall = false; |
340 | continue; |
341 | } |
342 | if (Feature == "+wide-arithmetic" ) { |
343 | HasWideArithmetic = true; |
344 | continue; |
345 | } |
346 | if (Feature == "-wide-arithmetic" ) { |
347 | HasWideArithmetic = false; |
348 | continue; |
349 | } |
350 | |
351 | Diags.Report(diag::err_opt_not_valid_with_opt) |
352 | << Feature << "-target-feature" ; |
353 | return false; |
354 | } |
355 | |
356 | // bulk-memory-opt is a subset of bulk-memory. |
357 | if (HasBulkMemory) { |
358 | HasBulkMemoryOpt = true; |
359 | } |
360 | |
361 | // The reference-types feature included the change to `call_indirect` |
362 | // encodings to support overlong immediates. |
363 | if (HasReferenceTypes) { |
364 | HasCallIndirectOverlong = true; |
365 | } |
366 | |
367 | return true; |
368 | } |
369 | |
370 | llvm::SmallVector<Builtin::InfosShard> |
371 | WebAssemblyTargetInfo::getTargetBuiltins() const { |
372 | return {{&BuiltinStrings, BuiltinInfos}}; |
373 | } |
374 | |
375 | void WebAssemblyTargetInfo::adjust(DiagnosticsEngine &Diags, |
376 | LangOptions &Opts) { |
377 | TargetInfo::adjust(Diags, Opts); |
378 | // Turn off POSIXThreads and ThreadModel so that we don't predefine _REENTRANT |
379 | // or __STDCPP_THREADS__ if we will eventually end up stripping atomics |
380 | // because they are unsupported. |
381 | if (!HasAtomics || !HasBulkMemory) { |
382 | Opts.POSIXThreads = false; |
383 | Opts.setThreadModel(LangOptions::ThreadModelKind::Single); |
384 | Opts.ThreadsafeStatics = false; |
385 | } |
386 | } |
387 | |
388 | void WebAssembly32TargetInfo::getTargetDefines(const LangOptions &Opts, |
389 | MacroBuilder &Builder) const { |
390 | WebAssemblyTargetInfo::getTargetDefines(Opts, Builder); |
391 | defineCPUMacros(Builder, CPUName: "wasm32" , /*Tuning=*/false); |
392 | } |
393 | |
394 | void WebAssembly64TargetInfo::getTargetDefines(const LangOptions &Opts, |
395 | MacroBuilder &Builder) const { |
396 | WebAssemblyTargetInfo::getTargetDefines(Opts, Builder); |
397 | defineCPUMacros(Builder, CPUName: "wasm64" , /*Tuning=*/false); |
398 | } |
399 | |